vendor: 91 bytes, lines 1-4
1/******/ (() => { // webpackBootstrap 2/******/ var __webpack_modules__ = ({ 3 4/***/ "./src/
4index.js": 5/*!**********************!* 6 !*** ./src/index.js ***! 7 \**********************/ 8/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 9 10"use strict"; 11__webpack_require__.r(__webpack_exports__); 12/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! leaflet */ "./node_modules/leaflet/dist/leaflet-src.js"); 13/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(leaflet__WEBPACK_IMPORTED_MODULE_0__); 14 15 16__webpack_require__(/*! leaflet-fullscreen */ "./node_modules/leaflet-fullscreen/dist/Leaflet.fullscreen.js"); 17 18var dayjs = __webpack_require__(/*! dayjs */ "./node_modules/dayjs/dayjs.min.js"); 19 20var relativeTime = __webpack_require__(/*! dayjs/plugin/relativeTime */ "./node_modules/dayjs/plugin/relativeTime.js"); 21 22var dayjs_es = __webpack_require__(/*! dayjs/locale/es */ "./node_modules/dayjs/locale/es.js"); 23 24dayjs.extend(relativeTime); 25const current_lang = document.querySelector('html').lang.split('-')[0]; 26dayjs.locale(current_lang); 27/** 28 * Public Space Tools map 29 */ 30 31/** 32 * Map settings 33 */ 34 35let map; // inits in DOMContentLoaded, see bottom 36 37const map_settings = { 38 minZoom: 4, 39 fullscreenControl: true, 40 scrollWheelZoom: false 41}; 42let map_center = { 43 // This one is a variable because we'll reuse it to remind map centers 44 latlon: [38, 18], 45 zoom: 5 46}; 47const map_layer = { 48 tiles: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 49 attr: '<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' 50}; // This dictionary holds categories, terms, and whether they're selected 51// by the user using the legend 52 53var categories = { 54 'category': { 55 'selected': 6, 56 'terms': { 57 'a': true, 58 'b': true, 59 'c': true, 60 'd': true, 61 'e': true, 62 'f': true 63 } 64 }, 65 'theme': { 66 'selected': 9, 67 'terms': { 68 'a': true, 69 'b': true, 70 'c': true, 71 'd': true, 72 'e': true, 73 'f': true, 74 'g': true, 75 'h': true, 76 'i': true 77 } 78 } 79}; 80var current_post = null; // Elements 81 82let filtrable; // Includes map and timeline elements. Inits in DOMContentLoaded, see bottom 83 84let markers; // inits in DOMContentLoaded, see bottom 85 86let list; // inits in DOMContentLoaded, see bottom 87 88/** 89 * Unselects all items 90 */ 91 92const unselect_all = function () { 93 filtrable.forEach(function (i) { 94 i.classList.remove('hidden'); 95 }); 96 markers.forEach(function (i) { 97 i.classList.remove('unselected'); 98 }); 99}; 100/** 101 * Inits main map 102 */ 103 104 105const init_main_map = function () { 106 map = leaflet__WEBPACK_IMPORTED_MODULE_0___default().map('main-map', map_settings).on('click', () => { 107 unselect_all(); 108 }).setView(map_center.latlon, map_center.zoom); 109 leaflet__WEBPACK_IMPORTED_MODULE_0___default().tileLayer(map_layer.tiles, { 110 attribution: map_layer.attribution 111 }).addTo(map); 112}; 113/** 114 * Get cases. Create cases list items and markers. 115 */ 116 117 118const fetch_posts = function () { 119 fetch('/wp-json/pst-cases/v1/all/' + lang).then(data => data.json()).then(posts => { 120 posts.forEach(function (i) { 121 var template_node = document.querySelector('#case-template'); 122 var template = document.importNode(template_node.content, true); // Populate timeline 123 124 const post = template.querySelector('.case'); 125 post.id = 'case-' + i.id; 126 post.classList.add('category--' + i.cat.value); 127 post.classList.add('theme--' + i.thm.value); 128 post.classList.add('case--' + i.id); 129 post.dataset.cat = i.cat.value; 130 let img = template.querySelector('.case__image'); 131 132 if (i.img) { 133 img.setAttribute('src', i.img); 134 } else { 135 img.remove(); 136 } 137 138 template.querySelector('.case__tags-type').innerHTML = i.cat.label; 139 template.querySelector('.case__tags-theme').innerHTML = i.thm.label; 140 template.querySelector('.case__title').innerHTML = i.tit; 141 template.querySelector('.case__metadata').innerHTML = dayjs(i.dat * 1000).fromNow(); 142 template.querySelector('.case').addEventListener('click', function () { 143 location.hash = i.id; 144 load_post(i.id); 145 }); 146 list.appendChild(template); // Populate map 147 148 if (i.pos.lat && i.pos.lng) { 149 leaflet__WEBPACK_IMPORTED_MODULE_0___default().marker([i.pos.lat, i.pos.lng], { 150 icon: leaflet__WEBPACK_IMPORTED_MODULE_0___default().divIcon({ 151 type: 'div', 152 iconSize: [40, 40], 153 iconAnchor: [20, 50], 154 popupAnchor: [0, -30], 155 layer: 'hidden', 156 className: `item-filtrable case--${i.id} category--${i.cat.value} theme--${i.thm.value}`, 157 html: `<img class="icon" src="/wp-content/themes/pst/img/icons--themes/${i.thm.value}.svg" />` 158 }) 159 }).on('click', function (e) { 160 load_post(i.id); 161 document.querySelectorAll('.leaflet-marker-icon').forEach(function (i) { 162 i.classList.add('unselected'); 163 }); 164 165 e.target._icon.classList.remove('unselected'); 166 }).addTo(map); 167 } 168 169 ; 170 }); // Get current hash if any and select related post 171 172 let id = window.location.hash; 173 174 if (id) { 175 id = id.substring(1, id.length); 176 if (document.querySelector(`#case-${id}`)) load_post(id); 177 } 178 }); 179}; 180/** 181 * Shows selected case 182 */ 183 184 185const show_case = function (id) { 186 document.querySelectorAll(`.item-filtrable:not(.case--${id})`).forEach(function (i) { 187 i.classList.add('hidden'); 188 }); 189}; 190/** 191 * Load post 192 */ 193 194 195function load_post(id) { 196 const cat = document.querySelector(`#case-${id}`).dataset.cat; 197 show_case(id); 198 current_post = id; 199 fetch('/wp-json/pst-cases/v1/id=' + id).then(data => data.json()).then(post => { 200 document.body.classList.add('showing-post'); 201 202 if (post.pos.lat && post.pos.lng) { 203 map_center.latlon = map.getCenter(); 204 map_center.zoom = map.getZoom(); 205 document.body.classList.add('showing-post--located');
206 map.setView([post.pos.lat, post.pos.lng], 8); 207 map.invalidateSize(); 208 } // Populate timeline 209 210 211 var template_node = document.querySelector('#post-template'); 212 var template = document.importNode(template_node.content, true); 213 var modal = template.querySelector('.post-modal'); 214 modal.classList.add('cat--' + cat); 215 const url = `https://publicspace.tools/${escape('#' + id)}`; 216 modal.querySelector('.twitter').href = `https://twitter.com/intent/tweet?text=${post.title} ${url}`; 217 modal.querySelector('.telegram').href = `https://t.me/share/url?url=<?= $url}&text=${post.title}`; 218 modal.querySelector('.post-modal__summary').innerHTML = post.summary; 219 modal.querySelector('.post-modal__body').innerHTML = post.body; 220 modal.querySelector('.post-modal__howto').innerHTML = post.howto; 221 const update = modal.querySelector('.post-modal__update'); 222 if (update) update.href = `/wp-admin/post.php?post=${id}&action=edit`; 223 var links = modal.querySelector('.post-modal__links'); 224 225 if (post.links.length > 0) { 226 links.innerHTML = '<h5 class="post-modal__links-label">' + seemore + '</h5>'; 227 post.links.forEach(function (link) { 228 if (link) { 229 var link_markup = '<li class="post-modal__link"><a target="_blank" href="' + link + '">' + link + '</a></li>'; 230 links.innerHTML += link_markup; 231 } 232 }); 233 } 234 235 let file_markup = `<a target="_blank" href="${post.file}">${post.file}</a>`; 236 modal.querySelector('.post-modal__file').innerHTML = post.howto; 237 modal.querySelector('.post-modal__close').addEventListener('click', function () { 238 document.body.classList.remove('showing-post'); 239 location.hash = ''; 240 241 if (document.body.classList.contains('showing-post--located')) { 242 document.body.classList.remove('showing-post--located');
243 map.setView(map_center.latlon, map_center.zoom); 244 map.invalidateSize(); 245 } 246 247 ; 248 unselect_items(); 249 modal.remove(); 250 document.querySelector('.case-list .case--' + current_post).scrollIntoView(); 251 }); 252 document.querySelector('.layout-map__right').appendChild(modal); 253 }); 254} 255/** 256 * Checks markers and sets its visibility regarding selected categories 257 */ 258 259 260const check_items = function () { 261 document.querySelectorAll('.item-filtrable').forEach(item => { 262 let classes = item.classList; 263 264 for (const category in categories) { 265 var is_visible = false; 266 267 if (classes.contains('filtered')) { 268 classes.remove('filtered'); 269 } 270 271 Object.keys(categories[category].terms).forEach(id => { 272 if (categories[category].terms[id] && classes.contains(`${category}--${id}`)) { 273 is_visible = true; 274 return; 275 } 276 }); 277 278 if (!is_visible) { 279 classes.add('filtered'); 280 return; 281 } 282 } 283 }); 284}; 285/** 286 * Unselect items 287 */ 288 289 290function unselect_items() { 291 document.querySelectorAll('.item-filtrable.hidden').forEach(function (i) { 292 i.classList.remove('hidden'); 293 }); 294 document.querySelectorAll('.leaflet-marker-icon').forEach(function (i) { 295 i.classList.remove('unselected'); 296 }); 297} 298/** 299 * Legend interaction 300 */ 301 302 303const init_legend = function () { 304 var legend_items = document.querySelectorAll('.legend__item'); 305 var legend_reset = document.querySelector('.legends__reset'); 306 legend_items.forEach(legend_item => { 307 legend_item.addEventListener('click', () => { 308 const category = legend_item.parentNode.dataset.cat; 309 const category_terms = legend_item.parentNode.querySelectorAll('.legend__item'); 310 const term_id = legend_item.dataset.id; // If first term selected, uncheck all excepting itself 311 312 if (categories[category].selected == category_terms.length) { 313 for (const term in categories[category].terms) { 314 categories[category].terms[term] = false; 315 } 316 317 categories[category].terms[term_id] = true; 318 category_terms.forEach(i => { 319 i.classList.remove('on'); 320 }); 321 categories[category].selected = 1; 322 legend_item.classList.add('on'); 323 } else { 324 categories[category].terms[term_id] = !categories[category].terms[term_id]; 325 legend_item.classList.toggle('on'); 326 327 if (categories[category].terms[term_id]) { 328 categories[category].selected++; 329 } else { 330 categories[category].selected--; 331 } 332 } 333 334 if (categories[category].selected == 0) { 335 for (const term in categories[category].terms) { 336 categories[category].terms[term] = true; 337 } 338 339 category_terms.forEach(i => { 340 i.classList.add('on'); 341 }); 342 categories[category].selected = category_terms.length; 343 } 344 345 check_items(); 346 }); 347 }); // Reset button in legend 348 349 legend_reset.addEventListener('click', () => { 350 ['category', 'theme'].forEach(function (cat) { 351 for (var term in categories[cat].terms) { 352 categories[cat].terms[term] = true; 353 } 354 355 categories[cat].selected = Object.keys(categories[cat].terms).length; 356 }); 357 legend_items.forEach(i => { 358 i.classList.add('on'); 359 }); 360 document.querySelectorAll('.item-filtrable').forEach(i => { 361 i.classList.remove('filtered'); 362 }); 363 }); 364}; 365 366const init_search = function () { 367 document.querySelector('.search-trigger').addEventListener('click', e => { 368 document.querySelector('.site-search').classList.remove('hidden'); 369 }); 370 document.querySelector('.site-search__button').addEventListener('click', e => { 371 const q = document.querySelector('.site-search__input').value; 372 document.querySelectorAll('.not-queried').forEach(i => { 373 i.classList.remove('not-queried'); 374 }); 375 fetch(`/wp-json/pst-cases/v1/search/${q}/${lang}`).then(data => data.json()).then(posts => { 376 var not_queried = '.item-filtrable'; 377 posts.forEach(id => { 378 not_queried += `:not(.case--${id})`; 379 });
380 document.querySelectorAll(not_queried).forEach(i => { 381 i.classList.add('not-queried'); 382 }); 383 }); 384 }); 385 document.querySelector('.site-search__close').addEventListener('click', e => { 386 document.querySelector('.site-search').classList.add('hidden'); 387 document.querySelector('.site-search__input').value = ''; 388 document.querySelectorAll('.not-queried').forEach(i => { 389 i.classList.remove('not-queried'); 390 }); 391 }); 392}; 393 394const init_about = function () { 395 document.querySelector('.site-about__close').addEventListener('click', e => { 396 document.querySelector('.site-about').classList.add('hidden'); 397 }); 398 document.querySelector('.site-branding').addEventListener('click', e => { 399 document.querySelector('.site-about').classList.toggle('hidden'); 400 }); 401}; 402 403document.addEventListener("DOMContentLoaded", function () { 404 filtrable = document.querySelectorAll('.item-filtrable'); 405 markers = document.querySelectorAll('.leaflet-marker-icon'); 406 list = document.querySelector('.case-list'); 407 document.body.classList.add('loading'); 408 init_main_map(); 409 init_legend(); 410 fetch_posts(); 411 init_search(); 412 init_about(); 413 document.body.classList.remove('loading'); 414}); 415 416/***/ }), 417
vendor: 6,544 bytes, lines 418-428
418/***/ "./node_modules/dayjs/dayjs.min.js": 419/*!*****************************************!* 420 !*** ./node_modules/dayjs/dayjs.min.js ***! 421 \*****************************************/ 422/***/ (function(module) { 423 424!function(t,e){ true?module.exports=e():0}(this,function(){"use strict";var t="millisecond",e="second",n="minute",r="hour",i="day",s="week",u="month",a="quarter",o="year",f="date",h=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,c=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,d={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},$=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},l={s:$,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+$(r,2,"0")+":"+$(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,u),s=n-i<0,a=e.clone().add(r+(s?-1:1),u);return+(-(r+(n-i)/(s?i-a:a-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(h){return{M:u,y:o,w:s,d:i,D:f,h:r,m:n,s:e,ms:t,Q:a}[h]||String(h||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},y="en",M={};M[y]=d;var m=function(t){return t instanceof S},D=function(t,e,n){var r;if(!t)return y;if("string"==typeof t)M[t]&&(r=t),e&&(M[t]=e,r=t);else{var i=t.name;M[i]=t,r=i}return!n&&r&&(y=r),r||!n&&y},v=function(t,e){if(m(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new S(n)},g=l;g.l=D,g.i=m,g.w=function(t,e){return v(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var S=function(){function d(t){this.$L=D(t.locale,null,!0),this.parse(t)}var $=d.prototype;return $.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(g.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match(h);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},$.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},$.$utils=function(){return g},$.isValid=function(){return!("Invalid Date"===this.$d.toString())},$.isSame=function(t,e){var n=v(t);return this.startOf(e)<=n&&n<=this.endOf(e)},$.isAfter=function(t,e){return v(t)<this.startOf(e)},$.isBefore=function(t,e){return this.endOf(e)<v(t)},$.$g=function(t,e,n){return g.u(t)?this[e]:this.set(n,t)},$.unix=function(){return Math.floor(this.valueOf()/1e3)},$.valueOf=function(){return this.$d.getTime()},$.startOf=function(t,a){var h=this,c=!!g.u(a)||a,d=g.p(t),$=function(t,e){var n=g.w(h.$u?Date.UTC(h.$y,e,t):new Date(h.$y,e,t),h);return c?n:n.endOf(i)},l=function(t,e){return g.w(h.toDate()[t].apply(h.toDate("s"),(c?[0,0,0,0]:[23,59,59,999]).slice(e)),h)},y=this.$W,M=this.$M,m=this.$D,D="set"+(this.$u?"UTC":"");switch(d){case o:return c?$(1,0):$(31,11);case u:return c?$(1,M):$(0,M+1);case s:var v=this.$locale().weekStart||0,S=(y<v?y+7:y)-v;return $(c?m-S:m+(6-S),M);case i:case f:return l(D+"Hours",0);case r:return l(D+"Minutes",1);case n:return l(D+"Seconds",2);case e:return l(D+"Milliseconds",3);default:return this.clone()}},$.endOf=function(t){return this.startOf(t,!1)},$.$set=function(s,a){var h,c=g.p(s),d="set"+(this.$u?"UTC":""),$=(h={},h[i]=d+"Date",h[f]=d+"Date",h[u]=d+"Month",h[o]=d+"FullYear",h[r]=d+"Hours",h[n]=d+"Minutes",h[e]=d+"Seconds",h[t]=d+"Milliseconds",h)[c],l=c===i?this.$D+(a-this.$W):a;if(c===u||c===o){var y=this.clone().set(f,1);y.$d[$](l),y.init(),this.$d=y.set(f,Math.min(this.$D,y.daysInMonth())).$d}else $&&this.$d[$](l);return this.init(),this},$.set=function(t,e){return this.clone().$set(t,e)},$.get=function(t){return this[g.p(t)]()},$.add=function(t,a){var f,h=this;t=Number(t);var c=g.p(a),d=function(e){var n=v(h);return g.w(n.date(n.date()+Math.round(e*t)),h)};if(c===u)return this.set(u,this.$M+t);if(c===o)return this.set(o,this.$y+t);if(c===i)return d(1);if(c===s)return d(7);var $=(f={},f[n]=6e4,f[r]=36e5,f[e]=1e3,f)[c]||1,l=this.$d.getTime()+t*$;return g.w(l,this)},$.subtract=function(t,e){return this.add(-1*t,e)},$.format=function(t){var e=this;if(!this.isValid())return"Invalid Date";var n=t||"YYYY-MM-DDTHH:mm:ssZ",r=g.z(this),i=this.$locale(),s=this.$H,u=this.$m,a=this.$M,o=i.weekdays,f=i.months,h=function(t,r,i,s){return t&&(t[r]||t(e,n))||i[r].substr(0,s)},d=function(t){return g.s(s%12||12,t,"0")},$=i.meridiem||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r},l={YY:String(this.$y).slice(-2),YYYY:this.$y,M:a+1,MM:g.s(a+1,2,"0"),MMM:h(i.monthsShort,a,f,3),MMMM:h(f,a),D:this.$D,DD:g.s(this.$D,2,"0"),d:String(this.$W),dd:h(i.weekdaysMin,this.$W,o,2),ddd:h(i.weekdaysShort,this.$W,o,3),dddd:o[this.$W],H:String(s),HH:g.s(s,2,"0"),h:d(1),hh:d(2),a:$(s,u,!0),A:$(s,u,!1),m:String(u),mm:g.s(u,2,"0"),s:String(this.$s),ss:g.s(this.$s,2,"0"),SSS:g.s(this.$ms,3,"0"),Z:r};return n.replace(c,function(t,e){return e||l[t]||r.replace(":","")})},$.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},$.diff=function(t,f,h){var c,d=g.p(f),$=v(t),l=6e4*($.utcOffset()-this.utcOffset()),y=this-$,M=g.m(this,$);return M=(c={},c[o]=M/12,c[u]=M,c[a]=M/3,c[s]=(y-l)/6048e5,c[i]=(y-l)/864e5,c[r]=y/36e5,c[n]=y/6e4,c[e]=y/1e3,c)[d]||y,h?M:g.a(M)},$.daysInMonth=function(){return this.endOf(u).$D},$.$locale=function(){return M[this.$L]},$.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=D(t,e,!0);return r&&(n.$L=r),n},$.clone=function(){return g.w(this.$d,this)},$.toDate=function(){return new Date(this.valueOf())},$.toJSON=function(){return this.isValid()?this.toISOString():null},$.toISOString=function(){return this.$d.toISOString()},$.toString=function(){return this.$d.toUTCString()},d}(),p=S.prototype;return v.prototype=p,[["$ms",t],["$s",e],["$m",n],["$H",r],["$W",i],["$M",u],["$y",o],["$D",f]].forEach(function(t){p[t[1]]=function(e){return this.$g(e,t[0],t[1])}}),v.extend=function(t,e){return t.$i||(t(e,S,v),t.$i=!0),v},v.locale=D,v.isDayjs=m,v.unix=function(t){return v(1e3*t)},v.en=M[y],v.Ls=M,v.p={},v}); 425 426 427/***/ }), 428
vendor: 1,269 bytes, lines 429-439
429/***/ "./node_modules/dayjs/locale/es.js": 430/*!*****************************************!* 431 !*** ./node_modules/dayjs/locale/es.js ***! 432 \*****************************************/ 433/***/ (function(module, __unused_webpack_exports, __webpack_require__) { 434 435!function(e,s){ true?module.exports=s(__webpack_require__(/*! dayjs */ "./node_modules/dayjs/dayjs.min.js")):0}(this,function(e){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var s={name:"es",monthsShort:"ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),months:"Enero_Febrero_Marzo_Abril_Mayo_Junio_Julio_Agosto_Septiembre_Octubre_Noviembre_Diciembre".split("_"),weekStart:1,formats:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dÃa",dd:"%d dÃas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinal:function(e){return e+"º"}};return e.locale(s,null,!0),s}); 436 437 438/***/ }), 439
vendor: 1,506 bytes, lines 440-450
440/***/ "./node_modules/dayjs/plugin/relativeTime.js": 441/*!***************************************************!* 442 !*** ./node_modules/dayjs/plugin/relativeTime.js ***! 443 \***************************************************/ 444/***/ (function(module) { 445 446!function(r,t){ true?module.exports=t():0}(this,function(){"use strict";return function(r,t,e){r=r||{};var n=t.prototype,o={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function i(r,t,e,o){return n.fromToBase(r,t,e,o)}e.en.relativeTime=o,n.fromToBase=function(t,n,i,d,u){for(var a,f,s,l=i.$locale().relativeTime||o,h=r.thresholds||[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],m=h.length,c=0;c<m;c+=1){var y=h[c];y.d&&(a=d?e(t).diff(i,y.d,!0):i.diff(t,y.d,!0));var p=(r.rounding||Math.round)(Math.abs(a));if(s=a>0,p<=y.r||!y.r){p<=1&&c>0&&(y=h[c-1]);var v=l[y.l];u&&(p=u(""+p)),f="string"==typeof v?v.replace("%d",p):v(p,n,y.l,s);break}}if(n)return f;var M=s?l.future:l.past;return"function"==typeof M?M(f):M.replace("%s",f)},n.to=function(r,t){return i(r,t,this,!0)},n.from=function(r,t){return i(r,t,this)};var d=function(r){return r.$u?e.utc():e()};n.toNow=function(r){return this.to(d(this),r)},n.fromNow=function(r){return this.from(d(this),r)}}}); 447 448 449/***/ }), 450
vendor: 5,360 bytes, lines 451-612
451/***/ "./node_modules/leaflet-fullscreen/dist/Leaflet.fullscreen.js": 452/*!********************************************************************!* 453 !*** ./node_modules/leaflet-fullscreen/dist/Leaflet.fullscreen.js ***! 454 \********************************************************************/ 455/***/ (() => { 456 457L.Control.Fullscreen = L.Control.extend({ 458 options: { 459 position: 'topleft', 460 title: { 461 'false': 'View Fullscreen', 462 'true': 'Exit Fullscreen' 463 } 464 }, 465 466 onAdd: function (map) { 467 var container = L.DomUtil.create('div', 'leaflet-control-fullscreen leaflet-bar leaflet-control'); 468 469 this.link = L.DomUtil.create('a', 'leaflet-control-fullscreen-button leaflet-bar-part', container); 470 this.link.href = '#'; 471 472 this._map = map; 473 this._map.on('fullscreenchange', this._toggleTitle, this); 474 this._toggleTitle(); 475 476 L.DomEvent.on(this.link, 'click', this._click, this); 477 478 return container; 479 }, 480 481 _click: function (e) { 482 L.DomEvent.stopPropagation(e); 483 L.DomEvent.preventDefault(e); 484 this._map.toggleFullscreen(this.options); 485 }, 486 487 _toggleTitle: function() { 488 this.link.title = this.options.title[this._map.isFullscreen()]; 489 } 490}); 491 492L.Map.include({ 493 isFullscreen: function () { 494 return this._isFullscreen || false; 495 }, 496 497 toggleFullscreen: function (options) { 498 var container = this.getContainer(); 499 if (this.isFullscreen()) { 500 if (options && options.pseudoFullscreen) { 501 this._disablePseudoFullscreen(container); 502 } else if (document.exitFullscreen) { 503 document.exitFullscreen(); 504 } else if (document.mozCancelFullScreen) { 505 document.mozCancelFullScreen(); 506 } else if (document.webkitCancelFullScreen) { 507 document.webkitCancelFullScreen(); 508 } else if (document.msExitFullscreen) { 509 document.msExitFullscreen(); 510 } else { 511 this._disablePseudoFullscreen(container); 512 } 513 } else { 514 if (options && options.pseudoFullscreen) { 515 this._enablePseudoFullscreen(container); 516 } else if (container.requestFullscreen) { 517 container.requestFullscreen(); 518 } else if (container.mozRequestFullScreen) { 519 container.mozRequestFullScreen(); 520 } else if (container.webkitRequestFullscreen) { 521 container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); 522 } else if (container.msRequestFullscreen) { 523 container.msRequestFullscreen(); 524 } else { 525 this._enablePseudoFullscreen(container); 526 } 527 } 528 529 }, 530 531 _enablePseudoFullscreen: function (container) { 532 L.DomUtil.addClass(container, 'leaflet-pseudo-fullscreen'); 533 this._setFullscreen(true); 534 this.fire('fullscreenchange'); 535 }, 536 537 _disablePseudoFullscreen: function (container) { 538 L.DomUtil.removeClass(container, 'leaflet-pseudo-fullscreen'); 539 this._setFullscreen(false); 540 this.fire('fullscreenchange'); 541 }, 542 543 _setFullscreen: function(fullscreen) { 544 this._isFullscreen = fullscreen; 545 var container = this.getContainer(); 546 if (fullscreen) { 547 L.DomUtil.addClass(container, 'leaflet-fullscreen-on'); 548 } else { 549 L.DomUtil.removeClass(container, 'leaflet-fullscreen-on'); 550 } 551 this.invalidateSize(); 552 }, 553 554 _onFullscreenChange: function (e) { 555 var fullscreenElement = 556 document.fullscreenElement || 557 document.mozFullScreenElement || 558 document.webkitFullscreenElement || 559 document.msFullscreenElement; 560 561 if (fullscreenElement === this.getContainer() && !this._isFullscreen) { 562 this._setFullscreen(true); 563 this.fire('fullscreenchange'); 564 } else if (fullscreenElement !== this.getContainer() && this._isFullscreen) { 565 this._setFullscreen(false); 566 this.fire('fullscreenchange'); 567 } 568 } 569}); 570 571L.Map.mergeOptions({ 572 fullscreenControl: false 573}); 574 575L.Map.addInitHook(function () { 576 if (this.options.fullscreenControl) { 577 this.fullscreenControl = new L.Control.Fullscreen(this.options.fullscreenControl); 578 this.addControl(this.fullscreenControl); 579 } 580 581 var fullscreenchange; 582 583 if ('onfullscreenchange' in document) { 584 fullscreenchange = 'fullscreenchange'; 585 } else if ('onmozfullscreenchange' in document) { 586 fullscreenchange = 'mozfullscreenchange'; 587 } else if ('onwebkitfullscreenchange' in document) { 588 fullscreenchange = 'webkitfullscreenchange'; 589 } else if ('onmsfullscreenchange' in document) { 590 fullscreenchange = 'MSFullscreenChange'; 591 } 592 593 if (fullscreenchange) { 594 var onFullscreenChange = L.bind(this._onFullscreenChange, this); 595 596 this.whenReady(function () { 597 L.DomEvent.on(document, fullscreenchange, onFullscreenChange); 598 }); 599 600 this.on('unload', function () { 601 L.DomEvent.off(document, fullscreenchange, onFullscreenChange); 602 }); 603 } 604}); 605 606L.control.fullscreen = function (options) { 607 return new L.Control.Fullscreen(options); 608}; 609 610 611/***/ }), 612
vendor: 409,149 bytes, lines 613-14700
613/***/ "./node_modules/leaflet/dist/leaflet-src.js": 614/*!**************************************************!* 615 !*** ./node_modules/leaflet/dist/leaflet-src.js ***! 616 \**************************************************/ 617/***/ (function(__unused_webpack_module, exports) { 618 619/* @preserve 620 * Leaflet 1.6.0, a JS library for interactive maps. http://leafletjs.com 621 * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade 622 */ 623 624(function (global, factory) { 625 true ? factory(exports) : 626 0; 627}(this, (function (exports) { 'use strict'; 628 629var version = "1.6.0"; 630 631/* 632 * @namespace Util 633 * 634 * Various utility functions, used by Leaflet internally. 635 */ 636 637var freeze = Object.freeze; 638Object.freeze = function (obj) { return obj; }; 639 640// @function extend(dest: Object, src?: Object): Object 641// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. 642function extend(dest) { 643 var i, j, len, src; 644 645 for (j = 1, len = arguments.length; j < len; j++) { 646 src = arguments[j]; 647 for (i in src) { 648 dest[i] = src[i]; 649 } 650 } 651 return dest; 652} 653 654// @function create(proto: Object, properties?: Object): Object 655// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) 656var create = Object.create || (function () { 657 function F() {} 658 return function (proto) { 659 F.prototype = proto; 660 return new F(); 661 }; 662})(); 663 664// @function bind(fn: Function, â¦): Function 665// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). 666// Has a `L.bind()` shortcut. 667function bind(fn, obj) { 668 var slice = Array.prototype.slice; 669 670 if (fn.bind) { 671 return fn.bind.apply(fn, slice.call(arguments, 1)); 672 } 673 674 var args = slice.call(arguments, 2); 675 676 return function () { 677 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); 678 }; 679} 680 681// @property lastId: Number 682// Last unique ID used by [`stamp()`](#util-stamp) 683var lastId = 0; 684 685// @function stamp(obj: Object): Number 686// Returns the unique ID of an object, assigning it one if it doesn't have it. 687function stamp(obj) { 688 /*eslint-disable */ 689 obj._leaflet_id = obj._leaflet_id || ++lastId; 690 return obj._leaflet_id; 691 /* eslint-enable */ 692} 693 694// @function throttle(fn: Function, time: Number, context: Object): Function 695// Returns a function which executes function `fn` with the given scope `context` 696// (so that the `this` keyword refers to `context` inside `fn`'s code). The function 697// `fn` will be called no more than one time per given amount of `time`. The arguments 698// received by the bound function will be any arguments passed when binding the 699// function, followed by any arguments passed when invoking the bound function. 700// Has an `L.throttle` shortcut. 701function throttle(fn, time, context) { 702 var lock, args, wrapperFn, later; 703 704 later = function () { 705 // reset lock and call if queued 706 lock = false; 707 if (args) { 708 wrapperFn.apply(context, args); 709 args = false; 710 } 711 }; 712 713 wrapperFn = function () { 714 if (lock) { 715 // called too soon, queue to call later 716 args = arguments; 717 718 } else { 719 // call and lock until later 720 fn.apply(context, arguments); 721 setTimeout(later, time); 722 lock = true; 723 } 724 }; 725 726 return wrapperFn; 727} 728 729// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number 730// Returns the number `num` modulo `range` in such a way so it lies within 731// `range[0]` and `range[1]`. The returned value will be always smaller than 732// `range[1]` unless `includeMax` is set to `true`. 733function wrapNum(x, range, includeMax) { 734 var max = range[1], 735 min = range[0], 736 d = max - min; 737 return x === max && includeMax ? x : ((x - min) % d + d) % d + min; 738} 739 740// @function falseFn(): Function 741// Returns a function which always returns `false`. 742function falseFn() { return false; } 743 744// @function formatNum(num: Number, digits?: Number): Number 745// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. 746function formatNum(num, digits) { 747 var pow = Math.pow(10, (digits === undefined ? 6 : digits)); 748 return Math.round(num * pow) / pow; 749} 750 751// @function trim(str: String): String 752// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) 753function trim(str) { 754 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); 755} 756 757// @function splitWords(str: String): String[] 758// Trims and splits the string on whitespace and returns the array of parts. 759function splitWords(str) { 760 return trim(str).split(/\s+/); 761} 762 763// @function setOptions(obj: Object, options: Object): Object 764// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. 765function setOptions(obj, options) { 766 if (!obj.hasOwnProperty('options')) { 767 obj.options = obj.options ? create(obj.options) : {}; 768 } 769 for (var i in options) { 770 obj.options[i] = options[i]; 771 } 772 return obj.options; 773} 774 775// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String 776// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` 777// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will 778// be appended at the end. If `uppercase` is `true`, the parameter names will 779// be uppercased (e.g. `'?A=foo&B=bar'`) 780function getParamString(obj, existingUrl, uppercase) { 781 var params = []; 782 for (var i in obj) { 783 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); 784 } 785 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); 786} 787 788var templateRe = /\{ *([\w_-]+) *\}/g; 789 790// @function template(str: String, data: Object): String 791// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` 792// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string 793// `('Hello foo, bar')`. You can also specify functions instead of strings for 794// data values â they will be evaluated passing `data` as an argument. 795function template(str, data) { 796 return str.replace(templateRe, function (str, key) { 797 var value = data[key]; 798 799 if (value === undefined) { 800 throw new Error('No value provided for variable ' + str); 801 802 } else if (typeof value === 'function') { 803 value = value(data); 804 } 805 return value; 806 }); 807} 808 809// @function isArray(obj): Boolean 810// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) 811var isArray = Array.isArray || function (obj) { 812 return (Object.prototype.toString.call(obj) === '[object Array]'); 813}; 814 815// @function indexOf(array: Array, el: Object): Number 816// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) 817function indexOf(array, el) { 818 for (var i = 0; i < array.length; i++) { 819 if (array[i] === el) { return i; } 820 } 821 return -1; 822} 823 824// @property emptyImageUrl: String 825// Data URI string containing a base64-encoded empty GIF image. 826// Used as a hack to free memory from unused images on WebKit-powered 827// mobile devices (by setting image `src` to this string). 828var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; 829 830// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 831 832function getPrefixed(name) { 833 return window['webkit' + name] || window['moz' + name] || window['ms' + name]; 834} 835 836var lastTime = 0; 837 838// fallback for IE 7-8 839function timeoutDefer(fn) { 840 var time = +new Date(), 841 timeToCall = Math.max(0, 16 - (time - lastTime)); 842 843 lastTime = time + timeToCall; 844 return window.setTimeout(fn, timeToCall); 845} 846 847var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; 848var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || 849 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; 850 851// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number 852// Schedules `fn` to be executed when the browser repaints. `fn` is bound to 853// `context` if given. When `immediate` is set, `fn` is called immediately if 854// the browser doesn't have native support for 855// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), 856// otherwise it's delayed. Returns a request ID that can be used to cancel the request. 857function requestAnimFrame(fn, context, immediate) { 858 if (immediate && requestFn === timeoutDefer) { 859 fn.call(context); 860 } else { 861 return requestFn.call(window, bind(fn, context)); 862 } 863} 864 865// @function cancelAnimFrame(id: Number): undefined 866// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). 867function cancelAnimFrame(id) { 868 if (id) { 869 cancelFn.call(window, id); 870 } 871} 872 873 874var Util = (Object.freeze || Object)({ 875 freeze: freeze, 876 extend: extend, 877 create: create, 878 bind: bind, 879 lastId: lastId, 880 stamp: stamp, 881 throttle: throttle, 882 wrapNum: wrapNum, 883 falseFn: falseFn, 884 formatNum: formatNum, 885 trim: trim, 886 splitWords: splitWords, 887 setOptions: setOptions, 888 getParamString: getParamString, 889 template: template, 890 isArray: isArray, 891 indexOf: indexOf, 892 emptyImageUrl: emptyImageUrl, 893 requestFn: requestFn, 894 cancelFn: cancelFn, 895 requestAnimFrame: requestAnimFrame, 896 cancelAnimFrame: cancelAnimFrame 897}); 898 899// @class Class 900// @aka L.Class 901 902// @section 903// @uninheritable 904 905// Thanks to John Resig and Dean Edwards for inspiration! 906 907function Class() {} 908 909Class.extend = function (props) { 910 911 // @function extend(props: Object): Function 912 // [Extends the current class](#class-inheritance) given the properties to be included. 913 // Returns a Javascript function that is a class constructor (to be called with `new`). 914 var NewClass = function () { 915 916 // call the constructor 917 if (this.initialize) { 918 this.initialize.apply(this, arguments); 919 } 920 921 // call all constructor hooks 922 this.callInitHooks(); 923 }; 924 925 var parentProto = NewClass.__super__ = this.prototype; 926 927 var proto = create(parentProto); 928 proto.constructor = NewClass; 929 930 NewClass.prototype = proto; 931 932 // inherit parent's statics 933 for (var i in this) { 934 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { 935 NewClass[i] = this[i]; 936 } 937 } 938 939 // mix static properties into the class 940 if (props.statics) { 941 extend(NewClass, props.statics); 942 delete props.statics; 943 } 944 945 // mix includes into the prototype 946 if (props.includes) { 947 checkDeprecatedMixinEvents(props.includes); 948 extend.apply(null, [proto].concat(props.includes)); 949 delete props.includes; 950 } 951 952 // merge options 953 if (proto.options) { 954 props.options = extend(create(proto.options), props.options); 955 } 956 957 // mix given properties into the prototype 958 extend(proto, props); 959 960 proto._initHooks = []; 961 962 // add method for calling all hooks 963 proto.callInitHooks = function () { 964 965 if (this._initHooksCalled) { return; } 966 967 if (parentProto.callInitHooks) { 968 parentProto.callInitHooks.call(this); 969 } 970 971 this._initHooksCalled = true; 972 973 for (var i = 0, len = proto._initHooks.length; i < len; i++) { 974 proto._initHooks[i].call(this); 975 } 976 }; 977 978 return NewClass; 979}; 980 981 982// @function include(properties: Object): this 983// [Includes a mixin](#class-includes) into the current class. 984Class.include = function (props) { 985 extend(this.prototype, props); 986 return this; 987}; 988 989// @function mergeOptions(options: Object): this 990// [Merges `options`](#class-options) into the defaults of the class. 991Class.mergeOptions = function (options) { 992 extend(this.prototype.options, options); 993 return this; 994}; 995 996// @function addInitHook(fn: Function): this 997// Adds a [constructor hook](#class-constructor-hooks) to the class. 998Class.addInitHook = function (fn) { // (Function) || (String, args...) 999 var args = Array.prototype.slice.call(arguments, 1); 1000 1001 var init = typeof fn === 'function' ? fn : function () { 1002 this[fn].apply(this, args); 1003 }; 1004 1005 this.prototype._initHooks = this.prototype._initHooks || []; 1006 this.prototype._initHooks.push(init); 1007 return this; 1008}; 1009 1010function checkDeprecatedMixinEvents(includes) { 1011 if (typeof L === 'undefined' || !L || !L.Mixin) { return; } 1012 1013 includes = isArray(includes) ? includes : [includes]; 1014 1015 for (var i = 0; i < includes.length; i++) { 1016 if (includes[i] === L.Mixin.Events) { 1017 console.warn('Deprecated include of L.Mixin.Events: ' + 1018 'this property will be removed in future releases, ' + 1019 'please inherit from L.Evented instead.', new Error().stack); 1020 } 1021 } 1022} 1023 1024/* 1025 * @class Evented 1026 * @aka L.Evented 1027 * @inherits Class 1028 * 1029 * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). 1030 * 1031 * @example 1032 * 1033 * ```js 1034 * map.on('click', function(e) { 1035 * alert(e.latlng); 1036 * } ); 1037 * ``` 1038 * 1039 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: 1040 * 1041 * ```js 1042 * function onClick(e) { ... } 1043 * 1044 * map.on('click', onClick); 1045 * map.off('click', onClick); 1046 * ``` 1047 */ 1048 1049var Events = { 1050 /* @method on(type: String, fn: Function, context?: Object): this 1051 * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). 1052 * 1053 * @alternative 1054 * @method on(eventMap: Object): this 1055 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 1056 */ 1057 on: function (types, fn, context) { 1058 1059 // types can be a map of types/handlers 1060 if (typeof types === 'object') { 1061 for (var type in types) { 1062 // we don't process space-separated events here for performance; 1063 // it's a hot path since Layer uses the on(obj) syntax 1064 this._on(type, types[type], fn); 1065 } 1066 1067 } else { 1068 // types can be a string of space-separated words 1069 types = splitWords(types); 1070 1071 for (var i = 0, len = types.length; i < len; i++) { 1072 this._on(types[i], fn, context); 1073 } 1074 } 1075 1076 return this; 1077 }, 1078 1079 /* @method off(type: String, fn?: Function, context?: Object): this 1080 * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. 1081 * 1082 * @alternative 1083 * @method off(eventMap: Object): this 1084 * Removes a set of type/listener pairs. 1085 * 1086 * @alternative 1087 * @method off: this 1088 * Removes all listeners to all events on the object. This includes implicitly attached events. 1089 */ 1090 off: function (types, fn, context) { 1091 1092 if (!types) { 1093 // clear all listeners if called without arguments 1094 delete this._events; 1095 1096 } else if (typeof types === 'object') { 1097 for (var type in types) { 1098 this._off(type, types[type], fn); 1099 } 1100 1101 } else { 1102 types = splitWords(types); 1103 1104 for (var i = 0, len = types.length; i < len; i++) { 1105 this._off(types[i], fn, context); 1106 } 1107 } 1108 1109 return this; 1110 }, 1111 1112 // attach listener (without syntactic sugar now) 1113 _on: function (type, fn, context) { 1114 this._events = this._events || {}; 1115 1116 /* get/init listeners for type */ 1117 var typeListeners = this._events[type]; 1118 if (!typeListeners) { 1119 typeListeners = []; 1120 this._events[type] = typeListeners; 1121 } 1122 1123 if (context === this) { 1124 // Less memory footprint. 1125 context = undefined; 1126 } 1127 var newListener = {fn: fn, ctx: context}, 1128 listeners = typeListeners; 1129 1130 // check if fn already there 1131 for (var i = 0, len = listeners.length; i < len; i++) { 1132 if (listeners[i].fn === fn && listeners[i].ctx === context) { 1133 return; 1134 } 1135 } 1136 1137 listeners.push(newListener); 1138 }, 1139 1140 _off: function (type, fn, context) { 1141 var listeners, 1142 i, 1143 len; 1144 1145 if (!this._events) { return; } 1146 1147 listeners = this._events[type]; 1148 1149 if (!listeners) { 1150 return; 1151 } 1152 1153 if (!fn) { 1154 // Set all removed listeners to noop so they are not called if remove happens in fire 1155 for (i = 0, len = listeners.length; i < len; i++) { 1156 listeners[i].fn = falseFn; 1157 } 1158 // clear all listeners for a type if function isn't specified 1159 delete this._events[type]; 1160 return; 1161 } 1162 1163 if (context === this) { 1164 context = undefined; 1165 } 1166 1167 if (listeners) { 1168 1169 // find fn and remove it 1170 for (i = 0, len = listeners.length; i < len; i++) { 1171 var l = listeners[i]; 1172 if (l.ctx !== context) { continue; } 1173 if (l.fn === fn) { 1174 1175 // set the removed listener to noop so that's not called if remove happens in fire 1176 l.fn = falseFn; 1177 1178 if (this._firingCount) { 1179 /* copy array in case events are being fired */ 1180 this._events[type] = listeners = listeners.slice(); 1181 } 1182 listeners.splice(i, 1); 1183 1184 return; 1185 } 1186 } 1187 } 1188 }, 1189 1190 // @method fire(type: String, data?: Object, propagate?: Boolean): this 1191 // Fires an event of the specified type. You can optionally provide an data 1192 // object â the first argument of the listener function will contain its 1193 // properties. The event can optionally be propagated to event parents. 1194 fire: function (type, data, propagate) { 1195 if (!this.listens(type, propagate)) { return this; } 1196 1197 var event = extend({}, data, { 1198 type: type, 1199 target: this, 1200 sourceTarget: data && data.sourceTarget || this 1201 }); 1202 1203 if (this._events) { 1204 var listeners = this._events[type]; 1205 1206 if (listeners) { 1207 this._firingCount = (this._firingCount + 1) || 1; 1208 for (var i = 0, len = listeners.length; i < len; i++) { 1209 var l = listeners[i]; 1210 l.fn.call(l.ctx || this, event); 1211 } 1212 1213 this._firingCount--; 1214 } 1215 } 1216 1217 if (propagate) { 1218 // propagate the event to parents (set with addEventParent) 1219 this._propagateEvent(event); 1220 } 1221 1222 return this; 1223 }, 1224 1225 // @method listens(type: String): Boolean 1226 // Returns `true` if a particular event type has any listeners attached to it. 1227 listens: function (type, propagate) { 1228 var listeners = this._events && this._events[type]; 1229 if (listeners && listeners.length) { return true; } 1230 1231 if (propagate) { 1232 // also check parents for listeners if event propagates 1233 for (var id in this._eventParents) { 1234 if (this._eventParents[id].listens(type, propagate)) { return true; } 1235 } 1236 } 1237 return false; 1238 }, 1239 1240 // @method once(â¦): this 1241 // Behaves as [`on(â¦)`](#evented-on), except the listener will only get fired once and then removed. 1242 once: function (types, fn, context) { 1243 1244 if (typeof types === 'object') { 1245 for (var type in types) { 1246 this.once(type, types[type], fn); 1247 } 1248 return this; 1249 } 1250 1251 var handler = bind(function () { 1252 this 1253 .off(types, fn, context) 1254 .off(types, handler, context); 1255 }, this); 1256 1257 // add a listener that's executed once and removed after that 1258 return this 1259 .on(types, fn, context) 1260 .on(types, handler, context); 1261 }, 1262 1263 // @method addEventParent(obj: Evented): this 1264 // Adds an event parent - an `Evented` that will receive propagated events 1265 addEventParent: function (obj) { 1266 this._eventParents = this._eventParents || {}; 1267 this._eventParents[stamp(obj)] = obj; 1268 return this; 1269 }, 1270 1271 // @method removeEventParent(obj: Evented): this 1272 // Removes an event parent, so it will stop receiving propagated events 1273 removeEventParent: function (obj) { 1274 if (this._eventParents) { 1275 delete this._eventParents[stamp(obj)]; 1276 } 1277 return this; 1278 }, 1279 1280 _propagateEvent: function (e) { 1281 for (var id in this._eventParents) { 1282 this._eventParents[id].fire(e.type, extend({ 1283 layer: e.target, 1284 propagatedFrom: e.target 1285 }, e), true); 1286 } 1287 } 1288}; 1289 1290// aliases; we should ditch those eventually 1291 1292// @method addEventListener(â¦): this 1293// Alias to [`on(â¦)`](#evented-on) 1294Events.addEventListener = Events.on; 1295 1296// @method removeEventListener(â¦): this 1297// Alias to [`off(â¦)`](#evented-off) 1298 1299// @method clearAllEventListeners(â¦): this 1300// Alias to [`off()`](#evented-off) 1301Events.removeEventListener = Events.clearAllEventListeners = Events.off; 1302 1303// @method addOneTimeEventListener(â¦): this 1304// Alias to [`once(â¦)`](#evented-once) 1305Events.addOneTimeEventListener = Events.once; 1306 1307// @method fireEvent(â¦): this 1308// Alias to [`fire(â¦)`](#evented-fire) 1309Events.fireEvent = Events.fire; 1310 1311// @method hasEventListeners(â¦): Boolean 1312// Alias to [`listens(â¦)`](#evented-listens) 1313Events.hasEventListeners = Events.listens; 1314 1315var Evented = Class.extend(Events); 1316 1317/* 1318 * @class Point 1319 * @aka L.Point 1320 * 1321 * Represents a point with `x` and `y` coordinates in pixels. 1322 * 1323 * @example 1324 * 1325 * ```js 1326 * var point = L.point(200, 300); 1327 * ``` 1328 * 1329 * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: 1330 * 1331 * ```js 1332 * map.panBy([200, 300]); 1333 * map.panBy(L.point(200, 300)); 1334 * ``` 1335 * 1336 * Note that `Point` does not inherit from Leafet's `Class` object, 1337 * which means new classes can't inherit from it, and new methods 1338 * can't be added to it with the `include` function. 1339 */ 1340 1341function Point(x, y, round) { 1342 // @property x: Number; The `x` coordinate of the point 1343 this.x = (round ? Math.round(x) : x); 1344 // @property y: Number; The `y` coordinate of the point 1345 this.y = (round ? Math.round(y) : y); 1346} 1347 1348var trunc = Math.trunc || function (v) { 1349 return v > 0 ? Math.floor(v) : Math.ceil(v); 1350}; 1351 1352Point.prototype = { 1353 1354 // @method clone(): Point 1355 // Returns a copy of the current point. 1356 clone: function () { 1357 return new Point(this.x, this.y); 1358 }, 1359 1360 // @method add(otherPoint: Point): Point 1361 // Returns the result of addition of the current and the given points. 1362 add: function (point) { 1363 // non-destructive, returns a new point 1364 return this.clone()._add(toPoint(point)); 1365 }, 1366 1367 _add: function (point) { 1368 // destructive, used directly for performance in situations where it's safe to modify existing point 1369 this.x += point.x; 1370 this.y += point.y; 1371 return this; 1372 }, 1373 1374 // @method subtract(otherPoint: Point): Point 1375 // Returns the result of subtraction of the given point from the current. 1376 subtract: function (point) { 1377 return this.clone()._subtract(toPoint(point)); 1378 }, 1379 1380 _subtract: function (point) { 1381 this.x -= point.x; 1382 this.y -= point.y; 1383 return this; 1384 }, 1385 1386 // @method divideBy(num: Number): Point 1387 // Returns the result of division of the current point by the given number. 1388 divideBy: function (num) { 1389 return this.clone()._divideBy(num); 1390 }, 1391 1392 _divideBy: function (num) { 1393 this.x /= num; 1394 this.y /= num; 1395 return this; 1396 }, 1397 1398 // @method multiplyBy(num: Number): Point 1399 // Returns the result of multiplication of the current point by the given number. 1400 multiplyBy: function (num) { 1401 return this.clone()._multiplyBy(num); 1402 }, 1403 1404 _multiplyBy: function (num) { 1405 this.x *= num; 1406 this.y *= num; 1407 return this; 1408 }, 1409 1410 // @method scaleBy(scale: Point): Point 1411 // Multiply each coordinate of the current point by each coordinate of 1412 // `scale`. In linear algebra terms, multiply the point by the 1413 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) 1414 // defined by `scale`. 1415 scaleBy: function (point) { 1416 return new Point(this.x * point.x, this.y * point.y); 1417 }, 1418 1419 // @method unscaleBy(scale: Point): Point 1420 // Inverse of `scaleBy`. Divide each coordinate of the current point by 1421 // each coordinate of `scale`. 1422 unscaleBy: function (point) { 1423 return new Point(this.x / point.x, this.y / point.y); 1424 }, 1425 1426 // @method round(): Point 1427 // Returns a copy of the current point with rounded coordinates. 1428 round: function () { 1429 return this.clone()._round(); 1430 }, 1431 1432 _round: function () { 1433 this.x = Math.round(this.x); 1434 this.y = Math.round(this.y); 1435 return this; 1436 }, 1437 1438 // @method floor(): Point 1439 // Returns a copy of the current point with floored coordinates (rounded down). 1440 floor: function () { 1441 return this.clone()._floor(); 1442 }, 1443 1444 _floor: function () { 1445 this.x = Math.floor(this.x); 1446 this.y = Math.floor(this.y); 1447 return this; 1448 }, 1449 1450 // @method ceil(): Point 1451 // Returns a copy of the current point with ceiled coordinates (rounded up). 1452 ceil: function () { 1453 return this.clone()._ceil(); 1454 }, 1455 1456 _ceil: function () { 1457 this.x = Math.ceil(this.x); 1458 this.y = Math.ceil(this.y); 1459 return this; 1460 }, 1461 1462 // @method trunc(): Point 1463 // Returns a copy of the current point with truncated coordinates (rounded towards zero). 1464 trunc: function () { 1465 return this.clone()._trunc(); 1466 }, 1467 1468 _trunc: function () { 1469 this.x = trunc(this.x); 1470 this.y = trunc(this.y); 1471 return this; 1472 }, 1473 1474 // @method distanceTo(otherPoint: Point): Number 1475 // Returns the cartesian distance between the current and the given points. 1476 distanceTo: function (point) { 1477 point = toPoint(point); 1478 1479 var x = point.x - this.x, 1480 y = point.y - this.y; 1481 1482 return Math.sqrt(x * x + y * y); 1483 }, 1484 1485 // @method equals(otherPoint: Point): Boolean 1486 // Returns `true` if the given point has the same coordinates. 1487 equals: function (point) { 1488 point = toPoint(point); 1489 1490 return point.x === this.x && 1491 point.y === this.y; 1492 }, 1493 1494 // @method contains(otherPoint: Point): Boolean 1495 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). 1496 contains: function (point) { 1497 point = toPoint(point); 1498 1499 return Math.abs(point.x) <= Math.abs(this.x) && 1500 Math.abs(point.y) <= Math.abs(this.y); 1501 }, 1502 1503 // @method toString(): String 1504 // Returns a string representation of the point for debugging purposes. 1505 toString: function () { 1506 return 'Point(' + 1507 formatNum(this.x) + ', ' + 1508 formatNum(this.y) + ')'; 1509 } 1510}; 1511 1512// @factory L.point(x: Number, y: Number, round?: Boolean) 1513// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. 1514 1515// @alternative 1516// @factory L.point(coords: Number[]) 1517// Expects an array of the form `[x, y]` instead. 1518 1519// @alternative 1520// @factory L.point(coords: Object) 1521// Expects a plain object of the form `{x: Number, y: Number}` instead. 1522function toPoint(x, y, round) { 1523 if (x instanceof Point) { 1524 return x; 1525 } 1526 if (isArray(x)) { 1527 return new Point(x[0], x[1]); 1528 } 1529 if (x === undefined || x === null) { 1530 return x; 1531 } 1532 if (typeof x === 'object' && 'x' in x && 'y' in x) { 1533 return new Point(x.x, x.y); 1534 } 1535 return new Point(x, y, round); 1536} 1537 1538/* 1539 * @class Bounds 1540 * @aka L.Bounds 1541 * 1542 * Represents a rectangular area in pixel coordinates. 1543 * 1544 * @example 1545 * 1546 * ```js 1547 * var p1 = L.point(10, 10), 1548 * p2 = L.point(40, 60), 1549 * bounds = L.bounds(p1, p2); 1550 * ``` 1551 * 1552 * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: 1553 * 1554 * ```js 1555 * otherBounds.intersects([[10, 10], [40, 60]]); 1556 * ``` 1557 * 1558 * Note that `Bounds` does not inherit from Leafet's `Class` object, 1559 * which means new classes can't inherit from it, and new methods 1560 * can't be added to it with the `include` function. 1561 */ 1562 1563function Bounds(a, b) { 1564 if (!a) { return; } 1565 1566 var points = b ? [a, b] : a; 1567 1568 for (var i = 0, len = points.length; i < len; i++) { 1569 this.extend(points[i]); 1570 } 1571} 1572 1573Bounds.prototype = { 1574 // @method extend(point: Point): this 1575 // Extends the bounds to contain the given point. 1576 extend: function (point) { // (Point) 1577 point = toPoint(point); 1578 1579 // @property min: Point 1580 // The top left corner of the rectangle. 1581 // @property max: Point 1582 // The bottom right corner of the rectangle. 1583 if (!this.min && !this.max) { 1584 this.min = point.clone(); 1585 this.max = point.clone(); 1586 } else { 1587 this.min.x = Math.min(point.x, this.min.x); 1588 this.max.x = Math.max(point.x, this.max.x); 1589 this.min.y = Math.min(point.y, this.min.y); 1590 this.max.y = Math.max(point.y, this.max.y); 1591 } 1592 return this; 1593 }, 1594 1595 // @method getCenter(round?: Boolean): Point 1596 // Returns the center point of the bounds. 1597 getCenter: function (round) { 1598 return new Point( 1599 (this.min.x + this.max.x) / 2, 1600 (this.min.y + this.max.y) / 2, round); 1601 }, 1602 1603 // @method getBottomLeft(): Point 1604 // Returns the bottom-left point of the bounds. 1605 getBottomLeft: function () { 1606 return new Point(this.min.x, this.max.y); 1607 }, 1608 1609 // @method getTopRight(): Point 1610 // Returns the top-right point of the bounds. 1611 getTopRight: function () { // -> Point 1612 return new Point(this.max.x, this.min.y); 1613 }, 1614 1615 // @method getTopLeft(): Point 1616 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). 1617 getTopLeft: function () { 1618 return this.min; // left, top 1619 }, 1620 1621 // @method getBottomRight(): Point 1622 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). 1623 getBottomRight: function () { 1624 return this.max; // right, bottom 1625 }, 1626 1627 // @method getSize(): Point 1628 // Returns the size of the given bounds 1629 getSize: function () { 1630 return this.max.subtract(this.min); 1631 }, 1632 1633 // @method contains(otherBounds: Bounds): Boolean 1634 // Returns `true` if the rectangle contains the given one. 1635 // @alternative 1636 // @method contains(point: Point): Boolean 1637 // Returns `true` if the rectangle contains the given point. 1638 contains: function (obj) { 1639 var min, max; 1640 1641 if (typeof obj[0] === 'number' || obj instanceof Point) { 1642 obj = toPoint(obj); 1643 } else { 1644 obj = toBounds(obj); 1645 } 1646 1647 if (obj instanceof Bounds) { 1648 min = obj.min; 1649 max = obj.max; 1650 } else { 1651 min = max = obj; 1652 } 1653 1654 return (min.x >= this.min.x) && 1655 (max.x <= this.max.x) && 1656 (min.y >= this.min.y) && 1657 (max.y <= this.max.y); 1658 }, 1659 1660 // @method intersects(otherBounds: Bounds): Boolean 1661 // Returns `true` if the rectangle intersects the given bounds. Two bounds 1662 // intersect if they have at least one point in common. 1663 intersects: function (bounds) { // (Bounds) -> Boolean 1664 bounds = toBounds(bounds); 1665 1666 var min = this.min, 1667 max = this.max, 1668 min2 = bounds.min, 1669 max2 = bounds.max, 1670 xIntersects = (max2.x >= min.x) && (min2.x <= max.x), 1671 yIntersects = (max2.y >= min.y) && (min2.y <= max.y); 1672 1673 return xIntersects && yIntersects; 1674 }, 1675 1676 // @method overlaps(otherBounds: Bounds): Boolean 1677 // Returns `true` if the rectangle overlaps the given bounds. Two bounds 1678 // overlap if their intersection is an area. 1679 overlaps: function (bounds) { // (Bounds) -> Boolean 1680 bounds = toBounds(bounds); 1681 1682 var min = this.min, 1683 max = this.max, 1684 min2 = bounds.min, 1685 max2 = bounds.max, 1686 xOverlaps = (max2.x > min.x) && (min2.x < max.x), 1687 yOverlaps = (max2.y > min.y) && (min2.y < max.y); 1688 1689 return xOverlaps && yOverlaps; 1690 }, 1691 1692 isValid: function () { 1693 return !!(this.min && this.max); 1694 } 1695}; 1696 1697 1698// @factory L.bounds(corner1: Point, corner2: Point) 1699// Creates a Bounds object from two corners coordinate pairs. 1700// @alternative 1701// @factory L.bounds(points: Point[]) 1702// Creates a Bounds object from the given array of points. 1703function toBounds(a, b) { 1704 if (!a || a instanceof Bounds) { 1705 return a; 1706 } 1707 return new Bounds(a, b); 1708} 1709 1710/* 1711 * @class LatLngBounds 1712 * @aka L.LatLngBounds 1713 * 1714 * Represents a rectangular geographical area on a map. 1715 * 1716 * @example 1717 * 1718 * ```js 1719 * var corner1 = L.latLng(40.712, -74.227), 1720 * corner2 = L.latLng(40.774, -74.125), 1721 * bounds = L.latLngBounds(corner1, corner2); 1722 * ``` 1723 * 1724 * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: 1725 * 1726 * ```js 1727 * map.fitBounds([ 1728 * [40.712, -74.227], 1729 * [40.774, -74.125] 1730 * ]); 1731 * ``` 1732 * 1733 * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. 1734 * 1735 * Note that `LatLngBounds` does not inherit from Leafet's `Class` object, 1736 * which means new classes can't inherit from it, and new methods 1737 * can't be added to it with the `include` function. 1738 */ 1739 1740function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) 1741 if (!corner1) { return; } 1742 1743 var latlngs = corner2 ? [corner1, corner2] : corner1; 1744 1745 for (var i = 0, len = latlngs.length; i < len; i++) { 1746 this.extend(latlngs[i]); 1747 } 1748} 1749 1750LatLngBounds.prototype = { 1751 1752 // @method extend(latlng: LatLng): this 1753 // Extend the bounds to contain the given point 1754 1755 // @alternative 1756 // @method extend(otherBounds: LatLngBounds): this 1757 // Extend the bounds to contain the given bounds 1758 extend: function (obj) { 1759 var sw = this._southWest, 1760 ne = this._northEast, 1761 sw2, ne2; 1762 1763 if (obj instanceof LatLng) { 1764 sw2 = obj; 1765 ne2 = obj; 1766 1767 } else if (obj instanceof LatLngBounds) { 1768 sw2 = obj._southWest; 1769 ne2 = obj._northEast; 1770 1771 if (!sw2 || !ne2) { return this; } 1772 1773 } else { 1774 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; 1775 } 1776 1777 if (!sw && !ne) { 1778 this._southWest = new LatLng(sw2.lat, sw2.lng); 1779 this._northEast = new LatLng(ne2.lat, ne2.lng); 1780 } else { 1781 sw.lat = Math.min(sw2.lat, sw.lat); 1782 sw.lng = Math.min(sw2.lng, sw.lng); 1783 ne.lat = Math.max(ne2.lat, ne.lat); 1784 ne.lng = Math.max(ne2.lng, ne.lng); 1785 } 1786 1787 return this; 1788 }, 1789 1790 // @method pad(bufferRatio: Number): LatLngBounds 1791 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. 1792 // For example, a ratio of 0.5 extends the bounds by 50% in each direction. 1793 // Negative values will retract the bounds. 1794 pad: function (bufferRatio) { 1795 var sw = this._southWest, 1796 ne = this._northEast, 1797 heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, 1798 widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; 1799 1800 return new LatLngBounds( 1801 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), 1802 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); 1803 }, 1804 1805 // @method getCenter(): LatLng 1806 // Returns the center point of the bounds. 1807 getCenter: function () { 1808 return new LatLng( 1809 (this._southWest.lat + this._northEast.lat) / 2, 1810 (this._southWest.lng + this._northEast.lng) / 2); 1811 }, 1812 1813 // @method getSouthWest(): LatLng 1814 // Returns the south-west point of the bounds. 1815 getSouthWest: function () { 1816 return this._southWest; 1817 }, 1818 1819 // @method getNorthEast(): LatLng 1820 // Returns the north-east point of the bounds. 1821 getNorthEast: function () { 1822 return this._northEast; 1823 }, 1824 1825 // @method getNorthWest(): LatLng 1826 // Returns the north-west point of the bounds. 1827 getNorthWest: function () { 1828 return new LatLng(this.getNorth(), this.getWest()); 1829 }, 1830 1831 // @method getSouthEast(): LatLng 1832 // Returns the south-east point of the bounds. 1833 getSouthEast: function () { 1834 return new LatLng(this.getSouth(), this.getEast()); 1835 }, 1836 1837 // @method getWest(): Number 1838 // Returns the west longitude of the bounds 1839 getWest: function () { 1840 return this._southWest.lng; 1841 }, 1842 1843 // @method getSouth(): Number 1844 // Returns the south latitude of the bounds 1845 getSouth: function () { 1846 return this._southWest.lat; 1847 }, 1848 1849 // @method getEast(): Number 1850 // Returns the east longitude of the bounds 1851 getEast: function () { 1852 return this._northEast.lng; 1853 }, 1854 1855 // @method getNorth(): Number 1856 // Returns the north latitude of the bounds 1857 getNorth: function () { 1858 return this._northEast.lat; 1859 }, 1860 1861 // @method contains(otherBounds: LatLngBounds): Boolean 1862 // Returns `true` if the rectangle contains the given one. 1863 1864 // @alternative 1865 // @method contains (latlng: LatLng): Boolean 1866 // Returns `true` if the rectangle contains the given point. 1867 contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean 1868 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { 1869 obj = toLatLng(obj); 1870 } else { 1871 obj = toLatLngBounds(obj); 1872 } 1873 1874 var sw = this._southWest, 1875 ne = this._northEast, 1876 sw2, ne2; 1877 1878 if (obj instanceof LatLngBounds) { 1879 sw2 = obj.getSouthWest(); 1880 ne2 = obj.getNorthEast(); 1881 } else { 1882 sw2 = ne2 = obj; 1883 } 1884 1885 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && 1886 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); 1887 }, 1888 1889 // @method intersects(otherBounds: LatLngBounds): Boolean 1890 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. 1891 intersects: function (bounds) { 1892 bounds = toLatLngBounds(bounds); 1893 1894 var sw = this._southWest, 1895 ne = this._northEast, 1896 sw2 = bounds.getSouthWest(), 1897 ne2 = bounds.getNorthEast(), 1898 1899 latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), 1900 lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); 1901 1902 return latIntersects && lngIntersects; 1903 }, 1904 1905 // @method overlaps(otherBounds: Bounds): Boolean 1906 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. 1907 overlaps: function (bounds) { 1908 bounds = toLatLngBounds(bounds); 1909 1910 var sw = this._southWest, 1911 ne = this._northEast, 1912 sw2 = bounds.getSouthWest(), 1913 ne2 = bounds.getNorthEast(), 1914 1915 latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), 1916 lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); 1917 1918 return latOverlaps && lngOverlaps; 1919 }, 1920 1921 // @method toBBoxString(): String 1922 // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. 1923 toBBoxString: function () { 1924 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); 1925 }, 1926 1927 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean 1928 // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. 1929 equals: function (bounds, maxMargin) { 1930 if (!bounds) { return false; } 1931 1932 bounds = toLatLngBounds(bounds); 1933 1934 return this._southWest.equals(bounds.getSouthWest(), maxMargin) && 1935 this._northEast.equals(bounds.getNorthEast(), maxMargin); 1936 }, 1937 1938 // @method isValid(): Boolean 1939 // Returns `true` if the bounds are properly initialized. 1940 isValid: function () { 1941 return !!(this._southWest && this._northEast); 1942 } 1943}; 1944 1945// TODO International date line? 1946 1947// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) 1948// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. 1949 1950// @alternative 1951// @factory L.latLngBounds(latlngs: LatLng[]) 1952// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). 1953function toLatLngBounds(a, b) { 1954 if (a instanceof LatLngBounds) { 1955 return a; 1956 } 1957 return new LatLngBounds(a, b); 1958} 1959 1960/* @class LatLng 1961 * @aka L.LatLng 1962 * 1963 * Represents a geographical point with a certain latitude and longitude. 1964 * 1965 * @example 1966 * 1967 * ``` 1968 * var latlng = L.latLng(50.5, 30.5); 1969 * ``` 1970 * 1971 * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: 1972 * 1973 * ``` 1974 * map.panTo([50, 30]); 1975 * map.panTo({lon: 30, lat: 50}); 1976 * map.panTo({lat: 50, lng: 30}); 1977 * map.panTo(L.latLng(50, 30)); 1978 * ``` 1979 * 1980 * Note that `LatLng` does not inherit from Leaflet's `Class` object, 1981 * which means new classes can't inherit from it, and new methods 1982 * can't be added to it with the `include` function. 1983 */ 1984 1985function LatLng(lat, lng, alt) { 1986 if (isNaN(lat) || isNaN(lng)) { 1987 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); 1988 } 1989 1990 // @property lat: Number 1991 // Latitude in degrees 1992 this.lat = +lat; 1993 1994 // @property lng: Number 1995 // Longitude in degrees 1996 this.lng = +lng; 1997 1998 // @property alt: Number 1999 // Altitude in meters (optional) 2000 if (alt !== undefined) { 2001 this.alt = +alt; 2002 } 2003} 2004 2005LatLng.prototype = { 2006 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean 2007 // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. 2008 equals: function (obj, maxMargin) { 2009 if (!obj) { return false; } 2010 2011 obj = toLatLng(obj); 2012 2013 var margin = Math.max( 2014 Math.abs(this.lat - obj.lat), 2015 Math.abs(this.lng - obj.lng)); 2016 2017 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); 2018 }, 2019 2020 // @method toString(): String 2021 // Returns a string representation of the point (for debugging purposes). 2022 toString: function (precision) { 2023 return 'LatLng(' + 2024 formatNum(this.lat, precision) + ', ' + 2025 formatNum(this.lng, precision) + ')'; 2026 }, 2027 2028 // @method distanceTo(otherLatLng: LatLng): Number 2029 // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). 2030 distanceTo: function (other) { 2031 return Earth.distance(this, toLatLng(other)); 2032 }, 2033 2034 // @method wrap(): LatLng 2035 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. 2036 wrap: function () { 2037 return Earth.wrapLatLng(this); 2038 }, 2039 2040 // @method toBounds(sizeInMeters: Number): LatLngBounds 2041 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. 2042 toBounds: function (sizeInMeters) { 2043 var latAccuracy = 180 * sizeInMeters / 40075017, 2044 lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); 2045 2046 return toLatLngBounds( 2047 [this.lat - latAccuracy, this.lng - lngAccuracy], 2048 [this.lat + latAccuracy, this.lng + lngAccuracy]); 2049 }, 2050 2051 clone: function () { 2052 return new LatLng(this.lat, this.lng, this.alt); 2053 } 2054}; 2055 2056 2057 2058// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng 2059// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). 2060 2061// @alternative 2062// @factory L.latLng(coords: Array): LatLng 2063// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. 2064 2065// @alternative 2066// @factory L.latLng(coords: Object): LatLng 2067// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. 2068 2069function toLatLng(a, b, c) { 2070 if (a instanceof LatLng) { 2071 return a; 2072 } 2073 if (isArray(a) && typeof a[0] !== 'object') { 2074 if (a.length === 3) { 2075 return new LatLng(a[0], a[1], a[2]); 2076 } 2077 if (a.length === 2) { 2078 return new LatLng(a[0], a[1]); 2079 } 2080 return null; 2081 } 2082 if (a === undefined || a === null) { 2083 return a; 2084 } 2085 if (typeof a === 'object' && 'lat' in a) { 2086 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); 2087 } 2088 if (b === undefined) { 2089 return null; 2090 } 2091 return new LatLng(a, b, c); 2092} 2093 2094/* 2095 * @namespace CRS 2096 * @crs L.CRS.Base 2097 * Object that defines coordinate reference systems for projecting 2098 * geographical points into pixel (screen) coordinates and back (and to 2099 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See 2100 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). 2101 * 2102 * Leaflet defines the most usual CRSs by default. If you want to use a 2103 * CRS not defined by default, take a look at the 2104 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. 2105 * 2106 * Note that the CRS instances do not inherit from Leafet's `Class` object, 2107 * and can't be instantiated. Also, new classes can't inherit from them, 2108 * and methods can't be added to them with the `include` function. 2109 */ 2110 2111var CRS = { 2112 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point 2113 // Projects geographical coordinates into pixel coordinates for a given zoom. 2114 latLngToPoint: function (latlng, zoom) { 2115 var projectedPoint = this.projection.project(latlng), 2116 scale = this.scale(zoom); 2117 2118 return this.transformation._transform(projectedPoint, scale); 2119 }, 2120 2121 // @method pointToLatLng(point: Point, zoom: Number): LatLng 2122 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given 2123 // zoom into geographical coordinates. 2124 pointToLatLng: function (point, zoom) { 2125 var scale = this.scale(zoom), 2126 untransformedPoint = this.transformation.untransform(point, scale); 2127 2128 return this.projection.unproject(untransformedPoint); 2129 }, 2130 2131 // @method project(latlng: LatLng): Point 2132 // Projects geographical coordinates into coordinates in units accepted for 2133 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). 2134 project: function (latlng) { 2135 return this.projection.project(latlng); 2136 }, 2137 2138 // @method unproject(point: Point): LatLng 2139 // Given a projected coordinate returns the corresponding LatLng. 2140 // The inverse of `project`. 2141 unproject: function (point) { 2142 return this.projection.unproject(point); 2143 }, 2144 2145 // @method scale(zoom: Number): Number 2146 // Returns the scale used when transforming projected coordinates into 2147 // pixel coordinates for a particular zoom. For example, it returns 2148 // `256 * 2^zoom` for Mercator-based CRS. 2149 scale: function (zoom) { 2150 return 256 * Math.pow(2, zoom); 2151 }, 2152 2153 // @method zoom(scale: Number): Number 2154 // Inverse of `scale()`, returns the zoom level corresponding to a scale 2155 // factor of `scale`. 2156 zoom: function (scale) { 2157 return Math.log(scale / 256) / Math.LN2; 2158 }, 2159 2160 // @method getProjectedBounds(zoom: Number): Bounds 2161 // Returns the projection's bounds scaled and transformed for the provided `zoom`. 2162 getProjectedBounds: function (zoom) { 2163 if (this.infinite) { return null; } 2164 2165 var b = this.projection.bounds, 2166 s = this.scale(zoom), 2167 min = this.transformation.transform(b.min, s), 2168 max = this.transformation.transform(b.max, s); 2169 2170 return new Bounds(min, max); 2171 }, 2172 2173 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 2174 // Returns the distance between two geographical coordinates. 2175 2176 // @property code: String 2177 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) 2178 // 2179 // @property wrapLng: Number[] 2180 // An array of two numbers defining whether the longitude (horizontal) coordinate 2181 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most 2182 // geographical CRSs. If `undefined`, the longitude axis does not wrap around. 2183 // 2184 // @property wrapLat: Number[] 2185 // Like `wrapLng`, but for the latitude (vertical) axis. 2186 2187 // wrapLng: [min, max], 2188 // wrapLat: [min, max], 2189 2190 // @property infinite: Boolean 2191 // If true, the coordinate space will be unbounded (infinite in both axes) 2192 infinite: false, 2193 2194 // @method wrapLatLng(latlng: LatLng): LatLng 2195 // Returns a `LatLng` where lat and lng has been wrapped according to the 2196 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. 2197 wrapLatLng: function (latlng) { 2198 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, 2199 lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, 2200 alt = latlng.alt; 2201 2202 return new LatLng(lat, lng, alt); 2203 }, 2204 2205 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 2206 // Returns a `LatLngBounds` with the same size as the given one, ensuring 2207 // that its center is within the CRS's bounds. 2208 // Only accepts actual `L.LatLngBounds` instances, not arrays. 2209 wrapLatLngBounds: function (bounds) { 2210 var center = bounds.getCenter(), 2211 newCenter = this.wrapLatLng(center), 2212 latShift = center.lat - newCenter.lat, 2213 lngShift = center.lng - newCenter.lng; 2214 2215 if (latShift === 0 && lngShift === 0) { 2216 return bounds; 2217 } 2218 2219 var sw = bounds.getSouthWest(), 2220 ne = bounds.getNorthEast(), 2221 newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), 2222 newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); 2223 2224 return new LatLngBounds(newSw, newNe); 2225 } 2226}; 2227 2228/* 2229 * @namespace CRS 2230 * @crs L.CRS.Earth 2231 * 2232 * Serves as the base for CRS that are global such that they cover the earth. 2233 * Can only be used as the base for other CRS and cannot be used directly, 2234 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns 2235 * meters. 2236 */ 2237 2238var Earth = extend({}, CRS, { 2239 wrapLng: [-180, 180], 2240 2241 // Mean Earth Radius, as recommended for use by 2242 // the International Union of Geodesy and Geophysics, 2243 // see http://rosettacode.org/wiki/Haversine_formula 2244 R: 6371000, 2245 2246 // distance between two geographical points using spherical law of cosines approximation 2247 distance: function (latlng1, latlng2) { 2248 var rad = Math.PI / 180, 2249 lat1 = latlng1.lat * rad, 2250 lat2 = latlng2.lat * rad, 2251 sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), 2252 sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), 2253 a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, 2254 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 2255 return this.R * c; 2256 } 2257}); 2258 2259/* 2260 * @namespace Projection 2261 * @projection L.Projection.SphericalMercator 2262 * 2263 * Spherical Mercator projection â the most common projection for online maps, 2264 * used by almost all free and commercial tile providers. Assumes that Earth is 2265 * a sphere. Used by the `EPSG:3857` CRS. 2266 */ 2267 2268var earthRadius = 6378137; 2269 2270var SphericalMercator = { 2271 2272 R: earthRadius, 2273 MAX_LATITUDE: 85.0511287798, 2274 2275 project: function (latlng) { 2276 var d = Math.PI / 180, 2277 max = this.MAX_LATITUDE, 2278 lat = Math.max(Math.min(max, latlng.lat), -max), 2279 sin = Math.sin(lat * d); 2280 2281 return new Point( 2282 this.R * latlng.lng * d, 2283 this.R * Math.log((1 + sin) / (1 - sin)) / 2); 2284 }, 2285 2286 unproject: function (point) { 2287 var d = 180 / Math.PI; 2288 2289 return new LatLng( 2290 (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, 2291 point.x * d / this.R); 2292 }, 2293 2294 bounds: (function () { 2295 var d = earthRadius * Math.PI; 2296 return new Bounds([-d, -d], [d, d]); 2297 })() 2298}; 2299 2300/* 2301 * @class Transformation 2302 * @aka L.Transformation 2303 * 2304 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` 2305 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing 2306 * the reverse. Used by Leaflet in its projections code. 2307 * 2308 * @example 2309 * 2310 * ```js 2311 * var transformation = L.transformation(2, 5, -1, 10), 2312 * p = L.point(1, 2), 2313 * p2 = transformation.transform(p), // L.point(7, 8) 2314 * p3 = transformation.untransform(p2); // L.point(1, 2) 2315 * ``` 2316 */ 2317 2318 2319// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) 2320// Creates a `Transformation` object with the given coefficients. 2321function Transformation(a, b, c, d) { 2322 if (isArray(a)) { 2323 // use array properties 2324 this._a = a[0]; 2325 this._b = a[1]; 2326 this._c = a[2]; 2327 this._d = a[3]; 2328 return; 2329 } 2330 this._a = a; 2331 this._b = b; 2332 this._c = c; 2333 this._d = d; 2334} 2335 2336Transformation.prototype = { 2337 // @method transform(point: Point, scale?: Number): Point 2338 // Returns a transformed point, optionally multiplied by the given scale. 2339 // Only accepts actual `L.Point` instances, not arrays. 2340 transform: function (point, scale) { // (Point, Number) -> Point 2341 return this._transform(point.clone(), scale); 2342 }, 2343 2344 // destructive transform (faster) 2345 _transform: function (point, scale) { 2346 scale = scale || 1; 2347 point.x = scale * (this._a * point.x + this._b); 2348 point.y = scale * (this._c * point.y + this._d); 2349 return point; 2350 }, 2351 2352 // @method untransform(point: Point, scale?: Number): Point 2353 // Returns the reverse transformation of the given point, optionally divided 2354 // by the given scale. Only accepts actual `L.Point` instances, not arrays. 2355 untransform: function (point, scale) { 2356 scale = scale || 1; 2357 return new Point( 2358 (point.x / scale - this._b) / this._a, 2359 (point.y / scale - this._d) / this._c); 2360 } 2361}; 2362 2363// factory L.transformation(a: Number, b: Number, c: Number, d: Number) 2364 2365// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) 2366// Instantiates a Transformation object with the given coefficients. 2367 2368// @alternative 2369// @factory L.transformation(coefficients: Array): Transformation 2370// Expects an coefficients array of the form 2371// `[a: Number, b: Number, c: Number, d: Number]`. 2372 2373function toTransformation(a, b, c, d) { 2374 return new Transformation(a, b, c, d); 2375} 2376 2377/* 2378 * @namespace CRS 2379 * @crs L.CRS.EPSG3857 2380 * 2381 * The most common CRS for online maps, used by almost all free and commercial 2382 * tile providers. Uses Spherical Mercator projection. Set in by default in 2383 * Map's `crs` option. 2384 */ 2385 2386var EPSG3857 = extend({}, Earth, { 2387 code: 'EPSG:3857', 2388 projection: SphericalMercator, 2389 2390 transformation: (function () { 2391 var scale = 0.5 / (Math.PI * SphericalMercator.R); 2392 return toTransformation(scale, 0.5, -scale, 0.5); 2393 }()) 2394}); 2395 2396var EPSG900913 = extend({}, EPSG3857, { 2397 code: 'EPSG:900913' 2398}); 2399 2400// @namespace SVG; @section 2401// There are several static functions which can be called without instantiating L.SVG: 2402 2403// @function create(name: String): SVGElement 2404// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), 2405// corresponding to the class name passed. For example, using 'line' will return 2406// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). 2407function svgCreate(name) { 2408 return document.createElementNS('http://www.w3.org/2000/svg', name); 2409} 2410 2411// @function pointsToPath(rings: Point[], closed: Boolean): String 2412// Generates a SVG path string for multiple rings, with each ring turning 2413// into "M..L..L.." instructions 2414function pointsToPath(rings, closed) { 2415 var str = '', 2416 i, j, len, len2, points, p; 2417 2418 for (i = 0, len = rings.length; i < len; i++) { 2419 points = rings[i]; 2420 2421 for (j = 0, len2 = points.length; j < len2; j++) { 2422 p = points[j]; 2423 str += (j ? 'L' : 'M') + p.x + ' ' + p.y; 2424 } 2425 2426 // closes the ring for polygons; "x" is VML syntax 2427 str += closed ? (svg ? 'z' : 'x') : ''; 2428 } 2429 2430 // SVG complains about empty path strings 2431 return str || 'M0 0'; 2432} 2433 2434/* 2435 * @namespace Browser 2436 * @aka L.Browser 2437 * 2438 * A namespace with static properties for browser/feature detection used by Leaflet internally. 2439 * 2440 * @example 2441 * 2442 * ```js 2443 * if (L.Browser.ielt9) { 2444 * alert('Upgrade your browser, dude!'); 2445 * } 2446 * ``` 2447 */ 2448 2449var style$1 = document.documentElement.style; 2450 2451// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). 2452var ie = 'ActiveXObject' in window; 2453 2454// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. 2455var ielt9 = ie && !document.addEventListener; 2456 2457// @property edge: Boolean; `true` for the Edge web browser. 2458var edge = 'msLaunchUri' in navigator && !('documentMode' in document); 2459 2460// @property webkit: Boolean; 2461// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). 2462var webkit = userAgentContains('webkit'); 2463 2464// @property android: Boolean 2465// `true` for any browser running on an Android platform. 2466var android = userAgentContains('android'); 2467 2468// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. 2469var android23 = userAgentContains('android 2') || userAgentContains('android 3'); 2470 2471/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ 2472var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit 2473// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) 2474var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); 2475 2476// @property opera: Boolean; `true` for the Opera browser 2477var opera = !!window.opera; 2478 2479// @property chrome: Boolean; `true` for the Chrome browser. 2480var chrome = userAgentContains('chrome'); 2481 2482// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. 2483var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; 2484 2485// @property safari: Boolean; `true` for the Safari browser. 2486var safari = !chrome && userAgentContains('safari'); 2487 2488var phantom = userAgentContains('phantom'); 2489 2490// @property opera12: Boolean 2491// `true` for the Opera browser supporting CSS transforms (version 12 or later). 2492var opera12 = 'OTransition' in style$1; 2493 2494// @property win: Boolean; `true` when the browser is running in a Windows platform 2495var win = navigator.platform.indexOf('Win') === 0; 2496 2497// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. 2498var ie3d = ie && ('transition' in style$1); 2499 2500// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. 2501var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; 2502 2503// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. 2504var gecko3d = 'MozPerspective' in style$1; 2505 2506// @property any3d: Boolean 2507// `true` for all browsers supporting CSS transforms. 2508var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; 2509 2510// @property mobile: Boolean; `true` for all browsers running in a mobile device. 2511var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); 2512 2513// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. 2514var mobileWebkit = mobile && webkit; 2515 2516// @property mobileWebkit3d: Boolean 2517// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. 2518var mobileWebkit3d = mobile && webkit3d; 2519 2520// @property msPointer: Boolean 2521// `true` for browsers implementing the Microsoft touch events model (notably IE10). 2522var msPointer = !window.PointerEvent && window.MSPointerEvent; 2523 2524// @property pointer: Boolean 2525// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). 2526var pointer = !webkit && !!(window.PointerEvent || msPointer); 2527 2528// @property touch: Boolean 2529// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). 2530// This does not necessarily mean that the browser is running in a computer with 2531// a touchscreen, it only means that the browser is capable of understanding 2532// touch events. 2533var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || 2534 (window.DocumentTouch && document instanceof window.DocumentTouch)); 2535 2536// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. 2537var mobileOpera = mobile && opera; 2538 2539// @property mobileGecko: Boolean 2540// `true` for gecko-based browsers running in a mobile device. 2541var mobileGecko = mobile && gecko; 2542 2543// @property retina: Boolean 2544// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. 2545var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; 2546 2547// @property passiveEvents: Boolean 2548// `true` for browsers that support passive events. 2549var passiveEvents = (function () { 2550 var supportsPassiveOption = false; 2551 try { 2552 var opts = Object.defineProperty({}, 'passive', { 2553 get: function () { 2554 supportsPassiveOption = true; 2555 } 2556 }); 2557 window.addEventListener('testPassiveEventSupport', falseFn, opts); 2558 window.removeEventListener('testPassiveEventSupport', falseFn, opts); 2559 } catch (e) { 2560 // Errors can safely be ignored since this is only a browser support test. 2561 } 2562 return supportsPassiveOption; 2563}); 2564 2565// @property canvas: Boolean 2566// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 2567var canvas = (function () { 2568 return !!document.createElement('canvas').getContext; 2569}()); 2570 2571// @property svg: Boolean 2572// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). 2573var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); 2574 2575// @property vml: Boolean 2576// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). 2577var vml = !svg && (function () { 2578 try { 2579 var div = document.createElement('div'); 2580 div.innerHTML = '<v:shape adj="1"/>'; 2581 2582 var shape = div.firstChild; 2583 shape.style.behavior = 'url(#default#VML)'; 2584 2585 return shape && (typeof shape.adj === 'object'); 2586 2587 } catch (e) { 2588 return false; 2589 } 2590}()); 2591 2592 2593function userAgentContains(str) { 2594 return navigator.userAgent.toLowerCase().indexOf(str) >= 0; 2595} 2596 2597 2598var Browser = (Object.freeze || Object)({ 2599 ie: ie, 2600 ielt9: ielt9, 2601 edge: edge, 2602 webkit: webkit, 2603 android: android, 2604 android23: android23, 2605 androidStock: androidStock, 2606 opera: opera, 2607 chrome: chrome, 2608 gecko: gecko, 2609 safari: safari, 2610 phantom: phantom, 2611 opera12: opera12, 2612 win: win, 2613 ie3d: ie3d, 2614 webkit3d: webkit3d, 2615 gecko3d: gecko3d, 2616 any3d: any3d, 2617 mobile: mobile, 2618 mobileWebkit: mobileWebkit, 2619 mobileWebkit3d: mobileWebkit3d, 2620 msPointer: msPointer, 2621 pointer: pointer, 2622 touch: touch, 2623 mobileOpera: mobileOpera, 2624 mobileGecko: mobileGecko, 2625 retina: retina, 2626 passiveEvents: passiveEvents, 2627 canvas: canvas, 2628 svg: svg, 2629 vml: vml 2630}); 2631 2632/* 2633 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. 2634 */ 2635 2636 2637var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; 2638var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; 2639var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; 2640var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; 2641var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; 2642 2643var _pointers = {}; 2644var _pointerDocListener = false; 2645 2646// DomEvent.DoubleTap needs to know about this 2647var _pointersCount = 0; 2648 2649// Provides a touch events wrapper for (ms)pointer events. 2650// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 2651 2652function addPointerListener(obj, type, handler, id) { 2653 if (type === 'touchstart') { 2654 _addPointerStart(obj, handler, id); 2655 2656 } else if (type === 'touchmove') { 2657 _addPointerMove(obj, handler, id); 2658 2659 } else if (type === 'touchend') { 2660 _addPointerEnd(obj, handler, id); 2661 } 2662 2663 return this; 2664} 2665 2666function removePointerListener(obj, type, id) { 2667 var handler = obj['_leaflet_' + type + id]; 2668 2669 if (type === 'touchstart') { 2670 obj.removeEventListener(POINTER_DOWN, handler, false); 2671 2672 } else if (type === 'touchmove') { 2673 obj.removeEventListener(POINTER_MOVE, handler, false); 2674 2675 } else if (type === 'touchend') { 2676 obj.removeEventListener(POINTER_UP, handler, false); 2677 obj.removeEventListener(POINTER_CANCEL, handler, false); 2678 } 2679 2680 return this; 2681} 2682 2683function _addPointerStart(obj, handler, id) { 2684 var onDown = bind(function (e) { 2685 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { 2686 // In IE11, some touch events needs to fire for form controls, or 2687 // the controls will stop working. We keep a whitelist of tag names that 2688 // need these events. For other target tags, we prevent default on the event. 2689 if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { 2690 preventDefault(e); 2691 } else { 2692 return; 2693 } 2694 } 2695 2696 _handlePointer(e, handler); 2697 }); 2698 2699 obj['_leaflet_touchstart' + id] = onDown; 2700 obj.addEventListener(POINTER_DOWN, onDown, false); 2701 2702 // need to keep track of what pointers and how many are active to provide e.touches emulation 2703 if (!_pointerDocListener) { 2704 // we listen documentElement as any drags that end by moving the touch off the screen get fired there 2705 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); 2706 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); 2707 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); 2708 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); 2709 2710 _pointerDocListener = true; 2711 } 2712} 2713 2714function _globalPointerDown(e) { 2715 _pointers[e.pointerId] = e; 2716 _pointersCount++; 2717} 2718 2719function _globalPointerMove(e) { 2720 if (_pointers[e.pointerId]) { 2721 _pointers[e.pointerId] = e; 2722 } 2723} 2724 2725function _globalPointerUp(e) { 2726 delete _pointers[e.pointerId]; 2727 _pointersCount--; 2728} 2729 2730function _handlePointer(e, handler) { 2731 e.touches = []; 2732 for (var i in _pointers) { 2733 e.touches.push(_pointers[i]); 2734 } 2735 e.changedTouches = [e]; 2736 2737 handler(e); 2738} 2739 2740function _addPointerMove(obj, handler, id) { 2741 var onMove = function (e) { 2742 // don't fire touch moves when mouse isn't down 2743 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } 2744 2745 _handlePointer(e, handler); 2746 }; 2747 2748 obj['_leaflet_touchmove' + id] = onMove; 2749 obj.addEventListener(POINTER_MOVE, onMove, false); 2750} 2751 2752function _addPointerEnd(obj, handler, id) { 2753 var onUp = function (e) { 2754 _handlePointer(e, handler); 2755 }; 2756 2757 obj['_leaflet_touchend' + id] = onUp; 2758 obj.addEventListener(POINTER_UP, onUp, false); 2759 obj.addEventListener(POINTER_CANCEL, onUp, false); 2760} 2761 2762/* 2763 * Extends the event handling code with double tap support for mobile browsers. 2764 */ 2765 2766var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; 2767var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; 2768var _pre = '_leaflet_'; 2769 2770// inspired by Zepto touch code by Thomas Fuchs 2771function addDoubleTapListener(obj, handler, id) { 2772 var last, touch$$1, 2773 doubleTap = false, 2774 delay = 250; 2775 2776 function onTouchStart(e) { 2777 var count; 2778 2779 if (pointer) { 2780 if ((!edge) || e.pointerType === 'mouse') { return; } 2781 count = _pointersCount; 2782 } else { 2783 count = e.touches.length; 2784 } 2785 2786 if (count > 1) { return; } 2787 2788 var now = Date.now(), 2789 delta = now - (last || now); 2790 2791 touch$$1 = e.touches ? e.touches[0] : e; 2792 doubleTap = (delta > 0 && delta <= delay); 2793 last = now; 2794 } 2795 2796 function onTouchEnd(e) { 2797 if (doubleTap && !touch$$1.cancelBubble) { 2798 if (pointer) { 2799 if ((!edge) || e.pointerType === 'mouse') { return; } 2800 // work around .type being readonly with MSPointer* events 2801 var newTouch = {}, 2802 prop, i; 2803 2804 for (i in touch$$1) { 2805 prop = touch$$1[i]; 2806 newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; 2807 } 2808 touch$$1 = newTouch; 2809 } 2810 touch$$1.type = 'dblclick'; 2811 touch$$1.button = 0; 2812 handler(touch$$1); 2813 last = null; 2814 } 2815 } 2816 2817 obj[_pre + _touchstart + id] = onTouchStart; 2818 obj[_pre + _touchend + id] = onTouchEnd; 2819 obj[_pre + 'dblclick' + id] = handler; 2820 2821 obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false); 2822 obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false); 2823 2824 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), 2825 // the browser doesn't fire touchend/pointerup events but does fire 2826 // native dblclicks. See #4127. 2827 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. 2828 obj.addEventListener('dblclick', handler, false); 2829 2830 return this; 2831} 2832 2833function removeDoubleTapListener(obj, id) { 2834 var touchstart = obj[_pre + _touchstart + id], 2835 touchend = obj[_pre + _touchend + id], 2836 dblclick = obj[_pre + 'dblclick' + id]; 2837 2838 obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false); 2839 obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false); 2840 if (!edge) { 2841 obj.removeEventListener('dblclick', dblclick, false); 2842 } 2843 2844 return this; 2845} 2846 2847/* 2848 * @namespace DomUtil 2849 * 2850 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) 2851 * tree, used by Leaflet internally. 2852 * 2853 * Most functions expecting or returning a `HTMLElement` also work for 2854 * SVG elements. The only difference is that classes refer to CSS classes 2855 * in HTML and SVG classes in SVG. 2856 */ 2857 2858 2859// @property TRANSFORM: String 2860// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). 2861var TRANSFORM = testProp( 2862 ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']); 2863 2864// webkitTransition comes first because some browser versions that drop vendor prefix don't do 2865// the same for the transitionend event, in particular the Android 4.1 stock browser 2866 2867// @property TRANSITION: String 2868// Vendor-prefixed transition style name. 2869var TRANSITION = testProp( 2870 ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); 2871 2872// @property TRANSITION_END: String 2873// Vendor-prefixed transitionend event name. 2874var TRANSITION_END = 2875 TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; 2876 2877 2878// @function get(id: String|HTMLElement): HTMLElement 2879// Returns an element given its DOM id, or returns the element itself 2880// if it was passed directly. 2881function get(id) { 2882 return typeof id === 'string' ? document.getElementById(id) : id; 2883} 2884 2885// @function getStyle(el: HTMLElement, styleAttrib: String): String 2886// Returns the value for a certain style attribute on an element, 2887// including computed values or values set through CSS. 2888function getStyle(el, style) { 2889 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); 2890 2891 if ((!value || value === 'auto') && document.defaultView) { 2892 var css = document.defaultView.getComputedStyle(el, null); 2893 value = css ? css[style] : null; 2894 } 2895 return value === 'auto' ? null : value; 2896} 2897 2898// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement 2899// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. 2900function create$1(tagName, className, container) { 2901 var el = document.createElement(tagName); 2902 el.className = className || ''; 2903 2904 if (container) { 2905 container.appendChild(el); 2906 } 2907 return el; 2908} 2909 2910// @function remove(el: HTMLElement) 2911// Removes `el` from its parent element 2912function remove(el) { 2913 var parent = el.parentNode; 2914 if (parent) { 2915 parent.removeChild(el); 2916 } 2917} 2918 2919// @function empty(el: HTMLElement) 2920// Removes all of `el`'s children elements from `el` 2921function empty(el) { 2922 while (el.firstChild) { 2923 el.removeChild(el.firstChild); 2924 } 2925} 2926 2927// @function toFront(el: HTMLElement) 2928// Makes `el` the last child of its parent, so it renders in front of the other children. 2929function toFront(el) { 2930 var parent = el.parentNode; 2931 if (parent && parent.lastChild !== el) { 2932 parent.appendChild(el); 2933 } 2934} 2935 2936// @function toBack(el: HTMLElement) 2937// Makes `el` the first child of its parent, so it renders behind the other children. 2938function toBack(el) { 2939 var parent = el.parentNode; 2940 if (parent && parent.firstChild !== el) { 2941 parent.insertBefore(el, parent.firstChild); 2942 } 2943} 2944 2945// @function hasClass(el: HTMLElement, name: String): Boolean 2946// Returns `true` if the element's class attribute contains `name`. 2947function hasClass(el, name) { 2948 if (el.classList !== undefined) { 2949 return el.classList.contains(name); 2950 } 2951 var className = getClass(el); 2952 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); 2953} 2954 2955// @function addClass(el: HTMLElement, name: String) 2956// Adds `name` to the element's class attribute. 2957function addClass(el, name) { 2958 if (el.classList !== undefined) { 2959 var classes = splitWords(name); 2960 for (var i = 0, len = classes.length; i < len; i++) { 2961 el.classList.add(classes[i]); 2962 } 2963 } else if (!hasClass(el, name)) { 2964 var className = getClass(el); 2965 setClass(el, (className ? className + ' ' : '') + name); 2966 } 2967} 2968 2969// @function removeClass(el: HTMLElement, name: String) 2970// Removes `name` from the element's class attribute. 2971function removeClass(el, name) { 2972 if (el.classList !== undefined) { 2973 el.classList.remove(name); 2974 } else { 2975 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); 2976 } 2977} 2978 2979// @function setClass(el: HTMLElement, name: String) 2980// Sets the element's class. 2981function setClass(el, name) { 2982 if (el.className.baseVal === undefined) { 2983 el.className = name; 2984 } else { 2985 // in case of SVG element 2986 el.className.baseVal = name; 2987 } 2988} 2989 2990// @function getClass(el: HTMLElement): String 2991// Returns the element's class. 2992function getClass(el) { 2993 // Check if the element is an SVGElementInstance and use the correspondingElement instead 2994 // (Required for linked SVG elements in IE11.) 2995 if (el.correspondingElement) { 2996 el = el.correspondingElement; 2997 } 2998 return el.className.baseVal === undefined ? el.className : el.className.baseVal; 2999} 3000 3001// @function setOpacity(el: HTMLElement, opacity: Number) 3002// Set the opacity of an element (including old IE support). 3003// `opacity` must be a number from `0` to `1`. 3004function setOpacity(el, value) { 3005 if ('opacity' in el.style) { 3006 el.style.opacity = value; 3007 } else if ('filter' in el.style) { 3008 _setOpacityIE(el, value); 3009 } 3010} 3011 3012function _setOpacityIE(el, value) { 3013 var filter = false, 3014 filterName = 'DXImageTransform.Microsoft.Alpha'; 3015 3016 // filters collection throws an error if we try to retrieve a filter that doesn't exist 3017 try { 3018 filter = el.filters.item(filterName); 3019 } catch (e) { 3020 // don't set opacity to 1 if we haven't already set an opacity, 3021 // it isn't needed and breaks transparent pngs. 3022 if (value === 1) { return; } 3023 } 3024 3025 value = Math.round(value * 100); 3026 3027 if (filter) { 3028 filter.Enabled = (value !== 100); 3029 filter.Opacity = value; 3030 } else { 3031 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; 3032 } 3033} 3034 3035// @function testProp(props: String[]): String|false 3036// Goes through the array of style names and returns the first name 3037// that is a valid style name for an element. If no such name is found, 3038// it returns false. Useful for vendor-prefixed styles like `transform`. 3039function testProp(props) { 3040 var style = document.documentElement.style; 3041 3042 for (var i = 0; i < props.length; i++) { 3043 if (props[i] in style) { 3044 return props[i]; 3045 } 3046 } 3047 return false; 3048} 3049 3050// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) 3051// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels 3052// and optionally scaled by `scale`. Does not have an effect if the 3053// browser doesn't support 3D CSS transforms. 3054function setTransform(el, offset, scale) { 3055 var pos = offset || new Point(0, 0); 3056 3057 el.style[TRANSFORM] = 3058 (ie3d ? 3059 'translate(' + pos.x + 'px,' + pos.y + 'px)' : 3060 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + 3061 (scale ? ' scale(' + scale + ')' : ''); 3062} 3063 3064// @function setPosition(el: HTMLElement, position: Point) 3065// Sets the position of `el` to coordinates specified by `position`, 3066// using CSS translate or top/left positioning depending on the browser 3067// (used by Leaflet internally to position its layers). 3068function setPosition(el, point) { 3069 3070 /*eslint-disable */ 3071 el._leaflet_pos = point; 3072 /* eslint-enable */ 3073 3074 if (any3d) { 3075 setTransform(el, point); 3076 } else { 3077 el.style.left = point.x + 'px'; 3078 el.style.top = point.y + 'px'; 3079 } 3080} 3081 3082// @function getPosition(el: HTMLElement): Point 3083// Returns the coordinates of an element previously positioned with setPosition. 3084function getPosition(el) { 3085 // this method is only used for elements previously positioned using setPosition, 3086 // so it's safe to cache the position for performance 3087 3088 return el._leaflet_pos || new Point(0, 0); 3089} 3090 3091// @function disableTextSelection() 3092// Prevents the user from generating `selectstart` DOM events, usually generated 3093// when the user drags the mouse through a page with text. Used internally 3094// by Leaflet to override the behaviour of any click-and-drag interaction on 3095// the map. Affects drag interactions on the whole document. 3096 3097// @function enableTextSelection() 3098// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). 3099var disableTextSelection; 3100var enableTextSelection; 3101var _userSelect; 3102if ('onselectstart' in document) { 3103 disableTextSelection = function () { 3104 on(window, 'selectstart', preventDefault); 3105 }; 3106 enableTextSelection = function () { 3107 off(window, 'selectstart', preventDefault); 3108 }; 3109} else { 3110 var userSelectProperty = testProp( 3111 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); 3112 3113 disableTextSelection = function () { 3114 if (userSelectProperty) { 3115 var style = document.documentElement.style; 3116 _userSelect = style[userSelectProperty]; 3117 style[userSelectProperty] = 'none'; 3118 } 3119 }; 3120 enableTextSelection = function () { 3121 if (userSelectProperty) { 3122 document.documentElement.style[userSelectProperty] = _userSelect; 3123 _userSelect = undefined; 3124 } 3125 }; 3126} 3127 3128// @function disableImageDrag() 3129// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but 3130// for `dragstart` DOM events, usually generated when the user drags an image. 3131function disableImageDrag() { 3132 on(window, 'dragstart', preventDefault); 3133} 3134 3135// @function enableImageDrag() 3136// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). 3137function enableImageDrag() { 3138 off(window, 'dragstart', preventDefault); 3139} 3140 3141var _outlineElement; 3142var _outlineStyle; 3143// @function preventOutline(el: HTMLElement) 3144// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) 3145// of the element `el` invisible. Used internally by Leaflet to prevent 3146// focusable elements from displaying an outline when the user performs a 3147// drag interaction on them. 3148function preventOutline(element) { 3149 while (element.tabIndex === -1) { 3150 element = element.parentNode; 3151 } 3152 if (!element.style) { return; } 3153 restoreOutline(); 3154 _outlineElement = element; 3155 _outlineStyle = element.style.outline; 3156 element.style.outline = 'none'; 3157 on(window, 'keydown', restoreOutline); 3158} 3159 3160// @function restoreOutline() 3161// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). 3162function restoreOutline() { 3163 if (!_outlineElement) { return; } 3164 _outlineElement.style.outline = _outlineStyle; 3165 _outlineElement = undefined; 3166 _outlineStyle = undefined; 3167 off(window, 'keydown', restoreOutline); 3168} 3169 3170// @function getSizedParentNode(el: HTMLElement): HTMLElement 3171// Finds the closest parent node which size (width and height) is not null. 3172function getSizedParentNode(element) { 3173 do { 3174 element = element.parentNode; 3175 } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body); 3176 return element; 3177} 3178 3179// @function getScale(el: HTMLElement): Object 3180// Computes the CSS scale currently applied on the element. 3181// Returns an object with `x` and `y` members as horizontal and vertical scales respectively, 3182// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). 3183function getScale(element) { 3184 var rect = element.getBoundingClientRect(); // Read-only in old browsers. 3185 3186 return { 3187 x: rect.width / element.offsetWidth || 1, 3188 y: rect.height / element.offsetHeight || 1, 3189 boundingClientRect: rect 3190 }; 3191} 3192 3193 3194var DomUtil = (Object.freeze || Object)({ 3195 TRANSFORM: TRANSFORM, 3196 TRANSITION: TRANSITION, 3197 TRANSITION_END: TRANSITION_END, 3198 get: get, 3199 getStyle: getStyle, 3200 create: create$1, 3201 remove: remove, 3202 empty: empty, 3203 toFront: toFront, 3204 toBack: toBack, 3205 hasClass: hasClass, 3206 addClass: addClass, 3207 removeClass: removeClass, 3208 setClass: setClass, 3209 getClass: getClass, 3210 setOpacity: setOpacity, 3211 testProp: testProp, 3212 setTransform: setTransform, 3213 setPosition: setPosition, 3214 getPosition: getPosition, 3215 disableTextSelection: disableTextSelection, 3216 enableTextSelection: enableTextSelection, 3217 disableImageDrag: disableImageDrag, 3218 enableImageDrag: enableImageDrag, 3219 preventOutline: preventOutline, 3220 restoreOutline: restoreOutline, 3221 getSizedParentNode: getSizedParentNode, 3222 getScale: getScale 3223}); 3224 3225/* 3226 * @namespace DomEvent 3227 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. 3228 */ 3229 3230// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. 3231 3232// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this 3233// Adds a listener function (`fn`) to a particular DOM event type of the 3234// element `el`. You can optionally specify the context of the listener 3235// (object the `this` keyword will point to). You can also pass several 3236// space-separated types (e.g. `'click dblclick'`). 3237 3238// @alternative 3239// @function on(el: HTMLElement, eventMap: Object, context?: Object): this 3240// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 3241function on(obj, types, fn, context) { 3242 3243 if (typeof types === 'object') { 3244 for (var type in types) { 3245 addOne(obj, type, types[type], fn); 3246 } 3247 } else { 3248 types = splitWords(types); 3249 3250 for (var i = 0, len = types.length; i < len; i++) { 3251 addOne(obj, types[i], fn, context); 3252 } 3253 } 3254 3255 return this; 3256} 3257 3258var eventsKey = '_leaflet_events'; 3259 3260// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this 3261// Removes a previously added listener function. 3262// Note that if you passed a custom context to on, you must pass the same 3263// context to `off` in order to remove the listener. 3264 3265// @alternative 3266// @function off(el: HTMLElement, eventMap: Object, context?: Object): this 3267// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 3268function off(obj, types, fn, context) { 3269 3270 if (typeof types === 'object') { 3271 for (var type in types) { 3272 removeOne(obj, type, types[type], fn); 3273 } 3274 } else if (types) { 3275 types = splitWords(types); 3276 3277 for (var i = 0, len = types.length; i < len; i++) { 3278 removeOne(obj, types[i], fn, context); 3279 } 3280 } else { 3281 for (var j in obj[eventsKey]) { 3282 removeOne(obj, j, obj[eventsKey][j]); 3283 } 3284 delete obj[eventsKey]; 3285 } 3286 3287 return this; 3288} 3289 3290function addOne(obj, type, fn, context) { 3291 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); 3292 3293 if (obj[eventsKey] && obj[eventsKey][id]) { return this; } 3294 3295 var handler = function (e) { 3296 return fn.call(context || obj, e || window.event); 3297 }; 3298 3299 var originalHandler = handler; 3300 3301 if (pointer && type.indexOf('touch') === 0) { 3302 // Needs DomEvent.Pointer.js 3303 addPointerListener(obj, type, handler, id); 3304 3305 } else if (touch && (type === 'dblclick') && addDoubleTapListener && 3306 !(pointer && chrome)) { 3307 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener 3308 // See #5180 3309 addDoubleTapListener(obj, handler, id); 3310 3311 } else if ('addEventListener' in obj) { 3312 3313 if (type === 'mousewheel') { 3314 obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false); 3315 3316 } else if ((type === 'mouseenter') || (type === 'mouseleave')) { 3317 handler = function (e) { 3318 e = e || window.event; 3319 if (isExternalTarget(obj, e)) { 3320 originalHandler(e); 3321 } 3322 }; 3323 obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); 3324 3325 } else { 3326 if (type === 'click' && android) { 3327 handler = function (e) { 3328 filterClick(e, originalHandler); 3329 }; 3330 } 3331 obj.addEventListener(type, handler, false); 3332 } 3333 3334 } else if ('attachEvent' in obj) { 3335 obj.attachEvent('on' + type, handler); 3336 } 3337 3338 obj[eventsKey] = obj[eventsKey] || {}; 3339 obj[eventsKey][id] = handler; 3340} 3341 3342function removeOne(obj, type, fn, context) { 3343 3344 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), 3345 handler = obj[eventsKey] && obj[eventsKey][id]; 3346 3347 if (!handler) { return this; } 3348 3349 if (pointer && type.indexOf('touch') === 0) { 3350 removePointerListener(obj, type, id); 3351 3352 } else if (touch && (type === 'dblclick') && removeDoubleTapListener && 3353 !(pointer && chrome)) { 3354 removeDoubleTapListener(obj, id); 3355 3356 } else if ('removeEventListener' in obj) { 3357 3358 if (type === 'mousewheel') { 3359 obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false); 3360 3361 } else { 3362 obj.removeEventListener( 3363 type === 'mouseenter' ? 'mouseover' : 3364 type === 'mouseleave' ? 'mouseout' : type, handler, false); 3365 } 3366 3367 } else if ('detachEvent' in obj) { 3368 obj.detachEvent('on' + type, handler); 3369 } 3370 3371 obj[eventsKey][id] = null; 3372} 3373 3374// @function stopPropagation(ev: DOMEvent): this 3375// Stop the given event from propagation to parent elements. Used inside the listener functions: 3376// ```js 3377// L.DomEvent.on(div, 'click', function (ev) { 3378// L.DomEvent.stopPropagation(ev); 3379// }); 3380// ``` 3381function stopPropagation(e) { 3382 3383 if (e.stopPropagation) { 3384 e.stopPropagation(); 3385 } else if (e.originalEvent) { // In case of Leaflet event. 3386 e.originalEvent._stopped = true; 3387 } else { 3388 e.cancelBubble = true; 3389 } 3390 skipped(e); 3391 3392 return this; 3393} 3394 3395// @function disableScrollPropagation(el: HTMLElement): this 3396// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). 3397function disableScrollPropagation(el) { 3398 addOne(el, 'mousewheel', stopPropagation); 3399 return this; 3400} 3401 3402// @function disableClickPropagation(el: HTMLElement): this 3403// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, 3404// `'mousedown'` and `'touchstart'` events (plus browser variants). 3405function disableClickPropagation(el) { 3406 on(el, 'mousedown touchstart dblclick', stopPropagation); 3407 addOne(el, 'click', fakeStop); 3408 return this; 3409} 3410 3411// @function preventDefault(ev: DOMEvent): this 3412// Prevents the default action of the DOM Event `ev` from happening (such as 3413// following a link in the href of the a element, or doing a POST request 3414// with page reload when a `<form>` is submitted). 3415// Use it inside listener functions. 3416function preventDefault(e) { 3417 if (e.preventDefault) { 3418 e.preventDefault(); 3419 } else { 3420 e.returnValue = false; 3421 } 3422 return this; 3423} 3424 3425// @function stop(ev: DOMEvent): this 3426// Does `stopPropagation` and `preventDefault` at the same time. 3427function stop(e) { 3428 preventDefault(e); 3429 stopPropagation(e); 3430 return this; 3431} 3432 3433// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point 3434// Gets normalized mouse position from a DOM event relative to the 3435// `container` (border excluded) or to the whole page if not specified. 3436function getMousePosition(e, container) { 3437 if (!container) { 3438 return new Point(e.clientX, e.clientY); 3439 } 3440 3441 var scale = getScale(container), 3442 offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y) 3443 3444 return new Point( 3445 // offset.left/top values are in page scale (like clientX/Y), 3446 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies). 3447 (e.clientX - offset.left) / scale.x - container.clientLeft, 3448 (e.clientY - offset.top) / scale.y - container.clientTop 3449 ); 3450} 3451 3452// Chrome on Win scrolls double the pixels as in other platforms (see #4538), 3453// and Firefox scrolls device pixels, not CSS pixels 3454var wheelPxFactor = 3455 (win && chrome) ? 2 * window.devicePixelRatio : 3456 gecko ? window.devicePixelRatio : 1; 3457 3458// @function getWheelDelta(ev: DOMEvent): Number 3459// Gets normalized wheel delta from a mousewheel DOM event, in vertical 3460// pixels scrolled (negative if scrolling down). 3461// Events from pointing devices without precise scrolling are mapped to 3462// a best guess of 60 pixels. 3463function getWheelDelta(e) { 3464 return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta 3465 (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels 3466 (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines 3467 (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages 3468 (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events 3469 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels 3470 (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines 3471 e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages 3472 0; 3473} 3474 3475var skipEvents = {}; 3476 3477function fakeStop(e) { 3478 // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) 3479 skipEvents[e.type] = true; 3480} 3481 3482function skipped(e) { 3483 var events = skipEvents[e.type]; 3484 // reset when checking, as it's only used in map container and propagates outside of the map 3485 skipEvents[e.type] = false; 3486 return events; 3487} 3488 3489// check if element really left/entered the event target (for mouseenter/mouseleave) 3490function isExternalTarget(el, e) { 3491 3492 var related = e.relatedTarget; 3493 3494 if (!related) { return true; } 3495 3496 try { 3497 while (related && (related !== el)) { 3498 related = related.parentNode; 3499 } 3500 } catch (err) { 3501 return false; 3502 } 3503 return (related !== el); 3504} 3505 3506var lastClick; 3507 3508// this is a horrible workaround for a bug in Android where a single touch triggers two click events 3509function filterClick(e, handler) { 3510 var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), 3511 elapsed = lastClick && (timeStamp - lastClick); 3512 3513 // are they closer together than 500ms yet more than 100ms? 3514 // Android typically triggers them ~300ms apart while multiple listeners 3515 // on the same event should be triggered far faster; 3516 // or check if click is simulated on the element, and if it is, reject any non-simulated events 3517 3518 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { 3519 stop(e); 3520 return; 3521 } 3522 lastClick = timeStamp; 3523 3524 handler(e); 3525} 3526 3527 3528 3529 3530var DomEvent = (Object.freeze || Object)({ 3531 on: on, 3532 off: off, 3533 stopPropagation: stopPropagation, 3534 disableScrollPropagation: disableScrollPropagation, 3535 disableClickPropagation: disableClickPropagation, 3536 preventDefault: preventDefault, 3537 stop: stop, 3538 getMousePosition: getMousePosition, 3539 getWheelDelta: getWheelDelta, 3540 fakeStop: fakeStop, 3541 skipped: skipped, 3542 isExternalTarget: isExternalTarget, 3543 addListener: on, 3544 removeListener: off 3545}); 3546 3547/* 3548 * @class PosAnimation 3549 * @aka L.PosAnimation 3550 * @inherits Evented 3551 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. 3552 * 3553 * @example 3554 * ```js 3555 * var fx = new L.PosAnimation(); 3556 * fx.run(el, [300, 500], 0.5); 3557 * ``` 3558 * 3559 * @constructor L.PosAnimation() 3560 * Creates a `PosAnimation` object. 3561 * 3562 */ 3563 3564var PosAnimation = Evented.extend({ 3565 3566 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) 3567 // Run an animation of a given element to a new position, optionally setting 3568 // duration in seconds (`0.25` by default) and easing linearity factor (3rd 3569 // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), 3570 // `0.5` by default). 3571 run: function (el, newPos, duration, easeLinearity) { 3572 this.stop(); 3573 3574 this._el = el; 3575 this._inProgress = true; 3576 this._duration = duration || 0.25; 3577 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); 3578 3579 this._startPos = getPosition(el); 3580 this._offset = newPos.subtract(this._startPos); 3581 this._startTime = +new Date(); 3582 3583 // @event start: Event 3584 // Fired when the animation starts 3585 this.fire('start'); 3586 3587 this._animate(); 3588 }, 3589 3590 // @method stop() 3591 // Stops the animation (if currently running). 3592 stop: function () { 3593 if (!this._inProgress) { return; } 3594 3595 this._step(true); 3596 this._complete(); 3597 }, 3598 3599 _animate: function () { 3600 // animation loop 3601 this._animId = requestAnimFrame(this._animate, this); 3602 this._step(); 3603 }, 3604 3605 _step: function (round) { 3606 var elapsed = (+new Date()) - this._startTime, 3607 duration = this._duration * 1000; 3608 3609 if (elapsed < duration) { 3610 this._runFrame(this._easeOut(elapsed / duration), round); 3611 } else { 3612 this._runFrame(1); 3613 this._complete(); 3614 } 3615 }, 3616 3617 _runFrame: function (progress, round) { 3618 var pos = this._startPos.add(this._offset.multiplyBy(progress)); 3619 if (round) { 3620 pos._round(); 3621 } 3622 setPosition(this._el, pos); 3623 3624 // @event step: Event 3625 // Fired continuously during the animation. 3626 this.fire('step'); 3627 }, 3628 3629 _complete: function () { 3630 cancelAnimFrame(this._animId); 3631 3632 this._inProgress = false; 3633 // @event end: Event 3634 // Fired when the animation ends. 3635 this.fire('end'); 3636 }, 3637 3638 _easeOut: function (t) { 3639 return 1 - Math.pow(1 - t, this._easeOutPower); 3640 } 3641}); 3642 3643/* 3644 * @class Map 3645 * @aka L.Map 3646 * @inherits Evented 3647 * 3648 * The central class of the API â it is used to create a map on a page and manipulate it. 3649 * 3650 * @example 3651 * 3652 * ```js 3653 * // initialize the map on the "map" div with a given center and zoom 3654 * var map = L.map('map', { 3655 * center: [51.505, -0.09], 3656 * zoom: 13 3657 * }); 3658 * ``` 3659 * 3660 */ 3661 3662var Map = Evented.extend({ 3663 3664 options: { 3665 // @section Map State Options 3666 // @option crs: CRS = L.CRS.EPSG3857 3667 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not 3668 // sure what it means. 3669 crs: EPSG3857, 3670 3671 // @option center: LatLng = undefined 3672 // Initial geographic center of the map 3673 center: undefined, 3674 3675 // @option zoom: Number = undefined 3676 // Initial map zoom level 3677 zoom: undefined, 3678 3679 // @option minZoom: Number = * 3680 // Minimum zoom level of the map. 3681 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3682 // the lowest of their `minZoom` options will be used instead. 3683 minZoom: undefined, 3684 3685 // @option maxZoom: Number = * 3686 // Maximum zoom level of the map. 3687 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3688 // the highest of their `maxZoom` options will be used instead. 3689 maxZoom: undefined, 3690 3691 // @option layers: Layer[] = [] 3692 // Array of layers that will be added to the map initially 3693 layers: [], 3694 3695 // @option maxBounds: LatLngBounds = null 3696 // When this option is set, the map restricts the view to the given 3697 // geographical bounds, bouncing the user back if the user tries to pan 3698 // outside the view. To set the restriction dynamically, use 3699 // [`setMaxBounds`](#map-setmaxbounds) method. 3700 maxBounds: undefined, 3701 3702 // @option renderer: Renderer = * 3703 // The default method for drawing vector layers on the map. `L.SVG` 3704 // or `L.Canvas` by default depending on browser support. 3705 renderer: undefined, 3706 3707 3708 // @section Animation Options 3709 // @option zoomAnimation: Boolean = true 3710 // Whether the map zoom animation is enabled. By default it's enabled 3711 // in all browsers that support CSS3 Transitions except Android. 3712 zoomAnimation: true, 3713 3714 // @option zoomAnimationThreshold: Number = 4 3715 // Won't animate zoom if the zoom difference exceeds this value. 3716 zoomAnimationThreshold: 4, 3717 3718 // @option fadeAnimation: Boolean = true 3719 // Whether the tile fade animation is enabled. By default it's enabled 3720 // in all browsers that support CSS3 Transitions except Android. 3721 fadeAnimation: true, 3722 3723 // @option markerZoomAnimation: Boolean = true 3724 // Whether markers animate their zoom with the zoom animation, if disabled 3725 // they will disappear for the length of the animation. By default it's 3726 // enabled in all browsers that support CSS3 Transitions except Android. 3727 markerZoomAnimation: true, 3728 3729 // @option transform3DLimit: Number = 2^23 3730 // Defines the maximum size of a CSS translation transform. The default 3731 // value should not be changed unless a web browser positions layers in 3732 // the wrong place after doing a large `panBy`. 3733 transform3DLimit: 8388608, // Precision limit of a 32-bit float 3734 3735 // @section Interaction Options 3736 // @option zoomSnap: Number = 1 3737 // Forces the map's zoom level to always be a multiple of this, particularly 3738 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. 3739 // By default, the zoom level snaps to the nearest integer; lower values 3740 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` 3741 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. 3742 zoomSnap: 1, 3743 3744 // @option zoomDelta: Number = 1 3745 // Controls how much the map's zoom level will change after a 3746 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` 3747 // or `-` on the keyboard, or using the [zoom controls](#control-zoom). 3748 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. 3749 zoomDelta: 1, 3750 3751 // @option trackResize: Boolean = true 3752 // Whether the map automatically handles browser window resize to update itself. 3753 trackResize: true 3754 }, 3755 3756 initialize: function (id, options) { // (HTMLElement or String, Object) 3757 options = setOptions(this, options); 3758 3759 // Make sure to assign internal flags at the beginning, 3760 // to avoid inconsistent state in some edge cases. 3761 this._handlers = []; 3762 this._layers = {}; 3763 this._zoomBoundLayers = {}; 3764 this._sizeChanged = true; 3765 3766 this._initContainer(id); 3767 this._initLayout(); 3768 3769 // hack for https://github.com/Leaflet/Leaflet/issues/1980 3770 this._onResize = bind(this._onResize, this); 3771 3772 this._initEvents(); 3773 3774 if (options.maxBounds) { 3775 this.setMaxBounds(options.maxBounds); 3776 } 3777 3778 if (options.zoom !== undefined) { 3779 this._zoom = this._limitZoom(options.zoom); 3780 } 3781 3782 if (options.center && options.zoom !== undefined) { 3783 this.setView(toLatLng(options.center), options.zoom, {reset: true}); 3784 } 3785 3786 this.callInitHooks(); 3787 3788 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera 3789 this._zoomAnimated = TRANSITION && any3d && !mobileOpera && 3790 this.options.zoomAnimation; 3791 3792 // zoom transitions run with the same duration for all layers, so if one of transitionend events 3793 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally 3794 if (this._zoomAnimated) { 3795 this._createAnimProxy(); 3796 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); 3797 } 3798 3799 this._addLayers(this.options.layers); 3800 }, 3801 3802 3803 // @section Methods for modifying map state 3804 3805 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this 3806 // Sets the view of the map (geographical center and zoom) with the given 3807 // animation options. 3808 setView: function (center, zoom, options) { 3809 3810 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); 3811 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); 3812 options = options || {}; 3813 3814 this._stop(); 3815 3816 if (this._loaded && !options.reset && options !== true) { 3817 3818 if (options.animate !== undefined) { 3819 options.zoom = extend({animate: options.animate}, options.zoom); 3820 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); 3821 } 3822 3823 // try animating pan or zoom 3824 var moved = (this._zoom !== zoom) ? 3825 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : 3826 this._tryAnimatedPan(center, options.pan); 3827 3828 if (moved) { 3829 // prevent resize handler call, the view will refresh after animation anyway 3830 clearTimeout(this._sizeTimer); 3831 return this; 3832 } 3833 } 3834 3835 // animation didn't start, just reset the map view 3836 this._resetView(center, zoom); 3837 3838 return this; 3839 }, 3840 3841 // @method setZoom(zoom: Number, options?: Zoom/pan options): this 3842 // Sets the zoom of the map. 3843 setZoom: function (zoom, options) { 3844 if (!this._loaded) { 3845 this._zoom = zoom; 3846 return this; 3847 } 3848 return this.setView(this.getCenter(), zoom, {zoom: options}); 3849 }, 3850 3851 // @method zoomIn(delta?: Number, options?: Zoom options): this 3852 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3853 zoomIn: function (delta, options) { 3854 delta = delta || (any3d ? this.options.zoomDelta : 1); 3855 return this.setZoom(this._zoom + delta, options); 3856 }, 3857 3858 // @method zoomOut(delta?: Number, options?: Zoom options): this 3859 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3860 zoomOut: function (delta, options) { 3861 delta = delta || (any3d ? this.options.zoomDelta : 1); 3862 return this.setZoom(this._zoom - delta, options); 3863 }, 3864 3865 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this 3866 // Zooms the map while keeping a specified geographical point on the map 3867 // stationary (e.g. used internally for scroll zoom and double-click zoom). 3868 // @alternative 3869 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this 3870 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. 3871 setZoomAround: function (latlng, zoom, options) { 3872 var scale = this.getZoomScale(zoom), 3873 viewHalf = this.getSize().divideBy(2), 3874 containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), 3875 3876 centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), 3877 newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); 3878 3879 return this.setView(newCenter, zoom, {zoom: options}); 3880 }, 3881 3882 _getBoundsCenterZoom: function (bounds, options) { 3883 3884 options = options || {}; 3885 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); 3886 3887 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 3888 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 3889 3890 zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); 3891 3892 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; 3893 3894 if (zoom === Infinity) { 3895 return { 3896 center: bounds.getCenter(), 3897 zoom: zoom 3898 }; 3899 } 3900 3901 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), 3902 3903 swPoint = this.project(bounds.getSouthWest(), zoom), 3904 nePoint = this.project(bounds.getNorthEast(), zoom), 3905 center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); 3906 3907 return { 3908 center: center, 3909 zoom: zoom 3910 }; 3911 }, 3912 3913 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this 3914 // Sets a map view that contains the given geographical bounds with the 3915 // maximum zoom level possible. 3916 fitBounds: function (bounds, options) { 3917 3918 bounds = toLatLngBounds(bounds); 3919 3920 if (!bounds.isValid()) { 3921 throw new Error('Bounds are not valid.'); 3922 } 3923 3924 var target = this._getBoundsCenterZoom(bounds, options); 3925 return this.setView(target.center, target.zoom, options); 3926 }, 3927 3928 // @method fitWorld(options?: fitBounds options): this 3929 // Sets a map view that mostly contains the whole world with the maximum 3930 // zoom level possible. 3931 fitWorld: function (options) { 3932 return this.fitBounds([[-90, -180], [90, 180]], options); 3933 }, 3934 3935 // @method panTo(latlng: LatLng, options?: Pan options): this 3936 // Pans the map to a given center. 3937 panTo: function (center, options) { // (LatLng) 3938 return this.setView(center, this._zoom, {pan: options}); 3939 }, 3940 3941 // @method panBy(offset: Point, options?: Pan options): this 3942 // Pans the map by a given number of pixels (animated). 3943 panBy: function (offset, options) { 3944 offset = toPoint(offset).round(); 3945 options = options || {}; 3946 3947 if (!offset.x && !offset.y) { 3948 return this.fire('moveend'); 3949 } 3950 // If we pan too far, Chrome gets issues with tiles 3951 // and makes them disappear or appear in the wrong place (slightly offset) #2602 3952 if (options.animate !== true && !this.getSize().contains(offset)) { 3953 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); 3954 return this; 3955 } 3956 3957 if (!this._panAnim) { 3958 this._panAnim = new PosAnimation(); 3959 3960 this._panAnim.on({ 3961 'step': this._onPanTransitionStep, 3962 'end': this._onPanTransitionEnd 3963 }, this); 3964 } 3965 3966 // don't fire movestart if animating inertia 3967 if (!options.noMoveStart) { 3968 this.fire('movestart'); 3969 } 3970 3971 // animate pan unless animate: false specified 3972 if (options.animate !== false) { 3973 addClass(this._mapPane, 'leaflet-pan-anim'); 3974 3975 var newPos = this._getMapPanePos().subtract(offset).round(); 3976 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); 3977 } else { 3978 this._rawPanBy(offset); 3979 this.fire('move').fire('moveend'); 3980 } 3981 3982 return this; 3983 }, 3984 3985 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this 3986 // Sets the view of the map (geographical center and zoom) performing a smooth 3987 // pan-zoom animation. 3988 flyTo: function (targetCenter, targetZoom, options) { 3989 3990 options = options || {}; 3991 if (options.animate === false || !any3d) { 3992 return this.setView(targetCenter, targetZoom, options); 3993 } 3994 3995 this._stop(); 3996 3997 var from = this.project(this.getCenter()), 3998 to = this.project(targetCenter), 3999 size = this.getSize(), 4000 startZoom = this._zoom; 4001 4002 targetCenter = toLatLng(targetCenter); 4003 targetZoom = targetZoom === undefined ? startZoom : targetZoom; 4004 4005 var w0 = Math.max(size.x, size.y), 4006 w1 = w0 * this.getZoomScale(startZoom, targetZoom), 4007 u1 = (to.distanceTo(from)) || 1, 4008 rho = 1.42, 4009 rho2 = rho * rho; 4010 4011 function r(i) { 4012 var s1 = i ? -1 : 1, 4013 s2 = i ? w1 : w0, 4014 t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, 4015 b1 = 2 * s2 * rho2 * u1, 4016 b = t1 / b1, 4017 sq = Math.sqrt(b * b + 1) - b; 4018 4019 // workaround for floating point precision bug when sq = 0, log = -Infinite, 4020 // thus triggering an infinite loop in flyTo 4021 var log = sq < 0.000000001 ? -18 : Math.log(sq); 4022 4023 return log; 4024 } 4025 4026 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } 4027 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } 4028 function tanh(n) { return sinh(n) / cosh(n); } 4029 4030 var r0 = r(0); 4031 4032 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } 4033 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } 4034 4035 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } 4036 4037 var start = Date.now(), 4038 S = (r(1) - r0) / rho, 4039 duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; 4040 4041 function frame() { 4042 var t = (Date.now() - start) / duration, 4043 s = easeOut(t) * S; 4044 4045 if (t <= 1) { 4046 this._flyToFrame = requestAnimFrame(frame, this); 4047 4048 this._move( 4049 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), 4050 this.getScaleZoom(w0 / w(s), startZoom), 4051 {flyTo: true}); 4052 4053 } else { 4054 this 4055 ._move(targetCenter, targetZoom) 4056 ._moveEnd(true); 4057 } 4058 } 4059 4060 this._moveStart(true, options.noMoveStart); 4061 4062 frame.call(this); 4063 return this; 4064 }, 4065 4066 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this 4067 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), 4068 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). 4069 flyToBounds: function (bounds, options) { 4070 var target = this._getBoundsCenterZoom(bounds, options); 4071 return this.flyTo(target.center, target.zoom, options); 4072 }, 4073 4074 // @method setMaxBounds(bounds: Bounds): this 4075 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). 4076 setMaxBounds: function (bounds) { 4077 bounds = toLatLngBounds(bounds); 4078 4079 if (!bounds.isValid()) { 4080 this.options.maxBounds = null; 4081 return this.off('moveend', this._panInsideMaxBounds); 4082 } else if (this.options.maxBounds) { 4083 this.off('moveend', this._panInsideMaxBounds); 4084 } 4085 4086 this.options.maxBounds = bounds; 4087 4088 if (this._loaded) { 4089 this._panInsideMaxBounds(); 4090 } 4091 4092 return this.on('moveend', this._panInsideMaxBounds); 4093 }, 4094 4095 // @method setMinZoom(zoom: Number): this 4096 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). 4097 setMinZoom: function (zoom) { 4098 var oldZoom = this.options.minZoom; 4099 this.options.minZoom = zoom; 4100 4101 if (this._loaded && oldZoom !== zoom) { 4102 this.fire('zoomlevelschange'); 4103 4104 if (this.getZoom() < this.options.minZoom) { 4105 return this.setZoom(zoom); 4106 } 4107 } 4108 4109 return this; 4110 }, 4111 4112 // @method setMaxZoom(zoom: Number): this 4113 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). 4114 setMaxZoom: function (zoom) { 4115 var oldZoom = this.options.maxZoom; 4116 this.options.maxZoom = zoom; 4117 4118 if (this._loaded && oldZoom !== zoom) { 4119 this.fire('zoomlevelschange'); 4120 4121 if (this.getZoom() > this.options.maxZoom) { 4122 return this.setZoom(zoom); 4123 } 4124 } 4125 4126 return this; 4127 }, 4128 4129 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this 4130 // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. 4131 panInsideBounds: function (bounds, options) { 4132 this._enforcingBounds = true; 4133 var center = this.getCenter(), 4134 newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); 4135 4136 if (!center.equals(newCenter)) { 4137 this.panTo(newCenter, options); 4138 } 4139 4140 this._enforcingBounds = false; 4141 return this; 4142 }, 4143 4144 // @method panInside(latlng: LatLng, options?: options): this 4145 // Pans the map the minimum amount to make the `latlng` visible. Use 4146 // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit 4147 // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds). 4148 // If `latlng` is already within the (optionally padded) display bounds, 4149 // the map will not be panned. 4150 panInside: function (latlng, options) { 4151 options = options || {}; 4152 4153 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 4154 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 4155 center = this.getCenter(), 4156 pixelCenter = this.project(center), 4157 pixelPoint = this.project(latlng), 4158 pixelBounds = this.getPixelBounds(), 4159 halfPixelBounds = pixelBounds.getSize().divideBy(2), 4160 paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]); 4161 4162 if (!paddedBounds.contains(pixelPoint)) { 4163 this._enforcingBounds = true; 4164 var diff = pixelCenter.subtract(pixelPoint), 4165 newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y); 4166 4167 if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) { 4168 newCenter.x = pixelCenter.x - diff.x; 4169 if (diff.x > 0) { 4170 newCenter.x += halfPixelBounds.x - paddingTL.x; 4171 } else { 4172 newCenter.x -= halfPixelBounds.x - paddingBR.x; 4173 } 4174 } 4175 if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) { 4176 newCenter.y = pixelCenter.y - diff.y; 4177 if (diff.y > 0) { 4178 newCenter.y += halfPixelBounds.y - paddingTL.y; 4179 } else { 4180 newCenter.y -= halfPixelBounds.y - paddingBR.y; 4181 } 4182 } 4183 this.panTo(this.unproject(newCenter), options); 4184 this._enforcingBounds = false; 4185 } 4186 return this; 4187 }, 4188 4189 // @method invalidateSize(options: Zoom/pan options): this 4190 // Checks if the map container size changed and updates the map if so â 4191 // call it after you've changed the map size dynamically, also animating 4192 // pan by default. If `options.pan` is `false`, panning will not occur. 4193 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so 4194 // that it doesn't happen often even if the method is called many 4195 // times in a row. 4196 4197 // @alternative 4198 // @method invalidateSize(animate: Boolean): this 4199 // Checks if the map container size changed and updates the map if so â 4200 // call it after you've changed the map size dynamically, also animating 4201 // pan by default. 4202 invalidateSize: function (options) { 4203 if (!this._loaded) { return this; } 4204 4205 options = extend({ 4206 animate: false, 4207 pan: true 4208 }, options === true ? {animate: true} : options); 4209 4210 var oldSize = this.getSize(); 4211 this._sizeChanged = true; 4212 this._lastCenter = null; 4213 4214 var newSize = this.getSize(), 4215 oldCenter = oldSize.divideBy(2).round(), 4216 newCenter = newSize.divideBy(2).round(), 4217 offset = oldCenter.subtract(newCenter); 4218 4219 if (!offset.x && !offset.y) { return this; } 4220 4221 if (options.animate && options.pan) { 4222 this.panBy(offset); 4223 4224 } else { 4225 if (options.pan) { 4226 this._rawPanBy(offset); 4227 } 4228 4229 this.fire('move'); 4230 4231 if (options.debounceMoveend) { 4232 clearTimeout(this._sizeTimer); 4233 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); 4234 } else { 4235 this.fire('moveend'); 4236 } 4237 } 4238 4239 // @section Map state change events 4240 // @event resize: ResizeEvent 4241 // Fired when the map is resized. 4242 return this.fire('resize', { 4243 oldSize: oldSize, 4244 newSize: newSize 4245 }); 4246 }, 4247 4248 // @section Methods for modifying map state 4249 // @method stop(): this 4250 // Stops the currently running `panTo` or `flyTo` animation, if any. 4251 stop: function () { 4252 this.setZoom(this._limitZoom(this._zoom)); 4253 if (!this.options.zoomSnap) { 4254 this.fire('viewreset'); 4255 } 4256 return this._stop(); 4257 }, 4258 4259 // @section Geolocation methods 4260 // @method locate(options?: Locate options): this 4261 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) 4262 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, 4263 // and optionally sets the map view to the user's location with respect to 4264 // detection accuracy (or to the world view if geolocation failed). 4265 // Note that, if your page doesn't use HTTPS, this method will fail in 4266 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) 4267 // See `Locate options` for more details. 4268 locate: function (options) { 4269 4270 options = this._locateOptions = extend({ 4271 timeout: 10000, 4272 watch: false 4273 // setView: false 4274 // maxZoom: <Number> 4275 // maximumAge: 0 4276 // enableHighAccuracy: false 4277 }, options); 4278 4279 if (!('geolocation' in navigator)) { 4280 this._handleGeolocationError({ 4281 code: 0, 4282 message: 'Geolocation not supported.' 4283 }); 4284 return this; 4285 } 4286 4287 var onResponse = bind(this._handleGeolocationResponse, this), 4288 onError = bind(this._handleGeolocationError, this); 4289 4290 if (options.watch) { 4291 this._locationWatchId = 4292 navigator.geolocation.watchPosition(onResponse, onError, options); 4293 } else { 4294 navigator.geolocation.getCurrentPosition(onResponse, onError, options); 4295 } 4296 return this; 4297 }, 4298 4299 // @method stopLocate(): this 4300 // Stops watching location previously initiated by `map.locate({watch: true})` 4301 // and aborts resetting the map view if map.locate was called with 4302 // `{setView: true}`. 4303 stopLocate: function () { 4304 if (navigator.geolocation && navigator.geolocation.clearWatch) { 4305 navigator.geolocation.clearWatch(this._locationWatchId); 4306 } 4307 if (this._locateOptions) { 4308 this._locateOptions.setView = false; 4309 } 4310 return this; 4311 }, 4312 4313 _handleGeolocationError: function (error) { 4314 var c = error.code, 4315 message = error.message || 4316 (c === 1 ? 'permission denied' : 4317 (c === 2 ? 'position unavailable' : 'timeout')); 4318 4319 if (this._locateOptions.setView && !this._loaded) { 4320 this.fitWorld(); 4321 } 4322 4323 // @section Location events 4324 // @event locationerror: ErrorEvent 4325 // Fired when geolocation (using the [`locate`](#map-locate) method) failed. 4326 this.fire('locationerror', { 4327 code: c, 4328 message: 'Geolocation error: ' + message + '.' 4329 }); 4330 }, 4331 4332 _handleGeolocationResponse: function (pos) { 4333 var lat = pos.coords.latitude, 4334 lng = pos.coords.longitude, 4335 latlng = new LatLng(lat, lng), 4336 bounds = latlng.toBounds(pos.coords.accuracy * 2), 4337 options = this._locateOptions; 4338 4339 if (options.setView) { 4340 var zoom = this.getBoundsZoom(bounds); 4341 this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); 4342 } 4343 4344 var data = { 4345 latlng: latlng, 4346 bounds: bounds, 4347 timestamp: pos.timestamp 4348 }; 4349 4350 for (var i in pos.coords) { 4351 if (typeof pos.coords[i] === 'number') { 4352 data[i] = pos.coords[i]; 4353 } 4354 } 4355 4356 // @event locationfound: LocationEvent 4357 // Fired when geolocation (using the [`locate`](#map-locate) method) 4358 // went successfully. 4359 this.fire('locationfound', data); 4360 }, 4361 4362 // TODO Appropriate docs section? 4363 // @section Other Methods 4364 // @method addHandler(name: String, HandlerClass: Function): this 4365 // Adds a new `Handler` to the map, given its name and constructor function. 4366 addHandler: function (name, HandlerClass) { 4367 if (!HandlerClass) { return this; } 4368 4369 var handler = this[name] = new HandlerClass(this); 4370 4371 this._handlers.push(handler); 4372 4373 if (this.options[name]) { 4374 handler.enable(); 4375 } 4376 4377 return this; 4378 }, 4379 4380 // @method remove(): this 4381 // Destroys the map and clears all related event listeners. 4382 remove: function () { 4383 4384 this._initEvents(true); 4385 4386 if (this._containerId !== this._container._leaflet_id) { 4387 throw new Error('Map container is being reused by another instance'); 4388 } 4389 4390 try { 4391 // throws error in IE6-8 4392 delete this._container._leaflet_id; 4393 delete this._containerId; 4394 } catch (e) { 4395 /*eslint-disable */ 4396 this._container._leaflet_id = undefined; 4397 /* eslint-enable */ 4398 this._containerId = undefined; 4399 } 4400 4401 if (this._locationWatchId !== undefined) { 4402 this.stopLocate(); 4403 } 4404 4405 this._stop(); 4406 4407 remove(this._mapPane); 4408 4409 if (this._clearControlPos) { 4410 this._clearControlPos(); 4411 } 4412 if (this._resizeRequest) { 4413 cancelAnimFrame(this._resizeRequest); 4414 this._resizeRequest = null; 4415 } 4416 4417 this._clearHandlers(); 4418 4419 if (this._loaded) { 4420 // @section Map state change events 4421 // @event unload: Event 4422 // Fired when the map is destroyed with [remove](#map-remove) method. 4423 this.fire('unload'); 4424 } 4425 4426 var i; 4427 for (i in this._layers) { 4428 this._layers[i].remove(); 4429 } 4430 for (i in this._panes) { 4431 remove(this._panes[i]); 4432 } 4433 4434 this._layers = []; 4435 this._panes = []; 4436 delete this._mapPane; 4437 delete this._renderer; 4438 4439 return this; 4440 }, 4441 4442 // @section Other Methods 4443 // @method createPane(name: String, container?: HTMLElement): HTMLElement 4444 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, 4445 // then returns it. The pane is created as a child of `container`, or 4446 // as a child of the main map pane if not set. 4447 createPane: function (name, container) { 4448 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), 4449 pane = create$1('div', className, container || this._mapPane); 4450 4451 if (name) { 4452 this._panes[name] = pane; 4453 } 4454 return pane; 4455 }, 4456 4457 // @section Methods for Getting Map State 4458 4459 // @method getCenter(): LatLng 4460 // Returns the geographical center of the map view 4461 getCenter: function () { 4462 this._checkIfLoaded(); 4463 4464 if (this._lastCenter && !this._moved()) { 4465 return this._lastCenter; 4466 } 4467 return this.layerPointToLatLng(this._getCenterLayerPoint()); 4468 }, 4469 4470 // @method getZoom(): Number 4471 // Returns the current zoom level of the map view 4472 getZoom: function () { 4473 return this._zoom; 4474 }, 4475 4476 // @method getBounds(): LatLngBounds 4477 // Returns the geographical bounds visible in the current map view 4478 getBounds: function () { 4479 var bounds = this.getPixelBounds(), 4480 sw = this.unproject(bounds.getBottomLeft()), 4481 ne = this.unproject(bounds.getTopRight()); 4482 4483 return new LatLngBounds(sw, ne); 4484 }, 4485 4486 // @method getMinZoom(): Number 4487 // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. 4488 getMinZoom: function () { 4489 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; 4490 }, 4491 4492 // @method getMaxZoom(): Number 4493 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). 4494 getMaxZoom: function () { 4495 return this.options.maxZoom === undefined ? 4496 (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : 4497 this.options.maxZoom; 4498 }, 4499 4500 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number 4501 // Returns the maximum zoom level on which the given bounds fit to the map 4502 // view in its entirety. If `inside` (optional) is set to `true`, the method 4503 // instead returns the minimum zoom level on which the map view fits into 4504 // the given bounds in its entirety. 4505 getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number 4506 bounds = toLatLngBounds(bounds); 4507 padding = toPoint(padding || [0, 0]); 4508 4509 var zoom = this.getZoom() || 0, 4510 min = this.getMinZoom(), 4511 max = this.getMaxZoom(), 4512 nw = bounds.getNorthWest(), 4513 se = bounds.getSouthEast(), 4514 size = this.getSize().subtract(padding), 4515 boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), 4516 snap = any3d ? this.options.zoomSnap : 1, 4517 scalex = size.x / boundsSize.x, 4518 scaley = size.y / boundsSize.y, 4519 scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); 4520 4521 zoom = this.getScaleZoom(scale, zoom); 4522 4523 if (snap) { 4524 zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level 4525 zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; 4526 } 4527 4528 return Math.max(min, Math.min(max, zoom)); 4529 }, 4530 4531 // @method getSize(): Point 4532 // Returns the current size of the map container (in pixels). 4533 getSize: function () { 4534 if (!this._size || this._sizeChanged) { 4535 this._size = new Point( 4536 this._container.clientWidth || 0, 4537 this._container.clientHeight || 0); 4538 4539 this._sizeChanged = false; 4540 } 4541 return this._size.clone(); 4542 }, 4543 4544 // @method getPixelBounds(): Bounds 4545 // Returns the bounds of the current map view in projected pixel 4546 // coordinates (sometimes useful in layer and overlay implementations). 4547 getPixelBounds: function (center, zoom) { 4548 var topLeftPoint = this._getTopLeftPoint(center, zoom); 4549 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); 4550 }, 4551 4552 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to 4553 // the map pane? "left point of the map layer" can be confusing, specially 4554 // since there can be negative offsets. 4555 // @method getPixelOrigin(): Point 4556 // Returns the projected pixel coordinates of the top left point of 4557 // the map layer (useful in custom layer and overlay implementations). 4558 getPixelOrigin: function () { 4559 this._checkIfLoaded(); 4560 return this._pixelOrigin; 4561 }, 4562 4563 // @method getPixelWorldBounds(zoom?: Number): Bounds 4564 // Returns the world's bounds in pixel coordinates for zoom level `zoom`. 4565 // If `zoom` is omitted, the map's current zoom level is used. 4566 getPixelWorldBounds: function (zoom) { 4567 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); 4568 }, 4569 4570 // @section Other Methods 4571 4572 // @method getPane(pane: String|HTMLElement): HTMLElement 4573 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). 4574 getPane: function (pane) { 4575 return typeof pane === 'string' ? this._panes[pane] : pane; 4576 }, 4577 4578 // @method getPanes(): Object 4579 // Returns a plain object containing the names of all [panes](#map-pane) as keys and 4580 // the panes as values. 4581 getPanes: function () { 4582 return this._panes; 4583 }, 4584 4585 // @method getContainer: HTMLElement 4586 // Returns the HTML element that contains the map. 4587 getContainer: function () { 4588 return this._container; 4589 }, 4590 4591 4592 // @section Conversion Methods 4593 4594 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number 4595 // Returns the scale factor to be applied to a map transition from zoom level 4596 // `fromZoom` to `toZoom`. Used internally to help with zoom animations. 4597 getZoomScale: function (toZoom, fromZoom) { 4598 // TODO replace with universal implementation after refactoring projections 4599 var crs = this.options.crs; 4600 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 4601 return crs.scale(toZoom) / crs.scale(fromZoom); 4602 }, 4603 4604 // @method getScaleZoom(scale: Number, fromZoom: Number): Number 4605 // Returns the zoom level that the map would end up at, if it is at `fromZoom` 4606 // level and everything is scaled by a factor of `scale`. Inverse of 4607 // [`getZoomScale`](#map-getZoomScale). 4608 getScaleZoom: function (scale, fromZoom) { 4609 var crs = this.options.crs; 4610 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 4611 var zoom = crs.zoom(scale * crs.scale(fromZoom)); 4612 return isNaN(zoom) ? Infinity : zoom; 4613 }, 4614 4615 // @method project(latlng: LatLng, zoom: Number): Point 4616 // Projects a geographical coordinate `LatLng` according to the projection 4617 // of the map's CRS, then scales it according to `zoom` and the CRS's 4618 // `Transformation`. The result is pixel coordinate relative to 4619 // the CRS origin. 4620 project: function (latlng, zoom) { 4621 zoom = zoom === undefined ? this._zoom : zoom; 4622 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); 4623 }, 4624 4625 // @method unproject(point: Point, zoom: Number): LatLng 4626 // Inverse of [`project`](#map-project). 4627 unproject: function (point, zoom) { 4628 zoom = zoom === undefined ? this._zoom : zoom; 4629 return this.options.crs.pointToLatLng(toPoint(point), zoom); 4630 }, 4631 4632 // @method layerPointToLatLng(point: Point): LatLng 4633 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 4634 // returns the corresponding geographical coordinate (for the current zoom level). 4635 layerPointToLatLng: function (point) { 4636 var projectedPoint = toPoint(point).add(this.getPixelOrigin()); 4637 return this.unproject(projectedPoint); 4638 }, 4639 4640 // @method latLngToLayerPoint(latlng: LatLng): Point 4641 // Given a geographical coordinate, returns the corresponding pixel coordinate 4642 // relative to the [origin pixel](#map-getpixelorigin). 4643 latLngToLayerPoint: function (latlng) { 4644 var projectedPoint = this.project(toLatLng(latlng))._round(); 4645 return projectedPoint._subtract(this.getPixelOrigin()); 4646 }, 4647 4648 // @method wrapLatLng(latlng: LatLng): LatLng 4649 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the 4650 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the 4651 // CRS's bounds. 4652 // By default this means longitude is wrapped around the dateline so its 4653 // value is between -180 and +180 degrees. 4654 wrapLatLng: function (latlng) { 4655 return this.options.crs.wrapLatLng(toLatLng(latlng)); 4656 }, 4657 4658 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 4659 // Returns a `LatLngBounds` with the same size as the given one, ensuring that 4660 // its center is within the CRS's bounds. 4661 // By default this means the center longitude is wrapped around the dateline so its 4662 // value is between -180 and +180 degrees, and the majority of the bounds 4663 // overlaps the CRS's bounds. 4664 wrapLatLngBounds: function (latlng) { 4665 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); 4666 }, 4667 4668 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 4669 // Returns the distance between two geographical coordinates according to 4670 // the map's CRS. By default this measures distance in meters. 4671 distance: function (latlng1, latlng2) { 4672 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); 4673 }, 4674 4675 // @method containerPointToLayerPoint(point: Point): Point 4676 // Given a pixel coordinate relative to the map container, returns the corresponding 4677 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). 4678 containerPointToLayerPoint: function (point) { // (Point) 4679 return toPoint(point).subtract(this._getMapPanePos()); 4680 }, 4681 4682 // @method layerPointToContainerPoint(point: Point): Point 4683 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 4684 // returns the corresponding pixel coordinate relative to the map container. 4685 layerPointToContainerPoint: function (point) { // (Point) 4686 return toPoint(point).add(this._getMapPanePos()); 4687 }, 4688 4689 // @method containerPointToLatLng(point: Point): LatLng 4690 // Given a pixel coordinate relative to the map container, returns 4691 // the corresponding geographical coordinate (for the current zoom level). 4692 containerPointToLatLng: function (point) { 4693 var layerPoint = this.containerPointToLayerPoint(toPoint(point)); 4694 return this.layerPointToLatLng(layerPoint); 4695 }, 4696 4697 // @method latLngToContainerPoint(latlng: LatLng): Point 4698 // Given a geographical coordinate, returns the corresponding pixel coordinate 4699 // relative to the map container. 4700 latLngToContainerPoint: function (latlng) { 4701 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); 4702 }, 4703 4704 // @method mouseEventToContainerPoint(ev: MouseEvent): Point 4705 // Given a MouseEvent object, returns the pixel coordinate relative to the 4706 // map container where the event took place. 4707 mouseEventToContainerPoint: function (e) { 4708 return getMousePosition(e, this._container); 4709 }, 4710 4711 // @method mouseEventToLayerPoint(ev: MouseEvent): Point 4712 // Given a MouseEvent object, returns the pixel coordinate relative to 4713 // the [origin pixel](#map-getpixelorigin) where the event took place. 4714 mouseEventToLayerPoint: function (e) { 4715 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); 4716 }, 4717 4718 // @method mouseEventToLatLng(ev: MouseEvent): LatLng 4719 // Given a MouseEvent object, returns geographical coordinate where the 4720 // event took place. 4721 mouseEventToLatLng: function (e) { // (MouseEvent) 4722 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); 4723 }, 4724 4725 4726 // map initialization methods 4727 4728 _initContainer: function (id) { 4729 var container = this._container = get(id); 4730 4731 if (!container) { 4732 throw new Error('Map container not found.'); 4733 } else if (container._leaflet_id) { 4734 throw new Error('Map container is already initialized.'); 4735 } 4736 4737 on(container, 'scroll', this._onScroll, this); 4738 this._containerId = stamp(container); 4739 }, 4740 4741 _initLayout: function () { 4742 var container = this._container; 4743 4744 this._fadeAnimated = this.options.fadeAnimation && any3d; 4745 4746 addClass(container, 'leaflet-container' + 4747 (touch ? ' leaflet-touch' : '') + 4748 (retina ? ' leaflet-retina' : '') + 4749 (ielt9 ? ' leaflet-oldie' : '') + 4750 (safari ? ' leaflet-safari' : '') + 4751 (this._fadeAnimated ? ' leaflet-fade-anim' : '')); 4752 4753 var position = getStyle(container, 'position'); 4754 4755 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { 4756 container.style.position = 'relative'; 4757 } 4758 4759 this._initPanes(); 4760 4761 if (this._initControlPos) { 4762 this._initControlPos(); 4763 } 4764 }, 4765 4766 _initPanes: function () { 4767 var panes = this._panes = {}; 4768 this._paneRenderers = {}; 4769 4770 // @section 4771 // 4772 // Panes are DOM elements used to control the ordering of layers on the map. You 4773 // can access panes with [`map.getPane`](#map-getpane) or 4774 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the 4775 // [`map.createPane`](#map-createpane) method. 4776 // 4777 // Every map has the following default panes that differ only in zIndex. 4778 // 4779 // @pane mapPane: HTMLElement = 'auto' 4780 // Pane that contains all other map panes 4781 4782 this._mapPane = this.createPane('mapPane', this._container); 4783 setPosition(this._mapPane, new Point(0, 0)); 4784 4785 // @pane tilePane: HTMLElement = 200 4786 // Pane for `GridLayer`s and `TileLayer`s 4787 this.createPane('tilePane'); 4788 // @pane overlayPane: HTMLElement = 400 4789 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s 4790 this.createPane('shadowPane'); 4791 // @pane shadowPane: HTMLElement = 500 4792 // Pane for overlay shadows (e.g. `Marker` shadows) 4793 this.createPane('overlayPane'); 4794 // @pane markerPane: HTMLElement = 600 4795 // Pane for `Icon`s of `Marker`s 4796 this.createPane('markerPane'); 4797 // @pane tooltipPane: HTMLElement = 650 4798 // Pane for `Tooltip`s. 4799 this.createPane('tooltipPane'); 4800 // @pane popupPane: HTMLElement = 700 4801 // Pane for `Popup`s. 4802 this.createPane('popupPane'); 4803 4804 if (!this.options.markerZoomAnimation) { 4805 addClass(panes.markerPane, 'leaflet-zoom-hide'); 4806 addClass(panes.shadowPane, 'leaflet-zoom-hide'); 4807 } 4808 }, 4809 4810 4811 // private methods that modify map state 4812 4813 // @section Map state change events 4814 _resetView: function (center, zoom) { 4815 setPosition(this._mapPane, new Point(0, 0)); 4816 4817 var loading = !this._loaded; 4818 this._loaded = true; 4819 zoom = this._limitZoom(zoom); 4820 4821 this.fire('viewprereset'); 4822 4823 var zoomChanged = this._zoom !== zoom; 4824 this 4825 ._moveStart(zoomChanged, false) 4826 ._move(center, zoom) 4827 ._moveEnd(zoomChanged); 4828 4829 // @event viewreset: Event 4830 // Fired when the map needs to redraw its content (this usually happens 4831 // on map zoom or load). Very useful for creating custom overlays. 4832 this.fire('viewreset'); 4833 4834 // @event load: Event 4835 // Fired when the map is initialized (when its center and zoom are set 4836 // for the first time). 4837 if (loading) { 4838 this.fire('load'); 4839 } 4840 }, 4841 4842 _moveStart: function (zoomChanged, noMoveStart) { 4843 // @event zoomstart: Event 4844 // Fired when the map zoom is about to change (e.g. before zoom animation). 4845 // @event movestart: Event 4846 // Fired when the view of the map starts changing (e.g. user starts dragging the map). 4847 if (zoomChanged) { 4848 this.fire('zoomstart'); 4849 } 4850 if (!noMoveStart) { 4851 this.fire('movestart'); 4852 } 4853 return this; 4854 }, 4855 4856 _move: function (center, zoom, data) { 4857 if (zoom === undefined) { 4858 zoom = this._zoom; 4859 } 4860 var zoomChanged = this._zoom !== zoom; 4861 4862 this._zoom = zoom; 4863 this._lastCenter = center; 4864 this._pixelOrigin = this._getNewPixelOrigin(center); 4865 4866 // @event zoom: Event 4867 // Fired repeatedly during any change in zoom level, including zoom 4868 // and fly animations. 4869 if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 4870 this.fire('zoom', data); 4871 } 4872 4873 // @event move: Event 4874 // Fired repeatedly during any movement of the map, including pan and 4875 // fly animations. 4876 return this.fire('move', data); 4877 }, 4878 4879 _moveEnd: function (zoomChanged) { 4880 // @event zoomend: Event 4881 // Fired when the map has changed, after any animations. 4882 if (zoomChanged) { 4883 this.fire('zoomend'); 4884 } 4885 4886 // @event moveend: Event 4887 // Fired when the center of the map stops changing (e.g. user stopped 4888 // dragging the map). 4889 return this.fire('moveend'); 4890 }, 4891 4892 _stop: function () { 4893 cancelAnimFrame(this._flyToFrame); 4894 if (this._panAnim) { 4895 this._panAnim.stop(); 4896 } 4897 return this; 4898 }, 4899 4900 _rawPanBy: function (offset) { 4901 setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); 4902 }, 4903 4904 _getZoomSpan: function () { 4905 return this.getMaxZoom() - this.getMinZoom(); 4906 }, 4907 4908 _panInsideMaxBounds: function () { 4909 if (!this._enforcingBounds) { 4910 this.panInsideBounds(this.options.maxBounds); 4911 } 4912 }, 4913 4914 _checkIfLoaded: function () { 4915 if (!this._loaded) { 4916 throw new Error('Set map center and zoom first.'); 4917 } 4918 }, 4919 4920 // DOM event handling 4921 4922 // @section Interaction events 4923 _initEvents: function (remove$$1) { 4924 this._targets = {}; 4925 this._targets[stamp(this._container)] = this; 4926 4927 var onOff = remove$$1 ? off : on; 4928 4929 // @event click: MouseEvent 4930 // Fired when the user clicks (or taps) the map. 4931 // @event dblclick: MouseEvent 4932 // Fired when the user double-clicks (or double-taps) the map. 4933 // @event mousedown: MouseEvent 4934 // Fired when the user pushes the mouse button on the map. 4935 // @event mouseup: MouseEvent 4936 // Fired when the user releases the mouse button on the map. 4937 // @event mouseover: MouseEvent 4938 // Fired when the mouse enters the map. 4939 // @event mouseout: MouseEvent 4940 // Fired when the mouse leaves the map. 4941 // @event mousemove: MouseEvent 4942 // Fired while the mouse moves over the map. 4943 // @event contextmenu: MouseEvent 4944 // Fired when the user pushes the right mouse button on the map, prevents 4945 // default browser context menu from showing if there are listeners on 4946 // this event. Also fired on mobile when the user holds a single touch 4947 // for a second (also called long press). 4948 // @event keypress: KeyboardEvent 4949 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused. 4950 // @event keydown: KeyboardEvent 4951 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event, 4952 // the `keydown` event is fired for keys that produce a character value and for keys 4953 // that do not produce a character value. 4954 // @event keyup: KeyboardEvent 4955 // Fired when the user releases a key from the keyboard while the map is focused. 4956 onOff(this._container, 'click dblclick mousedown mouseup ' + 4957 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this); 4958 4959 if (this.options.trackResize) { 4960 onOff(window, 'resize', this._onResize, this); 4961 } 4962 4963 if (any3d && this.options.transform3DLimit) { 4964 (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); 4965 } 4966 }, 4967 4968 _onResize: function () { 4969 cancelAnimFrame(this._resizeRequest); 4970 this._resizeRequest = requestAnimFrame( 4971 function () { this.invalidateSize({debounceMoveend: true}); }, this); 4972 }, 4973 4974 _onScroll: function () { 4975 this._container.scrollTop = 0; 4976 this._container.scrollLeft = 0; 4977 }, 4978 4979 _onMoveEnd: function () { 4980 var pos = this._getMapPanePos(); 4981 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { 4982 // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have 4983 // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ 4984 this._resetView(this.getCenter(), this.getZoom()); 4985 } 4986 }, 4987 4988 _findEventTargets: function (e, type) { 4989 var targets = [], 4990 target, 4991 isHover = type === 'mouseout' || type === 'mouseover', 4992 src = e.target || e.srcElement, 4993 dragging = false; 4994 4995 while (src) { 4996 target = this._targets[stamp(src)]; 4997 if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { 4998 // Prevent firing click after you just dragged an object. 4999 dragging = true; 5000 break; 5001 } 5002 if (target && target.listens(type, true)) { 5003 if (isHover && !isExternalTarget(src, e)) { break; } 5004 targets.push(target); 5005 if (isHover) { break; } 5006 } 5007 if (src === this._container) { break; } 5008 src = src.parentNode; 5009 } 5010 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { 5011 targets = [this]; 5012 } 5013 return targets; 5014 }, 5015 5016 _handleDOMEvent: function (e) { 5017 if (!this._loaded || skipped(e)) { return; } 5018 5019 var type = e.type; 5020 5021 if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') { 5022 // prevents outline when clicking on keyboard-focusable element 5023 preventOutline(e.target || e.srcElement); 5024 } 5025 5026 this._fireDOMEvent(e, type); 5027 }, 5028 5029 _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], 5030 5031 _fireDOMEvent: function (e, type, targets) { 5032 5033 if (e.type === 'click') { 5034 // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). 5035 // @event preclick: MouseEvent 5036 // Fired before mouse click on the map (sometimes useful when you 5037 // want something to happen on click before any existing click 5038 // handlers start running). 5039 var synth = extend({}, e); 5040 synth.type = 'preclick'; 5041 this._fireDOMEvent(synth, synth.type, targets); 5042 } 5043 5044 if (e._stopped) { return; } 5045 5046 // Find the layer the event is propagating from and its parents. 5047 targets = (targets || []).concat(this._findEventTargets(e, type)); 5048 5049 if (!targets.length) { return; } 5050 5051 var target = targets[0]; 5052 if (type === 'contextmenu' && target.listens(type, true)) { 5053 preventDefault(e); 5054 } 5055 5056 var data = { 5057 originalEvent: e 5058 }; 5059 5060 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') { 5061 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); 5062 data.containerPoint = isMarker ? 5063 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); 5064 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); 5065 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); 5066 } 5067 5068 for (var i = 0; i < targets.length; i++) { 5069 targets[i].fire(type, data, true); 5070 if (data.originalEvent._stopped || 5071 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } 5072 } 5073 }, 5074 5075 _draggableMoved: function (obj) { 5076 obj = obj.dragging && obj.dragging.enabled() ? obj : this; 5077 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); 5078 }, 5079 5080 _clearHandlers: function () { 5081 for (var i = 0, len = this._handlers.length; i < len; i++) { 5082 this._handlers[i].disable(); 5083 } 5084 }, 5085 5086 // @section Other Methods 5087 5088 // @method whenReady(fn: Function, context?: Object): this 5089 // Runs the given function `fn` when the map gets initialized with 5090 // a view (center and zoom) and at least one layer, or immediately 5091 // if it's already initialized, optionally passing a function context. 5092 whenReady: function (callback, context) { 5093 if (this._loaded) { 5094 callback.call(context || this, {target: this}); 5095 } else { 5096 this.on('load', callback, context); 5097 } 5098 return this; 5099 }, 5100 5101 5102 // private methods for getting map state 5103 5104 _getMapPanePos: function () { 5105 return getPosition(this._mapPane) || new Point(0, 0); 5106 }, 5107 5108 _moved: function () { 5109 var pos = this._getMapPanePos(); 5110 return pos && !pos.equals([0, 0]); 5111 }, 5112 5113 _getTopLeftPoint: function (center, zoom) { 5114 var pixelOrigin = center && zoom !== undefined ? 5115 this._getNewPixelOrigin(center, zoom) : 5116 this.getPixelOrigin(); 5117 return pixelOrigin.subtract(this._getMapPanePos()); 5118 }, 5119 5120 _getNewPixelOrigin: function (center, zoom) { 5121 var viewHalf = this.getSize()._divideBy(2); 5122 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); 5123 }, 5124 5125 _latLngToNewLayerPoint: function (latlng, zoom, center) { 5126 var topLeft = this._getNewPixelOrigin(center, zoom); 5127 return this.project(latlng, zoom)._subtract(topLeft); 5128 }, 5129 5130 _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { 5131 var topLeft = this._getNewPixelOrigin(center, zoom); 5132 return toBounds([ 5133 this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), 5134 this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), 5135 this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), 5136 this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) 5137 ]); 5138 }, 5139 5140 // layer point of the current center 5141 _getCenterLayerPoint: function () { 5142 return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); 5143 }, 5144 5145 // offset of the specified place to the current center in pixels 5146 _getCenterOffset: function (latlng) { 5147 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); 5148 }, 5149 5150 // adjust center for view to get inside bounds 5151 _limitCenter: function (center, zoom, bounds) { 5152 5153 if (!bounds) { return center; } 5154 5155 var centerPoint = this.project(center, zoom), 5156 viewHalf = this.getSize().divideBy(2), 5157 viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), 5158 offset = this._getBoundsOffset(viewBounds, bounds, zoom); 5159 5160 // If offset is less than a pixel, ignore. 5161 // This prevents unstable projections from getting into 5162 // an infinite loop of tiny offsets. 5163 if (offset.round().equals([0, 0])) { 5164 return center; 5165 } 5166 5167 return this.unproject(centerPoint.add(offset), zoom); 5168 }, 5169 5170 // adjust offset for view to get inside bounds 5171 _limitOffset: function (offset, bounds) { 5172 if (!bounds) { return offset; } 5173 5174 var viewBounds = this.getPixelBounds(), 5175 newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); 5176 5177 return offset.add(this._getBoundsOffset(newBounds, bounds)); 5178 }, 5179 5180 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom 5181 _getBoundsOffset: function (pxBounds, maxBounds, zoom) { 5182 var projectedMaxBounds = toBounds( 5183 this.project(maxBounds.getNorthEast(), zoom), 5184 this.project(maxBounds.getSouthWest(), zoom) 5185 ), 5186 minOffset = projectedMaxBounds.min.subtract(pxBounds.min), 5187 maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), 5188 5189 dx = this._rebound(minOffset.x, -maxOffset.x), 5190 dy = this._rebound(minOffset.y, -maxOffset.y); 5191 5192 return new Point(dx, dy); 5193 }, 5194 5195 _rebound: function (left, right) { 5196 return left + right > 0 ? 5197 Math.round(left - right) / 2 : 5198 Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); 5199 }, 5200 5201 _limitZoom: function (zoom) { 5202 var min = this.getMinZoom(), 5203 max = this.getMaxZoom(), 5204 snap = any3d ? this.options.zoomSnap : 1; 5205 if (snap) { 5206 zoom = Math.round(zoom / snap) * snap; 5207 } 5208 return Math.max(min, Math.min(max, zoom)); 5209 }, 5210 5211 _onPanTransitionStep: function () { 5212 this.fire('move'); 5213 }, 5214 5215 _onPanTransitionEnd: function () { 5216 removeClass(this._mapPane, 'leaflet-pan-anim'); 5217 this.fire('moveend'); 5218 }, 5219 5220 _tryAnimatedPan: function (center, options) { 5221 // difference between the new and current centers in pixels 5222 var offset = this._getCenterOffset(center)._trunc(); 5223 5224 // don't animate too far unless animate: true specified in options 5225 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } 5226 5227 this.panBy(offset, options); 5228 5229 return true; 5230 }, 5231 5232 _createAnimProxy: function () { 5233 5234 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); 5235 this._panes.mapPane.appendChild(proxy); 5236 5237 this.on('zoomanim', function (e) { 5238 var prop = TRANSFORM, 5239 transform = this._proxy.style[prop]; 5240 5241 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); 5242 5243 // workaround for case when transform is the same and so transitionend event is not fired 5244 if (transform === this._proxy.style[prop] && this._animatingZoom) { 5245 this._onZoomTransitionEnd(); 5246 } 5247 }, this); 5248 5249 this.on('load moveend', this._animMoveEnd, this); 5250 5251 this._on('unload', this._destroyAnimProxy, this); 5252 }, 5253 5254 _destroyAnimProxy: function () { 5255 remove(this._proxy); 5256 this.off('load moveend', this._animMoveEnd, this); 5257 delete this._proxy; 5258 }, 5259 5260 _animMoveEnd: function () { 5261 var c = this.getCenter(), 5262 z = this.getZoom(); 5263 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); 5264 }, 5265 5266 _catchTransitionEnd: function (e) { 5267 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { 5268 this._onZoomTransitionEnd(); 5269 } 5270 }, 5271 5272 _nothingToAnimate: function () { 5273 return !this._container.getElementsByClassName('leaflet-zoom-animated').length; 5274 }, 5275 5276 _tryAnimatedZoom: function (center, zoom, options) { 5277 5278 if (this._animatingZoom) { return true; } 5279 5280 options = options || {}; 5281 5282 // don't animate if disabled, not supported or zoom difference is too large 5283 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || 5284 Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } 5285 5286 // offset is the pixel coords of the zoom origin relative to the current center 5287 var scale = this.getZoomScale(zoom), 5288 offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); 5289 5290 // don't animate if the zoom origin isn't within one screen from the current center, unless forced 5291 if (options.animate !== true && !this.getSize().contains(offset)) { return false; } 5292 5293 requestAnimFrame(function () { 5294 this 5295 ._moveStart(true, false) 5296 ._animateZoom(center, zoom, true); 5297 }, this); 5298 5299 return true; 5300 }, 5301 5302 _animateZoom: function (center, zoom, startAnim, noUpdate) { 5303 if (!this._mapPane) { return; } 5304 5305 if (startAnim) { 5306 this._animatingZoom = true; 5307 5308 // remember what center/zoom to set after animation 5309 this._animateToCenter = center; 5310 this._animateToZoom = zoom; 5311 5312 addClass(this._mapPane, 'leaflet-zoom-anim'); 5313 } 5314 5315 // @section Other Events 5316 // @event zoomanim: ZoomAnimEvent 5317 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom. 5318 this.fire('zoomanim', { 5319 center: center, 5320 zoom: zoom, 5321 noUpdate: noUpdate 5322 }); 5323 5324 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 5325 setTimeout(bind(this._onZoomTransitionEnd, this), 250); 5326 }, 5327 5328 _onZoomTransitionEnd: function () { 5329 if (!this._animatingZoom) { return; } 5330 5331 if (this._mapPane) { 5332 removeClass(this._mapPane, 'leaflet-zoom-anim'); 5333 } 5334 5335 this._animatingZoom = false; 5336 5337 this._move(this._animateToCenter, this._animateToZoom); 5338 5339 // This anim frame should prevent an obscure iOS webkit tile loading race condition. 5340 requestAnimFrame(function () { 5341 this._moveEnd(true); 5342 }, this); 5343 } 5344}); 5345 5346// @section 5347 5348// @factory L.map(id: String, options?: Map options) 5349// Instantiates a map object given the DOM ID of a `<div>` element 5350// and optionally an object literal with `Map options`. 5351// 5352// @alternative 5353// @factory L.map(el: HTMLElement, options?: Map options) 5354// Instantiates a map object given an instance of a `<div>` HTML element 5355// and optionally an object literal with `Map options`. 5356function createMap(id, options) { 5357 return new Map(id, options); 5358} 5359 5360/* 5361 * @class Control 5362 * @aka L.Control 5363 * @inherits Class 5364 * 5365 * L.Control is a base class for implementing map controls. Handles positioning. 5366 * All other controls extend from this class. 5367 */ 5368 5369var Control = Class.extend({ 5370 // @section 5371 // @aka Control options 5372 options: { 5373 // @option position: String = 'topright' 5374 // The position of the control (one of the map corners). Possible values are `'topleft'`, 5375 // `'topright'`, `'bottomleft'` or `'bottomright'` 5376 position: 'topright' 5377 }, 5378 5379 initialize: function (options) { 5380 setOptions(this, options); 5381 }, 5382 5383 /* @section 5384 * Classes extending L.Control will inherit the following methods: 5385 * 5386 * @method getPosition: string 5387 * Returns the position of the control. 5388 */ 5389 getPosition: function () { 5390 return this.options.position; 5391 }, 5392 5393 // @method setPosition(position: string): this 5394 // Sets the position of the control. 5395 setPosition: function (position) { 5396 var map = this._map; 5397 5398 if (map) { 5399 map.removeControl(this); 5400 } 5401 5402 this.options.position = position; 5403 5404 if (map) { 5405 map.addControl(this); 5406 } 5407 5408 return this; 5409 }, 5410 5411 // @method getContainer: HTMLElement 5412 // Returns the HTMLElement that contains the control. 5413 getContainer: function () { 5414 return this._container; 5415 }, 5416 5417 // @method addTo(map: Map): this 5418 // Adds the control to the given map. 5419 addTo: function (map) { 5420 this.remove(); 5421 this._map = map; 5422 5423 var container = this._container = this.onAdd(map), 5424 pos = this.getPosition(), 5425 corner = map._controlCorners[pos]; 5426 5427 addClass(container, 'leaflet-control'); 5428 5429 if (pos.indexOf('bottom') !== -1) { 5430 corner.insertBefore(container, corner.firstChild); 5431 } else { 5432 corner.appendChild(container); 5433 } 5434 5435 this._map.on('unload', this.remove, this); 5436 5437 return this; 5438 }, 5439 5440 // @method remove: this 5441 // Removes the control from the map it is currently active on. 5442 remove: function () { 5443 if (!this._map) { 5444 return this; 5445 } 5446 5447 remove(this._container); 5448 5449 if (this.onRemove) { 5450 this.onRemove(this._map); 5451 } 5452 5453 this._map.off('unload', this.remove, this); 5454 this._map = null; 5455 5456 return this; 5457 }, 5458 5459 _refocusOnMap: function (e) { 5460 // if map exists and event is not a keyboard event 5461 if (this._map && e && e.screenX > 0 && e.screenY > 0) { 5462 this._map.getContainer().focus(); 5463 } 5464 } 5465}); 5466 5467var control = function (options) { 5468 return new Control(options); 5469}; 5470 5471/* @section Extension methods 5472 * @uninheritable 5473 * 5474 * Every control should extend from `L.Control` and (re-)implement the following methods. 5475 * 5476 * @method onAdd(map: Map): HTMLElement 5477 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). 5478 * 5479 * @method onRemove(map: Map) 5480 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). 5481 */ 5482 5483/* @namespace Map 5484 * @section Methods for Layers and Controls 5485 */ 5486Map.include({ 5487 // @method addControl(control: Control): this 5488 // Adds the given control to the map 5489 addControl: function (control) { 5490 control.addTo(this); 5491 return this; 5492 }, 5493 5494 // @method removeControl(control: Control): this 5495 // Removes the given control from the map 5496 removeControl: function (control) { 5497 control.remove(); 5498 return this; 5499 }, 5500 5501 _initControlPos: function () { 5502 var corners = this._controlCorners = {}, 5503 l = 'leaflet-', 5504 container = this._controlContainer = 5505 create$1('div', l + 'control-container', this._container); 5506 5507 function createCorner(vSide, hSide) { 5508 var className = l + vSide + ' ' + l + hSide; 5509 5510 corners[vSide + hSide] = create$1('div', className, container); 5511 } 5512 5513 createCorner('top', 'left'); 5514 createCorner('top', 'right'); 5515 createCorner('bottom', 'left'); 5516 createCorner('bottom', 'right'); 5517 }, 5518 5519 _clearControlPos: function () { 5520 for (var i in this._controlCorners) { 5521 remove(this._controlCorners[i]); 5522 } 5523 remove(this._controlContainer); 5524 delete this._controlCorners; 5525 delete this._controlContainer; 5526 } 5527}); 5528 5529/* 5530 * @class Control.Layers 5531 * @aka L.Control.Layers 5532 * @inherits Control 5533 * 5534 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. 5535 * 5536 * @example 5537 * 5538 * ```js 5539 * var baseLayers = { 5540 * "Mapbox": mapbox, 5541 * "OpenStreetMap": osm 5542 * }; 5543 * 5544 * var overlays = { 5545 * "Marker": marker, 5546 * "Roads": roadsLayer 5547 * }; 5548 * 5549 * L.control.layers(baseLayers, overlays).addTo(map); 5550 * ``` 5551 * 5552 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: 5553 * 5554 * ```js 5555 * { 5556 * "<someName1>": layer1, 5557 * "<someName2>": layer2 5558 * } 5559 * ``` 5560 * 5561 * The layer names can contain HTML, which allows you to add additional styling to the items: 5562 * 5563 * ```js 5564 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer} 5565 * ``` 5566 */ 5567 5568var Layers = Control.extend({ 5569 // @section 5570 // @aka Control.Layers options 5571 options: { 5572 // @option collapsed: Boolean = true 5573 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. 5574 collapsed: true, 5575 position: 'topright', 5576 5577 // @option autoZIndex: Boolean = true 5578 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. 5579 autoZIndex: true, 5580 5581 // @option hideSingleBase: Boolean = false 5582 // If `true`, the base layers in the control will be hidden when there is only one. 5583 hideSingleBase: false, 5584 5585 // @option sortLayers: Boolean = false 5586 // Whether to sort the layers. When `false`, layers will keep the order 5587 // in which they were added to the control. 5588 sortLayers: false, 5589 5590 // @option sortFunction: Function = * 5591 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 5592 // that will be used for sorting the layers, when `sortLayers` is `true`. 5593 // The function receives both the `L.Layer` instances and their names, as in 5594 // `sortFunction(layerA, layerB, nameA, nameB)`. 5595 // By default, it sorts layers alphabetically by their name. 5596 sortFunction: function (layerA, layerB, nameA, nameB) { 5597 return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); 5598 } 5599 }, 5600 5601 initialize: function (baseLayers, overlays, options) { 5602 setOptions(this, options); 5603 5604 this._layerControlInputs = []; 5605 this._layers = []; 5606 this._lastZIndex = 0; 5607 this._handlingClick = false; 5608 5609 for (var i in baseLayers) { 5610 this._addLayer(baseLayers[i], i); 5611 } 5612 5613 for (i in overlays) { 5614 this._addLayer(overlays[i], i, true); 5615 } 5616 }, 5617 5618 onAdd: function (map) { 5619 this._initLayout(); 5620 this._update(); 5621 5622 this._map = map; 5623 map.on('zoomend', this._checkDisabledLayers, this); 5624 5625 for (var i = 0; i < this._layers.length; i++) { 5626 this._layers[i].layer.on('add remove', this._onLayerChange, this); 5627 } 5628 5629 return this._container; 5630 }, 5631 5632 addTo: function (map) { 5633 Control.prototype.addTo.call(this, map); 5634 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. 5635 return this._expandIfNotCollapsed(); 5636 }, 5637 5638 onRemove: function () { 5639 this._map.off('zoomend', this._checkDisabledLayers, this); 5640 5641 for (var i = 0; i < this._layers.length; i++) { 5642 this._layers[i].layer.off('add remove', this._onLayerChange, this); 5643 } 5644 }, 5645 5646 // @method addBaseLayer(layer: Layer, name: String): this 5647 // Adds a base layer (radio button entry) with the given name to the control. 5648 addBaseLayer: function (layer, name) { 5649 this._addLayer(layer, name); 5650 return (this._map) ? this._update() : this; 5651 }, 5652 5653 // @method addOverlay(layer: Layer, name: String): this 5654 // Adds an overlay (checkbox entry) with the given name to the control. 5655 addOverlay: function (layer, name) { 5656 this._addLayer(layer, name, true); 5657 return (this._map) ? this._update() : this; 5658 }, 5659 5660 // @method removeLayer(layer: Layer): this 5661 // Remove the given layer from the control. 5662 removeLayer: function (layer) { 5663 layer.off('add remove', this._onLayerChange, this); 5664 5665 var obj = this._getLayer(stamp(layer)); 5666 if (obj) { 5667 this._layers.splice(this._layers.indexOf(obj), 1); 5668 } 5669 return (this._map) ? this._update() : this; 5670 }, 5671 5672 // @method expand(): this 5673 // Expand the control container if collapsed. 5674 expand: function () { 5675 addClass(this._container, 'leaflet-control-layers-expanded'); 5676 this._section.style.height = null; 5677 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); 5678 if (acceptableHeight < this._section.clientHeight) { 5679 addClass(this._section, 'leaflet-control-layers-scrollbar'); 5680 this._section.style.height = acceptableHeight + 'px'; 5681 } else { 5682 removeClass(this._section, 'leaflet-control-layers-scrollbar'); 5683 } 5684 this._checkDisabledLayers(); 5685 return this; 5686 }, 5687 5688 // @method collapse(): this 5689 // Collapse the control container if expanded. 5690 collapse: function () { 5691 removeClass(this._container, 'leaflet-control-layers-expanded'); 5692 return this; 5693 }, 5694 5695 _initLayout: function () { 5696 var className = 'leaflet-control-layers', 5697 container = this._container = create$1('div', className), 5698 collapsed = this.options.collapsed; 5699 5700 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released 5701 container.setAttribute('aria-haspopup', true); 5702 5703 disableClickPropagation(container); 5704 disableScrollPropagation(container); 5705 5706 var section = this._section = create$1('section', className + '-list'); 5707 5708 if (collapsed) { 5709 this._map.on('click', this.collapse, this); 5710 5711 if (!android) { 5712 on(container, { 5713 mouseenter: this.expand, 5714 mouseleave: this.collapse 5715 }, this); 5716 } 5717 } 5718 5719 var link = this._layersLink = create$1('a', className + '-toggle', container); 5720 link.href = '#'; 5721 link.title = 'Layers'; 5722 5723 if (touch) { 5724 on(link, 'click', stop); 5725 on(link, 'click', this.expand, this); 5726 } else { 5727 on(link, 'focus', this.expand, this); 5728 } 5729 5730 if (!collapsed) { 5731 this.expand(); 5732 } 5733 5734 this._baseLayersList = create$1('div', className + '-base', section); 5735 this._separator = create$1('div', className + '-separator', section); 5736 this._overlaysList = create$1('div', className + '-overlays', section); 5737 5738 container.appendChild(section); 5739 }, 5740 5741 _getLayer: function (id) { 5742 for (var i = 0; i < this._layers.length; i++) { 5743 5744 if (this._layers[i] && stamp(this._layers[i].layer) === id) { 5745 return this._layers[i]; 5746 } 5747 } 5748 }, 5749 5750 _addLayer: function (layer, name, overlay) { 5751 if (this._map) { 5752 layer.on('add remove', this._onLayerChange, this); 5753 } 5754 5755 this._layers.push({ 5756 layer: layer, 5757 name: name, 5758 overlay: overlay 5759 }); 5760 5761 if (this.options.sortLayers) { 5762 this._layers.sort(bind(function (a, b) { 5763 return this.options.sortFunction(a.layer, b.layer, a.name, b.name); 5764 }, this)); 5765 } 5766 5767 if (this.options.autoZIndex && layer.setZIndex) { 5768 this._lastZIndex++; 5769 layer.setZIndex(this._lastZIndex); 5770 } 5771 5772 this._expandIfNotCollapsed(); 5773 }, 5774 5775 _update: function () { 5776 if (!this._container) { return this; } 5777 5778 empty(this._baseLayersList); 5779 empty(this._overlaysList); 5780 5781 this._layerControlInputs = []; 5782 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; 5783 5784 for (i = 0; i < this._layers.length; i++) { 5785 obj = this._layers[i]; 5786 this._addItem(obj); 5787 overlaysPresent = overlaysPresent || obj.overlay; 5788 baseLayersPresent = baseLayersPresent || !obj.overlay; 5789 baseLayersCount += !obj.overlay ? 1 : 0; 5790 } 5791 5792 // Hide base layers section if there's only one layer. 5793 if (this.options.hideSingleBase) { 5794 baseLayersPresent = baseLayersPresent && baseLayersCount > 1; 5795 this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; 5796 } 5797 5798 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; 5799 5800 return this; 5801 }, 5802 5803 _onLayerChange: function (e) { 5804 if (!this._handlingClick) { 5805 this._update(); 5806 } 5807 5808 var obj = this._getLayer(stamp(e.target)); 5809 5810 // @namespace Map 5811 // @section Layer events 5812 // @event baselayerchange: LayersControlEvent 5813 // Fired when the base layer is changed through the [layer control](#control-layers). 5814 // @event overlayadd: LayersControlEvent 5815 // Fired when an overlay is selected through the [layer control](#control-layers). 5816 // @event overlayremove: LayersControlEvent 5817 // Fired when an overlay is deselected through the [layer control](#control-layers). 5818 // @namespace Control.Layers 5819 var type = obj.overlay ? 5820 (e.type === 'add' ? 'overlayadd' : 'overlayremove') : 5821 (e.type === 'add' ? 'baselayerchange' : null); 5822 5823 if (type) { 5824 this._map.fire(type, obj); 5825 } 5826 }, 5827 5828 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) 5829 _createRadioElement: function (name, checked) { 5830 5831 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + 5832 name + '"' + (checked ? ' checked="checked"' : '') + '/>'; 5833 5834 var radioFragment = document.createElement('div'); 5835 radioFragment.innerHTML = radioHtml; 5836 5837 return radioFragment.firstChild; 5838 }, 5839 5840 _addItem: function (obj) { 5841 var label = document.createElement('label'), 5842 checked = this._map.hasLayer(obj.layer), 5843 input; 5844 5845 if (obj.overlay) { 5846 input = document.createElement('input'); 5847 input.type = 'checkbox'; 5848 input.className = 'leaflet-control-layers-selector'; 5849 input.defaultChecked = checked; 5850 } else { 5851 input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked); 5852 } 5853 5854 this._layerControlInputs.push(input); 5855 input.layerId = stamp(obj.layer); 5856 5857 on(input, 'click', this._onInputClick, this); 5858 5859 var name = document.createElement('span'); 5860 name.innerHTML = ' ' + obj.name; 5861 5862 // Helps from preventing layer control flicker when checkboxes are disabled 5863 // https://github.com/Leaflet/Leaflet/issues/2771 5864 var holder = document.createElement('div'); 5865 5866 label.appendChild(holder); 5867 holder.appendChild(input); 5868 holder.appendChild(name); 5869 5870 var container = obj.overlay ? this._overlaysList : this._baseLayersList; 5871 container.appendChild(label); 5872 5873 this._checkDisabledLayers(); 5874 return label; 5875 }, 5876 5877 _onInputClick: function () { 5878 var inputs = this._layerControlInputs, 5879 input, layer; 5880 var addedLayers = [], 5881 removedLayers = []; 5882 5883 this._handlingClick = true; 5884 5885 for (var i = inputs.length - 1; i >= 0; i--) { 5886 input = inputs[i]; 5887 layer = this._getLayer(input.layerId).layer; 5888 5889 if (input.checked) { 5890 addedLayers.push(layer); 5891 } else if (!input.checked) { 5892 removedLayers.push(layer); 5893 } 5894 } 5895 5896 // Bugfix issue 2318: Should remove all old layers before readding new ones 5897 for (i = 0; i < removedLayers.length; i++) { 5898 if (this._map.hasLayer(removedLayers[i])) { 5899 this._map.removeLayer(removedLayers[i]); 5900 } 5901 } 5902 for (i = 0; i < addedLayers.length; i++) { 5903 if (!this._map.hasLayer(addedLayers[i])) { 5904 this._map.addLayer(addedLayers[i]); 5905 } 5906 } 5907 5908 this._handlingClick = false; 5909 5910 this._refocusOnMap(); 5911 }, 5912 5913 _checkDisabledLayers: function () { 5914 var inputs = this._layerControlInputs, 5915 input, 5916 layer, 5917 zoom = this._map.getZoom(); 5918 5919 for (var i = inputs.length - 1; i >= 0; i--) { 5920 input = inputs[i]; 5921 layer = this._getLayer(input.layerId).layer; 5922 input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || 5923 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); 5924 5925 } 5926 }, 5927 5928 _expandIfNotCollapsed: function () { 5929 if (this._map && !this.options.collapsed) { 5930 this.expand(); 5931 } 5932 return this; 5933 }, 5934 5935 _expand: function () { 5936 // Backward compatibility, remove me in 1.1. 5937 return this.expand(); 5938 }, 5939 5940 _collapse: function () { 5941 // Backward compatibility, remove me in 1.1. 5942 return this.collapse(); 5943 } 5944 5945}); 5946 5947 5948// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) 5949// Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. 5950var layers = function (baseLayers, overlays, options) { 5951 return new Layers(baseLayers, overlays, options); 5952}; 5953 5954/* 5955 * @class Control.Zoom 5956 * @aka L.Control.Zoom 5957 * @inherits Control 5958 * 5959 * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. 5960 */ 5961 5962var Zoom = Control.extend({ 5963 // @section 5964 // @aka Control.Zoom options 5965 options: { 5966 position: 'topleft', 5967 5968 // @option zoomInText: String = '+' 5969 // The text set on the 'zoom in' button. 5970 zoomInText: '+', 5971 5972 // @option zoomInTitle: String = 'Zoom in' 5973 // The title set on the 'zoom in' button. 5974 zoomInTitle: 'Zoom in', 5975 5976 // @option zoomOutText: String = '−' 5977 // The text set on the 'zoom out' button. 5978 zoomOutText: '−', 5979 5980 // @option zoomOutTitle: String = 'Zoom out' 5981 // The title set on the 'zoom out' button. 5982 zoomOutTitle: 'Zoom out' 5983 }, 5984 5985 onAdd: function (map) { 5986 var zoomName = 'leaflet-control-zoom', 5987 container = create$1('div', zoomName + ' leaflet-bar'), 5988 options = this.options; 5989 5990 this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, 5991 zoomName + '-in', container, this._zoomIn); 5992 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, 5993 zoomName + '-out', container, this._zoomOut); 5994 5995 this._updateDisabled(); 5996 map.on('zoomend zoomlevelschange', this._updateDisabled, this); 5997 5998 return container; 5999 }, 6000 6001 onRemove: function (map) { 6002 map.off('zoomend zoomlevelschange', this._updateDisabled, this); 6003 }, 6004 6005 disable: function () { 6006 this._disabled = true; 6007 this._updateDisabled(); 6008 return this; 6009 }, 6010 6011 enable: function () { 6012 this._disabled = false; 6013 this._updateDisabled(); 6014 return this; 6015 }, 6016 6017 _zoomIn: function (e) { 6018 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { 6019 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 6020 } 6021 }, 6022 6023 _zoomOut: function (e) { 6024 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { 6025 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 6026 } 6027 }, 6028 6029 _createButton: function (html, title, className, container, fn) { 6030 var link = create$1('a', className, container); 6031 link.innerHTML = html; 6032 link.href = '#'; 6033 link.title = title; 6034 6035 /* 6036 * Will force screen readers like VoiceOver to read this as "Zoom in - button" 6037 */ 6038 link.setAttribute('role', 'button'); 6039 link.setAttribute('aria-label', title); 6040 6041 disableClickPropagation(link); 6042 on(link, 'click', stop); 6043 on(link, 'click', fn, this); 6044 on(link, 'click', this._refocusOnMap, this); 6045 6046 return link; 6047 }, 6048 6049 _updateDisabled: function () { 6050 var map = this._map, 6051 className = 'leaflet-disabled'; 6052 6053 removeClass(this._zoomInButton, className); 6054 removeClass(this._zoomOutButton, className); 6055 6056 if (this._disabled || map._zoom === map.getMinZoom()) { 6057 addClass(this._zoomOutButton, className); 6058 } 6059 if (this._disabled || map._zoom === map.getMaxZoom()) { 6060 addClass(this._zoomInButton, className); 6061 } 6062 } 6063}); 6064 6065// @namespace Map 6066// @section Control options 6067// @option zoomControl: Boolean = true 6068// Whether a [zoom control](#control-zoom) is added to the map by default. 6069Map.mergeOptions({ 6070 zoomControl: true 6071}); 6072 6073Map.addInitHook(function () { 6074 if (this.options.zoomControl) { 6075 // @section Controls 6076 // @property zoomControl: Control.Zoom 6077 // The default zoom control (only available if the 6078 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map). 6079 this.zoomControl = new Zoom(); 6080 this.addControl(this.zoomControl); 6081 } 6082}); 6083 6084// @namespace Control.Zoom 6085// @factory L.control.zoom(options: Control.Zoom options) 6086// Creates a zoom control 6087var zoom = function (options) { 6088 return new Zoom(options); 6089}; 6090 6091/* 6092 * @class Control.Scale 6093 * @aka L.Control.Scale 6094 * @inherits Control 6095 * 6096 * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. 6097 * 6098 * @example 6099 * 6100 * ```js 6101 * L.control.scale().addTo(map); 6102 * ``` 6103 */ 6104 6105var Scale = Control.extend({ 6106 // @section 6107 // @aka Control.Scale options 6108 options: { 6109 position: 'bottomleft', 6110 6111 // @option maxWidth: Number = 100 6112 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). 6113 maxWidth: 100, 6114 6115 // @option metric: Boolean = True 6116 // Whether to show the metric scale line (m/km). 6117 metric: true, 6118 6119 // @option imperial: Boolean = True 6120 // Whether to show the imperial scale line (mi/ft). 6121 imperial: true 6122 6123 // @option updateWhenIdle: Boolean = false 6124 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). 6125 }, 6126 6127 onAdd: function (map) { 6128 var className = 'leaflet-control-scale', 6129 container = create$1('div', className), 6130 options = this.options; 6131 6132 this._addScales(options, className + '-line', container); 6133 6134 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 6135 map.whenReady(this._update, this); 6136 6137 return container; 6138 }, 6139 6140 onRemove: function (map) { 6141 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 6142 }, 6143 6144 _addScales: function (options, className, container) { 6145 if (options.metric) { 6146 this._mScale = create$1('div', className, container); 6147 } 6148 if (options.imperial) { 6149 this._iScale = create$1('div', className, container); 6150 } 6151 }, 6152 6153 _update: function () { 6154 var map = this._map, 6155 y = map.getSize().y / 2; 6156 6157 var maxMeters = map.distance( 6158 map.containerPointToLatLng([0, y]), 6159 map.containerPointToLatLng([this.options.maxWidth, y])); 6160 6161 this._updateScales(maxMeters); 6162 }, 6163 6164 _updateScales: function (maxMeters) { 6165 if (this.options.metric && maxMeters) { 6166 this._updateMetric(maxMeters); 6167 } 6168 if (this.options.imperial && maxMeters) { 6169 this._updateImperial(maxMeters); 6170 } 6171 }, 6172 6173 _updateMetric: function (maxMeters) { 6174 var meters = this._getRoundNum(maxMeters), 6175 label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; 6176 6177 this._updateScale(this._mScale, label, meters / maxMeters); 6178 }, 6179 6180 _updateImperial: function (maxMeters) { 6181 var maxFeet = maxMeters * 3.2808399, 6182 maxMiles, miles, feet; 6183 6184 if (maxFeet > 5280) { 6185 maxMiles = maxFeet / 5280; 6186 miles = this._getRoundNum(maxMiles); 6187 this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); 6188 6189 } else { 6190 feet = this._getRoundNum(maxFeet); 6191 this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); 6192 } 6193 }, 6194 6195 _updateScale: function (scale, text, ratio) { 6196 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; 6197 scale.innerHTML = text; 6198 }, 6199 6200 _getRoundNum: function (num) { 6201 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), 6202 d = num / pow10; 6203 6204 d = d >= 10 ? 10 : 6205 d >= 5 ? 5 : 6206 d >= 3 ? 3 : 6207 d >= 2 ? 2 : 1; 6208 6209 return pow10 * d; 6210 } 6211}); 6212 6213 6214// @factory L.control.scale(options?: Control.Scale options) 6215// Creates an scale control with the given options. 6216var scale = function (options) { 6217 return new Scale(options); 6218}; 6219 6220/* 6221 * @class Control.Attribution 6222 * @aka L.Control.Attribution 6223 * @inherits Control 6224 * 6225 * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. 6226 */ 6227 6228var Attribution = Control.extend({ 6229 // @section 6230 // @aka Control.Attribution options 6231 options: { 6232 position: 'bottomright', 6233 6234 // @option prefix: String = 'Leaflet' 6235 // The HTML text shown before the attributions. Pass `false` to disable. 6236 prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>' 6237 }, 6238 6239 initialize: function (options) { 6240 setOptions(this, options); 6241 6242 this._attributions = {}; 6243 }, 6244 6245 onAdd: function (map) { 6246 map.attributionControl = this; 6247 this._container = create$1('div', 'leaflet-control-attribution'); 6248 disableClickPropagation(this._container); 6249 6250 // TODO ugly, refactor 6251 for (var i in map._layers) { 6252 if (map._layers[i].getAttribution) { 6253 this.addAttribution(map._layers[i].getAttribution()); 6254 } 6255 } 6256 6257 this._update(); 6258 6259 return this._container; 6260 }, 6261 6262 // @method setPrefix(prefix: String): this 6263 // Sets the text before the attributions. 6264 setPrefix: function (prefix) { 6265 this.options.prefix = prefix; 6266 this._update(); 6267 return this; 6268 }, 6269 6270 // @method addAttribution(text: String): this 6271 // Adds an attribution text (e.g. `'Vector data © Mapbox'`). 6272 addAttribution: function (text) { 6273 if (!text) { return this; } 6274 6275 if (!this._attributions[text]) { 6276 this._attributions[text] = 0; 6277 } 6278 this._attributions[text]++; 6279 6280 this._update(); 6281 6282 return this; 6283 }, 6284 6285 // @method removeAttribution(text: String): this 6286 // Removes an attribution text. 6287 removeAttribution: function (text) { 6288 if (!text) { return this; } 6289 6290 if (this._attributions[text]) { 6291 this._attributions[text]--; 6292 this._update(); 6293 } 6294 6295 return this; 6296 }, 6297 6298 _update: function () { 6299 if (!this._map) { return; } 6300 6301 var attribs = []; 6302 6303 for (var i in this._attributions) { 6304 if (this._attributions[i]) { 6305 attribs.push(i); 6306 } 6307 } 6308 6309 var prefixAndAttribs = []; 6310 6311 if (this.options.prefix) { 6312 prefixAndAttribs.push(this.options.prefix); 6313 } 6314 if (attribs.length) { 6315 prefixAndAttribs.push(attribs.join(', ')); 6316 } 6317 6318 this._container.innerHTML = prefixAndAttribs.join(' | '); 6319 } 6320}); 6321 6322// @namespace Map 6323// @section Control options 6324// @option attributionControl: Boolean = true 6325// Whether a [attribution control](#control-attribution) is added to the map by default. 6326Map.mergeOptions({ 6327 attributionControl: true 6328}); 6329 6330Map.addInitHook(function () { 6331 if (this.options.attributionControl) { 6332 new Attribution().addTo(this); 6333 } 6334}); 6335 6336// @namespace Control.Attribution 6337// @factory L.control.attribution(options: Control.Attribution options) 6338// Creates an attribution control. 6339var attribution = function (options) { 6340 return new Attribution(options); 6341}; 6342 6343Control.Layers = Layers; 6344Control.Zoom = Zoom; 6345Control.Scale = Scale; 6346Control.Attribution = Attribution; 6347 6348control.layers = layers; 6349control.zoom = zoom; 6350control.scale = scale; 6351control.attribution = attribution; 6352 6353/* 6354 L.Handler is a base class for handler classes that are used internally to inject 6355 interaction features like dragging to classes like Map and Marker. 6356*/ 6357 6358// @class Handler 6359// @aka L.Handler 6360// Abstract class for map interaction handlers 6361 6362var Handler = Class.extend({ 6363 initialize: function (map) { 6364 this._map = map; 6365 }, 6366 6367 // @method enable(): this 6368 // Enables the handler 6369 enable: function () { 6370 if (this._enabled) { return this; } 6371 6372 this._enabled = true; 6373 this.addHooks(); 6374 return this; 6375 }, 6376 6377 // @method disable(): this 6378 // Disables the handler 6379 disable: function () { 6380 if (!this._enabled) { return this; } 6381 6382 this._enabled = false; 6383 this.removeHooks(); 6384 return this; 6385 }, 6386 6387 // @method enabled(): Boolean 6388 // Returns `true` if the handler is enabled 6389 enabled: function () { 6390 return !!this._enabled; 6391 } 6392 6393 // @section Extension methods 6394 // Classes inheriting from `Handler` must implement the two following methods: 6395 // @method addHooks() 6396 // Called when the handler is enabled, should add event hooks. 6397 // @method removeHooks() 6398 // Called when the handler is disabled, should remove the event hooks added previously. 6399}); 6400 6401// @section There is static function which can be called without instantiating L.Handler: 6402// @function addTo(map: Map, name: String): this 6403// Adds a new Handler to the given map with the given name. 6404Handler.addTo = function (map, name) { 6405 map.addHandler(name, this); 6406 return this; 6407}; 6408 6409var Mixin = {Events: Events}; 6410 6411/* 6412 * @class Draggable 6413 * @aka L.Draggable 6414 * @inherits Evented 6415 * 6416 * A class for making DOM elements draggable (including touch support). 6417 * Used internally for map and marker dragging. Only works for elements 6418 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). 6419 * 6420 * @example 6421 * ```js 6422 * var draggable = new L.Draggable(elementToDrag); 6423 * draggable.enable(); 6424 * ``` 6425 */ 6426 6427var START = touch ? 'touchstart mousedown' : 'mousedown'; 6428var END = { 6429 mousedown: 'mouseup', 6430 touchstart: 'touchend', 6431 pointerdown: 'touchend', 6432 MSPointerDown: 'touchend' 6433}; 6434var MOVE = { 6435 mousedown: 'mousemove', 6436 touchstart: 'touchmove', 6437 pointerdown: 'touchmove', 6438 MSPointerDown: 'touchmove' 6439}; 6440 6441 6442var Draggable = Evented.extend({ 6443 6444 options: { 6445 // @section 6446 // @aka Draggable options 6447 // @option clickTolerance: Number = 3 6448 // The max number of pixels a user can shift the mouse pointer during a click 6449 // for it to be considered a valid click (as opposed to a mouse drag). 6450 clickTolerance: 3 6451 }, 6452 6453 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) 6454 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). 6455 initialize: function (element, dragStartTarget, preventOutline$$1, options) { 6456 setOptions(this, options); 6457 6458 this._element = element; 6459 this._dragStartTarget = dragStartTarget || element; 6460 this._preventOutline = preventOutline$$1; 6461 }, 6462 6463 // @method enable() 6464 // Enables the dragging ability 6465 enable: function () { 6466 if (this._enabled) { return; } 6467 6468 on(this._dragStartTarget, START, this._onDown, this); 6469 6470 this._enabled = true; 6471 }, 6472 6473 // @method disable() 6474 // Disables the dragging ability 6475 disable: function () { 6476 if (!this._enabled) { return; } 6477 6478 // If we're currently dragging this draggable, 6479 // disabling it counts as first ending the drag. 6480 if (Draggable._dragging === this) { 6481 this.finishDrag(); 6482 } 6483 6484 off(this._dragStartTarget, START, this._onDown, this); 6485 6486 this._enabled = false; 6487 this._moved = false; 6488 }, 6489 6490 _onDown: function (e) { 6491 // Ignore simulated events, since we handle both touch and 6492 // mouse explicitly; otherwise we risk getting duplicates of 6493 // touch events, see #4315. 6494 // Also ignore the event if disabled; this happens in IE11 6495 // under some circumstances, see #3666. 6496 if (e._simulated || !this._enabled) { return; } 6497 6498 this._moved = false; 6499 6500 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } 6501 6502 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } 6503 Draggable._dragging = this; // Prevent dragging multiple objects at once. 6504 6505 if (this._preventOutline) { 6506 preventOutline(this._element); 6507 } 6508 6509 disableImageDrag(); 6510 disableTextSelection(); 6511 6512 if (this._moving) { return; } 6513 6514 // @event down: Event 6515 // Fired when a drag is about to start. 6516 this.fire('down'); 6517 6518 var first = e.touches ? e.touches[0] : e, 6519 sizedParent = getSizedParentNode(this._element); 6520 6521 this._startPoint = new Point(first.clientX, first.clientY); 6522 6523 // Cache the scale, so that we can continuously compensate for it during drag (_onMove). 6524 this._parentScale = getScale(sizedParent); 6525 6526 on(document, MOVE[e.type], this._onMove, this); 6527 on(document, END[e.type], this._onUp, this); 6528 }, 6529 6530 _onMove: function (e) { 6531 // Ignore simulated events, since we handle both touch and 6532 // mouse explicitly; otherwise we risk getting duplicates of 6533 // touch events, see #4315. 6534 // Also ignore the event if disabled; this happens in IE11 6535 // under some circumstances, see #3666. 6536 if (e._simulated || !this._enabled) { return; } 6537 6538 if (e.touches && e.touches.length > 1) { 6539 this._moved = true; 6540 return; 6541 } 6542 6543 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), 6544 offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint); 6545 6546 if (!offset.x && !offset.y) { return; } 6547 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } 6548 6549 // We assume that the parent container's position, border and scale do not change for the duration of the drag. 6550 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction) 6551 // and we can use the cached value for the scale. 6552 offset.x /= this._parentScale.x; 6553 offset.y /= this._parentScale.y; 6554 6555 preventDefault(e); 6556 6557 if (!this._moved) { 6558 // @event dragstart: Event 6559 // Fired when a drag starts 6560 this.fire('dragstart'); 6561 6562 this._moved = true; 6563 this._startPos = getPosition(this._element).subtract(offset); 6564 6565 addClass(document.body, 'leaflet-dragging'); 6566 6567 this._lastTarget = e.target || e.srcElement; 6568 // IE and Edge do not give the <use> element, so fetch it 6569 // if necessary 6570 if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { 6571 this._lastTarget = this._lastTarget.correspondingUseElement; 6572 } 6573 addClass(this._lastTarget, 'leaflet-drag-target'); 6574 } 6575 6576 this._newPos = this._startPos.add(offset); 6577 this._moving = true; 6578 6579 cancelAnimFrame(this._animRequest); 6580 this._lastEvent = e; 6581 this._animRequest = requestAnimFrame(this._updatePosition, this, true); 6582 }, 6583 6584 _updatePosition: function () { 6585 var e = {originalEvent: this._lastEvent}; 6586 6587 // @event predrag: Event 6588 // Fired continuously during dragging *before* each corresponding 6589 // update of the element's position. 6590 this.fire('predrag', e); 6591 setPosition(this._element, this._newPos); 6592 6593 // @event drag: Event 6594 // Fired continuously during dragging. 6595 this.fire('drag', e); 6596 }, 6597 6598 _onUp: function (e) { 6599 // Ignore simulated events, since we handle both touch and 6600 // mouse explicitly; otherwise we risk getting duplicates of 6601 // touch events, see #4315. 6602 // Also ignore the event if disabled; this happens in IE11 6603 // under some circumstances, see #3666. 6604 if (e._simulated || !this._enabled) { return; } 6605 this.finishDrag(); 6606 }, 6607 6608 finishDrag: function () { 6609 removeClass(document.body, 'leaflet-dragging'); 6610 6611 if (this._lastTarget) { 6612 removeClass(this._lastTarget, 'leaflet-drag-target'); 6613 this._lastTarget = null; 6614 } 6615 6616 for (var i in MOVE) { 6617 off(document, MOVE[i], this._onMove, this); 6618 off(document, END[i], this._onUp, this); 6619 } 6620 6621 enableImageDrag(); 6622 enableTextSelection(); 6623 6624 if (this._moved && this._moving) { 6625 // ensure drag is not fired after dragend 6626 cancelAnimFrame(this._animRequest); 6627 6628 // @event dragend: DragEndEvent 6629 // Fired when the drag ends. 6630 this.fire('dragend', { 6631 distance: this._newPos.distanceTo(this._startPos) 6632 }); 6633 } 6634 6635 this._moving = false; 6636 Draggable._dragging = false; 6637 } 6638 6639}); 6640 6641/* 6642 * @namespace LineUtil 6643 * 6644 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. 6645 */ 6646 6647// Simplify polyline with vertex reduction and Douglas-Peucker simplification. 6648// Improves rendering performance dramatically by lessening the number of points to draw. 6649 6650// @function simplify(points: Point[], tolerance: Number): Point[] 6651// Dramatically reduces the number of points in a polyline while retaining 6652// its shape and returns a new array of simplified points, using the 6653// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). 6654// Used for a huge performance boost when processing/displaying Leaflet polylines for 6655// each zoom level and also reducing visual noise. tolerance affects the amount of 6656// simplification (lesser value means higher quality but slower and with more points). 6657// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). 6658function simplify(points, tolerance) { 6659 if (!tolerance || !points.length) { 6660 return points.slice(); 6661 } 6662 6663 var sqTolerance = tolerance * tolerance; 6664 6665 // stage 1: vertex reduction 6666 points = _reducePoints(points, sqTolerance); 6667 6668 // stage 2: Douglas-Peucker simplification 6669 points = _simplifyDP(points, sqTolerance); 6670 6671 return points; 6672} 6673 6674// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number 6675// Returns the distance between point `p` and segment `p1` to `p2`. 6676function pointToSegmentDistance(p, p1, p2) { 6677 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); 6678} 6679 6680// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number 6681// Returns the closest point from a point `p` on a segment `p1` to `p2`. 6682function closestPointOnSegment(p, p1, p2) { 6683 return _sqClosestPointOnSegment(p, p1, p2); 6684} 6685 6686// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm 6687function _simplifyDP(points, sqTolerance) { 6688 6689 var len = points.length, 6690 ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, 6691 markers = new ArrayConstructor(len); 6692 6693 markers[0] = markers[len - 1] = 1; 6694 6695 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); 6696 6697 var i, 6698 newPoints = []; 6699 6700 for (i = 0; i < len; i++) { 6701 if (markers[i]) { 6702 newPoints.push(points[i]); 6703 } 6704 } 6705 6706 return newPoints; 6707} 6708 6709function _simplifyDPStep(points, markers, sqTolerance, first, last) { 6710 6711 var maxSqDist = 0, 6712 index, i, sqDist; 6713 6714 for (i = first + 1; i <= last - 1; i++) { 6715 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); 6716 6717 if (sqDist > maxSqDist) { 6718 index = i; 6719 maxSqDist = sqDist; 6720 } 6721 } 6722 6723 if (maxSqDist > sqTolerance) { 6724 markers[index] = 1; 6725 6726 _simplifyDPStep(points, markers, sqTolerance, first, index); 6727 _simplifyDPStep(points, markers, sqTolerance, index, last); 6728 } 6729} 6730 6731// reduce points that are too close to each other to a single point 6732function _reducePoints(points, sqTolerance) { 6733 var reducedPoints = [points[0]]; 6734 6735 for (var i = 1, prev = 0, len = points.length; i < len; i++) { 6736 if (_sqDist(points[i], points[prev]) > sqTolerance) { 6737 reducedPoints.push(points[i]); 6738 prev = i; 6739 } 6740 } 6741 if (prev < len - 1) { 6742 reducedPoints.push(points[len - 1]); 6743 } 6744 return reducedPoints; 6745} 6746 6747var _lastCode; 6748 6749// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean 6750// Clips the segment a to b by rectangular bounds with the 6751// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) 6752// (modifying the segment points directly!). Used by Leaflet to only show polyline 6753// points that are on the screen or near, increasing performance. 6754function clipSegment(a, b, bounds, useLastCode, round) { 6755 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), 6756 codeB = _getBitCode(b, bounds), 6757 6758 codeOut, p, newCode; 6759 6760 // save 2nd code to avoid calculating it on the next segment 6761 _lastCode = codeB; 6762 6763 while (true) { 6764 // if a,b is inside the clip window (trivial accept) 6765 if (!(codeA | codeB)) { 6766 return [a, b]; 6767 } 6768 6769 // if a,b is outside the clip window (trivial reject) 6770 if (codeA & codeB) { 6771 return false; 6772 } 6773 6774 // other cases 6775 codeOut = codeA || codeB; 6776 p = _getEdgeIntersection(a, b, codeOut, bounds, round); 6777 newCode = _getBitCode(p, bounds); 6778 6779 if (codeOut === codeA) { 6780 a = p; 6781 codeA = newCode; 6782 } else { 6783 b = p; 6784 codeB = newCode; 6785 } 6786 } 6787} 6788 6789function _getEdgeIntersection(a, b, code, bounds, round) { 6790 var dx = b.x - a.x, 6791 dy = b.y - a.y, 6792 min = bounds.min, 6793 max = bounds.max, 6794 x, y; 6795 6796 if (code & 8) { // top 6797 x = a.x + dx * (max.y - a.y) / dy; 6798 y = max.y; 6799 6800 } else if (code & 4) { // bottom 6801 x = a.x + dx * (min.y - a.y) / dy; 6802 y = min.y; 6803 6804 } else if (code & 2) { // right 6805 x = max.x; 6806 y = a.y + dy * (max.x - a.x) / dx; 6807 6808 } else if (code & 1) { // left 6809 x = min.x; 6810 y = a.y + dy * (min.x - a.x) / dx; 6811 } 6812 6813 return new Point(x, y, round); 6814} 6815 6816function _getBitCode(p, bounds) { 6817 var code = 0; 6818 6819 if (p.x < bounds.min.x) { // left 6820 code |= 1; 6821 } else if (p.x > bounds.max.x) { // right 6822 code |= 2; 6823 } 6824 6825 if (p.y < bounds.min.y) { // bottom 6826 code |= 4; 6827 } else if (p.y > bounds.max.y) { // top 6828 code |= 8; 6829 } 6830 6831 return code; 6832} 6833 6834// square distance (to avoid unnecessary Math.sqrt calls) 6835function _sqDist(p1, p2) { 6836 var dx = p2.x - p1.x, 6837 dy = p2.y - p1.y; 6838 return dx * dx + dy * dy; 6839} 6840 6841// return closest point on segment or distance to that point 6842function _sqClosestPointOnSegment(p, p1, p2, sqDist) { 6843 var x = p1.x, 6844 y = p1.y, 6845 dx = p2.x - x, 6846 dy = p2.y - y, 6847 dot = dx * dx + dy * dy, 6848 t; 6849 6850 if (dot > 0) { 6851 t = ((p.x - x) * dx + (p.y - y) * dy) / dot; 6852 6853 if (t > 1) { 6854 x = p2.x; 6855 y = p2.y; 6856 } else if (t > 0) { 6857 x += dx * t; 6858 y += dy * t; 6859 } 6860 } 6861 6862 dx = p.x - x; 6863 dy = p.y - y; 6864 6865 return sqDist ? dx * dx + dy * dy : new Point(x, y); 6866} 6867 6868 6869// @function isFlat(latlngs: LatLng[]): Boolean 6870// Returns true if `latlngs` is a flat array, false is nested. 6871function isFlat(latlngs) { 6872 return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); 6873} 6874 6875function _flat(latlngs) { 6876 console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); 6877 return isFlat(latlngs); 6878} 6879 6880 6881var LineUtil = (Object.freeze || Object)({ 6882 simplify: simplify, 6883 pointToSegmentDistance: pointToSegmentDistance, 6884 closestPointOnSegment: closestPointOnSegment, 6885 clipSegment: clipSegment, 6886 _getEdgeIntersection: _getEdgeIntersection, 6887 _getBitCode: _getBitCode, 6888 _sqClosestPointOnSegment: _sqClosestPointOnSegment, 6889 isFlat: isFlat, 6890 _flat: _flat 6891}); 6892 6893/* 6894 * @namespace PolyUtil 6895 * Various utility functions for polygon geometries. 6896 */ 6897 6898/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] 6899 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). 6900 * Used by Leaflet to only show polygon points that are on the screen or near, increasing 6901 * performance. Note that polygon points needs different algorithm for clipping 6902 * than polyline, so there's a separate method for it. 6903 */ 6904function clipPolygon(points, bounds, round) { 6905 var clippedPoints, 6906 edges = [1, 4, 2, 8], 6907 i, j, k, 6908 a, b, 6909 len, edge, p; 6910 6911 for (i = 0, len = points.length; i < len; i++) { 6912 points[i]._code = _getBitCode(points[i], bounds); 6913 } 6914 6915 // for each edge (left, bottom, right, top) 6916 for (k = 0; k < 4; k++) { 6917 edge = edges[k]; 6918 clippedPoints = []; 6919 6920 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { 6921 a = points[i]; 6922 b = points[j]; 6923 6924 // if a is inside the clip window 6925 if (!(a._code & edge)) { 6926 // if b is outside the clip window (a->b goes out of screen) 6927 if (b._code & edge) { 6928 p = _getEdgeIntersection(b, a, edge, bounds, round); 6929 p._code = _getBitCode(p, bounds); 6930 clippedPoints.push(p); 6931 } 6932 clippedPoints.push(a); 6933 6934 // else if b is inside the clip window (a->b enters the screen) 6935 } else if (!(b._code & edge)) { 6936 p = _getEdgeIntersection(b, a, edge, bounds, round); 6937 p._code = _getBitCode(p, bounds); 6938 clippedPoints.push(p); 6939 } 6940 } 6941 points = clippedPoints; 6942 } 6943 6944 return points; 6945} 6946 6947 6948var PolyUtil = (Object.freeze || Object)({ 6949 clipPolygon: clipPolygon 6950}); 6951 6952/* 6953 * @namespace Projection 6954 * @section 6955 * Leaflet comes with a set of already defined Projections out of the box: 6956 * 6957 * @projection L.Projection.LonLat 6958 * 6959 * Equirectangular, or Plate Carree projection â the most simple projection, 6960 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as 6961 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the 6962 * `EPSG:4326` and `Simple` CRS. 6963 */ 6964 6965var LonLat = { 6966 project: function (latlng) { 6967 return new Point(latlng.lng, latlng.lat); 6968 }, 6969 6970 unproject: function (point) { 6971 return new LatLng(point.y, point.x); 6972 }, 6973 6974 bounds: new Bounds([-180, -90], [180, 90]) 6975}; 6976 6977/* 6978 * @namespace Projection 6979 * @projection L.Projection.Mercator 6980 * 6981 * Elliptical Mercator projection â more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS. 6982 */ 6983 6984var Mercator = { 6985 R: 6378137, 6986 R_MINOR: 6356752.314245179, 6987 6988 bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), 6989 6990 project: function (latlng) { 6991 var d = Math.PI / 180, 6992 r = this.R, 6993 y = latlng.lat * d, 6994 tmp = this.R_MINOR / r, 6995 e = Math.sqrt(1 - tmp * tmp), 6996 con = e * Math.sin(y); 6997 6998 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); 6999 y = -r * Math.log(Math.max(ts, 1E-10)); 7000 7001 return new Point(latlng.lng * d * r, y); 7002 }, 7003 7004 unproject: function (point) { 7005 var d = 180 / Math.PI, 7006 r = this.R, 7007 tmp = this.R_MINOR / r, 7008 e = Math.sqrt(1 - tmp * tmp), 7009 ts = Math.exp(-point.y / r), 7010 phi = Math.PI / 2 - 2 * Math.atan(ts); 7011 7012 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { 7013 con = e * Math.sin(phi); 7014 con = Math.pow((1 - con) / (1 + con), e / 2); 7015 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; 7016 phi += dphi; 7017 } 7018 7019 return new LatLng(phi * d, point.x * d / r); 7020 } 7021}; 7022 7023/* 7024 * @class Projection 7025 7026 * An object with methods for projecting geographical coordinates of the world onto 7027 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). 7028 7029 * @property bounds: Bounds 7030 * The bounds (specified in CRS units) where the projection is valid 7031 7032 * @method project(latlng: LatLng): Point 7033 * Projects geographical coordinates into a 2D point. 7034 * Only accepts actual `L.LatLng` instances, not arrays. 7035 7036 * @method unproject(point: Point): LatLng 7037 * The inverse of `project`. Projects a 2D point into a geographical location. 7038 * Only accepts actual `L.Point` instances, not arrays. 7039 7040 * Note that the projection instances do not inherit from Leafet's `Class` object, 7041 * and can't be instantiated. Also, new classes can't inherit from them, 7042 * and methods can't be added to them with the `include` function. 7043 7044 */ 7045 7046 7047 7048 7049var index = (Object.freeze || Object)({ 7050 LonLat: LonLat, 7051 Mercator: Mercator, 7052 SphericalMercator: SphericalMercator 7053}); 7054 7055/* 7056 * @namespace CRS 7057 * @crs L.CRS.EPSG3395 7058 * 7059 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. 7060 */ 7061var EPSG3395 = extend({}, Earth, { 7062 code: 'EPSG:3395', 7063 projection: Mercator, 7064 7065 transformation: (function () { 7066 var scale = 0.5 / (Math.PI * Mercator.R); 7067 return toTransformation(scale, 0.5, -scale, 0.5); 7068 }()) 7069}); 7070 7071/* 7072 * @namespace CRS 7073 * @crs L.CRS.EPSG4326 7074 * 7075 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. 7076 * 7077 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), 7078 * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` 7079 * with this CRS, ensure that there are two 256x256 pixel tiles covering the 7080 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), 7081 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. 7082 */ 7083 7084var EPSG4326 = extend({}, Earth, { 7085 code: 'EPSG:4326', 7086 projection: LonLat, 7087 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) 7088}); 7089 7090/* 7091 * @namespace CRS 7092 * @crs L.CRS.Simple 7093 * 7094 * A simple CRS that maps longitude and latitude into `x` and `y` directly. 7095 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` 7096 * axis should still be inverted (going from bottom to top). `distance()` returns 7097 * simple euclidean distance. 7098 */ 7099 7100var Simple = extend({}, CRS, { 7101 projection: LonLat, 7102 transformation: toTransformation(1, 0, -1, 0), 7103 7104 scale: function (zoom) { 7105 return Math.pow(2, zoom); 7106 }, 7107 7108 zoom: function (scale) { 7109 return Math.log(scale) / Math.LN2; 7110 }, 7111 7112 distance: function (latlng1, latlng2) { 7113 var dx = latlng2.lng - latlng1.lng, 7114 dy = latlng2.lat - latlng1.lat; 7115 7116 return Math.sqrt(dx * dx + dy * dy); 7117 }, 7118 7119 infinite: true 7120}); 7121 7122CRS.Earth = Earth; 7123CRS.EPSG3395 = EPSG3395; 7124CRS.EPSG3857 = EPSG3857; 7125CRS.EPSG900913 = EPSG900913; 7126CRS.EPSG4326 = EPSG4326; 7127CRS.Simple = Simple; 7128 7129/* 7130 * @class Layer 7131 * @inherits Evented 7132 * @aka L.Layer 7133 * @aka ILayer 7134 * 7135 * A set of methods from the Layer base class that all Leaflet layers use. 7136 * Inherits all methods, options and events from `L.Evented`. 7137 * 7138 * @example 7139 * 7140 * ```js 7141 * var layer = L.marker(latlng).addTo(map); 7142 * layer.addTo(map); 7143 * layer.remove(); 7144 * ``` 7145 * 7146 * @event add: Event 7147 * Fired after the layer is added to a map 7148 * 7149 * @event remove: Event 7150 * Fired after the layer is removed from a map 7151 */ 7152 7153 7154var Layer = Evented.extend({ 7155 7156 // Classes extending `L.Layer` will inherit the following options: 7157 options: { 7158 // @option pane: String = 'overlayPane' 7159 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. 7160 pane: 'overlayPane', 7161 7162 // @option attribution: String = null 7163 // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers. 7164 attribution: null, 7165 7166 bubblingMouseEvents: true 7167 }, 7168 7169 /* @section 7170 * Classes extending `L.Layer` will inherit the following methods: 7171 * 7172 * @method addTo(map: Map|LayerGroup): this 7173 * Adds the layer to the given map or layer group. 7174 */ 7175 addTo: function (map) { 7176 map.addLayer(this); 7177 return this; 7178 }, 7179 7180 // @method remove: this 7181 // Removes the layer from the map it is currently active on. 7182 remove: function () { 7183 return this.removeFrom(this._map || this._mapToAdd); 7184 }, 7185 7186 // @method removeFrom(map: Map): this 7187 // Removes the layer from the given map 7188 removeFrom: function (obj) { 7189 if (obj) { 7190 obj.removeLayer(this); 7191 } 7192 return this; 7193 }, 7194 7195 // @method getPane(name? : String): HTMLElement 7196 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. 7197 getPane: function (name) { 7198 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); 7199 }, 7200 7201 addInteractiveTarget: function (targetEl) { 7202 this._map._targets[stamp(targetEl)] = this; 7203 return this; 7204 }, 7205 7206 removeInteractiveTarget: function (targetEl) { 7207 delete this._map._targets[stamp(targetEl)]; 7208 return this; 7209 }, 7210 7211 // @method getAttribution: String 7212 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). 7213 getAttribution: function () { 7214 return this.options.attribution; 7215 }, 7216 7217 _layerAdd: function (e) { 7218 var map = e.target; 7219 7220 // check in case layer gets added and then removed before the map is ready 7221 if (!map.hasLayer(this)) { return; } 7222 7223 this._map = map; 7224 this._zoomAnimated = map._zoomAnimated; 7225 7226 if (this.getEvents) { 7227 var events = this.getEvents(); 7228 map.on(events, this); 7229 this.once('remove', function () { 7230 map.off(events, this); 7231 }, this); 7232 } 7233 7234 this.onAdd(map); 7235 7236 if (this.getAttribution && map.attributionControl) { 7237 map.attributionControl.addAttribution(this.getAttribution()); 7238 } 7239 7240 this.fire('add'); 7241 map.fire('layeradd', {layer: this}); 7242 } 7243}); 7244 7245/* @section Extension methods 7246 * @uninheritable 7247 * 7248 * Every layer should extend from `L.Layer` and (re-)implement the following methods. 7249 * 7250 * @method onAdd(map: Map): this 7251 * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). 7252 * 7253 * @method onRemove(map: Map): this 7254 * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). 7255 * 7256 * @method getEvents(): Object 7257 * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. 7258 * 7259 * @method getAttribution(): String 7260 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. 7261 * 7262 * @method beforeAdd(map: Map): this 7263 * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. 7264 */ 7265 7266 7267/* @namespace Map 7268 * @section Layer events 7269 * 7270 * @event layeradd: LayerEvent 7271 * Fired when a new layer is added to the map. 7272 * 7273 * @event layerremove: LayerEvent 7274 * Fired when some layer is removed from the map 7275 * 7276 * @section Methods for Layers and Controls 7277 */ 7278Map.include({ 7279 // @method addLayer(layer: Layer): this 7280 // Adds the given layer to the map 7281 addLayer: function (layer) { 7282 if (!layer._layerAdd) { 7283 throw new Error('The provided object is not a Layer.'); 7284 } 7285 7286 var id = stamp(layer); 7287 if (this._layers[id]) { return this; } 7288 this._layers[id] = layer; 7289 7290 layer._mapToAdd = this; 7291 7292 if (layer.beforeAdd) { 7293 layer.beforeAdd(this); 7294 } 7295 7296 this.whenReady(layer._layerAdd, layer); 7297 7298 return this; 7299 }, 7300 7301 // @method removeLayer(layer: Layer): this 7302 // Removes the given layer from the map. 7303 removeLayer: function (layer) { 7304 var id = stamp(layer); 7305 7306 if (!this._layers[id]) { return this; } 7307 7308 if (this._loaded) { 7309 layer.onRemove(this); 7310 } 7311 7312 if (layer.getAttribution && this.attributionControl) { 7313 this.attributionControl.removeAttribution(layer.getAttribution()); 7314 } 7315 7316 delete this._layers[id]; 7317 7318 if (this._loaded) { 7319 this.fire('layerremove', {layer: layer}); 7320 layer.fire('remove'); 7321 } 7322 7323 layer._map = layer._mapToAdd = null; 7324 7325 return this; 7326 }, 7327 7328 // @method hasLayer(layer: Layer): Boolean 7329 // Returns `true` if the given layer is currently added to the map 7330 hasLayer: function (layer) { 7331 return !!layer && (stamp(layer) in this._layers); 7332 }, 7333 7334 /* @method eachLayer(fn: Function, context?: Object): this 7335 * Iterates over the layers of the map, optionally specifying context of the iterator function. 7336 * ``` 7337 * map.eachLayer(function(layer){ 7338 * layer.bindPopup('Hello'); 7339 * }); 7340 * ``` 7341 */ 7342 eachLayer: function (method, context) { 7343 for (var i in this._layers) { 7344 method.call(context, this._layers[i]); 7345 } 7346 return this; 7347 }, 7348 7349 _addLayers: function (layers) { 7350 layers = layers ? (isArray(layers) ? layers : [layers]) : []; 7351 7352 for (var i = 0, len = layers.length; i < len; i++) { 7353 this.addLayer(layers[i]); 7354 } 7355 }, 7356 7357 _addZoomLimit: function (layer) { 7358 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { 7359 this._zoomBoundLayers[stamp(layer)] = layer; 7360 this._updateZoomLevels(); 7361 } 7362 }, 7363 7364 _removeZoomLimit: function (layer) { 7365 var id = stamp(layer); 7366 7367 if (this._zoomBoundLayers[id]) { 7368 delete this._zoomBoundLayers[id]; 7369 this._updateZoomLevels(); 7370 } 7371 }, 7372 7373 _updateZoomLevels: function () { 7374 var minZoom = Infinity, 7375 maxZoom = -Infinity, 7376 oldZoomSpan = this._getZoomSpan(); 7377 7378 for (var i in this._zoomBoundLayers) { 7379 var options = this._zoomBoundLayers[i].options; 7380 7381 minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); 7382 maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); 7383 } 7384 7385 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; 7386 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; 7387 7388 // @section Map state change events 7389 // @event zoomlevelschange: Event 7390 // Fired when the number of zoomlevels on the map is changed due 7391 // to adding or removing a layer. 7392 if (oldZoomSpan !== this._getZoomSpan()) { 7393 this.fire('zoomlevelschange'); 7394 } 7395 7396 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { 7397 this.setZoom(this._layersMaxZoom); 7398 } 7399 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { 7400 this.setZoom(this._layersMinZoom); 7401 } 7402 } 7403}); 7404 7405/* 7406 * @class LayerGroup 7407 * @aka L.LayerGroup 7408 * @inherits Layer 7409 * 7410 * Used to group several layers and handle them as one. If you add it to the map, 7411 * any layers added or removed from the group will be added/removed on the map as 7412 * well. Extends `Layer`. 7413 * 7414 * @example 7415 * 7416 * ```js 7417 * L.layerGroup([marker1, marker2]) 7418 * .addLayer(polyline) 7419 * .addTo(map); 7420 * ``` 7421 */ 7422 7423var LayerGroup = Layer.extend({ 7424 7425 initialize: function (layers, options) { 7426 setOptions(this, options); 7427 7428 this._layers = {}; 7429 7430 var i, len; 7431 7432 if (layers) { 7433 for (i = 0, len = layers.length; i < len; i++) { 7434 this.addLayer(layers[i]); 7435 } 7436 } 7437 }, 7438 7439 // @method addLayer(layer: Layer): this 7440 // Adds the given layer to the group. 7441 addLayer: function (layer) { 7442 var id = this.getLayerId(layer); 7443 7444 this._layers[id] = layer; 7445 7446 if (this._map) { 7447 this._map.addLayer(layer); 7448 } 7449 7450 return this; 7451 }, 7452 7453 // @method removeLayer(layer: Layer): this 7454 // Removes the given layer from the group. 7455 // @alternative 7456 // @method removeLayer(id: Number): this 7457 // Removes the layer with the given internal ID from the group. 7458 removeLayer: function (layer) { 7459 var id = layer in this._layers ? layer : this.getLayerId(layer); 7460 7461 if (this._map && this._layers[id]) { 7462 this._map.removeLayer(this._layers[id]); 7463 } 7464 7465 delete this._layers[id]; 7466 7467 return this; 7468 }, 7469 7470 // @method hasLayer(layer: Layer): Boolean 7471 // Returns `true` if the given layer is currently added to the group. 7472 // @alternative 7473 // @method hasLayer(id: Number): Boolean 7474 // Returns `true` if the given internal ID is currently added to the group. 7475 hasLayer: function (layer) { 7476 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); 7477 }, 7478 7479 // @method clearLayers(): this 7480 // Removes all the layers from the group. 7481 clearLayers: function () { 7482 return this.eachLayer(this.removeLayer, this); 7483 }, 7484 7485 // @method invoke(methodName: String, â¦): this 7486 // Calls `methodName` on every layer contained in this group, passing any 7487 // additional parameters. Has no effect if the layers contained do not 7488 // implement `methodName`. 7489 invoke: function (methodName) { 7490 var args = Array.prototype.slice.call(arguments, 1), 7491 i, layer; 7492 7493 for (i in this._layers) { 7494 layer = this._layers[i]; 7495 7496 if (layer[methodName]) { 7497 layer[methodName].apply(layer, args); 7498 } 7499 } 7500 7501 return this; 7502 }, 7503 7504 onAdd: function (map) { 7505 this.eachLayer(map.addLayer, map); 7506 }, 7507 7508 onRemove: function (map) { 7509 this.eachLayer(map.removeLayer, map); 7510 }, 7511 7512 // @method eachLayer(fn: Function, context?: Object): this 7513 // Iterates over the layers of the group, optionally specifying context of the iterator function. 7514 // ```js 7515 // group.eachLayer(function (layer) { 7516 // layer.bindPopup('Hello'); 7517 // }); 7518 // ``` 7519 eachLayer: function (method, context) { 7520 for (var i in this._layers) { 7521 method.call(context, this._layers[i]); 7522 } 7523 return this; 7524 }, 7525 7526 // @method getLayer(id: Number): Layer 7527 // Returns the layer with the given internal ID. 7528 getLayer: function (id) { 7529 return this._layers[id]; 7530 }, 7531 7532 // @method getLayers(): Layer[] 7533 // Returns an array of all the layers added to the group. 7534 getLayers: function () { 7535 var layers = []; 7536 this.eachLayer(layers.push, layers); 7537 return layers; 7538 }, 7539 7540 // @method setZIndex(zIndex: Number): this 7541 // Calls `setZIndex` on every layer contained in this group, passing the z-index. 7542 setZIndex: function (zIndex) { 7543 return this.invoke('setZIndex', zIndex); 7544 }, 7545 7546 // @method getLayerId(layer: Layer): Number 7547 // Returns the internal ID for a layer 7548 getLayerId: function (layer) { 7549 return stamp(layer); 7550 } 7551}); 7552 7553 7554// @factory L.layerGroup(layers?: Layer[], options?: Object) 7555// Create a layer group, optionally given an initial set of layers and an `options` object. 7556var layerGroup = function (layers, options) { 7557 return new LayerGroup(layers, options); 7558}; 7559 7560/* 7561 * @class FeatureGroup 7562 * @aka L.FeatureGroup 7563 * @inherits LayerGroup 7564 * 7565 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: 7566 * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) 7567 * * Events are propagated to the `FeatureGroup`, so if the group has an event 7568 * handler, it will handle events from any of the layers. This includes mouse events 7569 * and custom events. 7570 * * Has `layeradd` and `layerremove` events 7571 * 7572 * @example 7573 * 7574 * ```js 7575 * L.featureGroup([marker1, marker2, polyline]) 7576 * .bindPopup('Hello world!') 7577 * .on('click', function() { alert('Clicked on a member of the group!'); }) 7578 * .addTo(map); 7579 * ``` 7580 */ 7581 7582var FeatureGroup = LayerGroup.extend({ 7583 7584 addLayer: function (layer) { 7585 if (this.hasLayer(layer)) { 7586 return this; 7587 } 7588 7589 layer.addEventParent(this); 7590 7591 LayerGroup.prototype.addLayer.call(this, layer); 7592 7593 // @event layeradd: LayerEvent 7594 // Fired when a layer is added to this `FeatureGroup` 7595 return this.fire('layeradd', {layer: layer}); 7596 }, 7597 7598 removeLayer: function (layer) { 7599 if (!this.hasLayer(layer)) { 7600 return this; 7601 } 7602 if (layer in this._layers) { 7603 layer = this._layers[layer]; 7604 } 7605 7606 layer.removeEventParent(this); 7607 7608 LayerGroup.prototype.removeLayer.call(this, layer); 7609 7610 // @event layerremove: LayerEvent 7611 // Fired when a layer is removed from this `FeatureGroup` 7612 return this.fire('layerremove', {layer: layer}); 7613 }, 7614 7615 // @method setStyle(style: Path options): this 7616 // Sets the given path options to each layer of the group that has a `setStyle` method. 7617 setStyle: function (style) { 7618 return this.invoke('setStyle', style); 7619 }, 7620 7621 // @method bringToFront(): this 7622 // Brings the layer group to the top of all other layers 7623 bringToFront: function () { 7624 return this.invoke('bringToFront'); 7625 }, 7626 7627 // @method bringToBack(): this 7628 // Brings the layer group to the back of all other layers 7629 bringToBack: function () { 7630 return this.invoke('bringToBack'); 7631 }, 7632 7633 // @method getBounds(): LatLngBounds 7634 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). 7635 getBounds: function () { 7636 var bounds = new LatLngBounds(); 7637 7638 for (var id in this._layers) { 7639 var layer = this._layers[id]; 7640 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); 7641 } 7642 return bounds; 7643 } 7644}); 7645 7646// @factory L.featureGroup(layers: Layer[]) 7647// Create a feature group, optionally given an initial set of layers. 7648var featureGroup = function (layers) { 7649 return new FeatureGroup(layers); 7650}; 7651 7652/* 7653 * @class Icon 7654 * @aka L.Icon 7655 * 7656 * Represents an icon to provide when creating a marker. 7657 * 7658 * @example 7659 * 7660 * ```js 7661 * var myIcon = L.icon({ 7662 * iconUrl: 'my-icon.png', 7663 * iconRetinaUrl: '[email protected]', 7664 * iconSize: [38, 95], 7665 * iconAnchor: [22, 94], 7666 * popupAnchor: [-3, -76], 7667 * shadowUrl: 'my-icon-shadow.png', 7668 * shadowRetinaUrl: '[email protected]', 7669 * shadowSize: [68, 95], 7670 * shadowAnchor: [22, 94] 7671 * }); 7672 * 7673 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 7674 * ``` 7675 * 7676 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. 7677 * 7678 */ 7679 7680var Icon = Class.extend({ 7681 7682 /* @section 7683 * @aka Icon options 7684 * 7685 * @option iconUrl: String = null 7686 * **(required)** The URL to the icon image (absolute or relative to your script path). 7687 * 7688 * @option iconRetinaUrl: String = null 7689 * The URL to a retina sized version of the icon image (absolute or relative to your 7690 * script path). Used for Retina screen devices. 7691 * 7692 * @option iconSize: Point = null 7693 * Size of the icon image in pixels. 7694 * 7695 * @option iconAnchor: Point = null 7696 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon 7697 * will be aligned so that this point is at the marker's geographical location. Centered 7698 * by default if size is specified, also can be set in CSS with negative margins. 7699 * 7700 * @option popupAnchor: Point = [0, 0] 7701 * The coordinates of the point from which popups will "open", relative to the icon anchor. 7702 * 7703 * @option tooltipAnchor: Point = [0, 0] 7704 * The coordinates of the point from which tooltips will "open", relative to the icon anchor. 7705 * 7706 * @option shadowUrl: String = null 7707 * The URL to the icon shadow image. If not specified, no shadow image will be created. 7708 * 7709 * @option shadowRetinaUrl: String = null 7710 * 7711 * @option shadowSize: Point = null 7712 * Size of the shadow image in pixels. 7713 * 7714 * @option shadowAnchor: Point = null 7715 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same 7716 * as iconAnchor if not specified). 7717 * 7718 * @option className: String = '' 7719 * A custom class name to assign to both icon and shadow images. Empty by default. 7720 */ 7721 7722 options: { 7723 popupAnchor: [0, 0], 7724 tooltipAnchor: [0, 0] 7725 }, 7726 7727 initialize: function (options) { 7728 setOptions(this, options); 7729 }, 7730 7731 // @method createIcon(oldIcon?: HTMLElement): HTMLElement 7732 // Called internally when the icon has to be shown, returns a `<img>` HTML element 7733 // styled according to the options. 7734 createIcon: function (oldIcon) { 7735 return this._createIcon('icon', oldIcon); 7736 }, 7737 7738 // @method createShadow(oldIcon?: HTMLElement): HTMLElement 7739 // As `createIcon`, but for the shadow beneath it. 7740 createShadow: function (oldIcon) { 7741 return this._createIcon('shadow', oldIcon); 7742 }, 7743 7744 _createIcon: function (name, oldIcon) { 7745 var src = this._getIconUrl(name); 7746 7747 if (!src) { 7748 if (name === 'icon') { 7749 throw new Error('iconUrl not set in Icon options (see the docs).'); 7750 } 7751 return null; 7752 } 7753 7754 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); 7755 this._setIconStyles(img, name); 7756 7757 return img; 7758 }, 7759 7760 _setIconStyles: function (img, name) { 7761 var options = this.options; 7762 var sizeOption = options[name + 'Size']; 7763 7764 if (typeof sizeOption === 'number') { 7765 sizeOption = [sizeOption, sizeOption]; 7766 } 7767 7768 var size = toPoint(sizeOption), 7769 anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || 7770 size && size.divideBy(2, true)); 7771 7772 img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); 7773 7774 if (anchor) { 7775 img.style.marginLeft = (-anchor.x) + 'px'; 7776 img.style.marginTop = (-anchor.y) + 'px'; 7777 } 7778 7779 if (size) { 7780 img.style.width = size.x + 'px'; 7781 img.style.height = size.y + 'px'; 7782 } 7783 }, 7784 7785 _createImg: function (src, el) { 7786 el = el || document.createElement('img'); 7787 el.src = src; 7788 return el; 7789 }, 7790 7791 _getIconUrl: function (name) { 7792 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; 7793 } 7794}); 7795 7796 7797// @factory L.icon(options: Icon options) 7798// Creates an icon instance with the given options. 7799function icon(options) { 7800 return new Icon(options); 7801} 7802 7803/* 7804 * @miniclass Icon.Default (Icon) 7805 * @aka L.Icon.Default 7806 * @section 7807 * 7808 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when 7809 * no icon is specified. Points to the blue marker image distributed with Leaflet 7810 * releases. 7811 * 7812 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` 7813 * (which is a set of `Icon options`). 7814 * 7815 * If you want to _completely_ replace the default icon, override the 7816 * `L.Marker.prototype.options.icon` with your own icon instead. 7817 */ 7818 7819var IconDefault = Icon.extend({ 7820 7821 options: { 7822 iconUrl: 'marker-icon.png', 7823 iconRetinaUrl: 'marker-icon-2x.png', 7824 shadowUrl: 'marker-shadow.png', 7825 iconSize: [25, 41], 7826 iconAnchor: [12, 41], 7827 popupAnchor: [1, -34], 7828 tooltipAnchor: [16, -28], 7829 shadowSize: [41, 41] 7830 }, 7831 7832 _getIconUrl: function (name) { 7833 if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only 7834 IconDefault.imagePath = this._detectIconPath(); 7835 } 7836 7837 // @option imagePath: String 7838 // `Icon.Default` will try to auto-detect the location of the 7839 // blue icon images. If you are placing these images in a non-standard 7840 // way, set this option to point to the right path. 7841 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); 7842 }, 7843 7844 _detectIconPath: function () { 7845 var el = create$1('div', 'leaflet-default-icon-path', document.body); 7846 var path = getStyle(el, 'background-image') || 7847 getStyle(el, 'backgroundImage'); // IE8 7848 7849 document.body.removeChild(el); 7850 7851 if (path === null || path.indexOf('url') !== 0) { 7852 path = ''; 7853 } else { 7854 path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, ''); 7855 } 7856 7857 return path; 7858 } 7859}); 7860 7861/* 7862 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. 7863 */ 7864 7865 7866/* @namespace Marker 7867 * @section Interaction handlers 7868 * 7869 * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: 7870 * 7871 * ```js 7872 * marker.dragging.disable(); 7873 * ``` 7874 * 7875 * @property dragging: Handler 7876 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). 7877 */ 7878 7879var MarkerDrag = Handler.extend({ 7880 initialize: function (marker) { 7881 this._marker = marker; 7882 }, 7883 7884 addHooks: function () { 7885 var icon = this._marker._icon; 7886 7887 if (!this._draggable) { 7888 this._draggable = new Draggable(icon, icon, true); 7889 } 7890 7891 this._draggable.on({ 7892 dragstart: this._onDragStart, 7893 predrag: this._onPreDrag, 7894 drag: this._onDrag, 7895 dragend: this._onDragEnd 7896 }, this).enable(); 7897 7898 addClass(icon, 'leaflet-marker-draggable'); 7899 }, 7900 7901 removeHooks: function () { 7902 this._draggable.off({ 7903 dragstart: this._onDragStart, 7904 predrag: this._onPreDrag, 7905 drag: this._onDrag, 7906 dragend: this._onDragEnd 7907 }, this).disable(); 7908 7909 if (this._marker._icon) { 7910 removeClass(this._marker._icon, 'leaflet-marker-draggable'); 7911 } 7912 }, 7913 7914 moved: function () { 7915 return this._draggable && this._draggable._moved; 7916 }, 7917 7918 _adjustPan: function (e) { 7919 var marker = this._marker, 7920 map = marker._map, 7921 speed = this._marker.options.autoPanSpeed, 7922 padding = this._marker.options.autoPanPadding, 7923 iconPos = getPosition(marker._icon), 7924 bounds = map.getPixelBounds(), 7925 origin = map.getPixelOrigin(); 7926 7927 var panBounds = toBounds( 7928 bounds.min._subtract(origin).add(padding), 7929 bounds.max._subtract(origin).subtract(padding) 7930 ); 7931 7932 if (!panBounds.contains(iconPos)) { 7933 // Compute incremental movement 7934 var movement = toPoint( 7935 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - 7936 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), 7937 7938 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - 7939 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) 7940 ).multiplyBy(speed); 7941 7942 map.panBy(movement, {animate: false}); 7943 7944 this._draggable._newPos._add(movement); 7945 this._draggable._startPos._add(movement); 7946 7947 setPosition(marker._icon, this._draggable._newPos); 7948 this._onDrag(e); 7949 7950 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7951 } 7952 }, 7953 7954 _onDragStart: function () { 7955 // @section Dragging events 7956 // @event dragstart: Event 7957 // Fired when the user starts dragging the marker. 7958 7959 // @event movestart: Event 7960 // Fired when the marker starts moving (because of dragging). 7961 7962 this._oldLatLng = this._marker.getLatLng(); 7963 this._marker 7964 .closePopup() 7965 .fire('movestart') 7966 .fire('dragstart'); 7967 }, 7968 7969 _onPreDrag: function (e) { 7970 if (this._marker.options.autoPan) { 7971 cancelAnimFrame(this._panRequest); 7972 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7973 } 7974 }, 7975 7976 _onDrag: function (e) { 7977 var marker = this._marker, 7978 shadow = marker._shadow, 7979 iconPos = getPosition(marker._icon), 7980 latlng = marker._map.layerPointToLatLng(iconPos); 7981 7982 // update shadow position 7983 if (shadow) { 7984 setPosition(shadow, iconPos); 7985 } 7986 7987 marker._latlng = latlng; 7988 e.latlng = latlng; 7989 e.oldLatLng = this._oldLatLng; 7990 7991 // @event drag: Event 7992 // Fired repeatedly while the user drags the marker. 7993 marker 7994 .fire('move', e) 7995 .fire('drag', e); 7996 }, 7997 7998 _onDragEnd: function (e) { 7999 // @event dragend: DragEndEvent 8000 // Fired when the user stops dragging the marker. 8001 8002 cancelAnimFrame(this._panRequest); 8003 8004 // @event moveend: Event 8005 // Fired when the marker stops moving (because of dragging). 8006 delete this._oldLatLng; 8007 this._marker 8008 .fire('moveend') 8009 .fire('dragend', e); 8010 } 8011}); 8012 8013/* 8014 * @class Marker 8015 * @inherits Interactive layer 8016 * @aka L.Marker 8017 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. 8018 * 8019 * @example 8020 * 8021 * ```js 8022 * L.marker([50.5, 30.5]).addTo(map); 8023 * ``` 8024 */ 8025 8026var Marker = Layer.extend({ 8027 8028 // @section 8029 // @aka Marker options 8030 options: { 8031 // @option icon: Icon = * 8032 // Icon instance to use for rendering the marker. 8033 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. 8034 // If not specified, a common instance of `L.Icon.Default` is used. 8035 icon: new IconDefault(), 8036 8037 // Option inherited from "Interactive layer" abstract class 8038 interactive: true, 8039 8040 // @option keyboard: Boolean = true 8041 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. 8042 keyboard: true, 8043 8044 // @option title: String = '' 8045 // Text for the browser tooltip that appear on marker hover (no tooltip by default). 8046 title: '', 8047 8048 // @option alt: String = '' 8049 // Text for the `alt` attribute of the icon image (useful for accessibility). 8050 alt: '', 8051 8052 // @option zIndexOffset: Number = 0 8053 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). 8054 zIndexOffset: 0, 8055 8056 // @option opacity: Number = 1.0 8057 // The opacity of the marker. 8058 opacity: 1, 8059 8060 // @option riseOnHover: Boolean = false 8061 // If `true`, the marker will get on top of others when you hover the mouse over it. 8062 riseOnHover: false, 8063 8064 // @option riseOffset: Number = 250 8065 // The z-index offset used for the `riseOnHover` feature. 8066 riseOffset: 250, 8067 8068 // @option pane: String = 'markerPane' 8069 // `Map pane` where the markers icon will be added. 8070 pane: 'markerPane', 8071 8072 // @option pane: String = 'shadowPane' 8073 // `Map pane` where the markers shadow will be added. 8074 shadowPane: 'shadowPane', 8075 8076 // @option bubblingMouseEvents: Boolean = false 8077 // When `true`, a mouse event on this marker will trigger the same event on the map 8078 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 8079 bubblingMouseEvents: false, 8080 8081 // @section Draggable marker options 8082 // @option draggable: Boolean = false 8083 // Whether the marker is draggable with mouse/touch or not. 8084 draggable: false, 8085 8086 // @option autoPan: Boolean = false 8087 // Whether to pan the map when dragging this marker near its edge or not. 8088 autoPan: false, 8089 8090 // @option autoPanPadding: Point = Point(50, 50) 8091 // Distance (in pixels to the left/right and to the top/bottom) of the 8092 // map edge to start panning the map. 8093 autoPanPadding: [50, 50], 8094 8095 // @option autoPanSpeed: Number = 10 8096 // Number of pixels the map should pan by. 8097 autoPanSpeed: 10 8098 }, 8099 8100 /* @section 8101 * 8102 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: 8103 */ 8104 8105 initialize: function (latlng, options) { 8106 setOptions(this, options); 8107 this._latlng = toLatLng(latlng); 8108 }, 8109 8110 onAdd: function (map) { 8111 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; 8112 8113 if (this._zoomAnimated) { 8114 map.on('zoomanim', this._animateZoom, this); 8115 } 8116 8117 this._initIcon(); 8118 this.update(); 8119 }, 8120 8121 onRemove: function (map) { 8122 if (this.dragging && this.dragging.enabled()) { 8123 this.options.draggable = true; 8124 this.dragging.removeHooks(); 8125 } 8126 delete this.dragging; 8127 8128 if (this._zoomAnimated) { 8129 map.off('zoomanim', this._animateZoom, this); 8130 } 8131 8132 this._removeIcon(); 8133 this._removeShadow(); 8134 }, 8135 8136 getEvents: function () { 8137 return { 8138 zoom: this.update, 8139 viewreset: this.update 8140 }; 8141 }, 8142 8143 // @method getLatLng: LatLng 8144 // Returns the current geographical position of the marker. 8145 getLatLng: function () { 8146 return this._latlng; 8147 }, 8148 8149 // @method setLatLng(latlng: LatLng): this 8150 // Changes the marker position to the given point. 8151 setLatLng: function (latlng) { 8152 var oldLatLng = this._latlng; 8153 this._latlng = toLatLng(latlng); 8154 this.update(); 8155 8156 // @event move: Event 8157 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. 8158 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); 8159 }, 8160 8161 // @method setZIndexOffset(offset: Number): this 8162 // Changes the [zIndex offset](#marker-zindexoffset) of the marker. 8163 setZIndexOffset: function (offset) { 8164 this.options.zIndexOffset = offset; 8165 return this.update(); 8166 }, 8167 8168 // @method getIcon: Icon 8169 // Returns the current icon used by the marker 8170 getIcon: function () { 8171 return this.options.icon; 8172 }, 8173 8174 // @method setIcon(icon: Icon): this 8175 // Changes the marker icon. 8176 setIcon: function (icon) { 8177 8178 this.options.icon = icon; 8179 8180 if (this._map) { 8181 this._initIcon(); 8182 this.update(); 8183 } 8184 8185 if (this._popup) { 8186 this.bindPopup(this._popup, this._popup.options); 8187 } 8188 8189 return this; 8190 }, 8191 8192 getElement: function () { 8193 return this._icon; 8194 }, 8195 8196 update: function () { 8197 8198 if (this._icon && this._map) { 8199 var pos = this._map.latLngToLayerPoint(this._latlng).round(); 8200 this._setPos(pos); 8201 } 8202 8203 return this; 8204 }, 8205 8206 _initIcon: function () { 8207 var options = this.options, 8208 classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 8209 8210 var icon = options.icon.createIcon(this._icon), 8211 addIcon = false; 8212 8213 // if we're not reusing the icon, remove the old one and init new one 8214 if (icon !== this._icon) { 8215 if (this._icon) { 8216 this._removeIcon(); 8217 } 8218 addIcon = true; 8219 8220 if (options.title) { 8221 icon.title = options.title; 8222 } 8223 8224 if (icon.tagName === 'IMG') { 8225 icon.alt = options.alt || ''; 8226 } 8227 } 8228 8229 addClass(icon, classToAdd); 8230 8231 if (options.keyboard) { 8232 icon.tabIndex = '0'; 8233 } 8234 8235 this._icon = icon; 8236 8237 if (options.riseOnHover) { 8238 this.on({ 8239 mouseover: this._bringToFront, 8240 mouseout: this._resetZIndex 8241 }); 8242 } 8243 8244 var newShadow = options.icon.createShadow(this._shadow), 8245 addShadow = false; 8246 8247 if (newShadow !== this._shadow) { 8248 this._removeShadow(); 8249 addShadow = true; 8250 } 8251 8252 if (newShadow) { 8253 addClass(newShadow, classToAdd); 8254 newShadow.alt = ''; 8255 } 8256 this._shadow = newShadow; 8257 8258 8259 if (options.opacity < 1) { 8260 this._updateOpacity(); 8261 } 8262 8263 8264 if (addIcon) { 8265 this.getPane().appendChild(this._icon); 8266 } 8267 this._initInteraction(); 8268 if (newShadow && addShadow) { 8269 this.getPane(options.shadowPane).appendChild(this._shadow); 8270 } 8271 }, 8272 8273 _removeIcon: function () { 8274 if (this.options.riseOnHover) { 8275 this.off({ 8276 mouseover: this._bringToFront, 8277 mouseout: this._resetZIndex 8278 }); 8279 } 8280 8281 remove(this._icon); 8282 this.removeInteractiveTarget(this._icon); 8283 8284 this._icon = null; 8285 }, 8286 8287 _removeShadow: function () { 8288 if (this._shadow) { 8289 remove(this._shadow); 8290 } 8291 this._shadow = null; 8292 }, 8293 8294 _setPos: function (pos) { 8295 8296 if (this._icon) { 8297 setPosition(this._icon, pos); 8298 } 8299 8300 if (this._shadow) { 8301 setPosition(this._shadow, pos); 8302 } 8303 8304 this._zIndex = pos.y + this.options.zIndexOffset; 8305 8306 this._resetZIndex(); 8307 }, 8308 8309 _updateZIndex: function (offset) { 8310 if (this._icon) { 8311 this._icon.style.zIndex = this._zIndex + offset; 8312 } 8313 }, 8314 8315 _animateZoom: function (opt) { 8316 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); 8317 8318 this._setPos(pos); 8319 }, 8320 8321 _initInteraction: function () { 8322 8323 if (!this.options.interactive) { return; } 8324 8325 addClass(this._icon, 'leaflet-interactive'); 8326 8327 this.addInteractiveTarget(this._icon); 8328 8329 if (MarkerDrag) { 8330 var draggable = this.options.draggable; 8331 if (this.dragging) { 8332 draggable = this.dragging.enabled(); 8333 this.dragging.disable(); 8334 } 8335 8336 this.dragging = new MarkerDrag(this); 8337 8338 if (draggable) { 8339 this.dragging.enable(); 8340 } 8341 } 8342 }, 8343 8344 // @method setOpacity(opacity: Number): this 8345 // Changes the opacity of the marker. 8346 setOpacity: function (opacity) { 8347 this.options.opacity = opacity; 8348 if (this._map) { 8349 this._updateOpacity(); 8350 } 8351 8352 return this; 8353 }, 8354 8355 _updateOpacity: function () { 8356 var opacity = this.options.opacity; 8357 8358 if (this._icon) { 8359 setOpacity(this._icon, opacity); 8360 } 8361 8362 if (this._shadow) { 8363 setOpacity(this._shadow, opacity); 8364 } 8365 }, 8366 8367 _bringToFront: function () { 8368 this._updateZIndex(this.options.riseOffset); 8369 }, 8370 8371 _resetZIndex: function () { 8372 this._updateZIndex(0); 8373 }, 8374 8375 _getPopupAnchor: function () { 8376 return this.options.icon.options.popupAnchor; 8377 }, 8378 8379 _getTooltipAnchor: function () { 8380 return this.options.icon.options.tooltipAnchor; 8381 } 8382}); 8383 8384 8385// factory L.marker(latlng: LatLng, options? : Marker options) 8386 8387// @factory L.marker(latlng: LatLng, options? : Marker options) 8388// Instantiates a Marker object given a geographical point and optionally an options object. 8389function marker(latlng, options) { 8390 return new Marker(latlng, options); 8391} 8392 8393/* 8394 * @class Path 8395 * @aka L.Path 8396 * @inherits Interactive layer 8397 * 8398 * An abstract class that contains options and constants shared between vector 8399 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. 8400 */ 8401 8402var Path = Layer.extend({ 8403 8404 // @section 8405 // @aka Path options 8406 options: { 8407 // @option stroke: Boolean = true 8408 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. 8409 stroke: true, 8410 8411 // @option color: String = '#3388ff' 8412 // Stroke color 8413 color: '#3388ff', 8414 8415 // @option weight: Number = 3 8416 // Stroke width in pixels 8417 weight: 3, 8418 8419 // @option opacity: Number = 1.0 8420 // Stroke opacity 8421 opacity: 1, 8422 8423 // @option lineCap: String= 'round' 8424 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. 8425 lineCap: 'round', 8426 8427 // @option lineJoin: String = 'round' 8428 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. 8429 lineJoin: 'round', 8430 8431 // @option dashArray: String = null 8432 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 8433 dashArray: null, 8434 8435 // @option dashOffset: String = null 8436 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 8437 dashOffset: null, 8438 8439 // @option fill: Boolean = depends 8440 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. 8441 fill: false, 8442 8443 // @option fillColor: String = * 8444 // Fill color. Defaults to the value of the [`color`](#path-color) option 8445 fillColor: null, 8446 8447 // @option fillOpacity: Number = 0.2 8448 // Fill opacity. 8449 fillOpacity: 0.2, 8450 8451 // @option fillRule: String = 'evenodd' 8452 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. 8453 fillRule: 'evenodd', 8454 8455 // className: '', 8456 8457 // Option inherited from "Interactive layer" abstract class 8458 interactive: true, 8459 8460 // @option bubblingMouseEvents: Boolean = true 8461 // When `true`, a mouse event on this path will trigger the same event on the map 8462 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 8463 bubblingMouseEvents: true 8464 }, 8465 8466 beforeAdd: function (map) { 8467 // Renderer is set here because we need to call renderer.getEvents 8468 // before this.getEvents. 8469 this._renderer = map.getRenderer(this); 8470 }, 8471 8472 onAdd: function () { 8473 this._renderer._initPath(this); 8474 this._reset(); 8475 this._renderer._addPath(this); 8476 }, 8477 8478 onRemove: function () { 8479 this._renderer._removePath(this); 8480 }, 8481 8482 // @method redraw(): this 8483 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. 8484 redraw: function () { 8485 if (this._map) { 8486 this._renderer._updatePath(this); 8487 } 8488 return this; 8489 }, 8490 8491 // @method setStyle(style: Path options): this 8492 // Changes the appearance of a Path based on the options in the `Path options` object. 8493 setStyle: function (style) { 8494 setOptions(this, style); 8495 if (this._renderer) { 8496 this._renderer._updateStyle(this); 8497 if (this.options.stroke && style && style.hasOwnProperty('weight')) { 8498 this._updateBounds(); 8499 } 8500 } 8501 return this; 8502 }, 8503 8504 // @method bringToFront(): this 8505 // Brings the layer to the top of all path layers. 8506 bringToFront: function () { 8507 if (this._renderer) { 8508 this._renderer._bringToFront(this); 8509 } 8510 return this; 8511 }, 8512 8513 // @method bringToBack(): this 8514 // Brings the layer to the bottom of all path layers. 8515 bringToBack: function () { 8516 if (this._renderer) { 8517 this._renderer._bringToBack(this); 8518 } 8519 return this; 8520 }, 8521 8522 getElement: function () { 8523 return this._path; 8524 }, 8525 8526 _reset: function () { 8527 // defined in child classes 8528 this._project(); 8529 this._update(); 8530 }, 8531 8532 _clickTolerance: function () { 8533 // used when doing hit detection for Canvas layers 8534 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance; 8535 } 8536}); 8537 8538/* 8539 * @class CircleMarker 8540 * @aka L.CircleMarker 8541 * @inherits Path 8542 * 8543 * A circle of a fixed size with radius specified in pixels. Extends `Path`. 8544 */ 8545 8546var CircleMarker = Path.extend({ 8547 8548 // @section 8549 // @aka CircleMarker options 8550 options: { 8551 fill: true, 8552 8553 // @option radius: Number = 10 8554 // Radius of the circle marker, in pixels 8555 radius: 10 8556 }, 8557 8558 initialize: function (latlng, options) { 8559 setOptions(this, options); 8560 this._latlng = toLatLng(latlng); 8561 this._radius = this.options.radius; 8562 }, 8563 8564 // @method setLatLng(latLng: LatLng): this 8565 // Sets the position of a circle marker to a new location. 8566 setLatLng: function (latlng) { 8567 var oldLatLng = this._latlng; 8568 this._latlng = toLatLng(latlng); 8569 this.redraw(); 8570 8571 // @event move: Event 8572 // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. 8573 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); 8574 }, 8575 8576 // @method getLatLng(): LatLng 8577 // Returns the current geographical position of the circle marker 8578 getLatLng: function () { 8579 return this._latlng; 8580 }, 8581 8582 // @method setRadius(radius: Number): this 8583 // Sets the radius of a circle marker. Units are in pixels. 8584 setRadius: function (radius) { 8585 this.options.radius = this._radius = radius; 8586 return this.redraw(); 8587 }, 8588 8589 // @method getRadius(): Number 8590 // Returns the current radius of the circle 8591 getRadius: function () { 8592 return this._radius; 8593 }, 8594 8595 setStyle : function (options) { 8596 var radius = options && options.radius || this._radius; 8597 Path.prototype.setStyle.call(this, options); 8598 this.setRadius(radius); 8599 return this; 8600 }, 8601 8602 _project: function () { 8603 this._point = this._map.latLngToLayerPoint(this._latlng); 8604 this._updateBounds(); 8605 }, 8606 8607 _updateBounds: function () { 8608 var r = this._radius, 8609 r2 = this._radiusY || r, 8610 w = this._clickTolerance(), 8611 p = [r + w, r2 + w]; 8612 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); 8613 }, 8614 8615 _update: function () { 8616 if (this._map) { 8617 this._updatePath(); 8618 } 8619 }, 8620 8621 _updatePath: function () { 8622 this._renderer._updateCircle(this); 8623 }, 8624 8625 _empty: function () { 8626 return this._radius && !this._renderer._bounds.intersects(this._pxBounds); 8627 }, 8628 8629 // Needed by the `Canvas` renderer for interactivity 8630 _containsPoint: function (p) { 8631 return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); 8632 } 8633}); 8634 8635 8636// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) 8637// Instantiates a circle marker object given a geographical point, and an optional options object. 8638function circleMarker(latlng, options) { 8639 return new CircleMarker(latlng, options); 8640} 8641 8642/* 8643 * @class Circle 8644 * @aka L.Circle 8645 * @inherits CircleMarker 8646 * 8647 * A class for drawing circle overlays on a map. Extends `CircleMarker`. 8648 * 8649 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). 8650 * 8651 * @example 8652 * 8653 * ```js 8654 * L.circle([50.5, 30.5], {radius: 200}).addTo(map); 8655 * ``` 8656 */ 8657 8658var Circle = CircleMarker.extend({ 8659 8660 initialize: function (latlng, options, legacyOptions) { 8661 if (typeof options === 'number') { 8662 // Backwards compatibility with 0.7.x factory (latlng, radius, options?) 8663 options = extend({}, legacyOptions, {radius: options}); 8664 } 8665 setOptions(this, options); 8666 this._latlng = toLatLng(latlng); 8667 8668 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } 8669 8670 // @section 8671 // @aka Circle options 8672 // @option radius: Number; Radius of the circle, in meters. 8673 this._mRadius = this.options.radius; 8674 }, 8675 8676 // @method setRadius(radius: Number): this 8677 // Sets the radius of a circle. Units are in meters. 8678 setRadius: function (radius) { 8679 this._mRadius = radius; 8680 return this.redraw(); 8681 }, 8682 8683 // @method getRadius(): Number 8684 // Returns the current radius of a circle. Units are in meters. 8685 getRadius: function () { 8686 return this._mRadius; 8687 }, 8688 8689 // @method getBounds(): LatLngBounds 8690 // Returns the `LatLngBounds` of the path. 8691 getBounds: function () { 8692 var half = [this._radius, this._radiusY || this._radius]; 8693 8694 return new LatLngBounds( 8695 this._map.layerPointToLatLng(this._point.subtract(half)), 8696 this._map.layerPointToLatLng(this._point.add(half))); 8697 }, 8698 8699 setStyle: Path.prototype.setStyle, 8700 8701 _project: function () { 8702 8703 var lng = this._latlng.lng, 8704 lat = this._latlng.lat, 8705 map = this._map, 8706 crs = map.options.crs; 8707 8708 if (crs.distance === Earth.distance) { 8709 var d = Math.PI / 180, 8710 latR = (this._mRadius / Earth.R) / d, 8711 top = map.project([lat + latR, lng]), 8712 bottom = map.project([lat - latR, lng]), 8713 p = top.add(bottom).divideBy(2), 8714 lat2 = map.unproject(p).lat, 8715 lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / 8716 (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; 8717 8718 if (isNaN(lngR) || lngR === 0) { 8719 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 8720 } 8721 8722 this._point = p.subtract(map.getPixelOrigin()); 8723 this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; 8724 this._radiusY = p.y - top.y; 8725 8726 } else { 8727 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); 8728 8729 this._point = map.latLngToLayerPoint(this._latlng); 8730 this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; 8731 } 8732 8733 this._updateBounds(); 8734 } 8735}); 8736 8737// @factory L.circle(latlng: LatLng, options?: Circle options) 8738// Instantiates a circle object given a geographical point, and an options object 8739// which contains the circle radius. 8740// @alternative 8741// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) 8742// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. 8743// Do not use in new applications or plugins. 8744function circle(latlng, options, legacyOptions) { 8745 return new Circle(latlng, options, legacyOptions); 8746} 8747 8748/* 8749 * @class Polyline 8750 * @aka L.Polyline 8751 * @inherits Path 8752 * 8753 * A class for drawing polyline overlays on a map. Extends `Path`. 8754 * 8755 * @example 8756 * 8757 * ```js 8758 * // create a red polyline from an array of LatLng points 8759 * var latlngs = [ 8760 * [45.51, -122.68], 8761 * [37.77, -122.43], 8762 * [34.04, -118.2] 8763 * ]; 8764 * 8765 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); 8766 * 8767 * // zoom the map to the polyline 8768 * map.fitBounds(polyline.getBounds()); 8769 * ``` 8770 * 8771 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: 8772 * 8773 * ```js 8774 * // create a red polyline from an array of arrays of LatLng points 8775 * var latlngs = [ 8776 * [[45.51, -122.68], 8777 * [37.77, -122.43], 8778 * [34.04, -118.2]], 8779 * [[40.78, -73.91], 8780 * [41.83, -87.62], 8781 * [32.76, -96.72]] 8782 * ]; 8783 * ``` 8784 */ 8785 8786 8787var Polyline = Path.extend({ 8788 8789 // @section 8790 // @aka Polyline options 8791 options: { 8792 // @option smoothFactor: Number = 1.0 8793 // How much to simplify the polyline on each zoom level. More means 8794 // better performance and smoother look, and less means more accurate representation. 8795 smoothFactor: 1.0, 8796 8797 // @option noClip: Boolean = false 8798 // Disable polyline clipping. 8799 noClip: false 8800 }, 8801 8802 initialize: function (latlngs, options) { 8803 setOptions(this, options); 8804 this._setLatLngs(latlngs); 8805 }, 8806 8807 // @method getLatLngs(): LatLng[] 8808 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. 8809 getLatLngs: function () { 8810 return this._latlngs; 8811 }, 8812 8813 // @method setLatLngs(latlngs: LatLng[]): this 8814 // Replaces all the points in the polyline with the given array of geographical points. 8815 setLatLngs: function (latlngs) { 8816 this._setLatLngs(latlngs); 8817 return this.redraw(); 8818 }, 8819 8820 // @method isEmpty(): Boolean 8821 // Returns `true` if the Polyline has no LatLngs. 8822 isEmpty: function () { 8823 return !this._latlngs.length; 8824 }, 8825 8826 // @method closestLayerPoint(p: Point): Point 8827 // Returns the point closest to `p` on the Polyline. 8828 closestLayerPoint: function (p) { 8829 var minDistance = Infinity, 8830 minPoint = null, 8831 closest = _sqClosestPointOnSegment, 8832 p1, p2; 8833 8834 for (var j = 0, jLen = this._parts.length; j < jLen; j++) { 8835 var points = this._parts[j]; 8836 8837 for (var i = 1, len = points.length; i < len; i++) { 8838 p1 = points[i - 1]; 8839 p2 = points[i]; 8840 8841 var sqDist = closest(p, p1, p2, true); 8842 8843 if (sqDist < minDistance) { 8844 minDistance = sqDist; 8845 minPoint = closest(p, p1, p2); 8846 } 8847 } 8848 } 8849 if (minPoint) { 8850 minPoint.distance = Math.sqrt(minDistance); 8851 } 8852 return minPoint; 8853 }, 8854 8855 // @method getCenter(): LatLng 8856 // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. 8857 getCenter: function () { 8858 // throws error when not yet added to map as this center calculation requires projected coordinates 8859 if (!this._map) { 8860 throw new Error('Must add layer to map before using getCenter()'); 8861 } 8862 8863 var i, halfDist, segDist, dist, p1, p2, ratio, 8864 points = this._rings[0], 8865 len = points.length; 8866 8867 if (!len) { return null; } 8868 8869 // polyline centroid algorithm; only uses the first ring if there are multiple 8870 8871 for (i = 0, halfDist = 0; i < len - 1; i++) { 8872 halfDist += points[i].distanceTo(points[i + 1]) / 2; 8873 } 8874 8875 // The line is so small in the current view that all points are on the same pixel. 8876 if (halfDist === 0) { 8877 return this._map.layerPointToLatLng(points[0]); 8878 } 8879 8880 for (i = 0, dist = 0; i < len - 1; i++) { 8881 p1 = points[i]; 8882 p2 = points[i + 1]; 8883 segDist = p1.distanceTo(p2); 8884 dist += segDist; 8885 8886 if (dist > halfDist) { 8887 ratio = (dist - halfDist) / segDist; 8888 return this._map.layerPointToLatLng([ 8889 p2.x - ratio * (p2.x - p1.x), 8890 p2.y - ratio * (p2.y - p1.y) 8891 ]); 8892 } 8893 } 8894 }, 8895 8896 // @method getBounds(): LatLngBounds 8897 // Returns the `LatLngBounds` of the path. 8898 getBounds: function () { 8899 return this._bounds; 8900 }, 8901 8902 // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this 8903 // Adds a given point to the polyline. By default, adds to the first ring of 8904 // the polyline in case of a multi-polyline, but can be overridden by passing 8905 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). 8906 addLatLng: function (latlng, latlngs) { 8907 latlngs = latlngs || this._defaultShape(); 8908 latlng = toLatLng(latlng); 8909 latlngs.push(latlng); 8910 this._bounds.extend(latlng); 8911 return this.redraw(); 8912 }, 8913 8914 _setLatLngs: function (latlngs) { 8915 this._bounds = new LatLngBounds(); 8916 this._latlngs = this._convertLatLngs(latlngs); 8917 }, 8918 8919 _defaultShape: function () { 8920 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; 8921 }, 8922 8923 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way 8924 _convertLatLngs: function (latlngs) { 8925 var result = [], 8926 flat = isFlat(latlngs); 8927 8928 for (var i = 0, len = latlngs.length; i < len; i++) { 8929 if (flat) { 8930 result[i] = toLatLng(latlngs[i]); 8931 this._bounds.extend(result[i]); 8932 } else { 8933 result[i] = this._convertLatLngs(latlngs[i]); 8934 } 8935 } 8936 8937 return result; 8938 }, 8939 8940 _project: function () { 8941 var pxBounds = new Bounds(); 8942 this._rings = []; 8943 this._projectLatlngs(this._latlngs, this._rings, pxBounds); 8944 8945 if (this._bounds.isValid() && pxBounds.isValid()) { 8946 this._rawPxBounds = pxBounds; 8947 this._updateBounds(); 8948 } 8949 }, 8950 8951 _updateBounds: function () { 8952 var w = this._clickTolerance(), 8953 p = new Point(w, w); 8954 this._pxBounds = new Bounds([ 8955 this._rawPxBounds.min.subtract(p), 8956 this._rawPxBounds.max.add(p) 8957 ]); 8958 }, 8959 8960 // recursively turns latlngs into a set of rings with projected coordinates 8961 _projectLatlngs: function (latlngs, result, projectedBounds) { 8962 var flat = latlngs[0] instanceof LatLng, 8963 len = latlngs.length, 8964 i, ring; 8965 8966 if (flat) { 8967 ring = []; 8968 for (i = 0; i < len; i++) { 8969 ring[i] = this._map.latLngToLayerPoint(latlngs[i]); 8970 projectedBounds.extend(ring[i]); 8971 } 8972 result.push(ring); 8973 } else { 8974 for (i = 0; i < len; i++) { 8975 this._projectLatlngs(latlngs[i], result, projectedBounds); 8976 } 8977 } 8978 }, 8979 8980 // clip polyline by renderer bounds so that we have less to render for performance 8981 _clipPoints: function () { 8982 var bounds = this._renderer._bounds; 8983 8984 this._parts = []; 8985 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 8986 return; 8987 } 8988 8989 if (this.options.noClip) { 8990 this._parts = this._rings; 8991 return; 8992 } 8993 8994 var parts = this._parts, 8995 i, j, k, len, len2, segment, points; 8996 8997 for (i = 0, k = 0, len = this._rings.length; i < len; i++) { 8998 points = this._rings[i]; 8999 9000 for (j = 0, len2 = points.length; j < len2 - 1; j++) { 9001 segment = clipSegment(points[j], points[j + 1], bounds, j, true); 9002 9003 if (!segment) { continue; } 9004 9005 parts[k] = parts[k] || []; 9006 parts[k].push(segment[0]); 9007 9008 // if segment goes out of screen, or it's the last one, it's the end of the line part 9009 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { 9010 parts[k].push(segment[1]); 9011 k++; 9012 } 9013 } 9014 } 9015 }, 9016 9017 // simplify each clipped part of the polyline for performance 9018 _simplifyPoints: function () { 9019 var parts = this._parts, 9020 tolerance = this.options.smoothFactor; 9021 9022 for (var i = 0, len = parts.length; i < len; i++) { 9023 parts[i] = simplify(parts[i], tolerance); 9024 } 9025 }, 9026 9027 _update: function () { 9028 if (!this._map) { return; } 9029 9030 this._clipPoints(); 9031 this._simplifyPoints(); 9032 this._updatePath(); 9033 }, 9034 9035 _updatePath: function () { 9036 this._renderer._updatePoly(this); 9037 }, 9038 9039 // Needed by the `Canvas` renderer for interactivity 9040 _containsPoint: function (p, closed) { 9041 var i, j, k, len, len2, part, 9042 w = this._clickTolerance(); 9043 9044 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 9045 9046 // hit detection for polylines 9047 for (i = 0, len = this._parts.length; i < len; i++) { 9048 part = this._parts[i]; 9049 9050 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 9051 if (!closed && (j === 0)) { continue; } 9052 9053 if (pointToSegmentDistance(p, part[k], part[j]) <= w) { 9054 return true; 9055 } 9056 } 9057 } 9058 return false; 9059 } 9060}); 9061 9062// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) 9063// Instantiates a polyline object given an array of geographical points and 9064// optionally an options object. You can create a `Polyline` object with 9065// multiple separate lines (`MultiPolyline`) by passing an array of arrays 9066// of geographic points. 9067function polyline(latlngs, options) { 9068 return new Polyline(latlngs, options); 9069} 9070 9071// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. 9072Polyline._flat = _flat; 9073 9074/* 9075 * @class Polygon 9076 * @aka L.Polygon 9077 * @inherits Polyline 9078 * 9079 * A class for drawing polygon overlays on a map. Extends `Polyline`. 9080 * 9081 * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one â it's better to filter out such points. 9082 * 9083 * 9084 * @example 9085 * 9086 * ```js 9087 * // create a red polygon from an array of LatLng points 9088 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; 9089 * 9090 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); 9091 * 9092 * // zoom the map to the polygon 9093 * map.fitBounds(polygon.getBounds()); 9094 * ``` 9095 * 9096 * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: 9097 * 9098 * ```js 9099 * var latlngs = [ 9100 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 9101 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 9102 * ]; 9103 * ``` 9104 * 9105 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. 9106 * 9107 * ```js 9108 * var latlngs = [ 9109 * [ // first polygon 9110 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 9111 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 9112 * ], 9113 * [ // second polygon 9114 * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] 9115 * ] 9116 * ]; 9117 * ``` 9118 */ 9119 9120var Polygon = Polyline.extend({ 9121 9122 options: { 9123 fill: true 9124 }, 9125 9126 isEmpty: function () { 9127 return !this._latlngs.length || !this._latlngs[0].length; 9128 }, 9129 9130 getCenter: function () { 9131 // throws error when not yet added to map as this center calculation requires projected coordinates 9132 if (!this._map) { 9133 throw new Error('Must add layer to map before using getCenter()'); 9134 } 9135 9136 var i, j, p1, p2, f, area, x, y, center, 9137 points = this._rings[0], 9138 len = points.length; 9139 9140 if (!len) { return null; } 9141 9142 // polygon centroid algorithm; only uses the first ring if there are multiple 9143 9144 area = x = y = 0; 9145 9146 for (i = 0, j = len - 1; i < len; j = i++) { 9147 p1 = points[i]; 9148 p2 = points[j]; 9149 9150 f = p1.y * p2.x - p2.y * p1.x; 9151 x += (p1.x + p2.x) * f; 9152 y += (p1.y + p2.y) * f; 9153 area += f * 3; 9154 } 9155 9156 if (area === 0) { 9157 // Polygon is so small that all points are on same pixel. 9158 center = points[0]; 9159 } else { 9160 center = [x / area, y / area]; 9161 } 9162 return this._map.layerPointToLatLng(center); 9163 }, 9164 9165 _convertLatLngs: function (latlngs) { 9166 var result = Polyline.prototype._convertLatLngs.call(this, latlngs), 9167 len = result.length; 9168 9169 // remove last point if it equals first one 9170 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { 9171 result.pop(); 9172 } 9173 return result; 9174 }, 9175 9176 _setLatLngs: function (latlngs) { 9177 Polyline.prototype._setLatLngs.call(this, latlngs); 9178 if (isFlat(this._latlngs)) { 9179 this._latlngs = [this._latlngs]; 9180 } 9181 }, 9182 9183 _defaultShape: function () { 9184 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; 9185 }, 9186 9187 _clipPoints: function () { 9188 // polygons need a different clipping algorithm so we redefine that 9189 9190 var bounds = this._renderer._bounds, 9191 w = this.options.weight, 9192 p = new Point(w, w); 9193 9194 // increase clip padding by stroke width to avoid stroke on clip edges 9195 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); 9196 9197 this._parts = []; 9198 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 9199 return; 9200 } 9201 9202 if (this.options.noClip) { 9203 this._parts = this._rings; 9204 return; 9205 } 9206 9207 for (var i = 0, len = this._rings.length, clipped; i < len; i++) { 9208 clipped = clipPolygon(this._rings[i], bounds, true); 9209 if (clipped.length) { 9210 this._parts.push(clipped); 9211 } 9212 } 9213 }, 9214 9215 _updatePath: function () { 9216 this._renderer._updatePoly(this, true); 9217 }, 9218 9219 // Needed by the `Canvas` renderer for interactivity 9220 _containsPoint: function (p) { 9221 var inside = false, 9222 part, p1, p2, i, j, k, len, len2; 9223 9224 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 9225 9226 // ray casting algorithm for detecting if point is in polygon 9227 for (i = 0, len = this._parts.length; i < len; i++) { 9228 part = this._parts[i]; 9229 9230 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 9231 p1 = part[j]; 9232 p2 = part[k]; 9233 9234 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { 9235 inside = !inside; 9236 } 9237 } 9238 } 9239 9240 // also check if it's on polygon stroke 9241 return inside || Polyline.prototype._containsPoint.call(this, p, true); 9242 } 9243 9244}); 9245 9246 9247// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) 9248function polygon(latlngs, options) { 9249 return new Polygon(latlngs, options); 9250} 9251 9252/* 9253 * @class GeoJSON 9254 * @aka L.GeoJSON 9255 * @inherits FeatureGroup 9256 * 9257 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse 9258 * GeoJSON data and display it on the map. Extends `FeatureGroup`. 9259 * 9260 * @example 9261 * 9262 * ```js 9263 * L.geoJSON(data, { 9264 * style: function (feature) { 9265 * return {color: feature.properties.color}; 9266 * } 9267 * }).bindPopup(function (layer) { 9268 * return layer.feature.properties.description; 9269 * }).addTo(map); 9270 * ``` 9271 */ 9272 9273var GeoJSON = FeatureGroup.extend({ 9274 9275 /* @section 9276 * @aka GeoJSON options 9277 * 9278 * @option pointToLayer: Function = * 9279 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally 9280 * called when data is added, passing the GeoJSON point feature and its `LatLng`. 9281 * The default is to spawn a default `Marker`: 9282 * ```js 9283 * function(geoJsonPoint, latlng) { 9284 * return L.marker(latlng); 9285 * } 9286 * ``` 9287 * 9288 * @option style: Function = * 9289 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, 9290 * called internally when data is added. 9291 * The default value is to not override any defaults: 9292 * ```js 9293 * function (geoJsonFeature) { 9294 * return {} 9295 * } 9296 * ``` 9297 * 9298 * @option onEachFeature: Function = * 9299 * A `Function` that will be called once for each created `Feature`, after it has 9300 * been created and styled. Useful for attaching events and popups to features. 9301 * The default is to do nothing with the newly created layers: 9302 * ```js 9303 * function (feature, layer) {} 9304 * ``` 9305 * 9306 * @option filter: Function = * 9307 * A `Function` that will be used to decide whether to include a feature or not. 9308 * The default is to include all features: 9309 * ```js 9310 * function (geoJsonFeature) { 9311 * return true; 9312 * } 9313 * ``` 9314 * Note: dynamically changing the `filter` option will have effect only on newly 9315 * added data. It will _not_ re-evaluate already included features. 9316 * 9317 * @option coordsToLatLng: Function = * 9318 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. 9319 * The default is the `coordsToLatLng` static method. 9320 * 9321 * @option markersInheritOptions: Boolean = false 9322 * Whether default Markers for "Point" type Features inherit from group options. 9323 */ 9324 9325 initialize: function (geojson, options) { 9326 setOptions(this, options); 9327 9328 this._layers = {}; 9329 9330 if (geojson) { 9331 this.addData(geojson); 9332 } 9333 }, 9334 9335 // @method addData( <GeoJSON> data ): this 9336 // Adds a GeoJSON object to the layer. 9337 addData: function (geojson) { 9338 var features = isArray(geojson) ? geojson : geojson.features, 9339 i, len, feature; 9340 9341 if (features) { 9342 for (i = 0, len = features.length; i < len; i++) { 9343 // only add this if geometry or geometries are set and not null 9344 feature = features[i]; 9345 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { 9346 this.addData(feature); 9347 } 9348 } 9349 return this; 9350 } 9351 9352 var options = this.options; 9353 9354 if (options.filter && !options.filter(geojson)) { return this; } 9355 9356 var layer = geometryToLayer(geojson, options); 9357 if (!layer) { 9358 return this; 9359 } 9360 layer.feature = asFeature(geojson); 9361 9362 layer.defaultOptions = layer.options; 9363 this.resetStyle(layer); 9364 9365 if (options.onEachFeature) { 9366 options.onEachFeature(geojson, layer); 9367 } 9368 9369 return this.addLayer(layer); 9370 }, 9371 9372 // @method resetStyle( <Path> layer? ): this 9373 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. 9374 // If `layer` is omitted, the style of all features in the current layer is reset. 9375 resetStyle: function (layer) { 9376 if (layer === undefined) { 9377 return this.eachLayer(this.resetStyle, this); 9378 } 9379 // reset any custom styles 9380 layer.options = extend({}, layer.defaultOptions); 9381 this._setLayerStyle(layer, this.options.style); 9382 return this; 9383 }, 9384 9385 // @method setStyle( <Function> style ): this 9386 // Changes styles of GeoJSON vector layers with the given style function. 9387 setStyle: function (style) { 9388 return this.eachLayer(function (layer) { 9389 this._setLayerStyle(layer, style); 9390 }, this); 9391 }, 9392 9393 _setLayerStyle: function (layer, style) { 9394 if (layer.setStyle) { 9395 if (typeof style === 'function') { 9396 style = style(layer.feature); 9397 } 9398 layer.setStyle(style); 9399 } 9400 } 9401}); 9402 9403// @section 9404// There are several static functions which can be called without instantiating L.GeoJSON: 9405 9406// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer 9407// Creates a `Layer` from a given GeoJSON feature. Can use a custom 9408// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) 9409// functions if provided as options. 9410function geometryToLayer(geojson, options) { 9411 9412 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, 9413 coords = geometry ? geometry.coordinates : null, 9414 layers = [], 9415 pointToLayer = options && options.pointToLayer, 9416 _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, 9417 latlng, latlngs, i, len; 9418 9419 if (!coords && !geometry) { 9420 return null; 9421 } 9422 9423 switch (geometry.type) { 9424 case 'Point': 9425 latlng = _coordsToLatLng(coords); 9426 return _pointToLayer(pointToLayer, geojson, latlng, options); 9427 9428 case 'MultiPoint': 9429 for (i = 0, len = coords.length; i < len; i++) { 9430 latlng = _coordsToLatLng(coords[i]); 9431 layers.push(_pointToLayer(pointToLayer, geojson, latlng, options)); 9432 } 9433 return new FeatureGroup(layers); 9434 9435 case 'LineString': 9436 case 'MultiLineString': 9437 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); 9438 return new Polyline(latlngs, options); 9439 9440 case 'Polygon': 9441 case 'MultiPolygon': 9442 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); 9443 return new Polygon(latlngs, options); 9444 9445 case 'GeometryCollection': 9446 for (i = 0, len = geometry.geometries.length; i < len; i++) { 9447 var layer = geometryToLayer({ 9448 geometry: geometry.geometries[i], 9449 type: 'Feature', 9450 properties: geojson.properties 9451 }, options); 9452 9453 if (layer) { 9454 layers.push(layer); 9455 } 9456 } 9457 return new FeatureGroup(layers); 9458 9459 default: 9460 throw new Error('Invalid GeoJSON object.'); 9461 } 9462} 9463 9464function _pointToLayer(pointToLayerFn, geojson, latlng, options) { 9465 return pointToLayerFn ? 9466 pointToLayerFn(geojson, latlng) : 9467 new Marker(latlng, options && options.markersInheritOptions && options); 9468} 9469 9470// @function coordsToLatLng(coords: Array): LatLng 9471// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) 9472// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. 9473function coordsToLatLng(coords) { 9474 return new LatLng(coords[1], coords[0], coords[2]); 9475} 9476 9477// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array 9478// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. 9479// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). 9480// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. 9481function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { 9482 var latlngs = []; 9483 9484 for (var i = 0, len = coords.length, latlng; i < len; i++) { 9485 latlng = levelsDeep ? 9486 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : 9487 (_coordsToLatLng || coordsToLatLng)(coords[i]); 9488 9489 latlngs.push(latlng); 9490 } 9491 9492 return latlngs; 9493} 9494 9495// @function latLngToCoords(latlng: LatLng, precision?: Number): Array 9496// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) 9497function latLngToCoords(latlng, precision) { 9498 precision = typeof precision === 'number' ? precision : 6; 9499 return latlng.alt !== undefined ? 9500 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : 9501 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; 9502} 9503 9504// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array 9505// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) 9506// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. 9507function latLngsToCoords(latlngs, levelsDeep, closed, precision) { 9508 var coords = []; 9509 9510 for (var i = 0, len = latlngs.length; i < len; i++) { 9511 coords.push(levelsDeep ? 9512 latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : 9513 latLngToCoords(latlngs[i], precision)); 9514 } 9515 9516 if (!levelsDeep && closed) { 9517 coords.push(coords[0]); 9518 } 9519 9520 return coords; 9521} 9522 9523function getFeature(layer, newGeometry) { 9524 return layer.feature ? 9525 extend({}, layer.feature, {geometry: newGeometry}) : 9526 asFeature(newGeometry); 9527} 9528 9529// @function asFeature(geojson: Object): Object 9530// Normalize GeoJSON geometries/features into GeoJSON features. 9531function asFeature(geojson) { 9532 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { 9533 return geojson; 9534 } 9535 9536 return { 9537 type: 'Feature', 9538 properties: {}, 9539 geometry: geojson 9540 }; 9541} 9542 9543var PointToGeoJSON = { 9544 toGeoJSON: function (precision) { 9545 return getFeature(this, { 9546 type: 'Point', 9547 coordinates: latLngToCoords(this.getLatLng(), precision) 9548 }); 9549 } 9550}; 9551 9552// @namespace Marker 9553// @section Other methods 9554// @method toGeoJSON(precision?: Number): Object 9555// `precision` is the number of decimal places for coordinates. 9556// The default value is 6 places. 9557// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). 9558Marker.include(PointToGeoJSON); 9559 9560// @namespace CircleMarker 9561// @method toGeoJSON(precision?: Number): Object 9562// `precision` is the number of decimal places for coordinates. 9563// The default value is 6 places. 9564// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). 9565Circle.include(PointToGeoJSON); 9566CircleMarker.include(PointToGeoJSON); 9567 9568 9569// @namespace Polyline 9570// @method toGeoJSON(precision?: Number): Object 9571// `precision` is the number of decimal places for coordinates. 9572// The default value is 6 places. 9573// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). 9574Polyline.include({ 9575 toGeoJSON: function (precision) { 9576 var multi = !isFlat(this._latlngs); 9577 9578 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); 9579 9580 return getFeature(this, { 9581 type: (multi ? 'Multi' : '') + 'LineString', 9582 coordinates: coords 9583 }); 9584 } 9585}); 9586 9587// @namespace Polygon 9588// @method toGeoJSON(precision?: Number): Object 9589// `precision` is the number of decimal places for coordinates. 9590// The default value is 6 places. 9591// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). 9592Polygon.include({ 9593 toGeoJSON: function (precision) { 9594 var holes = !isFlat(this._latlngs), 9595 multi = holes && !isFlat(this._latlngs[0]); 9596 9597 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); 9598 9599 if (!holes) { 9600 coords = [coords]; 9601 } 9602 9603 return getFeature(this, { 9604 type: (multi ? 'Multi' : '') + 'Polygon', 9605 coordinates: coords 9606 }); 9607 } 9608}); 9609 9610 9611// @namespace LayerGroup 9612LayerGroup.include({ 9613 toMultiPoint: function (precision) { 9614 var coords = []; 9615 9616 this.eachLayer(function (layer) { 9617 coords.push(layer.toGeoJSON(precision).geometry.coordinates); 9618 }); 9619 9620 return getFeature(this, { 9621 type: 'MultiPoint', 9622 coordinates: coords 9623 }); 9624 }, 9625 9626 // @method toGeoJSON(precision?: Number): Object 9627 // `precision` is the number of decimal places for coordinates. 9628 // The default value is 6 places. 9629 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). 9630 toGeoJSON: function (precision) { 9631 9632 var type = this.feature && this.feature.geometry && this.feature.geometry.type; 9633 9634 if (type === 'MultiPoint') { 9635 return this.toMultiPoint(precision); 9636 } 9637 9638 var isGeometryCollection = type === 'GeometryCollection', 9639 jsons = []; 9640 9641 this.eachLayer(function (layer) { 9642 if (layer.toGeoJSON) { 9643 var json = layer.toGeoJSON(precision); 9644 if (isGeometryCollection) { 9645 jsons.push(json.geometry); 9646 } else { 9647 var feature = asFeature(json); 9648 // Squash nested feature collections 9649 if (feature.type === 'FeatureCollection') { 9650 jsons.push.apply(jsons, feature.features); 9651 } else { 9652 jsons.push(feature); 9653 } 9654 } 9655 } 9656 }); 9657 9658 if (isGeometryCollection) { 9659 return getFeature(this, { 9660 geometries: jsons, 9661 type: 'GeometryCollection' 9662 }); 9663 } 9664 9665 return { 9666 type: 'FeatureCollection', 9667 features: jsons 9668 }; 9669 } 9670}); 9671 9672// @namespace GeoJSON 9673// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) 9674// Creates a GeoJSON layer. Optionally accepts an object in 9675// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map 9676// (you can alternatively add it later with `addData` method) and an `options` object. 9677function geoJSON(geojson, options) { 9678 return new GeoJSON(geojson, options); 9679} 9680 9681// Backward compatibility. 9682var geoJson = geoJSON; 9683 9684/* 9685 * @class ImageOverlay 9686 * @aka L.ImageOverlay 9687 * @inherits Interactive layer 9688 * 9689 * Used to load and display a single image over specific bounds of the map. Extends `Layer`. 9690 * 9691 * @example 9692 * 9693 * ```js 9694 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', 9695 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; 9696 * L.imageOverlay(imageUrl, imageBounds).addTo(map); 9697 * ``` 9698 */ 9699 9700var ImageOverlay = Layer.extend({ 9701 9702 // @section 9703 // @aka ImageOverlay options 9704 options: { 9705 // @option opacity: Number = 1.0 9706 // The opacity of the image overlay. 9707 opacity: 1, 9708 9709 // @option alt: String = '' 9710 // Text for the `alt` attribute of the image (useful for accessibility). 9711 alt: '', 9712 9713 // @option interactive: Boolean = false 9714 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. 9715 interactive: false, 9716 9717 // @option crossOrigin: Boolean|String = false 9718 // Whether the crossOrigin attribute will be added to the image. 9719 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data. 9720 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 9721 crossOrigin: false, 9722 9723 // @option errorOverlayUrl: String = '' 9724 // URL to the overlay image to show in place of the overlay that failed to load. 9725 errorOverlayUrl: '', 9726 9727 // @option zIndex: Number = 1 9728 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer. 9729 zIndex: 1, 9730 9731 // @option className: String = '' 9732 // A custom class name to assign to the image. Empty by default. 9733 className: '' 9734 }, 9735 9736 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) 9737 this._url = url; 9738 this._bounds = toLatLngBounds(bounds); 9739 9740 setOptions(this, options); 9741 }, 9742 9743 onAdd: function () { 9744 if (!this._image) { 9745 this._initImage(); 9746 9747 if (this.options.opacity < 1) { 9748 this._updateOpacity(); 9749 } 9750 } 9751 9752 if (this.options.interactive) { 9753 addClass(this._image, 'leaflet-interactive'); 9754 this.addInteractiveTarget(this._image); 9755 } 9756 9757 this.getPane().appendChild(this._image); 9758 this._reset(); 9759 }, 9760 9761 onRemove: function () { 9762 remove(this._image); 9763 if (this.options.interactive) { 9764 this.removeInteractiveTarget(this._image); 9765 } 9766 }, 9767 9768 // @method setOpacity(opacity: Number): this 9769 // Sets the opacity of the overlay. 9770 setOpacity: function (opacity) { 9771 this.options.opacity = opacity; 9772 9773 if (this._image) { 9774 this._updateOpacity(); 9775 } 9776 return this; 9777 }, 9778 9779 setStyle: function (styleOpts) { 9780 if (styleOpts.opacity) { 9781 this.setOpacity(styleOpts.opacity); 9782 } 9783 return this; 9784 }, 9785 9786 // @method bringToFront(): this 9787 // Brings the layer to the top of all overlays. 9788 bringToFront: function () { 9789 if (this._map) { 9790 toFront(this._image); 9791 } 9792 return this; 9793 }, 9794 9795 // @method bringToBack(): this 9796 // Brings the layer to the bottom of all overlays. 9797 bringToBack: function () { 9798 if (this._map) { 9799 toBack(this._image); 9800 } 9801 return this; 9802 }, 9803 9804 // @method setUrl(url: String): this 9805 // Changes the URL of the image. 9806 setUrl: function (url) { 9807 this._url = url; 9808 9809 if (this._image) { 9810 this._image.src = url; 9811 } 9812 return this; 9813 }, 9814 9815 // @method setBounds(bounds: LatLngBounds): this 9816 // Update the bounds that this ImageOverlay covers 9817 setBounds: function (bounds) { 9818 this._bounds = toLatLngBounds(bounds); 9819 9820 if (this._map) { 9821 this._reset(); 9822 } 9823 return this; 9824 }, 9825 9826 getEvents: function () { 9827 var events = { 9828 zoom: this._reset, 9829 viewreset: this._reset 9830 }; 9831 9832 if (this._zoomAnimated) { 9833 events.zoomanim = this._animateZoom; 9834 } 9835 9836 return events; 9837 }, 9838 9839 // @method setZIndex(value: Number): this 9840 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. 9841 setZIndex: function (value) { 9842 this.options.zIndex = value; 9843 this._updateZIndex(); 9844 return this; 9845 }, 9846 9847 // @method getBounds(): LatLngBounds 9848 // Get the bounds that this ImageOverlay covers 9849 getBounds: function () { 9850 return this._bounds; 9851 }, 9852 9853 // @method getElement(): HTMLElement 9854 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) 9855 // used by this overlay. 9856 getElement: function () { 9857 return this._image; 9858 }, 9859 9860 _initImage: function () { 9861 var wasElementSupplied = this._url.tagName === 'IMG'; 9862 var img = this._image = wasElementSupplied ? this._url : create$1('img'); 9863 9864 addClass(img, 'leaflet-image-layer'); 9865 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } 9866 if (this.options.className) { addClass(img, this.options.className); } 9867 9868 img.onselectstart = falseFn; 9869 img.onmousemove = falseFn; 9870 9871 // @event load: Event 9872 // Fired when the ImageOverlay layer has loaded its image 9873 img.onload = bind(this.fire, this, 'load'); 9874 img.onerror = bind(this._overlayOnError, this, 'error'); 9875 9876 if (this.options.crossOrigin || this.options.crossOrigin === '') { 9877 img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 9878 } 9879 9880 if (this.options.zIndex) { 9881 this._updateZIndex(); 9882 } 9883 9884 if (wasElementSupplied) { 9885 this._url = img.src; 9886 return; 9887 } 9888 9889 img.src = this._url; 9890 img.alt = this.options.alt; 9891 }, 9892 9893 _animateZoom: function (e) { 9894 var scale = this._map.getZoomScale(e.zoom), 9895 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; 9896 9897 setTransform(this._image, offset, scale); 9898 }, 9899 9900 _reset: function () { 9901 var image = this._image, 9902 bounds = new Bounds( 9903 this._map.latLngToLayerPoint(this._bounds.getNorthWest()), 9904 this._map.latLngToLayerPoint(this._bounds.getSouthEast())), 9905 size = bounds.getSize(); 9906 9907 setPosition(image, bounds.min); 9908 9909 image.style.width = size.x + 'px'; 9910 image.style.height = size.y + 'px'; 9911 }, 9912 9913 _updateOpacity: function () { 9914 setOpacity(this._image, this.options.opacity); 9915 }, 9916 9917 _updateZIndex: function () { 9918 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { 9919 this._image.style.zIndex = this.options.zIndex; 9920 } 9921 }, 9922 9923 _overlayOnError: function () { 9924 // @event error: Event 9925 // Fired when the ImageOverlay layer fails to load its image 9926 this.fire('error'); 9927 9928 var errorUrl = this.options.errorOverlayUrl; 9929 if (errorUrl && this._url !== errorUrl) { 9930 this._url = errorUrl; 9931 this._image.src = errorUrl; 9932 } 9933 } 9934}); 9935 9936// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) 9937// Instantiates an image overlay object given the URL of the image and the 9938// geographical bounds it is tied to. 9939var imageOverlay = function (url, bounds, options) { 9940 return new ImageOverlay(url, bounds, options); 9941}; 9942 9943/* 9944 * @class VideoOverlay 9945 * @aka L.VideoOverlay 9946 * @inherits ImageOverlay 9947 * 9948 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. 9949 * 9950 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video) 9951 * HTML5 element. 9952 * 9953 * @example 9954 * 9955 * ```js 9956 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm', 9957 * videoBounds = [[ 32, -130], [ 13, -100]]; 9958 * L.videoOverlay(videoUrl, videoBounds ).addTo(map); 9959 * ``` 9960 */ 9961 9962var VideoOverlay = ImageOverlay.extend({ 9963 9964 // @section 9965 // @aka VideoOverlay options 9966 options: { 9967 // @option autoplay: Boolean = true 9968 // Whether the video starts playing automatically when loaded. 9969 autoplay: true, 9970 9971 // @option loop: Boolean = true 9972 // Whether the video will loop back to the beginning when played. 9973 loop: true, 9974 9975 // @option keepAspectRatio: Boolean = true 9976 // Whether the video will save aspect ratio after the projection. 9977 // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit 9978 keepAspectRatio: true 9979 }, 9980 9981 _initImage: function () { 9982 var wasElementSupplied = this._url.tagName === 'VIDEO'; 9983 var vid = this._image = wasElementSupplied ? this._url : create$1('video'); 9984 9985 addClass(vid, 'leaflet-image-layer'); 9986 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); } 9987 if (this.options.className) { addClass(vid, this.options.className); } 9988 9989 vid.onselectstart = falseFn; 9990 vid.onmousemove = falseFn; 9991 9992 // @event load: Event 9993 // Fired when the video has finished loading the first frame 9994 vid.onloadeddata = bind(this.fire, this, 'load'); 9995 9996 if (wasElementSupplied) { 9997 var sourceElements = vid.getElementsByTagName('source'); 9998 var sources = []; 9999 for (var j = 0; j < sourceElements.length; j++) { 10000 sources.push(sourceElements[j].src); 10001 } 10002 10003 this._url = (sourceElements.length > 0) ? sources : [vid.src]; 10004 return; 10005 } 10006 10007 if (!isArray(this._url)) { this._url = [this._url]; } 10008 10009 if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; } 10010 vid.autoplay = !!this.options.autoplay; 10011 vid.loop = !!this.options.loop; 10012 for (var i = 0; i < this._url.length; i++) { 10013 var source = create$1('source'); 10014 source.src = this._url[i]; 10015 vid.appendChild(source); 10016 } 10017 } 10018 10019 // @method getElement(): HTMLVideoElement 10020 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement) 10021 // used by this overlay. 10022}); 10023 10024 10025// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options) 10026// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the 10027// geographical bounds it is tied to. 10028 10029function videoOverlay(video, bounds, options) { 10030 return new VideoOverlay(video, bounds, options); 10031} 10032 10033/* 10034 * @class SVGOverlay 10035 * @aka L.SVGOverlay 10036 * @inherits ImageOverlay 10037 * 10038 * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`. 10039 * 10040 * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element. 10041 * 10042 * @example 10043 * 10044 * ```js 10045 * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 10046 * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg"); 10047 * svgElement.setAttribute('viewBox', "0 0 200 200"); 10048 * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>'; 10049 * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ]; 10050 * L.svgOverlay(svgElement, svgElementBounds).addTo(map); 10051 * ``` 10052 */ 10053 10054var SVGOverlay = ImageOverlay.extend({ 10055 _initImage: function () { 10056 var el = this._image = this._url; 10057 10058 addClass(el, 'leaflet-image-layer'); 10059 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); } 10060 if (this.options.className) { addClass(el, this.options.className); } 10061 10062 el.onselectstart = falseFn; 10063 el.onmousemove = falseFn; 10064 } 10065 10066 // @method getElement(): SVGElement 10067 // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement) 10068 // used by this overlay. 10069}); 10070 10071 10072// @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options) 10073// Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to. 10074// A viewBox attribute is required on the SVG element to zoom in and out properly. 10075 10076function svgOverlay(el, bounds, options) { 10077 return new SVGOverlay(el, bounds, options); 10078} 10079 10080/* 10081 * @class DivOverlay 10082 * @inherits Layer 10083 * @aka L.DivOverlay 10084 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins. 10085 */ 10086 10087// @namespace DivOverlay 10088var DivOverlay = Layer.extend({ 10089 10090 // @section 10091 // @aka DivOverlay options 10092 options: { 10093 // @option offset: Point = Point(0, 7) 10094 // The offset of the popup position. Useful to control the anchor 10095 // of the popup when opening it on some overlays. 10096 offset: [0, 7], 10097 10098 // @option className: String = '' 10099 // A custom CSS class name to assign to the popup. 10100 className: '', 10101 10102 // @option pane: String = 'popupPane' 10103 // `Map pane` where the popup will be added. 10104 pane: 'popupPane' 10105 }, 10106 10107 initialize: function (options, source) { 10108 setOptions(this, options); 10109 10110 this._source = source; 10111 }, 10112 10113 onAdd: function (map) { 10114 this._zoomAnimated = map._zoomAnimated; 10115 10116 if (!this._container) { 10117 this._initLayout(); 10118 } 10119 10120 if (map._fadeAnimated) { 10121 setOpacity(this._container, 0); 10122 } 10123 10124 clearTimeout(this._removeTimeout); 10125 this.getPane().appendChild(this._container); 10126 this.update(); 10127 10128 if (map._fadeAnimated) { 10129 setOpacity(this._container, 1); 10130 } 10131 10132 this.bringToFront(); 10133 }, 10134 10135 onRemove: function (map) { 10136 if (map._fadeAnimated) { 10137 setOpacity(this._container, 0); 10138 this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200); 10139 } else { 10140 remove(this._container); 10141 } 10142 }, 10143 10144 // @namespace Popup 10145 // @method getLatLng: LatLng 10146 // Returns the geographical point of popup. 10147 getLatLng: function () { 10148 return this._latlng; 10149 }, 10150 10151 // @method setLatLng(latlng: LatLng): this 10152 // Sets the geographical point where the popup will open. 10153 setLatLng: function (latlng) { 10154 this._latlng = toLatLng(latlng); 10155 if (this._map) { 10156 this._updatePosition(); 10157 this._adjustPan(); 10158 } 10159 return this; 10160 }, 10161 10162 // @method getContent: String|HTMLElement 10163 // Returns the content of the popup. 10164 getContent: function () { 10165 return this._content; 10166 }, 10167 10168 // @method setContent(htmlContent: String|HTMLElement|Function): this 10169 // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup. 10170 setContent: function (content) { 10171 this._content = content; 10172 this.update(); 10173 return this; 10174 }, 10175 10176 // @method getElement: String|HTMLElement 10177 // Alias for [getContent()](#popup-getcontent) 10178 getElement: function () { 10179 return this._container; 10180 }, 10181 10182 // @method update: null 10183 // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded. 10184 update: function () { 10185 if (!this._map) { return; } 10186 10187 this._container.style.visibility = 'hidden'; 10188 10189 this._updateContent(); 10190 this._updateLayout(); 10191 this._updatePosition(); 10192 10193 this._container.style.visibility = ''; 10194 10195 this._adjustPan(); 10196 }, 10197 10198 getEvents: function () { 10199 var events = { 10200 zoom: this._updatePosition, 10201 viewreset: this._updatePosition 10202 }; 10203 10204 if (this._zoomAnimated) { 10205 events.zoomanim = this._animateZoom; 10206 } 10207 return events; 10208 }, 10209 10210 // @method isOpen: Boolean 10211 // Returns `true` when the popup is visible on the map. 10212 isOpen: function () { 10213 return !!this._map && this._map.hasLayer(this); 10214 }, 10215 10216 // @method bringToFront: this 10217 // Brings this popup in front of other popups (in the same map pane). 10218 bringToFront: function () { 10219 if (this._map) { 10220 toFront(this._container); 10221 } 10222 return this; 10223 }, 10224 10225 // @method bringToBack: this 10226 // Brings this popup to the back of other popups (in the same map pane). 10227 bringToBack: function () { 10228 if (this._map) { 10229 toBack(this._container); 10230 } 10231 return this; 10232 }, 10233 10234 _prepareOpen: function (parent, layer, latlng) { 10235 if (!(layer instanceof Layer)) { 10236 latlng = layer; 10237 layer = parent; 10238 } 10239 10240 if (layer instanceof FeatureGroup) { 10241 for (var id in parent._layers) { 10242 layer = parent._layers[id]; 10243 break; 10244 } 10245 } 10246 10247 if (!latlng) { 10248 if (layer.getCenter) { 10249 latlng = layer.getCenter(); 10250 } else if (layer.getLatLng) { 10251 latlng = layer.getLatLng(); 10252 } else { 10253 throw new Error('Unable to get source layer LatLng.'); 10254 } 10255 } 10256 10257 // set overlay source to this layer 10258 this._source = layer; 10259 10260 // update the overlay (content, layout, ect...) 10261 this.update(); 10262 10263 return latlng; 10264 }, 10265 10266 _updateContent: function () { 10267 if (!this._content) { return; } 10268 10269 var node = this._contentNode; 10270 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content; 10271 10272 if (typeof content === 'string') { 10273 node.innerHTML = content; 10274 } else { 10275 while (node.hasChildNodes()) { 10276 node.removeChild(node.firstChild); 10277 } 10278 node.appendChild(content); 10279 } 10280 this.fire('contentupdate'); 10281 }, 10282 10283 _updatePosition: function () { 10284 if (!this._map) { return; } 10285 10286 var pos = this._map.latLngToLayerPoint(this._latlng), 10287 offset = toPoint(this.options.offset), 10288 anchor = this._getAnchor(); 10289 10290 if (this._zoomAnimated) { 10291 setPosition(this._container, pos.add(anchor)); 10292 } else { 10293 offset = offset.add(pos).add(anchor); 10294 } 10295 10296 var bottom = this._containerBottom = -offset.y, 10297 left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; 10298 10299 // bottom position the popup in case the height of the popup changes (images loading etc) 10300 this._container.style.bottom = bottom + 'px'; 10301 this._container.style.left = left + 'px'; 10302 }, 10303 10304 _getAnchor: function () { 10305 return [0, 0]; 10306 } 10307 10308}); 10309 10310/* 10311 * @class Popup 10312 * @inherits DivOverlay 10313 * @aka L.Popup 10314 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to 10315 * open popups while making sure that only one popup is open at one time 10316 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want. 10317 * 10318 * @example 10319 * 10320 * If you want to just bind a popup to marker click and then open it, it's really easy: 10321 * 10322 * ```js 10323 * marker.bindPopup(popupContent).openPopup(); 10324 * ``` 10325 * Path overlays like polylines also have a `bindPopup` method. 10326 * Here's a more complicated way to open a popup on a map: 10327 * 10328 * ```js 10329 * var popup = L.popup() 10330 * .setLatLng(latlng) 10331 * .setContent('<p>Hello world!<br />This is a nice popup.</p>') 10332 * .openOn(map); 10333 * ``` 10334 */ 10335 10336 10337// @namespace Popup 10338var Popup = DivOverlay.extend({ 10339 10340 // @section 10341 // @aka Popup options 10342 options: { 10343 // @option maxWidth: Number = 300 10344 // Max width of the popup, in pixels. 10345 maxWidth: 300, 10346 10347 // @option minWidth: Number = 50 10348 // Min width of the popup, in pixels. 10349 minWidth: 50, 10350 10351 // @option maxHeight: Number = null 10352 // If set, creates a scrollable container of the given height 10353 // inside a popup if its content exceeds it. 10354 maxHeight: null, 10355 10356 // @option autoPan: Boolean = true 10357 // Set it to `false` if you don't want the map to do panning animation 10358 // to fit the opened popup. 10359 autoPan: true, 10360 10361 // @option autoPanPaddingTopLeft: Point = null 10362 // The margin between the popup and the top left corner of the map 10363 // view after autopanning was performed. 10364 autoPanPaddingTopLeft: null, 10365 10366 // @option autoPanPaddingBottomRight: Point = null 10367 // The margin between the popup and the bottom right corner of the map 10368 // view after autopanning was performed. 10369 autoPanPaddingBottomRight: null, 10370 10371 // @option autoPanPadding: Point = Point(5, 5) 10372 // Equivalent of setting both top left and bottom right autopan padding to the same value. 10373 autoPanPadding: [5, 5], 10374 10375 // @option keepInView: Boolean = false 10376 // Set it to `true` if you want to prevent users from panning the popup 10377 // off of the screen while it is open. 10378 keepInView: false, 10379 10380 // @option closeButton: Boolean = true 10381 // Controls the presence of a close button in the popup. 10382 closeButton: true, 10383 10384 // @option autoClose: Boolean = true 10385 // Set it to `false` if you want to override the default behavior of 10386 // the popup closing when another popup is opened. 10387 autoClose: true, 10388 10389 // @option closeOnEscapeKey: Boolean = true 10390 // Set it to `false` if you want to override the default behavior of 10391 // the ESC key for closing of the popup. 10392 closeOnEscapeKey: true, 10393 10394 // @option closeOnClick: Boolean = * 10395 // Set it if you want to override the default behavior of the popup closing when user clicks 10396 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option. 10397 10398 // @option className: String = '' 10399 // A custom CSS class name to assign to the popup. 10400 className: '' 10401 }, 10402 10403 // @namespace Popup 10404 // @method openOn(map: Map): this 10405 // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`. 10406 openOn: function (map) { 10407 map.openPopup(this); 10408 return this; 10409 }, 10410 10411 onAdd: function (map) { 10412 DivOverlay.prototype.onAdd.call(this, map); 10413 10414 // @namespace Map 10415 // @section Popup events 10416 // @event popupopen: PopupEvent 10417 // Fired when a popup is opened in the map 10418 map.fire('popupopen', {popup: this}); 10419 10420 if (this._source) { 10421 // @namespace Layer 10422 // @section Popup events 10423 // @event popupopen: PopupEvent 10424 // Fired when a popup bound to this layer is opened 10425 this._source.fire('popupopen', {popup: this}, true); 10426 // For non-path layers, we toggle the popup when clicking 10427 // again the layer, so prevent the map to reopen it. 10428 if (!(this._source instanceof Path)) { 10429 this._source.on('preclick', stopPropagation); 10430 } 10431 } 10432 }, 10433 10434 onRemove: function (map) { 10435 DivOverlay.prototype.onRemove.call(this, map); 10436 10437 // @namespace Map 10438 // @section Popup events 10439 // @event popupclose: PopupEvent 10440 // Fired when a popup in the map is closed 10441 map.fire('popupclose', {popup: this}); 10442 10443 if (this._source) { 10444 // @namespace Layer 10445 // @section Popup events 10446 // @event popupclose: PopupEvent 10447 // Fired when a popup bound to this layer is closed 10448 this._source.fire('popupclose', {popup: this}, true); 10449 if (!(this._source instanceof Path)) { 10450 this._source.off('preclick', stopPropagation); 10451 } 10452 } 10453 }, 10454 10455 getEvents: function () { 10456 var events = DivOverlay.prototype.getEvents.call(this); 10457 10458 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) { 10459 events.preclick = this._close; 10460 } 10461 10462 if (this.options.keepInView) { 10463 events.moveend = this._adjustPan; 10464 } 10465 10466 return events; 10467 }, 10468 10469 _close: function () { 10470 if (this._map) { 10471 this._map.closePopup(this); 10472 } 10473 }, 10474 10475 _initLayout: function () { 10476 var prefix = 'leaflet-popup', 10477 container = this._container = create$1('div', 10478 prefix + ' ' + (this.options.className || '') + 10479 ' leaflet-zoom-animated'); 10480 10481 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container); 10482 this._contentNode = create$1('div', prefix + '-content', wrapper); 10483 10484 disableClickPropagation(wrapper); 10485 disableScrollPropagation(this._contentNode); 10486 on(wrapper, 'contextmenu', stopPropagation); 10487 10488 this._tipContainer = create$1('div', prefix + '-tip-container', container); 10489 this._tip = create$1('div', prefix + '-tip', this._tipContainer); 10490 10491 if (this.options.closeButton) { 10492 var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container); 10493 closeButton.href = '#close'; 10494 closeButton.innerHTML = '×'; 10495 10496 on(closeButton, 'click', this._onCloseButtonClick, this); 10497 } 10498 }, 10499 10500 _updateLayout: function () { 10501 var container = this._contentNode, 10502 style = container.style; 10503 10504 style.width = ''; 10505 style.whiteSpace = 'nowrap'; 10506 10507 var width = container.offsetWidth; 10508 width = Math.min(width, this.options.maxWidth); 10509 width = Math.max(width, this.options.minWidth); 10510 10511 style.width = (width + 1) + 'px'; 10512 style.whiteSpace = ''; 10513 10514 style.height = ''; 10515 10516 var height = container.offsetHeight, 10517 maxHeight = this.options.maxHeight, 10518 scrolledClass = 'leaflet-popup-scrolled'; 10519 10520 if (maxHeight && height > maxHeight) { 10521 style.height = maxHeight + 'px'; 10522 addClass(container, scrolledClass); 10523 } else { 10524 removeClass(container, scrolledClass); 10525 } 10526 10527 this._containerWidth = this._container.offsetWidth; 10528 }, 10529 10530 _animateZoom: function (e) { 10531 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), 10532 anchor = this._getAnchor(); 10533 setPosition(this._container, pos.add(anchor)); 10534 }, 10535 10536 _adjustPan: function () { 10537 if (!this.options.autoPan) { return; } 10538 if (this._map._panAnim) { this._map._panAnim.stop(); } 10539 10540 var map = this._map, 10541 marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0, 10542 containerHeight = this._container.offsetHeight + marginBottom, 10543 containerWidth = this._containerWidth, 10544 layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom); 10545 10546 layerPos._add(getPosition(this._container)); 10547 10548 var containerPos = map.layerPointToContainerPoint(layerPos), 10549 padding = toPoint(this.options.autoPanPadding), 10550 paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), 10551 paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), 10552 size = map.getSize(), 10553 dx = 0, 10554 dy = 0; 10555 10556 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right 10557 dx = containerPos.x + containerWidth - size.x + paddingBR.x; 10558 } 10559 if (containerPos.x - dx - paddingTL.x < 0) { // left 10560 dx = containerPos.x - paddingTL.x; 10561 } 10562 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom 10563 dy = containerPos.y + containerHeight - size.y + paddingBR.y; 10564 } 10565 if (containerPos.y - dy - paddingTL.y < 0) { // top 10566 dy = containerPos.y - paddingTL.y; 10567 } 10568 10569 // @namespace Map 10570 // @section Popup events 10571 // @event autopanstart: Event 10572 // Fired when the map starts autopanning when opening a popup. 10573 if (dx || dy) { 10574 map 10575 .fire('autopanstart') 10576 .panBy([dx, dy]); 10577 } 10578 }, 10579 10580 _onCloseButtonClick: function (e) { 10581 this._close(); 10582 stop(e); 10583 }, 10584 10585 _getAnchor: function () { 10586 // Where should we anchor the popup on the source layer? 10587 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); 10588 } 10589 10590}); 10591 10592// @namespace Popup 10593// @factory L.popup(options?: Popup options, source?: Layer) 10594// Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers. 10595var popup = function (options, source) { 10596 return new Popup(options, source); 10597}; 10598 10599 10600/* @namespace Map 10601 * @section Interaction Options 10602 * @option closePopupOnClick: Boolean = true 10603 * Set it to `false` if you don't want popups to close when user clicks the map. 10604 */ 10605Map.mergeOptions({ 10606 closePopupOnClick: true 10607}); 10608 10609 10610// @namespace Map 10611// @section Methods for Layers and Controls 10612Map.include({ 10613 // @method openPopup(popup: Popup): this 10614 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). 10615 // @alternative 10616 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this 10617 // Creates a popup with the specified content and options and opens it in the given point on a map. 10618 openPopup: function (popup, latlng, options) { 10619 if (!(popup instanceof Popup)) { 10620 popup = new Popup(options).setContent(popup); 10621 } 10622 10623 if (latlng) { 10624 popup.setLatLng(latlng); 10625 } 10626 10627 if (this.hasLayer(popup)) { 10628 return this; 10629 } 10630 10631 if (this._popup && this._popup.options.autoClose) { 10632 this.closePopup(); 10633 } 10634 10635 this._popup = popup; 10636 return this.addLayer(popup); 10637 }, 10638 10639 // @method closePopup(popup?: Popup): this 10640 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). 10641 closePopup: function (popup) { 10642 if (!popup || popup === this._popup) { 10643 popup = this._popup; 10644 this._popup = null; 10645 } 10646 if (popup) { 10647 this.removeLayer(popup); 10648 } 10649 return this; 10650 } 10651}); 10652 10653/* 10654 * @namespace Layer 10655 * @section Popup methods example 10656 * 10657 * All layers share a set of methods convenient for binding popups to it. 10658 * 10659 * ```js 10660 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map); 10661 * layer.openPopup(); 10662 * layer.closePopup(); 10663 * ``` 10664 * 10665 * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened. 10666 */ 10667 10668// @section Popup methods 10669Layer.include({ 10670 10671 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this 10672 // Binds a popup to the layer with the passed `content` and sets up the 10673 // necessary event listeners. If a `Function` is passed it will receive 10674 // the layer as the first argument and should return a `String` or `HTMLElement`. 10675 bindPopup: function (content, options) { 10676 10677 if (content instanceof Popup) { 10678 setOptions(content, options); 10679 this._popup = content; 10680 content._source = this; 10681 } else { 10682 if (!this._popup || options) { 10683 this._popup = new Popup(options, this); 10684 } 10685 this._popup.setContent(content); 10686 } 10687 10688 if (!this._popupHandlersAdded) { 10689 this.on({ 10690 click: this._openPopup, 10691 keypress: this._onKeyPress, 10692 remove: this.closePopup, 10693 move: this._movePopup 10694 }); 10695 this._popupHandlersAdded = true; 10696 } 10697 10698 return this; 10699 }, 10700 10701 // @method unbindPopup(): this 10702 // Removes the popup previously bound with `bindPopup`. 10703 unbindPopup: function () { 10704 if (this._popup) { 10705 this.off({ 10706 click: this._openPopup, 10707 keypress: this._onKeyPress, 10708 remove: this.closePopup, 10709 move: this._movePopup 10710 }); 10711 this._popupHandlersAdded = false; 10712 this._popup = null; 10713 } 10714 return this; 10715 }, 10716 10717 // @method openPopup(latlng?: LatLng): this 10718 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed. 10719 openPopup: function (layer, latlng) { 10720 if (this._popup && this._map) { 10721 latlng = this._popup._prepareOpen(this, layer, latlng); 10722 10723 // open the popup on the map 10724 this._map.openPopup(this._popup, latlng); 10725 } 10726 10727 return this; 10728 }, 10729 10730 // @method closePopup(): this 10731 // Closes the popup bound to this layer if it is open. 10732 closePopup: function () { 10733 if (this._popup) { 10734 this._popup._close(); 10735 } 10736 return this; 10737 }, 10738 10739 // @method togglePopup(): this 10740 // Opens or closes the popup bound to this layer depending on its current state. 10741 togglePopup: function (target) { 10742 if (this._popup) { 10743 if (this._popup._map) { 10744 this.closePopup(); 10745 } else { 10746 this.openPopup(target); 10747 } 10748 } 10749 return this; 10750 }, 10751 10752 // @method isPopupOpen(): boolean 10753 // Returns `true` if the popup bound to this layer is currently open. 10754 isPopupOpen: function () { 10755 return (this._popup ? this._popup.isOpen() : false); 10756 }, 10757 10758 // @method setPopupContent(content: String|HTMLElement|Popup): this 10759 // Sets the content of the popup bound to this layer. 10760 setPopupContent: function (content) { 10761 if (this._popup) { 10762 this._popup.setContent(content); 10763 } 10764 return this; 10765 }, 10766 10767 // @method getPopup(): Popup 10768 // Returns the popup bound to this layer. 10769 getPopup: function () { 10770 return this._popup; 10771 }, 10772 10773 _openPopup: function (e) { 10774 var layer = e.layer || e.target; 10775 10776 if (!this._popup) { 10777 return; 10778 } 10779 10780 if (!this._map) { 10781 return; 10782 } 10783 10784 // prevent map click 10785 stop(e); 10786 10787 // if this inherits from Path its a vector and we can just 10788 // open the popup at the new location 10789 if (layer instanceof Path) { 10790 this.openPopup(e.layer || e.target, e.latlng); 10791 return; 10792 } 10793 10794 // otherwise treat it like a marker and figure out 10795 // if we should toggle it open/closed 10796 if (this._map.hasLayer(this._popup) && this._popup._source === layer) { 10797 this.closePopup(); 10798 } else { 10799 this.openPopup(layer, e.latlng); 10800 } 10801 }, 10802 10803 _movePopup: function (e) { 10804 this._popup.setLatLng(e.latlng); 10805 }, 10806 10807 _onKeyPress: function (e) { 10808 if (e.originalEvent.keyCode === 13) { 10809 this._openPopup(e); 10810 } 10811 } 10812}); 10813 10814/* 10815 * @class Tooltip 10816 * @inherits DivOverlay 10817 * @aka L.Tooltip 10818 * Used to display small texts on top of map layers. 10819 * 10820 * @example 10821 * 10822 * ```js 10823 * marker.bindTooltip("my tooltip text").openTooltip(); 10824 * ``` 10825 * Note about tooltip offset. Leaflet takes two options in consideration 10826 * for computing tooltip offsetting: 10827 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip. 10828 * Add a positive x offset to move the tooltip to the right, and a positive y offset to 10829 * move it to the bottom. Negatives will move to the left and top. 10830 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You 10831 * should adapt this value if you use a custom icon. 10832 */ 10833 10834 10835// @namespace Tooltip 10836var Tooltip = DivOverlay.extend({ 10837 10838 // @section 10839 // @aka Tooltip options 10840 options: { 10841 // @option pane: String = 'tooltipPane' 10842 // `Map pane` where the tooltip will be added. 10843 pane: 'tooltipPane', 10844 10845 // @option offset: Point = Point(0, 0) 10846 // Optional offset of the tooltip position. 10847 offset: [0, 0], 10848 10849 // @option direction: String = 'auto' 10850 // Direction where to open the tooltip. Possible values are: `right`, `left`, 10851 // `top`, `bottom`, `center`, `auto`. 10852 // `auto` will dynamically switch between `right` and `left` according to the tooltip 10853 // position on the map. 10854 direction: 'auto', 10855 10856 // @option permanent: Boolean = false 10857 // Whether to open the tooltip permanently or only on mouseover. 10858 permanent: false, 10859 10860 // @option sticky: Boolean = false 10861 // If true, the tooltip will follow the mouse instead of being fixed at the feature center. 10862 sticky: false, 10863 10864 // @option interactive: Boolean = false 10865 // If true, the tooltip will listen to the feature events. 10866 interactive: false, 10867 10868 // @option opacity: Number = 0.9 10869 // Tooltip container opacity. 10870 opacity: 0.9 10871 }, 10872 10873 onAdd: function (map) { 10874 DivOverlay.prototype.onAdd.call(this, map); 10875 this.setOpacity(this.options.opacity); 10876 10877 // @namespace Map 10878 // @section Tooltip events 10879 // @event tooltipopen: TooltipEvent 10880 // Fired when a tooltip is opened in the map. 10881 map.fire('tooltipopen', {tooltip: this}); 10882 10883 if (this._source) { 10884 // @namespace Layer 10885 // @section Tooltip events 10886 // @event tooltipopen: TooltipEvent 10887 // Fired when a tooltip bound to this layer is opened. 10888 this._source.fire('tooltipopen', {tooltip: this}, true); 10889 } 10890 }, 10891 10892 onRemove: function (map) { 10893 DivOverlay.prototype.onRemove.call(this, map); 10894 10895 // @namespace Map 10896 // @section Tooltip events 10897 // @event tooltipclose: TooltipEvent 10898 // Fired when a tooltip in the map is closed. 10899 map.fire('tooltipclose', {tooltip: this}); 10900 10901 if (this._source) { 10902 // @namespace Layer 10903 // @section Tooltip events 10904 // @event tooltipclose: TooltipEvent 10905 // Fired when a tooltip bound to this layer is closed. 10906 this._source.fire('tooltipclose', {tooltip: this}, true); 10907 } 10908 }, 10909 10910 getEvents: function () { 10911 var events = DivOverlay.prototype.getEvents.call(this); 10912 10913 if (touch && !this.options.permanent) { 10914 events.preclick = this._close; 10915 } 10916 10917 return events; 10918 }, 10919 10920 _close: function () { 10921 if (this._map) { 10922 this._map.closeTooltip(this); 10923 } 10924 }, 10925 10926 _initLayout: function () { 10927 var prefix = 'leaflet-tooltip', 10928 className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 10929 10930 this._contentNode = this._container = create$1('div', className); 10931 }, 10932 10933 _updateLayout: function () {}, 10934 10935 _adjustPan: function () {}, 10936 10937 _setPosition: function (pos) { 10938 var map = this._map, 10939 container = this._container, 10940 centerPoint = map.latLngToContainerPoint(map.getCenter()), 10941 tooltipPoint = map.layerPointToContainerPoint(pos), 10942 direction = this.options.direction, 10943 tooltipWidth = container.offsetWidth, 10944 tooltipHeight = container.offsetHeight, 10945 offset = toPoint(this.options.offset), 10946 anchor = this._getAnchor(); 10947 10948 if (direction === 'top') { 10949 pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true)); 10950 } else if (direction === 'bottom') { 10951 pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true)); 10952 } else if (direction === 'center') { 10953 pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true)); 10954 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) { 10955 direction = 'right'; 10956 pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true)); 10957 } else { 10958 direction = 'left'; 10959 pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true)); 10960 } 10961 10962 removeClass(container, 'leaflet-tooltip-right'); 10963 removeClass(container, 'leaflet-tooltip-left'); 10964 removeClass(container, 'leaflet-tooltip-top'); 10965 removeClass(container, 'leaflet-tooltip-bottom'); 10966 addClass(container, 'leaflet-tooltip-' + direction); 10967 setPosition(container, pos); 10968 }, 10969 10970 _updatePosition: function () { 10971 var pos = this._map.latLngToLayerPoint(this._latlng); 10972 this._setPosition(pos); 10973 }, 10974 10975 setOpacity: function (opacity) { 10976 this.options.opacity = opacity; 10977 10978 if (this._container) { 10979 setOpacity(this._container, opacity); 10980 } 10981 }, 10982 10983 _animateZoom: function (e) { 10984 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); 10985 this._setPosition(pos); 10986 }, 10987 10988 _getAnchor: function () { 10989 // Where should we anchor the tooltip on the source layer? 10990 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); 10991 } 10992 10993}); 10994 10995// @namespace Tooltip 10996// @factory L.tooltip(options?: Tooltip options, source?: Layer) 10997// Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers. 10998var tooltip = function (options, source) { 10999 return new Tooltip(options, source); 11000}; 11001 11002// @namespace Map 11003// @section Methods for Layers and Controls 11004Map.include({ 11005 11006 // @method openTooltip(tooltip: Tooltip): this 11007 // Opens the specified tooltip. 11008 // @alternative 11009 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this 11010 // Creates a tooltip with the specified content and options and open it. 11011 openTooltip: function (tooltip, latlng, options) { 11012 if (!(tooltip instanceof Tooltip)) { 11013 tooltip = new Tooltip(options).setContent(tooltip); 11014 } 11015 11016 if (latlng) { 11017 tooltip.setLatLng(latlng); 11018 } 11019 11020 if (this.hasLayer(tooltip)) { 11021 return this; 11022 } 11023 11024 return this.addLayer(tooltip); 11025 }, 11026 11027 // @method closeTooltip(tooltip?: Tooltip): this 11028 // Closes the tooltip given as parameter. 11029 closeTooltip: function (tooltip) { 11030 if (tooltip) { 11031 this.removeLayer(tooltip); 11032 } 11033 return this; 11034 } 11035 11036}); 11037 11038/* 11039 * @namespace Layer 11040 * @section Tooltip methods example 11041 * 11042 * All layers share a set of methods convenient for binding tooltips to it. 11043 * 11044 * ```js 11045 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map); 11046 * layer.openTooltip(); 11047 * layer.closeTooltip(); 11048 * ``` 11049 */ 11050 11051// @section Tooltip methods 11052Layer.include({ 11053 11054 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this 11055 // Binds a tooltip to the layer with the passed `content` and sets up the 11056 // necessary event listeners. If a `Function` is passed it will receive 11057 // the layer as the first argument and should return a `String` or `HTMLElement`. 11058 bindTooltip: function (content, options) { 11059 11060 if (content instanceof Tooltip) { 11061 setOptions(content, options); 11062 this._tooltip = content; 11063 content._source = this; 11064 } else { 11065 if (!this._tooltip || options) { 11066 this._tooltip = new Tooltip(options, this); 11067 } 11068 this._tooltip.setContent(content); 11069 11070 } 11071 11072 this._initTooltipInteractions(); 11073 11074 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { 11075 this.openTooltip(); 11076 } 11077 11078 return this; 11079 }, 11080 11081 // @method unbindTooltip(): this 11082 // Removes the tooltip previously bound with `bindTooltip`. 11083 unbindTooltip: function () { 11084 if (this._tooltip) { 11085 this._initTooltipInteractions(true); 11086 this.closeTooltip(); 11087 this._tooltip = null; 11088 } 11089 return this; 11090 }, 11091 11092 _initTooltipInteractions: function (remove$$1) { 11093 if (!remove$$1 && this._tooltipHandlersAdded) { return; } 11094 var onOff = remove$$1 ? 'off' : 'on', 11095 events = { 11096 remove: this.closeTooltip, 11097 move: this._moveTooltip 11098 }; 11099 if (!this._tooltip.options.permanent) { 11100 events.mouseover = this._openTooltip; 11101 events.mouseout = this.closeTooltip; 11102 if (this._tooltip.options.sticky) { 11103 events.mousemove = this._moveTooltip; 11104 } 11105 if (touch) { 11106 events.click = this._openTooltip; 11107 } 11108 } else { 11109 events.add = this._openTooltip; 11110 } 11111 this[onOff](events); 11112 this._tooltipHandlersAdded = !remove$$1; 11113 }, 11114 11115 // @method openTooltip(latlng?: LatLng): this 11116 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed. 11117 openTooltip: function (layer, latlng) { 11118 if (this._tooltip && this._map) { 11119 latlng = this._tooltip._prepareOpen(this, layer, latlng); 11120 11121 // open the tooltip on the map 11122 this._map.openTooltip(this._tooltip, latlng); 11123 11124 // Tooltip container may not be defined if not permanent and never 11125 // opened. 11126 if (this._tooltip.options.interactive && this._tooltip._container) { 11127 addClass(this._tooltip._container, 'leaflet-clickable'); 11128 this.addInteractiveTarget(this._tooltip._container); 11129 } 11130 } 11131 11132 return this; 11133 }, 11134 11135 // @method closeTooltip(): this 11136 // Closes the tooltip bound to this layer if it is open. 11137 closeTooltip: function () { 11138 if (this._tooltip) { 11139 this._tooltip._close(); 11140 if (this._tooltip.options.interactive && this._tooltip._container) { 11141 removeClass(this._tooltip._container, 'leaflet-clickable'); 11142 this.removeInteractiveTarget(this._tooltip._container); 11143 } 11144 } 11145 return this; 11146 }, 11147 11148 // @method toggleTooltip(): this 11149 // Opens or closes the tooltip bound to this layer depending on its current state. 11150 toggleTooltip: function (target) { 11151 if (this._tooltip) { 11152 if (this._tooltip._map) { 11153 this.closeTooltip(); 11154 } else { 11155 this.openTooltip(target); 11156 } 11157 } 11158 return this; 11159 }, 11160 11161 // @method isTooltipOpen(): boolean 11162 // Returns `true` if the tooltip bound to this layer is currently open. 11163 isTooltipOpen: function () { 11164 return this._tooltip.isOpen(); 11165 }, 11166 11167 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this 11168 // Sets the content of the tooltip bound to this layer. 11169 setTooltipContent: function (content) { 11170 if (this._tooltip) { 11171 this._tooltip.setContent(content); 11172 } 11173 return this; 11174 }, 11175 11176 // @method getTooltip(): Tooltip 11177 // Returns the tooltip bound to this layer. 11178 getTooltip: function () { 11179 return this._tooltip; 11180 }, 11181 11182 _openTooltip: function (e) { 11183 var layer = e.layer || e.target; 11184 11185 if (!this._tooltip || !this._map) { 11186 return; 11187 } 11188 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined); 11189 }, 11190 11191 _moveTooltip: function (e) { 11192 var latlng = e.latlng, containerPoint, layerPoint; 11193 if (this._tooltip.options.sticky && e.originalEvent) { 11194 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); 11195 layerPoint = this._map.containerPointToLayerPoint(containerPoint); 11196 latlng = this._map.layerPointToLatLng(layerPoint); 11197 } 11198 this._tooltip.setLatLng(latlng); 11199 } 11200}); 11201 11202/* 11203 * @class DivIcon 11204 * @aka L.DivIcon 11205 * @inherits Icon 11206 * 11207 * Represents a lightweight icon for markers that uses a simple `<div>` 11208 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. 11209 * 11210 * @example 11211 * ```js 11212 * var myIcon = L.divIcon({className: 'my-div-icon'}); 11213 * // you can set .my-div-icon styles in CSS 11214 * 11215 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 11216 * ``` 11217 * 11218 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. 11219 */ 11220 11221var DivIcon = Icon.extend({ 11222 options: { 11223 // @section 11224 // @aka DivIcon options 11225 iconSize: [12, 12], // also can be set through CSS 11226 11227 // iconAnchor: (Point), 11228 // popupAnchor: (Point), 11229 11230 // @option html: String|HTMLElement = '' 11231 // Custom HTML code to put inside the div element, empty by default. Alternatively, 11232 // an instance of `HTMLElement`. 11233 html: false, 11234 11235 // @option bgPos: Point = [0, 0] 11236 // Optional relative position of the background, in pixels 11237 bgPos: null, 11238 11239 className: 'leaflet-div-icon' 11240 }, 11241 11242 createIcon: function (oldIcon) { 11243 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), 11244 options = this.options; 11245 11246 if (options.html instanceof Element) { 11247 empty(div); 11248 div.appendChild(options.html); 11249 } else { 11250 div.innerHTML = options.html !== false ? options.html : ''; 11251 } 11252 11253 if (options.bgPos) { 11254 var bgPos = toPoint(options.bgPos); 11255 div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; 11256 } 11257 this._setIconStyles(div, 'icon'); 11258 11259 return div; 11260 }, 11261 11262 createShadow: function () { 11263 return null; 11264 } 11265}); 11266 11267// @factory L.divIcon(options: DivIcon options) 11268// Creates a `DivIcon` instance with the given options. 11269function divIcon(options) { 11270 return new DivIcon(options); 11271} 11272 11273Icon.Default = IconDefault; 11274 11275/* 11276 * @class GridLayer 11277 * @inherits Layer 11278 * @aka L.GridLayer 11279 * 11280 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`. 11281 * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you. 11282 * 11283 * 11284 * @section Synchronous usage 11285 * @example 11286 * 11287 * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile. 11288 * 11289 * ```js 11290 * var CanvasLayer = L.GridLayer.extend({ 11291 * createTile: function(coords){ 11292 * // create a <canvas> element for drawing 11293 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 11294 * 11295 * // setup tile width and height according to the options 11296 * var size = this.getTileSize(); 11297 * tile.width = size.x; 11298 * tile.height = size.y; 11299 * 11300 * // get a canvas context and draw something on it using coords.x, coords.y and coords.z 11301 * var ctx = tile.getContext('2d'); 11302 * 11303 * // return the tile so it can be rendered on screen 11304 * return tile; 11305 * } 11306 * }); 11307 * ``` 11308 * 11309 * @section Asynchronous usage 11310 * @example 11311 * 11312 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback. 11313 * 11314 * ```js 11315 * var CanvasLayer = L.GridLayer.extend({ 11316 * createTile: function(coords, done){ 11317 * var error; 11318 * 11319 * // create a <canvas> element for drawing 11320 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 11321 * 11322 * // setup tile width and height according to the options 11323 * var size = this.getTileSize(); 11324 * tile.width = size.x; 11325 * tile.height = size.y; 11326 * 11327 * // draw something asynchronously and pass the tile to the done() callback 11328 * setTimeout(function() { 11329 * done(error, tile); 11330 * }, 1000); 11331 * 11332 * return tile; 11333 * } 11334 * }); 11335 * ``` 11336 * 11337 * @section 11338 */ 11339 11340 11341var GridLayer = Layer.extend({ 11342 11343 // @section 11344 // @aka GridLayer options 11345 options: { 11346 // @option tileSize: Number|Point = 256 11347 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. 11348 tileSize: 256, 11349 11350 // @option opacity: Number = 1.0 11351 // Opacity of the tiles. Can be used in the `createTile()` function. 11352 opacity: 1, 11353 11354 // @option updateWhenIdle: Boolean = (depends) 11355 // Load new tiles only when panning ends. 11356 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation. 11357 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the 11358 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers. 11359 updateWhenIdle: mobile, 11360 11361 // @option updateWhenZooming: Boolean = true 11362 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends. 11363 updateWhenZooming: true, 11364 11365 // @option updateInterval: Number = 200 11366 // Tiles will not update more than once every `updateInterval` milliseconds when panning. 11367 updateInterval: 200, 11368 11369 // @option zIndex: Number = 1 11370 // The explicit zIndex of the tile layer. 11371 zIndex: 1, 11372 11373 // @option bounds: LatLngBounds = undefined 11374 // If set, tiles will only be loaded inside the set `LatLngBounds`. 11375 bounds: null, 11376 11377 // @option minZoom: Number = 0 11378 // The minimum zoom level down to which this layer will be displayed (inclusive). 11379 minZoom: 0, 11380 11381 // @option maxZoom: Number = undefined 11382 // The maximum zoom level up to which this layer will be displayed (inclusive). 11383 maxZoom: undefined, 11384 11385 // @option maxNativeZoom: Number = undefined 11386 // Maximum zoom number the tile source has available. If it is specified, 11387 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded 11388 // from `maxNativeZoom` level and auto-scaled. 11389 maxNativeZoom: undefined, 11390 11391 // @option minNativeZoom: Number = undefined 11392 // Minimum zoom number the tile source has available. If it is specified, 11393 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded 11394 // from `minNativeZoom` level and auto-scaled. 11395 minNativeZoom: undefined, 11396 11397 // @option noWrap: Boolean = false 11398 // Whether the layer is wrapped around the antimeridian. If `true`, the 11399 // GridLayer will only be displayed once at low zoom levels. Has no 11400 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used 11401 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting 11402 // tiles outside the CRS limits. 11403 noWrap: false, 11404 11405 // @option pane: String = 'tilePane' 11406 // `Map pane` where the grid layer will be added. 11407 pane: 'tilePane', 11408 11409 // @option className: String = '' 11410 // A custom class name to assign to the tile layer. Empty by default. 11411 className: '', 11412 11413 // @option keepBuffer: Number = 2 11414 // When panning the map, keep this many rows and columns of tiles before unloading them. 11415 keepBuffer: 2 11416 }, 11417 11418 initialize: function (options) { 11419 setOptions(this, options); 11420 }, 11421 11422 onAdd: function () { 11423 this._initContainer(); 11424 11425 this._levels = {}; 11426 this._tiles = {}; 11427 11428 this._resetView(); 11429 this._update(); 11430 }, 11431 11432 beforeAdd: function (map) { 11433 map._addZoomLimit(this); 11434 }, 11435 11436 onRemove: function (map) { 11437 this._removeAllTiles(); 11438 remove(this._container); 11439 map._removeZoomLimit(this); 11440 this._container = null; 11441 this._tileZoom = undefined; 11442 }, 11443 11444 // @method bringToFront: this 11445 // Brings the tile layer to the top of all tile layers. 11446 bringToFront: function () { 11447 if (this._map) { 11448 toFront(this._container); 11449 this._setAutoZIndex(Math.max); 11450 } 11451 return this; 11452 }, 11453 11454 // @method bringToBack: this 11455 // Brings the tile layer to the bottom of all tile layers. 11456 bringToBack: function () { 11457 if (this._map) { 11458 toBack(this._container); 11459 this._setAutoZIndex(Math.min); 11460 } 11461 return this; 11462 }, 11463 11464 // @method getContainer: HTMLElement 11465 // Returns the HTML element that contains the tiles for this layer. 11466 getContainer: function () { 11467 return this._container; 11468 }, 11469 11470 // @method setOpacity(opacity: Number): this 11471 // Changes the [opacity](#gridlayer-opacity) of the grid layer. 11472 setOpacity: function (opacity) { 11473 this.options.opacity = opacity; 11474 this._updateOpacity(); 11475 return this; 11476 }, 11477 11478 // @method setZIndex(zIndex: Number): this 11479 // Changes the [zIndex](#gridlayer-zindex) of the grid layer. 11480 setZIndex: function (zIndex) { 11481 this.options.zIndex = zIndex; 11482 this._updateZIndex(); 11483 11484 return this; 11485 }, 11486 11487 // @method isLoading: Boolean 11488 // Returns `true` if any tile in the grid layer has not finished loading. 11489 isLoading: function () { 11490 return this._loading; 11491 }, 11492 11493 // @method redraw: this 11494 // Causes the layer to clear all the tiles and request them again. 11495 redraw: function () { 11496 if (this._map) { 11497 this._removeAllTiles(); 11498 this._update(); 11499 } 11500 return this; 11501 }, 11502 11503 getEvents: function () { 11504 var events = { 11505 viewprereset: this._invalidateAll, 11506 viewreset: this._resetView, 11507 zoom: this._resetView, 11508 moveend: this._onMoveEnd 11509 }; 11510 11511 if (!this.options.updateWhenIdle) { 11512 // update tiles on move, but not more often than once per given interval 11513 if (!this._onMove) { 11514 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this); 11515 } 11516 11517 events.move = this._onMove; 11518 } 11519 11520 if (this._zoomAnimated) { 11521 events.zoomanim = this._animateZoom; 11522 } 11523 11524 return events; 11525 }, 11526 11527 // @section Extension methods 11528 // Layers extending `GridLayer` shall reimplement the following method. 11529 // @method createTile(coords: Object, done?: Function): HTMLElement 11530 // Called only internally, must be overridden by classes extending `GridLayer`. 11531 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback 11532 // is specified, it must be called when the tile has finished loading and drawing. 11533 createTile: function () { 11534 return document.createElement('div'); 11535 }, 11536 11537 // @section 11538 // @method getTileSize: Point 11539 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. 11540 getTileSize: function () { 11541 var s = this.options.tileSize; 11542 return s instanceof Point ? s : new Point(s, s); 11543 }, 11544 11545 _updateZIndex: function () { 11546 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) { 11547 this._container.style.zIndex = this.options.zIndex; 11548 } 11549 }, 11550 11551 _setAutoZIndex: function (compare) { 11552 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back) 11553 11554 var layers = this.getPane().children, 11555 edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min 11556 11557 for (var i = 0, len = layers.length, zIndex; i < len; i++) { 11558 11559 zIndex = layers[i].style.zIndex; 11560 11561 if (layers[i] !== this._container && zIndex) { 11562 edgeZIndex = compare(edgeZIndex, +zIndex); 11563 } 11564 } 11565 11566 if (isFinite(edgeZIndex)) { 11567 this.options.zIndex = edgeZIndex + compare(-1, 1); 11568 this._updateZIndex(); 11569 } 11570 }, 11571 11572 _updateOpacity: function () { 11573 if (!this._map) { return; } 11574 11575 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles 11576 if (ielt9) { return; } 11577 11578 setOpacity(this._container, this.options.opacity); 11579 11580 var now = +new Date(), 11581 nextFrame = false, 11582 willPrune = false; 11583 11584 for (var key in this._tiles) { 11585 var tile = this._tiles[key]; 11586 if (!tile.current || !tile.loaded) { continue; } 11587 11588 var fade = Math.min(1, (now - tile.loaded) / 200); 11589 11590 setOpacity(tile.el, fade); 11591 if (fade < 1) { 11592 nextFrame = true; 11593 } else { 11594 if (tile.active) { 11595 willPrune = true; 11596 } else { 11597 this._onOpaqueTile(tile); 11598 } 11599 tile.active = true; 11600 } 11601 } 11602 11603 if (willPrune && !this._noPrune) { this._pruneTiles(); } 11604 11605 if (nextFrame) { 11606 cancelAnimFrame(this._fadeFrame); 11607 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 11608 } 11609 }, 11610 11611 _onOpaqueTile: falseFn, 11612 11613 _initContainer: function () { 11614 if (this._container) { return; } 11615 11616 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || '')); 11617 this._updateZIndex(); 11618 11619 if (this.options.opacity < 1) { 11620 this._updateOpacity(); 11621 } 11622 11623 this.getPane().appendChild(this._container); 11624 }, 11625 11626 _updateLevels: function () { 11627 11628 var zoom = this._tileZoom, 11629 maxZoom = this.options.maxZoom; 11630 11631 if (zoom === undefined) { return undefined; } 11632 11633 for (var z in this._levels) { 11634 if (this._levels[z].el.children.length || z === zoom) { 11635 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); 11636 this._onUpdateLevel(z); 11637 } else { 11638 remove(this._levels[z].el); 11639 this._removeTilesAtZoom(z); 11640 this._onRemoveLevel(z); 11641 delete this._levels[z]; 11642 } 11643 } 11644 11645 var level = this._levels[zoom], 11646 map = this._map; 11647 11648 if (!level) { 11649 level = this._levels[zoom] = {}; 11650 11651 level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); 11652 level.el.style.zIndex = maxZoom; 11653 11654 level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); 11655 level.zoom = zoom; 11656 11657 this._setZoomTransform(level, map.getCenter(), map.getZoom()); 11658 11659 // force the browser to consider the newly added element for transition 11660 falseFn(level.el.offsetWidth); 11661 11662 this._onCreateLevel(level); 11663 } 11664 11665 this._level = level; 11666 11667 return level; 11668 }, 11669 11670 _onUpdateLevel: falseFn, 11671 11672 _onRemoveLevel: falseFn, 11673 11674 _onCreateLevel: falseFn, 11675 11676 _pruneTiles: function () { 11677 if (!this._map) { 11678 return; 11679 } 11680 11681 var key, tile; 11682 11683 var zoom = this._map.getZoom(); 11684 if (zoom > this.options.maxZoom || 11685 zoom < this.options.minZoom) { 11686 this._removeAllTiles(); 11687 return; 11688 } 11689 11690 for (key in this._tiles) { 11691 tile = this._tiles[key]; 11692 tile.retain = tile.current; 11693 } 11694 11695 for (key in this._tiles) { 11696 tile = this._tiles[key]; 11697 if (tile.current && !tile.active) { 11698 var coords = tile.coords; 11699 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { 11700 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); 11701 } 11702 } 11703 } 11704 11705 for (key in this._tiles) { 11706 if (!this._tiles[key].retain) { 11707 this._removeTile(key); 11708 } 11709 } 11710 }, 11711 11712 _removeTilesAtZoom: function (zoom) { 11713 for (var key in this._tiles) { 11714 if (this._tiles[key].coords.z !== zoom) { 11715 continue; 11716 } 11717 this._removeTile(key); 11718 } 11719 }, 11720 11721 _removeAllTiles: function () { 11722 for (var key in this._tiles) { 11723 this._removeTile(key); 11724 } 11725 }, 11726 11727 _invalidateAll: function () { 11728 for (var z in this._levels) { 11729 remove(this._levels[z].el); 11730 this._onRemoveLevel(z); 11731 delete this._levels[z]; 11732 } 11733 this._removeAllTiles(); 11734 11735 this._tileZoom = undefined; 11736 }, 11737 11738 _retainParent: function (x, y, z, minZoom) { 11739 var x2 = Math.floor(x / 2), 11740 y2 = Math.floor(y / 2), 11741 z2 = z - 1, 11742 coords2 = new Point(+x2, +y2); 11743 coords2.z = +z2; 11744 11745 var key = this._tileCoordsToKey(coords2), 11746 tile = this._tiles[key]; 11747 11748 if (tile && tile.active) { 11749 tile.retain = true; 11750 return true; 11751 11752 } else if (tile && tile.loaded) { 11753 tile.retain = true; 11754 } 11755 11756 if (z2 > minZoom) { 11757 return this._retainParent(x2, y2, z2, minZoom); 11758 } 11759 11760 return false; 11761 }, 11762 11763 _retainChildren: function (x, y, z, maxZoom) { 11764 11765 for (var i = 2 * x; i < 2 * x + 2; i++) { 11766 for (var j = 2 * y; j < 2 * y + 2; j++) { 11767 11768 var coords = new Point(i, j); 11769 coords.z = z + 1; 11770 11771 var key = this._tileCoordsToKey(coords), 11772 tile = this._tiles[key]; 11773 11774 if (tile && tile.active) { 11775 tile.retain = true; 11776 continue; 11777 11778 } else if (tile && tile.loaded) { 11779 tile.retain = true; 11780 } 11781 11782 if (z + 1 < maxZoom) { 11783 this._retainChildren(i, j, z + 1, maxZoom); 11784 } 11785 } 11786 } 11787 }, 11788 11789 _resetView: function (e) { 11790 var animating = e && (e.pinch || e.flyTo); 11791 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); 11792 }, 11793 11794 _animateZoom: function (e) { 11795 this._setView(e.center, e.zoom, true, e.noUpdate); 11796 }, 11797 11798 _clampZoom: function (zoom) { 11799 var options = this.options; 11800 11801 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) { 11802 return options.minNativeZoom; 11803 } 11804 11805 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) { 11806 return options.maxNativeZoom; 11807 } 11808 11809 return zoom; 11810 }, 11811 11812 _setView: function (center, zoom, noPrune, noUpdate) { 11813 var tileZoom = this._clampZoom(Math.round(zoom)); 11814 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) || 11815 (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) { 11816 tileZoom = undefined; 11817 } 11818 11819 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom); 11820 11821 if (!noUpdate || tileZoomChanged) { 11822 11823 this._tileZoom = tileZoom; 11824 11825 if (this._abortLoading) { 11826 this._abortLoading(); 11827 } 11828 11829 this._updateLevels(); 11830 this._resetGrid(); 11831 11832 if (tileZoom !== undefined) { 11833 this._update(center); 11834 } 11835 11836 if (!noPrune) { 11837 this._pruneTiles(); 11838 } 11839 11840 // Flag to prevent _updateOpacity from pruning tiles during 11841 // a zoom anim or a pinch gesture 11842 this._noPrune = !!noPrune; 11843 } 11844 11845 this._setZoomTransforms(center, zoom); 11846 }, 11847 11848 _setZoomTransforms: function (center, zoom) { 11849 for (var i in this._levels) { 11850 this._setZoomTransform(this._levels[i], center, zoom); 11851 } 11852 }, 11853 11854 _setZoomTransform: function (level, center, zoom) { 11855 var scale = this._map.getZoomScale(zoom, level.zoom), 11856 translate = level.origin.multiplyBy(scale) 11857 .subtract(this._map._getNewPixelOrigin(center, zoom)).round(); 11858 11859 if (any3d) { 11860 setTransform(level.el, translate, scale); 11861 } else { 11862 setPosition(level.el, translate); 11863 } 11864 }, 11865 11866 _resetGrid: function () { 11867 var map = this._map, 11868 crs = map.options.crs, 11869 tileSize = this._tileSize = this.getTileSize(), 11870 tileZoom = this._tileZoom; 11871 11872 var bounds = this._map.getPixelWorldBounds(this._tileZoom); 11873 if (bounds) { 11874 this._globalTileRange = this._pxBoundsToTileRange(bounds); 11875 } 11876 11877 this._wrapX = crs.wrapLng && !this.options.noWrap && [ 11878 Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), 11879 Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) 11880 ]; 11881 this._wrapY = crs.wrapLat && !this.options.noWrap && [ 11882 Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), 11883 Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) 11884 ]; 11885 }, 11886 11887 _onMoveEnd: function () { 11888 if (!this._map || this._map._animatingZoom) { return; } 11889 11890 this._update(); 11891 }, 11892 11893 _getTiledPixelBounds: function (center) { 11894 var map = this._map, 11895 mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(), 11896 scale = map.getZoomScale(mapZoom, this._tileZoom), 11897 pixelCenter = map.project(center, this._tileZoom).floor(), 11898 halfSize = map.getSize().divideBy(scale * 2); 11899 11900 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); 11901 }, 11902 11903 // Private method to load tiles in the grid's active zoom level according to map bounds 11904 _update: function (center) { 11905 var map = this._map; 11906 if (!map) { return; } 11907 var zoom = this._clampZoom(map.getZoom()); 11908 11909 if (center === undefined) { center = map.getCenter(); } 11910 if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom 11911 11912 var pixelBounds = this._getTiledPixelBounds(center), 11913 tileRange = this._pxBoundsToTileRange(pixelBounds), 11914 tileCenter = tileRange.getCenter(), 11915 queue = [], 11916 margin = this.options.keepBuffer, 11917 noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]), 11918 tileRange.getTopRight().add([margin, -margin])); 11919 11920 // Sanity check: panic if the tile range contains Infinity somewhere. 11921 if (!(isFinite(tileRange.min.x) && 11922 isFinite(tileRange.min.y) && 11923 isFinite(tileRange.max.x) && 11924 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } 11925 11926 for (var key in this._tiles) { 11927 var c = this._tiles[key].coords; 11928 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) { 11929 this._tiles[key].current = false; 11930 } 11931 } 11932 11933 // _update just loads more tiles. If the tile zoom level differs too much 11934 // from the map's, let _setView reset levels and prune old tiles. 11935 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } 11936 11937 // create a queue of coordinates to load tiles from 11938 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { 11939 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { 11940 var coords = new Point(i, j); 11941 coords.z = this._tileZoom; 11942 11943 if (!this._isValidTile(coords)) { continue; } 11944 11945 var tile = this._tiles[this._tileCoordsToKey(coords)]; 11946 if (tile) { 11947 tile.current = true; 11948 } else { 11949 queue.push(coords); 11950 } 11951 } 11952 } 11953 11954 // sort tile queue to load tiles in order of their distance to center 11955 queue.sort(function (a, b) { 11956 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); 11957 }); 11958 11959 if (queue.length !== 0) { 11960 // if it's the first batch of tiles to load 11961 if (!this._loading) { 11962 this._loading = true; 11963 // @event loading: Event 11964 // Fired when the grid layer starts loading tiles. 11965 this.fire('loading'); 11966 } 11967 11968 // create DOM fragment to append tiles in one batch 11969 var fragment = document.createDocumentFragment(); 11970 11971 for (i = 0; i < queue.length; i++) { 11972 this._addTile(queue[i], fragment); 11973 } 11974 11975 this._level.el.appendChild(fragment); 11976 } 11977 }, 11978 11979 _isValidTile: function (coords) { 11980 var crs = this._map.options.crs; 11981 11982 if (!crs.infinite) { 11983 // don't load tile if it's out of bounds and not wrapped 11984 var bounds = this._globalTileRange; 11985 if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) || 11986 (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; } 11987 } 11988 11989 if (!this.options.bounds) { return true; } 11990 11991 // don't load tile if it doesn't intersect the bounds in options 11992 var tileBounds = this._tileCoordsToBounds(coords); 11993 return toLatLngBounds(this.options.bounds).overlaps(tileBounds); 11994 }, 11995 11996 _keyToBounds: function (key) { 11997 return this._tileCoordsToBounds(this._keyToTileCoords(key)); 11998 }, 11999 12000 _tileCoordsToNwSe: function (coords) { 12001 var map = this._map, 12002 tileSize = this.getTileSize(), 12003 nwPoint = coords.scaleBy(tileSize), 12004 sePoint = nwPoint.add(tileSize), 12005 nw = map.unproject(nwPoint, coords.z), 12006 se = map.unproject(sePoint, coords.z); 12007 return [nw, se]; 12008 }, 12009 12010 // converts tile coordinates to its geographical bounds 12011 _tileCoordsToBounds: function (coords) { 12012 var bp = this._tileCoordsToNwSe(coords), 12013 bounds = new LatLngBounds(bp[0], bp[1]); 12014 12015 if (!this.options.noWrap) { 12016 bounds = this._map.wrapLatLngBounds(bounds); 12017 } 12018 return bounds; 12019 }, 12020 // converts tile coordinates to key for the tile cache 12021 _tileCoordsToKey: function (coords) { 12022 return coords.x + ':' + coords.y + ':' + coords.z; 12023 }, 12024 12025 // converts tile cache key to coordinates 12026 _keyToTileCoords: function (key) { 12027 var k = key.split(':'), 12028 coords = new Point(+k[0], +k[1]); 12029 coords.z = +k[2]; 12030 return coords; 12031 }, 12032 12033 _removeTile: function (key) { 12034 var tile = this._tiles[key]; 12035 if (!tile) { return; } 12036 12037 remove(tile.el); 12038 12039 delete this._tiles[key]; 12040 12041 // @event tileunload: TileEvent 12042 // Fired when a tile is removed (e.g. when a tile goes off the screen). 12043 this.fire('tileunload', { 12044 tile: tile.el, 12045 coords: this._keyToTileCoords(key) 12046 }); 12047 }, 12048 12049 _initTile: function (tile) { 12050 addClass(tile, 'leaflet-tile'); 12051 12052 var tileSize = this.getTileSize(); 12053 tile.style.width = tileSize.x + 'px'; 12054 tile.style.height = tileSize.y + 'px'; 12055 12056 tile.onselectstart = falseFn; 12057 tile.onmousemove = falseFn; 12058 12059 // update opacity on tiles in IE7-8 because of filter inheritance problems 12060 if (ielt9 && this.options.opacity < 1) { 12061 setOpacity(tile, this.options.opacity); 12062 } 12063 12064 // without this hack, tiles disappear after zoom on Chrome for Android 12065 // https://github.com/Leaflet/Leaflet/issues/2078 12066 if (android && !android23) { 12067 tile.style.WebkitBackfaceVisibility = 'hidden'; 12068 } 12069 }, 12070 12071 _addTile: function (coords, container) { 12072 var tilePos = this._getTilePos(coords), 12073 key = this._tileCoordsToKey(coords); 12074 12075 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords)); 12076 12077 this._initTile(tile); 12078 12079 // if createTile is defined with a second argument ("done" callback), 12080 // we know that tile is async and will be ready later; otherwise 12081 if (this.createTile.length < 2) { 12082 // mark tile as ready, but delay one frame for opacity animation to happen 12083 requestAnimFrame(bind(this._tileReady, this, coords, null, tile)); 12084 } 12085 12086 setPosition(tile, tilePos); 12087 12088 // save tile in cache 12089 this._tiles[key] = { 12090 el: tile, 12091 coords: coords, 12092 current: true 12093 }; 12094 12095 container.appendChild(tile); 12096 // @event tileloadstart: TileEvent 12097 // Fired when a tile is requested and starts loading. 12098 this.fire('tileloadstart', { 12099 tile: tile, 12100 coords: coords 12101 }); 12102 }, 12103 12104 _tileReady: function (coords, err, tile) { 12105 if (err) { 12106 // @event tileerror: TileErrorEvent 12107 // Fired when there is an error loading a tile. 12108 this.fire('tileerror', { 12109 error: err, 12110 tile: tile, 12111 coords: coords 12112 }); 12113 } 12114 12115 var key = this._tileCoordsToKey(coords); 12116 12117 tile = this._tiles[key]; 12118 if (!tile) { return; } 12119 12120 tile.loaded = +new Date(); 12121 if (this._map._fadeAnimated) { 12122 setOpacity(tile.el, 0); 12123 cancelAnimFrame(this._fadeFrame); 12124 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 12125 } else { 12126 tile.active = true; 12127 this._pruneTiles(); 12128 } 12129 12130 if (!err) { 12131 addClass(tile.el, 'leaflet-tile-loaded'); 12132 12133 // @event tileload: TileEvent 12134 // Fired when a tile loads. 12135 this.fire('tileload', { 12136 tile: tile.el, 12137 coords: coords 12138 }); 12139 } 12140 12141 if (this._noTilesToLoad()) { 12142 this._loading = false; 12143 // @event load: Event 12144 // Fired when the grid layer loaded all visible tiles. 12145 this.fire('load'); 12146 12147 if (ielt9 || !this._map._fadeAnimated) { 12148 requestAnimFrame(this._pruneTiles, this); 12149 } else { 12150 // Wait a bit more than 0.2 secs (the duration of the tile fade-in) 12151 // to trigger a pruning. 12152 setTimeout(bind(this._pruneTiles, this), 250); 12153 } 12154 } 12155 }, 12156 12157 _getTilePos: function (coords) { 12158 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); 12159 }, 12160 12161 _wrapCoords: function (coords) { 12162 var newCoords = new Point( 12163 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x, 12164 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y); 12165 newCoords.z = coords.z; 12166 return newCoords; 12167 }, 12168 12169 _pxBoundsToTileRange: function (bounds) { 12170 var tileSize = this.getTileSize(); 12171 return new Bounds( 12172 bounds.min.unscaleBy(tileSize).floor(), 12173 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1])); 12174 }, 12175 12176 _noTilesToLoad: function () { 12177 for (var key in this._tiles) { 12178 if (!this._tiles[key].loaded) { return false; } 12179 } 12180 return true; 12181 } 12182}); 12183 12184// @factory L.gridLayer(options?: GridLayer options) 12185// Creates a new instance of GridLayer with the supplied options. 12186function gridLayer(options) { 12187 return new GridLayer(options); 12188} 12189 12190/* 12191 * @class TileLayer 12192 * @inherits GridLayer 12193 * @aka L.TileLayer 12194 * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`. 12195 * 12196 * @example 12197 * 12198 * ```js 12199 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map); 12200 * ``` 12201 * 12202 * @section URL template 12203 * @example 12204 * 12205 * A string of the following form: 12206 * 12207 * ``` 12208 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png' 12209 * ``` 12210 * 12211 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` â zoom level, `{x}` and `{y}` â tile coordinates. `{r}` can be used to add "@2x" to the URL to load retina tiles. 12212 * 12213 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this: 12214 * 12215 * ``` 12216 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'}); 12217 * ``` 12218 */ 12219 12220 12221var TileLayer = GridLayer.extend({ 12222 12223 // @section 12224 // @aka TileLayer options 12225 options: { 12226 // @option minZoom: Number = 0 12227 // The minimum zoom level down to which this layer will be displayed (inclusive). 12228 minZoom: 0, 12229 12230 // @option maxZoom: Number = 18 12231 // The maximum zoom level up to which this layer will be displayed (inclusive). 12232 maxZoom: 18, 12233 12234 // @option subdomains: String|String[] = 'abc' 12235 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings. 12236 subdomains: 'abc', 12237 12238 // @option errorTileUrl: String = '' 12239 // URL to the tile image to show in place of the tile that failed to load. 12240 errorTileUrl: '', 12241 12242 // @option zoomOffset: Number = 0 12243 // The zoom number used in tile URLs will be offset with this value. 12244 zoomOffset: 0, 12245 12246 // @option tms: Boolean = false 12247 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). 12248 tms: false, 12249 12250 // @option zoomReverse: Boolean = false 12251 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) 12252 zoomReverse: false, 12253 12254 // @option detectRetina: Boolean = false 12255 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution. 12256 detectRetina: false, 12257 12258 // @option crossOrigin: Boolean|String = false 12259 // Whether the crossOrigin attribute will be added to the tiles. 12260 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data. 12261 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 12262 crossOrigin: false 12263 }, 12264 12265 initialize: function (url, options) { 12266 12267 this._url = url; 12268 12269 options = setOptions(this, options); 12270 12271 // detecting retina displays, adjusting tileSize and zoom levels 12272 if (options.detectRetina && retina && options.maxZoom > 0) { 12273 12274 options.tileSize = Math.floor(options.tileSize / 2); 12275 12276 if (!options.zoomReverse) { 12277 options.zoomOffset++; 12278 options.maxZoom--; 12279 } else { 12280 options.zoomOffset--; 12281 options.minZoom++; 12282 } 12283 12284 options.minZoom = Math.max(0, options.minZoom); 12285 } 12286 12287 if (typeof options.subdomains === 'string') { 12288 options.subdomains = options.subdomains.split(''); 12289 } 12290 12291 // for https://github.com/Leaflet/Leaflet/issues/137 12292 if (!android) { 12293 this.on('tileunload', this._onTileRemove); 12294 } 12295 }, 12296 12297 // @method setUrl(url: String, noRedraw?: Boolean): this 12298 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). 12299 // If the URL does not change, the layer will not be redrawn unless 12300 // the noRedraw parameter is set to false. 12301 setUrl: function (url, noRedraw) { 12302 if (this._url === url && noRedraw === undefined) { 12303 noRedraw = true; 12304 } 12305 12306 this._url = url; 12307 12308 if (!noRedraw) { 12309 this.redraw(); 12310 } 12311 return this; 12312 }, 12313 12314 // @method createTile(coords: Object, done?: Function): HTMLElement 12315 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) 12316 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done` 12317 // callback is called when the tile has been loaded. 12318 createTile: function (coords, done) { 12319 var tile = document.createElement('img'); 12320 12321 on(tile, 'load', bind(this._tileOnLoad, this, done, tile)); 12322 on(tile, 'error', bind(this._tileOnError, this, done, tile)); 12323 12324 if (this.options.crossOrigin || this.options.crossOrigin === '') { 12325 tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 12326 } 12327 12328 /* 12329 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons 12330 http://www.w3.org/TR/WCAG20-TECHS/H67 12331 */ 12332 tile.alt = ''; 12333 12334 /* 12335 Set role="presentation" to force screen readers to ignore this 12336 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation 12337 */ 12338 tile.setAttribute('role', 'presentation'); 12339 12340 tile.src = this.getTileUrl(coords); 12341 12342 return tile; 12343 }, 12344 12345 // @section Extension methods 12346 // @uninheritable 12347 // Layers extending `TileLayer` might reimplement the following method. 12348 // @method getTileUrl(coords: Object): String 12349 // Called only internally, returns the URL for a tile given its coordinates. 12350 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. 12351 getTileUrl: function (coords) { 12352 var data = { 12353 r: retina ? '@2x' : '', 12354 s: this._getSubdomain(coords), 12355 x: coords.x, 12356 y: coords.y, 12357 z: this._getZoomForUrl() 12358 }; 12359 if (this._map && !this._map.options.crs.infinite) { 12360 var invertedY = this._globalTileRange.max.y - coords.y; 12361 if (this.options.tms) { 12362 data['y'] = invertedY; 12363 } 12364 data['-y'] = invertedY; 12365 } 12366 12367 return template(this._url, extend(data, this.options)); 12368 }, 12369 12370 _tileOnLoad: function (done, tile) { 12371 // For https://github.com/Leaflet/Leaflet/issues/3332 12372 if (ielt9) { 12373 setTimeout(bind(done, this, null, tile), 0); 12374 } else { 12375 done(null, tile); 12376 } 12377 }, 12378 12379 _tileOnError: function (done, tile, e) { 12380 var errorUrl = this.options.errorTileUrl; 12381 if (errorUrl && tile.getAttribute('src') !== errorUrl) { 12382 tile.src = errorUrl; 12383 } 12384 done(e, tile); 12385 }, 12386 12387 _onTileRemove: function (e) { 12388 e.tile.onload = null; 12389 }, 12390 12391 _getZoomForUrl: function () { 12392 var zoom = this._tileZoom, 12393 maxZoom = this.options.maxZoom, 12394 zoomReverse = this.options.zoomReverse, 12395 zoomOffset = this.options.zoomOffset; 12396 12397 if (zoomReverse) { 12398 zoom = maxZoom - zoom; 12399 } 12400 12401 return zoom + zoomOffset; 12402 }, 12403 12404 _getSubdomain: function (tilePoint) { 12405 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; 12406 return this.options.subdomains[index]; 12407 }, 12408 12409 // stops loading all tiles in the background layer 12410 _abortLoading: function () { 12411 var i, tile; 12412 for (i in this._tiles) { 12413 if (this._tiles[i].coords.z !== this._tileZoom) { 12414 tile = this._tiles[i].el; 12415 12416 tile.onload = falseFn; 12417 tile.onerror = falseFn; 12418 12419 if (!tile.complete) { 12420 tile.src = emptyImageUrl; 12421 remove(tile); 12422 delete this._tiles[i]; 12423 } 12424 } 12425 } 12426 }, 12427 12428 _removeTile: function (key) { 12429 var tile = this._tiles[key]; 12430 if (!tile) { return; } 12431 12432 // Cancels any pending http requests associated with the tile 12433 // unless we're on Android's stock browser, 12434 // see https://github.com/Leaflet/Leaflet/issues/137 12435 if (!androidStock) { 12436 tile.el.setAttribute('src', emptyImageUrl); 12437 } 12438 12439 return GridLayer.prototype._removeTile.call(this, key); 12440 }, 12441 12442 _tileReady: function (coords, err, tile) { 12443 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) { 12444 return; 12445 } 12446 12447 return GridLayer.prototype._tileReady.call(this, coords, err, tile); 12448 } 12449}); 12450 12451 12452// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options) 12453// Instantiates a tile layer object given a `URL template` and optionally an options object. 12454 12455function tileLayer(url, options) { 12456 return new TileLayer(url, options); 12457} 12458 12459/* 12460 * @class TileLayer.WMS 12461 * @inherits TileLayer 12462 * @aka L.TileLayer.WMS 12463 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`. 12464 * 12465 * @example 12466 * 12467 * ```js 12468 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", { 12469 * layers: 'nexrad-n0r-900913', 12470 * format: 'image/png', 12471 * transparent: true, 12472 * attribution: "Weather data © 2012 IEM Nexrad" 12473 * }); 12474 * ``` 12475 */ 12476 12477var TileLayerWMS = TileLayer.extend({ 12478 12479 // @section 12480 // @aka TileLayer.WMS options 12481 // If any custom options not documented here are used, they will be sent to the 12482 // WMS server as extra parameters in each request URL. This can be useful for 12483 // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html). 12484 defaultWmsParams: { 12485 service: 'WMS', 12486 request: 'GetMap', 12487 12488 // @option layers: String = '' 12489 // **(required)** Comma-separated list of WMS layers to show. 12490 layers: '', 12491 12492 // @option styles: String = '' 12493 // Comma-separated list of WMS styles. 12494 styles: '', 12495 12496 // @option format: String = 'image/jpeg' 12497 // WMS image format (use `'image/png'` for layers with transparency). 12498 format: 'image/jpeg', 12499 12500 // @option transparent: Boolean = false 12501 // If `true`, the WMS service will return images with transparency. 12502 transparent: false, 12503 12504 // @option version: String = '1.1.1' 12505 // Version of the WMS service to use 12506 version: '1.1.1' 12507 }, 12508 12509 options: { 12510 // @option crs: CRS = null 12511 // Coordinate Reference System to use for the WMS requests, defaults to 12512 // map CRS. Don't change this if you're not sure what it means. 12513 crs: null, 12514 12515 // @option uppercase: Boolean = false 12516 // If `true`, WMS request parameter keys will be uppercase. 12517 uppercase: false 12518 }, 12519 12520 initialize: function (url, options) { 12521 12522 this._url = url; 12523 12524 var wmsParams = extend({}, this.defaultWmsParams); 12525 12526 // all keys that are not TileLayer options go to WMS params 12527 for (var i in options) { 12528 if (!(i in this.options)) { 12529 wmsParams[i] = options[i]; 12530 } 12531 } 12532 12533 options = setOptions(this, options); 12534 12535 var realRetina = options.detectRetina && retina ? 2 : 1; 12536 var tileSize = this.getTileSize(); 12537 wmsParams.width = tileSize.x * realRetina; 12538 wmsParams.height = tileSize.y * realRetina; 12539 12540 this.wmsParams = wmsParams; 12541 }, 12542 12543 onAdd: function (map) { 12544 12545 this._crs = this.options.crs || map.options.crs; 12546 this._wmsVersion = parseFloat(this.wmsParams.version); 12547 12548 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; 12549 this.wmsParams[projectionKey] = this._crs.code; 12550 12551 TileLayer.prototype.onAdd.call(this, map); 12552 }, 12553 12554 getTileUrl: function (coords) { 12555 12556 var tileBounds = this._tileCoordsToNwSe(coords), 12557 crs = this._crs, 12558 bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), 12559 min = bounds.min, 12560 max = bounds.max, 12561 bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? 12562 [min.y, min.x, max.y, max.x] : 12563 [min.x, min.y, max.x, max.y]).join(','), 12564 url = TileLayer.prototype.getTileUrl.call(this, coords); 12565 return url + 12566 getParamString(this.wmsParams, url, this.options.uppercase) + 12567 (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox; 12568 }, 12569 12570 // @method setParams(params: Object, noRedraw?: Boolean): this 12571 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). 12572 setParams: function (params, noRedraw) { 12573 12574 extend(this.wmsParams, params); 12575 12576 if (!noRedraw) { 12577 this.redraw(); 12578 } 12579 12580 return this; 12581 } 12582}); 12583 12584 12585// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options) 12586// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object. 12587function tileLayerWMS(url, options) { 12588 return new TileLayerWMS(url, options); 12589} 12590 12591TileLayer.WMS = TileLayerWMS; 12592tileLayer.wms = tileLayerWMS; 12593 12594/* 12595 * @class Renderer 12596 * @inherits Layer 12597 * @aka L.Renderer 12598 * 12599 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the 12600 * DOM container of the renderer, its bounds, and its zoom animation. 12601 * 12602 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer 12603 * itself can be added or removed to the map. All paths use a renderer, which can 12604 * be implicit (the map will decide the type of renderer and use it automatically) 12605 * or explicit (using the [`renderer`](#path-renderer) option of the path). 12606 * 12607 * Do not use this class directly, use `SVG` and `Canvas` instead. 12608 * 12609 * @event update: Event 12610 * Fired when the renderer updates its bounds, center and zoom, for example when 12611 * its map has moved 12612 */ 12613 12614var Renderer = Layer.extend({ 12615 12616 // @section 12617 // @aka Renderer options 12618 options: { 12619 // @option padding: Number = 0.1 12620 // How much to extend the clip area around the map view (relative to its size) 12621 // e.g. 0.1 would be 10% of map view in each direction 12622 padding: 0.1, 12623 12624 // @option tolerance: Number = 0 12625 // How much to extend click tolerance round a path/object on the map 12626 tolerance : 0 12627 }, 12628 12629 initialize: function (options) { 12630 setOptions(this, options); 12631 stamp(this); 12632 this._layers = this._layers || {}; 12633 }, 12634 12635 onAdd: function () { 12636 if (!this._container) { 12637 this._initContainer(); // defined by renderer implementations 12638 12639 if (this._zoomAnimated) { 12640 addClass(this._container, 'leaflet-zoom-animated'); 12641 } 12642 } 12643 12644 this.getPane().appendChild(this._container); 12645 this._update(); 12646 this.on('update', this._updatePaths, this); 12647 }, 12648 12649 onRemove: function () { 12650 this.off('update', this._updatePaths, this); 12651 this._destroyContainer(); 12652 }, 12653 12654 getEvents: function () { 12655 var events = { 12656 viewreset: this._reset, 12657 zoom: this._onZoom, 12658 moveend: this._update, 12659 zoomend: this._onZoomEnd 12660 }; 12661 if (this._zoomAnimated) { 12662 events.zoomanim = this._onAnimZoom; 12663 } 12664 return events; 12665 }, 12666 12667 _onAnimZoom: function (ev) { 12668 this._updateTransform(ev.center, ev.zoom); 12669 }, 12670 12671 _onZoom: function () { 12672 this._updateTransform(this._map.getCenter(), this._map.getZoom()); 12673 }, 12674 12675 _updateTransform: function (center, zoom) { 12676 var scale = this._map.getZoomScale(zoom, this._zoom), 12677 position = getPosition(this._container), 12678 viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), 12679 currentCenterPoint = this._map.project(this._center, zoom), 12680 destCenterPoint = this._map.project(center, zoom), 12681 centerOffset = destCenterPoint.subtract(currentCenterPoint), 12682 12683 topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset); 12684 12685 if (any3d) { 12686 setTransform(this._container, topLeftOffset, scale); 12687 } else { 12688 setPosition(this._container, topLeftOffset); 12689 } 12690 }, 12691 12692 _reset: function () { 12693 this._update(); 12694 this._updateTransform(this._center, this._zoom); 12695 12696 for (var id in this._layers) { 12697 this._layers[id]._reset(); 12698 } 12699 }, 12700 12701 _onZoomEnd: function () { 12702 for (var id in this._layers) { 12703 this._layers[id]._project(); 12704 } 12705 }, 12706 12707 _updatePaths: function () { 12708 for (var id in this._layers) { 12709 this._layers[id]._update(); 12710 } 12711 }, 12712 12713 _update: function () { 12714 // Update pixel bounds of renderer container (for positioning/sizing/clipping later) 12715 // Subclasses are responsible of firing the 'update' event. 12716 var p = this.options.padding, 12717 size = this._map.getSize(), 12718 min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); 12719 12720 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); 12721 12722 this._center = this._map.getCenter(); 12723 this._zoom = this._map.getZoom(); 12724 } 12725}); 12726 12727/* 12728 * @class Canvas 12729 * @inherits Renderer 12730 * @aka L.Canvas 12731 * 12732 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 12733 * Inherits `Renderer`. 12734 * 12735 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not 12736 * available in all web browsers, notably IE8, and overlapping geometries might 12737 * not display properly in some edge cases. 12738 * 12739 * @example 12740 * 12741 * Use Canvas by default for all paths in the map: 12742 * 12743 * ```js 12744 * var map = L.map('map', { 12745 * renderer: L.canvas() 12746 * }); 12747 * ``` 12748 * 12749 * Use a Canvas renderer with extra padding for specific vector geometries: 12750 * 12751 * ```js 12752 * var map = L.map('map'); 12753 * var myRenderer = L.canvas({ padding: 0.5 }); 12754 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 12755 * var circle = L.circle( center, { renderer: myRenderer } ); 12756 * ``` 12757 */ 12758 12759var Canvas = Renderer.extend({ 12760 getEvents: function () { 12761 var events = Renderer.prototype.getEvents.call(this); 12762 events.viewprereset = this._onViewPreReset; 12763 return events; 12764 }, 12765 12766 _onViewPreReset: function () { 12767 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once 12768 this._postponeUpdatePaths = true; 12769 }, 12770 12771 onAdd: function () { 12772 Renderer.prototype.onAdd.call(this); 12773 12774 // Redraw vectors since canvas is cleared upon removal, 12775 // in case of removing the renderer itself from the map. 12776 this._draw(); 12777 }, 12778 12779 _initContainer: function () { 12780 var container = this._container = document.createElement('canvas'); 12781 12782 on(container, 'mousemove', this._onMouseMove, this); 12783 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this); 12784 on(container, 'mouseout', this._handleMouseOut, this); 12785 12786 this._ctx = container.getContext('2d'); 12787 }, 12788 12789 _destroyContainer: function () { 12790 cancelAnimFrame(this._redrawRequest); 12791 delete this._ctx; 12792 remove(this._container); 12793 off(this._container); 12794 delete this._container; 12795 }, 12796 12797 _updatePaths: function () { 12798 if (this._postponeUpdatePaths) { return; } 12799 12800 var layer; 12801 this._redrawBounds = null; 12802 for (var id in this._layers) { 12803 layer = this._layers[id]; 12804 layer._update(); 12805 } 12806 this._redraw(); 12807 }, 12808 12809 _update: function () { 12810 if (this._map._animatingZoom && this._bounds) { return; } 12811 12812 Renderer.prototype._update.call(this); 12813 12814 var b = this._bounds, 12815 container = this._container, 12816 size = b.getSize(), 12817 m = retina ? 2 : 1; 12818 12819 setPosition(container, b.min); 12820 12821 // set canvas size (also clearing it); use double size on retina 12822 container.width = m * size.x; 12823 container.height = m * size.y; 12824 container.style.width = size.x + 'px'; 12825 container.style.height = size.y + 'px'; 12826 12827 if (retina) { 12828 this._ctx.scale(2, 2); 12829 } 12830 12831 // translate so we use the same path coordinates after canvas element moves 12832 this._ctx.translate(-b.min.x, -b.min.y); 12833 12834 // Tell paths to redraw themselves 12835 this.fire('update'); 12836 }, 12837 12838 _reset: function () { 12839 Renderer.prototype._reset.call(this); 12840 12841 if (this._postponeUpdatePaths) { 12842 this._postponeUpdatePaths = false; 12843 this._updatePaths(); 12844 } 12845 }, 12846 12847 _initPath: function (layer) { 12848 this._updateDashArray(layer); 12849 this._layers[stamp(layer)] = layer; 12850 12851 var order = layer._order = { 12852 layer: layer, 12853 prev: this._drawLast, 12854 next: null 12855 }; 12856 if (this._drawLast) { this._drawLast.next = order; } 12857 this._drawLast = order; 12858 this._drawFirst = this._drawFirst || this._drawLast; 12859 }, 12860 12861 _addPath: function (layer) { 12862 this._requestRedraw(layer); 12863 }, 12864 12865 _removePath: function (layer) { 12866 var order = layer._order; 12867 var next = order.next; 12868 var prev = order.prev; 12869 12870 if (next) { 12871 next.prev = prev; 12872 } else { 12873 this._drawLast = prev; 12874 } 12875 if (prev) { 12876 prev.next = next; 12877 } else { 12878 this._drawFirst = next; 12879 } 12880 12881 delete layer._order; 12882 12883 delete this._layers[stamp(layer)]; 12884 12885 this._requestRedraw(layer); 12886 }, 12887 12888 _updatePath: function (layer) { 12889 // Redraw the union of the layer's old pixel 12890 // bounds and the new pixel bounds. 12891 this._extendRedrawBounds(layer); 12892 layer._project(); 12893 layer._update(); 12894 // The redraw will extend the redraw bounds 12895 // with the new pixel bounds. 12896 this._requestRedraw(layer); 12897 }, 12898 12899 _updateStyle: function (layer) { 12900 this._updateDashArray(layer); 12901 this._requestRedraw(layer); 12902 }, 12903 12904 _updateDashArray: function (layer) { 12905 if (typeof layer.options.dashArray === 'string') { 12906 var parts = layer.options.dashArray.split(/[, ]+/), 12907 dashArray = [], 12908 dashValue, 12909 i; 12910 for (i = 0; i < parts.length; i++) { 12911 dashValue = Number(parts[i]); 12912 // Ignore dash array containing invalid lengths 12913 if (isNaN(dashValue)) { return; } 12914 dashArray.push(dashValue); 12915 } 12916 layer.options._dashArray = dashArray; 12917 } else { 12918 layer.options._dashArray = layer.options.dashArray; 12919 } 12920 }, 12921 12922 _requestRedraw: function (layer) { 12923 if (!this._map) { return; } 12924 12925 this._extendRedrawBounds(layer); 12926 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this); 12927 }, 12928 12929 _extendRedrawBounds: function (layer) { 12930 if (layer._pxBounds) { 12931 var padding = (layer.options.weight || 0) + 1; 12932 this._redrawBounds = this._redrawBounds || new Bounds(); 12933 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); 12934 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); 12935 } 12936 }, 12937 12938 _redraw: function () { 12939 this._redrawRequest = null; 12940 12941 if (this._redrawBounds) { 12942 this._redrawBounds.min._floor(); 12943 this._redrawBounds.max._ceil(); 12944 } 12945 12946 this._clear(); // clear layers in redraw bounds 12947 this._draw(); // draw layers 12948 12949 this._redrawBounds = null; 12950 }, 12951 12952 _clear: function () { 12953 var bounds = this._redrawBounds; 12954 if (bounds) { 12955 var size = bounds.getSize(); 12956 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); 12957 } else { 12958 this._ctx.clearRect(0, 0, this._container.width, this._container.height); 12959 } 12960 }, 12961 12962 _draw: function () { 12963 var layer, bounds = this._redrawBounds; 12964 this._ctx.save(); 12965 if (bounds) { 12966 var size = bounds.getSize(); 12967 this._ctx.beginPath(); 12968 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); 12969 this._ctx.clip(); 12970 } 12971 12972 this._drawing = true; 12973 12974 for (var order = this._drawFirst; order; order = order.next) { 12975 layer = order.layer; 12976 if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) { 12977 layer._updatePath(); 12978 } 12979 } 12980 12981 this._drawing = false; 12982 12983 this._ctx.restore(); // Restore state before clipping. 12984 }, 12985 12986 _updatePoly: function (layer, closed) { 12987 if (!this._drawing) { return; } 12988 12989 var i, j, len2, p, 12990 parts = layer._parts, 12991 len = parts.length, 12992 ctx = this._ctx; 12993 12994 if (!len) { return; } 12995 12996 ctx.beginPath(); 12997 12998 for (i = 0; i < len; i++) { 12999 for (j = 0, len2 = parts[i].length; j < len2; j++) { 13000 p = parts[i][j]; 13001 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y); 13002 } 13003 if (closed) { 13004 ctx.closePath(); 13005 } 13006 } 13007 13008 this._fillStroke(ctx, layer); 13009 13010 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature 13011 }, 13012 13013 _updateCircle: function (layer) { 13014 13015 if (!this._drawing || layer._empty()) { return; } 13016 13017 var p = layer._point, 13018 ctx = this._ctx, 13019 r = Math.max(Math.round(layer._radius), 1), 13020 s = (Math.max(Math.round(layer._radiusY), 1) || r) / r; 13021 13022 if (s !== 1) { 13023 ctx.save(); 13024 ctx.scale(1, s); 13025 } 13026 13027 ctx.beginPath(); 13028 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); 13029 13030 if (s !== 1) { 13031 ctx.restore(); 13032 } 13033 13034 this._fillStroke(ctx, layer); 13035 }, 13036 13037 _fillStroke: function (ctx, layer) { 13038 var options = layer.options; 13039 13040 if (options.fill) { 13041 ctx.globalAlpha = options.fillOpacity; 13042 ctx.fillStyle = options.fillColor || options.color; 13043 ctx.fill(options.fillRule || 'evenodd'); 13044 } 13045 13046 if (options.stroke && options.weight !== 0) { 13047 if (ctx.setLineDash) { 13048 ctx.setLineDash(layer.options && layer.options._dashArray || []); 13049 } 13050 ctx.globalAlpha = options.opacity; 13051 ctx.lineWidth = options.weight; 13052 ctx.strokeStyle = options.color; 13053 ctx.lineCap = options.lineCap; 13054 ctx.lineJoin = options.lineJoin; 13055 ctx.stroke(); 13056 } 13057 }, 13058 13059 // Canvas obviously doesn't have mouse events for individual drawn objects, 13060 // so we emulate that by calculating what's under the mouse on mousemove/click manually 13061 13062 _onClick: function (e) { 13063 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; 13064 13065 for (var order = this._drawFirst; order; order = order.next) { 13066 layer = order.layer; 13067 if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) { 13068 clickedLayer = layer; 13069 } 13070 } 13071 if (clickedLayer) { 13072 fakeStop(e); 13073 this._fireEvent([clickedLayer], e); 13074 } 13075 }, 13076 13077 _onMouseMove: function (e) { 13078 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } 13079 13080 var point = this._map.mouseEventToLayerPoint(e); 13081 this._handleMouseHover(e, point); 13082 }, 13083 13084 13085 _handleMouseOut: function (e) { 13086 var layer = this._hoveredLayer; 13087 if (layer) { 13088 // if we're leaving the layer, fire mouseout 13089 removeClass(this._container, 'leaflet-interactive'); 13090 this._fireEvent([layer], e, 'mouseout'); 13091 this._hoveredLayer = null; 13092 this._mouseHoverThrottled = false; 13093 } 13094 }, 13095 13096 _handleMouseHover: function (e, point) { 13097 if (this._mouseHoverThrottled) { 13098 return; 13099 } 13100 13101 var layer, candidateHoveredLayer; 13102 13103 for (var order = this._drawFirst; order; order = order.next) { 13104 layer = order.layer; 13105 if (layer.options.interactive && layer._containsPoint(point)) { 13106 candidateHoveredLayer = layer; 13107 } 13108 } 13109 13110 if (candidateHoveredLayer !== this._hoveredLayer) { 13111 this._handleMouseOut(e); 13112 13113 if (candidateHoveredLayer) { 13114 addClass(this._container, 'leaflet-interactive'); // change cursor 13115 this._fireEvent([candidateHoveredLayer], e, 'mouseover'); 13116 this._hoveredLayer = candidateHoveredLayer; 13117 } 13118 } 13119 13120 if (this._hoveredLayer) { 13121 this._fireEvent([this._hoveredLayer], e); 13122 } 13123 13124 this._mouseHoverThrottled = true; 13125 setTimeout(L.bind(function () { 13126 this._mouseHoverThrottled = false; 13127 }, this), 32); 13128 }, 13129 13130 _fireEvent: function (layers, e, type) { 13131 this._map._fireDOMEvent(e, type || e.type, layers); 13132 }, 13133 13134 _bringToFront: function (layer) { 13135 var order = layer._order; 13136 13137 if (!order) { return; } 13138 13139 var next = order.next; 13140 var prev = order.prev; 13141 13142 if (next) { 13143 next.prev = prev; 13144 } else { 13145 // Already last 13146 return; 13147 } 13148 if (prev) { 13149 prev.next = next; 13150 } else if (next) { 13151 // Update first entry unless this is the 13152 // single entry 13153 this._drawFirst = next; 13154 } 13155 13156 order.prev = this._drawLast; 13157 this._drawLast.next = order; 13158 13159 order.next = null; 13160 this._drawLast = order; 13161 13162 this._requestRedraw(layer); 13163 }, 13164 13165 _bringToBack: function (layer) { 13166 var order = layer._order; 13167 13168 if (!order) { return; } 13169 13170 var next = order.next; 13171 var prev = order.prev; 13172 13173 if (prev) { 13174 prev.next = next; 13175 } else { 13176 // Already first 13177 return; 13178 } 13179 if (next) { 13180 next.prev = prev; 13181 } else if (prev) { 13182 // Update last entry unless this is the 13183 // single entry 13184 this._drawLast = prev; 13185 } 13186 13187 order.prev = null; 13188 13189 order.next = this._drawFirst; 13190 this._drawFirst.prev = order; 13191 this._drawFirst = order; 13192 13193 this._requestRedraw(layer); 13194 } 13195}); 13196 13197// @factory L.canvas(options?: Renderer options) 13198// Creates a Canvas renderer with the given options. 13199function canvas$1(options) { 13200 return canvas ? new Canvas(options) : null; 13201} 13202 13203/* 13204 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! 13205 */ 13206 13207 13208var vmlCreate = (function () { 13209 try { 13210 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); 13211 return function (name) { 13212 return document.createElement('<lvml:' + name + ' class="lvml">'); 13213 }; 13214 } catch (e) { 13215 return function (name) { 13216 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); 13217 }; 13218 } 13219})(); 13220 13221 13222/* 13223 * @class SVG 13224 * 13225 * 13226 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility 13227 * with old versions of Internet Explorer. 13228 */ 13229 13230// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences 13231var vmlMixin = { 13232 13233 _initContainer: function () { 13234 this._container = create$1('div', 'leaflet-vml-container'); 13235 }, 13236 13237 _update: function () { 13238 if (this._map._animatingZoom) { return; } 13239 Renderer.prototype._update.call(this); 13240 this.fire('update'); 13241 }, 13242 13243 _initPath: function (layer) { 13244 var container = layer._container = vmlCreate('shape'); 13245 13246 addClass(container, 'leaflet-vml-shape ' + (this.options.className || '')); 13247 13248 container.coordsize = '1 1'; 13249 13250 layer._path = vmlCreate('path'); 13251 container.appendChild(layer._path); 13252 13253 this._updateStyle(layer); 13254 this._layers[stamp(layer)] = layer; 13255 }, 13256 13257 _addPath: function (layer) { 13258 var container = layer._container; 13259 this._container.appendChild(container); 13260 13261 if (layer.options.interactive) { 13262 layer.addInteractiveTarget(container); 13263 } 13264 }, 13265 13266 _removePath: function (layer) { 13267 var container = layer._container; 13268 remove(container); 13269 layer.removeInteractiveTarget(container); 13270 delete this._layers[stamp(layer)]; 13271 }, 13272 13273 _updateStyle: function (layer) { 13274 var stroke = layer._stroke, 13275 fill = layer._fill, 13276 options = layer.options, 13277 container = layer._container; 13278 13279 container.stroked = !!options.stroke; 13280 container.filled = !!options.fill; 13281 13282 if (options.stroke) { 13283 if (!stroke) { 13284 stroke = layer._stroke = vmlCreate('stroke'); 13285 } 13286 container.appendChild(stroke); 13287 stroke.weight = options.weight + 'px'; 13288 stroke.color = options.color; 13289 stroke.opacity = options.opacity; 13290 13291 if (options.dashArray) { 13292 stroke.dashStyle = isArray(options.dashArray) ? 13293 options.dashArray.join(' ') : 13294 options.dashArray.replace(/( *, *)/g, ' '); 13295 } else { 13296 stroke.dashStyle = ''; 13297 } 13298 stroke.endcap = options.lineCap.replace('butt', 'flat'); 13299 stroke.joinstyle = options.lineJoin; 13300 13301 } else if (stroke) { 13302 container.removeChild(stroke); 13303 layer._stroke = null; 13304 } 13305 13306 if (options.fill) { 13307 if (!fill) { 13308 fill = layer._fill = vmlCreate('fill'); 13309 } 13310 container.appendChild(fill); 13311 fill.color = options.fillColor || options.color; 13312 fill.opacity = options.fillOpacity; 13313 13314 } else if (fill) { 13315 container.removeChild(fill); 13316 layer._fill = null; 13317 } 13318 }, 13319 13320 _updateCircle: function (layer) { 13321 var p = layer._point.round(), 13322 r = Math.round(layer._radius), 13323 r2 = Math.round(layer._radiusY || r); 13324 13325 this._setPath(layer, layer._empty() ? 'M0 0' : 13326 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360)); 13327 }, 13328 13329 _setPath: function (layer, path) { 13330 layer._path.v = path; 13331 }, 13332 13333 _bringToFront: function (layer) { 13334 toFront(layer._container); 13335 }, 13336 13337 _bringToBack: function (layer) { 13338 toBack(layer._container); 13339 } 13340}; 13341 13342var create$2 = vml ? vmlCreate : svgCreate; 13343 13344/* 13345 * @class SVG 13346 * @inherits Renderer 13347 * @aka L.SVG 13348 * 13349 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). 13350 * Inherits `Renderer`. 13351 * 13352 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not 13353 * available in all web browsers, notably Android 2.x and 3.x. 13354 * 13355 * Although SVG is not available on IE7 and IE8, these browsers support 13356 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) 13357 * (a now deprecated technology), and the SVG renderer will fall back to VML in 13358 * this case. 13359 * 13360 * @example 13361 * 13362 * Use SVG by default for all paths in the map: 13363 * 13364 * ```js 13365 * var map = L.map('map', { 13366 * renderer: L.svg() 13367 * }); 13368 * ``` 13369 * 13370 * Use a SVG renderer with extra padding for specific vector geometries: 13371 * 13372 * ```js 13373 * var map = L.map('map'); 13374 * var myRenderer = L.svg({ padding: 0.5 }); 13375 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 13376 * var circle = L.circle( center, { renderer: myRenderer } ); 13377 * ``` 13378 */ 13379 13380var SVG = Renderer.extend({ 13381 13382 getEvents: function () { 13383 var events = Renderer.prototype.getEvents.call(this); 13384 events.zoomstart = this._onZoomStart; 13385 return events; 13386 }, 13387 13388 _initContainer: function () { 13389 this._container = create$2('svg'); 13390 13391 // makes it possible to click through svg root; we'll reset it back in individual paths 13392 this._container.setAttribute('pointer-events', 'none'); 13393 13394 this._rootGroup = create$2('g'); 13395 this._container.appendChild(this._rootGroup); 13396 }, 13397 13398 _destroyContainer: function () { 13399 remove(this._container); 13400 off(this._container); 13401 delete this._container; 13402 delete this._rootGroup; 13403 delete this._svgSize; 13404 }, 13405 13406 _onZoomStart: function () { 13407 // Drag-then-pinch interactions might mess up the center and zoom. 13408 // In this case, the easiest way to prevent this is re-do the renderer 13409 // bounds and padding when the zooming starts. 13410 this._update(); 13411 }, 13412 13413 _update: function () { 13414 if (this._map._animatingZoom && this._bounds) { return; } 13415 13416 Renderer.prototype._update.call(this); 13417 13418 var b = this._bounds, 13419 size = b.getSize(), 13420 container = this._container; 13421 13422 // set size of svg-container if changed 13423 if (!this._svgSize || !this._svgSize.equals(size)) { 13424 this._svgSize = size; 13425 container.setAttribute('width', size.x); 13426 container.setAttribute('height', size.y); 13427 } 13428 13429 // movement: update container viewBox so that we don't have to change coordinates of individual layers 13430 setPosition(container, b.min); 13431 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); 13432 13433 this.fire('update'); 13434 }, 13435 13436 // methods below are called by vector layers implementations 13437 13438 _initPath: function (layer) { 13439 var path = layer._path = create$2('path'); 13440 13441 // @namespace Path 13442 // @option className: String = null 13443 // Custom class name set on an element. Only for SVG renderer. 13444 if (layer.options.className) { 13445 addClass(path, layer.options.className); 13446 } 13447 13448 if (layer.options.interactive) { 13449 addClass(path, 'leaflet-interactive'); 13450 } 13451 13452 this._updateStyle(layer); 13453 this._layers[stamp(layer)] = layer; 13454 }, 13455 13456 _addPath: function (layer) { 13457 if (!this._rootGroup) { this._initContainer(); } 13458 this._rootGroup.appendChild(layer._path); 13459 layer.addInteractiveTarget(layer._path); 13460 }, 13461 13462 _removePath: function (layer) { 13463 remove(layer._path); 13464 layer.removeInteractiveTarget(layer._path); 13465 delete this._layers[stamp(layer)]; 13466 }, 13467 13468 _updatePath: function (layer) { 13469 layer._project(); 13470 layer._update(); 13471 }, 13472 13473 _updateStyle: function (layer) { 13474 var path = layer._path, 13475 options = layer.options; 13476 13477 if (!path) { return; } 13478 13479 if (options.stroke) { 13480 path.setAttribute('stroke', options.color); 13481 path.setAttribute('stroke-opacity', options.opacity); 13482 path.setAttribute('stroke-width', options.weight); 13483 path.setAttribute('stroke-linecap', options.lineCap); 13484 path.setAttribute('stroke-linejoin', options.lineJoin); 13485 13486 if (options.dashArray) { 13487 path.setAttribute('stroke-dasharray', options.dashArray); 13488 } else { 13489 path.removeAttribute('stroke-dasharray'); 13490 } 13491 13492 if (options.dashOffset) { 13493 path.setAttribute('stroke-dashoffset', options.dashOffset); 13494 } else { 13495 path.removeAttribute('stroke-dashoffset'); 13496 } 13497 } else { 13498 path.setAttribute('stroke', 'none'); 13499 } 13500 13501 if (options.fill) { 13502 path.setAttribute('fill', options.fillColor || options.color); 13503 path.setAttribute('fill-opacity', options.fillOpacity); 13504 path.setAttribute('fill-rule', options.fillRule || 'evenodd'); 13505 } else { 13506 path.setAttribute('fill', 'none'); 13507 } 13508 }, 13509 13510 _updatePoly: function (layer, closed) { 13511 this._setPath(layer, pointsToPath(layer._parts, closed)); 13512 }, 13513 13514 _updateCircle: function (layer) { 13515 var p = layer._point, 13516 r = Math.max(Math.round(layer._radius), 1), 13517 r2 = Math.max(Math.round(layer._radiusY), 1) || r, 13518 arc = 'a' + r + ',' + r2 + ' 0 1,0 '; 13519 13520 // drawing a circle with two half-arcs 13521 var d = layer._empty() ? 'M0 0' : 13522 'M' + (p.x - r) + ',' + p.y + 13523 arc + (r * 2) + ',0 ' + 13524 arc + (-r * 2) + ',0 '; 13525 13526 this._setPath(layer, d); 13527 }, 13528 13529 _setPath: function (layer, path) { 13530 layer._path.setAttribute('d', path); 13531 }, 13532 13533 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements 13534 _bringToFront: function (layer) { 13535 toFront(layer._path); 13536 }, 13537 13538 _bringToBack: function (layer) { 13539 toBack(layer._path); 13540 } 13541}); 13542 13543if (vml) { 13544 SVG.include(vmlMixin); 13545} 13546 13547// @namespace SVG 13548// @factory L.svg(options?: Renderer options) 13549// Creates a SVG renderer with the given options. 13550function svg$1(options) { 13551 return svg || vml ? new SVG(options) : null; 13552} 13553 13554Map.include({ 13555 // @namespace Map; @method getRenderer(layer: Path): Renderer 13556 // Returns the instance of `Renderer` that should be used to render the given 13557 // `Path`. It will ensure that the `renderer` options of the map and paths 13558 // are respected, and that the renderers do exist on the map. 13559 getRenderer: function (layer) { 13560 // @namespace Path; @option renderer: Renderer 13561 // Use this specific instance of `Renderer` for this path. Takes 13562 // precedence over the map's [default renderer](#map-renderer). 13563 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; 13564 13565 if (!renderer) { 13566 renderer = this._renderer = this._createRenderer(); 13567 } 13568 13569 if (!this.hasLayer(renderer)) { 13570 this.addLayer(renderer); 13571 } 13572 return renderer; 13573 }, 13574 13575 _getPaneRenderer: function (name) { 13576 if (name === 'overlayPane' || name === undefined) { 13577 return false; 13578 } 13579 13580 var renderer = this._paneRenderers[name]; 13581 if (renderer === undefined) { 13582 renderer = this._createRenderer({pane: name}); 13583 this._paneRenderers[name] = renderer; 13584 } 13585 return renderer; 13586 }, 13587 13588 _createRenderer: function (options) { 13589 // @namespace Map; @option preferCanvas: Boolean = false 13590 // Whether `Path`s should be rendered on a `Canvas` renderer. 13591 // By default, all `Path`s are rendered in a `SVG` renderer. 13592 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options); 13593 } 13594}); 13595 13596/* 13597 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. 13598 */ 13599 13600/* 13601 * @class Rectangle 13602 * @aka L.Rectangle 13603 * @inherits Polygon 13604 * 13605 * A class for drawing rectangle overlays on a map. Extends `Polygon`. 13606 * 13607 * @example 13608 * 13609 * ```js 13610 * // define rectangle geographical bounds 13611 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; 13612 * 13613 * // create an orange rectangle 13614 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); 13615 * 13616 * // zoom the map to the rectangle bounds 13617 * map.fitBounds(bounds); 13618 * ``` 13619 * 13620 */ 13621 13622 13623var Rectangle = Polygon.extend({ 13624 initialize: function (latLngBounds, options) { 13625 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); 13626 }, 13627 13628 // @method setBounds(latLngBounds: LatLngBounds): this 13629 // Redraws the rectangle with the passed bounds. 13630 setBounds: function (latLngBounds) { 13631 return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); 13632 }, 13633 13634 _boundsToLatLngs: function (latLngBounds) { 13635 latLngBounds = toLatLngBounds(latLngBounds); 13636 return [ 13637 latLngBounds.getSouthWest(), 13638 latLngBounds.getNorthWest(), 13639 latLngBounds.getNorthEast(), 13640 latLngBounds.getSouthEast() 13641 ]; 13642 } 13643}); 13644 13645 13646// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) 13647function rectangle(latLngBounds, options) { 13648 return new Rectangle(latLngBounds, options); 13649} 13650 13651SVG.create = create$2; 13652SVG.pointsToPath = pointsToPath; 13653 13654GeoJSON.geometryToLayer = geometryToLayer; 13655GeoJSON.coordsToLatLng = coordsToLatLng; 13656GeoJSON.coordsToLatLngs = coordsToLatLngs; 13657GeoJSON.latLngToCoords = latLngToCoords; 13658GeoJSON.latLngsToCoords = latLngsToCoords; 13659GeoJSON.getFeature = getFeature; 13660GeoJSON.asFeature = asFeature; 13661 13662/* 13663 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map 13664 * (zoom to a selected bounding box), enabled by default. 13665 */ 13666 13667// @namespace Map 13668// @section Interaction Options 13669Map.mergeOptions({ 13670 // @option boxZoom: Boolean = true 13671 // Whether the map can be zoomed to a rectangular area specified by 13672 // dragging the mouse while pressing the shift key. 13673 boxZoom: true 13674}); 13675 13676var BoxZoom = Handler.extend({ 13677 initialize: function (map) { 13678 this._map = map; 13679 this._container = map._container; 13680 this._pane = map._panes.overlayPane; 13681 this._resetStateTimeout = 0; 13682 map.on('unload', this._destroy, this); 13683 }, 13684 13685 addHooks: function () { 13686 on(this._container, 'mousedown', this._onMouseDown, this); 13687 }, 13688 13689 removeHooks: function () { 13690 off(this._container, 'mousedown', this._onMouseDown, this); 13691 }, 13692 13693 moved: function () { 13694 return this._moved; 13695 }, 13696 13697 _destroy: function () { 13698 remove(this._pane); 13699 delete this._pane; 13700 }, 13701 13702 _resetState: function () { 13703 this._resetStateTimeout = 0; 13704 this._moved = false; 13705 }, 13706 13707 _clearDeferredResetState: function () { 13708 if (this._resetStateTimeout !== 0) { 13709 clearTimeout(this._resetStateTimeout); 13710 this._resetStateTimeout = 0; 13711 } 13712 }, 13713 13714 _onMouseDown: function (e) { 13715 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } 13716 13717 // Clear the deferred resetState if it hasn't executed yet, otherwise it 13718 // will interrupt the interaction and orphan a box element in the container. 13719 this._clearDeferredResetState(); 13720 this._resetState(); 13721 13722 disableTextSelection(); 13723 disableImageDrag(); 13724 13725 this._startPoint = this._map.mouseEventToContainerPoint(e); 13726 13727 on(document, { 13728 contextmenu: stop, 13729 mousemove: this._onMouseMove, 13730 mouseup: this._onMouseUp, 13731 keydown: this._onKeyDown 13732 }, this); 13733 }, 13734 13735 _onMouseMove: function (e) { 13736 if (!this._moved) { 13737 this._moved = true; 13738 13739 this._box = create$1('div', 'leaflet-zoom-box', this._container); 13740 addClass(this._container, 'leaflet-crosshair'); 13741 13742 this._map.fire('boxzoomstart'); 13743 } 13744 13745 this._point = this._map.mouseEventToContainerPoint(e); 13746 13747 var bounds = new Bounds(this._point, this._startPoint), 13748 size = bounds.getSize(); 13749 13750 setPosition(this._box, bounds.min); 13751 13752 this._box.style.width = size.x + 'px'; 13753 this._box.style.height = size.y + 'px'; 13754 }, 13755 13756 _finish: function () { 13757 if (this._moved) { 13758 remove(this._box); 13759 removeClass(this._container, 'leaflet-crosshair'); 13760 } 13761 13762 enableTextSelection(); 13763 enableImageDrag(); 13764 13765 off(document, { 13766 contextmenu: stop, 13767 mousemove: this._onMouseMove, 13768 mouseup: this._onMouseUp, 13769 keydown: this._onKeyDown 13770 }, this); 13771 }, 13772 13773 _onMouseUp: function (e) { 13774 if ((e.which !== 1) && (e.button !== 1)) { return; } 13775 13776 this._finish(); 13777 13778 if (!this._moved) { return; } 13779 // Postpone to next JS tick so internal click event handling 13780 // still see it as "moved". 13781 this._clearDeferredResetState(); 13782 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0); 13783 13784 var bounds = new LatLngBounds( 13785 this._map.containerPointToLatLng(this._startPoint), 13786 this._map.containerPointToLatLng(this._point)); 13787 13788 this._map 13789 .fitBounds(bounds) 13790 .fire('boxzoomend', {boxZoomBounds: bounds}); 13791 }, 13792 13793 _onKeyDown: function (e) { 13794 if (e.keyCode === 27) { 13795 this._finish(); 13796 } 13797 } 13798}); 13799 13800// @section Handlers 13801// @property boxZoom: Handler 13802// Box (shift-drag with mouse) zoom handler. 13803Map.addInitHook('addHandler', 'boxZoom', BoxZoom); 13804 13805/* 13806 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. 13807 */ 13808 13809// @namespace Map 13810// @section Interaction Options 13811 13812Map.mergeOptions({ 13813 // @option doubleClickZoom: Boolean|String = true 13814 // Whether the map can be zoomed in by double clicking on it and 13815 // zoomed out by double clicking while holding shift. If passed 13816 // `'center'`, double-click zoom will zoom to the center of the 13817 // view regardless of where the mouse was. 13818 doubleClickZoom: true 13819}); 13820 13821var DoubleClickZoom = Handler.extend({ 13822 addHooks: function () { 13823 this._map.on('dblclick', this._onDoubleClick, this); 13824 }, 13825 13826 removeHooks: function () { 13827 this._map.off('dblclick', this._onDoubleClick, this); 13828 }, 13829 13830 _onDoubleClick: function (e) { 13831 var map = this._map, 13832 oldZoom = map.getZoom(), 13833 delta = map.options.zoomDelta, 13834 zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; 13835 13836 if (map.options.doubleClickZoom === 'center') { 13837 map.setZoom(zoom); 13838 } else { 13839 map.setZoomAround(e.containerPoint, zoom); 13840 } 13841 } 13842}); 13843 13844// @section Handlers 13845// 13846// Map properties include interaction handlers that allow you to control 13847// interaction behavior in runtime, enabling or disabling certain features such 13848// as dragging or touch zoom (see `Handler` methods). For example: 13849// 13850// ```js 13851// map.doubleClickZoom.disable(); 13852// ``` 13853// 13854// @property doubleClickZoom: Handler 13855// Double click zoom handler. 13856Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom); 13857 13858/* 13859 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. 13860 */ 13861 13862// @namespace Map 13863// @section Interaction Options 13864Map.mergeOptions({ 13865 // @option dragging: Boolean = true 13866 // Whether the map be draggable with mouse/touch or not. 13867 dragging: true, 13868 13869 // @section Panning Inertia Options 13870 // @option inertia: Boolean = * 13871 // If enabled, panning of the map will have an inertia effect where 13872 // the map builds momentum while dragging and continues moving in 13873 // the same direction for some time. Feels especially nice on touch 13874 // devices. Enabled by default unless running on old Android devices. 13875 inertia: !android23, 13876 13877 // @option inertiaDeceleration: Number = 3000 13878 // The rate with which the inertial movement slows down, in pixels/second². 13879 inertiaDeceleration: 3400, // px/s^2 13880 13881 // @option inertiaMaxSpeed: Number = Infinity 13882 // Max speed of the inertial movement, in pixels/second. 13883 inertiaMaxSpeed: Infinity, // px/s 13884 13885 // @option easeLinearity: Number = 0.2 13886 easeLinearity: 0.2, 13887 13888 // TODO refactor, move to CRS 13889 // @option worldCopyJump: Boolean = false 13890 // With this option enabled, the map tracks when you pan to another "copy" 13891 // of the world and seamlessly jumps to the original one so that all overlays 13892 // like markers and vector layers are still visible. 13893 worldCopyJump: false, 13894 13895 // @option maxBoundsViscosity: Number = 0.0 13896 // If `maxBounds` is set, this option will control how solid the bounds 13897 // are when dragging the map around. The default value of `0.0` allows the 13898 // user to drag outside the bounds at normal speed, higher values will 13899 // slow down map dragging outside bounds, and `1.0` makes the bounds fully 13900 // solid, preventing the user from dragging outside the bounds. 13901 maxBoundsViscosity: 0.0 13902}); 13903 13904var Drag = Handler.extend({ 13905 addHooks: function () { 13906 if (!this._draggable) { 13907 var map = this._map; 13908 13909 this._draggable = new Draggable(map._mapPane, map._container); 13910 13911 this._draggable.on({ 13912 dragstart: this._onDragStart, 13913 drag: this._onDrag, 13914 dragend: this._onDragEnd 13915 }, this); 13916 13917 this._draggable.on('predrag', this._onPreDragLimit, this); 13918 if (map.options.worldCopyJump) { 13919 this._draggable.on('predrag', this._onPreDragWrap, this); 13920 map.on('zoomend', this._onZoomEnd, this); 13921 13922 map.whenReady(this._onZoomEnd, this); 13923 } 13924 } 13925 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag'); 13926 this._draggable.enable(); 13927 this._positions = []; 13928 this._times = []; 13929 }, 13930 13931 removeHooks: function () { 13932 removeClass(this._map._container, 'leaflet-grab'); 13933 removeClass(this._map._container, 'leaflet-touch-drag'); 13934 this._draggable.disable(); 13935 }, 13936 13937 moved: function () { 13938 return this._draggable && this._draggable._moved; 13939 }, 13940 13941 moving: function () { 13942 return this._draggable && this._draggable._moving; 13943 }, 13944 13945 _onDragStart: function () { 13946 var map = this._map; 13947 13948 map._stop(); 13949 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { 13950 var bounds = toLatLngBounds(this._map.options.maxBounds); 13951 13952 this._offsetLimit = toBounds( 13953 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), 13954 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1) 13955 .add(this._map.getSize())); 13956 13957 this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity)); 13958 } else { 13959 this._offsetLimit = null; 13960 } 13961 13962 map 13963 .fire('movestart') 13964 .fire('dragstart'); 13965 13966 if (map.options.inertia) { 13967 this._positions = []; 13968 this._times = []; 13969 } 13970 }, 13971 13972 _onDrag: function (e) { 13973 if (this._map.options.inertia) { 13974 var time = this._lastTime = +new Date(), 13975 pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; 13976 13977 this._positions.push(pos); 13978 this._times.push(time); 13979 13980 this._prunePositions(time); 13981 } 13982 13983 this._map 13984 .fire('move', e) 13985 .fire('drag', e); 13986 }, 13987 13988 _prunePositions: function (time) { 13989 while (this._positions.length > 1 && time - this._times[0] > 50) { 13990 this._positions.shift(); 13991 this._times.shift(); 13992 } 13993 }, 13994 13995 _onZoomEnd: function () { 13996 var pxCenter = this._map.getSize().divideBy(2), 13997 pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); 13998 13999 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; 14000 this._worldWidth = this._map.getPixelWorldBounds().getSize().x; 14001 }, 14002 14003 _viscousLimit: function (value, threshold) { 14004 return value - (value - threshold) * this._viscosity; 14005 }, 14006 14007 _onPreDragLimit: function () { 14008 if (!this._viscosity || !this._offsetLimit) { return; } 14009 14010 var offset = this._draggable._newPos.subtract(this._draggable._startPos); 14011 14012 var limit = this._offsetLimit; 14013 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); } 14014 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); } 14015 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); } 14016 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); } 14017 14018 this._draggable._newPos = this._draggable._startPos.add(offset); 14019 }, 14020 14021 _onPreDragWrap: function () { 14022 // TODO refactor to be able to adjust map pane position after zoom 14023 var worldWidth = this._worldWidth, 14024 halfWidth = Math.round(worldWidth / 2), 14025 dx = this._initialWorldOffset, 14026 x = this._draggable._newPos.x, 14027 newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, 14028 newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, 14029 newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; 14030 14031 this._draggable._absPos = this._draggable._newPos.clone(); 14032 this._draggable._newPos.x = newX; 14033 }, 14034 14035 _onDragEnd: function (e) { 14036 var map = this._map, 14037 options = map.options, 14038 14039 noInertia = !options.inertia || this._times.length < 2; 14040 14041 map.fire('dragend', e); 14042 14043 if (noInertia) { 14044 map.fire('moveend'); 14045 14046 } else { 14047 this._prunePositions(+new Date()); 14048 14049 var direction = this._lastPos.subtract(this._positions[0]), 14050 duration = (this._lastTime - this._times[0]) / 1000, 14051 ease = options.easeLinearity, 14052 14053 speedVector = direction.multiplyBy(ease / duration), 14054 speed = speedVector.distanceTo([0, 0]), 14055 14056 limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), 14057 limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), 14058 14059 decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), 14060 offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); 14061 14062 if (!offset.x && !offset.y) { 14063 map.fire('moveend'); 14064 14065 } else { 14066 offset = map._limitOffset(offset, map.options.maxBounds); 14067 14068 requestAnimFrame(function () { 14069 map.panBy(offset, { 14070 duration: decelerationDuration, 14071 easeLinearity: ease, 14072 noMoveStart: true, 14073 animate: true 14074 }); 14075 }); 14076 } 14077 } 14078 } 14079}); 14080 14081// @section Handlers 14082// @property dragging: Handler 14083// Map dragging handler (by both mouse and touch). 14084Map.addInitHook('addHandler', 'dragging', Drag); 14085 14086/* 14087 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. 14088 */ 14089 14090// @namespace Map 14091// @section Keyboard Navigation Options 14092Map.mergeOptions({ 14093 // @option keyboard: Boolean = true 14094 // Makes the map focusable and allows users to navigate the map with keyboard 14095 // arrows and `+`/`-` keys. 14096 keyboard: true, 14097 14098 // @option keyboardPanDelta: Number = 80 14099 // Amount of pixels to pan when pressing an arrow key. 14100 keyboardPanDelta: 80 14101}); 14102 14103var Keyboard = Handler.extend({ 14104 14105 keyCodes: { 14106 left: [37], 14107 right: [39], 14108 down: [40], 14109 up: [38], 14110 zoomIn: [187, 107, 61, 171], 14111 zoomOut: [189, 109, 54, 173] 14112 }, 14113 14114 initialize: function (map) { 14115 this._map = map; 14116 14117 this._setPanDelta(map.options.keyboardPanDelta); 14118 this._setZoomDelta(map.options.zoomDelta); 14119 }, 14120 14121 addHooks: function () { 14122 var container = this._map._container; 14123 14124 // make the container focusable by tabbing 14125 if (container.tabIndex <= 0) { 14126 container.tabIndex = '0'; 14127 } 14128 14129 on(container, { 14130 focus: this._onFocus, 14131 blur: this._onBlur, 14132 mousedown: this._onMouseDown 14133 }, this); 14134 14135 this._map.on({ 14136 focus: this._addHooks, 14137 blur: this._removeHooks 14138 }, this); 14139 }, 14140 14141 removeHooks: function () { 14142 this._removeHooks(); 14143 14144 off(this._map._container, { 14145 focus: this._onFocus, 14146 blur: this._onBlur, 14147 mousedown: this._onMouseDown 14148 }, this); 14149 14150 this._map.off({ 14151 focus: this._addHooks, 14152 blur: this._removeHooks 14153 }, this); 14154 }, 14155 14156 _onMouseDown: function () { 14157 if (this._focused) { return; } 14158 14159 var body = document.body, 14160 docEl = document.documentElement, 14161 top = body.scrollTop || docEl.scrollTop, 14162 left = body.scrollLeft || docEl.scrollLeft; 14163 14164 this._map._container.focus(); 14165 14166 window.scrollTo(left, top); 14167 }, 14168 14169 _onFocus: function () { 14170 this._focused = true; 14171 this._map.fire('focus'); 14172 }, 14173 14174 _onBlur: function () { 14175 this._focused = false; 14176 this._map.fire('blur'); 14177 }, 14178 14179 _setPanDelta: function (panDelta) { 14180 var keys = this._panKeys = {}, 14181 codes = this.keyCodes, 14182 i, len; 14183 14184 for (i = 0, len = codes.left.length; i < len; i++) { 14185 keys[codes.left[i]] = [-1 * panDelta, 0]; 14186 } 14187 for (i = 0, len = codes.right.length; i < len; i++) { 14188 keys[codes.right[i]] = [panDelta, 0]; 14189 } 14190 for (i = 0, len = codes.down.length; i < len; i++) { 14191 keys[codes.down[i]] = [0, panDelta]; 14192 } 14193 for (i = 0, len = codes.up.length; i < len; i++) { 14194 keys[codes.up[i]] = [0, -1 * panDelta]; 14195 } 14196 }, 14197 14198 _setZoomDelta: function (zoomDelta) { 14199 var keys = this._zoomKeys = {}, 14200 codes = this.keyCodes, 14201 i, len; 14202 14203 for (i = 0, len = codes.zoomIn.length; i < len; i++) { 14204 keys[codes.zoomIn[i]] = zoomDelta; 14205 } 14206 for (i = 0, len = codes.zoomOut.length; i < len; i++) { 14207 keys[codes.zoomOut[i]] = -zoomDelta; 14208 } 14209 }, 14210 14211 _addHooks: function () { 14212 on(document, 'keydown', this._onKeyDown, this); 14213 }, 14214 14215 _removeHooks: function () { 14216 off(document, 'keydown', this._onKeyDown, this); 14217 }, 14218 14219 _onKeyDown: function (e) { 14220 if (e.altKey || e.ctrlKey || e.metaKey) { return; } 14221 14222 var key = e.keyCode, 14223 map = this._map, 14224 offset; 14225 14226 if (key in this._panKeys) { 14227 if (!map._panAnim || !map._panAnim._inProgress) { 14228 offset = this._panKeys[key]; 14229 if (e.shiftKey) { 14230 offset = toPoint(offset).multiplyBy(3); 14231 } 14232 14233 map.panBy(offset); 14234 14235 if (map.options.maxBounds) { 14236 map.panInsideBounds(map.options.maxBounds); 14237 } 14238 } 14239 } else if (key in this._zoomKeys) { 14240 map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]); 14241 14242 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) { 14243 map.closePopup(); 14244 14245 } else { 14246 return; 14247 } 14248 14249 stop(e); 14250 } 14251}); 14252 14253// @section Handlers 14254// @section Handlers 14255// @property keyboard: Handler 14256// Keyboard navigation handler. 14257Map.addInitHook('addHandler', 'keyboard', Keyboard); 14258 14259/* 14260 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. 14261 */ 14262 14263// @namespace Map 14264// @section Interaction Options 14265Map.mergeOptions({ 14266 // @section Mousewheel options 14267 // @option scrollWheelZoom: Boolean|String = true 14268 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, 14269 // it will zoom to the center of the view regardless of where the mouse was. 14270 scrollWheelZoom: true, 14271 14272 // @option wheelDebounceTime: Number = 40 14273 // Limits the rate at which a wheel can fire (in milliseconds). By default 14274 // user can't zoom via wheel more often than once per 40 ms. 14275 wheelDebounceTime: 40, 14276 14277 // @option wheelPxPerZoomLevel: Number = 60 14278 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) 14279 // mean a change of one full zoom level. Smaller values will make wheel-zooming 14280 // faster (and vice versa). 14281 wheelPxPerZoomLevel: 60 14282}); 14283 14284var ScrollWheelZoom = Handler.extend({ 14285 addHooks: function () { 14286 on(this._map._container, 'mousewheel', this._onWheelScroll, this); 14287 14288 this._delta = 0; 14289 }, 14290 14291 removeHooks: function () { 14292 off(this._map._container, 'mousewheel', this._onWheelScroll, this); 14293 }, 14294 14295 _onWheelScroll: function (e) { 14296 var delta = getWheelDelta(e); 14297 14298 var debounce = this._map.options.wheelDebounceTime; 14299 14300 this._delta += delta; 14301 this._lastMousePos = this._map.mouseEventToContainerPoint(e); 14302 14303 if (!this._startTime) { 14304 this._startTime = +new Date(); 14305 } 14306 14307 var left = Math.max(debounce - (+new Date() - this._startTime), 0); 14308 14309 clearTimeout(this._timer); 14310 this._timer = setTimeout(bind(this._performZoom, this), left); 14311 14312 stop(e); 14313 }, 14314 14315 _performZoom: function () { 14316 var map = this._map, 14317 zoom = map.getZoom(), 14318 snap = this._map.options.zoomSnap || 0; 14319 14320 map._stop(); // stop panning and fly animations if any 14321 14322 // map the delta with a sigmoid function to -4..4 range leaning on -1..1 14323 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), 14324 d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, 14325 d4 = snap ? Math.ceil(d3 / snap) * snap : d3, 14326 delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom; 14327 14328 this._delta = 0; 14329 this._startTime = null; 14330 14331 if (!delta) { return; } 14332 14333 if (map.options.scrollWheelZoom === 'center') { 14334 map.setZoom(zoom + delta); 14335 } else { 14336 map.setZoomAround(this._lastMousePos, zoom + delta); 14337 } 14338 } 14339}); 14340 14341// @section Handlers 14342// @property scrollWheelZoom: Handler 14343// Scroll wheel zoom handler. 14344Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom); 14345 14346/* 14347 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. 14348 */ 14349 14350// @namespace Map 14351// @section Interaction Options 14352Map.mergeOptions({ 14353 // @section Touch interaction options 14354 // @option tap: Boolean = true 14355 // Enables mobile hacks for supporting instant taps (fixing 200ms click 14356 // delay on iOS/Android) and touch holds (fired as `contextmenu` events). 14357 tap: true, 14358 14359 // @option tapTolerance: Number = 15 14360 // The max number of pixels a user can shift his finger during touch 14361 // for it to be considered a valid tap. 14362 tapTolerance: 15 14363}); 14364 14365var Tap = Handler.extend({ 14366 addHooks: function () { 14367 on(this._map._container, 'touchstart', this._onDown, this); 14368 }, 14369 14370 removeHooks: function () { 14371 off(this._map._container, 'touchstart', this._onDown, this); 14372 }, 14373 14374 _onDown: function (e) { 14375 if (!e.touches) { return; } 14376 14377 preventDefault(e); 14378 14379 this._fireClick = true; 14380 14381 // don't simulate click or track longpress if more than 1 touch 14382 if (e.touches.length > 1) { 14383 this._fireClick = false; 14384 clearTimeout(this._holdTimeout); 14385 return; 14386 } 14387 14388 var first = e.touches[0], 14389 el = first.target; 14390 14391 this._startPos = this._newPos = new Point(first.clientX, first.clientY); 14392 14393 // if touching a link, highlight it 14394 if (el.tagName && el.tagName.toLowerCase() === 'a') { 14395 addClass(el, 'leaflet-active'); 14396 } 14397 14398 // simulate long hold but setting a timeout 14399 this._holdTimeout = setTimeout(bind(function () { 14400 if (this._isTapValid()) { 14401 this._fireClick = false; 14402 this._onUp(); 14403 this._simulateEvent('contextmenu', first); 14404 } 14405 }, this), 1000); 14406 14407 this._simulateEvent('mousedown', first); 14408 14409 on(document, { 14410 touchmove: this._onMove, 14411 touchend: this._onUp 14412 }, this); 14413 }, 14414 14415 _onUp: function (e) { 14416 clearTimeout(this._holdTimeout); 14417 14418 off(document, { 14419 touchmove: this._onMove, 14420 touchend: this._onUp 14421 }, this); 14422 14423 if (this._fireClick && e && e.changedTouches) { 14424 14425 var first = e.changedTouches[0], 14426 el = first.target; 14427 14428 if (el && el.tagName && el.tagName.toLowerCase() === 'a') { 14429 removeClass(el, 'leaflet-active'); 14430 } 14431 14432 this._simulateEvent('mouseup', first); 14433 14434 // simulate click if the touch didn't move too much 14435 if (this._isTapValid()) { 14436 this._simulateEvent('click', first); 14437 } 14438 } 14439 }, 14440 14441 _isTapValid: function () { 14442 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; 14443 }, 14444 14445 _onMove: function (e) { 14446 var first = e.touches[0]; 14447 this._newPos = new Point(first.clientX, first.clientY); 14448 this._simulateEvent('mousemove', first); 14449 }, 14450 14451 _simulateEvent: function (type, e) { 14452 var simulatedEvent = document.createEvent('MouseEvents'); 14453 14454 simulatedEvent._simulated = true; 14455 e.target._simulatedClick = true; 14456 14457 simulatedEvent.initMouseEvent( 14458 type, true, true, window, 1, 14459 e.screenX, e.screenY, 14460 e.clientX, e.clientY, 14461 false, false, false, false, 0, null); 14462 14463 e.target.dispatchEvent(simulatedEvent); 14464 } 14465}); 14466 14467// @section Handlers 14468// @property tap: Handler 14469// Mobile touch hacks (quick tap and touch hold) handler. 14470if (touch && !pointer) { 14471 Map.addInitHook('addHandler', 'tap', Tap); 14472} 14473 14474/* 14475 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers. 14476 */ 14477 14478// @namespace Map 14479// @section Interaction Options 14480Map.mergeOptions({ 14481 // @section Touch interaction options 14482 // @option touchZoom: Boolean|String = * 14483 // Whether the map can be zoomed by touch-dragging with two fingers. If 14484 // passed `'center'`, it will zoom to the center of the view regardless of 14485 // where the touch events (fingers) were. Enabled for touch-capable web 14486 // browsers except for old Androids. 14487 touchZoom: touch && !android23, 14488 14489 // @option bounceAtZoomLimits: Boolean = true 14490 // Set it to false if you don't want the map to zoom beyond min/max zoom 14491 // and then bounce back when pinch-zooming. 14492 bounceAtZoomLimits: true 14493}); 14494 14495var TouchZoom = Handler.extend({ 14496 addHooks: function () { 14497 addClass(this._map._container, 'leaflet-touch-zoom'); 14498 on(this._map._container, 'touchstart', this._onTouchStart, this); 14499 }, 14500 14501 removeHooks: function () { 14502 removeClass(this._map._container, 'leaflet-touch-zoom'); 14503 off(this._map._container, 'touchstart', this._onTouchStart, this); 14504 }, 14505 14506 _onTouchStart: function (e) { 14507 var map = this._map; 14508 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } 14509 14510 var p1 = map.mouseEventToContainerPoint(e.touches[0]), 14511 p2 = map.mouseEventToContainerPoint(e.touches[1]); 14512 14513 this._centerPoint = map.getSize()._divideBy(2); 14514 this._startLatLng = map.containerPointToLatLng(this._centerPoint); 14515 if (map.options.touchZoom !== 'center') { 14516 this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); 14517 } 14518 14519 this._startDist = p1.distanceTo(p2); 14520 this._startZoom = map.getZoom(); 14521 14522 this._moved = false; 14523 this._zooming = true; 14524 14525 map._stop(); 14526 14527 on(document, 'touchmove', this._onTouchMove, this); 14528 on(document, 'touchend', this._onTouchEnd, this); 14529 14530 preventDefault(e); 14531 }, 14532 14533 _onTouchMove: function (e) { 14534 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } 14535 14536 var map = this._map, 14537 p1 = map.mouseEventToContainerPoint(e.touches[0]), 14538 p2 = map.mouseEventToContainerPoint(e.touches[1]), 14539 scale = p1.distanceTo(p2) / this._startDist; 14540 14541 this._zoom = map.getScaleZoom(scale, this._startZoom); 14542 14543 if (!map.options.bounceAtZoomLimits && ( 14544 (this._zoom < map.getMinZoom() && scale < 1) || 14545 (this._zoom > map.getMaxZoom() && scale > 1))) { 14546 this._zoom = map._limitZoom(this._zoom); 14547 } 14548 14549 if (map.options.touchZoom === 'center') { 14550 this._center = this._startLatLng; 14551 if (scale === 1) { return; } 14552 } else { 14553 // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng 14554 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); 14555 if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } 14556 this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom); 14557 } 14558 14559 if (!this._moved) { 14560 map._moveStart(true, false); 14561 this._moved = true; 14562 } 14563 14564 cancelAnimFrame(this._animRequest); 14565 14566 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}); 14567 this._animRequest = requestAnimFrame(moveFn, this, true); 14568 14569 preventDefault(e); 14570 }, 14571 14572 _onTouchEnd: function () { 14573 if (!this._moved || !this._zooming) { 14574 this._zooming = false; 14575 return; 14576 } 14577 14578 this._zooming = false; 14579 cancelAnimFrame(this._animRequest); 14580 14581 off(document, 'touchmove', this._onTouchMove); 14582 off(document, 'touchend', this._onTouchEnd); 14583 14584 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate. 14585 if (this._map.options.zoomAnimation) { 14586 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); 14587 } else { 14588 this._map._resetView(this._center, this._map._limitZoom(this._zoom)); 14589 } 14590 } 14591}); 14592 14593// @section Handlers 14594// @property touchZoom: Handler 14595// Touch zoom handler. 14596Map.addInitHook('addHandler', 'touchZoom', TouchZoom); 14597 14598Map.BoxZoom = BoxZoom; 14599Map.DoubleClickZoom = DoubleClickZoom; 14600Map.Drag = Drag; 14601Map.Keyboard = Keyboard; 14602Map.ScrollWheelZoom = ScrollWheelZoom; 14603Map.Tap = Tap; 14604Map.TouchZoom = TouchZoom; 14605 14606Object.freeze = freeze; 14607 14608exports.version = version; 14609exports.Control = Control; 14610exports.control = control; 14611exports.Browser = Browser; 14612exports.Evented = Evented; 14613exports.Mixin = Mixin; 14614exports.Util = Util; 14615exports.Class = Class; 14616exports.Handler = Handler; 14617exports.extend = extend; 14618exports.bind = bind; 14619exports.stamp = stamp; 14620exports.setOptions = setOptions; 14621exports.DomEvent = DomEvent; 14622exports.DomUtil = DomUtil; 14623exports.PosAnimation = PosAnimation; 14624exports.Draggable = Draggable; 14625exports.LineUtil = LineUtil; 14626exports.PolyUtil = PolyUtil; 14627exports.Point = Point; 14628exports.point = toPoint; 14629exports.Bounds = Bounds; 14630exports.bounds = toBounds; 14631exports.Transformation = Transformation; 14632exports.transformation = toTransformation; 14633exports.Projection = index; 14634exports.LatLng = LatLng; 14635exports.latLng = toLatLng; 14636exports.LatLngBounds = LatLngBounds; 14637exports.latLngBounds = toLatLngBounds; 14638exports.CRS = CRS; 14639exports.GeoJSON = GeoJSON; 14640exports.geoJSON = geoJSON; 14641exports.geoJson = geoJson; 14642exports.Layer = Layer; 14643exports.LayerGroup = LayerGroup; 14644exports.layerGroup = layerGroup; 14645exports.FeatureGroup = FeatureGroup; 14646exports.featureGroup = featureGroup; 14647exports.ImageOverlay = ImageOverlay; 14648exports.imageOverlay = imageOverlay; 14649exports.VideoOverlay = VideoOverlay; 14650exports.videoOverlay = videoOverlay; 14651exports.SVGOverlay = SVGOverlay; 14652exports.svgOverlay = svgOverlay; 14653exports.DivOverlay = DivOverlay; 14654exports.Popup = Popup; 14655exports.popup = popup; 14656exports.Tooltip = Tooltip; 14657exports.tooltip = tooltip; 14658exports.Icon = Icon; 14659exports.icon = icon; 14660exports.DivIcon = DivIcon; 14661exports.divIcon = divIcon; 14662exports.Marker = Marker; 14663exports.marker = marker; 14664exports.TileLayer = TileLayer; 14665exports.tileLayer = tileLayer; 14666exports.GridLayer = GridLayer; 14667exports.gridLayer = gridLayer; 14668exports.SVG = SVG; 14669exports.svg = svg$1; 14670exports.Renderer = Renderer; 14671exports.Canvas = Canvas; 14672exports.canvas = canvas$1; 14673exports.Path = Path; 14674exports.CircleMarker = CircleMarker; 14675exports.circleMarker = circleMarker; 14676exports.Circle = Circle; 14677exports.circle = circle; 14678exports.Polyline = Polyline; 14679exports.polyline = polyline; 14680exports.Polygon = Polygon; 14681exports.polygon = polygon; 14682exports.Rectangle = Rectangle; 14683exports.rectangle = rectangle; 14684exports.Map = Map; 14685exports.map = createMap; 14686 14687var oldL = window.L; 14688exports.noConflict = function() { 14689 window.L = oldL; 14690 return this; 14691} 14692 14693// Always export us to window global (see #2364) 14694window.L = exports; 14695 14696}))); 14697//# sourceMappingURL=leaflet-src.js.map 14698 14699 14700/***/ })
vendor: 4,409 bytes, lines 14700-14775
14700 14701 14702/******/ }); 14703/************************************************************************/ 14704/******/ // The module cache 14705/******/ var __webpack_module_cache__ = {}; 14706/******/ 14707/******/ // The require function 14708/******/ function __webpack_require__(moduleId) { 14709/******/ // Check if module is in cache 14710/******/ if(__webpack_module_cache__[moduleId]) { 14711/******/ return __webpack_module_cache__[moduleId].exports; 14712/******/ } 14713/******/ // Create a new module (and put it into the cache) 14714/******/ var module = __webpack_module_cache__[moduleId] = { 14715/******/ // no module.id needed 14716/******/ // no module.loaded needed 14717/******/ exports: {} 14718/******/ }; 14719/******/ 14720/******/ // Execute the module function 14721/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); 14722/******/ 14723/******/ // Return the exports of the module 14724/******/ return module.exports; 14725/******/ } 14726/******/ 14727/************************************************************************/ 14728/******/ /* webpack/runtime/compat get default export */ 14729/******/ (() => { 14730/******/ // getDefaultExport function for compatibility with non-harmony modules 14731/******/ __webpack_require__.n = (module) => { 14732/******/ var getter = module && module.__esModule ? 14733/******/ () => module['default'] : 14734/******/ () => module; 14735/******/ __webpack_require__.d(getter, { a: getter }); 14736/******/ return getter; 14737/******/ }; 14738/******/ })(); 14739/******/ 14740/******/ /* webpack/runtime/define property getters */ 14741/******/ (() => { 14742/******/ // define getter functions for harmony exports 14743/******/ __webpack_require__.d = (exports, definition) => { 14744/******/ for(var key in definition) { 14745/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 14746/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 14747/******/ } 14748/******/ } 14749/******/ }; 14750/******/ })(); 14751/******/ 14752/******/ /* webpack/runtime/hasOwnProperty shorthand */ 14753/******/ (() => { 14754/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop) 14755/******/ })(); 14756/******/ 14757/******/ /* webpack/runtime/make namespace object */ 14758/******/ (() => { 14759/******/ // define __esModule on exports 14760/******/ __webpack_require__.r = (exports) => { 14761/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 14762/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 14763/******/ } 14764/******/ Object.defineProperty(exports, '__esModule', { value: true }); 14765/******/ }; 14766/******/ })(); 14767/******/ 14768/************************************************************************/ 14769/******/ // startup 14770/******/ // Load entry module 14771/******/ __webpack_require__("./src/index.js"); 14772/******/ // This entry module used 'exports' so it can't be inlined 14773/******/ })() 14774; 14775//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9wc3QvLi9zcmMvaW5kZXguanMiLCJ3ZWJwYWNrOi8vcHN0Ly4vbm9kZV9tb2R1bGVzL2RheWpzL2RheWpzLm1pbi5qcyIsIndlYnBhY2s6Ly9wc3QvLi9ub2RlX21vZHVsZXMvZGF5anMvbG9jYWxlL2VzLmpzIiwid2VicGFjazovL3BzdC8uL25vZGVfbW9kdWxlcy9kYXlqcy9wbHVnaW4vcmVsYXRpdmVUaW1lLmpzIiwid2VicGFjazovL3BzdC8uL25vZGVfbW9kdWxlcy9sZWFmbGV0LWZ1bGxzY3JlZW4vZGlzdC9MZWFmbGV0LmZ1bGxzY3JlZW4uanMiLCJ3ZWJwYWNrOi8vcHN0Ly4vbm9kZV9tb2R1bGVzL2xlYWZsZXQvZGlzdC9sZWFmbGV0LXNyYy5qcyIsIndlYnBhY2s6Ly9wc3Qvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vcHN0L3dlYnBhY2svcnVudGltZS9jb21wYXQgZ2V0IGRlZmF1bHQgZXhwb3J0Iiwid2VicGFjazovL3BzdC93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vcHN0L3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vcHN0L3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vcHN0L3dlYnBhY2svc3RhcnR1cCJdLCJuYW1lcyI6WyJyZXF1aXJlIiwiZGF5anMiLCJyZWxhdGl2ZVRpbWUiLCJkYXlqc19lcyIsImV4dGVuZCIsImN1cnJlbnRfbGFuZyIsImRvY3VtZW50IiwicXVlcnlTZWxlY3RvciIsImxhbmciLCJzcGxpdCIsImxvY2FsZSIsIm1hcCIsIm1hcF9zZXR0aW5ncyIsIm1pblpvb20iLCJmdWxsc2NyZWVuQ29udHJvbCIsInNjcm9sbFdoZWVsWm9vbSIsIm1hcF9jZW50ZXIiLCJsYXRsb24iLCJ6b29tIiwibWFwX2xheWVyIiwidGlsZXMiLCJhdHRyIiwiY2F0ZWdvcmllcyIsImN1cnJlbnRfcG9zdCIsImZpbHRyYWJsZSIsIm1hcmtlcnMiLCJsaXN0IiwidW5zZWxlY3RfYWxsIiwiZm9yRWFjaCIsImkiLCJjbGFzc0xpc3QiLCJyZW1vdmUiLCJpbml0X21haW5fbWFwIiwiTCIsIm9uIiwic2V0VmlldyIsImF0dHJpYnV0aW9uIiwiYWRkVG8iLCJmZXRjaF9wb3N0cyIsImZldGNoIiwidGhlbiIsImRh
14775dGEiLCJqc29uIiwicG9zdHMiLCJ0ZW1wbGF0ZV9ub2RlIiwidGVtcGxhdGUiLCJpbXBvcnROb2RlIiwiY29udGVudCIsInBvc3QiLCJpZCIsImFkZCIsImNhdCIsInZhbHVlIiwidGhtIiwiZGF0YXNldCIsImltZyIsInNldEF0dHJpYnV0ZSIsImlubmVySFRNTCIsImxhYmVsIiwidGl0IiwiZGF0IiwiZnJvbU5vdyIsImFkZEV2ZW50TGlzdGVuZXIiLCJsb2NhdGlvbiIsImhhc2giLCJsb2FkX3Bvc3QiLCJhcHBlbmRDaGlsZCIsInBvcyIsImxhdCIsImxuZyIsImljb24iLCJ0eXBlIiwiaWNvblNpemUiLCJpY29uQW5jaG9yIiwicG9wdXBBbmNob3IiLCJsYXllciIsImNsYXNzTmFtZSIsImh0bWwiLCJlIiwicXVlcnlTZWxlY3RvckFsbCIsInRhcmdldCIsIl9pY29uIiwid2luZG93Iiwic3Vic3RyaW5nIiwibGVuZ3RoIiwic2hvd19jYXNlIiwiYm9keSIsImdldENlbnRlciIsImdldFpvb20iLCJpbnZhbGlkYXRlU2l6ZSIsIm1vZGFsIiwidXJsIiwiZXNjYXBlIiwiaHJlZiIsInRpdGxlIiwic3VtbWFyeSIsImhvd3RvIiwidXBkYXRlIiwibGlua3MiLCJzZWVtb3JlIiwibGluayIsImxpbmtfbWFya3VwIiwiZmlsZV9tYXJrdXAiLCJmaWxlIiwiY29udGFpbnMiLCJ1bnNlbGVjdF9pdGVtcyIsInNjcm9sbEludG9WaWV3IiwiY2hlY2tfaXRlbXMiLCJpdGVtIiwiY2xhc3NlcyIsImNhdGVnb3J5IiwiaXNfdmlzaWJsZSIsIk9iamVjdCIsImtleXMiLCJ0ZXJtcyIsImluaXRfbGVnZW5kIiwibGVnZW5kX2l0ZW1zIiwibGVnZW5kX3Jlc2V0IiwibGVnZW5kX2l0ZW0iLCJwYXJlbnROb2RlIiwiY2F0ZWdvcnlfdGVybXMiLCJ0ZXJtX2lkIiwic2VsZWN0ZWQiLCJ0ZXJtIiwidG9nZ2xlIiwiaW5pdF9zZWFyY2giLCJxIiwibm90X3F1ZXJpZWQiLCJpbml0X2Fib3V0Il0sIm1hcHBpbmd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UFDYjtBQUNBLGFBQWE7QUFDYjtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQSxhQUFhO0FBQ2I7QUFDQSxhQUFhO0FBQ2I7QUFDQSxhQUFhO0FBQ2I7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBOztBQUVBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLOztBQUVMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0EsS0FBSztBQUNMO0FBQ0EsS0FBSztBQUNMO0FBQ0E7O0F
14775BRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsU0FBUzs7QUFFVDtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7O0FDdkpBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsQ0FBQyxLQUE0RDtBQUM3RCxDQUFDLENBQzBCO0FBQzNCLENBQUMsNEJBQTRCOztBQUU3Qjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsZ0NBQWdDLFlBQVk7O0FBRTVDO0FBQ0E7QUFDQTtBQUNBOztBQUVBLG9DQUFvQyxTQUFTO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0Esb0JBQW9CLGNBQWM7O0FBRWxDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDBEQUEwRCxtQkFBbUI7QUFDN0U7QUFDQTtBQU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FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx3Q0FBd0MscUJBQXFCO0FBQzdEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxVQUFVLFFBQVE7O0FBRWxCOztBQUVBLHFDQUFxQyxTQUFTO0FBQzlDO0FBQ0E7QUFDQT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E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhLGNBQWM7O0FBRTNCOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7OztBQUlBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5Q0FBeUMseUJBQXlCLE9BQU8sc0NBQXNDOztBQUUvRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLGFBQWE7O0FBRW5DO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCO0FBQ3JCOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0NBQXdDO0FBQ3hDO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUNBQXFDO0FBQ3JDO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQU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tBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsbUVBQW1FLGVBQWU7QUFDbEYsK0RBQStELGVBQWU7QUFDOUU7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1Q0FBdUMsU0FBUztBQUNoRDtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0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tBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUEsZ0JBQWdCLGFBQWE7O0FBRTdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLDBCQUEwQixRQUFROztBQUVsQztBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSw2REFBNkQ7QUFDN0Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGLHFDQUFxQztBQUNyQzs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSx5REFBeUQsWUFBWTtBQUNyRTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7O0FBR0Y7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBOztBQUVBO0FBQ0EsMkJBQTJCLHlCQUF5QjtBQUNwRCwwQkFBMEIscURBQXFEO0FBQy9FOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxjQUFjO0FBQzdELEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSx3Q0FBd0MsY0FBYztBQUN0RCxFQUFFOztBQUVGOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLG9DQUFvQztBQUNwQywyQ0FBMkMsYUFBYTtBQUN4RCxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBLG9CQUFvQix5Q0FBeUM7QUFDN0Qsb0JBQW9CLHlDQUF5QztBQUM3RCxvQkFBb0IsMEJBQTBCOztBQUU5Qzs7QUFFQSxpQkFBaUIsNkNBQTZDO0FBQzlELGlCQUFpQiwrREFBK0Q7O0FBRWhGLHVCQUF1QixpQ0FBaUM7O0FBRXhEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxNQUFNLFlBQVk7O0FBRWxCLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixhQUFhOztBQUVuQztBQUNBO0FBQ0E7QUFDQSxHQUFHLHNCQUFzQixjQUFjOztBQUV2QztBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsK0JBQStCLGFBQWE7O0FBRTVDO0FBQ0E7O0FBRUEsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSCxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLGlFQUFpRSxZQUFZO0FBQzdFO0FBQ0EsTUFBTSxjQUFjO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSCxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0IsYUFBYTs7QUFFbkM7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvREFBb0Q7QUFDcEQ7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0EseURBQXlEO0FBQ3pEO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7OztBQUdGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLCtDQUErQztBQUMvQztBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsK0NBQStDO0FBQy9DO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLG1DQUFtQztBQUNuQztBQUNBLEVBQUU7OztBQUdGOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7O0FBR0Y7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSw0Q0FBNEM7QUFDNUM7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSx1QkFBdUIsc0JBQXNCLHNCQUFzQixFQUFFLEVBQUU7QUFDdkUsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxPQUFPO0FBQ3REO0FBQ0Esa0JBQWtCLE9BQU87QUFDekI7QUFDQSxpQ0FBaUMsT0FBTztBQUN4QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0Esb0NBQW9DLFFBQVE7O0FBRTVDOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0I7QUFDeEI7QUFDQTtBQUNBOztBQUVBLG1CQUFtQixRQUFROztBQUUzQjtBQUNBOztBQUVBLHdCQUF3QixRQUFROztBQUVoQztBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsaUJBQWlCLG9CQUFvQjtBQUNyQztBQUNBO0FBQ0Esb0dBQW9HLFFBQVE7QUFDNUc7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSw4Q0FBOEMsU0FBUztBQUN2RDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBbUMsYUFBYTtBQUNoRCxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7O0FBR0Y7O0FBRUE7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUEsZ0JBQWdCLGVBQWU7O0FBRS9CO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLGdCQUFnQixlQUFlOztBQUUvQjtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGtGQUFrRixjQUFjOztBQUVoRzs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQSw0QkFBNEIsYUFBYTs7QUFFekM7O0FBRUE7QUFDQTtBQUNBLDhFQUE4RSxjQUFjOztBQUU1RjtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxxRUFBcUUsY0FBYzs7QUFFbkY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLHVCQUF1QixRQUFROztBQUUvQjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLDZCQUE2QixRQUFROztBQUVyQztBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0EsQ0FBQzs7QUFFRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTs7QUFFQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSx5Q0FBeUM7QUFDekM7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUEsaUJBQWlCLHlCQUF5QjtBQUMxQztBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQSxpQkFBaUIseUJBQXlCO0FBQzFDO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSxpQkFBaUIseUJBQXlCOztBQUUxQztBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSx5QkFBeUIsYUFBYTs7QUFFdEM7QUFDQTs7QUFFQTtBQUNBOztBQUVBLGFBQWEseUJBQXlCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQSxpQ0FBaUMsUUFBUTtBQUN6QztBQUNBOztBQUVBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBO0FBQ0EsYUFBYSwwQkFBMEI7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhLHdCQUF3QjtB
14775QUNyQztBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxpQ0FBaUMsUUFBUTtBQUN6QztBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxDQUFDOzs7QUFHRDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSw0Q0FBNEM7QUFDNUM7QUFDQSx3QkFBd0I7O0FBRXhCO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxDQUFDOzs7QUFHRDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSx1REFBdUQ7QUFDdkQ7QUFDQSxjQUFjLGFBQWE7O0FBRTNCO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxjQUFjLGFBQWE7O0FBRTNCO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLG1CQUFtQixRQUFROztBQUUzQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLGFBQWE7O0FBRW5DO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsdUJBQXVCLGFBQWE7O0FBRXBDO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLGFBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxzQkF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BQUVBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQSxtQ0FBbUMsU0FBUztBQUM1QztBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7OztBQUdEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZCQUE2Qiw0Q0FBNEMsRUFBRTtBQUMzRTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLGdDQUFnQyxhQUFhO0FBQzdDLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLG1DQUFtQyxhQUFhO0FBQ2hELEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0EsOEJBQThCLGFBQWE7QUFDM0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOzs7QUFHRDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsK0JBQStCO0FBQy9CO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsNkNBQTZDOztBQUU3Qzs7QUFFQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEsd0JBQXdCLGVBQWU7O0FBRXZDO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNEJBQTRCLDJDQUEyQztBQUN2RSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKOztBQUVBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQSxrQ0FBa0MsUUFBUTs7QUFFMUM7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOzs7QUFHRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNEJBQTRCLDJDQUEyQztBQUN2RSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQU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FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxxQ0FBcUMsU0FBUztBQUM5QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBLG1EQUFtRCxhQUFhOztBQUVoRTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkJBQTJCO0FBQzNCO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGtDQUFrQyxTQUFTO0FBQzNDO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsK0NBQStDLFNBQVM7QUFDeEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJOztBQUVKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLDZDQUE2QyxTQUFTO0FBQ3REO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLHNDQUFzQyxTQUFTO0FBQy9DO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxXQUFXLGtCQUFrQixzQkFBc0I7QUFDbkQ7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQSxDQUFDOzs7QUFHRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUYsOENBQThDO0FBQzlDO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0F
14775BRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7QUFDQSwyQkFBMkIsd0NBQXdDO0FBQ25FLCtCQUErQix1Q0FBdUM7O0FBRXRFO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDJCQUEyQix3Q0FBd0M7QUFDbkUsK0JBQStCLHVDQUF1Qzs7QUFFdEU7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLDJCQUEyQjtBQUM3QztBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSw0QkFBNEIseUJBQXlCOztBQUVyRCwrRUFBK0UsaUNBQWlDO0FBQ2hIO0FBQ0E7QUFDQSxpQkFBaUIsc0JBQXNCO0FBQ3ZDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7O0FBR0Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDJCQUEyQix1Q0FBdUM7QUFDbEUsK0JBQStCLHNDQU
14775FzQzs7QUFFckU7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7OztBQUdEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLG1CQUFtQixRQUFROztBQUUzQjs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLHVCQUF1QixRQUFROztBQUUvQjtBQUNBOztBQUVBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLG1CQUFtQixRQUFROztBQUUzQjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUEsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUJBQXlCLFlBQVk7O0FBRXJDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBbUMsWUFBWTtBQUMvQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEIsWUFBWTs7QUFFdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9DQUFvQyxZQUFZO0FBQ2hEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDOztBQUVsQztBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBLDhCQUE4QixRQUFRO0FBQ3RDLDJCQUEyQiwyQkFBMkI7O0FBRXREO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsK0RBQStEO0FBQy9EO0FBQ0E7QUFDQSw4Q0FBOEM7QUFDOUM7QUFDQTtBQUNBLGdFQUFnRTtBQUNoRTtBQUNBO0FBQ0EsOENBQThDO0FBQzlDO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBOztBQUVBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7OztBQUdEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7O0F
14775BRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJCQUEyQixjQUFjOztBQUV6Qz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VBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0EsaURBQWlEOztBQUVqRCw4Q0FBOEMsU0FBUzs7QUFFdkQ7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsbUJBQW1CLFFBQVE7O0FBRTNCO0FBQ0EsY0FBYyxRQUFROztBQUV0Qjs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVDQUF1QyxVQUFVOztBQUVqRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLG9DQUFvQyxvQkFBb0I7O0FBRXhEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBLHdCQUF3QixRQUFROztBQUVoQztBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTs7QUFFQSwyQkFBMkIsa0JBQWtCOztBQUU3QztBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGOztBQUVBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUEscUJBQXFCLGVBQWU7QUFDcEMsc0JBQXNCLGVBQWU7O0FBRXJDO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEsS0FBSztBQUNMO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsK0NBQStDLFFBQVE7O0FBRXZEO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLGFBQWEsUUFBUTtBQUNyQjs7QUFFQSw2QkFBNkIsMEJBQTBCO0FBQ3ZELHFDQUFxQyxRQUFRLEVBQUU7O0FBRS9DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUNBQXFDLGtFQUFrRTs7QUFFdkc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0Q0FBNEMsNkJBQTZCLFFBQVE7O0FBRWpGO0FBQ0EsK0JBQStCLHNCQUFzQjtBQUNyRCxnQ0FBZ0Msc0JBQXNCO0FBQ3REO0FBQ0E7O0FBRUEscUNBQXFDLFVBQVU7O0FBRS9DO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSxjQUFjLGtCQUFrQjtBQUNoQztBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnRkFBZ0YsY0FBYztBQUM5Rjs7QUFFQSw2QkFBNkIsYUFBYTs7QUFFMUM7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLGNBQWMsUUFBUTs7QUFFdEI7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSCxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBOztBQUVBOztBQUVBO0FBQ0Esd0RBQXdEO0FBQ3hEO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKOztBQUVBOztBQUVBO0FBQ0EsY0FBYyxRQUFROztBQUV0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLGtDQUFrQyxjQUFjO0FBQ2hEO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5QkFBeUIsRUFBRSx5QkFBeUIsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sSUFBSSxJQUFJLHlDQUF5QyxrSkFBa0o7QUFDdlE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVksRUFBRSx3QkFBd0IsRUFBRSxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUU7QUFDbkQ7QUFDQTtBQUNBLEtBQUssRUFBRSx3SEFBd0gsMkNBQTJDLGdEQUFnRCxFQUFFLGtCQUFrQixFQUFFLFFBQVEsRUFBRSx3QkFBd0IsRUFBRSw4QkFBOEI7QUFDbFQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0IsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxRQUFRLFdBQVc7QUFDOUU7QUFDQTs7O0FBR0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLGNBQWMsUUFBUTs7QUFFdEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsQ0FBQzs7O0FBR0Q7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGOztBQUVBOztBQUVBLDJCQUEyQjs7QUFFM0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsQ0FBQzs7O0FBR0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0F
14775BRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSx5QkFBeUI7O0FBRXpCO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOEJBQThCLGVBQWU7QUFDN0Msd0NBQXdDLHVCQUF1QjtBQUMvRCxtQ0FBbUMsdUJBQXVCO0FBQzFEO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSxrQ0FBa0MsUUFBUTs7QU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NBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EseUJBQXlCLHVCQUF1QjtBQUNoRDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUEsY0FBYyxRQUFROztBQUV0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBOztBQUVBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxtQkFBbUI7QUFDbkI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQkFBcUI7QUFDckI7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLG9DQUFvQyxXQUFXO0FBQy9DO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSxvQkFBb0I7QUFDcEI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0JBQXdCLDRCQUE0QjtBQUNwRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7OztBQUdEO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQSw2REFBNkQsY0FBYzs7QUFFM0U7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNILEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNILEVBQUU7O0FBRUY7QUFDQSw0Q0FBNEMsUUFBUTs7QUFFcEQ7O0FBRUEscUJBQXFCLFF
14775BQVE7QUFDN0I7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx3QkFBd0Isc0JBQXNCO0FBQzlDLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7O0FBRUo7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsK0NBQStDLFFBQVE7O0FBRXZEOztBQUVBO0FBQ0EsK0JBQStCLHNEQUFzRDtBQUNyRiwrQkFBK0Isc0RBQXNEO0FBQ3JGLCtCQUErQixzREFBc0Q7QUFDckYsK0JBQStCLHNEQUFzRDs7QUFFckY7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQSxHQUFHO0FBQ0g7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUEsSUFBSTtBQUNKOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTixLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQT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FwLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IEwgZnJvbSAnbGVhZmxldCc7XG5yZXF1aXJlKCdsZWFmbGV0LWZ1bGxzY3JlZW4nKTtcbnZhciBkYXlqcyA9IHJlcXVpcmUoJ2RheWpzJylcbnZhciByZWxhdGl2ZVRpbWUgPSByZXF1aXJlKCdkYXlqcy9wbHVnaW4vcmVsYXRpdmVUaW1lJylcbnZhciBkYXlqc19lcyA9IHJlcXVpcmUoJ2RheWpzL2xvY2FsZS9lcycpO1xuZGF5anMuZXh0ZW5kKHJlbGF0aXZlVGltZSk7XG5cbmNvbnN0IGN1cnJlbnRfbGFuZyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ2h0bWwnKS5sYW5nLnNwbGl0KCctJylbMF07XG5kYXlqcy5sb2NhbGUoIGN1cnJlbnRfbGFuZyApO1xuXG5cbi8qKlxuICogICAgUHVibGljIFNwYWNlIFRvb2xzIG1hcFxuICovXG5cblxuLyoqXG4gKiAgTWFwIHNldH
14775RpbmdzXG4gKi9cblxubGV0IG1hcDsgIC8vIGluaXRzIGluIERPTUNvbnRlbnRMb2FkZWQsIHNlZSBib3R0b21cblxuY29uc3QgbWFwX3NldHRpbmdzID0ge1xuICBtaW5ab29tICAgICAgICAgICA6IDQsXG4gIGZ1bGxzY3JlZW5Db250cm9sIDogdHJ1ZSxcbiAgc2Nyb2xsV2hlZWxab29tICAgOiBmYWxzZSxcbn07XG5sZXQgbWFwX2NlbnRlciA9IHsgIC8vIFRoaXMgb25lIGlzIGEgdmFyaWFibGUgYmVjYXVzZSB3ZSdsbCByZXVzZSBpdCB0byByZW1pbmQgbWFwIGNlbnRlcnNcbiAgbGF0bG9uIDogWyAzOCwgMTggXSxcbiAgem9vbSAgIDogNSxcbn07XG5jb25zdCBtYXBfbGF5ZXIgPSB7XG4gIHRpbGVzIDogJ2h0dHBzOi8ve3N9LnRpbGUub3BlbnN0cmVldG1hcC5vcmcve3p9L3t4fS97eX0ucG5nJyxcbiAgYXR0ciAgOiAnPGEgaHJlZj1cImh0dHBzOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnL2NvcHlyaWdodFwiPk9wZW5TdHJlZXRNYXA8L2E+IGNvbnRyaWJ1dG9ycydcbn1cblxuLy8gVGhpcyBkaWN0aW9uYXJ5IGhvbGRzIGNhdGVnb3JpZXMsIHRlcm1zLCBhbmQgd2hldGhlciB0aGV5J3JlIHNlbGVjdGVkXG4vLyBieSB0aGUgdXNlciB1c2luZyB0aGUgbGVnZW5kXG5cbnZhciBjYXRlZ29yaWVzID0ge1xuICAnY2F0ZWdvcnknIDoge1xuICAgICdzZWxlY3RlZCcgOiA2LFxuICAgICd0ZXJtcycgOiB7XG4gICAgICAnYScgOiB0cnVlLFxuICAgICAgJ2InIDogdHJ1ZSxcbiAgICAgICdjJyA6IHRydWUsXG4gICAgICAnZCcgOiB0cnVlLFxuICAgICAgJ2UnIDogdHJ1ZSxcbiAgICAgICdmJyA6IHRydWUsXG4gICAgfSxcbiAgfSxcbiAgJ3RoZW1lJyA6IHtcbiAgICAnc2VsZWN0ZWQnIDogOSxcbiAgICAndGVybXMnIDoge1xuICAgICAgJ2EnIDogdHJ1ZSxcbiAgICAgICdiJyA6IHRydWUsXG4gICAgICAnYycgOiB0cnVlLFxuICAgICAgJ2QnIDogdHJ1ZSxcbiAgICAgICdlJyA6IHRydWUsXG4gICAgICAnZicgOiB0cnVlLFxuICAgICAgJ2cnIDogdHJ1ZSxcbiAgICAgICdoJyA6IHRydWUsXG4gICAgICAnaScgOiB0cnVlLFxuICAgIH1cbiAgfVxufTtcblxudmFyIGN1cnJlbnRfcG9zdCA9IG51bGw7XG5cbi8vIEVsZW1lbnRzXG5cbmxldCBmaWx0cmFibGU7IC8vIEluY2x1ZGVzIG1hcCBhbmQgdGltZWxpbmUgZWxlbWVudHMuIEluaXRzIGluIERPTUNvbnRlbnRMb2FkZWQsIHNlZSBib3R0b21cbmxldCBtYXJrZXJzIDsgIC8vIGluaXRzIGluIERPTUNvbnRlbnRMb2FkZWQsIHNlZSBib3R0b21cbmxldCBsaXN0OyAgICAgIC8vIGluaXRzIGluIERPTUNvbnRlbnRMb2FkZWQsIHNlZSBib3R0b21cblxuXG4vKipcbiAqICBVbnNlbGVjdHMgYWxsIGl0ZW1zXG4gKi9cbmNvbnN0IHVuc2VsZWN0X2FsbCA9IGZ1bmN0aW9uKCl7XG4gIGZpbHRyYWJsZS5mb3JFYWNoKGZ1bmN0aW9uKGkpe1xuICAgIGkuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7XG4gIH0pO1xuICBtYXJrZXJzLmZvckVhY2goZnVuY3Rpb24oaSl7XG4gICAgaS5jbGFzc0xpc3QucmVtb3ZlKCd1bnNlbGVjdGVkJyk7XG4gIH0pO1xufVxuXG4vKipcbiAqICBJbml0cyBtYWluIG1hcFxuICovXG5cbmNvbnN0IGluaXRfbWFpbl9tYXAgPSBmdW5jdGlvbigpe1xuICBtYXAgPSBMLm1hcCgnbWFpbi1tYXAnLCBtYXBfc2V0dGluZ3MpLm9uKFxuICAgICdjbGljaycsICgpID0+IHsgdW5zZWxlY3RfYWxsKCk7IH1cbiAgKS5zZXRWaWV3KFxuICAgIG1hcF9jZW50ZXIubGF0bG9uLFxuICAgIG1hcF9jZW50ZXIuem9vbVxuICApO1xuICBMLnRpbGVMYXllcihtYXBfbGF5ZXIudGlsZXMsIHtcbiAgICBhdHRyaWJ1dGlvbjogbWFwX2xheWVyLmF0dHJpYnV0aW9uXG4gIH0pLmFkZFRvKG1hcCk7XG59XG5cbi8qKlxuICogIEdldCBjYXNlcy4gQ3JlYXRlIGNhc2VzIGxpc3QgaXRlbXMgYW5kIG1hcmtlcnMuXG4gKi9cblxuY29uc3QgZmV0Y2hfcG9zdHMgPSBmdW5jdGlvbigpe1xuICBmZXRjaCgnL3dwLWpzb24vcHN0LWNhc2VzL3YxL2FsbC8nK2xhbmcpLnRoZW4oIGRhdGEgPT4gZGF0YS5qc29uKCkgKS50aGVuKCBwb3N0cyA9PiB7XG4gICAgcG9zdHMuZm9yRWFjaChmdW5jdGlvbihpKXtcbiAgICAgIHZhciB0ZW1wbGF0ZV9ub2RlID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI2Nhc2UtdGVtcGxhdGUnKTtcbiAgICAgIHZhciB0ZW1wbGF0ZSA9IGRvY3VtZW50LmltcG9ydE5vZGUodGVtcGxhdGVfbm9kZS5jb250ZW50LCB0cnVlKTtcblxuICAgICAgLy8gUG9wdWxhdGUgdGltZWxpbmVcbiAgICAgIGNvbnN0IHBvc3QgPSB0ZW1wbGF0ZS5xdWVyeVNlbGVjdG9yKCcuY2FzZScpO1xuICAgICAgcG9zdC5pZCA9ICdjYXNlLScgKyBpLmlkO1xuICAgICAgcG9zdC5jbGFzc0xpc3QuYWRkKCdjYXRlZ29yeS0tJyArIGkuY2F0LnZhbHVlKTtcbiAgICAgIHBvc3QuY2xhc3NMaXN0LmFkZCgndGhlbWUtLScgKyBpLnRobS52YWx1ZSk7XG4gICAgICBwb3N0LmNsYXNzTGlzdC5hZGQoJ2Nhc2UtLScgKyBpLmlkKTtcbiAgICAgIHBvc3QuZGF0YXNldC5jYXQgPSBpLmNhdC52YWx1ZTtcbiAgICAgIGxldCBpbWcgPSB0ZW1wbGF0ZS5xdWVyeVNlbGVjdG9yKCcuY2FzZV9faW1hZ2UnKTtcbiAgICAgIGlmKGkuaW1nKXtcbiAgICAgICAgICBpbWcuc2V0QXR0cmlidXRlKCdzcmMnLCBpLmltZyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAgIGltZy5yZW1vdmUoKTtcbiAgICAgIH1cbiAgICAgIHRlbXBsYXRlLnF1ZXJ5U2VsZWN0b3IoJy5jYXNlX190YWdzLXR5cGUnKS5pbm5lckhUTUwgPSBpLmNhdC5sYWJlbDtcbiAgICAgIHRlbXBsYXRlLnF1ZXJ5U2VsZWN0b3IoJy5jYXNlX190YWdzLXRoZW1lJykuaW5uZXJIVE1MID0gaS50aG0ubGFiZWw7XG4gICAgICB0ZW1wbGF0ZS5xdWVyeVNlbGVjdG9yKCcuY2FzZV9fdGl0bGUnKS5pbm5lckhUTUwgPSBpLnRpdDtcbiAgICAgIHRlbXBsYXRlLnF1ZXJ5U2VsZWN0b3IoJy5jYXNlX19tZXRhZGF0YScpLmlubmVySFRNTCA9IGRheWpzKGkuZGF0KjEwMDApLmZyb21Ob3coKTtcbiAgICAgIHRlbXBsYXRlLnF1ZXJ5U2VsZWN0b3IoJy5jYXNlJykuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24oKXtcbiAgICAgICAgICBsb2NhdGlvbi5oYXNoID0gaS5pZDtcbiAgICAgICAgICBsb2FkX3Bvc3QoaS5pZCk7XG4gICAgICB9KTtcbiAgICAgIGxpc3QuYXBwZW5kQ2hpbGQodGVtcGxhdGUpO1xuXG4gICAgICAvLyBQb3B1bGF0ZSBtYXBcbiAgICAgIGlmKGkucG9zLmx
14775hdCAmJiBpLnBvcy5sbmcpe1xuICAgICAgICAgIEwubWFya2VyKFtpLnBvcy5sYXQsIGkucG9zLmxuZ10sIHtcbiAgICAgICAgICAgIGljb24gOiBMLmRpdkljb24oe1xuICAgICAgICAgICAgICB0eXBlOiAnZGl2JyxcbiAgICAgICAgICAgICAgaWNvblNpemU6WzQwLCA0MF0sXG4gICAgICAgICAgICAgIGljb25BbmNob3I6IFsyMCwgNTBdLFxuICAgICAgICAgICAgICBwb3B1cEFuY2hvcjogWzAsIC0zMF0sXG4gICAgICAgICAgICAgIGxheWVyOiAnaGlkZGVuJyxcbiAgICAgICAgICAgICAgY2xhc3NOYW1lOiBgaXRlbS1maWx0cmFibGUgY2FzZS0tJHtpLmlkfSBjYXRlZ29yeS0tJHtpLmNhdC52YWx1ZX0gdGhlbWUtLSR7aS50aG0udmFsdWV9YCxcbiAgICAgICAgICAgICAgaHRtbDogYDxpbWcgY2xhc3M9XCJpY29uXCIgc3JjPVwiL3dwLWNvbnRlbnQvdGhlbWVzL3BzdC9pbWcvaWNvbnMtLXRoZW1lcy8ke2kudGhtLnZhbHVlfS5zdmdcIiAvPmBcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgIH0pLm9uKCdjbGljaycsIGZ1bmN0aW9uKGUpe1xuICAgICAgICAgICAgICBsb2FkX3Bvc3QoaS5pZCk7XG4gICAgICAgICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5sZWFmbGV0LW1hcmtlci1pY29uJykuZm9yRWFjaChmdW5jdGlvbihpKXtcbiAgICAgICAgICAgICAgICAgIGkuY2xhc3NMaXN0LmFkZCgndW5zZWxlY3RlZCcpO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgZS50YXJnZXQuX2ljb24uY2xhc3NMaXN0LnJlbW92ZSgndW5zZWxlY3RlZCcpO1xuICAgICAgICAgIH0pLmFkZFRvKG1hcCk7XG4gICAgICB9O1xuICAgIH0pO1xuICAgIC8vIEdldCBjdXJyZW50IGhhc2ggaWYgYW55IGFuZCBzZWxlY3QgcmVsYXRlZCBwb3N0XG4gICAgbGV0IGlkID0gd2luZG93LmxvY2F0aW9uLmhhc2g7XG4gICAgaWYoaWQpe1xuICAgICAgICBpZCA9IGlkLnN1YnN0cmluZygxLCBpZC5sZW5ndGgpO1xuICAgICAgICBpZihkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGAjY2FzZS0ke2lkfWApKSBsb2FkX3Bvc3QoaWQpO1xuICAgIH1cbiAgfSk7XG59XG5cblxuLyoqXG4gKiAgU2hvd3Mgc2VsZWN0ZWQgY2FzZVxuICovXG5cbmNvbnN0IHNob3dfY2FzZSA9IGZ1bmN0aW9uKGlkKVxue1xuICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoYC5pdGVtLWZpbHRyYWJsZTpub3QoLmNhc2UtLSR7aWR9KWApLmZvckVhY2goZnVuY3Rpb24oaSl7XG4gICAgICAgIGkuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7XG4gICAgfSk7XG59XG5cbi8qKlxuICogIExvYWQgcG9zdFxuICovXG5cbmZ1bmN0aW9uIGxvYWRfcG9zdChpZClcbntcbiAgY29uc3QgY2F0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihgI2Nhc2UtJHtpZH1gKS5kYXRhc2V0LmNhdDtcbiAgc2hvd19jYXNlKGlkKTtcblxuICBjdXJyZW50X3Bvc3QgPSBpZDtcblxuICBmZXRjaCgnL3dwLWpzb24vcHN0LWNhc2VzL3YxL2lkPScraWQpLnRoZW4oZGF0YSA9PiBkYXRhLmpzb24oKSApLnRoZW4oIHBvc3Q9PntcbiAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoJ3Nob3dpbmctcG9zdCcpO1xuICAgIGlmKHBvc3QucG9zLmxhdCAmJiBwb3N0LnBvcy5sbmcpe1xuICAgICAgICBtYXBfY2VudGVyLmxhdGxvbiA9IG1hcC5nZXRDZW50ZXIoKTtcbiAgICAgICAgbWFwX2NlbnRlci56b29tICAgPSBtYXAuZ2V0Wm9vbSgpO1xuICAgICAgICBkb2
14775N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoJ3Nob3dpbmctcG9zdC0tbG9jYXRlZCcpO1xuICAgICAgICBtYXAuc2V0VmlldyhbIHBvc3QucG9zLmxhdCwgcG9zdC5wb3MubG5nXSwgOCk7XG4gICAgICAgIG1hcC5pbnZhbGlkYXRlU2l6ZSgpO1xuICAgIH1cbiAgICAvLyBQb3B1bGF0ZSB0aW1lbGluZVxuICAgIHZhciB0ZW1wbGF0ZV9ub2RlID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3Bvc3QtdGVtcGxhdGUnKTtcbiAgICB2YXIgdGVtcGxhdGUgPSBkb2N1bWVudC5pbXBvcnROb2RlKHRlbXBsYXRlX25vZGUuY29udGVudCwgdHJ1ZSk7XG4gICAgdmFyIG1vZGFsID0gdGVtcGxhdGUucXVlcnlTZWxlY3RvcignLnBvc3QtbW9kYWwnKTtcbiAgICBtb2RhbC5jbGFzc0xpc3QuYWRkKCdjYXQtLScgKyBjYXQpO1xuICAgIGNvbnN0IHVybCA9IGBodHRwczovL3B1YmxpY3NwYWNlLnRvb2xzLyR7IGVzY2FwZSgnIycraWQpIH1gO1xuICAgIG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy50d2l0dGVyJykuaHJlZiA9IGBodHRwczovL3R3aXR0ZXIuY29tL2ludGVudC90d2VldD90ZXh0PSR7cG9zdC50aXRsZX0gJHt1cmx9YDtcbiAgICBtb2RhbC5xdWVyeVNlbGVjdG9yKCcudGVsZWdyYW0nKS5ocmVmID0gYGh0dHBzOi8vdC5tZS9zaGFyZS91cmw/dXJsPTw/PSAkdXJsfSZ0ZXh0PSR7cG9zdC50aXRsZX1gO1xuICAgIG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy5wb3N0LW1vZGFsX19zdW1tYXJ5JykuaW5uZXJIVE1MID0gcG9zdC5zdW1tYXJ5O1xuICAgIG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy5wb3N0LW1vZGFsX19ib2R5JykuaW5uZXJIVE1MICAgID0gcG9zdC5ib2R5O1xuICAgIG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy5wb3N0LW1vZGFsX19ob3d0bycpLmlubmVySFRNTCAgID0gcG9zdC5ob3d0bztcbiAgICBjb25zdCB1cGRhdGUgPSBtb2RhbC5xdWVyeVNlbGVjdG9yKCcucG9zdC1tb2RhbF9fdXBkYXRlJyk7XG4gICAgaWYodXBkYXRlKSB1cGRhdGUuaHJlZiAgPSBgL3dwLWFkbWluL3Bvc3QucGhwP3Bvc3Q9JHtpZH0mYWN0aW9uPWVkaXRgO1xuICAgIHZhciBsaW5rcyA9IG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy5wb3N0LW1vZGFsX19saW5rcycpO1xuICAgIGlmKHBvc3QubGlua3MubGVuZ3RoID4gMCl7XG4gICAgICAgIGxpbmtzLmlubmVySFRNTCA9ICc8aDUgY2xhc3M9XCJwb3N0LW1vZGFsX19saW5rcy1sYWJlbFwiPicrc2VlbW9yZSsnPC9oNT4nO1xuICAgICAgICBwb3N0LmxpbmtzLmZvckVhY2goIGZ1bmN0aW9uKGxpbmspe1xuICAgICAgICAgICAgaWYobGluaykge1xuICAgICAgICAgICAgICAgIHZhciBsaW5rX21hcmt1cCA9ICc8bGkgY2xhc3M9XCJwb3N0LW1vZGFsX19saW5rXCI+PGEgdGFyZ2V0PVwiX2JsYW5rXCIgaHJlZj1cIicrbGluaysnXCI+JytsaW5rKyc8L2E+PC9saT4nO1xuICAgICAgICAgICAgICAgIGxpbmtzLmlubmVySFRNTCArPSBsaW5rX21hcmt1cDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIGxldCBmaWxlX21hcmt1cCA9IGA8YSB0YXJnZXQ9XCJfYmxhbmtcIiBocmVmPVwiJHsgcG9zdC5maWxlIH1cIj4keyBwb3N0LmZpbGUgfTwvYT5gO1xuICAgIG1vZGFsLnF1ZXJ5U2VsZWN0b3IoJy5wb3N0LW1vZGFsX19maWxlJykuaW5uZXJIVE1MID0gcG9zdC5ob3d0bztcbiAgICBtb2RhbC5xdWVyeVNlbGVjdG9yKCcucG9zdC1tb2RhbF9fY2xvc2UnKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7XG4gICAgICAgIGRvY3VtZW50LmJvZHkuY2xhc3NMaXN0LnJlbW92ZSgnc2hvd2luZy1wb3N0Jyk7XG4gICAgICAgIGxvY2F0aW9uLmhhc2ggPSAnJztcbiAgICAgICAgaWYoZG9jdW1lbnQuYm9keS5jbGFzc0xpc3QuY29udGFpbnMoJ3Nob3dpbmctcG9zdC0tbG9jYXRlZCcpKXtcbiAgICAgICAgICBkb2
14775N1bWVudC5ib2R5LmNsYXNzTGlzdC5yZW1vdmUoJ3Nob3dpbmctcG9zdC0tbG9jYXRlZCcpO1xuICAgICAgICAgICAgbWFwLnNldFZpZXcoIG1hcF9jZW50ZXIubGF0bG9uLCBtYXBfY2VudGVyLnpvb20gKTtcbiAgICAgICAgICAgIG1hcC5pbnZhbGlkYXRlU2l6ZSgpO1xuICAgICAgICB9O1xuICAgICAgICB1bnNlbGVjdF9pdGVtcygpO1xuICAgICAgICBtb2RhbC5yZW1vdmUoKTtcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmNhc2UtbGlzdCAuY2FzZS0tJyArIGN1cnJlbnRfcG9zdCApLnNjcm9sbEludG9WaWV3KCk7XG4gICAgfSk7XG4gICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmxheW91dC1tYXBfX3JpZ2h0JykuYXBwZW5kQ2hpbGQobW9kYWwpO1xuICB9KTtcbn1cblxuXG4vKipcbiAqICBDaGVja3MgbWFya2VycyBhbmQgc2V0cyBpdHMgdmlzaWJpbGl0eSByZWdhcmRpbmcgc2VsZWN0ZWQgY2F0ZWdvcmllc1xuICovXG5jb25zdCBjaGVja19pdGVtcyA9IGZ1bmN0aW9uKClcbntcbiAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLml0ZW0tZmlsdHJhYmxlJykuZm9yRWFjaCggaXRlbSA9PiB7XG4gICAgbGV0IGNsYXNzZXMgPSBpdGVtLmNsYXNzTGlzdDtcbiAgICBmb3IoY29uc3QgY2F0ZWdvcnkgaW4gY2F0ZWdvcmllcyl7XG4gICAgICB2YXIgaXNfdmlzaWJsZSA9IGZhbHNlO1xuICAgICAgaWYoY2xhc3Nlcy5jb250YWlucygnZmlsdGVyZWQnKSl7IGNsYXNzZXMucmVtb3ZlKCdmaWx0ZXJlZCcpIH1cbiAgICAgIE9iamVjdC5rZXlzKGNhdGVnb3JpZXNbY2F0ZWdvcnldLnRlcm1zKS5mb3JFYWNoKCBpZCA9PntcbiAgICAgICAgaWYoIGNhdGVnb3JpZXNbY2F0ZWdvcnldLnRlcm1zW2lkXSAmJiBjbGFzc2VzLmNvbnRhaW5zKGAke2NhdGVnb3J5fS0tJHtpZH1gKSApe1xuICAgICAgICAgIGlzX3Zpc2libGUgPSB0cnVlO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBpZighaXNfdmlzaWJsZSl7IGNsYXNzZXMuYWRkKCdmaWx0ZXJlZCcpOyByZXR1cm47IH1cbiAgICB9XG4gIH0pO1xufVxuXG5cbi8qKlxuICogIFVuc2VsZWN0IGl0ZW1zXG4gKi9cblxuZnVuY3Rpb24gdW5zZWxlY3RfaXRlbXMoKXtcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuaXRlbS1maWx0cmFibGUuaGlkZGVuJykuZm9yRWFjaChmdW5jdGlvbihpKXtcbiAgICAgICAgaS5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTtcbiAgICB9KTtcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcubGVhZmxldC1tYXJrZXItaWNvbicpLmZvckVhY2goZnVuY3Rpb24oaSl7XG4gICAgICAgIGkuY2xhc3NMaXN0LnJlbW92ZSgndW5zZWxlY3RlZCcpO1xuICAgIH0pO1xufVxuXG5cbi8qKlxuICogIExlZ2VuZCBpbnRlcmFjdGlvblxuICovXG5cbmNvbnN0IGluaXRfbGVnZW5kID0gZnVuY3Rpb24oKVxue1xuICB2YXIgbGVnZW5kX2l0ZW1zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmxlZ2VuZF9faXRlbScpO1xuICB2YXIgbGVnZW5kX3Jlc2V0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmxlZ2VuZHNfX3Jlc2V0Jyk7XG5cbiAgbGVnZW5kX2l0ZW1zLmZvckVhY2goIGxlZ2VuZF9pdGVtID0+IHsgbGVnZW5kX2l0ZW0uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PlxuICB7XG4gICAgICBjb25zdCBjYXRlZ29yeSAgICAgICA9IGxlZ2VuZF9pdGVtLnBhcmVudE5vZGUuZGF0YXNldC5jYXQ7XG4gICAgICBjb25zdCBjYXRlZ29yeV90ZXJtcyA9IGxlZ2VuZF9pdGVtLnBhcmVudE5vZGUucXVlcnlTZWxlY3RvckFsbCgnLmxlZ2VuZF9faXRlbScpO1xuICAgICAgY29uc3QgdGVybV9pZCAgICAgICAgPSBsZWdlbmRfaXRlbS5kYXRhc2V0LmlkO1xuXG4gICAgICAvLyBJZiBmaXJzdCB0ZXJtIHNlbGVjdGVkLCB1bmNoZWNrIGFsbCBleGNlcHRpbmcgaXRzZWxmXG4gICAgICBpZihjYXRlZ29yaWVzW2NhdGVnb3J5XS5zZWxlY3RlZCA9PSBjYXRlZ29yeV90ZXJtcy5sZW5ndGgpe1xuICAgICAgICBmb3IoY29uc3QgdGVybSBpbiBjYXRlZ29yaWVzW2NhdGVnb3J5XS50ZXJtcyl7XG4gICAgICAgICAgY2F0ZWdvcmllc1tjYXRlZ29yeV0udGVybXNbdGVybV0gPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBjYXRlZ29yaWVzW2NhdGVnb3J5XS50ZXJtc1t0ZXJtX2lkXSA9IHRydWU7XG4gICAgICAgIGNhdGVnb3J5X3Rlcm1zLmZvckVhY2goIGkgPT4geyBpLmNsYXNzTGlzdC5yZW1vdmUoJ29uJyk7IH0pXG4gICAgICAgIGNhdGVnb3JpZXNbY2F0ZWdvcnldLnNlbGVjdGVkID0gMTtcbiAgICAgICAgbGVnZW5kX2l0ZW0uY2xhc3NMaXN0LmFkZCgnb24nKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhdGVnb3JpZXNbY2F0ZWdvcnldLnRlcm1zW3Rlcm1faWRdID0gIWNhdGVnb3JpZXNbY2F0ZWdvcnldLnRlcm1zW3Rlcm1faWRdO1xuICAgICAgICBsZWdlbmRfaXRlbS5jbGFzc0xpc3QudG9nZ2xlKCdvbicpO1xuICAgICAgICBpZihjYXRlZ29yaWVzW2NhdGVnb3J5XS50ZXJtc1t0ZXJtX2lkXSl7XG4gICAgICAgICAgY2F0ZWdvcmllc1tjYXRlZ29yeV0uc2VsZWN0ZWQrKztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjYXRlZ29yaWVzW2NhdGVnb3J5XS5zZWxlY3RlZC0tO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZihjYXRlZ29yaWVzW2NhdGVnb3J5XS5zZWxlY3RlZCA9PSAwKXtcbiAgICAgICAgZm9yKGNvbnN0IHRlcm0gaW4gY2F0ZWdvcmllc1tjYXRlZ29yeV0udGVybXMpe1xuICAgICAgICAgICAgY2F0ZWdvcmllc1tjYXRlZ29yeV0udGVybXNbdGVybV0gPSB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIGNhdGVnb3J5X3Rlcm1zLmZvckVhY2goIGkgPT4geyBpLmNsYXNzTGlzdC5hZGQoJ29uJyk7IH0pXG4gICAgICAgIGNhdGVnb3JpZXNbY2F0ZWdvcnldLnNlbGVjdGVkID0gY2F0ZWdvcnlfdGVybXMubGVuZ3RoO1xuICAgICAgfVxuICAgICAgY2hlY2tfaXRlbXMoKTtcbiAgfSl9KTtcblxuICAvLyBSZXNldCBidXR0b24gaW4gbGVnZW5kXG4gIGxlZ2VuZF9yZXNldC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpPT57XG4gICAgWydjYXRlZ29yeScsICd0aGVtZSddLmZvckVhY2goIGZ1bmN0aW9uKGNhdCl7XG4gICAgICBmb3IodmFyIHRlcm0gaW4gY2F0ZWdvcmllc1tjYXRdLnRlcm1zKSB7XG4gICAgICAgIGNhdGVnb3JpZXNbY2F0XS50ZXJtc1t0ZXJtXSA9IHRydWU7XG4gICAgICB9XG4gICAgICBjYXRlZ29yaWVzW2NhdF0uc2VsZWN0ZWQgPSBPYmplY3Qua2V5cyhjYXRlZ29yaWVzW2NhdF0udGVybXMpLmxlbmd0aDtcbiAgICB9KTtcbiAgICBsZWdlbmRfaXRlbXMuZm9yRWFjaCggaSA9PntcbiAgICAgICAgaS5jbGFzc0xpc3QuYWRkKCdvbicpO1xuICAgIH0pO1xuICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5pdGVtLWZpbHRyYWJsZScpLmZvckVhY2goIGkgPT57XG4gICAgICAgIGkuY2xhc3NMaXN0LnJlbW92ZSgnZmlsdGVyZWQnKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbmNvbnN0IGluaXRfc2VhcmNoID0gZnVuY3Rpb24oKXtcbiAgICBkb2
14775N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2VhcmNoLXRyaWdnZXInKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKT0+e1xuICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2l0ZS1zZWFyY2gnKS5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTtcbiAgICB9KTtcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2l0ZS1zZWFyY2hfX2J1dHRvbicpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpPT57XG4gICAgICAgIGNvbnN0IHEgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2l0ZS1zZWFyY2hfX2lucHV0JykudmFsdWU7XG4gICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5ub3QtcXVlcmllZCcpLmZvckVhY2goIGkgPT4geyBpLmNsYXNzTGlzdC5yZW1vdmUoJ25vdC1xdWVyaWVkJykgfSk7XG4gICAgICAgIGZldGNoKGAvd3AtanNvbi9wc3QtY2FzZXMvdjEvc2VhcmNoLyR7cX0vJHtsYW5nfWApLnRoZW4oZGF0YSA9PiBkYXRhLmpzb24oKSkudGhlbihwb3N0cyA9PntcbiAgICAgICAgICAgIHZhciBub3RfcXVlcmllZCA9ICcuaXRlbS1maWx0cmFibGUnO1xuICAgICAgICAgICAgcG9zdHMuZm9yRWFjaCggaWQgPT4geyBub3RfcXVlcmllZCArPSBgOm5vdCguY2FzZS0tJHtpZH0pYCB9KTtcbiAgICAgICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwobm90X3F1ZXJpZWQpLmZvckVhY2goIGkgPT4geyBpLmNsYXNzTGlzdC5hZGQoJ25vdC1xdWVyaWVkJyk7IH0gKTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG4gICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLnNpdGUtc2VhcmNoX19jbG9zZScpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpPT57XG4gICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5zaXRlLXNlYXJjaCcpLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpO1xuICAgICAgICBkb2
14775N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2l0ZS1zZWFyY2hfX2lucHV0JykudmFsdWUgPSAnJztcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLm5vdC1xdWVyaWVkJykuZm9yRWFjaCggaSA9PiB7IGkuY2xhc3NMaXN0LnJlbW92ZSgnbm90LXF1ZXJpZWQnKSB9KTtcbiAgICB9KTtcbn1cblxuY29uc3QgaW5pdF9hYm91dCA9IGZ1bmN0aW9uKCl7XG4gICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLnNpdGUtYWJvdXRfX2Nsb3NlJykuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoZSk9PntcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLnNpdGUtYWJvdXQnKS5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTtcbiAgICB9KTtcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2l0ZS1icmFuZGluZycpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpPT57XG4gICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5zaXRlLWFib3V0JykuY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJyk7XG4gICAgfSk7XG59XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJET01Db250ZW50TG9hZGVkXCIsIGZ1bmN0aW9uKClcbntcbiAgICBmaWx0cmFibGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuaXRlbS1maWx0cmFibGUnKTtcbiAgICBtYXJrZXJzICAgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcubGVhZmxldC1tYXJrZXItaWNvbicpO1xuICAgIGxpc3QgICAgICA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5jYXNlLWxpc3QnKTtcbiAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoJ2xvYWRpbmcnKTtcbiAgICBpbml0X21haW5fbWFwKCk7XG4gICAgaW5pdF9sZWdlbmQoKTtcbiAgICBmZXRjaF9wb3N0cygpO1xuICAgIGluaXRfc2VhcmNoKCk7XG4gICAgaW5pdF9hYm91dCgpO1xuICAgIGRvY3VtZW50LmJvZHkuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXG59KVxuIiwiIWZ1bmN0aW9uKHQsZSl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9ZSgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoZSk6dC5kYXlqcz1lKCl9KHRoaXMsZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgdD1cIm1pbGxpc2Vjb25kXCIsZT1cInNlY29uZFwiLG49XCJtaW51dGVcIixyPVwiaG91clwiLGk9XCJkYXlcIixzPVwid2Vla1wiLHU9XCJtb250aFwiLGE9XCJxdWFydGVyXCIsbz1cInllYXJcIixmPVwiZGF0ZVwiLGg9L14oXFxkezR9KVstL10/KFxcZHsxLDJ9KT9bLS9dPyhcXGR7MCwyfSlbXjAtOV0qKFxcZHsxLDJ9KT86PyhcXGR7MSwyfSk/Oj8oXFxkezEsMn0pP1suOl0/KFxcZCspPyQvLGM9L1xcWyhbXlxcXV0rKV18WXsxLDR9fE17MSw0fXxEezEsMn18ZHsxLDR9fEh7MSwyfXxoezEsMn18YXxBfG17MSwyfXxzezEsMn18WnsxLDJ9fFNTUy9nLGQ9e25hbWU6XCJlblwiLHdlZWtkYXlzOlwiU3VuZGF5X01vbmRheV9UdWVzZGF5X1dlZG5lc2RheV9UaHVyc2RheV9GcmlkYXlfU2F0dXJkYXlcIi5zcGxpdChcIl9cIiksbW9udGhzOlwiSmFudWFyeV9GZWJydWFyeV9NYXJjaF9BcHJpbF9NYXlfSnVuZV9KdWx5X0F1Z3VzdF9TZXB0ZW1iZXJfT2N0b2Jlcl9Ob3ZlbWJlcl9EZWNlbWJlclwiLnNwbGl0KFwiX1wiKX0sJD1mdW5jdGlvbih0LGUsbil7dmFyIHI9U3RyaW5nKHQpO3JldHVybiFyfHxyLmxlbmd0aD49ZT90OlwiXCIrQXJyYXkoZSsxLXIubGVuZ3RoKS5qb2luKG4pK3R9LGw9e3M6JCx6OmZ1bmN0aW9uKHQpe3ZhciBlPS10LnV0Y09mZnNldCgpLG49TWF0aC5hYnMoZSkscj1NYXRoLmZsb29yKG4vNjApLGk9biU2MDtyZXR1cm4oZTw9MD9cIitcIjpcIi1cIikrJChyLDIsXCIwXCIpK1wiOlwiKyQoaSwyLFwiMFwiKX0sbTpmdW5jdGlvbiB0KGUsbil7aWYoZS5kYXRlKCk8bi5kYXRlKCkpcmV0dXJuLXQobixlKTt2YXIgcj0xMioobi55ZWFyKCktZS55ZWFyKCkpKyhuLm1vbnRoKCktZS5tb250aCgpKSxpPWUuY2xvbmUoKS5hZGQocix1KSxzPW4taTwwLGE9ZS5jbG9uZSgpLmFkZChyKyhzPy0xOjEpLHUpO3JldHVybisoLShyKyhuLWkpLy
14775hzP2ktYTphLWkpKXx8MCl9LGE6ZnVuY3Rpb24odCl7cmV0dXJuIHQ8MD9NYXRoLmNlaWwodCl8fDA6TWF0aC5mbG9vcih0KX0scDpmdW5jdGlvbihoKXtyZXR1cm57TTp1LHk6byx3OnMsZDppLEQ6ZixoOnIsbTpuLHM6ZSxtczp0LFE6YX1baF18fFN0cmluZyhofHxcIlwiKS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL3MkLyxcIlwiKX0sdTpmdW5jdGlvbih0KXtyZXR1cm4gdm9pZCAwPT09dH19LHk9XCJlblwiLE09e307TVt5XT1kO3ZhciBtPWZ1bmN0aW9uKHQpe3JldHVybiB0IGluc3RhbmNlb2YgU30sRD1mdW5jdGlvbih0LGUsbil7dmFyIHI7aWYoIXQpcmV0dXJuIHk7aWYoXCJzdHJpbmdcIj09dHlwZW9mIHQpTVt0XSYmKHI9dCksZSYmKE1bdF09ZSxyPXQpO2Vsc2V7dmFyIGk9dC5uYW1lO01baV09dCxyPWl9cmV0dXJuIW4mJnImJih5PXIpLHJ8fCFuJiZ5fSx2PWZ1bmN0aW9uKHQsZSl7aWYobSh0KSlyZXR1cm4gdC5jbG9uZSgpO3ZhciBuPVwib2JqZWN0XCI9PXR5cGVvZiBlP2U6e307cmV0dXJuIG4uZGF0ZT10LG4uYXJncz1hcmd1bWVudHMsbmV3IFMobil9LGc9bDtnLmw9RCxnLmk9bSxnLnc9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdih0LHtsb2NhbGU6ZS4kTCx1dGM6ZS4kdSx4OmUuJHgsJG9mZnNldDplLiRvZmZzZXR9KX07dmFyIFM9ZnVuY3Rpb24oKXtmdW5jdGlvbiBkKHQpe3RoaXMuJEw9RCh0LmxvY2FsZSxudWxsLCEwKSx0aGlzLnBhcnNlKHQpfXZhciAkPWQucHJvdG90eXBlO3JldHVybiAkLnBhcnNlPWZ1bmN0aW9uKHQpe3RoaXMuJGQ9ZnVuY3Rpb24odCl7dmFyIGU9dC5kYXRlLG49dC51dGM7aWYobnVsbD09PWUpcmV0dXJuIG5ldyBEYXRlKE5hTik7aWYoZy51KGUpKXJldHVybiBuZXcgRGF0ZTtpZihlIGluc3RhbmNlb2YgRGF0ZSlyZXR1cm4gbmV3IERhdGUoZSk7aWYoXCJzdHJpbmdcIj09dHlwZW9mIGUmJiEvWiQvaS50ZXN0KGUpKXt2YXIgcj1lLm1hdGNoKGgpO2lmKHIpe3ZhciBpPXJbMl0tMXx8MCxzPShyWzddfHxcIjBcIikuc3Vic3RyaW5nKDAsMyk7cmV0dXJuIG4/bmV3IERhdGUoRGF0ZS5VVEMoclsxXSxpLHJbM118fDEscls0XXx8MCxyWzVdfHwwLHJbNl18fDAscykpOm5ldyBEYXRlKHJbMV0saSxyWzNdfHwxLHJbNF18fDAscls1XXx8MCxyWzZdfHwwLHMpfX1yZXR1cm4gbmV3IERhdGUoZSl9KHQpLHRoaXMuJHg9dC54fHx7fSx0aGlzLmluaXQoKX0sJC5pbml0PWZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy4kZDt0aGlzLiR5PXQuZ2V0RnVsbFllYXIoKSx0aGlzLiRNPXQuZ2V0TW9udGgoKSx0aGlzLiREPXQuZ2V0RGF0ZSgpLHRoaXMuJFc9dC5nZXREYXkoKSx0aGlzLiRIPXQuZ2V0SG91cnMoKSx0aGlzLiRtPXQuZ2V0TWludXRlcygpLHRoaXMuJHM9dC5nZXRTZWNvbmRzKCksdGhpcy4kbXM9dC5nZXRNaWxsaXNlY29uZHMoKX0sJC4kdXRpbHM9ZnVuY3Rpb24oKXtyZXR1cm4gZ30sJC5pc1ZhbGlkPWZ1bmN0aW9uKCl7cmV0dXJuIShcIkludmFsaWQgRGF0ZVwiPT09dGhpcy4kZC50b1N0cmluZygpKX0sJC5pc1NhbWU9ZnVuY3Rpb24odCxlKXt2YXIgbj12KHQpO3JldHVybiB0aGlzLnN0YXJ0T2YoZSk8PW4mJm48PXRoaXMuZW5kT2YoZSl9LCQuaXNBZnRlcj1mdW5jdGlvbih0LGUpe3JldHVybiB2KHQpPHRoaXMuc3RhcnRPZihlKX0sJC5pc0JlZm9yZT1mdW5jdGlvbih0LGUpe3JldHVybiB0aGlzLmVuZE9mKGUpPHYodCl9LCQuJGc9ZnVuY3Rpb24odCxlLG4pe3JldHVybiBnLnUodCk/dGhpc1tlXTp0aGlzLnNldChuLHQpfSwkLnVuaXg9ZnVuY3Rpb24oKXtyZXR1cm4gTWF0aC5mbG9vcih0aGlzLnZhbHVlT2YoKS8xZTMpfSwkLnZhbHVlT2Y9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kZC5nZXRUaW1lKCl9LCQuc3RhcnRPZj1mdW5jdGlvbih0LGEpe3ZhciBoPXRoaXMsYz0hIWcudShhKXx8YSxkPWcucCh0KSwkPWZ1bmN0aW9uKHQsZSl7dmFyIG49Zy53KGguJHU/RGF0ZS5VVEMoaC4keSxlLHQpOm5ldyBEYXRlKGguJHksZSx0KSxoKTtyZXR1cm4gYz9uOm4uZW5kT2YoaSl9LGw9ZnVuY3Rpb24odCxlKXtyZXR1cm4gZy53KGgudG9EYXRlKClbdF0uYXBwbHkoaC50b0RhdGUoXCJzXCIpLChjP1swLDAsMCwwXTpbMjMsNTksNTksOTk5XSkuc2xpY2UoZSkpLGgpfSx5PXRoaXMuJFcsTT10aGlzLiRNLG09dGhpcy4kRCxEPVwic2V0XCIrKHRoaXMuJHU/XCJVVENcIjpcIlwiKTtzd2l0Y2goZCl7Y2FzZSBvOnJldHVybiBjPyQoMSwwKTokKDMxLDExKTtjYXNlIHU6cmV0dXJuIGM/JCgxLE0pOiQoMCxNKzEpO2Nhc2Ugczp2YXIgdj10aGlzLiRsb2NhbGUoKS53ZWVrU3RhcnR8fDAsUz0oeTx2P3krNzp5KS12O3JldHVybiAkKGM/bS1TOm0rKDYtUyksTSk7Y2FzZSBpOmNhc2UgZjpyZXR1cm4gbChEK1wiSG91cnNcIiwwKTtjYXNlIHI6cmV0dXJuIGwoRCtcIk1pbnV0ZXNcIiwxKTtjYXNlIG46cmV0dXJuIGwoRCtcIlNlY29uZHNcIiwyKTtjYXNlIGU6cmV0dXJuIGwoRCtcIk1pbGxpc2Vjb25kc1wiLDMpO2RlZmF1bHQ6cmV0dXJuIHRoaXMuY2xvbmUoKX19LCQuZW5kT2Y9ZnVuY3Rpb24odCl7cmV0dXJuIHRoaXMuc3RhcnRPZih0LCExKX0sJC4kc2V0PWZ1bmN0aW9uKHMsYSl7dmFyIGgsYz1nLnAocyksZD1cInNldFwiKyh0aGlzLiR1P1wiVVRDXCI6XCJcIiksJD0oaD17fSxoW2ldPWQrXCJEYXRlXCIsaFtmXT1kK1wiRGF0ZVwiLGhbdV09ZCtcIk1vbnRoXCIsaFtvXT1kK1wiRnVsbFllYXJcIixoW3JdPWQrXCJIb3Vyc1wiLGhbbl09ZCtcIk1pbnV0ZXNcIixoW2VdPWQrXCJTZWNvbmRzXCIsaFt0XT1kK1wiTWlsbGlzZWNvbmRzXCIsaClbY10sbD1jPT09aT90aGlzLiREKyhhLXRoaXMuJFcpOmE7aWYoYz09PXV8fGM9PT1vKXt2YXIgeT10aGlzLmNsb25lKCkuc2V0KGYsMSk7eS4kZFskXShsKSx5LmluaXQoKSx0aGlzLiRkPXkuc2V0KGYsTWF0aC5taW4odGhpcy4kRCx5LmRheXNJbk1vbnRoKCkpKS4kZH1lbHNlICQmJnRoaXMuJGRbJF0obCk7cmV0dXJuIHRoaXMuaW5pdCgpLHRoaXN9LCQuc2V0PWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRoaXMuY2xvbmUoKS4kc2V0KHQsZSl9LCQuZ2V0PWZ1bmN0aW9uKHQpe3JldHVybiB0aGlzW2cucCh0KV0oKX0sJC5hZGQ9ZnVuY3Rpb24odCxhKXt2YXIgZixoPXRoaXM7dD1OdW1iZXIodCk7dmFyIGM9Zy5wKGEpLGQ9ZnVuY3Rpb24oZSl7dmFyIG49dihoKTtyZXR1cm4gZy53KG4uZGF0ZShuLmRhdGUoKStNYXRoLnJvdW5kKGUqdCkpLGgpfTtpZihjPT09dSlyZXR1cm4gdGhpcy5zZXQodSx0aGlzLiRNK3QpO2lmKGM9PT1vKXJldHVybi
14775B0aGlzLnNldChvLHRoaXMuJHkrdCk7aWYoYz09PWkpcmV0dXJuIGQoMSk7aWYoYz09PXMpcmV0dXJuIGQoNyk7dmFyICQ9KGY9e30sZltuXT02ZTQsZltyXT0zNmU1LGZbZV09MWUzLGYpW2NdfHwxLGw9dGhpcy4kZC5nZXRUaW1lKCkrdCokO3JldHVybiBnLncobCx0aGlzKX0sJC5zdWJ0cmFjdD1mdW5jdGlvbih0LGUpe3JldHVybiB0aGlzLmFkZCgtMSp0LGUpfSwkLmZvcm1hdD1mdW5jdGlvbih0KXt2YXIgZT10aGlzO2lmKCF0aGlzLmlzVmFsaWQoKSlyZXR1cm5cIkludmFsaWQgRGF0ZVwiO3ZhciBuPXR8fFwiWVlZWS1NTS1ERFRISDptbTpzc1pcIixyPWcueih0aGlzKSxpPXRoaXMuJGxvY2FsZSgpLHM9dGhpcy4kSCx1PXRoaXMuJG0sYT10aGlzLiRNLG89aS53ZWVrZGF5cyxmPWkubW9udGhzLGg9ZnVuY3Rpb24odCxyLGkscyl7cmV0dXJuIHQmJih0W3JdfHx0KGUsbikpfHxpW3JdLnN1YnN0cigwLHMpfSxkPWZ1bmN0aW9uKHQpe3JldHVybiBnLnMocyUxMnx8MTIsdCxcIjBcIil9LCQ9aS5tZXJpZGllbXx8ZnVuY3Rpb24odCxlLG4pe3ZhciByPXQ8MTI/XCJBTVwiOlwiUE1cIjtyZXR1cm4gbj9yLnRvTG93ZXJDYXNlKCk6cn0sbD17WVk6U3RyaW5nKHRoaXMuJHkpLnNsaWNlKC0yKSxZWVlZOnRoaXMuJHksTTphKzEsTU06Zy5zKGErMSwyLFwiMFwiKSxNTU06aChpLm1vbnRoc1Nob3J0LGEsZiwzKSxNTU1NOmgoZixhKSxEOnRoaXMuJEQsREQ6Zy5zKHRoaXMuJEQsMixcIjBcIiksZDpTdHJpbmcodGhpcy4kVyksZGQ6aChpLndlZWtkYXlzTWluLHRoaXMuJFcsbywyKSxkZGQ6aChpLndlZWtkYXlzU2hvcnQsdGhpcy4kVyxvLDMpLGRkZGQ6b1t0aGlzLiRXXSxIOlN0cmluZyhzKSxISDpnLnMocywyLFwiMFwiKSxoOmQoMSksaGg6ZCgyKSxhOiQocyx1LCEwKSxBOiQocyx1LCExKSxtOlN0cmluZyh1KSxtbTpnLnModSwyLFwiMFwiKSxzOlN0cmluZyh0aGlzLiRzKSxzczpnLnModGhpcy4kcywyLFwiMFwiKSxTU1M6Zy5zKHRoaXMuJG1zLDMsXCIwXCIpLFo6cn07cmV0dXJuIG4ucmVwbGFjZShjLGZ1bmN0aW9uKHQsZSl7cmV0dXJuIGV8fGxbdF18fHIucmVwbGFjZShcIjpcIixcIlwiKX0pfSwkLnV0Y09mZnNldD1mdW5jdGlvbigpe3JldHVybiAxNSotTWF0aC5yb3VuZCh0aGlzLiRkLmdldFRpbWV6b25lT2Zmc2V0KCkvMTUpfSwkLmRpZmY9ZnVuY3Rpb24odCxmLGgpe3ZhciBjLGQ9Zy5wKGYpLCQ9dih0KSxsPTZlNCooJC51dGNPZmZzZXQoKS10aGlzLnV0Y09mZnNldCgpKSx5PXRoaXMtJCxNPWcubSh0aGlzLCQpO3JldHVybiBNPShjPXt9LGNbb109TS8xMixjW3VdPU0sY1thXT1NLzMsY1tzXT0oeS1sKS82MDQ4ZTUsY1tpXT0oeS1sKS84NjRlNSxjW3JdPXkvMzZlNSxjW25dPXkvNmU0LGNbZV09eS8xZTMsYylbZF18fHksaD9NOmcuYShNKX0sJC5kYXlzSW5Nb250aD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmVuZE9mKHUpLiREfSwkLiRsb2NhbGU9ZnVuY3Rpb24oKXtyZXR1cm4gTVt0aGlzLiRMXX0sJC5sb2NhbGU9ZnVuY3Rpb24odCxlKXtpZighdClyZXR1cm4gdGhpcy4kTDt2YXIgbj10aGlzLmNsb25lKCkscj1EKHQsZSwhMCk7cmV0dXJuIHImJihuLiRMPXIpLG59LCQuY2xvbmU9ZnVuY3Rpb24oKXtyZXR1cm4gZy53KHRoaXMuJGQsdGhpcyl9LCQudG9EYXRlPWZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBEYXRlKHRoaXMudmFsdWVPZigpKX0sJC50b0pTT049ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1ZhbGlkKCk/dGhpcy50b0lTT1N0cmluZygpOm51bGx9LCQudG9JU09TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kZC50b0lTT1N0cmluZygpfSwkLnRvU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJGQudG9VVENTdHJpbmcoKX0sZH0oKSxwPVMucHJvdG90eXBlO3JldHVybiB2LnByb3RvdHlwZT1wLFtbXCIkbXNcIix0XSxbXCIkc1wiLGVdLFtcIiRtXCIsbl0sW1wiJEhcIixyXSxbXCIkV1wiLGldLFtcIiRNXCIsdV0sW1wiJHlcIixvXSxbXCIkRFwiLGZdXS5mb3JFYWNoKGZ1bmN0aW9uKHQpe3BbdFsxXV09ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuJGcoZSx0WzBdLHRbMV0pfX0pLHYuZXh0ZW5kPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHQuJGl8fCh0KGUsUyx2KSx0LiRpPSEwKSx2fSx2LmxvY2FsZT1ELHYuaXNEYXlqcz1tLHYudW5peD1mdW5jdGlvbih0KXtyZXR1cm4gdigxZTMqdCl9LHYuZW49TVt5XSx2LkxzPU0sdi5wPXt9LHZ9KTtcbiIsIiFmdW5jdGlvbihlLHMpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPXMocmVxdWlyZShcImRheWpzXCIpKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKFtcImRheWpzXCJdLHMpOmUuZGF5anNfbG9jYWxlX2VzPXMoZS5kYXlqcyl9KHRoaXMsZnVuY3Rpb24oZSl7XCJ1c2Ugc3RyaWN0XCI7ZT1lJiZlLmhhc093blByb3BlcnR5KFwiZGVmYXVsdFwiKT9lLmRlZmF1bHQ6ZTt2YXIgcz17bmFtZTpcImVzXCIsbW9udGhzU2hvcnQ6XCJlbmVfZmViX21hcl9hYnJfbWF5X2p1bl9qdWxfYWdvX3NlcF9vY3Rfbm92X2RpY1wiLnNwbGl0KFwiX1wiKSx3ZWVrZGF5czpcImRvbWluZ29fbHVuZXNfbWFydGVzX21pw6lyY29sZXNfanVldmVzX3ZpZXJuZXNfc8OhYmFkb1wiLnNwbGl0KFwiX1wiKSx3ZWVrZGF5c1Nob3J0OlwiZG9tLl9sdW4uX21hci5fbWnDqS5fanVlLl92aWUuX3PDoWIuXCIuc3BsaXQoXCJfXCIpLHdlZWtkYXlzTWluOlwiZG9fbHVfbWFfbWlfanVfdmlfc8OhXCIuc3BsaXQoXCJfXCIpLG1vbnRoczpcIkVuZXJvX0ZlYnJlcm9fTWFyem9fQWJyaWxfTWF5b19KdW5pb19KdWxpb19BZ29zdG9fU2VwdGllbWJyZV9PY3R1YnJlX05vdmllbWJyZV9EaWNpZW1icmVcIi5zcGxpdChcIl9cIiksd2Vla1N0YXJ0OjEsZm9ybWF0czp7TFQ6XCJIOm1tXCIsTFRTOlwiSDptbTpzc1wiLEw6XCJERC9NTS9ZWVlZXCIsTEw6XCJEIFtkZV0gTU1NTSBbZGVdIFlZWVlcIixMTEw6XCJEIFtkZV0gTU1NTSBbZGVdIFlZWVkgSDptbVwiLExMTEw6XCJkZGRkLCBEIFtkZV0gTU1NTSBbZGVdIFlZWVkgSDptbVwifSxyZWxhdGl2ZVRpbWU6e2Z1dHVyZTpcImVuICVzXCIscGFzdDpcImhhY2UgJXNcIixzOlwidW5vcyBzZWd1bmRvc1wiLG06XCJ1biBtaW51dG9
14775cIixtbTpcIiVkIG1pbnV0b3NcIixoOlwidW5hIGhvcmFcIixoaDpcIiVkIGhvcmFzXCIsZDpcInVuIGTDrWFcIixkZDpcIiVkIGTDrWFzXCIsTTpcInVuIG1lc1wiLE1NOlwiJWQgbWVzZXNcIix5OlwidW4gYcOxb1wiLHl5OlwiJWQgYcOxb3NcIn0sb3JkaW5hbDpmdW5jdGlvbihlKXtyZXR1cm4gZStcIsK6XCJ9fTtyZXR1cm4gZS5sb2NhbGUocyxudWxsLCEwKSxzfSk7XG4iLCIhZnVuY3Rpb24ocix0KXtcIm9iamVjdFwiPT10eXBlb2YgZXhwb3J0cyYmXCJ1bmRlZmluZWRcIiE9dHlwZW9mIG1vZHVsZT9tb2R1bGUuZXhwb3J0cz10KCk6XCJmdW5jdGlvblwiPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kP2RlZmluZSh0KTpyLmRheWpzX3BsdWdpbl9yZWxhdGl2ZVRpbWU9dCgpfSh0aGlzLGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7cmV0dXJuIGZ1bmN0aW9uKHIsdCxlKXtyPXJ8fHt9O3ZhciBuPXQucHJvdG90eXBlLG89e2Z1dHVyZTpcImluICVzXCIscGFzdDpcIiVzIGFnb1wiLHM6XCJhIGZldyBzZWNvbmRzXCIsbTpcImEgbWludXRlXCIsbW06XCIlZCBtaW51dGVzXCIsaDpcImFuIGhvdXJcIixoaDpcIiVkIGhvdXJzXCIsZDpcImEgZGF5XCIsZGQ6XCIlZCBkYXlzXCIsTTpcImEgbW9udGhcIixNTTpcIiVkIG1vbnRoc1wiLHk6XCJhIHllYXJcIix5eTpcIiVkIHllYXJzXCJ9O2Z1bmN0aW9uIGkocix0LGUsbyl7cmV0dXJuIG4uZnJvbVRvQmFzZShyLHQsZSxvKX1lLmVuLnJlbGF0aXZlVGltZT1vLG4uZnJvbVRvQmFzZT1mdW5jdGlvbih0LG4saSxkLHUpe2Zvcih2YXIgYSxmLHMsbD1pLiRsb2NhbGUoKS5yZWxhdGl2ZVRpbWV8fG8saD1yLnRocmVzaG9sZHN8fFt7bDpcInNcIixyOjQ0LGQ6XCJzZWNvbmRcIn0se2w6XCJtXCIscjo4OX0se2w6XCJtbVwiLHI6NDQsZDpcIm1pbnV0ZVwifSx7bDpcImhcIixyOjg5fSx7bDpcImhoXCIscjoyMSxkOlwiaG91clwifSx7bDpcImRcIixyOjM1fSx7bDpcImRkXCIscjoyNSxkOlwiZGF5XCJ9LHtsOlwiTVwiLHI6NDV9LHtsOlwiTU1cIixyOjEwLGQ6XCJtb250aFwifSx7bDpcInlcIixyOjE3fSx7bDpcInl5XCIsZDpcInllYXJcIn1dLG09aC5sZW5ndGgsYz0wO2M8bTtjKz0xKXt2YXIgeT1oW2NdO3kuZCYmKGE9ZD9lKHQpLmRpZmYoaSx5LmQsITApOmkuZGlmZih0LHkuZCwhMCkpO3ZhciBwPShyLnJvdW5kaW5nfHxNYXRoLnJvdW5kKShNYXRoLmFicyhhKSk7aWYocz1hPjAscDw9eS5yfHwheS5yKXtwPD0xJiZjPjAmJih5PWhbYy0xXSk7dmFyIHY9bFt5LmxdO3UmJihwPXUoXCJcIitwKSksZj1cInN0cmluZ1wiPT10eXBlb2Ygdj92LnJlcGxhY2UoXCIlZFwiLHApOnYocCxuLHkubCxzKTticmVha319aWYobilyZXR1cm4gZjt2YXIgTT1zP2wuZnV0dXJlOmwucGFzdDtyZXR1cm5cImZ1bmN0aW9uXCI9PXR5cGVvZiBNP00oZik6TS5yZXBsYWNlKFwiJXNcIixmKX0sbi50bz1mdW5jdGlvbihyLHQpe3JldHVybiBpKHIsdCx0aGlzLCEwKX0sbi5mcm9tPWZ1bmN0aW9uKHIsdCl7cmV0dXJuIGkocix0LHRoaXMpfTt2YXIgZD1mdW5jdGlvbihyKXtyZXR1cm4gci4kdT9lLnV0YygpOmUoKX07bi50b05vdz1mdW5jdGlvbihyKXtyZXR1cm4gdGhpcy50byhkKHRoaXMpLHIpfSxuLmZyb21Ob3c9ZnVuY3Rpb24ocil7cmV0dXJuIHRoaXMuZnJvbShkKHRoaXMpLHIpfX19KTtcbiIsIkwuQ29udHJvbC5GdWxsc2NyZWVuID0gTC5Db250cm9sLmV4dGVuZCh7XG4gICAgb3B0aW9uczoge1xuICAgICAgICBwb3NpdGlvbjogJ3RvcGxlZnQnLFxuICAgICAgICB0aXRsZToge1xuICAgICAgICAgICAgJ2ZhbHNlJzogJ1ZpZXcgRnVsbHNjcmVlbicsXG4gICAgICAgICAgICAndHJ1ZSc6ICdFeGl0IEZ1bGxzY3JlZW4nXG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtZnVsbHNjcmVlbiBsZWFmbGV0LWJhciBsZWFmbGV0LWNvbnRyb2wnKTtcblxuICAgICAgICB0aGlzLmxpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgJ2xlYWZsZXQtY29udHJvbC1mdWxsc2NyZWVuLWJ1dHRvbiBsZWFmbGV0LWJhci1wYXJ0JywgY29udGFpbmVyKTtcbiAgICAgICAgdGhpcy5saW5rLmhyZWYgPSAnIyc7XG5cbiAgICAgICAgdGhpcy5fbWFwID0gbWFwO1xuICAgICAgICB0aGlzLl9tYXAub24oJ2Z1bGxzY3JlZW5jaGFuZ2UnLCB0aGlzLl90b2dnbGVUaXRsZSwgdGhpcyk7XG4gICAgICAgIHRoaXMuX3RvZ2dsZVRpdGxlKCk7XG5cbiAgICAgICAgTC5Eb21FdmVudC5vbih0aGlzLmxpbmssICdjbGljaycsIHRoaXMuX2NsaWNrLCB0aGlzKTtcblxuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xuICAgIH0sXG5cbiAgICBfY2xpY2s6IGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKGUpO1xuICAgICAgICBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuICAgICAgICB0aGlzLl9tYXAudG9nZ2xlRnVsbHNjcmVlbih0aGlzLm9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBfdG9nZ2xlVGl0bGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLmxpbmsudGl0bGUgPSB0aGlzLm9wdGlvbnMudGl0bGVbdGhpcy5fbWFwLmlzRnVsbHNjcmVlbigpXTtcbiAgICB9XG59KTtcblxuTC5NYXAuaW5jbHVkZSh7XG4gICAgaXNGdWxsc2NyZWVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9pc0Z1bGxzY3JlZW4gfHwgZmFsc2U7XG4gICAgfSxcblxuICAgIHRvZ2dsZUZ1bGxzY3JlZW46IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHZhciBjb250YWluZXIgPSB0aGlzLmdldENvbnRhaW5lcigpO1xuICAgICAgICBpZiAodGhpcy5pc0Z1bGxzY3JlZW4oKSkge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5wc2V1ZG9GdWxsc2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fZGlzYWJsZVBzZXVkb0Z1bGxzY3JlZW4oY29udGFpbmVyKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQuZXhpdEZ1bGxzY3JlZW4pIHtcbiAgICAgICAgICAgICAgICBkb2
14775N1bWVudC5leGl0RnVsbHNjcmVlbigpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChkb2N1bWVudC5tb3pDYW5jZWxGdWxsU2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChkb2N1bWVudC53ZWJraXRDYW5jZWxGdWxsU2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQud2Via2l0Q2FuY2VsRnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChkb2N1bWVudC5tc0V4aXRGdWxsc2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQubXNFeGl0RnVsbHNjcmVlbigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9kaXNhYmxlUHNldWRvRnVsbHNjcmVlbihjb250YWluZXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5wc2V1ZG9GdWxsc2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fZW5hYmxlUHNldWRvRnVsbHNjcmVlbihjb250YWluZXIpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChjb250YWluZXIucmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICAgICAgICAgICAgICBjb250YWluZXIucmVxdWVzdEZ1bGxzY3JlZW4oKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY29udGFpbmVyLm1velJlcXVlc3RGdWxsU2NyZWVuKSB7XG4gICAgICAgICAgICAgICAgY29udGFpbmVyLm1velJlcXVlc3RGdWxsU2NyZWVuKCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGNvbnRhaW5lci53ZWJraXRSZXF1ZXN0RnVsbHNjcmVlbikge1xuICAgICAgICAgICAgICAgIGNvbnRhaW5lci53ZWJraXRSZXF1ZXN0RnVsbHNjcmVlbihFbGVtZW50LkFMTE9XX0tFWUJPQVJEX0lOUFVUKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY29udGFpbmVyLm1zUmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICAgICAgICAgICAgICBjb250YWluZXIubXNSZXF1ZXN0RnVsbHNjcmVlbigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9lbmFibGVQc2V1ZG9GdWxsc2NyZWVuKGNvbnRhaW5lcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgIH0sXG5cbiAgICBfZW5hYmxlUHNldWRvRnVsbHNjcmVlbjogZnVuY3Rpb24gKGNvbnRhaW5lcikge1xuICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1wc2V1ZG8tZnVsbHNjcmVlbicpO1xuICAgICAgICB0aGlzLl9zZXRGdWxsc2NyZWVuKHRydWUpO1xuICAgICAgICB0aGlzLmZpcmUoJ2Z1bGxzY3JlZW5jaGFuZ2UnKTtcbiAgICB9LFxuXG4gICAgX2Rpc2FibGVQc2V1ZG9GdWxsc2NyZWVuOiBmdW5jdGlvbiAoY29udGFpbmVyKSB7XG4gICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXBzZXVkby1mdWxsc2NyZWVuJyk7XG4gICAgICAgIHRoaXMuX3NldEZ1bGxzY3JlZW4oZmFsc2UpO1xuICAgICAgICB0aGlzLmZpcmUoJ2Z1bGxzY3JlZW5jaGFuZ2UnKTtcbiAgICB9LFxuXG4gICAgX3NldEZ1bGxzY3JlZW46IGZ1bmN0aW9uKGZ1bGxzY3JlZW4pIHtcbiAgICAgICAgdGhpcy5faXNGdWxsc2NyZWVuID0gZnVsbHNjcmVlbjtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IHRoaXMuZ2V0Q29udGFpbmVyKCk7XG4gICAgICAgIGlmIChmdWxsc2NyZWVuKSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1mdWxsc2NyZWVuLW9uJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1mdWxsc2NyZWVuLW9uJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5pbnZhbGlkYXRlU2l6ZSgpO1xuICAgIH0sXG5cbiAgICBfb25GdWxsc2NyZWVuQ2hhbmdlOiBmdW5jdGlvbiAoZSkge1xuICAgICAgICB2Y
14775XIgZnVsbHNjcmVlbkVsZW1lbnQgPVxuICAgICAgICAgICAgZG9jdW1lbnQuZnVsbHNjcmVlbkVsZW1lbnQgfHxcbiAgICAgICAgICAgIGRvY3VtZW50Lm1vekZ1bGxTY3JlZW5FbGVtZW50IHx8XG4gICAgICAgICAgICBkb2N1bWVudC53ZWJraXRGdWxsc2NyZWVuRWxlbWVudCB8fFxuICAgICAgICAgICAgZG9jdW1lbnQubXNGdWxsc2NyZWVuRWxlbWVudDtcblxuICAgICAgICBpZiAoZnVsbHNjcmVlbkVsZW1lbnQgPT09IHRoaXMuZ2V0Q29udGFpbmVyKCkgJiYgIXRoaXMuX2lzRnVsbHNjcmVlbikge1xuICAgICAgICAgICAgdGhpcy5fc2V0RnVsbHNjcmVlbih0cnVlKTtcbiAgICAgICAgICAgIHRoaXMuZmlyZSgnZnVsbHNjcmVlbmNoYW5nZScpO1xuICAgICAgICB9IGVsc2UgaWYgKGZ1bGxzY3JlZW5FbGVtZW50ICE9PSB0aGlzLmdldENvbnRhaW5lcigpICYmIHRoaXMuX2lzRnVsbHNjcmVlbikge1xuICAgICAgICAgICAgdGhpcy5fc2V0RnVsbHNjcmVlbihmYWxzZSk7XG4gICAgICAgICAgICB0aGlzLmZpcmUoJ2Z1bGxzY3JlZW5jaGFuZ2UnKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuICAgIGZ1bGxzY3JlZW5Db250cm9sOiBmYWxzZVxufSk7XG5cbkwuTWFwLmFkZEluaXRIb29rKGZ1bmN0aW9uICgpIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLmZ1bGxzY3JlZW5Db250cm9sKSB7XG4gICAgICAgIHRoaXMuZnVsbHNjcmVlbkNvbnRyb2wgPSBuZXcgTC5Db250cm9sLkZ1bGxzY3JlZW4odGhpcy5vcHRpb25zLmZ1bGxzY3JlZW5Db250cm9sKTtcbiAgICAgICAgdGhpcy5hZGRDb250cm9sKHRoaXMuZnVsbHNjcmVlbkNvbnRyb2wpO1xuICAgIH1cblxuICAgIHZhciBmdWxsc2NyZWVuY2hhbmdlO1xuXG4gICAgaWYgKCdvbmZ1bGxzY3JlZW5jaGFuZ2UnIGluIGRvY3VtZW50KSB7XG4gICAgICAgIGZ1bGxzY3JlZW5jaGFuZ2UgPSAnZnVsbHNjcmVlbmNoYW5nZSc7XG4gICAgfSBlbHNlIGlmICgnb25tb3pmdWxsc2NyZWVuY2hhbmdlJyBpbiBkb2N1bWVudCkge1xuICAgICAgICBmdWxsc2NyZWVuY2hhbmdlID0gJ21vemZ1bGxzY3JlZW5jaGFuZ2UnO1xuICAgIH0gZWxzZSBpZiAoJ29ud2Via2l0ZnVsbHNjcmVlbmNoYW5nZScgaW4gZG9jdW1lbnQpIHtcbiAgICAgICAgZnVsbHNjcmVlbmNoYW5nZSA9ICd3ZWJraXRmdWxsc2NyZWVuY2hhbmdlJztcbiAgICB9IGVsc2UgaWYgKCdvbm1zZnVsbHNjcmVlbmNoYW5nZScgaW4gZG9jdW1lbnQpIHtcbiAgICAgICAgZnVsbHNjcmVlbmNoYW5nZSA9ICdNU0Z1bGxzY3JlZW5DaGFuZ2UnO1xuICAgIH1cblxuICAgIGlmIChmdWxsc2NyZWVuY2hhbmdlKSB7XG4gICAgICAgIHZhciBvbkZ1bGxzY3JlZW5DaGFuZ2UgPSBMLmJpbmQodGhpcy5fb25GdWxsc2NyZWVuQ2hhbmdlLCB0aGlzKTtcblxuICAgICAgICB0aGlzLndoZW5SZWFkeShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBMLkRvbUV2ZW50Lm9uKGRvY3VtZW50LCBmdWxsc2NyZWVuY2hhbmdlLCBvbkZ1bGxzY3JlZW5DaGFuZ2UpO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLm9uKCd1bmxvYWQnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBMLkRvbUV2ZW50Lm9mZihkb2N1bWVudCwgZnVsbHNjcmVlbmNoYW5nZSwgb25GdWxsc2NyZWVuQ2hhbmdlKTtcbiAgICAgICAgfSk7XG4gICAgfVxufSk7XG5cbkwuY29udHJvbC5mdWxsc2NyZWVuID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IEwuQ29udHJvbC5GdWxsc2NyZWVuKG9wdGlvbnMpO1xufTtcbiIsIi8qIEBwcmVzZXJ2ZVxuICogTGVhZmxldCAxLjYuMCwgYSBKUyBsaWJyYXJ5IGZvciBpbnRlcmFjdGl2ZSBtYXBzLiBodHRwOi8vbGVhZmxldGpzLmNvbVxuICogKGMpIDIwMTAtMjAxOSBWbGFkaW1pciBBZ2Fmb25raW4sIChjKSAyMDEwLTIwMTEgQ2xvdWRNYWRlXG4gKi9cblxuKGZ1bmN0aW9uIChnbG9iYWwsIGZhY3RvcnkpIHtcblx0dHlwZW9mIGV4cG9ydHMgPT09ICdvYmplY3QnICYmIHR5cGVvZiBtb2R1bGUgIT09ICd1bmRlZmluZWQnID8gZmFjdG9yeShleHBvcnRzKSA6XG5cdHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCA/IGRlZmluZShbJ2V4cG9ydHMnXSwgZmFjdG9yeSkgOlxuXHQoZmFjdG9yeSgoZ2xvYmFsLkwgPSB7fSkpKTtcbn0odGhpcywgKGZ1bmN0aW9uIChleHBvcnRzKSB7ICd1c2Ugc3RyaWN0JztcblxudmFyIHZlcnNpb24gPSBcIjEuNi4wXCI7XG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgVXRpbFxyXG4gKlxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zLCB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICovXHJcblxyXG52YXIgZnJlZXplID0gT2JqZWN0LmZyZWV6ZTtcclxuT2JqZWN0LmZyZWV6ZSA9IGZ1bmN0aW9uIChvYmopIHsgcmV0dXJuIG9iajsgfTtcclxuXHJcbi8vIEBmdW5jdGlvbiBleHRlbmQoZGVzdDogT2JqZWN0LCBzcmM/OiBPYmplY3QpOiBPYmplY3RcclxuLy8gTWVyZ2VzIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBgc3JjYCBvYmplY3QgKG9yIG11bHRpcGxlIG9iamVjdHMpIGludG8gYGRlc3RgIG9iamVjdCBhbmQgcmV0dXJucyB0aGUgbGF0dGVyLiBIYXMgYW4gYEwuZXh0ZW5kYCBzaG9ydGN1dC5cclxuZnVuY3Rpb24gZXh0ZW5kKGRlc3QpIHtcclxuXHR2YXIgaSwgaiwgbGVuLCBzcmM7XHJcblxyXG5cdGZvciAoaiA9IDEsIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGogPCBsZW47IGorKykge1xyXG5cdFx0c3JjID0gYXJndW1lbnRzW2pdO1xyXG5cdFx0Zm9yIChpIGluIHNyYykge1xyXG5cdFx0XHRkZXN0W2ldID0gc3JjW2ldO1xyXG5cdFx0fVxyXG5cdH1cclxuXHRyZXR1cm4gZGVzdDtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGNyZWF0ZShwcm90bzogT2JqZWN0LCBwcm9wZXJ0aWVzPzogT2JqZWN0KTogT2JqZWN0XHJcbi8vIENvbXBhdGliaWxpdHkgcG9seWZpbGwgZm9yIFtPYmplY3QuY3JlYXRlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9PYmplY3QvY3JlYXRlKVxyXG52YXIgY3JlYXRlID0gT2JqZWN0LmNyZWF0ZSB8fCAoZnVuY3Rpb24gKCkge1xyXG5cdGZ1bmN0aW9uIEYoKSB7fVxyXG5cdHJldHVybi
14775BmdW5jdGlvbiAocHJvdG8pIHtcclxuXHRcdEYucHJvdG90eXBlID0gcHJvdG87XHJcblx0XHRyZXR1cm4gbmV3IEYoKTtcclxuXHR9O1xyXG59KSgpO1xyXG5cclxuLy8gQGZ1bmN0aW9uIGJpbmQoZm46IEZ1bmN0aW9uLCDigKYpOiBGdW5jdGlvblxyXG4vLyBSZXR1cm5zIGEgbmV3IGZ1bmN0aW9uIGJvdW5kIHRvIHRoZSBhcmd1bWVudHMgcGFzc2VkLCBsaWtlIFtGdW5jdGlvbi5wcm90b3R5cGUuYmluZF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvRnVuY3Rpb24vYmluZCkuXHJcbi8vIEhhcyBhIGBMLmJpbmQoKWAgc2hvcnRjdXQuXHJcbmZ1bmN0aW9uIGJpbmQoZm4sIG9iaikge1xyXG5cdHZhciBzbGljZSA9IEFycmF5LnByb3RvdHlwZS5zbGljZTtcclxuXHJcblx0aWYgKGZuLmJpbmQpIHtcclxuXHRcdHJldHVybiBmbi5iaW5kLmFwcGx5KGZuLCBzbGljZS5jYWxsKGFyZ3VtZW50cywgMSkpO1xyXG5cdH1cclxuXHJcblx0dmFyIGFyZ3MgPSBzbGljZS5jYWxsKGFyZ3VtZW50cywgMik7XHJcblxyXG5cdHJldHVybiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gZm4uYXBwbHkob2JqLCBhcmdzLmxlbmd0aCA/IGFyZ3MuY29uY2F0KHNsaWNlLmNhbGwoYXJndW1lbnRzKSkgOiBhcmd1bWVudHMpO1xyXG5cdH07XHJcbn1cclxuXHJcbi8vIEBwcm9wZXJ0eSBsYXN0SWQ6IE51bWJlclxyXG4vLyBMYXN0IHVuaXF1ZSBJRCB1c2VkIGJ5IFtgc3RhbXAoKWBdKCN1dGlsLXN0YW1wKVxyXG52YXIgbGFzdElkID0gMDtcclxuXHJcbi8vIEBmdW5jdGlvbiBzdGFtcChvYmo6IE9iamVjdCk6IE51bWJlclxyXG4vLyBSZXR1cm5zIHRoZSB1bmlxdWUgSUQgb2YgYW4gb2JqZWN0LCBhc3NpZ25pbmcgaXQgb25lIGlmIGl0IGRvZXNuJ3QgaGF2ZSBpdC5cclxuZnVuY3Rpb24gc3RhbXAob2JqKSB7XHJcblx0Lyplc2xpbnQtZGlzYWJsZSAqL1xyXG5cdG9iai5fbGVhZmxldF9pZCA9IG9iai5fbGVhZmxldF9pZCB8fCArK2xhc3RJZDtcclxuXHRyZXR1cm4gb2JqLl9sZWFmbGV0X2lkO1xyXG5cdC8qIGVzbGludC1lbmFibGUgKi9cclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIHRocm90dGxlKGZuOiBGdW5jdGlvbiwgdGltZTogTnVtYmVyLCBjb250ZXh0OiBPYmplY3QpOiBGdW5jdGlvblxyXG4vLyBSZXR1cm5zIGEgZnVuY3Rpb24gd2hpY2ggZXhlY3V0ZXMgZnVuY3Rpb24gYGZuYCB3aXRoIHRoZSBnaXZlbiBzY29wZSBgY29udGV4dGBcclxuLy8gKHNvIHRoYXQgdGhlIGB0aGlzYCBrZXl3b3JkIHJlZmVycyB0byBgY29udGV4dGAgaW5zaWRlIGBmbmAncyBjb2RlKS4gVGhlIGZ1bmN0aW9uXHJcbi8vIGBmbmAgd2lsbCBiZSBjYWxsZWQgbm8gbW9yZSB0aGFuIG9uZSB0aW1lIHBlciBnaXZlbiBhbW91bnQgb2YgYHRpbWVgLiBUaGUgYXJndW1lbnRzXHJcbi8vIHJlY2VpdmVkIGJ5IHRoZSBib3VuZCBmdW5jdGlvbiB3aWxsIGJlIGFueSBhcmd1bWVudHMgcGFzc2VkIHdoZW4gYmluZGluZyB0aGVcclxuLy8gZnVuY3Rpb24sIGZvbGxvd2VkIGJ5IGFueSBhcmd1bWVudHMgcGFzc2VkIHdoZW4gaW52b2tpbmcgdGhlIGJvdW5kIGZ1bmN0aW9uLlxyXG4vLyBIYXMgYW4gYEwudGhyb3R0bGVgIHNob3J0Y3V0LlxyXG5mdW5jdGlvbiB0aHJvdHRsZShmbiwgdGltZSwgY29udGV4dCkge1xyXG5cdHZhciBsb2NrLCBhcmdzLCB3cmFwcGVyRm4sIGxhdGVyO1xyXG5cclxuXHRsYXRlciA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdC8vIHJlc2V0IGxvY2sgYW5kIGNhbGwgaWYgcXVldWVkXHJcblx0XHRsb2NrID0gZmFsc2U7XHJcblx0XHRpZiAoYXJncykge1xyXG5cdFx0XHR3cmFwcGVyRm4uYXBwbHkoY29udGV4dCwgYXJncyk7XHJcblx0XHRcdGFyZ3MgPSBmYWxzZTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHR3cmFwcGVyRm4gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAobG9jaykge1xyXG5cdFx0XHQvLyBjYWxsZWQgdG9vIHNvb24sIHF1ZXVlIHRvIGNhbGwgbGF0ZXJcclxuXHRcdFx0YXJncyA9IGFyZ3VtZW50cztcclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHQvLyBjYWxsIGFuZCBsb2NrIHVudGlsIGxhdGVyXHJcblx0XHRcdGZuLmFwcGx5KGNvbnRleHQsIGFyZ3VtZW50cyk7XHJcblx0XHRcdHNldFRpbWVvdXQobGF0ZXIsIHRpbWUpO1xyXG5cdFx0XHRsb2NrID0gdHJ1ZTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHRyZXR1cm4gd3JhcHBlckZuO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gd3JhcE51bShudW06IE51bWJlciwgcmFuZ2U6IE51bWJlcltdLCBpbmNsdWRlTWF4PzogQm9vbGVhbik6IE51bWJlclxyXG4vLyBSZXR1cm5zIHRoZSBudW1iZXIgYG51bWAgbW9kdWxvIGByYW5nZWAgaW4gc3VjaCBhIHdheSBzbyBpdCBsaWVzIHdpdGhpblxyXG4vLyBgcmFuZ2VbMF1gIGFuZCBgcmFuZ2VbMV1gLiBUaGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSBhbHdheXMgc21hbGxlciB0aGFuXHJcbi8vIGByYW5nZVsxXWAgdW5sZXNzIGBpbmNsdWRlTWF4YCBpcyBzZXQgdG8gYHRydWVgLlxyXG5mdW5jdGlvbiB3cmFwTnVtKHgsIHJhbmdlLCBpbmNsdWRlTWF4KSB7XHJcblx0dmFyIG1heCA9IHJhbmdlWzFdLFxyXG5cdCAgICBtaW4gPSByYW5nZVswXSxcclxuXHQgICAgZCA9IG1heCAtIG1pbjtcclxuXHRyZXR1cm4geCA9PT0gbWF4ICYmIGluY2x1ZGVNYXggPyB4IDogKCh4IC0gbWluKSAlIGQgKyBkKSAlIGQgKyBtaW47XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBmYWxzZUZuKCk6IEZ1bmN0aW9uXHJcbi8vIFJldHVybnMgYSBmdW5jdGlvbiB3aGljaCBhbHdheXMgcmV0dXJucyBgZmFsc2VgLlxyXG5mdW5jdGlvbiBmYWxzZUZuKCkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcbi8vIEBmdW5jdGlvbiBmb3JtYXROdW0obnVtOiBOdW1iZXIsIGRpZ2l0cz86IE51bWJlcik6IE51bWJlclxyXG4vLyBSZXR1cm5zIHRoZSBudW1iZXIgYG51bWAgcm91bmRlZCB0byBgZGlnaXRzYCBkZWNpbWFscywgb3IgdG8gNiBkZWNpbWFscyBieSBkZWZh
14775dWx0LlxyXG5mdW5jdGlvbiBmb3JtYXROdW0obnVtLCBkaWdpdHMpIHtcclxuXHR2YXIgcG93ID0gTWF0aC5wb3coMTAsIChkaWdpdHMgPT09IHVuZGVmaW5lZCA/IDYgOiBkaWdpdHMpKTtcclxuXHRyZXR1cm4gTWF0aC5yb3VuZChudW0gKiBwb3cpIC8gcG93O1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gdHJpbShzdHI6IFN0cmluZyk6IFN0cmluZ1xyXG4vLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbU3RyaW5nLnByb3RvdHlwZS50cmltXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9TdHJpbmcvVHJpbSlcclxuZnVuY3Rpb24gdHJpbShzdHIpIHtcclxuXHRyZXR1cm4gc3RyLnRyaW0gPyBzdHIudHJpbSgpIDogc3RyLnJlcGxhY2UoL15cXHMrfFxccyskL2csICcnKTtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIHNwbGl0V29yZHMoc3RyOiBTdHJpbmcpOiBTdHJpbmdbXVxyXG4vLyBUcmltcyBhbmQgc3BsaXRzIHRoZSBzdHJpbmcgb24gd2hpdGVzcGFjZSBhbmQgcmV0dXJucyB0aGUgYXJyYXkgb2YgcGFydHMuXHJcbmZ1bmN0aW9uIHNwbGl0V29yZHMoc3RyKSB7XHJcblx0cmV0dXJuIHRyaW0oc3RyKS5zcGxpdCgvXFxzKy8pO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gc2V0T3B0aW9ucyhvYmo6IE9iamVjdCwgb3B0aW9uczogT2JqZWN0KTogT2JqZWN0XHJcbi8vIE1lcmdlcyB0aGUgZ2l2ZW4gcHJvcGVydGllcyB0byB0aGUgYG9wdGlvbnNgIG9mIHRoZSBgb2JqYCBvYmplY3QsIHJldHVybmluZyB0aGUgcmVzdWx0aW5nIG9wdGlvbnMuIFNlZSBgQ2xhc3Mgb3B0aW9uc2AuIEhhcyBhbiBgTC5zZXRPcHRpb25zYCBzaG9ydGN1dC5cclxuZnVuY3Rpb24gc2V0T3B0aW9ucyhvYmosIG9wdGlvbnMpIHtcclxuXHRpZiAoIW9iai5oYXNPd25Qcm9wZXJ0eSgnb3B0aW9ucycpKSB7XHJcblx0XHRvYmoub3B0aW9ucyA9IG9iai5vcHRpb25zID8gY3JlYXRlKG9iai5vcHRpb25zKSA6IHt9O1xyXG5cdH1cclxuXHRmb3IgKHZhciBpIGluIG9wdGlvbnMpIHtcclxuXHRcdG9iai5vcHRpb25zW2ldID0gb3B0aW9uc1tpXTtcclxuXHR9XHJcblx0cmV0dXJuIG9iai5vcHRpb25zO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gZ2V0UGFyYW1TdHJpbmcob2JqOiBPYmplY3QsIGV4aXN0aW5nVXJsPzogU3RyaW5nLCB1cHBlcmNhc2U/OiBCb29sZWFuKTogU3RyaW5nXHJcbi8vIENvbnZlcnRzIGFuIG9iamVjdCBpbnRvIGEgcGFyYW1ldGVyIFVSTCBzdHJpbmcsIGUuZy4gYHthOiBcImZvb1wiLCBiOiBcImJhclwifWBcclxuLy8gdHJhbnNsYXRlcyB0byBgJz9hPWZvbyZiPWJhcidgLiBJZiBgZXhpc3RpbmdVcmxgIGlzIHNldCwgdGhlIHBhcmFtZXRlcnMgd2lsbFxyXG4vLyBiZSBhcHBlbmRlZCBhdCB0aGUgZW5kLiBJZiBgdXBwZXJjYXNlYCBpcyBgdHJ1ZWAsIHRoZSBwYXJhbWV0ZXIgbmFtZXMgd2lsbFxyXG4vLyBiZSB1cHBlcmNhc2VkIChlLmcuIGAnP0E9Zm9vJkI9YmFyJ2ApXHJcbmZ1bmN0aW9uIGdldFBhcmFtU3RyaW5nKG9iaiwgZXhpc3RpbmdVcmwsIHVwcGVyY2FzZSkge1xyXG5cdHZhciBwYXJhbXMgPSBbXTtcclxuXHRmb3IgKHZhciBpIGluIG9iaikge1xyXG5cdFx0cGFyYW1zLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHVwcGVyY2FzZSA/IGkudG9VcHBlckNhc2UoKSA6IGkpICsgJz0nICsgZW5jb2RlVVJJQ29tcG9uZW50KG9ialtpXSkpO1xyXG5cdH1cclxuXHRyZXR1cm4gKCghZXhpc3RpbmdVcmwgfHwgZXhpc3RpbmdVcmwuaW5kZXhPZignPycpID09PSAtMSkgPyAnPycgOiAnJicpICsgcGFyYW1zLmpvaW4oJyYnKTtcclxufVxyXG5cclxudmFyIHRlbXBsYXRlUmUgPSAvXFx7ICooW1xcd18tXSspICpcXH0vZztcclxuXHJcbi8vIEBmdW5jdGlvbiB0ZW1wbGF0ZShzdHI6IFN0cmluZywgZGF0YTogT2JqZWN0KTogU3RyaW5nXHJcbi8vIFNpbXBsZSB0ZW1wbGF0aW5nIGZhY2lsaXR5LCBhY2NlcHRzIGEgdGVtcGxhdGUgc3RyaW5nIG9mIHRoZSBmb3JtIGAnSGVsbG8ge2F9LCB7Yn0nYFxyXG4vLyBhbmQgYSBkYXRhIG9iamVjdCBsaWtlIGB7YTogJ2ZvbycsIGI6ICdiYXInfWAsIHJldHVybnMgZXZhbHVhdGVkIHN0cmluZ1xyXG4vLyBgKCdIZWxsbyBmb28sIGJhcicpYC4gWW91IGNhbiBhbHNvIHNwZWNpZnkgZnVuY3Rpb25zIGluc3RlYWQgb2Ygc3RyaW5ncyBmb3JcclxuLy8gZGF0YSB2YWx1ZXMg4oCUIHRoZXkgd2lsbCBiZSBldmFsdWF0ZWQgcGFzc2luZyBgZGF0YWAgYXMgYW4gYXJndW1lbnQuXHJcbmZ1bmN0aW9uIHRlbXBsYXRlKHN0ciwgZGF0YSkge1xyXG5cdHJldHVybiBzdHIucmVwbGFjZSh0ZW1wbGF0ZVJlLCBmdW5jdGlvbiAoc3RyLCBrZXkpIHtcclxuXHRcdHZhciB2YWx1ZSA9IGRhdGFba2V5XTtcclxuXHJcblx0XHRpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ05vIHZhbHVlIHByb3ZpZGVkIGZvciB2YXJpYWJsZSAnICsgc3RyKTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykge1xyXG5cdFx0XHR2YWx1ZSA9IHZhbHVlKGRhdGEpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHZhbHVlO1xyXG5cdH0pO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gaXNBcnJheShvYmopOiBCb29sZWFuXHJcbi8vIENvbXBhdGliaWxpdHkgcG9seWZpbGwgZm9yIFtBcnJheS5pc0FycmF5XShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9pc0FycmF5KVxyXG52YXIgaXNBcnJheSA9IEFycmF5LmlzQXJyYXkgfHwgZnVuY3Rpb24gKG9iaikge1xyXG5cdHJldHVybiAoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikgPT09ICdbb2JqZWN0IEFycmF5XScpO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIGluZGV4T2YoYXJyYXk6IEFycmF5LCBlbDogT2JqZWN0KTogTnVtYmVyXHJcbi8vIENvbXBhdGliaWxpdHkgcG9seWZpbGwgZm9yIFtBcnJheS5wcm90b3R5cGUuaW5kZXhPZl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvaW5kZXhPZilcclxuZnVuY3Rpb24gaW5kZXhPZihhcnJheSwgZWwpIHtcclxuXHRmb3IgKHZhciBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgaSsrKSB7XHJcblx0XHRpZiAoYXJyYXlbaV0gPT09IGVsKSB7IHJldHVybi
14775BpOyB9XHJcblx0fVxyXG5cdHJldHVybiAtMTtcclxufVxyXG5cclxuLy8gQHByb3BlcnR5IGVtcHR5SW1hZ2VVcmw6IFN0cmluZ1xyXG4vLyBEYXRhIFVSSSBzdHJpbmcgY29udGFpbmluZyBhIGJhc2U2NC1lbmNvZGVkIGVtcHR5IEdJRiBpbWFnZS5cclxuLy8gVXNlZCBhcyBhIGhhY2sgdG8gZnJlZSBtZW1vcnkgZnJvbSB1bnVzZWQgaW1hZ2VzIG9uIFdlYktpdC1wb3dlcmVkXHJcbi8vIG1vYmlsZSBkZXZpY2VzIChieSBzZXR0aW5nIGltYWdlIGBzcmNgIHRvIHRoaXMgc3RyaW5nKS5cclxudmFyIGVtcHR5SW1hZ2VVcmwgPSAnZGF0YTppbWFnZS9naWY7YmFzZTY0LFIwbEdPRGxoQVFBQkFBRC9BQ3dBQUFBQUFRQUJBQUFDQURzPSc7XHJcblxyXG4vLyBpbnNwaXJlZCBieSBodHRwOi8vcGF1bGlyaXNoLmNvbS8yMDExL3JlcXVlc3RhbmltYXRpb25mcmFtZS1mb3Itc21hcnQtYW5pbWF0aW5nL1xyXG5cclxuZnVuY3Rpb24gZ2V0UHJlZml4ZWQobmFtZSkge1xyXG5cdHJldHVybiB3aW5kb3dbJ3dlYmtpdCcgKyBuYW1lXSB8fCB3aW5kb3dbJ21veicgKyBuYW1lXSB8fCB3aW5kb3dbJ21zJyArIG5hbWVdO1xyXG59XHJcblxyXG52YXIgbGFzdFRpbWUgPSAwO1xyXG5cclxuLy8gZmFsbGJhY2sgZm9yIElFIDctOFxyXG5mdW5jdGlvbiB0aW1lb3V0RGVmZXIoZm4pIHtcclxuXHR2YXIgdGltZSA9ICtuZXcgRGF0ZSgpLFxyXG5cdCAgICB0aW1lVG9DYWxsID0gTWF0aC5tYXgoMCwgMTYgLSAodGltZSAtIGxhc3RUaW1lKSk7XHJcblxyXG5cdGxhc3RUaW1lID0gdGltZSArIHRpbWVUb0NhbGw7XHJcblx0cmV0dXJuIHdpbmRvdy5zZXRUaW1lb3V0KGZuLCB0aW1lVG9DYWxsKTtcclxufVxyXG5cclxudmFyIHJlcXVlc3RGbiA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgZ2V0UHJlZml4ZWQoJ1JlcXVlc3RBbmltYXRpb25GcmFtZScpIHx8IHRpbWVvdXREZWZlcjtcclxudmFyIGNhbmNlbEZuID0gd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lIHx8IGdldFByZWZpeGVkKCdDYW5jZWxBbmltYXRpb25GcmFtZScpIHx8XHJcblx0XHRnZXRQcmVmaXhlZCgnQ2FuY2VsUmVxdWVzdEFuaW1hdGlvbkZyYW1lJykgfHwgZnVuY3Rpb24gKGlkKSB7IHdpbmRvdy5jbGVhclRpbWVvdXQoaWQpOyB9O1xyXG5cclxuLy8gQGZ1bmN0aW9uIHJlcXVlc3RBbmltRnJhbWUoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0LCBpbW1lZGlhdGU/OiBCb29sZWFuKTogTnVtYmVyXHJcbi8vIFNjaGVkdWxlcyBgZm5gIHRvIGJlIGV4ZWN1dGVkIHdoZW4gdGhlIGJyb3dzZXIgcmVwYWludHMuIGBmbmAgaXMgYm91bmQgdG9cclxuLy8gYGNvbnRleHRgIGlmIGdpdmVuLiBXaGVuIGBpbW1lZGlhdGVgIGlzIHNldCwgYGZuYCBpcyBjYWxsZWQgaW1tZWRpYXRlbHkgaWZcclxuLy8gdGhlIGJyb3dzZXIgZG9lc24ndCBoYXZlIG5hdGl2ZSBzdXBwb3J0IGZvclxyXG4vLyBbYHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWVgXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvd2luZG93L3JlcXVlc3RBbmltYXRpb25GcmFtZSksXHJcbi8vIG90aGVyd2lzZSBpdCdzIGRlbGF5ZWQuIFJldHVybnMgYSByZXF1ZXN0IElEIHRoYXQgY2FuIGJlIHVzZWQgdG8gY2FuY2VsIHRoZSByZXF1ZXN0LlxyXG5mdW5jdGlvbiByZXF1ZXN0QW5pbUZyYW1lKGZuLCBjb250ZXh0LCBpbW1lZGlhdGUpIHtcclxuXHRpZiAoaW1tZWRpYXRlICYmIHJlcXVlc3RGbiA9PT0gdGltZW91dERlZmVyKSB7XHJcblx0XHRmbi5jYWxsKGNvbnRleHQpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRyZXR1cm4gcmVxdWVzdEZuLmNhbGwod2luZG93LCBiaW5kKGZuLCBjb250ZXh0KSk7XHJcblx0fVxyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gY2FuY2VsQW5pbUZyYW1lKGlkOiBOdW1iZXIpOiB1bmRlZmluZWRcclxuLy8gQ2FuY2VscyBhIHByZXZpb3VzIGByZXF1ZXN0QW5pbUZyYW1lYC4gU2VlIGFsc28gW3dpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL3dpbmRvdy9jYW5jZWxBbmltYXRpb25GcmFtZSkuXHJcbmZ1bmN0aW9uIGNhbmNlbEFuaW1GcmFtZShpZCkge1xyXG5cdGlmIChpZCkge1xyXG5cdFx0Y2FuY2VsRm4uY2FsbCh3aW5kb3csIGlkKTtcclxuXHR9XHJcbn1cclxuXG5cbnZhciBVdGlsID0gKE9iamVjdC5mcmVlemUgfHwgT2JqZWN0KSh7XG5cdGZyZWV6ZTogZnJlZXplLFxuXHRleHRlbmQ6IGV4dGVuZCxcblx0Y3JlYXRlOiBjcmVhdGUsXG5cdGJpbmQ6IGJpbmQsXG5cdGxhc3RJZDogbGFzdElkLFxuXHRzdGFtcDogc3RhbXAsXG5cdHRocm90dGxlOiB0aHJvdHRsZSxcblx0d3JhcE51bTogd3JhcE51bSxcblx0ZmFsc2VGbjogZmFsc2VGbixcblx0Zm9ybWF0TnVtOiBmb3JtYXROdW0sXG5cdHRyaW06IHRyaW0sXG5cdHNwbGl0V29yZHM6IHNwbGl0V29yZHMsXG5cdHNldE9wdGlvbnM6IHNldE9wdGlvbnMsXG5cdGdldFBhcmFtU3RyaW5nOiBnZXRQYXJhbVN0cmluZyxcblx0dGVtcGxhdGU6IHRlbXBsYXRlLFxuXHRpc0FycmF5OiBpc0FycmF5LFxuXHRpbmRleE9mOiBpbmRleE9mLFxuXHRlbXB0eUltYWdlVXJsOiBlbXB0eUltYWdlVXJsLFxuXHRyZXF1ZXN0Rm46IHJlcXVlc3RGbixcblx0Y2FuY2VsRm46IGNhbmNlbEZuLFxuXHRyZXF1ZXN0QW5pbUZyYW1lOiByZXF1ZXN0QW5pbUZyYW1lLFxuXHRjYW5jZWxBbmltRnJhbWU6IGNhbmNlbEFuaW1GcmFtZVxufSk7XG5cbi8vIEBjbGFzcyBDbGFzc1xyXG4vLyBAYWthIEwuQ2xhc3NcclxuXHJcbi8vIEBzZWN0aW9uXHJcbi8vIEB1bmluaGVyaXRhYmxlXHJcblxyXG4vLyBUaGFua3MgdG8gSm9obiBSZXNpZyBhbmQgRGVhbiBFZHdhcmRzIGZvciBpbnNwaXJhdGlvbiFcclxuXHJcbmZ1bmN0aW9uIENsYXNzKCkge31cclxuXHJcbkNsYXNzLmV4dGVuZCA9IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZXh0ZW5kKHByb3BzOiBPYmplY3QpOiBGdW5jdGlvblxyXG5cdC8vIFtFeHRlbmRzIHRoZSBjdXJyZW50IGNsYXNzXSgjY2xhc3MtaW5oZXJpdGFuY2UpIGdpdmVuIHRoZSBwcm9wZXJ0aWVzIHRvIGJlIGluY2x1ZGVkLlxyXG5cdC8vIFJldHVybnMgYSBKYXZhc2NyaXB0IGZ1bmN0aW9uIHRoYXQgaXMgYSBjbGFzcyBjb25zdHJ1Y3RvciAodG8gYmUgY2FsbGVkIHdpdGggYG5ld2ApLlxyXG5cdHZhciBOZXdDbGFzcyA9IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHQvLyBjYWxsIHRoZSBjb25zdHJ1Y3RvclxyXG5cdFx0aWYgKHRoaXMuaW5pdGlhbGl6ZSkge1xyXG5cdFx0XHR0aGlzLmluaXRpYWxpemUuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBjYWxsIGFsbCBjb25zdHJ1Y3RvciBob29rc1xyXG5cdFx0dGhpcy5jYWxsSW5pdEhvb2tzKCk7XHJcblx0fTtcclxuXHJcblx0dmFyIHBhcmVudFByb3RvID0gTmV3Q2xhc3MuX19zdXBlcl9fID0gdGhpcy5wcm90b3R5cGU7XHJcblxyXG5cdHZhciBwcm90byA9IGNyZWF0ZShwYXJlbnRQcm90byk
147757XHJcblx0cHJvdG8uY29uc3RydWN0b3IgPSBOZXdDbGFzcztcclxuXHJcblx0TmV3Q2xhc3MucHJvdG90eXBlID0gcHJvdG87XHJcblxyXG5cdC8vIGluaGVyaXQgcGFyZW50J3Mgc3RhdGljc1xyXG5cdGZvciAodmFyIGkgaW4gdGhpcykge1xyXG5cdFx0aWYgKHRoaXMuaGFzT3duUHJvcGVydHkoaSkgJiYgaSAhPT0gJ3Byb3RvdHlwZScgJiYgaSAhPT0gJ19fc3VwZXJfXycpIHtcclxuXHRcdFx0TmV3Q2xhc3NbaV0gPSB0aGlzW2ldO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0Ly8gbWl4IHN0YXRpYyBwcm9wZXJ0aWVzIGludG8gdGhlIGNsYXNzXHJcblx0aWYgKHByb3BzLnN0YXRpY3MpIHtcclxuXHRcdGV4dGVuZChOZXdDbGFzcywgcHJvcHMuc3RhdGljcyk7XHJcblx0XHRkZWxldGUgcHJvcHMuc3RhdGljcztcclxuXHR9XHJcblxyXG5cdC8vIG1peCBpbmNsdWRlcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRpZiAocHJvcHMuaW5jbHVkZXMpIHtcclxuXHRcdGNoZWNrRGVwcmVjYXRlZE1peGluRXZlbnRzKHByb3BzLmluY2x1ZGVzKTtcclxuXHRcdGV4dGVuZC5hcHBseShudWxsLCBbcHJvdG9dLmNvbmNhdChwcm9wcy5pbmNsdWRlcykpO1xyXG5cdFx0ZGVsZXRlIHByb3BzLmluY2x1ZGVzO1xyXG5cdH1cclxuXHJcblx0Ly8gbWVyZ2Ugb3B0aW9uc1xyXG5cdGlmIChwcm90by5vcHRpb25zKSB7XHJcblx0XHRwcm9wcy5vcHRpb25zID0gZXh0ZW5kKGNyZWF0ZShwcm90by5vcHRpb25zKSwgcHJvcHMub3B0aW9ucyk7XHJcblx0fVxyXG5cclxuXHQvLyBtaXggZ2l2ZW4gcHJvcGVydGllcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRleHRlbmQocHJvdG8sIHByb3BzKTtcclxuXHJcblx0cHJvdG8uX2luaXRIb29rcyA9IFtdO1xyXG5cclxuXHQvLyBhZGQgbWV0aG9kIGZvciBjYWxsaW5nIGFsbCBob29rc1xyXG5cdHByb3RvLmNhbGxJbml0SG9va3MgPSBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRIb29rc0NhbGxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAocGFyZW50UHJvdG8uY2FsbEluaXRIb29rcykge1xyXG5cdFx0XHRwYXJlbnRQcm90by5jYWxsSW5pdEhvb2tzLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdEhvb2tzQ2FsbGVkID0gdHJ1ZTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gcHJvdG8uX2luaXRIb29rcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRwcm90by5faW5pdEhvb2tzW2ldLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0cmV0dXJuIE5ld0NsYXNzO1xyXG59O1xyXG5cclxuXHJcbi8vIEBmdW5jdGlvbiBpbmNsdWRlKHByb3BlcnRpZXM6IE9iamVjdCk6IHRoaXNcclxuLy8gW0luY2x1ZGVzIGEgbWl4aW5dKCNjbGFzcy1pbmNsdWRlcykgaW50byB0aGUgY3VycmVudCBjbGFzcy5cclxuQ2xhc3MuaW5jbHVkZSA9IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cdGV4dGVuZCh0aGlzLnByb3RvdHlwZSwgcHJvcHMpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIG1lcmdlT3B0aW9ucyhvcHRpb25zOiBPYmplY3QpOiB0aGlzXHJcbi8vIFtNZXJnZXMgYG9wdGlvbnNgXSgjY2xhc3Mtb3B0aW9ucykgaW50byB0aGUgZGVmYXVsdHMgb2YgdGhlIGNsYXNzLlxyXG5DbGFzcy5tZXJnZU9wdGlvbnMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdGV4dGVuZCh0aGlzLnByb3RvdHlwZS5vcHRpb25zLCBvcHRpb25zKTtcclxuXHRyZXR1cm4gdGhpcztcclxufTtcclxuXHJcbi8vIEBmdW5jdGlvbiBhZGRJbml0SG9vayhmbjogRnVuY3Rpb24pOiB0aGlzXHJcbi8vIEFkZHMgYSBbY29uc3RydWN0b3IgaG9va10oI2NsYXNzLWNvbnN0cnVjdG9yLWhvb2tzKSB0byB0aGUgY2xhc3MuXHJcbkNsYXNzLmFkZEluaXRIb29rID0gZnVuY3Rpb24gKGZuKSB7IC8vIChGdW5jdGlvbikgfHwgKFN0cmluZywgYXJncy4uLilcclxuXHR2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XHJcblxyXG5cdHZhciBpbml0ID0gdHlwZW9mIGZuID09PSAnZnVuY3Rpb24nID8gZm4gOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzW2ZuXS5hcHBseSh0aGlzLCBhcmdzKTtcclxuXHR9O1xyXG5cclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzID0gdGhpcy5wcm90b3R5cGUuX2luaXRIb29rcyB8fCBbXTtcclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzLnB1c2goaW5pdCk7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxyXG5mdW5jdGlvbiBjaGVja0RlcHJlY2F0ZWRNaXhpbkV2ZW50cyhpbmNsdWRlcykge1xyXG5cdGlmICh0eXBlb2YgTCA9PT0gJ3VuZGVmaW5lZCcgfHwgIUwgfHwgIUwuTWl4aW4pIHsgcmV0dXJuOyB9XHJcblxyXG5cdGluY2x1ZGVzID0gaXNBcnJheShpbmNsdWRlcykgPyBpbmNsdWRlcyA6IFtpbmNsdWRlc107XHJcblxyXG5cdGZvciAodmFyIGkgPSAwOyBpIDwgaW5jbHVkZXMubGVuZ3RoOyBpKyspIHtcclxuXHRcdGlmIChpbmNsdWRlc1tpXSA9PT0gTC5NaXhpbi5FdmVudHMpIHtcclxuXHRcdFx0Y29uc29sZS53YXJuKCdEZXByZWNhdGVkIGluY2x1ZGUgb2YgTC5NaXhpbi5FdmVudHM6ICcgK1xyXG5cdFx0XHRcdCd0aGlzIHByb3BlcnR5IHdpbGwgYmUgcmVtb3ZlZCBpbiBmdXR1cmUgcmVsZWFzZXMsICcgK1xyXG5cdFx0XHRcdCdwbGVhc2UgaW5oZXJpdCBmcm9tIEwuRXZlbnRlZCBpbnN0ZWFkLicsIG5ldyBFcnJvcigpLnN0YWNrKTtcclxuXHRcdH1cclxuXHR9XHJcbn1cblxuLypcclxuICogQGNsYXNzIEV2ZW50ZWRcclxuICogQGFrYSBMLkV2ZW50ZWRcclxuICogQGluaGVyaXRzIENsYXNzXHJcbiAqXHJcbiAqIEEgc2V0IG9mIG1ldGhvZHMgc2hhcmVkIGJldHdlZW4gZXZlbnQtcG93ZXJlZCBjbGFzc2VzIChsaWtlIGBNYXBgIGFuZCBgTWFya2VyYCkuIEdlbmVyYWxseSwgZXZlbnRzIGFsbG93IHlvdSB0byBleGVjdXRlIHNvbWUgZnVuY3Rpb24gd2hlbiBzb21ldGhpbmcgaGFwcGVucyB3aXRoIGFuIG9iamVjdCAoZS5nLiB0aGUgdXNlciBjbGlja3Mgb24gdGhlIG1hcCwgY2F1c2luZyB0aGUgbWFwIHRvIGZpcmUgYCdjbGljaydgIGV2ZW50KS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogbWFwLm9uKCdjbGljaycsIGZ1bmN0aW9uKGUpIHtcclxuICogXHRhbGVydChlLmxhdGxuZyk7XHJcbiAqIH0gKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIExlYWZsZXQgZGVhbHMgd2l0aCBldmVudCBsaXN0ZW5lcnMgYnkgcmVmZXJlbmNlLCBzbyBpZiB5b3Ugd2FudCB0byBhZGQgYSBsaXN0ZW5lciBhbmQgdGhlbiByZW1vdmUgaXQsIGRlZmluZSBpdCBhcyBh
14775IGZ1bmN0aW9uOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBmdW5jdGlvbiBvbkNsaWNrKGUpIHsgLi4uIH1cclxuICpcclxuICogbWFwLm9uKCdjbGljaycsIG9uQ2xpY2spO1xyXG4gKiBtYXAub2ZmKCdjbGljaycsIG9uQ2xpY2spO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG52YXIgRXZlbnRzID0ge1xyXG5cdC8qIEBtZXRob2Qgb24odHlwZTogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0ICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgZXZlbnQgdHlwZSBvZiB0aGUgb2JqZWN0LiBZb3UgY2FuIG9wdGlvbmFsbHkgc3BlY2lmeSB0aGUgY29udGV4dCBvZiB0aGUgbGlzdGVuZXIgKG9iamVjdCB0aGUgdGhpcyBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvbihldmVudE1hcDogT2JqZWN0KTogdGhpc1xyXG5cdCAqIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHQgKi9cclxuXHRvbjogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdC8vIHR5cGVzIGNhbiBiZSBhIG1hcCBvZiB0eXBlcy9oYW5kbGVyc1xyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdC8vIHdlIGRvbid0IHByb2Nlc3Mgc3BhY2Utc2VwYXJhdGVkIGV2ZW50cyBoZXJlIGZvciBwZXJmb3JtYW5jZTtcclxuXHRcdFx0XHQvLyBpdCdzIGEgaG90IHBhdGggc2luY2UgTGF5ZXIgdXNlcyB0aGUgb24ob2JqKSBzeW50YXhcclxuXHRcdFx0XHR0aGlzLl9vbih0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Ly8gdHlwZXMgY2FuIGJlIGEgc3RyaW5nIG9mIHNwYWNlLXNlcGFyYXRlZCB3b3Jkc1xyXG5cdFx0XHR0eXBlcyA9IHNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb24odHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8qIEBtZXRob2Qgb2ZmKHR5cGU6IFN0cmluZywgZm4/OiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgcHJldmlvdXNseSBhZGRlZCBsaXN0ZW5lciBmdW5jdGlvbi4gSWYgbm8gZnVuY3Rpb24gaXMgc3BlY2lmaWVkLCBpdCB3aWxsIHJlbW92ZSBhbGwgdGhlIGxpc3RlbmVycyBvZiB0aGF0IHBhcnRpY3VsYXIgZXZlbnQgZnJvbSB0aGUgb2JqZWN0LiBOb3RlIHRoYXQgaWYgeW91IHBhc3NlZCBhIGN1c3RvbSBjb250ZXh0IHRvIGBvbmAsIHlvdSBtdXN0IHBhc3MgdGhlIHNhbWUgY29udGV4dCB0byBgb2ZmYCBpbiBvcmRlciB0byByZW1vdmUgdGhlIGxpc3RlbmVyLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvZmYoZXZlbnRNYXA6IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgc2V0IG9mIHR5cGUvbGlzdGVuZXIgcGFpcnMuXHJcblx0ICpcclxuXHQgKiBAYWx0ZXJuYXRpdmVcclxuXHQgKiBAbWV0aG9kIG9mZjogdGhpc1xyXG5cdCAqIFJlbW92ZXMgYWxsIGxpc3RlbmVycyB0byBhbGwgZXZlbnRzIG9uIHRoZSBvYmplY3QuIFRoaXMgaW5jbHVkZXMgaW1wbGljaXRseSBhdHRhY2hlZCBldmVudHMuXHJcblx0ICovXHJcblx0b2ZmOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKCF0eXBlcykge1xyXG5cdFx0XHQvLyBjbGVhciBhbGwgbGlzdGVuZXJzIGlmIGNhbGxlZCB3aXRob3V0IGFyZ3VtZW50c1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IHNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBhdHRhY2ggbGlzdGVuZXIgKHdpdGhvdXQgc3ludGFjdGljIHN1Z2FyIG5vdylcclxuXHRfb246IGZ1bmN0aW9uICh0eXBlLCBmbiwgY29udGV4dCkge1xyXG5cdFx0dGhpcy5fZXZlbnRzID0gdGhpcy5fZXZlbnRzIHx8IHt9O1xyXG5cclxuXHRcdC8qIGdldC9pbml0IGxpc3RlbmVycyBmb3IgdHlwZSAqL1xyXG5cdFx0dmFyIHR5cGVMaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblx0XHRpZiAoIXR5cGVMaXN0ZW5lcnMpIHtcclxuXHRcdFx0dHlwZUxpc3RlbmVycyA9IFtdO1xyXG5cdFx0XHR0aGlzLl9ldmVudHNbdHlwZV0gPSB0eXBlTGlzdGVuZXJzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChjb250ZXh0ID09PSB0aGlzKSB7XHJcblx0XHRcdC8vIExlc3MgbWVtb3J5IGZvb3RwcmludC5cclxuXHRcdFx0Y29udGV4dCA9IHVuZGVmaW5lZDtcclxuXHRcdH1cclxuXHRcdHZhciBuZXdMaXN0ZW5lciA9IHtmbjogZm4sIGN0eDogY29udGV4dH0sXHJcblx0XHQgICAgbGlzdGVuZXJzID0gdHlwZUxpc3RlbmVycztcclxuXHJcblx0XHQvLyBjaGVjayBpZiBmbiBhbHJlYWR5IHRoZXJlXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGlmIChsaXN0ZW5lcnNbaV0uZm4gPT09IGZuICYmIGxpc3RlbmVyc1tpXS5jdHggPT09IGNvbnRleHQpIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRsaXN0ZW5lcnMucHVzaChuZXdMaXN0ZW5lcik7XHJcblx0fSxcclxuXHJcblx0X29mZjogZnVuY3Rpb24gKHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblx0XHR2YXIgbGlzdGVuZXJzLFxyXG5cdFx0ICAgIGksXHJcblx0XHQgICAgbGVuO1xyXG5cclxuXHRcdGlmICghdGhpcy5fZXZlbnRzKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcclxuXHJcblx0XHRpZiAoIWxpc3RlbmVycykge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFmbikge1xyXG5cdFx0XHQvLyBTZXQgYWxsIHJlbW92ZWQgbGlzdGVuZXJzIHRvIG5vb3Agc28gdGhleSBhcmUgbm90IGNhbGxlZCBpZiByZW1vdmUgaGFwcGVucyBpbiBmaXJlXHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGxpc3RlbmVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdGxpc3RlbmVyc1tpXS5mbiA9IGZ
14775hbHNlRm47XHJcblx0XHRcdH1cclxuXHRcdFx0Ly8gY2xlYXIgYWxsIGxpc3RlbmVycyBmb3IgYSB0eXBlIGlmIGZ1bmN0aW9uIGlzbid0IHNwZWNpZmllZFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvbnRleHQgPT09IHRoaXMpIHtcclxuXHRcdFx0Y29udGV4dCA9IHVuZGVmaW5lZDtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblxyXG5cdFx0XHQvLyBmaW5kIGZuIGFuZCByZW1vdmUgaXRcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dmFyIGwgPSBsaXN0ZW5lcnNbaV07XHJcblx0XHRcdFx0aWYgKGwuY3R4ICE9PSBjb250ZXh0KSB7IGNvbnRpbnVlOyB9XHJcblx0XHRcdFx0aWYgKGwuZm4gPT09IGZuKSB7XHJcblxyXG5cdFx0XHRcdFx0Ly8gc2V0IHRoZSByZW1vdmVkIGxpc3RlbmVyIHRvIG5vb3Agc28gdGhhdCdzIG5vdCBjYWxsZWQgaWYgcmVtb3ZlIGhhcHBlbnMgaW4gZmlyZVxyXG5cdFx0XHRcdFx0bC5mbiA9IGZhbHNlRm47XHJcblxyXG5cdFx0XHRcdFx0aWYgKHRoaXMuX2ZpcmluZ0NvdW50KSB7XHJcblx0XHRcdFx0XHRcdC8qIGNvcHkgYXJyYXkgaW4gY2FzZSBldmVudHMgYXJlIGJlaW5nIGZpcmVkICovXHJcblx0XHRcdFx0XHRcdHRoaXMuX2V2ZW50c1t0eXBlXSA9IGxpc3RlbmVycyA9IGxpc3RlbmVycy5zbGljZSgpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0bGlzdGVuZXJzLnNwbGljZShpLCAxKTtcclxuXHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXJlKHR5cGU6IFN0cmluZywgZGF0YT86IE9iamVjdCwgcHJvcGFnYXRlPzogQm9vbGVhbik6IHRoaXNcclxuXHQvLyBGaXJlcyBhbiBldmVudCBvZiB0aGUgc3BlY2lmaWVkIHR5cGUuIFlvdSBjYW4gb3B0aW9uYWxseSBwcm92aWRlIGFuIGRhdGFcclxuXHQvLyBvYmplY3Qg4oCUIHRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGUgbGlzdGVuZXIgZnVuY3Rpb24gd2lsbCBjb250YWluIGl0c1xyXG5cdC8vIHByb3BlcnRpZXMuIFRoZSBldmVudCBjYW4gb3B0aW9uYWxseSBiZSBwcm9wYWdhdGVkIHRvIGV2ZW50IHBhcmVudHMuXHJcblx0ZmlyZTogZnVuY3Rpb24gKHR5cGUsIGRhdGEsIHByb3BhZ2F0ZSkge1xyXG5cdFx0aWYgKCF0aGlzLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBldmVudCA9IGV4dGVuZCh7fSwgZGF0YSwge1xyXG5cdFx0XHR0eXBlOiB0eXBlLFxyXG5cdFx0XHR0YXJnZXQ6IHRoaXMsXHJcblx0XHRcdHNvdXJjZVRhcmdldDogZGF0YSAmJiBkYXRhLnNvdXJjZVRhcmdldCB8fCB0aGlzXHJcblx0XHR9KTtcclxuXHJcblx0XHRpZiAodGhpcy5fZXZlbnRzKSB7XHJcblx0XHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblxyXG5cdFx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblx0XHRcdFx0dGhpcy5fZmlyaW5nQ291bnQgPSAodGhpcy5fZmlyaW5nQ291bnQgKyAxKSB8fCAxO1xyXG5cdFx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRcdHZhciBsID0gbGlzdGVuZXJzW2ldO1xyXG5cdFx0XHRcdFx0bC5mbi5jYWxsKGwuY3R4IHx8IHRoaXMsIGV2ZW50KTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdHRoaXMuX2ZpcmluZ0NvdW50LS07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAocHJvcGFnYXRlKSB7XHJcblx0XHRcdC8vIHByb3BhZ2F0ZSB0aGUgZXZlbnQgdG8gcGFyZW50cyAoc2V0IHdpdGggYWRkRXZlbnRQYXJlbnQpXHJcblx0XHRcdHRoaXMuX3Byb3BhZ2F0ZUV2ZW50KGV2ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxpc3RlbnModHlwZTogU3RyaW5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIGEgcGFydGljdWxhciBldmVudCB0eXBlIGhhcyBhbnkgbGlzdGVuZXJzIGF0dGFjaGVkIHRvIGl0LlxyXG5cdGxpc3RlbnM6IGZ1bmN0aW9uICh0eXBlLCBwcm9wYWdhdGUpIHtcclxuXHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHMgJiYgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0aWYgKGxpc3RlbmVycyAmJiBsaXN0ZW5lcnMubGVuZ3RoKSB7IHJldHVybiB0cnVlOyB9XHJcblxyXG5cdFx0aWYgKHByb3BhZ2F0ZSkge1xyXG5cdFx0XHQvLyBhbHNvIGNoZWNrIHBhcmVudHMgZm9yIGxpc3RlbmVycyBpZiBldmVudCBwcm9wYWdhdGVzXHJcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2V2ZW50UGFyZW50cykge1xyXG5cdFx0XHRcdGlmICh0aGlzLl9ldmVudFBhcmVudHNbaWRdLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdHJ1ZTsgfVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvbmNlKOKApik6IHRoaXNcclxuXHQvLyBCZWhhdmVzIGFzIFtgb24o4oCmKWBdKCNldmVudGVkLW9uKSwgZXhjZXB0IHRoZSBsaXN0ZW5lciB3aWxsIG9ubHkgZ2V0IGZpcmVkIG9uY2UgYW5kIHRoZW4gcmVtb3ZlZC5cclxuXHRvbmNlOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMub25jZSh0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gYmluZChmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5vZmYodHlwZXMsIGZuLCBjb250ZXh0KVxyXG5cdFx0XHQgICAgLm9mZih0eXBlcywgaGFuZGxlciwgY29udGV4dCk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHQvLyBhZGQgYSBsaXN0ZW5lciB0aGF0J3MgZXhlY3V0ZWQgb25jZSBhbmQgcmVtb3ZlZCBhZnRlciB0aGF0XHJcblx0XHRyZXR1cm4gdGhpc1xyXG5cdFx0ICAgIC5vbih0eXBlcywgZm4sIGNvbnRleHQpXHJcblx0XHQgICAgLm9uKHR5cGVzLCBoYW5kbGVyLCBjb250ZXh0KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEV2ZW50UGFyZW50KG9iajogRXZlbnRlZCk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGV2ZW50IHBhcmVudCAtIGFuIGBFdmVudGVkYCB0aGF0IHdpbGwgcmVjZWl2ZSBwcm9wYWdhdGVkIGV2ZW50c1xyXG5cdGFkZEV2ZW50UGFyZW50OiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHMgPSB0aGlzLl9ldmVudFBhcmVudHMgfHwge307XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHNbc3RhbXAob2JqKV0gPSBvYmo7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUV2ZW50UGFyZW50KG9iajogRXZlbnRlZCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIGFuIGV2ZW50IHBhcmVudCwgc28gaXQgd2lsbCBzdG9wIHJlY2VpdmluZyBwcm9wYWdhdGVkIGV2ZW50c1xyXG5cdHJlbW92ZUV2ZW50UGFyZW50OiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRpZiAodGhpcy5fZXZlbnRQYXJlbnRzKSB7XHJcblx0XHRcdGRlbGV0ZSB0aGlzLl9ldmVudFBhcmVudHNbc3RhbXAob2JqKV07XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcHJvcGFnYXRlRXZlbnQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0dGhpcy5fZXZlbnRQYXJlbnRzW2lkXS5maXJlKGUudHlwZSwgZXh0ZW5kKHtcclxuXHRcdFx0XHRsYXllcjogZS50YXJnZXQsXHJcblx0XHRcdFx0cHJvcGFnYXRlZEZyb206IGUudGFyZ2V0XHJcblx0XHRcdH0sIGUpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9XHJcbn07XHJcblxyXG4vLyBhbGlhc2VzOyB3ZSBzaG91bGQgZGl0Y2ggdGhvc2UgZXZlbnR1YWxseVxyXG5cclxuLy8gQG1ldGhvZCBhZGRFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvbijigKYpYF0oI2V2ZW50ZWQtb24pXHJcbkV2ZW50cy5hZGRFdmVudExpc3RlbmVyID0gRXZlbnRzLm9uO1xyXG5cclxuLy8gQG1ldGhvZCByZW1vdmVFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvZmYo4oCmKWBdKCNldmVudGVkLW9mZilcclxuXHJcbi8vIEBtZXRob2QgY2xlYXJBbGxFdmVudExpc3RlbmVycyjigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb2ZmKClgXSgjZXZlbnRlZC1vZmYpXHJcbkV2ZW50cy5yZW1vdmVFdmVudExpc3RlbmVyID0gRXZlbnRzLmNsZWFyQWxsRXZlbnRMaXN0ZW5lcnMgPSBFdmVudHMub2ZmO1xyXG5cclxuLy8gQG1ldGhvZCBhZGRPbmVUaW1lRXZlbnRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb25jZSjigKYpYF0oI2V2ZW50ZWQtb25jZSlcclxuRXZlbnRzLmFkZE9uZVRpbWVFdmVudExpc3RlbmVyID0gRXZlbnRzLm9uY2U7XHJcblxyXG4vLyBAbWV0aG9kIGZpcmVFdmVudCjigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgZmlyZSjigKYpYF0oI2V2ZW50ZWQtZmlyZSlcclxuRXZlbnRzLmZpcmVFdmVudCA9IEV2ZW50cy5maXJlO1xyXG5cclxuLy8gQG1ldGhvZCBoYXNFdmVudExpc3RlbmVycyjigKYpOiBCb29sZWFuXHJcbi8vIEFsaWFzIHRvIFtgbGlzdGVucyjigKYpYF0oI2V2ZW50ZWQtbGlzdGVucylcclxuRXZlbnRzLmhhc0V2ZW50TGlzdGVuZXJzID0gRXZlbnRzLmxpc3Rlb
14775nM7XHJcblxyXG52YXIgRXZlbnRlZCA9IENsYXNzLmV4dGVuZChFdmVudHMpO1xuXG4vKlxyXG4gKiBAY2xhc3MgUG9pbnRcclxuICogQGFrYSBMLlBvaW50XHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSBwb2ludCB3aXRoIGB4YCBhbmQgYHlgIGNvb3JkaW5hdGVzIGluIHBpeGVscy5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIHBvaW50ID0gTC5wb2ludCgyMDAsIDMwMCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIGFuZCBvcHRpb25zIHRoYXQgYWNjZXB0IGBQb2ludGAgb2JqZWN0cyBhbHNvIGFjY2VwdCB0aGVtIGluIGEgc2ltcGxlIEFycmF5IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGVzZSBsaW5lcyBhcmUgZXF1aXZhbGVudDpcclxuICpcclxuICogYGBganNcclxuICogbWFwLnBhbkJ5KFsyMDAsIDMwMF0pO1xyXG4gKiBtYXAucGFuQnkoTC5wb2ludCgyMDAsIDMwMCkpO1xyXG4gKiBgYGBcclxuICpcclxuICogTm90ZSB0aGF0IGBQb2ludGAgZG9lcyBub3QgaW5oZXJpdCBmcm9tIExlYWZldCdzIGBDbGFzc2Agb2JqZWN0LFxyXG4gKiB3aGljaCBtZWFucyBuZXcgY2xhc3NlcyBjYW4ndCBpbmhlcml0IGZyb20gaXQsIGFuZCBuZXcgbWV0aG9kc1xyXG4gKiBjYW4ndCBiZSBhZGRlZCB0byBpdCB3aXRoIHRoZSBgaW5jbHVkZWAgZnVuY3Rpb24uXHJcbiAqL1xyXG5cclxuZnVuY3Rpb24gUG9pbnQoeCwgeSwgcm91bmQpIHtcclxuXHQvLyBAcHJvcGVydHkgeDogTnVtYmVyOyBUaGUgYHhgIGNvb3JkaW5hdGUgb2YgdGhlIHBvaW50XHJcblx0dGhpcy54ID0gKHJvdW5kID8gTWF0aC5yb3VuZCh4KSA6IHgpO1xyXG5cdC8vIEBwcm9wZXJ0eSB5OiBOdW1iZXI7IFRoZSBgeWAgY29vcmRpbmF0ZSBvZiB0aGUgcG9pbnRcclxuXHR0aGlzLnkgPSAocm91bmQgPyBNYXRoLnJvdW5kKHkpIDogeSk7XHJcbn1cclxuXHJcbnZhciB0cnVuYyA9IE1hdGgudHJ1bmMgfHwgZnVuY3Rpb24gKHYpIHtcclxuXHRyZXR1cm4gdiA+IDAgPyBNYXRoLmZsb29yKHYpIDogTWF0aC5jZWlsKHYpO1xyXG59O1xyXG5cclxuUG9pbnQucHJvdG90eXBlID0ge1xyXG5cclxuXHQvLyBAbWV0aG9kIGNsb25lKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQuXHJcblx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgUG9pbnQodGhpcy54LCB0aGlzLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkKG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgYWRkaXRpb24gb2YgdGhlIGN1cnJlbnQgYW5kIHRoZSBnaXZlbiBwb2ludHMuXHJcblx0YWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIG5vbi1kZXN0cnVjdGl2ZSwgcmV0dXJucyBhIG5ldyBwb2ludFxyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fYWRkKHRvUG9pbnQocG9pbnQpKTtcclxuXHR9LFxyXG5cclxuXHRfYWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIGRlc3RydWN0aXZlLCB1c2VkIGRpcmVjdGx5IGZvciBwZXJmb3JtYW5jZSBpbiBzaXR1YXRpb25zIHdoZXJlIGl0J3Mgc2FmZSB0byBtb2RpZnkgZXhpc3RpbmcgcG9pbnRcclxuXHRcdHRoaXMueCArPSBwb2ludC54O1xyXG5cdFx0dGhpcy55ICs9IHBvaW50Lnk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHN1YnRyYWN0KG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2Ygc3VidHJhY3Rpb24gb2YgdGhlIGdpdmVuIHBvaW50IGZyb20gdGhlIGN1cnJlbnQuXHJcblx0c3VidHJhY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fc3VidHJhY3QodG9Qb2ludChwb2ludCkpO1xyXG5cdH0sXHJcblxyXG5cdF9zdWJ0cmFjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR0aGlzLnggLT0gcG9pbnQueDtcclxuXHRcdHRoaXMueSAtPSBwb2ludC55O1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXZpZGVCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIGRpdmlzaW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0ZGl2aWRlQnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2RpdmlkZUJ5KG51bSk7XHJcblx0fSxcclxuXHJcblx0X2RpdmlkZUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggLz0gbnVtO1xyXG5cdFx0dGhpcy55IC89IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbXVsdGlwbHlCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIG11bHRpcGxpY2F0aW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0bXVsdGlwbHlCeTogZnVuY3Rpb24gKG51bSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fbXVsdGlwbHlCeShudW0pO1xyXG5cdH0sXHJcblxyXG5cdF9tdWx0aXBseUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggKj0gbnVtO1xyXG5cdFx0dGhpcy55ICo9IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2NhbGVCeShzY2FsZTogUG9pbnQpOiBQb2ludFxyXG5cdC8vIE11bHRpcGx5IGVhY2ggY29vcmRpbmF0ZSBvZiB0aGUgY3VycmVudCBwb2ludCBieSBlYWNoIGNvb3JkaW5hdGUgb2ZcclxuXHQvLyBgc2NhbGVgLiBJbiBsaW5lYXIgYWxnZWJyYSB0ZXJtcywgbXVsdGlwbHkgdGhlIHBvaW50IGJ5IHRoZVxyXG5cdC8vIFtzY2FsaW5nIG1hdHJpeF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2NhbGluZ18lMjhnZW9tZXRyeSUyOSNNYXRyaXhfcmVwcmVzZW50YXRpb24pXHJcblx0Ly8gZGVmaW5lZCBieSBgc2NhbGVgLlxyXG5cdHNjYWxlQnk6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIG5ldyBQb2ludCh0aGlzLnggKiBwb2ludC54LCB0aGlzLnkgKiBwb2ludC55KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVuc2NhbGVCeShzY2FsZTogUG9pbnQpOiBQb2ludFxyXG5cdC8vIEludmVyc2Ugb2YgYHNjYWxlQnlgLiBEaXZpZGUgZWFjaCBjb29yZGluYX
14775RlIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5XHJcblx0Ly8gZWFjaCBjb29yZGluYXRlIG9mIGBzY2FsZWAuXHJcblx0dW5zY2FsZUJ5OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHJldHVybiBuZXcgUG9pbnQodGhpcy54IC8gcG9pbnQueCwgdGhpcy55IC8gcG9pbnQueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByb3VuZCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50IHdpdGggcm91bmRlZCBjb29yZGluYXRlcy5cclxuXHRyb3VuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fcm91bmQoKTtcclxuXHR9LFxyXG5cclxuXHRfcm91bmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMueCA9IE1hdGgucm91bmQodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGgucm91bmQodGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmxvb3IoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGZsb29yZWQgY29vcmRpbmF0ZXMgKHJvdW5kZWQgZG93bikuXHJcblx0Zmxvb3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2Zsb29yKCk7XHJcblx0fSxcclxuXHJcblx0X2Zsb29yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmZsb29yKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLmZsb29yKHRoaXMueSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNlaWwoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGNlaWxlZCBjb29yZGluYXRlcyAocm91bmRlZCB1cCkuXHJcblx0Y2VpbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fY2VpbCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jZWlsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmNlaWwodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGguY2VpbCh0aGlzLnkpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0cnVuYygpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50IHdpdGggdHJ1bmNhdGVkIGNvb3JkaW5hdGVzIChyb3VuZGVkIHRvd2FyZHMgemVybykuXHJcblx0dHJ1bmM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX3RydW5jKCk7XHJcblx0fSxcclxuXHJcblx0X3RydW5jOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSB0cnVuYyh0aGlzLngpO1xyXG5cdFx0dGhpcy55ID0gdHJ1bmModGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlclBvaW50OiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGNhcnRlc2lhbiBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBjdXJyZW50IGFuZCB0aGUgZ2l2ZW4gcG9pbnRzLlxyXG5cdGRpc3RhbmNlVG86IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSB0b1BvaW50KHBvaW50KTtcclxuXHJcblx0XHR2YXIgeCA9IHBvaW50LnggLSB0aGlzLngsXHJcblx0XHQgICAgeSA9IHBvaW50LnkgLSB0aGlzLnk7XHJcblxyXG5cdFx0cmV0dXJuIE1hdGguc3FydCh4ICogeCArIHkgKiB5KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gcG9pbnQgaGFzIHRoZSBzYW1lIGNvb3JkaW5hdGVzLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IHRvUG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHJldHVybiBwb2ludC54ID09PSB0aGlzLnggJiZcclxuXHRcdCAgICAgICBwb2ludC55ID09PSB0aGlzLnk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBib3RoIGNvb3JkaW5hdGVzIG9mIHRoZSBnaXZlbiBwb2ludCBhcmUgbGVzcyB0aGFuIHRoZSBjb3JyZXNwb25kaW5nIGN1cnJlbnQgcG9pbnQgY29vcmRpbmF0ZXMgKGluIGFic29sdXRlIHZhbHVlcykuXHJcblx0Y29udGFpbnM6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSB0b1BvaW50KHBvaW50KTtcclxuXHJcblx0XHRyZXR1cm4gTWF0aC5hYnMocG9pbnQueCkgPD0gTWF0aC5hYnModGhpcy54KSAmJlxyXG5cdFx0ICAgICAgIE1hdGguYWJzKHBvaW50LnkpIDw9IE1hdGguYWJzKHRoaXMueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxyXG5cdHRvU3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gJ1BvaW50KCcgK1xyXG5cdFx0ICAgICAgICBmb3JtYXROdW0odGhpcy54KSArICcsICcgK1xyXG5cdFx0ICAgICAgICBmb3JtYXROdW0odGhpcy55KSArICcpJztcclxuXHR9XHJcbn07XHJcblxyXG4vLyBAZmFjdG9yeSBMLnBvaW50KHg6IE51bWJlciwgeTogTnVtYmVyLCByb3VuZD86IEJvb2xlYW4pXHJcbi8vIENyZWF0ZXMgYSBQb2ludCBvYmplY3Qgd2l0aCB0aGUgZ2l2ZW4gYHhgIGFuZCBgeWAgY29vcmRpbmF0ZXMuIElmIG9wdGlvbmFsIGByb3VuZGAgaXMgc2V0IHRvIHRydWUsIHJvdW5kcyB0aGUgYHhgIGFuZCBgeWAgdmFsdWVzLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwucG9pbnQoY29vcmRzOiBOdW1iZXJbXSlcclxuLy8gRXhwZWN0cyBhbiBhcnJheSBvZiB0aGUgZm9ybSBgW3gsIHldYCBpbnN0ZWFkLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwucG9pbnQoY29vcmRzOiBPYmplY3QpXHJcbi8vIEV4cGVjdHMgYSBwbGFpbiBvYmplY3Qgb2YgdGhlIGZvcm0gYHt4OiBOdW1iZXIsIHk6IE51bWJlcn1gIGluc3RlYWQuXHJcbmZ1bmN0aW9uIHRvUG9pbnQoeCwgeSwgcm91bmQpIHtcclxuXHRpZiAoeCBpbnN0YW5jZW9mIFBvaW50KSB7XHJcblx0XHRyZXR1cm4geDtcclxuXHR9XHJcblx0aWYgKGlzQXJyYXkoeCkpIHtcclxuXHRcdHJldHVybi
14775BuZXcgUG9pbnQoeFswXSwgeFsxXSk7XHJcblx0fVxyXG5cdGlmICh4ID09PSB1bmRlZmluZWQgfHwgeCA9PT0gbnVsbCkge1xyXG5cdFx0cmV0dXJuIHg7XHJcblx0fVxyXG5cdGlmICh0eXBlb2YgeCA9PT0gJ29iamVjdCcgJiYgJ3gnIGluIHggJiYgJ3knIGluIHgpIHtcclxuXHRcdHJldHVybiBuZXcgUG9pbnQoeC54LCB4LnkpO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IFBvaW50KHgsIHksIHJvdW5kKTtcclxufVxuXG4vKlxyXG4gKiBAY2xhc3MgQm91bmRzXHJcbiAqIEBha2EgTC5Cb3VuZHNcclxuICpcclxuICogUmVwcmVzZW50cyBhIHJlY3Rhbmd1bGFyIGFyZWEgaW4gcGl4ZWwgY29vcmRpbmF0ZXMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwMSA9IEwucG9pbnQoMTAsIDEwKSxcclxuICogcDIgPSBMLnBvaW50KDQwLCA2MCksXHJcbiAqIGJvdW5kcyA9IEwuYm91bmRzKHAxLCBwMik7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IGBCb3VuZHNgIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlIGJvdW5kcyBleGFtcGxlIGFib3ZlIGNhbiBiZSBwYXNzZWQgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBvdGhlckJvdW5kcy5pbnRlcnNlY3RzKFtbMTAsIDEwXSwgWzQwLCA2MF1dKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIE5vdGUgdGhhdCBgQm91bmRzYCBkb2VzIG5vdCBpbmhlcml0IGZyb20gTGVhZmV0J3MgYENsYXNzYCBvYmplY3QsXHJcbiAqIHdoaWNoIG1lYW5zIG5ldyBjbGFzc2VzIGNhbid0IGluaGVyaXQgZnJvbSBpdCwgYW5kIG5ldyBtZXRob2RzXHJcbiAqIGNhbid0IGJlIGFkZGVkIHRvIGl0IHdpdGggdGhlIGBpbmNsdWRlYCBmdW5jdGlvbi5cclxuICovXHJcblxyXG5mdW5jdGlvbiBCb3VuZHMoYSwgYikge1xyXG5cdGlmICghYSkgeyByZXR1cm47IH1cclxuXHJcblx0dmFyIHBvaW50cyA9IGIgPyBbYSwgYl0gOiBhO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHR0aGlzLmV4dGVuZChwb2ludHNbaV0pO1xyXG5cdH1cclxufVxyXG5cclxuQm91bmRzLnByb3RvdHlwZSA9IHtcclxuXHQvLyBAbWV0aG9kIGV4dGVuZChwb2ludDogUG9pbnQpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kcyB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50LlxyXG5cdGV4dGVuZDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHBvaW50ID0gdG9Qb2ludChwb2ludCk7XHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1pbjogUG9pbnRcclxuXHRcdC8vIFRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIHJlY3RhbmdsZS5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtYXg6IFBvaW50XHJcblx0XHQvLyBUaGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cdFx0aWYgKCF0aGlzLm1pbiAmJiAhdGhpcy5tYXgpIHtcclxuXHRcdFx0dGhpcy5taW4gPSBwb2ludC5jbG9uZSgpO1xyXG5cdFx0XHR0aGlzLm1heCA9IHBvaW50LmNsb25lKCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm1pbi54ID0gTWF0aC5taW4ocG9pbnQueCwgdGhpcy5taW4ueCk7XHJcblx0XHRcdHRoaXMubWF4LnggPSBNYXRoLm1heChwb2ludC54LCB0aGlzLm1heC54KTtcclxuXHRcdFx0dGhpcy5taW4ueSA9IE1hdGgubWluKHBvaW50LnksIHRoaXMubWluLnkpO1xyXG5cdFx0XHR0aGlzLm1heC55ID0gTWF0aC5tYXgocG9pbnQueSwgdGhpcy5tYXgueSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENlbnRlcihyb3VuZD86IEJvb2xlYW4pOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKHJvdW5kKSB7XHJcblx0XHRyZXR1cm4gbmV3IFBvaW50KFxyXG5cdFx0ICAgICAgICAodGhpcy5taW4ueCArIHRoaXMubWF4LngpIC8gMixcclxuXHRcdCAgICAgICAgKHRoaXMubWluLnkgKyB0aGlzLm1heC55KSAvIDIsIHJvdW5kKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEJvdHRvbUxlZnQoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBib3R0b20tbGVmdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldEJvdHRvbUxlZnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgUG9pbnQodGhpcy5taW4ueCwgdGhpcy5tYXgueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRUb3BSaWdodCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHRvcC1yaWdodCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFRvcFJpZ2h0OiBmdW5jdGlvbiAoKSB7IC8vIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gbmV3IFBvaW50KHRoaXMubWF4LngsIHRoaXMubWluLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0VG9wTGVmdCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHRvcC1sZWZ0IHBvaW50IG9mIHRoZSBib3VuZHMgKGkuZS4gW2B0aGlzLm1pbmBdKCNib3VuZHMtbWluKSkuXHJcblx0Z2V0VG9wTGVmdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMubWluOyAvLyBsZWZ0LCB0b3BcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEJvdHRvbVJpZ2h0KCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgYm90dG9tLXJpZ2h0IHBvaW50IG9mIHRoZSBib3VuZHMgKGkuZS4gW2B0aGlzLm1heGBdKCNib3VuZHMtbWF4KSkuXHJcblx0Z2V0Qm90dG9tUmlnaHQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm1heDsgLy8gcmlnaHQsIGJvdHRvbVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U2l6ZSgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHNpemUgb2YgdGhlIGdpdmVuIGJvdW5kc1xyXG5cdGdldFNpemU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybi
14775B0aGlzLm1heC5zdWJ0cmFjdCh0aGlzLm1pbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIG9uZS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKHBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBwb2ludC5cclxuXHRjb250YWluczogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0dmFyIG1pbiwgbWF4O1xyXG5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBQb2ludCkge1xyXG5cdFx0XHRvYmogPSB0b1BvaW50KG9iaik7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRvYmogPSB0b0JvdW5kcyhvYmopO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChvYmogaW5zdGFuY2VvZiBCb3VuZHMpIHtcclxuXHRcdFx0bWluID0gb2JqLm1pbjtcclxuXHRcdFx0bWF4ID0gb2JqLm1heDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG1pbiA9IG1heCA9IG9iajtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gKG1pbi54ID49IHRoaXMubWluLngpICYmXHJcblx0XHQgICAgICAgKG1heC54IDw9IHRoaXMubWF4LngpICYmXHJcblx0XHQgICAgICAgKG1pbi55ID49IHRoaXMubWluLnkpICYmXHJcblx0XHQgICAgICAgKG1heC55IDw9IHRoaXMubWF4LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW50ZXJzZWN0cyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgaW50ZXJzZWN0cyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHsgLy8gKEJvdW5kcykgLT4gQm9vbGVhblxyXG5cdFx0Ym91bmRzID0gdG9Cb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgbWluID0gdGhpcy5taW4sXHJcblx0XHQgICAgbWF4ID0gdGhpcy5tYXgsXHJcblx0XHQgICAgbWluMiA9IGJvdW5kcy5taW4sXHJcblx0XHQgICAgbWF4MiA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeEludGVyc2VjdHMgPSAobWF4Mi54ID49IG1pbi54KSAmJiAobWluMi54IDw9IG1heC54KSxcclxuXHRcdCAgICB5SW50ZXJzZWN0cyA9IChtYXgyLnkgPj0gbWluLnkpICYmIChtaW4yLnkgPD0gbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4SW50ZXJzZWN0cyAmJi
14775B5SW50ZXJzZWN0cztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG92ZXJsYXBzKG90aGVyQm91bmRzOiBCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBvdmVybGFwcyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykgeyAvLyAoQm91bmRzKSAtPiBCb29sZWFuXHJcblx0XHRib3VuZHMgPSB0b0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBtaW4gPSB0aGlzLm1pbixcclxuXHRcdCAgICBtYXggPSB0aGlzLm1heCxcclxuXHRcdCAgICBtaW4yID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXgyID0gYm91bmRzLm1heCxcclxuXHRcdCAgICB4T3ZlcmxhcHMgPSAobWF4Mi54ID4gbWluLngpICYmIChtaW4yLnggPCBtYXgueCksXHJcblx0XHQgICAgeU92ZXJsYXBzID0gKG1heDIueSA+IG1pbi55KSAmJiAobWluMi55IDwgbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4T3ZlcmxhcHMgJiYgeU92ZXJsYXBzO1xyXG5cdH0sXHJcblxyXG5cdGlzVmFsaWQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhISh0aGlzLm1pbiAmJiB0aGlzLm1heCk7XHJcblx0fVxyXG59O1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwuYm91bmRzKGNvcm5lcjE6IFBvaW50LCBjb3JuZXIyOiBQb2ludClcclxuLy8gQ3JlYXRlcyBhIEJvdW5kcyBvYmplY3QgZnJvbSB0d28gY29ybmVycyBjb29yZGluYXRlIHBhaXJzLlxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5ib3VuZHMocG9pbnRzOiBQb2ludFtdKVxyXG4vLyBDcmVhdGVzIGEgQm91bmRzIG9iamVjdCBmcm9tIHRoZSBnaXZlbiBhcnJheSBvZiBwb2ludHMuXHJcbmZ1bmN0aW9uIHRvQm91bmRzKGEsIGIpIHtcclxuXHRpZiAoIWEgfHwgYSBpbnN0YW5jZW9mIEJvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgQm91bmRzKGEsIGIpO1xyXG59XG5cbi8qXHJcbiAqIEBjbGFzcyBMYXRMbmdCb3VuZHNcclxuICogQGFrYSBMLkxhdExuZ0JvdW5kc1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgcmVjdGFuZ3VsYXIgZ2VvZ3JhcGhpY2FsIGFyZWEgb24gYSBtYXAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBjb3JuZXIxID0gTC5sYXRMbmcoNDAuNzEyLCAtNzQuMjI3KSxcclxuICogY29ybmVyMiA9IEwubGF0TG5nKDQwLjc3NCwgLTc0LjEyNSksXHJcbiAqIGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGNvcm5lcjEsIGNvcm5lcjIpO1xyXG4gKiBgYGBcclxuICpcclxuICogQWxsIExlYWZsZXQgbWV0aG9kcyB0aGF0IGFjY2VwdCBMYXRMbmdCb3VuZHMgb2JqZWN0cyBhbHNvIGFjY2VwdCB0aGVtIGluIGEgc2ltcGxlIEFycmF5IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGUgYm91bmRzIGV4YW1wbGUgYWJvdmUgY2FuIGJlIHBhc3NlZCBsaWtlIHRoaXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcC5maXRCb3VuZHMoW1xyXG4gKiBcdFs0MC43MTIsIC03NC4yMjddLFxyXG4gKiBcdFs0MC43NzQsIC03NC4xMjVdXHJcbiAqIF0pO1xyXG4gKiBgYGBcclxuICpcclxuICogQ2F1dGlvbjogaWYgdGhlIGFyZWEgY3Jvc3NlcyB0aGUgYW50aW1lcmlkaWFuIChvZnRlbiBjb25mdXNlZCB3aXRoIHRoZSBJbnRlcm5hdGlvbmFsIERhdGUgTGluZSksIHlvdSBtdXN0IHNwZWNpZnkgY29ybmVycyBfb3V0c2lkZV8gdGhlIFstMTgwLCAxODBdIGRlZ3JlZXMgbG9uZ2l0dWRlIHJhbmdlLlxyXG4gKlxyXG4gKiBOb3RlIHRoYXQgYExhdExuZ0JvdW5kc2AgZG9lcyBub3QgaW5oZXJpdCBmcm9tIExlYWZldCdzIGBDbGFzc2Agb2JqZWN0LFxyXG4gKiB3aGljaCBtZWFucyBuZXcgY2xhc3NlcyBjYW4ndCBpbmhlcml0IGZyb20gaXQsIGFuZCBuZXcgbWV0aG9kc1xyXG4gKiBjYW4ndCBiZSBhZGRlZCB0byBpdCB3aXRoIHRoZSBgaW5jbHVkZWAgZnVuY3Rpb24uXHJcbiAqL1xyXG5cclxuZnVuY3Rpb24gTGF0TG5nQm91bmRzKGNvcm5lcjEsIGNvcm5lcjIpIHsgLy8gKExhdExuZywgTGF0TG5nKSBvciAoTGF0TG5nW10pXHJcblx0aWYgKCFjb3JuZXIxKSB7IHJldHVybjsgfVxyXG5cclxuXHR2YXIgbGF0bG5ncyA9IGNvcm5lcjIgPyBbY29ybmVyMSwgY29ybmVyMl0gOiBjb3JuZXIxO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF0bG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0dGhpcy5leHRlbmQobGF0bG5nc1tpXSk7XHJcblx0fVxyXG59XHJcblxyXG5MYXRMbmdCb3VuZHMucHJvdG90eXBlID0ge1xyXG5cclxuXHQvLyBAbWV0aG9kIGV4dGVuZChsYXRsbmc6IExhdExuZyk6IHRoaXNcclxuXHQvLyBFeHRlbmQgdGhlIGJvdW5kcyB0byBjb250YWluIHRoZSBnaXZlbiBwb2ludFxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGV4dGVuZChvdGhlckJvdW5kczogTGF0TG5nQm91bmRzKTogdGhpc1xyXG5cdC8vIEV4dGVuZCB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIGJvdW5kc1xyXG5cdGV4dGVuZDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0dmFyIHN3ID0gdGhpcy5fc291dGhXZXN0LFxyXG5cdFx0ICAgIG5lID0gdGhpcy5fbm9ydGhFYXN0LFxyXG5cdFx0ICAgIHN3MiwgbmUyO1xyXG5cclxuXHRcdGlmIChvYmogaW5zdGFuY2VvZiBMYXRMbmcpIHtcclxuXHRcdFx0c3cyID0gb2JqO1xyXG5cdFx0XHRuZTIgPSBvYmo7XHJcblxyXG5cdFx0fSBlbHNlIGlmIChvYmogaW5zdGFuY2VvZiBMYXRMbmdCb3VuZHMpIHtcclxuXHRcdFx0c3cyID0gb2JqLl9zb3V0aFdlc3Q7XHJcblx0XHRcdG5lMiA9IG9iai5fbm9ydGhFYXN0O1xyXG5cclxuXHRcdFx0aWYgKCFzdzIgfHwgIW5lMikgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHJldHVybiBvYmogPyB0aGlzLmV4dGVuZCh0b0xhdExuZyhvYmopIHx8IHRvTGF0TG5nQm91bmRzKG9iaikpIDogdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXN3ICYmICFuZSkge1xyXG5cdFx0XHR0aGlzLl9zb3V0aFdlc3QgPSBuZXcgTGF0TG5nKHN3Mi5sYXQsIHN3Mi5sbmcpO1xyXG5cdFx0XHR0aGlzLl9ub3J0aEVhc3QgPSBuZXcgTGF0TG5nKG5lMi5sYXQsIG5lMi5sbmcpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cubGF0ID0gTWF0aC5taW4oc3cyLmxhdCwgc3cubGF0KTtcclxuXHRcdFx0c3cubG5nID0gTWF0aC5taW4oc3cyLmxuZywgc3cubG5nKTtcclxuXHRcdFx0bmUubGF0ID0gTWF0aC5tYXgobmUyLmxhdCwgbmUubGF0KTtcclxuXHRcdFx0bmUubG5nID0gTWF0aC5tYXgobmUyLmxuZywgbmUubG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhZChidWZmZXJSYXRpbzogTnVtYmVyKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBib3VuZHMgY3JlYXRlZCBieSBleHRlbmRpbmcgb3IgcmV0cmFjdGluZyB0aGUgY3VycmVudCBib3VuZHMgYnkgYSBnaXZlbiByYXRpbyBpbiBlYWNoIGRpcmVjdGlvbi5cclxuXHQvLyBGb3IgZXhhbXBsZSwgYSByYXRpbyBvZiAwLjUgZXh0ZW5kcyB0aGUgYm91bmRzIGJ5IDUwJSBpbiBlYWNoIGRpcmVjdGlvbi5cclxuXHQvLyBOZWdhdGl2ZSB2YWx1ZXMgd2lsbCByZXRyYWN0IHRoZSBib3VuZHMuXHJcblx0cGFkOiBmdW5jdGlvbiAoYnVmZmVyUmF0aW8pIHtcclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBoZWlnaHRCdWZmZXIgPSBNYXRoLmFicyhzdy5sYXQgLSBuZS5sYXQpICogYnVmZmVyUmF0aW8sXHJcblx0XHQgICAgd2lkdGhCdWZmZXIgPSBNYXRoLmFicyhzdy5sbmcgLSBuZS5sbmcpICogYnVmZmVyUmF0aW87XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMYXRMbmdCb3VuZHMoXHJcblx0XHQgICAgICAgIG5ldyBMYXRMbmcoc3cubGF0IC0gaGVpZ2h0QnVmZmVyLCBzdy5sbmcgLSB3aWR0aEJ1ZmZlciksXHJcblx0XHQgICAgICAgIG5ldyBMYXRMbmcobmUubGF0ICsgaGVpZ2h0QnVmZmVyLCBuZS5sbmcgKyB3aWR0aEJ1ZmZlcikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMYXRMbmcoXHJcblx0XHQgICAgICAgICh0aGlzLl9zb3V0aFdlc3QubGF0ICsgdGh
14775pcy5fbm9ydGhFYXN0LmxhdCkgLyAyLFxyXG5cdFx0ICAgICAgICAodGhpcy5fc291dGhXZXN0LmxuZyArIHRoaXMuX25vcnRoRWFzdC5sbmcpIC8gMik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aFdlc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGgtd2VzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFNvdXRoV2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE5vcnRoRWFzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aC1lYXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Tm9ydGhFYXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbm9ydGhFYXN0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGhXZXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIG5vcnRoLXdlc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXROb3J0aFdlc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTGF0TG5nKHRoaXMuZ2V0Tm9ydGgoKSwgdGhpcy5nZXRXZXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U291dGhFYXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIHNvdXRoLWVhc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRTb3V0aEVhc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTGF0TG5nKHRoaXMuZ2V0U291dGgoKSwgdGhpcy5nZXRFYXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0V2VzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSB3ZXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0V2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0U291dGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWFzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBlYXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0RWFzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX25vcnRoRWFzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXROb3J0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0Tm9ydGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBvbmUuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMgKGxhdGxuZzogTGF0TG5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIHBvaW50LlxyXG5cdGNvbnRhaW5zOiBmdW5jdGlvbiAob2JqKSB7IC8vIChMYXRMbmdCb3VuZHMpIG9yIChMYXRMbmcpIC0+IEJvb2xlYW5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBMYXRMbmcgfHwgJ2xhdCcgaW4gb2JqKSB7XHJcblx0XHRcdG9iaiA9IHRvTGF0TG5nKG9iaik7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRvYmogPSB0b0xhdExuZ0JvdW5kcyhvYmopO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIsIG5lMjtcclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTGF0TG5nQm91bmRzKSB7XHJcblx0XHRcdHN3MiA9IG9iai5nZXRTb3V0aFdlc3QoKTtcclxuXHRcdFx0bmUyID0gb2JqLmdldE5vcnRoRWFzdCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cyID0gbmUyID0gb2JqO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiAoc3cyLmxhdCA+PSBzdy5sYXQpICYmIChuZTIubGF0IDw9IG5lLmxhdCkgJiZcclxuXHRcdCAgICAgICAoc3cyLmxuZyA+PSBzdy5sbmcpICYmIChuZTIubG5nIDw9IG5lLmxuZyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnRlcnNlY3RzKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpbnRlcnNlY3RzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdGJvdW5kcyA9IHRvTGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIHN3ID0gdGhpcy5fc291dGhXZXN0LFxyXG5cdFx0ICAgIG5lID0gdGhpcy5fbm9ydGhFYXN0LFxyXG5cdFx0ICAgIHN3MiA9IGJvdW5kcy5nZXRTb3V0aFdlc3QoKSxcclxuXHRcdCAgICBuZTIgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblxyXG5cdFx0ICAgIGxhdEludGVyc2VjdHMgPSAobmUyLmxhdCA+PSBzdy5sYXQpICYmIChzdzIubGF0IDw9IG5lLmxhdCksXHJcblx0XHQgICAgbG5nSW50ZXJzZWN0cyA9IChuZTIubG5nID49IHN3LmxuZykgJiYgKHN3Mi5sbmcgPD0gbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0SW50ZXJzZWN0cyAmJi
14775BsbmdJbnRlcnNlY3RzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb3ZlcmxhcHMob3RoZXJCb3VuZHM6IEJvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIG92ZXJsYXBzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0Ym91bmRzID0gdG9MYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyID0gYm91bmRzLmdldFNvdXRoV2VzdCgpLFxyXG5cdFx0ICAgIG5lMiA9IGJvdW5kcy5nZXROb3J0aEVhc3QoKSxcclxuXHJcblx0XHQgICAgbGF0T3ZlcmxhcHMgPSAobmUyLmxhdCA+IHN3LmxhdCkgJiYgKHN3Mi5sYXQgPCBuZS5sYXQpLFxyXG5cdFx0ICAgIGxuZ092ZXJsYXBzID0gKG5lMi5sbmcgPiBzdy5sbmcpICYmIChzdzIubG5nIDwgbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0T3ZlcmxhcHMgJiYgbG5nT3ZlcmxhcHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JCb3hTdHJpbmcoKTogU3RyaW5nXHJcblx0Ly8gUmV0dXJucyBhIHN0cmluZyB3aXRoIGJvdW5kaW5nIGJveCBjb29yZGluYXRlcyBpbiBhICdzb3V0aHdlc3RfbG5nLHNvdXRod2VzdF9sYXQsbm9ydGhlYXN0X2xuZyxub3J0aGVhc3RfbGF0JyBmb3JtYXQuIFVzZWZ1bCBmb3Igc2VuZGluZyByZXF1ZXN0cyB0byB3ZWIgc2VydmljZXMgdGhhdCByZXR1cm4gZ2VvIGRhdGEuXHJcblx0dG9CQm94U3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gW3RoaXMuZ2V0V2VzdCgpLCB0aGlzLmdldFNvdXRoKCksIHRoaXMuZ2V0RWFzdCgpLCB0aGlzLmdldE5vcnRoKCldLmpvaW4oJywnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlckJvdW5kczogTGF0TG5nQm91bmRzLCBtYXhNYXJnaW4/OiBOdW1iZXIpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpcyBlcXVpdmFsZW50ICh3aXRoaW4gYSBzbWFsbCBtYXJnaW4gb2YgZXJyb3IpIHRvIHRoZSBnaXZlbiBib3VuZHMuIFRoZSBtYXJnaW4gb2YgZXJyb3IgY2FuIGJlIG92ZXJyaWRkZW4gYnkgc2V0dGluZyBgbWF4TWFyZ2luYCB0byBhIHNtYWxsIG51bWJlci5cclxuXHRlcXVhbHM6IGZ1bmN0aW9uIChib3VuZHMsIG1heE1hcmdpbikge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0Ym91bmRzID0gdG9MYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0LmVxdWFscyhib3VuZHMuZ2V0U291dGhXZXN0KCksIG1heE1hcmdpbikgJiZcclxuXHRcdCAgICAgICB0aGlzLl9ub3J0aEVhc3QuZXF1YWxzKGJvdW5kcy5nZXROb3J0aEVhc3QoKSwgbWF4TWFyZ2luKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGlzVmFsaWQoKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBib3VuZHMgYXJlIHByb3Blcmx5IGluaXRpYWxpemVkLlxyXG5cdGlzVmFsaWQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhISh0aGlzLl9zb3V0aFdlc3QgJiYgdGhpcy5fbm9ydGhFYXN0KTtcclxuXHR9XHJcbn07XHJcblxyXG4vLyBUT0RPIEludGVybmF0aW9uYWwgZGF0ZSBsaW5lP1xyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXRMbmdCb3VuZHMoY29ybmVyMTogTGF0TG5nLCBjb3JuZXIyOiBMYXRMbmcpXHJcbi8vIENyZWF0ZXMgYSBgTGF0TG5nQm91bmRzYCBvYmplY3QgYnkgZGVmaW5pbmcgdHdvIGRpYWdvbmFsbHkgb3Bwb3NpdGUgY29ybmVycyBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nQm91bmRzKGxhdGxuZ3M6IExhdExuZ1tdKVxyXG4vLyBDcmVhdGVzIGEgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGRlZmluZWQgYnkgdGhlIGdlb2dyYXBoaWNhbCBwb2ludHMgaXQgY29udGFpbnMuIFZlcnkgdXNlZnVsIGZvciB6b29taW5nIHRoZSBtYXAgdG8gZml0IGEgcGFydGljdWxhciBzZXQgb2YgbG9jYXRpb25zIHdpdGggW2BmaXRCb3VuZHNgXSgjbWFwLWZpdGJvdW5kcykuXHJcbmZ1bmN0aW9uIHRvTGF0TG5nQm91bmRzKGEsIGIpIHtcclxuXHRpZiAoYSBpbnN0YW5jZW9mIExhdExuZ0JvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTGF0TG5nQm91bmRzKGEsIGIpO1xyXG59XG5cbi8qIEBjbGFzcyBMYXRMbmdcclxuICogQGFrYSBMLkxhdExuZ1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgZ2VvZ3JhcGhpY2FsIHBvaW50IHdpdGggYSBjZXJ0YWluIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYFxyXG4gKiB2YXIgbGF0bG5nID0gTC5sYXRMbmcoNTAuNSwgMzAuNSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IExhdExuZyBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSBhbmQgc2ltcGxlIG9iamVjdCBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlc2UgbGluZXMgYXJlIGVxdWl2YWxlbnQ6XHJcbiAqXHJcbiAqIGBgYFxyXG4gKiBtYXAucGFuVG8oWzUwLCAzMF0pO1xyXG4gKiBtYXAucGFuVG8oe2xvbjogMzAsIGxhdDogNTB9KTtcclxuICogbWFwLnBhblRvKHtsYXQ6IDUwLCBsbmc6IDMwfSk7XHJcbiAqIG1hcC5wYW5UbyhMLmxhdExuZyg1MCwgMzApKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIE5vdGUgdGhhdCBgTGF0TG5nYCBkb2VzIG5vdCBpbmhlcml0IGZyb20gTGVhZmxldCdzIGBDbGFzc2Agb2JqZWN0LFxyXG4gKiB3aGljaCBtZWFucyBuZXcgY2xhc3NlcyBjYW4ndCBpbmhlcml0IGZyb20gaXQsIGFuZCBuZXcgbWV0aG9kc1xyXG4gKiBjYW4ndCBiZSBhZGRlZCB0byBpdCB3aXRoIHRoZSBgaW5jbHVkZWAgZnVuY3Rpb24uXHJcbiAqL1xyXG5cclxuZnVuY3Rpb24gTGF0TG5nKGxhdCwgbG5nLCBhbHQpIHtcclxuXHRpZiAoaXNOYU4obGF0KSB8fCBpc05hTihsbmcpKSB7XHJcblx0XHR0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgTGF0TG5nIG9iamVjdDogKCcgKyBsYXQgKyAnLCAnICsgbG5nICsgJyknKTtcclxuXHR9XHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBsYXQ6IE51bWJlclxyXG5cdC8vIExhdGl0dWRlIGluIGRlZ3JlZXNcclxuXHR0aGlzLmxhdCA9ICtsYXQ7XHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBsbmc6IE51bWJlclxyXG5cdC8vIExvbmdpdHVkZSBpbiBkZWdyZWVzXHJcblx0dGhpcy5sbmcgPSArbG5nO1xyXG5cclxuXHQvLyBAcHJvcGVydHkgYWx0OiBOdW1iZXJcclxuXHQvLyBBbHRpdHVkZSBpbiBtZXRlcnMgKG9wdGlvbmFsKVxyXG5cdGlmIChhbHQgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0dGhpcy5hbHQgPSArYWx0O1xyXG5cdH1cclxufVxyXG5cclxuTGF0TG5nLnByb3RvdHlwZSA9IHtcclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlckxhdExuZzogTGF0TG5nLCBtYXhNYXJnaW4/OiBOdW1iZXIpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGdpdmVuIGBMYXRMbmdgIHBvaW50IGlzIGF0IHRoZSBzYW1lIHBvc2l0aW9uICh3aXRoaW4gYSBzbWFsbCBtYXJnaW4gb2YgZXJyb3IpLiBUaGUgbWFyZ2luIG9mIGVycm9yIGNhbiBiZSBvdmVycmlkZGVuIGJ5IHNldH
14775RpbmcgYG1heE1hcmdpbmAgdG8gYSBzbWFsbCBudW1iZXIuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAob2JqLCBtYXhNYXJnaW4pIHtcclxuXHRcdGlmICghb2JqKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdG9iaiA9IHRvTGF0TG5nKG9iaik7XHJcblxyXG5cdFx0dmFyIG1hcmdpbiA9IE1hdGgubWF4KFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxhdCAtIG9iai5sYXQpLFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxuZyAtIG9iai5sbmcpKTtcclxuXHJcblx0XHRyZXR1cm4gbWFyZ2luIDw9IChtYXhNYXJnaW4gPT09IHVuZGVmaW5lZCA/IDEuMEUtOSA6IG1heE1hcmdpbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCAoZm9yIGRlYnVnZ2luZyBwdXJwb3NlcykuXHJcblx0dG9TdHJpbmc6IGZ1bmN0aW9uIChwcmVjaXNpb24pIHtcclxuXHRcdHJldHVybiAnTGF0TG5nKCcgK1xyXG5cdFx0ICAgICAgICBmb3JtYXROdW0odGhpcy5sYXQsIHByZWNpc2lvbikgKyAnLCAnICtcclxuXHRcdCAgICAgICAgZm9ybWF0TnVtKHRoaXMubG5nLCBwcmVjaXNpb24pICsgJyknO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlckxhdExuZzogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgKGluIG1ldGVycykgdG8gdGhlIGdpdmVuIGBMYXRMbmdgIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIFtTcGhlcmljYWwgTGF3IG9mIENvc2luZXNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NwaGVyaWNhbF9sYXdfb2ZfY29zaW5lcykuXHJcblx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKG90aGVyKSB7XHJcblx0XHRyZXR1cm4gRWFydGguZGlzdGFuY2UodGhpcywgdG9MYXRMbmcob3RoZXIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXAoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyBhIG5ldyBgTGF0TG5nYCBvYmplY3Qgd2l0aCB0aGUgbG9uZ2l0dWRlIHdyYXBwZWQgc28gaXQncyBhbHdheXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMuXHJcblx0d3JhcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIEVhcnRoLndyYXBMYXRMbmcodGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JvdW5kcyhzaXplSW5NZXRlcnM6IE51bWJlcik6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBuZXcgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGluIHdoaWNoIGVhY2ggYm91bmRhcnkgaXMgYHNpemVJbk1ldGVycy8yYCBtZXRlcnMgYXBhcnQgZnJvbSB0aGUgYExhdExuZ2AuXHJcblx0dG9Cb3VuZHM6IGZ1bmN0aW9uIChzaXplSW5NZXRlcnMpIHtcclxuXHRcdHZhciBsYXRBY2N1cmFjeSA9IDE4MCAqIHNpemVJbk1ldGVycyAvIDQwMDc1MDE3LFxyXG5cdFx0ICAgIGxuZ0FjY3VyYWN5ID0gbGF0QWNjdXJhY3kgLyBNYXRoLmNvcygoTWF0aC5QSSAvIDE4MCkgKiB0aGlzLmxhdCk7XHJcblxyXG5cdFx0cmV0dXJuIHRvTGF0TG5nQm91bmRzKFxyXG5cdFx0ICAgICAgICBbdGhpcy5sYXQgLSBsYXRBY2N1cmFjeSwgdGhpcy5sbmcgLSBsbmdBY2N1cmFjeV0sXHJcblx0XHQgICAgICAgIFt0aGlzLmxhdCArIGxhdEFjY3VyYWN5LCB0aGlzLmxuZyArIGxuZ0FjY3VyYWN5XSk7XHJcblx0fSxcclxuXHJcblx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTGF0TG5nKHRoaXMubGF0LCB0aGlzLmxuZywgdGhpcy5hbHQpO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcobGF0aXR1ZGU6IE51bWJlciwgbG9uZ2l0dWRlOiBOdW1iZXIsIGFsdGl0dWRlPzogTnVtYmVyKTogTGF0TG5nXHJcbi8vIENyZWF0ZXMgYW4gb2JqZWN0IHJlcHJlc2VudGluZyBhIGdlb2dyYXBoaWNhbCBwb2ludCB3aXRoIHRoZSBnaXZlbiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIChhbmQgb3B0aW9uYWxseSBhbHRpdHVkZSkuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcoY29vcmRzOiBBcnJheSk6IExhdExuZ1xyXG4vLyBFeHBlY3RzIGFuIGFycmF5IG9mIHRoZSBmb3JtIGBbTnVtYmVyLCBOdW1iZXJdYCBvciBgW051bWJlciwgTnVtYmVyLCBOdW1iZXJdYCBpbnN0ZWFkLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGNvb3JkczogT2JqZWN0KTogTGF0TG5nXHJcbi8vIEV4cGVjdHMgYW4gcGxhaW4gb2JqZWN0IG9mIHRoZSBmb3JtIGB7bGF0OiBOdW1iZXIsIGxuZzogTnVtYmVyfWAgb3IgYHtsYXQ6IE51bWJlciwgbG5nOiBOdW1iZXIsIGFsdDogTnVtYmVyfWAgaW5zdGVhZC5cclxuXHJcbmZ1bmN0aW9uIHRvTGF0TG5nKGEsIGIsIGMpIHtcclxuXHRpZiAoYSBpbnN0YW5jZW9mIExhdExuZykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdGlmIChpc0FycmF5KGEpICYmIHR5cGVvZiBhWzBdICE9PSAnb2JqZWN0Jykge1xyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAzKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTGF0TG5nKGFbMF0sIGFbMV0sIGFbMl0pO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAyKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTGF0TG5nKGFbMF0sIGFbMV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIG51bGw7XHJcblx0fVxyXG5cdGlmIChhID09PSB1bmRlZmluZWQgfHwgYSA9PT0gbnVsbCkge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdGlmICh0eXBlb2YgYSA9PT0gJ29iamVjdCcgJiYgJ2xhdCcgaW4gYSkge1xyXG5cdFx0cmV0dXJuIG5ldyBMYXRMbmcoYS5sYXQsICdsbmcnIGluIGEgPyBhLmxuZyA6IGEubG9uLCBhLmFsdCk7XHJcblx0fVxyXG5cdGlmIChiID09PSB1bmRlZmluZWQpIHtcclxuXHRcdHJldHVybiBudWxsO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IExhdExuZyhhLCBiLCBjKTtcclxufVxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkJhc2VcclxuICogT2JqZWN0IHRoYXQgZGVmaW5lcyBjb29yZGluYX
14775RlIHJlZmVyZW5jZSBzeXN0ZW1zIGZvciBwcm9qZWN0aW5nXHJcbiAqIGdlb2dyYXBoaWNhbCBwb2ludHMgaW50byBwaXhlbCAoc2NyZWVuKSBjb29yZGluYXRlcyBhbmQgYmFjayAoYW5kIHRvXHJcbiAqIGNvb3JkaW5hdGVzIGluIG90aGVyIHVuaXRzIGZvciBbV01TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XZWJfTWFwX1NlcnZpY2UpIHNlcnZpY2VzKS4gU2VlXHJcbiAqIFtzcGF0aWFsIHJlZmVyZW5jZSBzeXN0ZW1dKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29vcmRpbmF0ZV9yZWZlcmVuY2Vfc3lzdGVtKS5cclxuICpcclxuICogTGVhZmxldCBkZWZpbmVzIHRoZSBtb3N0IHVzdWFsIENSU3MgYnkgZGVmYXVsdC4gSWYgeW91IHdhbnQgdG8gdXNlIGFcclxuICogQ1JTIG5vdCBkZWZpbmVkIGJ5IGRlZmF1bHQsIHRha2UgYSBsb29rIGF0IHRoZVxyXG4gKiBbUHJvajRMZWFmbGV0XShodHRwczovL2dpdGh1Yi5jb20va2FydGVuYS9Qcm9qNExlYWZsZXQpIHBsdWdpbi5cclxuICpcclxuICogTm90ZSB0aGF0IHRoZSBDUlMgaW5zdGFuY2VzIGRvIG5vdCBpbmhlcml0IGZyb20gTGVhZmV0J3MgYENsYXNzYCBvYmplY3QsXHJcbiAqIGFuZCBjYW4ndCBiZSBpbnN0YW50aWF0ZWQuIEFsc28sIG5ldyBjbGFzc2VzIGNhbid0IGluaGVyaXQgZnJvbSB0aGVtLFxyXG4gKiBhbmQgbWV0aG9kcyBjYW4ndCBiZSBhZGRlZCB0byB0aGVtIHdpdGggdGhlIGBpbmNsdWRlYCBmdW5jdGlvbi5cclxuICovXHJcblxyXG52YXIgQ1JTID0ge1xyXG5cdC8vIEBtZXRob2QgbGF0TG5nVG9Qb2ludChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMgaW50byBwaXhlbCBjb29yZGluYXRlcyBmb3IgYSBnaXZlbiB6b29tLlxyXG5cdGxhdExuZ1RvUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdGlvbi5wcm9qZWN0KGxhdGxuZyksXHJcblx0XHQgICAgc2NhbGUgPSB0aGlzLnNjYWxlKHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLnRyYW5zZm9ybWF0aW9uLl90cmFuc2Zvcm0ocHJvamVjdGVkUG9pbnQsIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBUaGUgaW52ZXJzZSBvZiBgbGF0TG5nVG9Qb2ludGAuIFByb2plY3RzIHBpeGVsIGNvb3JkaW5hdGVzIG9uIGEgZ2l2ZW5cclxuXHQvLyB6b29tIGludG8gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzLlxyXG5cdHBvaW50VG9MYXRMbmc6IGZ1bmN0aW9uIChwb2ludCwgem9vbSkge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5zY2FsZSh6b29tKSxcclxuXHRcdCAgICB1bnRyYW5zZm9ybWVkUG9pbnQgPSB0aGlzLnRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHBvaW50LCBzY2FsZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdGlvbi51bnByb2plY3QodW50cmFuc2Zvcm1lZFBvaW50KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHByb2plY3QobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIFByb2plY3RzIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBpbnRvIGNvb3JkaW5hdGVzIGluIHVuaXRzIGFjY2VwdGVkIGZvclxyXG5cdC8vIHRoaXMgQ1JTIChlLmcuIG1ldGVycyBmb3IgRVBTRzozODU3LCBmb3IgcGFzc2luZyBpdCB0byBXTVMgc2VydmljZXMpLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLnByb2plY3Rpb24ucHJvamVjdChsYXRsbmcpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5wcm9qZWN0KHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcHJvamVjdGVkIGNvb3JkaW5hdGUgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBMYXRMbmcuXHJcblx0Ly8gVGhlIGludmVyc2Ugb2YgYHByb2plY3RgLlxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0aW9uLnVucHJvamVjdChwb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzY2FsZSh6b29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzY2FsZSB1c2VkIHdoZW4gdHJhbnNmb3JtaW5nIHByb2plY3RlZCBjb29yZGluYXRlcyBpbnRvXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIGEgcGFydGljdWxhciB6b29tLiBGb3IgZXhhbXBsZSwgaXQgcmV0dXJuc1xyXG5cdC8vIGAyNTYgKiAyXnpvb21gIGZvciBNZXJjYXRvci1iYXNlZCBDUlMuXHJcblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHRyZXR1cm4gMjU2ICogTWF0aC5wb3coMiwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tKHNjYWxlOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBJbnZlcnNlIG9mIGBzY2FsZSgpYCwgcmV0dXJucyB0aGUgem9vbSBsZXZlbCBjb3JyZXNwb25kaW5nIHRvIGEgc2NhbGVcclxuXHQvLyBmYWN0b3Igb2YgYHNjYWxlYC5cclxuXHR6b29tOiBmdW5jdGlvbiAoc2NhbGUpIHtcclxuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSAvIDI1NikgLyBNYXRoLkxOMjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFByb2plY3RlZEJvdW5kcyh6b29tOiBOdW1iZXIpOiBCb3VuZHNcclxuXHQvLyBSZXR1cm5zIHRoZSBwcm9qZWN0aW9uJ3MgYm91bmRzIHNjYWxlZCBhbmQgdHJhbnNmb3JtZWQgZm9yIHRoZSBwcm92aWRlZCBgem9vbWAuXHJcblx0Z2V0UHJvamVjdGVkQm91bmRzOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0aWYgKHRoaXMuaW5maW5pdGUpIHsgcmV0dXJuIG51bGw7IH1cclxuXHJcblx0XHR2YXIgYiA9IHRoaXMucHJvamVjdGlvbi5ib3VuZHMsXHJcblx0XHQgICAgcyA9IHRoaXMuc2NhbGUoem9vbSksXHJcblx0XHQgICAgbWluID0gdGhpcy50cmFuc2Zvcm1hdGlvbi50cmFuc2Zvcm0oYi5taW4sIHMpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMudHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKGIubWF4LCBzKTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEJvdW5kcyhtaW4sIG1heCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZShsYXRsbmcxOiBMYXRMbmcsIGxhdGxuZzI6IExhdExuZyk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdHdvIGdlb2dyYXBoaWNhbCBjb29yZGluYX
14775Rlcy5cclxuXHJcblx0Ly8gQHByb3BlcnR5IGNvZGU6IFN0cmluZ1xyXG5cdC8vIFN0YW5kYXJkIGNvZGUgbmFtZSBvZiB0aGUgQ1JTIHBhc3NlZCBpbnRvIFdNUyBzZXJ2aWNlcyAoZS5nLiBgJ0VQU0c6Mzg1NydgKVxyXG5cdC8vXHJcblx0Ly8gQHByb3BlcnR5IHdyYXBMbmc6IE51bWJlcltdXHJcblx0Ly8gQW4gYXJyYXkgb2YgdHdvIG51bWJlcnMgZGVmaW5pbmcgd2hldGhlciB0aGUgbG9uZ2l0dWRlIChob3Jpem9udGFsKSBjb29yZGluYXRlXHJcblx0Ly8gYXhpcyB3cmFwcyBhcm91bmQgYSBnaXZlbiByYW5nZSBhbmQgaG93LiBEZWZhdWx0cyB0byBgWy0xODAsIDE4MF1gIGluIG1vc3RcclxuXHQvLyBnZW9ncmFwaGljYWwgQ1JTcy4gSWYgYHVuZGVmaW5lZGAsIHRoZSBsb25naXR1ZGUgYXhpcyBkb2VzIG5vdCB3cmFwIGFyb3VuZC5cclxuXHQvL1xyXG5cdC8vIEBwcm9wZXJ0eSB3cmFwTGF0OiBOdW1iZXJbXVxyXG5cdC8vIExpa2UgYHdyYXBMbmdgLCBidXQgZm9yIHRoZSBsYXRpdHVkZSAodmVydGljYWwpIGF4aXMuXHJcblxyXG5cdC8vIHdyYXBMbmc6IFttaW4sIG1heF0sXHJcblx0Ly8gd3JhcExhdDogW21pbiwgbWF4XSxcclxuXHJcblx0Ly8gQHByb3BlcnR5IGluZmluaXRlOiBCb29sZWFuXHJcblx0Ly8gSWYgdHJ1ZSwgdGhlIGNvb3JkaW5hdGUgc3BhY2Ugd2lsbCBiZSB1bmJvdW5kZWQgKGluZmluaXRlIGluIGJvdGggYXhlcylcclxuXHRpbmZpbml0ZTogZmFsc2UsXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZyhsYXRsbmc6IExhdExuZyk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nYCB3aGVyZSBsYXQgYW5kIGxuZyBoYXMgYmVlbiB3cmFwcGVkIGFjY29yZGluZyB0byB0aGVcclxuXHQvLyBDUlMncyBgd3JhcExhdGAgYW5kIGB3cmFwTG5nYCBwcm9wZXJ0aWVzLCBpZiB0aGV5IGFyZSBvdXRzaWRlIHRoZSBDUlMncyBib3VuZHMuXHJcblx0d3JhcExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGxuZyA9IHRoaXMud3JhcExuZyA/IHdyYXBOdW0obGF0bG5nLmxuZywgdGhpcy53cmFwTG5nLCB0cnVlKSA6IGxhdGxuZy5sbmcsXHJcblx0XHQgICAgbGF0ID0gdGhpcy53cmFwTGF0ID8gd3JhcE51bShsYXRsbmcubGF0LCB0aGlzLndyYXBMYXQsIHRydWUpIDogbGF0bG5nLmxhdCxcclxuXHRcdCAgICBhbHQgPSBsYXRsbmcuYWx0O1xyXG5cclxuXHRcdHJldHVybiBuZXcgTGF0TG5nKGxhdCwgbG5nLCBhbHQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZ0JvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcyk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nQm91bmRzYCB3aXRoIHRoZSBzYW1lIHNpemUgYXMgdGhlIGdpdmVuIG9uZSwgZW5zdXJpbmdcclxuXHQvLyB0aGF0IGl0cyBjZW50ZXIgaXMgd2l0aGluIHRoZSBDUlMncyBib3VuZHMuXHJcblx0Ly8gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5MYXRMbmdCb3VuZHNgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR3cmFwTGF0TG5nQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHR2YXIgY2VudGVyID0gYm91bmRzLmdldENlbnRlcigpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMud3JhcExhdExuZyhjZW50ZXIpLFxyXG5cdFx0ICAgIGxhdFNoaWZ0ID0gY2VudGVyLmxhdCAtIG5ld0NlbnRlci5sYXQsXHJcblx0XHQgICAgbG5nU2hpZnQgPSBjZW50ZXIubG5nIC0gbmV3Q2VudGVyLmxuZztcclxuXHJcblx0XHRpZiAobGF0U2hpZnQgPT09IDAgJiYgbG5nU2hpZnQgPT09IDApIHtcclxuXHRcdFx0cmV0dXJuIGJvdW5kcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3cgPSBib3VuZHMuZ2V0U291dGhXZXN0KCksXHJcblx0XHQgICAgbmUgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblx0XHQgICAgbmV3U3cgPSBuZXcgTGF0TG5nKHN3LmxhdCAtIGxhdFNoaWZ0LCBzdy5sbmcgLSBsbmdTaGlmdCksXHJcblx0XHQgICAgbmV3TmUgPSBuZXcgTGF0TG5nKG5lLmxhdCAtIGxhdFNoaWZ0LCBuZS5sbmcgLSBsbmdTaGlmdCk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMYXRMbmdCb3VuZHMobmV3U3csIG5ld05lKTtcclxuXHR9XHJcbn07XG5cbi8qXG4gKiBAbmFtZXNwYWNlIENSU1xuICogQGNycyBMLkNSUy5FYXJ0aFxuICpcbiAqIFNlcnZlcyBhcyB0aGUgYmFzZSBmb3IgQ1JTIHRoYXQgYXJlIGdsb2JhbCBzdWNoIHRoYXQgdGhleSBjb3ZlciB0aGUgZWFydGguXG4gKiBDYW4gb25seSBiZSB1c2VkIGFzIHRoZSBiYXNlIGZvciBvdGhlciBDUlMgYW5kIGNhbm5vdCBiZSB1c2VkIGRpcmVjdGx5LFxuICogc2luY2UgaXQgZG9lcyBub3QgaGF2ZSBhIGBjb2RlYCwgYHByb2plY3Rpb25gIG9yIGB0cmFuc2Zvcm1hdGlvbmAuIGBkaXN0YW5jZSgpYCByZXR1cm5zXG4gKiBtZXRlcnMuXG4gKi9cblxudmFyIEVhcnRoID0gZXh0ZW5kKHt9LCBDUlMsIHtcblx0d3JhcExuZzogWy0xODAsIDE4MF0sXG5cblx0Ly8gTWVhbiBFYXJ0aCBSYWRpdXMsIGFzIHJlY29tbWVuZGVkIGZvciB1c2UgYnlcblx0Ly8gdGhlIEludGVybmF0aW9uYWwgVW5pb24gb2YgR2VvZGVzeSBhbmQgR2VvcGh5c2ljcyxcblx0Ly8gc2VlIGh0dHA6Ly9yb3NldHRhY29kZS5vcmcvd2lraS9IYXZlcnNpbmVfZm9ybXVsYVxuXHRSOiA2MzcxMDAwLFxuXG5cdC8vIGRpc3RhbmNlIGJldHdlZW4gdHdvIGdlb2dyYXBoaWNhbCBwb2ludHMgdXNpbmcgc3BoZXJpY2FsIGxhdyBvZiBjb3NpbmVzIGFwcHJveGltYXRpb25cblx0ZGlzdGFuY2U6IGZ1bmN0aW9uIChsYXRsbmcxLCBsYXRsbmcyKSB7XG5cdFx0dmFyIHJhZCA9IE1hdGguUEkgLyAxODAsXG5cdFx0ICAgIGxhdDEgPSBsYXRsbmcxLmxhdCAqIHJhZCxcblx0XHQgICAgbGF0MiA9IGxhdGxuZzIubGF0ICogcmFkLFxuXHRcdCAgICBzaW5ETGF0ID0gTWF0aC5zaW4oKGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQpICogcmFkIC8gMiksXG5cdFx0ICAgIHNpbkRMb24gPSBNYXRoLnNpbigobGF0bG5nMi5sbmcgLSBsYXRsbmcxLmxuZykgKiByYWQgLyAyKSxcblx0XHQgICAgYSA9IHNpbkRMYXQgKiBzaW5ETGF0ICsgTWF0aC5jb3MobGF0MSkgKiBNYXRoLmNvcyhsYXQyKSAqIHNpbkRMb24gKiBzaW5ETG9uLFxuXHRcdCAgICBjID0gMiAqIE1hdGguYXRhbjIoTWF0aC5zcXJ0KGEpLCBNYXRoLnNxcnQ
14775oMSAtIGEpKTtcblx0XHRyZXR1cm4gdGhpcy5SICogYztcblx0fVxufSk7XG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgUHJvamVjdGlvblxyXG4gKiBAcHJvamVjdGlvbiBMLlByb2plY3Rpb24uU3BoZXJpY2FsTWVyY2F0b3JcclxuICpcclxuICogU3BoZXJpY2FsIE1lcmNhdG9yIHByb2plY3Rpb24g4oCUIHRoZSBtb3N0IGNvbW1vbiBwcm9qZWN0aW9uIGZvciBvbmxpbmUgbWFwcyxcclxuICogdXNlZCBieSBhbG1vc3QgYWxsIGZyZWUgYW5kIGNvbW1lcmNpYWwgdGlsZSBwcm92aWRlcnMuIEFzc3VtZXMgdGhhdCBFYXJ0aCBpc1xyXG4gKiBhIHNwaGVyZS4gVXNlZCBieSB0aGUgYEVQU0c6Mzg1N2AgQ1JTLlxyXG4gKi9cclxuXHJcbnZhciBlYXJ0aFJhZGl1cyA9IDYzNzgxMzc7XHJcblxyXG52YXIgU3BoZXJpY2FsTWVyY2F0b3IgPSB7XHJcblxyXG5cdFI6IGVhcnRoUmFkaXVzLFxyXG5cdE1BWF9MQVRJVFVERTogODUuMDUxMTI4Nzc5OCxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuTUFYX0xBVElUVURFLFxyXG5cdFx0ICAgIGxhdCA9IE1hdGgubWF4KE1hdGgubWluKG1heCwgbGF0bG5nLmxhdCksIC1tYXgpLFxyXG5cdFx0ICAgIHNpbiA9IE1hdGguc2luKGxhdCAqIGQpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgUG9pbnQoXHJcblx0XHRcdHRoaXMuUiAqIGxhdGxuZy5sbmcgKiBkLFxyXG5cdFx0XHR0aGlzLlIgKiBNYXRoLmxvZygoMSArIHNpbikgLyAoMSAtIHNpbikpIC8gMik7XHJcblx0fSxcclxuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBkID0gMTgwIC8gTWF0aC5QSTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IExhdExuZyhcclxuXHRcdFx0KDIgKiBNYXRoLmF0YW4oTWF0aC5leHAocG9pbnQueSAvIHRoaXMuUikpIC0gKE1hdGguUEkgLyAyKSkgKiBkLFxyXG5cdFx0XHRwb2ludC54ICogZCAvIHRoaXMuUik7XHJcblx0fSxcclxuXHJcblx0Ym91bmRzOiAoZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGQgPSBlYXJ0aFJhZGl1cyAqIE1hdGguUEk7XHJcblx0XHRyZXR1cm4gbmV3IEJvdW5kcyhbLWQsIC1kXSwgW2QsIGRdKTtcclxuXHR9KSgpXHJcbn07XG5cbi8qXHJcbiAqIEBjbGFzcyBUcmFuc2Zvcm1hdGlvblxyXG4gKiBAYWthIEwuVHJhbnNmb3JtYXRpb25cclxuICpcclxuICogUmVwcmVzZW50cyBhbiBhZmZpbmUgdHJhbnNmb3JtYXRpb246IGEgc2V0IG9mIGNvZWZmaWNpZW50cyBgYWAsIGBiYCwgYGNgLCBgZGBcclxuICogZm9yIHRyYW5zZm9ybWluZyBhIHBvaW50IG9mIGEgZm9ybSBgKHgsIHkpYCBpbnRvIGAoYSp4ICsgYiwgYyp5ICsgZClgIGFuZCBkb2luZ1xyXG4gKiB0aGUgcmV2ZXJzZS4gVXNlZCBieSBMZWFmbGV0IGluIGl0cyBwcm9qZWN0aW9ucyBjb2RlLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgdHJhbnNmb3JtYXRpb24gPSBMLnRyYW5zZm9ybWF0aW9uKDIsIDUsIC0xLCAxMCksXHJcbiAqIFx0cCA9IEwucG9pbnQoMSwgMiksXHJcbiAqIFx0cDIgPSB0cmFuc2Zvcm1hdGlvbi50cmFuc2Zvcm0ocCksIC8vICBMLnBvaW50KDcsIDgpXHJcbiAqIFx0cDMgPSB0cmFuc2Zvcm1hdGlvbi51bnRyYW5zZm9ybShwMik7IC8vICBMLnBvaW50KDEsIDIpXHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG4vLyBmYWN0b3J5IG5ldyBMLlRyYW5zZm9ybWF0aW9uKGE6IE51bWJlciwgYjogTnVtYmVyLCBjOiBOdW1iZXIsIGQ6IE51bWJlcilcclxuLy8gQ3JlYXRlcyBhIGBUcmFuc2Zvcm1hdGlvbmAgb2JqZWN0IHdpdGggdGhlIGdpdmVuIGNvZWZmaWNpZW50cy5cclxuZnVuY3Rpb24gVHJhbnNmb3JtYXRpb24oYSwgYiwgYywgZCkge1xyXG5cdGlmIChpc0FycmF5KGEpKSB7XHJcblx0XHQvLyB1c2UgYXJyYXkgcHJvcGVydGllc1xyXG5cdFx0dGhpcy5fYSA9IGFbMF07XHJcblx0XHR0aGlzLl9iID0gYVsxXTtcclxuXHRcdHRoaXMuX2MgPSBhWzJdO1xyXG5cdFx0dGhpcy5fZCA9IGFbM107XHJcblx0XHRyZXR1cm47XHJcblx0fVxyXG5cdHRoaXMuX2EgPSBhO1xyXG5cdHRoaXMuX2IgPSBiO1xyXG5cdHRoaXMuX2MgPSBjO1xyXG5cdHRoaXMuX2QgPSBkO1xyXG59XHJcblxyXG5UcmFuc2Zvcm1hdGlvbi5wcm90b3R5cGUgPSB7XHJcblx0Ly8gQG1ldGhvZCB0cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIHRyYW5zZm9ybWVkIHBvaW50LCBvcHRpb25hbGx5IG11bHRpcGxpZWQgYnkgdGhlIGdpdmVuIHNjYWxlLlxyXG5cdC8vIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuUG9pbnRgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR0cmFuc2Zvcm06IGZ1bmN0aW9uIChwb2ludCwgc2NhbGUpIHsgLy8gKFBvaW50LCBOdW1iZXIpIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gdGhpcy5fdHJhbnNmb3JtKHBvaW50LmNsb25lKCksIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBkZXN0cnVjdGl2ZSB0cmFuc2Zvcm0gKGZhc3RlcilcclxuXHRfdHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRwb2ludC54ID0gc2NhbGUgKiAodGhpcy5fYSAqIHBvaW50LnggKyB0aGlzLl9iKTtcclxuXHRcdHBvaW50LnkgPSBzY2FsZSAqICh0aGlzLl9jICogcG9pbnQueSArIHRoaXMuX2QpO1xyXG5cdFx0cmV0dXJuIHBvaW50O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW50cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmV2ZXJzZSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZ2l2ZW4gcG9pbnQsIG9wdGlvbmFsbHkgZGl2aWRlZFxyXG5cdC8vIGJ5IHRoZSBnaXZlbiBzY2FsZS4gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5Qb2ludGAgaW5zdGFuY2VzLCBub3QgYXJyYXlzLlxyXG5cdHVudHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRyZXR1cm4gbmV3IFBvaW50KFxyXG5cdFx0ICAgICAgICAocG9pbnQueCAvIHNjYWxlIC0gdGhpcy5fYikgLyB0aGlzLl9hLFxyXG5cdFx0ICAgICAgICAocG9pbnQueSAvIHNjYWxlIC0gdGhpcy5fZCkgLyB0aGlzLl9jKTtcclxuXHR9XHJcbn07XHJcblxyXG4vLyBmYWN0b3J5IEwudHJhbnNmb3JtYXRpb24oYTogTnVtYmVyLCBiOiBOdW1iZXIsIGM6IE51bWJlciwgZDogTnVtYmVyKVxyXG5cclxuLy8gQGZhY3RvcnkgTC50cmFuc2Zvcm1hdGlvbihhOiBOdW1iZXIsIGI6IE51bWJlciwgYzogTnVtYmVyLCBkOiBOdW1iZXIpXHJcbi8vIEluc3RhbnRpYXRlcyBhIFRyYW5zZm9ybWF0aW9uIG9iamVjdCB3aXRoIHRoZSBnaXZlbiBjb2VmZmljaWVudHMuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC50cmFuc2Zvcm1hdGlvbihjb2VmZmljaWVudHM6IEFycmF
147755KTogVHJhbnNmb3JtYXRpb25cclxuLy8gRXhwZWN0cyBhbiBjb2VmZmljaWVudHMgYXJyYXkgb2YgdGhlIGZvcm1cclxuLy8gYFthOiBOdW1iZXIsIGI6IE51bWJlciwgYzogTnVtYmVyLCBkOiBOdW1iZXJdYC5cclxuXHJcbmZ1bmN0aW9uIHRvVHJhbnNmb3JtYXRpb24oYSwgYiwgYywgZCkge1xyXG5cdHJldHVybiBuZXcgVHJhbnNmb3JtYXRpb24oYSwgYiwgYywgZCk7XHJcbn1cblxuLypcclxuICogQG5hbWVzcGFjZSBDUlNcclxuICogQGNycyBMLkNSUy5FUFNHMzg1N1xyXG4gKlxyXG4gKiBUaGUgbW9zdCBjb21tb24gQ1JTIGZvciBvbmxpbmUgbWFwcywgdXNlZCBieSBhbG1vc3QgYWxsIGZyZWUgYW5kIGNvbW1lcmNpYWxcclxuICogdGlsZSBwcm92aWRlcnMuIFVzZXMgU3BoZXJpY2FsIE1lcmNhdG9yIHByb2plY3Rpb24uIFNldCBpbiBieSBkZWZhdWx0IGluXHJcbiAqIE1hcCdzIGBjcnNgIG9wdGlvbi5cclxuICovXHJcblxyXG52YXIgRVBTRzM4NTcgPSBleHRlbmQoe30sIEVhcnRoLCB7XHJcblx0Y29kZTogJ0VQU0c6Mzg1NycsXHJcblx0cHJvamVjdGlvbjogU3BoZXJpY2FsTWVyY2F0b3IsXHJcblxyXG5cdHRyYW5zZm9ybWF0aW9uOiAoZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHNjYWxlID0gMC41IC8gKE1hdGguUEkgKiBTcGhlcmljYWxNZXJjYXRvci5SKTtcclxuXHRcdHJldHVybiB0b1RyYW5zZm9ybWF0aW9uKHNjYWxlLCAwLjUsIC1zY2FsZSwgMC41KTtcclxuXHR9KCkpXHJcbn0pO1xyXG5cclxudmFyIEVQU0c5MDA5MTMgPSBleHRlbmQoe30sIEVQU0czODU3LCB7XHJcblx0Y29kZTogJ0VQU0c6OTAwOTEzJ1xyXG59KTtcblxuLy8gQG5hbWVzcGFjZSBTVkc7IEBzZWN0aW9uXG4vLyBUaGVyZSBhcmUgc2V2ZXJhbCBzdGF0aWMgZnVuY3Rpb25zIHdoaWNoIGNhbiBiZSBjYWxsZWQgd2l0aG91dCBpbnN0YW50aWF0aW5nIEwuU1ZHOlxuXG4vLyBAZnVuY3Rpb24gY3JlYXRlKG5hbWU6IFN0cmluZyk6IFNWR0VsZW1lbnRcbi8vIFJldHVybnMgYSBpbnN0YW5jZSBvZiBbU1ZHRWxlbWVudF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1NWR0VsZW1lbnQpLFxuLy8gY29ycmVzcG9uZGluZyB0byB0aGUgY2xhc3MgbmFtZSBwYXNzZWQuIEZvciBleGFtcGxlLCB1c2luZyAnbGluZScgd2lsbCByZXR1cm5cbi8vIGFuIGluc3RhbmNlIG9mIFtTVkdMaW5lRWxlbWVudF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1NWR0xpbmVFbGVtZW50KS5cbmZ1bmN0aW9uIHN2Z0NyZWF0ZShuYW1lKSB7XG5cdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJywgbmFtZSk7XG59XG5cbi8vIEBmdW5jdGlvbiBwb2ludHNUb1BhdGgocmluZ3M6IFBvaW50W10sIGNsb3NlZDogQm9vbGVhbik6IFN0cmluZ1xuLy8gR2VuZXJhdGVzIGEgU1ZHIHBhdGggc3RyaW5nIGZvciBtdWx0aXBsZSByaW5ncywgd2l0aCBlYWNoIHJpbmcgdHVybmluZ1xuLy8gaW50byBcIk0uLkwuLkwuLlwiIGluc3RydWN0aW9uc1xuZnVuY3Rpb24gcG9pbnRzVG9QYXRoKHJpbmdzLCBjbG9zZWQpIHtcblx0dmFyIHN0ciA9ICcnLFxuXHRpLCBqLCBsZW4sIGxlbjIsIHBvaW50cywgcDtcblxuXHRmb3IgKGkgPSAwLCBsZW4gPSByaW5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdHBvaW50cyA9IHJpbmdzW2ldO1xuXG5cdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBvaW50cy5sZW5ndGg7IGogPCBsZW4yOyBqKyspIHtcblx0XHRcdHAgPSBwb2ludHNbal07XG5cdFx0XHRzdHIgKz0gKGogPyAnTCcgOiAnTScpICsgcC54ICsgJyAnICsgcC55O1xuXHRcdH1cblxuXHRcdC8vIGNsb3NlcyB0aGUgcmluZyBmb3IgcG9seWdvbnM7IFwieFwiIGlzIFZNTCBzeW50YXhcblx0XHRzdHIgKz0gY2xvc2VkID8gKHN2ZyA/ICd6JyA6ICd4JykgOiAnJztcblx0fVxuXG5cdC8vIFNWRyBjb21wbGFpbnMgYWJvdXQgZW1wdHkgcGF0aCBzdHJpbmdzXG5cdHJldHVybiBzdHIgfHwgJ00wIDAnO1xufVxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIEJyb3dzZXJcclxuICogQGFrYSBMLkJyb3dzZXJcclxuICpcclxuICogQSBuYW1lc3BhY2Ugd2l0aCBzdGF0aWMgcHJvcGVydGllcyBmb3IgYnJvd3Nlci9mZWF0dXJlIGRldGVjdGlvbiB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogaWYgKEwuQnJvd3Nlci5pZWx0OSkge1xyXG4gKiAgIGFsZXJ0KCdVcGdyYWRlIHlvdXIgYnJvd3NlciwgZHVkZSEnKTtcclxuICogfVxyXG4gKiBgYGBcclxuICovXHJcblxyXG52YXIgc3R5bGUkMSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBpZTogQm9vbGVhbjsgYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgKG5vdCBFZGdlKS5cclxudmFyIGllID0gJ0FjdGl2ZVhPYmplY3QnIGluIHdpbmRvdztcclxuXHJcbi8vIEBwcm9wZXJ0eSBpZWx0OTogQm9vbGVhbjsgYHRydWVgIGZvciBJbnRlcm5ldCBFeHBsb3JlciB2ZXJzaW9ucyBsZXNzIHRoYW4gOS5cclxudmFyIGllbHQ5ID0gaWUgJiYgIWRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXI7XHJcblxyXG4vLyBAcHJvcGVydHkgZWRnZTogQm9vbGVhbjsgYHRydWVgIGZvciB0aGUgRWRnZSB3ZWIgYnJvd3Nlci5cclxudmFyIGVkZ2UgPSAnbXNMYXVuY2hVcmknIGluIG5hdmlnYXRvciAmJiAhKCdkb2N1bWVudE1vZGUnIGluIGRvY3VtZW50KTtcclxuXHJcbi8vIEBwcm9wZXJ0eSB3ZWJraXQ6IEJvb2xlYW47XHJcbi8vIGB0cnVlYCBmb3Igd2Via2l0LWJhc2VkIGJyb3dzZXJzIGxpa2UgQ2hyb21lIGFuZCBTYWZhcmkgKGluY2x1ZGluZyBtb2JpbGUgdmVyc2lvbnMpLlxyXG52YXIgd2Via2l0ID0gdXNlckFnZW50Q29udGFpbnMoJ3dlYmtpdCcpO1xyXG5cclxuLy8gQHByb3BlcnR5IGFuZHJvaWQ6IEJvb2xlYW5cclxuLy8gYHRydWVgIGZvciBhbnkgYnJvd3NlciBydW5uaW5nIG9uIGFuIEFuZHJvaWQgcGxhdGZvcm0uXHJcbnZhciBhbmRyb2lkID0gdXNlckFnZW50Q29udGFpbnMoJ2FuZHJvaWQnKTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBhbmRyb2lkMjM6IEJvb2xlYW47IGB0cnVlYCBmb3IgYnJvd3NlcnMgcnVubmluZyBvbiBBbmRyb2lkIDIgb3IgQW5kcm9pZCAzLlxyXG52YXIgYW5kcm9pZDIzID0gdXNlckFnZW50Q29udGFpbnMoJ2FuZHJvaWQgMicpIHx8IHVzZXJBZ2VudENvbnRhaW5zKCdhbmRyb2lkIDMnKTtcclxuXHJcbi8qIFNlZSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMTc5NjEyNjYgZm9yIGRldGFpbHMgb24gZGV0ZWN0aW5nIHN0b2NrIEFuZHJvaWQgKi9cclxudmFyIHdlYmtpdFZlciA9IHBhcnNlSW50KC9XZWJLaXRcXC8oWzAtOV0rKXwkLy5leGVjKG5hdmlnYXRvci51c2VyQWdlbnQpWzFdLCAxMCk7IC8vIGFsc28gbWF0Y2hlcyBBcHBsZVdlYktpdFxyXG4vLyBAcHJvcGVydHkgYW5kcm9pZFN0b2NrOiBCb29sZWFuOyBgdHJ1ZWAgZm9yIHRoZSBBbmRyb2lkIHN0b2NrIGJyb3dzZXIgKGkuZS4gbm90IENocm9tZSlcclxudmFyIGFuZHJvaWRTdG9jayA9IGFuZHJvaWQgJiYgdXNlckFnZW50Q29udGFpbnMoJ0dvb2ds
14775ZScpICYmIHdlYmtpdFZlciA8IDUzNyAmJiAhKCdBdWRpb05vZGUnIGluIHdpbmRvdyk7XHJcblxyXG4vLyBAcHJvcGVydHkgb3BlcmE6IEJvb2xlYW47IGB0cnVlYCBmb3IgdGhlIE9wZXJhIGJyb3dzZXJcclxudmFyIG9wZXJhID0gISF3aW5kb3cub3BlcmE7XHJcblxyXG4vLyBAcHJvcGVydHkgY2hyb21lOiBCb29sZWFuOyBgdHJ1ZWAgZm9yIHRoZSBDaHJvbWUgYnJvd3Nlci5cclxudmFyIGNocm9tZSA9IHVzZXJBZ2VudENvbnRhaW5zKCdjaHJvbWUnKTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBnZWNrbzogQm9vbGVhbjsgYHRydWVgIGZvciBnZWNrby1iYXNlZCBicm93c2VycyBsaWtlIEZpcmVmb3guXHJcbnZhciBnZWNrbyA9IHVzZXJBZ2VudENvbnRhaW5zKCdnZWNrbycpICYmICF3ZWJraXQgJiYgIW9wZXJhICYmICFpZTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBzYWZhcmk6IEJvb2xlYW47IGB0cnVlYCBmb3IgdGhlIFNhZmFyaSBicm93c2VyLlxyXG52YXIgc2FmYXJpID0gIWNocm9tZSAmJiB1c2VyQWdlbnRDb250YWlucygnc2FmYXJpJyk7XHJcblxyXG52YXIgcGhhbnRvbSA9IHVzZXJBZ2VudENvbnRhaW5zKCdwaGFudG9tJyk7XHJcblxyXG4vLyBAcHJvcGVydHkgb3BlcmExMjogQm9vbGVhblxyXG4vLyBgdHJ1ZWAgZm9yIHRoZSBPcGVyYSBicm93c2VyIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMgKHZlcnNpb24gMTIgb3IgbGF0ZXIpLlxyXG52YXIgb3BlcmExMiA9ICdPVHJhbnNpdGlvbicgaW4gc3R5bGUkMTtcclxuXHJcbi8vIEBwcm9wZXJ0eSB3aW46IEJvb2xlYW47IGB0cnVlYCB3aGVuIHRoZSBicm93c2VyIGlzIHJ1bm5pbmcgaW4gYSBXaW5kb3dzIHBsYXRmb3JtXHJcbnZhciB3aW4gPSBuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZignV2luJykgPT09IDA7XHJcblxyXG4vLyBAcHJvcGVydHkgaWUzZDogQm9vbGVhbjsgYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxudmFyIGllM2QgPSBpZSAmJiAoJ3RyYW5zaXRpb24nIGluIHN0eWxlJDEpO1xyXG5cclxuLy8gQHByb3BlcnR5IHdlYmtpdDNkOiBCb29sZWFuOyBgdHJ1ZWAgZm9yIHdlYmtpdC1iYXNlZCBicm93c2VycyBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG52YXIgd2Via2l0M2QgPSAoJ1dlYktpdENTU01hdHJpeCcgaW4gd2luZG93KSAmJiAoJ20xMScgaW4gbmV3IHdpbmRvdy5XZWJLaXRDU1NNYXRyaXgoKSkgJiYgIWFuZHJvaWQyMztcclxuXHJcbi8vIEBwcm9wZXJ0eSBnZWNrbzNkOiBCb29sZWFuOyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcbnZhciBnZWNrbzNkID0gJ01velBlcnNwZWN0aXZlJyBpbiBzdHlsZSQxO1xyXG5cclxuLy8gQHByb3BlcnR5IGFueTNkOiBCb29sZWFuXHJcbi8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcbnZhciBhbnkzZCA9ICF3aW5kb3cuTF9ESVNBQkxFXzNEICYmIChpZTNkIHx8IHdlYmtpdDNkIHx8IGdlY2tvM2QpICYmICFvcGVyYTEyICYmICFwaGFudG9tO1xyXG5cclxuLy8gQHByb3BlcnR5IG1vYmlsZTogQm9vbGVhbjsgYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgcnVubmluZyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcbnZhciBtb2JpbGUgPSB0eXBlb2Ygb3JpZW50YXRpb24gIT09ICd1bmRlZmluZWQnIHx8IHVzZXJBZ2VudENvbnRhaW5zKCdtb2JpbGUnKTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBtb2JpbGVXZWJraXQ6IEJvb2xlYW47IGB0cnVlYCBmb3IgYWxsIHdlYmtpdC1iYXNlZCBicm93c2VycyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcbnZhciBtb2JpbGVXZWJraXQgPSBtb2JpbGUgJiYgd2Via2l0O1xyXG5cclxuLy8gQHByb3BlcnR5IG1vYmlsZVdlYmtpdDNkOiBCb29sZWFuXHJcbi8vIGB0cnVlYCBmb3IgYWxsIHdlYmtpdC1iYXNlZCBicm93c2VycyBpbiBhIG1vYmlsZSBkZXZpY2Ugc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxudmFyIG1vYmlsZVdlYmtpdDNkID0gbW9iaWxlICYmIHdlYmtpdDNkO1xyXG5cclxuLy8gQHByb3BlcnR5IG1zUG9pbnRlcjogQm9vbGVhblxyXG4vLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIGltcGxlbWVudGluZyB0aGUgTWljcm9zb2Z0IHRvdWNoIGV2ZW50cyBtb2RlbCAobm90YWJseSBJRTEwKS5cclxudmFyIG1zUG9pbnRlciA9ICF3aW5kb3cuUG9pbnRlckV2ZW50ICYmIHdpbmRvdy5NU1BvaW50ZXJFdmVudDtcclxuXHJcbi8vIEBwcm9wZXJ0eSBwb2ludGVyOiBCb29sZWFuXHJcbi8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgW3BvaW50ZXIgZXZlbnRzXShodHRwczovL21zZG4ubWljcm9zb2Z0LmNvbS9lbi11cy9saWJyYXJ5L2RuNDMzMjQ0JTI4dj12cy44NSUyOS5hc3B4KS5cclxudmFyIHBvaW50ZXIgPSAhd2Via2l0ICYmICEhKHdpbmRvdy5Qb2ludGVyRXZlbnQgfHwgbXNQb2ludGVyKTtcclxuXHJcbi8vIEBwcm9wZXJ0eSB0b3VjaDogQm9vbGVhblxyXG4vLyBgdHJ1ZWAgZm9yIGFsbCBicm93c2VycyBzdXBwb3J0aW5nIFt0b3VjaCBldmVudHNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Ub3VjaF9ldmVudHMpLlxyXG4vLyBUaGlzIGRvZXMgbm90IG5lY2Vzc2FyaWx5IG1lYW4gdGhhdCB0aGUgYnJvd3NlciBpcyBydW5uaW5nIGluIGEgY29tcHV0ZXIgd2l0aFxyXG4vLyBhIHRvdWNoc2NyZWVuLCBpdCBvbmx5IG1lYW5zIHRoYXQgdGhlIGJyb3dzZXIgaXMgY2FwYWJsZSBvZiB1bmRlcnN0YW5kaW5nXHJcbi8vIHRvdWNoIGV2ZW50cy5cclxudmFyIHRvdWNoID0gIXdpbmRvdy5MX05PX1RPVUNIICYmIChwb2ludGVyIHx8ICdvbnRvdWNoc3RhcnQnIGluIHdpbmRvdyB8fFxyXG5cdFx0KHdpbmRvdy5Eb2N1bWVudFRvdWNoICYmIGRvY3VtZW50IGluc3RhbmNlb2Ygd2luZG93LkRvY3VtZW50VG91Y2gpKTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBtb2JpbGVPcGVyYTogQm9vbGVhbjsgYHRydWVgIGZvciB0aGUgT3BlcmEgYnJvd3NlciBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcbnZhciBtb2JpbGVPcGVyYSA9IG1vYmlsZSAmJiBvcGVyYTtcclxuXHJcbi8vIEBwcm9wZXJ0eSBtb2JpbGVHZWNrbzogQm9vbGVhblxyXG4vLyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHJ1bm5pbmcgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG52YXIgbW9iaWxlR2Vja28gPSBtb2JpbGUgJiYgZ2Vja287XHJcblxyXG4vLyBAcHJvcGVydHkgcmV0aW5hOiBCb29sZWFuXHJcbi8vIGB0cnVlYCBmb3IgYnJvd3NlcnMgb24gYSBoaWdoLXJlc29sdXRpb24gXCJyZXRpbmFcIiBzY3JlZW4gb3Igb24gYW55IHNjcmVlbiB3aGVuIGJyb3dzZXIncyBkaXNwbGF5IHpvb20gaXMgbW9yZSB0aGFuIDEwMCUuXHJcbnZhciByZXRpbmEgPSAod2luZG93LmRldmljZVBpeGVsUmF0aW8gfHwgKHdpbmRvdy5zY3JlZW4uZGV2aWNlWERQSSAvIHdpbmRvdy5zY3JlZW4ubG9naWNhbFhEUEkpKSA+IDE7XHJcblxyXG4vLyBAcHJvcGVydHkgcGFzc2l2ZUV2ZW50czogQm9vbGVhblxyXG4vLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIHRoYXQgc3VwcG9ydCBwYXNzaXZlIGV2ZW50cy5cclxudmFyIHBhc3NpdmVFdmVudHMgPSAoZnVuY3Rpb24gKCkge1xyXG5cdHZhciBzdXBwb3J0c1Bhc3NpdmVPcHRpb24gPSBmYWxzZTtcclxuXHR0cnkge1xyXG5cdFx0dmFyIG9wdHMgPSBPYmplY3QuZGVmaW5lUHJvcGVydHkoe30sICdwYXNzaXZlJywge1xyXG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0XHRzdXBwb3J0c1Bhc3NpdmVPcHRpb24gPSB0cnVlO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHRcdHdpbmRvdy5hZGR
14775FdmVudExpc3RlbmVyKCd0ZXN0UGFzc2l2ZUV2ZW50U3VwcG9ydCcsIGZhbHNlRm4sIG9wdHMpO1xyXG5cdFx0d2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Rlc3RQYXNzaXZlRXZlbnRTdXBwb3J0JywgZmFsc2VGbiwgb3B0cyk7XHJcblx0fSBjYXRjaCAoZSkge1xyXG5cdFx0Ly8gRXJyb3JzIGNhbiBzYWZlbHkgYmUgaWdub3JlZCBzaW5jZSB0aGlzIGlzIG9ubHkgYSBicm93c2VyIHN1cHBvcnQgdGVzdC5cclxuXHR9XHJcblx0cmV0dXJuIHN1cHBvcnRzUGFzc2l2ZU9wdGlvbjtcclxufSk7XHJcblxyXG4vLyBAcHJvcGVydHkgY2FudmFzOiBCb29sZWFuXHJcbi8vIGB0cnVlYCB3aGVuIHRoZSBicm93c2VyIHN1cHBvcnRzIFtgPGNhbnZhcz5gXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvQ2FudmFzX0FQSSkuXHJcbnZhciBjYW52YXMgPSAoZnVuY3Rpb24gKCkge1xyXG5cdHJldHVybiAhIWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpLmdldENvbnRleHQ7XHJcbn0oKSk7XHJcblxyXG4vLyBAcHJvcGVydHkgc3ZnOiBCb29sZWFuXHJcbi8vIGB0cnVlYCB3aGVuIHRoZSBicm93c2VyIHN1cHBvcnRzIFtTVkddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRykuXHJcbnZhciBzdmcgPSAhIShkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMgJiYgc3ZnQ3JlYXRlKCdzdmcnKS5jcmVhdGVTVkdSZWN0KTtcclxuXHJcbi8vIEBwcm9wZXJ0eSB2bWw6IEJvb2xlYW5cclxuLy8gYHRydWVgIGlmIHRoZSBicm93c2VyIHN1cHBvcnRzIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpLlxyXG52YXIgdm1sID0gIXN2ZyAmJiAoZnVuY3Rpb24gKCkge1xyXG5cdHRyeSB7XHJcblx0XHR2YXIgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XHJcblx0XHRkaXYuaW5uZXJIVE1MID0gJzx2OnNoYXBlIGFkaj1cIjFcIi8+JztcclxuXHJcblx0XHR2YXIgc2hhcGUgPSBkaXYuZmlyc3RDaGlsZDtcclxuXHRcdHNoYXBlLnN0eWxlLmJlaGF2aW9yID0gJ3VybCgjZGVmYXVsdCNWTUwpJztcclxuXHJcblx0XHRyZXR1cm4gc2hhcGUgJiYgKHR5cGVvZiBzaGFwZS5hZGogPT09ICdvYmplY3QnKTtcclxuXHJcblx0fSBjYXRjaCAoZSkge1xyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH1cclxufSgpKTtcclxuXHJcblxyXG5mdW5jdGlvbiB1c2VyQWdlbnRDb250YWlucyhzdHIpIHtcclxuXHRyZXR1cm4gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpLmluZGV4T2Yoc3RyKSA+PSAwO1xyXG59XHJcblxuXG52YXIgQnJvd3NlciA9IChPYmplY3QuZnJlZXplIHx8IE9iamVjdCkoe1xuXHRpZTogaWUsXG5cdGllbHQ5OiBpZWx0OSxcblx0ZWRnZTogZWRnZSxcblx0d2Via2l0OiB3ZWJraXQsXG5cdGFuZHJvaWQ6IGFuZHJvaWQsXG5cdGFuZHJvaWQyMzogYW5kcm9pZDIzLFxuXHRhbmRyb2lkU3RvY2s6IGFuZHJvaWRTdG9jayxcblx0b3BlcmE6IG9wZXJhLFxuXHRjaHJvbWU6IGNocm9tZSxcblx0Z2Vja286IGdlY2tvLFxuXHRzYWZhcmk6IHNhZmFyaSxcblx0cGhhbnRvbTogcGhhbnRvbSxcblx0b3BlcmExMjogb3BlcmExMixcblx0d2luOiB3aW4sXG5cdGllM2Q6IGllM2QsXG5cdHdlYmtpdDNkOiB3ZWJraXQzZCxcblx0Z2Vja28zZDogZ2Vja28zZCxcblx0YW55M2Q6IGFueTNkLFxuXHRtb2JpbGU6IG1vYmlsZSxcblx0bW9iaWxlV2Via2l0OiBtb2JpbGVXZWJraXQsXG5cdG1vYmlsZVdlYmtpdDNkOiBtb2JpbGVXZWJraXQzZCxcblx0bXNQb2ludGVyOiBtc1BvaW50ZXIsXG5cdHBvaW50ZXI6IHBvaW50ZXIsXG5cdHRvdWNoOiB0b3VjaCxcblx0bW9iaWxlT3BlcmE6IG1vYmlsZU9wZXJhLFxuXHRtb2JpbGVHZWNrbzogbW9iaWxlR2Vja28sXG5cdHJldGluYTogcmV0aW5hLFxuXHRwYXNzaXZlRXZlbnRzOiBwYXNzaXZlRXZlbnRzLFxuXHRjYW52YXM6IGNhbnZhcyxcblx0c3ZnOiBzdmcsXG5cdHZtbDogdm1sXG59KTtcblxuLypcbiAqIEV4dGVuZHMgTC5Eb21FdmVudCB0byBwcm92aWRlIHRvdWNoIHN1cHBvcnQgZm9yIEludGVybmV0IEV4cGxvcmVyIGFuZCBXaW5kb3dzLWJhc2VkIGRldmljZXMuXG4gKi9cblxuXG52YXIgUE9JTlRFUl9ET1dOID0gICBtc1BvaW50ZXIgPyAnTVNQb2ludGVyRG93bicgICA6ICdwb2ludGVyZG93bic7XG52YXIgUE9JTlRFUl9NT1ZFID0gICBtc1BvaW50ZXIgPyAnTVNQb2ludGVyTW92ZScgICA6ICdwb2ludGVybW92ZSc7XG52YXIgUE9JTlRFUl9VUCA9ICAgICBtc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnICAgICA6ICdwb2ludGVydXAnO1xudmFyIFBPSU5URVJfQ0FOQ0VMID0gbXNQb2ludGVyID8gJ01TUG9pbnRlckNhbmNlbCcgOiAncG9pbnRlcmNhbmNlbCc7XG52YXIgVEFHX1dISVRFX0xJU1QgPSBbJ0lOUFVUJywgJ1NFTEVDVCcsICdPUFRJT04nXTtcblxudmFyIF9wb2ludGVycyA9IHt9O1xudmFyIF9wb2ludGVyRG9jTGlzdGVuZXIgPSBmYWxzZTtcblxuLy8gRG9tRXZlbnQuRG91YmxlVGFwIG5lZWRzIHRvIGtub3cgYWJvdXQgdGhpc1xudmFyIF9wb2ludGVyc0NvdW50ID0gMDtcblxuLy8gUHJvdmlkZXMgYSB0b3VjaCBldmVudHMgd3JhcHBlciBmb3IgKG1zKXBvaW50ZXIgZXZlbnRzLlxuLy8gcmVmIGh0dHA6Ly93d3cudzMub3JnL1RSL3BvaW50ZXJldmVudHMvIGh0dHBzOi8vd3d3LnczLm9yZy9CdWdzL1B1YmxpYy9zaG93X2J1Zy5jZ2k/aWQ9MjI4OTBcblxuZnVuY3Rpb24gYWRkUG9pbnRlckxpc3RlbmVyKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpIHtcblx0aWYgKHR5cGUgPT09ICd0b3VjaHN0YXJ0Jykge1xuXHRcdF9hZGRQb2ludGVyU3RhcnQob2JqLCBoYW5kbGVyLCBpZCk7XG5cblx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2htb3ZlJykge1xuXHRcdF9hZGRQb2ludGVyTW92ZShvYmosIGhhbmRsZXIsIGlkKTtcblxuXHR9IGVsc2UgaWYgKHR5cGUgPT09ICd0b3VjaGVuZCcpIHtcblx0XHRfYWRkUG9pbnRlckVuZChvYmosIGhhbmRsZXIsIGlkKTtcblx0fVxuXG5cdHJldHVybiB0aGlzO1xufVxuXG5mdW5jdGlvbiByZW1vdmVQb2ludGVyTGlzdGVuZXIob2JqLCB0eXBlLCBpZCkge1xuXHR2YXIgaGFuZGxlciA9IG9ialsnX2xlYWZsZXRfJyArIHR5cGUgKyBpZF07XG5cblx0aWYgKHR5cGUgPT09ICd0b3VjaHN0YXJ0Jykge1xuXHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKFBPSU5URVJfRE9XTiwgaGFuZGxlciwgZmFsc2UpO1xuXG5cdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHtcblx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcihQT0lOVEVSX01PVkUsIGhhbmRsZXIsIGZhbHNlKTtcblxuXHR9IGVsc2UgaWYgKHR5cGUgPT09ICd0b3VjaGVuZCcpIHtcblx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcihQT0lOVEVSX1VQLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoUE9JTlRFUl9DQU5DRUwsIGhhbmRsZXIsIGZhbHNlKTtcblx0fVxuXG5cdHJldHVybi
14775B0aGlzO1xufVxuXG5mdW5jdGlvbiBfYWRkUG9pbnRlclN0YXJ0KG9iaiwgaGFuZGxlciwgaWQpIHtcblx0dmFyIG9uRG93biA9IGJpbmQoZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoZS5wb2ludGVyVHlwZSAhPT0gJ21vdXNlJyAmJiBlLk1TUE9JTlRFUl9UWVBFX01PVVNFICYmIGUucG9pbnRlclR5cGUgIT09IGUuTVNQT0lOVEVSX1RZUEVfTU9VU0UpIHtcblx0XHRcdC8vIEluIElFMTEsIHNvbWUgdG91Y2ggZXZlbnRzIG5lZWRzIHRvIGZpcmUgZm9yIGZvcm0gY29udHJvbHMsIG9yXG5cdFx0XHQvLyB0aGUgY29udHJvbHMgd2lsbCBzdG9wIHdvcmtpbmcuIFdlIGtlZXAgYSB3aGl0ZWxpc3Qgb2YgdGFnIG5hbWVzIHRoYXRcblx0XHRcdC8vIG5lZWQgdGhlc2UgZXZlbnRzLiBGb3Igb3RoZXIgdGFyZ2V0IHRhZ3MsIHdlIHByZXZlbnQgZGVmYXVsdCBvbiB0aGUgZXZlbnQuXG5cdFx0XHRpZiAoVEFHX1dISVRFX0xJU1QuaW5kZXhPZihlLnRhcmdldC50YWdOYW1lKSA8IDApIHtcblx0XHRcdFx0cHJldmVudERlZmF1bHQoZSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0X2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdH0pO1xuXG5cdG9ialsnX2xlYWZsZXRfdG91Y2hzdGFydCcgKyBpZF0gPSBvbkRvd247XG5cdG9iai5hZGRFdmVudExpc3RlbmVyKFBPSU5URVJfRE9XTiwgb25Eb3duLCBmYWxzZSk7XG5cblx0Ly8gbmVlZCB0byBrZWVwIHRyYWNrIG9mIHdoYXQgcG9pbnRlcnMgYW5kIGhvdyBtYW55IGFyZSBhY3RpdmUgdG8gcHJvdmlkZSBlLnRvdWNoZXMgZW11bGF0aW9uXG5cdGlmICghX3BvaW50ZXJEb2NMaXN0ZW5lcikge1xuXHRcdC8vIHdlIGxpc3RlbiBkb2N1bWVudEVsZW1lbnQgYXMgYW55IGRyYWdzIHRoYXQgZW5kIGJ5IG1vdmluZyB0aGUgdG91Y2ggb2ZmIHRoZSBzY3JlZW4gZ2V0IGZpcmVkIHRoZXJlXG5cdFx0ZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoUE9JTlRFUl9ET1dOLCBfZ2xvYmFsUG9pbnRlckRvd24sIHRydWUpO1xuXHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFBPSU5URVJfTU9WRSwgX2dsb2JhbFBvaW50ZXJNb3ZlLCB0cnVlKTtcblx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihQT0lOVEVSX1VQLCBfZ2xvYmFsUG9pbnRlclVwLCB0cnVlKTtcblx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihQT0lOVEVSX0NBTkNFTCwgX2dsb2JhbFBvaW50ZXJVcCwgdHJ1ZSk7XG5cblx0XHRfcG9pbnRlckRvY0xpc3RlbmVyID0gdHJ1ZTtcblx0fVxufVxuXG5mdW5jdGlvbiBfZ2xvYmFsUG9pbnRlckRvd24oZSkge1xuXHRfcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0X3BvaW50ZXJzQ291bnQrKztcbn1cblxuZnVuY3Rpb24gX2dsb2JhbFBvaW50ZXJNb3ZlKGUpIHtcblx0aWYgKF9wb2ludGVyc1tlLnBvaW50ZXJJZF0pIHtcblx0XHRfcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0fVxufVxuXG5mdW5jdGlvbiBfZ2xvYmFsUG9pbnRlclVwKGUpIHtcblx0ZGVsZXRlIF9wb2ludGVyc1tlLnBvaW50ZXJJZF07XG5cdF9wb2ludGVyc0NvdW50LS07XG59XG5cbmZ1bmN0aW9uIF9oYW5kbGVQb2ludGVyKGUsIGhhbmRsZXIpIHtcblx0ZS50b3VjaGVzID0gW107XG5cdGZvciAodmFyIGkgaW4gX3BvaW50ZXJzKSB7XG5cdFx0ZS50b3VjaGVzLnB1c2goX3BvaW50ZXJzW2ldKTtcblx0fVxuXHRlLmNoYW5nZWRUb3VjaGVzID0gW2VdO1xuXG5cdGhhbmRsZXIoZSk7XG59XG5cbmZ1bmN0aW9uIF9hZGRQb2ludGVyTW92ZShvYmosIGhhbmRsZXIsIGlkKSB7XG5cdHZhciBvbk1vdmUgPSBmdW5jdGlvbiAoZSkge1xuXHRcdC8vIGRvbid0IGZpcmUgdG91Y2ggbW92ZXMgd2hlbiBtb3VzZSBpc24ndCBkb3duXG5cdFx0aWYgKChlLnBvaW50ZXJUeXBlID09PSBlLk1TUE9JTlRFUl9UWVBFX01PVVNFIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpICYmIGUuYnV0dG9ucyA9PT0gMCkgeyByZXR1cm47IH1cblxuXHRcdF9oYW5kbGVQb2ludGVyKGUsIGhhbmRsZXIpO1xuXHR9O1xuXG5cdG9ialsnX2xlYWZsZXRfdG91Y2htb3ZlJyArIGlkXSA9IG9uTW92ZTtcblx0b2JqLmFkZEV2ZW50TGlzdGVuZXIoUE9JTlRFUl9NT1ZFLCBvbk1vdmUsIGZhbHNlKTtcbn1cblxuZnVuY3Rpb24gX2FkZFBvaW50ZXJFbmQob2JqLCBoYW5kbGVyLCBpZCkge1xuXHR2YXIgb25VcCA9IGZ1bmN0aW9uIChlKSB7XG5cdFx0X2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdH07XG5cblx0b2JqWydfbGVhZmxldF90b3VjaGVuZCcgKyBpZF0gPSBvblVwO1xuXHRvYmouYWRkRXZlbnRMaXN0ZW5lcihQT0lOVEVSX1VQLCBvblVwLCBmYWxzZSk7XG5cdG9iai5hZGRFdmVudExpc3RlbmVyKFBPSU5URVJfQ0FOQ0VMLCBvblVwLCBmYWxzZSk7XG59XG5cbi8qXHJcbiAqIEV4dGVuZHMgdGhlIGV2ZW50IGhhbmRsaW5nIGNvZGUgd2l0aCBkb3VibGUgdGFwIHN1cHBvcnQgZm9yIG1vYmlsZSBicm93c2Vycy5cclxuICovXHJcblxyXG52YXIgX3RvdWNoc3RhcnQgPSBtc1BvaW50ZXIgPyAnTVNQb2ludGVyRG93bicgOiBwb2ludGVyID8gJ3BvaW50ZXJkb3duJyA6ICd0b3VjaHN0YXJ0JztcclxudmFyIF90b3VjaGVuZCA9IG1zUG9pbnRlciA/ICdNU1BvaW50ZXJVcCcgOiBwb2ludGVyID8gJ3BvaW50ZXJ1cCcgOiAndG91Y2hlbmQnO1xyXG52YXIgX3ByZSA9ICdfbGVhZmxldF8nO1xyXG5cclxuLy8gaW5zcGlyZWQgYnkgWmVwdG8gdG91Y2ggY29kZSBieSBUaG9tYXMgRnVjaHNcclxuZnVuY3Rpb24gYWRkRG91YmxlVGFwTGlzdGVuZXIob2JqLCBoYW5kbGVyLCBpZCkge1xyXG5cdHZhciBsYXN0LCB0b3VjaCQkMSxcclxuXHQgICAgZG91YmxlVGFwID0gZmFsc2UsXHJcblx0ICAgIGRlbGF5ID0gMjUwO1xyXG5cclxuXHRmdW5jdGlvbiBvblRvdWNoU3RhcnQoZSkge1xyXG5cdFx0dmFyIGNvdW50O1xyXG5cclxuXHRcdGlmIChwb2ludGVyKSB7XHJcblx0XHRcdGlmICgoIWVkZ2UpIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpIHsgcmV0dXJuOyB9XHJcblx0XHRcdGNvdW50ID0gX3BvaW50ZXJzQ291bnQ7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRjb3VudCA9IGUudG91Y2hlcy5sZW5ndGg7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvdW50ID4gMSkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgbm93ID0gRGF0ZS5ub3coKSxcclxuXHRcdCAgICBkZWx0YSA9IG5vdyAtIChsYXN0IHx8IG5vdyk7XHJcblxyXG5cdFx0dG91Y2gkJDEgPSBlLnRvdWNoZXMgPyBlLnRvdWNoZXNbMF0gOiBlO1xyXG5cdFx0ZG91YmxlVGFwID0gKGRlbHRhID4gMCAmJiBkZWx0YSA8PSBkZWxheSk7XHJcblx0XHRsYXN0ID0gbm93O1xyXG5cdH1cclxuXHJcblx0ZnVuY3Rpb24gb25Ub3VjaEVuZChlKSB7XHJcblx0XHRpZiAoZG91YmxlVGFwICYmICF0b3VjaCQkMS5jYW5jZWxCdWJibGUpIHtcclxuXHRcdFx0aWYgKHBvaW50ZXIpIHtcclxuXHRcdFx0XHRpZiAoKCFlZGdlKSB8fCBlLnBvaW50ZXJUeXBlID09PSAnbW91c2UnKSB7IHJldHVybjsgfVxyXG5cdFx0XHRcdC8vIHdvcmsgYXJvdW5kIC50eXBlIGJlaW5nIHJlYWRvbmx5IHdpdGggTVNQb2ludGVyKiBldmVudHNcclxuXHRcdFx0XHR2YXIgbmV3VG91Y2ggPSB7fSxcclxuXHRcdFx0XHQgICAgcHJvcCwgaTtcclxuXHJcblx0XHRcdFx0Zm9yIChpIGluIHRvdWNoJCQxKSB7X
14775HJcblx0XHRcdFx0XHRwcm9wID0gdG91Y2gkJDFbaV07XHJcblx0XHRcdFx0XHRuZXdUb3VjaFtpXSA9IHByb3AgJiYgcHJvcC5iaW5kID8gcHJvcC5iaW5kKHRvdWNoJCQxKSA6IHByb3A7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHRvdWNoJCQxID0gbmV3VG91Y2g7XHJcblx0XHRcdH1cclxuXHRcdFx0dG91Y2gkJDEudHlwZSA9ICdkYmxjbGljayc7XHJcblx0XHRcdHRvdWNoJCQxLmJ1dHRvbiA9IDA7XHJcblx0XHRcdGhhbmRsZXIodG91Y2gkJDEpO1xyXG5cdFx0XHRsYXN0ID0gbnVsbDtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdG9ialtfcHJlICsgX3RvdWNoc3RhcnQgKyBpZF0gPSBvblRvdWNoU3RhcnQ7XHJcblx0b2JqW19wcmUgKyBfdG91Y2hlbmQgKyBpZF0gPSBvblRvdWNoRW5kO1xyXG5cdG9ialtfcHJlICsgJ2RibGNsaWNrJyArIGlkXSA9IGhhbmRsZXI7XHJcblxyXG5cdG9iai5hZGRFdmVudExpc3RlbmVyKF90b3VjaHN0YXJ0LCBvblRvdWNoU3RhcnQsIHBhc3NpdmVFdmVudHMgPyB7cGFzc2l2ZTogZmFsc2V9IDogZmFsc2UpO1xyXG5cdG9iai5hZGRFdmVudExpc3RlbmVyKF90b3VjaGVuZCwgb25Ub3VjaEVuZCwgcGFzc2l2ZUV2ZW50cyA/IHtwYXNzaXZlOiBmYWxzZX0gOiBmYWxzZSk7XHJcblxyXG5cdC8vIE9uIHNvbWUgcGxhdGZvcm1zIChub3RhYmx5LCBjaHJvbWU8NTUgb24gd2luMTAgKyB0b3VjaHNjcmVlbiArIG1vdXNlKSxcclxuXHQvLyB0aGUgYnJvd3NlciBkb2Vzbid0IGZpcmUgdG91Y2hlbmQvcG9pbnRlcnVwIGV2ZW50cyBidXQgZG9lcyBmaXJlXHJcblx0Ly8gbmF0aXZlIGRibGNsaWNrcy4gU2VlICM0MTI3LlxyXG5cdC8vIEVkZ2UgMTQgYWxzbyBmaXJlcyBuYXRpdmUgZGJsY2xpY2tzLCBidXQgb25seSBmb3IgcG9pbnRlclR5cGUgbW91c2UsIHNlZSAjNTE4MC5cclxuXHRvYmouYWRkRXZlbnRMaXN0ZW5lcignZGJsY2xpY2snLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdHJldHVybiB0aGlzO1xyXG59XHJcblxyXG5mdW5jdGlvbiByZW1vdmVEb3VibGVUYXBMaXN0ZW5lcihvYmosIGlkKSB7XHJcblx0dmFyIHRvdWNoc3RhcnQgPSBvYmpbX3ByZSArIF90b3VjaHN0YXJ0ICsgaWRdLFxyXG5cdCAgICB0b3VjaGVuZCA9IG9ialtfcHJlICsgX3RvdWNoZW5kICsgaWRdLFxyXG5cdCAgICBkYmxjbGljayA9IG9ialtfcHJlICsgJ2RibGNsaWNrJyArIGlkXTtcclxuXHJcblx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoX3RvdWNoc3RhcnQsIHRvdWNoc3RhcnQsIHBhc3NpdmVFdmVudHMgPyB7cGFzc2l2ZTogZmFsc2V9IDogZmFsc2UpO1xyXG5cdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKF90b3VjaGVuZCwgdG91Y2hlbmQsIHBhc3NpdmVFdmVudHMgPyB7cGFzc2l2ZTogZmFsc2V9IDogZmFsc2UpO1xyXG5cdGlmICghZWRnZSkge1xyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZGJsY2xpY2ssIGZhbHNlKTtcclxuXHR9XHJcblxyXG5cdHJldHVybiB0aGlzO1xyXG59XG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgRG9tVXRpbFxyXG4gKlxyXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggdGhlIFtET01dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Eb2N1bWVudF9PYmplY3RfTW9kZWwpXHJcbiAqIHRyZWUsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKlxyXG4gKiBNb3N0IGZ1bmN0aW9ucyBleHBlY3Rpbmcgb3IgcmV0dXJuaW5nIGEgYEhUTUxFbGVtZW50YCBhbHNvIHdvcmsgZm9yXHJcbiAqIFNWRyBlbGVtZW50cy4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IGNsYXNzZXMgcmVmZXIgdG8gQ1NTIGNsYXNzZXNcclxuICogaW4gSFRNTCBhbmQgU1ZHIGNsYXNzZXMgaW4gU1ZHLlxyXG4gKi9cclxuXHJcblxyXG4vLyBAcHJvcGVydHkgVFJBTlNGT1JNOiBTdHJpbmdcclxuLy8gVmVuZG9yLXByZWZpeGVkIHRyYW5zZm9ybSBzdHlsZSBuYW1lIChlLmcuIGAnd2Via2l0VHJhbnNmb3JtJ2AgZm9yIFdlYktpdCkuXHJcbnZhciBUUkFOU0ZPUk0gPSB0ZXN0UHJvcChcclxuXHRbJ3RyYW5zZm9ybScsICd3ZWJraXRUcmFuc2Zvcm0nLCAnT1RyYW5zZm9ybScsICdNb3pUcmFuc2Zvcm0nLCAnbXNUcmFuc2Zvcm0nXSk7XHJcblxyXG4vLyB3ZWJraXRUcmFuc2l0aW9uIGNvbWVzIGZpcnN0IGJlY2F1c2Ugc29tZSBicm93c2VyIHZlcnNpb25zIHRoYXQgZHJvcCB2ZW5kb3IgcHJlZml4IGRvbid0IGRvXHJcbi8vIHRoZSBzYW1lIGZvciB0aGUgdHJhbnNpdGlvbmVuZCBldmVudCwgaW4gcGFydGljdWxhciB0aGUgQW5kcm9pZCA0LjEgc3RvY2sgYnJvd3NlclxyXG5cclxuLy8gQHByb3BlcnR5IFRSQU5TSVRJT046IFN0cmluZ1xyXG4vLyBWZW5kb3ItcHJlZml4ZWQgdHJhbnNpdGlvbiBzdHlsZSBuYW1lLlxyXG52YXIgVFJBTlNJVElPTiA9IHRlc3RQcm9wKFxyXG5cdFsnd2Via2l0VHJhbnNpdGlvbicsICd0cmFuc2l0aW9uJywgJ09UcmFuc2l0aW9uJywgJ01velRyYW5zaXRpb24nLCAnbXNUcmFuc2l0aW9uJ10pO1xyXG5cclxuLy8gQHByb3BlcnR5IFRSQU5TSVRJT05fRU5EOiBTdHJpbmdcclxuLy8gVmVuZG9yLXByZWZpeGVkIHRyYW5zaXRpb25lbmQgZXZlbnQgbmFtZS5cclxudmFyIFRSQU5TSVRJT05fRU5EID1cclxuXHRUUkFOU0lUSU9OID09PSAnd2Via2l0VHJhbnNpdGlvbicgfHwgVFJBTlNJVElPTiA9PT0gJ09UcmFuc2l0aW9uJyA/IFRSQU5TSVRJT04gKyAnRW5kJyA6ICd0cmFuc2l0aW9uZW5kJztcclxuXHJcblxyXG4vLyBAZnVuY3Rpb24gZ2V0KGlkOiBTdHJpbmd8SFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG4vLyBSZXR1cm5zIGFuIGVsZW1lbnQgZ2l2ZW4gaXRzIERPTSBpZCwgb3IgcmV0dXJucyB0aGUgZWxlbWVudCBpdHNlbGZcclxuLy8gaWYgaXQgd2FzIHBhc3NlZCBkaXJlY3RseS5cclxuZnVuY3Rpb24gZ2V0KGlkKSB7X
14775HJcblx0cmV0dXJuIHR5cGVvZiBpZCA9PT0gJ3N0cmluZycgPyBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCkgOiBpZDtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGdldFN0eWxlKGVsOiBIVE1MRWxlbWVudCwgc3R5bGVBdHRyaWI6IFN0cmluZyk6IFN0cmluZ1xyXG4vLyBSZXR1cm5zIHRoZSB2YWx1ZSBmb3IgYSBjZXJ0YWluIHN0eWxlIGF0dHJpYnV0ZSBvbiBhbiBlbGVtZW50LFxyXG4vLyBpbmNsdWRpbmcgY29tcHV0ZWQgdmFsdWVzIG9yIHZhbHVlcyBzZXQgdGhyb3VnaCBDU1MuXHJcbmZ1bmN0aW9uIGdldFN0eWxlKGVsLCBzdHlsZSkge1xyXG5cdHZhciB2YWx1ZSA9IGVsLnN0eWxlW3N0eWxlXSB8fCAoZWwuY3VycmVudFN0eWxlICYmIGVsLmN1cnJlbnRTdHlsZVtzdHlsZV0pO1xyXG5cclxuXHRpZiAoKCF2YWx1ZSB8fCB2YWx1ZSA9PT0gJ2F1dG8nKSAmJiBkb2N1bWVudC5kZWZhdWx0Vmlldykge1xyXG5cdFx0dmFyIGNzcyA9IGRvY3VtZW50LmRlZmF1bHRWaWV3LmdldENvbXB1dGVkU3R5bGUoZWwsIG51bGwpO1xyXG5cdFx0dmFsdWUgPSBjc3MgPyBjc3Nbc3R5bGVdIDogbnVsbDtcclxuXHR9XHJcblx0cmV0dXJuIHZhbHVlID09PSAnYXV0bycgPyBudWxsIDogdmFsdWU7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBjcmVhdGUodGFnTmFtZTogU3RyaW5nLCBjbGFzc05hbWU/OiBTdHJpbmcsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuLy8gQ3JlYXRlcyBhbiBIVE1MIGVsZW1lbnQgd2l0aCBgdGFnTmFtZWAsIHNldHMgaXRzIGNsYXNzIHRvIGBjbGFzc05hbWVgLCBhbmQgb3B0aW9uYWxseSBhcHBlbmRzIGl0IHRvIGBjb250YWluZXJgIGVsZW1lbnQuXHJcbmZ1bmN0aW9uIGNyZWF0ZSQxKHRhZ05hbWUsIGNsYXNzTmFtZSwgY29udGFpbmVyKSB7XHJcblx0dmFyIGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0YWdOYW1lKTtcclxuXHRlbC5jbGFzc05hbWUgPSBjbGFzc05hbWUgfHwgJyc7XHJcblxyXG5cdGlmIChjb250YWluZXIpIHtcclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChlbCk7XHJcblx0fVxyXG5cdHJldHVybiBlbDtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIHJlbW92ZShlbDogSFRNTEVsZW1lbnQpXHJcbi8vIFJlbW92ZXMgYGVsYCBmcm9tIGl0cyBwYXJlbnQgZWxlbWVudFxyXG5mdW5jdGlvbiByZW1vdmUoZWwpIHtcclxuXHR2YXIgcGFyZW50ID0gZWwucGFyZW50Tm9kZTtcclxuXHRpZiAocGFyZW50KSB7XHJcblx0XHRwYXJlbnQucmVtb3ZlQ2hpbGQoZWwpO1xyXG5cdH1cclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGVtcHR5KGVsOiBIVE1MRWxlbWVudClcclxuLy8gUmVtb3ZlcyBhbGwgb2YgYGVsYCdzIGNoaWxkcmVuIGVsZW1lbnRzIGZyb20gYGVsYFxyXG5mdW5jdGlvbiBlbXB0eShlbCkge1xyXG5cdHdoaWxlIChlbC5maXJzdENoaWxkKSB7XHJcblx0XHRlbC5yZW1vdmVDaGlsZChlbC5maXJzdENoaWxkKTtcclxuXHR9XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiB0b0Zyb250KGVsOiBIVE1MRWxlbWVudClcclxuLy8gTWFrZXMgYGVsYCB0aGUgbGFzdCBjaGlsZCBvZiBpdHMgcGFyZW50LCBzbyBpdCByZW5kZXJzIGluIGZyb250IG9mIHRoZSBvdGhlciBjaGlsZHJlbi5cclxuZnVuY3Rpb24gdG9Gcm9udChlbCkge1xyXG5cdHZhciBwYXJlbnQgPSBlbC5wYXJlbnROb2RlO1xyXG5cdGlmIChwYXJlbnQgJiYgcGFyZW50Lmxhc3RDaGlsZCAhPT0gZWwpIHtcclxuXHRcdHBhcmVudC5hcHBlbmRDaGlsZChlbCk7XHJcblx0fVxyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gdG9CYWNrKGVsOiBIVE1MRWxlbWVudClcclxuLy8gTWFrZXMgYGVsYCB0aGUgZmlyc3QgY2hpbGQgb2YgaXRzIHBhcmVudCwgc28gaXQgcmVuZGVycyBiZWhpbmQgdGhlIG90aGVyIGNoaWxkcmVuLlxyXG5mdW5jdGlvbiB0b0JhY2soZWwpIHtcclxuXHR2YXIgcGFyZW50ID0gZWwucGFyZW50Tm9kZTtcclxuXHRpZiAocGFyZW50ICYmIHBhcmVudC5maXJzdENoaWxkICE9PSBlbCkge1xyXG5cdFx0cGFyZW50Lmluc2VydEJlZm9yZShlbCwgcGFyZW50LmZpcnN0Q2hpbGQpO1xyXG5cdH1cclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGhhc0NsYXNzKGVsOiBIVE1MRWxlbWVudCwgbmFtZTogU3RyaW5nKTogQm9vbGVhblxyXG4vLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZSBjb250YWlucyBgbmFtZWAuXHJcbmZ1bmN0aW9uIGhhc0NsYXNzKGVsLCBuYW1lKSB7XHJcblx0aWYgKGVsLmNsYXNzTGlzdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRyZXR1cm4gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKG5hbWUpO1xyXG5cdH1cclxuXHR2YXIgY2xhc3NOYW1lID0gZ2V0Q2xhc3MoZWwpO1xyXG5cdHJldHVybiBjbGFzc05hbWUubGVuZ3RoID4gMCAmJiBuZXcgUmVnRXhwKCcoXnxcXFxccyknICsgbmFtZSArICcoXFxcXHN8JCknKS50ZXN0KGNsYXNzTmFtZSk7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBhZGRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuLy8gQWRkcyBgbmFtZWAgdG8gdGhlIGVsZW1lbnQncyBjbGFzcyBhdHRyaWJ1dGUuXHJcbmZ1bmN0aW9uIGFkZENsYXNzKGVsLCBuYW1lKSB7XHJcblx0aWYgKGVsLmNsYXNzTGlzdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHR2YXIgY2xhc3NlcyA9IHNwbGl0V29yZHMobmFtZSk7XHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gY2xhc3Nlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRlbC5jbGFzc0xpc3QuYWRkKGNsYXNzZXNbaV0pO1xyXG5cdFx0fVxyXG5cdH0gZWxzZSBpZiAoIWhhc0NsYXNzKGVsLCBuYW1lKSkge1xyXG5cdFx0dmFyIGNsYXNzTmFtZSA9IGdldENsYXNzKGVsKTtcclxuXHRcdHNldENsYXNzKGVsLCAoY2xhc3NOYW1lID8gY2xhc3NOYW1lICsgJyAnIDogJycpICsgbmFtZSk7XHJcblx0fVxyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gcmVtb3ZlQ2xhc3MoZWw6IEhUTUxFbGVtZW50LCBuYW1lOiBTdHJpbmcpXHJcbi8vIFJlbW92ZXMgYG5hbWVgIGZyb20gdGhlIGVsZW1lbnQncyBjbGFzcyBhdHRyaWJ1dGUuXHJcbmZ1bmN0aW9uIHJlbW92ZUNsYXNzKGVsLCBuYW1lKSB7XHJcblx0aWYgKGVsLmNsYXNzTGlzdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRlbC5jbGFzc0xpc3QucmVtb3ZlKG5hbWUpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRzZXRDbGFzcyhlbCwgdHJpbSgoJyAnICsgZ2V0Q2xhc3MoZWwpICsgJyAnKS5yZXBsYWNlKCcgJyArIG5hbWUgKyAnICcsICcgJykpKTtcclxuXHR9XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBzZXRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbW
14775U6IFN0cmluZylcclxuLy8gU2V0cyB0aGUgZWxlbWVudCdzIGNsYXNzLlxyXG5mdW5jdGlvbiBzZXRDbGFzcyhlbCwgbmFtZSkge1xyXG5cdGlmIChlbC5jbGFzc05hbWUuYmFzZVZhbCA9PT0gdW5kZWZpbmVkKSB7XHJcblx0XHRlbC5jbGFzc05hbWUgPSBuYW1lO1xyXG5cdH0gZWxzZSB7XHJcblx0XHQvLyBpbiBjYXNlIG9mIFNWRyBlbGVtZW50XHJcblx0XHRlbC5jbGFzc05hbWUuYmFzZVZhbCA9IG5hbWU7XHJcblx0fVxyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gZ2V0Q2xhc3MoZWw6IEhUTUxFbGVtZW50KTogU3RyaW5nXHJcbi8vIFJldHVybnMgdGhlIGVsZW1lbnQncyBjbGFzcy5cclxuZnVuY3Rpb24gZ2V0Q2xhc3MoZWwpIHtcclxuXHQvLyBDaGVjayBpZiB0aGUgZWxlbWVudCBpcyBhbiBTVkdFbGVtZW50SW5zdGFuY2UgYW5kIHVzZSB0aGUgY29ycmVzcG9uZGluZ0VsZW1lbnQgaW5zdGVhZFxyXG5cdC8vIChSZXF1aXJlZCBmb3IgbGlua2VkIFNWRyBlbGVtZW50cyBpbiBJRTExLilcclxuXHRpZiAoZWwuY29ycmVzcG9uZGluZ0VsZW1lbnQpIHtcclxuXHRcdGVsID0gZWwuY29ycmVzcG9uZGluZ0VsZW1lbnQ7XHJcblx0fVxyXG5cdHJldHVybiBlbC5jbGFzc05hbWUuYmFzZVZhbCA9PT0gdW5kZWZpbmVkID8gZWwuY2xhc3NOYW1lIDogZWwuY2xhc3NOYW1lLmJhc2VWYWw7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBzZXRPcGFjaXR5KGVsOiBIVE1MRWxlbWVudCwgb3BhY2l0eTogTnVtYmVyKVxyXG4vLyBTZXQgdGhlIG9wYWNpdHkgb2YgYW4gZWxlbWVudCAoaW5jbHVkaW5nIG9sZCBJRSBzdXBwb3J0KS5cclxuLy8gYG9wYWNpdHlgIG11c3QgYmUgYSBudW1iZXIgZnJvbSBgMGAgdG8gYDFgLlxyXG5mdW5jdGlvbiBzZXRPcGFjaXR5KGVsLCB2YWx1ZSkge1xyXG5cdGlmICgnb3BhY2l0eScgaW4gZWwuc3R5bGUpIHtcclxuXHRcdGVsLnN0eWxlLm9wYWNpdHkgPSB2YWx1ZTtcclxuXHR9IGVsc2UgaWYgKCdmaWx0ZXInIGluIGVsLnN0eWxlKSB7XHJcblx0XHRfc2V0T3BhY2l0eUlFKGVsLCB2YWx1ZSk7XHJcblx0fVxyXG59XHJcblxyXG5mdW5jdGlvbiBfc2V0T3BhY2l0eUlFKGVsLCB2YWx1ZSkge1xyXG5cdHZhciBmaWx0ZXIgPSBmYWxzZSxcclxuXHQgICAgZmlsdGVyTmFtZSA9ICdEWEltYWdlVHJhbnNmb3JtLk1pY3Jvc29mdC5BbHBoYSc7XHJcblxyXG5cdC8vIGZpbHRlcnMgY29sbGVjdGlvbiB0aHJvd3MgYW4gZXJyb3IgaWYgd2UgdHJ5IHRvIHJldHJpZXZlIGEgZmlsdGVyIHRoYXQgZG9lc24ndCBleGlzdFxyXG5cdHRyeSB7XHJcblx0XHRmaWx0ZXIgPSBlbC5maWx0ZXJzLml0ZW0oZmlsdGVyTmFtZSk7XHJcblx0fSBjYXRjaCAoZSkge1xyXG5cdFx0Ly8gZG9uJ3Qgc2V0IG9wYWNpdHkgdG8gMSBpZiB3ZSBoYXZlbid0IGFscmVhZHkgc2V0IGFuIG9wYWNpdHksXHJcblx0XHQvLyBpdCBpc24ndCBuZWVkZWQgYW5kIGJyZWFrcyB0cmFuc3BhcmVudCBwbmdzLlxyXG5cdFx0aWYgKHZhbHVlID09PSAxKSB7IHJldHVybjsgfVxyXG5cdH1cclxuXHJcblx0dmFsdWUgPSBNYXRoLnJvdW5kKHZhbHVlICogMTAwKTtcclxuXHJcblx0aWYgKGZpbHRlcikge1xyXG5cdFx0ZmlsdGVyLkVuYWJsZWQgPSAodmFsdWUgIT09IDEwMCk7XHJcblx0XHRmaWx0ZXIuT3BhY2l0eSA9IHZhbHVlO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRlbC5zdHlsZS5maWx0ZXIgKz0gJyBwcm9naWQ6JyArIGZpbHRlck5hbWUgKyAnKG9wYWNpdHk9JyArIHZhbHVlICsgJyknO1xyXG5cdH1cclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIHRlc3RQcm9wKHByb3BzOiBTdHJpbmdbXSk6IFN0cmluZ3xmYWxzZVxyXG4vLyBHb2VzIHRocm91Z2ggdGhlIGFycmF5IG9mIHN0eWxlIG5hbWVzIGFuZCByZXR1cm5zIHRoZSBmaXJzdCBuYW1lXHJcbi8vIHRoYXQgaXMgYSB2YWxpZCBzdHlsZSBuYW1lIGZvciBhbiBlbGVtZW50LiBJZiBubyBzdWNoIG5hbWUgaXMgZm91bmQsXHJcbi8vIGl0IHJldHVybnMgZmFsc2UuIFVzZWZ1bCBmb3IgdmVuZG9yLXByZWZpeGVkIHN0eWxlcyBsaWtlIGB0cmFuc2Zvcm1gLlxyXG5mdW5jdGlvbiB0ZXN0UHJvcChwcm9wcykge1xyXG5cdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHJcblx0Zm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykge1xyXG5cdFx0aWYgKHByb3BzW2ldIGluIHN0eWxlKSB7XHJcblx0XHRcdHJldHVybiBwcm9wc1tpXTtcclxuXHRcdH1cclxuXHR9XHJcblx0cmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gc2V0VHJhbnNmb3JtKGVsOiBIVE1MRWxlbWVudCwgb2Zmc2V0OiBQb2ludCwgc2NhbGU/OiBOdW1iZXIpXHJcbi8vIFJlc2V0cyB0aGUgM0QgQ1NTIHRyYW5zZm9ybSBvZiBgZWxgIHNvIGl0IGlzIHRyYW5zbGF0ZWQgYnkgYG9mZnNldGAgcGl4ZWxzXHJcbi8vIGFuZCBvcHRpb25hbGx5IHNjYWxlZCBieSBgc2NhbGVgLiBEb2VzIG5vdCBoYXZlIGFuIGVmZmVjdCBpZiB0aGVcclxuLy8gYnJvd3NlciBkb2Vzbid0IHN1cHBvcnQgM0QgQ1NTIHRyYW5zZm9ybXMuXHJcbmZ1bmN0aW9uIHNldFRyYW5zZm9ybShlbCwgb2Zmc2V0LCBzY2FsZSkge1xyXG5cdHZhciBwb3MgPSBvZmZzZXQgfHwgbmV3IFBvaW50KDAsIDApO1xyXG5cclxuXHRlbC5zdHlsZVtUUkFOU0ZPUk1dID1cclxuXHRcdChpZTNkID9cclxuXHRcdFx0J3RyYW5zbGF0ZSgnICsgcG9zLnggKyAncHgsJyArIHBvcy55ICsgJ3B4KScgOlxyXG5cdFx0XHQndHJhbnNsYXRlM2QoJyArIHBvcy54ICsgJ3B4LCcgKyBwb3MueSArICdweCwwKScpICtcclxuXHRcdChzY2FsZSA/ICcgc2NhbGUoJyArIHNjYWxlICsgJyknIDogJycpO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gc2V0UG9zaXRpb24oZWw6IEhUTUxFbGVtZW50LCBwb3NpdGlvbjogUG9pbnQpXHJcbi8vIFNldHMgdGhlIHBvc2l0aW9uIG9mIGBlbGAgdG8gY29vcmRpbmF0ZXMgc3BlY2lmaWVkIGJ5IGBwb3NpdGlvbmAsXHJcbi8vIHVzaW5nIENTUyB0cmFuc2xhdGUgb3IgdG9wL2xlZnQgcG9zaXRpb25pbmcgZGVwZW5kaW5nIG9uIHRoZSBicm93c2VyXHJcbi8vICh1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseSB0byBwb3NpdGlvbiBpdHMgbGF5ZXJzKS5cclxuZnVuY3Rpb24gc2V0UG9zaXRpb24oZWwsIHBvaW50KSB7XHJcblxyXG5cdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRlbC5fbGVhZmxldF9wb3MgPSBwb2ludDtcclxuXHQvKiBlc2xpbnQtZW5hYmxlICovXHJcblxyXG5cdGlmIChhbnkzZCkge1xyXG5cdFx0c2V0VHJhbnNmb3JtKGVsLCBwb2ludCk7XHJcblx0fSBlbHNlIHtcclxuXHRcdGVsLnN0eWxlLmxlZnQgPSBwb2ludC54ICsgJ3B4JztcclxuXHRcdGVsLnN0eWxlLnRvcCA9IHBvaW50LnkgKyAncHgnO1xyXG5cdH1cclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGdldFBvc2l0aW9uKGVsOiBIVE1MRWxlbWVudCk6IFBvaW50XHJcbi8vIFJldHVybnMgdGhlIGNvb3JkaW5hdGVzIG9mIGFuIGVsZW1lbnQgcHJldmlvdXNseSBwb3NpdGlvbmVkIHdpdGggc2V0UG9zaXRpb24uXHJcbmZ1bmN0aW9uIGdldFBvc2l0aW9uKGVsKSB7XHJcblx0Ly8gdGhpcyBtZXRob2QgaXMgb25seSB1c2VkIGZvciBlbGVtZW50cyBwcmV2aW91c2x5IHBvc2l0aW9uZWQgdXNpbmcgc2V0UG9zaXRpb24sXHJcblx0Ly8gc28gaXQncyBzYWZlIHRvIGNhY2hlIHRoZSBwb3NpdGlvbiBmb3IgcGVyZm9ybWFuY2VcclxuXHJcblx0cmV0dXJuIGVsLl9sZWFmbGV0X3BvcyB8fCBuZXcgUG9pbnQoMCwgMCk7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBkaXNhYmxlVGV4dFNlbGVjdGlvbigpXHJcbi8vIFByZXZlbn
14775RzIHRoZSB1c2VyIGZyb20gZ2VuZXJhdGluZyBgc2VsZWN0c3RhcnRgIERPTSBldmVudHMsIHVzdWFsbHkgZ2VuZXJhdGVkXHJcbi8vIHdoZW4gdGhlIHVzZXIgZHJhZ3MgdGhlIG1vdXNlIHRocm91Z2ggYSBwYWdlIHdpdGggdGV4dC4gVXNlZCBpbnRlcm5hbGx5XHJcbi8vIGJ5IExlYWZsZXQgdG8gb3ZlcnJpZGUgdGhlIGJlaGF2aW91ciBvZiBhbnkgY2xpY2stYW5kLWRyYWcgaW50ZXJhY3Rpb24gb25cclxuLy8gdGhlIG1hcC4gQWZmZWN0cyBkcmFnIGludGVyYWN0aW9ucyBvbiB0aGUgd2hvbGUgZG9jdW1lbnQuXHJcblxyXG4vLyBAZnVuY3Rpb24gZW5hYmxlVGV4dFNlbGVjdGlvbigpXHJcbi8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbmBdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKS5cclxudmFyIGRpc2FibGVUZXh0U2VsZWN0aW9uO1xyXG52YXIgZW5hYmxlVGV4dFNlbGVjdGlvbjtcclxudmFyIF91c2VyU2VsZWN0O1xyXG5pZiAoJ29uc2VsZWN0c3RhcnQnIGluIGRvY3VtZW50KSB7XHJcblx0ZGlzYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRvbih3aW5kb3csICdzZWxlY3RzdGFydCcsIHByZXZlbnREZWZhdWx0KTtcclxuXHR9O1xyXG5cdGVuYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRvZmYod2luZG93LCAnc2VsZWN0c3RhcnQnLCBwcmV2ZW50RGVmYXVsdCk7XHJcblx0fTtcclxufSBlbHNlIHtcclxuXHR2YXIgdXNlclNlbGVjdFByb3BlcnR5ID0gdGVzdFByb3AoXHJcblx0XHRbJ3VzZXJTZWxlY3QnLCAnV2Via2l0VXNlclNlbGVjdCcsICdPVXNlclNlbGVjdCcsICdNb3pVc2VyU2VsZWN0JywgJ21zVXNlclNlbGVjdCddKTtcclxuXHJcblx0ZGlzYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodXNlclNlbGVjdFByb3BlcnR5KSB7XHJcblx0XHRcdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHRcdFx0X3VzZXJTZWxlY3QgPSBzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldO1xyXG5cdFx0XHRzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldID0gJ25vbmUnO1xyXG5cdFx0fVxyXG5cdH07XHJcblx0ZW5hYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh1c2VyU2VsZWN0UHJvcGVydHkpIHtcclxuXHRcdFx0ZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlW3VzZXJTZWxlY3RQcm9wZXJ0eV0gPSBfdXNlclNlbGVjdDtcclxuXHRcdFx0X3VzZXJTZWxlY3QgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblx0fTtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGRpc2FibGVJbWFnZURyYWcoKVxyXG4vLyBBcyBbYEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbmBdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKSwgYnV0XHJcbi8vIGZvciBgZHJhZ3N0YXJ0YCBET00gZXZlbnRzLCB1c3VhbGx5IGdlbmVyYXRlZCB3aGVuIHRoZSB1c2VyIGRyYWdzIGFuIGltYWdlLlxyXG5mdW5jdGlvbiBkaXNhYmxlSW1hZ2VEcmFnKCkge1xyXG5cdG9uKHdpbmRvdywgJ2RyYWdzdGFydCcsIHByZXZlbnREZWZhdWx0KTtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGVuYWJsZUltYWdlRHJhZygpXHJcbi8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnYF0oI2RvbXV0aWwtZGlzYWJsZXRleHRzZWxlY3Rpb24pLlxyXG5mdW5jdGlvbiBlbmFibGVJbWFnZURyYWcoKSB7XHJcblx0b2ZmKHdpbmRvdywgJ2RyYWdzdGFydCcsIHByZXZlbnREZWZhdWx0KTtcclxufVxyXG5cclxudmFyIF9vdXRsaW5lRWxlbWVudDtcbnZhciBfb3V0bGluZVN0eWxlO1xyXG4vLyBAZnVuY3Rpb24gcHJldmVudE91dGxpbmUoZWw6IEhUTUxFbGVtZW50KVxyXG4vLyBNYWtlcyB0aGUgW291dGxpbmVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0NTUy9vdXRsaW5lKVxyXG4vLyBvZiB0aGUgZWxlbWVudCBgZWxgIGludmlzaWJsZS4gVXNlZCBpbnRlcm5hbGx5IGJ5IExlYWZsZXQgdG8gcHJldmVudFxyXG4vLyBmb2N1c2FibGUgZWxlbWVudHMgZnJvbSBkaXNwbGF5aW5nIGFuIG91dGxpbmUgd2hlbiB0aGUgdXNlciBwZXJmb3JtcyBhXHJcbi8vIGRyYWcgaW50ZXJhY3Rpb24gb24gdGhlbS5cclxuZnVuY3Rpb24gcHJldmVudE91dGxpbmUoZWxlbWVudCkge1xyXG5cdHdoaWxlIChlbGVtZW50LnRhYkluZGV4ID09PSAtMSkge1xyXG5cdFx0ZWxlbWVudCA9IGVsZW1lbnQucGFyZW50Tm9kZTtcclxuXHR9XHJcblx0aWYgKCFlbGVtZW50LnN0eWxlKSB7IHJldHVybjsgfVxyXG5cdHJlc3RvcmVPdXRsaW5lKCk7XHJcblx0X291dGxpbmVFbGVtZW50ID0gZWxlbWVudDtcclxuXHRfb3V0bGluZVN0eWxlID0gZWxlbWVudC5zdHlsZS5vdXRsaW5lO1xyXG5cdGVsZW1lbnQuc3R5bGUub3V0bGluZSA9ICdub25lJztcclxuXHRvbih3aW5kb3csICdrZXlkb3duJywgcmVzdG9yZU91dGxpbmUpO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gcmVzdG9yZU91dGxpbmUoKVxyXG4vLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwucHJldmVudE91dGxpbmVgXSgpLlxyXG5mdW5jdGlvbiByZXN0b3JlT3V0bGluZSgpIHtcclxuXHRpZiAoIV9vdXRsaW5lRWxlbWVudCkgeyByZXR1cm47IH1cclxuXHRfb3V0bGluZUVsZW1lbnQuc3R5bGUub3V0bGluZSA9IF9vdXRsaW5lU3R5bGU7XHJcblx0X291dGxpbmVFbGVtZW50ID0gdW5kZWZpbmVkO1xyXG5cdF9vdXRsaW5lU3R5bGUgPSB1bmRlZmluZWQ7XHJcblx0b2ZmKHdpbmRvdywgJ2tleWRvd24nLCByZXN0b3JlT3V0bGluZSk7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBnZXRTaXplZFBhcmVudE5vZGUoZWw6IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuLy8gRmluZHMgdGhlIGNsb3Nlc3QgcGFyZW50IG5vZGUgd2hpY2ggc2l6ZSAod2lkdGggYW5kIGhlaWdodCkgaXMgbm90IG51bGwuXHJcbmZ1bmN0aW9uIGdldFNpemVkUGFyZW50Tm9kZShlbGVtZW50KSB7XHJcblx0ZG8ge1xyXG5cdFx0ZWxlbWVudCA9IGVsZW1lbnQucGFyZW50Tm9kZTtcclxuXHR9IHdoaWxlICgoIWVsZW1lbnQub2Zmc2V0V2lkdGggfHwgIWVsZW1lbnQub2Zmc2V0SGVpZ2h0KSAmJiBlbGVtZW50ICE9PSBkb2N1bWVudC5ib2R5KTtcclxuXHRyZXR1cm4gZWxlbWVudDtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGdldFNjYWxlKGVsOiBIVE1MRWxlbWVudCk6IE9iamVjdFxyXG4vLyBDb21wdXRlcyB0aGUgQ1NTIHNjYWxlIGN1cnJlbnRseSBhcHBsaWVkIG9uIHRoZSBlbGVtZW50LlxyXG4vLyBSZXR1cm5zIGFuIG9iamVjdCB3aXRoIGB4YCBhbmQgYHlgIG1lbWJlcnMgYXMgaG9yaXpvbn
14775RhbCBhbmQgdmVydGljYWwgc2NhbGVzIHJlc3BlY3RpdmVseSxcclxuLy8gYW5kIGBib3VuZGluZ0NsaWVudFJlY3RgIGFzIHRoZSByZXN1bHQgb2YgW2BnZXRCb3VuZGluZ0NsaWVudFJlY3QoKWBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9FbGVtZW50L2dldEJvdW5kaW5nQ2xpZW50UmVjdCkuXHJcbmZ1bmN0aW9uIGdldFNjYWxlKGVsZW1lbnQpIHtcclxuXHR2YXIgcmVjdCA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7IC8vIFJlYWQtb25seSBpbiBvbGQgYnJvd3NlcnMuXHJcblxyXG5cdHJldHVybiB7XHJcblx0XHR4OiByZWN0LndpZHRoIC8gZWxlbWVudC5vZmZzZXRXaWR0aCB8fCAxLFxyXG5cdFx0eTogcmVjdC5oZWlnaHQgLyBlbGVtZW50Lm9mZnNldEhlaWdodCB8fCAxLFxyXG5cdFx0Ym91bmRpbmdDbGllbnRSZWN0OiByZWN0XHJcblx0fTtcclxufVxyXG5cblxudmFyIERvbVV0aWwgPSAoT2JqZWN0LmZyZWV6ZSB8fCBPYmplY3QpKHtcblx0VFJBTlNGT1JNOiBUUkFOU0ZPUk0sXG5cdFRSQU5TSVRJT046IFRSQU5TSVRJT04sXG5cdFRSQU5TSVRJT05fRU5EOiBUUkFOU0lUSU9OX0VORCxcblx0Z2V0OiBnZXQsXG5cdGdldFN0eWxlOiBnZXRTdHlsZSxcblx0Y3JlYXRlOiBjcmVhdGUkMSxcblx0cmVtb3ZlOiByZW1vdmUsXG5cdGVtcHR5OiBlbXB0eSxcblx0dG9Gcm9udDogdG9Gcm9udCxcblx0dG9CYWNrOiB0b0JhY2ssXG5cdGhhc0NsYXNzOiBoYXNDbGFzcyxcblx0YWRkQ2xhc3M6IGFkZENsYXNzLFxuXHRyZW1vdmVDbGFzczogcmVtb3ZlQ2xhc3MsXG5cdHNldENsYXNzOiBzZXRDbGFzcyxcblx0Z2V0Q2xhc3M6IGdldENsYXNzLFxuXHRzZXRPcGFjaXR5OiBzZXRPcGFjaXR5LFxuXHR0ZXN0UHJvcDogdGVzdFByb3AsXG5cdHNldFRyYW5zZm9ybTogc2V0VHJhbnNmb3JtLFxuXHRzZXRQb3NpdGlvbjogc2V0UG9zaXRpb24sXG5cdGdldFBvc2l0aW9uOiBnZXRQb3NpdGlvbixcblx0ZGlzYWJsZVRleHRTZWxlY3Rpb246IGRpc2FibGVUZXh0U2VsZWN0aW9uLFxuXHRlbmFibGVUZXh0U2VsZWN0aW9uOiBlbmFibGVUZXh0U2VsZWN0aW9uLFxuXHRkaXNhYmxlSW1hZ2VEcmFnOiBkaXNhYmxlSW1hZ2VEcmFnLFxuXHRlbmFibGVJbWFnZURyYWc6IGVuYWJsZUltYWdlRHJhZyxcblx0cHJldmVudE91dGxpbmU6IHByZXZlbnRPdXRsaW5lLFxuXHRyZXN0b3JlT3V0bGluZTogcmVzdG9yZU91dGxpbmUsXG5cdGdldFNpemVkUGFyZW50Tm9kZTogZ2V0U2l6ZWRQYXJlbnROb2RlLFxuXHRnZXRTY2FsZTogZ2V0U2NhbGVcbn0pO1xuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIERvbUV2ZW50XHJcbiAqIFV0aWxpdHkgZnVuY3Rpb25zIHRvIHdvcmsgd2l0aCB0aGUgW0RPTSBldmVudHNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9FdmVudCksIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKi9cclxuXHJcbi8vIEluc3BpcmVkIGJ5IEpvaG4gUmVzaWcsIERlYW4gRWR3YXJkcyBhbmQgWVVJIGFkZEV2ZW50IGltcGxlbWVudGF0aW9ucy5cclxuXHJcbi8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIHR5cGVzOiBTdHJpbmcsIGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuLy8gQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgRE9NIGV2ZW50IHR5cGUgb2YgdGhlXHJcbi8vIGVsZW1lbnQgYGVsYC4gWW91IGNhbiBvcHRpb25hbGx5IHNwZWNpZnkgdGhlIGNvbnRleHQgb2YgdGhlIGxpc3RlbmVyXHJcbi8vIChvYmplY3QgdGhlIGB0aGlzYCBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsXHJcbi8vIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcbi8vIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuZnVuY3Rpb24gb24ob2JqLCB0eXBlcywgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdGZvciAodmFyIHR5cGUgaW4gdHlwZXMpIHtcclxuXHRcdFx0YWRkT25lKG9iaiwgdHlwZSwgdHlwZXNbdHlwZV0sIGZuKTtcclxuXHRcdH1cclxuXHR9IGVsc2Uge1xyXG5cdFx0dHlwZXMgPSBzcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0YWRkT25lKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdHJldHVybiB0aGlzO1xyXG59XHJcblxyXG52YXIgZXZlbnRzS2V5ID0gJ19sZWFmbGV0X2V2ZW50cyc7XHJcblxyXG4vLyBAZnVuY3Rpb24gb2ZmKGVsOiBIVE1MRWxlbWVudCwgdHlwZXM6IFN0cmluZywgZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG4vLyBSZW1vdmVzIGEgcHJldmlvdXNseSBhZGRlZCBsaXN0ZW5lciBmdW5jdGlvbi5cclxuLy8gTm90ZSB0aGF0IGlmIHlvdSBwYXNzZWQgYSBjdXN0b20gY29udGV4dCB0byBvbiwgeW91IG11c3QgcGFzcyB0aGUgc2FtZVxyXG4vLyBjb250ZXh0IHRvIGBvZmZgIGluIG9yZGVyIHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZ1bmN0aW9uIG9mZihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcbi8vIFJlbW92ZXMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuZnVuY3Rpb24gb2ZmKG9iaiwgdHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdGlmICh0eXBlb2YgdHlwZXMgPT09ICdvYmplY3QnKSB7XHJcblx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdHJlbW92ZU9uZShvYmosIHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHR9XHJcblx0fSBlbHNlIGlmICh0eXBlcykge1xyXG5cdFx0dHlwZXMgPSBzcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0cmVtb3ZlT25lKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdH1cclxuXHR9IGVsc2Uge1xyXG5cdFx0Zm9yICh2YXIgaiBpbiBvYmpbZXZlbnRzS2V5XSkge1xyXG5cdFx0XHRyZW1vdmVPbmUob2JqLCBqLCBvYmpbZXZlbnRzS2V5XVtqXSk7XHJcblx0XHR9XHJcblx0XHRkZWxldGUgb2JqW2V2ZW50c0tleV0
147757XHJcblx0fVxyXG5cclxuXHRyZXR1cm4gdGhpcztcclxufVxyXG5cclxuZnVuY3Rpb24gYWRkT25lKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHR2YXIgaWQgPSB0eXBlICsgc3RhbXAoZm4pICsgKGNvbnRleHQgPyAnXycgKyBzdGFtcChjb250ZXh0KSA6ICcnKTtcclxuXHJcblx0aWYgKG9ialtldmVudHNLZXldICYmIG9ialtldmVudHNLZXldW2lkXSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHR2YXIgaGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRyZXR1cm4gZm4uY2FsbChjb250ZXh0IHx8IG9iaiwgZSB8fCB3aW5kb3cuZXZlbnQpO1xyXG5cdH07XHJcblxyXG5cdHZhciBvcmlnaW5hbEhhbmRsZXIgPSBoYW5kbGVyO1xyXG5cclxuXHRpZiAocG9pbnRlciAmJiB0eXBlLmluZGV4T2YoJ3RvdWNoJykgPT09IDApIHtcclxuXHRcdC8vIE5lZWRzIERvbUV2ZW50LlBvaW50ZXIuanNcclxuXHRcdGFkZFBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGhhbmRsZXIsIGlkKTtcclxuXHJcblx0fSBlbHNlIGlmICh0b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgYWRkRG91YmxlVGFwTGlzdGVuZXIgJiZcclxuXHQgICAgICAgICAgICEocG9pbnRlciAmJiBjaHJvbWUpKSB7XHJcblx0XHQvLyBDaHJvbWUgPjU1IGRvZXMgbm90IG5lZWQgdGhlIHN5bnRoZXRpYyBkYmxjbGlja3MgZnJvbSBhZGREb3VibGVUYXBMaXN0ZW5lclxyXG5cdFx0Ly8gU2VlICM1MTgwXHJcblx0XHRhZGREb3VibGVUYXBMaXN0ZW5lcihvYmosIGhhbmRsZXIsIGlkKTtcclxuXHJcblx0fSBlbHNlIGlmICgnYWRkRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcignb253aGVlbCcgaW4gb2JqID8gJ3doZWVsJyA6ICdtb3VzZXdoZWVsJywgaGFuZGxlciwgcGFzc2l2ZUV2ZW50cyA/IHtwYXNzaXZlOiBmYWxzZX0gOiBmYWxzZSk7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgodHlwZSA9PT0gJ21vdXNlZW50ZXInKSB8fCAodHlwZSA9PT0gJ21vdXNlbGVhdmUnKSkge1xyXG5cdFx0XHRoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHRlID0gZSB8fCB3aW5kb3cuZXZlbnQ7XHJcblx0XHRcdFx0aWYgKGlzRXh0ZXJuYWxUYXJnZXQob2JqLCBlKSkge1xyXG5cdFx0XHRcdFx0b3JpZ2luYWxIYW5kbGVyKGUpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fTtcclxuXHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOiAnbW91c2VvdXQnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0aWYgKHR5cGUgPT09ICdjbGljaycgJiYgYW5kcm9pZCkge1xyXG5cdFx0XHRcdGhhbmRsZXIgPSBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHRcdFx0ZmlsdGVyQ2xpY2soZSwgb3JpZ2luYWxIYW5kbGVyKTtcclxuXHRcdFx0XHR9O1xyXG5cdFx0XHR9XHJcblx0XHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHRcdH1cclxuXHJcblx0fSBlbHNlIGlmICgnYXR0YWNoRXZlbnQnIGluIG9iaikge1xyXG5cdFx0b2JqLmF0dGFjaEV2ZW50KCdvbicgKyB0eXBlLCBoYW5kbGVyKTtcclxuXHR9XHJcblxyXG5cdG9ialtldmVudHNLZXldID0gb2JqW2V2ZW50c0tleV0gfHwge307XHJcblx0b2JqW2V2ZW50c0tleV1baWRdID0gaGFuZGxlcjtcclxufVxyXG5cclxuZnVuY3Rpb24gcmVtb3ZlT25lKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0dmFyIGlkID0gdHlwZSArIHN0YW1wKGZuKSArIChjb250ZXh0ID8gJ18nICsgc3RhbXAoY29udGV4dCkgOiAnJyksXHJcblx0ICAgIGhhbmRsZXIgPSBvYmpbZXZlbnRzS2V5XSAmJiBvYmpbZXZlbnRzS2V5XVtpZF07XHJcblxyXG5cdGlmICghaGFuZGxlcikgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRpZiAocG9pbnRlciAmJiB0eXBlLmluZGV4T2YoJ3RvdWNoJykgPT09IDApIHtcclxuXHRcdHJlbW92ZVBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGlkKTtcclxuXHJcblx0fSBlbHNlIGlmICh0b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgcmVtb3ZlRG91YmxlVGFwTGlzdGVuZXIgJiZcclxuXHQgICAgICAgICAgICEocG9pbnRlciAmJiBjaHJvbWUpKSB7XHJcblx0XHRyZW1vdmVEb3VibGVUYXBMaXN0ZW5lcihvYmosIGlkKTtcclxuXHJcblx0fSBlbHNlIGlmICgncmVtb3ZlRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcignb253aGVlbCcgaW4gb2JqID8gJ3doZWVsJyA6ICdtb3VzZXdoZWVsJywgaGFuZGxlciwgcGFzc2l2ZUV2ZW50cyA/IHtwYXNzaXZlOiBmYWxzZX0gOiBmYWxzZSk7XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoXHJcblx0XHRcdFx0dHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOlxyXG5cdFx0XHRcdHR5cGUgPT09ICdtb3VzZWxlYXZlJyA/ICdtb3VzZW91dCcgOiB0eXBlLCBoYW5kbGVyLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdH0gZWxzZSBpZiAoJ2RldGFjaEV2ZW50JyBpbiBvYmopIHtcclxuXHRcdG9iai5kZXRhY2hFdmVudCgnb24nICsgdHlwZSwgaGFuZGxlcik7XHJcblx0fVxyXG5cclxuXHRvYmpbZXZlbnRzS2V5XVtpZF0gPSBudWxsO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gc3RvcFByb3BhZ2F0aW9uKGV2OiBET01FdmVudCk6IHRoaXNcclxuLy8gU3RvcCB0aGUgZ2l2ZW4gZXZlbnQgZnJvbSBwcm9wYWdhdGlvbiB0byBwYXJlbnQgZWxlbWVudHMuIFVzZWQgaW5zaWRlIHRoZSBsaXN0ZW5lciBmdW5jdGlvbnM6XHJcbi8vIGBgYGpzXHJcbi8vIEwuRG9tRXZlbnQub24oZGl2LCAnY2xpY2snLCBmdW5jdGlvbiAoZXYpIHtcclxuLy8gXHRMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbihldik7XHJcbi8vIH0pO1xyXG4vLyBgYGBcclxuZnVuY3Rpb24gc3RvcFByb3BhZ2F0aW9uKGUpIHtcclxuXHJcblx0aWYgKGUuc3RvcFByb3BhZ2F0aW9uKSB7XHJcblx0XHRlLnN0b3BQcm9wYWdhdGlvbigpO1xyXG5cdH0gZWxzZSBpZiAoZS5vcmlnaW5hbEV2ZW50KSB7ICAvLyBJbiBjYXNlIG9mIExlYWZsZXQgZXZlbnQuXHJcblx0XHRlLm9yaWdpbmFsRXZlbnQuX3N0b3BwZWQgPSB0cnVlO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRlLmNhbmNlbEJ1YmJsZSA9IHRydWU7XHJcblx0fVxyXG5cdHNraXBwZWQoZSk7XHJcblxyXG5cdHJldHVybi
14775B0aGlzO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uKGVsOiBIVE1MRWxlbWVudCk6IHRoaXNcclxuLy8gQWRkcyBgc3RvcFByb3BhZ2F0aW9uYCB0byB0aGUgZWxlbWVudCdzIGAnbW91c2V3aGVlbCdgIGV2ZW50cyAocGx1cyBicm93c2VyIHZhcmlhbnRzKS5cclxuZnVuY3Rpb24gZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uKGVsKSB7XHJcblx0YWRkT25lKGVsLCAnbW91c2V3aGVlbCcsIHN0b3BQcm9wYWdhdGlvbik7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihlbDogSFRNTEVsZW1lbnQpOiB0aGlzXHJcbi8vIEFkZHMgYHN0b3BQcm9wYWdhdGlvbmAgdG8gdGhlIGVsZW1lbnQncyBgJ2NsaWNrJ2AsIGAnZG91YmxlY2xpY2snYCxcclxuLy8gYCdtb3VzZWRvd24nYCBhbmQgYCd0b3VjaHN0YXJ0J2AgZXZlbnRzIChwbHVzIGJyb3dzZXIgdmFyaWFudHMpLlxyXG5mdW5jdGlvbiBkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihlbCkge1xyXG5cdG9uKGVsLCAnbW91c2Vkb3duIHRvdWNoc3RhcnQgZGJsY2xpY2snLCBzdG9wUHJvcGFnYXRpb24pO1xyXG5cdGFkZE9uZShlbCwgJ2NsaWNrJywgZmFrZVN0b3ApO1xyXG5cdHJldHVybiB0aGlzO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gcHJldmVudERlZmF1bHQoZXY6IERPTUV2ZW50KTogdGhpc1xyXG4vLyBQcmV2ZW50cyB0aGUgZGVmYXVsdCBhY3Rpb24gb2YgdGhlIERPTSBFdmVudCBgZXZgIGZyb20gaGFwcGVuaW5nIChzdWNoIGFzXHJcbi8vIGZvbGxvd2luZyBhIGxpbmsgaW4gdGhlIGhyZWYgb2YgdGhlIGEgZWxlbWVudCwgb3IgZG9pbmcgYSBQT1NUIHJlcXVlc3RcclxuLy8gd2l0aCBwYWdlIHJlbG9hZCB3aGVuIGEgYDxmb3JtPmAgaXMgc3VibWl0dGVkKS5cclxuLy8gVXNlIGl0IGluc2lkZSBsaXN0ZW5lciBmdW5jdGlvbnMuXHJcbmZ1bmN0aW9uIHByZXZlbnREZWZhdWx0KGUpIHtcclxuXHRpZiAoZS5wcmV2ZW50RGVmYXVsdCkge1xyXG5cdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRlLnJldHVyblZhbHVlID0gZmFsc2U7XHJcblx0fVxyXG5cdHJldHVybiB0aGlzO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gc3RvcChldjogRE9NRXZlbnQpOiB0aGlzXHJcbi8vIERvZXMgYHN0b3BQcm9wYWdhdGlvbmAgYW5kIGBwcmV2ZW50RGVmYXVsdGAgYXQgdGhlIHNhbWUgdGltZS5cclxuZnVuY3Rpb24gc3RvcChlKSB7XHJcblx0cHJldmVudERlZmF1bHQoZSk7XHJcblx0c3RvcFByb3BhZ2F0aW9uKGUpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gZ2V0TW91c2VQb3NpdGlvbihldjogRE9NRXZlbnQsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogUG9pbnRcclxuLy8gR2V0cyBub3JtYWxpemVkIG1vdXNlIHBvc2l0aW9uIGZyb20gYSBET00gZXZlbnQgcmVsYXRpdmUgdG8gdGhlXHJcbi8vIGBjb250YWluZXJgIChib3JkZXIgZXhjbHVkZWQpIG9yIHRvIHRoZSB3aG9sZSBwYWdlIGlmIG5vdCBzcGVjaWZpZWQuXHJcbmZ1bmN0aW9uIGdldE1vdXNlUG9zaXRpb24oZSwgY29udGFpbmVyKSB7XHJcblx0aWYgKCFjb250YWluZXIpIHtcclxuXHRcdHJldHVybiBuZXcgUG9pbnQoZS5jbGllbnRYLCBlLmNsaWVudFkpO1xyXG5cdH1cclxuXHJcblx0dmFyIHNjYWxlID0gZ2V0U2NhbGUoY29udGFpbmVyKSxcclxuXHQgICAgb2Zmc2V0ID0gc2NhbGUuYm91bmRpbmdDbGllbnRSZWN0OyAvLyBsZWZ0IGFuZCB0b3AgIHZhbHVlcyBhcmUgaW4gcGFnZSBzY2FsZSAobGlrZSB0aGUgZXZlbnQgY2xpZW50WC9ZKVxyXG5cclxuXHRyZXR1cm4gbmV3IFBvaW50KFxyXG5cdFx0Ly8gb2Zmc2V0LmxlZnQvdG9wIHZhbHVlcyBhcmUgaW4gcGFnZSBzY2FsZSAobGlrZSBjbGllbnRYL1kpLFxyXG5cdFx0Ly8gd2hlcmVhcyBjbGllbnRMZWZ0L1RvcCAoYm9yZGVyIHdpZHRoKSB2YWx1ZXMgYXJlIHRoZSBvcmlnaW5hbCB2YWx1ZXMgKGJlZm9yZSBDU1Mgc2NhbGUgYXBwbGllcykuXHJcblx0XHQoZS5jbGllbnRYIC0gb2Zmc2V0LmxlZnQpIC8gc2NhbGUueCAtIGNvbnRhaW5lci5jbGllbnRMZWZ0LFxyXG5cdFx0KGUuY2xpZW50WSAtIG9mZnNldC50b3ApIC8gc2NhbGUueSAtIGNvbnRhaW5lci5jbGllbnRUb3BcclxuXHQpO1xyXG59XHJcblxyXG4vLyBDaHJvbWUgb24gV2luIHNjcm9sbHMgZG91YmxlIHRoZSBwaXhlbHMgYXMgaW4gb3RoZXIgcGxhdGZvcm1zIChzZWUgIzQ1MzgpLFxyXG4vLyBhbmQgRmlyZWZveCBzY3JvbGxzIGRldmljZSBwaXhlbHMsIG5vdCBDU1MgcGl4ZWxzXHJcbnZhciB3aGVlbFB4RmFjdG9yID1cclxuXHQod2luICYmIGNocm9tZSkgPyAyICogd2luZG93LmRldmljZVBpeGVsUmF0aW8gOlxyXG5cdGdlY2tvID8gd2luZG93LmRldmljZVBpeGVsUmF0aW8gOiAxO1xyXG5cclxuLy8gQGZ1bmN0aW9uIGdldFdoZWVsRGVsdGEoZXY6IERPTUV2ZW50KTogTnVtYmVyXHJcbi8vIEdldHMgbm9ybWFsaXplZCB3aGVlbCBkZWx0YSBmcm9tIGEgbW91c2V3aGVlbCBET00gZXZlbnQsIGluIHZlcnRpY2FsXHJcbi8vIHBpeGVscyBzY3JvbGxlZCAobmVnYXRpdmUgaWYgc2Nyb2xsaW5nIGRvd24pLlxyXG4vLyBFdmVudHMgZnJvbSBwb2ludGluZyBkZXZpY2VzIHdpdGhvdXQgcHJlY2lzZSBzY3JvbGxpbmcgYXJlIG1hcHBlZCB0b1xyXG4vLyBhIGJlc3QgZ3Vlc3Mgb2YgNjAgcGl4ZWxzLlxyXG5mdW5jdGlvbiBnZXRXaGVlbERlbHRhKGUpIHtcclxuXHRyZXR1cm4gKGVkZ2UpID8gZS53aGVlbERlbHRhWSAvIDIgOiAvLyBEb24ndCB0cnVzdCB3aW5kb3ctZ2VvbWV0cnktYmFzZWQgZGVsdGFcclxuXHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAwKSA/IC1lLmRlbHRhWSAvIHdoZWVsUHhGYWN0b3IgOiAvLyBQaXhlbHNcclxuXHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAxKSA/IC1lLmRlbHRhWSAqIDIwIDogLy8gTGluZXNcclxuXHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAyKSA/IC1lLmRlbHRhWSAqIDYwIDogLy8gUGFnZXNcclxuXHQgICAgICAgKGUuZGVsdGFYIHx8IGUuZGVsdGFaKSA/IDAgOlx0Ly8gU2tpcCBob3Jpem9udGFsL2RlcHRoIHdoZWVsIGV2ZW50c1xyXG5cdCAgICAgICBlLndoZWVsRGVsdGEgPyAoZS53aGVlbERlbHRhWSB8fCBlLndoZWVsRGVsdGEpIC8gMiA6IC8vIExlZ2FjeSBJRSBwaXhlbHNcclxuXHQgICAgICAgKGUuZGV0YWlsICYmIE1hdGguYWJzKGUuZGV0YWlsKSA8IDMyNzY1KSA/IC1lLmRldGFpbCAqIDIwIDogLy8gTGVnYWN5IE1veiBsaW5lc1xyXG5cdCAgICAgICBlLmRldGFpbCA/IGUuZGV0YWlsIC8gLTMyNzY1ICogNjAgOiAvLyBMZWdhY3kgTW96IHBhZ2VzXHJcblx0ICAgICAgIDA7XHJcbn1cclxuXHJcbnZhciBza2lwRXZlbn
14775RzID0ge307XHJcblxyXG5mdW5jdGlvbiBmYWtlU3RvcChlKSB7XHJcblx0Ly8gZmFrZXMgc3RvcFByb3BhZ2F0aW9uIGJ5IHNldHRpbmcgYSBzcGVjaWFsIGV2ZW50IGZsYWcsIGNoZWNrZWQvcmVzZXQgd2l0aCBza2lwcGVkKGUpXHJcblx0c2tpcEV2ZW50c1tlLnR5cGVdID0gdHJ1ZTtcclxufVxyXG5cclxuZnVuY3Rpb24gc2tpcHBlZChlKSB7XHJcblx0dmFyIGV2ZW50cyA9IHNraXBFdmVudHNbZS50eXBlXTtcclxuXHQvLyByZXNldCB3aGVuIGNoZWNraW5nLCBhcyBpdCdzIG9ubHkgdXNlZCBpbiBtYXAgY29udGFpbmVyIGFuZCBwcm9wYWdhdGVzIG91dHNpZGUgb2YgdGhlIG1hcFxyXG5cdHNraXBFdmVudHNbZS50eXBlXSA9IGZhbHNlO1xyXG5cdHJldHVybiBldmVudHM7XHJcbn1cclxuXHJcbi8vIGNoZWNrIGlmIGVsZW1lbnQgcmVhbGx5IGxlZnQvZW50ZXJlZCB0aGUgZXZlbnQgdGFyZ2V0IChmb3IgbW91c2VlbnRlci9tb3VzZWxlYXZlKVxyXG5mdW5jdGlvbiBpc0V4dGVybmFsVGFyZ2V0KGVsLCBlKSB7XHJcblxyXG5cdHZhciByZWxhdGVkID0gZS5yZWxhdGVkVGFyZ2V0O1xyXG5cclxuXHRpZiAoIXJlbGF0ZWQpIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0dHJ5IHtcclxuXHRcdHdoaWxlIChyZWxhdGVkICYmIChyZWxhdGVkICE9PSBlbCkpIHtcclxuXHRcdFx0cmVsYXRlZCA9IHJlbGF0ZWQucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHR9IGNhdGNoIChlcnIpIHtcclxuXHRcdHJldHVybiBmYWxzZTtcclxuXHR9XHJcblx0cmV0dXJuIChyZWxhdGVkICE9PSBlbCk7XHJcbn1cclxuXHJcbnZhciBsYXN0Q2xpY2s7XHJcblxyXG4vLyB0aGlzIGlzIGEgaG9ycmlibGUgd29ya2Fyb3VuZCBmb3IgYSBidWcgaW4gQW5kcm9pZCB3aGVyZSBhIHNpbmdsZSB0b3VjaCB0cmlnZ2VycyB0d28gY2xpY2sgZXZlbnRzXHJcbmZ1bmN0aW9uIGZpbHRlckNsaWNrKGUsIGhhbmRsZXIpIHtcclxuXHR2YXIgdGltZVN0YW1wID0gKGUudGltZVN0YW1wIHx8IChlLm9yaWdpbmFsRXZlbnQgJiYgZS5vcmlnaW5hbEV2ZW50LnRpbWVTdGFtcCkpLFxyXG5cdCAgICBlbGFwc2VkID0gbGFzdENsaWNrICYmICh0aW1lU3RhbXAgLSBsYXN0Q2xpY2spO1xyXG5cclxuXHQvLyBhcmUgdGhleSBjbG9zZXIgdG9nZXRoZXIgdGhhbiA1MDBtcyB5ZXQgbW9yZSB0aGFuIDEwMG1zP1xyXG5cdC8vIEFuZHJvaWQgdHlwaWNhbGx5IHRyaWdnZXJzIHRoZW0gfjMwMG1zIGFwYXJ0IHdoaWxlIG11bHRpcGxlIGxpc3RlbmVyc1xyXG5cdC8vIG9uIHRoZSBzYW1lIGV2ZW50IHNob3VsZCBiZSB0cmlnZ2VyZWQgZmFyIGZhc3RlcjtcclxuXHQvLyBvciBjaGVjayBpZiBjbGljayBpcyBzaW11bGF0ZWQgb24gdGhlIGVsZW1lbnQsIGFuZCBpZiBpdCBpcywgcmVqZWN0IGFueSBub24tc2ltdWxhdGVkIGV2ZW50c1xyXG5cclxuXHRpZiAoKGVsYXBzZWQgJiYgZWxhcHNlZCA+IDEwMCAmJiBlbGFwc2VkIDwgNTAwKSB8fCAoZS50YXJnZXQuX3NpbXVsYXRlZENsaWNrICYmICFlLl9zaW11bGF0ZWQpKSB7XHJcblx0XHRzdG9wKGUpO1xyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHRsYXN0Q2xpY2sgPSB0aW1lU3RhbXA7XHJcblxyXG5cdGhhbmRsZXIoZSk7XHJcbn1cclxuXHJcblxyXG5cblxudmFyIERvbUV2ZW50ID0gKE9iamVjdC5mcmVlemUgfHwgT2JqZWN0KSh7XG5cdG9uOiBvbixcblx0b2ZmOiBvZmYsXG5cdHN0b3BQcm9wYWdhdGlvbjogc3RvcFByb3BhZ2F0aW9uLFxuXHRkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb246IGRpc2FibGVTY3JvbGxQcm9wYWdhdGlvbixcblx0ZGlzYWJsZUNsaWNrUHJvcGFnYXRpb246IGRpc2FibGVDbGlja1Byb3BhZ2F0aW9uLFxuXHRwcmV2ZW50RGVmYXVsdDogcHJldmVudERlZmF1bHQsXG5cdHN0b3A6IHN0b3AsXG5cdGdldE1vdXNlUG9zaXRpb246IGdldE1vdXNlUG9zaXRpb24sXG5cdGdldFdoZWVsRGVsdGE6IGdldFdoZWVsRGVsdGEsXG5cdGZha2VTdG9wOiBmYWtlU3RvcCxcblx0c2tpcHBlZDogc2tpcHBlZCxcblx0aXNFeHRlcm5hbFRhcmdldDogaXNFeHRlcm5hbFRhcmdldCxcblx0YWRkTGlzdGVuZXI6IG9uLFxuXHRyZW1vdmVMaXN0ZW5lcjogb2ZmXG59KTtcblxuLypcbiAqIEBjbGFzcyBQb3NBbmltYXRpb25cbiAqIEBha2EgTC5Qb3NBbmltYXRpb25cbiAqIEBpbmhlcml0cyBFdmVudGVkXG4gKiBVc2VkIGludGVybmFsbHkgZm9yIHBhbm5pbmcgYW5pbWF0aW9ucywgdXRpbGl6aW5nIENTUzMgVHJhbnNpdGlvbnMgZm9yIG1vZGVybiBicm93c2VycyBhbmQgYSB0aW1lciBmYWxsYmFjayBmb3IgSUU2LTkuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYGpzXG4gKiB2YXIgZnggPSBuZXcgTC5Qb3NBbmltYXRpb24oKTtcbiAqIGZ4LnJ1bihlbCwgWzMwMCwgNTAwXSwgMC41KTtcbiAqIGBgYFxuICpcbiAqIEBjb25zdHJ1Y3RvciBMLlBvc0FuaW1hdGlvbigpXG4gKiBDcmVhdGVzIGEgYFBvc0FuaW1hdGlvbmAgb2JqZWN0LlxuICpcbiAqL1xuXG52YXIgUG9zQW5pbWF0aW9uID0gRXZlbnRlZC5leHRlbmQoe1xuXG5cdC8vIEBtZXRob2QgcnVuKGVsOiBIVE1MRWxlbWVudCwgbmV3UG9zOiBQb2ludCwgZHVyYXRpb24/OiBOdW1iZXIsIGVhc2VMaW5lYXJpdHk/OiBOdW1iZXIpXG5cdC8vIFJ1biBhbiBhbmltYXRpb24gb2YgYSBnaXZlbiBlbGVtZW50IHRvIGEgbmV3IHBvc2l0aW9uLCBvcHRpb25hbGx5IHNldHRpbmdcblx0Ly8gZHVyYXRpb24gaW4gc2Vjb25kcyAoYDAuMjVgIGJ5IGRlZmF1bHQpIGFuZCBlYXNpbmcgbGluZWFyaXR5IGZhY3RvciAoM3JkXG5cdC8vIGFyZ3VtZW50IG9mIHRoZSBbY3ViaWMgYmV6aWVyIGN1cnZlXShodHRwOi8vY3ViaWMtYmV6aWVyLmNvbS8jMCwwLC41LDEpLFxuXHQvLyBgMC41YCBieSBkZWZhdWx0KS5cblx0cnVuOiBmdW5jdGlvbiAoZWwsIG5ld1BvcywgZHVyYXRpb24sIGVhc2VMaW5lYXJpdHkpIHtcblx0XHR0aGlzLnN0b3AoKTtcblxuXHRcdHRoaXMuX2VsID0gZWw7XG5cdFx0dGhpcy5faW5Qcm9ncmVzcyA9IHRydWU7XG5cdFx0dGhpcy5fZHVyYXRpb24gPSBkdXJhdGlvbiB8fCAwLjI1O1xuXHRcdHRoaXMuX2Vhc2VPdXRQb3dlciA9IDEgLyBNYXRoLm1heChlYXNlTGluZWFyaXR5IHx8IDAuNSwgMC4yKTtcblxuXHRcdHRoaXMuX3N0YXJ0UG9zID0gZ2V0UG9zaXRpb24oZWwpO1xuXHRcdHRoaXMuX29mZnNldCA9IG5ld1Bvcy5zdWJ0cmFjdCh0aGlzLl9zdGFydFBvcyk7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gK25ldyBEYXRlKCk7XG5cblx0XHQvLyBAZXZlbnQgc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYW5pbWF0aW9uIHN0YXJ0c1xuXHRcdHRoaXMuZmlyZSgnc3RhcnQnKTtcblxuXHRcdHRoaXMuX2FuaW1hdGUoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHN0b3AoKVxuXHQvLyBTdG9wcyB0aGUgYW5pbWF0aW9uIChpZiBjdXJyZW50bHkgcnVubmluZykuXG5cdHN0b3A6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2luUHJvZ3Jlc3MpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9zdGVwKHRydWUpO1xuXHRcdHRoaXMuX2NvbXBsZXRlKCk7XG5cdH0sXG5cblx0X2FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBhbmltYXRpb24gbG9vcFxuXHRcdHRoaXMuX2FuaW1JZCA9IHJlcXVlc3RBbmltRnJhbWUodGhpcy5fYW5pbWF0ZSwgdGhpcyk7XG5cdFx0dGhpcy5fc3RlcCgpO1xuXHR9LFxuXG5cdF9zdGVwOiBmdW5jdGlvbiAocm91bmQpIHtcblx0XHR2YXIgZWxhcHNlZCA9ICgrbmV3IERhdGUoKSkgLSB0aGlzLl9zdGFydFRpbWUsXG5cdFx0ICAgIGR1cmF0aW9uID0gdGhpcy5fZHVyYXRpb24gKiAxMDAwO1xuXG5cdFx0aWYgKGVsYXBzZWQgPCBkdXJhdGlvbikge1xuXHRcdFx0dGhpcy5fcnVuRnJhbWUodGhpcy5fZWFzZU91dChlbGFwc2VkIC8gZHVyYXRpb24pLCByb3VuZCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX3J1bkZyYW1lKDEpO1xuXHRcdFx0dGhpcy5fY29tcGxldGUoKTtcblx0XHR9XG5cdH0sXG5cblx0X3J1bkZyYW1lOiBmdW5jdGlvbiAocHJvZ3Jlc3MsIHJvdW5kKSB7XG5cdFx0dmFyIHBvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZCh0aGlzLl9vZmZzZXQubXVsdGlwbHlCeShwcm9ncmVzcykpO1xuXHRcdGlmIChyb3VuZCkge1xuXHRcdFx0cG9zLl9yb3VuZCgpO1xuXHRcdH1cblx0XHRzZXRQb3NpdGlvbih0aGlzLl9lbCwgcG9zKTtcblxuXHRcdC8vIEBldmVudCBzdGVwOiBFdmVudFxuXHRcdC8vIEZpcmVkIGNvbnRpbnVvdXNseSBkdXJpbmcgdGhlIGFuaW1hdGlvbi5cblx0XHR0aGlzLmZpcmUoJ3N0ZXAnKTtcblx0fSxcblxuXHRfY29tcGxldGU6IGZ1bmN0aW9uICgpIHtcblx0XHRjYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbUlkKTtcblxuXHRcdHRoaXMuX2luUHJvZ3Jlc3MgPSBmYWxzZTtcblx0XHQvLyBAZXZlbnQgZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGFuaW1hdGlvbiBlbmRzLlxuXHRcdHRoaXMuZmlyZSgnZW5kJyk7XG5cdH0sXG5cblx0X2Vhc2VPdXQ6IGZ1bmN0aW9uICh0KSB7XG5cdFx0cmV0dXJuIDEgLSBNYXRoLnBvdygxIC0gdCwgdGhpcy5fZWFzZU91dFBvd2VyKTtcblx0fVxufSk7XG5cbi8qXHJcbiAqIEBjbGFzcyBNYXBcclxuICogQGFrYSBMLk1hcFxyXG4gKiBAaW5oZXJpdHMgRXZlbnRlZFxyXG4gKlxyXG4gKiBUaGUgY2VudHJhbCBjbGFzcyBvZiB0aGUgQVBJIOKAlCBpdCBpcyB1c2VkIHRvIGNyZWF0ZSBhIG1hcCBvbiBhIHBhZ2UgYW5kIG1hbmlwdWxhdGUgaXQuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIC8vIGluaXRpYWxpemUgdGhlIG1hcCBvbiB0aGUgXCJtYXBcIiBkaXYgd2l0aCBhIGdpdmVuIGNlbnRlciBhbmQgem9vbVxyXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcclxuICogXHRjZW50ZXI6IFs1MS41MDUsIC0wLjA5XSxcclxuICogXHR6b29tOiAxM1xyXG4gKiB9KTtcclxuICogYGBgXHJcbiAqXHJcbiAqL1xyXG5cclxudmFyIE1hcCA9IEV2ZW50ZWQuZXh0ZW5kKHtcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQHNlY3Rpb24gTWFwIFN0YXRlIE9wdGlvbnNcclxuXHRcdC8vIEBvcHRpb24gY3JzOiBDUlMgPSBMLkNSUy5FUFNHMzg1N1xyXG5cdFx0Ly8gVGhlIFtDb29yZGluYX
14775RlIFJlZmVyZW5jZSBTeXN0ZW1dKCNjcnMpIHRvIHVzZS4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgeW91J3JlIG5vdFxyXG5cdFx0Ly8gc3VyZSB3aGF0IGl0IG1lYW5zLlxyXG5cdFx0Y3JzOiBFUFNHMzg1NyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNlbnRlcjogTGF0TG5nID0gdW5kZWZpbmVkXHJcblx0XHQvLyBJbml0aWFsIGdlb2dyYXBoaWMgY2VudGVyIG9mIHRoZSBtYXBcclxuXHRcdGNlbnRlcjogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBJbml0aWFsIG1hcCB6b29tIGxldmVsXHJcblx0XHR6b29tOiB1bmRlZmluZWQsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAqXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC5cclxuXHRcdC8vIElmIG5vdCBzcGVjaWZpZWQgYW5kIGF0IGxlYXN0IG9uZSBgR3JpZExheWVyYCBvciBgVGlsZUxheWVyYCBpcyBpbiB0aGUgbWFwLFxyXG5cdFx0Ly8gdGhlIGxvd2VzdCBvZiB0aGVpciBgbWluWm9vbWAgb3B0aW9ucyB3aWxsIGJlIHVzZWQgaW5zdGVhZC5cclxuXHRcdG1pblpvb206IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9ICpcclxuXHRcdC8vIE1heGltdW0gem9vbSBsZXZlbCBvZiB0aGUgbWFwLlxyXG5cdFx0Ly8gSWYgbm90IHNwZWNpZmllZCBhbmQgYXQgbGVhc3Qgb25lIGBHcmlkTGF5ZXJgIG9yIGBUaWxlTGF5ZXJgIGlzIGluIHRoZSBtYXAsXHJcblx0XHQvLyB0aGUgaGlnaGVzdCBvZiB0aGVpciBgbWF4Wm9vbWAgb3B0aW9ucyB3aWxsIGJlIHVzZWQgaW5zdGVhZC5cclxuXHRcdG1heFpvb206IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGxheWVyczogTGF5ZXJbXSA9IFtdXHJcblx0XHQvLyBBcnJheSBvZiBsYXllcnMgdGhhdCB3aWxsIGJlIGFkZGVkIHRvIHRoZSBtYXAgaW5pdGlhbGx5XHJcblx0XHRsYXllcnM6IFtdLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWF4Qm91bmRzOiBMYXRMbmdCb3VuZHMgPSBudWxsXHJcblx0XHQvLyBXaGVuIHRoaXMgb3B0aW9uIGlzIHNldCwgdGhlIG1hcCByZXN0cmljdHMgdGhlIHZpZXcgdG8gdGhlIGdpdmVuXHJcblx0XHQvLyBnZW9ncmFwaGljYWwgYm91bmRzLCBib3VuY2luZyB0aGUgdXNlciBiYWNrIGlmIHRoZSB1c2VyIHRyaWVzIHRvIHBhblxyXG5cdFx0Ly8gb3V0c2lkZSB0aGUgdmlldy4gVG8gc2V0IHRoZSByZXN0cmljdGlvbiBkeW5hbWljYWxseSwgdXNlXHJcblx0XHQvLyBbYHNldE1heEJvdW5kc2BdKCNtYXAtc2V0bWF4Ym91bmRzKSBtZXRob2QuXHJcblx0XHRtYXhCb3VuZHM6IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJlbmRlcmVyOiBSZW5kZXJlciA9ICpcclxuXHRcdC8vIFRoZSBkZWZhdWx0IG1ldGhvZCBmb3IgZHJhd2luZyB2ZWN0b3IgbGF5ZXJzIG9uIHRoZSBtYXAuIGBMLlNWR2BcclxuXHRcdC8vIG9yIGBMLkNhbnZhc2AgYnkgZGVmYXVsdCBkZXBlbmRpbmcgb24gYnJvd3NlciBzdXBwb3J0LlxyXG5cdFx0cmVuZGVyZXI6IHVuZGVmaW5lZCxcclxuXHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gQW5pbWF0aW9uIE9wdGlvbnNcclxuXHRcdC8vIEBvcHRpb24gem9vbUFuaW1hdGlvbjogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIG1hcCB6b29tIGFuaW1hdGlvbiBpcyBlbmFibGVkLiBCeSBkZWZhdWx0IGl0J3MgZW5hYmxlZFxyXG5cdFx0Ly8gaW4gYWxsIGJyb3dzZXJzIHRoYXQgc3VwcG9ydCBDU1MzIFRyYW5zaXRpb25zIGV4Y2VwdCBBbmRyb2lkLlxyXG5cdFx0em9vbUFuaW1hdGlvbjogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21BbmltYXRpb25UaHJlc2hvbGQ6IE51bWJlciA9IDRcclxuXHRcdC8vIFdvbid0IGFuaW1hdGUgem9vbSBpZiB0aGUgem9vbSBkaWZmZXJlbmNlIGV4Y2VlZHMgdGhpcyB2YWx1ZS5cclxuXHRcdHpvb21BbmltYXRpb25UaHJlc2hvbGQ6IDQsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBmYWRlQW5pbWF0aW9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgdGlsZSBmYWRlIGFuaW1hdGlvbiBpcyBlbmFibGVkLiBCeSBkZWZhdWx0IGl0J3MgZW5hYmxlZFxyXG5cdFx0Ly8gaW4gYWxsIGJyb3dzZXJzIHRoYXQgc3VwcG9ydCBDU1MzIFRyYW5zaXRpb25zIGV4Y2VwdCBBbmRyb2lkLlxyXG5cdFx0ZmFkZUFuaW1hdGlvbjogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1hcmtlclpvb21BbmltYXRpb246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIG1hcmtlcnMgYW5pbWF0ZSB0aGVpciB6b29tIHdpdGggdGhlIHpvb20gYW5pbWF0aW9uLCBpZiBkaXNhYmxlZFxyXG5cdFx0Ly8gdGhleSB3aWxsIGRpc2FwcGVhciBmb3IgdGhlIGxlbmd0aCBvZiB0aGUgYW5pbWF0aW9uLiBCeSBkZWZhdWx0IGl0J3NcclxuXHRcdC8vIGVuYWJsZWQgaW4gYWxsIGJyb3dzZXJzIHRoYXQgc3VwcG9ydCBDU1MzIFRyYW5zaXRpb25zIGV4Y2VwdCBBbmRyb2lkLlxyXG5cdFx0bWFya2VyWm9vbUFuaW1hdGlvbjogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRyYW5zZm9ybTNETGltaXQ6IE51bWJlciA9IDJeMjNcclxuXHRcdC8vIERlZmluZXMgdGhlIG1heGltdW0gc2l6ZSBvZiBhIENTUyB0cmFuc2xhdGlvbiB0cmFuc2Zvcm0uIFRoZSBkZWZhdWx0XHJcblx0XHQvLyB2YWx1ZSBzaG91bGQgbm90IGJlIGNoYW5nZWQgdW5sZXNzIGEgd2ViIGJyb3dzZXIgcG9zaXRpb25zIGxheWVycyBpblxyXG5cdFx0Ly8gdGhlIHdyb25nIHBsYWNlIGFmdGVyIGRvaW5nIGEgbGFyZ2UgYHBhbkJ5YC5cclxuXHRcdHRyYW5zZm9ybTNETGltaXQ6IDgzODg2MDgsIC8vIFByZWNpc2lvbiBsaW1pdCBvZiBhIDMyLWJpdCBmbG9hdFxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcclxuXHRcdC8vIEBvcHRpb24gem9vbVNuYXA6IE51bWJlciA9IDFcclxuXHRcdC8vIEZvcmNlcyB0aGUgbWFwJ3Mgem9vbSBsZXZlbCB0byBhbHdheXMgYmUgYSBtdWx0aXBsZSBvZiB0aGlzLCBwYXJ0aWN1bGFybHlcclxuXHRcdC8vIHJpZ2h0IGFmdGVyIGEgW2BmaXRCb3VuZHMoKWBdKCNtYXAtZml0Ym91bmRzKSBvciBhIHBpbmNoLXpvb20uXHJcblx0XHQvLyBCeSBkZWZh
14775dWx0LCB0aGUgem9vbSBsZXZlbCBzbmFwcyB0byB0aGUgbmVhcmVzdCBpbnRlZ2VyOyBsb3dlciB2YWx1ZXNcclxuXHRcdC8vIChlLmcuIGAwLjVgIG9yIGAwLjFgKSBhbGxvdyBmb3IgZ3JlYXRlciBncmFudWxhcml0eS4gQSB2YWx1ZSBvZiBgMGBcclxuXHRcdC8vIG1lYW5zIHRoZSB6b29tIGxldmVsIHdpbGwgbm90IGJlIHNuYXBwZWQgYWZ0ZXIgYGZpdEJvdW5kc2Agb3IgYSBwaW5jaC16b29tLlxyXG5cdFx0em9vbVNuYXA6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tRGVsdGE6IE51bWJlciA9IDFcclxuXHRcdC8vIENvbnRyb2xzIGhvdyBtdWNoIHRoZSBtYXAncyB6b29tIGxldmVsIHdpbGwgY2hhbmdlIGFmdGVyIGFcclxuXHRcdC8vIFtgem9vbUluKClgXSgjbWFwLXpvb21pbiksIFtgem9vbU91dCgpYF0oI21hcC16b29tb3V0KSwgcHJlc3NpbmcgYCtgXHJcblx0XHQvLyBvciBgLWAgb24gdGhlIGtleWJvYXJkLCBvciB1c2luZyB0aGUgW3pvb20gY29udHJvbHNdKCNjb250cm9sLXpvb20pLlxyXG5cdFx0Ly8gVmFsdWVzIHNtYWxsZXIgdGhhbiBgMWAgKGUuZy4gYDAuNWApIGFsbG93IGZvciBncmVhdGVyIGdyYW51bGFyaXR5LlxyXG5cdFx0em9vbURlbHRhOiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhY2tSZXNpemU6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSBtYXAgYXV0b21hdGljYWxseSBoYW5kbGVzIGJyb3dzZXIgd2luZG93IHJlc2l6ZSB0byB1cGRhdGUgaXRzZWxmLlxyXG5cdFx0dHJhY2tSZXNpemU6IHRydWVcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAoaWQsIG9wdGlvbnMpIHsgLy8gKEhUTUxFbGVtZW50IG9yIFN0cmluZywgT2JqZWN0KVxyXG5cdFx0b3B0aW9ucyA9IHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0Ly8gTWFrZSBzdXJlIHRvIGFzc2lnbiBpbnRlcm5hbCBmbGFncyBhdCB0aGUgYmVnaW5uaW5nLFxyXG5cdFx0Ly8gdG8gYXZvaWQgaW5jb25zaXN0ZW50IHN0YXRlIGluIHNvbWUgZWRnZSBjYXNlcy5cclxuXHRcdHRoaXMuX2hhbmRsZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXllcnMgPSB7fTtcclxuXHRcdHRoaXMuX3pvb21Cb3VuZExheWVycyA9IHt9O1xyXG5cdFx0dGhpcy5fc2l6ZUNoYW5nZWQgPSB0cnVlO1xyXG5cclxuXHRcdHRoaXMuX2luaXRDb250YWluZXIoaWQpO1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cclxuXHRcdC8vIGhhY2sgZm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzE5ODBcclxuXHRcdHRoaXMuX29uUmVzaXplID0gYmluZCh0aGlzLl9vblJlc2l6ZSwgdGhpcyk7XHJcblxyXG5cdFx0dGhpcy5faW5pdEV2ZW50cygpO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLm1heEJvdW5kcykge1xyXG5cdFx0XHR0aGlzLnNldE1heEJvdW5kcyhvcHRpb25zLm1heEJvdW5kcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuem9vbSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuX3pvb20gPSB0aGlzLl9saW1pdFpvb20ob3B0aW9ucy56b29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAob3B0aW9ucy5jZW50ZXIgJiYgb3B0aW9ucy56b29tICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dGhpcy5zZXRWaWV3KHRvTGF0TG5nKG9wdGlvbnMuY2VudGVyKSwgb3B0aW9ucy56b29tLCB7cmVzZXQ6IHRydWV9KTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLmNhbGxJbml0SG9va3MoKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIG9uIGJyb3dzZXJzIHdpdGhvdXQgaGFyZHdhcmUtYWNjZWxlcmF0ZWQgdHJhbnNpdGlvbnMgb3Igb2xkIEFuZHJvaWQvT3BlcmFcclxuXHRcdHRoaXMuX3pvb21BbmltYXRlZCA9IFRSQU5TSVRJT04gJiYgYW55M2QgJiYgIW1vYmlsZU9wZXJhICYmXHJcblx0XHRcdFx0dGhpcy5vcHRpb25zLnpvb21BbmltYXRpb247XHJcblxyXG5cdFx0Ly8gem9vbSB0cmFuc2l0aW9ucyBydW4gd2l0aCB0aGUgc2FtZSBkdXJhdGlvbiBmb3IgYWxsIGxheWVycywgc28gaWYgb25lIG9mIHRyYW5zaXRpb25lbmQgZXZlbnRzXHJcblx0XHQvLyBoYXBwZW5zIGFmdGVyIHN0YXJ0aW5nIHpvb20gYW5pbWF0aW9uIChwcm9wYWdhdGluZyB0byB0aGUgbWFwIHBhbmUpLCB3ZSBrbm93IHRoYXQgaXQgZW5kZWQgZ2xvYmFsbHlcclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0dGhpcy5fY3JlYXRlQW5pbVByb3h5KCk7XHJcblx0XHRcdG9uKHRoaXMuX3Byb3h5LCBUUkFOU0lUSU9OX0VORCwgdGhpcy5fY2F0Y2hUcmFuc2l0aW9uRW5kLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9hZGRMYXllcnModGhpcy5vcHRpb25zLmxheWVycyk7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIG1vZGlmeWluZyBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRWaWV3KGNlbnRlcjogTGF0TG5nLCB6b29tOiBOdW1iZXIsIG9wdGlvbnM/OiBab29tL3BhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHZpZXcgb2YgdGhlIG1hcCAoZ2VvZ3JhcGh
14775pY2FsIGNlbnRlciBhbmQgem9vbSkgd2l0aCB0aGUgZ2l2ZW5cclxuXHQvLyBhbmltYXRpb24gb3B0aW9ucy5cclxuXHRzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0em9vbSA9IHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX3pvb20gOiB0aGlzLl9saW1pdFpvb20oem9vbSk7XHJcblx0XHRjZW50ZXIgPSB0aGlzLl9saW1pdENlbnRlcih0b0xhdExuZyhjZW50ZXIpLCB6b29tLCB0aGlzLm9wdGlvbnMubWF4Qm91bmRzKTtcclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cclxuXHRcdHRoaXMuX3N0b3AoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkICYmICFvcHRpb25zLnJlc2V0ICYmIG9wdGlvbnMgIT09IHRydWUpIHtcclxuXHJcblx0XHRcdGlmIChvcHRpb25zLmFuaW1hdGUgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRcdG9wdGlvbnMuem9vbSA9IGV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlfSwgb3B0aW9ucy56b29tKTtcclxuXHRcdFx0XHRvcHRpb25zLnBhbiA9IGV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlLCBkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbn0sIG9wdGlvbnMucGFuKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gdHJ5IGFuaW1hdGluZyBwYW4gb3Igem9vbVxyXG5cdFx0XHR2YXIgbW92ZWQgPSAodGhpcy5fem9vbSAhPT0gem9vbSkgP1xyXG5cdFx0XHRcdHRoaXMuX3RyeUFuaW1hdGVkWm9vbSAmJiB0aGlzLl90cnlBbmltYXRlZFpvb20oY2VudGVyLCB6b29tLCBvcHRpb25zLnpvb20pIDpcclxuXHRcdFx0XHR0aGlzLl90cnlBbmltYXRlZFBhbihjZW50ZXIsIG9wdGlvbnMucGFuKTtcclxuXHJcblx0XHRcdGlmIChtb3ZlZCkge1xyXG5cdFx0XHRcdC8vIHByZXZlbnQgcmVzaXplIGhhbmRsZXIgY2FsbCwgdGhlIHZpZXcgd2lsbCByZWZyZXNoIGFmdGVyIGFuaW1hdGlvbiBhbnl3YXlcclxuXHRcdFx0XHRjbGVhclRpbWVvdXQodGhpcy5fc2l6ZVRpbWVyKTtcclxuXHRcdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGlvbiBkaWRuJ3Qgc3RhcnQsIGp1c3QgcmVzZXQgdGhlIG1hcCB2aWV3XHJcblx0XHR0aGlzLl9yZXNldFZpZXcoY2VudGVyLCB6b29tKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpvb20oem9vbTogTnVtYmVyLCBvcHRpb25zPzogWm9vbS9wYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB6b29tIG9mIHRoZSBtYXAuXHJcblx0c2V0Wm9vbTogZnVuY3Rpb24gKHpvb20sIG9wdGlvbnMpIHtcclxuXHRcdGlmICghdGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuX3pvb20gPSB6b29tO1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcodGhpcy5nZXRDZW50ZXIoKSwgem9vbSwge3pvb206IG9wdGlvbnN9KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHpvb21JbihkZWx0YT86IE51bWJlciwgb3B0aW9ucz86IFpvb20gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBJbmNyZWFzZXMgdGhlIHpvb20gb2YgdGhlIG1hcCBieSBgZGVsdGFgIChbYHpvb21EZWx0YWBdKCNtYXAtem9vbWRlbHRhKSBieSBkZWZh
14775dWx0KS5cclxuXHR6b29tSW46IGZ1bmN0aW9uIChkZWx0YSwgb3B0aW9ucykge1xyXG5cdFx0ZGVsdGEgPSBkZWx0YSB8fCAoYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gKyBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tT3V0KGRlbHRhPzogTnVtYmVyLCBvcHRpb25zPzogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIERlY3JlYXNlcyB0aGUgem9vbSBvZiB0aGUgbWFwIGJ5IGBkZWx0YWAgKFtgem9vbURlbHRhYF0oI21hcC16b29tZGVsdGEpIGJ5IGRlZmF1bHQpLlxyXG5cdHpvb21PdXQ6IGZ1bmN0aW9uIChkZWx0YSwgb3B0aW9ucykge1xyXG5cdFx0ZGVsdGEgPSBkZWx0YSB8fCAoYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gLSBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRab29tQXJvdW5kKGxhdGxuZzogTGF0TG5nLCB6b29tOiBOdW1iZXIsIG9wdGlvbnM6IFpvb20gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBab29tcyB0aGUgbWFwIHdoaWxlIGtlZXBpbmcgYSBzcGVjaWZpZWQgZ2VvZ3JhcGhpY2FsIHBvaW50IG9uIHRoZSBtYXBcclxuXHQvLyBzdGF0aW9uYXJ5IChlLmcuIHVzZWQgaW50ZXJuYWxseSBmb3Igc2Nyb2xsIHpvb20gYW5kIGRvdWJsZS1jbGljayB6b29tKS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIHNldFpvb21Bcm91bmQob2Zmc2V0OiBQb2ludCwgem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gWm9vbXMgdGhlIG1hcCB3aGlsZSBrZWVwaW5nIGEgc3BlY2lmaWVkIHBpeGVsIG9uIHRoZSBtYXAgKHJlbGF0aXZlIHRvIHRoZSB0b3AtbGVmdCBjb3JuZXIpIHN0YXRpb25hcnkuXHJcblx0c2V0Wm9vbUFyb3VuZDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgb3B0aW9ucykge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5nZXRab29tU2NhbGUoem9vbSksXHJcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcclxuXHRcdCAgICBjb250YWluZXJQb2ludCA9IGxhdGxuZyBpbnN0YW5jZW9mIFBvaW50ID8gbGF0bG5nIDogdGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZyksXHJcblxyXG5cdFx0ICAgIGNlbnRlck9mZnNldCA9IGNvbnRhaW5lclBvaW50LnN1YnRyYWN0KHZpZXdIYWxmKS5tdWx0aXBseUJ5KDEgLSAxIC8gc2NhbGUpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMuY29udGFpbmVyUG9pbnRUb0xhdExuZyh2aWV3SGFsZi5hZGQoY2VudGVyT2Zmc2V0KSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0VmlldyhuZXdDZW50ZXIsIHpvb20sIHt6b29tOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0X2dldEJvdW5kc0NlbnRlclpvb206IGZ1bmN0aW9uIChib3VuZHMsIG9wdGlvbnMpIHtcclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHRcdGJvdW5kcyA9IGJvdW5kcy5nZXRCb3VuZHMgPyBib3VuZHMuZ2V0Qm91bmRzKCkgOiB0b0xhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBwYWRkaW5nVEwgPSB0b1BvaW50KG9wdGlvbnMucGFkZGluZ1RvcExlZnQgfHwgb3B0aW9ucy5wYWRkaW5nIHx8IFswLCAwXSksXHJcblx0XHQgICAgcGFkZGluZ0JSID0gdG9Qb2ludChvcHRpb25zLnBhZGRpbmdCb3R0b21SaWdodCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHJcblx0XHQgICAgem9vbSA9IHRoaXMuZ2V0Qm91bmRzWm9vbShib3VuZHMsIGZhbHNlLCBwYWRkaW5nVEwuYWRkKHBhZGRpbmdCUikpO1xyXG5cclxuXHRcdHpvb20gPSAodHlwZW9mIG9wdGlvbnMubWF4Wm9vbSA9PT0gJ251bWJlcicpID8gTWF0aC5taW4ob3B0aW9ucy5tYXhab29tLCB6b29tKSA6IHpvb207XHJcblxyXG5cdFx0aWYgKHpvb20gPT09IEluZmluaXR5KSB7XHJcblx0XHRcdHJldHVybiB7XHJcblx0XHRcdFx0Y2VudGVyOiBib3VuZHMuZ2V0Q2VudGVyKCksXHJcblx0XHRcdFx0em9vbTogem9vbVxyXG5cdFx0XHR9O1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwYWRkaW5nT2Zmc2V0ID0gcGFkZGluZ0JSLnN1YnRyYWN0KHBhZGRpbmdUTCkuZGl2aWRlQnkoMiksXHJcblxyXG5cdFx0ICAgIHN3UG9pbnQgPSB0aGlzLnByb2plY3QoYm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKSxcclxuXHRcdCAgICBuZVBvaW50ID0gdGhpcy5wcm9qZWN0KGJvdW5kcy5nZXROb3J0aEVhc3QoKSwgem9vbSksXHJcblx0XHQgICAgY2VudGVyID0gdGhpcy51bnByb2plY3Qoc3dQb2ludC5hZGQobmVQb2ludCkuZGl2aWRlQnkoMikuYWRkKHBhZGRpbmdPZmZzZXQpLCB6b29tKTtcclxuXHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHRjZW50ZXI6IGNlbnRlcixcclxuXHRcdFx0em9vbTogem9vbVxyXG5cdFx0fTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZpdEJvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcywgb3B0aW9ucz86IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IGNvbnRhaW5zIHRoZSBnaXZlbiBnZW9ncmFwaGljYWwgYm91bmRzIHdpdGggdGhlXHJcblx0Ly8gbWF4aW11bSB6b29tIGxldmVsIHBvc3NpYmxlLlxyXG5cdGZpdEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cclxuXHRcdGJvdW5kcyA9IHRvTGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMuaXNWYWxpZCgpKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignQm91bmRzIGFyZSBub3QgdmFsaWQuJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcodGFyZ2V0LmNlbnRlciwgdGFyZ2V0Lnpvb20sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZml0V29ybGQob3B0aW9ucz86IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IG1vc3RseSBjb250YWlucyB0aGUgd2hvbGUgd29ybGQgd2l0aCB0aGUgbWF4aW11bVxyXG5cdC8vIHpvb20gbGV2ZWwgcG9zc2libGUuXHJcblx0Zml0V29ybGQ6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5maXRCb3VuZHMoW1stOTAsIC0xODBdLCBbOTAsIDE4MF1dLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhblRvKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIGEgZ2l2ZW4gY2VudGVyLlxyXG5cdHBhblRvOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7IC8vIChMYXRMbmcpXHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KGNlbnRlciwgdGhpcy5fem9vbSwge3Bhbjogb3B0aW9uc30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuQnkob2Zmc2V0OiBQb2ludCwgb3B0aW9ucz86IFBhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFBhbnMgdGhlIG1hcCBieSBhIGdpdmVuIG51bWJlciBvZiBwaXhlbHMgKGFuaW1hdGVkKS5cclxuXHRwYW5CeTogZnVuY3Rpb24gKG9mZnNldCwgb3B0aW9ucykge1xyXG5cdFx0b2Zmc2V0ID0gdG9Qb2ludChvZmZzZXQpLnJvdW5kKCk7XHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblx0XHQvLyBJZiB3ZSBwYW4gdG9vIGZhciwgQ2hyb21lIGdldHMgaXNzdWVzIHdpdGggdGlsZXNcclxuXHRcdC8vIGFuZCBtYWtlcyB0aGVtIGRpc2FwcGVhciBvciBhcHBlYXIgaW4gdGhlIHdyb25nIHBsYWNlIChzbGlnaHRseSBvZmZzZXQpICMyNjAyXHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlICE9PSB0cnVlICYmICF0aGlzLmdldFNpemUoKS5jb250YWlucyhvZmZzZXQpKSB7XHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLnVucHJvamVjdCh0aGlzLnByb2plY3QodGhpcy5nZXRDZW50ZXIoKSkuYWRkKG9mZnNldCkpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHRcdHJldHVybi
14775B0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltID0gbmV3IFBvc0FuaW1hdGlvbigpO1xyXG5cclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5vbih7XHJcblx0XHRcdFx0J3N0ZXAnOiB0aGlzLl9vblBhblRyYW5zaXRpb25TdGVwLFxyXG5cdFx0XHRcdCdlbmQnOiB0aGlzLl9vblBhblRyYW5zaXRpb25FbmRcclxuXHRcdFx0fSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gZG9uJ3QgZmlyZSBtb3Zlc3RhcnQgaWYgYW5pbWF0aW5nIGluZXJ0aWFcclxuXHRcdGlmICghb3B0aW9ucy5ub01vdmVTdGFydCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmVzdGFydCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGUgcGFuIHVubGVzcyBhbmltYXRlOiBmYWxzZSBzcGVjaWZpZWRcclxuXHRcdGlmIChvcHRpb25zLmFuaW1hdGUgIT09IGZhbHNlKSB7XHJcblx0XHRcdGFkZENsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXBhbi1hbmltJyk7XHJcblxyXG5cdFx0XHR2YXIgbmV3UG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpLnN1YnRyYWN0KG9mZnNldCkucm91bmQoKTtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5ydW4odGhpcy5fbWFwUGFuZSwgbmV3UG9zLCBvcHRpb25zLmR1cmF0aW9uIHx8IDAuMjUsIG9wdGlvbnMuZWFzZUxpbmVhcml0eSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLl9yYXdQYW5CeShvZmZzZXQpO1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmUnKS5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmbHlUbyhsYXRsbmc6IExhdExuZywgem9vbT86IE51bWJlciwgb3B0aW9ucz86IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIChnZW9ncmFwaGljYWwgY2VudGVyIGFuZCB6b29tKSBwZXJmb3JtaW5nIGEgc21vb3RoXHJcblx0Ly8gcGFuLXpvb20gYW5pbWF0aW9uLlxyXG5cdGZseVRvOiBmdW5jdGlvbiAodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlID09PSBmYWxzZSB8fCAhYW55M2QpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuc2V0Vmlldyh0YXJnZXRDZW50ZXIsIHRhcmdldFpvb20sIG9wdGlvbnMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3N0b3AoKTtcclxuXHJcblx0XHR2YXIgZnJvbSA9IHRoaXMucHJvamVjdCh0aGlzLmdldENlbnRlcigpKSxcclxuXHRcdCAgICB0byA9IHRoaXMucHJvamVjdCh0YXJnZXRDZW50ZXIpLFxyXG5cdFx0ICAgIHNpemUgPSB0aGlzLmdldFNpemUoKSxcclxuXHRcdCAgICBzdGFydFpvb20gPSB0aGlzLl96b29tO1xyXG5cclxuXHRcdHRhcmdldENlbnRlciA9IHRvTGF0TG5nKHRhcmdldENlbnRlcik7XHJcblx0XHR0YXJnZXRab29tID0gdGFyZ2V0Wm9vbSA9PT0gdW5kZWZpbmVkID8gc3RhcnRab29tIDogdGFyZ2V0Wm9vbTtcclxuXHJcblx0XHR2YXIgdzAgPSBNYXRoLm1heChzaXplLngsIHNpemUueSksXHJcblx0XHQgICAgdzEgPSB3MCAqIHRoaXMuZ2V0Wm9vbVNjYWxlKHN0YXJ0Wm9vbSwgdGFyZ2V0Wm9vbSksXHJcblx0XHQgICAgdTEgPSAodG8uZGlzdGFuY2VUbyhmcm9tKSkgfHwgMSxcclxuXHRcdCAgICByaG8gPSAxLjQyLFxyXG5cdFx0ICAgIHJobzIgPSByaG8gKiByaG87XHJcblxyXG5cdFx0ZnVuY3Rpb24gcihpKSB7XHJcblx0XHRcdHZhciBzMSA9IGkgPyAtMSA6IDEsXHJcblx0XHRcdCAgICBzMiA9IGkgPyB3MSA6IHcwLFxyXG5cdFx0XHQgICAgdDEgPSB3MSAqIHcxIC0gdzAgKiB3MCArIHMxICogcmhvMiAqIHJobzIgKiB1MSAqIHUxLFxyXG5cdFx0XHQgICAgYjEgPSAyICogczIgKiByaG8yICogdTEsXHJcblx0XHRcdCAgICBiID0gdDEgLyBiMSxcclxuXHRcdFx0ICAgIHNxID0gTWF0aC5zcXJ0KGIgKiBiICsgMSkgLSBiO1xyXG5cclxuXHRcdFx0ICAgIC8vIHdvcmthcm91bmQgZm9yIGZsb2F0aW5nIHBvaW50IHByZWNpc2lvbiBidWcgd2hlbiBzcSA9IDAsIGxvZyA9IC1JbmZpbml0ZSxcclxuXHRcdFx0ICAgIC8vIHRodXMgdHJpZ2dlcmluZyBhbiBpbmZpbml0ZSBsb29wIGluIGZseVRvXHJcblx0XHRcdCAgICB2YXIgbG9nID0gc3EgPCAwLjAwMDAwMDAwMSA/IC0xOCA6IE1hdGgubG9nKHNxKTtcclxuXHJcblx0XHRcdHJldHVybiBsb2c7XHJcblx0XHR9XHJcblxyXG5cdFx0ZnVuY3Rpb24gc2luaChuKSB7IHJldHVybiAoTWF0aC5leHAobikgLSBNYXRoLmV4cCgtbikpIC8gMjsgfVxyXG5cdFx0ZnVuY3Rpb24gY29zaChuKSB7IHJldHVybiAoTWF0aC5leHAobikgKyBNYXRoLmV4cCgtbikpIC8gMjsgfVxyXG5cdFx0ZnVuY3Rpb24gdGFuaChuKSB7IHJldHVybiBzaW5oKG4pIC8gY29zaChuKTsgfVxyXG5cclxuXHRcdHZhciByMCA9IHIoMCk7XHJcblxyXG5cdFx0ZnVuY3Rpb24gdyhzKSB7IHJldHVybiB3MCAqIChjb3NoKHIwKSAvIGNvc2gocjAgKyByaG8gKiBzKSk7IH1cclxuXHRcdGZ1bmN0aW9uIHUocykgeyByZXR1cm4gdzAgKiAoY29zaChyMCkgKiB0YW5oKHIwICsgcmhvICogcykgLSBzaW5oKHIwKSkgLyByaG8yOyB9XHJcblxyXG5cdFx0ZnVuY3Rpb24gZWFzZU91dCh0KSB7IHJldHVybiAxIC0gTWF0aC5wb3coMSAtIHQsIDEuNSk7IH1cclxuXHJcblx0XHR2YXIgc3RhcnQgPSBEYXRlLm5vdygpLFxyXG5cdFx0ICAgIFMgPSAocigxKSAtIHIwKSAvIHJobyxcclxuXHRcdCAgICBkdXJhdGlvbiA9IG9wdGlvbnMuZHVyYXRpb24gPyAxMDAwICogb3B0aW9ucy5kdXJhdGlvbiA6IDEwMDAgKiBTICogMC44O1xyXG5cclxuXHRcdGZ1bmN0aW9uIGZyYW1lKCkge1xyXG5cdFx0XHR2YXIgdCA9IChEYXRlLm5vdygpIC0gc3RhcnQpIC8gZHVyYXRpb24sXHJcblx0XHRcdCAgICBzID0gZWFzZU91dCh0KSAqIFM7XHJcblxyXG5cdFx0XHRpZiAodCA8PSAxKSB7XHJcblx0XHRcdFx0dGhpcy5fZmx5VG9GcmFtZSA9IHJlcXVlc3RBbmltRnJhbWUoZnJhbWUsIHRoaXMpO1xyXG5cclxuXHRcdFx0XHR0aGlzLl9tb3ZlKFxyXG5cdFx0XHRcdFx0dGhpcy51bnByb2plY3QoZnJvbS5hZGQodG8uc3VidHJhY3QoZnJvbSkubXVsdGlwbHlCeSh1KHMpIC8gdTEpKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHRoaXMuZ2V0U2NhbGVab29tKHcwIC8gdyhzKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHtmbHlUbzogdHJ1ZX0pO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzXHJcblx0XHRcdFx0XHQuX21vdmUodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tKVxyXG5cdFx0XHRcdFx0Ll9tb3ZlRW5kKHRydWUpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbW92ZVN0YXJ0KHRydWUsIG9wdGlvbnMubm9Nb3ZlU3RhcnQpO1xyXG5cclxuXHRcdGZyYW1lLmNhbGwodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZseVRvQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIHdpdGggYSBzbW9vdGggYW5pbWF0aW9uIGxpa2UgW2BmbHlUb2BdKCNtYXAtZmx5dG8pLFxyXG5cdC8vIGJ1dCB0YWtlcyBhIGJvdW5kcyBwYXJhbWV0ZXIgbGlrZSBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuXHRmbHlUb0JvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybi
14775B0aGlzLmZseVRvKHRhcmdldC5jZW50ZXIsIHRhcmdldC56b29tLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1heEJvdW5kcyhib3VuZHM6IEJvdW5kcyk6IHRoaXNcclxuXHQvLyBSZXN0cmljdHMgdGhlIG1hcCB2aWV3IHRvIHRoZSBnaXZlbiBib3VuZHMgKHNlZSB0aGUgW21heEJvdW5kc10oI21hcC1tYXhib3VuZHMpIG9wdGlvbikuXHJcblx0c2V0TWF4Qm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRib3VuZHMgPSB0b0xhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdGlmICghYm91bmRzLmlzVmFsaWQoKSkge1xyXG5cdFx0XHR0aGlzLm9wdGlvbnMubWF4Qm91bmRzID0gbnVsbDtcclxuXHRcdFx0cmV0dXJuIHRoaXMub2ZmKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHRcdH0gZWxzZSBpZiAodGhpcy5vcHRpb25zLm1heEJvdW5kcykge1xyXG5cdFx0XHR0aGlzLm9mZignbW92ZWVuZCcsIHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLm1heEJvdW5kcyA9IGJvdW5kcztcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLm9uKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1pblpvb20oem9vbTogTnVtYmVyKTogdGh
14775pc1xyXG5cdC8vIFNldHMgdGhlIGxvd2VyIGxpbWl0IGZvciB0aGUgYXZhaWxhYmxlIHpvb20gbGV2ZWxzIChzZWUgdGhlIFttaW5ab29tXSgjbWFwLW1pbnpvb20pIG9wdGlvbikuXHJcblx0c2V0TWluWm9vbTogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHZhciBvbGRab29tID0gdGhpcy5vcHRpb25zLm1pblpvb207XHJcblx0XHR0aGlzLm9wdGlvbnMubWluWm9vbSA9IHpvb207XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCAmJiBvbGRab29tICE9PSB6b29tKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnem9vbWxldmVsc2NoYW5nZScpO1xyXG5cclxuXHRcdFx0aWYgKHRoaXMuZ2V0Wm9vbSgpIDwgdGhpcy5vcHRpb25zLm1pblpvb20pIHtcclxuXHRcdFx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHpvb20pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRNYXhab29tKHpvb206IE51bWJlcik6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB1cHBlciBsaW1pdCBmb3IgdGhlIGF2YWlsYWJsZSB6b29tIGxldmVscyAoc2VlIHRoZSBbbWF4Wm9vbV0oI21hcC1tYXh6b29tKSBvcHRpb24pLlxyXG5cdHNldE1heFpvb206IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHR2YXIgb2xkWm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xyXG5cdFx0dGhpcy5vcHRpb25zLm1heFpvb20gPSB6b29tO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgb2xkWm9vbSAhPT0gem9vbSkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21sZXZlbHNjaGFuZ2UnKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLmdldFpvb20oKSA+IHRoaXMub3B0aW9ucy5tYXhab29tKSB7XHJcblx0XHRcdFx0cmV0dXJuIHRoaXMuc2V0Wm9vbSh6b29tKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuSW5zaWRlQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIHRoZSBjbG9zZXN0IHZpZXcgdGhhdCB3b3VsZCBsaWUgaW5zaWRlIHRoZSBnaXZlbiBib3VuZHMgKGlmIGl0J3Mgbm90IGFscmVhZHkpLCBjb250cm9sbGluZyB0aGUgYW5pbWF0aW9uIHVzaW5nIHRoZSBvcHRpb25zIHNwZWNpZmljLCBpZiBhbnkuXHJcblx0cGFuSW5zaWRlQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblx0XHR0aGlzLl9lbmZvcmNpbmdCb3VuZHMgPSB0cnVlO1xyXG5cdFx0dmFyIGNlbnRlciA9IHRoaXMuZ2V0Q2VudGVyKCksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gdGhpcy5fbGltaXRDZW50ZXIoY2VudGVyLCB0aGlzLl96b29tLCB0b0xhdExuZ0JvdW5kcyhib3VuZHMpKTtcclxuXHJcblx0XHRpZiAoIWNlbnRlci5lcXVhbHMobmV3Q2VudGVyKSkge1xyXG5cdFx0XHR0aGlzLnBhblRvKG5ld0NlbnRlciwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fZW5mb3JjaW5nQm91bmRzID0gZmFsc2U7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhbkluc2lkZShsYXRsbmc6IExhdExuZywgb3B0aW9ucz86IG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRoZSBtaW5pbXVtIGFtb3VudCB0byBtYWtlIHRoZSBgbGF0bG5nYCB2aXNpYmxlLiBVc2VcclxuXHQvLyBgcGFkZGluZ2AsIGBwYWRkaW5nVG9wTGVmdGAgYW5kIGBwYWRkaW5nVG9wUmlnaHRgIG9wdGlvbnMgdG8gZml0XHJcblx0Ly8gdGhlIGRpc3BsYXkgdG8gbW9yZSByZXN0cmljdGVkIGJvdW5kcywgbGlrZSBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuXHQvLyBJZiBgbGF0bG5nYCBpcyBhbHJlYWR5IHdpdGhpbiB0aGUgKG9wdGlvbmFsbHkgcGFkZGVkKSBkaXNwbGF5IGJvdW5kcyxcclxuXHQvLyB0aGUgbWFwIHdpbGwgbm90IGJlIHBhbm5lZC5cclxuXHRwYW5JbnNpZGU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cclxuXHRcdHZhciBwYWRkaW5nVEwgPSB0b1BvaW50KG9wdGlvbnMucGFkZGluZ1RvcExlZnQgfHwgb3B0aW9ucy5wYWRkaW5nIHx8IFswLCAwXSksXHJcblx0XHQgICAgcGFkZGluZ0JSID0gdG9Qb2ludChvcHRpb25zLnBhZGRpbmdCb3R0b21SaWdodCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHRcdCAgICBjZW50ZXIgPSB0aGlzLmdldENlbnRlcigpLFxyXG5cdFx0ICAgIHBpeGVsQ2VudGVyID0gdGhpcy5wcm9qZWN0KGNlbnRlciksXHJcblx0XHQgICAgcGl4ZWxQb2ludCA9IHRoaXMucHJvamVjdChsYXRsbmcpLFxyXG5cdFx0ICAgIHBpeGVsQm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIGhhbGZQaXhlbEJvdW5kcyA9IHBpeGVsQm91bmRzLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcclxuXHRcdCAgICBwYWRkZWRCb3VuZHMgPSB0b0JvdW5kcyhbcGl4ZWxCb3VuZHMubWluLmFkZChwYWRkaW5nVEwpLCBwaXhlbEJvdW5kcy5tYXguc3VidHJhY3QocGFkZGluZ0JSKV0pO1xyXG5cclxuXHRcdGlmICghcGFkZGVkQm91bmRzLmNvbnRhaW5zKHBpeGVsUG9pbnQpKSB7XHJcblx0XHRcdHRoaXMuX2VuZm9yY2luZ0JvdW5kcyA9IHRydWU7XHJcblx0XHRcdHZhciBkaWZmID0gcGl4ZWxDZW50ZXIuc3VidHJhY3QocGl4ZWxQb2ludCksXHJcblx0XHRcdCAgICBuZXdDZW50ZXIgPSB0b1BvaW50KHBpeGVsUG9pbnQueCArIGRpZmYueCwgcGl4ZWxQb2ludC55ICsgZGlmZi55KTtcclxuXHJcblx0XHRcdGlmIChwaXhlbFBvaW50LnggPCBwYWRkZWRCb3VuZHMubWluLnggfHwgcGl4ZWxQb2ludC54ID4gcGFkZGVkQm91bmRzLm1heC54KSB7XHJcblx0XHRcdFx0bmV3Q2VudGVyLnggPSBwaXhlbENlbnRlci54IC0gZGlmZi54O1xyXG5cdFx0XHRcdGlmIChkaWZmLnggPiAwKSB7XHJcblx0XHRcdFx0XHRuZXdDZW50ZXIueCArPSBoYWxmUGl4ZWxCb3VuZHMueCAtIHBhZGRpbmdUTC54O1xyXG5cdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRuZXdDZW50ZXIueCAtPSBoYWxmUGl4ZWxCb3VuZHMueCAtIHBhZGRpbmdCUi54O1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAocGl4ZWxQb2ludC55IDwgcGFkZGVkQm91bmRzLm1pbi55IHx8IHBpeGVsUG9pbnQueSA+IHBhZGRlZEJvdW5kcy5tYXgueSkge1xyXG5cdFx0XHRcdG5ld0NlbnRlci55ID0gcGl4ZWxDZW50ZXIueSAtIGRpZmYueTtcclxuXHRcdFx0XHRpZiAoZGlmZi55ID4gMCkge1xyXG5cdFx0XHRcdFx0bmV3Q2VudGVyLnkgKz0gaGFsZlBpeGVsQm91bmRzLnkgLSBwYWRkaW5nVEwueTtcclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0bmV3Q2VudGVyLnkgLT0gaGFsZlBpeGVsQm91bmRzLnkgLSBwYWRkaW5nQlIueTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5wYW5Ubyh0aGlzLnVucHJvamVjdChuZXdDZW50ZXIpLCBvcHRpb25zKTtcclxuXHRcdFx0dGhpcy5fZW5mb3JjaW5nQm91bmRzID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludmFsaWRhdGVTaXplKG9wdGlvbnM6IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQ2hlY2tzIGlmIHRoZSBtYXAgY29udGFpbmVyIHNpemUgY2hhbmdlZCBhbmQgdXBkYXRlcyB0aGUgbWFwIGlmIHNvIOKAlFxyXG5cdC8vIGNhbGwgaXQgYWZ0ZXIgeW91J3ZlIGNoYW5nZWQgdGhlIG1hcCBzaXplIGR5bmFtaWNhbGx5LCBhbHNvIGFuaW1hdGluZ1xyXG5cdC8vIHBhbiBieSBkZWZh
14775dWx0LiBJZiBgb3B0aW9ucy5wYW5gIGlzIGBmYWxzZWAsIHBhbm5pbmcgd2lsbCBub3Qgb2NjdXIuXHJcblx0Ly8gSWYgYG9wdGlvbnMuZGVib3VuY2VNb3ZlZW5kYCBpcyBgdHJ1ZWAsIGl0IHdpbGwgZGVsYXkgYG1vdmVlbmRgIGV2ZW50IHNvXHJcblx0Ly8gdGhhdCBpdCBkb2Vzbid0IGhhcHBlbiBvZnRlbiBldmVuIGlmIHRoZSBtZXRob2QgaXMgY2FsbGVkIG1hbnlcclxuXHQvLyB0aW1lcyBpbiBhIHJvdy5cclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBpbnZhbGlkYXRlU2l6ZShhbmltYXRlOiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIENoZWNrcyBpZiB0aGUgbWFwIGNvbnRhaW5lciBzaXplIGNoYW5nZWQgYW5kIHVwZGF0ZXMgdGhlIG1hcCBpZiBzbyDigJRcclxuXHQvLyBjYWxsIGl0IGFmdGVyIHlvdSd2ZSBjaGFuZ2VkIHRoZSBtYXAgc2l6ZSBkeW5hbWljYWxseSwgYWxzbyBhbmltYXRpbmdcclxuXHQvLyBwYW4gYnkgZGVmYXVsdC5cclxuXHRpbnZhbGlkYXRlU2l6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdGlmICghdGhpcy5fbG9hZGVkKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0b3B0aW9ucyA9IGV4dGVuZCh7XHJcblx0XHRcdGFuaW1hdGU6IGZhbHNlLFxyXG5cdFx0XHRwYW46IHRydWVcclxuXHRcdH0sIG9wdGlvbnMgPT09IHRydWUgPyB7YW5pbWF0ZTogdHJ1ZX0gOiBvcHRpb25zKTtcclxuXHJcblx0XHR2YXIgb2xkU2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpO1xyXG5cdFx0dGhpcy5fc2l6ZUNoYW5nZWQgPSB0cnVlO1xyXG5cdFx0dGhpcy5fbGFzdENlbnRlciA9IG51bGw7XHJcblxyXG5cdFx0dmFyIG5ld1NpemUgPSB0aGlzLmdldFNpemUoKSxcclxuXHRcdCAgICBvbGRDZW50ZXIgPSBvbGRTaXplLmRpdmlkZUJ5KDIpLnJvdW5kKCksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gbmV3U2l6ZS5kaXZpZGVCeSgyKS5yb3VuZCgpLFxyXG5cdFx0ICAgIG9mZnNldCA9IG9sZENlbnRlci5zdWJ0cmFjdChuZXdDZW50ZXIpO1xyXG5cclxuXHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAmJiBvcHRpb25zLnBhbikge1xyXG5cdFx0XHR0aGlzLnBhbkJ5KG9mZnNldCk7XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0aWYgKG9wdGlvbnMucGFuKSB7XHJcblx0XHRcdFx0dGhpcy5fcmF3UGFuQnkob2Zmc2V0KTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5maXJlKCdtb3ZlJyk7XHJcblxyXG5cdFx0XHRpZiAob3B0aW9ucy5kZWJvdW5jZU1vdmVlbmQpIHtcclxuXHRcdFx0XHRjbGVhclRpbWVvdXQodGhpcy5fc2l6ZVRpbWVyKTtcclxuXHRcdFx0XHR0aGlzLl9zaXplVGltZXIgPSBzZXRUaW1lb3V0KGJpbmQodGhpcy5maXJlLCB0aGlzLCAnbW92ZWVuZCcpLCAyMDApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXMuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRcdC8vIEBldmVudCByZXNpemU6IFJlc2l6ZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgcmVzaXplZC5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ3Jlc2l6ZScsIHtcclxuXHRcdFx0b2xkU2l6ZTogb2xkU2l6ZSxcclxuXHRcdFx0bmV3U2l6ZTogbmV3U2l6ZVxyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgbW9kaWZ5aW5nIG1hcCBzdGF0ZVxyXG5cdC8vIEBtZXRob2Qgc3RvcCgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgdGhlIGN1cnJlbnRseSBydW5uaW5nIGBwYW5Ub2Agb3IgYGZseVRvYCBhbmltYXRpb24sIGlmIGFueS5cclxuXHRzdG9wOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnNldFpvb20odGhpcy5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcclxuXHRcdGlmICghdGhpcy5vcHRpb25zLnpvb21TbmFwKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc3RvcCgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEdlb2xvY2F0aW9uIG1ldGhvZHNcclxuXHQvLyBAbWV0aG9kIGxvY2F0ZShvcHRpb25zPzogTG9jYXRlIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gVHJpZXMgdG8gbG9jYXRlIHRoZSB1c2VyIHVzaW5nIHRoZSBHZW9sb2NhdGlvbiBBUEksIGZpcmluZyBhIFtgbG9jYXRpb25mb3VuZGBdKCNtYXAtbG9jYXRpb25mb3VuZClcclxuXHQvLyBldmVudCB3aXRoIGxvY2F0aW9uIGRhdGEgb24gc3VjY2VzcyBvciBhIFtgbG9jYXRpb25lcnJvcmBdKCNtYXAtbG9jYXRpb25lcnJvcikgZXZlbnQgb24gZmFpbHVy
14775ZSxcclxuXHQvLyBhbmQgb3B0aW9uYWxseSBzZXRzIHRoZSBtYXAgdmlldyB0byB0aGUgdXNlcidzIGxvY2F0aW9uIHdpdGggcmVzcGVjdCB0b1xyXG5cdC8vIGRldGVjdGlvbiBhY2N1cmFjeSAob3IgdG8gdGhlIHdvcmxkIHZpZXcgaWYgZ2VvbG9jYXRpb24gZmFpbGVkKS5cclxuXHQvLyBOb3RlIHRoYXQsIGlmIHlvdXIgcGFnZSBkb2Vzbid0IHVzZSBIVFRQUywgdGhpcyBtZXRob2Qgd2lsbCBmYWlsIGluXHJcblx0Ly8gbW9kZXJuIGJyb3dzZXJzIChbQ2hyb21lIDUwIGFuZCBuZXdlcl0oaHR0cHM6Ly9zaXRlcy5nb29nbGUuY29tL2EvY2hyb21pdW0ub3JnL2Rldi9Ib21lL2Nocm9taXVtLXNlY3VyaXR5L2RlcHJlY2F0aW5nLXBvd2VyZnVsLWZlYXR1cmVzLW9uLWluc2VjdXJlLW9yaWdpbnMpKVxyXG5cdC8vIFNlZSBgTG9jYXRlIG9wdGlvbnNgIGZvciBtb3JlIGRldGFpbHMuXHJcblx0bG9jYXRlOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSB0aGlzLl9sb2NhdGVPcHRpb25zID0gZXh0ZW5kKHtcclxuXHRcdFx0dGltZW91dDogMTAwMDAsXHJcblx0XHRcdHdhdGNoOiBmYWxzZVxyXG5cdFx0XHQvLyBzZXRWaWV3OiBmYWxzZVxyXG5cdFx0XHQvLyBtYXhab29tOiA8TnVtYmVyPlxyXG5cdFx0XHQvLyBtYXhpbXVtQWdlOiAwXHJcblx0XHRcdC8vIGVuYWJsZUhpZ2hBY2N1cmFjeTogZmFsc2VcclxuXHRcdH0sIG9wdGlvbnMpO1xyXG5cclxuXHRcdGlmICghKCdnZW9sb2NhdGlvbicgaW4gbmF2aWdhdG9yKSkge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yKHtcclxuXHRcdFx0XHRjb2RlOiAwLFxyXG5cdFx0XHRcdG1lc3NhZ2U6ICdHZW9sb2NhdGlvbiBub3Qgc3VwcG9ydGVkLidcclxuXHRcdFx0fSk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvblJlc3BvbnNlID0gYmluZCh0aGlzLl9oYW5kbGVHZW9sb2NhdGlvblJlc3BvbnNlLCB0aGlzKSxcclxuXHRcdCAgICBvbkVycm9yID0gYmluZCh0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yLCB0aGlzKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy53YXRjaCkge1xyXG5cdFx0XHR0aGlzLl9sb2NhdGlvbldhdGNoSWQgPVxyXG5cdFx0XHQgICAgICAgIG5hdmlnYXRvci5nZW9sb2NhdGlvbi53YXRjaFBvc2l0aW9uKG9uUmVzcG9uc2UsIG9uRXJyb3IsIG9wdGlvbnMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0bmF2aWdhdG9yLmdlb2xvY2F0aW9uLmdldEN1cnJlbnRQb3NpdGlvbihvblJlc3BvbnNlLCBvbkVycm9yLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc3RvcExvY2F0ZSgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgd2F0Y2hpbmcgbG9jYXRpb24gcHJldmlvdXNseSBpbml0aWF0ZWQgYnkgYG1hcC5sb2NhdGUoe3dhdGNoOiB0cnVlfSlgXHJcblx0Ly8gYW5kIGFib3J0cyByZXNldHRpbmcgdGhlIG1hcCB2aWV3IGlmIG1hcC5sb2NhdGUgd2FzIGNhbGxlZCB3aXRoXHJcblx0Ly8gYHtzZXRWaWV3OiB0cnVlfWAuXHJcblx0c3RvcExvY2F0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKG5hdmlnYXRvci5nZW9sb2NhdGlvbiAmJiBuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCkge1xyXG5cdFx0XHRuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCh0aGlzLl9sb2NhdGlvbldhdGNoSWQpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKHRoaXMuX2xvY2F0ZU9wdGlvbnMpIHtcclxuXHRcdFx0dGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaGFuZGxlR2VvbG9jYXRpb25FcnJvcjogZnVuY3Rpb24gKGVycm9yKSB7XHJcblx0XHR2YXIgYyA9IGVycm9yLmNvZGUsXHJcblx0XHQgICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2UgfHxcclxuXHRcdCAgICAgICAgICAgIChjID09PSAxID8gJ3Blcm1pc3Npb24gZGVuaWVkJyA6XHJcblx0XHQgICAgICAgICAgICAoYyA9PT0gMiA/ICdwb3NpdGlvbiB1bmF2YWlsYWJsZScgOiAndGltZW91dCcpKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ICYmICF0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5maXRXb3JsZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIExvY2F0aW9uIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IGxvY2F0aW9uZXJyb3I6IEVycm9yRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gZ2VvbG9jYXRpb24gKHVzaW5nIHRoZSBbYGxvY2F0ZWBdKCNtYXAtbG9jYXRlKSBtZXRob2QpIGZhaWxlZC5cclxuXHRcdHRoaXMuZmlyZSgnbG9jYXRpb25lcnJvcicsIHtcclxuXHRcdFx0Y29kZTogYyxcclxuXHRcdFx0bWVzc2FnZTogJ0dlb2xvY2F0aW9uIGVycm9yOiAnICsgbWVzc2FnZSArICcuJ1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZUdlb2xvY2F0aW9uUmVzcG9uc2U6IGZ1bmN0aW9uIChwb3MpIHtcclxuXHRcdHZhciBsYXQgPSBwb3MuY29vcmRzLmxhdGl0dWRlLFxyXG5cdFx0ICAgIGxuZyA9IHBvcy5jb29yZHMubG9uZ2l0dWRlLFxyXG5cdFx0ICAgIGxhdGxuZyA9IG5ldyBMYXRMbmcobGF0LCBsbmcpLFxyXG5cdFx0ICAgIGJvdW5kcyA9IGxhdGxuZy50b0JvdW5kcyhwb3MuY29vcmRzLmFjY3VyYWN5ICogMiksXHJcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMuX2xvY2F0ZU9wdGlvbnM7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuc2V0Vmlldykge1xyXG5cdFx0XHR2YXIgem9vbSA9IHRoaXMuZ2V0Qm91bmRzWm9vbShib3VuZHMpO1xyXG5cdFx0XHR0aGlzLnNldFZpZXcobGF0bG5nLCBvcHRpb25zLm1heFpvb20gPyBNYXRoLm1pbih6b29tLCBvcHRpb25zLm1heFpvb20pIDogem9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGRhdGEgPSB7XHJcblx0XHRcdGxhdGxuZzogbGF0bG5nLFxyXG5cdFx0XHRib3VuZHM6IGJvdW5kcyxcclxuXHRcdFx0dGltZXN0YW1wOiBwb3MudGltZXN0YW1wXHJcblx0XHR9O1xyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gcG9zLmNvb3Jkcykge1xyXG5cdFx0XHRpZiAodHlwZW9mIHBvcy5jb29yZHNbaV0gPT09ICdudW1iZXInKSB7XHJcblx0XHRcdFx0ZGF0YVtpXSA9IHBvcy5jb29yZHNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbG9jYXRpb25mb3VuZDogTG9jYXRpb25FdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBnZW9sb2NhdGlvbiAodXNpbmcgdGhlIFtgbG9jYXRlYF0oI21hcC1sb2NhdGUpIG1ldGhvZClcclxuXHRcdC8vIHdlbnQgc3VjY2Vzc2Z1bGx5LlxyXG5cdFx0dGhpcy5maXJlKCdsb2NhdGlvbmZvdW5kJywgZGF0YSk7XHJcblx0fSxcclxuXHJcblx0Ly8gVE9ETyBBcHByb3ByaWF0ZSBkb2NzIHNlY3Rpb24/XHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cdC8vIEBtZXRob2QgYWRkSGFuZGxlcihuYW1lOiBTdHJpbmcsIEhhbmRsZXJDbGFzczogRnVuY3Rpb24pOiB0aGlzXHJcblx0Ly8gQWRkcyBhIG5ldyBgSGFuZGxlcmAgdG8gdGhlIG1hcCwgZ2l2ZW4gaXRzIG5hbWUgYW5kIGNvbnN0cnVjdG9yIGZ1bmN0aW9uLlxyXG5cdGFkZEhhbmRsZXI6IGZ1bmN0aW9uIChuYW1lLCBIYW5kbGVyQ2xhc3MpIHtcclxuXHRcdGlmICghSGFuZGxlckNsYXNzKSB7IHJldHVybi
14775B0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGhhbmRsZXIgPSB0aGlzW25hbWVdID0gbmV3IEhhbmRsZXJDbGFzcyh0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9oYW5kbGVycy5wdXNoKGhhbmRsZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnNbbmFtZV0pIHtcclxuXHRcdFx0aGFuZGxlci5lbmFibGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZSgpOiB0aGlzXHJcblx0Ly8gRGVzdHJveXMgdGhlIG1hcCBhbmQgY2xlYXJzIGFsbCByZWxhdGVkIGV2ZW50IGxpc3RlbmVycy5cclxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR0aGlzLl9pbml0RXZlbnRzKHRydWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9jb250YWluZXJJZCAhPT0gdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBiZWluZyByZXVzZWQgYnkgYW5vdGhlciBpbnN0YW5jZScpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRyeSB7XHJcblx0XHRcdC8vIHRocm93cyBlcnJvciBpbiBJRTYtOFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkO1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVySWQ7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRcdFx0dGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkID0gdW5kZWZpbmVkO1xyXG5cdFx0XHQvKiBlc2xpbnQtZW5hYmxlICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lcklkID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLl9sb2NhdGlvbldhdGNoSWQgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLnN0b3BMb2NhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9zdG9wKCk7XHJcblxyXG5cdFx0cmVtb3ZlKHRoaXMuX21hcFBhbmUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9jbGVhckNvbnRyb2xQb3MpIHtcclxuXHRcdFx0dGhpcy5fY2xlYXJDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5fcmVzaXplUmVxdWVzdCkge1xyXG5cdFx0XHRjYW5jZWxBbmltRnJhbWUodGhpcy5fcmVzaXplUmVxdWVzdCk7XHJcblx0XHRcdHRoaXMuX3Jlc2l6ZVJlcXVlc3QgPSBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NsZWFySGFuZGxlcnMoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXHJcblx0XHRcdC8vIEBldmVudCB1bmxvYWQ6IEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBpcyBkZXN0cm95ZWQgd2l0aCBbcmVtb3ZlXSgjbWFwLXJlbW92ZSkgbWV0aG9kLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ3VubG9hZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpO1xyXG5cdFx0Zm9yIChpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHR0aGlzLl9sYXllcnNbaV0ucmVtb3ZlKCk7XHJcblx0XHR9XHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fcGFuZXMpIHtcclxuXHRcdFx0cmVtb3ZlKHRoaXMuX3BhbmVzW2ldKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9sYXllcnMgPSBbXTtcclxuXHRcdHRoaXMuX3BhbmVzID0gW107XHJcblx0XHRkZWxldGUgdGhpcy5fbWFwUGFuZTtcclxuXHRcdGRlbGV0ZSB0aGlzLl9yZW5kZXJlcjtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAc2VjdGlvbiBPdGhlciBNZXRob2RzXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVQYW5lKG5hbWU6IFN0cmluZywgY29udGFpbmVyPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENyZWF0ZXMgYSBuZXcgW21hcCBwYW5lXSgjbWFwLXBhbmUpIHdpdGggdGhlIGdpdmVuIG5hbWUgaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5LFxyXG5cdC8vIHRoZW4gcmV0dXJucyBpdC4gVGhlIHBhbmUgaXMgY3JlYXRlZCBhcyBhIGNoaWxkIG9mIGBjb250YWluZXJgLCBvclxyXG5cdC8vIGFzIGEgY2hpbGQgb2YgdGhlIG1haW4gbWFwIHBhbmUgaWYgbm90IHNldC5cclxuXHRjcmVhdGVQYW5lOiBmdW5jdGlvbiAobmFtZSwgY29udGFpbmVyKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtcGFuZScgKyAobmFtZSA/ICcgbGVhZmxldC0nICsgbmFtZS5yZXBsYWNlKCdQYW5lJywgJycpICsgJy1wYW5lJyA6ICcnKSxcclxuXHRcdCAgICBwYW5lID0gY3JlYXRlJDEoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyIHx8IHRoaXMuX21hcFBhbmUpO1xyXG5cclxuXHRcdGlmIChuYW1lKSB7XHJcblx0XHRcdHRoaXMuX3BhbmVzW25hbWVdID0gcGFuZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBwYW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIEdldHRpbmcgTWFwIFN0YXRlXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBjZW50ZXIgb2YgdGhlIG1hcCB2aWV3XHJcblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jaGVja0lmTG9hZGVkKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xhc3RDZW50ZXIgJiYgIXRoaXMuX21vdmVkKCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuX2xhc3RDZW50ZXI7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fZ2V0Q2VudGVyTGF5ZXJQb2ludCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCB6b29tIGxldmVsIG9mIHRoZSBtYXAgdmlld1xyXG5cdGdldFpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl96b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBib3VuZHMgdmlzaWJsZSBpbiB0aGUgY3VycmVudCBtYXAgdmlld1xyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuZ2V0UGl4ZWxCb3VuZHMoKSxcclxuXHRcdCAgICBzdyA9IHRoaXMudW5wcm9qZWN0KGJvdW5kcy5nZXRCb3R0b21MZWZ0KCkpLFxyXG5cdFx0ICAgIG5lID0gdGhpcy51bnByb2plY3QoYm91bmRzLmdldFRvcFJpZ2h0KCkpO1xyXG5cclxuXHRcdHJldHVybi
14775BuZXcgTGF0TG5nQm91bmRzKHN3LCBuZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRNaW5ab29tKCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1pbmltdW0gem9vbSBsZXZlbCBvZiB0aGUgbWFwIChpZiBzZXQgaW4gdGhlIGBtaW5ab29tYCBvcHRpb24gb2YgdGhlIG1hcCBvciBvZiBhbnkgbGF5ZXJzKSwgb3IgYDBgIGJ5IGRlZmF1bHQuXHJcblx0Z2V0TWluWm9vbTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgPyB0aGlzLl9sYXllcnNNaW5ab29tIHx8IDAgOiB0aGlzLm9wdGlvbnMubWluWm9vbTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE1heFpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbWF4aW11bSB6b29tIGxldmVsIG9mIHRoZSBtYXAgKGlmIHNldCBpbiB0aGUgYG1heFpvb21gIG9wdGlvbiBvZiB0aGUgbWFwIG9yIG9mIGFueSBsYXllcnMpLlxyXG5cdGdldE1heFpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkID9cclxuXHRcdFx0KHRoaXMuX2xheWVyc01heFpvb20gPT09IHVuZGVmaW5lZCA/IEluZmluaXR5IDogdGhpcy5fbGF5ZXJzTWF4Wm9vbSkgOlxyXG5cdFx0XHR0aGlzLm9wdGlvbnMubWF4Wm9vbTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEJvdW5kc1pvb20oYm91bmRzOiBMYXRMbmdCb3VuZHMsIGluc2lkZT86IEJvb2xlYW4sIHBhZGRpbmc/OiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1heGltdW0gem9vbSBsZXZlbCBvbiB3aGljaCB0aGUgZ2l2ZW4gYm91bmRzIGZpdCB0byB0aGUgbWFwXHJcblx0Ly8gdmlldyBpbiBpdHMgZW50aXJldHkuIElmIGBpbnNpZGVgIChvcHRpb25hbCkgaXMgc2V0IHRvIGB0cnVlYCwgdGhlIG1ldGhvZFxyXG5cdC8vIGluc3RlYWQgcmV0dXJucyB0aGUgbWluaW11bSB6b29tIGxldmVsIG9uIHdoaWNoIHRoZSBtYXAgdmlldyBmaXRzIGludG9cclxuXHQvLyB0aGUgZ2l2ZW4gYm91bmRzIGluIGl0cyBlbnRpcmV0eS5cclxuXHRnZXRCb3VuZHNab29tOiBmdW5jdGlvbiAoYm91bmRzLCBpbnNpZGUsIHBhZGRpbmcpIHsgLy8gKExhdExuZ0JvdW5kc1ssIEJvb2xlYW4sIFBvaW50XSkgLT4gTnVtYmVyXHJcblx0XHRib3VuZHMgPSB0b0xhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cdFx0cGFkZGluZyA9IHRvUG9pbnQocGFkZGluZyB8fCBbMCwgMF0pO1xyXG5cclxuXHRcdHZhciB6b29tID0gdGhpcy5nZXRab29tKCkgfHwgMCxcclxuXHRcdCAgICBtaW4gPSB0aGlzLmdldE1pblpvb20oKSxcclxuXHRcdCAgICBtYXggPSB0aGlzLmdldE1heFpvb20oKSxcclxuXHRcdCAgICBudyA9IGJvdW5kcy5nZXROb3J0aFdlc3QoKSxcclxuXHRcdCAgICBzZSA9IGJvdW5kcy5nZXRTb3V0aEVhc3QoKSxcclxuXHRcdCAgICBzaXplID0gdGhpcy5nZXRTaXplKCkuc3VidHJhY3QocGFkZGluZyksXHJcblx0XHQgICAgYm91bmRzU2l6ZSA9IHRvQm91bmRzKHRoaXMucHJvamVjdChzZSwgem9vbSksIHRoaXMucHJvamVjdChudywgem9vbSkpLmdldFNpemUoKSxcclxuXHRcdCAgICBzbmFwID0gYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbVNuYXAgOiAxLFxyXG5cdFx0ICAgIHNjYWxleCA9IHNpemUueCAvIGJvdW5kc1NpemUueCxcclxuXHRcdCAgICBzY2FsZXkgPSBzaXplLnkgLyBib3VuZHNTaXplLnksXHJcblx0XHQgICAgc2NhbGUgPSBpbnNpZGUgPyBNYXRoLm1heChzY2FsZXgsIHNjYWxleSkgOiBNYXRoLm1pbihzY2FsZXgsIHNjYWxleSk7XHJcblxyXG5cdFx0em9vbSA9IHRoaXMuZ2V0U2NhbGVab29tKHNjYWxlLCB6b29tKTtcclxuXHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gKHNuYXAgLyAxMDApKSAqIChzbmFwIC8gMTAwKTsgLy8gZG9uJ3QganVtcCBpZiB3aXRoaW4gMSUgb2YgYSBzbmFwIGxldmVsXHJcblx0XHRcdHpvb20gPSBpbnNpZGUgPyBNYXRoLmNlaWwoem9vbSAvIHNuYXApICogc25hcCA6IE1hdGguZmxvb3Ioem9vbSAvIHNuYXApICogc25hcDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbihtYXgsIHpvb20pKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNpemUoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IHNpemUgb2YgdGhlIG1hcCBjb250YWluZXIgKGluIHBpeGVscykuXHJcblx0Z2V0U2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9zaXplIHx8IHRoaXMuX3NpemVDaGFuZ2VkKSB7XHJcblx0XHRcdHRoaXMuX3NpemUgPSBuZXcgUG9pbnQoXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudFdpZHRoIHx8IDAsXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudEhlaWdodCB8fCAwKTtcclxuXHJcblx0XHRcdHRoaXMuX3NpemVDaGFuZ2VkID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc2l6ZS5jbG9uZSgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxCb3VuZHMoKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgYm91bmRzIG9mIHRoZSBjdXJyZW50IG1hcCB2aWV3IGluIHByb2plY3RlZCBwaXhlbFxyXG5cdC8vIGNvb3JkaW5hdGVzIChzb21ldGltZXMgdXNlZnVsIGluIGxheWVyIGFuZCBvdmVybGF5IGltcGxlbWVudGF0aW9ucykuXHJcblx0Z2V0UGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciB0b3BMZWZ0UG9pbnQgPSB0aGlzLl9nZXRUb3BMZWZ0UG9pbnQoY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBuZXcgQm91bmRzKHRvcExlZnRQb2ludCwgdG9wTGVmdFBvaW50LmFkZCh0aGlzLmdldFNpemUoKSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIFRPRE86IENoZWNrIHNlbWFudGljcyAtIGlzbid0IHRoZSBwaXhlbCBvcmlnaW4gdGhlIDAsMCBjb29yZCByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBtYXAgcGFuZT8gXCJsZWZ0IHBvaW50IG9mIHRoZSBtYXAgbGF5ZXJcIiBjYW4gYmUgY29uZnVzaW5nLCBzcGVjaWFsbHlcclxuXHQvLyBzaW5jZSB0aGVyZSBjYW4gYmUgbmVnYXRpdmUgb2Zmc2V0cy5cclxuXHQvLyBAbWV0aG9kIGdldFBpeGVsT3JpZ2luKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcHJvamVjdGVkIHBpeGVsIGNvb3JkaW5hdGVzIG9mIHRoZSB0b3AgbGVmdCBwb2ludCBvZlxyXG5cdC8vIHRoZSBtYXAgbGF5ZXIgKHVzZWZ1bCBpbiBjdXN0b20gbGF5ZXIgYW5kIG92ZXJsYXkgaW1wbGVtZW50YXRpb25zKS5cclxuXHRnZXRQaXhlbE9yaWdpbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fY2hlY2tJZkxvYWRlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BpeGVsT3JpZ2luO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxXb3JsZEJvdW5kcyh6b29tPzogTnVtYmVyKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgd29ybGQncyBib3VuZHMgaW4gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIHpvb20gbGV2ZWwgYHpvb21gLlxyXG5cdC8vIElmIGB6b29tYCBpcyBvbWl0
14775dGVkLCB0aGUgbWFwJ3MgY3VycmVudCB6b29tIGxldmVsIGlzIHVzZWQuXHJcblx0Z2V0UGl4ZWxXb3JsZEJvdW5kczogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmdldFByb2plY3RlZEJvdW5kcyh6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLmdldFpvb20oKSA6IHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQYW5lKHBhbmU6IFN0cmluZ3xIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyBhIFttYXAgcGFuZV0oI21hcC1wYW5lKSwgZ2l2ZW4gaXRzIG5hbWUgb3IgaXRzIEhUTUwgZWxlbWVudCAoaXRzIGlkZW50aXR5KS5cclxuXHRnZXRQYW5lOiBmdW5jdGlvbiAocGFuZSkge1xyXG5cdFx0cmV0dXJuIHR5cGVvZiBwYW5lID09PSAnc3RyaW5nJyA/IHRoaXMuX3BhbmVzW3BhbmVdIDogcGFuZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBhbmVzKCk6IE9iamVjdFxyXG5cdC8vIFJldHVybnMgYSBwbGFpbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmFtZXMgb2YgYWxsIFtwYW5lc10oI21hcC1wYW5lKSBhcyBrZXlzIGFuZFxyXG5cdC8vIHRoZSBwYW5lcyBhcyB2YWx1ZXMuXHJcblx0Z2V0UGFuZXM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9wYW5lcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRhaW5lcjogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBIVE1MIGVsZW1lbnQgdGhhdCBjb250YWlucyB0aGUgbWFwLlxyXG5cdGdldENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQHNlY3Rpb24gQ29udmVyc2lvbiBNZXRob2RzXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Wm9vbVNjYWxlKHRvWm9vbTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc2NhbGUgZmFjdG9yIHRvIGJlIGFwcGxpZWQgdG8gYSBtYXAgdHJhbnNpdGlvbiBmcm9tIHpvb20gbGV2ZWxcclxuXHQvLyBgZnJvbVpvb21gIHRvIGB0b1pvb21gLiBVc2VkIGludGVybmFsbHkgdG8gaGVscCB3aXRoIHpvb20gYW5pbWF0aW9ucy5cclxuXHRnZXRab29tU2NhbGU6IGZ1bmN0aW9uICh0b1pvb20sIGZyb21ab29tKSB7XHJcblx0XHQvLyBUT0RPIHJlcGxhY2Ugd2l0aCB1bml2ZXJzYWwgaW1wbGVtZW50YXRpb24gYWZ0ZXIgcmVmYWN0b3JpbmcgcHJvamVjdGlvbnNcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0cmV0dXJuIGNycy5zY2FsZSh0b1pvb20pIC8gY3JzLnNjYWxlKGZyb21ab29tKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNjYWxlWm9vbShzY2FsZTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgem9vbSBsZXZlbCB0aGF0IHRoZSBtYXAgd291bGQgZW5kIHVwIGF0LCBpZiBpdCBpcyBhdCBgZnJvbVpvb21gXHJcblx0Ly8gbGV2ZWwgYW5kIGV2ZXJ5dGhpbmcgaXMgc2NhbGVkIGJ5IGEgZmFjdG9yIG9mIGBzY2FsZWAuIEludmVyc2Ugb2ZcclxuXHQvLyBbYGdldFpvb21TY2FsZWBdKCNtYXAtZ2V0Wm9vbVNjYWxlKS5cclxuXHRnZXRTY2FsZVpvb206IGZ1bmN0aW9uIChzY2FsZSwgZnJvbVpvb20pIHtcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0dmFyIHpvb20gPSBjcnMuem9vbShzY2FsZSAqIGNycy5zY2FsZShmcm9tWm9vbSkpO1xyXG5cdFx0cmV0dXJuIGlzTmFOKHpvb20pID8gSW5maW5pdHkgOiB6b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcHJvamVjdChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIGBMYXRMbmdgIGFjY29yZGluZyB0byB0aGUgcHJvamVjdGlvblxyXG5cdC8vIG9mIHRoZSBtYXAncyBDUlMsIHRoZW4gc2NhbGVzIGl0IGFjY29yZGluZyB0byBgem9vbWAgYW5kIHRoZSBDUlMnc1xyXG5cdC8vIGBUcmFuc2Zvcm1hdGlvbmAuIFRoZSByZXN1bHQgaXMgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBDUlMgb3JpZ2luLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmxhdExuZ1RvUG9pbnQodG9MYXRMbmcobGF0bG5nKSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1bnByb2plY3QocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBJbnZlcnNlIG9mIFtgcHJvamVjdGBdKCNtYXAtcHJvamVjdCkuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLnBvaW50VG9MYXRMbmcodG9Qb2ludChwb2ludCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF5ZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbiksXHJcblx0Ly8gcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSAoZm9yIHRoZSBjdXJyZW50IHpvb20gbGV2ZWwpLlxyXG5cdGxheWVyUG9pbnRUb0xhdExuZzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgcHJvamVjdGVkUG9pbnQgPSB0b1BvaW50KHBvaW50KS5hZGQodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHRcdHJldHVybi
14775B0aGlzLnVucHJvamVjdChwcm9qZWN0ZWRQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIEdpdmVuIGEgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUsIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcGl4ZWwgY29vcmRpbmF0ZVxyXG5cdC8vIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKS5cclxuXHRsYXRMbmdUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdCh0b0xhdExuZyhsYXRsbmcpKS5fcm91bmQoKTtcclxuXHRcdHJldHVybiBwcm9qZWN0ZWRQb2ludC5fc3VidHJhY3QodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ2Agd2hlcmUgYGxhdGAgYW5kIGBsbmdgIGhhcyBiZWVuIHdyYXBwZWQgYWNjb3JkaW5nIHRvIHRoZVxyXG5cdC8vIG1hcCdzIENSUydzIGB3cmFwTGF0YCBhbmQgYHdyYXBMbmdgIHByb3BlcnRpZXMsIGlmIHRoZXkgYXJlIG91dHNpZGUgdGhlXHJcblx0Ly8gQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIEJ5IGRlZmF1bHQgdGhpcyBtZWFucyBsb25naXR1ZGUgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGRhdGVsaW5lIHNvIGl0c1xyXG5cdC8vIHZhbHVlIGlzIGJldHdlZW4gLTE4MCBhbmQgKzE4MCBkZWdyZWVzLlxyXG5cdHdyYXBMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLndyYXBMYXRMbmcodG9MYXRMbmcobGF0bG5nKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB3cmFwTGF0TG5nQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBhIGBMYXRMbmdCb3VuZHNgIHdpdGggdGhlIHNhbWUgc2l6ZSBhcyB0aGUgZ2l2ZW4gb25lLCBlbnN1cmluZyB0aGF0XHJcblx0Ly8gaXRzIGNlbnRlciBpcyB3aXRoaW4gdGhlIENSUydzIGJvdW5kcy5cclxuXHQvLyBCeSBkZWZhdWx0IHRoaXMgbWVhbnMgdGhlIGNlbnRlciBsb25naXR1ZGUgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGRhdGVsaW5lIHNvIGl0c1xyXG5cdC8vIHZhbHVlIGlzIGJldHdlZW4gLTE4MCBhbmQgKzE4MCBkZWdyZWVzLCBhbmQgdGhlIG1ham9yaXR5IG9mIHRoZSBib3VuZHNcclxuXHQvLyBvdmVybGFwcyB0aGUgQ1JTJ3MgYm91bmRzLlxyXG5cdHdyYXBMYXRMbmdCb3VuZHM6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLndyYXBMYXRMbmdCb3VuZHModG9MYXRMbmdCb3VuZHMobGF0bG5nKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZShsYXRsbmcxOiBMYXRMbmcsIGxhdGxuZzI6IExhdExuZyk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdHdvIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBhY2NvcmRpbmcgdG9cclxuXHQvLyB0aGUgbWFwJ3MgQ1JTLiBCeSBkZWZhdWx0IHRoaXMgbWVhc3VyZXMgZGlzdGFuY2UgaW4gbWV0ZXJzLlxyXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMuZGlzdGFuY2UodG9MYXRMbmcobGF0bG5nMSksIHRvTGF0TG5nKGxhdGxuZzIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbikuXHJcblx0Y29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gdG9Qb2ludChwb2ludCkuc3VidHJhY3QodGhpcy5fZ2V0TWFwUGFuZVBvcygpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSxcclxuXHQvLyByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIuXHJcblx0bGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gdG9Qb2ludChwb2ludCkuYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWluZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lciwgcmV0dXJuc1xyXG5cdC8vIHRoZSBjb3JyZXNwb25kaW5nIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIChmb3IgdGhlIGN1cnJlbnQgem9vbSBsZXZlbCkuXHJcblx0Y29udGFpbmVyUG9pbnRUb0xhdExuZzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgbGF5ZXJQb2ludCA9IHRoaXMuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQodG9Qb2ludChwb2ludCkpO1xyXG5cdFx0cmV0dXJuIHRoaXMubGF5ZXJQb2ludFRvTGF0TG5nKGxheWVyUG9pbnQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF0TG5nVG9Db250YWluZXJQb2ludChsYXRsbmc6IExhdExuZyk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSwgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBwaXhlbCBjb29yZGluYXRlXHJcblx0Ly8gcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIuXHJcblx0bGF0TG5nVG9Db250YWluZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIHRoaXMubGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQodGhpcy5sYXRMbmdUb0xheWVyUG9pbnQodG9MYXRMbmcobGF0bG5nKSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZXY6IE1vdXNlRXZlbnQpOiBQb2ludFxyXG5cdC8vIEdpdmVuIGEgTW91c2VFdmVudCBvYmplY3QsIHJldHVybnMgdGhlIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlXHJcblx0Ly8gbWFwIGNvbnRhaW5lciB3aGVyZSB0aGUgZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9Db250YWluZXJQb2ludDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHJldHVybi
14775BnZXRNb3VzZVBvc2l0aW9uKGUsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9MYXllclBvaW50KGV2OiBNb3VzZUV2ZW50KTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIE1vdXNlRXZlbnQgb2JqZWN0LCByZXR1cm5zIHRoZSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvXHJcblx0Ly8gdGhlIFtvcmlnaW4gcGl4ZWxdKCNtYXAtZ2V0cGl4ZWxvcmlnaW4pIHdoZXJlIHRoZSBldmVudCB0b29rIHBsYWNlLlxyXG5cdG1vdXNlRXZlbnRUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludCh0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG1vdXNlRXZlbnRUb0xhdExuZyhldjogTW91c2VFdmVudCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgTW91c2VFdmVudCBvYmplY3QsIHJldHVybnMgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgd2hlcmUgdGhlXHJcblx0Ly8gZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9MYXRMbmc6IGZ1bmN0aW9uIChlKSB7IC8vIChNb3VzZUV2ZW50KVxyXG5cdFx0cmV0dXJuIHRoaXMubGF5ZXJQb2ludFRvTGF0TG5nKHRoaXMubW91c2VFdmVudFRvTGF5ZXJQb2ludChlKSk7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIG1hcCBpbml0aWFsaXphdGlvbiBtZXRob2RzXHJcblxyXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoaWQpIHtcclxuXHRcdHZhciBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSBnZXQoaWQpO1xyXG5cclxuXHRcdGlmICghY29udGFpbmVyKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBub3QgZm91bmQuJyk7XHJcblx0XHR9IGVsc2UgaWYgKGNvbnRhaW5lci5fbGVhZmxldF9pZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ01hcCBjb250YWluZXIgaXMgYWxyZWFkeSBpbml0aWFsaXplZC4nKTtcclxuXHRcdH1cclxuXHJcblx0XHRvbihjb250YWluZXIsICdzY3JvbGwnLCB0aGlzLl9vblNjcm9sbCwgdGhpcyk7XHJcblx0XHR0aGlzLl9jb250YWluZXJJZCA9IHN0YW1wKGNvbnRhaW5lcik7XHJcblx0fSxcclxuXHJcblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXI7XHJcblxyXG5cdFx0dGhpcy5fZmFkZUFuaW1hdGVkID0gdGhpcy5vcHRpb25zLmZhZGVBbmltYXRpb24gJiYgYW55M2Q7XHJcblxyXG5cdFx0YWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1jb250YWluZXInICtcclxuXHRcdFx0KHRvdWNoID8gJyBsZWFmbGV0LXRvdWNoJyA6ICcnKSArXHJcblx0XHRcdChyZXRpbmEgPyAnIGxlYWZsZXQtcmV0aW5hJyA6ICcnKSArXHJcblx0XHRcdChpZWx0OSA/ICcgbGVhZmxldC1vbGRpZScgOiAnJykgK1xyXG5cdFx0XHQoc2FmYXJpID8gJyBsZWFmbGV0LXNhZmFyaScgOiAnJykgK1xyXG5cdFx0XHQodGhpcy5fZmFkZUFuaW1hdGVkID8gJyBsZWFmbGV0LWZhZGUtYW5pbScgOiAnJykpO1xyXG5cclxuXHRcdHZhciBwb3NpdGlvbiA9IGdldFN0eWxlKGNvbnRhaW5lciwgJ3Bvc2l0aW9uJyk7XHJcblxyXG5cdFx0aWYgKHBvc2l0aW9uICE9PSAnYWJzb2x1dGUnICYmIHBvc2l0aW9uICE9PSAncmVsYXRpdmUnICYmIHBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdFBhbmVzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2luaXRDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBhbmVzID0gdGhpcy5fcGFuZXMgPSB7fTtcclxuXHRcdHRoaXMuX3BhbmVSZW5kZXJlcnMgPSB7fTtcclxuXHJcblx0XHQvLyBAc2VjdGlvblxyXG5cdFx0Ly9cclxuXHRcdC8vIFBhbmVzIGFyZSBET00gZWxlbWVudHMgdXNlZCB0byBjb250cm9sIHRoZSBvcmRlcmluZyBvZiBsYXllcnMgb24gdGhlIG1hcC4gWW91XHJcblx0XHQvLyBjYW4gYWNjZXNzIHBhbmVzIHdpdGggW2BtYXAuZ2V0UGFuZWBdKCNtYXAtZ2V0cGFuZSkgb3JcclxuXHRcdC8vIFtgbWFwLmdldFBhbmVzYF0oI21hcC1nZXRwYW5lcykgbWV0aG9kcy4gTmV3IHBhbmVzIGNhbiBiZSBjcmVhdGVkIHdpdGggdGhlXHJcblx0XHQvLyBbYG1hcC5jcmVhdGVQYW5lYF0oI21hcC1jcmVhdGVwYW5lKSBtZXRob2QuXHJcblx0XHQvL1xyXG5cdFx0Ly8gRXZlcnkgbWFwIGhhcyB0aGUgZm9sbG93aW5nIGRlZmF1bHQgcGFuZXMgdGhhdCBkaWZmZXIgb25seSBpbiB6SW5kZXguXHJcblx0XHQvL1xyXG5cdFx0Ly8gQHBhbmUgbWFwUGFuZTogSFRNTEVsZW1lbnQgPSAnYXV0bydcclxuXHRcdC8vIFBhbmUgdGhhdCBjb250YWlucyBhbGwgb3RoZXIgbWFwIHBhbmVzXHJcblxyXG5cdFx0dGhpcy5fbWFwUGFuZSA9IHRoaXMuY3JlYXRlUGFuZSgnbWFwUGFuZScsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHRzZXRQb3NpdGlvbih0aGlzLl9tYXBQYW5lLCBuZXcgUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdC8vIEBwYW5lIHRpbGVQYW5lOiBIVE1MRWxlbWVudCA9IDIwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEdyaWRMYXllcmBzIGFuZCBgVGlsZUxheWVyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgndGlsZVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG92ZXJsYXlQYW5lOiBIVE1MRWxlbWVudCA9IDQwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgdmVjdG9ycyAoYFBhdGhgcywgbGlrZSBgUG9seWxpbmVgcyBhbmQgYFBvbHlnb25gcyksIGBJbWFnZU92ZXJsYXlgcyBhbmQgYFZpZGVvT3ZlcmxheWBzXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3NoYWRvd1BhbmUnKTtcclxuXHRcdC8vIEBwYW5lIHNoYWRvd1BhbmU6IEhUTUxFbGVtZW50ID0gNTAwXHJcblx0XHQvLyBQYW5lIGZvciBvdmVybGF5IHNoYWRvd3MgKGUuZy4gYE1hcmtlcmAgc2hhZG93cylcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgnb3ZlcmxheVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG1hcmtlclBhbmU6IEhUTUxFbGVtZW50ID0gNjAwXHJcblx0XHQvLyBQYW5lIGZvciBgSWNvbmBzIG9mIGBNYXJrZXJgc1xyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCdtYXJrZXJQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSB0b29sdGlwUGFuZTogSFRNTEVsZW1lbnQgPSA2NTBcclxuXHRcdC8vIFBhbmUgZm9yIGBUb29sdGlwYHMuXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3Rvb2x0aXBQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBwb3B1cFBhbmU6IEhUTUxFbGVtZW50ID0gNzAwXHJcblx0XHQvLyBQYW5lIGZvciBgUG9wdXBgcy5cclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgncG9wdXBQYW5lJyk7XHJcblxyXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMubWFya2VyWm9vbUFuaW1hdGlvbikge1xyXG5cdFx0XHRhZGRDbGFzcyhwYW5lcy5tYXJrZXJQYW5lLCAnbGVhZmxldC16b29tLWhpZGUnKTtcclxuXHRcdFx0YWRkQ2xhc3MocGFuZXMuc2hhZG93UGFuZSwgJ2xlYWZsZXQtem9vbS1oaWRlJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyB0aGF0IG1vZGlmeSBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRfcmVzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHRzZXRQb3NpdGlvbih0aGlzLl9tYXBQYW5lLCBuZXcgUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdHZhciBsb2FkaW5nID0gIXRoaXMuX2xvYWRlZDtcclxuXHRcdHRoaXMuX2xvYWRlZCA9IHRydWU7XHJcblx0XHR6b29tID0gdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cclxuXHRcdHRoaXMuZmlyZSgndmlld3ByZXJlc2V0Jyk7XHJcblxyXG5cdFx0dmFyIHpvb21DaGFuZ2VkI
14775D0gdGhpcy5fem9vbSAhPT0gem9vbTtcclxuXHRcdHRoaXNcclxuXHRcdFx0Ll9tb3ZlU3RhcnQoem9vbUNoYW5nZWQsIGZhbHNlKVxyXG5cdFx0XHQuX21vdmUoY2VudGVyLCB6b29tKVxyXG5cdFx0XHQuX21vdmVFbmQoem9vbUNoYW5nZWQpO1xyXG5cclxuXHRcdC8vIEBldmVudCB2aWV3cmVzZXQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgbmVlZHMgdG8gcmVkcmF3IGl0cyBjb250ZW50ICh0aGlzIHVzdWFsbHkgaGFwcGVuc1xyXG5cdFx0Ly8gb24gbWFwIHpvb20gb3IgbG9hZCkuIFZlcnkgdXNlZnVsIGZvciBjcmVhdGluZyBjdXN0b20gb3ZlcmxheXMuXHJcblx0XHR0aGlzLmZpcmUoJ3ZpZXdyZXNldCcpO1xyXG5cclxuXHRcdC8vIEBldmVudCBsb2FkOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGlzIGluaXRpYWxpemVkICh3aGVuIGl0cyBjZW50ZXIgYW5kIHpvb20gYXJlIHNldFxyXG5cdFx0Ly8gZm9yIHRoZSBmaXJzdCB0aW1lKS5cclxuXHRcdGlmIChsb2FkaW5nKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnbG9hZCcpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9tb3ZlU3RhcnQ6IGZ1bmN0aW9uICh6b29tQ2hhbmdlZCwgbm9Nb3ZlU3RhcnQpIHtcclxuXHRcdC8vIEBldmVudCB6b29tc3RhcnQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgem9vbSBpcyBhYm91dCB0byBjaGFuZ2UgKGUuZy4gYmVmb3JlIHpvb20gYW5pbWF0aW9uKS5cclxuXHRcdC8vIEBldmVudCBtb3Zlc3RhcnQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB2aWV3IG9mIHRoZSBtYXAgc3RhcnRzIGNoYW5naW5nIChlLmcuIHVzZXIgc3RhcnRzIGRyYWdnaW5nIHRoZSBtYXApLlxyXG5cdFx0aWYgKHpvb21DaGFuZ2VkKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnem9vbXN0YXJ0Jyk7XHJcblx0XHR9XHJcblx0XHRpZiAoIW5vTW92ZVN0YXJ0KSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfbW92ZTogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgZGF0YSkge1xyXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR6b29tID0gdGhpcy5fem9vbTtcclxuXHRcdH1cclxuXHRcdHZhciB6b29tQ2hhbmdlZCA9IHRoaXMuX3pvb20gIT09IHpvb207XHJcblxyXG5cdFx0dGhpcy5fem9vbSA9IHpvb207XHJcblx0XHR0aGlzLl9sYXN0Q2VudGVyID0gY2VudGVyO1xyXG5cdFx0dGhpcy5fcGl4ZWxPcmlnaW4gPSB0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIpO1xyXG5cclxuXHRcdC8vIEBldmVudCB6b29tOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgcmVwZWF0ZWRseSBkdXJpbmcgYW55IGNoYW5nZSBpbiB6b29tIGxldmVsLCBpbmNsdWRpbmcgem9vbVxyXG5cdFx0Ly8gYW5kIGZseSBhbmltYXRpb25zLlxyXG5cdFx0aWYgKHpvb21DaGFuZ2VkIHx8IChkYXRhICYmIGRhdGEucGluY2gpKSB7XHQvLyBBbHdheXMgZmlyZSAnem9vbScgaWYgcGluY2hpbmcgYmVjYXVzZSAjMzUzMFxyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb20nLCBkYXRhKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgZHVyaW5nIGFueSBtb3ZlbWVudCBvZiB0aGUgbWFwLCBpbmNsdWRpbmcgcGFuIGFuZFxyXG5cdFx0Ly8gZmx5IGFuaW1hdGlvbnMuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlJywgZGF0YSk7XHJcblx0fSxcclxuXHJcblx0X21vdmVFbmQ6IGZ1bmN0aW9uICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0Ly8gQGV2ZW50IHpvb21lbmQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaGFzIGNoYW5nZWQsIGFmdGVyIGFueSBhbmltYXRpb25zLlxyXG5cdFx0aWYgKHpvb21DaGFuZ2VkKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnem9vbWVuZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBldmVudCBtb3ZlZW5kOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgY2VudGVyIG9mIHRoZSBtYXAgc3RvcHMgY2hhbmdpbmcgKGUuZy4gdXNlciBzdG9wcGVkXHJcblx0XHQvLyBkcmFnZ2luZyB0aGUgbWFwKS5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmVlbmQnKTtcclxuXHR9LFxyXG5cclxuXHRfc3RvcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0Y2FuY2VsQW5pbUZyYW1lKHRoaXMuX2ZseVRvRnJhbWUpO1xyXG5cdFx0aWYgKHRoaXMuX3BhbkFuaW0pIHtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5zdG9wKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcmF3UGFuQnk6IGZ1bmN0aW9uIChvZmZzZXQpIHtcclxuXHRcdHNldFBvc2l0aW9uKHRoaXMuX21hcFBhbmUsIHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbVNwYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmdldE1heFpvb20oKSAtIHRoaXMuZ2V0TWluWm9vbSgpO1xyXG5cdH0sXHJcblxyXG5cdF9wYW5JbnNpZGVNYXhCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5mb3JjaW5nQm91bmRzKSB7XHJcblx0XHRcdHRoaXMucGFuSW5zaWRlQm91bmRzKHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jaGVja0lmTG9hZGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1NldCBtYXAgY2VudGVyIGFuZCB6b29tIGZpcnN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIERPTSBldmVudCBoYW5kbGluZ1xyXG5cclxuXHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBldmVudHNcclxuXHRfaW5pdEV2ZW50czogZnVuY3Rpb24gKHJlbW92ZSQkMSkge1xyXG5cdFx0dGhpcy5fdGFyZ2V0cyA9IHt9O1xyXG5cdFx0dGhpcy5fdGFyZ2V0c1tzdGFtcCh0aGlzLl9jb250YWluZXIpXSA9IHRoaXM7XHJcblxyXG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlJCQxID8gb2ZmIDogb247XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGNsaWNrOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIGNsaWNrcyAob3IgdGFwcykgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBkYmxjbGljazogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBkb3VibGUtY2xpY2tzIChvciBkb3VibGUtdGFwcykgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZWRvd246IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgcHVzaGVzIHRoZSBtb3VzZSBidXR0b24gb24gdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZXVwOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHJlbGVhc2VzIHRoZSBtb3VzZSBidXR0b24gb24gdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW92ZXI6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1vdXNlIGVudGVycyB0aGUgbWFwLlxyXG5cdFx0Ly8gQGV2ZW50IG1vdXNlb3V0OiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtb3VzZSBsZWF2ZXMgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW1vdmU6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoaWxlIHRoZSBtb3VzZSBtb3ZlcyBvdmVyIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgY29udGV4dG1lbnU6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgcHVzaGVzIHRoZSByaWdodCBtb3VzZSBidXR0b24gb24gdGhlIG1hcCwgcHJldmVudHNcclxuXHRcdC8vIGRlZmF1bHQgYnJvd3NlciBjb250ZXh0IG1lbnUgZnJvbSBzaG93aW5nIGlmIHRoZXJlIGFyZSBsaXN0ZW5lcnMgb25cclxuXHRcdC8vIHRoaXMgZXZlbnQuIEFsc28gZmlyZWQgb24gbW9iaWxlIHdoZW4gdGhlIHVzZXIgaG9sZHMgYSBzaW5nbGUgdG91Y2hcclxuXHRcdC8vIGZvciBhIHNlY29uZCAoYWxzbyBjYWxsZWQgbG9uZyBwcmVzcykuXHJcblx0XHQvLyBAZXZlbnQga2V5cHJlc3M6IEtleWJvYXJkRXZlbn
14775RcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgcHJlc3NlcyBhIGtleSBmcm9tIHRoZSBrZXlib2FyZCB0aGF0IHByb2R1Y2VzIGEgY2hhcmFjdGVyIHZhbHVlIHdoaWxlIHRoZSBtYXAgaXMgZm9jdXNlZC5cclxuXHRcdC8vIEBldmVudCBrZXlkb3duOiBLZXlib2FyZEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHByZXNzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLiBVbmxpa2UgdGhlIGBrZXlwcmVzc2AgZXZlbnQsXHJcblx0XHQvLyB0aGUgYGtleWRvd25gIGV2ZW50IGlzIGZpcmVkIGZvciBrZXlzIHRoYXQgcHJvZHVjZSBhIGNoYXJhY3RlciB2YWx1ZSBhbmQgZm9yIGtleXNcclxuXHRcdC8vIHRoYXQgZG8gbm90IHByb2R1Y2UgYSBjaGFyYWN0ZXIgdmFsdWUuXHJcblx0XHQvLyBAZXZlbnQga2V5dXA6IEtleWJvYXJkRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgcmVsZWFzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLlxyXG5cdFx0b25PZmYodGhpcy5fY29udGFpbmVyLCAnY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgJyArXHJcblx0XHRcdCdtb3VzZW92ZXIgbW91c2VvdXQgbW91c2Vtb3ZlIGNvbnRleHRtZW51IGtleXByZXNzIGtleWRvd24ga2V5dXAnLCB0aGlzLl9oYW5kbGVET01FdmVudCwgdGhpcyk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy50cmFja1Jlc2l6ZSkge1xyXG5cdFx0XHRvbk9mZih3aW5kb3csICdyZXNpemUnLCB0aGlzLl9vblJlc2l6ZSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGFueTNkICYmIHRoaXMub3B0aW9ucy50cmFuc2Zvcm0zRExpbWl0KSB7XHJcblx0XHRcdChyZW1vdmUkJDEgPyB0aGlzLm9mZiA6IHRoaXMub24pLmNhbGwodGhpcywgJ21vdmVlbmQnLCB0aGlzLl9vbk1vdmVFbmQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vblJlc2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Y2FuY2VsQW5pbUZyYW1lKHRoaXMuX3Jlc2l6ZVJlcXVlc3QpO1xyXG5cdFx0dGhpcy5fcmVzaXplUmVxdWVzdCA9IHJlcXVlc3RBbmltRnJhbWUoXHJcblx0XHQgICAgICAgIGZ1bmN0aW9uICgpIHsgdGhpcy5pbnZhbGlkYXRlU2l6ZSh7ZGVib3VuY2VNb3ZlZW5kOiB0cnVlfSk7IH0sIHRoaXMpO1xyXG5cdH0sXHJcblxyXG5cdF9vblNjcm9sbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fY29udGFpbmVyLnNjcm9sbFRvcCAgPSAwO1xyXG5cdFx0dGhpcy5fY29udGFpbmVyLnNjcm9sbExlZnQgPSAwO1xyXG5cdH0sXHJcblxyXG5cdF9vbk1vdmVFbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBwb3MgPSB0aGlzLl9nZXRNYXBQYW5lUG9zKCk7XHJcblx0XHRpZiAoTWF0aC5tYXgoTWF0aC5hYnMocG9zLngpLCBNYXRoLmFicyhwb3MueSkpID49IHRoaXMub3B0aW9ucy50cmFuc2Zvcm0zRExpbWl0KSB7XHJcblx0XHRcdC8vIGh0dHBzOi8vYnVnemlsbGEubW96
14775aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTEyMDM4NzMgYnV0IFdlYmtpdCBhbHNvIGhhdmVcclxuXHRcdFx0Ly8gYSBwaXhlbCBvZmZzZXQgb24gdmVyeSBoaWdoIHZhbHVlcywgc2VlOiBodHRwOi8vanNmaWRkbGUubmV0L2RnNnI1aGhiL1xyXG5cdFx0XHR0aGlzLl9yZXNldFZpZXcodGhpcy5nZXRDZW50ZXIoKSwgdGhpcy5nZXRab29tKCkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9maW5kRXZlbnRUYXJnZXRzOiBmdW5jdGlvbiAoZSwgdHlwZSkge1xyXG5cdFx0dmFyIHRhcmdldHMgPSBbXSxcclxuXHRcdCAgICB0YXJnZXQsXHJcblx0XHQgICAgaXNIb3ZlciA9IHR5cGUgPT09ICdtb3VzZW91dCcgfHwgdHlwZSA9PT0gJ21vdXNlb3ZlcicsXHJcblx0XHQgICAgc3JjID0gZS50YXJnZXQgfHwgZS5zcmNFbGVtZW50LFxyXG5cdFx0ICAgIGRyYWdnaW5nID0gZmFsc2U7XHJcblxyXG5cdFx0d2hpbGUgKHNyYykge1xyXG5cdFx0XHR0YXJnZXQgPSB0aGlzLl90YXJnZXRzW3N0YW1wKHNyYyldO1xyXG5cdFx0XHRpZiAodGFyZ2V0ICYmICh0eXBlID09PSAnY2xpY2snIHx8IHR5cGUgPT09ICdwcmVjbGljaycpICYmICFlLl9zaW11bGF0ZWQgJiYgdGhpcy5fZHJhZ2dhYmxlTW92ZWQodGFyZ2V0KSkge1xyXG5cdFx0XHRcdC8vIFByZXZlbnQgZmlyaW5nIGNsaWNrIGFmdGVyIHlvdSBqdXN0IGRyYWdnZWQgYW4gb2JqZWN0LlxyXG5cdFx0XHRcdGRyYWdnaW5nID0gdHJ1ZTtcclxuXHRcdFx0XHRicmVhaztcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAodGFyZ2V0ICYmIHRhcmdldC5saXN0ZW5zKHR5cGUsIHRydWUpKSB7XHJcblx0XHRcdFx0aWYgKGlzSG92ZXIgJiYgIWlzRXh0ZXJuYWxUYXJnZXQoc3JjLCBlKSkgeyBicmVhazsgfVxyXG5cdFx0XHRcdHRhcmdldHMucHVzaCh0YXJnZXQpO1xyXG5cdFx0XHRcdGlmIChpc0hvdmVyKSB7IGJyZWFrOyB9XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKHNyYyA9PT0gdGhpcy5fY29udGFpbmVyKSB7IGJyZWFrOyB9XHJcblx0XHRcdHNyYyA9IHNyYy5wYXJlbnROb2RlO1xyXG5cdFx0fVxyXG5cdFx0aWYgKCF0YXJnZXRzLmxlbmd0aCAmJiAhZHJhZ2dpbmcgJiYgIWlzSG92ZXIgJiYgaXNFeHRlcm5hbFRhcmdldChzcmMsIGUpKSB7XHJcblx0XHRcdHRhcmdldHMgPSBbdGhpc107XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGFyZ2V0cztcclxuXHR9LFxyXG5cclxuXHRfaGFuZGxlRE9NRXZlbnQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCB8fCBza2lwcGVkKGUpKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciB0eXBlID0gZS50eXBlO1xyXG5cclxuXHRcdGlmICh0eXBlID09PSAnbW91c2Vkb3duJyB8fCB0eXBlID09PSAna2V5cHJlc3MnIHx8IHR5cGUgPT09ICdrZXl1cCcgfHwgdHlwZSA9PT0gJ2tleWRvd24nKSB7XHJcblx0XHRcdC8vIHByZXZlbnRzIG91dGxpbmUgd2hlbiBjbGlja2luZyBvbiBrZXlib2FyZC1mb2N1c2FibGUgZWxlbWVudFxyXG5cdFx0XHRwcmV2ZW50T3V0bGluZShlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ZpcmVET01FdmVudChlLCB0eXBlKTtcclxuXHR9LFxyXG5cclxuXHRfbW91c2VFdmVudHM6IFsnY2xpY2snLCAnZGJsY2xpY2snLCAnbW91c2VvdmVyJywgJ21vdXNlb3V0JywgJ2NvbnRleHRtZW51J10sXHJcblxyXG5cdF9maXJlRE9NRXZlbnQ6IGZ1bmN0aW9uIChlLCB0eXBlLCB0YXJnZXRzKSB7XHJcblxyXG5cdFx0aWYgKGUudHlwZSA9PT0gJ2NsaWNrJykge1xyXG5cdFx0XHQvLyBGaXJlIGEgc3ludGhldGljICdwcmVjbGljaycgZXZlbnQgd2hpY2ggcHJvcGFnYXRlcyB1cCAobWFpbmx5IGZvciBjbG9zaW5nIHBvcHVwcykuXHJcblx0XHRcdC8vIEBldmVudCBwcmVjbGljazogTW91c2VFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCBiZWZvcmUgbW91c2UgY2xpY2sgb24gdGhlIG1hcCAoc29tZXRpbWVzIHVzZWZ1bCB3aGVuIHlvdVxyXG5cdFx0XHQvLyB3YW50IHNvbWV0aGluZyB0byBoYXBwZW4gb24gY2xpY2sgYmVmb3JlIGFueSBleGlzdGluZyBjbGlja1xyXG5cdFx0XHQvLyBoYW5kbGVycyBzdGFydCBydW5uaW5nKS5cclxuXHRcdFx0dmFyIHN5bnRoID0gZXh0ZW5kKHt9LCBlKTtcclxuXHRcdFx0c3ludGgudHlwZSA9ICdwcmVjbGljayc7XHJcblx0XHRcdHRoaXMuX2ZpcmVET01FdmVudChzeW50aCwgc3ludGgudHlwZSwgdGFyZ2V0cyk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGUuX3N0b3BwZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gRmluZCB0aGUgbGF5ZXIgdGhlIGV2ZW50IGlzIHByb3BhZ2F0aW5nIGZyb20gYW5kIGl0cyBwYXJlbnRzLlxyXG5cdFx0dGFyZ2V0cyA9ICh0YXJnZXRzIHx8IFtdKS5jb25jYXQodGhpcy5fZmluZEV2ZW50VGFyZ2V0cyhlLCB0eXBlKSk7XHJcblxyXG5cdFx0aWYgKCF0YXJnZXRzLmxlbmd0aCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgdGFyZ2V0ID0gdGFyZ2V0c1swXTtcclxuXHRcdGlmICh0eXBlID09PSAnY29udGV4dG1lbnUnICYmIHRhcmdldC5saXN0ZW5zKHR5cGUsIHRydWUpKSB7XHJcblx0XHRcdHByZXZlbnREZWZhdWx0KGUpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBkYXRhID0ge1xyXG5cdFx0XHRvcmlnaW5hbEV2ZW50OiBlXHJcblx0XHR9O1xyXG5cclxuXHRcdGlmIChlLnR5cGUgIT09ICdrZXlwcmVzcycgJiYgZS50eXBlICE9PSAna2V5ZG93bicgJiYgZS50eXBlICE9PSAna2V5dXAnKSB7XHJcblx0XHRcdHZhciBpc01hcmtlciA9IHRhcmdldC5nZXRMYXRMbmcgJiYgKCF0YXJnZXQuX3JhZGl1cyB8fCB0YXJnZXQuX3JhZGl1cyA8PSAxMCk7XHJcblx0XHRcdGRhdGEuY29udGFpbmVyUG9pbnQgPSBpc01hcmtlciA/XHJcblx0XHRcdFx0dGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KHRhcmdldC5nZXRMYXRMbmcoKSkgOiB0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xyXG5cdFx0XHRkYXRhLmxheWVyUG9pbnQgPSB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGRhdGEuY29udGFpbmVyUG9pbnQpO1xyXG5cdFx0XHRkYXRhLmxhdGxuZyA9IGlzTWFya2VyID8gdGFyZ2V0LmdldExhdExuZygpIDogdGhpcy5sYXllclBvaW50VG9MYXRMbmcoZGF0YS5sYXllclBvaW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGFyZ2V0c1tpXS5maXJlKHR5cGUsIGRhdGEsIHRydWUpO1xyXG5cdFx0XHRpZiAoZGF0YS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkIHx8XHJcblx0XHRcdFx0KHRhcmdldHNbaV0ub3B0aW9ucy5idWJibGluZ01vdXNlRXZlbnRzID09PSBmYWxzZSAmJiBpbmRleE9mKHRoaXMuX21vdXNlRXZlbnRzLCB0eXBlKSAhPT0gLTEpKSB7IHJldHVybjsgfVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9kcmFnZ2FibGVNb3ZlZDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0b2JqID0gb2JqLmRyYWdnaW5nICYmIG9iai5kcmFnZ2luZy5lbmFibGVkKCkgPyBvYmogOiB0aGlzO1xyXG5cdFx0cmV0dXJuIChvYmouZHJhZ2dpbmcgJiYgb2JqLmRyYWdnaW5nLm1vdmVkKCkpIHx8ICh0aGlzLmJveFpvb20gJiYgdGhpcy5ib3hab29tLm1vdmVkKCkpO1xyXG5cdH0sXHJcblxyXG5cdF9jbGVhckhhbmRsZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5faGFuZGxlcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0dGhpcy5faGFuZGxlcnNbaV0uZGlzYWJsZSgpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCB3aGVuUmVhZHkoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGh
14775pc1xyXG5cdC8vIFJ1bnMgdGhlIGdpdmVuIGZ1bmN0aW9uIGBmbmAgd2hlbiB0aGUgbWFwIGdldHMgaW5pdGlhbGl6ZWQgd2l0aFxyXG5cdC8vIGEgdmlldyAoY2VudGVyIGFuZCB6b29tKSBhbmQgYXQgbGVhc3Qgb25lIGxheWVyLCBvciBpbW1lZGlhdGVseVxyXG5cdC8vIGlmIGl0J3MgYWxyZWFkeSBpbml0aWFsaXplZCwgb3B0aW9uYWxseSBwYXNzaW5nIGEgZnVuY3Rpb24gY29udGV4dC5cclxuXHR3aGVuUmVhZHk6IGZ1bmN0aW9uIChjYWxsYmFjaywgY29udGV4dCkge1xyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHRjYWxsYmFjay5jYWxsKGNvbnRleHQgfHwgdGhpcywge3RhcmdldDogdGhpc30pO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dGhpcy5vbignbG9hZCcsIGNhbGxiYWNrLCBjb250ZXh0KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBwcml2YXRlIG1ldGhvZHMgZm9yIGdldHRpbmcgbWFwIHN0YXRlXHJcblxyXG5cdF9nZXRNYXBQYW5lUG9zOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gZ2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSkgfHwgbmV3IFBvaW50KDAsIDApO1xyXG5cdH0sXHJcblxyXG5cdF9tb3ZlZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKTtcclxuXHRcdHJldHVybiBwb3MgJiYgIXBvcy5lcXVhbHMoWzAsIDBdKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0VG9wTGVmdFBvaW50OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgcGl4ZWxPcmlnaW4gPSBjZW50ZXIgJiYgem9vbSAhPT0gdW5kZWZpbmVkID9cclxuXHRcdFx0dGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKSA6XHJcblx0XHRcdHRoaXMuZ2V0UGl4ZWxPcmlnaW4oKTtcclxuXHRcdHJldHVybiBwaXhlbE9yaWdpbi5zdWJ0cmFjdCh0aGlzLl9nZXRNYXBQYW5lUG9zKCkpO1xyXG5cdH0sXHJcblxyXG5cdF9nZXROZXdQaXhlbE9yaWdpbjogZnVuY3Rpb24gKGNlbnRlciwgem9vbSkge1xyXG5cdFx0dmFyIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuX2RpdmlkZUJ5KDIpO1xyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdChjZW50ZXIsIHpvb20pLl9zdWJ0cmFjdCh2aWV3SGFsZikuX2FkZCh0aGlzLl9nZXRNYXBQYW5lUG9zKCkpLl9yb3VuZCgpO1xyXG5cdH0sXHJcblxyXG5cdF9sYXRMbmdUb05ld0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20sIGNlbnRlcikge1xyXG5cdFx0dmFyIHRvcExlZnQgPSB0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIsIHpvb20pO1xyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdChsYXRsbmcsIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KTtcclxuXHR9LFxyXG5cclxuXHRfbGF0TG5nQm91bmRzVG9OZXdMYXllckJvdW5kczogZnVuY3Rpb24gKGxhdExuZ0JvdW5kcywgem9vbSwgY2VudGVyKSB7XHJcblx0XHR2YXIgdG9wTGVmdCA9IHRoaXMuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gdG9Cb3VuZHMoW1xyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdCksXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhXZXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KSxcclxuXHRcdFx0dGhpcy5wcm9qZWN0KGxhdExuZ0JvdW5kcy5nZXRTb3V0aEVhc3QoKSwgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpLFxyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldE5vcnRoRWFzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdClcclxuXHRcdF0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIGxheWVyIHBvaW50IG9mIHRoZSBjdXJyZW50IGNlbnRlclxyXG5cdF9nZXRDZW50ZXJMYXllclBvaW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludCh0aGlzLmdldFNpemUoKS5fZGl2aWRlQnkoMikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIG9mZnNldCBvZiB0aGUgc3BlY2lmaWVkIHBsYWNlIHRvIHRoZSBjdXJyZW50IGNlbnRlciBpbiBwaXhlbHNcclxuXHRfZ2V0Q2VudGVyT2Zmc2V0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nKS5zdWJ0cmFjdCh0aGlzLl9nZXRDZW50ZXJMYXllclBvaW50KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIGFkanVzdCBjZW50ZXIgZm9yIHZpZXcgdG8gZ2V0IGluc2lkZSBib3VuZHNcclxuXHRfbGltaXRDZW50ZXI6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIGJvdW5kcykge1xyXG5cclxuXHRcdGlmICghYm91bmRzKSB7IHJldHVybiBjZW50ZXI7IH1cclxuXHJcblx0XHR2YXIgY2VudGVyUG9pbnQgPSB0aGlzLnByb2plY3QoY2VudGVyLCB6b29tKSxcclxuXHRcdCAgICB2aWV3SGFsZiA9IHRoaXMuZ2V0U2l6ZSgpLmRpdmlkZUJ5KDIpLFxyXG5cdFx0ICAgIHZpZXdCb3VuZHMgPSBuZXcgQm91bmRzKGNlbnRlclBvaW50LnN1YnRyYWN0KHZpZXdIYWxmKSwgY2VudGVyUG9pbnQuYWRkKHZpZXdIYWxmKSksXHJcblx0XHQgICAgb2Zmc2V0ID0gdGhpcy5fZ2V0Qm91bmRzT2Zmc2V0KHZpZXdCb3VuZHMsIGJvdW5kcywgem9vbSk7XHJcblxyXG5cdFx0Ly8gSWYgb2Zmc2V0IGlzIGxlc3MgdGhhbiBhIHBpeGVsLCBpZ25vcmUuXHJcblx0XHQvLyBUaGlzIHByZXZlbnRzIHVuc3RhYmxlIHByb2plY3Rpb25zIGZyb20gZ2V0dGluZyBpbnRvXHJcblx0XHQvLyBhbiBpbmZpbml0ZSBsb29wIG9mIHRpbnkgb2Zmc2V0cy5cclxuXHRcdGlmIChvZmZzZXQucm91bmQoKS5lcXVhbHMoWzAsIDBdKSkge1xyXG5cdFx0XHRyZXR1cm4gY2VudGVyO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLnVucHJvamVjdChjZW50ZXJQb2ludC5hZGQob2Zmc2V0KSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gYWRqdXN0IG9mZnNldCBmb3IgdmlldyB0byBnZXQgaW5zaWRlIGJvdW5kc1xyXG5cdF9saW1pdE9mZnNldDogZnVuY3Rpb24gKG9mZnNldCwgYm91bmRzKSB7XHJcblx0XHRpZiAoIWJvdW5kcykgeyByZXR1cm4gb2Zmc2V0OyB9XHJcblxyXG5cdFx0dmFyIHZpZXdCb3VuZHMgPSB0aGlzLmdldFBpeGVsQm91bmRzKCksXHJcblx0XHQgICAgbmV3Qm91bmRzID0gbmV3IEJvdW5kcyh2aWV3Qm91bmRzLm1pbi5hZGQob2Zmc2V0KSwgdmlld0JvdW5kcy5tYXguYWRkKG9mZnNldCkpO1xyXG5cclxuXHRcdHJldHVybi
14775BvZmZzZXQuYWRkKHRoaXMuX2dldEJvdW5kc09mZnNldChuZXdCb3VuZHMsIGJvdW5kcykpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHJldHVybnMgb2Zmc2V0IG5lZWRlZCBmb3IgcHhCb3VuZHMgdG8gZ2V0IGluc2lkZSBtYXhCb3VuZHMgYXQgYSBzcGVjaWZpZWQgem9vbVxyXG5cdF9nZXRCb3VuZHNPZmZzZXQ6IGZ1bmN0aW9uIChweEJvdW5kcywgbWF4Qm91bmRzLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkTWF4Qm91bmRzID0gdG9Cb3VuZHMoXHJcblx0XHQgICAgICAgIHRoaXMucHJvamVjdChtYXhCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgICAgICB0aGlzLnByb2plY3QobWF4Qm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKVxyXG5cdFx0ICAgICksXHJcblx0XHQgICAgbWluT2Zmc2V0ID0gcHJvamVjdGVkTWF4Qm91bmRzLm1pbi5zdWJ0cmFjdChweEJvdW5kcy5taW4pLFxyXG5cdFx0ICAgIG1heE9mZnNldCA9IHByb2plY3RlZE1heEJvdW5kcy5tYXguc3VidHJhY3QocHhCb3VuZHMubWF4KSxcclxuXHJcblx0XHQgICAgZHggPSB0aGlzLl9yZWJvdW5kKG1pbk9mZnNldC54LCAtbWF4T2Zmc2V0LngpLFxyXG5cdFx0ICAgIGR5ID0gdGhpcy5fcmVib3VuZChtaW5PZmZzZXQueSwgLW1heE9mZnNldC55KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IFBvaW50KGR4LCBkeSk7XHJcblx0fSxcclxuXHJcblx0X3JlYm91bmQ6IGZ1bmN0aW9uIChsZWZ0LCByaWdodCkge1xyXG5cdFx0cmV0dXJuIGxlZnQgKyByaWdodCA+IDAgP1xyXG5cdFx0XHRNYXRoLnJvdW5kKGxlZnQgLSByaWdodCkgLyAyIDpcclxuXHRcdFx0TWF0aC5tYXgoMCwgTWF0aC5jZWlsKGxlZnQpKSAtIE1hdGgubWF4KDAsIE1hdGguZmxvb3IocmlnaHQpKTtcclxuXHR9LFxyXG5cclxuXHRfbGltaXRab29tOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0dmFyIG1pbiA9IHRoaXMuZ2V0TWluWm9vbSgpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuZ2V0TWF4Wm9vbSgpLFxyXG5cdFx0ICAgIHNuYXAgPSBhbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gc25hcCkgKiBzbmFwO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0X29uUGFuVHJhbnNpdGlvblN0ZXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cdH0sXHJcblxyXG5cdF9vblBhblRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJlbW92ZUNsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXBhbi1hbmltJyk7XHJcblx0XHR0aGlzLmZpcmUoJ21vdmVlbmQnKTtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRQYW46IGZ1bmN0aW9uIChjZW50ZXIsIG9wdGlvbnMpIHtcclxuXHRcdC8vIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbmV3IGFuZCBjdXJyZW50IGNlbnRlcnMgaW4gcGl4ZWxzXHJcblx0XHR2YXIgb2Zmc2V0ID0gdGhpcy5fZ2V0Q2VudGVyT2Zmc2V0KGNlbnRlcikuX3RydW5jKCk7XHJcblxyXG5cdFx0Ly8gZG9uJ3QgYW5pbWF0ZSB0b28gZmFyIHVubGVzcyBhbmltYXRlOiB0cnVlIHNwZWNpZmllZCBpbiBvcHRpb25zXHJcblx0XHRpZiAoKG9wdGlvbnMgJiYgb3B0aW9ucy5hbmltYXRlKSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHR0aGlzLnBhbkJ5KG9mZnNldCwgb3B0aW9ucyk7XHJcblxyXG5cdFx0cmV0dXJuIHRydWU7XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUFuaW1Qcm94eTogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdHZhciBwcm94eSA9IHRoaXMuX3Byb3h5ID0gY3JlYXRlJDEoJ2RpdicsICdsZWFmbGV0LXByb3h5IGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cdFx0dGhpcy5fcGFuZXMubWFwUGFuZS5hcHBlbmRDaGlsZChwcm94eSk7XHJcblxyXG5cdFx0dGhpcy5vbignem9vbWFuaW0nLCBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHR2YXIgcHJvcCA9IFRSQU5TRk9STSxcclxuXHRcdFx0ICAgIHRyYW5zZm9ybSA9IHRoaXMuX3Byb3h5LnN0eWxlW3Byb3BdO1xyXG5cclxuXHRcdFx0c2V0VHJhbnNmb3JtKHRoaXMuX3Byb3h5LCB0aGlzLnByb2plY3QoZS5jZW50ZXIsIGUuem9vbSksIHRoaXMuZ2V0Wm9vbVNjYWxlKGUuem9vbSwgMSkpO1xyXG5cclxuXHRcdFx0Ly8gd29ya2Fyb3VuZCBmb3IgY2FzZSB3aGVuIHRyYW5zZm9ybSBpcyB0aGUgc2FtZSBhbmQgc28gdHJhbnNpdGlvbmVuZCBldmVudCBpcyBub3QgZmlyZWRcclxuXHRcdFx0aWYgKHRyYW5zZm9ybSA9PT0gdGhpcy5fcHJveHkuc3R5bGVbcHJvcF0gJiYgdGhpcy5fYW5pbWF0aW5nWm9vbSkge1xyXG5cdFx0XHRcdHRoaXMuX29uWm9vbVRyYW5zaXRpb25FbmQoKTtcclxuXHRcdFx0fVxyXG5cdFx0fSwgdGhpcyk7XHJcblxyXG5cdFx0dGhpcy5vbignbG9hZCBtb3ZlZW5kJywgdGhpcy5fYW5pbU1vdmVFbmQsIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX29uKCd1bmxvYWQnLCB0aGlzLl9kZXN0cm95QW5pbVByb3h5LCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfZGVzdHJveUFuaW1Qcm94eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmVtb3ZlKHRoaXMuX3Byb3h5KTtcclxuXHRcdHRoaXMub2ZmKCdsb2FkIG1vdmVlbmQnLCB0aGlzLl9hbmltTW92ZUVuZCwgdGhpcyk7XHJcblx0XHRkZWxldGUgdGhpcy5fcHJveHk7XHJcblx0fSxcclxuXHJcblx0X2FuaW1Nb3ZlRW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgYyA9IHRoaXMuZ2V0Q2VudGVyKCksXHJcblx0XHQgICAgeiA9IHRoaXMuZ2V0Wm9vbSgpO1xyXG5cdFx0c2V0VHJhbnNmb3JtKHRoaXMuX3Byb3h5LCB0aGlzLnByb2plY3QoYywgeiksIHRoaXMuZ2V0Wm9vbVNjYWxlKHosIDEpKTtcclxuXHR9LFxyXG5cclxuXHRfY2F0Y2hUcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20gJiYgZS5wcm9wZXJ0eU5hbWUuaW5kZXhPZigndHJhbnNmb3JtJykgPj0gMCkge1xyXG5cdFx0XHR0aGlzLl9vblpvb21UcmFuc2l0aW9uRW5kKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X25vdGhpbmdUb0FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhdGhpcy5fY29udGFpbmVyLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpLmxlbmd0aDtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRab29tOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIGRpc2FibGVkLCBub3Qgc3VwcG9ydGVkIG9yIHpvb20gZGlmZmVyZW5jZSBpcyB0b28gbGFy
14775Z2VcclxuXHRcdGlmICghdGhpcy5fem9vbUFuaW1hdGVkIHx8IG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgdGhpcy5fbm90aGluZ1RvQW5pbWF0ZSgpIHx8XHJcblx0XHQgICAgICAgIE1hdGguYWJzKHpvb20gLSB0aGlzLl96b29tKSA+IHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uVGhyZXNob2xkKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdC8vIG9mZnNldCBpcyB0aGUgcGl4ZWwgY29vcmRzIG9mIHRoZSB6b29tIG9yaWdpbiByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBjZW50ZXJcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9kaXZpZGVCeSgxIC0gMSAvIHNjYWxlKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIHRoZSB6b29tIG9yaWdpbiBpc24ndCB3aXRoaW4gb25lIHNjcmVlbiBmcm9tIHRoZSBjdXJyZW50IGNlbnRlciwgdW5sZXNzIGZvcmNlZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRyZXF1ZXN0QW5pbUZyYW1lKGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0dGhpc1xyXG5cdFx0XHQgICAgLl9tb3ZlU3RhcnQodHJ1ZSwgZmFsc2UpXHJcblx0XHRcdCAgICAuX2FuaW1hdGVab29tKGNlbnRlciwgem9vbSwgdHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIHN0YXJ0QW5pbSwgbm9VcGRhdGUpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwUGFuZSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoc3RhcnRBbmltKSB7XHJcblx0XHRcdHRoaXMuX2FuaW1hdGluZ1pvb20gPSB0cnVlO1xyXG5cclxuXHRcdFx0Ly8gcmVtZW1iZXIgd2hhdCBjZW50ZXIvem9vbSB0byBzZXQgYWZ0ZXIgYW5pbWF0aW9uXHJcblx0XHRcdHRoaXMuX2FuaW1hdGVUb0NlbnRlciA9IGNlbnRlcjtcclxuXHRcdFx0dGhpcy5fYW5pbWF0ZVRvWm9vbSA9IHpvb207XHJcblxyXG5cdFx0XHRhZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAc2VjdGlvbiBPdGhlciBFdmVudHNcclxuXHRcdC8vIEBldmVudCB6b29tYW5pbTogWm9vbUFuaW1FdmVudFxyXG5cdFx0Ly8gRmlyZWQgYXQgbGVhc3Qgb25jZSBwZXIgem9vbSBhbmltYXRpb24uIEZvciBjb250aW51b3VzIHpvb20sIGxpa2UgcGluY2ggem9vbWluZywgZmlyZWQgb25jZSBwZXIgZnJhbWUgZHVyaW5nIHpvb20uXHJcblx0XHR0aGlzLmZpcmUoJ3pvb21hbmltJywge1xyXG5cdFx0XHRjZW50ZXI6IGNlbnRlcixcclxuXHRcdFx0em9vbTogem9vbSxcclxuXHRcdFx0bm9VcGRhdGU6IG5vVXBkYXRlXHJcblx0XHR9KTtcclxuXHJcblx0XHQvLyBXb3JrIGFyb3VuZCB3ZWJraXQgbm90IGZpcmluZyAndHJhbnNpdGlvbmVuZCcsIHNlZSBodHRwczovL2dpdGh1Yi5jb20vTGVhZmxldC9MZWFmbGV0L2lzc3Vlcy8zNjg5LCAyNjkzXHJcblx0XHRzZXRUaW1lb3V0KGJpbmQodGhpcy5fb25ab29tVHJhbnNpdGlvbkVuZCwgdGhpcyksIDI1MCk7XHJcblx0fSxcclxuXHJcblx0X29uWm9vbVRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAodGhpcy5fbWFwUGFuZSkge1xyXG5cdFx0XHRyZW1vdmVDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9hbmltYXRpbmdab29tID0gZmFsc2U7XHJcblxyXG5cdFx0dGhpcy5fbW92ZSh0aGlzLl9hbmltYXRlVG9DZW50ZXIsIHRoaXMuX2FuaW1hdGVUb1pvb20pO1xyXG5cclxuXHRcdC8vIFRoaXMgYW5pbSBmcmFtZSBzaG91bGQgcHJldmVudCBhbiBvYnNjdXJlIGlPUyB3ZWJraXQgdGlsZSBsb2FkaW5nIHJhY2UgY29uZGl0aW9uLlxyXG5cdFx0cmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXMuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQHNlY3Rpb25cclxuXHJcbi8vIEBmYWN0b3J5IEwubWFwKGlkOiBTdHJpbmcsIG9wdGlvbnM/OiBNYXAgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgbWFwIG9iamVjdCBnaXZlbiB0aGUgRE9NIElEIG9mIGEgYDxkaXY+YCBlbGVtZW50XHJcbi8vIGFuZCBvcHRpb25hbGx5IGFuIG9iamVjdCBsaXRlcmFsIHdpdGggYE1hcCBvcHRpb25zYC5cclxuLy9cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubWFwKGVsOiBIVE1MRWxlbWVudCwgb3B0aW9ucz86IE1hcCBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBtYXAgb2JqZWN0IGdpdmVuIGFuIGluc3RhbmNlIG9mIGEgYDxkaXY+YCBIVE1MIGVsZW1lbnRcclxuLy8gYW5kIG9wdGlvbmFsbHkgYW4gb2JqZWN0IGxpdGVyYWwgd2l0aCBgTWFwIG9wdGlvbnNgLlxyXG5mdW5jdGlvbiBjcmVhdGVNYXAoaWQsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IE1hcChpZCwgb3B0aW9ucyk7XHJcbn1cblxuLypcclxuICogQGNsYXNzIENvbnRyb2xcclxuICogQGFrYSBMLkNvbnRyb2xcclxuICogQGluaGVyaXRzIENsYXNzXHJcbiAqXHJcbiAqIEwuQ29udHJvbCBpcyBhIGJhc2UgY2xhc3MgZm9yIGltcGxlbWVudGluZyBtYXAgY29udHJvbHMuIEhhbmRsZXMgcG9zaXRpb25pbmcuXHJcbiAqIEFsbCBvdGhlciBjb250cm9scyBleHRlbmQgZnJvbSB0aGlzIGNsYXNzLlxyXG4gKi9cclxuXHJcbnZhciBDb250cm9sID0gQ2xhc3MuZXh0ZW5kKHtcclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgQ29udHJvbCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBwb3NpdGlvbjogU3RyaW5nID0gJ3RvcHJpZ2h0J1xyXG5cdFx0Ly8gVGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sIChvbmUgb2YgdGhlIG1hcCBjb3JuZXJzKS4gUG9zc2libGUgdmFsdWVzIGFyZSBgJ3RvcGxlZnQnYCxcclxuXHRcdC8vIGAndG9wcmlnaHQnYCwgYCdib3R0b21sZWZ0J2Agb3IgYCdib3R0b21yaWdodCdgXHJcblx0XHRwb3NpdGlvbjogJ3RvcHJpZ2h0J1xyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRzZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQ2xhc3NlcyBleHRlbmRpbmcgTC5Db250cm9sIHdpbGwgaW5oZXJpdCB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XHJcblx0ICpcclxuXHQgKiBAbWV0aG9kIGdldFBvc2l0aW9uOiBzdHJpbmdcclxuXHQgKiBSZXR1cm5zIHRoZSBwb3NpdGlvbiBvZiB0aGUgY29udHJvbC5cclxuXHQgKi9cclxuXHRnZXRQb3NpdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5wb3NpdGlvbjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBvc2l0aW9uKHBvc2l0aW9uOiBzdHJpbmcpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgcG9zaXRpb24gb2YgdGhlIGNvbnRyb2wuXHJcblx0c2V0UG9zaXRpb246IGZ1bmN0aW9uIChwb3NpdGlvbikge1xyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcDtcclxuXHJcblx0XHRpZiAobWFwKSB7XHJcblx0XHRcdG1hcC5yZW1vdmVDb250cm9sKHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMub3B0aW9ucy5wb3NpdGlvbiA9IHBvc2l0aW9uO1xyXG5cclxuXHRcdGlmIChtYXApIHtcclxuXHRcdFx0bWFwLmFkZENvbnRyb2wodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyB0aGUgSFRNTEVsZW1lbnQgdGhhdCBjb250YWlucyB0aGUgY29udHJvbC5cclxuXHRnZXRDb250YWluZXI6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybi
14775B0aGlzLl9jb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRUbyhtYXA6IE1hcCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBjb250cm9sIHRvIHRoZSBnaXZlbiBtYXAuXHJcblx0YWRkVG86IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMucmVtb3ZlKCk7XHJcblx0XHR0aGlzLl9tYXAgPSBtYXA7XHJcblxyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IHRoaXMub25BZGQobWFwKSxcclxuXHRcdCAgICBwb3MgPSB0aGlzLmdldFBvc2l0aW9uKCksXHJcblx0XHQgICAgY29ybmVyID0gbWFwLl9jb250cm9sQ29ybmVyc1twb3NdO1xyXG5cclxuXHRcdGFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbCcpO1xyXG5cclxuXHRcdGlmIChwb3MuaW5kZXhPZignYm90dG9tJykgIT09IC0xKSB7XHJcblx0XHRcdGNvcm5lci5pbnNlcnRCZWZvcmUoY29udGFpbmVyLCBjb3JuZXIuZmlyc3RDaGlsZCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRjb3JuZXIuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tYXAub24oJ3VubG9hZCcsIHRoaXMucmVtb3ZlLCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGNvbnRyb2wgZnJvbSB0aGUgbWFwIGl0IGlzIGN1cnJlbnRseSBhY3RpdmUgb24uXHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRyZW1vdmUodGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5vblJlbW92ZSkge1xyXG5cdFx0XHR0aGlzLm9uUmVtb3ZlKHRoaXMuX21hcCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbWFwLm9mZigndW5sb2FkJywgdGhpcy5yZW1vdmUsIHRoaXMpO1xyXG5cdFx0dGhpcy5fbWFwID0gbnVsbDtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcmVmb2N1c09uTWFwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gaWYgbWFwIGV4aXN0cyBhbmQgZXZlbnQgaXMgbm90IGEga2V5Ym9hcmQgZXZlbnRcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgZSAmJiBlLnNjcmVlblggPiAwICYmIGUuc2NyZWVuWSA+IDApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmdldENvbnRhaW5lcigpLmZvY3VzKCk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbnZhciBjb250cm9sID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IENvbnRyb2wob3B0aW9ucyk7XHJcbn07XHJcblxyXG4vKiBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xyXG4gKiBAdW5pbmhlcml0YWJsZVxyXG4gKlxyXG4gKiBFdmVyeSBjb250cm9sIHNob3VsZCBleHRlbmQgZnJvbSBgTC5Db250cm9sYCBhbmQgKHJlLSlpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2RzLlxyXG4gKlxyXG4gKiBAbWV0aG9kIG9uQWRkKG1hcDogTWFwKTogSFRNTEVsZW1lbnRcclxuICogU2hvdWxkIHJldHVybiB0aGUgY29udGFpbmVyIERPTSBlbGVtZW50IGZvciB0aGUgY29udHJvbCBhbmQgYWRkIGxpc3RlbmVycyBvbiByZWxldmFudCBtYXAgZXZlbnRzLiBDYWxsZWQgb24gW2Bjb250cm9sLmFkZFRvKG1hcClgXSgjY29udHJvbC1hZGRUbykuXHJcbiAqXHJcbiAqIEBtZXRob2Qgb25SZW1vdmUobWFwOiBNYXApXHJcbiAqIE9wdGlvbmFsIG1ldGhvZC4gU2hvdWxkIGNvbnRhaW4gYWxsIGNsZWFuIHVwIGNvZGUgdGhhdCByZW1vdmVzIHRoZSBsaXN0ZW5lcnMgcHJldmlvdXNseSBhZGRlZCBpbiBbYG9uQWRkYF0oI2NvbnRyb2wtb25hZGQpLiBDYWxsZWQgb24gW2Bjb250cm9sLnJlbW92ZSgpYF0oI2NvbnRyb2wtcmVtb3ZlKS5cclxuICovXHJcblxyXG4vKiBAbmFtZXNwYWNlIE1hcFxyXG4gKiBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXHJcbiAqL1xyXG5NYXAuaW5jbHVkZSh7XHJcblx0Ly8gQG1ldGhvZCBhZGRDb250cm9sKGNvbnRyb2w6IENvbnRyb2wpOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gY29udHJvbCB0byB0aGUgbWFwXHJcblx0YWRkQ29udHJvbDogZnVuY3Rpb24gKGNvbnRyb2wpIHtcclxuXHRcdGNvbnRyb2wuYWRkVG8odGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUNvbnRyb2woY29udHJvbDogQ29udHJvbCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBjb250cm9sIGZyb20gdGhlIG1hcFxyXG5cdHJlbW92ZUNvbnRyb2w6IGZ1bmN0aW9uIChjb250cm9sKSB7XHJcblx0XHRjb250cm9sLnJlbW92ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2luaXRDb250cm9sUG9zOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29ybmVycyA9IHRoaXMuX2NvbnRyb2xDb3JuZXJzID0ge30sXHJcblx0XHQgICAgbCA9ICdsZWFmbGV0LScsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udHJvbENvbnRhaW5lciA9XHJcblx0XHQgICAgICAgICAgICBjcmVhdGUkMSgnZGl2JywgbCArICdjb250cm9sLWNvbnRhaW5lcicsIHRoaXMuX2NvbnRhaW5lcik7XHJcblxyXG5cdFx0ZnVuY3Rpb24gY3JlYXRlQ29ybmVyKHZTaWRlLCBoU2lkZSkge1xyXG5cdFx0XHR2YXIgY2xhc3NOYW1lID0gbCArIHZTaWRlICsgJyAnICsgbCArIGhTaWRlO1xyXG5cclxuXHRcdFx0Y29ybmVyc1t2U2lkZSArIGhTaWRlXSA9IGNyZWF0ZSQxKCdkaXYnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHR9XHJcblxyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAnbGVmdCcpO1xyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAncmlnaHQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ2xlZnQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ3JpZ2h0Jyk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFyQ29udHJvbFBvczogZnVuY3Rpb24gKCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9jb250cm9sQ29ybmVycykge1xyXG5cdFx0XHRyZW1vdmUodGhpcy5fY29udHJvbENvcm5lcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmVtb3ZlKHRoaXMuX2NvbnRyb2xDb250YWluZXIpO1xyXG5cdFx0ZGVsZXRlIHRoaXMuX2NvbnRyb2xDb3JuZXJzO1xyXG5cdFx0ZGVsZXRlIHRoaXMuX2NvbnRyb2xDb250YWluZXI7XHJcblx0fVxyXG59KTtcblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuTGF5ZXJzXHJcbiAqIEBha2EgTC5Db250cm9sLkxheWVyc1xyXG4gKiBAaW5oZXJpdHMgQ29udHJvbFxyXG4gKlxyXG4gKiBUaGUgbGF5ZXJzIGNvbnRyb2wgZ2l2ZXMgdXNlcnMgdGhlIGFiaWxpdHkgdG8gc3dpdGNoIGJldHdlZW4gZGlmZmVyZW50IGJhc2UgbGF5ZXJzIGFuZCBzd2l0Y2ggb3ZlcmxheXMgb24vb2ZmIChjaGVjayBvdXQgdGhlIFtkZXRhaWxlZCBleGFtcGxlXShodHRwOi8vbGVhZmxldGpzLmNvbS9leGFtcGxlcy9sYXllcnMtY29udHJvbC8pKS4gRXh0ZW5kcyBgQ29udHJvbGAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBiYXNlTGF5ZXJzID0ge1xyXG4gKiBcdFwiTWFwYm94XCI6IG1hcGJveCxcclxuICogXHRcIk9wZW5TdHJlZXRNYXBcIjogb3NtXHJcbiAqIH07XHJcbiAqXHJcbiAqIHZhciBvdmVybGF5cyA9IHtcclxuICogXHRcIk1hcmtlclwiOiBtYXJrZXIsXHJcbiAqIFx0XCJSb2Fkc1wiOiByb2Fkc0xheWVyXHJcbiAqIH07XHJcbiAqXHJcbiAqIEwuY29udHJvbC5sYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMpLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBUaGUgYGJhc2VMYXllcnNgIGFuZCBgb3ZlcmxheXNgIHBhcmFtZXRlcnMgYXJlIG9iamVjdCBsaXRlcmFscyB3aXRoIGxheWVyIG5hbWVzIGFzIGtleXMgYW5kIGBMYXllcmAgb2JqZWN0cyBhcyB2YWx1ZXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcclxuICogICAgIFwiPHNvbWVOYW1lMT5cIjogbGF5ZXIxLFxyXG4gKiAgICAgXCI8c29tZU5hbWUyPlwiOiBsYXllcjJcclxuICogfVxyXG4gKiBgYGBcclxuICpcclxuICogVGhlIGxheWVyIG5hbWVzIGNhbiBjb250YWluIEhUTUwsIHdoaWNoIGFsbG93cyB5b3UgdG8gYWRkIGFkZGl0aW9uYWwgc3R5bGluZyB0byB0aGUgaXRlbXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcIjxpbWcgc3JjPSdteS1sYXllci1pY29uJyAvPiA8c3BhbiBjbGFzcz0nbXktbGF
147755ZXItaXRlbSc+TXkgTGF5ZXI8L3NwYW4+XCI6IG15TGF5ZXJ9XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbnZhciBMYXllcnMgPSBDb250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuTGF5ZXJzIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGNvbGxhcHNlZDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBiZSBjb2xsYXBzZWQgaW50byBhbiBpY29uIGFuZCBleHBhbmRlZCBvbiBtb3VzZSBob3ZlciBvciB0b3VjaC5cclxuXHRcdGNvbGxhcHNlZDogdHJ1ZSxcclxuXHRcdHBvc2l0aW9uOiAndG9wcmlnaHQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1pJbmRleDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBhc3NpZ24gekluZGV4ZXMgaW4gaW5jcmVhc2luZyBvcmRlciB0byBhbGwgb2YgaXRzIGxheWVycyBzbyB0aGF0IHRoZSBvcmRlciBpcyBwcmVzZXJ2ZWQgd2hlbiBzd2l0Y2hpbmcgdGhlbSBvbi9vZmYuXHJcblx0XHRhdXRvWkluZGV4OiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gaGlkZVNpbmdsZUJhc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgYmFzZSBsYXllcnMgaW4gdGhlIGNvbnRyb2wgd2lsbCBiZSBoaWRkZW4gd2hlbiB0aGVyZSBpcyBvbmx5IG9uZS5cclxuXHRcdGhpZGVTaW5nbGVCYXNlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHNvcnRMYXllcnM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0byBzb3J0IHRoZSBsYXllcnMuIFdoZW4gYGZhbHNlYCwgbGF5ZXJzIHdpbGwga2VlcCB0aGUgb3JkZXJcclxuXHRcdC8vIGluIHdoaWNoIHRoZXkgd2VyZSBhZGRlZCB0byB0aGUgY29udHJvbC5cclxuXHRcdHNvcnRMYXllcnM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gc29ydEZ1bmN0aW9uOiBGdW5jdGlvbiA9ICpcclxuXHRcdC8vIEEgW2NvbXBhcmUgZnVuY3Rpb25dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L3NvcnQpXHJcblx0XHQvLyB0aGF0IHdpbGwgYmUgdXNlZCBmb3Igc29ydGluZyB0aGUgbGF5ZXJzLCB3aGVuIGBzb3J0TGF5ZXJzYCBpcyBgdHJ1ZWAuXHJcblx0XHQvLyBUaGUgZnVuY3Rpb24gcmVjZWl2ZXMgYm90aCB0aGUgYEwuTGF5ZXJgIGluc3RhbmNlcyBhbmQgdGhlaXIgbmFtZXMsIGFzIGluXHJcblx0XHQvLyBgc29ydEZ1bmN0aW9uKGxheWVyQSwgbGF5ZXJCLCBuYW1lQSwgbmFtZUIpYC5cclxuXHRcdC8vIEJ5IGRlZmF1bHQsIGl0IHNvcnRzIGxheWVycyBhbHBoYWJldGljYWxseSBieSB0aGVpciBuYW1lLlxyXG5cdFx0c29ydEZ1bmN0aW9uOiBmdW5jdGlvbiAobGF5ZXJBLCBsYXllckIsIG5hbWVBLCBuYW1lQikge1xyXG5cdFx0XHRyZXR1cm4gbmFtZUEgPCBuYW1lQiA/IC0xIDogKG5hbWVCIDwgbmFtZUEgPyAxIDogMCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGJhc2VMYXllcnMsIG92ZXJsYXlzLCBvcHRpb25zKSB7XHJcblx0XHRzZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVyQ29udHJvbElucHV0cyA9IFtdO1xyXG5cdFx0dGhpcy5fbGF5ZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXN0WkluZGV4ID0gMDtcclxuXHRcdHRoaXMuX2hhbmRsaW5nQ2xpY2sgPSBmYWxzZTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIGJhc2VMYXllcnMpIHtcclxuXHRcdFx0dGhpcy5fYWRkTGF5ZXIoYmFzZUxheWVyc1tpXSwgaSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yIChpIGluIG92ZXJsYXlzKSB7XHJcblx0XHRcdHRoaXMuX2FkZExheWVyKG92ZXJsYXlzW2ldLCBpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblxyXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xyXG5cdFx0bWFwLm9uKCd6b29tZW5kJywgdGhpcy5fY2hlY2tEaXNhYmxlZExheWVycywgdGhpcyk7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGhpcy5fbGF5ZXJzW2ldLmxheWVyLm9uKCdhZGQgcmVtb3ZlJywgdGhpcy5fb25MYXllckNoYW5nZSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHRhZGRUbzogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0Q29udHJvbC5wcm90b3R5cGUuYWRkVG8uY2FsbCh0aGlzLCBtYXApO1xyXG5cdFx0Ly8gVHJpZ2dlciBleHBhbmQgYWZ0ZXIgTGF5ZXJzIENvbnRyb2wgaGFzIGJlZW4gaW5zZXJ0ZWQgaW50byBET00gc28gdGhhdCBpcyBub3cgaGFzIGFuIGFjdHVhbCBoZWlnaHQuXHJcblx0XHRyZXR1cm4gdGhpcy5fZXhwYW5kSWZOb3RDb2xsYXBzZWQoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fbWFwLm9mZignem9vbWVuZCcsIHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMsIHRoaXMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX2xheWVyc1tpXS5sYXllci5vZmYoJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEJhc2VMYXllcihsYXllcjogTGF5ZXIsIG5hbWU6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGEgYmFzZSBsYXllciAocmFkaW8gYnV0dG9uIGVudHJ5KSB3aXRoIHRoZSBnaXZlbiBuYW1lIHRvIHRoZSBjb250cm9sLlxyXG5cdGFkZEJhc2VMYXllcjogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRPdmVybGF5KGxheWVyOiBMYXllciwgbmFtZTogU3RyaW5nKTogdGh
14775pc1xyXG5cdC8vIEFkZHMgYW4gb3ZlcmxheSAoY2hlY2tib3ggZW50cnkpIHdpdGggdGhlIGdpdmVuIG5hbWUgdG8gdGhlIGNvbnRyb2wuXHJcblx0YWRkT3ZlcmxheTogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSwgdHJ1ZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBjb250cm9sLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGxheWVyLm9mZignYWRkIHJlbW92ZScsIHRoaXMuX29uTGF5ZXJDaGFuZ2UsIHRoaXMpO1xyXG5cclxuXHRcdHZhciBvYmogPSB0aGlzLl9nZXRMYXllcihzdGFtcChsYXllcikpO1xyXG5cdFx0aWYgKG9iaikge1xyXG5cdFx0XHR0aGlzLl9sYXllcnMuc3BsaWNlKHRoaXMuX2xheWVycy5pbmRleE9mKG9iaiksIDEpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXhwYW5kKCk6IHRoaXNcclxuXHQvLyBFeHBhbmQgdGhlIGNvbnRyb2wgY29udGFpbmVyIGlmIGNvbGxhcHNlZC5cclxuXHRleHBhbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHRoaXMuX3NlY3Rpb24uc3R5bGUuaGVpZ2h0ID0gbnVsbDtcclxuXHRcdHZhciBhY2NlcHRhYmxlSGVpZ2h0ID0gdGhpcy5fbWFwLmdldFNpemUoKS55IC0gKHRoaXMuX2NvbnRhaW5lci5vZmZzZXRUb3AgKyA1MCk7XHJcblx0XHRpZiAoYWNjZXB0YWJsZUhlaWdodCA8IHRoaXMuX3NlY3Rpb24uY2xpZW50SGVpZ2h0KSB7XHJcblx0XHRcdGFkZENsYXNzKHRoaXMuX3NlY3Rpb24sICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzLXNjcm9sbGJhcicpO1xyXG5cdFx0XHR0aGlzLl9zZWN0aW9uLnN0eWxlLmhlaWdodCA9IGFjY2VwdGFibGVIZWlnaHQgKyAncHgnO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmVtb3ZlQ2xhc3ModGhpcy5fc2VjdGlvbiwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtc2Nyb2xsYmFyJyk7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbGxhcHNlKCk6IHRoaXNcclxuXHQvLyBDb2xsYXBzZSB0aGUgY29udHJvbCBjb250YWluZXIgaWYgZXhwYW5kZWQuXHJcblx0Y29sbGFwc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9pbml0TGF5b3V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtY29udHJvbC1sYXllcnMnLFxyXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGNyZWF0ZSQxKCdkaXYnLCBjbGFzc05hbWUpLFxyXG5cdFx0ICAgIGNvbGxhcHNlZCA9IHRoaXMub3B0aW9ucy5jb2xsYXBzZWQ7XHJcblxyXG5cdFx0Ly8gbWFrZXMgdGhpcyB3b3JrIG9uIElFIHRvdWNoIGRldmljZXMgYnkgc3RvcHBpbmcgaXQgZnJvbSBmaXJpbmcgYSBtb3VzZW91dCBldmVudCB3aGVuIHRoZSB0b3VjaCBpcyByZWxlYXNlZFxyXG5cdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnYXJpYS1oYXNwb3B1cCcsIHRydWUpO1xyXG5cclxuXHRcdGRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKGNvbnRhaW5lcik7XHJcblx0XHRkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24oY29udGFpbmVyKTtcclxuXHJcblx0XHR2YXIgc2VjdGlvbiA9IHRoaXMuX3NlY3Rpb24gPSBjcmVhdGUkMSgnc2VjdGlvbicsIGNsYXNzTmFtZSArICctbGlzdCcpO1xyXG5cclxuXHRcdGlmIChjb2xsYXBzZWQpIHtcclxuXHRcdFx0dGhpcy5fbWFwLm9uKCdjbGljaycsIHRoaXMuY29sbGFwc2UsIHRoaXMpO1xyXG5cclxuXHRcdFx0aWYgKCFhbmRyb2lkKSB7XHJcblx0XHRcdFx0b24oY29udGFpbmVyLCB7XHJcblx0XHRcdFx0XHRtb3VzZWVudGVyOiB0aGlzLmV4cGFuZCxcclxuXHRcdFx0XHRcdG1vdXNlbGVhdmU6IHRoaXMuY29sbGFwc2VcclxuXHRcdFx0XHR9LCB0aGlzKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBsaW5rID0gdGhpcy5fbGF5ZXJzTGluayA9IGNyZWF0ZSQxKCdhJywgY2xhc3NOYW1lICsgJy10b2dnbGUnLCBjb250YWluZXIpO1xyXG5cdFx0bGluay5ocmVmID0gJyMnO1xyXG5cdFx0bGluay50aXRsZSA9ICdMYXllcnMnO1xyXG5cclxuXHRcdGlmICh0b3VjaCkge1xyXG5cdFx0XHRvbihsaW5rLCAnY2xpY2snLCBzdG9wKTtcclxuXHRcdFx0b24obGluaywgJ2NsaWNrJywgdGhpcy5leHBhbmQsIHRoaXMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b24obGluaywgJ2ZvY3VzJywgdGhpcy5leHBhbmQsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghY29sbGFwc2VkKSB7XHJcblx0XHRcdHRoaXMuZXhwYW5kKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fYmFzZUxheWVyc0xpc3QgPSBjcmVhdGUkMSgnZGl2JywgY2xhc3NOYW1lICsgJy1iYXNlJywgc2VjdGlvbik7XHJcblx0XHR0aGlzLl9zZXBhcmF0b3IgPSBjcmVhdGUkMSgnZGl2JywgY2xhc3NOYW1lICsgJy1zZXBhcmF0b3InLCBzZWN0aW9uKTtcclxuXHRcdHRoaXMuX292ZXJsYXlzTGlzdCA9IGNyZWF0ZSQxKCdkaXYnLCBjbGFzc05hbWUgKyAnLW92ZXJsYXlzJywgc2VjdGlvbik7XHJcblxyXG5cdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKHNlY3Rpb24pO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRMYXllcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuX2xheWVycy5sZW5ndGg7IGkrKykge1xyXG5cclxuXHRcdFx0aWYgKHRoaXMuX2xheWVyc1tpXSAmJi
14775BzdGFtcCh0aGlzLl9sYXllcnNbaV0ubGF5ZXIpID09PSBpZCkge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLl9sYXllcnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSwgb3ZlcmxheSkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRsYXllci5vbignYWRkIHJlbW92ZScsIHRoaXMuX29uTGF5ZXJDaGFuZ2UsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2xheWVycy5wdXNoKHtcclxuXHRcdFx0bGF5ZXI6IGxheWVyLFxyXG5cdFx0XHRuYW1lOiBuYW1lLFxyXG5cdFx0XHRvdmVybGF5OiBvdmVybGF5XHJcblx0XHR9KTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLnNvcnRMYXllcnMpIHtcclxuXHRcdFx0dGhpcy5fbGF5ZXJzLnNvcnQoYmluZChmdW5jdGlvbiAoYSwgYikge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc29ydEZ1bmN0aW9uKGEubGF5ZXIsIGIubGF5ZXIsIGEubmFtZSwgYi5uYW1lKTtcclxuXHRcdFx0fSwgdGhpcykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuYXV0b1pJbmRleCAmJiBsYXllci5zZXRaSW5kZXgpIHtcclxuXHRcdFx0dGhpcy5fbGFzdFpJbmRleCsrO1xyXG5cdFx0XHRsYXllci5zZXRaSW5kZXgodGhpcy5fbGFzdFpJbmRleCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fZXhwYW5kSWZOb3RDb2xsYXBzZWQoKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGVtcHR5KHRoaXMuX2Jhc2VMYXllcnNMaXN0KTtcclxuXHRcdGVtcHR5KHRoaXMuX292ZXJsYXlzTGlzdCk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJDb250cm9sSW5wdXRzID0gW107XHJcblx0XHR2YXIgYmFzZUxheWVyc1ByZXNlbnQsIG92ZXJsYXlzUHJlc2VudCwgaSwgb2JqLCBiYXNlTGF5ZXJzQ291bnQgPSAwO1xyXG5cclxuXHRcdGZvciAoaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0b2JqID0gdGhpcy5fbGF5ZXJzW2ldO1xyXG5cdFx0XHR0aGlzLl9hZGRJdGVtKG9iaik7XHJcblx0XHRcdG92ZXJsYXlzUHJlc2VudCA9IG92ZXJsYXlzUHJlc2VudCB8fCBvYmoub3ZlcmxheTtcclxuXHRcdFx0YmFzZUxheWVyc1ByZXNlbnQgPSBiYXNlTGF5ZXJzUHJlc2VudCB8fCAhb2JqLm92ZXJsYXk7XHJcblx0XHRcdGJhc2VMYXllcnNDb3VudCArPSAhb2JqLm92ZXJsYXkgPyAxIDogMDtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBIaWRlIGJhc2UgbGF5ZXJzIHNlY3Rpb24gaWYgdGhlcmUncyBvbmx5IG9uZSBsYXllci5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuaGlkZVNpbmdsZUJhc2UpIHtcclxuXHRcdFx0YmFzZUxheWVyc1ByZXNlbnQgPSBiYXNlTGF5ZXJzUHJlc2VudCAmJiBiYXNlTGF5ZXJzQ291bnQgPiAxO1xyXG5cdFx0XHR0aGlzLl9iYXNlTGF5ZXJzTGlzdC5zdHlsZS5kaXNwbGF5ID0gYmFzZUxheWVyc1ByZXNlbnQgPyAnJyA6ICdub25lJztcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9zZXBhcmF0b3Iuc3R5bGUuZGlzcGxheSA9IG92ZXJsYXlzUHJlc2VudCAmJiBiYXNlTGF5ZXJzUHJlc2VudCA/ICcnIDogJ25vbmUnO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9vbkxheWVyQ2hhbmdlOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9oYW5kbGluZ0NsaWNrKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvYmogPSB0aGlzLl9nZXRMYXllcihzdGFtcChlLnRhcmdldCkpO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBMYXllciBldmVudHNcclxuXHRcdC8vIEBldmVudCBiYXNlbGF5ZXJjaGFuZ2U6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYmFzZSBsYXllciBpcyBjaGFuZ2VkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlhZGQ6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIHNlbGVjdGVkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlyZW1vdmU6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIGRlc2VsZWN0ZWQgdGhyb3VnaCB0aGUgW2xheWVyIGNvbnRyb2xdKCNjb250cm9sLWxheWVycykuXHJcblx0XHQvLyBAbmFtZXNwYWNlIENvbnRyb2wuTGF5ZXJzXHJcblx0XHR2YXIgdHlwZSA9IG9iai5vdmVybGF5ID9cclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnb3ZlcmxheWFkZCcgOiAnb3ZlcmxheXJlbW92ZScpIDpcclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnYmFzZWxheWVyY2hhbmdlJyA6IG51bGwpO1xyXG5cclxuXHRcdGlmICh0eXBlKSB7XHJcblx0XHRcdHRoaXMuX21hcC5maXJlKHR5cGUsIG9iaik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gSUU3IGJ1Z3Mgb3V0IGlmIHlvdSBjcmVhdGUgYSByYWRpbyBkeW5hbWljYWxseSwgc28geW91IGhhdmUgdG8gZG8gaXQgdGhpcyBoYWNreSB3YXkgKHNlZSBodHRwOi8vYml0Lmx5L1BxWUxCZSlcclxuXHRfY3JlYXRlUmFkaW9FbGVtZW50OiBmdW5jdGlvbiAobmFtZSwgY2hlY2tlZCkge1xyXG5cclxuXHRcdHZhciByYWRpb0h0bWwgPSAnPGlucHV0IHR5cGU9XCJyYWRpb1wiIGNsYXNzPVwibGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3RvclwiIG5hbWU9XCInICtcclxuXHRcdFx0XHRuYW1lICsgJ1wiJyArIChjaGVja2VkID8gJyBjaGVja2VkPVwiY2hlY2tlZFwiJyA6ICcnKSArICcvPic7XHJcblxyXG5cdFx0dmFyIHJhZGlvRnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdHJhZGlvRnJhZ21lbnQuaW5uZXJIVE1MID0gcmFkaW9IdG1sO1xyXG5cclxuXHRcdHJldHVybi
14775ByYWRpb0ZyYWdtZW50LmZpcnN0Q2hpbGQ7XHJcblx0fSxcclxuXHJcblx0X2FkZEl0ZW06IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyksXHJcblx0XHQgICAgY2hlY2tlZCA9IHRoaXMuX21hcC5oYXNMYXllcihvYmoubGF5ZXIpLFxyXG5cdFx0ICAgIGlucHV0O1xyXG5cclxuXHRcdGlmIChvYmoub3ZlcmxheSkge1xyXG5cdFx0XHRpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcblx0XHRcdGlucHV0LnR5cGUgPSAnY2hlY2tib3gnO1xyXG5cdFx0XHRpbnB1dC5jbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3Rvcic7XHJcblx0XHRcdGlucHV0LmRlZmF1bHRDaGVja2VkID0gY2hlY2tlZDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlucHV0ID0gdGhpcy5fY3JlYXRlUmFkaW9FbGVtZW50KCdsZWFmbGV0LWJhc2UtbGF5ZXJzXycgKyBzdGFtcCh0aGlzKSwgY2hlY2tlZCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJDb250cm9sSW5wdXRzLnB1c2goaW5wdXQpO1xyXG5cdFx0aW5wdXQubGF5ZXJJZCA9IHN0YW1wKG9iai5sYXllcik7XHJcblxyXG5cdFx0b24oaW5wdXQsICdjbGljaycsIHRoaXMuX29uSW5wdXRDbGljaywgdGhpcyk7XHJcblxyXG5cdFx0dmFyIG5hbWUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XHJcblx0XHRuYW1lLmlubmVySFRNTCA9ICcgJyArIG9iai5uYW1lO1xyXG5cclxuXHRcdC8vIEhlbHBzIGZyb20gcHJldmVudGluZyBsYXllciBjb250cm9sIGZsaWNrZXIgd2hlbiBjaGVja2JveGVzIGFyZSBkaXNhYmxlZFxyXG5cdFx0Ly8gaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMjc3MVxyXG5cdFx0dmFyIGhvbGRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG5cclxuXHRcdGxhYmVsLmFwcGVuZENoaWxkKGhvbGRlcik7XHJcblx0XHRob2xkZXIuYXBwZW5kQ2hpbGQoaW5wdXQpO1xyXG5cdFx0aG9sZGVyLmFwcGVuZENoaWxkKG5hbWUpO1xyXG5cclxuXHRcdHZhciBjb250YWluZXIgPSBvYmoub3ZlcmxheSA/IHRoaXMuX292ZXJsYXlzTGlzdCA6IHRoaXMuX2Jhc2VMYXllcnNMaXN0O1xyXG5cdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKGxhYmVsKTtcclxuXHJcblx0XHR0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzKCk7XHJcblx0XHRyZXR1cm4gbGFiZWw7XHJcblx0fSxcclxuXHJcblx0X29uSW5wdXRDbGljazogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGlucHV0cyA9IHRoaXMuX2xheWVyQ29udHJvbElucHV0cyxcclxuXHRcdCAgICBpbnB1dCwgbGF5ZXI7XHJcblx0XHR2YXIgYWRkZWRMYXllcnMgPSBbXSxcclxuXHRcdCAgICByZW1vdmVkTGF5ZXJzID0gW107XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IHRydWU7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cdFx0XHRpbnB1dCA9IGlucHV0c1tpXTtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9nZXRMYXllcihpbnB1dC5sYXllcklkKS5sYXllcjtcclxuXHJcblx0XHRcdGlmIChpbnB1dC5jaGVja2VkKSB7XHJcblx0XHRcdFx0YWRkZWRMYXllcnMucHVzaChsYXllcik7XHJcblx0XHRcdH0gZWxzZSBpZiAoIWlucHV0LmNoZWNrZWQpIHtcclxuXHRcdFx0XHRyZW1vdmVkTGF5ZXJzLnB1c2gobGF5ZXIpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQnVnZml4IGlzc3VlIDIzMTg6IFNob3VsZCByZW1vdmUgYWxsIG9sZCBsYXllcnMgYmVmb3JlIHJlYWRkaW5nIG5ldyBvbmVzXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgcmVtb3ZlZExheWVycy5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHRpZiAodGhpcy5fbWFwLmhhc0xheWVyKHJlbW92ZWRMYXllcnNbaV0pKSB7XHJcblx0XHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHJlbW92ZWRMYXllcnNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgYWRkZWRMYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0aWYgKCF0aGlzLl9tYXAuaGFzTGF5ZXIoYWRkZWRMYXllcnNbaV0pKSB7XHJcblx0XHRcdFx0dGhpcy5fbWFwLmFkZExheWVyKGFkZGVkTGF5ZXJzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2hhbmRsaW5nQ2xpY2sgPSBmYWxzZTtcclxuXHJcblx0XHR0aGlzLl9yZWZvY3VzT25NYXAoKTtcclxuXHR9LFxyXG5cclxuXHRfY2hlY2tEaXNhYmxlZExheWVyczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGlucHV0cyA9IHRoaXMuX2xheWVyQ29udHJvbElucHV0cyxcclxuXHRcdCAgICBpbnB1dCxcclxuXHRcdCAgICBsYXllcixcclxuXHRcdCAgICB6b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gaW5wdXRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblx0XHRcdGlucHV0ID0gaW5wdXRzW2ldO1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2dldExheWVyKGlucHV0LmxheWVySWQpLmxheWVyO1xyXG5cdFx0XHRpbnB1dC5kaXNhYmxlZCA9IChsYXllci5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tIDwgbGF5ZXIub3B0aW9ucy5taW5ab29tKSB8fFxyXG5cdFx0XHQgICAgICAgICAgICAgICAgIChsYXllci5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tID4gbGF5ZXIub3B0aW9ucy5tYXhab29tKTtcclxuXHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2V4cGFuZElmTm90Q29sbGFwc2VkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwICYmICF0aGlzLm9wdGlvbnMuY29sbGFwc2VkKSB7XHJcblx0XHRcdHRoaXMuZXhwYW5kKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfZXhwYW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5LCByZW1vdmUgbWUgaW4gMS4xLlxyXG5cdFx0cmV0dXJuIHRoaXMuZXhwYW5kKCk7XHJcblx0fSxcclxuXHJcblx0X2NvbGxhcHNlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5LCByZW1vdmUgbWUgaW4gMS4xLlxyXG5cdFx0cmV0dXJuIHRoaXMuY29sbGFwc2UoKTtcclxuXHR9XHJcblxyXG59KTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wubGF5ZXJzKGJhc2VsYXllcnM/OiBPYmplY3QsIG92ZXJsYXlzPzogT2JqZWN0LCBvcHRpb25zPzogQ29udHJvbC5MYXllcnMgb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhIGxheWVycyBjb250cm9sIHdpdGggdGhlIGdpdmVuIGxheWVycy4gQmFzZSBsYXllcnMgd2lsbCBiZSBzd2l0Y2hlZCB3aXRoIHJhZGlvIGJ1dHRvbnMsIHdoaWxlIG92ZXJsYXlzIHdpbGwgYmUgc3dpdGNoZWQgd2l0aCBjaGVja2JveGVzLiBOb3RlIHRoYXQgYWxsIGJhc2UgbGF5ZXJzIHNob3VsZCBiZSBwYXNzZWQgaW4gdGhlIGJhc2UgbGF5ZXJzIG9iamVjdCwgYnV0IG9ubHkgb25lIHNob3VsZCBiZSBhZGRlZCB0byB0aGUgbWFwIGR1cmluZyBtYXAgaW5zdGFudGlhdGlvbi5cclxudmFyIGxheWVycyA9IGZ1bmN0aW9uIChiYXNlTGF5ZXJzLCBvdmVybGF5cywgb3B0aW9ucykge1xyXG5cdHJldHVybi
14775BuZXcgTGF5ZXJzKGJhc2VMYXllcnMsIG92ZXJsYXlzLCBvcHRpb25zKTtcclxufTtcblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuWm9vbVxyXG4gKiBAYWthIEwuQ29udHJvbC5ab29tXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIEEgYmFzaWMgem9vbSBjb250cm9sIHdpdGggdHdvIGJ1dHRvbnMgKHpvb20gaW4gYW5kIHpvb20gb3V0KS4gSXQgaXMgcHV0IG9uIHRoZSBtYXAgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHNldCBpdHMgW2B6b29tQ29udHJvbGAgb3B0aW9uXSgjbWFwLXpvb21jb250cm9sKSB0byBgZmFsc2VgLiBFeHRlbmRzIGBDb250cm9sYC5cclxuICovXHJcblxyXG52YXIgWm9vbSA9IENvbnRyb2wuZXh0ZW5kKHtcclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgQ29udHJvbC5ab29tIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHRwb3NpdGlvbjogJ3RvcGxlZnQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbUluVGV4dDogU3RyaW5nID0gJysnXHJcblx0XHQvLyBUaGUgdGV4dCBzZXQgb24gdGhlICd6b29tIGluJyBidXR0b24uXHJcblx0XHR6b29tSW5UZXh0OiAnKycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tSW5UaXRsZTogU3RyaW5nID0gJ1pvb20gaW4nXHJcblx0XHQvLyBUaGUgdGl0bGUgc2V0IG9uIHRoZSAnem9vbSBpbicgYnV0dG9uLlxyXG5cdFx0em9vbUluVGl0bGU6ICdab29tIGluJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21PdXRUZXh0OiBTdHJpbmcgPSAnJiN4MjIxMjsnXHJcblx0XHQvLyBUaGUgdGV4dCBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRleHQ6ICcmI3gyMjEyOycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT3V0VGl0bGU6IFN0cmluZyA9ICdab29tIG91dCdcclxuXHRcdC8vIFRoZSB0aXRsZSBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRpdGxlOiAnWm9vbSBvdXQnXHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHZhciB6b29tTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtem9vbScsXHJcblx0XHQgICAgY29udGFpbmVyID0gY3JlYXRlJDEoJ2RpdicsIHpvb21OYW1lICsgJyBsZWFmbGV0LWJhcicpLFxyXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblxyXG5cdFx0dGhpcy5fem9vbUluQnV0dG9uICA9IHRoaXMuX2NyZWF0ZUJ1dHRvbihvcHRpb25zLnpvb21JblRleHQsIG9wdGlvbnMuem9vbUluVGl0bGUsXHJcblx0XHQgICAgICAgIHpvb21OYW1lICsgJy1pbicsICBjb250YWluZXIsIHRoaXMuX3pvb21Jbik7XHJcblx0XHR0aGlzLl96b29tT3V0QnV0dG9uID0gdGhpcy5fY3JlYXRlQnV0dG9uKG9wdGlvbnMuem9vbU91dFRleHQsIG9wdGlvbnMuem9vbU91dFRpdGxlLFxyXG5cdFx0ICAgICAgICB6b29tTmFtZSArICctb3V0JywgY29udGFpbmVyLCB0aGlzLl96b29tT3V0KTtcclxuXHJcblx0XHR0aGlzLl91cGRhdGVEaXNhYmxlZCgpO1xyXG5cdFx0bWFwLm9uKCd6b29tZW5kIHpvb21sZXZlbHNjaGFuZ2UnLCB0aGlzLl91cGRhdGVEaXNhYmxlZCwgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0bWFwLm9mZignem9vbWVuZCB6b29tbGV2ZWxzY2hhbmdlJywgdGhpcy5fdXBkYXRlRGlzYWJsZWQsIHRoaXMpO1xyXG5cdH0sXHJcblxyXG5cdGRpc2FibGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX2Rpc2FibGVkID0gdHJ1ZTtcclxuXHRcdHRoaXMuX3VwZGF0ZURpc2FibGVkKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRlbmFibGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX2Rpc2FibGVkID0gZmFsc2U7XHJcblx0XHR0aGlzLl91cGRhdGVEaXNhYmxlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3pvb21JbjogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5fZGlzYWJsZWQgJiYgdGhpcy5fbWFwLl96b29tIDwgdGhpcy5fbWFwLmdldE1heFpvb20oKSkge1xyXG5cdFx0XHR0aGlzLl9tYXAuem9vbUluKHRoaXMuX21hcC5vcHRpb25zLnpvb21EZWx0YSAqIChlLnNoaWZ0S2V5ID8gMyA6IDEpKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfem9vbU91dDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5fZGlzYWJsZWQgJiYgdGhpcy5fbWFwLl96b29tID4gdGhpcy5fbWFwLmdldE1pblpvb20oKSkge1xyXG5cdFx0XHR0aGlzLl9tYXAuem9vbU91dCh0aGlzLl9tYXAub3B0aW9ucy56b29tRGVsdGEgKiAoZS5zaGlmdEtleSA/IDMgOiAxKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUJ1dHRvbjogZnVuY3Rpb24gKGh0bWwsIHRpdGxlLCBjbGFzc05hbWUsIGNvbnRhaW5lciwgZm4pIHtcclxuXHRcdHZhciBsaW5rID0gY3JlYXRlJDEoJ2EnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHRsaW5rLmlubmVySFRNTCA9IGh0bWw7XHJcblx0XHRsaW5rLmhyZWYgPSAnIyc7XHJcblx0XHRsaW5rLnRpdGxlID0gdGl0bGU7XHJcblxyXG5cdFx0LypcclxuXHRcdCAqIFdpbGwgZm9yY2Ugc2NyZWVuIHJlYWRlcnMgbGlrZSBWb2ljZU92ZXIgdG8gcmVhZCB0aGlzIGFzIFwiWm9vbSBpbiAtIGJ1dHRvblwiXHJcblx0XHQgKi9cclxuXHRcdGxpbmsuc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xyXG5cdFx0bGluay5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCB0aXRsZSk7XHJcblxyXG5cdFx0ZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24obGluayk7XHJcblx0XHRvbihsaW5rLCAnY2xpY2snLCBzdG9wKTtcclxuXHRcdG9uKGxpbmssICdjbGljaycsIGZuLCB0aGlzKTtcclxuXHRcdG9uKGxpbmssICdjbGljaycsIHRoaXMuX3JlZm9jdXNPbk1hcCwgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIGxpbms7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZURpc2FibGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxyXG5cdFx0ICAgIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWRpc2FibGVkJztcclxuXHJcblx0XHRyZW1vdmVDbGFzcyh0aGlzLl96b29tSW5CdXR0b24sIGNsYXNzTmFtZSk7XHJcblx0XHRyZW1vdmVDbGFzcyh0aGlzLl96b29tT3V0QnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNaW5ab29tKCkpIHtcclxuXHRcdFx0YWRkQ2xhc3ModGhpcy5fem9vbU91dEJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdH1cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNYXhab29tKCkpIHtcclxuXHRcdFx0YWRkQ2xhc3ModGhpcy5fem9vbUluQnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBDb250cm9sIG9wdGlvbnNcclxuLy8gQG9wdGlvbiB6b29tQ29udHJvbDogQm9vbGVhbiA9IHRydWVcclxuLy8gV2hldGhlciBhIFt6b29tIGNvbnRyb2xdKCNjb250cm9sLXpvb20pIGlzIGFkZGVkIHRvIHRoZSBtYXAgYnkgZGVmYXVsdC5cclxuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0em9vbUNvbnRyb2w6IHRydWVcclxufSk7XHJcblxyXG5NYXAuYWRkSW5pdEhvb2soZnVuY3Rpb24gKCkge1xyXG5cdGlmICh0aGlzLm9wdGlvbnMuem9vbUNvbnRyb2wpIHtcclxuXHRcdC8vIEBzZWN0aW9uIENvbnRyb2xzXHJcblx0XHQvLyBAcHJvcGVydHkgem9vbUNvbnRyb2w6IENvbnRyb2wuWm9vbVxyXG5cdFx0Ly8gVGhlIGRlZmF1bHQgem9vbSBjb250cm9sIChvbmx5IGF2YWlsYWJsZSBpZiB0aGVcclxuXHRcdC8vIFtgem9vbUNvbnRyb2xgIG9wdGlvbl0oI21hcC16b29tY29udHJvbCkgd2FzIGB0cnVlYCB3aGVuIGNyZWF0aW5nIHRoZSBtYXApLlxyXG5cdFx0dGhpcy56b29tQ29udHJvbCA9IG5ldyBab29tKCk7XHJcblx0XHR0aGlzLmFkZENvbnRyb2wodGhpcy56b29tQ29udHJvbCk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ29udHJvbC5ab29tXHJcbi8vIEBmYWN0b3J5IEwuY29udHJvbC56b29tKG9wdGlvbnM6IENvbnRyb2wuWm9vbSBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGEgem9vbSBjb250cm9sXHJcbnZhciB6b29tID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IFpvb20ob3B0aW9ucyk7XHJcbn07XG5cbi8qXG4gKiBAY2xhc3MgQ29udHJvbC5TY2FsZVxuICogQGFrYSBMLkNvbnRyb2wuU2NhbGVcbiAqIEBpbmhlcml0cyBDb250cm9sXG4gKlxuICogQSBzaW1wbGUgc2NhbGUgY29udHJvbCB0aGF0IHNob3dzIHRoZSBzY2FsZSBvZiB0aGUgY3VycmVudCBjZW50ZXIgb2Ygc2NyZWVuIGluIG1ldHJpYyAobS9rbSkgYW5kIGltcGVyaWFsIChtaS9mdCkgc3lzdGVtcy4gRXh0ZW5kcyBgQ29udHJvbGAuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jb250cm9sLnNjYWxlKCkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICovXG5cbnZhciBTY2FsZSA9IENvbnRyb2wuZXh0ZW5kKHtcblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBDb250cm9sLlNjYWxlIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdHBvc2l0aW9uOiAnYm90dG9tbGVmdCcsXG5cblx0XHQvLyBAb3B0aW9uIG1heFdpZHRoOiBOdW1iZXIgPSAxMDBcblx0XHQvLyBNYXhpbXVtIHdpZHRoIG9mIHRoZSBjb250cm9sIGluIHBpeGVscy4gVGhlIHdpZHRoIGlzIHNldCBkeW5hbWljYWxseSB0byBzaG93IHJvdW5kIHZhbHVlcyAoZS5nLiAxMDAsIDIwMCwgNTAwKS5cblx0XHRtYXhXaWR0aDogMTAwLFxuXG5cdFx0Ly8gQG9wdGlvbiBtZXRyaWM6IEJvb2xlYW4gPSBUcnVlXG5cdFx0Ly8gV2hldGhlciB0byBzaG93IHRoZSBtZXRyaWMgc2NhbGUgbGluZSAobS9rbSkuXG5cdFx0bWV0cmljOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiBpbXBlcmlhbDogQm9vbGVhbiA9IFRydWVcblx0XHQvLyBXaGV0aGVyIHRvIHNob3cgdGhlIGltcGVyaWFsIHNjYWxlIGxpbmUgKG1pL2Z0KS5cblx0XHRpbXBlcmlhbDogdHJ1ZVxuXG5cdFx0Ly8gQG9wdGlvbiB1cGRhdGVXaGVuSWRsZTogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgY29udHJvbCBpcyB1cGRhdGVkIG9uIFtgbW92ZWVuZGBdKCNtYXAtbW92ZWVuZCksIG90aGVyd2lzZSBpdCdzIGFsd2F5cyB1cC10by1kYXRlICh1cGRhdGVkIG9uIFtgbW92ZWBdKCNtYXAtbW92ZSkpLlxuXHR9LFxuXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtc2NhbGUnLFxuXHRcdCAgICBjb250YWluZXIgPSBjcmVhdGUkMSgnZGl2JywgY2xhc3NOYW1lKSxcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcblxuXHRcdHRoaXMuX2FkZFNjYWxlcyhvcHRpb25zLCBjbGFzc05hbWUgKyAnLWxpbmUnLCBjb250YWluZXIpO1xuXG5cdFx0bWFwLm9uKG9wdGlvbnMudXBkYXRlV2hlbklkbGUgPyAnbW92ZWVuZCcgOiAnbW92ZScsIHRoaXMuX3VwZGF0ZSwgdGhpcyk7XG5cdFx0bWFwLndoZW5SZWFkeSh0aGlzLl91cGRhdGUsIHRoaXMpO1xuXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdG1hcC5vZmYodGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5JZGxlID8gJ21vdmVlbmQnIDogJ21vdmUnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuXHR9LFxuXG5cdF9hZGRTY2FsZXM6IGZ1bmN0aW9uIChvcHRpb25zLCBjbGFzc05hbWUsIGNvbnRhaW5lcikge1xuXHRcdGlmIChvcHRpb25zLm1ldHJpYykge1xuXHRcdFx0dGhpcy5fbVNjYWxlID0gY3JlYXRlJDEoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyKTtcblx0XHR9XG5cdFx0aWYgKG9wdGlvbnMuaW1wZXJpYWwpIHtcblx0XHRcdHRoaXMuX2lTY2FsZSA9IGNyZWF0ZSQxKCdkaXYnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICB5ID0gbWFwLmdldFNpemUoKS55IC8gMjtcblxuXHRcdHZhciBtYXhNZXRlcnMgPSBtYXAuZGlzdGFuY2UoXG5cdFx0XHRtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhbMCwgeV0pLFxuXHRcdFx0bWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcoW3RoaXMub3B0aW9ucy5tYXhXaWR0aCwgeV0
14775pKTtcblxuXHRcdHRoaXMuX3VwZGF0ZVNjYWxlcyhtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVTY2FsZXM6IGZ1bmN0aW9uIChtYXhNZXRlcnMpIHtcblx0XHRpZiAodGhpcy5vcHRpb25zLm1ldHJpYyAmJiBtYXhNZXRlcnMpIHtcblx0XHRcdHRoaXMuX3VwZGF0ZU1ldHJpYyhtYXhNZXRlcnMpO1xuXHRcdH1cblx0XHRpZiAodGhpcy5vcHRpb25zLmltcGVyaWFsICYmIG1heE1ldGVycykge1xuXHRcdFx0dGhpcy5fdXBkYXRlSW1wZXJpYWwobWF4TWV0ZXJzKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU1ldHJpYzogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtZXRlcnMgPSB0aGlzLl9nZXRSb3VuZE51bShtYXhNZXRlcnMpLFxuXHRcdCAgICBsYWJlbCA9IG1ldGVycyA8IDEwMDAgPyBtZXRlcnMgKyAnIG0nIDogKG1ldGVycyAvIDEwMDApICsgJyBrbSc7XG5cblx0XHR0aGlzLl91cGRhdGVTY2FsZSh0aGlzLl9tU2NhbGUsIGxhYmVsLCBtZXRlcnMgLyBtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVJbXBlcmlhbDogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtYXhGZWV0ID0gbWF4TWV0ZXJzICogMy4yODA4Mzk5LFxuXHRcdCAgICBtYXhNaWxlcywgbWlsZXMsIGZlZXQ7XG5cblx0XHRpZiAobWF4RmVldCA+IDUyODApIHtcblx0XHRcdG1heE1pbGVzID0gbWF4RmVldCAvIDUyODA7XG5cdFx0XHRtaWxlcyA9IHRoaXMuX2dldFJvdW5kTnVtKG1heE1pbGVzKTtcblx0XHRcdHRoaXMuX3VwZGF0ZVNjYWxlKHRoaXMuX2lTY2FsZSwgbWlsZXMgKyAnIG1pJywgbWlsZXMgLyBtYXhNaWxlcyk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0ZmVldCA9IHRoaXMuX2dldFJvdW5kTnVtKG1heEZlZXQpO1xuXHRcdFx0dGhpcy5fdXBkYXRlU2NhbGUodGhpcy5faVNjYWxlLCBmZWV0ICsgJyBmdCcsIGZlZXQgLyBtYXhGZWV0KTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVNjYWxlOiBmdW5jdGlvbiAoc2NhbGUsIHRleHQsIHJhdGlvKSB7XG5cdFx0c2NhbGUuc3R5bGUud2lkdGggPSBNYXRoLnJvdW5kKHRoaXMub3B0aW9ucy5tYXhXaWR0aCAqIHJhdGlvKSArICdweCc7XG5cdFx0c2NhbGUuaW5uZXJIVE1MID0gdGV4dDtcblx0fSxcblxuXHRfZ2V0Um91bmROdW06IGZ1bmN0aW9uIChudW0pIHtcblx0XHR2YXIgcG93MTAgPSBNYXRoLnBvdygxMCwgKE1hdGguZmxvb3IobnVtKSArICcnKS5sZW5ndGggLSAxKSxcblx0XHQgICAgZCA9IG51bSAvIHBvdzEwO1xuXG5cdFx0ZCA9IGQgPj0gMTAgPyAxMCA6XG5cdFx0ICAgIGQgPj0gNSA/IDUgOlxuXHRcdCAgICBkID49IDMgPyAzIDpcblx0XHQgICAgZCA+PSAyID8gMiA6IDE7XG5cblx0XHRyZXR1cm4gcG93MTAgKiBkO1xuXHR9XG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuc2NhbGUob3B0aW9ucz86IENvbnRyb2wuU2NhbGUgb3B0aW9ucylcbi8vIENyZWF0ZXMgYW4gc2NhbGUgY29udHJvbCB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxudmFyIHNjYWxlID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBTY2FsZShvcHRpb25zKTtcbn07XG5cbi8qXHJcbiAqIEBjbGFzcyBDb250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBha2EgTC5Db250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIFRoZSBhdHRyaWJ1dGlvbiBjb250cm9sIGFsbG93cyB5b3UgdG8gZGlzcGxheSBhdHRyaWJ1dGlvbiBkYXRhIGluIGEgc21hbGwgdGV4dCBib3ggb24gYSBtYXAuIEl0IGlzIHB1dCBvbiB0aGUgbWFwIGJ5IGRlZmF1bHQgdW5sZXNzIHlvdSBzZXQgaXRzIFtgYXR0cmlidXRpb25Db250cm9sYCBvcHRpb25dKCNtYXAtYXR0cmlidXRpb25jb250cm9sKSB0byBgZmFsc2VgLCBhbmQgaXQgZmV0Y2hlcyBhdHRyaWJ1dGlvbiB0ZXh0cyBmcm9tIGxheWVycyB3aXRoIHRoZSBbYGdldEF0dHJpYnV0aW9uYCBtZXRob2RdKCNsYXllci1nZXRhdHRyaWJ1dGlvbikgYXV0b21hdGljYWxseS4gRXh0ZW5kcyBDb250cm9sLlxyXG4gKi9cclxuXHJcbnZhciBBdHRyaWJ1dGlvbiA9IENvbnRyb2wuZXh0ZW5kKHtcclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgQ29udHJvbC5BdHRyaWJ1dGlvbiBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0cG9zaXRpb246ICdib3R0b21yaWdodCcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwcmVmaXg6IFN0cmluZyA9ICdMZWFmbGV0J1xyXG5cdFx0Ly8gVGhlIEhUTUwgdGV4dCBzaG93biBiZWZvcmUgdGhlIGF0dHJpYnV0aW9ucy4gUGFzcyBgZmFsc2VgIHRvIGRpc2FibGUuXHJcblx0XHRwcmVmaXg6ICc8YSBocmVmPVwiaHR0cHM6Ly9sZWFmbGV0anMuY29tXCIgdGl0bGU9XCJBIEpTIGxpYnJhcnkgZm9yIGludGVyYWN0aXZlIG1hcHNcIj5MZWFmbGV0PC9hPidcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0c2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHR0aGlzLl9hdHRyaWJ1dGlvbnMgPSB7fTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0bWFwLmF0dHJpYnV0aW9uQ29udHJvbCA9IHRoaXM7XHJcblx0XHR0aGlzLl9jb250YWluZXIgPSBjcmVhdGUkMSgnZGl2JywgJ2xlYWZsZXQtY29udHJvbC1hdHRyaWJ1dGlvbicpO1xyXG5cdFx0ZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24odGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHQvLyBUT0RPIHVnbHksIHJlZmFjdG9yXHJcblx0XHRmb3IgKHZhciBpIGluIG1hcC5fbGF5ZXJzKSB7XHJcblx0XHRcdGlmIChtYXAuX2xheWVyc1tpXS5nZXRBdHRyaWJ1dGlvbikge1xyXG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRpb24obWFwLl9sYXllcnNbaV0uZ2V0QXR0cmlidXRpb24oKSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UHJlZml4KHByZWZpeDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHRleHQgYmVmb3JlIHRoZSBhdHRyaWJ1dGlvbnMuXHJcblx0c2V0UHJlZml4OiBmdW5jdGlvbiAocHJlZml4KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMucHJlZml4ID0gcHJlZml4O1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEF0dHJpYnV0aW9uKHRleHQ6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGF0dHJpYnV0aW9uIHRleHQgKGUuZy4gYCdWZWN0b3IgZGF0YSAmY29weTsgTWFwYm94J2ApLlxyXG5cdGFkZEF0dHJpYnV0aW9uOiBmdW5jdGlvbiAodGV4dCkge1xyXG5cdFx0aWYgKCF0ZXh0KSB7IHJldHVybi
14775B0aGlzOyB9XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdID0gMDtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XSsrO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlQXR0cmlidXRpb24odGV4dDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYW4gYXR0cmlidXRpb24gdGV4dC5cclxuXHRyZW1vdmVBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKHRleHQpIHtcclxuXHRcdGlmICghdGV4dCkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmICh0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdLS07XHJcblx0XHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBhdHRyaWJzID0gW107XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9hdHRyaWJ1dGlvbnMpIHtcclxuXHRcdFx0aWYgKHRoaXMuX2F0dHJpYnV0aW9uc1tpXSkge1xyXG5cdFx0XHRcdGF0dHJpYnMucHVzaChpKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwcmVmaXhBbmRBdHRyaWJzID0gW107XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5wcmVmaXgpIHtcclxuXHRcdFx0cHJlZml4QW5kQXR0cmlicy5wdXNoKHRoaXMub3B0aW9ucy5wcmVmaXgpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGF0dHJpYnMubGVuZ3RoKSB7XHJcblx0XHRcdHByZWZpeEFuZEF0dHJpYnMucHVzaChhdHRyaWJzLmpvaW4oJywgJykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5pbm5lckhUTUwgPSBwcmVmaXhBbmRBdHRyaWJzLmpvaW4oJyB8ICcpO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBDb250cm9sIG9wdGlvbnNcclxuLy8gQG9wdGlvbiBhdHRyaWJ1dGlvbkNvbnRyb2w6IEJvb2xlYW4gPSB0cnVlXHJcbi8vIFdoZXRoZXIgYSBbYXR0cmlidXRpb24gY29udHJvbF0oI2NvbnRyb2wtYXR0cmlidXRpb24pIGlzIGFkZGVkIHRvIHRoZSBtYXAgYnkgZGVmYXVsdC5cclxuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0YXR0cmlidXRpb25Db250cm9sOiB0cnVlXHJcbn0pO1xyXG5cclxuTWFwLmFkZEluaXRIb29rKGZ1bmN0aW9uICgpIHtcclxuXHRpZiAodGhpcy5vcHRpb25zLmF0dHJpYnV0aW9uQ29udHJvbCkge1xyXG5cdFx0bmV3IEF0dHJpYnV0aW9uKCkuYWRkVG8odGhpcyk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ29udHJvbC5BdHRyaWJ1dGlvblxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuYXR0cmlidXRpb24ob3B0aW9uczogQ29udHJvbC5BdHRyaWJ1dGlvbiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGFuIGF0dHJpYnV0aW9uIGNvbnRyb2wuXHJcbnZhciBhdHRyaWJ1dGlvbiA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBBdHRyaWJ1dGlvbihvcHRpb25zKTtcclxufTtcblxuQ29udHJvbC5MYXllcnMgPSBMYXllcnM7XG5Db250cm9sLlpvb20gPSBab29tO1xuQ29udHJvbC5TY2FsZSA9IFNjYWxlO1xuQ29udHJvbC5BdHRyaWJ1dGlvbiA9IEF0dHJpYnV0aW9uO1xuXG5jb250cm9sLmxheWVycyA9IGxheWVycztcbmNvbnRyb2wuem9vbSA9IHpvb207XG5jb250cm9sLnNjYWxlID0gc2NhbGU7XG5jb250cm9sLmF0dHJpYnV0aW9uID0gYXR0cmlidXRpb247XG5cbi8qXG5cdEwuSGFuZGxlciBpcyBhIGJhc2UgY2xhc3MgZm9yIGhhbmRsZXIgY2xhc3NlcyB0aGF0IGFyZSB1c2VkIGludGVybmFsbHkgdG8gaW5qZWN0XG5cdGludGVyYWN0aW9uIGZlYXR1cmVzIGxpa2UgZHJhZ2dpbmcgdG8gY2xhc3NlcyBsaWtlIE1hcCBhbmQgTWFya2VyLlxuKi9cblxuLy8gQGNsYXNzIEhhbmRsZXJcbi8vIEBha2EgTC5IYW5kbGVyXG4vLyBBYnN0cmFjdCBjbGFzcyBmb3IgbWFwIGludGVyYWN0aW9uIGhhbmRsZXJzXG5cbnZhciBIYW5kbGVyID0gQ2xhc3MuZXh0ZW5kKHtcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX21hcCA9IG1hcDtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGVuYWJsZSgpOiB0aGlzXG5cdC8vIEVuYWJsZXMgdGhlIGhhbmRsZXJcblx0ZW5hYmxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuIHRoaXM7IH1cblxuXHRcdHRoaXMuX2VuYWJsZWQgPSB0cnVlO1xuXHRcdHRoaXMuYWRkSG9va3MoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGRpc2FibGUoKTogdGhpc1xuXHQvLyBEaXNhYmxlcyB0aGUgaGFuZGxlclxuXHRkaXNhYmxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9lbmFibGVkKSB7IHJldHVybiB0aGlzOyB9XG5cblx0XHR0aGlzLl9lbmFibGVkID0gZmFsc2U7XG5cdFx0dGhpcy5yZW1vdmVIb29rcygpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZW5hYmxlZCgpOiBCb29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBoYW5kbGVyIGlzIGVuYWJsZWRcblx0ZW5hYmxlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiAhIXRoaXMuX2VuYWJsZWQ7XG5cdH1cblxuXHQvLyBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xuXHQvLyBDbGFzc2VzIGluaGVyaXRpbmcgZnJvbSBgSGFuZGxlcmAgbXVzdCBpbXBsZW1lbnQgdGhlIHR3byBmb2xsb3dpbmcgbWV0aG9kczpcblx0Ly8gQG1ldGhvZCBhZGRIb29rcygpXG5cdC8vIENhbGxlZCB3aGVuIHRoZSBoYW5kbGVyIGlzIGVuYWJsZWQsIHNob3VsZCBhZGQgZXZlbnQgaG9va3MuXG5cdC8vIEBtZXRob2QgcmVtb3ZlSG9va3MoKVxuXHQvLyBDYWxsZWQgd2hlbiB0aGUgaGFuZGxlciBpcyBkaXNhYmxlZCwgc2hvdWxkIHJlbW92ZSB0aGUgZXZlbnQgaG9va3MgYWRkZWQgcHJldmlvdXNseS5cbn0pO1xuXG4vLyBAc2VjdGlvbiBUaGVyZSBpcyBzdGF0aWMgZnVuY3Rpb24gd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5
14775IYW5kbGVyOlxuLy8gQGZ1bmN0aW9uIGFkZFRvKG1hcDogTWFwLCBuYW1lOiBTdHJpbmcpOiB0aGlzXG4vLyBBZGRzIGEgbmV3IEhhbmRsZXIgdG8gdGhlIGdpdmVuIG1hcCB3aXRoIHRoZSBnaXZlbiBuYW1lLlxuSGFuZGxlci5hZGRUbyA9IGZ1bmN0aW9uIChtYXAsIG5hbWUpIHtcblx0bWFwLmFkZEhhbmRsZXIobmFtZSwgdGhpcyk7XG5cdHJldHVybiB0aGlzO1xufTtcblxudmFyIE1peGluID0ge0V2ZW50czogRXZlbnRzfTtcblxuLypcclxuICogQGNsYXNzIERyYWdnYWJsZVxyXG4gKiBAYWthIEwuRHJhZ2dhYmxlXHJcbiAqIEBpbmhlcml0cyBFdmVudGVkXHJcbiAqXHJcbiAqIEEgY2xhc3MgZm9yIG1ha2luZyBET00gZWxlbWVudHMgZHJhZ2dhYmxlIChpbmNsdWRpbmcgdG91Y2ggc3VwcG9ydCkuXHJcbiAqIFVzZWQgaW50ZXJuYWxseSBmb3IgbWFwIGFuZCBtYXJrZXIgZHJhZ2dpbmcuIE9ubHkgd29ya3MgZm9yIGVsZW1lbnRzXHJcbiAqIHRoYXQgd2VyZSBwb3NpdGlvbmVkIHdpdGggW2BMLkRvbVV0aWwuc2V0UG9zaXRpb25gXSgjZG9tdXRpbC1zZXRwb3NpdGlvbikuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBkcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUoZWxlbWVudFRvRHJhZyk7XHJcbiAqIGRyYWdnYWJsZS5lbmFibGUoKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxudmFyIFNUQVJUID0gdG91Y2ggPyAndG91Y2hzdGFydCBtb3VzZWRvd24nIDogJ21vdXNlZG93bic7XHJcbnZhciBFTkQgPSB7XHJcblx0bW91c2Vkb3duOiAnbW91c2V1cCcsXHJcblx0dG91Y2hzdGFydDogJ3RvdWNoZW5kJyxcclxuXHRwb2ludGVyZG93bjogJ3RvdWNoZW5kJyxcclxuXHRNU1BvaW50ZXJEb3duOiAndG91Y2hlbmQnXHJcbn07XHJcbnZhciBNT1ZFID0ge1xyXG5cdG1vdXNlZG93bjogJ21vdXNlbW92ZScsXHJcblx0dG91Y2hzdGFydDogJ3RvdWNobW92ZScsXHJcblx0cG9pbnRlcmRvd246ICd0b3VjaG1vdmUnLFxyXG5cdE1TUG9pbnRlckRvd246ICd0b3VjaG1vdmUnXHJcbn07XHJcblxyXG5cclxudmFyIERyYWdnYWJsZSA9IEV2ZW50ZWQuZXh0ZW5kKHtcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQHNlY3Rpb25cclxuXHRcdC8vIEBha2EgRHJhZ2dhYmxlIG9wdGlvbnNcclxuXHRcdC8vIEBvcHRpb24gY2xpY2tUb2xlcmFuY2U6IE51bWJlciA9IDNcclxuXHRcdC8vIFRoZSBtYXggbnVtYmVyIG9mIHBpeGVscyBhIHVzZXIgY2FuIHNoaWZ0IHRoZSBtb3VzZSBwb2ludGVyIGR1cmluZyBhIGNsaWNrXHJcblx0XHQvLyBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCBhIHZhbGlkIGNsaWNrIChhcyBvcHBvc2VkIHRvIGEgbW91c2UgZHJhZykuXHJcblx0XHRjbGlja1RvbGVyYW5jZTogM1xyXG5cdH0sXHJcblxyXG5cdC8vIEBjb25zdHJ1Y3RvciBMLkRyYWdnYWJsZShlbDogSFRNTEVsZW1lbnQsIGRyYWdIYW5kbGU/OiBIVE1MRWxlbWVudCwgcHJldmVudE91dGxpbmU/OiBCb29sZWFuLCBvcHRpb25zPzogRHJhZ2dhYmxlIG9wdGlvbnMpXHJcblx0Ly8gQ3JlYXRlcyBhIGBEcmFnZ2FibGVgIG9iamVjdCBmb3IgbW92aW5nIGBlbGAgd2hlbiB5b3Ugc3RhcnQgZHJhZ2dpbmcgdGhlIGBkcmFnSGFuZGxlYCBlbGVtZW50IChlcXVhbHMgYGVsYCBpdHNlbGYgYnkgZGVmYXVsdCkuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGVsZW1lbnQsIGRyYWdTdGFydFRhcmdldCwgcHJldmVudE91dGxpbmUkJDEsIG9wdGlvbnMpIHtcclxuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fZWxlbWVudCA9IGVsZW1lbnQ7XHJcblx0XHR0aGlzLl9kcmFnU3RhcnRUYXJnZXQgPSBkcmFnU3RhcnRUYXJnZXQgfHwgZWxlbWVudDtcclxuXHRcdHRoaXMuX3ByZXZlbnRPdXRsaW5lID0gcHJldmVudE91dGxpbmUkJDE7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBlbmFibGUoKVxyXG5cdC8vIEVuYWJsZXMgdGhlIGRyYWdnaW5nIGFiaWxpdHlcclxuXHRlbmFibGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9lbmFibGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdG9uKHRoaXMuX2RyYWdTdGFydFRhcmdldCwgU1RBUlQsIHRoaXMuX29uRG93biwgdGhpcyk7XHJcblxyXG5cdFx0dGhpcy5fZW5hYmxlZCA9IHRydWU7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXNhYmxlKClcclxuXHQvLyBEaXNhYmxlcyB0aGUgZHJhZ2dpbmcgYWJpbGl0eVxyXG5cdGRpc2FibGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBJZiB3ZSdyZSBjdXJyZW50bHkgZHJhZ2dpbmcgdGhpcyBkcmFnZ2FibGUsXHJcblx0XHQvLyBkaXNhYmxpbmcgaXQgY291bnRzIGFzIGZpcnN0IGVuZGluZyB0aGUgZHJhZy5cclxuXHRcdGlmIChEcmFnZ2FibGUuX2RyYWdnaW5nID09PSB0aGlzKSB7XHJcblx0XHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdG9mZih0aGlzLl9kcmFnU3RhcnRUYXJnZXQsIFNUQVJULCB0aGlzLl9vbkRvd24sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2VuYWJsZWQgPSBmYWxzZTtcclxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dGhpcy5fbW92ZWQgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAoaGFzQ2xhc3ModGhpcy5fZWxlbWVudCwgJ2xlYWZsZXQtem9vbS1hbmltJykpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0aWYgKERyYWdnYWJsZS5fZHJhZ2dpbmcgfHwgZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1
14775dHRvbiAhPT0gMSkgJiYgIWUudG91Y2hlcykpIHsgcmV0dXJuOyB9XHJcblx0XHREcmFnZ2FibGUuX2RyYWdnaW5nID0gdGhpczsgIC8vIFByZXZlbnQgZHJhZ2dpbmcgbXVsdGlwbGUgb2JqZWN0cyBhdCBvbmNlLlxyXG5cclxuXHRcdGlmICh0aGlzLl9wcmV2ZW50T3V0bGluZSkge1xyXG5cdFx0XHRwcmV2ZW50T3V0bGluZSh0aGlzLl9lbGVtZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRkaXNhYmxlSW1hZ2VEcmFnKCk7XHJcblx0XHRkaXNhYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZpbmcpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGRvd246IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBpcyBhYm91dCB0byBzdGFydC5cclxuXHRcdHRoaXMuZmlyZSgnZG93bicpO1xyXG5cclxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlcyA/IGUudG91Y2hlc1swXSA6IGUsXHJcblx0XHQgICAgc2l6ZWRQYXJlbnQgPSBnZXRTaXplZFBhcmVudE5vZGUodGhpcy5fZWxlbWVudCk7XHJcblxyXG5cdFx0dGhpcy5fc3RhcnRQb2ludCA9IG5ldyBQb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKTtcclxuXHJcblx0XHQvLyBDYWNoZSB0aGUgc2NhbGUsIHNvIHRoYXQgd2UgY2FuIGNvbnRpbnVvdXNseSBjb21wZW5zYXRlIGZvciBpdCBkdXJpbmcgZHJhZyAoX29uTW92ZSkuXHJcblx0XHR0aGlzLl9wYXJlbnRTY2FsZSA9IGdldFNjYWxlKHNpemVkUGFyZW50KTtcclxuXHJcblx0XHRvbihkb2N1bWVudCwgTU9WRVtlLnR5cGVdLCB0aGlzLl9vbk1vdmUsIHRoaXMpO1xyXG5cdFx0b24oZG9jdW1lbnQsIEVORFtlLnR5cGVdLCB0aGlzLl9vblVwLCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfb25Nb3ZlOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlIHdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoZS50b3VjaGVzICYmIGUudG91Y2hlcy5sZW5ndGggPiAxKSB7XHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBmaXJzdCA9IChlLnRvdWNoZXMgJiYgZS50b3VjaGVzLmxlbmd0aCA9PT0gMSA/IGUudG91Y2hlc1swXSA6IGUpLFxyXG5cdFx0ICAgIG9mZnNldCA9IG5ldyBQb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKS5fc3VidHJhY3QodGhpcy5fc3RhcnRQb2ludCk7XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHsgcmV0dXJuOyB9XHJcblx0XHRpZiAoTWF0aC5hYnMob2Zmc2V0LngpICsgTWF0aC5hYnMob2Zmc2V0LnkpIDwgdGhpcy5vcHRpb25zLmNsaWNrVG9sZXJhbmNlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdC8vIFdlIGFzc3VtZSB0aGF0IHRoZSBwYXJlbnQgY29udGFpbmVyJ3MgcG9zaXRpb24sIGJvcmRlciBhbmQgc2NhbGUgZG8gbm90IGNoYW5nZSBmb3IgdGhlIGR1cmF0aW9uIG9mIHRoZSBkcmFnLlxyXG5cdFx0Ly8gVGhlcmVmb3JlIHRoZXJlIGlzIG5vIG5lZWQgdG8gYWNjb3VudCBmb3IgdGhlIHBvc2l0aW9uIGFuZCBib3JkZXIgKHRoZXkgYXJlIGVsaW1pbmF0ZWQgYnkgdGhlIHN1YnRyYWN0aW9uKVxyXG5cdFx0Ly8gYW5kIHdlIGNhbiB1c2UgdGhlIGNhY2hlZCB2YWx1ZSBmb3IgdGhlIHNjYWxlLlxyXG5cdFx0b2Zmc2V0LnggLz0gdGhpcy5fcGFyZW50U2NhbGUueDtcclxuXHRcdG9mZnNldC55IC89IHRoaXMuX3BhcmVudFNjYWxlLnk7XHJcblxyXG5cdFx0cHJldmVudERlZmF1bHQoZSk7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xyXG5cdFx0XHQvLyBAZXZlbnQgZHJhZ3N0YXJ0OiBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBzdGFydHNcclxuXHRcdFx0dGhpcy5maXJlKCdkcmFnc3RhcnQnKTtcclxuXHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0dGhpcy5fc3RhcnRQb3MgPSBnZXRQb3NpdGlvbih0aGlzLl9lbGVtZW50KS5zdWJ0cmFjdChvZmZzZXQpO1xyXG5cclxuXHRcdFx0YWRkQ2xhc3MoZG9jdW1lbnQuYm9keSwgJ2xlYWZsZXQtZHJhZ2dpbmcnKTtcclxuXHJcblx0XHRcdHRoaXMuX2xhc3RUYXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XHJcblx0XHRcdC8vIElFIGFuZCBFZGdlIGRvIG5vdCBnaXZlIHRoZSA8dXNlPiBlbGVtZW50LCBzbyBmZXRjaCBpdFxyXG5cdFx0XHQvLyBpZiBuZWNlc3NhcnlcclxuXHRcdFx0aWYgKCh3aW5kb3cuU1ZHRWxlbWVudEluc3RhbmNlKSAmJiAodGhpcy5fbGFzdFRhcmdldCBpbnN0YW5jZW9mIFNWR0VsZW1lbnRJbnN0YW5jZSkpIHtcclxuXHRcdFx0XHR0aGlzLl9sYXN0VGFyZ2V0ID0gdGhpcy5fbGFzdFRhcmdldC5jb3JyZXNwb25kaW5nVXNlRWxlbWVudDtcclxuXHRcdFx0fVxyXG5cdFx0XHRhZGRDbGFzcyh0aGlzLl9sYXN0VGFyZ2V0LCAnbGVhZmxldC1kcmFnLXRhcmdldCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX25ld1BvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xyXG5cdFx0dGhpcy5fbW92aW5nID0gdHJ1ZTtcclxuXHJcblx0XHRjYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbVJlcXVlc3QpO1xyXG5cdFx0dGhpcy5fbGFzdEV2ZW50ID0gZTtcclxuXHRcdHRoaXMuX2FuaW1SZXF1ZXN0ID0gcmVxdWVzdEFuaW1GcmFtZSh0aGlzLl91cGRhdGVQb3NpdGlvbiwgdGhpcywgdHJ1ZSk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZSA9IHtvcmlnaW5hbEV2ZW50OiB0aGlzLl9sYXN0RXZlbnR9O1xyXG5cclxuXHRcdC8vIEBldmVudCBwcmVkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZyAqYmVmb3JlKiBlYWNoIGNvcnJlc3BvbmRpbmdcclxuXHRcdC8vIHVwZGF0ZSBvZiB0aGUgZWxlbWVudCdzIHBvc2l0aW9uLlxyXG5cdFx0dGhpcy5maXJlKCdwcmVkcmFnJywgZSk7XHJcblx0XHRzZXRQb3NpdGlvbih0aGlzLl9lbGVtZW50LCB0aGlzLl9uZXdQb3MpO1xyXG5cclxuXHRcdC8vIEBldmVudCBkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZy5cclxuXHRcdHRoaXMuZmlyZSgnZHJhZycsIGUpO1xyXG5cdH0sXHJcblxyXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlI
14775HdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdH0sXHJcblxyXG5cdGZpbmlzaERyYWc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJlbW92ZUNsYXNzKGRvY3VtZW50LmJvZHksICdsZWFmbGV0LWRyYWdnaW5nJyk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xhc3RUYXJnZXQpIHtcclxuXHRcdFx0cmVtb3ZlQ2xhc3ModGhpcy5fbGFzdFRhcmdldCwgJ2xlYWZsZXQtZHJhZy10YXJnZXQnKTtcclxuXHRcdFx0dGhpcy5fbGFzdFRhcmdldCA9IG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBNT1ZFKSB7XHJcblx0XHRcdG9mZihkb2N1bWVudCwgTU9WRVtpXSwgdGhpcy5fb25Nb3ZlLCB0aGlzKTtcclxuXHRcdFx0b2ZmKGRvY3VtZW50LCBFTkRbaV0sIHRoaXMuX29uVXAsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGVuYWJsZUltYWdlRHJhZygpO1xyXG5cdFx0ZW5hYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZlZCAmJiB0aGlzLl9tb3ZpbmcpIHtcclxuXHRcdFx0Ly8gZW5zdXJlIGRyYWcgaXMgbm90IGZpcmVkIGFmdGVyIGRyYWdlbmRcclxuXHRcdFx0Y2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcclxuXHJcblx0XHRcdC8vIEBldmVudCBkcmFnZW5kOiBEcmFnRW5kRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGUgZHJhZyBlbmRzLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ2RyYWdlbmQnLCB7XHJcblx0XHRcdFx0ZGlzdGFuY2U6IHRoaXMuX25ld1Bvcy5kaXN0YW5jZVRvKHRoaXMuX3N0YXJ0UG9zKVxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tb3ZpbmcgPSBmYWxzZTtcclxuXHRcdERyYWdnYWJsZS5fZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHR9XHJcblxyXG59KTtcblxuLypcclxuICogQG5hbWVzcGFjZSBMaW5lVXRpbFxyXG4gKlxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBwb2x5bGluZSBwb2ludHMgcHJvY2Vzc2luZywgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkgdG8gbWFrZSBwb2x5bGluZXMgbGlnaHRuaW5nLWZhc3QuXHJcbiAqL1xyXG5cclxuLy8gU2ltcGxpZnkgcG9seWxpbmUgd2l0aCB2ZXJ0ZXggcmVkdWN0aW9uIGFuZCBEb3VnbGFzLVBldWNrZXIgc2ltcGxpZmljYXRpb24uXHJcbi8vIEltcHJvdmVzIHJlbmRlcmluZyBwZXJmb3JtYW5jZSBkcmFtYXRpY2FsbHkgYnkgbGVzc2VuaW5nIHRoZSBudW1iZXIgb2YgcG9pbnRzIHRvIGRyYXcuXHJcblxyXG4vLyBAZnVuY3Rpb24gc2ltcGxpZnkocG9pbnRzOiBQb2ludFtdLCB0b2xlcmFuY2U6IE51bWJlcik6IFBvaW50W11cclxuLy8gRHJhbWF0aWNhbGx5IHJlZHVjZXMgdGhlIG51bWJlciBvZiBwb2ludHMgaW4gYSBwb2x5bGluZSB3aGlsZSByZXRhaW5pbmdcclxuLy8gaXRzIHNoYXBlIGFuZCByZXR1cm5zIGEgbmV3IGFycmF5IG9mIHNpbXBsaWZpZWQgcG9pbnRzLCB1c2luZyB0aGVcclxuLy8gW0RvdWdsYXMtUGV1Y2tlciBhbGdvcml0aG1dKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRG91Z2xhcy1QZXVja2VyX2FsZ29yaXRobSkuXHJcbi8vIFVzZWQgZm9yIGEgaHVnZSBwZXJmb3JtYW5jZSBib29zdCB3aGVuIHByb2Nlc3NpbmcvZGlzcGxheWluZyBMZWFmbGV0IHBvbHlsaW5lcyBmb3JcclxuLy8gZWFjaCB6b29tIGxldmVsIGFuZCBhbHNvIHJlZHVjaW5nIHZpc3VhbCBub2lzZS4gdG9sZXJhbmNlIGFmZmVjdHMgdGhlIGFtb3VudCBvZlxyXG4vLyBzaW1wbGlmaWNhdGlvbiAobGVzc2VyIHZhbHVlIG1lYW5zIGhpZ2hlciBxdWFsaXR5IGJ1dCBzbG93ZXIgYW5kIHdpdGggbW9yZSBwb2ludHMpLlxyXG4vLyBBbHNvIHJlbGVhc2VkIGFzIGEgc2VwYXJhdGVkIG1pY3JvLWxpYnJhcnkgW1NpbXBsaWZ5LmpzXShodHRwOi8vbW91cm5lci5naXRodWIuY29tL3NpbXBsaWZ5LWpzLykuXHJcbmZ1bmN0aW9uIHNpbXBsaWZ5KHBvaW50cywgdG9sZXJhbmNlKSB7XHJcblx0aWYgKCF0b2xlcmFuY2UgfHwgIXBvaW50cy5sZW5ndGgpIHtcclxuXHRcdHJldHVybiBwb2ludHMuc2xpY2UoKTtcclxuXHR9XHJcblxyXG5cdHZhciBzcVRvbGVyYW5jZSA9IHRvbGVyYW5jZSAqIHRvbGVyYW5jZTtcclxuXHJcblx0ICAgIC8vIHN0YWdlIDE6IHZlcnRleCByZWR1Y3Rpb25cclxuXHQgICAgcG9pbnRzID0gX3JlZHVjZVBvaW50cyhwb2ludHMsIHNxVG9sZXJhbmNlKTtcclxuXHJcblx0ICAgIC8vIHN0YWdlIDI6IERvdWdsYXMtUGV1Y2tlciBzaW1wbGlmaWNhdGlvblxyXG5cdCAgICBwb2ludHMgPSBfc2ltcGxpZnlEUChwb2ludHMsIHNxVG9sZXJhbmNlKTtcclxuXHJcblx0cmV0dXJuIHBvaW50cztcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIHBvaW50VG9TZWdtZW50RGlzdGFuY2UocDogUG9pbnQsIHAxOiBQb2ludCwgcDI6IFBvaW50KTogTnVtYmVyXHJcbi8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gcG9pbnQgYHBgIGFuZCBzZWdtZW50IGBwMWAgdG8gYHAyYC5cclxuZnVuY3Rpb24gcG9pbnRUb1NlZ21lbnREaXN0YW5jZShwLCBwMSwgcDIpIHtcclxuXHRyZXR1cm4gTWF0aC5zcXJ0KF9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudChwLCBwMSwgcDIsIHRydWUpKTtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGNsb3Nlc3RQb2ludE9uU2VnbWVudChwOiBQb2ludCwgcDE6IFBvaW50LCBwMjogUG9pbnQpOiBOdW1iZXJcclxuLy8gUmV0dXJucyB0aGUgY2xvc2VzdCBwb2ludCBmcm9tIGEgcG9pbnQgYHBgIG9uIGEgc2VnbWVudCBgcDFgIHRvIGBwMmAuXHJcbmZ1bmN0aW9uIGNsb3Nlc3RQb2ludE9uU2VnbWVudChwLCBwMSwgcDIpIHtcclxuXHRyZXR1cm4gX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHAsIHAxLCBwMik7XHJcbn1cclxuXHJcbi8vIERvdWdsYXMtUGV1Y2tlciBzaW1wbGlmaWNhdGlvbiwgc2VlIGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRG91Z2xhcy1QZXVja2VyX2FsZ29yaXRobVxyXG5mdW5jdGlvbiBfc2ltcGxpZnlEUChwb2ludHMsIHNxVG9sZXJhbmNlKSB7XHJcblxyXG5cdHZhciBsZW4gPSBwb2ludHMubGVuZ3RoLFxyXG5cdCAgICBBcnJheUNvbnN0cnVjdG9yID0gdHlwZW9mIFVpbnQ4QXJyYXkgIT09I
14775HVuZGVmaW5lZCArICcnID8gVWludDhBcnJheSA6IEFycmF5LFxyXG5cdCAgICBtYXJrZXJzID0gbmV3IEFycmF5Q29uc3RydWN0b3IobGVuKTtcclxuXHJcblx0ICAgIG1hcmtlcnNbMF0gPSBtYXJrZXJzW2xlbiAtIDFdID0gMTtcclxuXHJcblx0X3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIDAsIGxlbiAtIDEpO1xyXG5cclxuXHR2YXIgaSxcclxuXHQgICAgbmV3UG9pbnRzID0gW107XHJcblxyXG5cdGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0aWYgKG1hcmtlcnNbaV0pIHtcclxuXHRcdFx0bmV3UG9pbnRzLnB1c2gocG9pbnRzW2ldKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdHJldHVybiBuZXdQb2ludHM7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIF9zaW1wbGlmeURQU3RlcChwb2ludHMsIG1hcmtlcnMsIHNxVG9sZXJhbmNlLCBmaXJzdCwgbGFzdCkge1xyXG5cclxuXHR2YXIgbWF4U3FEaXN0ID0gMCxcclxuXHRpbmRleCwgaSwgc3FEaXN0O1xyXG5cclxuXHRmb3IgKGkgPSBmaXJzdCArIDE7IGkgPD0gbGFzdCAtIDE7IGkrKykge1xyXG5cdFx0c3FEaXN0ID0gX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHBvaW50c1tpXSwgcG9pbnRzW2ZpcnN0XSwgcG9pbnRzW2xhc3RdLCB0cnVlKTtcclxuXHJcblx0XHRpZiAoc3FEaXN0ID4gbWF4U3FEaXN0KSB7XHJcblx0XHRcdGluZGV4ID0gaTtcclxuXHRcdFx0bWF4U3FEaXN0ID0gc3FEaXN0O1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0aWYgKG1heFNxRGlzdCA+IHNxVG9sZXJhbmNlKSB7XHJcblx0XHRtYXJrZXJzW2luZGV4XSA9IDE7XHJcblxyXG5cdFx0X3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIGZpcnN0LCBpbmRleCk7XHJcblx0XHRfc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgaW5kZXgsIGxhc3QpO1xyXG5cdH1cclxufVxyXG5cclxuLy8gcmVkdWNlIHBvaW50cyB0aGF0IGFyZSB0b28gY2xvc2UgdG8gZWFjaCBvdGhlciB0byBhIHNpbmdsZSBwb2ludFxyXG5mdW5jdGlvbiBfcmVkdWNlUG9pbnRzKHBvaW50cywgc3FUb2xlcmFuY2UpIHtcclxuXHR2YXIgcmVkdWNlZFBvaW50cyA9IFtwb2ludHNbMF1dO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMSwgcHJldiA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0aWYgKF9zcURpc3QocG9pbnRzW2ldLCBwb2ludHNbcHJldl0pID4gc3FUb2xlcmFuY2UpIHtcclxuXHRcdFx0cmVkdWNlZFBvaW50cy5wdXNoKHBvaW50c1tpXSk7XHJcblx0XHRcdHByZXYgPSBpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHRpZiAocHJldiA8IGxlbiAtIDEpIHtcclxuXHRcdHJlZHVjZWRQb2ludHMucHVzaChwb2ludHNbbGVuIC0gMV0pO1xyXG5cdH1cclxuXHRyZXR1cm4gcmVkdWNlZFBvaW50cztcclxufVxyXG5cclxudmFyIF9sYXN0Q29kZTtcclxuXHJcbi8vIEBmdW5jdGlvbiBjbGlwU2VnbWVudChhOiBQb2ludCwgYjogUG9pbnQsIGJvdW5kczogQm91bmRzLCB1c2VMYXN0Q29kZT86IEJvb2xlYW4sIHJvdW5kPzogQm9vbGVhbik6IFBvaW50W118Qm9vbGVhblxyXG4vLyBDbGlwcyB0aGUgc2VnbWVudCBhIHRvIGIgYnkgcmVjdGFuZ3VsYXIgYm91bmRzIHdpdGggdGhlXHJcbi8vIFtDb2hlbi1TdXRoZXJsYW5kIGFsZ29yaXRobV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29oZW4lRTIlODAlOTNTdXRoZXJsYW5kX2FsZ29yaXRobSlcclxuLy8gKG1vZGlmeWluZyB0aGUgc2VnbWVudCBwb2ludHMgZGlyZWN0bHkhKS4gVXNlZCBieSBMZWFmbGV0IHRvIG9ubHkgc2hvdyBwb2x5bGluZVxyXG4vLyBwb2ludHMgdGhhdCBhcmUgb24gdGhlIHNjcmVlbiBvciBuZWFyLCBpbmNyZWFzaW5nIHBlcmZvcm1hbmNlLlxyXG5mdW5jdGlvbiBjbGlwU2VnbWVudChhLCBiLCBib3VuZHMsIHVzZUxhc3RDb2RlLCByb3VuZCkge1xyXG5cdHZhciBjb2RlQSA9IHVzZUxhc3RDb2RlID8gX2xhc3RDb2RlIDogX2dldEJpdENvZGUoYSwgYm91bmRzKSxcclxuXHQgICAgY29kZUIgPSBfZ2V0Qml0Q29kZShiLCBib3VuZHMpLFxyXG5cclxuXHQgICAgY29kZU91dCwgcCwgbmV3Q29kZTtcclxuXHJcblx0ICAgIC8vIHNhdmUgMm5kIGNvZGUgdG8gYXZvaWQgY2FsY3VsYXRpbmcgaXQgb24gdGhlIG5leHQgc2VnbWVudFxyXG5cdCAgICBfbGFzdENvZGUgPSBjb2RlQjtcclxuXHJcblx0d2hpbGUgKHRydWUpIHtcclxuXHRcdC8vIGlmIGEsYiBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIGFjY2VwdClcclxuXHRcdGlmICghKGNvZGVBIHwgY29kZUIpKSB7XHJcblx0XHRcdHJldHVybiBbYSwgYl07XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gaWYgYSxiIGlzIG91dHNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIHJlamVjdClcclxuXHRcdGlmIChjb2RlQSAmIGNvZGVCKSB7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBvdGhlciBjYXNlc1xyXG5cdFx0Y29kZU91dCA9IGNvZGVBIHx8IGNvZGVCO1xyXG5cdFx0cCA9IF9nZXRFZGdlSW50ZXJzZWN0aW9uKGEsIGIsIGNvZGVPdXQsIGJvdW5kcywgcm91bmQpO1xyXG5cdFx0bmV3Q29kZSA9IF9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKGNvZGVPdXQgPT09IGNvZGVBKSB7XHJcblx0XHRcdGEgPSBwO1xyXG5cdFx0XHRjb2RlQSA9IG5ld0NvZGU7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRiID0gcDtcclxuXHRcdFx0Y29kZUIgPSBuZXdDb2RlO1xyXG5cdFx0fVxyXG5cdH1cclxufVxyXG5cclxuZnVuY3Rpb24gX2dldEVkZ2VJbnRlcnNlY3Rpb24oYSwgYiwgY29kZSwgYm91bmRzLCByb3VuZCkge1xyXG5cdHZhciBkeCA9IGIueCAtIGEueCxcclxuXHQgICAgZHkgPSBiLnkgLSBhLnksXHJcblx0ICAgIG1pbiA9IGJvdW5kcy5taW4sXHJcblx0ICAgIG1heCA9IGJvdW5kcy5tYXgsXHJcblx0ICAgIHgsIHk7XHJcblxyXG5cdGlmIChjb2RlICYgOCkgeyAvLyB0b3BcclxuXHRcdHggPSBhLnggKyBkeCAqIChtYXgueSAtIGEueSkgLyBkeTtcclxuXHRcdHkgPSBtYXgueTtcclxuXHJcblx0fSBlbHNlIGlmIChjb2RlICYgNCkgeyAvLyBib3R0b21cclxuXHRcdHggPSBhLnggKyBkeCAqIChtaW4ueSAtIGEueSkgLyBkeTtcclxuXHRcdHkgPSBtaW4ueTtcclxuXHJcblx0fSBlbHNlIGlmIChjb2RlICYgMikgeyAvLyByaWdodFxyXG5cdFx0eCA9IG1heC54O1xyXG5cdFx0eSA9IGEueSArIGR5ICogKG1heC54IC0gYS54KSAvIGR4O1xyXG5cclxuXHR9IGVsc2UgaWYgKGNvZGUgJiAxKSB7IC8vIGxlZnRcclxuXHRcdHggPSBtaW4ueDtcclxuXHRcdHkgPSBhLnkgKyBkeSAqIChtaW4ueCAtIGEueCkgLyBkeDtcclxuXHR9XHJcblxyXG5cdHJldHVybi
14775BuZXcgUG9pbnQoeCwgeSwgcm91bmQpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBfZ2V0Qml0Q29kZShwLCBib3VuZHMpIHtcclxuXHR2YXIgY29kZSA9IDA7XHJcblxyXG5cdGlmIChwLnggPCBib3VuZHMubWluLngpIHsgLy8gbGVmdFxyXG5cdFx0Y29kZSB8PSAxO1xyXG5cdH0gZWxzZSBpZiAocC54ID4gYm91bmRzLm1heC54KSB7IC8vIHJpZ2h0XHJcblx0XHRjb2RlIHw9IDI7XHJcblx0fVxyXG5cclxuXHRpZiAocC55IDwgYm91bmRzLm1pbi55KSB7IC8vIGJvdHRvbVxyXG5cdFx0Y29kZSB8PSA0O1xyXG5cdH0gZWxzZSBpZiAocC55ID4gYm91bmRzLm1heC55KSB7IC8vIHRvcFxyXG5cdFx0Y29kZSB8PSA4O1xyXG5cdH1cclxuXHJcblx0cmV0dXJuIGNvZGU7XHJcbn1cclxuXHJcbi8vIHNxdWFyZSBkaXN0YW5jZSAodG8gYXZvaWQgdW5uZWNlc3NhcnkgTWF0aC5zcXJ0IGNhbGxzKVxyXG5mdW5jdGlvbiBfc3FEaXN0KHAxLCBwMikge1xyXG5cdHZhciBkeCA9IHAyLnggLSBwMS54LFxyXG5cdCAgICBkeSA9IHAyLnkgLSBwMS55O1xyXG5cdHJldHVybiBkeCAqIGR4ICsgZHkgKiBkeTtcclxufVxyXG5cclxuLy8gcmV0dXJuIGNsb3Nlc3QgcG9pbnQgb24gc2VnbWVudCBvciBkaXN0YW5jZSB0byB0aGF0IHBvaW50XHJcbmZ1bmN0aW9uIF9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudChwLCBwMSwgcDIsIHNxRGlzdCkge1xyXG5cdHZhciB4ID0gcDEueCxcclxuXHQgICAgeSA9IHAxLnksXHJcblx0ICAgIGR4ID0gcDIueCAtIHgsXHJcblx0ICAgIGR5ID0gcDIueSAtIHksXHJcblx0ICAgIGRvdCA9IGR4ICogZHggKyBkeSAqIGR5LFxyXG5cdCAgICB0O1xyXG5cclxuXHRpZiAoZG90ID4gMCkge1xyXG5cdFx0dCA9ICgocC54IC0geCkgKiBkeCArIChwLnkgLSB5KSAqIGR5KSAvIGRvdDtcclxuXHJcblx0XHRpZiAodCA+IDEpIHtcclxuXHRcdFx0eCA9IHAyLng7XHJcblx0XHRcdHkgPSBwMi55O1xyXG5cdFx0fSBlbHNlIGlmICh0ID4gMCkge1xyXG5cdFx0XHR4ICs9IGR4ICogdDtcclxuXHRcdFx0eSArPSBkeSAqIHQ7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRkeCA9IHAueCAtIHg7XHJcblx0ZHkgPSBwLnkgLSB5O1xyXG5cclxuXHRyZXR1cm4gc3FEaXN0ID8gZHggKiBkeCArIGR5ICogZHkgOiBuZXcgUG9pbnQoeCwgeSk7XHJcbn1cclxuXHJcblxyXG4vLyBAZnVuY3Rpb24gaXNGbGF0KGxhdGxuZ3M6IExhdExuZ1tdKTogQm9vbGVhblxyXG4vLyBSZXR1cm5zIHRydWUgaWYgYGxhdGxuZ3NgIGlzIGEgZmxhdCBhcnJheSwgZmFsc2UgaXMgbmVzdGVkLlxyXG5mdW5jdGlvbiBpc0ZsYXQobGF0bG5ncykge1xyXG5cdHJldHVybiAhaXNBcnJheShsYXRsbmdzWzBdKSB8fCAodHlwZW9mIGxhdGxuZ3NbMF1bMF0gIT09ICdvYmplY3QnICYmIHR5cGVvZiBsYXRsbmdzWzBdWzBdICE9PSAndW5kZWZpbmVkJyk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIF9mbGF0KGxhdGxuZ3MpIHtcclxuXHRjb25zb2xlLndhcm4oJ0RlcHJlY2F0ZWQgdXNlIG9mIF9mbGF0LCBwbGVhc2UgdXNlIEwuTGluZVV0aWwuaXNGbGF0IGluc3RlYWQuJyk7XHJcblx0cmV0dXJuIGlzRmxhdChsYXRsbmdzKTtcclxufVxyXG5cblxudmFyIExpbmVVdGlsID0gKE9iamVjdC5mcmVlemUgfHwgT2JqZWN0KSh7XG5cdHNpbXBsaWZ5OiBzaW1wbGlmeSxcblx0cG9pbnRUb1NlZ21lbnREaXN0YW5jZTogcG9pbnRUb1NlZ21lbnREaXN0YW5jZSxcblx0Y2xvc2VzdFBvaW50T25TZWdtZW50OiBjbG9zZXN0UG9pbnRPblNlZ21lbnQsXG5cdGNsaXBTZWdtZW50OiBjbGlwU2VnbWVudCxcblx0X2dldEVkZ2VJbnRlcnNlY3Rpb246IF9nZXRFZGdlSW50ZXJzZWN0aW9uLFxuXHRfZ2V0Qml0Q29kZTogX2dldEJpdENvZGUsXG5cdF9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudDogX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50LFxuXHRpc0ZsYXQ6IGlzRmxhdCxcblx0X2ZsYXQ6IF9mbGF0XG59KTtcblxuLypcclxuICogQG5hbWVzcGFjZSBQb2x5VXRpbFxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBwb2x5Z29uIGdlb21ldHJpZXMuXHJcbiAqL1xyXG5cclxuLyogQGZ1bmN0aW9uIGNsaXBQb2x5Z29uKHBvaW50czogUG9pbnRbXSwgYm91bmRzOiBCb3VuZHMsIHJvdW5kPzogQm9vbGVhbik6IFBvaW50W11cclxuICogQ2xpcHMgdGhlIHBvbHlnb24gZ2VvbWV0cnkgZGVmaW5lZCBieSB0aGUgZ2l2ZW4gYHBvaW50c2AgYnkgdGhlIGdpdmVuIGJvdW5kcyAodXNpbmcgdGhlIFtTdXRoZXJsYW5kLUhvZGdtYW4gYWxnb3JpdGhtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdXRoZXJsYW5kJUUyJTgwJTkzSG9kZ21hbl9hbGdvcml0aG0pKS5cclxuICogVXNlZCBieSBMZWFmbGV0IHRvIG9ubHkgc2hvdyBwb2x5Z29uIHBvaW50cyB0aGF0IGFyZSBvbiB0aGUgc2NyZWVuIG9yIG5lYXIsIGluY3JlYXNpbmdcclxuICogcGVyZm9ybWFuY2UuIE5vdGUgdGhhdCBwb2x5Z29uIHBvaW50cyBuZWVkcyBkaWZmZXJlbnQgYWxnb3JpdGhtIGZvciBjbGlwcGluZ1xyXG4gKiB0aGFuIHBvbHlsaW5lLCBzbyB0aGVyZSdzIGEgc2VwYXJhdGUgbWV0aG9kIGZvciBpdC5cclxuICovXHJcbmZ1bmN0aW9uIGNsaXBQb2x5Z29uKHBvaW50cywgYm91bmRzLCByb3VuZCkge1xyXG5cdHZhciBjbGlwcGVkUG9pbnRzLFxyXG5cdCAgICBlZGdlcyA9IFsxLCA0LCAyLCA4XSxcclxuXHQgICAgaSwgaiwgayxcclxuXHQgICAgYSwgYixcclxuXHQgICAgbGVuLCBlZGdlLCBwO1xyXG5cclxuXHRmb3IgKGkgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdHBvaW50c1tpXS5fY29kZSA9IF9nZXRCaXRDb2RlKHBvaW50c1tpXSwgYm91bmRzKTtcclxuXHR9XHJcblxyXG5cdC8vIGZvciBlYWNoIGVkZ2UgKGxlZnQsIGJvdHRvbSwgcmlnaHQsIHRvcClcclxuXHRmb3IgKGsgPSAwOyBrIDwgNDsgaysrKSB7XHJcblx0XHRlZGdlID0gZWRnZXNba107XHJcblx0XHRjbGlwcGVkUG9pbnRzID0gW107XHJcblxyXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aCwgaiA9IGxlbiAtIDE7IGkgPCBsZW47IGogPSBpKyspIHtcclxuXHRcdFx0YSA9IHBvaW50c1tpXTtcclxuXHRcdFx0YiA9IHBvaW50c1tqXTtcclxuXHJcblx0XHRcdC8vIGlmIGEgaXMgaW5zaWRlIHRoZSBjbGlwIHdpbmRvd1xyXG5cdFx0XHRpZiAoIShhLl9jb2RlICYgZWRnZSkpIHtcclxuXHRcdFx0XHQvLyBpZiBiIGlzIG91dHNpZGUgdGhlIGNsaXAgd2luZG93IChhLT5iIGdvZXMgb3V0IG9mIHNjcmVlbilcclxuXHRcdFx0XHRpZiAoYi5fY29kZSAmIGVkZ2UpIHtcclxuXHRcdFx0XHRcdHAgPSBfZ2V0RWRnZUludGVyc2VjdGlvbihiLCBhLCBlZGdlLCBib3VuZHMsIHJvdW5kKTtcclxuXHRcdFx0XHRcdHAuX2NvZGUgPSBfZ2V0Qml0Q29kZShwLCBib3VuZHMpO1xyXG5cdFx0XHRcdFx0Y2xpcHBlZFBvaW50cy5wdXNoKHApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRjbGlwcGVkUG9pbnRzLnB1c2goYSk7XHJcblxyXG5cdFx0XHQvLyBlbHNlIGlmIGIgaXMgaW5zaWRlIHRoZSBjbGlwIHdpbmRvdyAoYS0+YiBlbnRlcnMgdGhlIHNjcmVlbilcclxuXHRcdFx0fSBlbHNlIGlmICghKGIuX2NvZGUgJiBlZGdlKSkge1xyXG5cdFx0XHRcdHAgPSBfZ2V0RWRnZUludGVyc2VjdGlvbihiLCBhLCBlZGdlLCBib3VuZHMsIHJvdW5kKTtcclxuXHRcdFx0XHRwLl9jb2RlID0gX2dldEJpdENvZGUocCwgYm91bmRzKTtcclxuXHRcdFx0XHRjbGlwcGVkUG9pbnRzLnB1c2gocCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdH
14775BvaW50cyA9IGNsaXBwZWRQb2ludHM7XHJcblx0fVxyXG5cclxuXHRyZXR1cm4gcG9pbnRzO1xyXG59XHJcblxuXG52YXIgUG9seVV0aWwgPSAoT2JqZWN0LmZyZWV6ZSB8fCBPYmplY3QpKHtcblx0Y2xpcFBvbHlnb246IGNsaXBQb2x5Z29uXG59KTtcblxuLypcclxuICogQG5hbWVzcGFjZSBQcm9qZWN0aW9uXHJcbiAqIEBzZWN0aW9uXHJcbiAqIExlYWZsZXQgY29tZXMgd2l0aCBhIHNldCBvZiBhbHJlYWR5IGRlZmluZWQgUHJvamVjdGlvbnMgb3V0IG9mIHRoZSBib3g6XHJcbiAqXHJcbiAqIEBwcm9qZWN0aW9uIEwuUHJvamVjdGlvbi5Mb25MYXRcclxuICpcclxuICogRXF1aXJlY3Rhbmd1bGFyLCBvciBQbGF0ZSBDYXJyZWUgcHJvamVjdGlvbiDigJQgdGhlIG1vc3Qgc2ltcGxlIHByb2plY3Rpb24sXHJcbiAqIG1vc3RseSB1c2VkIGJ5IEdJUyBlbnRodXNpYXN0cy4gRGlyZWN0bHkgbWFwcyBgeGAgYXMgbG9uZ2l0dWRlLCBhbmQgYHlgIGFzXHJcbiAqIGxhdGl0dWRlLiBBbHNvIHN1aXRhYmxlIGZvciBmbGF0IHdvcmxkcywgZS5nLiBnYW1lIG1hcHMuIFVzZWQgYnkgdGhlXHJcbiAqIGBFUFNHOjQzMjZgIGFuZCBgU2ltcGxlYCBDUlMuXHJcbiAqL1xyXG5cclxudmFyIExvbkxhdCA9IHtcclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gbmV3IFBvaW50KGxhdGxuZy5sbmcsIGxhdGxuZy5sYXQpO1xyXG5cdH0sXHJcblxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IExhdExuZyhwb2ludC55LCBwb2ludC54KTtcclxuXHR9LFxyXG5cclxuXHRib3VuZHM6IG5ldyBCb3VuZHMoWy0xODAsIC05MF0sIFsxODAsIDkwXSlcclxufTtcblxuLypcclxuICogQG5hbWVzcGFjZSBQcm9qZWN0aW9uXHJcbiAqIEBwcm9qZWN0aW9uIEwuUHJvamVjdGlvbi5NZXJjYXRvclxyXG4gKlxyXG4gKiBFbGxpcHRpY2FsIE1lcmNhdG9yIHByb2plY3Rpb24g4oCUIG1vcmUgY29tcGxleCB0aGFuIFNwaGVyaWNhbCBNZXJjYXRvci4gQXNzdW1lcyB0aGF0IEVhcnRoIGlzIGFuIGVsbGlwc29pZC4gVXNlZCBieSB0aGUgRVBTRzozMzk1IENSUy5cclxuICovXHJcblxyXG52YXIgTWVyY2F0b3IgPSB7XHJcblx0UjogNjM3ODEzNyxcclxuXHRSX01JTk9SOiA2MzU2NzUyLjMxNDI0NTE3OSxcclxuXHJcblx0Ym91bmRzOiBuZXcgQm91bmRzKFstMjAwMzc1MDguMzQyNzksIC0xNTQ5NjU3MC43Mzk3Ml0sIFsyMDAzNzUwOC4zNDI3OSwgMTg3NjQ2NTYuMjMxMzhdKSxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgeSA9IGxhdGxuZy5sYXQgKiBkLFxyXG5cdFx0ICAgIHRtcCA9IHRoaXMuUl9NSU5PUiAvIHIsXHJcblx0XHQgICAgZSA9IE1hdGguc3FydCgxIC0gdG1wICogdG1wKSxcclxuXHRcdCAgICBjb24gPSBlICogTWF0aC5zaW4oeSk7XHJcblxyXG5cdFx0dmFyIHRzID0gTWF0aC50YW4oTWF0aC5QSSAvIDQgLSB5IC8gMikgLyBNYXRoLnBvdygoMSAtIGNvbikgLyAoMSArIGNvbiksIGUgLyAyKTtcclxuXHRcdHkgPSAtciAqIE1hdGgubG9nKE1hdGgubWF4KHRzLCAxRS0xMCkpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgUG9pbnQobGF0bG5nLmxuZyAqIGQgKiByLCB5KTtcclxuXHR9LFxyXG5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0dmFyIGQgPSAxODAgLyBNYXRoLlBJLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgdG1wID0gdGhpcy5SX01JTk9SIC8gcixcclxuXHRcdCAgICBlID0gTWF0aC5zcXJ0KDEgLSB0bXAgKiB0bXApLFxyXG5cdFx0ICAgIHRzID0gTWF0aC5leHAoLXBvaW50LnkgLyByKSxcclxuXHRcdCAgICBwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBkcGhpID0gMC4xLCBjb247IGkgPCAxNSAmJiBNYXRoLmFicyhkcGhpKSA+IDFlLTc7IGkrKykge1xyXG5cdFx0XHRjb24gPSBlICogTWF0aC5zaW4ocGhpKTtcclxuXHRcdFx0Y29uID0gTWF0aC5wb3coKDEgLSBjb24pIC8gKDEgKyBjb24pLCBlIC8gMik7XHJcblx0XHRcdGRwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMgKiBjb24pIC0gcGhpO1xyXG5cdFx0XHRwaGkgKz0gZHBoaTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbmV3IExhdExuZyhwaGkgKiBkLCBwb2ludC54ICogZCAvIHIpO1xyXG5cdH1cclxufTtcblxuLypcbiAqIEBjbGFzcyBQcm9qZWN0aW9uXG5cbiAqIEFuIG9iamVjdCB3aXRoIG1ldGhvZHMgZm9yIHByb2plY3RpbmcgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIG9mIHRoZSB3b3JsZCBvbnRvXG4gKiBhIGZsYXQgc3VyZmFjZSAoYW5kIGJhY2spLiBTZWUgW01hcCBwcm9qZWN0aW9uXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01hcF9wcm9qZWN0aW9uKS5cblxuICogQHByb3BlcnR5IGJvdW5kczogQm91bmRzXG4gKiBUaGUgYm91bmRzIChzcGVjaWZpZWQgaW4gQ1JTIHVuaXRzKSB3aGVyZSB0aGUgcHJvamVjdGlvbiBpcyB2YWxpZFxuXG4gKiBAbWV0aG9kIHByb2plY3QobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxuICogUHJvamVjdHMgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIGludG8gYSAyRCBwb2ludC5cbiAqIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuTGF0TG5nYCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXG5cbiAqIEBtZXRob2QgdW5wcm9qZWN0KHBvaW50OiBQb2ludCk6IExhdExuZ1xuICogVGhlIGludmVyc2Ugb2YgYHByb2plY3RgLiBQcm9qZWN0cyBhIDJEIHBvaW50IGludG8gYSBnZW9ncmFwaGljYWwgbG9jYXRpb24uXG4gKiBPbmx5IGFjY2VwdHMgYWN0dWFsIGBMLlBvaW50YCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXG5cbiAqIE5vdGUgdGhhdCB0aGUgcHJvamVjdGlvbiBpbnN0YW5jZXMgZG8gbm90IGluaGVyaXQgZnJvbSBMZWFmZXQncyBgQ2xhc3NgIG9iamVjdCxcbiAqIGFuZCBjYW4ndCBiZSBpbnN0YW50aWF0ZWQuIEFsc28sIG5ldyBjbGFzc2VzIGNhbid0IGluaGVyaXQgZnJvbSB0aGVtLFxuICogYW5kIG1ldGhvZHMgY2FuJ3QgYmUgYWRkZWQgdG8gdGhlbSB3aXRoIHRoZSBgaW5jbHVkZWAgZnVuY3Rpb24uXG5cbiAqL1xuXG5cblxuXG52YXIgaW5kZXggPSAoT2JqZWN0LmZyZWV6ZSB8fCBPYmplY3QpKHtcblx0TG9uTGF0OiBMb25MYXQsXG5cdE1lcmNhdG9yOiBNZXJjYXRvcixcblx0U3BoZXJpY2FsTWVyY2F0b3I6IFNwaGVyaWNhbE1lcmNhdG9yXG59KTtcblxuLypcclxuICogQG5hbWVzcGFjZSBDUlNcclxuICogQGNycyBMLkNSUy5FUFNHMzM5NVxyXG4gKlxyXG4gKiBSYXJlbHkgdXNlZCBieSBzb21lIGNvbW1lcmNpYWwgdGlsZSBwcm92aWRlcnMuIFVzZXMgRWxsaXB0aWNhbCBNZXJjYXRvciBwcm9qZWN0aW9uLlxyXG4gKi9cclxudmFyIEVQU0czMzk1ID0gZXh0ZW5kKHt9LCBFYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjMzOTUnLFxyXG5cdHByb2plY3Rpb246IE1lcmNhdG9yLFxyXG5cclxuXHR0cmFuc2Zvcm1hdGlvbjogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBzY2FsZSA9IDAuNSAvIChNYXRoLlBJICogTWVyY2F0b3IuUik7XHJcblx0XHRyZXR1cm4gdG9UcmFuc2Zvcm1hdGlvbihzY2FsZSwgMC41LCAtc2NhbGUsIDAuNSk7XHJcblx0fSgpKVxyXG59KTtcblxuLypcclxuICogQG5hbWVzcGFjZSBDUlNcclxuICogQGNycyBMLkNSUy5FUFNHNDMyNlxyXG4gKlxyXG4gKiBBIGNvbW1vbiBDUlMgYW1vbmcgR0lTIGVudGh1c2lhc3RzLiBVc2VzIHNpbXBsZSBFcXVpcmVjdGFuZ3VsYXIgcHJvamVjdGlvbi5cclxuICpcclxuICogTGVhZmxldCAxLjAueCBjb21wbGllcyB3aXRoIHRoZSBbVE1TIGNvb3JkaW5hdGUgc2NoZW1lIGZvciBFUFNHOjQzMjZdKGh0dHBzOi8vd2lraS5vc2dlby5vcmcvd2lraS9UaWxlX01hcF9TZXJ2aWNlX1Nw
14775ZWNpZmljYXRpb24jZ2xvYmFsLWdlb2RldGljKSxcclxuICogd2hpY2ggaXMgYSBicmVha2luZyBjaGFuZ2UgZnJvbSAwLjcueCBiZWhhdmlvdXIuICBJZiB5b3UgYXJlIHVzaW5nIGEgYFRpbGVMYXllcmBcclxuICogd2l0aCB0aGlzIENSUywgZW5zdXJlIHRoYXQgdGhlcmUgYXJlIHR3byAyNTZ4MjU2IHBpeGVsIHRpbGVzIGNvdmVyaW5nIHRoZVxyXG4gKiB3aG9sZSBlYXJ0aCBhdCB6b29tIGxldmVsIHplcm8sIGFuZCB0aGF0IHRoZSB0aWxlIGNvb3JkaW5hdGUgb3JpZ2luIGlzICgtMTgwLCs5MCksXHJcbiAqIG9yICgtMTgwLC05MCkgZm9yIGBUaWxlTGF5ZXJgcyB3aXRoIFt0aGUgYHRtc2Agb3B0aW9uXSgjdGlsZWxheWVyLXRtcykgc2V0LlxyXG4gKi9cclxuXHJcbnZhciBFUFNHNDMyNiA9IGV4dGVuZCh7fSwgRWFydGgsIHtcclxuXHRjb2RlOiAnRVBTRzo0MzI2JyxcclxuXHRwcm9qZWN0aW9uOiBMb25MYXQsXHJcblx0dHJhbnNmb3JtYXRpb246IHRvVHJhbnNmb3JtYXRpb24oMSAvIDE4MCwgMSwgLTEgLyAxODAsIDAuNSlcclxufSk7XG5cbi8qXG4gKiBAbmFtZXNwYWNlIENSU1xuICogQGNycyBMLkNSUy5TaW1wbGVcbiAqXG4gKiBBIHNpbXBsZSBDUlMgdGhhdCBtYXBzIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUgaW50byBgeGAgYW5kIGB5YCBkaXJlY3RseS5cbiAqIE1heSBiZSB1c2VkIGZvciBtYXBzIG9mIGZsYXQgc3VyZmFjZXMgKGUuZy4gZ2FtZSBtYXBzKS4gTm90ZSB0aGF0IHRoZSBgeWBcbiAqIGF4aXMgc2hvdWxkIHN0aWxsIGJlIGludmVydGVkIChnb2luZyBmcm9tIGJvdHRvbSB0byB0b3ApLiBgZGlzdGFuY2UoKWAgcmV0dXJuc1xuICogc2ltcGxlIGV1Y2xpZGVhbiBkaXN0YW5jZS5cbiAqL1xuXG52YXIgU2ltcGxlID0gZXh0ZW5kKHt9LCBDUlMsIHtcblx0cHJvamVjdGlvbjogTG9uTGF0LFxuXHR0cmFuc2Zvcm1hdGlvbjogdG9UcmFuc2Zvcm1hdGlvbigxLCAwLCAtMSwgMCksXG5cblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XG5cdFx0cmV0dXJuIE1hdGgucG93KDIsIHpvb20pO1xuXHR9LFxuXG5cdHpvb206IGZ1bmN0aW9uIChzY2FsZSkge1xuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSkgLyBNYXRoLkxOMjtcblx0fSxcblxuXHRkaXN0YW5jZTogZnVuY3Rpb24gKGxhdGxuZzEsIGxhdGxuZzIpIHtcblx0XHR2YXIgZHggPSBsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nLFxuXHRcdCAgICBkeSA9IGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQ7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KTtcblx0fSxcblxuXHRpbmZpbml0ZTogdHJ1ZVxufSk7XG5cbkNSUy5FYXJ0aCA9IEVhcnRoO1xuQ1JTLkVQU0czMzk1ID0gRVBTRzMzOTU7XG5DUlMuRVBTRzM4NTcgPSBFUFNHMzg1NztcbkNSUy5FUFNHOTAwOTEzID0gRVBTRzkwMDkxMztcbkNSUy5FUFNHNDMyNiA9IEVQU0c0MzI2O1xuQ1JTLlNpbXBsZSA9IFNpbXBsZTtcblxuLypcbiAqIEBjbGFzcyBMYXllclxuICogQGluaGVyaXRzIEV2ZW50ZWRcbiAqIEBha2EgTC5MYXllclxuICogQGFrYSBJTGF5ZXJcbiAqXG4gKiBBIHNldCBvZiBtZXRob2RzIGZyb20gdGhlIExheWVyIGJhc2UgY2xhc3MgdGhhdCBhbGwgTGVhZmxldCBsYXllcnMgdXNlLlxuICogSW5oZXJpdHMgYWxsIG1ldGhvZHMsIG9wdGlvbnMgYW5kIGV2ZW50cyBmcm9tIGBMLkV2ZW50ZWRgLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIHZhciBsYXllciA9IEwubWFya2VyKGxhdGxuZykuYWRkVG8obWFwKTtcbiAqIGxheWVyLmFkZFRvKG1hcCk7XG4gKiBsYXllci5yZW1vdmUoKTtcbiAqIGBgYFxuICpcbiAqIEBldmVudCBhZGQ6IEV2ZW50XG4gKiBGaXJlZCBhZnRlciB0aGUgbGF5ZXIgaXMgYWRkZWQgdG8gYSBtYXBcbiAqXG4gKiBAZXZlbnQgcmVtb3ZlOiBFdmVudFxuICogRmlyZWQgYWZ0ZXIgdGhlIGxheWVyIGlzIHJlbW92ZWQgZnJvbSBhIG1hcFxuICovXG5cblxudmFyIExheWVyID0gRXZlbnRlZC5leHRlbmQoe1xuXG5cdC8vIENsYXNzZXMgZXh0ZW5kaW5nIGBMLkxheWVyYCB3aWxsIGluaGVyaXQgdGhlIGZvbGxvd2luZyBvcHRpb25zOlxuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAnb3ZlcmxheVBhbmUnXG5cdFx0Ly8gQnkgZGVmYXVsdCB0aGUgbGF5ZXIgd2lsbCBiZSBhZGRlZCB0byB0aGUgbWFwJ3MgW292ZXJsYXkgcGFuZV0oI21hcC1vdmVybGF5cGFuZSkuIE92ZXJyaWRpbmcgdGhpcyBvcHRpb24gd2lsbCBjYXVzZSB0aGUgbGF5ZXIgdG8gYmUgcGxhY2VkIG9uIGFub3RoZXIgcGFuZSBieSBkZWZh
14775dWx0LlxuXHRcdHBhbmU6ICdvdmVybGF5UGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGF0dHJpYnV0aW9uOiBTdHJpbmcgPSBudWxsXG5cdFx0Ly8gU3RyaW5nIHRvIGJlIHNob3duIGluIHRoZSBhdHRyaWJ1dGlvbiBjb250cm9sLCBlLmcuIFwiwqkgT3BlblN0cmVldE1hcCBjb250cmlidXRvcnNcIi4gSXQgZGVzY3JpYmVzIHRoZSBsYXllciBkYXRhIGFuZCBpcyBvZnRlbiBhIGxlZ2FsIG9ibGlnYXRpb24gdG93YXJkcyBjb3B5cmlnaHQgaG9sZGVycyBhbmQgdGlsZSBwcm92aWRlcnMuXG5cdFx0YXR0cmlidXRpb246IG51bGwsXG5cblx0XHRidWJibGluZ01vdXNlRXZlbnRzOiB0cnVlXG5cdH0sXG5cblx0LyogQHNlY3Rpb25cblx0ICogQ2xhc3NlcyBleHRlbmRpbmcgYEwuTGF5ZXJgIHdpbGwgaW5oZXJpdCB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XG5cdCAqXG5cdCAqIEBtZXRob2QgYWRkVG8obWFwOiBNYXB8TGF5ZXJHcm91cCk6IHRoaXNcblx0ICogQWRkcyB0aGUgbGF5ZXIgdG8gdGhlIGdpdmVuIG1hcCBvciBsYXllciBncm91cC5cblx0ICovXG5cdGFkZFRvOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0bWFwLmFkZExheWVyKHRoaXMpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIGZyb20gdGhlIG1hcCBpdCBpcyBjdXJyZW50bHkgYWN0aXZlIG9uLlxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5yZW1vdmVGcm9tKHRoaXMuX21hcCB8fCB0aGlzLl9tYXBUb0FkZCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVGcm9tKG1hcDogTWFwKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBsYXllciBmcm9tIHRoZSBnaXZlbiBtYXBcblx0cmVtb3ZlRnJvbTogZnVuY3Rpb24gKG9iaikge1xuXHRcdGlmIChvYmopIHtcblx0XHRcdG9iai5yZW1vdmVMYXllcih0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRQYW5lKG5hbWU/IDogU3RyaW5nKTogSFRNTEVsZW1lbnRcblx0Ly8gUmV0dXJucyB0aGUgYEhUTUxFbGVtZW50YCByZXByZXNlbnRpbmcgdGhlIG5hbWVkIHBhbmUgb24gdGhlIG1hcC4gSWYgYG5hbWVgIGlzIG9taXR0ZWQsIHJldHVybnMgdGhlIHBhbmUgZm9yIHRoaXMgbGF5ZXIuXG5cdGdldFBhbmU6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0cmV0dXJuIHRoaXMuX21hcC5nZXRQYW5lKG5hbWUgPyAodGhpcy5vcHRpb25zW25hbWVdIHx8IG5hbWUpIDogdGhpcy5vcHRpb25zLnBhbmUpO1xuXHR9LFxuXG5cdGFkZEludGVyYWN0aXZlVGFyZ2V0OiBmdW5jdGlvbiAodGFyZ2V0RWwpIHtcblx0XHR0aGlzLl9tYXAuX3RhcmdldHNbc3RhbXAodGFyZ2V0RWwpXSA9IHRoaXM7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0cmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQ6IGZ1bmN0aW9uICh0YXJnZXRFbCkge1xuXHRcdGRlbGV0ZSB0aGlzLl9tYXAuX3RhcmdldHNbc3RhbXAodGFyZ2V0RWwpXTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldEF0dHJpYnV0aW9uOiBTdHJpbmdcblx0Ly8gVXNlZCBieSB0aGUgYGF0dHJpYnV0aW9uIGNvbnRyb2xgLCByZXR1cm5zIHRoZSBbYXR0cmlidXRpb24gb3B0aW9uXSgjZ3JpZGxheWVyLWF0dHJpYnV0aW9uKS5cblx0Z2V0QXR0cmlidXRpb246IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLmF0dHJpYnV0aW9uO1xuXHR9LFxuXG5cdF9sYXllckFkZDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gZS50YXJnZXQ7XG5cblx0XHQvLyBjaGVjayBpbiBjYXNlIGxheWVyIGdldHMgYWRkZWQgYW5kIHRoZW4gcmVtb3ZlZCBiZWZvcmUgdGhlIG1hcCBpcyByZWFkeVxuXHRcdGlmICghbWFwLmhhc0xheWVyKHRoaXMpKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHRcdHRoaXMuX3pvb21BbmltYXRlZCA9IG1hcC5fem9vbUFuaW1hdGVkO1xuXG5cdFx0aWYgKHRoaXMuZ2V0RXZlbnRzKSB7XG5cdFx0XHR2YXIgZXZlbnRzID0gdGhpcy5nZXRFdmVudHMoKTtcblx0XHRcdG1hcC5vbihldmVudHMsIHRoaXMpO1xuXHRcdFx0dGhpcy5vbmNlKCdyZW1vdmUnLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdG1hcC5vZmYoZXZlbnRzLCB0aGlzKTtcblx0XHRcdH0sIHRoaXMpO1xuXHRcdH1cblxuXHRcdHRoaXMub25BZGQobWFwKTtcblxuXHRcdGlmICh0aGlzLmdldEF0dHJpYnV0aW9uICYmIG1hcC5hdHRyaWJ1dGlvbkNvbnRyb2wpIHtcblx0XHRcdG1hcC5hdHRyaWJ1dGlvbkNvbnRyb2wuYWRkQXR0cmlidXRpb24odGhpcy5nZXRBdHRyaWJ1dGlvbigpKTtcblx0XHR9XG5cblx0XHR0aGlzLmZpcmUoJ2FkZCcpO1xuXHRcdG1hcC5maXJlKCdsYXllcmFkZCcsIHtsYXllcjogdGhpc30pO1xuXHR9XG59KTtcblxuLyogQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcbiAqIEB1bmluaGVyaXRhYmxlXG4gKlxuICogRXZlcnkgbGF5ZXIgc2hvdWxkIGV4dGVuZCBmcm9tIGBMLkxheWVyYCBhbmQgKHJlLSlpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2RzLlxuICpcbiAqIEBtZXRob2Qgb25BZGQobWFwOiBNYXApOiB0aGlzXG4gKiBTaG91bGQgY29udGFpbiBjb2RlIHRoYXQgY3JlYXRlcyBET00gZWxlbWVudHMgZm9yIHRoZSBsYXllciwgYWRkcyB0aGVtIHRvIGBtYXAgcGFuZXNgIHdoZXJlIHRoZXkgc2hvdWxkIGJlbG9uZyBhbmQgcHV0cyBsaXN0ZW5lcnMgb24gcmVsZXZhbnQgbWFwIGV2ZW50cy4gQ2FsbGVkIG9uIFtgbWFwLmFkZExheWVyKGxheWVyKWBdKCNtYXAtYWRkbGF5ZXIpLlxuICpcbiAqIEBtZXRob2Qgb25SZW1vdmUobWFwOiBNYXApOiB0aGlzXG4gKiBTaG91bGQgY29udGFpbiBhbGwgY2xlYW4gdXAgY29kZSB0aGF0IHJlbW92ZXMgdGhlIGxheWVyJ3MgZWxlbWVudHMgZnJvbSB0aGUgRE9NIGFuZCByZW1vdmVzIGxpc3RlbmVycyBwcmV2aW91c2x5IGFkZGVkIGluIFtgb25BZGRgXSgjbGF5ZXItb25hZGQpLiBDYWxsZWQgb24gW2BtYXAucmVtb3ZlTGF5ZXIobGF5ZXIpYF0oI21hcC1yZW1vdmVsYXllcikuXG4gKlxuICogQG1ldGhvZCBnZXRFdmVudHMoKTogT2JqZWN0XG4gKiBUaGlzIG9wdGlvbmFsIG1ldGhvZCBzaG91bGQgcmV0dXJuIGFuIG9iamVjdCBsaWtlIGB7IHZpZXdyZXNldDogdGhpcy5fcmVzZXQgfWAgZm9yIFtgYWRkRXZlbnRMaXN0ZW5lcmBdKCNldmVudGVkLWFkZGV2ZW50bGlzdGVuZXIpLiBUaGUgZXZlbnQgaGFuZGxlcnMgaW4gdGhpcyBvYmplY3Qgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IGFkZGVkIGFuZCByZW1vdmVkIGZyb20gdGhlIG1hcCB3aXRoIHlvdXIgbGF5ZXIuXG4gKlxuICogQG1ldGhvZCBnZXRBdHRyaWJ1dGlvbigpOiBTdHJpbmdcbiAqIFRoaXMgb3B0aW9uYWwgbWV0aG9kIHNob3VsZCByZXR1cm4gYSBzdHJpbmcgY29udGFpbmluZyBIVE1MIHRvIGJlIHNob3duIG9uIHRoZSBgQXR0cmlidXRpb24gY29udHJvbGAgd2hlbmV2ZXIgdGhlIGxheWVyIGlzIHZpc2libGUuXG4gKlxuICogQG1ldGhvZCBiZWZvcmVBZGQobWFwOiBNYXApOiB0aGlzXG4gKiBPcHRpb25hbCBtZXRob2QuIENhbGxlZCBvbiBbYG1hcC5hZGRMYXllcihsYXllcilgXSgjbWFwLWFkZGxheWVyKSwgYmVmb3JlIHRoZSBsYXllciBpcyBhZGRlZCB0byB0aGUgbWFwLCBiZWZvcmUgZXZlbnRzIGFyZSBpbml0aWFsaXplZCwgd2l0aG91dCB3YWl0aW5nIHVudGlsIHRoZSBtYXAgaXMgaW4gYSB1c2FibGUgc3RhdGUuIFVzZSBmb3IgZWFybHkgaW5pdGlhbGl6YXRpb24gb25seS5cbiAqL1xuXG5cbi8qIEBuYW1lc3BhY2UgTWFwXG4gKiBAc2VjdGlvbiBMYXllciBldmVudHNcbiAqXG4gKiBAZXZlbnQgbGF5ZXJhZGQ6IExheWVyRXZlbnRcbiAqIEZpcmVkIHdoZW4gYSBuZXcgbGF5ZXIgaXMgYWRkZWQgdG8gdGhlIG1hcC5cbiAqXG4gKiBAZXZlbnQgbGF5ZXJyZW
147751vdmU6IExheWVyRXZlbnRcbiAqIEZpcmVkIHdoZW4gc29tZSBsYXllciBpcyByZW1vdmVkIGZyb20gdGhlIG1hcFxuICpcbiAqIEBzZWN0aW9uIE1ldGhvZHMgZm9yIExheWVycyBhbmQgQ29udHJvbHNcbiAqL1xuTWFwLmluY2x1ZGUoe1xuXHQvLyBAbWV0aG9kIGFkZExheWVyKGxheWVyOiBMYXllcik6IHRoaXNcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gbGF5ZXIgdG8gdGhlIG1hcFxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKCFsYXllci5fbGF5ZXJBZGQpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignVGhlIHByb3ZpZGVkIG9iamVjdCBpcyBub3QgYSBMYXllci4nKTtcblx0XHR9XG5cblx0XHR2YXIgaWQgPSBzdGFtcChsYXllcik7XG5cdFx0aWYgKHRoaXMuX2xheWVyc1tpZF0pIHsgcmV0dXJuIHRoaXM7IH1cblx0XHR0aGlzLl9sYXllcnNbaWRdID0gbGF5ZXI7XG5cblx0XHRsYXllci5fbWFwVG9BZGQgPSB0aGlzO1xuXG5cdFx0aWYgKGxheWVyLmJlZm9yZUFkZCkge1xuXHRcdFx0bGF5ZXIuYmVmb3JlQWRkKHRoaXMpO1xuXHRcdH1cblxuXHRcdHRoaXMud2hlblJlYWR5KGxheWVyLl9sYXllckFkZCwgbGF5ZXIpO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGdpdmVuIGxheWVyIGZyb20gdGhlIG1hcC5cblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IHN0YW1wKGxheWVyKTtcblxuXHRcdGlmICghdGhpcy5fbGF5ZXJzW2lkXSkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xuXHRcdFx0bGF5ZXIub25SZW1vdmUodGhpcyk7XG5cdFx0fVxuXG5cdFx0aWYgKGxheWVyLmdldEF0dHJpYnV0aW9uICYmIHRoaXMuYXR0cmlidXRpb25Db250cm9sKSB7XG5cdFx0XHR0aGlzLmF0dHJpYnV0aW9uQ29udHJvbC5yZW1vdmVBdHRyaWJ1dGlvbihsYXllci5nZXRBdHRyaWJ1dGlvbigpKTtcblx0XHR9XG5cblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW2lkXTtcblxuXHRcdGlmICh0aGlzLl9sb2FkZWQpIHtcblx0XHRcdHRoaXMuZmlyZSgnbGF5ZXJyZW1vdmUnLCB7bGF5ZXI6IGxheWVyfSk7XG5cdFx0XHRsYXllci5maXJlKCdyZW1vdmUnKTtcblx0XHR9XG5cblx0XHRsYXllci5fbWFwID0gbGF5ZXIuX21hcFRvQWRkID0gbnVsbDtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gbGF5ZXIgaXMgY3VycmVudGx5IGFkZGVkIHRvIHRoZSBtYXBcblx0aGFzTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHJldHVybiAhIWxheWVyICYmIChzdGFtcChsYXllcikgaW4gdGhpcy5fbGF5ZXJzKTtcblx0fSxcblxuXHQvKiBAbWV0aG9kIGVhY2hMYXllcihmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXG5cdCAqIEl0ZXJhdGVzIG92ZXIgdGhlIGxheWVycyBvZiB0aGUgbWFwLCBvcHRpb25hbGx5IHNwZWNpZnlpbmcgY29udGV4dCBvZiB0aGUgaXRlcmF0b3IgZnVuY3Rpb24uXG5cdCAqIGBgYFxuXHQgKiBtYXAuZWFjaExheWVyKGZ1bmN0aW9uKGxheWVyKXtcblx0ICogICAgIGxheWVyLmJpbmRQb3B1cCgnSGVsbG8nKTtcblx0ICogfSk7XG5cdCAqIGBgYFxuXHQgKi9cblx0ZWFjaExheWVyOiBmdW5jdGlvbiAobWV0aG9kLCBjb250ZXh0KSB7XG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdG1ldGhvZC5jYWxsKGNvbnRleHQsIHRoaXMuX2xheWVyc1tpXSk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9hZGRMYXllcnM6IGZ1bmN0aW9uIChsYXllcnMpIHtcblx0XHRsYXllcnMgPSBsYXllcnMgPyAoaXNBcnJheShsYXllcnMpID8gbGF5ZXJzIDogW2xheWVyc10pIDogW107XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHR0aGlzLmFkZExheWVyKGxheWVyc1tpXSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hZGRab29tTGltaXQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdGlmIChpc05hTihsYXllci5vcHRpb25zLm1heFpvb20pIHx8ICFpc05hTihsYXllci5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aGlzLl96b29tQm91bmRMYXllcnNbc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXHRcdFx0dGhpcy5fdXBkYXRlWm9vbUxldmVscygpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlWm9vbUxpbWl0OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgaWQgPSBzdGFtcChsYXllcik7XG5cblx0XHRpZiAodGhpcy5fem9vbUJvdW5kTGF5ZXJzW2lkXSkge1xuXHRcdFx0ZGVsZXRlIHRoaXMuX3pvb21Cb3VuZExheWVyc1tpZF07XG5cdFx0XHR0aGlzLl91cGRhdGVab29tTGV2ZWxzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVab29tTGV2ZWxzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1pblpvb20gPSBJbmZpbml0eSxcblx0XHQgICAgbWF4Wm9vbSA9IC1JbmZpbml0eSxcblx0XHQgICAgb2xkWm9vbVNwYW4gPSB0aGlzLl9nZXRab29tU3BhbigpO1xuXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl96b29tQm91bmRMYXllcnMpIHtcblx0XHRcdHZhciBvcHRpb25zID0gdGhpcy5fem9vbUJvdW5kTGF5ZXJzW2ldLm9wdGlvbnM7XG5cblx0XHRcdG1pblpvb20gPSBvcHRpb25zLm1pblpvb20gPT09IHVuZGVmaW5lZCA/IG1pblpvb20gOiBNYXRoLm1pbihtaW5ab29tLCBvcHRpb25zLm1pblpvb20pO1xuXHRcdFx0bWF4Wm9vbSA9IG9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkID8gbWF4Wm9vbSA6IE1hdGgubWF4KG1heFpvb20sIG9wdGlvbnMubWF4Wm9vbSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fbGF5ZXJzTWF4Wm9vbSA9IG1heFpvb20gPT09IC1JbmZpbml0eSA/IHVuZGVmaW5lZCA6IG1heFpvb207XG5cdFx0dGhpcy5fbGF5ZXJzTWluWm9vbSA9IG1pblpvb20gPT09IEluZmluaXR5ID8gdW5kZWZpbmVkIDogbWluWm9vbTtcblxuXHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHpvb21sZXZlbHNjaGFuZ2U6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbnVtYmVyIG9mIHpvb21sZXZlbHMgb24gdGhlIG1hcCBpcyBjaGFuZ2VkIGR1ZVxuXHRcdC8vIHRvIGFkZGluZyBvciByZW1vdmluZyBhIGxheWVyLlxuXHRcdGlmIChvbGRab29tU3BhbiAhPT0gdGhpcy5fZ2V0Wm9vbVNwYW4oKSkge1xuXHRcdFx0dGhpcy5maXJlKCd6b29tbGV2ZWxzY2hhbmdlJyk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5tYXhab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWF4Wm9vbSAmJiB0aGlzLmdldFpvb20oKSA+IHRoaXMuX2xheWVyc01heFpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNYXhab29tKTtcblx0XHR9XG5cdFx0aWYgKHRoaXMub3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWluWm9vbSAmJiB0aGlzLmdldFpvb20oKSA8IHRoaXMuX2xheWVyc01pblpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNaW5ab29tKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vKlxyXG4gKiBAY2xhc3MgTGF5ZXJHcm91cFxyXG4gKiBAYWthIEwuTGF5ZXJHcm91cFxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcclxuICpcclxuICogVXNlZCB0byBncm91cCBzZXZlcmFsIGxheWVycyBhbmQgaGFuZGxlIHRoZW0gYXMgb25lLiBJZiB5b3UgYWRkIGl0IHRvIHRoZSBtYXAsXHJcbiAqIGFueSBsYXllcnMgYWRkZWQgb3IgcmVtb3ZlZCBmcm9tIHRoZSBncm91cCB3aWxsIGJlIGFkZGVkL3JlbW92ZWQgb24gdGhlIG1hcCBhc1xyXG4gKiB3ZWxsLiBFeHRlbmRzIGBMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwubGF5ZXJHcm91cChbbWFya2VyMSwgbWFya2VyMl0pXHJcbiAqIFx0LmFkZExheWVyKHBvbHlsaW5lKVxyXG4gKiBcdC5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG52YXIgTGF5ZXJHcm91cCA9IExheWVyLmV4dGVuZCh7XHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXllcnMsIG9wdGlvbnMpIHtcclxuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0dmFyIGksIGxlbjtcclxuXHJcblx0XHRpZiAobGF5ZXJzKSB7XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGxheWVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuYWRkTGF5ZXIobGF5ZXJzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkTGF5ZXIobGF5ZXI6IExheWVyKTogdGh
14775pc1xyXG5cdC8vIEFkZHMgdGhlIGdpdmVuIGxheWVyIHRvIHRoZSBncm91cC5cclxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHR2YXIgaWQgPSB0aGlzLmdldExheWVySWQobGF5ZXIpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVyc1tpZF0gPSBsYXllcjtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihsYXllcik7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgZ2l2ZW4gbGF5ZXIgZnJvbSB0aGUgZ3JvdXAuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihpZDogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIHdpdGggdGhlIGdpdmVuIGludGVybmFsIElEIGZyb20gdGhlIGdyb3VwLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHZhciBpZCA9IGxheWVyIGluIHRoaXMuX2xheWVycyA/IGxheWVyIDogdGhpcy5nZXRMYXllcklkKGxheWVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwICYmIHRoaXMuX2xheWVyc1tpZF0pIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX2xheWVyc1tpZF0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBsYXllciBpcyBjdXJyZW50bHkgYWRkZWQgdG8gdGhlIGdyb3VwLlxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIoaWQ6IE51bWJlcik6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gaW50ZXJuYWwgSUQgaXMgY3VycmVudGx5IGFkZGVkIHRvIHRoZSBncm91cC5cclxuXHRoYXNMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRyZXR1cm4gISFsYXllciAmJiAobGF5ZXIgaW4gdGhpcy5fbGF5ZXJzIHx8IHRoaXMuZ2V0TGF5ZXJJZChsYXllcikgaW4gdGhpcy5fbGF5ZXJzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNsZWFyTGF5ZXJzKCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIGFsbCB0aGUgbGF5ZXJzIGZyb20gdGhlIGdyb3VwLlxyXG5cdGNsZWFyTGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5lYWNoTGF5ZXIodGhpcy5yZW1vdmVMYXllciwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnZva2UobWV0aG9kTmFtZTogU3RyaW5nLCDigKYpOiB0aGlzXHJcblx0Ly8gQ2FsbHMgYG1ldGhvZE5hbWVgIG9uIGV2ZXJ5IGxheWVyIGNvbnRhaW5lZCBpbiB0aGlzIGdyb3VwLCBwYXNzaW5nIGFueVxyXG5cdC8vIGFkZGl0aW9uYWwgcGFyYW1ldGVycy4gSGFzIG5vIGVmZmVjdCBpZiB0aGUgbGF5ZXJzIGNvbnRhaW5lZCBkbyBub3RcclxuXHQvLyBpbXBsZW1lbnQgYG1ldGhvZE5hbWVgLlxyXG5cdGludm9rZTogZnVuY3Rpb24gKG1ldGhvZE5hbWUpIHtcclxuXHRcdHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSxcclxuXHRcdCAgICBpLCBsYXllcjtcclxuXHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2ldO1xyXG5cclxuXHRcdFx0aWYgKGxheWVyW21ldGhvZE5hbWVdKSB7XHJcblx0XHRcdFx0bGF5ZXJbbWV0aG9kTmFtZV0uYXBwbHkobGF5ZXIsIGFyZ3MpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuZWFjaExheWVyKG1hcC5hZGRMYXllciwgbWFwKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5lYWNoTGF5ZXIobWFwLnJlbW92ZUxheWVyLCBtYXApO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZWFjaExheWVyKGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQvLyBJdGVyYXRlcyBvdmVyIHRoZSBsYXllcnMgb2YgdGhlIGdyb3VwLCBvcHRpb25hbGx5IHNwZWNpZnlpbmcgY29udGV4dCBvZiB0aGUgaXRlcmF0b3IgZnVuY3Rpb24uXHJcblx0Ly8gYGBganNcclxuXHQvLyBncm91cC5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0Ly8gXHRsYXllci5iaW5kUG9wdXAoJ0hlbGxvJyk7XHJcblx0Ly8gfSk7XHJcblx0Ly8gYGBgXHJcblx0ZWFjaExheWVyOiBmdW5jdGlvbiAobWV0aG9kLCBjb250ZXh0KSB7XHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRtZXRob2QuY2FsbChjb250ZXh0LCB0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXllcihpZDogTnVtYmVyKTogTGF5ZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBsYXllciB3aXRoIHRoZSBnaXZlbiBpbnRlcm5hbCBJRC5cclxuXHRnZXRMYXllcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbGF5ZXJzW2lkXTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExheWVycygpOiBMYXllcltdXHJcblx0Ly8gUmV0dXJucyBhbiBhcnJheSBvZiBhbGwgdGhlIGxheWVycyBhZGRlZCB0byB0aGUgZ3JvdXAuXHJcblx0Z2V0TGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbGF5ZXJzID0gW107XHJcblx0XHR0aGlzLmVhY2hMYXllcihsYXllcnMucHVzaCwgbGF5ZXJzKTtcclxuXHRcdHJldHVybiBsYXllcnM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRaSW5kZXgoekluZGV4OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gQ2FsbHMgYHNldFpJbmRleGAgb24gZXZlcnkgbGF5ZXIgY29udGFpbmVkIGluIHRoaXMgZ3JvdXAsIHBhc3NpbmcgdGhlIHotaW5kZXguXHJcblx0c2V0WkluZGV4OiBmdW5jdGlvbiAoekluZGV4KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ3NldFpJbmRleCcsIHpJbmRleCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXllcklkKGxheWVyOiBMYXllcik6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGludGVybmFsIElEIGZvciBhIGxheWVyXHJcblx0Z2V0TGF5ZXJJZDogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRyZXR1cm4gc3RhbXAobGF5ZXIpO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXllckdyb3VwKGxheWVycz86IExheWVyW10sIG9wdGlvbnM/OiBPYmplY3QpXHJcbi8vIENyZWF0ZSBhIGxheWVyIGdyb3VwLCBvcHRpb25hbGx5IGdpdmVuIGFuIGluaXRpYWwgc2V0IG9mIGxheWVycyBhbmQgYW4gYG9wdGlvbnNgIG9iamVjdC5cclxudmFyIGxheWVyR3JvdXAgPSBmdW5jdGlvbiAobGF5ZXJzLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMYXllckdyb3VwKGxheWVycywgb3B0aW9ucyk7XHJcbn07XG5cbi8qXHJcbiAqIEBjbGFzcyBGZWF0dXJlR3JvdXBcclxuICogQGFrYSBMLkZlYXR1cmVHcm91cFxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJHcm91cFxyXG4gKlxyXG4gKiBFeHRlbmRlZCBgTGF5ZXJHcm91cGAgdGhhdCBtYWtlcyBpdCBlYXNpZXIgdG8gZG8gdGhlIHNhbWUgdGhpbmcgdG8gYWxsIGl0cyBtZW1iZXIgbGF5ZXJzOlxyXG4gKiAgKiBbYGJpbmRQb3B1cGBdKCNsYXllci1iaW5kcG9wdXApIGJpbmRzIGEgcG9wdXAgdG8gYWxsIG9mIHRoZSBsYXllcnMgYXQgb25jZSAobGlrZXdpc2Ugd2l0aCBbYGJpbmRUb29sdGlwYF0oI2xheWVyLWJpbmR0b29sdGlwKSlcclxuICogICogRXZlbnRzIGFyZSBwcm9wYWdhdGVkIHRvIHRoZSBgRmVhdHVyZUdyb3VwYCwgc28gaWYgdGhlIGdyb3VwIGhhcyBhbiBldmVudFxyXG4gKiBoYW5kbGVyLCBpdCB3aWxsIGhhbmRsZSBldmVudHMgZnJvbSBhbnkgb2YgdGhlIGxheWVycy4gVGhpcyBpbmNsdWRlcyBtb3VzZSBldmVudHNcclxuICogYW5kIGN1c3RvbSBldmVudHMuXHJcbiAqICAqIEhhcyBgbGF5ZXJhZGRgIGFuZCBgbGF5ZXJyZW
147751vdmVgIGV2ZW50c1xyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmZlYXR1cmVHcm91cChbbWFya2VyMSwgbWFya2VyMiwgcG9seWxpbmVdKVxyXG4gKiBcdC5iaW5kUG9wdXAoJ0hlbGxvIHdvcmxkIScpXHJcbiAqIFx0Lm9uKCdjbGljaycsIGZ1bmN0aW9uKCkgeyBhbGVydCgnQ2xpY2tlZCBvbiBhIG1lbWJlciBvZiB0aGUgZ3JvdXAhJyk7IH0pXHJcbiAqIFx0LmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbnZhciBGZWF0dXJlR3JvdXAgPSBMYXllckdyb3VwLmV4dGVuZCh7XHJcblxyXG5cdGFkZExheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGlmICh0aGlzLmhhc0xheWVyKGxheWVyKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRsYXllci5hZGRFdmVudFBhcmVudCh0aGlzKTtcclxuXHJcblx0XHRMYXllckdyb3VwLnByb3RvdHlwZS5hZGRMYXllci5jYWxsKHRoaXMsIGxheWVyKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgbGF5ZXJhZGQ6IExheWVyRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBsYXllciBpcyBhZGRlZCB0byB0aGlzIGBGZWF0dXJlR3JvdXBgXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdsYXllcmFkZCcsIHtsYXllcjogbGF5ZXJ9KTtcclxuXHR9LFxyXG5cclxuXHRyZW1vdmVMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRpZiAoIXRoaXMuaGFzTGF5ZXIobGF5ZXIpKSB7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGxheWVyIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tsYXllcl07XHJcblx0XHR9XHJcblxyXG5cdFx0bGF5ZXIucmVtb3ZlRXZlbnRQYXJlbnQodGhpcyk7XHJcblxyXG5cdFx0TGF5ZXJHcm91cC5wcm90b3R5cGUucmVtb3ZlTGF5ZXIuY2FsbCh0aGlzLCBsYXllcik7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxheWVycmVtb3ZlOiBMYXllckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoaXMgYEZlYXR1cmVHcm91cGBcclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ2xheWVycmVtb3ZlJywge2xheWVyOiBsYXllcn0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoc3R5bGU6IFBhdGggb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBnaXZlbiBwYXRoIG9wdGlvbnMgdG8gZWFjaCBsYXllciBvZiB0aGUgZ3JvdXAgdGhhdCBoYXMgYSBgc2V0U3R5bGVgIG1ldGhvZC5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ3NldFN0eWxlJywgc3R5bGUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250KCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSB0b3Agb2YgYWxsIG90aGVyIGxheWVyc1xyXG5cdGJyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvRnJvbnQnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrKCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSBiYWNrIG9mIGFsbCBvdGhlciBsYXllcnNcclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvQmFjaycpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIExhdExuZ0JvdW5kcyBvZiB0aGUgRmVhdHVyZSBHcm91cCAoY3JlYXRlZCBmcm9tIGJvdW5kcyBhbmQgY29vcmRpbmF0ZXMgb2YgaXRzIGNoaWxkcmVuKS5cclxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBib3VuZHMgPSBuZXcgTGF0TG5nQm91bmRzKCk7XHJcblxyXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdHZhciBsYXllciA9IHRoaXMuX2xheWVyc1tpZF07XHJcblx0XHRcdGJvdW5kcy5leHRlbmQobGF5ZXIuZ2V0Qm91bmRzID8gbGF5ZXIuZ2V0Qm91bmRzKCkgOiBsYXllci5nZXRMYXRMbmcoKSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gYm91bmRzO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAZmFjdG9yeSBMLmZlYXR1cmVHcm91cChsYXllcnM6IExheWVyW10pXHJcbi8vIENyZWF0ZSBhIGZlYXR1cmUgZ3JvdXAsIG9wdGlvbmFsbHkgZ2l2ZW4gYW4gaW5pdGlhbCBzZXQgb2YgbGF5ZXJzLlxyXG52YXIgZmVhdHVyZUdyb3VwID0gZnVuY3Rpb24gKGxheWVycykge1xyXG5cdHJldHVybiBuZXcgRmVhdHVyZUdyb3VwKGxheWVycyk7XHJcbn07XG5cbi8qXHJcbiAqIEBjbGFzcyBJY29uXHJcbiAqIEBha2EgTC5JY29uXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYW4gaWNvbiB0byBwcm92aWRlIHdoZW4gY3JlYXRpbmcgYSBtYXJrZXIuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBteUljb24gPSBMLmljb24oe1xyXG4gKiAgICAgaWNvblVybDogJ215LWljb24ucG5nJyxcclxuICogICAgIGljb25SZXRpbmFVcmw6ICdteS1pY29uQDJ4LnBuZycsXHJcbiAqICAgICBpY29uU2l6ZTogWzM4LCA5NV0sXHJcbiAqICAgICBpY29uQW5jaG9yOiBbMjIsIDk0XSxcclxuICogICAgIHBvcHVwQW5jaG9yOiBbLTMsIC03Nl0sXHJcbiAqICAgICBzaGFkb3dVcmw6ICdteS1pY29uLXNoYWRvdy5wbmcnLFxyXG4gKiAgICAgc2hhZG93UmV0aW5hVXJsOiAnbXktaWNvbi1zaGFkb3dAMngucG5nJyxcclxuICogICAgIHNoYWRvd1NpemU6IFs2OCwgOTVdLFxyXG4gKiAgICAgc2hhZG93QW5jaG9yOiBbMjIsIDk0XVxyXG4gKiB9KTtcclxuICpcclxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIGBMLkljb24uRGVmYXVsdGAgZXh0ZW5kcyBgTC5JY29uYCBhbmQgaXMgdGhlIGJsdWUgaWNvbiBMZWFmbGV0IHVzZXMgZm9yIG1hcmtlcnMgYnkgZGVmYXVsdC5cclxuICpcclxuICovXHJcblxyXG52YXIgSWNvbiA9IENsYXNzLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQGFrYSBJY29uIG9wdGlvbnNcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvblVybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqICoqKHJlcXVpcmVkKSoqIFRoZSBVUkwgdG8gdGhlIGljb24gaW1hZ2UgKGFic29sdXRlIG9yIHJlbGF0aXZlIHRvIHlvdXIgc2NyaXB0IHBhdGgpLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uUmV0aW5hVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogVGhlIFVSTCB0byBhIHJldGluYSBzaXplZCB2ZXJzaW9uIG9mIHRoZSBpY29uIGltYWdlIChhYnNvbHV0ZSBvciByZWxhdGl2ZSB0byB5b3VyXHJcblx0ICogc2NyaXB0IHBhdGgpLiBVc2VkIGZvciBSZXRpbmEgc2NyZWVuIGRldmljZXMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGljb25TaXplOiBQb2ludCA9IG51bGxcclxuXHQgKiBTaXplIG9mIHRoZSBpY29uIGltYWdlIGluIHBpeGVscy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvbkFuY2hvcjogUG9pbnQgPSBudWxsXHJcblx0ICogVGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBcInRpcFwiIG9mIHRoZSBpY29uIChyZWxhdGl2ZSB0byBpdHMgdG9wIGxlZnQgY29ybmVyKS4gVGhlIGljb25cclxuXHQgKiB3aWxsIGJlIGFsaWduZWQgc28gdGhhdCB0aGlzIHBvaW50IGlzIGF0IHRoZSBtYXJrZXIncyBnZW9ncmFwaGljYWwgbG9jYXRpb24uIENlbnRlcmVkXHJcblx0ICogYnkgZGVmYXVsdCBpZiBzaXplIGlzIHNwZWNpZmllZCwgYWxzbyBjYW4gYmUgc2V0IGluIENTUyB3aXRoIG5lZ2F0aXZlIG1hcmdpbnMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvcHVwQW5jaG9yOiBQb2ludCA9IFswLCAwXVxyXG5cdCAqIFRoZSBjb29yZGluYX
14775RlcyBvZiB0aGUgcG9pbnQgZnJvbSB3aGljaCBwb3B1cHMgd2lsbCBcIm9wZW5cIiwgcmVsYXRpdmUgdG8gdGhlIGljb24gYW5jaG9yLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiB0b29sdGlwQW5jaG9yOiBQb2ludCA9IFswLCAwXVxyXG5cdCAqIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgcG9pbnQgZnJvbSB3aGljaCB0b29sdGlwcyB3aWxsIFwib3BlblwiLCByZWxhdGl2ZSB0byB0aGUgaWNvbiBhbmNob3IuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd1VybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqIFRoZSBVUkwgdG8gdGhlIGljb24gc2hhZG93IGltYWdlLiBJZiBub3Qgc3BlY2lmaWVkLCBubyBzaGFkb3cgaW1hZ2Ugd2lsbCBiZSBjcmVhdGVkLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dSZXRpbmFVcmw6IFN0cmluZyA9IG51bGxcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93U2l6ZTogUG9pbnQgPSBudWxsXHJcblx0ICogU2l6ZSBvZiB0aGUgc2hhZG93IGltYWdlIGluIHBpeGVscy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93QW5jaG9yOiBQb2ludCA9IG51bGxcclxuXHQgKiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIFwidGlwXCIgb2YgdGhlIHNoYWRvdyAocmVsYXRpdmUgdG8gaXRzIHRvcCBsZWZ0IGNvcm5lcikgKHRoZSBzYW1lXHJcblx0ICogYXMgaWNvbkFuY2hvciBpZiBub3Qgc3BlY2lmaWVkKS5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdCAqIEEgY3VzdG9tIGNsYXNzIG5hbWUgdG8gYXNzaWduIHRvIGJvdGggaWNvbiBhbmQgc2hhZG93IGltYWdlcy4gRW1wdHkgYnkgZGVmYXVsdC5cclxuXHQgKi9cclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0cG9wdXBBbmNob3I6IFswLCAwXSxcclxuXHRcdHRvb2x0aXBBbmNob3I6IFswLCAwXVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRzZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlSWNvbihvbGRJY29uPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENhbGxlZCBpbnRlcm5hbGx5IHdoZW4gdGhlIGljb24gaGFzIHRvIGJlIHNob3duLCByZXR1cm5zIGEgYDxpbWc+YCBIVE1MIGVsZW1lbnRcclxuXHQvLyBzdHlsZWQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zLlxyXG5cdGNyZWF0ZUljb246IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignaWNvbicsIG9sZEljb24pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlU2hhZG93KG9sZEljb24/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQXMgYGNyZWF0ZUljb25gLCBidXQgZm9yIHRoZSBzaGFkb3cgYmVuZWF0aCBpdC5cclxuXHRjcmVhdGVTaGFkb3c6IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignc2hhZG93Jywgb2xkSWNvbik7XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUljb246IGZ1bmN0aW9uIChuYW1lLCBvbGRJY29uKSB7XHJcblx0XHR2YXIgc3JjID0gdGhpcy5fZ2V0SWNvblVybChuYW1lKTtcclxuXHJcblx0XHRpZiAoIXNyYykge1xyXG5cdFx0XHRpZiAobmFtZSA9PT0gJ2ljb24nKSB7XHJcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCdpY29uVXJsIG5vdCBzZXQgaW4gSWNvbiBvcHRpb25zIChzZWUgdGhlIGRvY3MpLicpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpbWcgPSB0aGlzLl9jcmVhdGVJbWcoc3JjLCBvbGRJY29uICYmIG9sZEljb24udGFnTmFtZSA9PT0gJ0lNRycgPyBvbGRJY29uIDogbnVsbCk7XHJcblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGltZywgbmFtZSk7XHJcblxyXG5cdFx0cmV0dXJuIGltZztcclxuXHR9LFxyXG5cclxuXHRfc2V0SWNvblN0eWxlczogZnVuY3Rpb24gKGltZywgbmFtZSkge1xyXG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblx0XHR2YXIgc2l6ZU9wdGlvbiA9IG9wdGlvbnNbbmFtZSArICdTaXplJ107XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBzaXplT3B0aW9uID09PSAnbnVtYmVyJykge1xyXG5cdFx0XHRzaXplT3B0aW9uID0gW3NpemVPcHRpb24sIHNpemVPcHRpb25dO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzaXplID0gdG9Qb2ludChzaXplT3B0aW9uKSxcclxuXHRcdCAgICBhbmNob3IgPSB0b1BvaW50KG5hbWUgPT09ICdzaGFkb3cnICYmIG9wdGlvbnMuc2hhZG93QW5jaG9yIHx8IG9wdGlvbnMuaWNvbkFuY2hvciB8fFxyXG5cdFx0ICAgICAgICAgICAgc2l6ZSAmJiBzaXplLmRpdmlkZUJ5KDIsIHRydWUpKTtcclxuXHJcblx0XHRpbWcuY2xhc3NOYW1lID0gJ2xlYWZsZXQtbWFya2VyLScgKyBuYW1lICsgJyAnICsgKG9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKTtcclxuXHJcblx0XHRpZiAoYW5jaG9yKSB7XHJcblx0XHRcdGltZy5zdHlsZS5tYXJnaW5MZWZ0ID0gKC1hbmNob3IueCkgKyAncHgnO1xyXG5cdFx0XHRpbWcuc3R5bGUubWFyZ2luVG9wICA9ICgtYW5jaG9yLnkpICsgJ3B4JztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoc2l6ZSkge1xyXG5cdFx0XHRpbWcuc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4JztcclxuXHRcdFx0aW1nLnN0eWxlLmhlaWdodCA9IHNpemUueSArICdweCc7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUltZzogZnVuY3Rpb24gKHNyYywgZWwpIHtcclxuXHRcdGVsID0gZWwgfHwgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XHJcblx0XHRlbC5zcmMgPSBzcmM7XHJcblx0XHRyZXR1cm4gZWw7XHJcblx0fSxcclxuXHJcblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XHJcblx0XHRyZXR1cm4gcmV0aW5hICYmIHRoaXMub3B0aW9uc1tuYW1lICsgJ1JldGluYVVybCddIHx8IHRoaXMub3B0aW9uc1tuYW1lICsgJ1VybCddO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5pY29uKG9wdGlvbnM6IEljb24gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhbiBpY29uIGluc3RhbmNlIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXHJcbmZ1bmN0aW9uIGljb24ob3B0aW9ucykge1xyXG5cdHJldHVybi
14775BuZXcgSWNvbihvcHRpb25zKTtcclxufVxuXG4vKlxuICogQG1pbmljbGFzcyBJY29uLkRlZmF1bHQgKEljb24pXG4gKiBAYWthIEwuSWNvbi5EZWZhdWx0XG4gKiBAc2VjdGlvblxuICpcbiAqIEEgdHJpdmlhbCBzdWJjbGFzcyBvZiBgSWNvbmAsIHJlcHJlc2VudHMgdGhlIGljb24gdG8gdXNlIGluIGBNYXJrZXJgcyB3aGVuXG4gKiBubyBpY29uIGlzIHNwZWNpZmllZC4gUG9pbnRzIHRvIHRoZSBibHVlIG1hcmtlciBpbWFnZSBkaXN0cmlidXRlZCB3aXRoIExlYWZsZXRcbiAqIHJlbGVhc2VzLlxuICpcbiAqIEluIG9yZGVyIHRvIGN1c3RvbWl6ZSB0aGUgZGVmYXVsdCBpY29uLCBqdXN0IGNoYW5nZSB0aGUgcHJvcGVydGllcyBvZiBgTC5JY29uLkRlZmF1bHQucHJvdG90eXBlLm9wdGlvbnNgXG4gKiAod2hpY2ggaXMgYSBzZXQgb2YgYEljb24gb3B0aW9uc2ApLlxuICpcbiAqIElmIHlvdSB3YW50IHRvIF9jb21wbGV0ZWx5XyByZXBsYWNlIHRoZSBkZWZhdWx0IGljb24sIG92ZXJyaWRlIHRoZVxuICogYEwuTWFya2VyLnByb3RvdHlwZS5vcHRpb25zLmljb25gIHdpdGggeW91ciBvd24gaWNvbiBpbnN0ZWFkLlxuICovXG5cbnZhciBJY29uRGVmYXVsdCA9IEljb24uZXh0ZW5kKHtcblxuXHRvcHRpb25zOiB7XG5cdFx0aWNvblVybDogICAgICAgJ21hcmtlci1pY29uLnBuZycsXG5cdFx0aWNvblJldGluYVVybDogJ21hcmtlci1pY29uLTJ4LnBuZycsXG5cdFx0c2hhZG93VXJsOiAgICAgJ21hcmtlci1zaGFkb3cucG5nJyxcblx0XHRpY29uU2l6ZTogICAgWzI1LCA0MV0sXG5cdFx0aWNvbkFuY2hvcjogIFsxMiwgNDFdLFxuXHRcdHBvcHVwQW5jaG9yOiBbMSwgLTM0XSxcblx0XHR0b29sdGlwQW5jaG9yOiBbMTYsIC0yOF0sXG5cdFx0c2hhZG93U2l6ZTogIFs0MSwgNDFdXG5cdH0sXG5cblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0aWYgKCFJY29uRGVmYXVsdC5pbWFnZVBhdGgpIHtcdC8vIERlcHJlY2F0ZWQsIGJhY2t3YXJkcy1jb21wYXRpYmlsaXR5IG9ubHlcblx0XHRcdEljb25EZWZhdWx0LmltYWdlUGF0aCA9IHRoaXMuX2RldGVjdEljb25QYXRoKCk7XG5cdFx0fVxuXG5cdFx0Ly8gQG9wdGlvbiBpbWFnZVBhdGg6IFN0cmluZ1xuXHRcdC8vIGBJY29uLkRlZmF1bHRgIHdpbGwgdHJ5IHRvIGF1dG8tZGV0ZWN0IHRoZSBsb2NhdGlvbiBvZiB0aGVcblx0XHQvLyBibHVlIGljb24gaW1hZ2VzLiBJZiB5b3UgYXJlIHBsYWNpbmcgdGhlc2UgaW1hZ2VzIGluIGEgbm9uLXN0YW5kYXJkXG5cdFx0Ly8gd2F5LCBzZXQgdGhpcyBvcHRpb24gdG8gcG9pbnQgdG8gdGhlIHJpZ2h0IHBhdGguXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuaW1hZ2VQYXRoIHx8IEljb25EZWZhdWx0LmltYWdlUGF0aCkgKyBJY29uLnByb3RvdHlwZS5fZ2V0SWNvblVybC5jYWxsKHRoaXMsIG5hbW
14775UpO1xuXHR9LFxuXG5cdF9kZXRlY3RJY29uUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBlbCA9IGNyZWF0ZSQxKCdkaXYnLCAgJ2xlYWZsZXQtZGVmYXVsdC1pY29uLXBhdGgnLCBkb2N1bWVudC5ib2R5KTtcblx0XHR2YXIgcGF0aCA9IGdldFN0eWxlKGVsLCAnYmFja2dyb3VuZC1pbWFnZScpIHx8XG5cdFx0ICAgICAgICAgICBnZXRTdHlsZShlbCwgJ2JhY2tncm91bmRJbWFnZScpO1x0Ly8gSUU4XG5cblx0XHRkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGVsKTtcblxuXHRcdGlmIChwYXRoID09PSBudWxsIHx8IHBhdGguaW5kZXhPZigndXJsJykgIT09IDApIHtcblx0XHRcdHBhdGggPSAnJztcblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aCA9IHBhdGgucmVwbGFjZSgvXnVybFxcKFtcIiddPy8sICcnKS5yZXBsYWNlKC9tYXJrZXItaWNvblxcLnBuZ1tcIiddP1xcKSQvLCAnJyk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHBhdGg7XG5cdH1cbn0pO1xuXG4vKlxuICogTC5IYW5kbGVyLk1hcmtlckRyYWcgaXMgdXNlZCBpbnRlcm5hbGx5IGJ5IEwuTWFya2VyIHRvIG1ha2UgdGhlIG1hcmtlcnMgZHJhZ2dhYmxlLlxuICovXG5cblxuLyogQG5hbWVzcGFjZSBNYXJrZXJcbiAqIEBzZWN0aW9uIEludGVyYWN0aW9uIGhhbmRsZXJzXG4gKlxuICogSW50ZXJhY3Rpb24gaGFuZGxlcnMgYXJlIHByb3BlcnRpZXMgb2YgYSBtYXJrZXIgaW5zdGFuY2UgdGhhdCBhbGxvdyB5b3UgdG8gY29udHJvbCBpbnRlcmFjdGlvbiBiZWhhdmlvciBpbiBydW50aW1lLCBlbmFibGluZyBvciBkaXNhYmxpbmcgY2VydGFpbiBmZWF0dXJlcyBzdWNoIGFzIGRyYWdnaW5nIChzZWUgYEhhbmRsZXJgIG1ldGhvZHMpLiBFeGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBtYXJrZXIuZHJhZ2dpbmcuZGlzYWJsZSgpO1xuICogYGBgXG4gKlxuICogQHByb3BlcnR5IGRyYWdnaW5nOiBIYW5kbGVyXG4gKiBNYXJrZXIgZHJhZ2dpbmcgaGFuZGxlciAoYnkgYm90aCBtb3VzZSBhbmQgdG91Y2gpLiBPbmx5IHZhbGlkIHdoZW4gdGhlIG1hcmtlciBpcyBvbiB0aGUgbWFwIChPdGhlcndpc2Ugc2V0IFtgbWFya2VyLm9wdGlvbnMuZHJhZ2dhYmxlYF0oI21hcmtlci1kcmFnZ2FibGUpKS5cbiAqL1xuXG52YXIgTWFya2VyRHJhZyA9IEhhbmRsZXIuZXh0ZW5kKHtcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcmtlcikge1xuXHRcdHRoaXMuX21hcmtlciA9IG1hcmtlcjtcblx0fSxcblxuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBpY29uID0gdGhpcy5fbWFya2VyLl9pY29uO1xuXG5cdFx0aWYgKCF0aGlzLl9kcmFnZ2FibGUpIHtcblx0XHRcdHRoaXMuX2RyYWdnYWJsZSA9IG5ldyBEcmFnZ2FibGUoaWNvbiwgaWNvbiwgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLm9uKHtcblx0XHRcdGRyYWdzdGFydDogdGhpcy5fb25EcmFnU3RhcnQsXG5cdFx0XHRwcmVkcmFnOiB0aGlzLl9vblByZURyYWcsXG5cdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHR9LCB0aGlzKS5lbmFibGUoKTtcblxuXHRcdGFkZENsYXNzKGljb24sICdsZWFmbGV0LW1hcmtlci1kcmFnZ2FibGUnKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5vZmYoe1xuXHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdHByZWRyYWc6IHRoaXMuX29uUHJlRHJhZyxcblx0XHRcdGRyYWc6IHRoaXMuX29uRHJhZyxcblx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdH0sIHRoaXMpLmRpc2FibGUoKTtcblxuXHRcdGlmICh0aGlzLl9tYXJrZXIuX2ljb24pIHtcblx0XHRcdHJlbW92ZUNsYXNzKHRoaXMuX21hcmtlci5faWNvbiwgJ2xlYWZsZXQtbWFya2VyLWRyYWdnYWJsZScpO1xuXHRcdH1cblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9kcmFnZ2FibGUgJiYgdGhpcy5fZHJhZ2dhYmxlLl9tb3ZlZDtcblx0fSxcblxuXHRfYWRqdXN0UGFuOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXJrZXIgPSB0aGlzLl9tYXJrZXIsXG5cdFx0ICAgIG1hcCA9IG1hcmtlci5fbWFwLFxuXHRcdCAgICBzcGVlZCA9IHRoaXMuX21hcmtlci5vcHRpb25zLmF1dG9QYW5TcGVlZCxcblx0XHQgICAgcGFkZGluZyA9IHRoaXMuX21hcmtlci5vcHRpb25zLmF1dG9QYW5QYWRkaW5nLFxuXHRcdCAgICBpY29uUG9zID0gZ2V0UG9zaXRpb24obWFya2VyLl9pY29uKSxcblx0XHQgICAgYm91bmRzID0gbWFwLmdldFBpeGVsQm91bmRzKCksXG5cdFx0ICAgIG9yaWdpbiA9IG1hcC5nZXRQaXhlbE9yaWdpbigpO1xuXG5cdFx0dmFyIHBhbkJvdW5kcyA9IHRvQm91bmRzKFxuXHRcdFx0Ym91bmRzLm1pbi5fc3VidHJhY3Qob3JpZ2luKS5hZGQocGFkZGluZyksXG5cdFx0XHRib3VuZHMubWF4Ll9zdWJ0cmFjdChvcmlnaW4pLnN1YnRyYWN0KHBhZGRpbmcpXG5cdFx0KTtcblxuXHRcdGlmICghcGFuQm91bmRzLmNvbnRhaW5zKGljb25Qb3MpKSB7XG5cdFx0XHQvLyBDb21wdXRlIGluY3JlbWVudGFsIG1vdmVtZW50XG5cdFx0XHR2YXIgbW92ZW1lbnQgPSB0b1BvaW50KFxuXHRcdFx0XHQoTWF0aC5tYXgocGFuQm91bmRzLm1heC54LCBpY29uUG9zLngpIC0gcGFuQm91bmRzLm1heC54KSAvIChib3VuZHMubWF4LnggLSBwYW5Cb3VuZHMubWF4LngpIC1cblx0XHRcdFx0KE1hdGgubWluKHBhbkJvdW5kcy5taW4ueCwgaWNvblBvcy54KSAtIHBhbkJvdW5kcy5taW4ueCkgLyAoYm91bmRzLm1pbi54IC0gcGFuQm91bmRzLm1pbi54KSxcblxuXHRcdFx0XHQoTWF0aC5tYXgocGFuQm91bmRzLm1heC55LCBpY29uUG9zLnkpIC0gcGFuQm91bmRzLm1heC55KSAvIChib3VuZHMubWF4LnkgLSBwYW5Cb3VuZHMubWF4LnkpIC1cblx0XHRcdFx0KE1hdGgubWluKHBhbkJvdW5kcy5taW4ueSwgaWNvblBvcy55KSAtIHBhbkJvdW5kcy5taW4ueSkgLyAoYm91bmRzLm1pbi55IC0gcGFuQm91bmRzLm1pbi55KVxuXHRcdFx0KS5tdWx0aXBseUJ5KHNwZWVkKTtcblxuXHRcdFx0bWFwLnBhbkJ5KG1vdmVtZW50LCB7YW5pbWF0ZTogZmFsc2V9KTtcblxuXHRcdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MuX2FkZChtb3ZlbWVudCk7XG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zLl9hZGQobW92ZW1lbnQpO1xuXG5cdFx0XHRzZXRQb3NpdGlvbihtY
14775XJrZXIuX2ljb24sIHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zKTtcblx0XHRcdHRoaXMuX29uRHJhZyhlKTtcblxuXHRcdFx0dGhpcy5fcGFuUmVxdWVzdCA9IHJlcXVlc3RBbmltRnJhbWUodGhpcy5fYWRqdXN0UGFuLmJpbmQodGhpcywgZSkpO1xuXHRcdH1cblx0fSxcblxuXHRfb25EcmFnU3RhcnQ6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBAc2VjdGlvbiBEcmFnZ2luZyBldmVudHNcblx0XHQvLyBAZXZlbnQgZHJhZ3N0YXJ0OiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgc3RhcnRzIGRyYWdnaW5nIHRoZSBtYXJrZXIuXG5cblx0XHQvLyBAZXZlbnQgbW92ZXN0YXJ0OiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBzdGFydHMgbW92aW5nIChiZWNhdXNlIG9mIGRyYWdnaW5nKS5cblxuXHRcdHRoaXMuX29sZExhdExuZyA9IHRoaXMuX21hcmtlci5nZXRMYXRMbmcoKTtcblx0XHR0aGlzLl9tYXJrZXJcblx0XHQgICAgLmNsb3NlUG9wdXAoKVxuXHRcdCAgICAuZmlyZSgnbW92ZXN0YXJ0Jylcblx0XHQgICAgLmZpcmUoJ2RyYWdzdGFydCcpO1xuXHR9LFxuXG5cdF9vblByZURyYWc6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKHRoaXMuX21hcmtlci5vcHRpb25zLmF1dG9QYW4pIHtcblx0XHRcdGNhbmNlbEFuaW1GcmFtZSh0aGlzLl9wYW5SZXF1ZXN0KTtcblx0XHRcdHRoaXMuX3BhblJlcXVlc3QgPSByZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX2FkanVzdFBhbi5iaW5kKHRoaXMsIGUpKTtcblx0XHR9XG5cdH0sXG5cblx0X29uRHJhZzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFya2VyID0gdGhpcy5fbWFya2VyLFxuXHRcdCAgICBzaGFkb3cgPSBtYXJrZXIuX3NoYWRvdyxcblx0XHQgICAgaWNvblBvcyA9IGdldFBvc2l0aW9uKG1hcmtlci5faWNvbiksXG5cdFx0ICAgIGxhdGxuZyA9IG1hcmtlci5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhpY29uUG9zKTtcblxuXHRcdC8vIHVwZGF0ZSBzaGFkb3cgcG9zaXRpb25cblx0XHRpZiAoc2hhZG93KSB7XG5cdFx0XHRzZXRQb3NpdGlvbihzaGFkb3csIGljb25Qb3MpO1xuXHRcdH1cblxuXHRcdG1hcmtlci5fbGF0bG5nID0gbGF0bG5nO1xuXHRcdGUubGF0bG5nID0gbGF0bG5nO1xuXHRcdGUub2xkTGF0TG5nID0gdGhpcy5fb2xkTGF0TG5nO1xuXG5cdFx0Ly8gQGV2ZW50IGRyYWc6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgcmVwZWF0ZWRseSB3aGlsZSB0aGUgdXNlciBkcmFncyB0aGUgbWFya2VyLlxuXHRcdG1hcmtlclxuXHRcdCAgICAuZmlyZSgnbW92ZScsIGUpXG5cdFx0ICAgIC5maXJlKCdkcmFnJywgZSk7XG5cdH0sXG5cblx0X29uRHJhZ0VuZDogZnVuY3Rpb24gKGUpIHtcblx0XHQvLyBAZXZlbnQgZHJhZ2VuZDogRHJhZ0VuZEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBzdG9wcyBkcmFnZ2luZyB0aGUgbWFya2VyLlxuXG5cdFx0IGNhbmNlbEFuaW1GcmFtZSh0aGlzLl9wYW5SZXF1ZXN0KTtcblxuXHRcdC8vIEBldmVudCBtb3ZlZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBzdG9wcyBtb3ZpbmcgKGJlY2F1c2Ugb2YgZHJhZ2dpbmcpLlxuXHRcdGRlbGV0ZSB0aGlzLl9vbGRMYXRMbmc7XG5cdFx0dGhpcy5fbWFya2VyXG5cdFx0ICAgIC5maXJlKCdtb3ZlZW5kJylcblx0XHQgICAgLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblx0fVxufSk7XG5cbi8qXHJcbiAqIEBjbGFzcyBNYXJrZXJcclxuICogQGluaGVyaXRzIEludGVyYWN0aXZlIGxheWVyXHJcbiAqIEBha2EgTC5NYXJrZXJcclxuICogTC5NYXJrZXIgaXMgdXNlZCB0byBkaXNwbGF5IGNsaWNrYWJsZS9kcmFnZ2FibGUgaWNvbnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLm1hcmtlcihbNTAuNSwgMzAuNV0pLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbnZhciBNYXJrZXIgPSBMYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgTWFya2VyIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGljb246IEljb24gPSAqXHJcblx0XHQvLyBJY29uIGluc3RhbmNlIHRvIHVzZSBmb3IgcmVuZGVyaW5nIHRoZSBtYXJrZXIuXHJcblx0XHQvLyBTZWUgW0ljb24gZG9jdW1lbnRhdGlvbl0oI0wuSWNvbikgZm9yIGRldGFpbHMgb24gaG93IHRvIGN1c3RvbWl6ZSB0aGUgbWFya2VyIGljb24uXHJcblx0XHQvLyBJZiBub3Qgc3BlY2lmaWVkLCBhIGNvbW1vbiBpbnN0YW5jZSBvZiBgTC5JY29uLkRlZmF1bHRgIGlzIHVzZWQuXHJcblx0XHRpY29uOiBuZXcgSWNvbkRlZmF1bHQoKSxcclxuXHJcblx0XHQvLyBPcHRpb24gaW5oZXJpdGVkIGZyb20gXCJJbnRlcmFjdGl2ZSBsYXllclwiIGFic3RyYWN0IGNsYXNzXHJcblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtleWJvYXJkOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGNhbiBiZSB0YWJiZWQgdG8gd2l0aCBhIGtleWJvYXJkIGFuZCBjbGlja2VkIGJ5IHByZXNzaW5nIGVudGVyLlxyXG5cdFx0a2V5Ym9hcmQ6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0aXRsZTogU3RyaW5nID0gJydcclxuXHRcdC8vIFRleHQgZm9yIHRoZSBicm93c2VyIHRvb2x0aXAgdGhhdCBhcHBlYXIgb24gbWFya2VyIGhvdmVyIChubyB0b29sdGlwIGJ5IGRlZmF1bHQpLlxyXG5cdFx0dGl0bGU6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYWx0OiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVGV4dCBmb3IgdGhlIGBhbHRgIGF0dHJpYnV0ZSBvZiB0aGUgaWNvbiBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6SW5kZXhPZmZzZXQ6IE51bWJlciA9IDBcclxuXHRcdC8vIEJ5IGRlZmF1bHQsIG1hcmtlciBpbWFnZXMgekluZGV4IGlzIHNldCBhdXRvbWF0aWNhbGx5IGJhc2VkIG9uIGl0cyBsYXRpdHVkZS4gVXNlIHRoaXMgb3B0aW9uIGlmIHlvdSB3YW50IHRvIHB1dCB0aGUgbWFya2VyIG9uIHRvcCBvZiBhbGwgb3RoZXJzIChvciBiZWxvdyksIHNwZWNpZnlpbmcgYSBoaWdoIHZhbHVlIGxpa2UgYDEwMDBgIChvciBoaWdoIG5lZ2F0aXZlIHZhbHVlLCByZXNwZWN0aXZlbHkpLlxyXG5cdFx0ekluZGV4T2Zmc2V0OiAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgbWFya2VyLlxyXG5cdFx0b3BhY2l0eTogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJpc2VPbkhvdmVyOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIG1hcmtlciB3aWxsIGdldCBvbiB0b3Agb2Ygb3RoZXJzIHdoZW4geW91IGhvdmVyIHRoZSBtb3VzZSBvdmVyIGl0LlxyXG5cdFx0cmlzZU9uSG92ZXI6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcmlzZU9mZnNldDogTnVtYmVyID0gMjUwXHJcblx0XHQvLyBUaGUgei1pbmRleCBvZmZzZXQgdXNlZCBmb3IgdGhlIGByaXNlT25Ib3ZlcmAgZmVhdHVyZS5cclxuXHRcdHJpc2VPZmZzZXQ6IDI1MCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICdtY
14775XJrZXJQYW5lJ1xyXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgbWFya2VycyBpY29uIHdpbGwgYmUgYWRkZWQuXHJcblx0XHRwYW5lOiAnbWFya2VyUGFuZScsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAnc2hhZG93UGFuZSdcclxuXHRcdC8vIGBNYXAgcGFuZWAgd2hlcmUgdGhlIG1hcmtlcnMgc2hhZG93IHdpbGwgYmUgYWRkZWQuXHJcblx0XHRzaGFkb3dQYW5lOiAnc2hhZG93UGFuZScsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBidWJibGluZ01vdXNlRXZlbnRzOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIFdoZW4gYHRydWVgLCBhIG1vdXNlIGV2ZW50IG9uIHRoaXMgbWFya2VyIHdpbGwgdHJpZ2dlciB0aGUgc2FtZSBldmVudCBvbiB0aGUgbWFwXHJcblx0XHQvLyAodW5sZXNzIFtgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb25gXSgjZG9tZXZlbnQtc3RvcHByb3BhZ2F0aW9uKSBpcyB1c2VkKS5cclxuXHRcdGJ1YmJsaW5nTW91c2VFdmVudHM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIERyYWdnYWJsZSBtYXJrZXIgb3B0aW9uc1xyXG5cdFx0Ly8gQG9wdGlvbiBkcmFnZ2FibGU6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGlzIGRyYWdnYWJsZSB3aXRoIG1vdXNlL3RvdWNoIG9yIG5vdC5cclxuXHRcdGRyYWdnYWJsZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIFdoZXRoZXIgdG8gcGFuIHRoZSBtYXAgd2hlbiBkcmFnZ2luZyB0aGlzIG1hcmtlciBuZWFyIGl0cyBlZGdlIG9yIG5vdC5cclxuXHRcdGF1dG9QYW46IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmc6IFBvaW50ID0gUG9pbnQoNTAsIDUwKVxyXG5cdFx0Ly8gRGlzdGFuY2UgKGluIHBpeGVscyB0byB0aGUgbGVmdC9yaWdodCBhbmQgdG8gdGhlIHRvcC9ib3R0b20pIG9mIHRoZVxyXG5cdFx0Ly8gbWFwIGVkZ2UgdG8gc3RhcnQgcGFubmluZyB0aGUgbWFwLlxyXG5cdFx0YXV0b1BhblBhZGRpbmc6IFs1MCwgNTBdLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblNwZWVkOiBOdW1iZXIgPSAxMFxyXG5cdFx0Ly8gTnVtYmVyIG9mIHBpeGVscyB0aGUgbWFwIHNob3VsZCBwYW4gYnkuXHJcblx0XHRhdXRvUGFuU3BlZWQ6IDEwXHJcblx0fSxcclxuXHJcblx0LyogQHNlY3Rpb25cclxuXHQgKlxyXG5cdCAqIEluIGFkZGl0aW9uIHRvIFtzaGFyZWQgbGF5ZXIgbWV0aG9kc10oI0xheWVyKSBsaWtlIGBhZGRUbygpYCBhbmQgYHJlbW92ZSgpYCBhbmQgW3BvcHVwIG1ldGhvZHNdKCNQb3B1cCkgbGlrZSBiaW5kUG9wdXAoKSB5b3UgY2FuIGFsc28gdXNlIHRoZSBmb2xsb3dpbmcgbWV0aG9kczpcclxuXHQgKi9cclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucykge1xyXG5cdFx0c2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHRcdHRoaXMuX2xhdGxuZyA9IHRvTGF0TG5nKGxhdGxuZyk7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuX3pvb21BbmltYXRlZCA9IHRoaXMuX3pvb21BbmltYXRlZCAmJiBtYXAub3B0aW9ucy5tYXJrZXJab29tQW5pbWF0aW9uO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0bWFwLm9uKCd6b29tYW5pbScsIHRoaXMuX2FuaW1hdGVab29tLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pbml0SWNvbigpO1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0aWYgKHRoaXMuZHJhZ2dpbmcgJiYgdGhpcy5kcmFnZ2luZy5lbmFibGVkKCkpIHtcclxuXHRcdFx0dGhpcy5vcHRpb25zLmRyYWdnYWJsZSA9IHRydWU7XHJcblx0XHRcdHRoaXMuZHJhZ2dpbmcucmVtb3ZlSG9va3MoKTtcclxuXHRcdH1cclxuXHRcdGRlbGV0ZSB0aGlzLmRyYWdnaW5nO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0bWFwLm9mZignem9vbWFuaW0nLCB0aGlzLl9hbmltYXRlWm9vbSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fcmVtb3ZlSWNvbigpO1xyXG5cdFx0dGhpcy5fcmVtb3ZlU2hhZG93KCk7XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHR6b29tOiB0aGlzLnVwZGF0ZSxcclxuXHRcdFx0dmlld3Jlc2V0OiB0aGlzLnVwZGF0ZVxyXG5cdFx0fTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExhdExuZzogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBnZW9ncmFwaGljYWwgcG9zaXRpb24gb2YgdGhlIG1hcmtlci5cclxuXHRnZXRMYXRMbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9sYXRsbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgbWFya2VyIHBvc2l0aW9uIHRvIHRoZSBnaXZlbiBwb2ludC5cclxuXHRzZXRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBvbGRMYXRMbmcgPSB0aGlzLl9sYXRsbmc7XHJcblx0XHR0aGlzLl9sYXRsbmcgPSB0b0xhdExuZyhsYXRsbmcpO1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBpcyBtb3ZlZCB2aWEgW2BzZXRMYXRMbmdgXSgjbWFya2VyLXNldGxhdGxuZykgb3IgYnkgW2RyYWdnaW5nXSgjbWFya2VyLWRyYWdnaW5nKS4gT2xkIGFuZCBuZXcgY29vcmRpbmF0ZXMgYXJlIGluY2x1ZGVkIGluIGV2ZW50IGFyZ3VtZW50cyBhcyBgb2xkTGF0TG5nYCwgYGxhdGxuZ2AuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlJywge29sZExhdExuZzogb2xkTGF0TG5nLCBsYXRsbmc6IHRoaXMuX2xhdGxuZ30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4T2Zmc2V0KG9mZnNldDogTnVtYmVyKTogdGh
14775pc1xyXG5cdC8vIENoYW5nZXMgdGhlIFt6SW5kZXggb2Zmc2V0XSgjbWFya2VyLXppbmRleG9mZnNldCkgb2YgdGhlIG1hcmtlci5cclxuXHRzZXRaSW5kZXhPZmZzZXQ6IGZ1bmN0aW9uIChvZmZzZXQpIHtcclxuXHRcdHRoaXMub3B0aW9ucy56SW5kZXhPZmZzZXQgPSBvZmZzZXQ7XHJcblx0XHRyZXR1cm4gdGhpcy51cGRhdGUoKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEljb246IEljb25cclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IGljb24gdXNlZCBieSB0aGUgbWFya2VyXHJcblx0Z2V0SWNvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5pY29uO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0SWNvbihpY29uOiBJY29uKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG1hcmtlciBpY29uLlxyXG5cdHNldEljb246IGZ1bmN0aW9uIChpY29uKSB7XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLmljb24gPSBpY29uO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5faW5pdEljb24oKTtcclxuXHRcdFx0dGhpcy51cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5iaW5kUG9wdXAodGhpcy5fcG9wdXAsIHRoaXMuX3BvcHVwLm9wdGlvbnMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9pY29uO1xyXG5cdH0sXHJcblxyXG5cdHVwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICh0aGlzLl9pY29uICYmIHRoaXMuX21hcCkge1xyXG5cdFx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpLnJvdW5kKCk7XHJcblx0XHRcdHRoaXMuX3NldFBvcyhwb3MpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9pbml0SWNvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnMsXHJcblx0XHQgICAgY2xhc3NUb0FkZCA9ICdsZWFmbGV0LXpvb20tJyArICh0aGlzLl96b29tQW5pbWF0ZWQgPyAnYW5pbWF0ZWQnIDogJ2hpZGUnKTtcclxuXHJcblx0XHR2YXIgaWNvbiA9IG9wdGlvbnMuaWNvbi5jcmVhdGVJY29uKHRoaXMuX2ljb24pLFxyXG5cdFx0ICAgIGFkZEljb24gPSBmYWxzZTtcclxuXHJcblx0XHQvLyBpZiB3ZSdyZSBub3QgcmV1c2luZyB0aGUgaWNvbiwgcmVtb3ZlIHRoZSBvbGQgb25lIGFuZCBpbml0IG5ldyBvbmVcclxuXHRcdGlmIChpY29uICE9PSB0aGlzLl9pY29uKSB7XHJcblx0XHRcdGlmICh0aGlzLl9pY29uKSB7XHJcblx0XHRcdFx0dGhpcy5fcmVtb3ZlSWNvbigpO1xyXG5cdFx0XHR9XHJcblx0XHRcdGFkZEljb24gPSB0cnVlO1xyXG5cclxuXHRcdFx0aWYgKG9wdGlvbnMudGl0bGUpIHtcclxuXHRcdFx0XHRpY29uLnRpdGxlID0gb3B0aW9ucy50aXRsZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKGljb24udGFnTmFtZSA9PT0gJ0lNRycpIHtcclxuXHRcdFx0XHRpY29uLmFsdCA9IG9wdGlvbnMuYWx0IHx8ICcnO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0YWRkQ2xhc3MoaWNvbiwgY2xhc3NUb0FkZCk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMua2V5Ym9hcmQpIHtcclxuXHRcdFx0aWNvbi50YWJJbmRleCA9ICcwJztcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pY29uID0gaWNvbjtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5yaXNlT25Ib3Zlcikge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRtb3VzZW92ZXI6IHRoaXMuX2JyaW5nVG9Gcm9udCxcclxuXHRcdFx0XHRtb3VzZW91dDogdGhpcy5fcmVzZXRaSW5kZXhcclxuXHRcdFx0fSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG5ld1NoYWRvdyA9IG9wdGlvbnMuaWNvbi5jcmVhdGVTaGFkb3codGhpcy5fc2hhZG93KSxcclxuXHRcdCAgICBhZGRTaGFkb3cgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAobmV3U2hhZG93ICE9PSB0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0dGhpcy5fcmVtb3ZlU2hhZG93KCk7XHJcblx0XHRcdGFkZFNoYWRvdyA9IHRydWU7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG5ld1NoYWRvdykge1xyXG5cdFx0XHRhZGRDbGFzcyhuZXdTaGFkb3csIGNsYXNzVG9BZGQpO1xyXG5cdFx0XHRuZXdTaGFkb3cuYWx0ID0gJyc7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9zaGFkb3cgPSBuZXdTaGFkb3c7XHJcblxyXG5cclxuXHRcdGlmIChvcHRpb25zLm9wYWNpdHkgPCAxKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZU9wYWNpdHkoKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0aWYgKGFkZEljb24pIHtcclxuXHRcdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5faWNvbik7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9pbml0SW50ZXJhY3Rpb24oKTtcclxuXHRcdGlmIChuZXdTaGFkb3cgJiYgYWRkU2hhZG93KSB7XHJcblx0XHRcdHRoaXMuZ2V0UGFuZShvcHRpb25zLnNoYWRvd1BhbmUpLmFwcGVuZENoaWxkKHRoaXMuX3NoYWRvdyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3JlbW92ZUljb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLm9wdGlvbnMucmlzZU9uSG92ZXIpIHtcclxuXHRcdFx0dGhpcy5vZmYoe1xyXG5cdFx0XHRcdG1vdXNlb3ZlcjogdGhpcy5fYnJpbmdUb0Zyb250LFxyXG5cdFx0XHRcdG1vdXNlb3V0OiB0aGlzLl9yZXNldFpJbmRleFxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZW1vdmUodGhpcy5faWNvbik7XHJcblx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ljb24pO1xyXG5cclxuXHRcdHRoaXMuX2ljb24gPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9yZW1vdmVTaGFkb3c6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0cmVtb3ZlKHRoaXMuX3NoYWRvdyk7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9zaGFkb3cgPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9zZXRQb3M6IGZ1bmN0aW9uIChwb3MpIHtcclxuXHJcblx0XHRpZiAodGhpcy5faWNvbikge1xyXG5cdFx0XHRzZXRQb3NpdGlvbih0aGlzLl9pY29uLCBwb3MpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0c2V0UG9zaXRpb24odGhpcy5fc2hhZG93LCBwb3MpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3pJbmRleCA9IHBvcy55
14775ICsgdGhpcy5vcHRpb25zLnpJbmRleE9mZnNldDtcclxuXHJcblx0XHR0aGlzLl9yZXNldFpJbmRleCgpO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVaSW5kZXg6IGZ1bmN0aW9uIChvZmZzZXQpIHtcclxuXHRcdGlmICh0aGlzLl9pY29uKSB7XHJcblx0XHRcdHRoaXMuX2ljb24uc3R5bGUuekluZGV4ID0gdGhpcy5fekluZGV4ICsgb2Zmc2V0O1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKG9wdCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5fbGF0TG5nVG9OZXdMYXllclBvaW50KHRoaXMuX2xhdGxuZywgb3B0Lnpvb20sIG9wdC5jZW50ZXIpLnJvdW5kKCk7XHJcblxyXG5cdFx0dGhpcy5fc2V0UG9zKHBvcyk7XHJcblx0fSxcclxuXHJcblx0X2luaXRJbnRlcmFjdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGFkZENsYXNzKHRoaXMuX2ljb24sICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XHJcblxyXG5cdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pY29uKTtcclxuXHJcblx0XHRpZiAoTWFya2VyRHJhZykge1xyXG5cdFx0XHR2YXIgZHJhZ2dhYmxlID0gdGhpcy5vcHRpb25zLmRyYWdnYWJsZTtcclxuXHRcdFx0aWYgKHRoaXMuZHJhZ2dpbmcpIHtcclxuXHRcdFx0XHRkcmFnZ2FibGUgPSB0aGlzLmRyYWdnaW5nLmVuYWJsZWQoKTtcclxuXHRcdFx0XHR0aGlzLmRyYWdnaW5nLmRpc2FibGUoKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5kcmFnZ2luZyA9IG5ldyBNYXJrZXJEcmFnKHRoaXMpO1xyXG5cclxuXHRcdFx0aWYgKGRyYWdnYWJsZSkge1xyXG5cdFx0XHRcdHRoaXMuZHJhZ2dpbmcuZW5hYmxlKCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGh
14775pc1xyXG5cdC8vIENoYW5nZXMgdGhlIG9wYWNpdHkgb2YgdGhlIG1hcmtlci5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBvcGFjaXR5ID0gdGhpcy5vcHRpb25zLm9wYWNpdHk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2ljb24pIHtcclxuXHRcdFx0c2V0T3BhY2l0eSh0aGlzLl9pY29uLCBvcGFjaXR5KTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdHNldE9wYWNpdHkodGhpcy5fc2hhZG93LCBvcGFjaXR5KTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl91cGRhdGVaSW5kZXgodGhpcy5vcHRpb25zLnJpc2VPZmZzZXQpO1xyXG5cdH0sXHJcblxyXG5cdF9yZXNldFpJbmRleDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KDApO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRQb3B1cEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5pY29uLm9wdGlvbnMucG9wdXBBbmNob3I7XHJcblx0fSxcclxuXHJcblx0X2dldFRvb2x0aXBBbmNob3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuaWNvbi5vcHRpb25zLnRvb2x0aXBBbmNob3I7XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBmYWN0b3J5IEwubWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPyA6IE1hcmtlciBvcHRpb25zKVxyXG5cclxuLy8gQGZhY3RvcnkgTC5tYXJrZXIobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/IDogTWFya2VyIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIE1hcmtlciBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQgYW5kIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuXHJcbmZ1bmN0aW9uIG1hcmtlcihsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IE1hcmtlcihsYXRsbmcsIG9wdGlvbnMpO1xyXG59XG5cbi8qXG4gKiBAY2xhc3MgUGF0aFxuICogQGFrYSBMLlBhdGhcbiAqIEBpbmhlcml0cyBJbnRlcmFjdGl2ZSBsYXllclxuICpcbiAqIEFuIGFic3RyYWN0IGNsYXNzIHRoYXQgY29udGFpbnMgb3B0aW9ucyBhbmQgY29uc3RhbnRzIHNoYXJlZCBiZXR3ZWVuIHZlY3RvclxuICogb3ZlcmxheXMgKFBvbHlnb24sIFBvbHlsaW5lLCBDaXJjbGUpLiBEbyBub3QgdXNlIGl0IGRpcmVjdGx5LiBFeHRlbmRzIGBMYXllcmAuXG4gKi9cblxudmFyIFBhdGggPSBMYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUGF0aCBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHQvLyBAb3B0aW9uIHN0cm9rZTogQm9vbGVhbiA9IHRydWVcblx0XHQvLyBXaGV0aGVyIHRvIGRyYXcgc3Ryb2tlIGFsb25nIHRoZSBwYXRoLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGJvcmRlcnMgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRzdHJva2U6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIGNvbG9yOiBTdHJpbmcgPSAnIzMzODhmZidcblx0XHQvLyBTdHJva2UgY29sb3Jcblx0XHRjb2xvcjogJyMzMzg4ZmYnLFxuXG5cdFx0Ly8gQG9wdGlvbiB3ZWlnaHQ6IE51bWJlciA9IDNcblx0XHQvLyBTdHJva2Ugd2lkdGggaW4gcGl4ZWxzXG5cdFx0d2VpZ2h0OiAzLFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBTdHJva2Ugb3BhY2l0eVxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVDYXA6IFN0cmluZz0gJ3JvdW5kJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbc2hhcGUgdG8gYmUgdXNlZCBhdCB0aGUgZW5kXSho
14775dHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lY2FwKSBvZiB0aGUgc3Ryb2tlLlxuXHRcdGxpbmVDYXA6ICdyb3VuZCcsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVKb2luOiBTdHJpbmcgPSAncm91bmQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtzaGFwZSB0byBiZSB1c2VkIGF0IHRoZSBjb3JuZXJzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lam9pbikgb2YgdGhlIHN0cm9rZS5cblx0XHRsaW5lSm9pbjogJ3JvdW5kJyxcblxuXHRcdC8vIEBvcHRpb24gZGFzaEFycmF5OiBTdHJpbmcgPSBudWxsXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIHRoZSBzdHJva2UgW2Rhc2ggcGF0dGVybl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaGFycmF5KS4gRG9lc24ndCB3b3JrIG9uIGBDYW52YXNgLXBvd2VyZWQgbGF5ZXJzIGluIFtzb21lIG9sZCBicm93c2Vyc10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc1JlbmRlcmluZ0NvbnRleHQyRC9zZXRMaW5lRGFzaCNCcm93c2VyX2NvbXBhdGliaWxpdHkpLlxuXHRcdGRhc2hBcnJheTogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZGFzaE9mZnNldDogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyB0aGUgW2Rpc3RhbmNlIGludG8gdGhlIGRhc2ggcGF0dGVybiB0byBzdGFydCB0aGUgZGFzaF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaG9mZnNldCkuIERvZXNuJ3Qgd29yayBvbiBgQ2FudmFzYC1wb3dlcmVkIGxheWVycyBpbiBbc29tZSBvbGQgYnJvd3NlcnNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNSZW5kZXJpbmdDb250ZXh0MkQvc2V0TGluZURhc2gjQnJvd3Nlcl9jb21wYXRpYmlsaXR5KS5cblx0XHRkYXNoT2Zmc2V0OiBudWxsLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsOiBCb29sZWFuID0gZGVwZW5kc1xuXHRcdC8vIFdoZXRoZXIgdG8gZmlsbCB0aGUgcGF0aCB3aXRoIGNvbG9yLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGZpbGxpbmcgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRmaWxsOiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gZmlsbENvbG9yOiBTdHJpbmcgPSAqXG5cdFx0Ly8gRmlsbCBjb2xvci4gRGVmYXVsdHMgdG8gdGhlIHZhbHVlIG9mIHRoZSBbYGNvbG9yYF0oI3BhdGgtY29sb3IpIG9wdGlvblxuXHRcdGZpbGxDb2xvcjogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZmlsbE9wYWNpdHk6IE51bWJlciA9IDAuMlxuXHRcdC8vIEZpbGwgb3BhY2l0eS5cblx0XHRmaWxsT3BhY2l0eTogMC4yLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsUnVsZTogU3RyaW5nID0gJ2V2ZW5vZGQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtob3cgdGhlIGluc2lkZSBvZiBhIHNoYXBlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL2ZpbGwtcnVsZSkgaXMgZGV0ZXJtaW5lZC5cblx0XHRmaWxsUnVsZTogJ2V2ZW5vZGQnLFxuXG5cdFx0Ly8gY2xhc3NOYW1lOiAnJyxcblxuXHRcdC8vIE9wdGlvbiBpbmhlcml0ZWQgZnJvbSBcIkludGVyYWN0aXZlIGxheWVyXCIgYWJzdHJhY3QgY2xhc3Ncblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gYnViYmxpbmdNb3VzZUV2ZW50czogQm9vbGVhbiA9IHRydWVcblx0XHQvLyBXaGVuIGB0cnVlYCwgYSBtb3VzZSBldmVudCBvbiB0aGlzIHBhdGggd2lsbCB0cmlnZ2VyIHRoZSBzYW1lIGV2ZW50IG9uIHRoZSBtYXBcblx0XHQvLyAodW5sZXNzIFtgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb25gXSgjZG9tZXZlbnQtc3RvcHByb3BhZ2F0aW9uKSBpcyB1c2VkKS5cblx0XHRidWJibGluZ01vdXNlRXZlbnRzOiB0cnVlXG5cdH0sXG5cblx0YmVmb3JlQWRkOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0Ly8gUmVuZGVyZXIgaXMgc2V0IGhlcmUgYmVjYXVzZSB3ZSBuZWVkIHRvIGNhbGwgcmVuZGVyZXIuZ2V0RXZlbnRzXG5cdFx0Ly8gYmVmb3JlIHRoaXMuZ2V0RXZlbnRzLlxuXHRcdHRoaXMuX3JlbmRlcmVyID0gbWFwLmdldFJlbmRlcmVyKHRoaXMpO1xuXHR9LFxuXG5cdG9uQWRkOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX2luaXRQYXRoKHRoaXMpO1xuXHRcdHRoaXMuX3Jlc2V0KCk7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX2FkZFBhdGgodGhpcyk7XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fcmVtb3ZlUGF0aCh0aGlzKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHJlZHJhdygpOiB0aGlzXG5cdC8vIFJlZHJhd3MgdGhlIGxheWVyLiBTb21ldGltZXMgdXNlZnVsIGFmdGVyIHlvdSBjaGFuZ2VkIHRoZSBjb29yZGluYXRlcyB0aGF0IHRoZSBwYXRoIHVzZXMuXG5cdHJlZHJhdzogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXApIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVQYXRoKHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldFN0eWxlKHN0eWxlOiBQYXRoIG9wdGlvbnMpOiB0aGlzXG5cdC8vIENoYW5nZXMgdGhlIGFwcGVhcmFuY2Ugb2YgYSBQYXRoIGJhc2VkIG9uIHRoZSBvcHRpb25zIGluIHRoZSBgUGF0aCBvcHRpb25zYCBvYmplY3QuXG5cdHNldFN0eWxlOiBmdW5jdGlvbiAoc3R5bGUpIHtcblx0XHRzZXRPcHRpb25zKHRoaXMsIHN0eWxlKTtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVTdHlsZSh0aGlzKTtcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMuc3Ryb2tlICYmIHN0eWxlICYmIHN0eWxlLmhhc093blByb3BlcnR5KCd3ZWlnaHQnKSkge1xuXHRcdFx0XHR0aGlzLl91cGRhdGVCb3VuZHMoKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSB0b3Agb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl9icmluZ1RvRnJvbnQodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybi
14775B0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2soKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSBib3R0b20gb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9yZW5kZXJlcikge1xuXHRcdFx0dGhpcy5fcmVuZGVyZXIuX2JyaW5nVG9CYWNrKHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3BhdGg7XG5cdH0sXG5cblx0X3Jlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gZGVmaW5lZCBpbiBjaGlsZCBjbGFzc2VzXG5cdFx0dGhpcy5fcHJvamVjdCgpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF9jbGlja1RvbGVyYW5jZTogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHVzZWQgd2hlbiBkb2luZyBoaXQgZGV0ZWN0aW9uIGZvciBDYW52YXMgbGF5ZXJzXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuc3Ryb2tlID8gdGhpcy5vcHRpb25zLndlaWdodCAvIDIgOiAwKSArIHRoaXMuX3JlbmRlcmVyLm9wdGlvbnMudG9sZXJhbmNlO1xuXHR9XG59KTtcblxuLypcbiAqIEBjbGFzcyBDaXJjbGVNYXJrZXJcbiAqIEBha2EgTC5DaXJjbGVNYXJrZXJcbiAqIEBpbmhlcml0cyBQYXRoXG4gKlxuICogQSBjaXJjbGUgb2YgYSBmaXhlZCBzaXplIHdpdGggcmFkaXVzIHNwZWNpZmllZCBpbiBwaXhlbHMuIEV4dGVuZHMgYFBhdGhgLlxuICovXG5cbnZhciBDaXJjbGVNYXJrZXIgPSBQYXRoLmV4dGVuZCh7XG5cblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBDaXJjbGVNYXJrZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0ZmlsbDogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXIgPSAxMFxuXHRcdC8vIFJhZGl1cyBvZiB0aGUgY2lyY2xlIG1hcmtlciwgaW4gcGl4ZWxzXG5cdFx0cmFkaXVzOiAxMFxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcblx0XHRzZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuXHRcdHRoaXMuX2xhdGxuZyA9IHRvTGF0TG5nKGxhdGxuZyk7XG5cdFx0dGhpcy5fcmFkaXVzID0gdGhpcy5vcHRpb25zLnJhZGl1cztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldExhdExuZyhsYXRMbmc6IExhdExuZyk6IHRoaXNcblx0Ly8gU2V0cyB0aGUgcG9zaXRpb24gb2YgYSBjaXJjbGUgbWFya2VyIHRvIGEgbmV3IGxvY2F0aW9uLlxuXHRzZXRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcblx0XHR2YXIgb2xkTGF0TG5nID0gdGhpcy5fbGF0bG5nO1xuXHRcdHRoaXMuX2xhdGxuZyA9IHRvTGF0TG5nKGxhdGxuZyk7XG5cdFx0dGhpcy5yZWRyYXcoKTtcblxuXHRcdC8vIEBldmVudCBtb3ZlOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBpcyBtb3ZlZCB2aWEgW2BzZXRMYXRMbmdgXSgjY2lyY2xlbWFya2VyLXNldGxhdGxuZykuIE9sZCBhbmQgbmV3IGNvb3JkaW5hdGVzIGFyZSBpbmNsdWRlZCBpbiBldmVudCBhcmd1bWVudHMgYXMgYG9sZExhdExuZ2AsIGBsYXRsbmdgLlxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmUnLCB7b2xkTGF0TG5nOiBvbGRMYXRMbmcsIGxhdGxuZzogdGhpcy5fbGF0bG5nfSk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmcoKTogTGF0TG5nXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgZ2VvZ3JhcGhpY2FsIHBvc2l0aW9uIG9mIHRoZSBjaXJjbGUgbWFya2VyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sYXRsbmc7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZSBtYXJrZXIuIFVuaXRzIGFyZSBpbiBwaXhlbHMuXG5cdHNldFJhZGl1czogZnVuY3Rpb24gKHJhZGl1cykge1xuXHRcdHRoaXMub3B0aW9ucy5yYWRpdXMgPSB0aGlzLl9yYWRpdXMgPSByYWRpdXM7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRSYWRpdXMoKTogTnVtYmVyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgcmFkaXVzIG9mIHRoZSBjaXJjbGVcblx0Z2V0UmFkaXVzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cztcblx0fSxcblxuXHRzZXRTdHlsZSA6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdFx0dmFyIHJhZGl1cyA9IG9wdGlvbnMgJiYgb3B0aW9ucy5yYWRpdXMgfHwgdGhpcy5fcmFkaXVzO1xuXHRcdFBhdGgucHJvdG90eXBlLnNldFN0eWxlLmNhbGwodGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5zZXRSYWRpdXMocmFkaXVzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfcHJvamVjdDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3BvaW50ID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpO1xuXHRcdHRoaXMuX3VwZGF0ZUJvdW5kcygpO1xuXHR9LFxuXG5cdF91cGRhdGVCb3VuZHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgciA9IHRoaXMuX3JhZGl1cyxcblx0XHQgICAgcjIgPSB0aGlzLl9yYWRpdXNZIHx8IHIsXG5cdFx0ICAgIHcgPSB0aGlzLl9jbGlja1RvbGVyYW5jZSgpLFxuXHRcdCAgICBwID0gW3IgKyB3LCByMiArIHddO1xuXHRcdHRoaXMuX3B4Qm91bmRzID0gbmV3IEJvdW5kcyh0aGlzLl9wb2ludC5zdWJ0cmFjdChwKSwgdGhpcy5fcG9pbnQuYWRkKHApKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fdXBkYXRlUGF0aCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVDaXJjbGUodGhpcyk7XG5cdH0sXG5cblx0X2VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cyAmJiAhdGhpcy5fcmVuZGVyZXIuX2JvdW5kcy5pbnRlcnNlY3RzKHRoaXMuX3B4Qm91bmRzKTtcblx0fSxcblxuXHQvLyBOZWVkZWQgYnkgdGhlIGBDYW52YXNgIHJlbmRlcmVyIGZvciBpbnRlcmFjdGl2aXR5XG5cdF9jb250YWluc1BvaW50OiBmdW5jdGlvbiAocCkge1xuXHRcdHJldHVybi
14775BwLmRpc3RhbmNlVG8odGhpcy5fcG9pbnQpIDw9IHRoaXMuX3JhZGl1cyArIHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwuY2lyY2xlTWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogQ2lyY2xlTWFya2VyIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBjaXJjbGUgbWFya2VyIG9iamVjdCBnaXZlbiBhIGdlb2dyYXBoaWNhbCBwb2ludCwgYW5kIGFuIG9wdGlvbmFsIG9wdGlvbnMgb2JqZWN0LlxuZnVuY3Rpb24gY2lyY2xlTWFya2VyKGxhdGxuZywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IENpcmNsZU1hcmtlcihsYXRsbmcsIG9wdGlvbnMpO1xufVxuXG4vKlxuICogQGNsYXNzIENpcmNsZVxuICogQGFrYSBMLkNpcmNsZVxuICogQGluaGVyaXRzIENpcmNsZU1hcmtlclxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgY2lyY2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBDaXJjbGVNYXJrZXJgLlxuICpcbiAqIEl0J3MgYW4gYXBwcm94aW1hdGlvbiBhbmQgc3RhcnRzIHRvIGRpdmVyZ2UgZnJvbSBhIHJlYWwgY2lyY2xlIGNsb3NlciB0byBwb2xlcyAoZHVlIHRvIHByb2plY3Rpb24gZGlzdG9ydGlvbikuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jaXJjbGUoWzUwLjUsIDMwLjVdLCB7cmFkaXVzOiAyMDB9KS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxudmFyIENpcmNsZSA9IENpcmNsZU1hcmtlci5leHRlbmQoe1xuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMsIGxlZ2FjeU9wdGlvbnMpIHtcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInKSB7XG5cdFx0XHQvLyBCYWNrd2FyZHMgY29tcGF0aWJpbGl0eSB3aXRoIDAuNy54IGZhY3RvcnkgKGxhdGxuZywgcmFkaXVzLCBvcHRpb25zPylcblx0XHRcdG9wdGlvbnMgPSBleHRlbmQoe30sIGxlZ2FjeU9wdGlvbnMsIHtyYWRpdXM6IG9wdGlvbnN9KTtcblx0XHR9XG5cdFx0c2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9sYXRsbmcgPSB0b0xhdExuZyhsYXRsbmcpO1xuXG5cdFx0aWYgKGlzTmFOKHRoaXMub3B0aW9ucy5yYWRpdXMpKSB7IHRocm93IG5ldyBFcnJvcignQ2lyY2xlIHJhZGl1cyBjYW5ub3QgYmUgTmFOJyk7IH1cblxuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBDaXJjbGUgb3B0aW9uc1xuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXI7IFJhZGl1cyBvZiB0aGUgY2lyY2xlLCBpbiBtZXRlcnMuXG5cdFx0dGhpcy5fbVJhZGl1cyA9IHRoaXMub3B0aW9ucy5yYWRpdXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZS4gVW5pdHMgYXJlIGluIG1ldGVycy5cblx0c2V0UmFkaXVzOiBmdW5jdGlvbiAocmFkaXVzKSB7XG5cdFx0dGhpcy5fbVJhZGl1cyA9IHJhZGl1cztcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFJhZGl1cygpOiBOdW1iZXJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCByYWRpdXMgb2YgYSBjaXJjbGUuIFVuaXRzIGFyZSBpbiBtZXRlcnMuXG5cdGdldFJhZGl1czogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tUmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xuXHQvLyBSZXR1cm5zIHRoZSBgTGF0TG5nQm91bmRzYCBvZiB0aGUgcGF0aC5cblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGhhbGYgPSBbdGhpcy5fcmFkaXVzLCB0aGlzLl9yYWRpdXNZIHx8IHRoaXMuX3JhZGl1c107XG5cblx0XHRyZXR1cm4gbmV3IExhdExuZ0JvdW5kcyhcblx0XHRcdHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQuc3VidHJhY3QoaGFsZikpLFxuXHRcdFx0dGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludC5hZGQoaGFsZikpKTtcblx0fSxcblxuXHRzZXRTdHlsZTogUGF0aC5wcm90b3R5cGUuc2V0U3R5bGUsXG5cblx0X3Byb2plY3Q6IGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciBsbmcgPSB0aGlzLl9sYXRsbmcubG5nLFxuXHRcdCAgICBsYXQgPSB0aGlzLl9sYXRsbmcubGF0LFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIGNycyA9IG1hcC5vcHRpb25zLmNycztcblxuXHRcdGlmIChjcnMuZGlzdGFuY2UgPT09IEVhcnRoLmRpc3RhbmNlKSB7XG5cdFx0XHR2YXIgZCA9IE1hdGguUEkgLyAxODAsXG5cdFx0XHQgICAgbGF0UiA9ICh0aGlzLl9tUmFkaXVzIC8gRWFydGguUikgLyBkLFxuXHRcdFx0ICAgIHRvcCA9IG1hcC5wcm9qZWN0KFtsYXQgKyBsYXRSLCBsbmddKSxcblx0XHRcdCAgICBib3R0b20gPSBtYXAucHJvamVjdChbbGF0IC0gbGF0UiwgbG5nXSksXG5cdFx0XHQgICAgcCA9IHRvcC5hZGQoYm90dG9tKS5kaXZpZGVCeSgyKSxcblx0XHRcdCAgICBsYXQyID0gbWFwLnVucHJvamVjdChwKS5sYXQsXG5cdFx0XHQgICAgbG5nUiA9IE1hdGguYWNvcygoTWF0aC5jb3MobGF0UiAqIGQpIC0gTWF0aC5zaW4obGF0ICogZCkgKiBNYXRoLnNpbihsYXQyICogZCkpIC9cblx0XHRcdCAgICAgICAgICAgIChNYXRoLmNvcyhsYXQgKiBkKSAqIE1hdGguY29zKGxhdDIgKiBkKSkpIC8gZDtcblxuXHRcdFx0aWYgKGlzTmFOKGxuZ1IpIHx8IGxuZ1IgPT09IDApIHtcblx0XHRcdFx0bG5nUiA9IGxhdFIgLyBNYXRoLmNvcyhNYXRoLlBJIC8gMTgwICogbGF0KTsgLy8gRmFsbGJhY2sgZm9yIGVkZ2UgY2FzZSwgIzI0MjVcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fcG9pbnQgPSBwLnN1YnRyYWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKTtcblx0XHRcdHRoaXMuX3JhZGl1cyA9IGlzTmFOKGxuZ1IpID8gMCA6IHAueCAtIG1hcC5wcm9qZWN0KFtsYXQyLCBsbmcgLSBsbmdSXSkueDtcblx0XHRcdHRoaXMuX3JhZGl1c1kgPSBwLnkgLSB0b3AueTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHR2YXIgbGF0bG5nMiA9IGNycy51bnByb2plY3QoY3JzLnByb2plY3QodGhpcy5fbGF0bG5nKS5zdWJ0cmFjdChbdGhpcy5fbVJhZGl1cywgMF0pKTtcblxuXHRcdFx0dGhpcy5fcG9pbnQgPSBtYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0XHR0aGlzLl9yYWRpdXMgPSB0aGlzLl9wb2ludC54IC0gbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcyKS54O1xuXHRcdH1cblxuXHRcdHRoaXMuX3VwZGF0ZUJvdW5kcygpO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5jaXJjbGUobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBDaXJjbGUgb3B0aW9ucylcbi8vIEluc3RhbnRpYXRlcyBhIGNpcmNsZSBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQsIGFuZCBhbiBvcHRpb25zIG9iamVjdFxuLy8gd2hpY2ggY29udGFpbnMgdGhlIGNpcmNsZSByYWRpdXMuXG4vLyBAYWx0ZXJuYXRpdmVcbi8vIEBmYWN0b3J5IEwuY2lyY2xlKGxhdGxuZzogTGF0TG5nLCByYWRpdXM6IE51bWJlciwgb3B0aW9ucz86IENpcmNsZSBvcHRpb25zKVxuLy8gT2Jzb2xldGUgd2F5IG9mIGluc3RhbnRpYXRpbmcgYSBjaXJjbGUsIGZvciBjb21wYXRpYmlsaXR5IHdpdGggMC43LnggY29kZS5cbi8vIERvIG5vdCB1c2UgaW4gbmV3IGFwcGxpY2F0aW9ucyBvciBwbHVnaW5zLlxuZnVuY3Rpb24gY2lyY2xlKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IENpcmNsZShsYXRsbmcsIG9wdGlvbnMsIGxlZ2FjeU9wdGlvbnMpO1xufVxuXG4vKlxuICogQGNsYXNzIFBvbHlsaW5lXG4gKiBAYWthIEwuUG9seWxpbmVcbiAqIEBpbmhlcml0cyBQYXRoXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyBwb2x5bGluZSBvdmVybGF5cyBvbiBhIG1hcC4gRXh0ZW5kcyBgUGF0aGAuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogLy8gY3JlYXRlIGEgcmVkIHBvbHlsaW5lIGZyb20gYW4gYXJyYXkgb2YgTGF0TG5nIHBvaW50c1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiBcdFs0NS41MSwgLTEyMi42OF0sXG4gKiBcdFszNy43NywgLTEyMi40M10sXG4gKiBcdFszNC4wNCwgLTExOC4yXVxuICogXTtcbiAqXG4gKiB2YXIgcG9seWxpbmUgPSBMLnBvbHlsaW5lKGxhdGxuZ3MsIHtjb2xvcjogJ3JlZCd9KS5hZGRUbyhtYXApO1xuICpcbiAqIC8vIHpvb20gdGhlIG1hcCB0byB0aGUgcG9seWxpbmVcbiAqIG1hcC5maXRCb3VuZHMocG9seWxpbmUuZ2V0Qm91bmRzKCkpO1xuICogYGBgXG4gKlxuICogWW91IGNhbiBhbHNvIHBhc3MgYSBtdWx0aS1kaW1lbnNpb25hbCBhcnJheSB0byByZXByZXNlbnQgYSBgTXVsdGlQb2x5bGluZWAgc2hhcGU6XG4gKlxuICogYGBganNcbiAqIC8vIGNyZWF0ZSBhIHJlZCBwb2x5bGluZSBmcm9tIGFuIGFycmF5IG9mIGFycmF5cyBvZiBMYXRMbmcgcG9pbnRzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqIFx0W1s0NS41MSwgLTEyMi42OF0sXG4gKiBcdCBbMzcuNzcsIC0xMjIuNDNdLFxuICogXHQgWzM0LjA0LCAtMTE4LjJdXSxcbiAqIFx0W1s0MC43OCwgLTczLjkxXSxcbiAqIFx0IFs0MS44MywgLTg3LjYyXSxcbiAqIFx0IFszMi43NiwgLTk2LjcyXV1cbiAqIF07XG4gKiBgYGBcbiAqL1xuXG5cbnZhciBQb2x5bGluZSA9IFBhdGguZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFBvbHlsaW5lIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gc21vb3RoRmFjdG9yOiBOdW1iZXIgPSAxLjBcblx0XHQvLyBIb3cgbXVjaCB0byBzaW1wbGlmeSB0aGUgcG9seWxpbmUgb24gZWFjaCB6b29tIGxldmVsLiBNb3JlIG1lYW5zXG5cdFx0Ly8gYmV0dGVyIHBlcmZvcm1hbmNlIGFuZCBzbW9vdGhlciBsb29rLCBhbmQgbGVzcyBtZWFucyBtb3JlIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uLlxuXHRcdHNtb290aEZhY3RvcjogMS4wLFxuXG5cdFx0Ly8gQG9wdGlvbiBub0NsaXA6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIERpc2FibGUgcG9seWxpbmUgY2xpcHBpbmcuXG5cdFx0bm9DbGlwOiBmYWxzZVxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdFx0c2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9zZXRMYXRMbmdzKGxhdGxuZ3MpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0TGF0TG5ncygpOiBMYXRMbmdbXVxuXHQvLyBSZXR1cm5zIGFuIGFycmF5IG9mIHRoZSBwb2ludHMgaW4gdGhlIHBhdGgsIG9yIG5lc3RlZCBhcnJheXMgb2YgcG9pbnRzIGluIGNhc2Ugb2YgbXVsdGktcG9seWxpbmUuXG5cdGdldExhdExuZ3M6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbGF0bG5ncztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldExhdExuZ3MobGF0bG5nczogTGF0TG5nW10pOiB0aGlzXG5cdC8vIFJlcGxhY2VzIGFsbCB0aGUgcG9pbnRzIGluIHRoZSBwb2x5bGluZSB3aXRoIHRoZSBnaXZlbiBhcnJheSBvZiBnZW9ncmFwaGljYWwgcG9pbnRzLlxuXHRzZXRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHRoaXMuX3NldExhdExuZ3MobGF0bG5ncyk7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBpc0VtcHR5KCk6IEJvb2xlYW5cblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIFBvbHlsaW5lIGhhcyBubyBMYXRMbmdzLlxuXHRpc0VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuICF0aGlzLl9sYXRsbmdzLmxlbmd0aDtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGNsb3Nlc3RMYXllclBvaW50KHA6IFBvaW50KTogUG9pbnRcblx0Ly8gUmV0dXJucyB0aGUgcG9pbnQgY2xvc2VzdCB0byBgcGAgb24gdGhlIFBvbHlsaW5lLlxuXHRjbG9zZXN0TGF5ZXJQb2ludDogZnVuY3Rpb24gKHApIHtcblx0XHR2YXIgbWluRGlzdGFuY2UgPSBJbmZpbml0eSxcblx0XHQgICAgbWluUG9pbnQgPSBudWxsLFxuXHRcdCAgICBjbG9zZXN0ID0gX3NxQ2xvc2Vzd
14775FBvaW50T25TZWdtZW50LFxuXHRcdCAgICBwMSwgcDI7XG5cblx0XHRmb3IgKHZhciBqID0gMCwgakxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaiA8IGpMZW47IGorKykge1xuXHRcdFx0dmFyIHBvaW50cyA9IHRoaXMuX3BhcnRzW2pdO1xuXG5cdFx0XHRmb3IgKHZhciBpID0gMSwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHAxID0gcG9pbnRzW2kgLSAxXTtcblx0XHRcdFx0cDIgPSBwb2ludHNbaV07XG5cblx0XHRcdFx0dmFyIHNxRGlzdCA9IGNsb3Nlc3QocCwgcDEsIHAyLCB0cnVlKTtcblxuXHRcdFx0XHRpZiAoc3FEaXN0IDwgbWluRGlzdGFuY2UpIHtcblx0XHRcdFx0XHRtaW5EaXN0YW5jZSA9IHNxRGlzdDtcblx0XHRcdFx0XHRtaW5Qb2ludCA9IGNsb3Nlc3QocCwgcDEsIHAyKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAobWluUG9pbnQpIHtcblx0XHRcdG1pblBvaW50LmRpc3RhbmNlID0gTWF0aC5zcXJ0KG1pbkRpc3RhbmNlKTtcblx0XHR9XG5cdFx0cmV0dXJuIG1pblBvaW50O1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xuXHQvLyBSZXR1cm5zIHRoZSBjZW50ZXIgKFtjZW50cm9pZF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DZW50cm9pZCkpIG9mIHRoZSBwb2x5bGluZS5cblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gdGhyb3dzIGVycm9yIHdoZW4gbm90IHlldCBhZGRlZCB0byBtYXAgYXMgdGhpcyBjZW50ZXIgY2FsY3VsYXRpb24gcmVxdWlyZXMgcHJvamVjdGVkIGNvb3JkaW5hdGVzXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignTXVzdCBhZGQgbGF5ZXIgdG8gbWFwIGJlZm9yZSB1c2luZyBnZXRDZW50ZXIoKScpO1xuXHRcdH1cblxuXHRcdHZhciBpLCBoYWxmRGlzdCwgc2VnRGlzdCwgZGlzdCwgcDEsIHAyLCByYXRpbyxcblx0XHQgICAgcG9pbnRzID0gdGhpcy5fcmluZ3NbMF0sXG5cdFx0ICAgIGxlbiA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm4gbnVsbDsgfVxuXG5cdFx0Ly8gcG9seWxpbmUgY2VudHJvaWQgYWxnb3JpdGhtOyBvbmx5IHVzZXMgdGhlIGZpcnN0IHJpbmcgaWYgdGhlcmUgYXJlIG11bHRpcGxlXG5cblx0XHRmb3IgKGkgPSAwLCBoYWxmRGlzdCA9IDA7IGkgPCBsZW4gLSAxOyBpKyspIHtcblx0XHRcdGhhbGZEaXN0ICs9IHBvaW50c1tpXS5kaXN0YW5jZVRvKHBvaW50c1tpICsgMV0pIC8gMjtcblx0XHR9XG5cblx0XHQvLyBUaGUgbGluZSBpcyBzbyBzbWFsbCBpbiB0aGUgY3VycmVudCB2aWV3IHRoYXQgYWxsIHBvaW50cyBhcmUgb24gdGhlIHNhbWUgcGl4ZWwuXG5cdFx0aWYgKGhhbGZEaXN0ID09PSAwKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhwb2ludHNbMF0pO1xuXHRcdH1cblxuXHRcdGZvciAoaSA9IDAsIGRpc3QgPSAwOyBpIDwgbGVuIC0gMTsgaSsrKSB7XG5cdFx0XHRwMSA9IHBvaW50c1tpXTtcblx0XHRcdHAyID0gcG9pbnRzW2kgKyAxXTtcblx0XHRcdHNlZ0Rpc3QgPSBwMS5kaXN0YW5jZVRvKHAyKTtcblx0XHRcdGRpc3QgKz0gc2VnRGlzdDtcblxuXHRcdFx0aWYgKGRpc3QgPiBoYWxmRGlzdCkge1xuXHRcdFx0XHRyYXRpbyA9IChkaXN0IC0gaGFsZkRpc3QpIC8gc2VnRGlzdDtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcoW1xuXHRcdFx0XHRcdHAyLnggLSByYXRpbyAqIChwMi54IC0gcDEueCksXG5cdFx0XHRcdFx0cDIueSAtIHJhdGlvICogKHAyLnkgLSBwMS55KVxuXHRcdFx0XHRdKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXG5cdC8vIFJldHVybnMgdGhlIGBMYXRMbmdCb3VuZHNgIG9mIHRoZSBwYXRoLlxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fYm91bmRzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYWRkTGF0TG5nKGxhdGxuZzogTGF0TG5nLCBsYXRsbmdzPyBMYXRMbmdbXSk6IHRoaXNcblx0Ly8gQWRkcyBhIGdpdmVuIHBvaW50IHRvIHRoZSBwb2x5bGluZS4gQnkgZGVmYXVsdCwgYWRkcyB0byB0aGUgZmlyc3QgcmluZyBvZlxuXHQvLyB0aGUgcG9seWxpbmUgaW4gY2FzZSBvZiBhIG11bHRpLXBvbHlsaW5lLCBidXQgY2FuIGJlIG92ZXJyaWRkZW4gYnkgcGFzc2luZ1xuXHQvLyBhIHNwZWNpZmljIHJpbmcgYXMgYSBMYXRMbmcgYXJyYXkgKHRoYXQgeW91IGNhbiBlYXJsaWVyIGFjY2VzcyB3aXRoIFtgZ2V0TGF0TG5nc2BdKCNwb2x5bGluZS1nZXRsYXRsbmdzKSkuXG5cdGFkZExhdExuZzogZnVuY3Rpb24gKGxhdGxuZywgbGF0bG5ncykge1xuXHRcdGxhdGxuZ3MgPSBsYXRsbmdzIHx8IHRoaXMuX2RlZmF1bHRTaGFwZSgpO1xuXHRcdGxhdGxuZyA9IHRvTGF0TG5nKGxhdGxuZyk7XG5cdFx0bGF0bG5ncy5wdXNoKGxhdGxuZyk7XG5cdFx0dGhpcy5fYm91bmRzLmV4dGVuZChsYXRsbmcpO1xuXHRcdHJldHVybiB0aGlzLnJlZHJhdygpO1xuXHR9LFxuXG5cdF9zZXRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBMYXRMbmdCb3VuZHMoKTtcblx0XHR0aGlzLl9sYXRsbmdzID0gdGhpcy5fY29udmVydExhdExuZ3MobGF0bG5ncyk7XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBpc0ZsYXQodGhpcy5fbGF0bG5ncykgPyB0aGlzLl9sYXRsbmdzIDogdGhpcy5fbGF0bG5nc1swXTtcblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSBjb252ZXJ0IGxhdGxuZ3MgaW5wdXQgaW50byBhY3R1YWwgTGF0TG5nIGluc3RhbmNlczsgY2FsY3VsYXRlIGJvdW5kcyBhbG9uZyB0aGUgd2F5XG5cdF9jb252ZXJ0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR2YXIgcmVzdWx0ID0gW10sXG5cdFx0ICAgIGZsYXQgPSBpc0ZsYXQobGF0bG5ncyk7XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF0bG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0aWYgKGZsYXQpIHtcblx0XHRcdFx0cmVzdWx0W2ldID0gdG9MYXRMbmcobGF0bG5nc1tpXSk7XG5cdFx0XHRcdHRoaXMuX2JvdW5kcy5leHRlbmQocmVzdWx0W2ldKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlc3VsdFtpXSA9IHRoaXMuX2NvbnZlcnRMYXRMbmdzKGxhdGxuZ3NbaV0pO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybi
14775ByZXN1bHQ7XG5cdH0sXG5cblx0X3Byb2plY3Q6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHhCb3VuZHMgPSBuZXcgQm91bmRzKCk7XG5cdFx0dGhpcy5fcmluZ3MgPSBbXTtcblx0XHR0aGlzLl9wcm9qZWN0TGF0bG5ncyh0aGlzLl9sYXRsbmdzLCB0aGlzLl9yaW5ncywgcHhCb3VuZHMpO1xuXG5cdFx0aWYgKHRoaXMuX2JvdW5kcy5pc1ZhbGlkKCkgJiYgcHhCb3VuZHMuaXNWYWxpZCgpKSB7XG5cdFx0XHR0aGlzLl9yYXdQeEJvdW5kcyA9IHB4Qm91bmRzO1xuXHRcdFx0dGhpcy5fdXBkYXRlQm91bmRzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVCb3VuZHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCksXG5cdFx0ICAgIHAgPSBuZXcgUG9pbnQodywgdyk7XG5cdFx0dGhpcy5fcHhCb3VuZHMgPSBuZXcgQm91bmRzKFtcblx0XHRcdHRoaXMuX3Jhd1B4Qm91bmRzLm1pbi5zdWJ0cmFjdChwKSxcblx0XHRcdHRoaXMuX3Jhd1B4Qm91bmRzLm1heC5hZGQocClcblx0XHRdKTtcblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSB0dXJucyBsYXRsbmdzIGludG8gYSBzZXQgb2YgcmluZ3Mgd2l0aCBwcm9qZWN0ZWQgY29vcmRpbmF0ZXNcblx0X3Byb2plY3RMYXRsbmdzOiBmdW5jdGlvbiAobGF0bG5ncywgcmVzdWx0LCBwcm9qZWN0ZWRCb3VuZHMpIHtcblx0XHR2YXIgZmxhdCA9IGxhdGxuZ3NbMF0gaW5zdGFuY2VvZiBMYXRMbmcsXG5cdFx0ICAgIGxlbiA9IGxhdGxuZ3MubGVuZ3RoLFxuXHRcdCAgICBpLCByaW5nO1xuXG5cdFx0aWYgKGZsYXQpIHtcblx0XHRcdHJpbmcgPSBbXTtcblx0XHRcdGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0XHRyaW5nW2ldID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmdzW2ldKTtcblx0XHRcdFx0cHJvamVjdGVkQm91bmRzLmV4dGVuZChyaW5nW2ldKTtcblx0XHRcdH1cblx0XHRcdHJlc3VsdC5wdXNoKHJpbmcpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdFx0dGhpcy5fcHJvamVjdExhdGxuZ3MobGF0bG5nc1tpXSwgcmVzdWx0LCBwcm9qZWN0ZWRCb3VuZHMpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHQvLyBjbGlwIHBvbHlsaW5lIGJ5IHJlbmRlcmVyIGJvdW5kcyBzbyB0aGF0IHdlIGhhdmUgbGVzcyB0byByZW5kZXIgZm9yIHBlcmZvcm1hbmNlXG5cdF9jbGlwUG9pbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuX3JlbmRlcmVyLl9ib3VuZHM7XG5cblx0XHR0aGlzLl9wYXJ0cyA9IFtdO1xuXHRcdGlmICghdGhpcy5fcHhCb3VuZHMgfHwgIXRoaXMuX3B4Qm91bmRzLmludGVyc2VjdHMoYm91bmRzKSkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLm9wdGlvbnMubm9DbGlwKSB7XG5cdFx0XHR0aGlzLl9wYXJ0cyA9IHRoaXMuX3JpbmdzO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBwYXJ0cyA9IHRoaXMuX3BhcnRzLFxuXHRcdCAgICBpLCBqLCBrLCBsZW4sIGxlbjIsIHNlZ21lbnQsIHBvaW50cztcblxuXHRcdGZvciAoaSA9IDAsIGsgPSAwLCBsZW4gPSB0aGlzLl9yaW5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0cG9pbnRzID0gdGhpcy5fcmluZ3NbaV07XG5cblx0XHRcdGZvciAoaiA9IDAsIGxlbjIgPSBwb2ludHMubGVuZ3RoOyBqIDwgbGVuMiAtIDE7IGorKykge1xuXHRcdFx0XHRzZWdtZW50ID0gY2xpcFNlZ21lbnQocG9pbnRzW2pdLCBwb2ludHNbaiArIDFdLCBib3VuZHMsIGosIHRydWUpO1xuXG5cdFx0XHRcdGlmICghc2VnbWVudCkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHBhcnRzW2tdID0gcGFydHNba10gfHwgW107XG5cdFx0XHRcdHBhcnRzW2tdLnB1c2goc2VnbWVudFswXSk7XG5cblx0XHRcdFx0Ly8gaWYgc2VnbWVudCBnb2VzIG91dCBvZiBzY3JlZW4sIG9yIGl0J3MgdGhlIGxhc3Qgb25lLCBpdCdzIHRoZSBlbmQgb2YgdGhlIGxpbmUgcGFydFxuXHRcdFx0XHRpZiAoKHNlZ21lbnRbMV0gIT09IHBvaW50c1tqICsgMV0pIHx8IChqID09PSBsZW4yIC0gMikpIHtcblx0XHRcdFx0XHRwYXJ0c1trXS5wdXNoKHNlZ21lbnRbMV0pO1xuXHRcdFx0XHRcdGsrKztcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHQvLyBzaW1wbGlmeSBlYWNoIGNsaXBwZWQgcGFydCBvZiB0aGUgcG9seWxpbmUgZm9yIHBlcmZvcm1hbmNlXG5cdF9zaW1wbGlmeVBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwYXJ0cyA9IHRoaXMuX3BhcnRzLFxuXHRcdCAgICB0b2xlcmFuY2UgPSB0aGlzLm9wdGlvbnMuc21vb3RoRmFjdG9yO1xuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHBhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwYXJ0c1tpXSA9IHNpbXBsaWZ5KHBhcnRzW2ldLCB0b2xlcmFuY2UpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9jbGlwUG9pbnRzKCk7XG5cdFx0dGhpcy5fc2ltcGxpZnlQb2ludHMoKTtcblx0XHR0aGlzLl91cGRhdGVQYXRoKCk7XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzKTtcblx0fSxcblxuXHQvLyBOZWVkZWQgYnkgdGhlIGBDYW52YXNgIHJlbmRlcmVyIGZvciBpbnRlcmFjdGl2aXR5XG5cdF9jb250YWluc1BvaW50OiBmdW5jdGlvbiAocCwgY2xvc2VkKSB7XG5cdFx0dmFyIGksIGosIGssIGxlbiwgbGVuMiwgcGFydCxcblx0XHQgICAgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG5cblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHRcdC8vIGhpdCBkZXRlY3Rpb24gZm9yIHBvbHlsaW5lc1xuXHRcdGZvciAoaSA9IDAsIGxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwYXJ0ID0gdGhpcy5fcGFydHNbaV07XG5cblx0XHRcdGZvciAoaiA9IDAsIGxlbjIgPSBwYXJ0Lmxlbmd0aCwgayA9IGxlbjIgLSAxOyBqIDwgbGVuMjsgayA9IGorKykge1xuXHRcdFx0XHRpZiAoIWNsb3NlZCAmJiAoaiA9PT0gMCkpIHsgY29udGludWU7IH1cblxuXHRcdFx0XHRpZiAocG9pbnRUb1NlZ21lbnREaXN0YW5jZShwLCBwYXJ0W2tdLCBwYXJ0W2pdKSA8PSB3KSB7XG5cdFx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5wb2x5bGluZShsYXRsbmdzOiBMYXRMbmdbXSwgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBwb2x5bGluZSBvYmplY3QgZ2l2ZW4gYW4gYXJyYXkgb2YgZ2VvZ3JhcGh
14775pY2FsIHBvaW50cyBhbmRcbi8vIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuIFlvdSBjYW4gY3JlYXRlIGEgYFBvbHlsaW5lYCBvYmplY3Qgd2l0aFxuLy8gbXVsdGlwbGUgc2VwYXJhdGUgbGluZXMgKGBNdWx0aVBvbHlsaW5lYCkgYnkgcGFzc2luZyBhbiBhcnJheSBvZiBhcnJheXNcbi8vIG9mIGdlb2dyYXBoaWMgcG9pbnRzLlxuZnVuY3Rpb24gcG9seWxpbmUobGF0bG5ncywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IFBvbHlsaW5lKGxhdGxuZ3MsIG9wdGlvbnMpO1xufVxuXG4vLyBSZXRyb2NvbXBhdC4gQWxsb3cgcGx1Z2lucyB0byBzdXBwb3J0IExlYWZsZXQgdmVyc2lvbnMgYmVmb3JlIGFuZCBhZnRlciAxLjEuXG5Qb2x5bGluZS5fZmxhdCA9IF9mbGF0O1xuXG4vKlxuICogQGNsYXNzIFBvbHlnb25cbiAqIEBha2EgTC5Qb2x5Z29uXG4gKiBAaW5oZXJpdHMgUG9seWxpbmVcbiAqXG4gKiBBIGNsYXNzIGZvciBkcmF3aW5nIHBvbHlnb24gb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYFBvbHlsaW5lYC5cbiAqXG4gKiBOb3RlIHRoYXQgcG9pbnRzIHlvdSBwYXNzIHdoZW4gY3JlYXRpbmcgYSBwb2x5Z29uIHNob3VsZG4ndCBoYXZlIGFuIGFkZGl0aW9uYWwgbGFzdCBwb2ludCBlcXVhbCB0byB0aGUgZmlyc3Qgb25lIOKAlCBpdCdzIGJldHRlciB0byBmaWx0ZXIgb3V0IHN1Y2ggcG9pbnRzLlxuICpcbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBjcmVhdGUgYSByZWQgcG9seWdvbiBmcm9tIGFuIGFycmF5IG9mIExhdExuZyBwb2ludHNcbiAqIHZhciBsYXRsbmdzID0gW1szNywgLTEwOS4wNV0sWzQxLCAtMTA5LjAzXSxbNDEsIC0xMDIuMDVdLFszNywgLTEwMi4wNF1dO1xuICpcbiAqIHZhciBwb2x5Z29uID0gTC5wb2x5Z29uKGxhdGxuZ3MsIHtjb2xvcjogJ3JlZCd9KS5hZGRUbyhtYXApO1xuICpcbiAqIC8vIHpvb20gdGhlIG1hcCB0byB0aGUgcG9seWdvblxuICogbWFwLmZpdEJvdW5kcyhwb2x5Z29uLmdldEJvdW5kcygpKTtcbiAqIGBgYFxuICpcbiAqIFlvdSBjYW4gYWxzbyBwYXNzIGFuIGFycmF5IG9mIGFycmF5cyBvZiBsYXRsbmdzLCB3aXRoIHRoZSBmaXJzdCBhcnJheSByZXByZXNlbnRpbmcgdGhlIG91dGVyIHNoYXBlIGFuZCB0aGUgb3RoZXIgYXJyYXlzIHJlcHJlc2VudGluZyBob2xlcyBpbiB0aGUgb3V0ZXIgc2hhcGU6XG4gKlxuICogYGBganNcbiAqIHZhciBsYXRsbmdzID0gW1xuICogICBbWzM3LCAtMTA5LjA1XSxbNDEsIC0xMDkuMDNdLFs0MSwgLTEwMi4wNV0sWzM3LCAtMTAyLjA0XV0sIC8vIG91dGVyIHJpbmdcbiAqICAgW1szNy4yOSwgLTEwOC41OF0sWzQwLjcxLCAtMTA4LjU4XSxbNDAuNzEsIC0xMDIuNTBdLFszNy4yOSwgLTEwMi41MF1dIC8vIGhvbGVcbiAqIF07XG4gKiBgYGBcbiAqXG4gKiBBZGRpdGlvbmFsbHksIHlvdSBjYW4gcGFzcyBhIG11bHRpLWRpbWVuc2lvbmFsIGFycmF5IHRvIHJlcHJlc2VudCBhIE11bHRpUG9seWdvbiBzaGFwZS5cbiAqXG4gKiBgYGBqc1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiAgIFsgLy8gZmlyc3QgcG9seWdvblxuICogICAgIFtbMzcsIC0xMDkuMDVdLFs0MSwgLTEwOS4wM10sWzQxLCAtMTAyLjA1XSxbMzcsIC0xMDIuMDRdXSwgLy8gb3V0ZXIgcmluZ1xuICogICAgIFtbMzcuMjksIC0xMDguNThdLFs0MC43MSwgLTEwOC41OF0sWzQwLjcxLCAtMTAyLjUwXSxbMzcuMjksIC0xMDIuNTBdXSAvLyBob2xlXG4gKiAgIF0sXG4gKiAgIFsgLy8gc2Vjb25kIHBvbHlnb25cbiAqICAgICBbWzQxLCAtMTExLjAzXSxbNDUsIC0xMTEuMDRdLFs0NSwgLTEwNC4wNV0sWzQxLCAtMTA0LjA1XV1cbiAqICAgXVxuICogXTtcbiAqIGBgYFxuICovXG5cbnZhciBQb2x5Z29uID0gUG9seWxpbmUuZXh0ZW5kKHtcblxuXHRvcHRpb25zOiB7XG5cdFx0ZmlsbDogdHJ1ZVxuXHR9LFxuXG5cdGlzRW1wdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gIXRoaXMuX2xhdGxuZ3MubGVuZ3RoIHx8ICF0aGlzLl9sYXRsbmdzWzBdLmxlbmd0aDtcblx0fSxcblxuXHRnZXRDZW50ZXI6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyB0aHJvd3MgZXJyb3Igd2hlbiBub3QgeWV0IGFkZGVkIHRvIG1hcCBhcyB0aGlzIGNlbnRlciBjYWxjdWxhdGlvbiByZXF1aXJlcyBwcm9qZWN0ZWQgY29vcmRpbmF0ZXNcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdNdXN0IGFkZCBsYXllciB0byBtYXAgYmVmb3JlIHVzaW5nIGdldENlbnRlcigpJyk7XG5cdFx0fVxuXG5cdFx0dmFyIGksIGosIHAxLCBwMiwgZiwgYXJlYSwgeCwgeSwgY2VudGVyLFxuXHRcdCAgICBwb2ludHMgPSB0aGlzLl9yaW5nc1swXSxcblx0XHQgICAgbGVuID0gcG9pbnRzLmxlbmd0aDtcblxuXHRcdGlmICghbGVuKSB7IHJldHVybiBudWxsOyB9XG5cblx0XHQvLyBwb2x5Z29uIGNlbnRyb2lkIGFsZ29yaXRobTsgb25seSB1c2VzIHRoZSBmaXJzdCByaW5nIGlmIHRoZXJlIGFyZSBtdWx0aXBsZVxuXG5cdFx0YXJlYSA9IHggPSB5ID0gMDtcblxuXHRcdGZvciAoaSA9IDAsIGogPSBsZW4gLSAxOyBpIDwgbGVuOyBqID0gaSsrKSB7XG5cdFx0XHRwMSA9IHBvaW50c1tpXTtcblx0XHRcdHAyID0gcG9pbnRzW2pdO1xuXG5cdFx0XHRmID0gcDEueSAqIHAyLnggLSBwMi55ICogcDEueDtcblx0XHRcdHggKz0gKHAxLnggKyBwMi54KSAqIGY7XG5cdFx0XHR5ICs9IChwMS55ICsgcDIueSkgKiBmO1xuXHRcdFx0YXJlYSArPSBmICogMztcblx0XHR9XG5cblx0XHRpZiAoYXJlYSA9PT0gMCkge1xuXHRcdFx0Ly8gUG9seWdvbiBpcyBzbyBzbWFsbCB0aGF0IGFsbCBwb2ludHMgYXJlIG9uIHNhbWUgcGl4ZWwuXG5cdFx0XHRjZW50ZXIgPSBwb2ludHNbMF07XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNlbnRlciA9IFt4IC8gYXJlYSwgeSAvIGFyZWFdO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhjZW50ZXIpO1xuXHR9LFxuXG5cdF9jb252ZXJ0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR2YXIgcmVzdWx0ID0gUG9seWxpbmUucHJvdG90eXBlLl9jb252ZXJ0TGF0TG5ncy5jYWxsKHRoaXMsIGxhdGxuZ3MpLFxuXHRcdCAgICBsZW4gPSByZXN1bHQubGVuZ3RoO1xuXG5cdFx0Ly8gcmVtb3ZlIGxhc3QgcG9pbnQgaWYgaXQgZXF1YWxzIGZpcnN0IG9uZVxuXHRcdGlmIChsZW4gPj0gMiAmJiByZXN1bHRbMF0gaW5zdGFuY2VvZiBMYXRMbmcgJiYgcmVzdWx0WzBdLmVxdWFscyhyZXN1bHRbbGVuIC0gMV0pKSB7XG5cdFx0XHRyZXN1bHQucG9wKCk7XG5cdFx0fVxuXHRcdHJldHVybi
14775ByZXN1bHQ7XG5cdH0sXG5cblx0X3NldExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0UG9seWxpbmUucHJvdG90eXBlLl9zZXRMYXRMbmdzLmNhbGwodGhpcywgbGF0bG5ncyk7XG5cdFx0aWYgKGlzRmxhdCh0aGlzLl9sYXRsbmdzKSkge1xuXHRcdFx0dGhpcy5fbGF0bG5ncyA9IFt0aGlzLl9sYXRsbmdzXTtcblx0XHR9XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBpc0ZsYXQodGhpcy5fbGF0bG5nc1swXSkgPyB0aGlzLl9sYXRsbmdzWzBdIDogdGhpcy5fbGF0bG5nc1swXVswXTtcblx0fSxcblxuXHRfY2xpcFBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHBvbHlnb25zIG5lZWQgYSBkaWZmZXJlbnQgY2xpcHBpbmcgYWxnb3JpdGhtIHNvIHdlIHJlZGVmaW5lIHRoYXRcblxuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZW5kZXJlci5fYm91bmRzLFxuXHRcdCAgICB3ID0gdGhpcy5vcHRpb25zLndlaWdodCxcblx0XHQgICAgcCA9IG5ldyBQb2ludCh3LCB3KTtcblxuXHRcdC8vIGluY3JlYXNlIGNsaXAgcGFkZGluZyBieSBzdHJva2Ugd2lkdGggdG8gYXZvaWQgc3Ryb2tlIG9uIGNsaXAgZWRnZXNcblx0XHRib3VuZHMgPSBuZXcgQm91bmRzKGJvdW5kcy5taW4uc3VidHJhY3QocCksIGJvdW5kcy5tYXguYWRkKHApKTtcblxuXHRcdHRoaXMuX3BhcnRzID0gW107XG5cdFx0aWYgKCF0aGlzLl9weEJvdW5kcyB8fCAhdGhpcy5fcHhCb3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5ub0NsaXApIHtcblx0XHRcdHRoaXMuX3BhcnRzID0gdGhpcy5fcmluZ3M7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHRoaXMuX3JpbmdzLmxlbmd0aCwgY2xpcHBlZDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRjbGlwcGVkID0gY2xpcFBvbHlnb24odGhpcy5fcmluZ3NbaV0sIGJvdW5kcywgdHJ1ZSk7XG5cdFx0XHRpZiAoY2xpcHBlZC5sZW5ndGgpIHtcblx0XHRcdFx0dGhpcy5fcGFydHMucHVzaChjbGlwcGVkKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzLCB0cnVlKTtcblx0fSxcblxuXHQvLyBOZWVkZWQgYnkgdGhlIGBDYW52YXNgIHJlbmRlcmVyIGZvciBpbnRlcmFjdGl2aXR5XG5cdF9jb250YWluc1BvaW50OiBmdW5jdGlvbiAocCkge1xuXHRcdHZhciBpbnNpZGUgPSBmYWxzZSxcblx0XHQgICAgcGFydCwgcDEsIHAyLCBpLCBqLCBrLCBsZW4sIGxlbjI7XG5cblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHRcdC8vIHJheSBjYXN0aW5nIGFsZ29yaXRobSBmb3IgZGV0ZWN0aW5nIGlmIHBvaW50IGlzIGluIHBvbHlnb25cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSB0aGlzLl9wYXJ0cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0cGFydCA9IHRoaXMuX3BhcnRzW2ldO1xuXG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydC5sZW5ndGgsIGsgPSBsZW4yIC0gMTsgaiA8IGxlbjI7IGsgPSBqKyspIHtcblx0XHRcdFx0cDEgPSBwYXJ0W2pdO1xuXHRcdFx0XHRwMiA9IHBhcnRba107XG5cblx0XHRcdFx0aWYgKCgocDEueSA+IHAueSkgIT09IChwMi55ID4gcC55KSkgJiYgKHAueCA8IChwMi54IC0gcDEueCkgKiAocC55IC0gcDEueSkgLyAocDIueSAtIHAxLnkpICsgcDEueCkpIHtcblx0XHRcdFx0XHRpbnNpZGUgPSAhaW5zaWRlO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gYWxzbyBjaGVjayBpZiBpdCdzIG9uIHBvbHlnb24gc3Ryb2tlXG5cdFx0cmV0dXJuIGluc2lkZSB8fCBQb2x5bGluZS5wcm90b3R5cGUuX2NvbnRhaW5zUG9pbnQuY2FsbCh0aGlzLCBwLCB0cnVlKTtcblx0fVxuXG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLnBvbHlnb24obGF0bG5nczogTGF0TG5nW10sIG9wdGlvbnM/OiBQb2x5bGluZSBvcHRpb25zKVxuZnVuY3Rpb24gcG9seWdvbihsYXRsbmdzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgUG9seWdvbihsYXRsbmdzLCBvcHRpb25zKTtcbn1cblxuLypcclxuICogQGNsYXNzIEdlb0pTT05cclxuICogQGFrYSBMLkdlb0pTT05cclxuICogQGluaGVyaXRzIEZlYXR1cmVHcm91cFxyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgR2VvSlNPTiBvYmplY3Qgb3IgYW4gYXJyYXkgb2YgR2VvSlNPTiBvYmplY3RzLiBBbGxvd3MgeW91IHRvIHBhcnNlXHJcbiAqIEdlb0pTT04gZGF0YSBhbmQgZGlzcGxheSBpdCBvbiB0aGUgbWFwLiBFeHRlbmRzIGBGZWF0dXJlR3JvdXBgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmdlb0pTT04oZGF0YSwge1xyXG4gKiBcdHN0eWxlOiBmdW5jdGlvbiAoZmVhdHVyZSkge1xyXG4gKiBcdFx0cmV0dXJuIHtjb2xvcjogZmVhdHVyZS5wcm9wZXJ0aWVzLmNvbG9yfTtcclxuICogXHR9XHJcbiAqIH0pLmJpbmRQb3B1cChmdW5jdGlvbiAobGF5ZXIpIHtcclxuICogXHRyZXR1cm4gbGF5ZXIuZmVhdHVyZS5wcm9wZXJ0aWVzLmRlc2NyaXB0aW9uO1xyXG4gKiB9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG52YXIgR2VvSlNPTiA9IEZlYXR1cmVHcm91cC5leHRlbmQoe1xyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqIEBha2EgR2VvSlNPTiBvcHRpb25zXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvaW50VG9MYXllcjogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIGRlZmluaW5nIGhvdyBHZW9KU09OIHBvaW50cyBzcGF3biBMZWFmbGV0IGxheWVycy4gSXQgaXMgaW50ZXJuYWxseVxyXG5cdCAqIGNhbGxlZCB3aGVuIGRhdGEgaXMgYWRkZWQsIHBhc3NpbmcgdGhlIEdlb0pTT04gcG9pbnQgZmVhdHVyZSBhbmQgaXRzIGBMYXRMbmdgLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRvIHNwYXduIGEgZGVmYXVsdCBgTWFya2VyYDpcclxuXHQgKiBgYGBqc1xyXG5cdCAqIGZ1bmN0aW9uKGdlb0pzb25Qb2ludCwgbGF0bG5nKSB7XHJcblx0ICogXHRyZXR1cm4gTC5tYXJrZXIobGF0bG5nKTtcclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHN0eWxlOiBGdW5jdGlvbiA9ICpcclxuXHQgKiBBIGBGdW5jdGlvbmAgZGVmaW5pbmcgdGhlIGBQYXRoIG9wdGlvbnNgIGZvciBzdHlsaW5nIEdlb0pTT04gbGluZXMgYW5kIHBvbHlnb25zLFxyXG5cdCAqIGNhbGxlZCBpbnRlcm5hbGx5IHdoZW4gZGF0YSBpcyBhZGRlZC5cclxuXHQgKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0byBub3Qgb3ZlcnJpZGUgYW55IGRlZmF1bHRzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGdlb0pzb25GZWF0dXJlKSB7XHJcblx0ICogXHRyZXR1cm4ge31cclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIG9uRWFjaEZlYXR1cmU6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgY2FsbGVkIG9uY2UgZm9yIGVhY2ggY3JlYXRlZCBgRmVhdHVyZWAsIGFmdGVyIGl0IGhhc1xyXG5cdCAqIGJlZW4gY3JlYXRlZCBhbmQgc3R5bGVkLiBVc2VmdWwgZm9yIGF0dGFjaGluZyBldmVudHMgYW5kIHBvcHVwcyB0byBmZWF0dXJlcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0byBkbyBub3RoaW5nIHdpdGggdGhlIG5ld2x5IGNyZWF0ZWQgbGF5ZXJzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGZlYXR1cmUsIGxheWVyKSB7fVxyXG5cdCAqIGBgYFxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBmaWx0ZXI6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCB0byBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIGEgZmVhdHVyZSBvciBub3QuXHJcblx0ICogVGhlIGRlZmF1bHQgaXMgdG8gaW5jbHVkZSBhbGwgZmVhdHVyZXM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZ2VvSnNvbkZlYXR1cmUpIHtcclxuXHQgKiBcdHJldHVybi
14775B0cnVlO1xyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKiBOb3RlOiBkeW5hbWljYWxseSBjaGFuZ2luZyB0aGUgYGZpbHRlcmAgb3B0aW9uIHdpbGwgaGF2ZSBlZmZlY3Qgb25seSBvbiBuZXdseVxyXG5cdCAqIGFkZGVkIGRhdGEuIEl0IHdpbGwgX25vdF8gcmUtZXZhbHVhdGUgYWxyZWFkeSBpbmNsdWRlZCBmZWF0dXJlcy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gY29vcmRzVG9MYXRMbmc6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgY29udmVydGluZyBHZW9KU09OIGNvb3JkaW5hdGVzIHRvIGBMYXRMbmdgcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0aGUgYGNvb3Jkc1RvTGF0TG5nYCBzdGF0aWMgbWV0aG9kLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBtYXJrZXJzSW5oZXJpdE9wdGlvbnM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdCAqIFdoZXRoZXIgZGVmYXVsdCBNYXJrZXJzIGZvciBcIlBvaW50XCIgdHlwZSBGZWF0dXJlcyBpbmhlcml0IGZyb20gZ3JvdXAgb3B0aW9ucy5cclxuXHQgKi9cclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGdlb2pzb24sIG9wdGlvbnMpIHtcclxuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0aWYgKGdlb2pzb24pIHtcclxuXHRcdFx0dGhpcy5hZGREYXRhKGdlb2pzb24pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkRGF0YSggPEdlb0pTT04+IGRhdGEgKTogdGhpc1xyXG5cdC8vIEFkZHMgYSBHZW9KU09OIG9iamVjdCB0byB0aGUgbGF5ZXIuXHJcblx0YWRkRGF0YTogZnVuY3Rpb24gKGdlb2pzb24pIHtcclxuXHRcdHZhciBmZWF0dXJlcyA9IGlzQXJyYXkoZ2VvanNvbikgPyBnZW9qc29uIDogZ2VvanNvbi5mZWF0dXJlcyxcclxuXHRcdCAgICBpLCBsZW4sIGZlYXR1cmU7XHJcblxyXG5cdFx0aWYgKGZlYXR1cmVzKSB7XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGZlYXR1cmVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0Ly8gb25seSBhZGQgdGh
14775pcyBpZiBnZW9tZXRyeSBvciBnZW9tZXRyaWVzIGFyZSBzZXQgYW5kIG5vdCBudWxsXHJcblx0XHRcdFx0ZmVhdHVyZSA9IGZlYXR1cmVzW2ldO1xyXG5cdFx0XHRcdGlmIChmZWF0dXJlLmdlb21ldHJpZXMgfHwgZmVhdHVyZS5nZW9tZXRyeSB8fCBmZWF0dXJlLmZlYXR1cmVzIHx8IGZlYXR1cmUuY29vcmRpbmF0ZXMpIHtcclxuXHRcdFx0XHRcdHRoaXMuYWRkRGF0YShmZWF0dXJlKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuZmlsdGVyICYmICFvcHRpb25zLmZpbHRlcihnZW9qc29uKSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBsYXllciA9IGdlb21ldHJ5VG9MYXllcihnZW9qc29uLCBvcHRpb25zKTtcclxuXHRcdGlmICghbGF5ZXIpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRsYXllci5mZWF0dXJlID0gYXNGZWF0dXJlKGdlb2pzb24pO1xyXG5cclxuXHRcdGxheWVyLmRlZmF1bHRPcHRpb25zID0gbGF5ZXIub3B0aW9ucztcclxuXHRcdHRoaXMucmVzZXRTdHlsZShsYXllcik7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMub25FYWNoRmVhdHVyZSkge1xyXG5cdFx0XHRvcHRpb25zLm9uRWFjaEZlYXR1cmUoZ2VvanNvbiwgbGF5ZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKGxheWVyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlc2V0U3R5bGUoIDxQYXRoPiBsYXllcj8gKTogdGhpc1xyXG5cdC8vIFJlc2V0cyB0aGUgZ2l2ZW4gdmVjdG9yIGxheWVyJ3Mgc3R5bGUgdG8gdGhlIG9yaWdpbmFsIEdlb0pTT04gc3R5bGUsIHVzZWZ1bCBmb3IgcmVzZXR0aW5nIHN0eWxlIGFmdGVyIGhvdmVyIGV2ZW50cy5cclxuXHQvLyBJZiBgbGF5ZXJgIGlzIG9taXR0ZWQsIHRoZSBzdHlsZSBvZiBhbGwgZmVhdHVyZXMgaW4gdGhlIGN1cnJlbnQgbGF5ZXIgaXMgcmVzZXQuXHJcblx0cmVzZXRTdHlsZTogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRpZiAobGF5ZXIgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5lYWNoTGF5ZXIodGhpcy5yZXNldFN0eWxlLCB0aGlzKTtcclxuXHRcdH1cclxuXHRcdC8vIHJlc2V0IGFueSBjdXN0b20gc3R5bGVzXHJcblx0XHRsYXllci5vcHRpb25zID0gZXh0ZW5kKHt9LCBsYXllci5kZWZhdWx0T3B0aW9ucyk7XHJcblx0XHR0aGlzLl9zZXRMYXllclN0eWxlKGxheWVyLCB0aGlzLm9wdGlvbnMuc3R5bGUpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRTdHlsZSggPEZ1bmN0aW9uPiBzdHlsZSApOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyBzdHlsZXMgb2YgR2VvSlNPTiB2ZWN0b3IgbGF5ZXJzIHdpdGggdGhlIGdpdmVuIHN0eWxlIGZ1bmN0aW9uLlxyXG5cdHNldFN0eWxlOiBmdW5jdGlvbiAoc3R5bGUpIHtcclxuXHRcdHJldHVybiB0aGlzLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdFx0dGhpcy5fc2V0TGF5ZXJTdHlsZShsYXllciwgc3R5bGUpO1xyXG5cdFx0fSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X3NldExheWVyU3R5bGU6IGZ1bmN0aW9uIChsYXllciwgc3R5bGUpIHtcclxuXHRcdGlmIChsYXllci5zZXRTdHlsZSkge1xyXG5cdFx0XHRpZiAodHlwZW9mIHN0eWxlID09PSAnZnVuY3Rpb24nKSB7XHJcblx0XHRcdFx0c3R5bGUgPSBzdHlsZShsYXllci5mZWF0dXJlKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRsYXllci5zZXRTdHlsZShzdHlsZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBzZWN0aW9uXHJcbi8vIFRoZXJlIGFyZSBzZXZlcmFsIHN0YXRpYyBmdW5jdGlvbnMgd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5HZW9KU09OOlxyXG5cclxuLy8gQGZ1bmN0aW9uIGdlb21ldHJ5VG9MYXllcihmZWF0dXJlRGF0YTogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKTogTGF5ZXJcclxuLy8gQ3JlYXRlcyBhIGBMYXllcmAgZnJvbSBhIGdpdmVuIEdlb0pTT04gZmVhdHVyZS4gQ2FuIHVzZSBhIGN1c3RvbVxyXG4vLyBbYHBvaW50VG9MYXllcmBdKCNnZW9qc29uLXBvaW50dG9sYXllcikgYW5kL29yIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZylcclxuLy8gZnVuY3Rpb25zIGlmIHByb3ZpZGVkIGFzIG9wdGlvbnMuXHJcbmZ1bmN0aW9uIGdlb21ldHJ5VG9MYXllcihnZW9qc29uLCBvcHRpb25zKSB7XHJcblxyXG5cdHZhciBnZW9tZXRyeSA9IGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmUnID8gZ2VvanNvbi5nZW9tZXRyeSA6IGdlb2pzb24sXHJcblx0ICAgIGNvb3JkcyA9IGdlb21ldHJ5ID8gZ2VvbWV0cnkuY29vcmRpbmF0ZXMgOiBudWxsLFxyXG5cdCAgICBsYXllcnMgPSBbXSxcclxuXHQgICAgcG9pbnRUb0xheWVyID0gb3B0aW9ucyAmJiBvcHRpb25zLnBvaW50VG9MYXllcixcclxuXHQgICAgX2Nvb3Jkc1RvTGF0TG5nID0gb3B0aW9ucyAmJiBvcHRpb25zLmNvb3Jkc1RvTGF0TG5nIHx8IGNvb3Jkc1RvTGF0TG5nLFxyXG5cdCAgICBsYXRsbmcsIGxhdGxuZ3MsIGksIGxlbjtcclxuXHJcblx0aWYgKCFjb29yZHMgJiYgIWdlb21ldHJ5KSB7XHJcblx0XHRyZXR1cm4gbnVsbDtcclxuXHR9XHJcblxyXG5cdHN3aXRjaCAoZ2VvbWV0cnkudHlwZSkge1xyXG5cdGNhc2UgJ1BvaW50JzpcclxuXHRcdGxhdGxuZyA9IF9jb29yZHNUb0xhdExuZyhjb29yZHMpO1xyXG5cdFx0cmV0dXJuIF9wb2ludFRvTGF5ZXIocG9pbnRUb0xheWVyLCBnZW9qc29uLCBsYXRsbmcsIG9wdGlvbnMpO1xyXG5cclxuXHRjYXNlICdNdWx0aVBvaW50JzpcclxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvb3Jkcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRsYXRsbmcgPSBfY29vcmRzVG9MYXRMbmcoY29vcmRzW2ldKTtcclxuXHRcdFx0bGF5ZXJzLnB1c2goX3BvaW50VG9MYXllcihwb2ludFRvTGF5ZXIsIGdlb2pzb24sIGxhdGxuZywgb3B0aW9ucykpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIG5ldyBGZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxuXHJcblx0Y2FzZSAnTGluZVN0cmluZyc6XHJcblx0Y2FzZSAnTXVsdGlMaW5lU3RyaW5nJzpcclxuXHRcdGxhdGxuZ3MgPSBjb29yZHNUb0xhdExuZ3MoY29vcmRzLCBnZW9tZXRyeS50eXBlID09PSAnTGluZVN0cmluZycgPyAwIDogMSwgX2Nvb3Jkc1RvTGF0TG5nKTtcclxuXHRcdHJldHVybi
14775BuZXcgUG9seWxpbmUobGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdGNhc2UgJ1BvbHlnb24nOlxyXG5cdGNhc2UgJ011bHRpUG9seWdvbic6XHJcblx0XHRsYXRsbmdzID0gY29vcmRzVG9MYXRMbmdzKGNvb3JkcywgZ2VvbWV0cnkudHlwZSA9PT0gJ1BvbHlnb24nID8gMSA6IDIsIF9jb29yZHNUb0xhdExuZyk7XHJcblx0XHRyZXR1cm4gbmV3IFBvbHlnb24obGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdGNhc2UgJ0dlb21ldHJ5Q29sbGVjdGlvbic6XHJcblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBnZW9tZXRyeS5nZW9tZXRyaWVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdHZhciBsYXllciA9IGdlb21ldHJ5VG9MYXllcih7XHJcblx0XHRcdFx0Z2VvbWV0cnk6IGdlb21ldHJ5Lmdlb21ldHJpZXNbaV0sXHJcblx0XHRcdFx0dHlwZTogJ0ZlYXR1cmUnLFxyXG5cdFx0XHRcdHByb3BlcnRpZXM6IGdlb2pzb24ucHJvcGVydGllc1xyXG5cdFx0XHR9LCBvcHRpb25zKTtcclxuXHJcblx0XHRcdGlmIChsYXllcikge1xyXG5cdFx0XHRcdGxheWVycy5wdXNoKGxheWVyKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIG5ldyBGZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxuXHJcblx0ZGVmYXVsdDpcclxuXHRcdHRocm93IG5ldyBFcnJvcignSW52YWxpZCBHZW9KU09OIG9iamVjdC4nKTtcclxuXHR9XHJcbn1cclxuXHJcbmZ1bmN0aW9uIF9wb2ludFRvTGF5ZXIocG9pbnRUb0xheWVyRm4sIGdlb2pzb24sIGxhdGxuZywgb3B0aW9ucykge1xyXG5cdHJldHVybiBwb2ludFRvTGF5ZXJGbiA/XHJcblx0XHRwb2ludFRvTGF5ZXJGbihnZW9qc29uLCBsYXRsbmcpIDpcclxuXHRcdG5ldyBNYXJrZXIobGF0bG5nLCBvcHRpb25zICYmIG9wdGlvbnMubWFya2Vyc0luaGVyaXRPcHRpb25zICYmIG9wdGlvbnMpO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gY29vcmRzVG9MYXRMbmcoY29vcmRzOiBBcnJheSk6IExhdExuZ1xyXG4vLyBDcmVhdGVzIGEgYExhdExuZ2Agb2JqZWN0IGZyb20gYW4gYXJyYXkgb2YgMiBudW1iZXJzIChsb25naXR1ZGUsIGxhdGl0dWRlKVxyXG4vLyBvciAzIG51bWJlcnMgKGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGFsdGl0dWRlKSB1c2VkIGluIEdlb0pTT04gZm9yIHBvaW50cy5cclxuZnVuY3Rpb24gY29vcmRzVG9MYXRMbmcoY29vcmRzKSB7XHJcblx0cmV0dXJuIG5ldyBMYXRMbmcoY29vcmRzWzFdLCBjb29yZHNbMF0sIGNvb3Jkc1syXSk7XHJcbn1cclxuXHJcbi8vIEBmdW5jdGlvbiBjb29yZHNUb0xhdExuZ3MoY29vcmRzOiBBcnJheSwgbGV2ZWxzRGVlcD86IE51bWJlciwgY29vcmRzVG9MYXRMbmc/OiBGdW5jdGlvbik6IEFycmF5XHJcbi8vIENyZWF0ZXMgYSBtdWx0aWRpbWVuc2lvbmFsIGFycmF5IG9mIGBMYXRMbmdgcyBmcm9tIGEgR2VvSlNPTiBjb29yZGluYXRlcyBhcnJheS5cclxuLy8gYGxldmVsc0RlZXBgIHNwZWNpZmllcyB0aGUgbmVzdGluZyBsZXZlbCAoMCBpcyBmb3IgYW4gYXJyYXkgb2YgcG9pbnRzLCAxIGZvciBhbiBhcnJheSBvZiBhcnJheXMgb2YgcG9pbnRzLCBldGMuLCAwIGJ5IGRlZmF1bHQpLlxyXG4vLyBDYW4gdXNlIGEgY3VzdG9tIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZykgZnVuY3Rpb24uXHJcbmZ1bmN0aW9uIGNvb3Jkc1RvTGF0TG5ncyhjb29yZHMsIGxldmVsc0RlZXAsIF9jb29yZHNUb0xhdExuZykge1xyXG5cdHZhciBsYXRsbmdzID0gW107XHJcblxyXG5cdGZvciAodmFyIGkgPSAwLCBsZW4gPSBjb29yZHMubGVuZ3RoLCBsYXRsbmc7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0bGF0bG5nID0gbGV2ZWxzRGVlcCA/XHJcblx0XHRcdGNvb3Jkc1RvTGF0TG5ncyhjb29yZHNbaV0sIGxldmVsc0RlZXAgLSAxLCBfY29vcmRzVG9MYXRMbmcpIDpcclxuXHRcdFx0KF9jb29yZHNUb0xhdExuZyB8fCBjb29yZHNUb0xhdExuZykoY29vcmRzW2ldKTtcclxuXHJcblx0XHRsYXRsbmdzLnB1c2gobGF0bG5nKTtcclxuXHR9XHJcblxyXG5cdHJldHVybiBsYXRsbmdzO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gbGF0TG5nVG9Db29yZHMobGF0bG5nOiBMYXRMbmcsIHByZWNpc2lvbj86IE51bWJlcik6IEFycmF5XHJcbi8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ2BdKCNnZW9qc29uLWNvb3Jkc3RvbGF0bG5nKVxyXG5mdW5jdGlvbiBsYXRMbmdUb0Nvb3JkcyhsYXRsbmcsIHByZWNpc2lvbikge1xyXG5cdHByZWNpc2lvbiA9IHR5cGVvZiBwcmVjaXNpb24gPT09ICdudW1iZXInID8gcHJlY2lzaW9uIDogNjtcclxuXHRyZXR1cm4gbGF0bG5nLmFsdCAhPT0gdW5kZWZpbmVkID9cclxuXHRcdFtmb3JtYXROdW0obGF0bG5nLmxuZywgcHJlY2lzaW9uKSwgZm9ybWF0TnVtKGxhdGxuZy5sYXQsIHByZWNpc2lvbiksIGZvcm1hdE51bShsYXRsbmcuYWx0LCBwcmVjaXNpb24pXSA6XHJcblx0XHRbZm9ybWF0TnVtKGxhdGxuZy5sbmcsIHByZWNpc2lvbiksIGZvcm1hdE51bShsYXRsbmcubGF0LCBwcmVjaXNpb24pXTtcclxufVxyXG5cclxuLy8gQGZ1bmN0aW9uIGxhdExuZ3NUb0Nvb3JkcyhsYXRsbmdzOiBBcnJheSwgbGV2ZWxzRGVlcD86IE51bWJlciwgY2xvc2VkPzogQm9vbGVhbik6IEFycmF5XHJcbi8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ3NgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZ3MpXHJcbi8vIGBjbG9zZWRgIGRldGVybWluZXMgd2hldGhlciB0aGUgZmlyc3QgcG9pbnQgc2hvdWxkIGJlIGFwcGVuZGVkIHRvIHRoZSBlbmQgb2YgdGhlIGFycmF5IHRvIGNsb3NlIHRoZSBmZWF0dXJlLCBvbmx5IHVzZWQgd2hlbiBgbGV2ZWxzRGVlcGAgaXMgMC4gRmFsc2UgYnkgZGVmYXVsdC5cclxuZnVuY3Rpb24gbGF0TG5nc1RvQ29vcmRzKGxhdGxuZ3MsIGxldmVsc0RlZXAsIGNsb3NlZCwgcHJlY2lzaW9uKSB7XHJcblx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF0bG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0Y29vcmRzLnB1c2gobGV2ZWxzRGVlcCA/XHJcblx0XHRcdGxhdExuZ3NUb0Nvb3JkcyhsYXRsbmdzW2ldLCBsZXZlbHNEZWVwIC0gMSwgY2xvc2VkLCBwcmVjaXNpb24pIDpcclxuXHRcdFx0bGF0TG5nVG9Db29yZHMobGF0bG5nc1tpXSwgcHJlY2lzaW9uKSk7XHJcblx0fVxyXG5cclxuXHRpZiAoIWxldmVsc0RlZXAgJiYgY2xvc2VkKSB7XHJcblx0XHRjb29yZHMucHVzaChjb29yZHNbMF0pO1xyXG5cdH1cclxuXHJcblx0cmV0dXJuIGNvb3JkcztcclxufVxyXG5cclxuZnVuY3Rpb24gZ2V0RmVhdHVyZShsYXllciwgbmV3R2VvbWV0cnkpIHtcclxuXHRyZXR1cm4gbGF5ZXIuZmVhdHVyZSA/XHJcblx0XHRleHRlbmQoe30sIGxheWVyLmZlYXR1cmUsIHtnZW9tZXRyeTogbmV3R2VvbWV0cnl9KSA6XHJcblx0XHRhc0ZlYXR1cmUobmV3R2VvbWV0cnkpO1xyXG59XHJcblxyXG4vLyBAZnVuY3Rpb24gYXNGZWF0dXJlKGdlb2pzb246IE9iamVjdCk6IE9iamVjdFxyXG4vLyBOb3JtYWxpemUgR2VvSlNPTiBnZW9tZXRyaWVzL2ZlYXR1cmVzIGludG8gR2VvSlNPTiBmZWF0dXJlcy5cclxuZnVuY3Rpb24gYXNGZWF0dXJlKGdlb2pzb24pIHtcclxuXHRpZiAoZ2VvanNvbi50eX
14775BlID09PSAnRmVhdHVyZScgfHwgZ2VvanNvbi50eXBlID09PSAnRmVhdHVyZUNvbGxlY3Rpb24nKSB7XHJcblx0XHRyZXR1cm4gZ2VvanNvbjtcclxuXHR9XHJcblxyXG5cdHJldHVybiB7XHJcblx0XHR0eXBlOiAnRmVhdHVyZScsXHJcblx0XHRwcm9wZXJ0aWVzOiB7fSxcclxuXHRcdGdlb21ldHJ5OiBnZW9qc29uXHJcblx0fTtcclxufVxyXG5cclxudmFyIFBvaW50VG9HZW9KU09OID0ge1xyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKHByZWNpc2lvbikge1xyXG5cdFx0cmV0dXJuIGdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHR0eXBlOiAnUG9pbnQnLFxyXG5cdFx0XHRjb29yZGluYXRlczogbGF0TG5nVG9Db29yZHModGhpcy5nZXRMYXRMbmcoKSwgcHJlY2lzaW9uKVxyXG5cdFx0fSk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXJrZXJcclxuLy8gQHNlY3Rpb24gT3RoZXIgbWV0aG9kc1xyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTihwcmVjaXNpb24/OiBOdW1iZXIpOiBPYmplY3RcclxuLy8gYHByZWNpc2lvbmAgaXMgdGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyBmb3IgY29vcmRpbmF0ZXMuXHJcbi8vIFRoZSBkZWZhdWx0IHZhbHVlIGlzIDYgcGxhY2VzLlxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgbWFya2VyIChhcyBhIEdlb0pTT04gYFBvaW50YCBGZWF0dXJlKS5cclxuTWFya2VyLmluY2x1ZGUoUG9pbnRUb0dlb0pTT04pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBDaXJjbGVNYXJrZXJcclxuLy8gQG1ldGhvZCB0b0dlb0pTT04ocHJlY2lzaW9uPzogTnVtYmVyKTogT2JqZWN0XHJcbi8vIGBwcmVjaXNpb25gIGlzIHRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgZm9yIGNvb3JkaW5hdGVzLlxyXG4vLyBUaGUgZGVmYXVsdCB2YWx1ZSBpcyA2IHBsYWNlcy5cclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNpcmNsZSBtYXJrZXIgKGFzIGEgR2VvSlNPTiBgUG9pbnRgIEZlYXR1cmUpLlxyXG5DaXJjbGUuaW5jbHVkZShQb2ludFRvR2VvSlNPTik7XHJcbkNpcmNsZU1hcmtlci5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIFBvbHlsaW5lXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKHByZWNpc2lvbj86IE51bWJlcik6IE9iamVjdFxyXG4vLyBgcHJlY2lzaW9uYCBpcyB0aGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIGZvciBjb29yZGluYXRlcy5cclxuLy8gVGhlIGRlZmF1bHQgdmFsdWUgaXMgNiBwbGFjZXMuXHJcbi8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2x5bGluZSAoYXMgYSBHZW9KU09OIGBMaW5lU3RyaW5nYCBvciBgTXVsdGlMaW5lU3RyaW5nYCBGZWF0dXJlKS5cclxuUG9seWxpbmUuaW5jbHVkZSh7XHJcblx0dG9HZW9KU09OOiBmdW5jdGlvbiAocHJlY2lzaW9uKSB7XHJcblx0XHR2YXIgbXVsdGkgPSAhaXNGbGF0KHRoaXMuX2xhdGxuZ3MpO1xyXG5cclxuXHRcdHZhciBjb29yZHMgPSBsYXRMbmdzVG9Db29yZHModGhpcy5fbGF0bG5ncywgbXVsdGkgPyAxIDogMCwgZmFsc2UsIHByZWNpc2lvbik7XHJcblxyXG5cdFx0cmV0dXJuIGdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHR0eXBlOiAobXVsdGkgP
14775yAnTXVsdGknIDogJycpICsgJ0xpbmVTdHJpbmcnLFxyXG5cdFx0XHRjb29yZGluYXRlczogY29vcmRzXHJcblx0XHR9KTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb2x5Z29uXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKHByZWNpc2lvbj86IE51bWJlcik6IE9iamVjdFxyXG4vLyBgcHJlY2lzaW9uYCBpcyB0aGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIGZvciBjb29yZGluYXRlcy5cclxuLy8gVGhlIGRlZmF1bHQgdmFsdWUgaXMgNiBwbGFjZXMuXHJcbi8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2x5Z29uIChhcyBhIEdlb0pTT04gYFBvbHlnb25gIG9yIGBNdWx0aVBvbHlnb25gIEZlYXR1cmUpLlxyXG5Qb2x5Z29uLmluY2x1ZGUoe1xyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKHByZWNpc2lvbikge1xyXG5cdFx0dmFyIGhvbGVzID0gIWlzRmxhdCh0aGlzLl9sYXRsbmdzKSxcclxuXHRcdCAgICBtdWx0aSA9IGhvbGVzICYmICFpc0ZsYXQodGhpcy5fbGF0bG5nc1swXSk7XHJcblxyXG5cdFx0dmFyIGNvb3JkcyA9IGxhdExuZ3NUb0Nvb3Jkcyh0aGlzLl9sYXRsbmdzLCBtdWx0aSA/IDIgOiBob2xlcyA/IDEgOiAwLCB0cnVlLCBwcmVjaXNpb24pO1xyXG5cclxuXHRcdGlmICghaG9sZXMpIHtcclxuXHRcdFx0Y29vcmRzID0gW2Nvb3Jkc107XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHR0eXBlOiAobXVsdGkgP
14775yAnTXVsdGknIDogJycpICsgJ1BvbHlnb24nLFxyXG5cdFx0XHRjb29yZGluYXRlczogY29vcmRzXHJcblx0XHR9KTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBuYW1lc3BhY2UgTGF5ZXJHcm91cFxyXG5MYXllckdyb3VwLmluY2x1ZGUoe1xyXG5cdHRvTXVsdGlQb2ludDogZnVuY3Rpb24gKHByZWNpc2lvbikge1xyXG5cdFx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRcdHRoaXMuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0XHRjb29yZHMucHVzaChsYXllci50b0dlb0pTT04ocHJlY2lzaW9uKS5nZW9tZXRyeS5jb29yZGluYXRlcyk7XHJcblx0XHR9KTtcclxuXHJcblx0XHRyZXR1cm4gZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHRcdHR5cGU6ICdNdWx0aVBvaW50JyxcclxuXHRcdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0dlb0pTT04ocHJlY2lzaW9uPzogTnVtYmVyKTogT2JqZWN0XHJcblx0Ly8gYHByZWNpc2lvbmAgaXMgdGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyBmb3IgY29vcmRpbmF0ZXMuXHJcblx0Ly8gVGhlIGRlZmF1bHQgdmFsdWUgaXMgNiBwbGFjZXMuXHJcblx0Ly8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGxheWVyIGdyb3VwIChhcyBhIEdlb0pTT04gYEZlYXR1cmVDb2xsZWN0aW9uYCwgYEdlb21ldHJ5Q29sbGVjdGlvbmAsIG9yIGBNdWx0aVBvaW50YCkuXHJcblx0dG9HZW9KU09OOiBmdW5jdGlvbiAocHJlY2lzaW9uKSB7XHJcblxyXG5cdFx0dmFyIHR5cGUgPSB0aGlzLmZlYXR1cmUgJiYgdGhpcy5mZWF0dXJlLmdlb21ldHJ5ICYmIHRoaXMuZmVhdHVyZS5nZW9tZXRyeS50eXBlO1xyXG5cclxuXHRcdGlmICh0eXBlID09PSAnTXVsdGlQb2ludCcpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMudG9NdWx0aVBvaW50KHByZWNpc2lvbik7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGlzR2VvbWV0cnlDb2xsZWN0aW9uID0gdHlwZSA9PT0gJ0dlb21ldHJ5Q29sbGVjdGlvbicsXHJcblx0XHQgICAganNvbnMgPSBbXTtcclxuXHJcblx0XHR0aGlzLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdFx0aWYgKGxheWVyLnRvR2VvSlNPTikge1xyXG5cdFx0XHRcdHZhciBqc29uID0gbGF5ZXIudG9HZW9KU09OKHByZWNpc2lvbik7XHJcblx0XHRcdFx0aWYgKGlzR2VvbWV0cnlDb2xsZWN0aW9uKSB7XHJcblx0XHRcdFx0XHRqc29ucy5wdXNoKGpzb24uZ2VvbWV0cnkpO1xyXG5cdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHR2YXIgZmVhdHVyZSA9IGFzRmVhdHVyZShqc29uKTtcclxuXHRcdFx0XHRcdC8vIFNxdWFzaCBuZXN0ZWQgZmVhdHVyZSBjb2xsZWN0aW9uc1xyXG5cdFx0XHRcdFx0aWYgKGZlYXR1cmUudHlwZSA9PT0gJ0ZlYXR1cmVDb2xsZWN0aW9uJykge1xyXG5cdFx0XHRcdFx0XHRqc29ucy5wdXNoLmFwcGx5KGpzb25zLCBmZWF0dXJlLmZlYXR1cmVzKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdGpzb25zLnB1c2goZmVhdHVyZSk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHRpZiAoaXNHZW9tZXRyeUNvbGxlY3Rpb24pIHtcclxuXHRcdFx0cmV0dXJuIGdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHRcdGdlb21ldHJpZXM6IGpzb25zLFxyXG5cdFx0XHRcdHR5cGU6ICdHZW9tZXRyeUNvbGxlY3Rpb24nXHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB7XHJcblx0XHRcdHR5cGU6ICdGZWF0dXJlQ29sbGVjdGlvbicsXHJcblx0XHRcdGZlYXR1cmVzOiBqc29uc1xyXG5cdFx0fTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBHZW9KU09OXHJcbi8vIEBmYWN0b3J5IEwuZ2VvSlNPTihnZW9qc29uPzogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGEgR2VvSlNPTiBsYXllci4gT3B0aW9uYWxseSBhY2NlcHRzIGFuIG9iamVjdCBpblxyXG4vLyBbR2VvSlNPTiBmb3JtYXRdKGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9yZmM3OTQ2KSB0byBkaXNwbGF5IG9uIHRoZSBtYXBcclxuLy8gKHlvdSBjYW4gYWx0ZXJuYXRpdmVseSBhZGQgaXQgbGF0ZXIgd2l0aCBgYWRkRGF0YWAgbWV0aG9kKSBhbmQgYW4gYG9wdGlvbnNgIG9iamVjdC5cclxuZnVuY3Rpb24gZ2VvSlNPTihnZW9qc29uLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBHZW9KU09OKGdlb2pzb24sIG9wdGlvbnMpO1xyXG59XHJcblxyXG4vLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5LlxyXG52YXIgZ2VvSnNvbiA9IGdlb0pTT047XG5cbi8qXHJcbiAqIEBjbGFzcyBJbWFnZU92ZXJsYXlcclxuICogQGFrYSBMLkltYWdlT3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcclxuICpcclxuICogVXNlZCB0byBsb2FkIGFuZCBkaXNwbGF5IGEgc2luZ2xlIGltYWdlIG92ZXIgc3BlY2lmaWMgYm91bmRzIG9mIHRoZSBtYXAuIEV4dGVuZHMgYExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIGltYWdlVXJsID0gJ2h0dHA6Ly93d3cubGliLnV0ZXhhcy5lZHUvbWFwcy9oaXN0b3JpY2FsL25ld2Fya19ual8xOTIyLmpwZycsXHJcbiAqIFx0aW1hZ2VCb3VuZHMgPSBbWzQwLjcxMjIxNiwgLTc0LjIyNjU1XSwgWzQwLjc3Mzk0MSwgLTc0LjEyNTQ0XV07XHJcbiAqIEwuaW1hZ2VPdmVybGF5KGltYWdlVXJsLCBpbWFnZUJvdW5kcykuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxudmFyIEltYWdlT3ZlcmxheSA9IExheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBJbWFnZU92ZXJsYXkgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgaW1hZ2Ugb3ZlcmxheS5cclxuXHRcdG9wYWNpdHk6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhbHQ6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBUZXh0IGZvciB0aGUgYGFsdGAgYXR0cmlidXRlIG9mIHRoZSBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBpbnRlcmFjdGl2ZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBpbWFnZSBvdmVybGF5IHdpbGwgZW1pdCBbbW91c2UgZXZlbnRzXSgjaW50ZXJhY3RpdmUtbGF5ZXIpIHdoZW4gY2xpY2tlZCBvciBob3ZlcmVkLlxyXG5cdFx0aW50ZXJhY3RpdmU6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY3Jvc3NPcmlnaW46IEJvb2xlYW58U3RyaW5nID0gZmFsc2VcclxuXHRcdC8vIFdoZXRoZXIgdGhlIGNyb3NzT3JpZ2luIGF0dHJpYnV0ZSB3aWxsIGJlIGFkZGVkIHRvIHRoZSBpbWFnZS5cclxuXHRcdC8vIElmIGEgU3RyaW5nIGlzIHByb3ZpZGVkLCB0aGUgaW1hZ2Ugd2lsbCB
14775oYXZlIGl0cyBjcm9zc09yaWdpbiBhdHRyaWJ1dGUgc2V0IHRvIHRoZSBTdHJpbmcgcHJvdmlkZWQuIFRoaXMgaXMgbmVlZGVkIGlmIHlvdSB3YW50IHRvIGFjY2VzcyBpbWFnZSBwaXhlbCBkYXRhLlxyXG5cdFx0Ly8gUmVmZXIgdG8gW0NPUlMgU2V0dGluZ3NdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUTUwvQ09SU19zZXR0aW5nc19hdHRyaWJ1dGVzKSBmb3IgdmFsaWQgU3RyaW5nIHZhbHVlcy5cclxuXHRcdGNyb3NzT3JpZ2luOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGVycm9yT3ZlcmxheVVybDogU3RyaW5nID0gJydcclxuXHRcdC8vIFVSTCB0byB0aGUgb3ZlcmxheSBpbWFnZSB0byBzaG93IGluIHBsYWNlIG9mIHRoZSBvdmVybGF5IHRoYXQgZmFpbGVkIHRvIGxvYWQuXHJcblx0XHRlcnJvck92ZXJsYXlVcmw6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gekluZGV4OiBOdW1iZXIgPSAxXHJcblx0XHQvLyBUaGUgZXhwbGljaXQgW3pJbmRleF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQ1NTL0NTU19Qb3NpdGlvbmluZy9VbmRlcnN0YW5kaW5nX3pfaW5kZXgpIG9mIHRoZSBvdmVybGF5IGxheWVyLlxyXG5cdFx0ekluZGV4OiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQSBjdXN0b20gY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIGltYWdlLiBFbXB0eSBieSBkZWZhdWx0LlxyXG5cdFx0Y2xhc3NOYW1lOiAnJ1xyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIGJvdW5kcywgb3B0aW9ucykgeyAvLyAoU3RyaW5nLCBMYXRMbmdCb3VuZHMsIE9iamVjdClcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHRcdHRoaXMuX2JvdW5kcyA9IHRvTGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0c2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl9pbml0SW1hZ2UoKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMub3BhY2l0eSA8IDEpIHtcclxuXHRcdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7XHJcblx0XHRcdGFkZENsYXNzKHRoaXMuX2ltYWdlLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xyXG5cdFx0XHR0aGlzLmFkZEludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9pbWFnZSk7XHJcblx0XHR0aGlzLl9yZXNldCgpO1xyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZW1vdmUodGhpcy5faW1hZ2UpO1xyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGh
14775pc1xyXG5cdC8vIFNldHMgdGhlIG9wYWNpdHkgb2YgdGhlIG92ZXJsYXkuXHJcblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcclxuXHRcdHRoaXMub3B0aW9ucy5vcGFjaXR5ID0gb3BhY2l0eTtcclxuXHJcblx0XHRpZiAodGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZU9wdHMpIHtcclxuXHRcdGlmIChzdHlsZU9wdHMub3BhY2l0eSkge1xyXG5cdFx0XHR0aGlzLnNldE9wYWNpdHkoc3R5bGVPcHRzLm9wYWNpdHkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGUgbGF5ZXIgdG8gdGhlIHRvcCBvZiBhbGwgb3ZlcmxheXMuXHJcblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRvRnJvbnQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjaygpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCBvdmVybGF5cy5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0b0JhY2sodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRVcmwodXJsOiBTdHJpbmcpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgVVJMIG9mIHRoZSBpbWFnZS5cclxuXHRzZXRVcmw6IGZ1bmN0aW9uICh1cmwpIHtcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHJcblx0XHRpZiAodGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5faW1hZ2Uuc3JjID0gdXJsO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMpOiB0aGlzXHJcblx0Ly8gVXBkYXRlIHRoZSBib3VuZHMgdGhhdCB0aGlzIEltYWdlT3ZlcmxheSBjb3ZlcnNcclxuXHRzZXRCb3VuZHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdHRoaXMuX2JvdW5kcyA9IHRvTGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl9yZXNldCgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0ge1xyXG5cdFx0XHR6b29tOiB0aGlzLl9yZXNldCxcclxuXHRcdFx0dmlld3Jlc2V0OiB0aGlzLl9yZXNldFxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX2FuaW1hdGVab29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBldmVudHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRaSW5kZXgodmFsdWU6IE51bWJlcik6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjaW1hZ2VvdmVybGF5LXppbmRleCkgb2YgdGhlIGltYWdlIG92ZXJsYXkuXHJcblx0c2V0WkluZGV4OiBmdW5jdGlvbiAodmFsdWUpIHtcclxuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB2YWx1ZTtcclxuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gR2V0IHRoZSBib3VuZHMgdGhhdCB0aGlzIEltYWdlT3ZlcmxheSBjb3ZlcnNcclxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ib3VuZHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRFbGVtZW50KCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyB0aGUgaW5zdGFuY2Ugb2YgW2BIVE1MSW1hZ2VFbGVtZW50YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0hUTUxJbWFnZUVsZW1lbnQpXHJcblx0Ly8gdXNlZCBieSB0aGlzIG92ZXJsYXkuXHJcblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2ltYWdlO1xyXG5cdH0sXHJcblxyXG5cdF9pbml0SW1hZ2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciB3YXNFbGVtZW50U3VwcGxpZWQgPSB0aGlzLl91cmwudGFnTmFtZSA9PT0gJ0lNRyc7XHJcblx0XHR2YXIgaW1nID0gdGhpcy5faW1hZ2UgPSB3YXNFbGVtZW50U3VwcGxpZWQgPyB0aGlzLl91cmwgOiBjcmVhdGUkMSgnaW1nJyk7XHJcblxyXG5cdFx0YWRkQ2xhc3MoaW1nLCAnbGVhZmxldC1pbWFnZS1sYXllcicpO1xyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkgeyBhZGRDbGFzcyhpbWcsICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTsgfVxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUpIHsgYWRkQ2xhc3MoaW1nLCB0aGlzLm9wdGlvbnMuY2xhc3NOYW1lKTsgfVxyXG5cclxuXHRcdGltZy5vbnNlbGVjdHN0YXJ0ID0gZmFsc2VGbjtcclxuXHRcdGltZy5vbm1vdXNlbW92ZSA9IGZhbHNlRm47XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBJbWFnZU92ZXJsYXkgbGF5ZXIgaGFzIGxvYWRlZCBpdHMgaW1hZ2VcclxuXHRcdGltZy5vbmxvYWQgPSBiaW5kKHRoaXMuZmlyZSwgdGhpcywgJ2xvYWQnKTtcclxuXHRcdGltZy5vbmVycm9yID0gYmluZCh0aGlzLl9vdmVybGF5T25FcnJvciwgdGhpcywgJ2Vycm9yJyk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbiB8fCB0aGlzLm9wdGlvbnMuY3Jvc3NPcmlnaW4gPT09ICcnKSB7XHJcblx0XHRcdGltZy5jcm9zc09yaWdpbiA9IHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbiA9PT0gdHJ1ZSA/ICcnIDogdGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuekluZGV4KSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh3YXNFbGVtZW50U3VwcGxpZWQpIHtcclxuXHRcdFx0dGhpcy5fdXJsID0gaW1nLnNyYztcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdGltZy5zcmMgPSB0aGlzLl91cmw7XHJcblx0XHRpbWcuYWx0ID0gdGhpcy5vcHRpb25zLmFsdDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgc2NhbGUgPSB0aGlzLl9tYXAuZ2V0Wm9vbVNjYWxlKGUuem9vbSksXHJcblx0XHQgICAgb2Zmc2V0ID0gdGhpcy5fbWFwLl9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzKHRoaXMuX2JvdW5kcywgZS56b29tLCBlLmNlbnRlcikubWluO1xyXG5cclxuXHRcdHNldFRyYW5zZm9ybSh0aGlzLl9pbWFnZSwgb2Zmc2V0LCBzY2FsZSk7XHJcblx0fSxcclxuXHJcblx0X3Jlc2V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW1hZ2UgPSB0aGlzLl9pbWFnZSxcclxuXHRcdCAgICBib3VuZHMgPSBuZXcgQm91bmRzKFxyXG5cdFx0ICAgICAgICB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2JvdW5kcy5nZXROb3J0aFdlc3QoKSksXHJcblx0XHQgICAgICAgI
14775HRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fYm91bmRzLmdldFNvdXRoRWFzdCgpKSksXHJcblx0XHQgICAgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XHJcblxyXG5cdFx0c2V0UG9zaXRpb24oaW1hZ2UsIGJvdW5kcy5taW4pO1xyXG5cclxuXHRcdGltYWdlLnN0eWxlLndpZHRoICA9IHNpemUueCArICdweCc7XHJcblx0XHRpbWFnZS5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVPcGFjaXR5OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRzZXRPcGFjaXR5KHRoaXMuX2ltYWdlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVpJbmRleDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX2ltYWdlICYmIHRoaXMub3B0aW9ucy56SW5kZXggIT09IHVuZGVmaW5lZCAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSBudWxsKSB7XHJcblx0XHRcdHRoaXMuX2ltYWdlLnN0eWxlLnpJbmRleCA9IHRoaXMub3B0aW9ucy56SW5kZXg7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X292ZXJsYXlPbkVycm9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBAZXZlbnQgZXJyb3I6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBJbWFnZU92ZXJsYXkgbGF5ZXIgZmFpbHMgdG8gbG9hZCBpdHMgaW1hZ2VcclxuXHRcdHRoaXMuZmlyZSgnZXJyb3InKTtcclxuXHJcblx0XHR2YXIgZXJyb3JVcmwgPSB0aGlzLm9wdGlvbnMuZXJyb3JPdmVybGF5VXJsO1xyXG5cdFx0aWYgKGVycm9yVXJsICYmIHRoaXMuX3VybCAhPT0gZXJyb3JVcmwpIHtcclxuXHRcdFx0dGhpcy5fdXJsID0gZXJyb3JVcmw7XHJcblx0XHRcdHRoaXMuX2ltYWdlLnNyYyA9IGVycm9yVXJsO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAZmFjdG9yeSBMLmltYWdlT3ZlcmxheShpbWFnZVVybDogU3RyaW5nLCBib3VuZHM6IExhdExuZ0JvdW5kcywgb3B0aW9ucz86IEltYWdlT3ZlcmxheSBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYW4gaW1hZ2Ugb3ZlcmxheSBvYmplY3QgZ2l2ZW4gdGhlIFVSTCBvZiB0aGUgaW1hZ2UgYW5kIHRoZVxyXG4vLyBnZW9ncmFwaGljYWwgYm91bmRzIGl0IGlzIHRpZWQgdG8uXHJcbnZhciBpbWFnZU92ZXJsYXkgPSBmdW5jdGlvbiAodXJsLCBib3VuZHMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEltYWdlT3ZlcmxheSh1cmwsIGJvdW5kcywgb3B0aW9ucyk7XHJcbn07XG5cbi8qXHJcbiAqIEBjbGFzcyBWaWRlb092ZXJsYXlcclxuICogQGFrYSBMLlZpZGVvT3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgSW1hZ2VPdmVybGF5XHJcbiAqXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSBhIHZpZGVvIHBsYXllciBvdmVyIHNwZWNpZmljIGJvdW5kcyBvZiB0aGUgbWFwLiBFeHRlbmRzIGBJbWFnZU92ZXJsYXlgLlxyXG4gKlxyXG4gKiBBIHZpZGVvIG92ZXJsYXkgdXNlcyB0aGUgW2A8dmlkZW8+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSFRNTC9FbGVtZW50L3ZpZGVvKVxyXG4gKiBIVE1MNSBlbGVtZW50LlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgdmlkZW9VcmwgPSAnaHR0cHM6Ly93d3cubWFwYm94LmNvbS9iaXRlcy8wMDE4OC9wYXRyaWNpYV9uYXNhLndlYm0nLFxyXG4gKiBcdHZpZGVvQm91bmRzID0gW1sgMzIsIC0xMzBdLCBbIDEzLCAtMTAwXV07XHJcbiAqIEwudmlkZW9PdmVybGF5KHZpZGVvVXJsLCB2aWRlb0JvdW5kcyApLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbnZhciBWaWRlb092ZXJsYXkgPSBJbWFnZU92ZXJsYXkuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFZpZGVvT3ZlcmxheSBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvcGxheTogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIHZpZGVvIHN0YXJ0cyBwbGF5aW5nIGF1dG9tYXRpY2FsbHkgd2hlbiBsb2FkZWQuXHJcblx0XHRhdXRvcGxheTogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGxvb3A6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSB2aWRlbyB3aWxsIGxvb3AgYmFjayB0byB0aGUgYmVnaW5uaW5nIHdoZW4gcGxheWVkLlxyXG5cdFx0bG9vcDogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtlZXBBc3BlY3RSYXRpbzogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIHZpZGVvIHdpbGwgc2F2ZSBhc3BlY3QgcmF0aW8gYWZ0ZXIgdGhlIHByb2plY3Rpb24uXHJcblx0XHQvLyBSZWxldmFudCBmb3Igc3VwcG9ydGVkIGJyb3dzZXJzLiBCcm93c2VyIGNvbXBhdGliaWxpdHktIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9vYmplY3QtZml0XHJcblx0XHRrZWVwQXNwZWN0UmF0aW86IHRydWVcclxuXHR9LFxyXG5cclxuXHRfaW5pdEltYWdlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgd2FzRWxlbWVudFN1cHBsaWVkID0gdGhpcy5fdXJsLnRhZ05hbWUgPT09ICdWSURFTyc7XHJcblx0XHR2YXIgdmlkID0gdGhpcy5faW1hZ2UgPSB3YXNFbGVtZW50U3VwcGxpZWQgPyB0aGlzLl91cmwgOiBjcmVhdGUkMSgndmlkZW8nKTtcclxuXHJcblx0XHRhZGRDbGFzcyh2aWQsICdsZWFmbGV0LWltYWdlLWxheWVyJyk7XHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7IGFkZENsYXNzKHZpZCwgJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpOyB9XHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSkgeyBhZGRDbGFzcyh2aWQsIHRoaXMub3B0aW9ucy5jbGFzc05hbWUpOyB9XHJcblxyXG5cdFx0dmlkLm9uc2VsZWN0c3RhcnQgPSBmYWxzZUZuO1xyXG5cdFx0dmlkLm9ubW91c2Vtb3ZlID0gZmFsc2VGbjtcclxuXHJcblx0XHQvLyBAZXZlbnQgbG9hZDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHZpZGVvIGhhcyBmaW5pc2hlZCBsb2FkaW5nIHRoZSBmaXJzdCBmcmFtZVxyXG5cdFx0dmlkLm9ubG9hZGVkZGF0YSA9IGJpbmQodGhpcy5maXJlLCB0aGlzLCAnbG9hZCcpO1xyXG5cclxuXHRcdGlmICh3YXNFbGVtZW50U3VwcGxpZWQpIHtcclxuXHRcdFx0dmFyIHNvdXJjZUVsZW1lbnRzID0gdmlkLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzb3VyY2UnKTtcclxuXHRcdFx0dmFyIHNvdXJjZXMgPSBbXTtcclxuXHRcdFx0Zm9yICh2YXIgaiA9IDA7IGogPCBzb3VyY2VFbGVtZW50cy5sZW5ndGg7IGorKykge1xyXG5cdFx0XHRcdHNvdXJjZXMucHVzaChzb3VyY2VFbGVtZW50c1tqXS5zcmMpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLl91cmwgPSAoc291cmNlRWxlbWVudHMubGVuZ3RoID4gMCkgPyBzb3VyY2VzIDogW3ZpZC5zcmNdO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFpc0FycmF
147755KHRoaXMuX3VybCkpIHsgdGhpcy5fdXJsID0gW3RoaXMuX3VybF07IH1cclxuXHJcblx0XHRpZiAoIXRoaXMub3B0aW9ucy5rZWVwQXNwZWN0UmF0aW8gJiYgdmlkLnN0eWxlLmhhc093blByb3BlcnR5KCdvYmplY3RGaXQnKSkgeyB2aWQuc3R5bGVbJ29iamVjdEZpdCddID0gJ2ZpbGwnOyB9XHJcblx0XHR2aWQuYXV0b3BsYXkgPSAhIXRoaXMub3B0aW9ucy5hdXRvcGxheTtcclxuXHRcdHZpZC5sb29wID0gISF0aGlzLm9wdGlvbnMubG9vcDtcclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5fdXJsLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHZhciBzb3VyY2UgPSBjcmVhdGUkMSgnc291cmNlJyk7XHJcblx0XHRcdHNvdXJjZS5zcmMgPSB0aGlzLl91cmxbaV07XHJcblx0XHRcdHZpZC5hcHBlbmRDaGlsZChzb3VyY2UpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRFbGVtZW50KCk6IEhUTUxWaWRlb0VsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBpbnN0YW5jZSBvZiBbYEhUTUxWaWRlb0VsZW1lbnRgXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvSFRNTFZpZGVvRWxlbWVudClcclxuXHQvLyB1c2VkIGJ5IHRoaXMgb3ZlcmxheS5cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC52aWRlb092ZXJsYXkodmlkZW86IFN0cmluZ3xBcnJheXxIVE1MVmlkZW9FbGVtZW50LCBib3VuZHM6IExhdExuZ0JvdW5kcywgb3B0aW9ucz86IFZpZGVvT3ZlcmxheSBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYW4gaW1hZ2Ugb3ZlcmxheSBvYmplY3QgZ2l2ZW4gdGhlIFVSTCBvZiB0aGUgdmlkZW8gKG9yIGFycmF5IG9mIFVSTHMsIG9yIGV2ZW4gYSB2aWRlbyBlbGVtZW50KSBhbmQgdGhlXHJcbi8vIGdlb2dyYXBoaWNhbCBib3VuZHMgaXQgaXMgdGllZCB0by5cclxuXHJcbmZ1bmN0aW9uIHZpZGVvT3ZlcmxheSh2aWRlbywgYm91bmRzLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBWaWRlb092ZXJsYXkodmlkZW8sIGJvdW5kcywgb3B0aW9ucyk7XHJcbn1cblxuLypcbiAqIEBjbGFzcyBTVkdPdmVybGF5XG4gKiBAYWthIEwuU1ZHT3ZlcmxheVxuICogQGluaGVyaXRzIEltYWdlT3ZlcmxheVxuICpcbiAqIFVzZWQgdG8gbG9hZCwgZGlzcGxheSBhbmQgcHJvdmlkZSBET00gYWNjZXNzIHRvIGFuIFNWRyBmaWxlIG92ZXIgc3BlY2lmaWMgYm91bmRzIG9mIHRoZSBtYXAuIEV4dGVuZHMgYEltYWdlT3ZlcmxheWAuXG4gKlxuICogQW4gU1ZHIG92ZXJsYXkgdXNlcyB0aGUgW2A8c3ZnPmBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRy9FbGVtZW50L3N2ZykgZWxlbWVudC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiB2YXIgc3ZnRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhcImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIsIFwic3ZnXCIpO1xuICogc3ZnRWxlbWVudC5zZXRBdHRyaWJ1dGUoJ3htbG5zJywgXCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiKTtcbiAqIHN2Z0VsZW1lbnQuc2V0QXR0cmlidXRlKCd2aWV3Qm94JywgXCIwIDAgMjAwIDIwMFwiKTtcbiAqIHN2Z0VsZW1lbnQuaW5uZXJIVE1MID0gJzxyZWN0IHdpZHRoPVwiMjAwXCIgaGVpZ2h0PVwiMjAwXCIvPjxyZWN0IHg9XCI3NVwiIHk9XCIyM1wiIHdpZHRoPVwiNTBcIiBoZWlnaHQ9XCI1MFwiIHN0eWxlPVwiZmlsbDpyZWRcIi8+PHJlY3QgeD1cIjc1XCIgeT1cIjEyM1wiIHdpZHRoPVwiNTBcIiBoZWlnaHQ9XCI1MFwiIHN0eWxlPVwiZmlsbDojMDAxM2ZmXCIvPic7XG4gKiB2YXIgc3ZnRWxlbWVudEJvdW5kcyA9IFsgWyAzMiwgLTEzMCBdLCBbIDEzLCAtMTAwIF0gXTtcbiAqIEwuc3ZnT3ZlcmxheShzdmdFbGVtZW50LCBzdmdFbGVtZW50Qm91bmRzKS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxudmFyIFNWR092ZXJsYXkgPSBJbWFnZU92ZXJsYXkuZXh0ZW5kKHtcblx0X2luaXRJbWFnZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBlbCA9IHRoaXMuX2ltYWdlID0gdGhpcy5fdXJsO1xuXG5cdFx0YWRkQ2xhc3MoZWwsICdsZWFmbGV0LWltYWdlLWxheWVyJyk7XG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkgeyBhZGRDbGFzcyhlbCwgJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpOyB9XG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUpIHsgYWRkQ2xhc3MoZWwsIHRoaXMub3B0aW9ucy5jbGFzc05hbWUpOyB9XG5cblx0XHRlbC5vbnNlbGVjdHN0YXJ0ID0gZmFsc2VGbjtcblx0XHRlbC5vbm1vdXNlbW92ZSA9IGZhbHNlRm47XG5cdH1cblxuXHQvLyBAbWV0aG9kIGdldEVsZW1lbnQoKTogU1ZHRWxlbWVudFxuXHQvLyBSZXR1cm5zIHRoZSBpbnN0YW5jZSBvZiBbYFNWR0VsZW1lbnRgXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvU1ZHRWxlbWVudClcblx0Ly8gdXNlZCBieSB0aGlzIG92ZXJsYXkuXG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLnN2Z092ZXJsYXkoc3ZnOiBTdHJpbmd8U1ZHRWxlbWVudCwgYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBTVkdPdmVybGF5IG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYW4gaW1hZ2Ugb3ZlcmxheSBvYmplY3QgZ2l2ZW4gYW4gU1ZHIGVsZW1lbnQgYW5kIHRoZSBnZW9ncmFwaGljYWwgYm91bmRzIGl0IGlzIHRpZWQgdG8uXG4vLyBBIHZpZXdCb3ggYXR0cmlidXRlIGlzIHJlcXVpcmVkIG9uIHRoZSBTVkcgZWxlbWVudCB0byB6b29tIGluIGFuZCBvdXQgcHJvcGVybHkuXG5cbmZ1bmN0aW9uIHN2Z092ZXJsYXkoZWwsIGJvdW5kcywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IFNWR092ZXJsYXkoZWwsIGJvdW5kcywgb3B0aW9ucyk7XG59XG5cbi8qXHJcbiAqIEBjbGFzcyBEaXZPdmVybGF5XHJcbiAqIEBpbmhlcml0cyBMYXllclxyXG4gKiBAYWthIEwuRGl2T3ZlcmxheVxyXG4gKiBCYXNlIG1vZGVsIGZvciBMLlBvcHVwIGFuZCBMLlRvb2x0aXAuIEluaGVyaXQgZnJvbSBpdCBmb3IgY3VzdG9tIHBvcHVwIGxpa2UgcGx1Z2lucy5cclxuICovXHJcblxyXG4vLyBAbmFtZXNwYWNlIERpdk92ZXJsYXlcclxudmFyIERpdk92ZXJsYXkgPSBMYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgRGl2T3ZlcmxheSBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBvZmZzZXQ6IFBvaW50ID0gUG9pbnQoMCwgNylcclxuXHRcdC8vIFRoZSBvZmZzZXQgb2YgdGhlIHBvcHVwIHBvc2l0aW9uLiBVc2VmdWwgdG8gY29udHJvbCB0aGUgYW5jaG9yXHJcblx0XHQvLyBvZiB0aGUgcG9wdXAgd2hlbiBvcGVuaW5nIGl0IG9uIHNvbWUgb3ZlcmxheXMuXHJcblx0XHRvZmZzZXQ6IFswLCA3XSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcclxuXHRcdC8vIEEgY3VzdG9tIENTUyBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgcG9wdXAuXHJcblx0XHRjbGFzc05hbWU6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcGFuZTogU3RyaW5nID0gJ3BvcHVwUGFuZSdcclxuXHRcdC8vIGBNYXAgcGFuZWAgd2hlcmUgdGhlIHBvcHVwIHdpbGwgYmUgYWRkZWQuXHJcblx0XHRwYW5lOiAncG9wdXBQYW5lJ
147751xyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zLCBzb3VyY2UpIHtcclxuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fc291cmNlID0gc291cmNlO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBtYXAuX3pvb21BbmltYXRlZDtcclxuXHJcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikge1xyXG5cdFx0XHR0aGlzLl9pbml0TGF5b3V0KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdHNldE9wYWNpdHkodGhpcy5fY29udGFpbmVyLCAwKTtcclxuXHRcdH1cclxuXHJcblx0XHRjbGVhclRpbWVvdXQodGhpcy5fcmVtb3ZlVGltZW91dCk7XHJcblx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9jb250YWluZXIpO1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHJcblx0XHRpZiAobWFwLl9mYWRlQW5pbWF0ZWQpIHtcclxuXHRcdFx0c2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDEpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuYnJpbmdUb0Zyb250KCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRzZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMCk7XHJcblx0XHRcdHRoaXMuX3JlbW92ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KGJpbmQocmVtb3ZlLCB1bmRlZmluZWQsIHRoaXMuX2NvbnRhaW5lciksIDIwMCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRyZW1vdmUodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbmFtZXNwYWNlIFBvcHVwXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmc6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBwb2ludCBvZiBwb3B1cC5cclxuXHRnZXRMYXRMbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9sYXRsbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgZ2VvZ3JhcGhpY2FsIHBvaW50IHdoZXJlIHRoZSBwb3B1cCB3aWxsIG9wZW4uXHJcblx0c2V0TGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR0aGlzLl9sYXRsbmcgPSB0b0xhdExuZyhsYXRsbmcpO1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVQb3NpdGlvbigpO1xyXG5cdFx0XHR0aGlzLl9hZGp1c3RQYW4oKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY29udGVudCBvZiB0aGUgcG9wdXAuXHJcblx0Z2V0Q29udGVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRlbnQ7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRDb250ZW50KGh0bWxDb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8RnVuY3Rpb24pOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgSFRNTCBjb250ZW50IG9mIHRoZSBwb3B1cC4gSWYgYSBmdW5jdGlvbiBpcyBwYXNzZWQgdGhlIHNvdXJjZSBsYXllciB3aWxsIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uIFRoZSBmdW5jdGlvbiBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YCB0byBiZSB1c2VkIGluIHRoZSBwb3B1cC5cclxuXHRzZXRDb250ZW50OiBmdW5jdGlvbiAoY29udGVudCkge1xyXG5cdFx0dGhpcy5fY29udGVudCA9IGNvbnRlbnQ7XHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRFbGVtZW50OiBTdHJpbmd8SFRNTEVsZW1lbnRcclxuXHQvLyBBbGlhcyBmb3IgW2dldENvbnRlbnQoKV0oI3BvcHVwLWdldGNvbnRlbnQpXHJcblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVwZGF0ZTogbnVsbFxyXG5cdC8vIFVwZGF0ZXMgdGhlIHBvcHVwIGNvbnRlbnQsIGxheW91dCBhbmQgcG9zaXRpb24uIFVzZWZ1bCBmb3IgdXBkYXRpbmcgdGhlIHBvcHVwIGFmdGVyIHNvbWV0aGluZyBpbnNpZGUgY2hhbmdlZCwgZS5nLiBpbWFnZSBsb2FkZWQuXHJcblx0dXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUudmlzaWJpbGl0eSA9ICdoaWRkZW4nO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZUNvbnRlbnQoKTtcclxuXHRcdHRoaXMuX3VwZGF0ZUxheW91dCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlUG9zaXRpb24oKTtcclxuXHJcblx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUudmlzaWJpbGl0eSA9ICcnO1xyXG5cclxuXHRcdHRoaXMuX2FkanVzdFBhbigpO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IHtcclxuXHRcdFx0em9vbTogdGhpcy5fdXBkYXRlUG9zaXRpb24sXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fdXBkYXRlUG9zaXRpb25cclxuXHRcdH07XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBldmVudHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpc09wZW46IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCB3aGVuIHRoZSBwb3B1cCBpcyB2aXNpYmxlIG9uIHRoZSBtYXAuXHJcblx0aXNPcGVuOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gISF0aGlzLl9tYXAgJiYgdGhpcy5fbWFwLmhhc0xheWVyKHRoaXMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250OiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoaXMgcG9wdXAgaW4gZnJvbnQgb2Ygb3RoZXIgcG9wdXBzIChpbiB0aGUgc2FtZSBtYXAgcGFuZSkuXHJcblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybi
14775B0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2s6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhpcyBwb3B1cCB0byB0aGUgYmFjayBvZiBvdGhlciBwb3B1cHMgKGluIHRoZSBzYW1lIG1hcCBwYW5lKS5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0b0JhY2sodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9wcmVwYXJlT3BlbjogZnVuY3Rpb24gKHBhcmVudCwgbGF5ZXIsIGxhdGxuZykge1xyXG5cdFx0aWYgKCEobGF5ZXIgaW5zdGFuY2VvZiBMYXllcikpIHtcclxuXHRcdFx0bGF0bG5nID0gbGF5ZXI7XHJcblx0XHRcdGxheWVyID0gcGFyZW50O1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChsYXllciBpbnN0YW5jZW9mIEZlYXR1cmVHcm91cCkge1xyXG5cdFx0XHRmb3IgKHZhciBpZCBpbiBwYXJlbnQuX2xheWVycykge1xyXG5cdFx0XHRcdGxheWVyID0gcGFyZW50Ll9sYXllcnNbaWRdO1xyXG5cdFx0XHRcdGJyZWFrO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFsYXRsbmcpIHtcclxuXHRcdFx0aWYgKGxheWVyLmdldENlbnRlcikge1xyXG5cdFx0XHRcdGxhdGxuZyA9IGxheWVyLmdldENlbnRlcigpO1xyXG5cdFx0XHR9IGVsc2UgaWYgKGxheWVyLmdldExhdExuZykge1xyXG5cdFx0XHRcdGxhdGxuZyA9IGxheWVyLmdldExhdExuZygpO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIGdldCBzb3VyY2UgbGF5ZXIgTGF0TG5nLicpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gc2V0IG92ZXJsYXkgc291cmNlIHRvIHRoaXMgbGF5ZXJcclxuXHRcdHRoaXMuX3NvdXJjZSA9IGxheWVyO1xyXG5cclxuXHRcdC8vIHVwZGF0ZSB0aGUgb3ZlcmxheSAoY29udGVudCwgbGF5b3V0LCBlY3QuLi4pXHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cclxuXHRcdHJldHVybiBsYXRsbmc7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZUNvbnRlbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fY29udGVudCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgbm9kZSA9IHRoaXMuX2NvbnRlbnROb2RlO1xyXG5cdFx0dmFyIGNvbnRlbnQgPSAodHlwZW9mIHRoaXMuX2NvbnRlbnQgPT09ICdmdW5jdGlvbicpID8gdGhpcy5fY29udGVudCh0aGlzLl9zb3VyY2UgfHwgdGhpcykgOiB0aGlzLl9jb250ZW50O1xyXG5cclxuXHRcdGlmICh0eXBlb2YgY29udGVudCA9PT0gJ3N0cmluZycpIHtcclxuXHRcdFx0bm9kZS5pbm5lckhUTUwgPSBjb250ZW50O1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0d2hpbGUgKG5vZGUuaGFzQ2hpbGROb2RlcygpKSB7XHJcblx0XHRcdFx0bm9kZS5yZW1vdmVDaGlsZChub2RlLmZpcnN0Q2hpbGQpO1xyXG5cdFx0XHR9XHJcblx0XHRcdG5vZGUuYXBwZW5kQ2hpbGQoY29udGVudCk7XHJcblx0XHR9XHJcblx0XHR0aGlzLmZpcmUoJ2NvbnRlbnR1cGRhdGUnKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlUG9zaXRpb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBwb3MgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyksXHJcblx0XHQgICAgb2Zmc2V0ID0gdG9Qb2ludCh0aGlzLm9wdGlvbnMub2Zmc2V0KSxcclxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdHNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgcG9zLmFkZChhbmNob3IpKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG9mZnNldCA9IG9mZnNldC5hZGQocG9zKS5hZGQoYW5jaG9yKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgYm90dG9tID0gdGhpcy5fY29udGFpbmVyQm90dG9tID0gLW9mZnNldC55LFxyXG5cdFx0ICAgIGxlZnQgPSB0aGlzLl9jb250YWluZXJMZWZ0ID0gLU1hdGgucm91bmQodGhpcy5fY29udGFpbmVyV2lkdGggLyAyKSArIG9mZnNldC54O1xyXG5cclxuXHRcdC8vIGJvdHRvbSBwb3NpdGlvbiB0aGUgcG9wdXAgaW4gY2FzZSB0aGUgaGVpZ2h0IG9mIHRoZSBwb3B1cCBjaGFuZ2VzIChpbWFnZXMgbG9hZGluZyBldGMpXHJcblx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUuYm90dG9tID0gYm90dG9tICsgJ3B4JztcclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS5sZWZ0ID0gbGVmdCArICdweCc7XHJcblx0fSxcclxuXHJcblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIFswLCAwXTtcclxuXHR9XHJcblxyXG59KTtcblxuLypcclxuICogQGNsYXNzIFBvcHVwXHJcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XHJcbiAqIEBha2EgTC5Qb3B1cFxyXG4gKiBVc2VkIHRvIG9wZW4gcG9wdXBzIGluIGNlcnRhaW4gcGxhY2VzIG9mIHRoZSBtYXAuIFVzZSBbTWFwLm9wZW5Qb3B1cF0oI21hcC1vcGVucG9wdXApIHRvXHJcbiAqIG9wZW4gcG9wdXBzIHdoaWxlIG1ha2luZyBzdXJlIHRoYXQgb25seSBvbmUgcG9wdXAgaXMgb3BlbiBhdCBvbmUgdGltZVxyXG4gKiAocmVjb21tZW5kZWQgZm9yIHVzYWJpbGl0eSksIG9yIHVzZSBbTWFwLmFkZExheWVyXSgjbWFwLWFkZGxheWVyKSB0byBvcGVuIGFzIG1hbnkgYXMgeW91IHdhbnQuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIElmIHlvdSB3YW50IHRvIGp1c3QgYmluZCBhIHBvcHVwIHRvIG1hcmtlciBjbGljayBhbmQgdGhlbiBvcGVuIGl0LCBpdCdzIHJlYWxseSBlYXN5OlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXJrZXIuYmluZFBvcHVwKHBvcHVwQ29udGVudCkub3BlblBvcHVwKCk7XHJcbiAqIGBgYFxyXG4gKiBQYXRoIG92ZXJsYXlzIGxpa2UgcG9seWxpbmVzIGFsc28gaGF2ZSBhIGBiaW5kUG9wdXBgIG1ldGhvZC5cclxuICogSGVyZSdzIGEgbW9yZSBjb21wbGljYXRlZCB3YXkgdG8gb3BlbiBhIHBvcHVwIG9uIGEgbWFwOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgcG9wdXAgPSBMLnBvcHVwKClcclxuICogXHQuc2V0TGF0TG5nKGxhdGxuZylcclxuICogXHQuc2V0Q29udGVudCgnPHA+SGVsbG8gd29ybGQhPGJyIC8+VGhpcyBpcyBh
14775IG5pY2UgcG9wdXAuPC9wPicpXHJcbiAqIFx0Lm9wZW5PbihtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG52YXIgUG9wdXAgPSBEaXZPdmVybGF5LmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBQb3B1cCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtYXhXaWR0aDogTnVtYmVyID0gMzAwXHJcblx0XHQvLyBNYXggd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtYXhXaWR0aDogMzAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluV2lkdGg6IE51bWJlciA9IDUwXHJcblx0XHQvLyBNaW4gd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtaW5XaWR0aDogNTAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhIZWlnaHQ6IE51bWJlciA9IG51bGxcclxuXHRcdC8vIElmIHNldCwgY3JlYXRlcyBhIHNjcm9sbGFibGUgY29udGFpbmVyIG9mIHRoZSBnaXZlbiBoZWlnaHRcclxuXHRcdC8vIGluc2lkZSBhIHBvcHVwIGlmIGl0cyBjb250ZW50IGV4Y2VlZHMgaXQuXHJcblx0XHRtYXhIZWlnaHQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IGRvbid0IHdhbnQgdGhlIG1hcCB0byBkbyBwYW5uaW5nIGFuaW1hdGlvblxyXG5cdFx0Ly8gdG8gZml0IHRoZSBvcGVuZWQgcG9wdXAuXHJcblx0XHRhdXRvUGFuOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBQb2ludCA9IG51bGxcclxuXHRcdC8vIFRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgcG9wdXAgYW5kIHRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIG1hcFxyXG5cdFx0Ly8gdmlldyBhZnRlciBhdXRvcGFubmluZyB3YXMgcGVyZm9ybWVkLlxyXG5cdFx0YXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdCb3R0b21SaWdodDogUG9pbnQgPSBudWxsXHJcblx0XHQvLyBUaGUgbWFyZ2luIGJldHdlZW4gdGhlIHBvcHVwIGFuZCB0aGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgbWFwXHJcblx0XHQvLyB2aWV3IGFmdGVyIGF1dG9wYW5uaW5nIHdhcyBwZXJmb3JtZWQuXHJcblx0XHRhdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmc6IFBvaW50ID0gUG9pbnQoNSwgNSlcclxuXHRcdC8vIEVxdWl2YWxlbnQgb2Ygc2V0dGluZyBib3RoIHRvcCBsZWZ0IGFuZCBib3R0b20gcmlnaHQgYXV0b3BhbiBwYWRkaW5nIHRvIHRoZSBzYW1lIHZhbHVlLlxyXG5cdFx0YXV0b1BhblBhZGRpbmc6IFs1LCA1XSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtlZXBJblZpZXc6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGB0cnVlYCBpZiB5b3Ugd2FudCB0byBwcmV2ZW50IHVzZXJzIGZyb20gcGFubmluZyB0aGUgcG9wdXBcclxuXHRcdC8vIG9mZiBvZiB0aGUgc2NyZWVuIHdoaWxlIGl0IGlzIG9wZW4uXHJcblx0XHRrZWVwSW5WaWV3OiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNsb3NlQnV0dG9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gQ29udHJvbHMgdGhlIHByZXNlbmNlIG9mIGEgY2xvc2UgYnV0dG9uIGluIHRoZSBwb3B1cC5cclxuXHRcdGNsb3NlQnV0dG9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b0Nsb3NlOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2ZcclxuXHRcdC8vIHRoZSBwb3B1cCBjbG9zaW5nIHdoZW4gYW5vdGhlciBwb3B1cCBpcyBvcGVuZWQuXHJcblx0XHRhdXRvQ2xvc2U6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbG9zZU9uRXNjYXBlS2V5OiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2ZcclxuXHRcdC8vIHRoZSBFU0Mga2V5IGZvciBjbG9zaW5nIG9mIHRoZSBwb3B1cC5cclxuXHRcdGNsb3NlT25Fc2NhcGVLZXk6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbG9zZU9uQ2xpY2s6IEJvb2xlYW4gPSAqXHJcblx0XHQvLyBTZXQgaXQgaWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2YgdGhlIHBvcHVwIGNsb3Npbmcgd2hlbiB1c2VyIGNsaWNrc1xyXG5cdFx0Ly8gb24gdGhlIG1hcC4gRGVmYXVsdHMgdG8gdGhlIG1hcCdzIFtgY2xvc2VQb3B1cE9uQ2xpY2tgXSgjbWFwLWNsb3NlcG9wdXBvbmNsaWNrKSBvcHRpb24uXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBBIGN1c3RvbSBDU1MgY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIHBvcHVwLlxyXG5cdFx0Y2xhc3NOYW1lOiAnJ1xyXG5cdH0sXHJcblxyXG5cdC8vIEBuYW1lc3BhY2UgUG9wdXBcclxuXHQvLyBAbWV0aG9kIG9wZW5PbihtYXA6IE1hcCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBwb3B1cCB0byB0aGUgbWFwIGFuZCBjbG9zZXMgdGhlIHByZXZpb3VzIG9uZS4gVGhlIHNhbWUgYXMgYG1hcC5vcGVuUG9wdXAocG9wdXApYC5cclxuXHRvcGVuT246IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdG1hcC5vcGVuUG9wdXAodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0RGl2T3ZlcmxheS5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBwb3B1cG9wZW46IFBvcHVwRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBwb3B1cCBpcyBvcGVuZWQgaW4gdGhlIG1hcFxyXG5cdFx0bWFwLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwb3BlbjogUG9wdXBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBvcGVuZWRcclxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30sIHRydWUpO1xyXG5cdFx0XHQvLyBGb3Igbm9uLXBhdGggbGF5ZXJzLCB3ZSB0b2dnbGUgdGhlIHBvcHVwIHdoZW4gY2xpY2tpbmdcclxuXHRcdFx0Ly8gYWdhaW4gdGhlIGxheWVyLCBzbyBwcmV2ZW50IHRoZSBtYXAgdG8gcmVvcGVuIGl0LlxyXG5cdFx0XHRpZiAoISh0aGlzLl9zb3VyY2UgaW5zdGFuY2VvZiBQYXRoKSkge1xyXG5cdFx0XHRcdHRoaXMuX3NvdXJjZS5vbigncHJlY2xpY2snLCBzdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdERpdk92ZXJsYXkucHJvdG90eXBlLm9uUmVtb3ZlLmNhbGwodGhpcywgbWFwKTtcclxuXHJcblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxyXG5cdFx0Ly8gQHNlY3Rpb24gUG9wdXAgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgcG9wdXBjbG9zZTogUG9wdXBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGluIHRoZSBtYXAgaXMgY2xvc2VkXHJcblx0XHRtYXAuZmlyZSgncG9wdXBjbG9zZScsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwY2xvc2U6IFBvcHVwRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY2xvc2VkXHJcblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCdwb3B1cGNsb3NlJywge3BvcHVwOiB0aGlzfSwgdHJ1ZSk7XHJcblx0XHRcdGlmICghKHRoaXMuX3NvdXJjZSBpbnN0YW5jZW9mIFBhdGgpKSB7XHJcblx0XHRcdFx0dGhpcy5fc291cmNlLm9mZigncHJlY2xpY2snLCBzdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0gRGl2T3ZlcmxheS5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jbG9zZU9uQ2xpY2sgIT09IHVuZGVmaW5lZCA/IHRoaXMub3B0aW9ucy5jbG9zZU9uQ2xpY2sgOiB0aGlzLl9tYXAub3B0aW9ucy5jbG9zZVBvcHVwT25DbGljaykge1xyXG5cdFx0XHRldmVudHMucHJlY2xpY2sgPSB0aGlzLl9jbG9zZTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmtlZXBJblZpZXcpIHtcclxuXHRcdFx0ZXZlbnRzLm1vdmVlbmQgPSB0aGlzLl9hZGp1c3RQYW47XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGV2ZW50cztcclxuXHR9LFxyXG5cclxuXHRfY2xvc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmNsb3NlUG9wdXAodGhpcyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBwcmVmaXggPSAnbGVhZmxldC1wb3B1cCcsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gY3JlYXRlJDEoJ2RpdicsXHJcblx0XHRcdHByZWZpeCArICcgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSArXHJcblx0XHRcdCcgbGVhZmxldC16b29tLWFuaW1hdGVkJyk7XHJcblxyXG5cdFx0dmFyIHdyYXBwZXIgPSB0aGlzLl93cmFwcGVyID0gY3JlYXRlJDEoJ2RpdicsIHByZWZpeCArICctY29udGVudC13cmFwcGVyJywgY29udGFpbmVyKTtcclxuXHRcdHRoaXMuX2NvbnRlbn
14775ROb2RlID0gY3JlYXRlJDEoJ2RpdicsIHByZWZpeCArICctY29udGVudCcsIHdyYXBwZXIpO1xyXG5cclxuXHRcdGRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHdyYXBwZXIpO1xyXG5cdFx0ZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uKHRoaXMuX2NvbnRlbnROb2RlKTtcclxuXHRcdG9uKHdyYXBwZXIsICdjb250ZXh0bWVudScsIHN0b3BQcm9wYWdhdGlvbik7XHJcblxyXG5cdFx0dGhpcy5fdGlwQ29udGFpbmVyID0gY3JlYXRlJDEoJ2RpdicsIHByZWZpeCArICctdGlwLWNvbnRhaW5lcicsIGNvbnRhaW5lcik7XHJcblx0XHR0aGlzLl90aXAgPSBjcmVhdGUkMSgnZGl2JywgcHJlZml4ICsgJy10aXAnLCB0aGlzLl90aXBDb250YWluZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuY2xvc2VCdXR0b24pIHtcclxuXHRcdFx0dmFyIGNsb3NlQnV0dG9uID0gdGhpcy5fY2xvc2VCdXR0b24gPSBjcmVhdGUkMSgnYScsIHByZWZpeCArICctY2xvc2UtYnV0dG9uJywgY29udGFpbmVyKTtcclxuXHRcdFx0Y2xvc2VCdXR0b24uaHJlZiA9ICcjY2xvc2UnO1xyXG5cdFx0XHRjbG9zZUJ1dHRvbi5pbm5lckhUTUwgPSAnJiMyMTU7JztcclxuXHJcblx0XHRcdG9uKGNsb3NlQnV0dG9uLCAnY2xpY2snLCB0aGlzLl9vbkNsb3NlQnV0dG9uQ2xpY2ssIHRoaXMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBjb250YWluZXIgPSB0aGlzLl9jb250ZW50Tm9kZSxcclxuXHRcdCAgICBzdHlsZSA9IGNvbnRhaW5lci5zdHlsZTtcclxuXHJcblx0XHRzdHlsZS53aWR0aCA9ICcnO1xyXG5cdFx0c3R5bGUud2hpdGVTcGFjZSA9ICdub3dyYXAnO1xyXG5cclxuXHRcdHZhciB3aWR0aCA9IGNvbnRhaW5lci5vZmZzZXRXaWR0aDtcclxuXHRcdHdpZHRoID0gTWF0aC5taW4od2lkdGgsIHRoaXMub3B0aW9ucy5tYXhXaWR0aCk7XHJcblx0XHR3aWR0aCA9IE1hdGgubWF4KHdpZHRoLCB0aGlzLm9wdGlvbnMubWluV2lkdGgpO1xyXG5cclxuXHRcdHN0eWxlLndpZHRoID0gKHdpZHRoICsgMSkgKyAncHgnO1xyXG5cdFx0c3R5bGUud2hpdGVTcGFjZSA9ICcnO1xyXG5cclxuXHRcdHN0eWxlLmhlaWdodCA9ICcnO1xyXG5cclxuXHRcdHZhciBoZWlnaHQgPSBjb250YWluZXIub2Zmc2V0SGVpZ2h0LFxyXG5cdFx0ICAgIG1heEhlaWdodCA9IHRoaXMub3B0aW9ucy5tYXhIZWlnaHQsXHJcblx0XHQgICAgc2Nyb2xsZWRDbGFzcyA9ICdsZWFmbGV0LXBvcHVwLXNjcm9sbGVkJztcclxuXHJcblx0XHRpZiAobWF4SGVpZ2h0ICYmIGhlaWdodCA+IG1heEhlaWdodCkge1xyXG5cdFx0XHRzdHlsZS5oZWlnaHQgPSBtYXhIZWlnaHQgKyAncHgnO1xyXG5cdFx0XHRhZGRDbGFzcyhjb250YWluZXIsIHNjcm9sbGVkQ2xhc3MpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmVtb3ZlQ2xhc3MoY29udGFpbmVyLCBzY3JvbGxlZENsYXNzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXJXaWR0aCA9IHRoaXMuX2NvbnRhaW5lci5vZmZzZXRXaWR0aDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKSxcclxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcclxuXHRcdHNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgcG9zLmFkZChhbmNob3IpKTtcclxuXHR9LFxyXG5cclxuXHRfYWRqdXN0UGFuOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMub3B0aW9ucy5hdXRvUGFuKSB7IHJldHVybjsgfVxyXG5cdFx0aWYgKHRoaXMuX21hcC5fcGFuQW5pbSkgeyB0aGlzLl9tYXAuX3BhbkFuaW0uc3RvcCgpOyB9XHJcblxyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdCAgICBtYXJnaW5Cb3R0b20gPSBwYXJzZUludChnZXRTdHlsZSh0aGlzLl9jb250YWluZXIsICdtYXJnaW5Cb3R0b20nKSwgMTApIHx8IDAsXHJcblx0XHQgICAgY29udGFpbmVySGVpZ2h0ID0gdGhpcy5fY29udGFpbmVyLm9mZnNldEhlaWdodCArIG1hcmdpbkJvdHRvbSxcclxuXHRcdCAgICBjb250YWluZXJXaWR0aCA9IHRoaXMuX2NvbnRhaW5lcldpZHRoLFxyXG5cdFx0ICAgIGxheWVyUG9zID0gbmV3IFBvaW50KHRoaXMuX2NvbnRhaW5lckxlZnQsIC1jb250YWluZXJIZWlnaHQgLSB0aGlzLl9jb250YWluZXJCb3R0b20pO1xyXG5cclxuXHRcdGxheWVyUG9zLl9hZGQoZ2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyKSk7XHJcblxyXG5cdFx0dmFyIGNvbnRhaW5lclBvcyA9IG1hcC5sYXllclBvaW50VG9Db250YWluZXJQb2ludChsYXllclBvcyksXHJcblx0XHQgICAgcGFkZGluZyA9IHRvUG9pbnQodGhpcy5vcHRpb25zLmF1dG9QYW5QYWRkaW5nKSxcclxuXHRcdCAgICBwYWRkaW5nVEwgPSB0b1BvaW50KHRoaXMub3B0aW9ucy5hdXRvUGFuUGFkZGluZ1RvcExlZnQgfHwgcGFkZGluZyksXHJcblx0XHQgICAgcGFkZGluZ0JSID0gdG9Qb2ludCh0aGlzLm9wdGlvbnMuYXV0b1BhblBhZGRpbmdCb3R0b21SaWdodCB8fCBwYWRkaW5nKSxcclxuXHRcdCAgICBzaXplID0gbWFwLmdldFNpemUoKSxcclxuXHRcdCAgICBkeCA9IDAsXHJcblx0XHQgICAgZHkgPSAwO1xyXG5cclxuXHRcdGlmIChjb250YWluZXJQb3MueCArIGNvbnRhaW5lcldpZHRoICsgcGFkZGluZ0JSLnggPiBzaXplLngpIHsgLy8gcmlnaHRcclxuXHRcdFx0ZHggPSBjb250YWluZXJQb3MueCArIGNvbnRhaW5lcldpZHRoIC0gc2l6ZS54ICsgcGFkZGluZ0JSLng7XHJcblx0XHR9XHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnggLSBkeCAtIHBhZGRpbmdUTC54IDwgMCkgeyAvLyBsZWZ0XHJcblx0XHRcdGR4ID0gY29udGFpbmVyUG9zLnggLSBwYWRkaW5nVEwueDtcclxuXHRcdH1cclxuXHRcdGlmIChjb250YWluZXJQb3MueSArIGNvbnRhaW5lckhlaWdodCArIHBhZGRpbmdCUi55ID4gc2l6ZS55KSB7IC8vIGJvdHRvbVxyXG5cdFx0XHRkeSA9IGNvbnRhaW5lclBvcy55ICsgY29udGFpbmVySGVpZ2h0IC0gc2l6ZS55ICsgcGFkZGluZ0JSLnk7XHJcblx0XHR9XHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnkgLSBkeSAtIHBhZGRpbmdUTC55IDwgMCkgeyAvLyB0b3BcclxuXHRcdFx0ZHkgPSBjb250YWluZXJQb3MueSAtIHBhZGRpbmdUTC55O1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBhdXRvcGFuc3RhcnQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgc3RhcnRzIGF1dG9wYW5uaW5nIHdoZW4gb3BlbmluZyBhIHBvcHVwLlxyXG5cdFx0aWYgKGR4IHx8IGR5KSB7X
14775HJcblx0XHRcdG1hcFxyXG5cdFx0XHQgICAgLmZpcmUoJ2F1dG9wYW5zdGFydCcpXHJcblx0XHRcdCAgICAucGFuQnkoW2R4LCBkeV0pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vbkNsb3NlQnV0dG9uQ2xpY2s6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR0aGlzLl9jbG9zZSgpO1xyXG5cdFx0c3RvcChlKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0QW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSBwb3B1cCBvbiB0aGUgc291cmNlIGxheWVyP1xyXG5cdFx0cmV0dXJuIHRvUG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0UG9wdXBBbmNob3IgPyB0aGlzLl9zb3VyY2UuX2dldFBvcHVwQW5jaG9yKCkgOiBbMCwgMF0pO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG4vLyBAZmFjdG9yeSBMLnBvcHVwKG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zLCBzb3VyY2U/OiBMYXllcilcclxuLy8gSW5zdGFudGlhdGVzIGEgYFBvcHVwYCBvYmplY3QgZ2l2ZW4gYW4gb3B0aW9uYWwgYG9wdGlvbnNgIG9iamVjdCB0aGF0IGRlc2NyaWJlcyBpdHMgYXBwZWFyYW5jZSBhbmQgbG9jYXRpb24gYW5kIGFuIG9wdGlvbmFsIGBzb3VyY2VgIG9iamVjdCB0aGF0IGlzIHVzZWQgdG8gdGFnIHRoZSBwb3B1cCB3aXRoIGEgcmVmZXJlbmNlIHRvIHRoZSBMYXllciB0byB3aGljaCBpdCByZWZlcnMuXHJcbnZhciBwb3B1cCA9IGZ1bmN0aW9uIChvcHRpb25zLCBzb3VyY2UpIHtcclxuXHRyZXR1cm4gbmV3IFBvcHVwKG9wdGlvbnMsIHNvdXJjZSk7XHJcbn07XHJcblxyXG5cclxuLyogQG5hbWVzcGFjZSBNYXBcclxuICogQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xyXG4gKiBAb3B0aW9uIGNsb3NlUG9wdXBPbkNsaWNrOiBCb29sZWFuID0gdHJ1ZVxyXG4gKiBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3UgZG9uJ3Qgd2FudCBwb3B1cHMgdG8gY2xvc2Ugd2hlbiB1c2VyIGNsaWNrcyB0aGUgbWFwLlxyXG4gKi9cclxuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0Y2xvc2VQb3B1cE9uQ2xpY2s6IHRydWVcclxufSk7XHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xyXG5NYXAuaW5jbHVkZSh7XHJcblx0Ly8gQG1ldGhvZCBvcGVuUG9wdXAocG9wdXA6IFBvcHVwKTogdGhpc1xyXG5cdC8vIE9wZW5zIHRoZSBzcGVjaWZpZWQgcG9wdXAgd2hpbGUgY2xvc2luZyB0aGUgcHJldmlvdXNseSBvcGVuZWQgKHRvIG1ha2Ugc3VyZSBvbmx5IG9uZSBpcyBvcGVuZWQgYXQgb25lIHRpbWUgZm9yIHVzYWJpbGl0eSkuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBvcGVuUG9wdXAoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50LCBsYXRsbmc6IExhdExuZywgb3B0aW9ucz86IFBvcHVwIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQ3JlYXRlcyBhIHBvcHVwIHdpdGggdGhlIHNwZWNpZmllZCBjb250ZW50IGFuZCBvcHRpb25zIGFuZCBvcGVucyBpdCBpbiB0aGUgZ2l2ZW4gcG9pbnQgb24gYSBtYXAuXHJcblx0b3BlblBvcHVwOiBmdW5jdGlvbiAocG9wdXAsIGxhdGxuZywgb3B0aW9ucykge1xyXG5cdFx0aWYgKCEocG9wdXAgaW5zdGFuY2VvZiBQb3B1cCkpIHtcclxuXHRcdFx0cG9wdXAgPSBuZXcgUG9wdXAob3B0aW9ucykuc2V0Q29udGVudChwb3B1cCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGxhdGxuZykge1xyXG5cdFx0XHRwb3B1cC5zZXRMYXRMbmcobGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5oYXNMYXllcihwb3B1cCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3BvcHVwICYmIHRoaXMuX3BvcHVwLm9wdGlvbnMuYXV0b0Nsb3NlKSB7XHJcblx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3BvcHVwID0gcG9wdXA7XHJcblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcihwb3B1cCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9zZVBvcHVwKHBvcHVwPzogUG9wdXApOiB0aGlzXHJcblx0Ly8gQ2xvc2VzIHRoZSBwb3B1cCBwcmV2aW91c2x5IG9wZW5lZCB3aXRoIFtvcGVuUG9wdXBdKCNtYXAtb3BlbnBvcHVwKSAob3IgdGhlIGdpdmVuIG9uZSkuXHJcblx0Y2xvc2VQb3B1cDogZnVuY3Rpb24gKHBvcHVwKSB7XHJcblx0XHRpZiAoIXBvcHVwIHx8IHBvcHVwID09PSB0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRwb3B1cCA9IHRoaXMuX3BvcHVwO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRpZiAocG9wdXApIHtcclxuXHRcdFx0dGhpcy5yZW1vdmVMYXllcihwb3B1cCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9XHJcbn0pO1xyXG5cclxuLypcclxuICogQG5hbWVzcGFjZSBMYXllclxyXG4gKiBAc2VjdGlvbiBQb3B1cCBtZXRob2RzIGV4YW1wbGVcclxuICpcclxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgcG9wdXBzIHRvIGl0LlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgbGF5ZXIgPSBMLlBvbHlnb24obGF0bG5ncykuYmluZFBvcHVwKCdIaSBUaGVyZSEnKS5hZGRUbyhtYXApO1xyXG4gKiBsYXllci5vcGVuUG9wdXAoKTtcclxuICogbGF5ZXIuY2xvc2VQb3B1cCgpO1xyXG4gKiBgYGBcclxuICpcclxuICogUG9wdXBzIHdpbGwgYWxzbyBiZSBhdXRvbWF0aWNhbGx5IG9wZW5lZCB3aGVuIHRoZSBsYXllciBpcyBjbGlja2VkIG9uIGFuZCBjbG9zZWQgd2hlbiB0aGUgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoZSBtYXAgb3IgYW5vdGhlciBwb3B1cCBpcyBvcGVuZWQuXHJcbiAqL1xyXG5cclxuLy8gQHNlY3Rpb24gUG9wdXAgbWV0aG9kc1xyXG5MYXllci5pbmNsdWRlKHtcclxuXHJcblx0Ly8gQG1ldGhvZCBiaW5kUG9wdXAoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fEZ1bmN0aW9ufFBvcHVwLCBvcHRpb25zPzogUG9wdXAgb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBCaW5kcyBhIHBvcHVwIHRvIHRoZSBsYXllciB3aXRoIHRoZSBwYXNzZWQgYGNvbnRlbn
14775RgIGFuZCBzZXRzIHVwIHRoZVxyXG5cdC8vIG5lY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMuIElmIGEgYEZ1bmN0aW9uYCBpcyBwYXNzZWQgaXQgd2lsbCByZWNlaXZlXHJcblx0Ly8gdGhlIGxheWVyIGFzIHRoZSBmaXJzdCBhcmd1bWVudCBhbmQgc2hvdWxkIHJldHVybiBhIGBTdHJpbmdgIG9yIGBIVE1MRWxlbWVudGAuXHJcblx0YmluZFBvcHVwOiBmdW5jdGlvbiAoY29udGVudCwgb3B0aW9ucykge1xyXG5cclxuXHRcdGlmIChjb250ZW50IGluc3RhbmNlb2YgUG9wdXApIHtcclxuXHRcdFx0c2V0T3B0aW9ucyhjb250ZW50LCBvcHRpb25zKTtcclxuXHRcdFx0dGhpcy5fcG9wdXAgPSBjb250ZW50O1xyXG5cdFx0XHRjb250ZW50Ll9zb3VyY2UgPSB0aGlzO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0aWYgKCF0aGlzLl9wb3B1cCB8fCBvcHRpb25zKSB7XHJcblx0XHRcdFx0dGhpcy5fcG9wdXAgPSBuZXcgUG9wdXAob3B0aW9ucywgdGhpcyk7XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fcG9wdXAuc2V0Q29udGVudChjb250ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCkge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRjbGljazogdGhpcy5fb3BlblBvcHVwLFxyXG5cdFx0XHRcdGtleXByZXNzOiB0aGlzLl9vbktleVByZXNzLFxyXG5cdFx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVBvcHVwLFxyXG5cdFx0XHRcdG1vdmU6IHRoaXMuX21vdmVQb3B1cFxyXG5cdFx0XHR9KTtcclxuXHRcdFx0dGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkID0gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVuYmluZFBvcHVwKCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBwb3B1cCBwcmV2aW91c2x5IGJvdW5kIHdpdGggYGJpbmRQb3B1cGAuXHJcblx0dW5iaW5kUG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLm9mZih7XHJcblx0XHRcdFx0Y2xpY2s6IHRoaXMuX29wZW5Qb3B1cCxcclxuXHRcdFx0XHRrZXlwcmVzczogdGhpcy5fb25LZXlQcmVzcyxcclxuXHRcdFx0XHRyZW1vdmU6IHRoaXMuY2xvc2VQb3B1cCxcclxuXHRcdFx0XHRtb3ZlOiB0aGlzLl9tb3ZlUG9wdXBcclxuXHRcdFx0fSk7XHJcblx0XHRcdHRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCA9IGZhbHNlO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChsYXRsbmc/OiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gT3BlbnMgdGhlIGJvdW5kIHBvcHVwIGF0IHRoZSBzcGVjaWZpZWQgYGxhdGxuZ2Agb3IgYXQgdGhlIGRlZmF1bHQgcG9wdXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cclxuXHRvcGVuUG9wdXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXAgJiYgdGhpcy5fbWFwKSB7XHJcblx0XHRcdGxhdGxuZyA9IHRoaXMuX3BvcHVwLl9wcmVwYXJlT3Blbih0aGlzLCBsYXllciwgbGF0bG5nKTtcclxuXHJcblx0XHRcdC8vIG9wZW4gdGhlIHBvcHVwIG9uIHRoZSBtYXBcclxuXHRcdFx0dGhpcy5fbWFwLm9wZW5Qb3B1cCh0aGlzLl9wb3B1cCwgbGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNsb3NlUG9wdXAoKTogdGhpc1xyXG5cdC8vIENsb3NlcyB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpZiBpdCBpcyBvcGVuLlxyXG5cdGNsb3NlUG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLl9wb3B1cC5fY2xvc2UoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdG9nZ2xlUG9wdXAoKTogdGhpc1xyXG5cdC8vIE9wZW5zIG9yIGNsb3NlcyB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBkZXBlbmRpbmcgb24gaXRzIGN1cnJlbnQgc3RhdGUuXHJcblx0dG9nZ2xlUG9wdXA6IGZ1bmN0aW9uICh0YXJnZXQpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRpZiAodGhpcy5fcG9wdXAuX21hcCkge1xyXG5cdFx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXMub3BlblBvcHVwKHRhcmdldCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaXNQb3B1cE9wZW4oKTogYm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGN1cnJlbnRseSBvcGVuLlxyXG5cdGlzUG9wdXBPcGVuOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gKHRoaXMuX3BvcHVwID8gdGhpcy5fcG9wdXAuaXNPcGVuKCkgOiBmYWxzZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRQb3B1cENvbnRlbnQoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fFBvcHVwKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIGNvbnRlbnQgb2YgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXHJcblx0c2V0UG9wdXBDb250ZW50OiBmdW5jdGlvbiAoY29udGVudCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHRoaXMuX3BvcHVwLnNldENvbnRlbnQoY29udGVudCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBvcHVwKCk6IFBvcHVwXHJcblx0Ly8gUmV0dXJucyB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllci5cclxuXHRnZXRQb3B1cDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BvcHVwO1xyXG5cdH0sXHJcblxyXG5cdF9vcGVuUG9wdXA6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgbGF5ZXIgPSBlLmxheWVyIHx8IGUudGFyZ2V0O1xyXG5cclxuXHRcdGlmICghdGhpcy5fcG9wdXApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fbWFwKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBwcmV2ZW50IG1hcCBjbGlja1xyXG5cdFx0c3RvcChlKTtcclxuXHJcblx0XHQvLyBpZiB0aGlzIGluaGVyaXRzIGZyb20gUGF0aCBpdHMgYSB2ZWN0b3IgYW5kIHdlIGNhbiBqdXN0XHJcblx0XHQvLyBvcGVuIHRoZSBwb3B1cCBhdCB0aGUgbmV3IGxvY2F0aW9uXHJcblx0XHRpZiAobGF5ZXIgaW5zdGFuY2VvZiBQYXRoKSB7X
14775HJcblx0XHRcdHRoaXMub3BlblBvcHVwKGUubGF5ZXIgfHwgZS50YXJnZXQsIGUubGF0bG5nKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIG90aGVyd2lzZSB0cmVhdCBpdCBsaWtlIGEgbWFya2VyIGFuZCBmaWd1cmUgb3V0XHJcblx0XHQvLyBpZiB3ZSBzaG91bGQgdG9nZ2xlIGl0IG9wZW4vY2xvc2VkXHJcblx0XHRpZiAodGhpcy5fbWFwLmhhc0xheWVyKHRoaXMuX3BvcHVwKSAmJiB0aGlzLl9wb3B1cC5fc291cmNlID09PSBsYXllcikge1xyXG5cdFx0XHR0aGlzLmNsb3NlUG9wdXAoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMub3BlblBvcHVwKGxheWVyLCBlLmxhdGxuZyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVQb3B1cDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHRoaXMuX3BvcHVwLnNldExhdExuZyhlLmxhdGxuZyk7XHJcblx0fSxcclxuXHJcblx0X29uS2V5UHJlc3M6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoZS5vcmlnaW5hbEV2ZW50LmtleUNvZGUgPT09IDEzKSB7XHJcblx0XHRcdHRoaXMuX29wZW5Qb3B1cChlKTtcclxuXHRcdH1cclxuXHR9XHJcbn0pO1xuXG4vKlxuICogQGNsYXNzIFRvb2x0aXBcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XG4gKiBAYWthIEwuVG9vbHRpcFxuICogVXNlZCB0byBkaXNwbGF5IHNtYWxsIHRleHRzIG9uIHRvcCBvZiBtYXAgbGF5ZXJzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIG1hcmtlci5iaW5kVG9vbHRpcChcIm15IHRvb2x0aXAgdGV4dFwiKS5vcGVuVG9vbHRpcCgpO1xuICogYGBgXG4gKiBOb3RlIGFib3V0IHRvb2x0aXAgb2Zmc2V0LiBMZWFmbGV0IHRha2VzIHR3byBvcHRpb25zIGluIGNvbnNpZGVyYXRpb25cbiAqIGZvciBjb21wdXRpbmcgdG9vbHRpcCBvZmZzZXR0aW5nOlxuICogLSB0aGUgYG9mZnNldGAgVG9vbHRpcCBvcHRpb246IGl0IGRlZmF1bHRzIHRvIFswLCAwXSwgYW5kIGl0J3Mgc3BlY2lmaWMgdG8gb25lIHRvb2x0aXAuXG4gKiAgIEFkZCBhIHBvc2l0aXZlIHggb2Zmc2V0IHRvIG1vdmUgdGhlIHRvb2x0aXAgdG8gdGhlIHJpZ2h0LCBhbmQgYSBwb3NpdGl2ZSB5IG9mZnNldCB0b1xuICogICBtb3ZlIGl0IHRvIHRoZSBib3R0b20uIE5lZ2F0aXZlcyB3aWxsIG1vdmUgdG8gdGhlIGxlZnQgYW5kIHRvcC5cbiAqIC0gdGhlIGB0b29sdGlwQW5jaG9yYCBJY29uIG9wdGlvbjogdGhpcyB3aWxsIG9ubHkgYmUgY29uc2lkZXJlZCBmb3IgTWFya2VyLiBZb3VcbiAqICAgc2hvdWxkIGFkYXB0IHRoaXMgdmFsdWUgaWYgeW91IHVzZSBhIGN1c3RvbSBpY29uLlxuICovXG5cblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG52YXIgVG9vbHRpcCA9IERpdk92ZXJsYXkuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFRvb2x0aXAgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndG9vbHRpcFBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgdG9vbHRpcCB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0b29sdGlwUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIG9mZnNldDogUG9pbnQgPSBQb2ludCgwLCAwKVxuXHRcdC8vIE9wdGlvbmFsIG9mZnNldCBvZiB0aGUgdG9vbHRpcCBwb3NpdGlvbi5cblx0XHRvZmZzZXQ6IFswLCAwXSxcblxuXHRcdC8vIEBvcHRpb24gZGlyZWN0aW9uOiBTdHJpbmcgPSAnYXV0bydcblx0XHQvLyBEaXJlY3Rpb24gd2hlcmUgdG8gb3BlbiB0aGUgdG9vbHRpcC4gUG9zc2libGUgdmFsdWVzIGFyZTogYHJpZ2h0YCwgYGxlZnRgLFxuXHRcdC8vIGB0b3BgLCBgYm90dG9tYCwgYGNlbnRlcmAsIGBhdXRvYC5cblx0XHQvLyBgYXV0b2Agd2lsbCBkeW5hbWljYWxseSBzd2l0Y2ggYmV0d2VlbiBgcmlnaHRgIGFuZCBgbGVmdGAgYWNjb3JkaW5nIHRvIHRoZSB0b29sdGlwXG5cdFx0Ly8gcG9zaXRpb24gb24gdGhlIG1hcC5cblx0XHRkaXJlY3Rpb246ICdhdXRvJyxcblxuXHRcdC8vIEBvcHRpb24gcGVybWFuZW50OiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBXaGV0aGVyIHRvIG9wZW4gdGhlIHRvb2x0aXAgcGVybWFuZW50bHkgb3Igb25seSBvbiBtb3VzZW92ZXIuXG5cdFx0cGVybWFuZW50OiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gc3RpY2t5OiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBJZiB0cnVlLCB0aGUgdG9vbHRpcCB3aWxsIGZvbGxvdyB0aGUgbW91c2UgaW5zdGVhZCBvZiBiZWluZyBmaXhlZCBhdCB0aGUgZmVhdHVyZSBjZW50ZXIuXG5cdFx0c3RpY2t5OiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gaW50ZXJhY3RpdmU6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIElmIHRydWUsIHRoZSB0b29sdGlwIHdpbGwgbGlzdGVuIHRvIHRoZSBmZWF0dXJlIGV2ZW50cy5cblx0XHRpbnRlcmFjdGl2ZTogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDAuOVxuXHRcdC8vIFRvb2x0aXAgY29udGFpbmVyIG9wYWNpdHkuXG5cdFx0b3BhY2l0eTogMC45XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHREaXZPdmVybGF5LnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XG5cdFx0dGhpcy5zZXRPcGFjaXR5KHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXG5cdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHQvLyBAZXZlbnQgdG9vbHRpcG9wZW46IFRvb2x0aXBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGlzIG9wZW5lZCBpbiB0aGUgbWFwLlxuXHRcdG1hcC5maXJlKCd0b29sdGlwb3BlbicsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBvcGVuOiBUb29sdGlwRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgb3BlbmVkLlxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3Rvb2x0aXBvcGVuJywge3Rvb2x0aXA6IHRoaXN9LCB0cnVlKTtcblx0XHR9XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHREaXZPdmVybGF5LnByb3RvdHlwZS5vblJlbW92ZS5jYWxsKHRoaXMsIG1hcCk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxuXHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHRvb2x0aXAgaW4gdGhlIG1hcCBpcyBjbG9zZWQuXG5cdFx0bWFwLmZpcmUoJ3Rvb2x0aXBjbG9zZScsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGNsb3NlZC5cblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCd0b29sdGlwY2xvc2UnLCB7dG9vbHRpcDogdGhpc30sIHRydWUpO1xuXHRcdH1cblx0fSxcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gRGl2T3ZlcmxheS5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XG5cblx0XHRpZiAodG91Y2ggJiYgIXRoaXMub3B0aW9ucy5wZXJtYW5lbnQpIHtcblx0XHRcdGV2ZW50cy5wcmVjbGljayA9IHRoaXMuX2Nsb3NlO1xuXHRcdH1cblxuXHRcdHJldHVybi
14775BldmVudHM7XG5cdH0sXG5cblx0X2Nsb3NlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fbWFwLmNsb3NlVG9vbHRpcCh0aGlzKTtcblx0XHR9XG5cdH0sXG5cblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHJlZml4ID0gJ2xlYWZsZXQtdG9vbHRpcCcsXG5cdFx0ICAgIGNsYXNzTmFtZSA9IHByZWZpeCArICcgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSArICcgbGVhZmxldC16b29tLScgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2FuaW1hdGVkJyA6ICdoaWRlJyk7XG5cblx0XHR0aGlzLl9jb250ZW50Tm9kZSA9IHRoaXMuX2NvbnRhaW5lciA9IGNyZWF0ZSQxKCdkaXYnLCBjbGFzc05hbWUpO1xuXHR9LFxuXG5cdF91cGRhdGVMYXlvdXQ6IGZ1bmN0aW9uICgpIHt9LFxuXG5cdF9hZGp1c3RQYW46IGZ1bmN0aW9uICgpIHt9LFxuXG5cdF9zZXRQb3NpdGlvbjogZnVuY3Rpb24gKHBvcykge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcixcblx0XHQgICAgY2VudGVyUG9pbnQgPSBtYXAubGF0TG5nVG9Db250YWluZXJQb2ludChtYXAuZ2V0Q2VudGVyKCkpLFxuXHRcdCAgICB0b29sdGlwUG9pbnQgPSBtYXAubGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQocG9zKSxcblx0XHQgICAgZGlyZWN0aW9uID0gdGhpcy5vcHRpb25zLmRpcmVjdGlvbixcblx0XHQgICAgdG9vbHRpcFdpZHRoID0gY29udGFpbmVyLm9mZnNldFdpZHRoLFxuXHRcdCAgICB0b29sdGlwSGVpZ2h0ID0gY29udGFpbmVyLm9mZnNldEhlaWdodCxcblx0XHQgICAgb2Zmc2V0ID0gdG9Qb2ludCh0aGlzLm9wdGlvbnMub2Zmc2V0KSxcblx0XHQgICAgYW5jaG9yID0gdGhpcy5fZ2V0QW5jaG9yKCk7XG5cblx0XHRpZiAoZGlyZWN0aW9uID09PSAndG9wJykge1xuXHRcdFx0cG9zID0gcG9zLmFkZCh0b1BvaW50KC10b29sdGlwV2lkdGggLyAyICsgb2Zmc2V0LngsIC10b29sdGlwSGVpZ2h0ICsgb2Zmc2V0LnkgKyBhbmNob3IueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAnYm90dG9tJykge1xuXHRcdFx0cG9zID0gcG9zLnN1YnRyYWN0KHRvUG9pbnQodG9vbHRpcFdpZHRoIC8gMiAtIG9mZnNldC54LCAtb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9IGVsc2UgaWYgKGRpcmVjdGlvbiA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdHBvcyA9IHBvcy5zdWJ0cmFjdCh0b1BvaW50KHRvb2x0aXBXaWR0aCAvIDIgKyBvZmZzZXQueCwgdG9vbHRpcEhlaWdodCAvIDIgLSBhbmNob3IueSArIG9mZnNldC55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIGlmIChkaXJlY3Rpb24gPT09ICdyaWdodCcgfHwgZGlyZWN0aW9uID09PSAnYXV0bycgJiYgdG9vbHRpcFBvaW50LnggPCBjZW50ZXJQb2ludC54KSB7XG5cdFx0XHRkaXJlY3Rpb24gPSAncmlnaHQnO1xuXHRcdFx0cG9zID0gcG9zLmFkZCh0b1BvaW50KG9mZnNldC54ICsgYW5jaG9yLngsIGFuY2hvci55IC0gdG9vbHRpcEhlaWdodCAvIDIgKyBvZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRkaXJlY3Rpb24gPSAnbGVmdCc7XG5cdFx0XHRwb3MgPSBwb3Muc3VidHJhY3QodG9Qb2ludCh0b29sdGlwV2lkdGggKyBhbmNob3IueCAtIG9mZnNldC54LCB0b29sdGlwSGVpZ2h0IC8gMiAtIGFuY2hvci55IC0gb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9XG5cblx0XHRyZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtcmlnaHQnKTtcblx0XHRyZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtbGVmdCcpO1xuXHRcdHJlbW92ZUNsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC10b3AnKTtcblx0XHRyZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtYm90dG9tJyk7XG5cdFx0YWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLScgKyBkaXJlY3Rpb24pO1xuXHRcdHNldFBvc2l0aW9uKGNvbnRhaW5lciwgcG9zKTtcblx0fSxcblxuXHRfdXBkYXRlUG9zaXRpb246IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpO1xuXHRcdHRoaXMuX3NldFBvc2l0aW9uKHBvcyk7XG5cdH0sXG5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cblx0XHRpZiAodGhpcy5fY29udGFpbmVyKSB7XG5cdFx0XHRzZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgb3BhY2l0eSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKTtcblx0XHR0aGlzLl9zZXRQb3NpdGlvbihwb3MpO1xuXHR9LFxuXG5cdF9nZXRBbmNob3I6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSB0b29sdGlwIG9uIHRoZSBzb3VyY2UgbGF5ZXI/XG5cdFx0cmV0dXJuIHRvUG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvciAmJiAhdGhpcy5vcHRpb25zLnN0aWNreSA/IHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvcigpIDogWzAsIDBdKTtcblx0fVxuXG59KTtcblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG4vLyBAZmFjdG9yeSBMLnRvb2x0aXAob3B0aW9ucz86IFRvb2x0aXAgb3B0aW9ucywgc291cmNlPzogTGF5ZXIpXG4vLyBJbnN0YW50aWF0ZXMgYSBUb29sdGlwIG9iamVjdCBnaXZlbiBhbiBvcHRpb25hbCBgb3B0aW9uc2Agb2JqZWN0IHRoYXQgZGVzY3JpYmVzIGl0cyBhcHBlYXJhbmNlIGFuZCBsb2NhdGlvbiBhbmQgYW4gb3B0aW9uYWwgYHNvdXJjZWAgb2JqZWN0IHRoYXQgaXMgdXNlZCB0byB0YWcgdGhlIHRvb2x0aXAgd2l0aCBhIHJlZmVyZW5jZSB0byB0aGUgTGF5ZXIgdG8gd2hpY2ggaXQgcmVmZXJzLlxudmFyIHRvb2x0aXAgPSBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XG5cdHJldHVybi
14775BuZXcgVG9vbHRpcChvcHRpb25zLCBzb3VyY2UpO1xufTtcblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIExheWVycyBhbmQgQ29udHJvbHNcbk1hcC5pbmNsdWRlKHtcblxuXHQvLyBAbWV0aG9kIG9wZW5Ub29sdGlwKHRvb2x0aXA6IFRvb2x0aXApOiB0aGlzXG5cdC8vIE9wZW5zIHRoZSBzcGVjaWZpZWQgdG9vbHRpcC5cblx0Ly8gQGFsdGVybmF0aXZlXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50LCBsYXRsbmc6IExhdExuZywgb3B0aW9ucz86IFRvb2x0aXAgb3B0aW9ucyk6IHRoaXNcblx0Ly8gQ3JlYXRlcyBhIHRvb2x0aXAgd2l0aCB0aGUgc3BlY2lmaWVkIGNvbnRlbnQgYW5kIG9wdGlvbnMgYW5kIG9wZW4gaXQuXG5cdG9wZW5Ub29sdGlwOiBmdW5jdGlvbiAodG9vbHRpcCwgbGF0bG5nLCBvcHRpb25zKSB7XG5cdFx0aWYgKCEodG9vbHRpcCBpbnN0YW5jZW9mIFRvb2x0aXApKSB7XG5cdFx0XHR0b29sdGlwID0gbmV3IFRvb2x0aXAob3B0aW9ucykuc2V0Q29udGVudCh0b29sdGlwKTtcblx0XHR9XG5cblx0XHRpZiAobGF0bG5nKSB7XG5cdFx0XHR0b29sdGlwLnNldExhdExuZyhsYXRsbmcpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLmhhc0xheWVyKHRvb2x0aXApKSB7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcih0b29sdGlwKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGNsb3NlVG9vbHRpcCh0b29sdGlwPzogVG9vbHRpcCk6IHRoaXNcblx0Ly8gQ2xvc2VzIHRoZSB0b29sdGlwIGdpdmVuIGFzIHBhcmFtZXRlci5cblx0Y2xvc2VUb29sdGlwOiBmdW5jdGlvbiAodG9vbHRpcCkge1xuXHRcdGlmICh0b29sdGlwKSB7XG5cdFx0XHR0aGlzLnJlbW92ZUxheWVyKHRvb2x0aXApO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG59KTtcblxuLypcbiAqIEBuYW1lc3BhY2UgTGF5ZXJcbiAqIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kcyBleGFtcGxlXG4gKlxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgdG9vbHRpcHMgdG8gaXQuXG4gKlxuICogYGBganNcbiAqIHZhciBsYXllciA9IEwuUG9seWdvbihsYXRsbmdzKS5iaW5kVG9vbHRpcCgnSGkgVGhlcmUhJykuYWRkVG8obWFwKTtcbiAqIGxheWVyLm9wZW5Ub29sdGlwKCk7XG4gKiBsYXllci5jbG9zZVRvb2x0aXAoKTtcbiAqIGBgYFxuICovXG5cbi8vIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kc1xuTGF5ZXIuaW5jbHVkZSh7XG5cblx0Ly8gQG1ldGhvZCBiaW5kVG9vbHRpcChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8RnVuY3Rpb258VG9vbHRpcCwgb3B0aW9ucz86IFRvb2x0aXAgb3B0aW9ucyk6IHRoaXNcblx0Ly8gQmluZHMgYSB0b29sdGlwIHRvIHRoZSBsYXllciB3aXRoIHRoZSBwYXNzZWQgYGNvbnRlbnRgIGFuZCBzZXRzIHVwIHRoZVxuXHQvLyBuZWNlc3NhcnkgZXZlbnQgbGlzdGVuZXJzLiBJZiBhIGBGdW5jdGlvbmAgaXMgcGFzc2VkIGl0IHdpbGwgcmVjZWl2ZVxuXHQvLyB0aGUgbGF5ZXIgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YC5cblx0YmluZFRvb2x0aXA6IGZ1bmN0aW9uIChjb250ZW50LCBvcHRpb25zKSB7XG5cblx0XHRpZiAoY29udGVudCBpbnN0YW5jZW9mIFRvb2x0aXApIHtcblx0XHRcdHNldE9wdGlvbnMoY29udGVudCwgb3B0aW9ucyk7XG5cdFx0XHR0aGlzLl90b29sdGlwID0gY29udGVudDtcblx0XHRcdGNvbnRlbnQuX3NvdXJjZSA9IHRoaXM7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGlmICghdGhpcy5fdG9vbHRpcCB8fCBvcHRpb25zKSB7XG5cdFx0XHRcdHRoaXMuX3Rvb2x0aXAgPSBuZXcgVG9vbHRpcChvcHRpb25zLCB0aGlzKTtcblx0XHRcdH1cblx0XHRcdHRoaXMuX3Rvb2x0aXAuc2V0Q29udGVudChjb250ZW50KTtcblxuXHRcdH1cblxuXHRcdHRoaXMuX2luaXRUb29sdGlwSW50ZXJhY3Rpb25zKCk7XG5cblx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLnBlcm1hbmVudCAmJiB0aGlzLl9tYXAgJiYgdGhpcy5fbWFwLmhhc0xheWVyKHRoaXMpKSB7XG5cdFx0XHR0aGlzLm9wZW5Ub29sdGlwKCk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCB1bmJpbmRUb29sdGlwKCk6IHRoaXNcblx0Ly8gUmVtb3ZlcyB0aGUgdG9vbHRpcCBwcmV2aW91c2x5IGJvdW5kIHdpdGggYGJpbmRUb29sdGlwYC5cblx0dW5iaW5kVG9vbHRpcDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl90b29sdGlwKSB7XG5cdFx0XHR0aGlzLl9pbml0VG9vbHRpcEludGVyYWN0aW9ucyh0cnVlKTtcblx0XHRcdHRoaXMuY2xvc2VUb29sdGlwKCk7XG5cdFx0XHR0aGlzLl90b29sdGlwID0gbnVsbDtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2luaXRUb29sdGlwSW50ZXJhY3Rpb25zOiBmdW5jdGlvbiAocmVtb3ZlJCQxKSB7XG5cdFx0aWYgKCFyZW1vdmUkJDEgJiYgdGhpcy5fdG9vbHRpcEhhbmRsZXJzQWRkZWQpIHsgcmV0dXJuOyB9XG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlJCQxID8gJ29mZicgOiAnb24nLFxuXHRcdCAgICBldmVudHMgPSB7XG5cdFx0XHRyZW1vdmU6IHRoaXMuY2xvc2VUb29sdGlwLFxuXHRcdFx0bW92ZTogdGhpcy5fbW92ZVRvb2x0aXBcblx0XHQgICAgfTtcblx0XHRpZiAoIXRoaXMuX3Rvb2x0aXAub3B0aW9ucy5wZXJtYW5lbnQpIHtcblx0XHRcdGV2ZW50cy5tb3VzZW92ZXIgPSB0aGlzLl9vcGVuVG9vbHRpcDtcblx0XHRcdGV2ZW50cy5tb3VzZW91dCA9IHRoaXMuY2xvc2VUb29sdGlwO1xuXHRcdFx0aWYgKHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5zdGlja3kpIHtcblx0XHRcdFx0ZXZlbnRzLm1vdXNlbW92ZSA9IHRoaXMuX21vdmVUb29sdGlwO1xuXHRcdFx0fVxuXHRcdFx0aWYgKHRvdWNoKSB7XG5cdFx0XHRcdGV2ZW50cy5jbGljayA9IHRoaXMuX29wZW5Ub29sdGlwO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRldmVudHMuYWRkID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0fVxuXHRcdHRoaXNbb25PZmZdKGV2ZW50cyk7XG5cdFx0dGhpcy5fdG9vbHRpcEhhbmRsZXJzQWRkZWQgPSAhcmVtb3ZlJCQxO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAobGF0bG5nPzogTGF0TG5nKTogdGh
14775pc1xuXHQvLyBPcGVucyB0aGUgYm91bmQgdG9vbHRpcCBhdCB0aGUgc3BlY2lmaWVkIGBsYXRsbmdgIG9yIGF0IHRoZSBkZWZhdWx0IHRvb2x0aXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cblx0b3BlblRvb2x0aXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXAgJiYgdGhpcy5fbWFwKSB7XG5cdFx0XHRsYXRsbmcgPSB0aGlzLl90b29sdGlwLl9wcmVwYXJlT3Blbih0aGlzLCBsYXllciwgbGF0bG5nKTtcblxuXHRcdFx0Ly8gb3BlbiB0aGUgdG9vbHRpcCBvbiB0aGUgbWFwXG5cdFx0XHR0aGlzLl9tYXAub3BlblRvb2x0aXAodGhpcy5fdG9vbHRpcCwgbGF0bG5nKTtcblxuXHRcdFx0Ly8gVG9vbHRpcCBjb250YWluZXIgbWF5IG5vdCBiZSBkZWZpbmVkIGlmIG5vdCBwZXJtYW5lbnQgYW5kIG5ldmVyXG5cdFx0XHQvLyBvcGVuZWQuXG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLmludGVyYWN0aXZlICYmIHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcikge1xuXHRcdFx0XHRhZGRDbGFzcyh0aGlzLl90b29sdGlwLl9jb250YWluZXIsICdsZWFmbGV0LWNsaWNrYWJsZScpO1xuXHRcdFx0XHR0aGlzLmFkZEludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcik7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBjbG9zZVRvb2x0aXAoKTogdGhpc1xuXHQvLyBDbG9zZXMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBpZiBpdCBpcyBvcGVuLlxuXHRjbG9zZVRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5fdG9vbHRpcC5fY2xvc2UoKTtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgdGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKSB7XG5cdFx0XHRcdHJlbW92ZUNsYXNzKHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY2xpY2thYmxlJyk7XG5cdFx0XHRcdHRoaXMucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCB0b2dnbGVUb29sdGlwKCk6IHRoaXNcblx0Ly8gT3BlbnMgb3IgY2xvc2VzIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgZGVwZW5kaW5nIG9uIGl0cyBjdXJyZW50IHN0YXRlLlxuXHR0b2dnbGVUb29sdGlwOiBmdW5jdGlvbiAodGFyZ2V0KSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLl9tYXApIHtcblx0XHRcdFx0dGhpcy5jbG9zZVRvb2x0aXAoKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMub3BlblRvb2x0aXAodGFyZ2V0KTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBpc1Rvb2x0aXBPcGVuKCk6IGJvb2xlYW5cblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBjdXJyZW50bHkgb3Blbi5cblx0aXNUb29sdGlwT3BlbjogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl90b29sdGlwLmlzT3BlbigpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0VG9vbHRpcENvbnRlbnQoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fFRvb2x0aXApOiB0aGlzXG5cdC8vIFNldHMgdGhlIGNvbnRlbnQgb2YgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllci5cblx0c2V0VG9vbHRpcENvbnRlbnQ6IGZ1bmN0aW9uIChjb250ZW50KSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdHRoaXMuX3Rvb2x0aXAuc2V0Q29udGVudChjb250ZW50KTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRUb29sdGlwKCk6IFRvb2x0aXBcblx0Ly8gUmV0dXJucyB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyLlxuXHRnZXRUb29sdGlwOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3Rvb2x0aXA7XG5cdH0sXG5cblx0X29wZW5Ub29sdGlwOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBsYXllciA9IGUubGF5ZXIgfHwgZS50YXJnZXQ7XG5cblx0XHRpZiAoIXRoaXMuX3Rvb2x0aXAgfHwgIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLm9wZW5Ub29sdGlwKGxheWVyLCB0aGlzLl90b29sdGlwLm9wdGlvbnMuc3RpY2t5ID8gZS5sYXRsbmcgOiB1bmRlZmluZWQpO1xuXHR9LFxuXG5cdF9tb3ZlVG9vbHRpcDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbGF0bG5nID0gZS5sYXRsbmcsIGNvbnRhaW5lclBvaW50LCBsYXllclBvaW50O1xuXHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuc3RpY2t5ICYmIGUub3JpZ2luYWxFdmVudCkge1xuXHRcdFx0Y29udGFpbmVyUG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS5vcmlnaW5hbEV2ZW50KTtcblx0XHRcdGxheWVyUG9pbnQgPSB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQoY29udGFpbmVyUG9pbnQpO1xuXHRcdFx0bGF0bG5nID0gdGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhsYXllclBvaW50KTtcblx0XHR9XG5cdFx0dGhpcy5fdG9vbHRpcC5zZXRMYXRMbmcobGF0bG5nKTtcblx0fVxufSk7XG5cbi8qXG4gKiBAY2xhc3MgRGl2SWNvblxuICogQGFrYSBMLkRpdkljb25cbiAqIEBpbmhlcml0cyBJY29uXG4gKlxuICogUmVwcmVzZW50cyBhIGxpZ2h0d2VpZ2h0IGljb24gZm9yIG1hcmtlcnMgdGhhdCB1c2VzIGEgc2ltcGxlIGA8ZGl2PmBcbiAqIGVsZW1lbnQgaW5zdGVhZCBvZiBhbiBpbWFnZS4gSW5oZXJpdHMgZnJvbSBgSWNvbmAgYnV0IGlnbm9yZXMgdGhlIGBpY29uVXJsYCBhbmQgc2hhZG93IG9wdGlvbnMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYGpzXG4gKiB2YXIgbXlJY29uID0gTC5kaXZJY29uKHtjbGFzc05hbWU6ICdteS1kaXYtaWNvbid9KTtcbiAqIC8vIHlvdSBjYW4gc2V0IC5teS1kaXYtaWNvbiBzdHlsZXMgaW4gQ1NTXG4gKlxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICpcbiAqIEJ5IGRlZmF1bHQsIGl0IGhhcyBhICdsZWFmbGV0LWRpdi1pY29uJyBDU1MgY2xhc3MgYW5kIGlzIHN0eWxlZCBhcyBhIGxpdHRsZSB3aGl0ZSBzcXVhcmUgd2l0aCBhIHNoYWRvdy5cbiAqL1xuXG52YXIgRGl2SWNvbiA9IEljb24uZXh0ZW5kKHtcblx0b3B0aW9uczoge1xuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBEaXZJY29uIG9wdGlvbnNcblx0XHRpY29uU2l6ZTogWzEyLCAxMl0sIC8vIGFsc28gY2FuIGJlIHNldCB0aHJvdWdoIENTU1xuXG5cdFx0Ly8gaWNvbkFuY2hvcjogKFBvaW50KSxcblx0XHQvLyBwb3B1cEFuY2hvcjogKFBvaW50KSxcblxuXHRcdC8vIEBvcHRpb24gaHRtbDogU3RyaW5nfEhUTUxFbGVtZW50ID0gJydcblx0XHQvLyBDdXN0b20gSFRNTCBjb2RlIHRvIHB1dCBpbnNpZGUgdGhlIGRpdiBlbGVtZW50LCBlbXB0eSBieSBkZWZh
14775dWx0LiBBbHRlcm5hdGl2ZWx5LFxuXHRcdC8vIGFuIGluc3RhbmNlIG9mIGBIVE1MRWxlbWVudGAuXG5cdFx0aHRtbDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGJnUG9zOiBQb2ludCA9IFswLCAwXVxuXHRcdC8vIE9wdGlvbmFsIHJlbGF0aXZlIHBvc2l0aW9uIG9mIHRoZSBiYWNrZ3JvdW5kLCBpbiBwaXhlbHNcblx0XHRiZ1BvczogbnVsbCxcblxuXHRcdGNsYXNzTmFtZTogJ2xlYWZsZXQtZGl2LWljb24nXG5cdH0sXG5cblx0Y3JlYXRlSWNvbjogZnVuY3Rpb24gKG9sZEljb24pIHtcblx0XHR2YXIgZGl2ID0gKG9sZEljb24gJiYgb2xkSWNvbi50YWdOYW1lID09PSAnRElWJykgPyBvbGRJY29uIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHRpZiAob3B0aW9ucy5odG1sIGluc3RhbmNlb2YgRWxlbWVudCkge1xuXHRcdFx0ZW1wdHkoZGl2KTtcblx0XHRcdGRpdi5hcHBlbmRDaGlsZChvcHRpb25zLmh0bWwpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRkaXYuaW5uZXJIVE1MID0gb3B0aW9ucy5odG1sICE9PSBmYWxzZSA/IG9wdGlvbnMuaHRtbCA6ICcnO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLmJnUG9zKSB7XG5cdFx0XHR2YXIgYmdQb3MgPSB0b1BvaW50KG9wdGlvbnMuYmdQb3MpO1xuXHRcdFx0ZGl2LnN0eWxlLmJhY2tncm91bmRQb3NpdGlvbiA9ICgtYmdQb3MueCkgKyAncHggJyArICgtYmdQb3MueSkgKyAncHgnO1xuXHRcdH1cblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGRpdiwgJ2ljb24nKTtcblxuXHRcdHJldHVybiBkaXY7XG5cdH0sXG5cblx0Y3JlYXRlU2hhZG93OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmRpdkljb24ob3B0aW9uczogRGl2SWNvbiBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIGBEaXZJY29uYCBpbnN0YW5jZSB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuZnVuY3Rpb24gZGl2SWNvbihvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgRGl2SWNvbihvcHRpb25zKTtcbn1cblxuSWNvbi5EZWZhdWx0ID0gSWNvbkRlZmF1bHQ7XG5cbi8qXG4gKiBAY2xhc3MgR3JpZExheWVyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcbiAqIEBha2EgTC5HcmlkTGF5ZXJcbiAqXG4gKiBHZW5lcmljIGNsYXNzIGZvciBoYW5kbGluZyBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cy4gVGhpcyBpcyB0aGUgYmFzZSBjbGFzcyBmb3IgYWxsIHRpbGUgbGF5ZXJzIGFuZCByZXBsYWNlcyBgVGlsZUxheWVyLkNhbnZhc2AuXG4gKiBHcmlkTGF5ZXIgY2FuIGJlIGV4dGVuZGVkIHRvIGNyZWF0ZSBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cyBsaWtlIGA8Y2FudmFzPmAsIGA8aW1nPmAgb3IgYDxkaXY+YC4gR3JpZExheWVyIHdpbGwgaGFuZGxlIGNyZWF0aW5nIGFuZCBhbmltYXRpbmcgdGhlc2UgRE9NIGVsZW1lbnRzIGZvciB5b3UuXG4gKlxuICpcbiAqIEBzZWN0aW9uIFN5bmNocm9ub3VzIHVzYWdlXG4gKiBAZXhhbXBsZVxuICpcbiAqIFRvIGNyZWF0ZSBhIGN1c3RvbSBsYXllciwgZXh0ZW5kIEdyaWRMYXllciBhbmQgaW1wbGVtZW50IHRoZSBgY3JlYXRlVGlsZSgpYCBtZXRob2QsIHdoaWNoIHdpbGwgYmUgcGFzc2VkIGEgYFBvaW50YCBvYmplY3Qgd2l0aCB0aGUgYHhgLCBgeWAsIGFuZCBgemAgKHpvb20gbGV2ZWwpIGNvb3JkaW5hdGVzIHRvIGRyYXcgeW91ciB0aWxlLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3Jkcyl7XG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGdldCBhIGNhbnZhcyBjb250ZXh0IGFuZCBkcmF3IHNvbWV0aGluZyBvbiBpdCB1c2luZyBjb29yZHMueCwgY29vcmRzLnkgYW5kIGNvb3Jkcy56XG4gKiAgICAgICAgIHZhciBjdHggPSB0aWxlLmdldENvbnRleHQoJzJkJyk7XG4gKlxuICogICAgICAgICAvLyByZXR1cm4gdGhlIHRpbGUgc28gaXQgY2FuIGJlIHJlbmRlcmVkIG9uIHNjcmVlblxuICogICAgICAgICByZXR1cm4gdGlsZTtcbiAqICAgICB9XG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIEBzZWN0aW9uIEFzeW5jaHJvbm91cyB1c2FnZVxuICogQGV4YW1wbGVcbiAqXG4gKiBUaWxlIGNyZWF0aW9uIGNhbiBhbHNvIGJlIGFzeW5jaHJvbm91cywgdGhpcyBpcyB1c2VmdWwgd2hlbiB1c2luZyBhIHRoaXJkLXBhcnR5IGRyYXdpbmcgbGlicmFyeS4gT25jZSB0aGUgdGlsZSBpcyBmaW5pc2hlZCBkcmF3aW5nIGl0IGNhbiBiZSBwYXNzZWQgdG8gdGhlIGBkb25lKClgIGNhbGxiYWNrLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3JkcywgZG9uZSl7XG4gKiAgICAgICAgIHZhciBlcnJvcjtcbiAqXG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGRyYXcgc29tZXRoaW5nIGFzeW5jaHJvbm91c2x5IGFuZCBwYXNzIHRoZSB0aWxlIHRvIH
14775RoZSBkb25lKCkgY2FsbGJhY2tcbiAqICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAqICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHRpbGUpO1xuICogICAgICAgICB9LCAxMDAwKTtcbiAqXG4gKiAgICAgICAgIHJldHVybiB0aWxlO1xuICogICAgIH1cbiAqIH0pO1xuICogYGBgXG4gKlxuICogQHNlY3Rpb25cbiAqL1xuXG5cbnZhciBHcmlkTGF5ZXIgPSBMYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgR3JpZExheWVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gdGlsZVNpemU6IE51bWJlcnxQb2ludCA9IDI1NlxuXHRcdC8vIFdpZHRoIGFuZCBoZWlnaHQgb2YgdGlsZXMgaW4gdGhlIGdyaWQuIFVzZSBhIG51bWJlciBpZiB3aWR0aCBhbmQgaGVpZ2h0IGFyZSBlcXVhbCwgb3IgYEwucG9pbnQod2lkdGgsIGhlaWdodClgIG90aGVyd2lzZS5cblx0XHR0aWxlU2l6ZTogMjU2LFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBPcGFjaXR5IG9mIHRoZSB0aWxlcy4gQ2FuIGJlIHVzZWQgaW4gdGhlIGBjcmVhdGVUaWxlKClgIGZ1bmN0aW9uLlxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5JZGxlOiBCb29sZWFuID0gKGRlcGVuZHMpXG5cdFx0Ly8gTG9hZCBuZXcgdGlsZXMgb25seSB3aGVuIHBhbm5pbmcgZW5kcy5cblx0XHQvLyBgdHJ1ZWAgYnkgZGVmYXVsdCBvbiBtb2JpbGUgYnJvd3NlcnMsIGluIG9yZGVyIHRvIGF2b2lkIHRvbyBtYW55IHJlcXVlc3RzIGFuZCBrZWVwIHNtb290aCBuYXZpZ2F0aW9uLlxuXHRcdC8vIGBmYWxzZWAgb3RoZXJ3aXNlIGluIG9yZGVyIHRvIGRpc3BsYXkgbmV3IHRpbGVzIF9kdXJpbmdfIHBhbm5pbmcsIHNpbmNlIGl0IGlzIGVhc3kgdG8gcGFuIG91dHNpZGUgdGhlXG5cdFx0Ly8gW2BrZWVwQnVmZmVyYF0oI2dyaWRsYXllci1rZWVwYnVmZmVyKSBvcHRpb24gaW4gZGVza3RvcCBicm93c2Vycy5cblx0XHR1cGRhdGVXaGVuSWRsZTogbW9iaWxlLFxuXG5cdFx0Ly8gQG9wdGlvbiB1cGRhdGVXaGVuWm9vbWluZzogQm9vbGVhbiA9IHRydWVcblx0XHQvLyBCeSBkZWZhdWx0LCBhIHNtb290aCB6b29tIGFuaW1hdGlvbiAoZHVyaW5nIGEgW3RvdWNoIHpvb21dKCNtYXAtdG91Y2h6b29tKSBvciBhIFtgZmx5VG8oKWBdKCNtYXAtZmx5dG8pKSB3aWxsIHVwZGF0ZSBncmlkIGxheWVycyBldmVyeSBpbnRlZ2VyIHpvb20gbGV2ZWwuIFNldHRpbmcgdGhpcyBvcHRpb24gdG8gYGZhbHNlYCB3aWxsIHVwZGF0ZSB0aGUgZ3JpZCBsYXllciBvbmx5IHdoZW4gdGhlIHNtb290aCBhbmltYXRpb24gZW5kcy5cblx0XHR1cGRhdGVXaGVuWm9vbWluZzogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlSW50ZXJ2YWw6IE51bWJlciA9IDIwMFxuXHRcdC8vIFRpbGVzIHdpbGwgbm90IHVwZGF0ZSBtb3JlIHRoYW4gb25jZSBldmVyeSBgdXBkYXRlSW50ZXJ2YWxgIG1pbGxpc2Vjb25kcyB3aGVuIHBhbm5pbmcuXG5cdFx0dXBkYXRlSW50ZXJ2YWw6IDIwMCxcblxuXHRcdC8vIEBvcHRpb24gekluZGV4OiBOdW1iZXIgPSAxXG5cdFx0Ly8gVGhlIGV4cGxpY2l0IHpJbmRleCBvZiB0aGUgdGlsZSBsYXllci5cblx0XHR6SW5kZXg6IDEsXG5cblx0XHQvLyBAb3B0aW9uIGJvdW5kczogTGF0TG5nQm91bmRzID0gdW5kZWZpbmVkXG5cdFx0Ly8gSWYgc2V0LCB0aWxlcyB3aWxsIG9ubHkgYmUgbG9hZGVkIGluc2lkZSB0aGUgc2V0IGBMYXRMbmdCb3VuZHNgLlxuXHRcdGJvdW5kczogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gbWluWm9vbTogTnVtYmVyID0gMFxuXHRcdC8vIFRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgZG93biB0byB3aGljaCB0aGlzIGxheWVyIHdpbGwgYmUgZGlzcGxheWVkIChpbmNsdXNpdmUpLlxuXHRcdG1pblpvb206IDAsXG5cblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IHVuZGVmaW5lZFxuXHRcdC8vIFRoZSBtYXhpbXVtIHpvb20gbGV2ZWwgdXAgdG8gd2hpY2ggdGhpcyBsYXllciB3aWxsIGJlIGRpc3BsYXllZCAoaW5jbHVzaXZlKS5cblx0XHRtYXhab29tOiB1bmRlZmluZWQsXG5cblx0XHQvLyBAb3B0aW9uIG1heE5hdGl2ZVpvb206IE51bWJlciA9IHVuZGVmaW5lZFxuXHRcdC8vIE1heGltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGhpZ2hlciB0aGFuIGBtYXhOYXRpdmVab29tYCB3aWxsIGJlIGxvYWRlZFxuXHRcdC8vIGZyb20gYG1heE5hdGl2ZVpvb21gIGxldmVsIGFuZCBhdXRvLXNjYWxlZC5cblx0XHRtYXhOYXRpdmVab29tOiB1bmRlZmluZWQsXG5cblx0XHQvLyBAb3B0aW9uIG1pbk5hdGl2ZVpvb206IE51bWJlciA9IHVuZGVmaW5lZFxuXHRcdC8vIE1pbmltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGxvd2VyIHRoYW4gYG1pbk5hdGl2ZVpvb21gIHdpbGwgYmUgbG9hZGVkXG5cdFx0Ly8gZnJvbSBgbWluTmF0aXZlWm9vbWAgbGV2ZWwgYW5kIGF1dG8tc2NhbGVkLlxuXHRcdG1pbk5hdGl2ZVpvb206IHVuZGVmaW5lZCxcblxuXHRcdC8vIEBvcHRpb24gbm9XcmFwOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBXaGV0aGVyIHRoZSBsYXllciBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgYW50aW1lcmlkaWFuLiBJZiBgdHJ1ZWAsIHRoZVxuXHRcdC8vIEdyaWRMYXllciB3aWxsIG9ubHkgYmUgZGlzcGxheWVkIG9uY2UgYXQgbG93IHpvb20gbGV2ZWxzLiBIYXMgbm9cblx0XHQvLyBlZmZlY3Qgd2hlbiB0aGUgW21hcCBDUlNdKCNtYXAtY3JzKSBkb2Vzbid0IHdyYXAgYXJvdW5kLiBDYW4gYmUgdXNlZFxuXHRcdC8vIGluIGNvbWJpbmF0aW9uIHdpdGggW2Bib3VuZHNgXSgjZ3JpZGxheWVyLWJvdW5kcykgdG8gcHJldmVudCByZXF1ZXN0aW5nXG5cdFx0Ly8gdGlsZXMgb3V0c2lkZSB0aGUgQ1JTIGxpbWl0cy5cblx0XHRub1dyYXA6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndGlsZVBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgZ3JpZCBsYXllciB3aWxsIGJlIGFkZGVkLlxuXHRcdH
14775BhbmU6ICd0aWxlUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcblx0XHQvLyBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgdGlsZSBsYXllci4gRW1wdHkgYnkgZGVmYXVsdC5cblx0XHRjbGFzc05hbWU6ICcnLFxuXG5cdFx0Ly8gQG9wdGlvbiBrZWVwQnVmZmVyOiBOdW1iZXIgPSAyXG5cdFx0Ly8gV2hlbiBwYW5uaW5nIHRoZSBtYXAsIGtlZXAgdGhpcyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgb2YgdGlsZXMgYmVmb3JlIHVubG9hZGluZyB0aGVtLlxuXHRcdGtlZXBCdWZmZXI6IDJcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9pbml0Q29udGFpbmVyKCk7XG5cblx0XHR0aGlzLl9sZXZlbHMgPSB7fTtcblx0XHR0aGlzLl90aWxlcyA9IHt9O1xuXG5cdFx0dGhpcy5fcmVzZXRWaWV3KCk7XG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0YmVmb3JlQWRkOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0bWFwLl9hZGRab29tTGltaXQodGhpcyk7XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdHJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG1hcC5fcmVtb3ZlWm9vbUxpbWl0KHRoaXMpO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG51bGw7XG5cdFx0dGhpcy5fdGlsZVpvb20gPSB1bmRlZmluZWQ7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQ6IHRoaXNcblx0Ly8gQnJpbmdzIHRoZSB0aWxlIGxheWVyIHRvIHRoZSB0b3Agb2YgYWxsIHRpbGUgbGF5ZXJzLlxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0b0Zyb250KHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWF4KTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjazogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIHRpbGUgbGF5ZXIgdG8gdGhlIGJvdHRvbSBvZiBhbGwgdGlsZSBsYXllcnMuXG5cdGJyaW5nVG9CYWNrOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSB0aWxlcyBmb3IgdGhpcyBsYXllci5cblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGh
14775pc1xuXHQvLyBDaGFuZ2VzIHRoZSBbb3BhY2l0eV0oI2dyaWRsYXllci1vcGFjaXR5KSBvZiB0aGUgZ3JpZCBsYXllci5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjZ3JpZGxheWVyLXppbmRleCkgb2YgdGhlIGdyaWQgbGF5ZXIuXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB6SW5kZXg7XG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KCk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzTG9hZGluZzogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhbnkgdGlsZSBpbiB0aGUgZ3JpZCBsYXllciBoYXMgbm90IGZpbmlzaGVkIGxvYWRpbmcuXG5cdGlzTG9hZGluZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sb2FkaW5nO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3OiB0aGlzXG5cdC8vIENhdXNlcyB0aGUgbGF5ZXIgdG8gY2xlYXIgYWxsIHRoZSB0aWxlcyBhbmQgcmVxdWVzdCB0aGVtIGFnYWluLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cHJlcmVzZXQ6IHRoaXMuX2ludmFsaWRhdGVBbGwsXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdHpvb206IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdG1vdmVlbmQ6IHRoaXMuX29uTW92ZUVuZFxuXHRcdH07XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSkge1xuXHRcdFx0Ly8gdXBkYXRlIHRpbGVzIG9uIG1vdmUsIGJ1dCBub3QgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIGdpdmVuIGludGVydmFsXG5cdFx0XHRpZiAoIXRoaXMuX29uTW92ZSkge1xuXHRcdFx0XHR0aGlzLl9vbk1vdmUgPSB0aHJvdHRsZSh0aGlzLl9vbk1vdmVFbmQsIHRoaXMub3B0aW9ucy51cGRhdGVJbnRlcnZhbCwgdGhpcyk7XG5cdFx0XHR9XG5cblx0XHRcdGV2ZW50cy5tb3ZlID0gdGhpcy5fb25Nb3ZlO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX2FuaW1hdGVab29tO1xuXHRcdH1cblxuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0Ly8gQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcblx0Ly8gTGF5ZXJzIGV4dGVuZGluZyBgR3JpZExheWVyYCBzaGFsbCByZWltcGxlbWVudCB0aGUgZm9sbG93aW5nIG1ldGhvZC5cblx0Ly8gQG1ldGhvZCBjcmVhdGVUaWxlKGNvb3JkczogT2JqZWN0LCBkb25lPzogRnVuY3Rpb24pOiBIVE1MRWxlbWVudFxuXHQvLyBDYWxsZWQgb25seSBpbnRlcm5hbGx5LCBtdXN0IGJlIG92ZXJyaWRkZW4gYnkgY2xhc3NlcyBleHRlbmRpbmcgYEdyaWRMYXllcmAuXG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gYGNvb3Jkc2AuIElmIHRoZSBgZG9uZWAgY2FsbGJhY2tcblx0Ly8gaXMgc3BlY2lmaWVkLCBpdCBtdXN0IGJlIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBmaW5pc2hlZCBsb2FkaW5nIGFuZCBkcmF3aW5nLlxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVNpemU6IFBvaW50XG5cdC8vIE5vcm1hbGl6ZXMgdGhlIFt0aWxlU2l6ZSBvcHRpb25dKCNncmlkbGF5ZXItdGlsZXNpemUpIGludG8gYSBwb2ludC4gVXNlZCBieSB0aGUgYGNyZWF0ZVRpbGUoKWAgbWV0aG9kLlxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzID0gdGhpcy5vcHRpb25zLnRpbGVTaXplO1xuXHRcdHJldHVybiBzIGluc3RhbmNlb2YgUG9pbnQgPyBzIDogbmV3IFBvaW50KHMsIHMpO1xuXHR9LFxuXG5cdF91cGRhdGVaSW5kZXg6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fY29udGFpbmVyICYmIHRoaXMub3B0aW9ucy56SW5kZXggIT09IHVuZGVmaW5lZCAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSBudWxsKSB7XG5cdFx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUuekluZGV4ID0gdGhpcy5vcHRpb25zLnpJbmRleDtcblx0XHR9XG5cdH0sXG5cblx0X3NldEF1dG9aSW5kZXg6IGZ1bmN0aW9uIChjb21wYXJlKSB7XG5cdFx0Ly8gZ28gdGhyb3VnaCBhbGwgb3RoZXIgbGF5ZXJzIG9mIHRoZSBzYW1lIHBhbmUsIHNldCB6SW5kZXggdG8gbWF4ICsgMSAoZnJvbnQpIG9yIG1pbiAtIDEgKGJhY2spXG5cblx0XHR2YXIgbGF5ZXJzID0gdGhpcy5nZXRQYW5lKCkuY2hpbGRyZW4sXG5cdFx0ICAgIGVkZ2VaSW5kZXggPSAtY29tcGFyZSgtSW5maW5pdHksIEluZmluaXR5KTsgLy8gLUluZmluaXR5IGZvciBtYXgsIEluZmluaXR5IGZvciBtaW5cblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXllcnMubGVuZ3RoLCB6SW5kZXg7IGkgPCBsZW47IGkrKykge1xuXG5cdFx0XHR6SW5kZXggPSBsYXllcnNbaV0uc3R5bGUuekluZGV4O1xuXG5cdFx0XHRpZiAobGF5ZXJzW2ldICE9PSB0aGlzLl9jb250YWluZXIgJiYgekluZGV4KSB7XG5cdFx0XHRcdGVkZ2VaSW5kZXggPSBjb21wYXJlKGVkZ2VaSW5kZXgsICt6SW5kZXgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmIChpc0Zpbml0ZShlZGdlWkluZGV4KSkge1xuXHRcdFx0dGhpcy5vcHRpb25zLnpJbmRleCA9IGVkZ2VaSW5kZXggKyBjb21wYXJlKC0xLCAxKTtcblx0XHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlT3BhY2l0eTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxuXG5cdFx0Ly8gSUUgZG9lc24ndCBpbmhlcml0IGZpbHRlciBvcGFjaXR5IHByb3Blcmx5LCBzbyB3ZSdyZSBmb3JjZWQgdG8gc2V0IGl0IG9uIHRpbGVzXG5cdFx0aWYgKGllbHQ5KSB7IHJldHVybjsgfVxuXG5cdFx0c2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdHZhciBub3cgP
14775SArbmV3IERhdGUoKSxcblx0XHQgICAgbmV4dEZyYW1lID0gZmFsc2UsXG5cdFx0ICAgIHdpbGxQcnVuZSA9IGZhbHNlO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0XHRpZiAoIXRpbGUuY3VycmVudCB8fCAhdGlsZS5sb2FkZWQpIHsgY29udGludWU7IH1cblxuXHRcdFx0dmFyIGZhZGUgPSBNYXRoLm1pbigxLCAobm93IC0gdGlsZS5sb2FkZWQpIC8gMjAwKTtcblxuXHRcdFx0c2V0T3BhY2l0eSh0aWxlLmVsLCBmYWRlKTtcblx0XHRcdGlmIChmYWRlIDwgMSkge1xuXHRcdFx0XHRuZXh0RnJhbWUgPSB0cnVlO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0aWYgKHRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdFx0d2lsbFBydW5lID0gdHJ1ZTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aGlzLl9vbk9wYXF1ZVRpbGUodGlsZSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0dGlsZS5hY3RpdmUgPSB0cnVlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICh3aWxsUHJ1bmUgJiYgIXRoaXMuX25vUHJ1bmUpIHsgdGhpcy5fcHJ1bmVUaWxlcygpOyB9XG5cblx0XHRpZiAobmV4dEZyYW1lKSB7XG5cdFx0XHRjYW5jZWxBbmltRnJhbWUodGhpcy5fZmFkZUZyYW1lKTtcblx0XHRcdHRoaXMuX2ZhZGVGcmFtZSA9IHJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fVxuXHR9LFxuXG5cdF9vbk9wYXF1ZVRpbGU6IGZhbHNlRm4sXG5cblx0X2luaXRDb250YWluZXI6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fY29udGFpbmVyKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fY29udGFpbmVyID0gY3JlYXRlJDEoJ2RpdicsICdsZWFmbGV0LWxheWVyICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdH0sXG5cblx0X3VwZGF0ZUxldmVsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcblx0XHQgICAgbWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xuXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG5cblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0aWYgKHRoaXMuX2xldmVsc1t6XS5lbC5jaGlsZHJlbi5sZW5ndGggfHwgeiA9PT0gem9vbSkge1xuXHRcdFx0XHR0aGlzLl9sZXZlbHNbel0uZWwuc3R5bGUuekluZGV4ID0gbWF4Wm9vbSAtIE1hdGguYWJzKHpvb20gLSB6KTtcblx0XHRcdFx0dGhpcy5fb25VcGRhdGVMZXZlbCh6KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlbW92ZSh0aGlzLl9sZXZlbHNbel0uZWwpO1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlc0F0Wm9vbSh6KTtcblx0XHRcdFx0dGhpcy5fb25SZW1vdmVMZXZlbCh6KTtcblx0XHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR2YXIgbGV2ZWwgPSB0aGlzLl9sZXZlbHNbem9vbV0sXG5cdFx0ICAgIG1hcCA9IHRoaXMuX21hcDtcblxuXHRcdGlmICghbGV2ZWwpIHtcblx0XHRcdGxldmVsID0gdGhpcy5fbGV2ZWxzW3pvb21dID0ge307XG5cblx0XHRcdGxldmVsLmVsID0gY3JlYXRlJDEoJ2RpdicsICdsZWFmbGV0LXRpbGUtY29udGFpbmVyIGxlYWZsZXQtem9vbS1hbmltYXRlZCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRsZXZlbC5lbC5zdHlsZS56SW5kZXggPSBtYXhab29tO1xuXG5cdFx0XHRsZXZlbC5vcmlnaW4gPSBtYXAucHJvamVjdChtYXAudW5wcm9qZWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKSwgem9vbSkucm91bmQoKTtcblx0XHRcdGxldmVsLnpvb20gPSB6b29tO1xuXG5cdFx0XHR0aGlzLl9zZXRab29tVHJhbnNmb3JtKGxldmVsLCBtYXAuZ2V0Q2VudGVyKCksIG1hcC5nZXRab29tKCkpO1xuXG5cdFx0XHQvLyBmb3JjZSB0aGUgYnJvd3NlciB0byBjb25zaWRlciB0aGUgbmV3bHkgYWRkZWQgZWxlbWVudCBmb3IgdHJhbnNpdGlvblxuXHRcdFx0ZmFsc2VGbihsZXZlbC5lbC5vZmZzZXRXaWR0aCk7XG5cblx0XHRcdHRoaXMuX29uQ3JlYXRlTGV2ZWwobGV2ZWwpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xldmVsID0gbGV2ZWw7XG5cblx0XHRyZXR1cm4gbGV2ZWw7XG5cdH0sXG5cblx0X29uVXBkYXRlTGV2ZWw6IGZhbHNlRm4sXG5cblx0X29uUmVtb3ZlTGV2ZWw6IGZhbHNlRm4sXG5cblx0X29uQ3JlYXRlTGV2ZWw6IGZhbHNlRm4sXG5cblx0X3BydW5lVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBrZXksIHRpbGU7XG5cblx0XHR2YXIgem9vbSA9IHRoaXMuX21hcC5nZXRab29tKCk7XG5cdFx0aWYgKHpvb20gPiB0aGlzLm9wdGlvbnMubWF4Wm9vbSB8fFxuXHRcdFx0em9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdHRpbGUucmV0YWluID0gdGlsZS5jdXJyZW50O1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdGlmICh0aWxlLmN1cnJlbnQgJiYgIXRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSB0aWxlLmNvb3Jkcztcblx0XHRcdFx0aWYgKCF0aGlzLl9yZXRhaW5QYXJlbnQoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueiwgY29vcmRzLnogLSA1KSkge1xuXHRcdFx0XHRcdHRoaXMuX3JldGFpbkNoaWxkcmVuKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnosIGNvb3Jkcy56ICsgMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRmb3IgKGtleSBpbiB0aGlzLl90
14775aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLnJldGFpbikge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlKGtleSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVUaWxlc0F0Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdGlmICh0aGlzLl90aWxlc1trZXldLmNvb3Jkcy56ICE9PSB6b29tKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlVGlsZShrZXkpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlQWxsVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHRoaXMuX3JlbW92ZVRpbGUoa2V5KTtcblx0XHR9XG5cdH0sXG5cblx0X2ludmFsaWRhdGVBbGw6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0cmVtb3ZlKHRoaXMuX2xldmVsc1t6XS5lbCk7XG5cdFx0XHR0aGlzLl9vblJlbW92ZUxldmVsKHopO1xuXHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHR9XG5cdFx0dGhpcy5fcmVtb3ZlQWxsVGlsZXMoKTtcblxuXHRcdHRoaXMuX3RpbGVab29tID0gdW5kZWZpbmVkO1xuXHR9LFxuXG5cdF9yZXRhaW5QYXJlbnQ6IGZ1bmN0aW9uICh4LCB5LCB6LCBtaW5ab29tKSB7XG5cdFx0dmFyIHgyID0gTWF0aC5mbG9vcih4IC8gMiksXG5cdFx0ICAgIHkyID0gTWF0aC5mbG9vcih5IC8gMiksXG5cdFx0ICAgIHoyID0geiAtIDEsXG5cdFx0ICAgIGNvb3JkczIgPSBuZXcgUG9pbnQoK3gyLCAreTIpO1xuXHRcdGNvb3JkczIueiA9ICt6MjtcblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzMiksXG5cdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0aWYgKHRpbGUgJiYgdGlsZS5hY3RpdmUpIHtcblx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSBlbHNlIGlmICh0aWxlICYmIHRpbGUubG9hZGVkKSB7XG5cdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0fVxuXG5cdFx0aWYgKHoyID4gbWluWm9vbSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3JldGFpblBhcmVudCh4MiwgeTIsIHoyLCBtaW5ab29tKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X3JldGFpbkNoaWxkcmVuOiBmdW5jdGlvbiAoeCwgeSwgeiwgbWF4Wm9vbSkge1xuXG5cdFx0Zm9yICh2YXIgaSA9IDIgKiB4OyBpIDwgMiAqIHggKyAyOyBpKyspIHtcblx0XHRcdGZvciAodmFyIGogPSAyICogeTsgaiA8IDIgKiB5ICsgMjsgaisrKSB7XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBQb2ludChpLCBqKTtcblx0XHRcdFx0Y29vcmRzLnogPSB6ICsgMTtcblxuXHRcdFx0XHR2YXIga2V5ID0gdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3JkcyksXG5cdFx0XHRcdCAgICB0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblxuXHRcdFx0XHRpZiAodGlsZSAmJiB0aWxlLmFjdGl2ZSkge1xuXHRcdFx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdFx0XHRjb250aW51ZTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKHRpbGUgJiYgdGlsZS5sb2FkZWQpIHtcblx0XHRcdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoeiArIDEgPCBtYXhab29tKSB7XG5cdFx0XHRcdFx0dGhpcy5fcmV0YWluQ2hpbGRyZW4oaSwgaiwgeiArIDEsIG1heFpvb20pO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldFZpZXc6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGFuaW1hdGluZyA9IGUgJiYgKGUucGluY2ggfHwgZS5mbHlUbyk7XG5cdFx0dGhpcy5fc2V0Vmlldyh0aGlzLl9tYXAuZ2V0Q2VudGVyKCksIHRoaXMuX21hcC5nZXRab29tKCksIGFuaW1hdGluZywgYW5pbWF0aW5nKTtcblx0fSxcblxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XG5cdFx0dGhpcy5fc2V0VmlldyhlLmNlbnRlciwgZS56b29tLCB0cnVlLCBlLm5vVXBkYXRlKTtcblx0fSxcblxuXHRfY2xhbXBab29tOiBmdW5jdGlvbiAoem9vbSkge1xuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0aWYgKHVuZGVmaW5lZCAhPT0gb3B0aW9ucy5taW5OYXRpdmVab29tICYmIHpvb20gPCBvcHRpb25zLm1pbk5hdGl2ZVpvb20pIHtcblx0XHRcdHJldHVybiBvcHRpb25zLm1pbk5hdGl2ZVpvb207XG5cdFx0fVxuXG5cdFx0aWYgKHVuZGVmaW5lZCAhPT0gb3B0aW9ucy5tYXhOYXRpdmVab29tICYmIG9wdGlvbnMubWF4TmF0aXZlWm9vbSA8IHpvb20pIHtcblx0XHRcdHJldHVybiBvcHRpb25zLm1heE5hdGl2ZVpvb207XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHpvb207XG5cdH0sXG5cblx0X3NldFZpZXc6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIG5vUHJ1bmUsIG5vVXBkYXRlKSB7XG5cdFx0dmFyIHRpbGVab29tID0gdGhpcy5fY2xhbXBab29tKE1hdGgucm91bmQoem9vbSkpO1xuXHRcdGlmICgodGhpcy5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB0aWxlWm9vbSA+IHRoaXMub3B0aW9ucy5tYXhab29tKSB8fFxuXHRcdCAgICAodGhpcy5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB0aWxlWm9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSkge1xuXHRcdFx0dGlsZVpvb20gPSB1bmRlZmluZWQ7XG5cdFx0fVxuXG5cdFx0dmFyIHRpbGVab29tQ2hhbmdlZCA9IHRoaXMub3B0aW9ucy51cGRhdGVXaGVuWm9vbWluZyAmJiAodGlsZVpvb20gIT09IHRoaXMuX3RpbGVab29tKTtcblxuXHRcdGlmICghbm9VcGRhdGUgfHwgdGlsZVpvb21DaGFuZ2VkKSB7XG5cblx0XHRcdHRoaXMuX3RpbGVab29tID0gdGlsZVpvb207XG5cblx0XHRcdGlmICh0aGlzLl9hYm9ydExvYWRpbmcpIHtcblx0XHRcdFx0dGhpcy5fYWJvcnRMb2FkaW5nKCk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3VwZGF0ZUxldmVscygpO1xuXHRcdFx0dGhpcy5fcmVzZXRHcmlkKCk7XG5cblx0XHRcdGlmICh0aWxlWm9vbSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRcdHRoaXMuX3VwZGF0ZShjZW50ZXIpO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIW5vUHJ1bmUpIHtcblx0XHRcdFx0dGhpcy5fcHJ1bmVUaWxlcygpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBGbGFnIHRvIHByZXZlbnQgX3VwZGF0ZU9wYWNpdHkgZnJvbSBwcnVuaW5nIHRpbGVzIGR1cmluZ1xuXHRcdFx0Ly8gYSB6b29tIGFuaW0gb3IgYSBwaW5jaCBnZXN0dXJlXG5cdFx0XHR0aGlzLl9ub1BydW5lID0gISFub1BydW5lO1xuXHRcdH1cblxuXHRcdHRoaXMuX3NldFpvb21UcmFuc2Zvcm1zKGNlbnRlciwgem9vbSk7XG5cdH0sXG5cblx0X3NldFpvb21UcmFuc2Zvcm1zOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sZXZlbHMpIHtcblx0XHRcdHRoaXMuX3NldFpvb21UcmFuc2Zvcm0odGhpcy5fbGV2ZWxzW2ldLCBjZW50ZXIsIHpvb20pO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0Wm9vbVRyYW5zZm9ybTogZnVuY3Rpb24gKGxldmVsLCBjZW50ZXIsIHpvb20pIHtcblx0XHR2YXIgc2NhbGUgPSB0aGlzLl9tYXAuZ2V0Wm9vbVNjYWxlKHpvb20sIGxldmVsLnpvb20pLFxuXHRcdCAgICB0cmFuc2xhdGUgPSBsZXZlbC5vcmlnaW4ubXVsdGlwbHlCeShzY2FsZSlcblx0XHQgICAgICAgIC5zdWJ0cmFjdCh0aGlzLl9tYXAuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSkpLnJvdW5kKCk7XG5cblx0XHRpZiAoYW55M2QpIHtcblx0XHRcdHNldFRyYW5zZm9ybShsZXZlbC5lbCwgdHJhbnNsYXRlLCBzY2FsZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHNldFBvc2l0aW9uKGxldmVsLmVsLCB0cmFuc2xhdGUpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVzZXRHcmlkOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgY3JzID0gbWFwLm9wdGlvbnMuY3JzLFxuXHRcdCAgICB0aWxlU2l6ZSA9IHRoaXMuX3RpbGVTaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpLFxuXHRcdCAgICB0aWxlWm9vbSA9IHRoaXMuX3RpbGVab29tO1xuXG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuX21hcC5nZXRQaXhlbFdvcmxkQm91bmRzKHRoaXMuX3RpbGVab29tKTtcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR0aGlzLl9nbG9iYWxUaWxlUmFuZ2UgPSB0aGlzLl9weEJvdW5kc1RvVGlsZVJhbmdlKGJvdW5kcyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fd3JhcFggPSBjcnMud3JhcExuZyAmJiAhdGhpcy5vcHRpb25zLm5vV3JhcCAmJiBbXG5cdFx0XHRNYXRoLmZsb29yKG1hcC5wcm9qZWN0KFswLCBjcnMud3JhcExuZ1swXV0sIHRpbGVab29tKS54IC8gdGlsZVNpemUueCksXG5cdFx0XHRNYXRoLmNlaWwobWFwLnByb2plY3QoWzAsIGNycy53cmFwTG5nWzFdXSwgdGlsZVpvb20pLnggLyB0aWxlU2l6ZS55KVxuXHRcdF07XG5cdFx0dGhpcy5fd3JhcFkgPSBjcnMud3JhcExhdCAmJiAhdGhpcy5vcHRpb25zLm5vV3JhcCAmJiBbXG5cdFx0XHRNYXRoLmZsb29yKG1hcC5wcm9qZWN0KFtjcnMud3JhcExhdFswXSwgMF0sIHRpbGVab29tKS55IC8gdGlsZVNpemUueCksXG5cdFx0XHRNYXRoLmNlaWwobWFwLnByb2plY3QoW2Nycy53cmFwTGF0WzFdLCAwXSwgdGlsZVpvb20pLnkgLyB0aWxlU2l6ZS55KVxuXHRcdF07XG5cdH0sXG5cblx0X29uTW92ZUVuZDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fbWFwIHx8IHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF9nZXRUaWxlZFBpeGVsQm91bmRzOiBmdW5jdGlvbiAoY2VudGVyKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgbWFwWm9vbSA9IG1hcC5fYW5pbWF0aW5nWm9vbSA/IE1hdGgubWF4KG1hcC5fYW5pbWF0ZVRvWm9vbSwgbWFwLmdldFpvb20oKSkgOiBtYXAuZ2V0Wm9vbSgpLFxuXHRcdCAgICBzY2FsZSA9IG1hcC5nZXRab29tU2NhbGUobWFwWm9vbSwgdGhpcy5fdGlsZVpvb20pLFxuXHRcdCAgICBwaXhlbENlbnRlciA9IG1hcC5wcm9qZWN0KGNlbnRlciwgdGhpcy5fdGlsZVpvb20pLmZsb29yKCksXG5cdFx0ICAgIGhhbGZTaXplID0gbWFwLmdldFNpemUoKS5kaXZpZGVCeShzY2FsZSAqIDIpO1xuXG5cdFx0cmV0dXJuIG5ldyBCb3VuZHMocGl4ZWxDZW50ZXIuc3VidHJhY3QoaGFsZlNpemUpLCBwaXhlbENlbnRlci5hZGQoaGFsZlNpemUpKTtcblx0fSxcblxuXHQvLyBQcml2YXRlIG1ldGhvZCB0byBsb2FkIHRpbGVzIGluIHRoZSBncmlkJ3MgYWN0aXZlIHpvb20gbGV2ZWwgYWNjb3JkaW5nIHRvIG1hcCBib3VuZHNcblx0X3VwZGF0ZTogZnVuY3Rpb24gKGNlbnRlcikge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cdFx0aWYgKCFtYXApIHsgcmV0dXJuOyB9XG5cdFx0dmFyIHpvb20gPSB0aGlzLl9jbGFtcFpvb20obWFwLmdldFpvb20oKSk7XG5cblx0XHRpZiAoY2VudGVyID09PSB1bmRlZmluZWQpIHsgY2VudGVyID0gbWFwLmdldENlbnRlcigpOyB9XG5cdFx0aWYgKHRoaXMuX3RpbGVab29tID09PSB1bmRlZmluZWQpIHsgcmV0dXJuOyB9XHQvLyBpZiBvdXQgb2YgbWluem9vbS9tYXh6b29tXG5cblx0XHR2YXIgcGl4ZWxCb3VuZHMgPSB0aGlzLl9nZXRUaWxlZFBpeGVsQm91bmRzKGNlbnRlciksXG5cdFx0ICAgIHRpbGVSYW5nZSA9IHRoaXMuX3B4Qm91bmRzVG9UaWxlUmFuZ2UocGl4ZWxCb3VuZHMpLFxuXHRcdCAgICB0aWxlQ2VudGVyID0gdGlsZVJhbmdlLmdldENlbnRlcigpLFxuXHRcdCAgICBxdWV1ZSA9IFtdLFxuXHRcdCAgICBtYXJnaW4gPSB0aGlzLm9wdGlvbnMua2VlcEJ1ZmZlcixcblx0XHQgICAgbm9QcnVuZVJhbmdlID0gbmV3IEJvdW5kcyh0aWxlUmFuZ2UuZ2V0Qm90dG9tTGVmdCgpLnN1YnRyYWN0KFttYXJnaW4sIC1tYXJnaW5dKSxcblx0XHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWxlUmFuZ2UuZ2V0VG9wUmlnaHQoKS5hZGQoW21hcmdpbiwgLW1hcmdpbl0pKTtcblxuXHRcdC8vIFNhbml0eSBjaGVjazogcGFuaWMgaWYgdGhlIHRpbGUgcmFuZ2UgY29udGFpbnMgSW5maW5pdHkgc29tZXdoZXJlLlxuXHRcdGlmICghKGlzRmluaXRlKHRpbGVSYW5nZS5taW4ueCkgJiZcblx0XHQgICAgICBpc0Zpbml0ZSh0aWxlUmFuZ2UubWluLnkpICYmXG5cdFx0ICAgICAgaXNGaW5pdGUodGlsZVJhbmdlLm1heC54KSAmJlxuXHRcdCAgICAgIGlzRmluaXRlKHRpbGVSYW5nZS5tYXgueSkpKSB7IHRocm93IG5ldyBFcnJvcignQXR0ZW1wdGVkIHRvIGxvYWQgYW4gaW5maW5pdGUgbnVtYmVyIG9mIHRpbGVzJyk7IH1cblxuXHRcdGZvciAodmFyIGtleSBpbiB0aGlzLl90
14775aWxlcykge1xuXHRcdFx0dmFyIGMgPSB0aGlzLl90aWxlc1trZXldLmNvb3Jkcztcblx0XHRcdGlmIChjLnogIT09IHRoaXMuX3RpbGVab29tIHx8ICFub1BydW5lUmFuZ2UuY29udGFpbnMobmV3IFBvaW50KGMueCwgYy55KSkpIHtcblx0XHRcdFx0dGhpcy5fdGlsZXNba2V5XS5jdXJyZW50ID0gZmFsc2U7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gX3VwZGF0ZSBqdXN0IGxvYWRzIG1vcmUgdGlsZXMuIElmIHRoZSB0aWxlIHpvb20gbGV2ZWwgZGlmZmVycyB0b28gbXVjaFxuXHRcdC8vIGZyb20gdGhlIG1hcCdzLCBsZXQgX3NldFZpZXcgcmVzZXQgbGV2ZWxzIGFuZCBwcnVuZSBvbGQgdGlsZXMuXG5cdFx0aWYgKE1hdGguYWJzKHpvb20gLSB0aGlzLl90aWxlWm9vbSkgPiAxKSB7IHRoaXMuX3NldFZpZXcoY2VudGVyLCB6b29tKTsgcmV0dXJuOyB9XG5cblx0XHQvLyBjcmVhdGUgYSBxdWV1ZSBvZiBjb29yZGluYXRlcyB0byBsb2FkIHRpbGVzIGZyb21cblx0XHRmb3IgKHZhciBqID0gdGlsZVJhbmdlLm1pbi55OyBqIDw9IHRpbGVSYW5nZS5tYXgueTsgaisrKSB7XG5cdFx0XHRmb3IgKHZhciBpID0gdGlsZVJhbmdlLm1pbi54OyBpIDw9IHRpbGVSYW5nZS5tYXgueDsgaSsrKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSBuZXcgUG9pbnQoaSwgaik7XG5cdFx0XHRcdGNvb3Jkcy56ID0gdGhpcy5fdGlsZVpvb207XG5cblx0XHRcdFx0aWYgKCF0aGlzLl9pc1ZhbGlkVGlsZShjb29yZHMpKSB7IGNvbnRpbnVlOyB9XG5cblx0XHRcdFx0dmFyIHRpbGUgPSB0aGlzLl90aWxlc1t0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKV07XG5cdFx0XHRcdGlmICh0aWxlKSB7XG5cdFx0XHRcdFx0dGlsZS5jdXJyZW50ID0gdHJ1ZTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRxdWV1ZS5wdXNoKGNvb3Jkcyk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBzb3J0IHRpbGUgcXVldWUgdG8gbG9hZCB0aWxlcyBpbiBvcmRlciBvZiB0aGVpciBkaXN0YW5jZSB0byBjZW50ZXJcblx0XHRxdWV1ZS5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7XG5cdFx0XHRyZXR1cm4gYS5kaXN0YW5jZVRvKHRpbGVDZW50ZXIpIC0gYi5kaXN0YW5jZVRvKHRpbGVDZW50ZXIpO1xuXHRcdH0pO1xuXG5cdFx0aWYgKHF1ZXVlLmxlbmd0aCAhPT0gMCkge1xuXHRcdFx0Ly8gaWYgaXQncyB0aGUgZmlyc3QgYmF0Y2ggb2YgdGlsZXMgdG8gbG9hZFxuXHRcdFx0aWYgKCF0aGlzLl9sb2FkaW5nKSB7XG5cdFx0XHRcdHRoaXMuX2xvYWRpbmcgPSB0cnVlO1xuXHRcdFx0XHQvLyBAZXZlbnQgbG9hZGluZzogRXZlbnRcblx0XHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGUgZ3JpZCBsYXllciBzdGFydHMgbG9hZGluZyB0aWxlcy5cblx0XHRcdFx0dGhpcy5maXJlKCdsb2FkaW5nJyk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIGNyZWF0ZSBET00gZnJhZ21lbnQgdG8gYXBwZW5kIHRpbGVzIGluIG9uZSBiYXRjaFxuXHRcdFx0dmFyIGZyYWdtZW50ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuXG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgcXVldWUubGVuZ3RoOyBpKyspIHtcblx0XHRcdFx0dGhpcy5fYWRkVGlsZShxdWV1ZVtpXSwgZnJhZ21lbnQpO1xuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLl9sZXZlbC5lbC5hcHBlbmRDaGlsZChmcmFnbWVudCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pc1ZhbGlkVGlsZTogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHZhciBjcnMgPSB0aGlzLl9tYXAub3B0aW9ucy5jcnM7XG5cblx0XHRpZiAoIWNycy5pbmZpbml0ZSkge1xuXHRcdFx0Ly8gZG9uJ3QgbG9hZCB0aWxlIGlmIGl0J3Mgb3V0IG9mIGJvdW5kcyBhbmQgbm90IHdyYXBwZWRcblx0XHRcdHZhciBib3VuZHMgPSB0aGlzLl9nbG9iYWxUaWxlUmFuZ2U7XG5cdFx0XHRpZiAoKCFjcnMud3JhcExuZyAmJiAoY29vcmRzLnggPCBib3VuZHMubWluLnggfHwgY29vcmRzLnggPiBib3VuZHMubWF4LngpKSB8fFxuXHRcdFx0ICAgICghY3JzLndyYXBMYXQgJiYgKGNvb3Jkcy55IDwgYm91bmRzLm1pbi55IHx8IGNvb3Jkcy55ID4gYm91bmRzLm1heC55KSkpIHsgcmV0dXJuIGZhbHNlOyB9XG5cdFx0fVxuXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMuYm91bmRzKSB7IHJldHVybiB0cnVlOyB9XG5cblx0XHQvLyBkb24ndCBsb2FkIHRpbGUgaWYgaXQgZG9lc24ndCBpbnRlcnNlY3QgdGhlIGJvdW5kcyBpbiBvcHRpb25zXG5cdFx0dmFyIHRpbGVCb3VuZHMgPSB0aGlzLl90aWxlQ29vcmRzVG9Cb3VuZHMoY29vcmRzKTtcblx0XHRyZXR1cm4gdG9MYXRMbmdCb3VuZHModGhpcy5vcHRpb25zLmJvdW5kcykub3ZlcmxhcHModGlsZUJvdW5kcyk7XG5cdH0sXG5cblx0X2tleVRvQm91bmRzOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0cmV0dXJuIHRoaXMuX3RpbGVDb29yZHNUb0JvdW5kcyh0aGlzLl9rZXlUb1RpbGVDb29yZHMoa2V5KSk7XG5cdH0sXG5cblx0X3RpbGVDb29yZHNUb053U2U6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKSxcblx0XHQgICAgbndQb2ludCA9IGNvb3Jkcy5zY2FsZUJ5KHRpbGVTaXplKSxcblx0XHQgICAgc2VQb2ludCA9IG53UG9pbnQuYWRkKHRpbGVTaXplKSxcblx0XHQgICAgbncgPSBtYXAudW5wcm9qZWN0KG53UG9pbnQsIGNvb3Jkcy56KSxcblx0XHQgICAgc2UgPSBtYXAudW5wcm9qZWN0KHNlUG9pbnQsIGNvb3Jkcy56KTtcblx0XHRyZXR1cm4gW253LCBzZV07XG5cdH0sXG5cblx0Ly8gY29udmVydHMgdGlsZSBjb29yZGluYXRlcyB0byBpdHMgZ2VvZ3JhcGhpY2FsIGJvdW5kc1xuXHRfdGlsZUNvb3Jkc1RvQm91bmRzOiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0dmFyIGJwID0gdGhpcy5fdGlsZUNvb3Jkc1RvTndTZShjb29yZHMpLFxuXHRcdCAgICBib3VuZHMgPSBuZXcgTGF0TG5nQm91bmRzKGJwWzBdLCBicFsxXSk7XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy5ub1dyYXApIHtcblx0XHRcdGJvdW5kcyA9IHRoaXMuX21hcC53cmFwTGF0TG5nQm91bmRzKGJvdW5kcyk7XG5cdFx0fVxuXHRcdHJldHVybi
14775Bib3VuZHM7XG5cdH0sXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8ga2V5IGZvciB0aGUgdGlsZSBjYWNoZVxuXHRfdGlsZUNvb3Jkc1RvS2V5OiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0cmV0dXJuIGNvb3Jkcy54ICsgJzonICsgY29vcmRzLnkgKyAnOicgKyBjb29yZHMuejtcblx0fSxcblxuXHQvLyBjb252ZXJ0cyB0aWxlIGNhY2hlIGtleSB0byBjb29yZGluYXRlc1xuXHRfa2V5VG9UaWxlQ29vcmRzOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIGsgPSBrZXkuc3BsaXQoJzonKSxcblx0XHQgICAgY29vcmRzID0gbmV3IFBvaW50KCtrWzBdLCAra1sxXSk7XG5cdFx0Y29vcmRzLnogPSAra1syXTtcblx0XHRyZXR1cm4gY29vcmRzO1xuXHR9LFxuXG5cdF9yZW1vdmVUaWxlOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cblxuXHRcdHJlbW92ZSh0aWxlLmVsKTtcblxuXHRcdGRlbGV0ZSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0Ly8gQGV2ZW50IHRpbGV1bmxvYWQ6IFRpbGVFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGlzIHJlbW92ZWQgKGUuZy4gd2hlbiBhIHRpbGUgZ29lcyBvZmYgdGhlIHNjcmVlbikuXG5cdFx0dGhpcy5maXJlKCd0aWxldW5sb2FkJywge1xuXHRcdFx0dGlsZTogdGlsZS5lbCxcblx0XHRcdGNvb3JkczogdGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSlcblx0XHR9KTtcblx0fSxcblxuXHRfaW5pdFRpbGU6IGZ1bmN0aW9uICh0aWxlKSB7XG5cdFx0YWRkQ2xhc3ModGlsZSwgJ2xlYWZsZXQtdGlsZScpO1xuXG5cdFx0dmFyIHRpbGVTaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuXHRcdHRpbGUuc3R5bGUud2lkdGggPSB0aWxlU2l6ZS54ICsgJ3B4Jztcblx0XHR0aWxlLnN0eWxlLmhlaWdodCA9IHRpbGVTaXplLnkgKyAncHgnO1xuXG5cdFx0dGlsZS5vbnNlbGVjdHN0YXJ0ID0gZmFsc2VGbjtcblx0XHR0aWxlLm9ubW91c2Vtb3ZlID0gZmFsc2VGbjtcblxuXHRcdC8vIHVwZGF0ZSBvcGFjaXR5IG9uIHRpbGVzIGluIElFNy04IGJlY2F1c2Ugb2YgZmlsdGVyIGluaGVyaXRhbmNlIHByb2JsZW1zXG5cdFx0aWYgKGllbHQ5ICYmIHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0c2V0T3BhY2l0eSh0aWxlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XG5cdFx0fVxuXG5cdFx0Ly8gd2l0aG91dCB0aGlzIGhhY2ssIHRpbGVzIGRpc2FwcGVhciBhZnRlciB6b29tIG9uIENocm9tZSBmb3IgQW5kcm9pZFxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzIwNzhcblx0XHRpZiAoYW5kcm9pZCAmJiAhYW5kcm9pZDIzKSB7XG5cdFx0XHR0aWxlLnN0eWxlLldlYmtpdEJhY2tmYWNlVmlzaWJpbGl0eSA9ICdoaWRkZW4nO1xuXHRcdH1cblx0fSxcblxuXHRfYWRkVGlsZTogZnVuY3Rpb24gKGNvb3JkcywgY29udGFpbmVyKSB7XG5cdFx0dmFyIHRpbGVQb3MgPSB0aGlzLl9nZXRUaWxlUG9zKGNvb3JkcyksXG5cdFx0ICAgIGtleSA9IHRoaXMuX3RpbGVDb29yZHNUb0tleShjb29yZHMpO1xuXG5cdFx0dmFyIHRpbGUgPSB0aGlzLmNyZWF0ZVRpbGUodGhpcy5fd3JhcENvb3Jkcyhjb29yZHMpLCBiaW5kKHRoaXMuX3RpbGVSZWFkeSwgdGhpcywgY29vcmRzKSk7XG5cblx0XHR0aGlzLl9pbml0VGlsZSh0aWxlKTtcblxuXHRcdC8vIGlmIGNyZWF0ZVRpbGUgaXMgZGVmaW5lZCB3aXRoIGEgc2Vjb25kIGFyZ3VtZW50IChcImRvbmVcIiBjYWxsYmFjayksXG5cdFx0Ly8gd2Uga25vdyB0aGF0IHRpbGUgaXMgYXN5bmMgYW5kIHdpbGwgYmUgcmVhZHkgbGF0ZXI7IG90aGVyd2lzZVxuXHRcdGlmICh0aGlzLmNyZWF0ZVRpbGUubGVuZ3RoIDwgMikge1xuXHRcdFx0Ly8gbWFyayB0aWxlIGFzIHJlYWR5LCBidXQgZGVsYXkgb25lIGZyYW1lIGZvciBvcGFjaXR5IGFuaW1hdGlvbiB0byBoYXBwZW5cblx0XHRcdHJlcXVlc3RBbmltRnJhbWUoYmluZCh0aGlzLl90aWxlUmVhZHksIHRoaXMsIGNvb3JkcywgbnVsbCwgdGlsZSkpO1xuXHRcdH1cblxuXHRcdHNldFBvc2l0aW9uKHRpbGUsIHRpbGVQb3MpO1xuXG5cdFx0Ly8gc2F2ZSB0aWxlIGluIGNhY2hlXG5cdFx0dGhpcy5fdGlsZXNba2V5XSA9IHtcblx0XHRcdGVsOiB0aWxlLFxuXHRcdFx0Y29vcmRzOiBjb29yZHMsXG5cdFx0XHRjdXJyZW50OiB0cnVlXG5cdFx0fTtcblxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZCh0aWxlKTtcblx0XHQvLyBAZXZlbnQgdGlsZWxvYWRzdGFydDogVGlsZUV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHRpbGUgaXMgcmVxdWVzdGVkIGFuZCBzdGFydHMgbG9hZGluZy5cblx0XHR0aGlzLmZpcmUoJ3RpbGVsb2Fkc3RhcnQnLCB7XG5cdFx0XHR0aWxlOiB0aWxlLFxuXHRcdFx0Y29vcmRzOiBjb29yZHNcblx0XHR9KTtcblx0fSxcblxuXHRfdGlsZVJlYWR5OiBmdW5jdGlvbiAoY29vcmRzLCBlcnIsIHRpbGUpIHtcblx0XHRpZiAoZXJyKSB7XG5cdFx0XHQvLyBAZXZlbnQgdGlsZWVycm9yOiBUaWxlRXJyb3JFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGVyZSBpcyBhbiBlcnJvciBsb2FkaW5nIGEgdGlsZS5cblx0XHRcdHRoaXMuZmlyZSgndGlsZWVycm9yJywge1xuXHRcdFx0XHRlcnJvcjogZXJyLFxuXHRcdFx0XHR0aWxlOiB0aWxlLFxuXHRcdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0dmFyIGtleSA9IHRoaXMuX3RpbGVDb29yZHNUb0tleShjb29yZHMpO1xuXG5cdFx0dGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0aWYgKCF0aWxlKSB7IHJldHVybjsgfVxuXG5cdFx0dGlsZS5sb2FkZWQgPSArbmV3IERhdGUoKTtcblx0XHRpZiAodGhpcy5fbWFwLl9mYWRlQW5pbWF0ZWQpIHtcblx0XHRcdHNldE9wYWNpdHkodGlsZS5lbCwgMCk7XG5cdFx0XHRjYW5jZWxBbmltRnJhbWUodGhpcy5fZmFkZUZyYW1lKTtcblx0XHRcdHRoaXMuX2ZhZGVGcmFtZSA9IHJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdHRoaXMuX3BydW5lVGlsZXMoKTtcblx0XHR9XG5cblx0XHRpZiAoIWVycikge1xuXHRcdFx0YWRkQ2xhc3ModGlsZS5lbCwgJ2xlYWZsZXQtdGlsZS1sb2FkZWQnKTtcblxuXHRcdFx0Ly8gQGV2ZW50IHRpbGVsb2FkOiBUaWxlRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGxvYWRzLlxuXHRcdFx0dGhpcy5maXJlKCd0aWxlbG9hZCcsIHtcblx0XHRcdFx0dGlsZTogdGlsZS5lbCxcblx0XHRcdFx0Y29vcmRzOiBjb29yZHNcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLl9ub1RpbGVzVG9Mb2FkKCkpIHtcblx0XHRcdHRoaXMuX2xvYWRpbmcgPSBmYWxzZTtcblx0XHRcdC8vIEBldmVudCBsb2FkOiBFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGUgZ3JpZCBsYXllciBsb2FkZWQgYWxsIHZpc2libGUgdGlsZXMuXG5cdFx0XHR0aGlzLmZpcmUoJ2xvYWQnKTtcblxuXHRcdFx0aWYgKGllbHQ5IHx8ICF0aGlzLl9tYXAuX2ZhZGVBbmltYXRlZCkge1xuXHRcdFx0XHRyZXF1
14775ZXN0QW5pbUZyYW1lKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gV2FpdCBhIGJpdCBtb3JlIHRoYW4gMC4yIHNlY3MgKHRoZSBkdXJhdGlvbiBvZiB0aGUgdGlsZSBmYWRlLWluKVxuXHRcdFx0XHQvLyB0byB0cmlnZ2VyIGEgcHJ1bmluZy5cblx0XHRcdFx0c2V0VGltZW91dChiaW5kKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpLCAyNTApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfZ2V0VGlsZVBvczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHJldHVybiBjb29yZHMuc2NhbGVCeSh0aGlzLmdldFRpbGVTaXplKCkpLnN1YnRyYWN0KHRoaXMuX2xldmVsLm9yaWdpbik7XG5cdH0sXG5cblx0X3dyYXBDb29yZHM6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgbmV3Q29vcmRzID0gbmV3IFBvaW50KFxuXHRcdFx0dGhpcy5fd3JhcFggPyB3cmFwTnVtKGNvb3Jkcy54LCB0aGlzLl93cmFwWCkgOiBjb29yZHMueCxcblx0XHRcdHRoaXMuX3dyYXBZID8gd3JhcE51bShjb29yZHMueSwgdGhpcy5fd3JhcFkpIDogY29vcmRzLnkpO1xuXHRcdG5ld0Nvb3Jkcy56ID0gY29vcmRzLno7XG5cdFx0cmV0dXJuIG5ld0Nvb3Jkcztcblx0fSxcblxuXHRfcHhCb3VuZHNUb1RpbGVSYW5nZTogZnVuY3Rpb24gKGJvdW5kcykge1xuXHRcdHZhciB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKTtcblx0XHRyZXR1cm4gbmV3IEJvdW5kcyhcblx0XHRcdGJvdW5kcy5taW4udW5zY2FsZUJ5KHRpbGVTaXplKS5mbG9vcigpLFxuXHRcdFx0Ym91bmRzLm1heC51bnNjYWxlQnkodGlsZVNpemUpLmNlaWwoKS5zdWJ0cmFjdChbMSwgMV0pKTtcblx0fSxcblxuXHRfbm9UaWxlc1RvTG9hZDogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLmxvYWRlZCkgeyByZXR1cm4gZmFsc2U7IH1cblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmdyaWRMYXllcihvcHRpb25zPzogR3JpZExheWVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgbmV3IGluc3RhbmNlIG9mIEdyaWRMYXllciB3aXRoIHRoZSBzdXBwbGllZCBvcHRpb25zLlxuZnVuY3Rpb24gZ3JpZExheWVyKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBHcmlkTGF5ZXIob3B0aW9ucyk7XG59XG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXJcclxuICogQGluaGVyaXRzIEdyaWRMYXllclxyXG4gKiBAYWthIEwuVGlsZUxheWVyXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSB0aWxlIGxheWVycyBvbiB0aGUgbWFwLiBOb3RlIHRoYXQgbW9zdCB0aWxlIHNlcnZlcnMgcmVxdWlyZSBhdHRyaWJ1dGlvbiwgd2hpY2ggeW91IGNhbiBzZXQgdW5kZXIgYExheWVyYC4gRXh0ZW5kcyBgR3JpZExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogTC50aWxlTGF5ZXIoJ2h0dHBzOi8ve3N9LnRpbGUub3BlbnN0cmVldG1hcC5vcmcve3p9L3t4fS97eX0ucG5nP3tmb299Jywge2ZvbzogJ2JhcicsIGF0dHJpYnV0aW9uOiAnTWFwIGRhdGEgJmNvcHk7IDxhIGhyZWY9XCJodHRwczovL3d3dy5vcGVuc3RyZWV0bWFwLm9yZy9cIj5PcGVuU3RyZWV0TWFwPC9hPiBjb250cmlidXRvcnMsIDxhIGhyZWY9XCJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvMi4wL1wiPkNDLUJZLVNBPC9hPid9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogQHNlY3Rpb24gVVJMIHRlbXBsYXRlXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIEEgc3RyaW5nIG9mIHRoZSBmb2xsb3dpbmcgZm9ybTpcclxuICpcclxuICogYGBgXHJcbiAqICdodHRwOi8ve3N9LnNvbWVkb21haW4uY29tL2JsYWJsYS97en0ve3h9L3t5fXtyfS5wbmcnXHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBge3N9YCBtZWFucyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBzdWJkb21haW5zICh1c2VkIHNlcXVlbnRpYWxseSB0byBoZWxwIHdpdGggYnJvd3NlciBwYXJhbGxlbCByZXF1ZXN0cyBwZXIgZG9tYWluIGxpbWl0YXRpb247IHN1YmRvbWFpbiB2YWx1ZXMgYXJlIHNwZWNpZmllZCBpbiBvcHRpb25zOyBgYWAsIGBiYCBvciBgY2AgYnkgZGVmYXVsdCwgY2FuIGJlIG9taXR0ZWQpLCBge3p9YCDigJQgem9vbSBsZXZlbCwgYHt4fWAgYW5kIGB7eX1gIOKAlCB0aWxlIGNvb3JkaW5hdGVzLiBge3J9YCBjYW4gYmUgdXNlZCB0byBhZGQgXCImY29tbWF0OzJ4XCIgdG8gdGhlIFVSTCB0byBsb2FkIHJldGluYSB0aWxlcy5cclxuICpcclxuICogWW91IGNhbiB1c2UgY3VzdG9tIGtleXMgaW4gdGhlIHRlbXBsYXRlLCB3aGljaCB3aWxsIGJlIFtldmFsdWF0ZWRdKCN1dGlsLXRlbXBsYXRlKSBmcm9tIFRpbGVMYXllciBvcHRpb25zLCBsaWtlIHRoaXM6XHJcbiAqXHJcbiAqIGBgYFxyXG4gKiBMLnRpbGVMYXllcignaHR0cDovL3tzfS5zb21lZG9tYWluLmNvbS97Zm9vfS97en0ve3h9L3t5fS5wbmcnLCB7Zm9vOiAnYmFyJ30pO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxudmFyIFRpbGVMYXllciA9IEdyaWRMYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgVGlsZUxheWVyIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG1pblpvb206IE51bWJlciA9IDBcclxuXHRcdC8vIFRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgZG93biB0byB3aGljaCB0aGlzIGxheWVyIHdpbGwgYmUgZGlzcGxheWVkIChpbmNsdXNpdmUpLlxyXG5cdFx0bWluWm9vbTogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IDE4XHJcblx0XHQvLyBUaGUgbWF4aW11bSB6b29tIGxldmVsIHVwIHRvIHdoaWNoIHRoaXMgbGF5ZXIgd2lsbCBiZSBkaXNwbGF5ZWQgKGluY2x1c2l2ZSkuXHJcblx0XHRtYXhab29tOiAxOCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHN1YmRvbWFpbnM6IFN0cmluZ3xTdHJpbmdbXSA9ICdhYmMnXHJcblx0XHQvLyBTdWJkb21haW5zIG9mIHRoZSB0aWxlIHNlcnZpY2UuIENhbiBiZSBwYXNzZWQgaW4gdGhlIGZvcm0gb2Ygb25lIHN0cmluZyAod2hlcmUgZWFjaCBsZXR0ZXIgaXMgYSBzdWJkb21haW4gbmFtZSkgb3IgYW4gYXJyYXkgb2Ygc3RyaW5ncy5cclxuXHRcdHN1YmRvbWFpbnM6ICdhYmMnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZXJyb3JUaWxlVXJsOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVVJMIHRvIHRoZSB0aWxlIGltYWdlIHRvIHNob3cgaW4gcGxhY2Ugb2YgdGhlIHRpbGUgdGh
14775hdCBmYWlsZWQgdG8gbG9hZC5cclxuXHRcdGVycm9yVGlsZVVybDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT2Zmc2V0OiBOdW1iZXIgPSAwXHJcblx0XHQvLyBUaGUgem9vbSBudW1iZXIgdXNlZCBpbiB0aWxlIFVSTHMgd2lsbCBiZSBvZmZzZXQgd2l0aCB0aGlzIHZhbHVlLlxyXG5cdFx0em9vbU9mZnNldDogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRtczogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIGludmVyc2VzIFkgYXhpcyBudW1iZXJpbmcgZm9yIHRpbGVzICh0dXJuIHRoaXMgb24gZm9yIFtUTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RpbGVfTWFwX1NlcnZpY2UpIHNlcnZpY2VzKS5cclxuXHRcdHRtczogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tUmV2ZXJzZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBzZXQgdG8gdHJ1ZSwgdGhlIHpvb20gbnVtYmVyIHVzZWQgaW4gdGlsZSBVUkxzIHdpbGwgYmUgcmV2ZXJzZWQgKGBtYXhab29tIC0gem9vbWAgaW5zdGVhZCBvZiBgem9vbWApXHJcblx0XHR6b29tUmV2ZXJzZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBkZXRlY3RSZXRpbmE6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgIGFuZCB1c2VyIGlzIG9uIGEgcmV0aW5hIGRpc3BsYXksIGl0IHdpbGwgcmVxdWVzdCBmb3VyIHRpbGVzIG9mIGhhbGYgdGhlIHNwZWNpZmllZCBzaXplIGFuZCBhIGJpZ2dlciB6b29tIGxldmVsIGluIHBsYWNlIG9mIG9uZSB0byB1dGlsaXplIHRoZSBoaWdoIHJlc29sdXRpb24uXHJcblx0XHRkZXRlY3RSZXRpbmE6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY3Jvc3NPcmlnaW46IEJvb2xlYW58U3RyaW5nID0gZmFsc2VcclxuXHRcdC8vIFdoZXRoZXIgdGhlIGNyb3NzT3JpZ2luIGF0dHJpYnV0ZSB3aWxsIGJlIGFkZGVkIHRvIHRoZSB0aWxlcy5cclxuXHRcdC8vIElmIGEgU3RyaW5nIGlzIHByb3ZpZGVkLCBhbGwgdGlsZXMgd2lsbCBoYXZlIHRoZWlyIGNyb3NzT3JpZ2luIGF0dHJpYnV0ZSBzZXQgdG8gdGhlIFN0cmluZyBwcm92aWRlZC4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIHRpbGUgcGl4ZWwgZGF0YS5cclxuXHRcdC8vIFJlZmVyIHRvIFtDT1JTIFNldHRpbmdzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9IVE1ML0NPUlNfc2V0dGluZ3NfYXR0cmlidXRlcykgZm9yIHZhbGlkIFN0cmluZyB2YWx1ZXMuXHJcblx0XHRjcm9zc09yaWdpbjogZmFsc2VcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdG9wdGlvbnMgPSBzZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdC8vIGRldGVjdGluZyByZXRpbmEgZGlzcGxheXMsIGFkanVzdGluZyB0aWxlU2l6ZSBhbmQgem9vbSBsZXZlbHNcclxuXHRcdGlmIChvcHRpb25zLmRldGVjdFJldGluYSAmJiByZXRpbmEgJiYgb3B0aW9ucy5tYXhab29tID4gMCkge1xyXG5cclxuXHRcdFx0b3B0aW9ucy50aWxlU2l6ZSA9IE1hdGguZmxvb3Iob3B0aW9ucy50aWxlU2l6ZSAvIDIpO1xyXG5cclxuXHRcdFx0aWYgKCFvcHRpb25zLnpvb21SZXZlcnNlKSB7XHJcblx0XHRcdFx0b3B0aW9ucy56b29tT2Zmc2V0Kys7XHJcblx0XHRcdFx0b3B0aW9ucy5tYXhab29tLS07XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0b3B0aW9ucy56b29tT2Zmc2V0LS07XHJcblx0XHRcdFx0b3B0aW9ucy5taW5ab29tKys7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdG9wdGlvbnMubWluWm9vbSA9IE1hdGgubWF4KDAsIG9wdGlvbnMubWluWm9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBvcHRpb25zLnN1YmRvbWFpbnMgPT09ICdzdHJpbmcnKSB7XHJcblx0XHRcdG9wdGlvbnMuc3ViZG9tYWlucyA9IG9wdGlvbnMuc3ViZG9tYWlucy5zcGxpdCgnJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gZm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzEzN1xyXG5cdFx0aWYgKCFhbmRyb2lkKSB7XHJcblx0XHRcdHRoaXMub24oJ3RpbGV1bmxvYWQnLCB0aGlzLl9vblRpbGVSZW1vdmUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nLCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gVXBkYXRlcyB0aGUgbGF5ZXIncyBVUkwgdGVtcGxhdGUgYW5kIHJlZHJhd3MgaXQgKHVubGVzcyBgbm9SZWRyYXdgIGlzIHNldCB0byBgdHJ1ZWApLlxyXG5cdC8vIElmIHRoZSBVUkwgZG9lcyBub3QgY2hhbmdlLCB0aGUgbGF5ZXIgd2lsbCBub3QgYmUgcmVkcmF3biB1bmxlc3NcclxuXHQvLyB0aGUgbm9SZWRyYXcgcGFyYW1ldGVyIGlzIHNldCB0byBmYWxzZS5cclxuXHRzZXRVcmw6IGZ1bmN0aW9uICh1cmwsIG5vUmVkcmF3KSB7XHJcblx0XHRpZiAodGhpcy5fdXJsID09PSB1cmwgJiYgbm9SZWRyYXcgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRub1JlZHJhdyA9IHRydWU7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdGlmICghbm9SZWRyYXcpIHtcclxuXHRcdFx0dGhpcy5yZWRyYXcoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlVGlsZShjb29yZHM6IE9iamVjdCwgZG9uZT86IEZ1bmN0aW9uKTogSFRNTEVsZW1lbnRcclxuXHQvLyBDYWxsZWQgb25seSBpbnRlcm5hbGx5LCBvdmVycmlkZXMgR3JpZExheWVyJ3MgW2BjcmVhdGVUaWxlKClgXSgjZ3JpZGxheWVyLWNyZWF0ZXRpbGUpXHJcblx0Ly8gdG8gcmV0dXJuIGFuIGA8aW1nPmAgSFRNTCBlbGVtZW50IHdpdGggdGhlIGFwcHJvcHJpYXRlIGltYWdlIFVSTCBnaXZlbiBgY29vcmRzYC4gVGhlIGBkb25lYFxyXG5cdC8vIGNhbGxiYWNrIGlzIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBiZWVuIGxvYWRlZC5cclxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBkb25lKSB7XHJcblx0XHR2YXIgdGlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG5cclxuXHRcdG9uKHRpbGUsICdsb2FkJywgYmluZCh0aGlzLl90
14775aWxlT25Mb2FkLCB0aGlzLCBkb25lLCB0aWxlKSk7XHJcblx0XHRvbih0aWxlLCAnZXJyb3InLCBiaW5kKHRoaXMuX3RpbGVPbkVycm9yLCB0aGlzLCBkb25lLCB0aWxlKSk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbiB8fCB0aGlzLm9wdGlvbnMuY3Jvc3NPcmlnaW4gPT09ICcnKSB7XHJcblx0XHRcdHRpbGUuY3Jvc3NPcmlnaW4gPSB0aGlzLm9wdGlvbnMuY3Jvc3NPcmlnaW4gPT09IHRydWUgPyAnJyA6IHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbjtcclxuXHRcdH1cclxuXHJcblx0XHQvKlxyXG5cdFx0IEFsdCB0YWcgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBrZWVwIHNjcmVlbiByZWFkZXJzIGZyb20gcmVhZGluZyBVUkwgYW5kIGZvciBjb21wbGlhbmNlIHJlYXNvbnNcclxuXHRcdCBodHRwOi8vd3d3LnczLm9yZy9UUi9XQ0FHMjAtVEVDSFMvSDY3XHJcblx0XHQqL1xyXG5cdFx0dGlsZS5hbHQgPSAnJztcclxuXHJcblx0XHQvKlxyXG5cdFx0IFNldCByb2xlPVwicHJlc2VudGF0aW9uXCIgdG8gZm9yY2Ugc2NyZWVuIHJlYWRlcnMgdG8gaWdub3JlIHRoaXNcclxuXHRcdCBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEvcm9sZXMjdGV4dGFsdGVybmF0aXZlY29tcHV0YXRpb25cclxuXHRcdCovXHJcblx0XHR0aWxlLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcclxuXHJcblx0XHR0aWxlLnNyYyA9IHRoaXMuZ2V0VGlsZVVybChjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aWxlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXHJcblx0Ly8gQHVuaW5oZXJpdGFibGVcclxuXHQvLyBMYXllcnMgZXh0ZW5kaW5nIGBUaWxlTGF5ZXJgIG1pZ2h0IHJlaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kLlxyXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVVybChjb29yZHM6IE9iamVjdCk6IFN0cmluZ1xyXG5cdC8vIENhbGxlZCBvbmx5IGludGVybmFsbHksIHJldHVybnMgdGhlIFVSTCBmb3IgYSB0aWxlIGdpdmVuIGl0cyBjb29yZGluYXRlcy5cclxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgVGlsZUxheWVyYCBjYW4gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiB0byBwcm92aWRlIGN1c3RvbSB0aWxlIFVSTCBuYW1pbmcgc2NoZW1lcy5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0cjogcmV0aW5hID8gJ0AyeCcgOiAnJyxcclxuXHRcdFx0czogdGhpcy5fZ2V0U3ViZG9tYWluKGNvb3JkcyksXHJcblx0XHRcdHg6IGNvb3Jkcy54LFxyXG5cdFx0XHR5OiBjb29yZHMueSxcclxuXHRcdFx0ejogdGhpcy5fZ2V0Wm9vbUZvclVybCgpXHJcblx0XHR9O1xyXG5cdFx0aWYgKHRoaXMuX21hcCAmJiAhdGhpcy5fbWFwLm9wdGlvbnMuY3JzLmluZmluaXRlKSB7XHJcblx0XHRcdHZhciBpbnZlcnRlZFkgPSB0aGlzLl9nbG9iYWxUaWxlUmFuZ2UubWF4LnkgLSBjb29yZHMueTtcclxuXHRcdFx0aWYgKHRoaXMub3B0aW9ucy50bXMpIHtcclxuXHRcdFx0XHRkYXRhWyd5J10gPSBpbnZlcnRlZFk7XHJcblx0XHRcdH1cclxuXHRcdFx0ZGF0YVsnLXknXSA9IGludmVydGVkWTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGVtcGxhdGUodGhpcy5fdXJsLCBleHRlbmQoZGF0YSwgdGhpcy5vcHRpb25zKSk7XHJcblx0fSxcclxuXHJcblx0X3RpbGVPbkxvYWQ6IGZ1bmN0aW9uIChkb25lLCB0aWxlKSB7XHJcblx0XHQvLyBGb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMzMzMlxyXG5cdFx0aWYgKGllbHQ5KSB7XHJcblx0XHRcdHNldFRpbWVvdXQoYmluZChkb25lLCB0aGlzLCBudWxsLCB0aWxlKSwgMCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRkb25lKG51bGwsIHRpbGUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF90aWxlT25FcnJvcjogZnVuY3Rpb24gKGRvbmUsIHRpbGUsIGUpIHtcclxuXHRcdHZhciBlcnJvclVybCA9IHRoaXMub3B0aW9ucy5lcnJvclRpbGVVcmw7XHJcblx0XHRpZiAoZXJyb3JVcmwgJiYgdGlsZS5nZXRBdHRyaWJ1dGUoJ3NyYycpICE9PSBlcnJvclVybCkge1xyXG5cdFx0XHR0aWxlLnNyYyA9IGVycm9yVXJsO1xyXG5cdFx0fVxyXG5cdFx0ZG9uZShlLCB0aWxlKTtcclxuXHR9LFxyXG5cclxuXHRfb25UaWxlUmVtb3ZlOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0ZS50aWxlLm9ubG9hZCA9IG51bGw7XHJcblx0fSxcclxuXHJcblx0X2dldFpvb21Gb3JVcmw6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciB6b29tID0gdGhpcy5fdGlsZVpvb20sXHJcblx0XHRtYXhab29tID0gdGhpcy5vcHRpb25zLm1heFpvb20sXHJcblx0XHR6b29tUmV2ZXJzZSA9IHRoaXMub3B0aW9ucy56b29tUmV2ZXJzZSxcclxuXHRcdHpvb21PZmZzZXQgPSB0aGlzLm9wdGlvbnMuem9vbU9mZnNldDtcclxuXHJcblx0XHRpZiAoem9vbVJldmVyc2UpIHtcclxuXHRcdFx0em9vbSA9IG1heFpvb20gLSB6b29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB6b29tICsgem9vbU9mZnNldDtcclxuXHR9LFxyXG5cclxuXHRfZ2V0U3ViZG9tYWluOiBmdW5jdGlvbiAodGlsZVBvaW50KSB7XHJcblx0XHR2YXIgaW5kZXggPSBNYXRoLmFicyh0aWxlUG9pbnQueCArIHRpbGVQb2ludC55KSAlIHRoaXMub3B0aW9ucy5zdWJkb21haW5zLmxlbmd0aDtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc3ViZG9tYWluc1tpbmRleF07XHJcblx0fSxcclxuXHJcblx0Ly8gc3RvcHMgbG9hZGluZyBhbGwgdGlsZXMgaW4gdGhlIGJhY2tncm91bmQgbGF5ZXJcclxuXHRfYWJvcnRMb2FkaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaSwgdGlsZTtcclxuXHRcdGZvciAoaSBpbiB0aGlzLl90aWxlcykge1xyXG5cdFx0XHRpZiAodGhpcy5fdGlsZXNbaV0uY29vcmRzLnogIT09IHRoaXMuX3RpbGVab29tKSB7XHJcblx0XHRcdFx0dGlsZSA9IHRoaXMuX3RpbGVzW2ldLmVsO1xyXG5cclxuXHRcdFx0XHR0aWxlLm9ubG9hZCA9IGZhbHNlRm47XHJcblx0XHRcdFx0dGlsZS5vbmVycm9yID0gZmFsc2VGbjtcclxuXHJcblx0XHRcdFx0aWYgKCF0aWxlLmNvbXBsZXRlKSB7XHJcblx0XHRcdFx0XHR0aWxlLnNyYyA9IGVtcHR5SW1hZ2VVcmw7XHJcblx0XHRcdFx0XHRyZW1vdmUodGlsZSk7XHJcblx0XHRcdFx0XHRkZWxldGUgdGhpcy5fdGlsZXNbaV07XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3JlbW92ZVRpbGU6IGZ1bmN0aW9uIChrZXkpIHtcclxuXHRcdHZhciB0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcclxuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBDYW5jZWxzIGFueSBwZW5kaW5nIGh0dHAgcmVx
14775dWVzdHMgYXNzb2NpYXRlZCB3aXRoIHRoZSB0aWxlXHJcblx0XHQvLyB1bmxlc3Mgd2UncmUgb24gQW5kcm9pZCdzIHN0b2NrIGJyb3dzZXIsXHJcblx0XHQvLyBzZWUgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTM3XHJcblx0XHRpZiAoIWFuZHJvaWRTdG9jaykge1xyXG5cdFx0XHR0aWxlLmVsLnNldEF0dHJpYnV0ZSgnc3JjJywgZW1wdHlJbWFnZVVybCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEdyaWRMYXllci5wcm90b3R5cGUuX3JlbW92ZVRpbGUuY2FsbCh0aGlzLCBrZXkpO1xyXG5cdH0sXHJcblxyXG5cdF90aWxlUmVhZHk6IGZ1bmN0aW9uIChjb29yZHMsIGVyciwgdGlsZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgKHRpbGUgJiYgdGlsZS5nZXRBdHRyaWJ1dGUoJ3NyYycpID09PSBlbXB0eUltYWdlVXJsKSkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEdyaWRMYXllci5wcm90b3R5cGUuX3RpbGVSZWFkeS5jYWxsKHRoaXMsIGNvb3JkcywgZXJyLCB0aWxlKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwudGlsZWxheWVyKHVybFRlbXBsYXRlOiBTdHJpbmcsIG9wdGlvbnM/OiBUaWxlTGF5ZXIgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgdGlsZSBsYXllciBvYmplY3QgZ2l2ZW4gYSBgVVJMIHRlbXBsYXRlYCBhbmQgb3B0aW9uYWxseSBhbiBvcHRpb25zIG9iamVjdC5cclxuXHJcbmZ1bmN0aW9uIHRpbGVMYXllcih1cmwsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IFRpbGVMYXllcih1cmwsIG9wdGlvbnMpO1xyXG59XG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXIuV01TXHJcbiAqIEBpbmhlcml0cyBUaWxlTGF5ZXJcclxuICogQGFrYSBMLlRpbGVMYXllci5XTVNcclxuICogVXNlZCB0byBkaXNwbGF5IFtXTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9NYXBfU2VydmljZSkgc2VydmljZXMgYXMgdGlsZSBsYXllcnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgVGlsZUxheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIG5leHJhZCA9IEwudGlsZUxheWVyLndtcyhcImh0dHA6Ly9tZXNvbmV0LmFncm9uLmlhc3RhdGUuZWR1L2NnaS1iaW4vd21zL25leHJhZC9uMHIuY2dpXCIsIHtcclxuICogXHRsYXllcnM6ICduZXhyYWQtbjByLTkwMDkxMycsXHJcbiAqIFx0Zm9ybWF0OiAnaW1hZ2UvcG5nJyxcclxuICogXHR0cmFuc3BhcmVudDogdHJ1ZSxcclxuICogXHRhdHRyaWJ1dGlvbjogXCJXZWF0aGVyIGRhdGEgwqkgMjAxMiBJRU0gTmV4cmFkXCJcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbnZhciBUaWxlTGF5ZXJXTVMgPSBUaWxlTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllci5XTVMgb3B0aW9uc1xyXG5cdC8vIElmIGFueSBjdXN0b20gb3B0aW9ucyBub3QgZG9jdW1lbnRlZCBoZXJlIGFyZSB1c2VkLCB0aGV5IHdpbGwgYmUgc2VudCB0byB0aGVcclxuXHQvLyBXTVMgc2VydmVyIGFzIGV4dHJhIHBhcmFtZXRlcnMgaW4gZWFjaCByZXF1ZXN0IFVSTC4gVGhpcyBjYW4gYmUgdXNlZnVsIGZvclxyXG5cdC8vIFtub24tc3RhbmRhcmQgdmVuZG9yIFdNUyBwYXJhbWV0ZXJzXShodHRwOi8vZG9jcy5nZW9zZXJ2ZXIub3JnL3N0YWJsZS9lbi91c2VyL3NlcnZpY2VzL3dtcy92ZW5kb3IuaHRtbCkuXHJcblx0ZGVmYXVsdFdtc1BhcmFtczoge1xyXG5cdFx0c2VydmljZTogJ1dNUycsXHJcblx0XHRyZXF1ZXN0OiAnR2V0TWFwJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGxheWVyczogU3RyaW5nID0gJydcclxuXHRcdC8vICoqKHJlcXVpcmVkKSoqIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIFdNUyBsYXllcnMgdG8gc2hvdy5cclxuXHRcdGxheWVyczogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdHlsZXM6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBXTVMgc3R5bGVzLlxyXG5cdFx0c3R5bGVzOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGZvcm1hdDogU3RyaW5nID0gJ2ltYWdlL2pwZWcnXHJcblx0XHQvLyBXTVMgaW1hZ2UgZm9ybWF0ICh1c2UgYCdpbWFnZS9wbmcnYCBmb3IgbGF5ZXJzIHdpdGggdHJhbnNwYXJlbmN5KS5cclxuXHRcdGZvcm1hdDogJ2ltYWdlL2pwZWcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhbnNwYXJlbnQ6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgV01TIHNlcnZpY2Ugd2lsbCByZXR1cm4gaW1hZ2VzIHdpdGggdHJhbnNwYXJlbmN5LlxyXG5cdFx0dHJhbnNwYXJlbnQ6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdmVyc2lvbjogU3RyaW5nID0gJzEuMS4xJ1xyXG5cdFx0Ly8gVmVyc2lvbiBvZiB0aGUgV01TIHNlcnZpY2UgdG8gdXNlXHJcblx0XHR2ZXJzaW9uOiAnMS4xLjEnXHJcblx0fSxcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjcnM6IENSUyA9IG51bGxcclxuXHRcdC8vIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbSB0byB1c2UgZm9yIHRoZSBXTVMgcmVxdWVzdHMsIGRlZmF1bHRzIHRvXHJcblx0XHQvLyBtYXAgQ1JTLiBEb24ndCBjaGFuZ2UgdGhpcyBpZiB5b3UncmUgbm90IHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHVwcGVyY2FzZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIFdNUyByZXF1ZXN0IHBhcmFtZXRlciBrZXlzIHdpbGwgYmUgdXBwZXJjYXNlLlxyXG5cdFx0dXBwZXJjYXNlOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0dmFyIHdtc1BhcmFtcyA9IGV4dGVuZCh7fSwgdGhpcy5kZWZhdWx0V21zUGFyYW1zKTtcclxuXHJcblx0XHQvLyBhbGwga2V5cyB0aGF0IGFyZSBub3QgVGlsZUxheWVyIG9wdGlvbnMgZ28gdG8gV01TIHBhcmFtc1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvcHRpb25zKSB7XHJcblx0XHRcdGlmICghKGkgaW4gdGhpcy5vcHRpb25zKSkge1xyXG5cdFx0XHRcdHdtc1BhcmFtc1tpXSA9IG9wdGlvbnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRvcHRpb25zID0gc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHR2YXIgcmVhbFJldGluYSA9IG9wdGlvbnMuZGV0ZWN0UmV0aW5hICYmIHJldGluYSA/IDIgOiAxO1xyXG5cdFx0dmFyIHRpbGVTaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xyXG5cdFx0d21zUGFyYW1zLndpZHRoID0gdGlsZVNpemUueCAqIHJlYWxSZXRpbmE7XHJcblx0XHR3bXNQYXJhbXMuaGVpZ2h0ID0gdGlsZVNpemUueSAqIHJlYWxSZXRpbmE7XHJcblxyXG5cdFx0dGhpcy53bXNQYXJhbXMgPSB3bXNQYXJhbXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHJcblx0XHR0aGlzLl9jcnMgPSB0aGlzLm9wdGlvbnMuY3JzIHx8IG1hcC5vcHRpb25zLmNycztcclxuXHRcdHRoaXMuX3dtc1ZlcnNpb24gPSBwYXJzZU
14775Zsb2F0KHRoaXMud21zUGFyYW1zLnZlcnNpb24pO1xyXG5cclxuXHRcdHZhciBwcm9qZWN0aW9uS2V5ID0gdGhpcy5fd21zVmVyc2lvbiA+PSAxLjMgPyAnY3JzJyA6ICdzcnMnO1xyXG5cdFx0dGhpcy53bXNQYXJhbXNbcHJvamVjdGlvbktleV0gPSB0aGlzLl9jcnMuY29kZTtcclxuXHJcblx0XHRUaWxlTGF5ZXIucHJvdG90eXBlLm9uQWRkLmNhbGwodGhpcywgbWFwKTtcclxuXHR9LFxyXG5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblxyXG5cdFx0dmFyIHRpbGVCb3VuZHMgPSB0aGlzLl90aWxlQ29vcmRzVG9Od1NlKGNvb3JkcyksXHJcblx0XHQgICAgY3JzID0gdGhpcy5fY3JzLFxyXG5cdFx0ICAgIGJvdW5kcyA9IHRvQm91bmRzKGNycy5wcm9qZWN0KHRpbGVCb3VuZHNbMF0pLCBjcnMucHJvamVjdCh0aWxlQm91bmRzWzFdKSksXHJcblx0XHQgICAgbWluID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXggPSBib3VuZHMubWF4LFxyXG5cdFx0ICAgIGJib3ggPSAodGhpcy5fd21zVmVyc2lvbiA+PSAxLjMgJiYgdGhpcy5fY3JzID09PSBFUFNHNDMyNiA/XHJcblx0XHQgICAgW21pbi55LCBtaW4ueCwgbWF4LnksIG1heC54XSA6XHJcblx0XHQgICAgW21pbi54LCBtaW4ueSwgbWF4LngsIG1heC55XSkuam9pbignLCcpLFxyXG5cdFx0ICAgIHVybCA9IFRpbGVMYXllci5wcm90b3R5cGUuZ2V0VGlsZVVybC5jYWxsKHRoaXMsIGNvb3Jkcyk7XHJcblx0XHRyZXR1cm4gdXJsICtcclxuXHRcdFx0Z2V0UGFyYW1TdHJpbmcodGhpcy53bXNQYXJhbXMsIHVybCwgdGhpcy5vcHRpb25zLnVwcGVyY2FzZSkgK1xyXG5cdFx0XHQodGhpcy5vcHRpb25zLnVwcGVyY2FzZSA/ICcmQkJPWD0nIDogJyZiYm94PScpICsgYmJveDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBhcmFtcyhwYXJhbXM6IE9iamVjdCwgbm9SZWRyYXc/OiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIE1lcmdlcyBhbiBvYmplY3Qgd2l0aCB0aGUgbmV3IHBhcmFtZXRlcnMgYW5kIHJlLXJlcXVlc3RzIHRpbGVzIG9uIHRoZSBjdXJyZW50IHNjcmVlbiAodW5sZXNzIGBub1JlZHJhd2Agd2FzIHNldCB0byB0cnVlKS5cclxuXHRzZXRQYXJhbXM6IGZ1bmN0aW9uIChwYXJhbXMsIG5vUmVkcmF3KSB7XHJcblxyXG5cdFx0ZXh0ZW5kKHRoaXMud21zUGFyYW1zLCBwYXJhbXMpO1xyXG5cclxuXHRcdGlmICghbm9SZWRyYXcpIHtcclxuXHRcdFx0dGhpcy5yZWRyYXcoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwudGlsZUxheWVyLndtcyhiYXNlVXJsOiBTdHJpbmcsIG9wdGlvbnM6IFRpbGVMYXllci5XTVMgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgV01TIHRpbGUgbGF5ZXIgb2JqZWN0IGdpdmVuIGEgYmFzZSBVUkwgb2YgdGhlIFdNUyBzZXJ2aWNlIGFuZCBhIFdNUyBwYXJhbWV0ZXJzL29wdGlvbnMgb2JqZWN0LlxyXG5mdW5jdGlvbiB0aWxlTGF5ZXJXTVModXJsLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBUaWxlTGF5ZXJXTVModXJsLCBvcHRpb25zKTtcclxufVxuXG5UaWxlTGF5ZXIuV01TID0gVGlsZUxheWVyV01TO1xudGlsZUxheWVyLndtcyA9IHRpbGVMYXllcldNUztcblxuLypcbiAqIEBjbGFzcyBSZW5kZXJlclxuICogQGluaGVyaXRzIExheWVyXG4gKiBAYWthIEwuUmVuZGVyZXJcbiAqXG4gKiBCYXNlIGNsYXNzIGZvciB2ZWN0b3IgcmVuZGVyZXIgaW1wbGVtZW50YXRpb25zIChgU1ZHYCwgYENhbnZhc2ApLiBIYW5kbGVzIHRoZVxuICogRE9NIGNvbnRhaW5lciBvZiB0aGUgcmVuZGVyZXIsIGl0cyBib3VuZHMsIGFuZCBpdHMgem9vbSBhbmltYXRpb24uXG4gKlxuICogQSBgUmVuZGVyZXJgIHdvcmtzIGFzIGFuIGltcGxpY2l0IGxheWVyIGdyb3VwIGZvciBhbGwgYFBhdGhgcyAtIHRoZSByZW5kZXJlclxuICogaXRzZWxmIGNhbiBiZSBhZGRlZCBvciByZW1vdmVkIHRvIHRoZSBtYXAuIEFsbCBwYXRocyB1c2UgYSByZW5kZXJlciwgd2hpY2ggY2FuXG4gKiBiZSBpbXBsaWNpdCAodGhlIG1hcCB3aWxsIGRlY2lkZSB0aGUgdHlwZSBvZiByZW5kZXJlciBhbmQgdXNlIGl0IGF1dG9tYXRpY2FsbHkpXG4gKiBvciBleHBsaWNpdCAodXNpbmcgdGhlIFtgcmVuZGVyZXJgXSgjcGF0aC1yZW5kZXJlcikgb3B0aW9uIG9mIHRoZSBwYXRoKS5cbiAqXG4gKiBEbyBub3QgdXNlIHRoaXMgY2xhc3MgZGlyZWN0bHksIHVzZSBgU1ZHYCBhbmQgYENhbnZhc2AgaW5zdGVhZC5cbiAqXG4gKiBAZXZlbnQgdXBkYXRlOiBFdmVudFxuICogRmlyZWQgd2hlbiB0aGUgcmVuZGVyZXIgdXBkYXRlcyBpdHMgYm91bmRzLCBjZW50ZXIgYW5kIHpvb20sIGZvciBleGFtcGxlIHdoZW5cbiAqIGl0cyBtYXAgaGFzIG1vdmVkXG4gKi9cblxudmFyIFJlbmRlcmVyID0gTGF5ZXIuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFJlbmRlcmVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFkZGluZzogTnVtYmVyID0gMC4xXG5cdFx0Ly8gSG93IG11Y2ggdG8gZXh0ZW5kIHRoZSBjbGlwIGFyZWEgYXJvdW5kIHRoZSBtYXAgdmlldyAocmVsYXRpdmUgdG8gaXRzIHNpemUpXG5cdFx0Ly8gZS5nLiAwLjEgd291bGQgYmUgMTAlIG9mIG1hcCB2aWV3IGluIGVhY2ggZGlyZWN0aW9uXG5cdFx0cGFkZGluZzogMC4xLFxuXG5cdFx0Ly8gQG9wdGlvbiB0b2xlcmFuY2U6IE51bWJlciA9IDBcblx0XHQvLyBIb3cgbXVjaCB0byBleHRlbmQgY2xpY2sgdG9sZXJhbmNlIHJvdW5kIGEgcGF0aC9vYmplY3Qgb24gdGhlIG1hcFxuXHRcdHRvbGVyYW5jZSA6IDBcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdHNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0c3RhbXAodGhpcyk7XG5cdFx0dGhpcy5fbGF5ZXJzID0gdGhpcy5fbGF5ZXJzIHx8IHt9O1xuXHR9LFxuXG5cdG9uQWRkOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHtcblx0XHRcdHRoaXMuX2luaXRDb250YWluZXIoKT
14775sgLy8gZGVmaW5lZCBieSByZW5kZXJlciBpbXBsZW1lbnRhdGlvbnNcblxuXHRcdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xuXHRcdFx0XHRhZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9jb250YWluZXIpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMub24oJ3VwZGF0ZScsIHRoaXMuX3VwZGF0ZVBhdGhzLCB0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMub2ZmKCd1cGRhdGUnLCB0aGlzLl91cGRhdGVQYXRocywgdGhpcyk7XG5cdFx0dGhpcy5fZGVzdHJveUNvbnRhaW5lcigpO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0LFxuXHRcdFx0em9vbTogdGhpcy5fb25ab29tLFxuXHRcdFx0bW92ZWVuZDogdGhpcy5fdXBkYXRlLFxuXHRcdFx0em9vbWVuZDogdGhpcy5fb25ab29tRW5kXG5cdFx0fTtcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9vbkFuaW1ab29tO1xuXHRcdH1cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9vbkFuaW1ab29tOiBmdW5jdGlvbiAoZXYpIHtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0oZXYuY2VudGVyLCBldi56b29tKTtcblx0fSxcblxuXHRfb25ab29tOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fdXBkYXRlVHJhbnNmb3JtKHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSk7XG5cdH0sXG5cblx0X3VwZGF0ZVRyYW5zZm9ybTogZnVuY3Rpb24gKGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgdGhpcy5fem9vbSksXG5cdFx0ICAgIHBvc2l0aW9uID0gZ2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyKSxcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLm11bHRpcGx5QnkoMC41ICsgdGhpcy5vcHRpb25zLnBhZGRpbmcpLFxuXHRcdCAgICBjdXJyZW50Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdCh0aGlzLl9jZW50ZXIsIHpvb20pLFxuXHRcdCAgICBkZXN0Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdChjZW50ZXIsIHpvb20pLFxuXHRcdCAgICBjZW50ZXJPZmZzZXQgPSBkZXN0Q2VudGVyUG9pbnQuc3VidHJhY3QoY3VycmVudENlbnRlclBvaW50KSxcblxuXHRcdCAgICB0b3BMZWZ0T2Zmc2V0ID0gdmlld0hhbGYubXVsdGlwbHlCeSgtc2NhbGUpLmFkZChwb3NpdGlvbikuYWRkKHZpZXdIYWxmKS5zdWJ0cmFjdChjZW50ZXJPZmZzZXQpO1xuXG5cdFx0aWYgKGFueTNkKSB7XG5cdFx0XHRzZXRUcmFuc2Zvcm0odGhpcy5fY29udGFpbmVyLCB0b3BMZWZ0T2Zmc2V0LCBzY2FsZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgdG9wTGVmdE9mZnNldCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMuX3VwZGF0ZVRyYW5zZm9ybSh0aGlzLl9jZW50ZXIsIHRoaXMuX3pvb20pO1xuXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9yZXNldCgpO1xuXHRcdH1cblx0fSxcblxuXHRfb25ab29tRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9wcm9qZWN0KCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fdXBkYXRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBVcGRhdGUgcGl4ZWwgYm91bmRzIG9mIHJlbmRlcmVyIGNvbnRhaW5lciAoZm9yIHBvc2l0aW9uaW5nL3NpemluZy9jbGlwcGluZyBsYXRlcilcblx0XHQvLyBTdWJjbGFzc2VzIGFyZSByZXNwb25zaWJsZSBvZiBmaXJpbmcgdGhlICd1cGRhdGUnIGV2ZW50LlxuXHRcdHZhciBwID0gdGhpcy5vcHRpb25zLnBhZGRpbmcsXG5cdFx0ICAgIHNpemUgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLFxuXHRcdCAgICBtaW4gPSB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQoc2l6ZS5tdWx0aXBseUJ5KC1wKSkucm91bmQoKTtcblxuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBCb3VuZHMobWluLCBtaW4uYWRkKHNpemUubXVsdGlwbHlCeSgxICsgcCAqIDIpKS5yb3VuZCgpKTtcblxuXHRcdHRoaXMuX2NlbnRlciA9IHRoaXMuX21hcC5nZXRDZW50ZXIoKTtcblx0XHR0aGlzLl96b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcblx0fVxufSk7XG5cbi8qXG4gKiBAY2xhc3MgQ2FudmFzXG4gKiBAaW5oZXJpdHMgUmVuZGVyZXJcbiAqIEBha2EgTC5DYW52YXNcbiAqXG4gKiBBbGxvd3MgdmVjdG9yIGxheWVycyB0byBiZSBkaXNwbGF5ZWQgd2l0aCBbYDxjYW52YXM+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc19BUEkpLlxuICogSW5oZXJpdHMgYFJlbmRlcmVyYC5cbiAqXG4gKiBEdWUgdG8gW3RlY2huaWNhbCBsaW1pdGF0aW9uc10oaHR0cDovL2Nhbml1c2UuY29tLyNzZWFyY2g9Y2FudmFzKSwgQ2FudmFzIGlzIG5vdFxuICogYXZhaWxhYmxlIGluIGFsbCB3ZWIgYnJvd3NlcnMsIG5vdGFibHkgSUU4LCBhbmQgb3ZlcmxhcHBpbmcgZ2VvbWV0cmllcyBtaWdodFxuICogbm90IGRpc3BsYXkgcHJvcGVybHkgaW4gc29tZSBlZGdlIGNhc2VzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogVXNlIENhbnZhcyBieSBkZWZh
14775dWx0IGZvciBhbGwgcGF0aHMgaW4gdGhlIG1hcDpcbiAqXG4gKiBgYGBqc1xuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XG4gKiBcdHJlbmRlcmVyOiBMLmNhbnZhcygpXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIFVzZSBhIENhbnZhcyByZW5kZXJlciB3aXRoIGV4dHJhIHBhZGRpbmcgZm9yIHNwZWNpZmljIHZlY3RvciBnZW9tZXRyaWVzOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcpO1xuICogdmFyIG15UmVuZGVyZXIgPSBMLmNhbnZhcyh7IHBhZGRpbmc6IDAuNSB9KTtcbiAqIHZhciBsaW5lID0gTC5wb2x5bGluZSggY29vcmRpbmF0ZXMsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogdmFyIGNpcmNsZSA9IEwuY2lyY2xlKCBjZW50ZXIsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogYGBgXG4gKi9cblxudmFyIENhbnZhcyA9IFJlbmRlcmVyLmV4dGVuZCh7XG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSBSZW5kZXJlci5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XG5cdFx0ZXZlbnRzLnZpZXdwcmVyZXNldCA9IHRoaXMuX29uVmlld1ByZVJlc2V0O1xuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X29uVmlld1ByZVJlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gU2V0IGEgZmxhZyBzbyB0aGF0IGEgdmlld3ByZXJlc2V0K21vdmVlbmQrdmlld3Jlc2V0IG9ubHkgdXBkYXRlcyZyZWRyYXdzIG9uY2Vcblx0XHR0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzID0gdHJ1ZTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdFJlbmRlcmVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMpO1xuXG5cdFx0Ly8gUmVkcmF3IHZlY3RvcnMgc2luY2UgY2FudmFzIGlzIGNsZWFyZWQgdXBvbiByZW1vdmFsLFxuXHRcdC8vIGluIGNhc2Ugb2YgcmVtb3ZpbmcgdGhlIHJlbmRlcmVyIGl0c2VsZiBmcm9tIHRoZSBtYXAuXG5cdFx0dGhpcy5fZHJhdygpO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuXG5cdFx0b24oY29udGFpbmVyLCAnbW91c2Vtb3ZlJywgdGhpcy5fb25Nb3VzZU1vdmUsIHRoaXMpO1xuXHRcdG9uKGNvbnRhaW5lciwgJ2NsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIGNvbnRleHRtZW51JywgdGhpcy5fb25DbGljaywgdGhpcyk7XG5cdFx0b24oY29udGFpbmVyLCAnbW91c2VvdXQnLCB0aGlzLl9oYW5kbGVNb3VzZU91dCwgdGhpcyk7XG5cblx0XHR0aGlzLl9jdHggPSBjb250YWluZXIuZ2V0Q29udGV4dCgnMmQnKTtcblx0fSxcblxuXHRfZGVzdHJveUNvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xuXHRcdGNhbmNlbEFuaW1GcmFtZSh0aGlzLl9yZWRyYXdSZXF1ZXN0KTtcblx0XHRkZWxldGUgdGhpcy5fY3R4O1xuXHRcdHJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG9mZih0aGlzLl9jb250YWluZXIpO1xuXHRcdGRlbGV0ZSB0aGlzLl9jb250YWluZXI7XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGhzOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3Bvc3Rwb25lVXBkYXRlUGF0aHMpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgbGF5ZXI7XG5cdFx0dGhpcy5fcmVkcmF3Qm91bmRzID0gbnVsbDtcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2lkXTtcblx0XHRcdGxheWVyLl91cGRhdGUoKTtcblx0XHR9XG5cdFx0dGhpcy5fcmVkcmF3KCk7XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXAuX2FuaW1hdGluZ1pvb20gJiYgdGhpcy5fYm91bmRzKSB7IHJldHVybjsgfVxuXG5cdFx0UmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblxuXHRcdHZhciBiID0gdGhpcy5fYm91bmRzLFxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIsXG5cdFx0ICAgIHNpemUgPSBiLmdldFNpemUoKSxcblx0XHQgICAgbSA9IHJldGluYSA/IDIgOiAxO1xuXG5cdFx0c2V0UG9zaXRpb24oY29udGFpbmVyLCBiLm1pbik7XG5cblx0XHQvLyBzZXQgY2FudmFzIHNpemUgKGFsc28gY2xlYXJpbmcgaXQpOyB1c2UgZG91YmxlIHNpemUgb24gcmV0aW5hXG5cdFx0Y29udGFpbmVyLndpZHR
14775oID0gbSAqIHNpemUueDtcblx0XHRjb250YWluZXIuaGVpZ2h0ID0gbSAqIHNpemUueTtcblx0XHRjb250YWluZXIuc3R5bGUud2lkdGggPSBzaXplLnggKyAncHgnO1xuXHRcdGNvbnRhaW5lci5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXG5cdFx0aWYgKHJldGluYSkge1xuXHRcdFx0dGhpcy5fY3R4LnNjYWxlKDIsIDIpO1xuXHRcdH1cblxuXHRcdC8vIHRyYW5zbGF0ZSBzbyB3ZSB1c2UgdGhlIHNhbWUgcGF0aCBjb29yZGluYXRlcyBhZnRlciBjYW52YXMgZWxlbWVudCBtb3Zlc1xuXHRcdHRoaXMuX2N0eC50cmFuc2xhdGUoLWIubWluLngsIC1iLm1pbi55KTtcblxuXHRcdC8vIFRlbGwgcGF0aHMgdG8gcmVkcmF3IHRoZW1zZWx2ZXNcblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdFJlbmRlcmVyLnByb3RvdHlwZS5fcmVzZXQuY2FsbCh0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzKSB7XG5cdFx0XHR0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzID0gZmFsc2U7XG5cdFx0XHR0aGlzLl91cGRhdGVQYXRocygpO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3VwZGF0ZURhc2hBcnJheShsYXllcik7XG5cdFx0dGhpcy5fbGF5ZXJzW3N0YW1wKGxheWVyKV0gPSBsYXllcjtcblxuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlciA9IHtcblx0XHRcdGxheWVyOiBsYXllcixcblx0XHRcdHByZXY6IHRoaXMuX2RyYXdMYXN0LFxuXHRcdFx0bmV4dDogbnVsbFxuXHRcdH07XG5cdFx0aWYgKHRoaXMuX2RyYXdMYXN0KSB7IHRoaXMuX2RyYXdMYXN0Lm5leHQgPSBvcmRlcjsgfVxuXHRcdHRoaXMuX2RyYXdMYXN0ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gdGhpcy5fZHJhd0ZpcnN0IHx8IHRoaXMuX2RyYXdMYXN0O1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0xhc3QgPSBwcmV2O1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRkZWxldGUgbGF5ZXIuX29yZGVyO1xuXG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tzdGFtcChsYXllcildO1xuXG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdC8vIFJlZHJhdyB0aGUgdW5pb24gb2YgdGhlIGxheWVyJ3Mgb2xkIHBpeGVsXG5cdFx0Ly8gYm91bmRzIGFuZCB0aGUgbmV3IHBpeGVsIGJvdW5kcy5cblx0XHR0aGlzLl9leHRlbmRSZWRyYXdCb3VuZHMobGF5ZXIpO1xuXHRcdGxheWVyLl9wcm9qZWN0KCk7XG5cdFx0bGF5ZXIuX3VwZGF0ZSgpO1xuXHRcdC8vIFRoZSByZWRyYXcgd2lsbCBleHRlbmQgdGhlIHJlZHJhdyBib3VuZHNcblx0XHQvLyB3aXRoIHRoZSBuZXcgcGl4ZWwgYm91bmRzLlxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF91cGRhdGVTdHlsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dGhpcy5fdXBkYXRlRGFzaEFycmF5KGxheWVyKTtcblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfdXBkYXRlRGFzaEFycmF5OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRpZiAodHlwZW9mIGxheWVyLm9wdGlvbnMuZGFzaEFycmF5ID09PSAnc3RyaW5nJykge1xuXHRcdFx0dmFyIHBhcnRzID0gbGF5ZXIub3B0aW9ucy5kYXNoQXJyYXkuc3BsaXQoL1ssIF0rLyksXG5cdFx0XHQgICAgZGFzaEFycmF5ID0gW10sXG5cdFx0XHQgICAgZGFzaFZhbHVlLFxuXHRcdFx0ICAgIGk7XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcblx0XHRcdFx0ZGFzaFZhbHVlID0gTnVtYmVyKHBhcnRzW2ldKTtcblx0XHRcdFx0Ly8gSWdub3JlIGRhc2ggYXJyYXkgY29udGFpbmluZyBpbnZhbGlkIGxlbmd0aHNcblx0XHRcdFx0aWYgKGlzTmFOKGRhc2hWYWx1ZSkpIHsgcmV0dXJuOyB9XG5cdFx0XHRcdGRhc2hBcnJheS5wdXNoKGRhc2hWYWx1ZSk7XG5cdFx0XHR9XG5cdFx0XHRsYXllci5vcHRpb25zLl9kYXNoQXJyYXkgPSBkYXNoQXJyYXk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGxheWVyLm9wdGlvbnMuX2Rhc2hBcnJheSA9IGxheWVyLm9wdGlvbnMuZGFzaEFycmF5O1xuXHRcdH1cblx0fSxcblxuXHRfcmVxdWVzdFJlZHJhdzogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9leHRlbmRSZWRyYXdCb3VuZHMobGF5ZXIpO1xuXHRcdHRoaXMuX3JlZHJhd1JlcXVlc3QgPSB0aGlzLl9yZWRyYXdSZXF1ZXN0IHx8IHJlcXVlc3RBbmltRnJhbWUodGhpcy5fcmVkcmF3LCB0aGlzKTtcblx0fSxcblxuXHRfZXh0ZW5kUmVkcmF3Qm91bmRzOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRpZiAobGF5ZXIuX3B4Qm91bmRzKSB7XG5cdFx0XHR2YXIgcGFkZGluZyA9IChsYXllci5vcHRpb25zLndlaWdodCB8fCAwKSArIDE7XG5cdFx0XHR0aGlzLl9yZWRyYXdCb3VuZHMgPSB0aGlzLl9yZWRyYXdCb3VuZHMgfHwgbmV3IEJvdW5kcygpO1xuXHRcdFx0dGhpcy5fcmVkcmF3Qm91bmRzLmV4dGVuZChsYXllci5fcHhCb3VuZHMubWluLnN1YnRyYWN0KFtwYWRkaW5nLCBwYWRkaW5nXSkpO1xuXHRcdFx0dGhpcy5fcmVkcmF3Qm91bmRzLmV4dGVuZChsYXllci5fcHhCb3VuZHMubWF4LmFkZChbcGFkZGluZywgcGFkZGluZ10pKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlZHJhdzogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlZHJhd1JlcXVlc3QgPSBudWxsO1xuXG5cdFx0aWYgKHRoaXMuX3JlZHJhd0JvdW5kcykge1xuXHRcdFx0dGhpcy5fcmVkcmF3Qm91bmRzLm1pbi5fZmxvb3IoKTtcblx0XHRcdHRoaXMuX3JlZHJhd0JvdW5kcy5tYXguX2NlaWwoKTtcblx0XHR9XG5cblx0XHR0aGlzLl9jbGVhcigpOyAvLyBjbGVhciBsYXllcnMgaW4gcmVkcmF3IGJvdW5kc1xuXHRcdHRoaXMuX2RyYXcoKTsgLy8gZHJhdyBsYXllcnNcblxuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IG51bGw7XG5cdH0sXG5cblx0X2NsZWFyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR2YXIgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cdFx0XHR0aGlzLl9jdHguY2xlYXJSZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2N0eC5jbGVhclJlY3QoMCwgMCwgdGhpcy5fY29udGFpbmVyLndpZHR
14775oLCB0aGlzLl9jb250YWluZXIuaGVpZ2h0KTtcblx0XHR9XG5cdH0sXG5cblx0X2RyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbGF5ZXIsIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHR0aGlzLl9jdHguc2F2ZSgpO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHZhciBzaXplID0gYm91bmRzLmdldFNpemUoKTtcblx0XHRcdHRoaXMuX2N0eC5iZWdpblBhdGgoKTtcblx0XHRcdHRoaXMuX2N0eC5yZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0XHR0aGlzLl9jdHguY2xpcCgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXdpbmcgPSB0cnVlO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAoIWJvdW5kcyB8fCAobGF5ZXIuX3B4Qm91bmRzICYmIGxheWVyLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpKSB7XG5cdFx0XHRcdGxheWVyLl91cGRhdGVQYXRoKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhd2luZyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5fY3R4LnJlc3RvcmUoKTsgIC8vIFJlc3RvcmUgc3RhdGUgYmVmb3JlIGNsaXBwaW5nLlxuXHR9LFxuXG5cdF91cGRhdGVQb2x5OiBmdW5jdGlvbiAobGF5ZXIsIGNsb3NlZCkge1xuXHRcdGlmICghdGhpcy5fZHJhd2luZykgeyByZXR1cm47IH1cblxuXHRcdHZhciBpLCBqLCBsZW4yLCBwLFxuXHRcdCAgICBwYXJ0cyA9IGxheWVyLl9wYXJ0cyxcblx0XHQgICAgbGVuID0gcGFydHMubGVuZ3RoLFxuXHRcdCAgICBjdHggPSB0aGlzLl9jdHg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm47IH1cblxuXHRcdGN0eC5iZWdpblBhdGgoKTtcblxuXHRcdGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBhcnRzW2ldLmxlbmd0aDsgaiA8IGxlbjI7IGorKykge1xuXHRcdFx0XHRwID0gcGFydHNbaV1bal07XG5cdFx0XHRcdGN0eFtqID8gJ2xpbmVUbycgOiAnbW92ZVRvJ10ocC54LCBwLnkpO1xuXHRcdFx0fVxuXHRcdFx0aWYgKGNsb3NlZCkge1xuXHRcdFx0XHRjdHguY2xvc2VQYXRoKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fZmlsbFN0cm9rZShjdHgsIGxheWVyKTtcblxuXHRcdC8vIFRPRE8gb3B0aW1pemF0aW9uOiAxIGZpbGwvc3Ryb2tlIGZvciBhbGwgZmVhdHVyZXMgd2l0aCBlcXVhbCBzdHlsZSBpbnN0ZWFkIG9mIDEgZm9yIGVhY2ggZmVhdHVyZVxuXHR9LFxuXG5cdF91cGRhdGVDaXJjbGU6IGZ1bmN0aW9uIChsYXllcikge1xuXG5cdFx0aWYgKCF0aGlzLl9kcmF3aW5nIHx8IGxheWVyLl9lbXB0eSgpKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQsXG5cdFx0ICAgIGN0eCA9IHRoaXMuX2N0eCxcblx0XHQgICAgciA9IE1hdGgubWF4KE1hdGgucm91bmQobGF5ZXIuX3JhZGl1cyksIDEpLFxuXHRcdCAgICBzID0gKE1hdGgubWF4KE1hdGgucm91bmQobGF5ZXIuX3JhZGl1c1kpLCAxKSB8fCByKSAvIHI7XG5cblx0XHRpZiAocyAhPT0gMSkge1xuXHRcdFx0Y3R4LnNhdmUoKTtcblx0XHRcdGN0eC5zY2FsZSgxLCBzKTtcblx0XHR9XG5cblx0XHRjdHguYmVnaW5QYXRoKCk7XG5cdFx0Y3R4LmFyYyhwLngsIHAueSAvIHMsIHIsIDAsIE1hdGguUEkgKiAyLCBmYWxzZSk7XG5cblx0XHRpZiAocyAhPT0gMSkge1xuXHRcdFx0Y3R4LnJlc3RvcmUoKTtcblx0XHR9XG5cblx0XHR0aGlzLl9maWxsU3Ryb2tlKGN0eCwgbGF5ZXIpO1xuXHR9LFxuXG5cdF9maWxsU3Ryb2tlOiBmdW5jdGlvbiAoY3R4LCBsYXllcikge1xuXHRcdHZhciBvcHRpb25zID0gbGF5ZXIub3B0aW9ucztcblxuXHRcdGlmIChvcHRpb25zLmZpbGwpIHtcblx0XHRcdGN0eC5nbG9iYWxBbHBoYSA9IG9wdGlvbnMuZmlsbE9wYWNpdHk7XG5cdFx0XHRjdHguZmlsbFN0eWxlID0gb3B0aW9ucy5maWxsQ29sb3IgfHwgb3B0aW9ucy5jb2xvcjtcblx0XHRcdGN0eC5maWxsKG9wdGlvbnMuZmlsbFJ1bGUgfHwgJ2V2ZW5vZGQnKTtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5zdHJva2UgJiYgb3B0aW9ucy53ZWlnaHQgIT09IDApIHtcblx0XHRcdGlmIChjdHguc2V0TGluZURhc2gpIHtcblx0XHRcdFx0Y3R4LnNldExpbmVEYXNoKGxheWVyLm9wdGlvbnMgJiYgbGF5ZXIub3B0aW9ucy5fZGFzaEFycmF5IHx8IFtdKTtcblx0XHRcdH1cblx0XHRcdGN0eC5nbG9iYWxBbHBoYSA9IG9wdGlvbnMub3BhY2l0eTtcblx0XHRcdGN0eC5saW5lV2lkdGggPSBvcHRpb25zLndlaWdodDtcblx0XHRcdGN0eC5zdHJva2VTdHlsZSA9IG9wdGlvbnMuY29sb3I7XG5cdFx0XHRjdHgubGluZUNhcCA9IG9wdGlvbnMubGluZUNhcDtcblx0XHRcdGN0eC5saW5lSm9pbiA9IG9wdGlvbnMubGluZUpvaW47XG5cdFx0XHRjdHguc3Ryb2tlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIENhbnZhcyBvYnZpb3VzbHkgZG9lc24ndCBoYXZlIG1vdXNlIGV2ZW50cyBmb3IgaW5kaXZpZHVhbCBkcmF3biBvYmplY3RzLFxuXHQvLyBzbyB3ZSBlbXVsYXRlIHRoYXQgYnkgY2FsY3VsYXRpbmcgd2hhdCdzIHVuZGVyIHRoZSBtb3VzZSBvbiBtb3VzZW1vdmUvY2xpY2sgbWFudWFsbHlcblxuXHRfb25DbGljazogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgcG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvTGF5ZXJQb2ludChlKSwgbGF5ZXIsIGNsaWNrZWRMYXllcjtcblxuXHRcdGZvciAodmFyIG9yZGVyID0gdGhpcy5fZHJhd0ZpcnN0OyBvcmRlcjsgb3JkZXIgPSBvcmRlci5uZXh0KSB7XG5cdFx0XHRsYXllciA9IG9yZGVyLmxheWVyO1xuXHRcdFx0aWYgKGxheWVyLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgbGF5ZXIuX2NvbnRhaW5zUG9pbnQocG9pbnQpICYmICF0aGlzLl9tYXAuX2RyYWdnYWJsZU1vdmVkKGxheWVyKSkge1xuXHRcdFx0XHRjbGlja2VkTGF5ZXIgPSBsYXllcjtcblx0XHRcdH1cblx0XHR9XG5cdFx0aWYgKGNsaWNrZWRMYXllcikgIHtcblx0XHRcdGZha2VTdG9wKGUpO1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtjbGlja2VkTGF5ZXJdLCBlKTtcblx0XHR9XG5cdH0sXG5cblx0X29uTW91c2VNb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghdGhpcy5fbWFwIHx8IHRoaXMuX21hcC5kcmFnZ2luZy5tb3ZpbmcoKSB8fCB0aGlzLl9tYXAuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvTGF5ZXJQb2ludChlKTtcblx0XHR0aGlzLl9oYW5kbGVNb3VzZUhvdmVyKGUsIHBvaW50KTtcblx0fSxcblxuXG5cdF9oYW5kbGVNb3VzZU91dDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbGF5ZXIgPSB0aGlzLl9ob3ZlcmVkTGF5ZXI7XG5cdFx0aWYgKGxheWVyKSB7XG5cdFx0XHQvLyBpZiB3ZSdyZSBsZWF2aW5nIHRoZSBsYXllciwgZmlyZSBtb3VzZW91dFxuXHRcdFx0cmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtsYXllcl0sIGUsICdtb3VzZW91dCcpO1xuXHRcdFx0dGhpcy5faG92ZXJlZExheWVyID0gbnVsbDtcblx0XHRcdHRoaXMuX21vdXNlSG92ZXJUaHJvdHRsZWQgPSBmYWxzZTtcblx0XHR9XG5cdH0sXG5cblx0X2hhbmRsZU1vdXNlSG92ZXI6IGZ1bmN0aW9uIChlLCBwb2ludCkge1xuXHRcdGlmICh0aGlzLl9tb3VzZUhvdmVyVGhyb3R0bGVkKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGxheWVyLCBjYW5kaWRhdGVIb3ZlcmVkTGF5ZXI7XG5cblx0XHRmb3IgKHZhciBvcmRlciA9IHRoaXMuX2RyYXdGaXJzdDsgb3JkZXI7IG9yZGVyID0gb3JkZXIubmV4dCkge1xuXHRcdFx0bGF5ZXIgPSBvcmRlci5sYXllcjtcblx0XHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlICYmIGxheWVyLl9jb250YWluc1BvaW50KHBvaW50KSkge1xuXHRcdFx0XHRjYW5kaWRhdGVIb3ZlcmVkTGF5ZXIgPSBsYXllcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyICE9PSB0aGlzLl9ob3ZlcmVkTGF5ZXIpIHtcblx0XHRcdHRoaXMuX2hhbmRsZU1vdXNlT3V0KGUpO1xuXG5cdFx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyKSB7XG5cdFx0XHRcdGFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTsgLy8gY2hhbmdlIGN1cnNvclxuXHRcdFx0XHR0aGlzLl9maXJlRXZlbnQoW2NhbmRpZGF0ZUhvdmVyZWRMYXllcl0sIGUsICdtb3VzZW92ZXInKTtcblx0XHRcdFx0dGhpcy5faG92ZXJlZExheWVyID0gY2FuZGlkYXRlSG92ZXJlZExheWVyO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICh0aGlzLl9ob3ZlcmVkTGF5ZXIpIHtcblx0XHRcdHRoaXMuX2ZpcmVFdmVudChbdGhpcy5faG92ZXJlZExheWVyXSwgZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fbW91c2VIb3ZlclRocm90dGxlZCA9IHRydWU7XG5cdFx0c2V0VGltZW91dChMLmJpbmQoZnVuY3Rpb24gKCkge1xuXHRcdFx0dGhpcy5fbW91c2VIb3ZlclRocm90dGxlZCA9IGZhbHNlO1xuXHRcdH0sIHRoaXMpLCAzMik7XG5cdH0sXG5cblx0X2Z
14775pcmVFdmVudDogZnVuY3Rpb24gKGxheWVycywgZSwgdHlwZSkge1xuXHRcdHRoaXMuX21hcC5fZmlyZURPTUV2ZW50KGUsIHR5cGUgfHwgZS50eXBlLCBsYXllcnMpO1xuXHR9LFxuXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlcjtcblxuXHRcdGlmICghb3JkZXIpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgbmV4dCA9IG9yZGVyLm5leHQ7XG5cdFx0dmFyIHByZXYgPSBvcmRlci5wcmV2O1xuXG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdG5leHQucHJldiA9IHByZXY7XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEFscmVhZHkgbGFzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2UgaWYgKG5leHQpIHtcblx0XHRcdC8vIFVwZGF0ZSBmaXJzdCBlbnRyeSB1bmxlc3MgdGhpcyBpcyB0aGVcblx0XHRcdC8vIHNpbmdsZSBlbnRyeVxuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRvcmRlci5wcmV2ID0gdGhpcy5fZHJhd0xhc3Q7XG5cdFx0dGhpcy5fZHJhd0xhc3QubmV4dCA9IG9yZGVyO1xuXG5cdFx0b3JkZXIubmV4dCA9IG51bGw7XG5cdFx0dGhpcy5fZHJhd0xhc3QgPSBvcmRlcjtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXG5cdFx0aWYgKCFvcmRlcikgeyByZXR1cm47IH1cblxuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gQWxyZWFkeSBmaXJzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2UgaWYgKHByZXYpIHtcblx0XHRcdC8vIFVwZGF0ZSBsYXN0IGVudHJ5IHVubGVzcyB0aGlzIGlzIHRoZVxuXHRcdFx0Ly8gc2luZ2xlIGVudHJ5XG5cdFx0XHR0aGlzLl9kcmF3TGFzdCA9IHByZXY7XG5cdFx0fVxuXG5cdFx0b3JkZXIucHJldiA9IG51bGw7XG5cblx0XHRvcmRlci5uZXh0ID0gdGhpcy5fZHJhd0ZpcnN0O1xuXHRcdHRoaXMuX2RyYXdGaXJzdC5wcmV2ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gb3JkZXI7XG5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuY2FudmFzKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIENhbnZhcyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuZnVuY3Rpb24gY2FudmFzJDEob3B0aW9ucykge1xuXHRyZXR1cm4gY2FudmFzID8gbmV3IENhbnZhcyhvcHRpb25zKSA6IG51bGw7XG59XG5cbi8qXG4gKiBUaGFua3MgdG8gRG1pdHJ5IEJhcmFub3Zza3kgYW5kIGhpcyBSYXBoYWVsIGxpYnJhcnkgZm9yIGluc3BpcmF0aW9uIVxuICovXG5cblxudmFyIHZtbENyZWF0ZSA9IChmdW5jdGlvbiAoKSB7XG5cdHRyeSB7XG5cdFx0ZG9jdW1lbnQubmFtZXNwYWNlcy5hZGQoJ2x2bWwnLCAndXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwnKTtcblx0XHRyZXR1cm4gZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCc8bHZtbDonICsgbmFtZSArICcgY2xhc3M9XCJsdm1sXCI+Jyk7XG5cdFx0fTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdHJldHVybiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJzwnICsgbmFtZSArICcgeG1sbnM9XCJ1cm46c2NoZW1hcy1taWNyb3NvZnQuY29tOnZtbFwiIGNsYXNzPVwibHZtbFwiPicpO1xuXHRcdH07XG5cdH1cbn0pKCk7XG5cblxuLypcbiAqIEBjbGFzcyBTVkdcbiAqXG4gKlxuICogVk1MIHdhcyBkZXByZWNhdGVkIGluIDIwMTIsIHdoaWNoIG1lYW5zIFZNTCBmdW5jdGlvbmFsaXR5IGV4aXN0cyBvbmx5IGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eVxuICogd2l0aCBvbGQgdmVyc2lvbnMgb2YgSW50ZXJuZXQgRXhwbG9yZXIuXG4gKi9cblxuLy8gbWl4aW4gdG8gcmVkZWZpbmUgc29tZSBTVkcgbWV0aG9kcyB0byBoYW5kbGUgVk1MIHN5bnRheCB3aGljaCBpcyBzaW1pbGFyIGJ1dCB3aXRoIHNvbWUgZGlmZmVyZW5jZXNcbnZhciB2bWxNaXhpbiA9IHtcblxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IGNyZWF0ZSQxKCdkaXYnLCAnbGVhZmxldC12bWwtY29udGFpbmVyJyk7XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXAuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XG5cdFx0UmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdF9pbml0UGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXIgPSB2bWxDcmVhdGUoJ3NoYXBlJyk7XG5cblx0XHRhZGRDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXZtbC1zaGFwZSAnICsgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUgfHwgJycpKTtcblxuXHRcdGNvbnRhaW5lci5jb29yZHNpemUgPSAnMSAxJztcblxuXHRcdGxheWVyLl9wYXRoID0gdm1sQ3JlYXRlKCdwYXRoJyk7XG5cdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKGxheWVyLl9wYXRoKTtcblxuXHRcdHRoaXMuX3VwZGF0ZVN0eWxlKGxheWVyKTtcblx0XHR0aGlzLl9sYXllcnNbc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgY29udGFpbmVyID0gbGF5ZXIuX2NvbnRhaW5lcjtcblx0XHR0aGlzLl9jb250YWluZXIuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcblxuXHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlKSB7XG5cdFx0XHRsYXllci5hZGRJb
14775nRlcmFjdGl2ZVRhcmdldChjb250YWluZXIpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXI7XG5cdFx0cmVtb3ZlKGNvbnRhaW5lcik7XG5cdFx0bGF5ZXIucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQoY29udGFpbmVyKTtcblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW3N0YW1wKGxheWVyKV07XG5cdH0sXG5cblx0X3VwZGF0ZVN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgc3Ryb2tlID0gbGF5ZXIuX3N0cm9rZSxcblx0XHQgICAgZmlsbCA9IGxheWVyLl9maWxsLFxuXHRcdCAgICBvcHRpb25zID0gbGF5ZXIub3B0aW9ucyxcblx0XHQgICAgY29udGFpbmVyID0gbGF5ZXIuX2NvbnRhaW5lcjtcblxuXHRcdGNvbnRhaW5lci5zdHJva2VkID0gISFvcHRpb25zLnN0cm9rZTtcblx0XHRjb250YWluZXIuZmlsbGVkID0gISFvcHRpb25zLmZpbGw7XG5cblx0XHRpZiAob3B0aW9ucy5zdHJva2UpIHtcblx0XHRcdGlmICghc3Ryb2tlKSB7XG5cdFx0XHRcdHN0cm9rZSA9IGxheWVyLl9zdHJva2UgPSB2bWxDcmVhdGUoJ3N0cm9rZScpO1xuXHRcdFx0fVxuXHRcdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKHN0cm9rZSk7XG5cdFx0XHRzdHJva2Uud2VpZ2h0ID0gb3B0aW9ucy53ZWlnaHQgKyAncHgnO1xuXHRcdFx0c3Ryb2tlLmNvbG9yID0gb3B0aW9ucy5jb2xvcjtcblx0XHRcdHN0cm9rZS5vcGFjaXR5ID0gb3B0aW9ucy5vcGFjaXR5O1xuXG5cdFx0XHRpZiAob3B0aW9ucy5kYXNoQXJyYXkpIHtcblx0XHRcdFx0c3Ryb2tlLmRhc2hTdHlsZSA9IGlzQXJyYXkob3B0aW9ucy5kYXNoQXJyYXkpID9cblx0XHRcdFx0ICAgIG9wdGlvbnMuZGFzaEFycmF5LmpvaW4oJyAnKSA6XG5cdFx0XHRcdCAgICBvcHRpb25zLmRhc2hBcnJheS5yZXBsYWNlKC8oICosICopL2csICcgJyk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRzdHJva2UuZGFzaFN0eWxlID0gJyc7XG5cdFx0XHR9XG5cdFx0XHRzdHJva2UuZW5kY2FwID0gb3B0aW9ucy5saW5lQ2FwLnJlcGxhY2UoJ2J1dHQnLCAnZmxhdCcpO1xuXHRcdFx0c3Ryb2tlLmpvaW5zdHlsZSA9IG9wdGlvbnMubGluZUpvaW47XG5cblx0XHR9IGVsc2UgaWYgKHN0cm9rZSkge1xuXHRcdFx0Y29udGFpbmVyLnJlbW92ZUNoaWxkKHN0cm9rZSk7XG5cdFx0XHRsYXllci5fc3Ryb2tlID0gbnVsbDtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRpZiAoIWZpbGwpIHtcblx0XHRcdFx0ZmlsbCA9IGxheWVyLl9maWxsID0gdm1sQ3JlYXRlKCdmaWxsJyk7XG5cdFx0XHR9XG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoZmlsbCk7XG5cdFx0XHRmaWxsLmNvbG9yID0gb3B0aW9ucy5maWxsQ29sb3IgfHwgb3B0aW9ucy5jb2xvcjtcblx0XHRcdGZpbGwub3BhY2l0eSA9IG9wdGlvbnMuZmlsbE9wYWNpdHk7XG5cblx0XHR9IGVsc2UgaWYgKGZpbGwpIHtcblx0XHRcdGNvbnRhaW5lci5yZW1vdmVDaGlsZChmaWxsKTtcblx0XHRcdGxheWVyLl9maWxsID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQucm91bmQoKSxcblx0XHQgICAgciA9IE1hdGgucm91bmQobGF5ZXIuX3JhZGl1cyksXG5cdFx0ICAgIHIyID0gTWF0aC5yb3VuZChsYXllci5fcmFkaXVzWSB8fCByKTtcblxuXHRcdHRoaXMuX3NldFBhdGgobGF5ZXIsIGxheWVyLl9lbXB0eSgpID8gJ00wIDAnIDpcblx0XHRcdCdBTCAnICsgcC54ICsgJywnICsgcC55ICsgJyAnICsgciArICcsJyArIHIyICsgJyAwLCcgKyAoNjU1MzUgKiAzNjApKTtcblx0fSxcblxuXHRfc2V0UGF0aDogZnVuY3Rpb24gKGxheWVyLCBwYXRoKSB7XG5cdFx0bGF5ZXIuX3BhdGgudiA9IHBhdGg7XG5cdH0sXG5cblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dG9Gcm9udChsYXllci5fY29udGFpbmVyKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRvQmFjayhsYXllci5fY29udGFpbmVyKTtcblx0fVxufTtcblxudmFyIGNyZWF0ZSQyID0gdm1sID8gdm1sQ3JlYXRlIDogc3ZnQ3JlYXRlO1xuXG4vKlxuICogQGNsYXNzIFNWR1xuICogQGluaGVyaXRzIFJlbmRlcmVyXG4gKiBAYWthIEwuU1ZHXG4gKlxuICogQWxsb3dzIHZlY3RvciBsYXllcnMgdG8gYmUgZGlzcGxheWVkIHdpdGggW1NWR10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHKS5cbiAqIEluaGVyaXRzIGBSZW5kZXJlcmAuXG4gKlxuICogRHVlIHRvIFt0ZWNobmljYWwgbGltaXRhdGlvbnNdKGh0dHA6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPXN2ZyksIFNWRyBpcyBub3RcbiAqIGF2YWlsYWJsZSBpbiBhbGwgd2ViIGJyb3dzZXJzLCBub3RhYmx5IEFuZHJvaWQgMi54IGFuZCAzLnguXG4gKlxuICogQWx0aG91Z2ggU1ZHIGlzIG5vdCBhdmFpbGFibGUgb24gSUU3IGFuZCBJRTgsIHRoZXNlIGJyb3dzZXJzIHN1cHBvcnRcbiAqIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpXG4gKiAoYSBub3cgZGVwcmVjYXRlZCB0ZWNobm9sb2d5KSwgYW5kIHR
14775oZSBTVkcgcmVuZGVyZXIgd2lsbCBmYWxsIGJhY2sgdG8gVk1MIGluXG4gKiB0aGlzIGNhc2UuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBVc2UgU1ZHIGJ5IGRlZmF1bHQgZm9yIGFsbCBwYXRocyBpbiB0aGUgbWFwOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcbiAqIFx0cmVuZGVyZXI6IEwuc3ZnKClcbiAqIH0pO1xuICogYGBgXG4gKlxuICogVXNlIGEgU1ZHIHJlbmRlcmVyIHdpdGggZXh0cmEgcGFkZGluZyBmb3Igc3BlY2lmaWMgdmVjdG9yIGdlb21ldHJpZXM6XG4gKlxuICogYGBganNcbiAqIHZhciBtYXAgPSBMLm1hcCgnbWFwJyk7XG4gKiB2YXIgbXlSZW5kZXJlciA9IEwuc3ZnKHsgcGFkZGluZzogMC41IH0pO1xuICogdmFyIGxpbmUgPSBMLnBvbHlsaW5lKCBjb29yZGluYXRlcywgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiB2YXIgY2lyY2xlID0gTC5jaXJjbGUoIGNlbnRlciwgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiBgYGBcbiAqL1xuXG52YXIgU1ZHID0gUmVuZGVyZXIuZXh0ZW5kKHtcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gUmVuZGVyZXIucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXHRcdGV2ZW50cy56b29tc3RhcnQgPSB0aGlzLl9vblpvb21TdGFydDtcblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gY3JlYXRlJDIoJ3N2ZycpO1xuXG5cdFx0Ly8gbWFrZXMgaXQgcG9zc2libGUgdG8gY2xpY2sgdGhyb3VnaCBzdmcgcm9vdDsgd2UnbGwgcmVzZXQgaXQgYmFjayBpbiBpbmRpdmlkdWFsIHBhdGhzXG5cdFx0dGhpcy5fY29udGFpbmVyLnNldEF0dHJpYnV0ZSgncG9pbnRlci1ldmVudHMnLCAnbm9uZScpO1xuXG5cdFx0dGhpcy5fcm9vdEdyb3VwID0gY3JlYXRlJDIoJ2cnKTtcblx0XHR0aGlzLl9jb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5fcm9vdEdyb3VwKTtcblx0fSxcblxuXHRfZGVzdHJveUNvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xuXHRcdHJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG9mZih0aGlzLl9jb250YWluZXIpO1xuXHRcdGRlbGV0ZSB0aGlzLl9jb250YWluZXI7XG5cdFx0ZGVsZXRlIHRoaXMuX3Jvb3RHcm91cDtcblx0XHRkZWxldGUgdGhpcy5fc3ZnU2l6ZTtcblx0fSxcblxuXHRfb25ab29tU3RhcnQ6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBEcmFnLXRoZW4tcGluY2ggaW50ZXJhY3Rpb25zIG1pZ2h0IG1lc3MgdXAgdGhlIGNlbnRlciBhbmQgem9vbS5cblx0XHQvLyBJbiB0aGlzIGNhc2UsIHRoZSBlYXNpZXN0IHdheSB0byBwcmV2ZW50IHRoaXMgaXMgcmUtZG8gdGhlIHJlbmRlcmVyXG5cdFx0Ly8gICBib3VuZHMgYW5kIHBhZGRpbmcgd2hlbiB0aGUgem9vbWluZyBzdGFydHMuXG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXAuX2FuaW1hdGluZ1pvb20gJiYgdGhpcy5fYm91bmRzKSB7IHJldHVybjsgfVxuXG5cdFx0UmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblxuXHRcdHZhciBiID0gdGhpcy5fYm91bmRzLFxuXHRcdCAgICBzaXplID0gYi5nZXRTaXplKCksXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcblxuXHRcdC8vIHNldCBzaXplIG9mIHN2Zy1jb250YWluZXIgaWYgY2hhbmdlZFxuXHRcdGlmICghdGhpcy5fc3ZnU2l6ZSB8fCAhdGhpcy5fc3ZnU2l6ZS5lcXVhbHMoc2l6ZSkpIHtcblx0XHRcdHRoaXMuX3N2Z1NpemUgPSBzaXplO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCBzaXplLngpO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnaGVpZ2h0Jywgc2l6ZS55KTtcblx0XHR9XG5cblx0XHQvLyBtb3ZlbWVudDogdXBkYXRlIGNvbnRhaW5lciB2aWV3Qm94IHNvIHRoYXQgd2UgZG9uJ3QgaGF2ZSB0byBjaGFuZ2UgY29vcmRpbmF0ZXMgb2YgaW5kaXZpZHVhbCBsYXllcnNcblx0XHRzZXRQb3NpdGlvbihjb250YWluZXIsIGIubWluKTtcblx0XHRjb250YWluZXIuc2V0QXR0cmlidXRlKCd2aWV3Qm94JywgW2IubWluLngsIGIubWluLnksIHNpemUueCwgc2l6ZS55XS5qb2luKCcgJykpO1xuXG5cdFx0dGhpcy5maXJlKCd1cGRhdGUnKTtcblx0fSxcblxuXHQvLyBtZXRob2RzIGJlbG93IGFyZSBjYWxsZWQgYnkgdmVjdG9yIGxheWVycyBpbXBsZW1lbnRhdGlvbnNcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBwYXRoID0gbGF5ZXIuX3BhdGggPSBjcmVhdGUkMigncGF0aCcpO1xuXG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9IG51bGxcblx0XHQvLyBDdXN0b20gY2xhc3MgbmFtZSBzZXQgb24gYW4gZWxlbWVudC4gT25seSBmb3IgU1ZHIHJlbmRlcmVyLlxuXHRcdGlmIChsYXllci5vcHRpb25zLmNsYXNzTmFtZSkge1xuXHRcdFx0YWRkQ2xhc3MocGF0aCwgbGF5ZXIub3B0aW9ucy5jbGFzc05hbWUpO1xuXHRcdH1cblxuXHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlKSB7XG5cdFx0XHRhZGRDbGFzcyhwYXRoLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xuXHRcdH1cblxuXHRcdHRoaXMuX3VwZGF0ZVN0eWxlKGxheWVyKTtcblx0XHR0aGlzLl9sYXllcnNbc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRpZiAoIXRoaXMuX3Jvb3RHcm91cCkgeyB0aGlzLl9pbml0Q29udGFpbmVyKCk7IH1cblx0XHR0aGlzLl9yb290R3JvdXAuYXBwZW5kQ2hpbGQobGF5ZXIuX3BhdGgpO1xuXHRcdGxheWVyLmFkZEludGVyYWN0aXZlVGFyZ2V0KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0cmVtb3ZlKGxheWVyLl9wYXRoKTtcblx0XHRsYXllci5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldChsYXllci5fcGF0aCk7XG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tzdGFtcChsYXllcildO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRsYXllci5fcHJvamVjdCgpO1xuXHRcdGxheWVyLl91cGRhdGUoKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBwYXRoID0gbGF5ZXIuX3BhdGgsXG5cdFx0ICAgIG9wdGlvbnMgPSBsYXllci5vcHRpb25zO1xuXG5cdFx0aWYgKCFwYXRoKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgb3B0aW9ucy5jb2xvcik7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLW9wYWNpdHknLCBvcHRpb25zLm9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS13aWR0aCcsIG9wdGlvbnMud2VpZ2h0KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2UtbGluZWNhcCcsIG9wdGlvbnMubGluZUNhcCk7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWxpbmVqb2luJywgb3B0aW9ucy5saW5lSm9pbik7XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hhcnJheScsIG9wdGlvbnMuZGFzaEFycmF5KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaGFycmF5Jyk7XG5cdFx0XHR9XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hPZmZzZXQpIHtcblx0XHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1kYXNob2Zmc2V0Jywgb3B0aW9ucy5kYXNoT2Zmc2V0KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaG9mZnNldCcpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgJ25vbmUnKTtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnZmlsbCcsIG9wdGlvbnMuZmlsbENvbG9yIHx8IG9wdGlvbnMuY29sb3IpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtb3BhY2l0eScsIG9wdGlvbnMuZmlsbE9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtcnVsZScsIG9wdGlvbnMuZmlsbFJ1bGUgfHwgJ2V2ZW5vZGQnKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCAnbm9uZScpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUG9seTogZnVuY3Rpb24gKGxheWVyLCBjbG9zZWQpIHtcblx0XHR0aGlzLl9zZXRQYXRoKGxheWVyLCBwb2ludHNUb1BhdGgobGF5ZXIuX3BhcnRzLCBjbG9zZWQpKTtcblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludCxcblx0XHQgICAgciA9IE1hdGgubWF4KE1hdGgucm91bmQobGF5ZXIuX3JhZGl1cyksIDEpLFxuXHRcdCAgICByMiA9IE1hdGgubWF4KE1hdGgucm91bmQobGF5ZXIuX3JhZGl1c1kpLCAxKSB8fCByLFxuXHRcdCAgICBhcmMgPSAnYScgKyByICsgJywnICsgcjIgKyAnIDAgMSwwICc7XG5cblx0XHQvLyBkcmF3aW5nIGEgY2lyY2xlIHdpdGggdHdvIGhhbGYtYXJjc1xuXHRcdHZhciBkID0gbGF5ZXIuX2VtcHR5KCkgPyAnTTAgMCcgOlxuXHRcdFx0J00nICsgKHAueCAtIHIpICsgJywnICsgcC55ICtcblx0XHRcdGFyYyArIChyICogMikgKyAnLDAgJyArXG5cdFx0XHRhcmMgKyAoLXIgKiAyKSArICcsMCAnO1xuXG5cdFx0dGhpcy5fc2V0UGF0aChsYXllciwgZCk7XG5cdH0sXG5cblx0X3NldFBhdGg6IGZ1bmN0aW9uIChsYXllciwgcGF0aCkge1xuXHRcdGxheWVyLl9wYXR
14775oLnNldEF0dHJpYnV0ZSgnZCcsIHBhdGgpO1xuXHR9LFxuXG5cdC8vIFNWRyBkb2VzIG5vdCBoYXZlIHRoZSBjb25jZXB0IG9mIHpJbmRleCBzbyB3ZSByZXNvcnQgdG8gY2hhbmdpbmcgdGhlIERPTSBvcmRlciBvZiBlbGVtZW50c1xuXHRfYnJpbmdUb0Zyb250OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0b0Zyb250KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRvQmFjayhsYXllci5fcGF0aCk7XG5cdH1cbn0pO1xuXG5pZiAodm1sKSB7XG5cdFNWRy5pbmNsdWRlKHZtbE1peGluKTtcbn1cblxuLy8gQG5hbWVzcGFjZSBTVkdcbi8vIEBmYWN0b3J5IEwuc3ZnKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIFNWRyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuZnVuY3Rpb24gc3ZnJDEob3B0aW9ucykge1xuXHRyZXR1cm4gc3ZnIHx8IHZtbCA/IG5ldyBTVkcob3B0aW9ucykgOiBudWxsO1xufVxuXG5NYXAuaW5jbHVkZSh7XG5cdC8vIEBuYW1lc3BhY2UgTWFwOyBAbWV0aG9kIGdldFJlbmRlcmVyKGxheWVyOiBQYXRoKTogUmVuZGVyZXJcblx0Ly8gUmV0dXJucyB0aGUgaW5zdGFuY2Ugb2YgYFJlbmRlcmVyYCB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIHJlbmRlciB0aGUgZ2l2ZW5cblx0Ly8gYFBhdGhgLiBJdCB3aWxsIGVuc3VyZSB0aGF0IHRoZSBgcmVuZGVyZXJgIG9wdGlvbnMgb2YgdGhlIG1hcCBhbmQgcGF0aHNcblx0Ly8gYXJlIHJlc3BlY3RlZCwgYW5kIHRoYXQgdGhlIHJlbmRlcmVycyBkbyBleGlzdCBvbiB0aGUgbWFwLlxuXHRnZXRSZW5kZXJlcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoOyBAb3B0aW9uIHJlbmRlcmVyOiBSZW5kZXJlclxuXHRcdC8vIFVzZSB0aGlzIHNwZWNpZmljIGluc3RhbmNlIG9mIGBSZW5kZXJlcmAgZm9yIHRoaXMgcGF0aC4gVGFrZXNcblx0XHQvLyBwcmVjZWRlbmNlIG92ZXIgdGhlIG1hcCdzIFtkZWZhdWx0IHJlbmRlcmVyXSgjbWFwLXJlbmRlcmVyKS5cblx0XHR2YXIgcmVuZGVyZXIgPSBsYXllci5vcHRpb25zLnJlbmRlcmVyIHx8IHRoaXMuX2dldFBhbmVSZW5kZXJlcihsYXllci5vcHRpb25zLnBhbmUpIHx8IHRoaXMub3B0aW9ucy5yZW5kZXJlciB8fCB0aGlzLl9yZW5kZXJlcjtcblxuXHRcdGlmICghcmVuZGVyZXIpIHtcblx0XHRcdHJlbmRlcmVyID0gdGhpcy5fcmVuZGVyZXIgPSB0aGlzLl9jcmVhdGVSZW5kZXJlcigpO1xuXHRcdH1cblxuXHRcdGlmICghdGhpcy5oYXNMYXllcihyZW5kZXJlcikpIHtcblx0XHRcdHRoaXMuYWRkTGF5ZXIocmVuZGVyZXIpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVuZGVyZXI7XG5cdH0sXG5cblx0X2dldFBhbmVSZW5kZXJlcjogZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRpZiAobmFtZSA9PT0gJ292ZXJsYXlQYW5lJyB8fCBuYW1lID09PSB1bmRlZmluZWQpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR2YXIgcmVuZGVyZXIgPSB0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdO1xuXHRcdGlmIChyZW5kZXJlciA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRyZW5kZXJlciA9IHRoaXMuX2NyZWF0ZVJlbmRlcmVyKHtwYW5lOiBuYW1lfSk7XG5cdFx0XHR0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdID0gcmVuZGVyZXI7XG5cdFx0fVxuXHRcdHJldHVybiByZW5kZXJlcjtcblx0fSxcblxuXHRfY3JlYXRlUmVuZGVyZXI6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdFx0Ly8gQG5hbWVzcGFjZSBNYXA7IEBvcHRpb24gcHJlZmVyQ2FudmFzOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBXaGV0aGVyIGBQYXRoYHMgc2hvdWxkIGJlIHJlbmRlcmVkIG9uIGEgYENhbnZhc2AgcmVuZGVyZXIuXG5cdFx0Ly8gQnkgZGVmYXVsdCwgYWxsIGBQYXRoYHMgYXJlIHJlbmRlcmVkIGluIGEgYFNWR2AgcmVuZGVyZXIuXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMucHJlZmVyQ2FudmFzICYmIGNhbnZhcyQxKG9wdGlvbnMpKSB8fCBzdmckMShvcHRpb25zKTtcblx0fVxufSk7XG5cbi8qXG4gKiBMLlJlY3RhbmdsZSBleHRlbmRzIFBvbHlnb24gYW5kIGNyZWF0ZXMgYSByZWN0YW5nbGUgd2hlbiBwYXNzZWQgYSBMYXRMbmdCb3VuZHMgb2JqZWN0LlxuICovXG5cbi8qXG4gKiBAY2xhc3MgUmVjdGFuZ2xlXG4gKiBAYWthIEwuUmVjdGFuZ2xlXG4gKiBAaW5oZXJpdHMgUG9seWdvblxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcmVjdGFuZ2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQb2x5Z29uYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBkZWZpbmUgcmVjdGFuZ2xlIGdlb2dyYXBoaWNhbCBib3VuZHNcbiAqIHZhciBib3VuZHMgPSBbWzU0LjU1OTMyMiwgLTUuNzY3ODIyXSwgWzU2LjEyMTA2MDQsIC0zLjAyMTI0MF1dO1xuICpcbiAqIC8vIGNyZWF0ZSBhbiBvcmFuZ2UgcmVjdGFuZ2xlXG4gKiBMLnJlY3RhbmdsZShib3VuZHMsIHtjb2xvcjogXCIjZmY3ODAwXCIsIHdlaWdodDogMX0pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSByZWN0YW5nbGUgYm91bmRzXG4gKiBtYXAuZml0Qm91bmRzKGJvdW5kcyk7XG4gKiBgYGBcbiAqXG4gKi9cblxuXG52YXIgUmVjdGFuZ2xlID0gUG9seWdvbi5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdFx0UG9seWdvbi5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsKHRoaXMsIHR
14775oaXMuX2JvdW5kc1RvTGF0TG5ncyhsYXRMbmdCb3VuZHMpLCBvcHRpb25zKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldEJvdW5kcyhsYXRMbmdCb3VuZHM6IExhdExuZ0JvdW5kcyk6IHRoaXNcblx0Ly8gUmVkcmF3cyB0aGUgcmVjdGFuZ2xlIHdpdGggdGhlIHBhc3NlZCBib3VuZHMuXG5cdHNldEJvdW5kczogZnVuY3Rpb24gKGxhdExuZ0JvdW5kcykge1xuXHRcdHJldHVybiB0aGlzLnNldExhdExuZ3ModGhpcy5fYm91bmRzVG9MYXRMbmdzKGxhdExuZ0JvdW5kcykpO1xuXHR9LFxuXG5cdF9ib3VuZHNUb0xhdExuZ3M6IGZ1bmN0aW9uIChsYXRMbmdCb3VuZHMpIHtcblx0XHRsYXRMbmdCb3VuZHMgPSB0b0xhdExuZ0JvdW5kcyhsYXRMbmdCb3VuZHMpO1xuXHRcdHJldHVybiBbXG5cdFx0XHRsYXRMbmdCb3VuZHMuZ2V0U291dGhXZXN0KCksXG5cdFx0XHRsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhXZXN0KCksXG5cdFx0XHRsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksXG5cdFx0XHRsYXRMbmdCb3VuZHMuZ2V0U291dGhFYXN0KClcblx0XHRdO1xuXHR9XG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLnJlY3RhbmdsZShsYXRMbmdCb3VuZHM6IExhdExuZ0JvdW5kcywgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG5mdW5jdGlvbiByZWN0YW5nbGUobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgUmVjdGFuZ2xlKGxhdExuZ0JvdW5kcywgb3B0aW9ucyk7XG59XG5cblNWRy5jcmVhdGUgPSBjcmVhdGUkMjtcblNWRy5wb2ludHNUb1BhdGggPSBwb2ludHNUb1BhdGg7XG5cbkdlb0pTT04uZ2VvbWV0cnlUb0xheWVyID0gZ2VvbWV0cnlUb0xheWVyO1xuR2VvSlNPTi5jb29yZHNUb0xhdExuZyA9IGNvb3Jkc1RvTGF0TG5nO1xuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MgPSBjb29yZHNUb0xhdExuZ3M7XG5HZW9KU09OLmxhdExuZ1RvQ29vcmRzID0gbGF0TG5nVG9Db29yZHM7XG5HZW9KU09OLmxhdExuZ3NUb0Nvb3JkcyA9IGxhdExuZ3NUb0Nvb3Jkcztcbkdlb0pTT04uZ2V0RmVhdHVyZSA9IGdldEZlYXR1cmU7XG5HZW9KU09OLmFzRmVhdHVyZSA9IGFzRmVhdHVyZTtcblxuLypcbiAqIEwuSGFuZGxlci5Cb3hab29tIGlzIHVzZWQgdG8gYWRkIHNoaWZ0LWRyYWcgem9vbSBpbnRlcmFjdGlvbiB0byB0aGUgbWFwXG4gKiAoem9vbSB0byBhIHNlbGVjdGVkIGJvdW5kaW5nIGJveCksIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBvcHRpb24gYm94Wm9vbTogQm9vbGVhbiA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGNhbiBiZSB6b29tZWQgdG8gYSByZWN0YW5ndWxhciBhcmVhIHNwZWNpZmllZCBieVxuXHQvLyBkcmFnZ2luZyB0aGUgbW91c2Ugd2hpbGUgcHJl
14775c3NpbmcgdGhlIHNoaWZ0IGtleS5cblx0Ym94Wm9vbTogdHJ1ZVxufSk7XG5cbnZhciBCb3hab29tID0gSGFuZGxlci5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG1hcC5fY29udGFpbmVyO1xuXHRcdHRoaXMuX3BhbmUgPSBtYXAuX3BhbmVzLm92ZXJsYXlQYW5lO1xuXHRcdHRoaXMuX3Jlc2V0U3RhdGVUaW1lb3V0ID0gMDtcblx0XHRtYXAub24oJ3VubG9hZCcsIHRoaXMuX2Rlc3Ryb3ksIHRoaXMpO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0b24odGhpcy5fY29udGFpbmVyLCAnbW91c2Vkb3duJywgdGhpcy5fb25Nb3VzZURvd24sIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0b2ZmKHRoaXMuX2NvbnRhaW5lciwgJ21vdXNlZG93bicsIHRoaXMuX29uTW91c2VEb3duLCB0aGlzKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tb3ZlZDtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24gKCkge1xuXHRcdHJlbW92ZSh0aGlzLl9wYW5lKTtcblx0XHRkZWxldGUgdGhpcy5fcGFuZTtcblx0fSxcblxuXHRfcmVzZXRTdGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3Jlc2V0U3RhdGVUaW1lb3V0ID0gMDtcblx0XHR0aGlzLl9tb3ZlZCA9IGZhbHNlO1xuXHR9LFxuXG5cdF9jbGVhckRlZmVycmVkUmVzZXRTdGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9yZXNldFN0YXRlVGltZW91dCAhPT0gMCkge1xuXHRcdFx0Y2xlYXJUaW1lb3V0KHRoaXMuX3Jlc2V0U3RhdGVUaW1lb3V0KTtcblx0XHRcdHRoaXMuX3Jlc2V0U3RhdGVUaW1lb3V0ID0gMDtcblx0XHR9XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1
14775dHRvbiAhPT0gMSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXG5cdFx0Ly8gQ2xlYXIgdGhlIGRlZmVycmVkIHJlc2V0U3RhdGUgaWYgaXQgaGFzbid0IGV4ZWN1dGVkIHlldCwgb3RoZXJ3aXNlIGl0XG5cdFx0Ly8gd2lsbCBpbnRlcnJ1cHQgdGhlIGludGVyYWN0aW9uIGFuZCBvcnBoYW4gYSBib3ggZWxlbWVudCBpbiB0aGUgY29udGFpbmVyLlxuXHRcdHRoaXMuX2NsZWFyRGVmZXJyZWRSZXNldFN0YXRlKCk7XG5cdFx0dGhpcy5fcmVzZXRTdGF0ZSgpO1xuXG5cdFx0ZGlzYWJsZVRleHRTZWxlY3Rpb24oKTtcblx0XHRkaXNhYmxlSW1hZ2VEcmFnKCk7XG5cblx0XHR0aGlzLl9zdGFydFBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0b24oZG9jdW1lbnQsIHtcblx0XHRcdGNvbnRleHRtZW51OiBzdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7XG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XG5cblx0XHRcdHRoaXMuX2JveCA9IGNyZWF0ZSQxKCdkaXYnLCAnbGVhZmxldC16b29tLWJveCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRhZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNyb3NzaGFpcicpO1xuXG5cdFx0XHR0aGlzLl9tYXAuZmlyZSgnYm94em9vbXN0YXJ0Jyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fcG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZSk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IEJvdW5kcyh0aGlzLl9wb2ludCwgdGhpcy5fc3RhcnRQb2ludCksXG5cdFx0ICAgIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuXG5cdFx0c2V0UG9zaXRpb24odGhpcy5fYm94LCBib3VuZHMubWluKTtcblxuXHRcdHRoaXMuX2JveC5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xuXHRcdHRoaXMuX2JveC5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXHR9LFxuXG5cdF9maW5pc2g6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbW92ZWQpIHtcblx0XHRcdHJlbW92ZSh0aGlzLl9ib3gpO1xuXHRcdFx0cmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1jcm9zc2hhaXInKTtcblx0XHR9XG5cblx0XHRlbmFibGVUZXh0U2VsZWN0aW9uKCk7XG5cdFx0ZW5hYmxlSW1hZ2VEcmFnKCk7XG5cblx0XHRvZmYoZG9jdW1lbnQsIHtcblx0XHRcdGNvbnRleHRtZW51OiBzdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlVXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fZmluaXNoKCk7XG5cblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7IHJldHVybjsgfVxuXHRcdC8vIFBvc3Rwb25lIHRvIG5leHQgSlMgdGljayBzbyBpbnRlcm5hbCBjbGljayBldmVudCBoYW5kbGluZ1xuXHRcdC8vIHN0aWxsIHNlZSBpdCBhcyBcIm1vdmVkXCIuXG5cdFx0dGhpcy5fY2xlYXJEZWZlcnJlZFJlc2V0U3RhdGUoKTtcblx0XHR0aGlzLl9yZXNldFN0YXRlVGltZW91dCA9IHNldFRpbWVvdXQoYmluZCh0aGlzLl9yZXNldFN0YXRlLCB0aGlzKSwgMCk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IExhdExuZ0JvdW5kcyhcblx0XHQgICAgICAgIHRoaXMuX21hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHRoaXMuX3N0YXJ0UG9pbnQpLFxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQpKTtcblxuXHRcdHRoaXMuX21hcFxuXHRcdFx0LmZpdEJvdW5kcyhib3VuZHMpXG5cdFx0XHQuZmlyZSgnYm94em9vbWVuZCcsIHtib3hab29tQm91bmRzOiBib3VuZHN9KTtcblx0fSxcblxuXHRfb25LZXlEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmIChlLmtleUNvZGUgPT09IDI3KSB7XG5cdFx0XHR0aGlzLl9maW5pc2goKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGJveFpvb206IEhhbmRsZXJcbi8vIEJveCAoc2hpZnQtZHJhZyB3aXRoIG1vdXNlKSB6b29tIGhhbmRsZXIuXG5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnYm94Wm9vbScsIEJveFpvb20pO1xuXG4vKlxuICogTC5IYW5kbGVyLkRvdWJsZUNsaWNrWm9vbSBpcyB1c2VkIHRvIGhhbmRsZSBkb3VibGUtY2xpY2sgem9vbSBvbiB0aGUgbWFwLCBlbmFibGVkIGJ5IGRlZmF1bHQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcblxuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBvcHRpb24gZG91YmxlQ2xpY2tab29tOiBCb29sZWFufFN0cmluZyA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGNhbiBiZSB6b29tZWQgaW4gYnkgZG91YmxlIGNsaWNraW5nIG9uIGl0IGFuZFxuXHQvLyB6b29tZWQgb3V0IGJ5IGRvdWJsZSBjbGlja2luZyB3aGlsZSBob2xkaW5nIHNoaWZ0LiBJZiBwYXNzZWRcblx0Ly8gYCdjZW50ZXInYCwgZG91YmxlLWNsaWNrIHpvb20gd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlXG5cdC8vICB2aWV3IHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgdGhlIG1vdXNlIHdhcy5cblx0ZG91YmxlQ2xpY2tab29tOiB0cnVlXG59KTtcblxudmFyIERvdWJsZUNsaWNrWm9vbSA9IEhhbmRsZXIuZXh0ZW5kKHtcblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tYXAub24oJ2RibGNsaWNrJywgdGhpcy5fb25Eb3VibGVDbGljaywgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tYXAub2ZmKCdkYmxjbGljaycsIHRoaXMuX29uRG91YmxlQ2xpY2ssIHRoaXMpO1xuXHR9LFxuXG5cdF9vbkRvdWJsZUNsaWNrOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIG9sZFpvb20gPSBtYXAuZ2V0Wm9vbSgpLFxuXHRcdCAgICBkZWx0YSA9IG1hcC5vcHRpb25zLnpvb21EZWx0YSxcblx0XHQgICAgem9vbSA9IGUub3JpZ2luYWxFdmVudC5zaGlmdEtleSA/IG9sZFpvb20gLSBkZWx0YSA6IG9sZFpvb20gKyBkZWx0YTtcblxuXHRcdGlmIChtYXAub3B0aW9ucy5kb3VibGVDbGlja1pvb20gPT09ICdjZW50ZXInKSB7XG5cdFx0XHRtYXAuc2V0Wm9vbSh6b29tKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bWFwLnNldFpvb21Bcm91bmQoZS5jb250YWluZXJQb2ludCwgem9vbSk7XG5cdFx0fVxuXHR9XG59KTtcblxuLy8gQHNlY3Rpb24gSGFuZGxlcnNcbi8vXG4vLyBNYXAgcHJvcGVydGllcyBpbmNsdWRlIGludGVyYWN0aW9uIGhhbmRsZXJzIHRoYXQgYWxsb3cgeW91IHRvIGNvbnRyb2xcbi8vIGludGVyYWN0aW9uIGJlaGF2aW9yIGluIHJ1bnRpbWUsIGVuYWJsaW5nIG9yIGRpc2FibGluZyBjZXJ0YWluIGZlYXR1cmVzIHN1Y2hcbi8vIGFzIGRyYWdnaW5nIG9yIHRvdWNoIHpvb20gKHNlZSBgSGFuZGxlcmAgbWV0aG9kcykuIEZvciBleGFtcGxlOlxuLy9cbi8vIGBgYGpzXG4vLyBtYXAuZG91YmxlQ2xpY2tab29tLmRpc2FibGUoKTtcbi8vIGBgYFxuLy9cbi8vIEBwcm9wZXJ0eSBkb3VibGVDbGlja1pvb206IEhhbmRsZXJcbi8vIERvdWJsZSBjbGljayB6b29tIGhhbmRsZXIuXG5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnZG91YmxlQ2xpY2tab29tJywgRG91YmxlQ2xpY2tab29tKTtcblxuLypcbiAqIEwuSGFuZGxlci5NYXBEcmFnIGlzIHVzZWQgdG8gbWFrZSB0aGUgbWFwIGRyYWdnYWJsZSAod2l0aCBwYW5uaW5nIGluZXJ0aWEpLCBlbmFibGVkIGJ5IGRlZmF1bHQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGRyYWdnaW5nOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHR
14775oZSBtYXAgYmUgZHJhZ2dhYmxlIHdpdGggbW91c2UvdG91Y2ggb3Igbm90LlxuXHRkcmFnZ2luZzogdHJ1ZSxcblxuXHQvLyBAc2VjdGlvbiBQYW5uaW5nIEluZXJ0aWEgT3B0aW9uc1xuXHQvLyBAb3B0aW9uIGluZXJ0aWE6IEJvb2xlYW4gPSAqXG5cdC8vIElmIGVuYWJsZWQsIHBhbm5pbmcgb2YgdGhlIG1hcCB3aWxsIGhhdmUgYW4gaW5lcnRpYSBlZmZlY3Qgd2hlcmVcblx0Ly8gdGhlIG1hcCBidWlsZHMgbW9tZW50dW0gd2hpbGUgZHJhZ2dpbmcgYW5kIGNvbnRpbnVlcyBtb3ZpbmcgaW5cblx0Ly8gdGhlIHNhbWUgZGlyZWN0aW9uIGZvciBzb21lIHRpbWUuIEZlZWxzIGVzcGVjaWFsbHkgbmljZSBvbiB0b3VjaFxuXHQvLyBkZXZpY2VzLiBFbmFibGVkIGJ5IGRlZmF1bHQgdW5sZXNzIHJ1bm5pbmcgb24gb2xkIEFuZHJvaWQgZGV2aWNlcy5cblx0aW5lcnRpYTogIWFuZHJvaWQyMyxcblxuXHQvLyBAb3B0aW9uIGluZXJ0aWFEZWNlbGVyYXRpb246IE51bWJlciA9IDMwMDBcblx0Ly8gVGhlIHJhdGUgd2l0aCB3aGljaCB0aGUgaW5lcnRpYWwgbW92ZW1lbnQgc2xvd3MgZG93biwgaW4gcGl4ZWxzL3NlY29uZMKyLlxuXHRpbmVydGlhRGVjZWxlcmF0aW9uOiAzNDAwLCAvLyBweC9zXjJcblxuXHQvLyBAb3B0aW9uIGluZXJ0aWFNYXhTcGVlZDogTnVtYmVyID0gSW5maW5pdHlcblx0Ly8gTWF4IHNwZWVkIG9mIHRoZSBpbmVydGlhbCBtb3ZlbWVudCwgaW4gcGl4ZWxzL3NlY29uZC5cblx0aW5lcnRpYU1heFNwZWVkOiBJbmZpbml0eSwgLy8gcHgvc1xuXG5cdC8vIEBvcHRpb24gZWFzZUxpbmVhcml0eTogTnVtYmVyID0gMC4yXG5cdGVhc2VMaW5lYXJpdHk6IDAuMixcblxuXHQvLyBUT0RPIHJlZmFjdG9yLCBtb3ZlIHRvIENSU1xuXHQvLyBAb3B0aW9uIHdvcmxkQ29weUp1bXA6IEJvb2xlYW4gPSBmYWxzZVxuXHQvLyBXaXRoIHRoaXMgb3B0aW9uIGVuYWJsZWQsIHRoZSBtYXAgdHJhY2tzIHdoZW4geW91IHBhbiB0byBhbm90aGVyIFwiY29weVwiXG5cdC8vIG9mIHRoZSB3b3JsZCBhbmQgc2VhbWxlc3NseSBqdW1wcyB0byB0aGUgb3JpZ2luYWwgb25lIHNvIHRoYXQgYWxsIG92ZXJsYXlzXG5cdC8vIGxpa2UgbWFya2VycyBhbmQgdmVjdG9yIGxheWVycyBhcmUgc3RpbGwgdmlzaWJsZS5cblx0d29ybGRDb3B5SnVtcDogZmFsc2UsXG5cblx0Ly8gQG9wdGlvbiBtYXhCb3VuZHNWaXNjb3NpdHk6IE51bWJlciA9IDAuMFxuXHQvLyBJZiBgbWF4Qm91bmRzYCBpcyBzZXQsIHRoaXMgb3B0aW9uIHdpbGwgY29udHJvbCBob3cgc29saWQgdGhlIGJvdW5kc1xuXHQvLyBhcmUgd2hlbiBkcmFnZ2luZyB0aGUgbWFwIGFyb3VuZC4gVGhlIGRlZmF1bHQgdmFsdWUgb2YgYDAuMGAgYWxsb3dzIHRoZVxuXHQvLyB1c2VyIHRvIGRyYWcgb3V0c2lkZSB0aGUgYm91bmRzIGF0IG5vcm1hbCBzcGVlZCwgaGlnaGVyIHZhbHVlcyB3aWxsXG5cdC8vIHNsb3cgZG93biBtYXAgZHJhZ2dpbmcgb3V0c2lkZSBib3VuZHMsIGFuZCBgMS4wYCBtYWtlcyB0aGUgYm91bmRzIGZ1bGx5XG5cdC8vIHNvbGlkLCBwcmV2ZW50aW5nIHRoZSB1c2VyIGZyb20gZHJhZ2dpbmcgb3V0c2lkZSB0aGUgYm91bmRzLlxuXHRtYXhCb3VuZHNWaXNjb3NpdHk6IDAuMFxufSk7XG5cbnZhciBEcmFnID0gSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fZHJhZ2dhYmxlKSB7XG5cdFx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUgPSBuZXcgRHJhZ2dhYmxlKG1hcC5fbWFwUGFuZSwgbWFwLl9jb250YWluZXIpO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUub24oe1xuXHRcdFx0XHRkcmFnc3RhcnQ6IHRoaXMuX29uRHJhZ1N0YXJ0LFxuXHRcdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdFx0fSwgdGhpcyk7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ0xpbWl0LCB0aGlzKTtcblx0XHRcdGlmIChtYXAub3B0aW9ucy53b3JsZENvcHlKdW1wKSB7XG5cdFx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ1dyYXAsIHRoaXMpO1xuXHRcdFx0XHRtYXAub24oJ3pvb21lbmQnLCB0aGlzLl9vblpvb21FbmQsIHRoaXMpO1xuXG5cdFx0XHRcdG1hcC53aGVuUmVhZHkodGhpcy5fb25ab29tRW5kLCB0aGlzKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0YWRkQ2xhc3ModGhpcy5fbWFwLl9jb250YWluZXIsICdsZWFmbGV0LWdyYWIgbGVhZmxldC10b3VjaC1kcmFnJyk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLmVuYWJsZSgpO1xuXHRcdHRoaXMuX3Bvc2l0aW9ucyA9IFtdO1xuXHRcdHRoaXMuX3RpbWVzID0gW107XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRyZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtZ3JhYicpO1xuXHRcdHJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC1kcmFnJyk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLmRpc2FibGUoKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9kcmFnZ2FibGUgJiYgdGhpcy5fZHJhZ2dhYmxlLl9tb3ZlZDtcblx0fSxcblxuXHRtb3Zpbmc6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92aW5nO1xuXHR9LFxuXG5cdF9vbkRyYWdTdGFydDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRtYXAuX3N0b3AoKTtcblx0XHRpZiAodGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzICYmIHRoaXMuX21hcC5vcHRpb25zLm1heEJvdW5kc1Zpc2Nvc2l0eSkge1xuXHRcdFx0dmFyIGJvdW5kcyA9IHRvTGF0TG5nQm91bmRzKHRoaXMuX21hcC5vcHRpb25zLm1heEJvdW5kcyk7XG5cblx0XHRcdHRoaXMuX29mZnNldExpbWl0ID0gdG9Cb3VuZHMoXG5cdFx0XHRcdHRoaXMuX21hcC5sYXRMbmdUb0NvbnRhaW5lclBvaW50KGJvdW5kcy5nZXROb3J0aFdlc3QoKSkubXVsdGlwbHlCeSgtMSksXG5cdFx0XHRcdHRoaXMuX21hcC5sYXRMbmdUb0NvbnRhaW5lclBvaW50KGJvdW5kcy5nZXRTb3V0aEVhc3QoKSkubXVsdGlwbHlCeSgtMSlcblx0XHRcdFx0XHQuYWRkKHRoaXMuX21hcC5nZXRTaXplKCkpKTtcblxuXHRcdFx0dGhpcy5fdmlzY29zaXR5ID0gTWF0aC5taW4oMS4wLCBNYXRoLm1heCgwLjAsIHRoaXMuX21hcC5vcHRpb25zLm1heEJvdW5kc1Zpc2Nvc2l0eSkpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9vZmZzZXRMaW1pdCA9IG51bGw7XG5cdFx0fVxuXG5cdFx0bWFwXG5cdFx0ICAgIC5maXJlKCdtb3Zlc3RhcnQnKVxuXHRcdCAgICAuZmlyZSgnZHJhZ3N0YXJ0Jyk7XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuaW5lcnRpYSkge1xuXHRcdFx0dGhpcy5fcG9zaXRpb25zID0gW107XG5cdFx0XHR0aGlzLl90
14775aW1lcyA9IFtdO1xuXHRcdH1cblx0fSxcblxuXHRfb25EcmFnOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICh0aGlzLl9tYXAub3B0aW9ucy5pbmVydGlhKSB7XG5cdFx0XHR2YXIgdGltZSA9IHRoaXMuX2xhc3RUaW1lID0gK25ldyBEYXRlKCksXG5cdFx0XHQgICAgcG9zID0gdGhpcy5fbGFzdFBvcyA9IHRoaXMuX2RyYWdnYWJsZS5fYWJzUG9zIHx8IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zO1xuXG5cdFx0XHR0aGlzLl9wb3NpdGlvbnMucHVzaChwb3MpO1xuXHRcdFx0dGhpcy5fdGltZXMucHVzaCh0aW1lKTtcblxuXHRcdFx0dGhpcy5fcHJ1bmVQb3NpdGlvbnModGltZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fbWFwXG5cdFx0ICAgIC5maXJlKCdtb3ZlJywgZSlcblx0XHQgICAgLmZpcmUoJ2RyYWcnLCBlKTtcblx0fSxcblxuXHRfcHJ1bmVQb3NpdGlvbnM6IGZ1bmN0aW9uICh0aW1lKSB7XG5cdFx0d2hpbGUgKHRoaXMuX3Bvc2l0aW9ucy5sZW5ndGggPiAxICYmIHRpbWUgLSB0aGlzLl90aW1lc1swXSA+IDUwKSB7XG5cdFx0XHR0aGlzLl9wb3NpdGlvbnMuc2hpZnQoKTtcblx0XHRcdHRoaXMuX3RpbWVzLnNoaWZ0KCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9vblpvb21FbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHhDZW50ZXIgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KDIpLFxuXHRcdCAgICBweFdvcmxkQ2VudGVyID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChbMCwgMF0pO1xuXG5cdFx0dGhpcy5faW5pdGlhbFdvcmxkT2Zmc2V0ID0gcHhXb3JsZENlbnRlci5zdWJ0cmFjdChweENlbnRlcikueDtcblx0XHR0aGlzLl93b3JsZFdpZHRoID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHMoKS5nZXRTaXplKCkueDtcblx0fSxcblxuXHRfdmlzY291c0xpbWl0OiBmdW5jdGlvbiAodmFsdWUsIHRocmVzaG9sZCkge1xuXHRcdHJldHVybiB2YWx1ZSAtICh2YWx1ZSAtIHRocmVzaG9sZCkgKiB0aGlzLl92aXNjb3NpdHk7XG5cdH0sXG5cblx0X29uUHJlRHJhZ0xpbWl0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl92aXNjb3NpdHkgfHwgIXRoaXMuX29mZnNldExpbWl0KSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLnN1YnRyYWN0KHRoaXMuX2RyYWdnYWJsZS5fc3RhcnRQb3MpO1xuXG5cdFx0dmFyIGxpbWl0ID0gdGhpcy5fb2Zmc2V0TGltaXQ7XG5cdFx0aWYgKG9mZnNldC54IDwgbGltaXQubWluLngpIHsgb2Zmc2V0LnggPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LngsIGxpbWl0Lm1pbi54KTsgfVxuXHRcdGlmIChvZmZzZXQueSA8IGxpbWl0Lm1pbi55KSB7IG9mZnNldC55ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC55LCBsaW1pdC5taW4ueSk7IH1cblx0XHRpZiAob2Zmc2V0LnggPiBsaW1pdC5tYXgueCkgeyBvZmZzZXQueCA9IHRoaXMuX3Zpc2NvdXNMaW1pdChvZmZzZXQueCwgbGltaXQubWF4LngpOyB9XG5cdFx0aWYgKG9mZnNldC55ID4gbGltaXQubWF4LnkpIHsgb2Zmc2V0LnkgPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LnksIGxpbWl0Lm1heC55KTsgfVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xuXHR9LFxuXG5cdF9vblByZURyYWdXcmFwOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gVE9ETyByZWZhY3RvciB0byBiZSBhYmxlIHRvIGFkanVzdCBtYXAgcGFuZSBwb3NpdGlvbiBhZnRlciB6b29tXG5cdFx0dmFyIHdvcmxkV2lkdGggPSB0aGlzLl93b3JsZFdpZHRoLFxuXHRcdCAgICBoYWxmV2lkdGggPSBNYXRoLnJvdW5kKHdvcmxkV2lkdGggLyAyKSxcblx0XHQgICAgZHggPSB0aGlzLl9pbml0aWFsV29ybGRPZmZzZXQsXG5cdFx0ICAgIHggPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy54LFxuXHRcdCAgICBuZXdYMSA9ICh4IC0gaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCArIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYMiA9ICh4ICsgaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCAtIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYID0gTWF0aC5hYnMobmV3WDEgKyBkeCkgPCBNYXRoLmFicyhuZXdYMiArIGR4KSA/IG5ld1gxIDogbmV3WDI7XG5cblx0XHR0aGlzLl9kcmFnZ2FibGUuX2Fic1BvcyA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLmNsb25lKCk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MueCA9IG5ld1g7XG5cdH0sXG5cblx0X29uRHJhZ0VuZDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvcHRpb25zID0gbWFwLm9wdGlvbnMsXG5cblx0XHQgICAgbm9JbmVydGlhID0gIW9wdGlvbnMuaW5lcnRpYSB8fCB0aGlzLl90aW1lcy5sZW5ndGggPCAyO1xuXG5cdFx0bWFwLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblxuXHRcdGlmIChub0luZXJ0aWEpIHtcblx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fcHJ1bmVQb3NpdGlvbnMoK25ldyBEYXRlKCkpO1xuXG5cdFx0XHR2YXIgZGlyZWN0aW9uID0gdGhpcy5fbGFzdFBvcy5zdWJ0cmFjdCh0aGlzLl9wb3NpdGlvbnNbMF0pLFxuXHRcdFx0ICAgIGR1cmF0aW9uID0gKHRoaXMuX2xhc3RUaW1lIC0gdGhpcy5fdGltZXNbMF0pIC8gMTAwMCxcblx0XHRcdCAgICBlYXNlID0gb3B0aW9ucy5lYXNlTGluZWFyaXR5LFxuXG5cdFx0XHQgICAgc3BlZWRWZWN0b3IgPSBkaXJlY3Rpb24ubXVsdGlwbHlCeShlYXNlIC8gZHVyYXRpb24pLFxuXHRcdFx0ICAgIHNwZWVkID0gc3BlZWRWZWN0b3IuZGlzdGFuY2VUbyhbMCwgMF0pLFxuXG5cdFx0XHQgICAgbGltaXRlZFNwZWVkID0gTWF0aC5taW4ob3B0aW9ucy5pbmVydGlhTWF4U3BlZWQsIHNwZWVkKSxcblx0XHRcdCAgICBsaW1pdGVkU3BlZWRWZWN0b3IgPSBzcGVlZFZlY3Rvci5tdWx0aXBseUJ5KGxpbWl0ZWRTcGVlZCAvIHNwZWVkKSxcblxuXHRcdFx0ICAgIGRlY2VsZXJhdGlvbkR1cmF0aW9uID0gbGltaXRlZFNwZWVkIC8gKG9wdGlvbnMuaW5lcnRpYURlY2VsZXJhdGlvbiAqIGVhc2UpLFxuXHRcdFx0ICAgIG9mZnNldCA9IGxpbWl0ZWRTcGVlZFZlY3Rvci5tdWx0aXBseUJ5KC1kZWNlbGVyYXRpb25EdXJhdGlvbiAvIDIpLnJvdW5kKCk7XG5cblx0XHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7XG5cdFx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdG9mZnNldCA9IG1hcC5fbGltaXRPZmZzZXQob2Zmc2V0LCBtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXG5cdFx0XHRcdHJlcXVlc3RBbmltRnJhbWUoZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRcdG1hcC5wYW5CeShvZmZzZXQsIHtcblx0XHRcdFx0XHRcdGR1cmF0aW9uOiBkZWNlbGVyYXRpb25EdXJhdGlvbixcblx0XHRcdFx0XHRcdGVhc2VMaW5lYXJpdHk6IGVhc2UsXG5cdFx0XHRcdFx0XHRub01vdmVTdGFydDogdHJ1ZSxcblx0XHRcdFx0XHRcdGFuaW1hdGU6IHRydWVcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG59KTtcblxuLy8gQHNlY3Rpb24gSGFuZGxlcnNcbi8vIEBwcm9wZXJ0eSBkcmFnZ2luZzogSGFuZGxlclxuLy8gTWFwIGRyYWdnaW5nIGhhbmRsZXIgKGJ5IGJvdGggbW91c2UgYW5kIHRvdWNoKS5cbk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdkcmFnZ2luZycsIERyYWcpO1xuXG4vKlxuICogTC5NYXAuS2V5Ym9hcmQgaXMgaGFuZGxpbmcga2V5Ym9hcmQgaW50ZXJhY3Rpb24gd2l0aCB0aGUgbWFwLCBlbmFibGVkIGJ5IGRlZmF1bHQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEtleWJvYXJkIE5hdmlnYXRpb24gT3B0aW9uc1xuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBvcHRpb24ga2V5Ym9hcmQ6IEJvb2xlYW4gPSB0cnVlXG5cdC8vIE1ha2VzIHRoZSBtYXAgZm9jdXNhYmxlIGFuZCBhbGxvd3MgdXNlcnMgdG8gbmF2aWdhdGUgdGhlIG1hcCB3aXRoIGtleWJvYXJkXG5cdC8vIGFycm93cyBhbmQgYCtgL2AtYCBrZXlzLlxuXHRrZXlib2FyZDogdHJ1ZSxcblxuXHQvLyBAb3B0aW9uIGtleWJvYXJkUGFuRGVsdGE6IE51bWJlciA9IDgwXG5cdC8vIEFtb3VudCBvZiBwaXhlbHMgdG8gcGFuIHdoZW4gcHJlc3NpbmcgYW4gYXJyb3cga2V5LlxuXHRrZXlib2FyZFBhbkRlbHRhOiA4MFxufSk7XG5cbnZhciBLZXlib2FyZCA9IEhhbmRsZXIuZXh0ZW5kKHtcblxuXHRrZXlDb2Rlczoge1xuXHRcdGxlZnQ6ICAgIFszN10sXG5cdFx0cmlnaHQ6ICAgWzM5XSxcblx0XHRkb3duOiAgICBbNDBdLFxuXHRcdHVwOiAgICAgIFszOF0sXG5cdFx0em9vbUluOiAgWzE4NywgMTA3LCA2MSwgMTcxXSxcblx0XHR6b29tT3V0OiBbMTg5LCAxMDksIDU0LCAxNzNdXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX21hcCA9IG1hcDtcblxuXHRcdHRoaXMuX3NldFBhbkRlbHRhKG1hcC5vcHRpb25zLmtleWJvYXJkUGFuRGVsdGEpO1xuXHRcdHRoaXMuX3NldFpvb21EZWx0YShtYXAub3B0aW9ucy56b29tRGVsdGEpO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX21hcC5fY29udGFpbmVyO1xuXG5cdFx0Ly8gbWFrZSB0aGUgY29udGFpbmVyIGZvY3VzYWJsZSBieSB0YWJiaW5nXG5cdFx0aWYgKGNvbnRhaW5lci50YWJJbmRleCA8PSAwKSB7XG5cdFx0XHRjb250YWluZXIudGFiSW5kZXggPSAnMCc7XG5cdFx0fVxuXG5cdFx0b24oY29udGFpbmVyLCB7XG5cdFx0XHRmb2N1czogdGhpcy5fb25Gb2N1cyxcblx0XHRcdGJsdXI6IHRoaXMuX29uQmx1cixcblx0XHRcdG1vdXNlZG93bjogdGhpcy5fb25Nb3VzZURvd25cblx0XHR9LCB0aGlzKTtcblxuXHRcdHRoaXMuX21hcC5vbih7XG5cdFx0XHRmb2N1czogdGhpcy5fYWRkSG9va3MsXG5cdFx0XHRibHVyOiB0aGlzLl9yZW1vdmVIb29rc1xuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVtb3ZlSG9va3MoKTtcblxuXHRcdG9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwge1xuXHRcdFx0Zm9jdXM6IHRoaXMuX29uRm9jdXMsXG5cdFx0XHRibHVyOiB0aGlzLl9vbkJsdXIsXG5cdFx0XHRtb3VzZWRvd246IHRoaXMuX29uTW91c2VEb3duXG5cdFx0fSwgdGhpcyk7XG5cblx0XHR0aGlzLl9tYXAub2ZmKHtcblx0XHRcdGZvY3VzOiB0aGlzLl9hZGRIb29rcyxcblx0XHRcdGJsdXI6IHRoaXMuX3JlbW92ZUhvb2tzXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ZvY3VzZWQpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgYm9keSA9IGRvY3VtZW50LmJvZHksXG5cdFx0ICAgIGRvY0VsID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LFxuXHRcdCAgICB0b3AgPSBib2R5LnNjcm9sbFRvcCB8fCBkb2NFbC5zY3JvbGxUb3AsXG5cdFx0ICAgIGxlZnQgPSBib2R5LnNjcm9sbExlZnQgfHwgZG9jRWwuc2Nyb2xsTGVmdDtcblxuXHRcdHRoaXMuX21hcC5fY29udGFpbmVyLmZvY3VzKCk7XG5cblx0XHR3aW5kb3cuc2Nyb2xsVG8obGVmdCwgdG9wKTtcblx0fSxcblxuXHRfb25Gb2N1czogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2ZvY3VzZWQgPSB0cnVlO1xuXHRcdHRoaXMuX21hcC5maXJlKCdmb2N1cycpO1xuXHR9LFxuXG5cdF9vbkJsdXI6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9mb2N1c2VkID0gZmFsc2U7XG5cdFx0dGhpcy5fbWFwLmZpcmUoJ2JsdXInKTtcblx0fSxcblxuXHRfc2V0UGFuRGVsdGE6IGZ1bmN0aW9uIChwYW5EZWx0YSkge1xuXHRcdHZhciBrZXlzID0gdGhpcy5fcGFuS2V5cyA9IHt9LFxuXHRcdCAgICBjb2RlcyA9IHRoaXMua2V5Q29kZXMsXG5cdFx0ICAgIGksIGxlbjtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmxlZnQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMubGVmdFtpXV0gPSBbLTEgKiBwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnJpZ2h0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnJpZ2h0W2ldXSA9IFtwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmRvd24ubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuZG93bltpXV0gPSBbMCwgcGFuRGVsdGFdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy51cC5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy51cFtpXV0gPSBbMCwgLTEgKiBwYW5EZWx0YV07XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tRGVsdGE6IGZ1bmN0aW9uICh6b29tRGVsdGEpIHtcblx0XHR2YXIga2V5cyA9IHRoaXMuX3pvb21LZXlzID0ge30sXG5cdFx0ICAgIGNvZGVzID0gdGhpcy5rZXlDb2Rlcyxcblx0XHQgICAgaSwgbGVuO1xuXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gY29kZXMuem9vbUluLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnpvb21JbltpXV0gPSB6b29tRGVsdGE7XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnpvb21PdXQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuem9vbU91dFtpXV0gPSAtem9vbURlbHRhO1xuXHRcdH1cblx0fSxcblxuXHRfYWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRvbihkb2N1bWVudCwgJ2tleWRvd24nLCB0aGlzLl9vbktleURvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9yZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdG9mZihkb2N1bWVudCwgJ2tleWRvd24nLCB0aGlzLl9vbktleURvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbktleURvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKGUuYWx0S2V5IHx8IGUuY3RybEtleSB8fCBlLm1ldGFLZXkpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIga2V5ID0gZS5rZXlDb2RlLFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIG9mZnNldDtcblxuXHRcdGlmIChrZXkgaW4gdGhpcy5fcGFuS2V5cykge1xuXHRcdFx0aWYgKCFtYXAuX3BhbkFuaW0gfHwgIW1hcC5fcGFuQW5pbS5faW5Qcm9ncmVzcykge1xuXHRcdFx0XHRvZmZzZXQgPSB0aGlzLl9wYW5LZXlzW2tleV07XG5cdFx0XHRcdGlmIChlLnNoaWZ0S2V5KSB7XG5cdFx0XHRcdFx0b2Zmc2V0ID0gdG9Qb2ludChvZmZzZXQpLm11bHRpcGx5QnkoMyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRtYXAucGFuQnkob2Zmc2V0KTtcblxuXHRcdFx0XHRpZiAobWFwLm9wdGlvbnMubWF4Qm91bmRzKSB7XG5cdFx0XHRcdFx0bWFwLnBhbkluc2lkZUJvdW5kcyhtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSBlbHNlIGlmIChrZXkgaW4gdGhpcy5fem9vbUtleXMpIHtcblx0XHRcdG1hcC5zZXRab29tKG1hcC5nZXRab29tKCkgKyAoZS5zaGlmdEtleSA/IDMgOiAxKSAqIHRoaXMuX3pvb21LZXlzW2tleV0pO1xuXG5cdFx0fSBlbHNlIGlmIChrZXkgPT09IDI3ICYmIG1hcC5fcG9wdXAgJiYgbWFwLl9wb3B1cC5vcHRpb25zLmNsb3NlT25Fc2NhcGVLZXkpIHtcblx0XHRcdG1hcC5jbG9zZVBvcHVwKCk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHN0b3AoZSk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHNlY3Rpb24gSGFuZGxlcnNcbi8vIEBwcm9wZXJ0eSBrZXlib2FyZDogSGFuZGxlclxuLy8gS2V5Ym9hcmQgbmF2aWdhdGlvbiB
14775oYW5kbGVyLlxuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ2tleWJvYXJkJywgS2V5Ym9hcmQpO1xuXG4vKlxuICogTC5IYW5kbGVyLlNjcm9sbFdoZWVsWm9vbSBpcyB1c2VkIGJ5IEwuTWFwIHRvIGVuYWJsZSBtb3VzZSBzY3JvbGwgd2hlZWwgem9vbSBvbiB0aGUgbWFwLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQHNlY3Rpb24gTW91c2V3aGVlbCBvcHRpb25zXG5cdC8vIEBvcHRpb24gc2Nyb2xsV2hlZWxab29tOiBCb29sZWFufFN0cmluZyA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGNhbiBiZSB6b29tZWQgYnkgdXNpbmcgdGhlIG1vdXNlIHdoZWVsLiBJZiBwYXNzZWQgYCdjZW50ZXInYCxcblx0Ly8gaXQgd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlIHZpZXcgcmVnYXJkbGVzcyBvZiB3aGVyZSB0aGUgbW91c2Ugd2FzLlxuXHRzY3JvbGxXaGVlbFpvb206IHRydWUsXG5cblx0Ly8gQG9wdGlvbiB3aGVlbERlYm91bmNlVGltZTogTnVtYmVyID0gNDBcblx0Ly8gTGltaXRzIHRoZSByYXRlIGF0IHdoaWNoIGEgd2hlZWwgY2FuIGZpcmUgKGluIG1pbGxpc2Vjb25kcykuIEJ5IGRlZmF1bHRcblx0Ly8gdXNlciBjYW4ndCB6b29tIHZpYSB3aGVlbCBtb3JlIG9mdGVuIHRoYW4gb25jZSBwZXIgNDAgbXMuXG5cdHdoZWVsRGVib3VuY2VUaW1lOiA0MCxcblxuXHQvLyBAb3B0aW9uIHdoZWVsUHhQZXJab29tTGV2ZWw6IE51bWJlciA9IDYwXG5cdC8vIEhvdyBtYW55IHNjcm9sbCBwaXhlbHMgKGFzIHJlcG9ydGVkIGJ5IFtMLkRvbUV2ZW50LmdldFdoZWVsRGVsdGFdKCNkb21ldmVudC1nZXR3aGVlbGRlbHRhKSlcblx0Ly8gbWVhbiBhIGNoYW5nZSBvZiBvbmUgZnVsbCB6b29tIGxldmVsLiBTbWFsbGVyIHZhbHVlcyB3aWxsIG1ha2Ugd2hlZWwtem9vbWluZ1xuXHQvLyBmYXN0ZXIgKGFuZCB2aWNlIHZlcnNhKS5cblx0d2hlZWxQeFBlclpvb21MZXZlbDogNjBcbn0pO1xuXG52YXIgU2Nyb2xsV2hlZWxab29tID0gSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdG9uKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbW91c2V3aGVlbCcsIHRoaXMuX29uV2hlZWxTY3JvbGwsIHRoaXMpO1xuXG5cdFx0dGhpcy5fZGVsdGEgPSAwO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0b2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbW91c2V3aGVlbCcsIHRoaXMuX29uV2hlZWxTY3JvbGwsIHRoaXMpO1xuXHR9LFxuXG5cdF9vbldoZWVsU2Nyb2xsOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBkZWx0YSA9IGdldFdoZWVsRGVsdGEoZSk7XG5cblx0XHR2YXIgZGVib3VuY2UgPSB0aGlzLl9tYXAub3B0aW9ucy53aGVlbERlYm91bmNlVGltZTtcblxuXHRcdHRoaXMuX2RlbHRhICs9IGRlbHRhO1xuXHRcdHRoaXMuX2xhc3RNb3VzZVBvcyA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKTtcblxuXHRcdGlmICghdGhpcy5fc3RhcnRUaW1lKSB7XG5cdFx0XHR0aGlzLl9zdGFydFRpbWUgPSArbmV3IERhdGUoKTtcblx0XHR9XG5cblx0XHR2YXIgbGVmdCA9IE1hdGgubWF4KGRlYm91bmNlIC0gKCtuZXcgRGF0ZSgpIC0gdGhpcy5fc3RhcnRUaW1lKSwgMCk7XG5cblx0XHRjbGVhclRpbWVvdXQodGhpcy5fdGltZXIpO1xuXHRcdHRoaXMuX3RpbWVyID0gc2V0VGltZW91dChiaW5kKHRoaXMuX3BlcmZvcm1ab29tLCB0aGlzKSwgbGVmdCk7XG5cblx0XHRzdG9wKGUpO1xuXHR9LFxuXG5cdF9wZXJmb3JtWm9vbTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIHpvb20gPSBtYXAuZ2V0Wm9vbSgpLFxuXHRcdCAgICBzbmFwID0gdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXAgfHwgMDtcblxuXHRcdG1hcC5fc3RvcCgpOyAvLyBzdG9wIHBhbm5pbmcgYW5kIGZseSBhbmltYXRpb25zIGlmIGFueVxuXG5cdFx0Ly8gbWFwIHRoZSBkZWx0YSB3aXRoIGEgc2lnbW9pZCBmdW5jdGlvbiB0byAtNC4uNCByYW5nZSBsZWFuaW5nIG9uIC0xLi4xXG5cdFx0dmFyIGQyID0gdGhpcy5fZGVsdGEgLyAodGhpcy5fbWFwLm9wdGlvbnMud2hlZWxQeFBlclpvb21MZXZlbCAqIDQpLFxuXHRcdCAgICBkMyA9IDQgKiBNYXRoLmxvZygyIC8gKDEgKyBNYXRoLmV4cCgtTWF0aC5hYnMoZDIpKSkpIC8gTWF0aC5MTjIsXG5cdFx0ICAgIGQ0ID0gc25hcCA/IE1hdGguY2VpbChkMyAvIHNuYXApICogc25hcCA6IGQzLFxuXHRcdCAgICBkZWx0YSA9IG1hcC5fbGltaXRab29tKHpvb20gKyAodGhpcy5fZGVsdGEgPiAwID8gZDQgOiAtZDQpKSAtIHpvb207XG5cblx0XHR0aGlzLl9kZWx0YSA9IDA7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gbnVsbDtcblxuXHRcdGlmICghZGVsdGEpIHsgcmV0dXJuOyB9XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuc2Nyb2xsV2hlZWxab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0bWFwLnNldFpvb20oem9vbSArIGRlbHRhKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bWFwLnNldFpvb21Bcm91bmQodGhpcy5fbGFzdE1vdXNlUG9zLCB6b29tICsgZGVsdGEpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgc2Nyb2xsV2hlZWxab29tOiBIYW5kbGVyXG4vLyBTY3JvbGwgd2hlZWwgem9vbSBoYW5kbGVyLlxuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3Njcm9sbFdoZWVsWm9vbScsIFNjcm9sbFdoZWVsWm9vbSk7XG5cbi8qXG4gKiBMLk1hcC5UYXAgaXMgdXNlZCB0byBlbmFibGUgbW9iaWxlIGhhY2tzIGxpa2UgcXVpY2sgdGFwcyBhbmQgbG9uZyBob2xkLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQHNlY3Rpb24gVG91Y2ggaW50ZXJhY3Rpb24gb3B0aW9uc1xuXHQvLyBAb3B0aW9uIHRhcDogQm9vbGVhbiA9IHRydWVcblx0Ly8gRW5hYmxlcyBtb2JpbGUgaGFja3MgZm9yIHN1cHBvcnRpbmcgaW5zdGFudCB0YXBzIChmaXhpbmcgMjAwbXMgY2xpY2tcblx0Ly8gZGVsYXkgb24gaU9TL0FuZHJvaWQpIGFuZCB0b3VjaCBob2xkcyAoZmlyZWQgYXMgYGNvbnRleHRtZW51YCBldmVudHMpLlxuXHR0YXA6IHRydWUsXG5cblx0Ly8gQG9wdGlvbiB0YXBUb2xlcmFuY2U6IE51bWJlciA9IDE1XG5cdC8vIFRoZSBtYXggbnVtYmVyIG9mIHBpeGVscyBhIHVzZXIgY2FuIHNoaWZ0IGhpcyBmaW5nZXIgZHVyaW5nIHRvdWNoXG5cdC8vIGZvciBpdCB0byBiZSBjb25zaWRlcmVkIGEgdmFsaWQgdGFwLlxuXHR0YXBUb2xlcmFuY2U6IDE1XG59KTtcblxudmFyIFRhcCA9IEhhbmRsZXIuZXh0ZW5kKHtcblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRvbih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ3RvdWNoc3RhcnQnLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0b2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uRG93biwgdGhpcyk7XG5cdH0sXG5cblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcykgeyByZXR1cm47IH1cblxuXHRcdH
14775ByZXZlbnREZWZhdWx0KGUpO1xuXG5cdFx0dGhpcy5fZmlyZUNsaWNrID0gdHJ1ZTtcblxuXHRcdC8vIGRvbid0IHNpbXVsYXRlIGNsaWNrIG9yIHRyYWNrIGxvbmdwcmVzcyBpZiBtb3JlIHRoYW4gMSB0b3VjaFxuXHRcdGlmIChlLnRvdWNoZXMubGVuZ3RoID4gMSkge1xuXHRcdFx0dGhpcy5fZmlyZUNsaWNrID0gZmFsc2U7XG5cdFx0XHRjbGVhclRpbWVvdXQodGhpcy5faG9sZFRpbWVvdXQpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlc1swXSxcblx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHR0aGlzLl9zdGFydFBvcyA9IHRoaXMuX25ld1BvcyA9IG5ldyBQb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKTtcblxuXHRcdC8vIGlmIHRvdWNoaW5nIGEgbGluaywgaGlnaGxpZ2h0IGl0XG5cdFx0aWYgKGVsLnRhZ05hbWUgJiYgZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnYScpIHtcblx0XHRcdGFkZENsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHR9XG5cblx0XHQvLyBzaW11bGF0ZSBsb25nIGhvbGQgYnV0IHNldHRpbmcgYSB0aW1lb3V0XG5cdFx0dGhpcy5faG9sZFRpbWVvdXQgPSBzZXRUaW1lb3V0KGJpbmQoZnVuY3Rpb24gKCkge1xuXHRcdFx0aWYgKHRoaXMuX2lzVGFwVmFsaWQoKSkge1xuXHRcdFx0XHR0aGlzLl9maXJlQ2xpY2sgPSBmYWxzZTtcblx0XHRcdFx0dGhpcy5fb25VcCgpO1xuXHRcdFx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdjb250ZXh0bWVudScsIGZpcnN0KTtcblx0XHRcdH1cblx0XHR9LCB0aGlzKSwgMTAwMCk7XG5cblx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdtb3VzZWRvd24nLCBmaXJzdCk7XG5cblx0XHRvbihkb2N1bWVudCwge1xuXHRcdFx0dG91Y2htb3ZlOiB0aGlzLl9vbk1vdmUsXG5cdFx0XHR0b3VjaGVuZDogdGhpcy5fb25VcFxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xuXHRcdGNsZWFyVGltZW91dCh0aGlzLl9ob2xkVGltZW91dCk7XG5cblx0XHRvZmYoZG9jdW1lbnQsIHtcblx0XHRcdHRvdWNobW92ZTogdGhpcy5fb25Nb3ZlLFxuXHRcdFx0dG91Y2hlbmQ6IHRoaXMuX29uVXBcblx0XHR9LCB0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9maXJlQ2xpY2sgJiYgZSAmJiBlLmNoYW5nZWRUb3VjaGVzKSB7XG5cblx0XHRcdHZhciBmaXJzdCA9IGUuY2hhbmdlZFRvdWNoZXNbMF0sXG5cdFx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHRcdGlmIChlbCAmJiBlbC50YWdOYW1lICYmIGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2EnKSB7XG5cdFx0XHRcdHJlbW92ZUNsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnbW91c2V1cCcsIGZpcnN0KTtcblxuXHRcdFx0Ly8gc2ltdWxhdGUgY2xpY2sgaWYgdGhlIHRvdWNoIGRpZG4ndCBtb3ZlIHRvbyBtdWNoXG5cdFx0XHRpZiAodGhpcy5faXNUYXBWYWxpZCgpKSB7XG5cdFx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ2NsaWNrJywgZmlyc3QpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfaXNUYXBWYWxpZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9uZXdQb3MuZGlzdGFuY2VUbyh0aGlzLl9zdGFydFBvcykgPD0gdGhpcy5fbWFwLm9wdGlvbnMudGFwVG9sZXJhbmNlO1xuXHR9LFxuXG5cdF9vbk1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzWzBdO1xuXHRcdHRoaXMuX25ld1BvcyA9IG5ldyBQb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKTtcblx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdtb3VzZW1vdmUnLCBmaXJzdCk7XG5cdH0sXG5cblx0X3NpbXVsYXRlRXZlbnQ6IGZ1bmN0aW9uICh0eXBlLCBlKSB7XG5cdFx0dmFyIHNpbXVsYXRlZEV2ZW50ID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ01vdXNlRXZlbnRzJyk7XG5cblx0XHRzaW11bGF0ZWRFdmVudC5fc2ltdWxhdGVkID0gdHJ1ZTtcblx0XHRlLnRhcmdldC5fc2ltdWxhdGVkQ2xpY2sgPSB0cnVlO1xuXG5cdFx0c2ltdWxhdGVkRXZlbnQuaW5pdE1vdXNlRXZlbnQoXG5cdFx0ICAgICAgICB0eXBlLCB0cnVlLCB0cnVlLCB3aW5kb3csIDEsXG5cdFx0ICAgICAgICBlLnNjcmVlblgsIGUuc2NyZWVuWSxcblx0XHQgICAgICAgIGUuY2xpZW50WCwgZS5jbGllbnRZLFxuXHRcdCAgICAgICAgZmFsc2UsIGZhbHNlLCBmYWxzZSwgZmFsc2UsIDAsIG51bGwpO1xuXG5cdFx0ZS50YXJnZXQuZGlzcGF0Y2hFdmVudChzaW11bGF0ZWRFdmVudCk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRhcDogSGFuZGxlclxuLy8gTW9iaWxlIHRvdWNoIGhhY2tzIChxdWljayB0YXAgYW5kIHRvdWNoIGhvbGQpIGhhbmRsZXIuXG5pZiAodG91Y2ggJiYgIXBvaW50ZXIpIHtcblx0TWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3RhcCcsIFRhcCk7XG59XG5cbi8qXG4gKiBMLkhhbmRsZXIuVG91Y2hab29tIGlzIHVzZWQgYnkgTC5NYXAgdG8gYWRkIHBpbmNoIHpvb20gb24gc3VwcG9ydGVkIG1vYmlsZSBicm93c2Vycy5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIFRvdWNoIGludGVyYWN0aW9uIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiB0b3VjaFpvb206IEJvb2xlYW58U3RyaW5nID0gKlxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCBieSB0b3VjaC1kcmFnZ2luZyB3aXRoIHR3byBmaW5nZXJzLiBJZlxuXHQvLyBwYXNzZWQgYCdjZW50ZXInYCwgaXQgd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlIHZpZXcgcmVnYXJkbGVzcyBvZlxuXHQvLyB3aGVyZSB0aGUgdG91Y2ggZXZlbnRzIChmaW5nZXJzKSB3ZXJlLiBFbmFibGVkIGZvciB0b3VjaC1jYXBhYmxlIHdlYlxuXHQvLyBicm93c2VycyBleGNlcHQgZm9yIG9sZCBBbmRyb2lkcy5cblx0dG91Y2hab29tOiB0b3VjaCAmJiAhYW5kcm9pZDIzLFxuXG5cdC8vIEBvcHRpb24gYm91bmNlQXRab29tTGltaXRzOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBTZXQgaXQgdG8gZmFsc2UgaWYgeW91IGRvbid0IHdhbnQgdGhlIG1hcCB0byB6b29tIGJleW9uZCBtaW4vbWF4IHpvb21cblx0Ly8gYW5kIHRoZW4gYm91bmNlIGJhY2sgd2hlbiBwaW5jaC16b29taW5nLlxuXHRib3VuY2VBdFpvb21MaW1pdHM6IHRydWVcbn0pO1xuXG52YXIgVG91Y2hab29tID0gSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdGFkZENsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0b24odGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Ub3VjaFN0YXJ0LCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0b2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uVG91Y2hTdGFydCwgdGhpcyk7XG5cdH0sXG5cblx0X29uVG91Y2hTdGFydDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT09IDIgfHwgbWFwLl9hbmltYXRpbmdab29tIHx8IHRoaXMuX3pvb21pbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKTtcblxuXHRcdHRoaXMuX2NlbnRlclBvaW50ID0gbWFwLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XG5cdFx0dGhpcy5fc3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyh0aGlzLl9jZW50ZXJQb2ludCk7XG5cdFx0aWYgKG1hcC5vcHRpb25zLnRvdWNoWm9vbSAhPT0gJ2Nlbn
14775RlcicpIHtcblx0XHRcdHRoaXMuX3BpbmNoU3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhwMS5hZGQocDIpLl9kaXZpZGVCeSgyKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc3RhcnREaXN0ID0gcDEuZGlzdGFuY2VUbyhwMik7XG5cdFx0dGhpcy5fc3RhcnRab29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XG5cdFx0dGhpcy5fem9vbWluZyA9IHRydWU7XG5cblx0XHRtYXAuX3N0b3AoKTtcblxuXHRcdG9uKGRvY3VtZW50LCAndG91Y2htb3ZlJywgdGhpcy5fb25Ub3VjaE1vdmUsIHRoaXMpO1xuXHRcdG9uKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kLCB0aGlzKTtcblxuXHRcdHByZXZlbnREZWZhdWx0KGUpO1xuXHR9LFxuXG5cdF9vblRvdWNoTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcyB8fCBlLnRvdWNoZXMubGVuZ3RoICE9PSAyIHx8ICF0aGlzLl96b29taW5nKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKSxcblx0XHQgICAgc2NhbGUgPSBwMS5kaXN0YW5jZVRvKHAyKSAvIHRoaXMuX3N0YXJ0RGlzdDtcblxuXHRcdHRoaXMuX3pvb20gPSBtYXAuZ2V0U2NhbGVab29tKHNjYWxlLCB0aGlzLl9zdGFydFpvb20pO1xuXG5cdFx0aWYgKCFtYXAub3B0aW9ucy5ib3VuY2VBdFpvb21MaW1pdHMgJiYgKFxuXHRcdFx0KHRoaXMuX3pvb20gPCBtYXAuZ2V0TWluWm9vbSgpICYmIHNjYWxlIDwgMSkgfHxcblx0XHRcdCh0aGlzLl96b29tID4gbWFwLmdldE1heFpvb20oKSAmJiBzY2FsZSA+IDEpKSkge1xuXHRcdFx0dGhpcy5fem9vbSA9IG1hcC5fbGltaXRab29tKHRoaXMuX3pvb20pO1xuXHRcdH1cblxuXHRcdGlmIChtYXAub3B0aW9ucy50b3VjaFpvb20gPT09ICdjZW50ZXInKSB7XG5cdFx0XHR0aGlzLl9jZW50ZXIgPSB0aGlzLl9zdGFydExhdExuZztcblx0XHRcdGlmIChzY2FsZSA9PT0gMSkgeyByZXR1cm47IH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gR2V0IGRlbHRhIGZyb20gcGluY2ggdG8gY2VudGVyLCBzbyBjZW50ZXJMYXRMbmcgaXMgZGVsdGEgYXBwbGllZCB0byBpbml0aWFsIHBpbmNoTGF0TG5nXG5cdFx0XHR2YXIgZGVsdGEgPSBwMS5fYWRkKHAyKS5fZGl2aWRlQnkoMikuX3N1YnRyYWN0KHRoaXMuX2NlbnRlclBvaW50KTtcblx0XHRcdGlmIChzY2FsZSA9PT0gMSAmJiBkZWx0YS54ID09PSAwICYmIGRlbHRhLnkgPT09IDApIHsgcmV0dXJuOyB9XG5cdFx0XHR0aGlzLl9jZW50ZXIgPSBtYXAudW5wcm9qZWN0KG1hcC5wcm9qZWN0KHRoaXMuX3BpbmNoU3RhcnRMYXRMbmcsIHRoaXMuX3pvb20pLnN1YnRyYWN0KGRlbHRhKSwgdGhpcy5fem9vbSk7XG5cdFx0fVxuXG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xuXHRcdFx0bWFwLl9tb3ZlU3RhcnQodHJ1ZSwgZmFsc2UpO1xuXHRcdFx0dGhpcy5fbW92ZWQgPSB0cnVlO1xuXHRcdH1cblxuXHRcdGNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XG5cblx0XHR2YXIgbW92ZUZuID0gYmluZChtYXAuX21vdmUsIG1hcCwgdGhpcy5fY2VudGVyLCB0aGlzLl96b29tLCB7cGluY2g6IHRydWUsIHJvdW5kOiBmYWxzZX0pO1xuXHRcdHRoaXMuX2FuaW1SZXF1ZXN0ID0gcmVxdWVzdEFuaW1GcmFtZShtb3ZlRm4sIHRoaXMsIHRydWUpO1xuXG5cdFx0cHJldmVudERlZmF1bHQoZSk7XG5cdH0sXG5cblx0X29uVG91Y2hFbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkIHx8ICF0aGlzLl96b29taW5nKSB7XG5cdFx0XHR0aGlzLl96b29taW5nID0gZmFsc2U7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5fem9vbWluZyA9IGZhbHNlO1xuXHRcdGNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XG5cblx0XHRvZmYoZG9jdW1lbnQsICd0b3VjaG1vdmUnLCB0aGlzLl9vblRvdWNoTW92ZSk7XG5cdFx0b2ZmKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kKTtcblxuXHRcdC8vIFBpbmNoIHVwZGF0ZXMgR3JpZExheWVycycgbGV2ZWxzIG9ubHkgd2hlbiB6b29tU25hcCBpcyBvZmYsIHNvIHpvb21TbmFwIGJlY29tZXMgbm9VcGRhdGUuXG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLnpvb21BbmltYXRpb24pIHtcblx0XHRcdHRoaXMuX21hcC5fYW5pbWF0ZVpvb20odGhpcy5fY2VudGVyLCB0aGlzLl9tYXAuX2xpbWl0Wm9vbSh0aGlzLl96b29tKSwgdHJ1ZSwgdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9tYXAuX3Jlc2V0Vmlldyh0aGlzLl9jZW50ZXIsIHRoaXMuX21hcC5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRvdWNoWm9vbTogSGFuZGxlclxuLy8gVG91Y2ggem9vbSBoYW5kbGVyLlxuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3RvdWNoWm9vbScsIFRvdWNoWm9vbSk7XG5cbk1hcC5Cb3hab29tID0gQm94Wm9vbTtcbk1hcC5Eb3VibGVDbGlja1pvb20gPSBEb3VibGVDbGlja1pvb207XG5NYXAuRHJhZyA9IERyYWc7XG5NYXAuS2V5Ym9hcmQgPSBLZXlib2FyZDtcbk1hcC5TY3JvbGxXaGVlbFpvb20gPSBTY3JvbGxXaGVlbFpvb207XG5NYXAuVGFwID0gVGFwO1xuTWFwLlRvdWNoWm9vbSA9IFRvdWNoWm9vbTtcblxuT2JqZWN0LmZyZWV6ZSA9IGZyZWV6ZTtcblxuZXhwb3J0cy52ZXJzaW9uID0gdmVyc2lvbjtcbmV4cG9ydHMuQ29udHJvbCA9IENvbnRyb2w7XG5leHBvcnRzLmNvbnRyb2wgPSBjb250cm9sO1xuZXhwb3J0cy5Ccm93c2VyID0gQnJvd3NlcjtcbmV4cG9ydHMuRXZlbnRlZCA9IEV2ZW50ZWQ7XG5leHBvcnRzLk1peGluID0gTWl4aW47XG5leHBvcnRzLlV0aWwgPSBVdGlsO1xuZXhwb3J0cy5DbGFzcyA9IENsYXNzO1xuZXhwb3J0cy5IYW5kbGVyID0gSGFuZGxlcjtcbmV4cG9ydHMuZXh0ZW5kID0gZXh0ZW5kO1xuZXhwb3J0cy5iaW5kID0gYmluZDtcbmV4cG9ydHMuc3RhbXAgPSBzdGFtcDtcbmV4cG9ydHMuc2V0T3B0aW9ucyA9IHNldE9wdGlvbnM7XG5leHBvcnRzLkRvbUV2ZW50ID0gRG9tRXZlbnQ7XG5leHBvcnRzLkRvbVV0aWwgPSBEb21VdGlsO1xuZXhwb3J0cy5Qb3NBbmltYXRpb24gPSBQb3NBbmltYXRpb247XG5leHBvcnRzLkRyYWdnYWJsZSA9IERyYWdnYWJsZTtcbmV4cG9ydHMuTGluZVV0aWwgPSBMaW5lVXRpbDtcbmV4cG9ydHMuUG9seVV0aWwgPSBQb2x5VXRpbDtcbmV4cG9ydHMuUG9pbnQgPSBQb2ludDtcbmV4cG9ydHMucG9pbnQgPSB0b1BvaW50O1xuZXhwb3J0cy5Cb3VuZHMgPSBCb3VuZHM7XG5leHBvcnRzLmJvdW5kcyA9IHRvQm91bmRzO1xuZXhwb3J0cy5UcmFuc2Zvcm1hdGlvbiA9I
14775FRyYW5zZm9ybWF0aW9uO1xuZXhwb3J0cy50cmFuc2Zvcm1hdGlvbiA9IHRvVHJhbnNmb3JtYXRpb247XG5leHBvcnRzLlByb2plY3Rpb24gPSBpbmRleDtcbmV4cG9ydHMuTGF0TG5nID0gTGF0TG5nO1xuZXhwb3J0cy5sYXRMbmcgPSB0b0xhdExuZztcbmV4cG9ydHMuTGF0TG5nQm91bmRzID0gTGF0TG5nQm91bmRzO1xuZXhwb3J0cy5sYXRMbmdCb3VuZHMgPSB0b0xhdExuZ0JvdW5kcztcbmV4cG9ydHMuQ1JTID0gQ1JTO1xuZXhwb3J0cy5HZW9KU09OID0gR2VvSlNPTjtcbmV4cG9ydHMuZ2VvSlNPTiA9IGdlb0pTT047XG5leHBvcnRzLmdlb0pzb24gPSBnZW9Kc29uO1xuZXhwb3J0cy5MYXllciA9IExheWVyO1xuZXhwb3J0cy5MYXllckdyb3VwID0gTGF5ZXJHcm91cDtcbmV4cG9ydHMubGF5ZXJHcm91cCA9IGxheWVyR3JvdXA7XG5leHBvcnRzLkZlYXR1cmVHcm91cCA9IEZlYXR1cmVHcm91cDtcbmV4cG9ydHMuZmVhdHVyZUdyb3VwID0gZmVhdHVyZUdyb3VwO1xuZXhwb3J0cy5JbWFnZU92ZXJsYXkgPSBJbWFnZU92ZXJsYXk7XG5leHBvcnRzLmltYWdlT3ZlcmxheSA9IGltYWdlT3ZlcmxheTtcbmV4cG9ydHMuVmlkZW9PdmVybGF5ID0gVmlkZW9PdmVybGF5O1xuZXhwb3J0cy52aWRlb092ZXJsYXkgPSB2aWRlb092ZXJsYXk7XG5leHBvcnRzLlNWR092ZXJsYXkgPSBTVkdPdmVybGF5O1xuZXhwb3J0cy5zdmdPdmVybGF5ID0gc3ZnT3ZlcmxheTtcbmV4cG9ydHMuRGl2T3ZlcmxheSA9IERpdk92ZXJsYXk7XG5leHBvcnRzLlBvcHVwID0gUG9wdXA7XG5leHBvcnRzLnBvcHVwID0gcG9wdXA7XG5leHBvcnRzLlRvb2x0aXAgPSBUb29sdGlwO1xuZXhwb3J0cy50b29sdGlwID0gdG9vbHRpcDtcbmV4cG9ydHMuSWNvbiA9IEljb247XG5leHBvcnRzLmljb24gPSBpY29uO1xuZXhwb3J0cy5EaXZJY29uID0gRGl2SWNvbjtcbmV4cG9ydHMuZGl2SWNvbiA9IGRpdkljb247XG5leHBvcnRzLk1hcmtlciA9IE1hcmtlcjtcbmV4cG9ydHMubWFya2VyID0gbWFya2VyO1xuZXhwb3J0cy5UaWxlTGF5ZXIgPSBUaWxlTGF5ZXI7XG5leHBvcnRzLnRpbGVMYXllciA9IHRpbGVMYXllcjtcbmV4cG9ydHMuR3JpZExheWVyID0gR3JpZExheWVyO1xuZXhwb3J0cy5ncmlkTGF5ZXIgPSBncmlkTGF5ZXI7XG5leHBvcnRzLlNWRyA9IFNWRztcbmV4cG9ydHMuc3ZnID0gc3ZnJDE7XG5leHBvcnRzLlJlbmRlcmVyID0gUmVuZGVyZXI7XG5leHBvcnRzLkNhbnZhcyA9IENhbnZhcztcbmV4cG9ydHMuY2FudmFzID0gY2FudmFzJDE7XG5leHBvcnRzLlBhdGggPSBQYXRoO1xuZXhwb3J0cy5DaXJjbGVNYXJrZXIgPSBDaXJjbGVNYXJrZXI7XG5leHBvcnRzLmNpcmNsZU1hcmtlciA9IGNpcmNsZU1hcmtlcjtcbmV4cG9ydHMuQ2lyY2xlID0gQ2lyY2xlO1xuZXhwb3J0cy5jaXJjbGUgPSBjaXJjbGU7XG5leHBvcnRzLlBvbHlsaW5lID0gUG9seWxpbmU7XG5leHBvcnRzLnBvbHlsaW5lID0gcG9seWxpbmU7XG5leHBvcnRzLlBvbHlnb24gPSBQb2x5Z29uO1xuZXhwb3J0cy5wb2x5Z29uID0gcG9seWdvbjtcbmV4cG9ydHMuUmVjdGFuZ2xlID0gUmVjdGFuZ2xlO1xuZXhwb3J0cy5yZWN0YW5nbGUgPSByZWN0YW5nbGU7XG5leHBvcnRzLk1hcCA9IE1hcDtcbmV4cG9ydHMubWFwID0gY3JlYXRlTWFwO1xuXG52YXIgb2xkTCA9IHdpbmRvdy5MO1xuZXhwb3J0cy5ub0NvbmZsaWN0ID0gZnVuY3Rpb24oKSB7XG5cdHdpbmRvdy5MID0gb2xkTDtcblx0cmV0dXJuIHRoaXM7XG59XG5cbi8vIEFsd2F5cyBleHBvcnQgdXMgdG8gd2luZG93IGdsb2JhbCAoc2VlICMyMzY0KVxud2luZG93LkwgPSBleHBvcnRzO1xuXG59KSkpO1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9bGVhZmxldC1zcmMuanMubWFwXG4iLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHRpZihfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdKSB7XG5cdFx0cmV0dXJuIF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0uZXhwb3J0cztcblx0fVxuXHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuXHR2YXIgbW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXSA9IHtcblx0XHQvLyBubyBtb2R1bGUuaWQgbmVlZGVkXG5cdFx0Ly8gbm8gbW9kdWxlLmxvYWRlZCBuZWVkZWRcblx0XHRleHBvcnRzOiB7fVxuXHR9O1xuXG5cdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuXHRfX3dlYnBhY2tfbW9kdWxlc19fW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuXHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuXHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG59XG5cbiIsIi8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG5fX3dlYn
14775BhY2tfcmVxdWlyZV9fLm4gPSAobW9kdWxlKSA9PiB7XG5cdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuXHRcdCgpID0+IG1vZHVsZVsnZGVmYXVsdCddIDpcblx0XHQoKSA9PiBtb2R1bGU7XG5cdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsIHsgYTogZ2V0dGVyIH0pO1xuXHRyZXR1cm4gZ2V0dGVyO1xufTsiLCIvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9ucyBmb3IgaGFybW9ueSBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSAoZXhwb3J0cywgZGVmaW5pdGlvbikgPT4ge1xuXHRmb3IodmFyIGtleSBpbiBkZWZpbml0aW9uKSB7XG5cdFx0aWYoX193ZWJwYWNrX3JlcXVpcmVfXy5vKGRlZmluaXRpb24sIGtleSkgJiYgIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBrZXkpKSB7XG5cdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywga2V5LCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZGVmaW5pdGlvbltrZXldIH0pO1xuXHRcdH1cblx0fVxufTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSAob2JqLCBwcm9wKSA9PiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSIsIi8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uciA9IChleHBvcnRzKSA9PiB7XG5cdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuXHR9XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG59OyIsIi8vIHN0YXJ0dXBcbi8vIExvYWQgZW50cnkgbW9kdWxlXG5fX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvaW5kZXguanNcIik7XG4vLyBUaGlzIGVudHJ5IG1vZHVsZSB1c2VkICdleHBvcnRzJyBzbyBpdCBjYW4ndCBiZSBpbmxpbmVkXG4iXSwic291cmNlUm9vdCI6IiJ9
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.