Skip to:
Content

BuddyPress.org

Ticket #8001: 8001.tribute01.diff

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

Use Tribute for @-lookups of users.

Line 
1diff --git .gitignore .gitignore
2index 769dc445e..c4f380218 100644
3--- .gitignore
4+++ .gitignore
5@@ -14,8 +14,8 @@ lib-cov
6 pids
7 logs
8 results
9-src/vendor
10-vendor
11+#src/vendor
12+#vendor
13 
14 node_modules
15 npm-debug.log
16diff --git src/bp-activity/bp-activity-cssjs.php src/bp-activity/bp-activity-cssjs.php
17index ec14cd416..945a0ad75 100644
18--- src/bp-activity/bp-activity-cssjs.php
19+++ src/bp-activity/bp-activity-cssjs.php
20@@ -33,7 +33,7 @@ function bp_activity_mentions_script() {
21 
22        $min = bp_core_get_minified_asset_suffix();
23 
24-       wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( 'jquery', 'jquery-atwho' ), bp_get_version(), true );
25+       wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( 'zurb-tribute' ), bp_get_version(), true );
26        wp_enqueue_style( 'bp-mentions-css', buddypress()->plugin_url . "bp-activity/css/mentions{$min}.css", array(), bp_get_version() );
27 
28        wp_style_add_data( 'bp-mentions-css', 'rtl', true );
29diff --git src/bp-activity/css/mentions.css src/bp-activity/css/mentions.css
30index 7773c75ec..f7bc47d12 100644
31--- src/bp-activity/css/mentions.css
32+++ src/bp-activity/css/mentions.css
33@@ -1,107 +1,41 @@
34-.atwho-view {
35-       background: rgba(204, 204, 204, 0.8);
36-       border-radius: 2px;
37-       border: 1px solid rgb(204, 204, 204);
38-       box-shadow: 0 0 5px rgba(204, 204, 204, 0.25), 0 0 1px #fff;
39-       color: #d84800;
40-       display: none;
41-       font-family: sans-serif;
42-       margin-top: 18px;
43+.tribute-container {
44        position: absolute;
45        top: 0;
46-       z-index: 1000; /* >999 for wp-admin */
47-}
48-
49-/* rtl:ignore */
50-.atwho-view {
51        left: 0;
52+       height: auto;
53+       max-height: 300px;
54+       max-width: 500px;
55+       overflow: auto;
56+       display: block;
57+       z-index: 999999;
58 }
59-
60-.atwho-view ul {
61-       background: #fff;
62-       list-style: none;
63-       margin: auto;
64+.tribute-container ul {
65+       margin: 0;
66+       margin-top: 2px;
67        padding: 0;
68+       list-style: none;
69+       background: #efefef;
70 }
71-
72-.atwho-view ul li {
73-       border-bottom: 1px solid #efefef;
74-       box-sizing: content-box;
75+.tribute-container li {
76+       padding: 5px 5px;
77        cursor: pointer;
78-       display: block;
79-       font-size: 14px;
80-       height: 20px;
81-       line-height: 20px;
82-       margin: 0;
83-       overflow: hidden;
84-       padding: 5px 10px;
85-}
86-
87-.atwho-view img {
88-       border-radius: 2px;
89-       float: right;
90-       height: 20px;
91-       margin-top: 0;
92-       width: 20px;
93 }
94-
95-.atwho-view strong {
96-       background: #efefef;
97-       font-weight: 700;
98+.tribute-container li.highlight, .tribute-container li:hover {
99+       background: #ddd;
100 }
101-
102-.atwho-view .username strong {
103-       color: #d54e21;
104+.tribute-container li span {
105+       font-weight: bold;
106 }
107-
108-.atwho-view small {
109-       color: #aaa;
110-       float: right;
111-       font-size: smaller;
112-       font-weight: 400;
113-       margin: 0 10px 0 40px;
114+.tribute-container li.no-match {
115+       cursor: default;
116 }
117-
118-.atwho-view .cur {
119-       background: rgba(239, 239, 239, 0.5);
120+.tribute-container .menu-highlighted {
121+       font-weight: bold;
122 }
123-
124-@media (max-width: 900px) {
125-
126-       .atwho-view img {
127-               float: left;
128-               margin: 0 10px 0 0;
129-       }
130-}
131-
132-@media (max-width: 400px) {
133-
134-       .atwho-view ul li {
135-               font-size: 16px;
136-               line-height: 23px;
137-               padding: 13px;
138-       }
139-
140-       .atwho-view ul li img {
141-               height: 30px;
142-               margin-top: -5px;
143-               width: 30px;
144-       }
145-
146-       .atwho-view {
147-               border-radius: 0;
148-               left: 0 !important;
149-               width: 100%;
150-       }
151-
152-       .atwho-view ul li .username {
153-               display: inline-block;
154-               margin: -10px 0 0 0;
155-               padding: 10px 0;
156-       }
157-
158-       .atwho-view ul li small {
159-               display: inline-block;
160-               margin-left: 20px;
161-       }
162+.tribute-container li img {
163+       border-radius: 2px;
164+       float: right;
165+       height: 20px;
166+       /*margin-top: 0;*/
167+       width: 20px;
168 }
169diff --git src/bp-activity/js/mentions.js src/bp-activity/js/mentions.js
170index 2d0afa3ef..882f3690d 100644
171--- src/bp-activity/js/mentions.js
172+++ src/bp-activity/js/mentions.js
173@@ -16,241 +16,107 @@ window.bp = window.bp || {};
174        /**
175         * Adds BuddyPress @mentions to form inputs.
176         *
177-        * @param {array|object} options If array, becomes the suggestions' data source. If object, passed as config to $.atwho().
178+        * @param {array} defaultList If array, becomes the suggestions' default data source.
179         * @since 2.1.0
180         */
181-       $.fn.bp_mentions = function( options ) {
182-               if ( $.isArray( options ) ) {
183-                       options = { data: options };
184-               }
185-
186-               /**
187-                * Default options for at.js; see https://github.com/ichord/At.js/.
188-                */
189-               var suggestionsDefaults = {
190-                       delay:             200,
191-                       hideWithoutSuffix: true,
192-                       insertTpl:         '@${ID}',
193-                       limit:             10,
194-                       startWithSpace:    false,
195-                       suffix:            '',
196-
197-                       callbacks: {
198-                               /**
199-                                * Custom filter to only match the start of spaced words.
200-                                * Based on the core/default one.
201-                                *
202-                                * @param {string} query
203-                                * @param {array} data
204-                                * @param {string} search_key
205-                                * @return {array}
206-                                * @since 2.1.0
207-                                */
208-                               filter: function( query, data, search_key ) {
209-                                       var item, _i, _len, _results = [],
210-                                       regxp = new RegExp( '^' + query + '| ' + query, 'ig' ); // start of string, or preceded by a space.
211-
212-                                       for ( _i = 0, _len = data.length; _i < _len; _i++ ) {
213-                                               item = data[ _i ];
214-                                               if ( item[ search_key ].toLowerCase().match( regxp ) ) {
215-                                                       _results.push( item );
216-                                               }
217-                                       }
218-
219-                                       return _results;
220-                               },
221-
222-                               /**
223-                                * Removes some spaces around highlighted string and tweaks regex to allow spaces
224-                                * (to match display_name). Based on the core default.
225-                                *
226-                                * @param {unknown} li
227-                                * @param {string} query
228-                                * @return {string}
229-                                * @since 2.1.0
230-                                */
231-                               highlighter: function( li, query ) {
232-                                       if ( ! query ) {
233-                                               return li;
234-                                       }
235-
236-                                       var regexp = new RegExp( '>(\\s*|[\\w\\s]*)(' + this.at.replace( '+', '\\+') + '?' + query.replace( '+', '\\+' ) + ')([\\w ]*)\\s*<', 'ig' );
237-                                       return li.replace( regexp, function( str, $1, $2, $3 ) {
238-                                               return '>' + $1 + '<strong>' + $2 + '</strong>' + $3 + '<';
239-                                       });
240-                               },
241-
242-                               /**
243-                                * Reposition the suggestion list dynamically.
244-                                *
245-                                * @param {unknown} offset
246-                                * @since 2.1.0
247-                                */
248-                               before_reposition: function( offset ) {
249-                                       // get the iframe, if any, already applied with atwho
250-                                       var caret,
251-                                                       line,
252-                                                       iframeOffset,
253-                                                       move,
254-                                                       $view = $( '#atwho-ground-' + this.id + ' .atwho-view' ),
255-                                                       $body = $( 'body' ),
256-                                                       atwhoDataValue = this.$inputor.data( 'atwho' );
257-
258-                                       if ( 'undefined' !== atwhoDataValue && 'undefined' !== atwhoDataValue.iframe && null !== atwhoDataValue.iframe ) {
259-                                               caret = this.$inputor.caret( 'offset', { iframe: atwhoDataValue.iframe } );
260-                                               // Caret.js no longer calculates iframe caret position from the window (it's now just within the iframe).
261-                                               // We need to get the iframe offset from the window and merge that into our object.
262-                                               iframeOffset = $( atwhoDataValue.iframe ).offset();
263-                                               if ( 'undefined' !== iframeOffset ) {
264-                                                       caret.left += iframeOffset.left;
265-                                                       caret.top  += iframeOffset.top;
266-                                               }
267-                                       } else {
268-                                               caret = this.$inputor.caret( 'offset' );
269-                                       }
270-
271-                                       // If the caret is past horizontal half, then flip it, yo
272-                                       if ( caret.left > ( $body.width() / 2 ) ) {
273-                                               $view.addClass( 'right' );
274-                                               move = caret.left - offset.left - this.view.$el.width();
275-                                       } else {
276-                                               $view.removeClass( 'right' );
277-                                               move = caret.left - offset.left + 1;
278-                                       }
279-
280-                                       // If we're on a small screen, scroll to caret
281-                                       if ( $body.width() <= 400 ) {
282-                                               $( document ).scrollTop( caret.top - 6 );
283-                                       }
284+       $.fn.bp_mentions = function( defaultList ) {
285+               var debouncer = function(func, wait) {
286+                 let timeout;
287+
288+                 return function() {
289+                       var context = this;
290+                   var args = arguments;
291+
292+                   var callFunction = function() {
293+                       func.apply(context, args)
294+                   };
295+
296+                   clearTimeout(timeout);
297+                   timeout = setTimeout(callFunction, wait);
298+                 }
299+               };
300+
301+               var remoteSearch = function(text, cb) {
302+                       /**
303+                        * Immediately show the pre-created friends list, if it's populated,
304+                        * and the user has hesitated after hitting @ (no search text provided).
305+                        */
306+                       if ( text.length === 0 && $.isArray( defaultList ) && defaultList.length > 0) {
307+                               cb(defaultList);
308+                               return;
309+                       }
310 
311-                                       // New position is under the caret (never above) and positioned to follow
312-                                       // Dynamic sizing based on the input area (remove 'px' from end)
313-                                       line = parseInt( this.$inputor.css( 'line-height' ).substr( 0, this.$inputor.css( 'line-height' ).length - 2 ), 10 );
314-                                       if ( !line || line < 5 ) { // sanity check, and catch no line-height
315-                                               line = 19;
316-                                       }
317+                       mentionsItem = mentionsQueryCache[ text ];
318+                       if ( typeof mentionsItem === 'object' ) {
319+                               cb(mentionsItem);
320+                               return;
321+                       }
322 
323-                                       offset.top   = caret.top + line;
324-                                       offset.left += move;
325-                               },
326+                       var params = { 'action': 'bp_get_suggestions', 'term': text, 'type': 'members' };
327 
328-                               /**
329-                                * Override default behaviour which inserts junk tags in the WordPress Visual editor.
330-                                *
331-                                * @param {unknown} $inputor Element which we're inserting content into.
332-                                * @param {string) content The content that will be inserted.
333-                                * @param {string) suffix Applied to the end of the content string.
334-                                * @return {string}
335-                                * @since 2.1.0
336-                                */
337-                               inserting_wrapper: function( $inputor, content, suffix ) {
338-                                       return '' + content + suffix;
339-                               }
340+                       // Add the group ID to the request if group ID data is attached to the input.
341+                       if ( ".wp-editor-area" === $( this ).selector
342+                               && typeof document.activeElement !== undefined
343+                               && typeof document.activeElement.dataset !== undefined
344+                               && document.activeElement.dataset.suggestionsGroupId !== undefined
345+                               && $.isNumeric( document.activeElement.dataset.suggestionsGroupId ) ) {
346+                               params['group-id'] = parseInt( document.activeElement.dataset.suggestionsGroupId, 10 );
347                        }
348-               },
349 
350-               /**
351-                * Default options for our @mentions; see https://github.com/ichord/At.js/.
352-                */
353-               mentionsDefaults = {
354-                       callbacks: {
355+                       return $.getJSON( ajaxurl, params )
356                                /**
357-                                * If there are no matches for the query in this.data, then query BuddyPress.
358+                                * Success callback for the @suggestions lookup.
359                                 *
360-                                * @param {string} query Partial @mention to search for.
361-                                * @param {function} render_view Render page callback function.
362+                                * @param {object} response Details of users matching the query.
363                                 * @since 2.1.0
364-                                * @since 3.0.0. Renamed from "remote_filter" for at.js v1.5.4 support.
365                                 */
366-                               remoteFilter: function( query, render_view ) {
367-                                       var self = $( this ),
368-                                               params = {};
369-
370-                                       mentionsItem = mentionsQueryCache[ query ];
371-                                       if ( typeof mentionsItem === 'object' ) {
372-                                               render_view( mentionsItem );
373+                               .done(function( response ) {
374+                                       if ( ! response.success ) {
375+                                               cb([]);
376                                                return;
377                                        }
378 
379-                                       if ( self.xhr ) {
380-                                               self.xhr.abort();
381-                                       }
382-
383-                                       params = { 'action': 'bp_get_suggestions', 'term': query, 'type': 'members' };
384-
385-                                       if ( $.isNumeric( this.$inputor.data( 'suggestions-group-id' ) ) ) {
386-                                               params['group-id'] = parseInt( this.$inputor.data( 'suggestions-group-id' ), 10 );
387-                                       }
388-
389-                                       self.xhr = $.getJSON( ajaxurl, params )
390+                                       var data = $.map( response.data,
391                                                /**
392-                                                * Success callback for the @suggestions lookup.
393+                                                * Create a composite index to determine ordering of results;
394+                                                * nicename matches will appear on top.
395                                                 *
396-                                                * @param {object} response Details of users matching the query.
397+                                                * @param {array} suggestion A suggestion's original data.
398+                                                * @return {array} A suggestion's new data.
399                                                 * @since 2.1.0
400                                                 */
401-                                               .done(function( response ) {
402-                                                       if ( ! response.success ) {
403-                                                               return;
404-                                                       }
405+                                               function( suggestion ) {
406+                                                       suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
407+                                                       return suggestion;
408+                                               }
409+                                       );
410 
411-                                                       var data = $.map( response.data,
412-                                                               /**
413-                                                                * Create a composite index to determine ordering of results;
414-                                                                * nicename matches will appear on top.
415-                                                                *
416-                                                                * @param {array} suggestion A suggestion's original data.
417-                                                                * @return {array} A suggestion's new data.
418-                                                                * @since 2.1.0
419-                                                                */
420-                                                               function( suggestion ) {
421-                                                                       suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
422-                                                                       return suggestion;
423-                                                               }
424-                                                       );
425+                                       mentionsQueryCache[ text ] = data;
426+                                       cb(data);
427+                               });
428+               }
429 
430-                                                       mentionsQueryCache[ query ] = data;
431-                                                       render_view( data );
432-                                               });
433-                               }
434+               var tributeParams = {
435+                       values: debouncer( function (text, cb) {
436+                               remoteSearch(text, users => cb(users));
437+                       }, 250),
438+                       lookup: 'search',
439+                       fillAttr: 'ID',
440+                       menuItemTemplate: function (item) {
441+                               return '<img src="' + item.original.image + '" alt="Profile picture of ' + item.original.name + '"> @' + item.string;
442                        },
443+               };
444 
445-                       data: $.map( options.data,
446-                               /**
447-                                * Create a composite index to search against of nicename + display name.
448-                                * This will also determine ordering of results, so nicename matches will appear on top.
449-                                *
450-                                * @param {array} suggestion A suggestion's original data.
451-                                * @return {array} A suggestion's new data.
452-                                * @since 2.1.0
453-                                */
454-                               function( suggestion ) {
455-                                       suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name;
456-                                       return suggestion;
457-                               }
458-                       ),
459+               var tribute = new Tribute( tributeParams );
460 
461-                       at:         '@',
462-                       searchKey:  'search',
463-                       displayTpl: '<li data-value="@${ID}"><img src="${image}" /><span class="username">@${ID}</span><small>${name}</small></li>'
464-               },
465-
466-               opts = $.extend( true, {}, suggestionsDefaults, mentionsDefaults, options );
467-               return $.fn.atwho.call( this, opts );
468+               $( this ).each( function() {
469+                       tribute.attach( document.getElementById( $(this).attr("id") ) );
470+               });
471        };
472 
473        $( document ).ready(function() {
474                // Activity/reply, post comments, dashboard post 'text' editor.
475-               $( '.bp-suggestions, #comments form textarea, .wp-editor-area' ).bp_mentions( bp.mentions.users );
476+               $( '.bp-suggestions, #comments form textarea' ).bp_mentions( bp.mentions.users );
477        });
478 
479-       bp.mentions.tinyMCEinit = function() {
480-               if ( typeof window.tinyMCE === 'undefined' || window.tinyMCE.activeEditor === null || typeof window.tinyMCE.activeEditor === 'undefined' ) {
481-                       return;
482-               } else {
483-                       $( window.tinyMCE.activeEditor.contentDocument.activeElement )
484-                               .atwho( 'setIframe', $( '.wp-editor-wrap iframe' )[0] )
485-                               .bp_mentions( bp.mentions.users );
486-               }
487-       };
488 })( bp, jQuery );
489diff --git src/bp-core/bp-core-cssjs.php src/bp-core/bp-core-cssjs.php
490index 987deb642..4d9eff26a 100644
491--- src/bp-core/bp-core-cssjs.php
492+++ src/bp-core/bp-core-cssjs.php
493@@ -54,10 +54,6 @@ function bp_core_register_common_scripts() {
494                'bp-jquery-cookie'  => array( 'file' => "{$url}vendor/jquery-cookie{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
495                'bp-jquery-scroll-to' => array( 'file' => "{$url}vendor/jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
496 
497-               // Version 2.1.
498-               'jquery-caret' => array( 'file' => "{$url}vendor/jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => true ),
499-               'jquery-atwho' => array( 'file' => "{$url}vendor/jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ), 'footer' => true ),
500-
501                // Version 2.3.
502                'bp-plupload' => array( 'file' => "{$url}bp-plupload{$min}.js", 'dependencies' => array( 'plupload', 'jquery', 'json2', 'wp-backbone' ), 'footer' => true ),
503                'bp-avatar'   => array( 'file' => "{$url}avatar{$min}.js", 'dependencies' => array( 'jcrop' ), 'footer' => true ),
504@@ -69,6 +65,9 @@ function bp_core_register_common_scripts() {
505                // Version 2.7.
506                'bp-moment'    => array( 'file' => "{$url}vendor/moment-js/moment{$min}.js", 'dependencies' => array(), 'footer' => true ),
507                'bp-livestamp' => array( 'file' => "{$url}vendor/livestamp{$min}.js", 'dependencies' => array( 'jquery', 'bp-moment' ), 'footer' => true ),
508+
509+               // Version 5
510+               'zurb-tribute' => array( 'file' => "{$url}vendor/tribute{$min}.js", 'dependencies' => array(), 'footer' => true ),
511        );
512 
513        // Version 2.7 - Add Moment.js locale to our $scripts array if we found one.
514diff --git src/bp-core/js/vendor/jquery.atwho.js src/bp-core/js/vendor/jquery.atwho.js
515deleted file mode 100755
516index 795b6c67d..000000000
517--- src/bp-core/js/vendor/jquery.atwho.js
518+++ /dev/null
519@@ -1,1212 +0,0 @@
520-/**
521- * at.js - 1.5.4
522- * Copyright (c) 2017 chord.luo <chord.luo@gmail.com>;
523- * Homepage: http://ichord.github.com/At.js
524- * License: MIT
525- */
526-(function (root, factory) {
527-  if (typeof define === 'function' && define.amd) {
528-    // AMD. Register as an anonymous module unless amdModuleId is set
529-    define(["jquery"], function (a0) {
530-      return (factory(a0));
531-    });
532-  } else if (typeof exports === 'object') {
533-    // Node. Does not work with strict CommonJS, but
534-    // only CommonJS-like environments that support module.exports,
535-    // like Node.
536-    module.exports = factory(require("jquery"));
537-  } else {
538-    factory(jQuery);
539-  }
540-}(this, function ($) {
541-var DEFAULT_CALLBACKS, KEY_CODE;
542-
543-KEY_CODE = {
544-  ESC: 27,
545-  TAB: 9,
546-  ENTER: 13,
547-  CTRL: 17,
548-  A: 65,
549-  P: 80,
550-  N: 78,
551-  LEFT: 37,
552-  UP: 38,
553-  RIGHT: 39,
554-  DOWN: 40,
555-  BACKSPACE: 8,
556-  SPACE: 32
557-};
558-
559-DEFAULT_CALLBACKS = {
560-  beforeSave: function(data) {
561-    return Controller.arrayToDefaultHash(data);
562-  },
563-  matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
564-    var _a, _y, match, regexp, space;
565-    flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
566-    if (should_startWithSpace) {
567-      flag = '(?:^|\\s)' + flag;
568-    }
569-    _a = decodeURI("%C3%80");
570-    _y = decodeURI("%C3%BF");
571-    space = acceptSpaceBar ? "\ " : "";
572-    regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');
573-    match = regexp.exec(subtext);
574-    if (match) {
575-      return match[2] || match[1];
576-    } else {
577-      return null;
578-    }
579-  },
580-  filter: function(query, data, searchKey) {
581-    var _results, i, item, len;
582-    _results = [];
583-    for (i = 0, len = data.length; i < len; i++) {
584-      item = data[i];
585-      if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
586-        _results.push(item);
587-      }
588-    }
589-    return _results;
590-  },
591-  remoteFilter: null,
592-  sorter: function(query, items, searchKey) {
593-    var _results, i, item, len;
594-    if (!query) {
595-      return items;
596-    }
597-    _results = [];
598-    for (i = 0, len = items.length; i < len; i++) {
599-      item = items[i];
600-      item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());
601-      if (item.atwho_order > -1) {
602-        _results.push(item);
603-      }
604-    }
605-    return _results.sort(function(a, b) {
606-      return a.atwho_order - b.atwho_order;
607-    });
608-  },
609-  tplEval: function(tpl, map) {
610-    var error, error1, template;
611-    template = tpl;
612-    try {
613-      if (typeof tpl !== 'string') {
614-        template = tpl(map);
615-      }
616-      return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {
617-        return map[key];
618-      });
619-    } catch (error1) {
620-      error = error1;
621-      return "";
622-    }
623-  },
624-  highlighter: function(li, query) {
625-    var regexp;
626-    if (!query) {
627-      return li;
628-    }
629-    regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig');
630-    return li.replace(regexp, function(str, $1, $2, $3) {
631-      return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
632-    });
633-  },
634-  beforeInsert: function(value, $li, e) {
635-    return value;
636-  },
637-  beforeReposition: function(offset) {
638-    return offset;
639-  },
640-  afterMatchFailed: function(at, el) {}
641-};
642-
643-var App;
644-
645-App = (function() {
646-  function App(inputor) {
647-    this.currentFlag = null;
648-    this.controllers = {};
649-    this.aliasMaps = {};
650-    this.$inputor = $(inputor);
651-    this.setupRootElement();
652-    this.listen();
653-  }
654-
655-  App.prototype.createContainer = function(doc) {
656-    var ref;
657-    if ((ref = this.$el) != null) {
658-      ref.remove();
659-    }
660-    return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
661-  };
662-
663-  App.prototype.setupRootElement = function(iframe, asRoot) {
664-    var error, error1;
665-    if (asRoot == null) {
666-      asRoot = false;
667-    }
668-    if (iframe) {
669-      this.window = iframe.contentWindow;
670-      this.document = iframe.contentDocument || this.window.document;
671-      this.iframe = iframe;
672-    } else {
673-      this.document = this.$inputor[0].ownerDocument;
674-      this.window = this.document.defaultView || this.document.parentWindow;
675-      try {
676-        this.iframe = this.window.frameElement;
677-      } catch (error1) {
678-        error = error1;
679-        this.iframe = null;
680-        if ($.fn.atwho.debug) {
681-          throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);
682-        }
683-      }
684-    }
685-    return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
686-  };
687-
688-  App.prototype.controller = function(at) {
689-    var c, current, currentFlag, ref;
690-    if (this.aliasMaps[at]) {
691-      current = this.controllers[this.aliasMaps[at]];
692-    } else {
693-      ref = this.controllers;
694-      for (currentFlag in ref) {
695-        c = ref[currentFlag];
696-        if (currentFlag === at) {
697-          current = c;
698-          break;
699-        }
700-      }
701-    }
702-    if (current) {
703-      return current;
704-    } else {
705-      return this.controllers[this.currentFlag];
706-    }
707-  };
708-
709-  App.prototype.setContextFor = function(at) {
710-    this.currentFlag = at;
711-    return this;
712-  };
713-
714-  App.prototype.reg = function(flag, setting) {
715-    var base, controller;
716-    controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
717-    if (setting.alias) {
718-      this.aliasMaps[setting.alias] = flag;
719-    }
720-    controller.init(setting);
721-    return this;
722-  };
723-
724-  App.prototype.listen = function() {
725-    return this.$inputor.on('compositionstart', (function(_this) {
726-      return function(e) {
727-        var ref;
728-        if ((ref = _this.controller()) != null) {
729-          ref.view.hide();
730-        }
731-        _this.isComposing = true;
732-        return null;
733-      };
734-    })(this)).on('compositionend', (function(_this) {
735-      return function(e) {
736-        _this.isComposing = false;
737-        setTimeout(function(e) {
738-          return _this.dispatch(e);
739-        });
740-        return null;
741-      };
742-    })(this)).on('keyup.atwhoInner', (function(_this) {
743-      return function(e) {
744-        return _this.onKeyup(e);
745-      };
746-    })(this)).on('keydown.atwhoInner', (function(_this) {
747-      return function(e) {
748-        return _this.onKeydown(e);
749-      };
750-    })(this)).on('blur.atwhoInner', (function(_this) {
751-      return function(e) {
752-        var c;
753-        if (c = _this.controller()) {
754-          c.expectedQueryCBId = null;
755-          return c.view.hide(e, c.getOpt("displayTimeout"));
756-        }
757-      };
758-    })(this)).on('click.atwhoInner', (function(_this) {
759-      return function(e) {
760-        return _this.dispatch(e);
761-      };
762-    })(this)).on('scroll.atwhoInner', (function(_this) {
763-      return function() {
764-        var lastScrollTop;
765-        lastScrollTop = _this.$inputor.scrollTop();
766-        return function(e) {
767-          var currentScrollTop, ref;
768-          currentScrollTop = e.target.scrollTop;
769-          if (lastScrollTop !== currentScrollTop) {
770-            if ((ref = _this.controller()) != null) {
771-              ref.view.hide(e);
772-            }
773-          }
774-          lastScrollTop = currentScrollTop;
775-          return true;
776-        };
777-      };
778-    })(this)());
779-  };
780-
781-  App.prototype.shutdown = function() {
782-    var _, c, ref;
783-    ref = this.controllers;
784-    for (_ in ref) {
785-      c = ref[_];
786-      c.destroy();
787-      delete this.controllers[_];
788-    }
789-    this.$inputor.off('.atwhoInner');
790-    return this.$el.remove();
791-  };
792-
793-  App.prototype.dispatch = function(e) {
794-    var _, c, ref, results;
795-    ref = this.controllers;
796-    results = [];
797-    for (_ in ref) {
798-      c = ref[_];
799-      results.push(c.lookUp(e));
800-    }
801-    return results;
802-  };
803-
804-  App.prototype.onKeyup = function(e) {
805-    var ref;
806-    switch (e.keyCode) {
807-      case KEY_CODE.ESC:
808-        e.preventDefault();
809-        if ((ref = this.controller()) != null) {
810-          ref.view.hide();
811-        }
812-        break;
813-      case KEY_CODE.DOWN:
814-      case KEY_CODE.UP:
815-      case KEY_CODE.CTRL:
816-      case KEY_CODE.ENTER:
817-        $.noop();
818-        break;
819-      case KEY_CODE.P:
820-      case KEY_CODE.N:
821-        if (!e.ctrlKey) {
822-          this.dispatch(e);
823-        }
824-        break;
825-      default:
826-        this.dispatch(e);
827-    }
828-  };
829-
830-  App.prototype.onKeydown = function(e) {
831-    var ref, view;
832-    view = (ref = this.controller()) != null ? ref.view : void 0;
833-    if (!(view && view.visible())) {
834-      return;
835-    }
836-    switch (e.keyCode) {
837-      case KEY_CODE.ESC:
838-        e.preventDefault();
839-        view.hide(e);
840-        break;
841-      case KEY_CODE.UP:
842-        e.preventDefault();
843-        view.prev();
844-        break;
845-      case KEY_CODE.DOWN:
846-        e.preventDefault();
847-        view.next();
848-        break;
849-      case KEY_CODE.P:
850-        if (!e.ctrlKey) {
851-          return;
852-        }
853-        e.preventDefault();
854-        view.prev();
855-        break;
856-      case KEY_CODE.N:
857-        if (!e.ctrlKey) {
858-          return;
859-        }
860-        e.preventDefault();
861-        view.next();
862-        break;
863-      case KEY_CODE.TAB:
864-      case KEY_CODE.ENTER:
865-      case KEY_CODE.SPACE:
866-        if (!view.visible()) {
867-          return;
868-        }
869-        if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {
870-          return;
871-        }
872-        if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {
873-          return;
874-        }
875-        if (view.highlighted()) {
876-          e.preventDefault();
877-          view.choose(e);
878-        } else {
879-          view.hide(e);
880-        }
881-        break;
882-      default:
883-        $.noop();
884-    }
885-  };
886-
887-  return App;
888-
889-})();
890-
891-var Controller,
892-  slice = [].slice;
893-
894-Controller = (function() {
895-  Controller.prototype.uid = function() {
896-    return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
897-  };
898-
899-  function Controller(app, at1) {
900-    this.app = app;
901-    this.at = at1;
902-    this.$inputor = this.app.$inputor;
903-    this.id = this.$inputor[0].id || this.uid();
904-    this.expectedQueryCBId = null;
905-    this.setting = null;
906-    this.query = null;
907-    this.pos = 0;
908-    this.range = null;
909-    if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {
910-      this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
911-    }
912-    this.model = new Model(this);
913-    this.view = new View(this);
914-  }
915-
916-  Controller.prototype.init = function(setting) {
917-    this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
918-    this.view.init();
919-    return this.model.reload(this.setting.data);
920-  };
921-
922-  Controller.prototype.destroy = function() {
923-    this.trigger('beforeDestroy');
924-    this.model.destroy();
925-    this.view.destroy();
926-    return this.$el.remove();
927-  };
928-
929-  Controller.prototype.callDefault = function() {
930-    var args, error, error1, funcName;
931-    funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
932-    try {
933-      return DEFAULT_CALLBACKS[funcName].apply(this, args);
934-    } catch (error1) {
935-      error = error1;
936-      return $.error(error + " Or maybe At.js doesn't have function " + funcName);
937-    }
938-  };
939-
940-  Controller.prototype.trigger = function(name, data) {
941-    var alias, eventName;
942-    if (data == null) {
943-      data = [];
944-    }
945-    data.push(this);
946-    alias = this.getOpt('alias');
947-    eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";
948-    return this.$inputor.trigger(eventName, data);
949-  };
950-
951-  Controller.prototype.callbacks = function(funcName) {
952-    return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
953-  };
954-
955-  Controller.prototype.getOpt = function(at, default_value) {
956-    var e, error1;
957-    try {
958-      return this.setting[at];
959-    } catch (error1) {
960-      e = error1;
961-      return null;
962-    }
963-  };
964-
965-  Controller.prototype.insertContentFor = function($li) {
966-    var data, tpl;
967-    tpl = this.getOpt('insertTpl');
968-    data = $.extend({}, $li.data('item-data'), {
969-      'atwho-at': this.at
970-    });
971-    return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
972-  };
973-
974-  Controller.prototype.renderView = function(data) {
975-    var searchKey;
976-    searchKey = this.getOpt("searchKey");
977-    data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);
978-    return this.view.render(data.slice(0, this.getOpt('limit')));
979-  };
980-
981-  Controller.arrayToDefaultHash = function(data) {
982-    var i, item, len, results;
983-    if (!$.isArray(data)) {
984-      return data;
985-    }
986-    results = [];
987-    for (i = 0, len = data.length; i < len; i++) {
988-      item = data[i];
989-      if ($.isPlainObject(item)) {
990-        results.push(item);
991-      } else {
992-        results.push({
993-          name: item
994-        });
995-      }
996-    }
997-    return results;
998-  };
999-
1000-  Controller.prototype.lookUp = function(e) {
1001-    var query, wait;
1002-    if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
1003-      return;
1004-    }
1005-    if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
1006-      return;
1007-    }
1008-    query = this.catchQuery(e);
1009-    if (!query) {
1010-      this.expectedQueryCBId = null;
1011-      return query;
1012-    }
1013-    this.app.setContextFor(this.at);
1014-    if (wait = this.getOpt('delay')) {
1015-      this._delayLookUp(query, wait);
1016-    } else {
1017-      this._lookUp(query);
1018-    }
1019-    return query;
1020-  };
1021-
1022-  Controller.prototype._delayLookUp = function(query, wait) {
1023-    var now, remaining;
1024-    now = Date.now ? Date.now() : new Date().getTime();
1025-    this.previousCallTime || (this.previousCallTime = now);
1026-    remaining = wait - (now - this.previousCallTime);
1027-    if ((0 < remaining && remaining < wait)) {
1028-      this.previousCallTime = now;
1029-      this._stopDelayedCall();
1030-      return this.delayedCallTimeout = setTimeout((function(_this) {
1031-        return function() {
1032-          _this.previousCallTime = 0;
1033-          _this.delayedCallTimeout = null;
1034-          return _this._lookUp(query);
1035-        };
1036-      })(this), wait);
1037-    } else {
1038-      this._stopDelayedCall();
1039-      if (this.previousCallTime !== now) {
1040-        this.previousCallTime = 0;
1041-      }
1042-      return this._lookUp(query);
1043-    }
1044-  };
1045-
1046-  Controller.prototype._stopDelayedCall = function() {
1047-    if (this.delayedCallTimeout) {
1048-      clearTimeout(this.delayedCallTimeout);
1049-      return this.delayedCallTimeout = null;
1050-    }
1051-  };
1052-
1053-  Controller.prototype._generateQueryCBId = function() {
1054-    return {};
1055-  };
1056-
1057-  Controller.prototype._lookUp = function(query) {
1058-    var _callback;
1059-    _callback = function(queryCBId, data) {
1060-      if (queryCBId !== this.expectedQueryCBId) {
1061-        return;
1062-      }
1063-      if (data && data.length > 0) {
1064-        return this.renderView(this.constructor.arrayToDefaultHash(data));
1065-      } else {
1066-        return this.view.hide();
1067-      }
1068-    };
1069-    this.expectedQueryCBId = this._generateQueryCBId();
1070-    return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
1071-  };
1072-
1073-  return Controller;
1074-
1075-})();
1076-
1077-var TextareaController,
1078-  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; },
1079-  hasProp = {}.hasOwnProperty;
1080-
1081-TextareaController = (function(superClass) {
1082-  extend(TextareaController, superClass);
1083-
1084-  function TextareaController() {
1085-    return TextareaController.__super__.constructor.apply(this, arguments);
1086-  }
1087-
1088-  TextareaController.prototype.catchQuery = function() {
1089-    var caretPos, content, end, isString, query, start, subtext;
1090-    content = this.$inputor.val();
1091-    caretPos = this.$inputor.caret('pos', {
1092-      iframe: this.app.iframe
1093-    });
1094-    subtext = content.slice(0, caretPos);
1095-    query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
1096-    isString = typeof query === 'string';
1097-    if (isString && query.length < this.getOpt('minLen', 0)) {
1098-      return;
1099-    }
1100-    if (isString && query.length <= this.getOpt('maxLen', 20)) {
1101-      start = caretPos - query.length;
1102-      end = start + query.length;
1103-      this.pos = start;
1104-      query = {
1105-        'text': query,
1106-        'headPos': start,
1107-        'endPos': end
1108-      };
1109-      this.trigger("matched", [this.at, query.text]);
1110-    } else {
1111-      query = null;
1112-      this.view.hide();
1113-    }
1114-    return this.query = query;
1115-  };
1116-
1117-  TextareaController.prototype.rect = function() {
1118-    var c, iframeOffset, scaleBottom;
1119-    if (!(c = this.$inputor.caret('offset', this.pos - 1, {
1120-      iframe: this.app.iframe
1121-    }))) {
1122-      return;
1123-    }
1124-    if (this.app.iframe && !this.app.iframeAsRoot) {
1125-      iframeOffset = $(this.app.iframe).offset();
1126-      c.left += iframeOffset.left;
1127-      c.top += iframeOffset.top;
1128-    }
1129-    scaleBottom = this.app.document.selection ? 0 : 2;
1130-    return {
1131-      left: c.left,
1132-      top: c.top,
1133-      bottom: c.top + c.height + scaleBottom
1134-    };
1135-  };
1136-
1137-  TextareaController.prototype.insert = function(content, $li) {
1138-    var $inputor, source, startStr, suffix, text;
1139-    $inputor = this.$inputor;
1140-    source = $inputor.val();
1141-    startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
1142-    suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
1143-    content += suffix;
1144-    text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));
1145-    $inputor.val(text);
1146-    $inputor.caret('pos', startStr.length + content.length, {
1147-      iframe: this.app.iframe
1148-    });
1149-    if (!$inputor.is(':focus')) {
1150-      $inputor.focus();
1151-    }
1152-    return $inputor.change();
1153-  };
1154-
1155-  return TextareaController;
1156-
1157-})(Controller);
1158-
1159-var EditableController,
1160-  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; },
1161-  hasProp = {}.hasOwnProperty;
1162-
1163-EditableController = (function(superClass) {
1164-  extend(EditableController, superClass);
1165-
1166-  function EditableController() {
1167-    return EditableController.__super__.constructor.apply(this, arguments);
1168-  }
1169-
1170-  EditableController.prototype._getRange = function() {
1171-    var sel;
1172-    sel = this.app.window.getSelection();
1173-    if (sel.rangeCount > 0) {
1174-      return sel.getRangeAt(0);
1175-    }
1176-  };
1177-
1178-  EditableController.prototype._setRange = function(position, node, range) {
1179-    if (range == null) {
1180-      range = this._getRange();
1181-    }
1182-    if (!(range && node)) {
1183-      return;
1184-    }
1185-    node = $(node)[0];
1186-    if (position === 'after') {
1187-      range.setEndAfter(node);
1188-      range.setStartAfter(node);
1189-    } else {
1190-      range.setEndBefore(node);
1191-      range.setStartBefore(node);
1192-    }
1193-    range.collapse(false);
1194-    return this._clearRange(range);
1195-  };
1196-
1197-  EditableController.prototype._clearRange = function(range) {
1198-    var sel;
1199-    if (range == null) {
1200-      range = this._getRange();
1201-    }
1202-    sel = this.app.window.getSelection();
1203-    if (this.ctrl_a_pressed == null) {
1204-      sel.removeAllRanges();
1205-      return sel.addRange(range);
1206-    }
1207-  };
1208-
1209-  EditableController.prototype._movingEvent = function(e) {
1210-    var ref;
1211-    return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
1212-  };
1213-
1214-  EditableController.prototype._unwrap = function(node) {
1215-    var next;
1216-    node = $(node).unwrap().get(0);
1217-    if ((next = node.nextSibling) && next.nodeValue) {
1218-      node.nodeValue += next.nodeValue;
1219-      $(next).remove();
1220-    }
1221-    return node;
1222-  };
1223-
1224-  EditableController.prototype.catchQuery = function(e) {
1225-    var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
1226-    if (!(range = this._getRange())) {
1227-      return;
1228-    }
1229-    if (!range.collapsed) {
1230-      return;
1231-    }
1232-    if (e.which === KEY_CODE.ENTER) {
1233-      ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();
1234-      if ($query.is(':empty')) {
1235-        $query.remove();
1236-      }
1237-      ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();
1238-      this._clearRange();
1239-      return;
1240-    }
1241-    if (/firefox/i.test(navigator.userAgent)) {
1242-      if ($(range.startContainer).is(this.$inputor)) {
1243-        this._clearRange();
1244-        return;
1245-      }
1246-      if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {
1247-        _range = range.cloneRange();
1248-        _range.setStart(range.startContainer, offset);
1249-        if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
1250-          inserted = $(range.startContainer).contents().get(offset);
1251-          this._setRange('after', $(inserted).contents().last());
1252-        }
1253-      } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {
1254-        $inserted = $(range.startContainer.previousSibling);
1255-        if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
1256-          this._setRange('after', $inserted.contents().last());
1257-        }
1258-      }
1259-    }
1260-    $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
1261-    if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
1262-      $query.remove();
1263-    }
1264-    if (!this._movingEvent(e)) {
1265-      $query.removeClass('atwho-inserted');
1266-    }
1267-    if ($query.length > 0) {
1268-      switch (e.which) {
1269-        case KEY_CODE.LEFT:
1270-          this._setRange('before', $query.get(0), range);
1271-          $query.removeClass('atwho-query');
1272-          return;
1273-        case KEY_CODE.RIGHT:
1274-          this._setRange('after', $query.get(0).nextSibling, range);
1275-          $query.removeClass('atwho-query');
1276-          return;
1277-      }
1278-    }
1279-    if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
1280-      $query.empty().html(query_content).attr('data-atwho-at-query', null);
1281-      this._setRange('after', $query.get(0), range);
1282-    }
1283-    _range = range.cloneRange();
1284-    _range.setStart(range.startContainer, 0);
1285-    matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
1286-    isString = typeof matched === 'string';
1287-    if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
1288-      range.setStart(range.startContainer, index);
1289-      $query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
1290-      range.surroundContents($query.get(0));
1291-      lastNode = $query.contents().last().get(0);
1292-      if (lastNode) {
1293-        if (/firefox/i.test(navigator.userAgent)) {
1294-          range.setStart(lastNode, lastNode.length);
1295-          range.setEnd(lastNode, lastNode.length);
1296-          this._clearRange(range);
1297-        } else {
1298-          this._setRange('after', lastNode, range);
1299-        }
1300-      }
1301-    }
1302-    if (isString && matched.length < this.getOpt('minLen', 0)) {
1303-      return;
1304-    }
1305-    if (isString && matched.length <= this.getOpt('maxLen', 20)) {
1306-      query = {
1307-        text: matched,
1308-        el: $query
1309-      };
1310-      this.trigger("matched", [this.at, query.text]);
1311-      return this.query = query;
1312-    } else {
1313-      this.view.hide();
1314-      this.query = {
1315-        el: $query
1316-      };
1317-      if ($query.text().indexOf(this.at) >= 0) {
1318-        if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
1319-          $query.removeClass('atwho-query');
1320-        } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {
1321-          this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));
1322-        }
1323-      }
1324-      return null;
1325-    }
1326-  };
1327-
1328-  EditableController.prototype.rect = function() {
1329-    var $iframe, iframeOffset, rect;
1330-    rect = this.query.el.offset();
1331-    if (!(rect && this.query.el[0].getClientRects().length)) {
1332-      return;
1333-    }
1334-    if (this.app.iframe && !this.app.iframeAsRoot) {
1335-      iframeOffset = ($iframe = $(this.app.iframe)).offset();
1336-      rect.left += iframeOffset.left - this.$inputor.scrollLeft();
1337-      rect.top += iframeOffset.top - this.$inputor.scrollTop();
1338-    }
1339-    rect.bottom = rect.top + this.query.el.height();
1340-    return rect;
1341-  };
1342-
1343-  EditableController.prototype.insert = function(content, $li) {
1344-    var data, overrides, range, suffix, suffixNode;
1345-    if (!this.$inputor.is(':focus')) {
1346-      this.$inputor.focus();
1347-    }
1348-    overrides = this.getOpt('functionOverrides');
1349-    if (overrides.insert) {
1350-      return overrides.insert.call(this, content, $li);
1351-    }
1352-    suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
1353-    data = $li.data('item-data');
1354-    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");
1355-    if (range = this._getRange()) {
1356-      if (this.query.el.length) {
1357-        range.setEndAfter(this.query.el[0]);
1358-      }
1359-      range.collapse(false);
1360-      range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix));
1361-      this._setRange('after', suffixNode, range);
1362-    }
1363-    if (!this.$inputor.is(':focus')) {
1364-      this.$inputor.focus();
1365-    }
1366-    return this.$inputor.change();
1367-  };
1368-
1369-  return EditableController;
1370-
1371-})(Controller);
1372-
1373-var Model;
1374-
1375-Model = (function() {
1376-  function Model(context) {
1377-    this.context = context;
1378-    this.at = this.context.at;
1379-    this.storage = this.context.$inputor;
1380-  }
1381-
1382-  Model.prototype.destroy = function() {
1383-    return this.storage.data(this.at, null);
1384-  };
1385-
1386-  Model.prototype.saved = function() {
1387-    return this.fetch() > 0;
1388-  };
1389-
1390-  Model.prototype.query = function(query, callback) {
1391-    var _remoteFilter, data, searchKey;
1392-    data = this.fetch();
1393-    searchKey = this.context.getOpt("searchKey");
1394-    data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];
1395-    _remoteFilter = this.context.callbacks('remoteFilter');
1396-    if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
1397-      return callback(data);
1398-    } else {
1399-      return _remoteFilter.call(this.context, query, callback);
1400-    }
1401-  };
1402-
1403-  Model.prototype.fetch = function() {
1404-    return this.storage.data(this.at) || [];
1405-  };
1406-
1407-  Model.prototype.save = function(data) {
1408-    return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
1409-  };
1410-
1411-  Model.prototype.load = function(data) {
1412-    if (!(this.saved() || !data)) {
1413-      return this._load(data);
1414-    }
1415-  };
1416-
1417-  Model.prototype.reload = function(data) {
1418-    return this._load(data);
1419-  };
1420-
1421-  Model.prototype._load = function(data) {
1422-    if (typeof data === "string") {
1423-      return $.ajax(data, {
1424-        dataType: "json"
1425-      }).done((function(_this) {
1426-        return function(data) {
1427-          return _this.save(data);
1428-        };
1429-      })(this));
1430-    } else {
1431-      return this.save(data);
1432-    }
1433-  };
1434-
1435-  return Model;
1436-
1437-})();
1438-
1439-var View;
1440-
1441-View = (function() {
1442-  function View(context) {
1443-    this.context = context;
1444-    this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
1445-    this.$elUl = this.$el.children();
1446-    this.timeoutID = null;
1447-    this.context.$el.append(this.$el);
1448-    this.bindEvent();
1449-  }
1450-
1451-  View.prototype.init = function() {
1452-    var header_tpl, id;
1453-    id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
1454-    header_tpl = this.context.getOpt("headerTpl");
1455-    if (header_tpl && this.$el.children().length === 1) {
1456-      this.$el.prepend(header_tpl);
1457-    }
1458-    return this.$el.attr({
1459-      'id': "at-view-" + id
1460-    });
1461-  };
1462-
1463-  View.prototype.destroy = function() {
1464-    return this.$el.remove();
1465-  };
1466-
1467-  View.prototype.bindEvent = function() {
1468-    var $menu, lastCoordX, lastCoordY;
1469-    $menu = this.$el.find('ul');
1470-    lastCoordX = 0;
1471-    lastCoordY = 0;
1472-    return $menu.on('mousemove.atwho-view', 'li', (function(_this) {
1473-      return function(e) {
1474-        var $cur;
1475-        if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
1476-          return;
1477-        }
1478-        lastCoordX = e.clientX;
1479-        lastCoordY = e.clientY;
1480-        $cur = $(e.currentTarget);
1481-        if ($cur.hasClass('cur')) {
1482-          return;
1483-        }
1484-        $menu.find('.cur').removeClass('cur');
1485-        return $cur.addClass('cur');
1486-      };
1487-    })(this)).on('click.atwho-view', 'li', (function(_this) {
1488-      return function(e) {
1489-        $menu.find('.cur').removeClass('cur');
1490-        $(e.currentTarget).addClass('cur');
1491-        _this.choose(e);
1492-        return e.preventDefault();
1493-      };
1494-    })(this));
1495-  };
1496-
1497-  View.prototype.visible = function() {
1498-    return $.expr.filters.visible(this.$el[0]);
1499-  };
1500-
1501-  View.prototype.highlighted = function() {
1502-    return this.$el.find(".cur").length > 0;
1503-  };
1504-
1505-  View.prototype.choose = function(e) {
1506-    var $li, content;
1507-    if (($li = this.$el.find(".cur")).length) {
1508-      content = this.context.insertContentFor($li);
1509-      this.context._stopDelayedCall();
1510-      this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);
1511-      this.context.trigger("inserted", [$li, e]);
1512-      this.hide(e);
1513-    }
1514-    if (this.context.getOpt("hideWithoutSuffix")) {
1515-      return this.stopShowing = true;
1516-    }
1517-  };
1518-
1519-  View.prototype.reposition = function(rect) {
1520-    var _window, offset, overflowOffset, ref;
1521-    _window = this.context.app.iframeAsRoot ? this.context.app.window : window;
1522-    if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
1523-      rect.bottom = rect.top - this.$el.height();
1524-    }
1525-    if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
1526-      rect.left = overflowOffset;
1527-    }
1528-    offset = {
1529-      left: rect.left,
1530-      top: rect.bottom
1531-    };
1532-    if ((ref = this.context.callbacks("beforeReposition")) != null) {
1533-      ref.call(this.context, offset);
1534-    }
1535-    this.$el.offset(offset);
1536-    return this.context.trigger("reposition", [offset]);
1537-  };
1538-
1539-  View.prototype.next = function() {
1540-    var cur, next, nextEl, offset;
1541-    cur = this.$el.find('.cur').removeClass('cur');
1542-    next = cur.next();
1543-    if (!next.length) {
1544-      next = this.$el.find('li:first');
1545-    }
1546-    next.addClass('cur');
1547-    nextEl = next[0];
1548-    offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
1549-    return this.scrollTop(Math.max(0, offset - this.$el.height()));
1550-  };
1551-
1552-  View.prototype.prev = function() {
1553-    var cur, offset, prev, prevEl;
1554-    cur = this.$el.find('.cur').removeClass('cur');
1555-    prev = cur.prev();
1556-    if (!prev.length) {
1557-      prev = this.$el.find('li:last');
1558-    }
1559-    prev.addClass('cur');
1560-    prevEl = prev[0];
1561-    offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
1562-    return this.scrollTop(Math.max(0, offset - this.$el.height()));
1563-  };
1564-
1565-  View.prototype.scrollTop = function(scrollTop) {
1566-    var scrollDuration;
1567-    scrollDuration = this.context.getOpt('scrollDuration');
1568-    if (scrollDuration) {
1569-      return this.$elUl.animate({
1570-        scrollTop: scrollTop
1571-      }, scrollDuration);
1572-    } else {
1573-      return this.$elUl.scrollTop(scrollTop);
1574-    }
1575-  };
1576-
1577-  View.prototype.show = function() {
1578-    var rect;
1579-    if (this.stopShowing) {
1580-      this.stopShowing = false;
1581-      return;
1582-    }
1583-    if (!this.visible()) {
1584-      this.$el.show();
1585-      this.$el.scrollTop(0);
1586-      this.context.trigger('shown');
1587-    }
1588-    if (rect = this.context.rect()) {
1589-      return this.reposition(rect);
1590-    }
1591-  };
1592-
1593-  View.prototype.hide = function(e, time) {
1594-    var callback;
1595-    if (!this.visible()) {
1596-      return;
1597-    }
1598-    if (isNaN(time)) {
1599-      this.$el.hide();
1600-      return this.context.trigger('hidden', [e]);
1601-    } else {
1602-      callback = (function(_this) {
1603-        return function() {
1604-          return _this.hide();
1605-        };
1606-      })(this);
1607-      clearTimeout(this.timeoutID);
1608-      return this.timeoutID = setTimeout(callback, time);
1609-    }
1610-  };
1611-
1612-  View.prototype.render = function(list) {
1613-    var $li, $ul, i, item, len, li, tpl;
1614-    if (!($.isArray(list) && list.length > 0)) {
1615-      this.hide();
1616-      return;
1617-    }
1618-    this.$el.find('ul').empty();
1619-    $ul = this.$el.find('ul');
1620-    tpl = this.context.getOpt('displayTpl');
1621-    for (i = 0, len = list.length; i < len; i++) {
1622-      item = list[i];
1623-      item = $.extend({}, item, {
1624-        'atwho-at': this.context.at
1625-      });
1626-      li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");
1627-      $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
1628-      $li.data("item-data", item);
1629-      $ul.append($li);
1630-    }
1631-    this.show();
1632-    if (this.context.getOpt('highlightFirst')) {
1633-      return $ul.find("li:first").addClass("cur");
1634-    }
1635-  };
1636-
1637-  return View;
1638-
1639-})();
1640-
1641-var Api;
1642-
1643-Api = {
1644-  load: function(at, data) {
1645-    var c;
1646-    if (c = this.controller(at)) {
1647-      return c.model.load(data);
1648-    }
1649-  },
1650-  isSelecting: function() {
1651-    var ref;
1652-    return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);
1653-  },
1654-  hide: function() {
1655-    var ref;
1656-    return (ref = this.controller()) != null ? ref.view.hide() : void 0;
1657-  },
1658-  reposition: function() {
1659-    var c;
1660-    if (c = this.controller()) {
1661-      return c.view.reposition(c.rect());
1662-    }
1663-  },
1664-  setIframe: function(iframe, asRoot) {
1665-    this.setupRootElement(iframe, asRoot);
1666-    return null;
1667-  },
1668-  run: function() {
1669-    return this.dispatch();
1670-  },
1671-  destroy: function() {
1672-    this.shutdown();
1673-    return this.$inputor.data('atwho', null);
1674-  }
1675-};
1676-
1677-$.fn.atwho = function(method) {
1678-  var _args, result;
1679-  _args = arguments;
1680-  result = null;
1681-  this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() {
1682-    var $this, app;
1683-    if (!(app = ($this = $(this)).data("atwho"))) {
1684-      $this.data('atwho', (app = new App(this)));
1685-    }
1686-    if (typeof method === 'object' || !method) {
1687-      return app.reg(method.at, method);
1688-    } else if (Api[method] && app) {
1689-      return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
1690-    } else {
1691-      return $.error("Method " + method + " does not exist on jQuery.atwho");
1692-    }
1693-  });
1694-  if (result != null) {
1695-    return result;
1696-  } else {
1697-    return this;
1698-  }
1699-};
1700-
1701-$.fn.atwho["default"] = {
1702-  at: void 0,
1703-  alias: void 0,
1704-  data: null,
1705-  displayTpl: "<li>${name}</li>",
1706-  insertTpl: "${atwho-at}${name}",
1707-  headerTpl: null,
1708-  callbacks: DEFAULT_CALLBACKS,
1709-  functionOverrides: {},
1710-  searchKey: "name",
1711-  suffix: void 0,
1712-  hideWithoutSuffix: false,
1713-  startWithSpace: true,
1714-  acceptSpaceBar: false,
1715-  highlightFirst: true,
1716-  limit: 5,
1717-  maxLen: 20,
1718-  minLen: 0,
1719-  displayTimeout: 300,
1720-  delay: null,
1721-  spaceSelectsMatch: false,
1722-  tabSelectsMatch: true,
1723-  editableAtwhoQueryAttrs: {},
1724-  scrollDuration: 150,
1725-  suspendOnComposing: true,
1726-  lookUpOnClick: true
1727-};
1728-
1729-$.fn.atwho.debug = false;
1730-
1731-}));
1732diff --git src/bp-core/js/vendor/jquery.atwho.txt src/bp-core/js/vendor/jquery.atwho.txt
1733deleted file mode 100644
1734index 36cd1c122..000000000
1735--- src/bp-core/js/vendor/jquery.atwho.txt
1736+++ /dev/null
1737@@ -1,22 +0,0 @@
1738-Copyright (c) 2013 chord.luo@gmail.com
1739-
1740-Permission is hereby granted, free of charge, to any person
1741-obtaining a copy of this software and associated documentation
1742-files (the "Software"), to deal in the Software without
1743-restriction, including without limitation the rights to use,
1744-copy, modify, merge, publish, distribute, sublicense, and/or sell
1745-copies of the Software, and to permit persons to whom the
1746-Software is furnished to do so, subject to the following
1747-conditions:
1748-
1749-The above copyright notice and this permission notice shall be
1750-included in all copies or substantial portions of the Software.
1751-
1752-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1753-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1754-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1755-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1756-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
1757-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1758-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1759-OTHER DEALINGS IN THE SOFTWARE.
1760diff --git src/bp-core/js/vendor/jquery.caret.js src/bp-core/js/vendor/jquery.caret.js
1761deleted file mode 100755
1762index 811ec63ee..000000000
1763--- src/bp-core/js/vendor/jquery.caret.js
1764+++ /dev/null
1765@@ -1,436 +0,0 @@
1766-(function (root, factory) {
1767-  if (typeof define === 'function' && define.amd) {
1768-    // AMD. Register as an anonymous module.
1769-    define(["jquery"], function ($) {
1770-      return (root.returnExportsGlobal = factory($));
1771-    });
1772-  } else if (typeof exports === 'object') {
1773-    // Node. Does not work with strict CommonJS, but
1774-    // only CommonJS-like enviroments that support module.exports,
1775-    // like Node.
1776-    module.exports = factory(require("jquery"));
1777-  } else {
1778-    factory(jQuery);
1779-  }
1780-}(this, function ($) {
1781-
1782-/*
1783-  Implement Github like autocomplete mentions
1784-  http://ichord.github.com/At.js
1785-
1786-  Copyright (c) 2013 chord.luo@gmail.com
1787-  Licensed under the MIT license.
1788-*/
1789-
1790-/*
1791-本插件操䜜 textarea 或者 input 内的插入笊
1792-只实现了获埗插入笊圚文本框䞭的䜍眮我讟眮
1793-插入笊的䜍眮.
1794-*/
1795-
1796-"use strict";
1797-var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
1798-
1799-pluginName = 'caret';
1800-
1801-EditableCaret = (function() {
1802-  function EditableCaret($inputor) {
1803-    this.$inputor = $inputor;
1804-    this.domInputor = this.$inputor[0];
1805-  }
1806-
1807-  EditableCaret.prototype.setPos = function(pos) {
1808-    var fn, found, offset, sel;
1809-    if (sel = oWindow.getSelection()) {
1810-      offset = 0;
1811-      found = false;
1812-      (fn = function(pos, parent) {
1813-        var node, range, _i, _len, _ref, _results;
1814-        _ref = parent.childNodes;
1815-        _results = [];
1816-        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1817-          node = _ref[_i];
1818-          if (found) {
1819-            break;
1820-          }
1821-          if (node.nodeType === 3) {
1822-            if (offset + node.length >= pos) {
1823-              found = true;
1824-              range = oDocument.createRange();
1825-              range.setStart(node, pos - offset);
1826-              sel.removeAllRanges();
1827-              sel.addRange(range);
1828-              break;
1829-            } else {
1830-              _results.push(offset += node.length);
1831-            }
1832-          } else {
1833-            _results.push(fn(pos, node));
1834-          }
1835-        }
1836-        return _results;
1837-      })(pos, this.domInputor);
1838-    }
1839-    return this.domInputor;
1840-  };
1841-
1842-  EditableCaret.prototype.getIEPosition = function() {
1843-    return this.getPosition();
1844-  };
1845-
1846-  EditableCaret.prototype.getPosition = function() {
1847-    var inputor_offset, offset;
1848-    offset = this.getOffset();
1849-    inputor_offset = this.$inputor.offset();
1850-    offset.left -= inputor_offset.left;
1851-    offset.top -= inputor_offset.top;
1852-    return offset;
1853-  };
1854-
1855-  EditableCaret.prototype.getOldIEPos = function() {
1856-    var preCaretTextRange, textRange;
1857-    textRange = oDocument.selection.createRange();
1858-    preCaretTextRange = oDocument.body.createTextRange();
1859-    preCaretTextRange.moveToElementText(this.domInputor);
1860-    preCaretTextRange.setEndPoint("EndToEnd", textRange);
1861-    return preCaretTextRange.text.length;
1862-  };
1863-
1864-  EditableCaret.prototype.getPos = function() {
1865-    var clonedRange, pos, range;
1866-    if (range = this.range()) {
1867-      clonedRange = range.cloneRange();
1868-      clonedRange.selectNodeContents(this.domInputor);
1869-      clonedRange.setEnd(range.endContainer, range.endOffset);
1870-      pos = clonedRange.toString().length;
1871-      clonedRange.detach();
1872-      return pos;
1873-    } else if (oDocument.selection) {
1874-      return this.getOldIEPos();
1875-    }
1876-  };
1877-
1878-  EditableCaret.prototype.getOldIEOffset = function() {
1879-    var range, rect;
1880-    range = oDocument.selection.createRange().duplicate();
1881-    range.moveStart("character", -1);
1882-    rect = range.getBoundingClientRect();
1883-    return {
1884-      height: rect.bottom - rect.top,
1885-      left: rect.left,
1886-      top: rect.top
1887-    };
1888-  };
1889-
1890-  EditableCaret.prototype.getOffset = function(pos) {
1891-    var clonedRange, offset, range, rect, shadowCaret;
1892-    if (oWindow.getSelection && (range = this.range())) {
1893-      if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
1894-        clonedRange = range.cloneRange();
1895-        clonedRange.setStart(range.endContainer, range.endOffset - 1);
1896-        clonedRange.setEnd(range.endContainer, range.endOffset);
1897-        rect = clonedRange.getBoundingClientRect();
1898-        offset = {
1899-          height: rect.height,
1900-          left: rect.left + rect.width,
1901-          top: rect.top
1902-        };
1903-        clonedRange.detach();
1904-      }
1905-      if (!offset || (offset != null ? offset.height : void 0) === 0) {
1906-        clonedRange = range.cloneRange();
1907-        shadowCaret = $(oDocument.createTextNode("|"));
1908-        clonedRange.insertNode(shadowCaret[0]);
1909-        clonedRange.selectNode(shadowCaret[0]);
1910-        rect = clonedRange.getBoundingClientRect();
1911-        offset = {
1912-          height: rect.height,
1913-          left: rect.left,
1914-          top: rect.top
1915-        };
1916-        shadowCaret.remove();
1917-        clonedRange.detach();
1918-      }
1919-    } else if (oDocument.selection) {
1920-      offset = this.getOldIEOffset();
1921-    }
1922-    if (offset) {
1923-      offset.top += $(oWindow).scrollTop();
1924-      offset.left += $(oWindow).scrollLeft();
1925-    }
1926-    return offset;
1927-  };
1928-
1929-  EditableCaret.prototype.range = function() {
1930-    var sel;
1931-    if (!oWindow.getSelection) {
1932-      return;
1933-    }
1934-    sel = oWindow.getSelection();
1935-    if (sel.rangeCount > 0) {
1936-      return sel.getRangeAt(0);
1937-    } else {
1938-      return null;
1939-    }
1940-  };
1941-
1942-  return EditableCaret;
1943-
1944-})();
1945-
1946-InputCaret = (function() {
1947-  function InputCaret($inputor) {
1948-    this.$inputor = $inputor;
1949-    this.domInputor = this.$inputor[0];
1950-  }
1951-
1952-  InputCaret.prototype.getIEPos = function() {
1953-    var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
1954-    inputor = this.domInputor;
1955-    range = oDocument.selection.createRange();
1956-    pos = 0;
1957-    if (range && range.parentElement() === inputor) {
1958-      normalizedValue = inputor.value.replace(/\r\n/g, "\n");
1959-      len = normalizedValue.length;
1960-      textInputRange = inputor.createTextRange();
1961-      textInputRange.moveToBookmark(range.getBookmark());
1962-      endRange = inputor.createTextRange();
1963-      endRange.collapse(false);
1964-      if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
1965-        pos = len;
1966-      } else {
1967-        pos = -textInputRange.moveStart("character", -len);
1968-      }
1969-    }
1970-    return pos;
1971-  };
1972-
1973-  InputCaret.prototype.getPos = function() {
1974-    if (oDocument.selection) {
1975-      return this.getIEPos();
1976-    } else {
1977-      return this.domInputor.selectionStart;
1978-    }
1979-  };
1980-
1981-  InputCaret.prototype.setPos = function(pos) {
1982-    var inputor, range;
1983-    inputor = this.domInputor;
1984-    if (oDocument.selection) {
1985-      range = inputor.createTextRange();
1986-      range.move("character", pos);
1987-      range.select();
1988-    } else if (inputor.setSelectionRange) {
1989-      inputor.setSelectionRange(pos, pos);
1990-    }
1991-    return inputor;
1992-  };
1993-
1994-  InputCaret.prototype.getIEOffset = function(pos) {
1995-    var h, textRange, x, y;
1996-    textRange = this.domInputor.createTextRange();
1997-    pos || (pos = this.getPos());
1998-    textRange.move('character', pos);
1999-    x = textRange.boundingLeft;
2000-    y = textRange.boundingTop;
2001-    h = textRange.boundingHeight;
2002-    return {
2003-      left: x,
2004-      top: y,
2005-      height: h
2006-    };
2007-  };
2008-
2009-  InputCaret.prototype.getOffset = function(pos) {
2010-    var $inputor, offset, position;
2011-    $inputor = this.$inputor;
2012-    if (oDocument.selection) {
2013-      offset = this.getIEOffset(pos);
2014-      offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
2015-      offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
2016-      return offset;
2017-    } else {
2018-      offset = $inputor.offset();
2019-      position = this.getPosition(pos);
2020-      return offset = {
2021-        left: offset.left + position.left - $inputor.scrollLeft(),
2022-        top: offset.top + position.top - $inputor.scrollTop(),
2023-        height: position.height
2024-      };
2025-    }
2026-  };
2027-
2028-  InputCaret.prototype.getPosition = function(pos) {
2029-    var $inputor, at_rect, end_range, format, html, mirror, start_range;
2030-    $inputor = this.$inputor;
2031-    format = function(value) {
2032-      value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
2033-      if (/firefox/i.test(navigator.userAgent)) {
2034-        value = value.replace(/\s/g, '&nbsp;');
2035-      }
2036-      return value;
2037-    };
2038-    if (pos === void 0) {
2039-      pos = this.getPos();
2040-    }
2041-    start_range = $inputor.val().slice(0, pos);
2042-    end_range = $inputor.val().slice(pos);
2043-    html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
2044-    html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
2045-    html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
2046-    mirror = new Mirror($inputor);
2047-    return at_rect = mirror.create(html).rect();
2048-  };
2049-
2050-  InputCaret.prototype.getIEPosition = function(pos) {
2051-    var h, inputorOffset, offset, x, y;
2052-    offset = this.getIEOffset(pos);
2053-    inputorOffset = this.$inputor.offset();
2054-    x = offset.left - inputorOffset.left;
2055-    y = offset.top - inputorOffset.top;
2056-    h = offset.height;
2057-    return {
2058-      left: x,
2059-      top: y,
2060-      height: h
2061-    };
2062-  };
2063-
2064-  return InputCaret;
2065-
2066-})();
2067-
2068-Mirror = (function() {
2069-  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"];
2070-
2071-  function Mirror($inputor) {
2072-    this.$inputor = $inputor;
2073-  }
2074-
2075-  Mirror.prototype.mirrorCss = function() {
2076-    var css,
2077-      _this = this;
2078-    css = {
2079-      position: 'absolute',
2080-      left: -9999,
2081-      top: 0,
2082-      zIndex: -20000
2083-    };
2084-    if (this.$inputor.prop('tagName') === 'TEXTAREA') {
2085-      this.css_attr.push('width');
2086-    }
2087-    $.each(this.css_attr, function(i, p) {
2088-      return css[p] = _this.$inputor.css(p);
2089-    });
2090-    return css;
2091-  };
2092-
2093-  Mirror.prototype.create = function(html) {
2094-    this.$mirror = $('<div></div>');
2095-    this.$mirror.css(this.mirrorCss());
2096-    this.$mirror.html(html);
2097-    this.$inputor.after(this.$mirror);
2098-    return this;
2099-  };
2100-
2101-  Mirror.prototype.rect = function() {
2102-    var $flag, pos, rect;
2103-    $flag = this.$mirror.find("#caret");
2104-    pos = $flag.position();
2105-    rect = {
2106-      left: pos.left,
2107-      top: pos.top,
2108-      height: $flag.height()
2109-    };
2110-    this.$mirror.remove();
2111-    return rect;
2112-  };
2113-
2114-  return Mirror;
2115-
2116-})();
2117-
2118-Utils = {
2119-  contentEditable: function($inputor) {
2120-    return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
2121-  }
2122-};
2123-
2124-methods = {
2125-  pos: function(pos) {
2126-    if (pos || pos === 0) {
2127-      return this.setPos(pos);
2128-    } else {
2129-      return this.getPos();
2130-    }
2131-  },
2132-  position: function(pos) {
2133-    if (oDocument.selection) {
2134-      return this.getIEPosition(pos);
2135-    } else {
2136-      return this.getPosition(pos);
2137-    }
2138-  },
2139-  offset: function(pos) {
2140-    var offset;
2141-    offset = this.getOffset(pos);
2142-    return offset;
2143-  }
2144-};
2145-
2146-oDocument = null;
2147-
2148-oWindow = null;
2149-
2150-oFrame = null;
2151-
2152-setContextBy = function(settings) {
2153-  var iframe;
2154-  if (iframe = settings != null ? settings.iframe : void 0) {
2155-    oFrame = iframe;
2156-    oWindow = iframe.contentWindow;
2157-    return oDocument = iframe.contentDocument || oWindow.document;
2158-  } else {
2159-    oFrame = void 0;
2160-    oWindow = window;
2161-    return oDocument = document;
2162-  }
2163-};
2164-
2165-discoveryIframeOf = function($dom) {
2166-  var error;
2167-  oDocument = $dom[0].ownerDocument;
2168-  oWindow = oDocument.defaultView || oDocument.parentWindow;
2169-  try {
2170-    return oFrame = oWindow.frameElement;
2171-  } catch (_error) {
2172-    error = _error;
2173-  }
2174-};
2175-
2176-$.fn.caret = function(method, value, settings) {
2177-  var caret;
2178-  if (methods[method]) {
2179-    if ($.isPlainObject(value)) {
2180-      setContextBy(value);
2181-      value = void 0;
2182-    } else {
2183-      setContextBy(settings);
2184-    }
2185-    caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
2186-    return methods[method].apply(caret, [value]);
2187-  } else {
2188-    return $.error("Method " + method + " does not exist on jQuery.caret");
2189-  }
2190-};
2191-
2192-$.fn.caret.EditableCaret = EditableCaret;
2193-
2194-$.fn.caret.InputCaret = InputCaret;
2195-
2196-$.fn.caret.Utils = Utils;
2197-
2198-$.fn.caret.apis = methods;
2199-
2200-
2201-}));
2202diff --git src/bp-core/js/vendor/jquery.caret.txt src/bp-core/js/vendor/jquery.caret.txt
2203deleted file mode 100644
2204index 36cd1c122..000000000
2205--- src/bp-core/js/vendor/jquery.caret.txt
2206+++ /dev/null
2207@@ -1,22 +0,0 @@
2208-Copyright (c) 2013 chord.luo@gmail.com
2209-
2210-Permission is hereby granted, free of charge, to any person
2211-obtaining a copy of this software and associated documentation
2212-files (the "Software"), to deal in the Software without
2213-restriction, including without limitation the rights to use,
2214-copy, modify, merge, publish, distribute, sublicense, and/or sell
2215-copies of the Software, and to permit persons to whom the
2216-Software is furnished to do so, subject to the following
2217-conditions:
2218-
2219-The above copyright notice and this permission notice shall be
2220-included in all copies or substantial portions of the Software.
2221-
2222-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2223-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
2224-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2225-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
2226-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2227-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2228-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2229-OTHER DEALINGS IN THE SOFTWARE.
2230diff --git src/bp-core/js/vendor/tribute.js src/bp-core/js/vendor/tribute.js
2231new file mode 100644
2232index 000000000..bd029c928
2233--- /dev/null
2234+++ src/bp-core/js/vendor/tribute.js
2235@@ -0,0 +1,1798 @@
2236+(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){
2237+"use strict";
2238+
2239+Object.defineProperty(exports, "__esModule", {
2240+    value: true
2241+});
2242+
2243+var _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; }; }();
2244+
2245+var _utils = require("./utils");
2246+
2247+var _utils2 = _interopRequireDefault(_utils);
2248+
2249+var _TributeEvents = require("./TributeEvents");
2250+
2251+var _TributeEvents2 = _interopRequireDefault(_TributeEvents);
2252+
2253+var _TributeMenuEvents = require("./TributeMenuEvents");
2254+
2255+var _TributeMenuEvents2 = _interopRequireDefault(_TributeMenuEvents);
2256+
2257+var _TributeRange = require("./TributeRange");
2258+
2259+var _TributeRange2 = _interopRequireDefault(_TributeRange);
2260+
2261+var _TributeSearch = require("./TributeSearch");
2262+
2263+var _TributeSearch2 = _interopRequireDefault(_TributeSearch);
2264+
2265+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2266+
2267+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2268+
2269+var Tribute = function () {
2270+    function Tribute(_ref) {
2271+        var _this = this;
2272+
2273+        var _ref$values = _ref.values,
2274+            values = _ref$values === undefined ? null : _ref$values,
2275+            _ref$iframe = _ref.iframe,
2276+            iframe = _ref$iframe === undefined ? null : _ref$iframe,
2277+            _ref$selectClass = _ref.selectClass,
2278+            selectClass = _ref$selectClass === undefined ? 'highlight' : _ref$selectClass,
2279+            _ref$trigger = _ref.trigger,
2280+            trigger = _ref$trigger === undefined ? '@' : _ref$trigger,
2281+            _ref$selectTemplate = _ref.selectTemplate,
2282+            selectTemplate = _ref$selectTemplate === undefined ? null : _ref$selectTemplate,
2283+            _ref$menuItemTemplate = _ref.menuItemTemplate,
2284+            menuItemTemplate = _ref$menuItemTemplate === undefined ? null : _ref$menuItemTemplate,
2285+            _ref$lookup = _ref.lookup,
2286+            lookup = _ref$lookup === undefined ? 'key' : _ref$lookup,
2287+            _ref$fillAttr = _ref.fillAttr,
2288+            fillAttr = _ref$fillAttr === undefined ? 'value' : _ref$fillAttr,
2289+            _ref$collection = _ref.collection,
2290+            collection = _ref$collection === undefined ? null : _ref$collection,
2291+            _ref$menuContainer = _ref.menuContainer,
2292+            menuContainer = _ref$menuContainer === undefined ? null : _ref$menuContainer,
2293+            _ref$noMatchTemplate = _ref.noMatchTemplate,
2294+            noMatchTemplate = _ref$noMatchTemplate === undefined ? null : _ref$noMatchTemplate,
2295+            _ref$requireLeadingSp = _ref.requireLeadingSpace,
2296+            requireLeadingSpace = _ref$requireLeadingSp === undefined ? true : _ref$requireLeadingSp,
2297+            _ref$allowSpaces = _ref.allowSpaces,
2298+            allowSpaces = _ref$allowSpaces === undefined ? false : _ref$allowSpaces,
2299+            _ref$replaceTextSuffi = _ref.replaceTextSuffix,
2300+            replaceTextSuffix = _ref$replaceTextSuffi === undefined ? null : _ref$replaceTextSuffi,
2301+            _ref$positionMenu = _ref.positionMenu,
2302+            positionMenu = _ref$positionMenu === undefined ? true : _ref$positionMenu,
2303+            _ref$spaceSelectsMatc = _ref.spaceSelectsMatch,
2304+            spaceSelectsMatch = _ref$spaceSelectsMatc === undefined ? false : _ref$spaceSelectsMatc;
2305+
2306+        _classCallCheck(this, Tribute);
2307+
2308+        this.menuSelected = 0;
2309+        this.current = {};
2310+        this.inputEvent = false;
2311+        this.isActive = false;
2312+        this.menuContainer = menuContainer;
2313+        this.allowSpaces = allowSpaces;
2314+        this.replaceTextSuffix = replaceTextSuffix;
2315+        this.positionMenu = positionMenu;
2316+        this.hasTrailingSpace = false;
2317+        this.spaceSelectsMatch = spaceSelectsMatch;
2318+
2319+        if (values) {
2320+            this.collection = [{
2321+                // symbol that starts the lookup
2322+                trigger: trigger,
2323+
2324+                // is it wrapped in an iframe
2325+                iframe: iframe,
2326+
2327+                // class applied to selected item
2328+                selectClass: selectClass,
2329+
2330+                // function called on select that retuns the content to insert
2331+                selectTemplate: (selectTemplate || Tribute.defaultSelectTemplate).bind(this),
2332+
2333+                // function called that returns content for an item
2334+                menuItemTemplate: (menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(this),
2335+
2336+                // function called when menu is empty, disables hiding of menu.
2337+                noMatchTemplate: function (t) {
2338+                    if (typeof t === 'function') {
2339+                        return t.bind(_this);
2340+                    }
2341+
2342+                    return noMatchTemplate;
2343+                }(noMatchTemplate),
2344+
2345+                // column to search against in the object
2346+                lookup: lookup,
2347+
2348+                // column that contains the content to insert by default
2349+                fillAttr: fillAttr,
2350+
2351+                // array of objects or a function returning an array of objects
2352+                values: values,
2353+
2354+                requireLeadingSpace: requireLeadingSpace
2355+            }];
2356+        } else if (collection) {
2357+            this.collection = collection.map(function (item) {
2358+                return {
2359+                    trigger: item.trigger || trigger,
2360+                    iframe: item.iframe || iframe,
2361+                    selectClass: item.selectClass || selectClass,
2362+                    selectTemplate: (item.selectTemplate || Tribute.defaultSelectTemplate).bind(_this),
2363+                    menuItemTemplate: (item.menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(_this),
2364+                    // function called when menu is empty, disables hiding of menu.
2365+                    noMatchTemplate: function (t) {
2366+                        if (typeof t === 'function') {
2367+                            return t.bind(_this);
2368+                        }
2369+
2370+                        return null;
2371+                    }(noMatchTemplate),
2372+                    lookup: item.lookup || lookup,
2373+                    fillAttr: item.fillAttr || fillAttr,
2374+                    values: item.values,
2375+                    requireLeadingSpace: item.requireLeadingSpace
2376+                };
2377+            });
2378+        } else {
2379+            throw new Error('[Tribute] No collection specified.');
2380+        }
2381+
2382+        new _TributeRange2.default(this);
2383+        new _TributeEvents2.default(this);
2384+        new _TributeMenuEvents2.default(this);
2385+        new _TributeSearch2.default(this);
2386+    }
2387+
2388+    _createClass(Tribute, [{
2389+        key: "triggers",
2390+        value: function triggers() {
2391+            return this.collection.map(function (config) {
2392+                return config.trigger;
2393+            });
2394+        }
2395+    }, {
2396+        key: "attach",
2397+        value: function attach(el) {
2398+            if (!el) {
2399+                throw new Error('[Tribute] Must pass in a DOM node or NodeList.');
2400+            }
2401+
2402+            // Check if it is a jQuery collection
2403+            if (typeof jQuery !== 'undefined' && el instanceof jQuery) {
2404+                el = el.get();
2405+            }
2406+
2407+            // Is el an Array/Array-like object?
2408+            if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
2409+                var length = el.length;
2410+                for (var i = 0; i < length; ++i) {
2411+                    this._attach(el[i]);
2412+                }
2413+            } else {
2414+                this._attach(el);
2415+            }
2416+        }
2417+    }, {
2418+        key: "_attach",
2419+        value: function _attach(el) {
2420+            if (el.hasAttribute('data-tribute')) {
2421+                console.warn('Tribute was already bound to ' + el.nodeName);
2422+            }
2423+
2424+            this.ensureEditable(el);
2425+            this.events.bind(el);
2426+            el.setAttribute('data-tribute', true);
2427+        }
2428+    }, {
2429+        key: "ensureEditable",
2430+        value: function ensureEditable(element) {
2431+            if (Tribute.inputTypes().indexOf(element.nodeName) === -1) {
2432+                if (element.contentEditable) {
2433+                    element.contentEditable = true;
2434+                } else {
2435+                    throw new Error('[Tribute] Cannot bind to ' + element.nodeName);
2436+                }
2437+            }
2438+        }
2439+    }, {
2440+        key: "createMenu",
2441+        value: function createMenu() {
2442+            var wrapper = this.range.getDocument().createElement('div'),
2443+                ul = this.range.getDocument().createElement('ul');
2444+
2445+            wrapper.className = 'tribute-container';
2446+            wrapper.appendChild(ul);
2447+
2448+            if (this.menuContainer) {
2449+                return this.menuContainer.appendChild(wrapper);
2450+            }
2451+
2452+            return this.range.getDocument().body.appendChild(wrapper);
2453+        }
2454+    }, {
2455+        key: "showMenuFor",
2456+        value: function showMenuFor(element, scrollTo) {
2457+            var _this2 = this;
2458+
2459+            // Only proceed if menu isn't already shown for the current element & mentionText
2460+            if (this.isActive && this.current.element === element && this.current.mentionText === this.currentMentionTextSnapshot) {
2461+                return;
2462+            }
2463+            this.currentMentionTextSnapshot = this.current.mentionText;
2464+
2465+            // create the menu if it doesn't exist.
2466+            if (!this.menu) {
2467+                this.menu = this.createMenu();
2468+                element.tributeMenu = this.menu;
2469+                this.menuEvents.bind(this.menu);
2470+            }
2471+
2472+            this.isActive = true;
2473+            this.menuSelected = 0;
2474+
2475+            if (!this.current.mentionText) {
2476+                this.current.mentionText = '';
2477+            }
2478+
2479+            var processValues = function processValues(values) {
2480+                // Tribute may not be active any more by the time the value callback returns
2481+                if (!_this2.isActive) {
2482+                    return;
2483+                }
2484+
2485+                var items = _this2.search.filter(_this2.current.mentionText, values, {
2486+                    pre: '<span>',
2487+                    post: '</span>',
2488+                    extract: function extract(el) {
2489+                        if (typeof _this2.current.collection.lookup === 'string') {
2490+                            return el[_this2.current.collection.lookup];
2491+                        } else if (typeof _this2.current.collection.lookup === 'function') {
2492+                            return _this2.current.collection.lookup(el, _this2.current.mentionText);
2493+                        } else {
2494+                            throw new Error('Invalid lookup attribute, lookup must be string or function.');
2495+                        }
2496+                    }
2497+                });
2498+
2499+                _this2.current.filteredItems = items;
2500+
2501+                var ul = _this2.menu.querySelector('ul');
2502+
2503+                _this2.range.positionMenuAtCaret(scrollTo);
2504+
2505+                if (!items.length) {
2506+                    var noMatchEvent = new CustomEvent('tribute-no-match', { detail: _this2.menu });
2507+                    _this2.current.element.dispatchEvent(noMatchEvent);
2508+                    if (!_this2.current.collection.noMatchTemplate) {
2509+                        _this2.hideMenu();
2510+                    } else {
2511+                        ul.innerHTML = _this2.current.collection.noMatchTemplate();
2512+                    }
2513+
2514+                    return;
2515+                }
2516+
2517+                ul.innerHTML = '';
2518+
2519+                items.forEach(function (item, index) {
2520+                    var li = _this2.range.getDocument().createElement('li');
2521+                    li.setAttribute('data-index', index);
2522+                    li.addEventListener('mouseenter', function (e) {
2523+                        var li = e.target;
2524+                        var index = li.getAttribute('data-index');
2525+                        _this2.events.setActiveLi(index);
2526+                    });
2527+                    if (_this2.menuSelected === index) {
2528+                        li.className = _this2.current.collection.selectClass;
2529+                    }
2530+                    li.innerHTML = _this2.current.collection.menuItemTemplate(item);
2531+                    ul.appendChild(li);
2532+                });
2533+            };
2534+
2535+            if (typeof this.current.collection.values === 'function') {
2536+                this.current.collection.values(this.current.mentionText, processValues);
2537+            } else {
2538+                processValues(this.current.collection.values);
2539+            }
2540+        }
2541+    }, {
2542+        key: "showMenuForCollection",
2543+        value: function showMenuForCollection(element, collectionIndex) {
2544+            if (element !== document.activeElement) {
2545+                this.placeCaretAtEnd(element);
2546+            }
2547+
2548+            this.current.collection = this.collection[collectionIndex || 0];
2549+            this.current.externalTrigger = true;
2550+            this.current.element = element;
2551+
2552+            if (element.isContentEditable) this.insertTextAtCursor(this.current.collection.trigger);else this.insertAtCaret(element, this.current.collection.trigger);
2553+
2554+            this.showMenuFor(element);
2555+        }
2556+
2557+        // TODO: make sure this works for inputs/textareas
2558+
2559+    }, {
2560+        key: "placeCaretAtEnd",
2561+        value: function placeCaretAtEnd(el) {
2562+            el.focus();
2563+            if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
2564+                var range = document.createRange();
2565+                range.selectNodeContents(el);
2566+                range.collapse(false);
2567+                var sel = window.getSelection();
2568+                sel.removeAllRanges();
2569+                sel.addRange(range);
2570+            } else if (typeof document.body.createTextRange != "undefined") {
2571+                var textRange = document.body.createTextRange();
2572+                textRange.moveToElementText(el);
2573+                textRange.collapse(false);
2574+                textRange.select();
2575+            }
2576+        }
2577+
2578+        // for contenteditable
2579+
2580+    }, {
2581+        key: "insertTextAtCursor",
2582+        value: function insertTextAtCursor(text) {
2583+            var sel, range, html;
2584+            sel = window.getSelection();
2585+            range = sel.getRangeAt(0);
2586+            range.deleteContents();
2587+            var textNode = document.createTextNode(text);
2588+            range.insertNode(textNode);
2589+            range.selectNodeContents(textNode);
2590+            range.collapse(false);
2591+            sel.removeAllRanges();
2592+            sel.addRange(range);
2593+        }
2594+
2595+        // for regular inputs
2596+
2597+    }, {
2598+        key: "insertAtCaret",
2599+        value: function insertAtCaret(textarea, text) {
2600+            var scrollPos = textarea.scrollTop;
2601+            var caretPos = textarea.selectionStart;
2602+
2603+            var front = textarea.value.substring(0, caretPos);
2604+            var back = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
2605+            textarea.value = front + text + back;
2606+            caretPos = caretPos + text.length;
2607+            textarea.selectionStart = caretPos;
2608+            textarea.selectionEnd = caretPos;
2609+            textarea.focus();
2610+            textarea.scrollTop = scrollPos;
2611+        }
2612+    }, {
2613+        key: "hideMenu",
2614+        value: function hideMenu() {
2615+            if (this.menu) {
2616+                this.menu.style.cssText = 'display: none;';
2617+                this.isActive = false;
2618+                this.menuSelected = 0;
2619+                this.current = {};
2620+            }
2621+        }
2622+    }, {
2623+        key: "selectItemAtIndex",
2624+        value: function selectItemAtIndex(index, originalEvent) {
2625+            index = parseInt(index);
2626+            if (typeof index !== 'number') return;
2627+            var item = this.current.filteredItems[index];
2628+            var content = this.current.collection.selectTemplate(item);
2629+            if (content !== null) this.replaceText(content, originalEvent, item);
2630+        }
2631+    }, {
2632+        key: "replaceText",
2633+        value: function replaceText(content, originalEvent, item) {
2634+            this.range.replaceTriggerText(content, true, true, originalEvent, item);
2635+        }
2636+    }, {
2637+        key: "_append",
2638+        value: function _append(collection, newValues, replace) {
2639+            if (typeof collection.values === 'function') {
2640+                throw new Error('Unable to append to values, as it is a function.');
2641+            } else if (!replace) {
2642+                collection.values = collection.values.concat(newValues);
2643+            } else {
2644+                collection.values = newValues;
2645+            }
2646+        }
2647+    }, {
2648+        key: "append",
2649+        value: function append(collectionIndex, newValues, replace) {
2650+            var index = parseInt(collectionIndex);
2651+            if (typeof index !== 'number') throw new Error('please provide an index for the collection to update.');
2652+
2653+            var collection = this.collection[index];
2654+
2655+            this._append(collection, newValues, replace);
2656+        }
2657+    }, {
2658+        key: "appendCurrent",
2659+        value: function appendCurrent(newValues, replace) {
2660+            if (this.isActive) {
2661+                this._append(this.current.collection, newValues, replace);
2662+            } else {
2663+                throw new Error('No active state. Please use append instead and pass an index.');
2664+            }
2665+        }
2666+    }, {
2667+        key: "detach",
2668+        value: function detach(el) {
2669+            if (!el) {
2670+                throw new Error('[Tribute] Must pass in a DOM node or NodeList.');
2671+            }
2672+
2673+            // Check if it is a jQuery collection
2674+            if (typeof jQuery !== 'undefined' && el instanceof jQuery) {
2675+                el = el.get();
2676+            }
2677+
2678+            // Is el an Array/Array-like object?
2679+            if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
2680+                var length = el.length;
2681+                for (var i = 0; i < length; ++i) {
2682+                    this._detach(el[i]);
2683+                }
2684+            } else {
2685+                this._detach(el);
2686+            }
2687+        }
2688+    }, {
2689+        key: "_detach",
2690+        value: function _detach(el) {
2691+            var _this3 = this;
2692+
2693+            this.events.unbind(el);
2694+            if (el.tributeMenu) {
2695+                this.menuEvents.unbind(el.tributeMenu);
2696+            }
2697+
2698+            setTimeout(function () {
2699+                el.removeAttribute('data-tribute');
2700+                _this3.isActive = false;
2701+                if (el.tributeMenu) {
2702+                    el.tributeMenu.remove();
2703+                }
2704+            });
2705+        }
2706+    }], [{
2707+        key: "defaultSelectTemplate",
2708+        value: function defaultSelectTemplate(item) {
2709+            if (typeof item === 'undefined') return null;
2710+            if (this.range.isContentEditable(this.current.element)) {
2711+                return '<span class="tribute-mention">' + (this.current.collection.trigger + item.original[this.current.collection.fillAttr]) + '</span>';
2712+            }
2713+
2714+            return this.current.collection.trigger + item.original[this.current.collection.fillAttr];
2715+        }
2716+    }, {
2717+        key: "defaultMenuItemTemplate",
2718+        value: function defaultMenuItemTemplate(matchItem) {
2719+            return matchItem.string;
2720+        }
2721+    }, {
2722+        key: "inputTypes",
2723+        value: function inputTypes() {
2724+            return ['TEXTAREA', 'INPUT'];
2725+        }
2726+    }]);
2727+
2728+    return Tribute;
2729+}();
2730+
2731+exports.default = Tribute;
2732+module.exports = exports["default"];
2733+
2734+},{"./TributeEvents":2,"./TributeMenuEvents":3,"./TributeRange":4,"./TributeSearch":5,"./utils":7}],2:[function(require,module,exports){
2735+'use strict';
2736+
2737+Object.defineProperty(exports, "__esModule", {
2738+    value: true
2739+});
2740+
2741+var _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; }; }();
2742+
2743+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2744+
2745+var TributeEvents = function () {
2746+    function TributeEvents(tribute) {
2747+        _classCallCheck(this, TributeEvents);
2748+
2749+        this.tribute = tribute;
2750+        this.tribute.events = this;
2751+    }
2752+
2753+    _createClass(TributeEvents, [{
2754+        key: 'bind',
2755+        value: function bind(element) {
2756+            element.boundKeydown = this.keydown.bind(element, this);
2757+            element.boundKeyup = this.keyup.bind(element, this);
2758+            element.boundInput = this.input.bind(element, this);
2759+
2760+            element.addEventListener('keydown', element.boundKeydown, false);
2761+            element.addEventListener('keyup', element.boundKeyup, false);
2762+            element.addEventListener('input', element.boundInput, false);
2763+        }
2764+    }, {
2765+        key: 'unbind',
2766+        value: function unbind(element) {
2767+            element.removeEventListener('keydown', element.boundKeydown, false);
2768+            element.removeEventListener('keyup', element.boundKeyup, false);
2769+            element.removeEventListener('input', element.boundInput, false);
2770+
2771+            delete element.boundKeydown;
2772+            delete element.boundKeyup;
2773+            delete element.boundInput;
2774+        }
2775+    }, {
2776+        key: 'keydown',
2777+        value: function keydown(instance, event) {
2778+            if (instance.shouldDeactivate(event)) {
2779+                instance.tribute.isActive = false;
2780+                instance.tribute.hideMenu();
2781+            }
2782+
2783+            var element = this;
2784+            instance.commandEvent = false;
2785+
2786+            TributeEvents.keys().forEach(function (o) {
2787+                if (o.key === event.keyCode) {
2788+                    instance.commandEvent = true;
2789+                    instance.callbacks()[o.value.toLowerCase()](event, element);
2790+                }
2791+            });
2792+        }
2793+    }, {
2794+        key: 'input',
2795+        value: function input(instance, event) {
2796+            instance.inputEvent = true;
2797+            instance.keyup.call(this, instance, event);
2798+        }
2799+    }, {
2800+        key: 'click',
2801+        value: function click(instance, event) {
2802+            var tribute = instance.tribute;
2803+            if (tribute.menu && tribute.menu.contains(event.target)) {
2804+                var li = event.target;
2805+                event.preventDefault();
2806+                event.stopPropagation();
2807+                while (li.nodeName.toLowerCase() !== 'li') {
2808+                    li = li.parentNode;
2809+                    if (!li || li === tribute.menu) {
2810+                        throw new Error('cannot find the <li> container for the click');
2811+                    }
2812+                }
2813+                tribute.selectItemAtIndex(li.getAttribute('data-index'), event);
2814+                tribute.hideMenu();
2815+
2816+                // TODO: should fire with externalTrigger and target is outside of menu
2817+            } else if (tribute.current.element && !tribute.current.externalTrigger) {
2818+                tribute.current.externalTrigger = false;
2819+                setTimeout(function () {
2820+                    return tribute.hideMenu();
2821+                });
2822+            }
2823+        }
2824+    }, {
2825+        key: 'keyup',
2826+        value: function keyup(instance, event) {
2827+            if (instance.inputEvent) {
2828+                instance.inputEvent = false;
2829+            }
2830+            instance.updateSelection(this);
2831+
2832+            if (event.keyCode === 27) return;
2833+
2834+            if (!instance.tribute.allowSpaces && instance.tribute.hasTrailingSpace) {
2835+                instance.tribute.hasTrailingSpace = false;
2836+                instance.commandEvent = true;
2837+                instance.callbacks()["space"](event, this);
2838+                return;
2839+            }
2840+
2841+            if (!instance.tribute.isActive) {
2842+                var keyCode = instance.getKeyCode(instance, this, event);
2843+
2844+                if (isNaN(keyCode) || !keyCode) return;
2845+
2846+                var trigger = instance.tribute.triggers().find(function (trigger) {
2847+                    return trigger.charCodeAt(0) === keyCode;
2848+                });
2849+
2850+                if (typeof trigger !== 'undefined') {
2851+                    instance.callbacks().triggerChar(event, this, trigger);
2852+                }
2853+            }
2854+
2855+            if (instance.tribute.current.trigger && instance.commandEvent === false || instance.tribute.isActive && event.keyCode === 8) {
2856+                instance.tribute.showMenuFor(this, true);
2857+            }
2858+        }
2859+    }, {
2860+        key: 'shouldDeactivate',
2861+        value: function shouldDeactivate(event) {
2862+            if (!this.tribute.isActive) return false;
2863+
2864+            if (this.tribute.current.mentionText.length === 0) {
2865+                var eventKeyPressed = false;
2866+                TributeEvents.keys().forEach(function (o) {
2867+                    if (event.keyCode === o.key) eventKeyPressed = true;
2868+                });
2869+
2870+                return !eventKeyPressed;
2871+            }
2872+
2873+            return false;
2874+        }
2875+    }, {
2876+        key: 'getKeyCode',
2877+        value: function getKeyCode(instance, el, event) {
2878+            var char = void 0;
2879+            var tribute = instance.tribute;
2880+            var info = tribute.range.getTriggerInfo(false, tribute.hasTrailingSpace, true, tribute.allowSpaces);
2881+
2882+            if (info) {
2883+                return info.mentionTriggerChar.charCodeAt(0);
2884+            } else {
2885+                return false;
2886+            }
2887+        }
2888+    }, {
2889+        key: 'updateSelection',
2890+        value: function updateSelection(el) {
2891+            this.tribute.current.element = el;
2892+            var info = this.tribute.range.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces);
2893+
2894+            if (info) {
2895+                this.tribute.current.selectedPath = info.mentionSelectedPath;
2896+                this.tribute.current.mentionText = info.mentionText;
2897+                this.tribute.current.selectedOffset = info.mentionSelectedOffset;
2898+            }
2899+        }
2900+    }, {
2901+        key: 'callbacks',
2902+        value: function callbacks() {
2903+            var _this = this;
2904+
2905+            return {
2906+                triggerChar: function triggerChar(e, el, trigger) {
2907+                    var tribute = _this.tribute;
2908+                    tribute.current.trigger = trigger;
2909+
2910+                    var collectionItem = tribute.collection.find(function (item) {
2911+                        return item.trigger === trigger;
2912+                    });
2913+
2914+                    tribute.current.collection = collectionItem;
2915+                    if (tribute.inputEvent) tribute.showMenuFor(el, true);
2916+                },
2917+                enter: function enter(e, el) {
2918+                    // choose selection
2919+                    if (_this.tribute.isActive) {
2920+                        e.preventDefault();
2921+                        e.stopPropagation();
2922+                        setTimeout(function () {
2923+                            _this.tribute.selectItemAtIndex(_this.tribute.menuSelected, e);
2924+                            _this.tribute.hideMenu();
2925+                        }, 0);
2926+                    }
2927+                },
2928+                escape: function escape(e, el) {
2929+                    if (_this.tribute.isActive) {
2930+                        e.preventDefault();
2931+                        e.stopPropagation();
2932+                        _this.tribute.isActive = false;
2933+                        _this.tribute.hideMenu();
2934+                    }
2935+                },
2936+                tab: function tab(e, el) {
2937+                    // choose first match
2938+                    _this.callbacks().enter(e, el);
2939+                },
2940+                space: function space(e, el) {
2941+                    if (_this.tribute.isActive) {
2942+                        if (_this.tribute.spaceSelectsMatch) {
2943+                            _this.callbacks().enter(e, el);
2944+                        } else if (!_this.tribute.allowSpaces) {
2945+                            e.stopPropagation();
2946+                            setTimeout(function () {
2947+                                _this.tribute.hideMenu();
2948+                                _this.tribute.isActive = false;
2949+                            }, 0);
2950+                        }
2951+                    }
2952+                },
2953+                up: function up(e, el) {
2954+                    // navigate up ul
2955+                    if (_this.tribute.isActive) {
2956+                        e.preventDefault();
2957+                        e.stopPropagation();
2958+                        var count = _this.tribute.current.filteredItems.length,
2959+                            selected = _this.tribute.menuSelected;
2960+
2961+                        if (count > selected && selected > 0) {
2962+                            _this.tribute.menuSelected--;
2963+                            _this.setActiveLi();
2964+                        } else if (selected === 0) {
2965+                            _this.tribute.menuSelected = count - 1;
2966+                            _this.setActiveLi();
2967+                            _this.tribute.menu.scrollTop = _this.tribute.menu.scrollHeight;
2968+                        }
2969+                    }
2970+                },
2971+                down: function down(e, el) {
2972+                    // navigate down ul
2973+                    if (_this.tribute.isActive) {
2974+                        e.preventDefault();
2975+                        e.stopPropagation();
2976+                        var count = _this.tribute.current.filteredItems.length - 1,
2977+                            selected = _this.tribute.menuSelected;
2978+
2979+                        if (count > selected) {
2980+                            _this.tribute.menuSelected++;
2981+                            _this.setActiveLi();
2982+                        } else if (count === selected) {
2983+                            _this.tribute.menuSelected = 0;
2984+                            _this.setActiveLi();
2985+                            _this.tribute.menu.scrollTop = 0;
2986+                        }
2987+                    }
2988+                },
2989+                delete: function _delete(e, el) {
2990+                    if (_this.tribute.isActive && _this.tribute.current.mentionText.length < 1) {
2991+                        _this.tribute.hideMenu();
2992+                    } else if (_this.tribute.isActive) {
2993+                        _this.tribute.showMenuFor(el);
2994+                    }
2995+                }
2996+            };
2997+        }
2998+    }, {
2999+        key: 'setActiveLi',
3000+        value: function setActiveLi(index) {
3001+            var lis = this.tribute.menu.querySelectorAll('li'),
3002+                length = lis.length >>> 0;
3003+
3004+            // get heights
3005+            var menuFullHeight = this.getFullHeight(this.tribute.menu),
3006+                liHeight = this.getFullHeight(lis[0]);
3007+
3008+            if (index) this.tribute.menuSelected = index;
3009+
3010+            for (var i = 0; i < length; i++) {
3011+                var li = lis[i];
3012+                if (i === this.tribute.menuSelected) {
3013+                    var offset = liHeight * (i + 1);
3014+                    var scrollTop = this.tribute.menu.scrollTop;
3015+                    var totalScroll = scrollTop + menuFullHeight;
3016+
3017+                    if (offset > totalScroll) {
3018+                        this.tribute.menu.scrollTop += liHeight;
3019+                    } else if (offset < totalScroll) {
3020+                        this.tribute.menu.scrollTop -= liHeight;
3021+                    }
3022+
3023+                    li.className = this.tribute.current.collection.selectClass;
3024+                } else {
3025+                    li.className = '';
3026+                }
3027+            }
3028+        }
3029+    }, {
3030+        key: 'getFullHeight',
3031+        value: function getFullHeight(elem, includeMargin) {
3032+            var height = elem.getBoundingClientRect().height;
3033+
3034+            if (includeMargin) {
3035+                var style = elem.currentStyle || window.getComputedStyle(elem);
3036+                return height + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
3037+            }
3038+
3039+            return height;
3040+        }
3041+    }], [{
3042+        key: 'keys',
3043+        value: function keys() {
3044+            return [{
3045+                key: 9,
3046+                value: 'TAB'
3047+            }, {
3048+                key: 8,
3049+                value: 'DELETE'
3050+            }, {
3051+                key: 13,
3052+                value: 'ENTER'
3053+            }, {
3054+                key: 27,
3055+                value: 'ESCAPE'
3056+            }, {
3057+                key: 32,
3058+                value: 'SPACE'
3059+            }, {
3060+                key: 38,
3061+                value: 'UP'
3062+            }, {
3063+                key: 40,
3064+                value: 'DOWN'
3065+            }];
3066+        }
3067+    }]);
3068+
3069+    return TributeEvents;
3070+}();
3071+
3072+exports.default = TributeEvents;
3073+module.exports = exports['default'];
3074+
3075+},{}],3:[function(require,module,exports){
3076+'use strict';
3077+
3078+Object.defineProperty(exports, "__esModule", {
3079+    value: true
3080+});
3081+
3082+var _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; }; }();
3083+
3084+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3085+
3086+var TributeMenuEvents = function () {
3087+    function TributeMenuEvents(tribute) {
3088+        _classCallCheck(this, TributeMenuEvents);
3089+
3090+        this.tribute = tribute;
3091+        this.tribute.menuEvents = this;
3092+        this.menu = this.tribute.menu;
3093+    }
3094+
3095+    _createClass(TributeMenuEvents, [{
3096+        key: 'bind',
3097+        value: function bind(menu) {
3098+            var _this = this;
3099+
3100+            menu.menuKeydownEvent = this.tribute.events.keydown.bind(this.menu, this);
3101+            this.menuClickEvent = this.tribute.events.click.bind(null, this);
3102+            this.menuContainerScrollEvent = this.debounce(function () {
3103+                if (_this.tribute.isActive) {
3104+                    _this.tribute.showMenuFor(_this.tribute.current.element, false);
3105+                }
3106+            }, 300, false);
3107+            this.windowResizeEvent = this.debounce(function () {
3108+                if (_this.tribute.isActive) {
3109+                    _this.tribute.range.positionMenuAtCaret(true);
3110+                }
3111+            }, 300, false);
3112+
3113+            // fixes IE11 issues with mouseup
3114+            this.tribute.range.getDocument().addEventListener('MSPointerUp', this.menuClickEvent, false);
3115+            menu.addEventListener('keydown', this.menuKeydownEvent, false);
3116+            this.tribute.range.getDocument().addEventListener('mouseup', this.menuClickEvent, false);
3117+            window.addEventListener('resize', this.windowResizeEvent);
3118+
3119+            if (this.menuContainer) {
3120+                this.menuContainer.addEventListener('scroll', this.menuContainerScrollEvent, false);
3121+            } else {
3122+                window.addEventListener('scroll', this.menuContainerScrollEvent);
3123+            }
3124+        }
3125+    }, {
3126+        key: 'unbind',
3127+        value: function unbind(menu) {
3128+            menu.removeEventListener('keydown', menu.menuKeydownEvent, false);
3129+            delete menu.menuKeydownEvent;
3130+            this.tribute.range.getDocument().removeEventListener('mouseup', this.menuClickEvent, false);
3131+            this.tribute.range.getDocument().removeEventListener('MSPointerUp', this.menuClickEvent, false);
3132+            window.removeEventListener('resize', this.windowResizeEvent);
3133+
3134+            if (this.menuContainer) {
3135+                this.menuContainer.removeEventListener('scroll', this.menuContainerScrollEvent, false);
3136+            } else {
3137+                window.removeEventListener('scroll', this.menuContainerScrollEvent);
3138+            }
3139+        }
3140+    }, {
3141+        key: 'debounce',
3142+        value: function debounce(func, wait, immediate) {
3143+            var _this2 = this,
3144+                _arguments = arguments;
3145+
3146+            var timeout;
3147+            return function () {
3148+                var context = _this2,
3149+                    args = _arguments;
3150+                var later = function later() {
3151+                    timeout = null;
3152+                    if (!immediate) func.apply(context, args);
3153+                };
3154+                var callNow = immediate && !timeout;
3155+                clearTimeout(timeout);
3156+                timeout = setTimeout(later, wait);
3157+                if (callNow) func.apply(context, args);
3158+            };
3159+        }
3160+    }]);
3161+
3162+    return TributeMenuEvents;
3163+}();
3164+
3165+exports.default = TributeMenuEvents;
3166+module.exports = exports['default'];
3167+
3168+},{}],4:[function(require,module,exports){
3169+'use strict';
3170+
3171+Object.defineProperty(exports, "__esModule", {
3172+    value: true
3173+});
3174+
3175+var _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; }; }();
3176+
3177+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3178+
3179+// Thanks to https://github.com/jeff-collins/ment.io
3180+var TributeRange = function () {
3181+    function TributeRange(tribute) {
3182+        _classCallCheck(this, TributeRange);
3183+
3184+        this.tribute = tribute;
3185+        this.tribute.range = this;
3186+    }
3187+
3188+    _createClass(TributeRange, [{
3189+        key: 'getDocument',
3190+        value: function getDocument() {
3191+            var iframe = void 0;
3192+            if (this.tribute.current.collection) {
3193+                iframe = this.tribute.current.collection.iframe;
3194+            }
3195+
3196+            if (!iframe) {
3197+                return document;
3198+            }
3199+
3200+            return iframe.contentWindow.document;
3201+        }
3202+    }, {
3203+        key: 'positionMenuAtCaret',
3204+        value: function positionMenuAtCaret(scrollTo) {
3205+            var _this = this;
3206+
3207+            var context = this.tribute.current,
3208+                coordinates = void 0;
3209+
3210+            var info = this.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces);
3211+
3212+            if (typeof info !== 'undefined') {
3213+
3214+                if (!this.tribute.positionMenu) {
3215+                    this.tribute.menu.style.cssText = 'display: block;';
3216+                    return;
3217+                }
3218+
3219+                if (!this.isContentEditable(context.element)) {
3220+                    coordinates = this.getTextAreaOrInputUnderlinePosition(this.tribute.current.element, info.mentionPosition);
3221+                } else {
3222+                    coordinates = this.getContentEditableCaretPosition(info.mentionPosition);
3223+                }
3224+
3225+                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;';
3226+
3227+                if (coordinates.left === 'auto') {
3228+                    this.tribute.menu.style.left = 'auto';
3229+                }
3230+
3231+                if (coordinates.top === 'auto') {
3232+                    this.tribute.menu.style.top = 'auto';
3233+                }
3234+
3235+                if (scrollTo) this.scrollIntoView();
3236+
3237+                window.setTimeout(function () {
3238+                    var menuDimensions = {
3239+                        width: _this.tribute.menu.offsetWidth,
3240+                        height: _this.tribute.menu.offsetHeight
3241+                    };
3242+                    var menuIsOffScreen = _this.isMenuOffScreen(coordinates, menuDimensions);
3243+
3244+                    var menuIsOffScreenHorizontally = window.innerWidth > menuDimensions.width && (menuIsOffScreen.left || menuIsOffScreen.right);
3245+                    var menuIsOffScreenVertically = window.innerHeight > menuDimensions.height && (menuIsOffScreen.top || menuIsOffScreen.bottom);
3246+                    if (menuIsOffScreenHorizontally || menuIsOffScreenVertically) {
3247+                        _this.tribute.menu.style.cssText = 'display: none';
3248+                        _this.positionMenuAtCaret(scrollTo);
3249+                    }
3250+                }, 0);
3251+            } else {
3252+                this.tribute.menu.style.cssText = 'display: none';
3253+            }
3254+        }
3255+    }, {
3256+        key: 'selectElement',
3257+        value: function selectElement(targetElement, path, offset) {
3258+            var range = void 0;
3259+            var elem = targetElement;
3260+
3261+            if (path) {
3262+                for (var i = 0; i < path.length; i++) {
3263+                    elem = elem.childNodes[path[i]];
3264+                    if (elem === undefined) {
3265+                        return;
3266+                    }
3267+                    while (elem.length < offset) {
3268+                        offset -= elem.length;
3269+                        elem = elem.nextSibling;
3270+                    }
3271+                    if (elem.childNodes.length === 0 && !elem.length) {
3272+                        elem = elem.previousSibling;
3273+                    }
3274+                }
3275+            }
3276+            var sel = this.getWindowSelection();
3277+
3278+            range = this.getDocument().createRange();
3279+            range.setStart(elem, offset);
3280+            range.setEnd(elem, offset);
3281+            range.collapse(true);
3282+
3283+            try {
3284+                sel.removeAllRanges();
3285+            } catch (error) {}
3286+
3287+            sel.addRange(range);
3288+            targetElement.focus();
3289+        }
3290+    }, {
3291+        key: 'resetSelection',
3292+        value: function resetSelection(targetElement, path, offset) {
3293+            if (!this.isContentEditable(targetElement)) {
3294+                if (targetElement !== this.tribute.current.element) {
3295+                    targetElement.focus();
3296+                }
3297+            } else {
3298+                this.selectElement(targetElement, path, offset);
3299+            }
3300+        }
3301+    }, {
3302+        key: 'replaceTriggerText',
3303+        value: function replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) {
3304+            var context = this.tribute.current;
3305+            this.resetSelection(context.element, context.selectedPath, context.selectedOffset);
3306+
3307+            var info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces);
3308+
3309+            // Create the event
3310+            var replaceEvent = new CustomEvent('tribute-replaced', {
3311+                detail: {
3312+                    item: item,
3313+                    event: originalEvent
3314+                }
3315+            });
3316+
3317+            if (info !== undefined) {
3318+                if (!this.isContentEditable(context.element)) {
3319+                    var myField = this.tribute.current.element;
3320+                    var textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : ' ';
3321+                    text += textSuffix;
3322+                    var startPos = info.mentionPosition;
3323+                    var endPos = info.mentionPosition + info.mentionText.length + textSuffix.length;
3324+                    myField.value = myField.value.substring(0, startPos) + text + myField.value.substring(endPos, myField.value.length);
3325+                    myField.selectionStart = startPos + text.length;
3326+                    myField.selectionEnd = startPos + text.length;
3327+                } else {
3328+                    // add a space to the end of the pasted text
3329+                    var _textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : '\xA0';
3330+                    text += _textSuffix;
3331+                    this.pasteHtml(text, info.mentionPosition, info.mentionPosition + info.mentionText.length + 1);
3332+                }
3333+
3334+                context.element.dispatchEvent(replaceEvent);
3335+            }
3336+        }
3337+    }, {
3338+        key: 'pasteHtml',
3339+        value: function pasteHtml(html, startPos, endPos) {
3340+            var range = void 0,
3341+                sel = void 0;
3342+            sel = this.getWindowSelection();
3343+            range = this.getDocument().createRange();
3344+            range.setStart(sel.anchorNode, startPos);
3345+            range.setEnd(sel.anchorNode, endPos);
3346+            range.deleteContents();
3347+
3348+            var el = this.getDocument().createElement('div');
3349+            el.innerHTML = html;
3350+            var frag = this.getDocument().createDocumentFragment(),
3351+                node = void 0,
3352+                lastNode = void 0;
3353+            while (node = el.firstChild) {
3354+                lastNode = frag.appendChild(node);
3355+            }
3356+            range.insertNode(frag);
3357+
3358+            // Preserve the selection
3359+            if (lastNode) {
3360+                range = range.cloneRange();
3361+                range.setStartAfter(lastNode);
3362+                range.collapse(true);
3363+                sel.removeAllRanges();
3364+                sel.addRange(range);
3365+            }
3366+        }
3367+    }, {
3368+        key: 'getWindowSelection',
3369+        value: function getWindowSelection() {
3370+            if (this.tribute.collection.iframe) {
3371+                return this.tribute.collection.iframe.contentWindow.getSelection();
3372+            }
3373+
3374+            return window.getSelection();
3375+        }
3376+    }, {
3377+        key: 'getNodePositionInParent',
3378+        value: function getNodePositionInParent(element) {
3379+            if (element.parentNode === null) {
3380+                return 0;
3381+            }
3382+
3383+            for (var i = 0; i < element.parentNode.childNodes.length; i++) {
3384+                var node = element.parentNode.childNodes[i];
3385+
3386+                if (node === element) {
3387+                    return i;
3388+                }
3389+            }
3390+        }
3391+    }, {
3392+        key: 'getContentEditableSelectedPath',
3393+        value: function getContentEditableSelectedPath(ctx) {
3394+            var sel = this.getWindowSelection();
3395+            var selected = sel.anchorNode;
3396+            var path = [];
3397+            var offset = void 0;
3398+
3399+            if (selected != null) {
3400+                var i = void 0;
3401+                var ce = selected.contentEditable;
3402+                while (selected !== null && ce !== 'true') {
3403+                    i = this.getNodePositionInParent(selected);
3404+                    path.push(i);
3405+                    selected = selected.parentNode;
3406+                    if (selected !== null) {
3407+                        ce = selected.contentEditable;
3408+                    }
3409+                }
3410+                path.reverse();
3411+
3412+                // getRangeAt may not exist, need alternative
3413+                offset = sel.getRangeAt(0).startOffset;
3414+
3415+                return {
3416+                    selected: selected,
3417+                    path: path,
3418+                    offset: offset
3419+                };
3420+            }
3421+        }
3422+    }, {
3423+        key: 'getTextPrecedingCurrentSelection',
3424+        value: function getTextPrecedingCurrentSelection() {
3425+            var context = this.tribute.current,
3426+                text = '';
3427+
3428+            if (!this.isContentEditable(context.element)) {
3429+                var textComponent = this.tribute.current.element;
3430+                if (textComponent) {
3431+                    var startPos = textComponent.selectionStart;
3432+                    if (textComponent.value && startPos >= 0) {
3433+                        text = textComponent.value.substring(0, startPos);
3434+                    }
3435+                }
3436+            } else {
3437+                var selectedElem = this.getWindowSelection().anchorNode;
3438+
3439+                if (selectedElem != null) {
3440+                    var workingNodeContent = selectedElem.textContent;
3441+                    var selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset;
3442+
3443+                    if (workingNodeContent && selectStartOffset >= 0) {
3444+                        text = workingNodeContent.substring(0, selectStartOffset);
3445+                    }
3446+                }
3447+            }
3448+
3449+            return text;
3450+        }
3451+    }, {
3452+        key: 'getTriggerInfo',
3453+        value: function getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces) {
3454+            var _this2 = this;
3455+
3456+            var ctx = this.tribute.current;
3457+            var selected = void 0,
3458+                path = void 0,
3459+                offset = void 0;
3460+
3461+            if (!this.isContentEditable(ctx.element)) {
3462+                selected = this.tribute.current.element;
3463+            } else {
3464+                var selectionInfo = this.getContentEditableSelectedPath(ctx);
3465+
3466+                if (selectionInfo) {
3467+                    selected = selectionInfo.selected;
3468+                    path = selectionInfo.path;
3469+                    offset = selectionInfo.offset;
3470+                }
3471+            }
3472+
3473+            var effectiveRange = this.getTextPrecedingCurrentSelection();
3474+
3475+            if (effectiveRange !== undefined && effectiveRange !== null) {
3476+                var mostRecentTriggerCharPos = -1;
3477+                var triggerChar = void 0;
3478+
3479+                this.tribute.collection.forEach(function (config) {
3480+                    var c = config.trigger;
3481+                    var idx = config.requireLeadingSpace ? _this2.lastIndexWithLeadingSpace(effectiveRange, c) : effectiveRange.lastIndexOf(c);
3482+
3483+                    if (idx > mostRecentTriggerCharPos) {
3484+                        mostRecentTriggerCharPos = idx;
3485+                        triggerChar = c;
3486+                        requireLeadingSpace = config.requireLeadingSpace;
3487+                    }
3488+                });
3489+
3490+                if (mostRecentTriggerCharPos >= 0 && (mostRecentTriggerCharPos === 0 || !requireLeadingSpace || /[\xA0\s]/g.test(effectiveRange.substring(mostRecentTriggerCharPos - 1, mostRecentTriggerCharPos)))) {
3491+                    var currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + 1, effectiveRange.length);
3492+
3493+                    triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + 1);
3494+                    var firstSnippetChar = currentTriggerSnippet.substring(0, 1);
3495+                    var leadingSpace = currentTriggerSnippet.length > 0 && (firstSnippetChar === ' ' || firstSnippetChar === '\xA0');
3496+                    if (hasTrailingSpace) {
3497+                        currentTriggerSnippet = currentTriggerSnippet.trim();
3498+                    }
3499+
3500+                    var regex = allowSpaces ? /[^\S ]/g : /[\xA0\s]/g;
3501+
3502+                    this.tribute.hasTrailingSpace = regex.test(currentTriggerSnippet);
3503+
3504+                    if (!leadingSpace && (menuAlreadyActive || !regex.test(currentTriggerSnippet))) {
3505+                        return {
3506+                            mentionPosition: mostRecentTriggerCharPos,
3507+                            mentionText: currentTriggerSnippet,
3508+                            mentionSelectedElement: selected,
3509+                            mentionSelectedPath: path,
3510+                            mentionSelectedOffset: offset,
3511+                            mentionTriggerChar: triggerChar
3512+                        };
3513+                    }
3514+                }
3515+            }
3516+        }
3517+    }, {
3518+        key: 'lastIndexWithLeadingSpace',
3519+        value: function lastIndexWithLeadingSpace(str, char) {
3520+            var reversedStr = str.split('').reverse().join('');
3521+            var index = -1;
3522+
3523+            for (var cidx = 0, len = str.length; cidx < len; cidx++) {
3524+                var firstChar = cidx === str.length - 1;
3525+                var leadingSpace = /\s/.test(reversedStr[cidx + 1]);
3526+                var match = char === reversedStr[cidx];
3527+
3528+                if (match && (firstChar || leadingSpace)) {
3529+                    index = str.length - 1 - cidx;
3530+                    break;
3531+                }
3532+            }
3533+
3534+            return index;
3535+        }
3536+    }, {
3537+        key: 'isContentEditable',
3538+        value: function isContentEditable(element) {
3539+            return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA';
3540+        }
3541+    }, {
3542+        key: 'isMenuOffScreen',
3543+        value: function isMenuOffScreen(coordinates, menuDimensions) {
3544+            var windowWidth = window.innerWidth;
3545+            var windowHeight = window.innerHeight;
3546+            var doc = document.documentElement;
3547+            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
3548+            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
3549+
3550+            var menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height;
3551+            var menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width;
3552+            var menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height;
3553+            var menuLeft = typeof coordinates.left === 'number' ? coordinates.left : windowLeft + windowWidth - coordinates.right - menuDimensions.width;
3554+
3555+            return {
3556+                top: menuTop < Math.floor(windowTop),
3557+                right: menuRight > Math.ceil(windowLeft + windowWidth),
3558+                bottom: menuBottom > Math.ceil(windowTop + windowHeight),
3559+                left: menuLeft < Math.floor(windowLeft)
3560+            };
3561+        }
3562+    }, {
3563+        key: 'getMenuDimensions',
3564+        value: function getMenuDimensions() {
3565+            // Width of the menu depends of its contents and position
3566+            // We must check what its width would be without any obstruction
3567+            // This way, we can achieve good positioning for flipping the menu
3568+            var dimensions = {
3569+                width: null,
3570+                height: null
3571+            };
3572+
3573+            this.tribute.menu.style.cssText = 'top: 0px;\n                                 left: 0px;\n                                 position: fixed;\n                                 zIndex: 10000;\n                                 display: block;\n                                 visibility; hidden;';
3574+            dimensions.width = this.tribute.menu.offsetWidth;
3575+            dimensions.height = this.tribute.menu.offsetHeight;
3576+
3577+            this.tribute.menu.style.cssText = 'display: none;';
3578+
3579+            return dimensions;
3580+        }
3581+    }, {
3582+        key: 'getTextAreaOrInputUnderlinePosition',
3583+        value: function getTextAreaOrInputUnderlinePosition(element, position, flipped) {
3584+            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'];
3585+
3586+            var isFirefox = window.mozInnerScreenX !== null;
3587+
3588+            var div = this.getDocument().createElement('div');
3589+            div.id = 'input-textarea-caret-position-mirror-div';
3590+            this.getDocument().body.appendChild(div);
3591+
3592+            var style = div.style;
3593+            var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle;
3594+
3595+            style.whiteSpace = 'pre-wrap';
3596+            if (element.nodeName !== 'INPUT') {
3597+                style.wordWrap = 'break-word';
3598+            }
3599+
3600+            // position off-screen
3601+            style.position = 'absolute';
3602+            style.visibility = 'hidden';
3603+
3604+            // transfer the element's properties to the div
3605+            properties.forEach(function (prop) {
3606+                style[prop] = computed[prop];
3607+            });
3608+
3609+            if (isFirefox) {
3610+                style.width = parseInt(computed.width) - 2 + 'px';
3611+                if (element.scrollHeight > parseInt(computed.height)) style.overflowY = 'scroll';
3612+            } else {
3613+                style.overflow = 'hidden';
3614+            }
3615+
3616+            div.textContent = element.value.substring(0, position);
3617+
3618+            if (element.nodeName === 'INPUT') {
3619+                div.textContent = div.textContent.replace(/\s/g, ' ');
3620+            }
3621+
3622+            var span = this.getDocument().createElement('span');
3623+            span.textContent = element.value.substring(position) || '.';
3624+            div.appendChild(span);
3625+
3626+            var rect = element.getBoundingClientRect();
3627+            var doc = document.documentElement;
3628+            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
3629+            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
3630+
3631+            var coordinates = {
3632+                top: rect.top + windowTop + span.offsetTop + parseInt(computed.borderTopWidth) + parseInt(computed.fontSize) - element.scrollTop,
3633+                left: rect.left + windowLeft + span.offsetLeft + parseInt(computed.borderLeftWidth)
3634+            };
3635+
3636+            var windowWidth = window.innerWidth;
3637+            var windowHeight = window.innerHeight;
3638+
3639+            var menuDimensions = this.getMenuDimensions();
3640+            var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
3641+
3642+            if (menuIsOffScreen.right) {
3643+                coordinates.right = windowWidth - coordinates.left;
3644+                coordinates.left = 'auto';
3645+            }
3646+
3647+            var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
3648+
3649+            if (menuIsOffScreen.bottom) {
3650+                var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
3651+                var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
3652+
3653+                coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top - span.offsetTop);
3654+                coordinates.top = 'auto';
3655+            }
3656+
3657+            menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
3658+            if (menuIsOffScreen.left) {
3659+                coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
3660+                delete coordinates.right;
3661+            }
3662+            if (menuIsOffScreen.top) {
3663+                coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
3664+                delete coordinates.bottom;
3665+            }
3666+
3667+            this.getDocument().body.removeChild(div);
3668+            return coordinates;
3669+        }
3670+    }, {
3671+        key: 'getContentEditableCaretPosition',
3672+        value: function getContentEditableCaretPosition(selectedNodePosition) {
3673+            var markerTextChar = '';
3674+            var markerEl = void 0,
3675+                markerId = 'sel_' + new Date().getTime() + '_' + Math.random().toString().substr(2);
3676+            var range = void 0;
3677+            var sel = this.getWindowSelection();
3678+            var prevRange = sel.getRangeAt(0);
3679+
3680+            range = this.getDocument().createRange();
3681+            range.setStart(sel.anchorNode, selectedNodePosition);
3682+            range.setEnd(sel.anchorNode, selectedNodePosition);
3683+
3684+            range.collapse(false);
3685+
3686+            // Create the marker element containing a single invisible character using DOM methods and insert it
3687+            markerEl = this.getDocument().createElement('span');
3688+            markerEl.id = markerId;
3689+
3690+            markerEl.appendChild(this.getDocument().createTextNode(markerTextChar));
3691+            range.insertNode(markerEl);
3692+            sel.removeAllRanges();
3693+            sel.addRange(prevRange);
3694+
3695+            var rect = markerEl.getBoundingClientRect();
3696+            var doc = document.documentElement;
3697+            var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
3698+            var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
3699+            var coordinates = {
3700+                left: rect.left + windowLeft,
3701+                top: rect.top + markerEl.offsetHeight + windowTop
3702+            };
3703+            var windowWidth = window.innerWidth;
3704+            var windowHeight = window.innerHeight;
3705+
3706+            var menuDimensions = this.getMenuDimensions();
3707+            var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
3708+
3709+            if (menuIsOffScreen.right) {
3710+                coordinates.left = 'auto';
3711+                coordinates.right = windowWidth - rect.left - windowLeft;
3712+            }
3713+
3714+            var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
3715+
3716+            if (menuIsOffScreen.bottom) {
3717+                var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
3718+                var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
3719+
3720+                coordinates.top = 'auto';
3721+                coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top);
3722+            }
3723+
3724+            menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
3725+            if (menuIsOffScreen.left) {
3726+                coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
3727+                delete coordinates.right;
3728+            }
3729+            if (menuIsOffScreen.top) {
3730+                coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
3731+                delete coordinates.bottom;
3732+            }
3733+
3734+            markerEl.parentNode.removeChild(markerEl);
3735+            return coordinates;
3736+        }
3737+    }, {
3738+        key: 'scrollIntoView',
3739+        value: function scrollIntoView(elem) {
3740+            var reasonableBuffer = 20,
3741+                clientRect = void 0;
3742+            var maxScrollDisplacement = 100;
3743+            var e = this.menu;
3744+
3745+            if (typeof e === 'undefined') return;
3746+
3747+            while (clientRect === undefined || clientRect.height === 0) {
3748+                clientRect = e.getBoundingClientRect();
3749+
3750+                if (clientRect.height === 0) {
3751+                    e = e.childNodes[0];
3752+                    if (e === undefined || !e.getBoundingClientRect) {
3753+                        return;
3754+                    }
3755+                }
3756+            }
3757+
3758+            var elemTop = clientRect.top;
3759+            var elemBottom = elemTop + clientRect.height;
3760+
3761+            if (elemTop < 0) {
3762+                window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer);
3763+            } else if (elemBottom > window.innerHeight) {
3764+                var maxY = window.pageYOffset + clientRect.top - reasonableBuffer;
3765+
3766+                if (maxY - window.pageYOffset > maxScrollDisplacement) {
3767+                    maxY = window.pageYOffset + maxScrollDisplacement;
3768+                }
3769+
3770+                var targetY = window.pageYOffset - (window.innerHeight - elemBottom);
3771+
3772+                if (targetY > maxY) {
3773+                    targetY = maxY;
3774+                }
3775+
3776+                window.scrollTo(0, targetY);
3777+            }
3778+        }
3779+    }]);
3780+
3781+    return TributeRange;
3782+}();
3783+
3784+exports.default = TributeRange;
3785+module.exports = exports['default'];
3786+
3787+},{}],5:[function(require,module,exports){
3788+'use strict';
3789+
3790+Object.defineProperty(exports, "__esModule", {
3791+    value: true
3792+});
3793+
3794+var _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; }; }();
3795+
3796+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3797+
3798+// Thanks to https://github.com/mattyork/fuzzy
3799+var TributeSearch = function () {
3800+    function TributeSearch(tribute) {
3801+        _classCallCheck(this, TributeSearch);
3802+
3803+        this.tribute = tribute;
3804+        this.tribute.search = this;
3805+    }
3806+
3807+    _createClass(TributeSearch, [{
3808+        key: 'simpleFilter',
3809+        value: function simpleFilter(pattern, array) {
3810+            var _this = this;
3811+
3812+            return array.filter(function (string) {
3813+                return _this.test(pattern, string);
3814+            });
3815+        }
3816+    }, {
3817+        key: 'test',
3818+        value: function test(pattern, string) {
3819+            return this.match(pattern, string) !== null;
3820+        }
3821+    }, {
3822+        key: 'match',
3823+        value: function match(pattern, string, opts) {
3824+            opts = opts || {};
3825+            var patternIdx = 0,
3826+                result = [],
3827+                len = string.length,
3828+                totalScore = 0,
3829+                currScore = 0,
3830+                pre = opts.pre || '',
3831+                post = opts.post || '',
3832+                compareString = opts.caseSensitive && string || string.toLowerCase(),
3833+                ch = void 0,
3834+                compareChar = void 0;
3835+
3836+            pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
3837+
3838+            var patternCache = this.traverse(compareString, pattern, 0, 0, []);
3839+            if (!patternCache) {
3840+                return null;
3841+            }
3842+
3843+            return {
3844+                rendered: this.render(string, patternCache.cache, pre, post),
3845+                score: patternCache.score
3846+            };
3847+        }
3848+    }, {
3849+        key: 'traverse',
3850+        value: function traverse(string, pattern, stringIndex, patternIndex, patternCache) {
3851+            // if the pattern search at end
3852+            if (pattern.length === patternIndex) {
3853+
3854+                // calculate score and copy the cache containing the indices where it's found
3855+                return {
3856+                    score: this.calculateScore(patternCache),
3857+                    cache: patternCache.slice()
3858+                };
3859+            }
3860+
3861+            // if string at end or remaining pattern > remaining string
3862+            if (string.length === stringIndex || pattern.length - patternIndex > string.length - stringIndex) {
3863+                return undefined;
3864+            }
3865+
3866+            var c = pattern[patternIndex];
3867+            var index = string.indexOf(c, stringIndex);
3868+            var best = void 0,
3869+                temp = void 0;
3870+
3871+            while (index > -1) {
3872+                patternCache.push(index);
3873+                temp = this.traverse(string, pattern, index + 1, patternIndex + 1, patternCache);
3874+                patternCache.pop();
3875+
3876+                // if downstream traversal failed, return best answer so far
3877+                if (!temp) {
3878+                    return best;
3879+                }
3880+
3881+                if (!best || best.score < temp.score) {
3882+                    best = temp;
3883+                }
3884+
3885+                index = string.indexOf(c, index + 1);
3886+            }
3887+
3888+            return best;
3889+        }
3890+    }, {
3891+        key: 'calculateScore',
3892+        value: function calculateScore(patternCache) {
3893+            var score = 0;
3894+            var temp = 1;
3895+
3896+            patternCache.forEach(function (index, i) {
3897+                if (i > 0) {
3898+                    if (patternCache[i - 1] + 1 === index) {
3899+                        temp += temp + 1;
3900+                    } else {
3901+                        temp = 1;
3902+                    }
3903+                }
3904+
3905+                score += temp;
3906+            });
3907+
3908+            return score;
3909+        }
3910+    }, {
3911+        key: 'render',
3912+        value: function render(string, indices, pre, post) {
3913+            var rendered = string.substring(0, indices[0]);
3914+
3915+            indices.forEach(function (index, i) {
3916+                rendered += pre + string[index] + post + string.substring(index + 1, indices[i + 1] ? indices[i + 1] : string.length);
3917+            });
3918+
3919+            return rendered;
3920+        }
3921+    }, {
3922+        key: 'filter',
3923+        value: function filter(pattern, arr, opts) {
3924+            var _this2 = this;
3925+
3926+            opts = opts || {};
3927+            return arr.reduce(function (prev, element, idx, arr) {
3928+                var str = element;
3929+
3930+                if (opts.extract) {
3931+                    str = opts.extract(element);
3932+
3933+                    if (!str) {
3934+                        // take care of undefineds / nulls / etc.
3935+                        str = '';
3936+                    }
3937+                }
3938+
3939+                var rendered = _this2.match(pattern, str, opts);
3940+
3941+                if (rendered != null) {
3942+                    prev[prev.length] = {
3943+                        string: rendered.rendered,
3944+                        score: rendered.score,
3945+                        index: idx,
3946+                        original: element
3947+                    };
3948+                }
3949+
3950+                return prev;
3951+            }, []).sort(function (a, b) {
3952+                var compare = b.score - a.score;
3953+                if (compare) return compare;
3954+                return a.index - b.index;
3955+            });
3956+        }
3957+    }]);
3958+
3959+    return TributeSearch;
3960+}();
3961+
3962+exports.default = TributeSearch;
3963+module.exports = exports['default'];
3964+
3965+},{}],6:[function(require,module,exports){
3966+"use strict";
3967+
3968+Object.defineProperty(exports, "__esModule", {
3969+  value: true
3970+});
3971+
3972+var _Tribute = require("./Tribute");
3973+
3974+var _Tribute2 = _interopRequireDefault(_Tribute);
3975+
3976+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3977+
3978+exports.default = _Tribute2.default; /**
3979+                                     * Tribute.js
3980+                                     * Native ES6 JavaScript @mention Plugin
3981+                                     **/
3982+
3983+module.exports = exports["default"];
3984+
3985+},{"./Tribute":1}],7:[function(require,module,exports){
3986+'use strict';
3987+
3988+if (!Array.prototype.find) {
3989+    Array.prototype.find = function (predicate) {
3990+        if (this === null) {
3991+            throw new TypeError('Array.prototype.find called on null or undefined');
3992+        }
3993+        if (typeof predicate !== 'function') {
3994+            throw new TypeError('predicate must be a function');
3995+        }
3996+        var list = Object(this);
3997+        var length = list.length >>> 0;
3998+        var thisArg = arguments[1];
3999+        var value;
4000+
4001+        for (var i = 0; i < length; i++) {
4002+            value = list[i];
4003+            if (predicate.call(thisArg, value, i, list)) {
4004+                return value;
4005+            }
4006+        }
4007+        return undefined;
4008+    };
4009+}
4010+
4011+if (window && typeof window.CustomEvent !== "function") {
4012+    var CustomEvent = function CustomEvent(event, params) {
4013+        params = params || {
4014+            bubbles: false,
4015+            cancelable: false,
4016+            detail: undefined
4017+        };
4018+        var evt = document.createEvent('CustomEvent');
4019+        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
4020+        return evt;
4021+    };
4022+
4023+    if (typeof window.Event !== 'undefined') {
4024+        CustomEvent.prototype = window.Event.prototype;
4025+    }
4026+
4027+    window.CustomEvent = CustomEvent;
4028+}
4029+
4030+},{}]},{},[6])(6)
4031+});
4032+
4033+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvVHJpYnV0ZS5qcyIsInNyYy9UcmlidXRlRXZlbnRzLmpzIiwic3JjL1RyaWJ1dGVNZW51RXZlbnRzLmpzIiwic3JjL1RyaWJ1dGVSYW5nZS5qcyIsInNyYy9UcmlidXRlU2VhcmNoLmpzIiwic3JjL2luZGV4LmpzIiwic3JjL3V0aWxzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7QUNBQTs7OztBQUNBOzs7O0FBQ0E7Ozs7QUFDQTs7OztBQUNBOzs7Ozs7OztJQUVNLE87QUFDRiwyQkFpQkc7QUFBQTs7QUFBQSwrQkFoQkMsTUFnQkQ7QUFBQSxZQWhCQyxNQWdCRCwrQkFoQlUsSUFnQlY7QUFBQSwrQkFmQyxNQWVEO0FBQUEsWUFmQyxNQWVELCtCQWZVLElBZVY7QUFBQSxvQ0FkQyxXQWNEO0FBQUEsWUFkQyxXQWNELG9DQWRlLFdBY2Y7QUFBQSxnQ0FiQyxPQWFEO0FBQUEsWUFiQyxPQWFELGdDQWJXLEdBYVg7QUFBQSx1Q0FaQyxjQVlEO0FBQUEsWUFaQyxjQVlELHVDQVprQixJQVlsQjtBQUFBLHlDQVhDLGdCQVdEO0FBQUEsWUFYQyxnQkFXRCx5Q0FYb0IsSUFXcEI7QUFBQSwrQkFWQyxNQVVEO0FBQUEsWUFWQyxNQVVELCtCQVZVLEtBVVY7QUFBQSxpQ0FUQyxRQVNEO0FBQUEsWUFUQyxRQVNELGlDQVRZLE9BU1o7QUFBQSxtQ0FSQyxVQVFEO0FBQUEsWUFSQyxVQVFELG1DQVJjLElBUWQ7QUFBQSxzQ0FQQyxhQU9EO0FBQUEsWUFQQyxhQU9ELHNDQVBpQixJQU9qQjtBQUFBLHdDQU5DLGVBTUQ7QUFBQSxZQU5DLGVBTUQsd0NBTm1CLElBTW5CO0FBQUEseUNBTEMsbUJBS0Q7QUFBQSxZQUxDLG1CQUtELHlDQUx1QixJQUt2QjtBQUFBLG9DQUpDLFdBSUQ7QUFBQSxZQUpDLFdBSUQsb0NBSmUsS0FJZjtBQUFBLHlDQUhDLGlCQUdEO0FBQUEs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VSxPQUFPLE9BQU8sV0FBZCxLQUE4QixVQUE1QyxFQUF3RDtBQUFBLFFBQzdDLFdBRDZDLEdBQ3RELFNBQVMsV0FBVCxDQUFxQixLQUFyQixFQUE0QixNQUE1QixFQUFvQztBQUNsQyxpQkFBUyxVQUFVO0FBQ2pCLHFCQUFTLEtBRFE7QUFFakIsd0JBQVksS0FGSztBQUdqQixvQkFBUTtBQUhTLFNBQW5CO0FBS0EsWUFBSSxNQUFNLFNBQVMsV0FBVCxDQUFxQixhQUFyQixDQUFWO0FBQ0EsWUFBSSxlQUFKLENBQW9CLEtBQXBCLEVBQTJCLE9BQU8sT0FBbEMsRUFBMkMsT0FBTyxVQUFsRCxFQUE4RCxPQUFPLE1BQXJFO0FBQ0EsZUFBTyxHQUFQO0FBQ0QsS0FWcUQ7O0FBWXZELFFBQUksT0FBTyxPQUFPLEtBQWQsS0FBd0IsV0FBNUIsRUFBeUM7QUFDdkMsb0JBQVksU0FBWixHQUF3QixPQUFPLEtBQVAsQ0FBYSxTQUFyQztBQUNEOztBQUVBLFdBQU8sV0FBUCxHQUFxQixXQUFyQjtBQUNEIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiByKGUsbix0KXtmdW5jdGlvbiBvKGksZil7aWYoIW5baV0pe2lmKCFlW2ldKXt2YXIgYz1cImZ1bmN0aW9uXCI9PXR5cGVvZiByZXF1aXJlJiZyZXF1aXJlO2lmKCFmJiZjKXJldHVybiBjKGksITApO2lmKHUpcmV0dXJuIHUoaSwhMCk7dmFyIGE9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitpK1wiJ1wiKTt0aHJvdyBhLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsYX12YXIgcD1uW2ldPXtleHBvcnRzOnt9fTtlW2ldWzBdLmNhbGwocC5leHBvcnRzLGZ1bmN0aW9uKHIpe3ZhciBuPWVbaV1bMV1bcl07cmV0dXJuIG8obnx8cil9LHAscC5leHBvcnRzLHIsZSxuLHQpfXJldHVybiBuW2ldLmV4cG9ydHN9Zm9yKHZhciB1PVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmUsaT0wO2k8dC5sZW5ndGg7aSsrKW8odFtpXSk7cmV0dXJuIG99cmV0dXJuIHJ9KSgpIiwiaW1wb3J0IFRyaWJ1dGVVdGlscyBmcm9tIFwiLi91dGlsc1wiO1xuaW1wb3J0IFRyaWJ1dGVFdmVudHMgZnJvbSBcIi4vVHJpYnV0ZUV2ZW50c1wiO1xuaW1wb3J0IFRyaWJ1dGVNZW51RXZlbnRzIGZyb20gXCIuL1RyaWJ1dGVNZW51RXZlbnRzXCI7XG5pbXBvcnQgVHJpYnV0ZVJhbmdlIGZyb20gXCIuL1RyaWJ1dGVSYW5nZVwiO1xuaW1wb3J0IFRyaWJ1dGVTZWFyY2ggZnJvbSBcIi4vVHJpYnV0ZVNlYXJjaFwiO1xuXG5jbGFzcyBUcmlidXRlIHtcbiAgICBjb25zdHJ1Y3Rvcih7XG4gICAgICAgIHZhbHVlcyA9IG51bGwsXG4gICAgICAgIGlmcmFtZSA9IG51bGwsXG4gICAgICAgIHNlbGVjdENsYXNzID0gJ2hpZ2hsaWdodCcsXG4gICAgICAgIHRyaWdnZXIgPSAnQCcsXG4gICAgICAgIHNlbGVjdFRlbXBsYXRlID0gbnVsbCxcbiAgICAgICAgbWVudUl0ZW1UZW1wbGF0ZSA9IG51bGwsXG4gICAgICAgIGxvb2t1cCA9ICdrZXknLFxuICAgICAgICBmaWxsQXR0ciA9ICd2YWx1ZScsXG4gICAgICAgIGNvbGxlY3Rpb24gPSBudWxsLFxuICAgICAgICBtZW51Q29udGFpbmVyID0gbnVsbCxcbiAgICAgICAgbm9NYXRjaFRlbXBsYXRlID0gbnVsbCxcbiAgICAgICAgcmVxdWlyZUxlYWRpbmdTcGFjZSA9IHRydWUsXG4gICAgICAgIGFsbG93U3BhY2VzID0gZmFsc2UsXG4gICAgICAgIHJlcGxhY2VUZXh0U3VmZml4ID0gbnVsbCxcbiAgICAgICAgcG9zaXRpb25NZW51ID0gdHJ1ZSxcbiAgICAgICAgc3BhY2VTZWxlY3RzTWF0Y2ggPSBmYWxzZSxcbiAgICB9KSB7XG5cbiAgICAgICAgdGhpcy5tZW51U2VsZWN0ZWQgPSAwXG4gICAgICAgIHRoaXMuY3VycmVudCA9IHt9XG4gICAgICAgIHRoaXMuaW5wdXRFdmVudCA9IGZhbHNlXG4gICAgICAgIHRoaXMuaXNBY3RpdmUgPSBmYWxzZVxuICAgICAgICB0aGlzLm1lbnVDb250YWluZXIgPSBtZW51Q29udGFpbmVyXG4gICAgICAgIHRoaXMuYWxsb3dTcGFjZXMgPSBhbGxvd1NwYWNlc1xuICAgICAgICB0aGlzLnJlcGxhY2VUZXh0U3VmZml4ID0gcmVwbGFjZVRleHRTdWZmaXhcbiAgICAgICAgdGhpcy5wb3NpdGlvbk1lbnUgPSBwb3NpdGlvbk1lbnVcbiAgICAgICAgdGhpcy5oYXNUcmFpbGluZ1NwYWNlID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3BhY2VTZWxlY3RzTWF0Y2ggPSBzcGFjZVNlbGVjdHNNYXRjaDtcblxuICAgICAgICBpZiAodmFsdWVzKSB7XG4gICAgICAgICAgICB0aGlzLmNvbGxlY3Rpb24gPSBbe1xuICAgICAgICAgICAgICAgIC8vIHN5bWJvbCB0aGF0IHN0YXJ0cyB0aGUgbG9va3VwXG4gICAgICAgICAgICAgICAgdHJpZ2dlcjogdHJpZ2dlcixcblxuICAgICAgICAgICAgICAgIC8vIGlzIGl0IHdyYXBwZWQgaW4gYW4gaWZyYW1lXG4gICAgICAgICAgICAgICAgaWZyYW1lOiBpZnJhbWUsXG5cbiAgICAgICAgICAgICAgICAvLyBjbGFzcyBhcHBsaWVkIHRvIHNlbGVjdGVkIGl0ZW1cbiAgICAgICAgICAgICAgICBzZWxlY3RDbGFzczogc2VsZWN0Q2xhc3MsXG5cbiAgICAgICAgICAgICAgICAvLyBmdW5jdGlvbiBjYWxsZWQgb24gc2VsZWN0IHRoYXQgcmV0dW5zIHRoZSBjb250ZW50IHRvIGluc2VydFxuICAgICAgICAgICAgICAgIHNlbGVjdFRlbXBsYXRlOiAoc2VsZWN0VGVtcGxhdGUgfHwgVHJpYnV0ZS5kZWZhdWx0U2VsZWN0VGVtcGxhdGUpLmJpbmQodGhpcyksXG5cbiAgICAgICAgICAgICAgICAvLyBmdW5jdGlvbiBjYWxsZWQgdGhhdCByZXR1cm5zIGNvbnRlbnQgZm9yIGFuIGl0ZW1cbiAgICAgICAgICAgICAgICBtZW51SXRlbVRlbXBsYXRlOiAobWVudUl0ZW1UZW1wbGF0ZSB8fCBUcmlidXRlLmRlZmF1bHRNZW51SXRlbVRlbXBsYXRlKS5iaW5kKHRoaXMpLFxuXG4gICAgICAgICAgICAgICAgLy8gZnVuY3Rpb24gY2FsbGVkIHdoZW4gbWVudSBpcyBlbXB0eSwgZGlzYWJsZXMgaGlkaW5nIG9mIG1lbnUuXG4gICAgICAgICAgICAgICAgbm9NYXRjaFRlbXBsYXRlOiAodCA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHQuYmluZCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vTWF0Y2hUZW1wbGF0ZVxuICAgICAgICAgICAgICAgIH0pKG5vTWF0Y2hUZW1wbGF0ZSksXG5cbiAgICAgICAgICAgICAgICAvLyBjb2x1bW4gdG8gc2VhcmNoIGFnYWluc3QgaW4gdGhlIG9iamVjdFxuICAgICAgICAgICAgICAgIGxvb2t1cDogbG9va3VwLFxuXG4gICAgICAgICAgICAgICAgLy8gY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIGNvbnRlbnQgdG8gaW5zZXJ0IGJ5IGRlZmF1bHRcbiAgICAgICAgICAgICAgICBmaWxsQXR0cjogZmlsbEF0dHIsXG5cbiAgICAgICAgICAgICAgICAvLyBhcnJheSBvZiBvYmplY3RzIG9yIGEgZnVuY3Rpb24gcmV0dXJuaW5nIGFuIGFycmF5IG9mIG9iamVjdHNcbiAgICAgICAgICAgICAgICB2YWx1ZXM6IHZhbHVlcyxcblxuICAgICAgICAgICAgICAgIHJlcXVpcmVMZWFkaW5nU3BhY2U6IHJlcXVpcmVMZWFkaW5nU3BhY2VcbiAgICAgICAgICAgIH1dXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoY29sbGVjdGlvbikge1xuICAgICAgICAgICAgdGhpcy5jb2xsZWN0aW9uID0gY29sbGVjdGlvbi5tYXAoaXRlbSA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgdHJpZ2dlcjogaXRlbS50cmlnZ2VyIHx8IHRyaWdnZXIsXG4gICAgICAgICAgICAgICAgICAgIGlmcmFtZTogaXRlbS5pZnJhbWUgfHwgaWZyYW1lLFxuICAgICAgICAgICAgICAgICAgICBzZWxlY3RDbGFzczogaXRlbS5zZWxlY3RDbGFzcyB8fCBzZWxlY3RDbGFzcyxcbiAgICAgICAgICAgICAgICAgICAgc2VsZWN0VGVtcGxhdGU6IChpdGVtLnNlbGVjdFRlbXBsYXRlIHx8IFRyaWJ1dGUuZGVmYXVsdFNlbGVjdFRlbXBsYXRlKS5iaW5kKHRoaXMpLFxuICAgICAgICAgICAgICAgICAgICBtZW51SXRlbVRlbXBsYXRlOiAoaXRlbS5tZW51SXRlbVRlbXBsYXRlIHx8IFRyaWJ1dGUuZGVmYXVsdE1lbnVJdGVtVGVtcGxhdGUpLmJpbmQodGhpcyksXG4gICAgICAgICAgICAgICAgICAgIC8vIGZ1bmN0aW9uIGNhbGxlZCB3aGVuIG1lbnUgaXMgZW1wdHksIGRpc2FibGVzIGhpZGluZyBvZiBtZW51LlxuICAgICAgICAgICAgICAgICAgICBub01hdGNoVGVtcGxhdGU6ICh0ID0+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=
4034diff --git src/bp-friends/bp-friends-functions.php src/bp-friends/bp-friends-functions.php
4035index 051c65018..cf4ae4c0f 100644
4036--- src/bp-friends/bp-friends-functions.php
4037+++ src/bp-friends/bp-friends-functions.php
4038@@ -804,12 +804,14 @@ function bp_friends_prime_mentions_results() {
4039                $result        = new stdClass();
4040                $result->ID    = $user->user_nicename;
4041                $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) );
4042+               $result->user_id = $user->ID;
4043 
4044                if ( ! empty( $user->display_name ) && ! bp_disable_profile_sync() ) {
4045                        $result->name = $user->display_name;
4046                } else {
4047                        $result->name = bp_core_get_user_displayname( $user->ID );
4048                }
4049+               $result->search = "{$result->ID} {$result->name}";
4050 
4051                $results[] = $result;
4052        }