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.