PageSourceSearch

https://publicspace.tools/wp-content/themes/pst/js/dist/map.js?ver=1

js publicspace.tools collected 2026-10-08 15:59:15 UTC 1,194,429 bytes, 14,775 lines download raw bytes

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 = '&#x2212;'
5977		// The text set on the 'zoom out' button.
5978		zoomOutText: '&#x2212;',
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 &copy; 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 = '&#215;';
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 &copy; <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 "&commat;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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E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhLGNBQWM7O0FBRTNCOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7OztBQUlBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5Q0FBeUMseUJBQXlCLE9BQU8sc0NBQXNDOztBQUUvRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLGFBQWE7O0FBRW5DO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCO0FBQ3JCOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0NBQXdDO0FBQ3hDO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUNBQXFDO0FBQ3JDO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsd0JBQXdCO0FBQ3hCO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGLENBQUM7O0FBRUQsMEJBQTBCO0FBQzFCO0FBQ0EsQ0FBQzs7QUFFRCxrQkFBa0I7QUFDbEI7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLGdDQUFnQyxTQUFTO0FBQ3pDOztBQUVBLG1DQUFtQyxVQUFVO0FBQzdDO0FBQ0E7QUFDQTs7QUFFQSxrQ0FBa0M7QUFDbEM7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQSx5QkFBeUI7QUFDekI7O0FBRUEsNEJBQTRCO0FBQzVCOztBQUVBLDJCQUEyQjtBQUMzQjs7QUFFQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBOztBQUVBLGdDQUFnQztBQUNoQzs7QUFFQTtBQUNBLGdGQUFnRjtBQUNoRixtQ0FBbUM7QUFDbkM7O0FBRUEsNEJBQTRCO0FBQzVCOztBQUVBLDZCQUE2QjtBQUM3Qjs7QUFFQSw0QkFBNEI7QUFDNUI7O0FBRUEsNkJBQTZCO0FBQzdCOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSwwQkFBMEI7QUFDMUI7O0FBRUEsMkJBQTJCO0FBQzNCOztBQUVBLCtCQUErQjtBQUMvQjs7QUFFQSw4QkFBOEI7QUFDOUI7O0FBRUE7QUFDQTtBQUNBOztBQUVBLDZCQUE2QjtBQUM3Qjs7QUFFQSxtQ0FBbUM7QUFDbkM7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsa0NBQWtDO0FBQ2xDOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFDQUFxQztBQUNyQztBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUEsRUFBRTtBQUNGO0FBQ0E7QUFDQSxDQUFDOzs7QUFHRDtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEsRUFBRTtBQUNGOztBQUVBLEVBQUU7QUFDRjtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBLEVBQUU7QUFDRjs7QUFFQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUdBQW1HLFFBQVE7O0FBRTNHO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EsOENBQThDLFFBQVE7QUFDdEQ7QUFDQSxHQUFHO0FBQ0g7QUFDQTs7QUFFQSxrQkFBa0IsUUFBUTs7QUFFMUI7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsUUFBUTtBQUN2RDtBQUNBLHFCQUFxQjtBQUNyQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBLGtFQUFrRSxlQUFlO0FBQ2pGLDhEQUE4RCxlQUFlOztBQUU3RTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsbUVBQW1FLGVBQWU7QUFDbEYsK0RBQStELGVBQWU7QUFDOUU7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1Q0FBdUMsU0FBUztBQUNoRDtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQSxvQkFBb0IsUUFBUTtBQUM1Qjs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxnQkFBZ0Isa0JBQWtCO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
14775BRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLFFBQVE7QUFDOUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QixRQUFRO0FBQ2hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0Q0FBNEM7O0FBRTVDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNkNBQTZDLHVDQUF1QztBQUNwRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjs7QUFFQSxxQ0FBcUMsU0FBUztBQUM5QztBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsZ0RBQWdELHVDQUF1QztBQUN2Rjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjs7QUFFQSxxQ0FBcUMsU0FBUztBQUM5QztBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBLDRDQUE0QyxhQUFhOztBQUV6RDtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxFQUFFOztBQUVGO0FBQ0EsNkZBQTZGLGVBQWU7O0FBRTVHLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsRUFBRTtBQUNGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUEsZ0JBQWdCLGFBQWE7O0FBRTdCO0FBQ0E7O0FBRUEsRUFBRTtBQUNGO0FBQ0E7O0FBRUEsRUFBRTs7QUFFRjtBQUNBLGdHQUFnRyxlQUFlOztBQUUvRyxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsRUFBRTtBQUNGO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEVBQUUsNEJBQTRCO0FBQzlCO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSx1Q0FBdUM7O0FBRXZDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUEsZ0JBQWdCLGFBQWE7O0FBRTdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLDBCQUEwQixRQUFROztBQUVsQztBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQSxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0F
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
14775BRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0EsaURBQWlEOztBQUVqRCw4Q0FBOEMsU0FBUzs7QUFFdkQ7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsbUJBQW1CLFFBQVE7O0FBRTNCO0FBQ0EsY0FBYyxRQUFROztBQUV0Qjs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVDQUF1QyxVQUFVOztBQUVqRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLG9DQUFvQyxvQkFBb0I7O0FBRXhEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBLHdCQUF3QixRQUFROztBQUVoQztBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUE7QUFDQTs7QUFFQSwyQkFBMkIsa0JBQWtCOztBQUU3QztBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGOztBQUVBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7O0FBRUEscUJBQXFCLGVBQWU7QUFDcEMsc0JBQXNCLGVBQWU7O0FBRXJDO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEsS0FBSztBQUNMO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0EsK0NBQStDLFFBQVE7O0FBRXZEO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBLGFBQWEsUUFBUTtBQUNyQjs7QUFFQSw2QkFBNkIsMEJBQTBCO0FBQ3ZELHFDQUFxQyxRQU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FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSxFQUFFOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBLGNBQWMsUUFBUTs7QUFFdEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsQ0FBQzs7O0FBR0Q7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGOztBQUVBOztBQUVBLDJCQUEyQjs7QUFFM0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsRUFBRTs7QUFFRjs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQSxFQUFFOztBQUVGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0FBRUY7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsQ0FBQzs7O0FBR0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7O0F
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.