PageSourceSearch

https://upplication.com/scripts/main.js

js upplication.com collected 2026-09-24 09:37:43 UTC 88,554 bytes, 1,017 lines download raw bytes

1/**
2 * provide the functions to show or hide
3 * the cookies warning.
4 *
5 * must exists a html with an id: 'cookies' and must
6 * exist a close button with id: 'cookies-accept'
7 * to accept the cookies policies and close the message.
8 *
9 * depends on scripts/utils.js
10 */
11$(function() {
12    'use strict';
13    // Display cookies announce
14    if (!getCookie("showed-cookies")) {
15        $("#cookies").addClass("show");
16    }
17
18    $("#cookies-accept").click(function() {
19        setCookie("showed-cookies", "true");
20        $("#cookies").removeClass('show');
21    });
22});
23jQuery(document).ready(function($) {
24    /**
25     * ISO 4217 currency code. This means this should be a three characters
26     * string, all uppercase, representing the currency.
27     *
28     * @typedef {String} CurrencyCode
29     * @see {@link http://www.xe.com/symbols.php}
30     */
31
32    /**
33     * @typedef {Object} CurrencyRates
34     * @property {CurrencyCode} base
35     * @property {String} date - Date when this rates apply, in format
36     *         `yyyy-MM-dd`.
37     * @property {Object} rates - Currency conversion rates. Every property
38     *         on this object is a be {@link CurrencyCode} and it's value
39     *         is the conversion rate to be applied relative to current
40     *         object {@link #base}
41     */
42
43     /**
44      * Default base currency
45      * @type {CurrencyCore}
46      */
47    var CURRENCY_BASE = 'EUR'
48    /**
49     * Other currencies that are not the base and should be handled by this
50     * script.
51     * @type {CurrencyCode[]}
52     */
53    var CURRENCY_SYMBOLS = [ 'USD', 'MXN' ]
54
55    /**
56     * Provides currency rates information via an async HTTP API.
57     * @param {CurrencyCode} [conf.base] - ISO Symbol of the currency base for
58     *        rate calculation. All rates for other currencies will be relative
59     *        to this currency. If not provided, defaults to `'USD'`.
60     * @param {CurrencyCode[]} [conf.symbols] - When requesting currency rates
61     *        via this instance, only rates for this currencies should be
62     *        included on the result. If not provided all available rates will
63     *        be returned when rates are requested.
64     * @see {@link http://fixer.io/}
65     */
66    function CurrencyService(conf) {
67        this.conf = conf || {}
68    }
69
70    /**
71     * Returns via callback the currency conversion rates based on the conf
72     * provided on service constructon.
73     *
74     * @param  {Function} cb
75     * @return {CurrencyRates}
76     * @see {@link CurrencyService}
77     */
78    CurrencyService.prototype.getRates = function(cb) {
79        var conf = $.extend({}, this.conf)
80
81        var url = '//api.exchangeratesapi.io/latest'
82        var params = []
83        if (conf.symbols)
84            params.push('base=' + conf.base)
85        if (conf.symbols.length > 0)
86            params.push('symbols=' + conf.symbols.join(','))
87        url += '?' + params.join('&')
88        $.getJSON(url, cb)
89    }
90
91    /**
92     * Wraps a HTMLElement and provides functionality over its content related
93     * with currencies values. Changes on properties via getters and setters
94     * will be retrieved, stored and reflected directly on the HTMLElement.
95     *
96     * If a base PriceContainer is provided the actual price on the current
97     * instance is expected to be related with the base price, but it is not
98     * necessary available right after construction and probably will be needed
99     * to provide it via {@link #setPrice}. See {@link #getPrice} for a more
100     * detailed explanation.
101     *
102     * This class works under lazy-init, this means that if no setter is called
103     * after construction, maybe some html element properties won't be reflecting
104     * an updated value.
105     *
106     * @param {jQuery} $elem
107     * @param {PriceContainer} [basePriceContainer]
108     */
109    function PriceContainer($elem, basePriceContainer) {
110        this.$element  = $elem
111        this.basePriceContainer = basePriceContainer || null
112        this.$price = $elem.find('.price')
113        this.setPrice(this.getPrice())
114        this.setCurrencyCode(this.getCurrencyCode() || CURRENCY_BASE)
115    }
116
117    /**
118     * Matches and allows extraction of the currency code for any currency
119     * className created via {@link PriceContainer.getCurrencyClassName}
120     * @type {RegExp}
121     */
122    PriceContainer.CURRENCY_CLASS_NAME_REGEX = /^price-currency-([a-z]{3})/i
123
124    /**
125     * Generates a className for the given currencyCode.
126     * @param  {CurrencyCode} currencyCode
127     * @return {String}
128     */
129    PriceContainer.buildCurrencyClassName = function(currencyCode) {
130        if (typeof currencyCode !== 'string')
131            throw new TypeError('currencyCode must be a string')
132        if (currencyCode.length !== 3)
133            throw new Error('currencyCode is expected to be 3 characters long')
134        return 'price-currency-' + currencyCode.toLowerCase()
135    }
136
137    /**
138     * ClassName that will be added to the PriceContainer bound HTMLElement when
139     * price for the said container is not yet available.
140     *
141     * @type {String}
142     */
143    PriceContainer.PRICE_NOT_YET_AVAILABLE = 'price-loading'
144
145    /**
146     * Returns the current price value on the container. Null value is returned
147     * only if this container was provided with a base {@link PriceContainer} on
148     * construction and price has not been updated via {@link #setPrice}
149     * afterwards.
150     *
151     * @return {Number|null}
152     */
153    PriceContainer.prototype.getPrice = function() {
154        var price = this.$price.data('price')
155        return Number(price) || null
156    }
157
158    /**
159     * Updates the current price on the container. If price is null, className
160     * provided by {@link .PRICE_NOT_YET_AVAILABLE} will be added to the current
161     * container bound element.
162     *
163     * @param {Number} price
164     */
165    PriceContainer.prototype.setPrice = function(price) {
166        var className = PriceContainer.PRICE_NOT_YET_AVAILABLE
167        this.$element.toggleClass(className, price === null)
168        if (price !== null) {
169            price = Number(price)
170            this.$price.data('price', price)
171            this.updatePriceView()
172        }
173    }
174
175    PriceContainer.prototype.updatePriceView = function() {
176        var price = this.$price.data('price')
177        var locale = $('html').attr('lang')
178        this.$price.text(price.toLocaleString(locale, {
179            minimumFractionDigits: 2, // Decimal positions
180            maximumFractionDigits: 2,
181            useGrouping: false, // Hundreds separator
182        }))
183    }
184
185    /**
186     * Retrieves all classNames from current bound HTMLElement that match
187     * {@link PriceContainer.CURRENCY_CLASS_NAME_REGEX}.
188     * @private
189     * @return {String[]}
190     */
191    PriceContainer.prototype.getCurrencyClassNames = function() {
192        var classNames  = this.$element.attr('class').split(/\s+/)
193        var currencyClassRgx = PriceContainer.CURRENCY_CLASS_NAME_REGEX
194        return classNames.filter(function(c) {
195            return currencyClassRgx.test(c)
196        })
197    }
198
199    /**
200     * Retrieves current currency of the container based on its classNames.
201     * If no className is found that provides currency will return null
202     *
203     * @return {CurrencyCode|null}
204     */
205    PriceContainer.prototype.getCurrencyCode = function() {
206        var currencyClassNames = this.getCurrencyClassNames()
207
208        if (currencyClassNames.length <= 0)
209            return null
210
211        currencyClassNames = currencyClassNames[0]
212        var currencyClassRgx = PriceContainer.CURRENCY_CLASS_NAME_REGEX
213        var currencySymbol = currencyClassRgx.exec(currencyClassNames)[1]
214        return currencySymbol.toUpperCase()
215    }
216
217    /**
218     * Sets the current currency code to the one provided. This adds a class
219     * generated via {@link .getCurrencyClassName} to the bound HTML element
220     *
221     * @param {CurrencyCode} code
222     */
223    PriceContainer.prototype.setCurrencyCode = function(code) {
224        var currencyClasses = this.getCurrencyClassNames()
225        this.$element.removeClass(function() {
226            return currencyClasses.join(' ')
227        })
228        this.$element.addClass(PriceContainer.buildCurrencyClassName(code))
229    }
230
231    /**
232     * If a base {@link PriceContainer} was provided on construction, this
233     * method returns the result of {@link #getPrice} on said base container.
234     *
235     * @return {Number|Null}
236     */
237    PriceContainer.prototype.getBasePrice = function () {
238        if (!this.basePriceContainer)
239            return null
240        return this.basePriceContainer.getPrice()
241    }
242
243    /**
244     * If a base {@link PriceContainer} was provided on construction, this
245     * method returns the result of {@link #getCurrencyCode} on said base
246     * container.
247     *
248     * @return {Number|Null}
249     */
250    PriceContainer.prototype.getBaseCurrencyCode = function () {
251        if (!this.basePriceContainer)
252            return null
253        return this.basePriceContainer.getCurrencyCode()
254    }
255
256    /**
257     * Tries to find a pricing table and at least one currency changer. If any
258     * of them is not found, the function ends.
259     *
260     * Adds a click listener to every currency changer that toggles a currency
261     * class on the pricing table based on the `data-currency` attribute on
262     * the currency changer.
263     */
264    function initializeCurrencySelectors() {
265        var $pricing = $('.pricing-tables')
266        var $currencyChangers = $('[data-currency]')
267
268        if ($pricing.length == 0 || $currencyChangers.length === 0)
269            return
270
271        var setSelectedCurrencyClassName = function(currencyCode) {
272            $pricing.addClass('selected-currency-' + currencyCode)
273        }
274
275        var removeSelectedCurrencyClassName = function() {
276            $pricing.removeClass(function(i, classNames) {
277                return classNames.split(/\s+/).filter(function(className) {
278                    return /^selected-currency-([a-z]{3})$/.test(className)
279                }).join(' ')
280            })
281        }
282
283        $currencyChangers.on('click', function(ev) {
284            var $target = $(ev.target)
285            var currencyCode = $target.data('currency').toLowerCase()
286            $currencyChangers.removeClass('active')
287            $target.addClass('active')
288            removeSelectedCurrencyClassName()
289            setSelectedCurrencyClassName(currencyCode)
290        })
291    }
292
293    /**
294     * Finds all `.price-container` on the document, wraps every one of them
295     * on a {@link PriceContainer} and generates the missing priceContainers
296     * based on CURRENCY_SYMBOLS (HTMLElement and {@link PriceContainer}
297     * instances).
298     *
299     * @return {PriceContainer[]} All instances generated on the process.
300     */
301    function initializePriceContainers() {
302        var priceContainers = $('.price-container').map(function(i, e) {
303            var $priceContainer = $(e)
304            var priceContainer = new PriceContainer($priceContainer)
305            var priceContainers = [ priceContainer ]
306            CURRENCY_SYMBOLS.reduce(function(insertAfter, s) {
307                var $priceContainerClone = $priceContainer.clone()
308                var priceContainerClone = new PriceContainer($priceContainerClone, priceContainer)
309                priceContainerClone.setCurrencyCode(s)
310                priceContainerClone.setPrice(null)
311                priceContainers.push(priceContainerClone)
312                return $priceContainerClone.insertAfter(insertAfter)
313            }, $priceContainer)
314            return priceContainers
315        }).get()
316        // Flatten the "array of arrays"
317        return [].concat.apply([], priceContainers)
318    }
319
320    /**
321     * Updates all the provided priceContainers prices with the given
322     * conversion rates. Due to how priceContainers work this will only affect
323     * {@link PriceContainer}-s that were provided with a base PriceContaiener,
324     * in our case that means every priceContainer generated after the page
325     * loaded (created on {@link initializePriceContainers})
326     *
327     * @param  {PriceContainer[]} priceContainers [description]
328     * @param  {CurrencyRates} rates
329     */
330    function updatePriceContainers(priceContainers, rates) {
331        $.extend(fx, rates) // Setup FX with the provided base and rates from the api
332        priceContainers
333        .filter(function(priceContainer) {
334            // Igonre price containers that have not a base price
335            return priceContainer.getBaseCurrencyCode() !== null
336        })
337        .forEach(function(priceContainer) {
338            var basePrice = priceContainer.getBasePrice()
339            var baseSymbol = priceContainer.getBaseCurrencyCode()
340            var currencySymbol = priceContainer.getCurrencyCode()
341            var rate = fx(basePrice).from(baseSymbol).to(currencySymbol)
342            var price = rate.toFixed(2)
343            priceContainer.setPrice(price)
344        })
345    }
346
347    /**
348     * Setups everything necessary and starts it
349     */
350    function initialize() {
351        var currencyService = new CurrencyService({
352            base: CURRENCY_BASE,
353            symbols: CURRENCY_SYMBOLS
354        })
355
356        var priceContainers = initializePriceContainers()
357        if (priceContainers.length <= 0) // If no price containers, do nothing
358            return
359        // Retrieve conversion rates and update price containers
360        currencyService.getRates(function(data) {
361            updatePriceContainers(priceContainers, data)
362        })
363        initializeCurrencySelectors()
364    }
365
366    initialize()
367})
368
369/**
370 * Save the lang when it is clicked to store as default lang for the user.
371 *
372 * depends on scripts/utils.js
373 */
374$(function () {
375    var setupLang = function(){
376        var $langElem = $("a.lang-link[data-lang]");
377        //Add dynamic translations
378        var lang = $langElem.data("lang");
379        //Change lang_cookie
380        $langElem.click(function () {
381            setCookie("upp_language", $(this).data("lang"));
382            log("change lang to => " + $(this).data("lang"));
383        });
384        //Display cookies announce
385        if (!getCookie("showed-cookies")) {
386            $("#cookies").addClass("show");
387        }
388    };
389
390    setupLang();
391});
392/**
393 * Register form setup with:
394 * - utm parameters
395 * - ajax register with error codes
396 * - the form must have a concrete structure:
397 *      form(name='register')
398 *          p(class='general-error') with general error code
399 *          input(name=appName|email|password)
400 *          p(class='register-appName-error|register-email-error|register-password-error') with concrete error for the input
401 *
402 *          submit(data-text="default text", data-loading="loading text", value=optional***)
403 *              span text (optional)***
404 * *** span text or value must exist to set the text of the submit button|input
405 *
406 * depends on scripts/utils.js
407 */
408$(function () {
409    'use strict';
410
411    var UTM_PARAMS = ["utm_source", "utm_campaign", "utm_medium", "utm_content"];
412
413    /**
414     * Adds the UTM parameters to the given form.
415     * @param {jQuery} $form Form where the params will be appended
416     */
417    var addUtmParamsToForm = function ($form) {
418        $.each(UTM_PARAMS, function (i, val) {
419            var utmVal = getCookie(val);
420            if (utmVal) {
421                var $utm = $("<input>")
422                    .attr("type", "hidden")
423                    .attr("name", val).val(utmVal);
424                $form.append($utm);
425            }
426        });
427    };
428
429    /**
430     * Adds the UTM parameters to the given url
431     * @param {String} url with utm params at login
432     */
433    var addUtmParamsToUrl = function (url) {
434        var params = "&";
435        if (url.indexOf("?") == -1) {
436            params = "?";
437        }
438
439        $.each(UTM_PARAMS, function (i, val) {
440            var utmVal = getCookie(val);
441            if (utmVal) {
442                params += val + "=" + utmVal + "&";
443            }
444        });
445
446        url += params.substring(0, params.length - 1);
447
448        return url;
449    };
450
451    var setup = function () {
452
453        var registerSuccessCallback = function (fn, submit) {
454            return function (data) {
455                if (data.success) {
456                    Topic.fire("register-success");
457					window.setTimeout(function(){
458						window.location = addUtmParamsToUrl(DASHBOARD_PATH + "/v2" + data.url);
459					}, 400);
460                } else {
461                    $(this).find("input").prop("disabled", false);
462
463                    submit.val(submit.data("text"));
464                    var $innerSpan = submit.find("span");
465                    if ($innerSpan.length > 0) {
466                        $innerSpan.text(submit.data("text"));
467                    }
468
469                    submit.prop("disabled", false);
470                    var error = {};
471
472                    switch (data.case) {
473                        case 2:
474                            //CREATING_APPREQUEST_ERROR
475                            error.appName = ajax.error[2];
476                            break;
477                        case 3:
478                            //DUPLICATE_EMAIL_ERROR
479                            error.email = ajax.error[3];
480                            break;
481                        case 4:
482                            //DUPLICATE_NAME_ERROR
483                            error.appName = ajax.error[4];
484                            break;
485                        case 5:
486                            //SELLER_ERROR
487                            error.seller = ajax.error[5];
488                            break;
489                        case 6:
490                            //DUPLICATE_NAME_AND_DUPLICATED_EMAIL_ERROR
491                            error.email = ajax.error[3];
492                            error.appName = ajax.error[4];
493                            break;
494                        case 7:
495                            //DUPLICATE_NAME_AND_SELLER_ERROR
496                            error.appName = ajax.error[4];
497                            error.seller = ajax.error[5];
498                            break;
499                        case 8:
500                            //DUPLICATE_EMAIL_AND_SELLER_ERROR
501                            error.email = ajax.error[3];
502                            error.seller = ajax.error[5];
503                            break;
504                        case 9:
505                            //DUPLICATE_NAME_AND_DUPLICATED_EMAIL_AND_SELLER_ERROR
506                            error.email = ajax.error[3];
507                            error.appName = ajax.error[4];
508                            error.seller = ajax.error[5];
509                            break;
510                        case 10:
511                            //SOME_PARAMETER_NULL_ERROR
512                            error.general = ajax.error[10];
513                            break;
514                        case 11:
515                            //NAME_INVALID_ERROR
516                            error.appName = ajax.error[11];
517                            break;
518                        case 12:
519                            //NAME_INVALID_AND_DUPLICATED_EMAIL_ERROR
520                            error.appName = ajax.error[11];
521                            error.email = ajax.error[3];
522                            break;
523                        case 13:
524                            //LICENSE_INVALID_ERROR
525                            error.license = ajax.error[13];
526                            break;
527                    }
528
529                    fn(this, error);
530                }
531            };
532        };
533
534        var registerErrorCallback = function (submit) {
535            return function (xhr) {
536                // end loading
537                $(this).find("input").prop("disabled", false);
538
539                submit.val(submit.data("text"));
540                var $innerSpan = submit.find("span");
541                if ($innerSpan.length > 0) {
542                    $innerSpan.text(submit.data("text"));
543                }
544
545                submit.prop("disabled", false);
546
547                $(this).find(".general-error").show();
548            };
549        };
550
551        //Register form through AJAX
552        //Validation done in HTML5 (patterns, minlength, etc)
553        $("form[name='register']").submit(function (e) {
554
555            e.preventDefault();
556            e.stopPropagation();
557
558            $(this).find(".general-error").hide();
559
560            var $form = $(this);
561            addUtmParamsToForm($form);
562
563            var url = DASHBOARD_PATH + "/v2/register";
564            var data = $form.serialize();
565            if (!data) return;
566
567            log("URL: " + url);
568            log("Data to send:" + data);
569            // loading
570            $(this).find("input").prop("disabled", true);
571            var submit = $(this).find("[type='submit']");
572
573            submit.val(submit.data("loading"));
574            var $innerSpan = submit.find("span");
575            if ($innerSpan.length > 0) {
576                $innerSpan.text(submit.data("loading"));
577            }
578
579            submit.prop("disabled", true);
580            $.ajax({
581                type: "GET", // jsonp only work with get
582                url: url,
583                data: data,
584                context: this,
585                dataType: "jsonp",
586                success: registerSuccessCallback(function (form, error) {
587                    var $emailError = $(form).find(".register-email-error"),
588                        $appNameError = $(form).find(".register-appName-error"),
589                        $sellerError = $(form).find(".register-seller-error"),
590                        $licenseError = $(form).find(".register-license-error");
591
592
593                    if (error.email) {
594                        $emailError.text(error.email);
595                        $emailError.show();
596                        $(form).find('input[name=email]').addClass("error");
597                    }
598
599                    if (error.appName) {
600                        $appNameError.text(error.appName);
601                        $appNameError.show();
602                        $(form).find('input[name=appName]').addClass("error");
603                    }
604
605                    if (error.seller) {
606                        $sellerError.text(error.seller);
607                        $sellerError.show();
608                        $(form).find('input[name=seller]').addClass("error");
609                    }
610
611                    if (error.license) {
612                        $licenseError.text(error.license);
613                        $licenseError.show();
614                        $(form).find('input[name=license]').addClass("error");
615                    }
616
617                    if (error.general) {
618                        $(form).find(".general-error").show();
619                    }
620
621                }, submit),
622                error: registerErrorCallback(submit)
623            });
624        });
625
626        //Hide errors on input fields when it is modified
627        $("[name='register'] input").on('keypress', function () {
628            $(this).siblings(".error").hide();
629            $(this).siblings().find(".error").hide();
630            $(this).removeClass("error");
631        });
632    };
633
634    /**
635     * Tracks the UTM tracking with cookies.
636     */
637    var setupUTMTracking = function () {
638        var params = window.location.search.replace('?', '').split('&');
639        for (var i = 0, len = params.length; i < len; i++) {
640            var paramParts = params[i].split('=');
641
642            if (UTM_PARAMS.indexOf(paramParts[0]) !== -1) {
643                setCookie(paramParts[0], paramParts[1]);
644            }
645        }
646    };
647
648    setup();
649    setupUTMTracking();
650
651    // configure modal register (if exists)
652    if ($.fn.modal)
653        $('.modal').modal({
654            opacity: .7,
655            dismissible: true,
656            in_duration: 300,
657            out_duration: 200,
658            starting_top: '4%',
659            ending_top: '10%'
660        });
661});
662/**
663 * Extend jquery with a scrollspy plugin.
664 * This watches the window scroll and fires events when elements are scrolled into viewport.
665 *
666 * throttle() and getTime() taken from Underscore.js
667 * https://github.com/jashkenas/underscore
668 *
669 * @author Copyright 2013 John Smart
670 * @license https://raw.github.com/thesmart/jquery-scrollspy/master/LICENSE
671 * @see https://github.com/thesmart
672 * @version 0.1.2
673 *
674 * JS ADDED TO EXTENDS THE SCROLLSPY PLUGIN WITH A NEW OFFSET TO FIX THE CALLING SECTION (A
674NCHOR) TO TOP OF THE WINDOW, NOT IN THE MIDDLE ONE.
675 */
676(function($) {
677
678	var jWindow = $(window);
679	var elements = [];
680	var elementsInView = [];
681	var isSpying = false;
682	var ticks = 0;
683	var unique_id = 1;
684	var offset = {
685		top : 0,
686		right : 0,
687		bottom : 0,
688		left : 0,
689	}
690
691	/**
692	 * Find elements that are within the boundary
693	 * @param {number} top
694	 * @param {number} right
695	 * @param {number} bottom
696	 * @param {number} left
697	 * @return {jQuery}		A collection of elements
698	 */
699	function findElements(top, right, bottom, left) {
700		var hits = $();
701		$.each(elements, function(i, element) {
702			if (element.height() > 0) {
703				var elTop = element.offset().top,
704					elLeft = element.offset().left,
705					elRight = elLeft + element.width(),
706					elBottom = elTop + element.height();
707
708				var isIntersect = !(elLeft > right ||
709					elRight < left ||
710					elTop > bottom ||
711					elBottom < top);
712
713				if (isIntersect) {
714					hits.push(element);
715				}
716			}
717		});
718
719		return hits;
720	}
721
722
723	/**
724	 * Called when the user scrolls the window
725	 */
726	function onScroll() {
727		// unique tick id
728		++ticks;
729
730		// viewport rectangle
731		var top = jWindow.scrollTop(),
732			left = jWindow.scrollLeft(),
733			right = left + jWindow.width(),
734			bottom = top + jWindow.height();
735
736		// determine which elements are in view
737//        + 60 accounts for fixed nav
738		var intersections = findElements(top+offset.top + 200, right+offset.right, bottom+offset.bottom, left+offset.left);
739		$.each(intersections, function(i, element) {
740
741			var lastTick = element.data('scrollSpy:ticks');
742			if (typeof lastTick != 'number') {
743				// entered into view
744				element.triggerHandler('scrollSpy:enter');
745			}
746
747			// update tick id
748			element.data('scrollSpy:ticks', ticks);
749		});
750
751		// determine which elements are no longer in view
752		$.each(elementsInView, function(i, element) {
753			var lastTick = element.data('scrollSpy:ticks');
754			if (typeof lastTick == 'number' && lastTick !== ticks) {
755				// exited from view
756				element.triggerHandler('scrollSpy:exit');
757				element.data('scrollSpy:ticks', null);
758			}
759		});
760
761		// remember elements in view for next tick
762		elementsInView = intersections;
763	}
764
765	/**
766	 * Called when window is resized
767	*/
768	function onWinSize() {
769		jWindow.trigger('scrollSpy:winSize');
770	}
771
772	/**
773	 * Get time in ms
774   * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
775	 * @type {function}
776	 * @return {number}
777	 */
778	var getTime = (Date.now || function () {
779		return new Date().getTime();
780	});
781
782	/**
783	 * Returns a function, that, when invoked, will only be triggered at most once
784	 * during a given window of time. Normally, the throttled function will run
785	 * as much as it can, without ever going more than once per `wait` duration;
786	 * but if you'd like to disable the execution on the leading edge, pass
787	 * `{leading: false}`. To disable execution on the trailing edge, ditto.
788	 * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
789	 * @param {function} func
790	 * @param {number} wait
791	 * @param {Object=} options
792	 * @returns {Function}
793	 */
794	function throttle(func, wait, options) {
795		var context, args, result;
796		var timeout = null;
797		var previous = 0;
798		options || (options = {});
799		var later = function () {
800			previous = options.leading === false ? 0 : getTime();
801			timeout = null;
802			result = func.apply(context, args);
803			context = args = null;
804		};
805		return function () {
806			var now = getTime();
807			if (!previous && options.leading === false) previous = now;
808			var remaining = wait - (now - previous);
809			context = this;
810			args = arguments;
811			if (remaining <= 0) {
812				clearTimeout(timeout);
813				timeout = null;
814				previous = now;
815				result = func.apply(context, args);
816				context = args = null;
817			} else if (!timeout && options.trailing !== false) {
818				timeout = setTimeout(later, remaining);
819			}
820			return result;
821		};
822	};
823
824	/**
825	 * Enables ScrollSpy using a selector
826	 * @param {jQuery|string} selector  The elements collection, or a selector
827	 * @param {Object=} options	Optional.
828        throttle : number -> scrollspy throttling. Default: 100 ms
829        offsetTop : number -> offset from top. Default: 0
830        offsetRight : number -> offset from right. Default: 0
831        offsetBottom : number -> offset from bottom. Default: 0
832        offsetLeft : number -> offset from left. Default: 0
833	 * @returns {jQuery}
834	 */
835	$.scrollSpy = function(selector, options) {
836		var visible = [];
837		selector = $(selector);
838		selector.each(function(i, element) {
839			elements.push($(element));
840			$(element).data("scrollSpy:id", i);
841			// Smooth scroll to section
842		  $('a[href="#' + $(element).attr('id') + '"]').click(function(e) {
843		    e.preventDefault();
844		    var offset = $(this.hash).offset().top + 1;
845
846//          offset - 200 allows elements near bottom of page to scroll
847
848	    	$('html, body').animate({ scrollTop: offset - 20 }, {duration: 400, queue: false, easing: 'easeOutCubic'});
849
850		  });
851		});
852		options = options || {
853			throttle: 100
854		};
855
856		offset.top = options.offsetTop || 0;
857		offset.right = options.offsetRight || 0;
858		offset.bottom = options.offsetBottom || 0;
859		offset.left = options.offsetLeft || 0;
860
861		var throttledScroll = throttle(onScroll, options.throttle || 100);
862		var readyScroll = function(){
863			$(document).ready(throttledScroll);
864		};
865
866		if (!isSpying) {
867			jWindow.on('scroll', readyScroll);
868			jWindow.on('resize', readyScroll);
869			isSpying = true;
870		}
871
872		// perform a scan once, after current execution context, and after dom is ready
873		setTimeout(readyScroll, 0);
874
875
876		selector.on('scrollSpy:enter', function() {
877			visible = $.grep(visible, function(value) {
878	      return value.height() != 0;
879	    });
880
881			var $this = $(this);
882
883			if (visible[0]) {
884				$('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
885				if ($this.data('scrollSpy:id') < visible[0].data('scrollSpy:id')) {
886					visible.unshift($(this));
887				}
888				else {
889					visible.push($(this));
890				}
891			}
892			else {
893				visible.push($(this));
894			}
895
896
897			$('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
898		});
899		selector.on('scrollSpy:exit', function() {
900			visible = $.grep(visible, function(value) {
901	      return value.height() != 0;
902	    });
903
904			if (visible[0]) {
905				$('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
906				var $this = $(this);
907				visible = $.grep(visible, function(value) {
908	        return value.attr('id') != $this.attr('id');
909	      });
910	      if (visible[0]) { // Check if empty
911					$('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
912	      }
913			}
914		});
915
916		return selector;
917	};
918
919	/**
920	 * Listen for window resize events
921	 * @param {Object=} options						Optional. Set { throttle: number } to change throttling. Default: 100 ms
922	 * @returns {jQuery}		$(window)
923	 */
924	$.winSizeSpy = function(options) {
925		$.winSizeSpy = function() { return jWindow; }; // lock from multiple calls
926		options = options || {
927			throttle: 100
928		};
929		return jWindow.on('resize', throttle(onWinSize, options.throttle || 100));
930	};
931
932	/**
933	 * Enables ScrollSpy on a collection of elements
934	 * e.g. $('.scrollSpy').scrollSpy()
935	 * @param {Object=} options	Optional.
936											throttle : number -> scrollspy throttling. Default: 100 ms
937											offsetTop : number -> offset from top. Default: 0
938											offsetRight : number -> offset from right. Default: 0
939											offsetBottom : number -> offset from bottom. Default: 0
940											offsetLeft : number -> offset from left. Default: 0
941	 * @returns {jQuery}
942	 */
943	$.fn.scrollSpy = function(options) {
944		return $.scrollSpy($(this), options);
945	};
946
947})(jQuery);
948'use strict';
949/**
950 * Register observers:
951 * Topic.on("success", fn1);
952 * Topic.on("success", fn2);
953 * Launch the event:
954 * Topic.fire("success");
955 *
956 * Then fn1 and fn2 are executed
957 * @type {{, fire: fire, on: on}}
958 */
959var Topic = function($){
960    return {
961        _observers: {},
962
963        fire: function (event) {
964            var observers = this._observers[event];
965			if (observers){
966				$.each(observers, function (index, observer) {
967					observer();
968				});
969			}
970        },
971
972        on: function (event, fn) {
973            var observers = this._observers[event];
974            if (observers) {
975                observers.push(fn);
976            }
977            else {
978                this._observers[event] = [fn];
979            }
980        }
981    }
982}($);
983/**
984 * Set current year near to copyright in Footer common element
985 *
986 */
987$(function () {
988    $("#js-current-year").html(new Date().getFullYear());
989});
990document.addEventListener("DOMContentLoaded",
991    function () {
992        var div, n,
993            v = document.getElementsByClassName("youtube-player");
994        for (n = 0; n < v.length; n++) {
995            div = document.createElement("div");
996            div.setAttribute("data-id", v[n].dataset.id);
997            div.innerHTML = labnolThumb(v[n].dataset.id);
998            div.onclick = labnolIframe;
999            v[n].appendChild(div);
1000        }
1001    });
1002
1003function labnolThumb(id) {
1004    var thumb = '<img src="https://i.ytimg.com/vi/ID/hqdefault.jpg">',
1005        play = '<div class="play"></div>';
1006    return thumb.replace("ID", id) + play;
1007}
1008
1009function labnolIframe() {
1010    var iframe = document.createElement("iframe");
1011    var embed = "https://www.youtube.com/embed/ID?autoplay=1";
1012    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
1013    iframe.setAttribute("frameborder", "0");
1014    iframe.setAttribute("allowfullscreen", "1");
1015    this.parentNode.replaceChild(iframe, this);
1016}
1017//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvb2tpZXMuanMiLCJjdXJyZW5jeS5qcyIsImxhbmcuanMiLCJyZWdpc3Rlci5qcyIsInNjcm9sbC5qcyIsInRvcGljLmpzIiwieWVhci5qcyIsInlvdXR1YmUtcGxheWVyLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDckJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDelZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUN0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDN1FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDN1JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNsQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogcHJvdmlkZSB0aGUgZnVuY3Rpb25zIHRvIHNob3cgb3IgaGlkZVxuICogdGhlIGNvb2tpZXMgd2FybmluZy5cbiAqXG4gKiBtdXN0IGV4aXN0cyBhIGh0bWwgd2l0aCBhbiBpZDogJ2Nvb2tpZXMnIGFuZCBtdXN0XG4gKiBleGlzdCBhIGNsb3NlIGJ1dHRvbiB3aXRoIGlkOiAnY29va2llcy1hY2NlcHQnXG4gKiB0byBhY2NlcHQgdGhlIGNvb2tpZXMgcG9saWNpZXMgYW5kIGNsb3NlIHRoZSBtZXNzYWdlLlxuICpcbiAqIGRlcGVuZHMgb24gc2NyaXB0cy91dGlscy5qc1xuICovXG4kKGZ1bmN0aW9uKCkge1xuICAgICd1c2Ugc3RyaWN0JztcbiAgICAvLyBEaXNwbGF5IGNvb2tpZXMgYW5ub3VuY2VcbiAgICBpZiAoIWdldENvb2tpZShcInNob3dlZC1jb29raWVzXCIpKSB7XG4gICAgICAgICQoXCIjY29va2llc1wiKS5hZGRDbGFzcyhcInNob3dcIik7XG4gICAgfVxuXG4gICAgJChcIiNjb29raWVzLWFjY2VwdFwiKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICAgc2V0Q29va2llKFwic2hvd2VkLWNvb2tpZXNcIiwgXCJ0cnVlXCIpO1xuICAgICAgICAkKFwiI2Nvb2tpZXNcIikucmVtb3ZlQ2xhc3MoJ3Nob3cnKTtcbiAgICB9KTtcbn0pOyIsImpRdWVyeShkb2N1bWVudCkucmVhZHkoZnVuY3Rpb24oJCkge1xuICAgIC8qKlxuICAgICAqIElTTyA0MjE3IGN1cnJlbmN5IGNvZGUuIFRoaXMgbWVhbnMgdGhpcyBzaG91bGQgYmUgYSB0aHJlZSBjaGFyYWN0ZXJzXG4gICAgICogc3RyaW5nLCBhbGwgdXBwZXJjYXNlLCByZXByZXNlbnRpbmcgdGhlIGN1cnJlbmN5LlxuICAgICAqXG4gICAgICogQHR5cGVkZWYge1N0cmluZ30gQ3VycmVuY3lDb2RlXG4gICAgICogQHNlZSB7QGxpbmsgaHR0cDovL3d3dy54ZS5jb20vc3ltYm9scy5waHB9XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBDdXJyZW5jeVJhdGVzXG4gICAgICogQHByb3BlcnR5IHtDdXJyZW5jeUNvZGV9IGJhc2VcbiAgICAgKiBAcHJvcGVydHkge1N0cmluZ30gZGF0ZSAtIERhdGUgd2hlbiB0aGlzIHJhdGVzIGFwcGx5LCBpbiBmb3JtYXRcbiAgICAgKiAgICAgICAgIGB5eXl5LU1NLWRkYC5cbiAgICAgKiBAcHJvcGVydHkge09iamVjdH0gcmF0ZXMgLSBDdXJyZW5jeSBjb252ZXJzaW9uIHJhdGVzLiBFdmVyeSBwcm9wZXJ0eVxuICAgICAqICAgICAgICAgb24gdGhpcyBvYmplY3QgaXMgYSBiZSB7QGxpbmsgQ3VycmVuY3lDb2RlfSBhbmQgaXQncyB2YWx1ZVxuICAgICAqICAgICAgICAgaXMgdGhlIGNvbnZlcnNpb24gcmF0ZSB0byBiZSBhcHBsaWVkIHJlbGF0aXZlIHRvIGN1cnJlbnRcbiAgICAgKiAgICAgICAgIG9iamVjdCB7QGxpbmsgI2Jhc2V9XG4gICAgICovXG5cbiAgICAgLyoqXG4gICAgICAqIERlZmF1bHQgYmFzZSBjdXJyZW5jeVxuICAgICAgKiBAdHlwZSB7Q3VycmVuY3lDb3JlfVxuICAgICAgKi9cbiAgICB2YXIgQ1VSUkVOQ1lfQkFTRSA9ICdFVVInXG4gICAgLyoqXG4gICAgICogT3RoZXIgY3VycmVuY2llcyB0aGF0IGFyZSBub3QgdGhlIGJhc2UgYW5kIHNob3VsZCBiZSBoYW5kbGVkIGJ5IHRoaXNcbiAgICAgKiBzY3JpcHQuXG4gICAgICogQHR5cGUge0N1cnJlbmN5Q29kZVtdfVxuICAgICAqL1xuICAgIHZhciBDVVJSRU5DWV9TWU1CT0xTID0gWyAnVVNEJywgJ01YTicgXVxuXG4gICAgLyoqXG4gICAgICogUHJvdmlkZXMgY3VycmVuY3kgcmF0ZXMgaW5mb3JtYXRpb24gdmlhIGFuIGFzeW5jIEhUVFAgQVBJLlxuICAgICAqIEBwYXJhbSB7Q3VycmVuY3lDb2RlfSBbY29uZi5iYXNlXSAtIElTTyBTeW1ib2wgb2YgdGhlIGN1cnJlbmN5IGJhc2UgZm9yXG4gICAgICogICAgICAgI
1017HJhdGUgY2FsY3VsYXRpb24uIEFsbCByYXRlcyBmb3Igb3RoZXIgY3VycmVuY2llcyB3aWxsIGJlIHJlbGF0aXZlXG4gICAgICogICAgICAgIHRvIHRoaXMgY3VycmVuY3kuIElmIG5vdCBwcm92aWRlZCwgZGVmYXVsdHMgdG8gYCdVU0QnYC5cbiAgICAgKiBAcGFyYW0ge0N1cnJlbmN5Q29kZVtdfSBbY29uZi5zeW1ib2xzXSAtIFdoZW4gcmVxdWVzdGluZyBjdXJyZW5jeSByYXRlc1xuICAgICAqICAgICAgICB2aWEgdGhpcyBpbnN0YW5jZSwgb25seSByYXRlcyBmb3IgdGhpcyBjdXJyZW5jaWVzIHNob3VsZCBiZVxuICAgICAqICAgICAgICBpbmNsdWRlZCBvbiB0aGUgcmVzdWx0LiBJZiBub3QgcHJvdmlkZWQgYWxsIGF2YWlsYWJsZSByYXRlcyB3aWxsXG4gICAgICogICAgICAgIGJlIHJldHVybmVkIHdoZW4gcmF0ZXMgYXJlIHJlcXVlc3RlZC5cbiAgICAgKiBAc2VlIHtAbGluayBodHRwOi8vZml4ZXIuaW8vfVxuICAgICAqL1xuICAgIGZ1bmN0aW9uIEN1cnJlbmN5U2VydmljZShjb25mKSB7XG4gICAgICAgIHRoaXMuY29uZiA9IGNvbmYgfHwge31cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHZpYSBjYWxsYmFjayB0aGUgY3VycmVuY3kgY29udmVyc2lvbiByYXRlcyBiYXNlZCBvbiB0aGUgY29uZlxuICAgICAqIHByb3ZpZGVkIG9uIHNlcnZpY2UgY29uc3RydWN0b24uXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIHtGdW5jdGlvbn0gY2JcbiAgICAgKiBAcmV0dXJuIHtDdXJyZW5jeVJhdGVzfVxuICAgICAqIEBzZWUge0BsaW5rIEN1cnJlbmN5U2VydmljZX1cbiAgICAgKi9cbiAgICBDdXJyZW5jeVNlcnZpY2UucHJvdG90eXBlLmdldFJhdGVzID0gZnVuY3Rpb24oY2IpIHtcbiAgICAgICAgdmFyIGNvbmYgPSAkLmV4dGVuZCh7fSwgdGhpcy5jb25mKVxuXG4gICAgICAgIHZhciB1cmwgPSAnLy9hcGkuZXhjaGFuZ2VyYXRlc2FwaS5pby9sYXRlc3QnXG4gICAgICAgIHZhciBwYXJhbXMgPSBbXVxuICAgICAgICBpZiAoY29uZi5zeW1ib2xzKVxuICAgICAgICAgICAgcGFyYW1zLnB1c2goJ2Jhc2U9JyArIGNvbmYuYmFzZSlcbiAgICAgICAgaWYgKGNvbmYuc3ltYm9scy5sZW5ndGggPiAwKVxuICAgICAgICAgICAgcGFyYW1zLnB1c2goJ3N5bWJvbHM9JyArIGNvbmYuc3ltYm9scy5qb2luKCcsJykpXG4gICAgICAgIHVybCArPSAnPycgKyBwYXJhbXMuam9pbignJicpXG4gICAgICAgICQuZ2V0SlNPTih1cmwsIGNiKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdyYXBzIGEgSFRNTEVsZW1lbnQgYW5kIHByb3ZpZGVzIGZ1bmN0aW9uYWxpdHkgb3ZlciBpdHMgY29udGVudCByZWxhdGVkXG4gICAgICogd2l0aCBjdXJyZW5jaWVzIHZhbHVlcy4gQ2hhbmdlcyBvbiBwcm9wZXJ0aWVzIHZpYSBnZXR0ZXJzIGFuZCBzZXR0ZXJzXG4gICAgICogd2lsbCBiZSByZXRyaWV2ZWQsIHN0b3JlZCBhbmQgcmVmbGVjdGVkIGRpcmVjdGx5IG9uIHRoZSBIVE1MRWxlbWVudC5cbiAgICAgKlxuICAgICAqIElmIGEgYmFzZSBQcmljZUNvbnRhaW5lciBpcyBwcm92aWRlZCB0aGUgYWN0dWFsIHByaWNlIG9uIHRoZSBjdXJyZW50XG4gICAgICogaW5zdGFuY2UgaXMgZXhwZWN0ZWQgdG8gYmUgcmVsYXRlZCB3aXRoIHRoZSBiYXNlIHByaWNlLCBidXQgaXQgaXMgbm90XG4gICAgICogbmVjZXNzYXJ5IGF2YWlsYWJsZSByaWdodCBhZnRlciBjb25zdHJ1Y3Rpb24gYW5kIHByb2JhYmx5IHdpbGwgYmUgbmVlZGVkXG4gICAgICogdG8gcHJvdmlkZSBpdCB2aWEge0BsaW5rICNzZXRQcmljZX0uIFNlZSB7QGxpbmsgI2dldFByaWNlfSBmb3IgYSBtb3JlXG4gICAgICogZGV0YWlsZWQgZXhwbGFuYXRpb24uXG4gICAgICpcbiAgICAgKiBUaGlzIGNsYXNzIHdvcmtzIHVuZGVyIGxhenktaW5pdCwgdGhpcyBtZWFucyB0aGF0IGlmIG5vIHNldHRlciBpcyBjYWxsZWRcbiAgICAgKiBhZnRlciBjb25zdHJ1Y3Rpb24sIG1heWJlIHNvbWUgaHRtbCBlbGVtZW50IHByb3BlcnRpZXMgd29uJ3QgYmUgcmVmbGVjdGluZ1xuICAgICAqIGFuIHVwZGF0ZWQgdmFsdWUuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge2pRdWVyeX0gJGVsZW1cbiAgICAgKiBAcGFyYW0ge1ByaWNlQ29udGFpbmVyfSBbYmFzZVByaWNlQ29udGFpbmVyXVxuICAgICAqL1xuICAgIGZ1bmN0aW9uIFByaWNlQ29udGFpbmVyKCRlbGVtLCBiYXNlUHJpY2VDb250YWluZXIpIHtcbiAgICAgICAgdGhpcy4kZWxlbWVudCAgPSAkZWxlbVxuICAgICAgICB0aGlzLmJhc2VQcmljZUNvbnRhaW5lciA9IGJhc2VQcmljZUNvbnRhaW5lciB8fCBudWxsXG4gICAgICAgIHRoaXMuJHByaWNlID0gJGVsZW0uZmluZCgnLnByaWNlJylcbiAgICAgICAgdGhpcy5zZXRQcmljZSh0aGlzLmdldFByaWNlKCkpXG4gICAgICAgIHRoaXMuc2V0Q3VycmVuY3lDb2RlKHRoaXMuZ2V0Q3VycmVuY3lDb2RlKCkgfHwgQ1VSUkVOQ1lfQkFTRSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBNYXRjaGVzIGFuZCBhbGxvd3MgZXh0cmFjdGlvbiBvZiB0aGUgY3VycmVuY3kgY29kZSBmb3IgYW55IGN1cnJlbmN5XG4gICAgICogY2xhc3NOYW1lIGNyZWF0ZWQgdmlhIHtAbGluayBQcmljZUNvbnRhaW5lci5nZXRDdXJyZW5jeUNsYXNzTmFtZX1cbiAgICAgKiBAdHlwZSB7UmVnRXhwfVxuICAgICAqL1xuICAgIFByaWNlQ29udGFpbmVyLkNVUlJFTkNZX0NMQVNTX05BTUVfUkVHRVggPSAvXnByaWNlLWN1cnJlbmN5LShbYS16XXszfSkvaVxuXG4gICAgLyoqXG4gICAgICogR2VuZXJhdGVzIGEgY2xhc3NOYW1lIGZvciB0aGUgZ2l2ZW4gY3VycmVuY3lDb2RlLlxuICAgICAqIEBwYXJhbSAge0N1cnJlbmN5Q29kZX0gY3VycmVuY3lDb2RlXG4gICAgICogQHJldHVybiB7U3RyaW5nfVxuICAgICAqL1xuICAgIFByaWNlQ29udGFpbmVyLmJ1aWxkQ3VycmVuY3lDbGFzc05hbWUgPSBmdW5jdGlvbihjdXJyZW5jeUNvZGUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBjdXJyZW5jeUNvZGUgIT09ICdzdHJpbmcnKVxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignY3VycmVuY3lDb2RlIG11c3QgYmUgYSBzdHJpbmcnKVxuICAgICAgICBpZiAoY3VycmVuY3lDb2RlLmxlbmd0aCAhPT0gMylcbiAgICAgICAgICAgI
1017HRocm93IG5ldyBFcnJvcignY3VycmVuY3lDb2RlIGlzIGV4cGVjdGVkIHRvIGJlIDMgY2hhcmFjdGVycyBsb25nJylcbiAgICAgICAgcmV0dXJuICdwcmljZS1jdXJyZW5jeS0nICsgY3VycmVuY3lDb2RlLnRvTG93ZXJDYXNlKClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGFzc05hbWUgdGhhdCB3aWxsIGJlIGFkZGVkIHRvIHRoZSBQcmljZUNvbnRhaW5lciBib3VuZCBIVE1MRWxlbWVudCB3aGVuXG4gICAgICogcHJpY2UgZm9yIHRoZSBzYWlkIGNvbnRhaW5lciBpcyBub3QgeWV0IGF2YWlsYWJsZS5cbiAgICAgKlxuICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICovXG4gICAgUHJpY2VDb250YWluZXIuUFJJQ0VfTk9UX1lFVF9BVkFJTEFCTEUgPSAncHJpY2UtbG9hZGluZydcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGN1cnJlbnQgcHJpY2UgdmFsdWUgb24gdGhlIGNvbnRhaW5lci4gTnVsbCB2YWx1ZSBpcyByZXR1cm5lZFxuICAgICAqIG9ubHkgaWYgdGhpcyBjb250YWluZXIgd2FzIHByb3ZpZGVkIHdpdGggYSBiYXNlIHtAbGluayBQcmljZUNvbnRhaW5lcn0gb25cbiAgICAgKiBjb25zdHJ1Y3Rpb24gYW5kIHByaWNlIGhhcyBub3QgYmVlbiB1cGRhdGVkIHZpYSB7QGxpbmsgI3NldFByaWNlfVxuICAgICAqIGFmdGVyd2FyZHMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ8bnVsbH1cbiAgICAgKi9cbiAgICBQcmljZUNvbnRhaW5lci5wcm90b3R5cGUuZ2V0UHJpY2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHByaWNlID0gdGhpcy4kcHJpY2UuZGF0YSgncHJpY2UnKVxuICAgICAgICByZXR1cm4gTnVtYmVyKHByaWNlKSB8fCBudWxsXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgY3VycmVudCBwcmljZSBvbiB0aGUgY29udGFpbmVyLiBJZiBwcmljZSBpcyBudWxsLCBjbGFzc05hbWVcbiAgICAgKiBwcm92aWRlZCBieSB7QGxpbmsgLlBSSUNFX05PVF9ZRVRfQVZBSUxBQkxFfSB3aWxsIGJlIGFkZGVkIHRvIHRoZSBjdXJyZW50XG4gICAgICogY29udGFpbmVyIGJvdW5kIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge051bWJlcn0gcHJpY2VcbiAgICAgKi9cbiAgICBQcmljZUNvbnRhaW5lci5wcm90b3R5cGUuc2V0UHJpY2UgPSBmdW5jdGlvbihwcmljZSkge1xuICAgICAgICB2Y
1017XIgY2xhc3NOYW1lID0gUHJpY2VDb250YWluZXIuUFJJQ0VfTk9UX1lFVF9BVkFJTEFCTEVcbiAgICAgICAgdGhpcy4kZWxlbWVudC50b2dnbGVDbGFzcyhjbGFzc05hbWUsIHByaWNlID09PSBudWxsKVxuICAgICAgICBpZiAocHJpY2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHByaWNlID0gTnVtYmVyKHByaWNlKVxuICAgICAgICAgICAgdGhpcy4kcHJpY2UuZGF0YSgncHJpY2UnLCBwcmljZSlcbiAgICAgICAgICAgIHRoaXMudXBkYXRlUHJpY2VWaWV3KClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIFByaWNlQ29udGFpbmVyLnByb3RvdHlwZS51cGRhdGVQcmljZVZpZXcgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHByaWNlID0gdGhpcy4kcHJpY2UuZGF0YSgncHJpY2UnKVxuICAgICAgICB2YXIgbG9jYWxlID0gJCgnaHRtbCcpLmF0dHIoJ2xhbmcnKVxuICAgICAgICB0aGlzLiRwcmljZS50ZXh0KHByaWNlLnRvTG9jYWxlU3RyaW5nKGxvY2FsZSwge1xuICAgICAgICAgICAgbWluaW11bUZyYWN0aW9uRGlnaXRzOiAyLCAvLyBEZWNpbWFsIHBvc2l0aW9uc1xuICAgICAgICAgICAgbWF4aW11bUZyYWN0aW9uRGlnaXRzOiAyLFxuICAgICAgICAgICAgdXNlR3JvdXBpbmc6IGZhbHNlLCAvLyBIdW5kcmVkcyBzZXBhcmF0b3JcbiAgICAgICAgfSkpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0cmlldmVzIGFsbCBjbGFzc05hbWVzIGZyb20gY3VycmVudCBib3VuZCBIVE1MRWxlbWVudCB0aGF0IG1hdGNoXG4gICAgICoge0BsaW5rIFByaWNlQ29udGFpbmVyLkNVUlJFTkNZX0NMQVNTX05BTUVfUkVHRVh9LlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7U3RyaW5nW119XG4gICAgICovXG4gICAgUHJpY2VDb250YWluZXIucHJvdG90eXBlLmdldEN1cnJlbmN5Q2xhc3NOYW1lcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2Y
1017XIgY2xhc3NOYW1lcyAgPSB0aGlzLiRlbGVtZW50LmF0dHIoJ2NsYXNzJykuc3BsaXQoL1xccysvKVxuICAgICAgICB2YXIgY3VycmVuY3lDbGFzc1JneCA9IFByaWNlQ29udGFpbmVyLkNVUlJFTkNZX0NMQVNTX05BTUVfUkVHRVhcbiAgICAgICAgcmV0dXJuIGNsYXNzTmFtZXMuZmlsdGVyKGZ1bmN0aW9uKGMpIHtcbiAgICAgICAgICAgIHJldHVybiBjdXJyZW5jeUNsYXNzUmd4LnRlc3QoYylcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXRyaWV2ZXMgY3VycmVudCBjdXJyZW5jeSBvZiB0aGUgY29udGFpbmVyIGJhc2VkIG9uIGl0cyBjbGFzc05hbWVzLlxuICAgICAqIElmIG5vIGNsYXNzTmFtZSBpcyBmb3VuZCB0aGF0IHByb3ZpZGVzIGN1cnJlbmN5IHdpbGwgcmV0dXJuIG51bGxcbiAgICAgKlxuICAgICAqIEByZXR1cm4ge0N1cnJlbmN5Q29kZXxudWxsfVxuICAgICAqL1xuICAgIFByaWNlQ29udGFpbmVyLnByb3RvdHlwZS5nZXRDdXJyZW5jeUNvZGUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGN1cnJlbmN5Q2xhc3NOYW1lcyA9IHRoaXMuZ2V0Q3VycmVuY3lDbGFzc05hbWVzKClcblxuICAgICAgICBpZiAoY3VycmVuY3lDbGFzc05hbWVzLmxlbmd0aCA8PSAwKVxuICAgICAgICAgICAgcmV0dXJuIG51bGxcblxuICAgICAgICBjdXJyZW5jeUNsYXNzTmFtZXMgPSBjdXJyZW5jeUNsYXNzTmFtZXNbMF1cbiAgICAgICAgdmFyIGN1cnJlbmN5Q2xhc3NSZ3ggPSBQcmljZUNvbnRhaW5lci5DVVJSRU5DWV9DTEFTU19OQU1FX1JFR0VYXG4gICAgICAgIHZhciBjdXJyZW5jeVN5bWJvbCA9IGN1cnJlbmN5Q2xhc3NSZ3guZXhlYyhjdXJyZW5jeUNsYXNzTmFtZXMpWzFdXG4gICAgICAgIHJldHVybiBjdXJyZW5jeVN5bWJvbC50b1VwcGVyQ2FzZSgpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgY3VycmVudCBjdXJyZW5jeSBjb2RlIHRvIHRoZSBvbmUgcHJvdmlkZWQuIFRoaXMgYWRkcyBhIGNsYXNzXG4gICAgICogZ2VuZXJhdGVkIHZpYSB7QGxpbmsgLmdldEN1cnJlbmN5Q2xhc3NOYW1lfSB0byB0aGUgYm91bmQgSFRNTCBlbGVtZW50XG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0N1cnJlbmN5Q29kZX0gY29kZVxuICAgICAqL1xuICAgIFByaWNlQ29udGFpbmVyLnByb3RvdHlwZS5zZXRDdXJyZW5jeUNvZGUgPSBmdW5jdGlvbihjb2RlKSB7XG4gICAgICAgIHZhciBjdXJyZW5jeUNsYXNzZXMgPSB0aGlzLmdldEN1cnJlbmN5Q2xhc3NOYW1lcygpXG4gICAgICAgIHRoaXMuJGVsZW1lbnQucmVtb3ZlQ2xhc3MoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gY3VycmVuY3lDbGFzc2VzLmpvaW4oJyAnKVxuICAgICAgICB9KVxuICAgICAgICB0aGlzLiRlbGVtZW50LmFkZENsYXNzKFByaWNlQ29udGFpbmVyLmJ1aWxkQ3VycmVuY3lDbGFzc05hbWUoY29kZSkpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWYgYSBiYXNlIHtAbGluayBQcmljZUNvbnRhaW5lcn0gd2FzIHByb3ZpZGVkIG9uIGNvbnN0cnVjdGlvbiwgdGhpc1xuICAgICAqIG1ldGhvZCByZXR1cm5zIHRoZSByZXN1bHQgb2Yge0BsaW5rICNnZXRQcmljZX0gb24gc2FpZCBiYXNlIGNvbnRhaW5lci5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge051bWJlcnxOdWxsfVxuICAgICAqL1xuICAgIFByaWNlQ29udGFpbmVyLnByb3RvdHlwZS5nZXRCYXNlUHJpY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmICghdGhpcy5iYXNlUHJpY2VDb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICByZXR1cm4gdGhpcy5iYXNlUHJpY2VDb250YWluZXIuZ2V0UHJpY2UoKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIElmIGEgYmFzZSB7QGxpbmsgUHJpY2VDb250YWluZXJ9IHdhcyBwcm92aWRlZCBvbiBjb25zdHJ1Y3Rpb24sIHRoaXNcbiAgICAgKiBtZXRob2QgcmV0dXJucyB0aGUgcmVzdWx0IG9mIHtAbGluayAjZ2V0Q3VycmVuY3lDb2RlfSBvbiBzYWlkIGJhc2VcbiAgICAgKiBjb250YWluZXIuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ8TnVsbH1cbiAgICAgKi9cbiAgICBQcmljZUNvbnRhaW5lci5wcm90b3R5cGUuZ2V0QmFzZUN1cnJlbmN5Q29kZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmJhc2VQcmljZUNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIHJldHVybiB0aGlzLmJhc2VQcmljZUNvbnRhaW5lci5nZXRDdXJyZW5jeUNvZGUoKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRyaWVzIHRvIGZpbmQgYSBwcmljaW5nIHRhYmxlIGFuZCBhdCBsZWFzdCBvbmUgY3VycmVuY3kgY2hhbmdlci4gSWYgYW55XG4gICAgICogb2YgdGhlbSBpcyBub3QgZm91bmQsIHRoZSBmdW5jdGlvbiBlbmRzLlxuICAgICAqXG4gICAgICogQWRkcyBhIGNsaWNrIGxpc3RlbmVyIHRvIGV2ZXJ5IGN1cnJlbmN5IGNoYW5nZXIgdGhhdCB0b2dnbGVzIGEgY3VycmVuY3lcbiAgICAgKiBjbGFzcyBvbiB0aGUgcHJpY2luZyB0YWJsZSBiYXNlZCBvbiB0aGUgYGRhdGEtY3VycmVuY3lgIGF0dHJpYnV0ZSBvblxuICAgICAqIHRoZSBjdXJyZW5jeSBjaGFuZ2VyLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGluaXRpYWxpemVDdXJyZW5jeVNlbGVjdG9ycygpIHtcbiAgICAgICAgdmFyICRwcmljaW5nID0gJCgnLnByaWNpbmctdGFibGVzJylcbiAgICAgICAgdmFyICRjdXJyZW5jeUNoYW5nZXJzID0gJCgnW2RhdGEtY3VycmVuY3ldJylcblxuICAgICAgICBpZiAoJHByaWNpbmcubGVuZ3RoID09IDAgfHwgJGN1cnJlbmN5Q2hhbmdlcnMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuXG5cbiAgICAgICAgdmFyIHNldFNlbGVjdGVkQ3VycmVuY3lDbGFzc05hbWUgPSBmdW5jdGlvbihjdXJyZW5jeUNvZGUpIHtcbiAgICAgICAgICAgICRwcmljaW5nLmFkZENsYXNzKCdzZWxlY3RlZC1jdXJyZW5jeS0nICsgY3VycmVuY3lDb2RlKVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHJlbW92ZVNlbGVjdGVkQ3VycmVuY3lDbGFzc05hbWUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICRwcmljaW5nLnJlbW92ZUNsYXNzKGZ1bmN0aW9uKGksIGNsYXNzTmFtZXMpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY2xhc3NOYW1lcy5zcGxpdCgvXFxzKy8pLmZpbHRlcihmdW5jdGlvbihjbGFzc05hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIC9ec2VsZWN0ZWQtY3VycmVuY3ktKFthLXpdezN9KSQvLnRlc3QoY2xhc3NOYW1lKVxuICAgICAgICAgICAgICAgI
1017H0pLmpvaW4oJyAnKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgICRjdXJyZW5jeUNoYW5nZXJzLm9uKCdjbGljaycsIGZ1bmN0aW9uKGV2KSB7XG4gICAgICAgICAgICB2YXIgJHRhcmdldCA9ICQoZXYudGFyZ2V0KVxuICAgICAgICAgICAgdmFyIGN1cnJlbmN5Q29kZSA9ICR0YXJnZXQuZGF0YSgnY3VycmVuY3knKS50b0xvd2VyQ2FzZSgpXG4gICAgICAgICAgICAkY3VycmVuY3lDaGFuZ2Vycy5yZW1vdmVDbGFzcygnYWN0aXZlJylcbiAgICAgICAgICAgICR0YXJnZXQuYWRkQ2xhc3MoJ2FjdGl2ZScpXG4gICAgICAgICAgICByZW1vdmVTZWxlY3RlZEN1cnJlbmN5Q2xhc3NOYW1lKClcbiAgICAgICAgICAgIHNldFNlbGVjdGVkQ3VycmVuY3lDbGFzc05hbWUoY3VycmVuY3lDb2RlKVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpbmRzIGFsbCBgLnByaWNlLWNvbnRhaW5lcmAgb24gdGhlIGRvY3VtZW50LCB3cmFwcyBldmVyeSBvbmUgb2YgdGhlbVxuICAgICAqIG9uIGEge0BsaW5rIFByaWNlQ29udGFpbmVyfSBhbmQgZ2VuZXJhdGVzIHRoZSBtaXNzaW5nIHByaWNlQ29udGFpbmVyc1xuICAgICAqIGJhc2VkIG9uIENVUlJFTkNZX1NZTUJPTFMgKEhUTUxFbGVtZW50IGFuZCB7QGxpbmsgUHJpY2VDb250YWluZXJ9XG4gICAgICogaW5zdGFuY2VzKS5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge1ByaWNlQ29udGFpbmVyW119IEFsbCBpbnN0YW5jZXMgZ2VuZXJhdGVkIG9uIHRoZSBwcm9jZXNzLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGluaXRpYWxpemVQcmljZUNvbnRhaW5lcnMoKSB7XG4gICAgICAgIHZhciBwcmljZUNvbnRhaW5lcnMgPSAkKCcucHJpY2UtY29udGFpbmVyJykubWFwKGZ1bmN0aW9uKGksIGUpIHtcbiAgICAgICAgICAgIHZhciAkcHJpY2VDb250YWluZXIgPSAkKGUpXG4gICAgICAgICAgICB2Y
1017XIgcHJpY2VDb250YWluZXIgPSBuZXcgUHJpY2VDb250YWluZXIoJHByaWNlQ29udGFpbmVyKVxuICAgICAgICAgICAgdmFyIHByaWNlQ29udGFpbmVycyA9IFsgcHJpY2VDb250YWluZXIgXVxuICAgICAgICAgICAgQ1VSUkVOQ1lfU1lNQk9MUy5yZWR1Y2UoZnVuY3Rpb24oaW5zZXJ0QWZ0ZXIsIHMpIHtcbiAgICAgICAgICAgICAgICB2YXIgJHByaWNlQ29udGFpbmVyQ2xvbmUgPSAkcHJpY2VDb250YWluZXIuY2xvbmUoKVxuICAgICAgICAgICAgICAgIHZhciBwcmljZUNvbnRhaW5lckNsb25lID0gbmV3IFByaWNlQ29udGFpbmVyKCRwcmljZUNvbnRhaW5lckNsb25lLCBwcmljZUNvbnRhaW5lcilcbiAgICAgICAgICAgICAgICBwcmljZUNvbnRhaW5lckNsb25lLnNldEN1cnJlbmN5Q29kZShzKVxuICAgICAgICAgICAgICAgIHByaWNlQ29udGFpbmVyQ2xvbmUuc2V0UHJpY2UobnVsbClcbiAgICAgICAgICAgICAgICBwcmljZUNvbnRhaW5lcnMucHVzaChwcmljZUNvbnRhaW5lckNsb25lKVxuICAgICAgICAgICAgICAgIHJldHVybiAkcHJpY2VDb250YWluZXJDbG9uZS5pbnNlcnRBZnRlcihpbnNlcnRBZnRlcilcbiAgICAgICAgICAgIH0sICRwcmljZUNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybiBwcmljZUNvbnRhaW5lcnNcbiAgICAgICAgfSkuZ2V0KClcbiAgICAgICAgLy8gRmxhdHRlbiB0aGUgXCJhcnJheSBvZiBhcnJheXNcIlxuICAgICAgICByZXR1cm4gW10uY29uY2F0LmFwcGx5KFtdLCBwcmljZUNvbnRhaW5lcnMpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyBhbGwgdGhlIHByb3ZpZGVkIHByaWNlQ29udGFpbmVycyBwcmljZXMgd2l0aCB0aGUgZ2l2ZW5cbiAgICAgKiBjb252ZXJzaW9uIHJhdGVzLiBEdWUgdG8gaG93IHByaWNlQ29udGFpbmVycyB3b3JrIHRoaXMgd2lsbCBvbmx5IGFmZmVjdFxuICAgICAqIHtAbGluayBQcmljZUNvbnRhaW5lcn0tcyB0aGF0IHdlcmUgcHJvdmlkZWQgd2l0aCBhIGJhc2UgUHJpY2VDb250YWllbmVyLFxuICAgICAqIGluIG91ciBjYXNlIHRoYXQgbWVhbnMgZXZlcnkgcHJpY2VDb250YWluZXIgZ2VuZXJhdGVkIGFmdGVyIHRoZSBwYWdlXG4gICAgICogbG9hZGVkIChjcmVhdGVkIG9uIHtAbGluayBpbml0aWFsaXplUHJpY2VDb250YWluZXJzfSlcbiAgICAgKlxuICAgICAqIEBwYXJhbSAge1ByaWNlQ29udGFpbmVyW119IHByaWNlQ29udGFpbmVycyBbZGVzY3JpcHRpb25dXG4gICAgICogQHBhcmFtICB7Q3VycmVuY3lSYXRlc30gcmF0ZXNcbiAgICAgKi9cbiAgICBmdW5jdGlvbiB1cGRhdGVQcmljZUNvbnRhaW5lcnMocHJpY2VDb250YWluZXJzLCByYXRlcykge1xuICAgICAgICAkLmV4dGVuZChmeCwgcmF0ZXMpIC8vIFNldHVwIEZYIHdpdGggdGhlIHByb3ZpZGVkIGJhc2UgYW5kIHJhdGVzIGZyb20gdGhlIGFwaVxuICAgICAgICBwcmljZUNvbnRhaW5lcnNcbiAgICAgICAgLmZpbHRlcihmdW5jdGlvbihwcmljZUNvbnRhaW5lcikge1xuICAgICAgICAgICAgLy8gSWdvbnJlIHByaWNlIGNvbnRhaW5lcnMgdGhhdCBoYXZlIG5vdCBhIGJhc2UgcHJpY2VcbiAgICAgICAgICAgIHJldHVybiBwcmljZUNvbnRhaW5lci5nZXRCYXNlQ3VycmVuY3lDb2RlKCkgIT09IG51bGxcbiAgICAgICAgfSlcbiAgICAgICAgLmZvckVhY2goZnVuY3Rpb24ocHJpY2VDb250YWluZXIpIHtcbiAgICAgICAgICAgIHZhciBiYXNlUHJpY2UgPSBwcmljZUNvbnRhaW5lci5nZXRCYXNlUHJpY2UoKVxuICAgICAgICAgICAgdmFyIGJhc2VTeW1ib2wgPSBwcmljZUNvbnRhaW5lci5nZXRCYXNlQ3VycmVuY3lDb2RlKClcbiAgICAgICAgICAgIHZhciBjdXJyZW5jeVN5bWJvbCA9IHByaWNlQ29udGFpbmVyLmdldEN1cnJlbmN5Q29kZSgpXG4gICAgICAgICAgICB2Y
1017XIgcmF0ZSA9IGZ4KGJhc2VQcmljZSkuZnJvbShiYXNlU3ltYm9sKS50byhjdXJyZW5jeVN5bWJvbClcbiAgICAgICAgICAgIHZhciBwcmljZSA9IHJhdGUudG9GaXhlZCgyKVxuICAgICAgICAgICAgcHJpY2VDb250YWluZXIuc2V0UHJpY2UocHJpY2UpXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0dXBzIGV2ZXJ5dGhpbmcgbmVjZXNzYXJ5IGFuZCBzdGFydHMgaXRcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBpbml0aWFsaXplKCkge1xuICAgICAgICB2YXIgY3VycmVuY3lTZXJ2aWNlID0gbmV3IEN1cnJlbmN5U2VydmljZSh7XG4gICAgICAgICAgICBiYXNlOiBDVVJSRU5DWV9CQVNFLFxuICAgICAgICAgICAgc3ltYm9sczogQ1VSUkVOQ1lfU1lNQk9MU1xuICAgICAgICB9KVxuXG4gICAgICAgIHZhciBwcmljZUNvbnRhaW5lcnMgPSBpbml0aWFsaXplUHJpY2VDb250YWluZXJzKClcbiAgICAgICAgaWYgKHByaWNlQ29udGFpbmVycy5sZW5ndGggPD0gMCkgLy8gSWYgbm8gcHJpY2UgY29udGFpbmVycywgZG8gbm90aGluZ1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIC8vIFJldHJpZXZlIGNvbnZlcnNpb24gcmF0ZXMgYW5kIHVwZGF0ZSBwcmljZSBjb250YWluZXJzXG4gICAgICAgIGN1cnJlbmN5U2VydmljZS5nZXRSYXRlcyhmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgICAgICB1cGRhdGVQcmljZUNvbnRhaW5lcnMocHJpY2VDb250YWluZXJzLCBkYXRhKVxuICAgICAgICB9KVxuICAgICAgICBpbml0aWFsaXplQ3VycmVuY3lTZWxlY3RvcnMoKVxuICAgIH1cblxuICAgIGluaXRpYWxpemUoKVxufSlcbiIsIi8qKlxuICogU2F2ZSB0aGUgbGFuZyB3aGVuIGl0IGlzIGNsaWNrZWQgdG8gc3RvcmUgYXMgZGVmYXVsdCBsYW5nIGZvciB0aGUgdXNlci5cbiAqXG4gKiBkZXBlbmRzIG9uIHNjcmlwdHMvdXRpbHMuanNcbiAqL1xuJChmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHNldHVwTGFuZyA9IGZ1bmN0aW9uKCl7XG4gICAgICAgIHZhciAkbGFuZ0VsZW0gPSAkKFwiYS5sYW5nLWxpbmtbZGF0YS1sYW5nXVwiKTtcbiAgICAgICAgLy9BZGQgZHluYW1pYyB0cmFuc2xhdGlvbnNcbiAgICAgICAgdmFyIGxhbmcgPSAkbGFuZ0VsZW0uZGF0YShcImxhbmdcIik7XG4gICAgICAgIC8vQ2hhbmdlIGxhbmdfY29va2llXG4gICAgICAgICRsYW5nRWxlbS5jbGljayhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZXRDb29raWUoXCJ1cHBfbGFuZ3VhZ2VcIiwgJCh0aGlzKS5kYXRhKFwibGFuZ1wiKSk7XG4gICAgICAgICAgICBsb2coXCJjaGFuZ2UgbGFuZyB0byA9PiBcIiArICQodGhpcykuZGF0YShcImxhbmdcIikpO1xuICAgICAgICB9KTtcbiAgICAgICAgLy9EaXNwbGF5IGNvb2tpZXMgYW5ub3VuY2VcbiAgICAgICAgaWYgKCFnZXRDb29raWUoXCJzaG93ZWQtY29va2llc1wiKSkge1xuICAgICAgICAgICAgJChcIiNjb29raWVzXCIpLmFkZENsYXNzKFwic2hvd1wiKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBzZXR1cExhbmcoKTtcbn0pOyIsIi8qKlxuICogUmVnaXN0ZXIgZm9ybSBzZXR1cCB3aXRoOlxuICogLSB1dG0gcGFyYW1ldGVyc1xuICogLSBhamF4IHJlZ2lzdGVyIHdpdGggZXJyb3IgY29kZXNcbiAqIC0gdGhlIGZvcm0gbXVzdCBoYXZlIGEgY29uY3JldGUgc3RydWN0dXJlOlxuICogICAgICBmb3JtKG5hbWU9J3JlZ2lzdGVyJylcbiAqICAgICAgICAgIHAoY2xhc3M9J2dlbmVyYWwtZXJyb3InKSB3aXRoIGdlbmVyYWwgZXJyb3IgY29kZVxuICogICAgICAgICAgaW5wdXQobmFtZT1hcHBOYW1lfGVtYWlsfHBhc3N3b3JkKVxuICogICAgICAgICAgcChjbGFzcz0ncmVnaXN0ZXItYXBwTmFtZS1lcnJvcnxyZWdpc3Rlci1lbWFpbC1lcnJvcnxyZWdpc3Rlci1wYXNzd29yZC1lcnJvcicpIHdpdGggY29uY3JldGUgZXJyb3IgZm9yIHRoZSBpbnB1dFxuICpcbiAqICAgICAgICAgIHN1Ym1pdChkYXRhLXRleHQ9XCJkZWZhdWx0IHRleHRcIiwgZGF0YS1sb2FkaW5nPVwibG9hZGluZyB0ZXh0XCIsIHZhbHVlPW9wdGlvbmFsKioqKVxuICogICAgICAgICAgICAgIHNwYW4gdGV4dCAob3B0aW9uYWwpKioqXG4gKiAqKiogc3BhbiB0ZXh0IG9yIHZhbHVlIG11c3QgZXhpc3QgdG8gc2V0IHRoZSB0ZXh0IG9mIHRoZSBzdWJtaXQgYnV0dG9ufGlucHV0XG4gKlxuICogZGVwZW5kcyBvbiBzY3JpcHRzL3V0aWxzLmpzXG4gKi9cbiQoZnVuY3Rpb24gKCkge1xuICAgICd1c2Ugc3RyaWN0JztcblxuICAgIHZhciBVVE1fUEFSQU1TID0gW1widXRtX3NvdXJjZVwiLCBcInV0bV9jYW1wYWlnblwiLCBcInV0bV9tZWRpdW1cIiwgXCJ1dG1fY29udGVudFwiXTtcblxuICAgIC8qKlxuICAgICAqIEFkZHMgdGhlIFVUTSBwYXJhbWV0ZXJzIHRvIHRoZSBnaXZlbiBmb3JtLlxuICAgICAqIEBwYXJhbSB7alF1ZXJ5fSAkZm9ybSBGb3JtIHdoZXJlIHRoZSBwYXJhbXMgd2lsbCBiZSBhcHBlbmRlZFxuICAgICAqL1xuICAgIHZhciBhZGRVdG1QYXJhbXNUb0Zvcm0gPSBmdW5jdGlvbiAoJGZvcm0pIHtcbiAgICAgICAgJC5lYWNoKFVUTV9QQVJBTVMsIGZ1bmN0aW9uIChpLCB2YWwpIHtcbiAgICAgICAgICAgIHZhciB1dG1WYWwgPSBnZXRDb29raWUodmFsKTtcbiAgICAgICAgICAgIGlmICh1dG1WYWwpIHtcbiAgICAgICAgICAgICAgICB2Y
1017XIgJHV0bSA9ICQoXCI8aW5wdXQ+XCIpXG4gICAgICAgICAgICAgICAgICAgIC5hdHRyKFwidHlwZVwiLCBcImhpZGRlblwiKVxuICAgICAgICAgICAgICAgICAgICAuYXR0cihcIm5hbWVcIiwgdmFsKS52YWwodXRtVmFsKTtcbiAgICAgICAgICAgICAgICAkZm9ybS5hcHBlbmQoJHV0bSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIHRoZSBVVE0gcGFyYW1ldGVycyB0byB0aGUgZ2l2ZW4gdXJsXG4gICAgICogQHBhcmFtIHtTdHJpbmd9IHVybCB3aXRoIHV0bSBwYXJhbXMgYXQgbG9naW5cbiAgICAgKi9cbiAgICB2YXIgYWRkVXRtUGFyYW1zVG9VcmwgPSBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgIHZhciBwYXJhbXMgPSBcIiZcIjtcbiAgICAgICAgaWYgKHVybC5pbmRleE9mKFwiP1wiKSA9PSAtMSkge1xuICAgICAgICAgICAgcGFyYW1zID0gXCI/XCI7XG4gICAgICAgIH1cblxuICAgICAgICAkLmVhY2goVVRNX1BBUkFNUywgZnVuY3Rpb24gKGksIHZhbCkge1xuICAgICAgICAgICAgdmFyIHV0bVZhbCA9IGdldENvb2tpZSh2YWwpO1xuICAgICAgICAgICAgaWYgKHV0bVZhbCkge1xuICAgICAgICAgICAgICAgIHBhcmFtcyArPSB2YWwgKyBcIj1cIiArIHV0bVZhbCArIFwiJlwiO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICB1cmwgKz0gcGFyYW1zLnN1YnN0cmluZygwLCBwYXJhbXMubGVuZ3RoIC0gMSk7XG5cbiAgICAgICAgcmV0dXJuIHVybDtcbiAgICB9O1xuXG4gICAgdmFyIHNldHVwID0gZnVuY3Rpb24gKCkge1xuXG4gICAgICAgIHZhciByZWdpc3RlclN1Y2Nlc3NDYWxsYmFjayA9IGZ1bmN0aW9uIChmbiwgc3VibWl0KSB7XG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YS5zdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIFRvcGljLmZpcmUoXCJyZWdpc3Rlci1zdWNjZXNzXCIpO1xuXHRcdFx0XHRcdHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCl7XG5cdFx0XHRcdFx0XHR3aW5kb3cubG9jYXRpb24gPSBhZGRVdG1QYXJhbXNUb1VybChEQVNIQk9BUkRfUEFUSCArIFwiL3YyXCIgKyBkYXRhLnVybCk7XG5cdFx0XHRcdFx0fSwgNDAwKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAkKHRoaXMpLmZpbmQoXCJpbnB1dFwiKS5wcm9wKFwiZGlzYWJsZWRcIiwgZmFsc2UpO1xuXG4gICAgICAgICAgICAgICAgICAgIHN1Ym1pdC52YWwoc3VibWl0LmRhdGEoXCJ0ZXh0XCIpKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyICRpbm5lclNwYW4gPSBzdWJtaXQuZmluZChcInNwYW5cIik7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkaW5uZXJTcGFuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICRpbm5lclNwYW4udGV4dChzdWJtaXQuZGF0YShcInRleHRcIikpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgc3VibWl0LnByb3AoXCJkaXNhYmxlZFwiLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBlcnJvciA9IHt9O1xuXG4gICAgICAgICAgICAgICAgICAgIHN3aXRjaCAoZGF0YS5jYXNlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9DUkVBVElOR19BUFBSRVFVRVNUX0VSUk9SXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IuYXBwTmFtZSA9IGFqYXguZXJyb3JbMl07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9EVVBMSUNBVEVfRU1BSUxfRVJST1JcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5lbWFpbCA9IGFqYXguZXJyb3JbM107XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDQ6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9EVVBMSUNBVEVfTkFNRV9FUlJPUlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmFwcE5hbWUgPSBhamF4LmVycm9yWzRdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSA1OlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vU0VMTEVSX0VSUk9SXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3Iuc2VsbGVyID0gYWpheC5lcnJvcls1XTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgNjpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0RVUExJQ0FURV9OQU1FX0FORF9EVVBMSUNBVEVEX0VNQUlMX0VSUk9SXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IuZW1haWwgPSBhamF4LmVycm9yWzNdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmFwcE5hbWUgPSBhamF4LmVycm9yWzRdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSA3OlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vRFVQTElDQVRFX05BTUVfQU5EX1NFTExFUl9FUlJPUlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmFwcE5hbWUgPSBhamF4LmVycm9yWzRdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLnNlbGxlciA9IGFqYXguZXJyb3JbNV07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDg6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9EVVBMSUNBVEVfRU1BSUxfQU5EX1NFTExFUl9FUlJPUlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmVtYWlsID0gYWpheC5lcnJvclszXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zZWxsZXIgPSBhamF4LmVycm9yWzVdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSA5OlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vRFVQTElDQVRFX05BTUVfQU5EX0RVUExJQ0FURURfRU1BSUxfQU5EX1NFTExFUl9FUlJPUlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmVtYWlsID0gYWpheC5lcnJvclszXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5hcHBOYW1lID0gYWpheC5lcnJvcls0XTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zZWxsZXIgPSBhamF4LmVycm9yWzVdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAxMDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL1NPTUVfUEFSQU1FVEVSX05VTExfRVJST1JcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5nZW5lcmFsID0gYWpheC5lcnJvclsxMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDExOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTkFNRV9JTlZBTElEX0VSUk9SXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IuYXBwTmFtZSA9IGFqYXguZXJyb3JbMTFdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAxMjpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL05BTUVfSU5WQUxJRF9BTkRfRFVQTElDQVRFRF9FTUFJTF9FUlJPUlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLmFwcE5hbWUgPSBhamF4LmVycm9yWzExXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5lbWFpbCA9IGFqYXguZXJyb3JbM107XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDEzOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTElDRU5TRV9JTlZBTElEX0VSUk9SXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IubGljZW5zZSA9IGFqYXguZXJyb3JbMTNdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgZm4odGhpcywgZXJyb3IpO1xuICAgICAgICAgICAgICAgI
1017H1cbiAgICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIHJlZ2lzdGVyRXJyb3JDYWxsYmFjayA9IGZ1bmN0aW9uIChzdWJtaXQpIHtcbiAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbiAoeGhyKSB7XG4gICAgICAgICAgICAgICAgLy8gZW5kIGxvYWRpbmdcbiAgICAgICAgICAgICAgICAkKHRoaXMpLmZpbmQoXCJpbnB1dFwiKS5wcm9wKFwiZGlzYWJsZWRcIiwgZmFsc2UpO1xuXG4gICAgICAgICAgICAgICAgc3VibWl0LnZhbChzdWJtaXQuZGF0YShcInRleHRcIikpO1xuICAgICAgICAgICAgICAgIHZhciAkaW5uZXJTcGFuID0gc3VibWl0LmZpbmQoXCJzcGFuXCIpO1xuICAgICAgICAgICAgICAgIGlmICgkaW5uZXJTcGFuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgJGlubmVyU3Bhbi50ZXh0KHN1Ym1pdC5kYXRhKFwidGV4dFwiKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgc3VibWl0LnByb3AoXCJkaXNhYmxlZFwiLCBmYWxzZSk7XG5cbiAgICAgICAgICAgICAgICAkKHRoaXMpLmZpbmQoXCIuZ2VuZXJhbC1lcnJvclwiKS5zaG93KCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9O1xuXG4gICAgICAgIC8vUmVnaXN0ZXIgZm9ybSB0aHJvdWdoIEFKQVhcbiAgICAgICAgLy9WYWxpZGF0aW9uIGRvbmUgaW4gSFRNTDUgKHBhdHRlcm5zLCBtaW5sZW5ndGgsIGV0YylcbiAgICAgICAgJChcImZvcm1bbmFtZT0ncmVnaXN0ZXInXVwiKS5zdWJtaXQoZnVuY3Rpb24gKGUpIHtcblxuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgJCh0aGlzKS5maW5kKFwiLmdlbmVyYWwtZXJyb3JcIikuaGlkZSgpO1xuXG4gICAgICAgICAgICB2Y
1017XIgJGZvcm0gPSAkKHRoaXMpO1xuICAgICAgICAgICAgYWRkVXRtUGFyYW1zVG9Gb3JtKCRmb3JtKTtcblxuICAgICAgICAgICAgdmFyIHVybCA9IERBU0hCT0FSRF9QQVRIICsgXCIvdjIvcmVnaXN0ZXJcIjtcbiAgICAgICAgICAgIHZhciBkYXRhID0gJGZvcm0uc2VyaWFsaXplKCk7XG4gICAgICAgICAgICBpZiAoIWRhdGEpIHJldHVybjtcblxuICAgICAgICAgICAgbG9nKFwiVVJMOiBcIiArIHVybCk7XG4gICAgICAgICAgICBsb2coXCJEYXRhIHRvIHNlbmQ6XCIgKyBkYXRhKTtcbiAgICAgICAgICAgIC8vIGxvYWRpbmdcbiAgICAgICAgICAgICQodGhpcykuZmluZChcImlucHV0XCIpLnByb3AoXCJkaXNhYmxlZFwiLCB0cnVlKTtcbiAgICAgICAgICAgIHZhciBzdWJtaXQgPSAkKHRoaXMpLmZpbmQoXCJbdHlwZT0nc3VibWl0J11cIik7XG5cbiAgICAgICAgICAgIHN1Ym1pdC52YWwoc3VibWl0LmRhdGEoXCJsb2FkaW5nXCIpKTtcbiAgICAgICAgICAgIHZhciAkaW5uZXJTcGFuID0gc3VibWl0LmZpbmQoXCJzcGFuXCIpO1xuICAgICAgICAgICAgaWYgKCRpbm5lclNwYW4ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICRpbm5lclNwYW4udGV4dChzdWJtaXQuZGF0YShcImxvYWRpbmdcIikpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzdWJtaXQucHJvcChcImRpc2FibGVkXCIsIHRydWUpO1xuICAgICAgICAgICAgJC5hamF4KHtcbiAgICAgICAgICAgICAgICB0eXBlOiBcIkdFVFwiLCAvLyBqc29ucCBvbmx5IHdvcmsgd2l0aCBnZXRcbiAgICAgICAgICAgICAgICB1cmw6IHVybCxcbiAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLFxuICAgICAgICAgICAgICAgIGNvbnRleHQ6IHRoaXMsXG4gICAgICAgICAgICAgICAgZGF0YVR5cGU6IFwianNvbnBcIixcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiByZWdpc3RlclN1Y2Nlc3NDYWxsYmFjayhmdW5jdGlvbiAoZm9ybSwgZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyICRlbWFpbEVycm9yID0gJChmb3JtKS5maW5kKFwiLnJlZ2lzdGVyLWVtYWlsLWVycm9yXCIpLFxuICAgICAgICAgICAgICAgICAgICAgICAgJGFwcE5hbWVFcnJvciA9ICQoZm9ybSkuZmluZChcIi5yZWdpc3Rlci1hcHBOYW1lLWVycm9yXCIpLFxuICAgICAgICAgICAgICAgICAgICAgICAgJHNlbGxlckVycm9yID0gJChmb3JtKS5maW5kKFwiLnJlZ2lzdGVyLXNlbGxlci1lcnJvclwiKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICRsaWNlbnNlRXJyb3IgPSAkKGZvcm0pLmZpbmQoXCIucmVnaXN0ZXItbGljZW5zZS1lcnJvclwiKTtcblxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChlcnJvci5lbWFpbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJGVtYWlsRXJyb3IudGV4dChlcnJvci5lbWFpbCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkZW1haWxFcnJvci5zaG93KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGZvcm0pLmZpbmQoJ2lucHV0W25hbWU9ZW1haWxdJykuYWRkQ2xhc3MoXCJlcnJvclwiKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChlcnJvci5hcHBOYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkYXBwTmFtZUVycm9yLnRleHQoZXJyb3IuYXBwTmFtZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkYXBwTmFtZUVycm9yLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZm9ybSkuZmluZCgnaW5wdXRbbmFtZT1hcHBOYW1lXScpLmFkZENsYXNzKFwiZXJyb3JcIik7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoZXJyb3Iuc2VsbGVyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkc2VsbGVyRXJyb3IudGV4dChlcnJvci5zZWxsZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJHNlbGxlckVycm9yLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZm9ybSkuZmluZCgnaW5wdXRbbmFtZT1zZWxsZXJdJykuYWRkQ2xhc3MoXCJlcnJvclwiKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChlcnJvci5saWNlbnNlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkbGljZW5zZUVycm9yLnRleHQoZXJyb3IubGljZW5zZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkbGljZW5zZUVycm9yLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZm9ybSkuZmluZCgnaW5wdXRbbmFtZT1saWNlbnNlXScpLmFkZENsYXNzKFwiZXJyb3JcIik7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoZXJyb3IuZ2VuZXJhbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJChmb3JtKS5maW5kKFwiLmdlbmVyYWwtZXJyb3JcIikuc2hvdygpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB9LCBzdWJtaXQpLFxuICAgICAgICAgICAgICAgIGVycm9yOiByZWdpc3RlckVycm9yQ2FsbGJhY2soc3VibWl0KVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vSGlkZSBlcnJvcnMgb24gaW5wdXQgZmllbGRzIHdoZW4gaXQgaXMgbW9kaWZpZWRcbiAgICAgICAgJChcIltuYW1lPSdyZWdpc3RlciddIGlucHV0XCIpLm9uKCdrZXlwcmVzcycsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICQodGhpcykuc2libGluZ3MoXCIuZXJyb3JcIikuaGlkZSgpO1xuICAgICAgICAgICAgJCh0aGlzKS5zaWJsaW5ncygpLmZpbmQoXCIuZXJyb3JcIikuaGlkZSgpO1xuICAgICAgICAgICAgJCh0aGlzKS5yZW1vdmVDbGFzcyhcImVycm9yXCIpO1xuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVHJhY2tzIHRoZSBVVE0gdHJhY2tpbmcgd2l0aCBjb29raWVzLlxuICAgICAqL1xuICAgIHZhciBzZXR1cFVUTVRyYWNraW5nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2Y
1017XIgcGFyYW1zID0gd2luZG93LmxvY2F0aW9uLnNlYXJjaC5yZXBsYWNlKCc/JywgJycpLnNwbGl0KCcmJyk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBwYXJhbXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwYXJhbVBhcnRzID0gcGFyYW1zW2ldLnNwbGl0KCc9Jyk7XG5cbiAgICAgICAgICAgIGlmIChVVE1fUEFSQU1TLmluZGV4T2YocGFyYW1QYXJ0c1swXSkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgc2V0Q29va2llKHBhcmFtUGFydHNbMF0sIHBhcmFtUGFydHNbMV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIHNldHVwKCk7XG4gICAgc2V0dXBVVE1UcmFja2luZygpO1xuXG4gICAgLy8gY29uZmlndXJlIG1vZGFsIHJlZ2lzdGVyIChpZiBleGlzdHMpXG4gICAgaWYgKCQuZm4ubW9kYWwpXG4gICAgICAgICQoJy5tb2RhbCcpLm1vZGFsKHtcbiAgICAgICAgICAgIG9wYWNpdHk6IC43LFxuICAgICAgICAgICAgZGlzbWlzc2libGU6IHRydWUsXG4gICAgICAgICAgICBpbl9kdXJhdGlvbjogMzAwLFxuICAgICAgICAgICAgb3V0X2R1cmF0aW9uOiAyMDAsXG4gICAgICAgICAgICBzdGFydGluZ190b3A6ICc0JScsXG4gICAgICAgICAgICBlbmRpbmdfdG9wOiAnMTAlJ1xuICAgICAgICB9KTtcbn0pOyIsIi8qKlxuICogRXh0ZW5kIGpxdWVyeSB3aXRoIGEgc2Nyb2xsc3B5IHBsdWdpbi5cbiAqIFRoaXMgd2F0Y2hlcyB0aGUgd2luZG93IHNjcm9sbCBhbmQgZmlyZXMgZXZlbnRzIHdoZW4gZWxlbWVudHMgYXJlIHNjcm9sbGVkIGludG8gdmlld3BvcnQuXG4gKlxuICogdGhyb3R0bGUoKSBhbmQgZ2V0VGltZSgpIHRha2VuIGZyb20gVW5kZXJzY29yZS5qc1xuICogaHR0cHM6Ly9naXRodWIuY29tL2phc2hrZW5hcy91bmRlcnNjb3JlXG4gKlxuICogQGF1dGhvciBDb3B5cmlnaHQgMjAxMyBKb2huIFNtYXJ0XG4gKiBAbGljZW5zZSBodHRwczovL3Jhdy5naXRodWIuY29tL3RoZXNtYXJ0L2pxdWVyeS1zY3JvbGxzcHkvbWFzdGVyL0xJQ0VOU0VcbiAqIEBzZWUgaHR0cHM6Ly9naXRodWIuY29tL3RoZXNtYXJ0XG4gKiBAdmVyc2lvbiAwLjEuMlxuICpcbiAqIEpTIEFEREVEIFRPIEVYVEVORFMgVEhFIFNDUk9MTFNQWSBQTFVHSU4gV0lUSCBBIE5FVyBPRkZTRVQgVE8gRklYIFRIRSBDQUxMSU5HIFNFQ1RJT04gKEFOQ0hPUikgVE8gVE9QIE9GIFRIRSBXSU5ET1csIE5PVCBJTiBUSEUgTUlERExFIE9ORS5cbiAqL1xuKGZ1bmN0aW9uKCQpIHtcblxuXHR2YXIgaldpbmRvdyA9ICQod2luZG93KTtcblx0dmFyIGVsZW1lbnRzID0gW107XG5cdHZhciBlbGVtZW50c0luVmlldyA9IFtdO1xuXHR2YXIgaXNTcHlpbmcgPSBmYWxzZTtcblx0dmFyIHRpY2tzID0gMDtcblx0dmFyIHVuaXF1ZV9pZCA9IDE7XG5cdHZhciBvZmZzZXQgPSB7XG5cdFx0dG9wIDogMCxcblx0XHRyaWdodCA6IDAsXG5cdFx0Ym90dG9tIDogMCxcblx0XHRsZWZ0IDogMCxcblx0fVxuXG5cdC8qKlxuXHQgKiBGaW5kIGVsZW1lbnRzIHRoYXQgYXJlIHdpdGhpbiB0aGUgYm91bmRhcnlcblx0ICogQHBhcmFtIHtudW1iZXJ9IHRvcFxuXHQgKiBAcGFyYW0ge251bWJlcn0gcmlnaHRcblx0ICogQHBhcmFtIHtudW1iZXJ9IGJvdHRvbVxuXHQgKiBAcGFyYW0ge251bWJlcn0gbGVmdFxuXHQgKiBAcmV0dXJuIHtqUXVlcnl9XHRcdEEgY29sbGVjdGlvbiBvZiBlbGVtZW50c1xuXHQgKi9cblx0ZnVuY3Rpb24gZmluZEVsZW1lbnRzKHRvcCwgcmlnaHQsIGJvdHRvbSwgbGVmdCkge1xuXHRcdHZhciBoaXRzID0gJCgpO1xuXHRcdCQuZWFjaChlbGVtZW50cywgZnVuY3Rpb24oaSwgZWxlbWVudCkge1xuXHRcdFx0aWYgKGVsZW1lbnQuaGVpZ2h0KCkgPiAwKSB7XG5cdFx0XHRcdHZhciBlbFRvcCA9IGVsZW1lbnQub2Zmc2V0KCkudG9wLFxuXHRcdFx0XHRcdGVsTGVmdCA9IGVsZW1lbnQub2Zmc2V0KCkubGVmdCxcblx0XHRcdFx0XHRlbFJpZ2h0ID0gZWxMZWZ0ICsgZWxlbWVudC53aWR0aCgpLFxuXHRcdFx0XHRcdGVsQm90dG9tID0gZWxUb3AgKyBlbGVtZW50LmhlaWdodCgpO1xuXG5cdFx0XHRcdHZhciBpc0ludGVyc2VjdCA9ICEoZWxMZWZ0ID4gcmlnaHQgfHxcblx0XHRcdFx0XHRlbFJpZ2h0IDwgbGVmdCB8fFxuXHRcdFx0XHRcdGVsVG9wID4gYm90dG9tIHx8XG5cdFx0XHRcdFx0ZWxCb3R0b20gPCB0b3ApO1xuXG5cdFx0XHRcdGlmIChpc0ludGVyc2VjdCkge1xuXHRcdFx0XHRcdGhpdHMucHVzaChlbGVtZW50KTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXG5cdFx0cmV0dXJuIGhpdHM7XG5cdH1cblxuXG5cdC8qKlxuXHQgKiBDYWxsZWQgd2hlbiB0aGUgdXNlciBzY3JvbGxzIHRoZSB3aW5kb3dcblx0ICovXG5cdGZ1bmN0aW9uIG9uU2Nyb2xsKCkge1xuXHRcdC8vIHVuaXF1ZSB0aWNrIGlkXG5cdFx0Kyt0aWNrcztcblxuXHRcdC8vIHZpZXdwb3J0IHJlY3RhbmdsZVxuXHRcdHZhciB0b3AgPSBqV2luZG93LnNjcm9sbFRvcCgpLFxuXHRcdFx0bGVmdCA9IGpXaW5kb3cuc2Nyb2xsTGVmdCgpLFxuXHRcdFx0cmlnaHQgPSBsZWZ0ICsgaldpbmRvdy53aWR0aCgpLFxuXHRcdFx0Ym90dG9tID0gdG9wICsgaldpbmRvdy5oZWlnaHQoKTtcblxuXHRcdC8vIGRldGVybWluZSB3aGljaCBlbGVtZW50cyBhcmUgaW4gdmlld1xuLy8gICAgICAgICsgNjAgYWNjb3VudHMgZm9yIGZpeGVkIG5hdlxuXHRcdHZhciBpbnRlcnNlY3Rpb25zID0gZmluZEVsZW1lbnRzKHRvcCtvZmZzZXQudG9wICsgMjAwLCByaWdodCtvZmZzZXQucmlnaHQsIGJvdHRvbStvZmZzZXQuYm90dG9tLCBsZWZ0K29mZnNldC5sZWZ0KTtcblx0XHQkLmVhY2goaW50ZXJzZWN0aW9ucywgZnVuY3Rpb24oaSwgZWxlbWVudCkge1xuXG5cdFx0XHR2YXIgbGFzdFRpY2sgPSBlbGVtZW50LmRhdGEoJ3Njcm9sbFNweTp0aWNrcycpO1xuXHRcdFx0aWYgKHR5cGVvZiBsYXN0VGljayAhPSAnbnVtYmVyJykge1xuXHRcdFx0XHQvLyBlbnRlcmVkIGludG8gdmlld1xuXHRcdFx0XHRlbGVtZW50LnRyaWdnZXJIYW5kbGVyKCdzY3JvbGxTcHk6ZW50ZXInKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gdXBkYXRlIHRpY2sgaWRcblx0XHRcdGVsZW1lbnQuZGF0YSgnc2Nyb2xsU3B5OnRpY2tzJywgdGlja3MpO1xuXHRcdH0pO1xuXG5cdFx0Ly8gZGV0ZXJtaW5lIHdoaWNoIGVsZW1lbnRzIGFyZSBubyBsb25nZXIgaW4gdmlld1xuXHRcdCQuZWFjaChlbGVtZW50c0luVmlldywgZnVuY3Rpb24oaSwgZWxlbWVudCkge1xuXHRcdFx0dmFyIGxhc3RUaWNrID0gZWxlbWVudC5kYXRhKCdzY3JvbGxTcHk6dGlja3MnKTtcblx0XHRcdGlmICh0eXBlb2YgbGFzdFRpY2sgPT0gJ251bWJlcicgJiYgbGFzdFRpY2sgIT09IHRpY2tzKSB7XG5cdFx0XHRcdC8vIGV4aXRlZCBmcm9tIHZpZXdcblx0XHRcdFx0ZWxlbWVudC50cmlnZ2VySGFuZGxlcignc2Nyb2xsU3B5OmV4aXQnKTtcblx0XHRcdFx0ZWxlbWVudC5kYXRhKCdzY3JvbGxTcHk6dGlja3MnLCBudWxsKTtcblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdC8vIHJlbWVtYmVyIGVsZW1lbnRzIGluIHZpZXcgZm9yIG5leHQgdGlja1xuXHRcdGVsZW1lbnRzSW5WaWV3ID0gaW50ZXJzZWN0aW9ucztcblx0fVxuXG5cdC8qKlxuXHQgKiBDYWxsZWQgd2hlbiB3aW5kb3cgaXMgcmVzaXplZFxuXHQqL1xuXHRmdW5jdGlvbiBvbldpblNpemUoKSB7XG5cdFx0aldpbmRvdy50cmlnZ2VyKCdzY3JvbGxTcHk6d2luU2l6ZScpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldCB0aW1lIGluIG1zXG4gICAqIEBsaWNlbnNlIGh0dHBzOi8vcmF3LmdpdGh1Yi5jb20vamFzaGtlbmFzL3VuZGVyc2NvcmUvbWFzdGVyL0xJQ0VOU0Vcblx0ICogQHR5cGUge2Z1bmN0aW9ufVxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHR2YXIgZ2V0VGltZSA9IChEYXRlLm5vdyB8fCBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuXHR9KTtcblxuXHQvKipcblx0ICogUmV0dXJucyBhIGZ1bmN0aW9uLCB0aGF0LCB3aGVuIGludm9rZWQsIHdpbGwgb25seSBiZSB0cmlnZ2VyZWQgYXQgbW9zdCBvbmNlXG5cdCAqIGR1cmluZyBhIGdpdmVuIHdpbmRvdyBvZiB0aW1lLiBOb3JtYWxseSwgdGhlIHRocm90dGxlZCBmdW5jdGlvbiB3aWxsIHJ1blxuXHQgKiBhcyBtdWNoIGFzIGl0IGNhbiwgd2l0aG91dCBldmVyIGdvaW5nIG1vcmUgdGhhbiBvbmNlIHBlciBgd2FpdGAgZHVyYXRpb247XG5cdCAqIGJ1dCBpZiB5b3UnZCBsaWtlIHRvIGRpc2FibGUgdGhlIGV4ZWN1dGlvbiBvbiB0aGUgbGVhZGluZyBlZGdlLCBwYXNzXG5cdCAqIGB7bGVhZGluZzogZmFsc2V9YC4gVG8gZGlzYWJsZSBleGVjdXRpb24gb24gdGhlIHRyYWlsaW5nIGVkZ2UsIGRpdHRvLlxuXHQgKiBAbGljZW5zZSBodHRwczovL3Jhdy5naXRodWIuY29tL2phc2hrZW5hcy91bmRlcnNjb3JlL21hc3Rlci9MSUNFTlNFXG5cdCAqIEBwYXJhbSB7ZnVuY3Rpb259IGZ1bmNcblx0ICogQHBhcmFtIHtudW1iZXJ9IHdhaXRcblx0ICogQHBhcmFtIHtPYmplY3Q9fSBvcHRpb25zXG5cdCAqIEByZXR1cm5zIHtGdW5jdGlvbn1cblx0ICovXG5cdGZ1bmN0aW9uIHRocm90dGxlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcblx0XHR2YXIgY29udGV4dCwgYXJncywgcmVzdWx0O1xuXHRcdHZhciB0aW1lb3V0ID0gbnVsbDtcblx0XHR2YXIgcHJldmlvdXMgPSAwO1xuXHRcdG9wdGlvbnMgfHwgKG9wdGlvbnMgPSB7fSk7XG5cdFx0dmFyIGxhdGVyID0gZnVuY3Rpb24gKCkge1xuXHRcdFx0cHJldmlvdXMgPSBvcHRpb25zLmxlYWRpbmcgPT09IGZhbHNlID8gMCA6IGdldFRpbWUoKTtcblx0XHRcdHRpbWVvdXQgP
1017SBudWxsO1xuXHRcdFx0cmVzdWx0ID0gZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcblx0XHRcdGNvbnRleHQgPSBhcmdzID0gbnVsbDtcblx0XHR9O1xuXHRcdHJldHVybiBmdW5jdGlvbiAoKSB7XG5cdFx0XHR2YXIgbm93ID0gZ2V0VGltZSgpO1xuXHRcdFx0aWYgKCFwcmV2aW91cyAmJiBvcHRpb25zLmxlYWRpbmcgPT09IGZhbHNlKSBwcmV2aW91cyA9IG5vdztcblx0XHRcdHZhciByZW1haW5pbmcgPSB3YWl0IC0gKG5vdyAtIHByZXZpb3VzKTtcblx0XHRcdGNvbnRleHQgPSB0aGlzO1xuXHRcdFx0YXJncyA9IGFyZ3VtZW50cztcblx0XHRcdGlmIChyZW1haW5pbmcgPD0gMCkge1xuXHRcdFx0XHRjbGVhclRpbWVvdXQodGltZW91dCk7XG5cdFx0XHRcdHRpbWVvdXQgPSBudWxsO1xuXHRcdFx0XHRwcmV2aW91cyA9IG5vdztcblx0XHRcdFx0cmVzdWx0ID0gZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcblx0XHRcdFx0Y29udGV4dCA9IGFyZ3MgPSBudWxsO1xuXHRcdFx0fSBlbHNlIGlmICghdGltZW91dCAmJiBvcHRpb25zLnRyYWlsaW5nICE9PSBmYWxzZSkge1xuXHRcdFx0XHR0aW1lb3V0ID0gc2V0VGltZW91dChsYXRlciwgcmVtYWluaW5nKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiByZXN1bHQ7XG5cdFx0fTtcblx0fTtcblxuXHQvKipcblx0ICogRW5hYmxlcyBTY3JvbGxTcHkgdXNpbmcgYSBzZWxlY3RvclxuXHQgKiBAcGFyYW0ge2pRdWVyeXxzdHJpbmd9IHNlbGVjdG9yICBUaGUgZWxlbWVudHMgY29sbGVjdGlvbiwgb3IgYSBzZWxlY3RvclxuXHQgKiBAcGFyYW0ge09iamVjdD19IG9wdGlvbnNcdE9wdGlvbmFsLlxuICAgICAgICB0aHJvdHRsZSA6IG51bWJlciAtPiBzY3JvbGxzcHkgdGhyb3R0bGluZy4gRGVmYXVsdDogMTAwIG1zXG4gICAgICAgIG9mZnNldFRvcCA6IG51bWJlciAtPiBvZmZzZXQgZnJvbSB0b3AuIERlZmF1bHQ6IDBcbiAgICAgICAgb2Zmc2V0UmlnaHQgOiBudW1iZXIgLT4gb2Zmc2V0IGZyb20gcmlnaHQuIERlZmF1bHQ6IDBcbiAgICAgICAgb2Zmc2V0Qm90dG9tIDogbnVtYmVyIC0+IG9mZnNldCBmcm9tIGJvdHRvbS4gRGVmYXVsdDogMFxuICAgICAgICBvZmZzZXRMZWZ0IDogbnVtYmVyIC0+IG9mZnNldCBmcm9tIGxlZnQuIERlZmF1bHQ6IDBcblx0ICogQHJldHVybnMge2pRdWVyeX1cblx0ICovXG5cdCQuc2Nyb2xsU3B5ID0gZnVuY3Rpb24oc2VsZWN0b3IsIG9wdGlvbnMpIHtcblx0XHR2YXIgdmlzaWJsZSA9IFtdO1xuXHRcdHNlbGVjdG9yID0gJChzZWxlY3Rvcik7XG5cdFx0c2VsZWN0b3IuZWFjaChmdW5jdGlvbihpLCBlbGVtZW50KSB7XG5cdFx0XHRlbGVtZW50cy5wdXNoKCQoZWxlbWVudCkpO1xuXHRcdFx0JChlbGVtZW50KS5kYXRhKFwic2Nyb2xsU3B5OmlkXCIsIGkpO1xuXHRcdFx0Ly8gU21vb3RoIHNjcm9sbCB0byBzZWN0aW9uXG5cdFx0ICAkKCdhW2hyZWY9XCIjJyArICQoZWxlbWVudCkuYXR0cignaWQnKSArICdcIl0nKS5jbGljayhmdW5jdGlvbihlKSB7XG5cdFx0ICAgIGUucHJldmVudERlZmF1bHQoKTtcblx0XHQgICAgdmFyIG9mZnNldCA9ICQodGhpcy5oYXNoKS5vZmZzZXQoKS50b3AgKyAxO1xuXG4vLyAgICAgICAgICBvZmZzZXQgLSAyMDAgYWxsb3dzIGVsZW1lbnRzIG5lYXIgYm90dG9tIG9mIHBhZ2UgdG8gc2Nyb2xsXG5cblx0ICAgIFx0JCgnaHRtbCwgYm9keScpLmFuaW1hdGUoeyBzY3JvbGxUb3A6IG9mZnNldCAtIDIwIH0sIHtkdXJhdGlvbjogNDAwLCBxdWV1ZTogZmFsc2UsIGVhc2luZzogJ2Vhc2VPdXRDdWJpYyd9KTtcblxuXHRcdCAgfSk7XG5cdFx0fSk7XG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge1xuXHRcdFx0dGhyb3R0bGU6IDEwMFxuXHRcdH07XG5cblx0XHRvZmZzZXQudG9wID0gb3B0aW9ucy5vZmZzZXRUb3AgfHwgMDtcblx0XHRvZmZzZXQucmlnaHQgPSBvcHRpb25zLm9mZnNldFJpZ2h0IHx8IDA7XG5cdFx0b2Zmc2V0LmJvdHRvbSA9IG9wdGlvbnMub2Zmc2V0Qm90dG9tIHx8IDA7XG5cdFx0b2Zmc2V0LmxlZnQgPSBvcHRpb25zLm9mZnNldExlZnQgfHwgMDtcblxuXHRcdHZhciB0aHJvdHRsZWRTY3JvbGwgPSB0aHJvdHRsZShvblNjcm9sbCwgb3B0aW9ucy50aHJvdHRsZSB8fCAxMDApO1xuXHRcdHZhciByZWFkeVNjcm9sbCA9IGZ1bmN0aW9uKCl7XG5cdFx0XHQkKGRvY3VtZW50KS5yZWFkeSh0aHJvdHRsZWRTY3JvbGwpO1xuXHRcdH07XG5cblx0XHRpZiAoIWlzU3B5aW5nKSB7XG5cdFx0XHRqV2luZG93Lm9uKCdzY3JvbGwnLCByZWFkeVNjcm9sbCk7XG5cdFx0XHRqV2luZG93Lm9uKCdyZXNpemUnLCByZWFkeVNjcm9sbCk7XG5cdFx0XHRpc1NweWluZyA9IHRydWU7XG5cdFx0fVxuXG5cdFx0Ly8gcGVyZm9ybSBhIHNjYW4gb25jZSwgYWZ0ZXIgY3VycmVudCBleGVjdXRpb24gY29udGV4dCwgYW5kIGFmdGVyIGRvbSBpcyByZWFkeVxuXHRcdHNldFRpbWVvdXQocmVhZHlTY3JvbGwsIDApO1xuXG5cblx0XHRzZWxlY3Rvci5vbignc2Nyb2xsU3B5OmVudGVyJywgZnVuY3Rpb24oKSB7XG5cdFx0XHR2aXNpYmxlID0gJC5ncmVwKHZpc2libGUsIGZ1bmN0aW9uKHZhbHVlKSB7XG5cdCAgICAgIHJldHVybiB2YWx1ZS5oZWlnaHQoKSAhPSAwO1xuXHQgICAgfSk7XG5cblx0XHRcdHZhciAkdGhpcyA9ICQodGhpcyk7XG5cblx0XHRcdGlmICh2aXNpYmxlWzBdKSB7XG5cdFx0XHRcdCQoJ2FbaHJlZj1cIiMnICsgdmlzaWJsZVswXS5hdHRyKCdpZCcpICsgJ1wiXScpLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcblx0XHRcdFx0aWYgKCR0aGlzLmRhdGEoJ3Njcm9sbFNweTppZCcpIDwgdmlzaWJsZVswXS5kYXRhKCdzY3JvbGxTcHk6aWQnKSkge1xuXHRcdFx0XHRcdHZpc2libGUudW5zaGlmdCgkKHRoaXMpKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHR2aXNpYmxlLnB1c2goJCh0aGlzKSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHR2aXNpYmxlLnB1c2goJCh0aGlzKSk7XG5cdFx0XHR9XG5cblxuXHRcdFx0JCgnYVtocmVmPVwiIycgKyB2aXNpYmxlWzBdLmF0dHIoJ2lkJykgKyAnXCJdJykuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuXHRcdH0pO1xuXHRcdHNlbGVjdG9yLm9uKCdzY3JvbGxTcHk6ZXhpdCcsIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmlzaWJsZSA9ICQuZ3JlcCh2aXNpYmxlLCBmdW5jdGlvbih2YWx1ZSkge1xuXHQgICAgICByZXR1cm4gdmFsdWUuaGVpZ2h0KCkgIT0gMDtcblx0ICAgIH0pO1xuXG5cdFx0XHRpZiAodmlzaWJsZVswXSkge1xuXHRcdFx0XHQkKCdhW2hyZWY9XCIjJyArIHZpc2libGVbMF0uYXR0cignaWQnKSArICdcIl0nKS5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG5cdFx0XHRcdHZhciAkdGhpcyA9ICQodGhpcyk7XG5cdFx0XHRcdHZpc2libGUgPSAkLmdyZXAodmlzaWJsZSwgZnVuY3Rpb24odmFsdWUpIHtcblx0ICAgICAgICByZXR1cm4gdmFsdWUuYXR0cignaWQnKSAhPSAkdGhpcy5hdHRyKCdpZCcpO1xuXHQgICAgICB9KTtcblx0ICAgICAgaWYgKHZpc2libGVbMF0pIHsgLy8gQ2hlY2sgaWYgZW1wdHlcblx0XHRcdFx0XHQkKCdhW2hyZWY9XCIjJyArIHZpc2libGVbMF0uYXR0cignaWQnKSArICdcIl0nKS5hZGRDbGFzcygnYWN0aXZlJyk7XG5cdCAgICAgI
1017H1cblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdHJldHVybiBzZWxlY3Rvcjtcblx0fTtcblxuXHQvKipcblx0ICogTGlzdGVuIGZvciB3aW5kb3cgcmVzaXplIGV2ZW50c1xuXHQgKiBAcGFyYW0ge09iamVjdD19IG9wdGlvbnNcdFx0XHRcdFx0XHRPcHRpb25hbC4gU2V0IHsgdGhyb3R0bGU6IG51bWJlciB9IHRvIGNoYW5nZSB0aHJvdHRsaW5nLiBEZWZhdWx0OiAxMDAgbXNcblx0ICogQHJldHVybnMge2pRdWVyeX1cdFx0JCh3aW5kb3cpXG5cdCAqL1xuXHQkLndpblNpemVTcHkgPSBmdW5jdGlvbihvcHRpb25zKSB7XG5cdFx0JC53aW5TaXplU3B5ID0gZnVuY3Rpb24oKSB7IHJldHVybiBqV2luZG93OyB9OyAvLyBsb2NrIGZyb20gbXVsdGlwbGUgY2FsbHNcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7XG5cdFx0XHR0aHJvdHRsZTogMTAwXG5cdFx0fTtcblx0XHRyZXR1cm4galdpbmRvdy5vbigncmVzaXplJywgdGhyb3R0bGUob25XaW5TaXplLCBvcHRpb25zLnRocm90dGxlIHx8IDEwMCkpO1xuXHR9O1xuXG5cdC8qKlxuXHQgKiBFbmFibGVzIFNjcm9sbFNweSBvbiBhIGNvbGxlY3Rpb24gb2YgZWxlbWVudHNcblx0ICogZS5nLiAkKCcuc2Nyb2xsU3B5Jykuc2Nyb2xsU3B5KClcblx0ICogQHBhcmFtIHtPYmplY3Q9fSBvcHRpb25zXHRPcHRpb25hbC5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHR0aHJvdHRsZSA6IG51bWJlciAtPiBzY3JvbGxzcHkgdGhyb3R0bGluZy4gRGVmYXVsdDogMTAwIG1zXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0b2Zmc2V0VG9wIDogbnVtYmVyIC0+IG9mZnNldCBmcm9tIHRvcC4gRGVmYXVsdDogMFxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdG9mZnNldFJpZ2h0IDogbnVtYmVyIC0+IG9mZnNldCBmcm9tIHJpZ2h0LiBEZWZhdWx0OiAwXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0b2Zmc2V0Qm90dG9tIDogbnVtYmVyIC0+IG9mZnNldCBmcm9tIGJvdHRvbS4gRGVmYXVsdDogMFxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdG9mZnNldExlZnQgOiBudW1iZXIgLT4gb2Zmc2V0IGZyb20gbGVmdC4gRGVmYXVsdDogMFxuXHQgKiBAcmV0dXJucyB7alF1ZXJ5fVxuXHQgKi9cblx0JC5mbi5zY3JvbGxTcHkgPSBmdW5jdGlvbihvcHRpb25zKSB7XG5cdFx0cmV0dXJuICQuc2Nyb2xsU3B5KCQodGhpcyksIG9wdGlvbnMpO1xuXHR9O1xuXG59KShqUXVlcnkpOyIsIid1c2Ugc3RyaWN0Jztcbi8qKlxuICogUmVnaXN0ZXIgb2JzZXJ2ZXJzOlxuICogVG9waWMub24oXCJzdWNjZXNzXCIsIGZuMSk7XG4gKiBUb3BpYy5vbihcInN1Y2Nlc3NcIiwgZm4yKTtcbiAqIExhdW5jaCB0aGUgZXZlbnQ6XG4gKiBUb3BpYy5maXJlKFwic3VjY2Vzc1wiKTtcbiAqXG4gKiBUaGVuIGZuMSBhbmQgZm4yIGFyZSBleGVjdXRlZFxuICogQHR5cGUge3ssIGZpcmU6IGZpcmUsIG9uOiBvbn19XG4gKi9cbnZhciBUb3BpYyA9IGZ1bmN0aW9uKCQpe1xuICAgIHJldHVybiB7XG4gICAgICAgIF9vYnNlcnZlcnM6IHt9LFxuXG4gICAgICAgIGZpcmU6IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgdmFyIG9ic2VydmVycyA9IHRoaXMuX29ic2VydmVyc1tldmVudF07XG5cdFx0XHRpZiAob2JzZXJ2ZXJzKXtcblx0XHRcdFx0JC5lYWNoKG9ic2VydmVycywgZnVuY3Rpb24gKGluZGV4LCBvYnNlcnZlcikge1xuXHRcdFx0XHRcdG9ic2VydmVyKCk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuICAgICAgICB9LFxuXG4gICAgICAgIG9uOiBmdW5jdGlvbiAoZXZlbnQsIGZuKSB7XG4gICAgICAgICAgICB2Y
1017XIgb2JzZXJ2ZXJzID0gdGhpcy5fb2JzZXJ2ZXJzW2V2ZW50XTtcbiAgICAgICAgICAgIGlmIChvYnNlcnZlcnMpIHtcbiAgICAgICAgICAgICAgICBvYnNlcnZlcnMucHVzaChmbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9vYnNlcnZlcnNbZXZlbnRdID0gW2ZuXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbn0oJCk7IiwiLyoqXG4gKiBTZXQgY3VycmVudCB5ZWFyIG5lYXIgdG8gY29weXJpZ2h0IGluIEZvb3RlciBjb21tb24gZWxlbWVudFxuICpcbiAqL1xuJChmdW5jdGlvbiAoKSB7XG4gICAgJChcIiNqcy1jdXJyZW50LXllYXJcIikuaHRtbChuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCkpO1xufSk7IiwiZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcIkRPTUNvbnRlbnRMb2FkZWRcIixcbiAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBkaXYsIG4sXG4gICAgICAgICAgICB2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShcInlvdXR1YmUtcGxheWVyXCIpO1xuICAgICAgICBmb3IgKG4gPSAwOyBuIDwgdi5sZW5ndGg7IG4rKykge1xuICAgICAgICAgICAgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICAgICAgICAgIGRpdi5zZXRBdHRyaWJ1dGUoXCJkYXRhLWlkXCIsIHZbbl0uZGF0YXNldC5pZCk7XG4gICAgICAgICAgICBkaXYuaW5uZXJIVE1MID0gbGFibm9sVGh1bWIodltuXS5kYXRhc2V0LmlkKTtcbiAgICAgICAgICAgIGRpdi5vbmNsaWNrID0gbGFibm9sSWZyYW1lO1xuICAgICAgICAgICAgdltuXS5hcHBlbmRDaGlsZChkaXYpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbmZ1bmN0aW9uIGxhYm5vbFRodW1iKGlkKSB7XG4gICAgdmFyIHRodW1iID0gJzxpbWcgc3JjPVwiaHR0cHM6Ly9pLnl0aW1nLmNvbS92aS9JRC9ocWRlZmF1bHQuanBnXCI+JyxcbiAgICAgICAgcGxheSA9ICc8ZGl2IGNsYXNzPVwicGxheVwiPjwvZGl2Pic7XG4gICAgcmV0dXJuIHRodW1iLnJlcGxhY2UoXCJJRFwiLCBpZCkgKyBwbGF5O1xufVxuXG5mdW5jdGlvbiBsYWJub2xJZnJhbWUoKSB7XG4gICAgdmFyIGlmcmFtZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJpZnJhbWVcIik7XG4gICAgdmFyIGVtYmVkID0gXCJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9JRD9hdXRvcGxheT0xXCI7XG4gICAgaWZyYW1lLnNldEF0dHJpYnV0ZShcInNyY1wiLCBlbWJlZC5yZXBsYWNlKFwiSURcIiwgdGhpcy5kYXRhc2V0LmlkKSk7XG4gICAgaWZyYW1lLnNldEF0dHJpYnV0ZShcImZyYW1lYm9yZGVyXCIsIFwiMFwiKTtcbiAgICBpZnJhbWUuc2V0QXR0cmlidXRlKFwiYWxsb3dmdWxsc2NyZWVuXCIsIFwiMVwiKTtcbiAgICB0aGlzLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGlmcmFtZSwgdGhpcyk7XG59Il19

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.