PluginProbe
Booking Calendar / 10.15.6
Booking Calendar v10.15.6
11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 All 204 releases
booking / _dist / bfb / _src / wpbc_bfb.js

wpbc_bfb.js in Booking Calendar 10.15.6, at _dist/bfb/_src/wpbc_bfb.js

7,145 lines 663.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // ---------------------------------------------------------------------------------------------------------------------
2 // == File /includes/page-form-builder/_out/core/bfb-core.js == | 2025-09-10 15:47
3 // ---------------------------------------------------------------------------------------------------------------------
4 (function ( w ) {
5 'use strict';
6
7 // Single global namespace (idempotent & load-order safe).
8 const Core = ( w.WPBC_BFB_Core = w.WPBC_BFB_Core || {} );
9 const UI = ( Core.UI = Core.UI || {} );
10
11 /**
12 * Core sanitize/escape/normalize helpers.
13 * All methods use snake_case; camelCase aliases are provided for backwards compatibility.
14 */
15 Core.WPBC_BFB_Sanitize = class {
16
17 /**
18 * Escape text for safe use in CSS selectors.
19 * @param {string} s - raw selector fragment
20 * @returns {string}
21 */
22 static esc_css(s) {
23 return (w.CSS && w.CSS.escape) ? w.CSS.escape( String( s ) ) : String( s ).replace( /([^\w-])/g, '\\$1' );
24 }
25
26 /**
27 * Escape a value for attribute selectors, e.g. [data-id="<value>"].
28 * @param {string} v
29 * @returns {string}
30 */
31 static esc_attr_value_for_selector(v) {
32 return String( v )
33 .replace( /\\/g, '\\\\' )
34 .replace( /"/g, '\\"' )
35 .replace( /\n/g, '\\A ' )
36 .replace( /\]/g, '\\]' );
37 }
38
39 /**
40 * Sanitize into a broadly compatible HTML id: letters, digits, - _ : . ; must start with a letter.
41 * @param {string} v
42 * @returns {string}
43 */
44 static sanitize_html_id(v) {
45 let s = (v == null ? '' : String( v )).trim();
46 s = s
47 .replace( /\s+/g, '-' )
48 .replace( /[^A-Za-z0-9\-_\:.]/g, '-' )
49 .replace( /-+/g, '-' )
50 .replace( /^[-_.:]+|[-_.:]+$/g, '' );
51 if ( !s ) return 'field';
52 if ( !/^[A-Za-z]/.test( s ) ) s = 'f-' + s;
53 return s;
54 }
55
56 /**
57 * Sanitize into a safe HTML name token: letters, digits, _ -
58 * Must start with a letter; no dots/brackets/spaces.
59 * @param {string} v
60 * @returns {string}
61 */
62 static sanitize_html_name(v) {
63
64 let s = (v == null ? '' : String( v )).trim();
65
66 s = s.replace( /\s+/g, '_' ).replace( /[^A-Za-z0-9_-]/g, '_' ).replace( /_+/g, '_' );
67
68 if ( ! s ) {
69 s = 'field';
70 }
71 if ( ! /^[A-Za-z]/.test( s ) ) {
72 s = 'f_' + s;
73 }
74 return s;
75 }
76
77 /**
78 * Escape for HTML text/attributes (not URLs).
79 * @param {any} v
80 * @returns {string}
81 */
82 static escape_html(v) {
83 if ( v == null ) {
84 return '';
85 }
86 return String( v )
87 .replace( /&/g, '&amp;' )
88 .replace( /"/g, '&quot;' )
89 .replace( /'/g, '&#039;' )
90 .replace( /</g, '&lt;' )
91 .replace( />/g, '&gt;' );
92 }
93
94 /**
95 * Escape minimal set for attribute-safety without slugging.
96 * Keeps original human text; escapes &, <, >, " and ' only.
97 * @param {string} s
98 * @returns {string}
99 */
100 static escape_value_for_attr(s) {
101 return String( s == null ? '' : s )
102 .replace( /&/g, '&amp;' )
103 .replace( /</g, '&lt;' )
104 .replace( />/g, '&gt;' )
105 .replace( /"/g, '&quot;' )
106 .replace( /'/g, '&#39;' );
107 }
108
109 /**
110 * Sanitize a space-separated CSS class list.
111 * @param {any} v
112 * @returns {string}
113 */
114 static sanitize_css_classlist(v) {
115 if ( v == null ) return '';
116 return String( v ).replace( /[^\w\- ]+/g, ' ' ).replace( /\s+/g, ' ' ).trim();
117 }
118 // == NEW ==
119 /**
120 * Turn an arbitrary value into a conservative "token" (underscores, hyphens allowed).
121 * Useful for shortcode tokens, ids in plain text, etc.
122 * @param {any} v
123 * @returns {string}
124 */
125 static to_token(v) {
126 return String( v ?? '' )
127 .trim()
128 .replace( /\s+/g, '_' )
129 .replace( /[^A-Za-z0-9_\-]/g, '' );
130 }
131
132 /**
133 * Convert to kebab-case (letters, digits, hyphens).
134 * @param {any} v
135 * @returns {string}
136 */
137 static to_kebab(v) {
138 return String( v ?? '' )
139 .trim()
140 .replace( /[_\s]+/g, '-' )
141 .replace( /[^A-Za-z0-9-]/g, '' )
142 .replace( /-+/g, '-' )
143 .toLowerCase();
144 }
145
146 /**
147 * Truthy normalization for form-like inputs: true, 'true', 1, '1', 'yes', 'on'.
148 * @param {any} v
149 * @returns {boolean}
150 */
151 static is_truthy(v) {
152 if ( typeof v === 'boolean' ) return v;
153 const s = String( v ?? '' ).trim().toLowerCase();
154 return s === 'true' || s === '1' || s === 'yes' || s === 'on';
155 }
156
157 /**
158 * Coerce to boolean with an optional default for empty values.
159 * @param {any} v
160 * @param {boolean} [def=false]
161 * @returns {boolean}
162 */
163 static coerce_boolean(v, def = false) {
164 if ( v == null || v === '' ) return def;
165 return this.is_truthy( v );
166 }
167
168 /**
169 * Parse a "percent-like" value ('33'|'33%'|33) with fallback.
170 * @param {string|number|null|undefined} v
171 * @param {number} fallback_value
172 * @returns {number}
173 */
174 static parse_percent(v, fallback_value) {
175 if ( v == null ) {
176 return fallback_value;
177 }
178 const s = String( v ).trim();
179 const n = parseFloat( s.replace( /%/g, '' ) );
180 return Number.isFinite( n ) ? n : fallback_value;
181 }
182
183 /**
184 * Clamp a number to the [min, max] range.
185 * @param {number} n
186 * @param {number} min
187 * @param {number} max
188 * @returns {number}
189 */
190 static clamp(n, min, max) {
191 return Math.max( min, Math.min( max, n ) );
192 }
193
194 /**
195 * Escape a value for inclusion inside a quoted HTML attribute (double quotes).
196 * Replaces newlines with spaces and double quotes with single quotes.
197 * @param {any} v
198 * @returns {string}
199 */
200 static escape_for_attr_quoted(v) {
201 if ( v == null ) return '';
202 return String( v ).replace( /\r?\n/g, ' ' ).replace( /"/g, '\'' );
203 }
204
205 /**
206 * Escape for shortcode-like tokens where double quotes and newlines should be neutralized.
207 * @param {any} v
208 * @returns {string}
209 */
210 static escape_for_shortcode(v) {
211 return String( v ?? '' ).replace( /"/g, '\\"' ).replace( /\r?\n/g, ' ' );
212 }
213
214 /**
215 * JSON.parse with fallback (no throw).
216 * @param {string} s
217 * @param {any} [fallback=null]
218 * @returns {any}
219 */
220 static safe_json_parse(s, fallback = null) {
221 try {
222 return JSON.parse( s );
223 } catch ( _ ) {
224 return fallback;
225 }
226 }
227
228 /**
229 * Stringify data-* attribute value safely (objects -> JSON, others -> String).
230 * @param {any} v
231 * @returns {string}
232 */
233 static stringify_data_value(v) {
234 if ( typeof v === 'object' && v !== null ) {
235 try {
236 return JSON.stringify( v );
237 } catch {
238 console.error( 'WPBC: stringify_data_value' );
239 return '';
240 }
241 }
242 return String( v );
243 }
244
245 // -------------------------------------------------------------------------------------------------------------
246 // Strict value guards for CSS lengths and hex colors (defense-in-depth).
247 // -------------------------------------------------------------------------------------------------------------
248 /**
249 * Sanitize a CSS length. Allows: px, %, rem, em (lower/upper).
250 * Returns fallback if invalid.
251 * @param {any} v
252 * @param {string} [fallback='100%']
253 * @returns {string}
254 */
255 static sanitize_css_len(v, fallback = '100%') {
256 const s = String( v ?? '' ).trim();
257 const m = s.match( /^(-?\d+(?:\.\d+)?)(px|%|rem|em)$/i );
258 return m ? m[0] : String( fallback );
259 }
260
261 /**
262 * Sanitize a hex color. Allows #rgb or #rrggbb (case-insensitive).
263 * Returns fallback if invalid.
264 * @param {any} v
265 * @param {string} [fallback='#e0e0e0']
266 * @returns {string}
267 */
268 static sanitize_hex_color(v, fallback = '#e0e0e0') {
269 const s = String( v ?? '' ).trim();
270 return /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test( s ) ? s : String( fallback );
271 }
272
273 }
274
275 /**
276 * WPBC ID / Name service. Generates, sanitizes, and ensures uniqueness for field ids/names/html_ids within the
277 * canvas.
278 */
279 Core.WPBC_BFB_IdService = class {
280
281 /**
282 * Constructor. Set root container of the form pages.
283 *
284 * @param {HTMLElement} pages_container - Root container of the form pages.
285 */
286 constructor( pages_container ) {
287 this.pages_container = pages_container;
288 }
289
290 /**
291 * Ensure a unique **internal** field id (stored in data-id) within the canvas.
292 * Starts from a desired id (already sanitized or not) and appends suffixes if needed.
293 *
294 * @param {string} baseId - Desired id.
295 * @returns {string} Unique id.
296 */
297 ensure_unique_field_id(baseId, currentEl = null) {
298 const base = Core.WPBC_BFB_Sanitize.sanitize_html_id( baseId );
299 let id = base || 'field';
300 const esc = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
301 const escUid = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
302 const notSelf = currentEl?.dataset?.uid ? `:not([data-uid="${escUid( currentEl.dataset.uid )}"])` : '';
303 while ( this.pages_container?.querySelector(
304 `.wpbc_bfb__panel--preview .wpbc_bfb__field${notSelf}[data-id="${esc(id)}"], .wpbc_bfb__panel--preview .wpbc_bfb__section${notSelf}[data-id="${esc(id)}"]`
305 ) ) {
306 // Excludes self by data-uid .
307 const found = this.pages_container.querySelector( `.wpbc_bfb__panel--preview .wpbc_bfb__field[data-id="${esc( id )}"], .wpbc_bfb__panel--preview .wpbc_bfb__section[data-id="${esc( id )}"]` );
308 if ( found && currentEl && found === currentEl ) {
309 break;
310 }
311 id = `${base || 'field'}-${Math.random().toString( 36 ).slice( 2, 5 )}`;
312 }
313 return id;
314 }
315
316 /**
317 * Ensure a unique HTML name across the form.
318 *
319 * @param {string} base - Desired base name (un/sanitized).
320 * @param {HTMLElement|null} currentEl - If provided, ignore conflicts with this element.
321 * @returns {string} Unique name.
322 */
323 ensure_unique_field_name(base, currentEl = null) {
324 let name = base || 'field';
325 const esc = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
326 const escUid = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
327 // Exclude the current field *and any DOM mirrors of it* (same data-uid)
328 const uid = currentEl?.dataset?.uid;
329 const notSelf = uid ? `:not([data-uid="${escUid( uid )}"])` : '';
330 while ( true ) {
331 const selector = `.wpbc_bfb__panel--preview .wpbc_bfb__field${notSelf}[data-name="${esc( name )}"]`;
332 const clashes = this.pages_container?.querySelectorAll( selector ) || [];
333 if ( clashes.length === 0 ) break; // nobody else uses this name
334 const m = name.match( /-(\d+)$/ );
335 name = m ? name.replace( /-\d+$/, '-' + (Number( m[1] ) + 1) ) : `${base}-2`;
336 }
337 return name;
338 }
339
340 /**
341 * Set field's INTERNAL id (data-id) on an element. Ensures uniqueness and optionally asks caller to refresh
342 * preview.
343 *
344 * @param {HTMLElement} field_el - Field element in the canvas.
345 * @param {string} newIdRaw - Desired id (un/sanitized).
346 * @param {boolean} [renderPreview=false] - Caller can decide to re-render preview.
347 * @returns {string} Applied unique id.
348 */
349 set_field_id( field_el, newIdRaw, renderPreview = false ) {
350 const desired = Core.WPBC_BFB_Sanitize.sanitize_html_id( newIdRaw );
351 const unique = this.ensure_unique_field_id( desired, field_el );
352 field_el.setAttribute( 'data-id', unique );
353 if ( renderPreview ) {
354 // Caller decides if / when to render.
355 }
356 return unique;
357 }
358
359 /**
360 * Set field's REQUIRED HTML name (data-name). Ensures sanitized + unique per form.
361 * Falls back to sanitized internal id if user provides empty value.
362 *
363 * @param {HTMLElement} field_el - Field element in the canvas.
364 * @param {string} newNameRaw - Desired name (un/sanitized).
365 * @param {boolean} [renderPreview=false] - Caller can decide to re-render preview.
366 * @returns {string} Applied unique name.
367 */
368 set_field_name( field_el, newNameRaw, renderPreview = false ) {
369 const raw = (newNameRaw == null ? '' : String( newNameRaw )).trim();
370 const base = raw
371 ? Core.WPBC_BFB_Sanitize.sanitize_html_name( raw )
372 : Core.WPBC_BFB_Sanitize.sanitize_html_name( field_el.getAttribute( 'data-id' ) || 'field' );
373
374 const unique = this.ensure_unique_field_name( base, field_el );
375 field_el.setAttribute( 'data-name', unique );
376 if ( renderPreview ) {
377 // Caller decides if / when to render.
378 }
379 return unique;
380 }
381
382 /**
383 * Set field's OPTIONAL public HTML id (data-html_id). Empty value removes the attribute.
384 * Ensures sanitization + uniqueness among other declared HTML ids.
385 *
386 * @param {HTMLElement} field_el - Field element in the canvas.
387 * @param {string} newHtmlIdRaw - Desired html_id (optional).
388 * @param {boolean} [renderPreview=false] - Caller can decide to re-render preview.
389 * @returns {string} The applied html_id or empty string if removed.
390 */
391 set_field_html_id( field_el, newHtmlIdRaw, renderPreview = false ) {
392 const raw = (newHtmlIdRaw == null ? '' : String( newHtmlIdRaw )).trim();
393
394 if ( raw === '' ) {
395 field_el.removeAttribute( 'data-html_id' );
396 if ( renderPreview ) {
397 // Caller decides if / when to render.
398 }
399 return '';
400 }
401
402 const desired = Core.WPBC_BFB_Sanitize.sanitize_html_id( raw );
403 let htmlId = desired;
404 const esc = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
405 const escUid = (v) => Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( v );
406
407 while ( true ) {
408
409 const uid = field_el?.dataset?.uid;
410 const notSelf = uid ? `:not([data-uid="${escUid( uid )}"])` : '';
411
412 const clashInCanvas = this.pages_container?.querySelector(
413 `.wpbc_bfb__panel--preview .wpbc_bfb__field${notSelf}[data-html_id="${esc( htmlId )}"],` +
414 `.wpbc_bfb__panel--preview .wpbc_bfb__section${notSelf}[data-html_id="${esc( htmlId )}"]`
415 );
416 const domClash = document.getElementById( htmlId );
417
418 // Allow when the only "clash" is inside this same field (e.g., the input you just rendered)
419 const domClashIsSelf = domClash === field_el || (domClash && field_el.contains( domClash ));
420
421 if ( !clashInCanvas && (!domClash || domClashIsSelf) ) {
422 break;
423 }
424
425 const m = htmlId.match( /-(\d+)$/ );
426 htmlId = m ? htmlId.replace( /-\d+$/, '-' + (Number( m[1] ) + 1) ) : `${desired}-2`;
427 }
428
429 field_el.setAttribute( 'data-html_id', htmlId );
430 if ( renderPreview ) {
431 // Caller decides if / when to render.
432 }
433 return htmlId;
434 }
435 };
436
437 /**
438 * WPBC Layout service. Encapsulates column width math with gap handling, presets, and utilities.
439 */
440 Core.WPBC_BFB_LayoutService = class {
441
442 /**
443 * Constructor. Set options with gap between columns (%).
444 *
445 * @param {{ col_gap_percent?: number }} [opts] - Options with gap between columns (%).
446 */
447 constructor( opts = {} ) {
448 this.col_gap_percent = Number.isFinite( +opts.col_gap_percent ) ? +opts.col_gap_percent : 3;
449 }
450
451 /**
452 * Compute normalized flex-basis values for a row, respecting column gaps.
453 * Returns bases that sum to available = 100 - (n-1)*gap.
454 *
455 * @param {HTMLElement} row_el - Row element containing .wpbc_bfb__column children.
456 * @param {number} [gap_percent=this.col_gap_percent] - Gap percent between columns.
457 * @returns {{available:number,bases:number[]}} Available space and basis values.
458 */
459 compute_effective_bases_from_row( row_el, gap_percent = this.col_gap_percent ) {
460 const cols = Array.from( row_el?.querySelectorAll( ':scope > .wpbc_bfb__column' ) || [] );
461 const n = cols.length || 1;
462
463 const raw = cols.map( ( col ) => {
464 const w = col.style.flexBasis || '';
465 const p = Core.WPBC_BFB_Sanitize.parse_percent( w, NaN );
466 return Number.isFinite( p ) ? p : (100 / n);
467 } );
468
469 const sum_raw = raw.reduce( ( a, b ) => a + b, 0 ) || 100;
470 const gp = Number.isFinite( +gap_percent ) ? +gap_percent : 3;
471 const total_gaps = Math.max( 0, n - 1 ) * gp;
472 const available = Math.max( 0, 100 - total_gaps );
473 const scale = available / sum_raw;
474
475 return {
476 available,
477 bases: raw.map( ( p ) => Math.max( 0, p * scale ) )
478 };
479 }
480
481 /**
482 * Apply computed bases to the row's columns (sets flex-basis %).
483 *
484 * @param {HTMLElement} row_el - Row element.
485 * @param {number[]} bases - Array of basis values (percent of full 100).
486 * @returns {void}
487 */
488 apply_bases_to_row( row_el, bases ) {
489 const cols = Array.from( row_el?.querySelectorAll( ':scope > .wpbc_bfb__column' ) || [] );
490 cols.forEach( ( col, i ) => {
491 const p = bases[i] ?? 0;
492 col.style.flexBasis = `${p}%`;
493 } );
494 }
495
496 /**
497 * Distribute columns evenly, respecting gap.
498 *
499 * @param {HTMLElement} row_el - Row element.
500 * @param {number} [gap_percent=this.col_gap_percent] - Gap percent.
501 * @returns {void}
502 */
503 set_equal_bases( row_el, gap_percent = this.col_gap_percent ) {
504 const cols = Array.from( row_el?.querySelectorAll( ':scope > .wpbc_bfb__column' ) || [] );
505 const n = cols.length || 1;
506 const gp = Number.isFinite( +gap_percent ) ? +gap_percent : 3;
507 const total_gaps = Math.max( 0, n - 1 ) * gp;
508 const available = Math.max( 0, 100 - total_gaps );
509 const each = available / n;
510 this.apply_bases_to_row( row_el, Array( n ).fill( each ) );
511 }
512
513 /**
514 * Apply a preset of relative weights to a row/section.
515 *
516 * @param {HTMLElement} sectionOrRow - .wpbc_bfb__section or its child .wpbc_bfb__row.
517 * @param {number[]} weights - Relative weights (e.g., [1,3,1]).
518 * @param {number} [gap_percent=this.col_gap_percent] - Gap percent.
519 * @returns {void}
520 */
521 apply_layout_preset( sectionOrRow, weights, gap_percent = this.col_gap_percent ) {
522 const row = sectionOrRow?.classList?.contains( 'wpbc_bfb__row' )
523 ? sectionOrRow
524 : sectionOrRow?.querySelector( ':scope > .wpbc_bfb__row' );
525
526 if ( ! row ) {
527 return;
528 }
529
530 const cols = Array.from( row.querySelectorAll( ':scope > .wpbc_bfb__column' ) || [] );
531 const n = cols.length || 1;
532
533 if ( ! Array.isArray( weights ) || weights.length !== n ) {
534 this.set_equal_bases( row, gap_percent );
535 return;
536 }
537
538 const sum = weights.reduce( ( a, b ) => a + Math.max( 0, Number( b ) || 0 ), 0 ) || 1;
539 const gp = Number.isFinite( +gap_percent ) ? +gap_percent : 3;
540 const available = Math.max( 0, 100 - Math.max( 0, n - 1 ) * gp );
541 const bases = weights.map( ( w ) => Math.max( 0, (Number( w ) || 0) / sum * available ) );
542
543 this.apply_bases_to_row( row, bases );
544 }
545
546 /**
547 * Build preset weight lists for a given column count.
548 *
549 * @param {number} n - Column count.
550 * @returns {number[][]} List of weight arrays.
551 */
552 build_presets_for_columns( n ) {
553 switch ( n ) {
554 case 1:
555 return [ [ 1 ] ];
556 case 2:
557 return [ [ 1, 2 ], [ 2, 1 ], [ 1, 3 ], [ 3, 1 ] ];
558 case 3:
559 return [ [ 1, 3, 1 ], [ 1, 2, 1 ], [ 2, 1, 1 ], [ 1, 1, 2 ] ];
560 case 4:
561 return [ [ 1, 2, 2, 1 ], [ 2, 1, 1, 1 ], [ 1, 1, 1, 2 ] ];
562 default:
563 return [ Array( n ).fill( 1 ) ];
564 }
565 }
566
567 /**
568 * Format a human-readable label like "50%/25%/25%" from weights.
569 *
570 * @param {number[]} weights - Weight list.
571 * @returns {string} Label string.
572 */
573 format_preset_label( weights ) {
574 const sum = weights.reduce( ( a, b ) => a + (Number( b ) || 0), 0 ) || 1;
575 return weights.map( ( w ) => Math.round( ((Number( w ) || 0) / sum) * 100 ) ).join( '%/' ) + '%';
576 }
577
578 /**
579 * Parse comma/space separated weights into numbers.
580 *
581 * @param {string} input - User input like "20,60,20".
582 * @returns {number[]} Parsed weights.
583 */
584 parse_weights( input ) {
585 if ( ! input ) {
586 return [];
587 }
588 return String( input )
589 .replace( /[^\d,.\s]/g, '' )
590 .split( /[\s,]+/ )
591 .map( ( s ) => parseFloat( s ) )
592 .filter( ( n ) => Number.isFinite( n ) && n >= 0 );
593 }
594 };
595
596 /**
597 * WPBC Usage Limit service.
598 * Counts field usage by key, compares to palette limits, and updates palette UI.
599 */
600 Core.WPBC_BFB_UsageLimitService = class {
601
602 /**
603 * Constructor. Set pages_container and palette_ul.
604 *
605 * @param {HTMLElement} pages_container - Canvas root that holds placed fields.
606 * @param {HTMLElement[]|null} palette_uls?: Palettes UL with .wpbc_bfb__field items (may be null).
607 */
608 constructor(pages_container, palette_uls) {
609 this.pages_container = pages_container;
610 // Normalize to an array; we’ll still be robust if none provided.
611 this.palette_uls = Array.isArray( palette_uls ) ? palette_uls : (palette_uls ? [ palette_uls ] : []);
612 }
613
614
615 /**
616 * Parse usage limit from raw dataset value. Missing/invalid -> Infinity.
617 *
618 * @param {string|number|null|undefined} raw - Raw attribute value.
619 * @returns {number} Limit number or Infinity.
620 */
621 static parse_usage_limit( raw ) {
622 if ( raw == null ) {
623 return Infinity;
624 }
625 const n = parseInt( raw, 10 );
626 return Number.isFinite( n ) ? n : Infinity;
627 }
628
629 /**
630 * Count how many instances exist per usage_key in the canvas.
631 *
632 * @returns {Record<string, number>} Map of usage_key -> count.
633 */
634 count_usage_by_key() {
635 const used = {};
636 const all = this.pages_container?.querySelectorAll( '.wpbc_bfb__panel--preview .wpbc_bfb__field:not(.is-invalid)' ) || [];
637 all.forEach( ( el ) => {
638 const key = el.dataset.usage_key || el.dataset.type || el.dataset.id;
639 if ( ! key ) {
640 return;
641 }
642 used[key] = (used[key] || 0) + 1;
643 } );
644 return used;
645 }
646
647 /**
648 * Return palette limit for a given usage key (id of the palette item).
649 *
650 * @param {string} key - Usage key.
651 * @returns {number} Limit value or Infinity.
652 */
653 get_limit_for_key(key) {
654 if ( ! key ) {
655 return Infinity;
656 }
657 // Query across all palettes present now (stored + any newly added in DOM).
658 const roots = this.palette_uls?.length ? this.palette_uls : document.querySelectorAll( '.wpbc_bfb__panel_field_types__ul' );
659 const allPaletteFields = Array.from( roots ).flatMap( r => Array.from( r.querySelectorAll( '.wpbc_bfb__field' ) ) );
660 let limit = Infinity;
661
662 allPaletteFields.forEach( (el) => {
663 if ( el.dataset.id === key ) {
664 const n = Core.WPBC_BFB_UsageLimitService.parse_usage_limit( el.dataset.usagenumber );
665 // Choose the smallest finite limit (safest if palettes disagree).
666 if ( n < limit ) {
667 limit = n;
668 }
669 }
670 } );
671
672 return limit;
673 }
674
675
676 /**
677 * Disable/enable palette items based on current usage counts and limits.
678 *
679 * @returns {void}
680 */
681 update_palette_ui() {
682 // Always compute usage from the canvas:
683 const usage = this.count_usage_by_key();
684
685 // Update all palettes currently in DOM (not just the initially captured ones)
686 const palettes = document.querySelectorAll( '.wpbc_bfb__panel_field_types__ul' );
687
688 palettes.forEach( (pal) => {
689 pal.querySelectorAll( '.wpbc_bfb__field' ).forEach( (panel_field) => {
690 const paletteId = panel_field.dataset.id;
691 const raw_limit = panel_field.dataset.usagenumber;
692 const perElLimit = Core.WPBC_BFB_UsageLimitService.parse_usage_limit( raw_limit );
693 // Effective limit across all palettes is the global limit for this key.
694 const globalLimit = this.get_limit_for_key( paletteId );
695 const limit = Number.isFinite( globalLimit ) ? globalLimit : perElLimit; // prefer global min
696
697 const current = usage[paletteId] || 0;
698 const disable = Number.isFinite( limit ) && current >= limit;
699
700 panel_field.style.pointerEvents = disable ? 'none' : '';
701 panel_field.style.opacity = disable ? '0.4' : '';
702 panel_field.setAttribute( 'aria-disabled', disable ? 'true' : 'false' );
703 if ( disable ) {
704 panel_field.setAttribute( 'tabindex', '-1' );
705 } else {
706 panel_field.removeAttribute( 'tabindex' );
707 }
708 } );
709 } );
710 }
711
712
713 /**
714 * Return how many valid instances with this usage key exist in the canvas.
715 *
716 * @param {string} key - Usage key of a palette item.
717 * @returns {number} Count of existing non-invalid instances.
718 */
719 count_for_key( key ) {
720 if ( ! key ) {
721 return 0;
722 }
723 return ( this.pages_container?.querySelectorAll(
724 `.wpbc_bfb__panel--preview .wpbc_bfb__field[data-usage_key="${Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( key )}"]:not(.is-invalid),
725 .wpbc_bfb__panel--preview .wpbc_bfb__field[data-type="${Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( key )}"]:not(.is-invalid)`
726 ) || [] ).length;
727 }
728
729 /**
730 * Alias for limit lookup (readability).
731 *
732 * @param {string} key - Usage key of a palette item.
733 * @returns {number} Limit value or Infinity.
734 */
735 limit_for_key( key ) {
736 return this.get_limit_for_key( key );
737 }
738
739 /**
740 * Remaining slots for this key (Infinity if unlimited).
741 *
742 * @param {string} key - Usage key of a palette item.
743 * @returns {number} Remaining count (>= 0) or Infinity.
744 */
745 remaining_for_key( key ) {
746 const limit = this.limit_for_key( key );
747 if ( limit === Infinity ) {
748 return Infinity;
749 }
750 const used = this.count_for_key( key );
751 return Math.max( 0, limit - used );
752 }
753
754 /**
755 * True if you can add `delta` more items for this key.
756 *
757 * @param {string} key - Usage key of a palette item.
758 * @param {number} [delta=1] - How many items you intend to add.
759 * @returns {boolean} Whether adding is allowed.
760 */
761 can_add( key, delta = 1 ) {
762 const rem = this.remaining_for_key( key );
763 return ( rem === Infinity ) ? true : ( rem >= delta );
764 }
765
766 /**
767 * UI-facing gate: alert when exceeded. Returns boolean allowed/blocked.
768 *
769 * @param {string} key - Usage key of a palette item.
770 * @param {{label?: string, delta?: number}} [opts={}] - Optional UI info.
771 * @returns {boolean} True if allowed, false if blocked.
772 */
773 gate_or_alert( key, { label = key, delta = 1 } = {} ) {
774 if ( this.can_add( key, delta ) ) {
775 return true;
776 }
777 const limit = this.limit_for_key( key );
778 alert( `Only ${limit} instance${limit > 1 ? 's' : ''} of "${label}" allowed.` );
779 return false;
780 }
781
782 /**
783 * Backward-compatible alias used elsewhere in the codebase. - Check whether another instance with the given
784 * usage key can be added.
785 *
786 * @param {string} key - Usage key of a palette item.
787 * @returns {boolean} Whether adding one more is allowed.
788 */
789 is_usage_ok( key ) {
790 return this.can_add( key, 1 );
791 }
792
793 };
794
795 /**
796 * Constant event names for the builder.
797 */
798 Core.WPBC_BFB_Events = Object.freeze({
799 SELECT : 'wpbc:bfb:select',
800 CLEAR_SELECTION : 'wpbc:bfb:clear-selection',
801 FIELD_ADD : 'wpbc:bfb:field:add',
802 FIELD_REMOVE : 'wpbc:bfb:field:remove',
803 STRUCTURE_CHANGE : 'wpbc:bfb:structure:change',
804 STRUCTURE_LOADED : 'wpbc:bfb:structure:loaded'
805 });
806
807 /**
808 * Lightweight event bus that emits to both the pages container and document.
809 */
810 Core.WPBC_BFB_EventBus = class {
811 /**
812 * @param {HTMLElement} scope_el - Element to dispatch bubbled events from.
813 */
814 constructor( scope_el ) {
815 this.scope_el = scope_el;
816 }
817
818 /**
819 * Emit a DOM CustomEvent with payload.
820 *
821 * @param {string} type - Event type (use Core.WPBC_BFB_Events. when possible).
822 * @param {Object} [detail={}] - Arbitrary serializable payload.
823 * @returns {void}
824 */
825 emit( type, detail = {} ) {
826 if ( ! this.scope_el ) {
827 return;
828 }
829 this.scope_el.dispatchEvent( new CustomEvent( type, { detail: { ...detail }, bubbles: true } ) );
830 }
831
832 /**
833 * Subscribe to an event on document.
834 *
835 * @param {string} type - Event type.
836 * @param {(ev:CustomEvent)=>void} handler - Handler function.
837 * @returns {void}
838 */
839 on( type, handler ) {
840 document.addEventListener( type, handler );
841 }
842
843 /**
844 * Unsubscribe from an event on document.
845 *
846 * @param {string} type - Event type.
847 * @param {(ev:CustomEvent)=>void} handler - Handler function.
848 * @returns {void}
849 */
850 off( type, handler ) {
851 document.removeEventListener( type, handler );
852 }
853 };
854
855 /**
856 * SortableJS manager: single point for consistent DnD config.
857 */
858 Core.WPBC_BFB_SortableManager = class {
859
860 /**
861 * @param {WPBC_Form_Builder} builder - The active builder instance.
862 * @param {{ groupName?: string, animation?: number, ghostClass?: string, chosenClass?: string, dragClass?:
863 * string }} [opts={}] - Visual/behavior options.
864 */
865 constructor( builder, opts = {} ) {
866 this.builder = builder;
867 const gid = this.builder?.instance_id || Math.random().toString( 36 ).slice( 2, 8 );
868 this.opts = {
869 // groupName : 'form',
870 groupName: `form-${gid}`,
871 animation : 150,
872 ghostClass : 'wpbc_bfb__drag-ghost',
873 chosenClass: 'wpbc_bfb__highlight',
874 dragClass : 'wpbc_bfb__drag-active',
875 ...opts
876 };
877 /** @type {Set<HTMLElement>} */
878 this._containers = new Set();
879
880 /**
881 * Guard against lost mouseup / pointerup events.
882 *
883 * @type {boolean}
884 */
885 this._drag_fail_safe_bound = false;
886
887 this._bind_drag_fail_safe();
888 }
889
890 /**
891 * Cleanup drag UI state.
892 *
893 * This is a defensive cleanup for cases when Chrome or a browser extension
894 * loses the final mouseup / pointerup event during fallback dragging.
895 *
896 * @returns {void}
897 */
898 _cleanup_drag_ui() {
899 this._dragState = null;
900 this._toggle_dnd_root_flags( false );
901 this.builder?._remove_dragging_class?.();
902
903 // Remove only fallback mirrors. Do not touch real dragged elements.
904 document.querySelectorAll( '.sortable-fallback[data-drag-role], .wpbc_bfb__simple_list_fallback' )
905 .forEach( (el) => {
906 if ( el.parentNode ) {
907 el.parentNode.removeChild( el );
908 }
909 } );
910 }
911
912 /**
913 * Bind global fail-safe listeners for drag cleanup.
914 *
915 * @returns {void}
916 */
917 _bind_drag_fail_safe() {
918 if ( this._drag_fail_safe_bound ) {
919 return;
920 }
921
922 this._drag_fail_safe_bound = true;
923
924 const finish_drag = () => {
925 window.requestAnimationFrame( () => {
926 this._cleanup_drag_ui();
927 } );
928 };
929
930 [ 'mouseup', 'pointerup', 'touchend', 'dragend' ].forEach( (evt_name) => {
931 document.addEventListener( evt_name, finish_drag, true );
932 } );
933
934 window.addEventListener( 'blur', finish_drag, true );
935
936 document.addEventListener(
937 'visibilitychange',
938 () => {
939 if ( document.hidden ) {
940 finish_drag();
941 }
942 },
943 true
944 );
945 }
946
947 /**
948 * Tag the drag mirror (element under cursor) with role: 'palette' | 'canvas'.
949 * Works with Sortable's fallback mirror (.sortable-fallback / .sortable-drag) and with your dragClass
950 * (.wpbc_bfb__drag-active).
951 */
952 _tag_drag_mirror( evt ) {
953 const fromPalette = this.builder?.palette_uls?.includes?.( evt.from );
954 const role = fromPalette ? 'palette' : 'canvas';
955 // Wait a tick so the mirror exists. - The window.requestAnimationFrame() method tells the browser you wish to perform an animation.
956 requestAnimationFrame( () => {
957 const mirror = document.querySelector( '.sortable-fallback, .sortable-drag, .' + this.opts.dragClass );
958 if ( mirror ) {
959 mirror.setAttribute( 'data-drag-role', role );
960 }
961 } );
962 }
963
964 _toggle_dnd_root_flags( active, from_palette = false ) {
965
966 // set to root element of an HTML document, which is the <html>.
967 const root = document.documentElement;
968 if ( active ) {
969 root.classList.add( 'wpbc_bfb__dnd-active' );
970 if ( from_palette ) {
971 root.classList.add( 'wpbc_bfb__drag-from-palette' );
972 }
973 } else {
974 root.classList.remove( 'wpbc_bfb__dnd-active', 'wpbc_bfb__drag-from-palette' );
975 }
976 }
977
978
979 /**
980 * Ensure a simple vertical sortable list.
981 *
982 * This configuration is intended for inspector/sidebar lists such as:
983 * - dropdown choices
984 * - radio options
985 * - checkbox options
986 *
987 * It is intentionally much simpler than the canvas DnD config and does not
988 * use the column edge-fence / sticky-target logic.
989 *
990 * @param {HTMLElement} container - Sortable list container.
991 * @param {{ handle_selector?: string, draggable_selector?: string, onUpdate?: Function }} [handlers={}] -
992 * Optional handlers/selectors.
993 * @returns {void}
994 */
995 ensure_simple_list( container, handlers = {} ) {
996 if ( ! container || typeof Sortable === 'undefined' ) {
997 return;
998 }
999 if ( Sortable.get?.( container ) ) {
1000 return;
1001 }
1002
1003 const common = {
1004 animation : this.opts.animation,
1005 ghostClass : this.opts.ghostClass,
1006 chosenClass : this.opts.chosenClass,
1007 dragClass : this.opts.dragClass,
1008 forceFallback : true,
1009 // For a single scrollable sidebar list this is usually more stable.
1010 fallbackOnBody : false,
1011 fallbackTolerance: 8,
1012 removeCloneOnHide: true,
1013 onStart : () => {
1014 this.builder?._add_dragging_class?.();
1015 this._toggle_dnd_root_flags( true, false );
1016 },
1017 onEnd : () => {
1018 setTimeout( () => {
1019 this.builder?._remove_dragging_class?.();
1020 }, 50 );
1021 this._toggle_dnd_root_flags( false );
1022 this._dragState = null;
1023 this._cleanup_drag_ui();
1024 }
1025 };
1026
1027 Sortable.create(
1028 container,
1029 {
1030 ...common,
1031 group : { name: this.opts.groupName, pull: false, put: false },
1032 sort : true,
1033 direction : 'vertical',
1034 handle : handlers.handle_selector || '.wpbc_bfb__drag-handle',
1035 draggable : handlers.draggable_selector || '.wpbc_bfb__sortable-row',
1036 fallbackClass : 'wpbc_bfb__simple_list_fallback',
1037 filter : [
1038 'input',
1039 'textarea',
1040 'select',
1041 'button',
1042 'a',
1043 '.wpbc_bfb__no-drag-zone',
1044 '.wpbc_bfb__no-drag-zone *'
1045 ].join( ',' ),
1046 preventOnFilter : false,
1047 invertSwap : false,
1048 swapThreshold : 0.30,
1049 invertedSwapThreshold : 0.60,
1050 emptyInsertThreshold : 8,
1051 dragoverBubble : false,
1052 scroll : true,
1053 scrollSensitivity : 60,
1054 scrollSpeed : 14,
1055 onUpdate : handlers.onUpdate || function () {}
1056 }
1057 );
1058
1059 this._containers.add( container );
1060 }
1061
1062
1063 /**
1064 * Ensure Sortable is attached to a container with role 'palette' or 'canvas'.
1065 *
1066 * -- Handle selectors: handle: '.section-drag-handle, .wpbc_bfb__drag-handle, .wpbc_bfb__drag-anywhere,
1067 * [data-draggable="true"]'
1068 * -- Draggable gate: draggable: '.wpbc_bfb__field:not([data-draggable="false"]), .wpbc_bfb__section'
1069 * -- Filter (overlay-safe): ignore everything in overlay except the handle -
1070 * '.wpbc_bfb__overlay-controls
1071 * *:not(.wpbc_bfb__drag-handle):not(.section-drag-handle):not(.wpbc_icn_drag_indicator)'
1072 * -- No-drag wrapper: use .wpbc_bfb__no-drag-zone inside renderers for inputs/widgets.
1073 * -- Focus guard (optional): flip [data-draggable] on focusin/focusout to prevent accidental drags while
1074 * typing.
1075 *
1076 * @param {HTMLElement} container - The element to enhance with Sortable.
1077 * @param {'palette'|'canvas'} role - Behavior profile to apply.
1078 * @param {{ onAdd?: Function }} [handlers={}] - Optional handlers.
1079 * @returns {void}
1080 */
1081 ensure( container, role, handlers = {} ) {
1082 if ( ! container || typeof Sortable === 'undefined' ) {
1083 return;
1084 }
1085 if ( Sortable.get?.( container ) ) {
1086 return;
1087 }
1088
1089 const sortable_kind = handlers.sortable_kind || '';
1090
1091 if ( sortable_kind === 'simple_list' ) {
1092 this.ensure_simple_list( container, handlers );
1093 return;
1094 }
1095
1096 const common = {
1097 animation : this.opts.animation,
1098 ghostClass : this.opts.ghostClass,
1099 chosenClass: this.opts.chosenClass,
1100 dragClass : this.opts.dragClass,
1101 // == Element under the cursor == Ensure we drag a real DOM mirror you can style via CSS (cross-browser).
1102 forceFallback : true,
1103 fallbackOnBody : true,
1104 fallbackTolerance: 8,
1105 removeCloneOnHide: true,
1106 // Add body/html flags so you can style differently when dragging from palette.
1107 onStart: (evt) => {
1108 this.builder?._add_dragging_class?.();
1109
1110 const fromPalette = this.builder?.palette_uls?.includes?.( evt.from );
1111 this._toggle_dnd_root_flags( true, fromPalette ); // set to root HTML document: html.wpbc_bfb__dnd-active.wpbc_bfb__drag-from-palette .
1112
1113 this._tag_drag_mirror( evt ); // Add 'data-drag-role' attribute to element under cursor.
1114 },
1115 onEnd : () => {
1116 setTimeout( () => { this.builder._remove_dragging_class(); }, 50 );
1117 this._toggle_dnd_root_flags( false );
1118 }
1119 };
1120
1121 if ( role === 'palette' ) {
1122 Sortable.create( container, {
1123 ...common,
1124 group : { name: this.opts.groupName, pull: 'clone', put: false },
1125 sort : false
1126 } );
1127 this._containers.add( container );
1128 return;
1129 }
1130
1131 // role === 'canvas'.
1132 Sortable.create( container, {
1133 ...common,
1134 group : {
1135 name: this.opts.groupName,
1136 pull: true,
1137 put : (to, from, draggedEl) => {
1138 return draggedEl.classList.contains( 'wpbc_bfb__field' ) ||
1139 draggedEl.classList.contains( 'wpbc_bfb__section' );
1140 }
1141 },
1142 // ---------- DnD Handlers -------------- // Grab anywhere on fields that opt-in with the class or attribute. - Sections still require their dedicated handle.
1143 handle : '.section-drag-handle, .wpbc_bfb__drag-handle, .wpbc_bfb__drag-anywhere, [data-draggable="true"]',
1144 draggable: '.wpbc_bfb__field:not([data-draggable="false"]), .wpbc_bfb__section', // Per-field opt-out with [data-draggable="false"] (e.g., while editing).
1145 // ---------- Filters - No DnD ---------- // Declarative “no-drag zones”: anything inside these wrappers won’t start a drag.
1146 filter: [
1147 '.wpbc_bfb__no-drag-zone',
1148 '.wpbc_bfb__no-drag-zone *',
1149 '.wpbc_bfb__column-resizer', // Ignore the resizer rails during DnD (prevents edge “snap”).
1150 // In the overlay toolbar, block everything EXCEPT the drag handle (and its icon).
1151 '.wpbc_bfb__overlay-controls *:not(.wpbc_bfb__drag-handle):not(.section-drag-handle):not(.wpbc_icn_drag_indicator)'
1152 ].join( ',' ),
1153 preventOnFilter : false,
1154 // ---------- anti-jitter tuning ----------
1155 direction : 'vertical', // columns are vertical lists.
1156 invertSwap : true, // use swap on inverted overlap.
1157 swapThreshold : 0.65, // be less eager to swap.
1158 invertedSwapThreshold: 0.85, // require deeper overlap when inverted.
1159 emptyInsertThreshold : 24, // don’t jump into empty containers too early.
1160 dragoverBubble : false, // keep dragover local.
1161 scroll : true,
1162 scrollSensitivity : 40,
1163 scrollSpeed : 10,
1164 /**
1165 * Enter/leave hysteresis for cross-column moves. Only allow dropping into `to` when the pointer is
1166 * well inside it.
1167 */
1168 onMove: (evt, originalEvent) => {
1169
1170 const { to, from } = evt;
1171 if ( ! to || ! from ) {
1172 return true;
1173 }
1174
1175 const in_preview_canvas = !! to.closest( '.wpbc_bfb__panel--preview' );
1176 if ( ! in_preview_canvas ) {
1177 return true;
1178 }
1179
1180 // Only gate columns (not page containers), and only for cross-column moves in the same row
1181 const isColumn = to.classList?.contains( 'wpbc_bfb__column' );
1182 if ( !isColumn ) return true;
1183
1184 const fromRow = from.closest( '.wpbc_bfb__row' );
1185 const toRow = to.closest( '.wpbc_bfb__row' );
1186 if ( fromRow && toRow && fromRow !== toRow ) return true;
1187
1188 const rect = to.getBoundingClientRect();
1189 const evtX = (originalEvent.touches?.[0]?.clientX) ?? originalEvent.clientX;
1190 const evtY = (originalEvent.touches?.[0]?.clientY) ?? originalEvent.clientY;
1191
1192 // --- Edge fence (like you had), but clamped for tiny columns
1193 const paddingX = Core.WPBC_BFB_Sanitize.clamp( rect.width * 0.20, 12, 36 );
1194 const paddingY = Core.WPBC_BFB_Sanitize.clamp( rect.height * 0.10, 6, 16 );
1195
1196 // Looser Y if the column is visually tiny/empty
1197 const isVisuallyEmpty = to.childElementCount === 0 || rect.height < 64;
1198 const innerTop = rect.top + (isVisuallyEmpty ? 4 : paddingY);
1199 const innerBottom = rect.bottom - (isVisuallyEmpty ? 4 : paddingY);
1200 const innerLeft = rect.left + paddingX;
1201 const innerRight = rect.right - paddingX;
1202
1203 const insideX = evtX > innerLeft && evtX < innerRight;
1204 const insideY = evtY > innerTop && evtY < innerBottom;
1205 if ( !(insideX && insideY) ) return false; // stay in current column until well inside new one
1206
1207 // --- Sticky target commit distance: only switch if we’re clearly inside the new column
1208 const ds = this._dragState;
1209 if ( ds ) {
1210 if ( ds.stickyTo && ds.stickyTo !== to ) {
1211 // require a deeper penetration to switch columns
1212 const commitX = Core.WPBC_BFB_Sanitize.clamp( rect.width * 0.25, 18, 40 ); // 25% or 18–40px
1213 const commitY = Core.WPBC_BFB_Sanitize.clamp( rect.height * 0.15, 10, 28 ); // 15% or 10–28px
1214
1215 const deepInside =
1216 (evtX > rect.left + commitX && evtX < rect.right - commitX) &&
1217 (evtY > rect.top + commitY && evtY < rect.bottom - commitY);
1218
1219 if ( !deepInside ) return false;
1220 }
1221 // We accept the new target now.
1222 ds.stickyTo = to;
1223 ds.lastSwitchTs = performance.now();
1224 }
1225
1226 return true;
1227 },
1228 onStart: (evt) => {
1229 this.builder?._add_dragging_class?.();
1230 // Match the flags we set in common so CSS stays consistent on canvas drags too.
1231 const fromPalette = this.builder?.palette_uls?.includes?.( evt.from );
1232 this._toggle_dnd_root_flags( true, fromPalette ); // set to root HTML document: html.wpbc_bfb__dnd-active.wpbc_bfb__drag-from-palette .
1233 this._tag_drag_mirror( evt ); // Tag the mirror under cursor.
1234 this._dragState = { stickyTo: null, lastSwitchTs: 0 }; // per-drag state.
1235 },
1236 onEnd : () => {
1237 setTimeout( () => { this.builder._remove_dragging_class(); }, 50 );
1238 this._toggle_dnd_root_flags( false ); // set to root HTML document without these classes: html.wpbc_bfb__dnd-active.wpbc_bfb__drag-from-palette .
1239 this._dragState = null;
1240 },
1241 // ----------------------------------------
1242 // onAdd: handlers.onAdd || this.builder.handle_on_add.bind( this.builder )
1243 onAdd: (evt) => {
1244 if ( this._on_add_section( evt ) ) {
1245 return;
1246 }
1247 // Fallback: original handler for normal fields.
1248 (handlers.onAdd || this.builder.handle_on_add.bind( this.builder ))( evt );
1249 },
1250 onUpdate: () => {
1251 this.builder.bus?.emit?.( Core.WPBC_BFB_Events.STRUCTURE_CHANGE, { reason: 'sort-update' } );
1252 }
1253 } );
1254
1255 this._containers.add( container );
1256 }
1257
1258 /**
1259 * Handle adding/moving sections via Sortable onAdd.
1260 * Returns true if handled (i.e., it was a section), false to let the default field handler run.
1261 *
1262 * - Palette -> canvas: remove the placeholder clone and build a fresh section via add_section()
1263 * - Canvas -> canvas: keep the moved DOM (and its children), just re-wire overlays/sortables/metadata
1264 *
1265 * @param {Sortable.SortableEvent} evt
1266 * @returns {boolean}
1267 */
1268 _on_add_section(evt) {
1269
1270 const item = evt.item;
1271 if ( ! item ) {
1272 return false;
1273 }
1274
1275 // Identify sections both from palette items (li clones) and real canvas nodes.
1276 const data = Core.WPBC_Form_Builder_Helper.get_all_data_attributes( item );
1277 const isSection = item.classList.contains( 'wpbc_bfb__section' ) || (data?.type || item.dataset?.type) === 'section';
1278
1279 if ( ! isSection ) {
1280 return false;
1281 }
1282
1283 const fromPalette = this.builder?.palette_uls?.includes?.( evt.from ) === true;
1284
1285 if ( ! fromPalette ) {
1286 // Canvas -> canvas move: DO NOT rebuild/remove; preserve children.
1287 this.builder.add_overlay_toolbar?.( item ); // ensure overlay exists
1288 this.builder.pages_sections?.init_all_nested_sortables?.( item ); // ensure inner sortables
1289
1290 // Ensure metadata present/updated
1291 item.dataset.type = 'section';
1292 const cols = item.querySelectorAll( ':scope > .wpbc_bfb__row > .wpbc_bfb__column' ).length || 1;
1293 item.dataset.columns = String( cols );
1294
1295 // Select & notify subscribers (layout/min guards, etc.)
1296 this.builder.select_field?.( item );
1297 this.builder.bus?.emit?.( Core.WPBC_BFB_Events.STRUCTURE_CHANGE, { el: item, reason: 'section-move' } );
1298 this.builder.usage?.update_palette_ui?.();
1299 return true; // handled.
1300 }
1301
1302 // Palette -> canvas: build a brand-new section using the same path as the dropdown/menu
1303 const to = evt.to?.closest?.( '.wpbc_bfb__column, .wpbc_bfb__form_preview_section_container' ) || evt.to;
1304 const cols = parseInt( data?.columns || item.dataset.columns || 1, 10 ) || 1;
1305
1306 // Remove the palette clone placeholder.
1307 item.parentNode && item.parentNode.removeChild( item );
1308
1309 // Create the real section.
1310 this.builder.pages_sections.add_section( to, cols );
1311
1312 // Insert at the precise drop index.
1313 const section = to.lastElementChild; // add_section appends to end.
1314 if ( evt.newIndex != null && evt.newIndex < to.children.length - 1 ) {
1315 const ref = to.children[evt.newIndex] || null;
1316 to.insertBefore( section, ref );
1317 }
1318
1319 // Finalize: overlay, selection, events, usage refresh.
1320 this.builder.add_overlay_toolbar?.( section );
1321 this.builder.select_field?.( section );
1322 this.builder.bus?.emit?.( Core.WPBC_BFB_Events.FIELD_ADD, {
1323 el : section,
1324 id : section.dataset.id,
1325 uid: section.dataset.uid
1326 } );
1327 this.builder.usage?.update_palette_ui?.();
1328
1329 return true;
1330 }
1331
1332 /**
1333 * Destroy all Sortable instances created by this manager.
1334 *
1335 * @returns {void}
1336 */
1337 destroyAll() {
1338 this._containers.forEach( ( el ) => {
1339 const inst = Sortable.get?.( el );
1340 if ( inst ) {
1341 inst.destroy();
1342 }
1343 } );
1344 this._containers.clear();
1345 }
1346 };
1347
1348 /**
1349 * Small DOM contract and renderer helper
1350 *
1351 * @type {Readonly<{
1352 * SELECTORS: {pagePanel: string, field: string, validField: string, section: string, column:
1353 * string, row: string, overlay: string}, CLASSES: {selected: string}, ATTR: {id: string, name: string, htmlId:
1354 * string, usageKey: string, uid: string}}
1355 * >}
1356 */
1357 Core.WPBC_BFB_DOM = Object.freeze( {
1358 SELECTORS: {
1359 pagePanel : '.wpbc_bfb__panel--preview',
1360 field : '.wpbc_bfb__field',
1361 validField: '.wpbc_bfb__field:not(.is-invalid)',
1362 section : '.wpbc_bfb__section',
1363 column : '.wpbc_bfb__column',
1364 row : '.wpbc_bfb__row',
1365 overlay : '.wpbc_bfb__overlay-controls'
1366 },
1367 CLASSES : {
1368 selected: 'is-selected'
1369 },
1370 ATTR : {
1371 id : 'data-id',
1372 name : 'data-name',
1373 htmlId : 'data-html_id',
1374 usageKey: 'data-usage_key',
1375 uid : 'data-uid'
1376 }
1377 } );
1378
1379 Core.WPBC_Form_Builder_Helper = class {
1380
1381 /**
1382 * Create an HTML element.
1383 *
1384 * @param {string} tag - HTML tag name.
1385 * @param {string} [class_name=''] - Optional CSS class name.
1386 * @param {string} [inner_html=''] - Optional innerHTML.
1387 * @returns {HTMLElement} Created element.
1388 */
1389 static create_element( tag, class_name = '', inner_html = '' ) {
1390 const el = document.createElement( tag );
1391 if ( class_name ) {
1392 el.className = class_name;
1393 }
1394 if ( inner_html ) {
1395 el.innerHTML = inner_html;
1396 }
1397 return el;
1398 }
1399
1400 /**
1401 * Set multiple `data-*` attributes on a given element.
1402 *
1403 * @param {HTMLElement} el - Target element.
1404 * @param {Object} data_obj - Key-value pairs for data attributes.
1405 * @returns {void}
1406 */
1407 static set_data_attributes( el, data_obj ) {
1408 Object.entries( data_obj ).forEach( ( [ key, val ] ) => {
1409 // Previously: 2025-09-01 17:09:
1410 // const value = (typeof val === 'object') ? JSON.stringify( val ) : val;
1411 //New:
1412 let value;
1413 if ( typeof val === 'object' && val !== null ) {
1414 try {
1415 value = JSON.stringify( val );
1416 } catch {
1417 value = '';
1418 }
1419 } else {
1420 value = val;
1421 }
1422
1423 el.setAttribute( 'data-' + key, value );
1424 } );
1425 }
1426
1427 /**
1428 * Get all `data-*` attributes from an element and parse JSON where possible.
1429 *
1430 * @param {HTMLElement} el - Element to extract data from.
1431 * @returns {Object} Parsed key-value map of data attributes.
1432 */
1433 static get_all_data_attributes( el ) {
1434 const data = {};
1435
1436 if ( ! el || ! el.attributes ) {
1437 return data;
1438 }
1439
1440 Array.from( el.attributes ).forEach(
1441 ( attr ) => {
1442 if ( attr.name.startsWith( 'data-' ) ) {
1443 const key = attr.name.replace( /^data-/, '' );
1444 try {
1445 data[key] = JSON.parse( attr.value );
1446 } catch ( e ) {
1447 data[key] = attr.value;
1448 }
1449 }
1450 }
1451 );
1452
1453 // Only default the label if it's truly absent (undefined/null), not when it's an empty string.
1454 const hasExplicitLabel = Object.prototype.hasOwnProperty.call( data, 'label' );
1455 if ( ! hasExplicitLabel && data.id ) {
1456 data.label = data.id.charAt( 0 ).toUpperCase() + data.id.slice( 1 );
1457 }
1458
1459 return data;
1460 }
1461
1462 /**
1463 * Render a simple label + type preview (used for unknown or fallback fields).
1464 *
1465 * @param {Object} field_data - Field data object.
1466 * @returns {string} HTML content.
1467 */
1468 static render_field_inner_html( field_data ) {
1469 // Make the fallback preview respect an empty label.
1470 const hasLabel = Object.prototype.hasOwnProperty.call( field_data, 'label' );
1471 const label = hasLabel ? String( field_data.label ) : String( field_data.id || '(no label)' );
1472
1473 const type = String( field_data.type || 'unknown' );
1474 const is_required = field_data.required === true || field_data.required === 'true' || field_data.required === 1 || field_data.required === '1';
1475
1476 const wrapper = document.createElement( 'div' );
1477
1478 const spanLabel = document.createElement( 'span' );
1479 spanLabel.className = 'wpbc_bfb__field-label';
1480 spanLabel.textContent = label + (is_required ? ' *' : '');
1481 wrapper.appendChild( spanLabel );
1482
1483 const spanType = document.createElement( 'span' );
1484 spanType.className = 'wpbc_bfb__field-type';
1485 spanType.textContent = type;
1486 wrapper.appendChild( spanType );
1487
1488 return wrapper.innerHTML;
1489 }
1490
1491 /**
1492 * Debounce a function.
1493 *
1494 * @param {Function} fn - Function to debounce.
1495 * @param {number} wait - Delay in ms.
1496 * @returns {Function} Debounced function.
1497 */
1498 static debounce( fn, wait = 120 ) {
1499 let t = null;
1500 return function debounced( ...args ) {
1501 if ( t ) {
1502 clearTimeout( t );
1503 }
1504 t = setTimeout( () => fn.apply( this, args ), wait );
1505 };
1506 }
1507
1508 };
1509
1510 // Renderer registry. Allows late registration and avoids tight coupling to a global map.
1511 Core.WPBC_BFB_Field_Renderer_Registry = (function () {
1512 const map = new Map();
1513 return {
1514 register( type, ClassRef ) {
1515 map.set( String( type ), ClassRef );
1516 },
1517 get( type ) {
1518 return map.get( String( type ) );
1519 }
1520 };
1521 })();
1522
1523 }( window ));
1524 // ---------------------------------------------------------------------------------------------------------------------
1525 // == File /includes/page-form-builder/_out/core/bfb-fields.js == | 2025-09-10 15:47
1526 // ---------------------------------------------------------------------------------------------------------------------
1527 (function ( w ) {
1528 'use strict';
1529
1530 // Single global namespace (idempotent & load-order safe).
1531 const Core = ( w.WPBC_BFB_Core = w.WPBC_BFB_Core || {} );
1532 const UI = ( Core.UI = Core.UI || {} );
1533
1534 /**
1535 * Base class for field renderers (static-only contract).
1536 * ================================================================================================================
1537 * Contract exposed to the builder (static methods on the CLASS itself):
1538 * - render(el, data, ctx) // REQUIRED
1539 * - on_field_drop(data, el, meta) // OPTIONAL (default provided)
1540 *
1541 * Helpers for subclasses:
1542 * - get_defaults() -> per-field defaults (MUST override in subclass to set type/label)
1543 * - normalize_data(d) -> shallow merge with defaults
1544 * - get_template(id) -> per-id cached wp.template compiler
1545 *
1546 * Subclass usage:
1547 * class WPBC_BFB_Field_Text extends Core.WPBC_BFB_Field_Base { static get_defaults(){ ... } }
1548 * WPBC_BFB_Field_Text.template_id = 'wpbc-bfb-field-text';
1549 * ================================================================================================================
1550 */
1551 Core.WPBC_BFB_Field_Base = class {
1552
1553 /**
1554 * Default field data (generic baseline).
1555 * Subclasses MUST override to provide { type, label } appropriate for the field.
1556 * @returns {Object}
1557 */
1558 static get_defaults() {
1559 return {
1560 type : 'field',
1561 label : 'Field',
1562 name : 'field',
1563 html_id : '',
1564 placeholder : '',
1565 required : false,
1566 minlength : '',
1567 maxlength : '',
1568 pattern : '',
1569 cssclass : '',
1570 help : ''
1571 };
1572 }
1573
1574 /**
1575 * Shallow-merge incoming data with defaults.
1576 * @param {Object} data
1577 * @returns {Object}
1578 */
1579 static normalize_data( data ) {
1580 var d = data || {};
1581 var defaults = this.get_defaults();
1582 var out = {};
1583 var k;
1584
1585 for ( k in defaults ) {
1586 if ( Object.prototype.hasOwnProperty.call( defaults, k ) ) {
1587 out[k] = defaults[k];
1588 }
1589 }
1590 for ( k in d ) {
1591 if ( Object.prototype.hasOwnProperty.call( d, k ) ) {
1592 out[k] = d[k];
1593 }
1594 }
1595 return out;
1596 }
1597
1598 /**
1599 * Compile and cache a wp.template by id (per-id cache).
1600 * @param {string} template_id
1601 * @returns {Function|null}
1602 */
1603 static get_template(template_id) {
1604
1605 // Accept either "wpbc-bfb-field-text" or "tmpl-wpbc-bfb-field-text".
1606 if ( ! template_id || ! window.wp || ! wp.template ) {
1607 return null;
1608 }
1609 const domId = template_id.startsWith( 'tmpl-' ) ? template_id : ('tmpl-' + template_id);
1610 if ( ! document.getElementById( domId ) ) {
1611 return null;
1612 }
1613
1614 if ( ! Core.__bfb_tpl_cache_map ) {
1615 Core.__bfb_tpl_cache_map = {};
1616 }
1617
1618 // Normalize id for the compiler & cache. // wp.template expects id WITHOUT the "tmpl-" prefix !
1619 const key = template_id.replace( /^tmpl-/, '' );
1620 if ( Core.__bfb_tpl_cache_map[key] ) {
1621 return Core.__bfb_tpl_cache_map[key];
1622 }
1623
1624 const compiler = wp.template( key ); // <-- normalized id here
1625 if ( compiler ) {
1626 Core.__bfb_tpl_cache_map[key] = compiler;
1627 }
1628
1629 return compiler;
1630 }
1631
1632 /**
1633 * REQUIRED: render preview into host element (full redraw; idempotent).
1634 * Subclasses should set static `template_id` to a valid wp.template id.
1635 * @param {HTMLElement} el
1636 * @param {Object} data
1637 * @param {{mode?:string,builder?:any,tpl?:Function,sanit?:any}} ctx
1638 * @returns {void}
1639 */
1640 static render( el, data, ctx ) {
1641 if ( ! el ) {
1642 return;
1643 }
1644
1645 var compile = this.get_template( this.template_id );
1646 var d = this.normalize_data( data );
1647
1648 var s = (ctx && ctx.sanit) ? ctx.sanit : Core.WPBC_BFB_Sanitize;
1649
1650 // Sanitize critical attributes before templating.
1651 if ( s ) {
1652 d.html_id = d.html_id ? s.sanitize_html_id( String( d.html_id ) ) : '';
1653 d.name = s.sanitize_html_name( String( d.name || d.id || 'field' ) );
1654 } else {
1655 d.html_id = d.html_id ? String( d.html_id ) : '';
1656 d.name = String( d.name || d.id || 'field' );
1657 }
1658
1659 // Fall back to generic preview if template not available.
1660 if ( compile ) {
1661 el.innerHTML = compile( d );
1662
1663 // After render, set attribute values via DOM so quotes/newlines are handled correctly.
1664 const input = el.querySelector( 'input, textarea, select' );
1665 if ( input ) {
1666 if ( d.placeholder != null ) input.setAttribute( 'placeholder', String( d.placeholder ) );
1667 if ( d.title != null ) input.setAttribute( 'title', String( d.title ) );
1668 }
1669
1670 } else {
1671 el.innerHTML = Core.WPBC_Form_Builder_Helper.render_field_inner_html( d );
1672 }
1673
1674 el.dataset.type = d.type || 'field';
1675 el.setAttribute( 'data-label', (d.label != null ? String( d.label ) : '') ); // allow "".
1676 }
1677
1678
1679 /**
1680 * OPTIONAL hook executed after field is dropped/loaded/preview.
1681 * Default extended:
1682 * - On first drop: stamp default label (existing behavior) and mark field as "fresh" for auto-name.
1683 * - On load: mark as loaded so later label edits do not rename the saved name.
1684 */
1685 static on_field_drop(data, el, meta) {
1686
1687 const context = (meta && meta.context) ? String( meta.context ) : '';
1688
1689 // -----------------------------------------------------------------------------------------
1690 // NEW: Seed default "help" (and keep it in Structure) for all field packs that define it.
1691 // This fixes the mismatch where:
1692 // - UI shows default help via normalize_data() / templates
1693 // - but get_structure() / exporters see `help` as undefined/empty.
1694 //
1695 // Behavior:
1696 // - Runs ONLY on initial drop (context === 'drop').
1697 // - If get_defaults() exposes a non-empty "help", and data.help is
1698 // missing / null / empty string -> we persist the default into `data`
1699 // and notify Structure so exports see it.
1700 // - On "load" we do nothing, so existing forms where user *cleared*
1701 // help will not be overridden.
1702 // -----------------------------------------------------------------------------------------
1703 if ( context === 'drop' && data ) {
1704 try {
1705 const defs = (typeof this.get_defaults === 'function') ? this.get_defaults() : null;
1706 if ( defs && Object.prototype.hasOwnProperty.call( defs, 'help' ) ) {
1707 const current = Object.prototype.hasOwnProperty.call( data, 'help' ) ? data.help : undefined;
1708 const hasValue = (current !== undefined && current !== null && String( current ) !== '');
1709 const defaultVal = defs.help;
1710
1711 if ( ! hasValue && defaultVal != null && String( defaultVal ) !== '' ) {
1712 // 1) persist into data object (used by Structure).
1713 data.help = defaultVal;
1714
1715 // 2) mirror into dataset (for any DOM-based consumers).
1716 if ( el ) {
1717 el.dataset.help = String( defaultVal );
1718
1719 // 3) notify Structure / listeners (if available).
1720 try {
1721 Core.Structure?.update_field_prop?.( el, 'help', defaultVal );
1722 el.dispatchEvent(
1723 new CustomEvent( 'wpbc_bfb_field_data_changed', { bubbles: true, detail : { key: 'help', value: defaultVal } } )
1724 );
1725 } catch ( _inner ) {}
1726 }
1727 }
1728 }
1729 } catch ( _e ) {}
1730 }
1731 // -----------------------------------------------------------------------------------------
1732
1733 if ( context === 'drop' && !Object.prototype.hasOwnProperty.call( data, 'label' ) ) {
1734 const defs = this.get_defaults();
1735 data.label = defs.label || 'Field';
1736 el.setAttribute( 'data-label', data.label );
1737 }
1738 // Mark provenance flags.
1739 if ( context === 'drop' ) {
1740 el.dataset.fresh = '1'; // can auto-name on first label edit.
1741 el.dataset.autoname = '1';
1742 el.dataset.was_loaded = '0';
1743 // Seed a provisional unique name immediately.
1744 try {
1745 const b = meta?.builder;
1746 if ( b?.id && (!el.hasAttribute( 'data-name' ) || !el.getAttribute( 'data-name' )) ) {
1747 const S = Core.WPBC_BFB_Sanitize;
1748 const base = S.sanitize_html_name( el.getAttribute( 'data-id' ) || data?.id || data?.type || 'field' );
1749 const uniq = b.id.ensure_unique_field_name( base, el );
1750 el.setAttribute( 'data-name', uniq );
1751 el.dataset.name_user_touched = '0';
1752 }
1753 } catch ( _ ) {}
1754
1755 } else if ( context === 'load' ) {
1756 el.dataset.fresh = '0';
1757 el.dataset.autoname = '0';
1758 el.dataset.was_loaded = '1'; // never rename names for loaded fields.
1759 }
1760 }
1761
1762 // --- Auto Rename "Fresh" field, on entering the new Label ---
1763
1764 /**
1765 * Create a conservative field "name" from a human label.
1766 * Uses the same constraints as sanitize_html_name (letters/digits/_- and leading letter).
1767 */
1768 static name_from_label(label) {
1769 const s = Core.WPBC_BFB_Sanitize.sanitize_html_name( String( label ?? '' ) );
1770 return s.toLowerCase() || 'field';
1771 }
1772
1773 /**
1774 * Auto-fill data-name from label ONLY for freshly dropped fields that were not edited yet.
1775 * - Never runs for sections.
1776 * - Never runs for loaded/existing fields.
1777 * - Stops as soon as user edits the Name manually.
1778 *
1779 * @param {WPBC_Form_Builder} builder
1780 * @param {HTMLElement} el - .wpbc_bfb__field element
1781 * @param {string} labelVal
1782 */
1783 static maybe_autoname_from_label(builder, el, labelVal) {
1784 if ( !builder || !el ) return;
1785 if ( el.classList.contains( 'wpbc_bfb__section' ) ) return;
1786
1787 const allowAuto = el.dataset.autoname === '1';
1788
1789 const userTouched = el.dataset.name_user_touched === '1';
1790 const isLoaded = el.dataset.was_loaded === '1';
1791
1792 if ( !allowAuto || userTouched || isLoaded ) return;
1793
1794 // Only override placeholder-y names
1795 const S = Core.WPBC_BFB_Sanitize;
1796
1797 const base = this.name_from_label( labelVal );
1798 const unique = builder.id.ensure_unique_field_name( base, el );
1799 el.setAttribute( 'data-name', unique );
1800
1801 const ins = document.getElementById( 'wpbc_bfb__inspector' );
1802 const nameCtrl = ins?.querySelector( '[data-inspector-key="name"]' );
1803 if ( nameCtrl && 'value' in nameCtrl && nameCtrl.value !== unique ) nameCtrl.value = unique;
1804 }
1805
1806
1807 };
1808
1809 /**
1810 * Select_Base (shared base for select-like packs)
1811 *
1812 * @type {Core.WPBC_BFB_Select_Base}
1813 */
1814 Core.WPBC_BFB_Select_Base = class extends Core.WPBC_BFB_Field_Base {
1815
1816 static template_id = null; // main preview template id
1817 static option_row_template_id = 'wpbc-bfb-inspector-select-option-row'; // row tpl id
1818 static kind = 'select';
1819 static __root_wired = false;
1820 static __root_node = null;
1821
1822 // Single source of selectors used by the inspector UI.
1823 static ui = {
1824 list : '.wpbc_bfb__options_list',
1825 holder : '.wpbc_bfb__options_state[data-inspector-key="options"]',
1826 row : '.wpbc_bfb__options_row',
1827 label : '.wpbc_bfb__opt-label',
1828 value : '.wpbc_bfb__opt-value',
1829 toggle : '.wpbc_bfb__opt-selected-chk',
1830 add_btn: '.js-add-option',
1831
1832 drag_handle : '.wpbc_bfb__drag-handle',
1833 multiple_chk : '.js-opt-multiple[data-inspector-key="multiple"]',
1834 default_text : '.js-default-value[data-inspector-key="default_value"]',
1835 placeholder_input: '.js-placeholder[data-inspector-key="placeholder"]',
1836 placeholder_note : '.js-placeholder-note',
1837 size_input : '.inspector__input[data-inspector-key="size"]',
1838
1839 // Dropdown menu integration.
1840 menu_root : '.wpbc_ui_el__dropdown',
1841 menu_toggle: '[data-toggle="wpbc_dropdown"]',
1842 menu_action: '.ul_dropdown_menu_li_action[data-action]',
1843 // Value-differs toggle.
1844 value_differs_chk: '.js-value-differs[data-inspector-key="value_differs"]',
1845 };
1846
1847 /**
1848 * Build option value from label.
1849 * - If `differs === true` -> generate token (slug-like machine value).
1850 * - If `differs === false` -> keep human text; escape only dangerous chars.
1851 * @param {string} label
1852 * @param {boolean} differs
1853 * @returns {string}
1854 */
1855 static build_value_from_label(label, differs) {
1856 const S = Core.WPBC_BFB_Sanitize;
1857 if ( differs ) {
1858 return (S && typeof S.to_token === 'function')
1859 ? S.to_token( String( label || '' ) )
1860 : String( label || '' ).trim().toLowerCase().replace( /\s+/g, '_' ).replace( /[^\w-]/g, '' );
1861 }
1862 // single-input mode: keep human text; template will escape safely.
1863 return String( label == null ? '' : label );
1864 }
1865
1866 /**
1867 * Is the “value differs from label” toggle enabled?
1868 * @param {HTMLElement} panel
1869 * @returns {boolean}
1870 */
1871 static is_value_differs_enabled(panel) {
1872 const chk = panel?.querySelector( this.ui.value_differs_chk );
1873 return !!(chk && chk.checked);
1874 }
1875
1876 /**
1877 * Ensure visibility/enabled state of Value inputs based on the toggle.
1878 * When disabled -> hide Value inputs and keep them mirrored from Label.
1879 * @param {HTMLElement} panel
1880 * @returns {void}
1881 */
1882 static sync_value_inputs_visibility(panel) {
1883 const differs = this.is_value_differs_enabled( panel );
1884 const rows = panel?.querySelectorAll( this.ui.row ) || [];
1885
1886 for ( let i = 0; i < rows.length; i++ ) {
1887 const r = rows[i];
1888 const lbl_in = r.querySelector( this.ui.label );
1889 const val_in = r.querySelector( this.ui.value );
1890 if ( !val_in ) continue;
1891
1892 if ( differs ) {
1893 // Re-enable & show value input
1894 val_in.removeAttribute( 'disabled' );
1895 val_in.style.display = '';
1896
1897 // If we have a cached custom value and the row wasn't edited while OFF, restore it
1898 const hasCache = !!val_in.dataset.cached_value;
1899 const userEdited = r.dataset.value_user_touched === '1';
1900
1901 if ( hasCache && !userEdited ) {
1902 val_in.value = val_in.dataset.cached_value;
1903 } else if ( !hasCache ) {
1904 // No cache: if value is just a mirrored label, offer a tokenized default
1905 const lbl = lbl_in ? lbl_in.value : '';
1906 const mirrored = this.build_value_from_label( lbl, /*differs=*/false );
1907 if ( val_in.value === mirrored ) {
1908 val_in.value = this.build_value_from_label( lbl, /*differs=*/true );
1909 }
1910 }
1911 } else {
1912 // ON -> OFF: cache once, then mirror
1913 if ( !val_in.dataset.cached_value ) {
1914 val_in.dataset.cached_value = val_in.value || '';
1915 }
1916 const lbl = lbl_in ? lbl_in.value : '';
1917 val_in.value = this.build_value_from_label( lbl, /*differs=*/false );
1918
1919 val_in.setAttribute( 'disabled', 'disabled' );
1920 val_in.style.display = 'none';
1921 // NOTE: do NOT mark as user_touched here
1922 }
1923 }
1924 }
1925
1926
1927 /**
1928 * Return whether this row’s value has been edited by user.
1929 * @param {HTMLElement} row
1930 * @returns {boolean}
1931 */
1932 static is_row_value_user_touched(row) {
1933 return row?.dataset?.value_user_touched === '1';
1934 }
1935
1936 /**
1937 * Mark this row’s value as edited by user.
1938 * @param {HTMLElement} row
1939 */
1940 static mark_row_value_user_touched(row) {
1941 if ( row ) row.dataset.value_user_touched = '1';
1942 }
1943
1944 /**
1945 * Initialize “freshness” flags on a row (value untouched).
1946 * Call on creation/append of rows.
1947 * @param {HTMLElement} row
1948 */
1949 static init_row_fresh_flags(row) {
1950 if ( row ) {
1951 if ( !row.dataset.value_user_touched ) {
1952 row.dataset.value_user_touched = '0';
1953 }
1954 }
1955 }
1956
1957 // ---- defaults (packs can override) ----
1958 static get_defaults() {
1959 return {
1960 type : this.kind,
1961 label : 'Select',
1962 name : '',
1963 html_id : '',
1964 placeholder : '--- Select ---',
1965 required : false,
1966 multiple : false,
1967 size : null,
1968 cssclass : '',
1969 help : '',
1970 default_value: '',
1971 options : [
1972 { label: 'Option 1', value: 'Option 1', selected: false },
1973 { label: 'Option 2', value: 'Option 2', selected: false },
1974 { label: 'Option 3', value: 'Option 3', selected: false },
1975 { label: 'Option 4', value: 'Option 4', selected: false }
1976 ],
1977 min_width : '240px'
1978 };
1979 }
1980
1981 // ---- preview render (idempotent) ----
1982 static render(el, data, ctx) {
1983 if ( !el ) return;
1984
1985 const d = this.normalize_data( data );
1986
1987 if ( d.min_width != null ) {
1988 el.dataset.min_width = String( d.min_width );
1989 try {
1990 el.style.setProperty( '--wpbc-col-min', String( d.min_width ) );
1991 } catch ( _ ) {
1992 }
1993 }
1994 if ( d.html_id != null ) el.dataset.html_id = String( d.html_id || '' );
1995 if ( d.cssclass != null ) el.dataset.cssclass = String( d.cssclass || '' );
1996 if ( d.placeholder != null ) el.dataset.placeholder = String( d.placeholder || '' );
1997
1998 const tpl = this.get_template( this.template_id );
1999 if ( typeof tpl !== 'function' ) {
2000 el.innerHTML = '<div class="wpbc_bfb__error" role="alert">Template not found: ' + this.template_id + '.</div>';
2001 return;
2002 }
2003
2004 try {
2005 el.innerHTML = tpl( d );
2006 } catch ( e ) {
2007 window._wpbc?.dev?.error?.( 'Select_Base.render', e );
2008 el.innerHTML = '<div class="wpbc_bfb__error" role="alert">Error rendering field preview.</div>';
2009 return;
2010 }
2011
2012 el.dataset.type = d.type || this.kind;
2013 el.setAttribute( 'data-label', (d.label != null ? String( d.label ) : '') );
2014
2015 try {
2016 Core.UI?.WPBC_BFB_Overlay?.ensure?.( ctx?.builder, el );
2017 } catch ( _ ) {
2018 }
2019
2020 if ( !el.dataset.options && Array.isArray( d.options ) && d.options.length ) {
2021 try {
2022 el.dataset.options = JSON.stringify( d.options );
2023 } catch ( _ ) {
2024 }
2025 }
2026 }
2027
2028 // ---- drop seeding (options + placeholder) ----
2029 static on_field_drop(data, el, meta) {
2030 try {
2031 super.on_field_drop?.( data, el, meta );
2032 } catch ( _ ) {
2033 }
2034
2035 const is_drop = (meta && meta.context === 'drop');
2036
2037 if ( is_drop ) {
2038 if ( !Array.isArray( data.options ) || !data.options.length ) {
2039 const opts = (this.get_defaults().options || []).map( (o) => ({
2040 label : o.label,
2041 value : o.value,
2042 selected: !!o.selected
2043 }) );
2044 data.options = opts;
2045 try {
2046 el.dataset.options = JSON.stringify( opts );
2047 el.dispatchEvent( new CustomEvent( 'wpbc_bfb_field_data_changed', { bubbles: true,
2048 detail : {
2049 key : 'options',
2050 value: opts
2051 }
2052 } ) );
2053 Core.Structure?.update_field_prop?.( el, 'options', opts );
2054 } catch ( _ ) {
2055 }
2056 }
2057
2058 const ph = (data.placeholder ?? '').toString().trim();
2059 if ( !ph ) {
2060 const dflt = this.get_defaults().placeholder || '--- Select ---';
2061 data.placeholder = dflt;
2062 try {
2063 el.dataset.placeholder = String( dflt );
2064 el.dispatchEvent( new CustomEvent( 'wpbc_bfb_field_data_changed', { bubbles: true,
2065 detail : {
2066 key : 'placeholder',
2067 value: dflt
2068 }
2069 } ) );
2070 Core.Structure?.update_field_prop?.( el, 'placeholder', dflt );
2071 } catch ( _ ) {
2072 }
2073 }
2074 }
2075 }
2076
2077 // ==============================
2078 // Inspector helpers (snake_case)
2079 // ==============================
2080 static get_panel_root(el) {
2081 return el?.closest?.( '.wpbc_bfb__inspector__body' ) || el?.closest?.( '.wpbc_bfb__inspector' ) || null;
2082 }
2083
2084 static get_list(panel) {
2085 return panel ? panel.querySelector( this.ui.list ) : null;
2086 }
2087
2088 static get_holder(panel) {
2089 return panel ? panel.querySelector( this.ui.holder ) : null;
2090 }
2091
2092 static make_uid() {
2093 return 'wpbc_ins_auto_opt_' + Math.random().toString( 36 ).slice( 2, 10 );
2094 }
2095
2096 static append_row(panel, data) {
2097 const list = this.get_list( panel );
2098 if ( !list ) return;
2099
2100 const idx = list.children.length;
2101 const rowd = Object.assign( { label: '', value: '', selected: false, index: idx }, (data || {}) );
2102 if ( !rowd.uid ) rowd.uid = this.make_uid();
2103
2104 const tpl_id = this.option_row_template_id;
2105 const tpl = (window.wp && wp.template) ? wp.template( tpl_id ) : null;
2106 const html = tpl ? tpl( rowd ) : null;
2107
2108 // In append_row() -> fallback HTML.
2109 const wrap = document.createElement( 'div' );
2110 wrap.innerHTML = html || (
2111 '<div class="wpbc_bfb__options_row" data-index="' + (rowd.index || 0) + '">' +
2112 '<span class="wpbc_bfb__drag-handle"><span class="wpbc_icn_drag_indicator"></span></span>' +
2113 '<input type="text" class="wpbc_bfb__opt-label" placeholder="Label" value="' + (rowd.label || '') + '">' +
2114 '<input type="text" class="wpbc_bfb__opt-value" placeholder="Value" value="' + (rowd.value || '') + '">' +
2115 '<div class="wpbc_bfb__opt-selected">' +
2116 '<div class="inspector__control wpbc_ui__toggle">' +
2117 '<input type="checkbox" class="wpbc_bfb__opt-selected-chk inspector__input" id="' + rowd.uid + '" role="switch" ' + (rowd.selected ? 'checked aria-checked="true"' : 'aria-checked="false"') + '>' +
2118 '<label class="wpbc_ui__toggle_icon_radio" for="' + rowd.uid + '"></label>' +
2119 '<label class="wpbc_ui__toggle_label" for="' + rowd.uid + '">Default</label>' +
2120 '</div>' +
2121 '</div>' +
2122 // 3-dot dropdown (uses existing plugin dropdown JS).
2123 '<div class="wpbc_ui_el wpbc_ui_el_container wpbc_ui_el__dropdown">' +
2124 '<a href="javascript:void(0)" data-toggle="wpbc_dropdown" aria-expanded="false" class="ul_dropdown_menu_toggle">' +
2125 '<i class="menu_icon icon-1x wpbc_icn_more_vert"></i>' +
2126 '</a>' +
2127 '<ul class="ul_dropdown_menu" role="menu" style="right:0px; left:auto;">' +
2128 '<li>' +
2129 '<a class="ul_dropdown_menu_li_action" data-action="add_after" href="javascript:void(0)">' +
2130 'Add New' +
2131 '<i class="menu_icon icon-1x wpbc_icn_add_circle"></i>' +
2132 '</a>' +
2133 '</li>' +
2134 '<li>' +
2135 '<a class="ul_dropdown_menu_li_action" data-action="duplicate" href="javascript:void(0)">' +
2136 'Duplicate' +
2137 '<i class="menu_icon icon-1x wpbc_icn_content_copy"></i>' +
2138 '</a>' +
2139 '</li>' +
2140 '<li class="divider"></li>' +
2141 '<li>' +
2142 '<a class="ul_dropdown_menu_li_action" data-action="remove" href="javascript:void(0)">' +
2143 'Remove' +
2144 '<i class="menu_icon icon-1x wpbc_icn_delete_outline"></i>' +
2145 '</a>' +
2146 '</li>' +
2147 '</ul>' +
2148 '</div>' +
2149 '</div>'
2150 );
2151
2152 const node = wrap.firstElementChild;
2153 if (! node) {
2154 return;
2155 }
2156 // pre-hide Value input if toggle is OFF **before** appending.
2157 const differs = this.is_value_differs_enabled( panel );
2158 const valIn = node.querySelector( this.ui.value );
2159 const lblIn = node.querySelector( this.ui.label );
2160
2161 if ( !differs && valIn ) {
2162 if ( !valIn.dataset.cached_value ) {
2163 valIn.dataset.cached_value = valIn.value || '';
2164 }
2165 if ( lblIn ) valIn.value = this.build_value_from_label( lblIn.value, false );
2166 valIn.setAttribute( 'disabled', 'disabled' );
2167 valIn.style.display = 'none';
2168 }
2169
2170
2171 this.init_row_fresh_flags( node );
2172 list.appendChild( node );
2173
2174 // Keep your existing post-append sync as a safety net
2175 this.sync_value_inputs_visibility( panel );
2176 }
2177
2178 static close_dropdown(anchor_el) {
2179 try {
2180 var root = anchor_el?.closest?.( this.ui.menu_root );
2181 if ( root ) {
2182 // If your dropdown toggler toggles a class like 'open', close it.
2183 root.classList.remove( 'open' );
2184 // Or if it relies on aria-expanded on the toggle.
2185 var t = root.querySelector( this.ui.menu_toggle );
2186 if ( t ) {
2187 t.setAttribute( 'aria-expanded', 'false' );
2188 }
2189 }
2190 } catch ( _ ) { }
2191 }
2192
2193 static insert_after(new_node, ref_node) {
2194 if ( ref_node?.parentNode ) {
2195 if ( ref_node.nextSibling ) {
2196 ref_node.parentNode.insertBefore( new_node, ref_node.nextSibling );
2197 } else {
2198 ref_node.parentNode.appendChild( new_node );
2199 }
2200 }
2201 }
2202
2203 static commit_options(panel) {
2204 const list = this.get_list( panel );
2205 const holder = this.get_holder( panel );
2206 if ( !list || !holder ) return;
2207
2208 const differs = this.is_value_differs_enabled( panel );
2209
2210 const rows = list.querySelectorAll( this.ui.row );
2211 const options = [];
2212 for ( let i = 0; i < rows.length; i++ ) {
2213 const r = rows[i];
2214 const lbl_in = r.querySelector( this.ui.label );
2215 const val_in = r.querySelector( this.ui.value );
2216 const chk = r.querySelector( this.ui.toggle );
2217
2218 const lbl = (lbl_in && lbl_in.value) || '';
2219 let val = (val_in && val_in.value) || '';
2220
2221 // If single-input mode -> hard mirror to label.
2222 if ( ! differs ) {
2223 // single-input mode: mirror Label, minimal escaping (no slug).
2224 val = this.build_value_from_label( lbl, /*differs=*/false );
2225 if ( val_in ) {
2226 val_in.value = val; // keep hidden input in sync for any previews/debug.
2227 }
2228 }
2229
2230 const sel = !!(chk && chk.checked);
2231 options.push( { label: lbl, value: val, selected: sel } );
2232 }
2233
2234 try {
2235 holder.value = JSON.stringify( options );
2236 holder.dispatchEvent( new Event( 'input', { bubbles: true } ) );
2237 holder.dispatchEvent( new Event( 'change', { bubbles: true } ) );
2238 panel.dispatchEvent( new CustomEvent( 'wpbc_bfb_field_data_changed', {
2239 bubbles: true, detail: {
2240 key: 'options', value: options
2241 }
2242 } ) );
2243 } catch ( _ ) {
2244 }
2245
2246 this.sync_default_value_lock( panel );
2247 this.sync_placeholder_lock( panel );
2248
2249 // Mirror to the selected field element so canvas/export sees current options immediately.
2250 const field = panel.__selectbase_field
2251 || document.querySelector( '.wpbc_bfb__field.is-selected, .wpbc_bfb__field--selected' );
2252 if ( field ) {
2253 try {
2254 field.dataset.options = JSON.stringify( options );
2255 } catch ( _ ) {
2256 }
2257 Core.Structure?.update_field_prop?.( field, 'options', options );
2258 field.dispatchEvent( new CustomEvent( 'wpbc_bfb_field_data_changed', {
2259 bubbles: true, detail: { key: 'options', value: options }
2260 } ) );
2261 }
2262 }
2263
2264
2265 static ensure_sortable(panel) {
2266
2267 const list = this.get_list( panel );
2268 if ( ! list ) {
2269 return;
2270 }
2271
2272 try {
2273 const existing = window.Sortable?.get?.( list );
2274 if ( existing ) {
2275 return;
2276 }
2277
2278 const builder = window.wpbc_bfb_api?.get_builder?.() || window.wpbc_bfb || null;
2279
2280 // Prefer the shared Sortable manager so the sidebar list uses
2281 // the dedicated "simple_list" config instead of the canvas config.
2282 if ( builder && builder.sortable && typeof builder.sortable.ensure === 'function' ) {
2283
2284 builder.sortable.ensure(
2285 list,
2286 'canvas',
2287 {
2288 sortable_kind : 'simple_list',
2289 handle_selector : this.ui.drag_handle,
2290 draggable_selector: this.ui.row,
2291 onUpdate : () => {
2292 this.commit_options( panel );
2293 }
2294 }
2295 );
2296
2297 } else if ( window.Sortable?.create ) {
2298 // Fallback if builder is not ready for some reason.
2299 window.Sortable.create(
2300 list,
2301 {
2302 handle : this.ui.drag_handle,
2303 draggable : this.ui.row,
2304 animation : 120,
2305 forceFallback : true,
2306 fallbackOnBody : false,
2307 fallbackTolerance: 8,
2308 removeCloneOnHide: true,
2309 onUpdate : () => {
2310 this.commit_options( panel );
2311 }
2312 }
2313 );
2314 }
2315
2316 list.dataset.sortable_init = '1';
2317
2318 } catch ( e ) {
2319 window._wpbc?.dev?.error?.( 'Select_Base.ensure_sortable', e );
2320 }
2321 }
2322
2323 static rebuild_if_empty(panel) {
2324 const list = this.get_list( panel );
2325 const holder = this.get_holder( panel );
2326 if ( !list || !holder || list.children.length ) return;
2327
2328 let data = [];
2329 try {
2330 data = JSON.parse( holder.value || '[]' );
2331 } catch ( _ ) {
2332 data = [];
2333 }
2334
2335 if ( !Array.isArray( data ) || !data.length ) {
2336 data = (this.get_defaults().options || []).slice( 0 );
2337 try {
2338 holder.value = JSON.stringify( data );
2339 holder.dispatchEvent( new Event( 'input', { bubbles: true } ) );
2340 holder.dispatchEvent( new Event( 'change', { bubbles: true } ) );
2341 } catch ( _ ) {
2342 }
2343 }
2344
2345 for ( let i = 0; i < data.length; i++ ) {
2346 this.append_row( panel, {
2347 label : data[i]?.label || '',
2348 value : data[i]?.value || '',
2349 selected: !!data[i]?.selected,
2350 index : i,
2351 uid : this.make_uid()
2352 } );
2353 }
2354
2355 this.sync_default_value_lock( panel );
2356 this.sync_placeholder_lock( panel );
2357 this.sync_value_inputs_visibility( panel );
2358 }
2359
2360 static has_row_defaults(panel) {
2361 const checks = panel?.querySelectorAll( this.ui.toggle );
2362 if ( !checks?.length ) return false;
2363 for ( let i = 0; i < checks.length; i++ ) if ( checks[i].checked ) return true;
2364 return false;
2365 }
2366
2367 static is_multiple_enabled(panel) {
2368 const chk = panel?.querySelector( this.ui.multiple_chk );
2369 return !!(chk && chk.checked);
2370 }
2371
2372 static has_text_default_value(panel) {
2373 const dv = panel?.querySelector( this.ui.default_text );
2374 return !!(dv && String( dv.value || '' ).trim().length);
2375 }
2376
2377 static sync_default_value_lock(panel) {
2378 const input = panel?.querySelector( this.ui.default_text );
2379 const note = panel?.querySelector( '.js-default-value-note' );
2380 if ( !input ) return;
2381
2382 const lock = this.has_row_defaults( panel );
2383 input.disabled = !!lock;
2384 if ( lock ) {
2385 input.setAttribute( 'aria-disabled', 'true' );
2386 if ( note ) note.style.display = '';
2387 } else {
2388 input.removeAttribute( 'aria-disabled' );
2389 if ( note ) note.style.display = 'none';
2390 }
2391 }
2392
2393 static sync_placeholder_lock(panel) {
2394 const input = panel?.querySelector( this.ui.placeholder_input );
2395 const note = panel?.querySelector( this.ui.placeholder_note );
2396
2397 // NEW: compute multiple and toggle row visibility
2398 const isMultiple = this.is_multiple_enabled( panel );
2399 const placeholderRow = input?.closest( '.inspector__row' ) || null;
2400 const sizeInput = panel?.querySelector( this.ui.size_input ) || null;
2401 const sizeRow = sizeInput?.closest( '.inspector__row' ) || null;
2402
2403 // Show placeholder only for single-select; show size only for multiple
2404 if ( placeholderRow ) placeholderRow.style.display = isMultiple ? 'none' : '';
2405 if ( sizeRow ) sizeRow.style.display = isMultiple ? '' : 'none';
2406
2407 // Existing behavior (keep as-is)
2408 if ( !input ) return;
2409
2410 const lock = isMultiple || this.has_row_defaults( panel ) || this.has_text_default_value( panel );
2411 if ( note && !note.id ) note.id = 'wpbc_placeholder_note_' + Math.random().toString( 36 ).slice( 2, 10 );
2412
2413 input.disabled = !!lock;
2414 if ( lock ) {
2415 input.setAttribute( 'aria-disabled', 'true' );
2416 if ( note ) {
2417 note.style.display = '';
2418 input.setAttribute( 'aria-describedby', note.id );
2419 }
2420 } else {
2421 input.removeAttribute( 'aria-disabled' );
2422 input.removeAttribute( 'aria-describedby' );
2423 if ( note ) note.style.display = 'none';
2424 }
2425 }
2426
2427 static enforce_single_default(panel, clicked) {
2428 if ( this.is_multiple_enabled( panel ) ) return;
2429
2430 const checks = panel?.querySelectorAll( this.ui.toggle );
2431 if ( !checks?.length ) return;
2432
2433 if ( clicked && clicked.checked ) {
2434 for ( let i = 0; i < checks.length; i++ ) if ( checks[i] !== clicked ) {
2435 checks[i].checked = false;
2436 checks[i].setAttribute( 'aria-checked', 'false' );
2437 }
2438 clicked.setAttribute( 'aria-checked', 'true' );
2439 return;
2440 }
2441
2442 let kept = false;
2443 for ( let j = 0; j < checks.length; j++ ) if ( checks[j].checked ) {
2444 if ( !kept ) {
2445 kept = true;
2446 } else {
2447 checks[j].checked = false;
2448 checks[j].setAttribute( 'aria-checked', 'false' );
2449 }
2450 }
2451
2452 this.sync_default_value_lock( panel );
2453 this.sync_placeholder_lock( panel );
2454 }
2455
2456 // ---- one-time bootstrap of a panel ----
2457 static bootstrap_panel(panel) {
2458 if ( !panel ) return;
2459 if ( !panel.querySelector( '.wpbc_bfb__options_editor' ) ) return; // only select-like UIs
2460 if ( panel.dataset.selectbase_bootstrapped === '1' ) {
2461 this.ensure_sortable( panel );
2462 return;
2463 }
2464
2465 this.rebuild_if_empty( panel );
2466 this.ensure_sortable( panel );
2467 panel.dataset.selectbase_bootstrapped = '1';
2468
2469 this.sync_default_value_lock( panel );
2470 this.sync_placeholder_lock( panel );
2471 this.sync_value_inputs_visibility( panel );
2472 }
2473
2474 // ---- hook into inspector lifecycle (fires ONCE) ----
2475 static wire_once() {
2476 if ( Core.__selectbase_wired ) return;
2477 Core.__selectbase_wired = true;
2478
2479 const on_ready_or_render = (ev) => {
2480 const panel = ev?.detail?.panel;
2481 const field = ev?.detail?.el || ev?.detail?.field || null;
2482 if ( !panel ) return;
2483 if ( field ) panel.__selectbase_field = field;
2484 this.bootstrap_panel( panel );
2485 // If the inspector root was remounted, ensure root listeners are (re)bound.
2486 this.wire_root_listeners();
2487 };
2488
2489 document.addEventListener( 'wpbc_bfb_inspector_ready', on_ready_or_render );
2490 document.addEventListener( 'wpbc_bfb_inspector_render', on_ready_or_render );
2491
2492 this.wire_root_listeners();
2493 }
2494
2495 static wire_root_listeners() {
2496
2497 // If already wired AND the stored root is still in the DOM, bail out.
2498 if ( this.__root_wired && this.__root_node?.isConnected ) return;
2499
2500 const root = document.getElementById( 'wpbc_bfb__inspector' );
2501 if ( !root ) {
2502 // Root missing (e.g., SPA re-render) — clear flags so we can wire later.
2503 this.__root_wired = false;
2504 this.__root_node = null;
2505 return;
2506 }
2507
2508 this.__root_node = root;
2509 this.__root_wired = true;
2510 root.dataset.selectbase_root_wired = '1';
2511
2512 const get_panel = (target) =>
2513 target?.closest?.( '.wpbc_bfb__inspector__body' ) ||
2514 root.querySelector( '.wpbc_bfb__inspector__body' ) || null;
2515
2516 // Click handlers: add / delete / duplicate
2517 root.addEventListener( 'click', (e) => {
2518 const panel = get_panel( e.target );
2519 if ( !panel ) return;
2520
2521 this.bootstrap_panel( panel );
2522
2523 const ui = this.ui;
2524
2525 // Existing "Add option" button (top toolbar)
2526 const add = e.target.closest?.( ui.add_btn );
2527 if ( add ) {
2528 this.append_row( panel, { label: '', value: '', selected: false } );
2529 this.commit_options( panel );
2530 this.sync_value_inputs_visibility( panel );
2531 return;
2532 }
2533
2534 // Dropdown menu actions.
2535 const menu_action = e.target.closest?.( ui.menu_action );
2536 if ( menu_action ) {
2537 e.preventDefault();
2538 e.stopPropagation();
2539
2540 const action = (menu_action.getAttribute( 'data-action' ) || '').toLowerCase();
2541 const row = menu_action.closest?.( ui.row );
2542
2543 if ( !row ) {
2544 this.close_dropdown( menu_action );
2545 return;
2546 }
2547
2548 if ( 'add_after' === action ) {
2549 // Add empty row after current
2550 const prev_count = this.get_list( panel )?.children.length || 0;
2551 this.append_row( panel, { label: '', value: '', selected: false } );
2552 // Move the newly added last row just after current row to preserve "add after"
2553 const list = this.get_list( panel );
2554 if ( list && list.lastElementChild && list.lastElementChild !== row ) {
2555 this.insert_after( list.lastElementChild, row );
2556 }
2557 this.commit_options( panel );
2558 this.sync_value_inputs_visibility( panel );
2559 } else if ( 'duplicate' === action ) {
2560 const lbl = (row.querySelector( ui.label ) || {}).value || '';
2561 const val = (row.querySelector( ui.value ) || {}).value || '';
2562 const sel = !!((row.querySelector( ui.toggle ) || {}).checked);
2563 this.append_row( panel, { label: lbl, value: val, selected: sel, uid: this.make_uid() } );
2564 // Place the new row right after the current.
2565 const list = this.get_list( panel );
2566
2567 if ( list && list.lastElementChild && list.lastElementChild !== row ) {
2568 this.insert_after( list.lastElementChild, row );
2569 }
2570 this.enforce_single_default( panel, null );
2571 this.commit_options( panel );
2572 this.sync_value_inputs_visibility( panel );
2573 } else if ( 'remove' === action ) {
2574 if ( row && row.parentNode ) row.parentNode.removeChild( row );
2575 this.commit_options( panel );
2576 this.sync_value_inputs_visibility( panel );
2577 }
2578
2579 this.close_dropdown( menu_action );
2580 return;
2581 }
2582
2583 }, true );
2584
2585
2586 // Input delegation.
2587 root.addEventListener( 'input', (e) => {
2588 const panel = get_panel( e.target );
2589 if ( ! panel ) {
2590 return;
2591 }
2592 const ui = this.ui;
2593 const is_label_or_value = e.target.classList?.contains( 'wpbc_bfb__opt-label' ) || e.target.classList?.contains( 'wpbc_bfb__opt-value' );
2594 const is_toggle = e.target.classList?.contains( 'wpbc_bfb__opt-selected-chk' );
2595 const is_multiple = e.target.matches?.( ui.multiple_chk );
2596 const is_default_text = e.target.matches?.( ui.default_text );
2597 const is_value_differs = e.target.matches?.( ui.value_differs_chk );
2598
2599 // Handle "value differs" toggle live
2600 if ( is_value_differs ) {
2601 this.sync_value_inputs_visibility( panel );
2602 this.commit_options( panel );
2603 return;
2604 }
2605
2606 // Track when the user edits VALUE explicitly
2607 if ( e.target.classList?.contains( 'wpbc_bfb__opt-value' ) ) {
2608 const row = e.target.closest( this.ui.row );
2609 this.mark_row_value_user_touched( row );
2610 // Keep the cache updated so toggling OFF/ON later restores the latest custom value
2611 e.target.dataset.cached_value = e.target.value || '';
2612 }
2613
2614 // Auto-fill VALUE from LABEL if value is fresh (and differs is ON); if differs is OFF, we mirror anyway in commit
2615 if ( e.target.classList?.contains( 'wpbc_bfb__opt-label' ) ) {
2616 const row = e.target.closest( ui.row );
2617 const val_in = row?.querySelector( ui.value );
2618 const differs = this.is_value_differs_enabled( panel );
2619
2620 if ( val_in ) {
2621 if ( !differs ) {
2622 // single-input mode: mirror human label with minimal escaping
2623 val_in.value = this.build_value_from_label( e.target.value, false );
2624 } else if ( !this.is_row_value_user_touched( row ) ) {
2625 // separate-value mode, only while fresh
2626 val_in.value = this.build_value_from_label( e.target.value, true );
2627 }
2628 }
2629 }
2630
2631
2632 if ( is_label_or_value || is_toggle || is_multiple ) {
2633 if ( is_toggle ) e.target.setAttribute( 'aria-checked', e.target.checked ? 'true' : 'false' );
2634 if ( is_toggle || is_multiple ) this.enforce_single_default( panel, is_toggle ? e.target : null );
2635 this.commit_options( panel );
2636 }
2637
2638 if ( is_default_text ) {
2639 this.sync_default_value_lock( panel );
2640 this.sync_placeholder_lock( panel );
2641 const holder = this.get_holder( panel );
2642 if ( holder ) {
2643 holder.dispatchEvent( new Event( 'input', { bubbles: true } ) );
2644 holder.dispatchEvent( new Event( 'change', { bubbles: true } ) );
2645 }
2646 }
2647 }, true );
2648
2649
2650 // Change delegation
2651 root.addEventListener( 'change', (e) => {
2652 const panel = get_panel( e.target );
2653 if ( !panel ) return;
2654
2655 const ui = this.ui;
2656 const is_toggle = e.target.classList?.contains( 'wpbc_bfb__opt-selected-chk' );
2657 const is_multi = e.target.matches?.( ui.multiple_chk );
2658 if ( !is_toggle && !is_multi ) return;
2659
2660 if ( is_toggle ) e.target.setAttribute( 'aria-checked', e.target.checked ? 'true' : 'false' );
2661 this.enforce_single_default( panel, is_toggle ? e.target : null );
2662 this.commit_options( panel );
2663 }, true );
2664
2665 // Lazy bootstrap
2666 root.addEventListener( 'mouseenter', (e) => {
2667 const panel = get_panel( e.target );
2668 if ( panel && e.target?.closest?.( this.ui.list ) ) this.bootstrap_panel( panel );
2669 }, true );
2670
2671 root.addEventListener( 'mousedown', (e) => {
2672 const panel = get_panel( e.target );
2673 if ( panel && e.target?.closest?.( this.ui.drag_handle ) ) this.bootstrap_panel( panel );
2674 }, true );
2675 }
2676
2677 };
2678
2679 try { Core.WPBC_BFB_Select_Base.wire_once(); } catch (_) {}
2680 // Try immediately (if root is already in DOM), then again on DOMContentLoaded.
2681 Core.WPBC_BFB_Select_Base.wire_root_listeners();
2682
2683 document.addEventListener('DOMContentLoaded', () => { Core.WPBC_BFB_Select_Base.wire_root_listeners(); });
2684
2685 }( window ));
2686 // ---------------------------------------------------------------------------------------------------------------------
2687 // == File /includes/page-form-builder/_out/core/bfb-ui.js == | 2025-09-10 15:47
2688 // ---------------------------------------------------------------------------------------------------------------------
2689 (function (w, d) {
2690 'use strict';
2691
2692 // Single global namespace (idempotent & load-order safe).
2693 const Core = (w.WPBC_BFB_Core = w.WPBC_BFB_Core || {});
2694 const UI = (Core.UI = Core.UI || {});
2695
2696 // --- Highlight Element, like Generator brn - Tiny UI helpers ------------------------------------
2697 UI._pulse_timers = UI._pulse_timers || new Map(); // el -> timer_id
2698 UI._pulse_meta = UI._pulse_meta || new Map(); // el -> { token, last_ts, debounce_id, color_set }
2699 // Pulse tuning (milliseconds).
2700 UI.PULSE_THROTTLE_MS = Number.isFinite( UI.PULSE_THROTTLE_MS ) ? UI.PULSE_THROTTLE_MS : 500;
2701 UI.PULSE_DEBOUNCE_MS = Number.isFinite( UI.PULSE_DEBOUNCE_MS ) ? UI.PULSE_DEBOUNCE_MS : 750;
2702
2703 // Debounce STRUCTURE_CHANGE for continuous inspector controls (sliders / scrubbing).
2704 // Tune: 180..350 is usually a sweet spot.
2705 UI.STRUCTURE_CHANGE_DEBOUNCE_MS = Number.isFinite( UI.STRUCTURE_CHANGE_DEBOUNCE_MS ) ? UI.STRUCTURE_CHANGE_DEBOUNCE_MS : 180;
2706 // Change this to tune speed: 50..120 ms is a good range. Can be configured in <div data-len-group data-len-throttle="180">...</div>.
2707 UI.VALUE_SLIDER_THROTTLE_MS = Number.isFinite( UI.VALUE_SLIDER_THROTTLE_MS ) ? UI.VALUE_SLIDER_THROTTLE_MS : 120;
2708
2709 /**
2710 * Cancel any running pulse sequence for an element.
2711 * Uses token invalidation so already-scheduled callbacks become no-ops.
2712 *
2713 * @param {HTMLElement} el
2714 */
2715 UI.cancel_pulse = function (el) {
2716 if ( !el ) { return; }
2717 try {
2718 clearTimeout( UI._pulse_timers.get( el ) );
2719 } catch ( _ ) {}
2720 UI._pulse_timers.delete( el );
2721
2722 var meta = UI._pulse_meta.get( el ) || {};
2723 meta.token = (Number.isFinite( meta.token ) ? meta.token : 0) + 1;
2724 meta.color_set = false;
2725 try { el.classList.remove( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' ); } catch ( _ ) {}
2726 try { el.style.removeProperty( '--wpbc-bfb-pulse-color' ); } catch ( _ ) {}
2727 UI._pulse_meta.set( el, meta );
2728 try { clearTimeout( meta.debounce_id ); } catch ( _ ) {}
2729 meta.debounce_id = 0;
2730 };
2731
2732 /**
2733 * Force-restart a CSS animation on a class.
2734 * @param {HTMLElement} el
2735 * @param {string} cls
2736 */
2737 UI._restart_css_animation = function (el, cls) {
2738 if ( ! el ) { return; }
2739 try {
2740 el.classList.remove( cls );
2741 } catch ( _ ) {}
2742 // Force reflow so the next add() retriggers the keyframes.
2743 void el.offsetWidth;
2744 try {
2745 el.classList.add( cls );
2746 } catch ( _ ) {}
2747 };
2748
2749 /**
2750 Single pulse (back-compat).
2751 @param {HTMLElement} el
2752 @param {number} dur_ms
2753 */
2754 UI.pulse_once = function (el, dur_ms) {
2755 if ( ! el ) { return; }
2756 var cls = 'wpbc_bfb__scroll-pulse';
2757 var ms = Number.isFinite( dur_ms ) ? dur_ms : 700;
2758
2759 UI.cancel_pulse( el );
2760
2761 var meta = UI._pulse_meta.get( el ) || {};
2762 var token = (Number.isFinite( meta.token ) ? meta.token : 0) + 1;
2763 meta.token = token;
2764 UI._pulse_meta.set( el, meta );
2765
2766 UI._restart_css_animation( el, cls );
2767 var t = setTimeout( function () {
2768 // ignore if a newer pulse started.
2769 var m = UI._pulse_meta.get( el ) || {};
2770 if ( m.token !== token ) { return; }
2771 try {
2772 el.classList.remove( cls );
2773 } catch ( _ ) {}
2774 UI._pulse_timers.delete( el );
2775 }, ms );
2776 UI._pulse_timers.set( el, t );
2777 };
2778
2779 /**
2780 Multi-blink sequence with optional per-call color override.
2781 @param {HTMLElement} el
2782 @param {number} [times=3]
2783 @param {number} [on_ms=280]
2784 @param {number} [off_ms=180]
2785 @param {string} [hex_color] Optional CSS color (e.g. '#ff4d4f' or 'rgb(...)').
2786 */
2787 UI.pulse_sequence = function (el, times, on_ms, off_ms, hex_color) {
2788 if ( !el || !d.body.contains( el ) ) {
2789 return;
2790 }
2791 var cls = 'wpbc_bfb__highlight-pulse';
2792 var count = Number.isFinite( times ) ? times : 2;
2793 var on = Number.isFinite( on_ms ) ? on_ms : 280;
2794 var off = Number.isFinite( off_ms ) ? off_ms : 180;
2795
2796 // Throttle: avoid reflow spam if called repeatedly while typing/dragging.
2797 var meta = UI._pulse_meta.get( el ) || {};
2798 var now = Date.now();
2799 var throttle_ms = Number.isFinite( UI.PULSE_THROTTLE_MS ) ? UI.PULSE_THROTTLE_MS : 120;
2800 if ( Number.isFinite( meta.last_ts ) && (now - meta.last_ts) < throttle_ms ) {
2801 return;
2802 }
2803 meta.last_ts = now;
2804
2805 // cancel any running pulse and reset class (token invalidation).
2806 UI.cancel_pulse( el );
2807
2808 // new token for this run
2809 var token = (Number.isFinite( meta.token ) ? meta.token : 0) + 1;
2810 meta.token = token;
2811
2812 var have_color = !!hex_color && typeof hex_color === 'string';
2813 if ( have_color ) {
2814 try {
2815 el.style.setProperty( '--wpbc-bfb-pulse-color', hex_color );
2816 } catch ( _ ) {}
2817 meta.color_set = true;
2818 }
2819 UI._pulse_meta.set( el, meta );
2820
2821 var i = 0;
2822 (function tick() {
2823 var m = UI._pulse_meta.get( el ) || {};
2824 if ( m.token !== token ) {
2825 // canceled/replaced
2826 return;
2827 }
2828 if ( i >= count ) {
2829 UI._pulse_timers.delete( el );
2830 if ( have_color ) {
2831 try {
2832 el.style.removeProperty( '--wpbc-bfb-pulse-color' );
2833 } catch ( _ ) {}
2834 }
2835 return;
2836 }
2837 UI._restart_css_animation( el, cls );
2838 UI._pulse_timers.set( el, setTimeout( function () { // ON -> OFF
2839 var m2 = UI._pulse_meta.get( el ) || {};
2840 if ( m2.token !== token ) { return; }
2841 try {
2842 el.classList.remove( cls );
2843 } catch ( _ ) {
2844 }
2845 UI._pulse_timers.set( el, setTimeout( function () { // OFF gap -> next
2846 var m3 = UI._pulse_meta.get( el ) || {};
2847 if ( m3.token !== token ) { return; }
2848 i++;
2849 tick();
2850 }, off ) );
2851 }, on ) );
2852 })();
2853 };
2854
2855
2856 /**
2857 * Debounced query + pulse.
2858 * Useful for `input` events (sliders / typing) to avoid forced reflow spam.
2859 *
2860 * @param {HTMLElement|string} root_or_selector
2861 * @param {string} selector
2862 * @param {number} wait_ms
2863 * @param {number} [a]
2864 * @param {number} [b]
2865 * @param {number} [c]
2866 * @param {string} [color]
2867 */
2868 UI.pulse_query_debounced = function (root_or_selector, selector, wait_ms, a, b, c, color) {
2869 var root = (typeof root_or_selector === 'string') ? d : (root_or_selector || d);
2870 var sel = (typeof root_or_selector === 'string') ? root_or_selector : selector;
2871 if ( !sel ) { return; }
2872 var el = root.querySelector( sel );
2873 if ( !el ) { return; }
2874
2875 var def_ms = Number.isFinite( UI.PULSE_DEBOUNCE_MS ) ? UI.PULSE_DEBOUNCE_MS : 120;
2876 var ms = Number.isFinite( wait_ms ) ? wait_ms : def_ms;
2877 var meta = UI._pulse_meta.get( el ) || {};
2878 try { clearTimeout( meta.debounce_id ); } catch ( _ ) {}
2879 meta.debounce_id = setTimeout( function () {
2880 UI.pulse_sequence( el, a, b, c, color );
2881 }, ms );
2882 UI._pulse_meta.set( el, meta );
2883 };
2884
2885 /**
2886 Query + pulse:
2887 (BC) If only 3rd arg is a number and no 4th/5th -> single long pulse.
2888 Otherwise -> strong sequence (defaults 3×280/180).
2889 Optional 6th arg: color.
2890 @param {HTMLElement|string} root_or_selector
2891 @param {string} [selector]
2892 @param {number} [a]
2893 @param {number} [b]
2894
2895 @param {number} [c]
2896
2897 @param {string} [color]
2898 */
2899 UI.pulse_query = function (root_or_selector, selector, a, b, c, color) {
2900 var root = (typeof root_or_selector === 'string') ? d : (root_or_selector || d);
2901 var sel = (typeof root_or_selector === 'string') ? root_or_selector : selector;
2902 if ( !sel ) {
2903 return;
2904 }
2905
2906 var el = root.querySelector( sel );
2907 if ( !el ) {
2908 return;
2909 }
2910
2911 // Back-compat: UI.pulseQuery(root, sel, dur_ms)
2912 if ( Number.isFinite( a ) && b === undefined && c === undefined ) {
2913 return UI.pulse_once( el, a );
2914 }
2915 // New: sequence; params optional; supports optional color.
2916 UI.pulse_sequence( el, a, b, c, color );
2917 };
2918
2919 /**
2920 Convenience helper (snake_case) to call a strong pulse with options.
2921
2922 @param {HTMLElement} el
2923
2924 @param {Object} [opts]
2925
2926 @param {number} [opts.times=3]
2927
2928 @param {number} [opts.on_ms=280]
2929
2930 @param {number} [opts.off_ms=180]
2931
2932 @param {string} [opts.color]
2933 */
2934 UI.pulse_sequence_strong = function (el, opts) {
2935 opts = opts || {};
2936 UI.pulse_sequence(
2937 el,
2938 Number.isFinite( opts.times ) ? opts.times : 3,
2939 Number.isFinite( opts.on_ms ) ? opts.on_ms : 280,
2940 Number.isFinite( opts.off_ms ) ? opts.off_ms : 180,
2941 opts.color
2942 );
2943 };
2944
2945
2946 /**
2947 * Base class for BFB modules.
2948 */
2949 UI.WPBC_BFB_Module = class {
2950 /** @param {WPBC_Form_Builder} builder */
2951 constructor(builder) {
2952 this.builder = builder;
2953 }
2954
2955 /** Initialize the module. */
2956 init() {
2957 }
2958
2959 /** Cleanup the module. */
2960 destroy() {
2961 }
2962 };
2963
2964 /**
2965 * Central overlay/controls manager for fields/sections.
2966 * Pure UI composition; all actions route back into the builder instance.
2967 */
2968 UI.WPBC_BFB_Overlay = class {
2969
2970 /**
2971 * Ensure an overlay exists and is wired up on the element.
2972 * @param {WPBC_Form_Builder} builder
2973 * @param {HTMLElement} el - field or section element
2974 */
2975 static ensure(builder, el) {
2976
2977 if ( !el ) {
2978 return;
2979 }
2980 const isSection = el.classList.contains( 'wpbc_bfb__section' );
2981
2982 // let overlay = el.querySelector( Core.WPBC_BFB_DOM.SELECTORS.overlay );
2983 let overlay = el.querySelector( `:scope > ${Core.WPBC_BFB_DOM.SELECTORS.overlay}` );
2984 if ( !overlay ) {
2985 overlay = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__overlay-controls' );
2986 el.prepend( overlay );
2987 }
2988
2989 // Drag handle.
2990 if ( !overlay.querySelector( '.wpbc_bfb__drag-handle' ) ) {
2991 const dragClass = isSection ? 'wpbc_bfb__drag-handle section-drag-handle' : 'wpbc_bfb__drag-handle';
2992 overlay.appendChild(
2993 Core.WPBC_Form_Builder_Helper.create_element( 'span', dragClass, '<span class="wpbc_icn_drag_indicator"></span>' )
2994 );
2995 }
2996
2997 // SETTINGS button (shown for both fields & sections).
2998 if ( !overlay.querySelector( '.wpbc_bfb__settings-btn' ) ) {
2999 const settings_btn = Core.WPBC_Form_Builder_Helper.create_element( 'button', 'wpbc_bfb__settings-btn', '<i class="menu_icon icon-1x wpbc_icn_settings"></i>' );
3000 settings_btn.type = 'button';
3001 settings_btn.title = 'Open settings';
3002 settings_btn.onclick = (e) => {
3003 e.preventDefault();
3004 // Select THIS element and scroll it into view.
3005 builder.select_field( el, { scrollIntoView: true } );
3006
3007 // Auto-open Inspector from the overlay “Settings” button.
3008 wpbc_bfb__dispatch_event_safe(
3009 'wpbc_bfb:show_panel',
3010 {
3011 panel_id: 'wpbc_bfb__inspector',
3012 tab_id : 'wpbc_tab_inspector'
3013 }
3014 );
3015
3016 // Try to bring the inspector into view / focus first input.
3017 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3018 if ( ins ) {
3019 ins.scrollIntoView( { behavior: 'smooth', block: 'nearest' } );
3020 // Focus first interactive control (best-effort).
3021 setTimeout( () => {
3022 const focusable = ins.querySelector( 'input,select,textarea,button,[contenteditable],[tabindex]:not([tabindex="-1"])' );
3023 focusable?.focus?.();
3024 }, 260 );
3025 }
3026 };
3027
3028 overlay.appendChild( settings_btn );
3029 }
3030
3031 overlay.setAttribute( 'role', 'toolbar' );
3032 overlay.setAttribute( 'aria-label', el.classList.contains( 'wpbc_bfb__section' ) ? 'Section tools' : 'Field tools' );
3033
3034 return overlay;
3035 }
3036 };
3037
3038 /**
3039 * WPBC Layout Chips helper - visual layout picker (chips), e.g., "50%/50%", to a section overlay.
3040 *
3041 * Renders Equal/Presets/Custom chips into a host container and wires them to apply the layout.
3042 */
3043 UI.WPBC_BFB_Layout_Chips = class {
3044
3045 /** Read per-column min (px) from CSS var set by the guard. */
3046 static _get_col_min_px(col) {
3047 const v = getComputedStyle( col ).getPropertyValue( '--wpbc-col-min' ) || '0';
3048 const n = parseFloat( v );
3049 return Number.isFinite( n ) ? Math.max( 0, n ) : 0;
3050 }
3051
3052 /**
3053 * Turn raw weights (e.g. [1,1], [2,1,1]) into effective "available-%" bases that
3054 * (a) sum to the row's available %, and (b) meet every column's min px.
3055 * Returns an array of bases (numbers) or null if impossible to satisfy mins.
3056 */
3057 static _fit_weights_respecting_min(builder, row, weights) {
3058 const cols = Array.from( row.querySelectorAll( ':scope > .wpbc_bfb__column' ) );
3059 const n = cols.length;
3060 if ( !n ) return null;
3061 if ( !Array.isArray( weights ) || weights.length !== n ) return null;
3062
3063 // available % after gaps (from LayoutService)
3064 const gp = builder.col_gap_percent;
3065 const eff = builder.layout.compute_effective_bases_from_row( row, gp );
3066 const availPct = eff.available; // e.g. 94 if 2 cols and 3% gap
3067 const rowPx = row.getBoundingClientRect().width;
3068 const availPx = rowPx * (availPct / 100);
3069
3070 // collect minima in % of "available"
3071 const minPct = cols.map( (c) => {
3072 const minPx = UI.WPBC_BFB_Layout_Chips._get_col_min_px( c );
3073 if ( availPx <= 0 ) return 0;
3074 return (minPx / availPx) * availPct;
3075 } );
3076
3077 // If mins alone don't fit, bail.
3078 const sumMin = minPct.reduce( (a, b) => a + b, 0 );
3079 if ( sumMin > availPct - 1e-6 ) {
3080 return null; // impossible to respect mins; don't apply preset
3081 }
3082
3083 // Target percentages from weights, normalized to availPct.
3084 const wSum = weights.reduce( (a, w) => a + (Number( w ) || 0), 0 ) || n;
3085 const targetPct = weights.map( (w) => ((Number( w ) || 0) / wSum) * availPct );
3086
3087 // Lock columns that would be below min, then distribute the remainder
3088 // across the remaining columns proportionally to their targetPct.
3089 const locked = new Array( n ).fill( false );
3090 let lockedSum = 0;
3091 for ( let i = 0; i < n; i++ ) {
3092 if ( targetPct[i] < minPct[i] ) {
3093 locked[i] = true;
3094 lockedSum += minPct[i];
3095 }
3096 }
3097
3098 let remaining = availPct - lockedSum;
3099 const freeIdx = [];
3100 let freeTargetSum = 0;
3101 for ( let i = 0; i < n; i++ ) {
3102 if ( !locked[i] ) {
3103 freeIdx.push( i );
3104 freeTargetSum += targetPct[i];
3105 }
3106 }
3107
3108 const result = new Array( n ).fill( 0 );
3109 // Seed locked with their minima.
3110 for ( let i = 0; i < n; i++ ) {
3111 if ( locked[i] ) result[i] = minPct[i];
3112 }
3113
3114 if ( freeIdx.length === 0 ) {
3115 // everything locked exactly at min; any leftover (shouldn't happen)
3116 // would be ignored to keep simplicity and stability.
3117 return result;
3118 }
3119
3120 if ( remaining <= 0 ) {
3121 // nothing left to distribute; keep exactly mins on locked,
3122 // nothing for free (degenerate but consistent)
3123 return result;
3124 }
3125
3126 if ( freeTargetSum <= 0 ) {
3127 // distribute equally among free columns
3128 const each = remaining / freeIdx.length;
3129 freeIdx.forEach( (i) => (result[i] = each) );
3130 return result;
3131 }
3132
3133 // Distribute remaining proportionally to free columns' targetPct
3134 freeIdx.forEach( (i) => {
3135 result[i] = remaining * (targetPct[i] / freeTargetSum);
3136 } );
3137 return result;
3138 }
3139
3140 /** Apply a preset but guard it by minima; returns true if applied, false if skipped. */
3141 static _apply_preset_with_min_guard(builder, section_el, weights) {
3142 const row = section_el.querySelector( ':scope > .wpbc_bfb__row' );
3143 if ( !row ) return false;
3144
3145 const fitted = UI.WPBC_BFB_Layout_Chips._fit_weights_respecting_min( builder, row, weights );
3146 if ( !fitted ) {
3147 builder?._announce?.( 'Not enough space for this layout because of fields’ minimum widths.' );
3148 return false;
3149 }
3150
3151 // `fitted` already sums to the row’s available %, so we can apply bases directly.
3152 builder.layout.apply_bases_to_row( row, fitted );
3153 return true;
3154 }
3155
3156
3157 /**
3158 * Build and append layout chips for a section.
3159 *
3160 * @param {WPBC_Form_Builder} builder - The form builder instance.
3161 * @param {HTMLElement} section_el - The .wpbc_bfb__section element.
3162 * @param {HTMLElement} host_el - Container where chips should be rendered.
3163 * @returns {void}
3164 */
3165 static render_for_section(builder, section_el, host_el) {
3166
3167 if ( !builder || !section_el || !host_el ) {
3168 return;
3169 }
3170
3171 const row = section_el.querySelector( ':scope > .wpbc_bfb__row' );
3172 if ( !row ) {
3173 return;
3174 }
3175
3176 const cols = row.querySelectorAll( ':scope > .wpbc_bfb__column' ).length || 1;
3177
3178 // Clear host.
3179 host_el.innerHTML = '';
3180
3181 // Equal chip.
3182 host_el.appendChild(
3183 UI.WPBC_BFB_Layout_Chips._make_chip( builder, section_el, Array( cols ).fill( 1 ), 'Equal' )
3184 );
3185
3186 // Presets based on column count.
3187 const presets = builder.layout.build_presets_for_columns( cols );
3188 presets.forEach( (weights) => {
3189 host_el.appendChild(
3190 UI.WPBC_BFB_Layout_Chips._make_chip( builder, section_el, weights, null )
3191 );
3192 } );
3193
3194 // Custom chip.
3195 const customBtn = document.createElement( 'button' );
3196 customBtn.type = 'button';
3197 customBtn.className = 'wpbc_bfb__layout_chip';
3198 customBtn.textContent = 'Custom…';
3199 customBtn.title = `Enter ${cols} percentages`;
3200 customBtn.addEventListener( 'click', () => {
3201 const example = (cols === 2) ? '50,50' : (cols === 3 ? '20,60,20' : '25,25,25,25');
3202 const text = prompt( `Enter ${cols} percentages (comma or space separated):`, example );
3203 if ( text == null ) return;
3204 const weights = builder.layout.parse_weights( text );
3205 if ( weights.length !== cols ) {
3206 alert( `Please enter exactly ${cols} numbers.` );
3207 return;
3208 }
3209 // OLD:
3210 // builder.layout.apply_layout_preset( section_el, weights, builder.col_gap_percent );
3211 // Guarded apply:.
3212 if ( !UI.WPBC_BFB_Layout_Chips._apply_preset_with_min_guard( builder, section_el, weights ) ) {
3213 return;
3214 }
3215 host_el.querySelectorAll( '.wpbc_bfb__layout_chip' ).forEach( c => c.classList.remove( 'is-active' ) );
3216 customBtn.classList.add( 'is-active' );
3217 } );
3218 host_el.appendChild( customBtn );
3219 }
3220
3221 /**
3222 * Create a single layout chip button.
3223 *
3224 * @private
3225 * @param {WPBC_Form_Builder} builder
3226 * @param {HTMLElement} section_el
3227 * @param {number[]} weights
3228 * @param {string|null} label
3229 * @returns {HTMLButtonElement}
3230 */
3231 static _make_chip(builder, section_el, weights, label = null) {
3232
3233 const btn = document.createElement( 'button' );
3234 btn.type = 'button';
3235 btn.className = 'wpbc_bfb__layout_chip';
3236
3237 const title = label || builder.layout.format_preset_label( weights );
3238 btn.title = title;
3239
3240 // Visual miniature.
3241 const vis = document.createElement( 'div' );
3242 vis.className = 'wpbc_bfb__layout_chip-vis';
3243 const sum = weights.reduce( (a, b) => a + (Number( b ) || 0), 0 ) || 1;
3244 weights.forEach( (w) => {
3245 const bar = document.createElement( 'span' );
3246 bar.style.flex = `0 0 calc( ${((Number( w ) || 0) / sum * 100).toFixed( 3 )}% - 1.5px )`;
3247 vis.appendChild( bar );
3248 } );
3249 btn.appendChild( vis );
3250
3251 const txt = document.createElement( 'span' );
3252 txt.className = 'wpbc_bfb__layout_chip-label';
3253 txt.textContent = label || builder.layout.format_preset_label( weights );
3254 btn.appendChild( txt );
3255
3256 btn.addEventListener( 'click', () => {
3257 // OLD:
3258 // builder.layout.apply_layout_preset( section_el, weights, builder.col_gap_percent );
3259
3260 // NEW:
3261 if ( !UI.WPBC_BFB_Layout_Chips._apply_preset_with_min_guard( builder, section_el, weights ) ) {
3262 return; // do not toggle active if we didn't change layout
3263 }
3264
3265 btn.parentElement?.querySelectorAll( '.wpbc_bfb__layout_chip' ).forEach( c => c.classList.remove( 'is-active' ) );
3266 btn.classList.add( 'is-active' );
3267 } );
3268
3269 return btn;
3270 }
3271 };
3272
3273 /**
3274 * Selection controller for fields and announcements.
3275 */
3276 UI.WPBC_BFB_Selection_Controller = class extends UI.WPBC_BFB_Module {
3277
3278 init() {
3279
3280 this._selected_uid = null;
3281 this.builder.select_field = this.select_field.bind( this );
3282 this.builder.get_selected_field = this.get_selected_field.bind( this );
3283 this._on_clear = this.on_clear.bind( this );
3284
3285 // Centralized delete command used by keyboard + inspector + overlay.
3286 this.builder.delete_item = (el) => {
3287 if ( !el ) {
3288 return null;
3289 }
3290 const b = this.builder;
3291 const neighbor = b._find_neighbor_selectable?.( el ) || null;
3292 el.remove();
3293 // Use local Core constants (not a global) to avoid ReferenceErrors.
3294 b.bus?.emit?.( Core.WPBC_BFB_Events.FIELD_REMOVE, { el, id: el?.dataset?.id, uid: el?.dataset?.uid } );
3295 b.usage?.update_palette_ui?.();
3296 // Notify generic structure listeners, too:
3297 b.bus?.emit?.( Core.WPBC_BFB_Events.STRUCTURE_CHANGE, { reason: 'delete', el } );
3298 // Defer selection a tick so the DOM is fully settled before Inspector hydrates.
3299 requestAnimationFrame( () => {
3300 // This calls inspector.bind_to_field() and opens the Inspector panel.
3301 b.select_field?.( neighbor || null, { scrollIntoView: !!neighbor } );
3302 } );
3303 return neighbor;
3304 };
3305 this.builder.bus.on( Core.WPBC_BFB_Events.CLEAR_SELECTION, this._on_clear );
3306 this.builder.bus.on( Core.WPBC_BFB_Events.STRUCTURE_LOADED, this._on_clear );
3307 // delegated click selection (capture ensures we win before bubbling to containers).
3308 this._on_canvas_click = this._handle_canvas_click.bind( this );
3309 this.builder.pages_container.addEventListener( 'click', this._on_canvas_click, true );
3310 }
3311
3312 destroy() {
3313 this.builder.bus.off( Core.WPBC_BFB_Events.CLEAR_SELECTION, this._on_clear );
3314
3315 if ( this._on_canvas_click ) {
3316 this.builder.pages_container.removeEventListener( 'click', this._on_canvas_click, true );
3317 this._on_canvas_click = null;
3318 }
3319 }
3320
3321 /**
3322 * Delegated canvas click -> select closest field/section (inner beats outer).
3323 * @private
3324 * @param {MouseEvent} e
3325 */
3326 _handle_canvas_click(e) {
3327 const root = this.builder.pages_container;
3328 if ( !root ) return;
3329
3330 // Ignore clicks on controls/handles/resizers, etc.
3331 const IGNORE = [
3332 '.wpbc_bfb__overlay-controls',
3333 '.wpbc_bfb__layout_picker',
3334 '.wpbc_bfb__drag-handle',
3335 '.wpbc_bfb__field-remove-btn',
3336 '.wpbc_bfb__field-move-up',
3337 '.wpbc_bfb__field-move-down',
3338 '.wpbc_bfb__column-resizer'
3339 ].join( ',' );
3340
3341 if ( e.target.closest( IGNORE ) ) {
3342 return; // let those controls do their own thing.
3343 }
3344
3345 // Find the closest selectable (field OR section) from the click target.
3346 let hit = e.target.closest?.(
3347 `${Core.WPBC_BFB_DOM.SELECTORS.validField}, ${Core.WPBC_BFB_DOM.SELECTORS.section}, .wpbc_bfb__column`
3348 );
3349
3350 if ( !hit || !root.contains( hit ) ) {
3351 this.select_field( null ); // Clear selection on blank click.
3352 return; // Empty space is handled elsewhere.
3353 }
3354
3355 // NEW: if user clicked a COLUMN -> remember tab key on its SECTION, but still select the section.
3356 let preselect_tab_key = null;
3357 if ( hit.classList.contains( 'wpbc_bfb__column' ) ) {
3358 const row = hit.closest( '.wpbc_bfb__row' );
3359 const cols = row ? Array.from( row.querySelectorAll( ':scope > .wpbc_bfb__column' ) ) : [];
3360 const idx = Math.max( 0, cols.indexOf( hit ) );
3361 const sec = hit.closest( '.wpbc_bfb__section' );
3362 if ( sec ) {
3363 preselect_tab_key = String( idx + 1 ); // tabs are 1-based in ui-column-styles.js
3364 // Hint for the renderer (it reads this BEFORE rendering and restores the tab).
3365 sec.dataset.col_styles_active_tab = preselect_tab_key;
3366 // promote selection to the section (same UX as before).
3367 hit = sec;
3368 // NEW: visually mark which column is being edited
3369 if ( UI && UI.WPBC_BFB_Column_Styles && UI.WPBC_BFB_Column_Styles.set_selected_col_flag ) {
3370 UI.WPBC_BFB_Column_Styles.set_selected_col_flag( sec, preselect_tab_key );
3371 }
3372 }
3373 }
3374
3375 // Select and stop bubbling so outer containers don’t reselect a parent.
3376 this.select_field( hit );
3377 e.stopPropagation();
3378
3379 // Also set the tab after the inspector renders (works even if it was already open).
3380 if ( preselect_tab_key ) {
3381 (window.requestAnimationFrame || setTimeout)( function () {
3382 try {
3383 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3384 const tabs = ins && ins.querySelector( '[data-bfb-slot="column_styles"] [data-wpbc-tabs]' );
3385 if ( tabs && window.wpbc_ui_tabs && typeof window.wpbc_ui_tabs.set_active === 'function' ) {
3386 window.wpbc_ui_tabs.set_active( tabs, preselect_tab_key );
3387 }
3388 } catch ( _e ) {
3389 }
3390 }, 0 );
3391
3392 // Politely ask the Inspector to focus/open the "Column Styles" group and tab.
3393 wpbc_bfb__dispatch_event_safe(
3394 'wpbc_bfb:inspector_focus',
3395 {
3396 group : 'column_styles',
3397 tab_key: preselect_tab_key
3398 }
3399 );
3400 }
3401 }
3402
3403
3404 /**
3405 * Select a field element or clear selection.
3406 *
3407 * @param {HTMLElement|null} field_el
3408 * @param {{scrollIntoView?: boolean}} [opts = {}]
3409 */
3410 select_field(field_el, { scrollIntoView = false } = {}) {
3411 const root = this.builder.pages_container;
3412 const prevEl = this.get_selected_field?.() || null; // the one we’re leaving.
3413
3414 // Ignore elements not in the canvas.
3415 if ( field_el && !root.contains( field_el ) ) {
3416 field_el = null; // treat as "no selection".
3417 }
3418
3419 // NEW: if we are leaving a section, clear its column highlight
3420 if (
3421 prevEl && prevEl !== field_el &&
3422 prevEl.classList?.contains( 'wpbc_bfb__section' ) &&
3423 UI?.WPBC_BFB_Column_Styles?.clear_selected_col_flag
3424 ) {
3425 UI.WPBC_BFB_Column_Styles.clear_selected_col_flag( prevEl );
3426 }
3427
3428 // If we're leaving a field, permanently stop auto-name for it.
3429 if ( prevEl && prevEl !== field_el && prevEl.classList?.contains( 'wpbc_bfb__field' ) ) {
3430 prevEl.dataset.autoname = '0';
3431 prevEl.dataset.fresh = '0';
3432 }
3433
3434 root.querySelectorAll( '.is-selected' ).forEach( (n) => {
3435 n.classList.remove( 'is-selected' );
3436 } );
3437 if ( !field_el ) {
3438 const prev = this._selected_uid || null;
3439 this._selected_uid = null;
3440 this.builder.inspector?.clear?.();
3441 root.classList.remove( 'has-selection' );
3442 this.builder.bus.emit( Core.WPBC_BFB_Events.CLEAR_SELECTION, { prev_uid: prev, source: 'builder' } );
3443
3444 // Auto-open "Add Fields" when nothing is selected.
3445 wpbc_bfb__dispatch_event_safe(
3446 'wpbc_bfb:show_panel',
3447 {
3448 panel_id: 'wpbc_bfb__palette_add_new',
3449 tab_id : 'wpbc_tab_library'
3450 }
3451 );
3452
3453 return;
3454 }
3455 field_el.classList.add( 'is-selected' );
3456 this._selected_uid = field_el.getAttribute( 'data-uid' ) || null;
3457
3458 // Fallback: ensure sections announce themselves as type="section".
3459 if ( field_el.classList.contains( 'wpbc_bfb__section' ) && !field_el.dataset.type ) {
3460 field_el.dataset.type = 'section';
3461 }
3462
3463 if ( scrollIntoView ) {
3464 field_el.scrollIntoView( { behavior: 'smooth', block: 'center' } );
3465 }
3466 this.builder.inspector?.bind_to_field?.( field_el );
3467
3468 // Fallback: ensure inspector enhancers (incl. ValueSlider) run every bind.
3469 try {
3470 const ins = document.getElementById( 'wpbc_bfb__inspector' )
3471 || document.querySelector( '.wpbc_bfb__inspector' );
3472 if ( ins ) {
3473 UI.InspectorEnhancers?.scan?.( ins ); // runs all enhancers
3474 UI.WPBC_BFB_ValueSlider?.init_on?.( ins ); // extra belt-and-suspenders
3475 }
3476 } catch ( _ ) {
3477 }
3478
3479 // NEW: when selecting a section, reflect its active tab as the highlighted column.
3480 if ( field_el.classList.contains( 'wpbc_bfb__section' ) &&
3481 UI?.WPBC_BFB_Column_Styles?.set_selected_col_flag ) {
3482 var k = (field_el.dataset && field_el.dataset.col_styles_active_tab)
3483 ? field_el.dataset.col_styles_active_tab : '1';
3484 UI.WPBC_BFB_Column_Styles.set_selected_col_flag( field_el, k );
3485 }
3486
3487 // Keep sections & fields in the same flow:
3488 // 1) Generic hydrator for simple dataset-backed controls.
3489 if ( field_el ) {
3490 UI.WPBC_BFB_Inspector_Bridge._generic_hydrate_controls?.( this.builder, field_el );
3491 UI.WPBC_BFB_Inspector_Bridge._hydrate_special_controls?.( this.builder, field_el );
3492 }
3493
3494 // Auto-open Inspector when a user selects a field/section .
3495 wpbc_bfb__dispatch_event_safe(
3496 'wpbc_bfb:show_panel',
3497 {
3498 panel_id: 'wpbc_bfb__inspector',
3499 tab_id : 'wpbc_tab_inspector'
3500 }
3501 );
3502
3503 root.classList.add( 'has-selection' );
3504 this.builder.bus.emit( Core.WPBC_BFB_Events.SELECT, { uid: this._selected_uid, el: field_el } );
3505 const label = field_el?.querySelector( '.wpbc_bfb__field-label' )?.textContent || (field_el.classList.contains( 'wpbc_bfb__section' ) ? 'section' : '') || field_el?.dataset?.id || 'item';
3506 this.builder._announce( 'Selected ' + label + '.' );
3507 }
3508
3509 /** @returns {HTMLElement|null} */
3510 get_selected_field() {
3511 if ( !this._selected_uid ) {
3512 return null;
3513 }
3514 const esc_attr = Core.WPBC_BFB_Sanitize.esc_attr_value_for_selector( this._selected_uid );
3515 return this.builder.pages_container.querySelector( `.wpbc_bfb__field[data-uid="${esc_attr}"], .wpbc_bfb__section[data-uid="${esc_attr}"]` );
3516 }
3517
3518 /** @param {CustomEvent} ev */
3519 on_clear(ev) {
3520 const src = ev?.detail?.source ?? ev?.source;
3521 if ( src !== 'builder' ) {
3522 this.select_field( null );
3523 }
3524 }
3525
3526 };
3527
3528 /**
3529 * Bridges the builder with the Inspector and sanitizes id/name edits.
3530 */
3531 UI.WPBC_BFB_Inspector_Bridge = class extends UI.WPBC_BFB_Module {
3532
3533 init() {
3534 this._attach_inspector();
3535 this._bind_id_sanitizer();
3536 this._open_inspector_after_field_added();
3537 this._bind_focus_shortcuts();
3538 }
3539
3540 _attach_inspector() {
3541 const b = this.builder;
3542 const attach = () => {
3543 if ( typeof window.WPBC_BFB_Inspector === 'function' ) {
3544 b.inspector = new WPBC_BFB_Inspector( document.getElementById( 'wpbc_bfb__inspector' ), b );
3545 this._bind_id_sanitizer();
3546 document.removeEventListener( 'wpbc_bfb_inspector_ready', attach );
3547 }
3548 };
3549 // Ensure we bind after late ready as well.
3550 if ( typeof window.WPBC_BFB_Inspector === 'function' ) {
3551 attach();
3552 } else {
3553 b.inspector = {
3554 bind_to_field() {
3555 }, clear() {
3556 }
3557 };
3558 document.addEventListener( 'wpbc_bfb_inspector_ready', attach );
3559 setTimeout( attach, 0 );
3560 }
3561 }
3562
3563 /**
3564 * Listen for "focus" hints from the canvas and open the right group/tab.
3565 * - Supports: group === 'column_styles'
3566 * - Also scrolls the group into view.
3567 */
3568 _bind_focus_shortcuts() {
3569 /** @param {CustomEvent} e */
3570 const on_focus = (e) => {
3571 try {
3572 const grp_key = e && e.detail && e.detail.group;
3573 const tab_key = e && e.detail && e.detail.tab_key;
3574 if ( !grp_key ) {
3575 return;
3576 }
3577
3578 const ins = document.getElementById( 'wpbc_bfb__inspector' ) || document.querySelector( '.wpbc_bfb__inspector' );
3579 if ( !ins ) {
3580 return;
3581 }
3582
3583 if ( grp_key === 'column_styles' ) {
3584 // Find the Column Styles slot/group.
3585 const slot = ins.querySelector( '[data-bfb-slot="column_styles"]' ) || ins.querySelector( '[data-inspector-group-key="column_styles"]' );
3586 if ( slot ) {
3587 // Open collapsible container if present.
3588 const group_wrap = slot.closest( '.inspector__group' ) || slot.closest( '[data-inspector-group]' );
3589 if ( group_wrap && !group_wrap.classList.contains( 'is-open' ) ) {
3590 group_wrap.classList.add( 'is-open' );
3591 // Mirror ARIA state if your header uses aria-expanded.
3592 const header_btn = group_wrap.querySelector( '[aria-expanded]' );
3593 if ( header_btn ) {
3594 header_btn.setAttribute( 'aria-expanded', 'true' );
3595 }
3596 }
3597
3598 // Optional: set the requested tab key if tabs exist in this group.
3599 if ( tab_key ) {
3600 const tabs = slot.querySelector( '[data-wpbc-tabs]' );
3601 if ( tabs && window.wpbc_ui_tabs && typeof window.wpbc_ui_tabs.set_active === 'function' ) {
3602 window.wpbc_ui_tabs.set_active( tabs, String( tab_key ) );
3603 }
3604 }
3605
3606 // Bring into view for convenience.
3607 try {
3608 // Uncomment (Only if needed) this to AUTO SCROLL to specific COLUMN in the section:.
3609 // slot.scrollIntoView( { behavior: 'smooth', block: 'nearest' } );
3610 } catch ( _e ) {}
3611 }
3612 }
3613 } catch ( _e ) {}
3614 };
3615
3616 this._on_inspector_focus = on_focus;
3617 document.addEventListener( 'wpbc_bfb:inspector_focus', on_focus, true );
3618 }
3619
3620 destroy() {
3621 try {
3622 if ( this._on_inspector_focus ) {
3623 document.removeEventListener( 'wpbc_bfb:inspector_focus', this._on_inspector_focus, true );
3624 this._on_inspector_focus = null;
3625 }
3626 } catch ( _e ) {
3627 }
3628 }
3629
3630
3631 /**
3632 * Hydrate inspector inputs for "special" keys that we handle explicitly.
3633 * Works for both fields and sections.
3634 * @param {WPBC_Form_Builder} builder
3635 * @param {HTMLElement} sel
3636 */
3637 static _hydrate_special_controls(builder, sel) {
3638 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3639 if ( !ins || !sel ) return;
3640
3641 const setVal = (key, val) => {
3642 const ctrl = ins.querySelector( `[data-inspector-key="${key}"]` );
3643 if ( ctrl && 'value' in ctrl ) ctrl.value = String( val ?? '' );
3644 };
3645
3646 // Internal id / name / public html_id.
3647 setVal( 'id', sel.getAttribute( 'data-id' ) || '' );
3648 setVal( 'name', sel.getAttribute( 'data-name' ) || '' );
3649 setVal( 'html_id', sel.getAttribute( 'data-html_id' ) || '' );
3650
3651 // Section-only extras are harmless to set for fields (controls may not exist).
3652 setVal( 'cssclass', sel.getAttribute( 'data-cssclass' ) || '' );
3653 setVal( 'label', sel.getAttribute( 'data-label' ) || '' );
3654 }
3655
3656
3657 /**
3658 * Hydrate inspector inputs that declare a generic dataset mapping via
3659 * [data-inspector-key] but do NOT declare a custom value_from adapter.
3660 * This makes sections follow the same data flow as fields with almost no glue.
3661 *
3662 * @param {WPBC_Form_Builder} builder
3663 * @param {HTMLElement} sel - currently selected field/section
3664 */
3665 static _generic_hydrate_controls(builder, sel) {
3666 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3667 if ( !ins || !sel ) return;
3668
3669 const SKIP = /^(id|name|html_id|cssclass|label)$/; // handled by _hydrate_special_controls
3670
3671 // NEW: read schema for the selected element’s type.
3672 const schemas = window.WPBC_BFB_Schemas || {};
3673 const typeKey = (sel.dataset && sel.dataset.type) || '';
3674 const schemaEntry = schemas[typeKey] || null;
3675 const propsSchema = (schemaEntry && schemaEntry.schema && schemaEntry.schema.props) ? schemaEntry.schema.props : {};
3676 const hasOwn = Function.prototype.call.bind( Object.prototype.hasOwnProperty );
3677 const getDefault = (key) => {
3678 const meta = propsSchema[key];
3679 return (meta && hasOwn( meta, 'default' )) ? meta.default : undefined;
3680 };
3681
3682 ins.querySelectorAll( '[data-inspector-key]' ).forEach( (ctrl) => {
3683 const key = String( ctrl.dataset?.inspectorKey || '' ).toLowerCase();
3684 if ( !key || SKIP.test( key ) ) return;
3685
3686 // Element-level lock.
3687 const dl = (ctrl.dataset?.locked || '').trim().toLowerCase();
3688 if ( dl === '1' || dl === 'true' || dl === 'yes' ) return;
3689
3690 // Respect explicit adapters.
3691 if ( ctrl.dataset?.value_from || ctrl.dataset?.valueFrom ) return;
3692
3693 const raw = sel.dataset ? sel.dataset[key] : undefined;
3694 const hasRaw = sel.dataset ? hasOwn( sel.dataset, key ) : false;
3695 const defValue = getDefault( key );
3696
3697 // Best-effort control typing with schema default fallback when value is absent.
3698
3699 if ( ctrl instanceof HTMLInputElement && (ctrl.type === 'checkbox' || ctrl.type === 'radio') ) {
3700 // If dataset is missing the key entirely -> use schema default (boolean).
3701 if ( !hasRaw ) {
3702 ctrl.checked = !!defValue;
3703 } else {
3704 ctrl.checked = Core.WPBC_BFB_Sanitize.coerce_boolean( raw, !!defValue );
3705 }
3706 } else if ( 'value' in ctrl ) {
3707 if ( hasRaw ) {
3708 ctrl.value = (raw != null) ? String( raw ) : '';
3709 } else {
3710 ctrl.value = (defValue == null) ? '' : String( defValue );
3711 }
3712 }
3713 } );
3714 }
3715
3716 _bind_id_sanitizer() {
3717 const b = this.builder;
3718 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3719 if ( ! ins ) {
3720 return;
3721 }
3722 if ( ins.__wpbc_bfb_id_sanitizer_bound ) {
3723 return;
3724 }
3725 ins.__wpbc_bfb_id_sanitizer_bound = true;
3726
3727 const handler = (e) => {
3728
3729 const t = e.target;
3730 if ( !t || !('value' in t) ) {
3731 return;
3732 }
3733 const key = (t.dataset?.inspectorKey || '').toLowerCase();
3734 const sel = b.get_selected_field?.();
3735 const isSection = sel?.classList?.contains( 'wpbc_bfb__section' );
3736 if ( !sel ) return;
3737
3738 // Unified emitter that always includes the element reference.
3739 const EV = Core.WPBC_BFB_Events;
3740 // STRUCTURE_CHANGE can be "expensive" because other listeners may trigger full canvas refresh.
3741 // Debounce only continuous controls (e.g. value slider scrubbing) on the INPUT phase.
3742 const ensure_sc_debounce_state = () => {
3743 if ( b.__wpbc_bfb_sc_debounce_state ) {
3744 return b.__wpbc_bfb_sc_debounce_state;
3745 }
3746 b.__wpbc_bfb_sc_debounce_state = { timer_id: 0, pending_payload: null };
3747 return b.__wpbc_bfb_sc_debounce_state;
3748 };
3749
3750 const cancel_sc_debounced_emit = () => {
3751 const st = b.__wpbc_bfb_sc_debounce_state;
3752 if ( !st ) return;
3753 try { clearTimeout( st.timer_id ); } catch ( _ ) {}
3754 st.timer_id = 0;
3755 st.pending_payload = null;
3756 };
3757
3758 const bus_emit_change = (reason, extra = {}) => {
3759 // If we’re committing something (change/blur/etc), drop any pending "input" emit.
3760 cancel_sc_debounced_emit();
3761 b.bus?.emit?.( EV.STRUCTURE_CHANGE, { reason, el: sel, ...extra } );
3762 };
3763
3764 const bus_emit_change_debounced = (reason, extra = {}, wait_ms) => {
3765 const st = ensure_sc_debounce_state();
3766 const ms = Number.isFinite( wait_ms )
3767 ? wait_ms
3768 : (Number.isFinite( UI.STRUCTURE_CHANGE_DEBOUNCE_MS ) ? UI.STRUCTURE_CHANGE_DEBOUNCE_MS : 240);
3769
3770 // Capture the CURRENT selected element into the payload now (stable ref).
3771 st.pending_payload = { reason, el: sel, ...extra, debounced: true };
3772
3773 try { clearTimeout( st.timer_id ); } catch ( _ ) {}
3774 st.timer_id = setTimeout( function () {
3775 st.timer_id = 0;
3776 const payload = st.pending_payload;
3777 st.pending_payload = null;
3778 if ( payload ) {
3779 b.bus?.emit?.( EV.STRUCTURE_CHANGE, payload );
3780 }
3781 }, ms );
3782 };
3783
3784 // ---- FIELD/SECTION: internal id ----
3785 if ( key === 'id' ) {
3786 const unique = b.id.set_field_id( sel, t.value );
3787 if ( b.preview_mode && !isSection ) {
3788 b.render_preview( sel );
3789 }
3790 if ( t.value !== unique ) {
3791 t.value = unique;
3792 }
3793 bus_emit_change( 'id-change' );
3794 return;
3795 }
3796
3797 // ---- FIELD/SECTION: public HTML id ----
3798 if ( key === 'html_id' ) {
3799 const applied = b.id.set_field_html_id( sel, t.value );
3800 // For sections, also set the real DOM id so anchors/CSS can target it.
3801 if ( isSection ) {
3802 sel.id = applied || '';
3803 } else if ( b.preview_mode ) {
3804 b.render_preview( sel );
3805 }
3806 if ( t.value !== applied ) {
3807 t.value = applied;
3808 }
3809 bus_emit_change( 'html-id-change' );
3810 return;
3811 }
3812
3813 // ---- FIELDS ONLY: name ----
3814 if ( key === 'name' && !isSection ) {
3815
3816 // Live typing: sanitize only (NO uniqueness yet) to avoid "-2" spam
3817 if ( e.type === 'input' ) {
3818 const before = t.value;
3819 const sanitized = Core.WPBC_BFB_Sanitize.sanitize_html_name( before );
3820 if ( before !== sanitized ) {
3821 // optional: preserve caret to avoid jump
3822 const selStart = t.selectionStart, selEnd = t.selectionEnd;
3823 t.value = sanitized;
3824 try {
3825 t.setSelectionRange( selStart, selEnd );
3826 } catch ( _ ) {
3827 }
3828 }
3829 return; // uniqueness on change/blur
3830 }
3831
3832 // Commit (change/blur)
3833 const raw = String( t.value ?? '' ).trim();
3834
3835 if ( !raw ) {
3836 // RESEED: keep name non-empty and provisional (autoname stays ON)
3837 const S = Core.WPBC_BFB_Sanitize;
3838 const base = S.sanitize_html_name( sel.getAttribute( 'data-id' ) || sel.dataset.id || sel.dataset.type || 'field' );
3839 const uniq = b.id.ensure_unique_field_name( base, sel );
3840
3841 sel.setAttribute( 'data-name', uniq );
3842 sel.dataset.autoname = '1';
3843 sel.dataset.name_user_touched = '0';
3844
3845 // Keep DOM in sync if we’re not re-rendering
3846 if ( !b.preview_mode ) {
3847 const ctrl = sel.querySelector( 'input,textarea,select' );
3848 if ( ctrl ) ctrl.setAttribute( 'name', uniq );
3849 } else {
3850 b.render_preview( sel );
3851 }
3852
3853 if ( t.value !== uniq ) t.value = uniq;
3854 bus_emit_change( 'name-reseed' );
3855 return;
3856 }
3857
3858 // Non-empty commit: user takes control; disable autoname going forward
3859 sel.dataset.name_user_touched = '1';
3860 sel.dataset.autoname = '0';
3861
3862 const sanitized = Core.WPBC_BFB_Sanitize.sanitize_html_name( raw );
3863 const unique = b.id.set_field_name( sel, sanitized );
3864
3865 if ( !b.preview_mode ) {
3866 const ctrl = sel.querySelector( 'input,textarea,select' );
3867 if ( ctrl ) ctrl.setAttribute( 'name', unique );
3868 } else {
3869 b.render_preview( sel );
3870 }
3871
3872 if ( t.value !== unique ) t.value = unique;
3873 bus_emit_change( 'name-change' );
3874 return;
3875 }
3876
3877 // ---- SECTIONS & FIELDS: cssclass (live apply; no re-render) ----
3878 if ( key === 'cssclass' ) {
3879 const next = Core.WPBC_BFB_Sanitize.sanitize_css_classlist( t.value || '' );
3880 const desiredArr = next.split( /\s+/ ).filter( Boolean );
3881 const desiredSet = new Set( desiredArr );
3882
3883 // Core classes are never touched.
3884 const isCore = (cls) => cls === 'is-selected' || cls.startsWith( 'wpbc_' );
3885
3886 // Snapshot before mutating (DOMTokenList is live).
3887 const beforeClasses = Array.from( sel.classList );
3888 const customBefore = beforeClasses.filter( (c) => !isCore( c ) );
3889
3890 // Remove stray non-core classes not in desired.
3891 customBefore.forEach( (c) => {
3892 if ( !desiredSet.has( c ) ) sel.classList.remove( c );
3893 } );
3894
3895 // Add missing desired classes in one go.
3896 const missing = desiredArr.filter( (c) => !customBefore.includes( c ) );
3897 if ( missing.length ) sel.classList.add( ...missing );
3898
3899 // Keep dataset in sync (avoid useless attribute writes).
3900 if ( sel.getAttribute( 'data-cssclass' ) !== next ) {
3901 sel.setAttribute( 'data-cssclass', next );
3902 }
3903
3904 // Emit only if something actually changed.
3905 const afterClasses = Array.from( sel.classList );
3906 const changed = afterClasses.length !== beforeClasses.length || beforeClasses.some( (c, i) => c !== afterClasses[i] );
3907
3908 const detail = { key: 'cssclass', phase: e.type };
3909 if ( isSection ) {
3910 bus_emit_change( 'cssclass-change', detail );
3911 } else {
3912 bus_emit_change( 'prop-change', detail );
3913 }
3914 return;
3915 }
3916
3917
3918 // ---- SECTIONS: label ----
3919 if ( isSection && key === 'label' ) {
3920 const val = String( t.value ?? '' );
3921 sel.setAttribute( 'data-label', val );
3922 bus_emit_change( 'label-change' );
3923 return;
3924 }
3925
3926 // ---- FIELDS: label (auto-name while typing; freeze on commit) ----
3927 if ( !isSection && key === 'label' ) {
3928 const val = String( t.value ?? '' );
3929 sel.dataset.label = val;
3930
3931 // while typing, allow auto-name (if flags permit)
3932 try {
3933 Core.WPBC_BFB_Field_Base.maybe_autoname_from_label( b, sel, val );
3934 } catch ( _ ) {
3935 }
3936
3937 // if user committed the label (blur/change), freeze future auto-name
3938 if ( e.type !== 'input' ) {
3939 sel.dataset.autoname = '0'; // stop future label->name sync
3940 sel.dataset.fresh = '0'; // also kill the "fresh" escape hatch
3941 }
3942
3943 // Optional UI nicety: disable Name when auto is ON, enable when OFF
3944 const ins = document.getElementById( 'wpbc_bfb__inspector' );
3945 const nameCtrl = ins?.querySelector( '[data-inspector-key="name"]' );
3946 if ( nameCtrl ) {
3947 const autoActive =
3948 (sel.dataset.autoname ?? '1') !== '0' &&
3949 sel.dataset.name_user_touched !== '1' &&
3950 sel.dataset.was_loaded !== '1';
3951 nameCtrl.toggleAttribute( 'disabled', autoActive );
3952 if ( autoActive && !nameCtrl.placeholder ) {
3953 nameCtrl.placeholder = b?.i18n?.auto_from_label ?? 'auto — from label';
3954 }
3955 if ( !autoActive && nameCtrl.placeholder === (b?.i18n?.auto_from_label ?? 'auto — from label') ) {
3956 nameCtrl.placeholder = '';
3957 }
3958 }
3959
3960 // Always re-render the preview so label changes are visible immediately.
3961 b.render_preview( sel );
3962 bus_emit_change( 'label-change' );
3963 return;
3964 }
3965
3966
3967 // ---- DEFAULT (GENERIC): dataset writer for both fields & sections ----
3968 // Any inspector control with [data-inspector-key] that doesn't have a custom
3969 // adapter/value_from will simply read/write sel.dataset[key].
3970 if ( key ) {
3971
3972 const selfLocked = /^(1|true|yes)$/i.test( (t.dataset?.locked || '').trim() );
3973 if ( selfLocked ) {
3974 return;
3975 }
3976
3977 // Skip keys we handled above to avoid double work.
3978 if ( key === 'id' || key === 'name' || key === 'html_id' || key === 'cssclass' || key === 'label' ) {
3979 return;
3980 }
3981 let nextVal = '';
3982 if ( t instanceof HTMLInputElement && (t.type === 'checkbox' || t.type === 'radio') ) {
3983 nextVal = t.checked ? '1' : '';
3984 } else if ( 'value' in t ) {
3985 nextVal = String( t.value ?? '' );
3986 }
3987 // Persist to dataset.
3988 if ( sel?.dataset ) sel.dataset[key] = nextVal;
3989
3990 // Generator controls are "UI inputs" — avoid STRUCTURE_CHANGE spam while dragging/typing.
3991 const is_gen_key = (key.indexOf( 'gen_' ) === 0);
3992
3993 // Re-render on visual keys so preview stays in sync (calendar label/help, etc.).
3994 const visualKeys = new Set( [ 'help', 'placeholder', 'min_width', 'cssclass' ] );
3995 if ( !isSection && (visualKeys.has( key ) || key.startsWith( 'ui_' )) ) {
3996 // Light heuristic: only re-render on commit for heavy inputs; live for short ones is fine.
3997 if ( e.type === 'change' || key === 'help' || key === 'placeholder' ) {
3998 b.render_preview( sel );
3999 }
4000 }
4001
4002 if ( !(is_gen_key && e.type === 'input') ) {
4003 // Debounce continuous value slider input events to avoid full-canvas refresh spam.
4004 // We detect the slider group via [data-len-group] wrapper.
4005 const is_len_group_ctrl = !!(t && t.closest && t.closest( '[data-len-group]' ));
4006
4007 if ( is_len_group_ctrl && e.type === 'input' ) {
4008 bus_emit_change_debounced( 'prop-change', { key, phase: e.type } );
4009 } else {
4010 bus_emit_change( 'prop-change', { key, phase: e.type } );
4011 }
4012 }
4013 return;
4014 }
4015 };
4016
4017 ins.addEventListener( 'change', handler, true );
4018 // reflect instantly while typing as well.
4019 ins.addEventListener( 'input', handler, true );
4020 }
4021
4022 /**
4023 * Open Inspector after a field is added.
4024 * @private
4025 */
4026 _open_inspector_after_field_added() {
4027 const EV = Core.WPBC_BFB_Events;
4028 this.builder?.bus?.on?.( EV.FIELD_ADD, (e) => {
4029 const el = e?.detail?.el || null;
4030 if ( el && this.builder?.select_field ) {
4031 this.builder.select_field( el, { scrollIntoView: true } );
4032 }
4033 // Show Inspector Palette.
4034 wpbc_bfb__dispatch_event_safe(
4035 'wpbc_bfb:show_panel',
4036 {
4037 panel_id: 'wpbc_bfb__inspector',
4038 tab_id : 'wpbc_tab_inspector'
4039 }
4040 );
4041 } );
4042 }
4043 };
4044
4045 /**
4046 * Keyboard shortcuts for selection, deletion, and movement.
4047 */
4048 UI.WPBC_BFB_Keyboard_Controller = class extends UI.WPBC_BFB_Module {
4049 init() {
4050 this._on_key = this.on_key.bind( this );
4051 document.addEventListener( 'keydown', this._on_key, true );
4052 }
4053
4054 destroy() {
4055 document.removeEventListener( 'keydown', this._on_key, true );
4056 }
4057
4058 /** @param {KeyboardEvent} e */
4059 on_key(e) {
4060 const b = this.builder;
4061 const is_typing = this._is_typing_anywhere();
4062 if ( e.key === 'Escape' ) {
4063 if ( is_typing ) {
4064 return;
4065 }
4066 this.builder.bus.emit( Core.WPBC_BFB_Events.CLEAR_SELECTION, { source: 'esc' } );
4067 return;
4068 }
4069 const selected = b.get_selected_field?.();
4070 if ( !selected || is_typing ) {
4071 return;
4072 }
4073 if ( e.key === 'Delete' || e.key === 'Backspace' ) {
4074 e.preventDefault();
4075 b.delete_item?.( selected );
4076 return;
4077 }
4078 if ( (e.altKey || e.ctrlKey || e.metaKey) && (e.key === 'ArrowUp' || e.key === 'ArrowDown') && !e.shiftKey ) {
4079 e.preventDefault();
4080 const dir = (e.key === 'ArrowUp') ? 'up' : 'down';
4081 b.move_item?.( selected, dir );
4082 return;
4083 }
4084 if ( e.key === 'Enter' ) {
4085 e.preventDefault();
4086 b.select_field( selected, { scrollIntoView: true } );
4087 }
4088 }
4089
4090 /** @returns {boolean} */
4091 _is_typing_anywhere() {
4092 const a = document.activeElement;
4093 const tag = a?.tagName;
4094 if ( tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || (a?.isContentEditable === true) ) {
4095 return true;
4096 }
4097 const ins = document.getElementById( 'wpbc_bfb__inspector' );
4098 return !!(ins && a && ins.contains( a ));
4099 }
4100 };
4101
4102 /**
4103 * Column resize logic for section rows.
4104 */
4105 UI.WPBC_BFB_Resize_Controller = class extends UI.WPBC_BFB_Module {
4106 init() {
4107 this.builder.init_resize_handler = this.handle_resize.bind( this );
4108 }
4109
4110 /**
4111 * read the CSS var (kept local so it doesn’t depend on the Min-Width module)
4112 *
4113 * @param col
4114 * @returns {number|number}
4115 * @private
4116 */
4117 _get_col_min_px(col) {
4118 const v = getComputedStyle( col ).getPropertyValue( '--wpbc-col-min' ) || '0';
4119 const n = parseFloat( v );
4120 return Number.isFinite( n ) ? Math.max( 0, n ) : 0;
4121 }
4122
4123 /** @param {MouseEvent} e */
4124 handle_resize(e) {
4125 const b = this.builder;
4126 e.preventDefault();
4127 if ( e.button !== 0 ) return;
4128
4129 const resizer = e.currentTarget;
4130 const row_el = resizer.parentElement;
4131 const cols = Array.from( row_el.querySelectorAll( ':scope > .wpbc_bfb__column' ) );
4132 const left_col = resizer?.previousElementSibling;
4133 const right_col = resizer?.nextElementSibling;
4134 if ( !left_col || !right_col || !left_col.classList.contains( 'wpbc_bfb__column' ) || !right_col.classList.contains( 'wpbc_bfb__column' ) ) return;
4135
4136 const left_index = cols.indexOf( left_col );
4137 const right_index = cols.indexOf( right_col );
4138 if ( left_index === -1 || right_index !== left_index + 1 ) return;
4139
4140 const start_x = e.clientX;
4141 const left_start_px = left_col.getBoundingClientRect().width;
4142 const right_start_px = right_col.getBoundingClientRect().width;
4143 const pair_px = Math.max( 0, left_start_px + right_start_px );
4144
4145 const gp = b.col_gap_percent;
4146 const computed = b.layout.compute_effective_bases_from_row( row_el, gp );
4147 const available = computed.available; // % of the “full 100” after gaps
4148 const bases = computed.bases.slice( 0 ); // current effective %
4149 const pair_avail = bases[left_index] + bases[right_index];
4150
4151 // Bail if we can’t compute sane deltas.
4152 if (!pair_px || !Number.isFinite(pair_avail) || pair_avail <= 0) return;
4153
4154 // --- MIN CLAMPS (pixels) -------------------------------------------------
4155 const pctToPx = (pct) => (pair_px * (pct / pair_avail)); // pair-local percent -> px
4156 const genericMinPct = Math.min( 0.1, available ); // original 0.1% floor (in “available %” space)
4157 const genericMinPx = pctToPx( genericMinPct );
4158
4159 const leftMinPx = Math.max( this._get_col_min_px( left_col ), genericMinPx );
4160 const rightMinPx = Math.max( this._get_col_min_px( right_col ), genericMinPx );
4161
4162 // freeze text selection + cursor
4163 const prev_user_select = document.body.style.userSelect;
4164 document.body.style.userSelect = 'none';
4165 row_el.style.cursor = 'col-resize';
4166
4167 const on_mouse_move = (ev) => {
4168 if ( !pair_px ) return;
4169
4170 // work in pixels, clamp by each side’s min
4171 const delta_px = ev.clientX - start_x;
4172 let newLeftPx = left_start_px + delta_px;
4173 newLeftPx = Math.max( leftMinPx, Math.min( pair_px - rightMinPx, newLeftPx ) );
4174 const newRightPx = pair_px - newLeftPx;
4175
4176 // translate back to pair-local percentages
4177 const newLeftPct = (newLeftPx / pair_px) * pair_avail;
4178 const newBases = bases.slice( 0 );
4179 newBases[left_index] = newLeftPct;
4180 newBases[right_index] = pair_avail - newLeftPct;
4181
4182 b.layout.apply_bases_to_row( row_el, newBases );
4183 };
4184
4185 const on_mouse_up = () => {
4186 document.removeEventListener( 'mousemove', on_mouse_move );
4187 document.removeEventListener( 'mouseup', on_mouse_up );
4188 window.removeEventListener( 'mouseup', on_mouse_up );
4189 document.removeEventListener( 'mouseleave', on_mouse_up );
4190 document.body.style.userSelect = prev_user_select || '';
4191 row_el.style.cursor = '';
4192
4193 // normalize to the row’s available % again
4194 const normalized = b.layout.compute_effective_bases_from_row( row_el, gp );
4195 b.layout.apply_bases_to_row( row_el, normalized.bases );
4196 };
4197
4198 document.addEventListener( 'mousemove', on_mouse_move );
4199 document.addEventListener( 'mouseup', on_mouse_up );
4200 window.addEventListener( 'mouseup', on_mouse_up );
4201 document.addEventListener( 'mouseleave', on_mouse_up );
4202 }
4203
4204 };
4205
4206 /**
4207 * Page and section creation, rebuilding, and nested Sortable setup.
4208 */
4209 UI.WPBC_BFB_Pages_Sections = class extends UI.WPBC_BFB_Module {
4210
4211 init() {
4212 this.builder.add_page = (opts) => this.add_page( opts );
4213 this.builder.add_section = (container, cols) => this.add_section( container, cols );
4214 this.builder.rebuild_section = (section_data, container) => this.rebuild_section( section_data, container );
4215 this.builder.init_all_nested_sortables = (el) => this.init_all_nested_sortables( el );
4216 this.builder.init_section_sortable = (el) => this.init_section_sortable( el );
4217 this.builder.pages_sections = this;
4218 }
4219
4220 /**
4221 * Give every field/section in a cloned subtree a fresh data-uid so
4222 * uniqueness checks don't exclude their originals.
4223 */
4224 _retag_uids_in_subtree(root) {
4225 const b = this.builder;
4226 if ( !root ) return;
4227 const nodes = [];
4228 if ( root.classList?.contains( 'wpbc_bfb__section' ) || root.classList?.contains( 'wpbc_bfb__field' ) ) {
4229 nodes.push( root );
4230 }
4231 nodes.push( ...root.querySelectorAll( '.wpbc_bfb__section, .wpbc_bfb__field' ) );
4232 nodes.forEach( (el) => {
4233 const prefix = el.classList.contains( 'wpbc_bfb__section' ) ? 's' : 'f';
4234 el.dataset.uid = `${prefix}-${++b._uid_counter}-${Date.now()}-${Math.random().toString( 36 ).slice( 2, 7 )}`;
4235 } );
4236 }
4237
4238 /**
4239 * Bump "foo", "foo-2", "foo-3", ...
4240 */
4241 _make_unique(base, taken) {
4242 const s = Core.WPBC_BFB_Sanitize;
4243 let v = String( base || '' );
4244 if ( !v ) v = 'field';
4245 const m = v.match( /-(\d+)$/ );
4246 let n = m ? (parseInt( m[1], 10 ) || 1) : 1;
4247 let stem = m ? v.replace( /-\d+$/, '' ) : v;
4248 while ( taken.has( v ) ) {
4249 n = Math.max( 2, n + 1 );
4250 v = `${stem}-${n}`;
4251 }
4252 taken.add( v );
4253 return v;
4254 }
4255
4256 /**
4257 * Strict, one-pass de-duplication for a newly-inserted subtree.
4258 * - Ensures unique data-id (internal), data-name (fields), data-html_id (public)
4259 * - Also updates DOM: <section id>, <input id>, <label for>, and input[name].
4260 */
4261 _dedupe_subtree_strict(root) {
4262 const b = this.builder;
4263 const s = Core.WPBC_BFB_Sanitize;
4264 if ( !root || !b?.pages_container ) return;
4265
4266 // 1) Build "taken" sets from outside the subtree.
4267 const takenDataId = new Set();
4268 const takenDataName = new Set();
4269 const takenHtmlId = new Set();
4270 const takenDomId = new Set();
4271
4272 // All fields/sections outside root
4273 b.pages_container.querySelectorAll( '.wpbc_bfb__field, .wpbc_bfb__section' ).forEach( el => {
4274 if ( root.contains( el ) ) return;
4275 const did = el.getAttribute( 'data-id' );
4276 const dnam = el.getAttribute( 'data-name' );
4277 const hid = el.getAttribute( 'data-html_id' );
4278 if ( did ) takenDataId.add( did );
4279 if ( dnam ) takenDataName.add( dnam );
4280 if ( hid ) takenHtmlId.add( hid );
4281 } );
4282
4283 // All DOM ids outside root (labels, inputs, anything)
4284 document.querySelectorAll( '[id]' ).forEach( el => {
4285 if ( root.contains( el ) ) return;
4286 if ( el.id ) takenDomId.add( el.id );
4287 } );
4288
4289 const nodes = [];
4290 if ( root.classList?.contains( 'wpbc_bfb__section' ) || root.classList?.contains( 'wpbc_bfb__field' ) ) {
4291 nodes.push( root );
4292 }
4293 nodes.push( ...root.querySelectorAll( '.wpbc_bfb__section, .wpbc_bfb__field' ) );
4294
4295 // 2) Walk the subtree and fix collisions deterministically.
4296 nodes.forEach( el => {
4297 const isField = el.classList.contains( 'wpbc_bfb__field' );
4298 const isSection = el.classList.contains( 'wpbc_bfb__section' );
4299
4300 // INTERNAL data-id
4301 {
4302 const raw = el.getAttribute( 'data-id' ) || '';
4303 const base = s.sanitize_html_id( raw ) || (isSection ? 'section' : 'field');
4304 const uniq = this._make_unique( base, takenDataId );
4305 if ( uniq !== raw ) el.setAttribute( 'data-id', uniq );
4306 }
4307
4308 // HTML name (fields only)
4309 if ( isField ) {
4310 const raw = el.getAttribute( 'data-name' ) || '';
4311 if ( raw ) {
4312 const base = s.sanitize_html_name( raw );
4313 const uniq = this._make_unique( base, takenDataName );
4314 if ( uniq !== raw ) {
4315 el.setAttribute( 'data-name', uniq );
4316 // Update inner control immediately
4317 const input = el.querySelector( 'input, textarea, select' );
4318 if ( input ) input.setAttribute( 'name', uniq );
4319 }
4320 }
4321 }
4322
4323 // Public HTML id (fields + sections)
4324 {
4325 const raw = el.getAttribute( 'data-html_id' ) || '';
4326 if ( raw ) {
4327 const base = s.sanitize_html_id( raw );
4328 // Reserve against BOTH known data-html_id and real DOM ids.
4329 const combinedTaken = new Set( [ ...takenHtmlId, ...takenDomId ] );
4330 let candidate = this._make_unique( base, combinedTaken );
4331 // Record into the real sets so future checks see the reservation.
4332 takenHtmlId.add( candidate );
4333 takenDomId.add( candidate );
4334
4335 if ( candidate !== raw ) el.setAttribute( 'data-html_id', candidate );
4336
4337 // Reflect to DOM immediately
4338 if ( isSection ) {
4339 el.id = candidate || '';
4340 } else {
4341 const input = el.querySelector( 'input, textarea, select' );
4342 const label = el.querySelector( 'label.wpbc_bfb__field-label' );
4343 if ( input ) input.id = candidate || '';
4344 if ( label ) label.htmlFor = candidate || '';
4345 }
4346 } else if ( isSection ) {
4347 // Ensure no stale DOM id if data-html_id was cleared
4348 el.removeAttribute( 'id' );
4349 }
4350 }
4351 } );
4352 }
4353
4354 _make_add_columns_control(page_el, section_container, insert_pos = 'bottom') {
4355
4356 // Accept insert_pos ('top'|'bottom'), default 'bottom'.
4357
4358 const tpl = document.getElementById( 'wpbc_bfb__add_columns_template' );
4359 if ( !tpl ) {
4360 return null;
4361 }
4362
4363 // Clone *contents* (not the id), unhide, and add a page-scoped class.
4364 const src = (tpl.content && tpl.content.firstElementChild) ? tpl.content.firstElementChild : tpl.firstElementChild;
4365 if ( !src ) {
4366 return null;
4367 }
4368
4369 const clone = src.cloneNode( true );
4370 clone.removeAttribute( 'hidden' );
4371 if ( clone.id ) {
4372 clone.removeAttribute( 'id' );
4373 }
4374 clone.querySelectorAll( '[id]' ).forEach( n => n.removeAttribute( 'id' ) );
4375
4376 // Mark where this control inserts sections.
4377 clone.dataset.insert = insert_pos; // 'top' | 'bottom'
4378
4379 // // Optional UI hint for users (keeps existing markup intact).
4380 // const hint = clone.querySelector( '.nav-tab-text .selected_value' );
4381 // if ( hint ) {
4382 // hint.textContent = (insert_pos === 'top') ? ' (add at top)' : ' (add at bottom)';
4383 // }
4384
4385 // Click on options - add section with N columns.
4386 clone.addEventListener( 'click', (e) => {
4387 const a = e.target.closest( '.ul_dropdown_menu_li_action_add_sections' );
4388 if ( !a ) {
4389 return;
4390 }
4391 e.preventDefault();
4392
4393 // Read N either from data-cols or fallback to parsing text like "3 Columns".
4394 let cols = parseInt( a.dataset.cols || (a.textContent.match( /\b(\d+)\s*Column/i )?.[1] ?? '1'), 10 );
4395 cols = Math.max( 1, Math.min( 4, cols ) );
4396
4397 // NEW: honor the control's insertion position
4398 this.add_section( section_container, cols, insert_pos );
4399
4400 // Reflect last choice (unchanged)
4401 const val = clone.querySelector( '.selected_value' );
4402 if ( val ) {
4403 val.textContent = ` (${cols})`;
4404 }
4405 } );
4406
4407 return clone;
4408 }
4409
4410 /**
4411 * @param {{scroll?: boolean}} [opts = {}]
4412 * @returns {HTMLElement}
4413 */
4414 add_page({ scroll = true } = {}) {
4415 const b = this.builder;
4416 const page_el = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__panel wpbc_bfb__panel--preview wpbc_bfb_form wpbc_container wpbc_form wpbc_container_booking_form' );
4417 page_el.setAttribute( 'data-page', ++b.page_counter );
4418
4419 // "Page 1 | X" - Render page Title with Remove X button.
4420 const controls_html = UI.render_wp_template( 'wpbc-bfb-tpl-page-remove', { page_number: b.page_counter } );
4421 page_el.innerHTML = controls_html + '<div class="wpbc_bfb__form_preview_section_container wpbc_wizard__border_container"></div>';
4422
4423 b.pages_container.appendChild( page_el );
4424 if ( scroll ) {
4425 page_el.scrollIntoView( { behavior: 'smooth', block: 'start' } );
4426 }
4427
4428 const section_container = page_el.querySelector( '.wpbc_bfb__form_preview_section_container' );
4429 const section_count_on_add_page = 2;
4430 this.init_section_sortable( section_container );
4431 this.add_section( section_container, section_count_on_add_page );
4432
4433 // Dropdown control cloned from the hidden template.
4434 const controls_host_top = page_el.querySelector( '.wpbc_bfb__controls' );
4435 const ctrl_top = this._make_add_columns_control( page_el, section_container, 'top' );
4436 if ( ctrl_top ) {
4437 controls_host_top.appendChild( ctrl_top );
4438 }
4439 // Bottom control bar after the section container.
4440 const controls_host_bottom = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__controls wpbc_bfb__controls--bottom' );
4441 section_container.after( controls_host_bottom );
4442 const ctrl_bottom = this._make_add_columns_control( page_el, section_container, 'bottom' );
4443 if ( ctrl_bottom ) {
4444 controls_host_bottom.appendChild( ctrl_bottom );
4445 }
4446
4447 return page_el;
4448 }
4449
4450 /**
4451 * @param {HTMLElement} container
4452 * @param {number} cols
4453 * @param {'top'|'bottom'} [insert_pos='bottom'] // NEW
4454 */
4455 add_section(container, cols, insert_pos = 'bottom') {
4456 const b = this.builder;
4457 cols = Math.max( 1, parseInt( cols, 10 ) || 1 );
4458
4459 const section = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__section' );
4460 section.setAttribute( 'data-id', `section-${++b.section_counter}-${Date.now()}` );
4461 section.setAttribute( 'data-uid', `s-${++b._uid_counter}-${Date.now()}-${Math.random().toString( 36 ).slice( 2, 7 )}` );
4462 section.setAttribute( 'data-type', 'section' );
4463 section.setAttribute( 'data-label', 'Section' );
4464 section.setAttribute( 'data-columns', String( cols ) );
4465 // Do not persist or seed per-column styles by default (opt-in via inspector).
4466
4467 const row = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__row wpbc__row' );
4468 for ( let i = 0; i < cols; i++ ) {
4469 const col = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__column wpbc__field' );
4470 col.style.flexBasis = (100 / cols) + '%';
4471 // No default CSS vars here; real columns remain unaffected until user activates styles.
4472 b.init_sortable?.( col );
4473 row.appendChild( col );
4474 if ( i < cols - 1 ) {
4475 const resizer = Core.WPBC_Form_Builder_Helper.create_element( 'div', 'wpbc_bfb__column-resizer' );
4476 resizer.addEventListener( 'mousedown', b.init_resize_handler );
4477 row.appendChild( resizer );
4478 }
4479 }
4480 section.appendChild( row );
4481 b.layout.set_equal_bases( row, b.col_gap_percent );
4482 b.add_overlay_toolbar( section );
4483 section.setAttribute( 'tabindex', '0' );
4484 this.init_all_nested_sortables( section );
4485
4486 // Insertion policy: top | bottom.
4487 if ( insert_pos === 'top' && container.firstElementChild ) {
4488 container.insertBefore( section, container.firstElementChild );
4489 } else {
4490 container.appendChild( section );
4491 }
4492 }
4493
4494 /**
4495 * @param {Object} section_data
4496 * @param {HTMLElement} container
4497 * @returns {HTMLElement} The rebuilt section element.
4498 */
4499 rebuild_section(section_data, container) {
4500 const b = this.builder;
4501 const cols_data = Array.isArray( section_data?.columns ) ? section_data.columns : [];
4502 this.add_section( container, cols_data.length || 1 );
4503 const section = container.lastElementChild;
4504 if ( !section.dataset.uid ) {
4505 section.setAttribute( 'data-uid', `s-${++b._uid_counter}-${Date.now()}-${Math.random().toString( 36 ).slice( 2, 7 )}` );
4506 }
4507 section.setAttribute( 'data-id', section_data?.id || `section-${++b.section_counter}-${Date.now()}` );
4508 section.setAttribute( 'data-type', 'section' );
4509 section.setAttribute( 'data-label', section_data?.label || 'Section' );
4510 section.setAttribute( 'data-columns', String( (section_data?.columns || []).length || 1 ) );
4511 // Persisted attributes
4512 if ( section_data?.html_id ) {
4513 section.setAttribute( 'data-html_id', String( section_data.html_id ) );
4514 // give the container a real id so anchors/CSS can target it
4515 section.id = String( section_data.html_id );
4516 }
4517
4518 // NEW: restore persisted per-column styles (raw JSON string).
4519 if ( section_data?.col_styles != null ) {
4520 const json = String( section_data.col_styles );
4521 section.setAttribute( 'data-col_styles', json );
4522 try {
4523 section.dataset.col_styles = json;
4524 } catch ( _e ) {
4525 }
4526 }
4527 // (No render_preview() call here on purpose: sections’ builder DOM uses .wpbc_bfb__row/.wpbc_bfb__column.)
4528
4529
4530 if ( section_data?.cssclass ) {
4531 section.setAttribute( 'data-cssclass', String( section_data.cssclass ) );
4532 // keep core classes, then add custom class(es)
4533 String( section_data.cssclass ).split( /\s+/ ).filter( Boolean ).forEach( cls => section.classList.add( cls ) );
4534 }
4535
4536 const row = section.querySelector( '.wpbc_bfb__row' );
4537 // Delegate parsing + activation + application to the Column Styles service.
4538 try {
4539 const json = section.getAttribute( 'data-col_styles' )
4540 || (section.dataset ? (section.dataset.col_styles || '') : '');
4541 const arr = UI.WPBC_BFB_Column_Styles.parse_col_styles( json );
4542 UI.WPBC_BFB_Column_Styles.apply( section, arr );
4543 } catch ( _e ) {
4544 }
4545
4546 cols_data.forEach( (col_data, index) => {
4547 const columns_only = row.querySelectorAll( ':scope > .wpbc_bfb__column' );
4548 const col = columns_only[index];
4549 col.style.flexBasis = col_data.width || '100%';
4550 (col_data.items || []).forEach( (item) => {
4551 if ( !item || !item.type ) {
4552 return;
4553 }
4554 if ( item.type === 'field' ) {
4555 const el = b.build_field( item.data );
4556 if ( el ) {
4557 col.appendChild( el );
4558 b.trigger_field_drop_callback( el, 'load' );
4559 }
4560 return;
4561 }
4562 if ( item.type === 'section' ) {
4563 this.rebuild_section( item.data, col );
4564 }
4565 } );
4566 } );
4567 const computed = b.layout.compute_effective_bases_from_row( row, b.col_gap_percent );
4568 b.layout.apply_bases_to_row( row, computed.bases );
4569 this.init_all_nested_sortables( section );
4570
4571 // NEW: retag UIDs first (so uniqueness checks don't exclude originals), then dedupe all keys.
4572 this._retag_uids_in_subtree( section );
4573 this._dedupe_subtree_strict( section );
4574 return section;
4575 }
4576
4577 /** @param {HTMLElement} container */
4578 init_all_nested_sortables(container) {
4579 const b = this.builder;
4580 if ( container.classList.contains( 'wpbc_bfb__form_preview_section_container' ) ) {
4581 this.init_section_sortable( container );
4582 }
4583 container.querySelectorAll( '.wpbc_bfb__section' ).forEach( (section) => {
4584 section.querySelectorAll( '.wpbc_bfb__column' ).forEach( (col) => {
4585 this.init_section_sortable( col );
4586 } );
4587 } );
4588 }
4589
4590 /** @param {HTMLElement} container */
4591 init_section_sortable(container) {
4592 const b = this.builder;
4593 if ( !container ) {
4594 return;
4595 }
4596 const is_column = container.classList.contains( 'wpbc_bfb__column' );
4597 const is_top_level = container.classList.contains( 'wpbc_bfb__form_preview_section_container' );
4598 if ( !is_column && !is_top_level ) {
4599 return;
4600 }
4601 b.init_sortable?.( container );
4602 }
4603 };
4604
4605 /**
4606 * Serialization and deserialization of pages/sections/fields.
4607 */
4608 UI.WPBC_BFB_Structure_IO = class extends UI.WPBC_BFB_Module {
4609 init() {
4610 this.builder.get_structure = () => this.serialize();
4611 this.builder.load_saved_structure = (s, opts) => this.deserialize( s, opts );
4612 }
4613
4614 /** @returns {Array} */
4615 serialize() {
4616 const b = this.builder;
4617 this._normalize_ids();
4618 this._normalize_names();
4619 const pages = [];
4620 b.pages_container.querySelectorAll( '.wpbc_bfb__panel--preview' ).forEach( (page_el, page_index) => {
4621 const container = page_el.querySelector( '.wpbc_bfb__form_preview_section_container' );
4622 const content = [];
4623 if ( !container ) {
4624 pages.push( { page: page_index + 1, content } );
4625 return;
4626 }
4627 container.querySelectorAll( ':scope > *' ).forEach( (child) => {
4628 if ( child.classList.contains( 'wpbc_bfb__section' ) ) {
4629 content.push( { type: 'section', data: this.serialize_section( child ) } );
4630 return;
4631 }
4632 if ( child.classList.contains( 'wpbc_bfb__field' ) ) {
4633 if ( child.classList.contains( 'is-invalid' ) ) {
4634 return;
4635 }
4636 const f_data = Core.WPBC_Form_Builder_Helper.get_all_data_attributes( child );
4637 // Drop ephemeral/editor-only flags
4638 [ 'uid', 'fresh', 'autoname', 'was_loaded', 'name_user_touched' ]
4639 .forEach( k => {
4640 if ( k in f_data ) delete f_data[k];
4641 } );
4642 content.push( { type: 'field', data: f_data } );
4643 }
4644 } );
4645 pages.push( { page: page_index + 1, content } );
4646 } );
4647 return pages;
4648 }
4649
4650 /**
4651 * @param {HTMLElement} section_el
4652 * @returns {{id:string,label:string,html_id:string,cssclass:string,col_styles:string,columns:Array}}
4653 */
4654 serialize_section(section_el) {
4655 const row = section_el.querySelector( ':scope > .wpbc_bfb__row' );
4656
4657 // NEW: read per-column styles from dataset/attributes (underscore & hyphen)
4658 var col_styles_raw =
4659 section_el.getAttribute( 'data-col_styles' ) ||
4660 (section_el.dataset ? (section_el.dataset.col_styles) : '') ||
4661 '';
4662
4663 const base = {
4664 id : section_el.dataset.id,
4665 label : section_el.dataset.label || '',
4666 html_id : section_el.dataset.html_id || '',
4667 cssclass : section_el.dataset.cssclass || '',
4668 col_styles: String( col_styles_raw ) // <-- NEW: keep as raw JSON string
4669 };
4670
4671 if ( !row ) {
4672 return Object.assign( {}, base, { columns: [] } );
4673 }
4674
4675 const columns = [];
4676 row.querySelectorAll( ':scope > .wpbc_bfb__column' ).forEach( function (col) {
4677 const width = col.style.flexBasis || '100%';
4678 const items = [];
4679 Array.prototype.forEach.call( col.children, function (child) {
4680 if ( child.classList.contains( 'wpbc_bfb__section' ) ) {
4681 items.push( { type: 'section', data: this.serialize_section( child ) } );
4682 return;
4683 }
4684 if ( child.classList.contains( 'wpbc_bfb__field' ) ) {
4685 if ( child.classList.contains( 'is-invalid' ) ) {
4686 return;
4687 }
4688 const f_data = Core.WPBC_Form_Builder_Helper.get_all_data_attributes( child );
4689 [ 'uid', 'fresh', 'autoname', 'was_loaded', 'name_user_touched' ].forEach( function (k) {
4690 if ( k in f_data ) {
4691 delete f_data[k];
4692 }
4693 } );
4694 items.push( { type: 'field', data: f_data } );
4695 }
4696 }.bind( this ) );
4697 columns.push( { width: width, items: items } );
4698 }.bind( this ) );
4699
4700 // Clamp persisted col_styles to the actual number of columns on Save.
4701 try {
4702 const colCount = columns.length;
4703 const raw = String( col_styles_raw || '' ).trim();
4704
4705 if ( raw ) {
4706 let arr = [];
4707 try {
4708 const parsed = JSON.parse( raw );
4709 arr = Array.isArray( parsed ) ? parsed : (parsed && Array.isArray( parsed.columns ) ? parsed.columns : []);
4710 } catch ( _e ) {
4711 arr = [];
4712 }
4713
4714 if ( colCount <= 0 ) {
4715 base.col_styles = '[]';
4716 } else {
4717 if ( arr.length > colCount ) arr.length = colCount;
4718 while ( arr.length < colCount ) arr.push( {} );
4719 base.col_styles = JSON.stringify( arr );
4720 }
4721 } else {
4722 base.col_styles = '';
4723 }
4724 } catch ( _e ) {
4725 }
4726
4727 return Object.assign( {}, base, { columns: columns } );
4728 }
4729
4730 /**
4731 * @param {Array} structure
4732 * @param {{deferIfTyping?: boolean}} [opts = {}]
4733 */
4734 deserialize(structure, { deferIfTyping = true } = {}) {
4735 const b = this.builder;
4736 if ( deferIfTyping && this._is_typing_in_inspector() ) {
4737 clearTimeout( this._defer_timer );
4738 this._defer_timer = setTimeout( () => {
4739 this.deserialize( structure, { deferIfTyping: false } );
4740 }, 150 );
4741 return;
4742 }
4743 b.pages_container.innerHTML = '';
4744 b.page_counter = 0;
4745 (structure || []).forEach( (page_data) => {
4746 const page_el = b.pages_sections.add_page( { scroll: false } );
4747 const section_container = page_el.querySelector( '.wpbc_bfb__form_preview_section_container' );
4748 section_container.innerHTML = '';
4749 b.init_section_sortable?.( section_container );
4750 (page_data.content || []).forEach( (item) => {
4751 if ( item.type === 'section' ) {
4752 // Now returns the element; attributes (incl. col_styles) are applied inside rebuild.
4753 b.pages_sections.rebuild_section( item.data, section_container );
4754 return;
4755 }
4756 if ( item.type === 'field' ) {
4757 const el = b.build_field( item.data );
4758 if ( el ) {
4759 section_container.appendChild( el );
4760 b.trigger_field_drop_callback( el, 'load' );
4761 }
4762 }
4763 } );
4764 } );
4765 b.usage?.update_palette_ui?.();
4766 b.bus.emit( Core.WPBC_BFB_Events.STRUCTURE_LOADED, { structure } );
4767 }
4768
4769 _normalize_ids() {
4770 const b = this.builder;
4771 b.pages_container.querySelectorAll( '.wpbc_bfb__panel--preview .wpbc_bfb__field:not(.is-invalid)' ).forEach( (el) => {
4772 const data = Core.WPBC_Form_Builder_Helper.get_all_data_attributes( el );
4773 const want = Core.WPBC_BFB_Sanitize.sanitize_html_id( data.id || '' ) || 'field';
4774 const uniq = b.id.ensure_unique_field_id( want, el );
4775 if ( data.id !== uniq ) {
4776 el.setAttribute( 'data-id', uniq );
4777 if ( b.preview_mode ) {
4778 b.render_preview( el );
4779 }
4780 }
4781 } );
4782 }
4783
4784 _normalize_names() {
4785 const b = this.builder;
4786 b.pages_container.querySelectorAll( '.wpbc_bfb__panel--preview .wpbc_bfb__field:not(.is-invalid)' ).forEach( (el) => {
4787 const data = Core.WPBC_Form_Builder_Helper.get_all_data_attributes( el );
4788 const base = Core.WPBC_BFB_Sanitize.sanitize_html_name( (data.name != null) ? data.name : data.id ) || 'field';
4789 const uniq = b.id.ensure_unique_field_name( base, el );
4790 if ( data.name !== uniq ) {
4791 el.setAttribute( 'data-name', uniq );
4792 if ( b.preview_mode ) {
4793 b.render_preview( el );
4794 }
4795 }
4796 } );
4797 }
4798
4799 /** @returns {boolean} */
4800 _is_typing_in_inspector() {
4801 const ins = document.getElementById( 'wpbc_bfb__inspector' );
4802 return !!(ins && document.activeElement && ins.contains( document.activeElement ));
4803 }
4804 };
4805
4806 /**
4807 * Minimal, standalone guard that enforces per-column min widths based on fields' data-min_width.
4808 *
4809 * @type {UI.WPBC_BFB_Min_Width_Guard}
4810 */
4811 UI.WPBC_BFB_Min_Width_Guard = class extends UI.WPBC_BFB_Module {
4812
4813 constructor(builder) {
4814 super( builder );
4815 this._on_field_add = this._on_field_add.bind( this );
4816 this._on_field_remove = this._on_field_remove.bind( this );
4817 this._on_structure_loaded = this._on_structure_loaded.bind( this );
4818 this._on_structure_change = this._on_structure_change.bind( this );
4819 this._on_window_resize = this._on_window_resize.bind( this );
4820
4821 this._pending_rows = new Set();
4822 this._pending_all = false;
4823 this._raf_id = 0;
4824 }
4825
4826 init() {
4827 const EV = Core.WPBC_BFB_Events;
4828 this.builder?.bus?.on?.( EV.FIELD_ADD, this._on_field_add );
4829 this.builder?.bus?.on?.( EV.FIELD_REMOVE, this._on_field_remove );
4830 this.builder?.bus?.on?.( EV.STRUCTURE_LOADED, this._on_structure_loaded );
4831 // Refresh selectively on structure change (NOT on every prop input).
4832 this.builder?.bus?.on?.( EV.STRUCTURE_CHANGE, this._on_structure_change );
4833
4834 window.addEventListener( 'resize', this._on_window_resize, { passive: true } );
4835 this._schedule_refresh_all();
4836 }
4837
4838 destroy() {
4839 const EV = Core.WPBC_BFB_Events;
4840 this.builder?.bus?.off?.( EV.FIELD_ADD, this._on_field_add );
4841 this.builder?.bus?.off?.( EV.FIELD_REMOVE, this._on_field_remove );
4842 this.builder?.bus?.off?.( EV.STRUCTURE_LOADED, this._on_structure_loaded );
4843 this.builder?.bus?.off?.( EV.STRUCTURE_CHANGE, this._on_structure_change );
4844 window.removeEventListener( 'resize', this._on_window_resize );
4845 }
4846
4847 _on_field_add(e) {
4848 this._schedule_refresh_all();
4849 // if you really want to be minimal work here, keep your row-only version.
4850 }
4851
4852 _on_field_remove(e) {
4853 const src_el = e?.detail?.el || null;
4854 const row = (src_el && src_el.closest) ? src_el.closest( '.wpbc_bfb__row' ) : null;
4855 if ( row ) {
4856 this._schedule_refresh_row( row );
4857 } else {
4858 this._schedule_refresh_all();
4859 }
4860 }
4861
4862 _on_structure_loaded() {
4863 this._schedule_refresh_all();
4864 }
4865
4866 _on_structure_change(e) {
4867 const reason = e?.detail?.reason || '';
4868 const key = e?.detail?.key || '';
4869
4870 // Ignore noisy prop changes that don't affect min widths.
4871 if ( reason === 'prop-change' && key !== 'min_width' ) {
4872 return;
4873 }
4874
4875 const el = e?.detail?.el || null;
4876 const row = el?.closest?.( '.wpbc_bfb__row' ) || null;
4877 if ( row ) {
4878 this._schedule_refresh_row( row );
4879 } else {
4880 this._schedule_refresh_all();
4881 }
4882 }
4883
4884 _on_window_resize() {
4885 this._schedule_refresh_all();
4886 }
4887
4888 _schedule_refresh_row(row_el) {
4889 if ( !row_el ) return;
4890 this._pending_rows.add( row_el );
4891 this._kick_raf();
4892 }
4893
4894 _schedule_refresh_all() {
4895 this._pending_all = true;
4896 this._pending_rows.clear();
4897 this._kick_raf();
4898 }
4899
4900 _kick_raf() {
4901 if ( this._raf_id ) return;
4902 this._raf_id = (window.requestAnimationFrame || setTimeout)( () => {
4903 this._raf_id = 0;
4904 if ( this._pending_all ) {
4905 this._pending_all = false;
4906 this.refresh_all();
4907 return;
4908 }
4909 const rows = Array.from( this._pending_rows );
4910 this._pending_rows.clear();
4911 rows.forEach( (r) => this.refresh_row( r ) );
4912 }, 0 );
4913 }
4914
4915
4916 refresh_all() {
4917 this.builder?.pages_container
4918 ?.querySelectorAll?.( '.wpbc_bfb__row' )
4919 ?.forEach?.( (row) => this.refresh_row( row ) );
4920 }
4921
4922 refresh_row(row_el) {
4923 if ( !row_el ) return;
4924
4925 const cols = row_el.querySelectorAll( ':scope > .wpbc_bfb__column' );
4926
4927 // 1) Recalculate each column’s required min px and write it to the CSS var.
4928 cols.forEach( (col) => this.apply_col_min( col ) );
4929
4930 // 2) Enforce it at the CSS level right away so layout can’t render narrower.
4931 cols.forEach( (col) => {
4932 const px = parseFloat( getComputedStyle( col ).getPropertyValue( '--wpbc-col-min' ) || '0' ) || 0;
4933 col.style.minWidth = px > 0 ? Math.round( px ) + 'px' : '';
4934 } );
4935
4936 // 3) Normalize current bases so the row respects all mins without overflow.
4937 try {
4938 const b = this.builder;
4939 const gp = b.col_gap_percent;
4940 const eff = b.layout.compute_effective_bases_from_row( row_el, gp ); // { bases, available }
4941 // Re-fit *current* bases against mins (same algorithm layout chips use).
4942 const fitted = UI.WPBC_BFB_Layout_Chips._fit_weights_respecting_min( b, row_el, eff.bases );
4943 if ( Array.isArray( fitted ) ) {
4944 const changed = fitted.some( (v, i) => Math.abs( v - eff.bases[i] ) > 0.01 );
4945 if ( changed ) {
4946 b.layout.apply_bases_to_row( row_el, fitted );
4947 }
4948 }
4949 } catch ( e ) {
4950 w._wpbc?.dev?.error?.( 'WPBC_BFB_Min_Width_Guard - refresh_row', e );
4951 }
4952 }
4953
4954 apply_col_min(col_el) {
4955 if ( !col_el ) return;
4956 let max_px = 0;
4957 const colRect = col_el.getBoundingClientRect();
4958 col_el.querySelectorAll( ':scope > .wpbc_bfb__field' ).forEach( (field) => {
4959 const raw = field.getAttribute( 'data-min_width' );
4960 let px = 0;
4961 if ( raw ) {
4962 const s = String( raw ).trim().toLowerCase();
4963 if ( s.endsWith( '%' ) ) {
4964 const n = parseFloat( s );
4965 if ( Number.isFinite( n ) && colRect.width > 0 ) {
4966 px = (n / 100) * colRect.width;
4967 } else {
4968 px = 0;
4969 }
4970 } else {
4971 px = this.parse_len_px( s );
4972 }
4973 } else {
4974 const cs = getComputedStyle( field );
4975 px = parseFloat( cs.minWidth || '0' ) || 0;
4976 }
4977 if ( px > max_px ) max_px = px;
4978 } );
4979 col_el.style.setProperty( '--wpbc-col-min', max_px > 0 ? Math.round( max_px ) + 'px' : '0px' );
4980 }
4981
4982 parse_len_px(value) {
4983 if ( value == null ) return 0;
4984 const s = String( value ).trim().toLowerCase();
4985 if ( s === '' ) return 0;
4986 if ( s.endsWith( 'px' ) ) {
4987 const n = parseFloat( s );
4988 return Number.isFinite( n ) ? n : 0;
4989 }
4990 if ( s.endsWith( 'rem' ) || s.endsWith( 'em' ) ) {
4991 const n = parseFloat( s );
4992 const base = parseFloat( getComputedStyle( document.documentElement ).fontSize ) || 16;
4993 return Number.isFinite( n ) ? n * base : 0;
4994 }
4995 const n = parseFloat( s );
4996 return Number.isFinite( n ) ? n : 0;
4997 }
4998 };
4999
5000 /**
5001 * WPBC_BFB_Toggle_Normalizer
5002 *
5003 * Converts plain checkboxes into toggle UI:
5004 * <div class="inspector__control wpbc_ui__toggle">
5005 * <input type="checkbox" id="{unique}" data-inspector-key="..." class="inspector__input" role="switch"
5006 * aria-checked="true|false">
5007 * <label class="wpbc_ui__toggle_icon" for="{unique}"></label>
5008 * <label class="wpbc_ui__toggle_label" for="{unique}">Label</label>
5009 * </div>
5010 *
5011 * - Skips inputs already inside `.wpbc_ui__toggle`.
5012 * - Reuses an existing <label for="..."> text if present; otherwise falls back to nearby labels or attributes.
5013 * - Auto-generates a unique id when absent.
5014 */
5015 UI.WPBC_BFB_Toggle_Normalizer = class {
5016
5017 /**
5018 * Upgrade all raw checkboxes in a container to toggles.
5019 * @param {HTMLElement} root_el
5020 */
5021 static upgrade_checkboxes_in(root_el) {
5022
5023 if ( !root_el || !root_el.querySelectorAll ) {
5024 return;
5025 }
5026
5027 var inputs = root_el.querySelectorAll( 'input[type="checkbox"]' );
5028 if ( !inputs.length ) {
5029 return;
5030 }
5031
5032 Array.prototype.forEach.call( inputs, function (input) {
5033
5034 // 1) Skip if already inside toggle wrapper.
5035 if ( input.closest( '.wpbc_ui__toggle' ) ) {
5036 return;
5037 }
5038 // Skip rows / where input checkbox explicitly marked with attribute 'data-wpbc-ui-no-toggle'.
5039 if ( input.hasAttribute( 'data-wpbc-ui-no-toggle' ) ) {
5040 return;
5041 }
5042
5043 // 2) Ensure unique id; prefer existing.
5044 var input_id = input.getAttribute( 'id' );
5045 if ( !input_id ) {
5046 var key = (input.dataset && input.dataset.inspectorKey) ? String( input.dataset.inspectorKey ) : 'opt';
5047 input_id = UI.WPBC_BFB_Toggle_Normalizer.generate_unique_id( 'wpbc_ins_auto_' + key + '_' );
5048 input.setAttribute( 'id', input_id );
5049 }
5050
5051 // 3) Find best label text.
5052 var label_text = UI.WPBC_BFB_Toggle_Normalizer.resolve_label_text( root_el, input, input_id );
5053
5054 // 4) Build the toggle wrapper.
5055 var wrapper = document.createElement( 'div' );
5056 wrapper.className = 'inspector__control wpbc_ui__toggle';
5057
5058 // Keep original input; just move it into wrapper.
5059 input.classList.add( 'inspector__input' );
5060 input.setAttribute( 'role', 'switch' );
5061 input.setAttribute( 'aria-checked', input.checked ? 'true' : 'false' );
5062
5063 var icon_label = document.createElement( 'label' );
5064 icon_label.className = 'wpbc_ui__toggle_icon';
5065 icon_label.setAttribute( 'for', input_id );
5066
5067 var text_label = document.createElement( 'label' );
5068 text_label.className = 'wpbc_ui__toggle_label';
5069 text_label.setAttribute( 'for', input_id );
5070 text_label.appendChild( document.createTextNode( label_text ) );
5071
5072 // 5) Insert wrapper into DOM near the input.
5073 // Preferred: replace the original labeled row if it matches typical inspector layout.
5074 var replaced = UI.WPBC_BFB_Toggle_Normalizer.try_replace_known_row( input, wrapper, label_text );
5075
5076 if ( !replaced ) {
5077 if ( !input.parentNode ) return; // NEW guard
5078 // Fallback: just wrap the input in place and append labels.
5079 input.parentNode.insertBefore( wrapper, input );
5080 wrapper.appendChild( input );
5081 wrapper.appendChild( icon_label );
5082 wrapper.appendChild( text_label );
5083 }
5084
5085 // 6) ARIA sync on change.
5086 input.addEventListener( 'change', function () {
5087 input.setAttribute( 'aria-checked', input.checked ? 'true' : 'false' );
5088 } );
5089 } );
5090 }
5091
5092 /**
5093 * Generate a unique id with a given prefix.
5094 * @param {string} prefix
5095 * @returns {string}
5096 */
5097 static generate_unique_id(prefix) {
5098 var base = String( prefix || 'wpbc_ins_auto_' );
5099 var uid = Math.random().toString( 36 ).slice( 2, 8 );
5100 var id = base + uid;
5101 // Minimal collision guard in the current document scope.
5102 while ( document.getElementById( id ) ) {
5103 uid = Math.random().toString( 36 ).slice( 2, 8 );
5104 id = base + uid;
5105 }
5106 return id;
5107 }
5108
5109 /**
5110 * Resolve the best human label for an input.
5111 * Priority:
5112 * 1) <label for="{id}">text</label>
5113 * 2) nearest sibling/parent .inspector__label text
5114 * 3) input.getAttribute('aria-label') || data-label || data-inspector-key || name || 'Option'
5115 * @param {HTMLElement} root_el
5116 * @param {HTMLInputElement} input
5117 * @param {string} input_id
5118 * @returns {string}
5119 */
5120 static resolve_label_text(root_el, input, input_id) {
5121 // for= association
5122 if ( input_id ) {
5123 var assoc = root_el.querySelector( 'label[for="' + UI.WPBC_BFB_Toggle_Normalizer.css_escape( input_id ) + '"]' );
5124 if ( assoc && assoc.textContent ) {
5125 var txt = assoc.textContent.trim();
5126 // Remove the old label from DOM; its text will be used by toggle.
5127 assoc.parentNode && assoc.parentNode.removeChild( assoc );
5128 if ( txt ) {
5129 return txt;
5130 }
5131 }
5132 }
5133
5134 // nearby inspector label
5135 var near_label = input.closest( '.inspector__row' );
5136 if ( near_label ) {
5137 var il = near_label.querySelector( '.inspector__label' );
5138 if ( il && il.textContent ) {
5139 var t2 = il.textContent.trim();
5140 // If this row had the standard label+control, drop the old text label to avoid duplicates.
5141 il.parentNode && il.parentNode.removeChild( il );
5142 if ( t2 ) {
5143 return t2;
5144 }
5145 }
5146 }
5147
5148 // fallbacks
5149 var aria = input.getAttribute( 'aria-label' );
5150 if ( aria ) {
5151 return aria;
5152 }
5153 if ( input.dataset && input.dataset.label ) {
5154 return String( input.dataset.label );
5155 }
5156 if ( input.dataset && input.dataset.inspectorKey ) {
5157 return String( input.dataset.inspectorKey );
5158 }
5159 if ( input.name ) {
5160 return String( input.name );
5161 }
5162 return 'Option';
5163 }
5164
5165 /**
5166 * Try to replace a known inspector row pattern with a toggle wrapper.
5167 * Patterns:
5168 * <div.inspector__row>
5169 * <label.inspector__label>Text</label>
5170 * <div.inspector__control> [input[type=checkbox]] </div>
5171 * </div>
5172 *
5173 * @param {HTMLInputElement} input
5174 * @param {HTMLElement} wrapper
5175 * @returns {boolean} replaced
5176 */
5177 static try_replace_known_row(input, wrapper, label_text) {
5178 var row = input.closest( '.inspector__row' );
5179 var ctrl_wrap = input.parentElement;
5180
5181 if ( row && ctrl_wrap && ctrl_wrap.classList.contains( 'inspector__control' ) ) {
5182 // Clear control wrap and reinsert toggle structure.
5183 while ( ctrl_wrap.firstChild ) {
5184 ctrl_wrap.removeChild( ctrl_wrap.firstChild );
5185 }
5186 row.classList.add( 'inspector__row--toggle' );
5187
5188 ctrl_wrap.classList.add( 'wpbc_ui__toggle' );
5189 ctrl_wrap.appendChild( input );
5190
5191 var input_id = input.getAttribute( 'id' );
5192 var icon_lbl = document.createElement( 'label' );
5193 icon_lbl.className = 'wpbc_ui__toggle_icon';
5194 icon_lbl.setAttribute( 'for', input_id );
5195
5196 var text_lbl = document.createElement( 'label' );
5197 text_lbl.className = 'wpbc_ui__toggle_label';
5198 text_lbl.setAttribute( 'for', input_id );
5199 if ( label_text ) {
5200 text_lbl.appendChild( document.createTextNode( label_text ) );
5201 }
5202 // If the row previously had a .inspector__label (we removed it in resolve_label_text),
5203 // we intentionally do NOT recreate it; the toggle text label becomes the visible one.
5204 // The text content is already resolved in resolve_label_text() and set below by caller.
5205
5206 ctrl_wrap.appendChild( icon_lbl );
5207 ctrl_wrap.appendChild( text_lbl );
5208 return true;
5209 }
5210
5211 // Not a known pattern; caller will wrap in place.
5212 return false;
5213 }
5214
5215 /**
5216 * CSS.escape polyfill for selectors.
5217 * @param {string} s
5218 * @returns {string}
5219 */
5220 static css_escape(s) {
5221 s = String( s );
5222 if ( window.CSS && typeof window.CSS.escape === 'function' ) {
5223 return window.CSS.escape( s );
5224 }
5225 return s.replace( /([^\w-])/g, '\\$1' );
5226 }
5227 };
5228
5229 /**
5230 * Apply all UI normalizers/enhancers to a container (post-render).
5231 * Keep this file small and add more normalizers later in one place.
5232 *
5233 * @param {HTMLElement} root
5234 */
5235 UI.apply_post_render = function (root) {
5236 if ( !root ) {
5237 return;
5238 }
5239 try {
5240 UI.WPBC_BFB_ValueSlider?.init_on?.( root );
5241 } catch ( e ) { /* noop */
5242 }
5243 try {
5244 var T = UI.WPBC_BFB_Toggle_Normalizer;
5245 if ( T && typeof T.upgrade_checkboxes_in === 'function' ) {
5246 T.upgrade_checkboxes_in( root );
5247 }
5248 } catch ( e ) {
5249 w._wpbc?.dev?.error?.( 'apply_post_render.toggle', e );
5250 }
5251
5252 // Accessibility: keep aria-checked in sync for all toggles inside root.
5253 try {
5254 root.querySelectorAll( '.wpbc_ui__toggle input[type="checkbox"]' ).forEach( function (cb) {
5255 if ( cb.__wpbc_aria_hooked ) {
5256 return;
5257 }
5258 cb.__wpbc_aria_hooked = true;
5259 cb.setAttribute( 'aria-checked', cb.checked ? 'true' : 'false' );
5260 // Delegate ‘change’ just once per render – native delegation still works fine for your logic.
5261 cb.addEventListener( 'change', () => {
5262 cb.setAttribute( 'aria-checked', cb.checked ? 'true' : 'false' );
5263 }, { passive: true } );
5264 } );
5265 } catch ( e ) {
5266 w._wpbc?.dev?.error?.( 'apply_post_render.aria', e );
5267 }
5268 };
5269
5270 UI.InspectorEnhancers = UI.InspectorEnhancers || (function () {
5271 var regs = [];
5272
5273 function register(name, selector, init, destroy) {
5274 regs.push( { name, selector, init, destroy } );
5275 }
5276
5277 function scan(root) {
5278 if ( !root ) return;
5279 regs.forEach( function (r) {
5280 root.querySelectorAll( r.selector ).forEach( function (node) {
5281 node.__wpbc_eh = node.__wpbc_eh || {};
5282 if ( node.__wpbc_eh[r.name] ) return;
5283 try {
5284 r.init && r.init( node, root );
5285 node.__wpbc_eh[r.name] = true;
5286 } catch ( _e ) {
5287 }
5288 } );
5289 } );
5290 }
5291
5292 function destroy(root) {
5293 if ( !root ) return;
5294 regs.forEach( function (r) {
5295 root.querySelectorAll( r.selector ).forEach( function (node) {
5296 try {
5297 r.destroy && r.destroy( node, root );
5298 } catch ( _e ) {
5299 }
5300 if ( node.__wpbc_eh ) delete node.__wpbc_eh[r.name];
5301 } );
5302 } );
5303 }
5304
5305 return { register, scan, destroy };
5306 })();
5307
5308 UI.WPBC_BFB_ValueSlider = {
5309 init_on(root) {
5310 var groups = (root.nodeType === 1 ? [ root ] : []).concat( [].slice.call( root.querySelectorAll?.( '[data-len-group]' ) || [] ) );
5311 groups.forEach( function (g) {
5312 if ( !g.matches || !g.matches( '[data-len-group]' ) ) return;
5313 if ( g.__wpbc_len_wired ) return;
5314
5315 var number = g.querySelector( '[data-len-value]' );
5316 var range = g.querySelector( '[data-len-range]' );
5317 var unit = g.querySelector( '[data-len-unit]' );
5318
5319 if ( !number || !range ) return;
5320
5321 // Mirror constraints if missing on the range.
5322 [ 'min', 'max', 'step' ].forEach( function (a) {
5323 if ( !range.hasAttribute( a ) && number.hasAttribute( a ) ) {
5324 range.setAttribute( a, number.getAttribute( a ) );
5325 }
5326 } );
5327
5328
5329 function sync_range_from_number() {
5330 if ( range.value !== number.value ) {
5331 range.value = number.value;
5332 }
5333 }
5334
5335 function dispatch_input(el) {
5336 try { el.dispatchEvent( new Event( 'input', { bubbles: true } ) ); } catch ( _e ) {}
5337 }
5338 function dispatch_change(el) {
5339 try { el.dispatchEvent( new Event( 'change', { bubbles: true } ) ); } catch ( _e ) {}
5340 }
5341
5342 // Throttle range->number syncing (time-based).
5343 var timer_id = 0;
5344 var pending_val = null;
5345 var pending_change = false;
5346 var last_flush_ts = 0;
5347
5348 // Change this to tune speed: 50..120 ms is a good range.
5349 var min_interval_ms = parseInt( g.dataset.lenThrottle || UI.VALUE_SLIDER_THROTTLE_MS, 10 );
5350 min_interval_ms = Number.isFinite( min_interval_ms ) ? Math.max( 0, min_interval_ms ) : 120;
5351
5352 function flush_range_to_number() {
5353 timer_id = 0;
5354
5355 if ( pending_val == null ) {
5356 return;
5357 }
5358
5359 var next = String( pending_val );
5360 pending_val = null;
5361
5362 if ( number.value !== next ) {
5363 number.value = next;
5364 // IMPORTANT: only 'input' while dragging.
5365 dispatch_input( number );
5366 }
5367
5368 if ( pending_change ) {
5369 pending_change = false;
5370 dispatch_change( number );
5371 }
5372
5373 last_flush_ts = Date.now();
5374 }
5375
5376 function schedule_range_to_number(val, emit_change) {
5377 pending_val = val;
5378 if ( emit_change ) {
5379 pending_change = true;
5380 }
5381
5382 // If commit requested, flush immediately.
5383 if ( pending_change ) {
5384 if ( timer_id ) {
5385 clearTimeout( timer_id );
5386 timer_id = 0;
5387 }
5388 flush_range_to_number();
5389 return;
5390 }
5391
5392 var now = Date.now();
5393 var delta = now - last_flush_ts;
5394
5395 // If enough time passed, flush immediately; else schedule.
5396 if ( delta >= min_interval_ms ) {
5397 flush_range_to_number();
5398 return;
5399 }
5400
5401 if ( timer_id ) {
5402 return;
5403 }
5404
5405 timer_id = setTimeout( flush_range_to_number, Math.max( 0, min_interval_ms - delta ) );
5406 }
5407
5408 function on_number_input() {
5409 sync_range_from_number();
5410 }
5411
5412 function on_number_change() {
5413 sync_range_from_number();
5414 }
5415
5416 function on_range_input() {
5417 schedule_range_to_number( range.value, false );
5418 }
5419
5420 function on_range_change() {
5421 schedule_range_to_number( range.value, true );
5422 }
5423
5424 number.addEventListener( 'input', on_number_input );
5425 number.addEventListener( 'change', on_number_change );
5426 range.addEventListener( 'input', on_range_input );
5427 range.addEventListener( 'change', on_range_change );
5428
5429 if ( unit ) {
5430 unit.addEventListener( 'change', function () {
5431 // We just nudge the number so upstream handlers re-run.
5432 try {
5433 number.dispatchEvent( new Event( 'input', { bubbles: true } ) );
5434 } catch ( _e ) {
5435 }
5436 } );
5437 }
5438
5439 // Initial sync
5440 sync_range_from_number();
5441
5442 g.__wpbc_len_wired = {
5443 destroy() {
5444 number.removeEventListener( 'input', on_number_input );
5445 number.removeEventListener( 'change', on_number_change );
5446 range.removeEventListener( 'input', on_range_input );
5447 range.removeEventListener( 'change', on_range_change );
5448 }
5449 };
5450 } );
5451 },
5452 destroy_on(root) {
5453 var groups = (root && root.nodeType === 1 ? [ root ] : []).concat(
5454 [].slice.call( root.querySelectorAll?.( '[data-len-group]' ) || [] )
5455 );
5456 groups.forEach( function (g) {
5457 if ( !g.matches || !g.matches( '[data-len-group]' ) ) return;
5458 try {
5459 g.__wpbc_len_wired && g.__wpbc_len_wired.destroy && g.__wpbc_len_wired.destroy();
5460 } catch ( _e ) {
5461 }
5462 delete g.__wpbc_len_wired;
5463 } );
5464 }
5465 };
5466
5467 // Register with the global enhancers hub.
5468 UI.InspectorEnhancers && UI.InspectorEnhancers.register(
5469 'value-slider',
5470 '[data-len-group]',
5471 function (el, _root) {
5472 UI.WPBC_BFB_ValueSlider.init_on( el );
5473 },
5474 function (el, _root) {
5475 UI.WPBC_BFB_ValueSlider.destroy_on( el );
5476 }
5477 );
5478
5479 // Single, load-order-safe patch so enhancers auto-run on every bind.
5480 (function patchInspectorEnhancers() {
5481 function applyPatch() {
5482 var Inspector = w.WPBC_BFB_Inspector;
5483 if ( !Inspector || Inspector.__wpbc_enhancers_patched ) return false;
5484 Inspector.__wpbc_enhancers_patched = true;
5485 var orig = Inspector.prototype.bind_to_field;
5486 Inspector.prototype.bind_to_field = function (el) {
5487 orig.call( this, el );
5488 try {
5489 var ins = this.panel
5490 || document.getElementById( 'wpbc_bfb__inspector' )
5491 || document.querySelector( '.wpbc_bfb__inspector' );
5492 UI.InspectorEnhancers && UI.InspectorEnhancers.scan( ins );
5493 } catch ( _e ) {
5494 }
5495 };
5496 // Initial scan if the DOM is already present.
5497 try {
5498 var insEl = document.getElementById( 'wpbc_bfb__inspector' )
5499 || document.querySelector( '.wpbc_bfb__inspector' );
5500 UI.InspectorEnhancers && UI.InspectorEnhancers.scan( insEl );
5501 } catch ( _e ) {
5502 }
5503 return true;
5504 }
5505
5506 // Try now; if Inspector isn’t defined yet, patch when it becomes ready.
5507 if ( !applyPatch() ) {
5508 document.addEventListener(
5509 'wpbc_bfb_inspector_ready',
5510 function () {
5511 applyPatch();
5512 },
5513 { once: true }
5514 );
5515 }
5516 })();
5517
5518 }( window, document ));
5519
5520 // ---------------------------------------------------------------------------------------------------------------------
5521 // == File /includes/page-form-builder/__js/core/bfb-sync-code-tools-tab.js | 2026-04-06 | 16:38
5522 // ---------------------------------------------------------------------------------------------------------------------
5523 (function ( w, d ) {
5524 'use strict';
5525
5526 /**
5527 * Dispatch request to show a right sidebar panel.
5528 *
5529 * @param {string} panel_id
5530 * @param {string} tab_id
5531 *
5532 * @return {void}
5533 */
5534 function wpbc_bfb__dispatch_show_panel( panel_id, tab_id ) {
5535
5536 var event_detail;
5537
5538 if ( ! panel_id || ! tab_id ) {
5539 return;
5540 }
5541
5542 event_detail = {
5543 panel_id: panel_id,
5544 tab_id : tab_id
5545 };
5546
5547 if ( typeof w.wpbc_bfb__dispatch_event_safe === 'function' ) {
5548 w.wpbc_bfb__dispatch_event_safe( 'wpbc_bfb:show_panel', event_detail );
5549 return;
5550 }
5551
5552 d.dispatchEvent(
5553 new CustomEvent(
5554 'wpbc_bfb:show_panel',
5555 {
5556 detail: event_detail
5557 }
5558 )
5559 );
5560 }
5561
5562 /**
5563 * Sync right sidebar with top mode tabs.
5564 *
5565 * Uses top tabs nav attribute `data-active-tab` as the source of truth.
5566 * This avoids false detection on initial page load, because top panels are
5567 * hidden by inline style `display:none`, not by the `hidden` attribute.
5568 */
5569 class WPBC_BFB_Mode_Rightbar_Sync {
5570
5571 constructor() {
5572 this.top_tabs_nav = null;
5573 this.advanced_tab_wrap = null;
5574 this.advanced_panel = null;
5575 this.mutation_observer = null;
5576 this.last_mode = '';
5577 this.sync_rightbar_mode = this.sync_rightbar_mode.bind( this );
5578 this.handle_click = this.handle_click.bind( this );
5579 }
5580
5581 /**
5582 * Init controller.
5583 *
5584 * @return {void}
5585 */
5586 init() {
5587 this.top_tabs_nav = d.getElementById( 'wpbc_bfb__top_horisontal_nav' );
5588 this.advanced_tab_wrap = d.querySelector( '.wpbc_bfb__rightbar_tab_wrap--advanced_tools' );
5589 this.advanced_panel = d.getElementById( 'wpbc_bfb__inspector_advanced_tools' );
5590
5591 if ( ! this.advanced_tab_wrap || ! this.advanced_panel ) {
5592 return;
5593 }
5594
5595 // Observe active tab changes in the top tabs nav.
5596 if ( this.top_tabs_nav ) {
5597 this.mutation_observer = new MutationObserver( this.sync_rightbar_mode );
5598 this.mutation_observer.observe(
5599 this.top_tabs_nav,
5600 {
5601 attributes : true,
5602 attributeFilter: [ 'data-active-tab' ]
5603 }
5604 );
5605 }
5606
5607 // Fallback for click-driven switching.
5608 d.addEventListener( 'click', this.handle_click, true );
5609
5610 // Initial sync.
5611 this.sync_rightbar_mode();
5612 }
5613
5614 /**
5615 * Get active top tab id.
5616 *
5617 * Priority:
5618 * 1. data-active-tab from top tabs nav
5619 * 2. active CSS class in nav
5620 * 3. visible top panel fallback
5621 *
5622 * @return {string}
5623 */
5624 get_active_top_tab_id() {
5625 var active_tab_id = '';
5626 var active_link = null;
5627 var advanced_panel = null;
5628 var builder_panel = null;
5629
5630 if ( this.top_tabs_nav ) {
5631 active_tab_id = String( this.top_tabs_nav.getAttribute( 'data-active-tab' ) || '' ).trim();
5632 if ( '' !== active_tab_id ) {
5633 return active_tab_id;
5634 }
5635
5636 active_link = this.top_tabs_nav.querySelector( '.wpbc_ui_el__horis_nav_item.active [data-wpbc-bfb-tab]' );
5637 if ( active_link ) {
5638 active_tab_id = String( active_link.getAttribute( 'data-wpbc-bfb-tab' ) || '' ).trim();
5639 if ( '' !== active_tab_id ) {
5640 return active_tab_id;
5641 }
5642 }
5643 }
5644
5645 // Final fallback: detect visible panel.
5646 advanced_panel = d.querySelector( '.wpbc_bfb__top_tab_section__advanced_tab' );
5647 builder_panel = d.querySelector( '.wpbc_bfb__top_tab_section__builder_tab' );
5648
5649 if ( this.is_element_visible( advanced_panel ) ) {
5650 return 'advanced_tab';
5651 }
5652 if ( this.is_element_visible( builder_panel ) ) {
5653 return 'builder_tab';
5654 }
5655
5656 return '';
5657 }
5658
5659 /**
5660 * Check element visibility.
5661 *
5662 * @param {HTMLElement|null} el
5663 *
5664 * @return {boolean}
5665 */
5666 is_element_visible( el ) {
5667 var style;
5668
5669 if ( ! el ) {
5670 return false;
5671 }
5672
5673 style = w.getComputedStyle( el );
5674
5675 if ( ! style ) {
5676 return false;
5677 }
5678
5679 if ( 'none' === style.display ) {
5680 return false;
5681 }
5682
5683 if ( 'hidden' === style.visibility ) {
5684 return false;
5685 }
5686
5687 return true;
5688 }
5689
5690 /**
5691 * Check if Advanced Mode is active.
5692 *
5693 * @return {boolean}
5694 */
5695 is_advanced_mode_active() {
5696 return ( 'advanced_tab' === this.get_active_top_tab_id() );
5697 }
5698
5699 /**
5700 * Show Code Tools tab button.
5701 *
5702 * @return {void}
5703 */
5704 show_advanced_tools_tab() {
5705 this.advanced_tab_wrap.removeAttribute( 'hidden' );
5706 this.advanced_tab_wrap.setAttribute( 'aria-hidden', 'false' );
5707 }
5708
5709 /**
5710 * Hide Code Tools tab button.
5711 *
5712 * @return {void}
5713 */
5714 hide_advanced_tools_tab() {
5715 this.advanced_tab_wrap.setAttribute( 'hidden', 'true' );
5716 this.advanced_tab_wrap.setAttribute( 'aria-hidden', 'true' );
5717 }
5718
5719 /**
5720 * Check if Code Tools panel is currently active.
5721 *
5722 * @return {boolean}
5723 */
5724 is_advanced_tools_panel_active() {
5725 return !! ( this.advanced_panel && ! this.advanced_panel.hasAttribute( 'hidden' ) );
5726 }
5727
5728 /**
5729 * Open Code Tools panel.
5730 *
5731 * @return {void}
5732 */
5733 open_advanced_tools_panel() {
5734 wpbc_bfb__dispatch_show_panel( 'wpbc_bfb__inspector_advanced_tools', 'wpbc_tab_advanced_tools' );
5735 }
5736
5737 /**
5738 * Open default builder rightbar panel.
5739 *
5740 * @return {void}
5741 */
5742 open_default_visual_panel() {
5743 wpbc_bfb__dispatch_show_panel( 'wpbc_bfb__palette_add_new', 'wpbc_tab_library' );
5744 }
5745
5746 /**
5747 * Sync rightbar mode with top tabs mode.
5748 *
5749 * @return {void}
5750 */
5751 sync_rightbar_mode() {
5752 var is_advanced_mode = this.is_advanced_mode_active();
5753
5754 if ( is_advanced_mode ) {
5755 this.show_advanced_tools_tab();
5756
5757 if ( 'advanced' !== this.last_mode ) {
5758 this.open_advanced_tools_panel();
5759 }
5760
5761 this.last_mode = 'advanced';
5762 return;
5763 }
5764
5765 if ( this.is_advanced_tools_panel_active() ) {
5766 this.open_default_visual_panel();
5767 }
5768
5769 this.hide_advanced_tools_tab();
5770 this.last_mode = 'builder';
5771 }
5772
5773 /**
5774 * Fallback click handler for top tabs.
5775 *
5776 * @param {Event} event
5777 *
5778 * @return {void}
5779 */
5780 handle_click( event ) {
5781 var target = event.target.closest( '[data-wpbc-bfb-tab]' );
5782 var this_obj = this;
5783
5784 if ( ! target ) {
5785 return;
5786 }
5787
5788 setTimeout(
5789 function () {
5790 this_obj.sync_rightbar_mode();
5791 },
5792 0
5793 );
5794 }
5795 }
5796
5797 if ( d.readyState === 'loading' ) {
5798 d.addEventListener(
5799 'DOMContentLoaded',
5800 function () {
5801 ( new WPBC_BFB_Mode_Rightbar_Sync() ).init();
5802 }
5803 );
5804 } else {
5805 ( new WPBC_BFB_Mode_Rightbar_Sync() ).init();
5806 }
5807
5808 })( window, document );
5809 // ---------------------------------------------------------------------------------------------------------------------
5810 // == File /includes/page-form-builder/_out/core/bfb-inspector.js == Time point: 2025-09-06 14:08
5811 // ---------------------------------------------------------------------------------------------------------------------
5812 (function (w) {
5813 'use strict';
5814
5815 // 1) Actions registry.
5816
5817 /** @type {Record<string, (ctx: InspectorActionContext) => void>} */
5818 const __INSPECTOR_ACTIONS_MAP__ = Object.create( null );
5819
5820 // Built-ins.
5821 __INSPECTOR_ACTIONS_MAP__['deselect'] = ({ builder }) => {
5822 builder?.select_field?.( null );
5823 };
5824
5825 __INSPECTOR_ACTIONS_MAP__['scrollto'] = ({ builder, el }) => {
5826 if ( !el || !document.body.contains( el ) ) return;
5827 builder?.select_field?.( el, { scrollIntoView: true } );
5828 el.classList.add( 'wpbc_bfb__scroll-pulse' );
5829 setTimeout( () => el.classList.remove( 'wpbc_bfb__scroll-pulse' ), 700 );
5830 };
5831
5832 __INSPECTOR_ACTIONS_MAP__['move-up'] = ({ builder, el }) => {
5833 if ( !el ) return;
5834 builder?.move_item?.( el, 'up' );
5835 // Scroll after the DOM has settled.
5836 requestAnimationFrame(() => __INSPECTOR_ACTIONS_MAP__['scrollto']({ builder, el }));
5837 };
5838
5839 __INSPECTOR_ACTIONS_MAP__['move-down'] = ({ builder, el }) => {
5840 if ( !el ) return;
5841 builder?.move_item?.( el, 'down' );
5842 // Scroll after the DOM has settled.
5843 requestAnimationFrame(() => __INSPECTOR_ACTIONS_MAP__['scrollto']({ builder, el }));
5844 };
5845
5846 __INSPECTOR_ACTIONS_MAP__['delete'] = ({ builder, el, confirm = w.confirm }) => {
5847 if ( !el ) return;
5848 const is_field = el.classList.contains( 'wpbc_bfb__field' );
5849 const label = is_field
5850 ? (el.querySelector( '.wpbc_bfb__field-label' )?.textContent || el.dataset?.id || 'field')
5851 : (el.dataset?.id || 'section');
5852
5853 UI.Modal_Confirm_Delete.open( label, () => {
5854 // Central command will remove, emit events, and reselect neighbor (which re-binds Inspector).
5855 builder?.delete_item?.( el );
5856 } );
5857
5858 };
5859
5860 __INSPECTOR_ACTIONS_MAP__['duplicate'] = ({ builder, el }) => {
5861 if ( !el ) return;
5862 const clone = builder?.duplicate_item?.( el );
5863 if ( clone ) builder?.select_field?.( clone, { scrollIntoView: true } );
5864 };
5865
5866 // Public API.
5867 w.WPBC_BFB_Inspector_Actions = {
5868 run(name, ctx) {
5869 const fn = __INSPECTOR_ACTIONS_MAP__[name];
5870 if ( typeof fn === 'function' ) fn( ctx );
5871 else console.warn( 'WPBC. Inspector action not found:', name );
5872 },
5873 register(name, handler) {
5874 if ( !name || typeof handler !== 'function' ) {
5875 throw new Error( 'register(name, handler): invalid arguments' );
5876 }
5877 __INSPECTOR_ACTIONS_MAP__[name] = handler;
5878 },
5879 has(name) {
5880 return typeof __INSPECTOR_ACTIONS_MAP__[name] === 'function';
5881 }
5882 };
5883
5884 // 2) Inspector Factory.
5885
5886 var UI = (w.WPBC_BFB_Core.UI = w.WPBC_BFB_Core.UI || {});
5887
5888 // Global Hybrid++ registries (keep public).
5889 w.wpbc_bfb_inspector_factory_slots = w.wpbc_bfb_inspector_factory_slots || {};
5890 w.wpbc_bfb_inspector_factory_value_from = w.wpbc_bfb_inspector_factory_value_from || {};
5891
5892 // Define Factory only if missing (no early return for the whole bundle).
5893 // always define/replace Factory
5894 {
5895
5896 /**
5897 * Utility: create element with attributes and children.
5898 *
5899 * @param {string} tag
5900 * @param {Object=} attrs
5901 * @param {(Node|string|Array<Node|string>)=} children
5902 * @returns {HTMLElement}
5903 */
5904 function el(tag, attrs, children) {
5905 var node = document.createElement( tag );
5906 if ( attrs ) {
5907 Object.keys( attrs ).forEach( function (k) {
5908 var v = attrs[k];
5909 if ( v == null ) return;
5910 if ( k === 'class' ) {
5911 node.className = v;
5912 return;
5913 }
5914 if ( k === 'dataset' ) {
5915 Object.keys( v ).forEach( function (dk) {
5916 node.dataset[dk] = String( v[dk] );
5917 } );
5918 return;
5919 }
5920 if ( k === 'checked' && typeof v === 'boolean' ) {
5921 if ( v ) node.setAttribute( 'checked', 'checked' );
5922 return;
5923 }
5924 if ( k === 'disabled' && typeof v === 'boolean' ) {
5925 if ( v ) node.setAttribute( 'disabled', 'disabled' );
5926 return;
5927 }
5928 // normalize boolean attributes to strings.
5929 if ( typeof v === 'boolean' ) {
5930 node.setAttribute( k, v ? 'true' : 'false' );
5931 return;
5932 }
5933 node.setAttribute( k, String( v ) );
5934 } );
5935 }
5936 if ( children ) {
5937 (Array.isArray( children ) ? children : [ children ]).forEach( function (c) {
5938 if ( c == null ) return;
5939 node.appendChild( (typeof c === 'string') ? document.createTextNode( c ) : c );
5940 } );
5941 }
5942 return node;
5943 }
5944
5945 /**
5946 * Build a toggle control row (checkbox rendered as toggle).
5947 *
5948 * Structure:
5949 * <div class="inspector__row inspector__row--toggle">
5950 * <div class="inspector__control wpbc_ui__toggle">
5951 * <input type="checkbox" id="ID" data-inspector-key="KEY" class="inspector__input" checked>
5952 * <label class="wpbc_ui__toggle_icon" for="ID"></label>
5953 * <label class="wpbc_ui__toggle_label" for="ID">Label text</label>
5954 * </div>
5955 * </div>
5956 *
5957 * @param {string} input_id
5958 * @param {string} key
5959 * @param {boolean} checked
5960 * @param {string} label_text
5961 * @returns {HTMLElement}
5962 */
5963 function build_toggle_row( input_id, key, checked, label_text ) {
5964
5965 var row_el = el( 'div', { 'class': 'inspector__row inspector__row--toggle' } );
5966 var ctrl_wrap = el( 'div', { 'class': 'inspector__control wpbc_ui__toggle' } );
5967
5968 var input_el = el( 'input', {
5969 id : input_id,
5970 type : 'checkbox',
5971 'data-inspector-key': key,
5972 'class' : 'inspector__input',
5973 checked : !!checked,
5974 role : 'switch',
5975 'aria-checked' : !!checked
5976 } );
5977 var icon_lbl = el( 'label', { 'class': 'wpbc_ui__toggle_icon', 'for': input_id } );
5978 var text_lbl = el( 'label', { 'class': 'wpbc_ui__toggle_label', 'for': input_id }, label_text || '' );
5979
5980 ctrl_wrap.appendChild( input_el );
5981 ctrl_wrap.appendChild( icon_lbl );
5982 ctrl_wrap.appendChild( text_lbl );
5983
5984 row_el.appendChild( ctrl_wrap );
5985 return row_el;
5986 }
5987
5988 /**
5989 * Utility: choose initial value from data or schema default.
5990 */
5991 function get_initial_value(key, data, props_schema) {
5992 if ( data && Object.prototype.hasOwnProperty.call( data, key ) ) return data[key];
5993 var meta = props_schema && props_schema[key];
5994 return (meta && Object.prototype.hasOwnProperty.call( meta, 'default' )) ? meta.default : '';
5995 }
5996
5997 /**
5998 * Utility: coerce value by schema type.
5999 */
6000
6001
6002 function coerce_by_type(value, type) {
6003 switch ( type ) {
6004 case 'number':
6005 case 'int':
6006 case 'float':
6007 if ( value === '' || value == null ) {
6008 return '';
6009 }
6010 var n = Number( value );
6011 return isNaN( n ) ? '' : n;
6012 case 'boolean':
6013 return !!value;
6014 case 'array':
6015 return Array.isArray( value ) ? value : [];
6016 default:
6017 return (value == null) ? '' : String( value );
6018 }
6019 }
6020
6021 /**
6022 * Normalize <select> options (array of {value,label} or map {value:label}).
6023 */
6024 function normalize_select_options(options) {
6025 if ( Array.isArray( options ) ) {
6026 return options.map( function (o) {
6027 if ( typeof o === 'object' && o && 'value' in o ) {
6028 return { value: String( o.value ), label: String( o.label || o.value ) };
6029 }
6030 return { value: String( o ), label: String( o ) };
6031 } );
6032 }
6033 if ( options && typeof options === 'object' ) {
6034 return Object.keys( options ).map( function (k) {
6035 return { value: String( k ), label: String( options[k] ) };
6036 } );
6037 }
6038 return [];
6039 }
6040
6041 /** Parse a CSS length like "120px" or "80%" into { value:number, unit:string }. */
6042 function parse_len(value, fallback_unit) {
6043 value = (value == null) ? '' : String( value ).trim();
6044 var m = value.match( /^(-?\d+(?:\.\d+)?)(px|%|rem|em)$/i );
6045 if ( m ) {
6046 return { value: parseFloat( m[1] ), unit: m[2].toLowerCase() };
6047 }
6048 // plain number -> assume fallback unit
6049 if ( value !== '' && !isNaN( Number( value ) ) ) {
6050 return { value: Number( value ), unit: (fallback_unit || 'px') };
6051 }
6052 return { value: 0, unit: (fallback_unit || 'px') };
6053 }
6054
6055 /** Clamp helper. */
6056 function clamp_num(v, min, max) {
6057 if ( typeof v !== 'number' || isNaN( v ) ) return (min != null ? min : 0);
6058 if ( min != null && v < min ) v = min;
6059 if ( max != null && v > max ) v = max;
6060 return v;
6061 }
6062
6063 // Initialize Coloris pickers in a given root.
6064 // Relies on Coloris being enqueued (see bfb-bootstrap.php).
6065 function init_coloris_pickers(root) {
6066 if ( !root || !w.Coloris ) return;
6067 // Mark inputs we want Coloris to handle.
6068 var inputs = root.querySelectorAll( 'input[data-inspector-type="color"]' );
6069 if ( !inputs.length ) return;
6070
6071 // Add a stable class for Coloris targeting; avoid double-initializing.
6072 inputs.forEach( function (input) {
6073 if ( input.classList.contains( 'wpbc_bfb_coloris' ) ) return;
6074 input.classList.add( 'wpbc_bfb_coloris' );
6075 } );
6076
6077 // Create/refresh a Coloris instance bound to these inputs.
6078 // Keep HEX output to match schema defaults (e.g., "#e0e0e0").
6079 try {
6080 w.Coloris( {
6081 el : '.wpbc_bfb_coloris',
6082 alpha : false,
6083 format : 'hex',
6084 themeMode: 'auto'
6085 } );
6086 // Coloris already dispatches 'input' events on value changes.
6087 } catch ( e ) {
6088 // Non-fatal: if Coloris throws (rare), the text input still works.
6089 console.warn( 'WPBC Inspector: Coloris init failed:', e );
6090 }
6091 }
6092
6093 /**
6094 * Build: slider + number in one row (writes to a single data key).
6095 * Control meta: { type:'range_number', key, label, min, max, step }
6096 */
6097 function build_range_number_row(input_id, key, label_text, value, meta) {
6098 var row_el = el('div', { 'class': 'inspector__row' });
6099 var label_el = el('label', { 'for': input_id, 'class': 'inspector__label' }, label_text || key || '');
6100 var ctrl = el('div', { 'class': 'inspector__control' });
6101
6102 var min = (meta && meta.min != null) ? meta.min : 0;
6103 var max = (meta && meta.max != null) ? meta.max : 100;
6104 var step = (meta && meta.step != null) ? meta.step : 1;
6105
6106 var group = el('div', { 'class': 'wpbc_len_group wpbc_inline_inputs', 'data-len-group': key });
6107
6108 var range = el('input', {
6109 type : 'range',
6110 'class': 'inspector__input',
6111 'data-len-range': '',
6112 min : String(min),
6113 max : String(max),
6114 step : String(step),
6115 value: String(value == null || value === '' ? min : value)
6116 });
6117
6118 var num = el('input', {
6119 id : input_id,
6120 type : 'number',
6121 'class': 'inspector__input inspector__w_30',
6122 'data-len-value': '',
6123 'data-inspector-key': key,
6124 min : String(min),
6125 max : String(max),
6126 step : String(step),
6127 value: (value == null || value === '') ? String(min) : String(value)
6128 });
6129
6130 group.appendChild(range);
6131 group.appendChild(num);
6132 ctrl.appendChild(group);
6133 row_el.appendChild(label_el);
6134 row_el.appendChild(ctrl);
6135 return row_el;
6136 }
6137
6138 /**
6139 * Build: (number + unit) + slider, writing a *single* combined string to `key`.
6140 * Control meta:
6141 * {
6142 * type:'len', key, label, units:['px','%','rem','em'],
6143 * slider: { px:{min:0,max:512,step:1}, '%':{min:0,max:100,step:1}, rem:{min:0,max:10,step:0.1}, em:{...} },
6144 * fallback_unit:'px'
6145 * }
6146 */
6147 function build_len_compound_row(control, props_schema, data, uid) {
6148 var key = control.key;
6149 var label_text = control.label || key || '';
6150 var def_str = get_initial_value( key, data, props_schema );
6151 var fallback_u = control.fallback_unit || 'px';
6152 var parsed = parse_len( def_str, fallback_u );
6153
6154 var row = el( 'div', { 'class': 'inspector__row' } );
6155 var label = el( 'label', { 'class': 'inspector__label' }, label_text );
6156 var ctrl = el( 'div', { 'class': 'inspector__control' } );
6157
6158 var units = Array.isArray( control.units ) && control.units.length ? control.units : [ 'px', '%', 'rem', 'em' ];
6159 var slider_map = control.slider || {
6160 'px' : { min: 0, max: 512, step: 1 },
6161 '%' : { min: 0, max: 100, step: 1 },
6162 'rem': { min: 0, max: 10, step: 0.1 },
6163 'em' : { min: 0, max: 10, step: 0.1 }
6164 };
6165
6166 // Host with a hidden input that carries data-inspector-key to reuse the standard handler.
6167 var group = el( 'div', { 'class': 'wpbc_len_group', 'data-len-group': key } );
6168
6169 var inline = el( 'div', { 'class': 'wpbc_inline_inputs' } );
6170
6171 var num = el( 'input', {
6172 type : 'number',
6173 'class' : 'inspector__input',
6174 'data-len-value': '',
6175 min : '0',
6176 step : 'any',
6177 value : String( parsed.value )
6178 } );
6179
6180 var sel = el( 'select', { 'class': 'inspector__input', 'data-len-unit': '' } );
6181 units.forEach( function (u) {
6182 var opt = el( 'option', { value: u }, u );
6183 if ( u === parsed.unit ) opt.setAttribute( 'selected', 'selected' );
6184 sel.appendChild( opt );
6185 } );
6186
6187 inline.appendChild( num );
6188 inline.appendChild( sel );
6189
6190 // Slider (unit-aware)
6191 var current = slider_map[parsed.unit] || slider_map[units[0]];
6192 var range = el( 'input', {
6193 type : 'range',
6194 'class' : 'inspector__input',
6195 'data-len-range': '',
6196 min : String( current.min ),
6197 max : String( current.max ),
6198 step : String( current.step ),
6199 value : String( clamp_num( parsed.value, current.min, current.max ) )
6200 } );
6201
6202 // Hidden writer input that the default Inspector handler will catch.
6203 var hidden = el( 'input', {
6204 type : 'text',
6205 'class' : 'inspector__input',
6206 style : 'display:none',
6207 'aria-hidden' : 'true',
6208 tabindex : '-1',
6209 id : 'wpbc_ins_' + key + '_' + uid + '_len_hidden',
6210 'data-inspector-key': key,
6211 value : (String( parsed.value ) + parsed.unit)
6212 } );
6213
6214 group.appendChild( inline );
6215 group.appendChild( range );
6216 group.appendChild( hidden );
6217
6218 ctrl.appendChild( group );
6219 row.appendChild( label );
6220 row.appendChild( ctrl );
6221 return row;
6222 }
6223
6224 /**
6225 * Wire syncing for any .wpbc_len_group inside a given root (panel).
6226 * - range ⇄ number sync
6227 * - unit switches update slider bounds
6228 * - hidden writer (if present) gets updated and emits 'input'
6229 */
6230 function wire_len_group(root) {
6231 if ( !root ) return;
6232
6233 function find_group(el) {
6234 return el && el.closest && el.closest( '.wpbc_len_group' );
6235 }
6236
6237 root.addEventListener( 'input', function (e) {
6238 var t = e.target;
6239 // Slider moved -> update number (and writer/hidden)
6240 if ( t && t.hasAttribute( 'data-len-range' ) ) {
6241 var g = find_group( t );
6242 if ( !g ) return;
6243 var num = g.querySelector( '[data-len-value]' );
6244 if ( num ) {
6245 num.value = t.value;
6246 }
6247 var writer = g.querySelector( '[data-inspector-key]' );
6248 if ( writer && writer.type === 'text' ) {
6249 var unit = g.querySelector( '[data-len-unit]' );
6250 unit = unit ? unit.value : 'px';
6251 writer.value = String( t.value ) + String( unit );
6252 // trigger standard inspector handler:
6253 writer.dispatchEvent( new Event( 'input', { bubbles: true } ) );
6254 } else {
6255 // Plain range_number case (number has data-inspector-key) -> fire input on number
6256 if ( num && num.hasAttribute( 'data-inspector-key' ) ) {
6257 num.dispatchEvent( new Event( 'input', { bubbles: true } ) );
6258 }
6259 }
6260 }
6261
6262 // Number typed -> update slider and writer/hidden
6263 if ( t && t.hasAttribute( 'data-len-value' ) ) {
6264 var g = find_group( t );
6265 if ( !g ) return;
6266 var r = g.querySelector( '[data-len-range]' );
6267 if ( r ) {
6268 // clamp within slider bounds if present
6269 var min = Number( r.min );
6270 var max = Number( r.max );
6271 var v = Number( t.value );
6272 if ( !isNaN( v ) ) {
6273 v = clamp_num( v, isNaN( min ) ? undefined : min, isNaN( max ) ? undefined : max );
6274 r.value = String( v );
6275 if ( String( v ) !== t.value ) t.value = String( v );
6276 }
6277 }
6278 var writer = g.querySelector( '[data-inspector-key]' );
6279 if ( writer && writer.type === 'text' ) {
6280 var unit = g.querySelector( '[data-len-unit]' );
6281 unit = unit ? unit.value : 'px';
6282 writer.value = String( t.value || 0 ) + String( unit );
6283 writer.dispatchEvent( new Event( 'input', { bubbles: true } ) );
6284 }
6285 // else: number itself likely carries data-inspector-key (range_number); default handler will run.
6286 }
6287 }, true );
6288
6289 root.addEventListener( 'change', function (e) {
6290 var t = e.target;
6291 // Unit changed -> update slider limits and writer/hidden
6292 if ( t && t.hasAttribute( 'data-len-unit' ) ) {
6293 var g = find_group( t );
6294 if ( !g ) return;
6295
6296 // Find the control meta via a data attribute on group if provided
6297 // (Factory path sets nothing here; we re-derive from current slider bounds.)
6298 var r = g.querySelector( '[data-len-range]' );
6299 var num = g.querySelector( '[data-len-value]' );
6300 var writer = g.querySelector( '[data-inspector-key]' );
6301 var unit = t.value || 'px';
6302
6303 // Adjust slider bounds heuristically (match Factory defaults)
6304 var bounds_by_unit = {
6305 'px' : { min: 0, max: 512, step: 1 },
6306 '%' : { min: 0, max: 100, step: 1 },
6307 'rem': { min: 0, max: 10, step: 0.1 },
6308 'em' : { min: 0, max: 10, step: 0.1 }
6309 };
6310 if ( r ) {
6311 var b = bounds_by_unit[unit] || bounds_by_unit['px'];
6312 r.min = String( b.min );
6313 r.max = String( b.max );
6314 r.step = String( b.step );
6315 // clamp to new bounds
6316 var v = Number( num && num.value ? num.value : r.value );
6317 if ( !isNaN( v ) ) {
6318 v = clamp_num( v, b.min, b.max );
6319 r.value = String( v );
6320 if ( num ) num.value = String( v );
6321 }
6322 }
6323 if ( writer && writer.type === 'text' ) {
6324 var v = num && num.value ? num.value : (r ? r.value : '0');
6325 writer.value = String( v ) + String( unit );
6326 writer.dispatchEvent( new Event( 'input', { bubbles: true } ) );
6327 }
6328 }
6329 }, true );
6330 }
6331
6332 // =============================================================================================================
6333 // == C O N T R O L ==
6334 // =============================================================================================================
6335
6336 /**
6337 * Schema > Inspector > Control Element, e.g. Input! Build a single control row:
6338 * <div class="inspector__row">
6339 * <label class="inspector__label" for="...">Label</label>
6340 * <div class="inspector__control"><input|textarea|select class="inspector__input" ...></div>
6341 * </div>
6342 *
6343 * @param {Object} control - schema control meta ({type,key,label,...})
6344 * @param {Object} props_schema - schema.props
6345 * @param {Object} data - current element data-* map
6346 * @param {string} uid - unique suffix for input ids
6347 * @param {Object} ctx - { el, builder, type, data }
6348 * @returns {HTMLElement}
6349 */
6350 function build_control(control, props_schema, data, uid, ctx) {
6351 var type = control.type;
6352 var key = control.key;
6353
6354 var label_text = control.label || key || '';
6355 var prop_meta = (key ? (props_schema[key] || { type: 'string' }) : { type: 'string' });
6356 var value = coerce_by_type( get_initial_value( key, data, props_schema ), prop_meta.type );
6357 // Allow value_from override (computed at render-time).
6358 if ( control && control.value_from && w.wpbc_bfb_inspector_factory_value_from[control.value_from] ) {
6359 try {
6360 var computed = w.wpbc_bfb_inspector_factory_value_from[control.value_from]( ctx || {} );
6361 value = coerce_by_type( computed, prop_meta.type );
6362 } catch ( e ) {
6363 console.warn( 'value_from failed for', control.value_from, e );
6364 }
6365 }
6366
6367 var input_id = 'wpbc_ins_' + key + '_' + uid;
6368
6369 var row_el = el( 'div', { 'class': 'inspector__row' } );
6370 var label_el = el( 'label', { 'for': input_id, 'class': 'inspector__label' }, label_text );
6371 var ctrl_wrap = el( 'div', { 'class': 'inspector__control' } );
6372
6373 var field_el;
6374
6375 // --- slot host (named UI injection) -----------------------------------
6376 if ( type === 'slot' && control.slot ) {
6377 // add a marker class for the layout chips row
6378 var classes = 'inspector__row inspector__row--slot';
6379 if ( control.slot === 'layout_chips' ) classes += ' inspector__row--layout-chips';
6380
6381 var slot_row = el( 'div', { 'class': classes } );
6382
6383 if ( label_text ) slot_row.appendChild( el( 'label', { 'class': 'inspector__label' }, label_text ) );
6384
6385 // add a data attribute on the host so both CSS and the safety-net can target it
6386 var host_attrs = { 'class': 'inspector__control' };
6387 if ( control.slot === 'layout_chips' ) host_attrs['data-bfb-slot'] = 'layout_chips';
6388
6389 var slot_host = el( 'div', host_attrs );
6390 slot_row.appendChild( slot_host );
6391
6392 var slot_fn = w.wpbc_bfb_inspector_factory_slots[control.slot];
6393 if ( typeof slot_fn === 'function' ) {
6394 setTimeout( function () {
6395 try {
6396 slot_fn( slot_host, ctx || {} );
6397 } catch ( e ) {
6398 console.warn( 'slot "' + control.slot + '" failed:', e );
6399 }
6400 }, 0 );
6401 } else {
6402 slot_host.appendChild( el( 'div', { 'class': 'wpbc_bfb__slot__missing' }, '[slot: ' + control.slot + ']' ) );
6403 }
6404 return slot_row;
6405 }
6406
6407
6408 if ( type === 'textarea' ) {
6409 field_el = el( 'textarea', {
6410 id : input_id,
6411 'data-inspector-key': key,
6412 rows : control.rows || 3,
6413 'class' : 'inspector__input'
6414 }, (value == null ? '' : String( value )) );
6415 } else if ( type === 'select' ) {
6416 field_el = el( 'select', {
6417 id : input_id,
6418 'data-inspector-key': key,
6419 'class' : 'inspector__input'
6420 } );
6421 normalize_select_options( control.options || [] ).forEach( function (opt) {
6422 var opt_el = el( 'option', { value: opt.value }, opt.label );
6423 if ( String( value ) === opt.value ) opt_el.setAttribute( 'selected', 'selected' );
6424 field_el.appendChild( opt_el );
6425 } );
6426 } else if ( type === 'checkbox' ) {
6427 // field_el = el( 'input', { id: input_id, type: 'checkbox', 'data-inspector-key': key, checked: !!value, 'class': 'inspector__input' } ); //.
6428
6429 // Render as toggle UI instead of label-left + checkbox. Note: we return the full toggle row here and skip the default row/label flow below.
6430 return build_toggle_row( input_id, key, !!value, label_text );
6431
6432 } else if ( type === 'range_number' ) {
6433 // --- new: slider + number (single key).
6434 var rn_id = 'wpbc_ins_' + key + '_' + uid;
6435 var rn_val = value; // from get_initial_value/prop_meta already.
6436 return build_range_number_row( rn_id, key, label_text, rn_val, control );
6437
6438 } else if ( type === 'len' ) {
6439 // --- new: length compound (value+unit+slider -> writes a single string key).
6440 return build_len_compound_row( control, props_schema, data, uid );
6441
6442 } else if ( type === 'color' ) {
6443 // Color picker (Coloris). Store as string (e.g., "#e0e0e0").
6444 field_el = el( 'input', {
6445 id : input_id,
6446 type : 'text',
6447 'data-inspector-key' : key,
6448 'data-inspector-type': 'color',
6449 'data-coloris' : '',
6450 'class' : 'inspector__input',
6451 'data-default-color' : ( value != null && value !== '' ? String(value) : (control.placeholder || '') )
6452 } );
6453 if ( value !== '' ) {
6454 field_el.value = String( value );
6455 }
6456 } else {
6457 // text/number default.
6458 var attrs = {
6459 id : input_id,
6460 type : (type === 'number') ? 'number' : 'text',
6461 'data-inspector-key': key,
6462 'class' : 'inspector__input'
6463 };
6464 // number constraints (schema or control)
6465 if ( type === 'number' ) {
6466 if ( Object.prototype.hasOwnProperty.call( prop_meta, 'min' ) ) attrs.min = prop_meta.min;
6467 if ( Object.prototype.hasOwnProperty.call( prop_meta, 'max' ) ) attrs.max = prop_meta.max;
6468 if ( Object.prototype.hasOwnProperty.call( prop_meta, 'step' ) ) attrs.step = prop_meta.step;
6469 if ( Object.prototype.hasOwnProperty.call( control, 'min' ) ) attrs.min = control.min;
6470 if ( Object.prototype.hasOwnProperty.call( control, 'max' ) ) attrs.max = control.max;
6471 if ( Object.prototype.hasOwnProperty.call( control, 'step' ) ) attrs.step = control.step;
6472 }
6473 field_el = el( 'input', attrs );
6474 if ( value !== '' ) field_el.value = String( value );
6475 }
6476
6477 ctrl_wrap.appendChild( field_el );
6478 row_el.appendChild( label_el );
6479 row_el.appendChild( ctrl_wrap );
6480 return row_el;
6481 }
6482
6483 /**
6484 * Schema > Inspector > Groups! Build an inspector group (collapsible).
6485 * Structure:
6486 * <section class="wpbc_bfb__inspector__group wpbc_ui__collapsible_group is-open" data-group="...">
6487 * <button type="button" class="group__header" role="button" aria-expanded="true" aria-controls="wpbc_collapsible_panel_X">
6488 * <h3>Group Title</h3>
6489 * <i class="wpbc_ui_el__vert_menu_root_section_icon menu_icon icon-1x wpbc-bi-chevron-right"></i>
6490 * </button>
6491 * <div class="group__fields" id="wpbc_collapsible_panel_X" aria-hidden="false"> …rows… </div>
6492 * </section>
6493 *
6494 * @param {Object} group
6495 * @param {Object} props_schema
6496 * @param {Object} data
6497 * @param {string} uid
6498 * @param {Object} ctx
6499 * @returns {HTMLElement}
6500 */
6501 function build_group(group, props_schema, data, uid, ctx) {
6502 var is_open = !!group.open;
6503 var panel_id = 'wpbc_collapsible_panel_' + uid + '_' + (group.key || 'g');
6504
6505 var section = el( 'section', {
6506 'class' : 'wpbc_bfb__inspector__group wpbc_ui__collapsible_group' + (is_open ? ' is-open' : ''),
6507 'data-group': group.key || ''
6508 } );
6509
6510 var header_btn = el( 'button', {
6511 type : 'button',
6512 'class' : 'group__header',
6513 role : 'button',
6514 'aria-expanded': is_open ? 'true' : 'false',
6515 'aria-controls': panel_id
6516 }, [
6517 el( 'h3', null, group.title || group.label || group.key || '' ),
6518 el( 'i', { 'class': 'wpbc_ui_el__vert_menu_root_section_icon menu_icon icon-1x wpbc-bi-chevron-right' } )
6519 ] );
6520
6521 var fields = el( 'div', {
6522 'class' : 'group__fields',
6523 id : panel_id,
6524 'aria-hidden': is_open ? 'false' : 'true'
6525 } );
6526
6527 function asArray(x) {
6528 if ( Array.isArray( x ) ) return x;
6529 if ( x && typeof x === 'object' ) return Object.values( x );
6530 return x != null ? [ x ] : [];
6531 }
6532
6533 asArray( group.controls ).forEach( function (control) {
6534 fields.appendChild( build_control( control, props_schema, data, uid, ctx ) );
6535 } );
6536
6537 section.appendChild( header_btn );
6538 section.appendChild( fields );
6539 return section;
6540 }
6541
6542 /**
6543 * Schema > Inspector > Header! Build inspector header with action buttons wired to existing data-action handlers.
6544 *
6545 * @param {Array<string>} header_actions
6546 * @param {string} title_text
6547 * @returns {HTMLElement}
6548 */
6549 function build_header(inspector_ui, title_fallback, schema_for_type) {
6550
6551 inspector_ui = inspector_ui || {};
6552 schema_for_type = schema_for_type || {};
6553 var variant = inspector_ui.header_variant || 'minimal';
6554 var headerActions = inspector_ui.header_actions
6555 || schema_for_type.header_actions
6556 || [ 'deselect', 'scrollto', 'move-up', 'move-down', 'duplicate', 'delete' ];
6557
6558 var title = inspector_ui.title || title_fallback || '';
6559 var description = inspector_ui.description || '';
6560
6561 // helper to create a button for either header style
6562 function actionBtn(act, minimal) {
6563 if ( minimal ) {
6564 return el( 'button', { type: 'button', 'class': 'button-link', 'data-action': act }, '' );
6565 }
6566 // toolbar variant (rich)
6567 var iconMap = {
6568 'deselect' : 'wpbc_icn_remove_done',
6569 'scrollto' : 'wpbc_icn_ads_click filter_center_focus',
6570 'move-up' : 'wpbc_icn_arrow_upward',
6571 'move-down': 'wpbc_icn_arrow_downward',
6572 'duplicate': 'wpbc_icn_content_copy',
6573 'delete' : 'wpbc_icn_delete_outline'
6574 };
6575 var classes = 'button button-secondary wpbc_ui_control wpbc_ui_button';
6576 if ( act === 'delete' ) classes += ' wpbc_ui_button_danger button-link-delete';
6577
6578 var btn = el( 'button', {
6579 type : 'button',
6580 'class' : classes,
6581 'data-action': act,
6582 'aria-label' : act.replace( /-/g, ' ' )
6583 } );
6584
6585 if ( act === 'delete' ) {
6586 btn.appendChild( el( 'span', { 'class': 'in-button-text' }, 'Delete' ) );
6587 btn.appendChild( document.createTextNode( ' ' ) ); // minor spacing before icon
6588 }
6589 btn.appendChild( el( 'i', { 'class': 'menu_icon icon-1x ' + (iconMap[act] || '') } ) );
6590 return btn;
6591 }
6592
6593 // === minimal header (existing look; default) ===
6594 if ( variant !== 'toolbar' ) {
6595 var header = el( 'header', { 'class': 'wpbc_bfb__inspector__header' } );
6596 header.appendChild( el( 'h3', null, title || '' ) );
6597
6598 var actions = el( 'div', { 'class': 'wpbc_bfb__inspector__header_actions' } );
6599 headerActions.forEach( function (act) {
6600 actions.appendChild( actionBtn( act, /*minimal*/true ) );
6601 } );
6602 header.appendChild( actions );
6603 return header;
6604 }
6605
6606 // === toolbar header (rich title/desc + grouped buttons) ===
6607 var root = el( 'div', { 'class': 'wpbc_bfb__inspector__head' } );
6608 var wrap = el( 'div', { 'class': 'header_container' } );
6609 var left = el( 'div', { 'class': 'header_title_content' } );
6610 var h3 = el( 'h3', { 'class': 'title' }, title || '' );
6611 left.appendChild( h3 );
6612 if ( description ) {
6613 left.appendChild( el( 'div', { 'class': 'desc' }, description ) );
6614 }
6615
6616 var right = el( 'div', { 'class': 'actions wpbc_ajx_toolbar wpbc_no_borders' } );
6617 var uiC = el( 'div', { 'class': 'ui_container ui_container_small' } );
6618 var uiG = el( 'div', { 'class': 'ui_group' } );
6619
6620 // Split into visual groups: first 2, next 2, then the rest.
6621 var g1 = el( 'div', { 'class': 'ui_element' } );
6622 var g2 = el( 'div', { 'class': 'ui_element' } );
6623 var g3 = el( 'div', { 'class': 'ui_element' } );
6624
6625 headerActions.slice( 0, 2 ).forEach( function (act) {
6626 g1.appendChild( actionBtn( act, false ) );
6627 } );
6628 headerActions.slice( 2, 4 ).forEach( function (act) {
6629 g2.appendChild( actionBtn( act, false ) );
6630 } );
6631 headerActions.slice( 4 ).forEach( function (act) {
6632 g3.appendChild( actionBtn( act, false ) );
6633 } );
6634
6635 uiG.appendChild( g1 );
6636 uiG.appendChild( g2 );
6637 uiG.appendChild( g3 );
6638 uiC.appendChild( uiG );
6639 right.appendChild( uiC );
6640
6641 wrap.appendChild( left );
6642 wrap.appendChild( right );
6643 root.appendChild( wrap );
6644
6645 return root;
6646 }
6647
6648
6649 function factory_render(panel_el, schema_for_type, data, opts) {
6650 if ( !panel_el ) return panel_el;
6651
6652 schema_for_type = schema_for_type || {};
6653 var props_schema = (schema_for_type.schema && schema_for_type.schema.props) ? schema_for_type.schema.props : {};
6654 var inspector_ui = (schema_for_type.inspector_ui || {});
6655 var groups = inspector_ui.groups || [];
6656
6657 var header_actions = inspector_ui.header_actions || schema_for_type.header_actions || [];
6658 var title_text = (opts && opts.title) || inspector_ui.title || schema_for_type.label || (data && data.label) || '';
6659
6660 // Prepare rendering context for slots/value_from, etc.
6661 var ctx = {
6662 el : opts && opts.el || null,
6663 builder: opts && opts.builder || null,
6664 type : opts && opts.type || null,
6665 data : data || {}
6666 };
6667
6668 // clear panel.
6669 while ( panel_el.firstChild ) panel_el.removeChild( panel_el.firstChild );
6670
6671 var uid = Math.random().toString( 36 ).slice( 2, 8 );
6672
6673 // header.
6674 panel_el.appendChild( build_header( inspector_ui, title_text, schema_for_type ) );
6675
6676
6677 // groups.
6678 groups.forEach( function (g) {
6679 panel_el.appendChild( build_group( g, props_schema, data || {}, uid, ctx ) );
6680 } );
6681
6682 // ARIA sync for toggles created here (ensure aria-checked matches state).
6683 try {
6684 // Centralized UI normalizers (toggles + A11y): handled in Core.
6685 UI.apply_post_render( panel_el );
6686 try {
6687 wire_len_group( panel_el );
6688 // Initialize Coloris on color inputs rendered in this panel.
6689 init_coloris_pickers( panel_el );
6690 } catch ( _ ) { }
6691 } catch ( _ ) { }
6692
6693 return panel_el;
6694 }
6695
6696 UI.WPBC_BFB_Inspector_Factory = { render: factory_render }; // overwrite/refresh
6697
6698 // ---- Built-in slot + value_from for Sections ----
6699
6700 function slot_layout_chips(host, ctx) {
6701 try {
6702 var L = w.WPBC_BFB_Core && w.WPBC_BFB_Core.UI && w.WPBC_BFB_Core.UI.WPBC_BFB_Layout_Chips;
6703 if ( L && typeof L.render_for_section === 'function' ) {
6704 L.render_for_section( ctx.builder, ctx.el, host );
6705 } else {
6706 host.appendChild( document.createTextNode( '[layout_chips not available]' ) );
6707 }
6708 } catch ( e ) {
6709 console.warn( 'wpbc_bfb_slot_layout_chips failed:', e );
6710 }
6711 }
6712
6713 w.wpbc_bfb_inspector_factory_slots.layout_chips = slot_layout_chips;
6714
6715 function value_from_compute_section_columns(ctx) {
6716 try {
6717 var row = ctx && ctx.el && ctx.el.querySelector && ctx.el.querySelector( ':scope > .wpbc_bfb__row' );
6718 if ( !row ) return 1;
6719 var n = row.querySelectorAll( ':scope > .wpbc_bfb__column' ).length || 1;
6720 if ( n < 1 ) n = 1;
6721 if ( n > 4 ) n = 4;
6722 return n;
6723 } catch ( _ ) {
6724 return 1;
6725 }
6726 }
6727
6728 w.wpbc_bfb_inspector_factory_value_from.compute_section_columns = value_from_compute_section_columns;
6729 }
6730
6731 // 3) Inspector class.
6732
6733 class WPBC_BFB_Inspector {
6734
6735 constructor(panel_el, builder) {
6736 this.panel = panel_el || this._create_fallback_panel();
6737 this.builder = builder;
6738 this.selected_el = null;
6739 this._render_timer = null;
6740
6741 this._on_delegated_input = (e) => this._apply_control_from_event( e );
6742 this._on_delegated_change = (e) => this._apply_control_from_event( e );
6743 this.panel.addEventListener( 'input', this._on_delegated_input, true );
6744 this.panel.addEventListener( 'change', this._on_delegated_change, true );
6745
6746 this._on_delegated_click = (e) => {
6747 const btn = e.target.closest( '[data-action]' );
6748 if ( !btn || !this.panel.contains( btn ) ) return;
6749 e.preventDefault();
6750 e.stopPropagation();
6751
6752 const action = btn.getAttribute( 'data-action' );
6753 const el = this.selected_el;
6754 if ( !el ) return;
6755
6756 w.WPBC_BFB_Inspector_Actions?.run( action, {
6757 builder: this.builder,
6758 el,
6759 panel : this.panel,
6760 event : e
6761 } );
6762
6763 if ( action === 'delete' ) this.clear();
6764 };
6765 this.panel.addEventListener( 'click', this._on_delegated_click );
6766 }
6767
6768 _post_render_ui() {
6769 try {
6770 var UI = w.WPBC_BFB_Core && w.WPBC_BFB_Core.UI;
6771 if ( UI && typeof UI.apply_post_render === 'function' ) {
6772 UI.apply_post_render( this.panel );
6773 }
6774 // NEW: wire slider/number/unit syncing for length & range_number groups.
6775 try {
6776 wire_len_group( this.panel );
6777 init_coloris_pickers( this.panel );
6778 } catch ( _ ) {
6779 }
6780 } catch ( e ) {
6781 _wpbc?.dev?.error?.( 'inspector._post_render_ui', e );
6782 }
6783 }
6784
6785
6786 _apply_control_from_event(e) {
6787 if ( !this.panel.contains( e.target ) ) return;
6788
6789 const t = /** @type {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} */ (e.target);
6790 const key = t?.dataset?.inspectorKey;
6791 if ( !key ) return;
6792
6793 const el = this.selected_el;
6794 if ( !el || !document.body.contains( el ) ) return;
6795
6796 let v;
6797 if ( t instanceof HTMLInputElement && t.type === 'checkbox' ) {
6798 v = !!t.checked;
6799 t.setAttribute( 'aria-checked', v ? 'true' : 'false' ); // Keep ARIA state in sync for toggles (schema and template paths).
6800 } else if ( t instanceof HTMLInputElement && t.type === 'number' ) {
6801 v = (t.value === '' ? '' : Number( t.value ));
6802 } else {
6803 v = t.value;
6804 }
6805
6806 if ( key === 'id' ) {
6807 const unique = this.builder?.id?.set_field_id?.( el, v );
6808 if ( unique != null && t.value !== unique ) t.value = unique;
6809
6810 } else if ( key === 'name' ) {
6811 const unique = this.builder?.id?.set_field_name?.( el, v );
6812 if ( unique != null && t.value !== unique ) t.value = unique;
6813
6814 } else if ( key === 'html_id' ) {
6815 const applied = this.builder?.id?.set_field_html_id?.( el, v );
6816 if ( applied != null && t.value !== applied ) t.value = applied;
6817
6818 } else if ( key === 'columns' && el.classList.contains( 'wpbc_bfb__section' ) ) {
6819 const v_int = parseInt( String( v ), 10 );
6820 if ( Number.isFinite( v_int ) ) {
6821 const clamped = w.WPBC_BFB_Core.WPBC_BFB_Sanitize.clamp( v_int, 1, 4 );
6822 this.builder?.set_section_columns?.( el, clamped );
6823 if ( String( clamped ) !== t.value ) t.value = String( clamped );
6824 }
6825
6826 } else {
6827 if ( t instanceof HTMLInputElement && t.type === 'checkbox' ) {
6828 el.setAttribute( 'data-' + key, String( !!v ) );
6829 } else if ( t instanceof HTMLInputElement && t.type === 'number' ) {
6830 if ( t.value === '' || !Number.isFinite( v ) ) {
6831 el.removeAttribute( 'data-' + key );
6832 } else {
6833 el.setAttribute( 'data-' + key, String( v ) );
6834 }
6835 } else if ( v == null ) {
6836 el.removeAttribute( 'data-' + key );
6837 } else {
6838 el.setAttribute( 'data-' + key, (typeof v === 'object') ? JSON.stringify( v ) : String( v ) );
6839 }
6840 }
6841
6842 // Update preview/overlay
6843 if ( el.classList.contains( 'wpbc_bfb__field' ) ) {
6844 if ( this.builder?.preview_mode ) this.builder.render_preview( el );
6845 else this.builder.add_overlay_toolbar( el );
6846 } else {
6847 this.builder.add_overlay_toolbar( el );
6848 }
6849
6850 if ( this._needs_rerender( el, key, e ) ) {
6851 this._schedule_render_preserving_focus( 0 );
6852 }
6853 }
6854
6855 _needs_rerender(el, key, _e) {
6856 if ( el.classList.contains( 'wpbc_bfb__section' ) && key === 'columns' ) return true;
6857 return false;
6858 }
6859
6860 bind_to_field(field_el) {
6861 this.selected_el = field_el;
6862 this.render();
6863 }
6864
6865 clear() {
6866 this.selected_el = null;
6867 if ( this._render_timer ) {
6868 clearTimeout( this._render_timer );
6869 this._render_timer = null;
6870 }
6871 // Also clear the section-cols hint on empty state.
6872 this.panel.removeAttribute('data-bfb-section-cols');
6873 this.panel.innerHTML = '<div class="wpbc_bfb__inspector__empty">Select a field to edit its options.</div>';
6874 }
6875
6876 _schedule_render_preserving_focus(delay = 200) {
6877 const active = /** @type {HTMLInputElement|HTMLTextAreaElement|HTMLElement|null} */ (document.activeElement);
6878 const activeKey = active?.dataset?.inspectorKey || null;
6879 let selStart = null, selEnd = null;
6880
6881 if ( active && 'selectionStart' in active && 'selectionEnd' in active ) {
6882 // @ts-ignore
6883 selStart = active.selectionStart;
6884 // @ts-ignore
6885 selEnd = active.selectionEnd;
6886 }
6887
6888 if ( this._render_timer ) clearTimeout( this._render_timer );
6889 this._render_timer = /** @type {unknown} */ (setTimeout( () => {
6890 this.render();
6891 if ( activeKey ) {
6892 const next = /** @type {HTMLInputElement|HTMLTextAreaElement|HTMLElement|null} */ (
6893 this.panel.querySelector( `[data-inspector-key="${activeKey}"]` )
6894 );
6895 if ( next ) {
6896 next.focus();
6897 try {
6898 if ( selStart != null && selEnd != null && typeof next.setSelectionRange === 'function' ) {
6899 // @ts-ignore
6900 next.setSelectionRange( selStart, selEnd );
6901 }
6902 } catch( e ){ _wpbc?.dev?.error( '_render_timer', e ); }
6903 }
6904 }
6905 }, delay ));
6906 }
6907
6908 render() {
6909
6910 const el = this.selected_el;
6911 if ( !el || !document.body.contains( el ) ) return this.clear();
6912
6913 // Reset section-cols hint unless we set it later for a section.
6914 this.panel.removeAttribute( 'data-bfb-section-cols' );
6915
6916 const prev_scroll = this.panel.scrollTop;
6917
6918 // Section
6919 if ( el.classList.contains( 'wpbc_bfb__section' ) ) {
6920 let tpl = null;
6921 try {
6922 tpl = (w.wp && wp.template && document.getElementById( 'tmpl-wpbc-bfb-inspector-section' )) ? wp.template( 'wpbc-bfb-inspector-section' ) : null;
6923 } catch ( _ ) {
6924 tpl = null;
6925 }
6926
6927 if ( tpl ) {
6928 this.panel.innerHTML = tpl( {} );
6929 this._enforce_default_group_open();
6930 this._set_panel_section_cols( el );
6931 this._post_render_ui();
6932 this.panel.scrollTop = prev_scroll;
6933 return;
6934 }
6935
6936 const Factory = w.WPBC_BFB_Core.UI && w.WPBC_BFB_Core.UI.WPBC_BFB_Inspector_Factory;
6937 const schemas = w.WPBC_BFB_Schemas || {};
6938 const entry = schemas['section'] || null;
6939 if ( entry && Factory ) {
6940 this.panel.innerHTML = '';
6941 Factory.render(
6942 this.panel,
6943 entry,
6944 {},
6945 { el, builder: this.builder, type: 'section', title: entry.label || 'Section' }
6946 );
6947 this._enforce_default_group_open();
6948
6949 // --- Safety net: if for any reason the slot didn’t render chips, inject them now.
6950 try {
6951 const hasSlotHost =
6952 this.panel.querySelector( '[data-bfb-slot="layout_chips"]' ) ||
6953 this.panel.querySelector( '.inspector__row--layout-chips .wpbc_bfb__layout_chips' ) ||
6954 this.panel.querySelector( '#wpbc_bfb__layout_chips_host' );
6955
6956 const hasChips =
6957 !!this.panel.querySelector( '.wpbc_bfb__layout_chip' );
6958
6959 if ( !hasChips ) {
6960 // Create a host if missing and render chips into it.
6961 const host = (function ensureHost(root) {
6962 let h =
6963 root.querySelector( '[data-bfb-slot="layout_chips"]' ) ||
6964 root.querySelector( '.inspector__row--layout-chips .wpbc_bfb__layout_chips' ) ||
6965 root.querySelector( '#wpbc_bfb__layout_chips_host' );
6966 if ( h ) return h;
6967 // Fallback host inside (or after) the “layout” group
6968 const fields =
6969 root.querySelector( '.wpbc_bfb__inspector__group[data-group="layout"] .group__fields' ) ||
6970 root.querySelector( '.group__fields' ) || root;
6971 const row = document.createElement( 'div' );
6972 row.className = 'inspector__row inspector__row--layout-chips';
6973 const lab = document.createElement( 'label' );
6974 lab.className = 'inspector__label';
6975 lab.textContent = 'Layout';
6976 const ctl = document.createElement( 'div' );
6977 ctl.className = 'inspector__control';
6978 h = document.createElement( 'div' );
6979 h.className = 'wpbc_bfb__layout_chips';
6980 h.setAttribute( 'data-bfb-slot', 'layout_chips' );
6981 ctl.appendChild( h );
6982 row.appendChild( lab );
6983 row.appendChild( ctl );
6984 fields.appendChild( row );
6985 return h;
6986 })( this.panel );
6987
6988 const L = (w.WPBC_BFB_Core && w.WPBC_BFB_Core.UI && w.WPBC_BFB_Core.UI.WPBC_BFB_Layout_Chips) ;
6989 if ( L && typeof L.render_for_section === 'function' ) {
6990 host.innerHTML = '';
6991 L.render_for_section( this.builder, el, host );
6992 }
6993 }
6994 } catch( e ){ _wpbc?.dev?.error( 'WPBC_BFB_Inspector - render', e ); }
6995
6996 this._set_panel_section_cols( el );
6997 this.panel.scrollTop = prev_scroll;
6998 return;
6999 }
7000
7001 this.panel.innerHTML = '<div class="wpbc_bfb__inspector__empty">Select a field to edit its options.</div>';
7002 return;
7003 }
7004
7005 // Field
7006 if ( !el.classList.contains( 'wpbc_bfb__field' ) ) return this.clear();
7007
7008 const data = w.WPBC_BFB_Core.WPBC_Form_Builder_Helper.get_all_data_attributes( el );
7009 const type = data.type || 'text';
7010
7011 function _get_tpl(id) {
7012 if ( !w.wp || !wp.template ) return null;
7013 if ( !document.getElementById( 'tmpl-' + id ) ) return null;
7014 try {
7015 return wp.template( id );
7016 } catch ( e ) {
7017 return null;
7018 }
7019 }
7020
7021 const tpl_id = `wpbc-bfb-inspector-${type}`;
7022 const tpl = _get_tpl( tpl_id );
7023 const generic_tpl = _get_tpl( 'wpbc-bfb-inspector-generic' );
7024
7025 const schemas = w.WPBC_BFB_Schemas || {};
7026 const schema_for_type = schemas[type] || null;
7027 const Factory = w.WPBC_BFB_Core.UI && w.WPBC_BFB_Core.UI.WPBC_BFB_Inspector_Factory;
7028
7029 if ( tpl ) {
7030 // NEW: merge schema defaults so missing keys (esp. booleans) honor defaults on first paint
7031 const hasOwn = Function.call.bind( Object.prototype.hasOwnProperty );
7032 const props = (schema_for_type && schema_for_type.schema && schema_for_type.schema.props) ? schema_for_type.schema.props : {};
7033 const merged = { ...data };
7034 if ( props ) {
7035 Object.keys( props ).forEach( (k) => {
7036 const meta = props[k] || {};
7037 if ( !hasOwn( data, k ) || data[k] === '' ) {
7038 if ( hasOwn( meta, 'default' ) ) {
7039 // Coerce booleans to a real boolean; leave others as-is
7040 merged[k] = (meta.type === 'boolean') ? !!meta.default : meta.default;
7041 }
7042 } else if ( meta.type === 'boolean' ) {
7043 // Normalize truthy strings into booleans for templates that check on truthiness
7044 const v = data[k];
7045 merged[k] = (v === true || v === 'true' || v === 1 || v === '1');
7046 }
7047 } );
7048 }
7049 this.panel.innerHTML = tpl( merged );
7050
7051 this._post_render_ui();
7052 } else if ( schema_for_type && Factory ) {
7053 this.panel.innerHTML = '';
7054 Factory.render(
7055 this.panel,
7056 schema_for_type,
7057 { ...data },
7058 { el, builder: this.builder, type, title: data.label || '' }
7059 );
7060 // Ensure toggle normalizers and slider/number/unit wiring are attached.
7061 this._post_render_ui();
7062 } else if ( generic_tpl ) {
7063 this.panel.innerHTML = generic_tpl( { ...data } );
7064 this._post_render_ui();
7065 } else {
7066
7067 const msg = `There are no Inspector wp.template "${tpl_id}" or Schema for this "${String( type || '' )}" element.`;
7068 this.panel.innerHTML = '';
7069 const div = document.createElement( 'div' );
7070 div.className = 'wpbc_bfb__inspector__empty';
7071 div.textContent = msg; // safe.
7072 this.panel.appendChild( div );
7073 }
7074
7075 this._enforce_default_group_open();
7076 this.panel.scrollTop = prev_scroll;
7077 }
7078
7079 _enforce_default_group_open() {
7080 const groups = Array.from( this.panel.querySelectorAll( '.wpbc_bfb__inspector__group' ) );
7081 if ( !groups.length ) return;
7082
7083 let found = false;
7084 groups.forEach( (g) => {
7085 if ( !found && g.classList.contains( 'is-open' ) ) {
7086 found = true;
7087 } else {
7088 if ( g.classList.contains( 'is-open' ) ) {
7089 g.classList.remove( 'is-open' );
7090 g.dispatchEvent( new Event( 'wpbc:collapsible:close', { bubbles: true } ) );
7091 } else {
7092 g.classList.remove( 'is-open' );
7093 }
7094 }
7095 } );
7096
7097 if ( !found ) {
7098 groups[0].classList.add( 'is-open' );
7099 groups[0].dispatchEvent( new Event( 'wpbc:collapsible:open', { bubbles: true } ) );
7100 }
7101 }
7102
7103 /**
7104 * Set data-bfb-section-cols on the inspector panel based on the current section.
7105 * Uses the registered compute fn if available; falls back to direct DOM.
7106 * @param {HTMLElement} sectionEl
7107 */
7108 _set_panel_section_cols(sectionEl) {
7109 try {
7110 // Prefer the already-registered value_from helper if present.
7111 var compute = w.wpbc_bfb_inspector_factory_value_from && w.wpbc_bfb_inspector_factory_value_from.compute_section_columns;
7112
7113 var cols = 1;
7114 if ( typeof compute === 'function' ) {
7115 cols = compute( { el: sectionEl } ) || 1;
7116 } else {
7117 // Fallback: compute directly from the DOM.
7118 var row = sectionEl && sectionEl.querySelector( ':scope > .wpbc_bfb__row' );
7119 cols = row ? (row.querySelectorAll( ':scope > .wpbc_bfb__column' ).length || 1) : 1;
7120 if ( cols < 1 ) cols = 1;
7121 if ( cols > 4 ) cols = 4;
7122 }
7123 this.panel.setAttribute( 'data-bfb-section-cols', String( cols ) );
7124 } catch ( _ ) {
7125 }
7126 }
7127
7128
7129 _create_fallback_panel() {
7130 const p = document.createElement( 'div' );
7131 p.id = 'wpbc_bfb__inspector';
7132 p.className = 'wpbc_bfb__inspector';
7133 document.body.appendChild( p );
7134 return /** @type {HTMLDivElement} */ (p);
7135 }
7136 }
7137
7138 // Export class + ready signal.
7139 w.WPBC_BFB_Inspector = WPBC_BFB_Inspector;
7140 document.dispatchEvent( new Event( 'wpbc_bfb_inspector_ready' ) );
7141
7142 })( window );
7143
7144 //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJmYi1jb3JlLmpzIiwiYmZiLWZpZWxkcy5qcyIsImJmYi11aS5qcyIsImJmYi1zeW5jLWNvZGUtdG9vbHMtdGFiLmpzIiwiYmZiLWluc3BlY3Rvci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDbC9DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ3pvQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ2p4RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNoU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6IndwYmNfYmZiLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbi8vID09IEZpbGUgIC9pbmNsdWRlcy9wYWdlLWZvcm0tYnVpbGRlci9fb3V0L2NvcmUvYmZiLWNvcmUuanMgPT0gfCAyMDI1LTA5LTEwIDE1OjQ3XHJcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4oZnVuY3Rpb24gKCB3ICkge1xyXG5cdCd1c2Ugc3RyaWN0JztcclxuXHJcblx0Ly8gU2luZ2xlIGdsb2JhbCBuYW1lc3BhY2UgKGlkZW1wb3RlbnQgJiBsb2FkLW9yZGVyIHNhZmUpLlxyXG5cdGNvbnN0IENvcmUgPSAoIHcuV1BCQ19CRkJfQ29yZSA9IHcuV1BCQ19CRkJfQ29yZSB8fCB7fSApO1xyXG5cdGNvbnN0IFVJICAgPSAoIENvcmUuVUkgPSBDb3JlLlVJIHx8IHt9ICk7XHJcblxyXG5cdC8qKlxyXG5cdCAqIENvcmUgc2FuaXRpemUvZXNjYXBlL25vcm1hbGl6ZSBoZWxwZXJzLlxyXG5cdCAqIEFsbCBtZXRob2RzIHVzZSBzbmFrZV9jYXNlOyBjYW1lbENhc2UgYWxpYXNlcyBhcmUgcHJvdmlkZWQgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LlxyXG5cdCAqL1xyXG5cdENvcmUuV1BCQ19CRkJfU2FuaXRpemUgPSBjbGFzcyB7XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBFc2NhcGUgdGV4dCBmb3Igc2FmZSB1c2UgaW4gQ1NTIHNlbGVjdG9ycy5cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBzIC0gcmF3IHNlbGVjdG9yIGZyYWdtZW50XHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgZXNjX2NzcyhzKSB7XHJcblx0XHRcdHJldHVybiAody5DU1MgJiYgdy5DU1MuZXNjYXBlKSA/IHcuQ1NTLmVzY2FwZSggU3RyaW5nKCBzICkgKSA6IFN0cmluZyggcyApLnJlcGxhY2UoIC8oW15cXHctXSkvZywgJ1xcXFwkMScgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVzY2FwZSBhIHZhbHVlIGZvciBhdHRyaWJ1dGUgc2VsZWN0b3JzLCBlLmcuIFtkYXRhLWlkPVwiPHZhbHVlPlwiXS5cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSB2XHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgZXNjX2F0dHJfdmFsdWVfZm9yX3NlbGVjdG9yKHYpIHtcclxuXHRcdFx0cmV0dXJuIFN0cmluZyggdiApXHJcblx0XHRcdFx0LnJlcGxhY2UoIC9cXFxcL2csICdcXFxcXFxcXCcgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvXCIvZywgJ1xcXFxcIicgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvXFxuL2csICdcXFxcQSAnIClcclxuXHRcdFx0XHQucmVwbGFjZSggL1xcXS9nLCAnXFxcXF0nICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBTYW5pdGl6ZSBpbnRvIGEgYnJvYWRseSBjb21wYXRpYmxlIEhUTUwgaWQ6IGxldHRlcnMsIGRpZ2l0cywgLSBfIDogLiA7IG11c3Qgc3RhcnQgd2l0aCBhIGxldHRlci5cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSB2XHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgc2FuaXRpemVfaHRtbF9pZCh2KSB7XHJcblx0XHRcdGxldCBzID0gKHYgPT0gbnVsbCA/ICcnIDogU3RyaW5nKCB2ICkpLnRyaW0oKTtcclxuXHRcdFx0cyAgICAgPSBzXHJcblx0XHRcdFx0LnJlcGxhY2UoIC9cXHMrL2csICctJyApXHJcblx0XHRcdFx0LnJlcGxhY2UoIC9bXkEtWmEtejAtOVxcLV9cXDouXS9nLCAnLScgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvLSsvZywgJy0nIClcclxuXHRcdFx0XHQucmVwbGFjZSggL15bLV8uOl0rfFstXy46XSskL2csICcnICk7XHJcblx0XHRcdGlmICggIXMgKSByZXR1cm4gJ2ZpZWxkJztcclxuXHRcdFx0aWYgKCAhL15bQS1aYS16XS8udGVzdCggcyApICkgcyA9ICdmLScgKyBzO1xyXG5cdFx0XHRyZXR1cm4gcztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNhbml0aXplIGludG8gYSBzYWZlIEhUTUwgbmFtZSB0b2tlbjogbGV0dGVycywgZGlnaXRzLCBfIC1cclxuXHRcdCAqIE11c3Qgc3RhcnQgd2l0aCBhIGxldHRlcjsgbm8gZG90cy9icmFja2V0cy9zcGFjZXMuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHNhbml0aXplX2h0bWxfbmFtZSh2KSB7XHJcblxyXG5cdFx0XHRsZXQgcyA9ICh2ID09IG51bGwgPyAnJyA6IFN0cmluZyggdiApKS50cmltKCk7XHJcblxyXG5cdFx0XHRzID0gcy5yZXBsYWNlKCAvXFxzKy9nLCAnXycgKS5yZXBsYWNlKCAvW15BLVphLXowLTlfLV0vZywgJ18nICkucmVwbGFjZSggL18rL2csICdfJyApO1xyXG5cclxuXHRcdFx0aWYgKCAhIHMgKSB7XHJcblx0XHRcdFx0cyA9ICdmaWVsZCc7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCAhIC9eW0EtWmEtel0vLnRlc3QoIHMgKSApIHtcclxuXHRcdFx0XHRzID0gJ2ZfJyArIHM7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIHM7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBFc2NhcGUgZm9yIEhUTUwgdGV4dC9hdHRyaWJ1dGVzIChub3QgVVJMcykuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIGVzY2FwZV9odG1sKHYpIHtcclxuXHRcdFx0aWYgKCB2ID09IG51bGwgKSB7XHJcblx0XHRcdFx0cmV0dXJuICcnO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBTdHJpbmcoIHYgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvJi9nLCAnJmFtcDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggL1wiL2csICcmcXVvdDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggLycvZywgJyYjMDM5OycgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvPC9nLCAnJmx0OycgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvPi9nLCAnJmd0OycgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVzY2FwZSBtaW5pbWFsIHNldCBmb3IgYXR0cmlidXRlLXNhZmV0eSB3aXRob3V0IHNsdWdnaW5nLlxyXG5cdFx0ICogS2VlcHMgb3JpZ2luYWwgaHVtYW4gdGV4dDsgZXNjYXBlcyAmLCA8LCA+LCBcIiBhbmQgJyBvbmx5LlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHNcclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBlc2NhcGVfdmFsdWVfZm9yX2F0dHIocykge1xyXG5cdFx0XHRyZXR1cm4gU3RyaW5nKCBzID09IG51bGwgPyAnJyA6IHMgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvJi9nLCAnJmFtcDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggLzwvZywgJyZsdDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggLz4vZywgJyZndDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggL1wiL2csICcmcXVvdDsnIClcclxuXHRcdFx0XHQucmVwbGFjZSggLycvZywgJyYjMzk7JyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU2FuaXRpemUgYSBzcGFjZS1zZXBhcmF0ZWQgQ1NTIGNsYXNzIGxpc3QuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHNhbml0aXplX2Nzc19jbGFzc2xpc3Qodikge1xyXG5cdFx0XHRpZiAoIHYgPT0gbnVsbCApIHJldHVybiAnJztcclxuXHRcdFx0cmV0dXJuIFN0cmluZyggdiApLnJlcGxhY2UoIC9bXlxcd1xcLSBdKy9nLCAnICcgKS5yZXBsYWNlKCAvXFxzKy9nLCAnICcgKS50cmltKCk7XHJcblx0XHR9XHJcbi8vID09IE5FVyA9PVxyXG5cdFx0LyoqXHJcblx0XHQgKiBUdXJuIGFuIGFyYml0cmFyeSB2YWx1ZSBpbnRvIGEgY29uc2VydmF0aXZlIFwidG9rZW5cIiAodW5kZXJzY29yZXMsIGh5cGhlbnMgYWxsb3dlZCkuXHJcblx0XHQgKiBVc2VmdWwgZm9yIHNob3J0Y29kZSB0b2tlbnMsIGlkcyBpbiBwbGFpbiB0ZXh0LCBldGMuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHRvX3Rva2VuKHYpIHtcclxuXHRcdFx0cmV0dXJuIFN0cmluZyggdiA/PyAnJyApXHJcblx0XHRcdFx0LnRyaW0oKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvXFxzKy9nLCAnXycgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvW15BLVphLXowLTlfXFwtXS9nLCAnJyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ29udmVydCB0byBrZWJhYi1jYXNlIChsZXR0ZXJzLCBkaWdpdHMsIGh5cGhlbnMpLlxyXG5cdFx0ICogQHBhcmFtIHthbnl9IHZcclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyB0b19rZWJhYih2KSB7XHJcblx0XHRcdHJldHVybiBTdHJpbmcoIHYgPz8gJycgKVxyXG5cdFx0XHRcdC50cmltKClcclxuXHRcdFx0XHQucmVwbGFjZSggL1tfXFxzXSsvZywgJy0nIClcclxuXHRcdFx0XHQucmVwbGFjZSggL1teQS1aYS16MC05LV0vZywgJycgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvLSsvZywgJy0nIClcclxuXHRcdFx0XHQudG9Mb3dlckNhc2UoKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFRydXRoeSBub3JtYWxpemF0aW9uIGZvciBmb3JtLWxpa2UgaW5wdXRzOiB0cnVlLCAndHJ1ZScsIDEsICcxJywgJ3llcycsICdvbicuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge2Jvb2xlYW59XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBpc190cnV0aHkodikge1xyXG5cdFx0XHRpZiAoIHR5cGVvZiB2ID09PSAnYm9vbGVhbicgKSByZXR1cm4gdjtcclxuXHRcdFx0Y29uc3QgcyA9IFN0cmluZyggdiA/PyAnJyApLnRyaW0oKS50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHRyZXR1cm4gcyA9PT0gJ3RydWUnIHx8IHMgPT09ICcxJyB8fCBzID09PSAneWVzJyB8fCBzID09PSAnb24nO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ29lcmNlIHRvIGJvb2xlYW4gd2l0aCBhbiBvcHRpb25hbCBkZWZhdWx0IGZvciBlbXB0eSB2YWx1ZXMuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHBhcmFtIHtib29sZWFufSBbZGVmPWZhbHNlXVxyXG5cdFx0ICogQHJldHVybnMge2Jvb2xlYW59XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBjb2VyY2VfYm9vbGVhbih2LCBkZWYgPSBmYWxzZSkge1xyXG5cdFx0XHRpZiAoIHYgPT0gbnVsbCB8fCB2ID09PSAnJyApIHJldHVybiBkZWY7XHJcblx0XHRcdHJldHVybiB0aGlzLmlzX3RydXRoeSggdiApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogUGFyc2UgYSBcInBlcmNlbnQtbGlrZVwiIHZhbHVlICgnMzMnfCczMyUnfDMzKSB3aXRoIGZhbGxiYWNrLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd8bnVtYmVyfG51bGx8dW5kZWZpbmVkfSB2XHJcblx0XHQgKiBAcGFyYW0ge251bWJlcn0gZmFsbGJhY2tfdmFsdWVcclxuXHRcdCAqIEByZXR1cm5zIHtudW1iZXJ9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBwYXJzZV9wZXJjZW50KHYsIGZhbGxiYWNrX3ZhbHVlKSB7XHJcblx0XHRcdGlmICggdiA9PSBudWxsICkge1xyXG5cdFx0XHRcdHJldHVybiBmYWxsYmFja192YWx1ZTtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBzID0gU3RyaW5nKCB2ICkudHJpbSgpO1xyXG5cdFx0XHRjb25zdCBuID0gcGFyc2VGbG9hdCggcy5yZXBsYWNlKCAvJS9nLCAnJyApICk7XHJcblx0XHRcdHJldHVybiBOdW1iZXIuaXNGaW5pdGUoIG4gKSA/IG4gOiBmYWxsYmFja192YWx1ZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIENsYW1wIGEgbnVtYmVyIHRvIHRoZSBbbWluLCBtYXhdIHJhbmdlLlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJ9IG5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyfSBtaW5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyfSBtYXhcclxuXHRcdCAqIEByZXR1cm5zIHtudW1iZXJ9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBjbGFtcChuLCBtaW4sIG1heCkge1xyXG5cdFx0XHRyZXR1cm4gTWF0aC5tYXgoIG1pbiwgTWF0aC5taW4oIG1heCwgbiApICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBFc2NhcGUgYSB2YWx1ZSBmb3IgaW5jbHVzaW9uIGluc2lkZSBhIHF1b3RlZCBIVE1MIGF0dHJpYnV0ZSAoZG91YmxlIHF1b3RlcykuXHJcblx0XHQgKiBSZXBsYWNlcyBuZXdsaW5lcyB3aXRoIHNwYWNlcyBhbmQgZG91YmxlIHF1b3RlcyB3aXRoIHNpbmdsZSBxdW90ZXMuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIGVzY2FwZV9mb3JfYXR0cl9xdW90ZWQodikge1xyXG5cdFx0XHRpZiAoIHYgPT0gbnVsbCApIHJldHVybiAnJztcclxuXHRcdFx0cmV0dXJuIFN0cmluZyggdiApLnJlcGxhY2UoIC9cXHI/XFxuL2csICcgJyApLnJlcGxhY2UoIC9cIi9nLCAnXFwnJyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogRXNjYXBlIGZvciBzaG9ydGNvZGUtbGlrZSB0b2tlbnMgd2hlcmUgZG91YmxlIHF1b3RlcyBhbmQgbmV3bGluZXMgc2hvdWxkIGJlIG5ldXRyYWxpemVkLlxyXG5cdFx0ICogQHBhcmFtIHthbnl9IHZcclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBlc2NhcGVfZm9yX3Nob3J0Y29kZSh2KSB7XHJcblx0XHRcdHJldHVybiBTdHJpbmcoIHYgPz8gJycgKS5yZXBsYWNlKCAvXCIvZywgJ1xcXFxcIicgKS5yZXBsYWNlKCAvXFxyP1xcbi9nLCAnICcgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEpTT04ucGFyc2Ugd2l0aCBmYWxsYmFjayAobm8gdGhyb3cpLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHNcclxuXHRcdCAqIEBwYXJhbSB7YW55fSBbZmFsbGJhY2s9bnVsbF1cclxuXHRcdCAqIEByZXR1cm5zIHthbnl9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBzYWZlX2pzb25fcGFyc2UocywgZmFsbGJhY2sgPSBudWxsKSB7XHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoIHMgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0cmV0dXJuIGZhbGxiYWNrO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBTdHJpbmdpZnkgZGF0YS0qIGF0dHJpYnV0ZSB2YWx1ZSBzYWZlbHkgKG9iamVjdHMgLT4gSlNPTiwgb3RoZXJzIC0+IFN0cmluZykuXHJcblx0XHQgKiBAcGFyYW0ge2FueX0gdlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHN0cmluZ2lmeV9kYXRhX3ZhbHVlKHYpIHtcclxuXHRcdFx0aWYgKCB0eXBlb2YgdiA9PT0gJ29iamVjdCcgJiYgdiAhPT0gbnVsbCApIHtcclxuXHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0cmV0dXJuIEpTT04uc3RyaW5naWZ5KCB2ICk7XHJcblx0XHRcdFx0fSBjYXRjaCB7XHJcblx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnV1BCQzogc3RyaW5naWZ5X2RhdGFfdmFsdWUnICk7XHJcblx0XHRcdFx0XHRyZXR1cm4gJyc7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBTdHJpbmcoIHYgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHQvLyBTdHJpY3QgdmFsdWUgZ3VhcmRzIGZvciBDU1MgbGVuZ3RocyBhbmQgaGV4IGNvbG9ycyAoZGVmZW5zZS1pbi1kZXB0aCkuXHJcblx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHQvKipcclxuXHRcdCAqIFNhbml0aXplIGEgQ1NTIGxlbmd0aC4gQWxsb3dzOiBweCwgJSwgcmVtLCBlbSAobG93ZXIvdXBwZXIpLlxyXG5cdFx0ICogUmV0dXJucyBmYWxsYmFjayBpZiBpbnZhbGlkLlxyXG5cdFx0ICogQHBhcmFtIHthbnl9IHZcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBbZmFsbGJhY2s9JzEwMCUnXVxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHNhbml0aXplX2Nzc19sZW4odiwgZmFsbGJhY2sgPSAnMTAwJScpIHtcclxuXHRcdFx0Y29uc3QgcyA9IFN0cmluZyggdiA/PyAnJyApLnRyaW0oKTtcclxuXHRcdFx0Y29uc3QgbSA9IHMubWF0Y2goIC9eKC0/XFxkKyg/OlxcLlxcZCspPykocHh8JXxyZW18ZW0pJC9pICk7XHJcblx0XHRcdHJldHVybiBtID8gbVswXSA6IFN0cmluZyggZmFsbGJhY2sgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNhbml0aXplIGEgaGV4IGNvbG9yLiBBbGxvd3MgI3JnYiBvciAjcnJnZ2JiIChjYXNlLWluc2Vuc2l0aXZlKS5cclxuXHRcdCAqIFJldHVybnMgZmFsbGJhY2sgaWYgaW52YWxpZC5cclxuXHRcdCAqIEBwYXJhbSB7YW55fSB2XHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gW2ZhbGxiYWNrPScjZTBlMGUwJ11cclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBzYW5pdGl6ZV9oZXhfY29sb3IodiwgZmFsbGJhY2sgPSAnI2UwZTBlMCcpIHtcclxuXHRcdFx0Y29uc3QgcyA9IFN0cmluZyggdiA/PyAnJyApLnRyaW0oKTtcclxuXHRcdFx0cmV0dXJuIC9eIyg/OlswLTlhLWZdezN9fFswLTlhLWZdezZ9KSQvaS50ZXN0KCBzICkgPyBzIDogU3RyaW5nKCBmYWxsYmFjayApO1xyXG5cdFx0fVxyXG5cclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIFdQQkMgSUQgLyBOYW1lIHNlcnZpY2UuIEdlbmVyYXRlcywgc2FuaXRpemVzLCBhbmQgZW5zdXJlcyB1bmlxdWVuZXNzIGZvciBmaWVsZCBpZHMvbmFtZXMvaHRtbF9pZHMgd2l0aGluIHRoZVxyXG5cdCAqIGNhbnZhcy5cclxuXHQgKi9cclxuXHRDb3JlLldQQkNfQkZCX0lkU2VydmljZSA9IGNsYXNzICB7XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDb25zdHJ1Y3Rvci4gU2V0IHJvb3QgY29udGFpbmVyIG9mIHRoZSBmb3JtIHBhZ2VzLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHBhZ2VzX2NvbnRhaW5lciAtIFJvb3QgY29udGFpbmVyIG9mIHRoZSBmb3JtIHBhZ2VzLlxyXG5cdFx0ICovXHJcblx0XHRjb25zdHJ1Y3RvciggcGFnZXNfY29udGFpbmVyICkge1xyXG5cdFx0XHR0aGlzLnBhZ2VzX2NvbnRhaW5lciA9IHBhZ2VzX2NvbnRhaW5lcjtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVuc3VyZSBhIHVuaXF1ZSAqKmludGVybmFsKiogZmllbGQgaWQgKHN0b3JlZCBpbiBkYXRhLWlkKSB3aXRoaW4gdGhlIGNhbnZhcy5cclxuXHRcdCAqIFN0YXJ0cyBmcm9tIGEgZGVzaXJlZCBpZCAoYWxyZWFkeSBzYW5pdGl6ZWQgb3Igbm90KSBhbmQgYXBwZW5kcyBzdWZmaXhlcyBpZiBuZWVkZWQuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGJhc2VJZCAtIERlc2lyZWQgaWQuXHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfSBVbmlxdWUgaWQuXHJcblx0XHQgKi9cclxuXHRcdGVuc3VyZV91bmlxdWVfZmllbGRfaWQoYmFzZUlkLCBjdXJyZW50RWwgPSBudWxsKSB7XHJcblx0XHRcdGNvbnN0IGJhc2UgICAgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfaWQoIGJhc2VJZCApO1xyXG5cdFx0XHRsZXQgaWQgICAgICAgID0gYmFzZSB8fCAnZmllbGQnO1xyXG5cdFx0XHRjb25zdCBlc2MgICAgID0gKHYpID0+IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuZXNjX2F0dHJfdmFsdWVfZm9yX3NlbGVjdG9yKCB2ICk7XHJcblx0XHRcdGNvbnN0IGVzY1VpZCAgPSAodikgPT4gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5lc2NfYXR0cl92YWx1ZV9mb3Jfc2VsZWN0b3IoIHYgKTtcclxuXHRcdFx0Y29uc3Qgbm90U2VsZiA9IGN1cnJlbnRFbD8uZGF0YXNldD8udWlkID8gYDpub3QoW2RhdGEtdWlkPVwiJHtlc2NVaWQoIGN1cnJlbnRFbC5kYXRhc2V0LnVpZCApfVwiXSlgIDogJyc7XHJcblx0XHRcdHdoaWxlICggdGhpcy5wYWdlc19jb250YWluZXI/LnF1ZXJ5U2VsZWN0b3IoXHJcblx0XHRcdFx0YC53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgLndwYmNfYmZiX19maWVsZCR7bm90U2VsZn1bZGF0YS1pZD1cIiR7ZXNjKGlkKX1cIl0sIC53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgLndwYmNfYmZiX19zZWN0aW9uJHtub3RTZWxmfVtkYXRhLWlkPVwiJHtlc2MoaWQpfVwiXWBcclxuXHRcdFx0KSApIHtcclxuXHRcdFx0XHQvLyBFeGNsdWRlcyBzZWxmIGJ5IGRhdGEtdWlkIC5cclxuXHRcdFx0XHRjb25zdCBmb3VuZCA9IHRoaXMucGFnZXNfY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoIGAud3BiY19iZmJfX3BhbmVsLS1wcmV2aWV3IC53cGJjX2JmYl9fZmllbGRbZGF0YS1pZD1cIiR7ZXNjKCBpZCApfVwiXSwgLndwYmNfYmZiX19wYW5lbC0tcHJldmlldyAud3BiY19iZmJfX3NlY3Rpb25bZGF0YS1pZD1cIiR7ZXNjKCBpZCApfVwiXWAgKTtcclxuXHRcdFx0XHRpZiAoIGZvdW5kICYmIGN1cnJlbnRFbCAmJiBmb3VuZCA9PT0gY3VycmVudEVsICkge1xyXG5cdFx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGlkID0gYCR7YmFzZSB8fCAnZmllbGQnfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoIDM2ICkuc2xpY2UoIDIsIDUgKX1gO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBpZDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVuc3VyZSBhIHVuaXF1ZSBIVE1MIG5hbWUgYWNyb3NzIHRoZSBmb3JtLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBiYXNlIC0gRGVzaXJlZCBiYXNlIG5hbWUgKHVuL3Nhbml0aXplZCkuXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fG51bGx9IGN1cnJlbnRFbCAtIElmIHByb3ZpZGVkLCBpZ25vcmUgY29uZmxpY3RzIHdpdGggdGhpcyBlbGVtZW50LlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gVW5pcXVlIG5hbWUuXHJcblx0XHQgKi9cclxuXHRcdGVuc3VyZV91bmlxdWVfZmllbGRfbmFtZShiYXNlLCBjdXJyZW50RWwgPSBudWxsKSB7XHJcblx0XHRcdGxldCBuYW1lICAgICAgPSBiYXNlIHx8ICdmaWVsZCc7XHJcblx0XHRcdGNvbnN0IGVzYyAgICAgPSAodikgPT4gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5lc2NfYXR0cl92YWx1ZV9mb3Jfc2VsZWN0b3IoIHYgKTtcclxuXHRcdFx0Y29uc3QgZXNjVWlkICA9ICh2KSA9PiBDb3JlLldQQkNfQkZCX1Nhbml0aXplLmVzY19hdHRyX3ZhbHVlX2Zvcl9zZWxlY3RvciggdiApO1xyXG5cdFx0XHQvLyBFeGNsdWRlIHRoZSBjdXJyZW50IGZpZWxkICphbmQgYW55IERPTSBtaXJyb3JzIG9mIGl0KiAoc2FtZSBkYXRhLXVpZClcclxuXHRcdFx0Y29uc3QgdWlkICAgICA9IGN1cnJlbnRFbD8uZGF0YXNldD8udWlkO1xyXG5cdFx0XHRjb25zdCBub3RTZWxmID0gdWlkID8gYDpub3QoW2RhdGEtdWlkPVwiJHtlc2NVaWQoIHVpZCApfVwiXSlgIDogJyc7XHJcblx0XHRcdHdoaWxlICggdHJ1ZSApIHtcclxuXHRcdFx0XHRjb25zdCBzZWxlY3RvciA9IGAud3BiY19iZmJfX3BhbmVsLS1wcmV2aWV3IC53cGJjX2JmYl9fZmllbGQke25vdFNlbGZ9W2RhdGEtbmFtZT1cIiR7ZXNjKCBuYW1lICl9XCJdYDtcclxuXHRcdFx0XHRjb25zdCBjbGFzaGVzICA9IHRoaXMucGFnZXNfY29udGFpbmVyPy5xdWVyeVNlbGVjdG9yQWxsKCBzZWxlY3RvciApIHx8IFtdO1xyXG5cdFx0XHRcdGlmICggY2xhc2hlcy5sZW5ndGggPT09IDAgKSBicmVhazsgICAgICAgICAgIC8vIG5vYm9keSBlbHNlIHVzZXMgdGhpcyBuYW1lXHJcblx0XHRcdFx0Y29uc3QgbSA9IG5hbWUubWF0Y2goIC8tKFxcZCspJC8gKTtcclxuXHRcdFx0XHRuYW1lICAgID0gbSA/IG5hbWUucmVwbGFjZSggLy1cXGQrJC8sICctJyArIChOdW1iZXIoIG1bMV0gKSArIDEpICkgOiBgJHtiYXNlfS0yYDtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gbmFtZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNldCBmaWVsZCdzIElOVEVSTkFMIGlkIChkYXRhLWlkKSBvbiBhbiBlbGVtZW50LiBFbnN1cmVzIHVuaXF1ZW5lc3MgYW5kIG9wdGlvbmFsbHkgYXNrcyBjYWxsZXIgdG8gcmVmcmVzaFxyXG5cdFx0ICogcHJldmlldy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBmaWVsZF9lbCAtIEZpZWxkIGVsZW1lbnQgaW4gdGhlIGNhbnZhcy5cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBuZXdJZFJhdyAtIERlc2lyZWQgaWQgKHVuL3Nhbml0aXplZCkuXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IFtyZW5kZXJQcmV2aWV3PWZhbHNlXSAtIENhbGxlciBjYW4gZGVjaWRlIHRvIHJlLXJlbmRlciBwcmV2aWV3LlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gQXBwbGllZCB1bmlxdWUgaWQuXHJcblx0XHQgKi9cclxuXHRcdHNldF9maWVsZF9pZCggZmllbGRfZWwsIG5ld0lkUmF3LCByZW5kZXJQcmV2aWV3ID0gZmFsc2UgKSB7XHJcblx0XHRcdGNvbnN0IGRlc2lyZWQgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfaWQoIG5ld0lkUmF3ICk7XHJcblx0XHRcdGNvbnN0IHVuaXF1ZSAgPSB0aGlzLmVuc3VyZV91bmlxdWVfZmllbGRfaWQoIGRlc2lyZWQsIGZpZWxkX2VsICk7XHJcblx0XHRcdGZpZWxkX2VsLnNldEF0dHJpYnV0ZSggJ2RhdGEtaWQnLCB1bmlxdWUgKTtcclxuXHRcdFx0aWYgKCByZW5kZXJQcmV2aWV3ICkge1xyXG5cdFx0XHRcdC8vIENhbGxlciBkZWNpZGVzIGlmIC8gd2hlbiB0byByZW5kZXIuXHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIHVuaXF1ZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNldCBmaWVsZCdzIFJFUVVJUkVEIEhUTUwgbmFtZSAoZGF0YS1uYW1lKS4gRW5zdXJlcyBzYW5pdGl6ZWQgKyB1bmlxdWUgcGVyIGZvcm0uXHJcblx0XHQgKiBGYWxscyBiYWNrIHRvIHNhbml0aXplZCBpbnRlcm5hbCBpZCBpZiB1c2VyIHByb3ZpZGVzIGVtcHR5IHZhbHVlLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGZpZWxkX2VsIC0gRmllbGQgZWxlbWVudCBpbiB0aGUgY2FudmFzLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IG5ld05hbWVSYXcgLSBEZXNpcmVkIG5hbWUgKHVuL3Nhbml0aXplZCkuXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IFtyZW5kZXJQcmV2aWV3PWZhbHNlXSAtIENhbGxlciBjYW4gZGVjaWRlIHRvIHJlLXJlbmRlciBwcmV2aWV3LlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gQXBwbGllZCB1bmlxdWUgbmFtZS5cclxuXHRcdCAqL1xyXG5cdFx0c2V0X2ZpZWxkX25hbWUoIGZpZWxkX2VsLCBuZXdOYW1lUmF3LCByZW5kZXJQcmV2aWV3ID0gZmFsc2UgKSB7XHJcblx0XHRcdGNvbnN0IHJhdyAgPSAobmV3TmFtZVJhdyA9PSBudWxsID8gJycgOiBTdHJpbmcoIG5ld05hbWVSYXcgKSkudHJpbSgpO1xyXG5cdFx0XHRjb25zdCBiYXNlID0gcmF3XHJcblx0XHRcdFx0PyBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfbmFtZSggcmF3IClcclxuXHRcdFx0XHQ6IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuc2FuaXRpemVfaHRtbF9uYW1lKCBmaWVsZF9lbC5nZXRBdHRyaWJ1dGUoICdkYXRhLWlkJyApIHx8ICdmaWVsZCcgKTtcclxuXHJcblx0XHRcdGNvbnN0IHVuaXF1ZSA9IHRoaXMuZW5zdXJlX3VuaXF1ZV9maWVsZF9uYW1lKCBiYXNlLCBmaWVsZF9lbCApO1xyXG5cdFx0XHRmaWVsZF9lbC5zZXRBdHRyaWJ1dGUoICdkYXRhLW5hbWUnLCB1bmlxdWUgKTtcclxuXHRcdFx0aWYgKCByZW5kZXJQcmV2aWV3ICkge1xyXG5cdFx0XHRcdC8vIENhbGxlciBkZWNpZGVzIGlmIC8gd2hlbiB0byByZW5kZXIuXHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIHVuaXF1ZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNldCBmaWVsZCdzIE9QVElPTkFMIHB1YmxpYyBIVE1MIGlkIChkYXRhLWh0bWxfaWQpLiBFbXB0eSB2YWx1ZSByZW1vdmVzIHRoZSBhdHRyaWJ1dGUuXHJcblx0XHQgKiBFbnN1cmVzIHNhbml0aXphdGlvbiArIHVuaXF1ZW5lc3MgYW1vbmcgb3RoZXIgZGVjbGFyZWQgSFRNTCBpZHMuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZmllbGRfZWwgLSBGaWVsZCBlbGVtZW50IGluIHRoZSBjYW52YXMuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gbmV3SHRtbElkUmF3IC0gRGVzaXJlZCBodG1sX2lkIChvcHRpb25hbCkuXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IFtyZW5kZXJQcmV2aWV3PWZhbHNlXSAtIENhbGxlciBjYW4gZGVjaWRlIHRvIHJlLXJlbmRlciBwcmV2aWV3LlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gVGhlIGFwcGxpZWQgaHRtbF9pZCBvciBlbXB0eSBzdHJpbmcgaWYgcmVtb3ZlZC5cclxuXHRcdCAqL1xyXG5cdFx0c2V0X2ZpZWxkX2h0bWxfaWQoIGZpZWxkX2VsLCBuZXdIdG1sSWRSYXcsIHJlbmRlclByZXZpZXcgPSBmYWxzZSApIHtcclxuXHRcdFx0Y29uc3QgcmF3ID0gKG5ld0h0bWxJZFJhdyA9PSBudWxsID8gJycgOiBTdHJpbmcoIG5ld0h0bWxJZFJhdyApKS50cmltKCk7XHJcblxyXG5cdFx0XHRpZiAoIHJhdyA9PT0gJycgKSB7XHJcblx0XHRcdFx0ZmllbGRfZWwucmVtb3ZlQXR0cmlidXRlKCAnZGF0YS1odG1sX2lkJyApO1xyXG5cdFx0XHRcdGlmICggcmVuZGVyUHJldmlldyApIHtcclxuXHRcdFx0XHRcdC8vIENhbGxlciBkZWNpZGVzIGlmIC8gd2hlbiB0byByZW5kZXIuXHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHJldHVybiAnJztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3QgZGVzaXJlZCA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuc2FuaXRpemVfaHRtbF9pZCggcmF3ICk7XHJcblx0XHRcdGxldCBodG1sSWQgICAgPSBkZXNpcmVkO1xyXG5cdFx0XHRjb25zdCBlc2MgICAgID0gKHYpID0+IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuZXNjX2F0dHJfdmFsdWVfZm9yX3NlbGVjdG9yKCB2ICk7XHJcblx0XHRcdGNvbnN0IGVzY1VpZCAgPSAodikgPT4gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5lc2NfYXR0cl92YWx1ZV9mb3Jfc2VsZWN0b3IoIHYgKTtcclxuXHJcblx0XHRcdHdoaWxlICggdHJ1ZSApIHtcclxuXHJcblx0XHRcdFx0Y29uc3QgdWlkICAgICA9IGZpZWxkX2VsPy5kYXRhc2V0Py51aWQ7XHJcblx0XHRcdFx0Y29uc3Qgbm90U2VsZiA9IHVpZCA/IGA6bm90KFtkYXRhLXVpZD1cIiR7ZXNjVWlkKCB1aWQgKX1cIl0pYCA6ICcnO1xyXG5cclxuXHRcdFx0XHRjb25zdCBjbGFzaEluQ2FudmFzID0gdGhpcy5wYWdlc19jb250YWluZXI/LnF1ZXJ5U2VsZWN0b3IoXHJcblx0XHRcdFx0XHRgLndwYmNfYmZiX19wYW5lbC0tcHJldmlldyAud3BiY19iZmJfX2ZpZWxkJHtub3RTZWxmfVtkYXRhLWh0bWxfaWQ9XCIke2VzYyggaHRtbElkICl9XCJdLGAgK1xyXG5cdFx0XHRcdFx0YC53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgLndwYmNfYmZiX19zZWN0aW9uJHtub3RTZWxmfVtkYXRhLWh0bWxfaWQ9XCIke2VzYyggaHRtbElkICl9XCJdYFxyXG5cdFx0XHRcdCk7XHJcblx0XHRcdFx0Y29uc3QgZG9tQ2xhc2ggPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggaHRtbElkICk7XHJcblxyXG5cdFx0XHRcdC8vIEFsbG93IHdoZW4gdGhlIG9ubHkgXCJjbGFzaFwiIGlzIGluc2lkZSB0aGlzIHNhbWUgZmllbGQgKGUuZy4sIHRoZSBpbnB1dCB5b3UganVzdCByZW5kZXJlZClcclxuXHRcdFx0XHRjb25zdCBkb21DbGFzaElzU2VsZiA9IGRvbUNsYXNoID09PSBmaWVsZF9lbCB8fCAoZG9tQ2xhc2ggJiYgZmllbGRfZWwuY29udGFpbnMoIGRvbUNsYXNoICkpO1xyXG5cclxuXHRcdFx0XHRpZiAoICFjbGFzaEluQ2FudmFzICYmICghZG9tQ2xhc2ggfHwgZG9tQ2xhc2hJc1NlbGYpICkge1xyXG5cdFx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRjb25zdCBtID0gaHRtbElkLm1hdGNoKCAvLShcXGQrKSQvICk7XHJcblx0XHRcdFx0aHRtbElkICA9IG0gPyBodG1sSWQucmVwbGFjZSggLy1cXGQrJC8sICctJyArIChOdW1iZXIoIG1bMV0gKSArIDEpICkgOiBgJHtkZXNpcmVkfS0yYDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0ZmllbGRfZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1odG1sX2lkJywgaHRtbElkICk7XHJcblx0XHRcdGlmICggcmVuZGVyUHJldmlldyApIHtcclxuXHRcdFx0XHQvLyBDYWxsZXIgZGVjaWRlcyBpZiAvIHdoZW4gdG8gcmVuZGVyLlxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBodG1sSWQ7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogV1BCQyBMYXlvdXQgc2VydmljZS4gRW5jYXBzdWxhdGVzIGNvbHVtbiB3aWR0aCBtYXRoIHdpdGggZ2FwIGhhbmRsaW5nLCBwcmVzZXRzLCBhbmQgdXRpbGl0aWVzLlxyXG5cdCAqL1xyXG5cdENvcmUuV1BCQ19CRkJfTGF5b3V0U2VydmljZSA9IGNsYXNzICB7XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDb25zdHJ1Y3Rvci4gU2V0IG9wdGlvbnMgd2l0aCBnYXAgYmV0d2VlbiBjb2x1bW5zICglKS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3sgY29sX2dhcF9wZXJjZW50PzogbnVtYmVyIH19IFtvcHRzXSAtIE9wdGlvbnMgd2l0aCBnYXAgYmV0d2VlbiBjb2x1bW5zICglKS5cclxuXHRcdCAqL1xyXG5cdFx0Y29uc3RydWN0b3IoIG9wdHMgPSB7fSApIHtcclxuXHRcdFx0dGhpcy5jb2xfZ2FwX3BlcmNlbnQgPSBOdW1iZXIuaXNGaW5pdGUoICtvcHRzLmNvbF9nYXBfcGVyY2VudCApID8gK29wdHMuY29sX2dhcF9wZXJjZW50IDogMztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIENvbXB1dGUgbm9ybWFsaXplZCBmbGV4LWJhc2lzIHZhbHVlcyBmb3IgYSByb3csIHJlc3BlY3RpbmcgY29sdW1uIGdhcHMuXHJcblx0XHQgKiBSZXR1cm5zIGJhc2VzIHRoYXQgc3VtIHRvIGF2YWlsYWJsZSA9IDEwMCAtIChuLTEpKmdhcC5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSByb3dfZWwgLSBSb3cgZWxlbWVudCBjb250YWluaW5nIC53cGJjX2JmYl9fY29sdW1uIGNoaWxkcmVuLlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJ9IFtnYXBfcGVyY2VudD10aGlzLmNvbF9nYXBfcGVyY2VudF0gLSBHYXAgcGVyY2VudCBiZXR3ZWVuIGNvbHVtbnMuXHJcblx0XHQgKiBAcmV0dXJucyB7e2F2YWlsYWJsZTpudW1iZXIsYmFzZXM6bnVtYmVyW119fSBBdmFpbGFibGUgc3BhY2UgYW5kIGJhc2lzIHZhbHVlcy5cclxuXHRcdCAqL1xyXG5cdFx0Y29tcHV0ZV9lZmZlY3RpdmVfYmFzZXNfZnJvbV9yb3coIHJvd19lbCwgZ2FwX3BlcmNlbnQgPSB0aGlzLmNvbF9nYXBfcGVyY2VudCApIHtcclxuXHRcdFx0Y29uc3QgY29scyA9IEFycmF5LmZyb20oIHJvd19lbD8ucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApIHx8IFtdICk7XHJcblx0XHRcdGNvbnN0IG4gICAgPSBjb2xzLmxlbmd0aCB8fCAxO1xyXG5cclxuXHRcdFx0Y29uc3QgcmF3ID0gY29scy5tYXAoICggY29sICkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHcgPSBjb2wuc3R5bGUuZmxleEJhc2lzIHx8ICcnO1xyXG5cdFx0XHRcdGNvbnN0IHAgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnBhcnNlX3BlcmNlbnQoIHcsIE5hTiApO1xyXG5cdFx0XHRcdHJldHVybiBOdW1iZXIuaXNGaW5pdGUoIHAgKSA/IHAgOiAoMTAwIC8gbik7XHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdGNvbnN0IHN1bV9yYXcgICAgPSByYXcucmVkdWNlKCAoIGEsIGIgKSA9PiBhICsgYiwgMCApIHx8IDEwMDtcclxuXHRcdFx0Y29uc3QgZ3AgICAgICAgICA9IE51bWJlci5pc0Zpbml0ZSggK2dhcF9wZXJjZW50ICkgPyArZ2FwX3BlcmNlbnQgOiAzO1xyXG5cdFx0XHRjb25zdCB0b3RhbF9nYXBzID0gTWF0aC5tYXgoIDAsIG4gLSAxICkgKiBncDtcclxuXHRcdFx0Y29uc3QgYXZhaWxhYmxlICA9IE1hdGgubWF4KCAwLCAxMDAgLSB0b3RhbF9nYXBzICk7XHJcblx0XHRcdGNvbnN0IHNjYWxlICAgICAgPSBhdmFpbGFibGUgLyBzdW1fcmF3O1xyXG5cclxuXHRcdFx0cmV0dXJuIHtcclxuXHRcdFx0XHRhdmFpbGFibGUsXHJcblx0XHRcdFx0YmFzZXM6IHJhdy5tYXAoICggcCApID0+IE1hdGgubWF4KCAwLCBwICogc2NhbGUgKSApXHJcblx0XHRcdH07XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBBcHBseSBjb21wdXRlZCBiYXNlcyB0byB0aGUgcm93J3MgY29sdW1ucyAoc2V0cyBmbGV4LWJhc2lzICUpLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvd19lbCAtIFJvdyBlbGVtZW50LlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJbXX0gYmFzZXMgLSBBcnJheSBvZiBiYXNpcyB2YWx1ZXMgKHBlcmNlbnQgb2YgZnVsbCAxMDApLlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdGFwcGx5X2Jhc2VzX3RvX3Jvdyggcm93X2VsLCBiYXNlcyApIHtcclxuXHRcdFx0Y29uc3QgY29scyA9IEFycmF5LmZyb20oIHJvd19lbD8ucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApIHx8IFtdICk7XHJcblx0XHRcdGNvbHMuZm9yRWFjaCggKCBjb2wsIGkgKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcCAgICAgICAgICAgICA9IGJhc2VzW2ldID8/IDA7XHJcblx0XHRcdFx0Y29sLnN0eWxlLmZsZXhCYXNpcyA9IGAke3B9JWA7XHJcblx0XHRcdH0gKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIERpc3RyaWJ1dGUgY29sdW1ucyBldmVubHksIHJlc3BlY3RpbmcgZ2FwLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvd19lbCAtIFJvdyBlbGVtZW50LlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJ9IFtnYXBfcGVyY2VudD10aGlzLmNvbF9nYXBfcGVyY2VudF0gLSBHYXAgcGVyY2VudC5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRzZXRfZXF1YWxfYmFzZXMoIHJvd19lbCwgZ2FwX3BlcmNlbnQgPSB0aGlzLmNvbF9nYXBfcGVyY2VudCApIHtcclxuXHRcdFx0Y29uc3QgY29scyAgICAgICA9IEFycmF5LmZyb20oIHJvd19lbD8ucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApIHx8IFtdICk7XHJcblx0XHRcdGNvbnN0IG4gICAgICAgICAgPSBjb2xzLmxlbmd0aCB8fCAxO1xyXG5cdFx0XHRjb25zdCBncCAgICAgICAgID0gTnVtYmVyLmlzRmluaXRlKCArZ2FwX3BlcmNlbnQgKSA/ICtnYXBfcGVyY2VudCA6IDM7XHJcblx0XHRcdGNvbnN0IHRvdGFsX2dhcHMgPSBNYXRoLm1heCggMCwgbiAtIDEgKSAqIGdwO1xyXG5cdFx0XHRjb25zdCBhdmFpbGFibGUgID0gTWF0aC5tYXgoIDAsIDEwMCAtIHRvdGFsX2dhcHMgKTtcclxuXHRcdFx0Y29uc3QgZWFjaCAgICAgICA9IGF2YWlsYWJsZSAvIG47XHJcblx0XHRcdHRoaXMuYXBwbHlfYmFzZXNfdG9fcm93KCByb3dfZWwsIEFycmF5KCBuICkuZmlsbCggZWFjaCApICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBBcHBseSBhIHByZXNldCBvZiByZWxhdGl2ZSB3ZWlnaHRzIHRvIGEgcm93L3NlY3Rpb24uXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gc2VjdGlvbk9yUm93IC0gLndwYmNfYmZiX19zZWN0aW9uIG9yIGl0cyBjaGlsZCAud3BiY19iZmJfX3Jvdy5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyW119IHdlaWdodHMgLSBSZWxhdGl2ZSB3ZWlnaHRzIChlLmcuLCBbMSwzLDFdKS5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyfSBbZ2FwX3BlcmNlbnQ9dGhpcy5jb2xfZ2FwX3BlcmNlbnRdIC0gR2FwIHBlcmNlbnQuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0YXBwbHlfbGF5b3V0X3ByZXNldCggc2VjdGlvbk9yUm93LCB3ZWlnaHRzLCBnYXBfcGVyY2VudCA9IHRoaXMuY29sX2dhcF9wZXJjZW50ICkge1xyXG5cdFx0XHRjb25zdCByb3cgPSBzZWN0aW9uT3JSb3c/LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fcm93JyApXHJcblx0XHRcdFx0PyBzZWN0aW9uT3JSb3dcclxuXHRcdFx0XHQ6IHNlY3Rpb25PclJvdz8ucXVlcnlTZWxlY3RvciggJzpzY29wZSA+IC53cGJjX2JmYl9fcm93JyApO1xyXG5cclxuXHRcdFx0aWYgKCAhIHJvdyApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IGNvbHMgPSBBcnJheS5mcm9tKCByb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApIHx8IFtdICk7XHJcblx0XHRcdGNvbnN0IG4gICAgPSBjb2xzLmxlbmd0aCB8fCAxO1xyXG5cclxuXHRcdFx0aWYgKCAhIEFycmF5LmlzQXJyYXkoIHdlaWdodHMgKSB8fCB3ZWlnaHRzLmxlbmd0aCAhPT0gbiApIHtcclxuXHRcdFx0XHR0aGlzLnNldF9lcXVhbF9iYXNlcyggcm93LCBnYXBfcGVyY2VudCApO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3Qgc3VtICAgICAgID0gd2VpZ2h0cy5yZWR1Y2UoICggYSwgYiApID0+IGEgKyBNYXRoLm1heCggMCwgTnVtYmVyKCBiICkgfHwgMCApLCAwICkgfHwgMTtcclxuXHRcdFx0Y29uc3QgZ3AgICAgICAgID0gTnVtYmVyLmlzRmluaXRlKCArZ2FwX3BlcmNlbnQgKSA/ICtnYXBfcGVyY2VudCA6IDM7XHJcblx0XHRcdGNvbnN0IGF2YWlsYWJsZSA9IE1hdGgubWF4KCAwLCAxMDAgLSBNYXRoLm1heCggMCwgbiAtIDEgKSAqIGdwICk7XHJcblx0XHRcdGNvbnN0IGJhc2VzICAgICA9IHdlaWdodHMubWFwKCAoIHcgKSA9PiBNYXRoLm1heCggMCwgKE51bWJlciggdyApIHx8IDApIC8gc3VtICogYXZhaWxhYmxlICkgKTtcclxuXHJcblx0XHRcdHRoaXMuYXBwbHlfYmFzZXNfdG9fcm93KCByb3csIGJhc2VzICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBCdWlsZCBwcmVzZXQgd2VpZ2h0IGxpc3RzIGZvciBhIGdpdmVuIGNvbHVtbiBjb3VudC5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge251bWJlcn0gbiAtIENvbHVtbiBjb3VudC5cclxuXHRcdCAqIEByZXR1cm5zIHtudW1iZXJbXVtdfSBMaXN0IG9mIHdlaWdodCBhcnJheXMuXHJcblx0XHQgKi9cclxuXHRcdGJ1aWxkX3ByZXNldHNfZm9yX2NvbHVtbnMoIG4gKSB7XHJcblx0XHRcdHN3aXRjaCAoIG4gKSB7XHJcblx0XHRcdFx0Y2FzZSAxOlxyXG5cdFx0XHRcdFx0cmV0dXJuIFsgWyAxIF0gXTtcclxuXHRcdFx0XHRjYXNlIDI6XHJcblx0XHRcdFx0XHRyZXR1cm4gWyBbIDEsIDIgXSwgWyAyLCAxIF0sIFsgMSwgMyBdLCBbIDMsIDEgXSBdO1xyXG5cdFx0XHRcdGNhc2UgMzpcclxuXHRcdFx0XHRcdHJldHVybiBbIFsgMSwgMywgMSBdLCBbIDEsIDIsIDEgXSwgWyAyLCAxLCAxIF0sIFsgMSwgMSwgMiBdIF07XHJcblx0XHRcdFx0Y2FzZSA0OlxyXG5cdFx0XHRcdFx0cmV0dXJuIFsgWyAxLCAyLCAyLCAxIF0sIFsgMiwgMSwgMSwgMSBdLCBbIDEsIDEsIDEsIDIgXSBdO1xyXG5cdFx0XHRcdGRlZmF1bHQ6XHJcblx0XHRcdFx0XHRyZXR1cm4gWyBBcnJheSggbiApLmZpbGwoIDEgKSBdO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBGb3JtYXQgYSBodW1hbi1yZWFkYWJsZSBsYWJlbCBsaWtlIFwiNTAlLzI1JS8yNSVcIiBmcm9tIHdlaWdodHMuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJbXX0gd2VpZ2h0cyAtIFdlaWdodCBsaXN0LlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gTGFiZWwgc3RyaW5nLlxyXG5cdFx0ICovXHJcblx0XHRmb3JtYXRfcHJlc2V0X2xhYmVsKCB3ZWlnaHRzICkge1xyXG5cdFx0XHRjb25zdCBzdW0gPSB3ZWlnaHRzLnJlZHVjZSggKCBhLCBiICkgPT4gYSArIChOdW1iZXIoIGIgKSB8fCAwKSwgMCApIHx8IDE7XHJcblx0XHRcdHJldHVybiB3ZWlnaHRzLm1hcCggKCB3ICkgPT4gTWF0aC5yb3VuZCggKChOdW1iZXIoIHcgKSB8fCAwKSAvIHN1bSkgKiAxMDAgKSApLmpvaW4oICclLycgKSArICclJztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFBhcnNlIGNvbW1hL3NwYWNlIHNlcGFyYXRlZCB3ZWlnaHRzIGludG8gbnVtYmVycy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBVc2VyIGlucHV0IGxpa2UgXCIyMCw2MCwyMFwiLlxyXG5cdFx0ICogQHJldHVybnMge251bWJlcltdfSBQYXJzZWQgd2VpZ2h0cy5cclxuXHRcdCAqL1xyXG5cdFx0cGFyc2Vfd2VpZ2h0cyggaW5wdXQgKSB7XHJcblx0XHRcdGlmICggISBpbnB1dCApIHtcclxuXHRcdFx0XHRyZXR1cm4gW107XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIFN0cmluZyggaW5wdXQgKVxyXG5cdFx0XHRcdC5yZXBsYWNlKCAvW15cXGQsLlxcc10vZywgJycgKVxyXG5cdFx0XHRcdC5zcGxpdCggL1tcXHMsXSsvIClcclxuXHRcdFx0XHQubWFwKCAoIHMgKSA9PiBwYXJzZUZsb2F0KCBzICkgKVxyXG5cdFx0XHRcdC5maWx0ZXIoICggbiApID0+IE51bWJlci5pc0Zpbml0ZSggbiApICYmIG4gPj0gMCApO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdCAqIFdQQkMgVXNhZ2UgTGltaXQgc2VydmljZS5cclxuXHQgKiBDb3VudHMgZmllbGQgdXNhZ2UgYnkga2V5LCBjb21wYXJlcyB0byBwYWxldHRlIGxpbWl0cywgYW5kIHVwZGF0ZXMgcGFsZXR0ZSBVSS5cclxuXHQgKi9cclxuXHRDb3JlLldQQkNfQkZCX1VzYWdlTGltaXRTZXJ2aWNlID0gY2xhc3MgIHtcclxuXHJcblx0XHQvKipcclxuXHRcdCAqIENvbnN0cnVjdG9yLiBTZXQgcGFnZXNfY29udGFpbmVyIGFuZCBwYWxldHRlX3VsLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHBhZ2VzX2NvbnRhaW5lciAtIENhbnZhcyByb290IHRoYXQgaG9sZHMgcGxhY2VkIGZpZWxkcy5cclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnRbXXxudWxsfSBwYWxldHRlX3Vscz86ICAgUGFsZXR0ZXMgVUwgd2l0aCAud3BiY19iZmJfX2ZpZWxkIGl0ZW1zIChtYXkgYmUgbnVsbCkuXHJcblx0XHQgKi9cclxuXHRcdGNvbnN0cnVjdG9yKHBhZ2VzX2NvbnRhaW5lciwgcGFsZXR0ZV91bHMpIHtcclxuXHRcdFx0dGhpcy5wYWdlc19jb250YWluZXIgPSBwYWdlc19jb250YWluZXI7XHJcblx0XHRcdC8vIE5vcm1hbGl6ZSB0byBhbiBhcnJheTsgd2XigJlsbCBzdGlsbCBiZSByb2J1c3QgaWYgbm9uZSBwcm92aWRlZC5cclxuXHRcdFx0dGhpcy5wYWxldHRlX3VscyAgICAgPSBBcnJheS5pc0FycmF5KCBwYWxldHRlX3VscyApID8gcGFsZXR0ZV91bHMgOiAocGFsZXR0ZV91bHMgPyBbIHBhbGV0dGVfdWxzIF0gOiBbXSk7XHJcblx0XHR9XHJcblxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogUGFyc2UgdXNhZ2UgbGltaXQgZnJvbSByYXcgZGF0YXNldCB2YWx1ZS4gTWlzc2luZy9pbnZhbGlkIC0+IEluZmluaXR5LlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfG51bWJlcnxudWxsfHVuZGVmaW5lZH0gcmF3IC0gUmF3IGF0dHJpYnV0ZSB2YWx1ZS5cclxuXHRcdCAqIEByZXR1cm5zIHtudW1iZXJ9IExpbWl0IG51bWJlciBvciBJbmZpbml0eS5cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHBhcnNlX3VzYWdlX2xpbWl0KCByYXcgKSB7XHJcblx0XHRcdGlmICggcmF3ID09IG51bGwgKSB7XHJcblx0XHRcdFx0cmV0dXJuIEluZmluaXR5O1xyXG5cdFx0XHR9XHJcblx0XHRcdGNvbnN0IG4gPSBwYXJzZUludCggcmF3LCAxMCApO1xyXG5cdFx0XHRyZXR1cm4gTnVtYmVyLmlzRmluaXRlKCBuICkgPyBuIDogSW5maW5pdHk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDb3VudCBob3cgbWFueSBpbnN0YW5jZXMgZXhpc3QgcGVyIHVzYWdlX2tleSBpbiB0aGUgY2FudmFzLlxyXG5cdFx0ICpcclxuXHRcdCAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCBudW1iZXI+fSBNYXAgb2YgdXNhZ2Vfa2V5IC0+IGNvdW50LlxyXG5cdFx0ICovXHJcblx0XHRjb3VudF91c2FnZV9ieV9rZXkoKSB7XHJcblx0XHRcdGNvbnN0IHVzZWQgPSB7fTtcclxuXHRcdFx0Y29uc3QgYWxsICA9IHRoaXMucGFnZXNfY29udGFpbmVyPy5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfYmZiX19wYW5lbC0tcHJldmlldyAud3BiY19iZmJfX2ZpZWxkOm5vdCguaXMtaW52YWxpZCknICkgfHwgW107XHJcblx0XHRcdGFsbC5mb3JFYWNoKCAoIGVsICkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGtleSA9IGVsLmRhdGFzZXQudXNhZ2Vfa2V5IHx8IGVsLmRhdGFzZXQudHlwZSB8fCBlbC5kYXRhc2V0LmlkO1xyXG5cdFx0XHRcdGlmICggISBrZXkgKSB7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHVzZWRba2V5XSA9ICh1c2VkW2tleV0gfHwgMCkgKyAxO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdHJldHVybiB1c2VkO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogUmV0dXJuIHBhbGV0dGUgbGltaXQgZm9yIGEgZ2l2ZW4gdXNhZ2Uga2V5IChpZCBvZiB0aGUgcGFsZXR0ZSBpdGVtKS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gVXNhZ2Uga2V5LlxyXG5cdFx0ICogQHJldHVybnMge251bWJlcn0gTGltaXQgdmFsdWUgb3IgSW5maW5pdHkuXHJcblx0XHQgKi9cclxuXHRcdGdldF9saW1pdF9mb3Jfa2V5KGtleSkge1xyXG5cdFx0XHRpZiAoICEga2V5ICkge1xyXG5cdFx0XHRcdHJldHVybiBJbmZpbml0eTtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBRdWVyeSBhY3Jvc3MgYWxsIHBhbGV0dGVzIHByZXNlbnQgbm93IChzdG9yZWQgKyBhbnkgbmV3bHkgYWRkZWQgaW4gRE9NKS5cclxuXHRcdFx0Y29uc3Qgcm9vdHMgICAgICAgICAgICA9IHRoaXMucGFsZXR0ZV91bHM/Lmxlbmd0aCA/IHRoaXMucGFsZXR0ZV91bHMgOiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfYmZiX19wYW5lbF9maWVsZF90eXBlc19fdWwnICk7XHJcblx0XHRcdGNvbnN0IGFsbFBhbGV0dGVGaWVsZHMgPSBBcnJheS5mcm9tKCByb290cyApLmZsYXRNYXAoIHIgPT4gQXJyYXkuZnJvbSggci5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfYmZiX19maWVsZCcgKSApICk7XHJcblx0XHRcdGxldCBsaW1pdCAgICAgICAgICAgICAgPSBJbmZpbml0eTtcclxuXHJcblx0XHRcdGFsbFBhbGV0dGVGaWVsZHMuZm9yRWFjaCggKGVsKSA9PiB7XHJcblx0XHRcdFx0aWYgKCBlbC5kYXRhc2V0LmlkID09PSBrZXkgKSB7XHJcblx0XHRcdFx0XHRjb25zdCBuID0gQ29yZS5XUEJDX0JGQl9Vc2FnZUxpbWl0U2VydmljZS5wYXJzZV91c2FnZV9saW1pdCggZWwuZGF0YXNldC51c2FnZW51bWJlciApO1xyXG5cdFx0XHRcdFx0Ly8gQ2hvb3NlIHRoZSBzbWFsbGVzdCBmaW5pdGUgbGltaXQgKHNhZmVzdCBpZiBwYWxldHRlcyBkaXNhZ3JlZSkuXHJcblx0XHRcdFx0XHRpZiAoIG4gPCBsaW1pdCApIHtcclxuXHRcdFx0XHRcdFx0bGltaXQgPSBuO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0cmV0dXJuIGxpbWl0O1xyXG5cdFx0fVxyXG5cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIERpc2FibGUvZW5hYmxlIHBhbGV0dGUgaXRlbXMgYmFzZWQgb24gY3VycmVudCB1c2FnZSBjb3VudHMgYW5kIGxpbWl0cy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0dXBkYXRlX3BhbGV0dGVfdWkoKSB7XHJcblx0XHRcdC8vIEFsd2F5cyBjb21wdXRlIHVzYWdlIGZyb20gdGhlIGNhbnZhczpcclxuXHRcdFx0Y29uc3QgdXNhZ2UgPSB0aGlzLmNvdW50X3VzYWdlX2J5X2tleSgpO1xyXG5cclxuXHRcdFx0Ly8gVXBkYXRlIGFsbCBwYWxldHRlcyBjdXJyZW50bHkgaW4gRE9NIChub3QganVzdCB0aGUgaW5pdGlhbGx5IGNhcHR1cmVkIG9uZXMpXHJcblx0XHRcdGNvbnN0IHBhbGV0dGVzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fcGFuZWxfZmllbGRfdHlwZXNfX3VsJyApO1xyXG5cclxuXHRcdFx0cGFsZXR0ZXMuZm9yRWFjaCggKHBhbCkgPT4ge1xyXG5cdFx0XHRcdHBhbC5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfYmZiX19maWVsZCcgKS5mb3JFYWNoKCAocGFuZWxfZmllbGQpID0+IHtcclxuXHRcdFx0XHRcdGNvbnN0IHBhbGV0dGVJZCAgID0gcGFuZWxfZmllbGQuZGF0YXNldC5pZDtcclxuXHRcdFx0XHRcdGNvbnN0IHJhd19saW1pdCAgID0gcGFuZWxfZmllbGQuZGF0YXNldC51c2FnZW51bWJlcjtcclxuXHRcdFx0XHRcdGNvbnN0IHBlckVsTGltaXQgID0gQ29yZS5XUEJDX0JGQl9Vc2FnZUxpbWl0U2VydmljZS5wYXJzZV91c2FnZV9saW1pdCggcmF3X2xpbWl0ICk7XHJcblx0XHRcdFx0XHQvLyBFZmZlY3RpdmUgbGltaXQgYWNyb3NzIGFsbCBwYWxldHRlcyBpcyB0aGUgZ2xvYmFsIGxpbWl0IGZvciB0aGlzIGtleS5cclxuXHRcdFx0XHRcdGNvbnN0IGdsb2JhbExpbWl0ID0gdGhpcy5nZXRfbGltaXRfZm9yX2tleSggcGFsZXR0ZUlkICk7XHJcblx0XHRcdFx0XHRjb25zdCBsaW1pdCAgICAgICA9IE51bWJlci5pc0Zpbml0ZSggZ2xvYmFsTGltaXQgKSA/IGdsb2JhbExpbWl0IDogcGVyRWxMaW1pdDsgLy8gcHJlZmVyIGdsb2JhbCBtaW5cclxuXHJcblx0XHRcdFx0XHRjb25zdCBjdXJyZW50ID0gdXNhZ2VbcGFsZXR0ZUlkXSB8fCAwO1xyXG5cdFx0XHRcdFx0Y29uc3QgZGlzYWJsZSA9IE51bWJlci5pc0Zpbml0ZSggbGltaXQgKSAmJiBjdXJyZW50ID49IGxpbWl0O1xyXG5cclxuXHRcdFx0XHRcdHBhbmVsX2ZpZWxkLnN0eWxlLnBvaW50ZXJFdmVudHMgPSBkaXNhYmxlID8gJ25vbmUnIDogJyc7XHJcblx0XHRcdFx0XHRwYW5lbF9maWVsZC5zdHlsZS5vcGFjaXR5ICAgICAgID0gZGlzYWJsZSA/ICcwLjQnIDogJyc7XHJcblx0XHRcdFx0XHRwYW5lbF9maWVsZC5zZXRBdHRyaWJ1dGUoICdhcmlhLWRpc2FibGVkJywgZGlzYWJsZSA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHRcdFx0XHRcdGlmICggZGlzYWJsZSApIHtcclxuXHRcdFx0XHRcdFx0cGFuZWxfZmllbGQuc2V0QXR0cmlidXRlKCAndGFiaW5kZXgnLCAnLTEnICk7XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRwYW5lbF9maWVsZC5yZW1vdmVBdHRyaWJ1dGUoICd0YWJpbmRleCcgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdH0gKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBSZXR1cm4gaG93IG1hbnkgdmFsaWQgaW5zdGFuY2VzIHdpdGggdGhpcyB1c2FnZSBrZXkgZXhpc3QgaW4gdGhlIGNhbnZhcy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gVXNhZ2Uga2V5IG9mIGEgcGFsZXR0ZSBpdGVtLlxyXG5cdFx0ICogQHJldHVybnMge251bWJlcn0gQ291bnQgb2YgZXhpc3Rpbmcgbm9uLWludmFsaWQgaW5zdGFuY2VzLlxyXG5cdFx0ICovXHJcblx0XHRjb3VudF9mb3Jfa2V5KCBrZXkgKSB7XHJcblx0XHRcdGlmICggISBrZXkgKSB7XHJcblx0XHRcdFx0cmV0dXJuIDA7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuICggdGhpcy5wYWdlc19jb250YWluZXI/LnF1ZXJ5U2VsZWN0b3JBbGwoXHJcbiAgICAgICAgICAgICAgICBgLndwYmNfYmZiX19wYW5lbC0tcHJldmlldyAud3BiY19iZmJfX2ZpZWxkW2RhdGEtdXNhZ2Vfa2V5PVwiJHtDb3JlLldQQkNfQkZCX1Nhbml0aXplLmVzY19hdHRyX3ZhbHVlX2Zvcl9zZWxlY3Rvcigga2V5ICl9XCJdOm5vdCguaXMtaW52YWxpZCksIFxyXG4gICAgICAgICAgICAgICAgIC53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgLndwYmNfYmZiX19maWVsZFtkYXRhLXR5cGU9XCIke0NvcmUuV1BCQ19CRkJfU2FuaXRpemUuZXNjX2F0dHJfdmFsdWVfZm9yX3NlbGVjdG9yKCBrZXkgKX1cIl06bm90KC5pcy1pbnZhbGlkKWBcclxuXHRcdFx0KSB8fCBbXSApLmxlbmd0aDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEFsaWFzIGZvciBsaW1pdCBsb29rdXAgKHJlYWRhYmlsaXR5KS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gVXNhZ2Uga2V5IG9mIGEgcGFsZXR0ZSBpdGVtLlxyXG5cdFx0ICogQHJldHVybnMge251bWJlcn0gTGltaXQgdmFsdWUgb3IgSW5maW5pdHkuXHJcblx0XHQgKi9cclxuXHRcdGxpbWl0X2Zvcl9rZXkoIGtleSApIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0X2xpbWl0X2Zvcl9rZXkoIGtleSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogUmVtYWluaW5nIHNsb3RzIGZvciB0aGlzIGtleSAoSW5maW5pdHkgaWYgdW5saW1pdGVkKS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gVXNhZ2Uga2V5IG9mIGEgcGFsZXR0ZSBpdGVtLlxyXG5cdFx0ICogQHJldHVybnMge251bWJlcn0gUmVtYWluaW5nIGNvdW50ICg+PSAwKSBvciBJbmZpbml0eS5cclxuXHRcdCAqL1xyXG5cdFx0cmVtYWluaW5nX2Zvcl9rZXkoIGtleSApIHtcclxuXHRcdFx0Y29uc3QgbGltaXQgPSB0aGlzLmxpbWl0X2Zvcl9rZXkoIGtleSApO1xyXG5cdFx0XHRpZiAoIGxpbWl0ID09PSBJbmZpbml0eSApIHtcclxuXHRcdFx0XHRyZXR1cm4gSW5maW5pdHk7XHJcblx0XHRcdH1cclxuXHRcdFx0Y29uc3QgdXNlZCA9IHRoaXMuY291bnRfZm9yX2tleSgga2V5ICk7XHJcblx0XHRcdHJldHVybiBNYXRoLm1heCggMCwgbGltaXQgLSB1c2VkICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBUcnVlIGlmIHlvdSBjYW4gYWRkIGBkZWx0YWAgbW9yZSBpdGVtcyBmb3IgdGhpcyBrZXkuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGtleSAtIFVzYWdlIGtleSBvZiBhIHBhbGV0dGUgaXRlbS5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyfSBbZGVsdGE9MV0gLSBIb3cgbWFueSBpdGVtcyB5b3UgaW50ZW5kIHRvIGFkZC5cclxuXHRcdCAqIEByZXR1cm5zIHtib29sZWFufSBXaGV0aGVyIGFkZGluZyBpcyBhbGxvd2VkLlxyXG5cdFx0ICovXHJcblx0XHRjYW5fYWRkKCBrZXksIGRlbHRhID0gMSApIHtcclxuXHRcdFx0Y29uc3QgcmVtID0gdGhpcy5yZW1haW5pbmdfZm9yX2tleSgga2V5ICk7XHJcblx0XHRcdHJldHVybiAoIHJlbSA9PT0gSW5maW5pdHkgKSA/IHRydWUgOiAoIHJlbSA+PSBkZWx0YSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVUktZmFjaW5nIGdhdGU6IGFsZXJ0IHdoZW4gZXhjZWVkZWQuIFJldHVybnMgYm9vbGVhbiBhbGxvd2VkL2Jsb2NrZWQuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGtleSAtIFVzYWdlIGtleSBvZiBhIHBhbGV0dGUgaXRlbS5cclxuXHRcdCAqIEBwYXJhbSB7e2xhYmVsPzogc3RyaW5nLCBkZWx0YT86IG51bWJlcn19IFtvcHRzPXt9XSAtIE9wdGlvbmFsIFVJIGluZm8uXHJcblx0XHQgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhbGxvd2VkLCBmYWxzZSBpZiBibG9ja2VkLlxyXG5cdFx0ICovXHJcblx0XHRnYXRlX29yX2FsZXJ0KCBrZXksIHsgbGFiZWwgPSBrZXksIGRlbHRhID0gMSB9ID0ge30gKSB7XHJcblx0XHRcdGlmICggdGhpcy5jYW5fYWRkKCBrZXksIGRlbHRhICkgKSB7XHJcblx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdH1cclxuXHRcdFx0Y29uc3QgbGltaXQgPSB0aGlzLmxpbWl0X2Zvcl9rZXkoIGtleSApO1xyXG5cdFx0XHRhbGVydCggYE9ubHkgJHtsaW1pdH0gaW5zdGFuY2Uke2xpbWl0ID4gMSA/ICdzJyA6ICcnfSBvZiBcIiR7bGFiZWx9XCIgYWxsb3dlZC5gICk7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEJhY2t3YXJkLWNvbXBhdGlibGUgYWxpYXMgdXNlZCBlbHNld2hlcmUgaW4gdGhlIGNvZGViYXNlLiAgLSBDaGVjayB3aGV0aGVyIGFub3RoZXIgaW5zdGFuY2Ugd2l0aCB0aGUgZ2l2ZW5cclxuXHRcdCAqIHVzYWdlIGtleSBjYW4gYmUgYWRkZWQuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGtleSAtIFVzYWdlIGtleSBvZiBhIHBhbGV0dGUgaXRlbS5cclxuXHRcdCAqIEByZXR1cm5zIHtib29sZWFufSBXaGV0aGVyIGFkZGluZyBvbmUgbW9yZSBpcyBhbGxvd2VkLlxyXG5cdFx0ICovXHJcblx0XHRpc191c2FnZV9vaygga2V5ICkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5jYW5fYWRkKCBrZXksIDEgKTtcclxuXHRcdH1cclxuXHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogQ29uc3RhbnQgZXZlbnQgbmFtZXMgZm9yIHRoZSBidWlsZGVyLlxyXG5cdCAqL1xyXG5cdENvcmUuV1BCQ19CRkJfRXZlbnRzID0gT2JqZWN0LmZyZWV6ZSh7XHJcblx0XHRTRUxFQ1QgICAgICAgICAgICA6ICd3cGJjOmJmYjpzZWxlY3QnLFxyXG5cdFx0Q0xFQVJfU0VMRUNUSU9OICAgOiAnd3BiYzpiZmI6Y2xlYXItc2VsZWN0aW9uJyxcclxuXHRcdEZJRUxEX0FERCAgICAgICAgIDogJ3dwYmM6YmZiOmZpZWxkOmFkZCcsXHJcblx0XHRGSUVMRF9SRU1PVkUgICAgICA6ICd3cGJjOmJmYjpmaWVsZDpyZW1vdmUnLFxyXG5cdFx0U1RSVUNUVVJFX0NIQU5HRSAgOiAnd3BiYzpiZmI6c3RydWN0dXJlOmNoYW5nZScsXHJcblx0XHRTVFJVQ1RVUkVfTE9BREVEICA6ICd3cGJjOmJmYjpzdHJ1Y3R1cmU6bG9hZGVkJ1xyXG5cdH0pO1xyXG5cclxuXHQvKipcclxuXHQgKiBMaWdodHdlaWdodCBldmVudCBidXMgdGhhdCBlbWl0cyB0byBib3RoIHRoZSBwYWdlcyBjb250YWluZXIgYW5kIGRvY3VtZW50LlxyXG5cdCAqL1xyXG5cdENvcmUuV1BCQ19CRkJfRXZlbnRCdXMgPSAgY2xhc3Mge1xyXG5cdFx0LyoqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBzY29wZV9lbCAtIEVsZW1lbnQgdG8gZGlzcGF0Y2ggYnViYmxlZCBldmVudHMgZnJvbS5cclxuXHRcdCAqL1xyXG5cdFx0Y29uc3RydWN0b3IoIHNjb3BlX2VsICkge1xyXG5cdFx0XHR0aGlzLnNjb3BlX2VsID0gc2NvcGVfZWw7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBFbWl0IGEgRE9NIEN1c3RvbUV2ZW50IHdpdGggcGF5bG9hZC5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEV2ZW50IHR5cGUgKHVzZSBDb3JlLldQQkNfQkZCX0V2ZW50cy4gd2hlbiBwb3NzaWJsZSkuXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gW2RldGFpbD17fV0gLSBBcmJpdHJhcnkgc2VyaWFsaXphYmxlIHBheWxvYWQuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0ZW1pdCggdHlwZSwgZGV0YWlsID0ge30gKSB7XHJcblx0XHRcdGlmICggISB0aGlzLnNjb3BlX2VsICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLnNjb3BlX2VsLmRpc3BhdGNoRXZlbnQoIG5ldyBDdXN0b21FdmVudCggdHlwZSwgeyBkZXRhaWw6IHsgLi4uZGV0YWlsIH0sIGJ1YmJsZXM6IHRydWUgfSApICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBTdWJzY3JpYmUgdG8gYW4gZXZlbnQgb24gZG9jdW1lbnQuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBFdmVudCB0eXBlLlxyXG5cdFx0ICogQHBhcmFtIHsoZXY6Q3VzdG9tRXZlbnQpPT52b2lkfSBoYW5kbGVyIC0gSGFuZGxlciBmdW5jdGlvbi5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRvbiggdHlwZSwgaGFuZGxlciApIHtcclxuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggdHlwZSwgaGFuZGxlciApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVW5zdWJzY3JpYmUgZnJvbSBhbiBldmVudCBvbiBkb2N1bWVudC5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEV2ZW50IHR5cGUuXHJcblx0XHQgKiBAcGFyYW0geyhldjpDdXN0b21FdmVudCk9PnZvaWR9IGhhbmRsZXIgLSBIYW5kbGVyIGZ1bmN0aW9uLlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdG9mZiggdHlwZSwgaGFuZGxlciApIHtcclxuXHRcdFx0ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggdHlwZSwgaGFuZGxlciApO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdCAqIFNvcnRhYmxlSlMgbWFuYWdlcjogc2luZ2xlIHBvaW50IGZvciBjb25zaXN0ZW50IERuRCBjb25maWcuXHJcblx0ICovXHJcblx0Q29yZS5XUEJDX0JGQl9Tb3J0YWJsZU1hbmFnZXIgPSBjbGFzcyAge1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQHBhcmFtIHtXUEJDX0Zvcm1fQnVpbGRlcn0gYnVpbGRlciAtIFRoZSBhY3RpdmUgYnVpbGRlciBpbnN0YW5jZS5cclxuXHRcdCAqIEBwYXJhbSB7eyBncm91cE5hbWU/OiBzdHJpbmcsIGFuaW1hdGlvbj86IG51bWJlciwgZ2hvc3RDbGFzcz86IHN0cmluZywgY2hvc2VuQ2xhc3M/OiBzdHJpbmcsIGRyYWdDbGFzcz86XHJcblx0XHQgKiAgICAgc3RyaW5nIH19IFtvcHRzPXt9XSAtIFZpc3VhbC9iZWhhdmlvciBvcHRpb25zLlxyXG5cdFx0ICovXHJcblx0XHRjb25zdHJ1Y3RvciggYnVpbGRlciwgb3B0cyA9IHt9ICkge1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXIgPSBidWlsZGVyO1xyXG5cdFx0XHRjb25zdCBnaWQgPSB0aGlzLmJ1aWxkZXI/Lmluc3RhbmNlX2lkIHx8IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoIDM2ICkuc2xpY2UoIDIsIDggKTtcclxuXHRcdFx0dGhpcy5vcHRzID0ge1xyXG5cdFx0XHRcdC8vIGdyb3VwTmFtZSAgOiAnZm9ybScsXHJcblx0XHRcdFx0Z3JvdXBOYW1lOiBgZm9ybS0ke2dpZH1gLFxyXG5cdFx0XHRcdGFuaW1hdGlvbiAgOiAxNTAsXHJcblx0XHRcdFx0Z2hvc3RDbGFzcyA6ICd3cGJjX2JmYl9fZHJhZy1naG9zdCcsXHJcblx0XHRcdFx0Y2hvc2VuQ2xhc3M6ICd3cGJjX2JmYl9faGlnaGxpZ2h0JyxcclxuXHRcdFx0XHRkcmFnQ2xhc3MgIDogJ3dwYmNfYmZiX19kcmFnLWFjdGl2ZScsXHJcblx0XHRcdFx0Li4ub3B0c1xyXG5cdFx0XHR9O1xyXG5cdFx0XHQvKiogQHR5cGUge1NldDxIVE1MRWxlbWVudD59ICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lcnMgPSBuZXcgU2V0KCk7XHJcblxyXG5cdFx0XHQvKipcclxuXHRcdFx0ICogR3VhcmQgYWdhaW5zdCBsb3N0IG1vdXNldXAgLyBwb2ludGVydXAgZXZlbnRzLlxyXG5cdFx0XHQgKlxyXG5cdFx0XHQgKiBAdHlwZSB7Ym9vbGVhbn1cclxuXHRcdFx0ICovXHJcblx0XHRcdHRoaXMuX2RyYWdfZmFpbF9zYWZlX2JvdW5kID0gZmFsc2U7XHJcblxyXG5cdFx0XHR0aGlzLl9iaW5kX2RyYWdfZmFpbF9zYWZlKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDbGVhbnVwIGRyYWcgVUkgc3RhdGUuXHJcblx0XHQgKlxyXG5cdFx0ICogVGhpcyBpcyBhIGRlZmVuc2l2ZSBjbGVhbnVwIGZvciBjYXNlcyB3aGVuIENocm9tZSBvciBhIGJyb3dzZXIgZXh0ZW5zaW9uXHJcblx0XHQgKiBsb3NlcyB0aGUgZmluYWwgbW91c2V1cCAvIHBvaW50ZXJ1cCBldmVudCBkdXJpbmcgZmFsbGJhY2sgZHJhZ2dpbmcuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdF9jbGVhbnVwX2RyYWdfdWkoKSB7XHJcblx0XHRcdHRoaXMuX2RyYWdTdGF0ZSA9IG51bGw7XHJcblx0XHRcdHRoaXMuX3RvZ2dsZV9kbmRfcm9vdF9mbGFncyggZmFsc2UgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyPy5fcmVtb3ZlX2RyYWdnaW5nX2NsYXNzPy4oKTtcclxuXHJcblx0XHRcdC8vIFJlbW92ZSBvbmx5IGZhbGxiYWNrIG1pcnJvcnMuIERvIG5vdCB0b3VjaCByZWFsIGRyYWdnZWQgZWxlbWVudHMuXHJcblx0XHRcdGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICcuc29ydGFibGUtZmFsbGJhY2tbZGF0YS1kcmFnLXJvbGVdLCAud3BiY19iZmJfX3NpbXBsZV9saXN0X2ZhbGxiYWNrJyApXHJcblx0XHRcdFx0XHQuZm9yRWFjaCggKGVsKSA9PiB7XHJcblx0XHRcdFx0XHRcdGlmICggZWwucGFyZW50Tm9kZSApIHtcclxuXHRcdFx0XHRcdFx0XHRlbC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKCBlbCApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBCaW5kIGdsb2JhbCBmYWlsLXNhZmUgbGlzdGVuZXJzIGZvciBkcmFnIGNsZWFudXAuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdF9iaW5kX2RyYWdfZmFpbF9zYWZlKCkge1xyXG5cdFx0XHRpZiAoIHRoaXMuX2RyYWdfZmFpbF9zYWZlX2JvdW5kICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5fZHJhZ19mYWlsX3NhZmVfYm91bmQgPSB0cnVlO1xyXG5cclxuXHRcdFx0Y29uc3QgZmluaXNoX2RyYWcgPSAoKSA9PiB7XHJcblx0XHRcdFx0d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSggKCkgPT4ge1xyXG5cdFx0XHRcdFx0dGhpcy5fY2xlYW51cF9kcmFnX3VpKCk7XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHR9O1xyXG5cclxuXHRcdFx0WyAnbW91c2V1cCcsICdwb2ludGVydXAnLCAndG91Y2hlbmQnLCAnZHJhZ2VuZCcgXS5mb3JFYWNoKCAoZXZ0X25hbWUpID0+IHtcclxuXHRcdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCBldnRfbmFtZSwgZmluaXNoX2RyYWcsIHRydWUgKTtcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoICdibHVyJywgZmluaXNoX2RyYWcsIHRydWUgKTtcclxuXHJcblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXHJcblx0XHRcdFx0J3Zpc2liaWxpdHljaGFuZ2UnLFxyXG5cdFx0XHRcdCgpID0+IHtcclxuXHRcdFx0XHRcdGlmICggZG9jdW1lbnQuaGlkZGVuICkge1xyXG5cdFx0XHRcdFx0XHRmaW5pc2hfZHJhZygpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0dHJ1ZVxyXG5cdFx0XHQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVGFnIHRoZSBkcmFnIG1pcnJvciAoZWxlbWVudCB1bmRlciBjdXJzb3IpIHdpdGggcm9sZTogJ3BhbGV0dGUnIHwgJ2NhbnZhcycuXHJcblx0XHQgKiBXb3JrcyB3aXRoIFNvcnRhYmxlJ3MgZmFsbGJhY2sgbWlycm9yICguc29ydGFibGUtZmFsbGJhY2sgLyAuc29ydGFibGUtZHJhZykgYW5kIHdpdGggeW91ciBkcmFnQ2xhc3NcclxuXHRcdCAqICgud3BiY19iZmJfX2RyYWctYWN0aXZlKS5cclxuXHRcdCAqL1xyXG5cdFx0X3RhZ19kcmFnX21pcnJvciggZXZ0ICkge1xyXG5cdFx0XHRjb25zdCBmcm9tUGFsZXR0ZSA9IHRoaXMuYnVpbGRlcj8ucGFsZXR0ZV91bHM/LmluY2x1ZGVzPy4oIGV2dC5mcm9tICk7XHJcblx0XHRcdGNvbnN0IHJvbGUgICAgICAgID0gZnJvbVBhbGV0dGUgPyAncGFsZXR0ZScgOiAnY2FudmFzJztcclxuXHRcdFx0Ly8gV2FpdCBhIHRpY2sgc28gdGhlIG1pcnJvciBleGlzdHMuICAtIFRoZSB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lKCkgbWV0aG9kIHRlbGxzIHRoZSBicm93c2VyIHlvdSB3aXNoIHRvIHBlcmZvcm0gYW4gYW5pbWF0aW9uLlxyXG5cdFx0XHRyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoICgpID0+IHtcclxuXHRcdFx0XHRjb25zdCBtaXJyb3IgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCAnLnNvcnRhYmxlLWZhbGxiYWNrLCAuc29ydGFibGUtZHJhZywgLicgKyB0aGlzLm9wdHMuZHJhZ0NsYXNzICk7XHJcblx0XHRcdFx0aWYgKCBtaXJyb3IgKSB7XHJcblx0XHRcdFx0XHRtaXJyb3Iuc2V0QXR0cmlidXRlKCAnZGF0YS1kcmFnLXJvbGUnLCByb2xlICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0X3RvZ2dsZV9kbmRfcm9vdF9mbGFncyggYWN0aXZlLCBmcm9tX3BhbGV0dGUgPSBmYWxzZSApIHtcclxuXHJcblx0XHRcdC8vIHNldCB0byByb290IGVsZW1lbnQgb2YgYW4gSFRNTCBkb2N1bWVudCwgd2hpY2ggaXMgdGhlIDxodG1sPi5cclxuXHRcdFx0Y29uc3Qgcm9vdCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcclxuXHRcdFx0aWYgKCBhY3RpdmUgKSB7XHJcblx0XHRcdFx0cm9vdC5jbGFzc0xpc3QuYWRkKCAnd3BiY19iZmJfX2RuZC1hY3RpdmUnICk7XHJcblx0XHRcdFx0aWYgKCBmcm9tX3BhbGV0dGUgKSB7XHJcblx0XHRcdFx0XHRyb290LmNsYXNzTGlzdC5hZGQoICd3cGJjX2JmYl9fZHJhZy1mcm9tLXBhbGV0dGUnICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHJvb3QuY2xhc3NMaXN0LnJlbW92ZSggJ3dwYmNfYmZiX19kbmQtYWN0aXZlJywgJ3dwYmNfYmZiX19kcmFnLWZyb20tcGFsZXR0ZScgKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVuc3VyZSBhIHNpbXBsZSB2ZXJ0aWNhbCBzb3J0YWJsZSBsaXN0LlxyXG5cdFx0ICpcclxuXHRcdCAqIFRoaXMgY29uZmlndXJhdGlvbiBpcyBpbnRlbmRlZCBmb3IgaW5zcGVjdG9yL3NpZGViYXIgbGlzdHMgc3VjaCBhczpcclxuXHRcdCAqIC0gZHJvcGRvd24gY2hvaWNlc1xyXG5cdFx0ICogLSByYWRpbyBvcHRpb25zXHJcblx0XHQgKiAtIGNoZWNrYm94IG9wdGlvbnNcclxuXHRcdCAqXHJcblx0XHQgKiBJdCBpcyBpbnRlbnRpb25hbGx5IG11Y2ggc2ltcGxlciB0aGFuIHRoZSBjYW52YXMgRG5EIGNvbmZpZyBhbmQgZG9lcyBub3RcclxuXHRcdCAqIHVzZSB0aGUgY29sdW1uIGVkZ2UtZmVuY2UgLyBzdGlja3ktdGFyZ2V0IGxvZ2ljLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciAtIFNvcnRhYmxlIGxpc3QgY29udGFpbmVyLlxyXG5cdFx0ICogQHBhcmFtIHt7IGhhbmRsZV9zZWxlY3Rvcj86IHN0cmluZywgZHJhZ2dhYmxlX3NlbGVjdG9yPzogc3RyaW5nLCBvblVwZGF0ZT86IEZ1bmN0aW9uIH19IFtoYW5kbGVycz17fV0gLVxyXG5cdFx0ICogICAgIE9wdGlvbmFsIGhhbmRsZXJzL3NlbGVjdG9ycy5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRlbnN1cmVfc2ltcGxlX2xpc3QoIGNvbnRhaW5lciwgaGFuZGxlcnMgPSB7fSApIHtcclxuXHRcdFx0aWYgKCAhIGNvbnRhaW5lciB8fCB0eXBlb2YgU29ydGFibGUgPT09ICd1bmRlZmluZWQnICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoIFNvcnRhYmxlLmdldD8uKCBjb250YWluZXIgKSApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IGNvbW1vbiA9IHtcclxuXHRcdFx0XHRhbmltYXRpb24gICAgICAgIDogdGhpcy5vcHRzLmFuaW1hdGlvbixcclxuXHRcdFx0XHRnaG9zdENsYXNzICAgICAgIDogdGhpcy5vcHRzLmdob3N0Q2xhc3MsXHJcblx0XHRcdFx0Y2hvc2VuQ2xhc3MgICAgICA6IHRoaXMub3B0cy5jaG9zZW5DbGFzcyxcclxuXHRcdFx0XHRkcmFnQ2xhc3MgICAgICAgIDogdGhpcy5vcHRzLmRyYWdDbGFzcyxcclxuXHRcdFx0XHRmb3JjZUZhbGxiYWNrICAgIDogdHJ1ZSxcclxuXHRcdFx0XHQvLyBGb3IgYSBzaW5nbGUgc2Nyb2xsYWJsZSBzaWRlYmFyIGxpc3QgdGhpcyBpcyB1c3VhbGx5IG1vcmUgc3RhYmxlLlxyXG5cdFx0XHRcdGZhbGxiYWNrT25Cb2R5ICAgOiBmYWxzZSxcclxuXHRcdFx0XHRmYWxsYmFja1RvbGVyYW5jZTogOCxcclxuXHRcdFx0XHRyZW1vdmVDbG9uZU9uSGlkZTogdHJ1ZSxcclxuXHRcdFx0XHRvblN0YXJ0ICAgICAgICAgIDogKCkgPT4ge1xyXG5cdFx0XHRcdFx0dGhpcy5idWlsZGVyPy5fYWRkX2RyYWdnaW5nX2NsYXNzPy4oKTtcclxuXHRcdFx0XHRcdHRoaXMuX3RvZ2dsZV9kbmRfcm9vdF9mbGFncyggdHJ1ZSwgZmFsc2UgKTtcclxuXHRcdFx0XHR9LFxyXG5cdFx0XHRcdG9uRW5kICAgICAgICAgICAgOiAoKSA9PiB7XHJcblx0XHRcdFx0XHRzZXRUaW1lb3V0KCAoKSA9PiB7XHJcblx0XHRcdFx0XHRcdHRoaXMuYnVpbGRlcj8uX3JlbW92ZV9kcmFnZ2luZ19jbGFzcz8uKCk7XHJcblx0XHRcdFx0XHR9LCA1MCApO1xyXG5cdFx0XHRcdFx0dGhpcy5fdG9nZ2xlX2RuZF9yb290X2ZsYWdzKCBmYWxzZSApO1xyXG5cdFx0XHRcdFx0dGhpcy5fZHJhZ1N0YXRlID0gbnVsbDtcclxuXHRcdFx0XHRcdHRoaXMuX2NsZWFudXBfZHJhZ191aSgpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fTtcclxuXHJcblx0XHRcdFNvcnRhYmxlLmNyZWF0ZShcclxuXHRcdFx0XHRjb250YWluZXIsXHJcblx0XHRcdFx0e1xyXG5cdFx0XHRcdFx0Li4uY29tbW9uLFxyXG5cdFx0XHRcdFx0Z3JvdXAgICAgICAgICAgICAgICAgICA6IHsgbmFtZTogdGhpcy5vcHRzLmdyb3VwTmFtZSwgcHVsbDogZmFsc2UsIHB1dDogZmFsc2UgfSxcclxuXHRcdFx0XHRcdHNvcnQgICAgICAgICAgICAgICAgICAgOiB0cnVlLFxyXG5cdFx0XHRcdFx0ZGlyZWN0aW9uICAgICAgICAgICAgICA6ICd2ZXJ0aWNhbCcsXHJcblx0XHRcdFx0XHRoYW5kbGUgICAgICAgICAgICAgICAgIDogaGFuZGxlcnMuaGFuZGxlX3NlbGVjdG9yIHx8ICcud3BiY19iZmJfX2RyYWctaGFuZGxlJyxcclxuXHRcdFx0XHRcdGRyYWdnYWJsZSAgICAgICAgICAgICAgOiBoYW5kbGVycy5kcmFnZ2FibGVfc2VsZWN0b3IgfHwgJy53cGJjX2JmYl9fc29ydGFibGUtcm93JyxcclxuXHRcdFx0XHRcdGZhbGxiYWNrQ2xhc3MgICAgICAgICAgOiAnd3BiY19iZmJfX3NpbXBsZV9saXN0X2ZhbGxiYWNrJyxcclxuXHRcdFx0XHRcdGZpbHRlciAgICAgICAgICAgICAgICAgOiBbXHJcblx0XHRcdFx0XHRcdCdpbnB1dCcsXHJcblx0XHRcdFx0XHRcdCd0ZXh0YXJlYScsXHJcblx0XHRcdFx0XHRcdCdzZWxlY3QnLFxyXG5cdFx0XHRcdFx0XHQnYnV0dG9uJyxcclxuXHRcdFx0XHRcdFx0J2EnLFxyXG5cdFx0XHRcdFx0XHQnLndwYmNfYmZiX19uby1kcmFnLXpvbmUnLFxyXG5cdFx0XHRcdFx0XHQnLndwYmNfYmZiX19uby1kcmFnLXpvbmUgKidcclxuXHRcdFx0XHRcdF0uam9pbiggJywnICksXHJcblx0XHRcdFx0XHRwcmV2ZW50T25GaWx0ZXIgICAgICAgIDogZmFsc2UsXHJcblx0XHRcdFx0XHRpbnZlcnRTd2FwICAgICAgICAgICAgIDogZmFsc2UsXHJcblx0XHRcdFx0XHRzd2FwVGhyZXNob2xkICAgICAgICAgIDogMC4zMCxcclxuXHRcdFx0XHRcdGludmVydGVkU3dhcFRocmVzaG9sZCAgOiAwLjYwLFxyXG5cdFx0XHRcdFx0ZW1wdHlJbnNlcnRUaHJlc2hvbGQgICA6IDgsXHJcblx0XHRcdFx0XHRkcmFnb3ZlckJ1YmJsZSAgICAgICAgIDogZmFsc2UsXHJcblx0XHRcdFx0XHRzY3JvbGwgICAgICAgICAgICAgICAgIDogdHJ1ZSxcclxuXHRcdFx0XHRcdHNjcm9sbFNlbnNpdGl2aXR5ICAgICAgOiA2MCxcclxuXHRcdFx0XHRcdHNjcm9sbFNwZWVkICAgICAgICAgICAgOiAxNCxcclxuXHRcdFx0XHRcdG9uVXBkYXRlICAgICAgICAgICAgICAgOiBoYW5kbGVycy5vblVwZGF0ZSB8fCBmdW5jdGlvbiAoKSB7fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0KTtcclxuXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lcnMuYWRkKCBjb250YWluZXIgKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBFbnN1cmUgU29ydGFibGUgaXMgYXR0YWNoZWQgdG8gYSBjb250YWluZXIgd2l0aCByb2xlICdwYWxldHRlJyBvciAnY2FudmFzJy5cclxuXHRcdCAqXHJcblx0XHQgKiAgLS0gSGFuZGxlIHNlbGVjdG9yczogaGFuZGxlOiAgJy5zZWN0aW9uLWRyYWctaGFuZGxlLCAud3BiY19iZmJfX2RyYWctaGFuZGxlLCAud3BiY19iZmJfX2RyYWctYW55d2hlcmUsXHJcblx0XHQgKiBbZGF0YS1kcmFnZ2FibGU9XCJ0cnVlXCJdJ1xyXG5cdFx0ICogIC0tIERyYWdnYWJsZSBnYXRlOiBkcmFnZ2FibGU6ICcud3BiY19iZmJfX2ZpZWxkOm5vdChbZGF0YS1kcmFnZ2FibGU9XCJmYWxzZVwiXSksIC53cGJjX2JmYl9fc2VjdGlvbidcclxuXHRcdCAqICAtLSBGaWx0ZXIgKG92ZXJsYXktc2FmZSk6ICAgICBpZ25vcmUgZXZlcnl0aGluZyBpbiBvdmVybGF5IGV4Y2VwdCB0aGUgaGFuZGxlIC1cclxuXHRcdCAqICcud3BiY19iZmJfX292ZXJsYXktY29udHJvbHNcclxuXHRcdCAqICo6bm90KC53cGJjX2JmYl9fZHJhZy1oYW5kbGUpOm5vdCguc2VjdGlvbi1kcmFnLWhhbmRsZSk6bm90KC53cGJjX2ljbl9kcmFnX2luZGljYXRvciknXHJcblx0XHQgKiAgLS0gTm8tZHJhZyB3cmFwcGVyOiAgICAgICAgICAgdXNlIC53cGJjX2JmYl9fbm8tZHJhZy16b25lIGluc2lkZSByZW5kZXJlcnMgZm9yIGlucHV0cy93aWRnZXRzLlxyXG5cdFx0ICogIC0tIEZvY3VzIGd1YXJkIChvcHRpb25hbCk6ICAgIGZsaXAgW2RhdGEtZHJhZ2dhYmxlXSBvbiBmb2N1c2luL2ZvY3Vzb3V0IHRvIHByZXZlbnQgYWNjaWRlbnRhbCBkcmFncyB3aGlsZVxyXG5cdFx0ICogdHlwaW5nLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciAtIFRoZSBlbGVtZW50IHRvIGVuaGFuY2Ugd2l0aCBTb3J0YWJsZS5cclxuXHRcdCAqIEBwYXJhbSB7J3BhbGV0dGUnfCdjYW52YXMnfSByb2xlIC0gQmVoYXZpb3IgcHJvZmlsZSB0byBhcHBseS5cclxuXHRcdCAqIEBwYXJhbSB7eyBvbkFkZD86IEZ1bmN0aW9uIH19IFtoYW5kbGVycz17fV0gLSBPcHRpb25hbCBoYW5kbGVycy5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRlbnN1cmUoIGNvbnRhaW5lciwgcm9sZSwgaGFuZGxlcnMgPSB7fSApIHtcclxuXHRcdFx0aWYgKCAhIGNvbnRhaW5lciB8fCB0eXBlb2YgU29ydGFibGUgPT09ICd1bmRlZmluZWQnICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoIFNvcnRhYmxlLmdldD8uKCBjb250YWluZXIgKSApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IHNvcnRhYmxlX2tpbmQgPSBoYW5kbGVycy5zb3J0YWJsZV9raW5kIHx8ICcnO1xyXG5cclxuXHRcdFx0aWYgKCBzb3J0YWJsZV9raW5kID09PSAnc2ltcGxlX2xpc3QnICkge1xyXG5cdFx0XHRcdHRoaXMuZW5zdXJlX3NpbXBsZV9saXN0KCBjb250YWluZXIsIGhhbmRsZXJzICk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb25zdCBjb21tb24gPSB7XHJcblx0XHRcdFx0YW5pbWF0aW9uICA6IHRoaXMub3B0cy5hbmltYXRpb24sXHJcblx0XHRcdFx0Z2hvc3RDbGFzcyA6IHRoaXMub3B0cy5naG9zdENsYXNzLFxyXG5cdFx0XHRcdGNob3NlbkNsYXNzOiB0aGlzLm9wdHMuY2hvc2VuQ2xhc3MsXHJcblx0XHRcdFx0ZHJhZ0NsYXNzICA6IHRoaXMub3B0cy5kcmFnQ2xhc3MsXHJcblx0XHRcdFx0Ly8gPT0gRWxlbWVudCB1bmRlciB0aGUgY3Vyc29yICA9PSBFbnN1cmUgd2UgZHJhZyBhIHJlYWwgRE9NIG1pcnJvciB5b3UgY2FuIHN0eWxlIHZpYSBDU1MgKGNyb3NzLWJyb3dzZXIpLlxyXG5cdFx0XHRcdGZvcmNlRmFsbGJhY2sgICAgOiB0cnVlLFxyXG5cdFx0XHRcdGZhbGxiYWNrT25Cb2R5ICAgOiB0cnVlLFxyXG5cdFx0XHRcdGZhbGxiYWNrVG9sZXJhbmNlOiA4LFxyXG5cdFx0XHRcdHJlbW92ZUNsb25lT25IaWRlOiB0cnVlLFxyXG5cdFx0XHRcdC8vIEFkZCBib2R5L2h0bWwgZmxhZ3Mgc28geW91IGNhbiBzdHlsZSBkaWZmZXJlbnRseSB3aGVuIGRyYWdnaW5nIGZyb20gcGFsZXR0ZS5cclxuXHRcdFx0XHRvblN0YXJ0OiAoZXZ0KSA9PiB7XHJcblx0XHRcdFx0XHR0aGlzLmJ1aWxkZXI/Ll9hZGRfZHJhZ2dpbmdfY2xhc3M/LigpO1xyXG5cclxuXHRcdFx0XHRcdGNvbnN0IGZyb21QYWxldHRlID0gdGhpcy5idWlsZGVyPy5wYWxldHRlX3Vscz8uaW5jbHVkZXM/LiggZXZ0LmZyb20gKTtcclxuXHRcdFx0XHRcdHRoaXMuX3RvZ2dsZV9kbmRfcm9vdF9mbGFncyggdHJ1ZSwgZnJvbVBhbGV0dGUgKTsgIC8vIHNldCB0byByb290IEhUTUwgZG9jdW1lbnQ6IGh0bWwud3BiY19iZmJfX2RuZC1hY3RpdmUud3BiY19iZmJfX2RyYWctZnJvbS1wYWxldHRlIC5cclxuXHJcblx0XHRcdFx0XHR0aGlzLl90YWdfZHJhZ19taXJyb3IoIGV2dCApOyAgICAgICAgICAgICAgICAgICAgICAvLyBBZGQgJ2RhdGEtZHJhZy1yb2xlJyBhdHRyaWJ1dGUgdG8gIGVsZW1lbnQgdW5kZXIgY3Vyc29yLlxyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0b25FbmQgIDogKCkgPT4ge1xyXG5cdFx0XHRcdFx0c2V0VGltZW91dCggKCkgPT4geyB0aGlzLmJ1aWxkZXIuX3JlbW92ZV9kcmFnZ2luZ19jbGFzcygpOyB9LCA1MCApO1xyXG5cdFx0XHRcdFx0dGhpcy5fdG9nZ2xlX2RuZF9yb290X2ZsYWdzKCBmYWxzZSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fTtcclxuXHJcblx0XHRcdGlmICggcm9sZSA9PT0gJ3BhbGV0dGUnICkge1xyXG5cdFx0XHRcdFNvcnRhYmxlLmNyZWF0ZSggY29udGFpbmVyLCB7XHJcblx0XHRcdFx0XHQuLi5jb21tb24sXHJcblx0XHRcdFx0XHRncm91cCAgIDogeyBuYW1lOiB0aGlzLm9wdHMuZ3JvdXBOYW1lLCBwdWxsOiAnY2xvbmUnLCBwdXQ6IGZhbHNlIH0sXHJcblx0XHRcdFx0XHRzb3J0ICAgIDogZmFsc2VcclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVycy5hZGQoIGNvbnRhaW5lciApO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gcm9sZSA9PT0gJ2NhbnZhcycuXHJcblx0XHRcdFNvcnRhYmxlLmNyZWF0ZSggY29udGFpbmVyLCB7XHJcblx0XHRcdFx0Li4uY29tbW9uLFxyXG5cdFx0XHRcdGdyb3VwICAgIDoge1xyXG5cdFx0XHRcdFx0bmFtZTogdGhpcy5vcHRzLmdyb3VwTmFtZSxcclxuXHRcdFx0XHRcdHB1bGw6IHRydWUsXHJcblx0XHRcdFx0XHRwdXQgOiAodG8sIGZyb20sIGRyYWdnZWRFbCkgPT4ge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gZHJhZ2dlZEVsLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19maWVsZCcgKSB8fFxyXG5cdFx0XHRcdFx0XHRcdCAgIGRyYWdnZWRFbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9LFxyXG5cdFx0XHRcdC8vIC0tLS0tLS0tLS0gRG5EIEhhbmRsZXJzIC0tLS0tLS0tLS0tLS0tICAgICAgICAgICAgICAgIC8vIEdyYWIgYW55d2hlcmUgb24gZmllbGRzIHRoYXQgb3B0LWluIHdpdGggdGhlIGNsYXNzIG9yIGF0dHJpYnV0ZS4gIC0gU2VjdGlvbnMgc3RpbGwgcmVxdWlyZSB0aGVpciBkZWRpY2F0ZWQgaGFuZGxlLlxyXG5cdFx0XHRcdGhhbmRsZSAgIDogJy5zZWN0aW9uLWRyYWctaGFuZGxlLCAud3BiY19iZmJfX2RyYWctaGFuZGxlLCAud3BiY19iZmJfX2RyYWctYW55d2hlcmUsIFtkYXRhLWRyYWdnYWJsZT1cInRydWVcIl0nLFxyXG5cdFx0XHRcdGRyYWdnYWJsZTogJy53cGJjX2JmYl9fZmllbGQ6bm90KFtkYXRhLWRyYWdnYWJsZT1cImZhbHNlXCJdKSwgLndwYmNfYmZiX19zZWN0aW9uJywgICAgICAgICAgICAgICAgICAgICAgICAvLyBQZXItZmllbGQgb3B0LW91dCB3aXRoIFtkYXRhLWRyYWdnYWJsZT1cImZhbHNlXCJdIChlLmcuLCB3aGlsZSBlZGl0aW5nKS5cclxuXHRcdFx0XHQvLyAtLS0tLS0tLS0tIEZpbHRlcnMgLSBObyBEbkQgLS0tLS0tLS0tLSAgICAgICAgICAgICAgICAvLyBEZWNsYXJhdGl2ZSDigJxuby1kcmFnIHpvbmVz4oCdOiBhbnl0aGluZyBpbnNpZGUgdGhlc2Ugd3JhcHBlcnMgd29u4oCZdCBzdGFydCBhIGRyYWcuXHJcblx0XHRcdFx0ZmlsdGVyOiBbXHJcblx0XHRcdFx0XHQnLndwYmNfYmZiX19uby1kcmFnLXpvbmUnLFxyXG5cdFx0XHRcdFx0Jy53cGJjX2JmYl9fbm8tZHJhZy16b25lIConLFxyXG5cdFx0XHRcdFx0Jy53cGJjX2JmYl9fY29sdW1uLXJlc2l6ZXInLCAgLy8gSWdub3JlIHRoZSByZXNpemVyIHJhaWxzIGR1cmluZyBEbkQgKHByZXZlbnRzIGVkZ2Ug4oCcc25hcOKAnSkuXHJcblx0XHRcdFx0XHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJbiB0aGUgb3ZlcmxheSB0b29sYmFyLCBibG9jayBldmVyeXRoaW5nIEVYQ0VQVCB0aGUgZHJhZyBoYW5kbGUgKGFuZCBpdHMgaWNvbikuXHJcblx0XHRcdFx0XHQnLndwYmNfYmZiX19vdmVybGF5LWNvbnRyb2xzICo6bm90KC53cGJjX2JmYl9fZHJhZy1oYW5kbGUpOm5vdCguc2VjdGlvbi1kcmFnLWhhbmRsZSk6bm90KC53cGJjX2ljbl9kcmFnX2luZGljYXRvciknXHJcblx0XHRcdFx0XS5qb2luKCAnLCcgKSxcclxuXHRcdFx0XHRwcmV2ZW50T25GaWx0ZXIgIDogZmFsc2UsXHJcblx0XHRcdFx0XHQvLyAtLS0tLS0tLS0tIGFudGktaml0dGVyIHR1bmluZyAtLS0tLS0tLS0tXHJcblx0XHRcdFx0ZGlyZWN0aW9uICAgICAgICAgICAgOiAndmVydGljYWwnLCAgICAgICAgICAgLy8gY29sdW1ucyBhcmUgdmVydGljYWwgbGlzdHMuXHJcblx0XHRcdFx0aW52ZXJ0U3dhcCAgICAgICAgICAgOiB0cnVlLCAgICAgICAgICAgICAgICAgLy8gdXNlIHN3YXAgb24gaW52ZXJ0ZWQgb3ZlcmxhcC5cclxuXHRcdFx0XHRzd2FwVGhyZXNob2xkICAgICAgICA6IDAuNjUsICAgICAgICAgICAgICAgICAvLyBiZSBsZXNzIGVhZ2VyIHRvIHN3YXAuXHJcblx0XHRcdFx0aW52ZXJ0ZWRTd2FwVGhyZXNob2xkOiAwLjg1LCAgICAgICAgICAgICAgICAgLy8gcmVxdWlyZSBkZWVwZXIgb3ZlcmxhcCB3aGVuIGludmVydGVkLlxyXG5cdFx0XHRcdGVtcHR5SW5zZXJ0VGhyZXNob2xkIDogMjQsICAgICAgICAgICAgICAgICAgIC8vIGRvbuKAmXQganVtcCBpbnRvIGVtcHR5IGNvbnRhaW5lcnMgdG9vIGVhcmx5LlxyXG5cdFx0XHRcdGRyYWdvdmVyQnViYmxlICAgICAgIDogZmFsc2UsICAgICAgICAgICAgICAgIC8vIGtlZXAgZHJhZ292ZXIgbG9jYWwuXHJcblx0XHRcdFx0c2Nyb2xsICAgICAgICAgICAgICAgOiB0cnVlLFxyXG5cdFx0XHRcdHNjcm9sbFNlbnNpdGl2aXR5ICAgIDogNDAsXHJcblx0XHRcdFx0c2Nyb2xsU3BlZWQgICAgICAgICAgOiAxMCxcclxuXHRcdFx0XHQvKipcclxuXHRcdFx0XHQgKiBFbnRlci9sZWF2ZSBoeXN0ZXJlc2lzIGZvciBjcm9zcy1jb2x1bW4gbW92ZXMuICAgIE9ubHkgYWxsb3cgZHJvcHBpbmcgaW50byBgdG9gIHdoZW4gdGhlIHBvaW50ZXIgaXNcclxuXHRcdFx0XHQgKiB3ZWxsIGluc2lkZSBpdC5cclxuXHRcdFx0XHQgKi9cclxuXHRcdFx0XHRvbk1vdmU6IChldnQsIG9yaWdpbmFsRXZlbnQpID0+IHtcclxuXHJcblx0XHRcdFx0XHRjb25zdCB7IHRvLCBmcm9tIH0gPSBldnQ7XHJcblx0XHRcdFx0XHRpZiAoICEgdG8gfHwgISBmcm9tICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHRjb25zdCBpbl9wcmV2aWV3X2NhbnZhcyA9ICEhIHRvLmNsb3Nlc3QoICcud3BiY19iZmJfX3BhbmVsLS1wcmV2aWV3JyApO1xyXG5cdFx0XHRcdFx0aWYgKCAhIGluX3ByZXZpZXdfY2FudmFzICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHQvLyBPbmx5IGdhdGUgY29sdW1ucyAobm90IHBhZ2UgY29udGFpbmVycyksIGFuZCBvbmx5IGZvciBjcm9zcy1jb2x1bW4gbW92ZXMgaW4gdGhlIHNhbWUgcm93XHJcblx0XHRcdFx0XHRjb25zdCBpc0NvbHVtbiA9IHRvLmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fY29sdW1uJyApO1xyXG5cdFx0XHRcdFx0aWYgKCAhaXNDb2x1bW4gKSByZXR1cm4gdHJ1ZTtcclxuXHJcblx0XHRcdFx0XHRjb25zdCBmcm9tUm93ID0gZnJvbS5jbG9zZXN0KCAnLndwYmNfYmZiX19yb3cnICk7XHJcblx0XHRcdFx0XHRjb25zdCB0b1JvdyAgID0gdG8uY2xvc2VzdCggJy53cGJjX2JmYl9fcm93JyApO1xyXG5cdFx0XHRcdFx0aWYgKCBmcm9tUm93ICYmIHRvUm93ICYmIGZyb21Sb3cgIT09IHRvUm93ICkgcmV0dXJuIHRydWU7XHJcblxyXG5cdFx0XHRcdFx0Y29uc3QgcmVjdCA9IHRvLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG5cdFx0XHRcdFx0Y29uc3QgZXZ0WCA9IChvcmlnaW5hbEV2ZW50LnRvdWNoZXM/LlswXT8uY2xpZW50WCkgPz8gb3JpZ2luYWxFdmVudC5jbGllbnRYO1xyXG5cdFx0XHRcdFx0Y29uc3QgZXZ0WSA9IChvcmlnaW5hbEV2ZW50LnRvdWNoZXM/LlswXT8uY2xpZW50WSkgPz8gb3JpZ2luYWxFdmVudC5jbGllbnRZO1xyXG5cclxuXHRcdFx0XHRcdC8vIC0tLSBFZGdlIGZlbmNlIChsaWtlIHlvdSBoYWQpLCBidXQgY2xhbXBlZCBmb3IgdGlueSBjb2x1bW5zXHJcblx0XHRcdFx0XHRjb25zdCBwYWRkaW5nWCA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuY2xhbXAoIHJlY3Qud2lkdGggKiAwLjIwLCAxMiwgMzYgKTtcclxuXHRcdFx0XHRcdGNvbnN0IHBhZGRpbmdZID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5jbGFtcCggcmVjdC5oZWlnaHQgKiAwLjEwLCA2LCAxNiApO1xyXG5cclxuXHRcdFx0XHRcdC8vIExvb3NlciBZIGlmIHRoZSBjb2x1bW4gaXMgdmlzdWFsbHkgdGlueS9lbXB0eVxyXG5cdFx0XHRcdFx0Y29uc3QgaXNWaXN1YWxseUVtcHR5ID0gdG8uY2hpbGRFbGVtZW50Q291bnQgPT09IDAgfHwgcmVjdC5oZWlnaHQgPCA2NDtcclxuXHRcdFx0XHRcdGNvbnN0IGlubmVyVG9wICAgICAgICA9IHJlY3QudG9wICsgKGlzVmlzdWFsbHlFbXB0eSA/IDQgOiBwYWRkaW5nWSk7XHJcblx0XHRcdFx0XHRjb25zdCBpbm5lckJvdHRvbSAgICAgPSByZWN0LmJvdHRvbSAtIChpc1Zpc3VhbGx5RW1wdHkgPyA0IDogcGFkZGluZ1kpO1xyXG5cdFx0XHRcdFx0Y29uc3QgaW5uZXJMZWZ0ICAgICAgID0gcmVjdC5sZWZ0ICsgcGFkZGluZ1g7XHJcblx0XHRcdFx0XHRjb25zdCBpbm5lclJpZ2h0ICAgICAgPSByZWN0LnJpZ2h0IC0gcGFkZGluZ1g7XHJcblxyXG5cdFx0XHRcdFx0Y29uc3QgaW5zaWRlWCA9IGV2dFggPiBpbm5lckxlZnQgJiYgZXZ0WCA8IGlubmVyUmlnaHQ7XHJcblx0XHRcdFx0XHRjb25zdCBpbnNpZGVZID0gZXZ0WSA+IGlubmVyVG9wICYmIGV2dFkgPCBpbm5lckJvdHRvbTtcclxuXHRcdFx0XHRcdGlmICggIShpbnNpZGVYICYmIGluc2lkZVkpICkgcmV0dXJuIGZhbHNlOyAgIC8vIHN0YXkgaW4gY3VycmVudCBjb2x1bW4gdW50aWwgd2VsbCBpbnNpZGUgbmV3IG9uZVxyXG5cclxuXHRcdFx0XHRcdC8vIC0tLSBTdGlja3kgdGFyZ2V0IGNvbW1pdCBkaXN0YW5jZTogb25seSBzd2l0Y2ggaWYgd2XigJlyZSBjbGVhcmx5IGluc2lkZSB0aGUgbmV3IGNvbHVtblxyXG5cdFx0XHRcdFx0Y29uc3QgZHMgPSB0aGlzLl9kcmFnU3RhdGU7XHJcblx0XHRcdFx0XHRpZiAoIGRzICkge1xyXG5cdFx0XHRcdFx0XHRpZiAoIGRzLnN0aWNreVRvICYmIGRzLnN0aWNreVRvICE9PSB0byApIHtcclxuXHRcdFx0XHRcdFx0XHQvLyByZXF1aXJlIGEgZGVlcGVyIHBlbmV0cmF0aW9uIHRvIHN3aXRjaCBjb2x1bW5zXHJcblx0XHRcdFx0XHRcdFx0Y29uc3QgY29tbWl0WCA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuY2xhbXAoIHJlY3Qud2lkdGggKiAwLjI1LCAxOCwgNDAgKTsgICAvLyAyNSUgb3IgMTjigJM0MHB4XHJcblx0XHRcdFx0XHRcdFx0Y29uc3QgY29tbWl0WSA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuY2xhbXAoIHJlY3QuaGVpZ2h0ICogMC4xNSwgMTAsIDI4ICk7ICAvLyAxNSUgb3IgMTDigJMyOHB4XHJcblxyXG5cdFx0XHRcdFx0XHRcdGNvbnN0IGRlZXBJbnNpZGUgPVxyXG5cdFx0XHRcdFx0XHRcdFx0XHQgIChldnRYID4gcmVjdC5sZWZ0ICsgY29tbWl0WCAmJiBldnRYIDwgcmVjdC5yaWdodCAtIGNvbW1pdFgpICYmXHJcblx0XHRcdFx0XHRcdFx0XHRcdCAgKGV2dFkgPiByZWN0LnRvcCArIGNvbW1pdFkgJiYgZXZ0WSA8IHJlY3QuYm90dG9tIC0gY29tbWl0WSk7XHJcblxyXG5cdFx0XHRcdFx0XHRcdGlmICggIWRlZXBJbnNpZGUgKSByZXR1cm4gZmFsc2U7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0Ly8gV2UgYWNjZXB0IHRoZSBuZXcgdGFyZ2V0IG5vdy5cclxuXHRcdFx0XHRcdFx0ZHMuc3RpY2t5VG8gICAgID0gdG87XHJcblx0XHRcdFx0XHRcdGRzLmxhc3RTd2l0Y2hUcyA9IHBlcmZvcm1hbmNlLm5vdygpO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0b25TdGFydDogKGV2dCkgPT4ge1xyXG5cdFx0XHRcdFx0dGhpcy5idWlsZGVyPy5fYWRkX2RyYWdnaW5nX2NsYXNzPy4oKTtcclxuXHRcdFx0XHRcdC8vIE1hdGNoIHRoZSBmbGFncyB3ZSBzZXQgaW4gY29tbW9uIHNvIENTUyBzdGF5cyBjb25zaXN0ZW50IG9uIGNhbnZhcyBkcmFncyB0b28uXHJcblx0XHRcdFx0XHRjb25zdCBmcm9tUGFsZXR0ZSA9IHRoaXMuYnVpbGRlcj8ucGFsZXR0ZV91bHM/LmluY2x1ZGVzPy4oIGV2dC5mcm9tICk7XHJcblx0XHRcdFx0XHR0aGlzLl90b2dnbGVfZG5kX3Jvb3RfZmxhZ3MoIHRydWUsIGZyb21QYWxldHRlICk7ICAgICAgICAgIC8vIHNldCB0byByb290IEhUTUwgZG9jdW1lbnQ6IGh0bWwud3BiY19iZmJfX2RuZC1hY3RpdmUud3BiY19iZmJfX2RyYWctZnJvbS1wYWxldHRlIC5cclxuXHRcdFx0XHRcdHRoaXMuX3RhZ19kcmFnX21pcnJvciggZXZ0ICk7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUYWcgdGhlIG1pcnJvciB1bmRlciBjdXJzb3IuXHJcblx0XHRcdFx0XHR0aGlzLl9kcmFnU3RhdGUgPSB7IHN0aWNreVRvOiBudWxsLCBsYXN0U3dpdGNoVHM6IDAgfTsgICAgLy8gcGVyLWRyYWcgc3RhdGUuXHJcblx0XHRcdFx0fSxcclxuXHRcdFx0XHRvbkVuZCAgOiAoKSA9PiB7XHJcblx0XHRcdFx0XHRzZXRUaW1lb3V0KCAoKSA9PiB7IHRoaXMuYnVpbGRlci5fcmVtb3ZlX2RyYWdnaW5nX2NsYXNzKCk7IH0sIDUwICk7XHJcblx0XHRcdFx0XHR0aGlzLl90b2dnbGVfZG5kX3Jvb3RfZmxhZ3MoIGZhbHNlICk7ICAgICAgICAgICAgICAgICAgICAvLyBzZXQgdG8gcm9vdCBIVE1MIGRvY3VtZW50IHdpdGhvdXQgdGhlc2UgY2xhc3NlczogaHRtbC53cGJjX2JmYl9fZG5kLWFjdGl2ZS53cGJjX2JmYl9fZHJhZy1mcm9tLXBhbGV0dGUgLlxyXG5cdFx0XHRcdFx0dGhpcy5fZHJhZ1N0YXRlID0gbnVsbDtcclxuXHRcdFx0XHR9LFxyXG5cdFx0XHRcdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRcdFx0XHQvLyBvbkFkZDogaGFuZGxlcnMub25BZGQgfHwgdGhpcy5idWlsZGVyLmhhbmRsZV9vbl9hZGQuYmluZCggdGhpcy5idWlsZGVyIClcclxuXHRcdFx0XHRvbkFkZDogKGV2dCkgPT4ge1xyXG5cdFx0XHRcdFx0aWYgKCB0aGlzLl9vbl9hZGRfc2VjdGlvbiggZXZ0ICkgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdC8vIEZhbGxiYWNrOiBvcmlnaW5hbCBoYW5kbGVyIGZvciBub3JtYWwgZmllbGRzLlxyXG5cdFx0XHRcdFx0KGhhbmRsZXJzLm9uQWRkIHx8IHRoaXMuYnVpbGRlci5oYW5kbGVfb25fYWRkLmJpbmQoIHRoaXMuYnVpbGRlciApKSggZXZ0ICk7XHJcblx0XHRcdFx0fSxcclxuXHRcdFx0XHRvblVwZGF0ZTogKCkgPT4ge1xyXG5cdFx0XHRcdFx0dGhpcy5idWlsZGVyLmJ1cz8uZW1pdD8uKCBDb3JlLldQQkNfQkZCX0V2ZW50cy5TVFJVQ1RVUkVfQ0hBTkdFLCB7IHJlYXNvbjogJ3NvcnQtdXBkYXRlJyB9ICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0XHR0aGlzLl9jb250YWluZXJzLmFkZCggY29udGFpbmVyICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBIYW5kbGUgYWRkaW5nL21vdmluZyBzZWN0aW9ucyB2aWEgU29ydGFibGUgb25BZGQuXHJcblx0XHQgKiBSZXR1cm5zIHRydWUgaWYgaGFuZGxlZCAoaS5lLiwgaXQgd2FzIGEgc2VjdGlvbiksIGZhbHNlIHRvIGxldCB0aGUgZGVmYXVsdCBmaWVsZCBoYW5kbGVyIHJ1bi5cclxuXHRcdCAqXHJcblx0XHQgKiAtIFBhbGV0dGUgLT4gY2FudmFzOiByZW1vdmUgdGhlIHBsYWNlaG9sZGVyIGNsb25lIGFuZCBidWlsZCBhIGZyZXNoIHNlY3Rpb24gdmlhIGFkZF9zZWN0aW9uKClcclxuXHRcdCAqIC0gQ2FudmFzIC0+IGNhbnZhczoga2VlcCB0aGUgbW92ZWQgRE9NIChhbmQgaXRzIGNoaWxkcmVuKSwganVzdCByZS13aXJlIG92ZXJsYXlzL3NvcnRhYmxlcy9tZXRhZGF0YVxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7U29ydGFibGUuU29ydGFibGVFdmVudH0gZXZ0XHJcblx0XHQgKiBAcmV0dXJucyB7Ym9vbGVhbn1cclxuXHRcdCAqL1xyXG5cdFx0X29uX2FkZF9zZWN0aW9uKGV2dCkge1xyXG5cclxuXHRcdFx0Y29uc3QgaXRlbSA9IGV2dC5pdGVtO1xyXG5cdFx0XHRpZiAoICEgaXRlbSApIHtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIElkZW50aWZ5IHNlY3Rpb25zIGJvdGggZnJvbSBwYWxldHRlIGl0ZW1zIChsaSBjbG9uZXMpIGFuZCByZWFsIGNhbnZhcyBub2Rlcy5cclxuXHRcdFx0Y29uc3QgZGF0YSAgICAgID0gQ29yZS5XUEJDX0Zvcm1fQnVpbGRlcl9IZWxwZXIuZ2V0X2FsbF9kYXRhX2F0dHJpYnV0ZXMoIGl0ZW0gKTtcclxuXHRcdFx0Y29uc3QgaXNTZWN0aW9uID0gaXRlbS5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSB8fCAoZGF0YT8udHlwZSB8fCBpdGVtLmRhdGFzZXQ/LnR5cGUpID09PSAnc2VjdGlvbic7XHJcblxyXG5cdFx0XHRpZiAoICEgaXNTZWN0aW9uICkge1xyXG5cdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3QgZnJvbVBhbGV0dGUgPSB0aGlzLmJ1aWxkZXI/LnBhbGV0dGVfdWxzPy5pbmNsdWRlcz8uKCBldnQuZnJvbSApID09PSB0cnVlO1xyXG5cclxuXHRcdFx0aWYgKCAhIGZyb21QYWxldHRlICkge1xyXG5cdFx0XHRcdC8vIENhbnZhcyAtPiBjYW52YXMgbW92ZTogRE8gTk9UIHJlYnVpbGQvcmVtb3ZlOyBwcmVzZXJ2ZSBjaGlsZHJlbi5cclxuXHRcdFx0XHR0aGlzLmJ1aWxkZXIuYWRkX292ZXJsYXlfdG9vbGJhcj8uKCBpdGVtICk7ICAgICAgICAgICAgICAgICAgICAgICAvLyBlbnN1cmUgb3ZlcmxheSBleGlzdHNcclxuXHRcdFx0XHR0aGlzLmJ1aWxkZXIucGFnZXNfc2VjdGlvbnM/LmluaXRfYWxsX25lc3RlZF9zb3J0YWJsZXM/LiggaXRlbSApOyAvLyBlbnN1cmUgaW5uZXIgc29ydGFibGVzXHJcblxyXG5cdFx0XHRcdC8vIEVuc3VyZSBtZXRhZGF0YSBwcmVzZW50L3VwZGF0ZWRcclxuXHRcdFx0XHRpdGVtLmRhdGFzZXQudHlwZSAgICA9ICdzZWN0aW9uJztcclxuXHRcdFx0XHRjb25zdCBjb2xzICAgICAgICAgICA9IGl0ZW0ucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fcm93ID4gLndwYmNfYmZiX19jb2x1bW4nICkubGVuZ3RoIHx8IDE7XHJcblx0XHRcdFx0aXRlbS5kYXRhc2V0LmNvbHVtbnMgPSBTdHJpbmcoIGNvbHMgKTtcclxuXHJcblx0XHRcdFx0Ly8gU2VsZWN0ICYgbm90aWZ5IHN1YnNjcmliZXJzIChsYXlvdXQvbWluIGd1YXJkcywgZXRjLilcclxuXHRcdFx0XHR0aGlzLmJ1aWxkZXIuc2VsZWN0X2ZpZWxkPy4oIGl0ZW0gKTtcclxuXHRcdFx0XHR0aGlzLmJ1aWxkZXIuYnVzPy5lbWl0Py4oIENvcmUuV1BCQ19CRkJfRXZlbnRzLlNUUlVDVFVSRV9DSEFOR0UsIHsgZWw6IGl0ZW0sIHJlYXNvbjogJ3NlY3Rpb24tbW92ZScgfSApO1xyXG5cdFx0XHRcdHRoaXMuYnVpbGRlci51c2FnZT8udXBkYXRlX3BhbGV0dGVfdWk/LigpO1xyXG5cdFx0XHRcdHJldHVybiB0cnVlOyAvLyBoYW5kbGVkLlxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBQYWxldHRlIC0+IGNhbnZhczogYnVpbGQgYSBicmFuZC1uZXcgc2VjdGlvbiB1c2luZyB0aGUgc2FtZSBwYXRoIGFzIHRoZSBkcm9wZG93bi9tZW51XHJcblx0XHRcdGNvbnN0IHRvICAgPSBldnQudG8/LmNsb3Nlc3Q/LiggJy53cGJjX2JmYl9fY29sdW1uLCAud3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lcicgKSB8fCBldnQudG87XHJcblx0XHRcdGNvbnN0IGNvbHMgPSBwYXJzZUludCggZGF0YT8uY29sdW1ucyB8fCBpdGVtLmRhdGFzZXQuY29sdW1ucyB8fCAxLCAxMCApIHx8IDE7XHJcblxyXG5cdFx0XHQvLyBSZW1vdmUgdGhlIHBhbGV0dGUgY2xvbmUgcGxhY2Vob2xkZXIuXHJcblx0XHRcdGl0ZW0ucGFyZW50Tm9kZSAmJiBpdGVtLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIGl0ZW0gKTtcclxuXHJcblx0XHRcdC8vIENyZWF0ZSB0aGUgcmVhbCBzZWN0aW9uLlxyXG5cdFx0XHR0aGlzLmJ1aWxkZXIucGFnZXNfc2VjdGlvbnMuYWRkX3NlY3Rpb24oIHRvLCBjb2xzICk7XHJcblxyXG5cdFx0XHQvLyBJbnNlcnQgYXQgdGhlIHByZWNpc2UgZHJvcCBpbmRleC5cclxuXHRcdFx0Y29uc3Qgc2VjdGlvbiA9IHRvLmxhc3RFbGVtZW50Q2hpbGQ7IC8vIGFkZF9zZWN0aW9uIGFwcGVuZHMgdG8gZW5kLlxyXG5cdFx0XHRpZiAoIGV2dC5uZXdJbmRleCAhPSBudWxsICYmIGV2dC5uZXdJbmRleCA8IHRvLmNoaWxkcmVuLmxlbmd0aCAtIDEgKSB7XHJcblx0XHRcdFx0Y29uc3QgcmVmID0gdG8uY2hpbGRyZW5bZXZ0Lm5ld0luZGV4XSB8fCBudWxsO1xyXG5cdFx0XHRcdHRvLmluc2VydEJlZm9yZSggc2VjdGlvbiwgcmVmICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIEZpbmFsaXplOiBvdmVybGF5LCBzZWxlY3Rpb24sIGV2ZW50cywgdXNhZ2UgcmVmcmVzaC5cclxuXHRcdFx0dGhpcy5idWlsZGVyLmFkZF9vdmVybGF5X3Rvb2xiYXI/Liggc2VjdGlvbiApO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXIuc2VsZWN0X2ZpZWxkPy4oIHNlY3Rpb24gKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmJ1cz8uZW1pdD8uKCBDb3JlLldQQkNfQkZCX0V2ZW50cy5GSUVMRF9BREQsIHtcclxuXHRcdFx0XHRlbCA6IHNlY3Rpb24sXHJcblx0XHRcdFx0aWQgOiBzZWN0aW9uLmRhdGFzZXQuaWQsXHJcblx0XHRcdFx0dWlkOiBzZWN0aW9uLmRhdGFzZXQudWlkXHJcblx0XHRcdH0gKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLnVzYWdlPy51cGRhdGVfcGFsZXR0ZV91aT8uKCk7XHJcblxyXG5cdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIERlc3Ryb3kgYWxsIFNvcnRhYmxlIGluc3RhbmNlcyBjcmVhdGVkIGJ5IHRoaXMgbWFuYWdlci5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0ZGVzdHJveUFsbCgpIHtcclxuXHRcdFx0dGhpcy5fY29udGFpbmVycy5mb3JFYWNoKCAoIGVsICkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGluc3QgPSBTb3J0YWJsZS5nZXQ/LiggZWwgKTtcclxuXHRcdFx0XHRpZiAoIGluc3QgKSB7XHJcblx0XHRcdFx0XHRpbnN0LmRlc3Ryb3koKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH0gKTtcclxuXHRcdFx0dGhpcy5fY29udGFpbmVycy5jbGVhcigpO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdCAqIFNtYWxsIERPTSBjb250cmFjdCBhbmQgcmVuZGVyZXIgaGVscGVyXHJcblx0ICpcclxuXHQgKiBAdHlwZSB7UmVhZG9ubHk8e1xyXG5cdCAqICAgICAgICAgICAgICAgICAgU0VMRUNUT1JTOiB7cGFnZVBhbmVsOiBzdHJpbmcsIGZpZWxkOiBzdHJpbmcsIHZhbGlkRmllbGQ6IHN0cmluZywgc2VjdGlvbjogc3RyaW5nLCBjb2x1bW46XHJcblx0ICogICAgIHN0cmluZywgcm93OiBzdHJpbmcsIG92ZXJsYXk6IHN0cmluZ30sIENMQVNTRVM6IHtzZWxlY3RlZDogc3RyaW5nfSwgQVRUUjoge2lkOiBzdHJpbmcsIG5hbWU6IHN0cmluZywgaHRtbElkOlxyXG5cdCAqICAgICBzdHJpbmcsIHVzYWdlS2V5OiBzdHJpbmcsIHVpZDogc3RyaW5nfX1cclxuXHQgKiAgICAgICAgPn1cclxuXHQgKi9cclxuXHRDb3JlLldQQkNfQkZCX0RPTSA9IE9iamVjdC5mcmVlemUoIHtcclxuXHRcdFNFTEVDVE9SUzoge1xyXG5cdFx0XHRwYWdlUGFuZWwgOiAnLndwYmNfYmZiX19wYW5lbC0tcHJldmlldycsXHJcblx0XHRcdGZpZWxkICAgICA6ICcud3BiY19iZmJfX2ZpZWxkJyxcclxuXHRcdFx0dmFsaWRGaWVsZDogJy53cGJjX2JmYl9fZmllbGQ6bm90KC5pcy1pbnZhbGlkKScsXHJcblx0XHRcdHNlY3Rpb24gICA6ICcud3BiY19iZmJfX3NlY3Rpb24nLFxyXG5cdFx0XHRjb2x1bW4gICAgOiAnLndwYmNfYmZiX19jb2x1bW4nLFxyXG5cdFx0XHRyb3cgICAgICAgOiAnLndwYmNfYmZiX19yb3cnLFxyXG5cdFx0XHRvdmVybGF5ICAgOiAnLndwYmNfYmZiX19vdmVybGF5LWNvbnRyb2xzJ1xyXG5cdFx0fSxcclxuXHRcdENMQVNTRVMgIDoge1xyXG5cdFx0XHRzZWxlY3RlZDogJ2lzLXNlbGVjdGVkJ1xyXG5cdFx0fSxcclxuXHRcdEFUVFIgICAgIDoge1xyXG5cdFx0XHRpZCAgICAgIDogJ2RhdGEtaWQnLFxyXG5cdFx0XHRuYW1lICAgIDogJ2RhdGEtbmFtZScsXHJcblx0XHRcdGh0bWxJZCAgOiAnZGF0YS1odG1sX2lkJyxcclxuXHRcdFx0dXNhZ2VLZXk6ICdkYXRhLXVzYWdlX2tleScsXHJcblx0XHRcdHVpZCAgICAgOiAnZGF0YS11aWQnXHJcblx0XHR9XHJcblx0fSApO1xyXG5cclxuXHRDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlciA9IGNsYXNzIHtcclxuXHJcblx0XHQvKipcclxuXHRcdCAqIENyZWF0ZSBhbiBIVE1MIGVsZW1lbnQuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHRhZyAtIEhUTUwgdGFnIG5hbWUuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gW2NsYXNzX25hbWU9JyddIC0gT3B0aW9uYWwgQ1NTIGNsYXNzIG5hbWUuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gW2lubmVyX2h0bWw9JyddIC0gT3B0aW9uYWwgaW5uZXJIVE1MLlxyXG5cdFx0ICogQHJldHVybnMge0hUTUxFbGVtZW50fSBDcmVhdGVkIGVsZW1lbnQuXHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBjcmVhdGVfZWxlbWVudCggdGFnLCBjbGFzc19uYW1lID0gJycsIGlubmVyX2h0bWwgPSAnJyApIHtcclxuXHRcdFx0Y29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCB0YWcgKTtcclxuXHRcdFx0aWYgKCBjbGFzc19uYW1lICkge1xyXG5cdFx0XHRcdGVsLmNsYXNzTmFtZSA9IGNsYXNzX25hbWU7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBpbm5lcl9odG1sICkge1xyXG5cdFx0XHRcdGVsLmlubmVySFRNTCA9IGlubmVyX2h0bWw7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIGVsO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU2V0IG11bHRpcGxlIGBkYXRhLSpgIGF0dHJpYnV0ZXMgb24gYSBnaXZlbiBlbGVtZW50LlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsIC0gVGFyZ2V0IGVsZW1lbnQuXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gZGF0YV9vYmogLSBLZXktdmFsdWUgcGFpcnMgZm9yIGRhdGEgYXR0cmlidXRlcy5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgc2V0X2RhdGFfYXR0cmlidXRlcyggZWwsIGRhdGFfb2JqICkge1xyXG5cdFx0XHRPYmplY3QuZW50cmllcyggZGF0YV9vYmogKS5mb3JFYWNoKCAoIFsga2V5LCB2YWwgXSApID0+IHtcclxuXHRcdFx0XHQvLyBQcmV2aW91c2x5OiAyMDI1LTA5LTAxIDE3OjA5OlxyXG5cdFx0XHRcdC8vIGNvbnN0IHZhbHVlID0gKHR5cGVvZiB2YWwgPT09ICdvYmplY3QnKSA/IEpTT04uc3RyaW5naWZ5KCB2YWwgKSA6IHZhbDtcclxuXHRcdFx0XHQvL05ldzpcclxuXHRcdFx0XHRsZXQgdmFsdWU7XHJcblx0XHRcdFx0aWYgKCB0eXBlb2YgdmFsID09PSAnb2JqZWN0JyAmJiB2YWwgIT09IG51bGwgKSB7XHJcblx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHR2YWx1ZSA9IEpTT04uc3RyaW5naWZ5KCB2YWwgKTtcclxuXHRcdFx0XHRcdH0gY2F0Y2gge1xyXG5cdFx0XHRcdFx0XHR2YWx1ZSA9ICcnO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHR2YWx1ZSA9IHZhbDtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGVsLnNldEF0dHJpYnV0ZSggJ2RhdGEtJyArIGtleSwgdmFsdWUgKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogR2V0IGFsbCBgZGF0YS0qYCBhdHRyaWJ1dGVzIGZyb20gYW4gZWxlbWVudCBhbmQgcGFyc2UgSlNPTiB3aGVyZSBwb3NzaWJsZS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbCAtIEVsZW1lbnQgdG8gZXh0cmFjdCBkYXRhIGZyb20uXHJcblx0XHQgKiBAcmV0dXJucyB7T2JqZWN0fSBQYXJzZWQga2V5LXZhbHVlIG1hcCBvZiBkYXRhIGF0dHJpYnV0ZXMuXHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBnZXRfYWxsX2RhdGFfYXR0cmlidXRlcyggZWwgKSB7XHJcblx0XHRcdGNvbnN0IGRhdGEgPSB7fTtcclxuXHJcblx0XHRcdGlmICggISBlbCB8fCAhIGVsLmF0dHJpYnV0ZXMgKSB7XHJcblx0XHRcdFx0cmV0dXJuIGRhdGE7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdEFycmF5LmZyb20oIGVsLmF0dHJpYnV0ZXMgKS5mb3JFYWNoKFxyXG5cdFx0XHRcdCggYXR0ciApID0+IHtcclxuXHRcdFx0XHRcdGlmICggYXR0ci5uYW1lLnN0YXJ0c1dpdGgoICdkYXRhLScgKSApIHtcclxuXHRcdFx0XHRcdFx0Y29uc3Qga2V5ID0gYXR0ci5uYW1lLnJlcGxhY2UoIC9eZGF0YS0vLCAnJyApO1xyXG5cdFx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHRcdGRhdGFba2V5XSA9IEpTT04ucGFyc2UoIGF0dHIudmFsdWUgKTtcclxuXHRcdFx0XHRcdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdFx0XHRcdFx0ZGF0YVtrZXldID0gYXR0ci52YWx1ZTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0KTtcclxuXHJcblx0XHRcdC8vIE9ubHkgZGVmYXVsdCB0aGUgbGFiZWwgaWYgaXQncyB0cnVseSBhYnNlbnQgKHVuZGVmaW5lZC9udWxsKSwgbm90IHdoZW4gaXQncyBhbiBlbXB0eSBzdHJpbmcuXHJcblx0XHRcdGNvbnN0IGhhc0V4cGxpY2l0TGFiZWwgPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoIGRhdGEsICdsYWJlbCcgKTtcclxuXHRcdFx0aWYgKCAhIGhhc0V4cGxpY2l0TGFiZWwgJiYgZGF0YS5pZCApIHtcclxuXHRcdFx0XHRkYXRhLmxhYmVsID0gZGF0YS5pZC5jaGFyQXQoIDAgKS50b1VwcGVyQ2FzZSgpICsgZGF0YS5pZC5zbGljZSggMSApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRyZXR1cm4gZGF0YTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFJlbmRlciBhIHNpbXBsZSBsYWJlbCArIHR5cGUgcHJldmlldyAodXNlZCBmb3IgdW5rbm93biBvciBmYWxsYmFjayBmaWVsZHMpLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7T2JqZWN0fSBmaWVsZF9kYXRhIC0gRmllbGQgZGF0YSBvYmplY3QuXHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfSBIVE1MIGNvbnRlbnQuXHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyByZW5kZXJfZmllbGRfaW5uZXJfaHRtbCggZmllbGRfZGF0YSApIHtcclxuXHRcdFx0Ly8gTWFrZSB0aGUgZmFsbGJhY2sgcHJldmlldyByZXNwZWN0IGFuIGVtcHR5IGxhYmVsLlxyXG5cdFx0XHRjb25zdCBoYXNMYWJlbCA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggZmllbGRfZGF0YSwgJ2xhYmVsJyApO1xyXG5cdFx0XHRjb25zdCBsYWJlbCAgICA9IGhhc0xhYmVsID8gU3RyaW5nKCBmaWVsZF9kYXRhLmxhYmVsICkgOiBTdHJpbmcoIGZpZWxkX2RhdGEuaWQgfHwgJyhubyBsYWJlbCknICk7XHJcblxyXG5cdFx0XHRjb25zdCB0eXBlICAgICAgICA9IFN0cmluZyggZmllbGRfZGF0YS50eXBlIHx8ICd1bmtub3duJyApO1xyXG5cdFx0XHRjb25zdCBpc19yZXF1aXJlZCA9IGZpZWxkX2RhdGEucmVxdWlyZWQgPT09IHRydWUgfHwgZmllbGRfZGF0YS5yZXF1aXJlZCA9PT0gJ3RydWUnIHx8IGZpZWxkX2RhdGEucmVxdWlyZWQgPT09IDEgfHwgZmllbGRfZGF0YS5yZXF1aXJlZCA9PT0gJzEnO1xyXG5cclxuXHRcdFx0Y29uc3Qgd3JhcHBlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XHJcblxyXG5cdFx0XHRjb25zdCBzcGFuTGFiZWwgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnc3BhbicgKTtcclxuXHRcdFx0c3BhbkxhYmVsLmNsYXNzTmFtZSAgID0gJ3dwYmNfYmZiX19maWVsZC1sYWJlbCc7XHJcblx0XHRcdHNwYW5MYWJlbC50ZXh0Q29udGVudCA9IGxhYmVsICsgKGlzX3JlcXVpcmVkID8gJyAqJyA6ICcnKTtcclxuXHRcdFx0d3JhcHBlci5hcHBlbmRDaGlsZCggc3BhbkxhYmVsICk7XHJcblxyXG5cdFx0XHRjb25zdCBzcGFuVHlwZSAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdzcGFuJyApO1xyXG5cdFx0XHRzcGFuVHlwZS5jbGFzc05hbWUgICA9ICd3cGJjX2JmYl9fZmllbGQtdHlwZSc7XHJcblx0XHRcdHNwYW5UeXBlLnRleHRDb250ZW50ID0gdHlwZTtcclxuXHRcdFx0d3JhcHBlci5hcHBlbmRDaGlsZCggc3BhblR5cGUgKTtcclxuXHJcblx0XHRcdHJldHVybiB3cmFwcGVyLmlubmVySFRNTDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIERlYm91bmNlIGEgZnVuY3Rpb24uXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBGdW5jdGlvbiB0byBkZWJvdW5jZS5cclxuXHRcdCAqIEBwYXJhbSB7bnVtYmVyfSB3YWl0IC0gRGVsYXkgaW4gbXMuXHJcblx0XHQgKiBAcmV0dXJucyB7RnVuY3Rpb259IERlYm91bmNlZCBmdW5jdGlvbi5cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIGRlYm91bmNlKCBmbiwgd2FpdCA9IDEyMCApIHtcclxuXHRcdFx0bGV0IHQgPSBudWxsO1xyXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gZGVib3VuY2VkKCAuLi5hcmdzICkge1xyXG5cdFx0XHRcdGlmICggdCApIHtcclxuXHRcdFx0XHRcdGNsZWFyVGltZW91dCggdCApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHR0ID0gc2V0VGltZW91dCggKCkgPT4gZm4uYXBwbHkoIHRoaXMsIGFyZ3MgKSwgd2FpdCApO1xyXG5cdFx0XHR9O1xyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHQvLyBSZW5kZXJlciByZWdpc3RyeS4gQWxsb3dzIGxhdGUgcmVnaXN0cmF0aW9uIGFuZCBhdm9pZHMgdGlnaHQgY291cGxpbmcgdG8gYSBnbG9iYWwgbWFwLlxyXG5cdENvcmUuV1BCQ19CRkJfRmllbGRfUmVuZGVyZXJfUmVnaXN0cnkgPSAoZnVuY3Rpb24gKCkge1xyXG5cdFx0Y29uc3QgbWFwID0gbmV3IE1hcCgpO1xyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0cmVnaXN0ZXIoIHR5cGUsIENsYXNzUmVmICkge1xyXG5cdFx0XHRcdG1hcC5zZXQoIFN0cmluZyggdHlwZSApLCBDbGFzc1JlZiApO1xyXG5cdFx0XHR9LFxyXG5cdFx0XHRnZXQoIHR5cGUgKSB7XHJcblx0XHRcdFx0cmV0dXJuIG1hcC5nZXQoIFN0cmluZyggdHlwZSApICk7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0fSkoKTtcclxuXHJcbn0oIHdpbmRvdyApKTsiLCIvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuLy8gPT0gRmlsZSAgL2luY2x1ZGVzL3BhZ2UtZm9ybS1idWlsZGVyL19vdXQvY29yZS9iZmItZmllbGRzLmpzID09IHwgMjAyNS0wOS0xMCAxNTo0N1xyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuKGZ1bmN0aW9uICggdyApIHtcclxuXHQndXNlIHN0cmljdCc7XHJcblxyXG5cdC8vIFNpbmdsZSBnbG9iYWwgbmFtZXNwYWNlIChpZGVtcG90ZW50ICYgbG9hZC1vcmRlciBzYWZlKS5cclxuXHRjb25zdCBDb3JlID0gKCB3LldQQkNfQkZCX0NvcmUgPSB3LldQQkNfQkZCX0NvcmUgfHwge30gKTtcclxuXHRjb25zdCBVSSAgID0gKCBDb3JlLlVJID0gQ29yZS5VSSB8fCB7fSApO1xyXG5cclxuXHQvKipcclxuXHQgKiBCYXNlIGNsYXNzIGZvciBmaWVsZCByZW5kZXJlcnMgKHN0YXRpYy1vbmx5IGNvbnRyYWN0KS5cclxuXHQgKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblx0ICogQ29udHJhY3QgZXhwb3NlZCB0byB0aGUgYnVpbGRlciAoc3RhdGljIG1ldGhvZHMgb24gdGhlIENMQVNTIGl0c2VsZik6XHJcblx0ICogICAtIHJlbmRlcihlbCwgZGF0YSwgY3R4KSAgICAgICAgICAgICAgLy8gUkVRVUlSRURcclxuXHQgKiAgIC0gb25fZmllbGRfZHJvcChkYXRhLCBlbCwgbWV0YSkgICAgICAvLyBPUFRJT05BTCAoZGVmYXVsdCBwcm92aWRlZClcclxuXHQgKlxyXG5cdCAqIEhlbHBlcnMgZm9yIHN1YmNsYXNzZXM6XHJcblx0ICogICAtIGdldF9kZWZhdWx0cygpICAgICAtPiBwZXItZmllbGQgZGVmYXVsdHMgKE1VU1Qgb3ZlcnJpZGUgaW4gc3ViY2xhc3MgdG8gc2V0IHR5cGUvbGFiZWwpXHJcblx0ICogICAtIG5vcm1hbGl6ZV9kYXRhKGQpICAtPiBzaGFsbG93IG1lcmdlIHdpdGggZGVmYXVsdHNcclxuXHQgKiAgIC0gZ2V0X3RlbXBsYXRlKGlkKSAgIC0+IHBlci1pZCBjYWNoZWQgd3AudGVtcGxhdGUgY29tcGlsZXJcclxuXHQgKlxyXG5cdCAqIFN1YmNsYXNzIHVzYWdlOlxyXG5cdCAqICAgY2xhc3MgV1BCQ19CRkJfRmllbGRfVGV4dCBleHRlbmRzIENvcmUuV1BCQ19CRkJfRmllbGRfQmFzZSB7IHN0YXRpYyBnZXRfZGVmYXVsdHMoKXsgLi4uIH0gfVxyXG5cdCAqICAgV1BCQ19CRkJfRmllbGRfVGV4dC50ZW1wbGF0ZV9pZCA9ICd3cGJjLWJmYi1maWVsZC10ZXh0JztcclxuXHQgKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblx0ICovXHJcblx0Q29yZS5XUEJDX0JGQl9GaWVsZF9CYXNlID0gY2xhc3Mge1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogRGVmYXVsdCBmaWVsZCBkYXRhIChnZW5lcmljIGJhc2VsaW5lKS5cclxuXHRcdCAqIFN1YmNsYXNzZXMgTVVTVCBvdmVycmlkZSB0byBwcm92aWRlIHsgdHlwZSwgbGFiZWwgfSBhcHByb3ByaWF0ZSBmb3IgdGhlIGZpZWxkLlxyXG5cdFx0ICogQHJldHVybnMge09iamVjdH1cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIGdldF9kZWZhdWx0cygpIHtcclxuXHRcdFx0cmV0dXJuIHtcclxuXHRcdFx0XHR0eXBlICAgICAgICA6ICdmaWVsZCcsXHJcblx0XHRcdFx0bGFiZWwgICAgICAgOiAnRmllbGQnLFxyXG5cdFx0XHRcdG5hbWUgICAgICAgIDogJ2ZpZWxkJyxcclxuXHRcdFx0XHRodG1sX2lkICAgICA6ICcnLFxyXG5cdFx0XHRcdHBsYWNlaG9sZGVyIDogJycsXHJcblx0XHRcdFx0cmVxdWlyZWQgICAgOiBmYWxzZSxcclxuXHRcdFx0XHRtaW5sZW5ndGggICA6ICcnLFxyXG5cdFx0XHRcdG1heGxlbmd0aCAgIDogJycsXHJcblx0XHRcdFx0cGF0dGVybiAgICAgOiAnJyxcclxuXHRcdFx0XHRjc3NjbGFzcyAgICA6ICcnLFxyXG5cdFx0XHRcdGhlbHAgICAgICAgIDogJydcclxuXHRcdFx0fTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNoYWxsb3ctbWVyZ2UgaW5jb21pbmcgZGF0YSB3aXRoIGRlZmF1bHRzLlxyXG5cdFx0ICogQHBhcmFtIHtPYmplY3R9IGRhdGFcclxuXHRcdCAqIEByZXR1cm5zIHtPYmplY3R9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBub3JtYWxpemVfZGF0YSggZGF0YSApIHtcclxuXHRcdFx0dmFyIGQgICAgICAgID0gZGF0YSB8fCB7fTtcclxuXHRcdFx0dmFyIGRlZmF1bHRzID0gdGhpcy5nZXRfZGVmYXVsdHMoKTtcclxuXHRcdFx0dmFyIG91dCAgICAgID0ge307XHJcblx0XHRcdHZhciBrO1xyXG5cclxuXHRcdFx0Zm9yICggayBpbiBkZWZhdWx0cyApIHtcclxuXHRcdFx0XHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggZGVmYXVsdHMsIGsgKSApIHtcclxuXHRcdFx0XHRcdG91dFtrXSA9IGRlZmF1bHRzW2tdO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRmb3IgKCBrIGluIGQgKSB7XHJcblx0XHRcdFx0aWYgKCBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoIGQsIGsgKSApIHtcclxuXHRcdFx0XHRcdG91dFtrXSA9IGRba107XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBvdXQ7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDb21waWxlIGFuZCBjYWNoZSBhIHdwLnRlbXBsYXRlIGJ5IGlkIChwZXItaWQgY2FjaGUpLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHRlbXBsYXRlX2lkXHJcblx0XHQgKiBAcmV0dXJucyB7RnVuY3Rpb258bnVsbH1cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIGdldF90ZW1wbGF0ZSh0ZW1wbGF0ZV9pZCkge1xyXG5cclxuXHRcdFx0Ly8gQWNjZXB0IGVpdGhlciBcIndwYmMtYmZiLWZpZWxkLXRleHRcIiBvciBcInRtcGwtd3BiYy1iZmItZmllbGQtdGV4dFwiLlxyXG5cdFx0XHRpZiAoICEgdGVtcGxhdGVfaWQgfHwgISB3aW5kb3cud3AgfHwgISB3cC50ZW1wbGF0ZSApIHtcclxuXHRcdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBkb21JZCA9IHRlbXBsYXRlX2lkLnN0YXJ0c1dpdGgoICd0bXBsLScgKSA/IHRlbXBsYXRlX2lkIDogKCd0bXBsLScgKyB0ZW1wbGF0ZV9pZCk7XHJcblx0XHRcdGlmICggISBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggZG9tSWQgKSApIHtcclxuXHRcdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCAhIENvcmUuX19iZmJfdHBsX2NhY2hlX21hcCApIHtcclxuXHRcdFx0XHRDb3JlLl9fYmZiX3RwbF9jYWNoZV9tYXAgPSB7fTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTm9ybWFsaXplIGlkIGZvciB0aGUgY29tcGlsZXIgJiBjYWNoZS4gLy8gd3AudGVtcGxhdGUgZXhwZWN0cyBpZCBXSVRIT1VUIHRoZSBcInRtcGwtXCIgcHJlZml4ICFcclxuXHRcdFx0Y29uc3Qga2V5ID0gdGVtcGxhdGVfaWQucmVwbGFjZSggL150bXBsLS8sICcnICk7XHJcblx0XHRcdGlmICggQ29yZS5fX2JmYl90cGxfY2FjaGVfbWFwW2tleV0gKSB7XHJcblx0XHRcdFx0cmV0dXJuIENvcmUuX19iZmJfdHBsX2NhY2hlX21hcFtrZXldO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb25zdCBjb21waWxlciA9IHdwLnRlbXBsYXRlKCBrZXkgKTsgICAgIC8vIDwtLSBub3JtYWxpemVkIGlkIGhlcmVcclxuXHRcdFx0aWYgKCBjb21waWxlciApIHtcclxuXHRcdFx0XHRDb3JlLl9fYmZiX3RwbF9jYWNoZV9tYXBba2V5XSA9IGNvbXBpbGVyO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRyZXR1cm4gY29tcGlsZXI7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBSRVFVSVJFRDogcmVuZGVyIHByZXZpZXcgaW50byBob3N0IGVsZW1lbnQgKGZ1bGwgcmVkcmF3OyBpZGVtcG90ZW50KS5cclxuXHRcdCAqIFN1YmNsYXNzZXMgc2hvdWxkIHNldCBzdGF0aWMgYHRlbXBsYXRlX2lkYCB0byBhIHZhbGlkIHdwLnRlbXBsYXRlIGlkLlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWxcclxuXHRcdCAqIEBwYXJhbSB7T2JqZWN0fSAgICAgIGRhdGFcclxuXHRcdCAqIEBwYXJhbSB7e21vZGU/OnN0cmluZyxidWlsZGVyPzphbnksdHBsPzpGdW5jdGlvbixzYW5pdD86YW55fX0gY3R4XHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHJlbmRlciggZWwsIGRhdGEsIGN0eCApIHtcclxuXHRcdFx0aWYgKCAhIGVsICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dmFyIGNvbXBpbGUgPSB0aGlzLmdldF90ZW1wbGF0ZSggdGhpcy50ZW1wbGF0ZV9pZCApO1xyXG5cdFx0XHR2YXIgZCAgICAgICA9IHRoaXMubm9ybWFsaXplX2RhdGEoIGRhdGEgKTtcclxuXHJcblx0XHRcdHZhciBzID0gKGN0eCAmJiBjdHguc2FuaXQpID8gY3R4LnNhbml0IDogQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZTtcclxuXHJcblx0XHRcdC8vIFNhbml0aXplIGNyaXRpY2FsIGF0dHJpYnV0ZXMgYmVmb3JlIHRlbXBsYXRpbmcuXHJcblx0XHRcdGlmICggcyApIHtcclxuXHRcdFx0XHRkLmh0bWxfaWQgPSBkLmh0bWxfaWQgPyBzLnNhbml0aXplX2h0bWxfaWQoIFN0cmluZyggZC5odG1sX2lkICkgKSA6ICcnO1xyXG5cdFx0XHRcdGQubmFtZSAgICA9IHMuc2FuaXRpemVfaHRtbF9uYW1lKCBTdHJpbmcoIGQubmFtZSB8fCBkLmlkIHx8ICdmaWVsZCcgKSApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGQuaHRtbF9pZCA9IGQuaHRtbF9pZCA/IFN0cmluZyggZC5odG1sX2lkICkgOiAnJztcclxuXHRcdFx0XHRkLm5hbWUgICAgPSBTdHJpbmcoIGQubmFtZSB8fCBkLmlkIHx8ICdmaWVsZCcgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gRmFsbCBiYWNrIHRvIGdlbmVyaWMgcHJldmlldyBpZiB0ZW1wbGF0ZSBub3QgYXZhaWxhYmxlLlxyXG5cdFx0XHRpZiAoIGNvbXBpbGUgKSB7XHJcblx0XHRcdFx0ZWwuaW5uZXJIVE1MID0gY29tcGlsZSggZCApO1xyXG5cclxuXHRcdFx0XHQvLyBBZnRlciByZW5kZXIsIHNldCBhdHRyaWJ1dGUgdmFsdWVzIHZpYSBET00gc28gcXVvdGVzL25ld2xpbmVzIGFyZSBoYW5kbGVkIGNvcnJlY3RseS5cclxuXHRcdFx0XHRjb25zdCBpbnB1dCA9IGVsLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dCwgdGV4dGFyZWEsIHNlbGVjdCcgKTtcclxuXHRcdFx0XHRpZiAoIGlucHV0ICkge1xyXG5cdFx0XHRcdFx0aWYgKCBkLnBsYWNlaG9sZGVyICE9IG51bGwgKSBpbnB1dC5zZXRBdHRyaWJ1dGUoICdwbGFjZWhvbGRlcicsIFN0cmluZyggZC5wbGFjZWhvbGRlciApICk7XHJcblx0XHRcdFx0XHRpZiAoIGQudGl0bGUgIT0gbnVsbCApIGlucHV0LnNldEF0dHJpYnV0ZSggJ3RpdGxlJywgU3RyaW5nKCBkLnRpdGxlICkgKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGVsLmlubmVySFRNTCA9IENvcmUuV1BCQ19Gb3JtX0J1aWxkZXJfSGVscGVyLnJlbmRlcl9maWVsZF9pbm5lcl9odG1sKCBkICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGVsLmRhdGFzZXQudHlwZSA9IGQudHlwZSB8fCAnZmllbGQnO1xyXG5cdFx0XHRlbC5zZXRBdHRyaWJ1dGUoICdkYXRhLWxhYmVsJywgKGQubGFiZWwgIT0gbnVsbCA/IFN0cmluZyggZC5sYWJlbCApIDogJycpICk7IC8vIGFsbG93IFwiXCIuXHJcblx0XHR9XHJcblxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogT1BUSU9OQUwgaG9vayBleGVjdXRlZCBhZnRlciBmaWVsZCBpcyBkcm9wcGVkL2xvYWRlZC9wcmV2aWV3LlxyXG5cdFx0ICogRGVmYXVsdCBleHRlbmRlZDpcclxuXHRcdCAqIC0gT24gZmlyc3QgZHJvcDogc3RhbXAgZGVmYXVsdCBsYWJlbCAoZXhpc3RpbmcgYmVoYXZpb3IpIGFuZCBtYXJrIGZpZWxkIGFzIFwiZnJlc2hcIiBmb3IgYXV0by1uYW1lLlxyXG5cdFx0ICogLSBPbiBsb2FkOiBtYXJrIGFzIGxvYWRlZCBzbyBsYXRlciBsYWJlbCBlZGl0cyBkbyBub3QgcmVuYW1lIHRoZSBzYXZlZCBuYW1lLlxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgb25fZmllbGRfZHJvcChkYXRhLCBlbCwgbWV0YSkge1xyXG5cclxuXHRcdFx0Y29uc3QgY29udGV4dCA9IChtZXRhICYmIG1ldGEuY29udGV4dCkgPyBTdHJpbmcoIG1ldGEuY29udGV4dCApIDogJyc7XHJcblxyXG5cdFx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdFx0XHQvLyBORVc6IFNlZWQgZGVmYXVsdCBcImhlbHBcIiAoYW5kIGtlZXAgaXQgaW4gU3RydWN0dXJlKSBmb3IgYWxsIGZpZWxkIHBhY2tzIHRoYXQgZGVmaW5lIGl0LlxyXG5cdFx0XHQvLyBUaGlzIGZpeGVzIHRoZSBtaXNtYXRjaCB3aGVyZTpcclxuXHRcdFx0Ly8gICAtIFVJIHNob3dzIGRlZmF1bHQgaGVscCB2aWEgbm9ybWFsaXplX2RhdGEoKSAvIHRlbXBsYXRlc1xyXG5cdFx0XHQvLyAgIC0gYnV0IGdldF9zdHJ1Y3R1cmUoKSAvIGV4cG9ydGVycyBzZWUgYGhlbHBgIGFzIHVuZGVmaW5lZC9lbXB0eS5cclxuXHRcdFx0Ly9cclxuXHRcdFx0Ly8gQmVoYXZpb3I6XHJcblx0XHRcdC8vICAgLSBSdW5zIE9OTFkgb24gaW5pdGlhbCBkcm9wIChjb250ZXh0ID09PSAnZHJvcCcpLlxyXG5cdFx0XHQvLyAgIC0gSWYgZ2V0X2RlZmF1bHRzKCkgZXhwb3NlcyBhIG5vbi1lbXB0eSBcImhlbHBcIiwgYW5kIGRhdGEuaGVscCBpc1xyXG5cdFx0XHQvLyAgICAgbWlzc2luZyAvIG51bGwgLyBlbXB0eSBzdHJpbmcgLT4gd2UgcGVyc2lzdCB0aGUgZGVmYXVsdCBpbnRvIGBkYXRhYFxyXG5cdFx0XHQvLyAgICAgYW5kIG5vdGlmeSBTdHJ1Y3R1cmUgc28gZXhwb3J0cyBzZWUgaXQuXHJcblx0XHRcdC8vICAgLSBPbiBcImxvYWRcIiB3ZSBkbyBub3RoaW5nLCBzbyBleGlzdGluZyBmb3JtcyB3aGVyZSB1c2VyICpjbGVhcmVkKlxyXG5cdFx0XHQvLyAgICAgaGVscCB3aWxsIG5vdCBiZSBvdmVycmlkZGVuLlxyXG5cdFx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdFx0XHRpZiAoIGNvbnRleHQgPT09ICdkcm9wJyAmJiBkYXRhICkge1xyXG5cdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRjb25zdCBkZWZzID0gKHR5cGVvZiB0aGlzLmdldF9kZWZhdWx0cyA9PT0gJ2Z1bmN0aW9uJykgPyB0aGlzLmdldF9kZWZhdWx0cygpIDogbnVsbDtcclxuXHRcdFx0XHRcdGlmICggZGVmcyAmJiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoIGRlZnMsICdoZWxwJyApICkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBjdXJyZW50ICAgID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKCBkYXRhLCAnaGVscCcgKSA/IGRhdGEuaGVscCA6IHVuZGVmaW5lZDtcclxuXHRcdFx0XHRcdFx0Y29uc3QgaGFzVmFsdWUgICA9IChjdXJyZW50ICE9PSB1bmRlZmluZWQgJiYgY3VycmVudCAhPT0gbnVsbCAmJiBTdHJpbmcoIGN1cnJlbnQgKSAhPT0gJycpO1xyXG5cdFx0XHRcdFx0XHRjb25zdCBkZWZhdWx0VmFsID0gZGVmcy5oZWxwO1xyXG5cclxuXHRcdFx0XHRcdFx0aWYgKCAhIGhhc1ZhbHVlICYmIGRlZmF1bHRWYWwgIT0gbnVsbCAmJiBTdHJpbmcoIGRlZmF1bHRWYWwgKSAhPT0gJycgKSB7XHJcblx0XHRcdFx0XHRcdFx0Ly8gMSkgcGVyc2lzdCBpbnRvIGRhdGEgb2JqZWN0ICh1c2VkIGJ5IFN0cnVjdHVyZSkuXHJcblx0XHRcdFx0XHRcdFx0ZGF0YS5oZWxwID0gZGVmYXVsdFZhbDtcclxuXHJcblx0XHRcdFx0XHRcdFx0Ly8gMikgbWlycm9yIGludG8gZGF0YXNldCAoZm9yIGFueSBET00tYmFzZWQgY29uc3VtZXJzKS5cclxuXHRcdFx0XHRcdFx0XHRpZiAoIGVsICkge1xyXG5cdFx0XHRcdFx0XHRcdFx0ZWwuZGF0YXNldC5oZWxwID0gU3RyaW5nKCBkZWZhdWx0VmFsICk7XHJcblxyXG5cdFx0XHRcdFx0XHRcdFx0Ly8gMykgbm90aWZ5IFN0cnVjdHVyZSAvIGxpc3RlbmVycyAoaWYgYXZhaWxhYmxlKS5cclxuXHRcdFx0XHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdFx0XHRcdENvcmUuU3RydWN0dXJlPy51cGRhdGVfZmllbGRfcHJvcD8uKCBlbCwgJ2hlbHAnLCBkZWZhdWx0VmFsICk7XHJcblx0XHRcdFx0XHRcdFx0XHRcdGVsLmRpc3BhdGNoRXZlbnQoXHJcblx0XHRcdFx0XHRcdFx0XHRcdFx0bmV3IEN1c3RvbUV2ZW50KCAnd3BiY19iZmJfZmllbGRfZGF0YV9jaGFuZ2VkJywgeyBidWJibGVzOiB0cnVlLCBkZXRhaWwgOiB7IGtleTogJ2hlbHAnLCB2YWx1ZTogZGVmYXVsdFZhbCB9IH0gKVxyXG5cdFx0XHRcdFx0XHRcdFx0XHQpO1xyXG5cdFx0XHRcdFx0XHRcdFx0fSBjYXRjaCAoIF9pbm5lciApIHt9XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBjYXRjaCAoIF9lICkge31cclxuXHRcdFx0fVxyXG5cdFx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuXHRcdFx0aWYgKCBjb250ZXh0ID09PSAnZHJvcCcgJiYgIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggZGF0YSwgJ2xhYmVsJyApICkge1xyXG5cdFx0XHRcdGNvbnN0IGRlZnMgPSB0aGlzLmdldF9kZWZhdWx0cygpO1xyXG5cdFx0XHRcdGRhdGEubGFiZWwgPSBkZWZzLmxhYmVsIHx8ICdGaWVsZCc7XHJcblx0XHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1sYWJlbCcsIGRhdGEubGFiZWwgKTtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBNYXJrIHByb3ZlbmFuY2UgZmxhZ3MuXHJcblx0XHRcdGlmICggY29udGV4dCA9PT0gJ2Ryb3AnICkge1xyXG5cdFx0XHRcdGVsLmRhdGFzZXQuZnJlc2ggICAgICA9ICcxJzsgICAvLyBjYW4gYXV0by1uYW1lIG9uIGZpcnN0IGxhYmVsIGVkaXQuXHJcblx0XHRcdFx0ZWwuZGF0YXNldC5hdXRvbmFtZSAgID0gJzEnO1xyXG5cdFx0XHRcdGVsLmRhdGFzZXQud2FzX2xvYWRlZCA9ICcwJztcclxuXHRcdFx0XHQvLyBTZWVkIGEgcHJvdmlzaW9uYWwgdW5pcXVlIG5hbWUgaW1tZWRpYXRlbHkuXHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGNvbnN0IGIgPSBtZXRhPy5idWlsZGVyO1xyXG5cdFx0XHRcdFx0aWYgKCBiPy5pZCAmJiAoIWVsLmhhc0F0dHJpYnV0ZSggJ2RhdGEtbmFtZScgKSB8fCAhZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJyApKSApIHtcclxuXHRcdFx0XHRcdFx0Y29uc3QgUyAgICA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemU7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGJhc2UgPSBTLnNhbml0aXplX2h0bWxfbmFtZSggZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1pZCcgKSB8fCBkYXRhPy5pZCB8fCBkYXRhPy50eXBlIHx8ICdmaWVsZCcgKTtcclxuXHRcdFx0XHRcdFx0Y29uc3QgdW5pcSA9IGIuaWQuZW5zdXJlX3VuaXF1ZV9maWVsZF9uYW1lKCBiYXNlLCBlbCApO1xyXG5cdFx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoICdkYXRhLW5hbWUnLCB1bmlxICk7XHJcblx0XHRcdFx0XHRcdGVsLmRhdGFzZXQubmFtZV91c2VyX3RvdWNoZWQgPSAnMCc7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7fVxyXG5cclxuXHRcdFx0fSBlbHNlIGlmICggY29udGV4dCA9PT0gJ2xvYWQnICkge1xyXG5cdFx0XHRcdGVsLmRhdGFzZXQuZnJlc2ggICAgICA9ICcwJztcclxuXHRcdFx0XHRlbC5kYXRhc2V0LmF1dG9uYW1lICAgPSAnMCc7XHJcblx0XHRcdFx0ZWwuZGF0YXNldC53YXNfbG9hZGVkID0gJzEnOyAgIC8vIG5ldmVyIHJlbmFtZSBuYW1lcyBmb3IgbG9hZGVkIGZpZWxkcy5cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIC0tLSBBdXRvIFJlbmFtZSBcIkZyZXNoXCIgZmllbGQsICBvbiBlbnRlcmluZyB0aGUgbmV3IExhYmVsIC0tLVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ3JlYXRlIGEgY29uc2VydmF0aXZlIGZpZWxkIFwibmFtZVwiIGZyb20gYSBodW1hbiBsYWJlbC5cclxuXHRcdCAqIFVzZXMgdGhlIHNhbWUgY29uc3RyYWludHMgYXMgc2FuaXRpemVfaHRtbF9uYW1lIChsZXR0ZXJzL2RpZ2l0cy9fLSBhbmQgbGVhZGluZyBsZXR0ZXIpLlxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgbmFtZV9mcm9tX2xhYmVsKGxhYmVsKSB7XHJcblx0XHRcdGNvbnN0IHMgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfbmFtZSggU3RyaW5nKCBsYWJlbCA/PyAnJyApICk7XHJcblx0XHRcdHJldHVybiBzLnRvTG93ZXJDYXNlKCkgfHwgJ2ZpZWxkJztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEF1dG8tZmlsbCBkYXRhLW5hbWUgZnJvbSBsYWJlbCBPTkxZIGZvciBmcmVzaGx5IGRyb3BwZWQgZmllbGRzIHRoYXQgd2VyZSBub3QgZWRpdGVkIHlldC5cclxuXHRcdCAqIC0gTmV2ZXIgcnVucyBmb3Igc2VjdGlvbnMuXHJcblx0XHQgKiAtIE5ldmVyIHJ1bnMgZm9yIGxvYWRlZC9leGlzdGluZyBmaWVsZHMuXHJcblx0XHQgKiAtIFN0b3BzIGFzIHNvb24gYXMgdXNlciBlZGl0cyB0aGUgTmFtZSBtYW51YWxseS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge1dQQkNfRm9ybV9CdWlsZGVyfSBidWlsZGVyXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbCAgLSAud3BiY19iZmJfX2ZpZWxkIGVsZW1lbnRcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBsYWJlbFZhbFxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgbWF5YmVfYXV0b25hbWVfZnJvbV9sYWJlbChidWlsZGVyLCBlbCwgbGFiZWxWYWwpIHtcclxuXHRcdFx0aWYgKCAhYnVpbGRlciB8fCAhZWwgKSByZXR1cm47XHJcblx0XHRcdGlmICggZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBhbGxvd0F1dG8gPSBlbC5kYXRhc2V0LmF1dG9uYW1lID09PSAnMSc7XHJcblxyXG5cdFx0XHRjb25zdCB1c2VyVG91Y2hlZCA9IGVsLmRhdGFzZXQubmFtZV91c2VyX3RvdWNoZWQgPT09ICcxJztcclxuXHRcdFx0Y29uc3QgaXNMb2FkZWQgICAgPSBlbC5kYXRhc2V0Lndhc19sb2FkZWQgPT09ICcxJztcclxuXHJcblx0XHRcdGlmICggIWFsbG93QXV0byB8fCB1c2VyVG91Y2hlZCB8fCBpc0xvYWRlZCApIHJldHVybjtcclxuXHJcblx0XHRcdC8vIE9ubHkgb3ZlcnJpZGUgcGxhY2Vob2xkZXIteSBuYW1lc1xyXG5cdFx0XHRjb25zdCBTID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZTtcclxuXHJcblx0XHRcdGNvbnN0IGJhc2UgICA9IHRoaXMubmFtZV9mcm9tX2xhYmVsKCBsYWJlbFZhbCApO1xyXG5cdFx0XHRjb25zdCB1bmlxdWUgPSBidWlsZGVyLmlkLmVuc3VyZV91bmlxdWVfZmllbGRfbmFtZSggYmFzZSwgZWwgKTtcclxuXHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJywgdW5pcXVlICk7XHJcblxyXG5cdFx0XHRjb25zdCBpbnMgICAgICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKTtcclxuXHRcdFx0Y29uc3QgbmFtZUN0cmwgPSBpbnM/LnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1pbnNwZWN0b3Ita2V5PVwibmFtZVwiXScgKTtcclxuXHRcdFx0aWYgKCBuYW1lQ3RybCAmJiAndmFsdWUnIGluIG5hbWVDdHJsICYmIG5hbWVDdHJsLnZhbHVlICE9PSB1bmlxdWUgKSBuYW1lQ3RybC52YWx1ZSA9IHVuaXF1ZTtcclxuXHRcdH1cclxuXHJcblxyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdCAqIFNlbGVjdF9CYXNlIChzaGFyZWQgYmFzZSBmb3Igc2VsZWN0LWxpa2UgcGFja3MpXHJcblx0ICpcclxuXHQgKiBAdHlwZSB7Q29yZS5XUEJDX0JGQl9TZWxlY3RfQmFzZX1cclxuXHQgKi9cclxuXHRDb3JlLldQQkNfQkZCX1NlbGVjdF9CYXNlID0gY2xhc3MgZXh0ZW5kcyBDb3JlLldQQkNfQkZCX0ZpZWxkX0Jhc2Uge1xyXG5cclxuXHRcdHN0YXRpYyB0ZW1wbGF0ZV9pZCAgICAgICAgICAgID0gbnVsbDsgICAgICAgICAgICAgICAgIC8vIG1haW4gcHJldmlldyB0ZW1wbGF0ZSBpZFxyXG5cdFx0c3RhdGljIG9wdGlvbl9yb3dfdGVtcGxhdGVfaWQgPSAnd3BiYy1iZmItaW5zcGVjdG9yLXNlbGVjdC1vcHRpb24tcm93JzsgLy8gcm93IHRwbCBpZFxyXG5cdFx0c3RhdGljIGtpbmQgICAgICAgICAgICAgICAgICAgPSAnc2VsZWN0JztcclxuXHRcdHN0YXRpYyBfX3Jvb3Rfd2lyZWQgICAgICAgICAgID0gZmFsc2U7XHJcblx0XHRzdGF0aWMgX19yb290X25vZGUgICAgICAgICAgICA9IG51bGw7XHJcblxyXG5cdFx0Ly8gU2luZ2xlIHNvdXJjZSBvZiBzZWxlY3RvcnMgdXNlZCBieSB0aGUgaW5zcGVjdG9yIFVJLlxyXG5cdFx0c3RhdGljIHVpID0ge1xyXG5cdFx0XHRsaXN0ICAgOiAnLndwYmNfYmZiX19vcHRpb25zX2xpc3QnLFxyXG5cdFx0XHRob2xkZXIgOiAnLndwYmNfYmZiX19vcHRpb25zX3N0YXRlW2RhdGEtaW5zcGVjdG9yLWtleT1cIm9wdGlvbnNcIl0nLFxyXG5cdFx0XHRyb3cgICAgOiAnLndwYmNfYmZiX19vcHRpb25zX3JvdycsXHJcblx0XHRcdGxhYmVsICA6ICcud3BiY19iZmJfX29wdC1sYWJlbCcsXHJcblx0XHRcdHZhbHVlICA6ICcud3BiY19iZmJfX29wdC12YWx1ZScsXHJcblx0XHRcdHRvZ2dsZSA6ICcud3BiY19iZmJfX29wdC1zZWxlY3RlZC1jaGsnLFxyXG5cdFx0XHRhZGRfYnRuOiAnLmpzLWFkZC1vcHRpb24nLFxyXG5cclxuXHRcdFx0ZHJhZ19oYW5kbGUgICAgICA6ICcud3BiY19iZmJfX2RyYWctaGFuZGxlJyxcclxuXHRcdFx0bXVsdGlwbGVfY2hrICAgICA6ICcuanMtb3B0LW11bHRpcGxlW2RhdGEtaW5zcGVjdG9yLWtleT1cIm11bHRpcGxlXCJdJyxcclxuXHRcdFx0ZGVmYXVsdF90ZXh0ICAgICA6ICcuanMtZGVmYXVsdC12YWx1ZVtkYXRhLWluc3BlY3Rvci1rZXk9XCJkZWZhdWx0X3ZhbHVlXCJdJyxcclxuXHRcdFx0cGxhY2Vob2xkZXJfaW5wdXQ6ICcuanMtcGxhY2Vob2xkZXJbZGF0YS1pbnNwZWN0b3Ita2V5PVwicGxhY2Vob2xkZXJcIl0nLFxyXG5cdFx0XHRwbGFjZWhvbGRlcl9ub3RlIDogJy5qcy1wbGFjZWhvbGRlci1ub3RlJyxcclxuXHRcdFx0c2l6ZV9pbnB1dCAgICAgICA6ICcuaW5zcGVjdG9yX19pbnB1dFtkYXRhLWluc3BlY3Rvci1rZXk9XCJzaXplXCJdJyxcclxuXHJcblx0XHRcdC8vIERyb3Bkb3duIG1lbnUgaW50ZWdyYXRpb24uXHJcblx0XHRcdG1lbnVfcm9vdCAgOiAnLndwYmNfdWlfZWxfX2Ryb3Bkb3duJyxcclxuXHRcdFx0bWVudV90b2dnbGU6ICdbZGF0YS10b2dnbGU9XCJ3cGJjX2Ryb3Bkb3duXCJdJyxcclxuXHRcdFx0bWVudV9hY3Rpb246ICcudWxfZHJvcGRvd25fbWVudV9saV9hY3Rpb25bZGF0YS1hY3Rpb25dJyxcclxuXHRcdFx0Ly8gVmFsdWUtZGlmZmVycyB0b2dnbGUuXHJcblx0XHRcdHZhbHVlX2RpZmZlcnNfY2hrOiAnLmpzLXZhbHVlLWRpZmZlcnNbZGF0YS1pbnNwZWN0b3Ita2V5PVwidmFsdWVfZGlmZmVyc1wiXScsXHJcblx0XHR9O1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQnVpbGQgb3B0aW9uIHZhbHVlIGZyb20gbGFiZWwuXHJcblx0XHQgKiAtIElmIGBkaWZmZXJzID09PSB0cnVlYCAtPiBnZW5lcmF0ZSB0b2tlbiAoc2x1Zy1saWtlIG1hY2hpbmUgdmFsdWUpLlxyXG5cdFx0ICogLSBJZiBgZGlmZmVycyA9PT0gZmFsc2VgIC0+IGtlZXAgaHVtYW4gdGV4dDsgZXNjYXBlIG9ubHkgZGFuZ2Vyb3VzIGNoYXJzLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGxhYmVsXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IGRpZmZlcnNcclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBidWlsZF92YWx1ZV9mcm9tX2xhYmVsKGxhYmVsLCBkaWZmZXJzKSB7XHJcblx0XHRcdGNvbnN0IFMgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplO1xyXG5cdFx0XHRpZiAoIGRpZmZlcnMgKSB7XHJcblx0XHRcdFx0cmV0dXJuIChTICYmIHR5cGVvZiBTLnRvX3Rva2VuID09PSAnZnVuY3Rpb24nKVxyXG5cdFx0XHRcdFx0PyBTLnRvX3Rva2VuKCBTdHJpbmcoIGxhYmVsIHx8ICcnICkgKVxyXG5cdFx0XHRcdFx0OiBTdHJpbmcoIGxhYmVsIHx8ICcnICkudHJpbSgpLnRvTG93ZXJDYXNlKCkucmVwbGFjZSggL1xccysvZywgJ18nICkucmVwbGFjZSggL1teXFx3LV0vZywgJycgKTtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBzaW5nbGUtaW5wdXQgbW9kZToga2VlcCBodW1hbiB0ZXh0OyB0ZW1wbGF0ZSB3aWxsIGVzY2FwZSBzYWZlbHkuXHJcblx0XHRcdHJldHVybiBTdHJpbmcoIGxhYmVsID09IG51bGwgPyAnJyA6IGxhYmVsICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBJcyB0aGUg4oCcdmFsdWUgZGlmZmVycyBmcm9tIGxhYmVs4oCdIHRvZ2dsZSBlbmFibGVkP1xyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcGFuZWxcclxuXHRcdCAqIEByZXR1cm5zIHtib29sZWFufVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgaXNfdmFsdWVfZGlmZmVyc19lbmFibGVkKHBhbmVsKSB7XHJcblx0XHRcdGNvbnN0IGNoayA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLnZhbHVlX2RpZmZlcnNfY2hrICk7XHJcblx0XHRcdHJldHVybiAhIShjaGsgJiYgY2hrLmNoZWNrZWQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogRW5zdXJlIHZpc2liaWxpdHkvZW5hYmxlZCBzdGF0ZSBvZiBWYWx1ZSBpbnB1dHMgYmFzZWQgb24gdGhlIHRvZ2dsZS5cclxuXHRcdCAqIFdoZW4gZGlzYWJsZWQgLT4gaGlkZSBWYWx1ZSBpbnB1dHMgYW5kIGtlZXAgdGhlbSBtaXJyb3JlZCBmcm9tIExhYmVsLlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcGFuZWxcclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eShwYW5lbCkge1xyXG5cdFx0XHRjb25zdCBkaWZmZXJzID0gdGhpcy5pc192YWx1ZV9kaWZmZXJzX2VuYWJsZWQoIHBhbmVsICk7XHJcblx0XHRcdGNvbnN0IHJvd3MgICAgPSBwYW5lbD8ucXVlcnlTZWxlY3RvckFsbCggdGhpcy51aS5yb3cgKSB8fCBbXTtcclxuXHJcblx0XHRcdGZvciAoIGxldCBpID0gMDsgaSA8IHJvd3MubGVuZ3RoOyBpKysgKSB7XHJcblx0XHRcdFx0Y29uc3QgciAgICAgID0gcm93c1tpXTtcclxuXHRcdFx0XHRjb25zdCBsYmxfaW4gPSByLnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkubGFiZWwgKTtcclxuXHRcdFx0XHRjb25zdCB2YWxfaW4gPSByLnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkudmFsdWUgKTtcclxuXHRcdFx0XHRpZiAoICF2YWxfaW4gKSBjb250aW51ZTtcclxuXHJcblx0XHRcdFx0aWYgKCBkaWZmZXJzICkge1xyXG5cdFx0XHRcdFx0Ly8gUmUtZW5hYmxlICYgc2hvdyB2YWx1ZSBpbnB1dFxyXG5cdFx0XHRcdFx0dmFsX2luLnJlbW92ZUF0dHJpYnV0ZSggJ2Rpc2FibGVkJyApO1xyXG5cdFx0XHRcdFx0dmFsX2luLnN0eWxlLmRpc3BsYXkgPSAnJztcclxuXHJcblx0XHRcdFx0XHQvLyBJZiB3ZSBoYXZlIGEgY2FjaGVkIGN1c3RvbSB2YWx1ZSBhbmQgdGhlIHJvdyB3YXNuJ3QgZWRpdGVkIHdoaWxlIE9GRiwgcmVzdG9yZSBpdFxyXG5cdFx0XHRcdFx0Y29uc3QgaGFzQ2FjaGUgICA9ICEhdmFsX2luLmRhdGFzZXQuY2FjaGVkX3ZhbHVlO1xyXG5cdFx0XHRcdFx0Y29uc3QgdXNlckVkaXRlZCA9IHIuZGF0YXNldC52YWx1ZV91c2VyX3RvdWNoZWQgPT09ICcxJztcclxuXHJcblx0XHRcdFx0XHRpZiAoIGhhc0NhY2hlICYmICF1c2VyRWRpdGVkICkge1xyXG5cdFx0XHRcdFx0XHR2YWxfaW4udmFsdWUgPSB2YWxfaW4uZGF0YXNldC5jYWNoZWRfdmFsdWU7XHJcblx0XHRcdFx0XHR9IGVsc2UgaWYgKCAhaGFzQ2FjaGUgKSB7XHJcblx0XHRcdFx0XHRcdC8vIE5vIGNhY2hlOiBpZiB2YWx1ZSBpcyBqdXN0IGEgbWlycm9yZWQgbGFiZWwsIG9mZmVyIGEgdG9rZW5pemVkIGRlZmF1bHRcclxuXHRcdFx0XHRcdFx0Y29uc3QgbGJsICAgICAgPSBsYmxfaW4gPyBsYmxfaW4udmFsdWUgOiAnJztcclxuXHRcdFx0XHRcdFx0Y29uc3QgbWlycm9yZWQgPSB0aGlzLmJ1aWxkX3ZhbHVlX2Zyb21fbGFiZWwoIGxibCwgLypkaWZmZXJzPSovZmFsc2UgKTtcclxuXHRcdFx0XHRcdFx0aWYgKCB2YWxfaW4udmFsdWUgPT09IG1pcnJvcmVkICkge1xyXG5cdFx0XHRcdFx0XHRcdHZhbF9pbi52YWx1ZSA9IHRoaXMuYnVpbGRfdmFsdWVfZnJvbV9sYWJlbCggbGJsLCAvKmRpZmZlcnM9Ki90cnVlICk7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0Ly8gT04gLT4gT0ZGOiBjYWNoZSBvbmNlLCB0aGVuIG1pcnJvclxyXG5cdFx0XHRcdFx0aWYgKCAhdmFsX2luLmRhdGFzZXQuY2FjaGVkX3ZhbHVlICkge1xyXG5cdFx0XHRcdFx0XHR2YWxfaW4uZGF0YXNldC5jYWNoZWRfdmFsdWUgPSB2YWxfaW4udmFsdWUgfHwgJyc7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRjb25zdCBsYmwgICAgPSBsYmxfaW4gPyBsYmxfaW4udmFsdWUgOiAnJztcclxuXHRcdFx0XHRcdHZhbF9pbi52YWx1ZSA9IHRoaXMuYnVpbGRfdmFsdWVfZnJvbV9sYWJlbCggbGJsLCAvKmRpZmZlcnM9Ki9mYWxzZSApO1xyXG5cclxuXHRcdFx0XHRcdHZhbF9pbi5zZXRBdHRyaWJ1dGUoICdkaXNhYmxlZCcsICdkaXNhYmxlZCcgKTtcclxuXHRcdFx0XHRcdHZhbF9pbi5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG5cdFx0XHRcdFx0Ly8gTk9URTogZG8gTk9UIG1hcmsgYXMgdXNlcl90b3VjaGVkIGhlcmVcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBSZXR1cm4gd2hldGhlciB0aGlzIHJvd+KAmXMgdmFsdWUgaGFzIGJlZW4gZWRpdGVkIGJ5IHVzZXIuXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSByb3dcclxuXHRcdCAqIEByZXR1cm5zIHtib29sZWFufVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgaXNfcm93X3ZhbHVlX3VzZXJfdG91Y2hlZChyb3cpIHtcclxuXHRcdFx0cmV0dXJuIHJvdz8uZGF0YXNldD8udmFsdWVfdXNlcl90b3VjaGVkID09PSAnMSc7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBNYXJrIHRoaXMgcm934oCZcyB2YWx1ZSBhcyBlZGl0ZWQgYnkgdXNlci5cclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvd1xyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgbWFya19yb3dfdmFsdWVfdXNlcl90b3VjaGVkKHJvdykge1xyXG5cdFx0XHRpZiAoIHJvdyApIHJvdy5kYXRhc2V0LnZhbHVlX3VzZXJfdG91Y2hlZCA9ICcxJztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEluaXRpYWxpemUg4oCcZnJlc2huZXNz4oCdIGZsYWdzIG9uIGEgcm93ICh2YWx1ZSB1bnRvdWNoZWQpLlxyXG5cdFx0ICogQ2FsbCBvbiBjcmVhdGlvbi9hcHBlbmQgb2Ygcm93cy5cclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvd1xyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgaW5pdF9yb3dfZnJlc2hfZmxhZ3Mocm93KSB7XHJcblx0XHRcdGlmICggcm93ICkge1xyXG5cdFx0XHRcdGlmICggIXJvdy5kYXRhc2V0LnZhbHVlX3VzZXJfdG91Y2hlZCApIHtcclxuXHRcdFx0XHRcdHJvdy5kYXRhc2V0LnZhbHVlX3VzZXJfdG91Y2hlZCA9ICcwJztcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tIGRlZmF1bHRzIChwYWNrcyBjYW4gb3ZlcnJpZGUpIC0tLS1cclxuXHRcdHN0YXRpYyBnZXRfZGVmYXVsdHMoKSB7XHJcblx0XHRcdHJldHVybiB7XHJcblx0XHRcdFx0dHlwZSAgICAgICAgIDogdGhpcy5raW5kLFxyXG5cdFx0XHRcdGxhYmVsICAgICAgICA6ICdTZWxlY3QnLFxyXG5cdFx0XHRcdG5hbWUgICAgICAgICA6ICcnLFxyXG5cdFx0XHRcdGh0bWxfaWQgICAgICA6ICcnLFxyXG5cdFx0XHRcdHBsYWNlaG9sZGVyICA6ICctLS0gU2VsZWN0IC0tLScsXHJcblx0XHRcdFx0cmVxdWlyZWQgICAgIDogZmFsc2UsXHJcblx0XHRcdFx0bXVsdGlwbGUgICAgIDogZmFsc2UsXHJcblx0XHRcdFx0c2l6ZSAgICAgICAgIDogbnVsbCxcclxuXHRcdFx0XHRjc3NjbGFzcyAgICAgOiAnJyxcclxuXHRcdFx0XHRoZWxwICAgICAgICAgOiAnJyxcclxuXHRcdFx0XHRkZWZhdWx0X3ZhbHVlOiAnJyxcclxuXHRcdFx0XHRvcHRpb25zICAgICAgOiBbXHJcblx0XHRcdFx0XHR7IGxhYmVsOiAnT3B0aW9uIDEnLCB2YWx1ZTogJ09wdGlvbiAxJywgc2VsZWN0ZWQ6IGZhbHNlIH0sXHJcblx0XHRcdFx0XHR7IGxhYmVsOiAnT3B0aW9uIDInLCB2YWx1ZTogJ09wdGlvbiAyJywgc2VsZWN0ZWQ6IGZhbHNlIH0sXHJcblx0XHRcdFx0XHR7IGxhYmVsOiAnT3B0aW9uIDMnLCB2YWx1ZTogJ09wdGlvbiAzJywgc2VsZWN0ZWQ6IGZhbHNlIH0sXHJcblx0XHRcdFx0XHR7IGxhYmVsOiAnT3B0aW9uIDQnLCB2YWx1ZTogJ09wdGlvbiA0Jywgc2VsZWN0ZWQ6IGZhbHNlIH1cclxuXHRcdFx0XHRdLFxyXG5cdFx0XHRcdG1pbl93aWR0aCAgICA6ICcyNDBweCdcclxuXHRcdFx0fTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tIHByZXZpZXcgcmVuZGVyIChpZGVtcG90ZW50KSAtLS0tXHJcblx0XHRzdGF0aWMgcmVuZGVyKGVsLCBkYXRhLCBjdHgpIHtcclxuXHRcdFx0aWYgKCAhZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBkID0gdGhpcy5ub3JtYWxpemVfZGF0YSggZGF0YSApO1xyXG5cclxuXHRcdFx0aWYgKCBkLm1pbl93aWR0aCAhPSBudWxsICkge1xyXG5cdFx0XHRcdGVsLmRhdGFzZXQubWluX3dpZHRoID0gU3RyaW5nKCBkLm1pbl93aWR0aCApO1xyXG5cdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRlbC5zdHlsZS5zZXRQcm9wZXJ0eSggJy0td3BiYy1jb2wtbWluJywgU3RyaW5nKCBkLm1pbl93aWR0aCApICk7XHJcblx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggZC5odG1sX2lkICE9IG51bGwgKSBlbC5kYXRhc2V0Lmh0bWxfaWQgPSBTdHJpbmcoIGQuaHRtbF9pZCB8fCAnJyApO1xyXG5cdFx0XHRpZiAoIGQuY3NzY2xhc3MgIT0gbnVsbCApIGVsLmRhdGFzZXQuY3NzY2xhc3MgPSBTdHJpbmcoIGQuY3NzY2xhc3MgfHwgJycgKTtcclxuXHRcdFx0aWYgKCBkLnBsYWNlaG9sZGVyICE9IG51bGwgKSBlbC5kYXRhc2V0LnBsYWNlaG9sZGVyID0gU3RyaW5nKCBkLnBsYWNlaG9sZGVyIHx8ICcnICk7XHJcblxyXG5cdFx0XHRjb25zdCB0cGwgPSB0aGlzLmdldF90ZW1wbGF0ZSggdGhpcy50ZW1wbGF0ZV9pZCApO1xyXG5cdFx0XHRpZiAoIHR5cGVvZiB0cGwgIT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0ZWwuaW5uZXJIVE1MID0gJzxkaXYgY2xhc3M9XCJ3cGJjX2JmYl9fZXJyb3JcIiByb2xlPVwiYWxlcnRcIj5UZW1wbGF0ZSBub3QgZm91bmQ6ICcgKyB0aGlzLnRlbXBsYXRlX2lkICsgJy48L2Rpdj4nO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRlbC5pbm5lckhUTUwgPSB0cGwoIGQgKTtcclxuXHRcdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdFx0d2luZG93Ll93cGJjPy5kZXY/LmVycm9yPy4oICdTZWxlY3RfQmFzZS5yZW5kZXInLCBlICk7XHJcblx0XHRcdFx0ZWwuaW5uZXJIVE1MID0gJzxkaXYgY2xhc3M9XCJ3cGJjX2JmYl9fZXJyb3JcIiByb2xlPVwiYWxlcnRcIj5FcnJvciByZW5kZXJpbmcgZmllbGQgcHJldmlldy48L2Rpdj4nO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0ZWwuZGF0YXNldC50eXBlID0gZC50eXBlIHx8IHRoaXMua2luZDtcclxuXHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1sYWJlbCcsIChkLmxhYmVsICE9IG51bGwgPyBTdHJpbmcoIGQubGFiZWwgKSA6ICcnKSApO1xyXG5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRDb3JlLlVJPy5XUEJDX0JGQl9PdmVybGF5Py5lbnN1cmU/LiggY3R4Py5idWlsZGVyLCBlbCApO1xyXG5cdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCAhZWwuZGF0YXNldC5vcHRpb25zICYmIEFycmF5LmlzQXJyYXkoIGQub3B0aW9ucyApICYmIGQub3B0aW9ucy5sZW5ndGggKSB7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGVsLmRhdGFzZXQub3B0aW9ucyA9IEpTT04uc3RyaW5naWZ5KCBkLm9wdGlvbnMgKTtcclxuXHRcdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tIGRyb3Agc2VlZGluZyAob3B0aW9ucyArIHBsYWNlaG9sZGVyKSAtLS0tXHJcblx0XHRzdGF0aWMgb25fZmllbGRfZHJvcChkYXRhLCBlbCwgbWV0YSkge1xyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdHN1cGVyLm9uX2ZpZWxkX2Ryb3A/LiggZGF0YSwgZWwsIG1ldGEgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IGlzX2Ryb3AgPSAobWV0YSAmJiBtZXRhLmNvbnRleHQgPT09ICdkcm9wJyk7XHJcblxyXG5cdFx0XHRpZiAoIGlzX2Ryb3AgKSB7XHJcblx0XHRcdFx0aWYgKCAhQXJyYXkuaXNBcnJheSggZGF0YS5vcHRpb25zICkgfHwgIWRhdGEub3B0aW9ucy5sZW5ndGggKSB7XHJcblx0XHRcdFx0XHRjb25zdCBvcHRzICAgPSAodGhpcy5nZXRfZGVmYXVsdHMoKS5vcHRpb25zIHx8IFtdKS5tYXAoIChvKSA9PiAoe1xyXG5cdFx0XHRcdFx0XHRsYWJlbCAgIDogby5sYWJlbCxcclxuXHRcdFx0XHRcdFx0dmFsdWUgICA6IG8udmFsdWUsXHJcblx0XHRcdFx0XHRcdHNlbGVjdGVkOiAhIW8uc2VsZWN0ZWRcclxuXHRcdFx0XHRcdH0pICk7XHJcblx0XHRcdFx0XHRkYXRhLm9wdGlvbnMgPSBvcHRzO1xyXG5cdFx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdFx0ZWwuZGF0YXNldC5vcHRpb25zID0gSlNPTi5zdHJpbmdpZnkoIG9wdHMgKTtcclxuXHRcdFx0XHRcdFx0ZWwuZGlzcGF0Y2hFdmVudCggbmV3IEN1c3RvbUV2ZW50KCAnd3BiY19iZmJfZmllbGRfZGF0YV9jaGFuZ2VkJywgeyBidWJibGVzOiB0cnVlLFxyXG5cdFx0XHRcdFx0XHRcdGRldGFpbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOiB7XHJcblx0XHRcdFx0XHRcdFx0XHRrZXkgIDogJ29wdGlvbnMnLFxyXG5cdFx0XHRcdFx0XHRcdFx0dmFsdWU6IG9wdHNcclxuXHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdH0gKSApO1xyXG5cdFx0XHRcdFx0XHRDb3JlLlN0cnVjdHVyZT8udXBkYXRlX2ZpZWxkX3Byb3A/LiggZWwsICdvcHRpb25zJywgb3B0cyApO1xyXG5cdFx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRjb25zdCBwaCA9IChkYXRhLnBsYWNlaG9sZGVyID8/ICcnKS50b1N0cmluZygpLnRyaW0oKTtcclxuXHRcdFx0XHRpZiAoICFwaCApIHtcclxuXHRcdFx0XHRcdGNvbnN0IGRmbHQgICAgICAgPSB0aGlzLmdldF9kZWZhdWx0cygpLnBsYWNlaG9sZGVyIHx8ICctLS0gU2VsZWN0IC0tLSc7XHJcblx0XHRcdFx0XHRkYXRhLnBsYWNlaG9sZGVyID0gZGZsdDtcclxuXHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdGVsLmRhdGFzZXQucGxhY2Vob2xkZXIgPSBTdHJpbmcoIGRmbHQgKTtcclxuXHRcdFx0XHRcdFx0ZWwuZGlzcGF0Y2hFdmVudCggbmV3IEN1c3RvbUV2ZW50KCAnd3BiY19iZmJfZmllbGRfZGF0YV9jaGFuZ2VkJywgeyBidWJibGVzOiB0cnVlLFxyXG5cdFx0XHRcdFx0XHRcdGRldGFpbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOiB7XHJcblx0XHRcdFx0XHRcdFx0XHRrZXkgIDogJ3BsYWNlaG9sZGVyJyxcclxuXHRcdFx0XHRcdFx0XHRcdHZhbHVlOiBkZmx0XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9ICkgKTtcclxuXHRcdFx0XHRcdFx0Q29yZS5TdHJ1Y3R1cmU/LnVwZGF0ZV9maWVsZF9wcm9wPy4oIGVsLCAncGxhY2Vob2xkZXInLCBkZmx0ICk7XHJcblx0XHRcdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHRcdC8vIEluc3BlY3RvciBoZWxwZXJzIChzbmFrZV9jYXNlKVxyXG5cdFx0Ly8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblx0XHRzdGF0aWMgZ2V0X3BhbmVsX3Jvb3QoZWwpIHtcclxuXHRcdFx0cmV0dXJuIGVsPy5jbG9zZXN0Py4oICcud3BiY19iZmJfX2luc3BlY3Rvcl9fYm9keScgKSB8fCBlbD8uY2xvc2VzdD8uKCAnLndwYmNfYmZiX19pbnNwZWN0b3InICkgfHwgbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgZ2V0X2xpc3QocGFuZWwpIHtcclxuXHRcdFx0cmV0dXJuIHBhbmVsID8gcGFuZWwucXVlcnlTZWxlY3RvciggdGhpcy51aS5saXN0ICkgOiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN0YXRpYyBnZXRfaG9sZGVyKHBhbmVsKSB7XHJcblx0XHRcdHJldHVybiBwYW5lbCA/IHBhbmVsLnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkuaG9sZGVyICkgOiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN0YXRpYyBtYWtlX3VpZCgpIHtcclxuXHRcdFx0cmV0dXJuICd3cGJjX2luc19hdXRvX29wdF8nICsgTWF0aC5yYW5kb20oKS50b1N0cmluZyggMzYgKS5zbGljZSggMiwgMTAgKTtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgYXBwZW5kX3JvdyhwYW5lbCwgZGF0YSkge1xyXG5cdFx0XHRjb25zdCBsaXN0ID0gdGhpcy5nZXRfbGlzdCggcGFuZWwgKTtcclxuXHRcdFx0aWYgKCAhbGlzdCApIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGlkeCAgPSBsaXN0LmNoaWxkcmVuLmxlbmd0aDtcclxuXHRcdFx0Y29uc3Qgcm93ZCA9IE9iamVjdC5hc3NpZ24oIHsgbGFiZWw6ICcnLCB2YWx1ZTogJycsIHNlbGVjdGVkOiBmYWxzZSwgaW5kZXg6IGlkeCB9LCAoZGF0YSB8fCB7fSkgKTtcclxuXHRcdFx0aWYgKCAhcm93ZC51aWQgKSByb3dkLnVpZCA9IHRoaXMubWFrZV91aWQoKTtcclxuXHJcblx0XHRcdGNvbnN0IHRwbF9pZCA9IHRoaXMub3B0aW9uX3Jvd190ZW1wbGF0ZV9pZDtcclxuXHRcdFx0Y29uc3QgdHBsICAgID0gKHdpbmRvdy53cCAmJiB3cC50ZW1wbGF0ZSkgPyB3cC50ZW1wbGF0ZSggdHBsX2lkICkgOiBudWxsO1xyXG5cdFx0XHRjb25zdCBodG1sICAgPSB0cGwgPyB0cGwoIHJvd2QgKSA6IG51bGw7XHJcblxyXG5cdFx0XHQvLyBJbiBhcHBlbmRfcm93KCkgLT4gZmFsbGJhY2sgSFRNTC5cclxuXHRcdFx0Y29uc3Qgd3JhcCAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xyXG5cdFx0XHR3cmFwLmlubmVySFRNTCA9IGh0bWwgfHwgKFxyXG5cdFx0XHRcdCc8ZGl2IGNsYXNzPVwid3BiY19iZmJfX29wdGlvbnNfcm93XCIgZGF0YS1pbmRleD1cIicgKyAocm93ZC5pbmRleCB8fCAwKSArICdcIj4nICtcclxuXHRcdFx0XHRcdCc8c3BhbiBjbGFzcz1cIndwYmNfYmZiX19kcmFnLWhhbmRsZVwiPjxzcGFuIGNsYXNzPVwid3BiY19pY25fZHJhZ19pbmRpY2F0b3JcIj48L3NwYW4+PC9zcGFuPicgK1xyXG5cdFx0XHRcdFx0JzxpbnB1dCB0eXBlPVwidGV4dFwiIGNsYXNzPVwid3BiY19iZmJfX29wdC1sYWJlbFwiIHBsYWNlaG9sZGVyPVwiTGFiZWxcIiB2YWx1ZT1cIicgKyAocm93ZC5sYWJlbCB8fCAnJykgKyAnXCI+JyArXHJcblx0XHRcdFx0XHQnPGlucHV0IHR5cGU9XCJ0ZXh0XCIgY2xhc3M9XCJ3cGJjX2JmYl9fb3B0LXZhbHVlXCIgcGxhY2Vob2xkZXI9XCJWYWx1ZVwiIHZhbHVlPVwiJyArIChyb3dkLnZhbHVlIHx8ICcnKSArICdcIj4nICtcclxuXHRcdFx0XHRcdCc8ZGl2IGNsYXNzPVwid3BiY19iZmJfX29wdC1zZWxlY3RlZFwiPicgK1xyXG5cdFx0XHRcdFx0XHQnPGRpdiBjbGFzcz1cImluc3BlY3Rvcl9fY29udHJvbCB3cGJjX3VpX190b2dnbGVcIj4nICtcclxuXHRcdFx0XHRcdFx0XHQnPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIGNsYXNzPVwid3BiY19iZmJfX29wdC1zZWxlY3RlZC1jaGsgaW5zcGVjdG9yX19pbnB1dFwiIGlkPVwiJyArIHJvd2QudWlkICsgJ1wiIHJvbGU9XCJzd2l0Y2hcIiAnICsgKHJvd2Quc2VsZWN0ZWQgPyAnY2hlY2tlZCBhcmlhLWNoZWNrZWQ9XCJ0cnVlXCInIDogJ2FyaWEtY2hlY2tlZD1cImZhbHNlXCInKSArICc+JyArXHJcblx0XHRcdFx0XHRcdFx0JzxsYWJlbCBjbGFzcz1cIndwYmNfdWlfX3RvZ2dsZV9pY29uX3JhZGlvXCIgZm9yPVwiJyArIHJvd2QudWlkICsgJ1wiPjwvbGFiZWw+JyArXHJcblx0XHRcdFx0XHRcdFx0JzxsYWJlbCBjbGFzcz1cIndwYmNfdWlfX3RvZ2dsZV9sYWJlbFwiIGZvcj1cIicgKyByb3dkLnVpZCArICdcIj5EZWZhdWx0PC9sYWJlbD4nICtcclxuXHRcdFx0XHRcdFx0JzwvZGl2PicgK1xyXG5cdFx0XHRcdFx0JzwvZGl2PicgK1xyXG5cdFx0XHRcdFx0Ly8gMy1kb3QgZHJvcGRvd24gKHVzZXMgZXhpc3RpbmcgcGx1Z2luIGRyb3Bkb3duIEpTKS5cclxuXHRcdFx0XHRcdCc8ZGl2IGNsYXNzPVwid3BiY191aV9lbCB3cGJjX3VpX2VsX2NvbnRhaW5lciB3cGJjX3VpX2VsX19kcm9wZG93blwiPicgK1xyXG5cdFx0XHRcdFx0XHQnPGEgaHJlZj1cImphdmFzY3JpcHQ6dm9pZCgwKVwiIGRhdGEtdG9nZ2xlPVwid3BiY19kcm9wZG93blwiIGFyaWEtZXhwYW5kZWQ9XCJmYWxzZVwiIGNsYXNzPVwidWxfZHJvcGRvd25fbWVudV90b2dnbGVcIj4nICtcclxuXHRcdFx0XHRcdFx0XHQnPGkgY2xhc3M9XCJtZW51X2ljb24gaWNvbi0xeCB3cGJjX2ljbl9tb3JlX3ZlcnRcIj48L2k+JyArXHJcblx0XHRcdFx0XHRcdCc8L2E+JyArXHJcblx0XHRcdFx0XHRcdCc8dWwgY2xhc3M9XCJ1bF9kcm9wZG93bl9tZW51XCIgcm9sZT1cIm1lbnVcIiBzdHlsZT1cInJpZ2h0OjBweDsgbGVmdDphdXRvO1wiPicgK1xyXG5cdFx0XHRcdFx0XHRcdCc8bGk+JyArXHJcblx0XHRcdFx0XHRcdFx0XHQnPGEgY2xhc3M9XCJ1bF9kcm9wZG93bl9tZW51X2xpX2FjdGlvblwiIGRhdGEtYWN0aW9uPVwiYWRkX2FmdGVyXCIgaHJlZj1cImphdmFzY3JpcHQ6dm9pZCgwKVwiPicgK1xyXG5cdFx0XHRcdFx0XHRcdFx0XHQnQWRkIE5ldycgK1xyXG5cdFx0XHRcdFx0XHRcdFx0XHQnPGkgY2xhc3M9XCJtZW51X2ljb24gaWNvbi0xeCB3cGJjX2ljbl9hZGRfY2lyY2xlXCI+PC9pPicgK1xyXG5cdFx0XHRcdFx0XHRcdFx0JzwvYT4nICtcclxuXHRcdFx0XHRcdFx0XHQnPC9saT4nICtcclxuXHRcdFx0XHRcdFx0XHQnPGxpPicgK1xyXG5cdFx0XHRcdFx0XHRcdFx0JzxhIGNsYXNzPVwidWxfZHJvcGRvd25fbWVudV9saV9hY3Rpb25cIiBkYXRhLWFjdGlvbj1cImR1cGxpY2F0ZVwiIGhyZWY9XCJqYXZhc2NyaXB0OnZvaWQoMClcIj4nICtcclxuXHRcdFx0XHRcdFx0XHRcdFx0J0R1cGxpY2F0ZScgK1xyXG5cdFx0XHRcdFx0XHRcdFx0XHQnPGkgY2xhc3M9XCJtZW51X2ljb24gaWNvbi0xeCB3cGJjX2ljbl9jb250ZW50X2NvcHlcIj48L2k+JyArXHJcblx0XHRcdFx0XHRcdFx0XHQnPC9hPicgK1xyXG5cdFx0XHRcdFx0XHRcdCc8L2xpPicgK1xyXG5cdFx0XHRcdFx0XHRcdCc8bGkgY2xhc3M9XCJkaXZpZGVyXCI+PC9saT4nICtcclxuXHRcdFx0XHRcdFx0XHQnPGxpPicgK1xyXG5cdFx0XHRcdFx0XHRcdFx0JzxhIGNsYXNzPVwidWxfZHJvcGRvd25fbWVudV9saV9hY3Rpb25cIiBkYXRhLWFjdGlvbj1cInJlbW92ZVwiIGhyZWY9XCJqYXZhc2NyaXB0OnZvaWQoMClcIj4nICtcclxuXHRcdFx0XHRcdFx0XHRcdFx0J1JlbW92ZScgK1xyXG5cdFx0XHRcdFx0XHRcdFx0XHQnPGkgY2xhc3M9XCJtZW51X2ljb24gaWNvbi0xeCB3cGJjX2ljbl9kZWxldGVfb3V0bGluZVwiPjwvaT4nICtcclxuXHRcdFx0XHRcdFx0XHRcdCc8L2E+JyArXHJcblx0XHRcdFx0XHRcdFx0JzwvbGk+JyArXHJcblx0XHRcdFx0XHRcdCc8L3VsPicgK1xyXG5cdFx0XHRcdFx0JzwvZGl2PicgK1xyXG5cdFx0XHRcdCc8L2Rpdj4nXHJcblx0XHRcdCk7XHJcblxyXG5cdFx0XHRjb25zdCBub2RlID0gd3JhcC5maXJzdEVsZW1lbnRDaGlsZDtcclxuXHRcdFx0IGlmICghIG5vZGUpIHtcclxuXHRcdFx0XHQgcmV0dXJuO1xyXG5cdFx0XHQgfVxyXG5cdFx0XHQvLyBwcmUtaGlkZSBWYWx1ZSBpbnB1dCBpZiB0b2dnbGUgaXMgT0ZGICoqYmVmb3JlKiogYXBwZW5kaW5nLlxyXG5cdFx0XHRjb25zdCBkaWZmZXJzID0gdGhpcy5pc192YWx1ZV9kaWZmZXJzX2VuYWJsZWQoIHBhbmVsICk7XHJcblx0XHRcdGNvbnN0IHZhbEluICAgPSBub2RlLnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkudmFsdWUgKTtcclxuXHRcdFx0Y29uc3QgbGJsSW4gICA9IG5vZGUucXVlcnlTZWxlY3RvciggdGhpcy51aS5sYWJlbCApO1xyXG5cclxuXHRcdFx0aWYgKCAhZGlmZmVycyAmJiB2YWxJbiApIHtcclxuXHRcdFx0XHRpZiAoICF2YWxJbi5kYXRhc2V0LmNhY2hlZF92YWx1ZSApIHtcclxuXHRcdFx0XHRcdHZhbEluLmRhdGFzZXQuY2FjaGVkX3ZhbHVlID0gdmFsSW4udmFsdWUgfHwgJyc7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGlmICggbGJsSW4gKSB2YWxJbi52YWx1ZSA9IHRoaXMuYnVpbGRfdmFsdWVfZnJvbV9sYWJlbCggbGJsSW4udmFsdWUsIGZhbHNlICk7XHJcblx0XHRcdFx0dmFsSW4uc2V0QXR0cmlidXRlKCAnZGlzYWJsZWQnLCAnZGlzYWJsZWQnICk7XHJcblx0XHRcdFx0dmFsSW4uc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuXHRcdFx0fVxyXG5cclxuXHJcblx0XHRcdHRoaXMuaW5pdF9yb3dfZnJlc2hfZmxhZ3MoIG5vZGUgKTtcclxuXHRcdFx0bGlzdC5hcHBlbmRDaGlsZCggbm9kZSApO1xyXG5cclxuXHRcdFx0Ly8gS2VlcCB5b3VyIGV4aXN0aW5nIHBvc3QtYXBwZW5kIHN5bmMgYXMgYSBzYWZldHkgbmV0XHJcblx0XHRcdHRoaXMuc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eSggcGFuZWwgKTtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgY2xvc2VfZHJvcGRvd24oYW5jaG9yX2VsKSB7XHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0dmFyIHJvb3QgPSBhbmNob3JfZWw/LmNsb3Nlc3Q/LiggdGhpcy51aS5tZW51X3Jvb3QgKTtcclxuXHRcdFx0XHRpZiAoIHJvb3QgKSB7XHJcblx0XHRcdFx0XHQvLyBJZiB5b3VyIGRyb3Bkb3duIHRvZ2dsZXIgdG9nZ2xlcyBhIGNsYXNzIGxpa2UgJ29wZW4nLCBjbG9zZSBpdC5cclxuXHRcdFx0XHRcdHJvb3QuY2xhc3NMaXN0LnJlbW92ZSggJ29wZW4nICk7XHJcblx0XHRcdFx0XHQvLyBPciBpZiBpdCByZWxpZXMgb24gYXJpYS1leHBhbmRlZCBvbiB0aGUgdG9nZ2xlLlxyXG5cdFx0XHRcdFx0dmFyIHQgPSByb290LnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkubWVudV90b2dnbGUgKTtcclxuXHRcdFx0XHRcdGlmICggdCApIHtcclxuXHRcdFx0XHRcdFx0dC5zZXRBdHRyaWJ1dGUoICdhcmlhLWV4cGFuZGVkJywgJ2ZhbHNlJyApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7IH1cclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgaW5zZXJ0X2FmdGVyKG5ld19ub2RlLCByZWZfbm9kZSkge1xyXG5cdFx0XHRpZiAoIHJlZl9ub2RlPy5wYXJlbnROb2RlICkge1xyXG5cdFx0XHRcdGlmICggcmVmX25vZGUubmV4dFNpYmxpbmcgKSB7XHJcblx0XHRcdFx0XHRyZWZfbm9kZS5wYXJlbnROb2RlLmluc2VydEJlZm9yZSggbmV3X25vZGUsIHJlZl9ub2RlLm5leHRTaWJsaW5nICk7XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdHJlZl9ub2RlLnBhcmVudE5vZGUuYXBwZW5kQ2hpbGQoIG5ld19ub2RlICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0c3RhdGljIGNvbW1pdF9vcHRpb25zKHBhbmVsKSB7XHJcblx0XHRcdGNvbnN0IGxpc3QgICA9IHRoaXMuZ2V0X2xpc3QoIHBhbmVsICk7XHJcblx0XHRcdGNvbnN0IGhvbGRlciA9IHRoaXMuZ2V0X2hvbGRlciggcGFuZWwgKTtcclxuXHRcdFx0aWYgKCAhbGlzdCB8fCAhaG9sZGVyICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0Y29uc3QgZGlmZmVycyA9IHRoaXMuaXNfdmFsdWVfZGlmZmVyc19lbmFibGVkKCBwYW5lbCApO1xyXG5cclxuXHRcdFx0Y29uc3Qgcm93cyAgICA9IGxpc3QucXVlcnlTZWxlY3RvckFsbCggdGhpcy51aS5yb3cgKTtcclxuXHRcdFx0Y29uc3Qgb3B0aW9ucyA9IFtdO1xyXG5cdFx0XHRmb3IgKCBsZXQgaSA9IDA7IGkgPCByb3dzLmxlbmd0aDsgaSsrICkge1xyXG5cdFx0XHRcdGNvbnN0IHIgICAgICA9IHJvd3NbaV07XHJcblx0XHRcdFx0Y29uc3QgbGJsX2luID0gci5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLmxhYmVsICk7XHJcblx0XHRcdFx0Y29uc3QgdmFsX2luID0gci5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLnZhbHVlICk7XHJcblx0XHRcdFx0Y29uc3QgY2hrICAgID0gci5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLnRvZ2dsZSApO1xyXG5cclxuXHRcdFx0XHRjb25zdCBsYmwgPSAobGJsX2luICYmIGxibF9pbi52YWx1ZSkgfHwgJyc7XHJcblx0XHRcdFx0bGV0IHZhbCAgID0gKHZhbF9pbiAmJiB2YWxfaW4udmFsdWUpIHx8ICcnO1xyXG5cclxuXHRcdFx0XHQvLyBJZiBzaW5nbGUtaW5wdXQgbW9kZSAtPiBoYXJkIG1pcnJvciB0byBsYWJlbC5cclxuXHRcdFx0XHRpZiAoICEgZGlmZmVycyApIHtcclxuXHRcdFx0XHRcdC8vIHNpbmdsZS1pbnB1dCBtb2RlOiBtaXJyb3IgTGFiZWwsIG1pbmltYWwgZXNjYXBpbmcgKG5vIHNsdWcpLlxyXG5cdFx0XHRcdFx0dmFsID0gdGhpcy5idWlsZF92YWx1ZV9mcm9tX2xhYmVsKCBsYmwsIC8qZGlmZmVycz0qL2ZhbHNlICk7XHJcblx0XHRcdFx0XHRpZiAoIHZhbF9pbiApIHtcclxuXHRcdFx0XHRcdFx0dmFsX2luLnZhbHVlID0gdmFsOyAgIC8vIGtlZXAgaGlkZGVuIGlucHV0IGluIHN5bmMgZm9yIGFueSBwcmV2aWV3cy9kZWJ1Zy5cclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGNvbnN0IHNlbCA9ICEhKGNoayAmJiBjaGsuY2hlY2tlZCk7XHJcblx0XHRcdFx0b3B0aW9ucy5wdXNoKCB7IGxhYmVsOiBsYmwsIHZhbHVlOiB2YWwsIHNlbGVjdGVkOiBzZWwgfSApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdGhvbGRlci52YWx1ZSA9IEpTT04uc3RyaW5naWZ5KCBvcHRpb25zICk7XHJcblx0XHRcdFx0aG9sZGVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdGhvbGRlci5kaXNwYXRjaEV2ZW50KCBuZXcgRXZlbnQoICdjaGFuZ2UnLCB7IGJ1YmJsZXM6IHRydWUgfSApICk7XHJcblx0XHRcdFx0cGFuZWwuZGlzcGF0Y2hFdmVudCggbmV3IEN1c3RvbUV2ZW50KCAnd3BiY19iZmJfZmllbGRfZGF0YV9jaGFuZ2VkJywge1xyXG5cdFx0XHRcdFx0YnViYmxlczogdHJ1ZSwgZGV0YWlsOiB7XHJcblx0XHRcdFx0XHRcdGtleTogJ29wdGlvbnMnLCB2YWx1ZTogb3B0aW9uc1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gKSApO1xyXG5cdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5zeW5jX2RlZmF1bHRfdmFsdWVfbG9jayggcGFuZWwgKTtcclxuXHRcdFx0dGhpcy5zeW5jX3BsYWNlaG9sZGVyX2xvY2soIHBhbmVsICk7XHJcblxyXG5cdFx0XHQvLyBNaXJyb3IgdG8gdGhlIHNlbGVjdGVkIGZpZWxkIGVsZW1lbnQgc28gY2FudmFzL2V4cG9ydCBzZWVzIGN1cnJlbnQgb3B0aW9ucyBpbW1lZGlhdGVseS5cclxuXHRcdFx0Y29uc3QgZmllbGQgPSBwYW5lbC5fX3NlbGVjdGJhc2VfZmllbGRcclxuXHRcdFx0XHR8fCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19maWVsZC5pcy1zZWxlY3RlZCwgLndwYmNfYmZiX19maWVsZC0tc2VsZWN0ZWQnICk7XHJcblx0XHRcdGlmICggZmllbGQgKSB7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGZpZWxkLmRhdGFzZXQub3B0aW9ucyA9IEpTT04uc3RyaW5naWZ5KCBvcHRpb25zICk7XHJcblx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdENvcmUuU3RydWN0dXJlPy51cGRhdGVfZmllbGRfcHJvcD8uKCBmaWVsZCwgJ29wdGlvbnMnLCBvcHRpb25zICk7XHJcblx0XHRcdFx0ZmllbGQuZGlzcGF0Y2hFdmVudCggbmV3IEN1c3RvbUV2ZW50KCAnd3BiY19iZmJfZmllbGRfZGF0YV9jaGFuZ2VkJywge1xyXG5cdFx0XHRcdFx0YnViYmxlczogdHJ1ZSwgZGV0YWlsOiB7IGtleTogJ29wdGlvbnMnLCB2YWx1ZTogb3B0aW9ucyB9XHJcblx0XHRcdFx0fSApICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblxyXG5cdFx0c3RhdGljIGVuc3VyZV9zb3J0YWJsZShwYW5lbCkge1xyXG5cclxuXHRcdFx0Y29uc3QgbGlzdCA9IHRoaXMuZ2V0X2xpc3QoIHBhbmVsICk7XHJcblx0XHRcdGlmICggISBsaXN0ICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRjb25zdCBleGlzdGluZyA9IHdpbmRvdy5Tb3J0YWJsZT8uZ2V0Py4oIGxpc3QgKTtcclxuXHRcdFx0XHRpZiAoIGV4aXN0aW5nICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Y29uc3QgYnVpbGRlciA9IHdpbmRvdy53cGJjX2JmYl9hcGk/LmdldF9idWlsZGVyPy4oKSB8fCB3aW5kb3cud3BiY19iZmIgfHwgbnVsbDtcclxuXHJcblx0XHRcdFx0Ly8gUHJlZmVyIHRoZSBzaGFyZWQgU29ydGFibGUgbWFuYWdlciBzbyB0aGUgc2lkZWJhciBsaXN0IHVzZXNcclxuXHRcdFx0XHQvLyB0aGUgZGVkaWNhdGVkIFwic2ltcGxlX2xpc3RcIiBjb25maWcgaW5zdGVhZCBvZiB0aGUgY2FudmFzIGNvbmZpZy5cclxuXHRcdFx0XHRpZiAoIGJ1aWxkZXIgJiYgYnVpbGRlci5zb3J0YWJsZSAmJiB0eXBlb2YgYnVpbGRlci5zb3J0YWJsZS5lbnN1cmUgPT09ICdmdW5jdGlvbicgKSB7XHJcblxyXG5cdFx0XHRcdFx0YnVpbGRlci5zb3J0YWJsZS5lbnN1cmUoXHJcblx0XHRcdFx0XHRcdGxpc3QsXHJcblx0XHRcdFx0XHRcdCdjYW52YXMnLFxyXG5cdFx0XHRcdFx0XHR7XHJcblx0XHRcdFx0XHRcdFx0c29ydGFibGVfa2luZCAgICAgOiAnc2ltcGxlX2xpc3QnLFxyXG5cdFx0XHRcdFx0XHRcdGhhbmRsZV9zZWxlY3RvciAgIDogdGhpcy51aS5kcmFnX2hhbmRsZSxcclxuXHRcdFx0XHRcdFx0XHRkcmFnZ2FibGVfc2VsZWN0b3I6IHRoaXMudWkucm93LFxyXG5cdFx0XHRcdFx0XHRcdG9uVXBkYXRlICAgICAgICAgIDogKCkgPT4ge1xyXG5cdFx0XHRcdFx0XHRcdFx0dGhpcy5jb21taXRfb3B0aW9ucyggcGFuZWwgKTtcclxuXHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdCk7XHJcblxyXG5cdFx0XHRcdH0gZWxzZSBpZiAoIHdpbmRvdy5Tb3J0YWJsZT8uY3JlYXRlICkge1xyXG5cdFx0XHRcdFx0Ly8gRmFsbGJhY2sgaWYgYnVpbGRlciBpcyBub3QgcmVhZHkgZm9yIHNvbWUgcmVhc29uLlxyXG5cdFx0XHRcdFx0d2luZG93LlNvcnRhYmxlLmNyZWF0ZShcclxuXHRcdFx0XHRcdFx0bGlzdCxcclxuXHRcdFx0XHRcdFx0e1xyXG5cdFx0XHRcdFx0XHRcdGhhbmRsZSAgICAgICAgICAgOiB0aGlzLnVpLmRyYWdfaGFuZGxlLFxyXG5cdFx0XHRcdFx0XHRcdGRyYWdnYWJsZSAgICAgICAgOiB0aGlzLnVpLnJvdyxcclxuXHRcdFx0XHRcdFx0XHRhbmltYXRpb24gICAgICAgIDogMTIwLFxyXG5cdFx0XHRcdFx0XHRcdGZvcmNlRmFsbGJhY2sgICAgOiB0cnVlLFxyXG5cdFx0XHRcdFx0XHRcdGZhbGxiYWNrT25Cb2R5ICAgOiBmYWxzZSxcclxuXHRcdFx0XHRcdFx0XHRmYWxsYmFja1RvbGVyYW5jZTogOCxcclxuXHRcdFx0XHRcdFx0XHRyZW1vdmVDbG9uZU9uSGlkZTogdHJ1ZSxcclxuXHRcdFx0XHRcdFx0XHRvblVwZGF0ZSAgICAgICAgIDogKCkgPT4ge1xyXG5cdFx0XHRcdFx0XHRcdFx0dGhpcy5jb21taXRfb3B0aW9ucyggcGFuZWwgKTtcclxuXHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdCk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRsaXN0LmRhdGFzZXQuc29ydGFibGVfaW5pdCA9ICcxJztcclxuXHJcblx0XHRcdH0gY2F0Y2ggKCBlICkge1xyXG5cdFx0XHRcdHdpbmRvdy5fd3BiYz8uZGV2Py5lcnJvcj8uKCAnU2VsZWN0X0Jhc2UuZW5zdXJlX3NvcnRhYmxlJywgZSApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0c3RhdGljIHJlYnVpbGRfaWZfZW1wdHkocGFuZWwpIHtcclxuXHRcdFx0Y29uc3QgbGlzdCAgID0gdGhpcy5nZXRfbGlzdCggcGFuZWwgKTtcclxuXHRcdFx0Y29uc3QgaG9sZGVyID0gdGhpcy5nZXRfaG9sZGVyKCBwYW5lbCApO1xyXG5cdFx0XHRpZiAoICFsaXN0IHx8ICFob2xkZXIgfHwgbGlzdC5jaGlsZHJlbi5sZW5ndGggKSByZXR1cm47XHJcblxyXG5cdFx0XHRsZXQgZGF0YSA9IFtdO1xyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdGRhdGEgPSBKU09OLnBhcnNlKCBob2xkZXIudmFsdWUgfHwgJ1tdJyApO1xyXG5cdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0XHRkYXRhID0gW107XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGlmICggIUFycmF5LmlzQXJyYXkoIGRhdGEgKSB8fCAhZGF0YS5sZW5ndGggKSB7XHJcblx0XHRcdFx0ZGF0YSA9ICh0aGlzLmdldF9kZWZhdWx0cygpLm9wdGlvbnMgfHwgW10pLnNsaWNlKCAwICk7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGhvbGRlci52YWx1ZSA9IEpTT04uc3RyaW5naWZ5KCBkYXRhICk7XHJcblx0XHRcdFx0XHRob2xkZXIuZGlzcGF0Y2hFdmVudCggbmV3IEV2ZW50KCAnaW5wdXQnLCB7IGJ1YmJsZXM6IHRydWUgfSApICk7XHJcblx0XHRcdFx0XHRob2xkZXIuZGlzcGF0Y2hFdmVudCggbmV3IEV2ZW50KCAnY2hhbmdlJywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfICkge1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Zm9yICggbGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkrKyApIHtcclxuXHRcdFx0XHR0aGlzLmFwcGVuZF9yb3coIHBhbmVsLCB7XHJcblx0XHRcdFx0XHRsYWJlbCAgIDogZGF0YVtpXT8ubGFiZWwgfHwgJycsXHJcblx0XHRcdFx0XHR2YWx1ZSAgIDogZGF0YVtpXT8udmFsdWUgfHwgJycsXHJcblx0XHRcdFx0XHRzZWxlY3RlZDogISFkYXRhW2ldPy5zZWxlY3RlZCxcclxuXHRcdFx0XHRcdGluZGV4ICAgOiBpLFxyXG5cdFx0XHRcdFx0dWlkICAgICA6IHRoaXMubWFrZV91aWQoKVxyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5zeW5jX2RlZmF1bHRfdmFsdWVfbG9jayggcGFuZWwgKTtcclxuXHRcdFx0dGhpcy5zeW5jX3BsYWNlaG9sZGVyX2xvY2soIHBhbmVsICk7XHJcblx0XHRcdHRoaXMuc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eSggcGFuZWwgKTtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgaGFzX3Jvd19kZWZhdWx0cyhwYW5lbCkge1xyXG5cdFx0XHRjb25zdCBjaGVja3MgPSBwYW5lbD8ucXVlcnlTZWxlY3RvckFsbCggdGhpcy51aS50b2dnbGUgKTtcclxuXHRcdFx0aWYgKCAhY2hlY2tzPy5sZW5ndGggKSByZXR1cm4gZmFsc2U7XHJcblx0XHRcdGZvciAoIGxldCBpID0gMDsgaSA8IGNoZWNrcy5sZW5ndGg7IGkrKyApIGlmICggY2hlY2tzW2ldLmNoZWNrZWQgKSByZXR1cm4gdHJ1ZTtcclxuXHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN0YXRpYyBpc19tdWx0aXBsZV9lbmFibGVkKHBhbmVsKSB7XHJcblx0XHRcdGNvbnN0IGNoayA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLm11bHRpcGxlX2NoayApO1xyXG5cdFx0XHRyZXR1cm4gISEoY2hrICYmIGNoay5jaGVja2VkKTtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgaGFzX3RleHRfZGVmYXVsdF92YWx1ZShwYW5lbCkge1xyXG5cdFx0XHRjb25zdCBkdiA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLmRlZmF1bHRfdGV4dCApO1xyXG5cdFx0XHRyZXR1cm4gISEoZHYgJiYgU3RyaW5nKCBkdi52YWx1ZSB8fCAnJyApLnRyaW0oKS5sZW5ndGgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN0YXRpYyBzeW5jX2RlZmF1bHRfdmFsdWVfbG9jayhwYW5lbCkge1xyXG5cdFx0XHRjb25zdCBpbnB1dCA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLmRlZmF1bHRfdGV4dCApO1xyXG5cdFx0XHRjb25zdCBub3RlICA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCAnLmpzLWRlZmF1bHQtdmFsdWUtbm90ZScgKTtcclxuXHRcdFx0aWYgKCAhaW5wdXQgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBsb2NrICAgICA9IHRoaXMuaGFzX3Jvd19kZWZhdWx0cyggcGFuZWwgKTtcclxuXHRcdFx0aW5wdXQuZGlzYWJsZWQgPSAhIWxvY2s7XHJcblx0XHRcdGlmICggbG9jayApIHtcclxuXHRcdFx0XHRpbnB1dC5zZXRBdHRyaWJ1dGUoICdhcmlhLWRpc2FibGVkJywgJ3RydWUnICk7XHJcblx0XHRcdFx0aWYgKCBub3RlICkgbm90ZS5zdHlsZS5kaXNwbGF5ID0gJyc7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0aW5wdXQucmVtb3ZlQXR0cmlidXRlKCAnYXJpYS1kaXNhYmxlZCcgKTtcclxuXHRcdFx0XHRpZiAoIG5vdGUgKSBub3RlLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgc3luY19wbGFjZWhvbGRlcl9sb2NrKHBhbmVsKSB7XHJcblx0XHRcdGNvbnN0IGlucHV0ID0gcGFuZWw/LnF1ZXJ5U2VsZWN0b3IoIHRoaXMudWkucGxhY2Vob2xkZXJfaW5wdXQgKTtcclxuXHRcdFx0Y29uc3Qgbm90ZSAgPSBwYW5lbD8ucXVlcnlTZWxlY3RvciggdGhpcy51aS5wbGFjZWhvbGRlcl9ub3RlICk7XHJcblxyXG5cdFx0XHQvLyBORVc6IGNvbXB1dGUgbXVsdGlwbGUgYW5kIHRvZ2dsZSByb3cgdmlzaWJpbGl0eVxyXG5cdFx0XHRjb25zdCBpc011bHRpcGxlICAgICA9IHRoaXMuaXNfbXVsdGlwbGVfZW5hYmxlZCggcGFuZWwgKTtcclxuXHRcdFx0Y29uc3QgcGxhY2Vob2xkZXJSb3cgPSBpbnB1dD8uY2xvc2VzdCggJy5pbnNwZWN0b3JfX3JvdycgKSB8fCBudWxsO1xyXG5cdFx0XHRjb25zdCBzaXplSW5wdXQgICAgICA9IHBhbmVsPy5xdWVyeVNlbGVjdG9yKCB0aGlzLnVpLnNpemVfaW5wdXQgKSB8fCBudWxsO1xyXG5cdFx0XHRjb25zdCBzaXplUm93ICAgICAgICA9IHNpemVJbnB1dD8uY2xvc2VzdCggJy5pbnNwZWN0b3JfX3JvdycgKSB8fCBudWxsO1xyXG5cclxuXHRcdFx0Ly8gU2hvdyBwbGFjZWhvbGRlciBvbmx5IGZvciBzaW5nbGUtc2VsZWN0OyBzaG93IHNpemUgb25seSBmb3IgbXVsdGlwbGVcclxuXHRcdFx0aWYgKCBwbGFjZWhvbGRlclJvdyApIHBsYWNlaG9sZGVyUm93LnN0eWxlLmRpc3BsYXkgPSBpc011bHRpcGxlID8gJ25vbmUnIDogJyc7XHJcblx0XHRcdGlmICggc2l6ZVJvdyApIHNpemVSb3cuc3R5bGUuZGlzcGxheSA9IGlzTXVsdGlwbGUgPyAnJyA6ICdub25lJztcclxuXHJcblx0XHRcdC8vIEV4aXN0aW5nIGJlaGF2aW9yIChrZWVwIGFzLWlzKVxyXG5cdFx0XHRpZiAoICFpbnB1dCApIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGxvY2sgPSBpc011bHRpcGxlIHx8IHRoaXMuaGFzX3Jvd19kZWZhdWx0cyggcGFuZWwgKSB8fCB0aGlzLmhhc190ZXh0X2RlZmF1bHRfdmFsdWUoIHBhbmVsICk7XHJcblx0XHRcdGlmICggbm90ZSAmJiAhbm90ZS5pZCApIG5vdGUuaWQgPSAnd3BiY19wbGFjZWhvbGRlcl9ub3RlXycgKyBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKCAzNiApLnNsaWNlKCAyLCAxMCApO1xyXG5cclxuXHRcdFx0aW5wdXQuZGlzYWJsZWQgPSAhIWxvY2s7XHJcblx0XHRcdGlmICggbG9jayApIHtcclxuXHRcdFx0XHRpbnB1dC5zZXRBdHRyaWJ1dGUoICdhcmlhLWRpc2FibGVkJywgJ3RydWUnICk7XHJcblx0XHRcdFx0aWYgKCBub3RlICkge1xyXG5cdFx0XHRcdFx0bm90ZS5zdHlsZS5kaXNwbGF5ID0gJyc7XHJcblx0XHRcdFx0XHRpbnB1dC5zZXRBdHRyaWJ1dGUoICdhcmlhLWRlc2NyaWJlZGJ5Jywgbm90ZS5pZCApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRpbnB1dC5yZW1vdmVBdHRyaWJ1dGUoICdhcmlhLWRpc2FibGVkJyApO1xyXG5cdFx0XHRcdGlucHV0LnJlbW92ZUF0dHJpYnV0ZSggJ2FyaWEtZGVzY3JpYmVkYnknICk7XHJcblx0XHRcdFx0aWYgKCBub3RlICkgbm90ZS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0c3RhdGljIGVuZm9yY2Vfc2luZ2xlX2RlZmF1bHQocGFuZWwsIGNsaWNrZWQpIHtcclxuXHRcdFx0aWYgKCB0aGlzLmlzX211bHRpcGxlX2VuYWJsZWQoIHBhbmVsICkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjaGVja3MgPSBwYW5lbD8ucXVlcnlTZWxlY3RvckFsbCggdGhpcy51aS50b2dnbGUgKTtcclxuXHRcdFx0aWYgKCAhY2hlY2tzPy5sZW5ndGggKSByZXR1cm47XHJcblxyXG5cdFx0XHRpZiAoIGNsaWNrZWQgJiYgY2xpY2tlZC5jaGVja2VkICkge1xyXG5cdFx0XHRcdGZvciAoIGxldCBpID0gMDsgaSA8IGNoZWNrcy5sZW5ndGg7IGkrKyApIGlmICggY2hlY2tzW2ldICE9PSBjbGlja2VkICkge1xyXG5cdFx0XHRcdFx0Y2hlY2tzW2ldLmNoZWNrZWQgPSBmYWxzZTtcclxuXHRcdFx0XHRcdGNoZWNrc1tpXS5zZXRBdHRyaWJ1dGUoICdhcmlhLWNoZWNrZWQnLCAnZmFsc2UnICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGNsaWNrZWQuc2V0QXR0cmlidXRlKCAnYXJpYS1jaGVja2VkJywgJ3RydWUnICk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRsZXQga2VwdCA9IGZhbHNlO1xyXG5cdFx0XHRmb3IgKCBsZXQgaiA9IDA7IGogPCBjaGVja3MubGVuZ3RoOyBqKysgKSBpZiAoIGNoZWNrc1tqXS5jaGVja2VkICkge1xyXG5cdFx0XHRcdGlmICggIWtlcHQgKSB7XHJcblx0XHRcdFx0XHRrZXB0ID0gdHJ1ZTtcclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0Y2hlY2tzW2pdLmNoZWNrZWQgPSBmYWxzZTtcclxuXHRcdFx0XHRcdGNoZWNrc1tqXS5zZXRBdHRyaWJ1dGUoICdhcmlhLWNoZWNrZWQnLCAnZmFsc2UnICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLnN5bmNfZGVmYXVsdF92YWx1ZV9sb2NrKCBwYW5lbCApO1xyXG5cdFx0XHR0aGlzLnN5bmNfcGxhY2Vob2xkZXJfbG9jayggcGFuZWwgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tIG9uZS10aW1lIGJvb3RzdHJhcCBvZiBhIHBhbmVsIC0tLS1cclxuXHRcdHN0YXRpYyBib290c3RyYXBfcGFuZWwocGFuZWwpIHtcclxuXHRcdFx0aWYgKCAhcGFuZWwgKSByZXR1cm47XHJcblx0XHRcdGlmICggIXBhbmVsLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX29wdGlvbnNfZWRpdG9yJyApICkgcmV0dXJuOyAvLyBvbmx5IHNlbGVjdC1saWtlIFVJc1xyXG5cdFx0XHRpZiAoIHBhbmVsLmRhdGFzZXQuc2VsZWN0YmFzZV9ib290c3RyYXBwZWQgPT09ICcxJyApIHtcclxuXHRcdFx0XHR0aGlzLmVuc3VyZV9zb3J0YWJsZSggcGFuZWwgKTtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMucmVidWlsZF9pZl9lbXB0eSggcGFuZWwgKTtcclxuXHRcdFx0dGhpcy5lbnN1cmVfc29ydGFibGUoIHBhbmVsICk7XHJcblx0XHRcdHBhbmVsLmRhdGFzZXQuc2VsZWN0YmFzZV9ib290c3RyYXBwZWQgPSAnMSc7XHJcblxyXG5cdFx0XHR0aGlzLnN5bmNfZGVmYXVsdF92YWx1ZV9sb2NrKCBwYW5lbCApO1xyXG5cdFx0XHR0aGlzLnN5bmNfcGxhY2Vob2xkZXJfbG9jayggcGFuZWwgKTtcclxuXHRcdFx0dGhpcy5zeW5jX3ZhbHVlX2lucHV0c192aXNpYmlsaXR5KCBwYW5lbCApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIC0tLS0gaG9vayBpbnRvIGluc3BlY3RvciBsaWZlY3ljbGUgKGZpcmVzIE9OQ0UpIC0tLS1cclxuXHRcdHN0YXRpYyB3aXJlX29uY2UoKSB7XHJcblx0XHRcdGlmICggQ29yZS5fX3NlbGVjdGJhc2Vfd2lyZWQgKSByZXR1cm47XHJcblx0XHRcdENvcmUuX19zZWxlY3RiYXNlX3dpcmVkID0gdHJ1ZTtcclxuXHJcblx0XHRcdGNvbnN0IG9uX3JlYWR5X29yX3JlbmRlciA9IChldikgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHBhbmVsID0gZXY/LmRldGFpbD8ucGFuZWw7XHJcblx0XHRcdFx0Y29uc3QgZmllbGQgPSBldj8uZGV0YWlsPy5lbCB8fCBldj8uZGV0YWlsPy5maWVsZCB8fCBudWxsO1xyXG5cdFx0XHRcdGlmICggIXBhbmVsICkgcmV0dXJuO1xyXG5cdFx0XHRcdGlmICggZmllbGQgKSBwYW5lbC5fX3NlbGVjdGJhc2VfZmllbGQgPSBmaWVsZDtcclxuXHRcdFx0XHR0aGlzLmJvb3RzdHJhcF9wYW5lbCggcGFuZWwgKTtcclxuXHRcdFx0XHQvLyBJZiB0aGUgaW5zcGVjdG9yIHJvb3Qgd2FzIHJlbW91bnRlZCwgZW5zdXJlIHJvb3QgbGlzdGVuZXJzIGFyZSAocmUpYm91bmQuXHJcblx0XHRcdFx0dGhpcy53aXJlX3Jvb3RfbGlzdGVuZXJzKCk7XHJcblx0XHRcdH07XHJcblxyXG5cdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCAnd3BiY19iZmJfaW5zcGVjdG9yX3JlYWR5Jywgb25fcmVhZHlfb3JfcmVuZGVyICk7XHJcblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICd3cGJjX2JmYl9pbnNwZWN0b3JfcmVuZGVyJywgb25fcmVhZHlfb3JfcmVuZGVyICk7XHJcblxyXG5cdFx0XHR0aGlzLndpcmVfcm9vdF9saXN0ZW5lcnMoKTtcclxuXHRcdH1cclxuXHJcblx0XHRzdGF0aWMgd2lyZV9yb290X2xpc3RlbmVycygpIHtcclxuXHJcblx0XHRcdC8vIElmIGFscmVhZHkgd2lyZWQgQU5EIHRoZSBzdG9yZWQgcm9vdCBpcyBzdGlsbCBpbiB0aGUgRE9NLCBiYWlsIG91dC5cclxuXHRcdFx0aWYgKCB0aGlzLl9fcm9vdF93aXJlZCAmJiB0aGlzLl9fcm9vdF9ub2RlPy5pc0Nvbm5lY3RlZCApIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IHJvb3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InICk7XHJcblx0XHRcdGlmICggIXJvb3QgKSB7XHJcblx0XHRcdFx0Ly8gUm9vdCBtaXNzaW5nIChlLmcuLCBTUEEgcmUtcmVuZGVyKSDigJQgY2xlYXIgZmxhZ3Mgc28gd2UgY2FuIHdpcmUgbGF0ZXIuXHJcblx0XHRcdFx0dGhpcy5fX3Jvb3Rfd2lyZWQgPSBmYWxzZTtcclxuXHRcdFx0XHR0aGlzLl9fcm9vdF9ub2RlICA9IG51bGw7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLl9fcm9vdF9ub2RlICAgICAgICAgICAgICAgICAgID0gcm9vdDtcclxuXHRcdFx0dGhpcy5fX3Jvb3Rfd2lyZWQgICAgICAgICAgICAgICAgICA9IHRydWU7XHJcblx0XHRcdHJvb3QuZGF0YXNldC5zZWxlY3RiYXNlX3Jvb3Rfd2lyZWQgPSAnMSc7XHJcblxyXG5cdFx0XHRjb25zdCBnZXRfcGFuZWwgPSAodGFyZ2V0KSA9PlxyXG5cdFx0XHRcdHRhcmdldD8uY2xvc2VzdD8uKCAnLndwYmNfYmZiX19pbnNwZWN0b3JfX2JvZHknICkgfHxcclxuXHRcdFx0XHRyb290LnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2luc3BlY3Rvcl9fYm9keScgKSB8fCBudWxsO1xyXG5cclxuXHRcdFx0Ly8gQ2xpY2sgaGFuZGxlcnM6IGFkZCAvIGRlbGV0ZSAvIGR1cGxpY2F0ZVxyXG5cdFx0XHRyb290LmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIChlKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcGFuZWwgPSBnZXRfcGFuZWwoIGUudGFyZ2V0ICk7XHJcblx0XHRcdFx0aWYgKCAhcGFuZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdHRoaXMuYm9vdHN0cmFwX3BhbmVsKCBwYW5lbCApO1xyXG5cclxuXHRcdFx0XHRjb25zdCB1aSA9IHRoaXMudWk7XHJcblxyXG5cdFx0XHRcdC8vIEV4aXN0aW5nIFwiQWRkIG9wdGlvblwiIGJ1dHRvbiAodG9wIHRvb2xiYXIpXHJcblx0XHRcdFx0Y29uc3QgYWRkID0gZS50YXJnZXQuY2xvc2VzdD8uKCB1aS5hZGRfYnRuICk7XHJcblx0XHRcdFx0aWYgKCBhZGQgKSB7XHJcblx0XHRcdFx0XHR0aGlzLmFwcGVuZF9yb3coIHBhbmVsLCB7IGxhYmVsOiAnJywgdmFsdWU6ICcnLCBzZWxlY3RlZDogZmFsc2UgfSApO1xyXG5cdFx0XHRcdFx0dGhpcy5jb21taXRfb3B0aW9ucyggcGFuZWwgKTtcclxuXHRcdFx0XHRcdHRoaXMuc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eSggcGFuZWwgKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdC8vIERyb3Bkb3duIG1lbnUgYWN0aW9ucy5cclxuXHRcdFx0XHRjb25zdCBtZW51X2FjdGlvbiA9IGUudGFyZ2V0LmNsb3Nlc3Q/LiggdWkubWVudV9hY3Rpb24gKTtcclxuXHRcdFx0XHRpZiAoIG1lbnVfYWN0aW9uICkge1xyXG5cdFx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0XHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHJcblx0XHRcdFx0XHRjb25zdCBhY3Rpb24gPSAobWVudV9hY3Rpb24uZ2V0QXR0cmlidXRlKCAnZGF0YS1hY3Rpb24nICkgfHwgJycpLnRvTG93ZXJDYXNlKCk7XHJcblx0XHRcdFx0XHRjb25zdCByb3cgICAgPSBtZW51X2FjdGlvbi5jbG9zZXN0Py4oIHVpLnJvdyApO1xyXG5cclxuXHRcdFx0XHRcdGlmICggIXJvdyApIHtcclxuXHRcdFx0XHRcdFx0dGhpcy5jbG9zZV9kcm9wZG93biggbWVudV9hY3Rpb24gKTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdGlmICggJ2FkZF9hZnRlcicgPT09IGFjdGlvbiApIHtcclxuXHRcdFx0XHRcdFx0Ly8gQWRkIGVtcHR5IHJvdyBhZnRlciBjdXJyZW50XHJcblx0XHRcdFx0XHRcdGNvbnN0IHByZXZfY291bnQgPSB0aGlzLmdldF9saXN0KCBwYW5lbCApPy5jaGlsZHJlbi5sZW5ndGggfHwgMDtcclxuXHRcdFx0XHRcdFx0dGhpcy5hcHBlbmRfcm93KCBwYW5lbCwgeyBsYWJlbDogJycsIHZhbHVlOiAnJywgc2VsZWN0ZWQ6IGZhbHNlIH0gKTtcclxuXHRcdFx0XHRcdFx0Ly8gTW92ZSB0aGUgbmV3bHkgYWRkZWQgbGFzdCByb3cganVzdCBhZnRlciBjdXJyZW50IHJvdyB0byBwcmVzZXJ2ZSBcImFkZCBhZnRlclwiXHJcblx0XHRcdFx0XHRcdGNvbnN0IGxpc3QgPSB0aGlzLmdldF9saXN0KCBwYW5lbCApO1xyXG5cdFx0XHRcdFx0XHRpZiAoIGxpc3QgJiYgbGlzdC5sYXN0RWxlbWVudENoaWxkICYmIGxpc3QubGFzdEVsZW1lbnRDaGlsZCAhPT0gcm93ICkge1xyXG5cdFx0XHRcdFx0XHRcdHRoaXMuaW5zZXJ0X2FmdGVyKCBsaXN0Lmxhc3RFbGVtZW50Q2hpbGQsIHJvdyApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdHRoaXMuY29tbWl0X29wdGlvbnMoIHBhbmVsICk7XHJcblx0XHRcdFx0XHRcdHRoaXMuc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eSggcGFuZWwgKTtcclxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoICdkdXBsaWNhdGUnID09PSBhY3Rpb24gKSB7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGxibCA9IChyb3cucXVlcnlTZWxlY3RvciggdWkubGFiZWwgKSB8fCB7fSkudmFsdWUgfHwgJyc7XHJcblx0XHRcdFx0XHRcdGNvbnN0IHZhbCA9IChyb3cucXVlcnlTZWxlY3RvciggdWkudmFsdWUgKSB8fCB7fSkudmFsdWUgfHwgJyc7XHJcblx0XHRcdFx0XHRcdGNvbnN0IHNlbCA9ICEhKChyb3cucXVlcnlTZWxlY3RvciggdWkudG9nZ2xlICkgfHwge30pLmNoZWNrZWQpO1xyXG5cdFx0XHRcdFx0XHR0aGlzLmFwcGVuZF9yb3coIHBhbmVsLCB7IGxhYmVsOiBsYmwsIHZhbHVlOiB2YWwsIHNlbGVjdGVkOiBzZWwsIHVpZDogdGhpcy5tYWtlX3VpZCgpIH0gKTtcclxuXHRcdFx0XHRcdFx0Ly8gUGxhY2UgdGhlIG5ldyByb3cgcmlnaHQgYWZ0ZXIgdGhlIGN1cnJlbnQuXHJcblx0XHRcdFx0XHRcdGNvbnN0IGxpc3QgPSB0aGlzLmdldF9saXN0KCBwYW5lbCApO1xyXG5cclxuXHRcdFx0XHRcdFx0aWYgKCBsaXN0ICYmIGxpc3QubGFzdEVsZW1lbnRDaGlsZCAmJiBsaXN0Lmxhc3RFbGVtZW50Q2hpbGQgIT09IHJvdyApIHtcclxuXHRcdFx0XHRcdFx0XHR0aGlzLmluc2VydF9hZnRlciggbGlzdC5sYXN0RWxlbWVudENoaWxkLCByb3cgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR0aGlzLmVuZm9yY2Vfc2luZ2xlX2RlZmF1bHQoIHBhbmVsLCBudWxsICk7XHJcblx0XHRcdFx0XHRcdHRoaXMuY29tbWl0X29wdGlvbnMoIHBhbmVsICk7XHJcblx0XHRcdFx0XHRcdHRoaXMuc3luY192YWx1ZV9pbnB1dHNfdmlzaWJpbGl0eSggcGFuZWwgKTtcclxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoICdyZW1vdmUnID09PSBhY3Rpb24gKSB7XHJcblx0XHRcdFx0XHRcdGlmICggcm93ICYmIHJvdy5wYXJlbnROb2RlICkgcm93LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIHJvdyApO1xyXG5cdFx0XHRcdFx0XHR0aGlzLmNvbW1pdF9vcHRpb25zKCBwYW5lbCApO1xyXG5cdFx0XHRcdFx0XHR0aGlzLnN5bmNfdmFsdWVfaW5wdXRzX3Zpc2liaWxpdHkoIHBhbmVsICk7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0dGhpcy5jbG9zZV9kcm9wZG93biggbWVudV9hY3Rpb24gKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHR9LCB0cnVlICk7XHJcblxyXG5cclxuXHRcdFx0Ly8gSW5wdXQgZGVsZWdhdGlvbi5cclxuXHRcdFx0cm9vdC5hZGRFdmVudExpc3RlbmVyKCAnaW5wdXQnLCAoZSkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHBhbmVsID0gZ2V0X3BhbmVsKCBlLnRhcmdldCApO1xyXG5cdFx0XHRcdGlmICggISBwYW5lbCApIHtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0Y29uc3QgdWkgICAgICAgICAgICAgICAgPSB0aGlzLnVpO1xyXG5cdFx0XHRcdGNvbnN0IGlzX2xhYmVsX29yX3ZhbHVlID0gZS50YXJnZXQuY2xhc3NMaXN0Py5jb250YWlucyggJ3dwYmNfYmZiX19vcHQtbGFiZWwnICkgfHwgZS50YXJnZXQuY2xhc3NMaXN0Py5jb250YWlucyggJ3dwYmNfYmZiX19vcHQtdmFsdWUnICk7XHJcblx0XHRcdFx0Y29uc3QgaXNfdG9nZ2xlICAgICAgICAgPSBlLnRhcmdldC5jbGFzc0xpc3Q/LmNvbnRhaW5zKCAnd3BiY19iZmJfX29wdC1zZWxlY3RlZC1jaGsnICk7XHJcblx0XHRcdFx0Y29uc3QgaXNfbXVsdGlwbGUgICAgICAgPSBlLnRhcmdldC5tYXRjaGVzPy4oIHVpLm11bHRpcGxlX2NoayApO1xyXG5cdFx0XHRcdGNvbnN0IGlzX2RlZmF1bHRfdGV4dCAgID0gZS50YXJnZXQubWF0Y2hlcz8uKCB1aS5kZWZhdWx0X3RleHQgKTtcclxuXHRcdFx0XHRjb25zdCBpc192YWx1ZV9kaWZmZXJzICA9IGUudGFyZ2V0Lm1hdGNoZXM/LiggdWkudmFsdWVfZGlmZmVyc19jaGsgKTtcclxuXHJcblx0XHRcdFx0Ly8gSGFuZGxlIFwidmFsdWUgZGlmZmVyc1wiIHRvZ2dsZSBsaXZlXHJcblx0XHRcdFx0aWYgKCBpc192YWx1ZV9kaWZmZXJzICkge1xyXG5cdFx0XHRcdFx0dGhpcy5zeW5jX3ZhbHVlX2lucHV0c192aXNpYmlsaXR5KCBwYW5lbCApO1xyXG5cdFx0XHRcdFx0dGhpcy5jb21taXRfb3B0aW9ucyggcGFuZWwgKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdC8vIFRyYWNrIHdoZW4gdGhlIHVzZXIgZWRpdHMgVkFMVUUgZXhwbGljaXRseVxyXG5cdFx0XHRcdGlmICggZS50YXJnZXQuY2xhc3NMaXN0Py5jb250YWlucyggJ3dwYmNfYmZiX19vcHQtdmFsdWUnICkgKSB7XHJcblx0XHRcdFx0XHRjb25zdCByb3cgPSBlLnRhcmdldC5jbG9zZXN0KCB0aGlzLnVpLnJvdyApO1xyXG5cdFx0XHRcdFx0dGhpcy5tYXJrX3Jvd192YWx1ZV91c2VyX3RvdWNoZWQoIHJvdyApO1xyXG5cdFx0XHRcdFx0Ly8gS2VlcCB0aGUgY2FjaGUgdXBkYXRlZCBzbyB0b2dnbGluZyBPRkYvT04gbGF0ZXIgcmVzdG9yZXMgdGhlIGxhdGVzdCBjdXN0b20gdmFsdWVcclxuXHRcdFx0XHRcdGUudGFyZ2V0LmRhdGFzZXQuY2FjaGVkX3ZhbHVlID0gZS50YXJnZXQudmFsdWUgfHwgJyc7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHQvLyBBdXRvLWZpbGwgVkFMVUUgZnJvbSBMQUJFTCBpZiB2YWx1ZSBpcyBmcmVzaCAoYW5kIGRpZmZlcnMgaXMgT04pOyBpZiBkaWZmZXJzIGlzIE9GRiwgd2UgbWlycm9yIGFueXdheSBpbiBjb21taXRcclxuXHRcdFx0XHRpZiAoIGUudGFyZ2V0LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fb3B0LWxhYmVsJyApICkge1xyXG5cdFx0XHRcdFx0Y29uc3Qgcm93ICAgICA9IGUudGFyZ2V0LmNsb3Nlc3QoIHVpLnJvdyApO1xyXG5cdFx0XHRcdFx0Y29uc3QgdmFsX2luICA9IHJvdz8ucXVlcnlTZWxlY3RvciggdWkudmFsdWUgKTtcclxuXHRcdFx0XHRcdGNvbnN0IGRpZmZlcnMgPSB0aGlzLmlzX3ZhbHVlX2RpZmZlcnNfZW5hYmxlZCggcGFuZWwgKTtcclxuXHJcblx0XHRcdFx0XHRpZiAoIHZhbF9pbiApIHtcclxuXHRcdFx0XHRcdFx0aWYgKCAhZGlmZmVycyApIHtcclxuXHRcdFx0XHRcdFx0XHQvLyBzaW5nbGUtaW5wdXQgbW9kZTogbWlycm9yIGh1bWFuIGxhYmVsIHdpdGggbWluaW1hbCBlc2NhcGluZ1xyXG5cdFx0XHRcdFx0XHRcdHZhbF9pbi52YWx1ZSA9IHRoaXMuYnVpbGRfdmFsdWVfZnJvbV9sYWJlbCggZS50YXJnZXQudmFsdWUsIGZhbHNlICk7XHJcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoICF0aGlzLmlzX3Jvd192YWx1ZV91c2VyX3RvdWNoZWQoIHJvdyApICkge1xyXG5cdFx0XHRcdFx0XHRcdC8vIHNlcGFyYXRlLXZhbHVlIG1vZGUsIG9ubHkgd2hpbGUgZnJlc2hcclxuXHRcdFx0XHRcdFx0XHR2YWxfaW4udmFsdWUgPSB0aGlzLmJ1aWxkX3ZhbHVlX2Zyb21fbGFiZWwoIGUudGFyZ2V0LnZhbHVlLCB0cnVlICk7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblxyXG5cclxuXHRcdFx0XHRpZiAoIGlzX2xhYmVsX29yX3ZhbHVlIHx8IGlzX3RvZ2dsZSB8fCBpc19tdWx0aXBsZSApIHtcclxuXHRcdFx0XHRcdGlmICggaXNfdG9nZ2xlICkgZS50YXJnZXQuc2V0QXR0cmlidXRlKCAnYXJpYS1jaGVja2VkJywgZS50YXJnZXQuY2hlY2tlZCA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHRcdFx0XHRcdGlmICggaXNfdG9nZ2xlIHx8IGlzX211bHRpcGxlICkgdGhpcy5lbmZvcmNlX3NpbmdsZV9kZWZhdWx0KCBwYW5lbCwgaXNfdG9nZ2xlID8gZS50YXJnZXQgOiBudWxsICk7XHJcblx0XHRcdFx0XHR0aGlzLmNvbW1pdF9vcHRpb25zKCBwYW5lbCApO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0aWYgKCBpc19kZWZhdWx0X3RleHQgKSB7XHJcblx0XHRcdFx0XHR0aGlzLnN5bmNfZGVmYXVsdF92YWx1ZV9sb2NrKCBwYW5lbCApO1xyXG5cdFx0XHRcdFx0dGhpcy5zeW5jX3BsYWNlaG9sZGVyX2xvY2soIHBhbmVsICk7XHJcblx0XHRcdFx0XHRjb25zdCBob2xkZXIgPSB0aGlzLmdldF9ob2xkZXIoIHBhbmVsICk7XHJcblx0XHRcdFx0XHRpZiAoIGhvbGRlciApIHtcclxuXHRcdFx0XHRcdFx0aG9sZGVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0XHRob2xkZXIuZGlzcGF0Y2hFdmVudCggbmV3IEV2ZW50KCAnY2hhbmdlJywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSwgdHJ1ZSApO1xyXG5cclxuXHJcblx0XHRcdC8vIENoYW5nZSBkZWxlZ2F0aW9uXHJcblx0XHRcdHJvb3QuYWRkRXZlbnRMaXN0ZW5lciggJ2NoYW5nZScsIChlKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcGFuZWwgPSBnZXRfcGFuZWwoIGUudGFyZ2V0ICk7XHJcblx0XHRcdFx0aWYgKCAhcGFuZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdGNvbnN0IHVpICAgICAgICA9IHRoaXMudWk7XHJcblx0XHRcdFx0Y29uc3QgaXNfdG9nZ2xlID0gZS50YXJnZXQuY2xhc3NMaXN0Py5jb250YWlucyggJ3dwYmNfYmZiX19vcHQtc2VsZWN0ZWQtY2hrJyApO1xyXG5cdFx0XHRcdGNvbnN0IGlzX211bHRpICA9IGUudGFyZ2V0Lm1hdGNoZXM/LiggdWkubXVsdGlwbGVfY2hrICk7XHJcblx0XHRcdFx0aWYgKCAhaXNfdG9nZ2xlICYmICFpc19tdWx0aSApIHJldHVybjtcclxuXHJcblx0XHRcdFx0aWYgKCBpc190b2dnbGUgKSBlLnRhcmdldC5zZXRBdHRyaWJ1dGUoICdhcmlhLWNoZWNrZWQnLCBlLnRhcmdldC5jaGVja2VkID8gJ3RydWUnIDogJ2ZhbHNlJyApO1xyXG5cdFx0XHRcdHRoaXMuZW5mb3JjZV9zaW5nbGVfZGVmYXVsdCggcGFuZWwsIGlzX3RvZ2dsZSA/IGUudGFyZ2V0IDogbnVsbCApO1xyXG5cdFx0XHRcdHRoaXMuY29tbWl0X29wdGlvbnMoIHBhbmVsICk7XHJcblx0XHRcdH0sIHRydWUgKTtcclxuXHJcblx0XHRcdC8vIExhenkgYm9vdHN0cmFwXHJcblx0XHRcdHJvb3QuYWRkRXZlbnRMaXN0ZW5lciggJ21vdXNlZW50ZXInLCAoZSkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHBhbmVsID0gZ2V0X3BhbmVsKCBlLnRhcmdldCApO1xyXG5cdFx0XHRcdGlmICggcGFuZWwgJiYgZS50YXJnZXQ/LmNsb3Nlc3Q/LiggdGhpcy51aS5saXN0ICkgKSB0aGlzLmJvb3RzdHJhcF9wYW5lbCggcGFuZWwgKTtcclxuXHRcdFx0fSwgdHJ1ZSApO1xyXG5cclxuXHRcdFx0cm9vdC5hZGRFdmVudExpc3RlbmVyKCAnbW91c2Vkb3duJywgKGUpID0+IHtcclxuXHRcdFx0XHRjb25zdCBwYW5lbCA9IGdldF9wYW5lbCggZS50YXJnZXQgKTtcclxuXHRcdFx0XHRpZiAoIHBhbmVsICYmIGUudGFyZ2V0Py5jbG9zZXN0Py4oIHRoaXMudWkuZHJhZ19oYW5kbGUgKSApIHRoaXMuYm9vdHN0cmFwX3BhbmVsKCBwYW5lbCApO1xyXG5cdFx0XHR9LCB0cnVlICk7XHJcblx0XHR9XHJcblxyXG5cdH07XHJcblxyXG5cdHRyeSB7IENvcmUuV1BCQ19CRkJfU2VsZWN0X0Jhc2Uud2lyZV9vbmNlKCk7IH0gY2F0Y2ggKF8pIHt9XHJcblx0Ly8gVHJ5IGltbWVkaWF0ZWx5IChpZiByb290IGlzIGFscmVhZHkgaW4gRE9NKSwgdGhlbiBhZ2FpbiBvbiBET01Db250ZW50TG9hZGVkLlxyXG5cdENvcmUuV1BCQ19CRkJfU2VsZWN0X0Jhc2Uud2lyZV9yb290X2xpc3RlbmVycygpO1xyXG5cclxuXHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4geyBDb3JlLldQQkNfQkZCX1NlbGVjdF9CYXNlLndpcmVfcm9vdF9saXN0ZW5lcnMoKTsgIH0pO1xyXG5cclxufSggd2luZG93ICkpOyIsIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4vLyA9PSBGaWxlICAvaW5jbHVkZXMvcGFnZS1mb3JtLWJ1aWxkZXIvX291dC9jb3JlL2JmYi11aS5qcyA9PSB8IDIwMjUtMDktMTAgMTU6NDdcclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbihmdW5jdGlvbiAodywgZCkge1xyXG5cdCd1c2Ugc3RyaWN0JztcclxuXHJcblx0Ly8gU2luZ2xlIGdsb2JhbCBuYW1lc3BhY2UgKGlkZW1wb3RlbnQgJiBsb2FkLW9yZGVyIHNhZmUpLlxyXG5cdGNvbnN0IENvcmUgPSAody5XUEJDX0JGQl9Db3JlID0gdy5XUEJDX0JGQl9Db3JlIHx8IHt9KTtcclxuXHRjb25zdCBVSSAgID0gKENvcmUuVUkgPSBDb3JlLlVJIHx8IHt9KTtcclxuXHJcblx0Ly8gLS0tIEhpZ2hsaWdodCBFbGVtZW50LCAgbGlrZSBHZW5lcmF0b3IgYnJuICAtICBUaW55IFVJIGhlbHBlcnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0VUkuX3B1bHNlX3RpbWVycyA9IFVJLl9wdWxzZV90aW1lcnMgfHwgbmV3IE1hcCgpOyAvLyBlbCAtPiB0aW1lcl9pZFxyXG5cdFVJLl9wdWxzZV9tZXRhICAgPSBVSS5fcHVsc2VfbWV0YSAgIHx8IG5ldyBNYXAoKTsgLy8gZWwgLT4geyB0b2tlbiwgbGFzdF90cywgZGVib3VuY2VfaWQsIGNvbG9yX3NldCB9XHJcblx0Ly8gUHVsc2UgdHVuaW5nIChtaWxsaXNlY29uZHMpLlxyXG5cdFVJLlBVTFNFX1RIUk9UVExFX01TICA9IE51bWJlci5pc0Zpbml0ZSggVUkuUFVMU0VfVEhST1RUTEVfTVMgKSA/IFVJLlBVTFNFX1RIUk9UVExFX01TIDogNTAwO1xyXG5cdFVJLlBVTFNFX0RFQk9VTkNFX01TICA9IE51bWJlci5pc0Zpbml0ZSggVUkuUFVMU0VfREVCT1VOQ0VfTVMgKSA/IFVJLlBVTFNFX0RFQk9VTkNFX01TIDogNzUwO1xyXG5cclxuXHQvLyBEZWJvdW5jZSBTVFJVQ1RVUkVfQ0hBTkdFIGZvciBjb250aW51b3VzIGluc3BlY3RvciBjb250cm9scyAoc2xpZGVycyAvIHNjcnViYmluZykuXHJcblx0Ly8gVHVuZTogMTgwLi4zNTAgaXMgdXN1YWxseSBhIHN3ZWV0IHNwb3QuXHJcblx0VUkuU1RSVUNUVVJFX0NIQU5HRV9ERUJPVU5DRV9NUyA9IE51bWJlci5pc0Zpbml0ZSggVUkuU1RSVUNUVVJFX0NIQU5HRV9ERUJPVU5DRV9NUyApID8gVUkuU1RSVUNUVVJFX0NIQU5HRV9ERUJPVU5DRV9NUyA6IDE4MDtcclxuXHQvLyBDaGFuZ2UgdGhpcyB0byB0dW5lIHNwZWVkOiA1MC4uMTIwIG1zIGlzIGEgZ29vZCByYW5nZS4gQ2FuIGJlIGNvbmZpZ3VyZWQgaW4gPGRpdiBkYXRhLWxlbi1ncm91cCBkYXRhLWxlbi10aHJvdHRsZT1cIjE4MFwiPi4uLjwvZGl2Pi5cclxuXHRVSS5WQUxVRV9TTElERVJfVEhST1RUTEVfTVMgPSBOdW1iZXIuaXNGaW5pdGUoIFVJLlZBTFVFX1NMSURFUl9USFJPVFRMRV9NUyApID8gVUkuVkFMVUVfU0xJREVSX1RIUk9UVExFX01TIDogMTIwO1xyXG5cclxuXHQvKipcclxuXHQgKiBDYW5jZWwgYW55IHJ1bm5pbmcgcHVsc2Ugc2VxdWVuY2UgZm9yIGFuIGVsZW1lbnQuXHJcblx0ICogVXNlcyB0b2tlbiBpbnZhbGlkYXRpb24gc28gYWxyZWFkeS1zY2hlZHVsZWQgY2FsbGJhY2tzIGJlY29tZSBuby1vcHMuXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbFxyXG5cdCAqL1xyXG5cdFVJLmNhbmNlbF9wdWxzZSA9IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0aWYgKCAhZWwgKSB7IHJldHVybjsgfVxyXG5cdFx0dHJ5IHtcclxuXHRcdFx0Y2xlYXJUaW1lb3V0KCBVSS5fcHVsc2VfdGltZXJzLmdldCggZWwgKSApO1xyXG5cdFx0fSBjYXRjaCAoIF8gKSB7fVxyXG5cdFx0VUkuX3B1bHNlX3RpbWVycy5kZWxldGUoIGVsICk7XHJcblxyXG5cdFx0dmFyIG1ldGEgPSBVSS5fcHVsc2VfbWV0YS5nZXQoIGVsICkgfHwge307XHJcblx0XHRtZXRhLnRva2VuID0gKE51bWJlci5pc0Zpbml0ZSggbWV0YS50b2tlbiApID8gbWV0YS50b2tlbiA6IDApICsgMTtcclxuXHRcdG1ldGEuY29sb3Jfc2V0ID0gZmFsc2U7XHJcblx0XHR0cnkgeyBlbC5jbGFzc0xpc3QucmVtb3ZlKCAnd3BiY19iZmJfX3Njcm9sbC1wdWxzZScsICd3cGJjX2JmYl9faGlnaGxpZ2h0LXB1bHNlJyApOyB9IGNhdGNoICggXyApIHt9XHJcblx0XHR0cnkgeyBlbC5zdHlsZS5yZW1vdmVQcm9wZXJ0eSggJy0td3BiYy1iZmItcHVsc2UtY29sb3InICk7IH0gY2F0Y2ggKCBfICkge31cclxuXHRcdFVJLl9wdWxzZV9tZXRhLnNldCggZWwsIG1ldGEgKTtcclxuXHRcdHRyeSB7IGNsZWFyVGltZW91dCggbWV0YS5kZWJvdW5jZV9pZCApOyB9IGNhdGNoICggXyApIHt9XHJcblx0XHRtZXRhLmRlYm91bmNlX2lkID0gMDtcclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHQgKiBGb3JjZS1yZXN0YXJ0IGEgQ1NTIGFuaW1hdGlvbiBvbiBhIGNsYXNzLlxyXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9IGNsc1xyXG5cdCAqL1xyXG5cdFVJLl9yZXN0YXJ0X2Nzc19hbmltYXRpb24gPSBmdW5jdGlvbiAoZWwsIGNscykge1xyXG5cdFx0aWYgKCAhIGVsICkgeyByZXR1cm47IH1cclxuXHRcdHRyeSB7XHJcblx0XHRcdGVsLmNsYXNzTGlzdC5yZW1vdmUoIGNscyApO1xyXG5cdFx0fSBjYXRjaCAoIF8gKSB7fVxyXG5cdFx0Ly8gRm9yY2UgcmVmbG93IHNvIHRoZSBuZXh0IGFkZCgpIHJldHJpZ2dlcnMgdGhlIGtleWZyYW1lcy5cclxuXHRcdHZvaWQgZWwub2Zmc2V0V2lkdGg7XHJcblx0XHR0cnkge1xyXG5cdFx0XHRlbC5jbGFzc0xpc3QuYWRkKCBjbHMgKTtcclxuXHRcdH0gY2F0Y2ggKCBfICkge31cclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHRcdFNpbmdsZSBwdWxzZSAoYmFjay1jb21wYXQpLlxyXG5cdFx0QHBhcmFtIHtIVE1MRWxlbWVudH0gZWxcclxuXHRcdEBwYXJhbSB7bnVtYmVyfSBkdXJfbXNcclxuXHQgKi9cclxuXHRVSS5wdWxzZV9vbmNlID0gZnVuY3Rpb24gKGVsLCBkdXJfbXMpIHtcclxuXHRcdGlmICggISBlbCApIHsgcmV0dXJuOyB9XHJcblx0XHR2YXIgY2xzID0gJ3dwYmNfYmZiX19zY3JvbGwtcHVsc2UnO1xyXG5cdFx0dmFyIG1zICA9IE51bWJlci5pc0Zpbml0ZSggZHVyX21zICkgPyBkdXJfbXMgOiA3MDA7XHJcblxyXG5cdFx0VUkuY2FuY2VsX3B1bHNlKCBlbCApO1xyXG5cclxuXHRcdHZhciBtZXRhICA9IFVJLl9wdWxzZV9tZXRhLmdldCggZWwgKSB8fCB7fTtcclxuXHRcdHZhciB0b2tlbiA9IChOdW1iZXIuaXNGaW5pdGUoIG1ldGEudG9rZW4gKSA/IG1ldGEudG9rZW4gOiAwKSArIDE7XHJcblx0XHRtZXRhLnRva2VuID0gdG9rZW47XHJcblx0XHRVSS5fcHVsc2VfbWV0YS5zZXQoIGVsLCBtZXRhICk7XHJcblxyXG5cdFx0VUkuX3Jlc3RhcnRfY3NzX2FuaW1hdGlvbiggZWwsIGNscyApO1xyXG5cdFx0dmFyIHQgPSBzZXRUaW1lb3V0KCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdC8vIGlnbm9yZSBpZiBhIG5ld2VyIHB1bHNlIHN0YXJ0ZWQuXHJcblx0XHRcdHZhciBtID0gVUkuX3B1bHNlX21ldGEuZ2V0KCBlbCApIHx8IHt9O1xyXG5cdFx0XHRpZiAoIG0udG9rZW4gIT09IHRva2VuICkgeyByZXR1cm47IH1cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRlbC5jbGFzc0xpc3QucmVtb3ZlKCBjbHMgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7fVxyXG5cdFx0XHRVSS5fcHVsc2VfdGltZXJzLmRlbGV0ZSggZWwgKTtcclxuXHRcdH0sIG1zICk7XHJcblx0XHRVSS5fcHVsc2VfdGltZXJzLnNldCggZWwsIHQgKTtcclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHRcdE11bHRpLWJsaW5rIHNlcXVlbmNlIHdpdGggb3B0aW9uYWwgcGVyLWNhbGwgY29sb3Igb3ZlcnJpZGUuXHJcblx0XHRAcGFyYW0ge0hUTUxFbGVtZW50fSBlbFxyXG5cdFx0QHBhcmFtIHtudW1iZXJ9IFt0aW1lcz0zXVxyXG5cdFx0QHBhcmFtIHtudW1iZXJ9IFtvbl9tcz0yODBdXHJcblx0XHRAcGFyYW0ge251bWJlcn0gW29mZl9tcz0xODBdXHJcblx0XHRAcGFyYW0ge3N0cmluZ30gW2hleF9jb2xvcl0gT3B0aW9uYWwgQ1NTIGNvbG9yIChlLmcuICcjZmY0ZDRmJyBvciAncmdiKC4uLiknKS5cclxuXHQgKi9cclxuXHRVSS5wdWxzZV9zZXF1ZW5jZSA9IGZ1bmN0aW9uIChlbCwgdGltZXMsIG9uX21zLCBvZmZfbXMsIGhleF9jb2xvcikge1xyXG5cdFx0aWYgKCAhZWwgfHwgIWQuYm9keS5jb250YWlucyggZWwgKSApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0dmFyIGNscyAgID0gJ3dwYmNfYmZiX19oaWdobGlnaHQtcHVsc2UnO1xyXG5cdFx0dmFyIGNvdW50ID0gTnVtYmVyLmlzRmluaXRlKCB0aW1lcyApID8gdGltZXMgOiAyO1xyXG5cdFx0dmFyIG9uICAgID0gTnVtYmVyLmlzRmluaXRlKCBvbl9tcyApID8gb25fbXMgOiAyODA7XHJcblx0XHR2YXIgb2ZmICAgPSBOdW1iZXIuaXNGaW5pdGUoIG9mZl9tcyApID8gb2ZmX21zIDogMTgwO1xyXG5cclxuXHRcdC8vIFRocm90dGxlOiBhdm9pZCByZWZsb3cgc3BhbSBpZiBjYWxsZWQgcmVwZWF0ZWRseSB3aGlsZSB0eXBpbmcvZHJhZ2dpbmcuXHJcblx0XHR2YXIgbWV0YSA9IFVJLl9wdWxzZV9tZXRhLmdldCggZWwgKSB8fCB7fTtcclxuXHRcdHZhciBub3cgID0gRGF0ZS5ub3coKTtcclxuXHRcdHZhciB0aHJvdHRsZV9tcyA9IE51bWJlci5pc0Zpbml0ZSggVUkuUFVMU0VfVEhST1RUTEVfTVMgKSA/IFVJLlBVTFNFX1RIUk9UVExFX01TIDogMTIwO1xyXG5cdFx0aWYgKCBOdW1iZXIuaXNGaW5pdGUoIG1ldGEubGFzdF90cyApICYmIChub3cgLSBtZXRhLmxhc3RfdHMpIDwgdGhyb3R0bGVfbXMgKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdG1ldGEubGFzdF90cyA9IG5vdztcclxuXHJcblx0XHQvLyBjYW5jZWwgYW55IHJ1bm5pbmcgcHVsc2UgYW5kIHJlc2V0IGNsYXNzICh0b2tlbiBpbnZhbGlkYXRpb24pLlxyXG5cdFx0VUkuY2FuY2VsX3B1bHNlKCBlbCApO1xyXG5cclxuXHRcdC8vIG5ldyB0b2tlbiBmb3IgdGhpcyBydW5cclxuXHRcdHZhciB0b2tlbiA9IChOdW1iZXIuaXNGaW5pdGUoIG1ldGEudG9rZW4gKSA/IG1ldGEudG9rZW4gOiAwKSArIDE7XHJcblx0XHRtZXRhLnRva2VuID0gdG9rZW47XHJcblxyXG5cdFx0dmFyIGhhdmVfY29sb3IgPSAhIWhleF9jb2xvciAmJiB0eXBlb2YgaGV4X2NvbG9yID09PSAnc3RyaW5nJztcclxuXHRcdGlmICggaGF2ZV9jb2xvciApIHtcclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRlbC5zdHlsZS5zZXRQcm9wZXJ0eSggJy0td3BiYy1iZmItcHVsc2UtY29sb3InLCBoZXhfY29sb3IgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7fVxyXG5cdFx0XHRtZXRhLmNvbG9yX3NldCA9IHRydWU7XHJcblx0XHR9XHJcblx0XHRVSS5fcHVsc2VfbWV0YS5zZXQoIGVsLCBtZXRhICk7XHJcblxyXG5cdFx0dmFyIGkgPSAwO1xyXG5cdFx0KGZ1bmN0aW9uIHRpY2soKSB7XHJcblx0XHRcdHZhciBtID0gVUkuX3B1bHNlX21ldGEuZ2V0KCBlbCApIHx8IHt9O1xyXG5cdFx0XHRpZiAoIG0udG9rZW4gIT09IHRva2VuICkge1xyXG5cdFx0XHRcdC8vIGNhbmNlbGVkL3JlcGxhY2VkXHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggaSA+PSBjb3VudCApIHtcclxuXHRcdFx0XHRVSS5fcHVsc2VfdGltZXJzLmRlbGV0ZSggZWwgKTtcclxuXHRcdFx0XHRpZiAoIGhhdmVfY29sb3IgKSB7XHJcblx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHRlbC5zdHlsZS5yZW1vdmVQcm9wZXJ0eSggJy0td3BiYy1iZmItcHVsc2UtY29sb3InICk7XHJcblx0XHRcdFx0XHR9IGNhdGNoICggXyApIHt9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRVSS5fcmVzdGFydF9jc3NfYW5pbWF0aW9uKCBlbCwgY2xzICk7XHJcblx0XHRcdFVJLl9wdWxzZV90aW1lcnMuc2V0KCBlbCwgc2V0VGltZW91dCggZnVuY3Rpb24gKCkgeyAgICAgLy8gT04gLT4gT0ZGXHJcblx0XHRcdFx0dmFyIG0yID0gVUkuX3B1bHNlX21ldGEuZ2V0KCBlbCApIHx8IHt9O1xyXG5cdFx0XHRcdGlmICggbTIudG9rZW4gIT09IHRva2VuICkgeyByZXR1cm47IH1cclxuXHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0ZWwuY2xhc3NMaXN0LnJlbW92ZSggY2xzICk7XHJcblx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdFVJLl9wdWxzZV90aW1lcnMuc2V0KCBlbCwgc2V0VGltZW91dCggZnVuY3Rpb24gKCkgeyAvLyBPRkYgZ2FwIC0+IG5leHRcclxuXHRcdFx0XHRcdHZhciBtMyA9IFVJLl9wdWxzZV9tZXRhLmdldCggZWwgKSB8fCB7fTtcclxuXHRcdFx0XHRcdGlmICggbTMudG9rZW4gIT09IHRva2VuICkgeyByZXR1cm47IH1cclxuXHRcdFx0XHRcdGkrKztcclxuXHRcdFx0XHRcdHRpY2soKTtcclxuXHRcdFx0XHR9LCBvZmYgKSApO1xyXG5cdFx0XHR9LCBvbiApICk7XHJcblx0XHR9KSgpO1xyXG5cdH07XHJcblxyXG5cclxuXHQvKipcclxuXHQgKiBEZWJvdW5jZWQgcXVlcnkgKyBwdWxzZS5cclxuXHQgKiBVc2VmdWwgZm9yIGBpbnB1dGAgZXZlbnRzIChzbGlkZXJzIC8gdHlwaW5nKSB0byBhdm9pZCBmb3JjZWQgcmVmbG93IHNwYW0uXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fHN0cmluZ30gcm9vdF9vcl9zZWxlY3RvclxyXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBzZWxlY3RvclxyXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSB3YWl0X21zXHJcblx0ICogQHBhcmFtIHtudW1iZXJ9IFthXVxyXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBbYl1cclxuXHQgKiBAcGFyYW0ge251bWJlcn0gW2NdXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9IFtjb2xvcl1cclxuXHQgKi9cclxuXHRVSS5wdWxzZV9xdWVyeV9kZWJvdW5jZWQgPSBmdW5jdGlvbiAocm9vdF9vcl9zZWxlY3Rvciwgc2VsZWN0b3IsIHdhaXRfbXMsIGEsIGIsIGMsIGNvbG9yKSB7XHJcblx0XHR2YXIgcm9vdCA9ICh0eXBlb2Ygcm9vdF9vcl9zZWxlY3RvciA9PT0gJ3N0cmluZycpID8gZCA6IChyb290X29yX3NlbGVjdG9yIHx8IGQpO1xyXG5cdFx0dmFyIHNlbCAgPSAodHlwZW9mIHJvb3Rfb3Jfc2VsZWN0b3IgPT09ICdzdHJpbmcnKSA/IHJvb3Rfb3Jfc2VsZWN0b3IgOiBzZWxlY3RvcjtcclxuXHRcdGlmICggIXNlbCApIHsgcmV0dXJuOyB9XHJcblx0XHR2YXIgZWwgPSByb290LnF1ZXJ5U2VsZWN0b3IoIHNlbCApO1xyXG5cdFx0aWYgKCAhZWwgKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBkZWZfbXMgPSBOdW1iZXIuaXNGaW5pdGUoIFVJLlBVTFNFX0RFQk9VTkNFX01TICkgPyBVSS5QVUxTRV9ERUJPVU5DRV9NUyA6IDEyMDtcclxuXHRcdHZhciBtcyAgICAgPSBOdW1iZXIuaXNGaW5pdGUoIHdhaXRfbXMgKSA/IHdhaXRfbXMgOiBkZWZfbXM7XHJcblx0XHR2YXIgbWV0YSA9IFVJLl9wdWxzZV9tZXRhLmdldCggZWwgKSB8fCB7fTtcclxuXHRcdHRyeSB7IGNsZWFyVGltZW91dCggbWV0YS5kZWJvdW5jZV9pZCApOyB9IGNhdGNoICggXyApIHt9XHJcblx0XHRtZXRhLmRlYm91bmNlX2lkID0gc2V0VGltZW91dCggZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRVSS5wdWxzZV9zZXF1ZW5jZSggZWwsIGEsIGIsIGMsIGNvbG9yICk7XHJcblx0XHR9LCBtcyApO1xyXG5cdFx0VUkuX3B1bHNlX21ldGEuc2V0KCBlbCwgbWV0YSApO1xyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdFx0UXVlcnkgKyBwdWxzZTpcclxuXHRcdChCQykgSWYgb25seSAzcmQgYXJnIGlzIGEgbnVtYmVyIGFuZCBubyA0dGgvNXRoIC0+IHNpbmdsZSBsb25nIHB1bHNlLlxyXG5cdFx0T3RoZXJ3aXNlIC0+IHN0cm9uZyBzZXF1ZW5jZSAoZGVmYXVsdHMgM8OXMjgwLzE4MCkuXHJcblx0XHRPcHRpb25hbCA2dGggYXJnOiBjb2xvci5cclxuXHRcdEBwYXJhbSB7SFRNTEVsZW1lbnR8c3RyaW5nfSByb290X29yX3NlbGVjdG9yXHJcblx0XHRAcGFyYW0ge3N0cmluZ30gW3NlbGVjdG9yXVxyXG5cdFx0QHBhcmFtIHtudW1iZXJ9IFthXVxyXG5cdFx0QHBhcmFtIHtudW1iZXJ9IFtiXVxyXG5cclxuXHRcdEBwYXJhbSB7bnVtYmVyfSBbY11cclxuXHJcblx0XHRAcGFyYW0ge3N0cmluZ30gW2NvbG9yXVxyXG5cdCAqL1xyXG5cdFVJLnB1bHNlX3F1ZXJ5ID0gZnVuY3Rpb24gKHJvb3Rfb3Jfc2VsZWN0b3IsIHNlbGVjdG9yLCBhLCBiLCBjLCBjb2xvcikge1xyXG5cdFx0dmFyIHJvb3QgPSAodHlwZW9mIHJvb3Rfb3Jfc2VsZWN0b3IgPT09ICdzdHJpbmcnKSA/IGQgOiAocm9vdF9vcl9zZWxlY3RvciB8fCBkKTtcclxuXHRcdHZhciBzZWwgID0gKHR5cGVvZiByb290X29yX3NlbGVjdG9yID09PSAnc3RyaW5nJykgPyByb290X29yX3NlbGVjdG9yIDogc2VsZWN0b3I7XHJcblx0XHRpZiAoICFzZWwgKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZWwgPSByb290LnF1ZXJ5U2VsZWN0b3IoIHNlbCApO1xyXG5cdFx0aWYgKCAhZWwgKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcbi8vIEJhY2stY29tcGF0OiBVSS5wdWxzZVF1ZXJ5KHJvb3QsIHNlbCwgZHVyX21zKVxyXG5cdFx0aWYgKCBOdW1iZXIuaXNGaW5pdGUoIGEgKSAmJiBiID09PSB1bmRlZmluZWQgJiYgYyA9PT0gdW5kZWZpbmVkICkge1xyXG5cdFx0XHRyZXR1cm4gVUkucHVsc2Vfb25jZSggZWwsIGEgKTtcclxuXHRcdH1cclxuLy8gTmV3OiBzZXF1ZW5jZTsgcGFyYW1zIG9wdGlvbmFsOyBzdXBwb3J0cyBvcHRpb25hbCBjb2xvci5cclxuXHRcdFVJLnB1bHNlX3NlcXVlbmNlKCBlbCwgYSwgYiwgYywgY29sb3IgKTtcclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHRDb252ZW5pZW5jZSBoZWxwZXIgKHNuYWtlX2Nhc2UpIHRvIGNhbGwgYSBzdHJvbmcgcHVsc2Ugd2l0aCBvcHRpb25zLlxyXG5cclxuXHRAcGFyYW0ge0hUTUxFbGVtZW50fSBlbFxyXG5cclxuXHRAcGFyYW0ge09iamVjdH0gW29wdHNdXHJcblxyXG5cdEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50aW1lcz0zXVxyXG5cclxuXHRAcGFyYW0ge251bWJlcn0gW29wdHMub25fbXM9MjgwXVxyXG5cclxuXHRAcGFyYW0ge251bWJlcn0gW29wdHMub2ZmX21zPTE4MF1cclxuXHJcblx0QHBhcmFtIHtzdHJpbmd9IFtvcHRzLmNvbG9yXVxyXG5cdCAqL1xyXG5cdFVJLnB1bHNlX3NlcXVlbmNlX3N0cm9uZyA9IGZ1bmN0aW9uIChlbCwgb3B0cykge1xyXG5cdFx0b3B0cyA9IG9wdHMgfHwge307XHJcblx0XHRVSS5wdWxzZV9zZXF1ZW5jZShcclxuXHRcdFx0ZWwsXHJcblx0XHRcdE51bWJlci5pc0Zpbml0ZSggb3B0cy50aW1lcyApID8gb3B0cy50aW1lcyA6IDMsXHJcblx0XHRcdE51bWJlci5pc0Zpbml0ZSggb3B0cy5vbl9tcyApID8gb3B0cy5vbl9tcyA6IDI4MCxcclxuXHRcdFx0TnVtYmVyLmlzRmluaXRlKCBvcHRzLm9mZl9tcyApID8gb3B0cy5vZmZfbXMgOiAxODAsXHJcblx0XHRcdG9wdHMuY29sb3JcclxuXHRcdCk7XHJcblx0fTtcclxuXHJcblxyXG5cdC8qKlxyXG5cdCAqIEJhc2UgY2xhc3MgZm9yIEJGQiBtb2R1bGVzLlxyXG5cdCAqL1xyXG5cdFVJLldQQkNfQkZCX01vZHVsZSA9IGNsYXNzIHtcclxuXHRcdC8qKiBAcGFyYW0ge1dQQkNfRm9ybV9CdWlsZGVyfSBidWlsZGVyICovXHJcblx0XHRjb25zdHJ1Y3RvcihidWlsZGVyKSB7XHJcblx0XHRcdHRoaXMuYnVpbGRlciA9IGJ1aWxkZXI7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEluaXRpYWxpemUgdGhlIG1vZHVsZS4gKi9cclxuXHRcdGluaXQoKSB7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIENsZWFudXAgdGhlIG1vZHVsZS4gKi9cclxuXHRcdGRlc3Ryb3koKSB7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogQ2VudHJhbCBvdmVybGF5L2NvbnRyb2xzIG1hbmFnZXIgZm9yIGZpZWxkcy9zZWN0aW9ucy5cclxuXHQgKiBQdXJlIFVJIGNvbXBvc2l0aW9uOyBhbGwgYWN0aW9ucyByb3V0ZSBiYWNrIGludG8gdGhlIGJ1aWxkZXIgaW5zdGFuY2UuXHJcblx0ICovXHJcblx0VUkuV1BCQ19CRkJfT3ZlcmxheSA9IGNsYXNzIHtcclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEVuc3VyZSBhbiBvdmVybGF5IGV4aXN0cyBhbmQgaXMgd2lyZWQgdXAgb24gdGhlIGVsZW1lbnQuXHJcblx0XHQgKiBAcGFyYW0ge1dQQkNfRm9ybV9CdWlsZGVyfSBidWlsZGVyXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbCAtIGZpZWxkIG9yIHNlY3Rpb24gZWxlbWVudFxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgZW5zdXJlKGJ1aWxkZXIsIGVsKSB7XHJcblxyXG5cdFx0XHRpZiAoICFlbCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0Y29uc3QgaXNTZWN0aW9uID0gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICk7XHJcblxyXG5cdFx0XHQvLyBsZXQgb3ZlcmxheSA9IGVsLnF1ZXJ5U2VsZWN0b3IoIENvcmUuV1BCQ19CRkJfRE9NLlNFTEVDVE9SUy5vdmVybGF5ICk7XHJcblx0XHRcdGxldCBvdmVybGF5ID0gZWwucXVlcnlTZWxlY3RvciggYDpzY29wZSA+ICR7Q29yZS5XUEJDX0JGQl9ET00uU0VMRUNUT1JTLm92ZXJsYXl9YCApO1xyXG5cdFx0XHRpZiAoICFvdmVybGF5ICkge1xyXG5cdFx0XHRcdG92ZXJsYXkgPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2RpdicsICd3cGJjX2JmYl9fb3ZlcmxheS1jb250cm9scycgKTtcclxuXHRcdFx0XHRlbC5wcmVwZW5kKCBvdmVybGF5ICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIERyYWcgaGFuZGxlLlxyXG5cdFx0XHRpZiAoICFvdmVybGF5LnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2RyYWctaGFuZGxlJyApICkge1xyXG5cdFx0XHRcdGNvbnN0IGRyYWdDbGFzcyA9IGlzU2VjdGlvbiA/ICd3cGJjX2JmYl9fZHJhZy1oYW5kbGUgc2VjdGlvbi1kcmFnLWhhbmRsZScgOiAnd3BiY19iZmJfX2RyYWctaGFuZGxlJztcclxuXHRcdFx0XHRvdmVybGF5LmFwcGVuZENoaWxkKFxyXG5cdFx0XHRcdFx0Q29yZS5XUEJDX0Zvcm1fQnVpbGRlcl9IZWxwZXIuY3JlYXRlX2VsZW1lbnQoICdzcGFuJywgZHJhZ0NsYXNzLCAnPHNwYW4gY2xhc3M9XCJ3cGJjX2ljbl9kcmFnX2luZGljYXRvclwiPjwvc3Bhbj4nIClcclxuXHRcdFx0XHQpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBTRVRUSU5HUyBidXR0b24gKHNob3duIGZvciBib3RoIGZpZWxkcyAmIHNlY3Rpb25zKS5cclxuXHRcdFx0aWYgKCAhb3ZlcmxheS5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19zZXR0aW5ncy1idG4nICkgKSB7XHJcblx0XHRcdFx0Y29uc3Qgc2V0dGluZ3NfYnRuICAgPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2J1dHRvbicsICd3cGJjX2JmYl9fc2V0dGluZ3MtYnRuJywgJzxpIGNsYXNzPVwibWVudV9pY29uIGljb24tMXggd3BiY19pY25fc2V0dGluZ3NcIj48L2k+JyApO1xyXG5cdFx0XHRcdHNldHRpbmdzX2J0bi50eXBlICAgID0gJ2J1dHRvbic7XHJcblx0XHRcdFx0c2V0dGluZ3NfYnRuLnRpdGxlICAgPSAnT3BlbiBzZXR0aW5ncyc7XHJcblx0XHRcdFx0c2V0dGluZ3NfYnRuLm9uY2xpY2sgPSAoZSkgPT4ge1xyXG5cdFx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0XHRcdFx0Ly8gU2VsZWN0IFRISVMgZWxlbWVudCBhbmQgc2Nyb2xsIGl0IGludG8gdmlldy5cclxuXHRcdFx0XHRcdGJ1aWxkZXIuc2VsZWN0X2ZpZWxkKCBlbCwgeyBzY3JvbGxJbnRvVmlldzogdHJ1ZSB9ICk7XHJcblxyXG5cdFx0XHRcdFx0Ly8gQXV0by1vcGVuIEluc3BlY3RvciBmcm9tIHRoZSBvdmVybGF5IOKAnFNldHRpbmdz4oCdIGJ1dHRvbi5cclxuXHRcdFx0XHRcdHdwYmNfYmZiX19kaXNwYXRjaF9ldmVudF9zYWZlKFxyXG5cdFx0XHRcdFx0XHQnd3BiY19iZmI6c2hvd19wYW5lbCcsXHJcblx0XHRcdFx0XHRcdHtcclxuXHRcdFx0XHRcdFx0XHRwYW5lbF9pZDogJ3dwYmNfYmZiX19pbnNwZWN0b3InLFxyXG5cdFx0XHRcdFx0XHRcdHRhYl9pZCAgOiAnd3BiY190YWJfaW5zcGVjdG9yJ1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHQpO1xyXG5cclxuXHRcdFx0XHRcdC8vIFRyeSB0byBicmluZyB0aGUgaW5zcGVjdG9yIGludG8gdmlldyAvIGZvY3VzIGZpcnN0IGlucHV0LlxyXG5cdFx0XHRcdFx0Y29uc3QgaW5zID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICd3cGJjX2JmYl9faW5zcGVjdG9yJyApO1xyXG5cdFx0XHRcdFx0aWYgKCBpbnMgKSB7XHJcblx0XHRcdFx0XHRcdGlucy5zY3JvbGxJbnRvVmlldyggeyBiZWhhdmlvcjogJ3Ntb290aCcsIGJsb2NrOiAnbmVhcmVzdCcgfSApO1xyXG5cdFx0XHRcdFx0XHQvLyBGb2N1cyBmaXJzdCBpbnRlcmFjdGl2ZSBjb250cm9sIChiZXN0LWVmZm9ydCkuXHJcblx0XHRcdFx0XHRcdHNldFRpbWVvdXQoICgpID0+IHtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBmb2N1c2FibGUgPSBpbnMucXVlcnlTZWxlY3RvciggJ2lucHV0LHNlbGVjdCx0ZXh0YXJlYSxidXR0b24sW2NvbnRlbnRlZGl0YWJsZV0sW3RhYmluZGV4XTpub3QoW3RhYmluZGV4PVwiLTFcIl0pJyApO1xyXG5cdFx0XHRcdFx0XHRcdGZvY3VzYWJsZT8uZm9jdXM/LigpO1xyXG5cdFx0XHRcdFx0XHR9LCAyNjAgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9O1xyXG5cclxuXHRcdFx0XHRvdmVybGF5LmFwcGVuZENoaWxkKCBzZXR0aW5nc19idG4gKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0b3ZlcmxheS5zZXRBdHRyaWJ1dGUoICdyb2xlJywgJ3Rvb2xiYXInICk7XHJcblx0XHRcdG92ZXJsYXkuc2V0QXR0cmlidXRlKCAnYXJpYS1sYWJlbCcsIGVsLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19zZWN0aW9uJyApID8gJ1NlY3Rpb24gdG9vbHMnIDogJ0ZpZWxkIHRvb2xzJyApO1xyXG5cclxuXHRcdFx0cmV0dXJuIG92ZXJsYXk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogV1BCQyBMYXlvdXQgQ2hpcHMgaGVscGVyIC0gdmlzdWFsIGxheW91dCBwaWNrZXIgKGNoaXBzKSwgZS5nLiwgXCI1MCUvNTAlXCIsIHRvIGEgc2VjdGlvbiBvdmVybGF5LlxyXG5cdCAqXHJcblx0ICogUmVuZGVycyBFcXVhbC9QcmVzZXRzL0N1c3RvbSBjaGlwcyBpbnRvIGEgaG9zdCBjb250YWluZXIgYW5kIHdpcmVzIHRoZW0gdG8gYXBwbHkgdGhlIGxheW91dC5cclxuXHQgKi9cclxuXHRVSS5XUEJDX0JGQl9MYXlvdXRfQ2hpcHMgPSBjbGFzcyB7XHJcblxyXG5cdFx0LyoqIFJlYWQgcGVyLWNvbHVtbiBtaW4gKHB4KSBmcm9tIENTUyB2YXIgc2V0IGJ5IHRoZSBndWFyZC4gKi9cclxuXHRcdHN0YXRpYyBfZ2V0X2NvbF9taW5fcHgoY29sKSB7XHJcblx0XHRcdGNvbnN0IHYgPSBnZXRDb21wdXRlZFN0eWxlKCBjb2wgKS5nZXRQcm9wZXJ0eVZhbHVlKCAnLS13cGJjLWNvbC1taW4nICkgfHwgJzAnO1xyXG5cdFx0XHRjb25zdCBuID0gcGFyc2VGbG9hdCggdiApO1xyXG5cdFx0XHRyZXR1cm4gTnVtYmVyLmlzRmluaXRlKCBuICkgPyBNYXRoLm1heCggMCwgbiApIDogMDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFR1cm4gcmF3IHdlaWdodHMgKGUuZy4gWzEsMV0sIFsyLDEsMV0pIGludG8gZWZmZWN0aXZlIFwiYXZhaWxhYmxlLSVcIiBiYXNlcyB0aGF0XHJcblx0XHQgKiAoYSkgc3VtIHRvIHRoZSByb3cncyBhdmFpbGFibGUgJSwgYW5kIChiKSBtZWV0IGV2ZXJ5IGNvbHVtbidzIG1pbiBweC5cclxuXHRcdCAqIFJldHVybnMgYW4gYXJyYXkgb2YgYmFzZXMgKG51bWJlcnMpIG9yIG51bGwgaWYgaW1wb3NzaWJsZSB0byBzYXRpc2Z5IG1pbnMuXHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBfZml0X3dlaWdodHNfcmVzcGVjdGluZ19taW4oYnVpbGRlciwgcm93LCB3ZWlnaHRzKSB7XHJcblx0XHRcdGNvbnN0IGNvbHMgPSBBcnJheS5mcm9tKCByb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApICk7XHJcblx0XHRcdGNvbnN0IG4gICAgPSBjb2xzLmxlbmd0aDtcclxuXHRcdFx0aWYgKCAhbiApIHJldHVybiBudWxsO1xyXG5cdFx0XHRpZiAoICFBcnJheS5pc0FycmF5KCB3ZWlnaHRzICkgfHwgd2VpZ2h0cy5sZW5ndGggIT09IG4gKSByZXR1cm4gbnVsbDtcclxuXHJcblx0XHRcdC8vIGF2YWlsYWJsZSAlIGFmdGVyIGdhcHMgKGZyb20gTGF5b3V0U2VydmljZSlcclxuXHRcdFx0Y29uc3QgZ3AgICAgICAgPSBidWlsZGVyLmNvbF9nYXBfcGVyY2VudDtcclxuXHRcdFx0Y29uc3QgZWZmICAgICAgPSBidWlsZGVyLmxheW91dC5jb21wdXRlX2VmZmVjdGl2ZV9iYXNlc19mcm9tX3Jvdyggcm93LCBncCApO1xyXG5cdFx0XHRjb25zdCBhdmFpbFBjdCA9IGVmZi5hdmFpbGFibGU7ICAgICAgICAgICAgICAgLy8gZS5nLiA5NCBpZiAyIGNvbHMgYW5kIDMlIGdhcFxyXG5cdFx0XHRjb25zdCByb3dQeCAgICA9IHJvdy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aDtcclxuXHRcdFx0Y29uc3QgYXZhaWxQeCAgPSByb3dQeCAqIChhdmFpbFBjdCAvIDEwMCk7XHJcblxyXG5cdFx0XHQvLyBjb2xsZWN0IG1pbmltYSBpbiAlIG9mIFwiYXZhaWxhYmxlXCJcclxuXHRcdFx0Y29uc3QgbWluUGN0ID0gY29scy5tYXAoIChjKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgbWluUHggPSBVSS5XUEJDX0JGQl9MYXlvdXRfQ2hpcHMuX2dldF9jb2xfbWluX3B4KCBjICk7XHJcblx0XHRcdFx0aWYgKCBhdmFpbFB4IDw9IDAgKSByZXR1cm4gMDtcclxuXHRcdFx0XHRyZXR1cm4gKG1pblB4IC8gYXZhaWxQeCkgKiBhdmFpbFBjdDtcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Ly8gSWYgbWlucyBhbG9uZSBkb24ndCBmaXQsIGJhaWwuXHJcblx0XHRcdGNvbnN0IHN1bU1pbiA9IG1pblBjdC5yZWR1Y2UoIChhLCBiKSA9PiBhICsgYiwgMCApO1xyXG5cdFx0XHRpZiAoIHN1bU1pbiA+IGF2YWlsUGN0IC0gMWUtNiApIHtcclxuXHRcdFx0XHRyZXR1cm4gbnVsbDsgLy8gaW1wb3NzaWJsZSB0byByZXNwZWN0IG1pbnM7IGRvbid0IGFwcGx5IHByZXNldFxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBUYXJnZXQgcGVyY2VudGFnZXMgZnJvbSB3ZWlnaHRzLCBub3JtYWxpemVkIHRvIGF2YWlsUGN0LlxyXG5cdFx0XHRjb25zdCB3U3VtICAgICAgPSB3ZWlnaHRzLnJlZHVjZSggKGEsIHcpID0+IGEgKyAoTnVtYmVyKCB3ICkgfHwgMCksIDAgKSB8fCBuO1xyXG5cdFx0XHRjb25zdCB0YXJnZXRQY3QgPSB3ZWlnaHRzLm1hcCggKHcpID0+ICgoTnVtYmVyKCB3ICkgfHwgMCkgLyB3U3VtKSAqIGF2YWlsUGN0ICk7XHJcblxyXG5cdFx0XHQvLyBMb2NrIGNvbHVtbnMgdGhhdCB3b3VsZCBiZSBiZWxvdyBtaW4sIHRoZW4gZGlzdHJpYnV0ZSB0aGUgcmVtYWluZGVyXHJcblx0XHRcdC8vIGFjcm9zcyB0aGUgcmVtYWluaW5nIGNvbHVtbnMgcHJvcG9ydGlvbmFsbHkgdG8gdGhlaXIgdGFyZ2V0UGN0LlxyXG5cdFx0XHRjb25zdCBsb2NrZWQgID0gbmV3IEFycmF5KCBuICkuZmlsbCggZmFsc2UgKTtcclxuXHRcdFx0bGV0IGxvY2tlZFN1bSA9IDA7XHJcblx0XHRcdGZvciAoIGxldCBpID0gMDsgaSA8IG47IGkrKyApIHtcclxuXHRcdFx0XHRpZiAoIHRhcmdldFBjdFtpXSA8IG1pblBjdFtpXSApIHtcclxuXHRcdFx0XHRcdGxvY2tlZFtpXSA9IHRydWU7XHJcblx0XHRcdFx0XHRsb2NrZWRTdW0gKz0gbWluUGN0W2ldO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0bGV0IHJlbWFpbmluZyAgICAgPSBhdmFpbFBjdCAtIGxvY2tlZFN1bTtcclxuXHRcdFx0Y29uc3QgZnJlZUlkeCAgICAgPSBbXTtcclxuXHRcdFx0bGV0IGZyZWVUYXJnZXRTdW0gPSAwO1xyXG5cdFx0XHRmb3IgKCBsZXQgaSA9IDA7IGkgPCBuOyBpKysgKSB7XHJcblx0XHRcdFx0aWYgKCAhbG9ja2VkW2ldICkge1xyXG5cdFx0XHRcdFx0ZnJlZUlkeC5wdXNoKCBpICk7XHJcblx0XHRcdFx0XHRmcmVlVGFyZ2V0U3VtICs9IHRhcmdldFBjdFtpXTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IHJlc3VsdCA9IG5ldyBBcnJheSggbiApLmZpbGwoIDAgKTtcclxuXHRcdFx0Ly8gU2VlZCBsb2NrZWQgd2l0aCB0aGVpciBtaW5pbWEuXHJcblx0XHRcdGZvciAoIGxldCBpID0gMDsgaSA8IG47IGkrKyApIHtcclxuXHRcdFx0XHRpZiAoIGxvY2tlZFtpXSApIHJlc3VsdFtpXSA9IG1pblBjdFtpXTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCBmcmVlSWR4Lmxlbmd0aCA9PT0gMCApIHtcclxuXHRcdFx0XHQvLyBldmVyeXRoaW5nIGxvY2tlZCBleGFjdGx5IGF0IG1pbjsgYW55IGxlZnRvdmVyIChzaG91bGRuJ3QgaGFwcGVuKVxyXG5cdFx0XHRcdC8vIHdvdWxkIGJlIGlnbm9yZWQgdG8ga2VlcCBzaW1wbGljaXR5IGFuZCBzdGFiaWxpdHkuXHJcblx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCByZW1haW5pbmcgPD0gMCApIHtcclxuXHRcdFx0XHQvLyBub3RoaW5nIGxlZnQgdG8gZGlzdHJpYnV0ZTsga2VlcCBleGFjdGx5IG1pbnMgb24gbG9ja2VkLFxyXG5cdFx0XHRcdC8vIG5vdGhpbmcgZm9yIGZyZWUgKGRlZ2VuZXJhdGUgYnV0IGNvbnNpc3RlbnQpXHJcblx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCBmcmVlVGFyZ2V0U3VtIDw9IDAgKSB7XHJcblx0XHRcdFx0Ly8gZGlzdHJpYnV0ZSBlcXVhbGx5IGFtb25nIGZyZWUgY29sdW1uc1xyXG5cdFx0XHRcdGNvbnN0IGVhY2ggPSByZW1haW5pbmcgLyBmcmVlSWR4Lmxlbmd0aDtcclxuXHRcdFx0XHRmcmVlSWR4LmZvckVhY2goIChpKSA9PiAocmVzdWx0W2ldID0gZWFjaCkgKTtcclxuXHRcdFx0XHRyZXR1cm4gcmVzdWx0O1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBEaXN0cmlidXRlIHJlbWFpbmluZyBwcm9wb3J0aW9uYWxseSB0byBmcmVlIGNvbHVtbnMnIHRhcmdldFBjdFxyXG5cdFx0XHRmcmVlSWR4LmZvckVhY2goIChpKSA9PiB7XHJcblx0XHRcdFx0cmVzdWx0W2ldID0gcmVtYWluaW5nICogKHRhcmdldFBjdFtpXSAvIGZyZWVUYXJnZXRTdW0pO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdHJldHVybiByZXN1bHQ7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEFwcGx5IGEgcHJlc2V0IGJ1dCBndWFyZCBpdCBieSBtaW5pbWE7IHJldHVybnMgdHJ1ZSBpZiBhcHBsaWVkLCBmYWxzZSBpZiBza2lwcGVkLiAqL1xyXG5cdFx0c3RhdGljIF9hcHBseV9wcmVzZXRfd2l0aF9taW5fZ3VhcmQoYnVpbGRlciwgc2VjdGlvbl9lbCwgd2VpZ2h0cykge1xyXG5cdFx0XHRjb25zdCByb3cgPSBzZWN0aW9uX2VsLnF1ZXJ5U2VsZWN0b3IoICc6c2NvcGUgPiAud3BiY19iZmJfX3JvdycgKTtcclxuXHRcdFx0aWYgKCAhcm93ICkgcmV0dXJuIGZhbHNlO1xyXG5cclxuXHRcdFx0Y29uc3QgZml0dGVkID0gVUkuV1BCQ19CRkJfTGF5b3V0X0NoaXBzLl9maXRfd2VpZ2h0c19yZXNwZWN0aW5nX21pbiggYnVpbGRlciwgcm93LCB3ZWlnaHRzICk7XHJcblx0XHRcdGlmICggIWZpdHRlZCApIHtcclxuXHRcdFx0XHRidWlsZGVyPy5fYW5ub3VuY2U/LiggJ05vdCBlbm91Z2ggc3BhY2UgZm9yIHRoaXMgbGF5b3V0IGJlY2F1c2Ugb2YgZmllbGRz4oCZIG1pbmltdW0gd2lkdGhzLicgKTtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIGBmaXR0ZWRgIGFscmVhZHkgc3VtcyB0byB0aGUgcm934oCZcyBhdmFpbGFibGUgJSwgc28gd2UgY2FuIGFwcGx5IGJhc2VzIGRpcmVjdGx5LlxyXG5cdFx0XHRidWlsZGVyLmxheW91dC5hcHBseV9iYXNlc190b19yb3coIHJvdywgZml0dGVkICk7XHJcblx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0fVxyXG5cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEJ1aWxkIGFuZCBhcHBlbmQgbGF5b3V0IGNoaXBzIGZvciBhIHNlY3Rpb24uXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtXUEJDX0Zvcm1fQnVpbGRlcn0gYnVpbGRlciAtIFRoZSBmb3JtIGJ1aWxkZXIgaW5zdGFuY2UuXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBzZWN0aW9uX2VsIC0gVGhlIC53cGJjX2JmYl9fc2VjdGlvbiBlbGVtZW50LlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gaG9zdF9lbCAtIENvbnRhaW5lciB3aGVyZSBjaGlwcyBzaG91bGQgYmUgcmVuZGVyZWQuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHJlbmRlcl9mb3Jfc2VjdGlvbihidWlsZGVyLCBzZWN0aW9uX2VsLCBob3N0X2VsKSB7XHJcblxyXG5cdFx0XHRpZiAoICFidWlsZGVyIHx8ICFzZWN0aW9uX2VsIHx8ICFob3N0X2VsICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3Qgcm93ID0gc2VjdGlvbl9lbC5xdWVyeVNlbGVjdG9yKCAnOnNjb3BlID4gLndwYmNfYmZiX19yb3cnICk7XHJcblx0XHRcdGlmICggIXJvdyApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IGNvbHMgPSByb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApLmxlbmd0aCB8fCAxO1xyXG5cclxuXHRcdFx0Ly8gQ2xlYXIgaG9zdC5cclxuXHRcdFx0aG9zdF9lbC5pbm5lckhUTUwgPSAnJztcclxuXHJcblx0XHRcdC8vIEVxdWFsIGNoaXAuXHJcblx0XHRcdGhvc3RfZWwuYXBwZW5kQ2hpbGQoXHJcblx0XHRcdFx0VUkuV1BCQ19CRkJfTGF5b3V0X0NoaXBzLl9tYWtlX2NoaXAoIGJ1aWxkZXIsIHNlY3Rpb25fZWwsIEFycmF5KCBjb2xzICkuZmlsbCggMSApLCAnRXF1YWwnIClcclxuXHRcdFx0KTtcclxuXHJcblx0XHRcdC8vIFByZXNldHMgYmFzZWQgb24gY29sdW1uIGNvdW50LlxyXG5cdFx0XHRjb25zdCBwcmVzZXRzID0gYnVpbGRlci5sYXlvdXQuYnVpbGRfcHJlc2V0c19mb3JfY29sdW1ucyggY29scyApO1xyXG5cdFx0XHRwcmVzZXRzLmZvckVhY2goICh3ZWlnaHRzKSA9PiB7XHJcblx0XHRcdFx0aG9zdF9lbC5hcHBlbmRDaGlsZChcclxuXHRcdFx0XHRcdFVJLldQQkNfQkZCX0xheW91dF9DaGlwcy5fbWFrZV9jaGlwKCBidWlsZGVyLCBzZWN0aW9uX2VsLCB3ZWlnaHRzLCBudWxsIClcclxuXHRcdFx0XHQpO1xyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0XHQvLyBDdXN0b20gY2hpcC5cclxuXHRcdFx0Y29uc3QgY3VzdG9tQnRuICAgICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2J1dHRvbicgKTtcclxuXHRcdFx0Y3VzdG9tQnRuLnR5cGUgICAgICAgID0gJ2J1dHRvbic7XHJcblx0XHRcdGN1c3RvbUJ0bi5jbGFzc05hbWUgICA9ICd3cGJjX2JmYl9fbGF5b3V0X2NoaXAnO1xyXG5cdFx0XHRjdXN0b21CdG4udGV4dENvbnRlbnQgPSAnQ3VzdG9t4oCmJztcclxuXHRcdFx0Y3VzdG9tQnRuLnRpdGxlICAgICAgID0gYEVudGVyICR7Y29sc30gcGVyY2VudGFnZXNgO1xyXG5cdFx0XHRjdXN0b21CdG4uYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgKCkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGV4YW1wbGUgPSAoY29scyA9PT0gMikgPyAnNTAsNTAnIDogKGNvbHMgPT09IDMgPyAnMjAsNjAsMjAnIDogJzI1LDI1LDI1LDI1Jyk7XHJcblx0XHRcdFx0Y29uc3QgdGV4dCAgICA9IHByb21wdCggYEVudGVyICR7Y29sc30gcGVyY2VudGFnZXMgKGNvbW1hIG9yIHNwYWNlIHNlcGFyYXRlZCk6YCwgZXhhbXBsZSApO1xyXG5cdFx0XHRcdGlmICggdGV4dCA9PSBudWxsICkgcmV0dXJuO1xyXG5cdFx0XHRcdGNvbnN0IHdlaWdodHMgPSBidWlsZGVyLmxheW91dC5wYXJzZV93ZWlnaHRzKCB0ZXh0ICk7XHJcblx0XHRcdFx0aWYgKCB3ZWlnaHRzLmxlbmd0aCAhPT0gY29scyApIHtcclxuXHRcdFx0XHRcdGFsZXJ0KCBgUGxlYXNlIGVudGVyIGV4YWN0bHkgJHtjb2xzfSBudW1iZXJzLmAgKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0Ly8gT0xEOlxyXG5cdFx0XHRcdC8vIGJ1aWxkZXIubGF5b3V0LmFwcGx5X2xheW91dF9wcmVzZXQoIHNlY3Rpb25fZWwsIHdlaWdodHMsIGJ1aWxkZXIuY29sX2dhcF9wZXJjZW50ICk7XHJcblx0XHRcdFx0Ly8gR3VhcmRlZCBhcHBseTouXHJcblx0XHRcdFx0aWYgKCAhVUkuV1BCQ19CRkJfTGF5b3V0X0NoaXBzLl9hcHBseV9wcmVzZXRfd2l0aF9taW5fZ3VhcmQoIGJ1aWxkZXIsIHNlY3Rpb25fZWwsIHdlaWdodHMgKSApIHtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0aG9zdF9lbC5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfYmZiX19sYXlvdXRfY2hpcCcgKS5mb3JFYWNoKCBjID0+IGMuY2xhc3NMaXN0LnJlbW92ZSggJ2lzLWFjdGl2ZScgKSApO1xyXG5cdFx0XHRcdGN1c3RvbUJ0bi5jbGFzc0xpc3QuYWRkKCAnaXMtYWN0aXZlJyApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdGhvc3RfZWwuYXBwZW5kQ2hpbGQoIGN1c3RvbUJ0biApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ3JlYXRlIGEgc2luZ2xlIGxheW91dCBjaGlwIGJ1dHRvbi5cclxuXHRcdCAqXHJcblx0XHQgKiBAcHJpdmF0ZVxyXG5cdFx0ICogQHBhcmFtIHtXUEJDX0Zvcm1fQnVpbGRlcn0gYnVpbGRlclxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gc2VjdGlvbl9lbFxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJbXX0gd2VpZ2h0c1xyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd8bnVsbH0gbGFiZWxcclxuXHRcdCAqIEByZXR1cm5zIHtIVE1MQnV0dG9uRWxlbWVudH1cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIF9tYWtlX2NoaXAoYnVpbGRlciwgc2VjdGlvbl9lbCwgd2VpZ2h0cywgbGFiZWwgPSBudWxsKSB7XHJcblxyXG5cdFx0XHRjb25zdCBidG4gICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2J1dHRvbicgKTtcclxuXHRcdFx0YnRuLnR5cGUgICAgICA9ICdidXR0b24nO1xyXG5cdFx0XHRidG4uY2xhc3NOYW1lID0gJ3dwYmNfYmZiX19sYXlvdXRfY2hpcCc7XHJcblxyXG5cdFx0XHRjb25zdCB0aXRsZSA9IGxhYmVsIHx8IGJ1aWxkZXIubGF5b3V0LmZvcm1hdF9wcmVzZXRfbGFiZWwoIHdlaWdodHMgKTtcclxuXHRcdFx0YnRuLnRpdGxlICAgPSB0aXRsZTtcclxuXHJcblx0XHRcdC8vIFZpc3VhbCBtaW5pYXR1cmUuXHJcblx0XHRcdGNvbnN0IHZpcyAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xyXG5cdFx0XHR2aXMuY2xhc3NOYW1lID0gJ3dwYmNfYmZiX19sYXlvdXRfY2hpcC12aXMnO1xyXG5cdFx0XHRjb25zdCBzdW0gICAgID0gd2VpZ2h0cy5yZWR1Y2UoIChhLCBiKSA9PiBhICsgKE51bWJlciggYiApIHx8IDApLCAwICkgfHwgMTtcclxuXHRcdFx0d2VpZ2h0cy5mb3JFYWNoKCAodykgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGJhciAgICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ3NwYW4nICk7XHJcblx0XHRcdFx0YmFyLnN0eWxlLmZsZXggPSBgMCAwIGNhbGMoICR7KChOdW1iZXIoIHcgKSB8fCAwKSAvIHN1bSAqIDEwMCkudG9GaXhlZCggMyApfSUgLSAxLjVweCApYDtcclxuXHRcdFx0XHR2aXMuYXBwZW5kQ2hpbGQoIGJhciApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdGJ0bi5hcHBlbmRDaGlsZCggdmlzICk7XHJcblxyXG5cdFx0XHRjb25zdCB0eHQgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnc3BhbicgKTtcclxuXHRcdFx0dHh0LmNsYXNzTmFtZSAgID0gJ3dwYmNfYmZiX19sYXlvdXRfY2hpcC1sYWJlbCc7XHJcblx0XHRcdHR4dC50ZXh0Q29udGVudCA9IGxhYmVsIHx8IGJ1aWxkZXIubGF5b3V0LmZvcm1hdF9wcmVzZXRfbGFiZWwoIHdlaWdodHMgKTtcclxuXHRcdFx0YnRuLmFwcGVuZENoaWxkKCB0eHQgKTtcclxuXHJcblx0XHRcdGJ0bi5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCAoKSA9PiB7XHJcblx0XHRcdFx0Ly8gT0xEOlxyXG5cdFx0XHRcdC8vIGJ1aWxkZXIubGF5b3V0LmFwcGx5X2xheW91dF9wcmVzZXQoIHNlY3Rpb25fZWwsIHdlaWdodHMsIGJ1aWxkZXIuY29sX2dhcF9wZXJjZW50ICk7XHJcblxyXG5cdFx0XHRcdC8vIE5FVzpcclxuXHRcdFx0XHRpZiAoICFVSS5XUEJDX0JGQl9MYXlvdXRfQ2hpcHMuX2FwcGx5X3ByZXNldF93aXRoX21pbl9ndWFyZCggYnVpbGRlciwgc2VjdGlvbl9lbCwgd2VpZ2h0cyApICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuOyAvLyBkbyBub3QgdG9nZ2xlIGFjdGl2ZSBpZiB3ZSBkaWRuJ3QgY2hhbmdlIGxheW91dFxyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0YnRuLnBhcmVudEVsZW1lbnQ/LnF1ZXJ5U2VsZWN0b3JBbGwoICcud3BiY19iZmJfX2xheW91dF9jaGlwJyApLmZvckVhY2goIGMgPT4gYy5jbGFzc0xpc3QucmVtb3ZlKCAnaXMtYWN0aXZlJyApICk7XHJcblx0XHRcdFx0YnRuLmNsYXNzTGlzdC5hZGQoICdpcy1hY3RpdmUnICk7XHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdHJldHVybiBidG47XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogU2VsZWN0aW9uIGNvbnRyb2xsZXIgZm9yIGZpZWxkcyBhbmQgYW5ub3VuY2VtZW50cy5cclxuXHQgKi9cclxuXHRVSS5XUEJDX0JGQl9TZWxlY3Rpb25fQ29udHJvbGxlciA9IGNsYXNzIGV4dGVuZHMgVUkuV1BCQ19CRkJfTW9kdWxlIHtcclxuXHJcblx0XHRpbml0KCkge1xyXG5cclxuXHRcdFx0dGhpcy5fc2VsZWN0ZWRfdWlkICAgICAgICAgICAgICA9IG51bGw7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5zZWxlY3RfZmllbGQgICAgICAgPSB0aGlzLnNlbGVjdF9maWVsZC5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5nZXRfc2VsZWN0ZWRfZmllbGQgPSB0aGlzLmdldF9zZWxlY3RlZF9maWVsZC5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuX29uX2NsZWFyICAgICAgICAgICAgICAgICAgPSB0aGlzLm9uX2NsZWFyLmJpbmQoIHRoaXMgKTtcclxuXHJcblx0XHRcdC8vIENlbnRyYWxpemVkIGRlbGV0ZSBjb21tYW5kIHVzZWQgYnkga2V5Ym9hcmQgKyBpbnNwZWN0b3IgKyBvdmVybGF5LlxyXG5cdFx0XHR0aGlzLmJ1aWxkZXIuZGVsZXRlX2l0ZW0gPSAoZWwpID0+IHtcclxuXHRcdFx0XHRpZiAoICFlbCApIHtcclxuXHRcdFx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRjb25zdCBiICAgICAgICA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0XHRjb25zdCBuZWlnaGJvciA9IGIuX2ZpbmRfbmVpZ2hib3Jfc2VsZWN0YWJsZT8uKCBlbCApIHx8IG51bGw7XHJcblx0XHRcdFx0ZWwucmVtb3ZlKCk7XHJcblx0XHRcdFx0Ly8gVXNlIGxvY2FsIENvcmUgY29uc3RhbnRzIChub3QgYSBnbG9iYWwpIHRvIGF2b2lkIFJlZmVyZW5jZUVycm9ycy5cclxuXHRcdFx0XHRiLmJ1cz8uZW1pdD8uKCBDb3JlLldQQkNfQkZCX0V2ZW50cy5GSUVMRF9SRU1PVkUsIHsgZWwsIGlkOiBlbD8uZGF0YXNldD8uaWQsIHVpZDogZWw/LmRhdGFzZXQ/LnVpZCB9ICk7XHJcblx0XHRcdFx0Yi51c2FnZT8udXBkYXRlX3BhbGV0dGVfdWk/LigpO1xyXG5cdFx0XHRcdC8vIE5vdGlmeSBnZW5lcmljIHN0cnVjdHVyZSBsaXN0ZW5lcnMsIHRvbzpcclxuXHRcdFx0XHRiLmJ1cz8uZW1pdD8uKCBDb3JlLldQQkNfQkZCX0V2ZW50cy5TVFJVQ1RVUkVfQ0hBTkdFLCB7IHJlYXNvbjogJ2RlbGV0ZScsIGVsIH0gKTtcclxuXHRcdFx0XHQvLyBEZWZlciBzZWxlY3Rpb24gYSB0aWNrIHNvIHRoZSBET00gaXMgZnVsbHkgc2V0dGxlZCBiZWZvcmUgSW5zcGVjdG9yIGh5ZHJhdGVzLlxyXG5cdFx0XHRcdHJlcXVlc3RBbmltYXRpb25GcmFtZSggKCkgPT4ge1xyXG5cdFx0XHRcdFx0Ly8gVGhpcyBjYWxscyBpbnNwZWN0b3IuYmluZF90b19maWVsZCgpIGFuZCBvcGVucyB0aGUgSW5zcGVjdG9yIHBhbmVsLlxyXG5cdFx0XHRcdFx0Yi5zZWxlY3RfZmllbGQ/LiggbmVpZ2hib3IgfHwgbnVsbCwgeyBzY3JvbGxJbnRvVmlldzogISFuZWlnaGJvciB9ICk7XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdHJldHVybiBuZWlnaGJvcjtcclxuXHRcdFx0fTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmJ1cy5vbiggQ29yZS5XUEJDX0JGQl9FdmVudHMuQ0xFQVJfU0VMRUNUSU9OLCB0aGlzLl9vbl9jbGVhciApO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXIuYnVzLm9uKCBDb3JlLldQQkNfQkZCX0V2ZW50cy5TVFJVQ1RVUkVfTE9BREVELCB0aGlzLl9vbl9jbGVhciApO1xyXG5cdFx0XHQvLyBkZWxlZ2F0ZWQgY2xpY2sgc2VsZWN0aW9uIChjYXB0dXJlIGVuc3VyZXMgd2Ugd2luIGJlZm9yZSBidWJibGluZyB0byBjb250YWluZXJzKS5cclxuXHRcdFx0dGhpcy5fb25fY2FudmFzX2NsaWNrID0gdGhpcy5faGFuZGxlX2NhbnZhc19jbGljay5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5wYWdlc19jb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcy5fb25fY2FudmFzX2NsaWNrLCB0cnVlICk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZGVzdHJveSgpIHtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmJ1cy5vZmYoIENvcmUuV1BCQ19CRkJfRXZlbnRzLkNMRUFSX1NFTEVDVElPTiwgdGhpcy5fb25fY2xlYXIgKTtcclxuXHJcblx0XHRcdGlmICggdGhpcy5fb25fY2FudmFzX2NsaWNrICkge1xyXG5cdFx0XHRcdHRoaXMuYnVpbGRlci5wYWdlc19jb250YWluZXIucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcy5fb25fY2FudmFzX2NsaWNrLCB0cnVlICk7XHJcblx0XHRcdFx0dGhpcy5fb25fY2FudmFzX2NsaWNrID0gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogRGVsZWdhdGVkIGNhbnZhcyBjbGljayAtPiBzZWxlY3QgY2xvc2VzdCBmaWVsZC9zZWN0aW9uIChpbm5lciBiZWF0cyBvdXRlcikuXHJcblx0XHQgKiBAcHJpdmF0ZVxyXG5cdFx0ICogQHBhcmFtIHtNb3VzZUV2ZW50fSBlXHJcblx0XHQgKi9cclxuXHRcdF9oYW5kbGVfY2FudmFzX2NsaWNrKGUpIHtcclxuXHRcdFx0Y29uc3Qgcm9vdCA9IHRoaXMuYnVpbGRlci5wYWdlc19jb250YWluZXI7XHJcblx0XHRcdGlmICggIXJvb3QgKSByZXR1cm47XHJcblxyXG5cdFx0XHQvLyBJZ25vcmUgY2xpY2tzIG9uIGNvbnRyb2xzL2hhbmRsZXMvcmVzaXplcnMsIGV0Yy5cclxuXHRcdFx0Y29uc3QgSUdOT1JFID0gW1xyXG5cdFx0XHRcdCcud3BiY19iZmJfX292ZXJsYXktY29udHJvbHMnLFxyXG5cdFx0XHRcdCcud3BiY19iZmJfX2xheW91dF9waWNrZXInLFxyXG5cdFx0XHRcdCcud3BiY19iZmJfX2RyYWctaGFuZGxlJyxcclxuXHRcdFx0XHQnLndwYmNfYmZiX19maWVsZC1yZW1vdmUtYnRuJyxcclxuXHRcdFx0XHQnLndwYmNfYmZiX19maWVsZC1tb3ZlLXVwJyxcclxuXHRcdFx0XHQnLndwYmNfYmZiX19maWVsZC1tb3ZlLWRvd24nLFxyXG5cdFx0XHRcdCcud3BiY19iZmJfX2NvbHVtbi1yZXNpemVyJ1xyXG5cdFx0XHRdLmpvaW4oICcsJyApO1xyXG5cclxuXHRcdFx0aWYgKCBlLnRhcmdldC5jbG9zZXN0KCBJR05PUkUgKSApIHtcclxuXHRcdFx0XHRyZXR1cm47IC8vIGxldCB0aG9zZSBjb250cm9scyBkbyB0aGVpciBvd24gdGhpbmcuXHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIEZpbmQgdGhlIGNsb3Nlc3Qgc2VsZWN0YWJsZSAoZmllbGQgT1Igc2VjdGlvbikgZnJvbSB0aGUgY2xpY2sgdGFyZ2V0LlxyXG5cdFx0XHRsZXQgaGl0ID0gZS50YXJnZXQuY2xvc2VzdD8uKFxyXG5cdFx0XHRcdGAke0NvcmUuV1BCQ19CRkJfRE9NLlNFTEVDVE9SUy52YWxpZEZpZWxkfSwgJHtDb3JlLldQQkNfQkZCX0RPTS5TRUxFQ1RPUlMuc2VjdGlvbn0sIC53cGJjX2JmYl9fY29sdW1uYFxyXG5cdFx0XHQpO1xyXG5cclxuXHRcdFx0aWYgKCAhaGl0IHx8ICFyb290LmNvbnRhaW5zKCBoaXQgKSApIHtcclxuXHRcdFx0XHR0aGlzLnNlbGVjdF9maWVsZCggbnVsbCApOyAgICAgICAgICAgLy8gQ2xlYXIgc2VsZWN0aW9uIG9uIGJsYW5rIGNsaWNrLlxyXG5cdFx0XHRcdHJldHVybjsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBFbXB0eSBzcGFjZSBpcyBoYW5kbGVkIGVsc2V3aGVyZS5cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTkVXOiBpZiB1c2VyIGNsaWNrZWQgYSBDT0xVTU4gLT4gcmVtZW1iZXIgdGFiIGtleSBvbiBpdHMgU0VDVElPTiwgYnV0IHN0aWxsIHNlbGVjdCB0aGUgc2VjdGlvbi5cclxuXHRcdFx0bGV0IHByZXNlbGVjdF90YWJfa2V5ID0gbnVsbDtcclxuXHRcdFx0aWYgKCBoaXQuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2NvbHVtbicgKSApIHtcclxuXHRcdFx0XHRjb25zdCByb3cgID0gaGl0LmNsb3Nlc3QoICcud3BiY19iZmJfX3JvdycgKTtcclxuXHRcdFx0XHRjb25zdCBjb2xzID0gcm93ID8gQXJyYXkuZnJvbSggcm93LnF1ZXJ5U2VsZWN0b3JBbGwoICc6c2NvcGUgPiAud3BiY19iZmJfX2NvbHVtbicgKSApIDogW107XHJcblx0XHRcdFx0Y29uc3QgaWR4ICA9IE1hdGgubWF4KCAwLCBjb2xzLmluZGV4T2YoIGhpdCApICk7XHJcblx0XHRcdFx0Y29uc3Qgc2VjICA9IGhpdC5jbG9zZXN0KCAnLndwYmNfYmZiX19zZWN0aW9uJyApO1xyXG5cdFx0XHRcdGlmICggc2VjICkge1xyXG5cdFx0XHRcdFx0cHJlc2VsZWN0X3RhYl9rZXkgPSBTdHJpbmcoIGlkeCArIDEgKTsgICAgICAgICAgICAgIC8vIHRhYnMgYXJlIDEtYmFzZWQgaW4gdWktY29sdW1uLXN0eWxlcy5qc1xyXG5cdFx0XHRcdFx0Ly8gSGludCBmb3IgdGhlIHJlbmRlcmVyIChpdCByZWFkcyB0aGlzIEJFRk9SRSByZW5kZXJpbmcgYW5kIHJlc3RvcmVzIHRoZSB0YWIpLlxyXG5cdFx0XHRcdFx0c2VjLmRhdGFzZXQuY29sX3N0eWxlc19hY3RpdmVfdGFiID0gcHJlc2VsZWN0X3RhYl9rZXk7XHJcblx0XHRcdFx0XHQvLyBwcm9tb3RlIHNlbGVjdGlvbiB0byB0aGUgc2VjdGlvbiAoc2FtZSBVWCBhcyBiZWZvcmUpLlxyXG5cdFx0XHRcdFx0aGl0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gc2VjO1xyXG5cdFx0XHRcdFx0Ly8gTkVXOiB2aXN1YWxseSBtYXJrIHdoaWNoIGNvbHVtbiBpcyBiZWluZyBlZGl0ZWRcclxuXHRcdFx0XHRcdGlmICggVUkgJiYgVUkuV1BCQ19CRkJfQ29sdW1uX1N0eWxlcyAmJiBVSS5XUEJDX0JGQl9Db2x1bW5fU3R5bGVzLnNldF9zZWxlY3RlZF9jb2xfZmxhZyApIHtcclxuXHRcdFx0XHRcdFx0VUkuV1BCQ19CRkJfQ29sdW1uX1N0eWxlcy5zZXRfc2VsZWN0ZWRfY29sX2ZsYWcoIHNlYywgcHJlc2VsZWN0X3RhYl9rZXkgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIFNlbGVjdCBhbmQgc3RvcCBidWJibGluZyBzbyBvdXRlciBjb250YWluZXJzIGRvbuKAmXQgcmVzZWxlY3QgYSBwYXJlbnQuXHJcblx0XHRcdHRoaXMuc2VsZWN0X2ZpZWxkKCBoaXQgKTtcclxuXHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHJcblx0XHRcdC8vIEFsc28gc2V0IHRoZSB0YWIgYWZ0ZXIgdGhlIGluc3BlY3RvciByZW5kZXJzICh3b3JrcyBldmVuIGlmIGl0IHdhcyBhbHJlYWR5IG9wZW4pLlxyXG5cdFx0XHRpZiAoIHByZXNlbGVjdF90YWJfa2V5ICkge1xyXG5cdFx0XHRcdCh3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IHNldFRpbWVvdXQpKCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBpbnMgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICd3cGJjX2JmYl9faW5zcGVjdG9yJyApO1xyXG5cdFx0XHRcdFx0XHRjb25zdCB0YWJzID0gaW5zICYmIGlucy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtYmZiLXNsb3Q9XCJjb2x1bW5fc3R5bGVzXCJdIFtkYXRhLXdwYmMtdGFic10nICk7XHJcblx0XHRcdFx0XHRcdGlmICggdGFicyAmJiB3aW5kb3cud3BiY191aV90YWJzICYmIHR5cGVvZiB3aW5kb3cud3BiY191aV90YWJzLnNldF9hY3RpdmUgPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0XHRcdFx0d2luZG93LndwYmNfdWlfdGFicy5zZXRfYWN0aXZlKCB0YWJzLCBwcmVzZWxlY3RfdGFiX2tleSApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSwgMCApO1xyXG5cclxuXHRcdFx0XHQvLyBQb2xpdGVseSBhc2sgdGhlIEluc3BlY3RvciB0byBmb2N1cy9vcGVuIHRoZSBcIkNvbHVtbiBTdHlsZXNcIiBncm91cCBhbmQgdGFiLlxyXG5cdFx0XHRcdHdwYmNfYmZiX19kaXNwYXRjaF9ldmVudF9zYWZlKFxyXG5cdFx0XHRcdFx0J3dwYmNfYmZiOmluc3BlY3Rvcl9mb2N1cycsXHJcblx0XHRcdFx0XHR7XHJcblx0XHRcdFx0XHRcdGdyb3VwICA6ICdjb2x1bW5fc3R5bGVzJyxcclxuXHRcdFx0XHRcdFx0dGFiX2tleTogcHJlc2VsZWN0X3RhYl9rZXlcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHQpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU2VsZWN0IGEgZmllbGQgZWxlbWVudCBvciBjbGVhciBzZWxlY3Rpb24uXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudHxudWxsfSBmaWVsZF9lbFxyXG5cdFx0ICogQHBhcmFtIHt7c2Nyb2xsSW50b1ZpZXc/OiBib29sZWFufX0gW29wdHMgPSB7fV1cclxuXHRcdCAqL1xyXG5cdFx0c2VsZWN0X2ZpZWxkKGZpZWxkX2VsLCB7IHNjcm9sbEludG9WaWV3ID0gZmFsc2UgfSA9IHt9KSB7XHJcblx0XHRcdGNvbnN0IHJvb3QgICA9IHRoaXMuYnVpbGRlci5wYWdlc19jb250YWluZXI7XHJcblx0XHRcdGNvbnN0IHByZXZFbCA9IHRoaXMuZ2V0X3NlbGVjdGVkX2ZpZWxkPy4oKSB8fCBudWxsOyAgIC8vIHRoZSBvbmUgd2XigJlyZSBsZWF2aW5nLlxyXG5cclxuXHRcdFx0Ly8gSWdub3JlIGVsZW1lbnRzIG5vdCBpbiB0aGUgY2FudmFzLlxyXG5cdFx0XHRpZiAoIGZpZWxkX2VsICYmICFyb290LmNvbnRhaW5zKCBmaWVsZF9lbCApICkge1xyXG5cdFx0XHRcdGZpZWxkX2VsID0gbnVsbDsgLy8gdHJlYXQgYXMgXCJubyBzZWxlY3Rpb25cIi5cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTkVXOiBpZiB3ZSBhcmUgbGVhdmluZyBhIHNlY3Rpb24sIGNsZWFyIGl0cyBjb2x1bW4gaGlnaGxpZ2h0XHJcblx0XHRcdGlmIChcclxuXHRcdFx0XHRwcmV2RWwgJiYgcHJldkVsICE9PSBmaWVsZF9lbCAmJlxyXG5cdFx0XHRcdHByZXZFbC5jbGFzc0xpc3Q/LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICkgJiZcclxuXHRcdFx0XHRVST8uV1BCQ19CRkJfQ29sdW1uX1N0eWxlcz8uY2xlYXJfc2VsZWN0ZWRfY29sX2ZsYWdcclxuXHRcdFx0KSB7XHJcblx0XHRcdFx0VUkuV1BCQ19CRkJfQ29sdW1uX1N0eWxlcy5jbGVhcl9zZWxlY3RlZF9jb2xfZmxhZyggcHJldkVsICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIElmIHdlJ3JlIGxlYXZpbmcgYSBmaWVsZCwgcGVybWFuZW50bHkgc3RvcCBhdXRvLW5hbWUgZm9yIGl0LlxyXG5cdFx0XHRpZiAoIHByZXZFbCAmJiBwcmV2RWwgIT09IGZpZWxkX2VsICYmIHByZXZFbC5jbGFzc0xpc3Q/LmNvbnRhaW5zKCAnd3BiY19iZmJfX2ZpZWxkJyApICkge1xyXG5cdFx0XHRcdHByZXZFbC5kYXRhc2V0LmF1dG9uYW1lID0gJzAnO1xyXG5cdFx0XHRcdHByZXZFbC5kYXRhc2V0LmZyZXNoICAgID0gJzAnO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRyb290LnF1ZXJ5U2VsZWN0b3JBbGwoICcuaXMtc2VsZWN0ZWQnICkuZm9yRWFjaCggKG4pID0+IHtcclxuXHRcdFx0XHRuLmNsYXNzTGlzdC5yZW1vdmUoICdpcy1zZWxlY3RlZCcgKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0XHRpZiAoICFmaWVsZF9lbCApIHtcclxuXHRcdFx0XHRjb25zdCBwcmV2ICAgICAgICAgPSB0aGlzLl9zZWxlY3RlZF91aWQgfHwgbnVsbDtcclxuXHRcdFx0XHR0aGlzLl9zZWxlY3RlZF91aWQgPSBudWxsO1xyXG5cdFx0XHRcdHRoaXMuYnVpbGRlci5pbnNwZWN0b3I/LmNsZWFyPy4oKTtcclxuXHRcdFx0XHRyb290LmNsYXNzTGlzdC5yZW1vdmUoICdoYXMtc2VsZWN0aW9uJyApO1xyXG5cdFx0XHRcdHRoaXMuYnVpbGRlci5idXMuZW1pdCggQ29yZS5XUEJDX0JGQl9FdmVudHMuQ0xFQVJfU0VMRUNUSU9OLCB7IHByZXZfdWlkOiBwcmV2LCBzb3VyY2U6ICdidWlsZGVyJyB9ICk7XHJcblxyXG5cdFx0XHRcdC8vIEF1dG8tb3BlbiBcIkFkZCBGaWVsZHNcIiB3aGVuIG5vdGhpbmcgaXMgc2VsZWN0ZWQuXHJcblx0XHRcdFx0d3BiY19iZmJfX2Rpc3BhdGNoX2V2ZW50X3NhZmUoXHJcblx0XHRcdFx0XHQnd3BiY19iZmI6c2hvd19wYW5lbCcsXHJcblx0XHRcdFx0XHR7XHJcblx0XHRcdFx0XHRcdHBhbmVsX2lkOiAnd3BiY19iZmJfX3BhbGV0dGVfYWRkX25ldycsXHJcblx0XHRcdFx0XHRcdHRhYl9pZCAgOiAnd3BiY190YWJfbGlicmFyeSdcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHQpO1xyXG5cclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0ZmllbGRfZWwuY2xhc3NMaXN0LmFkZCggJ2lzLXNlbGVjdGVkJyApO1xyXG5cdFx0XHR0aGlzLl9zZWxlY3RlZF91aWQgPSBmaWVsZF9lbC5nZXRBdHRyaWJ1dGUoICdkYXRhLXVpZCcgKSB8fCBudWxsO1xyXG5cclxuXHRcdFx0Ly8gRmFsbGJhY2s6IGVuc3VyZSBzZWN0aW9ucyBhbm5vdW5jZSB0aGVtc2VsdmVzIGFzIHR5cGU9XCJzZWN0aW9uXCIuXHJcblx0XHRcdGlmICggZmllbGRfZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICkgJiYgIWZpZWxkX2VsLmRhdGFzZXQudHlwZSApIHtcclxuXHRcdFx0XHRmaWVsZF9lbC5kYXRhc2V0LnR5cGUgPSAnc2VjdGlvbic7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGlmICggc2Nyb2xsSW50b1ZpZXcgKSB7XHJcblx0XHRcdFx0ZmllbGRfZWwuc2Nyb2xsSW50b1ZpZXcoIHsgYmVoYXZpb3I6ICdzbW9vdGgnLCBibG9jazogJ2NlbnRlcicgfSApO1xyXG5cdFx0XHR9XHJcblx0XHRcdHRoaXMuYnVpbGRlci5pbnNwZWN0b3I/LmJpbmRfdG9fZmllbGQ/LiggZmllbGRfZWwgKTtcclxuXHJcblx0XHRcdC8vIEZhbGxiYWNrOiBlbnN1cmUgaW5zcGVjdG9yIGVuaGFuY2VycyAoaW5jbC4gVmFsdWVTbGlkZXIpIHJ1biBldmVyeSBiaW5kLlxyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdGNvbnN0IGlucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKVxyXG5cdFx0XHRcdFx0fHwgZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy53cGJjX2JmYl9faW5zcGVjdG9yJyApO1xyXG5cdFx0XHRcdGlmICggaW5zICkge1xyXG5cdFx0XHRcdFx0VUkuSW5zcGVjdG9yRW5oYW5jZXJzPy5zY2FuPy4oIGlucyApOyAgICAgICAgICAgICAgLy8gcnVucyBhbGwgZW5oYW5jZXJzXHJcblx0XHRcdFx0XHRVSS5XUEJDX0JGQl9WYWx1ZVNsaWRlcj8uaW5pdF9vbj8uKCBpbnMgKTsgICAgICAgICAvLyBleHRyYSBiZWx0LWFuZC1zdXNwZW5kZXJzXHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9IGNhdGNoICggXyApIHtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTkVXOiB3aGVuIHNlbGVjdGluZyBhIHNlY3Rpb24sIHJlZmxlY3QgaXRzIGFjdGl2ZSB0YWIgYXMgdGhlIGhpZ2hsaWdodGVkIGNvbHVtbi5cclxuXHRcdFx0aWYgKCBmaWVsZF9lbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSAmJlxyXG5cdFx0XHRcdFVJPy5XUEJDX0JGQl9Db2x1bW5fU3R5bGVzPy5zZXRfc2VsZWN0ZWRfY29sX2ZsYWcgKSB7XHJcblx0XHRcdFx0dmFyIGsgPSAoZmllbGRfZWwuZGF0YXNldCAmJiBmaWVsZF9lbC5kYXRhc2V0LmNvbF9zdHlsZXNfYWN0aXZlX3RhYilcclxuXHRcdFx0XHRcdD8gZmllbGRfZWwuZGF0YXNldC5jb2xfc3R5bGVzX2FjdGl2ZV90YWIgOiAnMSc7XHJcblx0XHRcdFx0VUkuV1BCQ19CRkJfQ29sdW1uX1N0eWxlcy5zZXRfc2VsZWN0ZWRfY29sX2ZsYWcoIGZpZWxkX2VsLCBrICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIEtlZXAgc2VjdGlvbnMgJiBmaWVsZHMgaW4gdGhlIHNhbWUgZmxvdzpcclxuXHRcdFx0Ly8gMSkgR2VuZXJpYyBoeWRyYXRvciBmb3Igc2ltcGxlIGRhdGFzZXQtYmFja2VkIGNvbnRyb2xzLlxyXG5cdFx0XHRpZiAoIGZpZWxkX2VsICkge1xyXG5cdFx0XHRcdFVJLldQQkNfQkZCX0luc3BlY3Rvcl9CcmlkZ2UuX2dlbmVyaWNfaHlkcmF0ZV9jb250cm9scz8uKCB0aGlzLmJ1aWxkZXIsIGZpZWxkX2VsICk7XHJcblx0XHRcdFx0VUkuV1BCQ19CRkJfSW5zcGVjdG9yX0JyaWRnZS5faHlkcmF0ZV9zcGVjaWFsX2NvbnRyb2xzPy4oIHRoaXMuYnVpbGRlciwgZmllbGRfZWwgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gQXV0by1vcGVuIEluc3BlY3RvciB3aGVuIGEgdXNlciBzZWxlY3RzIGEgZmllbGQvc2VjdGlvbiAuXHJcblx0XHRcdHdwYmNfYmZiX19kaXNwYXRjaF9ldmVudF9zYWZlKFxyXG5cdFx0XHRcdCd3cGJjX2JmYjpzaG93X3BhbmVsJyxcclxuXHRcdFx0XHR7XHJcblx0XHRcdFx0XHRwYW5lbF9pZDogJ3dwYmNfYmZiX19pbnNwZWN0b3InLFxyXG5cdFx0XHRcdFx0dGFiX2lkICA6ICd3cGJjX3RhYl9pbnNwZWN0b3InXHJcblx0XHRcdFx0fVxyXG5cdFx0XHQpO1xyXG5cclxuXHRcdFx0cm9vdC5jbGFzc0xpc3QuYWRkKCAnaGFzLXNlbGVjdGlvbicgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmJ1cy5lbWl0KCBDb3JlLldQQkNfQkZCX0V2ZW50cy5TRUxFQ1QsIHsgdWlkOiB0aGlzLl9zZWxlY3RlZF91aWQsIGVsOiBmaWVsZF9lbCB9ICk7XHJcblx0XHRcdGNvbnN0IGxhYmVsID0gZmllbGRfZWw/LnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2ZpZWxkLWxhYmVsJyApPy50ZXh0Q29udGVudCB8fCAoZmllbGRfZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICkgPyAnc2VjdGlvbicgOiAnJykgfHwgZmllbGRfZWw/LmRhdGFzZXQ/LmlkIHx8ICdpdGVtJztcclxuXHRcdFx0dGhpcy5idWlsZGVyLl9hbm5vdW5jZSggJ1NlbGVjdGVkICcgKyBsYWJlbCArICcuJyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKiBAcmV0dXJucyB7SFRNTEVsZW1lbnR8bnVsbH0gKi9cclxuXHRcdGdldF9zZWxlY3RlZF9maWVsZCgpIHtcclxuXHRcdFx0aWYgKCAhdGhpcy5fc2VsZWN0ZWRfdWlkICkge1xyXG5cdFx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0XHR9XHJcblx0XHRcdGNvbnN0IGVzY19hdHRyID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5lc2NfYXR0cl92YWx1ZV9mb3Jfc2VsZWN0b3IoIHRoaXMuX3NlbGVjdGVkX3VpZCApO1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5idWlsZGVyLnBhZ2VzX2NvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCBgLndwYmNfYmZiX19maWVsZFtkYXRhLXVpZD1cIiR7ZXNjX2F0dHJ9XCJdLCAud3BiY19iZmJfX3NlY3Rpb25bZGF0YS11aWQ9XCIke2VzY19hdHRyfVwiXWAgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKiogQHBhcmFtIHtDdXN0b21FdmVudH0gZXYgKi9cclxuXHRcdG9uX2NsZWFyKGV2KSB7XHJcblx0XHRcdGNvbnN0IHNyYyA9IGV2Py5kZXRhaWw/LnNvdXJjZSA/PyBldj8uc291cmNlO1xyXG5cdFx0XHRpZiAoIHNyYyAhPT0gJ2J1aWxkZXInICkge1xyXG5cdFx0XHRcdHRoaXMuc2VsZWN0X2ZpZWxkKCBudWxsICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogQnJpZGdlcyB0aGUgYnVpbGRlciB3aXRoIHRoZSBJbnNwZWN0b3IgYW5kIHNhbml0aXplcyBpZC9uYW1lIGVkaXRzLlxyXG5cdCAqL1xyXG5cdFVJLldQQkNfQkZCX0luc3BlY3Rvcl9CcmlkZ2UgPSBjbGFzcyBleHRlbmRzIFVJLldQQkNfQkZCX01vZHVsZSB7XHJcblxyXG5cdFx0aW5pdCgpIHtcclxuXHRcdFx0dGhpcy5fYXR0YWNoX2luc3BlY3RvcigpO1xyXG5cdFx0XHR0aGlzLl9iaW5kX2lkX3Nhbml0aXplcigpO1xyXG5cdFx0XHR0aGlzLl9vcGVuX2luc3BlY3Rvcl9hZnRlcl9maWVsZF9hZGRlZCgpO1xyXG5cdFx0XHR0aGlzLl9iaW5kX2ZvY3VzX3Nob3J0Y3V0cygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9hdHRhY2hfaW5zcGVjdG9yKCkge1xyXG5cdFx0XHRjb25zdCBiICAgICAgPSB0aGlzLmJ1aWxkZXI7XHJcblx0XHRcdGNvbnN0IGF0dGFjaCA9ICgpID0+IHtcclxuXHRcdFx0XHRpZiAoIHR5cGVvZiB3aW5kb3cuV1BCQ19CRkJfSW5zcGVjdG9yID09PSAnZnVuY3Rpb24nICkge1xyXG5cdFx0XHRcdFx0Yi5pbnNwZWN0b3IgPSBuZXcgV1BCQ19CRkJfSW5zcGVjdG9yKCBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InICksIGIgKTtcclxuXHRcdFx0XHRcdHRoaXMuX2JpbmRfaWRfc2FuaXRpemVyKCk7XHJcblx0XHRcdFx0XHRkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAnd3BiY19iZmJfaW5zcGVjdG9yX3JlYWR5JywgYXR0YWNoICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9O1xyXG5cdFx0XHQvLyBFbnN1cmUgd2UgYmluZCBhZnRlciBsYXRlIHJlYWR5IGFzIHdlbGwuXHJcblx0XHRcdGlmICggdHlwZW9mIHdpbmRvdy5XUEJDX0JGQl9JbnNwZWN0b3IgPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0YXR0YWNoKCk7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Yi5pbnNwZWN0b3IgPSB7XHJcblx0XHRcdFx0XHRiaW5kX3RvX2ZpZWxkKCkge1xyXG5cdFx0XHRcdFx0fSwgY2xlYXIoKSB7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fTtcclxuXHRcdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCAnd3BiY19iZmJfaW5zcGVjdG9yX3JlYWR5JywgYXR0YWNoICk7XHJcblx0XHRcdFx0c2V0VGltZW91dCggYXR0YWNoLCAwICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIExpc3RlbiBmb3IgXCJmb2N1c1wiIGhpbnRzIGZyb20gdGhlIGNhbnZhcyBhbmQgb3BlbiB0aGUgcmlnaHQgZ3JvdXAvdGFiLlxyXG5cdFx0ICogLSBTdXBwb3J0czogZ3JvdXAgPT09ICdjb2x1bW5fc3R5bGVzJ1xyXG5cdFx0ICogLSBBbHNvIHNjcm9sbHMgdGhlIGdyb3VwIGludG8gdmlldy5cclxuXHRcdCAqL1xyXG5cdFx0X2JpbmRfZm9jdXNfc2hvcnRjdXRzKCkge1xyXG5cdFx0XHQvKiogQHBhcmFtIHtDdXN0b21FdmVudH0gZSAqL1xyXG5cdFx0XHRjb25zdCBvbl9mb2N1cyA9IChlKSA9PiB7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGNvbnN0IGdycF9rZXkgPSBlICYmIGUuZGV0YWlsICYmIGUuZGV0YWlsLmdyb3VwO1xyXG5cdFx0XHRcdFx0Y29uc3QgdGFiX2tleSA9IGUgJiYgZS5kZXRhaWwgJiYgZS5kZXRhaWwudGFiX2tleTtcclxuXHRcdFx0XHRcdGlmICggIWdycF9rZXkgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHRjb25zdCBpbnMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InICkgfHwgZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy53cGJjX2JmYl9faW5zcGVjdG9yJyApO1xyXG5cdFx0XHRcdFx0aWYgKCAhaW5zICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0aWYgKCBncnBfa2V5ID09PSAnY29sdW1uX3N0eWxlcycgKSB7XHJcblx0XHRcdFx0XHRcdC8vIEZpbmQgdGhlIENvbHVtbiBTdHlsZXMgc2xvdC9ncm91cC5cclxuXHRcdFx0XHRcdFx0Y29uc3Qgc2xvdCA9IGlucy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtYmZiLXNsb3Q9XCJjb2x1bW5fc3R5bGVzXCJdJyApIHx8IGlucy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtaW5zcGVjdG9yLWdyb3VwLWtleT1cImNvbHVtbl9zdHlsZXNcIl0nICk7XHJcblx0XHRcdFx0XHRcdGlmICggc2xvdCApIHtcclxuXHRcdFx0XHRcdFx0XHQvLyBPcGVuIGNvbGxhcHNpYmxlIGNvbnRhaW5lciBpZiBwcmVzZW50LlxyXG5cdFx0XHRcdFx0XHRcdGNvbnN0IGdyb3VwX3dyYXAgPSBzbG90LmNsb3Nlc3QoICcuaW5zcGVjdG9yX19ncm91cCcgKSB8fCBzbG90LmNsb3Nlc3QoICdbZGF0YS1pbnNwZWN0b3ItZ3JvdXBdJyApO1xyXG5cdFx0XHRcdFx0XHRcdGlmICggZ3JvdXBfd3JhcCAmJiAhZ3JvdXBfd3JhcC5jbGFzc0xpc3QuY29udGFpbnMoICdpcy1vcGVuJyApICkge1xyXG5cdFx0XHRcdFx0XHRcdFx0Z3JvdXBfd3JhcC5jbGFzc0xpc3QuYWRkKCAnaXMtb3BlbicgKTtcclxuXHRcdFx0XHRcdFx0XHRcdC8vIE1pcnJvciBBUklBIHN0YXRlIGlmIHlvdXIgaGVhZGVyIHVzZXMgYXJpYS1leHBhbmRlZC5cclxuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IGhlYWRlcl9idG4gPSBncm91cF93cmFwLnF1ZXJ5U2VsZWN0b3IoICdbYXJpYS1leHBhbmRlZF0nICk7XHJcblx0XHRcdFx0XHRcdFx0XHRpZiAoIGhlYWRlcl9idG4gKSB7XHJcblx0XHRcdFx0XHRcdFx0XHRcdGhlYWRlcl9idG4uc2V0QXR0cmlidXRlKCAnYXJpYS1leHBhbmRlZCcsICd0cnVlJyApO1xyXG5cdFx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHRcdFx0Ly8gT3B0aW9uYWw6IHNldCB0aGUgcmVxdWVzdGVkIHRhYiBrZXkgaWYgdGFicyBleGlzdCBpbiB0aGlzIGdyb3VwLlxyXG5cdFx0XHRcdFx0XHRcdGlmICggdGFiX2tleSApIHtcclxuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IHRhYnMgPSBzbG90LnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS13cGJjLXRhYnNdJyApO1xyXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCB0YWJzICYmIHdpbmRvdy53cGJjX3VpX3RhYnMgJiYgdHlwZW9mIHdpbmRvdy53cGJjX3VpX3RhYnMuc2V0X2FjdGl2ZSA9PT0gJ2Z1bmN0aW9uJyApIHtcclxuXHRcdFx0XHRcdFx0XHRcdFx0d2luZG93LndwYmNfdWlfdGFicy5zZXRfYWN0aXZlKCB0YWJzLCBTdHJpbmcoIHRhYl9rZXkgKSApO1xyXG5cdFx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHRcdFx0Ly8gQnJpbmcgaW50byB2aWV3IGZvciBjb252ZW5pZW5jZS5cclxuXHRcdFx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHRcdFx0Ly8gVW5jb21tZW50IChPbmx5ICBpZiBuZWVkZWQpIHRoaXMgdG8gQVVUTyBTQ1JPTEwgdG8gIHNwZWNpZmljIENPTFVNTiBpbiB0aGUgc2VjdGlvbjouXHJcblx0XHRcdFx0XHRcdFx0XHQvLyBzbG90LnNjcm9sbEludG9WaWV3KCB7IGJlaGF2aW9yOiAnc21vb3RoJywgYmxvY2s6ICduZWFyZXN0JyB9ICk7XHJcblx0XHRcdFx0XHRcdFx0fSBjYXRjaCAoIF9lICkge31cclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfZSApIHt9XHJcblx0XHRcdH07XHJcblxyXG5cdFx0XHR0aGlzLl9vbl9pbnNwZWN0b3JfZm9jdXMgPSBvbl9mb2N1cztcclxuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ3dwYmNfYmZiOmluc3BlY3Rvcl9mb2N1cycsIG9uX2ZvY3VzLCB0cnVlICk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZGVzdHJveSgpIHtcclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRpZiAoIHRoaXMuX29uX2luc3BlY3Rvcl9mb2N1cyApIHtcclxuXHRcdFx0XHRcdGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoICd3cGJjX2JmYjppbnNwZWN0b3JfZm9jdXMnLCB0aGlzLl9vbl9pbnNwZWN0b3JfZm9jdXMsIHRydWUgKTtcclxuXHRcdFx0XHRcdHRoaXMuX29uX2luc3BlY3Rvcl9mb2N1cyA9IG51bGw7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBIeWRyYXRlIGluc3BlY3RvciBpbnB1dHMgZm9yIFwic3BlY2lhbFwiIGtleXMgdGhhdCB3ZSBoYW5kbGUgZXhwbGljaXRseS5cclxuXHRcdCAqIFdvcmtzIGZvciBib3RoIGZpZWxkcyBhbmQgc2VjdGlvbnMuXHJcblx0XHQgKiBAcGFyYW0ge1dQQkNfRm9ybV9CdWlsZGVyfSBidWlsZGVyXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBzZWxcclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIF9oeWRyYXRlX3NwZWNpYWxfY29udHJvbHMoYnVpbGRlciwgc2VsKSB7XHJcblx0XHRcdGNvbnN0IGlucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKTtcclxuXHRcdFx0aWYgKCAhaW5zIHx8ICFzZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBzZXRWYWwgPSAoa2V5LCB2YWwpID0+IHtcclxuXHRcdFx0XHRjb25zdCBjdHJsID0gaW5zLnF1ZXJ5U2VsZWN0b3IoIGBbZGF0YS1pbnNwZWN0b3Ita2V5PVwiJHtrZXl9XCJdYCApO1xyXG5cdFx0XHRcdGlmICggY3RybCAmJiAndmFsdWUnIGluIGN0cmwgKSBjdHJsLnZhbHVlID0gU3RyaW5nKCB2YWwgPz8gJycgKTtcclxuXHRcdFx0fTtcclxuXHJcblx0XHRcdC8vIEludGVybmFsIGlkIC8gbmFtZSAvIHB1YmxpYyBodG1sX2lkLlxyXG5cdFx0XHRzZXRWYWwoICdpZCcsIHNlbC5nZXRBdHRyaWJ1dGUoICdkYXRhLWlkJyApIHx8ICcnICk7XHJcblx0XHRcdHNldFZhbCggJ25hbWUnLCBzZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJyApIHx8ICcnICk7XHJcblx0XHRcdHNldFZhbCggJ2h0bWxfaWQnLCBzZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1odG1sX2lkJyApIHx8ICcnICk7XHJcblxyXG5cdFx0XHQvLyBTZWN0aW9uLW9ubHkgZXh0cmFzIGFyZSBoYXJtbGVzcyB0byBzZXQgZm9yIGZpZWxkcyAoY29udHJvbHMgbWF5IG5vdCBleGlzdCkuXHJcblx0XHRcdHNldFZhbCggJ2Nzc2NsYXNzJywgc2VsLmdldEF0dHJpYnV0ZSggJ2RhdGEtY3NzY2xhc3MnICkgfHwgJycgKTtcclxuXHRcdFx0c2V0VmFsKCAnbGFiZWwnLCBzZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1sYWJlbCcgKSB8fCAnJyApO1xyXG5cdFx0fVxyXG5cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEh5ZHJhdGUgaW5zcGVjdG9yIGlucHV0cyB0aGF0IGRlY2xhcmUgYSBnZW5lcmljIGRhdGFzZXQgbWFwcGluZyB2aWFcclxuXHRcdCAqIFtkYXRhLWluc3BlY3Rvci1rZXldIGJ1dCBkbyBOT1QgZGVjbGFyZSBhIGN1c3RvbSB2YWx1ZV9mcm9tIGFkYXB0ZXIuXHJcblx0XHQgKiBUaGlzIG1ha2VzIHNlY3Rpb25zIGZvbGxvdyB0aGUgc2FtZSBkYXRhIGZsb3cgYXMgZmllbGRzIHdpdGggYWxtb3N0IG5vIGdsdWUuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtXUEJDX0Zvcm1fQnVpbGRlcn0gYnVpbGRlclxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gc2VsIC0gY3VycmVudGx5IHNlbGVjdGVkIGZpZWxkL3NlY3Rpb25cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIF9nZW5lcmljX2h5ZHJhdGVfY29udHJvbHMoYnVpbGRlciwgc2VsKSB7XHJcblx0XHRcdGNvbnN0IGlucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKTtcclxuXHRcdFx0aWYgKCAhaW5zIHx8ICFzZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBTS0lQID0gL14oaWR8bmFtZXxodG1sX2lkfGNzc2NsYXNzfGxhYmVsKSQvOyAvLyBoYW5kbGVkIGJ5IF9oeWRyYXRlX3NwZWNpYWxfY29udHJvbHNcclxuXHJcblx0XHRcdC8vIE5FVzogcmVhZCBzY2hlbWEgZm9yIHRoZSBzZWxlY3RlZCBlbGVtZW504oCZcyB0eXBlLlxyXG5cdFx0XHRjb25zdCBzY2hlbWFzICAgICA9IHdpbmRvdy5XUEJDX0JGQl9TY2hlbWFzIHx8IHt9O1xyXG5cdFx0XHRjb25zdCB0eXBlS2V5ICAgICA9IChzZWwuZGF0YXNldCAmJiBzZWwuZGF0YXNldC50eXBlKSB8fCAnJztcclxuXHRcdFx0Y29uc3Qgc2NoZW1hRW50cnkgPSBzY2hlbWFzW3R5cGVLZXldIHx8IG51bGw7XHJcblx0XHRcdGNvbnN0IHByb3BzU2NoZW1hID0gKHNjaGVtYUVudHJ5ICYmIHNjaGVtYUVudHJ5LnNjaGVtYSAmJiBzY2hlbWFFbnRyeS5zY2hlbWEucHJvcHMpID8gc2NoZW1hRW50cnkuc2NoZW1hLnByb3BzIDoge307XHJcblx0XHRcdGNvbnN0IGhhc093biAgICAgID0gRnVuY3Rpb24ucHJvdG90eXBlLmNhbGwuYmluZCggT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eSApO1xyXG5cdFx0XHRjb25zdCBnZXREZWZhdWx0ICA9IChrZXkpID0+IHtcclxuXHRcdFx0XHRjb25zdCBtZXRhID0gcHJvcHNTY2hlbWFba2V5XTtcclxuXHRcdFx0XHRyZXR1cm4gKG1ldGEgJiYgaGFzT3duKCBtZXRhLCAnZGVmYXVsdCcgKSkgPyBtZXRhLmRlZmF1bHQgOiB1bmRlZmluZWQ7XHJcblx0XHRcdH07XHJcblxyXG5cdFx0XHRpbnMucXVlcnlTZWxlY3RvckFsbCggJ1tkYXRhLWluc3BlY3Rvci1rZXldJyApLmZvckVhY2goIChjdHJsKSA9PiB7XHJcblx0XHRcdFx0Y29uc3Qga2V5ID0gU3RyaW5nKCBjdHJsLmRhdGFzZXQ/Lmluc3BlY3RvcktleSB8fCAnJyApLnRvTG93ZXJDYXNlKCk7XHJcblx0XHRcdFx0aWYgKCAha2V5IHx8IFNLSVAudGVzdCgga2V5ICkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdC8vIEVsZW1lbnQtbGV2ZWwgbG9jay5cclxuXHRcdFx0XHRjb25zdCBkbCA9IChjdHJsLmRhdGFzZXQ/LmxvY2tlZCB8fCAnJykudHJpbSgpLnRvTG93ZXJDYXNlKCk7XHJcblx0XHRcdFx0aWYgKCBkbCA9PT0gJzEnIHx8IGRsID09PSAndHJ1ZScgfHwgZGwgPT09ICd5ZXMnICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0XHQvLyBSZXNwZWN0IGV4cGxpY2l0IGFkYXB0ZXJzLlxyXG5cdFx0XHRcdGlmICggY3RybC5kYXRhc2V0Py52YWx1ZV9mcm9tIHx8IGN0cmwuZGF0YXNldD8udmFsdWVGcm9tICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0XHRjb25zdCByYXcgICAgICA9IHNlbC5kYXRhc2V0ID8gc2VsLmRhdGFzZXRba2V5XSA6IHVuZGVmaW5lZDtcclxuXHRcdFx0XHRjb25zdCBoYXNSYXcgICA9IHNlbC5kYXRhc2V0ID8gaGFzT3duKCBzZWwuZGF0YXNldCwga2V5ICkgOiBmYWxzZTtcclxuXHRcdFx0XHRjb25zdCBkZWZWYWx1ZSA9IGdldERlZmF1bHQoIGtleSApO1xyXG5cclxuXHRcdFx0XHQvLyBCZXN0LWVmZm9ydCBjb250cm9sIHR5cGluZyB3aXRoIHNjaGVtYSBkZWZhdWx0IGZhbGxiYWNrIHdoZW4gdmFsdWUgaXMgYWJzZW50LlxyXG5cclxuXHRcdFx0XHRpZiAoIGN0cmwgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmIChjdHJsLnR5cGUgPT09ICdjaGVja2JveCcgfHwgY3RybC50eXBlID09PSAncmFkaW8nKSApIHtcclxuXHRcdFx0XHRcdC8vIElmIGRhdGFzZXQgaXMgbWlzc2luZyB0aGUga2V5IGVudGlyZWx5IC0+IHVzZSBzY2hlbWEgZGVmYXVsdCAoYm9vbGVhbikuXHJcblx0XHRcdFx0XHRpZiAoICFoYXNSYXcgKSB7XHJcblx0XHRcdFx0XHRcdGN0cmwuY2hlY2tlZCA9ICEhZGVmVmFsdWU7XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRjdHJsLmNoZWNrZWQgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLmNvZXJjZV9ib29sZWFuKCByYXcsICEhZGVmVmFsdWUgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9IGVsc2UgaWYgKCAndmFsdWUnIGluIGN0cmwgKSB7XHJcblx0XHRcdFx0XHRpZiAoIGhhc1JhdyApIHtcclxuXHRcdFx0XHRcdFx0Y3RybC52YWx1ZSA9IChyYXcgIT0gbnVsbCkgPyBTdHJpbmcoIHJhdyApIDogJyc7XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRjdHJsLnZhbHVlID0gKGRlZlZhbHVlID09IG51bGwpID8gJycgOiBTdHJpbmcoIGRlZlZhbHVlICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0X2JpbmRfaWRfc2FuaXRpemVyKCkge1xyXG5cdFx0XHRjb25zdCBiICAgPSB0aGlzLmJ1aWxkZXI7XHJcblx0XHRcdGNvbnN0IGlucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKTtcclxuXHRcdFx0aWYgKCAhIGlucyApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBpbnMuX193cGJjX2JmYl9pZF9zYW5pdGl6ZXJfYm91bmQgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlucy5fX3dwYmNfYmZiX2lkX3Nhbml0aXplcl9ib3VuZCA9IHRydWU7XHJcblxyXG5cdFx0XHRjb25zdCBoYW5kbGVyID0gKGUpID0+IHtcclxuXHJcblx0XHRcdFx0Y29uc3QgdCA9IGUudGFyZ2V0O1xyXG5cdFx0XHRcdGlmICggIXQgfHwgISgndmFsdWUnIGluIHQpICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRjb25zdCBrZXkgICAgICAgPSAodC5kYXRhc2V0Py5pbnNwZWN0b3JLZXkgfHwgJycpLnRvTG93ZXJDYXNlKCk7XHJcblx0XHRcdFx0Y29uc3Qgc2VsICAgICAgID0gYi5nZXRfc2VsZWN0ZWRfZmllbGQ/LigpO1xyXG5cdFx0XHRcdGNvbnN0IGlzU2VjdGlvbiA9IHNlbD8uY2xhc3NMaXN0Py5jb250YWlucyggJ3dwYmNfYmZiX19zZWN0aW9uJyApO1xyXG5cdFx0XHRcdGlmICggIXNlbCApIHJldHVybjtcclxuXHJcblx0XHRcdFx0Ly8gVW5pZmllZCBlbWl0dGVyIHRoYXQgYWx3YXlzIGluY2x1ZGVzIHRoZSBlbGVtZW50IHJlZmVyZW5jZS5cclxuXHRcdFx0XHRjb25zdCBFViAgICAgICAgICAgICAgPSBDb3JlLldQQkNfQkZCX0V2ZW50cztcclxuXHRcdFx0XHQvLyBTVFJVQ1RVUkVfQ0hBTkdFIGNhbiBiZSBcImV4cGVuc2l2ZVwiIGJlY2F1c2Ugb3RoZXIgbGlzdGVuZXJzIG1heSB0cmlnZ2VyIGZ1bGwgY2FudmFzIHJlZnJlc2guXHJcblx0XHRcdFx0Ly8gRGVib3VuY2Ugb25seSBjb250aW51b3VzIGNvbnRyb2xzIChlLmcuIHZhbHVlIHNsaWRlciBzY3J1YmJpbmcpIG9uIHRoZSBJTlBVVCBwaGFzZS5cclxuXHRcdFx0XHRjb25zdCBlbnN1cmVfc2NfZGVib3VuY2Vfc3RhdGUgPSAoKSA9PiB7XHJcblx0XHRcdFx0XHRpZiAoIGIuX193cGJjX2JmYl9zY19kZWJvdW5jZV9zdGF0ZSApIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIGIuX193cGJjX2JmYl9zY19kZWJvdW5jZV9zdGF0ZTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGIuX193cGJjX2JmYl9zY19kZWJvdW5jZV9zdGF0ZSA9IHsgdGltZXJfaWQ6IDAsIHBlbmRpbmdfcGF5bG9hZDogbnVsbCB9O1xyXG5cdFx0XHRcdFx0cmV0dXJuIGIuX193cGJjX2JmYl9zY19kZWJvdW5jZV9zdGF0ZTtcclxuXHRcdFx0XHR9O1xyXG5cclxuXHRcdFx0XHRjb25zdCBjYW5jZWxfc2NfZGVib3VuY2VkX2VtaXQgPSAoKSA9PiB7XHJcblx0XHRcdFx0XHRjb25zdCBzdCA9IGIuX193cGJjX2JmYl9zY19kZWJvdW5jZV9zdGF0ZTtcclxuXHRcdFx0XHRcdGlmICggIXN0ICkgcmV0dXJuO1xyXG5cdFx0XHRcdFx0dHJ5IHsgY2xlYXJUaW1lb3V0KCBzdC50aW1lcl9pZCApOyB9IGNhdGNoICggXyApIHt9XHJcblx0XHRcdFx0XHRzdC50aW1lcl9pZCAgICAgICAgPSAwO1xyXG5cdFx0XHRcdFx0c3QucGVuZGluZ19wYXlsb2FkID0gbnVsbDtcclxuXHRcdFx0XHR9O1xyXG5cclxuXHRcdFx0XHRjb25zdCBidXNfZW1pdF9jaGFuZ2UgPSAocmVhc29uLCBleHRyYSA9IHt9KSA9PiB7XHJcblx0XHRcdFx0XHQvLyBJZiB3ZeKAmXJlIGNvbW1pdHRpbmcgc29tZXRoaW5nIChjaGFuZ2UvYmx1ci9ldGMpLCBkcm9wIGFueSBwZW5kaW5nIFwiaW5wdXRcIiBlbWl0LlxyXG5cdFx0XHRcdFx0Y2FuY2VsX3NjX2RlYm91bmNlZF9lbWl0KCk7XHJcblx0XHRcdFx0XHRiLmJ1cz8uZW1pdD8uKCBFVi5TVFJVQ1RVUkVfQ0hBTkdFLCB7IHJlYXNvbiwgZWw6IHNlbCwgLi4uZXh0cmEgfSApO1xyXG5cdFx0XHRcdH07XHJcblxyXG5cdFx0XHRcdGNvbnN0IGJ1c19lbWl0X2NoYW5nZV9kZWJvdW5jZWQgPSAocmVhc29uLCBleHRyYSA9IHt9LCB3YWl0X21zKSA9PiB7XHJcblx0XHRcdFx0XHRjb25zdCBzdCA9IGVuc3VyZV9zY19kZWJvdW5jZV9zdGF0ZSgpO1xyXG5cdFx0XHRcdFx0Y29uc3QgbXMgPSBOdW1iZXIuaXNGaW5pdGUoIHdhaXRfbXMgKVxyXG5cdFx0XHRcdFx0XHQ/IHdhaXRfbXNcclxuXHRcdFx0XHRcdFx0OiAoTnVtYmVyLmlzRmluaXRlKCBVSS5TVFJVQ1RVUkVfQ0hBTkdFX0RFQk9VTkNFX01TICkgPyBVSS5TVFJVQ1RVUkVfQ0hBTkdFX0RFQk9VTkNFX01TIDogMjQwKTtcclxuXHJcblx0XHRcdFx0XHQvLyBDYXB0dXJlIHRoZSBDVVJSRU5UIHNlbGVjdGVkIGVsZW1lbnQgaW50byB0aGUgcGF5bG9hZCBub3cgKHN0YWJsZSByZWYpLlxyXG5cdFx0XHRcdFx0c3QucGVuZGluZ19wYXlsb2FkID0geyByZWFzb24sIGVsOiBzZWwsIC4uLmV4dHJhLCBkZWJvdW5jZWQ6IHRydWUgfTtcclxuXHJcblx0XHRcdFx0XHR0cnkgeyBjbGVhclRpbWVvdXQoIHN0LnRpbWVyX2lkICk7IH0gY2F0Y2ggKCBfICkge31cclxuXHRcdFx0XHRcdHN0LnRpbWVyX2lkID0gc2V0VGltZW91dCggZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdFx0XHRzdC50aW1lcl9pZCA9IDA7XHJcblx0XHRcdFx0XHRcdGNvbnN0IHBheWxvYWQgPSBzdC5wZW5kaW5nX3BheWxvYWQ7XHJcblx0XHRcdFx0XHRcdHN0LnBlbmRpbmdfcGF5bG9hZCA9IG51bGw7XHJcblx0XHRcdFx0XHRcdGlmICggcGF5bG9hZCApIHtcclxuXHRcdFx0XHRcdFx0XHRiLmJ1cz8uZW1pdD8uKCBFVi5TVFJVQ1RVUkVfQ0hBTkdFLCBwYXlsb2FkICk7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH0sIG1zICk7XHJcblx0XHRcdFx0fTtcclxuXHJcblx0XHRcdFx0Ly8gLS0tLSBGSUVMRC9TRUNUSU9OOiBpbnRlcm5hbCBpZCAtLS0tXHJcblx0XHRcdFx0aWYgKCBrZXkgPT09ICdpZCcgKSB7XHJcblx0XHRcdFx0XHRjb25zdCB1bmlxdWUgPSBiLmlkLnNldF9maWVsZF9pZCggc2VsLCB0LnZhbHVlICk7XHJcblx0XHRcdFx0XHRpZiAoIGIucHJldmlld19tb2RlICYmICFpc1NlY3Rpb24gKSB7XHJcblx0XHRcdFx0XHRcdGIucmVuZGVyX3ByZXZpZXcoIHNlbCApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCB0LnZhbHVlICE9PSB1bmlxdWUgKSB7XHJcblx0XHRcdFx0XHRcdHQudmFsdWUgPSB1bmlxdWU7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRidXNfZW1pdF9jaGFuZ2UoICdpZC1jaGFuZ2UnICk7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHQvLyAtLS0tIEZJRUxEL1NFQ1RJT046IHB1YmxpYyBIVE1MIGlkIC0tLS1cclxuXHRcdFx0XHRpZiAoIGtleSA9PT0gJ2h0bWxfaWQnICkge1xyXG5cdFx0XHRcdFx0Y29uc3QgYXBwbGllZCA9IGIuaWQuc2V0X2ZpZWxkX2h0bWxfaWQoIHNlbCwgdC52YWx1ZSApO1xyXG5cdFx0XHRcdFx0Ly8gRm9yIHNlY3Rpb25zLCBhbHNvIHNldCB0aGUgcmVhbCBET00gaWQgc28gYW5jaG9ycy9DU1MgY2FuIHRhcmdldCBpdC5cclxuXHRcdFx0XHRcdGlmICggaXNTZWN0aW9uICkge1xyXG5cdFx0XHRcdFx0XHRzZWwuaWQgPSBhcHBsaWVkIHx8ICcnO1xyXG5cdFx0XHRcdFx0fSBlbHNlIGlmICggYi5wcmV2aWV3X21vZGUgKSB7XHJcblx0XHRcdFx0XHRcdGIucmVuZGVyX3ByZXZpZXcoIHNlbCApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCB0LnZhbHVlICE9PSBhcHBsaWVkICkge1xyXG5cdFx0XHRcdFx0XHR0LnZhbHVlID0gYXBwbGllZDtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGJ1c19lbWl0X2NoYW5nZSggJ2h0bWwtaWQtY2hhbmdlJyApO1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Ly8gLS0tLSBGSUVMRFMgT05MWTogbmFtZSAtLS0tXHJcblx0XHRcdFx0aWYgKCBrZXkgPT09ICduYW1lJyAmJiAhaXNTZWN0aW9uICkge1xyXG5cclxuXHRcdFx0XHRcdC8vIExpdmUgdHlwaW5nOiBzYW5pdGl6ZSBvbmx5IChOTyB1bmlxdWVuZXNzIHlldCkgdG8gYXZvaWQgXCItMlwiIHNwYW1cclxuXHRcdFx0XHRcdGlmICggZS50eXBlID09PSAnaW5wdXQnICkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBiZWZvcmUgICAgPSB0LnZhbHVlO1xyXG5cdFx0XHRcdFx0XHRjb25zdCBzYW5pdGl6ZWQgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfbmFtZSggYmVmb3JlICk7XHJcblx0XHRcdFx0XHRcdGlmICggYmVmb3JlICE9PSBzYW5pdGl6ZWQgKSB7XHJcblx0XHRcdFx0XHRcdFx0Ly8gb3B0aW9uYWw6IHByZXNlcnZlIGNhcmV0IHRvIGF2b2lkIGp1bXBcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBzZWxTdGFydCA9IHQuc2VsZWN0aW9uU3RhcnQsIHNlbEVuZCA9IHQuc2VsZWN0aW9uRW5kO1xyXG5cdFx0XHRcdFx0XHRcdHQudmFsdWUgICAgICAgID0gc2FuaXRpemVkO1xyXG5cdFx0XHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdFx0XHR0LnNldFNlbGVjdGlvblJhbmdlKCBzZWxTdGFydCwgc2VsRW5kICk7XHJcblx0XHRcdFx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdHJldHVybjsgLy8gdW5pcXVlbmVzcyBvbiBjaGFuZ2UvYmx1clxyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdC8vIENvbW1pdCAoY2hhbmdlL2JsdXIpXHJcblx0XHRcdFx0XHRjb25zdCByYXcgPSBTdHJpbmcoIHQudmFsdWUgPz8gJycgKS50cmltKCk7XHJcblxyXG5cdFx0XHRcdFx0aWYgKCAhcmF3ICkge1xyXG5cdFx0XHRcdFx0XHQvLyBSRVNFRUQ6IGtlZXAgbmFtZSBub24tZW1wdHkgYW5kIHByb3Zpc2lvbmFsIChhdXRvbmFtZSBzdGF5cyBPTilcclxuXHRcdFx0XHRcdFx0Y29uc3QgUyAgICA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemU7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGJhc2UgPSBTLnNhbml0aXplX2h0bWxfbmFtZSggc2VsLmdldEF0dHJpYnV0ZSggJ2RhdGEtaWQnICkgfHwgc2VsLmRhdGFzZXQuaWQgfHwgc2VsLmRhdGFzZXQudHlwZSB8fCAnZmllbGQnICk7XHJcblx0XHRcdFx0XHRcdGNvbnN0IHVuaXEgPSBiLmlkLmVuc3VyZV91bmlxdWVfZmllbGRfbmFtZSggYmFzZSwgc2VsICk7XHJcblxyXG5cdFx0XHRcdFx0XHRzZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJywgdW5pcSApO1xyXG5cdFx0XHRcdFx0XHRzZWwuZGF0YXNldC5hdXRvbmFtZSAgICAgICAgICA9ICcxJztcclxuXHRcdFx0XHRcdFx0c2VsLmRhdGFzZXQubmFtZV91c2VyX3RvdWNoZWQgPSAnMCc7XHJcblxyXG5cdFx0XHRcdFx0XHQvLyBLZWVwIERPTSBpbiBzeW5jIGlmIHdl4oCZcmUgbm90IHJlLXJlbmRlcmluZ1xyXG5cdFx0XHRcdFx0XHRpZiAoICFiLnByZXZpZXdfbW9kZSApIHtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBjdHJsID0gc2VsLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dCx0ZXh0YXJlYSxzZWxlY3QnICk7XHJcblx0XHRcdFx0XHRcdFx0aWYgKCBjdHJsICkgY3RybC5zZXRBdHRyaWJ1dGUoICduYW1lJywgdW5pcSApO1xyXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRcdGIucmVuZGVyX3ByZXZpZXcoIHNlbCApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0XHRpZiAoIHQudmFsdWUgIT09IHVuaXEgKSB0LnZhbHVlID0gdW5pcTtcclxuXHRcdFx0XHRcdFx0YnVzX2VtaXRfY2hhbmdlKCAnbmFtZS1yZXNlZWQnICk7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHQvLyBOb24tZW1wdHkgY29tbWl0OiB1c2VyIHRha2VzIGNvbnRyb2w7IGRpc2FibGUgYXV0b25hbWUgZ29pbmcgZm9yd2FyZFxyXG5cdFx0XHRcdFx0c2VsLmRhdGFzZXQubmFtZV91c2VyX3RvdWNoZWQgPSAnMSc7XHJcblx0XHRcdFx0XHRzZWwuZGF0YXNldC5hdXRvbmFtZSAgICAgICAgICA9ICcwJztcclxuXHJcblx0XHRcdFx0XHRjb25zdCBzYW5pdGl6ZWQgPSBDb3JlLldQQkNfQkZCX1Nhbml0aXplLnNhbml0aXplX2h0bWxfbmFtZSggcmF3ICk7XHJcblx0XHRcdFx0XHRjb25zdCB1bmlxdWUgICAgPSBiLmlkLnNldF9maWVsZF9uYW1lKCBzZWwsIHNhbml0aXplZCApO1xyXG5cclxuXHRcdFx0XHRcdGlmICggIWIucHJldmlld19tb2RlICkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBjdHJsID0gc2VsLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dCx0ZXh0YXJlYSxzZWxlY3QnICk7XHJcblx0XHRcdFx0XHRcdGlmICggY3RybCApIGN0cmwuc2V0QXR0cmlidXRlKCAnbmFtZScsIHVuaXF1ZSApO1xyXG5cdFx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdFx0Yi5yZW5kZXJfcHJldmlldyggc2VsICk7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0aWYgKCB0LnZhbHVlICE9PSB1bmlxdWUgKSB0LnZhbHVlID0gdW5pcXVlO1xyXG5cdFx0XHRcdFx0YnVzX2VtaXRfY2hhbmdlKCAnbmFtZS1jaGFuZ2UnICk7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHQvLyAtLS0tIFNFQ1RJT05TICYgRklFTERTOiBjc3NjbGFzcyAobGl2ZSBhcHBseTsgbm8gcmUtcmVuZGVyKSAtLS0tXHJcblx0XHRcdFx0aWYgKCBrZXkgPT09ICdjc3NjbGFzcycgKSB7XHJcblx0XHRcdFx0XHRjb25zdCBuZXh0ICAgICAgID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5zYW5pdGl6ZV9jc3NfY2xhc3NsaXN0KCB0LnZhbHVlIHx8ICcnICk7XHJcblx0XHRcdFx0XHRjb25zdCBkZXNpcmVkQXJyID0gbmV4dC5zcGxpdCggL1xccysvICkuZmlsdGVyKCBCb29sZWFuICk7XHJcblx0XHRcdFx0XHRjb25zdCBkZXNpcmVkU2V0ID0gbmV3IFNldCggZGVzaXJlZEFyciApO1xyXG5cclxuXHRcdFx0XHRcdC8vIENvcmUgY2xhc3NlcyBhcmUgbmV2ZXIgdG91Y2hlZC5cclxuXHRcdFx0XHRcdGNvbnN0IGlzQ29yZSA9IChjbHMpID0+IGNscyA9PT0gJ2lzLXNlbGVjdGVkJyB8fCBjbHMuc3RhcnRzV2l0aCggJ3dwYmNfJyApO1xyXG5cclxuXHRcdFx0XHRcdC8vIFNuYXBzaG90IGJlZm9yZSBtdXRhdGluZyAoRE9NVG9rZW5MaXN0IGlzIGxpdmUpLlxyXG5cdFx0XHRcdFx0Y29uc3QgYmVmb3JlQ2xhc3NlcyA9IEFycmF5LmZyb20oIHNlbC5jbGFzc0xpc3QgKTtcclxuXHRcdFx0XHRcdGNvbnN0IGN1c3RvbUJlZm9yZSAgPSBiZWZvcmVDbGFzc2VzLmZpbHRlciggKGMpID0+ICFpc0NvcmUoIGMgKSApO1xyXG5cclxuXHRcdFx0XHRcdC8vIFJlbW92ZSBzdHJheSBub24tY29yZSBjbGFzc2VzIG5vdCBpbiBkZXNpcmVkLlxyXG5cdFx0XHRcdFx0Y3VzdG9tQmVmb3JlLmZvckVhY2goIChjKSA9PiB7XHJcblx0XHRcdFx0XHRcdGlmICggIWRlc2lyZWRTZXQuaGFzKCBjICkgKSBzZWwuY2xhc3NMaXN0LnJlbW92ZSggYyApO1xyXG5cdFx0XHRcdFx0fSApO1xyXG5cclxuXHRcdFx0XHRcdC8vIEFkZCBtaXNzaW5nIGRlc2lyZWQgY2xhc3NlcyBpbiBvbmUgZ28uXHJcblx0XHRcdFx0XHRjb25zdCBtaXNzaW5nID0gZGVzaXJlZEFyci5maWx0ZXIoIChjKSA9PiAhY3VzdG9tQmVmb3JlLmluY2x1ZGVzKCBjICkgKTtcclxuXHRcdFx0XHRcdGlmICggbWlzc2luZy5sZW5ndGggKSBzZWwuY2xhc3NMaXN0LmFkZCggLi4ubWlzc2luZyApO1xyXG5cclxuXHRcdFx0XHRcdC8vIEtlZXAgZGF0YXNldCBpbiBzeW5jIChhdm9pZCB1c2VsZXNzIGF0dHJpYnV0ZSB3cml0ZXMpLlxyXG5cdFx0XHRcdFx0aWYgKCBzZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1jc3NjbGFzcycgKSAhPT0gbmV4dCApIHtcclxuXHRcdFx0XHRcdFx0c2VsLnNldEF0dHJpYnV0ZSggJ2RhdGEtY3NzY2xhc3MnLCBuZXh0ICk7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0Ly8gRW1pdCBvbmx5IGlmIHNvbWV0aGluZyBhY3R1YWxseSBjaGFuZ2VkLlxyXG5cdFx0XHRcdFx0Y29uc3QgYWZ0ZXJDbGFzc2VzID0gQXJyYXkuZnJvbSggc2VsLmNsYXNzTGlzdCApO1xyXG5cdFx0XHRcdFx0Y29uc3QgY2hhbmdlZCAgICAgID0gYWZ0ZXJDbGFzc2VzLmxlbmd0aCAhPT0gYmVmb3JlQ2xhc3Nlcy5sZW5ndGggfHwgYmVmb3JlQ2xhc3Nlcy5zb21lKCAoYywgaSkgPT4gYyAhPT0gYWZ0ZXJDbGFzc2VzW2ldICk7XHJcblxyXG5cdFx0XHRcdFx0Y29uc3QgZGV0YWlsID0geyBrZXk6ICdjc3NjbGFzcycsIHBoYXNlOiBlLnR5cGUgfTtcclxuXHRcdFx0XHRcdGlmICggaXNTZWN0aW9uICkge1xyXG5cdFx0XHRcdFx0XHRidXNfZW1pdF9jaGFuZ2UoICdjc3NjbGFzcy1jaGFuZ2UnLCBkZXRhaWwgKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdGJ1c19lbWl0X2NoYW5nZSggJ3Byb3AtY2hhbmdlJywgZGV0YWlsICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cclxuXHJcblx0XHRcdFx0Ly8gLS0tLSBTRUNUSU9OUzogbGFiZWwgLS0tLVxyXG5cdFx0XHRcdGlmICggaXNTZWN0aW9uICYmIGtleSA9PT0gJ2xhYmVsJyApIHtcclxuXHRcdFx0XHRcdGNvbnN0IHZhbCA9IFN0cmluZyggdC52YWx1ZSA/PyAnJyApO1xyXG5cdFx0XHRcdFx0c2VsLnNldEF0dHJpYnV0ZSggJ2RhdGEtbGFiZWwnLCB2YWwgKTtcclxuXHRcdFx0XHRcdGJ1c19lbWl0X2NoYW5nZSggJ2xhYmVsLWNoYW5nZScgKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdC8vIC0tLS0gRklFTERTOiBsYWJlbCAoYXV0by1uYW1lIHdoaWxlIHR5cGluZzsgZnJlZXplIG9uIGNvbW1pdCkgLS0tLVxyXG5cdFx0XHRcdGlmICggIWlzU2VjdGlvbiAmJiBrZXkgPT09ICdsYWJlbCcgKSB7XHJcblx0XHRcdFx0XHRjb25zdCB2YWwgICAgICAgICA9IFN0cmluZyggdC52YWx1ZSA/PyAnJyApO1xyXG5cdFx0XHRcdFx0c2VsLmRhdGFzZXQubGFiZWwgPSB2YWw7XHJcblxyXG5cdFx0XHRcdFx0Ly8gd2hpbGUgdHlwaW5nLCBhbGxvdyBhdXRvLW5hbWUgKGlmIGZsYWdzIHBlcm1pdClcclxuXHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdENvcmUuV1BCQ19CRkJfRmllbGRfQmFzZS5tYXliZV9hdXRvbmFtZV9mcm9tX2xhYmVsKCBiLCBzZWwsIHZhbCApO1xyXG5cdFx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0Ly8gaWYgdXNlciBjb21taXR0ZWQgdGhlIGxhYmVsIChibHVyL2NoYW5nZSksIGZyZWV6ZSBmdXR1cmUgYXV0by1uYW1lXHJcblx0XHRcdFx0XHRpZiAoIGUudHlwZSAhPT0gJ2lucHV0JyApIHtcclxuXHRcdFx0XHRcdFx0c2VsLmRhdGFzZXQuYXV0b25hbWUgPSAnMCc7ICAgLy8gc3RvcCBmdXR1cmUgbGFiZWwtPm5hbWUgc3luY1xyXG5cdFx0XHRcdFx0XHRzZWwuZGF0YXNldC5mcmVzaCAgICA9ICcwJzsgICAvLyBhbHNvIGtpbGwgdGhlIFwiZnJlc2hcIiBlc2NhcGUgaGF0Y2hcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHQvLyBPcHRpb25hbCBVSSBuaWNldHk6IGRpc2FibGUgTmFtZSB3aGVuIGF1dG8gaXMgT04sIGVuYWJsZSB3aGVuIE9GRlxyXG5cdFx0XHRcdFx0Y29uc3QgaW5zICAgICAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InICk7XHJcblx0XHRcdFx0XHRjb25zdCBuYW1lQ3RybCA9IGlucz8ucXVlcnlTZWxlY3RvciggJ1tkYXRhLWluc3BlY3Rvci1rZXk9XCJuYW1lXCJdJyApO1xyXG5cdFx0XHRcdFx0aWYgKCBuYW1lQ3RybCApIHtcclxuXHRcdFx0XHRcdFx0Y29uc3QgYXV0b0FjdGl2ZSA9XHJcblx0XHRcdFx0XHRcdFx0XHQgIChzZWwuZGF0YXNldC5hdXRvbmFtZSA/PyAnMScpICE9PSAnMCcgJiZcclxuXHRcdFx0XHRcdFx0XHRcdCAgc2VsLmRhdGFzZXQubmFtZV91c2VyX3RvdWNoZWQgIT09ICcxJyAmJlxyXG5cdFx0XHRcdFx0XHRcdFx0ICBzZWwuZGF0YXNldC53YXNfbG9hZGVkICE9PSAnMSc7XHJcblx0XHRcdFx0XHRcdG5hbWVDdHJsLnRvZ2dsZUF0dHJpYnV0ZSggJ2Rpc2FibGVkJywgYXV0b0FjdGl2ZSApO1xyXG5cdFx0XHRcdFx0XHRpZiAoIGF1dG9BY3RpdmUgJiYgIW5hbWVDdHJsLnBsYWNlaG9sZGVyICkge1xyXG5cdFx0XHRcdFx0XHRcdG5hbWVDdHJsLnBsYWNlaG9sZGVyID0gYj8uaTE4bj8uYXV0b19mcm9tX2xhYmVsID8/ICdhdXRvIOKAlCBmcm9tIGxhYmVsJztcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRpZiAoICFhdXRvQWN0aXZlICYmIG5hbWVDdHJsLnBsYWNlaG9sZGVyID09PSAoYj8uaTE4bj8uYXV0b19mcm9tX2xhYmVsID8/ICdhdXRvIOKAlCBmcm9tIGxhYmVsJykgKSB7XHJcblx0XHRcdFx0XHRcdFx0bmFtZUN0cmwucGxhY2Vob2xkZXIgPSAnJztcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdC8vIEFsd2F5cyByZS1yZW5kZXIgdGhlIHByZXZpZXcgc28gbGFiZWwgY2hhbmdlcyBhcmUgdmlzaWJsZSBpbW1lZGlhdGVseS5cclxuXHRcdFx0XHRcdGIucmVuZGVyX3ByZXZpZXcoIHNlbCApO1xyXG5cdFx0XHRcdFx0YnVzX2VtaXRfY2hhbmdlKCAnbGFiZWwtY2hhbmdlJyApO1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHJcblxyXG5cdFx0XHRcdC8vIC0tLS0gREVGQVVMVCAoR0VORVJJQyk6IGRhdGFzZXQgd3JpdGVyIGZvciBib3RoIGZpZWxkcyAmIHNlY3Rpb25zIC0tLS1cclxuXHRcdFx0XHQvLyBBbnkgaW5zcGVjdG9yIGNvbnRyb2wgd2l0aCBbZGF0YS1pbnNwZWN0b3Ita2V5XSB0aGF0IGRvZXNuJ3QgaGF2ZSBhIGN1c3RvbVxyXG5cdFx0XHRcdC8vIGFkYXB0ZXIvdmFsdWVfZnJvbSB3aWxsIHNpbXBseSByZWFkL3dyaXRlIHNlbC5kYXRhc2V0W2tleV0uXHJcblx0XHRcdFx0aWYgKCBrZXkgKSB7XHJcblxyXG5cdFx0XHRcdFx0Y29uc3Qgc2VsZkxvY2tlZCA9IC9eKDF8dHJ1ZXx5ZXMpJC9pLnRlc3QoICh0LmRhdGFzZXQ/LmxvY2tlZCB8fCAnJykudHJpbSgpICk7XHJcblx0XHRcdFx0XHRpZiAoIHNlbGZMb2NrZWQgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHQvLyBTa2lwIGtleXMgd2UgaGFuZGxlZCBhYm92ZSB0byBhdm9pZCBkb3VibGUgd29yay5cclxuXHRcdFx0XHRcdGlmICgga2V5ID09PSAnaWQnIHx8IGtleSA9PT0gJ25hbWUnIHx8IGtleSA9PT0gJ2h0bWxfaWQnIHx8IGtleSA9PT0gJ2Nzc2NsYXNzJyB8fCBrZXkgPT09ICdsYWJlbCcgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGxldCBuZXh0VmFsID0gJyc7XHJcblx0XHRcdFx0XHRpZiAoIHQgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmICh0LnR5cGUgPT09ICdjaGVja2JveCcgfHwgdC50eXBlID09PSAncmFkaW8nKSApIHtcclxuXHRcdFx0XHRcdFx0bmV4dFZhbCA9IHQuY2hlY2tlZCA/ICcxJyA6ICcnO1xyXG5cdFx0XHRcdFx0fSBlbHNlIGlmICggJ3ZhbHVlJyBpbiB0ICkge1xyXG5cdFx0XHRcdFx0XHRuZXh0VmFsID0gU3RyaW5nKCB0LnZhbHVlID8/ICcnICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHQvLyBQZXJzaXN0IHRvIGRhdGFzZXQuXHJcblx0XHRcdFx0XHRpZiAoIHNlbD8uZGF0YXNldCApIHNlbC5kYXRhc2V0W2tleV0gPSBuZXh0VmFsO1xyXG5cclxuXHRcdFx0XHRcdC8vIEdlbmVyYXRvciBjb250cm9scyBhcmUgXCJVSSBpbnB1dHNcIiDigJQgYXZvaWQgU1RSVUNUVVJFX0NIQU5HRSBzcGFtIHdoaWxlIGRyYWdnaW5nL3R5cGluZy5cclxuXHRcdFx0XHRcdGNvbnN0IGlzX2dlbl9rZXkgPSAoa2V5LmluZGV4T2YoICdnZW5fJyApID09PSAwKTtcclxuXHJcblx0XHRcdFx0XHQvLyBSZS1yZW5kZXIgb24gdmlzdWFsIGtleXMgc28gcHJldmlldyBzdGF5cyBpbiBzeW5jIChjYWxlbmRhciBsYWJlbC9oZWxwLCBldGMuKS5cclxuXHRcdFx0XHRcdGNvbnN0IHZpc3VhbEtleXMgPSBuZXcgU2V0KCBbICdoZWxwJywgJ3BsYWNlaG9sZGVyJywgJ21pbl93aWR0aCcsICdjc3NjbGFzcycgXSApO1xyXG5cdFx0XHRcdFx0aWYgKCAhaXNTZWN0aW9uICYmICh2aXN1YWxLZXlzLmhhcygga2V5ICkgfHwga2V5LnN0YXJ0c1dpdGgoICd1aV8nICkpICkge1xyXG5cdFx0XHRcdFx0XHQvLyBMaWdodCBoZXVyaXN0aWM6IG9ubHkgcmUtcmVuZGVyIG9uIGNvbW1pdCBmb3IgaGVhdnkgaW5wdXRzOyBsaXZlIGZvciBzaG9ydCBvbmVzIGlzIGZpbmUuXHJcblx0XHRcdFx0XHRcdGlmICggZS50eXBlID09PSAnY2hhbmdlJyB8fCBrZXkgPT09ICdoZWxwJyB8fCBrZXkgPT09ICdwbGFjZWhvbGRlcicgKSB7XHJcblx0XHRcdFx0XHRcdFx0Yi5yZW5kZXJfcHJldmlldyggc2VsICk7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHRpZiAoICEoaXNfZ2VuX2tleSAmJiBlLnR5cGUgPT09ICdpbnB1dCcpICkge1xyXG5cdFx0XHRcdFx0XHQvLyBEZWJvdW5jZSBjb250aW51b3VzIHZhbHVlIHNsaWRlciBpbnB1dCBldmVudHMgdG8gYXZvaWQgZnVsbC1jYW52YXMgcmVmcmVzaCBzcGFtLlxyXG5cdFx0XHRcdFx0XHQvLyBXZSBkZXRlY3QgdGhlIHNsaWRlciBncm91cCB2aWEgW2RhdGEtbGVuLWdyb3VwXSB3cmFwcGVyLlxyXG5cdFx0XHRcdFx0XHRjb25zdCBpc19sZW5fZ3JvdXBfY3RybCA9ICEhKHQgJiYgdC5jbG9zZXN0ICYmIHQuY2xvc2VzdCggJ1tkYXRhLWxlbi1ncm91cF0nICkpO1xyXG5cclxuXHRcdFx0XHRcdFx0aWYgKCBpc19sZW5fZ3JvdXBfY3RybCAmJiBlLnR5cGUgPT09ICdpbnB1dCcgKSB7XHJcblx0XHRcdFx0XHRcdFx0YnVzX2VtaXRfY2hhbmdlX2RlYm91bmNlZCggJ3Byb3AtY2hhbmdlJywgeyBrZXksIHBoYXNlOiBlLnR5cGUgfSApO1xyXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRcdGJ1c19lbWl0X2NoYW5nZSggJ3Byb3AtY2hhbmdlJywgeyBrZXksIHBoYXNlOiBlLnR5cGUgfSApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9O1xyXG5cclxuXHRcdFx0aW5zLmFkZEV2ZW50TGlzdGVuZXIoICdjaGFuZ2UnLCBoYW5kbGVyLCB0cnVlICk7XHJcblx0XHRcdC8vIHJlZmxlY3QgaW5zdGFudGx5IHdoaWxlIHR5cGluZyBhcyB3ZWxsLlxyXG5cdFx0XHRpbnMuYWRkRXZlbnRMaXN0ZW5lciggJ2lucHV0JywgaGFuZGxlciwgdHJ1ZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogT3BlbiBJbnNwZWN0b3IgYWZ0ZXIgYSBmaWVsZCBpcyBhZGRlZC5cclxuXHRcdCAqIEBwcml2YXRlXHJcblx0XHQgKi9cclxuXHRcdF9vcGVuX2luc3BlY3Rvcl9hZnRlcl9maWVsZF9hZGRlZCgpIHtcclxuXHRcdFx0Y29uc3QgRVYgPSBDb3JlLldQQkNfQkZCX0V2ZW50cztcclxuXHRcdFx0dGhpcy5idWlsZGVyPy5idXM/Lm9uPy4oIEVWLkZJRUxEX0FERCwgKGUpID0+IHtcclxuXHRcdFx0XHRjb25zdCBlbCA9IGU/LmRldGFpbD8uZWwgfHwgbnVsbDtcclxuXHRcdFx0XHRpZiAoIGVsICYmIHRoaXMuYnVpbGRlcj8uc2VsZWN0X2ZpZWxkICkge1xyXG5cdFx0XHRcdFx0dGhpcy5idWlsZGVyLnNlbGVjdF9maWVsZCggZWwsIHsgc2Nyb2xsSW50b1ZpZXc6IHRydWUgfSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHQvLyBTaG93IEluc3BlY3RvciBQYWxldHRlLlxyXG5cdFx0XHRcdHdwYmNfYmZiX19kaXNwYXRjaF9ldmVudF9zYWZlKFxyXG5cdFx0XHRcdFx0J3dwYmNfYmZiOnNob3dfcGFuZWwnLFxyXG5cdFx0XHRcdFx0e1xyXG5cdFx0XHRcdFx0XHRwYW5lbF9pZDogJ3dwYmNfYmZiX19pbnNwZWN0b3InLFxyXG5cdFx0XHRcdFx0XHR0YWJfaWQgIDogJ3dwYmNfdGFiX2luc3BlY3RvcidcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHQpO1xyXG5cdFx0XHR9ICk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogS2V5Ym9hcmQgc2hvcnRjdXRzIGZvciBzZWxlY3Rpb24sIGRlbGV0aW9uLCBhbmQgbW92ZW1lbnQuXHJcblx0ICovXHJcblx0VUkuV1BCQ19CRkJfS2V5Ym9hcmRfQ29udHJvbGxlciA9IGNsYXNzIGV4dGVuZHMgVUkuV1BCQ19CRkJfTW9kdWxlIHtcclxuXHRcdGluaXQoKSB7XHJcblx0XHRcdHRoaXMuX29uX2tleSA9IHRoaXMub25fa2V5LmJpbmQoIHRoaXMgKTtcclxuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ2tleWRvd24nLCB0aGlzLl9vbl9rZXksIHRydWUgKTtcclxuXHRcdH1cclxuXHJcblx0XHRkZXN0cm95KCkge1xyXG5cdFx0XHRkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAna2V5ZG93bicsIHRoaXMuX29uX2tleSwgdHJ1ZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKiBAcGFyYW0ge0tleWJvYXJkRXZlbnR9IGUgKi9cclxuXHRcdG9uX2tleShlKSB7XHJcblx0XHRcdGNvbnN0IGIgICAgICAgICA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0Y29uc3QgaXNfdHlwaW5nID0gdGhpcy5faXNfdHlwaW5nX2FueXdoZXJlKCk7XHJcblx0XHRcdGlmICggZS5rZXkgPT09ICdFc2NhcGUnICkge1xyXG5cdFx0XHRcdGlmICggaXNfdHlwaW5nICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHR0aGlzLmJ1aWxkZXIuYnVzLmVtaXQoIENvcmUuV1BCQ19CRkJfRXZlbnRzLkNMRUFSX1NFTEVDVElPTiwgeyBzb3VyY2U6ICdlc2MnIH0gKTtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0Y29uc3Qgc2VsZWN0ZWQgPSBiLmdldF9zZWxlY3RlZF9maWVsZD8uKCk7XHJcblx0XHRcdGlmICggIXNlbGVjdGVkIHx8IGlzX3R5cGluZyApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBlLmtleSA9PT0gJ0RlbGV0ZScgfHwgZS5rZXkgPT09ICdCYWNrc3BhY2UnICkge1xyXG5cdFx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHRcdFx0XHRiLmRlbGV0ZV9pdGVtPy4oIHNlbGVjdGVkICk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggKGUuYWx0S2V5IHx8IGUuY3RybEtleSB8fCBlLm1ldGFLZXkpICYmIChlLmtleSA9PT0gJ0Fycm93VXAnIHx8IGUua2V5ID09PSAnQXJyb3dEb3duJykgJiYgIWUuc2hpZnRLZXkgKSB7XHJcblx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0XHRcdGNvbnN0IGRpciA9IChlLmtleSA9PT0gJ0Fycm93VXAnKSA/ICd1cCcgOiAnZG93bic7XHJcblx0XHRcdFx0Yi5tb3ZlX2l0ZW0/Liggc2VsZWN0ZWQsIGRpciApO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoIGUua2V5ID09PSAnRW50ZXInICkge1xyXG5cdFx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHRcdFx0XHRiLnNlbGVjdF9maWVsZCggc2VsZWN0ZWQsIHsgc2Nyb2xsSW50b1ZpZXc6IHRydWUgfSApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEByZXR1cm5zIHtib29sZWFufSAqL1xyXG5cdFx0X2lzX3R5cGluZ19hbnl3aGVyZSgpIHtcclxuXHRcdFx0Y29uc3QgYSAgID0gZG9jdW1lbnQuYWN0aXZlRWxlbWVudDtcclxuXHRcdFx0Y29uc3QgdGFnID0gYT8udGFnTmFtZTtcclxuXHRcdFx0aWYgKCB0YWcgPT09ICdJTlBVVCcgfHwgdGFnID09PSAnVEVYVEFSRUEnIHx8IHRhZyA9PT0gJ1NFTEVDVCcgfHwgKGE/LmlzQ29udGVudEVkaXRhYmxlID09PSB0cnVlKSApIHtcclxuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBpbnMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InICk7XHJcblx0XHRcdHJldHVybiAhIShpbnMgJiYgYSAmJiBpbnMuY29udGFpbnMoIGEgKSk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0LyoqXHJcblx0ICogQ29sdW1uIHJlc2l6ZSBsb2dpYyBmb3Igc2VjdGlvbiByb3dzLlxyXG5cdCAqL1xyXG5cdFVJLldQQkNfQkZCX1Jlc2l6ZV9Db250cm9sbGVyID0gY2xhc3MgZXh0ZW5kcyBVSS5XUEJDX0JGQl9Nb2R1bGUge1xyXG5cdFx0aW5pdCgpIHtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmluaXRfcmVzaXplX2hhbmRsZXIgPSB0aGlzLmhhbmRsZV9yZXNpemUuYmluZCggdGhpcyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogcmVhZCB0aGUgQ1NTIHZhciAoa2VwdCBsb2NhbCBzbyBpdCBkb2VzbuKAmXQgZGVwZW5kIG9uIHRoZSBNaW4tV2lkdGggbW9kdWxlKVxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSBjb2xcclxuXHRcdCAqIEByZXR1cm5zIHtudW1iZXJ8bnVtYmVyfVxyXG5cdFx0ICogQHByaXZhdGVcclxuXHRcdCAqL1xyXG5cdFx0X2dldF9jb2xfbWluX3B4KGNvbCkge1xyXG5cdFx0XHRjb25zdCB2ID0gZ2V0Q29tcHV0ZWRTdHlsZSggY29sICkuZ2V0UHJvcGVydHlWYWx1ZSggJy0td3BiYy1jb2wtbWluJyApIHx8ICcwJztcclxuXHRcdFx0Y29uc3QgbiA9IHBhcnNlRmxvYXQoIHYgKTtcclxuXHRcdFx0cmV0dXJuIE51bWJlci5pc0Zpbml0ZSggbiApID8gTWF0aC5tYXgoIDAsIG4gKSA6IDA7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEBwYXJhbSB7TW91c2VFdmVudH0gZSAqL1xyXG5cdFx0aGFuZGxlX3Jlc2l6ZShlKSB7XHJcblx0XHRcdGNvbnN0IGIgPSB0aGlzLmJ1aWxkZXI7XHJcblx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHRcdFx0aWYgKCBlLmJ1dHRvbiAhPT0gMCApIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IHJlc2l6ZXIgICA9IGUuY3VycmVudFRhcmdldDtcclxuXHRcdFx0Y29uc3Qgcm93X2VsICAgID0gcmVzaXplci5wYXJlbnRFbGVtZW50O1xyXG5cdFx0XHRjb25zdCBjb2xzICAgICAgPSBBcnJheS5mcm9tKCByb3dfZWwucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApICk7XHJcblx0XHRcdGNvbnN0IGxlZnRfY29sICA9IHJlc2l6ZXI/LnByZXZpb3VzRWxlbWVudFNpYmxpbmc7XHJcblx0XHRcdGNvbnN0IHJpZ2h0X2NvbCA9IHJlc2l6ZXI/Lm5leHRFbGVtZW50U2libGluZztcclxuXHRcdFx0aWYgKCAhbGVmdF9jb2wgfHwgIXJpZ2h0X2NvbCB8fCAhbGVmdF9jb2wuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2NvbHVtbicgKSB8fCAhcmlnaHRfY29sLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19jb2x1bW4nICkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBsZWZ0X2luZGV4ICA9IGNvbHMuaW5kZXhPZiggbGVmdF9jb2wgKTtcclxuXHRcdFx0Y29uc3QgcmlnaHRfaW5kZXggPSBjb2xzLmluZGV4T2YoIHJpZ2h0X2NvbCApO1xyXG5cdFx0XHRpZiAoIGxlZnRfaW5kZXggPT09IC0xIHx8IHJpZ2h0X2luZGV4ICE9PSBsZWZ0X2luZGV4ICsgMSApIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IHN0YXJ0X3ggICAgICAgID0gZS5jbGllbnRYO1xyXG5cdFx0XHRjb25zdCBsZWZ0X3N0YXJ0X3B4ICA9IGxlZnRfY29sLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLndpZHRoO1xyXG5cdFx0XHRjb25zdCByaWdodF9zdGFydF9weCA9IHJpZ2h0X2NvbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aDtcclxuXHRcdFx0Y29uc3QgcGFpcl9weCAgICAgICAgPSBNYXRoLm1heCggMCwgbGVmdF9zdGFydF9weCArIHJpZ2h0X3N0YXJ0X3B4ICk7XHJcblxyXG5cdFx0XHRjb25zdCBncCAgICAgICAgID0gYi5jb2xfZ2FwX3BlcmNlbnQ7XHJcblx0XHRcdGNvbnN0IGNvbXB1dGVkICAgPSBiLmxheW91dC5jb21wdXRlX2VmZmVjdGl2ZV9iYXNlc19mcm9tX3Jvdyggcm93X2VsLCBncCApO1xyXG5cdFx0XHRjb25zdCBhdmFpbGFibGUgID0gY29tcHV0ZWQuYXZhaWxhYmxlOyAgICAgICAgICAgICAgICAgLy8gJSBvZiB0aGUg4oCcZnVsbCAxMDDigJ0gYWZ0ZXIgZ2Fwc1xyXG5cdFx0XHRjb25zdCBiYXNlcyAgICAgID0gY29tcHV0ZWQuYmFzZXMuc2xpY2UoIDAgKTsgICAgICAgICAgICAvLyBjdXJyZW50IGVmZmVjdGl2ZSAlXHJcblx0XHRcdGNvbnN0IHBhaXJfYXZhaWwgPSBiYXNlc1tsZWZ0X2luZGV4XSArIGJhc2VzW3JpZ2h0X2luZGV4XTtcclxuXHJcblx0XHRcdC8vIEJhaWwgaWYgd2UgY2Fu4oCZdCBjb21wdXRlIHNhbmUgZGVsdGFzLlxyXG5cdFx0XHRpZiAoIXBhaXJfcHggfHwgIU51bWJlci5pc0Zpbml0ZShwYWlyX2F2YWlsKSB8fCBwYWlyX2F2YWlsIDw9IDApIHJldHVybjtcclxuXHJcblx0XHRcdC8vIC0tLSBNSU4gQ0xBTVBTIChwaXhlbHMpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRcdFx0Y29uc3QgcGN0VG9QeCAgICAgICA9IChwY3QpID0+IChwYWlyX3B4ICogKHBjdCAvIHBhaXJfYXZhaWwpKTsgLy8gcGFpci1sb2NhbCBwZXJjZW50IC0+IHB4XHJcblx0XHRcdGNvbnN0IGdlbmVyaWNNaW5QY3QgPSBNYXRoLm1pbiggMC4xLCBhdmFpbGFibGUgKTsgICAgICAgICAgICAgICAgICAvLyBvcmlnaW5hbCAwLjElIGZsb29yIChpbiDigJxhdmFpbGFibGUgJeKAnSBzcGFjZSlcclxuXHRcdFx0Y29uc3QgZ2VuZXJpY01pblB4ICA9IHBjdFRvUHgoIGdlbmVyaWNNaW5QY3QgKTtcclxuXHJcblx0XHRcdGNvbnN0IGxlZnRNaW5QeCAgPSBNYXRoLm1heCggdGhpcy5fZ2V0X2NvbF9taW5fcHgoIGxlZnRfY29sICksIGdlbmVyaWNNaW5QeCApO1xyXG5cdFx0XHRjb25zdCByaWdodE1pblB4ID0gTWF0aC5tYXgoIHRoaXMuX2dldF9jb2xfbWluX3B4KCByaWdodF9jb2wgKSwgZ2VuZXJpY01pblB4ICk7XHJcblxyXG5cdFx0XHQvLyBmcmVlemUgdGV4dCBzZWxlY3Rpb24gKyBjdXJzb3JcclxuXHRcdFx0Y29uc3QgcHJldl91c2VyX3NlbGVjdCAgICAgICAgID0gZG9jdW1lbnQuYm9keS5zdHlsZS51c2VyU2VsZWN0O1xyXG5cdFx0XHRkb2N1bWVudC5ib2R5LnN0eWxlLnVzZXJTZWxlY3QgPSAnbm9uZSc7XHJcblx0XHRcdHJvd19lbC5zdHlsZS5jdXJzb3IgICAgICAgICAgICA9ICdjb2wtcmVzaXplJztcclxuXHJcblx0XHRcdGNvbnN0IG9uX21vdXNlX21vdmUgPSAoZXYpID0+IHtcclxuXHRcdFx0XHRpZiAoICFwYWlyX3B4ICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0XHQvLyB3b3JrIGluIHBpeGVscywgY2xhbXAgYnkgZWFjaCBzaWRl4oCZcyBtaW5cclxuXHRcdFx0XHRjb25zdCBkZWx0YV9weCAgID0gZXYuY2xpZW50WCAtIHN0YXJ0X3g7XHJcblx0XHRcdFx0bGV0IG5ld0xlZnRQeCAgICA9IGxlZnRfc3RhcnRfcHggKyBkZWx0YV9weDtcclxuXHRcdFx0XHRuZXdMZWZ0UHggICAgICAgID0gTWF0aC5tYXgoIGxlZnRNaW5QeCwgTWF0aC5taW4oIHBhaXJfcHggLSByaWdodE1pblB4LCBuZXdMZWZ0UHggKSApO1xyXG5cdFx0XHRcdGNvbnN0IG5ld1JpZ2h0UHggPSBwYWlyX3B4IC0gbmV3TGVmdFB4O1xyXG5cclxuXHRcdFx0XHQvLyB0cmFuc2xhdGUgYmFjayB0byBwYWlyLWxvY2FsIHBlcmNlbnRhZ2VzXHJcblx0XHRcdFx0Y29uc3QgbmV3TGVmdFBjdCAgICAgID0gKG5ld0xlZnRQeCAvIHBhaXJfcHgpICogcGFpcl9hdmFpbDtcclxuXHRcdFx0XHRjb25zdCBuZXdCYXNlcyAgICAgICAgPSBiYXNlcy5zbGljZSggMCApO1xyXG5cdFx0XHRcdG5ld0Jhc2VzW2xlZnRfaW5kZXhdICA9IG5ld0xlZnRQY3Q7XHJcblx0XHRcdFx0bmV3QmFzZXNbcmlnaHRfaW5kZXhdID0gcGFpcl9hdmFpbCAtIG5ld0xlZnRQY3Q7XHJcblxyXG5cdFx0XHRcdGIubGF5b3V0LmFwcGx5X2Jhc2VzX3RvX3Jvdyggcm93X2VsLCBuZXdCYXNlcyApO1xyXG5cdFx0XHR9O1xyXG5cclxuXHRcdFx0Y29uc3Qgb25fbW91c2VfdXAgPSAoKSA9PiB7XHJcblx0XHRcdFx0ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ21vdXNlbW92ZScsIG9uX21vdXNlX21vdmUgKTtcclxuXHRcdFx0XHRkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAnbW91c2V1cCcsIG9uX21vdXNlX3VwICk7XHJcblx0XHRcdFx0d2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdtb3VzZXVwJywgb25fbW91c2VfdXAgKTtcclxuXHRcdFx0XHRkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAnbW91c2VsZWF2ZScsIG9uX21vdXNlX3VwICk7XHJcblx0XHRcdFx0ZG9jdW1lbnQuYm9keS5zdHlsZS51c2VyU2VsZWN0ID0gcHJldl91c2VyX3NlbGVjdCB8fCAnJztcclxuXHRcdFx0XHRyb3dfZWwuc3R5bGUuY3Vyc29yICAgICAgICAgICAgPSAnJztcclxuXHJcblx0XHRcdFx0Ly8gbm9ybWFsaXplIHRvIHRoZSByb3figJlzIGF2YWlsYWJsZSAlIGFnYWluXHJcblx0XHRcdFx0Y29uc3Qgbm9ybWFsaXplZCA9IGIubGF5b3V0LmNvbXB1dGVfZWZmZWN0aXZlX2Jhc2VzX2Zyb21fcm93KCByb3dfZWwsIGdwICk7XHJcblx0XHRcdFx0Yi5sYXlvdXQuYXBwbHlfYmFzZXNfdG9fcm93KCByb3dfZWwsIG5vcm1hbGl6ZWQuYmFzZXMgKTtcclxuXHRcdFx0fTtcclxuXHJcblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICdtb3VzZW1vdmUnLCBvbl9tb3VzZV9tb3ZlICk7XHJcblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICdtb3VzZXVwJywgb25fbW91c2VfdXAgKTtcclxuXHRcdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoICdtb3VzZXVwJywgb25fbW91c2VfdXAgKTtcclxuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ21vdXNlbGVhdmUnLCBvbl9tb3VzZV91cCApO1xyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHQgKiBQYWdlIGFuZCBzZWN0aW9uIGNyZWF0aW9uLCByZWJ1aWxkaW5nLCBhbmQgbmVzdGVkIFNvcnRhYmxlIHNldHVwLlxyXG5cdCAqL1xyXG5cdFVJLldQQkNfQkZCX1BhZ2VzX1NlY3Rpb25zID0gY2xhc3MgZXh0ZW5kcyBVSS5XUEJDX0JGQl9Nb2R1bGUge1xyXG5cclxuXHRcdGluaXQoKSB7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5hZGRfcGFnZSAgICAgICAgICAgICAgICAgID0gKG9wdHMpID0+IHRoaXMuYWRkX3BhZ2UoIG9wdHMgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLmFkZF9zZWN0aW9uICAgICAgICAgICAgICAgPSAoY29udGFpbmVyLCBjb2xzKSA9PiB0aGlzLmFkZF9zZWN0aW9uKCBjb250YWluZXIsIGNvbHMgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyLnJlYnVpbGRfc2VjdGlvbiAgICAgICAgICAgPSAoc2VjdGlvbl9kYXRhLCBjb250YWluZXIpID0+IHRoaXMucmVidWlsZF9zZWN0aW9uKCBzZWN0aW9uX2RhdGEsIGNvbnRhaW5lciApO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXIuaW5pdF9hbGxfbmVzdGVkX3NvcnRhYmxlcyA9IChlbCkgPT4gdGhpcy5pbml0X2FsbF9uZXN0ZWRfc29ydGFibGVzKCBlbCApO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXIuaW5pdF9zZWN0aW9uX3NvcnRhYmxlICAgICA9IChlbCkgPT4gdGhpcy5pbml0X3NlY3Rpb25fc29ydGFibGUoIGVsICk7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5wYWdlc19zZWN0aW9ucyAgICAgICAgICAgID0gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEdpdmUgZXZlcnkgZmllbGQvc2VjdGlvbiBpbiBhIGNsb25lZCBzdWJ0cmVlIGEgZnJlc2ggZGF0YS11aWQgc29cclxuXHRcdCAqIHVuaXF1ZW5lc3MgY2hlY2tzIGRvbid0IGV4Y2x1ZGUgdGhlaXIgb3JpZ2luYWxzLlxyXG5cdFx0ICovXHJcblx0XHRfcmV0YWdfdWlkc19pbl9zdWJ0cmVlKHJvb3QpIHtcclxuXHRcdFx0Y29uc3QgYiA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0aWYgKCAhcm9vdCApIHJldHVybjtcclxuXHRcdFx0Y29uc3Qgbm9kZXMgPSBbXTtcclxuXHRcdFx0aWYgKCByb290LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSB8fCByb290LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fZmllbGQnICkgKSB7XHJcblx0XHRcdFx0bm9kZXMucHVzaCggcm9vdCApO1xyXG5cdFx0XHR9XHJcblx0XHRcdG5vZGVzLnB1c2goIC4uLnJvb3QucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fc2VjdGlvbiwgLndwYmNfYmZiX19maWVsZCcgKSApO1xyXG5cdFx0XHRub2Rlcy5mb3JFYWNoKCAoZWwpID0+IHtcclxuXHRcdFx0XHRjb25zdCBwcmVmaXggICA9IGVsLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19zZWN0aW9uJyApID8gJ3MnIDogJ2YnO1xyXG5cdFx0XHRcdGVsLmRhdGFzZXQudWlkID0gYCR7cHJlZml4fS0keysrYi5fdWlkX2NvdW50ZXJ9LSR7RGF0ZS5ub3coKX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKCAzNiApLnNsaWNlKCAyLCA3ICl9YDtcclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQnVtcCBcImZvb1wiLCBcImZvby0yXCIsIFwiZm9vLTNcIiwgLi4uXHJcblx0XHQgKi9cclxuXHRcdF9tYWtlX3VuaXF1ZShiYXNlLCB0YWtlbikge1xyXG5cdFx0XHRjb25zdCBzID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZTtcclxuXHRcdFx0bGV0IHYgICA9IFN0cmluZyggYmFzZSB8fCAnJyApO1xyXG5cdFx0XHRpZiAoICF2ICkgdiA9ICdmaWVsZCc7XHJcblx0XHRcdGNvbnN0IG0gID0gdi5tYXRjaCggLy0oXFxkKykkLyApO1xyXG5cdFx0XHRsZXQgbiAgICA9IG0gPyAocGFyc2VJbnQoIG1bMV0sIDEwICkgfHwgMSkgOiAxO1xyXG5cdFx0XHRsZXQgc3RlbSA9IG0gPyB2LnJlcGxhY2UoIC8tXFxkKyQvLCAnJyApIDogdjtcclxuXHRcdFx0d2hpbGUgKCB0YWtlbi5oYXMoIHYgKSApIHtcclxuXHRcdFx0XHRuID0gTWF0aC5tYXgoIDIsIG4gKyAxICk7XHJcblx0XHRcdFx0diA9IGAke3N0ZW19LSR7bn1gO1xyXG5cdFx0XHR9XHJcblx0XHRcdHRha2VuLmFkZCggdiApO1xyXG5cdFx0XHRyZXR1cm4gdjtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFN0cmljdCwgb25lLXBhc3MgZGUtZHVwbGljYXRpb24gZm9yIGEgbmV3bHktaW5zZXJ0ZWQgc3VidHJlZS5cclxuXHRcdCAqIC0gRW5zdXJlcyB1bmlxdWUgZGF0YS1pZCAoaW50ZXJuYWwpLCBkYXRhLW5hbWUgKGZpZWxkcyksIGRhdGEtaHRtbF9pZCAocHVibGljKVxyXG5cdFx0ICogLSBBbHNvIHVwZGF0ZXMgRE9NOiA8c2VjdGlvbiBpZD4sIDxpbnB1dCBpZD4sIDxsYWJlbCBmb3I+LCBhbmQgaW5wdXRbbmFtZV0uXHJcblx0XHQgKi9cclxuXHRcdF9kZWR1cGVfc3VidHJlZV9zdHJpY3Qocm9vdCkge1xyXG5cdFx0XHRjb25zdCBiID0gdGhpcy5idWlsZGVyO1xyXG5cdFx0XHRjb25zdCBzID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZTtcclxuXHRcdFx0aWYgKCAhcm9vdCB8fCAhYj8ucGFnZXNfY29udGFpbmVyICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0Ly8gMSkgQnVpbGQgXCJ0YWtlblwiIHNldHMgZnJvbSBvdXRzaWRlIHRoZSBzdWJ0cmVlLlxyXG5cdFx0XHRjb25zdCB0YWtlbkRhdGFJZCAgID0gbmV3IFNldCgpO1xyXG5cdFx0XHRjb25zdCB0YWtlbkRhdGFOYW1lID0gbmV3IFNldCgpO1xyXG5cdFx0XHRjb25zdCB0YWtlbkh0bWxJZCAgID0gbmV3IFNldCgpO1xyXG5cdFx0XHRjb25zdCB0YWtlbkRvbUlkICAgID0gbmV3IFNldCgpO1xyXG5cclxuXHRcdFx0Ly8gQWxsIGZpZWxkcy9zZWN0aW9ucyBvdXRzaWRlIHJvb3RcclxuXHRcdFx0Yi5wYWdlc19jb250YWluZXIucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fZmllbGQsIC53cGJjX2JmYl9fc2VjdGlvbicgKS5mb3JFYWNoKCBlbCA9PiB7XHJcblx0XHRcdFx0aWYgKCByb290LmNvbnRhaW5zKCBlbCApICkgcmV0dXJuO1xyXG5cdFx0XHRcdGNvbnN0IGRpZCAgPSBlbC5nZXRBdHRyaWJ1dGUoICdkYXRhLWlkJyApO1xyXG5cdFx0XHRcdGNvbnN0IGRuYW0gPSBlbC5nZXRBdHRyaWJ1dGUoICdkYXRhLW5hbWUnICk7XHJcblx0XHRcdFx0Y29uc3QgaGlkICA9IGVsLmdldEF0dHJpYnV0ZSggJ2RhdGEtaHRtbF9pZCcgKTtcclxuXHRcdFx0XHRpZiAoIGRpZCApIHRha2VuRGF0YUlkLmFkZCggZGlkICk7XHJcblx0XHRcdFx0aWYgKCBkbmFtICkgdGFrZW5EYXRhTmFtZS5hZGQoIGRuYW0gKTtcclxuXHRcdFx0XHRpZiAoIGhpZCApIHRha2VuSHRtbElkLmFkZCggaGlkICk7XHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdC8vIEFsbCBET00gaWRzIG91dHNpZGUgcm9vdCAobGFiZWxzLCBpbnB1dHMsIGFueXRoaW5nKVxyXG5cdFx0XHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnW2lkXScgKS5mb3JFYWNoKCBlbCA9PiB7XHJcblx0XHRcdFx0aWYgKCByb290LmNvbnRhaW5zKCBlbCApICkgcmV0dXJuO1xyXG5cdFx0XHRcdGlmICggZWwuaWQgKSB0YWtlbkRvbUlkLmFkZCggZWwuaWQgKTtcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Y29uc3Qgbm9kZXMgPSBbXTtcclxuXHRcdFx0aWYgKCByb290LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSB8fCByb290LmNsYXNzTGlzdD8uY29udGFpbnMoICd3cGJjX2JmYl9fZmllbGQnICkgKSB7XHJcblx0XHRcdFx0bm9kZXMucHVzaCggcm9vdCApO1xyXG5cdFx0XHR9XHJcblx0XHRcdG5vZGVzLnB1c2goIC4uLnJvb3QucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fc2VjdGlvbiwgLndwYmNfYmZiX19maWVsZCcgKSApO1xyXG5cclxuXHRcdFx0Ly8gMikgV2FsayB0aGUgc3VidHJlZSBhbmQgZml4IGNvbGxpc2lvbnMgZGV0ZXJtaW5pc3RpY2FsbHkuXHJcblx0XHRcdG5vZGVzLmZvckVhY2goIGVsID0+IHtcclxuXHRcdFx0XHRjb25zdCBpc0ZpZWxkICAgPSBlbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fZmllbGQnICk7XHJcblx0XHRcdFx0Y29uc3QgaXNTZWN0aW9uID0gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICk7XHJcblxyXG5cdFx0XHRcdC8vIElOVEVSTkFMIGRhdGEtaWRcclxuXHRcdFx0XHR7XHJcblx0XHRcdFx0XHRjb25zdCByYXcgID0gZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1pZCcgKSB8fCAnJztcclxuXHRcdFx0XHRcdGNvbnN0IGJhc2UgPSBzLnNhbml0aXplX2h0bWxfaWQoIHJhdyApIHx8IChpc1NlY3Rpb24gPyAnc2VjdGlvbicgOiAnZmllbGQnKTtcclxuXHRcdFx0XHRcdGNvbnN0IHVuaXEgPSB0aGlzLl9tYWtlX3VuaXF1ZSggYmFzZSwgdGFrZW5EYXRhSWQgKTtcclxuXHRcdFx0XHRcdGlmICggdW5pcSAhPT0gcmF3ICkgZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1pZCcsIHVuaXEgKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdC8vIEhUTUwgbmFtZSAoZmllbGRzIG9ubHkpXHJcblx0XHRcdFx0aWYgKCBpc0ZpZWxkICkge1xyXG5cdFx0XHRcdFx0Y29uc3QgcmF3ID0gZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJyApIHx8ICcnO1xyXG5cdFx0XHRcdFx0aWYgKCByYXcgKSB7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGJhc2UgPSBzLnNhbml0aXplX2h0bWxfbmFtZSggcmF3ICk7XHJcblx0XHRcdFx0XHRcdGNvbnN0IHVuaXEgPSB0aGlzLl9tYWtlX3VuaXF1ZSggYmFzZSwgdGFrZW5EYXRhTmFtZSApO1xyXG5cdFx0XHRcdFx0XHRpZiAoIHVuaXEgIT09IHJhdyApIHtcclxuXHRcdFx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoICdkYXRhLW5hbWUnLCB1bmlxICk7XHJcblx0XHRcdFx0XHRcdFx0Ly8gVXBkYXRlIGlubmVyIGNvbnRyb2wgaW1tZWRpYXRlbHlcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBpbnB1dCA9IGVsLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dCwgdGV4dGFyZWEsIHNlbGVjdCcgKTtcclxuXHRcdFx0XHRcdFx0XHRpZiAoIGlucHV0ICkgaW5wdXQuc2V0QXR0cmlidXRlKCAnbmFtZScsIHVuaXEgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Ly8gUHVibGljIEhUTUwgaWQgKGZpZWxkcyArIHNlY3Rpb25zKVxyXG5cdFx0XHRcdHtcclxuXHRcdFx0XHRcdGNvbnN0IHJhdyA9IGVsLmdldEF0dHJpYnV0ZSggJ2RhdGEtaHRtbF9pZCcgKSB8fCAnJztcclxuXHRcdFx0XHRcdGlmICggcmF3ICkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBiYXNlICAgICAgICAgID0gcy5zYW5pdGl6ZV9odG1sX2lkKCByYXcgKTtcclxuXHRcdFx0XHRcdFx0Ly8gUmVzZXJ2ZSBhZ2FpbnN0IEJPVEgga25vd24gZGF0YS1odG1sX2lkIGFuZCByZWFsIERPTSBpZHMuXHJcblx0XHRcdFx0XHRcdGNvbnN0IGNvbWJpbmVkVGFrZW4gPSBuZXcgU2V0KCBbIC4uLnRha2VuSHRtbElkLCAuLi50YWtlbkRvbUlkIF0gKTtcclxuXHRcdFx0XHRcdFx0bGV0IGNhbmRpZGF0ZSAgICAgICA9IHRoaXMuX21ha2VfdW5pcXVlKCBiYXNlLCBjb21iaW5lZFRha2VuICk7XHJcblx0XHRcdFx0XHRcdC8vIFJlY29yZCBpbnRvIHRoZSByZWFsIHNldHMgc28gZnV0dXJlIGNoZWNrcyBzZWUgdGhlIHJlc2VydmF0aW9uLlxyXG5cdFx0XHRcdFx0XHR0YWtlbkh0bWxJZC5hZGQoIGNhbmRpZGF0ZSApO1xyXG5cdFx0XHRcdFx0XHR0YWtlbkRvbUlkLmFkZCggY2FuZGlkYXRlICk7XHJcblxyXG5cdFx0XHRcdFx0XHRpZiAoIGNhbmRpZGF0ZSAhPT0gcmF3ICkgZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1odG1sX2lkJywgY2FuZGlkYXRlICk7XHJcblxyXG5cdFx0XHRcdFx0XHQvLyBSZWZsZWN0IHRvIERPTSBpbW1lZGlhdGVseVxyXG5cdFx0XHRcdFx0XHRpZiAoIGlzU2VjdGlvbiApIHtcclxuXHRcdFx0XHRcdFx0XHRlbC5pZCA9IGNhbmRpZGF0ZSB8fCAnJztcclxuXHRcdFx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBpbnB1dCA9IGVsLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dCwgdGV4dGFyZWEsIHNlbGVjdCcgKTtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBsYWJlbCA9IGVsLnF1ZXJ5U2VsZWN0b3IoICdsYWJlbC53cGJjX2JmYl9fZmllbGQtbGFiZWwnICk7XHJcblx0XHRcdFx0XHRcdFx0aWYgKCBpbnB1dCApIGlucHV0LmlkID0gY2FuZGlkYXRlIHx8ICcnO1xyXG5cdFx0XHRcdFx0XHRcdGlmICggbGFiZWwgKSBsYWJlbC5odG1sRm9yID0gY2FuZGlkYXRlIHx8ICcnO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBpc1NlY3Rpb24gKSB7XHJcblx0XHRcdFx0XHRcdC8vIEVuc3VyZSBubyBzdGFsZSBET00gaWQgaWYgZGF0YS1odG1sX2lkIHdhcyBjbGVhcmVkXHJcblx0XHRcdFx0XHRcdGVsLnJlbW92ZUF0dHJpYnV0ZSggJ2lkJyApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9tYWtlX2FkZF9jb2x1bW5zX2NvbnRyb2wocGFnZV9lbCwgc2VjdGlvbl9jb250YWluZXIsIGluc2VydF9wb3MgPSAnYm90dG9tJykge1xyXG5cclxuXHRcdFx0Ly8gQWNjZXB0IGluc2VydF9wb3MgKCd0b3AnfCdib3R0b20nKSwgZGVmYXVsdCAnYm90dG9tJy5cclxuXHJcblx0XHRcdGNvbnN0IHRwbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2FkZF9jb2x1bW5zX3RlbXBsYXRlJyApO1xyXG5cdFx0XHRpZiAoICF0cGwgKSB7XHJcblx0XHRcdFx0cmV0dXJuIG51bGw7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIENsb25lICpjb250ZW50cyogKG5vdCB0aGUgaWQpLCB1bmhpZGUsIGFuZCBhZGQgYSBwYWdlLXNjb3BlZCBjbGFzcy5cclxuXHRcdFx0Y29uc3Qgc3JjID0gKHRwbC5jb250ZW50ICYmIHRwbC5jb250ZW50LmZpcnN0RWxlbWVudENoaWxkKSA/IHRwbC5jb250ZW50LmZpcnN0RWxlbWVudENoaWxkIDogdHBsLmZpcnN0RWxlbWVudENoaWxkO1xyXG5cdFx0XHRpZiAoICFzcmMgKSB7XHJcblx0XHRcdFx0cmV0dXJuIG51bGw7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNvbnN0IGNsb25lID0gc3JjLmNsb25lTm9kZSggdHJ1ZSApO1xyXG5cdFx0XHRjbG9uZS5yZW1vdmVBdHRyaWJ1dGUoICdoaWRkZW4nICk7XHJcblx0XHRcdGlmICggY2xvbmUuaWQgKSB7XHJcblx0XHRcdFx0Y2xvbmUucmVtb3ZlQXR0cmlidXRlKCAnaWQnICk7XHJcblx0XHRcdH1cclxuXHRcdFx0Y2xvbmUucXVlcnlTZWxlY3RvckFsbCggJ1tpZF0nICkuZm9yRWFjaCggbiA9PiBuLnJlbW92ZUF0dHJpYnV0ZSggJ2lkJyApICk7XHJcblxyXG5cdFx0XHQvLyBNYXJrIHdoZXJlIHRoaXMgY29udHJvbCBpbnNlcnRzIHNlY3Rpb25zLlxyXG5cdFx0XHRjbG9uZS5kYXRhc2V0Lmluc2VydCA9IGluc2VydF9wb3M7IC8vICd0b3AnIHwgJ2JvdHRvbSdcclxuXHJcblx0XHRcdC8vIC8vIE9wdGlvbmFsIFVJIGhpbnQgZm9yIHVzZXJzIChrZWVwcyBleGlzdGluZyBtYXJrdXAgaW50YWN0KS5cclxuXHRcdFx0Ly8gY29uc3QgaGludCA9IGNsb25lLnF1ZXJ5U2VsZWN0b3IoICcubmF2LXRhYi10ZXh0IC5zZWxlY3RlZF92YWx1ZScgKTtcclxuXHRcdFx0Ly8gaWYgKCBoaW50ICkge1xyXG5cdFx0XHQvLyBcdGhpbnQudGV4dENvbnRlbnQgPSAoaW5zZXJ0X3BvcyA9PT0gJ3RvcCcpID8gJyAoYWRkIGF0IHRvcCknIDogJyAoYWRkIGF0IGJvdHRvbSknO1xyXG5cdFx0XHQvLyB9XHJcblxyXG5cdFx0XHQvLyBDbGljayBvbiBvcHRpb25zIC0gYWRkIHNlY3Rpb24gd2l0aCBOIGNvbHVtbnMuXHJcblx0XHRcdGNsb25lLmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIChlKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgYSA9IGUudGFyZ2V0LmNsb3Nlc3QoICcudWxfZHJvcGRvd25fbWVudV9saV9hY3Rpb25fYWRkX3NlY3Rpb25zJyApO1xyXG5cdFx0XHRcdGlmICggIWEgKSB7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHJcblx0XHRcdFx0Ly8gUmVhZCBOIGVpdGhlciBmcm9tIGRhdGEtY29scyBvciBmYWxsYmFjayB0byBwYXJzaW5nIHRleHQgbGlrZSBcIjMgQ29sdW1uc1wiLlxyXG5cdFx0XHRcdGxldCBjb2xzID0gcGFyc2VJbnQoIGEuZGF0YXNldC5jb2xzIHx8IChhLnRleHRDb250ZW50Lm1hdGNoKCAvXFxiKFxcZCspXFxzKkNvbHVtbi9pICk/LlsxXSA/PyAnMScpLCAxMCApO1xyXG5cdFx0XHRcdGNvbHMgICAgID0gTWF0aC5tYXgoIDEsIE1hdGgubWluKCA0LCBjb2xzICkgKTtcclxuXHJcblx0XHRcdFx0Ly8gTkVXOiBob25vciB0aGUgY29udHJvbCdzIGluc2VydGlvbiBwb3NpdGlvblxyXG5cdFx0XHRcdHRoaXMuYWRkX3NlY3Rpb24oIHNlY3Rpb25fY29udGFpbmVyLCBjb2xzLCBpbnNlcnRfcG9zICk7XHJcblxyXG5cdFx0XHRcdC8vIFJlZmxlY3QgbGFzdCBjaG9pY2UgKHVuY2hhbmdlZClcclxuXHRcdFx0XHRjb25zdCB2YWwgPSBjbG9uZS5xdWVyeVNlbGVjdG9yKCAnLnNlbGVjdGVkX3ZhbHVlJyApO1xyXG5cdFx0XHRcdGlmICggdmFsICkge1xyXG5cdFx0XHRcdFx0dmFsLnRleHRDb250ZW50ID0gYCAoJHtjb2xzfSlgO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0cmV0dXJuIGNsb25lO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQHBhcmFtIHt7c2Nyb2xsPzogYm9vbGVhbn19IFtvcHRzID0ge31dXHJcblx0XHQgKiBAcmV0dXJucyB7SFRNTEVsZW1lbnR9XHJcblx0XHQgKi9cclxuXHRcdGFkZF9wYWdlKHsgc2Nyb2xsID0gdHJ1ZSB9ID0ge30pIHtcclxuXHRcdFx0Y29uc3QgYiAgICAgICA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0Y29uc3QgcGFnZV9lbCA9IENvcmUuV1BCQ19Gb3JtX0J1aWxkZXJfSGVscGVyLmNyZWF0ZV9lbGVtZW50KCAnZGl2JywgJ3dwYmNfYmZiX19wYW5lbCB3cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgIHdwYmNfYmZiX2Zvcm0gd3BiY19jb250YWluZXIgd3BiY19mb3JtIHdwYmNfY29udGFpbmVyX2Jvb2tpbmdfZm9ybScgKTtcclxuXHRcdFx0cGFnZV9lbC5zZXRBdHRyaWJ1dGUoICdkYXRhLXBhZ2UnLCArK2IucGFnZV9jb3VudGVyICk7XHJcblxyXG5cdFx0XHQvLyBcIlBhZ2UgMSB8IFhcIiAtIFJlbmRlciBwYWdlIFRpdGxlIHdpdGggUmVtb3ZlIFggYnV0dG9uLlxyXG5cdFx0XHRjb25zdCBjb250cm9sc19odG1sID0gVUkucmVuZGVyX3dwX3RlbXBsYXRlKCAnd3BiYy1iZmItdHBsLXBhZ2UtcmVtb3ZlJywgeyBwYWdlX251bWJlcjogYi5wYWdlX2NvdW50ZXIgfSApO1xyXG5cdFx0XHRwYWdlX2VsLmlubmVySFRNTCAgID0gY29udHJvbHNfaHRtbCArICc8ZGl2IGNsYXNzPVwid3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lciB3cGJjX3dpemFyZF9fYm9yZGVyX2NvbnRhaW5lclwiPjwvZGl2Pic7XHJcblxyXG5cdFx0XHRiLnBhZ2VzX2NvbnRhaW5lci5hcHBlbmRDaGlsZCggcGFnZV9lbCApO1xyXG5cdFx0XHRpZiAoIHNjcm9sbCApIHtcclxuXHRcdFx0XHRwYWdlX2VsLnNjcm9sbEludG9WaWV3KCB7IGJlaGF2aW9yOiAnc21vb3RoJywgYmxvY2s6ICdzdGFydCcgfSApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb25zdCBzZWN0aW9uX2NvbnRhaW5lciAgICAgICAgID0gcGFnZV9lbC5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19mb3JtX3ByZXZpZXdfc2VjdGlvbl9jb250YWluZXInICk7XHJcblx0XHRcdGNvbnN0IHNlY3Rpb25fY291bnRfb25fYWRkX3BhZ2UgPSAyO1xyXG5cdFx0XHR0aGlzLmluaXRfc2VjdGlvbl9zb3J0YWJsZSggc2VjdGlvbl9jb250YWluZXIgKTtcclxuXHRcdFx0dGhpcy5hZGRfc2VjdGlvbiggc2VjdGlvbl9jb250YWluZXIsIHNlY3Rpb25fY291bnRfb25fYWRkX3BhZ2UgKTtcclxuXHJcblx0XHRcdC8vIERyb3Bkb3duIGNvbnRyb2wgY2xvbmVkIGZyb20gdGhlIGhpZGRlbiB0ZW1wbGF0ZS5cclxuXHRcdFx0Y29uc3QgY29udHJvbHNfaG9zdF90b3AgPSBwYWdlX2VsLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2NvbnRyb2xzJyApO1xyXG5cdFx0XHRjb25zdCBjdHJsX3RvcCAgICAgICAgICA9IHRoaXMuX21ha2VfYWRkX2NvbHVtbnNfY29udHJvbCggcGFnZV9lbCwgc2VjdGlvbl9jb250YWluZXIsICd0b3AnICk7XHJcblx0XHRcdGlmICggY3RybF90b3AgKSB7XHJcblx0XHRcdFx0Y29udHJvbHNfaG9zdF90b3AuYXBwZW5kQ2hpbGQoIGN0cmxfdG9wICk7XHJcblx0XHRcdH1cclxuXHRcdFx0Ly8gQm90dG9tIGNvbnRyb2wgYmFyIGFmdGVyIHRoZSBzZWN0aW9uIGNvbnRhaW5lci5cclxuXHRcdFx0Y29uc3QgY29udHJvbHNfaG9zdF9ib3R0b20gPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2RpdicsICd3cGJjX2JmYl9fY29udHJvbHMgd3BiY19iZmJfX2NvbnRyb2xzLS1ib3R0b20nICk7XHJcblx0XHRcdHNlY3Rpb25fY29udGFpbmVyLmFmdGVyKCBjb250cm9sc19ob3N0X2JvdHRvbSApO1xyXG5cdFx0XHRjb25zdCBjdHJsX2JvdHRvbSA9IHRoaXMuX21ha2VfYWRkX2NvbHVtbnNfY29udHJvbCggcGFnZV9lbCwgc2VjdGlvbl9jb250YWluZXIsICdib3R0b20nICk7XHJcblx0XHRcdGlmICggY3RybF9ib3R0b20gKSB7XHJcblx0XHRcdFx0Y29udHJvbHNfaG9zdF9ib3R0b20uYXBwZW5kQ2hpbGQoIGN0cmxfYm90dG9tICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHJldHVybiBwYWdlX2VsO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gY29udGFpbmVyXHJcblx0XHQgKiBAcGFyYW0ge251bWJlcn0gICAgICBjb2xzXHJcblx0XHQgKiBAcGFyYW0geyd0b3AnfCdib3R0b20nfSBbaW5zZXJ0X3Bvcz0nYm90dG9tJ10gIC8vIE5FV1xyXG5cdFx0ICovXHJcblx0XHRhZGRfc2VjdGlvbihjb250YWluZXIsIGNvbHMsIGluc2VydF9wb3MgPSAnYm90dG9tJykge1xyXG5cdFx0XHRjb25zdCBiID0gdGhpcy5idWlsZGVyO1xyXG5cdFx0XHRjb2xzICAgID0gTWF0aC5tYXgoIDEsIHBhcnNlSW50KCBjb2xzLCAxMCApIHx8IDEgKTtcclxuXHJcblx0XHRcdGNvbnN0IHNlY3Rpb24gPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2RpdicsICd3cGJjX2JmYl9fc2VjdGlvbicgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWlkJywgYHNlY3Rpb24tJHsrK2Iuc2VjdGlvbl9jb3VudGVyfS0ke0RhdGUubm93KCl9YCApO1xyXG5cdFx0XHRzZWN0aW9uLnNldEF0dHJpYnV0ZSggJ2RhdGEtdWlkJywgYHMtJHsrK2IuX3VpZF9jb3VudGVyfS0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZyggMzYgKS5zbGljZSggMiwgNyApfWAgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLXR5cGUnLCAnc2VjdGlvbicgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWxhYmVsJywgJ1NlY3Rpb24nICk7XHJcblx0XHRcdHNlY3Rpb24uc2V0QXR0cmlidXRlKCAnZGF0YS1jb2x1bW5zJywgU3RyaW5nKCBjb2xzICkgKTtcclxuXHRcdFx0Ly8gRG8gbm90IHBlcnNpc3Qgb3Igc2VlZCBwZXItY29sdW1uIHN0eWxlcyBieSBkZWZhdWx0IChvcHQtaW4gdmlhIGluc3BlY3RvcikuXHJcblxyXG5cdFx0XHRjb25zdCByb3cgPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2RpdicsICd3cGJjX2JmYl9fcm93IHdwYmNfX3JvdycgKTtcclxuXHRcdFx0Zm9yICggbGV0IGkgPSAwOyBpIDwgY29sczsgaSsrICkge1xyXG5cdFx0XHRcdGNvbnN0IGNvbCAgICAgICAgICAgPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5jcmVhdGVfZWxlbWVudCggJ2RpdicsICd3cGJjX2JmYl9fY29sdW1uIHdwYmNfX2ZpZWxkJyApO1xyXG5cdFx0XHRcdGNvbC5zdHlsZS5mbGV4QmFzaXMgPSAoMTAwIC8gY29scykgKyAnJSc7XHJcblx0XHRcdFx0Ly8gTm8gZGVmYXVsdCBDU1MgdmFycyBoZXJlOyByZWFsIGNvbHVtbnMgcmVtYWluIHVuYWZmZWN0ZWQgdW50aWwgdXNlciBhY3RpdmF0ZXMgc3R5bGVzLlxyXG5cdFx0XHRcdGIuaW5pdF9zb3J0YWJsZT8uKCBjb2wgKTtcclxuXHRcdFx0XHRyb3cuYXBwZW5kQ2hpbGQoIGNvbCApO1xyXG5cdFx0XHRcdGlmICggaSA8IGNvbHMgLSAxICkge1xyXG5cdFx0XHRcdFx0Y29uc3QgcmVzaXplciA9IENvcmUuV1BCQ19Gb3JtX0J1aWxkZXJfSGVscGVyLmNyZWF0ZV9lbGVtZW50KCAnZGl2JywgJ3dwYmNfYmZiX19jb2x1bW4tcmVzaXplcicgKTtcclxuXHRcdFx0XHRcdHJlc2l6ZXIuYWRkRXZlbnRMaXN0ZW5lciggJ21vdXNlZG93bicsIGIuaW5pdF9yZXNpemVfaGFuZGxlciApO1xyXG5cdFx0XHRcdFx0cm93LmFwcGVuZENoaWxkKCByZXNpemVyICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHNlY3Rpb24uYXBwZW5kQ2hpbGQoIHJvdyApO1xyXG5cdFx0XHRiLmxheW91dC5zZXRfZXF1YWxfYmFzZXMoIHJvdywgYi5jb2xfZ2FwX3BlcmNlbnQgKTtcclxuXHRcdFx0Yi5hZGRfb3ZlcmxheV90b29sYmFyKCBzZWN0aW9uICk7XHJcblx0XHRcdHNlY3Rpb24uc2V0QXR0cmlidXRlKCAndGFiaW5kZXgnLCAnMCcgKTtcclxuXHRcdFx0dGhpcy5pbml0X2FsbF9uZXN0ZWRfc29ydGFibGVzKCBzZWN0aW9uICk7XHJcblxyXG5cdFx0XHQvLyBJbnNlcnRpb24gcG9saWN5OiB0b3AgfCBib3R0b20uXHJcblx0XHRcdGlmICggaW5zZXJ0X3BvcyA9PT0gJ3RvcCcgJiYgY29udGFpbmVyLmZpcnN0RWxlbWVudENoaWxkICkge1xyXG5cdFx0XHRcdGNvbnRhaW5lci5pbnNlcnRCZWZvcmUoIHNlY3Rpb24sIGNvbnRhaW5lci5maXJzdEVsZW1lbnRDaGlsZCApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZCggc2VjdGlvbiApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gc2VjdGlvbl9kYXRhXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBjb250YWluZXJcclxuXHRcdCAqIEByZXR1cm5zIHtIVE1MRWxlbWVudH0gVGhlIHJlYnVpbHQgc2VjdGlvbiBlbGVtZW50LlxyXG5cdFx0ICovXHJcblx0XHRyZWJ1aWxkX3NlY3Rpb24oc2VjdGlvbl9kYXRhLCBjb250YWluZXIpIHtcclxuXHRcdFx0Y29uc3QgYiAgICAgICAgID0gdGhpcy5idWlsZGVyO1xyXG5cdFx0XHRjb25zdCBjb2xzX2RhdGEgPSBBcnJheS5pc0FycmF5KCBzZWN0aW9uX2RhdGE/LmNvbHVtbnMgKSA/IHNlY3Rpb25fZGF0YS5jb2x1bW5zIDogW107XHJcblx0XHRcdHRoaXMuYWRkX3NlY3Rpb24oIGNvbnRhaW5lciwgY29sc19kYXRhLmxlbmd0aCB8fCAxICk7XHJcblx0XHRcdGNvbnN0IHNlY3Rpb24gPSBjb250YWluZXIubGFzdEVsZW1lbnRDaGlsZDtcclxuXHRcdFx0aWYgKCAhc2VjdGlvbi5kYXRhc2V0LnVpZCApIHtcclxuXHRcdFx0XHRzZWN0aW9uLnNldEF0dHJpYnV0ZSggJ2RhdGEtdWlkJywgYHMtJHsrK2IuX3VpZF9jb3VudGVyfS0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZyggMzYgKS5zbGljZSggMiwgNyApfWAgKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRzZWN0aW9uLnNldEF0dHJpYnV0ZSggJ2RhdGEtaWQnLCBzZWN0aW9uX2RhdGE/LmlkIHx8IGBzZWN0aW9uLSR7KytiLnNlY3Rpb25fY291bnRlcn0tJHtEYXRlLm5vdygpfWAgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLXR5cGUnLCAnc2VjdGlvbicgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWxhYmVsJywgc2VjdGlvbl9kYXRhPy5sYWJlbCB8fCAnU2VjdGlvbicgKTtcclxuXHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWNvbHVtbnMnLCBTdHJpbmcoIChzZWN0aW9uX2RhdGE/LmNvbHVtbnMgfHwgW10pLmxlbmd0aCB8fCAxICkgKTtcclxuXHRcdFx0Ly8gUGVyc2lzdGVkIGF0dHJpYnV0ZXNcclxuXHRcdFx0aWYgKCBzZWN0aW9uX2RhdGE/Lmh0bWxfaWQgKSB7XHJcblx0XHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWh0bWxfaWQnLCBTdHJpbmcoIHNlY3Rpb25fZGF0YS5odG1sX2lkICkgKTtcclxuXHRcdFx0XHQvLyBnaXZlIHRoZSBjb250YWluZXIgYSByZWFsIGlkIHNvIGFuY2hvcnMvQ1NTIGNhbiB0YXJnZXQgaXRcclxuXHRcdFx0XHRzZWN0aW9uLmlkID0gU3RyaW5nKCBzZWN0aW9uX2RhdGEuaHRtbF9pZCApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBORVc6IHJlc3RvcmUgcGVyc2lzdGVkIHBlci1jb2x1bW4gc3R5bGVzIChyYXcgSlNPTiBzdHJpbmcpLlxyXG5cdFx0XHRpZiAoIHNlY3Rpb25fZGF0YT8uY29sX3N0eWxlcyAhPSBudWxsICkge1xyXG5cdFx0XHRcdGNvbnN0IGpzb24gPSBTdHJpbmcoIHNlY3Rpb25fZGF0YS5jb2xfc3R5bGVzICk7XHJcblx0XHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWNvbF9zdHlsZXMnLCBqc29uICk7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHNlY3Rpb24uZGF0YXNldC5jb2xfc3R5bGVzID0ganNvbjtcclxuXHRcdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdC8vIChObyByZW5kZXJfcHJldmlldygpIGNhbGwgaGVyZSBvbiBwdXJwb3NlOiBzZWN0aW9uc+KAmSBidWlsZGVyIERPTSB1c2VzIC53cGJjX2JmYl9fcm93Ly53cGJjX2JmYl9fY29sdW1uLilcclxuXHJcblxyXG5cdFx0XHRpZiAoIHNlY3Rpb25fZGF0YT8uY3NzY2xhc3MgKSB7XHJcblx0XHRcdFx0c2VjdGlvbi5zZXRBdHRyaWJ1dGUoICdkYXRhLWNzc2NsYXNzJywgU3RyaW5nKCBzZWN0aW9uX2RhdGEuY3NzY2xhc3MgKSApO1xyXG5cdFx0XHRcdC8vIGtlZXAgY29yZSBjbGFzc2VzLCB0aGVuIGFkZCBjdXN0b20gY2xhc3MoZXMpXHJcblx0XHRcdFx0U3RyaW5nKCBzZWN0aW9uX2RhdGEuY3NzY2xhc3MgKS5zcGxpdCggL1xccysvICkuZmlsdGVyKCBCb29sZWFuICkuZm9yRWFjaCggY2xzID0+IHNlY3Rpb24uY2xhc3NMaXN0LmFkZCggY2xzICkgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3Qgcm93ID0gc2VjdGlvbi5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19yb3cnICk7XHJcblx0XHRcdC8vIERlbGVnYXRlIHBhcnNpbmcgKyBhY3RpdmF0aW9uICsgYXBwbGljYXRpb24gdG8gdGhlIENvbHVtbiBTdHlsZXMgc2VydmljZS5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRjb25zdCBqc29uID0gc2VjdGlvbi5nZXRBdHRyaWJ1dGUoICdkYXRhLWNvbF9zdHlsZXMnIClcclxuXHRcdFx0XHRcdHx8IChzZWN0aW9uLmRhdGFzZXQgPyAoc2VjdGlvbi5kYXRhc2V0LmNvbF9zdHlsZXMgfHwgJycpIDogJycpO1xyXG5cdFx0XHRcdGNvbnN0IGFyciAgPSBVSS5XUEJDX0JGQl9Db2x1bW5fU3R5bGVzLnBhcnNlX2NvbF9zdHlsZXMoIGpzb24gKTtcclxuXHRcdFx0XHRVSS5XUEJDX0JGQl9Db2x1bW5fU3R5bGVzLmFwcGx5KCBzZWN0aW9uLCBhcnIgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF9lICkge1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb2xzX2RhdGEuZm9yRWFjaCggKGNvbF9kYXRhLCBpbmRleCkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGNvbHVtbnNfb25seSAgPSByb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApO1xyXG5cdFx0XHRcdGNvbnN0IGNvbCAgICAgICAgICAgPSBjb2x1bW5zX29ubHlbaW5kZXhdO1xyXG5cdFx0XHRcdGNvbC5zdHlsZS5mbGV4QmFzaXMgPSBjb2xfZGF0YS53aWR0aCB8fCAnMTAwJSc7XHJcblx0XHRcdFx0KGNvbF9kYXRhLml0ZW1zIHx8IFtdKS5mb3JFYWNoKCAoaXRlbSkgPT4ge1xyXG5cdFx0XHRcdFx0aWYgKCAhaXRlbSB8fCAhaXRlbS50eXBlICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRpZiAoIGl0ZW0udHlwZSA9PT0gJ2ZpZWxkJyApIHtcclxuXHRcdFx0XHRcdFx0Y29uc3QgZWwgPSBiLmJ1aWxkX2ZpZWxkKCBpdGVtLmRhdGEgKTtcclxuXHRcdFx0XHRcdFx0aWYgKCBlbCApIHtcclxuXHRcdFx0XHRcdFx0XHRjb2wuYXBwZW5kQ2hpbGQoIGVsICk7XHJcblx0XHRcdFx0XHRcdFx0Yi50cmlnZ2VyX2ZpZWxkX2Ryb3BfY2FsbGJhY2soIGVsLCAnbG9hZCcgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRpZiAoIGl0ZW0udHlwZSA9PT0gJ3NlY3Rpb24nICkge1xyXG5cdFx0XHRcdFx0XHR0aGlzLnJlYnVpbGRfc2VjdGlvbiggaXRlbS5kYXRhLCBjb2wgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdH0gKTtcclxuXHRcdFx0Y29uc3QgY29tcHV0ZWQgPSBiLmxheW91dC5jb21wdXRlX2VmZmVjdGl2ZV9iYXNlc19mcm9tX3Jvdyggcm93LCBiLmNvbF9nYXBfcGVyY2VudCApO1xyXG5cdFx0XHRiLmxheW91dC5hcHBseV9iYXNlc190b19yb3coIHJvdywgY29tcHV0ZWQuYmFzZXMgKTtcclxuXHRcdFx0dGhpcy5pbml0X2FsbF9uZXN0ZWRfc29ydGFibGVzKCBzZWN0aW9uICk7XHJcblxyXG5cdFx0XHQvLyBORVc6IHJldGFnIFVJRHMgZmlyc3QgKHNvIHVuaXF1ZW5lc3MgY2hlY2tzIGRvbid0IGV4Y2x1ZGUgb3JpZ2luYWxzKSwgdGhlbiBkZWR1cGUgYWxsIGtleXMuXHJcblx0XHRcdHRoaXMuX3JldGFnX3VpZHNfaW5fc3VidHJlZSggc2VjdGlvbiApO1xyXG5cdFx0XHR0aGlzLl9kZWR1cGVfc3VidHJlZV9zdHJpY3QoIHNlY3Rpb24gKTtcclxuXHRcdFx0cmV0dXJuIHNlY3Rpb247XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciAqL1xyXG5cdFx0aW5pdF9hbGxfbmVzdGVkX3NvcnRhYmxlcyhjb250YWluZXIpIHtcclxuXHRcdFx0Y29uc3QgYiA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0aWYgKCBjb250YWluZXIuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lcicgKSApIHtcclxuXHRcdFx0XHR0aGlzLmluaXRfc2VjdGlvbl9zb3J0YWJsZSggY29udGFpbmVyICk7XHJcblx0XHRcdH1cclxuXHRcdFx0Y29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoICcud3BiY19iZmJfX3NlY3Rpb24nICkuZm9yRWFjaCggKHNlY3Rpb24pID0+IHtcclxuXHRcdFx0XHRzZWN0aW9uLnF1ZXJ5U2VsZWN0b3JBbGwoICcud3BiY19iZmJfX2NvbHVtbicgKS5mb3JFYWNoKCAoY29sKSA9PiB7XHJcblx0XHRcdFx0XHR0aGlzLmluaXRfc2VjdGlvbl9zb3J0YWJsZSggY29sICk7XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciAqL1xyXG5cdFx0aW5pdF9zZWN0aW9uX3NvcnRhYmxlKGNvbnRhaW5lcikge1xyXG5cdFx0XHRjb25zdCBiID0gdGhpcy5idWlsZGVyO1xyXG5cdFx0XHRpZiAoICFjb250YWluZXIgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGNvbnN0IGlzX2NvbHVtbiAgICA9IGNvbnRhaW5lci5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fY29sdW1uJyApO1xyXG5cdFx0XHRjb25zdCBpc190b3BfbGV2ZWwgPSBjb250YWluZXIuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lcicgKTtcclxuXHRcdFx0aWYgKCAhaXNfY29sdW1uICYmICFpc190b3BfbGV2ZWwgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGIuaW5pdF9zb3J0YWJsZT8uKCBjb250YWluZXIgKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHQgKiBTZXJpYWxpemF0aW9uIGFuZCBkZXNlcmlhbGl6YXRpb24gb2YgcGFnZXMvc2VjdGlvbnMvZmllbGRzLlxyXG5cdCAqL1xyXG5cdFVJLldQQkNfQkZCX1N0cnVjdHVyZV9JTyA9IGNsYXNzIGV4dGVuZHMgVUkuV1BCQ19CRkJfTW9kdWxlIHtcclxuXHRcdGluaXQoKSB7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5nZXRfc3RydWN0dXJlICAgICAgICA9ICgpID0+IHRoaXMuc2VyaWFsaXplKCk7XHJcblx0XHRcdHRoaXMuYnVpbGRlci5sb2FkX3NhdmVkX3N0cnVjdHVyZSA9IChzLCBvcHRzKSA9PiB0aGlzLmRlc2VyaWFsaXplKCBzLCBvcHRzICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIEByZXR1cm5zIHtBcnJheX0gKi9cclxuXHRcdHNlcmlhbGl6ZSgpIHtcclxuXHRcdFx0Y29uc3QgYiA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0dGhpcy5fbm9ybWFsaXplX2lkcygpO1xyXG5cdFx0XHR0aGlzLl9ub3JtYWxpemVfbmFtZXMoKTtcclxuXHRcdFx0Y29uc3QgcGFnZXMgPSBbXTtcclxuXHRcdFx0Yi5wYWdlc19jb250YWluZXIucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcnICkuZm9yRWFjaCggKHBhZ2VfZWwsIHBhZ2VfaW5kZXgpID0+IHtcclxuXHRcdFx0XHRjb25zdCBjb250YWluZXIgPSBwYWdlX2VsLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lcicgKTtcclxuXHRcdFx0XHRjb25zdCBjb250ZW50ICAgPSBbXTtcclxuXHRcdFx0XHRpZiAoICFjb250YWluZXIgKSB7XHJcblx0XHRcdFx0XHRwYWdlcy5wdXNoKCB7IHBhZ2U6IHBhZ2VfaW5kZXggKyAxLCBjb250ZW50IH0gKTtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0Y29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoICc6c2NvcGUgPiAqJyApLmZvckVhY2goIChjaGlsZCkgPT4ge1xyXG5cdFx0XHRcdFx0aWYgKCBjaGlsZC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSApIHtcclxuXHRcdFx0XHRcdFx0Y29udGVudC5wdXNoKCB7IHR5cGU6ICdzZWN0aW9uJywgZGF0YTogdGhpcy5zZXJpYWxpemVfc2VjdGlvbiggY2hpbGQgKSB9ICk7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGlmICggY2hpbGQuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2ZpZWxkJyApICkge1xyXG5cdFx0XHRcdFx0XHRpZiAoIGNoaWxkLmNsYXNzTGlzdC5jb250YWlucyggJ2lzLWludmFsaWQnICkgKSB7XHJcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdGNvbnN0IGZfZGF0YSA9IENvcmUuV1BCQ19Gb3JtX0J1aWxkZXJfSGVscGVyLmdldF9hbGxfZGF0YV9hdHRyaWJ1dGVzKCBjaGlsZCApO1xyXG5cdFx0XHRcdFx0XHQvLyBEcm9wIGVwaGVtZXJhbC9lZGl0b3Itb25seSBmbGFnc1xyXG5cdFx0XHRcdFx0XHRbICd1aWQnLCAnZnJlc2gnLCAnYXV0b25hbWUnLCAnd2FzX2xvYWRlZCcsICduYW1lX3VzZXJfdG91Y2hlZCcgXVxyXG5cdFx0XHRcdFx0XHRcdC5mb3JFYWNoKCBrID0+IHtcclxuXHRcdFx0XHRcdFx0XHRcdGlmICggayBpbiBmX2RhdGEgKSBkZWxldGUgZl9kYXRhW2tdO1xyXG5cdFx0XHRcdFx0XHRcdH0gKTtcclxuXHRcdFx0XHRcdFx0Y29udGVudC5wdXNoKCB7IHR5cGU6ICdmaWVsZCcsIGRhdGE6IGZfZGF0YSB9ICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdHBhZ2VzLnB1c2goIHsgcGFnZTogcGFnZV9pbmRleCArIDEsIGNvbnRlbnQgfSApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdHJldHVybiBwYWdlcztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHNlY3Rpb25fZWxcclxuXHRcdCAqIEByZXR1cm5zIHt7aWQ6c3RyaW5nLGxhYmVsOnN0cmluZyxodG1sX2lkOnN0cmluZyxjc3NjbGFzczpzdHJpbmcsY29sX3N0eWxlczpzdHJpbmcsY29sdW1uczpBcnJheX19XHJcblx0XHQgKi9cclxuXHRcdHNlcmlhbGl6ZV9zZWN0aW9uKHNlY3Rpb25fZWwpIHtcclxuXHRcdFx0Y29uc3Qgcm93ID0gc2VjdGlvbl9lbC5xdWVyeVNlbGVjdG9yKCAnOnNjb3BlID4gLndwYmNfYmZiX19yb3cnICk7XHJcblxyXG5cdFx0XHQvLyBORVc6IHJlYWQgcGVyLWNvbHVtbiBzdHlsZXMgZnJvbSBkYXRhc2V0L2F0dHJpYnV0ZXMgKHVuZGVyc2NvcmUgJiBoeXBoZW4pXHJcblx0XHRcdHZhciBjb2xfc3R5bGVzX3JhdyA9XHJcblx0XHRcdFx0XHRzZWN0aW9uX2VsLmdldEF0dHJpYnV0ZSggJ2RhdGEtY29sX3N0eWxlcycgKSB8fFxyXG5cdFx0XHRcdFx0KHNlY3Rpb25fZWwuZGF0YXNldCA/IChzZWN0aW9uX2VsLmRhdGFzZXQuY29sX3N0eWxlcykgOiAnJykgfHxcclxuXHRcdFx0XHRcdCcnO1xyXG5cclxuXHRcdFx0Y29uc3QgYmFzZSA9IHtcclxuXHRcdFx0XHRpZCAgICAgICAgOiBzZWN0aW9uX2VsLmRhdGFzZXQuaWQsXHJcblx0XHRcdFx0bGFiZWwgICAgIDogc2VjdGlvbl9lbC5kYXRhc2V0LmxhYmVsIHx8ICcnLFxyXG5cdFx0XHRcdGh0bWxfaWQgICA6IHNlY3Rpb25fZWwuZGF0YXNldC5odG1sX2lkIHx8ICcnLFxyXG5cdFx0XHRcdGNzc2NsYXNzICA6IHNlY3Rpb25fZWwuZGF0YXNldC5jc3NjbGFzcyB8fCAnJyxcclxuXHRcdFx0XHRjb2xfc3R5bGVzOiBTdHJpbmcoIGNvbF9zdHlsZXNfcmF3ICkgICAgICAgIC8vIDwtLSBORVc6IGtlZXAgYXMgcmF3IEpTT04gc3RyaW5nXHJcblx0XHRcdH07XHJcblxyXG5cdFx0XHRpZiAoICFyb3cgKSB7XHJcblx0XHRcdFx0cmV0dXJuIE9iamVjdC5hc3NpZ24oIHt9LCBiYXNlLCB7IGNvbHVtbnM6IFtdIH0gKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3QgY29sdW1ucyA9IFtdO1xyXG5cdFx0XHRyb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApLmZvckVhY2goIGZ1bmN0aW9uIChjb2wpIHtcclxuXHRcdFx0XHRjb25zdCB3aWR0aCA9IGNvbC5zdHlsZS5mbGV4QmFzaXMgfHwgJzEwMCUnO1xyXG5cdFx0XHRcdGNvbnN0IGl0ZW1zID0gW107XHJcblx0XHRcdFx0QXJyYXkucHJvdG90eXBlLmZvckVhY2guY2FsbCggY29sLmNoaWxkcmVuLCBmdW5jdGlvbiAoY2hpbGQpIHtcclxuXHRcdFx0XHRcdGlmICggY2hpbGQuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX3NlY3Rpb24nICkgKSB7XHJcblx0XHRcdFx0XHRcdGl0ZW1zLnB1c2goIHsgdHlwZTogJ3NlY3Rpb24nLCBkYXRhOiB0aGlzLnNlcmlhbGl6ZV9zZWN0aW9uKCBjaGlsZCApIH0gKTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCBjaGlsZC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fZmllbGQnICkgKSB7XHJcblx0XHRcdFx0XHRcdGlmICggY2hpbGQuY2xhc3NMaXN0LmNvbnRhaW5zKCAnaXMtaW52YWxpZCcgKSApIHtcclxuXHRcdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0Y29uc3QgZl9kYXRhID0gQ29yZS5XUEJDX0Zvcm1fQnVpbGRlcl9IZWxwZXIuZ2V0X2FsbF9kYXRhX2F0dHJpYnV0ZXMoIGNoaWxkICk7XHJcblx0XHRcdFx0XHRcdFsgJ3VpZCcsICdmcmVzaCcsICdhdXRvbmFtZScsICd3YXNfbG9hZGVkJywgJ25hbWVfdXNlcl90b3VjaGVkJyBdLmZvckVhY2goIGZ1bmN0aW9uIChrKSB7XHJcblx0XHRcdFx0XHRcdFx0aWYgKCBrIGluIGZfZGF0YSApIHtcclxuXHRcdFx0XHRcdFx0XHRcdGRlbGV0ZSBmX2RhdGFba107XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9ICk7XHJcblx0XHRcdFx0XHRcdGl0ZW1zLnB1c2goIHsgdHlwZTogJ2ZpZWxkJywgZGF0YTogZl9kYXRhIH0gKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9LmJpbmQoIHRoaXMgKSApO1xyXG5cdFx0XHRcdGNvbHVtbnMucHVzaCggeyB3aWR0aDogd2lkdGgsIGl0ZW1zOiBpdGVtcyB9ICk7XHJcblx0XHRcdH0uYmluZCggdGhpcyApICk7XHJcblxyXG5cdFx0XHQvLyBDbGFtcCBwZXJzaXN0ZWQgY29sX3N0eWxlcyB0byB0aGUgYWN0dWFsIG51bWJlciBvZiBjb2x1bW5zIG9uIFNhdmUuXHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0Y29uc3QgY29sQ291bnQgPSBjb2x1bW5zLmxlbmd0aDtcclxuXHRcdFx0XHRjb25zdCByYXcgICAgICA9IFN0cmluZyggY29sX3N0eWxlc19yYXcgfHwgJycgKS50cmltKCk7XHJcblxyXG5cdFx0XHRcdGlmICggcmF3ICkge1xyXG5cdFx0XHRcdFx0bGV0IGFyciA9IFtdO1xyXG5cdFx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdFx0Y29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZSggcmF3ICk7XHJcblx0XHRcdFx0XHRcdGFyciAgICAgICAgICA9IEFycmF5LmlzQXJyYXkoIHBhcnNlZCApID8gcGFyc2VkIDogKHBhcnNlZCAmJiBBcnJheS5pc0FycmF5KCBwYXJzZWQuY29sdW1ucyApID8gcGFyc2VkLmNvbHVtbnMgOiBbXSk7XHJcblx0XHRcdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdFx0XHRcdGFyciA9IFtdO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdGlmICggY29sQ291bnQgPD0gMCApIHtcclxuXHRcdFx0XHRcdFx0YmFzZS5jb2xfc3R5bGVzID0gJ1tdJztcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdGlmICggYXJyLmxlbmd0aCA+IGNvbENvdW50ICkgYXJyLmxlbmd0aCA9IGNvbENvdW50O1xyXG5cdFx0XHRcdFx0XHR3aGlsZSAoIGFyci5sZW5ndGggPCBjb2xDb3VudCApIGFyci5wdXNoKCB7fSApO1xyXG5cdFx0XHRcdFx0XHRiYXNlLmNvbF9zdHlsZXMgPSBKU09OLnN0cmluZ2lmeSggYXJyICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdGJhc2UuY29sX3N0eWxlcyA9ICcnO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBjYXRjaCAoIF9lICkge1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRyZXR1cm4gT2JqZWN0LmFzc2lnbigge30sIGJhc2UsIHsgY29sdW1uczogY29sdW1ucyB9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBAcGFyYW0ge0FycmF5fSBzdHJ1Y3R1cmVcclxuXHRcdCAqIEBwYXJhbSB7e2RlZmVySWZUeXBpbmc/OiBib29sZWFufX0gW29wdHMgPSB7fV1cclxuXHRcdCAqL1xyXG5cdFx0ZGVzZXJpYWxpemUoc3RydWN0dXJlLCB7IGRlZmVySWZUeXBpbmcgPSB0cnVlIH0gPSB7fSkge1xyXG5cdFx0XHRjb25zdCBiID0gdGhpcy5idWlsZGVyO1xyXG5cdFx0XHRpZiAoIGRlZmVySWZUeXBpbmcgJiYgdGhpcy5faXNfdHlwaW5nX2luX2luc3BlY3RvcigpICkge1xyXG5cdFx0XHRcdGNsZWFyVGltZW91dCggdGhpcy5fZGVmZXJfdGltZXIgKTtcclxuXHRcdFx0XHR0aGlzLl9kZWZlcl90aW1lciA9IHNldFRpbWVvdXQoICgpID0+IHtcclxuXHRcdFx0XHRcdHRoaXMuZGVzZXJpYWxpemUoIHN0cnVjdHVyZSwgeyBkZWZlcklmVHlwaW5nOiBmYWxzZSB9ICk7XHJcblx0XHRcdFx0fSwgMTUwICk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGIucGFnZXNfY29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG5cdFx0XHRiLnBhZ2VfY291bnRlciAgICAgICAgICAgICAgPSAwO1xyXG5cdFx0XHQoc3RydWN0dXJlIHx8IFtdKS5mb3JFYWNoKCAocGFnZV9kYXRhKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcGFnZV9lbCAgICAgICAgICAgICAgID0gYi5wYWdlc19zZWN0aW9ucy5hZGRfcGFnZSggeyBzY3JvbGw6IGZhbHNlIH0gKTtcclxuXHRcdFx0XHRjb25zdCBzZWN0aW9uX2NvbnRhaW5lciAgICAgPSBwYWdlX2VsLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2Zvcm1fcHJldmlld19zZWN0aW9uX2NvbnRhaW5lcicgKTtcclxuXHRcdFx0XHRzZWN0aW9uX2NvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcclxuXHRcdFx0XHRiLmluaXRfc2VjdGlvbl9zb3J0YWJsZT8uKCBzZWN0aW9uX2NvbnRhaW5lciApO1xyXG5cdFx0XHRcdChwYWdlX2RhdGEuY29udGVudCB8fCBbXSkuZm9yRWFjaCggKGl0ZW0pID0+IHtcclxuXHRcdFx0XHRcdGlmICggaXRlbS50eXBlID09PSAnc2VjdGlvbicgKSB7XHJcblx0XHRcdFx0XHRcdC8vIE5vdyByZXR1cm5zIHRoZSBlbGVtZW50OyBhdHRyaWJ1dGVzIChpbmNsLiBjb2xfc3R5bGVzKSBhcmUgYXBwbGllZCBpbnNpZGUgcmVidWlsZC5cclxuXHRcdFx0XHRcdFx0Yi5wYWdlc19zZWN0aW9ucy5yZWJ1aWxkX3NlY3Rpb24oIGl0ZW0uZGF0YSwgc2VjdGlvbl9jb250YWluZXIgKTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCBpdGVtLnR5cGUgPT09ICdmaWVsZCcgKSB7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGVsID0gYi5idWlsZF9maWVsZCggaXRlbS5kYXRhICk7XHJcblx0XHRcdFx0XHRcdGlmICggZWwgKSB7XHJcblx0XHRcdFx0XHRcdFx0c2VjdGlvbl9jb250YWluZXIuYXBwZW5kQ2hpbGQoIGVsICk7XHJcblx0XHRcdFx0XHRcdFx0Yi50cmlnZ2VyX2ZpZWxkX2Ryb3BfY2FsbGJhY2soIGVsLCAnbG9hZCcgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0XHRiLnVzYWdlPy51cGRhdGVfcGFsZXR0ZV91aT8uKCk7XHJcblx0XHRcdGIuYnVzLmVtaXQoIENvcmUuV1BCQ19CRkJfRXZlbnRzLlNUUlVDVFVSRV9MT0FERUQsIHsgc3RydWN0dXJlIH0gKTtcclxuXHRcdH1cclxuXHJcblx0XHRfbm9ybWFsaXplX2lkcygpIHtcclxuXHRcdFx0Y29uc3QgYiA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0Yi5wYWdlc19jb250YWluZXIucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9fcGFuZWwtLXByZXZpZXcgLndwYmNfYmZiX19maWVsZDpub3QoLmlzLWludmFsaWQpJyApLmZvckVhY2goIChlbCkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IGRhdGEgPSBDb3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5nZXRfYWxsX2RhdGFfYXR0cmlidXRlcyggZWwgKTtcclxuXHRcdFx0XHRjb25zdCB3YW50ID0gQ29yZS5XUEJDX0JGQl9TYW5pdGl6ZS5zYW5pdGl6ZV9odG1sX2lkKCBkYXRhLmlkIHx8ICcnICkgfHwgJ2ZpZWxkJztcclxuXHRcdFx0XHRjb25zdCB1bmlxID0gYi5pZC5lbnN1cmVfdW5pcXVlX2ZpZWxkX2lkKCB3YW50LCBlbCApO1xyXG5cdFx0XHRcdGlmICggZGF0YS5pZCAhPT0gdW5pcSApIHtcclxuXHRcdFx0XHRcdGVsLnNldEF0dHJpYnV0ZSggJ2RhdGEtaWQnLCB1bmlxICk7XHJcblx0XHRcdFx0XHRpZiAoIGIucHJldmlld19tb2RlICkge1xyXG5cdFx0XHRcdFx0XHRiLnJlbmRlcl9wcmV2aWV3KCBlbCApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9ub3JtYWxpemVfbmFtZXMoKSB7XHJcblx0XHRcdGNvbnN0IGIgPSB0aGlzLmJ1aWxkZXI7XHJcblx0XHRcdGIucGFnZXNfY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoICcud3BiY19iZmJfX3BhbmVsLS1wcmV2aWV3IC53cGJjX2JmYl9fZmllbGQ6bm90KC5pcy1pbnZhbGlkKScgKS5mb3JFYWNoKCAoZWwpID0+IHtcclxuXHRcdFx0XHRjb25zdCBkYXRhID0gQ29yZS5XUEJDX0Zvcm1fQnVpbGRlcl9IZWxwZXIuZ2V0X2FsbF9kYXRhX2F0dHJpYnV0ZXMoIGVsICk7XHJcblx0XHRcdFx0Y29uc3QgYmFzZSA9IENvcmUuV1BCQ19CRkJfU2FuaXRpemUuc2FuaXRpemVfaHRtbF9uYW1lKCAoZGF0YS5uYW1lICE9IG51bGwpID8gZGF0YS5uYW1lIDogZGF0YS5pZCApIHx8ICdmaWVsZCc7XHJcblx0XHRcdFx0Y29uc3QgdW5pcSA9IGIuaWQuZW5zdXJlX3VuaXF1ZV9maWVsZF9uYW1lKCBiYXNlLCBlbCApO1xyXG5cdFx0XHRcdGlmICggZGF0YS5uYW1lICE9PSB1bmlxICkge1xyXG5cdFx0XHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1uYW1lJywgdW5pcSApO1xyXG5cdFx0XHRcdFx0aWYgKCBiLnByZXZpZXdfbW9kZSApIHtcclxuXHRcdFx0XHRcdFx0Yi5yZW5kZXJfcHJldmlldyggZWwgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH0gKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKiogQHJldHVybnMge2Jvb2xlYW59ICovXHJcblx0XHRfaXNfdHlwaW5nX2luX2luc3BlY3RvcigpIHtcclxuXHRcdFx0Y29uc3QgaW5zID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICd3cGJjX2JmYl9faW5zcGVjdG9yJyApO1xyXG5cdFx0XHRyZXR1cm4gISEoaW5zICYmIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgJiYgaW5zLmNvbnRhaW5zKCBkb2N1bWVudC5hY3RpdmVFbGVtZW50ICkpO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8qKlxyXG5cdCAqIE1pbmltYWwsIHN0YW5kYWxvbmUgZ3VhcmQgdGhhdCBlbmZvcmNlcyBwZXItY29sdW1uIG1pbiB3aWR0aHMgYmFzZWQgb24gZmllbGRzJyBkYXRhLW1pbl93aWR0aC5cclxuXHQgKlxyXG5cdCAqIEB0eXBlIHtVSS5XUEJDX0JGQl9NaW5fV2lkdGhfR3VhcmR9XHJcblx0ICovXHJcblx0VUkuV1BCQ19CRkJfTWluX1dpZHRoX0d1YXJkID0gY2xhc3MgZXh0ZW5kcyBVSS5XUEJDX0JGQl9Nb2R1bGUge1xyXG5cclxuXHRcdGNvbnN0cnVjdG9yKGJ1aWxkZXIpIHtcclxuXHRcdFx0c3VwZXIoIGJ1aWxkZXIgKTtcclxuXHRcdFx0dGhpcy5fb25fZmllbGRfYWRkICAgICAgICA9IHRoaXMuX29uX2ZpZWxkX2FkZC5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuX29uX2ZpZWxkX3JlbW92ZSAgICAgPSB0aGlzLl9vbl9maWVsZF9yZW1vdmUuYmluZCggdGhpcyApO1xyXG5cdFx0XHR0aGlzLl9vbl9zdHJ1Y3R1cmVfbG9hZGVkID0gdGhpcy5fb25fc3RydWN0dXJlX2xvYWRlZC5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuX29uX3N0cnVjdHVyZV9jaGFuZ2UgPSB0aGlzLl9vbl9zdHJ1Y3R1cmVfY2hhbmdlLmJpbmQoIHRoaXMgKTtcclxuXHRcdFx0dGhpcy5fb25fd2luZG93X3Jlc2l6ZSAgICA9IHRoaXMuX29uX3dpbmRvd19yZXNpemUuYmluZCggdGhpcyApO1xyXG5cclxuXHRcdFx0dGhpcy5fcGVuZGluZ19yb3dzID0gbmV3IFNldCgpO1xyXG5cdFx0XHR0aGlzLl9wZW5kaW5nX2FsbCAgPSBmYWxzZTtcclxuXHRcdFx0dGhpcy5fcmFmX2lkICAgICAgID0gMDtcclxuXHRcdH1cclxuXHJcblx0XHRpbml0KCkge1xyXG5cdFx0XHRjb25zdCBFViA9IENvcmUuV1BCQ19CRkJfRXZlbnRzO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXI/LmJ1cz8ub24/LiggRVYuRklFTERfQURELCB0aGlzLl9vbl9maWVsZF9hZGQgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyPy5idXM/Lm9uPy4oIEVWLkZJRUxEX1JFTU9WRSwgdGhpcy5fb25fZmllbGRfcmVtb3ZlICk7XHJcblx0XHRcdHRoaXMuYnVpbGRlcj8uYnVzPy5vbj8uKCBFVi5TVFJVQ1RVUkVfTE9BREVELCB0aGlzLl9vbl9zdHJ1Y3R1cmVfbG9hZGVkICk7XHJcblx0XHRcdC8vIFJlZnJlc2ggc2VsZWN0aXZlbHkgb24gc3RydWN0dXJlIGNoYW5nZSAoTk9UIG9uIGV2ZXJ5IHByb3AgaW5wdXQpLlxyXG5cdFx0XHR0aGlzLmJ1aWxkZXI/LmJ1cz8ub24/LiggRVYuU1RSVUNUVVJFX0NIQU5HRSwgdGhpcy5fb25fc3RydWN0dXJlX2NoYW5nZSApO1xyXG5cclxuXHRcdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoICdyZXNpemUnLCB0aGlzLl9vbl93aW5kb3dfcmVzaXplLCB7IHBhc3NpdmU6IHRydWUgfSApO1xyXG5cdFx0XHR0aGlzLl9zY2hlZHVsZV9yZWZyZXNoX2FsbCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGRlc3Ryb3koKSB7XHJcblx0XHRcdGNvbnN0IEVWID0gQ29yZS5XUEJDX0JGQl9FdmVudHM7XHJcblx0XHRcdHRoaXMuYnVpbGRlcj8uYnVzPy5vZmY/LiggRVYuRklFTERfQURELCB0aGlzLl9vbl9maWVsZF9hZGQgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyPy5idXM/Lm9mZj8uKCBFVi5GSUVMRF9SRU1PVkUsIHRoaXMuX29uX2ZpZWxkX3JlbW92ZSApO1xyXG5cdFx0XHR0aGlzLmJ1aWxkZXI/LmJ1cz8ub2ZmPy4oIEVWLlNUUlVDVFVSRV9MT0FERUQsIHRoaXMuX29uX3N0cnVjdHVyZV9sb2FkZWQgKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyPy5idXM/Lm9mZj8uKCBFVi5TVFJVQ1RVUkVfQ0hBTkdFLCB0aGlzLl9vbl9zdHJ1Y3R1cmVfY2hhbmdlICk7XHJcblx0XHRcdHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCAncmVzaXplJywgdGhpcy5fb25fd2luZG93X3Jlc2l6ZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9vbl9maWVsZF9hZGQoZSkge1xyXG5cdFx0XHR0aGlzLl9zY2hlZHVsZV9yZWZyZXNoX2FsbCgpO1xyXG5cdFx0XHQvLyBpZiB5b3UgcmVhbGx5IHdhbnQgdG8gYmUgbWluaW1hbCB3b3JrIGhlcmUsIGtlZXAgeW91ciByb3ctb25seSB2ZXJzaW9uLlxyXG5cdFx0fVxyXG5cclxuXHRcdF9vbl9maWVsZF9yZW1vdmUoZSkge1xyXG5cdFx0XHRjb25zdCBzcmNfZWwgPSBlPy5kZXRhaWw/LmVsIHx8IG51bGw7XHJcblx0XHRcdGNvbnN0IHJvdyAgICA9IChzcmNfZWwgJiYgc3JjX2VsLmNsb3Nlc3QpID8gc3JjX2VsLmNsb3Nlc3QoICcud3BiY19iZmJfX3JvdycgKSA6IG51bGw7XHJcblx0XHRcdGlmICggcm93ICkge1xyXG5cdFx0XHRcdHRoaXMuX3NjaGVkdWxlX3JlZnJlc2hfcm93KCByb3cgKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzLl9zY2hlZHVsZV9yZWZyZXNoX2FsbCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0X29uX3N0cnVjdHVyZV9sb2FkZWQoKSB7XHJcblx0XHRcdHRoaXMuX3NjaGVkdWxlX3JlZnJlc2hfYWxsKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0X29uX3N0cnVjdHVyZV9jaGFuZ2UoZSkge1xyXG5cdFx0XHRjb25zdCByZWFzb24gPSBlPy5kZXRhaWw/LnJlYXNvbiB8fCAnJztcclxuXHRcdFx0Y29uc3Qga2V5ICAgID0gZT8uZGV0YWlsPy5rZXkgfHwgJyc7XHJcblxyXG5cdFx0XHQvLyBJZ25vcmUgbm9pc3kgcHJvcCBjaGFuZ2VzIHRoYXQgZG9uJ3QgYWZmZWN0IG1pbiB3aWR0aHMuXHJcblx0XHRcdGlmICggcmVhc29uID09PSAncHJvcC1jaGFuZ2UnICYmIGtleSAhPT0gJ21pbl93aWR0aCcgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb25zdCBlbCAgPSBlPy5kZXRhaWw/LmVsIHx8IG51bGw7XHJcblx0XHRcdGNvbnN0IHJvdyA9IGVsPy5jbG9zZXN0Py4oICcud3BiY19iZmJfX3JvdycgKSB8fCBudWxsO1xyXG5cdFx0XHRpZiAoIHJvdyApIHtcclxuXHRcdFx0XHR0aGlzLl9zY2hlZHVsZV9yZWZyZXNoX3Jvdyggcm93ICk7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0dGhpcy5fc2NoZWR1bGVfcmVmcmVzaF9hbGwoKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdF9vbl93aW5kb3dfcmVzaXplKCkge1xyXG5cdFx0XHR0aGlzLl9zY2hlZHVsZV9yZWZyZXNoX2FsbCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9zY2hlZHVsZV9yZWZyZXNoX3Jvdyhyb3dfZWwpIHtcclxuXHRcdFx0aWYgKCAhcm93X2VsICkgcmV0dXJuO1xyXG5cdFx0XHR0aGlzLl9wZW5kaW5nX3Jvd3MuYWRkKCByb3dfZWwgKTtcclxuXHRcdFx0dGhpcy5fa2lja19yYWYoKTtcclxuXHRcdH1cclxuXHJcblx0XHRfc2NoZWR1bGVfcmVmcmVzaF9hbGwoKSB7XHJcblx0XHRcdHRoaXMuX3BlbmRpbmdfYWxsID0gdHJ1ZTtcclxuXHRcdFx0dGhpcy5fcGVuZGluZ19yb3dzLmNsZWFyKCk7XHJcblx0XHRcdHRoaXMuX2tpY2tfcmFmKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0X2tpY2tfcmFmKCkge1xyXG5cdFx0XHRpZiAoIHRoaXMuX3JhZl9pZCApIHJldHVybjtcclxuXHRcdFx0dGhpcy5fcmFmX2lkID0gKHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgc2V0VGltZW91dCkoICgpID0+IHtcclxuXHRcdFx0XHR0aGlzLl9yYWZfaWQgPSAwO1xyXG5cdFx0XHRcdGlmICggdGhpcy5fcGVuZGluZ19hbGwgKSB7XHJcblx0XHRcdFx0XHR0aGlzLl9wZW5kaW5nX2FsbCA9IGZhbHNlO1xyXG5cdFx0XHRcdFx0dGhpcy5yZWZyZXNoX2FsbCgpO1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRjb25zdCByb3dzID0gQXJyYXkuZnJvbSggdGhpcy5fcGVuZGluZ19yb3dzICk7XHJcblx0XHRcdFx0dGhpcy5fcGVuZGluZ19yb3dzLmNsZWFyKCk7XHJcblx0XHRcdFx0cm93cy5mb3JFYWNoKCAocikgPT4gdGhpcy5yZWZyZXNoX3JvdyggciApICk7XHJcblx0XHRcdH0sIDAgKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0cmVmcmVzaF9hbGwoKSB7XHJcblx0XHRcdHRoaXMuYnVpbGRlcj8ucGFnZXNfY29udGFpbmVyXHJcblx0XHRcdFx0Py5xdWVyeVNlbGVjdG9yQWxsPy4oICcud3BiY19iZmJfX3JvdycgKVxyXG5cdFx0XHRcdD8uZm9yRWFjaD8uKCAocm93KSA9PiB0aGlzLnJlZnJlc2hfcm93KCByb3cgKSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJlZnJlc2hfcm93KHJvd19lbCkge1xyXG5cdFx0XHRpZiAoICFyb3dfZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjb2xzID0gcm93X2VsLnF1ZXJ5U2VsZWN0b3JBbGwoICc6c2NvcGUgPiAud3BiY19iZmJfX2NvbHVtbicgKTtcclxuXHJcblx0XHRcdC8vIDEpIFJlY2FsY3VsYXRlIGVhY2ggY29sdW1u4oCZcyByZXF1aXJlZCBtaW4gcHggYW5kIHdyaXRlIGl0IHRvIHRoZSBDU1MgdmFyLlxyXG5cdFx0XHRjb2xzLmZvckVhY2goIChjb2wpID0+IHRoaXMuYXBwbHlfY29sX21pbiggY29sICkgKTtcclxuXHJcblx0XHRcdC8vIDIpIEVuZm9yY2UgaXQgYXQgdGhlIENTUyBsZXZlbCByaWdodCBhd2F5IHNvIGxheW91dCBjYW7igJl0IHJlbmRlciBuYXJyb3dlci5cclxuXHRcdFx0Y29scy5mb3JFYWNoKCAoY29sKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcHggICAgICAgICAgID0gcGFyc2VGbG9hdCggZ2V0Q29tcHV0ZWRTdHlsZSggY29sICkuZ2V0UHJvcGVydHlWYWx1ZSggJy0td3BiYy1jb2wtbWluJyApIHx8ICcwJyApIHx8IDA7XHJcblx0XHRcdFx0Y29sLnN0eWxlLm1pbldpZHRoID0gcHggPiAwID8gTWF0aC5yb3VuZCggcHggKSArICdweCcgOiAnJztcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Ly8gMykgTm9ybWFsaXplIGN1cnJlbnQgYmFzZXMgc28gdGhlIHJvdyByZXNwZWN0cyBhbGwgbWlucyB3aXRob3V0IG92ZXJmbG93LlxyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdGNvbnN0IGIgICA9IHRoaXMuYnVpbGRlcjtcclxuXHRcdFx0XHRjb25zdCBncCAgPSBiLmNvbF9nYXBfcGVyY2VudDtcclxuXHRcdFx0XHRjb25zdCBlZmYgPSBiLmxheW91dC5jb21wdXRlX2VmZmVjdGl2ZV9iYXNlc19mcm9tX3Jvdyggcm93X2VsLCBncCApOyAgLy8geyBiYXNlcywgYXZhaWxhYmxlIH1cclxuXHRcdFx0XHQvLyBSZS1maXQgKmN1cnJlbnQqIGJhc2VzIGFnYWluc3QgbWlucyAoc2FtZSBhbGdvcml0aG0gbGF5b3V0IGNoaXBzIHVzZSkuXHJcblx0XHRcdFx0Y29uc3QgZml0dGVkID0gVUkuV1BCQ19CRkJfTGF5b3V0X0NoaXBzLl9maXRfd2VpZ2h0c19yZXNwZWN0aW5nX21pbiggYiwgcm93X2VsLCBlZmYuYmFzZXMgKTtcclxuXHRcdFx0XHRpZiAoIEFycmF5LmlzQXJyYXkoIGZpdHRlZCApICkge1xyXG5cdFx0XHRcdFx0Y29uc3QgY2hhbmdlZCA9IGZpdHRlZC5zb21lKCAodiwgaSkgPT4gTWF0aC5hYnMoIHYgLSBlZmYuYmFzZXNbaV0gKSA+IDAuMDEgKTtcclxuXHRcdFx0XHRcdGlmICggY2hhbmdlZCApIHtcclxuXHRcdFx0XHRcdFx0Yi5sYXlvdXQuYXBwbHlfYmFzZXNfdG9fcm93KCByb3dfZWwsIGZpdHRlZCApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdFx0dy5fd3BiYz8uZGV2Py5lcnJvcj8uKCAnV1BCQ19CRkJfTWluX1dpZHRoX0d1YXJkIC0gcmVmcmVzaF9yb3cnLCBlICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRhcHBseV9jb2xfbWluKGNvbF9lbCkge1xyXG5cdFx0XHRpZiAoICFjb2xfZWwgKSByZXR1cm47XHJcblx0XHRcdGxldCBtYXhfcHggICAgPSAwO1xyXG5cdFx0XHRjb25zdCBjb2xSZWN0ID0gY29sX2VsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG5cdFx0XHRjb2xfZWwucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fZmllbGQnICkuZm9yRWFjaCggKGZpZWxkKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgcmF3ID0gZmllbGQuZ2V0QXR0cmlidXRlKCAnZGF0YS1taW5fd2lkdGgnICk7XHJcblx0XHRcdFx0bGV0IHB4ICAgID0gMDtcclxuXHRcdFx0XHRpZiAoIHJhdyApIHtcclxuXHRcdFx0XHRcdGNvbnN0IHMgPSBTdHJpbmcoIHJhdyApLnRyaW0oKS50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHRcdFx0aWYgKCBzLmVuZHNXaXRoKCAnJScgKSApIHtcclxuXHRcdFx0XHRcdFx0Y29uc3QgbiA9IHBhcnNlRmxvYXQoIHMgKTtcclxuXHRcdFx0XHRcdFx0aWYgKCBOdW1iZXIuaXNGaW5pdGUoIG4gKSAmJiBjb2xSZWN0LndpZHRoID4gMCApIHtcclxuXHRcdFx0XHRcdFx0XHRweCA9IChuIC8gMTAwKSAqIGNvbFJlY3Qud2lkdGg7XHJcblx0XHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdFx0cHggPSAwO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRweCA9IHRoaXMucGFyc2VfbGVuX3B4KCBzICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdGNvbnN0IGNzID0gZ2V0Q29tcHV0ZWRTdHlsZSggZmllbGQgKTtcclxuXHRcdFx0XHRcdHB4ICAgICAgID0gcGFyc2VGbG9hdCggY3MubWluV2lkdGggfHwgJzAnICkgfHwgMDtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0aWYgKCBweCA+IG1heF9weCApIG1heF9weCA9IHB4O1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdGNvbF9lbC5zdHlsZS5zZXRQcm9wZXJ0eSggJy0td3BiYy1jb2wtbWluJywgbWF4X3B4ID4gMCA/IE1hdGgucm91bmQoIG1heF9weCApICsgJ3B4JyA6ICcwcHgnICk7XHJcblx0XHR9XHJcblxyXG5cdFx0cGFyc2VfbGVuX3B4KHZhbHVlKSB7XHJcblx0XHRcdGlmICggdmFsdWUgPT0gbnVsbCApIHJldHVybiAwO1xyXG5cdFx0XHRjb25zdCBzID0gU3RyaW5nKCB2YWx1ZSApLnRyaW0oKS50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHRpZiAoIHMgPT09ICcnICkgcmV0dXJuIDA7XHJcblx0XHRcdGlmICggcy5lbmRzV2l0aCggJ3B4JyApICkge1xyXG5cdFx0XHRcdGNvbnN0IG4gPSBwYXJzZUZsb2F0KCBzICk7XHJcblx0XHRcdFx0cmV0dXJuIE51bWJlci5pc0Zpbml0ZSggbiApID8gbiA6IDA7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBzLmVuZHNXaXRoKCAncmVtJyApIHx8IHMuZW5kc1dpdGgoICdlbScgKSApIHtcclxuXHRcdFx0XHRjb25zdCBuICAgID0gcGFyc2VGbG9hdCggcyApO1xyXG5cdFx0XHRcdGNvbnN0IGJhc2UgPSBwYXJzZUZsb2F0KCBnZXRDb21wdXRlZFN0eWxlKCBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQgKS5mb250U2l6ZSApIHx8IDE2O1xyXG5cdFx0XHRcdHJldHVybiBOdW1iZXIuaXNGaW5pdGUoIG4gKSA/IG4gKiBiYXNlIDogMDtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBuID0gcGFyc2VGbG9hdCggcyApO1xyXG5cdFx0XHRyZXR1cm4gTnVtYmVyLmlzRmluaXRlKCBuICkgPyBuIDogMDtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHQgKiBXUEJDX0JGQl9Ub2dnbGVfTm9ybWFsaXplclxyXG5cdCAqXHJcblx0ICogQ29udmVydHMgcGxhaW4gY2hlY2tib3hlcyBpbnRvIHRvZ2dsZSBVSTpcclxuXHQgKiA8ZGl2IGNsYXNzPVwiaW5zcGVjdG9yX19jb250cm9sIHdwYmNfdWlfX3RvZ2dsZVwiPlxyXG5cdCAqICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIGlkPVwie3VuaXF1ZX1cIiBkYXRhLWluc3BlY3Rvci1rZXk9XCIuLi5cIiBjbGFzcz1cImluc3BlY3Rvcl9faW5wdXRcIiByb2xlPVwic3dpdGNoXCJcclxuXHQgKiBhcmlhLWNoZWNrZWQ9XCJ0cnVlfGZhbHNlXCI+XHJcblx0ICogICA8bGFiZWwgY2xhc3M9XCJ3cGJjX3VpX190b2dnbGVfaWNvblwiICBmb3I9XCJ7dW5pcXVlfVwiPjwvbGFiZWw+XHJcblx0ICogICA8bGFiZWwgY2xhc3M9XCJ3cGJjX3VpX190b2dnbGVfbGFiZWxcIiBmb3I9XCJ7dW5pcXVlfVwiPkxhYmVsPC9sYWJlbD5cclxuXHQgKiA8L2Rpdj5cclxuXHQgKlxyXG5cdCAqIC0gU2tpcHMgaW5wdXRzIGFscmVhZHkgaW5zaWRlIGAud3BiY191aV9fdG9nZ2xlYC5cclxuXHQgKiAtIFJldXNlcyBhbiBleGlzdGluZyA8bGFiZWwgZm9yPVwiLi4uXCI+IHRleHQgaWYgcHJlc2VudDsgb3RoZXJ3aXNlIGZhbGxzIGJhY2sgdG8gbmVhcmJ5IGxhYmVscyBvciBhdHRyaWJ1dGVzLlxyXG5cdCAqIC0gQXV0by1nZW5lcmF0ZXMgYSB1bmlxdWUgaWQgd2hlbiBhYnNlbnQuXHJcblx0ICovXHJcblx0VUkuV1BCQ19CRkJfVG9nZ2xlX05vcm1hbGl6ZXIgPSBjbGFzcyB7XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBVcGdyYWRlIGFsbCByYXcgY2hlY2tib3hlcyBpbiBhIGNvbnRhaW5lciB0byB0b2dnbGVzLlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcm9vdF9lbFxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgdXBncmFkZV9jaGVja2JveGVzX2luKHJvb3RfZWwpIHtcclxuXHJcblx0XHRcdGlmICggIXJvb3RfZWwgfHwgIXJvb3RfZWwucXVlcnlTZWxlY3RvckFsbCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHZhciBpbnB1dHMgPSByb290X2VsLnF1ZXJ5U2VsZWN0b3JBbGwoICdpbnB1dFt0eXBlPVwiY2hlY2tib3hcIl0nICk7XHJcblx0XHRcdGlmICggIWlucHV0cy5sZW5ndGggKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKCBpbnB1dHMsIGZ1bmN0aW9uIChpbnB1dCkge1xyXG5cclxuXHRcdFx0XHQvLyAxKSBTa2lwIGlmIGFscmVhZHkgaW5zaWRlIHRvZ2dsZSB3cmFwcGVyLlxyXG5cdFx0XHRcdGlmICggaW5wdXQuY2xvc2VzdCggJy53cGJjX3VpX190b2dnbGUnICkgKSB7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdC8vIFNraXAgcm93cyAvIHdoZXJlIGlucHV0IGNoZWNrYm94IGV4cGxpY2l0bHkgbWFya2VkIHdpdGggIGF0dHJpYnV0ZSAnZGF0YS13cGJjLXVpLW5vLXRvZ2dsZScuXHJcblx0XHRcdFx0aWYgKCBpbnB1dC5oYXNBdHRyaWJ1dGUoICdkYXRhLXdwYmMtdWktbm8tdG9nZ2xlJyApICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Ly8gMikgRW5zdXJlIHVuaXF1ZSBpZDsgcHJlZmVyIGV4aXN0aW5nLlxyXG5cdFx0XHRcdHZhciBpbnB1dF9pZCA9IGlucHV0LmdldEF0dHJpYnV0ZSggJ2lkJyApO1xyXG5cdFx0XHRcdGlmICggIWlucHV0X2lkICkge1xyXG5cdFx0XHRcdFx0dmFyIGtleSAgPSAoaW5wdXQuZGF0YXNldCAmJiBpbnB1dC5kYXRhc2V0Lmluc3BlY3RvcktleSkgPyBTdHJpbmcoIGlucHV0LmRhdGFzZXQuaW5zcGVjdG9yS2V5ICkgOiAnb3B0JztcclxuXHRcdFx0XHRcdGlucHV0X2lkID0gVUkuV1BCQ19CRkJfVG9nZ2xlX05vcm1hbGl6ZXIuZ2VuZXJhdGVfdW5pcXVlX2lkKCAnd3BiY19pbnNfYXV0b18nICsga2V5ICsgJ18nICk7XHJcblx0XHRcdFx0XHRpbnB1dC5zZXRBdHRyaWJ1dGUoICdpZCcsIGlucHV0X2lkICk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHQvLyAzKSBGaW5kIGJlc3QgbGFiZWwgdGV4dC5cclxuXHRcdFx0XHR2YXIgbGFiZWxfdGV4dCA9IFVJLldQQkNfQkZCX1RvZ2dsZV9Ob3JtYWxpemVyLnJlc29sdmVfbGFiZWxfdGV4dCggcm9vdF9lbCwgaW5wdXQsIGlucHV0X2lkICk7XHJcblxyXG5cdFx0XHRcdC8vIDQpIEJ1aWxkIHRoZSB0b2dnbGUgd3JhcHBlci5cclxuXHRcdFx0XHR2YXIgd3JhcHBlciAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XHJcblx0XHRcdFx0d3JhcHBlci5jbGFzc05hbWUgPSAnaW5zcGVjdG9yX19jb250cm9sIHdwYmNfdWlfX3RvZ2dsZSc7XHJcblxyXG5cdFx0XHRcdC8vIEtlZXAgb3JpZ2luYWwgaW5wdXQ7IGp1c3QgbW92ZSBpdCBpbnRvIHdyYXBwZXIuXHJcblx0XHRcdFx0aW5wdXQuY2xhc3NMaXN0LmFkZCggJ2luc3BlY3Rvcl9faW5wdXQnICk7XHJcblx0XHRcdFx0aW5wdXQuc2V0QXR0cmlidXRlKCAncm9sZScsICdzd2l0Y2gnICk7XHJcblx0XHRcdFx0aW5wdXQuc2V0QXR0cmlidXRlKCAnYXJpYS1jaGVja2VkJywgaW5wdXQuY2hlY2tlZCA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHJcblx0XHRcdFx0dmFyIGljb25fbGFiZWwgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnbGFiZWwnICk7XHJcblx0XHRcdFx0aWNvbl9sYWJlbC5jbGFzc05hbWUgPSAnd3BiY191aV9fdG9nZ2xlX2ljb24nO1xyXG5cdFx0XHRcdGljb25fbGFiZWwuc2V0QXR0cmlidXRlKCAnZm9yJywgaW5wdXRfaWQgKTtcclxuXHJcblx0XHRcdFx0dmFyIHRleHRfbGFiZWwgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnbGFiZWwnICk7XHJcblx0XHRcdFx0dGV4dF9sYWJlbC5jbGFzc05hbWUgPSAnd3BiY191aV9fdG9nZ2xlX2xhYmVsJztcclxuXHRcdFx0XHR0ZXh0X2xhYmVsLnNldEF0dHJpYnV0ZSggJ2ZvcicsIGlucHV0X2lkICk7XHJcblx0XHRcdFx0dGV4dF9sYWJlbC5hcHBlbmRDaGlsZCggZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoIGxhYmVsX3RleHQgKSApO1xyXG5cclxuXHRcdFx0XHQvLyA1KSBJbnNlcnQgd3JhcHBlciBpbnRvIERPTSBuZWFyIHRoZSBpbnB1dC5cclxuXHRcdFx0XHQvLyAgICBQcmVmZXJyZWQ6IHJlcGxhY2UgdGhlIG9yaWdpbmFsIGxhYmVsZWQgcm93IGlmIGl0IG1hdGNoZXMgdHlwaWNhbCBpbnNwZWN0b3IgbGF5b3V0LlxyXG5cdFx0XHRcdHZhciByZXBsYWNlZCA9IFVJLldQQkNfQkZCX1RvZ2dsZV9Ob3JtYWxpemVyLnRyeV9yZXBsYWNlX2tub3duX3JvdyggaW5wdXQsIHdyYXBwZXIsIGxhYmVsX3RleHQgKTtcclxuXHJcblx0XHRcdFx0aWYgKCAhcmVwbGFjZWQgKSB7XHJcblx0XHRcdFx0XHRpZiAoICFpbnB1dC5wYXJlbnROb2RlICkgcmV0dXJuOyAvLyBORVcgZ3VhcmRcclxuXHRcdFx0XHRcdC8vIEZhbGxiYWNrOiBqdXN0IHdyYXAgdGhlIGlucHV0IGluIHBsYWNlIGFuZCBhcHBlbmQgbGFiZWxzLlxyXG5cdFx0XHRcdFx0aW5wdXQucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoIHdyYXBwZXIsIGlucHV0ICk7XHJcblx0XHRcdFx0XHR3cmFwcGVyLmFwcGVuZENoaWxkKCBpbnB1dCApO1xyXG5cdFx0XHRcdFx0d3JhcHBlci5hcHBlbmRDaGlsZCggaWNvbl9sYWJlbCApO1xyXG5cdFx0XHRcdFx0d3JhcHBlci5hcHBlbmRDaGlsZCggdGV4dF9sYWJlbCApO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Ly8gNikgQVJJQSBzeW5jIG9uIGNoYW5nZS5cclxuXHRcdFx0XHRpbnB1dC5hZGRFdmVudExpc3RlbmVyKCAnY2hhbmdlJywgZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdFx0aW5wdXQuc2V0QXR0cmlidXRlKCAnYXJpYS1jaGVja2VkJywgaW5wdXQuY2hlY2tlZCA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdH0gKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEdlbmVyYXRlIGEgdW5pcXVlIGlkIHdpdGggYSBnaXZlbiBwcmVmaXguXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4XHJcblx0XHQgKiBAcmV0dXJucyB7c3RyaW5nfVxyXG5cdFx0ICovXHJcblx0XHRzdGF0aWMgZ2VuZXJhdGVfdW5pcXVlX2lkKHByZWZpeCkge1xyXG5cdFx0XHR2YXIgYmFzZSA9IFN0cmluZyggcHJlZml4IHx8ICd3cGJjX2luc19hdXRvXycgKTtcclxuXHRcdFx0dmFyIHVpZCAgPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKCAzNiApLnNsaWNlKCAyLCA4ICk7XHJcblx0XHRcdHZhciBpZCAgID0gYmFzZSArIHVpZDtcclxuXHRcdFx0Ly8gTWluaW1hbCBjb2xsaXNpb24gZ3VhcmQgaW4gdGhlIGN1cnJlbnQgZG9jdW1lbnQgc2NvcGUuXHJcblx0XHRcdHdoaWxlICggZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoIGlkICkgKSB7XHJcblx0XHRcdFx0dWlkID0gTWF0aC5yYW5kb20oKS50b1N0cmluZyggMzYgKS5zbGljZSggMiwgOCApO1xyXG5cdFx0XHRcdGlkICA9IGJhc2UgKyB1aWQ7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIGlkO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogUmVzb2x2ZSB0aGUgYmVzdCBodW1hbiBsYWJlbCBmb3IgYW4gaW5wdXQuXHJcblx0XHQgKiBQcmlvcml0eTpcclxuXHRcdCAqICAxKSA8bGFiZWwgZm9yPVwie2lkfVwiPnRleHQ8L2xhYmVsPlxyXG5cdFx0ICogIDIpIG5lYXJlc3Qgc2libGluZy9wYXJlbnQgLmluc3BlY3Rvcl9fbGFiZWwgdGV4dFxyXG5cdFx0ICogIDMpIGlucHV0LmdldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcpIHx8IGRhdGEtbGFiZWwgfHwgZGF0YS1pbnNwZWN0b3Ita2V5IHx8IG5hbWUgfHwgJ09wdGlvbidcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvb3RfZWxcclxuXHRcdCAqIEBwYXJhbSB7SFRNTElucHV0RWxlbWVudH0gaW5wdXRcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dF9pZFxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ31cclxuXHRcdCAqL1xyXG5cdFx0c3RhdGljIHJlc29sdmVfbGFiZWxfdGV4dChyb290X2VsLCBpbnB1dCwgaW5wdXRfaWQpIHtcclxuXHRcdFx0Ly8gZm9yPSBhc3NvY2lhdGlvblxyXG5cdFx0XHRpZiAoIGlucHV0X2lkICkge1xyXG5cdFx0XHRcdHZhciBhc3NvYyA9IHJvb3RfZWwucXVlcnlTZWxlY3RvciggJ2xhYmVsW2Zvcj1cIicgKyBVSS5XUEJDX0JGQl9Ub2dnbGVfTm9ybWFsaXplci5jc3NfZXNjYXBlKCBpbnB1dF9pZCApICsgJ1wiXScgKTtcclxuXHRcdFx0XHRpZiAoIGFzc29jICYmIGFzc29jLnRleHRDb250ZW50ICkge1xyXG5cdFx0XHRcdFx0dmFyIHR4dCA9IGFzc29jLnRleHRDb250ZW50LnRyaW0oKTtcclxuXHRcdFx0XHRcdC8vIFJlbW92ZSB0aGUgb2xkIGxhYmVsIGZyb20gRE9NOyBpdHMgdGV4dCB3aWxsIGJlIHVzZWQgYnkgdG9nZ2xlLlxyXG5cdFx0XHRcdFx0YXNzb2MucGFyZW50Tm9kZSAmJiBhc3NvYy5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKCBhc3NvYyApO1xyXG5cdFx0XHRcdFx0aWYgKCB0eHQgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybiB0eHQ7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBuZWFyYnkgaW5zcGVjdG9yIGxhYmVsXHJcblx0XHRcdHZhciBuZWFyX2xhYmVsID0gaW5wdXQuY2xvc2VzdCggJy5pbnNwZWN0b3JfX3JvdycgKTtcclxuXHRcdFx0aWYgKCBuZWFyX2xhYmVsICkge1xyXG5cdFx0XHRcdHZhciBpbCA9IG5lYXJfbGFiZWwucXVlcnlTZWxlY3RvciggJy5pbnNwZWN0b3JfX2xhYmVsJyApO1xyXG5cdFx0XHRcdGlmICggaWwgJiYgaWwudGV4dENvbnRlbnQgKSB7XHJcblx0XHRcdFx0XHR2YXIgdDIgPSBpbC50ZXh0Q29udGVudC50cmltKCk7XHJcblx0XHRcdFx0XHQvLyBJZiB0aGlzIHJvdyBoYWQgdGhlIHN0YW5kYXJkIGxhYmVsK2NvbnRyb2wsIGRyb3AgdGhlIG9sZCB0ZXh0IGxhYmVsIHRvIGF2b2lkIGR1cGxpY2F0ZXMuXHJcblx0XHRcdFx0XHRpbC5wYXJlbnROb2RlICYmIGlsLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIGlsICk7XHJcblx0XHRcdFx0XHRpZiAoIHQyICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gdDI7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBmYWxsYmFja3NcclxuXHRcdFx0dmFyIGFyaWEgPSBpbnB1dC5nZXRBdHRyaWJ1dGUoICdhcmlhLWxhYmVsJyApO1xyXG5cdFx0XHRpZiAoIGFyaWEgKSB7XHJcblx0XHRcdFx0cmV0dXJuIGFyaWE7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBpbnB1dC5kYXRhc2V0ICYmIGlucHV0LmRhdGFzZXQubGFiZWwgKSB7XHJcblx0XHRcdFx0cmV0dXJuIFN0cmluZyggaW5wdXQuZGF0YXNldC5sYWJlbCApO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggaW5wdXQuZGF0YXNldCAmJiBpbnB1dC5kYXRhc2V0Lmluc3BlY3RvcktleSApIHtcclxuXHRcdFx0XHRyZXR1cm4gU3RyaW5nKCBpbnB1dC5kYXRhc2V0Lmluc3BlY3RvcktleSApO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggaW5wdXQubmFtZSApIHtcclxuXHRcdFx0XHRyZXR1cm4gU3RyaW5nKCBpbnB1dC5uYW1lICk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuICdPcHRpb24nO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVHJ5IHRvIHJlcGxhY2UgYSBrbm93biBpbnNwZWN0b3Igcm93IHBhdHRlcm4gd2l0aCBhIHRvZ2dsZSB3cmFwcGVyLlxyXG5cdFx0ICogUGF0dGVybnM6XHJcblx0XHQgKiAgPGRpdi5pbnNwZWN0b3JfX3Jvdz5cclxuXHRcdCAqICAgIDxsYWJlbC5pbnNwZWN0b3JfX2xhYmVsPlRleHQ8L2xhYmVsPlxyXG5cdFx0ICogICAgPGRpdi5pbnNwZWN0b3JfX2NvbnRyb2w+IFtpbnB1dFt0eXBlPWNoZWNrYm94XV0gPC9kaXY+XHJcblx0XHQgKiAgPC9kaXY+XHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MSW5wdXRFbGVtZW50fSBpbnB1dFxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gd3JhcHBlclxyXG5cdFx0ICogQHJldHVybnMge2Jvb2xlYW59IHJlcGxhY2VkXHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyB0cnlfcmVwbGFjZV9rbm93bl9yb3coaW5wdXQsIHdyYXBwZXIsIGxhYmVsX3RleHQpIHtcclxuXHRcdFx0dmFyIHJvdyAgICAgICA9IGlucHV0LmNsb3Nlc3QoICcuaW5zcGVjdG9yX19yb3cnICk7XHJcblx0XHRcdHZhciBjdHJsX3dyYXAgPSBpbnB1dC5wYXJlbnRFbGVtZW50O1xyXG5cclxuXHRcdFx0aWYgKCByb3cgJiYgY3RybF93cmFwICYmIGN0cmxfd3JhcC5jbGFzc0xpc3QuY29udGFpbnMoICdpbnNwZWN0b3JfX2NvbnRyb2wnICkgKSB7XHJcblx0XHRcdFx0Ly8gQ2xlYXIgY29udHJvbCB3cmFwIGFuZCByZWluc2VydCB0b2dnbGUgc3RydWN0dXJlLlxyXG5cdFx0XHRcdHdoaWxlICggY3RybF93cmFwLmZpcnN0Q2hpbGQgKSB7XHJcblx0XHRcdFx0XHRjdHJsX3dyYXAucmVtb3ZlQ2hpbGQoIGN0cmxfd3JhcC5maXJzdENoaWxkICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHJvdy5jbGFzc0xpc3QuYWRkKCAnaW5zcGVjdG9yX19yb3ctLXRvZ2dsZScgKTtcclxuXHJcblx0XHRcdFx0Y3RybF93cmFwLmNsYXNzTGlzdC5hZGQoICd3cGJjX3VpX190b2dnbGUnICk7XHJcblx0XHRcdFx0Y3RybF93cmFwLmFwcGVuZENoaWxkKCBpbnB1dCApO1xyXG5cclxuXHRcdFx0XHR2YXIgaW5wdXRfaWQgICAgICAgPSBpbnB1dC5nZXRBdHRyaWJ1dGUoICdpZCcgKTtcclxuXHRcdFx0XHR2YXIgaWNvbl9sYmwgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnbGFiZWwnICk7XHJcblx0XHRcdFx0aWNvbl9sYmwuY2xhc3NOYW1lID0gJ3dwYmNfdWlfX3RvZ2dsZV9pY29uJztcclxuXHRcdFx0XHRpY29uX2xibC5zZXRBdHRyaWJ1dGUoICdmb3InLCBpbnB1dF9pZCApO1xyXG5cclxuXHRcdFx0XHR2YXIgdGV4dF9sYmwgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnbGFiZWwnICk7XHJcblx0XHRcdFx0dGV4dF9sYmwuY2xhc3NOYW1lID0gJ3dwYmNfdWlfX3RvZ2dsZV9sYWJlbCc7XHJcblx0XHRcdFx0dGV4dF9sYmwuc2V0QXR0cmlidXRlKCAnZm9yJywgaW5wdXRfaWQgKTtcclxuXHRcdFx0XHRpZiAoIGxhYmVsX3RleHQgKSB7XHJcblx0XHRcdFx0XHR0ZXh0X2xibC5hcHBlbmRDaGlsZCggZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoIGxhYmVsX3RleHQgKSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHQvLyBJZiB0aGUgcm93IHByZXZpb3VzbHkgaGFkIGEgLmluc3BlY3Rvcl9fbGFiZWwgKHdlIHJlbW92ZWQgaXQgaW4gcmVzb2x2ZV9sYWJlbF90ZXh0KSxcclxuXHRcdFx0XHQvLyB3ZSBpbnRlbnRpb25hbGx5IGRvIE5PVCByZWNyZWF0ZSBpdDsgdGhlIHRvZ2dsZSB0ZXh0IGxhYmVsIGJlY29tZXMgdGhlIHZpc2libGUgb25lLlxyXG5cdFx0XHRcdC8vIFRoZSB0ZXh0IGNvbnRlbnQgaXMgYWxyZWFkeSByZXNvbHZlZCBpbiByZXNvbHZlX2xhYmVsX3RleHQoKSBhbmQgc2V0IGJlbG93IGJ5IGNhbGxlci5cclxuXHJcblx0XHRcdFx0Y3RybF93cmFwLmFwcGVuZENoaWxkKCBpY29uX2xibCApO1xyXG5cdFx0XHRcdGN0cmxfd3JhcC5hcHBlbmRDaGlsZCggdGV4dF9sYmwgKTtcclxuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTm90IGEga25vd24gcGF0dGVybjsgY2FsbGVyIHdpbGwgd3JhcCBpbiBwbGFjZS5cclxuXHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ1NTLmVzY2FwZSBwb2x5ZmlsbCBmb3Igc2VsZWN0b3JzLlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHNcclxuXHRcdCAqIEByZXR1cm5zIHtzdHJpbmd9XHJcblx0XHQgKi9cclxuXHRcdHN0YXRpYyBjc3NfZXNjYXBlKHMpIHtcclxuXHRcdFx0cyA9IFN0cmluZyggcyApO1xyXG5cdFx0XHRpZiAoIHdpbmRvdy5DU1MgJiYgdHlwZW9mIHdpbmRvdy5DU1MuZXNjYXBlID09PSAnZnVuY3Rpb24nICkge1xyXG5cdFx0XHRcdHJldHVybiB3aW5kb3cuQ1NTLmVzY2FwZSggcyApO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBzLnJlcGxhY2UoIC8oW15cXHctXSkvZywgJ1xcXFwkMScgKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHQvKipcclxuXHQgKiBBcHBseSBhbGwgVUkgbm9ybWFsaXplcnMvZW5oYW5jZXJzIHRvIGEgY29udGFpbmVyIChwb3N0LXJlbmRlcikuXHJcblx0ICogS2VlcCB0aGlzIGZpbGUgc21hbGwgYW5kIGFkZCBtb3JlIG5vcm1hbGl6ZXJzIGxhdGVyIGluIG9uZSBwbGFjZS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvb3RcclxuXHQgKi9cclxuXHRVSS5hcHBseV9wb3N0X3JlbmRlciA9IGZ1bmN0aW9uIChyb290KSB7XHJcblx0XHRpZiAoICFyb290ICkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHR0cnkge1xyXG5cdFx0XHRVSS5XUEJDX0JGQl9WYWx1ZVNsaWRlcj8uaW5pdF9vbj8uKCByb290ICk7XHJcblx0XHR9IGNhdGNoICggZSApIHsgLyogbm9vcCAqL1xyXG5cdFx0fVxyXG5cdFx0dHJ5IHtcclxuXHRcdFx0dmFyIFQgPSBVSS5XUEJDX0JGQl9Ub2dnbGVfTm9ybWFsaXplcjtcclxuXHRcdFx0aWYgKCBUICYmIHR5cGVvZiBULnVwZ3JhZGVfY2hlY2tib3hlc19pbiA9PT0gJ2Z1bmN0aW9uJyApIHtcclxuXHRcdFx0XHRULnVwZ3JhZGVfY2hlY2tib3hlc19pbiggcm9vdCApO1xyXG5cdFx0XHR9XHJcblx0XHR9IGNhdGNoICggZSApIHtcclxuXHRcdFx0dy5fd3BiYz8uZGV2Py5lcnJvcj8uKCAnYXBwbHlfcG9zdF9yZW5kZXIudG9nZ2xlJywgZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEFjY2Vzc2liaWxpdHk6IGtlZXAgYXJpYS1jaGVja2VkIGluIHN5bmMgZm9yIGFsbCB0b2dnbGVzIGluc2lkZSByb290LlxyXG5cdFx0dHJ5IHtcclxuXHRcdFx0cm9vdC5xdWVyeVNlbGVjdG9yQWxsKCAnLndwYmNfdWlfX3RvZ2dsZSBpbnB1dFt0eXBlPVwiY2hlY2tib3hcIl0nICkuZm9yRWFjaCggZnVuY3Rpb24gKGNiKSB7XHJcblx0XHRcdFx0aWYgKCBjYi5fX3dwYmNfYXJpYV9ob29rZWQgKSB7XHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGNiLl9fd3BiY19hcmlhX2hvb2tlZCA9IHRydWU7XHJcblx0XHRcdFx0Y2Iuc2V0QXR0cmlidXRlKCAnYXJpYS1jaGVja2VkJywgY2IuY2hlY2tlZCA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHRcdFx0XHQvLyBEZWxlZ2F0ZSDigJhjaGFuZ2XigJkganVzdCBvbmNlIHBlciByZW5kZXIg4oCTIG5hdGl2ZSBkZWxlZ2F0aW9uIHN0aWxsIHdvcmtzIGZpbmUgZm9yIHlvdXIgbG9naWMuXHJcblx0XHRcdFx0Y2IuYWRkRXZlbnRMaXN0ZW5lciggJ2NoYW5nZScsICgpID0+IHtcclxuXHRcdFx0XHRcdGNiLnNldEF0dHJpYnV0ZSggJ2FyaWEtY2hlY2tlZCcsIGNiLmNoZWNrZWQgPyAndHJ1ZScgOiAnZmFsc2UnICk7XHJcblx0XHRcdFx0fSwgeyBwYXNzaXZlOiB0cnVlIH0gKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdHcuX3dwYmM/LmRldj8uZXJyb3I/LiggJ2FwcGx5X3Bvc3RfcmVuZGVyLmFyaWEnLCBlICk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0VUkuSW5zcGVjdG9yRW5oYW5jZXJzID0gVUkuSW5zcGVjdG9yRW5oYW5jZXJzIHx8IChmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgcmVncyA9IFtdO1xyXG5cclxuXHRcdGZ1bmN0aW9uIHJlZ2lzdGVyKG5hbWUsIHNlbGVjdG9yLCBpbml0LCBkZXN0cm95KSB7XHJcblx0XHRcdHJlZ3MucHVzaCggeyBuYW1lLCBzZWxlY3RvciwgaW5pdCwgZGVzdHJveSB9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZnVuY3Rpb24gc2Nhbihyb290KSB7XHJcblx0XHRcdGlmICggIXJvb3QgKSByZXR1cm47XHJcblx0XHRcdHJlZ3MuZm9yRWFjaCggZnVuY3Rpb24gKHIpIHtcclxuXHRcdFx0XHRyb290LnF1ZXJ5U2VsZWN0b3JBbGwoIHIuc2VsZWN0b3IgKS5mb3JFYWNoKCBmdW5jdGlvbiAobm9kZSkge1xyXG5cdFx0XHRcdFx0bm9kZS5fX3dwYmNfZWggPSBub2RlLl9fd3BiY19laCB8fCB7fTtcclxuXHRcdFx0XHRcdGlmICggbm9kZS5fX3dwYmNfZWhbci5uYW1lXSApIHJldHVybjtcclxuXHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdHIuaW5pdCAmJiByLmluaXQoIG5vZGUsIHJvb3QgKTtcclxuXHRcdFx0XHRcdFx0bm9kZS5fX3dwYmNfZWhbci5uYW1lXSA9IHRydWU7XHJcblx0XHRcdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZnVuY3Rpb24gZGVzdHJveShyb290KSB7XHJcblx0XHRcdGlmICggIXJvb3QgKSByZXR1cm47XHJcblx0XHRcdHJlZ3MuZm9yRWFjaCggZnVuY3Rpb24gKHIpIHtcclxuXHRcdFx0XHRyb290LnF1ZXJ5U2VsZWN0b3JBbGwoIHIuc2VsZWN0b3IgKS5mb3JFYWNoKCBmdW5jdGlvbiAobm9kZSkge1xyXG5cdFx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdFx0ci5kZXN0cm95ICYmIHIuZGVzdHJveSggbm9kZSwgcm9vdCApO1xyXG5cdFx0XHRcdFx0fSBjYXRjaCAoIF9lICkge1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCBub2RlLl9fd3BiY19laCApIGRlbGV0ZSBub2RlLl9fd3BiY19laFtyLm5hbWVdO1xyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB7IHJlZ2lzdGVyLCBzY2FuLCBkZXN0cm95IH07XHJcblx0fSkoKTtcclxuXHJcblx0VUkuV1BCQ19CRkJfVmFsdWVTbGlkZXIgPSB7XHJcblx0XHRpbml0X29uKHJvb3QpIHtcclxuXHRcdFx0dmFyIGdyb3VwcyA9IChyb290Lm5vZGVUeXBlID09PSAxID8gWyByb290IF0gOiBbXSkuY29uY2F0KCBbXS5zbGljZS5jYWxsKCByb290LnF1ZXJ5U2VsZWN0b3JBbGw/LiggJ1tkYXRhLWxlbi1ncm91cF0nICkgfHwgW10gKSApO1xyXG5cdFx0XHRncm91cHMuZm9yRWFjaCggZnVuY3Rpb24gKGcpIHtcclxuXHRcdFx0XHRpZiAoICFnLm1hdGNoZXMgfHwgIWcubWF0Y2hlcyggJ1tkYXRhLWxlbi1ncm91cF0nICkgKSByZXR1cm47XHJcblx0XHRcdFx0aWYgKCBnLl9fd3BiY19sZW5fd2lyZWQgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdHZhciBudW1iZXIgPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tdmFsdWVdJyApO1xyXG5cdFx0XHRcdHZhciByYW5nZSAgPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tcmFuZ2VdJyApO1xyXG5cdFx0XHRcdHZhciB1bml0ICAgPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tdW5pdF0nICk7XHJcblxyXG5cdFx0XHRcdGlmICggIW51bWJlciB8fCAhcmFuZ2UgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdC8vIE1pcnJvciBjb25zdHJhaW50cyBpZiBtaXNzaW5nIG9uIHRoZSByYW5nZS5cclxuXHRcdFx0XHRbICdtaW4nLCAnbWF4JywgJ3N0ZXAnIF0uZm9yRWFjaCggZnVuY3Rpb24gKGEpIHtcclxuXHRcdFx0XHRcdGlmICggIXJhbmdlLmhhc0F0dHJpYnV0ZSggYSApICYmIG51bWJlci5oYXNBdHRyaWJ1dGUoIGEgKSApIHtcclxuXHRcdFx0XHRcdFx0cmFuZ2Uuc2V0QXR0cmlidXRlKCBhLCBudW1iZXIuZ2V0QXR0cmlidXRlKCBhICkgKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9ICk7XHJcblxyXG5cclxuXHRcdFx0XHRmdW5jdGlvbiBzeW5jX3JhbmdlX2Zyb21fbnVtYmVyKCkge1xyXG5cdFx0XHRcdFx0aWYgKCByYW5nZS52YWx1ZSAhPT0gbnVtYmVyLnZhbHVlICkge1xyXG5cdFx0XHRcdFx0XHRyYW5nZS52YWx1ZSA9IG51bWJlci52YWx1ZTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGZ1bmN0aW9uIGRpc3BhdGNoX2lucHV0KGVsKSB7XHJcblx0XHRcdFx0XHR0cnkgeyBlbC5kaXNwYXRjaEV2ZW50KCBuZXcgRXZlbnQoICdpbnB1dCcsIHsgYnViYmxlczogdHJ1ZSB9ICkgKTsgfSBjYXRjaCAoIF9lICkge31cclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0ZnVuY3Rpb24gZGlzcGF0Y2hfY2hhbmdlKGVsKSB7XHJcblx0XHRcdFx0XHR0cnkgeyBlbC5kaXNwYXRjaEV2ZW50KCBuZXcgRXZlbnQoICdjaGFuZ2UnLCB7IGJ1YmJsZXM6IHRydWUgfSApICk7IH0gY2F0Y2ggKCBfZSApIHt9XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHQvLyBUaHJvdHRsZSByYW5nZS0+bnVtYmVyIHN5bmNpbmcgKHRpbWUtYmFzZWQpLlxyXG5cdFx0XHRcdHZhciB0aW1lcl9pZCAgICAgICA9IDA7XHJcblx0XHRcdFx0dmFyIHBlbmRpbmdfdmFsICAgID0gbnVsbDtcclxuXHRcdFx0XHR2YXIgcGVuZGluZ19jaGFuZ2UgPSBmYWxzZTtcclxuXHRcdFx0XHR2YXIgbGFzdF9mbHVzaF90cyAgPSAwO1xyXG5cclxuXHRcdFx0XHQvLyBDaGFuZ2UgdGhpcyB0byB0dW5lIHNwZWVkOiA1MC4uMTIwIG1zIGlzIGEgZ29vZCByYW5nZS5cclxuXHRcdFx0XHR2YXIgbWluX2ludGVydmFsX21zID0gcGFyc2VJbnQoIGcuZGF0YXNldC5sZW5UaHJvdHRsZSB8fCBVSS5WQUxVRV9TTElERVJfVEhST1RUTEVfTVMsIDEwICk7XHJcblx0XHRcdFx0bWluX2ludGVydmFsX21zID0gTnVtYmVyLmlzRmluaXRlKCBtaW5faW50ZXJ2YWxfbXMgKSA/IE1hdGgubWF4KCAwLCBtaW5faW50ZXJ2YWxfbXMgKSA6IDEyMDtcclxuXHJcblx0XHRcdFx0ZnVuY3Rpb24gZmx1c2hfcmFuZ2VfdG9fbnVtYmVyKCkge1xyXG5cdFx0XHRcdFx0dGltZXJfaWQgPSAwO1xyXG5cclxuXHRcdFx0XHRcdGlmICggcGVuZGluZ192YWwgPT0gbnVsbCApIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdHZhciBuZXh0ICAgID0gU3RyaW5nKCBwZW5kaW5nX3ZhbCApO1xyXG5cdFx0XHRcdFx0cGVuZGluZ192YWwgPSBudWxsO1xyXG5cclxuXHRcdFx0XHRcdGlmICggbnVtYmVyLnZhbHVlICE9PSBuZXh0ICkge1xyXG5cdFx0XHRcdFx0XHRudW1iZXIudmFsdWUgPSBuZXh0O1xyXG5cdFx0XHRcdFx0XHQvLyBJTVBPUlRBTlQ6IG9ubHkgJ2lucHV0JyB3aGlsZSBkcmFnZ2luZy5cclxuXHRcdFx0XHRcdFx0ZGlzcGF0Y2hfaW5wdXQoIG51bWJlciApO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdGlmICggcGVuZGluZ19jaGFuZ2UgKSB7XHJcblx0XHRcdFx0XHRcdHBlbmRpbmdfY2hhbmdlID0gZmFsc2U7XHJcblx0XHRcdFx0XHRcdGRpc3BhdGNoX2NoYW5nZSggbnVtYmVyICk7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0bGFzdF9mbHVzaF90cyA9IERhdGUubm93KCk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRmdW5jdGlvbiBzY2hlZHVsZV9yYW5nZV90b19udW1iZXIodmFsLCBlbWl0X2NoYW5nZSkge1xyXG5cdFx0XHRcdFx0cGVuZGluZ192YWwgPSB2YWw7XHJcblx0XHRcdFx0XHRpZiAoIGVtaXRfY2hhbmdlICkge1xyXG5cdFx0XHRcdFx0XHRwZW5kaW5nX2NoYW5nZSA9IHRydWU7XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0Ly8gSWYgY29tbWl0IHJlcXVlc3RlZCwgZmx1c2ggaW1tZWRpYXRlbHkuXHJcblx0XHRcdFx0XHRpZiAoIHBlbmRpbmdfY2hhbmdlICkge1xyXG5cdFx0XHRcdFx0XHRpZiAoIHRpbWVyX2lkICkge1xyXG5cdFx0XHRcdFx0XHRcdGNsZWFyVGltZW91dCggdGltZXJfaWQgKTtcclxuXHRcdFx0XHRcdFx0XHR0aW1lcl9pZCA9IDA7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0Zmx1c2hfcmFuZ2VfdG9fbnVtYmVyKCk7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0XHR2YXIgbm93ICAgPSBEYXRlLm5vdygpO1xyXG5cdFx0XHRcdFx0dmFyIGRlbHRhID0gbm93IC0gbGFzdF9mbHVzaF90cztcclxuXHJcblx0XHRcdFx0XHQvLyBJZiBlbm91Z2ggdGltZSBwYXNzZWQsIGZsdXNoIGltbWVkaWF0ZWx5OyBlbHNlIHNjaGVkdWxlLlxyXG5cdFx0XHRcdFx0aWYgKCBkZWx0YSA+PSBtaW5faW50ZXJ2YWxfbXMgKSB7XHJcblx0XHRcdFx0XHRcdGZsdXNoX3JhbmdlX3RvX251bWJlcigpO1xyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdFx0aWYgKCB0aW1lcl9pZCApIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdHRpbWVyX2lkID0gc2V0VGltZW91dCggZmx1c2hfcmFuZ2VfdG9fbnVtYmVyLCBNYXRoLm1heCggMCwgbWluX2ludGVydmFsX21zIC0gZGVsdGEgKSApO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0ZnVuY3Rpb24gb25fbnVtYmVyX2lucHV0KCkge1xyXG5cdFx0XHRcdFx0c3luY19yYW5nZV9mcm9tX251bWJlcigpO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0ZnVuY3Rpb24gb25fbnVtYmVyX2NoYW5nZSgpIHtcclxuXHRcdFx0XHRcdHN5bmNfcmFuZ2VfZnJvbV9udW1iZXIoKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGZ1bmN0aW9uIG9uX3JhbmdlX2lucHV0KCkge1xyXG5cdFx0XHRcdFx0c2NoZWR1bGVfcmFuZ2VfdG9fbnVtYmVyKCByYW5nZS52YWx1ZSwgZmFsc2UgKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGZ1bmN0aW9uIG9uX3JhbmdlX2NoYW5nZSgpIHtcclxuXHRcdFx0XHRcdHNjaGVkdWxlX3JhbmdlX3RvX251bWJlciggcmFuZ2UudmFsdWUsIHRydWUgKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdG51bWJlci5hZGRFdmVudExpc3RlbmVyKCAnaW5wdXQnLCAgb25fbnVtYmVyX2lucHV0ICk7XHJcblx0XHRcdFx0bnVtYmVyLmFkZEV2ZW50TGlzdGVuZXIoICdjaGFuZ2UnLCBvbl9udW1iZXJfY2hhbmdlICk7XHJcblx0XHRcdFx0cmFuZ2UuYWRkRXZlbnRMaXN0ZW5lciggJ2lucHV0JywgIG9uX3JhbmdlX2lucHV0ICk7XHJcblx0XHRcdFx0cmFuZ2UuYWRkRXZlbnRMaXN0ZW5lciggJ2NoYW5nZScsIG9uX3JhbmdlX2NoYW5nZSApO1xyXG5cclxuXHRcdFx0XHRpZiAoIHVuaXQgKSB7XHJcblx0XHRcdFx0XHR1bml0LmFkZEV2ZW50TGlzdGVuZXIoICdjaGFuZ2UnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdFx0XHRcdC8vIFdlIGp1c3QgbnVkZ2UgdGhlIG51bWJlciBzbyB1cHN0cmVhbSBoYW5kbGVycyByZS1ydW4uXHJcblx0XHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdFx0bnVtYmVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0XHR9IGNhdGNoICggX2UgKSB7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH0gKTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdC8vIEluaXRpYWwgc3luY1xyXG5cdFx0XHRcdHN5bmNfcmFuZ2VfZnJvbV9udW1iZXIoKTtcclxuXHJcblx0XHRcdFx0Zy5fX3dwYmNfbGVuX3dpcmVkID0ge1xyXG5cdFx0XHRcdFx0ZGVzdHJveSgpIHtcclxuXHRcdFx0XHRcdFx0bnVtYmVyLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdpbnB1dCcsICBvbl9udW1iZXJfaW5wdXQgKTtcclxuXHRcdFx0XHRcdFx0bnVtYmVyLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdjaGFuZ2UnLCBvbl9udW1iZXJfY2hhbmdlICk7XHJcblx0XHRcdFx0XHRcdHJhbmdlLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdpbnB1dCcsICBvbl9yYW5nZV9pbnB1dCApO1xyXG5cdFx0XHRcdFx0XHRyYW5nZS5yZW1vdmVFdmVudExpc3RlbmVyKCAnY2hhbmdlJywgb25fcmFuZ2VfY2hhbmdlICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fTtcclxuXHRcdFx0fSApO1xyXG5cdFx0fSxcclxuXHRcdGRlc3Ryb3lfb24ocm9vdCkge1xyXG5cdFx0XHR2YXIgZ3JvdXBzID0gKHJvb3QgJiYgcm9vdC5ub2RlVHlwZSA9PT0gMSA/IFsgcm9vdCBdIDogW10pLmNvbmNhdChcclxuXHRcdFx0XHRbXS5zbGljZS5jYWxsKCByb290LnF1ZXJ5U2VsZWN0b3JBbGw/LiggJ1tkYXRhLWxlbi1ncm91cF0nICkgfHwgW10gKVxyXG5cdFx0XHQpO1xyXG5cdFx0XHRncm91cHMuZm9yRWFjaCggZnVuY3Rpb24gKGcpIHtcclxuXHRcdFx0XHRpZiAoICFnLm1hdGNoZXMgfHwgIWcubWF0Y2hlcyggJ1tkYXRhLWxlbi1ncm91cF0nICkgKSByZXR1cm47XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdGcuX193cGJjX2xlbl93aXJlZCAmJiBnLl9fd3BiY19sZW5fd2lyZWQuZGVzdHJveSAmJiBnLl9fd3BiY19sZW5fd2lyZWQuZGVzdHJveSgpO1xyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfZSApIHtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0ZGVsZXRlIGcuX193cGJjX2xlbl93aXJlZDtcclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8vIFJlZ2lzdGVyIHdpdGggdGhlIGdsb2JhbCBlbmhhbmNlcnMgaHViLlxyXG5cdFVJLkluc3BlY3RvckVuaGFuY2VycyAmJiBVSS5JbnNwZWN0b3JFbmhhbmNlcnMucmVnaXN0ZXIoXHJcblx0XHQndmFsdWUtc2xpZGVyJyxcclxuXHRcdCdbZGF0YS1sZW4tZ3JvdXBdJyxcclxuXHRcdGZ1bmN0aW9uIChlbCwgX3Jvb3QpIHtcclxuXHRcdFx0VUkuV1BCQ19CRkJfVmFsdWVTbGlkZXIuaW5pdF9vbiggZWwgKTtcclxuXHRcdH0sXHJcblx0XHRmdW5jdGlvbiAoZWwsIF9yb290KSB7XHJcblx0XHRcdFVJLldQQkNfQkZCX1ZhbHVlU2xpZGVyLmRlc3Ryb3lfb24oIGVsICk7XHJcblx0XHR9XHJcblx0KTtcclxuXHJcblx0Ly8gU2luZ2xlLCBsb2FkLW9yZGVyLXNhZmUgcGF0Y2ggc28gZW5oYW5jZXJzIGF1dG8tcnVuIG9uIGV2ZXJ5IGJpbmQuXHJcblx0KGZ1bmN0aW9uIHBhdGNoSW5zcGVjdG9yRW5oYW5jZXJzKCkge1xyXG5cdFx0ZnVuY3Rpb24gYXBwbHlQYXRjaCgpIHtcclxuXHRcdFx0dmFyIEluc3BlY3RvciA9IHcuV1BCQ19CRkJfSW5zcGVjdG9yO1xyXG5cdFx0XHRpZiAoICFJbnNwZWN0b3IgfHwgSW5zcGVjdG9yLl9fd3BiY19lbmhhbmNlcnNfcGF0Y2hlZCApIHJldHVybiBmYWxzZTtcclxuXHRcdFx0SW5zcGVjdG9yLl9fd3BiY19lbmhhbmNlcnNfcGF0Y2hlZCA9IHRydWU7XHJcblx0XHRcdHZhciBvcmlnICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBJbnNwZWN0b3IucHJvdG90eXBlLmJpbmRfdG9fZmllbGQ7XHJcblx0XHRcdEluc3BlY3Rvci5wcm90b3R5cGUuYmluZF90b19maWVsZCAgPSBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdFx0XHRvcmlnLmNhbGwoIHRoaXMsIGVsICk7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHZhciBpbnMgPSB0aGlzLnBhbmVsXHJcblx0XHRcdFx0XHRcdHx8IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3RvcicgKVxyXG5cdFx0XHRcdFx0XHR8fCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19pbnNwZWN0b3InICk7XHJcblx0XHRcdFx0XHRVSS5JbnNwZWN0b3JFbmhhbmNlcnMgJiYgVUkuSW5zcGVjdG9yRW5oYW5jZXJzLnNjYW4oIGlucyApO1xyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfZSApIHtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH07XHJcblx0XHRcdC8vIEluaXRpYWwgc2NhbiBpZiB0aGUgRE9NIGlzIGFscmVhZHkgcHJlc2VudC5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHR2YXIgaW5zRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ3dwYmNfYmZiX19pbnNwZWN0b3InIClcclxuXHRcdFx0XHRcdHx8IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2luc3BlY3RvcicgKTtcclxuXHRcdFx0XHRVSS5JbnNwZWN0b3JFbmhhbmNlcnMgJiYgVUkuSW5zcGVjdG9yRW5oYW5jZXJzLnNjYW4oIGluc0VsICk7XHJcblx0XHRcdH0gY2F0Y2ggKCBfZSApIHtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBUcnkgbm93OyBpZiBJbnNwZWN0b3IgaXNu4oCZdCBkZWZpbmVkIHlldCwgcGF0Y2ggd2hlbiBpdCBiZWNvbWVzIHJlYWR5LlxyXG5cdFx0aWYgKCAhYXBwbHlQYXRjaCgpICkge1xyXG5cdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFxyXG5cdFx0XHRcdCd3cGJjX2JmYl9pbnNwZWN0b3JfcmVhZHknLFxyXG5cdFx0XHRcdGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0XHRcdGFwcGx5UGF0Y2goKTtcclxuXHRcdFx0XHR9LFxyXG5cdFx0XHRcdHsgb25jZTogdHJ1ZSB9XHJcblx0XHRcdCk7XHJcblx0XHR9XHJcblx0fSkoKTtcclxuXHJcbn0oIHdpbmRvdywgZG9jdW1lbnQgKSk7XHJcbiIsIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4vLyA9PSBGaWxlICAvaW5jbHVkZXMvcGFnZS1mb3JtLWJ1aWxkZXIvX19qcy9jb3JlL2JmYi1zeW5jLWNvZGUtdG9vbHMtdGFiLmpzICAgfCAyMDI2LTA0LTA2IHwgMTY6MzhcclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbihmdW5jdGlvbiAoIHcsIGQgKSB7XHJcblx0J3VzZSBzdHJpY3QnO1xyXG5cclxuXHQvKipcclxuXHQgKiBEaXNwYXRjaCByZXF1ZXN0IHRvIHNob3cgYSByaWdodCBzaWRlYmFyIHBhbmVsLlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9IHBhbmVsX2lkXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9IHRhYl9pZFxyXG5cdCAqXHJcblx0ICogQHJldHVybiB7dm9pZH1cclxuXHQgKi9cclxuXHRmdW5jdGlvbiB3cGJjX2JmYl9fZGlzcGF0Y2hfc2hvd19wYW5lbCggcGFuZWxfaWQsIHRhYl9pZCApIHtcclxuXHJcblx0XHR2YXIgZXZlbnRfZGV0YWlsO1xyXG5cclxuXHRcdGlmICggISBwYW5lbF9pZCB8fCAhIHRhYl9pZCApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdGV2ZW50X2RldGFpbCA9IHtcclxuXHRcdFx0cGFuZWxfaWQ6IHBhbmVsX2lkLFxyXG5cdFx0XHR0YWJfaWQgIDogdGFiX2lkXHJcblx0XHR9O1xyXG5cclxuXHRcdGlmICggdHlwZW9mIHcud3BiY19iZmJfX2Rpc3BhdGNoX2V2ZW50X3NhZmUgPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdHcud3BiY19iZmJfX2Rpc3BhdGNoX2V2ZW50X3NhZmUoICd3cGJjX2JmYjpzaG93X3BhbmVsJywgZXZlbnRfZGV0YWlsICk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRkLmRpc3BhdGNoRXZlbnQoXHJcblx0XHRcdG5ldyBDdXN0b21FdmVudChcclxuXHRcdFx0XHQnd3BiY19iZmI6c2hvd19wYW5lbCcsXHJcblx0XHRcdFx0e1xyXG5cdFx0XHRcdFx0ZGV0YWlsOiBldmVudF9kZXRhaWxcclxuXHRcdFx0XHR9XHJcblx0XHRcdClcclxuXHRcdCk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBTeW5jIHJpZ2h0IHNpZGViYXIgd2l0aCB0b3AgbW9kZSB0YWJzLlxyXG5cdCAqXHJcblx0ICogVXNlcyB0b3AgdGFicyBuYXYgYXR0cmlidXRlIGBkYXRhLWFjdGl2ZS10YWJgIGFzIHRoZSBzb3VyY2Ugb2YgdHJ1dGguXHJcblx0ICogVGhpcyBhdm9pZHMgZmFsc2UgZGV0ZWN0aW9uIG9uIGluaXRpYWwgcGFnZSBsb2FkLCBiZWNhdXNlIHRvcCBwYW5lbHMgYXJlXHJcblx0ICogaGlkZGVuIGJ5IGlubGluZSBzdHlsZSBgZGlzcGxheTpub25lYCwgbm90IGJ5IHRoZSBgaGlkZGVuYCBhdHRyaWJ1dGUuXHJcblx0ICovXHJcblx0Y2xhc3MgV1BCQ19CRkJfTW9kZV9SaWdodGJhcl9TeW5jIHtcclxuXHJcblx0XHRjb25zdHJ1Y3RvcigpIHtcclxuXHRcdFx0dGhpcy50b3BfdGFic19uYXYgICAgICA9IG51bGw7XHJcblx0XHRcdHRoaXMuYWR2YW5jZWRfdGFiX3dyYXAgPSBudWxsO1xyXG5cdFx0XHR0aGlzLmFkdmFuY2VkX3BhbmVsICAgID0gbnVsbDtcclxuXHRcdFx0dGhpcy5tdXRhdGlvbl9vYnNlcnZlciA9IG51bGw7XHJcblx0XHRcdHRoaXMubGFzdF9tb2RlICAgICAgICAgPSAnJztcclxuXHRcdFx0dGhpcy5zeW5jX3JpZ2h0YmFyX21vZGUgPSB0aGlzLnN5bmNfcmlnaHRiYXJfbW9kZS5iaW5kKCB0aGlzICk7XHJcblx0XHRcdHRoaXMuaGFuZGxlX2NsaWNrICAgICAgPSB0aGlzLmhhbmRsZV9jbGljay5iaW5kKCB0aGlzICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBJbml0IGNvbnRyb2xsZXIuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybiB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0aW5pdCgpIHtcclxuXHRcdFx0dGhpcy50b3BfdGFic19uYXYgICAgICA9IGQuZ2V0RWxlbWVudEJ5SWQoICd3cGJjX2JmYl9fdG9wX2hvcmlzb250YWxfbmF2JyApO1xyXG5cdFx0XHR0aGlzLmFkdmFuY2VkX3RhYl93cmFwID0gZC5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19yaWdodGJhcl90YWJfd3JhcC0tYWR2YW5jZWRfdG9vbHMnICk7XHJcblx0XHRcdHRoaXMuYWR2YW5jZWRfcGFuZWwgICAgPSBkLmdldEVsZW1lbnRCeUlkKCAnd3BiY19iZmJfX2luc3BlY3Rvcl9hZHZhbmNlZF90b29scycgKTtcclxuXHJcblx0XHRcdGlmICggISB0aGlzLmFkdmFuY2VkX3RhYl93cmFwIHx8ICEgdGhpcy5hZHZhbmNlZF9wYW5lbCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIE9ic2VydmUgYWN0aXZlIHRhYiBjaGFuZ2VzIGluIHRoZSB0b3AgdGFicyBuYXYuXHJcblx0XHRcdGlmICggdGhpcy50b3BfdGFic19uYXYgKSB7XHJcblx0XHRcdFx0dGhpcy5tdXRhdGlvbl9vYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKCB0aGlzLnN5bmNfcmlnaHRiYXJfbW9kZSApO1xyXG5cdFx0XHRcdHRoaXMubXV0YXRpb25fb2JzZXJ2ZXIub2JzZXJ2ZShcclxuXHRcdFx0XHRcdHRoaXMudG9wX3RhYnNfbmF2LFxyXG5cdFx0XHRcdFx0e1xyXG5cdFx0XHRcdFx0XHRhdHRyaWJ1dGVzICAgICA6IHRydWUsXHJcblx0XHRcdFx0XHRcdGF0dHJpYnV0ZUZpbHRlcjogWyAnZGF0YS1hY3RpdmUtdGFiJyBdXHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0KTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gRmFsbGJhY2sgZm9yIGNsaWNrLWRyaXZlbiBzd2l0Y2hpbmcuXHJcblx0XHRcdGQuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcy5oYW5kbGVfY2xpY2ssIHRydWUgKTtcclxuXHJcblx0XHRcdC8vIEluaXRpYWwgc3luYy5cclxuXHRcdFx0dGhpcy5zeW5jX3JpZ2h0YmFyX21vZGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEdldCBhY3RpdmUgdG9wIHRhYiBpZC5cclxuXHRcdCAqXHJcblx0XHQgKiBQcmlvcml0eTpcclxuXHRcdCAqIDEuIGRhdGEtYWN0aXZlLXRhYiBmcm9tIHRvcCB0YWJzIG5hdlxyXG5cdFx0ICogMi4gYWN0aXZlIENTUyBjbGFzcyBpbiBuYXZcclxuXHRcdCAqIDMuIHZpc2libGUgdG9wIHBhbmVsIGZhbGxiYWNrXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybiB7c3RyaW5nfVxyXG5cdFx0ICovXHJcblx0XHRnZXRfYWN0aXZlX3RvcF90YWJfaWQoKSB7XHJcblx0XHRcdHZhciBhY3RpdmVfdGFiX2lkID0gJyc7XHJcblx0XHRcdHZhciBhY3RpdmVfbGluayAgID0gbnVsbDtcclxuXHRcdFx0dmFyIGFkdmFuY2VkX3BhbmVsID0gbnVsbDtcclxuXHRcdFx0dmFyIGJ1aWxkZXJfcGFuZWwgID0gbnVsbDtcclxuXHJcblx0XHRcdGlmICggdGhpcy50b3BfdGFic19uYXYgKSB7XHJcblx0XHRcdFx0YWN0aXZlX3RhYl9pZCA9IFN0cmluZyggdGhpcy50b3BfdGFic19uYXYuZ2V0QXR0cmlidXRlKCAnZGF0YS1hY3RpdmUtdGFiJyApIHx8ICcnICkudHJpbSgpO1xyXG5cdFx0XHRcdGlmICggJycgIT09IGFjdGl2ZV90YWJfaWQgKSB7XHJcblx0XHRcdFx0XHRyZXR1cm4gYWN0aXZlX3RhYl9pZDtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGFjdGl2ZV9saW5rID0gdGhpcy50b3BfdGFic19uYXYucXVlcnlTZWxlY3RvciggJy53cGJjX3VpX2VsX19ob3Jpc19uYXZfaXRlbS5hY3RpdmUgW2RhdGEtd3BiYy1iZmItdGFiXScgKTtcclxuXHRcdFx0XHRpZiAoIGFjdGl2ZV9saW5rICkge1xyXG5cdFx0XHRcdFx0YWN0aXZlX3RhYl9pZCA9IFN0cmluZyggYWN0aXZlX2xpbmsuZ2V0QXR0cmlidXRlKCAnZGF0YS13cGJjLWJmYi10YWInICkgfHwgJycgKS50cmltKCk7XHJcblx0XHRcdFx0XHRpZiAoICcnICE9PSBhY3RpdmVfdGFiX2lkICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gYWN0aXZlX3RhYl9pZDtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIEZpbmFsIGZhbGxiYWNrOiBkZXRlY3QgdmlzaWJsZSBwYW5lbC5cclxuXHRcdFx0YWR2YW5jZWRfcGFuZWwgPSBkLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX3RvcF90YWJfc2VjdGlvbl9fYWR2YW5jZWRfdGFiJyApO1xyXG5cdFx0XHRidWlsZGVyX3BhbmVsICA9IGQucXVlcnlTZWxlY3RvciggJy53cGJjX2JmYl9fdG9wX3RhYl9zZWN0aW9uX19idWlsZGVyX3RhYicgKTtcclxuXHJcblx0XHRcdGlmICggdGhpcy5pc19lbGVtZW50X3Zpc2libGUoIGFkdmFuY2VkX3BhbmVsICkgKSB7XHJcblx0XHRcdFx0cmV0dXJuICdhZHZhbmNlZF90YWInO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmICggdGhpcy5pc19lbGVtZW50X3Zpc2libGUoIGJ1aWxkZXJfcGFuZWwgKSApIHtcclxuXHRcdFx0XHRyZXR1cm4gJ2J1aWxkZXJfdGFiJztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0cmV0dXJuICcnO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ2hlY2sgZWxlbWVudCB2aXNpYmlsaXR5LlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR8bnVsbH0gZWxcclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJuIHtib29sZWFufVxyXG5cdFx0ICovXHJcblx0XHRpc19lbGVtZW50X3Zpc2libGUoIGVsICkge1xyXG5cdFx0XHR2YXIgc3R5bGU7XHJcblxyXG5cdFx0XHRpZiAoICEgZWwgKSB7XHJcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRzdHlsZSA9IHcuZ2V0Q29tcHV0ZWRTdHlsZSggZWwgKTtcclxuXHJcblx0XHRcdGlmICggISBzdHlsZSApIHtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGlmICggJ25vbmUnID09PSBzdHlsZS5kaXNwbGF5ICkge1xyXG5cdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCAnaGlkZGVuJyA9PT0gc3R5bGUudmlzaWJpbGl0eSApIHtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ2hlY2sgaWYgQWR2YW5jZWQgTW9kZSBpcyBhY3RpdmUuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybiB7Ym9vbGVhbn1cclxuXHRcdCAqL1xyXG5cdFx0aXNfYWR2YW5jZWRfbW9kZV9hY3RpdmUoKSB7XHJcblx0XHRcdHJldHVybiAoICdhZHZhbmNlZF90YWInID09PSB0aGlzLmdldF9hY3RpdmVfdG9wX3RhYl9pZCgpICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBTaG93IENvZGUgVG9vbHMgdGFiIGJ1dHRvbi5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJuIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRzaG93X2FkdmFuY2VkX3Rvb2xzX3RhYigpIHtcclxuXHRcdFx0dGhpcy5hZHZhbmNlZF90YWJfd3JhcC5yZW1vdmVBdHRyaWJ1dGUoICdoaWRkZW4nICk7XHJcblx0XHRcdHRoaXMuYWR2YW5jZWRfdGFiX3dyYXAuc2V0QXR0cmlidXRlKCAnYXJpYS1oaWRkZW4nLCAnZmFsc2UnICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBIaWRlIENvZGUgVG9vbHMgdGFiIGJ1dHRvbi5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJuIHt2b2lkfVxyXG5cdFx0ICovXHJcblx0XHRoaWRlX2FkdmFuY2VkX3Rvb2xzX3RhYigpIHtcclxuXHRcdFx0dGhpcy5hZHZhbmNlZF90YWJfd3JhcC5zZXRBdHRyaWJ1dGUoICdoaWRkZW4nLCAndHJ1ZScgKTtcclxuXHRcdFx0dGhpcy5hZHZhbmNlZF90YWJfd3JhcC5zZXRBdHRyaWJ1dGUoICdhcmlhLWhpZGRlbicsICd0cnVlJyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ2hlY2sgaWYgQ29kZSBUb29scyBwYW5lbCBpcyBjdXJyZW50bHkgYWN0aXZlLlxyXG5cdFx0ICpcclxuXHRcdCAqIEByZXR1cm4ge2Jvb2xlYW59XHJcblx0XHQgKi9cclxuXHRcdGlzX2FkdmFuY2VkX3Rvb2xzX3BhbmVsX2FjdGl2ZSgpIHtcclxuXHRcdFx0cmV0dXJuICEhICggdGhpcy5hZHZhbmNlZF9wYW5lbCAmJiAhIHRoaXMuYWR2YW5jZWRfcGFuZWwuaGFzQXR0cmlidXRlKCAnaGlkZGVuJyApICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBPcGVuIENvZGUgVG9vbHMgcGFuZWwuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybiB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0b3Blbl9hZHZhbmNlZF90b29sc19wYW5lbCgpIHtcclxuXHRcdFx0d3BiY19iZmJfX2Rpc3BhdGNoX3Nob3dfcGFuZWwoICd3cGJjX2JmYl9faW5zcGVjdG9yX2FkdmFuY2VkX3Rvb2xzJywgJ3dwYmNfdGFiX2FkdmFuY2VkX3Rvb2xzJyApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogT3BlbiBkZWZhdWx0IGJ1aWxkZXIgcmlnaHRiYXIgcGFuZWwuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybiB7dm9pZH1cclxuXHRcdCAqL1xyXG5cdFx0b3Blbl9kZWZhdWx0X3Zpc3VhbF9wYW5lbCgpIHtcclxuXHRcdFx0d3BiY19iZmJfX2Rpc3BhdGNoX3Nob3dfcGFuZWwoICd3cGJjX2JmYl9fcGFsZXR0ZV9hZGRfbmV3JywgJ3dwYmNfdGFiX2xpYnJhcnknICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBTeW5jIHJpZ2h0YmFyIG1vZGUgd2l0aCB0b3AgdGFicyBtb2RlLlxyXG5cdFx0ICpcclxuXHRcdCAqIEByZXR1cm4ge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdHN5bmNfcmlnaHRiYXJfbW9kZSgpIHtcclxuXHRcdFx0dmFyIGlzX2FkdmFuY2VkX21vZGUgPSB0aGlzLmlzX2FkdmFuY2VkX21vZGVfYWN0aXZlKCk7XHJcblxyXG5cdFx0XHRpZiAoIGlzX2FkdmFuY2VkX21vZGUgKSB7XHJcblx0XHRcdFx0dGhpcy5zaG93X2FkdmFuY2VkX3Rvb2xzX3RhYigpO1xyXG5cclxuXHRcdFx0XHRpZiAoICdhZHZhbmNlZCcgIT09IHRoaXMubGFzdF9tb2RlICkge1xyXG5cdFx0XHRcdFx0dGhpcy5vcGVuX2FkdmFuY2VkX3Rvb2xzX3BhbmVsKCk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHR0aGlzLmxhc3RfbW9kZSA9ICdhZHZhbmNlZCc7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAoIHRoaXMuaXNfYWR2YW5jZWRfdG9vbHNfcGFuZWxfYWN0aXZlKCkgKSB7XHJcblx0XHRcdFx0dGhpcy5vcGVuX2RlZmF1bHRfdmlzdWFsX3BhbmVsKCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMuaGlkZV9hZHZhbmNlZF90b29sc190YWIoKTtcclxuXHRcdFx0dGhpcy5sYXN0X21vZGUgPSAnYnVpbGRlcic7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBGYWxsYmFjayBjbGljayBoYW5kbGVyIGZvciB0b3AgdGFicy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0V2ZW50fSBldmVudFxyXG5cdFx0ICpcclxuXHRcdCAqIEByZXR1cm4ge3ZvaWR9XHJcblx0XHQgKi9cclxuXHRcdGhhbmRsZV9jbGljayggZXZlbnQgKSB7XHJcblx0XHRcdHZhciB0YXJnZXQgPSBldmVudC50YXJnZXQuY2xvc2VzdCggJ1tkYXRhLXdwYmMtYmZiLXRhYl0nICk7XHJcblx0XHRcdHZhciB0aGlzX29iaiA9IHRoaXM7XHJcblxyXG5cdFx0XHRpZiAoICEgdGFyZ2V0ICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0c2V0VGltZW91dChcclxuXHRcdFx0XHRmdW5jdGlvbiAoKSB7XHJcblx0XHRcdFx0XHR0aGlzX29iai5zeW5jX3JpZ2h0YmFyX21vZGUoKTtcclxuXHRcdFx0XHR9LFxyXG5cdFx0XHRcdDBcclxuXHRcdFx0KTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdGlmICggZC5yZWFkeVN0YXRlID09PSAnbG9hZGluZycgKSB7XHJcblx0XHRkLmFkZEV2ZW50TGlzdGVuZXIoXHJcblx0XHRcdCdET01Db250ZW50TG9hZGVkJyxcclxuXHRcdFx0ZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdCggbmV3IFdQQkNfQkZCX01vZGVfUmlnaHRiYXJfU3luYygpICkuaW5pdCgpO1xyXG5cdFx0XHR9XHJcblx0XHQpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHQoIG5ldyBXUEJDX0JGQl9Nb2RlX1JpZ2h0YmFyX1N5bmMoKSApLmluaXQoKTtcclxuXHR9XHJcblxyXG59KSggd2luZG93LCBkb2N1bWVudCApOyIsIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4vLyA9PSBGaWxlICAvaW5jbHVkZXMvcGFnZS1mb3JtLWJ1aWxkZXIvX291dC9jb3JlL2JmYi1pbnNwZWN0b3IuanMgPT0gVGltZSBwb2ludDogMjAyNS0wOS0wNiAxNDowOFxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuKGZ1bmN0aW9uICh3KSB7XHJcblx0J3VzZSBzdHJpY3QnO1xyXG5cclxuXHQvLyAxKSBBY3Rpb25zIHJlZ2lzdHJ5LlxyXG5cclxuXHQvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIChjdHg6IEluc3BlY3RvckFjdGlvbkNvbnRleHQpID0+IHZvaWQ+fSAqL1xyXG5cdGNvbnN0IF9fSU5TUEVDVE9SX0FDVElPTlNfTUFQX18gPSBPYmplY3QuY3JlYXRlKCBudWxsICk7XHJcblxyXG5cdC8vIEJ1aWx0LWlucy5cclxuXHRfX0lOU1BFQ1RPUl9BQ1RJT05TX01BUF9fWydkZXNlbGVjdCddID0gKHsgYnVpbGRlciB9KSA9PiB7XHJcblx0XHRidWlsZGVyPy5zZWxlY3RfZmllbGQ/LiggbnVsbCApO1xyXG5cdH07XHJcblxyXG5cdF9fSU5TUEVDVE9SX0FDVElPTlNfTUFQX19bJ3Njcm9sbHRvJ10gPSAoeyBidWlsZGVyLCBlbCB9KSA9PiB7XHJcblx0XHRpZiAoICFlbCB8fCAhZG9jdW1lbnQuYm9keS5jb250YWlucyggZWwgKSApIHJldHVybjtcclxuXHRcdGJ1aWxkZXI/LnNlbGVjdF9maWVsZD8uKCBlbCwgeyBzY3JvbGxJbnRvVmlldzogdHJ1ZSB9ICk7XHJcblx0XHRlbC5jbGFzc0xpc3QuYWRkKCAnd3BiY19iZmJfX3Njcm9sbC1wdWxzZScgKTtcclxuXHRcdHNldFRpbWVvdXQoICgpID0+IGVsLmNsYXNzTGlzdC5yZW1vdmUoICd3cGJjX2JmYl9fc2Nyb2xsLXB1bHNlJyApLCA3MDAgKTtcclxuXHR9O1xyXG5cclxuXHRfX0lOU1BFQ1RPUl9BQ1RJT05TX01BUF9fWydtb3ZlLXVwJ10gPSAoeyBidWlsZGVyLCBlbCB9KSA9PiB7XHJcblx0XHRpZiAoICFlbCApIHJldHVybjtcclxuXHRcdGJ1aWxkZXI/Lm1vdmVfaXRlbT8uKCBlbCwgJ3VwJyApO1xyXG5cdFx0Ly8gU2Nyb2xsIGFmdGVyIHRoZSBET00gaGFzIHNldHRsZWQuXHJcblx0XHRyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gX19JTlNQRUNUT1JfQUNUSU9OU19NQVBfX1snc2Nyb2xsdG8nXSh7IGJ1aWxkZXIsIGVsIH0pKTtcclxuXHR9O1xyXG5cclxuXHRfX0lOU1BFQ1RPUl9BQ1RJT05TX01BUF9fWydtb3ZlLWRvd24nXSA9ICh7IGJ1aWxkZXIsIGVsIH0pID0+IHtcclxuXHRcdGlmICggIWVsICkgcmV0dXJuO1xyXG5cdFx0YnVpbGRlcj8ubW92ZV9pdGVtPy4oIGVsLCAnZG93bicgKTtcclxuXHRcdC8vIFNjcm9sbCBhZnRlciB0aGUgRE9NIGhhcyBzZXR0bGVkLlxyXG5cdFx0cmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IF9fSU5TUEVDVE9SX0FDVElPTlNfTUFQX19bJ3Njcm9sbHRvJ10oeyBidWlsZGVyLCBlbCB9KSk7XHJcblx0fTtcclxuXHJcblx0X19JTlNQRUNUT1JfQUNUSU9OU19NQVBfX1snZGVsZXRlJ10gPSAoeyBidWlsZGVyLCBlbCwgY29uZmlybSA9IHcuY29uZmlybSB9KSA9PiB7XHJcblx0XHRpZiAoICFlbCApIHJldHVybjtcclxuXHRcdGNvbnN0IGlzX2ZpZWxkID0gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfX2ZpZWxkJyApO1xyXG5cdFx0Y29uc3QgbGFiZWwgICAgPSBpc19maWVsZFxyXG5cdFx0XHQ/IChlbC5xdWVyeVNlbGVjdG9yKCAnLndwYmNfYmZiX19maWVsZC1sYWJlbCcgKT8udGV4dENvbnRlbnQgfHwgZWwuZGF0YXNldD8uaWQgfHwgJ2ZpZWxkJylcclxuXHRcdFx0OiAoZWwuZGF0YXNldD8uaWQgfHwgJ3NlY3Rpb24nKTtcclxuXHJcblx0XHRVSS5Nb2RhbF9Db25maXJtX0RlbGV0ZS5vcGVuKCBsYWJlbCwgKCkgPT4ge1xyXG5cdFx0XHQvLyBDZW50cmFsIGNvbW1hbmQgd2lsbCByZW1vdmUsIGVtaXQgZXZlbnRzLCBhbmQgcmVzZWxlY3QgbmVpZ2hib3IgKHdoaWNoIHJlLWJpbmRzIEluc3BlY3RvcikuXHJcblx0XHRcdGJ1aWxkZXI/LmRlbGV0ZV9pdGVtPy4oIGVsICk7XHJcblx0XHR9ICk7XHJcblxyXG5cdH07XHJcblxyXG5cdF9fSU5TUEVDVE9SX0FDVElPTlNfTUFQX19bJ2R1cGxpY2F0ZSddID0gKHsgYnVpbGRlciwgZWwgfSkgPT4ge1xyXG5cdFx0aWYgKCAhZWwgKSByZXR1cm47XHJcblx0XHRjb25zdCBjbG9uZSA9IGJ1aWxkZXI/LmR1cGxpY2F0ZV9pdGVtPy4oIGVsICk7XHJcblx0XHRpZiAoIGNsb25lICkgYnVpbGRlcj8uc2VsZWN0X2ZpZWxkPy4oIGNsb25lLCB7IHNjcm9sbEludG9WaWV3OiB0cnVlIH0gKTtcclxuXHR9O1xyXG5cclxuXHQvLyBQdWJsaWMgQVBJLlxyXG5cdHcuV1BCQ19CRkJfSW5zcGVjdG9yX0FjdGlvbnMgPSB7XHJcblx0XHRydW4obmFtZSwgY3R4KSB7XHJcblx0XHRcdGNvbnN0IGZuID0gX19JTlNQRUNUT1JfQUNUSU9OU19NQVBfX1tuYW1lXTtcclxuXHRcdFx0aWYgKCB0eXBlb2YgZm4gPT09ICdmdW5jdGlvbicgKSBmbiggY3R4ICk7XHJcblx0XHRcdGVsc2UgY29uc29sZS53YXJuKCAnV1BCQy4gSW5zcGVjdG9yIGFjdGlvbiBub3QgZm91bmQ6JywgbmFtZSApO1xyXG5cdFx0fSxcclxuXHRcdHJlZ2lzdGVyKG5hbWUsIGhhbmRsZXIpIHtcclxuXHRcdFx0aWYgKCAhbmFtZSB8fCB0eXBlb2YgaGFuZGxlciAhPT0gJ2Z1bmN0aW9uJyApIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoICdyZWdpc3RlcihuYW1lLCBoYW5kbGVyKTogaW52YWxpZCBhcmd1bWVudHMnICk7XHJcblx0XHRcdH1cclxuXHRcdFx0X19JTlNQRUNUT1JfQUNUSU9OU19NQVBfX1tuYW1lXSA9IGhhbmRsZXI7XHJcblx0XHR9LFxyXG5cdFx0aGFzKG5hbWUpIHtcclxuXHRcdFx0cmV0dXJuIHR5cGVvZiBfX0lOU1BFQ1RPUl9BQ1RJT05TX01BUF9fW25hbWVdID09PSAnZnVuY3Rpb24nO1xyXG5cdFx0fVxyXG5cdH07XHJcblxyXG5cdC8vIDIpIEluc3BlY3RvciBGYWN0b3J5LlxyXG5cclxuXHR2YXIgVUkgPSAody5XUEJDX0JGQl9Db3JlLlVJID0gdy5XUEJDX0JGQl9Db3JlLlVJIHx8IHt9KTtcclxuXHJcblx0Ly8gR2xvYmFsIEh5YnJpZCsrIHJlZ2lzdHJpZXMgKGtlZXAgcHVibGljKS5cclxuXHR3LndwYmNfYmZiX2luc3BlY3Rvcl9mYWN0b3J5X3Nsb3RzICAgICAgPSB3LndwYmNfYmZiX2luc3BlY3Rvcl9mYWN0b3J5X3Nsb3RzIHx8IHt9O1xyXG5cdHcud3BiY19iZmJfaW5zcGVjdG9yX2ZhY3RvcnlfdmFsdWVfZnJvbSA9IHcud3BiY19iZmJfaW5zcGVjdG9yX2ZhY3RvcnlfdmFsdWVfZnJvbSB8fCB7fTtcclxuXHJcblx0Ly8gRGVmaW5lIEZhY3Rvcnkgb25seSBpZiBtaXNzaW5nIChubyBlYXJseSByZXR1cm4gZm9yIHRoZSB3aG9sZSBidW5kbGUpLlxyXG5cdC8vIGFsd2F5cyBkZWZpbmUvcmVwbGFjZSBGYWN0b3J5XHJcblx0e1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVXRpbGl0eTogY3JlYXRlIGVsZW1lbnQgd2l0aCBhdHRyaWJ1dGVzIGFuZCBjaGlsZHJlbi5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gdGFnXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdD19IGF0dHJzXHJcblx0XHQgKiBAcGFyYW0geyhOb2RlfHN0cmluZ3xBcnJheTxOb2RlfHN0cmluZz4pPX0gY2hpbGRyZW5cclxuXHRcdCAqIEByZXR1cm5zIHtIVE1MRWxlbWVudH1cclxuXHRcdCAqL1xyXG5cdFx0ZnVuY3Rpb24gZWwodGFnLCBhdHRycywgY2hpbGRyZW4pIHtcclxuXHRcdFx0dmFyIG5vZGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCB0YWcgKTtcclxuXHRcdFx0aWYgKCBhdHRycyApIHtcclxuXHRcdFx0XHRPYmplY3Qua2V5cyggYXR0cnMgKS5mb3JFYWNoKCBmdW5jdGlvbiAoaykge1xyXG5cdFx0XHRcdFx0dmFyIHYgPSBhdHRyc1trXTtcclxuXHRcdFx0XHRcdGlmICggdiA9PSBudWxsICkgcmV0dXJuO1xyXG5cdFx0XHRcdFx0aWYgKCBrID09PSAnY2xhc3MnICkge1xyXG5cdFx0XHRcdFx0XHRub2RlLmNsYXNzTmFtZSA9IHY7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGlmICggayA9PT0gJ2RhdGFzZXQnICkge1xyXG5cdFx0XHRcdFx0XHRPYmplY3Qua2V5cyggdiApLmZvckVhY2goIGZ1bmN0aW9uIChkaykge1xyXG5cdFx0XHRcdFx0XHRcdG5vZGUuZGF0YXNldFtka10gPSBTdHJpbmcoIHZbZGtdICk7XHJcblx0XHRcdFx0XHRcdH0gKTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0aWYgKCBrID09PSAnY2hlY2tlZCcgJiYgdHlwZW9mIHYgPT09ICdib29sZWFuJyApIHtcclxuXHRcdFx0XHRcdFx0aWYgKCB2ICkgbm9kZS5zZXRBdHRyaWJ1dGUoICdjaGVja2VkJywgJ2NoZWNrZWQnICk7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGlmICggayA9PT0gJ2Rpc2FibGVkJyAmJiB0eXBlb2YgdiA9PT0gJ2Jvb2xlYW4nICkge1xyXG5cdFx0XHRcdFx0XHRpZiAoIHYgKSBub2RlLnNldEF0dHJpYnV0ZSggJ2Rpc2FibGVkJywgJ2Rpc2FibGVkJyApO1xyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHQvLyBub3JtYWxpemUgYm9vbGVhbiBhdHRyaWJ1dGVzIHRvIHN0cmluZ3MuXHJcblx0XHRcdFx0XHRpZiAoIHR5cGVvZiB2ID09PSAnYm9vbGVhbicgKSB7XHJcblx0XHRcdFx0XHRcdG5vZGUuc2V0QXR0cmlidXRlKCBrLCB2ID8gJ3RydWUnIDogJ2ZhbHNlJyApO1xyXG5cdFx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRub2RlLnNldEF0dHJpYnV0ZSggaywgU3RyaW5nKCB2ICkgKTtcclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKCBjaGlsZHJlbiApIHtcclxuXHRcdFx0XHQoQXJyYXkuaXNBcnJheSggY2hpbGRyZW4gKSA/IGNoaWxkcmVuIDogWyBjaGlsZHJlbiBdKS5mb3JFYWNoKCBmdW5jdGlvbiAoYykge1xyXG5cdFx0XHRcdFx0aWYgKCBjID09IG51bGwgKSByZXR1cm47XHJcblx0XHRcdFx0XHRub2RlLmFwcGVuZENoaWxkKCAodHlwZW9mIGMgPT09ICdzdHJpbmcnKSA/IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKCBjICkgOiBjICk7XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBub2RlO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQnVpbGQgYSB0b2dnbGUgY29udHJvbCByb3cgKGNoZWNrYm94IHJlbmRlcmVkIGFzIHRvZ2dsZSkuXHJcblx0XHQgKlxyXG5cdFx0ICogU3RydWN0dXJlOlxyXG5cdFx0ICogPGRpdiBjbGFzcz1cImluc3BlY3Rvcl9fcm93IGluc3BlY3Rvcl9fcm93LS10b2dnbGVcIj5cclxuXHRcdCAqICAgPGRpdiBjbGFzcz1cImluc3BlY3Rvcl9fY29udHJvbCB3cGJjX3VpX190b2dnbGVcIj5cclxuXHRcdCAqICAgICA8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgaWQ9XCJJRFwiIGRhdGEtaW5zcGVjdG9yLWtleT1cIktFWVwiIGNsYXNzPVwiaW5zcGVjdG9yX19pbnB1dFwiIGNoZWNrZWQ+XHJcblx0XHQgKiAgICAgPGxhYmVsIGNsYXNzPVwid3BiY191aV9fdG9nZ2xlX2ljb25cIiAgZm9yPVwiSURcIj48L2xhYmVsPlxyXG5cdFx0ICogICAgIDxsYWJlbCBjbGFzcz1cIndwYmNfdWlfX3RvZ2dsZV9sYWJlbFwiIGZvcj1cIklEXCI+TGFiZWwgdGV4dDwvbGFiZWw+XHJcblx0XHQgKiAgIDwvZGl2PlxyXG5cdFx0ICogPC9kaXY+XHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGlucHV0X2lkXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30ga2V5XHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IGNoZWNrZWRcclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSBsYWJlbF90ZXh0XHJcblx0XHQgKiBAcmV0dXJucyB7SFRNTEVsZW1lbnR9XHJcblx0XHQgKi9cclxuXHRcdGZ1bmN0aW9uIGJ1aWxkX3RvZ2dsZV9yb3coIGlucHV0X2lkLCBrZXksIGNoZWNrZWQsIGxhYmVsX3RleHQgKSB7XHJcblxyXG5cdFx0XHR2YXIgcm93X2VsICAgID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX3JvdyBpbnNwZWN0b3JfX3Jvdy0tdG9nZ2xlJyB9ICk7XHJcblx0XHRcdHZhciBjdHJsX3dyYXAgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ2luc3BlY3Rvcl9fY29udHJvbCB3cGJjX3VpX190b2dnbGUnIH0gKTtcclxuXHJcblx0XHRcdHZhciBpbnB1dF9lbCA9IGVsKCAnaW5wdXQnLCB7XHJcblx0XHRcdFx0aWQgICAgICAgICAgICAgICAgICA6IGlucHV0X2lkLFxyXG5cdFx0XHRcdHR5cGUgICAgICAgICAgICAgICAgOiAnY2hlY2tib3gnLFxyXG5cdFx0XHRcdCdkYXRhLWluc3BlY3Rvci1rZXknOiBrZXksXHJcblx0XHRcdFx0J2NsYXNzJyAgICAgICAgICAgICA6ICdpbnNwZWN0b3JfX2lucHV0JyxcclxuXHRcdFx0XHRjaGVja2VkICAgICAgICAgICAgIDogISFjaGVja2VkLFxyXG5cdFx0XHRcdHJvbGUgICAgICAgICAgICAgICAgOiAnc3dpdGNoJyxcclxuXHRcdFx0XHQnYXJpYS1jaGVja2VkJyAgICAgIDogISFjaGVja2VkXHJcblx0XHRcdH0gKTtcclxuXHRcdFx0dmFyIGljb25fbGJsID0gZWwoICdsYWJlbCcsIHsgJ2NsYXNzJzogJ3dwYmNfdWlfX3RvZ2dsZV9pY29uJywgJ2Zvcic6IGlucHV0X2lkIH0gKTtcclxuXHRcdFx0dmFyIHRleHRfbGJsID0gZWwoICdsYWJlbCcsIHsgJ2NsYXNzJzogJ3dwYmNfdWlfX3RvZ2dsZV9sYWJlbCcsICdmb3InOiBpbnB1dF9pZCB9LCBsYWJlbF90ZXh0IHx8ICcnICk7XHJcblxyXG5cdFx0XHRjdHJsX3dyYXAuYXBwZW5kQ2hpbGQoIGlucHV0X2VsICk7XHJcblx0XHRcdGN0cmxfd3JhcC5hcHBlbmRDaGlsZCggaWNvbl9sYmwgKTtcclxuXHRcdFx0Y3RybF93cmFwLmFwcGVuZENoaWxkKCB0ZXh0X2xibCApO1xyXG5cclxuXHRcdFx0cm93X2VsLmFwcGVuZENoaWxkKCBjdHJsX3dyYXAgKTtcclxuXHRcdFx0cmV0dXJuIHJvd19lbDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHQgKiBVdGlsaXR5OiBjaG9vc2UgaW5pdGlhbCB2YWx1ZSBmcm9tIGRhdGEgb3Igc2NoZW1hIGRlZmF1bHQuXHJcblx0ICovXHJcblx0XHRmdW5jdGlvbiBnZXRfaW5pdGlhbF92YWx1ZShrZXksIGRhdGEsIHByb3BzX3NjaGVtYSkge1xyXG5cdFx0XHRpZiAoIGRhdGEgJiYgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKCBkYXRhLCBrZXkgKSApIHJldHVybiBkYXRhW2tleV07XHJcblx0XHRcdHZhciBtZXRhID0gcHJvcHNfc2NoZW1hICYmIHByb3BzX3NjaGVtYVtrZXldO1xyXG5cdFx0XHRyZXR1cm4gKG1ldGEgJiYgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKCBtZXRhLCAnZGVmYXVsdCcgKSkgPyBtZXRhLmRlZmF1bHQgOiAnJztcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHQgKiBVdGlsaXR5OiBjb2VyY2UgdmFsdWUgYnkgc2NoZW1hIHR5cGUuXHJcblx0ICovXHJcblxyXG5cclxuXHRcdGZ1bmN0aW9uIGNvZXJjZV9ieV90eXBlKHZhbHVlLCB0eXBlKSB7XHJcblx0XHRcdHN3aXRjaCAoIHR5cGUgKSB7XHJcblx0XHRcdFx0Y2FzZSAnbnVtYmVyJzpcclxuXHRcdFx0XHRjYXNlICdpbnQnOlxyXG5cdFx0XHRcdGNhc2UgJ2Zsb2F0JzpcclxuXHRcdFx0XHRcdGlmICggdmFsdWUgPT09ICcnIHx8IHZhbHVlID09IG51bGwgKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybiAnJztcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdHZhciBuID0gTnVtYmVyKCB2YWx1ZSApO1xyXG5cdFx0XHRcdFx0cmV0dXJuIGlzTmFOKCBuICkgPyAnJyA6IG47XHJcblx0XHRcdFx0Y2FzZSAnYm9vbGVhbic6XHJcblx0XHRcdFx0XHRyZXR1cm4gISF2YWx1ZTtcclxuXHRcdFx0XHRjYXNlICdhcnJheSc6XHJcblx0XHRcdFx0XHRyZXR1cm4gQXJyYXkuaXNBcnJheSggdmFsdWUgKSA/IHZhbHVlIDogW107XHJcblx0XHRcdFx0ZGVmYXVsdDpcclxuXHRcdFx0XHRcdHJldHVybiAodmFsdWUgPT0gbnVsbCkgPyAnJyA6IFN0cmluZyggdmFsdWUgKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdCAqIE5vcm1hbGl6ZSA8c2VsZWN0PiBvcHRpb25zIChhcnJheSBvZiB7dmFsdWUsbGFiZWx9IG9yIG1hcCB7dmFsdWU6bGFiZWx9KS5cclxuXHQgKi9cclxuXHRcdGZ1bmN0aW9uIG5vcm1hbGl6ZV9zZWxlY3Rfb3B0aW9ucyhvcHRpb25zKSB7XHJcblx0XHRcdGlmICggQXJyYXkuaXNBcnJheSggb3B0aW9ucyApICkge1xyXG5cdFx0XHRcdHJldHVybiBvcHRpb25zLm1hcCggZnVuY3Rpb24gKG8pIHtcclxuXHRcdFx0XHRcdGlmICggdHlwZW9mIG8gPT09ICdvYmplY3QnICYmIG8gJiYgJ3ZhbHVlJyBpbiBvICkge1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4geyB2YWx1ZTogU3RyaW5nKCBvLnZhbHVlICksIGxhYmVsOiBTdHJpbmcoIG8ubGFiZWwgfHwgby52YWx1ZSApIH07XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRyZXR1cm4geyB2YWx1ZTogU3RyaW5nKCBvICksIGxhYmVsOiBTdHJpbmcoIG8gKSB9O1xyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoIG9wdGlvbnMgJiYgdHlwZW9mIG9wdGlvbnMgPT09ICdvYmplY3QnICkge1xyXG5cdFx0XHRcdHJldHVybiBPYmplY3Qua2V5cyggb3B0aW9ucyApLm1hcCggZnVuY3Rpb24gKGspIHtcclxuXHRcdFx0XHRcdHJldHVybiB7IHZhbHVlOiBTdHJpbmcoIGsgKSwgbGFiZWw6IFN0cmluZyggb3B0aW9uc1trXSApIH07XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBbXTtcclxuXHRcdH1cclxuXHJcblx0XHQvKiogUGFyc2UgYSBDU1MgbGVuZ3RoIGxpa2UgXCIxMjBweFwiIG9yIFwiODAlXCIgaW50byB7IHZhbHVlOm51bWJlciwgdW5pdDpzdHJpbmcgfS4gKi9cclxuXHRcdGZ1bmN0aW9uIHBhcnNlX2xlbih2YWx1ZSwgZmFsbGJhY2tfdW5pdCkge1xyXG5cdFx0XHR2YWx1ZSA9ICh2YWx1ZSA9PSBudWxsKSA/ICcnIDogU3RyaW5nKCB2YWx1ZSApLnRyaW0oKTtcclxuXHRcdFx0dmFyIG0gPSB2YWx1ZS5tYXRjaCggL14oLT9cXGQrKD86XFwuXFxkKyk/KShweHwlfHJlbXxlbSkkL2kgKTtcclxuXHRcdFx0aWYgKCBtICkge1xyXG5cdFx0XHRcdHJldHVybiB7IHZhbHVlOiBwYXJzZUZsb2F0KCBtWzFdICksIHVuaXQ6IG1bMl0udG9Mb3dlckNhc2UoKSB9O1xyXG5cdFx0XHR9XHJcblx0XHRcdC8vIHBsYWluIG51bWJlciAtPiBhc3N1bWUgZmFsbGJhY2sgdW5pdFxyXG5cdFx0XHRpZiAoIHZhbHVlICE9PSAnJyAmJiAhaXNOYU4oIE51bWJlciggdmFsdWUgKSApICkge1xyXG5cdFx0XHRcdHJldHVybiB7IHZhbHVlOiBOdW1iZXIoIHZhbHVlICksIHVuaXQ6IChmYWxsYmFja191bml0IHx8ICdweCcpIH07XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIHsgdmFsdWU6IDAsIHVuaXQ6IChmYWxsYmFja191bml0IHx8ICdweCcpIH07XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqIENsYW1wIGhlbHBlci4gKi9cclxuXHRcdGZ1bmN0aW9uIGNsYW1wX251bSh2LCBtaW4sIG1heCkge1xyXG5cdFx0XHRpZiAoIHR5cGVvZiB2ICE9PSAnbnVtYmVyJyB8fCBpc05hTiggdiApICkgcmV0dXJuIChtaW4gIT0gbnVsbCA/IG1pbiA6IDApO1xyXG5cdFx0XHRpZiAoIG1pbiAhPSBudWxsICYmIHYgPCBtaW4gKSB2ID0gbWluO1xyXG5cdFx0XHRpZiAoIG1heCAhPSBudWxsICYmIHYgPiBtYXggKSB2ID0gbWF4O1xyXG5cdFx0XHRyZXR1cm4gdjtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBJbml0aWFsaXplIENvbG9yaXMgcGlja2VycyBpbiBhIGdpdmVuIHJvb3QuXHJcblx0XHQvLyBSZWxpZXMgb24gQ29sb3JpcyBiZWluZyBlbnF1ZXVlZCAoc2VlIGJmYi1ib290c3RyYXAucGhwKS5cclxuXHRcdGZ1bmN0aW9uIGluaXRfY29sb3Jpc19waWNrZXJzKHJvb3QpIHtcclxuXHRcdFx0aWYgKCAhcm9vdCB8fCAhdy5Db2xvcmlzICkgcmV0dXJuO1xyXG5cdFx0XHQvLyBNYXJrIGlucHV0cyB3ZSB3YW50IENvbG9yaXMgdG8gaGFuZGxlLlxyXG5cdFx0XHR2YXIgaW5wdXRzID0gcm9vdC5xdWVyeVNlbGVjdG9yQWxsKCAnaW5wdXRbZGF0YS1pbnNwZWN0b3ItdHlwZT1cImNvbG9yXCJdJyApO1xyXG5cdFx0XHRpZiAoICFpbnB1dHMubGVuZ3RoICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0Ly8gQWRkIGEgc3RhYmxlIGNsYXNzIGZvciBDb2xvcmlzIHRhcmdldGluZzsgYXZvaWQgZG91YmxlLWluaXRpYWxpemluZy5cclxuXHRcdFx0aW5wdXRzLmZvckVhY2goIGZ1bmN0aW9uIChpbnB1dCkge1xyXG5cdFx0XHRcdGlmICggaW5wdXQuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19iZmJfY29sb3JpcycgKSApIHJldHVybjtcclxuXHRcdFx0XHRpbnB1dC5jbGFzc0xpc3QuYWRkKCAnd3BiY19iZmJfY29sb3JpcycgKTtcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Ly8gQ3JlYXRlL3JlZnJlc2ggYSBDb2xvcmlzIGluc3RhbmNlIGJvdW5kIHRvIHRoZXNlIGlucHV0cy5cclxuXHRcdFx0Ly8gS2VlcCBIRVggb3V0cHV0IHRvIG1hdGNoIHNjaGVtYSBkZWZhdWx0cyAoZS5nLiwgXCIjZTBlMGUwXCIpLlxyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdHcuQ29sb3Jpcygge1xyXG5cdFx0XHRcdFx0ZWwgICAgICAgOiAnLndwYmNfYmZiX2NvbG9yaXMnLFxyXG5cdFx0XHRcdFx0YWxwaGEgICAgOiBmYWxzZSxcclxuXHRcdFx0XHRcdGZvcm1hdCAgIDogJ2hleCcsXHJcblx0XHRcdFx0XHR0aGVtZU1vZGU6ICdhdXRvJ1xyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0XHQvLyBDb2xvcmlzIGFscmVhZHkgZGlzcGF0Y2hlcyAnaW5wdXQnIGV2ZW50cyBvbiB2YWx1ZSBjaGFuZ2VzLlxyXG5cdFx0XHR9IGNhdGNoICggZSApIHtcclxuXHRcdFx0XHQvLyBOb24tZmF0YWw6IGlmIENvbG9yaXMgdGhyb3dzIChyYXJlKSwgdGhlIHRleHQgaW5wdXQgc3RpbGwgd29ya3MuXHJcblx0XHRcdFx0Y29uc29sZS53YXJuKCAnV1BCQyBJbnNwZWN0b3I6IENvbG9yaXMgaW5pdCBmYWlsZWQ6JywgZSApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBCdWlsZDogc2xpZGVyICsgbnVtYmVyIGluIG9uZSByb3cgKHdyaXRlcyB0byBhIHNpbmdsZSBkYXRhIGtleSkuXHJcblx0XHQgKiBDb250cm9sIG1ldGE6IHsgdHlwZToncmFuZ2VfbnVtYmVyJywga2V5LCBsYWJlbCwgbWluLCBtYXgsIHN0ZXAgfVxyXG5cdFx0ICovXHJcblx0XHRmdW5jdGlvbiBidWlsZF9yYW5nZV9udW1iZXJfcm93KGlucHV0X2lkLCBrZXksIGxhYmVsX3RleHQsIHZhbHVlLCBtZXRhKSB7XHJcblx0XHRcdHZhciByb3dfZWwgICA9IGVsKCdkaXYnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX3JvdycgfSk7XHJcblx0XHRcdHZhciBsYWJlbF9lbCA9IGVsKCdsYWJlbCcsIHsgJ2Zvcic6IGlucHV0X2lkLCAnY2xhc3MnOiAnaW5zcGVjdG9yX19sYWJlbCcgfSwgbGFiZWxfdGV4dCB8fCBrZXkgfHwgJycpO1xyXG5cdFx0XHR2YXIgY3RybCAgICAgPSBlbCgnZGl2JywgeyAnY2xhc3MnOiAnaW5zcGVjdG9yX19jb250cm9sJyB9KTtcclxuXHJcblx0XHRcdHZhciBtaW4gID0gKG1ldGEgJiYgbWV0YS5taW4gIT0gbnVsbCkgID8gbWV0YS5taW4gIDogMDtcclxuXHRcdFx0dmFyIG1heCAgPSAobWV0YSAmJiBtZXRhLm1heCAhPSBudWxsKSAgPyBtZXRhLm1heCAgOiAxMDA7XHJcblx0XHRcdHZhciBzdGVwID0gKG1ldGEgJiYgbWV0YS5zdGVwICE9IG51bGwpID8gbWV0YS5zdGVwIDogMTtcclxuXHJcblx0XHRcdHZhciBncm91cCA9IGVsKCdkaXYnLCB7ICdjbGFzcyc6ICd3cGJjX2xlbl9ncm91cCB3cGJjX2lubGluZV9pbnB1dHMnLCAnZGF0YS1sZW4tZ3JvdXAnOiBrZXkgfSk7XHJcblxyXG5cdFx0XHR2YXIgcmFuZ2UgPSBlbCgnaW5wdXQnLCB7XHJcblx0XHRcdFx0dHlwZSA6ICdyYW5nZScsXHJcblx0XHRcdFx0J2NsYXNzJzogJ2luc3BlY3Rvcl9faW5wdXQnLFxyXG5cdFx0XHRcdCdkYXRhLWxlbi1yYW5nZSc6ICcnLFxyXG5cdFx0XHRcdG1pbiAgOiBTdHJpbmcobWluKSxcclxuXHRcdFx0XHRtYXggIDogU3RyaW5nKG1heCksXHJcblx0XHRcdFx0c3RlcCA6IFN0cmluZyhzdGVwKSxcclxuXHRcdFx0XHR2YWx1ZTogU3RyaW5nKHZhbHVlID09IG51bGwgfHwgdmFsdWUgPT09ICcnID8gbWluIDogdmFsdWUpXHJcblx0XHRcdH0pO1xyXG5cclxuXHRcdFx0dmFyIG51bSA9IGVsKCdpbnB1dCcsIHtcclxuXHRcdFx0XHRpZCAgIDogaW5wdXRfaWQsXHJcblx0XHRcdFx0dHlwZSA6ICdudW1iZXInLFxyXG5cdFx0XHRcdCdjbGFzcyc6ICdpbnNwZWN0b3JfX2lucHV0IGluc3BlY3Rvcl9fd18zMCcsXHJcblx0XHRcdFx0J2RhdGEtbGVuLXZhbHVlJzogJycsXHJcblx0XHRcdFx0J2RhdGEtaW5zcGVjdG9yLWtleSc6IGtleSxcclxuXHRcdFx0XHRtaW4gIDogU3RyaW5nKG1pbiksXHJcblx0XHRcdFx0bWF4ICA6IFN0cmluZyhtYXgpLFxyXG5cdFx0XHRcdHN0ZXAgOiBTdHJpbmcoc3RlcCksXHJcblx0XHRcdFx0dmFsdWU6ICh2YWx1ZSA9PSBudWxsIHx8IHZhbHVlID09PSAnJykgPyBTdHJpbmcobWluKSA6IFN0cmluZyh2YWx1ZSlcclxuXHRcdFx0fSk7XHJcblxyXG5cdFx0XHRncm91cC5hcHBlbmRDaGlsZChyYW5nZSk7XHJcblx0XHRcdGdyb3VwLmFwcGVuZENoaWxkKG51bSk7XHJcblx0XHRcdGN0cmwuYXBwZW5kQ2hpbGQoZ3JvdXApO1xyXG5cdFx0XHRyb3dfZWwuYXBwZW5kQ2hpbGQobGFiZWxfZWwpO1xyXG5cdFx0XHRyb3dfZWwuYXBwZW5kQ2hpbGQoY3RybCk7XHJcblx0XHRcdHJldHVybiByb3dfZWw7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBCdWlsZDogKG51bWJlciArIHVuaXQpICsgc2xpZGVyLCB3cml0aW5nIGEgKnNpbmdsZSogY29tYmluZWQgc3RyaW5nIHRvIGBrZXlgLlxyXG5cdFx0ICogQ29udHJvbCBtZXRhOlxyXG5cdFx0ICoge1xyXG5cdFx0ICogICB0eXBlOidsZW4nLCBrZXksIGxhYmVsLCB1bml0czpbJ3B4JywnJScsJ3JlbScsJ2VtJ10sXHJcblx0XHQgKiAgIHNsaWRlcjogeyBweDp7bWluOjAsbWF4OjUxMixzdGVwOjF9LCAnJSc6e21pbjowLG1heDoxMDAsc3RlcDoxfSwgcmVtOnttaW46MCxtYXg6MTAsc3RlcDowLjF9LCBlbTp7Li4ufSB9LFxyXG5cdFx0ICogICBmYWxsYmFja191bml0OidweCdcclxuXHRcdCAqIH1cclxuXHRcdCAqL1xyXG5cdFx0ZnVuY3Rpb24gYnVpbGRfbGVuX2NvbXBvdW5kX3Jvdyhjb250cm9sLCBwcm9wc19zY2hlbWEsIGRhdGEsIHVpZCkge1xyXG5cdFx0XHR2YXIga2V5ICAgICAgICA9IGNvbnRyb2wua2V5O1xyXG5cdFx0XHR2YXIgbGFiZWxfdGV4dCA9IGNvbnRyb2wubGFiZWwgfHwga2V5IHx8ICcnO1xyXG5cdFx0XHR2YXIgZGVmX3N0ciAgICA9IGdldF9pbml0aWFsX3ZhbHVlKCBrZXksIGRhdGEsIHByb3BzX3NjaGVtYSApO1xyXG5cdFx0XHR2YXIgZmFsbGJhY2tfdSA9IGNvbnRyb2wuZmFsbGJhY2tfdW5pdCB8fCAncHgnO1xyXG5cdFx0XHR2YXIgcGFyc2VkICAgICA9IHBhcnNlX2xlbiggZGVmX3N0ciwgZmFsbGJhY2tfdSApO1xyXG5cclxuXHRcdFx0dmFyIHJvdyAgID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX3JvdycgfSApO1xyXG5cdFx0XHR2YXIgbGFiZWwgPSBlbCggJ2xhYmVsJywgeyAnY2xhc3MnOiAnaW5zcGVjdG9yX19sYWJlbCcgfSwgbGFiZWxfdGV4dCApO1xyXG5cdFx0XHR2YXIgY3RybCAgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ2luc3BlY3Rvcl9fY29udHJvbCcgfSApO1xyXG5cclxuXHRcdFx0dmFyIHVuaXRzICAgICAgPSBBcnJheS5pc0FycmF5KCBjb250cm9sLnVuaXRzICkgJiYgY29udHJvbC51bml0cy5sZW5ndGggPyBjb250cm9sLnVuaXRzIDogWyAncHgnLCAnJScsICdyZW0nLCAnZW0nIF07XHJcblx0XHRcdHZhciBzbGlkZXJfbWFwID0gY29udHJvbC5zbGlkZXIgfHwge1xyXG5cdFx0XHRcdCdweCcgOiB7IG1pbjogMCwgbWF4OiA1MTIsIHN0ZXA6IDEgfSxcclxuXHRcdFx0XHQnJScgIDogeyBtaW46IDAsIG1heDogMTAwLCBzdGVwOiAxIH0sXHJcblx0XHRcdFx0J3JlbSc6IHsgbWluOiAwLCBtYXg6IDEwLCBzdGVwOiAwLjEgfSxcclxuXHRcdFx0XHQnZW0nIDogeyBtaW46IDAsIG1heDogMTAsIHN0ZXA6IDAuMSB9XHJcblx0XHRcdH07XHJcblxyXG5cdFx0XHQvLyBIb3N0IHdpdGggYSBoaWRkZW4gaW5wdXQgdGhhdCBjYXJyaWVzIGRhdGEtaW5zcGVjdG9yLWtleSB0byByZXVzZSB0aGUgc3RhbmRhcmQgaGFuZGxlci5cclxuXHRcdFx0dmFyIGdyb3VwID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd3cGJjX2xlbl9ncm91cCcsICdkYXRhLWxlbi1ncm91cCc6IGtleSB9ICk7XHJcblxyXG5cdFx0XHR2YXIgaW5saW5lID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd3cGJjX2lubGluZV9pbnB1dHMnIH0gKTtcclxuXHJcblx0XHRcdHZhciBudW0gPSBlbCggJ2lucHV0Jywge1xyXG5cdFx0XHRcdHR5cGUgICAgICAgICAgICA6ICdudW1iZXInLFxyXG5cdFx0XHRcdCdjbGFzcycgICAgICAgICA6ICdpbnNwZWN0b3JfX2lucHV0JyxcclxuXHRcdFx0XHQnZGF0YS1sZW4tdmFsdWUnOiAnJyxcclxuXHRcdFx0XHRtaW4gICAgICAgICAgICAgOiAnMCcsXHJcblx0XHRcdFx0c3RlcCAgICAgICAgICAgIDogJ2FueScsXHJcblx0XHRcdFx0dmFsdWUgICAgICAgICAgIDogU3RyaW5nKCBwYXJzZWQudmFsdWUgKVxyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0XHR2YXIgc2VsID0gZWwoICdzZWxlY3QnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX2lucHV0JywgJ2RhdGEtbGVuLXVuaXQnOiAnJyB9ICk7XHJcblx0XHRcdHVuaXRzLmZvckVhY2goIGZ1bmN0aW9uICh1KSB7XHJcblx0XHRcdFx0dmFyIG9wdCA9IGVsKCAnb3B0aW9uJywgeyB2YWx1ZTogdSB9LCB1ICk7XHJcblx0XHRcdFx0aWYgKCB1ID09PSBwYXJzZWQudW5pdCApIG9wdC5zZXRBdHRyaWJ1dGUoICdzZWxlY3RlZCcsICdzZWxlY3RlZCcgKTtcclxuXHRcdFx0XHRzZWwuYXBwZW5kQ2hpbGQoIG9wdCApO1xyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0XHRpbmxpbmUuYXBwZW5kQ2hpbGQoIG51bSApO1xyXG5cdFx0XHRpbmxpbmUuYXBwZW5kQ2hpbGQoIHNlbCApO1xyXG5cclxuXHRcdFx0Ly8gU2xpZGVyICh1bml0LWF3YXJlKVxyXG5cdFx0XHR2YXIgY3VycmVudCA9IHNsaWRlcl9tYXBbcGFyc2VkLnVuaXRdIHx8IHNsaWRlcl9tYXBbdW5pdHNbMF1dO1xyXG5cdFx0XHR2YXIgcmFuZ2UgICA9IGVsKCAnaW5wdXQnLCB7XHJcblx0XHRcdFx0dHlwZSAgICAgICAgICAgIDogJ3JhbmdlJyxcclxuXHRcdFx0XHQnY2xhc3MnICAgICAgICAgOiAnaW5zcGVjdG9yX19pbnB1dCcsXHJcblx0XHRcdFx0J2RhdGEtbGVuLXJhbmdlJzogJycsXHJcblx0XHRcdFx0bWluICAgICAgICAgICAgIDogU3RyaW5nKCBjdXJyZW50Lm1pbiApLFxyXG5cdFx0XHRcdG1heCAgICAgICAgICAgICA6IFN0cmluZyggY3VycmVudC5tYXggKSxcclxuXHRcdFx0XHRzdGVwICAgICAgICAgICAgOiBTdHJpbmcoIGN1cnJlbnQuc3RlcCApLFxyXG5cdFx0XHRcdHZhbHVlICAgICAgICAgICA6IFN0cmluZyggY2xhbXBfbnVtKCBwYXJzZWQudmFsdWUsIGN1cnJlbnQubWluLCBjdXJyZW50Lm1heCApIClcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Ly8gSGlkZGVuIHdyaXRlciBpbnB1dCB0aGF0IHRoZSBkZWZhdWx0IEluc3BlY3RvciBoYW5kbGVyIHdpbGwgY2F0Y2guXHJcblx0XHRcdHZhciBoaWRkZW4gPSBlbCggJ2lucHV0Jywge1xyXG5cdFx0XHRcdHR5cGUgICAgICAgICAgICAgICAgOiAndGV4dCcsXHJcblx0XHRcdFx0J2NsYXNzJyAgICAgICAgICAgICA6ICdpbnNwZWN0b3JfX2lucHV0JyxcclxuXHRcdFx0XHRzdHlsZSAgICAgICAgICAgICAgIDogJ2Rpc3BsYXk6bm9uZScsXHJcblx0XHRcdFx0J2FyaWEtaGlkZGVuJyAgICAgICA6ICd0cnVlJyxcclxuXHRcdFx0XHR0YWJpbmRleCAgICAgICAgICAgIDogJy0xJyxcclxuXHRcdFx0XHRpZCAgICAgICAgICAgICAgICAgIDogJ3dwYmNfaW5zXycgKyBrZXkgKyAnXycgKyB1aWQgKyAnX2xlbl9oaWRkZW4nLFxyXG5cdFx0XHRcdCdkYXRhLWluc3BlY3Rvci1rZXknOiBrZXksXHJcblx0XHRcdFx0dmFsdWUgICAgICAgICAgICAgICA6IChTdHJpbmcoIHBhcnNlZC52YWx1ZSApICsgcGFyc2VkLnVuaXQpXHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdGdyb3VwLmFwcGVuZENoaWxkKCBpbmxpbmUgKTtcclxuXHRcdFx0Z3JvdXAuYXBwZW5kQ2hpbGQoIHJhbmdlICk7XHJcblx0XHRcdGdyb3VwLmFwcGVuZENoaWxkKCBoaWRkZW4gKTtcclxuXHJcblx0XHRcdGN0cmwuYXBwZW5kQ2hpbGQoIGdyb3VwICk7XHJcblx0XHRcdHJvdy5hcHBlbmRDaGlsZCggbGFiZWwgKTtcclxuXHRcdFx0cm93LmFwcGVuZENoaWxkKCBjdHJsICk7XHJcblx0XHRcdHJldHVybiByb3c7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBXaXJlIHN5bmNpbmcgZm9yIGFueSAud3BiY19sZW5fZ3JvdXAgaW5zaWRlIGEgZ2l2ZW4gcm9vdCAocGFuZWwpLlxyXG5cdFx0ICogLSByYW5nZSDih4QgbnVtYmVyIHN5bmNcclxuXHRcdCAqIC0gdW5pdCBzd2l0Y2hlcyB1cGRhdGUgc2xpZGVyIGJvdW5kc1xyXG5cdFx0ICogLSBoaWRkZW4gd3JpdGVyIChpZiBwcmVzZW50KSBnZXRzIHVwZGF0ZWQgYW5kIGVtaXRzICdpbnB1dCdcclxuXHRcdCAqL1xyXG5cdFx0ZnVuY3Rpb24gd2lyZV9sZW5fZ3JvdXAocm9vdCkge1xyXG5cdFx0XHRpZiAoICFyb290ICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0ZnVuY3Rpb24gZmluZF9ncm91cChlbCkge1xyXG5cdFx0XHRcdHJldHVybiBlbCAmJiBlbC5jbG9zZXN0ICYmIGVsLmNsb3Nlc3QoICcud3BiY19sZW5fZ3JvdXAnICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHJvb3QuYWRkRXZlbnRMaXN0ZW5lciggJ2lucHV0JywgZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHR2YXIgdCA9IGUudGFyZ2V0O1xyXG5cdFx0XHRcdC8vIFNsaWRlciBtb3ZlZCAtPiB1cGRhdGUgbnVtYmVyIChhbmQgd3JpdGVyL2hpZGRlbilcclxuXHRcdFx0XHRpZiAoIHQgJiYgdC5oYXNBdHRyaWJ1dGUoICdkYXRhLWxlbi1yYW5nZScgKSApIHtcclxuXHRcdFx0XHRcdHZhciBnID0gZmluZF9ncm91cCggdCApO1xyXG5cdFx0XHRcdFx0aWYgKCAhZyApIHJldHVybjtcclxuXHRcdFx0XHRcdHZhciBudW0gPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tdmFsdWVdJyApO1xyXG5cdFx0XHRcdFx0aWYgKCBudW0gKSB7XHJcblx0XHRcdFx0XHRcdG51bS52YWx1ZSA9IHQudmFsdWU7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR2YXIgd3JpdGVyID0gZy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtaW5zcGVjdG9yLWtleV0nICk7XHJcblx0XHRcdFx0XHRpZiAoIHdyaXRlciAmJiB3cml0ZXIudHlwZSA9PT0gJ3RleHQnICkge1xyXG5cdFx0XHRcdFx0XHR2YXIgdW5pdCAgICAgPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tdW5pdF0nICk7XHJcblx0XHRcdFx0XHRcdHVuaXQgICAgICAgICA9IHVuaXQgPyB1bml0LnZhbHVlIDogJ3B4JztcclxuXHRcdFx0XHRcdFx0d3JpdGVyLnZhbHVlID0gU3RyaW5nKCB0LnZhbHVlICkgKyBTdHJpbmcoIHVuaXQgKTtcclxuXHRcdFx0XHRcdFx0Ly8gdHJpZ2dlciBzdGFuZGFyZCBpbnNwZWN0b3IgaGFuZGxlcjpcclxuXHRcdFx0XHRcdFx0d3JpdGVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdFx0Ly8gUGxhaW4gcmFuZ2VfbnVtYmVyIGNhc2UgKG51bWJlciBoYXMgZGF0YS1pbnNwZWN0b3Ita2V5KSAtPiBmaXJlIGlucHV0IG9uIG51bWJlclxyXG5cdFx0XHRcdFx0XHRpZiAoIG51bSAmJiBudW0uaGFzQXR0cmlidXRlKCAnZGF0YS1pbnNwZWN0b3Ita2V5JyApICkge1xyXG5cdFx0XHRcdFx0XHRcdG51bS5kaXNwYXRjaEV2ZW50KCBuZXcgRXZlbnQoICdpbnB1dCcsIHsgYnViYmxlczogdHJ1ZSB9ICkgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Ly8gTnVtYmVyIHR5cGVkIC0+IHVwZGF0ZSBzbGlkZXIgYW5kIHdyaXRlci9oaWRkZW5cclxuXHRcdFx0XHRpZiAoIHQgJiYgdC5oYXNBdHRyaWJ1dGUoICdkYXRhLWxlbi12YWx1ZScgKSApIHtcclxuXHRcdFx0XHRcdHZhciBnID0gZmluZF9ncm91cCggdCApO1xyXG5cdFx0XHRcdFx0aWYgKCAhZyApIHJldHVybjtcclxuXHRcdFx0XHRcdHZhciByID0gZy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtbGVuLXJhbmdlXScgKTtcclxuXHRcdFx0XHRcdGlmICggciApIHtcclxuXHRcdFx0XHRcdFx0Ly8gY2xhbXAgd2l0aGluIHNsaWRlciBib3VuZHMgaWYgcHJlc2VudFxyXG5cdFx0XHRcdFx0XHR2YXIgbWluID0gTnVtYmVyKCByLm1pbiApO1xyXG5cdFx0XHRcdFx0XHR2YXIgbWF4ID0gTnVtYmVyKCByLm1heCApO1xyXG5cdFx0XHRcdFx0XHR2YXIgdiAgID0gTnVtYmVyKCB0LnZhbHVlICk7XHJcblx0XHRcdFx0XHRcdGlmICggIWlzTmFOKCB2ICkgKSB7XHJcblx0XHRcdFx0XHRcdFx0diAgICAgICA9IGNsYW1wX251bSggdiwgaXNOYU4oIG1pbiApID8gdW5kZWZpbmVkIDogbWluLCBpc05hTiggbWF4ICkgPyB1bmRlZmluZWQgOiBtYXggKTtcclxuXHRcdFx0XHRcdFx0XHRyLnZhbHVlID0gU3RyaW5nKCB2ICk7XHJcblx0XHRcdFx0XHRcdFx0aWYgKCBTdHJpbmcoIHYgKSAhPT0gdC52YWx1ZSApIHQudmFsdWUgPSBTdHJpbmcoIHYgKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0dmFyIHdyaXRlciA9IGcucXVlcnlTZWxlY3RvciggJ1tkYXRhLWluc3BlY3Rvci1rZXldJyApO1xyXG5cdFx0XHRcdFx0aWYgKCB3cml0ZXIgJiYgd3JpdGVyLnR5cGUgPT09ICd0ZXh0JyApIHtcclxuXHRcdFx0XHRcdFx0dmFyIHVuaXQgICAgID0gZy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtbGVuLXVuaXRdJyApO1xyXG5cdFx0XHRcdFx0XHR1bml0ICAgICAgICAgPSB1bml0ID8gdW5pdC52YWx1ZSA6ICdweCc7XHJcblx0XHRcdFx0XHRcdHdyaXRlci52YWx1ZSA9IFN0cmluZyggdC52YWx1ZSB8fCAwICkgKyBTdHJpbmcoIHVuaXQgKTtcclxuXHRcdFx0XHRcdFx0d3JpdGVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0Ly8gZWxzZTogbnVtYmVyIGl0c2VsZiBsaWtlbHkgY2FycmllcyBkYXRhLWluc3BlY3Rvci1rZXkgKHJhbmdlX251bWJlcik7IGRlZmF1bHQgaGFuZGxlciB3aWxsIHJ1bi5cclxuXHRcdFx0XHR9XHJcblx0XHRcdH0sIHRydWUgKTtcclxuXHJcblx0XHRcdHJvb3QuYWRkRXZlbnRMaXN0ZW5lciggJ2NoYW5nZScsIGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdFx0dmFyIHQgPSBlLnRhcmdldDtcclxuXHRcdFx0XHQvLyBVbml0IGNoYW5nZWQgLT4gdXBkYXRlIHNsaWRlciBsaW1pdHMgYW5kIHdyaXRlci9oaWRkZW5cclxuXHRcdFx0XHRpZiAoIHQgJiYgdC5oYXNBdHRyaWJ1dGUoICdkYXRhLWxlbi11bml0JyApICkge1xyXG5cdFx0XHRcdFx0dmFyIGcgPSBmaW5kX2dyb3VwKCB0ICk7XHJcblx0XHRcdFx0XHRpZiAoICFnICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0XHRcdC8vIEZpbmQgdGhlIGNvbnRyb2wgbWV0YSB2aWEgYSBkYXRhIGF0dHJpYnV0ZSBvbiBncm91cCBpZiBwcm92aWRlZFxyXG5cdFx0XHRcdFx0Ly8gKEZhY3RvcnkgcGF0aCBzZXRzIG5vdGhpbmcgaGVyZTsgd2UgcmUtZGVyaXZlIGZyb20gY3VycmVudCBzbGlkZXIgYm91bmRzLilcclxuXHRcdFx0XHRcdHZhciByICAgICAgPSBnLnF1ZXJ5U2VsZWN0b3IoICdbZGF0YS1sZW4tcmFuZ2VdJyApO1xyXG5cdFx0XHRcdFx0dmFyIG51bSAgICA9IGcucXVlcnlTZWxlY3RvciggJ1tkYXRhLWxlbi12YWx1ZV0nICk7XHJcblx0XHRcdFx0XHR2YXIgd3JpdGVyID0gZy5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtaW5zcGVjdG9yLWtleV0nICk7XHJcblx0XHRcdFx0XHR2YXIgdW5pdCAgID0gdC52YWx1ZSB8fCAncHgnO1xyXG5cclxuXHRcdFx0XHRcdC8vIEFkanVzdCBzbGlkZXIgYm91bmRzIGhldXJpc3RpY2FsbHkgKG1hdGNoIEZhY3RvcnkgZGVmYXVsdHMpXHJcblx0XHRcdFx0XHR2YXIgYm91bmRzX2J5X3VuaXQgPSB7XHJcblx0XHRcdFx0XHRcdCdweCcgOiB7IG1pbjogMCwgbWF4OiA1MTIsIHN0ZXA6IDEgfSxcclxuXHRcdFx0XHRcdFx0JyUnICA6IHsgbWluOiAwLCBtYXg6IDEwMCwgc3RlcDogMSB9LFxyXG5cdFx0XHRcdFx0XHQncmVtJzogeyBtaW46IDAsIG1heDogMTAsIHN0ZXA6IDAuMSB9LFxyXG5cdFx0XHRcdFx0XHQnZW0nIDogeyBtaW46IDAsIG1heDogMTAsIHN0ZXA6IDAuMSB9XHJcblx0XHRcdFx0XHR9O1xyXG5cdFx0XHRcdFx0aWYgKCByICkge1xyXG5cdFx0XHRcdFx0XHR2YXIgYiAgPSBib3VuZHNfYnlfdW5pdFt1bml0XSB8fCBib3VuZHNfYnlfdW5pdFsncHgnXTtcclxuXHRcdFx0XHRcdFx0ci5taW4gID0gU3RyaW5nKCBiLm1pbiApO1xyXG5cdFx0XHRcdFx0XHRyLm1heCAgPSBTdHJpbmcoIGIubWF4ICk7XHJcblx0XHRcdFx0XHRcdHIuc3RlcCA9IFN0cmluZyggYi5zdGVwICk7XHJcblx0XHRcdFx0XHRcdC8vIGNsYW1wIHRvIG5ldyBib3VuZHNcclxuXHRcdFx0XHRcdFx0dmFyIHYgID0gTnVtYmVyKCBudW0gJiYgbnVtLnZhbHVlID8gbnVtLnZhbHVlIDogci52YWx1ZSApO1xyXG5cdFx0XHRcdFx0XHRpZiAoICFpc05hTiggdiApICkge1xyXG5cdFx0XHRcdFx0XHRcdHYgICAgICAgPSBjbGFtcF9udW0oIHYsIGIubWluLCBiLm1heCApO1xyXG5cdFx0XHRcdFx0XHRcdHIudmFsdWUgPSBTdHJpbmcoIHYgKTtcclxuXHRcdFx0XHRcdFx0XHRpZiAoIG51bSApIG51bS52YWx1ZSA9IFN0cmluZyggdiApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRpZiAoIHdyaXRlciAmJiB3cml0ZXIudHlwZSA9PT0gJ3RleHQnICkge1xyXG5cdFx0XHRcdFx0XHR2YXIgdiAgICAgICAgPSBudW0gJiYgbnVtLnZhbHVlID8gbnVtLnZhbHVlIDogKHIgPyByLnZhbHVlIDogJzAnKTtcclxuXHRcdFx0XHRcdFx0d3JpdGVyLnZhbHVlID0gU3RyaW5nKCB2ICkgKyBTdHJpbmcoIHVuaXQgKTtcclxuXHRcdFx0XHRcdFx0d3JpdGVyLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0gKSApO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSwgdHJ1ZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHRcdC8vID09ICBDIE8gTiBUIFIgTyBMICA9PVxyXG5cdFx0Ly8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG5cclxuXHRcdC8qKlxyXG5cdCAqIFNjaGVtYSA+IEluc3BlY3RvciA+IENvbnRyb2wgRWxlbWVudCwgZS5nLiBJbnB1dCEgIEJ1aWxkIGEgc2luZ2xlIGNvbnRyb2wgcm93OlxyXG5cdCAqIDxkaXYgY2xhc3M9XCJpbnNwZWN0b3JfX3Jvd1wiPlxyXG5cdCAqICAgPGxhYmVsIGNsYXNzPVwiaW5zcGVjdG9yX19sYWJlbFwiIGZvcj1cIi4uLlwiPkxhYmVsPC9sYWJlbD5cclxuXHQgKiAgIDxkaXYgY2xhc3M9XCJpbnNwZWN0b3JfX2NvbnRyb2xcIj48aW5wdXR8dGV4dGFyZWF8c2VsZWN0IGNsYXNzPVwiaW5zcGVjdG9yX19pbnB1dFwiIC4uLj48L2Rpdj5cclxuXHQgKiA8L2Rpdj5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7T2JqZWN0fSBjb250cm9sICAgICAgICAgICAtIHNjaGVtYSBjb250cm9sIG1ldGEgKHt0eXBlLGtleSxsYWJlbCwuLi59KVxyXG5cdCAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wc19zY2hlbWEgICAgICAtIHNjaGVtYS5wcm9wc1xyXG5cdCAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhICAgICAgICAgICAgICAtIGN1cnJlbnQgZWxlbWVudCBkYXRhLSogbWFwXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9IHVpZCAgICAgICAgICAgICAgIC0gdW5pcXVlIHN1ZmZpeCBmb3IgaW5wdXQgaWRzXHJcblx0ICogQHBhcmFtIHtPYmplY3R9IGN0eCAgICAgICAgICAgICAgIC0geyBlbCwgYnVpbGRlciwgdHlwZSwgZGF0YSB9XHJcblx0ICogQHJldHVybnMge0hUTUxFbGVtZW50fVxyXG5cdCAqL1xyXG5cdFx0ZnVuY3Rpb24gYnVpbGRfY29udHJvbChjb250cm9sLCBwcm9wc19zY2hlbWEsIGRhdGEsIHVpZCwgY3R4KSB7XHJcblx0XHRcdHZhciB0eXBlID0gY29udHJvbC50eXBlO1xyXG5cdFx0XHR2YXIga2V5ICA9IGNvbnRyb2wua2V5O1xyXG5cclxuXHRcdFx0dmFyIGxhYmVsX3RleHQgPSBjb250cm9sLmxhYmVsIHx8IGtleSB8fCAnJztcclxuXHRcdFx0dmFyIHByb3BfbWV0YSAgPSAoa2V5ID8gKHByb3BzX3NjaGVtYVtrZXldIHx8IHsgdHlwZTogJ3N0cmluZycgfSkgOiB7IHR5cGU6ICdzdHJpbmcnIH0pO1xyXG5cdFx0XHR2YXIgdmFsdWUgICAgICA9IGNvZXJjZV9ieV90eXBlKCBnZXRfaW5pdGlhbF92YWx1ZSgga2V5LCBkYXRhLCBwcm9wc19zY2hlbWEgKSwgcHJvcF9tZXRhLnR5cGUgKTtcclxuXHRcdC8vIEFsbG93IHZhbHVlX2Zyb20gb3ZlcnJpZGUgKGNvbXB1dGVkIGF0IHJlbmRlci10aW1lKS5cclxuXHRcdGlmICggY29udHJvbCAmJiBjb250cm9sLnZhbHVlX2Zyb20gJiYgdy53cGJjX2JmYl9pbnNwZWN0b3JfZmFjdG9yeV92YWx1ZV9mcm9tW2NvbnRyb2wudmFsdWVfZnJvbV0gKSB7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHZhciBjb21wdXRlZCA9IHcud3BiY19iZmJfaW5zcGVjdG9yX2ZhY3RvcnlfdmFsdWVfZnJvbVtjb250cm9sLnZhbHVlX2Zyb21dKCBjdHggfHwge30gKTtcclxuXHRcdFx0XHRcdHZhbHVlICAgICAgICA9IGNvZXJjZV9ieV90eXBlKCBjb21wdXRlZCwgcHJvcF9tZXRhLnR5cGUgKTtcclxuXHRcdFx0XHR9IGNhdGNoICggZSApIHtcclxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ3ZhbHVlX2Zyb20gZmFpbGVkIGZvcicsIGNvbnRyb2wudmFsdWVfZnJvbSwgZSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dmFyIGlucHV0X2lkID0gJ3dwYmNfaW5zXycgKyBrZXkgKyAnXycgKyB1aWQ7XHJcblxyXG5cdFx0XHR2YXIgcm93X2VsICAgID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX3JvdycgfSApO1xyXG5cdFx0XHR2YXIgbGFiZWxfZWwgID0gZWwoICdsYWJlbCcsIHsgJ2Zvcic6IGlucHV0X2lkLCAnY2xhc3MnOiAnaW5zcGVjdG9yX19sYWJlbCcgfSwgbGFiZWxfdGV4dCApO1xyXG5cdFx0XHR2YXIgY3RybF93cmFwID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX2NvbnRyb2wnIH0gKTtcclxuXHJcblx0XHRcdHZhciBmaWVsZF9lbDtcclxuXHJcblx0XHQvLyAtLS0gc2xvdCBob3N0IChuYW1lZCBVSSBpbmplY3Rpb24pIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHRpZiAoIHR5cGUgPT09ICdzbG90JyAmJiBjb250cm9sLnNsb3QgKSB7XHJcblx0XHRcdC8vIGFkZCBhIG1hcmtlciBjbGFzcyBmb3IgdGhlIGxheW91dCBjaGlwcyByb3dcclxuXHRcdFx0dmFyIGNsYXNzZXMgPSAnaW5zcGVjdG9yX19yb3cgaW5zcGVjdG9yX19yb3ctLXNsb3QnO1xyXG5cdFx0XHRpZiAoIGNvbnRyb2wuc2xvdCA9PT0gJ2xheW91dF9jaGlwcycgKSBjbGFzc2VzICs9ICcgaW5zcGVjdG9yX19yb3ctLWxheW91dC1jaGlwcyc7XHJcblxyXG5cdFx0XHR2YXIgc2xvdF9yb3cgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogY2xhc3NlcyB9ICk7XHJcblxyXG5cdFx0XHRpZiAoIGxhYmVsX3RleHQgKSBzbG90X3Jvdy5hcHBlbmRDaGlsZCggZWwoICdsYWJlbCcsIHsgJ2NsYXNzJzogJ2luc3BlY3Rvcl9fbGFiZWwnIH0sIGxhYmVsX3RleHQgKSApO1xyXG5cclxuXHRcdFx0Ly8gYWRkIGEgZGF0YSBhdHRyaWJ1dGUgb24gdGhlIGhvc3Qgc28gYm90aCBDU1MgYW5kIHRoZSBzYWZldHktbmV0IGNhbiB0YXJnZXQgaXRcclxuXHRcdFx0dmFyIGhvc3RfYXR0cnMgPSB7ICdjbGFzcyc6ICdpbnNwZWN0b3JfX2NvbnRyb2wnIH07XHJcblx0XHRcdGlmICggY29udHJvbC5zbG90ID09PSAnbGF5b3V0X2NoaXBzJyApIGhvc3RfYXR0cnNbJ2RhdGEtYmZiLXNsb3QnXSA9ICdsYXlvdXRfY2hpcHMnO1xyXG5cclxuXHRcdFx0dmFyIHNsb3RfaG9zdCA9IGVsKCAnZGl2JywgaG9zdF9hdHRycyApO1xyXG5cdFx0XHRzbG90X3Jvdy5hcHBlbmRDaGlsZCggc2xvdF9ob3N0ICk7XHJcblxyXG5cdFx0XHR2YXIgc2xvdF9mbiA9IHcud3BiY19iZmJfaW5zcGVjdG9yX2ZhY3Rvcnlfc2xvdHNbY29udHJvbC5zbG90XTtcclxuXHRcdFx0aWYgKCB0eXBlb2Ygc2xvdF9mbiA9PT0gJ2Z1bmN0aW9uJyApIHtcclxuXHRcdFx0XHRzZXRUaW1lb3V0KCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0XHRzbG90X2ZuKCBzbG90X2hvc3QsIGN0eCB8fCB7fSApO1xyXG5cdFx0XHRcdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ3Nsb3QgXCInICsgY29udHJvbC5zbG90ICsgJ1wiIGZhaWxlZDonLCBlICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSwgMCApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHNsb3RfaG9zdC5hcHBlbmRDaGlsZCggZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd3cGJjX2JmYl9fc2xvdF9fbWlzc2luZycgfSwgJ1tzbG90OiAnICsgY29udHJvbC5zbG90ICsgJ10nICkgKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gc2xvdF9yb3c7XHJcblx0XHR9XHJcblxyXG5cclxuXHRcdFx0aWYgKCB0eXBlID09PSAndGV4dGFyZWEnICkge1xyXG5cdFx0XHRcdGZpZWxkX2VsID0gZWwoICd0ZXh0YXJlYScsIHtcclxuXHRcdFx0XHRcdGlkICAgICAgICAgICAgICAgICAgOiBpbnB1dF9pZCxcclxuXHRcdFx0XHRcdCdkYXRhLWluc3BlY3Rvci1rZXknOiBrZXksXHJcblx0XHRcdFx0XHRyb3dzICAgICAgICAgICAgICAgIDogY29udHJvbC5yb3dzIHx8IDMsXHJcblx0XHRcdFx0XHQnY2xhc3MnICAgICAgICAgICAgIDogJ2luc3BlY3Rvcl9faW5wdXQnXHJcblx0XHRcdFx0fSwgKHZhbHVlID09IG51bGwgPyAnJyA6IFN0cmluZyggdmFsdWUgKSkgKTtcclxuXHRcdFx0fSBlbHNlIGlmICggdHlwZSA9PT0gJ3NlbGVjdCcgKSB7XHJcblx0XHRcdFx0ZmllbGRfZWwgPSBlbCggJ3NlbGVjdCcsIHtcclxuXHRcdFx0XHRcdGlkICAgICAgICAgICAgICAgICAgOiBpbnB1dF9pZCxcclxuXHRcdFx0XHRcdCdkYXRhLWluc3BlY3Rvci1rZXknOiBrZXksXHJcblx0XHRcdFx0XHQnY2xhc3MnICAgICAgICAgICAgIDogJ2luc3BlY3Rvcl9faW5wdXQnXHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdG5vcm1hbGl6ZV9zZWxlY3Rfb3B0aW9ucyggY29udHJvbC5vcHRpb25zIHx8IFtdICkuZm9yRWFjaCggZnVuY3Rpb24gKG9wdCkge1xyXG5cdFx0XHRcdFx0dmFyIG9wdF9lbCA9IGVsKCAnb3B0aW9uJywgeyB2YWx1ZTogb3B0LnZhbHVlIH0sIG9wdC5sYWJlbCApO1xyXG5cdFx0XHRcdFx0aWYgKCBTdHJpbmcoIHZhbHVlICkgPT09IG9wdC52YWx1ZSApIG9wdF9lbC5zZXRBdHRyaWJ1dGUoICdzZWxlY3RlZCcsICdzZWxlY3RlZCcgKTtcclxuXHRcdFx0XHRcdGZpZWxkX2VsLmFwcGVuZENoaWxkKCBvcHRfZWwgKTtcclxuXHRcdFx0XHR9ICk7XHJcblx0XHRcdH0gZWxzZSBpZiAoIHR5cGUgPT09ICdjaGVja2JveCcgKSB7XHJcblx0XHRcdFx0Ly8gZmllbGRfZWwgPSBlbCggJ2lucHV0JywgeyBpZDogaW5wdXRfaWQsIHR5cGU6ICdjaGVja2JveCcsICdkYXRhLWluc3BlY3Rvci1rZXknOiBrZXksIGNoZWNrZWQ6ICEhdmFsdWUsICdjbGFzcyc6ICdpbnNwZWN0b3JfX2lucHV0JyB9ICk7IC8vLlxyXG5cclxuXHRcdFx0XHQvLyBSZW5kZXIgYXMgdG9nZ2xlIFVJIGluc3RlYWQgb2YgbGFiZWwtbGVmdCArIGNoZWNrYm94LiAgTm90ZTogd2UgcmV0dXJuIHRoZSBmdWxsIHRvZ2dsZSByb3cgaGVyZSBhbmQgc2tpcCB0aGUgZGVmYXVsdCByb3cvbGFiZWwgZmxvdyBiZWxvdy5cclxuXHRcdFx0XHRyZXR1cm4gYnVpbGRfdG9nZ2xlX3JvdyggaW5wdXRfaWQsIGtleSwgISF2YWx1ZSwgbGFiZWxfdGV4dCApO1xyXG5cclxuXHRcdFx0fSBlbHNlIGlmICggdHlwZSA9PT0gJ3JhbmdlX251bWJlcicgKSB7XHJcblx0XHRcdFx0Ly8gLS0tIG5ldzogc2xpZGVyICsgbnVtYmVyIChzaW5nbGUga2V5KS5cclxuXHRcdFx0XHR2YXIgcm5faWQgID0gJ3dwYmNfaW5zXycgKyBrZXkgKyAnXycgKyB1aWQ7XHJcblx0XHRcdFx0dmFyIHJuX3ZhbCA9IHZhbHVlOyAvLyBmcm9tIGdldF9pbml0aWFsX3ZhbHVlL3Byb3BfbWV0YSBhbHJlYWR5LlxyXG5cdFx0XHRcdHJldHVybiBidWlsZF9yYW5nZV9udW1iZXJfcm93KCBybl9pZCwga2V5LCBsYWJlbF90ZXh0LCBybl92YWwsIGNvbnRyb2wgKTtcclxuXHJcblx0XHRcdH0gZWxzZSBpZiAoIHR5cGUgPT09ICdsZW4nICkge1xyXG5cdFx0XHRcdC8vIC0tLSBuZXc6IGxlbmd0aCBjb21wb3VuZCAodmFsdWUrdW5pdCtzbGlkZXIgLT4gd3JpdGVzIGEgc2luZ2xlIHN0cmluZyBrZXkpLlxyXG5cdFx0XHRcdHJldHVybiBidWlsZF9sZW5fY29tcG91bmRfcm93KCBjb250cm9sLCBwcm9wc19zY2hlbWEsIGRhdGEsIHVpZCApO1xyXG5cclxuXHRcdFx0fSBlbHNlIGlmICggdHlwZSA9PT0gJ2NvbG9yJyApIHtcclxuXHRcdFx0XHQvLyBDb2xvciBwaWNrZXIgKENvbG9yaXMpLiBTdG9yZSBhcyBzdHJpbmcgKGUuZy4sIFwiI2UwZTBlMFwiKS5cclxuXHRcdFx0XHRmaWVsZF9lbCA9IGVsKCAnaW5wdXQnLCB7XHJcblx0XHRcdFx0XHRpZCAgICAgICAgICAgICAgICAgICA6IGlucHV0X2lkLFxyXG5cdFx0XHRcdFx0dHlwZSAgICAgICAgICAgICAgICAgOiAndGV4dCcsXHJcblx0XHRcdFx0XHQnZGF0YS1pbnNwZWN0b3Ita2V5JyA6IGtleSxcclxuXHRcdFx0XHRcdCdkYXRhLWluc3BlY3Rvci10eXBlJzogJ2NvbG9yJyxcclxuXHRcdFx0XHRcdCdkYXRhLWNvbG9yaXMnICAgICAgIDogJycsXHJcblx0XHRcdFx0XHQnY2xhc3MnICAgICAgICAgICAgICA6ICdpbnNwZWN0b3JfX2lucHV0JyxcclxuXHRcdFx0XHRcdCdkYXRhLWRlZmF1bHQtY29sb3InIDogKCB2YWx1ZSAhPSBudWxsICYmIHZhbHVlICE9PSAnJyA/IFN0cmluZyh2YWx1ZSkgOiAoY29udHJvbC5wbGFjZWhvbGRlciB8fCAnJykgKVxyXG5cdFx0XHRcdH0gKTtcclxuXHRcdFx0XHRpZiAoIHZhbHVlICE9PSAnJyApIHtcclxuXHRcdFx0XHRcdGZpZWxkX2VsLnZhbHVlID0gU3RyaW5nKCB2YWx1ZSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHQvLyB0ZXh0L251bWJlciBkZWZhdWx0LlxyXG5cdFx0XHRcdHZhciBhdHRycyA9IHtcclxuXHRcdFx0XHRcdGlkICAgICAgICAgICAgICAgICAgOiBpbnB1dF9pZCxcclxuXHRcdFx0XHRcdHR5cGUgICAgICAgICAgICAgICAgOiAodHlwZSA9PT0gJ251bWJlcicpID8gJ251bWJlcicgOiAndGV4dCcsXHJcblx0XHRcdFx0XHQnZGF0YS1pbnNwZWN0b3Ita2V5Jzoga2V5LFxyXG5cdFx0XHRcdFx0J2NsYXNzJyAgICAgICAgICAgICA6ICdpbnNwZWN0b3JfX2lucHV0J1xyXG5cdFx0XHRcdH07XHJcblx0XHRcdC8vIG51bWJlciBjb25zdHJhaW50cyAoc2NoZW1hIG9yIGNvbnRyb2wpXHJcblx0XHRcdFx0aWYgKCB0eXBlID09PSAnbnVtYmVyJyApIHtcclxuXHRcdFx0XHRcdGlmICggT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKCBwcm9wX21ldGEsICdtaW4nICkgKSBhdHRycy5taW4gPSBwcm9wX21ldGEubWluO1xyXG5cdFx0XHRcdFx0aWYgKCBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoIHByb3BfbWV0YSwgJ21heCcgKSApIGF0dHJzLm1heCA9IHByb3BfbWV0YS5tYXg7XHJcblx0XHRcdFx0XHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggcHJvcF9tZXRhLCAnc3RlcCcgKSApIGF0dHJzLnN0ZXAgPSBwcm9wX21ldGEuc3RlcDtcclxuXHRcdFx0XHRcdGlmICggT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKCBjb250cm9sLCAnbWluJyApICkgYXR0cnMubWluID0gY29udHJvbC5taW47XHJcblx0XHRcdFx0XHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggY29udHJvbCwgJ21heCcgKSApIGF0dHJzLm1heCA9IGNvbnRyb2wubWF4O1xyXG5cdFx0XHRcdFx0aWYgKCBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoIGNvbnRyb2wsICdzdGVwJyApICkgYXR0cnMuc3RlcCA9IGNvbnRyb2wuc3RlcDtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0ZmllbGRfZWwgPSBlbCggJ2lucHV0JywgYXR0cnMgKTtcclxuXHRcdFx0XHRpZiAoIHZhbHVlICE9PSAnJyApIGZpZWxkX2VsLnZhbHVlID0gU3RyaW5nKCB2YWx1ZSApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjdHJsX3dyYXAuYXBwZW5kQ2hpbGQoIGZpZWxkX2VsICk7XHJcblx0XHRcdHJvd19lbC5hcHBlbmRDaGlsZCggbGFiZWxfZWwgKTtcclxuXHRcdFx0cm93X2VsLmFwcGVuZENoaWxkKCBjdHJsX3dyYXAgKTtcclxuXHRcdFx0cmV0dXJuIHJvd19lbDtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIFNjaGVtYSA+IEluc3BlY3RvciA+IEdyb3VwcyEgQnVpbGQgYW4gaW5zcGVjdG9yIGdyb3VwIChjb2xsYXBzaWJsZSkuXHJcblx0XHQgKiBTdHJ1Y3R1cmU6XHJcblx0XHQgKiA8c2VjdGlvbiBjbGFzcz1cIndwYmNfYmZiX19pbnNwZWN0b3JfX2dyb3VwIHdwYmNfdWlfX2NvbGxhcHNpYmxlX2dyb3VwIGlzLW9wZW5cIiBkYXRhLWdyb3VwPVwiLi4uXCI+XHJcblx0XHQgKiAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiZ3JvdXBfX2hlYWRlclwiIHJvbGU9XCJidXR0b25cIiBhcmlhLWV4cGFuZGVkPVwidHJ1ZVwiIGFyaWEtY29udHJvbHM9XCJ3cGJjX2NvbGxhcHNpYmxlX3BhbmVsX1hcIj5cclxuXHRcdCAqICAgICA8aDM+R3JvdXAgVGl0bGU8L2gzPlxyXG5cdFx0ICogICAgIDxpIGNsYXNzPVwid3BiY191aV9lbF9fdmVydF9tZW51X3Jvb3Rfc2VjdGlvbl9pY29uIG1lbnVfaWNvbiBpY29uLTF4IHdwYmMtYmktY2hldnJvbi1yaWdodFwiPjwvaT5cclxuXHRcdCAqICAgPC9idXR0b24+XHJcblx0XHQgKiAgIDxkaXYgY2xhc3M9XCJncm91cF9fZmllbGRzXCIgaWQ9XCJ3cGJjX2NvbGxhcHNpYmxlX3BhbmVsX1hcIiBhcmlhLWhpZGRlbj1cImZhbHNlXCI+IOKApnJvd3PigKYgPC9kaXY+XHJcblx0XHQgKiA8L3NlY3Rpb24+XHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtPYmplY3R9IGdyb3VwXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gcHJvcHNfc2NoZW1hXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gZGF0YVxyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IHVpZFxyXG5cdFx0ICogQHBhcmFtIHtPYmplY3R9IGN0eFxyXG5cdFx0ICogQHJldHVybnMge0hUTUxFbGVtZW50fVxyXG5cdFx0ICovXHJcblx0XHRmdW5jdGlvbiBidWlsZF9ncm91cChncm91cCwgcHJvcHNfc2NoZW1hLCBkYXRhLCB1aWQsIGN0eCkge1xyXG5cdFx0XHR2YXIgaXNfb3BlbiAgPSAhIWdyb3VwLm9wZW47XHJcblx0XHRcdHZhciBwYW5lbF9pZCA9ICd3cGJjX2NvbGxhcHNpYmxlX3BhbmVsXycgKyB1aWQgKyAnXycgKyAoZ3JvdXAua2V5IHx8ICdnJyk7XHJcblxyXG5cdFx0XHR2YXIgc2VjdGlvbiA9IGVsKCAnc2VjdGlvbicsIHtcclxuXHRcdFx0XHQnY2xhc3MnICAgICA6ICd3cGJjX2JmYl9faW5zcGVjdG9yX19ncm91cCB3cGJjX3VpX19jb2xsYXBzaWJsZV9ncm91cCcgKyAoaXNfb3BlbiA/ICcgaXMtb3BlbicgOiAnJyksXHJcblx0XHRcdFx0J2RhdGEtZ3JvdXAnOiBncm91cC5rZXkgfHwgJydcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0dmFyIGhlYWRlcl9idG4gPSBlbCggJ2J1dHRvbicsIHtcclxuXHRcdFx0XHR0eXBlICAgICAgICAgICA6ICdidXR0b24nLFxyXG5cdFx0XHRcdCdjbGFzcycgICAgICAgIDogJ2dyb3VwX19oZWFkZXInLFxyXG5cdFx0XHRcdHJvbGUgICAgICAgICAgIDogJ2J1dHRvbicsXHJcblx0XHRcdFx0J2FyaWEtZXhwYW5kZWQnOiBpc19vcGVuID8gJ3RydWUnIDogJ2ZhbHNlJyxcclxuXHRcdFx0XHQnYXJpYS1jb250cm9scyc6IHBhbmVsX2lkXHJcblx0XHRcdH0sIFtcclxuXHRcdFx0XHRlbCggJ2gzJywgbnVsbCwgZ3JvdXAudGl0bGUgfHwgZ3JvdXAubGFiZWwgfHwgZ3JvdXAua2V5IHx8ICcnICksXHJcblx0XHRcdFx0ZWwoICdpJywgeyAnY2xhc3MnOiAnd3BiY191aV9lbF9fdmVydF9tZW51X3Jvb3Rfc2VjdGlvbl9pY29uIG1lbnVfaWNvbiBpY29uLTF4IHdwYmMtYmktY2hldnJvbi1yaWdodCcgfSApXHJcblx0XHRcdF0gKTtcclxuXHJcblx0XHRcdHZhciBmaWVsZHMgPSBlbCggJ2RpdicsIHtcclxuXHRcdFx0XHQnY2xhc3MnICAgICAgOiAnZ3JvdXBfX2ZpZWxkcycsXHJcblx0XHRcdFx0aWQgICAgICAgICAgIDogcGFuZWxfaWQsXHJcblx0XHRcdFx0J2FyaWEtaGlkZGVuJzogaXNfb3BlbiA/ICdmYWxzZScgOiAndHJ1ZSdcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0ZnVuY3Rpb24gYXNBcnJheSh4KSB7XHJcblx0XHRcdFx0aWYgKCBBcnJheS5pc0FycmF5KCB4ICkgKSByZXR1cm4geDtcclxuXHRcdFx0XHRpZiAoIHggJiYgdHlwZW9mIHggPT09ICdvYmplY3QnICkgcmV0dXJuIE9iamVjdC52YWx1ZXMoIHggKTtcclxuXHRcdFx0XHRyZXR1cm4geCAhPSBudWxsID8gWyB4IF0gOiBbXTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0YXNBcnJheSggZ3JvdXAuY29udHJvbHMgKS5mb3JFYWNoKCBmdW5jdGlvbiAoY29udHJvbCkge1xyXG5cdFx0XHRcdGZpZWxkcy5hcHBlbmRDaGlsZCggYnVpbGRfY29udHJvbCggY29udHJvbCwgcHJvcHNfc2NoZW1hLCBkYXRhLCB1aWQsIGN0eCApICk7XHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdHNlY3Rpb24uYXBwZW5kQ2hpbGQoIGhlYWRlcl9idG4gKTtcclxuXHRcdFx0c2VjdGlvbi5hcHBlbmRDaGlsZCggZmllbGRzICk7XHJcblx0XHRcdHJldHVybiBzZWN0aW9uO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU2NoZW1hID4gSW5zcGVjdG9yID4gSGVhZGVyISBCdWlsZCBpbnNwZWN0b3IgaGVhZGVyIHdpdGggYWN0aW9uIGJ1dHRvbnMgd2lyZWQgdG8gZXhpc3RpbmcgZGF0YS1hY3Rpb24gaGFuZGxlcnMuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSBoZWFkZXJfYWN0aW9uc1xyXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9ICAgICAgICB0aXRsZV90ZXh0XHJcblx0XHQgKiBAcmV0dXJucyB7SFRNTEVsZW1lbnR9XHJcblx0XHQgKi9cclxuXHRcdGZ1bmN0aW9uIGJ1aWxkX2hlYWRlcihpbnNwZWN0b3JfdWksIHRpdGxlX2ZhbGxiYWNrLCBzY2hlbWFfZm9yX3R5cGUpIHtcclxuXHJcblx0XHRcdGluc3BlY3Rvcl91aSAgICAgID0gaW5zcGVjdG9yX3VpIHx8IHt9O1xyXG5cdFx0XHRzY2hlbWFfZm9yX3R5cGUgICA9IHNjaGVtYV9mb3JfdHlwZSB8fCB7fTtcclxuXHRcdFx0dmFyIHZhcmlhbnQgICAgICAgPSBpbnNwZWN0b3JfdWkuaGVhZGVyX3ZhcmlhbnQgfHwgJ21pbmltYWwnO1xyXG5cdFx0XHR2YXIgaGVhZGVyQWN0aW9ucyA9IGluc3BlY3Rvcl91aS5oZWFkZXJfYWN0aW9uc1xyXG5cdFx0XHRcdHx8IHNjaGVtYV9mb3JfdHlwZS5oZWFkZXJfYWN0aW9uc1xyXG5cdFx0XHRcdHx8IFsgJ2Rlc2VsZWN0JywgJ3Njcm9sbHRvJywgJ21vdmUtdXAnLCAnbW92ZS1kb3duJywgJ2R1cGxpY2F0ZScsICdkZWxldGUnIF07XHJcblxyXG5cdFx0XHR2YXIgdGl0bGUgICAgICAgPSBpbnNwZWN0b3JfdWkudGl0bGUgfHwgdGl0bGVfZmFsbGJhY2sgfHwgJyc7XHJcblx0XHRcdHZhciBkZXNjcmlwdGlvbiA9IGluc3BlY3Rvcl91aS5kZXNjcmlwdGlvbiB8fCAnJztcclxuXHJcblx0XHRcdC8vIGhlbHBlciB0byBjcmVhdGUgYSBidXR0b24gZm9yIGVpdGhlciBoZWFkZXIgc3R5bGVcclxuXHRcdFx0ZnVuY3Rpb24gYWN0aW9uQnRuKGFjdCwgbWluaW1hbCkge1xyXG5cdFx0XHRcdGlmICggbWluaW1hbCApIHtcclxuXHRcdFx0XHRcdHJldHVybiBlbCggJ2J1dHRvbicsIHsgdHlwZTogJ2J1dHRvbicsICdjbGFzcyc6ICdidXR0b24tbGluaycsICdkYXRhLWFjdGlvbic6IGFjdCB9LCAnJyApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHQvLyB0b29sYmFyIHZhcmlhbnQgKHJpY2gpXHJcblx0XHRcdFx0dmFyIGljb25NYXAgPSB7XHJcblx0XHRcdFx0XHQnZGVzZWxlY3QnIDogJ3dwYmNfaWNuX3JlbW92ZV9kb25lJyxcclxuXHRcdFx0XHRcdCdzY3JvbGx0bycgOiAnd3BiY19pY25fYWRzX2NsaWNrIGZpbHRlcl9jZW50ZXJfZm9jdXMnLFxyXG5cdFx0XHRcdFx0J21vdmUtdXAnICA6ICd3cGJjX2ljbl9hcnJvd191cHdhcmQnLFxyXG5cdFx0XHRcdFx0J21vdmUtZG93bic6ICd3cGJjX2ljbl9hcnJvd19kb3dud2FyZCcsXHJcblx0XHRcdFx0XHQnZHVwbGljYXRlJzogJ3dwYmNfaWNuX2NvbnRlbnRfY29weScsXHJcblx0XHRcdFx0XHQnZGVsZXRlJyAgIDogJ3dwYmNfaWNuX2RlbGV0ZV9vdXRsaW5lJ1xyXG5cdFx0XHRcdH07XHJcblx0XHRcdFx0dmFyIGNsYXNzZXMgPSAnYnV0dG9uIGJ1dHRvbi1zZWNvbmRhcnkgd3BiY191aV9jb250cm9sIHdwYmNfdWlfYnV0dG9uJztcclxuXHRcdFx0XHRpZiAoIGFjdCA9PT0gJ2RlbGV0ZScgKSBjbGFzc2VzICs9ICcgd3BiY191aV9idXR0b25fZGFuZ2VyIGJ1dHRvbi1saW5rLWRlbGV0ZSc7XHJcblxyXG5cdFx0XHRcdHZhciBidG4gPSBlbCggJ2J1dHRvbicsIHtcclxuXHRcdFx0XHRcdHR5cGUgICAgICAgICA6ICdidXR0b24nLFxyXG5cdFx0XHRcdFx0J2NsYXNzJyAgICAgIDogY2xhc3NlcyxcclxuXHRcdFx0XHRcdCdkYXRhLWFjdGlvbic6IGFjdCxcclxuXHRcdFx0XHRcdCdhcmlhLWxhYmVsJyA6IGFjdC5yZXBsYWNlKCAvLS9nLCAnICcgKVxyXG5cdFx0XHRcdH0gKTtcclxuXHJcblx0XHRcdFx0aWYgKCBhY3QgPT09ICdkZWxldGUnICkge1xyXG5cdFx0XHRcdFx0YnRuLmFwcGVuZENoaWxkKCBlbCggJ3NwYW4nLCB7ICdjbGFzcyc6ICdpbi1idXR0b24tdGV4dCcgfSwgJ0RlbGV0ZScgKSApO1xyXG5cdFx0XHRcdFx0YnRuLmFwcGVuZENoaWxkKCBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSggJyAnICkgKTsgLy8gbWlub3Igc3BhY2luZyBiZWZvcmUgaWNvblxyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRidG4uYXBwZW5kQ2hpbGQoIGVsKCAnaScsIHsgJ2NsYXNzJzogJ21lbnVfaWNvbiBpY29uLTF4ICcgKyAoaWNvbk1hcFthY3RdIHx8ICcnKSB9ICkgKTtcclxuXHRcdFx0XHRyZXR1cm4gYnRuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyA9PT0gbWluaW1hbCBoZWFkZXIgKGV4aXN0aW5nIGxvb2s7IGRlZmF1bHQpID09PVxyXG5cdFx0XHRpZiAoIHZhcmlhbnQgIT09ICd0b29sYmFyJyApIHtcclxuXHRcdFx0XHR2YXIgaGVhZGVyID0gZWwoICdoZWFkZXInLCB7ICdjbGFzcyc6ICd3cGJjX2JmYl9faW5zcGVjdG9yX19oZWFkZXInIH0gKTtcclxuXHRcdFx0XHRoZWFkZXIuYXBwZW5kQ2hpbGQoIGVsKCAnaDMnLCBudWxsLCB0aXRsZSB8fCAnJyApICk7XHJcblxyXG5cdFx0XHRcdHZhciBhY3Rpb25zID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd3cGJjX2JmYl9faW5zcGVjdG9yX19oZWFkZXJfYWN0aW9ucycgfSApO1xyXG5cdFx0XHRcdGhlYWRlckFjdGlvbnMuZm9yRWFjaCggZnVuY3Rpb24gKGFjdCkge1xyXG5cdFx0XHRcdFx0YWN0aW9ucy5hcHBlbmRDaGlsZCggYWN0aW9uQnRuKCBhY3QsIC8qbWluaW1hbCovdHJ1ZSApICk7XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdGhlYWRlci5hcHBlbmRDaGlsZCggYWN0aW9ucyApO1xyXG5cdFx0XHRcdHJldHVybiBoZWFkZXI7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vID09PSB0b29sYmFyIGhlYWRlciAocmljaCB0aXRsZS9kZXNjICsgZ3JvdXBlZCBidXR0b25zKSA9PT1cclxuXHRcdFx0dmFyIHJvb3QgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ3dwYmNfYmZiX19pbnNwZWN0b3JfX2hlYWQnIH0gKTtcclxuXHRcdFx0dmFyIHdyYXAgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ2hlYWRlcl9jb250YWluZXInIH0gKTtcclxuXHRcdFx0dmFyIGxlZnQgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ2hlYWRlcl90aXRsZV9jb250ZW50JyB9ICk7XHJcblx0XHRcdHZhciBoMyAgID0gZWwoICdoMycsIHsgJ2NsYXNzJzogJ3RpdGxlJyB9LCB0aXRsZSB8fCAnJyApO1xyXG5cdFx0XHRsZWZ0LmFwcGVuZENoaWxkKCBoMyApO1xyXG5cdFx0XHRpZiAoIGRlc2NyaXB0aW9uICkge1xyXG5cdFx0XHRcdGxlZnQuYXBwZW5kQ2hpbGQoIGVsKCAnZGl2JywgeyAnY2xhc3MnOiAnZGVzYycgfSwgZGVzY3JpcHRpb24gKSApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR2YXIgcmlnaHQgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ2FjdGlvbnMgd3BiY19hanhfdG9vbGJhciB3cGJjX25vX2JvcmRlcnMnIH0gKTtcclxuXHRcdFx0dmFyIHVpQyAgID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd1aV9jb250YWluZXIgdWlfY29udGFpbmVyX3NtYWxsJyB9ICk7XHJcblx0XHRcdHZhciB1aUcgICA9IGVsKCAnZGl2JywgeyAnY2xhc3MnOiAndWlfZ3JvdXAnIH0gKTtcclxuXHJcblx0XHRcdC8vIFNwbGl0IGludG8gdmlzdWFsIGdyb3VwczogZmlyc3QgMiwgbmV4dCAyLCB0aGVuIHRoZSByZXN0LlxyXG5cdFx0XHR2YXIgZzEgPSBlbCggJ2RpdicsIHsgJ2NsYXNzJzogJ3VpX2VsZW1lbnQnIH0gKTtcclxuXHRcdFx0dmFyIGcyID0gZWwoICdkaXYnLCB7ICdjbGFzcyc6ICd1aV9lbGVtZW50JyB9ICk7XHJcblx0XHRcdHZhciBnMyA9IGVsKCAnZGl2JywgeyAnY2xhc3MnOiAndWlfZWxlbWVudCcgfSApO1xyXG5cclxuXHRcdFx0aGVhZGVyQWN0aW9ucy5zbGljZSggMCwgMiApLmZvckVhY2goIGZ1bmN0aW9uIChhY3QpIHtcclxuXHRcdFx0XHRnMS5hcHBlbmRDaGlsZCggYWN0aW9uQnRuKCBhY3QsIGZhbHNlICkgKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0XHRoZWFkZXJBY3Rpb25zLnNsaWNlKCAyLCA0ICkuZm9yRWFjaCggZnVuY3Rpb24gKGFjdCkge1xyXG5cdFx0XHRcdGcyLmFwcGVuZENoaWxkKCBhY3Rpb25CdG4oIGFjdCwgZmFsc2UgKSApO1xyXG5cdFx0XHR9ICk7XHJcblx0XHRcdGhlYWRlckFjdGlvbnMuc2xpY2UoIDQgKS5mb3JFYWNoKCBmdW5jdGlvbiAoYWN0KSB7XHJcblx0XHRcdFx0ZzMuYXBwZW5kQ2hpbGQoIGFjdGlvbkJ0biggYWN0LCBmYWxzZSApICk7XHJcblx0XHRcdH0gKTtcclxuXHJcblx0XHRcdHVpRy5hcHBlbmRDaGlsZCggZzEgKTtcclxuXHRcdFx0dWlHLmFwcGVuZENoaWxkKCBnMiApO1xyXG5cdFx0XHR1aUcuYXBwZW5kQ2hpbGQoIGczICk7XHJcblx0XHRcdHVpQy5hcHBlbmRDaGlsZCggdWlHICk7XHJcblx0XHRcdHJpZ2h0LmFwcGVuZENoaWxkKCB1aUMgKTtcclxuXHJcblx0XHRcdHdyYXAuYXBwZW5kQ2hpbGQoIGxlZnQgKTtcclxuXHRcdFx0d3JhcC5hcHBlbmRDaGlsZCggcmlnaHQgKTtcclxuXHRcdFx0cm9vdC5hcHBlbmRDaGlsZCggd3JhcCApO1xyXG5cclxuXHRcdFx0cmV0dXJuIHJvb3Q7XHJcblx0XHR9XHJcblxyXG5cclxuXHRcdGZ1bmN0aW9uIGZhY3RvcnlfcmVuZGVyKHBhbmVsX2VsLCBzY2hlbWFfZm9yX3R5cGUsIGRhdGEsIG9wdHMpIHtcclxuXHRcdFx0aWYgKCAhcGFuZWxfZWwgKSByZXR1cm4gcGFuZWxfZWw7XHJcblxyXG5cdFx0XHRzY2hlbWFfZm9yX3R5cGUgID0gc2NoZW1hX2Zvcl90eXBlIHx8IHt9O1xyXG5cdFx0XHR2YXIgcHJvcHNfc2NoZW1hID0gKHNjaGVtYV9mb3JfdHlwZS5zY2hlbWEgJiYgc2NoZW1hX2Zvcl90eXBlLnNjaGVtYS5wcm9wcykgPyBzY2hlbWFfZm9yX3R5cGUuc2NoZW1hLnByb3BzIDoge307XHJcblx0XHRcdHZhciBpbnNwZWN0b3JfdWkgPSAoc2NoZW1hX2Zvcl90eXBlLmluc3BlY3Rvcl91aSB8fCB7fSk7XHJcblx0XHRcdHZhciBncm91cHMgICAgICAgPSBpbnNwZWN0b3JfdWkuZ3JvdXBzIHx8IFtdO1xyXG5cclxuXHRcdFx0dmFyIGhlYWRlcl9hY3Rpb25zID0gaW5zcGVjdG9yX3VpLmhlYWRlcl9hY3Rpb25zIHx8IHNjaGVtYV9mb3JfdHlwZS5oZWFkZXJfYWN0aW9ucyB8fCBbXTtcclxuXHRcdFx0dmFyIHRpdGxlX3RleHQgICAgID0gKG9wdHMgJiYgb3B0cy50aXRsZSkgfHwgaW5zcGVjdG9yX3VpLnRpdGxlIHx8IHNjaGVtYV9mb3JfdHlwZS5sYWJlbCB8fCAoZGF0YSAmJiBkYXRhLmxhYmVsKSB8fCAnJztcclxuXHJcblx0XHQvLyBQcmVwYXJlIHJlbmRlcmluZyBjb250ZXh0IGZvciBzbG90cy92YWx1ZV9mcm9tLCBldGMuXHJcblx0XHRcdHZhciBjdHggPSB7XHJcblx0XHRcdFx0ZWwgICAgIDogb3B0cyAmJiBvcHRzLmVsIHx8IG51bGwsXHJcblx0XHRcdFx0YnVpbGRlcjogb3B0cyAmJiBvcHRzLmJ1aWxkZXIgfHwgbnVsbCxcclxuXHRcdFx0XHR0eXBlICAgOiBvcHRzICYmIG9wdHMudHlwZSB8fCBudWxsLFxyXG5cdFx0XHRcdGRhdGEgICA6IGRhdGEgfHwge31cclxuXHRcdFx0fTtcclxuXHJcblx0XHRcdC8vIGNsZWFyIHBhbmVsLlxyXG5cdFx0XHR3aGlsZSAoIHBhbmVsX2VsLmZpcnN0Q2hpbGQgKSBwYW5lbF9lbC5yZW1vdmVDaGlsZCggcGFuZWxfZWwuZmlyc3RDaGlsZCApO1xyXG5cclxuXHRcdFx0dmFyIHVpZCA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoIDM2ICkuc2xpY2UoIDIsIDggKTtcclxuXHJcblx0XHRcdC8vIGhlYWRlci5cclxuXHRcdFx0cGFuZWxfZWwuYXBwZW5kQ2hpbGQoIGJ1aWxkX2hlYWRlciggaW5zcGVjdG9yX3VpLCB0aXRsZV90ZXh0LCBzY2hlbWFfZm9yX3R5cGUgKSApO1xyXG5cclxuXHJcblx0XHRcdC8vIGdyb3Vwcy5cclxuXHRcdFx0Z3JvdXBzLmZvckVhY2goIGZ1bmN0aW9uIChnKSB7XHJcblx0XHRcdFx0cGFuZWxfZWwuYXBwZW5kQ2hpbGQoIGJ1aWxkX2dyb3VwKCBnLCBwcm9wc19zY2hlbWEsIGRhdGEgfHwge30sIHVpZCwgY3R4ICkgKTtcclxuXHRcdFx0fSApO1xyXG5cclxuXHRcdFx0Ly8gQVJJQSBzeW5jIGZvciB0b2dnbGVzIGNyZWF0ZWQgaGVyZSAoZW5zdXJlIGFyaWEtY2hlY2tlZCBtYXRjaGVzIHN0YXRlKS5cclxuXHRcdFx0dHJ5IHtcclxuXHRcdFx0XHQvLyBDZW50cmFsaXplZCBVSSBub3JtYWxpemVycyAodG9nZ2xlcyArIEExMXkpOiBoYW5kbGVkIGluIENvcmUuXHJcblx0XHRcdFx0VUkuYXBwbHlfcG9zdF9yZW5kZXIoIHBhbmVsX2VsICk7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHdpcmVfbGVuX2dyb3VwKCBwYW5lbF9lbCApO1xyXG5cdFx0XHRcdFx0Ly8gSW5pdGlhbGl6ZSBDb2xvcmlzIG9uIGNvbG9yIGlucHV0cyByZW5kZXJlZCBpbiB0aGlzIHBhbmVsLlxyXG5cdFx0XHRcdFx0aW5pdF9jb2xvcmlzX3BpY2tlcnMoIHBhbmVsX2VsICk7XHJcblx0XHRcdFx0fSBjYXRjaCAoIF8gKSB7IH1cclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7IH1cclxuXHJcblx0XHRcdHJldHVybiBwYW5lbF9lbDtcclxuXHRcdH1cclxuXHJcblx0XHRVSS5XUEJDX0JGQl9JbnNwZWN0b3JfRmFjdG9yeSA9IHsgcmVuZGVyOiBmYWN0b3J5X3JlbmRlciB9OyAgIC8vIG92ZXJ3cml0ZS9yZWZyZXNoXHJcblxyXG5cdFx0Ly8gLS0tLSBCdWlsdC1pbiBzbG90ICsgdmFsdWVfZnJvbSBmb3IgU2VjdGlvbnMgLS0tLVxyXG5cclxuXHRcdGZ1bmN0aW9uIHNsb3RfbGF5b3V0X2NoaXBzKGhvc3QsIGN0eCkge1xyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdHZhciBMID0gdy5XUEJDX0JGQl9Db3JlICYmICB3LldQQkNfQkZCX0NvcmUuVUkgJiYgdy5XUEJDX0JGQl9Db3JlLlVJLldQQkNfQkZCX0xheW91dF9DaGlwcztcclxuXHRcdFx0XHRpZiAoIEwgJiYgdHlwZW9mIEwucmVuZGVyX2Zvcl9zZWN0aW9uID09PSAnZnVuY3Rpb24nICkge1xyXG5cdFx0XHRcdFx0TC5yZW5kZXJfZm9yX3NlY3Rpb24oIGN0eC5idWlsZGVyLCBjdHguZWwsIGhvc3QgKTtcclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0aG9zdC5hcHBlbmRDaGlsZCggZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoICdbbGF5b3V0X2NoaXBzIG5vdCBhdmFpbGFibGVdJyApICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9IGNhdGNoICggZSApIHtcclxuXHRcdFx0XHRjb25zb2xlLndhcm4oICd3cGJjX2JmYl9zbG90X2xheW91dF9jaGlwcyBmYWlsZWQ6JywgZSApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dy53cGJjX2JmYl9pbnNwZWN0b3JfZmFjdG9yeV9zbG90cy5sYXlvdXRfY2hpcHMgPSBzbG90X2xheW91dF9jaGlwcztcclxuXHJcblx0XHRmdW5jdGlvbiB2YWx1ZV9mcm9tX2NvbXB1dGVfc2VjdGlvbl9jb2x1bW5zKGN0eCkge1xyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdHZhciByb3cgPSBjdHggJiYgY3R4LmVsICYmIGN0eC5lbC5xdWVyeVNlbGVjdG9yICYmIGN0eC5lbC5xdWVyeVNlbGVjdG9yKCAnOnNjb3BlID4gLndwYmNfYmZiX19yb3cnICk7XHJcblx0XHRcdFx0aWYgKCAhcm93ICkgcmV0dXJuIDE7XHJcblx0XHRcdFx0dmFyIG4gPSByb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApLmxlbmd0aCB8fCAxO1xyXG5cdFx0XHRcdGlmICggbiA8IDEgKSBuID0gMTtcclxuXHRcdFx0XHRpZiAoIG4gPiA0ICkgbiA9IDQ7XHJcblx0XHRcdFx0cmV0dXJuIG47XHJcblx0XHRcdH0gY2F0Y2ggKCBfICkge1xyXG5cdFx0XHRcdHJldHVybiAxO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dy53cGJjX2JmYl9pbnNwZWN0b3JfZmFjdG9yeV92YWx1ZV9mcm9tLmNvbXB1dGVfc2VjdGlvbl9jb2x1bW5zID0gdmFsdWVfZnJvbV9jb21wdXRlX3NlY3Rpb25fY29sdW1ucztcclxuXHR9XHJcblxyXG5cdC8vIDMpIEluc3BlY3RvciBjbGFzcy5cclxuXHJcblx0Y2xhc3MgV1BCQ19CRkJfSW5zcGVjdG9yIHtcclxuXHJcblx0XHRjb25zdHJ1Y3RvcihwYW5lbF9lbCwgYnVpbGRlcikge1xyXG5cdFx0XHR0aGlzLnBhbmVsICAgICAgICAgPSBwYW5lbF9lbCB8fCB0aGlzLl9jcmVhdGVfZmFsbGJhY2tfcGFuZWwoKTtcclxuXHRcdFx0dGhpcy5idWlsZGVyICAgICAgID0gYnVpbGRlcjtcclxuXHRcdFx0dGhpcy5zZWxlY3RlZF9lbCAgID0gbnVsbDtcclxuXHRcdFx0dGhpcy5fcmVuZGVyX3RpbWVyID0gbnVsbDtcclxuXHJcblx0XHRcdHRoaXMuX29uX2RlbGVnYXRlZF9pbnB1dCAgPSAoZSkgPT4gdGhpcy5fYXBwbHlfY29udHJvbF9mcm9tX2V2ZW50KCBlICk7XHJcblx0XHRcdHRoaXMuX29uX2RlbGVnYXRlZF9jaGFuZ2UgPSAoZSkgPT4gdGhpcy5fYXBwbHlfY29udHJvbF9mcm9tX2V2ZW50KCBlICk7XHJcblx0XHRcdHRoaXMucGFuZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2lucHV0JywgdGhpcy5fb25fZGVsZWdhdGVkX2lucHV0LCB0cnVlICk7XHJcblx0XHRcdHRoaXMucGFuZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2NoYW5nZScsIHRoaXMuX29uX2RlbGVnYXRlZF9jaGFuZ2UsIHRydWUgKTtcclxuXHJcblx0XHRcdHRoaXMuX29uX2RlbGVnYXRlZF9jbGljayA9IChlKSA9PiB7XHJcblx0XHRcdFx0Y29uc3QgYnRuID0gZS50YXJnZXQuY2xvc2VzdCggJ1tkYXRhLWFjdGlvbl0nICk7XHJcblx0XHRcdFx0aWYgKCAhYnRuIHx8ICF0aGlzLnBhbmVsLmNvbnRhaW5zKCBidG4gKSApIHJldHVybjtcclxuXHRcdFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XHJcblx0XHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHJcblx0XHRcdFx0Y29uc3QgYWN0aW9uID0gYnRuLmdldEF0dHJpYnV0ZSggJ2RhdGEtYWN0aW9uJyApO1xyXG5cdFx0XHRcdGNvbnN0IGVsICAgICA9IHRoaXMuc2VsZWN0ZWRfZWw7XHJcblx0XHRcdFx0aWYgKCAhZWwgKSByZXR1cm47XHJcblxyXG5cdFx0XHRcdHcuV1BCQ19CRkJfSW5zcGVjdG9yX0FjdGlvbnM/LnJ1biggYWN0aW9uLCB7XHJcblx0XHRcdFx0XHRidWlsZGVyOiB0aGlzLmJ1aWxkZXIsXHJcblx0XHRcdFx0XHRlbCxcclxuXHRcdFx0XHRcdHBhbmVsICA6IHRoaXMucGFuZWwsXHJcblx0XHRcdFx0XHRldmVudCAgOiBlXHJcblx0XHRcdFx0fSApO1xyXG5cclxuXHRcdFx0XHRpZiAoIGFjdGlvbiA9PT0gJ2RlbGV0ZScgKSB0aGlzLmNsZWFyKCk7XHJcblx0XHRcdH07XHJcblx0XHRcdHRoaXMucGFuZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcy5fb25fZGVsZWdhdGVkX2NsaWNrICk7XHJcblx0XHR9XHJcblxyXG5cdFx0X3Bvc3RfcmVuZGVyX3VpKCkge1xyXG5cdFx0XHR0cnkge1xyXG5cdFx0XHRcdHZhciBVSSA9IHcuV1BCQ19CRkJfQ29yZSAmJiB3LldQQkNfQkZCX0NvcmUuVUk7XHJcblx0XHRcdFx0aWYgKCBVSSAmJiB0eXBlb2YgVUkuYXBwbHlfcG9zdF9yZW5kZXIgPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0XHRVSS5hcHBseV9wb3N0X3JlbmRlciggdGhpcy5wYW5lbCApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHQvLyBORVc6IHdpcmUgc2xpZGVyL251bWJlci91bml0IHN5bmNpbmcgZm9yIGxlbmd0aCAmIHJhbmdlX251bWJlciBncm91cHMuXHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHdpcmVfbGVuX2dyb3VwKCB0aGlzLnBhbmVsICk7XHJcblx0XHRcdFx0XHRpbml0X2NvbG9yaXNfcGlja2VycyggdGhpcy5wYW5lbCApO1xyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfICkge1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBjYXRjaCAoIGUgKSB7XHJcblx0XHRcdFx0X3dwYmM/LmRldj8uZXJyb3I/LiggJ2luc3BlY3Rvci5fcG9zdF9yZW5kZXJfdWknLCBlICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblxyXG5cdFx0X2FwcGx5X2NvbnRyb2xfZnJvbV9ldmVudChlKSB7XHJcblx0XHRcdGlmICggIXRoaXMucGFuZWwuY29udGFpbnMoIGUudGFyZ2V0ICkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCB0ICAgPSAvKiogQHR5cGUge0hUTUxJbnB1dEVsZW1lbnR8SFRNTFRleHRBcmVhRWxlbWVudHxIVE1MU2VsZWN0RWxlbWVudH0gKi8gKGUudGFyZ2V0KTtcclxuXHRcdFx0Y29uc3Qga2V5ID0gdD8uZGF0YXNldD8uaW5zcGVjdG9yS2V5O1xyXG5cdFx0XHRpZiAoICFrZXkgKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBlbCA9IHRoaXMuc2VsZWN0ZWRfZWw7XHJcblx0XHRcdGlmICggIWVsIHx8ICFkb2N1bWVudC5ib2R5LmNvbnRhaW5zKCBlbCApICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0bGV0IHY7XHJcblx0XHRcdGlmICggdCBpbnN0YW5jZW9mIEhUTUxJbnB1dEVsZW1lbnQgJiYgdC50eXBlID09PSAnY2hlY2tib3gnICkge1xyXG5cdFx0XHRcdHYgPSAhIXQuY2hlY2tlZDtcclxuXHRcdFx0XHR0LnNldEF0dHJpYnV0ZSggJ2FyaWEtY2hlY2tlZCcsIHYgPyAndHJ1ZScgOiAnZmFsc2UnICk7ICAgICAgICAgLy8gS2VlcCBBUklBIHN0YXRlIGluIHN5bmMgZm9yIHRvZ2dsZXMgKHNjaGVtYSBhbmQgdGVtcGxhdGUgcGF0aHMpLlxyXG5cdFx0XHR9IGVsc2UgaWYgKCB0IGluc3RhbmNlb2YgSFRNTElucHV0RWxlbWVudCAmJiB0LnR5cGUgPT09ICdudW1iZXInICkge1xyXG5cdFx0XHRcdHYgPSAodC52YWx1ZSA9PT0gJycgPyAnJyA6IE51bWJlciggdC52YWx1ZSApKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR2ID0gdC52YWx1ZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCBrZXkgPT09ICdpZCcgKSB7XHJcblx0XHRcdFx0Y29uc3QgdW5pcXVlID0gdGhpcy5idWlsZGVyPy5pZD8uc2V0X2ZpZWxkX2lkPy4oIGVsLCB2ICk7XHJcblx0XHRcdFx0aWYgKCB1bmlxdWUgIT0gbnVsbCAmJiB0LnZhbHVlICE9PSB1bmlxdWUgKSB0LnZhbHVlID0gdW5pcXVlO1xyXG5cclxuXHRcdFx0fSBlbHNlIGlmICgga2V5ID09PSAnbmFtZScgKSB7XHJcblx0XHRcdFx0Y29uc3QgdW5pcXVlID0gdGhpcy5idWlsZGVyPy5pZD8uc2V0X2ZpZWxkX25hbWU/LiggZWwsIHYgKTtcclxuXHRcdFx0XHRpZiAoIHVuaXF1ZSAhPSBudWxsICYmIHQudmFsdWUgIT09IHVuaXF1ZSApIHQudmFsdWUgPSB1bmlxdWU7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCBrZXkgPT09ICdodG1sX2lkJyApIHtcclxuXHRcdFx0XHRjb25zdCBhcHBsaWVkID0gdGhpcy5idWlsZGVyPy5pZD8uc2V0X2ZpZWxkX2h0bWxfaWQ/LiggZWwsIHYgKTtcclxuXHRcdFx0XHRpZiAoIGFwcGxpZWQgIT0gbnVsbCAmJiB0LnZhbHVlICE9PSBhcHBsaWVkICkgdC52YWx1ZSA9IGFwcGxpZWQ7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCBrZXkgPT09ICdjb2x1bW5zJyAmJiBlbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSApIHtcclxuXHRcdFx0XHRjb25zdCB2X2ludCA9IHBhcnNlSW50KCBTdHJpbmcoIHYgKSwgMTAgKTtcclxuXHRcdFx0XHRpZiAoIE51bWJlci5pc0Zpbml0ZSggdl9pbnQgKSApIHtcclxuXHRcdFx0XHRcdGNvbnN0IGNsYW1wZWQgPSB3LldQQkNfQkZCX0NvcmUuV1BCQ19CRkJfU2FuaXRpemUuY2xhbXAoIHZfaW50LCAxLCA0ICk7XHJcblx0XHRcdFx0XHR0aGlzLmJ1aWxkZXI/LnNldF9zZWN0aW9uX2NvbHVtbnM/LiggZWwsIGNsYW1wZWQgKTtcclxuXHRcdFx0XHRcdGlmICggU3RyaW5nKCBjbGFtcGVkICkgIT09IHQudmFsdWUgKSB0LnZhbHVlID0gU3RyaW5nKCBjbGFtcGVkICk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRpZiAoIHQgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmIHQudHlwZSA9PT0gJ2NoZWNrYm94JyApIHtcclxuXHRcdFx0XHRcdGVsLnNldEF0dHJpYnV0ZSggJ2RhdGEtJyArIGtleSwgU3RyaW5nKCAhIXYgKSApO1xyXG5cdFx0XHRcdH0gZWxzZSBpZiAoIHQgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmIHQudHlwZSA9PT0gJ251bWJlcicgKSB7XHJcblx0XHRcdFx0XHRpZiAoIHQudmFsdWUgPT09ICcnIHx8ICFOdW1iZXIuaXNGaW5pdGUoIHYgKSApIHtcclxuXHRcdFx0XHRcdFx0ZWwucmVtb3ZlQXR0cmlidXRlKCAnZGF0YS0nICsga2V5ICk7XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoICdkYXRhLScgKyBrZXksIFN0cmluZyggdiApICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBlbHNlIGlmICggdiA9PSBudWxsICkge1xyXG5cdFx0XHRcdFx0ZWwucmVtb3ZlQXR0cmlidXRlKCAnZGF0YS0nICsga2V5ICk7XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdGVsLnNldEF0dHJpYnV0ZSggJ2RhdGEtJyArIGtleSwgKHR5cGVvZiB2ID09PSAnb2JqZWN0JykgPyBKU09OLnN0cmluZ2lmeSggdiApIDogU3RyaW5nKCB2ICkgKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIFVwZGF0ZSBwcmV2aWV3L292ZXJsYXlcclxuXHRcdFx0aWYgKCBlbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fZmllbGQnICkgKSB7XHJcblx0XHRcdFx0aWYgKCB0aGlzLmJ1aWxkZXI/LnByZXZpZXdfbW9kZSApIHRoaXMuYnVpbGRlci5yZW5kZXJfcHJldmlldyggZWwgKTtcclxuXHRcdFx0XHRlbHNlIHRoaXMuYnVpbGRlci5hZGRfb3ZlcmxheV90b29sYmFyKCBlbCApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXMuYnVpbGRlci5hZGRfb3ZlcmxheV90b29sYmFyKCBlbCApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAoIHRoaXMuX25lZWRzX3JlcmVuZGVyKCBlbCwga2V5LCBlICkgKSB7XHJcblx0XHRcdFx0dGhpcy5fc2NoZWR1bGVfcmVuZGVyX3ByZXNlcnZpbmdfZm9jdXMoIDAgKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdF9uZWVkc19yZXJlbmRlcihlbCwga2V5LCBfZSkge1xyXG5cdFx0XHRpZiAoIGVsLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19zZWN0aW9uJyApICYmIGtleSA9PT0gJ2NvbHVtbnMnICkgcmV0dXJuIHRydWU7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHJcblx0XHRiaW5kX3RvX2ZpZWxkKGZpZWxkX2VsKSB7XHJcblx0XHRcdHRoaXMuc2VsZWN0ZWRfZWwgPSBmaWVsZF9lbDtcclxuXHRcdFx0dGhpcy5yZW5kZXIoKTtcclxuXHRcdH1cclxuXHJcblx0XHRjbGVhcigpIHtcclxuXHRcdFx0dGhpcy5zZWxlY3RlZF9lbCA9IG51bGw7XHJcblx0XHRcdGlmICggdGhpcy5fcmVuZGVyX3RpbWVyICkge1xyXG5cdFx0XHRcdGNsZWFyVGltZW91dCggdGhpcy5fcmVuZGVyX3RpbWVyICk7XHJcblx0XHRcdFx0dGhpcy5fcmVuZGVyX3RpbWVyID0gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBBbHNvIGNsZWFyIHRoZSBzZWN0aW9uLWNvbHMgaGludCBvbiBlbXB0eSBzdGF0ZS5cclxuXHRcdFx0dGhpcy5wYW5lbC5yZW1vdmVBdHRyaWJ1dGUoJ2RhdGEtYmZiLXNlY3Rpb24tY29scycpO1xyXG5cdFx0XHR0aGlzLnBhbmVsLmlubmVySFRNTCA9ICc8ZGl2IGNsYXNzPVwid3BiY19iZmJfX2luc3BlY3Rvcl9fZW1wdHlcIj5TZWxlY3QgYSBmaWVsZCB0byBlZGl0IGl0cyBvcHRpb25zLjwvZGl2Pic7XHJcblx0XHR9XHJcblxyXG5cdFx0X3NjaGVkdWxlX3JlbmRlcl9wcmVzZXJ2aW5nX2ZvY3VzKGRlbGF5ID0gMjAwKSB7XHJcblx0XHRcdGNvbnN0IGFjdGl2ZSAgICA9IC8qKiBAdHlwZSB7SFRNTElucHV0RWxlbWVudHxIVE1MVGV4dEFyZWFFbGVtZW50fEhUTUxFbGVtZW50fG51bGx9ICovIChkb2N1bWVudC5hY3RpdmVFbGVtZW50KTtcclxuXHRcdFx0Y29uc3QgYWN0aXZlS2V5ID0gYWN0aXZlPy5kYXRhc2V0Py5pbnNwZWN0b3JLZXkgfHwgbnVsbDtcclxuXHRcdFx0bGV0IHNlbFN0YXJ0ICAgID0gbnVsbCwgc2VsRW5kID0gbnVsbDtcclxuXHJcblx0XHRcdGlmICggYWN0aXZlICYmICdzZWxlY3Rpb25TdGFydCcgaW4gYWN0aXZlICYmICdzZWxlY3Rpb25FbmQnIGluIGFjdGl2ZSApIHtcclxuXHRcdFx0XHQvLyBAdHMtaWdub3JlXHJcblx0XHRcdFx0c2VsU3RhcnQgPSBhY3RpdmUuc2VsZWN0aW9uU3RhcnQ7XHJcblx0XHRcdFx0Ly8gQHRzLWlnbm9yZVxyXG5cdFx0XHRcdHNlbEVuZCAgID0gYWN0aXZlLnNlbGVjdGlvbkVuZDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKCB0aGlzLl9yZW5kZXJfdGltZXIgKSBjbGVhclRpbWVvdXQoIHRoaXMuX3JlbmRlcl90aW1lciApO1xyXG5cdFx0XHR0aGlzLl9yZW5kZXJfdGltZXIgPSAvKiogQHR5cGUge3Vua25vd259ICovIChzZXRUaW1lb3V0KCAoKSA9PiB7XHJcblx0XHRcdFx0dGhpcy5yZW5kZXIoKTtcclxuXHRcdFx0XHRpZiAoIGFjdGl2ZUtleSApIHtcclxuXHRcdFx0XHRcdGNvbnN0IG5leHQgPSAvKiogQHR5cGUge0hUTUxJbnB1dEVsZW1lbnR8SFRNTFRleHRBcmVhRWxlbWVudHxIVE1MRWxlbWVudHxudWxsfSAqLyAoXHJcblx0XHRcdFx0XHRcdHRoaXMucGFuZWwucXVlcnlTZWxlY3RvciggYFtkYXRhLWluc3BlY3Rvci1rZXk9XCIke2FjdGl2ZUtleX1cIl1gIClcclxuXHRcdFx0XHRcdCk7XHJcblx0XHRcdFx0XHRpZiAoIG5leHQgKSB7XHJcblx0XHRcdFx0XHRcdG5leHQuZm9jdXMoKTtcclxuXHRcdFx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdFx0XHRpZiAoIHNlbFN0YXJ0ICE9IG51bGwgJiYgc2VsRW5kICE9IG51bGwgJiYgdHlwZW9mIG5leHQuc2V0U2VsZWN0aW9uUmFuZ2UgPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0XHRcdFx0XHQvLyBAdHMtaWdub3JlXHJcblx0XHRcdFx0XHRcdFx0XHRuZXh0LnNldFNlbGVjdGlvblJhbmdlKCBzZWxTdGFydCwgc2VsRW5kICk7XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9IGNhdGNoKCBlICl7IF93cGJjPy5kZXY/LmVycm9yKCAnX3JlbmRlcl90aW1lcicsIGUgKTsgfVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSwgZGVsYXkgKSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmVuZGVyKCkge1xyXG5cclxuXHRcdFx0Y29uc3QgZWwgPSB0aGlzLnNlbGVjdGVkX2VsO1xyXG5cdFx0XHRpZiAoICFlbCB8fCAhZG9jdW1lbnQuYm9keS5jb250YWlucyggZWwgKSApIHJldHVybiB0aGlzLmNsZWFyKCk7XHJcblxyXG5cdFx0XHQvLyBSZXNldCBzZWN0aW9uLWNvbHMgaGludCB1bmxlc3Mgd2Ugc2V0IGl0IGxhdGVyIGZvciBhIHNlY3Rpb24uXHJcblx0XHRcdHRoaXMucGFuZWwucmVtb3ZlQXR0cmlidXRlKCAnZGF0YS1iZmItc2VjdGlvbi1jb2xzJyApO1xyXG5cclxuXHRcdFx0Y29uc3QgcHJldl9zY3JvbGwgPSB0aGlzLnBhbmVsLnNjcm9sbFRvcDtcclxuXHJcblx0XHRcdC8vIFNlY3Rpb25cclxuXHRcdFx0aWYgKCBlbC5jbGFzc0xpc3QuY29udGFpbnMoICd3cGJjX2JmYl9fc2VjdGlvbicgKSApIHtcclxuXHRcdFx0XHRsZXQgdHBsID0gbnVsbDtcclxuXHRcdFx0XHR0cnkge1xyXG5cdFx0XHRcdFx0dHBsID0gKHcud3AgJiYgd3AudGVtcGxhdGUgJiYgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICd0bXBsLXdwYmMtYmZiLWluc3BlY3Rvci1zZWN0aW9uJyApKSA/IHdwLnRlbXBsYXRlKCAnd3BiYy1iZmItaW5zcGVjdG9yLXNlY3Rpb24nICkgOiBudWxsO1xyXG5cdFx0XHRcdH0gY2F0Y2ggKCBfICkge1xyXG5cdFx0XHRcdFx0dHBsID0gbnVsbDtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdGlmICggdHBsICkge1xyXG5cdFx0XHRcdFx0dGhpcy5wYW5lbC5pbm5lckhUTUwgPSB0cGwoIHt9ICk7XHJcblx0XHRcdFx0XHR0aGlzLl9lbmZvcmNlX2RlZmF1bHRfZ3JvdXBfb3BlbigpO1xyXG5cdFx0XHRcdFx0dGhpcy5fc2V0X3BhbmVsX3NlY3Rpb25fY29scyggZWwgKTtcclxuXHRcdFx0XHRcdHRoaXMuX3Bvc3RfcmVuZGVyX3VpKCk7XHJcblx0XHRcdFx0XHR0aGlzLnBhbmVsLnNjcm9sbFRvcCA9IHByZXZfc2Nyb2xsO1xyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHJcblx0XHRcdFx0Y29uc3QgRmFjdG9yeSA9IHcuV1BCQ19CRkJfQ29yZS5VSSAmJiB3LldQQkNfQkZCX0NvcmUuVUkuV1BCQ19CRkJfSW5zcGVjdG9yX0ZhY3Rvcnk7XHJcblx0XHRcdFx0Y29uc3Qgc2NoZW1hcyA9IHcuV1BCQ19CRkJfU2NoZW1hcyB8fCB7fTtcclxuXHRcdFx0XHRjb25zdCBlbnRyeSAgID0gc2NoZW1hc1snc2VjdGlvbiddIHx8IG51bGw7XHJcblx0XHRcdFx0aWYgKCBlbnRyeSAmJiBGYWN0b3J5ICkge1xyXG5cdFx0XHRcdFx0dGhpcy5wYW5lbC5pbm5lckhUTUwgPSAnJztcclxuXHRcdFx0XHRcdEZhY3RvcnkucmVuZGVyKFxyXG5cdFx0XHRcdFx0XHR0aGlzLnBhbmVsLFxyXG5cdFx0XHRcdFx0XHRlbnRyeSxcclxuXHRcdFx0XHRcdFx0e30sXHJcblx0XHRcdFx0XHRcdHsgZWwsIGJ1aWxkZXI6IHRoaXMuYnVpbGRlciwgdHlwZTogJ3NlY3Rpb24nLCB0aXRsZTogZW50cnkubGFiZWwgfHwgJ1NlY3Rpb24nIH1cclxuXHRcdFx0XHRcdCk7XHJcblx0XHRcdFx0XHR0aGlzLl9lbmZvcmNlX2RlZmF1bHRfZ3JvdXBfb3BlbigpO1xyXG5cclxuXHRcdFx0XHRcdC8vIC0tLSBTYWZldHkgbmV0OiBpZiBmb3IgYW55IHJlYXNvbiB0aGUgc2xvdCBkaWRu4oCZdCByZW5kZXIgY2hpcHMsIGluamVjdCB0aGVtIG5vdy5cclxuXHRcdFx0XHRcdHRyeSB7XHJcblx0XHRcdFx0XHRcdGNvbnN0IGhhc1Nsb3RIb3N0ID1cclxuXHRcdFx0XHRcdFx0XHRcdCAgdGhpcy5wYW5lbC5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtYmZiLXNsb3Q9XCJsYXlvdXRfY2hpcHNcIl0nICkgfHxcclxuXHRcdFx0XHRcdFx0XHRcdCAgdGhpcy5wYW5lbC5xdWVyeVNlbGVjdG9yKCAnLmluc3BlY3Rvcl9fcm93LS1sYXlvdXQtY2hpcHMgLndwYmNfYmZiX19sYXlvdXRfY2hpcHMnICkgfHxcclxuXHRcdFx0XHRcdFx0XHRcdCAgdGhpcy5wYW5lbC5xdWVyeVNlbGVjdG9yKCAnI3dwYmNfYmZiX19sYXlvdXRfY2hpcHNfaG9zdCcgKTtcclxuXHJcblx0XHRcdFx0XHRcdGNvbnN0IGhhc0NoaXBzID1cclxuXHRcdFx0XHRcdFx0XHRcdCAgISF0aGlzLnBhbmVsLnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2xheW91dF9jaGlwJyApO1xyXG5cclxuXHRcdFx0XHRcdFx0aWYgKCAhaGFzQ2hpcHMgKSB7XHJcblx0XHRcdFx0XHRcdFx0Ly8gQ3JlYXRlIGEgaG9zdCBpZiBtaXNzaW5nIGFuZCByZW5kZXIgY2hpcHMgaW50byBpdC5cclxuXHRcdFx0XHRcdFx0XHRjb25zdCBob3N0ID0gKGZ1bmN0aW9uIGVuc3VyZUhvc3Qocm9vdCkge1xyXG5cdFx0XHRcdFx0XHRcdFx0bGV0IGggPVxyXG5cdFx0XHRcdFx0XHRcdFx0XHRcdHJvb3QucXVlcnlTZWxlY3RvciggJ1tkYXRhLWJmYi1zbG90PVwibGF5b3V0X2NoaXBzXCJdJyApIHx8XHJcblx0XHRcdFx0XHRcdFx0XHRcdFx0cm9vdC5xdWVyeVNlbGVjdG9yKCAnLmluc3BlY3Rvcl9fcm93LS1sYXlvdXQtY2hpcHMgLndwYmNfYmZiX19sYXlvdXRfY2hpcHMnICkgfHxcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHRyb290LnF1ZXJ5U2VsZWN0b3IoICcjd3BiY19iZmJfX2xheW91dF9jaGlwc19ob3N0JyApO1xyXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBoICkgcmV0dXJuIGg7XHJcblx0XHRcdFx0XHRcdFx0XHQvLyBGYWxsYmFjayBob3N0IGluc2lkZSAob3IgYWZ0ZXIpIHRoZSDigJxsYXlvdXTigJ0gZ3JvdXBcclxuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IGZpZWxkcyAgICA9XHJcblx0XHRcdFx0XHRcdFx0XHRcdFx0ICByb290LnF1ZXJ5U2VsZWN0b3IoICcud3BiY19iZmJfX2luc3BlY3Rvcl9fZ3JvdXBbZGF0YS1ncm91cD1cImxheW91dFwiXSAuZ3JvdXBfX2ZpZWxkcycgKSB8fFxyXG5cdFx0XHRcdFx0XHRcdFx0XHRcdCAgcm9vdC5xdWVyeVNlbGVjdG9yKCAnLmdyb3VwX19maWVsZHMnICkgfHwgcm9vdDtcclxuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IHJvdyAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XHJcblx0XHRcdFx0XHRcdFx0XHRyb3cuY2xhc3NOYW1lICAgPSAnaW5zcGVjdG9yX19yb3cgaW5zcGVjdG9yX19yb3ctLWxheW91dC1jaGlwcyc7XHJcblx0XHRcdFx0XHRcdFx0XHRjb25zdCBsYWIgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnbGFiZWwnICk7XHJcblx0XHRcdFx0XHRcdFx0XHRsYWIuY2xhc3NOYW1lICAgPSAnaW5zcGVjdG9yX19sYWJlbCc7XHJcblx0XHRcdFx0XHRcdFx0XHRsYWIudGV4dENvbnRlbnQgPSAnTGF5b3V0JztcclxuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IGN0bCAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XHJcblx0XHRcdFx0XHRcdFx0XHRjdGwuY2xhc3NOYW1lICAgPSAnaW5zcGVjdG9yX19jb250cm9sJztcclxuXHRcdFx0XHRcdFx0XHRcdGggICAgICAgICAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XHJcblx0XHRcdFx0XHRcdFx0XHRoLmNsYXNzTmFtZSAgICAgPSAnd3BiY19iZmJfX2xheW91dF9jaGlwcyc7XHJcblx0XHRcdFx0XHRcdFx0XHRoLnNldEF0dHJpYnV0ZSggJ2RhdGEtYmZiLXNsb3QnLCAnbGF5b3V0X2NoaXBzJyApO1xyXG5cdFx0XHRcdFx0XHRcdFx0Y3RsLmFwcGVuZENoaWxkKCBoICk7XHJcblx0XHRcdFx0XHRcdFx0XHRyb3cuYXBwZW5kQ2hpbGQoIGxhYiApO1xyXG5cdFx0XHRcdFx0XHRcdFx0cm93LmFwcGVuZENoaWxkKCBjdGwgKTtcclxuXHRcdFx0XHRcdFx0XHRcdGZpZWxkcy5hcHBlbmRDaGlsZCggcm93ICk7XHJcblx0XHRcdFx0XHRcdFx0XHRyZXR1cm4gaDtcclxuXHRcdFx0XHRcdFx0XHR9KSggdGhpcy5wYW5lbCApO1xyXG5cclxuXHRcdFx0XHRcdFx0XHRjb25zdCBMID0gKHcuV1BCQ19CRkJfQ29yZSAmJiB3LldQQkNfQkZCX0NvcmUuVUkgJiYgdy5XUEJDX0JGQl9Db3JlLlVJLldQQkNfQkZCX0xheW91dF9DaGlwcykgO1xyXG5cdFx0XHRcdFx0XHRcdGlmICggTCAmJiB0eXBlb2YgTC5yZW5kZXJfZm9yX3NlY3Rpb24gPT09ICdmdW5jdGlvbicgKSB7XHJcblx0XHRcdFx0XHRcdFx0XHRob3N0LmlubmVySFRNTCA9ICcnO1xyXG5cdFx0XHRcdFx0XHRcdFx0TC5yZW5kZXJfZm9yX3NlY3Rpb24oIHRoaXMuYnVpbGRlciwgZWwsIGhvc3QgKTtcclxuXHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH0gY2F0Y2goIGUgKXsgX3dwYmM/LmRldj8uZXJyb3IoICdXUEJDX0JGQl9JbnNwZWN0b3IgLSByZW5kZXInLCBlICk7IH1cclxuXHJcblx0XHRcdFx0XHR0aGlzLl9zZXRfcGFuZWxfc2VjdGlvbl9jb2xzKCBlbCApO1xyXG5cdFx0XHRcdFx0dGhpcy5wYW5lbC5zY3JvbGxUb3AgPSBwcmV2X3Njcm9sbDtcclxuXHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdHRoaXMucGFuZWwuaW5uZXJIVE1MID0gJzxkaXYgY2xhc3M9XCJ3cGJjX2JmYl9faW5zcGVjdG9yX19lbXB0eVwiPlNlbGVjdCBhIGZpZWxkIHRvIGVkaXQgaXRzIG9wdGlvbnMuPC9kaXY+JztcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIEZpZWxkXHJcblx0XHRcdGlmICggIWVsLmNsYXNzTGlzdC5jb250YWlucyggJ3dwYmNfYmZiX19maWVsZCcgKSApIHJldHVybiB0aGlzLmNsZWFyKCk7XHJcblxyXG5cdFx0XHRjb25zdCBkYXRhID0gdy5XUEJDX0JGQl9Db3JlLldQQkNfRm9ybV9CdWlsZGVyX0hlbHBlci5nZXRfYWxsX2RhdGFfYXR0cmlidXRlcyggZWwgKTtcclxuXHRcdFx0Y29uc3QgdHlwZSA9IGRhdGEudHlwZSB8fCAndGV4dCc7XHJcblxyXG5cdFx0XHRmdW5jdGlvbiBfZ2V0X3RwbChpZCkge1xyXG5cdFx0XHRcdGlmICggIXcud3AgfHwgIXdwLnRlbXBsYXRlICkgcmV0dXJuIG51bGw7XHJcblx0XHRcdFx0aWYgKCAhZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICd0bXBsLScgKyBpZCApICkgcmV0dXJuIG51bGw7XHJcblx0XHRcdFx0dHJ5IHtcclxuXHRcdFx0XHRcdHJldHVybiB3cC50ZW1wbGF0ZSggaWQgKTtcclxuXHRcdFx0XHR9IGNhdGNoICggZSApIHtcclxuXHRcdFx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3QgdHBsX2lkICAgICAgPSBgd3BiYy1iZmItaW5zcGVjdG9yLSR7dHlwZX1gO1xyXG5cdFx0XHRjb25zdCB0cGwgICAgICAgICA9IF9nZXRfdHBsKCB0cGxfaWQgKTtcclxuXHRcdFx0Y29uc3QgZ2VuZXJpY190cGwgPSBfZ2V0X3RwbCggJ3dwYmMtYmZiLWluc3BlY3Rvci1nZW5lcmljJyApO1xyXG5cclxuXHRcdFx0Y29uc3Qgc2NoZW1hcyAgICAgICAgID0gdy5XUEJDX0JGQl9TY2hlbWFzIHx8IHt9O1xyXG5cdFx0XHRjb25zdCBzY2hlbWFfZm9yX3R5cGUgPSBzY2hlbWFzW3R5cGVdIHx8IG51bGw7XHJcblx0XHRcdGNvbnN0IEZhY3RvcnkgICAgICAgICA9IHcuV1BCQ19CRkJfQ29yZS5VSSAmJiB3LldQQkNfQkZCX0NvcmUuVUkuV1BCQ19CRkJfSW5zcGVjdG9yX0ZhY3Rvcnk7XHJcblxyXG5cdFx0XHRpZiAoIHRwbCApIHtcclxuXHRcdFx0XHQvLyBORVc6IG1lcmdlIHNjaGVtYSBkZWZhdWx0cyBzbyBtaXNzaW5nIGtleXMgKGVzcC4gYm9vbGVhbnMpIGhvbm9yIGRlZmF1bHRzIG9uIGZpcnN0IHBhaW50XHJcblx0XHRcdFx0Y29uc3QgaGFzT3duID0gRnVuY3Rpb24uY2FsbC5iaW5kKCBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5ICk7XHJcblx0XHRcdFx0Y29uc3QgcHJvcHMgID0gKHNjaGVtYV9mb3JfdHlwZSAmJiBzY2hlbWFfZm9yX3R5cGUuc2NoZW1hICYmIHNjaGVtYV9mb3JfdHlwZS5zY2hlbWEucHJvcHMpID8gc2NoZW1hX2Zvcl90eXBlLnNjaGVtYS5wcm9wcyA6IHt9O1xyXG5cdFx0XHRcdGNvbnN0IG1lcmdlZCA9IHsgLi4uZGF0YSB9O1xyXG5cdFx0XHRcdGlmICggcHJvcHMgKSB7XHJcblx0XHRcdFx0XHRPYmplY3Qua2V5cyggcHJvcHMgKS5mb3JFYWNoKCAoaykgPT4ge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBtZXRhID0gcHJvcHNba10gfHwge307XHJcblx0XHRcdFx0XHRcdGlmICggIWhhc093biggZGF0YSwgayApIHx8IGRhdGFba10gPT09ICcnICkge1xyXG5cdFx0XHRcdFx0XHRcdGlmICggaGFzT3duKCBtZXRhLCAnZGVmYXVsdCcgKSApIHtcclxuXHRcdFx0XHRcdFx0XHRcdC8vIENvZXJjZSBib29sZWFucyB0byBhIHJlYWwgYm9vbGVhbjsgbGVhdmUgb3RoZXJzIGFzLWlzXHJcblx0XHRcdFx0XHRcdFx0XHRtZXJnZWRba10gPSAobWV0YS50eXBlID09PSAnYm9vbGVhbicpID8gISFtZXRhLmRlZmF1bHQgOiBtZXRhLmRlZmF1bHQ7XHJcblx0XHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR9IGVsc2UgaWYgKCBtZXRhLnR5cGUgPT09ICdib29sZWFuJyApIHtcclxuXHRcdFx0XHRcdFx0XHQvLyBOb3JtYWxpemUgdHJ1dGh5IHN0cmluZ3MgaW50byBib29sZWFucyBmb3IgdGVtcGxhdGVzIHRoYXQgY2hlY2sgb24gdHJ1dGhpbmVzc1xyXG5cdFx0XHRcdFx0XHRcdGNvbnN0IHYgICA9IGRhdGFba107XHJcblx0XHRcdFx0XHRcdFx0bWVyZ2VkW2tdID0gKHYgPT09IHRydWUgfHwgdiA9PT0gJ3RydWUnIHx8IHYgPT09IDEgfHwgdiA9PT0gJzEnKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHR0aGlzLnBhbmVsLmlubmVySFRNTCA9IHRwbCggbWVyZ2VkICk7XHJcblxyXG5cdFx0XHRcdHRoaXMuX3Bvc3RfcmVuZGVyX3VpKCk7XHJcblx0XHRcdH0gZWxzZSBpZiAoIHNjaGVtYV9mb3JfdHlwZSAmJiBGYWN0b3J5ICkge1xyXG5cdFx0XHRcdHRoaXMucGFuZWwuaW5uZXJIVE1MID0gJyc7XHJcblx0XHRcdFx0RmFjdG9yeS5yZW5kZXIoXHJcblx0XHRcdFx0XHR0aGlzLnBhbmVsLFxyXG5cdFx0XHRcdFx0c2NoZW1hX2Zvcl90eXBlLFxyXG5cdFx0XHRcdFx0eyAuLi5kYXRhIH0sXHJcblx0XHRcdFx0XHR7IGVsLCBidWlsZGVyOiB0aGlzLmJ1aWxkZXIsIHR5cGUsIHRpdGxlOiBkYXRhLmxhYmVsIHx8ICcnIH1cclxuXHRcdFx0XHQpO1xyXG5cdFx0XHRcdC8vIEVuc3VyZSB0b2dnbGUgbm9ybWFsaXplcnMgYW5kIHNsaWRlci9udW1iZXIvdW5pdCB3aXJpbmcgYXJlIGF0dGFjaGVkLlxyXG5cdFx0XHRcdHRoaXMuX3Bvc3RfcmVuZGVyX3VpKCk7XHJcblx0XHRcdH0gZWxzZSBpZiAoIGdlbmVyaWNfdHBsICkge1xyXG5cdFx0XHRcdHRoaXMucGFuZWwuaW5uZXJIVE1MID0gZ2VuZXJpY190cGwoIHsgLi4uZGF0YSB9ICk7XHJcblx0XHRcdFx0dGhpcy5fcG9zdF9yZW5kZXJfdWkoKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHJcblx0XHRcdFx0Y29uc3QgbXNnICAgICAgICAgICAgPSBgVGhlcmUgYXJlIG5vIEluc3BlY3RvciB3cC50ZW1wbGF0ZSBcIiR7dHBsX2lkfVwiIG9yIFNjaGVtYSBmb3IgdGhpcyBcIiR7U3RyaW5nKCB0eXBlIHx8ICcnICl9XCIgZWxlbWVudC5gO1xyXG5cdFx0XHRcdHRoaXMucGFuZWwuaW5uZXJIVE1MID0gJyc7XHJcblx0XHRcdFx0Y29uc3QgZGl2ICAgICAgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xyXG5cdFx0XHRcdGRpdi5jbGFzc05hbWUgICAgICAgID0gJ3dwYmNfYmZiX19pbnNwZWN0b3JfX2VtcHR5JztcclxuXHRcdFx0XHRkaXYudGV4dENvbnRlbnQgICAgICA9IG1zZzsgLy8gc2FmZS5cclxuXHRcdFx0XHR0aGlzLnBhbmVsLmFwcGVuZENoaWxkKCBkaXYgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5fZW5mb3JjZV9kZWZhdWx0X2dyb3VwX29wZW4oKTtcclxuXHRcdFx0dGhpcy5wYW5lbC5zY3JvbGxUb3AgPSBwcmV2X3Njcm9sbDtcclxuXHRcdH1cclxuXHJcblx0XHRfZW5mb3JjZV9kZWZhdWx0X2dyb3VwX29wZW4oKSB7XHJcblx0XHRcdGNvbnN0IGdyb3VwcyA9IEFycmF5LmZyb20oIHRoaXMucGFuZWwucXVlcnlTZWxlY3RvckFsbCggJy53cGJjX2JmYl9faW5zcGVjdG9yX19ncm91cCcgKSApO1xyXG5cdFx0XHRpZiAoICFncm91cHMubGVuZ3RoICkgcmV0dXJuO1xyXG5cclxuXHRcdFx0bGV0IGZvdW5kID0gZmFsc2U7XHJcblx0XHRcdGdyb3Vwcy5mb3JFYWNoKCAoZykgPT4ge1xyXG5cdFx0XHRcdGlmICggIWZvdW5kICYmIGcuY2xhc3NMaXN0LmNvbnRhaW5zKCAnaXMtb3BlbicgKSApIHtcclxuXHRcdFx0XHRcdGZvdW5kID0gdHJ1ZTtcclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0aWYgKCBnLmNsYXNzTGlzdC5jb250YWlucyggJ2lzLW9wZW4nICkgKSB7XHJcblx0XHRcdFx0XHRcdGcuY2xhc3NMaXN0LnJlbW92ZSggJ2lzLW9wZW4nICk7XHJcblx0XHRcdFx0XHRcdGcuZGlzcGF0Y2hFdmVudCggbmV3IEV2ZW50KCAnd3BiYzpjb2xsYXBzaWJsZTpjbG9zZScsIHsgYnViYmxlczogdHJ1ZSB9ICkgKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdGcuY2xhc3NMaXN0LnJlbW92ZSggJ2lzLW9wZW4nICk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0XHRpZiAoICFmb3VuZCApIHtcclxuXHRcdFx0XHRncm91cHNbMF0uY2xhc3NMaXN0LmFkZCggJ2lzLW9wZW4nICk7XHJcblx0XHRcdFx0Z3JvdXBzWzBdLmRpc3BhdGNoRXZlbnQoIG5ldyBFdmVudCggJ3dwYmM6Y29sbGFwc2libGU6b3BlbicsIHsgYnViYmxlczogdHJ1ZSB9ICkgKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU2V0IGRhdGEtYmZiLXNlY3Rpb24tY29scyBvbiB0aGUgaW5zcGVjdG9yIHBhbmVsIGJhc2VkIG9uIHRoZSBjdXJyZW50IHNlY3Rpb24uXHJcblx0XHQgKiBVc2VzIHRoZSByZWdpc3RlcmVkIGNvbXB1dGUgZm4gaWYgYXZhaWxhYmxlOyBmYWxscyBiYWNrIHRvIGRpcmVjdCBET00uXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBzZWN0aW9uRWxcclxuXHRcdCAqL1xyXG5cdFx0X3NldF9wYW5lbF9zZWN0aW9uX2NvbHMoc2VjdGlvbkVsKSB7XHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0Ly8gUHJlZmVyIHRoZSBhbHJlYWR5LXJlZ2lzdGVyZWQgdmFsdWVfZnJvbSBoZWxwZXIgaWYgcHJlc2VudC5cclxuXHRcdFx0XHR2YXIgY29tcHV0ZSA9IHcud3BiY19iZmJfaW5zcGVjdG9yX2ZhY3RvcnlfdmFsdWVfZnJvbSAmJiB3LndwYmNfYmZiX2luc3BlY3Rvcl9mYWN0b3J5X3ZhbHVlX2Zyb20uY29tcHV0ZV9zZWN0aW9uX2NvbHVtbnM7XHJcblxyXG5cdFx0XHRcdHZhciBjb2xzID0gMTtcclxuXHRcdFx0XHRpZiAoIHR5cGVvZiBjb21wdXRlID09PSAnZnVuY3Rpb24nICkge1xyXG5cdFx0XHRcdFx0Y29scyA9IGNvbXB1dGUoIHsgZWw6IHNlY3Rpb25FbCB9ICkgfHwgMTtcclxuXHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0Ly8gRmFsbGJhY2s6IGNvbXB1dGUgZGlyZWN0bHkgZnJvbSB0aGUgRE9NLlxyXG5cdFx0XHRcdFx0dmFyIHJvdyA9IHNlY3Rpb25FbCAmJiBzZWN0aW9uRWwucXVlcnlTZWxlY3RvciggJzpzY29wZSA+IC53cGJjX2JmYl9fcm93JyApO1xyXG5cdFx0XHRcdFx0Y29scyAgICA9IHJvdyA/IChyb3cucXVlcnlTZWxlY3RvckFsbCggJzpzY29wZSA+IC53cGJjX2JmYl9fY29sdW1uJyApLmxlbmd0aCB8fCAxKSA6IDE7XHJcblx0XHRcdFx0XHRpZiAoIGNvbHMgPCAxICkgY29scyA9IDE7XHJcblx0XHRcdFx0XHRpZiAoIGNvbHMgPiA0ICkgY29scyA9IDQ7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHRoaXMucGFuZWwuc2V0QXR0cmlidXRlKCAnZGF0YS1iZmItc2VjdGlvbi1jb2xzJywgU3RyaW5nKCBjb2xzICkgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF8gKSB7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblxyXG5cdFx0X2NyZWF0ZV9mYWxsYmFja19wYW5lbCgpIHtcclxuXHRcdFx0Y29uc3QgcCAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xyXG5cdFx0XHRwLmlkICAgICAgICA9ICd3cGJjX2JmYl9faW5zcGVjdG9yJztcclxuXHRcdFx0cC5jbGFzc05hbWUgPSAnd3BiY19iZmJfX2luc3BlY3Rvcic7XHJcblx0XHRcdGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoIHAgKTtcclxuXHRcdFx0cmV0dXJuIC8qKiBAdHlwZSB7SFRNTERpdkVsZW1lbnR9ICovIChwKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdC8vIEV4cG9ydCBjbGFzcyArIHJlYWR5IHNpZ25hbC5cclxuXHR3LldQQkNfQkZCX0luc3BlY3RvciA9IFdQQkNfQkZCX0luc3BlY3RvcjtcclxuXHRkb2N1bWVudC5kaXNwYXRjaEV2ZW50KCBuZXcgRXZlbnQoICd3cGJjX2JmYl9pbnNwZWN0b3JfcmVhZHknICkgKTtcclxuXHJcbn0pKCB3aW5kb3cgKTtcclxuIl19
7145