UNPKG

@angular/core

Version:

Angular - the core framework

625 lines • 74.2 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * The styling context acts as a styling manifest (shaped as an array) for determining which * styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp` * and `updateClassProp` functions. It also stores the static style/class values that were * extracted from the template by the compiler. * * A context is created by Angular when: * 1. An element contains static styling values (like style="..." or class="...") * 2. An element contains single property binding values (like [style.prop]="x" or * [class.prop]="y") * 3. An element contains multi property binding values (like [style]="x" or [class]="y") * 4. A directive contains host bindings for static, single or multi styling properties/bindings. * 5. An animation player is added to an element via `addPlayer` * * Note that even if an element contains static styling then this context will be created and * attached to it. The reason why this happens (instead of treating styles/classes as regular * HTML attributes) is because the style/class bindings must be able to default themselves back * to their respective static values when they are set to null. * * Say for example we have this: * ``` * <!-- when `myWidthExp=null` then a width of `100px` * will be used a default value for width --> * <div style="width:100px" [style.width]="myWidthExp"></div> * ``` * * Even in the situation where there are no bindings, the static styling is still placed into the * context because there may be another directive on the same element that has styling. * * When Angular initializes styling data for an element then it will first register the static * styling values on the element using one of these two instructions: * * 1. elementStart or element (within the template function of a component) * 2. elementHostAttrs (for directive host bindings) * * In either case, a styling context will be created and stored within an element's `LViewData`. * Once the styling context is created then single and multi properties can be stored within it. * For this to happen, the following function needs to be called: * * `styling` (called with style properties, class properties and a sanitizer + a directive * instance). * * When this instruction is called it will populate the styling context with the provided style * and class names into the context. * * The context itself looks like this: * * context = [ * // 0-8: header values (about 8 entries of configuration data) * // 9+: this is where each entry is stored: * ] * * Let's say we have the following template code: * * ``` * <div class="foo bar" * style="width:200px; color:red" * [style.width]="myWidthExp" * [style.height]="myHeightExp" * [class.baz]="myBazExp"> * ``` * * The context generated from these values will look like this (note that * for each binding name (the class and style bindings) the values will * be inserted twice into the array (once for single property entries and * again for multi property entries). * * context = [ * // 0-8: header values (about 8 entries of configuration data) * // 9+: this is where each entry is stored: * * // SINGLE PROPERTIES * configForWidth, * 'width' * myWidthExp, // the binding value not the binding itself * 0, // the directive owner * * configForHeight, * 'height' * myHeightExp, // the binding value not the binding itself * 0, // the directive owner * * configForBazClass, * 'baz * myBazClassExp, // the binding value not the binding itself * 0, // the directive owner * * // MULTI PROPERTIES * configForWidth, * 'width' * myWidthExp, // the binding value not the binding itself * 0, // the directive owner * * configForHeight, * 'height' * myHeightExp, // the binding value not the binding itself * 0, // the directive owner * * configForBazClass, * 'baz * myBazClassExp, // the binding value not the binding itself * 0, // the directive owner * ] * * The configuration values are left out of the example above because * the ordering of them could change between code patches. Please read the * documentation below to get a better understand of what the configuration * values are and how they work. * * Each time a binding property is updated (whether it be through a single * property instruction like `styleProp`, `classProp`, * `styleMap` or `classMap`) then the values in the context will be updated as * well. * * If for example `[style.width]` updates to `555px` then its value will be reflected * in the context as so: * * context = [ * // ... * configForWidth, // this will be marked DIRTY * 'width' * '555px', * 0, * //.. * ] * * The context and directive data will also be marked dirty. * * Despite the context being updated, nothing has been rendered on screen (not styles or * classes have been set on the element). To kick off rendering for an element the following * function needs to be run `stylingApply`. * * `stylingApply` will run through the context and find each dirty value and render them onto * the element. Once complete, all styles/classes will be set to clean. Because of this, the render * function will now know not to rerun itself again if called again unless new style/class values * have changed. * * ## Directives * Directive style/class values (which are provided through host bindings) are also supported and * housed within the same styling context as are template-level style/class properties/bindings * So long as they are all assigned to the same element, both directive-level and template-level * styling bindings share the same context. * * Each of the following instructions supports accepting a directive instance as an input parameter: * * - `elementHostAttrs` * - `styling` * - `styleProp` * - `classProp` * - `styleMap` * - `classMap` * - `stylingApply` * * Each time a directive value is passed in, it will be converted into an index by examining the * directive registry (which lives in the context configuration area). The index is then used * to help single style properties figure out where a value is located in the context. * * * ## Single-level styling bindings (`[style.prop]` and `[class.name]`) * * Both `[style.prop]` and `[class.name]` bindings are run through the `updateStyleProp` * and `updateClassProp` functions respectively. They work by examining the provided * `offset` value and are able to locate the exact spot in the context where the * matching style is located. * * Both `[style.prop]` and `[class.name]` bindings are able to process these values * from directive host bindings. When evaluated (from the host binding function) the * `directiveRef` value is then passed in. * * If two directives or a directive + a template binding both write to the same style/class * binding then the styling context code will decide which one wins based on the following * rule: * * 1. If the template binding has a value then it always wins * 2. Otherwise whichever first-registered directive that has that value first will win * * The code example helps make this clear: * * ``` * <!-- * <div [style.width]="myWidth" * [my-width-directive]="'600px'"> * --> * * \@Directive({ * selector: '[my-width-directive'] * }) * class MyWidthDirective { * \@Input('my-width-directive') * \@HostBinding('style.width') * public width = null; * } * ``` * * Since there is a style binding for width present on the element (`[style.width]`) then * it will always win over the width binding that is present as a host binding within * the `MyWidthDirective`. However, if `[style.width]` renders as `null` (so `myWidth=null`) * then the `MyWidthDirective` will be able to write to the `width` style within the context. * Simply put, whichever directive writes to a value first ends up having ownership of it as * long as the template didn't set anything. * * The way in which the ownership is facilitated is through index value. The earliest directives * get the smallest index values (with 0 being reserved for the template element bindings). Each * time a value is written from a directive or the template bindings, the value itself gets * assigned the directive index value in its data. If another directive writes a value again then * its directive index gets compared against the directive index that exists on the element. Only * when the new value's directive index is less than the existing directive index then the new * value will be written to the context. But, if the existing value is null then the new value is * written by the less important directive. * * Each directive also has its own sanitizer and dirty flags. These values are consumed within the * rendering function. * * * ## Multi-level styling bindings (`[style]` and `[class]`) * * Multi-level styling bindings are treated as less important (less specific) as single-level * bindings (things like `[style.prop]` and `[class.name]`). * * Multi-level bindings are still applied to the context in a similar way as are single level * bindings, but this process works by diffing the new multi-level values (which are key/value * maps) against the existing set of styles that live in the context. Each time a new map value * is detected (via identity check) then it will loop through the values and figure out what * has changed and reorder the context array to match the ordering of the keys. This reordering * of the context makes sure that follow-up traversals of the context when updated against the * key/value map are as close as possible to o(n) (where "n" is the size of the key/value map). * * If a `directiveRef` value is passed in then the styling algorithm code will take the directive's * prioritization index into account and update the values with respect to more important * directives. This means that if a value such as `width` is updated in two different `[style]` * bindings (say one on the template and another within a directive that sits on the same element) * then the algorithm will decide how to update the value based on the following heuristic: * * 1. If the template binding has a value then it always wins * 2. If not then whichever first-registered directive that has that value first will win * * It will also update the value if it was set to `null` by a previous directive (or the template). * * Each time a value is updated (or removed) then the context will change shape to better match * the ordering of the styling data as well as the ordering of each directive that contains styling * data. (See `patchStylingMapIntoContext` inside of class_and_style_bindings.ts to better * understand how this works.) * * ## Rendering * The rendering mechanism (when the styling data is applied on screen) occurs via the * `stylingApply` function and is designed to run after **all** styling functions have been * evaluated. The rendering algorithm will loop over the context and only apply the styles that are * flagged as dirty (either because they are new, updated or have been removed via multi or * single bindings). * @record */ export function StylingContext() { } if (false) { /* Skipping unnamed member: [StylingIndex.ElementPosition]: LContainer|LView|RElement|null;*/ /* Skipping unnamed member: [StylingIndex.MasterFlagPosition]: number;*/ /* Skipping unnamed member: [StylingIndex.DirectiveRegistryPosition]: DirectiveRegistryValues;*/ /* Skipping unnamed member: [StylingIndex.InitialStyleValuesPosition]: InitialStylingValues;*/ /* Skipping unnamed member: [StylingIndex.InitialClassValuesPosition]: InitialStylingValues;*/ /* Skipping unnamed member: [StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues;*/ /* Skipping unnamed member: [StylingIndex.CachedMultiClasses]: any|MapBasedOffsetValues;*/ /* Skipping unnamed member: [StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues;*/ /* Skipping unnamed member: [StylingIndex.HostInstructionsQueue]: HostInstructionsQueue|null;*/ /* Skipping unnamed member: [StylingIndex.PlayerContext]: PlayerContext|null;*/ } /** * A queue of all host-related styling instructions (these are buffered and evaluated just before * the styling is applied). * * This queue is used when any `hostStyling` instructions are executed from the `hostBindings` * function. Template-level styling functions (e.g. `styleMap` and `classProp`) * do not make use of this queue (they are applied to the styling context immediately). * * Due to the nature of how components/directives are evaluated, directives (both parent and * subclass directives) may not apply their styling at the right time for the styling * algorithm code to prioritize them. Therefore, all host-styling instructions are queued up * (buffered) into the array below and are automatically sorted in terms of priority. The * priority for host-styling is as follows: * * 1. The template (this doesn't get queued, but gets evaluated immediately) * 2. Any directives present on the host * 2a) first child directive styling bindings are updated * 2b) then any parent directives * 3. Component host bindings * * Angular runs change detection for each of these cases in a different order. Because of this * the array below is populated with each of the host styling functions + their arguments. * * context[HostInstructionsQueue] = [ * directiveIndex, * hostStylingFn, * [argumentsForFn], * ... * anotherDirectiveIndex, <-- this has a lower priority (a higher directive index) * anotherHostStylingFn, * [argumentsForFn], * ] * * When `renderStyling` is called (within `class_and_host_bindings.ts`) then the queue is * drained and each of the instructions are executed. Once complete the queue is empty then * the style/class binding code is rendered on the element (which is what happens normally * inside of `renderStyling`). * * Right now each directive's hostBindings function, as well the template function, both * call `stylingApply()` and `hostStylingApply()`. The fact that this is called * multiple times for the same element (b/c of change detection) causes some issues. To avoid * having styling code be rendered on an element multiple times, the `HostInstructionsQueue` * reserves a slot for a reference pointing to the very last directive that was registered and * only allows for styling to be applied once that directive is encountered (which will happen * as the last update for that element). * @record */ export function HostInstructionsQueue() { } if (false) { /* Skipping unnamed member: [0]: number;*/ } /** @enum {number} */ const HostInstructionsQueueIndex = { LastRegisteredDirectiveIndexPosition: 0, ValuesStartPosition: 1, DirectiveIndexOffset: 0, InstructionFnOffset: 1, ParamsOffset: 2, Size: 3, }; export { HostInstructionsQueueIndex }; /** * Used as a styling array to house static class and style values that were extracted * by the compiler and placed in the animation context via `elementStart` and * `elementHostAttrs`. * * See [InitialStylingValuesIndex] for a breakdown of how all this works. * @record */ export function InitialStylingValues() { } if (false) { /* Skipping unnamed member: [InitialStylingValuesIndex.DefaultNullValuePosition]: null;*/ /* Skipping unnamed member: [InitialStylingValuesIndex.CachedStringValuePosition]: string|null;*/ } /** @enum {number} */ const InitialStylingValuesIndex = { /** * The first value is always `null` so that `styles[0] == null` for unassigned values */ DefaultNullValuePosition: 0, /** * Used for non-styling code to examine what the style or className string is: * styles: ['width', '100px', 0, 'opacity', null, 0, 'height', '200px', 0] * => initialStyles[CachedStringValuePosition] = 'width:100px;height:200px'; * classes: ['foo', true, 0, 'bar', false, 0, 'baz', true, 0] * => initialClasses[CachedStringValuePosition] = 'foo bar'; * * Note that this value is `null` by default and it will only be populated * once `getInitialStyleStringValue` or `getInitialClassNameValue` is executed. */ CachedStringValuePosition: 1, /** * Where the style or class values start in the tuple */ KeyValueStartPosition: 2, /** * The offset value (index + offset) for the property value for each style/class entry */ PropOffset: 0, /** * The offset value (index + offset) for the style/class value for each style/class entry */ ValueOffset: 1, /** * The offset value (index + offset) for the style/class directive owner for each style/class entry */ DirectiveOwnerOffset: 2, /** * The first bit set aside to mark if the initial style was already rendere */ AppliedFlagBitPosition: 0, AppliedFlagBitLength: 1, /** * The total size for each style/class entry (prop + value + directiveOwner) */ Size: 3, }; export { InitialStylingValuesIndex }; /** * An array located in the StylingContext that houses all directive instances and additional * data about them. * * Each entry in this array represents a source of where style/class binding values could * come from. By default, there is always at least one directive here with a null value and * that represents bindings that live directly on an element in the template (not host bindings). * * Each successive entry in the array is an actual instance of a directive as well as some * additional info about that entry. * * An entry within this array has the following values: * [0] = The instance of the directive (the first entry is null because its reserved for the * template) * [1] = The pointer that tells where the single styling (stuff like [class.foo] and [style.prop]) * offset values are located. This value will allow for a binding instruction to find exactly * where a style is located. * [2] = Whether or not the directive has any styling values that are dirty. This is used as * reference within the `renderStyling` function to decide whether to skip iterating * through the context when rendering is executed. * [3] = The styleSanitizer instance that is assigned to the directive. Although it's unlikely, * a directive could introduce its own special style sanitizer and for this reach each * directive will get its own space for it (if null then the very first sanitizer is used). * * Each time a new directive is added it will insert these four values at the end of the array. * When this array is examined then the resulting directiveIndex will be resolved by dividing the * index value by the size of the array entries (so if DirA is at spot 8 then its index will be 2). * @record */ export function DirectiveRegistryValues() { } if (false) { /* Skipping unnamed member: [DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset]: number;*/ /* Skipping unnamed member: [DirectiveRegistryValuesIndex.StyleSanitizerOffset]: StyleSanitizeFn|null;*/ } /** @enum {number} */ const DirectiveRegistryValuesIndex = { SinglePropValuesIndexOffset: 0, StyleSanitizerOffset: 1, Size: 2, }; export { DirectiveRegistryValuesIndex }; /** * An array that contains the index pointer values for every single styling property * that exists in the context and for every directive. It also contains the total * single styles and single classes that exists in the context as the first two values. * * Let's say we have the following template code: * * <div [style.width]="myWidth" * [style.height]="myHeight" * [class.flipped]="flipClass" * directive-with-opacity> * directive-with-foo-bar-classes> * * We have two directive and template-binding sources, * 2 + 1 styles and 1 + 1 classes. When the bindings are * registered the SinglePropOffsets array will look like so: * * s_0/c_0 = template directive value * s_1/c_1 = directive one (directive-with-opacity) * s_2/c_2 = directive two (directive-with-foo-bar-classes) * * [3, 2, 2, 1, s_00, s01, c_01, 1, 0, s_10, 0, 1, c_20 * @record */ export function SinglePropOffsetValues() { } if (false) { /* Skipping unnamed member: [SinglePropOffsetValuesIndex.StylesCountPosition]: number;*/ /* Skipping unnamed member: [SinglePropOffsetValuesIndex.ClassesCountPosition]: number;*/ } /** @enum {number} */ const SinglePropOffsetValuesIndex = { StylesCountPosition: 0, ClassesCountPosition: 1, ValueStartPosition: 2, }; export { SinglePropOffsetValuesIndex }; /** * Used a reference for all multi styling values (values that are assigned via the * `[style]` and `[class]` bindings). * * Single-styling properties (things set via `[style.prop]` and `[class.name]` bindings) * are not handled using the same approach as multi-styling bindings (such as `[style]` * `[class]` bindings). * * Multi-styling bindings rely on a diffing algorithm to figure out what properties have been added, * removed and modified. Multi-styling properties are also evaluated across directives--which means * that Angular supports having multiple directives all write to the same `[style]` and `[class]` * bindings (using host bindings) even if the `[style]` and/or `[class]` bindings are being written * to on the template element. * * All multi-styling values that are written to an element (whether it be from the template or any * directives attached to the element) are all written into the `MapBasedOffsetValues` array. (Note * that there are two arrays: one for styles and another for classes.) * * This array is shaped in the following way: * * [0] = The total amount of unique multi-style or multi-class entries that exist currently in the * context. * [1+] = Contains an entry of four values ... Each entry is a value assigned by a * `[style]`/`[class]` * binding (we call this a **source**). * * An example entry looks like so (at a given `i` index): * [i + 0] = Whether or not the value is dirty * * [i + 1] = The index of where the map-based values * (for this **source**) start within the context * * [i + 2] = The untouched, last set value of the binding * * [i + 3] = The total amount of unqiue binding values that were * extracted and set into the context. (Note that this value does * not reflect the total amount of values within the binding * value (since it's a map), but instead reflects the total values * that were not used by another directive). * * Each time a directive (or template) writes a value to a `[class]`/`[style]` binding then the * styling diffing algorithm code will decide whether or not to update the value based on the * following rules: * * 1. If a more important directive (either the template or a directive that was registered * beforehand) has written a specific styling value into the context then any follow-up styling * values (set by another directive via its `[style]` and/or `[class]` host binding) will not be * able to set it. This is because the former directive has priorty. * 2. Only if a former directive has set a specific styling value to null (whether by actually * setting it to null or not including it in is map value) then a less imporatant directive can * set its own value. * * ## How the map-based styling algorithm updates itself * @record */ export function MapBasedOffsetValues() { } if (false) { /* Skipping unnamed member: [MapBasedOffsetValuesIndex.EntriesCountPosition]: number;*/ } /** @enum {number} */ const MapBasedOffsetValuesIndex = { EntriesCountPosition: 0, ValuesStartPosition: 1, DirtyFlagOffset: 0, PositionStartOffset: 1, ValueOffset: 2, ValueCountOffset: 3, Size: 4, }; export { MapBasedOffsetValuesIndex }; /** @enum {number} */ const StylingFlags = { // Implies no configurations None: 0, // Whether or not the entry or context itself is dirty Dirty: 1, // Whether or not this is a class-based assignment Class: 2, // Whether or not a sanitizer was applied to this property Sanitize: 4, // Whether or not any player builders within need to produce new players PlayerBuildersDirty: 8, // The max amount of bits used to represent these configuration values BindingAllocationLocked: 16, BitCountSize: 5, // There are only five bits here BitMask: 31, }; export { StylingFlags }; /** @enum {number} */ const StylingIndex = { // Position of where the initial styles are stored in the styling context // This index must align with HOST, see interfaces/view.ts ElementPosition: 0, // Index of location where the start of single properties are stored. (`updateStyleProp`) MasterFlagPosition: 1, // Position of where the registered directives exist for this styling context DirectiveRegistryPosition: 2, // Position of where the initial styles are stored in the styling context InitialStyleValuesPosition: 3, InitialClassValuesPosition: 4, // Index of location where the class index offset value is located SinglePropOffsetPositions: 5, // Position of where the last string-based CSS class value was stored (or a cached version of the // initial styles when a [class] directive is present) CachedMultiClasses: 6, // Position of where the last string-based CSS class value was stored CachedMultiStyles: 7, // Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue // Position of where the initial styles are stored in the styling context HostInstructionsQueue: 8, PlayerContext: 9, // Location of single (prop) value entries are stored within the context SingleStylesStartPosition: 10, FlagsOffset: 0, PropertyOffset: 1, ValueOffset: 2, PlayerBuilderIndexOffset: 3, // Size of each multi or single entry (flag + prop + value + playerBuilderIndex) Size: 4, // Each flag has a binary digit length of this value BitCountSize: 14, // The binary digit value as a mask BitMask: 16383, }; export { StylingIndex }; /** @enum {number} */ const DirectiveOwnerAndPlayerBuilderIndex = { BitCountSize: 16, BitMask: 65535, }; export { DirectiveOwnerAndPlayerBuilderIndex }; /** * The default directive styling index value for template-based bindings. * * All host-level bindings (e.g. `hostStyleProp` and `hostClassMap`) are * assigned a directive styling index value based on the current directive * uniqueId and the directive super-class inheritance depth. But for template * bindings they always have the same directive styling index value. * @type {?} */ export const DEFAULT_TEMPLATE_DIRECTIVE_INDEX = 0; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3R5bGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvcmUvc3JjL3JlbmRlcjMvaW50ZXJmYWNlcy9zdHlsaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXdRQSxvQ0FvRUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnREQsMkNBQTRGOzs7Ozs7O0lBTTFGLHVDQUF3QztJQUN4QyxzQkFBdUI7SUFDdkIsdUJBQXdCO0lBQ3hCLHNCQUF1QjtJQUN2QixlQUFnQjtJQUNoQixPQUFROzs7Ozs7Ozs7OztBQVVWLDBDQUdDOzs7Ozs7Ozs7SUEwR0M7O09BRUc7SUFDSCwyQkFBNEI7SUFFNUI7Ozs7Ozs7OztPQVNHO0lBQ0gsNEJBQTZCO0lBRTdCOztPQUVHO0lBQ0gsd0JBQXlCO0lBRXpCOztPQUVHO0lBQ0gsYUFBYztJQUVkOztPQUVHO0lBQ0gsY0FBZTtJQUVmOzs7T0FHRztJQUNILHVCQUF3QjtJQUV4Qjs7T0FFRztJQUNILHlCQUE0QjtJQUM1Qix1QkFBd0I7SUFFeEI7O09BRUc7SUFDSCxPQUFROzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQStCViw2Q0FHQzs7Ozs7Ozs7O0lBT0MsOEJBQStCO0lBQy9CLHVCQUF3QjtJQUN4QixPQUFROzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUEwQlYsNENBR0M7Ozs7Ozs7OztJQU9DLHNCQUF1QjtJQUN2Qix1QkFBd0I7SUFDeEIscUJBQXNCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUR4QiwwQ0FFQzs7Ozs7OztJQUdDLHVCQUF3QjtJQUN4QixzQkFBdUI7SUFDdkIsa0JBQW1CO0lBQ25CLHNCQUF1QjtJQUN2QixjQUFlO0lBQ2YsbUJBQW9CO0lBQ3BCLE9BQVE7Ozs7O0lBUVIsNEJBQTRCO0lBQzVCLE9BQWM7SUFDZCxzREFBc0Q7SUFDdEQsUUFBZTtJQUNmLGtEQUFrRDtJQUNsRCxRQUFlO0lBQ2YsMERBQTBEO0lBQzFELFdBQWtCO0lBQ2xCLHdFQUF3RTtJQUN4RSxzQkFBNkI7SUFDN0Isc0VBQXNFO0lBQ3RFLDJCQUFpQztJQUNqQyxlQUFnQjtJQUNoQixnQ0FBZ0M7SUFDaEMsV0FBaUI7Ozs7O0lBS2pCLHlFQUF5RTtJQUN6RSwwREFBMEQ7SUFDMUQsa0JBQW1CO0lBQ25CLHlGQUF5RjtJQUN6RixxQkFBc0I7SUFDdEIsNkVBQTZFO0lBQzdFLDRCQUE2QjtJQUM3Qix5RUFBeUU7SUFDekUsNkJBQThCO0lBQzlCLDZCQUE4QjtJQUM5QixrRUFBa0U7SUFDbEUsNEJBQTZCO0lBQzdCLGlHQUFpRztJQUNqRyxzREFBc0Q7SUFDdEQscUJBQXNCO0lBQ3RCLHFFQUFxRTtJQUNyRSxvQkFBcUI7SUFDckIsaUdBQWlHO0lBQ2pHLHlFQUF5RTtJQUN6RSx3QkFBeUI7SUFDekIsZ0JBQWlCO0lBQ2pCLHdFQUF3RTtJQUN4RSw2QkFBOEI7SUFDOUIsY0FBZTtJQUNmLGlCQUFrQjtJQUNsQixjQUFlO0lBQ2YsMkJBQTRCO0lBQzVCLGdGQUFnRjtJQUNoRixPQUFRO0lBQ1Isb0RBQW9EO0lBQ3BELGdCQUFpQjtJQUNqQixtQ0FBbUM7SUFDbkMsY0FBMEI7Ozs7O0lBWTFCLGdCQUFpQjtJQUNqQixjQUE0Qjs7Ozs7Ozs7Ozs7O0FBVzlCLE1BQU0sT0FBTyxnQ0FBZ0MsR0FBRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuaW1wb3J0IHtTdHlsZVNhbml0aXplRm59IGZyb20gJy4uLy4uL3Nhbml0aXphdGlvbi9zdHlsZV9zYW5pdGl6ZXInO1xuaW1wb3J0IHtSRWxlbWVudH0gZnJvbSAnLi4vaW50ZXJmYWNlcy9yZW5kZXJlcic7XG5pbXBvcnQge0xDb250YWluZXJ9IGZyb20gJy4vY29udGFpbmVyJztcbmltcG9ydCB7UGxheWVyQ29udGV4dH0gZnJvbSAnLi9wbGF5ZXInO1xuaW1wb3J0IHtMVmlld30gZnJvbSAnLi92aWV3JztcblxuXG4vKipcbiAqIFRoZSBzdHlsaW5nIGNvbnRleHQgYWN0cyBhcyBhIHN0eWxpbmcgbWFuaWZlc3QgKHNoYXBlZCBhcyBhbiBhcnJheSkgZm9yIGRldGVybWluaW5nIHdoaWNoXG4gKiBzdHlsaW5nIHByb3BlcnRpZXMgaGF2ZSBiZWVuIGFzc2lnbmVkIHZpYSB0aGUgcHJvdmlkZWQgYHVwZGF0ZVN0eWxpbmdNYXBgLCBgdXBkYXRlU3R5bGVQcm9wYFxuICogYW5kIGB1cGRhdGVDbGFzc1Byb3BgIGZ1bmN0aW9ucy4gSXQgYWxzbyBzdG9yZXMgdGhlIHN0YXRpYyBzdHlsZS9jbGFzcyB2YWx1ZXMgdGhhdCB3ZXJlXG4gKiBleHRyYWN0ZWQgZnJvbSB0aGUgdGVtcGxhdGUgYnkgdGhlIGNvbXBpbGVyLlxuICpcbiAqIEEgY29udGV4dCBpcyBjcmVhdGVkIGJ5IEFuZ3VsYXIgd2hlbjpcbiAqIDEuIEFuIGVsZW1lbnQgY29udGFpbnMgc3RhdGljIHN0eWxpbmcgdmFsdWVzIChsaWtlIHN0eWxlPVwiLi4uXCIgb3IgY2xhc3M9XCIuLi5cIilcbiAqIDIuIEFuIGVsZW1lbnQgY29udGFpbnMgc2luZ2xlIHByb3BlcnR5IGJpbmRpbmcgdmFsdWVzIChsaWtlIFtzdHlsZS5wcm9wXT1cInhcIiBvclxuICogW2NsYXNzLnByb3BdPVwieVwiKVxuICogMy4gQW4gZWxlbWVudCBjb250YWlucyBtdWx0aSBwcm9wZXJ0eSBiaW5kaW5nIHZhbHVlcyAobGlrZSBbc3R5bGVdPVwieFwiIG9yIFtjbGFzc109XCJ5XCIpXG4gKiA0LiBBIGRpcmVjdGl2ZSBjb250YWlucyBob3N0IGJpbmRpbmdzIGZvciBzdGF0aWMsIHNpbmdsZSBvciBtdWx0aSBzdHlsaW5nIHByb3BlcnRpZXMvYmluZGluZ3MuXG4gKiA1LiBBbiBhbmltYXRpb24gcGxheWVyIGlzIGFkZGVkIHRvIGFuIGVsZW1lbnQgdmlhIGBhZGRQbGF5ZXJgXG4gKlxuICogTm90ZSB0aGF0IGV2ZW4gaWYgYW4gZWxlbWVudCBjb250YWlucyBzdGF0aWMgc3R5bGluZyB0aGVuIHRoaXMgY29udGV4dCB3aWxsIGJlIGNyZWF0ZWQgYW5kXG4gKiBhdHRhY2hlZCB0byBpdC4gVGhlIHJlYXNvbiB3aHkgdGhpcyBoYXBwZW5zIChpbnN0ZWFkIG9mIHRyZWF0aW5nIHN0eWxlcy9jbGFzc2VzIGFzIHJlZ3VsYXJcbiAqIEhUTUwgYXR0cmlidXRlcykgaXMgYmVjYXVzZSB0aGUgc3R5bGUvY2xhc3MgYmluZGluZ3MgbXVzdCBiZSBhYmxlIHRvIGRlZmF1bHQgdGhlbXNlbHZlcyBiYWNrXG4gKiB0byB0aGVpciByZXNwZWN0aXZlIHN0YXRpYyB2YWx1ZXMgd2hlbiB0aGV5IGFyZSBzZXQgdG8gbnVsbC5cbiAqXG4gKiBTYXkgZm9yIGV4YW1wbGUgd2UgaGF2ZSB0aGlzOlxuICogYGBgXG4gKiA8IS0tIHdoZW4gYG15V2lkdGhFeHA9bnVsbGAgdGhlbiBhIHdpZHRoIG9mIGAxMDBweGBcbiAqICAgICAgd2lsbCBiZSB1c2VkIGEgZGVmYXVsdCB2YWx1ZSBmb3Igd2lkdGggLS0+XG4gKiA8ZGl2IHN0eWxlPVwid2lkdGg6MTAwcHhcIiBbc3R5bGUud2lkdGhdPVwibXlXaWR0aEV4cFwiPjwvZGl2PlxuICogYGBgXG4gKlxuICogRXZlbiBpbiB0aGUgc2l0dWF0aW9uIHdoZXJlIHRoZXJlIGFyZSBubyBiaW5kaW5ncywgdGhlIHN0YXRpYyBzdHlsaW5nIGlzIHN0aWxsIHBsYWNlZCBpbnRvIHRoZVxuICogY29udGV4dCBiZWNhdXNlIHRoZXJlIG1heSBiZSBhbm90aGVyIGRpcmVjdGl2ZSBvbiB0aGUgc2FtZSBlbGVtZW50IHRoYXQgaGFzIHN0eWxpbmcuXG4gKlxuICogV2hlbiBBbmd1bGFyIGluaXRpYWxpemVzIHN0eWxpbmcgZGF0YSBmb3IgYW4gZWxlbWVudCB0aGVuIGl0IHdpbGwgZmlyc3QgcmVnaXN0ZXIgdGhlIHN0YXRpY1xuICogc3R5bGluZyB2YWx1ZXMgb24gdGhlIGVsZW1lbnQgdXNpbmcgb25lIG9mIHRoZXNlIHR3byBpbnN0cnVjdGlvbnM6XG4gKlxuICogMS4gZWxlbWVudFN0YXJ0IG9yIGVsZW1lbnQgKHdpdGhpbiB0aGUgdGVtcGxhdGUgZnVuY3Rpb24gb2YgYSBjb21wb25lbnQpXG4gKiAyLiBlbGVtZW50SG9zdEF0dHJzIChmb3IgZGlyZWN0aXZlIGhvc3QgYmluZGluZ3MpXG4gKlxuICogSW4gZWl0aGVyIGNhc2UsIGEgc3R5bGluZyBjb250ZXh0IHdpbGwgYmUgY3JlYXRlZCBhbmQgc3RvcmVkIHdpdGhpbiBhbiBlbGVtZW50J3MgYExWaWV3RGF0YWAuXG4gKiBPbmNlIHRoZSBzdHlsaW5nIGNvbnRleHQgaXMgY3JlYXRlZCB0aGVuIHNpbmdsZSBhbmQgbXVsdGkgcHJvcGVydGllcyBjYW4gYmUgc3RvcmVkIHdpdGhpbiBpdC5cbiAqIEZvciB0aGlzIHRvIGhhcHBlbiwgdGhlIGZvbGxvd2luZyBmdW5jdGlvbiBuZWVkcyB0byBiZSBjYWxsZWQ6XG4gKlxuICogYHN0eWxpbmdgIChjYWxsZWQgd2l0aCBzdHlsZSBwcm9wZXJ0aWVzLCBjbGFzcyBwcm9wZXJ0aWVzIGFuZCBhIHNhbml0aXplciArIGEgZGlyZWN0aXZlXG4gKiBpbnN0YW5jZSkuXG4gKlxuICogV2hlbiB0aGlzIGluc3RydWN0aW9uIGlzIGNhbGxlZCBpdCB3aWxsIHBvcHVsYXRlIHRoZSBzdHlsaW5nIGNvbnRleHQgd2l0aCB0aGUgcHJvdmlkZWQgc3R5bGVcbiAqIGFuZCBjbGFzcyBuYW1lcyBpbnRvIHRoZSBjb250ZXh0LlxuICpcbiAqIFRoZSBjb250ZXh0IGl0c2VsZiBsb29rcyBsaWtlIHRoaXM6XG4gKlxuICogY29udGV4dCA9IFtcbiAqICAgLy8gMC04OiBoZWFkZXIgdmFsdWVzIChhYm91dCA4IGVudHJpZXMgb2YgY29uZmlndXJhdGlvbiBkYXRhKVxuICogICAvLyA5KzogdGhpcyBpcyB3aGVyZSBlYWNoIGVudHJ5IGlzIHN0b3JlZDpcbiAqIF1cbiAqXG4gKiBMZXQncyBzYXkgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIHRlbXBsYXRlIGNvZGU6XG4gKlxuICogYGBgXG4gKiA8ZGl2IGNsYXNzPVwiZm9vIGJhclwiXG4gKiAgICAgIHN0eWxlPVwid2lkdGg6MjAwcHg7IGNvbG9yOnJlZFwiXG4gKiAgICAgIFtzdHlsZS53aWR0aF09XCJteVdpZHRoRXhwXCJcbiAqICAgICAgW3N0eWxlLmhlaWdodF09XCJteUhlaWdodEV4cFwiXG4gKiAgICAgIFtjbGFzcy5iYXpdPVwibXlCYXpFeHBcIj5cbiAqIGBgYFxuICpcbiAqIFRoZSBjb250ZXh0IGdlbmVyYXRlZCBmcm9tIHRoZXNlIHZhbHVlcyB3aWxsIGxvb2sgbGlrZSB0aGlzIChub3RlIHRoYXRcbiAqIGZvciBlYWNoIGJpbmRpbmcgbmFtZSAodGhlIGNsYXNzIGFuZCBzdHlsZSBiaW5kaW5ncykgdGhlIHZhbHVlcyB3aWxsXG4gKiBiZSBpbnNlcnRlZCB0d2ljZSBpbnRvIHRoZSBhcnJheSAob25jZSBmb3Igc2luZ2xlIHByb3BlcnR5IGVudHJpZXMgYW5kXG4gKiBhZ2FpbiBmb3IgbXVsdGkgcHJvcGVydHkgZW50cmllcykuXG4gKlxuICogY29udGV4dCA9IFtcbiAqICAgLy8gMC04OiBoZWFkZXIgdmFsdWVzIChhYm91dCA4IGVudHJpZXMgb2YgY29uZmlndXJhdGlvbiBkYXRhKVxuICogICAvLyA5KzogdGhpcyBpcyB3aGVyZSBlYWNoIGVudHJ5IGlzIHN0b3JlZDpcbiAqXG4gKiAgIC8vIFNJTkdMRSBQUk9QRVJUSUVTXG4gKiAgIGNvbmZpZ0ZvcldpZHRoLFxuICogICAnd2lkdGgnXG4gKiAgIG15V2lkdGhFeHAsIC8vIHRoZSBiaW5kaW5nIHZhbHVlIG5vdCB0aGUgYmluZGluZyBpdHNlbGZcbiAqICAgMCwgLy8gdGhlIGRpcmVjdGl2ZSBvd25lclxuICpcbiAqICAgY29uZmlnRm9ySGVpZ2h0LFxuICogICAnaGVpZ2h0J1xuICogICBteUhlaWdodEV4cCwgLy8gdGhlIGJpbmRpbmcgdmFsdWUgbm90IHRoZSBiaW5kaW5nIGl0c2VsZlxuICogICAwLCAvLyB0aGUgZGlyZWN0aXZlIG93bmVyXG4gKlxuICogICBjb25maWdGb3JCYXpDbGFzcyxcbiAqICAgJ2JhelxuICogICBteUJhekNsYXNzRXhwLCAvLyB0aGUgYmluZGluZyB2YWx1ZSBub3QgdGhlIGJpbmRpbmcgaXRzZWxmXG4gKiAgIDAsIC8vIHRoZSBkaXJlY3RpdmUgb3duZXJcbiAqXG4gKiAgIC8vIE1VTFRJIFBST1BFUlRJRVNcbiAqICAgY29uZmlnRm9yV2lkdGgsXG4gKiAgICd3aWR0aCdcbiAqICAgbXlXaWR0aEV4cCwgLy8gdGhlIGJpbmRpbmcgdmFsdWUgbm90IHRoZSBiaW5kaW5nIGl0c2VsZlxuICogICAwLCAvLyB0aGUgZGlyZWN0aXZlIG93bmVyXG4gKlxuICogICBjb25maWdGb3JIZWlnaHQsXG4gKiAgICdoZWlnaHQnXG4gKiAgIG15SGVpZ2h0RXhwLCAvLyB0aGUgYmluZGluZyB2YWx1ZSBub3QgdGhlIGJpbmRpbmcgaXRzZWxmXG4gKiAgIDAsIC8vIHRoZSBkaXJlY3RpdmUgb3duZXJcbiAqXG4gKiAgIGNvbmZpZ0ZvckJhekNsYXNzLFxuICogICAnYmF6XG4gKiAgIG15QmF6Q2xhc3NFeHAsIC8vIHRoZSBiaW5kaW5nIHZhbHVlIG5vdCB0aGUgYmluZGluZyBpdHNlbGZcbiAqICAgMCwgLy8gdGhlIGRpcmVjdGl2ZSBvd25lclxuICogXVxuICpcbiAqIFRoZSBjb25maWd1cmF0aW9uIHZhbHVlcyBhcmUgbGVmdCBvdXQgb2YgdGhlIGV4YW1wbGUgYWJvdmUgYmVjYXVzZVxuICogdGhlIG9yZGVyaW5nIG9mIHRoZW0gY291bGQgY2hhbmdlIGJldHdlZW4gY29kZSBwYXRjaGVzLiBQbGVhc2UgcmVhZCB0aGVcbiAqIGRvY3VtZW50YXRpb24gYmVsb3cgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmQgb2Ygd2hhdCB0aGUgY29uZmlndXJhdGlvblxuICogdmFsdWVzIGFyZSBhbmQgaG93IHRoZXkgd29yay5cbiAqXG4gKiBFYWNoIHRpbWUgYSBiaW5kaW5nIHByb3BlcnR5IGlzIHVwZGF0ZWQgKHdoZXRoZXIgaXQgYmUgdGhyb3VnaCBhIHNpbmdsZVxuICogcHJvcGVydHkgaW5zdHJ1Y3Rpb24gbGlrZSBgc3R5bGVQcm9wYCwgYGNsYXNzUHJvcGAsXG4gKiBgc3R5bGVNYXBgIG9yIGBjbGFzc01hcGApIHRoZW4gdGhlIHZhbHVlcyBpbiB0aGUgY29udGV4dCB3aWxsIGJlIHVwZGF0ZWQgYXNcbiAqIHdlbGwuXG4gKlxuICogSWYgZm9yIGV4YW1wbGUgYFtzdHlsZS53aWR0aF1gIHVwZGF0ZXMgdG8gYDU1NXB4YCB0aGVuIGl0cyB2YWx1ZSB3aWxsIGJlIHJlZmxlY3RlZFxuICogaW4gdGhlIGNvbnRleHQgYXMgc286XG4gKlxuICogY29udGV4dCA9IFtcbiAqICAgLy8gLi4uXG4gKiAgIGNvbmZpZ0ZvcldpZHRoLCAvLyB0aGlzIHdpbGwgYmUgbWFya2VkIERJUlRZXG4gKiAgICd3aWR0aCdcbiAqICAgJzU1NXB4JyxcbiAqICAgMCxcbiAqICAgLy8uLlxuICogXVxuICpcbiAqIFRoZSBjb250ZXh0IGFuZCBkaXJlY3RpdmUgZGF0YSB3aWxsIGFsc28gYmUgbWFya2VkIGRpcnR5LlxuICpcbiAqIERlc3BpdGUgdGhlIGNvbnRleHQgYmVpbmcgdXBkYXRlZCwgbm90aGluZyBoYXMgYmVlbiByZW5kZXJlZCBvbiBzY3JlZW4gKG5vdCBzdHlsZXMgb3JcbiAqIGNsYXNzZXMgaGF2ZSBiZWVuIHNldCBvbiB0aGUgZWxlbWVudCkuIFRvIGtpY2sgb2ZmIHJlbmRlcmluZyBmb3IgYW4gZWxlbWVudCB0aGUgZm9sbG93aW5nXG4gKiBmdW5jdGlvbiBuZWVkcyB0byBiZSBydW4gYHN0eWxpbmdBcHBseWAuXG4gKlxuICogYHN0eWxpbmdBcHBseWAgd2lsbCBydW4gdGhyb3VnaCB0aGUgY29udGV4dCBhbmQgZmluZCBlYWNoIGRpcnR5IHZhbHVlIGFuZCByZW5kZXIgdGhlbSBvbnRvXG4gKiB0aGUgZWxlbWVudC4gT25jZSBjb21wbGV0ZSwgYWxsIHN0eWxlcy9jbGFzc2VzIHdpbGwgYmUgc2V0IHRvIGNsZWFuLiBCZWNhdXNlIG9mIHRoaXMsIHRoZSByZW5kZXJcbiAqIGZ1bmN0aW9uIHdpbGwgbm93IGtub3cgbm90IHRvIHJlcnVuIGl0c2VsZiBhZ2FpbiBpZiBjYWxsZWQgYWdhaW4gdW5sZXNzIG5ldyBzdHlsZS9jbGFzcyB2YWx1ZXNcbiAqIGhhdmUgY2hhbmdlZC5cbiAqXG4gKiAjIyBEaXJlY3RpdmVzXG4gKiBEaXJlY3RpdmUgc3R5bGUvY2xhc3MgdmFsdWVzICh3aGljaCBhcmUgcHJvdmlkZWQgdGhyb3VnaCBob3N0IGJpbmRpbmdzKSBhcmUgYWxzbyBzdXBwb3J0ZWQgYW5kXG4gKiBob3VzZWQgd2l0aGluIHRoZSBzYW1lIHN0eWxpbmcgY29udGV4dCBhcyBhcmUgdGVtcGxhdGUtbGV2ZWwgc3R5bGUvY2xhc3MgcHJvcGVydGllcy9iaW5kaW5nc1xuICogU28gbG9uZyBhcyB0aGV5IGFyZSBhbGwgYXNzaWduZWQgdG8gdGhlIHNhbWUgZWxlbWVudCwgYm90aCBkaXJlY3RpdmUtbGV2ZWwgYW5kIHRlbXBsYXRlLWxldmVsXG4gKiBzdHlsaW5nIGJpbmRpbmdzIHNoYXJlIHRoZSBzYW1lIGNvbnRleHQuXG4gKlxuICogRWFjaCBvZiB0aGUgZm9sbG93aW5nIGluc3RydWN0aW9ucyBzdXBwb3J0cyBhY2NlcHRpbmcgYSBkaXJlY3RpdmUgaW5zdGFuY2UgYXMgYW4gaW5wdXQgcGFyYW1ldGVyOlxuICpcbiAqIC0gYGVsZW1lbnRIb3N0QXR0cnNgXG4gKiAtIGBzdHlsaW5nYFxuICogLSBgc3R5bGVQcm9wYFxuICogLSBgY2xhc3NQcm9wYFxuICogLSBgc3R5bGVNYXBgXG4gKiAtIGBjbGFzc01hcGBcbiAqIC0gYHN0eWxpbmdBcHBseWBcbiAqXG4gKiBFYWNoIHRpbWUgYSBkaXJlY3RpdmUgdmFsdWUgaXMgcGFzc2VkIGluLCBpdCB3aWxsIGJlIGNvbnZlcnRlZCBpbnRvIGFuIGluZGV4IGJ5IGV4YW1pbmluZyB0aGVcbiAqIGRpcmVjdGl2ZSByZWdpc3RyeSAod2hpY2ggbGl2ZXMgaW4gdGhlIGNvbnRleHQgY29uZmlndXJhdGlvbiBhcmVhKS4gVGhlIGluZGV4IGlzIHRoZW4gdXNlZFxuICogdG8gaGVscCBzaW5nbGUgc3R5bGUgcHJvcGVydGllcyBmaWd1cmUgb3V0IHdoZXJlIGEgdmFsdWUgaXMgbG9jYXRlZCBpbiB0aGUgY29udGV4dC5cbiAqXG4gKlxuICogIyMgU2luZ2xlLWxldmVsIHN0eWxpbmcgYmluZGluZ3MgKGBbc3R5bGUucHJvcF1gIGFuZCBgW2NsYXNzLm5hbWVdYClcbiAqXG4gKiBCb3RoIGBbc3R5bGUucHJvcF1gIGFuZCBgW2NsYXNzLm5hbWVdYCBiaW5kaW5ncyBhcmUgcnVuIHRocm91Z2ggdGhlIGB1cGRhdGVTdHlsZVByb3BgXG4gKiBhbmQgYHVwZGF0ZUNsYXNzUHJvcGAgZnVuY3Rpb25zIHJlc3BlY3RpdmVseS4gVGhleSB3b3JrIGJ5IGV4YW1pbmluZyB0aGUgcHJvdmlkZWRcbiAqIGBvZmZzZXRgIHZhbHVlIGFuZCBhcmUgYWJsZSB0byBsb2NhdGUgdGhlIGV4YWN0IHNwb3QgaW4gdGhlIGNvbnRleHQgd2hlcmUgdGhlXG4gKiBtYXRjaGluZyBzdHlsZSBpcyBsb2NhdGVkLlxuICpcbiAqIEJvdGggYFtzdHlsZS5wcm9wXWAgYW5kIGBbY2xhc3MubmFtZV1gIGJpbmRpbmdzIGFyZSBhYmxlIHRvIHByb2Nlc3MgdGhlc2UgdmFsdWVzXG4gKiBmcm9tIGRpcmVjdGl2ZSBob3N0IGJpbmRpbmdzLiBXaGVuIGV2YWx1YXRlZCAoZnJvbSB0aGUgaG9zdCBiaW5kaW5nIGZ1bmN0aW9uKSB0aGVcbiAqIGBkaXJlY3RpdmVSZWZgIHZhbHVlIGlzIHRoZW4gcGFzc2VkIGluLlxuICpcbiAqIElmIHR3byBkaXJlY3RpdmVzIG9yIGEgZGlyZWN0aXZlICsgYSB0ZW1wbGF0ZSBiaW5kaW5nIGJvdGggd3JpdGUgdG8gdGhlIHNhbWUgc3R5bGUvY2xhc3NcbiAqIGJpbmRpbmcgdGhlbiB0aGUgc3R5bGluZyBjb250ZXh0IGNvZGUgd2lsbCBkZWNpZGUgd2hpY2ggb25lIHdpbnMgYmFzZWQgb24gdGhlIGZvbGxvd2luZ1xuICogcnVsZTpcbiAqXG4gKiAxLiBJZiB0aGUgdGVtcGxhdGUgYmluZGluZyBoYXMgYSB2YWx1ZSB0aGVuIGl0IGFsd2F5cyB3aW5zXG4gKiAyLiBPdGhlcndpc2Ugd2hpY2hldmVyIGZpcnN0LXJlZ2lzdGVyZWQgZGlyZWN0aXZlIHRoYXQgaGFzIHRoYXQgdmFsdWUgZmlyc3Qgd2lsbCB3aW5cbiAqXG4gKiBUaGUgY29kZSBleGFtcGxlIGhlbHBzIG1ha2UgdGhpcyBjbGVhcjpcbiAqXG4gKiBgYGBcbiAqIDwhLS1cbiAqIDxkaXYgW3N0eWxlLndpZHRoXT1cIm15V2lkdGhcIlxuICogICAgICBbbXktd2lkdGgtZGlyZWN0aXZlXT1cIic2MDBweCdcIj5cbiAqIC0tPlxuICpcbiAqIEBEaXJlY3RpdmUoe1xuICogIHNlbGVjdG9yOiAnW215LXdpZHRoLWRpcmVjdGl2ZSddXG4gKiB9KVxuICogY2xhc3MgTXlXaWR0aERpcmVjdGl2ZSB7XG4gKiAgIEBJbnB1dCgnbXktd2lkdGgtZGlyZWN0aXZlJylcbiAqICAgQEhvc3RCaW5kaW5nKCdzdHlsZS53aWR0aCcpXG4gKiAgIHB1YmxpYyB3aWR0aCA9IG51bGw7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBTaW5jZSB0aGVyZSBpcyBhIHN0eWxlIGJpbmRpbmcgZm9yIHdpZHRoIHByZXNlbnQgb24gdGhlIGVsZW1lbnQgKGBbc3R5bGUud2lkdGhdYCkgdGhlblxuICogaXQgd2lsbCBhbHdheXMgd2luIG92ZXIgdGhlIHdpZHRoIGJpbmRpbmcgdGhhdCBpcyBwcmVzZW50IGFzIGEgaG9zdCBiaW5kaW5nIHdpdGhpblxuICogdGhlIGBNeVdpZHRoRGlyZWN0aXZlYC4gSG93ZXZlciwgaWYgYFtzdHlsZS53aWR0aF1gIHJlbmRlcnMgYXMgYG51bGxgIChzbyBgbXlXaWR0aD1udWxsYClcbiAqIHRoZW4gdGhlIGBNeVdpZHRoRGlyZWN0aXZlYCB3aWxsIGJlIGFibGUgdG8gd3JpdGUgdG8gdGhlIGB3aWR0aGAgc3R5bGUgd2l0aGluIHRoZSBjb250ZXh0LlxuICogU2ltcGx5IHB1dCwgd2hpY2hldmVyIGRpcmVjdGl2ZSB3cml0ZXMgdG8gYSB2YWx1ZSBmaXJzdCBlbmRzIHVwIGhhdmluZyBvd25lcnNoaXAgb2YgaXQgYXNcbiAqIGxvbmcgYXMgdGhlIHRlbXBsYXRlIGRpZG4ndCBzZXQgYW55dGhpbmcuXG4gKlxuICogVGhlIHdheSBpbiB3aGljaCB0aGUgb3duZXJzaGlwIGlzIGZhY2lsaXRhdGVkIGlzIHRocm91Z2ggaW5kZXggdmFsdWUuIFRoZSBlYXJsaWVzdCBkaXJlY3RpdmVzXG4gKiBnZXQgdGhlIHNtYWxsZXN0IGluZGV4IHZhbHVlcyAod2l0aCAwIGJlaW5nIHJlc2VydmVkIGZvciB0aGUgdGVtcGxhdGUgZWxlbWVudCBiaW5kaW5ncykuIEVhY2hcbiAqIHRpbWUgYSB2YWx1ZSBpcyB3cml0dGVuIGZyb20gYSBkaXJlY3RpdmUgb3IgdGhlIHRlbXBsYXRlIGJpbmRpbmdzLCB0aGUgdmFsdWUgaXRzZWxmIGdldHNcbiAqIGFzc2lnbmVkIHRoZSBkaXJlY3RpdmUgaW5kZXggdmFsdWUgaW4gaXRzIGRhdGEuIElmIGFub3RoZXIgZGlyZWN0aXZlIHdyaXRlcyBhIHZhbHVlIGFnYWluIHRoZW5cbiAqIGl0cyBkaXJlY3RpdmUgaW5kZXggZ2V0cyBjb21wYXJlZCBhZ2FpbnN0IHRoZSBkaXJlY3RpdmUgaW5kZXggdGhhdCBleGlzdHMgb24gdGhlIGVsZW1lbnQuIE9ubHlcbiAqIHdoZW4gdGhlIG5ldyB2YWx1ZSdzIGRpcmVjdGl2ZSBpbmRleCBpcyBsZXNzIHRoYW4gdGhlIGV4aXN0aW5nIGRpcmVjdGl2ZSBpbmRleCB0aGVuIHRoZSBuZXdcbiAqIHZhbHVlIHdpbGwgYmUgd3JpdHRlbiB0byB0aGUgY29udGV4dC4gQnV0LCBpZiB0aGUgZXhpc3RpbmcgdmFsdWUgaXMgbnVsbCB0aGVuIHRoZSBuZXcgdmFsdWUgaXNcbiAqIHdyaXR0ZW4gYnkgdGhlIGxlc3MgaW1wb3J0YW50IGRpcmVjdGl2ZS5cbiAqXG4gKiBFYWNoIGRpcmVjdGl2ZSBhbHNvIGhhcyBpdHMgb3duIHNhbml0aXplciBhbmQgZGlydHkgZmxhZ3MuIFRoZXNlIHZhbHVlcyBhcmUgY29uc3VtZWQgd2l0aGluIHRoZVxuICogcmVuZGVyaW5nIGZ1bmN0aW9uLlxuICpcbiAqXG4gKiAjIyBNdWx0aS1sZXZlbCBzdHlsaW5nIGJpbmRpbmdzIChgW3N0eWxlXWAgYW5kIGBbY2xhc3NdYClcbiAqXG4gKiBNdWx0aS1sZXZlbCBzdHlsaW5nIGJpbmRpbmdzIGFyZSB0cmVhdGVkIGFzIGxlc3MgaW1wb3J0YW50IChsZXNzIHNwZWNpZmljKSBhcyBzaW5nbGUtbGV2ZWxcbiAqIGJpbmRpbmdzICh0aGluZ3MgbGlrZSBgW3N0eWxlLnByb3BdYCBhbmQgYFtjbGFzcy5uYW1lXWApLlxuICpcbiAqIE11bHRpLWxldmVsIGJpbmRpbmdzIGFyZSBzdGlsbCBhcHBsaWVkIHRvIHRoZSBjb250ZXh0IGluIGEgc2ltaWxhciB3YXkgYXMgYXJlIHNpbmdsZSBsZXZlbFxuICogYmluZGluZ3MsIGJ1dCB0aGlzIHByb2Nlc3Mgd29ya3MgYnkgZGlmZmluZyB0aGUgbmV3IG11bHRpLWxldmVsIHZhbHVlcyAod2hpY2ggYXJlIGtleS92YWx1ZVxuICogbWFwcykgYWdhaW5zdCB0aGUgZXhpc3Rpbmcgc2V0IG9mIHN0eWxlcyB0aGF0IGxpdmUgaW4gdGhlIGNvbnRleHQuIEVhY2ggdGltZSBhIG5ldyBtYXAgdmFsdWVcbiAqIGlzIGRldGVjdGVkICh2aWEgaWRlbnRpdHkgY2hlY2spIHRoZW4gaXQgd2lsbCBsb29wIHRocm91Z2ggdGhlIHZhbHVlcyBhbmQgZmlndXJlIG91dCB3aGF0XG4gKiBoYXMgY2hhbmdlZCBhbmQgcmVvcmRlciB0aGUgY29udGV4dCBhcnJheSB0byBtYXRjaCB0aGUgb3JkZXJpbmcgb2YgdGhlIGtleXMuIFRoaXMgcmVvcmRlcmluZ1xuICogb2YgdGhlIGNvbnRleHQgbWFrZXMgc3VyZSB0aGF0IGZvbGxvdy11cCB0cmF2ZXJzYWxzIG9mIHRoZSBjb250ZXh0IHdoZW4gdXBkYXRlZCBhZ2FpbnN0IHRoZVxuICoga2V5L3ZhbHVlIG1hcCBhcmUgYXMgY2xvc2UgYXMgcG9zc2libGUgdG8gbyhuKSAod2hlcmUgXCJuXCIgaXMgdGhlIHNpemUgb2YgdGhlIGtleS92YWx1ZSBtYXApLlxuICpcbiAqIElmIGEgYGRpcmVjdGl2ZVJlZmAgdmFsdWUgaXMgcGFzc2VkIGluIHRoZW4gdGhlIHN0eWxpbmcgYWxnb3JpdGhtIGNvZGUgd2lsbCB0YWtlIHRoZSBkaXJlY3RpdmUnc1xuICogcHJpb3JpdGl6YXRpb24gaW5kZXggaW50byBhY2NvdW50IGFuZCB1cGRhdGUgdGhlIHZhbHVlcyB3aXRoIHJlc3BlY3QgdG8gbW9yZSBpbXBvcnRhbnRcbiAqIGRpcmVjdGl2ZXMuIFRoaXMgbWVhbnMgdGhhdCBpZiBhIHZhbHVlIHN1Y2ggYXMgYHdpZHRoYCBpcyB1cGRhdGVkIGluIHR3byBkaWZmZXJlbnQgYFtzdHlsZV1gXG4gKiBiaW5kaW5ncyAoc2F5IG9uZSBvbiB0aGUgdGVtcGxhdGUgYW5kIGFub3RoZXIgd2l0aGluIGEgZGlyZWN0aXZlIHRoYXQgc2l0cyBvbiB0aGUgc2FtZSBlbGVtZW50KVxuICogdGhlbiB0aGUgYWxnb3JpdGhtIHdpbGwgZGVjaWRlIGhvdyB0byB1cGRhdGUgdGhlIHZhbHVlIGJhc2VkIG9uIHRoZSBmb2xsb3dpbmcgaGV1cmlzdGljOlxuICpcbiAqIDEuIElmIHRoZSB0ZW1wbGF0ZSBiaW5kaW5nIGhhcyBhIHZhbHVlIHRoZW4gaXQgYWx3YXlzIHdpbnNcbiAqIDIuIElmIG5vdCB0aGVuIHdoaWNoZXZlciBmaXJzdC1yZWdpc3RlcmVkIGRpcmVjdGl2ZSB0aGF0IGhhcyB0aGF0IHZhbHVlIGZpcnN0IHdpbGwgd2luXG4gKlxuICogSXQgd2lsbCBhbHNvIHVwZGF0ZSB0aGUgdmFsdWUgaWYgaXQgd2FzIHNldCB0byBgbnVsbGAgYnkgYSBwcmV2aW91cyBkaXJlY3RpdmUgKG9yIHRoZSB0ZW1wbGF0ZSkuXG4gKlxuICogRWFjaCB0aW1lIGEgdmFsdWUgaXMgdXBkYXRlZCAob3IgcmVtb3ZlZCkgdGhlbiB0aGUgY29udGV4dCB3aWxsIGNoYW5nZSBzaGFwZSB0byBiZXR0ZXIgbWF0Y2hcbiAqIHRoZSBvcmRlcmluZyBvZiB0aGUgc3R5bGluZyBkYXRhIGFzIHdlbGwgYXMgdGhlIG9yZGVyaW5nIG9mIGVhY2ggZGlyZWN0aXZlIHRoYXQgY29udGFpbnMgc3R5bGluZ1xuICogZGF0YS4gKFNlZSBgcGF0Y2hTdHlsaW5nTWFwSW50b0NvbnRleHRgIGluc2lkZSBvZiBjbGFzc19hbmRfc3R5bGVfYmluZGluZ3MudHMgdG8gYmV0dGVyXG4gKiB1bmRlcnN0YW5kIGhvdyB0aGlzIHdvcmtzLilcbiAqXG4gKiAjIyBSZW5kZXJpbmdcbiAqIFRoZSByZW5kZXJpbmcgbWVjaGFuaXNtICh3aGVuIHRoZSBzdHlsaW5nIGRhdGEgaXMgYXBwbGllZCBvbiBzY3JlZW4pIG9jY3VycyB2aWEgdGhlXG4gKiBgc3R5bGluZ0FwcGx5YCBmdW5jdGlvbiBhbmQgaXMgZGVzaWduZWQgdG8gcnVuIGFmdGVyICoqYWxsKiogc3R5bGluZyBmdW5jdGlvbnMgaGF2ZSBiZWVuXG4gKiBldmFsdWF0ZWQuIFRoZSByZW5kZXJpbmcgYWxnb3JpdGhtIHdpbGwgbG9vcCBvdmVyIHRoZSBjb250ZXh0IGFuZCBvbmx5IGFwcGx5IHRoZSBzdHlsZXMgdGhhdCBhcmVcbiAqIGZsYWdnZWQgYXMgZGlydHkgKGVpdGhlciBiZWNhdXNlIHRoZXkgYXJlIG5ldywgdXBkYXRlZCBvciBoYXZlIGJlZW4gcmVtb3ZlZCB2aWEgbXVsdGkgb3JcbiAqIHNpbmdsZSBiaW5kaW5ncykuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3R5bGluZ0NvbnRleHQgZXh0ZW5kc1xuICAgIEFycmF5PHtba2V5OiBzdHJpbmddOiBhbnl9fG51bWJlcnxzdHJpbmd8Ym9vbGVhbnxSRWxlbWVudHxTdHlsZVNhbml0aXplRm58UGxheWVyQ29udGV4dHxudWxsPiB7XG4gIC8qKlxuICAgKiBMb2NhdGlvbiBvZiBlbGVtZW50IHRoYXQgaXMgdXNlZCBhcyBhIHRhcmdldCBmb3IgdGhpcyBjb250ZXh0LlxuICAgKi9cbiAgW1N0eWxpbmdJbmRleC5FbGVtZW50UG9zaXRpb25dOiBMQ29udGFpbmVyfExWaWV3fFJFbGVtZW50fG51bGw7XG5cbiAgLyoqXG4gICAqIEEgbnVtZXJpYyB2YWx1ZSByZXByZXNlbnRpbmcgdGhlIGNvbmZpZ3VyYXRpb24gc3RhdHVzICh3aGV0aGVyIHRoZSBjb250ZXh0IGlzIGRpcnR5IG9yIG5vdClcbiAgICogbWl4ZWQgdG9nZXRoZXIgKHVzaW5nIGJpdCBzaGlmdGluZykgd2l0aCBhIGluZGV4IHZhbHVlIHdoaWNoIHRlbGxzIHRoZSBzdGFydGluZyBpbmRleCB2YWx1ZVxuICAgKiBvZiB3aGVyZSB0aGUgbXVsdGkgc3R5bGUgZW50cmllcyBiZWdpbi5cbiAgICovXG4gIFtTdHlsaW5nSW5kZXguTWFzdGVyRmxhZ1Bvc2l0aW9uXTogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBMb2NhdGlvbiBvZiB0aGUgY29sbGVjdGlvbiBvZiBkaXJlY3RpdmVzIGZvciB0aGlzIGNvbnRleHRcbiAgICovXG4gIFtTdHlsaW5nSW5kZXguRGlyZWN0aXZlUmVnaXN0cnlQb3NpdGlvbl06IERpcmVjdGl2ZVJlZ2lzdHJ5VmFsdWVzO1xuXG4gIC8qKlxuICAgKiBMb2NhdGlvbiBvZiBhbGwgc3RhdGljIHN0eWxlcyB2YWx1ZXNcbiAgICovXG4gIFtTdHlsaW5nSW5kZXguSW5pdGlhbFN0eWxlVmFsdWVzUG9zaXRpb25dOiBJbml0aWFsU3R5bGluZ1ZhbHVlcztcblxuICAvKipcbiAgICogTG9jYXRpb24gb2YgYWxsIHN0YXRpYyBjbGFzcyB2YWx1ZXNcbiAgICovXG4gIFtTdHlsaW5nSW5kZXguSW5pdGlhbENsYXNzVmFsdWVzUG9zaXRpb25dOiBJbml0aWFsU3R5bGluZ1ZhbHVlcztcblxuICAvKipcbiAgICogQSBudW1lcmljIHZhbHVlIHJlcHJlc2VudGluZyB0aGUgY2xhc3MgaW5kZXggb2Zmc2V0IHZhbHVlLiBXaGVuZXZlciBhIHNpbmdsZSBjbGFzcyBpc1xuICAgKiBhcHBsaWVkICh1c2luZyBgY2xhc3NQcm9wYCkgaXQgc2hvdWxkIGhhdmUgYW4gc3R5bGluZyBpbmRleCB2YWx1ZSB0aGF0IGRvZXNuJ3RcbiAgICogbmVlZCB0byB0YWtlIGludG8gYWNjb3VudCBhbnkgc3R5bGUgdmFsdWVzIHRoYXQgZXhpc3QgaW4gdGhlIGNvbnRleHQuXG4gICAqL1xuICBbU3R5bGluZ0luZGV4LlNpbmdsZVByb3BPZmZzZXRQb3NpdGlvbnNdOiBTaW5nbGVQcm9wT2Zmc2V0VmFsdWVzO1xuXG4gIC8qKlxuICAgKiBUaGUgbGFzdCBjbGFzcyB2YWx1ZSB0aGF0IHdhcyBpbnRlcnByZXRlZCBieSBgc3R5bGVNYXBgLiBUaGlzIGlzIGNhY2hlZFxuICAgKiBTbyB0aGF0IHRoZSBhbGdvcml0aG0gY2FuIGV4aXQgZWFybHkgaW5jYXNlIHRoZSB2YWx1ZSBoYXMgbm90IGNoYW5nZWQuXG4gICAqL1xuICBbU3R5bGluZ0luZGV4LkNhY2hlZE11bHRpQ2xhc3Nlc106IGFueXxNYXBCYXNlZE9mZnNldFZhbHVlcztcblxuICAvKipcbiAgICogVGhlIGxhc3Qgc3R5bGUgdmFsdWUgdGhhdCB3YXMgaW50ZXJwcmV0ZWQgYnkgYGNsYXNzTWFwYC4gVGhpcyBpcyBjYWNoZWRcbiAgICogU28gdGhhdCB0aGUgYWxnb3JpdGhtIGNhbiBleGl0IGVhcmx5IGluY2FzZSB0aGUgdmFsdWUgaGFzIG5vdCBjaGFuZ2VkLlxuICAgKi9cbiAgW1N0eWxpbmdJbmRleC5DYWNoZWRNdWx0aVN0eWxlc106IGFueXxNYXBCYXNlZE9mZnNldFZhbHVlcztcblxuICAvKipcbiAgICogQSBxdWV1ZSBvZiBhbGwgaG9zdFN0eWxpbmcgaW5zdHJ1Y3Rpb25zLlxuICAgKlxuICAgKiBUaGlzIGFycmF5IChxdWV1ZSkgaXMgcG9wdWxhdGVkIG9ubHkgd2hlbiBob3N0LWxldmVsIHN0eWxpbmcgaW5zdHJ1Y3Rpb25zXG4gICAqIChlLmcuIGBob3N0U3R5bGVNYXBgIGFuZCBgaG9zdENsYXNzUHJvcGApIGFyZSB1c2VkIHRvIGFwcGx5IHN0eWxlIGFuZFxuICAgKiBjbGFzcyB2YWx1ZXMgdmlhIGhvc3QgYmluZGluZ3MgdG8gdGhlIGhvc3QgZWxlbWVudC4gRGVzcGl0ZSB0aGVzZSBiZWluZ1xuICAgKiBzdGFuZGFyZCBhbmd1bGFyIGluc3RydWN0aW9ucywgdGhleSBhcmUgbm90IGRlc2lnbmVkIHRvIGltbWVkaWF0ZWx5IGFwcGx5XG4gICAqIHRoZWlyIHZhbHVlcyB0byB0aGUgc3R5bGluZyBjb250ZXh0IHdoZW4gZXhlY3V0ZWQuIFdoYXQgaGFwcGVucyBpbnN0ZWFkIGlzXG4gICAqIGEgcXVldWUgaXMgY29uc3RydWN0ZWQgYW5kIGVhY2ggaW5zdHJ1Y3Rpb24gaXMgcG9wdWxhdGVkIGludG8gdGhlIHF1ZXVlLlxuICAgKiBUaGVuLCBvbmNlIHRoZSBzdHlsZS9jbGFzcyB2YWx1ZXMgYXJlIHNldCB0byBmbHVzaCAodmlhIGBzdHlsaW5nQXBwbHlgIG9yXG4gICAqIGBob3N0U3R5bGluZ0FwcGx5YCksIHRoZSBxdWV1ZSBpcyBmbHVzaGVkIGFuZCB0aGUgdmFsdWVzIGFyZSByZW5kZXJlZCBvbnRvXG4gICAqIHRoZSBob3N0IGVsZW1lbnQuXG4gICAqL1xuICBbU3R5bGluZ0luZGV4Lkhvc3RJbnN0cnVjdGlvbnNRdWV1ZV06IEhvc3RJbnN0cnVjdGlvbnNRdWV1ZXxudWxsO1xuXG4gIC8qKlxuICAgKiBMb2NhdGlvbiBvZiBhbmltYXRpb24gY29udGV4dCAod2hpY2ggY29udGFpbnMgdGhlIGFjdGl2ZSBwbGF5ZXJzKSBmb3IgdGhpcyBlbGVtZW50IHN0eWxpbmdcbiAgICogY29udGV4dC5cbiAgICovXG4gIFtTdHlsaW5nSW5kZXguUGxheWVyQ29udGV4dF06IFBsYXllckNvbnRleHR8bnVsbDtcbn1cblxuLyoqXG4gKiBBIHF1ZXVlIG9mIGFsbCBob3N0LXJlbGF0ZWQgc3R5bGluZyBpbnN0cnVjdGlvbnMgKHRoZXNlIGFyZSBidWZmZXJlZCBhbmQgZXZhbHVhdGVkIGp1c3QgYmVmb3JlXG4gKiB0aGUgc3R5bGluZyBpcyBhcHBsaWVkKS5cbiAqXG4gKiBUaGlzIHF1ZXVlIGlzIHVzZWQgd2hlbiBhbnkgYGhvc3RTdHlsaW5nYCBpbnN0cnVjdGlvbnMgYXJlIGV4ZWN1dGVkIGZyb20gdGhlIGBob3N0QmluZGluZ3NgXG4gKiBmdW5jdGlvbi4gVGVtcGxhdGUtbGV2ZWwgc3R5bGluZyBmdW5jdGlvbnMgKGUuZy4gYHN0eWxlTWFwYCBhbmQgYGNsYXNzUHJvcGApXG4gKiBkbyBub3QgbWFrZSB1c2Ugb2YgdGhpcyBxdWV1ZSAodGhleSBhcmUgYXBwbGllZCB0byB0aGUgc3R5bGluZyBjb250ZXh0IGltbWVkaWF0ZWx5KS5cbiAqXG4gKiBEdWUgdG8gdGhlIG5hdHVyZSBvZiBob3cgY29tcG9uZW50cy9kaXJlY3RpdmVzIGFy