Skip to:
Content

BuddyPress.org

Ticket #8001: 8001.tribute01.diff

File 8001.tribute01.diff, 268.3 KB (added by dcavins, 7 years ago)

Use Tribute for @-lookups of users.

  • .gitignore

    diff --git .gitignore .gitignore
    index 769dc445e..c4f380218 100644
    lib-cov 
    1414pids
    1515logs
    1616results
    17 src/vendor
    18 vendor
     17#src/vendor
     18#vendor
    1919
    2020node_modules
    2121npm-debug.log
  • src/bp-activity/bp-activity-cssjs.php

    diff --git src/bp-activity/bp-activity-cssjs.php src/bp-activity/bp-activity-cssjs.php
    index ec14cd416..945a0ad75 100644
    function bp_activity_mentions_script() { 
    3333
    3434        $min = bp_core_get_minified_asset_suffix();
    3535
    36         wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( 'jquery', 'jquery-atwho' ), bp_get_version(), true );
     36        wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( 'zurb-tribute' ), bp_get_version(), true );
    3737        wp_enqueue_style( 'bp-mentions-css', buddypress()->plugin_url . "bp-activity/css/mentions{$min}.css", array(), bp_get_version() );
    3838
    3939        wp_style_add_data( 'bp-mentions-css', 'rtl', true );
  • src/bp-activity/css/mentions.css

    diff --git src/bp-activity/css/mentions.css src/bp-activity/css/mentions.css
    index 7773c75ec..f7bc47d12 100644
     
    1 .atwho-view {
    2         background: rgba(204, 204, 204, 0.8);
    3         border-radius: 2px;
    4         border: 1px solid rgb(204, 204, 204);
    5         box-shadow: 0 0 5px rgba(204, 204, 204, 0.25), 0 0 1px #fff;
    6         color: #d84800;
    7         display: none;
    8         font-family: sans-serif;
    9         margin-top: 18px;
     1.tribute-container {
    102        position: absolute;
    113        top: 0;
    12         z-index: 1000; /* >999 for wp-admin */
    13 }
    14 
    15 /* rtl:ignore */
    16 .atwho-view {
    174        left: 0;
     5        height: auto;
     6        max-height: 300px;
     7        max-width: 500px;
     8        overflow: auto;
     9        display: block;
     10        z-index: 999999;
    1811}
    19 
    20 .atwho-view ul {
    21         background: #fff;
    22         list-style: none;
    23         margin: auto;
     12.tribute-container ul {
     13        margin: 0;
     14        margin-top: 2px;
    2415        padding: 0;
     16        list-style: none;
     17        background: #efefef;
    2518}
    26 
    27 .atwho-view ul li {
    28         border-bottom: 1px solid #efefef;
    29         box-sizing: content-box;
     19.tribute-container li {
     20        padding: 5px 5px;
    3021        cursor: pointer;
    31         display: block;
    32         font-size: 14px;
    33         height: 20px;
    34         line-height: 20px;
    35         margin: 0;
    36         overflow: hidden;
    37         padding: 5px 10px;
    38 }
    39 
    40 .atwho-view img {
    41         border-radius: 2px;
    42         float: right;
    43         height: 20px;
    44         margin-top: 0;
    45         width: 20px;
    4622}
    47 
    48 .atwho-view strong {
    49         background: #efefef;
    50         font-weight: 700;
     23.tribute-container li.highlight, .tribute-container li:hover {
     24        background: #ddd;
    5125}
    52 
    53 .atwho-view .username strong {
    54         color: #d54e21;
     26.tribute-container li span {
     27        font-weight: bold;
    5528}
    56 
    57 .atwho-view small {
    58         color: #aaa;
    59         float: right;
    60         font-size: smaller;
    61         font-weight: 400;
    62         margin: 0 10px 0 40px;
     29.tribute-container li.no-match {
     30        cursor: default;
    6331}
    64 
    65 .atwho-view .cur {
    66         background: rgba(239, 239, 239, 0.5);
     32.tribute-container .menu-highlighted {
     33        font-weight: bold;
    6734}
    68 
    69 @media (max-width: 900px) {
    70 
    71         .atwho-view img {
    72                 float: left;
    73                 margin: 0 10px 0 0;
    74         }
    75 }
    76 
    77 @media (max-width: 400px) {
    78 
    79         .atwho-view ul li {
    80                 font-size: 16px;
    81                 line-height: 23px;
    82                 padding: 13px;
    83         }
    84 
    85         .atwho-view ul li img {
    86                 height: 30px;
    87                 margin-top: -5px;
    88                 width: 30px;
    89         }
    90 
    91         .atwho-view {
    92                 border-radius: 0;
    93                 left: 0 !important;
    94                 width: 100%;
    95         }
    96 
    97         .atwho-view ul li .username {
    98                 display: inline-block;
    99                 margin: -10px 0 0 0;
    100                 padding: 10px 0;
    101         }
    102 
    103         .atwho-view ul li small {
    104                 display: inline-block;
    105                 margin-left: 20px;
    106         }
     35.tribute-container li img {
     36        border-radius: 2px;
     37        float: right;
     38        height: 20px;
     39        /*margin-top: 0;*/
     40        width: 20px;
    10741}
  • src/bp-activity/js/mentions.js

    diff --git src/bp-activity/js/mentions.js src/bp-activity/js/mentions.js
    index 2d0afa3ef..882f3690d 100644
    window.bp = window.bp || {}; 
    1616        /**
    1717         * Adds BuddyPress @mentions to form inputs.
    1818         *
    19          * @param {array|object} options If array, becomes the suggestions' data source. If object, passed as config to $.atwho().
     19         * @param {array} defaultList If array, becomes the suggestions' default data source.
    2020         * @since 2.1.0
    2121         */
    22         $.fn.bp_mentions = function( options ) {
    23                 if ( $.isArray( options ) ) {
    24                         options = { data: options };
    25                 }
    26 
    27                 /**
    28                  * Default options for at.js; see https://github.com/ichord/At.js/.
    29                  */
    30                 var suggestionsDefaults = {
    31                         delay:             200,
    32                         hideWithoutSuffix: true,
    33                         insertTpl:         '@${ID}',
    34                         limit:             10,
    35                         startWithSpace:    false,
    36                         suffix:            '',
    37 
    38                         callbacks: {
    39                                 /**
    40                                  * Custom filter to only match the start of spaced words.
    41                                  * Based on the core/default one.
    42                                  *
    43                                  * @param {string} query
    44                                  * @param {array} data
    45                                  * @param {string} search_key
    46                                  * @return {array}
    47                                  * @since 2.1.0
    48                                  */
    49                                 filter: function( query, data, search_key ) {
    50                                         var item, _i, _len, _results = [],
    51                                         regxp = new RegExp( '^' + query + '| ' + query, 'ig' ); // start of string, or preceded by a space.
    52 
    53                                         for ( _i = 0, _len = data.length; _i < _len; _i++ ) {
    54                                                 item = data[ _i ];
    55                                                 if ( item[ search_key ].toLowerCase().match( regxp ) ) {
    56                                                         _results.push( item );
    57                                                 }
    58                                         }
    59 
    60                                         return _results;
    61                                 },
    62 
    63                                 /**
    64                                  * Removes some spaces around highlighted string and tweaks regex to allow spaces
    65                                  * (to match display_name). Based on the core default.
    66                                  *
    67                                  * @param {unknown} li
    68                                  * @param {string} query
    69                                  * @return {string}
    70                                  * @since 2.1.0
    71                                  */
    72                                 highlighter: function( li, query ) {
    73                                         if ( ! query ) {
    74                                                 return li;
    75                                         }
    76 
    77                                         var regexp = new RegExp( '>(\\s*|[\\w\\s]*)(' + this.at.replace( '+', '\\+') + '?' + query.replace( '+', '\\+' ) + ')([\\w ]*)\\s*<', 'ig' );
    78                                         return li.replace( regexp, function( str, $1, $2, $3 ) {
    79                                                 return '>' + $1 + '<strong>' + $2 + '</strong>' + $3 + '<';
    80                                         });
    81                                 },
    82 
    83                                 /**
    84                                  * Reposition the suggestion list dynamically.
    85                                  *
    86                                  * @param {unknown} offset
    87                                  * @since 2.1.0
    88                                  */
    89                                 before_reposition: function( offset ) {
    90                                         // get the iframe, if any, already applied with atwho
    91                                         var caret,
    92                                                         line,
    93                                                         iframeOffset,
    94                                                         move,
    95                                                         $view = $( '#atwho-ground-' + this.id + ' .atwho-view' ),
    96                                                         $body = $( 'body' ),
    97                                                         atwhoDataValue = this.$inputor.data( 'atwho' );
    98 
    99                                         if ( 'undefined' !== atwhoDataValue && 'undefined' !== atwhoDataValue.iframe && null !== atwhoDataValue.iframe ) {
    100                                                 caret = this.$inputor.caret( 'offset', { iframe: atwhoDataValue.iframe } );
    101                                                 // Caret.js no longer calculates iframe caret position from the window (it's now just within the iframe).
    102                                                 // We need to get the iframe offset from the window and merge that into our object.
    103                                                 iframeOffset = $( atwhoDataValue.iframe ).offset();
    104                                                 if ( 'undefined' !== iframeOffset ) {
    105                                                         caret.left += iframeOffset.left;
    106                                                         caret.top  += iframeOffset.top;
    107                                                 }
    108                                         } else {
    109                                                 caret = this.$inputor.caret( 'offset' );
    110                                         }
    111 
    112                                         // If the caret is past horizontal half, then flip it, yo
    113                                         if ( caret.left > ( $body.width() / 2 ) ) {
    114                                                 $view.addClass( 'right' );
    115                                                 move = caret.left - offset.left - this.view.$el.width();
    116                                         } else {
    117                                                 $view.removeClass( 'right' );
    118                                                 move = caret.left - offset.left + 1;
    119                                         }
    120 
    121                                         // If we're on a small screen, scroll to caret
    122                                         if ( $body.width() <= 400 ) {
    123                                                 $( document ).scrollTop( caret.top - 6 );
    124                                         }
     22        $.fn.bp_mentions = function( defaultList ) {
     23                var debouncer = function(func, wait) {
     24                  let timeout;
     25
     26                  return function() {
     27                        var context = this;
     28                    var args = arguments;
     29
     30                    var callFunction = function() {
     31                        func.apply(context, args)
     32                    };
     33
     34                    clearTimeout(timeout);
     35                    timeout = setTimeout(callFunction, wait);
     36                  }
     37                };
     38
     39                var remoteSearch = function(text, cb) {
     40                        /**
     41                         * Immediately show the pre-created friends list, if it's populated,
     42                         * and the user has hesitated after hitting @ (no search text provided).
     43                         */
     44                        if ( text.length === 0 && $.isArray( defaultList ) && defaultList.length > 0) {
     45                                cb(defaultList);
     46                                return;
     47                        }
    12548
    126                                         // New position is under the caret (never above) and positioned to follow
    127                                         // Dynamic sizing based on the input area (remove 'px' from end)
    128                                         line = parseInt( this.$inputor.css( 'line-height' ).substr( 0, this.$inputor.css( 'line-height' ).length - 2 ), 10 );
    129                                         if ( !line || line < 5 ) { // sanity check, and catch no line-height
    130                                                 line = 19;
    131                                         }
     49                        mentionsItem = mentionsQueryCache[ text ];
     50                        if ( typeof mentionsItem === 'object' ) {
     51                                cb(mentionsItem);
     52                                return;
     53                        }
    13254
    133                                         offset.top   = caret.top + line;
    134                                         offset.left += move;
    135                                 },
     55                        var params = { 'action': 'bp_get_suggestions', 'term': text, 'type': 'members' };
    13656
    137                                 /**
    138                                  * Override default behaviour which inserts junk tags in the WordPress Visual editor.
    139                                  *
    140                                  * @param {unknown} $inputor Element which we're inserting content into.
    141                                  * @param {string) content The content that will be inserted.
    142                                  * @param {string) suffix Applied to the end of the content string.
    143                                  * @return {string}
    144                                  * @since 2.1.0
    145                                  */
    146                                 inserting_wrapper: function( $inputor, content, suffix ) {
    147                                         return '' + content + suffix;
    148                                 }
     57                        // Add the group ID to the request if group ID data is attached to the input.
     58                        if ( ".wp-editor-area" === $( this ).selector
     59                                && typeof document.activeElement !== undefined
     60                                && typeof document.activeElement.dataset !== undefined
     61                                && document.activeElement.dataset.suggestionsGroupId !== undefined
     62                                && $.isNumeric( document.activeElement.dataset.suggestionsGroupId ) ) {
     63                                params['group-id'] = parseInt( document.activeElement.dataset.suggestionsGroupId, 10 );
    14964                        }
    150                 },
    15165
    152                 /**
    153                  * Default options for our @mentions; see https://github.com/ichord/At.js/.
    154                  */
    155                 mentionsDefaults = {
    156                         callbacks: {
     66                        return $.getJSON( ajaxurl, params )
    15767                                /**
    158                                  * If there are no matches for the query in this.data, then query BuddyPress.
     68                                 * Success callback for the @suggestions lookup.
    15969                                 *
    160                                  * @param {string} query Partial @mention to search for.
    161                                  * @param {function} render_view Render page callback function.
     70                                 * @param {object} response Details of users matching the query.
    16271                                 * @since 2.1.0
    163                                  * @since 3.0.0. Renamed from "remote_filter" for at.js v1.5.4 support.
    16472                                 */
    165                                 remoteFilter: function( query, render_view ) {
    166                                         var self = $( this ),
    167                                                 params = {};
    168 
    169                                         mentionsItem = mentionsQueryCache[ query ];
    170                                         if ( typeof mentionsItem === 'object' ) {
    171                                                 render_view( mentionsItem );
     73                                .done(function( response ) {
     74                                        if ( ! response.success ) {
     75                                                cb([]);
    17276                                                return;
    17377                                        }
    17478
    175                                         if ( self.xhr ) {
    176                                                 self.xhr.abort();
    177                                         }
    178 
    179                                         params = { 'action': 'bp_get_suggestions', 'term': query, 'type': 'members' };
    180 
    181                                         if ( $.isNumeric( this.$inputor.data( 'suggestions-group-id' ) ) ) {
    182                                                 params['group-id'] = parseInt( this.$inputor.data( 'suggestions-group-id' ), 10 );
    183                                         }
    184 
    185                                         self.xhr = $.getJSON( ajaxurl, params )
     79                                        var data = $.map( response.data,
    18680                                                /**
    187                                                  * Success callback for the @suggestions lookup.
     81                                                 * Create a composite index to determine ordering of results;
     82                                                 * nicename matches will appear on top.
    18883                                                 *
    189                                                  * @param {object} response Details of users matching the query.
     84                                                 * @param {array} suggestion A suggestion's original data.
     85                                                 * @return {array} A suggestion's new data.
    19086                                                 * @since 2.1.0
    19187                                                 */
    192                                                 .done(function( response ) {
    193                                                         if ( ! response.success ) {
    194                                                                 return;
    195                                                         }
     88                                                function( suggestion ) {
     89                                                        suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
     90                                                        return suggestion;
     91                                                }
     92                                        );
    19693
    197                                                         var data = $.map( response.data,
    198                                                                 /**
    199                                                                  * Create a composite index to determine ordering of results;
    200                                                                  * nicename matches will appear on top.
    201                                                                  *
    202                                                                  * @param {array} suggestion A suggestion's original data.
    203                                                                  * @return {array} A suggestion's new data.
    204                                                                  * @since 2.1.0
    205                                                                  */
    206                                                                 function( suggestion ) {
    207                                                                         suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
    208                                                                         return suggestion;
    209                                                                 }
    210                                                         );
     94                                        mentionsQueryCache[ text ] = data;
     95                                        cb(data);
     96                                });
     97                }
    21198
    212                                                         mentionsQueryCache[ query ] = data;
    213                                                         render_view( data );
    214                                                 });
    215                                 }
     99                var tributeParams = {
     100                        values: debouncer( function (text, cb) {
     101                                remoteSearch(text, users => cb(users));
     102                        }, 250),
     103                        lookup: 'search',
     104                        fillAttr: 'ID',
     105                        menuItemTemplate: function (item) {
     106                                return '<img src="' + item.original.image + '" alt="Profile picture of ' + item.original.name + '"> @' + item.string;
    216107                        },
     108                };
    217109
    218                         data: $.map( options.data,
    219                                 /**
    220                                  * Create a composite index to search against of nicename + display name.
    221                                  * This will also determine ordering of results, so nicename matches will appear on top.
    222                                  *
    223                                  * @param {array} suggestion A suggestion's original data.
    224                                  * @return {array} A suggestion's new data.
    225                                  * @since 2.1.0
    226                                  */
    227                                 function( suggestion ) {
    228                                         suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
    229                                         return suggestion;
    230                                 }
    231                         ),
     110                var tribute = new Tribute( tributeParams );
    232111
    233                         at:         '@',
    234                         searchKey:  'search',
    235                         displayTpl: '<li data-value="@${ID}"><img src="${image}" /><span class="username">@${ID}</span><small>${name}</small></li>'
    236                 },
    237 
    238                 opts = $.extend( true, {}, suggestionsDefaults, mentionsDefaults, options );
    239                 return $.fn.atwho.call( this, opts );
     112                $( this ).each( function() {
     113                        tribute.attach( document.getElementById( $(this).attr("id") ) );
     114                });
    240115        };
    241116
    242117        $( document ).ready(function() {
    243118                // Activity/reply, post comments, dashboard post 'text' editor.
    244                 $( '.bp-suggestions, #comments form textarea, .wp-editor-area' ).bp_mentions( bp.mentions.users );
     119                $( '.bp-suggestions, #comments form textarea' ).bp_mentions( bp.mentions.users );
    245120        });
    246121
    247         bp.mentions.tinyMCEinit = function() {
    248                 if ( typeof window.tinyMCE === 'undefined' || window.tinyMCE.activeEditor === null || typeof window.tinyMCE.activeEditor === 'undefined' ) {
    249                         return;
    250                 } else {
    251                         $( window.tinyMCE.activeEditor.contentDocument.activeElement )
    252                                 .atwho( 'setIframe', $( '.wp-editor-wrap iframe' )[0] )
    253                                 .bp_mentions( bp.mentions.users );
    254                 }
    255         };
    256122})( bp, jQuery );
  • src/bp-core/bp-core-cssjs.php

    diff --git src/bp-core/bp-core-cssjs.php src/bp-core/bp-core-cssjs.php
    index 987deb642..4d9eff26a 100644
    function bp_core_register_common_scripts() { 
    5454                'bp-jquery-cookie'  => array( 'file' => "{$url}vendor/jquery-cookie{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
    5555                'bp-jquery-scroll-to' => array( 'file' => "{$url}vendor/jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
    5656
    57                 // Version 2.1.
    58                 'jquery-caret' => array( 'file' => "{$url}vendor/jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => true ),
    59                 'jquery-atwho' => array( 'file' => "{$url}vendor/jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ), 'footer' => true ),
    60 
    6157                // Version 2.3.
    6258                'bp-plupload' => array( 'file' => "{$url}bp-plupload{$min}.js", 'dependencies' => array( 'plupload', 'jquery', 'json2', 'wp-backbone' ), 'footer' => true ),
    6359                'bp-avatar'   => array( 'file' => "{$url}avatar{$min}.js", 'dependencies' => array( 'jcrop' ), 'footer' => true ),
    function bp_core_register_common_scripts() { 
    6965                // Version 2.7.
    7066                'bp-moment'    => array( 'file' => "{$url}vendor/moment-js/moment{$min}.js", 'dependencies' => array(), 'footer' => true ),
    7167                'bp-livestamp' => array( 'file' => "{$url}vendor/livestamp{$min}.js", 'dependencies' => array( 'jquery', 'bp-moment' ), 'footer' => true ),
     68
     69                // Version 5
     70                'zurb-tribute' => array( 'file' => "{$url}vendor/tribute{$min}.js", 'dependencies' => array(), 'footer' => true ),
    7271        );
    7372
    7473        // Version 2.7 - Add Moment.js locale to our $scripts array if we found one.
  • deleted file src/bp-core/js/vendor/jquery.atwho.js

    diff --git src/bp-core/js/vendor/jquery.atwho.js src/bp-core/js/vendor/jquery.atwho.js
    deleted file mode 100755
    index 795b6c67d..000000000
    + -  
    1 /**
    2  * at.js - 1.5.4
    3  * Copyright (c) 2017 chord.luo <chord.luo@gmail.com>;
    4  * Homepage: http://ichord.github.com/At.js
    5  * License: MIT
    6  */
    7 (function (root, factory) {
    8   if (typeof define === 'function' && define.amd) {
    9     // AMD. Register as an anonymous module unless amdModuleId is set
    10     define(["jquery"], function (a0) {
    11       return (factory(a0));
    12     });
    13   } else if (typeof exports === 'object') {
    14     // Node. Does not work with strict CommonJS, but
    15     // only CommonJS-like environments that support module.exports,
    16     // like Node.
    17     module.exports = factory(require("jquery"));
    18   } else {
    19     factory(jQuery);
    20   }
    21 }(this, function ($) {
    22 var DEFAULT_CALLBACKS, KEY_CODE;
    23 
    24 KEY_CODE = {
    25   ESC: 27,
    26   TAB: 9,
    27   ENTER: 13,
    28   CTRL: 17,
    29   A: 65,
    30   P: 80,
    31   N: 78,
    32   LEFT: 37,
    33   UP: 38,
    34   RIGHT: 39,
    35   DOWN: 40,
    36   BACKSPACE: 8,
    37   SPACE: 32
    38 };
    39 
    40 DEFAULT_CALLBACKS = {
    41   beforeSave: function(data) {
    42     return Controller.arrayToDefaultHash(data);
    43   },
    44   matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
    45     var _a, _y, match, regexp, space;
    46     flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    47     if (should_startWithSpace) {
    48       flag = '(?:^|\\s)' + flag;
    49     }
    50     _a = decodeURI("%C3%80");
    51     _y = decodeURI("%C3%BF");
    52     space = acceptSpaceBar ? "\ " : "";
    53     regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');
    54     match = regexp.exec(subtext);
    55     if (match) {
    56       return match[2] || match[1];
    57     } else {
    58       return null;
    59     }
    60   },
    61   filter: function(query, data, searchKey) {
    62     var _results, i, item, len;
    63     _results = [];
    64     for (i = 0, len = data.length; i < len; i++) {
    65       item = data[i];
    66       if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
    67         _results.push(item);
    68       }
    69     }
    70     return _results;
    71   },
    72   remoteFilter: null,
    73   sorter: function(query, items, searchKey) {
    74     var _results, i, item, len;
    75     if (!query) {
    76       return items;
    77     }
    78     _results = [];
    79     for (i = 0, len = items.length; i < len; i++) {
    80       item = items[i];
    81       item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());
    82       if (item.atwho_order > -1) {
    83         _results.push(item);
    84       }
    85     }
    86     return _results.sort(function(a, b) {
    87       return a.atwho_order - b.atwho_order;
    88     });
    89   },
    90   tplEval: function(tpl, map) {
    91     var error, error1, template;
    92     template = tpl;
    93     try {
    94       if (typeof tpl !== 'string') {
    95         template = tpl(map);
    96       }
    97       return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {
    98         return map[key];
    99       });
    100     } catch (error1) {
    101       error = error1;
    102       return "";
    103     }
    104   },
    105   highlighter: function(li, query) {
    106     var regexp;
    107     if (!query) {
    108       return li;
    109     }
    110     regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig');
    111     return li.replace(regexp, function(str, $1, $2, $3) {
    112       return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
    113     });
    114   },
    115   beforeInsert: function(value, $li, e) {
    116     return value;
    117   },
    118   beforeReposition: function(offset) {
    119     return offset;
    120   },
    121   afterMatchFailed: function(at, el) {}
    122 };
    123 
    124 var App;
    125 
    126 App = (function() {
    127   function App(inputor) {
    128     this.currentFlag = null;
    129     this.controllers = {};
    130     this.aliasMaps = {};
    131     this.$inputor = $(inputor);
    132     this.setupRootElement();
    133     this.listen();
    134   }
    135 
    136   App.prototype.createContainer = function(doc) {
    137     var ref;
    138     if ((ref = this.$el) != null) {
    139       ref.remove();
    140     }
    141     return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
    142   };
    143 
    144   App.prototype.setupRootElement = function(iframe, asRoot) {
    145     var error, error1;
    146     if (asRoot == null) {
    147       asRoot = false;
    148     }
    149     if (iframe) {
    150       this.window = iframe.contentWindow;
    151       this.document = iframe.contentDocument || this.window.document;
    152       this.iframe = iframe;
    153     } else {
    154       this.document = this.$inputor[0].ownerDocument;
    155       this.window = this.document.defaultView || this.document.parentWindow;
    156       try {
    157         this.iframe = this.window.frameElement;
    158       } catch (error1) {
    159         error = error1;
    160         this.iframe = null;
    161         if ($.fn.atwho.debug) {
    162           throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);
    163         }
    164       }
    165     }
    166     return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
    167   };
    168 
    169   App.prototype.controller = function(at) {
    170     var c, current, currentFlag, ref;
    171     if (this.aliasMaps[at]) {
    172       current = this.controllers[this.aliasMaps[at]];
    173     } else {
    174       ref = this.controllers;
    175       for (currentFlag in ref) {
    176         c = ref[currentFlag];
    177         if (currentFlag === at) {
    178           current = c;
    179           break;
    180         }
    181       }
    182     }
    183     if (current) {
    184       return current;
    185     } else {
    186       return this.controllers[this.currentFlag];
    187     }
    188   };
    189 
    190   App.prototype.setContextFor = function(at) {
    191     this.currentFlag = at;
    192     return this;
    193   };
    194 
    195   App.prototype.reg = function(flag, setting) {
    196     var base, controller;
    197     controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
    198     if (setting.alias) {
    199       this.aliasMaps[setting.alias] = flag;
    200     }
    201     controller.init(setting);
    202     return this;
    203   };
    204 
    205   App.prototype.listen = function() {
    206     return this.$inputor.on('compositionstart', (function(_this) {
    207       return function(e) {
    208         var ref;
    209         if ((ref = _this.controller()) != null) {
    210           ref.view.hide();
    211         }
    212         _this.isComposing = true;
    213         return null;
    214       };
    215     })(this)).on('compositionend', (function(_this) {
    216       return function(e) {
    217         _this.isComposing = false;
    218         setTimeout(function(e) {
    219           return _this.dispatch(e);
    220         });
    221         return null;
    222       };
    223     })(this)).on('keyup.atwhoInner', (function(_this) {
    224       return function(e) {
    225         return _this.onKeyup(e);
    226       };
    227     })(this)).on('keydown.atwhoInner', (function(_this) {
    228       return function(e) {
    229         return _this.onKeydown(e);
    230       };
    231     })(this)).on('blur.atwhoInner', (function(_this) {
    232       return function(e) {
    233         var c;
    234         if (c = _this.controller()) {
    235           c.expectedQueryCBId = null;
    236           return c.view.hide(e, c.getOpt("displayTimeout"));
    237         }
    238       };
    239     })(this)).on('click.atwhoInner', (function(_this) {
    240       return function(e) {
    241         return _this.dispatch(e);
    242       };
    243     })(this)).on('scroll.atwhoInner', (function(_this) {
    244       return function() {
    245         var lastScrollTop;
    246         lastScrollTop = _this.$inputor.scrollTop();
    247         return function(e) {
    248           var currentScrollTop, ref;
    249           currentScrollTop = e.target.scrollTop;
    250           if (lastScrollTop !== currentScrollTop) {
    251             if ((ref = _this.controller()) != null) {
    252               ref.view.hide(e);
    253             }
    254           }
    255           lastScrollTop = currentScrollTop;
    256           return true;
    257         };
    258       };
    259     })(this)());
    260   };
    261 
    262   App.prototype.shutdown = function() {
    263     var _, c, ref;
    264     ref = this.controllers;
    265     for (_ in ref) {
    266       c = ref[_];
    267       c.destroy();
    268       delete this.controllers[_];
    269     }
    270     this.$inputor.off('.atwhoInner');
    271     return this.$el.remove();
    272   };
    273 
    274   App.prototype.dispatch = function(e) {
    275     var _, c, ref, results;
    276     ref = this.controllers;
    277     results = [];
    278     for (_ in ref) {
    279       c = ref[_];
    280       results.push(c.lookUp(e));
    281     }
    282     return results;
    283   };
    284 
    285   App.prototype.onKeyup = function(e) {
    286     var ref;
    287     switch (e.keyCode) {
    288       case KEY_CODE.ESC:
    289         e.preventDefault();
    290         if ((ref = this.controller()) != null) {
    291           ref.view.hide();
    292         }
    293         break;
    294       case KEY_CODE.DOWN:
    295       case KEY_CODE.UP:
    296       case KEY_CODE.CTRL:
    297       case KEY_CODE.ENTER:
    298         $.noop();
    299         break;
    300       case KEY_CODE.P:
    301       case KEY_CODE.N:
    302         if (!e.ctrlKey) {
    303           this.dispatch(e);
    304         }
    305         break;
    306       default:
    307         this.dispatch(e);
    308     }
    309   };
    310 
    311   App.prototype.onKeydown = function(e) {
    312     var ref, view;
    313     view = (ref = this.controller()) != null ? ref.view : void 0;
    314     if (!(view && view.visible())) {
    315       return;
    316     }
    317     switch (e.keyCode) {
    318       case KEY_CODE.ESC:
    319         e.preventDefault();
    320         view.hide(e);
    321         break;
    322       case KEY_CODE.UP:
    323         e.preventDefault();
    324         view.prev();
    325         break;
    326       case KEY_CODE.DOWN:
    327         e.preventDefault();
    328         view.next();
    329         break;
    330       case KEY_CODE.P:
    331         if (!e.ctrlKey) {
    332           return;
    333         }
    334         e.preventDefault();
    335         view.prev();
    336         break;
    337       case KEY_CODE.N:
    338         if (!e.ctrlKey) {
    339           return;
    340         }
    341         e.preventDefault();
    342         view.next();
    343         break;
    344       case KEY_CODE.TAB:
    345       case KEY_CODE.ENTER:
    346       case KEY_CODE.SPACE:
    347         if (!view.visible()) {
    348           return;
    349         }
    350         if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {
    351           return;
    352         }
    353         if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {
    354           return;
    355         }
    356         if (view.highlighted()) {
    357           e.preventDefault();
    358           view.choose(e);
    359         } else {
    360           view.hide(e);
    361         }
    362         break;
    363       default:
    364         $.noop();
    365     }
    366   };
    367 
    368   return App;
    369 
    370 })();
    371 
    372 var Controller,
    373   slice = [].slice;
    374 
    375 Controller = (function() {
    376   Controller.prototype.uid = function() {
    377     return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
    378   };
    379 
    380   function Controller(app, at1) {
    381     this.app = app;
    382     this.at = at1;
    383     this.$inputor = this.app.$inputor;
    384     this.id = this.$inputor[0].id || this.uid();
    385     this.expectedQueryCBId = null;
    386     this.setting = null;
    387     this.query = null;
    388     this.pos = 0;
    389     this.range = null;
    390     if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {
    391       this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
    392     }
    393     this.model = new Model(this);
    394     this.view = new View(this);
    395   }
    396 
    397   Controller.prototype.init = function(setting) {
    398     this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
    399     this.view.init();
    400     return this.model.reload(this.setting.data);
    401   };
    402 
    403   Controller.prototype.destroy = function() {
    404     this.trigger('beforeDestroy');
    405     this.model.destroy();
    406     this.view.destroy();
    407     return this.$el.remove();
    408   };
    409 
    410   Controller.prototype.callDefault = function() {
    411     var args, error, error1, funcName;
    412     funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    413     try {
    414       return DEFAULT_CALLBACKS[funcName].apply(this, args);
    415     } catch (error1) {
    416       error = error1;
    417       return $.error(error + " Or maybe At.js doesn't have function " + funcName);
    418     }
    419   };
    420 
    421   Controller.prototype.trigger = function(name, data) {
    422     var alias, eventName;
    423     if (data == null) {
    424       data = [];
    425     }
    426     data.push(this);
    427     alias = this.getOpt('alias');
    428     eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";
    429     return this.$inputor.trigger(eventName, data);
    430   };
    431 
    432   Controller.prototype.callbacks = function(funcName) {
    433     return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
    434   };
    435 
    436   Controller.prototype.getOpt = function(at, default_value) {
    437     var e, error1;
    438     try {
    439       return this.setting[at];
    440     } catch (error1) {
    441       e = error1;
    442       return null;
    443     }
    444   };
    445 
    446   Controller.prototype.insertContentFor = function($li) {
    447     var data, tpl;
    448     tpl = this.getOpt('insertTpl');
    449     data = $.extend({}, $li.data('item-data'), {
    450       'atwho-at': this.at
    451     });
    452     return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
    453   };
    454 
    455   Controller.prototype.renderView = function(data) {
    456     var searchKey;
    457     searchKey = this.getOpt("searchKey");
    458     data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);
    459     return this.view.render(data.slice(0, this.getOpt('limit')));
    460   };
    461 
    462   Controller.arrayToDefaultHash = function(data) {
    463     var i, item, len, results;
    464     if (!$.isArray(data)) {
    465       return data;
    466     }
    467     results = [];
    468     for (i = 0, len = data.length; i < len; i++) {
    469       item = data[i];
    470       if ($.isPlainObject(item)) {
    471         results.push(item);
    472       } else {
    473         results.push({
    474           name: item
    475         });
    476       }
    477     }
    478     return results;
    479   };
    480 
    481   Controller.prototype.lookUp = function(e) {
    482     var query, wait;
    483     if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
    484       return;
    485     }
    486     if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
    487       return;
    488     }
    489     query = this.catchQuery(e);
    490     if (!query) {
    491       this.expectedQueryCBId = null;
    492       return query;
    493     }
    494     this.app.setContextFor(this.at);
    495     if (wait = this.getOpt('delay')) {
    496       this._delayLookUp(query, wait);
    497     } else {
    498       this._lookUp(query);
    499     }
    500     return query;
    501   };
    502 
    503   Controller.prototype._delayLookUp = function(query, wait) {
    504     var now, remaining;
    505     now = Date.now ? Date.now() : new Date().getTime();
    506     this.previousCallTime || (this.previousCallTime = now);
    507     remaining = wait - (now - this.previousCallTime);
    508     if ((0 < remaining && remaining < wait)) {
    509       this.previousCallTime = now;
    510       this._stopDelayedCall();
    511       return this.delayedCallTimeout = setTimeout((function(_this) {
    512         return function() {
    513           _this.previousCallTime = 0;
    514           _this.delayedCallTimeout = null;
    515           return _this._lookUp(query);
    516         };
    517       })(this), wait);
    518     } else {
    519       this._stopDelayedCall();
    520       if (this.previousCallTime !== now) {
    521         this.previousCallTime = 0;
    522       }
    523       return this._lookUp(query);
    524     }
    525   };
    526 
    527   Controller.prototype._stopDelayedCall = function() {
    528     if (this.delayedCallTimeout) {
    529       clearTimeout(this.delayedCallTimeout);
    530       return this.delayedCallTimeout = null;
    531     }
    532   };
    533 
    534   Controller.prototype._generateQueryCBId = function() {
    535     return {};
    536   };
    537 
    538   Controller.prototype._lookUp = function(query) {
    539     var _callback;
    540     _callback = function(queryCBId, data) {
    541       if (queryCBId !== this.expectedQueryCBId) {
    542         return;
    543       }
    544       if (data && data.length > 0) {
    545         return this.renderView(this.constructor.arrayToDefaultHash(data));
    546       } else {
    547         return this.view.hide();
    548       }
    549     };
    550     this.expectedQueryCBId = this._generateQueryCBId();
    551     return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
    552   };
    553 
    554   return Controller;
    555 
    556 })();
    557 
    558 var TextareaController,
    559   extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    560   hasProp = {}.hasOwnProperty;
    561 
    562 TextareaController = (function(superClass) {
    563   extend(TextareaController, superClass);
    564 
    565   function TextareaController() {
    566     return TextareaController.__super__.constructor.apply(this, arguments);
    567   }
    568 
    569   TextareaController.prototype.catchQuery = function() {
    570     var caretPos, content, end, isString, query, start, subtext;
    571     content = this.$inputor.val();
    572     caretPos = this.$inputor.caret('pos', {
    573       iframe: this.app.iframe
    574     });
    575     subtext = content.slice(0, caretPos);
    576     query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
    577     isString = typeof query === 'string';
    578     if (isString && query.length < this.getOpt('minLen', 0)) {
    579       return;
    580     }
    581     if (isString && query.length <= this.getOpt('maxLen', 20)) {
    582       start = caretPos - query.length;
    583       end = start + query.length;
    584       this.pos = start;
    585       query = {
    586         'text': query,
    587         'headPos': start,
    588         'endPos': end
    589       };
    590       this.trigger("matched", [this.at, query.text]);
    591     } else {
    592       query = null;
    593       this.view.hide();
    594     }
    595     return this.query = query;
    596   };
    597 
    598   TextareaController.prototype.rect = function() {
    599     var c, iframeOffset, scaleBottom;
    600     if (!(c = this.$inputor.caret('offset', this.pos - 1, {
    601       iframe: this.app.iframe
    602     }))) {
    603       return;
    604     }
    605     if (this.app.iframe && !this.app.iframeAsRoot) {
    606       iframeOffset = $(this.app.iframe).offset();
    607       c.left += iframeOffset.left;
    608       c.top += iframeOffset.top;
    609     }
    610     scaleBottom = this.app.document.selection ? 0 : 2;
    611     return {
    612       left: c.left,
    613       top: c.top,
    614       bottom: c.top + c.height + scaleBottom
    615     };
    616   };
    617 
    618   TextareaController.prototype.insert = function(content, $li) {
    619     var $inputor, source, startStr, suffix, text;
    620     $inputor = this.$inputor;
    621     source = $inputor.val();
    622     startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
    623     suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
    624     content += suffix;
    625     text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));
    626     $inputor.val(text);
    627     $inputor.caret('pos', startStr.length + content.length, {
    628       iframe: this.app.iframe
    629     });
    630     if (!$inputor.is(':focus')) {
    631       $inputor.focus();
    632     }
    633     return $inputor.change();
    634   };
    635 
    636   return TextareaController;
    637 
    638 })(Controller);
    639 
    640 var EditableController,
    641   extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    642   hasProp = {}.hasOwnProperty;
    643 
    644 EditableController = (function(superClass) {
    645   extend(EditableController, superClass);
    646 
    647   function EditableController() {
    648     return EditableController.__super__.constructor.apply(this, arguments);
    649   }
    650 
    651   EditableController.prototype._getRange = function() {
    652     var sel;
    653     sel = this.app.window.getSelection();
    654     if (sel.rangeCount > 0) {
    655       return sel.getRangeAt(0);
    656     }
    657   };
    658 
    659   EditableController.prototype._setRange = function(position, node, range) {
    660     if (range == null) {
    661       range = this._getRange();
    662     }
    663     if (!(range && node)) {
    664       return;
    665     }
    666     node = $(node)[0];
    667     if (position === 'after') {
    668       range.setEndAfter(node);
    669       range.setStartAfter(node);
    670     } else {
    671       range.setEndBefore(node);
    672       range.setStartBefore(node);
    673     }
    674     range.collapse(false);
    675     return this._clearRange(range);
    676   };
    677 
    678   EditableController.prototype._clearRange = function(range) {
    679     var sel;
    680     if (range == null) {
    681       range = this._getRange();
    682     }
    683     sel = this.app.window.getSelection();
    684     if (this.ctrl_a_pressed == null) {
    685       sel.removeAllRanges();
    686       return sel.addRange(range);
    687     }
    688   };
    689 
    690   EditableController.prototype._movingEvent = function(e) {
    691     var ref;
    692     return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
    693   };
    694 
    695   EditableController.prototype._unwrap = function(node) {
    696     var next;
    697     node = $(node).unwrap().get(0);
    698     if ((next = node.nextSibling) && next.nodeValue) {
    699       node.nodeValue += next.nodeValue;
    700       $(next).remove();
    701     }
    702     return node;
    703   };
    704 
    705   EditableController.prototype.catchQuery = function(e) {
    706     var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
    707     if (!(range = this._getRange())) {
    708       return;
    709     }
    710     if (!range.collapsed) {
    711       return;
    712     }
    713     if (e.which === KEY_CODE.ENTER) {
    714       ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();
    715       if ($query.is(':empty')) {
    716         $query.remove();
    717       }
    718       ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();
    719       this._clearRange();
    720       return;
    721     }
    722     if (/firefox/i.test(navigator.userAgent)) {
    723       if ($(range.startContainer).is(this.$inputor)) {
    724         this._clearRange();
    725         return;
    726       }
    727       if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {
    728         _range = range.cloneRange();
    729         _range.setStart(range.startContainer, offset);
    730         if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
    731           inserted = $(range.startContainer).contents().get(offset);
    732           this._setRange('after', $(inserted).contents().last());
    733         }
    734       } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {
    735         $inserted = $(range.startContainer.previousSibling);
    736         if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
    737           this._setRange('after', $inserted.contents().last());
    738         }
    739       }
    740     }
    741     $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
    742     if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
    743       $query.remove();
    744     }
    745     if (!this._movingEvent(e)) {
    746       $query.removeClass('atwho-inserted');
    747     }
    748     if ($query.length > 0) {
    749       switch (e.which) {
    750         case KEY_CODE.LEFT:
    751           this._setRange('before', $query.get(0), range);
    752           $query.removeClass('atwho-query');
    753           return;
    754         case KEY_CODE.RIGHT:
    755           this._setRange('after', $query.get(0).nextSibling, range);
    756           $query.removeClass('atwho-query');
    757           return;
    758       }
    759     }
    760     if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
    761       $query.empty().html(query_content).attr('data-atwho-at-query', null);
    762       this._setRange('after', $query.get(0), range);
    763     }
    764     _range = range.cloneRange();
    765     _range.setStart(range.startContainer, 0);
    766     matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
    767     isString = typeof matched === 'string';
    768     if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
    769       range.setStart(range.startContainer, index);
    770       $query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
    771       range.surroundContents($query.get(0));
    772       lastNode = $query.contents().last().get(0);
    773       if (lastNode) {
    774         if (/firefox/i.test(navigator.userAgent)) {
    775           range.setStart(lastNode, lastNode.length);
    776           range.setEnd(lastNode, lastNode.length);
    777           this._clearRange(range);
    778         } else {
    779           this._setRange('after', lastNode, range);
    780         }
    781       }
    782     }
    783     if (isString && matched.length < this.getOpt('minLen', 0)) {
    784       return;
    785     }
    786     if (isString && matched.length <= this.getOpt('maxLen', 20)) {
    787       query = {
    788         text: matched,
    789         el: $query
    790       };
    791       this.trigger("matched", [this.at, query.text]);
    792       return this.query = query;
    793     } else {
    794       this.view.hide();
    795       this.query = {
    796         el: $query
    797       };
    798       if ($query.text().indexOf(this.at) >= 0) {
    799         if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
    800           $query.removeClass('atwho-query');
    801         } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {
    802           this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));
    803         }
    804       }
    805       return null;
    806     }
    807   };
    808 
    809   EditableController.prototype.rect = function() {
    810     var $iframe, iframeOffset, rect;
    811     rect = this.query.el.offset();
    812     if (!(rect && this.query.el[0].getClientRects().length)) {
    813       return;
    814     }
    815     if (this.app.iframe && !this.app.iframeAsRoot) {
    816       iframeOffset = ($iframe = $(this.app.iframe)).offset();
    817       rect.left += iframeOffset.left - this.$inputor.scrollLeft();
    818       rect.top += iframeOffset.top - this.$inputor.scrollTop();
    819     }
    820     rect.bottom = rect.top + this.query.el.height();
    821     return rect;
    822   };
    823 
    824   EditableController.prototype.insert = function(content, $li) {
    825     var data, overrides, range, suffix, suffixNode;
    826     if (!this.$inputor.is(':focus')) {
    827       this.$inputor.focus();
    828     }
    829     overrides = this.getOpt('functionOverrides');
    830     if (overrides.insert) {
    831       return overrides.insert.call(this, content, $li);
    832     }
    833     suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
    834     data = $li.data('item-data');
    835     this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false");
    836     if (range = this._getRange()) {
    837       if (this.query.el.length) {
    838         range.setEndAfter(this.query.el[0]);
    839       }
    840       range.collapse(false);
    841       range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix));
    842       this._setRange('after', suffixNode, range);
    843     }
    844     if (!this.$inputor.is(':focus')) {
    845       this.$inputor.focus();
    846     }
    847     return this.$inputor.change();
    848   };
    849 
    850   return EditableController;
    851 
    852 })(Controller);
    853 
    854 var Model;
    855 
    856 Model = (function() {
    857   function Model(context) {
    858     this.context = context;
    859     this.at = this.context.at;
    860     this.storage = this.context.$inputor;
    861   }
    862 
    863   Model.prototype.destroy = function() {
    864     return this.storage.data(this.at, null);
    865   };
    866 
    867   Model.prototype.saved = function() {
    868     return this.fetch() > 0;
    869   };
    870 
    871   Model.prototype.query = function(query, callback) {
    872     var _remoteFilter, data, searchKey;
    873     data = this.fetch();
    874     searchKey = this.context.getOpt("searchKey");
    875     data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];
    876     _remoteFilter = this.context.callbacks('remoteFilter');
    877     if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
    878       return callback(data);
    879     } else {
    880       return _remoteFilter.call(this.context, query, callback);
    881     }
    882   };
    883 
    884   Model.prototype.fetch = function() {
    885     return this.storage.data(this.at) || [];
    886   };
    887 
    888   Model.prototype.save = function(data) {
    889     return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
    890   };
    891 
    892   Model.prototype.load = function(data) {
    893     if (!(this.saved() || !data)) {
    894       return this._load(data);
    895     }
    896   };
    897 
    898   Model.prototype.reload = function(data) {
    899     return this._load(data);
    900   };
    901 
    902   Model.prototype._load = function(data) {
    903     if (typeof data === "string") {
    904       return $.ajax(data, {
    905         dataType: "json"
    906       }).done((function(_this) {
    907         return function(data) {
    908           return _this.save(data);
    909         };
    910       })(this));
    911     } else {
    912       return this.save(data);
    913     }
    914   };
    915 
    916   return Model;
    917 
    918 })();
    919 
    920 var View;
    921 
    922 View = (function() {
    923   function View(context) {
    924     this.context = context;
    925     this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
    926     this.$elUl = this.$el.children();
    927     this.timeoutID = null;
    928     this.context.$el.append(this.$el);
    929     this.bindEvent();
    930   }
    931 
    932   View.prototype.init = function() {
    933     var header_tpl, id;
    934     id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
    935     header_tpl = this.context.getOpt("headerTpl");
    936     if (header_tpl && this.$el.children().length === 1) {
    937       this.$el.prepend(header_tpl);
    938     }
    939     return this.$el.attr({
    940       'id': "at-view-" + id
    941     });
    942   };
    943 
    944   View.prototype.destroy = function() {
    945     return this.$el.remove();
    946   };
    947 
    948   View.prototype.bindEvent = function() {
    949     var $menu, lastCoordX, lastCoordY;
    950     $menu = this.$el.find('ul');
    951     lastCoordX = 0;
    952     lastCoordY = 0;
    953     return $menu.on('mousemove.atwho-view', 'li', (function(_this) {
    954       return function(e) {
    955         var $cur;
    956         if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
    957           return;
    958         }
    959         lastCoordX = e.clientX;
    960         lastCoordY = e.clientY;
    961         $cur = $(e.currentTarget);
    962         if ($cur.hasClass('cur')) {
    963           return;
    964         }
    965         $menu.find('.cur').removeClass('cur');
    966         return $cur.addClass('cur');
    967       };
    968     })(this)).on('click.atwho-view', 'li', (function(_this) {
    969       return function(e) {
    970         $menu.find('.cur').removeClass('cur');
    971         $(e.currentTarget).addClass('cur');
    972         _this.choose(e);
    973         return e.preventDefault();
    974       };
    975     })(this));
    976   };
    977 
    978   View.prototype.visible = function() {
    979     return $.expr.filters.visible(this.$el[0]);
    980   };
    981 
    982   View.prototype.highlighted = function() {
    983     return this.$el.find(".cur").length > 0;
    984   };
    985 
    986   View.prototype.choose = function(e) {
    987     var $li, content;
    988     if (($li = this.$el.find(".cur")).length) {
    989       content = this.context.insertContentFor($li);
    990       this.context._stopDelayedCall();
    991       this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);
    992       this.context.trigger("inserted", [$li, e]);
    993       this.hide(e);
    994     }
    995     if (this.context.getOpt("hideWithoutSuffix")) {
    996       return this.stopShowing = true;
    997     }
    998   };
    999 
    1000   View.prototype.reposition = function(rect) {
    1001     var _window, offset, overflowOffset, ref;
    1002     _window = this.context.app.iframeAsRoot ? this.context.app.window : window;
    1003     if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
    1004       rect.bottom = rect.top - this.$el.height();
    1005     }
    1006     if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
    1007       rect.left = overflowOffset;
    1008     }
    1009     offset = {
    1010       left: rect.left,
    1011       top: rect.bottom
    1012     };
    1013     if ((ref = this.context.callbacks("beforeReposition")) != null) {
    1014       ref.call(this.context, offset);
    1015     }
    1016     this.$el.offset(offset);
    1017     return this.context.trigger("reposition", [offset]);
    1018   };
    1019 
    1020   View.prototype.next = function() {
    1021     var cur, next, nextEl, offset;
    1022     cur = this.$el.find('.cur').removeClass('cur');
    1023     next = cur.next();
    1024     if (!next.length) {
    1025       next = this.$el.find('li:first');
    1026     }
    1027     next.addClass('cur');
    1028     nextEl = next[0];
    1029     offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
    1030     return this.scrollTop(Math.max(0, offset - this.$el.height()));
    1031   };
    1032 
    1033   View.prototype.prev = function() {
    1034     var cur, offset, prev, prevEl;
    1035     cur = this.$el.find('.cur').removeClass('cur');
    1036     prev = cur.prev();
    1037     if (!prev.length) {
    1038       prev = this.$el.find('li:last');
    1039     }
    1040     prev.addClass('cur');
    1041     prevEl = prev[0];
    1042     offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
    1043     return this.scrollTop(Math.max(0, offset - this.$el.height()));
    1044   };
    1045 
    1046   View.prototype.scrollTop = function(scrollTop) {
    1047     var scrollDuration;
    1048     scrollDuration = this.context.getOpt('scrollDuration');
    1049     if (scrollDuration) {
    1050       return this.$elUl.animate({
    1051         scrollTop: scrollTop
    1052       }, scrollDuration);
    1053     } else {
    1054       return this.$elUl.scrollTop(scrollTop);
    1055     }
    1056   };
    1057 
    1058   View.prototype.show = function() {
    1059     var rect;
    1060     if (this.stopShowing) {
    1061       this.stopShowing = false;
    1062       return;
    1063     }
    1064     if (!this.visible()) {
    1065       this.$el.show();
    1066       this.$el.scrollTop(0);
    1067       this.context.trigger('shown');
    1068     }
    1069     if (rect = this.context.rect()) {
    1070       return this.reposition(rect);
    1071     }
    1072   };
    1073 
    1074   View.prototype.hide = function(e, time) {
    1075     var callback;
    1076     if (!this.visible()) {
    1077       return;
    1078     }
    1079     if (isNaN(time)) {
    1080       this.$el.hide();
    1081       return this.context.trigger('hidden', [e]);
    1082     } else {
    1083       callback = (function(_this) {
    1084         return function() {
    1085           return _this.hide();
    1086         };
    1087       })(this);
    1088       clearTimeout(this.timeoutID);
    1089       return this.timeoutID = setTimeout(callback, time);
    1090     }
    1091   };
    1092 
    1093   View.prototype.render = function(list) {
    1094     var $li, $ul, i, item, len, li, tpl;
    1095     if (!($.isArray(list) && list.length > 0)) {
    1096       this.hide();
    1097       return;
    1098     }
    1099     this.$el.find('ul').empty();
    1100     $ul = this.$el.find('ul');
    1101     tpl = this.context.getOpt('displayTpl');
    1102     for (i = 0, len = list.length; i < len; i++) {
    1103       item = list[i];
    1104       item = $.extend({}, item, {
    1105         'atwho-at': this.context.at
    1106       });
    1107       li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");
    1108       $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
    1109       $li.data("item-data", item);
    1110       $ul.append($li);
    1111     }
    1112     this.show();
    1113     if (this.context.getOpt('highlightFirst')) {
    1114       return $ul.find("li:first").addClass("cur");
    1115     }
    1116   };
    1117 
    1118   return View;
    1119 
    1120 })();
    1121 
    1122 var Api;
    1123 
    1124 Api = {
    1125   load: function(at, data) {
    1126     var c;
    1127     if (c = this.controller(at)) {
    1128       return c.model.load(data);
    1129     }
    1130   },
    1131   isSelecting: function() {
    1132     var ref;
    1133     return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);
    1134   },
    1135   hide: function() {
    1136     var ref;
    1137     return (ref = this.controller()) != null ? ref.view.hide() : void 0;
    1138   },
    1139   reposition: function() {
    1140     var c;
    1141     if (c = this.controller()) {
    1142       return c.view.reposition(c.rect());
    1143     }
    1144   },
    1145   setIframe: function(iframe, asRoot) {
    1146     this.setupRootElement(iframe, asRoot);
    1147     return null;
    1148   },
    1149   run: function() {
    1150     return this.dispatch();
    1151   },
    1152   destroy: function() {
    1153     this.shutdown();
    1154     return this.$inputor.data('atwho', null);
    1155   }
    1156 };
    1157 
    1158 $.fn.atwho = function(method) {
    1159   var _args, result;
    1160   _args = arguments;
    1161   result = null;
    1162   this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() {
    1163     var $this, app;
    1164     if (!(app = ($this = $(this)).data("atwho"))) {
    1165       $this.data('atwho', (app = new App(this)));
    1166     }
    1167     if (typeof method === 'object' || !method) {
    1168       return app.reg(method.at, method);
    1169     } else if (Api[method] && app) {
    1170       return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
    1171     } else {
    1172       return $.error("Method " + method + " does not exist on jQuery.atwho");
    1173     }
    1174   });
    1175   if (result != null) {
    1176     return result;
    1177   } else {
    1178     return this;
    1179   }
    1180 };
    1181 
    1182 $.fn.atwho["default"] = {
    1183   at: void 0,
    1184   alias: void 0,
    1185   data: null,
    1186   displayTpl: "<li>${name}</li>",
    1187   insertTpl: "${atwho-at}${name}",
    1188   headerTpl: null,
    1189   callbacks: DEFAULT_CALLBACKS,
    1190   functionOverrides: {},
    1191   searchKey: "name",
    1192   suffix: void 0,
    1193   hideWithoutSuffix: false,
    1194   startWithSpace: true,
    1195   acceptSpaceBar: false,
    1196   highlightFirst: true,
    1197   limit: 5,
    1198   maxLen: 20,
    1199   minLen: 0,
    1200   displayTimeout: 300,
    1201   delay: null,
    1202   spaceSelectsMatch: false,
    1203   tabSelectsMatch: true,
    1204   editableAtwhoQueryAttrs: {},
    1205   scrollDuration: 150,
    1206   suspendOnComposing: true,
    1207   lookUpOnClick: true
    1208 };
    1209 
    1210 $.fn.atwho.debug = false;
    1211 
    1212 }));
  • deleted file src/bp-core/js/vendor/jquery.atwho.txt

    diff --git src/bp-core/js/vendor/jquery.atwho.txt src/bp-core/js/vendor/jquery.atwho.txt
    deleted file mode 100644
    index 36cd1c122..000000000
    + -  
    1 Copyright (c) 2013 chord.luo@gmail.com
    2 
    3 Permission is hereby granted, free of charge, to any person
    4 obtaining a copy of this software and associated documentation
    5 files (the "Software"), to deal in the Software without
    6 restriction, including without limitation the rights to use,
    7 copy, modify, merge, publish, distribute, sublicense, and/or sell
    8 copies of the Software, and to permit persons to whom the
    9 Software is furnished to do so, subject to the following
    10 conditions:
    11 
    12 The above copyright notice and this permission notice shall be
    13 included in all copies or substantial portions of the Software.
    14 
    15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    22 OTHER DEALINGS IN THE SOFTWARE.
  • deleted file src/bp-core/js/vendor/jquery.caret.js

    diff --git src/bp-core/js/vendor/jquery.caret.js src/bp-core/js/vendor/jquery.caret.js
    deleted file mode 100755
    index 811ec63ee..000000000
    + -  
    1 (function (root, factory) {
    2   if (typeof define === 'function' && define.amd) {
    3     // AMD. Register as an anonymous module.
    4     define(["jquery"], function ($) {
    5       return (root.returnExportsGlobal = factory($));
    6     });
    7   } else if (typeof exports === 'object') {
    8     // Node. Does not work with strict CommonJS, but
    9     // only CommonJS-like enviroments that support module.exports,
    10     // like Node.
    11     module.exports = factory(require("jquery"));
    12   } else {
    13     factory(jQuery);
    14   }
    15 }(this, function ($) {
    16 
    17 /*
    18   Implement Github like autocomplete mentions
    19   http://ichord.github.com/At.js
    20 
    21   Copyright (c) 2013 chord.luo@gmail.com
    22   Licensed under the MIT license.
    23 */
    24 
    25 /*
    26 本插件操作 textarea 或者 input 内的插入符
    27 只实现了获得插入符在文本框中的位置,我设置
    28 插入符的位置.
    29 */
    30 
    31 "use strict";
    32 var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
    33 
    34 pluginName = 'caret';
    35 
    36 EditableCaret = (function() {
    37   function EditableCaret($inputor) {
    38     this.$inputor = $inputor;
    39     this.domInputor = this.$inputor[0];
    40   }
    41 
    42   EditableCaret.prototype.setPos = function(pos) {
    43     var fn, found, offset, sel;
    44     if (sel = oWindow.getSelection()) {
    45       offset = 0;
    46       found = false;
    47       (fn = function(pos, parent) {
    48         var node, range, _i, _len, _ref, _results;
    49         _ref = parent.childNodes;
    50         _results = [];
    51         for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    52           node = _ref[_i];
    53           if (found) {
    54             break;
    55           }
    56           if (node.nodeType === 3) {
    57             if (offset + node.length >= pos) {
    58               found = true;
    59               range = oDocument.createRange();
    60               range.setStart(node, pos - offset);
    61               sel.removeAllRanges();
    62               sel.addRange(range);
    63               break;
    64             } else {
    65               _results.push(offset += node.length);
    66             }
    67           } else {
    68             _results.push(fn(pos, node));
    69           }
    70         }
    71         return _results;
    72       })(pos, this.domInputor);
    73     }
    74     return this.domInputor;
    75   };
    76 
    77   EditableCaret.prototype.getIEPosition = function() {
    78     return this.getPosition();
    79   };
    80 
    81   EditableCaret.prototype.getPosition = function() {
    82     var inputor_offset, offset;
    83     offset = this.getOffset();
    84     inputor_offset = this.$inputor.offset();
    85     offset.left -= inputor_offset.left;
    86     offset.top -= inputor_offset.top;
    87     return offset;
    88   };
    89 
    90   EditableCaret.prototype.getOldIEPos = function() {
    91     var preCaretTextRange, textRange;
    92     textRange = oDocument.selection.createRange();
    93     preCaretTextRange = oDocument.body.createTextRange();
    94     preCaretTextRange.moveToElementText(this.domInputor);
    95     preCaretTextRange.setEndPoint("EndToEnd", textRange);
    96     return preCaretTextRange.text.length;
    97   };
    98 
    99   EditableCaret.prototype.getPos = function() {
    100     var clonedRange, pos, range;
    101     if (range = this.range()) {
    102       clonedRange = range.cloneRange();
    103       clonedRange.selectNodeContents(this.domInputor);
    104       clonedRange.setEnd(range.endContainer, range.endOffset);
    105       pos = clonedRange.toString().length;
    106       clonedRange.detach();
    107       return pos;
    108     } else if (oDocument.selection) {
    109       return this.getOldIEPos();
    110     }
    111   };
    112 
    113   EditableCaret.prototype.getOldIEOffset = function() {
    114     var range, rect;
    115     range = oDocument.selection.createRange().duplicate();
    116     range.moveStart("character", -1);
    117     rect = range.getBoundingClientRect();
    118     return {
    119       height: rect.bottom - rect.top,
    120       left: rect.left,
    121       top: rect.top
    122     };
    123   };
    124 
    125   EditableCaret.prototype.getOffset = function(pos) {
    126     var clonedRange, offset, range, rect, shadowCaret;
    127     if (oWindow.getSelection && (range = this.range())) {
    128       if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
    129         clonedRange = range.cloneRange();
    130         clonedRange.setStart(range.endContainer, range.endOffset - 1);
    131         clonedRange.setEnd(range.endContainer, range.endOffset);
    132         rect = clonedRange.getBoundingClientRect();
    133         offset = {
    134           height: rect.height,
    135           left: rect.left + rect.width,
    136           top: rect.top
    137         };
    138         clonedRange.detach();
    139       }
    140       if (!offset || (offset != null ? offset.height : void 0) === 0) {
    141         clonedRange = range.cloneRange();
    142         shadowCaret = $(oDocument.createTextNode("|"));
    143         clonedRange.insertNode(shadowCaret[0]);
    144         clonedRange.selectNode(shadowCaret[0]);
    145         rect = clonedRange.getBoundingClientRect();
    146         offset = {
    147           height: rect.height,
    148           left: rect.left,
    149           top: rect.top
    150         };
    151         shadowCaret.remove();
    152         clonedRange.detach();
    153       }
    154     } else if (oDocument.selection) {
    155       offset = this.getOldIEOffset();
    156     }
    157     if (offset) {
    158       offset.top += $(oWindow).scrollTop();
    159       offset.left += $(oWindow).scrollLeft();
    160     }
    161     return offset;
    162   };
    163 
    164   EditableCaret.prototype.range = function() {
    165     var sel;
    166     if (!oWindow.getSelection) {
    167       return;
    168     }
    169     sel = oWindow.getSelection();
    170     if (sel.rangeCount > 0) {
    171       return sel.getRangeAt(0);
    172     } else {
    173       return null;
    174     }
    175   };
    176 
    177   return EditableCaret;
    178 
    179 })();
    180 
    181 InputCaret = (function() {
    182   function InputCaret($inputor) {
    183     this.$inputor = $inputor;
    184     this.domInputor = this.$inputor[0];
    185   }
    186 
    187   InputCaret.prototype.getIEPos = function() {
    188     var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
    189     inputor = this.domInputor;
    190     range = oDocument.selection.createRange();
    191     pos = 0;
    192     if (range && range.parentElement() === inputor) {
    193       normalizedValue = inputor.value.replace(/\r\n/g, "\n");
    194       len = normalizedValue.length;
    195       textInputRange = inputor.createTextRange();
    196       textInputRange.moveToBookmark(range.getBookmark());
    197       endRange = inputor.createTextRange();
    198       endRange.collapse(false);
    199       if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
    200         pos = len;
    201       } else {
    202         pos = -textInputRange.moveStart("character", -len);
    203       }
    204     }
    205     return pos;
    206   };
    207 
    208   InputCaret.prototype.getPos = function() {
    209     if (oDocument.selection) {
    210       return this.getIEPos();
    211     } else {
    212       return this.domInputor.selectionStart;
    213     }
    214   };
    215 
    216   InputCaret.prototype.setPos = function(pos) {
    217     var inputor, range;
    218     inputor = this.domInputor;
    219     if (oDocument.selection) {
    220       range = inputor.createTextRange();
    221       range.move("character", pos);
    222       range.select();
    223     } else if (inputor.setSelectionRange) {
    224       inputor.setSelectionRange(pos, pos);
    225     }
    226     return inputor;
    227   };
    228 
    229   InputCaret.prototype.getIEOffset = function(pos) {
    230     var h, textRange, x, y;
    231     textRange = this.domInputor.createTextRange();
    232     pos || (pos = this.getPos());
    233     textRange.move('character', pos);
    234     x = textRange.boundingLeft;
    235     y = textRange.boundingTop;
    236     h = textRange.boundingHeight;
    237     return {
    238       left: x,
    239       top: y,
    240       height: h
    241     };
    242   };
    243 
    244   InputCaret.prototype.getOffset = function(pos) {
    245     var $inputor, offset, position;
    246     $inputor = this.$inputor;
    247     if (oDocument.selection) {
    248       offset = this.getIEOffset(pos);
    249       offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
    250       offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
    251       return offset;
    252     } else {
    253       offset = $inputor.offset();
    254       position = this.getPosition(pos);
    255       return offset = {
    256         left: offset.left + position.left - $inputor.scrollLeft(),
    257         top: offset.top + position.top - $inputor.scrollTop(),
    258         height: position.height
    259       };
    260     }
    261   };
    262 
    263   InputCaret.prototype.getPosition = function(pos) {
    264     var $inputor, at_rect, end_range, format, html, mirror, start_range;
    265     $inputor = this.$inputor;
    266     format = function(value) {
    267       value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
    268       if (/firefox/i.test(navigator.userAgent)) {
    269         value = value.replace(/\s/g, '&nbsp;');
    270       }
    271       return value;
    272     };
    273     if (pos === void 0) {
    274       pos = this.getPos();
    275     }
    276     start_range = $inputor.val().slice(0, pos);
    277     end_range = $inputor.val().slice(pos);
    278     html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
    279     html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
    280     html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
    281     mirror = new Mirror($inputor);
    282     return at_rect = mirror.create(html).rect();
    283   };
    284 
    285   InputCaret.prototype.getIEPosition = function(pos) {
    286     var h, inputorOffset, offset, x, y;
    287     offset = this.getIEOffset(pos);
    288     inputorOffset = this.$inputor.offset();
    289     x = offset.left - inputorOffset.left;
    290     y = offset.top - inputorOffset.top;
    291     h = offset.height;
    292     return {
    293       left: x,
    294       top: y,
    295       height: h
    296     };
    297   };
    298 
    299   return InputCaret;
    300 
    301 })();
    302 
    303 Mirror = (function() {
    304   Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];
    305 
    306   function Mirror($inputor) {
    307     this.$inputor = $inputor;
    308   }
    309 
    310   Mirror.prototype.mirrorCss = function() {
    311     var css,
    312       _this = this;
    313     css = {
    314       position: 'absolute',
    315       left: -9999,
    316       top: 0,
    317       zIndex: -20000
    318     };
    319     if (this.$inputor.prop('tagName') === 'TEXTAREA') {
    320       this.css_attr.push('width');
    321     }
    322     $.each(this.css_attr, function(i, p) {
    323       return css[p] = _this.$inputor.css(p);
    324     });
    325     return css;
    326   };
    327 
    328   Mirror.prototype.create = function(html) {
    329     this.$mirror = $('<div></div>');
    330     this.$mirror.css(this.mirrorCss());
    331     this.$mirror.html(html);
    332     this.$inputor.after(this.$mirror);
    333     return this;
    334   };
    335 
    336   Mirror.prototype.rect = function() {
    337     var $flag, pos, rect;
    338     $flag = this.$mirror.find("#caret");
    339     pos = $flag.position();
    340     rect = {
    341       left: pos.left,
    342       top: pos.top,
    343       height: $flag.height()
    344     };
    345     this.$mirror.remove();
    346     return rect;
    347   };
    348 
    349   return Mirror;
    350 
    351 })();
    352 
    353 Utils = {
    354   contentEditable: function($inputor) {
    355     return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
    356   }
    357 };
    358 
    359 methods = {
    360   pos: function(pos) {
    361     if (pos || pos === 0) {
    362       return this.setPos(pos);
    363     } else {
    364       return this.getPos();
    365     }
    366   },
    367   position: function(pos) {
    368     if (oDocument.selection) {
    369       return this.getIEPosition(pos);
    370     } else {
    371       return this.getPosition(pos);
    372     }
    373   },
    374   offset: function(pos) {
    375     var offset;
    376     offset = this.getOffset(pos);
    377     return offset;
    378   }
    379 };
    380 
    381 oDocument = null;
    382 
    383 oWindow = null;
    384 
    385 oFrame = null;
    386 
    387 setContextBy = function(settings) {
    388   var iframe;
    389   if (iframe = settings != null ? settings.iframe : void 0) {
    390     oFrame = iframe;
    391     oWindow = iframe.contentWindow;
    392     return oDocument = iframe.contentDocument || oWindow.document;
    393   } else {
    394     oFrame = void 0;
    395     oWindow = window;
    396     return oDocument = document;
    397   }
    398 };
    399 
    400 discoveryIframeOf = function($dom) {
    401   var error;
    402   oDocument = $dom[0].ownerDocument;
    403   oWindow = oDocument.defaultView || oDocument.parentWindow;
    404   try {
    405     return oFrame = oWindow.frameElement;
    406   } catch (_error) {
    407     error = _error;
    408   }
    409 };
    410 
    411 $.fn.caret = function(method, value, settings) {
    412   var caret;
    413   if (methods[method]) {
    414     if ($.isPlainObject(value)) {
    415       setContextBy(value);
    416       value = void 0;
    417     } else {
    418       setContextBy(settings);
    419     }
    420     caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
    421     return methods[method].apply(caret, [value]);
    422   } else {
    423     return $.error("Method " + method + " does not exist on jQuery.caret");
    424   }
    425 };
    426 
    427 $.fn.caret.EditableCaret = EditableCaret;
    428 
    429 $.fn.caret.InputCaret = InputCaret;
    430 
    431 $.fn.caret.Utils = Utils;
    432 
    433 $.fn.caret.apis = methods;
    434 
    435 
    436 }));
  • deleted file src/bp-core/js/vendor/jquery.caret.txt

    diff --git src/bp-core/js/vendor/jquery.caret.txt src/bp-core/js/vendor/jquery.caret.txt
    deleted file mode 100644
    index 36cd1c122..000000000
    + -  
    1 Copyright (c) 2013 chord.luo@gmail.com
    2 
    3 Permission is hereby granted, free of charge, to any person
    4 obtaining a copy of this software and associated documentation
    5 files (the "Software"), to deal in the Software without
    6 restriction, including without limitation the rights to use,
    7 copy, modify, merge, publish, distribute, sublicense, and/or sell
    8 copies of the Software, and to permit persons to whom the
    9 Software is furnished to do so, subject to the following
    10 conditions:
    11 
    12 The above copyright notice and this permission notice shall be
    13 included in all copies or substantial portions of the Software.
    14 
    15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    22 OTHER DEALINGS IN THE SOFTWARE.
  • new file src/bp-core/js/vendor/tribute.js

    diff --git src/bp-core/js/vendor/tribute.js src/bp-core/js/vendor/tribute.js
    new file mode 100644
    index 000000000..bd029c928
    - +  
     1(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Tribute = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
     2"use strict";
     3
     4Object.defineProperty(exports, "__esModule", {
     5    value: true
     6});
     7
     8var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
     9
     10var _utils = require("./utils");
     11
     12var _utils2 = _interopRequireDefault(_utils);
     13
     14var _TributeEvents = require("./TributeEvents");
     15
     16var _TributeEvents2 = _interopRequireDefault(_TributeEvents);
     17
     18var _TributeMenuEvents = require("./TributeMenuEvents");
     19
     20var _TributeMenuEvents2 = _interopRequireDefault(_TributeMenuEvents);
     21
     22var _TributeRange = require("./TributeRange");
     23
     24var _TributeRange2 = _interopRequireDefault(_TributeRange);
     25
     26var _TributeSearch = require("./TributeSearch");
     27
     28var _TributeSearch2 = _interopRequireDefault(_TributeSearch);
     29
     30function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     31
     32function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
     33
     34var Tribute = function () {
     35    function Tribute(_ref) {
     36        var _this = this;
     37
     38        var _ref$values = _ref.values,
     39            values = _ref$values === undefined ? null : _ref$values,
     40            _ref$iframe = _ref.iframe,
     41            iframe = _ref$iframe === undefined ? null : _ref$iframe,
     42            _ref$selectClass = _ref.selectClass,
     43            selectClass = _ref$selectClass === undefined ? 'highlight' : _ref$selectClass,
     44            _ref$trigger = _ref.trigger,
     45            trigger = _ref$trigger === undefined ? '@' : _ref$trigger,
     46            _ref$selectTemplate = _ref.selectTemplate,
     47            selectTemplate = _ref$selectTemplate === undefined ? null : _ref$selectTemplate,
     48            _ref$menuItemTemplate = _ref.menuItemTemplate,
     49            menuItemTemplate = _ref$menuItemTemplate === undefined ? null : _ref$menuItemTemplate,
     50            _ref$lookup = _ref.lookup,
     51            lookup = _ref$lookup === undefined ? 'key' : _ref$lookup,
     52            _ref$fillAttr = _ref.fillAttr,
     53            fillAttr = _ref$fillAttr === undefined ? 'value' : _ref$fillAttr,
     54            _ref$collection = _ref.collection,
     55            collection = _ref$collection === undefined ? null : _ref$collection,
     56            _ref$menuContainer = _ref.menuContainer,
     57            menuContainer = _ref$menuContainer === undefined ? null : _ref$menuContainer,
     58            _ref$noMatchTemplate = _ref.noMatchTemplate,
     59            noMatchTemplate = _ref$noMatchTemplate === undefined ? null : _ref$noMatchTemplate,
     60            _ref$requireLeadingSp = _ref.requireLeadingSpace,
     61            requireLeadingSpace = _ref$requireLeadingSp === undefined ? true : _ref$requireLeadingSp,
     62            _ref$allowSpaces = _ref.allowSpaces,
     63            allowSpaces = _ref$allowSpaces === undefined ? false : _ref$allowSpaces,
     64            _ref$replaceTextSuffi = _ref.replaceTextSuffix,
     65            replaceTextSuffix = _ref$replaceTextSuffi === undefined ? null : _ref$replaceTextSuffi,
     66            _ref$positionMenu = _ref.positionMenu,
     67            positionMenu = _ref$positionMenu === undefined ? true : _ref$positionMenu,
     68            _ref$spaceSelectsMatc = _ref.spaceSelectsMatch,
     69            spaceSelectsMatch = _ref$spaceSelectsMatc === undefined ? false : _ref$spaceSelectsMatc;
     70
     71        _classCallCheck(this, Tribute);
     72
     73        this.menuSelected = 0;
     74        this.current = {};
     75        this.inputEvent = false;
     76        this.isActive = false;
     77        this.menuContainer = menuContainer;
     78        this.allowSpaces = allowSpaces;
     79        this.replaceTextSuffix = replaceTextSuffix;
     80        this.positionMenu = positionMenu;
     81        this.hasTrailingSpace = false;
     82        this.spaceSelectsMatch = spaceSelectsMatch;
     83
     84        if (values) {
     85            this.collection = [{
     86                // symbol that starts the lookup
     87                trigger: trigger,
     88
     89                // is it wrapped in an iframe
     90                iframe: iframe,
     91
     92                // class applied to selected item
     93                selectClass: selectClass,
     94
     95                // function called on select that retuns the content to insert
     96                selectTemplate: (selectTemplate || Tribute.defaultSelectTemplate).bind(this),
     97
     98                // function called that returns content for an item
     99                menuItemTemplate: (menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(this),
     100
     101                // function called when menu is empty, disables hiding of menu.
     102                noMatchTemplate: function (t) {
     103                    if (typeof t === 'function') {
     104                        return t.bind(_this);
     105                    }
     106
     107                    return noMatchTemplate;
     108                }(noMatchTemplate),
     109
     110                // column to search against in the object
     111                lookup: lookup,
     112
     113                // column that contains the content to insert by default
     114                fillAttr: fillAttr,
     115
     116                // array of objects or a function returning an array of objects
     117                values: values,
     118
     119                requireLeadingSpace: requireLeadingSpace
     120            }];
     121        } else if (collection) {
     122            this.collection = collection.map(function (item) {
     123                return {
     124                    trigger: item.trigger || trigger,
     125                    iframe: item.iframe || iframe,
     126                    selectClass: item.selectClass || selectClass,
     127                    selectTemplate: (item.selectTemplate || Tribute.defaultSelectTemplate).bind(_this),
     128                    menuItemTemplate: (item.menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(_this),
     129                    // function called when menu is empty, disables hiding of menu.
     130                    noMatchTemplate: function (t) {
     131                        if (typeof t === 'function') {
     132                            return t.bind(_this);
     133                        }
     134
     135                        return null;
     136                    }(noMatchTemplate),
     137                    lookup: item.lookup || lookup,
     138                    fillAttr: item.fillAttr || fillAttr,
     139                    values: item.values,
     140                    requireLeadingSpace: item.requireLeadingSpace
     141                };
     142            });
     143        } else {
     144            throw new Error('[Tribute] No collection specified.');
     145        }
     146
     147        new _TributeRange2.default(this);
     148        new _TributeEvents2.default(this);
     149        new _TributeMenuEvents2.default(this);
     150        new _TributeSearch2.default(this);
     151    }
     152
     153    _createClass(Tribute, [{
     154        key: "triggers",
     155        value: function triggers() {
     156            return this.collection.map(function (config) {
     157                return config.trigger;
     158            });
     159        }
     160    }, {
     161        key: "attach",
     162        value: function attach(el) {
     163            if (!el) {
     164                throw new Error('[Tribute] Must pass in a DOM node or NodeList.');
     165            }
     166
     167            // Check if it is a jQuery collection
     168            if (typeof jQuery !== 'undefined' && el instanceof jQuery) {
     169                el = el.get();
     170            }
     171
     172            // Is el an Array/Array-like object?
     173            if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
     174                var length = el.length;
     175                for (var i = 0; i < length; ++i) {
     176                    this._attach(el[i]);
     177                }
     178            } else {
     179                this._attach(el);
     180            }
     181        }
     182    }, {
     183        key: "_attach",
     184        value: function _attach(el) {
     185            if (el.hasAttribute('data-tribute')) {
     186                console.warn('Tribute was already bound to ' + el.nodeName);
     187            }
     188
     189            this.ensureEditable(el);
     190            this.events.bind(el);
     191            el.setAttribute('data-tribute', true);
     192        }
     193    }, {
     194        key: "ensureEditable",
     195        value: function ensureEditable(element) {
     196            if (Tribute.inputTypes().indexOf(element.nodeName) === -1) {
     197                if (element.contentEditable) {
     198                    element.contentEditable = true;
     199                } else {
     200                    throw new Error('[Tribute] Cannot bind to ' + element.nodeName);
     201                }
     202            }
     203        }
     204    }, {
     205        key: "createMenu",
     206        value: function createMenu() {
     207            var wrapper = this.range.getDocument().createElement('div'),
     208                ul = this.range.getDocument().createElement('ul');
     209
     210            wrapper.className = 'tribute-container';
     211            wrapper.appendChild(ul);
     212
     213            if (this.menuContainer) {
     214                return this.menuContainer.appendChild(wrapper);
     215            }
     216
     217            return this.range.getDocument().body.appendChild(wrapper);
     218        }
     219    }, {
     220        key: "showMenuFor",
     221        value: function showMenuFor(element, scrollTo) {
     222            var _this2 = this;
     223
     224            // Only proceed if menu isn't already shown for the current element & mentionText
     225            if (this.isActive && this.current.element === element && this.current.mentionText === this.currentMentionTextSnapshot) {
     226                return;
     227            }
     228            this.currentMentionTextSnapshot = this.current.mentionText;
     229
     230            // create the menu if it doesn't exist.
     231            if (!this.menu) {
     232                this.menu = this.createMenu();
     233                element.tributeMenu = this.menu;
     234                this.menuEvents.bind(this.menu);
     235            }
     236
     237            this.isActive = true;
     238            this.menuSelected = 0;
     239
     240            if (!this.current.mentionText) {
     241                this.current.mentionText = '';
     242            }
     243
     244            var processValues = function processValues(values) {
     245                // Tribute may not be active any more by the time the value callback returns
     246                if (!_this2.isActive) {
     247                    return;
     248                }
     249
     250                var items = _this2.search.filter(_this2.current.mentionText, values, {
     251                    pre: '<span>',
     252                    post: '</span>',
     253                    extract: function extract(el) {
     254                        if (typeof _this2.current.collection.lookup === 'string') {
     255                            return el[_this2.current.collection.lookup];
     256                        } else if (typeof _this2.current.collection.lookup === 'function') {
     257                            return _this2.current.collection.lookup(el, _this2.current.mentionText);
     258                        } else {
     259                            throw new Error('Invalid lookup attribute, lookup must be string or function.');
     260                        }
     261                    }
     262                });
     263
     264                _this2.current.filteredItems = items;
     265
     266                var ul = _this2.menu.querySelector('ul');
     267
     268                _this2.range.positionMenuAtCaret(scrollTo);
     269
     270                if (!items.length) {
     271                    var noMatchEvent = new CustomEvent('tribute-no-match', { detail: _this2.menu });
     272                    _this2.current.element.dispatchEvent(noMatchEvent);
     273                    if (!_this2.current.collection.noMatchTemplate) {
     274                        _this2.hideMenu();
     275                    } else {
     276                        ul.innerHTML = _this2.current.collection.noMatchTemplate();
     277                    }
     278
     279                    return;
     280                }
     281
     282                ul.innerHTML = '';
     283
     284                items.forEach(function (item, index) {
     285                    var li = _this2.range.getDocument().createElement('li');
     286                    li.setAttribute('data-index', index);
     287                    li.addEventListener('mouseenter', function (e) {
     288                        var li = e.target;
     289                        var index = li.getAttribute('data-index');
     290                        _this2.events.setActiveLi(index);
     291                    });
     292                    if (_this2.menuSelected === index) {
     293                        li.className = _this2.current.collection.selectClass;
     294                    }
     295                    li.innerHTML = _this2.current.collection.menuItemTemplate(item);
     296                    ul.appendChild(li);
     297                });
     298            };
     299
     300            if (typeof this.current.collection.values === 'function') {
     301                this.current.collection.values(this.current.mentionText, processValues);
     302            } else {
     303                processValues(this.current.collection.values);
     304            }
     305        }
     306    }, {
     307        key: "showMenuForCollection",
     308        value: function showMenuForCollection(element, collectionIndex) {
     309            if (element !== document.activeElement) {
     310                this.placeCaretAtEnd(element);
     311            }
     312
     313            this.current.collection = this.collection[collectionIndex || 0];
     314            this.current.externalTrigger = true;
     315            this.current.element = element;
     316
     317            if (element.isContentEditable) this.insertTextAtCursor(this.current.collection.trigger);else this.insertAtCaret(element, this.current.collection.trigger);
     318
     319            this.showMenuFor(element);
     320        }
     321
     322        // TODO: make sure this works for inputs/textareas
     323
     324    }, {
     325        key: "placeCaretAtEnd",
     326        value: function placeCaretAtEnd(el) {
     327            el.focus();
     328            if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
     329                var range = document.createRange();
     330                range.selectNodeContents(el);
     331                range.collapse(false);
     332                var sel = window.getSelection();
     333                sel.removeAllRanges();
     334                sel.addRange(range);
     335            } else if (typeof document.body.createTextRange != "undefined") {
     336                var textRange = document.body.createTextRange();
     337                textRange.moveToElementText(el);
     338                textRange.collapse(false);
     339                textRange.select();
     340            }
     341        }
     342
     343        // for contenteditable
     344
     345    }, {
     346        key: "insertTextAtCursor",
     347        value: function insertTextAtCursor(text) {
     348            var sel, range, html;
     349            sel = window.getSelection();
     350            range = sel.getRangeAt(0);
     351            range.deleteContents();
     352            var textNode = document.createTextNode(text);
     353            range.insertNode(textNode);
     354            range.selectNodeContents(textNode);
     355            range.collapse(false);
     356            sel.removeAllRanges();
     357            sel.addRange(range);
     358        }
     359
     360        // for regular inputs
     361
     362    }, {
     363        key: "insertAtCaret",
     364        value: function insertAtCaret(textarea, text) {
     365            var scrollPos = textarea.scrollTop;
     366            var caretPos = textarea.selectionStart;
     367
     368            var front = textarea.value.substring(0, caretPos);
     369            var back = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
     370            textarea.value = front + text + back;
     371            caretPos = caretPos + text.length;
     372            textarea.selectionStart = caretPos;
     373            textarea.selectionEnd = caretPos;
     374            textarea.focus();
     375            textarea.scrollTop = scrollPos;
     376        }
     377    }, {
     378        key: "hideMenu",
     379        value: function hideMenu() {
     380            if (this.menu) {
     381                this.menu.style.cssText = 'display: none;';
     382                this.isActive = false;
     383                this.menuSelected = 0;
     384                this.current = {};
     385            }
     386        }
     387    }, {
     388        key: "selectItemAtIndex",
     389        value: function selectItemAtIndex(index, originalEvent) {
     390            index = parseInt(index);
     391            if (typeof index !== 'number') return;
     392            var item = this.current.filteredItems[index];
     393            var content = this.current.collection.selectTemplate(item);
     394            if (content !== null) this.replaceText(content, originalEvent, item);
     395        }
     396    }, {
     397        key: "replaceText",
     398        value: function replaceText(content, originalEvent, item) {
     399            this.range.replaceTriggerText(content, true, true, originalEvent, item);
     400        }
     401    }, {
     402        key: "_append",
     403        value: function _append(collection, newValues, replace) {
     404            if (typeof collection.values === 'function') {
     405                throw new Error('Unable to append to values, as it is a function.');
     406            } else if (!replace) {
     407                collection.values = collection.values.concat(newValues);
     408            } else {
     409                collection.values = newValues;
     410            }
     411        }
     412    }, {
     413        key: "append",
     414        value: function append(collectionIndex, newValues, replace) {
     415            var index = parseInt(collectionIndex);
     416            if (typeof index !== 'number') throw new Error('please provide an index for the collection to update.');
     417
     418            var collection = this.collection[index];
     419
     420            this._append(collection, newValues, replace);
     421        }
     422    }, {
     423        key: "appendCurrent",
     424        value: function appendCurrent(newValues, replace) {
     425            if (this.isActive) {
     426                this._append(this.current.collection, newValues, replace);
     427            } else {
     428                throw new Error('No active state. Please use append instead and pass an index.');
     429            }
     430        }
     431    }, {
     432        key: "detach",
     433        value: function detach(el) {
     434            if (!el) {
     435                throw new Error('[Tribute] Must pass in a DOM node or NodeList.');
     436            }
     437
     438            // Check if it is a jQuery collection
     439            if (typeof jQuery !== 'undefined' && el instanceof jQuery) {
     440                el = el.get();
     441            }
     442
     443            // Is el an Array/Array-like object?
     444            if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
     445                var length = el.length;
     446                for (var i = 0; i < length; ++i) {
     447                    this._detach(el[i]);
     448                }
     449            } else {
     450                this._detach(el);
     451            }
     452        }
     453    }, {
     454        key: "_detach",
     455        value: function _detach(el) {
     456            var _this3 = this;
     457
     458            this.events.unbind(el);
     459            if (el.tributeMenu) {
     460                this.menuEvents.unbind(el.tributeMenu);
     461            }
     462
     463            setTimeout(function () {
     464                el.removeAttribute('data-tribute');
     465                _this3.isActive = false;
     466                if (el.tributeMenu) {
     467                    el.tributeMenu.remove();
     468                }
     469            });
     470        }
     471    }], [{
     472        key: "defaultSelectTemplate",
     473        value: function defaultSelectTemplate(item) {
     474            if (typeof item === 'undefined') return null;
     475            if (this.range.isContentEditable(this.current.element)) {
     476                return '<span class="tribute-mention">' + (this.current.collection.trigger + item.original[this.current.collection.fillAttr]) + '</span>';
     477            }
     478
     479            return this.current.collection.trigger + item.original[this.current.collection.fillAttr];
     480        }
     481    }, {
     482        key: "defaultMenuItemTemplate",
     483        value: function defaultMenuItemTemplate(matchItem) {
     484            return matchItem.string;
     485        }
     486    }, {
     487        key: "inputTypes",
     488        value: function inputTypes() {
     489            return ['TEXTAREA', 'INPUT'];
     490        }
     491    }]);
     492
     493    return Tribute;
     494}();
     495
     496exports.default = Tribute;
     497module.exports = exports["default"];
     498
     499},{"./TributeEvents":2,"./TributeMenuEvents":3,"./TributeRange":4,"./TributeSearch":5,"./utils":7}],2:[function(require,module,exports){
     500'use strict';
     501
     502Object.defineProperty(exports, "__esModule", {
     503    value: true
     504});
     505
     506var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
     507
     508function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
     509
     510var TributeEvents = function () {
     511    function TributeEvents(tribute) {
     512        _classCallCheck(this, TributeEvents);
     513
     514        this.tribute = tribute;
     515        this.tribute.events = this;
     516    }
     517
     518    _createClass(TributeEvents, [{
     519        key: 'bind',
     520        value: function bind(element) {
     521            element.boundKeydown = this.keydown.bind(element, this);
     522            element.boundKeyup = this.keyup.bind(element, this);
     523            element.boundInput = this.input.bind(element, this);
     524
     525            element.addEventListener('keydown', element.boundKeydown, false);
     526            element.addEventListener('keyup', element.boundKeyup, false);
     527            element.addEventListener('input', element.boundInput, false);
     528        }
     529    }, {
     530        key: 'unbind',
     531        value: function unbind(element) {
     532            element.removeEventListener('keydown', element.boundKeydown, false);
     533            element.removeEventListener('keyup', element.boundKeyup, false);
     534            element.removeEventListener('input', element.boundInput, false);
     535
     536            delete element.boundKeydown;
     537            delete element.boundKeyup;
     538            delete element.boundInput;
     539        }
     540    }, {
     541        key: 'keydown',
     542        value: function keydown(instance, event) {
     543            if (instance.shouldDeactivate(event)) {
     544                instance.tribute.isActive = false;
     545                instance.tribute.hideMenu();
     546            }
     547
     548            var element = this;
     549            instance.commandEvent = false;
     550
     551            TributeEvents.keys().forEach(function (o) {
     552                if (o.key === event.keyCode) {
     553                    instance.commandEvent = true;
     554                    instance.callbacks()[o.value.toLowerCase()](event, element);
     555                }
     556            });
     557        }
     558    }, {
     559        key: 'input',
     560        value: function input(instance, event) {
     561            instance.inputEvent = true;
     562            instance.keyup.call(this, instance, event);
     563        }
     564    }, {
     565        key: 'click',
     566        value: function click(instance, event) {
     567            var tribute = instance.tribute;
     568            if (tribute.menu && tribute.menu.contains(event.target)) {
     569                var li = event.target;
     570                event.preventDefault();
     571                event.stopPropagation();
     572                while (li.nodeName.toLowerCase() !== 'li') {
     573                    li = li.parentNode;
     574                    if (!li || li === tribute.menu) {
     575                        throw new Error('cannot find the <li> container for the click');
     576                    }
     577                }
     578                tribute.selectItemAtIndex(li.getAttribute('data-index'), event);
     579                tribute.hideMenu();
     580
     581                // TODO: should fire with externalTrigger and target is outside of menu
     582            } else if (tribute.current.element && !tribute.current.externalTrigger) {
     583                tribute.current.externalTrigger = false;
     584                setTimeout(function () {
     585                    return tribute.hideMenu();
     586                });
     587            }
     588        }
     589    }, {
     590        key: 'keyup',
     591        value: function keyup(instance, event) {
     592            if (instance.inputEvent) {
     593                instance.inputEvent = false;
     594            }
     595            instance.updateSelection(this);
     596
     597            if (event.keyCode === 27) return;
     598
     599            if (!instance.tribute.allowSpaces && instance.tribute.hasTrailingSpace) {
     600                instance.tribute.hasTrailingSpace = false;
     601                instance.commandEvent = true;
     602                instance.callbacks()["space"](event, this);
     603                return;
     604            }
     605
     606            if (!instance.tribute.isActive) {
     607                var keyCode = instance.getKeyCode(instance, this, event);
     608
     609                if (isNaN(keyCode) || !keyCode) return;
     610
     611                var trigger = instance.tribute.triggers().find(function (trigger) {
     612                    return trigger.charCodeAt(0) === keyCode;
     613                });
     614
     615                if (typeof trigger !== 'undefined') {
     616                    instance.callbacks().triggerChar(event, this, trigger);
     617                }
     618            }
     619
     620            if (instance.tribute.current.trigger && instance.commandEvent === false || instance.tribute.isActive && event.keyCode === 8) {
     621                instance.tribute.showMenuFor(this, true);
     622            }
     623        }
     624    }, {
     625        key: 'shouldDeactivate',
     626        value: function shouldDeactivate(event) {
     627            if (!this.tribute.isActive) return false;
     628
     629            if (this.tribute.current.mentionText.length === 0) {
     630                var eventKeyPressed = false;
     631                TributeEvents.keys().forEach(function (o) {
     632                    if (event.keyCode === o.key) eventKeyPressed = true;
     633                });
     634
     635                return !eventKeyPressed;
     636            }
     637
     638            return false;
     639        }
     640    }, {
     641        key: 'getKeyCode',
     642        value: function getKeyCode(instance, el, event) {
     643            var char = void 0;
     644            var tribute = instance.tribute;
     645            var info = tribute.range.getTriggerInfo(false, tribute.hasTrailingSpace, true, tribute.allowSpaces);
     646
     647            if (info) {
     648                return info.mentionTriggerChar.charCodeAt(0);
     649            } else {
     650                return false;
     651            }
     652        }
     653    }, {
     654        key: 'updateSelection',
     655        value: function updateSelection(el) {
     656            this.tribute.current.element = el;
     657            var info = this.tribute.range.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces);
     658
     659            if (info) {
     660                this.tribute.current.selectedPath = info.mentionSelectedPath;
     661                this.tribute.current.mentionText = info.mentionText;
     662                this.tribute.current.selectedOffset = info.mentionSelectedOffset;
     663            }
     664        }
     665    }, {
     666        key: 'callbacks',
     667        value: function callbacks() {
     668            var _this = this;
     669
     670            return {
     671                triggerChar: function triggerChar(e, el, trigger) {
     672                    var tribute = _this.tribute;
     673                    tribute.current.trigger = trigger;
     674
     675                    var collectionItem = tribute.collection.find(function (item) {
     676                        return item.trigger === trigger;
     677                    });
     678
     679                    tribute.current.collection = collectionItem;
     680                    if (tribute.inputEvent) tribute.showMenuFor(el, true);
     681                },
     682                enter: function enter(e, el) {
     683                    // choose selection
     684                    if (_this.tribute.isActive) {
     685                        e.preventDefault();
     686                        e.stopPropagation();
     687                        setTimeout(function () {
     688                            _this.tribute.selectItemAtIndex(_this.tribute.menuSelected, e);
     689                            _this.tribute.hideMenu();
     690                        }, 0);
     691                    }
     692                },
     693                escape: function escape(e, el) {
     694                    if (_this.tribute.isActive) {
     695                        e.preventDefault();
     696                        e.stopPropagation();
     697                        _this.tribute.isActive = false;
     698                        _this.tribute.hideMenu();
     699                    }
     700                },
     701                tab: function tab(e, el) {
     702                    // choose first match
     703                    _this.callbacks().enter(e, el);
     704                },
     705                space: function space(e, el) {
     706                    if (_this.tribute.isActive) {
     707                        if (_this.tribute.spaceSelectsMatch) {
     708                            _this.callbacks().enter(e, el);
     709                        } else if (!_this.tribute.allowSpaces) {
     710                            e.stopPropagation();
     711                            setTimeout(function () {
     712                                _this.tribute.hideMenu();
     713                                _this.tribute.isActive = false;
     714                            }, 0);
     715                        }
     716                    }
     717                },
     718                up: function up(e, el) {
     719                    // navigate up ul
     720                    if (_this.tribute.isActive) {
     721                        e.preventDefault();
     722                        e.stopPropagation();
     723                        var count = _this.tribute.current.filteredItems.length,
     724                            selected = _this.tribute.menuSelected;
     725
     726                        if (count > selected && selected > 0) {
     727                            _this.tribute.menuSelected--;
     728                            _this.setActiveLi();
     729                        } else if (selected === 0) {
     730                            _this.tribute.menuSelected = count - 1;
     731                            _this.setActiveLi();
     732                            _this.tribute.menu.scrollTop = _this.tribute.menu.scrollHeight;
     733                        }
     734                    }
     735                },
     736                down: function down(e, el) {
     737                    // navigate down ul
     738                    if (_this.tribute.isActive) {
     739                        e.preventDefault();
     740                        e.stopPropagation();
     741                        var count = _this.tribute.current.filteredItems.length - 1,
     742                            selected = _this.tribute.menuSelected;
     743
     744                        if (count > selected) {
     745                            _this.tribute.menuSelected++;
     746                            _this.setActiveLi();
     747                        } else if (count === selected) {
     748                            _this.tribute.menuSelected = 0;
     749                            _this.setActiveLi();
     750                            _this.tribute.menu.scrollTop = 0;
     751                        }
     752                    }
     753                },
     754                delete: function _delete(e, el) {
     755                    if (_this.tribute.isActive && _this.tribute.current.mentionText.length < 1) {
     756                        _this.tribute.hideMenu();
     757                    } else if (_this.tribute.isActive) {
     758                        _this.tribute.showMenuFor(el);
     759                    }
     760                }
     761            };
     762        }
     763    }, {
     764        key: 'setActiveLi',
     765        value: function setActiveLi(index) {
     766            var lis = this.tribute.menu.querySelectorAll('li'),
     767                length = lis.length >>> 0;
     768
     769            // get heights
     770            var menuFullHeight = this.getFullHeight(this.tribute.menu),
     771                liHeight = this.getFullHeight(lis[0]);
     772
     773            if (index) this.tribute.menuSelected = index;
     774
     775            for (var i = 0; i < length; i++) {
     776                var li = lis[i];
     777                if (i === this.tribute.menuSelected) {
     778                    var offset = liHeight * (i + 1);
     779                    var scrollTop = this.tribute.menu.scrollTop;
     780                    var totalScroll = scrollTop + menuFullHeight;
     781
     782                    if (offset > totalScroll) {
     783                        this.tribute.menu.scrollTop += liHeight;
     784                    } else if (offset < totalScroll) {
     785                        this.tribute.menu.scrollTop -= liHeight;
     786                    }
     787
     788                    li.className = this.tribute.current.collection.selectClass;
     789                } else {
     790                    li.className = '';
     791                }
     792            }
     793        }
     794    }, {
     795        key: 'getFullHeight',
     796        value: function getFullHeight(elem, includeMargin) {
     797            var height = elem.getBoundingClientRect().height;
     798
     799            if (includeMargin) {
     800                var style = elem.currentStyle || window.getComputedStyle(elem);
     801                return height + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
     802            }
     803
     804            return height;
     805        }
     806    }], [{
     807        key: 'keys',
     808        value: function keys() {
     809            return [{
     810                key: 9,
     811                value: 'TAB'
     812            }, {
     813                key: 8,
     814                value: 'DELETE'
     815            }, {
     816                key: 13,
     817                value: 'ENTER'
     818            }, {
     819                key: 27,
     820                value: 'ESCAPE'
     821            }, {
     822                key: 32,
     823                value: 'SPACE'
     824            }, {
     825                key: 38,
     826                value: 'UP'
     827            }, {
     828                key: 40,
     829                value: 'DOWN'
     830            }];
     831        }
     832    }]);
     833
     834    return TributeEvents;
     835}();
     836
     837exports.default = TributeEvents;
     838module.exports = exports['default'];
     839
     840},{}],3:[function(require,module,exports){
     841'use strict';
     842
     843Object.defineProperty(exports, "__esModule", {
     844    value: true
     845});
     846
     847var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
     848
     849function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
     850
     851var TributeMenuEvents = function () {
     852    function TributeMenuEvents(tribute) {
     853        _classCallCheck(this, TributeMenuEvents);
     854
     855        this.tribute = tribute;
     856        this.tribute.menuEvents = this;
     857        this.menu = this.tribute.menu;
     858    }
     859
     860    _createClass(TributeMenuEvents, [{
     861        key: 'bind',
     862        value: function bind(menu) {
     863            var _this = this;
     864
     865            menu.menuKeydownEvent = this.tribute.events.keydown.bind(this.menu, this);
     866            this.menuClickEvent = this.tribute.events.click.bind(null, this);
     867            this.menuContainerScrollEvent = this.debounce(function () {
     868                if (_this.tribute.isActive) {
     869                    _this.tribute.showMenuFor(_this.tribute.current.element, false);
     870                }
     871            }, 300, false);
     872            this.windowResizeEvent = this.debounce(function () {
     873                if (_this.tribute.isActive) {
     874                    _this.tribute.range.positionMenuAtCaret(true);
     875                }
     876            }, 300, false);
     877
     878            // fixes IE11 issues with mouseup
     879            this.tribute.range.getDocument().addEventListener('MSPointerUp', this.menuClickEvent, false);
     880            menu.addEventListener('keydown', this.menuKeydownEvent, false);
     881            this.tribute.range.getDocument().addEventListener('mouseup', this.menuClickEvent, false);
     882            window.addEventListener('resize', this.windowResizeEvent);
     883
     884            if (this.menuContainer) {
     885                this.menuContainer.addEventListener('scroll', this.menuContainerScrollEvent, false);
     886            } else {
     887                window.addEventListener('scroll', this.menuContainerScrollEvent);
     888            }
     889        }
     890    }, {
     891        key: 'unbind',
     892        value: function unbind(menu) {
     893            menu.removeEventListener('keydown', menu.menuKeydownEvent, false);
     894            delete menu.menuKeydownEvent;
     895            this.tribute.range.getDocument().removeEventListener('mouseup', this.menuClickEvent, false);
     896            this.tribute.range.getDocument().removeEventListener('MSPointerUp', this.menuClickEvent, false);
     897            window.removeEventListener('resize', this.windowResizeEvent);
     898
     899            if (this.menuContainer) {
     900                this.menuContainer.removeEventListener('scroll', this.menuContainerScrollEvent, false);
     901            } else {
     902                window.removeEventListener('scroll', this.menuContainerScrollEvent);
     903            }
     904        }
     905    }, {
     906        key: 'debounce',
     907        value: function debounce(func, wait, immediate) {
     908            var _this2 = this,
     909                _arguments = arguments;
     910
     911            var timeout;
     912            return function () {
     913                var context = _this2,
     914                    args = _arguments;
     915                var later = function later() {
     916                    timeout = null;
     917                    if (!immediate) func.apply(context, args);
     918                };
     919                var callNow = immediate && !timeout;
     920                clearTimeout(timeout);
     921                timeout = setTimeout(later, wait);
     922                if (callNow) func.apply(context, args);
     923            };
     924        }
     925    }]);
     926
     927    return TributeMenuEvents;
     928}();
     929
     930exports.default = TributeMenuEvents;
     931module.exports = exports['default'];
     932
     933},{}],4:[function(require,module,exports){
     934'use strict';
     935
     936Object.defineProperty(exports, "__esModule", {
     937    value: true
     938});
     939
     940var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
     941
     942function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
     943
     944// Thanks to https://github.com/jeff-collins/ment.io
     945var TributeRange = function () {
     946    function TributeRange(tribute) {
     947        _classCallCheck(this, TributeRange);
     948
     949        this.tribute = tribute;
     950        this.tribute.range = this;
     951    }
     952
     953    _createClass(TributeRange, [{
     954        key: 'getDocument',
     955        value: function getDocument() {
     956            var iframe = void 0;
     957            if (this.tribute.current.collection) {
     958                iframe = this.tribute.current.collection.iframe;
     959            }
     960
     961            if (!iframe) {
     962                return document;
     963            }
     964
     965            return iframe.contentWindow.document;
     966        }
     967    }, {
     968        key: 'positionMenuAtCaret',
     969        value: function positionMenuAtCaret(scrollTo) {
     970            var _this = this;
     971
     972            var context = this.tribute.current,
     973                coordinates = void 0;
     974
     975            var info = this.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces);
     976
     977            if (typeof info !== 'undefined') {
     978
     979                if (!this.tribute.positionMenu) {
     980                    this.tribute.menu.style.cssText = 'display: block;';
     981                    return;
     982                }
     983
     984                if (!this.isContentEditable(context.element)) {
     985                    coordinates = this.getTextAreaOrInputUnderlinePosition(this.tribute.current.element, info.mentionPosition);
     986                } else {
     987                    coordinates = this.getContentEditableCaretPosition(info.mentionPosition);
     988                }
     989
     990                this.tribute.menu.style.cssText = 'top: ' + coordinates.top + 'px;\n                                     left: ' + coordinates.left + 'px;\n                                     right: ' + coordinates.right + 'px;\n                                     bottom: ' + coordinates.bottom + 'px;\n                                     position: absolute;\n                                     zIndex: 10000;\n                                     display: block;';
     991
     992                if (coordinates.left === 'auto') {
     993                    this.tribute.menu.style.left = 'auto';
     994                }
     995
     996                if (coordinates.top === 'auto') {
     997                    this.tribute.menu.style.top = 'auto';
     998                }
     999
     1000                if (scrollTo) this.scrollIntoView();
     1001
     1002                window.setTimeout(function () {
     1003                    var menuDimensions = {
     1004                        width: _this.tribute.menu.offsetWidth,
     1005                        height: _this.tribute.menu.offsetHeight
     1006                    };
     1007                    var menuIsOffScreen = _this.isMenuOffScreen(coordinates, menuDimensions);
     1008
     1009                    var menuIsOffScreenHorizontally = window.innerWidth > menuDimensions.width && (menuIsOffScreen.left || menuIsOffScreen.right);
     1010                    var menuIsOffScreenVertically = window.innerHeight > menuDimensions.height && (menuIsOffScreen.top || menuIsOffScreen.bottom);
     1011                    if (menuIsOffScreenHorizontally || menuIsOffScreenVertically) {
     1012                        _this.tribute.menu.style.cssText = 'display: none';
     1013                        _this.positionMenuAtCaret(scrollTo);
     1014                    }
     1015                }, 0);
     1016            } else {
     1017                this.tribute.menu.style.cssText = 'display: none';
     1018            }
     1019        }
     1020    }, {
     1021        key: 'selectElement',
     1022        value: function selectElement(targetElement, path, offset) {
     1023            var range = void 0;
     1024            var elem = targetElement;
     1025
     1026            if (path) {
     1027                for (var i = 0; i < path.length; i++) {
     1028                    elem = elem.childNodes[path[i]];
     1029                    if (elem === undefined) {
     1030                        return;
     1031                    }
     1032                    while (elem.length < offset) {
     1033                        offset -= elem.length;
     1034                        elem = elem.nextSibling;
     1035                    }
     1036                    if (elem.childNodes.length === 0 && !elem.length) {
     1037                        elem = elem.previousSibling;
     1038                    }
     1039                }
     1040            }
     1041            var sel = this.getWindowSelection();
     1042
     1043            range = this.getDocument().createRange();
     1044            range.setStart(elem, offset);
     1045            range.setEnd(elem, offset);
     1046            range.collapse(true);
     1047
     1048            try {
     1049                sel.removeAllRanges();
     1050            } catch (error) {}
     1051
     1052            sel.addRange(range);
     1053            targetElement.focus();
     1054        }
     1055    }, {
     1056        key: 'resetSelection',
     1057        value: function resetSelection(targetElement, path, offset) {
     1058            if (!this.isContentEditable(targetElement)) {
     1059                if (targetElement !== this.tribute.current.element) {
     1060                    targetElement.focus();
     1061                }
     1062            } else {
     1063                this.selectElement(targetElement, path, offset);
     1064            }
     1065        }
     1066    }, {
     1067        key: 'replaceTriggerText',
     1068        value: function replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) {
     1069            var context = this.tribute.current;
     1070            this.resetSelection(context.element, context.selectedPath, context.selectedOffset);
     1071
     1072            var info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces);
     1073
     1074            // Create the event
     1075            var replaceEvent = new CustomEvent('tribute-replaced', {
     1076                detail: {
     1077                    item: item,
     1078                    event: originalEvent
     1079                }
     1080            });
     1081
     1082            if (info !== undefined) {
     1083                if (!this.isContentEditable(context.element)) {
     1084                    var myField = this.tribute.current.element;
     1085                    var textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : ' ';
     1086                    text += textSuffix;
     1087                    var startPos = info.mentionPosition;
     1088                    var endPos = info.mentionPosition + info.mentionText.length + textSuffix.length;
     1089                    myField.value = myField.value.substring(0, startPos) + text + myField.value.substring(endPos, myField.value.length);
     1090                    myField.selectionStart = startPos + text.length;
     1091                    myField.selectionEnd = startPos + text.length;
     1092                } else {
     1093                    // add a space to the end of the pasted text
     1094                    var _textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : '\xA0';
     1095                    text += _textSuffix;
     1096                    this.pasteHtml(text, info.mentionPosition, info.mentionPosition + info.mentionText.length + 1);
     1097                }
     1098
     1099                context.element.dispatchEvent(replaceEvent);
     1100            }
     1101        }
     1102    }, {
     1103        key: 'pasteHtml',
     1104        value: function pasteHtml(html, startPos, endPos) {
     1105            var range = void 0,
     1106                sel = void 0;
     1107            sel = this.getWindowSelection();
     1108            range = this.getDocument().createRange();
     1109            range.setStart(sel.anchorNode, startPos);
     1110            range.setEnd(sel.anchorNode, endPos);
     1111            range.deleteContents();
     1112
     1113            var el = this.getDocument().createElement('div');
     1114            el.innerHTML = html;
     1115            var frag = this.getDocument().createDocumentFragment(),
     1116                node = void 0,
     1117                lastNode = void 0;
     1118            while (node = el.firstChild) {
     1119                lastNode = frag.appendChild(node);
     1120            }
     1121            range.insertNode(frag);
     1122
     1123            // Preserve the selection
     1124            if (lastNode) {
     1125                range = range.cloneRange();
     1126                range.setStartAfter(lastNode);
     1127                range.collapse(true);
     1128                sel.removeAllRanges();
     1129                sel.addRange(range);
     1130            }
     1131        }
     1132    }, {
     1133        key: 'getWindowSelection',
     1134        value: function getWindowSelection() {
     1135            if (this.tribute.collection.iframe) {
     1136                return this.tribute.collection.iframe.contentWindow.getSelection();
     1137            }
     1138
     1139            return window.getSelection();
     1140        }
     1141    }, {
     1142        key: 'getNodePositionInParent',
     1143        value: function getNodePositionInParent(element) {
     1144            if (element.parentNode === null) {
     1145                return 0;
     1146            }
     1147
     1148            for (var i = 0; i < element.parentNode.childNodes.length; i++) {
     1149                var node = element.parentNode.childNodes[i];
     1150
     1151                if (node === element) {
     1152                    return i;
     1153                }
     1154            }
     1155        }
     1156    }, {
     1157        key: 'getContentEditableSelectedPath',
     1158        value: function getContentEditableSelectedPath(ctx) {
     1159            var sel = this.getWindowSelection();
     1160            var selected = sel.anchorNode;
     1161            var path = [];
     1162            var offset = void 0;
     1163
     1164            if (selected != null) {
     1165                var i = void 0;
     1166                var ce = selected.contentEditable;
     1167                while (selected !== null && ce !== 'true') {
     1168                    i = this.getNodePositionInParent(selected);
     1169                    path.push(i);
     1170                    selected = selected.parentNode;
     1171                    if (selected !== null) {
     1172                        ce = selected.contentEditable;
     1173                    }
     1174                }
     1175                path.reverse();
     1176
     1177                // getRangeAt may not exist, need alternative
     1178                offset = sel.getRangeAt(0).startOffset;
     1179
     1180                return {
     1181                    selected: selected,
     1182                    path: path,
     1183                    offset: offset
     1184                };
     1185            }
     1186        }
     1187    }, {
     1188        key: 'getTextPrecedingCurrentSelection',
     1189        value: function getTextPrecedingCurrentSelection() {
     1190            var context = this.tribute.current,
     1191                text = '';
     1192
     1193            if (!this.isContentEditable(context.element)) {
     1194                var textComponent = this.tribute.current.element;
     1195                if (textComponent) {
     1196                    var startPos = textComponent.selectionStart;
     1197                    if (textComponent.value && startPos >= 0) {
     1198                        text = textComponent.value.substring(0, startPos);
     1199                    }
     1200                }
     1201            } else {
     1202                var selectedElem = this.getWindowSelection().anchorNode;
     1203
     1204                if (selectedElem != null) {
     1205                    var workingNodeContent = selectedElem.textContent;
     1206                    var selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset;
     1207
     1208                    if (workingNodeContent && selectStartOffset >= 0) {
     1209                        text = workingNodeContent.substring(0, selectStartOffset);
     1210                    }
     1211                }
     1212            }
     1213
     1214            return text;
     1215        }
     1216    }, {
     1217        key: 'getTriggerInfo',
     1218        value: function getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces) {
     1219            var _this2 = this;
     1220
     1221            var ctx = this.tribute.current;
     1222            var selected = void 0,
     1223                path = void 0,
     1224                offset = void 0;
     1225
     1226            if (!this.isContentEditable(ctx.element)) {
     1227                selected = this.tribute.current.element;
     1228            } else {
     1229                var selectionInfo = this.getContentEditableSelectedPath(ctx);
     1230
     1231                if (selectionInfo) {
     1232                    selected = selectionInfo.selected;
     1233                    path = selectionInfo.path;
     1234                    offset = selectionInfo.offset;
     1235                }
     1236            }
     1237
     1238            var effectiveRange = this.getTextPrecedingCurrentSelection();
     1239
     1240            if (effectiveRange !== undefined && effectiveRange !== null) {
     1241                var mostRecentTriggerCharPos = -1;
     1242                var triggerChar = void 0;
     1243
     1244                this.tribute.collection.forEach(function (config) {
     1245                    var c = config.trigger;
     1246                    var idx = config.requireLeadingSpace ? _this2.lastIndexWithLeadingSpace(effectiveRange, c) : effectiveRange.lastIndexOf(c);
     1247
     1248                    if (idx > mostRecentTriggerCharPos) {
     1249                        mostRecentTriggerCharPos = idx;
     1250                        triggerChar = c;
     1251                        requireLeadingSpace = config.requireLeadingSpace;
     1252                    }
     1253                });
     1254
     1255                if (mostRecentTriggerCharPos >= 0 && (mostRecentTriggerCharPos === 0 || !requireLeadingSpace || /[\xA0\s]/g.test(effectiveRange.substring(mostRecentTriggerCharPos - 1, mostRecentTriggerCharPos)))) {
     1256                    var currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + 1, effectiveRange.length);
     1257
     1258                    triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + 1);
     1259                    var firstSnippetChar = currentTriggerSnippet.substring(0, 1);
     1260                    var leadingSpace = currentTriggerSnippet.length > 0 && (firstSnippetChar === ' ' || firstSnippetChar === '\xA0');
     1261                    if (hasTrailingSpace) {
     1262                        currentTriggerSnippet = currentTriggerSnippet.trim();
     1263                    }
     1264
     1265                    var regex = allowSpaces ? /[^\S ]/g : /[\xA0\s]/g;
     1266
     1267                    this.tribute.hasTrailingSpace = regex.test(currentTriggerSnippet);
     1268
     1269                    if (!leadingSpace && (menuAlreadyActive || !regex.test(currentTriggerSnippet))) {
     1270                        return {
     1271                            mentionPosition: mostRecentTriggerCharPos,
     1272                            mentionText: currentTriggerSnippet,
     1273                            mentionSelectedElement: selected,
     1274                            mentionSelectedPath: path,
     1275                            mentionSelectedOffset: offset,
     1276                            mentionTriggerChar: triggerChar
     1277                        };
     1278                    }
     1279                }
     1280            }
     1281        }
     1282    }, {
     1283        key: 'lastIndexWithLeadingSpace',
     1284        value: function lastIndexWithLeadingSpace(str, char) {
     1285            var reversedStr = str.split('').reverse().join('');
     1286            var index = -1;
     1287
     1288            for (var cidx = 0, len = str.length; cidx < len; cidx++) {
     1289                var firstChar = cidx === str.length - 1;
     1290                var leadingSpace = /\s/.test(reversedStr[cidx + 1]);
     1291                var match = char === reversedStr[cidx];
     1292
     1293                if (match && (firstChar || leadingSpace)) {
     1294                    index = str.length - 1 - cidx;
     1295                    break;
     1296                }
     1297            }
     1298
     1299            return index;
     1300        }
     1301    }, {
     1302        key: 'isContentEditable',
     1303        value: function isContentEditable(element) {
     1304            return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA';
     1305        }
     1306    }, {
     1307        key: 'isMenuOffScreen',
     1308        value: function isMenuOffScreen(coordinates, menuDimensions) {
     1309            var windowWidth = window.innerWidth;
     1310            var windowHeight = window.innerHeight;
     1311            var doc = document.documentElement;
     1312            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
     1313            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
     1314
     1315            var menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height;
     1316            var menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width;
     1317            var menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height;
     1318            var menuLeft = typeof coordinates.left === 'number' ? coordinates.left : windowLeft + windowWidth - coordinates.right - menuDimensions.width;
     1319
     1320            return {
     1321                top: menuTop < Math.floor(windowTop),
     1322                right: menuRight > Math.ceil(windowLeft + windowWidth),
     1323                bottom: menuBottom > Math.ceil(windowTop + windowHeight),
     1324                left: menuLeft < Math.floor(windowLeft)
     1325            };
     1326        }
     1327    }, {
     1328        key: 'getMenuDimensions',
     1329        value: function getMenuDimensions() {
     1330            // Width of the menu depends of its contents and position
     1331            // We must check what its width would be without any obstruction
     1332            // This way, we can achieve good positioning for flipping the menu
     1333            var dimensions = {
     1334                width: null,
     1335                height: null
     1336            };
     1337
     1338            this.tribute.menu.style.cssText = 'top: 0px;\n                                 left: 0px;\n                                 position: fixed;\n                                 zIndex: 10000;\n                                 display: block;\n                                 visibility; hidden;';
     1339            dimensions.width = this.tribute.menu.offsetWidth;
     1340            dimensions.height = this.tribute.menu.offsetHeight;
     1341
     1342            this.tribute.menu.style.cssText = 'display: none;';
     1343
     1344            return dimensions;
     1345        }
     1346    }, {
     1347        key: 'getTextAreaOrInputUnderlinePosition',
     1348        value: function getTextAreaOrInputUnderlinePosition(element, position, flipped) {
     1349            var properties = ['direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing'];
     1350
     1351            var isFirefox = window.mozInnerScreenX !== null;
     1352
     1353            var div = this.getDocument().createElement('div');
     1354            div.id = 'input-textarea-caret-position-mirror-div';
     1355            this.getDocument().body.appendChild(div);
     1356
     1357            var style = div.style;
     1358            var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle;
     1359
     1360            style.whiteSpace = 'pre-wrap';
     1361            if (element.nodeName !== 'INPUT') {
     1362                style.wordWrap = 'break-word';
     1363            }
     1364
     1365            // position off-screen
     1366            style.position = 'absolute';
     1367            style.visibility = 'hidden';
     1368
     1369            // transfer the element's properties to the div
     1370            properties.forEach(function (prop) {
     1371                style[prop] = computed[prop];
     1372            });
     1373
     1374            if (isFirefox) {
     1375                style.width = parseInt(computed.width) - 2 + 'px';
     1376                if (element.scrollHeight > parseInt(computed.height)) style.overflowY = 'scroll';
     1377            } else {
     1378                style.overflow = 'hidden';
     1379            }
     1380
     1381            div.textContent = element.value.substring(0, position);
     1382
     1383            if (element.nodeName === 'INPUT') {
     1384                div.textContent = div.textContent.replace(/\s/g, ' ');
     1385            }
     1386
     1387            var span = this.getDocument().createElement('span');
     1388            span.textContent = element.value.substring(position) || '.';
     1389            div.appendChild(span);
     1390
     1391            var rect = element.getBoundingClientRect();
     1392            var doc = document.documentElement;
     1393            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
     1394            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
     1395
     1396            var coordinates = {
     1397                top: rect.top + windowTop + span.offsetTop + parseInt(computed.borderTopWidth) + parseInt(computed.fontSize) - element.scrollTop,
     1398                left: rect.left + windowLeft + span.offsetLeft + parseInt(computed.borderLeftWidth)
     1399            };
     1400
     1401            var windowWidth = window.innerWidth;
     1402            var windowHeight = window.innerHeight;
     1403
     1404            var menuDimensions = this.getMenuDimensions();
     1405            var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
     1406
     1407            if (menuIsOffScreen.right) {
     1408                coordinates.right = windowWidth - coordinates.left;
     1409                coordinates.left = 'auto';
     1410            }
     1411
     1412            var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
     1413
     1414            if (menuIsOffScreen.bottom) {
     1415                var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
     1416                var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
     1417
     1418                coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top - span.offsetTop);
     1419                coordinates.top = 'auto';
     1420            }
     1421
     1422            menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
     1423            if (menuIsOffScreen.left) {
     1424                coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
     1425                delete coordinates.right;
     1426            }
     1427            if (menuIsOffScreen.top) {
     1428                coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
     1429                delete coordinates.bottom;
     1430            }
     1431
     1432            this.getDocument().body.removeChild(div);
     1433            return coordinates;
     1434        }
     1435    }, {
     1436        key: 'getContentEditableCaretPosition',
     1437        value: function getContentEditableCaretPosition(selectedNodePosition) {
     1438            var markerTextChar = '';
     1439            var markerEl = void 0,
     1440                markerId = 'sel_' + new Date().getTime() + '_' + Math.random().toString().substr(2);
     1441            var range = void 0;
     1442            var sel = this.getWindowSelection();
     1443            var prevRange = sel.getRangeAt(0);
     1444
     1445            range = this.getDocument().createRange();
     1446            range.setStart(sel.anchorNode, selectedNodePosition);
     1447            range.setEnd(sel.anchorNode, selectedNodePosition);
     1448
     1449            range.collapse(false);
     1450
     1451            // Create the marker element containing a single invisible character using DOM methods and insert it
     1452            markerEl = this.getDocument().createElement('span');
     1453            markerEl.id = markerId;
     1454
     1455            markerEl.appendChild(this.getDocument().createTextNode(markerTextChar));
     1456            range.insertNode(markerEl);
     1457            sel.removeAllRanges();
     1458            sel.addRange(prevRange);
     1459
     1460            var rect = markerEl.getBoundingClientRect();
     1461            var doc = document.documentElement;
     1462            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
     1463            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
     1464            var coordinates = {
     1465                left: rect.left + windowLeft,
     1466                top: rect.top + markerEl.offsetHeight + windowTop
     1467            };
     1468            var windowWidth = window.innerWidth;
     1469            var windowHeight = window.innerHeight;
     1470
     1471            var menuDimensions = this.getMenuDimensions();
     1472            var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
     1473
     1474            if (menuIsOffScreen.right) {
     1475                coordinates.left = 'auto';
     1476                coordinates.right = windowWidth - rect.left - windowLeft;
     1477            }
     1478
     1479            var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
     1480
     1481            if (menuIsOffScreen.bottom) {
     1482                var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
     1483                var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
     1484
     1485                coordinates.top = 'auto';
     1486                coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top);
     1487            }
     1488
     1489            menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
     1490            if (menuIsOffScreen.left) {
     1491                coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
     1492                delete coordinates.right;
     1493            }
     1494            if (menuIsOffScreen.top) {
     1495                coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
     1496                delete coordinates.bottom;
     1497            }
     1498
     1499            markerEl.parentNode.removeChild(markerEl);
     1500            return coordinates;
     1501        }
     1502    }, {
     1503        key: 'scrollIntoView',
     1504        value: function scrollIntoView(elem) {
     1505            var reasonableBuffer = 20,
     1506                clientRect = void 0;
     1507            var maxScrollDisplacement = 100;
     1508            var e = this.menu;
     1509
     1510            if (typeof e === 'undefined') return;
     1511
     1512            while (clientRect === undefined || clientRect.height === 0) {
     1513                clientRect = e.getBoundingClientRect();
     1514
     1515                if (clientRect.height === 0) {
     1516                    e = e.childNodes[0];
     1517                    if (e === undefined || !e.getBoundingClientRect) {
     1518                        return;
     1519                    }
     1520                }
     1521            }
     1522
     1523            var elemTop = clientRect.top;
     1524            var elemBottom = elemTop + clientRect.height;
     1525
     1526            if (elemTop < 0) {
     1527                window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer);
     1528            } else if (elemBottom > window.innerHeight) {
     1529                var maxY = window.pageYOffset + clientRect.top - reasonableBuffer;
     1530
     1531                if (maxY - window.pageYOffset > maxScrollDisplacement) {
     1532                    maxY = window.pageYOffset + maxScrollDisplacement;
     1533                }
     1534
     1535                var targetY = window.pageYOffset - (window.innerHeight - elemBottom);
     1536
     1537                if (targetY > maxY) {
     1538                    targetY = maxY;
     1539                }
     1540
     1541                window.scrollTo(0, targetY);
     1542            }
     1543        }
     1544    }]);
     1545
     1546    return TributeRange;
     1547}();
     1548
     1549exports.default = TributeRange;
     1550module.exports = exports['default'];
     1551
     1552},{}],5:[function(require,module,exports){
     1553'use strict';
     1554
     1555Object.defineProperty(exports, "__esModule", {
     1556    value: true
     1557});
     1558
     1559var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
     1560
     1561function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
     1562
     1563// Thanks to https://github.com/mattyork/fuzzy
     1564var TributeSearch = function () {
     1565    function TributeSearch(tribute) {
     1566        _classCallCheck(this, TributeSearch);
     1567
     1568        this.tribute = tribute;
     1569        this.tribute.search = this;
     1570    }
     1571
     1572    _createClass(TributeSearch, [{
     1573        key: 'simpleFilter',
     1574        value: function simpleFilter(pattern, array) {
     1575            var _this = this;
     1576
     1577            return array.filter(function (string) {
     1578                return _this.test(pattern, string);
     1579            });
     1580        }
     1581    }, {
     1582        key: 'test',
     1583        value: function test(pattern, string) {
     1584            return this.match(pattern, string) !== null;
     1585        }
     1586    }, {
     1587        key: 'match',
     1588        value: function match(pattern, string, opts) {
     1589            opts = opts || {};
     1590            var patternIdx = 0,
     1591                result = [],
     1592                len = string.length,
     1593                totalScore = 0,
     1594                currScore = 0,
     1595                pre = opts.pre || '',
     1596                post = opts.post || '',
     1597                compareString = opts.caseSensitive && string || string.toLowerCase(),
     1598                ch = void 0,
     1599                compareChar = void 0;
     1600
     1601            pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
     1602
     1603            var patternCache = this.traverse(compareString, pattern, 0, 0, []);
     1604            if (!patternCache) {
     1605                return null;
     1606            }
     1607
     1608            return {
     1609                rendered: this.render(string, patternCache.cache, pre, post),
     1610                score: patternCache.score
     1611            };
     1612        }
     1613    }, {
     1614        key: 'traverse',
     1615        value: function traverse(string, pattern, stringIndex, patternIndex, patternCache) {
     1616            // if the pattern search at end
     1617            if (pattern.length === patternIndex) {
     1618
     1619                // calculate score and copy the cache containing the indices where it's found
     1620                return {
     1621                    score: this.calculateScore(patternCache),
     1622                    cache: patternCache.slice()
     1623                };
     1624            }
     1625
     1626            // if string at end or remaining pattern > remaining string
     1627            if (string.length === stringIndex || pattern.length - patternIndex > string.length - stringIndex) {
     1628                return undefined;
     1629            }
     1630
     1631            var c = pattern[patternIndex];
     1632            var index = string.indexOf(c, stringIndex);
     1633            var best = void 0,
     1634                temp = void 0;
     1635
     1636            while (index > -1) {
     1637                patternCache.push(index);
     1638                temp = this.traverse(string, pattern, index + 1, patternIndex + 1, patternCache);
     1639                patternCache.pop();
     1640
     1641                // if downstream traversal failed, return best answer so far
     1642                if (!temp) {
     1643                    return best;
     1644                }
     1645
     1646                if (!best || best.score < temp.score) {
     1647                    best = temp;
     1648                }
     1649
     1650                index = string.indexOf(c, index + 1);
     1651            }
     1652
     1653            return best;
     1654        }
     1655    }, {
     1656        key: 'calculateScore',
     1657        value: function calculateScore(patternCache) {
     1658            var score = 0;
     1659            var temp = 1;
     1660
     1661            patternCache.forEach(function (index, i) {
     1662                if (i > 0) {
     1663                    if (patternCache[i - 1] + 1 === index) {
     1664                        temp += temp + 1;
     1665                    } else {
     1666                        temp = 1;
     1667                    }
     1668                }
     1669
     1670                score += temp;
     1671            });
     1672
     1673            return score;
     1674        }
     1675    }, {
     1676        key: 'render',
     1677        value: function render(string, indices, pre, post) {
     1678            var rendered = string.substring(0, indices[0]);
     1679
     1680            indices.forEach(function (index, i) {
     1681                rendered += pre + string[index] + post + string.substring(index + 1, indices[i + 1] ? indices[i + 1] : string.length);
     1682            });
     1683
     1684            return rendered;
     1685        }
     1686    }, {
     1687        key: 'filter',
     1688        value: function filter(pattern, arr, opts) {
     1689            var _this2 = this;
     1690
     1691            opts = opts || {};
     1692            return arr.reduce(function (prev, element, idx, arr) {
     1693                var str = element;
     1694
     1695                if (opts.extract) {
     1696                    str = opts.extract(element);
     1697
     1698                    if (!str) {
     1699                        // take care of undefineds / nulls / etc.
     1700                        str = '';
     1701                    }
     1702                }
     1703
     1704                var rendered = _this2.match(pattern, str, opts);
     1705
     1706                if (rendered != null) {
     1707                    prev[prev.length] = {
     1708                        string: rendered.rendered,
     1709                        score: rendered.score,
     1710                        index: idx,
     1711                        original: element
     1712                    };
     1713                }
     1714
     1715                return prev;
     1716            }, []).sort(function (a, b) {
     1717                var compare = b.score - a.score;
     1718                if (compare) return compare;
     1719                return a.index - b.index;
     1720            });
     1721        }
     1722    }]);
     1723
     1724    return TributeSearch;
     1725}();
     1726
     1727exports.default = TributeSearch;
     1728module.exports = exports['default'];
     1729
     1730},{}],6:[function(require,module,exports){
     1731"use strict";
     1732
     1733Object.defineProperty(exports, "__esModule", {
     1734  value: true
     1735});
     1736
     1737var _Tribute = require("./Tribute");
     1738
     1739var _Tribute2 = _interopRequireDefault(_Tribute);
     1740
     1741function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     1742
     1743exports.default = _Tribute2.default; /**
     1744                                     * Tribute.js
     1745                                     * Native ES6 JavaScript @mention Plugin
     1746                                     **/
     1747
     1748module.exports = exports["default"];
     1749
     1750},{"./Tribute":1}],7:[function(require,module,exports){
     1751'use strict';
     1752
     1753if (!Array.prototype.find) {
     1754    Array.prototype.find = function (predicate) {
     1755        if (this === null) {
     1756            throw new TypeError('Array.prototype.find called on null or undefined');
     1757        }
     1758        if (typeof predicate !== 'function') {
     1759            throw new TypeError('predicate must be a function');
     1760        }
     1761        var list = Object(this);
     1762        var length = list.length >>> 0;
     1763        var thisArg = arguments[1];
     1764        var value;
     1765
     1766        for (var i = 0; i < length; i++) {
     1767            value = list[i];
     1768            if (predicate.call(thisArg, value, i, list)) {
     1769                return value;
     1770            }
     1771        }
     1772        return undefined;
     1773    };
     1774}
     1775
     1776if (window && typeof window.CustomEvent !== "function") {
     1777    var CustomEvent = function CustomEvent(event, params) {
     1778        params = params || {
     1779            bubbles: false,
     1780            cancelable: false,
     1781            detail: undefined
     1782        };
     1783        var evt = document.createEvent('CustomEvent');
     1784        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
     1785        return evt;
     1786    };
     1787
     1788    if (typeof window.Event !== 'undefined') {
     1789        CustomEvent.prototype = window.Event.prototype;
     1790    }
     1791
     1792    window.CustomEvent = CustomEvent;
     1793}
     1794
     1795},{}]},{},[6])(6)
     1796});
     1797
     1798//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvVHJpYnV0ZS5qcyIsInNyYy9UcmlidXRlRXZlbnRzLmpzIiwic3JjL1RyaWJ1dGVNZW51RXZlbnRzLmpzIiwic3JjL1RyaWJ1dGVSYW5nZS5qcyIsInNyYy9UcmlidXRlU2VhcmNoLmpzIiwic3JjL2luZGV4LmpzIiwic3JjL3V0aWxzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7QUNBQTs7OztBQUNBOzs7O0FBQ0E7Ozs7QUFDQTs7OztBQUNBOzs7Ozs7OztJQUVNLE87QUFDRiwyQkFpQkc7QUFBQTs7QUFBQSwrQkFoQkMsTUFnQkQ7QUFBQSxZQWhCQyxNQWdCRCwrQkFoQlUsSUFnQlY7QUFBQSwrQkFmQyxNQWVEO0FBQUEsWUFmQyxNQWVELCtCQWZVLElBZVY7QUFBQSxvQ0FkQyxXQWNEO0FBQUEsWUFkQyxXQWNELG9DQWRlLFdBY2Y7QUFBQSxnQ0FiQyxPQWFEO0FBQUEsWUFiQyxPQWFELGdDQWJXLEdBYVg7QUFBQSx1Q0FaQyxjQVlEO0FBQUEsWUFaQyxjQVlELHVDQVprQixJQVlsQjtBQUFBLHlDQVhDLGdCQVdEO0FBQUEsWUFYQyxnQkFXRCx5Q0FYb0IsSUFXcEI7QUFBQSwrQkFWQyxNQVVEO0FBQUEsWUFWQyxNQVVELCtCQVZVLEtBVVY7QUFBQSxpQ0FUQyxRQVNEO0FBQUEsWUFUQyxRQVNELGlDQVRZLE9BU1o7QUFBQSxtQ0FSQyxVQVFEO0FBQUEsWUFSQyxVQVFELG1DQVJjLElBUWQ7QUFBQSxzQ0FQQyxhQU9EO0FBQUEsWUFQQyxhQU9ELHNDQVBpQixJQU9qQjtBQUFBLHdDQU5DLGVBTUQ7QUFBQSxZQU5DLGVBTUQsd0NBTm1CLElBTW5CO0FBQUEseUNBTEMsbUJBS0Q7QUFBQSxZQUxDLG1CQUtELHlDQUx1QixJQUt2QjtBQUFBLG9DQUpDLFdBSUQ7QUFBQSxZQUpDLFdBSUQsb0NBSmUsS0FJZjtBQUFBLHlDQUhDLGlCQUdEO0FBQUEsWUFIQyxpQkFHRCx5Q0FIcUIsSUFHckI7QUFBQSxxQ0FGQyxZQUVEO0FBQUEsWUFGQyxZQUVELHFDQUZnQixJQUVoQjtBQUFBLHlDQURDLGlCQUNEO0FBQUEsWUFEQyxpQkFDRCx5Q0FEcUIsS0FDckI7O0FBQUE7O0FBRUMsYUFBSyxZQUFMLEdBQW9CLENBQXBCO0FBQ0EsYUFBSyxPQUFMLEdBQWUsRUFBZjtBQUNBLGFBQUssVUFBTCxHQUFrQixLQUFsQjtBQUNBLGFBQUssUUFBTCxHQUFnQixLQUFoQjtBQUNBLGFBQUssYUFBTCxHQUFxQixhQUFyQjtBQUNBLGFBQUssV0FBTCxHQUFtQixXQUFuQjtBQUNBLGFBQUssaUJBQUwsR0FBeUIsaUJBQXpCO0FBQ0EsYUFBSyxZQUFMLEdBQW9CLFlBQXBCO0FBQ0EsYUFBSyxnQkFBTCxHQUF3QixLQUF4QjtBQUNBLGFBQUssaUJBQUwsR0FBeUIsaUJBQXpCOztBQUVBLFlBQUksTUFBSixFQUFZO0FBQ1IsaUJBQUssVUFBTCxHQUFrQixDQUFDO0FBQ2Y7QUFDQSx5QkFBUyxPQUZNOztBQUlmO0FBQ0Esd0JBQVEsTUFMTzs7QUFPZjtBQUNBLDZCQUFhLFdBUkU7O0FBVWY7QUFDQSxnQ0FBZ0IsQ0FBQyxrQkFBa0IsUUFBUSxxQkFBM0IsRUFBa0QsSUFBbEQsQ0FBdUQsSUFBdkQsQ0FYRDs7QUFhZjtBQUNBLGtDQUFrQixDQUFDLG9CQUFvQixRQUFRLHVCQUE3QixFQUFzRCxJQUF0RCxDQUEyRCxJQUEzRCxDQWRIOztBQWdCZjtBQUNBLGlDQUFrQixhQUFLO0FBQ25CLHdCQUFJLE9BQU8sQ0FBUCxLQUFhLFVBQWpCLEVBQTZCO0FBQ3pCLCtCQUFPLEVBQUUsSUFBRixDQUFPLEtBQVAsQ0FBUDtBQUNIOztBQUVELDJCQUFPLGVBQVA7QUFDSCxpQkFOZ0IsQ0FNZCxlQU5jLENBakJGOztBQXlCZjtBQUNBLHdCQUFRLE1BMUJPOztBQTRCZjtBQUNBLDBCQUFVLFFBN0JLOztBQStCZjtBQUNBLHdCQUFRLE1BaENPOztBQWtDZixxQ0FBcUI7QUFsQ04sYUFBRCxDQUFsQjtBQW9DSCxTQXJDRCxNQXNDSyxJQUFJLFVBQUosRUFBZ0I7QUFDakIsaUJBQUssVUFBTCxHQUFrQixXQUFXLEdBQVgsQ0FBZSxnQkFBUTtBQUNyQyx1QkFBTztBQUNILDZCQUFTLEtBQUssT0FBTCxJQUFnQixPQUR0QjtBQUVILDRCQUFRLEtBQUssTUFBTCxJQUFlLE1BRnBCO0FBR0gsaUNBQWEsS0FBSyxXQUFMLElBQW9CLFdBSDlCO0FBSUgsb0NBQWdCLENBQUMsS0FBSyxjQUFMLElBQXVCLFFBQVEscUJBQWhDLEVBQXVELElBQXZELENBQTRELEtBQTVELENBSmI7QUFLSCxzQ0FBa0IsQ0FBQyxLQUFLLGdCQUFMLElBQXlCLFFBQVEsdUJBQWxDLEVBQTJELElBQTNELENBQWdFLEtBQWhFLENBTGY7QUFNSDtBQUNBLHFDQUFrQixhQUFLO0FBQ25CLDRCQUFJLE9BQU8sQ0FBUCxLQUFhLFVBQWpCLEVBQTZCO0FBQ3pCLG1DQUFPLEVBQUUsSUFBRixDQUFPLEtBQVAsQ0FBUDtBQUNIOztBQUVELCtCQUFPLElBQVA7QUFDSCxxQkFOZ0IsQ0FNZCxlQU5jLENBUGQ7QUFjSCw0QkFBUSxLQUFLLE1BQUwsSUFBZSxNQWRwQjtBQWVILDhCQUFVLEtBQUssUUFBTCxJQUFpQixRQWZ4QjtBQWdCSCw0QkFBUSxLQUFLLE1BaEJWO0FBaUJILHlDQUFxQixLQUFLO0FBakJ2QixpQkFBUDtBQW1CSCxhQXBCaUIsQ0FBbEI7QUFxQkgsU0F0QkksTUF1QkE7QUFDRCxrQkFBTSxJQUFJLEtBQUosQ0FBVSxvQ0FBVixDQUFOO0FBQ0g7O0FBRUQsWUFBSSxzQkFBSixDQUFpQixJQUFqQjtBQUNBLFlBQUksdUJBQUosQ0FBa0IsSUFBbEI7QUFDQSxZQUFJLDJCQUFKLENBQXNCLElBQXRCO0FBQ0EsWUFBSSx1QkFBSixDQUFrQixJQUFsQjtBQUNIOzs7O21DQW1CVTtBQUNQLG1CQUFPLEtBQUssVUFBTCxDQUFnQixHQUFoQixDQUFvQixrQkFBVTtBQUNqQyx1QkFBTyxPQUFPLE9BQWQ7QUFDSCxhQUZNLENBQVA7QUFHSDs7OytCQUVNLEUsRUFBSTtBQUNQLGdCQUFJLENBQUMsRUFBTCxFQUFTO0FBQ0wsc0JBQU0sSUFBSSxLQUFKLENBQVUsZ0RBQVYsQ0FBTjtBQUNIOztBQUVEO0FBQ0EsZ0JBQUksT0FBTyxNQUFQLEtBQWtCLFdBQWxCLElBQWlDLGNBQWMsTUFBbkQsRUFBMkQ7QUFDdkQscUJBQUssR0FBRyxHQUFILEVBQUw7QUFDSDs7QUFFRDtBQUNBLGdCQUFJLEdBQUcsV0FBSCxLQUFtQixRQUFuQixJQUErQixHQUFHLFdBQUgsS0FBbUIsY0FBbEQsSUFBb0UsR0FBRyxXQUFILEtBQW1CLEtBQTNGLEVBQWtHO0FBQzlGLG9CQUFJLFNBQVMsR0FBRyxNQUFoQjtBQUNBLHFCQUFLLElBQUksSUFBSSxDQUFiLEVBQWdCLElBQUksTUFBcEIsRUFBNEIsRUFBRSxDQUE5QixFQUFpQztBQUM3Qix5QkFBSyxPQUFMLENBQWEsR0FBRyxDQUFILENBQWI7QUFDSDtBQUNKLGFBTEQsTUFLTztBQUNILHFCQUFLLE9BQUwsQ0FBYSxFQUFiO0FBQ0g7QUFDSjs7O2dDQUVPLEUsRUFBSTtBQUNSLGdCQUFJLEdBQUcsWUFBSCxDQUFnQixjQUFoQixDQUFKLEVBQXFDO0FBQ2pDLHdCQUFRLElBQVIsQ0FBYSxrQ0FBa0MsR0FBRyxRQUFsRDtBQUNIOztBQUVELGlCQUFLLGNBQUwsQ0FBb0IsRUFBcEI7QUFDQSxpQkFBSyxNQUFMLENBQVksSUFBWixDQUFpQixFQUFqQjtBQUNBLGVBQUcsWUFBSCxDQUFnQixjQUFoQixFQUFnQyxJQUFoQztBQUNIOzs7dUNBRWMsTyxFQUFTO0FBQ3BCLGdCQUFJLFFBQVEsVUFBUixHQUFxQixPQUFyQixDQUE2QixRQUFRLFFBQXJDLE1BQW1ELENBQUMsQ0FBeEQsRUFBMkQ7QUFDdkQsb0JBQUksUUFBUSxlQUFaLEVBQTZCO0FBQ3pCLDRCQUFRLGVBQVIsR0FBMEIsSUFBMUI7QUFDSCxpQkFGRCxNQUVPO0FBQ0gsMEJBQU0sSUFBSSxLQUFKLENBQVUsOEJBQThCLFFBQVEsUUFBaEQsQ0FBTjtBQUNIO0FBQ0o7QUFDSjs7O3FDQUVZO0FBQ1QsZ0JBQUksVUFBVSxLQUFLLEtBQUwsQ0FBVyxXQUFYLEdBQXlCLGFBQXpCLENBQXVDLEtBQXZDLENBQWQ7QUFBQSxnQkFDSSxLQUFLLEtBQUssS0FBTCxDQUFXLFdBQVgsR0FBeUIsYUFBekIsQ0FBdUMsSUFBdkMsQ0FEVDs7QUFHQSxvQkFBUSxTQUFSLEdBQW9CLG1CQUFwQjtBQUNBLG9CQUFRLFdBQVIsQ0FBb0IsRUFBcEI7O0FBRUEsZ0JBQUksS0FBSyxhQUFULEVBQXdCO0FBQ3BCLHVCQUFPLEtBQUssYUFBTCxDQUFtQixXQUFuQixDQUErQixPQUEvQixDQUFQO0FBQ0g7O0FBRUQsbUJBQU8sS0FBSyxLQUFMLENBQVcsV0FBWCxHQUF5QixJQUF6QixDQUE4QixXQUE5QixDQUEwQyxPQUExQyxDQUFQO0FBQ0g7OztvQ0FFVyxPLEVBQVMsUSxFQUFVO0FBQUE7O0FBQzNCO0FBQ0EsZ0JBQUksS0FBSyxRQUFMLElBQWlCLEtBQUssT0FBTCxDQUFhLE9BQWIsS0FBeUIsT0FBMUMsSUFBcUQsS0FBSyxPQUFMLENBQWEsV0FBYixLQUE2QixLQUFLLDBCQUEzRixFQUF1SDtBQUNySDtBQUNEO0FBQ0QsaUJBQUssMEJBQUwsR0FBa0MsS0FBSyxPQUFMLENBQWEsV0FBL0M7O0FBRUE7QUFDQSxnQkFBSSxDQUFDLEtBQUssSUFBVixFQUFnQjtBQUNaLHFCQUFLLElBQUwsR0FBWSxLQUFLLFVBQUwsRUFBWjtBQUNBLHdCQUFRLFdBQVIsR0FBc0IsS0FBSyxJQUEzQjtBQUNBLHFCQUFLLFVBQUwsQ0FBZ0IsSUFBaEIsQ0FBcUIsS0FBSyxJQUExQjtBQUNIOztBQUVELGlCQUFLLFFBQUwsR0FBZ0IsSUFBaEI7QUFDQSxpQkFBSyxZQUFMLEdBQW9CLENBQXBCOztBQUVBLGdCQUFJLENBQUMsS0FBSyxPQUFMLENBQWEsV0FBbEIsRUFBK0I7QUFDM0IscUJBQUssT0FBTCxDQUFhLFdBQWIsR0FBMkIsRUFBM0I7QUFDSDs7QUFFRCxnQkFBTSxnQkFBZ0IsU0FBaEIsYUFBZ0IsQ0FBQyxNQUFELEVBQVk7QUFDOUI7QUFDQSxvQkFBSSxDQUFDLE9BQUssUUFBVixFQUFvQjtBQUNoQjtBQUNIOztBQUVELG9CQUFJLFFBQVEsT0FBSyxNQUFMLENBQVksTUFBWixDQUFtQixPQUFLLE9BQUwsQ0FBYSxXQUFoQyxFQUE2QyxNQUE3QyxFQUFxRDtBQUM3RCx5QkFBSyxRQUR3RDtBQUU3RCwwQkFBTSxTQUZ1RDtBQUc3RCw2QkFBUyxpQkFBQyxFQUFELEVBQVE7QUFDYiw0QkFBSSxPQUFPLE9BQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsTUFBL0IsS0FBMEMsUUFBOUMsRUFBd0Q7QUFDcEQsbUNBQU8sR0FBRyxPQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLE1BQTNCLENBQVA7QUFDSCx5QkFGRCxNQUVPLElBQUksT0FBTyxPQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLE1BQS9CLEtBQTBDLFVBQTlDLEVBQTBEO0FBQzdELG1DQUFPLE9BQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsTUFBeEIsQ0FBK0IsRUFBL0IsRUFBbUMsT0FBSyxPQUFMLENBQWEsV0FBaEQsQ0FBUDtBQUNILHlCQUZNLE1BRUE7QUFDSCxrQ0FBTSxJQUFJLEtBQUosQ0FBVSw4REFBVixDQUFOO0FBQ0g7QUFDSjtBQVg0RCxpQkFBckQsQ0FBWjs7QUFjQSx1QkFBSyxPQUFMLENBQWEsYUFBYixHQUE2QixLQUE3Qjs7QUFHQSxvQkFBSSxLQUFLLE9BQUssSUFBTCxDQUFVLGFBQVYsQ0FBd0IsSUFBeEIsQ0FBVDs7QUFFQSx1QkFBSyxLQUFMLENBQVcsbUJBQVgsQ0FBK0IsUUFBL0I7O0FBRUEsb0JBQUksQ0FBQyxNQUFNLE1BQVgsRUFBbUI7QUFDZix3QkFBSSxlQUFlLElBQUksV0FBSixDQUFnQixrQkFBaEIsRUFBb0MsRUFBRSxRQUFRLE9BQUssSUFBZixFQUFwQyxDQUFuQjtBQUNBLDJCQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLGFBQXJCLENBQW1DLFlBQW5DO0FBQ0Esd0JBQUksQ0FBQyxPQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLGVBQTdCLEVBQThDO0FBQzFDLCtCQUFLLFFBQUw7QUFDSCxxQkFGRCxNQUVPO0FBQ0gsMkJBQUcsU0FBSCxHQUFlLE9BQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsZUFBeEIsRUFBZjtBQUNIOztBQUVEO0FBQ0g7O0FBRUQsbUJBQUcsU0FBSCxHQUFlLEVBQWY7O0FBRUEsc0JBQU0sT0FBTixDQUFjLFVBQUMsSUFBRCxFQUFPLEtBQVAsRUFBaUI7QUFDM0Isd0JBQUksS0FBSyxPQUFLLEtBQUwsQ0FBVyxXQUFYLEdBQXlCLGFBQXpCLENBQXVDLElBQXZDLENBQVQ7QUFDQSx1QkFBRyxZQUFILENBQWdCLFlBQWhCLEVBQThCLEtBQTlCO0FBQ0EsdUJBQUcsZ0JBQUgsQ0FBb0IsWUFBcEIsRUFBa0MsVUFBQyxDQUFELEVBQU87QUFDdkMsNEJBQUksS0FBSyxFQUFFLE1BQVg7QUFDQSw0QkFBSSxRQUFRLEdBQUcsWUFBSCxDQUFnQixZQUFoQixDQUFaO0FBQ0EsK0JBQUssTUFBTCxDQUFZLFdBQVosQ0FBd0IsS0FBeEI7QUFDRCxxQkFKRDtBQUtBLHdCQUFJLE9BQUssWUFBTCxLQUFzQixLQUExQixFQUFpQztBQUM3QiwyQkFBRyxTQUFILEdBQWUsT0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixXQUF2QztBQUNIO0FBQ0QsdUJBQUcsU0FBSCxHQUFlLE9BQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsZ0JBQXhCLENBQXlDLElBQXpDLENBQWY7QUFDQSx1QkFBRyxXQUFILENBQWUsRUFBZjtBQUNILGlCQWJEO0FBY0gsYUF2REQ7O0FBeURBLGdCQUFJLE9BQU8sS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixNQUEvQixLQUEwQyxVQUE5QyxFQUEwRDtBQUN0RCxxQkFBSyxPQUFMLENBQWEsVUFBYixDQUF3QixNQUF4QixDQUErQixLQUFLLE9BQUwsQ0FBYSxXQUE1QyxFQUF5RCxhQUF6RDtBQUNILGFBRkQsTUFFTztBQUNILDhCQUFjLEtBQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsTUFBdEM7QUFDSDtBQUNKOzs7OENBRXFCLE8sRUFBUyxlLEVBQWlCO0FBQzVDLGdCQUFJLFlBQVksU0FBUyxhQUF6QixFQUF3QztBQUNwQyxxQkFBSyxlQUFMLENBQXFCLE9BQXJCO0FBQ0g7O0FBRUQsaUJBQUssT0FBTCxDQUFhLFVBQWIsR0FBMEIsS0FBSyxVQUFMLENBQWdCLG1CQUFtQixDQUFuQyxDQUExQjtBQUNBLGlCQUFLLE9BQUwsQ0FBYSxlQUFiLEdBQStCLElBQS9CO0FBQ0EsaUJBQUssT0FBTCxDQUFhLE9BQWIsR0FBdUIsT0FBdkI7O0FBRUEsZ0JBQUksUUFBUSxpQkFBWixFQUNJLEtBQUssa0JBQUwsQ0FBd0IsS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixPQUFoRCxFQURKLEtBR0ksS0FBSyxhQUFMLENBQW1CLE9BQW5CLEVBQTRCLEtBQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsT0FBcEQ7O0FBRUosaUJBQUssV0FBTCxDQUFpQixPQUFqQjtBQUNIOztBQUVEOzs7O3dDQUNnQixFLEVBQUk7QUFDaEIsZUFBRyxLQUFIO0FBQ0EsZ0JBQUksT0FBTyxPQUFPLFlBQWQsSUFBOEIsV0FBOUIsSUFDTyxPQUFPLFNBQVMsV0FBaEIsSUFBK0IsV0FEMUMsRUFDdUQ7QUFDbkQsb0JBQUksUUFBUSxTQUFTLFdBQVQsRUFBWjtBQUNBLHNCQUFNLGtCQUFOLENBQXlCLEVBQXpCO0FBQ0Esc0JBQU0sUUFBTixDQUFlLEtBQWY7QUFDQSxvQkFBSSxNQUFNLE9BQU8sWUFBUCxFQUFWO0FBQ0Esb0JBQUksZUFBSjtBQUNBLG9CQUFJLFFBQUosQ0FBYSxLQUFiO0FBQ0gsYUFSRCxNQVFPLElBQUksT0FBTyxTQUFTLElBQVQsQ0FBYyxlQUFyQixJQUF3QyxXQUE1QyxFQUF5RDtBQUM1RCxvQkFBSSxZQUFZLFNBQVMsSUFBVCxDQUFjLGVBQWQsRUFBaEI7QUFDQSwwQkFBVSxpQkFBVixDQUE0QixFQUE1QjtBQUNBLDBCQUFVLFFBQVYsQ0FBbUIsS0FBbkI7QUFDQSwwQkFBVSxNQUFWO0FBQ0g7QUFDSjs7QUFFRDs7OzsyQ0FDbUIsSSxFQUFNO0FBQ3JCLGdCQUFJLEdBQUosRUFBUyxLQUFULEVBQWdCLElBQWhCO0FBQ0Esa0JBQU0sT0FBTyxZQUFQLEVBQU47QUFDQSxvQkFBUSxJQUFJLFVBQUosQ0FBZSxDQUFmLENBQVI7QUFDQSxrQkFBTSxjQUFOO0FBQ0EsZ0JBQUksV0FBVyxTQUFTLGNBQVQsQ0FBd0IsSUFBeEIsQ0FBZjtBQUNBLGtCQUFNLFVBQU4sQ0FBaUIsUUFBakI7QUFDQSxrQkFBTSxrQkFBTixDQUF5QixRQUF6QjtBQUNBLGtCQUFNLFFBQU4sQ0FBZSxLQUFmO0FBQ0EsZ0JBQUksZUFBSjtBQUNBLGdCQUFJLFFBQUosQ0FBYSxLQUFiO0FBQ0g7O0FBRUQ7Ozs7c0NBQ2MsUSxFQUFVLEksRUFBTTtBQUMxQixnQkFBSSxZQUFZLFNBQVMsU0FBekI7QUFDQSxnQkFBSSxXQUFXLFNBQVMsY0FBeEI7O0FBRUEsZ0JBQUksUUFBUyxTQUFTLEtBQVYsQ0FBaUIsU0FBakIsQ0FBMkIsQ0FBM0IsRUFBOEIsUUFBOUIsQ0FBWjtBQUNBLGdCQUFJLE9BQVEsU0FBUyxLQUFWLENBQWlCLFNBQWpCLENBQTJCLFNBQVMsWUFBcEMsRUFBa0QsU0FBUyxLQUFULENBQWUsTUFBakUsQ0FBWDtBQUNBLHFCQUFTLEtBQVQsR0FBaUIsUUFBUSxJQUFSLEdBQWUsSUFBaEM7QUFDQSx1QkFBVyxXQUFXLEtBQUssTUFBM0I7QUFDQSxxQkFBUyxjQUFULEdBQTBCLFFBQTFCO0FBQ0EscUJBQVMsWUFBVCxHQUF3QixRQUF4QjtBQUNBLHFCQUFTLEtBQVQ7QUFDQSxxQkFBUyxTQUFULEdBQXFCLFNBQXJCO0FBQ0g7OzttQ0FFVTtBQUNQLGdCQUFJLEtBQUssSUFBVCxFQUFlO0FBQ1gscUJBQUssSUFBTCxDQUFVLEtBQVYsQ0FBZ0IsT0FBaEIsR0FBMEIsZ0JBQTFCO0FBQ0EscUJBQUssUUFBTCxHQUFnQixLQUFoQjtBQUNBLHFCQUFLLFlBQUwsR0FBb0IsQ0FBcEI7QUFDQSxxQkFBSyxPQUFMLEdBQWUsRUFBZjtBQUNIO0FBQ0o7OzswQ0FFaUIsSyxFQUFPLGEsRUFBZTtBQUNwQyxvQkFBUSxTQUFTLEtBQVQsQ0FBUjtBQUNBLGdCQUFJLE9BQU8sS0FBUCxLQUFpQixRQUFyQixFQUErQjtBQUMvQixnQkFBSSxPQUFPLEtBQUssT0FBTCxDQUFhLGFBQWIsQ0FBMkIsS0FBM0IsQ0FBWDtBQUNBLGdCQUFJLFVBQVUsS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixjQUF4QixDQUF1QyxJQUF2QyxDQUFkO0FBQ0EsZ0JBQUksWUFBWSxJQUFoQixFQUFzQixLQUFLLFdBQUwsQ0FBaUIsT0FBakIsRUFBMEIsYUFBMUIsRUFBeUMsSUFBekM7QUFDekI7OztvQ0FFVyxPLEVBQVMsYSxFQUFlLEksRUFBTTtBQUN0QyxpQkFBSyxLQUFMLENBQVcsa0JBQVgsQ0FBOEIsT0FBOUIsRUFBdUMsSUFBdkMsRUFBNkMsSUFBN0MsRUFBbUQsYUFBbkQsRUFBa0UsSUFBbEU7QUFDSDs7O2dDQUVPLFUsRUFBWSxTLEVBQVcsTyxFQUFTO0FBQ3BDLGdCQUFJLE9BQU8sV0FBVyxNQUFsQixLQUE2QixVQUFqQyxFQUE2QztBQUN6QyxzQkFBTSxJQUFJLEtBQUosQ0FBVSxrREFBVixDQUFOO0FBQ0gsYUFGRCxNQUVPLElBQUksQ0FBQyxPQUFMLEVBQWM7QUFDakIsMkJBQVcsTUFBWCxHQUFvQixXQUFXLE1BQVgsQ0FBa0IsTUFBbEIsQ0FBeUIsU0FBekIsQ0FBcEI7QUFDSCxhQUZNLE1BRUE7QUFDSCwyQkFBVyxNQUFYLEdBQW9CLFNBQXBCO0FBQ0g7QUFDSjs7OytCQUVNLGUsRUFBaUIsUyxFQUFXLE8sRUFBUztBQUN4QyxnQkFBSSxRQUFRLFNBQVMsZUFBVCxDQUFaO0FBQ0EsZ0JBQUksT0FBTyxLQUFQLEtBQWlCLFFBQXJCLEVBQStCLE1BQU0sSUFBSSxLQUFKLENBQVUsdURBQVYsQ0FBTjs7QUFFL0IsZ0JBQUksYUFBYSxLQUFLLFVBQUwsQ0FBZ0IsS0FBaEIsQ0FBakI7O0FBRUEsaUJBQUssT0FBTCxDQUFhLFVBQWIsRUFBeUIsU0FBekIsRUFBb0MsT0FBcEM7QUFDSDs7O3NDQUVhLFMsRUFBVyxPLEVBQVM7QUFDOUIsZ0JBQUksS0FBSyxRQUFULEVBQW1CO0FBQ2YscUJBQUssT0FBTCxDQUFhLEtBQUssT0FBTCxDQUFhLFVBQTFCLEVBQXNDLFNBQXRDLEVBQWlELE9BQWpEO0FBQ0gsYUFGRCxNQUVPO0FBQ0gsc0JBQU0sSUFBSSxLQUFKLENBQVUsK0RBQVYsQ0FBTjtBQUNIO0FBQ0o7OzsrQkFFTSxFLEVBQUk7QUFDUCxnQkFBSSxDQUFDLEVBQUwsRUFBUztBQUNMLHNCQUFNLElBQUksS0FBSixDQUFVLGdEQUFWLENBQU47QUFDSDs7QUFFRDtBQUNBLGdCQUFJLE9BQU8sTUFBUCxLQUFrQixXQUFsQixJQUFpQyxjQUFjLE1BQW5ELEVBQTJEO0FBQ3ZELHFCQUFLLEdBQUcsR0FBSCxFQUFMO0FBQ0g7O0FBRUQ7QUFDQSxnQkFBSSxHQUFHLFdBQUgsS0FBbUIsUUFBbkIsSUFBK0IsR0FBRyxXQUFILEtBQW1CLGNBQWxELElBQW9FLEdBQUcsV0FBSCxLQUFtQixLQUEzRixFQUFrRztBQUM5RixvQkFBSSxTQUFTLEdBQUcsTUFBaEI7QUFDQSxxQkFBSyxJQUFJLElBQUksQ0FBYixFQUFnQixJQUFJLE1BQXBCLEVBQTRCLEVBQUUsQ0FBOUIsRUFBaUM7QUFDN0IseUJBQUssT0FBTCxDQUFhLEdBQUcsQ0FBSCxDQUFiO0FBQ0g7QUFDSixhQUxELE1BS087QUFDSCxxQkFBSyxPQUFMLENBQWEsRUFBYjtBQUNIO0FBQ0o7OztnQ0FFTyxFLEVBQUk7QUFBQTs7QUFDUixpQkFBSyxNQUFMLENBQVksTUFBWixDQUFtQixFQUFuQjtBQUNBLGdCQUFJLEdBQUcsV0FBUCxFQUFvQjtBQUNoQixxQkFBSyxVQUFMLENBQWdCLE1BQWhCLENBQXVCLEdBQUcsV0FBMUI7QUFDSDs7QUFFRCx1QkFBVyxZQUFNO0FBQ2IsbUJBQUcsZUFBSCxDQUFtQixjQUFuQjtBQUNBLHVCQUFLLFFBQUwsR0FBZ0IsS0FBaEI7QUFDQSxvQkFBSSxHQUFHLFdBQVAsRUFBb0I7QUFDaEIsdUJBQUcsV0FBSCxDQUFlLE1BQWY7QUFDSDtBQUNKLGFBTkQ7QUFPSDs7OzhDQXRUNEIsSSxFQUFNO0FBQ2pDLGdCQUFJLE9BQU8sSUFBUCxLQUFnQixXQUFwQixFQUFpQyxPQUFPLElBQVA7QUFDakMsZ0JBQUksS0FBSyxLQUFMLENBQVcsaUJBQVgsQ0FBNkIsS0FBSyxPQUFMLENBQWEsT0FBMUMsQ0FBSixFQUF3RDtBQUNwRCx1QkFBTyxvQ0FBb0MsS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixPQUF4QixHQUFrQyxLQUFLLFFBQUwsQ0FBYyxLQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLFFBQXRDLENBQXRFLElBQXlILFNBQWhJO0FBQ0g7O0FBRUQsbUJBQU8sS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixPQUF4QixHQUFrQyxLQUFLLFFBQUwsQ0FBYyxLQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLFFBQXRDLENBQXpDO0FBQ0Q7OztnREFFOEIsUyxFQUFXO0FBQ3RDLG1CQUFPLFVBQVUsTUFBakI7QUFDSDs7O3FDQUVtQjtBQUNoQixtQkFBTyxDQUFDLFVBQUQsRUFBYSxPQUFiLENBQVA7QUFDSDs7Ozs7O2tCQTBTVSxPOzs7Ozs7Ozs7Ozs7OztJQ3JhVCxhO0FBQ0YsMkJBQVksT0FBWixFQUFxQjtBQUFBOztBQUNqQixhQUFLLE9BQUwsR0FBZSxPQUFmO0FBQ0EsYUFBSyxPQUFMLENBQWEsTUFBYixHQUFzQixJQUF0QjtBQUNIOzs7OzZCQTJCSSxPLEVBQVM7QUFDVixvQkFBUSxZQUFSLEdBQXVCLEtBQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsT0FBbEIsRUFBMkIsSUFBM0IsQ0FBdkI7QUFDQSxvQkFBUSxVQUFSLEdBQXFCLEtBQUssS0FBTCxDQUFXLElBQVgsQ0FBZ0IsT0FBaEIsRUFBeUIsSUFBekIsQ0FBckI7QUFDQSxvQkFBUSxVQUFSLEdBQXFCLEtBQUssS0FBTCxDQUFXLElBQVgsQ0FBZ0IsT0FBaEIsRUFBeUIsSUFBekIsQ0FBckI7O0FBRUEsb0JBQVEsZ0JBQVIsQ0FBeUIsU0FBekIsRUFDSSxRQUFRLFlBRFosRUFDMEIsS0FEMUI7QUFFQSxvQkFBUSxnQkFBUixDQUF5QixPQUF6QixFQUNJLFFBQVEsVUFEWixFQUN3QixLQUR4QjtBQUVBLG9CQUFRLGdCQUFSLENBQXlCLE9BQXpCLEVBQ0ksUUFBUSxVQURaLEVBQ3dCLEtBRHhCO0FBRUg7OzsrQkFFTSxPLEVBQVM7QUFDWixvQkFBUSxtQkFBUixDQUE0QixTQUE1QixFQUNJLFFBQVEsWUFEWixFQUMwQixLQUQxQjtBQUVBLG9CQUFRLG1CQUFSLENBQTRCLE9BQTVCLEVBQ0ksUUFBUSxVQURaLEVBQ3dCLEtBRHhCO0FBRUEsb0JBQVEsbUJBQVIsQ0FBNEIsT0FBNUIsRUFDSSxRQUFRLFVBRFosRUFDd0IsS0FEeEI7O0FBR0EsbUJBQU8sUUFBUSxZQUFmO0FBQ0EsbUJBQU8sUUFBUSxVQUFmO0FBQ0EsbUJBQU8sUUFBUSxVQUFmO0FBQ0g7OztnQ0FFTyxRLEVBQVUsSyxFQUFPO0FBQ3JCLGdCQUFJLFNBQVMsZ0JBQVQsQ0FBMEIsS0FBMUIsQ0FBSixFQUFzQztBQUNsQyx5QkFBUyxPQUFULENBQWlCLFFBQWpCLEdBQTRCLEtBQTVCO0FBQ0EseUJBQVMsT0FBVCxDQUFpQixRQUFqQjtBQUNIOztBQUVELGdCQUFJLFVBQVUsSUFBZDtBQUNBLHFCQUFTLFlBQVQsR0FBd0IsS0FBeEI7O0FBRUEsMEJBQWMsSUFBZCxHQUFxQixPQUFyQixDQUE2QixhQUFLO0FBQzlCLG9CQUFJLEVBQUUsR0FBRixLQUFVLE1BQU0sT0FBcEIsRUFBNkI7QUFDekIsNkJBQVMsWUFBVCxHQUF3QixJQUF4QjtBQUNBLDZCQUFTLFNBQVQsR0FBcUIsRUFBRSxLQUFGLENBQVEsV0FBUixFQUFyQixFQUE0QyxLQUE1QyxFQUFtRCxPQUFuRDtBQUNIO0FBQ0osYUFMRDtBQU1IOzs7OEJBRUssUSxFQUFVLEssRUFBTztBQUNuQixxQkFBUyxVQUFULEdBQXNCLElBQXRCO0FBQ0EscUJBQVMsS0FBVCxDQUFlLElBQWYsQ0FBb0IsSUFBcEIsRUFBMEIsUUFBMUIsRUFBb0MsS0FBcEM7QUFDSDs7OzhCQUVLLFEsRUFBVSxLLEVBQU87QUFDbkIsZ0JBQUksVUFBVSxTQUFTLE9BQXZCO0FBQ0EsZ0JBQUksUUFBUSxJQUFSLElBQWdCLFFBQVEsSUFBUixDQUFhLFFBQWIsQ0FBc0IsTUFBTSxNQUE1QixDQUFwQixFQUF5RDtBQUNyRCxvQkFBSSxLQUFLLE1BQU0sTUFBZjtBQUNBLHNCQUFNLGNBQU47QUFDQSxzQkFBTSxlQUFOO0FBQ0EsdUJBQU8sR0FBRyxRQUFILENBQVksV0FBWixPQUE4QixJQUFyQyxFQUEyQztBQUN2Qyx5QkFBSyxHQUFHLFVBQVI7QUFDQSx3QkFBSSxDQUFDLEVBQUQsSUFBTyxPQUFPLFFBQVEsSUFBMUIsRUFBZ0M7QUFDNUIsOEJBQU0sSUFBSSxLQUFKLENBQVUsOENBQVYsQ0FBTjtBQUNIO0FBQ0o7QUFDRCx3QkFBUSxpQkFBUixDQUEwQixHQUFHLFlBQUgsQ0FBZ0IsWUFBaEIsQ0FBMUIsRUFBeUQsS0FBekQ7QUFDQSx3QkFBUSxRQUFSOztBQUVKO0FBQ0MsYUFkRCxNQWNPLElBQUksUUFBUSxPQUFSLENBQWdCLE9BQWhCLElBQTJCLENBQUMsUUFBUSxPQUFSLENBQWdCLGVBQWhELEVBQWlFO0FBQ3BFLHdCQUFRLE9BQVIsQ0FBZ0IsZUFBaEIsR0FBa0MsS0FBbEM7QUFDQSwyQkFBVztBQUFBLDJCQUFNLFFBQVEsUUFBUixFQUFOO0FBQUEsaUJBQVg7QUFDSDtBQUNKOzs7OEJBRUssUSxFQUFVLEssRUFBTztBQUNuQixnQkFBSSxTQUFTLFVBQWIsRUFBeUI7QUFDckIseUJBQVMsVUFBVCxHQUFzQixLQUF0QjtBQUNIO0FBQ0QscUJBQVMsZUFBVCxDQUF5QixJQUF6Qjs7QUFFQSxnQkFBSSxNQUFNLE9BQU4sS0FBa0IsRUFBdEIsRUFBMEI7O0FBRTFCLGdCQUFJLENBQUMsU0FBUyxPQUFULENBQWlCLFdBQWxCLElBQWlDLFNBQVMsT0FBVCxDQUFpQixnQkFBdEQsRUFBd0U7QUFDcEUseUJBQVMsT0FBVCxDQUFpQixnQkFBakIsR0FBb0MsS0FBcEM7QUFDQSx5QkFBUyxZQUFULEdBQXdCLElBQXhCO0FBQ0EseUJBQVMsU0FBVCxHQUFxQixPQUFyQixFQUE4QixLQUE5QixFQUFxQyxJQUFyQztBQUNBO0FBQ0g7O0FBRUQsZ0JBQUksQ0FBQyxTQUFTLE9BQVQsQ0FBaUIsUUFBdEIsRUFBZ0M7QUFDNUIsb0JBQUksVUFBVSxTQUFTLFVBQVQsQ0FBb0IsUUFBcEIsRUFBOEIsSUFBOUIsRUFBb0MsS0FBcEMsQ0FBZDs7QUFFQSxvQkFBSSxNQUFNLE9BQU4sS0FBa0IsQ0FBQyxPQUF2QixFQUFnQzs7QUFFaEMsb0JBQUksVUFBVSxTQUFTLE9BQVQsQ0FBaUIsUUFBakIsR0FBNEIsSUFBNUIsQ0FBaUMsbUJBQVc7QUFDdEQsMkJBQU8sUUFBUSxVQUFSLENBQW1CLENBQW5CLE1BQTBCLE9BQWpDO0FBQ0gsaUJBRmEsQ0FBZDs7QUFJQSxvQkFBSSxPQUFPLE9BQVAsS0FBbUIsV0FBdkIsRUFBb0M7QUFDaEMsNkJBQVMsU0FBVCxHQUFxQixXQUFyQixDQUFpQyxLQUFqQyxFQUF3QyxJQUF4QyxFQUE4QyxPQUE5QztBQUNIO0FBQ0o7O0FBRUQsZ0JBQUksU0FBUyxPQUFULENBQWlCLE9BQWpCLENBQXlCLE9BQXpCLElBQW9DLFNBQVMsWUFBVCxLQUEwQixLQUE5RCxJQUNHLFNBQVMsT0FBVCxDQUFpQixRQUFqQixJQUE2QixNQUFNLE9BQU4sS0FBa0IsQ0FEdEQsRUFDeUQ7QUFDdkQseUJBQVMsT0FBVCxDQUFpQixXQUFqQixDQUE2QixJQUE3QixFQUFtQyxJQUFuQztBQUNEO0FBQ0o7Ozt5Q0FFZ0IsSyxFQUFPO0FBQ3BCLGdCQUFJLENBQUMsS0FBSyxPQUFMLENBQWEsUUFBbEIsRUFBNEIsT0FBTyxLQUFQOztBQUU1QixnQkFBSSxLQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLFdBQXJCLENBQWlDLE1BQWpDLEtBQTRDLENBQWhELEVBQW1EO0FBQy9DLG9CQUFJLGtCQUFrQixLQUF0QjtBQUNBLDhCQUFjLElBQWQsR0FBcUIsT0FBckIsQ0FBNkIsYUFBSztBQUM5Qix3QkFBSSxNQUFNLE9BQU4sS0FBa0IsRUFBRSxHQUF4QixFQUE2QixrQkFBa0IsSUFBbEI7QUFDaEMsaUJBRkQ7O0FBSUEsdUJBQU8sQ0FBQyxlQUFSO0FBQ0g7O0FBRUQsbUJBQU8sS0FBUDtBQUNIOzs7bUNBRVUsUSxFQUFVLEUsRUFBSSxLLEVBQU87QUFDNUIsZ0JBQUksYUFBSjtBQUNBLGdCQUFJLFVBQVUsU0FBUyxPQUF2QjtBQUNBLGdCQUFJLE9BQU8sUUFBUSxLQUFSLENBQWMsY0FBZCxDQUE2QixLQUE3QixFQUFvQyxRQUFRLGdCQUE1QyxFQUE4RCxJQUE5RCxFQUFvRSxRQUFRLFdBQTVFLENBQVg7O0FBRUEsZ0JBQUksSUFBSixFQUFVO0FBQ04sdUJBQU8sS0FBSyxrQkFBTCxDQUF3QixVQUF4QixDQUFtQyxDQUFuQyxDQUFQO0FBQ0gsYUFGRCxNQUVPO0FBQ0gsdUJBQU8sS0FBUDtBQUNIO0FBQ0o7Ozt3Q0FFZSxFLEVBQUk7QUFDaEIsaUJBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsT0FBckIsR0FBK0IsRUFBL0I7QUFDQSxnQkFBSSxPQUFPLEtBQUssT0FBTCxDQUFhLEtBQWIsQ0FBbUIsY0FBbkIsQ0FBa0MsS0FBbEMsRUFBeUMsS0FBSyxPQUFMLENBQWEsZ0JBQXRELEVBQXdFLElBQXhFLEVBQThFLEtBQUssT0FBTCxDQUFhLFdBQTNGLENBQVg7O0FBRUEsZ0JBQUksSUFBSixFQUFVO0FBQ04scUJBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsWUFBckIsR0FBb0MsS0FBSyxtQkFBekM7QUFDQSxxQkFBSyxPQUFMLENBQWEsT0FBYixDQUFxQixXQUFyQixHQUFtQyxLQUFLLFdBQXhDO0FBQ0EscUJBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsY0FBckIsR0FBc0MsS0FBSyxxQkFBM0M7QUFDSDtBQUNKOzs7b0NBRVc7QUFBQTs7QUFDUixtQkFBTztBQUNILDZCQUFhLHFCQUFDLENBQUQsRUFBSSxFQUFKLEVBQVEsT0FBUixFQUFvQjtBQUM3Qix3QkFBSSxVQUFVLE1BQUssT0FBbkI7QUFDQSw0QkFBUSxPQUFSLENBQWdCLE9BQWhCLEdBQTBCLE9BQTFCOztBQUVBLHdCQUFJLGlCQUFpQixRQUFRLFVBQVIsQ0FBbUIsSUFBbkIsQ0FBd0IsZ0JBQVE7QUFDakQsK0JBQU8sS0FBSyxPQUFMLEtBQWlCLE9BQXhCO0FBQ0gscUJBRm9CLENBQXJCOztBQUlBLDRCQUFRLE9BQVIsQ0FBZ0IsVUFBaEIsR0FBNkIsY0FBN0I7QUFDQSx3QkFBSSxRQUFRLFVBQVosRUFBd0IsUUFBUSxXQUFSLENBQW9CLEVBQXBCLEVBQXdCLElBQXhCO0FBQzNCLGlCQVhFO0FBWUgsdUJBQU8sZUFBQyxDQUFELEVBQUksRUFBSixFQUFXO0FBQ2Q7QUFDQSx3QkFBSSxNQUFLLE9BQUwsQ0FBYSxRQUFqQixFQUEyQjtBQUN2QiwwQkFBRSxjQUFGO0FBQ0EsMEJBQUUsZUFBRjtBQUNBLG1DQUFXLFlBQU07QUFDYixrQ0FBSyxPQUFMLENBQWEsaUJBQWIsQ0FBK0IsTUFBSyxPQUFMLENBQWEsWUFBNUMsRUFBMEQsQ0FBMUQ7QUFDQSxrQ0FBSyxPQUFMLENBQWEsUUFBYjtBQUNILHlCQUhELEVBR0csQ0FISDtBQUlIO0FBQ0osaUJBdEJFO0FBdUJILHdCQUFRLGdCQUFDLENBQUQsRUFBSSxFQUFKLEVBQVc7QUFDZix3QkFBSSxNQUFLLE9BQUwsQ0FBYSxRQUFqQixFQUEyQjtBQUN2QiwwQkFBRSxjQUFGO0FBQ0EsMEJBQUUsZUFBRjtBQUNBLDhCQUFLLE9BQUwsQ0FBYSxRQUFiLEdBQXdCLEtBQXhCO0FBQ0EsOEJBQUssT0FBTCxDQUFhLFFBQWI7QUFDSDtBQUNKLGlCQTlCRTtBQStCSCxxQkFBSyxhQUFDLENBQUQsRUFBSSxFQUFKLEVBQVc7QUFDWjtBQUNBLDBCQUFLLFNBQUwsR0FBaUIsS0FBakIsQ0FBdUIsQ0FBdkIsRUFBMEIsRUFBMUI7QUFDSCxpQkFsQ0U7QUFtQ0gsdUJBQU8sZUFBQyxDQUFELEVBQUksRUFBSixFQUFXO0FBQ2Qsd0JBQUksTUFBSyxPQUFMLENBQWEsUUFBakIsRUFBMkI7QUFDdkIsNEJBQUksTUFBSyxPQUFMLENBQWEsaUJBQWpCLEVBQW9DO0FBQ2hDLGtDQUFLLFNBQUwsR0FBaUIsS0FBakIsQ0FBdUIsQ0FBdkIsRUFBMEIsRUFBMUI7QUFDSCx5QkFGRCxNQUVPLElBQUksQ0FBQyxNQUFLLE9BQUwsQ0FBYSxXQUFsQixFQUErQjtBQUNsQyw4QkFBRSxlQUFGO0FBQ0EsdUNBQVcsWUFBTTtBQUNiLHNDQUFLLE9BQUwsQ0FBYSxRQUFiO0FBQ0Esc0NBQUssT0FBTCxDQUFhLFFBQWIsR0FBd0IsS0FBeEI7QUFDSCw2QkFIRCxFQUdHLENBSEg7QUFJSDtBQUNKO0FBQ0osaUJBL0NFO0FBZ0RILG9CQUFJLFlBQUMsQ0FBRCxFQUFJLEVBQUosRUFBVztBQUNYO0FBQ0Esd0JBQUksTUFBSyxPQUFMLENBQWEsUUFBakIsRUFBMkI7QUFDdkIsMEJBQUUsY0FBRjtBQUNBLDBCQUFFLGVBQUY7QUFDQSw0QkFBSSxRQUFRLE1BQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsYUFBckIsQ0FBbUMsTUFBL0M7QUFBQSw0QkFDSSxXQUFXLE1BQUssT0FBTCxDQUFhLFlBRDVCOztBQUdBLDRCQUFJLFFBQVEsUUFBUixJQUFvQixXQUFXLENBQW5DLEVBQXNDO0FBQ2xDLGtDQUFLLE9BQUwsQ0FBYSxZQUFiO0FBQ0Esa0NBQUssV0FBTDtBQUNILHlCQUhELE1BR08sSUFBSSxhQUFhLENBQWpCLEVBQW9CO0FBQ3pCLGtDQUFLLE9BQUwsQ0FBYSxZQUFiLEdBQTRCLFFBQVEsQ0FBcEM7QUFDQSxrQ0FBSyxXQUFMO0FBQ0Esa0NBQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsU0FBbEIsR0FBOEIsTUFBSyxPQUFMLENBQWEsSUFBYixDQUFrQixZQUFoRDtBQUNEO0FBQ0o7QUFDSixpQkFqRUU7QUFrRUgsc0JBQU0sY0FBQyxDQUFELEVBQUksRUFBSixFQUFXO0FBQ2I7QUFDQSx3QkFBSSxNQUFLLE9BQUwsQ0FBYSxRQUFqQixFQUEyQjtBQUN2QiwwQkFBRSxjQUFGO0FBQ0EsMEJBQUUsZUFBRjtBQUNBLDRCQUFJLFFBQVEsTUFBSyxPQUFMLENBQWEsT0FBYixDQUFxQixhQUFyQixDQUFtQyxNQUFuQyxHQUE0QyxDQUF4RDtBQUFBLDRCQUNJLFdBQVcsTUFBSyxPQUFMLENBQWEsWUFENUI7O0FBR0EsNEJBQUksUUFBUSxRQUFaLEVBQXNCO0FBQ2xCLGtDQUFLLE9BQUwsQ0FBYSxZQUFiO0FBQ0Esa0NBQUssV0FBTDtBQUNILHlCQUhELE1BR08sSUFBSSxVQUFVLFFBQWQsRUFBd0I7QUFDM0Isa0NBQUssT0FBTCxDQUFhLFlBQWIsR0FBNEIsQ0FBNUI7QUFDQSxrQ0FBSyxXQUFMO0FBQ0Esa0NBQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsU0FBbEIsR0FBOEIsQ0FBOUI7QUFDSDtBQUNKO0FBQ0osaUJBbkZFO0FBb0ZILHdCQUFRLGlCQUFDLENBQUQsRUFBSSxFQUFKLEVBQVc7QUFDZix3QkFBSSxNQUFLLE9BQUwsQ0FBYSxRQUFiLElBQXlCLE1BQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsV0FBckIsQ0FBaUMsTUFBakMsR0FBMEMsQ0FBdkUsRUFBMEU7QUFDdEUsOEJBQUssT0FBTCxDQUFhLFFBQWI7QUFDSCxxQkFGRCxNQUVPLElBQUksTUFBSyxPQUFMLENBQWEsUUFBakIsRUFBMkI7QUFDOUIsOEJBQUssT0FBTCxDQUFhLFdBQWIsQ0FBeUIsRUFBekI7QUFDSDtBQUNKO0FBMUZFLGFBQVA7QUE0Rkg7OztvQ0FFVyxLLEVBQU87QUFDZixnQkFBSSxNQUFNLEtBQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsZ0JBQWxCLENBQW1DLElBQW5DLENBQVY7QUFBQSxnQkFDSSxTQUFTLElBQUksTUFBSixLQUFlLENBRDVCOztBQUdBO0FBQ0EsZ0JBQUksaUJBQWlCLEtBQUssYUFBTCxDQUFtQixLQUFLLE9BQUwsQ0FBYSxJQUFoQyxDQUFyQjtBQUFBLGdCQUNJLFdBQVcsS0FBSyxhQUFMLENBQW1CLElBQUksQ0FBSixDQUFuQixDQURmOztBQUdBLGdCQUFJLEtBQUosRUFBVyxLQUFLLE9BQUwsQ0FBYSxZQUFiLEdBQTRCLEtBQTVCOztBQUVYLGlCQUFLLElBQUksSUFBSSxDQUFiLEVBQWdCLElBQUksTUFBcEIsRUFBNEIsR0FBNUIsRUFBaUM7QUFDN0Isb0JBQUksS0FBSyxJQUFJLENBQUosQ0FBVDtBQUNBLG9CQUFJLE1BQU0sS0FBSyxPQUFMLENBQWEsWUFBdkIsRUFBcUM7QUFDakMsd0JBQUksU0FBUyxZQUFZLElBQUUsQ0FBZCxDQUFiO0FBQ0Esd0JBQUksWUFBWSxLQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLFNBQWxDO0FBQ0Esd0JBQUksY0FBYyxZQUFZLGNBQTlCOztBQUVBLHdCQUFJLFNBQVMsV0FBYixFQUEwQjtBQUN4Qiw2QkFBSyxPQUFMLENBQWEsSUFBYixDQUFrQixTQUFsQixJQUErQixRQUEvQjtBQUNELHFCQUZELE1BRU8sSUFBSSxTQUFTLFdBQWIsRUFBMEI7QUFDL0IsNkJBQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsU0FBbEIsSUFBK0IsUUFBL0I7QUFDRDs7QUFFRCx1QkFBRyxTQUFILEdBQWUsS0FBSyxPQUFMLENBQWEsT0FBYixDQUFxQixVQUFyQixDQUFnQyxXQUEvQztBQUNILGlCQVpELE1BWU87QUFDSCx1QkFBRyxTQUFILEdBQWUsRUFBZjtBQUNIO0FBQ0o7QUFDSjs7O3NDQUVhLEksRUFBTSxhLEVBQWU7QUFDakMsZ0JBQUksU0FBUyxLQUFLLHFCQUFMLEdBQTZCLE1BQTFDOztBQUVBLGdCQUFJLGFBQUosRUFBbUI7QUFDakIsb0JBQUksUUFBUSxLQUFLLFlBQUwsSUFBcUIsT0FBTyxnQkFBUCxDQUF3QixJQUF4QixDQUFqQztBQUNBLHVCQUFPLFNBQVMsV0FBVyxNQUFNLFNBQWpCLENBQVQsR0FBdUMsV0FBVyxNQUFNLFlBQWpCLENBQTlDO0FBQ0Q7O0FBRUQsbUJBQU8sTUFBUDtBQUNEOzs7K0JBOVNhO0FBQ1YsbUJBQU8sQ0FBQztBQUNKLHFCQUFLLENBREQ7QUFFSix1QkFBTztBQUZILGFBQUQsRUFHSjtBQUNDLHFCQUFLLENBRE47QUFFQyx1QkFBTztBQUZSLGFBSEksRUFNSjtBQUNDLHFCQUFLLEVBRE47QUFFQyx1QkFBTztBQUZSLGFBTkksRUFTSjtBQUNDLHFCQUFLLEVBRE47QUFFQyx1QkFBTztBQUZSLGFBVEksRUFZSjtBQUNDLHFCQUFLLEVBRE47QUFFQyx1QkFBTztBQUZSLGFBWkksRUFlSjtBQUNDLHFCQUFLLEVBRE47QUFFQyx1QkFBTztBQUZSLGFBZkksRUFrQko7QUFDQyxxQkFBSyxFQUROO0FBRUMsdUJBQU87QUFGUixhQWxCSSxDQUFQO0FBc0JIOzs7Ozs7a0JBMlJVLGE7Ozs7Ozs7Ozs7Ozs7O0lDeFRULGlCO0FBQ0YsK0JBQVksT0FBWixFQUFxQjtBQUFBOztBQUNqQixhQUFLLE9BQUwsR0FBZSxPQUFmO0FBQ0EsYUFBSyxPQUFMLENBQWEsVUFBYixHQUEwQixJQUExQjtBQUNBLGFBQUssSUFBTCxHQUFZLEtBQUssT0FBTCxDQUFhLElBQXpCO0FBQ0g7Ozs7NkJBRUksSSxFQUFNO0FBQUE7O0FBQ1AsaUJBQUssZ0JBQUwsR0FBd0IsS0FBSyxPQUFMLENBQWEsTUFBYixDQUFvQixPQUFwQixDQUE0QixJQUE1QixDQUFpQyxLQUFLLElBQXRDLEVBQTRDLElBQTVDLENBQXhCO0FBQ0EsaUJBQUssY0FBTCxHQUFzQixLQUFLLE9BQUwsQ0FBYSxNQUFiLENBQW9CLEtBQXBCLENBQTBCLElBQTFCLENBQStCLElBQS9CLEVBQXFDLElBQXJDLENBQXRCO0FBQ0EsaUJBQUssd0JBQUwsR0FBZ0MsS0FBSyxRQUFMLENBQWMsWUFBTTtBQUNoRCxvQkFBSSxNQUFLLE9BQUwsQ0FBYSxRQUFqQixFQUEyQjtBQUN2QiwwQkFBSyxPQUFMLENBQWEsV0FBYixDQUF5QixNQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLE9BQTlDLEVBQXVELEtBQXZEO0FBQ0g7QUFDSixhQUorQixFQUk3QixHQUo2QixFQUl4QixLQUp3QixDQUFoQztBQUtBLGlCQUFLLGlCQUFMLEdBQXlCLEtBQUssUUFBTCxDQUFjLFlBQU07QUFDekMsb0JBQUksTUFBSyxPQUFMLENBQWEsUUFBakIsRUFBMkI7QUFDdkIsMEJBQUssT0FBTCxDQUFhLEtBQWIsQ0FBbUIsbUJBQW5CLENBQXVDLElBQXZDO0FBQ0g7QUFDSixhQUp3QixFQUl0QixHQUpzQixFQUlqQixLQUppQixDQUF6Qjs7QUFNQTtBQUNBLGlCQUFLLE9BQUwsQ0FBYSxLQUFiLENBQW1CLFdBQW5CLEdBQWlDLGdCQUFqQyxDQUFrRCxhQUFsRCxFQUNJLEtBQUssY0FEVCxFQUN5QixLQUR6QjtBQUVBLGlCQUFLLGdCQUFMLENBQXNCLFNBQXRCLEVBQ0ksS0FBSyxnQkFEVCxFQUMyQixLQUQzQjtBQUVBLGlCQUFLLE9BQUwsQ0FBYSxLQUFiLENBQW1CLFdBQW5CLEdBQWlDLGdCQUFqQyxDQUFrRCxTQUFsRCxFQUNJLEtBQUssY0FEVCxFQUN5QixLQUR6QjtBQUVBLG1CQUFPLGdCQUFQLENBQXdCLFFBQXhCLEVBQWtDLEtBQUssaUJBQXZDOztBQUVBLGdCQUFJLEtBQUssYUFBVCxFQUF3QjtBQUNwQixxQkFBSyxhQUFMLENBQW1CLGdCQUFuQixDQUFvQyxRQUFwQyxFQUE4QyxLQUFLLHdCQUFuRCxFQUE2RSxLQUE3RTtBQUNILGFBRkQsTUFFTztBQUNILHVCQUFPLGdCQUFQLENBQXdCLFFBQXhCLEVBQWtDLEtBQUssd0JBQXZDO0FBQ0g7QUFFSjs7OytCQUVNLEksRUFBTTtBQUNULGlCQUFLLG1CQUFMLENBQXlCLFNBQXpCLEVBQ0ksS0FBSyxnQkFEVCxFQUMyQixLQUQzQjtBQUVBLG1CQUFPLEtBQUssZ0JBQVo7QUFDQSxpQkFBSyxPQUFMLENBQWEsS0FBYixDQUFtQixXQUFuQixHQUFpQyxtQkFBakMsQ0FBcUQsU0FBckQsRUFDSSxLQUFLLGNBRFQsRUFDeUIsS0FEekI7QUFFQSxpQkFBSyxPQUFMLENBQWEsS0FBYixDQUFtQixXQUFuQixHQUFpQyxtQkFBakMsQ0FBcUQsYUFBckQsRUFDSSxLQUFLLGNBRFQsRUFDeUIsS0FEekI7QUFFQSxtQkFBTyxtQkFBUCxDQUEyQixRQUEzQixFQUFxQyxLQUFLLGlCQUExQzs7QUFFQSxnQkFBSSxLQUFLLGFBQVQsRUFBd0I7QUFDcEIscUJBQUssYUFBTCxDQUFtQixtQkFBbkIsQ0FBdUMsUUFBdkMsRUFBaUQsS0FBSyx3QkFBdEQsRUFBZ0YsS0FBaEY7QUFDSCxhQUZELE1BRU87QUFDSCx1QkFBTyxtQkFBUCxDQUEyQixRQUEzQixFQUFxQyxLQUFLLHdCQUExQztBQUNIO0FBQ0o7OztpQ0FFUSxJLEVBQU0sSSxFQUFNLFMsRUFBVztBQUFBO0FBQUE7O0FBQzVCLGdCQUFJLE9BQUo7QUFDQSxtQkFBTyxZQUFNO0FBQ1Qsb0JBQUksVUFBVSxNQUFkO0FBQUEsb0JBQ0ksT0FBTyxVQURYO0FBRUEsb0JBQUksUUFBUSxTQUFSLEtBQVEsR0FBTTtBQUNkLDhCQUFVLElBQVY7QUFDQSx3QkFBSSxDQUFDLFNBQUwsRUFBZ0IsS0FBSyxLQUFMLENBQVcsT0FBWCxFQUFvQixJQUFwQjtBQUNuQixpQkFIRDtBQUlBLG9CQUFJLFVBQVUsYUFBYSxDQUFDLE9BQTVCO0FBQ0EsNkJBQWEsT0FBYjtBQUNBLDBCQUFVLFdBQVcsS0FBWCxFQUFrQixJQUFsQixDQUFWO0FBQ0Esb0JBQUksT0FBSixFQUFhLEtBQUssS0FBTCxDQUFXLE9BQVgsRUFBb0IsSUFBcEI7QUFDaEIsYUFYRDtBQVlIOzs7Ozs7a0JBSVUsaUI7Ozs7Ozs7Ozs7Ozs7O0FDekVmO0lBQ00sWTtBQUNGLDBCQUFZLE9BQVosRUFBcUI7QUFBQTs7QUFDakIsYUFBSyxPQUFMLEdBQWUsT0FBZjtBQUNBLGFBQUssT0FBTCxDQUFhLEtBQWIsR0FBcUIsSUFBckI7QUFDSDs7OztzQ0FFYTtBQUNWLGdCQUFJLGVBQUo7QUFDQSxnQkFBSSxLQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLFVBQXpCLEVBQXFDO0FBQ2pDLHlCQUFTLEtBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsVUFBckIsQ0FBZ0MsTUFBekM7QUFDSDs7QUFFRCxnQkFBSSxDQUFDLE1BQUwsRUFBYTtBQUNULHVCQUFPLFFBQVA7QUFDSDs7QUFFRCxtQkFBTyxPQUFPLGFBQVAsQ0FBcUIsUUFBNUI7QUFDSDs7OzRDQUVtQixRLEVBQVU7QUFBQTs7QUFDMUIsZ0JBQUksVUFBVSxLQUFLLE9BQUwsQ0FBYSxPQUEzQjtBQUFBLGdCQUNJLG9CQURKOztBQUdBLGdCQUFJLE9BQU8sS0FBSyxjQUFMLENBQW9CLEtBQXBCLEVBQTJCLEtBQUssT0FBTCxDQUFhLGdCQUF4QyxFQUEwRCxJQUExRCxFQUFnRSxLQUFLLE9BQUwsQ0FBYSxXQUE3RSxDQUFYOztBQUVBLGdCQUFJLE9BQU8sSUFBUCxLQUFnQixXQUFwQixFQUFpQzs7QUFFN0Isb0JBQUcsQ0FBQyxLQUFLLE9BQUwsQ0FBYSxZQUFqQixFQUE4QjtBQUMxQix5QkFBSyxPQUFMLENBQWEsSUFBYixDQUFrQixLQUFsQixDQUF3QixPQUF4QjtBQUNBO0FBQ0g7O0FBRUQsb0JBQUksQ0FBQyxLQUFLLGlCQUFMLENBQXVCLFFBQVEsT0FBL0IsQ0FBTCxFQUE4QztBQUMxQyxrQ0FBYyxLQUFLLG1DQUFMLENBQXlDLEtBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsT0FBOUQsRUFDVixLQUFLLGVBREssQ0FBZDtBQUVILGlCQUhELE1BSUs7QUFDRCxrQ0FBYyxLQUFLLCtCQUFMLENBQXFDLEtBQUssZUFBMUMsQ0FBZDtBQUNIOztBQUdELHFCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLE9BQXhCLGFBQTBDLFlBQVksR0FBdEQsd0RBQ2lDLFlBQVksSUFEN0MseURBRWtDLFlBQVksS0FGOUMsMERBR21DLFlBQVksTUFIL0M7O0FBUUEsb0JBQUksWUFBWSxJQUFaLEtBQXFCLE1BQXpCLEVBQWlDO0FBQzdCLHlCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLElBQXhCLEdBQStCLE1BQS9CO0FBQ0g7O0FBRUQsb0JBQUksWUFBWSxHQUFaLEtBQW9CLE1BQXhCLEVBQWdDO0FBQzVCLHlCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLEdBQXhCLEdBQThCLE1BQTlCO0FBQ0g7O0FBRUQsb0JBQUksUUFBSixFQUFjLEtBQUssY0FBTDs7QUFFZCx1QkFBTyxVQUFQLENBQWtCLFlBQU07QUFDcEIsd0JBQUksaUJBQWlCO0FBQ2xCLCtCQUFPLE1BQUssT0FBTCxDQUFhLElBQWIsQ0FBa0IsV0FEUDtBQUVsQixnQ0FBUSxNQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCO0FBRlIscUJBQXJCO0FBSUEsd0JBQUksa0JBQWtCLE1BQUssZUFBTCxDQUFxQixXQUFyQixFQUFrQyxjQUFsQyxDQUF0Qjs7QUFFQSx3QkFBSSw4QkFBOEIsT0FBTyxVQUFQLEdBQW9CLGVBQWUsS0FBbkMsS0FBNkMsZ0JBQWdCLElBQWhCLElBQXdCLGdCQUFnQixLQUFyRixDQUFsQztBQUNBLHdCQUFJLDRCQUE0QixPQUFPLFdBQVAsR0FBcUIsZUFBZSxNQUFwQyxLQUErQyxnQkFBZ0IsR0FBaEIsSUFBdUIsZ0JBQWdCLE1BQXRGLENBQWhDO0FBQ0Esd0JBQUksK0JBQStCLHlCQUFuQyxFQUE4RDtBQUMxRCw4QkFBSyxPQUFMLENBQWEsSUFBYixDQUFrQixLQUFsQixDQUF3QixPQUF4QixHQUFrQyxlQUFsQztBQUNBLDhCQUFLLG1CQUFMLENBQXlCLFFBQXpCO0FBQ0g7QUFDSixpQkFiRCxFQWFHLENBYkg7QUFlSCxhQWpERCxNQWlETztBQUNILHFCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLE9BQXhCLEdBQWtDLGVBQWxDO0FBQ0g7QUFDSjs7O3NDQUVhLGEsRUFBZSxJLEVBQU0sTSxFQUFRO0FBQ3ZDLGdCQUFJLGNBQUo7QUFDQSxnQkFBSSxPQUFPLGFBQVg7O0FBRUEsZ0JBQUksSUFBSixFQUFVO0FBQ04scUJBQUssSUFBSSxJQUFJLENBQWIsRUFBZ0IsSUFBSSxLQUFLLE1BQXpCLEVBQWlDLEdBQWpDLEVBQXNDO0FBQ2xDLDJCQUFPLEtBQUssVUFBTCxDQUFnQixLQUFLLENBQUwsQ0FBaEIsQ0FBUDtBQUNBLHdCQUFJLFNBQVMsU0FBYixFQUF3QjtBQUNwQjtBQUNIO0FBQ0QsMkJBQU8sS0FBSyxNQUFMLEdBQWMsTUFBckIsRUFBNkI7QUFDekIsa0NBQVUsS0FBSyxNQUFmO0FBQ0EsK0JBQU8sS0FBSyxXQUFaO0FBQ0g7QUFDRCx3QkFBSSxLQUFLLFVBQUwsQ0FBZ0IsTUFBaEIsS0FBMkIsQ0FBM0IsSUFBZ0MsQ0FBQyxLQUFLLE1BQTFDLEVBQWtEO0FBQzlDLCtCQUFPLEtBQUssZUFBWjtBQUNIO0FBQ0o7QUFDSjtBQUNELGdCQUFJLE1BQU0sS0FBSyxrQkFBTCxFQUFWOztBQUVBLG9CQUFRLEtBQUssV0FBTCxHQUFtQixXQUFuQixFQUFSO0FBQ0Esa0JBQU0sUUFBTixDQUFlLElBQWYsRUFBcUIsTUFBckI7QUFDQSxrQkFBTSxNQUFOLENBQWEsSUFBYixFQUFtQixNQUFuQjtBQUNBLGtCQUFNLFFBQU4sQ0FBZSxJQUFmOztBQUVBLGdCQUFJO0FBQ0Esb0JBQUksZUFBSjtBQUNILGFBRkQsQ0FFRSxPQUFPLEtBQVAsRUFBYyxDQUFFOztBQUVsQixnQkFBSSxRQUFKLENBQWEsS0FBYjtBQUNBLDBCQUFjLEtBQWQ7QUFDSDs7O3VDQUVjLGEsRUFBZSxJLEVBQU0sTSxFQUFRO0FBQ3hDLGdCQUFJLENBQUMsS0FBSyxpQkFBTCxDQUF1QixhQUF2QixDQUFMLEVBQTRDO0FBQ3hDLG9CQUFJLGtCQUFrQixLQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLE9BQTNDLEVBQW9EO0FBQ2hELGtDQUFjLEtBQWQ7QUFDSDtBQUNKLGFBSkQsTUFJTztBQUNILHFCQUFLLGFBQUwsQ0FBbUIsYUFBbkIsRUFBa0MsSUFBbEMsRUFBd0MsTUFBeEM7QUFDSDtBQUNKOzs7MkNBRWtCLEksRUFBTSxtQixFQUFxQixnQixFQUFrQixhLEVBQWUsSSxFQUFNO0FBQ2pGLGdCQUFJLFVBQVUsS0FBSyxPQUFMLENBQWEsT0FBM0I7QUFDQSxpQkFBSyxjQUFMLENBQW9CLFFBQVEsT0FBNUIsRUFBcUMsUUFBUSxZQUE3QyxFQUEyRCxRQUFRLGNBQW5FOztBQUVBLGdCQUFJLE9BQU8sS0FBSyxjQUFMLENBQW9CLElBQXBCLEVBQTBCLGdCQUExQixFQUE0QyxtQkFBNUMsRUFBaUUsS0FBSyxPQUFMLENBQWEsV0FBOUUsQ0FBWDs7QUFFQTtBQUNBLGdCQUFJLGVBQWUsSUFBSSxXQUFKLENBQWdCLGtCQUFoQixFQUFvQztBQUNuRCx3QkFBUTtBQUNKLDBCQUFNLElBREY7QUFFSiwyQkFBTztBQUZIO0FBRDJDLGFBQXBDLENBQW5COztBQU9BLGdCQUFJLFNBQVMsU0FBYixFQUF3QjtBQUNwQixvQkFBSSxDQUFDLEtBQUssaUJBQUwsQ0FBdUIsUUFBUSxPQUEvQixDQUFMLEVBQThDO0FBQzFDLHdCQUFJLFVBQVUsS0FBSyxPQUFMLENBQWEsT0FBYixDQUFxQixPQUFuQztBQUNBLHdCQUFJLGFBQWEsT0FBTyxLQUFLLE9BQUwsQ0FBYSxpQkFBcEIsSUFBeUMsUUFBekMsR0FDWCxLQUFLLE9BQUwsQ0FBYSxpQkFERixHQUVYLEdBRk47QUFHQSw0QkFBUSxVQUFSO0FBQ0Esd0JBQUksV0FBVyxLQUFLLGVBQXBCO0FBQ0Esd0JBQUksU0FBUyxLQUFLLGVBQUwsR0FBdUIsS0FBSyxXQUFMLENBQWlCLE1BQXhDLEdBQWlELFdBQVcsTUFBekU7QUFDQSw0QkFBUSxLQUFSLEdBQWdCLFFBQVEsS0FBUixDQUFjLFNBQWQsQ0FBd0IsQ0FBeEIsRUFBMkIsUUFBM0IsSUFBdUMsSUFBdkMsR0FDWixRQUFRLEtBQVIsQ0FBYyxTQUFkLENBQXdCLE1BQXhCLEVBQWdDLFFBQVEsS0FBUixDQUFjLE1BQTlDLENBREo7QUFFQSw0QkFBUSxjQUFSLEdBQXlCLFdBQVcsS0FBSyxNQUF6QztBQUNBLDRCQUFRLFlBQVIsR0FBdUIsV0FBVyxLQUFLLE1BQXZDO0FBQ0gsaUJBWkQsTUFZTztBQUNIO0FBQ0Esd0JBQUksY0FBYSxPQUFPLEtBQUssT0FBTCxDQUFhLGlCQUFwQixJQUF5QyxRQUF6QyxHQUNYLEtBQUssT0FBTCxDQUFhLGlCQURGLEdBRVgsTUFGTjtBQUdBLDRCQUFRLFdBQVI7QUFDQSx5QkFBSyxTQUFMLENBQWUsSUFBZixFQUFxQixLQUFLLGVBQTFCLEVBQ0ksS0FBSyxlQUFMLEdBQXVCLEtBQUssV0FBTCxDQUFpQixNQUF4QyxHQUFpRCxDQURyRDtBQUVIOztBQUVELHdCQUFRLE9BQVIsQ0FBZ0IsYUFBaEIsQ0FBOEIsWUFBOUI7QUFDSDtBQUNKOzs7a0NBRVMsSSxFQUFNLFEsRUFBVSxNLEVBQVE7QUFDOUIsZ0JBQUksY0FBSjtBQUFBLGdCQUFXLFlBQVg7QUFDQSxrQkFBTSxLQUFLLGtCQUFMLEVBQU47QUFDQSxvQkFBUSxLQUFLLFdBQUwsR0FBbUIsV0FBbkIsRUFBUjtBQUNBLGtCQUFNLFFBQU4sQ0FBZSxJQUFJLFVBQW5CLEVBQStCLFFBQS9CO0FBQ0Esa0JBQU0sTUFBTixDQUFhLElBQUksVUFBakIsRUFBNkIsTUFBN0I7QUFDQSxrQkFBTSxjQUFOOztBQUVBLGdCQUFJLEtBQUssS0FBSyxXQUFMLEdBQW1CLGFBQW5CLENBQWlDLEtBQWpDLENBQVQ7QUFDQSxlQUFHLFNBQUgsR0FBZSxJQUFmO0FBQ0EsZ0JBQUksT0FBTyxLQUFLLFdBQUwsR0FBbUIsc0JBQW5CLEVBQVg7QUFBQSxnQkFDSSxhQURKO0FBQUEsZ0JBQ1UsaUJBRFY7QUFFQSxtQkFBUSxPQUFPLEdBQUcsVUFBbEIsRUFBK0I7QUFDM0IsMkJBQVcsS0FBSyxXQUFMLENBQWlCLElBQWpCLENBQVg7QUFDSDtBQUNELGtCQUFNLFVBQU4sQ0FBaUIsSUFBakI7O0FBRUE7QUFDQSxnQkFBSSxRQUFKLEVBQWM7QUFDVix3QkFBUSxNQUFNLFVBQU4sRUFBUjtBQUNBLHNCQUFNLGFBQU4sQ0FBb0IsUUFBcEI7QUFDQSxzQkFBTSxRQUFOLENBQWUsSUFBZjtBQUNBLG9CQUFJLGVBQUo7QUFDQSxvQkFBSSxRQUFKLENBQWEsS0FBYjtBQUNIO0FBQ0o7Ozs2Q0FFb0I7QUFDakIsZ0JBQUksS0FBSyxPQUFMLENBQWEsVUFBYixDQUF3QixNQUE1QixFQUFvQztBQUNoQyx1QkFBTyxLQUFLLE9BQUwsQ0FBYSxVQUFiLENBQXdCLE1BQXhCLENBQStCLGFBQS9CLENBQTZDLFlBQTdDLEVBQVA7QUFDSDs7QUFFRCxtQkFBTyxPQUFPLFlBQVAsRUFBUDtBQUNIOzs7Z0RBRXVCLE8sRUFBUztBQUM3QixnQkFBSSxRQUFRLFVBQVIsS0FBdUIsSUFBM0IsRUFBaUM7QUFDN0IsdUJBQU8sQ0FBUDtBQUNIOztBQUVELGlCQUFLLElBQUksSUFBSSxDQUFiLEVBQWdCLElBQUksUUFBUSxVQUFSLENBQW1CLFVBQW5CLENBQThCLE1BQWxELEVBQTBELEdBQTFELEVBQStEO0FBQzNELG9CQUFJLE9BQU8sUUFBUSxVQUFSLENBQW1CLFVBQW5CLENBQThCLENBQTlCLENBQVg7O0FBRUEsb0JBQUksU0FBUyxPQUFiLEVBQXNCO0FBQ2xCLDJCQUFPLENBQVA7QUFDSDtBQUNKO0FBQ0o7Ozt1REFFOEIsRyxFQUFLO0FBQ2hDLGdCQUFJLE1BQU0sS0FBSyxrQkFBTCxFQUFWO0FBQ0EsZ0JBQUksV0FBVyxJQUFJLFVBQW5CO0FBQ0EsZ0JBQUksT0FBTyxFQUFYO0FBQ0EsZ0JBQUksZUFBSjs7QUFFQSxnQkFBSSxZQUFZLElBQWhCLEVBQXNCO0FBQ2xCLG9CQUFJLFVBQUo7QUFDQSxvQkFBSSxLQUFLLFNBQVMsZUFBbEI7QUFDQSx1QkFBTyxhQUFhLElBQWIsSUFBcUIsT0FBTyxNQUFuQyxFQUEyQztBQUN2Qyx3QkFBSSxLQUFLLHVCQUFMLENBQTZCLFFBQTdCLENBQUo7QUFDQSx5QkFBSyxJQUFMLENBQVUsQ0FBVjtBQUNBLCtCQUFXLFNBQVMsVUFBcEI7QUFDQSx3QkFBSSxhQUFhLElBQWpCLEVBQXVCO0FBQ25CLDZCQUFLLFNBQVMsZUFBZDtBQUNIO0FBQ0o7QUFDRCxxQkFBSyxPQUFMOztBQUVBO0FBQ0EseUJBQVMsSUFBSSxVQUFKLENBQWUsQ0FBZixFQUFrQixXQUEzQjs7QUFFQSx1QkFBTztBQUNILDhCQUFVLFFBRFA7QUFFSCwwQkFBTSxJQUZIO0FBR0gsNEJBQVE7QUFITCxpQkFBUDtBQUtIO0FBQ0o7OzsyREFFa0M7QUFDL0IsZ0JBQUksVUFBVSxLQUFLLE9BQUwsQ0FBYSxPQUEzQjtBQUFBLGdCQUNJLE9BQU8sRUFEWDs7QUFHQSxnQkFBSSxDQUFDLEtBQUssaUJBQUwsQ0FBdUIsUUFBUSxPQUEvQixDQUFMLEVBQThDO0FBQzFDLG9CQUFJLGdCQUFnQixLQUFLLE9BQUwsQ0FBYSxPQUFiLENBQXFCLE9BQXpDO0FBQ0Esb0JBQUksYUFBSixFQUFtQjtBQUNmLHdCQUFJLFdBQVcsY0FBYyxjQUE3QjtBQUNBLHdCQUFJLGNBQWMsS0FBZCxJQUF1QixZQUFZLENBQXZDLEVBQTBDO0FBQ3RDLCtCQUFPLGNBQWMsS0FBZCxDQUFvQixTQUFwQixDQUE4QixDQUE5QixFQUFpQyxRQUFqQyxDQUFQO0FBQ0g7QUFDSjtBQUVKLGFBVEQsTUFTTztBQUNILG9CQUFJLGVBQWUsS0FBSyxrQkFBTCxHQUEwQixVQUE3Qzs7QUFFQSxvQkFBSSxnQkFBZ0IsSUFBcEIsRUFBMEI7QUFDdEIsd0JBQUkscUJBQXFCLGFBQWEsV0FBdEM7QUFDQSx3QkFBSSxvQkFBb0IsS0FBSyxrQkFBTCxHQUEwQixVQUExQixDQUFxQyxDQUFyQyxFQUF3QyxXQUFoRTs7QUFFQSx3QkFBSSxzQkFBc0IscUJBQXFCLENBQS9DLEVBQWtEO0FBQzlDLCtCQUFPLG1CQUFtQixTQUFuQixDQUE2QixDQUE3QixFQUFnQyxpQkFBaEMsQ0FBUDtBQUNIO0FBQ0o7QUFDSjs7QUFFRCxtQkFBTyxJQUFQO0FBQ0g7Ozt1Q0FFYyxpQixFQUFtQixnQixFQUFrQixtQixFQUFxQixXLEVBQWE7QUFBQTs7QUFDbEYsZ0JBQUksTUFBTSxLQUFLLE9BQUwsQ0FBYSxPQUF2QjtBQUNBLGdCQUFJLGlCQUFKO0FBQUEsZ0JBQWMsYUFBZDtBQUFBLGdCQUFvQixlQUFwQjs7QUFFQSxnQkFBSSxDQUFDLEtBQUssaUJBQUwsQ0FBdUIsSUFBSSxPQUEzQixDQUFMLEVBQTBDO0FBQ3RDLDJCQUFXLEtBQUssT0FBTCxDQUFhLE9BQWIsQ0FBcUIsT0FBaEM7QUFDSCxhQUZELE1BRU87QUFDSCxvQkFBSSxnQkFBZ0IsS0FBSyw4QkFBTCxDQUFvQyxHQUFwQyxDQUFwQjs7QUFFQSxvQkFBSSxhQUFKLEVBQW1CO0FBQ2YsK0JBQVcsY0FBYyxRQUF6QjtBQUNBLDJCQUFPLGNBQWMsSUFBckI7QUFDQSw2QkFBUyxjQUFjLE1BQXZCO0FBQ0g7QUFDSjs7QUFFRCxnQkFBSSxpQkFBaUIsS0FBSyxnQ0FBTCxFQUFyQjs7QUFFQSxnQkFBSSxtQkFBbUIsU0FBbkIsSUFBZ0MsbUJBQW1CLElBQXZELEVBQTZEO0FBQ3pELG9CQUFJLDJCQUEyQixDQUFDLENBQWhDO0FBQ0Esb0JBQUksb0JBQUo7O0FBRUEscUJBQUssT0FBTCxDQUFhLFVBQWIsQ0FBd0IsT0FBeEIsQ0FBZ0Msa0JBQVU7QUFDdEMsd0JBQUksSUFBSSxPQUFPLE9BQWY7QUFDQSx3QkFBSSxNQUFNLE9BQU8sbUJBQVAsR0FDTixPQUFLLHlCQUFMLENBQStCLGNBQS9CLEVBQStDLENBQS9DLENBRE0sR0FFTixlQUFlLFdBQWYsQ0FBMkIsQ0FBM0IsQ0FGSjs7QUFJQSx3QkFBSSxNQUFNLHdCQUFWLEVBQW9DO0FBQ2hDLG1EQUEyQixHQUEzQjtBQUNBLHNDQUFjLENBQWQ7QUFDQSw4Q0FBc0IsT0FBTyxtQkFBN0I7QUFDSDtBQUNKLGlCQVhEOztBQWFBLG9CQUFJLDRCQUE0QixDQUE1QixLQUVJLDZCQUE2QixDQUE3QixJQUNBLENBQUMsbUJBREQsSUFFQSxZQUFZLElBQVosQ0FDSSxlQUFlLFNBQWYsQ0FDSSwyQkFBMkIsQ0FEL0IsRUFFSSx3QkFGSixDQURKLENBSkosQ0FBSixFQVVFO0FBQ0Usd0JBQUksd0JBQXdCLGVBQWUsU0FBZixDQUF5QiwyQkFBMkIsQ0FBcEQsRUFDeEIsZUFBZSxNQURTLENBQTVCOztBQUdBLGtDQUFjLGVBQWUsU0FBZixDQUF5Qix3QkFBekIsRUFBbUQsMkJBQTJCLENBQTlFLENBQWQ7QUFDQSx3QkFBSSxtQkFBbUIsc0JBQXNCLFNBQXRCLENBQWdDLENBQWhDLEVBQW1DLENBQW5DLENBQXZCO0FBQ0Esd0JBQUksZUFBZSxzQkFBc0IsTUFBdEIsR0FBK0IsQ0FBL0IsS0FFWCxxQkFBcUIsR0FBckIsSUFDQSxxQkFBcUIsTUFIVixDQUFuQjtBQUtBLHdCQUFJLGdCQUFKLEVBQXNCO0FBQ2xCLGdEQUF3QixzQkFBc0IsSUFBdEIsRUFBeEI7QUFDSDs7QUFFRCx3QkFBSSxRQUFRLGNBQWMsU0FBZCxHQUEwQixXQUF0Qzs7QUFFQSx5QkFBSyxPQUFMLENBQWEsZ0JBQWIsR0FBZ0MsTUFBTSxJQUFOLENBQVcscUJBQVgsQ0FBaEM7O0FBRUEsd0JBQUksQ0FBQyxZQUFELEtBQWtCLHFCQUFxQixDQUFFLE1BQU0sSUFBTixDQUFXLHFCQUFYLENBQXpDLENBQUosRUFBa0Y7QUFDOUUsK0JBQU87QUFDSCw2Q0FBaUIsd0JBRGQ7QUFFSCx5Q0FBYSxxQkFGVjtBQUdILG9EQUF3QixRQUhyQjtBQUlILGlEQUFxQixJQUpsQjtBQUtILG1EQUF1QixNQUxwQjtBQU1ILGdEQUFvQjtBQU5qQix5QkFBUDtBQVFIO0FBQ0o7QUFDSjtBQUNKOzs7a0RBRTBCLEcsRUFBSyxJLEVBQU07QUFDbEMsZ0JBQUksY0FBYyxJQUFJLEtBQUosQ0FBVSxFQUFWLEVBQWMsT0FBZCxHQUF3QixJQUF4QixDQUE2QixFQUE3QixDQUFsQjtBQUNBLGdCQUFJLFFBQVEsQ0FBQyxDQUFiOztBQUVBLGlCQUFLLElBQUksT0FBTyxDQUFYLEVBQWMsTUFBTSxJQUFJLE1BQTdCLEVBQXFDLE9BQU8sR0FBNUMsRUFBaUQsTUFBakQsRUFBeUQ7QUFDckQsb0JBQUksWUFBWSxTQUFTLElBQUksTUFBSixHQUFhLENBQXRDO0FBQ0Esb0JBQUksZUFBZSxLQUFLLElBQUwsQ0FBVSxZQUFZLE9BQU8sQ0FBbkIsQ0FBVixDQUFuQjtBQUNBLG9CQUFJLFFBQVEsU0FBUyxZQUFZLElBQVosQ0FBckI7O0FBRUEsb0JBQUksVUFBVSxhQUFhLFlBQXZCLENBQUosRUFBMEM7QUFDdEMsNEJBQVEsSUFBSSxNQUFKLEdBQWEsQ0FBYixHQUFpQixJQUF6QjtBQUNBO0FBQ0g7QUFDSjs7QUFFRCxtQkFBTyxLQUFQO0FBQ0g7OzswQ0FFaUIsTyxFQUFTO0FBQ3ZCLG1CQUFPLFFBQVEsUUFBUixLQUFxQixPQUFyQixJQUFnQyxRQUFRLFFBQVIsS0FBcUIsVUFBNUQ7QUFDSDs7O3dDQUVlLFcsRUFBYSxjLEVBQWdCO0FBQ3pDLGdCQUFJLGNBQWMsT0FBTyxVQUF6QjtBQUNBLGdCQUFJLGVBQWUsT0FBTyxXQUExQjtBQUNBLGdCQUFJLE1BQU0sU0FBUyxlQUFuQjtBQUNBLGdCQUFJLGFBQWEsQ0FBQyxPQUFPLFdBQVAsSUFBc0IsSUFBSSxVQUEzQixLQUEwQyxJQUFJLFVBQUosSUFBa0IsQ0FBNUQsQ0FBakI7QUFDQSxnQkFBSSxZQUFZLENBQUMsT0FBTyxXQUFQLElBQXNCLElBQUksU0FBM0IsS0FBeUMsSUFBSSxTQUFKLElBQWlCLENBQTFELENBQWhCOztBQUVBLGdCQUFJLFVBQVUsT0FBTyxZQUFZLEdBQW5CLEtBQTJCLFFBQTNCLEdBQXNDLFlBQVksR0FBbEQsR0FBd0QsWUFBWSxZQUFaLEdBQTJCLFlBQVksTUFBdkMsR0FBZ0QsZUFBZSxNQUFySTtBQUNBLGdCQUFJLFlBQVksT0FBTyxZQUFZLEtBQW5CLEtBQTZCLFFBQTdCLEdBQXdDLFlBQVksS0FBcEQsR0FBNEQsWUFBWSxJQUFaLEdBQW1CLGVBQWUsS0FBOUc7QUFDQSxnQkFBSSxhQUFhLE9BQU8sWUFBWSxNQUFuQixLQUE4QixRQUE5QixHQUF5QyxZQUFZLE1BQXJELEdBQThELFlBQVksR0FBWixHQUFrQixlQUFlLE1BQWhIO0FBQ0EsZ0JBQUksV0FBVyxPQUFPLFlBQVksSUFBbkIsS0FBNEIsUUFBNUIsR0FBdUMsWUFBWSxJQUFuRCxHQUEwRCxhQUFhLFdBQWIsR0FBMkIsWUFBWSxLQUF2QyxHQUErQyxlQUFlLEtBQXZJOztBQUVBLG1CQUFPO0FBQ0gscUJBQUssVUFBVSxLQUFLLEtBQUwsQ0FBVyxTQUFYLENBRFo7QUFFSCx1QkFBTyxZQUFZLEtBQUssSUFBTCxDQUFVLGFBQWEsV0FBdkIsQ0FGaEI7QUFHSCx3QkFBUSxhQUFhLEtBQUssSUFBTCxDQUFVLFlBQVksWUFBdEIsQ0FIbEI7QUFJSCxzQkFBTSxXQUFXLEtBQUssS0FBTCxDQUFXLFVBQVg7QUFKZCxhQUFQO0FBTUg7Ozs0Q0FFbUI7QUFDaEI7QUFDQTtBQUNBO0FBQ0EsZ0JBQUksYUFBYTtBQUNiLHVCQUFPLElBRE07QUFFYix3QkFBUTtBQUZLLGFBQWpCOztBQUtBLGlCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLE9BQXhCO0FBTUQsdUJBQVcsS0FBWCxHQUFtQixLQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLFdBQXJDO0FBQ0EsdUJBQVcsTUFBWCxHQUFvQixLQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLFlBQXRDOztBQUVBLGlCQUFLLE9BQUwsQ0FBYSxJQUFiLENBQWtCLEtBQWxCLENBQXdCLE9BQXhCOztBQUVBLG1CQUFPLFVBQVA7QUFDRjs7OzREQUVtQyxPLEVBQVMsUSxFQUFVLE8sRUFBUztBQUM1RCxnQkFBSSxhQUFhLENBQUMsV0FBRCxFQUFjLFdBQWQsRUFBMkIsT0FBM0IsRUFBb0MsUUFBcEMsRUFBOEMsV0FBOUMsRUFDYixXQURhLEVBQ0EsZ0JBREEsRUFDa0Isa0JBRGxCLEVBRWIsbUJBRmEsRUFFUSxpQkFGUixFQUUyQixZQUYzQixFQUdiLGNBSGEsRUFHRyxlQUhILEVBR29CLGFBSHBCLEVBSWIsV0FKYSxFQUlBLGFBSkEsRUFJZSxZQUpmLEVBSTZCLGFBSjdCLEVBS2IsVUFMYSxFQUtELGdCQUxDLEVBS2lCLFlBTGpCLEVBSytCLFlBTC9CLEVBTWIsV0FOYSxFQU1BLGVBTkEsRUFNaUIsWUFOakIsRUFPYixnQkFQYSxFQU9LLGVBUEwsRUFPc0IsYUFQdEIsQ0FBakI7O0FBVUEsZ0JBQUksWUFBYSxPQUFPLGVBQVAsS0FBMkIsSUFBNUM7O0FBRUEsZ0JBQUksTUFBTSxLQUFLLFdBQUwsR0FBbUIsYUFBbkIsQ0FBaUMsS0FBakMsQ0FBVjtBQUNBLGdCQUFJLEVBQUosR0FBUywwQ0FBVDtBQUNBLGlCQUFLLFdBQUwsR0FBbUIsSUFBbkIsQ0FBd0IsV0FBeEIsQ0FBb0MsR0FBcEM7O0FBRUEsZ0JBQUksUUFBUSxJQUFJLEtBQWhCO0FBQ0EsZ0JBQUksV0FBVyxPQUFPLGdCQUFQLEdBQTBCLGlCQUFpQixPQUFqQixDQUExQixHQUFzRCxRQUFRLFlBQTdFOztBQUVBLGtCQUFNLFVBQU4sR0FBbUIsVUFBbkI7QUFDQSxnQkFBSSxRQUFRLFFBQVIsS0FBcUIsT0FBekIsRUFBa0M7QUFDOUIsc0JBQU0sUUFBTixHQUFpQixZQUFqQjtBQUNIOztBQUVEO0FBQ0Esa0JBQU0sUUFBTixHQUFpQixVQUFqQjtBQUNBLGtCQUFNLFVBQU4sR0FBbUIsUUFBbkI7O0FBRUE7QUFDQSx1QkFBVyxPQUFYLENBQW1CLGdCQUFRO0FBQ3ZCLHNCQUFNLElBQU4sSUFBYyxTQUFTLElBQVQsQ0FBZDtBQUNILGFBRkQ7O0FBSUEsZ0JBQUksU0FBSixFQUFlO0FBQ1gsc0JBQU0sS0FBTixHQUFrQixTQUFTLFNBQVMsS0FBbEIsSUFBMkIsQ0FBN0M7QUFDQSxvQkFBSSxRQUFRLFlBQVIsR0FBdUIsU0FBUyxTQUFTLE1BQWxCLENBQTNCLEVBQ0ksTUFBTSxTQUFOLEdBQWtCLFFBQWxCO0FBQ1AsYUFKRCxNQUlPO0FBQ0gsc0JBQU0sUUFBTixHQUFpQixRQUFqQjtBQUNIOztBQUVELGdCQUFJLFdBQUosR0FBa0IsUUFBUSxLQUFSLENBQWMsU0FBZCxDQUF3QixDQUF4QixFQUEyQixRQUEzQixDQUFsQjs7QUFFQSxnQkFBSSxRQUFRLFFBQVIsS0FBcUIsT0FBekIsRUFBa0M7QUFDOUIsb0JBQUksV0FBSixHQUFrQixJQUFJLFdBQUosQ0FBZ0IsT0FBaEIsQ0FBd0IsS0FBeEIsRUFBK0IsR0FBL0IsQ0FBbEI7QUFDSDs7QUFFRCxnQkFBSSxPQUFPLEtBQUssV0FBTCxHQUFtQixhQUFuQixDQUFpQyxNQUFqQyxDQUFYO0FBQ0EsaUJBQUssV0FBTCxHQUFtQixRQUFRLEtBQVIsQ0FBYyxTQUFkLENBQXdCLFFBQXhCLEtBQXFDLEdBQXhEO0FBQ0EsZ0JBQUksV0FBSixDQUFnQixJQUFoQjs7QUFFQSxnQkFBSSxPQUFPLFFBQVEscUJBQVIsRUFBWDtBQUNBLGdCQUFJLE1BQU0sU0FBUyxlQUFuQjtBQUNBLGdCQUFJLGFBQWEsQ0FBQyxPQUFPLFdBQVAsSUFBc0IsSUFBSSxVQUEzQixLQUEwQyxJQUFJLFVBQUosSUFBa0IsQ0FBNUQsQ0FBakI7QUFDQSxnQkFBSSxZQUFZLENBQUMsT0FBTyxXQUFQLElBQXNCLElBQUksU0FBM0IsS0FBeUMsSUFBSSxTQUFKLElBQWlCLENBQTFELENBQWhCOztBQUVBLGdCQUFJLGNBQWM7QUFDZCxxQkFBSyxLQUFLLEdBQUwsR0FBVyxTQUFYLEdBQXVCLEtBQUssU0FBNUIsR0FBd0MsU0FBUyxTQUFTLGNBQWxCLENBQXhDLEdBQTRFLFNBQVMsU0FBUyxRQUFsQixDQUE1RSxHQUEwRyxRQUFRLFNBRHpHO0FBRWQsc0JBQU0sS0FBSyxJQUFMLEdBQVksVUFBWixHQUF5QixLQUFLLFVBQTlCLEdBQTJDLFNBQVMsU0FBUyxlQUFsQjtBQUZuQyxhQUFsQjs7QUFLQSxnQkFBSSxjQUFjLE9BQU8sVUFBekI7QUFDQSxnQkFBSSxlQUFlLE9BQU8sV0FBMUI7O0FBRUEsZ0JBQUksaUJBQWlCLEtBQUssaUJBQUwsRUFBckI7QUFDQSxnQkFBSSxrQkFBa0IsS0FBSyxlQUFMLENBQXFCLFdBQXJCLEVBQWtDLGNBQWxDLENBQXRCOztBQUVBLGdCQUFJLGdCQUFnQixLQUFwQixFQUEyQjtBQUN2Qiw0QkFBWSxLQUFaLEdBQW9CLGNBQWMsWUFBWSxJQUE5QztBQUNBLDRCQUFZLElBQVosR0FBbUIsTUFBbkI7QUFDSDs7QUFFRCxnQkFBSSxlQUFlLEtBQUssT0FBTCxDQUFhLGFBQWIsR0FDYixLQUFLLE9BQUwsQ0FBYSxhQUFiLENBQTJCLFlBRGQsR0FFYixLQUFLLFdBQUwsR0FBbUIsSUFBbkIsQ0FBd0IsWUFGOUI7O0FBSUEsZ0JBQUksZ0JBQWdCLE1BQXBCLEVBQTRCO0FBQ3hCLG9CQUFJLGFBQWEsS0FBSyxPQUFMLENBQWEsYUFBYixHQUNYLEtBQUssT0FBTCxDQUFhLGFBQWIsQ0FBMkIscUJBQTNCLEVBRFcsR0FFWCxLQUFLLFdBQUwsR0FBbUIsSUFBbkIsQ0FBd0IscUJBQXhCLEVBRk47QUFHQSxvQkFBSSx1QkFBdUIsZ0JBQWdCLGVBQWUsV0FBVyxHQUExQyxDQUEzQjs7QUFFQSw0QkFBWSxNQUFaLEdBQXFCLHdCQUF3QixlQUFlLEtBQUssR0FBcEIsR0FBMEIsS0FBSyxTQUF2RCxDQUFyQjtBQUNBLDRCQUFZLEdBQVosR0FBa0IsTUFBbEI7QUFDSDs7QUFFRCw4QkFBa0IsS0FBSyxlQUFMLENBQXFCLFdBQXJCLEVBQWtDLGNBQWxDLENBQWxCO0FBQ0EsZ0JBQUksZ0JBQWdCLElBQXBCLEVBQTBCO0FBQ3RCLDRCQUFZLElBQVosR0FBbUIsY0FBYyxlQUFlLEtBQTdCLEdBQ2IsYUFBYSxXQUFiLEdBQTJCLGVBQWUsS0FEN0IsR0FFYixVQUZOO0FBR0EsdUJBQU8sWUFBWSxLQUFuQjtBQUNIO0FBQ0QsZ0JBQUksZ0JBQWdCLEdBQXBCLEVBQXlCO0FBQ3JCLDRCQUFZLEdBQVosR0FBa0IsZUFBZSxlQUFlLE1BQTlCLEdBQ1osWUFBWSxZQUFaLEdBQTJCLGVBQWUsTUFEOUIsR0FFWixTQUZOO0FBR0EsdUJBQU8sWUFBWSxNQUFuQjtBQUNIOztBQUVELGlCQUFLLFdBQUwsR0FBbUIsSUFBbkIsQ0FBd0IsV0FBeEIsQ0FBb0MsR0FBcEM7QUFDQSxtQkFBTyxXQUFQO0FBQ0g7Ozt3REFFK0Isb0IsRUFBc0I7QUFDbEQsZ0JBQUksaUJBQWlCLEdBQXJCO0FBQ0EsZ0JBQUksaUJBQUo7QUFBQSxnQkFBYyxvQkFBa0IsSUFBSSxJQUFKLEdBQVcsT0FBWCxFQUFsQixTQUEwQyxLQUFLLE1BQUwsR0FBYyxRQUFkLEdBQXlCLE1BQXpCLENBQWdDLENBQWhDLENBQXhEO0FBQ0EsZ0JBQUksY0FBSjtBQUNBLGdCQUFJLE1BQU0sS0FBSyxrQkFBTCxFQUFWO0FBQ0EsZ0JBQUksWUFBWSxJQUFJLFVBQUosQ0FBZSxDQUFmLENBQWhCOztBQUVBLG9CQUFRLEtBQUssV0FBTCxHQUFtQixXQUFuQixFQUFSO0FBQ0Esa0JBQU0sUUFBTixDQUFlLElBQUksVUFBbkIsRUFBK0Isb0JBQS9CO0FBQ0Esa0JBQU0sTUFBTixDQUFhLElBQUksVUFBakIsRUFBNkIsb0JBQTdCOztBQUVBLGtCQUFNLFFBQU4sQ0FBZSxLQUFmOztBQUVBO0FBQ0EsdUJBQVcsS0FBSyxXQUFMLEdBQW1CLGFBQW5CLENBQWlDLE1BQWpDLENBQVg7QUFDQSxxQkFBUyxFQUFULEdBQWMsUUFBZDs7QUFFQSxxQkFBUyxXQUFULENBQXFCLEtBQUssV0FBTCxHQUFtQixjQUFuQixDQUFrQyxjQUFsQyxDQUFyQjtBQUNBLGtCQUFNLFVBQU4sQ0FBaUIsUUFBakI7QUFDQSxnQkFBSSxlQUFKO0FBQ0EsZ0JBQUksUUFBSixDQUFhLFNBQWI7O0FBRUEsZ0JBQUksT0FBTyxTQUFTLHFCQUFULEVBQVg7QUFDQSxnQkFBSSxNQUFNLFNBQVMsZUFBbkI7QUFDQSxnQkFBSSxhQUFhLENBQUMsT0FBTyxXQUFQLElBQXNCLElBQUksVUFBM0IsS0FBMEMsSUFBSSxVQUFKLElBQWtCLENBQTVELENBQWpCO0FBQ0EsZ0JBQUksWUFBWSxDQUFDLE9BQU8sV0FBUCxJQUFzQixJQUFJLFNBQTNCLEtBQXlDLElBQUksU0FBSixJQUFpQixDQUExRCxDQUFoQjtBQUNBLGdCQUFJLGNBQWM7QUFDZCxzQkFBTSxLQUFLLElBQUwsR0FBWSxVQURKO0FBRWQscUJBQUssS0FBSyxHQUFMLEdBQVcsU0FBUyxZQUFwQixHQUFtQztBQUYxQixhQUFsQjtBQUlBLGdCQUFJLGNBQWMsT0FBTyxVQUF6QjtBQUNBLGdCQUFJLGVBQWUsT0FBTyxXQUExQjs7QUFFQSxnQkFBSSxpQkFBaUIsS0FBSyxpQkFBTCxFQUFyQjtBQUNBLGdCQUFJLGtCQUFrQixLQUFLLGVBQUwsQ0FBcUIsV0FBckIsRUFBa0MsY0FBbEMsQ0FBdEI7O0FBRUEsZ0JBQUksZ0JBQWdCLEtBQXBCLEVBQTJCO0FBQ3ZCLDRCQUFZLElBQVosR0FBbUIsTUFBbkI7QUFDQSw0QkFBWSxLQUFaLEdBQW9CLGNBQWMsS0FBSyxJQUFuQixHQUEwQixVQUE5QztBQUNIOztBQUVELGdCQUFJLGVBQWUsS0FBSyxPQUFMLENBQWEsYUFBYixHQUNiLEtBQUssT0FBTCxDQUFhLGFBQWIsQ0FBMkIsWUFEZCxHQUViLEtBQUssV0FBTCxHQUFtQixJQUFuQixDQUF3QixZQUY5Qjs7QUFJQSxnQkFBSSxnQkFBZ0IsTUFBcEIsRUFBNEI7QUFDeEIsb0JBQUksYUFBYSxLQUFLLE9BQUwsQ0FBYSxhQUFiLEdBQ1gsS0FBSyxPQUFMLENBQWEsYUFBYixDQUEyQixxQkFBM0IsRUFEVyxHQUVYLEtBQUssV0FBTCxHQUFtQixJQUFuQixDQUF3QixxQkFBeEIsRUFGTjtBQUdBLG9CQUFJLHVCQUF1QixnQkFBZ0IsZUFBZSxXQUFXLEdBQTFDLENBQTNCOztBQUVBLDRCQUFZLEdBQVosR0FBa0IsTUFBbEI7QUFDQSw0QkFBWSxNQUFaLEdBQXFCLHdCQUF3QixlQUFlLEtBQUssR0FBNUMsQ0FBckI7QUFDSDs7QUFFRCw4QkFBa0IsS0FBSyxlQUFMLENBQXFCLFdBQXJCLEVBQWtDLGNBQWxDLENBQWxCO0FBQ0EsZ0JBQUksZ0JBQWdCLElBQXBCLEVBQTBCO0FBQ3RCLDRCQUFZLElBQVosR0FBbUIsY0FBYyxlQUFlLEtBQTdCLEdBQ2IsYUFBYSxXQUFiLEdBQTJCLGVBQWUsS0FEN0IsR0FFYixVQUZOO0FBR0EsdUJBQU8sWUFBWSxLQUFuQjtBQUNIO0FBQ0QsZ0JBQUksZ0JBQWdCLEdBQXBCLEVBQXlCO0FBQ3JCLDRCQUFZLEdBQVosR0FBa0IsZUFBZSxlQUFlLE1BQTlCLEdBQ1osWUFBWSxZQUFaLEdBQTJCLGVBQWUsTUFEOUIsR0FFWixTQUZOO0FBR0EsdUJBQU8sWUFBWSxNQUFuQjtBQUNIOztBQUVELHFCQUFTLFVBQVQsQ0FBb0IsV0FBcEIsQ0FBZ0MsUUFBaEM7QUFDQSxtQkFBTyxXQUFQO0FBQ0g7Ozt1Q0FFYyxJLEVBQU07QUFDakIsZ0JBQUksbUJBQW1CLEVBQXZCO0FBQUEsZ0JBQ0ksbUJBREo7QUFFQSxnQkFBSSx3QkFBd0IsR0FBNUI7QUFDQSxnQkFBSSxJQUFJLEtBQUssSUFBYjs7QUFFQSxnQkFBSSxPQUFPLENBQVAsS0FBYSxXQUFqQixFQUE4Qjs7QUFFOUIsbUJBQU8sZUFBZSxTQUFmLElBQTRCLFdBQVcsTUFBWCxLQUFzQixDQUF6RCxFQUE0RDtBQUN4RCw2QkFBYSxFQUFFLHFCQUFGLEVBQWI7O0FBRUEsb0JBQUksV0FBVyxNQUFYLEtBQXNCLENBQTFCLEVBQTZCO0FBQ3pCLHdCQUFJLEVBQUUsVUFBRixDQUFhLENBQWIsQ0FBSjtBQUNBLHdCQUFJLE1BQU0sU0FBTixJQUFtQixDQUFDLEVBQUUscUJBQTFCLEVBQWlEO0FBQzdDO0FBQ0g7QUFDSjtBQUNKOztBQUVELGdCQUFJLFVBQVUsV0FBVyxHQUF6QjtBQUNBLGdCQUFJLGFBQWEsVUFBVSxXQUFXLE1BQXRDOztBQUVBLGdCQUFJLFVBQVUsQ0FBZCxFQUFpQjtBQUNiLHVCQUFPLFFBQVAsQ0FBZ0IsQ0FBaEIsRUFBbUIsT0FBTyxXQUFQLEdBQXFCLFdBQVcsR0FBaEMsR0FBc0MsZ0JBQXpEO0FBQ0gsYUFGRCxNQUVPLElBQUksYUFBYSxPQUFPLFdBQXhCLEVBQXFDO0FBQ3hDLG9CQUFJLE9BQU8sT0FBTyxXQUFQLEdBQXFCLFdBQVcsR0FBaEMsR0FBc0MsZ0JBQWpEOztBQUVBLG9CQUFJLE9BQU8sT0FBTyxXQUFkLEdBQTRCLHFCQUFoQyxFQUF1RDtBQUNuRCwyQkFBTyxPQUFPLFdBQVAsR0FBcUIscUJBQTVCO0FBQ0g7O0FBRUQsb0JBQUksVUFBVSxPQUFPLFdBQVAsSUFBc0IsT0FBTyxXQUFQLEdBQXFCLFVBQTNDLENBQWQ7O0FBRUEsb0JBQUksVUFBVSxJQUFkLEVBQW9CO0FBQ2hCLDhCQUFVLElBQVY7QUFDSDs7QUFFRCx1QkFBTyxRQUFQLENBQWdCLENBQWhCLEVBQW1CLE9BQW5CO0FBQ0g7QUFDSjs7Ozs7O2tCQUlVLFk7Ozs7Ozs7Ozs7Ozs7O0FDN25CZjtJQUNNLGE7QUFDRiwyQkFBWSxPQUFaLEVBQXFCO0FBQUE7O0FBQ2pCLGFBQUssT0FBTCxHQUFlLE9BQWY7QUFDQSxhQUFLLE9BQUwsQ0FBYSxNQUFiLEdBQXNCLElBQXRCO0FBQ0g7Ozs7cUNBRVksTyxFQUFTLEssRUFBTztBQUFBOztBQUN6QixtQkFBTyxNQUFNLE1BQU4sQ0FBYSxrQkFBVTtBQUMxQix1QkFBTyxNQUFLLElBQUwsQ0FBVSxPQUFWLEVBQW1CLE1BQW5CLENBQVA7QUFDSCxhQUZNLENBQVA7QUFHSDs7OzZCQUVJLE8sRUFBUyxNLEVBQVE7QUFDbEIsbUJBQU8sS0FBSyxLQUFMLENBQVcsT0FBWCxFQUFvQixNQUFwQixNQUFnQyxJQUF2QztBQUNIOzs7OEJBRUssTyxFQUFTLE0sRUFBUSxJLEVBQU07QUFDekIsbUJBQU8sUUFBUSxFQUFmO0FBQ0EsZ0JBQUksYUFBYSxDQUFqQjtBQUFBLGdCQUNJLFNBQVMsRUFEYjtBQUFBLGdCQUVJLE1BQU0sT0FBTyxNQUZqQjtBQUFBLGdCQUdJLGFBQWEsQ0FIakI7QUFBQSxnQkFJSSxZQUFZLENBSmhCO0FBQUEsZ0JBS0ksTUFBTSxLQUFLLEdBQUwsSUFBWSxFQUx0QjtBQUFBLGdCQU1JLE9BQU8sS0FBSyxJQUFMLElBQWEsRUFOeEI7QUFBQSxnQkFPSSxnQkFBZ0IsS0FBSyxhQUFMLElBQXNCLE1BQXRCLElBQWdDLE9BQU8sV0FBUCxFQVBwRDtBQUFBLGdCQVFJLFdBUko7QUFBQSxnQkFRUSxvQkFSUjs7QUFVQSxzQkFBVSxLQUFLLGFBQUwsSUFBc0IsT0FBdEIsSUFBaUMsUUFBUSxXQUFSLEVBQTNDOztBQUVBLGdCQUFJLGVBQWUsS0FBSyxRQUFMLENBQWMsYUFBZCxFQUE2QixPQUE3QixFQUFzQyxDQUF0QyxFQUF5QyxDQUF6QyxFQUE0QyxFQUE1QyxDQUFuQjtBQUNBLGdCQUFJLENBQUMsWUFBTCxFQUFtQjtBQUNmLHVCQUFPLElBQVA7QUFDSDs7QUFFRCxtQkFBTztBQUNILDBCQUFVLEtBQUssTUFBTCxDQUFZLE1BQVosRUFBb0IsYUFBYSxLQUFqQyxFQUF3QyxHQUF4QyxFQUE2QyxJQUE3QyxDQURQO0FBRUgsdUJBQU8sYUFBYTtBQUZqQixhQUFQO0FBSUg7OztpQ0FFUSxNLEVBQVEsTyxFQUFTLFcsRUFBYSxZLEVBQWMsWSxFQUFjO0FBQy9EO0FBQ0EsZ0JBQUksUUFBUSxNQUFSLEtBQW1CLFlBQXZCLEVBQXFDOztBQUVqQztBQUNBLHVCQUFPO0FBQ0gsMkJBQU8sS0FBSyxjQUFMLENBQW9CLFlBQXBCLENBREo7QUFFSCwyQkFBTyxhQUFhLEtBQWI7QUFGSixpQkFBUDtBQUlIOztBQUVEO0FBQ0EsZ0JBQUksT0FBTyxNQUFQLEtBQWtCLFdBQWxCLElBQWlDLFFBQVEsTUFBUixHQUFpQixZQUFqQixHQUFnQyxPQUFPLE1BQVAsR0FBZ0IsV0FBckYsRUFBa0c7QUFDOUYsdUJBQU8sU0FBUDtBQUNIOztBQUVELGdCQUFJLElBQUksUUFBUSxZQUFSLENBQVI7QUFDQSxnQkFBSSxRQUFRLE9BQU8sT0FBUCxDQUFlLENBQWYsRUFBa0IsV0FBbEIsQ0FBWjtBQUNBLGdCQUFJLGFBQUo7QUFBQSxnQkFBVSxhQUFWOztBQUVBLG1CQUFPLFFBQVEsQ0FBQyxDQUFoQixFQUFtQjtBQUNmLDZCQUFhLElBQWIsQ0FBa0IsS0FBbEI7QUFDQSx1QkFBTyxLQUFLLFFBQUwsQ0FBYyxNQUFkLEVBQXNCLE9BQXRCLEVBQStCLFFBQVEsQ0FBdkMsRUFBMEMsZUFBZSxDQUF6RCxFQUE0RCxZQUE1RCxDQUFQO0FBQ0EsNkJBQWEsR0FBYjs7QUFFQTtBQUNBLG9CQUFJLENBQUMsSUFBTCxFQUFXO0FBQ1AsMkJBQU8sSUFBUDtBQUNIOztBQUVELG9CQUFJLENBQUMsSUFBRCxJQUFTLEtBQUssS0FBTCxHQUFhLEtBQUssS0FBL0IsRUFBc0M7QUFDbEMsMkJBQU8sSUFBUDtBQUNIOztBQUVELHdCQUFRLE9BQU8sT0FBUCxDQUFlLENBQWYsRUFBa0IsUUFBUSxDQUExQixDQUFSO0FBQ0g7O0FBRUQsbUJBQU8sSUFBUDtBQUNIOzs7dUNBRWMsWSxFQUFjO0FBQ3pCLGdCQUFJLFFBQVEsQ0FBWjtBQUNBLGdCQUFJLE9BQU8sQ0FBWDs7QUFFQSx5QkFBYSxPQUFiLENBQXFCLFVBQUMsS0FBRCxFQUFRLENBQVIsRUFBYztBQUMvQixvQkFBSSxJQUFJLENBQVIsRUFBVztBQUNQLHdCQUFJLGFBQWEsSUFBSSxDQUFqQixJQUFzQixDQUF0QixLQUE0QixLQUFoQyxFQUF1QztBQUNuQyxnQ0FBUSxPQUFPLENBQWY7QUFDSCxxQkFGRCxNQUdLO0FBQ0QsK0JBQU8sQ0FBUDtBQUNIO0FBQ0o7O0FBRUQseUJBQVMsSUFBVDtBQUNILGFBWEQ7O0FBYUEsbUJBQU8sS0FBUDtBQUNIOzs7K0JBRU0sTSxFQUFRLE8sRUFBUyxHLEVBQUssSSxFQUFNO0FBQy9CLGdCQUFJLFdBQVcsT0FBTyxTQUFQLENBQWlCLENBQWpCLEVBQW9CLFFBQVEsQ0FBUixDQUFwQixDQUFmOztBQUVBLG9CQUFRLE9BQVIsQ0FBZ0IsVUFBQyxLQUFELEVBQVEsQ0FBUixFQUFjO0FBQzFCLDRCQUFZLE1BQU0sT0FBTyxLQUFQLENBQU4sR0FBc0IsSUFBdEIsR0FDUixPQUFPLFNBQVAsQ0FBaUIsUUFBUSxDQUF6QixFQUE2QixRQUFRLElBQUksQ0FBWixDQUFELEdBQW1CLFFBQVEsSUFBSSxDQUFaLENBQW5CLEdBQW9DLE9BQU8sTUFBdkUsQ0FESjtBQUVILGFBSEQ7O0FBS0EsbUJBQU8sUUFBUDtBQUNIOzs7K0JBRU0sTyxFQUFTLEcsRUFBSyxJLEVBQU07QUFBQTs7QUFDdkIsbUJBQU8sUUFBUSxFQUFmO0FBQ0EsbUJBQU8sSUFDRixNQURFLENBQ0ssVUFBQyxJQUFELEVBQU8sT0FBUCxFQUFnQixHQUFoQixFQUFxQixHQUFyQixFQUE2QjtBQUNqQyxvQkFBSSxNQUFNLE9BQVY7O0FBRUEsb0JBQUksS0FBSyxPQUFULEVBQWtCO0FBQ2QsMEJBQU0sS0FBSyxPQUFMLENBQWEsT0FBYixDQUFOOztBQUVBLHdCQUFJLENBQUMsR0FBTCxFQUFVO0FBQUU7QUFDUiw4QkFBTSxFQUFOO0FBQ0g7QUFDSjs7QUFFRCxvQkFBSSxXQUFXLE9BQUssS0FBTCxDQUFXLE9BQVgsRUFBb0IsR0FBcEIsRUFBeUIsSUFBekIsQ0FBZjs7QUFFQSxvQkFBSSxZQUFZLElBQWhCLEVBQXNCO0FBQ2xCLHlCQUFLLEtBQUssTUFBVixJQUFvQjtBQUNoQixnQ0FBUSxTQUFTLFFBREQ7QUFFaEIsK0JBQU8sU0FBUyxLQUZBO0FBR2hCLCtCQUFPLEdBSFM7QUFJaEIsa0NBQVU7QUFKTSxxQkFBcEI7QUFNSDs7QUFFRCx1QkFBTyxJQUFQO0FBQ0gsYUF4QkUsRUF3QkEsRUF4QkEsRUEwQk4sSUExQk0sQ0EwQkQsVUFBQyxDQUFELEVBQUksQ0FBSixFQUFVO0FBQ1osb0JBQUksVUFBVSxFQUFFLEtBQUYsR0FBVSxFQUFFLEtBQTFCO0FBQ0Esb0JBQUksT0FBSixFQUFhLE9BQU8sT0FBUDtBQUNiLHVCQUFPLEVBQUUsS0FBRixHQUFVLEVBQUUsS0FBbkI7QUFDSCxhQTlCTSxDQUFQO0FBK0JIOzs7Ozs7a0JBR1UsYTs7Ozs7Ozs7OztBQ2hKZjs7Ozs7O2tCQUVlLGlCLEVBUGY7Ozs7Ozs7Ozs7QUNBQSxJQUFJLENBQUMsTUFBTSxTQUFOLENBQWdCLElBQXJCLEVBQTJCO0FBQ3ZCLFVBQU0sU0FBTixDQUFnQixJQUFoQixHQUF1QixVQUFTLFNBQVQsRUFBb0I7QUFDdkMsWUFBSSxTQUFTLElBQWIsRUFBbUI7QUFDZixrQkFBTSxJQUFJLFNBQUosQ0FBYyxrREFBZCxDQUFOO0FBQ0g7QUFDRCxZQUFJLE9BQU8sU0FBUCxLQUFxQixVQUF6QixFQUFxQztBQUNqQyxrQkFBTSxJQUFJLFNBQUosQ0FBYyw4QkFBZCxDQUFOO0FBQ0g7QUFDRCxZQUFJLE9BQU8sT0FBTyxJQUFQLENBQVg7QUFDQSxZQUFJLFNBQVMsS0FBSyxNQUFMLEtBQWdCLENBQTdCO0FBQ0EsWUFBSSxVQUFVLFVBQVUsQ0FBVixDQUFkO0FBQ0EsWUFBSSxLQUFKOztBQUVBLGFBQUssSUFBSSxJQUFJLENBQWIsRUFBZ0IsSUFBSSxNQUFwQixFQUE0QixHQUE1QixFQUFpQztBQUM3QixvQkFBUSxLQUFLLENBQUwsQ0FBUjtBQUNBLGdCQUFJLFVBQVUsSUFBVixDQUFlLE9BQWYsRUFBd0IsS0FBeEIsRUFBK0IsQ0FBL0IsRUFBa0MsSUFBbEMsQ0FBSixFQUE2QztBQUN6Qyx1QkFBTyxLQUFQO0FBQ0g7QUFDSjtBQUNELGVBQU8sU0FBUDtBQUNILEtBbkJEO0FBb0JIOztBQUVELElBQUksVUFBVSxPQUFPLE9BQU8sV0FBZCxLQUE4QixVQUE1QyxFQUF3RDtBQUFBLFFBQzdDLFdBRDZDLEdBQ3RELFNBQVMsV0FBVCxDQUFxQixLQUFyQixFQUE0QixNQUE1QixFQUFvQztBQUNsQyxpQkFBUyxVQUFVO0FBQ2pCLHFCQUFTLEtBRFE7QUFFakIsd0JBQVksS0FGSztBQUdqQixvQkFBUTtBQUhTLFNBQW5CO0FBS0EsWUFBSSxNQUFNLFNBQVMsV0FBVCxDQUFxQixhQUFyQixDQUFWO0FBQ0EsWUFBSSxlQUFKLENBQW9CLEtBQXBCLEVBQTJCLE9BQU8sT0FBbEMsRUFBMkMsT0FBTyxVQUFsRCxFQUE4RCxPQUFPLE1BQXJFO0FBQ0EsZUFBTyxHQUFQO0FBQ0QsS0FWcUQ7O0FBWXZELFFBQUksT0FBTyxPQUFPLEtBQWQsS0FBd0IsV0FBNUIsRUFBeUM7QUFDdkMsb0JBQVksU0FBWixHQUF3QixPQUFPLEtBQVAsQ0FBYSxTQUFyQztBQUNEOztBQUVBLFdBQU8sV0FBUCxHQUFxQixXQUFyQjtBQUNEIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiByKGUsbix0KXtmdW5jdGlvbiBvKGksZil7aWYoIW5baV0pe2lmKCFlW2ldKXt2YXIgYz1cImZ1bmN0aW9uXCI9PXR5cGVvZiByZXF1aXJlJiZyZXF1aXJlO2lmKCFmJiZjKXJldHVybiBjKGksITApO2lmKHUpcmV0dXJuIHUoaSwhMCk7dmFyIGE9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitpK1wiJ1wiKTt0aHJvdyBhLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsYX12YXIgcD1uW2ldPXtleHBvcnRzOnt9fTtlW2ldWzBdLmNhbGwocC5leHBvcnRzLGZ1bmN0aW9uKHIpe3ZhciBuPWVbaV1bMV1bcl07cmV0dXJuIG8obnx8cil9LHAscC5leHBvcnRzLHIsZSxuLHQpfXJldHVybiBuW2ldLmV4cG9ydHN9Zm9yKHZhciB1PVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmUsaT0wO2k8dC5sZW5ndGg7aSsrKW8odFtpXSk7cmV0dXJuIG99cmV0dXJuIHJ9KSgpIiwiaW1wb3J0IFRyaWJ1dGVVdGlscyBmcm9tIFwiLi91dGlsc1wiO1xuaW1wb3J0IFRyaWJ1dGVFdmVudHMgZnJvbSBcIi4vVHJpYnV0ZUV2ZW50c1wiO1xuaW1wb3J0IFRyaWJ1dGVNZW51RXZlbnRzIGZyb20gXCIuL1RyaWJ1dGVNZW51RXZlbnRzXCI7XG5pbXBvcnQgVHJpYnV0ZVJhbmdlIGZyb20gXCIuL1RyaWJ1dGVSYW5nZVwiO1xuaW1wb3J0IFRyaWJ1dGVTZWFyY2ggZnJvbSBcIi4vVHJpYnV0ZVNlYXJjaFwiO1xuXG5jbGFzcyBUcmlidXRlIHtcbiAgICBjb25zdHJ1Y3Rvcih7XG4gICAgICAgIHZhbHVlcyA9IG51bGwsXG4gICAgICAgIGlmcmFtZSA9IG51bGwsXG4gICAgICAgIHNlbGVjdENsYXNzID0gJ2hpZ2hsaWdodCcsXG4gICAgICAgIHRyaWdnZXIgPSAnQCcsXG4gICAgICAgIHNlbGVjdFRlbXBsYXRlID0gbnVsbCxcbiAgICAgICAgbWVudUl0ZW1UZW1wbGF0ZSA9IG51bGwsXG4gICAgICAgIGxvb2t1cCA9ICdrZXknLFxuICAgICAgICBmaWxsQXR0ciA9ICd2YWx1ZScsXG4gICAgICAgIGNvbGxlY3Rpb24gPSBudWxsLFxuICAgICAgICBtZW51Q29udGFpbmVyID0gbnVsbCxcbiAgICAgICAgbm9NYXRjaFRlbXBsYXRlID0gbnVsbCxcbiAgICAgICAgcmVxdWlyZUxlYWRpbmdTcGFjZSA9IHRydWUsXG4gICAgICAgIGFsbG93U3BhY2VzID0gZmFsc2UsXG4gICAgICAgIHJlcGxhY2VUZXh0U3VmZml4ID0gbnVsbCxcbiAgICAgICAgcG9zaXRpb25NZW51ID0gdHJ1ZSxcbiAgICAgICAgc3BhY2VTZWxlY3RzTWF0Y2ggPSBmYWxzZSxcbiAgICB9KSB7XG5cbiAgICAgICAgdGhpcy5tZW51U2VsZWN0ZWQgPSAwXG4gICAgICAgIHRoaXMuY3VycmVudCA9IHt9XG4gICAgICAgIHRoaXMuaW5wdXRFdmVudCA9IGZhbHNlXG4gICAgICAgIHRoaXMuaXNBY3RpdmUgPSBmYWxzZVxuICAgICAgICB0aGlzLm1lbnVDb250YWluZXIgPSBtZW51Q29udGFpbmVyXG4gICAgICAgIHRoaXMuYWxsb3dTcGFjZXMgPSBhbGxvd1NwYWNlc1xuICAgICAgICB0aGlzLnJlcGxhY2VUZXh0U3VmZml4ID0gcmVwbGFjZVRleHRTdWZmaXhcbiAgICAgICAgdGhpcy5wb3NpdGlvbk1lbnUgPSBwb3NpdGlvbk1lbnVcbiAgICAgICAgdGhpcy5oYXNUcmFpbGluZ1NwYWNlID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3BhY2VTZWxlY3RzTWF0Y2ggPSBzcGFjZVNlbGVjdHNNYXRjaDtcblxuICAgICAgICBpZiAodmFsdWVzKSB7XG4gICAgICAgICAgICB0aGlzLmNvbGxlY3Rpb24gPSBbe1xuICAgICAgICAgICAgICAgIC8vIHN5bWJvbCB0aGF0IHN0YXJ0cyB0aGUgbG9va3VwXG4gICAgICAgICAgICAgICAgdHJpZ2dlcjogdHJpZ2dlcixcblxuICAgICAgICAgICAgICAgIC8vIGlzIGl0IHdyYXBwZWQgaW4gYW4gaWZyYW1lXG4gICAgICAgICAgICAgICAgaWZyYW1lOiBpZnJhbWUsXG5cbiAgICAgICAgICAgICAgICAvLyBjbGFzcyBhcHBsaWVkIHRvIHNlbGVjdGVkIGl0ZW1cbiAgICAgICAgICAgICAgICBzZWxlY3RDbGFzczogc2VsZWN0Q2xhc3MsXG5cbiAgICAgICAgICAgICAgICAvLyBmdW5jdGlvbiBjYWxsZWQgb24gc2VsZWN0IHRoYXQgcmV0dW5zIHRoZSBjb250ZW50IHRvIGluc2VydFxuICAgICAgICAgICAgICAgIHNlbGVjdFRlbXBsYXRlOiAoc2VsZWN0VGVtcGxhdGUgfHwgVHJpYnV0ZS5kZWZhdWx0U2VsZWN0VGVtcGxhdGUpLmJpbmQodGhpcyksXG5cbiAgICAgICAgICAgICAgICAvLyBmdW5jdGlvbiBjYWxsZWQgdGhhdCByZXR1cm5zIGNvbnRlbnQgZm9yIGFuIGl0ZW1cbiAgICAgICAgICAgICAgICBtZW51SXRlbVRlbXBsYXRlOiAobWVudUl0ZW1UZW1wbGF0ZSB8fCBUcmlidXRlLmRlZmF1bHRNZW51SXRlbVRlbXBsYXRlKS5iaW5kKHRoaXMpLFxuXG4gICAgICAgICAgICAgICAgLy8gZnVuY3Rpb24gY2FsbGVkIHdoZW4gbWVudSBpcyBlbXB0eSwgZGlzYWJsZXMgaGlkaW5nIG9mIG1lbnUuXG4gICAgICAgICAgICAgICAgbm9NYXRjaFRlbXBsYXRlOiAodCA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHQuYmluZCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vTWF0Y2hUZW1wbGF0ZVxuICAgICAgICAgICAgICAgIH0pKG5vTWF0Y2hUZW1wbGF0ZSksXG5cbiAgICAgICAgICAgICAgICAvLyBjb2x1bW4gdG8gc2VhcmNoIGFnYWluc3QgaW4gdGhlIG9iamVjdFxuICAgICAgICAgICAgICAgIGxvb2t1cDogbG9va3VwLFxuXG4gICAgICAgICAgICAgICAgLy8gY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIGNvbnRlbnQgdG8gaW5zZXJ0IGJ5IGRlZmF1bHRcbiAgICAgICAgICAgICAgICBmaWxsQXR0cjogZmlsbEF0dHIsXG5cbiAgICAgICAgICAgICAgICAvLyBhcnJheSBvZiBvYmplY3RzIG9yIGEgZnVuY3Rpb24gcmV0dXJuaW5nIGFuIGFycmF5IG9mIG9iamVjdHNcbiAgICAgICAgICAgICAgICB2YWx1ZXM6IHZhbHVlcyxcblxuICAgICAgICAgICAgICAgIHJlcXVpcmVMZWFkaW5nU3BhY2U6IHJlcXVpcmVMZWFkaW5nU3BhY2VcbiAgICAgICAgICAgIH1dXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoY29sbGVjdGlvbikge1xuICAgICAgICAgICAgdGhpcy5jb2xsZWN0aW9uID0gY29sbGVjdGlvbi5tYXAoaXRlbSA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgdHJpZ2dlcjogaXRlbS50cmlnZ2VyIHx8IHRyaWdnZXIsXG4gICAgICAgICAgICAgICAgICAgIGlmcmFtZTogaXRlbS5pZnJhbWUgfHwgaWZyYW1lLFxuICAgICAgICAgICAgICAgICAgICBzZWxlY3RDbGFzczogaXRlbS5zZWxlY3RDbGFzcyB8fCBzZWxlY3RDbGFzcyxcbiAgICAgICAgICAgICAgICAgICAgc2VsZWN0VGVtcGxhdGU6IChpdGVtLnNlbGVjdFRlbXBsYXRlIHx8IFRyaWJ1dGUuZGVmYXVsdFNlbGVjdFRlbXBsYXRlKS5iaW5kKHRoaXMpLFxuICAgICAgICAgICAgICAgICAgICBtZW51SXRlbVRlbXBsYXRlOiAoaXRlbS5tZW51SXRlbVRlbXBsYXRlIHx8IFRyaWJ1dGUuZGVmYXVsdE1lbnVJdGVtVGVtcGxhdGUpLmJpbmQodGhpcyksXG4gICAgICAgICAgICAgICAgICAgIC8vIGZ1bmN0aW9uIGNhbGxlZCB3aGVuIG1lbnUgaXMgZW1wdHksIGRpc2FibGVzIGhpZGluZyBvZiBtZW51LlxuICAgICAgICAgICAgICAgICAgICBub01hdGNoVGVtcGxhdGU6ICh0ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0LmJpbmQodGhpcylcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgICAgICAgICAgICAgfSkobm9NYXRjaFRlbXBsYXRlKSxcbiAgICAgICAgICAgICAgICAgICAgbG9va3VwOiBpdGVtLmxvb2t1cCB8fCBsb29rdXAsXG4gICAgICAgICAgICAgICAgICAgIGZpbGxBdHRyOiBpdGVtLmZpbGxBdHRyIHx8IGZpbGxBdHRyLFxuICAgICAgICAgICAgICAgICAgICB2YWx1ZXM6IGl0ZW0udmFsdWVzLFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlTGVhZGluZ1NwYWNlOiBpdGVtLnJlcXVpcmVMZWFkaW5nU3BhY2VcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdbVHJpYnV0ZV0gTm8gY29sbGVjdGlvbiBzcGVjaWZpZWQuJylcbiAgICAgICAgfVxuXG4gICAgICAgIG5ldyBUcmlidXRlUmFuZ2UodGhpcylcbiAgICAgICAgbmV3IFRyaWJ1dGVFdmVudHModGhpcylcbiAgICAgICAgbmV3IFRyaWJ1dGVNZW51RXZlbnRzKHRoaXMpXG4gICAgICAgIG5ldyBUcmlidXRlU2VhcmNoKHRoaXMpXG4gICAgfVxuXG4gICAgc3RhdGljIGRlZmF1bHRTZWxlY3RUZW1wbGF0ZShpdGVtKSB7XG4gICAgICBpZiAodHlwZW9mIGl0ZW0gPT09ICd1bmRlZmluZWQnKSByZXR1cm4gbnVsbDtcbiAgICAgIGlmICh0aGlzLnJhbmdlLmlzQ29udGVudEVkaXRhYmxlKHRoaXMuY3VycmVudC5lbGVtZW50KSkge1xuICAgICAgICAgIHJldHVybiAnPHNwYW4gY2xhc3M9XCJ0cmlidXRlLW1lbnRpb25cIj4nICsgKHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLnRyaWdnZXIgKyBpdGVtLm9yaWdpbmFsW3RoaXMuY3VycmVudC5jb2xsZWN0aW9uLmZpbGxBdHRyXSkgKyAnPC9zcGFuPic7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0aGlzLmN1cnJlbnQuY29sbGVjdGlvbi50cmlnZ2VyICsgaXRlbS5vcmlnaW5hbFt0aGlzLmN1cnJlbnQuY29sbGVjdGlvbi5maWxsQXR0cl07XG4gICAgfVxuXG4gICAgc3RhdGljIGRlZmF1bHRNZW51SXRlbVRlbXBsYXRlKG1hdGNoSXRlbSkge1xuICAgICAgICByZXR1cm4gbWF0Y2hJdGVtLnN0cmluZ1xuICAgIH1cblxuICAgIHN0YXRpYyBpbnB1dFR5cGVzKCkge1xuICAgICAgICByZXR1cm4gWydURVhUQVJFQScsICdJTlBVVCddXG4gICAgfVxuXG4gICAgdHJpZ2dlcnMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbGxlY3Rpb24ubWFwKGNvbmZpZyA9PiB7XG4gICAgICAgICAgICByZXR1cm4gY29uZmlnLnRyaWdnZXJcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBhdHRhY2goZWwpIHtcbiAgICAgICAgaWYgKCFlbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdbVHJpYnV0ZV0gTXVzdCBwYXNzIGluIGEgRE9NIG5vZGUgb3IgTm9kZUxpc3QuJylcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgalF1ZXJ5IGNvbGxlY3Rpb25cbiAgICAgICAgaWYgKHR5cGVvZiBqUXVlcnkgIT09ICd1bmRlZmluZWQnICYmIGVsIGluc3RhbmNlb2YgalF1ZXJ5KSB7XG4gICAgICAgICAgICBlbCA9IGVsLmdldCgpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBJcyBlbCBhbiBBcnJheS9BcnJheS1saWtlIG9iamVjdD9cbiAgICAgICAgaWYgKGVsLmNvbnN0cnVjdG9yID09PSBOb2RlTGlzdCB8fCBlbC5jb25zdHJ1Y3RvciA9PT0gSFRNTENvbGxlY3Rpb24gfHwgZWwuY29uc3RydWN0b3IgPT09IEFycmF5KSB7XG4gICAgICAgICAgICBsZXQgbGVuZ3RoID0gZWwubGVuZ3RoXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fYXR0YWNoKGVsW2ldKVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fYXR0YWNoKGVsKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgX2F0dGFjaChlbCkge1xuICAgICAgICBpZiAoZWwuaGFzQXR0cmlidXRlKCdkYXRhLXRyaWJ1dGUnKSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdUcmlidXRlIHdhcyBhbHJlYWR5IGJvdW5kIHRvICcgKyBlbC5ub2RlTmFtZSlcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZW5zdXJlRWRpdGFibGUoZWwpXG4gICAgICAgIHRoaXMuZXZlbnRzLmJpbmQoZWwpXG4gICAgICAgIGVsLnNldEF0dHJpYnV0ZSgnZGF0YS10cmlidXRlJywgdHJ1ZSlcbiAgICB9XG5cbiAgICBlbnN1cmVFZGl0YWJsZShlbGVtZW50KSB7XG4gICAgICAgIGlmIChUcmlidXRlLmlucHV0VHlwZXMoKS5pbmRleE9mKGVsZW1lbnQubm9kZU5hbWUpID09PSAtMSkge1xuICAgICAgICAgICAgaWYgKGVsZW1lbnQuY29udGVudEVkaXRhYmxlKSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudC5jb250ZW50RWRpdGFibGUgPSB0cnVlXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignW1RyaWJ1dGVdIENhbm5vdCBiaW5kIHRvICcgKyBlbGVtZW50Lm5vZGVOYW1lKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgY3JlYXRlTWVudSgpIHtcbiAgICAgICAgbGV0IHdyYXBwZXIgPSB0aGlzLnJhbmdlLmdldERvY3VtZW50KCkuY3JlYXRlRWxlbWVudCgnZGl2JyksXG4gICAgICAgICAgICB1bCA9IHRoaXMucmFuZ2UuZ2V0RG9jdW1lbnQoKS5jcmVhdGVFbGVtZW50KCd1bCcpXG5cbiAgICAgICAgd3JhcHBlci5jbGFzc05hbWUgPSAndHJpYnV0ZS1jb250YWluZXInXG4gICAgICAgIHdyYXBwZXIuYXBwZW5kQ2hpbGQodWwpXG5cbiAgICAgICAgaWYgKHRoaXMubWVudUNvbnRhaW5lcikge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMubWVudUNvbnRhaW5lci5hcHBlbmRDaGlsZCh3cmFwcGVyKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXMucmFuZ2UuZ2V0RG9jdW1lbnQoKS5ib2R5LmFwcGVuZENoaWxkKHdyYXBwZXIpXG4gICAgfVxuXG4gICAgc2hvd01lbnVGb3IoZWxlbWVudCwgc2Nyb2xsVG8pIHtcbiAgICAgICAgLy8gT25seSBwcm9jZWVkIGlmIG1lbnUgaXNuJ3QgYWxyZWFkeSBzaG93biBmb3IgdGhlIGN1cnJlbnQgZWxlbWVudCAmIG1lbnRpb25UZXh0XG4gICAgICAgIGlmICh0aGlzLmlzQWN0aXZlICYmIHRoaXMuY3VycmVudC5lbGVtZW50ID09PSBlbGVtZW50ICYmIHRoaXMuY3VycmVudC5tZW50aW9uVGV4dCA9PT0gdGhpcy5jdXJyZW50TWVudGlvblRleHRTbmFwc2hvdCkge1xuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIHRoaXMuY3VycmVudE1lbnRpb25UZXh0U25hcHNob3QgPSB0aGlzLmN1cnJlbnQubWVudGlvblRleHRcblxuICAgICAgICAvLyBjcmVhdGUgdGhlIG1lbnUgaWYgaXQgZG9lc24ndCBleGlzdC5cbiAgICAgICAgaWYgKCF0aGlzLm1lbnUpIHtcbiAgICAgICAgICAgIHRoaXMubWVudSA9IHRoaXMuY3JlYXRlTWVudSgpXG4gICAgICAgICAgICBlbGVtZW50LnRyaWJ1dGVNZW51ID0gdGhpcy5tZW51XG4gICAgICAgICAgICB0aGlzLm1lbnVFdmVudHMuYmluZCh0aGlzLm1lbnUpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmlzQWN0aXZlID0gdHJ1ZVxuICAgICAgICB0aGlzLm1lbnVTZWxlY3RlZCA9IDBcblxuICAgICAgICBpZiAoIXRoaXMuY3VycmVudC5tZW50aW9uVGV4dCkge1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50Lm1lbnRpb25UZXh0ID0gJydcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHByb2Nlc3NWYWx1ZXMgPSAodmFsdWVzKSA9PiB7XG4gICAgICAgICAgICAvLyBUcmlidXRlIG1heSBub3QgYmUgYWN0aXZlIGFueSBtb3JlIGJ5IHRoZSB0aW1lIHRoZSB2YWx1ZSBjYWxsYmFjayByZXR1cm5zXG4gICAgICAgICAgICBpZiAoIXRoaXMuaXNBY3RpdmUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGV0IGl0ZW1zID0gdGhpcy5zZWFyY2guZmlsdGVyKHRoaXMuY3VycmVudC5tZW50aW9uVGV4dCwgdmFsdWVzLCB7XG4gICAgICAgICAgICAgICAgcHJlOiAnPHNwYW4+JyxcbiAgICAgICAgICAgICAgICBwb3N0OiAnPC9zcGFuPicsXG4gICAgICAgICAgICAgICAgZXh0cmFjdDogKGVsKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdGhpcy5jdXJyZW50LmNvbGxlY3Rpb24ubG9va3VwID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGVsW3RoaXMuY3VycmVudC5jb2xsZWN0aW9uLmxvb2t1cF1cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdGhpcy5jdXJyZW50LmNvbGxlY3Rpb24ubG9va3VwID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5jdXJyZW50LmNvbGxlY3Rpb24ubG9va3VwKGVsLCB0aGlzLmN1cnJlbnQubWVudGlvblRleHQpXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgbG9va3VwIGF0dHJpYnV0ZSwgbG9va3VwIG11c3QgYmUgc3RyaW5nIG9yIGZ1bmN0aW9uLicpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICB0aGlzLmN1cnJlbnQuZmlsdGVyZWRJdGVtcyA9IGl0ZW1zXG5cblxuICAgICAgICAgICAgbGV0IHVsID0gdGhpcy5tZW51LnF1ZXJ5U2VsZWN0b3IoJ3VsJylcblxuICAgICAgICAgICAgdGhpcy5yYW5nZS5wb3NpdGlvbk1lbnVBdENhcmV0KHNjcm9sbFRvKVxuXG4gICAgICAgICAgICBpZiAoIWl0ZW1zLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGxldCBub01hdGNoRXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3RyaWJ1dGUtbm8tbWF0Y2gnLCB7IGRldGFpbDogdGhpcy5tZW51IH0pXG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50LmVsZW1lbnQuZGlzcGF0Y2hFdmVudChub01hdGNoRXZlbnQpXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmN1cnJlbnQuY29sbGVjdGlvbi5ub01hdGNoVGVtcGxhdGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oaWRlTWVudSgpXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdWwuaW5uZXJIVE1MID0gdGhpcy5jdXJyZW50LmNvbGxlY3Rpb24ubm9NYXRjaFRlbXBsYXRlKClcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdWwuaW5uZXJIVE1MID0gJydcblxuICAgICAgICAgICAgaXRlbXMuZm9yRWFjaCgoaXRlbSwgaW5kZXgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgbGkgPSB0aGlzLnJhbmdlLmdldERvY3VtZW50KCkuY3JlYXRlRWxlbWVudCgnbGknKVxuICAgICAgICAgICAgICAgIGxpLnNldEF0dHJpYnV0ZSgnZGF0YS1pbmRleCcsIGluZGV4KVxuICAgICAgICAgICAgICAgIGxpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZW50ZXInLCAoZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgbGV0IGxpID0gZS50YXJnZXQ7XG4gICAgICAgICAgICAgICAgICBsZXQgaW5kZXggPSBsaS5nZXRBdHRyaWJ1dGUoJ2RhdGEtaW5kZXgnKVxuICAgICAgICAgICAgICAgICAgdGhpcy5ldmVudHMuc2V0QWN0aXZlTGkoaW5kZXgpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5tZW51U2VsZWN0ZWQgPT09IGluZGV4KSB7XG4gICAgICAgICAgICAgICAgICAgIGxpLmNsYXNzTmFtZSA9IHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLnNlbGVjdENsYXNzXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxpLmlubmVySFRNTCA9IHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLm1lbnVJdGVtVGVtcGxhdGUoaXRlbSlcbiAgICAgICAgICAgICAgICB1bC5hcHBlbmRDaGlsZChsaSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodHlwZW9mIHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLnZhbHVlcyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50LmNvbGxlY3Rpb24udmFsdWVzKHRoaXMuY3VycmVudC5tZW50aW9uVGV4dCwgcHJvY2Vzc1ZhbHVlcylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHByb2Nlc3NWYWx1ZXModGhpcy5jdXJyZW50LmNvbGxlY3Rpb24udmFsdWVzKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc2hvd01lbnVGb3JDb2xsZWN0aW9uKGVsZW1lbnQsIGNvbGxlY3Rpb25JbmRleCkge1xuICAgICAgICBpZiAoZWxlbWVudCAhPT0gZG9jdW1lbnQuYWN0aXZlRWxlbWVudCkge1xuICAgICAgICAgICAgdGhpcy5wbGFjZUNhcmV0QXRFbmQoZWxlbWVudClcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuY3VycmVudC5jb2xsZWN0aW9uID0gdGhpcy5jb2xsZWN0aW9uW2NvbGxlY3Rpb25JbmRleCB8fCAwXVxuICAgICAgICB0aGlzLmN1cnJlbnQuZXh0ZXJuYWxUcmlnZ2VyID0gdHJ1ZVxuICAgICAgICB0aGlzLmN1cnJlbnQuZWxlbWVudCA9IGVsZW1lbnRcblxuICAgICAgICBpZiAoZWxlbWVudC5pc0NvbnRlbnRFZGl0YWJsZSlcbiAgICAgICAgICAgIHRoaXMuaW5zZXJ0VGV4dEF0Q3Vyc29yKHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLnRyaWdnZXIpXG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIHRoaXMuaW5zZXJ0QXRDYXJldChlbGVtZW50LCB0aGlzLmN1cnJlbnQuY29sbGVjdGlvbi50cmlnZ2VyKVxuXG4gICAgICAgIHRoaXMuc2hvd01lbnVGb3IoZWxlbWVudClcbiAgICB9XG5cbiAgICAvLyBUT0RPOiBtYWtlIHN1cmUgdGhpcyB3b3JrcyBmb3IgaW5wdXRzL3RleHRhcmVhc1xuICAgIHBsYWNlQ2FyZXRBdEVuZChlbCkge1xuICAgICAgICBlbC5mb2N1cygpO1xuICAgICAgICBpZiAodHlwZW9mIHdpbmRvdy5nZXRTZWxlY3Rpb24gIT0gXCJ1bmRlZmluZWRcIlxuICAgICAgICAgICAgICAgICYmIHR5cGVvZiBkb2N1bWVudC5jcmVhdGVSYW5nZSAhPSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICB2YXIgcmFuZ2UgPSBkb2N1bWVudC5jcmVhdGVSYW5nZSgpO1xuICAgICAgICAgICAgcmFuZ2Uuc2VsZWN0Tm9kZUNvbnRlbnRzKGVsKTtcbiAgICAgICAgICAgIHJhbmdlLmNvbGxhcHNlKGZhbHNlKTtcbiAgICAgICAgICAgIHZhciBzZWwgPSB3aW5kb3cuZ2V0U2VsZWN0aW9uKCk7XG4gICAgICAgICAgICBzZWwucmVtb3ZlQWxsUmFuZ2VzKCk7XG4gICAgICAgICAgICBzZWwuYWRkUmFuZ2UocmFuZ2UpO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkb2N1bWVudC5ib2R5LmNyZWF0ZVRleHRSYW5nZSAhPSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICB2YXIgdGV4dFJhbmdlID0gZG9jdW1lbnQuYm9keS5jcmVhdGVUZXh0UmFuZ2UoKTtcbiAgICAgICAgICAgIHRleHRSYW5nZS5tb3ZlVG9FbGVtZW50VGV4dChlbCk7XG4gICAgICAgICAgICB0ZXh0UmFuZ2UuY29sbGFwc2UoZmFsc2UpO1xuICAgICAgICAgICAgdGV4dFJhbmdlLnNlbGVjdCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gZm9yIGNvbnRlbnRlZGl0YWJsZVxuICAgIGluc2VydFRleHRBdEN1cnNvcih0ZXh0KSB7XG4gICAgICAgIHZhciBzZWwsIHJhbmdlLCBodG1sO1xuICAgICAgICBzZWwgPSB3aW5kb3cuZ2V0U2VsZWN0aW9uKCk7XG4gICAgICAgIHJhbmdlID0gc2VsLmdldFJhbmdlQXQoMCk7XG4gICAgICAgIHJhbmdlLmRlbGV0ZUNvbnRlbnRzKCk7XG4gICAgICAgIHZhciB0ZXh0Tm9kZSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRleHQpO1xuICAgICAgICByYW5nZS5pbnNlcnROb2RlKHRleHROb2RlKTtcbiAgICAgICAgcmFuZ2Uuc2VsZWN0Tm9kZUNvbnRlbnRzKHRleHROb2RlKVxuICAgICAgICByYW5nZS5jb2xsYXBzZShmYWxzZSlcbiAgICAgICAgc2VsLnJlbW92ZUFsbFJhbmdlcygpXG4gICAgICAgIHNlbC5hZGRSYW5nZShyYW5nZSlcbiAgICB9XG5cbiAgICAvLyBmb3IgcmVndWxhciBpbnB1dHNcbiAgICBpbnNlcnRBdENhcmV0KHRleHRhcmVhLCB0ZXh0KSB7XG4gICAgICAgIHZhciBzY3JvbGxQb3MgPSB0ZXh0YXJlYS5zY3JvbGxUb3A7XG4gICAgICAgIHZhciBjYXJldFBvcyA9IHRleHRhcmVhLnNlbGVjdGlvblN0YXJ0O1xuXG4gICAgICAgIHZhciBmcm9udCA9ICh0ZXh0YXJlYS52YWx1ZSkuc3Vic3RyaW5nKDAsIGNhcmV0UG9zKTtcbiAgICAgICAgdmFyIGJhY2sgPSAodGV4dGFyZWEudmFsdWUpLnN1YnN0cmluZyh0ZXh0YXJlYS5zZWxlY3Rpb25FbmQsIHRleHRhcmVhLnZhbHVlLmxlbmd0aCk7XG4gICAgICAgIHRleHRhcmVhLnZhbHVlID0gZnJvbnQgKyB0ZXh0ICsgYmFjaztcbiAgICAgICAgY2FyZXRQb3MgPSBjYXJldFBvcyArIHRleHQubGVuZ3RoO1xuICAgICAgICB0ZXh0YXJlYS5zZWxlY3Rpb25TdGFydCA9IGNhcmV0UG9zO1xuICAgICAgICB0ZXh0YXJlYS5zZWxlY3Rpb25FbmQgPSBjYXJldFBvcztcbiAgICAgICAgdGV4dGFyZWEuZm9jdXMoKTtcbiAgICAgICAgdGV4dGFyZWEuc2Nyb2xsVG9wID0gc2Nyb2xsUG9zO1xuICAgIH1cblxuICAgIGhpZGVNZW51KCkge1xuICAgICAgICBpZiAodGhpcy5tZW51KSB7XG4gICAgICAgICAgICB0aGlzLm1lbnUuc3R5bGUuY3NzVGV4dCA9ICdkaXNwbGF5OiBub25lOydcbiAgICAgICAgICAgIHRoaXMuaXNBY3RpdmUgPSBmYWxzZVxuICAgICAgICAgICAgdGhpcy5tZW51U2VsZWN0ZWQgPSAwXG4gICAgICAgICAgICB0aGlzLmN1cnJlbnQgPSB7fVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc2VsZWN0SXRlbUF0SW5kZXgoaW5kZXgsIG9yaWdpbmFsRXZlbnQpIHtcbiAgICAgICAgaW5kZXggPSBwYXJzZUludChpbmRleClcbiAgICAgICAgaWYgKHR5cGVvZiBpbmRleCAhPT0gJ251bWJlcicpIHJldHVyblxuICAgICAgICBsZXQgaXRlbSA9IHRoaXMuY3VycmVudC5maWx0ZXJlZEl0ZW1zW2luZGV4XVxuICAgICAgICBsZXQgY29udGVudCA9IHRoaXMuY3VycmVudC5jb2xsZWN0aW9uLnNlbGVjdFRlbXBsYXRlKGl0ZW0pXG4gICAgICAgIGlmIChjb250ZW50ICE9PSBudWxsKSB0aGlzLnJlcGxhY2VUZXh0KGNvbnRlbnQsIG9yaWdpbmFsRXZlbnQsIGl0ZW0pXG4gICAgfVxuXG4gICAgcmVwbGFjZVRleHQoY29udGVudCwgb3JpZ2luYWxFdmVudCwgaXRlbSkge1xuICAgICAgICB0aGlzLnJhbmdlLnJlcGxhY2VUcmlnZ2VyVGV4dChjb250ZW50LCB0cnVlLCB0cnVlLCBvcmlnaW5hbEV2ZW50LCBpdGVtKVxuICAgIH1cblxuICAgIF9hcHBlbmQoY29sbGVjdGlvbiwgbmV3VmFsdWVzLCByZXBsYWNlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgY29sbGVjdGlvbi52YWx1ZXMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIGFwcGVuZCB0byB2YWx1ZXMsIGFzIGl0IGlzIGEgZnVuY3Rpb24uJylcbiAgICAgICAgfSBlbHNlIGlmICghcmVwbGFjZSkge1xuICAgICAgICAgICAgY29sbGVjdGlvbi52YWx1ZXMgPSBjb2xsZWN0aW9uLnZhbHVlcy5jb25jYXQobmV3VmFsdWVzKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29sbGVjdGlvbi52YWx1ZXMgPSBuZXdWYWx1ZXNcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFwcGVuZChjb2xsZWN0aW9uSW5kZXgsIG5ld1ZhbHVlcywgcmVwbGFjZSkge1xuICAgICAgICBsZXQgaW5kZXggPSBwYXJzZUludChjb2xsZWN0aW9uSW5kZXgpXG4gICAgICAgIGlmICh0eXBlb2YgaW5kZXggIT09ICdudW1iZXInKSB0aHJvdyBuZXcgRXJyb3IoJ3BsZWFzZSBwcm92aWRlIGFuIGluZGV4IGZvciB0aGUgY29sbGVjdGlvbiB0byB1cGRhdGUuJylcblxuICAgICAgICBsZXQgY29sbGVjdGlvbiA9IHRoaXMuY29sbGVjdGlvbltpbmRleF1cblxuICAgICAgICB0aGlzLl9hcHBlbmQoY29sbGVjdGlvbiwgbmV3VmFsdWVzLCByZXBsYWNlKVxuICAgIH1cblxuICAgIGFwcGVuZEN1cnJlbnQobmV3VmFsdWVzLCByZXBsYWNlKSB7XG4gICAgICAgIGlmICh0aGlzLmlzQWN0aXZlKSB7XG4gICAgICAgICAgICB0aGlzLl9hcHBlbmQodGhpcy5jdXJyZW50LmNvbGxlY3Rpb24sIG5ld1ZhbHVlcywgcmVwbGFjZSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gYWN0aXZlIHN0YXRlLiBQbGVhc2UgdXNlIGFwcGVuZCBpbnN0ZWFkIGFuZCBwYXNzIGFuIGluZGV4LicpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkZXRhY2goZWwpIHtcbiAgICAgICAgaWYgKCFlbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdbVHJpYnV0ZV0gTXVzdCBwYXNzIGluIGEgRE9NIG5vZGUgb3IgTm9kZUxpc3QuJylcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgalF1ZXJ5IGNvbGxlY3Rpb25cbiAgICAgICAgaWYgKHR5cGVvZiBqUXVlcnkgIT09ICd1bmRlZmluZWQnICYmIGVsIGluc3RhbmNlb2YgalF1ZXJ5KSB7XG4gICAgICAgICAgICBlbCA9IGVsLmdldCgpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBJcyBlbCBhbiBBcnJheS9BcnJheS1saWtlIG9iamVjdD9cbiAgICAgICAgaWYgKGVsLmNvbnN0cnVjdG9yID09PSBOb2RlTGlzdCB8fCBlbC5jb25zdHJ1Y3RvciA9PT0gSFRNTENvbGxlY3Rpb24gfHwgZWwuY29uc3RydWN0b3IgPT09IEFycmF5KSB7XG4gICAgICAgICAgICBsZXQgbGVuZ3RoID0gZWwubGVuZ3RoXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fZGV0YWNoKGVsW2ldKVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fZGV0YWNoKGVsKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgX2RldGFjaChlbCkge1xuICAgICAgICB0aGlzLmV2ZW50cy51bmJpbmQoZWwpXG4gICAgICAgIGlmIChlbC50cmlidXRlTWVudSkge1xuICAgICAgICAgICAgdGhpcy5tZW51RXZlbnRzLnVuYmluZChlbC50cmlidXRlTWVudSlcbiAgICAgICAgfVxuXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgZWwucmVtb3ZlQXR0cmlidXRlKCdkYXRhLXRyaWJ1dGUnKVxuICAgICAgICAgICAgdGhpcy5pc0FjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICBpZiAoZWwudHJpYnV0ZU1lbnUpIHtcbiAgICAgICAgICAgICAgICBlbC50cmlidXRlTWVudS5yZW1vdmUoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgVHJpYnV0ZTtcbiIsImNsYXNzIFRyaWJ1dGVFdmVudHMge1xuICAgIGNvbnN0cnVjdG9yKHRyaWJ1dGUpIHtcbiAgICAgICAgdGhpcy50cmlidXRlID0gdHJpYnV0ZVxuICAgICAgICB0aGlzLnRyaWJ1dGUuZXZlbnRzID0gdGhpc1xuICAgIH1cblxuICAgIHN0YXRpYyBrZXlzKCkge1xuICAgICAgICByZXR1cm4gW3tcbiAgICAgICAgICAgIGtleTogOSxcbiAgICAgICAgICAgIHZhbHVlOiAnVEFCJ1xuICAgICAgICB9LCB7XG4gICAgICAgICAgICBrZXk6IDgsXG4gICAgICAgICAgICB2YWx1ZTogJ0RFTEVURSdcbiAgICAgICAgfSwge1xuICAgICAgICAgICAga2V5OiAxMyxcbiAgICAgICAgICAgIHZhbHVlOiAnRU5URVInXG4gICAgICAgIH0sIHtcbiAgICAgICAgICAgIGtleTogMjcsXG4gICAgICAgICAgICB2YWx1ZTogJ0VTQ0FQRSdcbiAgICAgICAgfSwge1xuICAgICAgICAgICAga2V5OiAzMixcbiAgICAgICAgICAgIHZhbHVlOiAnU1BBQ0UnXG4gICAgICAgIH0sIHtcbiAgICAgICAgICAgIGtleTogMzgsXG4gICAgICAgICAgICB2YWx1ZTogJ1VQJ1xuICAgICAgICB9LCB7XG4gICAgICAgICAgICBrZXk6IDQwLFxuICAgICAgICAgICAgdmFsdWU6ICdET1dOJ1xuICAgICAgICB9XVxuICAgIH1cblxuICAgIGJpbmQoZWxlbWVudCkge1xuICAgICAgICBlbGVtZW50LmJvdW5kS2V5ZG93biA9IHRoaXMua2V5ZG93bi5iaW5kKGVsZW1lbnQsIHRoaXMpO1xuICAgICAgICBlbGVtZW50LmJvdW5kS2V5dXAgPSB0aGlzLmtleXVwLmJpbmQoZWxlbWVudCwgdGhpcyk7XG4gICAgICAgIGVsZW1lbnQuYm91bmRJbnB1dCA9IHRoaXMuaW5wdXQuYmluZChlbGVtZW50LCB0aGlzKTtcblxuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLFxuICAgICAgICAgICAgZWxlbWVudC5ib3VuZEtleWRvd24sIGZhbHNlKVxuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2tleXVwJyxcbiAgICAgICAgICAgIGVsZW1lbnQuYm91bmRLZXl1cCwgZmFsc2UpXG4gICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLFxuICAgICAgICAgICAgZWxlbWVudC5ib3VuZElucHV0LCBmYWxzZSlcbiAgICB9XG5cbiAgICB1bmJpbmQoZWxlbWVudCkge1xuICAgICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLFxuICAgICAgICAgICAgZWxlbWVudC5ib3VuZEtleWRvd24sIGZhbHNlKVxuICAgICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2tleXVwJyxcbiAgICAgICAgICAgIGVsZW1lbnQuYm91bmRLZXl1cCwgZmFsc2UpXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignaW5wdXQnLFxuICAgICAgICAgICAgZWxlbWVudC5ib3VuZElucHV0LCBmYWxzZSlcblxuICAgICAgICBkZWxldGUgZWxlbWVudC5ib3VuZEtleWRvd25cbiAgICAgICAgZGVsZXRlIGVsZW1lbnQuYm91bmRLZXl1cFxuICAgICAgICBkZWxldGUgZWxlbWVudC5ib3VuZElucHV0XG4gICAgfVxuXG4gICAga2V5ZG93bihpbnN0YW5jZSwgZXZlbnQpIHtcbiAgICAgICAgaWYgKGluc3RhbmNlLnNob3VsZERlYWN0aXZhdGUoZXZlbnQpKSB7XG4gICAgICAgICAgICBpbnN0YW5jZS50cmlidXRlLmlzQWN0aXZlID0gZmFsc2VcbiAgICAgICAgICAgIGluc3RhbmNlLnRyaWJ1dGUuaGlkZU1lbnUoKVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGVsZW1lbnQgPSB0aGlzXG4gICAgICAgIGluc3RhbmNlLmNvbW1hbmRFdmVudCA9IGZhbHNlXG5cbiAgICAgICAgVHJpYnV0ZUV2ZW50cy5rZXlzKCkuZm9yRWFjaChvID0+IHtcbiAgICAgICAgICAgIGlmIChvLmtleSA9PT0gZXZlbnQua2V5Q29kZSkge1xuICAgICAgICAgICAgICAgIGluc3RhbmNlLmNvbW1hbmRFdmVudCA9IHRydWVcbiAgICAgICAgICAgICAgICBpbnN0YW5jZS5jYWxsYmFja3MoKVtvLnZhbHVlLnRvTG93ZXJDYXNlKCldKGV2ZW50LCBlbGVtZW50KVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIGlucHV0KGluc3RhbmNlLCBldmVudCkge1xuICAgICAgICBpbnN0YW5jZS5pbnB1dEV2ZW50ID0gdHJ1ZVxuICAgICAgICBpbnN0YW5jZS5rZXl1cC5jYWxsKHRoaXMsIGluc3RhbmNlLCBldmVudClcbiAgICB9XG5cbiAgICBjbGljayhpbnN0YW5jZSwgZXZlbnQpIHtcbiAgICAgICAgbGV0IHRyaWJ1dGUgPSBpbnN0YW5jZS50cmlidXRlXG4gICAgICAgIGlmICh0cmlidXRlLm1lbnUgJiYgdHJpYnV0ZS5tZW51LmNvbnRhaW5zKGV2ZW50LnRhcmdldCkpIHtcbiAgICAgICAgICAgIGxldCBsaSA9IGV2ZW50LnRhcmdldFxuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcbiAgICAgICAgICAgIHdoaWxlIChsaS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpICE9PSAnbGknKSB7XG4gICAgICAgICAgICAgICAgbGkgPSBsaS5wYXJlbnROb2RlXG4gICAgICAgICAgICAgICAgaWYgKCFsaSB8fCBsaSA9PT0gdHJpYnV0ZS5tZW51KSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignY2Fubm90IGZpbmQgdGhlIDxsaT4gY29udGFpbmVyIGZvciB0aGUgY2xpY2snKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRyaWJ1dGUuc2VsZWN0SXRlbUF0SW5kZXgobGkuZ2V0QXR0cmlidXRlKCdkYXRhLWluZGV4JyksIGV2ZW50KVxuICAgICAgICAgICAgdHJpYnV0ZS5oaWRlTWVudSgpXG5cbiAgICAgICAgLy8gVE9ETzogc2hvdWxkIGZpcmUgd2l0aCBleHRlcm5hbFRyaWdnZXIgYW5kIHRhcmdldCBpcyBvdXRzaWRlIG9mIG1lbnVcbiAgICAgICAgfSBlbHNlIGlmICh0cmlidXRlLmN1cnJlbnQuZWxlbWVudCAmJiAhdHJpYnV0ZS5jdXJyZW50LmV4dGVybmFsVHJpZ2dlcikge1xuICAgICAgICAgICAgdHJpYnV0ZS5jdXJyZW50LmV4dGVybmFsVHJpZ2dlciA9IGZhbHNlXG4gICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRyaWJ1dGUuaGlkZU1lbnUoKSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGtleXVwKGluc3RhbmNlLCBldmVudCkge1xuICAgICAgICBpZiAoaW5zdGFuY2UuaW5wdXRFdmVudCkge1xuICAgICAgICAgICAgaW5zdGFuY2UuaW5wdXRFdmVudCA9IGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgaW5zdGFuY2UudXBkYXRlU2VsZWN0aW9uKHRoaXMpXG5cbiAgICAgICAgaWYgKGV2ZW50LmtleUNvZGUgPT09IDI3KSByZXR1cm5cblxuICAgICAgICBpZiAoIWluc3RhbmNlLnRyaWJ1dGUuYWxsb3dTcGFjZXMgJiYgaW5zdGFuY2UudHJpYnV0ZS5oYXNUcmFpbGluZ1NwYWNlKSB7XG4gICAgICAgICAgICBpbnN0YW5jZS50cmlidXRlLmhhc1RyYWlsaW5nU3BhY2UgPSBmYWxzZTtcbiAgICAgICAgICAgIGluc3RhbmNlLmNvbW1hbmRFdmVudCA9IHRydWU7XG4gICAgICAgICAgICBpbnN0YW5jZS5jYWxsYmFja3MoKVtcInNwYWNlXCJdKGV2ZW50LCB0aGlzKTtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpbnN0YW5jZS50cmlidXRlLmlzQWN0aXZlKSB7XG4gICAgICAgICAgICBsZXQga2V5Q29kZSA9IGluc3RhbmNlLmdldEtleUNvZGUoaW5zdGFuY2UsIHRoaXMsIGV2ZW50KVxuXG4gICAgICAgICAgICBpZiAoaXNOYU4oa2V5Q29kZSkgfHwgIWtleUNvZGUpIHJldHVyblxuXG4gICAgICAgICAgICBsZXQgdHJpZ2dlciA9IGluc3RhbmNlLnRyaWJ1dGUudHJpZ2dlcnMoKS5maW5kKHRyaWdnZXIgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cmlnZ2VyLmNoYXJDb2RlQXQoMCkgPT09IGtleUNvZGVcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIGlmICh0eXBlb2YgdHJpZ2dlciAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgICAgICBpbnN0YW5jZS5jYWxsYmFja3MoKS50cmlnZ2VyQ2hhcihldmVudCwgdGhpcywgdHJpZ2dlcilcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpbnN0YW5jZS50cmlidXRlLmN1cnJlbnQudHJpZ2dlciAmJiBpbnN0YW5jZS5jb21tYW5kRXZlbnQgPT09IGZhbHNlXG4gICAgICAgICAgICB8fCBpbnN0YW5jZS50cmlidXRlLmlzQWN0aXZlICYmIGV2ZW50LmtleUNvZGUgPT09IDgpIHtcbiAgICAgICAgICBpbnN0YW5jZS50cmlidXRlLnNob3dNZW51Rm9yKHRoaXMsIHRydWUpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzaG91bGREZWFjdGl2YXRlKGV2ZW50KSB7XG4gICAgICAgIGlmICghdGhpcy50cmlidXRlLmlzQWN0aXZlKSByZXR1cm4gZmFsc2VcblxuICAgICAgICBpZiAodGhpcy50cmlidXRlLmN1cnJlbnQubWVudGlvblRleHQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnRLZXlQcmVzc2VkID0gZmFsc2VcbiAgICAgICAgICAgIFRyaWJ1dGVFdmVudHMua2V5cygpLmZvckVhY2gobyA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGV2ZW50LmtleUNvZGUgPT09IG8ua2V5KSBldmVudEtleVByZXNzZWQgPSB0cnVlXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICByZXR1cm4gIWV2ZW50S2V5UHJlc3NlZFxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgZ2V0S2V5Q29kZShpbnN0YW5jZSwgZWwsIGV2ZW50KSB7XG4gICAgICAgIGxldCBjaGFyXG4gICAgICAgIGxldCB0cmlidXRlID0gaW5zdGFuY2UudHJpYnV0ZVxuICAgICAgICBsZXQgaW5mbyA9IHRyaWJ1dGUucmFuZ2UuZ2V0VHJpZ2dlckluZm8oZmFsc2UsIHRyaWJ1dGUuaGFzVHJhaWxpbmdTcGFjZSwgdHJ1ZSwgdHJpYnV0ZS5hbGxvd1NwYWNlcylcblxuICAgICAgICBpZiAoaW5mbykge1xuICAgICAgICAgICAgcmV0dXJuIGluZm8ubWVudGlvblRyaWdnZXJDaGFyLmNoYXJDb2RlQXQoMClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXBkYXRlU2VsZWN0aW9uKGVsKSB7XG4gICAgICAgIHRoaXMudHJpYnV0ZS5jdXJyZW50LmVsZW1lbnQgPSBlbFxuICAgICAgICBsZXQgaW5mbyA9IHRoaXMudHJpYnV0ZS5yYW5nZS5nZXRUcmlnZ2VySW5mbyhmYWxzZSwgdGhpcy50cmlidXRlLmhhc1RyYWlsaW5nU3BhY2UsIHRydWUsIHRoaXMudHJpYnV0ZS5hbGxvd1NwYWNlcylcblxuICAgICAgICBpZiAoaW5mbykge1xuICAgICAgICAgICAgdGhpcy50cmlidXRlLmN1cnJlbnQuc2VsZWN0ZWRQYXRoID0gaW5mby5tZW50aW9uU2VsZWN0ZWRQYXRoXG4gICAgICAgICAgICB0aGlzLnRyaWJ1dGUuY3VycmVudC5tZW50aW9uVGV4dCA9IGluZm8ubWVudGlvblRleHRcbiAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5jdXJyZW50LnNlbGVjdGVkT2Zmc2V0ID0gaW5mby5tZW50aW9uU2VsZWN0ZWRPZmZzZXRcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGNhbGxiYWNrcygpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHRyaWdnZXJDaGFyOiAoZSwgZWwsIHRyaWdnZXIpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgdHJpYnV0ZSA9IHRoaXMudHJpYnV0ZVxuICAgICAgICAgICAgICAgIHRyaWJ1dGUuY3VycmVudC50cmlnZ2VyID0gdHJpZ2dlclxuXG4gICAgICAgICAgICAgICAgbGV0IGNvbGxlY3Rpb25JdGVtID0gdHJpYnV0ZS5jb2xsZWN0aW9uLmZpbmQoaXRlbSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpdGVtLnRyaWdnZXIgPT09IHRyaWdnZXJcbiAgICAgICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICAgICAgdHJpYnV0ZS5jdXJyZW50LmNvbGxlY3Rpb24gPSBjb2xsZWN0aW9uSXRlbVxuICAgICAgICAgICAgICAgIGlmICh0cmlidXRlLmlucHV0RXZlbnQpIHRyaWJ1dGUuc2hvd01lbnVGb3IoZWwsIHRydWUpXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZW50ZXI6IChlLCBlbCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIGNob29zZSBzZWxlY3Rpb25cbiAgICAgICAgICAgICAgICBpZiAodGhpcy50cmlidXRlLmlzQWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICAgICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpXG4gICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLnNlbGVjdEl0ZW1BdEluZGV4KHRoaXMudHJpYnV0ZS5tZW51U2VsZWN0ZWQsIGUpXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUuaGlkZU1lbnUoKVxuICAgICAgICAgICAgICAgICAgICB9LCAwKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBlc2NhcGU6IChlLCBlbCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnRyaWJ1dGUuaXNBY3RpdmUpIHtcbiAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgICAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKClcbiAgICAgICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLmlzQWN0aXZlID0gZmFsc2VcbiAgICAgICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLmhpZGVNZW51KClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgdGFiOiAoZSwgZWwpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBjaG9vc2UgZmlyc3QgbWF0Y2hcbiAgICAgICAgICAgICAgICB0aGlzLmNhbGxiYWNrcygpLmVudGVyKGUsIGVsKVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHNwYWNlOiAoZSwgZWwpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy50cmlidXRlLmlzQWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnRyaWJ1dGUuc3BhY2VTZWxlY3RzTWF0Y2gpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2FsbGJhY2tzKCkuZW50ZXIoZSwgZWwpXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoIXRoaXMudHJpYnV0ZS5hbGxvd1NwYWNlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5oaWRlTWVudSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5pc0FjdGl2ZSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSwgMCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgdXA6IChlLCBlbCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIG5hdmlnYXRlIHVwIHVsXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMudHJpYnV0ZS5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKVxuICAgICAgICAgICAgICAgICAgICBsZXQgY291bnQgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5maWx0ZXJlZEl0ZW1zLmxlbmd0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gdGhpcy50cmlidXRlLm1lbnVTZWxlY3RlZFxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChjb3VudCA+IHNlbGVjdGVkICYmIHNlbGVjdGVkID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLm1lbnVTZWxlY3RlZC0tXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEFjdGl2ZUxpKClcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChzZWxlY3RlZCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51U2VsZWN0ZWQgPSBjb3VudCAtIDFcbiAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEFjdGl2ZUxpKClcbiAgICAgICAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUubWVudS5zY3JvbGxUb3AgPSB0aGlzLnRyaWJ1dGUubWVudS5zY3JvbGxIZWlnaHRcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkb3duOiAoZSwgZWwpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBuYXZpZ2F0ZSBkb3duIHVsXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMudHJpYnV0ZS5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKVxuICAgICAgICAgICAgICAgICAgICBsZXQgY291bnQgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5maWx0ZXJlZEl0ZW1zLmxlbmd0aCAtIDEsXG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3RlZCA9IHRoaXMudHJpYnV0ZS5tZW51U2VsZWN0ZWRcblxuICAgICAgICAgICAgICAgICAgICBpZiAoY291bnQgPiBzZWxlY3RlZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLm1lbnVTZWxlY3RlZCsrXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEFjdGl2ZUxpKClcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjb3VudCA9PT0gc2VsZWN0ZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51U2VsZWN0ZWQgPSAwXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEFjdGl2ZUxpKClcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51LnNjcm9sbFRvcCA9IDBcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkZWxldGU6IChlLCBlbCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnRyaWJ1dGUuaXNBY3RpdmUgJiYgdGhpcy50cmlidXRlLmN1cnJlbnQubWVudGlvblRleHQubGVuZ3RoIDwgMSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUuaGlkZU1lbnUoKVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy50cmlidXRlLmlzQWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5zaG93TWVudUZvcihlbClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzZXRBY3RpdmVMaShpbmRleCkge1xuICAgICAgICBsZXQgbGlzID0gdGhpcy50cmlidXRlLm1lbnUucXVlcnlTZWxlY3RvckFsbCgnbGknKSxcbiAgICAgICAgICAgIGxlbmd0aCA9IGxpcy5sZW5ndGggPj4+IDBcblxuICAgICAgICAvLyBnZXQgaGVpZ2h0c1xuICAgICAgICBsZXQgbWVudUZ1bGxIZWlnaHQgPSB0aGlzLmdldEZ1bGxIZWlnaHQodGhpcy50cmlidXRlLm1lbnUpLFxuICAgICAgICAgICAgbGlIZWlnaHQgPSB0aGlzLmdldEZ1bGxIZWlnaHQobGlzWzBdKVxuXG4gICAgICAgIGlmIChpbmRleCkgdGhpcy50cmlidXRlLm1lbnVTZWxlY3RlZCA9IGluZGV4O1xuXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGxldCBsaSA9IGxpc1tpXVxuICAgICAgICAgICAgaWYgKGkgPT09IHRoaXMudHJpYnV0ZS5tZW51U2VsZWN0ZWQpIHtcbiAgICAgICAgICAgICAgICBsZXQgb2Zmc2V0ID0gbGlIZWlnaHQgKiAoaSsxKVxuICAgICAgICAgICAgICAgIGxldCBzY3JvbGxUb3AgPSB0aGlzLnRyaWJ1dGUubWVudS5zY3JvbGxUb3BcbiAgICAgICAgICAgICAgICBsZXQgdG90YWxTY3JvbGwgPSBzY3JvbGxUb3AgKyBtZW51RnVsbEhlaWdodFxuXG4gICAgICAgICAgICAgICAgaWYgKG9mZnNldCA+IHRvdGFsU2Nyb2xsKSB7XG4gICAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUubWVudS5zY3JvbGxUb3AgKz0gbGlIZWlnaHRcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG9mZnNldCA8IHRvdGFsU2Nyb2xsKSB7XG4gICAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUubWVudS5zY3JvbGxUb3AgLT0gbGlIZWlnaHRcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBsaS5jbGFzc05hbWUgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5jb2xsZWN0aW9uLnNlbGVjdENsYXNzXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxpLmNsYXNzTmFtZSA9ICcnXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXRGdWxsSGVpZ2h0KGVsZW0sIGluY2x1ZGVNYXJnaW4pIHtcbiAgICAgIGxldCBoZWlnaHQgPSBlbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmhlaWdodFxuXG4gICAgICBpZiAoaW5jbHVkZU1hcmdpbikge1xuICAgICAgICBsZXQgc3R5bGUgPSBlbGVtLmN1cnJlbnRTdHlsZSB8fCB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShlbGVtKVxuICAgICAgICByZXR1cm4gaGVpZ2h0ICsgcGFyc2VGbG9hdChzdHlsZS5tYXJnaW5Ub3ApICsgcGFyc2VGbG9hdChzdHlsZS5tYXJnaW5Cb3R0b20pXG4gICAgICB9XG5cbiAgICAgIHJldHVybiBoZWlnaHRcbiAgICB9XG5cbn1cblxuZXhwb3J0IGRlZmF1bHQgVHJpYnV0ZUV2ZW50cztcbiIsImNsYXNzIFRyaWJ1dGVNZW51RXZlbnRzIHtcbiAgICBjb25zdHJ1Y3Rvcih0cmlidXRlKSB7XG4gICAgICAgIHRoaXMudHJpYnV0ZSA9IHRyaWJ1dGVcbiAgICAgICAgdGhpcy50cmlidXRlLm1lbnVFdmVudHMgPSB0aGlzXG4gICAgICAgIHRoaXMubWVudSA9IHRoaXMudHJpYnV0ZS5tZW51XG4gICAgfVxuXG4gICAgYmluZChtZW51KSB7XG4gICAgICAgIG1lbnUubWVudUtleWRvd25FdmVudCA9IHRoaXMudHJpYnV0ZS5ldmVudHMua2V5ZG93bi5iaW5kKHRoaXMubWVudSwgdGhpcylcbiAgICAgICAgdGhpcy5tZW51Q2xpY2tFdmVudCA9IHRoaXMudHJpYnV0ZS5ldmVudHMuY2xpY2suYmluZChudWxsLCB0aGlzKVxuICAgICAgICB0aGlzLm1lbnVDb250YWluZXJTY3JvbGxFdmVudCA9IHRoaXMuZGVib3VuY2UoKCkgPT4ge1xuICAgICAgICAgICAgaWYgKHRoaXMudHJpYnV0ZS5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5zaG93TWVudUZvcih0aGlzLnRyaWJ1dGUuY3VycmVudC5lbGVtZW50LCBmYWxzZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSwgMzAwLCBmYWxzZSlcbiAgICAgICAgdGhpcy53aW5kb3dSZXNpemVFdmVudCA9IHRoaXMuZGVib3VuY2UoKCkgPT4ge1xuICAgICAgICAgICAgaWYgKHRoaXMudHJpYnV0ZS5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5yYW5nZS5wb3NpdGlvbk1lbnVBdENhcmV0KHRydWUpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDMwMCwgZmFsc2UpXG5cbiAgICAgICAgLy8gZml4ZXMgSUUxMSBpc3N1ZXMgd2l0aCBtb3VzZXVwXG4gICAgICAgIHRoaXMudHJpYnV0ZS5yYW5nZS5nZXREb2N1bWVudCgpLmFkZEV2ZW50TGlzdGVuZXIoJ01TUG9pbnRlclVwJyxcbiAgICAgICAgICAgIHRoaXMubWVudUNsaWNrRXZlbnQsIGZhbHNlKVxuICAgICAgICBtZW51LmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLFxuICAgICAgICAgICAgdGhpcy5tZW51S2V5ZG93bkV2ZW50LCBmYWxzZSlcbiAgICAgICAgdGhpcy50cmlidXRlLnJhbmdlLmdldERvY3VtZW50KCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsXG4gICAgICAgICAgICB0aGlzLm1lbnVDbGlja0V2ZW50LCBmYWxzZSlcbiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMud2luZG93UmVzaXplRXZlbnQpXG5cbiAgICAgICAgaWYgKHRoaXMubWVudUNvbnRhaW5lcikge1xuICAgICAgICAgICAgdGhpcy5tZW51Q29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRoaXMubWVudUNvbnRhaW5lclNjcm9sbEV2ZW50LCBmYWxzZSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aGlzLm1lbnVDb250YWluZXJTY3JvbGxFdmVudClcbiAgICAgICAgfVxuXG4gICAgfVxuXG4gICAgdW5iaW5kKG1lbnUpIHtcbiAgICAgICAgbWVudS5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXlkb3duJyxcbiAgICAgICAgICAgIG1lbnUubWVudUtleWRvd25FdmVudCwgZmFsc2UpXG4gICAgICAgIGRlbGV0ZSBtZW51Lm1lbnVLZXlkb3duRXZlbnRcbiAgICAgICAgdGhpcy50cmlidXRlLnJhbmdlLmdldERvY3VtZW50KCkucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsXG4gICAgICAgICAgICB0aGlzLm1lbnVDbGlja0V2ZW50LCBmYWxzZSlcbiAgICAgICAgdGhpcy50cmlidXRlLnJhbmdlLmdldERvY3VtZW50KCkucmVtb3ZlRXZlbnRMaXN0ZW5lcignTVNQb2ludGVyVXAnLFxuICAgICAgICAgICAgdGhpcy5tZW51Q2xpY2tFdmVudCwgZmFsc2UpXG4gICAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdyZXNpemUnLCB0aGlzLndpbmRvd1Jlc2l6ZUV2ZW50KVxuXG4gICAgICAgIGlmICh0aGlzLm1lbnVDb250YWluZXIpIHtcbiAgICAgICAgICAgIHRoaXMubWVudUNvbnRhaW5lci5yZW1vdmVFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aGlzLm1lbnVDb250YWluZXJTY3JvbGxFdmVudCwgZmFsc2UpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhpcy5tZW51Q29udGFpbmVyU2Nyb2xsRXZlbnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkZWJvdW5jZShmdW5jLCB3YWl0LCBpbW1lZGlhdGUpIHtcbiAgICAgICAgdmFyIHRpbWVvdXRcbiAgICAgICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgICAgIHZhciBjb250ZXh0ID0gdGhpcyxcbiAgICAgICAgICAgICAgICBhcmdzID0gYXJndW1lbnRzXG4gICAgICAgICAgICB2YXIgbGF0ZXIgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGltZW91dCA9IG51bGxcbiAgICAgICAgICAgICAgICBpZiAoIWltbWVkaWF0ZSkgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGNhbGxOb3cgPSBpbW1lZGlhdGUgJiYgIXRpbWVvdXRcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KVxuICAgICAgICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQobGF0ZXIsIHdhaXQpXG4gICAgICAgICAgICBpZiAoY2FsbE5vdykgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKVxuICAgICAgICB9XG4gICAgfVxufVxuXG5cbmV4cG9ydCBkZWZhdWx0IFRyaWJ1dGVNZW51RXZlbnRzO1xuIiwiLy8gVGhhbmtzIHRvIGh0dHBzOi8vZ2l0aHViLmNvbS9qZWZmLWNvbGxpbnMvbWVudC5pb1xuY2xhc3MgVHJpYnV0ZVJhbmdlIHtcbiAgICBjb25zdHJ1Y3Rvcih0cmlidXRlKSB7XG4gICAgICAgIHRoaXMudHJpYnV0ZSA9IHRyaWJ1dGVcbiAgICAgICAgdGhpcy50cmlidXRlLnJhbmdlID0gdGhpc1xuICAgIH1cblxuICAgIGdldERvY3VtZW50KCkge1xuICAgICAgICBsZXQgaWZyYW1lXG4gICAgICAgIGlmICh0aGlzLnRyaWJ1dGUuY3VycmVudC5jb2xsZWN0aW9uKSB7XG4gICAgICAgICAgICBpZnJhbWUgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5jb2xsZWN0aW9uLmlmcmFtZVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpZnJhbWUpIHtcbiAgICAgICAgICAgIHJldHVybiBkb2N1bWVudFxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGlmcmFtZS5jb250ZW50V2luZG93LmRvY3VtZW50XG4gICAgfVxuXG4gICAgcG9zaXRpb25NZW51QXRDYXJldChzY3JvbGxUbykge1xuICAgICAgICBsZXQgY29udGV4dCA9IHRoaXMudHJpYnV0ZS5jdXJyZW50LFxuICAgICAgICAgICAgY29vcmRpbmF0ZXNcblxuICAgICAgICBsZXQgaW5mbyA9IHRoaXMuZ2V0VHJpZ2dlckluZm8oZmFsc2UsIHRoaXMudHJpYnV0ZS5oYXNUcmFpbGluZ1NwYWNlLCB0cnVlLCB0aGlzLnRyaWJ1dGUuYWxsb3dTcGFjZXMpXG5cbiAgICAgICAgaWYgKHR5cGVvZiBpbmZvICE9PSAndW5kZWZpbmVkJykge1xuXG4gICAgICAgICAgICBpZighdGhpcy50cmlidXRlLnBvc2l0aW9uTWVudSl7XG4gICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLm1lbnUuc3R5bGUuY3NzVGV4dCA9IGBkaXNwbGF5OiBibG9jaztgXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghdGhpcy5pc0NvbnRlbnRFZGl0YWJsZShjb250ZXh0LmVsZW1lbnQpKSB7XG4gICAgICAgICAgICAgICAgY29vcmRpbmF0ZXMgPSB0aGlzLmdldFRleHRBcmVhT3JJbnB1dFVuZGVybGluZVBvc2l0aW9uKHRoaXMudHJpYnV0ZS5jdXJyZW50LmVsZW1lbnQsXG4gICAgICAgICAgICAgICAgICAgIGluZm8ubWVudGlvblBvc2l0aW9uKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29vcmRpbmF0ZXMgPSB0aGlzLmdldENvbnRlbnRFZGl0YWJsZUNhcmV0UG9zaXRpb24oaW5mby5tZW50aW9uUG9zaXRpb24pXG4gICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgdGhpcy50cmlidXRlLm1lbnUuc3R5bGUuY3NzVGV4dCA9IGB0b3A6ICR7Y29vcmRpbmF0ZXMudG9wfXB4O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZnQ6ICR7Y29vcmRpbmF0ZXMubGVmdH1weDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodDogJHtjb29yZGluYXRlcy5yaWdodH1weDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3R0b206ICR7Y29vcmRpbmF0ZXMuYm90dG9tfXB4O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6SW5kZXg6IDEwMDAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXk6IGJsb2NrO2BcblxuICAgICAgICAgICAgaWYgKGNvb3JkaW5hdGVzLmxlZnQgPT09ICdhdXRvJykge1xuICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51LnN0eWxlLmxlZnQgPSAnYXV0bydcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGNvb3JkaW5hdGVzLnRvcCA9PT0gJ2F1dG8nKSB7XG4gICAgICAgICAgICAgICAgdGhpcy50cmlidXRlLm1lbnUuc3R5bGUudG9wID0gJ2F1dG8nXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChzY3JvbGxUbykgdGhpcy5zY3JvbGxJbnRvVmlldygpXG5cbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgbWVudURpbWVuc2lvbnMgPSB7XG4gICAgICAgICAgICAgICAgICAgd2lkdGg6IHRoaXMudHJpYnV0ZS5tZW51Lm9mZnNldFdpZHRoLFxuICAgICAgICAgICAgICAgICAgIGhlaWdodDogdGhpcy50cmlidXRlLm1lbnUub2Zmc2V0SGVpZ2h0XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxldCBtZW51SXNPZmZTY3JlZW4gPSB0aGlzLmlzTWVudU9mZlNjcmVlbihjb29yZGluYXRlcywgbWVudURpbWVuc2lvbnMpXG5cbiAgICAgICAgICAgICAgICBsZXQgbWVudUlzT2ZmU2NyZWVuSG9yaXpvbnRhbGx5ID0gd2luZG93LmlubmVyV2lkdGggPiBtZW51RGltZW5zaW9ucy53aWR0aCAmJiAobWVudUlzT2ZmU2NyZWVuLmxlZnQgfHwgbWVudUlzT2ZmU2NyZWVuLnJpZ2h0KVxuICAgICAgICAgICAgICAgIGxldCBtZW51SXNPZmZTY3JlZW5WZXJ0aWNhbGx5ID0gd2luZG93LmlubmVySGVpZ2h0ID4gbWVudURpbWVuc2lvbnMuaGVpZ2h0ICYmIChtZW51SXNPZmZTY3JlZW4udG9wIHx8IG1lbnVJc09mZlNjcmVlbi5ib3R0b20pXG4gICAgICAgICAgICAgICAgaWYgKG1lbnVJc09mZlNjcmVlbkhvcml6b250YWxseSB8fCBtZW51SXNPZmZTY3JlZW5WZXJ0aWNhbGx5KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51LnN0eWxlLmNzc1RleHQgPSAnZGlzcGxheTogbm9uZSdcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wb3NpdGlvbk1lbnVBdENhcmV0KHNjcm9sbFRvKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sIDApXG5cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5tZW51LnN0eWxlLmNzc1RleHQgPSAnZGlzcGxheTogbm9uZSdcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNlbGVjdEVsZW1lbnQodGFyZ2V0RWxlbWVudCwgcGF0aCwgb2Zmc2V0KSB7XG4gICAgICAgIGxldCByYW5nZVxuICAgICAgICBsZXQgZWxlbSA9IHRhcmdldEVsZW1lbnRcblxuICAgICAgICBpZiAocGF0aCkge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYXRoLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgZWxlbSA9IGVsZW0uY2hpbGROb2Rlc1twYXRoW2ldXVxuICAgICAgICAgICAgICAgIGlmIChlbGVtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHdoaWxlIChlbGVtLmxlbmd0aCA8IG9mZnNldCkge1xuICAgICAgICAgICAgICAgICAgICBvZmZzZXQgLT0gZWxlbS5sZW5ndGhcbiAgICAgICAgICAgICAgICAgICAgZWxlbSA9IGVsZW0ubmV4dFNpYmxpbmdcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGVsZW0uY2hpbGROb2Rlcy5sZW5ndGggPT09IDAgJiYgIWVsZW0ubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0gPSBlbGVtLnByZXZpb3VzU2libGluZ1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgc2VsID0gdGhpcy5nZXRXaW5kb3dTZWxlY3Rpb24oKVxuXG4gICAgICAgIHJhbmdlID0gdGhpcy5nZXREb2N1bWVudCgpLmNyZWF0ZVJhbmdlKClcbiAgICAgICAgcmFuZ2Uuc2V0U3RhcnQoZWxlbSwgb2Zmc2V0KVxuICAgICAgICByYW5nZS5zZXRFbmQoZWxlbSwgb2Zmc2V0KVxuICAgICAgICByYW5nZS5jb2xsYXBzZSh0cnVlKVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBzZWwucmVtb3ZlQWxsUmFuZ2VzKClcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHt9XG5cbiAgICAgICAgc2VsLmFkZFJhbmdlKHJhbmdlKVxuICAgICAgICB0YXJnZXRFbGVtZW50LmZvY3VzKClcbiAgICB9XG5cbiAgICByZXNldFNlbGVjdGlvbih0YXJnZXRFbGVtZW50LCBwYXRoLCBvZmZzZXQpIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzQ29udGVudEVkaXRhYmxlKHRhcmdldEVsZW1lbnQpKSB7XG4gICAgICAgICAgICBpZiAodGFyZ2V0RWxlbWVudCAhPT0gdGhpcy50cmlidXRlLmN1cnJlbnQuZWxlbWVudCkge1xuICAgICAgICAgICAgICAgIHRhcmdldEVsZW1lbnQuZm9jdXMoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zZWxlY3RFbGVtZW50KHRhcmdldEVsZW1lbnQsIHBhdGgsIG9mZnNldClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJlcGxhY2VUcmlnZ2VyVGV4dCh0ZXh0LCByZXF1aXJlTGVhZGluZ1NwYWNlLCBoYXNUcmFpbGluZ1NwYWNlLCBvcmlnaW5hbEV2ZW50LCBpdGVtKSB7XG4gICAgICAgIGxldCBjb250ZXh0ID0gdGhpcy50cmlidXRlLmN1cnJlbnRcbiAgICAgICAgdGhpcy5yZXNldFNlbGVjdGlvbihjb250ZXh0LmVsZW1lbnQsIGNvbnRleHQuc2VsZWN0ZWRQYXRoLCBjb250ZXh0LnNlbGVjdGVkT2Zmc2V0KVxuXG4gICAgICAgIGxldCBpbmZvID0gdGhpcy5nZXRUcmlnZ2VySW5mbyh0cnVlLCBoYXNUcmFpbGluZ1NwYWNlLCByZXF1aXJlTGVhZGluZ1NwYWNlLCB0aGlzLnRyaWJ1dGUuYWxsb3dTcGFjZXMpXG5cbiAgICAgICAgLy8gQ3JlYXRlIHRoZSBldmVudFxuICAgICAgICBsZXQgcmVwbGFjZUV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KCd0cmlidXRlLXJlcGxhY2VkJywge1xuICAgICAgICAgICAgZGV0YWlsOiB7XG4gICAgICAgICAgICAgICAgaXRlbTogaXRlbSxcbiAgICAgICAgICAgICAgICBldmVudDogb3JpZ2luYWxFdmVudFxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIGlmIChpbmZvICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5pc0NvbnRlbnRFZGl0YWJsZShjb250ZXh0LmVsZW1lbnQpKSB7XG4gICAgICAgICAgICAgICAgbGV0IG15RmllbGQgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5lbGVtZW50XG4gICAgICAgICAgICAgICAgbGV0IHRleHRTdWZmaXggPSB0eXBlb2YgdGhpcy50cmlidXRlLnJlcGxhY2VUZXh0U3VmZml4ID09ICdzdHJpbmcnXG4gICAgICAgICAgICAgICAgICAgID8gdGhpcy50cmlidXRlLnJlcGxhY2VUZXh0U3VmZml4XG4gICAgICAgICAgICAgICAgICAgIDogJyAnXG4gICAgICAgICAgICAgICAgdGV4dCArPSB0ZXh0U3VmZml4XG4gICAgICAgICAgICAgICAgbGV0IHN0YXJ0UG9zID0gaW5mby5tZW50aW9uUG9zaXRpb25cbiAgICAgICAgICAgICAgICBsZXQgZW5kUG9zID0gaW5mby5tZW50aW9uUG9zaXRpb24gKyBpbmZvLm1lbnRpb25UZXh0Lmxlbmd0aCArIHRleHRTdWZmaXgubGVuZ3RoXG4gICAgICAgICAgICAgICAgbXlGaWVsZC52YWx1ZSA9IG15RmllbGQudmFsdWUuc3Vic3RyaW5nKDAsIHN0YXJ0UG9zKSArIHRleHQgK1xuICAgICAgICAgICAgICAgICAgICBteUZpZWxkLnZhbHVlLnN1YnN0cmluZyhlbmRQb3MsIG15RmllbGQudmFsdWUubGVuZ3RoKVxuICAgICAgICAgICAgICAgIG15RmllbGQuc2VsZWN0aW9uU3RhcnQgPSBzdGFydFBvcyArIHRleHQubGVuZ3RoXG4gICAgICAgICAgICAgICAgbXlGaWVsZC5zZWxlY3Rpb25FbmQgPSBzdGFydFBvcyArIHRleHQubGVuZ3RoXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIGFkZCBhIHNwYWNlIHRvIHRoZSBlbmQgb2YgdGhlIHBhc3RlZCB0ZXh0XG4gICAgICAgICAgICAgICAgbGV0IHRleHRTdWZmaXggPSB0eXBlb2YgdGhpcy50cmlidXRlLnJlcGxhY2VUZXh0U3VmZml4ID09ICdzdHJpbmcnXG4gICAgICAgICAgICAgICAgICAgID8gdGhpcy50cmlidXRlLnJlcGxhY2VUZXh0U3VmZml4XG4gICAgICAgICAgICAgICAgICAgIDogJ1xceEEwJ1xuICAgICAgICAgICAgICAgIHRleHQgKz0gdGV4dFN1ZmZpeFxuICAgICAgICAgICAgICAgIHRoaXMucGFzdGVIdG1sKHRleHQsIGluZm8ubWVudGlvblBvc2l0aW9uLFxuICAgICAgICAgICAgICAgICAgICBpbmZvLm1lbnRpb25Qb3NpdGlvbiArIGluZm8ubWVudGlvblRleHQubGVuZ3RoICsgMSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29udGV4dC5lbGVtZW50LmRpc3BhdGNoRXZlbnQocmVwbGFjZUV2ZW50KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcGFzdGVIdG1sKGh0bWwsIHN0YXJ0UG9zLCBlbmRQb3MpIHtcbiAgICAgICAgbGV0IHJhbmdlLCBzZWxcbiAgICAgICAgc2VsID0gdGhpcy5nZXRXaW5kb3dTZWxlY3Rpb24oKVxuICAgICAgICByYW5nZSA9IHRoaXMuZ2V0RG9jdW1lbnQoKS5jcmVhdGVSYW5nZSgpXG4gICAgICAgIHJhbmdlLnNldFN0YXJ0KHNlbC5hbmNob3JOb2RlLCBzdGFydFBvcylcbiAgICAgICAgcmFuZ2Uuc2V0RW5kKHNlbC5hbmNob3JOb2RlLCBlbmRQb3MpXG4gICAgICAgIHJhbmdlLmRlbGV0ZUNvbnRlbnRzKClcblxuICAgICAgICBsZXQgZWwgPSB0aGlzLmdldERvY3VtZW50KCkuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgZWwuaW5uZXJIVE1MID0gaHRtbFxuICAgICAgICBsZXQgZnJhZyA9IHRoaXMuZ2V0RG9jdW1lbnQoKS5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCksXG4gICAgICAgICAgICBub2RlLCBsYXN0Tm9kZVxuICAgICAgICB3aGlsZSAoKG5vZGUgPSBlbC5maXJzdENoaWxkKSkge1xuICAgICAgICAgICAgbGFzdE5vZGUgPSBmcmFnLmFwcGVuZENoaWxkKG5vZGUpXG4gICAgICAgIH1cbiAgICAgICAgcmFuZ2UuaW5zZXJ0Tm9kZShmcmFnKVxuXG4gICAgICAgIC8vIFByZXNlcnZlIHRoZSBzZWxlY3Rpb25cbiAgICAgICAgaWYgKGxhc3ROb2RlKSB7XG4gICAgICAgICAgICByYW5nZSA9IHJhbmdlLmNsb25lUmFuZ2UoKVxuICAgICAgICAgICAgcmFuZ2Uuc2V0U3RhcnRBZnRlcihsYXN0Tm9kZSlcbiAgICAgICAgICAgIHJhbmdlLmNvbGxhcHNlKHRydWUpXG4gICAgICAgICAgICBzZWwucmVtb3ZlQWxsUmFuZ2VzKClcbiAgICAgICAgICAgIHNlbC5hZGRSYW5nZShyYW5nZSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGdldFdpbmRvd1NlbGVjdGlvbigpIHtcbiAgICAgICAgaWYgKHRoaXMudHJpYnV0ZS5jb2xsZWN0aW9uLmlmcmFtZSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudHJpYnV0ZS5jb2xsZWN0aW9uLmlmcmFtZS5jb250ZW50V2luZG93LmdldFNlbGVjdGlvbigpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gd2luZG93LmdldFNlbGVjdGlvbigpXG4gICAgfVxuXG4gICAgZ2V0Tm9kZVBvc2l0aW9uSW5QYXJlbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoZWxlbWVudC5wYXJlbnROb2RlID09PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gMFxuICAgICAgICB9XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBlbGVtZW50LnBhcmVudE5vZGUuY2hpbGROb2Rlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgbGV0IG5vZGUgPSBlbGVtZW50LnBhcmVudE5vZGUuY2hpbGROb2Rlc1tpXVxuXG4gICAgICAgICAgICBpZiAobm9kZSA9PT0gZWxlbWVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXRDb250ZW50RWRpdGFibGVTZWxlY3RlZFBhdGgoY3R4KSB7XG4gICAgICAgIGxldCBzZWwgPSB0aGlzLmdldFdpbmRvd1NlbGVjdGlvbigpXG4gICAgICAgIGxldCBzZWxlY3RlZCA9IHNlbC5hbmNob3JOb2RlXG4gICAgICAgIGxldCBwYXRoID0gW11cbiAgICAgICAgbGV0IG9mZnNldFxuXG4gICAgICAgIGlmIChzZWxlY3RlZCAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgaVxuICAgICAgICAgICAgbGV0IGNlID0gc2VsZWN0ZWQuY29udGVudEVkaXRhYmxlXG4gICAgICAgICAgICB3aGlsZSAoc2VsZWN0ZWQgIT09IG51bGwgJiYgY2UgIT09ICd0cnVlJykge1xuICAgICAgICAgICAgICAgIGkgPSB0aGlzLmdldE5vZGVQb3NpdGlvbkluUGFyZW50KHNlbGVjdGVkKVxuICAgICAgICAgICAgICAgIHBhdGgucHVzaChpKVxuICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gc2VsZWN0ZWQucGFyZW50Tm9kZVxuICAgICAgICAgICAgICAgIGlmIChzZWxlY3RlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBjZSA9IHNlbGVjdGVkLmNvbnRlbnRFZGl0YWJsZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHBhdGgucmV2ZXJzZSgpXG5cbiAgICAgICAgICAgIC8vIGdldFJhbmdlQXQgbWF5IG5vdCBleGlzdCwgbmVlZCBhbHRlcm5hdGl2ZVxuICAgICAgICAgICAgb2Zmc2V0ID0gc2VsLmdldFJhbmdlQXQoMCkuc3RhcnRPZmZzZXRcblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzZWxlY3RlZDogc2VsZWN0ZWQsXG4gICAgICAgICAgICAgICAgcGF0aDogcGF0aCxcbiAgICAgICAgICAgICAgICBvZmZzZXQ6IG9mZnNldFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2V0VGV4dFByZWNlZGluZ0N1cnJlbnRTZWxlY3Rpb24oKSB7XG4gICAgICAgIGxldCBjb250ZXh0ID0gdGhpcy50cmlidXRlLmN1cnJlbnQsXG4gICAgICAgICAgICB0ZXh0ID0gJydcblxuICAgICAgICBpZiAoIXRoaXMuaXNDb250ZW50RWRpdGFibGUoY29udGV4dC5lbGVtZW50KSkge1xuICAgICAgICAgICAgbGV0IHRleHRDb21wb25lbnQgPSB0aGlzLnRyaWJ1dGUuY3VycmVudC5lbGVtZW50O1xuICAgICAgICAgICAgaWYgKHRleHRDb21wb25lbnQpIHtcbiAgICAgICAgICAgICAgICBsZXQgc3RhcnRQb3MgPSB0ZXh0Q29tcG9uZW50LnNlbGVjdGlvblN0YXJ0XG4gICAgICAgICAgICAgICAgaWYgKHRleHRDb21wb25lbnQudmFsdWUgJiYgc3RhcnRQb3MgPj0gMCkge1xuICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gdGV4dENvbXBvbmVudC52YWx1ZS5zdWJzdHJpbmcoMCwgc3RhcnRQb3MpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZXQgc2VsZWN0ZWRFbGVtID0gdGhpcy5nZXRXaW5kb3dTZWxlY3Rpb24oKS5hbmNob3JOb2RlXG5cbiAgICAgICAgICAgIGlmIChzZWxlY3RlZEVsZW0gIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGxldCB3b3JraW5nTm9kZUNvbnRlbnQgPSBzZWxlY3RlZEVsZW0udGV4dENvbnRlbnRcbiAgICAgICAgICAgICAgICBsZXQgc2VsZWN0U3RhcnRPZmZzZXQgPSB0aGlzLmdldFdpbmRvd1NlbGVjdGlvbigpLmdldFJhbmdlQXQoMCkuc3RhcnRPZmZzZXRcblxuICAgICAgICAgICAgICAgIGlmICh3b3JraW5nTm9kZUNvbnRlbnQgJiYgc2VsZWN0U3RhcnRPZmZzZXQgPj0gMCkge1xuICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gd29ya2luZ05vZGVDb250ZW50LnN1YnN0cmluZygwLCBzZWxlY3RTdGFydE9mZnNldClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGV4dFxuICAgIH1cblxuICAgIGdldFRyaWdnZXJJbmZvKG1lbnVBbHJlYWR5QWN0aXZlLCBoYXNUcmFpbGluZ1NwYWNlLCByZXF1aXJlTGVhZGluZ1NwYWNlLCBhbGxvd1NwYWNlcykge1xuICAgICAgICBsZXQgY3R4ID0gdGhpcy50cmlidXRlLmN1cnJlbnRcbiAgICAgICAgbGV0IHNlbGVjdGVkLCBwYXRoLCBvZmZzZXRcblxuICAgICAgICBpZiAoIXRoaXMuaXNDb250ZW50RWRpdGFibGUoY3R4LmVsZW1lbnQpKSB7XG4gICAgICAgICAgICBzZWxlY3RlZCA9IHRoaXMudHJpYnV0ZS5jdXJyZW50LmVsZW1lbnRcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxldCBzZWxlY3Rpb25JbmZvID0gdGhpcy5nZXRDb250ZW50RWRpdGFibGVTZWxlY3RlZFBhdGgoY3R4KVxuXG4gICAgICAgICAgICBpZiAoc2VsZWN0aW9uSW5mbykge1xuICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gc2VsZWN0aW9uSW5mby5zZWxlY3RlZFxuICAgICAgICAgICAgICAgIHBhdGggPSBzZWxlY3Rpb25JbmZvLnBhdGhcbiAgICAgICAgICAgICAgICBvZmZzZXQgPSBzZWxlY3Rpb25JbmZvLm9mZnNldFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGVmZmVjdGl2ZVJhbmdlID0gdGhpcy5nZXRUZXh0UHJlY2VkaW5nQ3VycmVudFNlbGVjdGlvbigpXG5cbiAgICAgICAgaWYgKGVmZmVjdGl2ZVJhbmdlICE9PSB1bmRlZmluZWQgJiYgZWZmZWN0aXZlUmFuZ2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBtb3N0UmVjZW50VHJpZ2dlckNoYXJQb3MgPSAtMVxuICAgICAgICAgICAgbGV0IHRyaWdnZXJDaGFyXG5cbiAgICAgICAgICAgIHRoaXMudHJpYnV0ZS5jb2xsZWN0aW9uLmZvckVhY2goY29uZmlnID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgYyA9IGNvbmZpZy50cmlnZ2VyXG4gICAgICAgICAgICAgICAgbGV0IGlkeCA9IGNvbmZpZy5yZXF1aXJlTGVhZGluZ1NwYWNlID9cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5sYXN0SW5kZXhXaXRoTGVhZGluZ1NwYWNlKGVmZmVjdGl2ZVJhbmdlLCBjKSA6XG4gICAgICAgICAgICAgICAgICAgIGVmZmVjdGl2ZVJhbmdlLmxhc3RJbmRleE9mKGMpXG5cbiAgICAgICAgICAgICAgICBpZiAoaWR4ID4gbW9zdFJlY2VudFRyaWdnZXJDaGFyUG9zKSB7XG4gICAgICAgICAgICAgICAgICAgIG1vc3RSZWNlbnRUcmlnZ2VyQ2hhclBvcyA9IGlkeFxuICAgICAgICAgICAgICAgICAgICB0cmlnZ2VyQ2hhciA9IGNcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZUxlYWRpbmdTcGFjZSA9IGNvbmZpZy5yZXF1aXJlTGVhZGluZ1NwYWNlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgaWYgKG1vc3RSZWNlbnRUcmlnZ2VyQ2hhclBvcyA+PSAwICYmXG4gICAgICAgICAgICAgICAgKFxuICAgICAgICAgICAgICAgICAgICBtb3N0UmVjZW50VHJpZ2dlckNoYXJQb3MgPT09IDAgfHxcbiAgICAgICAgICAgICAgICAgICAgIXJlcXVpcmVMZWFkaW5nU3BhY2UgfHxcbiAgICAgICAgICAgICAgICAgICAgL1tcXHhBMFxcc10vZy50ZXN0KFxuICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0aXZlUmFuZ2Uuc3Vic3RyaW5nKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vc3RSZWNlbnRUcmlnZ2VyQ2hhclBvcyAtIDEsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9zdFJlY2VudFRyaWdnZXJDaGFyUG9zKVxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgbGV0IGN1cnJlbnRUcmlnZ2VyU25pcHBldCA9IGVmZmVjdGl2ZVJhbmdlLnN1YnN0cmluZyhtb3N0UmVjZW50VHJpZ2dlckNoYXJQb3MgKyAxLFxuICAgICAgICAgICAgICAgICAgICBlZmZlY3RpdmVSYW5nZS5sZW5ndGgpXG5cbiAgICAgICAgICAgICAgICB0cmlnZ2VyQ2hhciA9IGVmZmVjdGl2ZVJhbmdlLnN1YnN0cmluZyhtb3N0UmVjZW50VHJpZ2dlckNoYXJQb3MsIG1vc3RSZWNlbnRUcmlnZ2VyQ2hhclBvcyArIDEpXG4gICAgICAgICAgICAgICAgbGV0IGZpcnN0U25pcHBldENoYXIgPSBjdXJyZW50VHJpZ2dlclNuaXBwZXQuc3Vic3RyaW5nKDAsIDEpXG4gICAgICAgICAgICAgICAgbGV0IGxlYWRpbmdTcGFjZSA9IGN1cnJlbnRUcmlnZ2VyU25pcHBldC5sZW5ndGggPiAwICYmXG4gICAgICAgICAgICAgICAgICAgIChcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpcnN0U25pcHBldENoYXIgPT09ICcgJyB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3RTbmlwcGV0Q2hhciA9PT0gJ1xceEEwJ1xuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgaWYgKGhhc1RyYWlsaW5nU3BhY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudFRyaWdnZXJTbmlwcGV0ID0gY3VycmVudFRyaWdnZXJTbmlwcGV0LnRyaW0oKVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGxldCByZWdleCA9IGFsbG93U3BhY2VzID8gL1teXFxTIF0vZyA6IC9bXFx4QTBcXHNdL2c7XG5cbiAgICAgICAgICAgICAgICB0aGlzLnRyaWJ1dGUuaGFzVHJhaWxpbmdTcGFjZSA9IHJlZ2V4LnRlc3QoY3VycmVudFRyaWdnZXJTbmlwcGV0KTtcblxuICAgICAgICAgICAgICAgIGlmICghbGVhZGluZ1NwYWNlICYmIChtZW51QWxyZWFkeUFjdGl2ZSB8fCAhKHJlZ2V4LnRlc3QoY3VycmVudFRyaWdnZXJTbmlwcGV0KSkpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZW50aW9uUG9zaXRpb246IG1vc3RSZWNlbnRUcmlnZ2VyQ2hhclBvcyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lbnRpb25UZXh0OiBjdXJyZW50VHJpZ2dlclNuaXBwZXQsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZW50aW9uU2VsZWN0ZWRFbGVtZW50OiBzZWxlY3RlZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lbnRpb25TZWxlY3RlZFBhdGg6IHBhdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZW50aW9uU2VsZWN0ZWRPZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lbnRpb25UcmlnZ2VyQ2hhcjogdHJpZ2dlckNoYXJcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGxhc3RJbmRleFdpdGhMZWFkaW5nU3BhY2UgKHN0ciwgY2hhcikge1xuICAgICAgICBsZXQgcmV2ZXJzZWRTdHIgPSBzdHIuc3BsaXQoJycpLnJldmVyc2UoKS5qb2luKCcnKVxuICAgICAgICBsZXQgaW5kZXggPSAtMVxuXG4gICAgICAgIGZvciAobGV0IGNpZHggPSAwLCBsZW4gPSBzdHIubGVuZ3RoOyBjaWR4IDwgbGVuOyBjaWR4KyspIHtcbiAgICAgICAgICAgIGxldCBmaXJzdENoYXIgPSBjaWR4ID09PSBzdHIubGVuZ3RoIC0gMVxuICAgICAgICAgICAgbGV0IGxlYWRpbmdTcGFjZSA9IC9cXHMvLnRlc3QocmV2ZXJzZWRTdHJbY2lkeCArIDFdKVxuICAgICAgICAgICAgbGV0IG1hdGNoID0gY2hhciA9PT0gcmV2ZXJzZWRTdHJbY2lkeF1cblxuICAgICAgICAgICAgaWYgKG1hdGNoICYmIChmaXJzdENoYXIgfHwgbGVhZGluZ1NwYWNlKSkge1xuICAgICAgICAgICAgICAgIGluZGV4ID0gc3RyLmxlbmd0aCAtIDEgLSBjaWR4XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBpbmRleFxuICAgIH1cblxuICAgIGlzQ29udGVudEVkaXRhYmxlKGVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQubm9kZU5hbWUgIT09ICdJTlBVVCcgJiYgZWxlbWVudC5ub2RlTmFtZSAhPT0gJ1RFWFRBUkVBJ1xuICAgIH1cblxuICAgIGlzTWVudU9mZlNjcmVlbihjb29yZGluYXRlcywgbWVudURpbWVuc2lvbnMpIHtcbiAgICAgICAgbGV0IHdpbmRvd1dpZHRoID0gd2luZG93LmlubmVyV2lkdGhcbiAgICAgICAgbGV0IHdpbmRvd0hlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodFxuICAgICAgICBsZXQgZG9jID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50XG4gICAgICAgIGxldCB3aW5kb3dMZWZ0ID0gKHdpbmRvdy5wYWdlWE9mZnNldCB8fCBkb2Muc2Nyb2xsTGVmdCkgLSAoZG9jLmNsaWVudExlZnQgfHwgMClcbiAgICAgICAgbGV0IHdpbmRvd1RvcCA9ICh3aW5kb3cucGFnZVlPZmZzZXQgfHwgZG9jLnNjcm9sbFRvcCkgLSAoZG9jLmNsaWVudFRvcCB8fCAwKVxuXG4gICAgICAgIGxldCBtZW51VG9wID0gdHlwZW9mIGNvb3JkaW5hdGVzLnRvcCA9PT0gJ251bWJlcicgPyBjb29yZGluYXRlcy50b3AgOiB3aW5kb3dUb3AgKyB3aW5kb3dIZWlnaHQgLSBjb29yZGluYXRlcy5ib3R0b20gLSBtZW51RGltZW5zaW9ucy5oZWlnaHRcbiAgICAgICAgbGV0IG1lbnVSaWdodCA9IHR5cGVvZiBjb29yZGluYXRlcy5yaWdodCA9PT0gJ251bWJlcicgPyBjb29yZGluYXRlcy5yaWdodCA6IGNvb3JkaW5hdGVzLmxlZnQgKyBtZW51RGltZW5zaW9ucy53aWR0aFxuICAgICAgICBsZXQgbWVudUJvdHRvbSA9IHR5cGVvZiBjb29yZGluYXRlcy5ib3R0b20gPT09ICdudW1iZXInID8gY29vcmRpbmF0ZXMuYm90dG9tIDogY29vcmRpbmF0ZXMudG9wICsgbWVudURpbWVuc2lvbnMuaGVpZ2h0XG4gICAgICAgIGxldCBtZW51TGVmdCA9IHR5cGVvZiBjb29yZGluYXRlcy5sZWZ0ID09PSAnbnVtYmVyJyA/IGNvb3JkaW5hdGVzLmxlZnQgOiB3aW5kb3dMZWZ0ICsgd2luZG93V2lkdGggLSBjb29yZGluYXRlcy5yaWdodCAtIG1lbnVEaW1lbnNpb25zLndpZHRoXG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHRvcDogbWVudVRvcCA8IE1hdGguZmxvb3Iod2luZG93VG9wKSxcbiAgICAgICAgICAgIHJpZ2h0OiBtZW51UmlnaHQgPiBNYXRoLmNlaWwod2luZG93TGVmdCArIHdpbmRvd1dpZHRoKSxcbiAgICAgICAgICAgIGJvdHRvbTogbWVudUJvdHRvbSA+IE1hdGguY2VpbCh3aW5kb3dUb3AgKyB3aW5kb3dIZWlnaHQpLFxuICAgICAgICAgICAgbGVmdDogbWVudUxlZnQgPCBNYXRoLmZsb29yKHdpbmRvd0xlZnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXRNZW51RGltZW5zaW9ucygpIHtcbiAgICAgICAgLy8gV2lkdGggb2YgdGhlIG1lbnUgZGVwZW5kcyBvZiBpdHMgY29udGVudHMgYW5kIHBvc2l0aW9uXG4gICAgICAgIC8vIFdlIG11c3QgY2hlY2sgd2hhdCBpdHMgd2lkdGggd291bGQgYmUgd2l0aG91dCBhbnkgb2JzdHJ1Y3Rpb25cbiAgICAgICAgLy8gVGhpcyB3YXksIHdlIGNhbiBhY2hpZXZlIGdvb2QgcG9zaXRpb25pbmcgZm9yIGZsaXBwaW5nIHRoZSBtZW51XG4gICAgICAgIGxldCBkaW1lbnNpb25zID0ge1xuICAgICAgICAgICAgd2lkdGg6IG51bGwsXG4gICAgICAgICAgICBoZWlnaHQ6IG51bGxcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMudHJpYnV0ZS5tZW51LnN0eWxlLmNzc1RleHQgPSBgdG9wOiAwcHg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0OiAwcHg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbjogZml4ZWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6SW5kZXg6IDEwMDAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aXNpYmlsaXR5OyBoaWRkZW47YFxuICAgICAgIGRpbWVuc2lvbnMud2lkdGggPSB0aGlzLnRyaWJ1dGUubWVudS5vZmZzZXRXaWR0aFxuICAgICAgIGRpbWVuc2lvbnMuaGVpZ2h0ID0gdGhpcy50cmlidXRlLm1lbnUub2Zmc2V0SGVpZ2h0XG5cbiAgICAgICB0aGlzLnRyaWJ1dGUubWVudS5zdHlsZS5jc3NUZXh0ID0gYGRpc3BsYXk6IG5vbmU7YFxuXG4gICAgICAgcmV0dXJuIGRpbWVuc2lvbnNcbiAgICB9XG5cbiAgICBnZXRUZXh0QXJlYU9ySW5wdXRVbmRlcmxpbmVQb3NpdGlvbihlbGVtZW50LCBwb3NpdGlvbiwgZmxpcHBlZCkge1xuICAgICAgICBsZXQgcHJvcGVydGllcyA9IFsnZGlyZWN0aW9uJywgJ2JveFNpemluZycsICd3aWR0aCcsICdoZWlnaHQnLCAnb3ZlcmZsb3dYJyxcbiAgICAgICAgICAgICdvdmVyZmxvd1knLCAnYm9yZGVyVG9wV2lkdGgnLCAnYm9yZGVyUmlnaHRXaWR0aCcsXG4gICAgICAgICAgICAnYm9yZGVyQm90dG9tV2lkdGgnLCAnYm9yZGVyTGVmdFdpZHRoJywgJ3BhZGRpbmdUb3AnLFxuICAgICAgICAgICAgJ3BhZGRpbmdSaWdodCcsICdwYWRkaW5nQm90dG9tJywgJ3BhZGRpbmdMZWZ0JyxcbiAgICAgICAgICAgICdmb250U3R5bGUnLCAnZm9udFZhcmlhbnQnLCAnZm9udFdlaWdodCcsICdmb250U3RyZXRjaCcsXG4gICAgICAgICAgICAnZm9udFNpemUnLCAnZm9udFNpemVBZGp1c3QnLCAnbGluZUhlaWdodCcsICdmb250RmFtaWx5JyxcbiAgICAgICAgICAgICd0ZXh0QWxpZ24nLCAndGV4dFRyYW5zZm9ybScsICd0ZXh0SW5kZW50JyxcbiAgICAgICAgICAgICd0ZXh0RGVjb3JhdGlvbicsICdsZXR0ZXJTcGFjaW5nJywgJ3dvcmRTcGFjaW5nJ1xuICAgICAgICBdXG5cbiAgICAgICAgbGV0IGlzRmlyZWZveCA9ICh3aW5kb3cubW96SW5uZXJTY3JlZW5YICE9PSBudWxsKVxuXG4gICAgICAgIGxldCBkaXYgPSB0aGlzLmdldERvY3VtZW50KCkuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgZGl2LmlkID0gJ2lucHV0LXRleHRhcmVhLWNhcmV0LXBvc2l0aW9uLW1pcnJvci1kaXYnXG4gICAgICAgIHRoaXMuZ2V0RG9jdW1lbnQoKS5ib2R5LmFwcGVuZENoaWxkKGRpdilcblxuICAgICAgICBsZXQgc3R5bGUgPSBkaXYuc3R5bGVcbiAgICAgICAgbGV0IGNvbXB1dGVkID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUgPyBnZXRDb21wdXRlZFN0eWxlKGVsZW1lbnQpIDogZWxlbWVudC5jdXJyZW50U3R5bGVcblxuICAgICAgICBzdHlsZS53aGl0ZVNwYWNlID0gJ3ByZS13cmFwJ1xuICAgICAgICBpZiAoZWxlbWVudC5ub2RlTmFtZSAhPT0gJ0lOUFVUJykge1xuICAgICAgICAgICAgc3R5bGUud29yZFdyYXAgPSAnYnJlYWstd29yZCdcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHBvc2l0aW9uIG9mZi1zY3JlZW5cbiAgICAgICAgc3R5bGUucG9zaXRpb24gPSAnYWJzb2x1dGUnXG4gICAgICAgIHN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJ1xuXG4gICAgICAgIC8vIHRyYW5zZmVyIHRoZSBlbGVtZW50J3MgcHJvcGVydGllcyB0byB0aGUgZGl2XG4gICAgICAgIHByb3BlcnRpZXMuZm9yRWFjaChwcm9wID0+IHtcbiAgICAgICAgICAgIHN0eWxlW3Byb3BdID0gY29tcHV0ZWRbcHJvcF1cbiAgICAgICAgfSlcblxuICAgICAgICBpZiAoaXNGaXJlZm94KSB7XG4gICAgICAgICAgICBzdHlsZS53aWR0aCA9IGAkeyhwYXJzZUludChjb21wdXRlZC53aWR0aCkgLSAyKX1weGBcbiAgICAgICAgICAgIGlmIChlbGVtZW50LnNjcm9sbEhlaWdodCA+IHBhcnNlSW50KGNvbXB1dGVkLmhlaWdodCkpXG4gICAgICAgICAgICAgICAgc3R5bGUub3ZlcmZsb3dZID0gJ3Njcm9sbCdcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHN0eWxlLm92ZXJmbG93ID0gJ2hpZGRlbidcbiAgICAgICAgfVxuXG4gICAgICAgIGRpdi50ZXh0Q29udGVudCA9IGVsZW1lbnQudmFsdWUuc3Vic3RyaW5nKDAsIHBvc2l0aW9uKVxuXG4gICAgICAgIGlmIChlbGVtZW50Lm5vZGVOYW1lID09PSAnSU5QVVQnKSB7XG4gICAgICAgICAgICBkaXYudGV4dENvbnRlbnQgPSBkaXYudGV4dENvbnRlbnQucmVwbGFjZSgvXFxzL2csICfCoCcpXG4gICAgICAgIH1cblxuICAgICAgICBsZXQgc3BhbiA9IHRoaXMuZ2V0RG9jdW1lbnQoKS5jcmVhdGVFbGVtZW50KCdzcGFuJylcbiAgICAgICAgc3Bhbi50ZXh0Q29udGVudCA9IGVsZW1lbnQudmFsdWUuc3Vic3RyaW5nKHBvc2l0aW9uKSB8fCAnLidcbiAgICAgICAgZGl2LmFwcGVuZENoaWxkKHNwYW4pXG5cbiAgICAgICAgbGV0IHJlY3QgPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG4gICAgICAgIGxldCBkb2MgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnRcbiAgICAgICAgbGV0IHdpbmRvd0xlZnQgPSAod2luZG93LnBhZ2VYT2Zmc2V0IHx8IGRvYy5zY3JvbGxMZWZ0KSAtIChkb2MuY2xpZW50TGVmdCB8fCAwKVxuICAgICAgICBsZXQgd2luZG93VG9wID0gKHdpbmRvdy5wYWdlWU9mZnNldCB8fCBkb2Muc2Nyb2xsVG9wKSAtIChkb2MuY2xpZW50VG9wIHx8IDApXG5cbiAgICAgICAgbGV0IGNvb3JkaW5hdGVzID0ge1xuICAgICAgICAgICAgdG9wOiByZWN0LnRvcCArIHdpbmRvd1RvcCArIHNwYW4ub2Zmc2V0VG9wICsgcGFyc2VJbnQoY29tcHV0ZWQuYm9yZGVyVG9wV2lkdGgpICsgcGFyc2VJbnQoY29tcHV0ZWQuZm9udFNpemUpIC0gZWxlbWVudC5zY3JvbGxUb3AsXG4gICAgICAgICAgICBsZWZ0OiByZWN0LmxlZnQgKyB3aW5kb3dMZWZ0ICsgc3Bhbi5vZmZzZXRMZWZ0ICsgcGFyc2VJbnQoY29tcHV0ZWQuYm9yZGVyTGVmdFdpZHRoKVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHdpbmRvd1dpZHRoID0gd2luZG93LmlubmVyV2lkdGhcbiAgICAgICAgbGV0IHdpbmRvd0hlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodFxuXG4gICAgICAgIGxldCBtZW51RGltZW5zaW9ucyA9IHRoaXMuZ2V0TWVudURpbWVuc2lvbnMoKVxuICAgICAgICBsZXQgbWVudUlzT2ZmU2NyZWVuID0gdGhpcy5pc01lbnVPZmZTY3JlZW4oY29vcmRpbmF0ZXMsIG1lbnVEaW1lbnNpb25zKVxuXG4gICAgICAgIGlmIChtZW51SXNPZmZTY3JlZW4ucmlnaHQpIHtcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLnJpZ2h0ID0gd2luZG93V2lkdGggLSBjb29yZGluYXRlcy5sZWZ0XG4gICAgICAgICAgICBjb29yZGluYXRlcy5sZWZ0ID0gJ2F1dG8nXG4gICAgICAgIH1cblxuICAgICAgICBsZXQgcGFyZW50SGVpZ2h0ID0gdGhpcy50cmlidXRlLm1lbnVDb250YWluZXJcbiAgICAgICAgICAgID8gdGhpcy50cmlidXRlLm1lbnVDb250YWluZXIub2Zmc2V0SGVpZ2h0XG4gICAgICAgICAgICA6IHRoaXMuZ2V0RG9jdW1lbnQoKS5ib2R5Lm9mZnNldEhlaWdodFxuXG4gICAgICAgIGlmIChtZW51SXNPZmZTY3JlZW4uYm90dG9tKSB7XG4gICAgICAgICAgICBsZXQgcGFyZW50UmVjdCA9IHRoaXMudHJpYnV0ZS5tZW51Q29udGFpbmVyXG4gICAgICAgICAgICAgICAgPyB0aGlzLnRyaWJ1dGUubWVudUNvbnRhaW5lci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgICAgICAgICAgICAgIDogdGhpcy5nZXREb2N1bWVudCgpLmJvZHkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgICAgIGxldCBzY3JvbGxTdGlsbEF2YWlsYWJsZSA9IHBhcmVudEhlaWdodCAtICh3aW5kb3dIZWlnaHQgLSBwYXJlbnRSZWN0LnRvcClcblxuICAgICAgICAgICAgY29vcmRpbmF0ZXMuYm90dG9tID0gc2Nyb2xsU3RpbGxBdmFpbGFibGUgKyAod2luZG93SGVpZ2h0IC0gcmVjdC50b3AgLSBzcGFuLm9mZnNldFRvcClcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLnRvcCA9ICdhdXRvJ1xuICAgICAgICB9XG5cbiAgICAgICAgbWVudUlzT2ZmU2NyZWVuID0gdGhpcy5pc01lbnVPZmZTY3JlZW4oY29vcmRpbmF0ZXMsIG1lbnVEaW1lbnNpb25zKVxuICAgICAgICBpZiAobWVudUlzT2ZmU2NyZWVuLmxlZnQpIHtcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLmxlZnQgPSB3aW5kb3dXaWR0aCA+IG1lbnVEaW1lbnNpb25zLndpZHRoXG4gICAgICAgICAgICAgICAgPyB3aW5kb3dMZWZ0ICsgd2luZG93V2lkdGggLSBtZW51RGltZW5zaW9ucy53aWR0aFxuICAgICAgICAgICAgICAgIDogd2luZG93TGVmdFxuICAgICAgICAgICAgZGVsZXRlIGNvb3JkaW5hdGVzLnJpZ2h0XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1lbnVJc09mZlNjcmVlbi50b3ApIHtcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLnRvcCA9IHdpbmRvd0hlaWdodCA+IG1lbnVEaW1lbnNpb25zLmhlaWdodFxuICAgICAgICAgICAgICAgID8gd2luZG93VG9wICsgd2luZG93SGVpZ2h0IC0gbWVudURpbWVuc2lvbnMuaGVpZ2h0XG4gICAgICAgICAgICAgICAgOiB3aW5kb3dUb3BcbiAgICAgICAgICAgIGRlbGV0ZSBjb29yZGluYXRlcy5ib3R0b21cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZ2V0RG9jdW1lbnQoKS5ib2R5LnJlbW92ZUNoaWxkKGRpdilcbiAgICAgICAgcmV0dXJuIGNvb3JkaW5hdGVzXG4gICAgfVxuXG4gICAgZ2V0Q29udGVudEVkaXRhYmxlQ2FyZXRQb3NpdGlvbihzZWxlY3RlZE5vZGVQb3NpdGlvbikge1xuICAgICAgICBsZXQgbWFya2VyVGV4dENoYXIgPSAn77u/J1xuICAgICAgICBsZXQgbWFya2VyRWwsIG1hcmtlcklkID0gYHNlbF8ke25ldyBEYXRlKCkuZ2V0VGltZSgpfV8ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoKS5zdWJzdHIoMil9YFxuICAgICAgICBsZXQgcmFuZ2VcbiAgICAgICAgbGV0IHNlbCA9IHRoaXMuZ2V0V2luZG93U2VsZWN0aW9uKClcbiAgICAgICAgbGV0IHByZXZSYW5nZSA9IHNlbC5nZXRSYW5nZUF0KDApXG5cbiAgICAgICAgcmFuZ2UgPSB0aGlzLmdldERvY3VtZW50KCkuY3JlYXRlUmFuZ2UoKVxuICAgICAgICByYW5nZS5zZXRTdGFydChzZWwuYW5jaG9yTm9kZSwgc2VsZWN0ZWROb2RlUG9zaXRpb24pXG4gICAgICAgIHJhbmdlLnNldEVuZChzZWwuYW5jaG9yTm9kZSwgc2VsZWN0ZWROb2RlUG9zaXRpb24pXG5cbiAgICAgICAgcmFuZ2UuY29sbGFwc2UoZmFsc2UpXG5cbiAgICAgICAgLy8gQ3JlYXRlIHRoZSBtYXJrZXIgZWxlbWVudCBjb250YWluaW5nIGEgc2luZ2xlIGludmlzaWJsZSBjaGFyYWN0ZXIgdXNpbmcgRE9NIG1ldGhvZHMgYW5kIGluc2VydCBpdFxuICAgICAgICBtYXJrZXJFbCA9IHRoaXMuZ2V0RG9jdW1lbnQoKS5jcmVhdGVFbGVtZW50KCdzcGFuJylcbiAgICAgICAgbWFya2VyRWwuaWQgPSBtYXJrZXJJZFxuXG4gICAgICAgIG1hcmtlckVsLmFwcGVuZENoaWxkKHRoaXMuZ2V0RG9jdW1lbnQoKS5jcmVhdGVUZXh0Tm9kZShtYXJrZXJUZXh0Q2hhcikpXG4gICAgICAgIHJhbmdlLmluc2VydE5vZGUobWFya2VyRWwpXG4gICAgICAgIHNlbC5yZW1vdmVBbGxSYW5nZXMoKVxuICAgICAgICBzZWwuYWRkUmFuZ2UocHJldlJhbmdlKVxuXG4gICAgICAgIGxldCByZWN0ID0gbWFya2VyRWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgbGV0IGRvYyA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudFxuICAgICAgICBsZXQgd2luZG93TGVmdCA9ICh3aW5kb3cucGFnZVhPZmZzZXQgfHwgZG9jLnNjcm9sbExlZnQpIC0gKGRvYy5jbGllbnRMZWZ0IHx8IDApXG4gICAgICAgIGxldCB3aW5kb3dUb3AgPSAod2luZG93LnBhZ2VZT2Zmc2V0IHx8IGRvYy5zY3JvbGxUb3ApIC0gKGRvYy5jbGllbnRUb3AgfHwgMClcbiAgICAgICAgbGV0IGNvb3JkaW5hdGVzID0ge1xuICAgICAgICAgICAgbGVmdDogcmVjdC5sZWZ0ICsgd2luZG93TGVmdCxcbiAgICAgICAgICAgIHRvcDogcmVjdC50b3AgKyBtYXJrZXJFbC5vZmZzZXRIZWlnaHQgKyB3aW5kb3dUb3BcbiAgICAgICAgfVxuICAgICAgICBsZXQgd2luZG93V2lkdGggPSB3aW5kb3cuaW5uZXJXaWR0aFxuICAgICAgICBsZXQgd2luZG93SGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0XG5cbiAgICAgICAgbGV0IG1lbnVEaW1lbnNpb25zID0gdGhpcy5nZXRNZW51RGltZW5zaW9ucygpXG4gICAgICAgIGxldCBtZW51SXNPZmZTY3JlZW4gPSB0aGlzLmlzTWVudU9mZlNjcmVlbihjb29yZGluYXRlcywgbWVudURpbWVuc2lvbnMpXG5cbiAgICAgICAgaWYgKG1lbnVJc09mZlNjcmVlbi5yaWdodCkge1xuICAgICAgICAgICAgY29vcmRpbmF0ZXMubGVmdCA9ICdhdXRvJ1xuICAgICAgICAgICAgY29vcmRpbmF0ZXMucmlnaHQgPSB3aW5kb3dXaWR0aCAtIHJlY3QubGVmdCAtIHdpbmRvd0xlZnRcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBwYXJlbnRIZWlnaHQgPSB0aGlzLnRyaWJ1dGUubWVudUNvbnRhaW5lclxuICAgICAgICAgICAgPyB0aGlzLnRyaWJ1dGUubWVudUNvbnRhaW5lci5vZmZzZXRIZWlnaHRcbiAgICAgICAgICAgIDogdGhpcy5nZXREb2N1bWVudCgpLmJvZHkub2Zmc2V0SGVpZ2h0XG5cbiAgICAgICAgaWYgKG1lbnVJc09mZlNjcmVlbi5ib3R0b20pIHtcbiAgICAgICAgICAgIGxldCBwYXJlbnRSZWN0ID0gdGhpcy50cmlidXRlLm1lbnVDb250YWluZXJcbiAgICAgICAgICAgICAgICA/IHRoaXMudHJpYnV0ZS5tZW51Q29udGFpbmVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG4gICAgICAgICAgICAgICAgOiB0aGlzLmdldERvY3VtZW50KCkuYm9keS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgICAgICAgICAgbGV0IHNjcm9sbFN0aWxsQXZhaWxhYmxlID0gcGFyZW50SGVpZ2h0IC0gKHdpbmRvd0hlaWdodCAtIHBhcmVudFJlY3QudG9wKVxuXG4gICAgICAgICAgICBjb29yZGluYXRlcy50b3AgPSAnYXV0bydcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLmJvdHRvbSA9IHNjcm9sbFN0aWxsQXZhaWxhYmxlICsgKHdpbmRvd0hlaWdodCAtIHJlY3QudG9wKVxuICAgICAgICB9XG5cbiAgICAgICAgbWVudUlzT2ZmU2NyZWVuID0gdGhpcy5pc01lbnVPZmZTY3JlZW4oY29vcmRpbmF0ZXMsIG1lbnVEaW1lbnNpb25zKVxuICAgICAgICBpZiAobWVudUlzT2ZmU2NyZWVuLmxlZnQpIHtcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLmxlZnQgPSB3aW5kb3dXaWR0aCA+IG1lbnVEaW1lbnNpb25zLndpZHRoXG4gICAgICAgICAgICAgICAgPyB3aW5kb3dMZWZ0ICsgd2luZG93V2lkdGggLSBtZW51RGltZW5zaW9ucy53aWR0aFxuICAgICAgICAgICAgICAgIDogd2luZG93TGVmdFxuICAgICAgICAgICAgZGVsZXRlIGNvb3JkaW5hdGVzLnJpZ2h0XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1lbnVJc09mZlNjcmVlbi50b3ApIHtcbiAgICAgICAgICAgIGNvb3JkaW5hdGVzLnRvcCA9IHdpbmRvd0hlaWdodCA+IG1lbnVEaW1lbnNpb25zLmhlaWdodFxuICAgICAgICAgICAgICAgID8gd2luZG93VG9wICsgd2luZG93SGVpZ2h0IC0gbWVudURpbWVuc2lvbnMuaGVpZ2h0XG4gICAgICAgICAgICAgICAgOiB3aW5kb3dUb3BcbiAgICAgICAgICAgIGRlbGV0ZSBjb29yZGluYXRlcy5ib3R0b21cbiAgICAgICAgfVxuXG4gICAgICAgIG1hcmtlckVsLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQobWFya2VyRWwpXG4gICAgICAgIHJldHVybiBjb29yZGluYXRlc1xuICAgIH1cblxuICAgIHNjcm9sbEludG9WaWV3KGVsZW0pIHtcbiAgICAgICAgbGV0IHJlYXNvbmFibGVCdWZmZXIgPSAyMCxcbiAgICAgICAgICAgIGNsaWVudFJlY3RcbiAgICAgICAgbGV0IG1heFNjcm9sbERpc3BsYWNlbWVudCA9IDEwMFxuICAgICAgICBsZXQgZSA9IHRoaXMubWVudVxuXG4gICAgICAgIGlmICh0eXBlb2YgZSA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybjtcblxuICAgICAgICB3aGlsZSAoY2xpZW50UmVjdCA9PT0gdW5kZWZpbmVkIHx8IGNsaWVudFJlY3QuaGVpZ2h0ID09PSAwKSB7XG4gICAgICAgICAgICBjbGllbnRSZWN0ID0gZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuXG4gICAgICAgICAgICBpZiAoY2xpZW50UmVjdC5oZWlnaHQgPT09IDApIHtcbiAgICAgICAgICAgICAgICBlID0gZS5jaGlsZE5vZGVzWzBdXG4gICAgICAgICAgICAgICAgaWYgKGUgPT09IHVuZGVmaW5lZCB8fCAhZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGVsZW1Ub3AgPSBjbGllbnRSZWN0LnRvcFxuICAgICAgICBsZXQgZWxlbUJvdHRvbSA9IGVsZW1Ub3AgKyBjbGllbnRSZWN0LmhlaWdodFxuXG4gICAgICAgIGlmIChlbGVtVG9wIDwgMCkge1xuICAgICAgICAgICAgd2luZG93LnNjcm9sbFRvKDAsIHdpbmRvdy5wYWdlWU9mZnNldCArIGNsaWVudFJlY3QudG9wIC0gcmVhc29uYWJsZUJ1ZmZlcilcbiAgICAgICAgfSBlbHNlIGlmIChlbGVtQm90dG9tID4gd2luZG93LmlubmVySGVpZ2h0KSB7XG4gICAgICAgICAgICBsZXQgbWF4WSA9IHdpbmRvdy5wYWdlWU9mZnNldCArIGNsaWVudFJlY3QudG9wIC0gcmVhc29uYWJsZUJ1ZmZlclxuXG4gICAgICAgICAgICBpZiAobWF4WSAtIHdpbmRvdy5wYWdlWU9mZnNldCA+IG1heFNjcm9sbERpc3BsYWNlbWVudCkge1xuICAgICAgICAgICAgICAgIG1heFkgPSB3aW5kb3cucGFnZVlPZmZzZXQgKyBtYXhTY3JvbGxEaXNwbGFjZW1lbnRcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGV0IHRhcmdldFkgPSB3aW5kb3cucGFnZVlPZmZzZXQgLSAod2luZG93LmlubmVySGVpZ2h0IC0gZWxlbUJvdHRvbSlcblxuICAgICAgICAgICAgaWYgKHRhcmdldFkgPiBtYXhZKSB7XG4gICAgICAgICAgICAgICAgdGFyZ2V0WSA9IG1heFlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgd2luZG93LnNjcm9sbFRvKDAsIHRhcmdldFkpXG4gICAgICAgIH1cbiAgICB9XG59XG5cblxuZXhwb3J0IGRlZmF1bHQgVHJpYnV0ZVJhbmdlO1xuIiwiLy8gVGhhbmtzIHRvIGh0dHBzOi8vZ2l0aHViLmNvbS9tYXR0eW9yay9mdXp6eVxuY2xhc3MgVHJpYnV0ZVNlYXJjaCB7XG4gICAgY29uc3RydWN0b3IodHJpYnV0ZSkge1xuICAgICAgICB0aGlzLnRyaWJ1dGUgPSB0cmlidXRlXG4gICAgICAgIHRoaXMudHJpYnV0ZS5zZWFyY2ggPSB0aGlzXG4gICAgfVxuXG4gICAgc2ltcGxlRmlsdGVyKHBhdHRlcm4sIGFycmF5KSB7XG4gICAgICAgIHJldHVybiBhcnJheS5maWx0ZXIoc3RyaW5nID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnRlc3QocGF0dGVybiwgc3RyaW5nKVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIHRlc3QocGF0dGVybiwgc3RyaW5nKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm1hdGNoKHBhdHRlcm4sIHN0cmluZykgIT09IG51bGxcbiAgICB9XG5cbiAgICBtYXRjaChwYXR0ZXJuLCBzdHJpbmcsIG9wdHMpIHtcbiAgICAgICAgb3B0cyA9IG9wdHMgfHwge31cbiAgICAgICAgbGV0IHBhdHRlcm5JZHggPSAwLFxuICAgICAgICAgICAgcmVzdWx0ID0gW10sXG4gICAgICAgICAgICBsZW4gPSBzdHJpbmcubGVuZ3RoLFxuICAgICAgICAgICAgdG90YWxTY29yZSA9IDAsXG4gICAgICAgICAgICBjdXJyU2NvcmUgPSAwLFxuICAgICAgICAgICAgcHJlID0gb3B0cy5wcmUgfHwgJycsXG4gICAgICAgICAgICBwb3N0ID0gb3B0cy5wb3N0IHx8ICcnLFxuICAgICAgICAgICAgY29tcGFyZVN0cmluZyA9IG9wdHMuY2FzZVNlbnNpdGl2ZSAmJiBzdHJpbmcgfHwgc3RyaW5nLnRvTG93ZXJDYXNlKCksXG4gICAgICAgICAgICBjaCwgY29tcGFyZUNoYXJcblxuICAgICAgICBwYXR0ZXJuID0gb3B0cy5jYXNlU2Vuc2l0aXZlICYmIHBhdHRlcm4gfHwgcGF0dGVybi50b0xvd2VyQ2FzZSgpXG5cbiAgICAgICAgbGV0IHBhdHRlcm5DYWNoZSA9IHRoaXMudHJhdmVyc2UoY29tcGFyZVN0cmluZywgcGF0dGVybiwgMCwgMCwgW10pXG4gICAgICAgIGlmICghcGF0dGVybkNhY2hlKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHJlbmRlcmVkOiB0aGlzLnJlbmRlcihzdHJpbmcsIHBhdHRlcm5DYWNoZS5jYWNoZSwgcHJlLCBwb3N0KSxcbiAgICAgICAgICAgIHNjb3JlOiBwYXR0ZXJuQ2FjaGUuc2NvcmVcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRyYXZlcnNlKHN0cmluZywgcGF0dGVybiwgc3RyaW5nSW5kZXgsIHBhdHRlcm5JbmRleCwgcGF0dGVybkNhY2hlKSB7XG4gICAgICAgIC8vIGlmIHRoZSBwYXR0ZXJuIHNlYXJjaCBhdCBlbmRcbiAgICAgICAgaWYgKHBhdHRlcm4ubGVuZ3RoID09PSBwYXR0ZXJuSW5kZXgpIHtcblxuICAgICAgICAgICAgLy8gY2FsY3VsYXRlIHNjb3JlIGFuZCBjb3B5IHRoZSBjYWNoZSBjb250YWluaW5nIHRoZSBpbmRpY2VzIHdoZXJlIGl0J3MgZm91bmRcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc2NvcmU6IHRoaXMuY2FsY3VsYXRlU2NvcmUocGF0dGVybkNhY2hlKSxcbiAgICAgICAgICAgICAgICBjYWNoZTogcGF0dGVybkNhY2hlLnNsaWNlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGlmIHN0cmluZyBhdCBlbmQgb3IgcmVtYWluaW5nIHBhdHRlcm4gPiByZW1haW5pbmcgc3RyaW5nXG4gICAgICAgIGlmIChzdHJpbmcubGVuZ3RoID09PSBzdHJpbmdJbmRleCB8fCBwYXR0ZXJuLmxlbmd0aCAtIHBhdHRlcm5JbmRleCA+IHN0cmluZy5sZW5ndGggLSBzdHJpbmdJbmRleCkge1xuICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGMgPSBwYXR0ZXJuW3BhdHRlcm5JbmRleF1cbiAgICAgICAgbGV0IGluZGV4ID0gc3RyaW5nLmluZGV4T2YoYywgc3RyaW5nSW5kZXgpXG4gICAgICAgIGxldCBiZXN0LCB0ZW1wXG5cbiAgICAgICAgd2hpbGUgKGluZGV4ID4gLTEpIHtcbiAgICAgICAgICAgIHBhdHRlcm5DYWNoZS5wdXNoKGluZGV4KVxuICAgICAgICAgICAgdGVtcCA9IHRoaXMudHJhdmVyc2Uoc3RyaW5nLCBwYXR0ZXJuLCBpbmRleCArIDEsIHBhdHRlcm5JbmRleCArIDEsIHBhdHRlcm5DYWNoZSlcbiAgICAgICAgICAgIHBhdHRlcm5DYWNoZS5wb3AoKVxuXG4gICAgICAgICAgICAvLyBpZiBkb3duc3RyZWFtIHRyYXZlcnNhbCBmYWlsZWQsIHJldHVybiBiZXN0IGFuc3dlciBzbyBmYXJcbiAgICAgICAgICAgIGlmICghdGVtcCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBiZXN0XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghYmVzdCB8fCBiZXN0LnNjb3JlIDwgdGVtcC5zY29yZSkge1xuICAgICAgICAgICAgICAgIGJlc3QgPSB0ZW1wXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGluZGV4ID0gc3RyaW5nLmluZGV4T2YoYywgaW5kZXggKyAxKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGJlc3RcbiAgICB9XG5cbiAgICBjYWxjdWxhdGVTY29yZShwYXR0ZXJuQ2FjaGUpIHtcbiAgICAgICAgbGV0IHNjb3JlID0gMFxuICAgICAgICBsZXQgdGVtcCA9IDFcblxuICAgICAgICBwYXR0ZXJuQ2FjaGUuZm9yRWFjaCgoaW5kZXgsIGkpID0+IHtcbiAgICAgICAgICAgIGlmIChpID4gMCkge1xuICAgICAgICAgICAgICAgIGlmIChwYXR0ZXJuQ2FjaGVbaSAtIDFdICsgMSA9PT0gaW5kZXgpIHtcbiAgICAgICAgICAgICAgICAgICAgdGVtcCArPSB0ZW1wICsgMVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdGVtcCA9IDFcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNjb3JlICs9IHRlbXBcbiAgICAgICAgfSlcblxuICAgICAgICByZXR1cm4gc2NvcmVcbiAgICB9XG5cbiAgICByZW5kZXIoc3RyaW5nLCBpbmRpY2VzLCBwcmUsIHBvc3QpIHtcbiAgICAgICAgdmFyIHJlbmRlcmVkID0gc3RyaW5nLnN1YnN0cmluZygwLCBpbmRpY2VzWzBdKVxuXG4gICAgICAgIGluZGljZXMuZm9yRWFjaCgoaW5kZXgsIGkpID0+IHtcbiAgICAgICAgICAgIHJlbmRlcmVkICs9IHByZSArIHN0cmluZ1tpbmRleF0gKyBwb3N0ICtcbiAgICAgICAgICAgICAgICBzdHJpbmcuc3Vic3RyaW5nKGluZGV4ICsgMSwgKGluZGljZXNbaSArIDFdKSA/IGluZGljZXNbaSArIDFdIDogc3RyaW5nLmxlbmd0aClcbiAgICAgICAgfSlcblxuICAgICAgICByZXR1cm4gcmVuZGVyZWRcbiAgICB9XG5cbiAgICBmaWx0ZXIocGF0dGVybiwgYXJyLCBvcHRzKSB7XG4gICAgICAgIG9wdHMgPSBvcHRzIHx8IHt9XG4gICAgICAgIHJldHVybiBhcnJcbiAgICAgICAgICAgIC5yZWR1Y2UoKHByZXYsIGVsZW1lbnQsIGlkeCwgYXJyKSA9PiB7XG4gICAgICAgICAgICAgICAgbGV0IHN0ciA9IGVsZW1lbnRcblxuICAgICAgICAgICAgICAgIGlmIChvcHRzLmV4dHJhY3QpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gb3B0cy5leHRyYWN0KGVsZW1lbnQpXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFzdHIpIHsgLy8gdGFrZSBjYXJlIG9mIHVuZGVmaW5lZHMgLyBudWxscyAvIGV0Yy5cbiAgICAgICAgICAgICAgICAgICAgICAgIHN0ciA9ICcnXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBsZXQgcmVuZGVyZWQgPSB0aGlzLm1hdGNoKHBhdHRlcm4sIHN0ciwgb3B0cylcblxuICAgICAgICAgICAgICAgIGlmIChyZW5kZXJlZCAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHByZXZbcHJldi5sZW5ndGhdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nOiByZW5kZXJlZC5yZW5kZXJlZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjb3JlOiByZW5kZXJlZC5zY29yZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGluZGV4OiBpZHgsXG4gICAgICAgICAgICAgICAgICAgICAgICBvcmlnaW5hbDogZWxlbWVudFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgICAgIH0sIFtdKVxuXG4gICAgICAgIC5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgICAgICBsZXQgY29tcGFyZSA9IGIuc2NvcmUgLSBhLnNjb3JlXG4gICAgICAgICAgICBpZiAoY29tcGFyZSkgcmV0dXJuIGNvbXBhcmVcbiAgICAgICAgICAgIHJldHVybiBhLmluZGV4IC0gYi5pbmRleFxuICAgICAgICB9KVxuICAgIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgVHJpYnV0ZVNlYXJjaDtcbiIsIi8qKlxuKiBUcmlidXRlLmpzXG4qIE5hdGl2ZSBFUzYgSmF2YVNjcmlwdCBAbWVudGlvbiBQbHVnaW5cbioqL1xuXG5pbXBvcnQgVHJpYnV0ZSBmcm9tIFwiLi9UcmlidXRlXCI7XG5cbmV4cG9ydCBkZWZhdWx0IFRyaWJ1dGU7XG4iLCJpZiAoIUFycmF5LnByb3RvdHlwZS5maW5kKSB7XG4gICAgQXJyYXkucHJvdG90eXBlLmZpbmQgPSBmdW5jdGlvbihwcmVkaWNhdGUpIHtcbiAgICAgICAgaWYgKHRoaXMgPT09IG51bGwpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FycmF5LnByb3RvdHlwZS5maW5kIGNhbGxlZCBvbiBudWxsIG9yIHVuZGVmaW5lZCcpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHR5cGVvZiBwcmVkaWNhdGUgIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ3ByZWRpY2F0ZSBtdXN0IGJlIGEgZnVuY3Rpb24nKVxuICAgICAgICB9XG4gICAgICAgIHZhciBsaXN0ID0gT2JqZWN0KHRoaXMpXG4gICAgICAgIHZhciBsZW5ndGggPSBsaXN0Lmxlbmd0aCA+Pj4gMFxuICAgICAgICB2YXIgdGhpc0FyZyA9IGFyZ3VtZW50c1sxXVxuICAgICAgICB2YXIgdmFsdWVcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IGxpc3RbaV1cbiAgICAgICAgICAgIGlmIChwcmVkaWNhdGUuY2FsbCh0aGlzQXJnLCB2YWx1ZSwgaSwgbGlzdCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdmFsdWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgfVxufVxuXG5pZiAod2luZG93ICYmIHR5cGVvZiB3aW5kb3cuQ3VzdG9tRXZlbnQgIT09IFwiZnVuY3Rpb25cIikge1xuICBmdW5jdGlvbiBDdXN0b21FdmVudChldmVudCwgcGFyYW1zKSB7XG4gICAgcGFyYW1zID0gcGFyYW1zIHx8IHtcbiAgICAgIGJ1YmJsZXM6IGZhbHNlLFxuICAgICAgY2FuY2VsYWJsZTogZmFsc2UsXG4gICAgICBkZXRhaWw6IHVuZGVmaW5lZFxuICAgIH1cbiAgICB2YXIgZXZ0ID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ0N1c3RvbUV2ZW50JylcbiAgICBldnQuaW5pdEN1c3RvbUV2ZW50KGV2ZW50LCBwYXJhbXMuYnViYmxlcywgcGFyYW1zLmNhbmNlbGFibGUsIHBhcmFtcy5kZXRhaWwpXG4gICAgcmV0dXJuIGV2dFxuICB9XG5cbiBpZiAodHlwZW9mIHdpbmRvdy5FdmVudCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgIEN1c3RvbUV2ZW50LnByb3RvdHlwZSA9IHdpbmRvdy5FdmVudC5wcm90b3R5cGVcbiB9XG5cbiAgd2luZG93LkN1c3RvbUV2ZW50ID0gQ3VzdG9tRXZlbnRcbn0iXX0=
  • src/bp-friends/bp-friends-functions.php

    diff --git src/bp-friends/bp-friends-functions.php src/bp-friends/bp-friends-functions.php
    index 051c65018..cf4ae4c0f 100644
    function bp_friends_prime_mentions_results() { 
    804804                $result        = new stdClass();
    805805                $result->ID    = $user->user_nicename;
    806806                $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) );
     807                $result->user_id = $user->ID;
    807808
    808809                if ( ! empty( $user->display_name ) && ! bp_disable_profile_sync() ) {
    809810                        $result->name = $user->display_name;
    810811                } else {
    811812                        $result->name = bp_core_get_user_displayname( $user->ID );
    812813                }
     814                $result->search = "{$result->ID} {$result->name}";
    813815
    814816                $results[] = $result;
    815817        }