PluginProbe
Booking Calendar / 11.4.1
Booking Calendar v11.4.1
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 / all / _src / wpbc_all_admin.js

wpbc_all_admin.js in Booking Calendar 11.4.1, at _dist/all/_src/wpbc_all_admin.js

2,339 lines 204.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2 /**
3 * Blink specific HTML element to set attention to this element.
4 *
5 * @param {string} element_to_blink - class or id of element: '.wpbc_widget_available_unavailable'
6 * @param {int} how_many_times - 4
7 * @param {int} how_long_to_blink - 350
8 */
9 function wpbc_blink_element( element_to_blink, how_many_times = 4, how_long_to_blink = 350 ){
10
11 for ( let i = 0; i < how_many_times; i++ ){
12 jQuery( element_to_blink ).fadeOut( how_long_to_blink ).fadeIn( how_long_to_blink );
13 }
14 jQuery( element_to_blink ).animate( {opacity: 1}, 500 );
15 }
16
17 /**
18 * Support Functions - Spin Icon in Buttons ------------------------------------------------------------------ */
19
20 /**
21 * Remove spin icon from button and Enable this button.
22 *
23 * @param button_clicked_element_id - HTML ID attribute of this button
24 * @return string - CSS classes that was previously in button icon
25 */
26 function wpbc_button__remove_spin(button_clicked_element_id) {
27
28 var previos_classes = '';
29 if (
30 (undefined != button_clicked_element_id)
31 && ('' != button_clicked_element_id)
32 ) {
33 var jElement = jQuery( '#' + button_clicked_element_id );
34 if ( jElement.length ) {
35 previos_classes = wpbc_button_disable_loading_icon( jElement.get( 0 ) );
36 }
37 }
38
39 return previos_classes;
40 }
41
42
43 /**
44 * Show Loading (rotating arrow) icon for button that has been clicked
45 *
46 * @param this_button - this object of specific button
47 * @return string - CSS classes that was previously in button icon
48 */
49 function wpbc_button_enable_loading_icon(this_button) {
50
51 var jButton = jQuery( this_button );
52 var jIcon = jButton.find( 'i' );
53 var previos_classes = jIcon.attr( 'class' );
54
55 jIcon.removeClass().addClass( 'menu_icon icon-1x wpbc_icn_rotate_right wpbc_spin' ); // Set Rotate icon.
56 // jIcon.addClass( 'wpbc_animation_pause' ); // Pause animation.
57 // jIcon.addClass( 'wpbc_ui_red' ); // Set icon color red.
58
59 jIcon.attr( 'wpbc_previous_class', previos_classes )
60
61 jButton.addClass( 'disabled' ); // Disable button
62 // We need to set here attr instead of prop, because for A elements, attribute 'disabled' do not added with jButton.prop( "disabled", true );.
63
64 jButton.attr( 'wpbc_previous_onclick', jButton.attr( 'onclick' ) ); // Save this value.
65 jButton.attr( 'onclick', '' ); // Disable actions "on click".
66
67 return previos_classes;
68 }
69
70
71 /**
72 * Hide Loading (rotating arrow) icon for button that was clicked and show previous icon and enable button
73 *
74 * @param this_button - this object of specific button
75 * @return string - CSS classes that was previously in button icon
76 */
77 function wpbc_button_disable_loading_icon(this_button) {
78
79 var jButton = jQuery( this_button );
80 var jIcon = jButton.find( 'i' );
81
82 var previos_classes = jIcon.attr( 'wpbc_previous_class' );
83 if (
84 (undefined != previos_classes)
85 && ('' != previos_classes)
86 ) {
87 jIcon.removeClass().addClass( previos_classes );
88 }
89
90 jButton.removeClass( 'disabled' ); // Remove Disable button.
91
92 var previous_onclick = jButton.attr( 'wpbc_previous_onclick' )
93 if (
94 (undefined != previous_onclick)
95 && ('' != previous_onclick)
96 ) {
97 jButton.attr( 'onclick', previous_onclick );
98 }
99
100 return previos_classes;
101 }
102
103 /**
104 * On selection of radio button, adjust attributes of radio container
105 *
106 * @param _this
107 */
108 function wpbc_ui_el__radio_container_selection(_this) {
109
110 if ( jQuery( _this ).is( ':checked' ) ) {
111 jQuery( _this ).parents( '.wpbc_ui_radio_section' ).find( '.wpbc_ui_radio_container' ).removeAttr( 'data-selected' );
112 jQuery( _this ).parents( '.wpbc_ui_radio_container:not(.disabled)' ).attr( 'data-selected', true );
113 }
114
115 if ( jQuery( _this ).is( ':disabled' ) ) {
116 jQuery( _this ).parents( '.wpbc_ui_radio_container' ).addClass( 'disabled' );
117 }
118 }
119
120 /**
121 * On click on Radio Container, we will select the radio button and then adjust attributes of radio container
122 *
123 * @param _this
124 */
125 function wpbc_ui_el__radio_container_click(_this) {
126
127 if ( jQuery( _this ).hasClass( 'disabled' ) ) {
128 return false;
129 }
130
131 var j_radio = jQuery( _this ).find( 'input[type=radio]:not(.wpbc-form-radio-internal)' );
132 if ( j_radio.length ) {
133 j_radio.prop( 'checked', true ).trigger( 'change' );
134 }
135
136 }
137 "use strict";
138 // =====================================================================================================================
139 // == Full Screen - support functions ==
140 // =====================================================================================================================
141
142 /**
143 * Save Full Screen preference in a short browser cookie.
144 *
145 * This makes the next admin page load deterministic even if the async user-meta
146 * AJAX request is interrupted by immediate navigation.
147 *
148 * @param string value 'On' or 'Off'.
149 */
150 function wpbc_admin_ui__full_screen__set_cookie( value ) {
151 var max_age = 60 * 60 * 24 * 365;
152
153 document.cookie = 'wpbc_admin_full_screen=' + encodeURIComponent( value ) + '; path=/; max-age=' + max_age + '; SameSite=Lax';
154 }
155
156 /**
157 * Apply Full Screen mode from a user click.
158 *
159 * @param HTMLElement el Clicked control.
160 * @param bool is_save_user_state Whether to save user preference.
161 */
162 function wpbc_admin_ui__full_screen__do_on( el, is_save_user_state ) {
163 jQuery( 'body' ).addClass( 'wpbc_admin_full_screen' );
164 wpbc_check_full_screen_mode();
165
166 if ( is_save_user_state ) {
167 wpbc_admin_ui__full_screen__set_cookie( 'On' );
168
169 if ( 'function' === typeof wpbc_save_custom_user_data_from_element ) {
170 wpbc_save_custom_user_data_from_element( el );
171 }
172 }
173 }
174
175 /**
176 * Exit Full Screen mode from a user click.
177 *
178 * @param HTMLElement el Clicked control.
179 * @param bool is_save_user_state Whether to save user preference.
180 */
181 function wpbc_admin_ui__full_screen__do_off( el, is_save_user_state ) {
182 jQuery( 'body' ).removeClass( 'wpbc_admin_full_screen' );
183 wpbc_check_full_screen_mode();
184
185 if ( is_save_user_state ) {
186 wpbc_admin_ui__full_screen__set_cookie( 'Off' );
187
188 if ( 'function' === typeof wpbc_save_custom_user_data_from_element ) {
189 wpbc_save_custom_user_data_from_element( el );
190 }
191 }
192 }
193
194 /**
195 * Check Full screen mode, by removing top tab
196 */
197 function wpbc_check_full_screen_mode(){
198 if ( jQuery( 'body' ).hasClass( 'wpbc_admin_full_screen' ) ) {
199 jQuery( 'html' ).removeClass( 'wp-toolbar' );
200 } else {
201 jQuery( 'html' ).addClass( 'wp-toolbar' );
202 }
203 wpbc_check_buttons_max_min_in_full_screen_mode();
204 }
205
206 function wpbc_check_buttons_max_min_in_full_screen_mode() {
207 if ( jQuery( 'body' ).hasClass( 'wpbc_admin_full_screen' ) ) {
208 jQuery( '.wpbc_ui__top_nav__btn_full_screen' ).addClass( 'wpbc_ui__hide' );
209 jQuery( '.wpbc_ui__top_nav__btn_normal_screen' ).removeClass( 'wpbc_ui__hide' );
210 } else {
211 jQuery( '.wpbc_ui__top_nav__btn_full_screen' ).removeClass( 'wpbc_ui__hide' );
212 jQuery( '.wpbc_ui__top_nav__btn_normal_screen' ).addClass( 'wpbc_ui__hide' );
213 }
214 }
215
216 jQuery( document ).ready( function () {
217 wpbc_check_full_screen_mode();
218 } );
219
220 /**
221 * Checkbox Selection functions for Listing.
222 */
223
224 /**
225 * Selections of several checkboxes like in gMail with shift :)
226 * Need to have this structure:
227 * .wpbc_selectable_table
228 * .wpbc_selectable_head
229 * .check-column
230 * :checkbox
231 * .wpbc_selectable_body
232 * .wpbc_row
233 * .check-column
234 * :checkbox
235 * .wpbc_selectable_foot
236 * .check-column
237 * :checkbox
238 */
239 function wpbc_define_gmail_checkbox_selection( $ ){
240
241 var checks, first, last, checked, sliced, lastClicked = false;
242
243 // Check all checkboxes.
244 $( '.wpbc_selectable_body' ).find( '.check-column' ).find( ':checkbox' ).on(
245 'click',
246 function (e) {
247 if ( 'undefined' == e.shiftKey ) {
248 return true;
249 }
250 if ( e.shiftKey ) {
251 if ( ! lastClicked ) {
252 return true;
253 }
254 checks = $( lastClicked ).closest( '.wpbc_selectable_body' ).find( ':checkbox' ).filter( ':visible:enabled' );
255 first = checks.index( lastClicked );
256 last = checks.index( this );
257 checked = $( this ).prop( 'checked' );
258 if ( 0 < first && 0 < last && first != last ) {
259 sliced = (last > first) ? checks.slice( first, last ) : checks.slice( last, first );
260 sliced.prop(
261 'checked',
262 function () {
263 if ( $( this ).closest( '.wpbc_row' ).is( ':visible' ) ) {
264 return checked;
265 }
266 return false;
267 }
268 ).trigger( 'change' );
269 }
270 }
271 lastClicked = this;
272
273 // toggle "check all" checkboxes.
274 var unchecked = $( this ).closest( '.wpbc_selectable_body' ).find( ':checkbox' ).filter( ':visible:enabled' ).not( ':checked' );
275 $( this ).closest( '.wpbc_selectable_table' ).children( '.wpbc_selectable_head, .wpbc_selectable_foot' ).find( ':checkbox' ).prop(
276 'checked',
277 function () {
278 return (0 === unchecked.length);
279 }
280 ).trigger( 'change' );
281
282 return true;
283 }
284 );
285
286 // Head || Foot clicking to select / deselect ALL.
287 $( '.wpbc_selectable_head, .wpbc_selectable_foot' ).find( '.check-column :checkbox' ).on(
288 'click',
289 function (event) {
290 var $this = $( this ),
291 $table = $this.closest( '.wpbc_selectable_table' ),
292 controlChecked = $this.prop( 'checked' ),
293 toggle = event.shiftKey || $this.data( 'wp-toggle' );
294
295 $table.children( '.wpbc_selectable_body' ).filter( ':visible' )
296 .find( '.check-column' ).find( ':checkbox' )
297 .prop(
298 'checked',
299 function () {
300 if ( $( this ).is( ':hidden,:disabled' ) ) {
301 return false;
302 }
303 if ( toggle ) {
304 return ! $( this ).prop( 'checked' );
305 } else if ( controlChecked ) {
306 return true;
307 }
308 return false;
309 }
310 ).trigger( 'change' );
311
312 $table.children( '.wpbc_selectable_head, .wpbc_selectable_foot' ).filter( ':visible' )
313 .find( '.check-column' ).find( ':checkbox' )
314 .prop(
315 'checked',
316 function () {
317 if ( toggle ) {
318 return false;
319 } else if ( controlChecked ) {
320 return true;
321 }
322 return false;
323 }
324 );
325 }
326 );
327
328
329 // Visually show selected border.
330 $( '.wpbc_selectable_body' ).find( '.check-column :checkbox' ).on(
331 'change',
332 function (event) {
333 if ( jQuery( this ).is( ':checked' ) ) {
334 jQuery( this ).closest( '.wpbc_list_row' ).addClass( 'row_selected_color' );
335 } else {
336 jQuery( this ).closest( '.wpbc_list_row' ).removeClass( 'row_selected_color' );
337 }
338
339 // Disable text selection while pressing 'shift'.
340 document.getSelection().removeAllRanges();
341
342 // Show or hide buttons on Actions toolbar at Booking Listing page, if we have some selected bookings.
343 wpbc_show_hide_action_buttons_for_selected_bookings();
344 }
345 );
346
347 wpbc_show_hide_action_buttons_for_selected_bookings();
348 }
349
350
351 /**
352 * Get ID array of selected elements
353 */
354 function wpbc_get_selected_row_id() {
355
356 var $table = jQuery( '.wpbc__wrap__booking_listing .wpbc_selectable_table' );
357 var checkboxes = $table.children( '.wpbc_selectable_body' ).filter( ':visible' ).find( '.check-column' ).find( ':checkbox' );
358 var selected_id = [];
359
360 jQuery.each(
361 checkboxes,
362 function (key, checkbox) {
363 if ( jQuery( checkbox ).is( ':checked' ) ) {
364 var element_id = wpbc_get_row_id_from_element( checkbox );
365 selected_id.push( element_id );
366 }
367 }
368 );
369
370 return selected_id;
371 }
372
373
374 /**
375 * Get ID of row, based on clciked element
376 *
377 * @param this_inbound_element - ususlly this
378 * @returns {number}
379 */
380 function wpbc_get_row_id_from_element(this_inbound_element) {
381
382 var element_id = jQuery( this_inbound_element ).closest( '.wpbc_listing_usual_row' ).attr( 'id' );
383
384 element_id = parseInt( element_id.replace( 'row_id_', '' ) );
385
386 return element_id;
387 }
388
389
390 /**
391 * == Booking Listing == Show or hide buttons on Actions toolbar at page, if we have some selected bookings.
392 */
393 function wpbc_show_hide_action_buttons_for_selected_bookings(){
394
395 var selected_rows_arr = wpbc_get_selected_row_id();
396
397 if ( selected_rows_arr.length > 0 ) {
398 jQuery( '.hide_button_if_no_selection' ).show();
399 } else {
400 jQuery( '.hide_button_if_no_selection' ).hide();
401 }
402 }
403 "use strict";
404 // =====================================================================================================================
405 // == Left Bar - expand / colapse functions ==
406 // =====================================================================================================================
407
408 /**
409 * Save user's preferred left sidebar mode.
410 *
411 * @param string mode
412 */
413 function wpbc_admin_ui__sidebar_left__save_mode( mode ) {
414 var allowed_modes = [ 'min', 'compact', 'max' ];
415
416 if ( allowed_modes.indexOf( mode ) === -1 ) {
417 return;
418 }
419
420 var $saver = jQuery( '#wpbc_left_sidebar_view_mode_saver' );
421
422 if ( ! $saver.length ) {
423 return;
424 }
425
426 if ( 'function' !== typeof wpbc_save_custom_user_data_from_element ) {
427 return;
428 }
429
430 $saver.data( 'wpbc-u-save-value', mode );
431 $saver.attr( 'data-wpbc-u-save-value', mode );
432
433 wpbc_save_custom_user_data_from_element( $saver.get( 0 ) );
434 }
435
436 /**
437 * Expand Vertical Left Bar.
438 *
439 * @param bool is_save_user_state Save this mode as user's preference.
440 */
441 function wpbc_admin_ui__sidebar_left__do_max( is_save_user_state ) {
442 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min max compact none' );
443 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'max' );
444 jQuery( '.wpbc_ui__top_nav__btn_open_left_vertical_nav' ).addClass( 'wpbc_ui__hide' );
445 jQuery( '.wpbc_ui__top_nav__btn_hide_left_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
446
447 jQuery( '.wp-admin' ).removeClass( 'wpbc_page_wrapper_left_min wpbc_page_wrapper_left_max wpbc_page_wrapper_left_compact wpbc_page_wrapper_left_none' );
448 jQuery( '.wp-admin' ).addClass( 'wpbc_page_wrapper_left_max' );
449
450 if ( is_save_user_state ) {
451 wpbc_admin_ui__sidebar_left__save_mode( 'max' );
452 }
453 }
454
455 /**
456 * Hide Vertical Left Bar.
457 *
458 * @param bool is_save_user_state Save this mode as user's preference.
459 */
460 function wpbc_admin_ui__sidebar_left__do_min( is_save_user_state ) {
461 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min max compact none' );
462 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'min' );
463 jQuery( '.wpbc_ui__top_nav__btn_open_left_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
464 jQuery( '.wpbc_ui__top_nav__btn_hide_left_vertical_nav' ).addClass( 'wpbc_ui__hide' );
465
466 jQuery( '.wp-admin' ).removeClass( 'wpbc_page_wrapper_left_min wpbc_page_wrapper_left_max wpbc_page_wrapper_left_compact wpbc_page_wrapper_left_none' );
467 jQuery( '.wp-admin' ).addClass( 'wpbc_page_wrapper_left_min' );
468
469 if ( is_save_user_state ) {
470 wpbc_admin_ui__sidebar_left__save_mode( 'min' );
471 }
472 }
473
474 /**
475 * Colapse Vertical Left Bar.
476 *
477 * @param bool is_save_user_state Save this mode as user's preference.
478 */
479 function wpbc_admin_ui__sidebar_left__do_compact( is_save_user_state ) {
480 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min max compact none' );
481 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'compact' );
482 jQuery( '.wpbc_ui__top_nav__btn_open_left_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
483 jQuery( '.wpbc_ui__top_nav__btn_hide_left_vertical_nav' ).addClass( 'wpbc_ui__hide' );
484
485 jQuery( '.wp-admin' ).removeClass( 'wpbc_page_wrapper_left_min wpbc_page_wrapper_left_max wpbc_page_wrapper_left_compact wpbc_page_wrapper_left_none' );
486 jQuery( '.wp-admin' ).addClass( 'wpbc_page_wrapper_left_compact' );
487
488 if ( is_save_user_state ) {
489 wpbc_admin_ui__sidebar_left__save_mode( 'compact' );
490 }
491 }
492
493 /**
494 * Completely Hide Vertical Left Bar.
495 */
496 function wpbc_admin_ui__sidebar_left__do_hide() {
497 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min max compact none' );
498 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'none' );
499 jQuery( '.wpbc_ui__top_nav__btn_open_left_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
500 jQuery( '.wpbc_ui__top_nav__btn_hide_left_vertical_nav' ).addClass( 'wpbc_ui__hide' );
501 // Hide top "Menu" button with divider.
502 jQuery( '.wpbc_ui__top_nav__btn_show_left_vertical_nav,.wpbc_ui__top_nav__btn_show_left_vertical_nav_divider' ).addClass( 'wpbc_ui__hide' );
503
504 jQuery( '.wp-admin' ).removeClass( 'wpbc_page_wrapper_left_min wpbc_page_wrapper_left_max wpbc_page_wrapper_left_compact wpbc_page_wrapper_left_none' );
505 jQuery( '.wp-admin' ).addClass( 'wpbc_page_wrapper_left_none' );
506 }
507
508 /**
509 * Action on click "Go Back" - show root menu
510 * or some other section in left sidebar.
511 *
512 * @param string menu_to_show - menu slug.
513 */
514 function wpbc_admin_ui__sidebar_left__show_section( menu_to_show ) {
515 jQuery( '.wpbc_ui_el__vert_left_bar__section' ).addClass( 'wpbc_ui__hide' )
516 jQuery( '.wpbc_ui_el__vert_left_bar__section_' + menu_to_show ).removeClass( 'wpbc_ui__hide' );
517 }
518
519 // =====================================================================================================================
520 // == Right Side Bar - expand / colapse functions ==
521 // =====================================================================================================================
522
523 /**
524 * Expand Vertical Right Bar.
525 */
526 function wpbc_admin_ui__sidebar_right__do_max() {
527 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min_right max_right compact_right none_right' );
528 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'max_right' );
529 jQuery( '.wpbc_ui__top_nav__btn_open_right_vertical_nav' ).addClass( 'wpbc_ui__hide' );
530 jQuery( '.wpbc_ui__top_nav__btn_hide_right_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
531 }
532
533 /**
534 * Hide Vertical Right Bar.
535 */
536 function wpbc_admin_ui__sidebar_right__do_min() {
537 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min_right max_right compact_right none_right' );
538 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'min_right' );
539 jQuery( '.wpbc_ui__top_nav__btn_open_right_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
540 jQuery( '.wpbc_ui__top_nav__btn_hide_right_vertical_nav' ).addClass( 'wpbc_ui__hide' );
541 }
542
543 /**
544 * Colapse Vertical Right Bar.
545 */
546 function wpbc_admin_ui__sidebar_right__do_compact() {
547 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min_right max_right compact_right none_right' );
548 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'compact_right' );
549 jQuery( '.wpbc_ui__top_nav__btn_open_right_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
550 jQuery( '.wpbc_ui__top_nav__btn_hide_right_vertical_nav' ).addClass( 'wpbc_ui__hide' );
551 }
552
553 /**
554 * Completely Hide Vertical Right Bar.
555 */
556 function wpbc_admin_ui__sidebar_right__do_hide() {
557 jQuery( '.wpbc_settings_page_wrapper' ).removeClass( 'min_right max_right compact_right none_right' );
558 jQuery( '.wpbc_settings_page_wrapper' ).addClass( 'none_right' );
559 jQuery( '.wpbc_ui__top_nav__btn_open_right_vertical_nav' ).removeClass( 'wpbc_ui__hide' );
560 jQuery( '.wpbc_ui__top_nav__btn_hide_right_vertical_nav' ).addClass( 'wpbc_ui__hide' );
561 // Hide top "Menu" button with divider.
562 jQuery( '.wpbc_ui__top_nav__btn_show_right_vertical_nav,.wpbc_ui__top_nav__btn_show_right_vertical_nav_divider' ).addClass( 'wpbc_ui__hide' );
563 }
564
565 /**
566 * Action on click "Go Back" - show root menu
567 * or some other section in right sidebar.
568 *
569 * @param string menu_to_show - menu slug.
570 */
571 function wpbc_admin_ui__sidebar_right__show_section( menu_to_show ) {
572 jQuery( '.wpbc_ui_el__vert_right_bar__section' ).addClass( 'wpbc_ui__hide' )
573 jQuery( '.wpbc_ui_el__vert_right_bar__section_' + menu_to_show ).removeClass( 'wpbc_ui__hide' );
574 }
575
576 // =====================================================================================================================
577 // == End Right Side Bar section ==
578 // =====================================================================================================================
579
580 /**
581 * Get anchor(s) array from URL.
582 * Doc: https://developer.mozilla.org/en-US/docs/Web/API/Location
583 *
584 * @returns {*[]}
585 */
586 function wpbc_url_get_anchors_arr() {
587 var hashes = window.location.hash.replace( '%23', '#' );
588 var hashes_arr = hashes.split( '#' );
589 var result = [];
590 var hashes_arr_length = hashes_arr.length;
591
592 for ( var i = 0; i < hashes_arr_length; i++ ) {
593 if ( hashes_arr[i].length > 0 ) {
594 result.push( hashes_arr[i] );
595 }
596 }
597 return result;
598 }
599
600 /**
601 * Auto Expand Settings section based on URL anchor, after page loaded.
602 */
603 jQuery( document ).ready( function () { wpbc_admin_ui__redirect_legacy_general_availability_url(); } );
604 jQuery( document ).ready( function () { wpbc_admin_ui__do_expand_section(); setTimeout( 'wpbc_admin_ui__do_expand_section', 10 ); } );
605 jQuery( document ).ready( function () { wpbc_admin_ui__do_expand_section(); setTimeout( 'wpbc_admin_ui__do_expand_section', 150 ); } );
606
607 /**
608 * Redirect old Settings > Availability anchors to the dedicated General Availability page.
609 */
610 function wpbc_admin_ui__redirect_legacy_general_availability_url() {
611
612 if (
613 ( window.location.href.indexOf( 'page=wpbc-settings' ) > -1 )
614 && (
615 ( window.location.hash.indexOf( 'wpbc_general_settings_availability_metabox' ) > -1 )
616 || ( window.location.hash.indexOf( 'wpbc_general_settings_availability_tab' ) > -1 )
617 )
618 ) {
619 window.location.replace( window.location.href.split( '?' )[0] + '?page=wpbc-availability&tab=general_availability' );
620 }
621 }
622
623 /**
624 * Expand section in General Settings page and select Menu item.
625 */
626 function wpbc_admin_ui__do_expand_section() {
627
628 // window.location.hash = #section_id / doc: https://developer.mozilla.org/en-US/docs/Web/API/Location .
629 var anchors_arr = wpbc_url_get_anchors_arr();
630 var anchors_arr_length = anchors_arr.length;
631
632 if ( anchors_arr_length > 0 ) {
633 var one_anchor_prop_value = anchors_arr[0].split( 'do_expand__' );
634 if ( one_anchor_prop_value.length > 1 ) {
635
636 // 'wpbc_general_settings_calendar_metabox'
637 var section_to_show = one_anchor_prop_value[1];
638 var section_id_to_show = '#' + section_to_show;
639
640
641 // -- Remove selected background in all left menu items ---------------------------------------------------
642 jQuery( '.wpbc_ui_el__vert_nav_item ' ).removeClass( 'active' );
643 // Set left menu selected.
644 jQuery( '.do_expand__' + section_to_show + '_link' ).addClass( 'active' );
645 var selected_title = jQuery( '.do_expand__' + section_to_show + '_link a .wpbc_ui_el__vert_nav_title ' ).text();
646
647 // Expand section, if it colapsed.
648 if ( ! jQuery( '.do_expand__' + section_to_show + '_link' ).parents( '.wpbc_ui_el__level__folder' ).hasClass( 'expanded' ) ) {
649 jQuery( '.wpbc_ui_el__level__folder' ).removeClass( 'expanded' );
650 jQuery( '.do_expand__' + section_to_show + '_link' ).parents( '.wpbc_ui_el__level__folder' ).addClass( 'expanded' );
651 }
652
653 // -- Expand section ---------------------------------------------------------------------------------------
654 var container_to_hide_class = '.postbox';
655 // Hide sections '.postbox' in admin page and show specific one.
656 jQuery( '.wpbc_admin_page ' + container_to_hide_class ).hide();
657 jQuery( '.wpbc_container_always_hide__on_left_nav_click' ).hide();
658 jQuery( section_id_to_show ).show();
659
660 // Show all other sections, if provided in URL: ..?page=wpbc-settings#do_expand__wpbc_general_settings_capacity_metabox#wpbc_general_settings_capacity_upgrade_metabox .
661 for ( let i = 1; i < anchors_arr_length; i++ ) {
662 jQuery( '#' + anchors_arr[i] ).show();
663 }
664
665 if ( false ) {
666 var targetOffset = wpbc_scroll_to( section_id_to_show );
667 }
668
669 // -- Set Value to Input about selected Nav element --------------------------------------------------------------- // FixIn: 9.8.6.1.
670 var section_id_tab = section_id_to_show.substring( 0, section_id_to_show.length - 8 ) + '_tab';
671 if ( container_to_hide_class == section_id_to_show ) {
672 section_id_tab = '#wpbc_general_settings_all_tab'
673 }
674 if ( '#wpbc_general_settings_capacity_metabox,#wpbc_general_settings_capacity_upgrade_metabox' == section_id_to_show ) {
675 section_id_tab = '#wpbc_general_settings_capacity_tab'
676 }
677 jQuery( '#form_visible_section' ).val( section_id_tab );
678 }
679
680 // Like blinking some elements.
681 wpbc_admin_ui__do__anchor__another_actions();
682 }
683 }
684
685 function wpbc_admin_ui__is_in_mobile_screen_size() {
686 return wpbc_admin_ui__is_in_this_screen_size( 605 );
687 }
688
689 function wpbc_admin_ui__is_in_this_screen_size(size) {
690 return (window.screen.width <= size);
691 }
692
693 /**
694 * Open settings page | Expand section | Select Menu item.
695 */
696 function wpbc_admin_ui__do__open_url__expand_section(url, section_id) {
697
698 // window.location.href = url + '&do_expand=' + section_id + '#do_expand__' + section_id; //.
699 window.location.href = url + '#do_expand__' + section_id;
700
701 if ( wpbc_admin_ui__is_in_mobile_screen_size() ) {
702 wpbc_admin_ui__sidebar_left__do_min();
703 }
704
705 wpbc_admin_ui__do_expand_section();
706 }
707
708
709 /**
710 * Check for Other actions: Like blinking some elements in settings page. E.g. Days selection or change-over days.
711 */
712 function wpbc_admin_ui__do__anchor__another_actions() {
713
714 var anchors_arr = wpbc_url_get_anchors_arr();
715 var anchors_arr_length = anchors_arr.length;
716
717 // Other actions: Like blinking some elements.
718 for ( var i = 0; i < anchors_arr_length; i++ ) {
719
720 var this_anchor = anchors_arr[i];
721
722 var this_anchor_prop_value = this_anchor.split( 'do_other_actions__' );
723
724 if ( this_anchor_prop_value.length > 1 ) {
725
726 var section_action = this_anchor_prop_value[1];
727
728 switch ( section_action ) {
729
730 case 'blink_day_selections':
731 // wpbc_ui_settings__panel__click( '#wpbc_general_settings_calendar_tab a', '#wpbc_general_settings_calendar_metabox', 'Days Selection' );.
732 wpbc_blink_element( '.wpbc_tr_set_gen_booking_type_of_day_selections', 4, 350 );
733 wpbc_scroll_to( '.wpbc_tr_set_gen_booking_type_of_day_selections' );
734 break;
735
736 case 'blink_change_over_days':
737 // wpbc_ui_settings__panel__click( '#wpbc_general_settings_calendar_tab a', '#wpbc_general_settings_calendar_metabox', 'Changeover Days' );.
738 wpbc_blink_element( '.wpbc_tr_set_gen_booking_range_selection_time_is_active', 4, 350 );
739 wpbc_scroll_to( '.wpbc_tr_set_gen_booking_range_selection_time_is_active' );
740 break;
741
742 case 'blink_captcha':
743 wpbc_blink_element( '.wpbc_tr_set_gen_booking_is_use_captcha', 4, 350 );
744 wpbc_scroll_to( '.wpbc_tr_set_gen_booking_is_use_captcha' );
745 break;
746
747 default:
748 }
749 }
750 }
751 }
752
753 /**
754 * Copy txt to clipbrd from Text fields.
755 *
756 * @param html_element_id - e.g. 'data_field'
757 * @returns {boolean}
758 */
759 function wpbc_copy_text_to_clipbrd_from_element( html_element_id ) {
760 // Get the text field.
761 var copyText = document.getElementById( html_element_id );
762
763 // Select the text field.
764 copyText.select();
765 copyText.setSelectionRange( 0, 99999 ); // For mobile devices.
766
767 // Copy the text inside the text field.
768 var is_copied = wpbc_copy_text_to_clipbrd( copyText.value );
769 if ( ! is_copied ) {
770 console.error( 'Oops, unable to copy', copyText.value );
771 }
772 return is_copied;
773 }
774
775 /**
776 * Copy txt to clipbrd.
777 *
778 * @param text
779 * @returns {boolean}
780 */
781 function wpbc_copy_text_to_clipbrd(text) {
782
783 if ( ! navigator.clipboard ) {
784 return wpbc_fallback_copy_text_to_clipbrd( text );
785 }
786
787 navigator.clipboard.writeText( text ).then(
788 function () {
789 // console.log( 'Async: Copying to clipboard was successful!' );.
790 return true;
791 },
792 function (err) {
793 // console.error( 'Async: Could not copy text: ', err );.
794 return false;
795 }
796 );
797 }
798
799 /**
800 * Copy txt to clipbrd - depricated method.
801 *
802 * @param text
803 * @returns {boolean}
804 */
805 function wpbc_fallback_copy_text_to_clipbrd( text ) {
806
807 // -----------------------------------------------------------------------------------------------------------------
808 // var textArea = document.createElement( "textarea" );
809 // textArea.value = text;
810 //
811 // // Avoid scrolling to bottom.
812 // textArea.style.top = "0";
813 // textArea.style.left = "0";
814 // textArea.style.position = "fixed";
815 // textArea.style.zIndex = "999999999";
816 // document.body.appendChild( textArea );
817 // textArea.focus();
818 // textArea.select();
819
820 // -----------------------------------------------------------------------------------------------------------------
821 // Now get it as HTML (original here https://stackoverflow.com/questions/34191780/javascript-copy-string-to-clipboard-as-text-html ).
822
823 // [1] - Create container for the HTML.
824 var container = document.createElement( 'div' );
825 container.innerHTML = text;
826
827 // [2] - Hide element.
828 container.style.position = 'fixed';
829 container.style.pointerEvents = 'none';
830 container.style.opacity = 0;
831
832 // Detect all style sheets of the page.
833 var activeSheets = Array.prototype.slice.call( document.styleSheets ).filter(
834 function (sheet) {
835 return ! sheet.disabled;
836 }
837 );
838
839 // [3] - Mount the container to the DOM to make `contentWindow` available.
840 document.body.appendChild( container );
841
842 // [4] - Copy to clipboard.
843 window.getSelection().removeAllRanges();
844
845 var range = document.createRange();
846 range.selectNode( container );
847 window.getSelection().addRange( range );
848 // -----------------------------------------------------------------------------------------------------------------
849
850 var result = false;
851
852 try {
853 result = document.execCommand( 'copy' );
854 // console.log( 'Fallback: Copying text command was ' + msg ); //.
855 } catch ( err ) {
856 // console.error( 'Fallback: Oops, unable to copy', err ); //.
857 }
858 // document.body.removeChild( textArea ); //.
859
860 // [5.4] - Enable CSS.
861 var activeSheets_length = activeSheets.length;
862 for ( var i = 0; i < activeSheets_length; i++ ) {
863 activeSheets[i].disabled = false;
864 }
865
866 // [6] - Remove the container
867 document.body.removeChild( container );
868
869 return result;
870 }
871 /**
872 * WPBC Collapsible Groups
873 *
874 * Universal, dependency-free controller for expanding/collapsing grouped sections in right-side panels (Inspector/Library/Form Settings, or any other WPBC page).
875 *
876 * === How to use it (quick) ? ===
877 *
878 * -- 1. Markup (independent mode: multiple open allowed) --
879 * <div class="wpbc_collapsible">
880 * <section class="wpbc_ui__collapsible_group is-open">
881 * <button type="button" class="group__header"><h3>General</h3></button>
882 * <div class="group__fields">…</div>
883 * </section>
884 * <section class="wpbc_ui__collapsible_group">
885 * <button type="button" class="group__header"><h3>Advanced</h3></button>
886 * <div class="group__fields">…</div>
887 * </section>
888 * </div>
889 *
890 * -- 2. Exclusive/accordion mode (one open at a time) --
891 * <div class="wpbc_collapsible wpbc_collapsible--exclusive">…</div>
892 *
893 * -- 3. Auto-init --
894 * The script auto-initializes on DOMContentLoaded. No extra code needed.
895 *
896 * -- 4. Programmatic control (optional)
897 * const root = document.querySelector('#wpbc_bfb__inspector');
898 * const api = root.__wpbc_collapsible_instance; // set by auto-init
899 *
900 * api.open_by_heading('Validation'); // open by heading text
901 * api.open_by_index(0); // open the first group
902 *
903 * -- 5.Listen to events (e.g., to persist “open group” state) --
904 * root.addEventListener('wpbc:collapsible:open', (e) => { console.log( e.detail.group ); });
905 * root.addEventListener('wpbc:collapsible:close', (e) => { console.log( e.detail.group ); });
906 *
907 *
908 *
909 * Markup expectations (minimal):
910 * <div class="wpbc_collapsible [wpbc_collapsible--exclusive]">
911 * <section class="wpbc_ui__collapsible_group [is-open]">
912 * <button type="button" class="group__header"> ... </button>
913 * <div class="group__fields"> ... </div>
914 * </section>
915 * ... more <section> ...
916 * </div>
917 *
918 * Notes:
919 * - Add `is-open` to any section you want initially expanded.
920 * - Add `wpbc_collapsible--exclusive` to the container for "open one at a time" behavior.
921 * - Works with your existing BFB markup (classes used there are the defaults).
922 *
923 * Accessibility:
924 * - Sets aria-expanded on .group__header
925 * - Sets aria-hidden + [hidden] on .group__fields
926 * - ArrowUp/ArrowDown move focus between headers; Enter/Space toggles
927 *
928 * Events (bubbles from the <section>):
929 * - 'wpbc:collapsible:open' (detail: { group, root, instance })
930 * - 'wpbc:collapsible:close' (detail: { group, root, instance })
931 *
932 * Public API (instance methods):
933 * - init(), destroy(), refresh()
934 * - expand(group, [exclusive]), collapse(group), toggle(group)
935 * - open_by_index(index), open_by_heading(text)
936 * - is_exclusive(), is_open(group)
937 *
938 * @version 2025-08-26
939 * @since 2025-08-26
940 */
941 // ---------------------------------------------------------------------------------------------------------------------
942 // == File /collapsible_groups.js == Time point: 2025-08-26 14:13
943 // ---------------------------------------------------------------------------------------------------------------------
944 (function (w, d) {
945 'use strict';
946
947 class WPBC_Collapsible_Groups {
948
949 /**
950 * Create a collapsible controller for a container.
951 *
952 * @param {HTMLElement|string} root_el
953 * The container element (or CSS selector) that wraps collapsible groups.
954 * The container usually has the class `.wpbc_collapsible`.
955 * @param {Object} [opts={}]
956 * @param {string} [opts.group_selector='.wpbc_ui__collapsible_group']
957 * Selector for each collapsible group inside the container.
958 * @param {string} [opts.header_selector='.group__header']
959 * Selector for the clickable header inside a group.
960 * @param {string} [opts.fields_selector='.group__fields']
961 * Selector for the content/panel element inside a group.
962 * @param {string} [opts.open_class='is-open']
963 * Class name that indicates the group is open.
964 * @param {boolean} [opts.exclusive=false]
965 * If true, only one group can be open at a time in this container.
966 *
967 * @constructor
968 * @since 2025-08-26
969 */
970 constructor(root_el, opts = {}) {
971 this.root = (typeof root_el === 'string') ? d.querySelector( root_el ) : root_el;
972 this.opts = Object.assign( {
973 group_selector : '.wpbc_ui__collapsible_group',
974 header_selector: '.group__header',
975 fields_selector: '.group__fields,.group__content',
976 open_class : 'is-open',
977 exclusive : false
978 }, opts );
979
980 // Bound handlers (for add/removeEventListener symmetry).
981 /** @private */
982 this._on_click = this._on_click.bind( this );
983 /** @private */
984 this._on_keydown = this._on_keydown.bind( this );
985
986 /** @type {HTMLElement[]} @private */
987 this._groups = [];
988 /** @type {MutationObserver|null} @private */
989 this._observer = null;
990 }
991
992 /**
993 * Initialize the controller: cache groups, attach listeners, set ARIA,
994 * and start observing DOM changes inside the container.
995 *
996 * @returns {WPBC_Collapsible_Groups} The instance (chainable).
997 * @listens click
998 * @listens keydown
999 * @since 2025-08-26
1000 */
1001 init() {
1002 if ( !this.root ) {
1003 return this;
1004 }
1005 this._groups = Array.prototype.slice.call(
1006 this.root.querySelectorAll( this.opts.group_selector )
1007 );
1008 this.root.addEventListener( 'click', this._on_click, false );
1009 this.root.addEventListener( 'keydown', this._on_keydown, false );
1010
1011 // Observe dynamic inserts/removals (Inspector re-renders).
1012 this._observer = new MutationObserver( () => {
1013 this.refresh();
1014 } );
1015 this._observer.observe( this.root, { childList: true, subtree: true } );
1016
1017 this._sync_all_aria();
1018 return this;
1019 }
1020
1021 /**
1022 * Tear down the controller: detach listeners, stop the observer,
1023 * and drop internal references.
1024 *
1025 * @returns {void}
1026 * @since 2025-08-26
1027 */
1028 destroy() {
1029 if ( !this.root ) {
1030 return;
1031 }
1032 this.root.removeEventListener( 'click', this._on_click, false );
1033 this.root.removeEventListener( 'keydown', this._on_keydown, false );
1034 if ( this._observer ) {
1035 this._observer.disconnect();
1036 this._observer = null;
1037 }
1038 this._groups = [];
1039 }
1040
1041 /**
1042 * Re-scan the DOM for current groups and re-apply ARIA to all of them.
1043 * Useful after dynamic (re)renders.
1044 *
1045 * @returns {void}
1046 * @since 2025-08-26
1047 */
1048 refresh() {
1049 if ( !this.root ) {
1050 return;
1051 }
1052 this._groups = Array.prototype.slice.call(
1053 this.root.querySelectorAll( this.opts.group_selector )
1054 );
1055 this._sync_all_aria();
1056 }
1057
1058 /**
1059 * Check whether the container is in exclusive (accordion) mode.
1060 *
1061 * Order of precedence:
1062 * 1) Explicit option `opts.exclusive`
1063 * 2) Container has class `.wpbc_collapsible--exclusive`
1064 * 3) Container matches `[data-wpbc-accordion="exclusive"]`
1065 *
1066 * @returns {boolean} True if exclusive mode is active.
1067 * @since 2025-08-26
1068 */
1069 is_exclusive() {
1070 return !!(
1071 this.opts.exclusive ||
1072 this.root.classList.contains( 'wpbc_collapsible--exclusive' ) ||
1073 this.root.matches( '[data-wpbc-accordion="exclusive"]' )
1074 );
1075 }
1076
1077 /**
1078 * Determine whether a specific group is open.
1079 *
1080 * @param {HTMLElement} group The group element to test.
1081 * @returns {boolean} True if the group is currently open.
1082 * @since 2025-08-26
1083 */
1084 is_open(group) {
1085 return group.classList.contains( this.opts.open_class );
1086 }
1087
1088 /**
1089 * Open a group. Honors exclusive mode by collapsing all sibling groups
1090 * (queried from the live DOM at call-time).
1091 *
1092 * @param {HTMLElement} group The group element to open.
1093 * @param {boolean} [exclusive]
1094 * If provided, overrides container mode for this action only.
1095 * @returns {void}
1096 * @fires CustomEvent#wpbc:collapsible:open
1097 * @since 2025-08-26
1098 */
1099 expand(group, exclusive) {
1100 if ( !group ) {
1101 return;
1102 }
1103 const do_exclusive = (typeof exclusive === 'boolean') ? exclusive : this.is_exclusive();
1104 if ( do_exclusive ) {
1105 // Always use the live DOM, not the cached list.
1106 Array.prototype.forEach.call(
1107 this.root.querySelectorAll( this.opts.group_selector ),
1108 (g) => {
1109 if ( g !== group ) {
1110 this._set_open( g, false );
1111 }
1112 }
1113 );
1114 }
1115 this._set_open( group, true );
1116 }
1117
1118 /**
1119 * Close a group.
1120 *
1121 * @param {HTMLElement} group The group element to close.
1122 * @returns {void}
1123 * @fires CustomEvent#wpbc:collapsible:close
1124 * @since 2025-08-26
1125 */
1126 collapse(group) {
1127 if ( !group ) {
1128 return;
1129 }
1130 this._set_open( group, false );
1131 }
1132
1133 /**
1134 * Toggle a group's open/closed state.
1135 *
1136 * @param {HTMLElement} group The group element to toggle.
1137 * @returns {void}
1138 * @since 2025-08-26
1139 */
1140 toggle(group) {
1141 if ( !group ) {
1142 return;
1143 }
1144 this[this.is_open( group ) ? 'collapse' : 'expand']( group );
1145 }
1146
1147 /**
1148 * Open a group by its index within the container (0-based).
1149 *
1150 * @param {number} index Zero-based index of the group.
1151 * @returns {void}
1152 * @since 2025-08-26
1153 */
1154 open_by_index(index) {
1155 const group = this._groups[index];
1156 if ( group ) {
1157 this.expand( group );
1158 }
1159 }
1160
1161 /**
1162 * Open a group by matching text contained within the <h3> inside the header.
1163 * The comparison is case-insensitive and substring-based.
1164 *
1165 * @param {string} text Text to match against the heading contents.
1166 * @returns {void}
1167 * @since 2025-08-26
1168 */
1169 open_by_heading(text) {
1170 if ( !text ) {
1171 return;
1172 }
1173 const t = String( text ).toLowerCase();
1174 const match = this._groups.find( (g) => {
1175 const h = g.querySelector( this.opts.header_selector + ' h3' );
1176 return h && h.textContent.toLowerCase().indexOf( t ) !== -1;
1177 } );
1178 if ( match ) {
1179 this.expand( match );
1180 }
1181 }
1182
1183 // -------------------------------------------------------------------------------------------------------------
1184 // Internal
1185 // -------------------------------------------------------------------------------------------------------------
1186
1187 /**
1188 * Delegated click handler for headers.
1189 *
1190 * @private
1191 * @param {MouseEvent} ev The click event.
1192 * @returns {void}
1193 * @since 2025-08-26
1194 */
1195 _on_click(ev) {
1196 const btn = ev.target.closest( this.opts.header_selector );
1197 if ( !btn || !this.root.contains( btn ) ) {
1198 return;
1199 }
1200 ev.preventDefault();
1201 ev.stopPropagation();
1202 const group = btn.closest( this.opts.group_selector );
1203 if ( group ) {
1204 this.toggle( group );
1205 }
1206 }
1207
1208 /**
1209 * Keyboard handler for header interactions and roving focus:
1210 * - Enter/Space toggles the active group.
1211 * - ArrowUp/ArrowDown moves focus between group headers.
1212 *
1213 * @private
1214 * @param {KeyboardEvent} ev The keyboard event.
1215 * @returns {void}
1216 * @since 2025-08-26
1217 */
1218 _on_keydown(ev) {
1219 const btn = ev.target.closest( this.opts.header_selector );
1220 if ( !btn ) {
1221 return;
1222 }
1223
1224 const key = ev.key;
1225
1226 // Toggle on Enter / Space.
1227 if ( key === 'Enter' || key === ' ' ) {
1228 ev.preventDefault();
1229 const group = btn.closest( this.opts.group_selector );
1230 if ( group ) {
1231 this.toggle( group );
1232 }
1233 return;
1234 }
1235
1236 // Move focus with ArrowUp/ArrowDown between headers in this container.
1237 if ( key === 'ArrowUp' || key === 'ArrowDown' ) {
1238 ev.preventDefault();
1239 const headers = Array.prototype.map.call(
1240 this.root.querySelectorAll( this.opts.group_selector ),
1241 (g) => g.querySelector( this.opts.header_selector )
1242 ).filter( Boolean );
1243 const idx = headers.indexOf( btn );
1244 if ( idx !== -1 ) {
1245 const next_idx = (key === 'ArrowDown')
1246 ? Math.min( headers.length - 1, idx + 1 )
1247 : Math.max( 0, idx - 1 );
1248 headers[next_idx].focus();
1249 }
1250 }
1251 }
1252
1253 /**
1254 * Apply ARIA synchronization to all known groups based on their open state.
1255 *
1256 * @private
1257 * @returns {void}
1258 * @since 2025-08-26
1259 */
1260 _sync_all_aria() {
1261 this._groups.forEach( (g) => this._sync_group_aria( g ) );
1262 }
1263
1264 /**
1265 * Sync ARIA attributes and visibility on a single group.
1266 *
1267 * @private
1268 * @param {HTMLElement} group The group element to sync.
1269 * @returns {void}
1270 * @since 2025-08-26
1271 */
1272 _sync_group_aria(group) {
1273 const is_open = this.is_open( group );
1274 const header = group.querySelector( this.opts.header_selector );
1275 // Only direct children that match.
1276 const panels = Array.prototype.filter.call( group.children, (el) => el.matches( this.opts.fields_selector ) );
1277
1278 // Header ARIA.
1279 if ( header ) {
1280 header.setAttribute( 'role', 'button' );
1281 header.setAttribute( 'aria-expanded', is_open ? 'true' : 'false' );
1282
1283 if ( panels.length ) {
1284 // Ensure each panel has an id; then wire aria-controls with space-separated ids.
1285 const ids = panels.map( (p) => {
1286 if ( !p.id ) p.id = this._generate_id( 'wpbc_collapsible_panel' );
1287 return p.id;
1288 } );
1289 header.setAttribute( 'aria-controls', ids.join( ' ' ) );
1290 }
1291 }
1292
1293 // (3) Panels ARIA + visibility.
1294 panels.forEach( (p) => {
1295 p.hidden = !is_open; // actual visibility.
1296 p.setAttribute( 'aria-hidden', is_open ? 'false' : 'true' ); // ARIA.
1297 } );
1298 }
1299
1300 /**
1301 * Internal state change: set a group's open/closed state, sync ARIA,
1302 * manage focus on collapse, and emit a custom event.
1303 *
1304 * @private
1305 * @param {HTMLElement} group The group element to mutate.
1306 * @param {boolean} open Whether the group should be open.
1307 * @returns {void}
1308 * @fires CustomEvent#wpbc:collapsible:open
1309 * @fires CustomEvent#wpbc:collapsible:close
1310 * @since 2025-08-26
1311 */
1312 _set_open(group, open) {
1313 if ( !open && group.contains( document.activeElement ) ) {
1314 const header = group.querySelector( this.opts.header_selector );
1315 header && header.focus();
1316 }
1317 group.classList.toggle( this.opts.open_class, open );
1318 this._sync_group_aria( group );
1319 const ev_name = open ? 'wpbc:collapsible:open' : 'wpbc:collapsible:close';
1320 group.dispatchEvent( new CustomEvent( ev_name, {
1321 bubbles: true,
1322 detail : { group, root: this.root, instance: this }
1323 } ) );
1324 }
1325
1326 /**
1327 * Generate a unique DOM id with the specified prefix.
1328 *
1329 * @private
1330 * @param {string} prefix The id prefix to use.
1331 * @returns {string} A unique element id not present in the document.
1332 * @since 2025-08-26
1333 */
1334 _generate_id(prefix) {
1335 let i = 1;
1336 let id;
1337 do {
1338 id = prefix + '_' + (i++);
1339 }
1340 while ( d.getElementById( id ) );
1341 return id;
1342 }
1343 }
1344
1345 /**
1346 * Auto-initialize collapsible controllers on the page.
1347 * Finds top-level `.wpbc_collapsible` containers (ignoring nested ones),
1348 * and instantiates {@link WPBC_Collapsible_Groups} on each.
1349 *
1350 * @function WPBC_Collapsible_AutoInit
1351 * @returns {void}
1352 * @since 2025-08-26
1353 * @example
1354 * // Runs automatically on DOMContentLoaded; can also be called manually:
1355 * WPBC_Collapsible_AutoInit();
1356 */
1357 function wpbc_collapsible__auto_init() {
1358 var ROOT = '.wpbc_collapsible';
1359 var nodes = Array.prototype.slice.call( d.querySelectorAll( ROOT ) )
1360 .filter( function (n) {
1361 return !n.parentElement || !n.parentElement.closest( ROOT );
1362 } );
1363
1364 nodes.forEach( function (node) {
1365 if ( node.__wpbc_collapsible_instance ) {
1366 return;
1367 }
1368 var exclusive = node.classList.contains( 'wpbc_collapsible--exclusive' ) || node.matches( '[data-wpbc-accordion="exclusive"]' );
1369
1370 node.__wpbc_collapsible_instance = new WPBC_Collapsible_Groups( node, { exclusive } ).init();
1371 } );
1372 }
1373
1374 // Export to global for manual control if needed.
1375 w.WPBC_Collapsible_Groups = WPBC_Collapsible_Groups;
1376 w.WPBC_Collapsible_AutoInit = wpbc_collapsible__auto_init;
1377
1378 // DOM-ready auto init.
1379 if ( d.readyState === 'loading' ) {
1380 d.addEventListener( 'DOMContentLoaded', wpbc_collapsible__auto_init, { once: true } );
1381 } else {
1382 wpbc_collapsible__auto_init();
1383 }
1384 })( window, document );
1385
1386 /* globals window, document */
1387 /**
1388 * WPBC Slider Length Groups
1389 *
1390 * Universal, dependency-free controller that keeps a "length" control in sync:
1391 * - number input (data-wpbc_slider_len_value)
1392 * - unit select (data-wpbc_slider_len_unit)
1393 * - range slider (data-wpbc_slider_len_range)
1394 * - writer input (data-wpbc_slider_len_writer) [optional but recommended]
1395 *
1396 * The "writer" stores the combined value like: "100%", "420px", "12.5rem".
1397 * When number/unit/slider change -> writer updates and emits 'input' (bubbles).
1398 * When writer is changed externally (apply-from-JSON, etc) -> UI updates.
1399 *
1400 * Markup expectations (minimal):
1401 * <div class="wpbc_slider_len_group"
1402 * data-wpbc_slider_len_bounds_map='{"%":{"min":30,"max":100,"step":1},"px":{"min":300,"max":2000,"step":10}}'
1403 * data-wpbc_slider_len_default_unit="%">
1404 * <input type="number" data-wpbc_slider_len_value>
1405 * <select data-wpbc_slider_len_unit>...</select>
1406 * <input type="range" data-wpbc_slider_len_range>
1407 * <input type="text" data-wpbc_slider_len_writer style="display:none;">
1408 * </div>
1409 *
1410 * Performance notes:
1411 * - MutationObserver is DISABLED by default (prevents performance issues).
1412 * - If your UI re-renders and inserts new groups dynamically, call:
1413 * WPBC_Slider_Len_AutoInit(); OR instance.refresh();
1414 * Or enable observer via: new WPBC_Slider_Len_Groups(root, { enable_observer:true }).init();
1415 *
1416 * Public API (instance methods):
1417 * - init(), destroy(), refresh()
1418 *
1419 * @version 2026-01-25
1420 * @since 2026-01-25
1421 * @file ../includes/__js/admin/slider_groups/wpbc_len_groups.js
1422 */
1423 (function (w, d) {
1424 'use strict';
1425
1426 // -------------------------------------------------------------------------------------------------
1427 // Helpers
1428 // -------------------------------------------------------------------------------------------------
1429 function clamp_num(v, min, max) {
1430 if (typeof min === 'number' && !isNaN(min)) v = Math.max(min, v);
1431 if (typeof max === 'number' && !isNaN(max)) v = Math.min(max, v);
1432 return v;
1433 }
1434
1435 function parse_float(v) {
1436 var n = parseFloat(v);
1437 return isNaN(n) ? null : n;
1438 }
1439
1440 function safe_json_parse(str) {
1441 try {
1442 return JSON.parse(str);
1443 } catch (e) {
1444 return null;
1445 }
1446 }
1447
1448 function parse_len_combined(raw, default_unit) {
1449 var s = (raw == null) ? '' : String(raw).trim();
1450 if (!s) return { num: '', unit: default_unit || '%' };
1451
1452 var m = s.match(/^\s*([\-]?\d+(?:\.\d+)?)\s*([a-z%]*)\s*$/i);
1453 if (!m) {
1454 // If it's not parseable, treat as number and keep default unit.
1455 return { num: s, unit: default_unit || '%' };
1456 }
1457
1458 var num = m[1] ? String(m[1]) : '';
1459 var unit = m[2] ? String(m[2]) : '';
1460 if (!unit) unit = default_unit || '%';
1461
1462 return { num: num, unit: unit };
1463 }
1464
1465 function build_combined(num, unit) {
1466 if (num == null || String(num).trim() === '') return '';
1467 return String(num) + String(unit || '');
1468 }
1469
1470 function emit_input(el) {
1471 if (!el) return;
1472 el.dispatchEvent(new Event('input', { bubbles: true }));
1473 }
1474
1475 // -------------------------------------------------------------------------------------------------
1476 // Controller
1477 // -------------------------------------------------------------------------------------------------
1478 class WPBC_Slider_Len_Groups {
1479
1480 /**
1481 * @param {HTMLElement|string} root_el Container (or selector). If omitted, uses document.
1482 * @param {Object} [opts={}]
1483 */
1484 constructor(root_el, opts) {
1485 this.root = root_el
1486 ? ((typeof root_el === 'string') ? d.querySelector(root_el) : root_el)
1487 : d;
1488
1489 this.opts = Object.assign({
1490 // Strict selectors (NO backward compatibility).
1491 group_selector : '.wpbc_slider_len_group',
1492 value_selector : '[data-wpbc_slider_len_value]',
1493 unit_selector : '[data-wpbc_slider_len_unit]',
1494 range_selector : '[data-wpbc_slider_len_range]',
1495 writer_selector : '[data-wpbc_slider_len_writer]',
1496
1497 default_unit : '%',
1498
1499 fallback_bounds : {
1500 'px' : { min: 0, max: 512, step: 1 },
1501 '%' : { min: 0, max: 100, step: 1 },
1502 'rem': { min: 0, max: 10, step: 0.1 },
1503 'em' : { min: 0, max: 10, step: 0.1 }
1504 },
1505
1506 // Disabled by default for performance.
1507 enable_observer : false,
1508 observer_debounce_ms: 150
1509 }, opts || {});
1510
1511 this._on_input = this._on_input.bind(this);
1512 this._on_change = this._on_change.bind(this);
1513
1514 this._bounds_cache = new WeakMap(); // group -> bounds_map_object
1515 this._observer = null;
1516 this._refresh_tmr = null;
1517 }
1518
1519 init() {
1520 if (!this.root) return this;
1521
1522 this.root.addEventListener('input', this._on_input, true);
1523 this.root.addEventListener('change', this._on_change, true);
1524
1525 if (this.opts.enable_observer && w.MutationObserver) {
1526 this._observer = new MutationObserver(() => { this._debounced_refresh(); });
1527 this._observer.observe(this.root === d ? d.documentElement : this.root, { childList: true, subtree: true });
1528 }
1529
1530 this.refresh();
1531 return this;
1532 }
1533
1534 destroy() {
1535 if (!this.root) return;
1536
1537 this.root.removeEventListener('input', this._on_input, true);
1538 this.root.removeEventListener('change', this._on_change, true);
1539
1540 if (this._observer) {
1541 this._observer.disconnect();
1542 this._observer = null;
1543 }
1544
1545 if (this._refresh_tmr) {
1546 clearTimeout(this._refresh_tmr);
1547 this._refresh_tmr = null;
1548 }
1549 }
1550
1551 refresh() {
1552 if (!this.root) return;
1553
1554 var scope = (this.root === d ? d : this.root);
1555 var groups = Array.prototype.slice.call(scope.querySelectorAll(this.opts.group_selector));
1556
1557 for (var i = 0; i < groups.length; i++) {
1558 this._sync_group_from_writer(groups[i]);
1559 this._apply_bounds_for_current_unit(groups[i]);
1560 }
1561 }
1562
1563 // -------------------------------------------------------------------------------------------------
1564 // Internal
1565 // -------------------------------------------------------------------------------------------------
1566 _debounced_refresh() {
1567 if (this._refresh_tmr) clearTimeout(this._refresh_tmr);
1568 this._refresh_tmr = setTimeout(() => {
1569 this._refresh_tmr = null;
1570 this.refresh();
1571 }, Number(this.opts.observer_debounce_ms) || 0);
1572 }
1573
1574 _find_group(el) {
1575 return (el && el.closest) ? el.closest(this.opts.group_selector) : null;
1576 }
1577
1578 _get_parts(group) {
1579 if (!group) return null;
1580 return {
1581 group : group,
1582 num : group.querySelector(this.opts.value_selector),
1583 unit : group.querySelector(this.opts.unit_selector),
1584 range : group.querySelector(this.opts.range_selector),
1585 writer: group.querySelector(this.opts.writer_selector)
1586 };
1587 }
1588
1589 _get_default_unit(group) {
1590 var du = (group && group.getAttribute)
1591 ? group.getAttribute('data-wpbc_slider_len_default_unit')
1592 : '';
1593 return du ? String(du) : this.opts.default_unit;
1594 }
1595
1596 _get_bounds_map(group) {
1597 if (!group) return null;
1598 if (this._bounds_cache.has(group)) {
1599 return this._bounds_cache.get(group);
1600 }
1601
1602 var raw = group.getAttribute('data-wpbc_slider_len_bounds_map');
1603 var map = raw ? safe_json_parse(raw) : null;
1604 if (!map || typeof map !== 'object') map = null;
1605
1606 this._bounds_cache.set(group, map);
1607 return map;
1608 }
1609
1610 _get_bounds_for_unit(group, unit) {
1611 var map = this._get_bounds_map(group);
1612 if (map && unit && map[unit]) {
1613 return map[unit];
1614 }
1615 return this.opts.fallback_bounds[unit] || this.opts.fallback_bounds['px'];
1616 }
1617
1618 _apply_bounds(parts, bounds) {
1619 if (!parts || !bounds) return;
1620
1621 var min = (bounds.min != null) ? Number(bounds.min) : null;
1622 var max = (bounds.max != null) ? Number(bounds.max) : null;
1623 var step = (bounds.step != null) ? Number(bounds.step) : null;
1624
1625 if (parts.range) {
1626 if (!isNaN(min)) parts.range.min = String(min);
1627 if (!isNaN(max)) parts.range.max = String(max);
1628 if (!isNaN(step)) parts.range.step = String(step);
1629 }
1630 if (parts.num) {
1631 if (!isNaN(min)) parts.num.min = String(min);
1632 if (!isNaN(max)) parts.num.max = String(max);
1633 if (!isNaN(step)) parts.num.step = String(step);
1634 }
1635 }
1636
1637 _apply_bounds_for_current_unit(group) {
1638 var parts = this._get_parts(group);
1639 if (!parts || !parts.unit) return;
1640
1641 var unit = parts.unit.value || this._get_default_unit(group);
1642 var b = this._get_bounds_for_unit(group, unit);
1643
1644 this._apply_bounds(parts, b);
1645
1646 // Clamp current value to new bounds.
1647 var v = parse_float(parts.num && parts.num.value ? parts.num.value : (parts.range ? parts.range.value : ''));
1648 if (v == null) return;
1649
1650 var min = (b && b.min != null) ? Number(b.min) : null;
1651 var max = (b && b.max != null) ? Number(b.max) : null;
1652 v = clamp_num(v, isNaN(min) ? null : min, isNaN(max) ? null : max);
1653
1654 if (parts.num) parts.num.value = String(v);
1655 if (parts.range) parts.range.value = String(v);
1656
1657 this._write_combined(parts, String(v), unit, /*emit*/ false);
1658 }
1659
1660 _write_combined(parts, num, unit, emit) {
1661 if (!parts) return;
1662
1663 var combined = build_combined(num, unit);
1664
1665 if (parts.writer) {
1666 // Avoid recursion: mark as internal write.
1667 parts.writer.__wpbc_slider_len_internal = true;
1668 parts.writer.value = combined;
1669 if (emit) emit_input(parts.writer);
1670 parts.writer.__wpbc_slider_len_internal = false;
1671 } else if (parts.num) {
1672 // If writer is missing, at least notify via number input.
1673 if (emit) emit_input(parts.num);
1674 }
1675 }
1676
1677 _sync_group_from_writer(group) {
1678 var parts = this._get_parts(group);
1679 if (!parts || !parts.writer) return;
1680
1681 var raw = String(parts.writer.value || '').trim();
1682 if (!raw) return;
1683
1684 var du = this._get_default_unit(group);
1685 var p = parse_len_combined(raw, du);
1686
1687 if (parts.unit) parts.unit.value = p.unit;
1688 if (parts.num) parts.num.value = p.num;
1689 if (parts.range) parts.range.value = p.num;
1690 }
1691
1692 _on_input(ev) {
1693 var t = ev.target;
1694 if (!t) return;
1695
1696 var group = this._find_group(t);
1697 if (!group) return;
1698
1699 var parts = this._get_parts(group);
1700 if (!parts) return;
1701
1702 // Writer changed externally -> update UI.
1703 if (parts.writer && t === parts.writer) {
1704 if (t.__wpbc_slider_len_internal) return;
1705 this._sync_group_from_writer(group);
1706 this._apply_bounds_for_current_unit(group);
1707 return;
1708 }
1709
1710 // Slider moved -> update number + writer.
1711 if (t.matches && t.matches(this.opts.range_selector)) {
1712 if (parts.num) parts.num.value = t.value;
1713
1714 var unit = (parts.unit && parts.unit.value) ? parts.unit.value : this._get_default_unit(group);
1715 this._write_combined(parts, t.value, unit, /*emit*/ true);
1716 return;
1717 }
1718
1719 // Number typed -> update slider + writer (clamp if slider has bounds).
1720 if (t.matches && t.matches(this.opts.value_selector)) {
1721 var v = parse_float(t.value);
1722
1723 if (v != null && parts.range) {
1724 var rmin = Number(parts.range.min);
1725 var rmax = Number(parts.range.max);
1726 v = clamp_num(v, isNaN(rmin) ? null : rmin, isNaN(rmax) ? null : rmax);
1727
1728 parts.range.value = String(v);
1729 if (String(v) !== t.value) t.value = String(v);
1730 }
1731
1732 var unit2 = (parts.unit && parts.unit.value) ? parts.unit.value : this._get_default_unit(group);
1733 this._write_combined(parts, t.value, unit2, /*emit*/ true);
1734 }
1735 }
1736
1737 _on_change(ev) {
1738 var t = ev.target;
1739 if (!t) return;
1740
1741 var group = this._find_group(t);
1742 if (!group) return;
1743
1744 var parts = this._get_parts(group);
1745 if (!parts) return;
1746
1747 // Unit changed -> update bounds + writer.
1748 if (t.matches && t.matches(this.opts.unit_selector)) {
1749 this._apply_bounds_for_current_unit(group);
1750
1751 var num = parts.num ? parts.num.value : (parts.range ? parts.range.value : '');
1752 var unit = t.value || this._get_default_unit(group);
1753 this._write_combined(parts, num, unit, /*emit*/ true);
1754 }
1755 }
1756 }
1757
1758 // -------------------------------------------------------------------------------------------------
1759 // Auto-init
1760 // -------------------------------------------------------------------------------------------------
1761 function wpbc_slider_len_groups__auto_init() {
1762 var ROOT = '.wpbc_slider_len_groups';
1763 var nodes = Array.prototype.slice.call(d.querySelectorAll(ROOT))
1764 .filter(function (n) { return !n.parentElement || !n.parentElement.closest(ROOT); });
1765
1766 // If no explicit containers, install a single document-root instance.
1767 if (!nodes.length) {
1768 if (!d.__wpbc_slider_len_groups_global_instance) {
1769 d.__wpbc_slider_len_groups_global_instance = new WPBC_Slider_Len_Groups(d).init();
1770 }
1771 return;
1772 }
1773
1774 nodes.forEach(function (node) {
1775 if (node.__wpbc_slider_len_groups_instance) return;
1776 node.__wpbc_slider_len_groups_instance = new WPBC_Slider_Len_Groups(node).init();
1777 });
1778 }
1779
1780 // Export globals (manual control if needed).
1781 w.WPBC_Slider_Len_Groups = WPBC_Slider_Len_Groups;
1782 w.WPBC_Slider_Len_AutoInit = wpbc_slider_len_groups__auto_init;
1783
1784 // DOM-ready auto init.
1785 if (d.readyState === 'loading') {
1786 d.addEventListener('DOMContentLoaded', wpbc_slider_len_groups__auto_init, { once: true });
1787 } else {
1788 wpbc_slider_len_groups__auto_init();
1789 }
1790
1791 })(window, document);
1792
1793 /* globals window, document */
1794 /**
1795 * WPBC Slider Range Groups
1796 *
1797 * Universal, dependency-free controller that keeps a "range + number" pair in sync:
1798 * - number input (data-wpbc_slider_range_value)
1799 * - range slider (data-wpbc_slider_range_range)
1800 * - writer input (data-wpbc_slider_range_writer) [optional]
1801 *
1802 * If writer exists: number/slider update writer and emit 'input' on writer (bubbles).
1803 * If writer is missing: emits 'input' on the number input.
1804 * If writer changes externally: updates number/slider.
1805 *
1806 * Markup expectations (minimal):
1807 * <div class="wpbc_slider_range_group">
1808 * <input type="number" data-wpbc_slider_range_value>
1809 * <input type="range" data-wpbc_slider_range_range>
1810 * <!-- optional -->
1811 * <input type="text" data-wpbc_slider_range_writer style="display:none;">
1812 * </div>
1813 *
1814 * Performance notes:
1815 * - MutationObserver is DISABLED by default.
1816 * - If your UI re-renders and inserts new groups dynamically, call:
1817 * WPBC_Slider_Range_AutoInit(); OR instance.refresh();
1818 * Or enable observer via: new WPBC_Slider_Range_Groups(root, { enable_observer:true }).init();
1819 *
1820 * Public API (instance methods):
1821 * - init(), destroy(), refresh()
1822 *
1823 * @version 2026-01-25
1824 * @since 2026-01-25
1825 * @file ../includes/__js/admin/slider_groups/wpbc_range_groups.js
1826 */
1827 (function (w, d) {
1828 'use strict';
1829
1830 // -------------------------------------------------------------------------------------------------
1831 // Helpers
1832 // -------------------------------------------------------------------------------------------------
1833 function clamp_num(v, min, max) {
1834 if (typeof min === 'number' && !isNaN(min)) v = Math.max(min, v);
1835 if (typeof max === 'number' && !isNaN(max)) v = Math.min(max, v);
1836 return v;
1837 }
1838
1839 function parse_float(v) {
1840 var n = parseFloat(v);
1841 return isNaN(n) ? null : n;
1842 }
1843
1844 function emit_input(el) {
1845 if (!el) return;
1846 el.dispatchEvent(new Event('input', { bubbles: true }));
1847 }
1848
1849 // -------------------------------------------------------------------------------------------------
1850 // Controller
1851 // -------------------------------------------------------------------------------------------------
1852 class WPBC_Slider_Range_Groups {
1853
1854 /**
1855 * @param {HTMLElement|string} root_el Container (or selector). If omitted, uses document.
1856 * @param {Object} [opts={}]
1857 */
1858 constructor(root_el, opts) {
1859 this.root = root_el
1860 ? ((typeof root_el === 'string') ? d.querySelector(root_el) : root_el)
1861 : d;
1862
1863 this.opts = Object.assign({
1864 // Strict selectors (NO backward compatibility).
1865 group_selector : '.wpbc_slider_range_group',
1866 value_selector : '[data-wpbc_slider_range_value]',
1867 range_selector : '[data-wpbc_slider_range_range]',
1868 writer_selector : '[data-wpbc_slider_range_writer]',
1869
1870 // Disabled by default for performance.
1871 enable_observer : false,
1872 observer_debounce_ms: 150
1873 }, opts || {});
1874
1875 this._on_input = this._on_input.bind(this);
1876 this._on_change = this._on_change.bind(this);
1877
1878 this._observer = null;
1879 this._refresh_tmr = null;
1880 }
1881
1882 init() {
1883 if (!this.root) return this;
1884
1885 this.root.addEventListener('input', this._on_input, true);
1886 this.root.addEventListener('change', this._on_change, true);
1887
1888 if (this.opts.enable_observer && w.MutationObserver) {
1889 this._observer = new MutationObserver(() => { this._debounced_refresh(); });
1890 this._observer.observe(this.root === d ? d.documentElement : this.root, { childList: true, subtree: true });
1891 }
1892
1893 this.refresh();
1894 return this;
1895 }
1896
1897 destroy() {
1898 if (!this.root) return;
1899
1900 this.root.removeEventListener('input', this._on_input, true);
1901 this.root.removeEventListener('change', this._on_change, true);
1902
1903 if (this._observer) {
1904 this._observer.disconnect();
1905 this._observer = null;
1906 }
1907
1908 if (this._refresh_tmr) {
1909 clearTimeout(this._refresh_tmr);
1910 this._refresh_tmr = null;
1911 }
1912 }
1913
1914 refresh() {
1915 if (!this.root) return;
1916
1917 var scope = (this.root === d ? d : this.root);
1918 var groups = Array.prototype.slice.call(scope.querySelectorAll(this.opts.group_selector));
1919
1920 for (var i = 0; i < groups.length; i++) {
1921 this._sync_from_writer(groups[i]);
1922 this._clamp_to_range(groups[i]);
1923 }
1924 }
1925
1926 // -------------------------------------------------------------------------------------------------
1927 // Internal
1928 // -------------------------------------------------------------------------------------------------
1929 _debounced_refresh() {
1930 if (this._refresh_tmr) clearTimeout(this._refresh_tmr);
1931 this._refresh_tmr = setTimeout(() => {
1932 this._refresh_tmr = null;
1933 this.refresh();
1934 }, Number(this.opts.observer_debounce_ms) || 0);
1935 }
1936
1937 _find_group(el) {
1938 return (el && el.closest) ? el.closest(this.opts.group_selector) : null;
1939 }
1940
1941 _get_parts(group) {
1942 if (!group) return null;
1943 return {
1944 group : group,
1945 num : group.querySelector(this.opts.value_selector),
1946 range : group.querySelector(this.opts.range_selector),
1947 writer: group.querySelector(this.opts.writer_selector)
1948 };
1949 }
1950
1951 _write(parts, value, emit) {
1952 if (!parts) return;
1953
1954 if (parts.writer) {
1955 parts.writer.__wpbc_slider_range_internal = true;
1956 parts.writer.value = String(value);
1957 if (emit) emit_input(parts.writer);
1958 parts.writer.__wpbc_slider_range_internal = false;
1959 } else if (parts.num) {
1960 // If writer is missing, at least notify via number input.
1961 if (emit) emit_input(parts.num);
1962 }
1963 }
1964
1965 _sync_from_writer(group) {
1966 var parts = this._get_parts(group);
1967 if (!parts || !parts.writer) return;
1968
1969 var raw = String(parts.writer.value || '').trim();
1970 if (!raw) return;
1971
1972 if (parts.num) parts.num.value = raw;
1973 if (parts.range) parts.range.value = raw;
1974 }
1975
1976 _clamp_to_range(group) {
1977 var parts = this._get_parts(group);
1978 if (!parts || !parts.range || !parts.num) return;
1979
1980 var v = parse_float(parts.num.value);
1981 if (v == null) return;
1982
1983 var min = Number(parts.range.min);
1984 var max = Number(parts.range.max);
1985 var vv = clamp_num(v, isNaN(min) ? null : min, isNaN(max) ? null : max);
1986
1987 if (String(vv) !== parts.num.value) parts.num.value = String(vv);
1988 parts.range.value = String(vv);
1989 }
1990
1991 _on_input(ev) {
1992 var t = ev.target;
1993 if (!t) return;
1994
1995 var group = this._find_group(t);
1996 if (!group) return;
1997
1998 var parts = this._get_parts(group);
1999 if (!parts) return;
2000
2001 // Writer changed externally -> update UI.
2002 if (parts.writer && t === parts.writer) {
2003 if (t.__wpbc_slider_range_internal) return;
2004 this._sync_from_writer(group);
2005 this._clamp_to_range(group);
2006 return;
2007 }
2008
2009 // Range moved -> update number + writer.
2010 if (t.matches && t.matches(this.opts.range_selector)) {
2011 if (parts.num) parts.num.value = t.value;
2012 this._write(parts, t.value, /*emit*/ true);
2013 return;
2014 }
2015
2016 // Number typed -> update range + writer (clamp by slider bounds).
2017 if (t.matches && t.matches(this.opts.value_selector)) {
2018 if (parts.range) {
2019 var v = parse_float(t.value);
2020 if (v != null) {
2021 var min = Number(parts.range.min);
2022 var max = Number(parts.range.max);
2023 v = clamp_num(v, isNaN(min) ? null : min, isNaN(max) ? null : max);
2024
2025 parts.range.value = String(v);
2026 if (String(v) !== t.value) t.value = String(v);
2027 }
2028 }
2029 this._write(parts, t.value, /*emit*/ true);
2030 }
2031 }
2032
2033 _on_change(ev) {
2034 // No special "change" handling needed currently; kept for symmetry/future.
2035 }
2036 }
2037
2038 // -------------------------------------------------------------------------------------------------
2039 // Auto-init
2040 // -------------------------------------------------------------------------------------------------
2041 function wpbc_slider_range_groups__auto_init() {
2042 var ROOT = '.wpbc_slider_range_groups';
2043 var nodes = Array.prototype.slice.call(d.querySelectorAll(ROOT))
2044 .filter(function (n) { return !n.parentElement || !n.parentElement.closest(ROOT); });
2045
2046 if (!nodes.length) {
2047 if (!d.__wpbc_slider_range_groups_global_instance) {
2048 d.__wpbc_slider_range_groups_global_instance = new WPBC_Slider_Range_Groups(d).init();
2049 }
2050 return;
2051 }
2052
2053 nodes.forEach(function (node) {
2054 if (node.__wpbc_slider_range_groups_instance) return;
2055 node.__wpbc_slider_range_groups_instance = new WPBC_Slider_Range_Groups(node).init();
2056 });
2057 }
2058
2059 // Export globals.
2060 w.WPBC_Slider_Range_Groups = WPBC_Slider_Range_Groups;
2061 w.WPBC_Slider_Range_AutoInit = wpbc_slider_range_groups__auto_init;
2062
2063 if (d.readyState === 'loading') {
2064 d.addEventListener('DOMContentLoaded', wpbc_slider_range_groups__auto_init, { once: true });
2065 } else {
2066 wpbc_slider_range_groups__auto_init();
2067 }
2068
2069 })(window, document);
2070
2071 /**
2072 * Booking Calendar — Generic UI Tabs Utility (JS)
2073 *
2074 * Purpose: Lightweight, dependency-free tabs controller for any small tab group in admin UIs.
2075 * - Auto-initializes groups marked with data-wpbc-tabs.
2076 * - Assigns ARIA roles and toggles aria-selected/aria-hidden/tabindex.
2077 * - Supports keyboard navigation (Left/Right/Home/End).
2078 * - Public API: window.wpbc_ui_tabs.{init_on, init_group, set_active}
2079 * - Emits 'wpbc:tabs:change' on the group root when the active tab changes.
2080 *
2081 * Markup contract:
2082 * - Root: [data-wpbc-tabs]
2083 * - Tabs: [data-wpbc-tab-key="K"]
2084 * - Panels: [data-wpbc-tab-panel="K"]
2085 *
2086 * @package Booking Calendar
2087 * @subpackage Admin\UI
2088 * @since 11.0.0
2089 * @version 1.0.0
2090 * @see /includes/__js/admin/ui_tabs/ui_tabs.js
2091 *
2092 *
2093 * How it works:
2094 * - Root node must have [data-wpbc-tabs] attribute (any value).
2095 * - Tab buttons must carry [data-wpbc-tab-key="..."] (unique per group).
2096 * - Panels must carry [data-wpbc-tab-panel="..."] with matching keys.
2097 * - Adds WAI-ARIA roles and aria-selected/hidden wiring.
2098 *
2099 * <div data-wpbc-tabs="column-styles" data-wpbc-tab-active="1" class="wpbc_ui_tabs_root" >
2100 * <!-- Top Tabs -->
2101 * <div data-wpbc-tablist="" role="tablist" class=" wpbc_ui_el__horis_top_bar__wrapper" >
2102 * <div class="wpbc_ui_el__horis_top_bar__content">
2103 * <h2 class="wpbc_ui_el__horis_nav_label">Column:</h2>
2104 *
2105 * <div class="wpbc_ui_el__horis_nav_item wpbc_ui_el__horis_nav_item__1">
2106 * <a
2107 * data-wpbc-tab-key="1"
2108 * aria-selected="true" role="tab" tabindex="0" aria-controls="wpbc_tab_panel_col_1"
2109 *
2110 * href="javascript:void(0);"
2111 * class="wpbc_ui_el__horis_nav_item__a wpbc_ui_el__horis_nav_item__single"
2112 * id="wpbc_tab_col_1"
2113 * title="Column 1"
2114 * ><span class="wpbc_ui_el__horis_nav_title">Title 1</span></a>
2115 * </div>
2116 * ...
2117 * </div>
2118 * </div>
2119 * <!-- Tabs Content -->
2120 * <div class="wpbc_tab__panel group__fields" data-wpbc-tab-panel="1" id="wpbc_tab_panel_col_1" role="tabpanel" aria-labelledby="wpbc_tab_col_1">
2121 * ...
2122 * </div>
2123 * ...
2124 * </div>
2125 *
2126 * Public API:
2127 * - wpbc_ui_tabs.init_on(root_or_selector) // find and init groups within a container
2128 * - wpbc_ui_tabs.init_group(root_el) // init a single group root
2129 * - wpbc_ui_tabs.set_active(root_el, key) // programmatically change active tab
2130 *
2131 * Events:
2132 * - Dispatches CustomEvent 'wpbc:tabs:change' on root when tab changes:
2133 * detail: { active_key: '2', prev_key: '1' }
2134 *
2135 * Switch a local (generic) tabs group to tab 3: var group = document.querySelector('[data-wpbc-tabs="column-styles"]'); if ( group ) { wpbc_ui_tabs.set_active(group, '3'); }
2136 */
2137 (function ( w ) {
2138 'use strict';
2139
2140 if ( w.wpbc_ui_tabs ) {
2141 return;
2142 }
2143
2144 /**
2145 * Internal: toggle active state.
2146 *
2147 * @param {HTMLElement} root_el
2148 * @param {string} key
2149 * @param {boolean} should_emit
2150 */
2151 function set_active_internal( root_el, key, should_emit ) {
2152 var tab_btns = root_el.querySelectorAll( '[data-wpbc-tab-key]' );
2153 var panels = root_el.querySelectorAll( '[data-wpbc-tab-panel]' );
2154
2155 var prev_key = root_el.getAttribute( 'data-wpbc-tab-active' ) || null;
2156 if ( String( prev_key ) === String( key ) ) {
2157 return;
2158 }
2159
2160 // Buttons: aria + class
2161 for ( var i = 0; i < tab_btns.length; i++ ) {
2162 var btn = tab_btns[i];
2163 var b_key = btn.getAttribute( 'data-wpbc-tab-key' );
2164 var is_on = String( b_key ) === String( key );
2165
2166 btn.setAttribute( 'role', 'tab' );
2167 btn.setAttribute( 'aria-selected', is_on ? 'true' : 'false' );
2168 btn.setAttribute( 'tabindex', is_on ? '0' : '-1' );
2169
2170 if ( is_on ) {
2171 btn.classList.add( 'active' );
2172 } else {
2173 btn.classList.remove( 'active' );
2174 }
2175 }
2176
2177 // Panels: aria + visibility
2178 for ( var j = 0; j < panels.length; j++ ) {
2179 var pn = panels[j];
2180 var pkey = pn.getAttribute( 'data-wpbc-tab-panel' );
2181 var show = String( pkey ) === String( key );
2182
2183 pn.setAttribute( 'role', 'tabpanel' );
2184 pn.setAttribute( 'aria-hidden', show ? 'false' : 'true' );
2185 if ( show ) {
2186 pn.removeAttribute( 'hidden' );
2187 } else {
2188 pn.setAttribute( 'hidden', '' );
2189 }
2190 }
2191
2192 root_el.setAttribute( 'data-wpbc-tab-active', String( key ) );
2193
2194 if ( should_emit ) {
2195 try {
2196 var ev = new w.CustomEvent( 'wpbc:tabs:change', {
2197 bubbles : true,
2198 detail : { active_key : String( key ), prev_key : prev_key }
2199 } );
2200 root_el.dispatchEvent( ev );
2201 } catch ( _e ) {}
2202 }
2203 }
2204
2205 /**
2206 * Internal: get ordered keys from buttons.
2207 *
2208 * @param {HTMLElement} root_el
2209 * @returns {string[]}
2210 */
2211 function get_keys( root_el ) {
2212 var list = [];
2213 var btns = root_el.querySelectorAll( '[data-wpbc-tab-key]' );
2214 for ( var i = 0; i < btns.length; i++ ) {
2215 var k = btns[i].getAttribute( 'data-wpbc-tab-key' );
2216 if ( k != null && k !== '' ) {
2217 list.push( String( k ) );
2218 }
2219 }
2220 return list;
2221 }
2222
2223 /**
2224 * Internal: move focus between tabs using keyboard.
2225 *
2226 * @param {HTMLElement} root_el
2227 * @param {number} dir +1 (next) / -1 (prev)
2228 */
2229 function focus_relative( root_el, dir ) {
2230 var keys = get_keys( root_el );
2231 var current = root_el.getAttribute( 'data-wpbc-tab-active' ) || keys[0] || null;
2232 var idx = Math.max( 0, keys.indexOf( String( current ) ) );
2233 var next = keys[ ( idx + ( dir > 0 ? 1 : keys.length - 1 ) ) % keys.length ];
2234
2235 var next_btn = root_el.querySelector( '[data-wpbc-tab-key="' + next + '"]' );
2236 if ( next_btn ) {
2237 next_btn.focus();
2238 set_active_internal( root_el, next, true );
2239 }
2240 }
2241
2242 /**
2243 * Initialize a single tabs group root.
2244 *
2245 * @param {HTMLElement} root_el
2246 */
2247 function init_group( root_el ) {
2248 if ( ! root_el || root_el.__wpbc_tabs_inited ) {
2249 return;
2250 }
2251 root_el.__wpbc_tabs_inited = true;
2252
2253 // Roles
2254 var tablist = root_el.querySelector( '[data-wpbc-tablist]' ) || root_el;
2255 tablist.setAttribute( 'role', 'tablist' );
2256
2257 // Default active: from attribute or first button
2258 var keys = get_keys( root_el );
2259 var def = root_el.getAttribute( 'data-wpbc-tab-active' ) || ( keys[0] || '1' );
2260 set_active_internal( root_el, def, false );
2261
2262 // Clicks
2263 root_el.addEventListener( 'click', function ( e ) {
2264 var btn = e.target.closest ? e.target.closest( '[data-wpbc-tab-key]' ) : null;
2265 if ( ! btn || ! root_el.contains( btn ) ) {
2266 return;
2267 }
2268 e.preventDefault();
2269 var key = btn.getAttribute( 'data-wpbc-tab-key' );
2270 if ( key != null ) {
2271 set_active_internal( root_el, key, true );
2272 }
2273 }, true );
2274
2275 // Keyboard (Left/Right/Home/End)
2276 root_el.addEventListener( 'keydown', function ( e ) {
2277 var tgt = e.target;
2278 if ( ! tgt || ! tgt.hasAttribute || ! tgt.hasAttribute( 'data-wpbc-tab-key' ) ) {
2279 return;
2280 }
2281 switch ( e.key ) {
2282 case 'ArrowLeft':
2283 e.preventDefault(); focus_relative( root_el, -1 ); break;
2284 case 'ArrowRight':
2285 e.preventDefault(); focus_relative( root_el, +1 ); break;
2286 case 'Home':
2287 e.preventDefault(); set_active_internal( root_el, ( get_keys( root_el )[0] || '1' ), true ); break;
2288 case 'End':
2289 e.preventDefault(); var ks = get_keys( root_el ); set_active_internal( root_el, ( ks[ ks.length - 1 ] || '1' ), true ); break;
2290 }
2291 }, true );
2292 }
2293
2294 /**
2295 * Initialize all groups within a container (or document).
2296 *
2297 * @param {HTMLElement|string|null} container
2298 */
2299 function init_on( container ) {
2300 var ctx = container ? ( typeof container === 'string' ? document.querySelector( container ) : container ) : document;
2301 if ( ! ctx ) {
2302 return;
2303 }
2304 var groups = ctx.querySelectorAll( '[data-wpbc-tabs]' );
2305 for ( var i = 0; i < groups.length; i++ ) {
2306 init_group( groups[i] );
2307 }
2308 }
2309
2310 /**
2311 * Programmatically set active tab by key.
2312 *
2313 * @param {HTMLElement} root_el
2314 * @param {string|number} key
2315 */
2316 function set_active( root_el, key ) {
2317 if ( root_el && root_el.hasAttribute && root_el.hasAttribute( 'data-wpbc-tabs' ) ) {
2318 set_active_internal( root_el, String( key ), true );
2319 }
2320 }
2321
2322 // Public API (snake_case)
2323 w.wpbc_ui_tabs = {
2324 init_on : init_on,
2325 init_group : init_group,
2326 set_active : set_active
2327 };
2328
2329 // Auto-init on DOM ready
2330 if ( document.readyState === 'loading' ) {
2331 document.addEventListener( 'DOMContentLoaded', function () { init_on( document ); } );
2332 } else {
2333 init_on( document );
2334 }
2335
2336 })( window );
2337
2338 //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVpX2VsZW1lbnRzLmpzIiwidWlfbG9hZGluZ19zcGluLmpzIiwidWlfcmFkaW9fY29udGFpbmVyLmpzIiwidWlfZnVsbF9zY3JlZW5fbW9kZS5qcyIsImdtYWlsX2NoZWNrYm94X3NlbGVjdGlvbi5qcyIsImJvb2tpbmdzX2NoZWNrYm94X3NlbGVjdGlvbi5qcyIsInVpX3NpZGViYXJfbGVmdF9fYWN0aW9ucy5qcyIsImNvcHlfdGV4dF90b19jbGlwYnJkLmpzIiwiY29sbGFwc2libGVfZ3JvdXBzLmpzIiwid3BiY19sZW5fZ3JvdXBzLmpzIiwid3BiY19yYW5nZV9ncm91cHMuanMiLCJ1aV90YWJzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ3JGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ2pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDbEZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDaklBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNwREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQzdWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ3JIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDbGdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDdFpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNyUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6IndwYmNfYWxsX2FkbWluLmpzIiwic291cmNlc0NvbnRlbnQiOlsiXHJcbi8qKlxyXG4gKiBCbGluayBzcGVjaWZpYyBIVE1MIGVsZW1lbnQgdG8gc2V0IGF0dGVudGlvbiB0byB0aGlzIGVsZW1lbnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbGVtZW50X3RvX2JsaW5rXHRcdCAgLSBjbGFzcyBvciBpZCBvZiBlbGVtZW50OiAnLndwYmNfd2lkZ2V0X2F2YWlsYWJsZV91bmF2YWlsYWJsZSdcclxuICogQHBhcmFtIHtpbnR9IGhvd19tYW55X3RpbWVzXHRcdFx0ICAtIDRcclxuICogQHBhcmFtIHtpbnR9IGhvd19sb25nX3RvX2JsaW5rXHRcdCAgLSAzNTBcclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYmxpbmtfZWxlbWVudCggZWxlbWVudF90b19ibGluaywgaG93X21hbnlfdGltZXMgPSA0LCBob3dfbG9uZ190b19ibGluayA9IDM1MCApe1xyXG5cclxuXHRmb3IgKCBsZXQgaSA9IDA7IGkgPCBob3dfbWFueV90aW1lczsgaSsrICl7XHJcblx0XHRqUXVlcnkoIGVsZW1lbnRfdG9fYmxpbmsgKS5mYWRlT3V0KCBob3dfbG9uZ190b19ibGluayApLmZhZGVJbiggaG93X2xvbmdfdG9fYmxpbmsgKTtcclxuXHR9XHJcbiAgICBqUXVlcnkoIGVsZW1lbnRfdG9fYmxpbmsgKS5hbmltYXRlKCB7b3BhY2l0eTogMX0sIDUwMCApO1xyXG59XHJcbiIsIi8qKlxyXG4gKiAgIFN1cHBvcnQgRnVuY3Rpb25zIC0gU3BpbiBJY29uIGluIEJ1dHRvbnMgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xyXG5cclxuLyoqXHJcbiAqIFJlbW92ZSBzcGluIGljb24gZnJvbSAgYnV0dG9uIGFuZCBFbmFibGUgdGhpcyBidXR0b24uXHJcbiAqXHJcbiAqIEBwYXJhbSBidXR0b25fY2xpY2tlZF9lbGVtZW50X2lkXHRcdC0gSFRNTCBJRCBhdHRyaWJ1dGUgb2YgdGhpcyBidXR0b25cclxuICogQHJldHVybiBzdHJpbmdcdFx0XHRcdFx0XHQtIENTUyBjbGFzc2VzIHRoYXQgd2FzIHByZXZpb3VzbHkgaW4gYnV0dG9uIGljb25cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYnV0dG9uX19yZW1vdmVfc3BpbihidXR0b25fY2xpY2tlZF9lbGVtZW50X2lkKSB7XHJcblxyXG5cdHZhciBwcmV2aW9zX2NsYXNzZXMgPSAnJztcclxuXHRpZiAoXHJcblx0XHQodW5kZWZpbmVkICE9IGJ1dHRvbl9jbGlja2VkX2VsZW1lbnRfaWQpXHJcblx0XHQmJiAoJycgIT0gYnV0dG9uX2NsaWNrZWRfZWxlbWVudF9pZClcclxuXHQpIHtcclxuXHRcdHZhciBqRWxlbWVudCA9IGpRdWVyeSggJyMnICsgYnV0dG9uX2NsaWNrZWRfZWxlbWVudF9pZCApO1xyXG5cdFx0aWYgKCBqRWxlbWVudC5sZW5ndGggKSB7XHJcblx0XHRcdHByZXZpb3NfY2xhc3NlcyA9IHdwYmNfYnV0dG9uX2Rpc2FibGVfbG9hZGluZ19pY29uKCBqRWxlbWVudC5nZXQoIDAgKSApO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0cmV0dXJuIHByZXZpb3NfY2xhc3NlcztcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiBTaG93IExvYWRpbmcgKHJvdGF0aW5nIGFycm93KSBpY29uIGZvciBidXR0b24gdGhhdCBoYXMgYmVlbiBjbGlja2VkXHJcbiAqXHJcbiAqIEBwYXJhbSB0aGlzX2J1dHRvblx0XHQtIHRoaXMgb2JqZWN0IG9mIHNwZWNpZmljIGJ1dHRvblxyXG4gKiBAcmV0dXJuIHN0cmluZ1x0XHRcdC0gQ1NTIGNsYXNzZXMgdGhhdCB3YXMgcHJldmlvdXNseSBpbiBidXR0b24gaWNvblxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19idXR0b25fZW5hYmxlX2xvYWRpbmdfaWNvbih0aGlzX2J1dHRvbikge1xyXG5cclxuXHR2YXIgakJ1dHRvbiAgICAgICAgID0galF1ZXJ5KCB0aGlzX2J1dHRvbiApO1xyXG5cdHZhciBqSWNvbiAgICAgICAgICAgPSBqQnV0dG9uLmZpbmQoICdpJyApO1xyXG5cdHZhciBwcmV2aW9zX2NsYXNzZXMgPSBqSWNvbi5hdHRyKCAnY2xhc3MnICk7XHJcblxyXG5cdGpJY29uLnJlbW92ZUNsYXNzKCkuYWRkQ2xhc3MoICdtZW51X2ljb24gaWNvbi0xeCB3cGJjX2ljbl9yb3RhdGVfcmlnaHQgd3BiY19zcGluJyApO1x0Ly8gU2V0IFJvdGF0ZSBpY29uLlxyXG5cdC8vIGpJY29uLmFkZENsYXNzKCAnd3BiY19hbmltYXRpb25fcGF1c2UnICk7XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0Ly8gUGF1c2UgYW5pbWF0aW9uLlxyXG5cdC8vIGpJY29uLmFkZENsYXNzKCAnd3BiY191aV9yZWQnICk7XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdC8vIFNldCBpY29uIGNvbG9yIHJlZC5cclxuXHJcblx0akljb24uYXR0ciggJ3dwYmNfcHJldmlvdXNfY2xhc3MnLCBwcmV2aW9zX2NsYXNzZXMgKVxyXG5cclxuXHRqQnV0dG9uLmFkZENsYXNzKCAnZGlzYWJsZWQnICk7XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0Ly8gRGlzYWJsZSBidXR0b25cclxuXHQvLyBXZSBuZWVkIHRvICBzZXQgIGhlcmUgYXR0ciBpbnN0ZWFkIG9mIHByb3AsIGJlY2F1c2UgZm9yIEEgZWxlbWVudHMsICBhdHRyaWJ1dGUgJ2Rpc2FibGVkJyBkbyAgbm90IGFkZGVkIHdpdGggakJ1dHRvbi5wcm9wKCBcImRpc2FibGVkXCIsIHRydWUgKTsuXHJcblxyXG5cdGpCdXR0b24uYXR0ciggJ3dwYmNfcHJldmlvdXNfb25jbGljaycsIGpCdXR0b24uYXR0ciggJ29uY2xpY2snICkgKTtcdFx0Ly8gU2F2ZSB0aGlzIHZhbHVlLlxyXG5cdGpCdXR0b24uYXR0ciggJ29uY2xpY2snLCAnJyApO1x0XHRcdFx0XHRcdFx0XHRcdFx0XHQvLyBEaXNhYmxlIGFjdGlvbnMgXCJvbiBjbGlja1wiLlxyXG5cclxuXHRyZXR1cm4gcHJldmlvc19jbGFzc2VzO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEhpZGUgTG9hZGluZyAocm90YXRpbmcgYXJyb3cpIGljb24gZm9yIGJ1dHRvbiB0aGF0IHdhcyBjbGlja2VkIGFuZCBzaG93IHByZXZpb3VzIGljb24gYW5kIGVuYWJsZSBidXR0b25cclxuICpcclxuICogQHBhcmFtIHRoaXNfYnV0dG9uXHRcdC0gdGhpcyBvYmplY3Qgb2Ygc3BlY2lmaWMgYnV0dG9uXHJcbiAqIEByZXR1cm4gc3RyaW5nXHRcdFx0LSBDU1MgY2xhc3NlcyB0aGF0IHdhcyBwcmV2aW91c2x5IGluIGJ1dHRvbiBpY29uXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2J1dHRvbl9kaXNhYmxlX2xvYWRpbmdfaWNvbih0aGlzX2J1dHRvbikge1xyXG5cclxuXHR2YXIgakJ1dHRvbiA9IGpRdWVyeSggdGhpc19idXR0b24gKTtcclxuXHR2YXIgakljb24gICA9IGpCdXR0b24uZmluZCggJ2knICk7XHJcblxyXG5cdHZhciBwcmV2aW9zX2NsYXNzZXMgPSBqSWNvbi5hdHRyKCAnd3BiY19wcmV2aW91c19jbGFzcycgKTtcclxuXHRpZiAoXHJcblx0XHQodW5kZWZpbmVkICE9IHByZXZpb3NfY2xhc3NlcylcclxuXHRcdCYmICgnJyAhPSBwcmV2aW9zX2NsYXNzZXMpXHJcblx0KSB7XHJcblx0XHRqSWNvbi5yZW1vdmVDbGFzcygpLmFkZENsYXNzKCBwcmV2aW9zX2NsYXNzZXMgKTtcclxuXHR9XHJcblxyXG5cdGpCdXR0b24ucmVtb3ZlQ2xhc3MoICdkaXNhYmxlZCcgKTtcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQvLyBSZW1vdmUgRGlzYWJsZSBidXR0b24uXHJcblxyXG5cdHZhciBwcmV2aW91c19vbmNsaWNrID0gakJ1dHRvbi5hdHRyKCAnd3BiY19wcmV2aW91c19vbmNsaWNrJyApXHJcblx0aWYgKFxyXG5cdFx0KHVuZGVmaW5lZCAhPSBwcmV2aW91c19vbmNsaWNrKVxyXG5cdFx0JiYgKCcnICE9IHByZXZpb3VzX29uY2xpY2spXHJcblx0KSB7XHJcblx0XHRqQnV0dG9uLmF0dHIoICdvbmNsaWNrJywgcHJldmlvdXNfb25jbGljayApO1xyXG5cdH1cclxuXHJcblx0cmV0dXJuIHByZXZpb3NfY2xhc3NlcztcclxufVxyXG4iLCIvKipcclxuICogT24gc2VsZWN0aW9uICBvZiByYWRpbyBidXR0b24sIGFkanVzdCBhdHRyaWJ1dGVzIG9mIHJhZGlvIGNvbnRhaW5lclxyXG4gKlxyXG4gKiBAcGFyYW0gX3RoaXNcclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfdWlfZWxfX3JhZGlvX2NvbnRhaW5lcl9zZWxlY3Rpb24oX3RoaXMpIHtcclxuXHJcblx0aWYgKCBqUXVlcnkoIF90aGlzICkuaXMoICc6Y2hlY2tlZCcgKSApIHtcclxuXHRcdGpRdWVyeSggX3RoaXMgKS5wYXJlbnRzKCAnLndwYmNfdWlfcmFkaW9fc2VjdGlvbicgKS5maW5kKCAnLndwYmNfdWlfcmFkaW9fY29udGFpbmVyJyApLnJlbW92ZUF0dHIoICdkYXRhLXNlbGVjdGVkJyApO1xyXG5cdFx0alF1ZXJ5KCBfdGhpcyApLnBhcmVudHMoICcud3BiY191aV9yYWRpb19jb250YWluZXI6bm90KC5kaXNhYmxlZCknICkuYXR0ciggJ2RhdGEtc2VsZWN0ZWQnLCB0cnVlICk7XHJcblx0fVxyXG5cclxuXHRpZiAoIGpRdWVyeSggX3RoaXMgKS5pcyggJzpkaXNhYmxlZCcgKSApIHtcclxuXHRcdGpRdWVyeSggX3RoaXMgKS5wYXJlbnRzKCAnLndwYmNfdWlfcmFkaW9fY29udGFpbmVyJyApLmFkZENsYXNzKCAnZGlzYWJsZWQnICk7XHJcblx0fVxyXG59XHJcblxyXG4vKipcclxuICogT24gY2xpY2sgb24gUmFkaW8gQ29udGFpbmVyLCB3ZSB3aWxsICBzZWxlY3QgIHRoZSAgcmFkaW8gYnV0dG9uICAgIGFuZCB0aGVuIGFkanVzdCBhdHRyaWJ1dGVzIG9mIHJhZGlvIGNvbnRhaW5lclxyXG4gKlxyXG4gKiBAcGFyYW0gX3RoaXNcclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfdWlfZWxfX3JhZGlvX2NvbnRhaW5lcl9jbGljayhfdGhpcykge1xyXG5cclxuXHRpZiAoIGpRdWVyeSggX3RoaXMgKS5oYXNDbGFzcyggJ2Rpc2FibGVkJyApICkge1xyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH1cclxuXHJcblx0dmFyIGpfcmFkaW8gPSBqUXVlcnkoIF90aGlzICkuZmluZCggJ2lucHV0W3R5cGU9cmFkaW9dOm5vdCgud3BiYy1mb3JtLXJhZGlvLWludGVybmFsKScgKTtcclxuXHRpZiAoIGpfcmFkaW8ubGVuZ3RoICkge1xyXG5cdFx0al9yYWRpby5wcm9wKCAnY2hlY2tlZCcsIHRydWUgKS50cmlnZ2VyKCAnY2hhbmdlJyApO1xyXG5cdH1cclxuXHJcbn0iLCJcInVzZSBzdHJpY3RcIjtcclxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbi8vID09IEZ1bGwgU2NyZWVuICAtICBzdXBwb3J0IGZ1bmN0aW9ucyAgID09XHJcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG5cclxuLyoqXHJcbiAqIFNhdmUgRnVsbCBTY3JlZW4gcHJlZmVyZW5jZSBpbiBhIHNob3J0IGJyb3dzZXIgY29va2llLlxyXG4gKlxyXG4gKiBUaGlzIG1ha2VzIHRoZSBuZXh0IGFkbWluIHBhZ2UgbG9hZCBkZXRlcm1pbmlzdGljIGV2ZW4gaWYgdGhlIGFzeW5jIHVzZXItbWV0YVxyXG4gKiBBSkFYIHJlcXVlc3QgaXMgaW50ZXJydXB0ZWQgYnkgaW1tZWRpYXRlIG5hdmlnYXRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSBzdHJpbmcgdmFsdWUgJ09uJyBvciAnT2ZmJy5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX2Z1bGxfc2NyZWVuX19zZXRfY29va2llKCB2YWx1ZSApIHtcclxuXHR2YXIgbWF4X2FnZSA9IDYwICogNjAgKiAyNCAqIDM2NTtcclxuXHJcblx0ZG9jdW1lbnQuY29va2llID0gJ3dwYmNfYWRtaW5fZnVsbF9zY3JlZW49JyArIGVuY29kZVVSSUNvbXBvbmVudCggdmFsdWUgKSArICc7IHBhdGg9LzsgbWF4LWFnZT0nICsgbWF4X2FnZSArICc7IFNhbWVTaXRlPUxheCc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcHBseSBGdWxsIFNjcmVlbiBtb2RlIGZyb20gYSB1c2VyIGNsaWNrLlxyXG4gKlxyXG4gKiBAcGFyYW0gSFRNTEVsZW1lbnQgZWwgICAgICAgICAgICAgICAgIENsaWNrZWQgY29udHJvbC5cclxuICogQHBhcmFtIGJvb2wgICAgICAgIGlzX3NhdmVfdXNlcl9zdGF0ZSBXaGV0aGVyIHRvIHNhdmUgdXNlciBwcmVmZXJlbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fZnVsbF9zY3JlZW5fX2RvX29uKCBlbCwgaXNfc2F2ZV91c2VyX3N0YXRlICkge1xyXG5cdGpRdWVyeSggJ2JvZHknICkuYWRkQ2xhc3MoICd3cGJjX2FkbWluX2Z1bGxfc2NyZWVuJyApO1xyXG5cdHdwYmNfY2hlY2tfZnVsbF9zY3JlZW5fbW9kZSgpO1xyXG5cclxuXHRpZiAoIGlzX3NhdmVfdXNlcl9zdGF0ZSApIHtcclxuXHRcdHdwYmNfYWRtaW5fdWlfX2Z1bGxfc2NyZWVuX19zZXRfY29va2llKCAnT24nICk7XHJcblxyXG5cdFx0aWYgKCAnZnVuY3Rpb24nID09PSB0eXBlb2Ygd3BiY19zYXZlX2N1c3RvbV91c2VyX2RhdGFfZnJvbV9lbGVtZW50ICkge1xyXG5cdFx0XHR3cGJjX3NhdmVfY3VzdG9tX3VzZXJfZGF0YV9mcm9tX2VsZW1lbnQoIGVsICk7XHJcblx0XHR9XHJcblx0fVxyXG59XHJcblxyXG4vKipcclxuICogRXhpdCBGdWxsIFNjcmVlbiBtb2RlIGZyb20gYSB1c2VyIGNsaWNrLlxyXG4gKlxyXG4gKiBAcGFyYW0gSFRNTEVsZW1lbnQgZWwgICAgICAgICAgICAgICAgIENsaWNrZWQgY29udHJvbC5cclxuICogQHBhcmFtIGJvb2wgICAgICAgIGlzX3NhdmVfdXNlcl9zdGF0ZSBXaGV0aGVyIHRvIHNhdmUgdXNlciBwcmVmZXJlbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fZnVsbF9zY3JlZW5fX2RvX29mZiggZWwsIGlzX3NhdmVfdXNlcl9zdGF0ZSApIHtcclxuXHRqUXVlcnkoICdib2R5JyApLnJlbW92ZUNsYXNzKCAnd3BiY19hZG1pbl9mdWxsX3NjcmVlbicgKTtcclxuXHR3cGJjX2NoZWNrX2Z1bGxfc2NyZWVuX21vZGUoKTtcclxuXHJcblx0aWYgKCBpc19zYXZlX3VzZXJfc3RhdGUgKSB7XHJcblx0XHR3cGJjX2FkbWluX3VpX19mdWxsX3NjcmVlbl9fc2V0X2Nvb2tpZSggJ09mZicgKTtcclxuXHJcblx0XHRpZiAoICdmdW5jdGlvbicgPT09IHR5cGVvZiB3cGJjX3NhdmVfY3VzdG9tX3VzZXJfZGF0YV9mcm9tX2VsZW1lbnQgKSB7XHJcblx0XHRcdHdwYmNfc2F2ZV9jdXN0b21fdXNlcl9kYXRhX2Zyb21fZWxlbWVudCggZWwgKTtcclxuXHRcdH1cclxuXHR9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVjayBGdWxsICBzY3JlZW4gbW9kZSwgIGJ5ICByZW1vdmluZyB0b3AgdGFiXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2NoZWNrX2Z1bGxfc2NyZWVuX21vZGUoKXtcclxuXHRpZiAoIGpRdWVyeSggJ2JvZHknICkuaGFzQ2xhc3MoICd3cGJjX2FkbWluX2Z1bGxfc2NyZWVuJyApICkge1xyXG5cdFx0alF1ZXJ5KCAnaHRtbCcgKS5yZW1vdmVDbGFzcyggJ3dwLXRvb2xiYXInICk7XHJcblx0fSBlbHNlIHtcclxuXHRcdGpRdWVyeSggJ2h0bWwnICkuYWRkQ2xhc3MoICd3cC10b29sYmFyJyApO1xyXG5cdH1cclxuXHR3cGJjX2NoZWNrX2J1dHRvbnNfbWF4X21pbl9pbl9mdWxsX3NjcmVlbl9tb2RlKCk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIHdwYmNfY2hlY2tfYnV0dG9uc19tYXhfbWluX2luX2Z1bGxfc2NyZWVuX21vZGUoKSB7XHJcblx0aWYgKCBqUXVlcnkoICdib2R5JyApLmhhc0NsYXNzKCAnd3BiY19hZG1pbl9mdWxsX3NjcmVlbicgKSApIHtcclxuXHRcdGpRdWVyeSggJy53cGJjX3VpX190b3BfbmF2X19idG5fZnVsbF9zY3JlZW4nICAgKS5hZGRDbGFzcyggICAgJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0XHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX25vcm1hbF9zY3JlZW4nICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cdH0gZWxzZSB7XHJcblx0XHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX2Z1bGxfc2NyZWVuJyAgICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cdFx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9ub3JtYWxfc2NyZWVuJyApLmFkZENsYXNzKCAgICAnd3BiY191aV9faGlkZScgKTtcclxuXHR9XHJcbn1cclxuXHJcbmpRdWVyeSggZG9jdW1lbnQgKS5yZWFkeSggZnVuY3Rpb24gKCkge1xyXG5cdHdwYmNfY2hlY2tfZnVsbF9zY3JlZW5fbW9kZSgpO1xyXG59ICk7XHJcbiIsIi8qKlxyXG4gKiBDaGVja2JveCBTZWxlY3Rpb24gZnVuY3Rpb25zIGZvciBMaXN0aW5nLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBTZWxlY3Rpb25zIG9mIHNldmVyYWwgIGNoZWNrYm94ZXMgbGlrZSBpbiBnTWFpbCB3aXRoIHNoaWZ0IDopXHJcbiAqIE5lZWQgdG8gIGhhdmUgdGhpcyBzdHJ1Y3R1cmU6XHJcbiAqIC53cGJjX3NlbGVjdGFibGVfdGFibGVcclxuICogICAgICAud3BiY19zZWxlY3RhYmxlX2hlYWRcclxuICogICAgICAgICAgICAgIC5jaGVjay1jb2x1bW5cclxuICogICAgICAgICAgICAgICAgICA6Y2hlY2tib3hcclxuICogICAgICAud3BiY19zZWxlY3RhYmxlX2JvZHlcclxuICogICAgICAgICAgLndwYmNfcm93XHJcbiAqICAgICAgICAgICAgICAuY2hlY2stY29sdW1uXHJcbiAqICAgICAgICAgICAgICAgICAgOmNoZWNrYm94XHJcbiAqICAgICAgLndwYmNfc2VsZWN0YWJsZV9mb290XHJcbiAqICAgICAgICAgICAgICAuY2hlY2stY29sdW1uXHJcbiAqICAgICAgICAgICAgICAgICAgOmNoZWNrYm94XHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2RlZmluZV9nbWFpbF9jaGVja2JveF9zZWxlY3Rpb24oICQgKXtcclxuXHJcblx0dmFyIGNoZWNrcywgZmlyc3QsIGxhc3QsIGNoZWNrZWQsIHNsaWNlZCwgbGFzdENsaWNrZWQgPSBmYWxzZTtcclxuXHJcblx0Ly8gQ2hlY2sgYWxsIGNoZWNrYm94ZXMuXHJcblx0JCggJy53cGJjX3NlbGVjdGFibGVfYm9keScgKS5maW5kKCAnLmNoZWNrLWNvbHVtbicgKS5maW5kKCAnOmNoZWNrYm94JyApLm9uKFxyXG5cdFx0J2NsaWNrJyxcclxuXHRcdGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdGlmICggJ3VuZGVmaW5lZCcgPT0gZS5zaGlmdEtleSApIHtcclxuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoIGUuc2hpZnRLZXkgKSB7XHJcblx0XHRcdFx0aWYgKCAhIGxhc3RDbGlja2VkICkge1xyXG5cdFx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGNoZWNrcyAgPSAkKCBsYXN0Q2xpY2tlZCApLmNsb3Nlc3QoICcud3BiY19zZWxlY3RhYmxlX2JvZHknICkuZmluZCggJzpjaGVja2JveCcgKS5maWx0ZXIoICc6dmlzaWJsZTplbmFibGVkJyApO1xyXG5cdFx0XHRcdGZpcnN0ICAgPSBjaGVja3MuaW5kZXgoIGxhc3RDbGlja2VkICk7XHJcblx0XHRcdFx0bGFzdCAgICA9IGNoZWNrcy5pbmRleCggdGhpcyApO1xyXG5cdFx0XHRcdGNoZWNrZWQgPSAkKCB0aGlzICkucHJvcCggJ2NoZWNrZWQnICk7XHJcblx0XHRcdFx0aWYgKCAwIDwgZmlyc3QgJiYgMCA8IGxhc3QgJiYgZmlyc3QgIT0gbGFzdCApIHtcclxuXHRcdFx0XHRcdHNsaWNlZCA9IChsYXN0ID4gZmlyc3QpID8gY2hlY2tzLnNsaWNlKCBmaXJzdCwgbGFzdCApIDogY2hlY2tzLnNsaWNlKCBsYXN0LCBmaXJzdCApO1xyXG5cdFx0XHRcdFx0c2xpY2VkLnByb3AoXHJcblx0XHRcdFx0XHRcdCdjaGVja2VkJyxcclxuXHRcdFx0XHRcdFx0ZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdFx0XHRcdGlmICggJCggdGhpcyApLmNsb3Nlc3QoICcud3BiY19yb3cnICkuaXMoICc6dmlzaWJsZScgKSApIHtcclxuXHRcdFx0XHRcdFx0XHRcdHJldHVybiBjaGVja2VkO1xyXG5cdFx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdCkudHJpZ2dlciggJ2NoYW5nZScgKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0bGFzdENsaWNrZWQgPSB0aGlzO1xyXG5cclxuXHRcdFx0Ly8gdG9nZ2xlIFwiY2hlY2sgYWxsXCIgY2hlY2tib3hlcy5cclxuXHRcdFx0dmFyIHVuY2hlY2tlZCA9ICQoIHRoaXMgKS5jbG9zZXN0KCAnLndwYmNfc2VsZWN0YWJsZV9ib2R5JyApLmZpbmQoICc6Y2hlY2tib3gnICkuZmlsdGVyKCAnOnZpc2libGU6ZW5hYmxlZCcgKS5ub3QoICc6Y2hlY2tlZCcgKTtcclxuXHRcdFx0JCggdGhpcyApLmNsb3Nlc3QoICcud3BiY19zZWxlY3RhYmxlX3RhYmxlJyApLmNoaWxkcmVuKCAnLndwYmNfc2VsZWN0YWJsZV9oZWFkLCAud3BiY19zZWxlY3RhYmxlX2Zvb3QnICkuZmluZCggJzpjaGVja2JveCcgKS5wcm9wKFxyXG5cdFx0XHRcdCdjaGVja2VkJyxcclxuXHRcdFx0XHRmdW5jdGlvbiAoKSB7XHJcblx0XHRcdFx0XHRyZXR1cm4gKDAgPT09IHVuY2hlY2tlZC5sZW5ndGgpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0KS50cmlnZ2VyKCAnY2hhbmdlJyApO1xyXG5cclxuXHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHR9XHJcblx0KTtcclxuXHJcblx0Ly8gSGVhZCB8fCBGb290IGNsaWNraW5nIHRvICBzZWxlY3QgLyBkZXNlbGVjdCBBTEwuXHJcblx0JCggJy53cGJjX3NlbGVjdGFibGVfaGVhZCwgLndwYmNfc2VsZWN0YWJsZV9mb290JyApLmZpbmQoICcuY2hlY2stY29sdW1uIDpjaGVja2JveCcgKS5vbihcclxuXHRcdCdjbGljaycsXHJcblx0XHRmdW5jdGlvbiAoZXZlbnQpIHtcclxuXHRcdFx0dmFyICR0aGlzICAgICAgICAgID0gJCggdGhpcyApLFxyXG5cdFx0XHRcdCR0YWJsZSAgICAgICAgID0gJHRoaXMuY2xvc2VzdCggJy53cGJjX3NlbGVjdGFibGVfdGFibGUnICksXHJcblx0XHRcdFx0Y29udHJvbENoZWNrZWQgPSAkdGhpcy5wcm9wKCAnY2hlY2tlZCcgKSxcclxuXHRcdFx0XHR0b2dnbGUgICAgICAgICA9IGV2ZW50LnNoaWZ0S2V5IHx8ICR0aGlzLmRhdGEoICd3cC10b2dnbGUnICk7XHJcblxyXG5cdFx0XHQkdGFibGUuY2hpbGRyZW4oICcud3BiY19zZWxlY3RhYmxlX2JvZHknICkuZmlsdGVyKCAnOnZpc2libGUnIClcclxuXHRcdFx0XHQuZmluZCggJy5jaGVjay1jb2x1bW4nICkuZmluZCggJzpjaGVja2JveCcgKVxyXG5cdFx0XHRcdC5wcm9wKFxyXG5cdFx0XHRcdFx0J2NoZWNrZWQnLFxyXG5cdFx0XHRcdFx0ZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdFx0XHRpZiAoICQoIHRoaXMgKS5pcyggJzpoaWRkZW4sOmRpc2FibGVkJyApICkge1xyXG5cdFx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRpZiAoIHRvZ2dsZSApIHtcclxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gISAkKCB0aGlzICkucHJvcCggJ2NoZWNrZWQnICk7XHJcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIGNvbnRyb2xDaGVja2VkICkge1xyXG5cdFx0XHRcdFx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHQpLnRyaWdnZXIoICdjaGFuZ2UnICk7XHJcblxyXG5cdFx0XHQkdGFibGUuY2hpbGRyZW4oICcud3BiY19zZWxlY3RhYmxlX2hlYWQsICAud3BiY19zZWxlY3RhYmxlX2Zvb3QnICkuZmlsdGVyKCAnOnZpc2libGUnIClcclxuXHRcdFx0XHQuZmluZCggJy5jaGVjay1jb2x1bW4nICkuZmluZCggJzpjaGVja2JveCcgKVxyXG5cdFx0XHRcdC5wcm9wKFxyXG5cdFx0XHRcdFx0J2NoZWNrZWQnLFxyXG5cdFx0XHRcdFx0ZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRcdFx0XHRpZiAoIHRvZ2dsZSApIHtcclxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIGNvbnRyb2xDaGVja2VkICkge1xyXG5cdFx0XHRcdFx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHQpO1xyXG5cdFx0fVxyXG5cdCk7XHJcblxyXG5cclxuXHQvLyBWaXN1YWxseSAgc2hvdyBzZWxlY3RlZCBib3JkZXIuXHJcblx0JCggJy53cGJjX3NlbGVjdGFibGVfYm9keScgKS5maW5kKCAnLmNoZWNrLWNvbHVtbiA6Y2hlY2tib3gnICkub24oXHJcblx0XHQnY2hhbmdlJyxcclxuXHRcdGZ1bmN0aW9uIChldmVudCkge1xyXG5cdFx0XHRpZiAoIGpRdWVyeSggdGhpcyApLmlzKCAnOmNoZWNrZWQnICkgKSB7XHJcblx0XHRcdFx0alF1ZXJ5KCB0aGlzICkuY2xvc2VzdCggJy53cGJjX2xpc3Rfcm93JyApLmFkZENsYXNzKCAncm93X3NlbGVjdGVkX2NvbG9yJyApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGpRdWVyeSggdGhpcyApLmNsb3Nlc3QoICcud3BiY19saXN0X3JvdycgKS5yZW1vdmVDbGFzcyggJ3Jvd19zZWxlY3RlZF9jb2xvcicgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gRGlzYWJsZSB0ZXh0IHNlbGVjdGlvbiB3aGlsZSBwcmVzc2luZyAnc2hpZnQnLlxyXG5cdFx0XHRkb2N1bWVudC5nZXRTZWxlY3Rpb24oKS5yZW1vdmVBbGxSYW5nZXMoKTtcclxuXHJcblx0XHRcdC8vIFNob3cgb3IgaGlkZSBidXR0b25zIG9uIEFjdGlvbnMgdG9vbGJhciAgYXQgIEJvb2tpbmcgTGlzdGluZyAgcGFnZSwgIGlmIHdlIGhhdmUgc29tZSBzZWxlY3RlZCBib29raW5ncy5cclxuXHRcdFx0d3BiY19zaG93X2hpZGVfYWN0aW9uX2J1dHRvbnNfZm9yX3NlbGVjdGVkX2Jvb2tpbmdzKCk7XHJcblx0XHR9XHJcblx0KTtcclxuXHJcblx0d3BiY19zaG93X2hpZGVfYWN0aW9uX2J1dHRvbnNfZm9yX3NlbGVjdGVkX2Jvb2tpbmdzKCk7XHJcbn1cclxuIiwiXHJcbi8qKlxyXG4gKiBHZXQgSUQgYXJyYXkgIG9mIHNlbGVjdGVkIGVsZW1lbnRzXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2dldF9zZWxlY3RlZF9yb3dfaWQoKSB7XHJcblxyXG5cdHZhciAkdGFibGUgICAgICA9IGpRdWVyeSggJy53cGJjX193cmFwX19ib29raW5nX2xpc3RpbmcgLndwYmNfc2VsZWN0YWJsZV90YWJsZScgKTtcclxuXHR2YXIgY2hlY2tib3hlcyAgPSAkdGFibGUuY2hpbGRyZW4oICcud3BiY19zZWxlY3RhYmxlX2JvZHknICkuZmlsdGVyKCAnOnZpc2libGUnICkuZmluZCggJy5jaGVjay1jb2x1bW4nICkuZmluZCggJzpjaGVja2JveCcgKTtcclxuXHR2YXIgc2VsZWN0ZWRfaWQgPSBbXTtcclxuXHJcblx0alF1ZXJ5LmVhY2goXHJcblx0XHRjaGVja2JveGVzLFxyXG5cdFx0ZnVuY3Rpb24gKGtleSwgY2hlY2tib3gpIHtcclxuXHRcdFx0aWYgKCBqUXVlcnkoIGNoZWNrYm94ICkuaXMoICc6Y2hlY2tlZCcgKSApIHtcclxuXHRcdFx0XHR2YXIgZWxlbWVudF9pZCA9IHdwYmNfZ2V0X3Jvd19pZF9mcm9tX2VsZW1lbnQoIGNoZWNrYm94ICk7XHJcblx0XHRcdFx0c2VsZWN0ZWRfaWQucHVzaCggZWxlbWVudF9pZCApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0KTtcclxuXHJcblx0cmV0dXJuIHNlbGVjdGVkX2lkO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEdldCBJRCBvZiByb3csICBiYXNlZCBvbiBjbGNpa2VkIGVsZW1lbnRcclxuICpcclxuICogQHBhcmFtIHRoaXNfaW5ib3VuZF9lbGVtZW50ICAtIHVzdXNsbHkgIHRoaXNcclxuICogQHJldHVybnMge251bWJlcn1cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfZ2V0X3Jvd19pZF9mcm9tX2VsZW1lbnQodGhpc19pbmJvdW5kX2VsZW1lbnQpIHtcclxuXHJcblx0dmFyIGVsZW1lbnRfaWQgPSBqUXVlcnkoIHRoaXNfaW5ib3VuZF9lbGVtZW50ICkuY2xvc2VzdCggJy53cGJjX2xpc3RpbmdfdXN1YWxfcm93JyApLmF0dHIoICdpZCcgKTtcclxuXHJcblx0ZWxlbWVudF9pZCA9IHBhcnNlSW50KCBlbGVtZW50X2lkLnJlcGxhY2UoICdyb3dfaWRfJywgJycgKSApO1xyXG5cclxuXHRyZXR1cm4gZWxlbWVudF9pZDtcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiA9PSBCb29raW5nIExpc3RpbmcgPT0gU2hvdyBvciBoaWRlIGJ1dHRvbnMgb24gQWN0aW9ucyB0b29sYmFyICBhdCAgICBwYWdlLCAgaWYgd2UgaGF2ZSBzb21lIHNlbGVjdGVkIGJvb2tpbmdzLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19zaG93X2hpZGVfYWN0aW9uX2J1dHRvbnNfZm9yX3NlbGVjdGVkX2Jvb2tpbmdzKCl7XHJcblxyXG5cdHZhciBzZWxlY3RlZF9yb3dzX2FyciA9IHdwYmNfZ2V0X3NlbGVjdGVkX3Jvd19pZCgpO1xyXG5cclxuXHRpZiAoIHNlbGVjdGVkX3Jvd3NfYXJyLmxlbmd0aCA+IDAgKSB7XHJcblx0XHRqUXVlcnkoICcuaGlkZV9idXR0b25faWZfbm9fc2VsZWN0aW9uJyApLnNob3coKTtcclxuXHR9IGVsc2Uge1xyXG5cdFx0alF1ZXJ5KCAnLmhpZGVfYnV0dG9uX2lmX25vX3NlbGVjdGlvbicgKS5oaWRlKCk7XHJcblx0fVxyXG59IiwiXCJ1c2Ugc3RyaWN0XCI7XHJcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4vLyA9PSBMZWZ0IEJhciAgLSAgZXhwYW5kIC8gY29sYXBzZSBmdW5jdGlvbnMgICA9PVxyXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHJcbi8qKlxyXG4gKiBTYXZlIHVzZXIncyBwcmVmZXJyZWQgbGVmdCBzaWRlYmFyIG1vZGUuXHJcbiAqXHJcbiAqIEBwYXJhbSBzdHJpbmcgbW9kZVxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fc2lkZWJhcl9sZWZ0X19zYXZlX21vZGUoIG1vZGUgKSB7XHJcblx0dmFyIGFsbG93ZWRfbW9kZXMgPSBbICdtaW4nLCAnY29tcGFjdCcsICdtYXgnIF07XHJcblxyXG5cdGlmICggYWxsb3dlZF9tb2Rlcy5pbmRleE9mKCBtb2RlICkgPT09IC0xICkge1xyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHJcblx0dmFyICRzYXZlciA9IGpRdWVyeSggJyN3cGJjX2xlZnRfc2lkZWJhcl92aWV3X21vZGVfc2F2ZXInICk7XHJcblxyXG5cdGlmICggISAkc2F2ZXIubGVuZ3RoICkge1xyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHJcblx0aWYgKCAnZnVuY3Rpb24nICE9PSB0eXBlb2Ygd3BiY19zYXZlX2N1c3RvbV91c2VyX2RhdGFfZnJvbV9lbGVtZW50ICkge1xyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHJcblx0JHNhdmVyLmRhdGEoICd3cGJjLXUtc2F2ZS12YWx1ZScsIG1vZGUgKTtcclxuXHQkc2F2ZXIuYXR0ciggJ2RhdGEtd3BiYy11LXNhdmUtdmFsdWUnLCBtb2RlICk7XHJcblxyXG5cdHdwYmNfc2F2ZV9jdXN0b21fdXNlcl9kYXRhX2Zyb21fZWxlbWVudCggJHNhdmVyLmdldCggMCApICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBhbmQgVmVydGljYWwgTGVmdCBCYXIuXHJcbiAqXHJcbiAqIEBwYXJhbSBib29sIGlzX3NhdmVfdXNlcl9zdGF0ZSBTYXZlIHRoaXMgbW9kZSBhcyB1c2VyJ3MgcHJlZmVyZW5jZS5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX3NpZGViYXJfbGVmdF9fZG9fbWF4KCBpc19zYXZlX3VzZXJfc3RhdGUgKSB7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLnJlbW92ZUNsYXNzKCAnbWluIG1heCBjb21wYWN0IG5vbmUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLmFkZENsYXNzKCAnbWF4JyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3VpX190b3BfbmF2X19idG5fb3Blbl9sZWZ0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9oaWRlX2xlZnRfdmVydGljYWxfbmF2JyApLnJlbW92ZUNsYXNzKCAnd3BiY191aV9faGlkZScgKTtcclxuXHJcblx0alF1ZXJ5KCAnLndwLWFkbWluJyApLnJlbW92ZUNsYXNzKCAnd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9taW4gd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9tYXggd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9jb21wYWN0IHdwYmNfcGFnZV93cmFwcGVyX2xlZnRfbm9uZScgKTtcclxuXHRqUXVlcnkoICcud3AtYWRtaW4nICkuYWRkQ2xhc3MoICd3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X21heCcgKTtcclxuXHJcblx0aWYgKCBpc19zYXZlX3VzZXJfc3RhdGUgKSB7XHJcblx0XHR3cGJjX2FkbWluX3VpX19zaWRlYmFyX2xlZnRfX3NhdmVfbW9kZSggJ21heCcgKTtcclxuXHR9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIaWRlIFZlcnRpY2FsIExlZnQgQmFyLlxyXG4gKlxyXG4gKiBAcGFyYW0gYm9vbCBpc19zYXZlX3VzZXJfc3RhdGUgU2F2ZSB0aGlzIG1vZGUgYXMgdXNlcidzIHByZWZlcmVuY2UuXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2FkbWluX3VpX19zaWRlYmFyX2xlZnRfX2RvX21pbiggaXNfc2F2ZV91c2VyX3N0YXRlICkge1xyXG5cdGpRdWVyeSggJy53cGJjX3NldHRpbmdzX3BhZ2Vfd3JhcHBlcicgKS5yZW1vdmVDbGFzcyggJ21pbiBtYXggY29tcGFjdCBub25lJyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3NldHRpbmdzX3BhZ2Vfd3JhcHBlcicgKS5hZGRDbGFzcyggJ21pbicgKTtcclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX29wZW5fbGVmdF92ZXJ0aWNhbF9uYXYnICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3VpX190b3BfbmF2X19idG5faGlkZV9sZWZ0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblxyXG5cdGpRdWVyeSggJy53cC1hZG1pbicgKS5yZW1vdmVDbGFzcyggJ3dwYmNfcGFnZV93cmFwcGVyX2xlZnRfbWluIHdwYmNfcGFnZV93cmFwcGVyX2xlZnRfbWF4IHdwYmNfcGFnZV93cmFwcGVyX2xlZnRfY29tcGFjdCB3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X25vbmUnICk7XHJcblx0alF1ZXJ5KCAnLndwLWFkbWluJyApLmFkZENsYXNzKCAnd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9taW4nICk7XHJcblxyXG5cdGlmICggaXNfc2F2ZV91c2VyX3N0YXRlICkge1xyXG5cdFx0d3BiY19hZG1pbl91aV9fc2lkZWJhcl9sZWZ0X19zYXZlX21vZGUoICdtaW4nICk7XHJcblx0fVxyXG59XHJcblxyXG4vKipcclxuICogQ29sYXBzZSBWZXJ0aWNhbCBMZWZ0IEJhci5cclxuICpcclxuICogQHBhcmFtIGJvb2wgaXNfc2F2ZV91c2VyX3N0YXRlIFNhdmUgdGhpcyBtb2RlIGFzIHVzZXIncyBwcmVmZXJlbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fc2lkZWJhcl9sZWZ0X19kb19jb21wYWN0KCBpc19zYXZlX3VzZXJfc3RhdGUgKSB7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLnJlbW92ZUNsYXNzKCAnbWluIG1heCBjb21wYWN0IG5vbmUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLmFkZENsYXNzKCAnY29tcGFjdCcgKTtcclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX29wZW5fbGVmdF92ZXJ0aWNhbF9uYXYnICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3VpX190b3BfbmF2X19idG5faGlkZV9sZWZ0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblxyXG5cdGpRdWVyeSggJy53cC1hZG1pbicgKS5yZW1vdmVDbGFzcyggJ3dwYmNfcGFnZV93cmFwcGVyX2xlZnRfbWluIHdwYmNfcGFnZV93cmFwcGVyX2xlZnRfbWF4IHdwYmNfcGFnZV93cmFwcGVyX2xlZnRfY29tcGFjdCB3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X25vbmUnICk7XHJcblx0alF1ZXJ5KCAnLndwLWFkbWluJyApLmFkZENsYXNzKCAnd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9jb21wYWN0JyApO1xyXG5cclxuXHRpZiAoIGlzX3NhdmVfdXNlcl9zdGF0ZSApIHtcclxuXHRcdHdwYmNfYWRtaW5fdWlfX3NpZGViYXJfbGVmdF9fc2F2ZV9tb2RlKCAnY29tcGFjdCcgKTtcclxuXHR9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb21wbGV0ZWx5IEhpZGUgVmVydGljYWwgTGVmdCBCYXIuXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2FkbWluX3VpX19zaWRlYmFyX2xlZnRfX2RvX2hpZGUoKSB7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLnJlbW92ZUNsYXNzKCAnbWluIG1heCBjb21wYWN0IG5vbmUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLmFkZENsYXNzKCAnbm9uZScgKTtcclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX29wZW5fbGVmdF92ZXJ0aWNhbF9uYXYnICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3VpX190b3BfbmF2X19idG5faGlkZV9sZWZ0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0Ly8gSGlkZSB0b3AgXCJNZW51XCIgYnV0dG9uIHdpdGggZGl2aWRlci5cclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX3Nob3dfbGVmdF92ZXJ0aWNhbF9uYXYsLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9zaG93X2xlZnRfdmVydGljYWxfbmF2X2RpdmlkZXInICkuYWRkQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG5cclxuXHRqUXVlcnkoICcud3AtYWRtaW4nICkucmVtb3ZlQ2xhc3MoICd3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X21pbiB3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X21heCB3cGJjX3BhZ2Vfd3JhcHBlcl9sZWZ0X2NvbXBhY3Qgd3BiY19wYWdlX3dyYXBwZXJfbGVmdF9ub25lJyApO1xyXG5cdGpRdWVyeSggJy53cC1hZG1pbicgKS5hZGRDbGFzcyggJ3dwYmNfcGFnZV93cmFwcGVyX2xlZnRfbm9uZScgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBvbiBjbGljayBcIkdvIEJhY2tcIiAtIHNob3cgcm9vdCBtZW51XHJcbiAqIG9yIHNvbWUgb3RoZXIgc2VjdGlvbiBpbiBsZWZ0IHNpZGViYXIuXHJcbiAqXHJcbiAqIEBwYXJhbSBzdHJpbmcgbWVudV90b19zaG93IC0gbWVudSBzbHVnLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fc2lkZWJhcl9sZWZ0X19zaG93X3NlY3Rpb24oIG1lbnVfdG9fc2hvdyApIHtcclxuXHRqUXVlcnkoICcud3BiY191aV9lbF9fdmVydF9sZWZ0X2Jhcl9fc2VjdGlvbicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnIClcclxuXHRqUXVlcnkoICcud3BiY191aV9lbF9fdmVydF9sZWZ0X2Jhcl9fc2VjdGlvbl8nICsgbWVudV90b19zaG93ICkucmVtb3ZlQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApO1xyXG59XHJcblxyXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuLy8gPT0gUmlnaHQgU2lkZSBCYXIgIC0gIGV4cGFuZCAvIGNvbGFwc2UgZnVuY3Rpb25zICAgPT1cclxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblxyXG4vKipcclxuICogRXhwYW5kIFZlcnRpY2FsIFJpZ2h0IEJhci5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX3NpZGViYXJfcmlnaHRfX2RvX21heCgpIHtcclxuXHRqUXVlcnkoICcud3BiY19zZXR0aW5nc19wYWdlX3dyYXBwZXInICkucmVtb3ZlQ2xhc3MoICdtaW5fcmlnaHQgbWF4X3JpZ2h0IGNvbXBhY3RfcmlnaHQgbm9uZV9yaWdodCcgKTtcclxuXHRqUXVlcnkoICcud3BiY19zZXR0aW5nc19wYWdlX3dyYXBwZXInICkuYWRkQ2xhc3MoICdtYXhfcmlnaHQnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9vcGVuX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9oaWRlX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5yZW1vdmVDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIaWRlIFZlcnRpY2FsIFJpZ2h0IEJhci5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX3NpZGViYXJfcmlnaHRfX2RvX21pbigpIHtcclxuXHRqUXVlcnkoICcud3BiY19zZXR0aW5nc19wYWdlX3dyYXBwZXInICkucmVtb3ZlQ2xhc3MoICdtaW5fcmlnaHQgbWF4X3JpZ2h0IGNvbXBhY3RfcmlnaHQgbm9uZV9yaWdodCcgKTtcclxuXHRqUXVlcnkoICcud3BiY19zZXR0aW5nc19wYWdlX3dyYXBwZXInICkuYWRkQ2xhc3MoICdtaW5fcmlnaHQnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9vcGVuX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5yZW1vdmVDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9oaWRlX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb2xhcHNlIFZlcnRpY2FsIFJpZ2h0IEJhci5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX3NpZGViYXJfcmlnaHRfX2RvX2NvbXBhY3QoKSB7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLnJlbW92ZUNsYXNzKCAnbWluX3JpZ2h0IG1heF9yaWdodCBjb21wYWN0X3JpZ2h0IG5vbmVfcmlnaHQnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfc2V0dGluZ3NfcGFnZV93cmFwcGVyJyApLmFkZENsYXNzKCAnY29tcGFjdF9yaWdodCcgKTtcclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX29wZW5fcmlnaHRfdmVydGljYWxfbmF2JyApLnJlbW92ZUNsYXNzKCAnd3BiY191aV9faGlkZScgKTtcclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX2hpZGVfcmlnaHRfdmVydGljYWxfbmF2JyApLmFkZENsYXNzKCAnd3BiY191aV9faGlkZScgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbXBsZXRlbHkgSGlkZSBWZXJ0aWNhbCBSaWdodCBCYXIuXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2FkbWluX3VpX19zaWRlYmFyX3JpZ2h0X19kb19oaWRlKCkge1xyXG5cdGpRdWVyeSggJy53cGJjX3NldHRpbmdzX3BhZ2Vfd3JhcHBlcicgKS5yZW1vdmVDbGFzcyggJ21pbl9yaWdodCBtYXhfcmlnaHQgY29tcGFjdF9yaWdodCBub25lX3JpZ2h0JyApO1xyXG5cdGpRdWVyeSggJy53cGJjX3NldHRpbmdzX3BhZ2Vfd3JhcHBlcicgKS5hZGRDbGFzcyggJ25vbmVfcmlnaHQnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9vcGVuX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5yZW1vdmVDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfX3RvcF9uYXZfX2J0bl9oaWRlX3JpZ2h0X3ZlcnRpY2FsX25hdicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcblx0Ly8gSGlkZSB0b3AgXCJNZW51XCIgYnV0dG9uIHdpdGggZGl2aWRlci5cclxuXHRqUXVlcnkoICcud3BiY191aV9fdG9wX25hdl9fYnRuX3Nob3dfcmlnaHRfdmVydGljYWxfbmF2LC53cGJjX3VpX190b3BfbmF2X19idG5fc2hvd19yaWdodF92ZXJ0aWNhbF9uYXZfZGl2aWRlcicgKS5hZGRDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBY3Rpb24gb24gY2xpY2sgXCJHbyBCYWNrXCIgLSBzaG93IHJvb3QgbWVudVxyXG4gKiBvciBzb21lIG90aGVyIHNlY3Rpb24gaW4gcmlnaHQgc2lkZWJhci5cclxuICpcclxuICogQHBhcmFtIHN0cmluZyBtZW51X3RvX3Nob3cgLSBtZW51IHNsdWcuXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2FkbWluX3VpX19zaWRlYmFyX3JpZ2h0X19zaG93X3NlY3Rpb24oIG1lbnVfdG9fc2hvdyApIHtcclxuXHRqUXVlcnkoICcud3BiY191aV9lbF9fdmVydF9yaWdodF9iYXJfX3NlY3Rpb24nICkuYWRkQ2xhc3MoICd3cGJjX3VpX19oaWRlJyApXHJcblx0alF1ZXJ5KCAnLndwYmNfdWlfZWxfX3ZlcnRfcmlnaHRfYmFyX19zZWN0aW9uXycgKyBtZW51X3RvX3Nob3cgKS5yZW1vdmVDbGFzcyggJ3dwYmNfdWlfX2hpZGUnICk7XHJcbn1cclxuXHJcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4vLyA9PSBFbmQgUmlnaHQgU2lkZSBCYXIgIHNlY3Rpb24gICA9PVxyXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYW5jaG9yKHMpIGFycmF5ICBmcm9tICBVUkwuXHJcbiAqIERvYzogaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0xvY2F0aW9uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsqW119XHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX3VybF9nZXRfYW5jaG9yc19hcnIoKSB7XHJcblx0dmFyIGhhc2hlcyAgICAgICAgICAgID0gd2luZG93LmxvY2F0aW9uLmhhc2gucmVwbGFjZSggJyUyMycsICcjJyApO1xyXG5cdHZhciBoYXNoZXNfYXJyICAgICAgICA9IGhhc2hlcy5zcGxpdCggJyMnICk7XHJcblx0dmFyIHJlc3VsdCAgICAgICAgICAgID0gW107XHJcblx0dmFyIGhhc2hlc19hcnJfbGVuZ3RoID0gaGFzaGVzX2Fyci5sZW5ndGg7XHJcblxyXG5cdGZvciAoIHZhciBpID0gMDsgaSA8IGhhc2hlc19hcnJfbGVuZ3RoOyBpKysgKSB7XHJcblx0XHRpZiAoIGhhc2hlc19hcnJbaV0ubGVuZ3RoID4gMCApIHtcclxuXHRcdFx0cmVzdWx0LnB1c2goIGhhc2hlc19hcnJbaV0gKTtcclxuXHRcdH1cclxuXHR9XHJcblx0cmV0dXJuIHJlc3VsdDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF1dG8gRXhwYW5kIFNldHRpbmdzIHNlY3Rpb24gYmFzZWQgb24gVVJMIGFuY2hvciwgYWZ0ZXIgIHBhZ2UgbG9hZGVkLlxyXG4gKi9cclxualF1ZXJ5KCBkb2N1bWVudCApLnJlYWR5KCBmdW5jdGlvbiAoKSB7IHdwYmNfYWRtaW5fdWlfX3JlZGlyZWN0X2xlZ2FjeV9nZW5lcmFsX2F2YWlsYWJpbGl0eV91cmwoKTsgfSApO1xyXG5qUXVlcnkoIGRvY3VtZW50ICkucmVhZHkoIGZ1bmN0aW9uICgpIHsgd3BiY19hZG1pbl91aV9fZG9fZXhwYW5kX3NlY3Rpb24oKTsgc2V0VGltZW91dCggJ3dwYmNfYWRtaW5fdWlfX2RvX2V4cGFuZF9zZWN0aW9uJywgMTAgKTsgfSApO1xyXG5qUXVlcnkoIGRvY3VtZW50ICkucmVhZHkoIGZ1bmN0aW9uICgpIHsgd3BiY19hZG1pbl91aV9fZG9fZXhwYW5kX3NlY3Rpb24oKTsgc2V0VGltZW91dCggJ3dwYmNfYWRtaW5fdWlfX2RvX2V4cGFuZF9zZWN0aW9uJywgMTUwICk7IH0gKTtcclxuXHJcbi8qKlxyXG4gKiBSZWRpcmVjdCBvbGQgU2V0dGluZ3MgPiBBdmFpbGFiaWxpdHkgYW5jaG9ycyB0byB0aGUgZGVkaWNhdGVkIEdlbmVyYWwgQXZhaWxhYmlsaXR5IHBhZ2UuXHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2FkbWluX3VpX19yZWRpcmVjdF9sZWdhY3lfZ2VuZXJhbF9hdmFpbGFiaWxpdHlfdXJsKCkge1xyXG5cclxuXHRpZiAoXHJcblx0XHQgICAoIHdpbmRvdy5sb2NhdGlvbi5ocmVmLmluZGV4T2YoICdwYWdlPXdwYmMtc2V0dGluZ3MnICkgPiAtMSApXHJcblx0XHQmJiAoXHJcblx0XHRcdCAgICggd2luZG93LmxvY2F0aW9uLmhhc2guaW5kZXhPZiggJ3dwYmNfZ2VuZXJhbF9zZXR0aW5nc19hdmFpbGFiaWxpdHlfbWV0YWJveCcgKSA+IC0xIClcclxuXHRcdFx0fHwgKCB3aW5kb3cubG9jYXRpb24uaGFzaC5pbmRleE9mKCAnd3BiY19nZW5lcmFsX3NldHRpbmdzX2F2YWlsYWJpbGl0eV90YWInICkgPiAtMSApXHJcblx0XHQpXHJcblx0KSB7XHJcblx0XHR3aW5kb3cubG9jYXRpb24ucmVwbGFjZSggd2luZG93LmxvY2F0aW9uLmhyZWYuc3BsaXQoICc/JyApWzBdICsgJz9wYWdlPXdwYmMtYXZhaWxhYmlsaXR5JnRhYj1nZW5lcmFsX2F2YWlsYWJpbGl0eScgKTtcclxuXHR9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBhbmQgc2VjdGlvbiBpbiAgR2VuZXJhbCBTZXR0aW5ncyBwYWdlIGFuZCBzZWxlY3QgTWVudSBpdGVtLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fZG9fZXhwYW5kX3NlY3Rpb24oKSB7XHJcblxyXG5cdC8vIHdpbmRvdy5sb2NhdGlvbi5oYXNoICA9ICNzZWN0aW9uX2lkICAvICBkb2M6IGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9Mb2NhdGlvbiAuXHJcblx0dmFyIGFuY2hvcnNfYXJyICAgICAgICA9IHdwYmNfdXJsX2dldF9hbmNob3JzX2FycigpO1xyXG5cdHZhciBhbmNob3JzX2Fycl9sZW5ndGggPSBhbmNob3JzX2Fyci5sZW5ndGg7XHJcblxyXG5cdGlmICggYW5jaG9yc19hcnJfbGVuZ3RoID4gMCApIHtcclxuXHRcdHZhciBvbmVfYW5jaG9yX3Byb3BfdmFsdWUgPSBhbmNob3JzX2FyclswXS5zcGxpdCggJ2RvX2V4cGFuZF9fJyApO1xyXG5cdFx0aWYgKCBvbmVfYW5jaG9yX3Byb3BfdmFsdWUubGVuZ3RoID4gMSApIHtcclxuXHJcblx0XHRcdC8vICd3cGJjX2dlbmVyYWxfc2V0dGluZ3NfY2FsZW5kYXJfbWV0YWJveCdcclxuXHRcdFx0dmFyIHNlY3Rpb25fdG9fc2hvdyAgICA9IG9uZV9hbmNob3JfcHJvcF92YWx1ZVsxXTtcclxuXHRcdFx0dmFyIHNlY3Rpb25faWRfdG9fc2hvdyA9ICcjJyArIHNlY3Rpb25fdG9fc2hvdztcclxuXHJcblxyXG5cdFx0XHQvLyAtLSBSZW1vdmUgc2VsZWN0ZWQgYmFja2dyb3VuZCBpbiBhbGwgbGVmdCAgbWVudSAgaXRlbXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHRcdGpRdWVyeSggJy53cGJjX3VpX2VsX192ZXJ0X25hdl9pdGVtICcgKS5yZW1vdmVDbGFzcyggJ2FjdGl2ZScgKTtcclxuXHRcdFx0Ly8gU2V0IGxlZnQgbWVudSBzZWxlY3RlZC5cclxuXHRcdFx0alF1ZXJ5KCAnLmRvX2V4cGFuZF9fJyArIHNlY3Rpb25fdG9fc2hvdyArICdfbGluaycgKS5hZGRDbGFzcyggJ2FjdGl2ZScgKTtcclxuXHRcdFx0dmFyIHNlbGVjdGVkX3RpdGxlID0galF1ZXJ5KCAnLmRvX2V4cGFuZF9fJyArIHNlY3Rpb25fdG9fc2hvdyArICdfbGluayBhIC53cGJjX3VpX2VsX192ZXJ0X25hdl90aXRsZSAnICkudGV4dCgpO1xyXG5cclxuXHRcdFx0Ly8gRXhwYW5kIHNlY3Rpb24sIGlmIGl0IGNvbGFwc2VkLlxyXG5cdFx0XHRpZiAoICEgalF1ZXJ5KCAnLmRvX2V4cGFuZF9fJyArIHNlY3Rpb25fdG9fc2hvdyArICdfbGluaycgKS5wYXJlbnRzKCAnLndwYmNfdWlfZWxfX2xldmVsX19mb2xkZXInICkuaGFzQ2xhc3MoICdleHBhbmRlZCcgKSApIHtcclxuXHRcdFx0XHRqUXVlcnkoICcud3BiY191aV9lbF9fbGV2ZWxfX2ZvbGRlcicgKS5yZW1vdmVDbGFzcyggJ2V4cGFuZGVkJyApO1xyXG5cdFx0XHRcdGpRdWVyeSggJy5kb19leHBhbmRfXycgKyBzZWN0aW9uX3RvX3Nob3cgKyAnX2xpbmsnICkucGFyZW50cyggJy53cGJjX3VpX2VsX19sZXZlbF9fZm9sZGVyJyApLmFkZENsYXNzKCAnZXhwYW5kZWQnICk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIC0tIEV4cGFuZCBzZWN0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdFx0XHR2YXIgY29udGFpbmVyX3RvX2hpZGVfY2xhc3MgPSAnLnBvc3Rib3gnO1xyXG5cdFx0XHQvLyBIaWRlIHNlY3Rpb25zICcucG9zdGJveCcgaW4gYWRtaW4gcGFnZSBhbmQgc2hvdyBzcGVjaWZpYyBvbmUuXHJcblx0XHRcdGpRdWVyeSggJy53cGJjX2FkbWluX3BhZ2UgJyArIGNvbnRhaW5lcl90b19oaWRlX2NsYXNzICkuaGlkZSgpO1xyXG5cdFx0XHRqUXVlcnkoICcud3BiY19jb250YWluZXJfYWx3YXlzX2hpZGVfX29uX2xlZnRfbmF2X2NsaWNrJyApLmhpZGUoKTtcclxuXHRcdFx0alF1ZXJ5KCBzZWN0aW9uX2lkX3RvX3Nob3cgKS5zaG93KCk7XHJcblxyXG5cdFx0XHQvLyBTaG93IGFsbCBvdGhlciBzZWN0aW9ucywgIGlmIHByb3ZpZGVkIGluIFVSTDogLi4/cGFnZT13cGJjLXNldHRpbmdzI2RvX2V4cGFuZF9fd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhcGFjaXR5X21ldGFib3gjd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhcGFjaXR5X3VwZ3JhZGVfbWV0YWJveCAuXHJcblx0XHRcdGZvciAoIGxldCBpID0gMTsgaSA8IGFuY2hvcnNfYXJyX2xlbmd0aDsgaSsrICkge1xyXG5cdFx0XHRcdGpRdWVyeSggJyMnICsgYW5jaG9yc19hcnJbaV0gKS5zaG93KCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGlmICggZmFsc2UgKSB7XHJcblx0XHRcdFx0dmFyIHRhcmdldE9mZnNldCA9IHdwYmNfc2Nyb2xsX3RvKCBzZWN0aW9uX2lkX3RvX3Nob3cgKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gLS0gU2V0IFZhbHVlIHRvIElucHV0IGFib3V0IHNlbGVjdGVkIE5hdiBlbGVtZW50ICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gICAgICAgLy8gRml4SW46IDkuOC42LjEuXHJcblx0XHRcdHZhciBzZWN0aW9uX2lkX3RhYiA9IHNlY3Rpb25faWRfdG9fc2hvdy5zdWJzdHJpbmcoIDAsIHNlY3Rpb25faWRfdG9fc2hvdy5sZW5ndGggLSA4ICkgKyAnX3RhYic7XHJcblx0XHRcdGlmICggY29udGFpbmVyX3RvX2hpZGVfY2xhc3MgPT0gc2VjdGlvbl9pZF90b19zaG93ICkge1xyXG5cdFx0XHRcdHNlY3Rpb25faWRfdGFiID0gJyN3cGJjX2dlbmVyYWxfc2V0dGluZ3NfYWxsX3RhYidcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAoICcjd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhcGFjaXR5X21ldGFib3gsI3dwYmNfZ2VuZXJhbF9zZXR0aW5nc19jYXBhY2l0eV91cGdyYWRlX21ldGFib3gnID09IHNlY3Rpb25faWRfdG9fc2hvdyApIHtcclxuXHRcdFx0XHRzZWN0aW9uX2lkX3RhYiA9ICcjd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhcGFjaXR5X3RhYidcclxuXHRcdFx0fVxyXG5cdFx0XHRqUXVlcnkoICcjZm9ybV92aXNpYmxlX3NlY3Rpb24nICkudmFsKCBzZWN0aW9uX2lkX3RhYiApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIExpa2UgYmxpbmtpbmcgc29tZSBlbGVtZW50cy5cclxuXHRcdHdwYmNfYWRtaW5fdWlfX2RvX19hbmNob3JfX2Fub3RoZXJfYWN0aW9ucygpO1xyXG5cdH1cclxufVxyXG5cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9faXNfaW5fbW9iaWxlX3NjcmVlbl9zaXplKCkge1xyXG5cdHJldHVybiB3cGJjX2FkbWluX3VpX19pc19pbl90aGlzX3NjcmVlbl9zaXplKCA2MDUgKTtcclxufVxyXG5cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9faXNfaW5fdGhpc19zY3JlZW5fc2l6ZShzaXplKSB7XHJcblx0cmV0dXJuICh3aW5kb3cuc2NyZWVuLndpZHRoIDw9IHNpemUpO1xyXG59XHJcblxyXG4vKipcclxuICogT3BlbiBzZXR0aW5ncyBwYWdlICB8ICBFeHBhbmQgc2VjdGlvbiAgfCAgU2VsZWN0IE1lbnUgaXRlbS5cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfYWRtaW5fdWlfX2RvX19vcGVuX3VybF9fZXhwYW5kX3NlY3Rpb24odXJsLCBzZWN0aW9uX2lkKSB7XHJcblxyXG5cdC8vIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gdXJsICsgJyZkb19leHBhbmQ9JyArIHNlY3Rpb25faWQgKyAnI2RvX2V4cGFuZF9fJyArIHNlY3Rpb25faWQ7IC8vLlxyXG5cdHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gdXJsICsgJyNkb19leHBhbmRfXycgKyBzZWN0aW9uX2lkO1xyXG5cclxuXHRpZiAoIHdwYmNfYWRtaW5fdWlfX2lzX2luX21vYmlsZV9zY3JlZW5fc2l6ZSgpICkge1xyXG5cdFx0d3BiY19hZG1pbl91aV9fc2lkZWJhcl9sZWZ0X19kb19taW4oKTtcclxuXHR9XHJcblxyXG5cdHdwYmNfYWRtaW5fdWlfX2RvX2V4cGFuZF9zZWN0aW9uKCk7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogQ2hlY2sgIGZvciBPdGhlciBhY3Rpb25zOiAgTGlrZSBibGlua2luZyBzb21lIGVsZW1lbnRzIGluIHNldHRpbmdzIHBhZ2UuIEUuZy4gRGF5cyBzZWxlY3Rpb24gIG9yICBjaGFuZ2Utb3ZlciBkYXlzLlxyXG4gKi9cclxuZnVuY3Rpb24gd3BiY19hZG1pbl91aV9fZG9fX2FuY2hvcl9fYW5vdGhlcl9hY3Rpb25zKCkge1xyXG5cclxuXHR2YXIgYW5jaG9yc19hcnIgICAgICAgID0gd3BiY191cmxfZ2V0X2FuY2hvcnNfYXJyKCk7XHJcblx0dmFyIGFuY2hvcnNfYXJyX2xlbmd0aCA9IGFuY2hvcnNfYXJyLmxlbmd0aDtcclxuXHJcblx0Ly8gT3RoZXIgYWN0aW9uczogIExpa2UgYmxpbmtpbmcgc29tZSBlbGVtZW50cy5cclxuXHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhbmNob3JzX2Fycl9sZW5ndGg7IGkrKyApIHtcclxuXHJcblx0XHR2YXIgdGhpc19hbmNob3IgPSBhbmNob3JzX2FycltpXTtcclxuXHJcblx0XHR2YXIgdGhpc19hbmNob3JfcHJvcF92YWx1ZSA9IHRoaXNfYW5jaG9yLnNwbGl0KCAnZG9fb3RoZXJfYWN0aW9uc19fJyApO1xyXG5cclxuXHRcdGlmICggdGhpc19hbmNob3JfcHJvcF92YWx1ZS5sZW5ndGggPiAxICkge1xyXG5cclxuXHRcdFx0dmFyIHNlY3Rpb25fYWN0aW9uID0gdGhpc19hbmNob3JfcHJvcF92YWx1ZVsxXTtcclxuXHJcblx0XHRcdHN3aXRjaCAoIHNlY3Rpb25fYWN0aW9uICkge1xyXG5cclxuXHRcdFx0XHRjYXNlICdibGlua19kYXlfc2VsZWN0aW9ucyc6XHJcblx0XHRcdFx0XHQvLyB3cGJjX3VpX3NldHRpbmdzX19wYW5lbF9fY2xpY2soICcjd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhbGVuZGFyX3RhYiBhJywgJyN3cGJjX2dlbmVyYWxfc2V0dGluZ3NfY2FsZW5kYXJfbWV0YWJveCcsICdEYXlzIFNlbGVjdGlvbicgKTsuXHJcblx0XHRcdFx0XHR3cGJjX2JsaW5rX2VsZW1lbnQoICcud3BiY190cl9zZXRfZ2VuX2Jvb2tpbmdfdHlwZV9vZl9kYXlfc2VsZWN0aW9ucycsIDQsIDM1MCApO1xyXG5cdFx0XHRcdFx0XHR3cGJjX3Njcm9sbF90byggJy53cGJjX3RyX3NldF9nZW5fYm9va2luZ190eXBlX29mX2RheV9zZWxlY3Rpb25zJyApO1xyXG5cdFx0XHRcdFx0YnJlYWs7XHJcblxyXG5cdFx0XHRcdGNhc2UgJ2JsaW5rX2NoYW5nZV9vdmVyX2RheXMnOlxyXG5cdFx0XHRcdFx0Ly8gd3BiY191aV9zZXR0aW5nc19fcGFuZWxfX2NsaWNrKCAnI3dwYmNfZ2VuZXJhbF9zZXR0aW5nc19jYWxlbmRhcl90YWIgYScsICcjd3BiY19nZW5lcmFsX3NldHRpbmdzX2NhbGVuZGFyX21ldGFib3gnLCAnQ2hhbmdlb3ZlciBEYXlzJyApOy5cclxuXHRcdFx0XHRcdHdwYmNfYmxpbmtfZWxlbWVudCggJy53cGJjX3RyX3NldF9nZW5fYm9va2luZ19yYW5nZV9zZWxlY3Rpb25fdGltZV9pc19hY3RpdmUnLCA0LCAzNTAgKTtcclxuXHRcdFx0XHRcdFx0d3BiY19zY3JvbGxfdG8oICcud3BiY190cl9zZXRfZ2VuX2Jvb2tpbmdfcmFuZ2Vfc2VsZWN0aW9uX3RpbWVfaXNfYWN0aXZlJyApO1xyXG5cdFx0XHRcdFx0YnJlYWs7XHJcblxyXG5cdFx0XHRcdGNhc2UgJ2JsaW5rX2NhcHRjaGEnOlxyXG5cdFx0XHRcdFx0d3BiY19ibGlua19lbGVtZW50KCAnLndwYmNfdHJfc2V0X2dlbl9ib29raW5nX2lzX3VzZV9jYXB0Y2hhJywgNCwgMzUwICk7XHJcblx0XHRcdFx0XHRcdHdwYmNfc2Nyb2xsX3RvKCAnLndwYmNfdHJfc2V0X2dlbl9ib29raW5nX2lzX3VzZV9jYXB0Y2hhJyApO1xyXG5cdFx0XHRcdFx0YnJlYWs7XHJcblxyXG5cdFx0XHRcdGRlZmF1bHQ6XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9XHJcbn1cclxuIiwiLyoqXHJcbiAqIENvcHkgdHh0IHRvIGNsaXBicmQgZnJvbSBUZXh0IGZpZWxkcy5cclxuICpcclxuICogQHBhcmFtIGh0bWxfZWxlbWVudF9pZCAgLSBlLmcuICdkYXRhX2ZpZWxkJ1xyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfY29weV90ZXh0X3RvX2NsaXBicmRfZnJvbV9lbGVtZW50KCBodG1sX2VsZW1lbnRfaWQgKSB7XHJcblx0Ly8gR2V0IHRoZSB0ZXh0IGZpZWxkLlxyXG5cdHZhciBjb3B5VGV4dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCBodG1sX2VsZW1lbnRfaWQgKTtcclxuXHJcblx0Ly8gU2VsZWN0IHRoZSB0ZXh0IGZpZWxkLlxyXG5cdGNvcHlUZXh0LnNlbGVjdCgpO1xyXG5cdGNvcHlUZXh0LnNldFNlbGVjdGlvblJhbmdlKCAwLCA5OTk5OSApOyAvLyBGb3IgbW9iaWxlIGRldmljZXMuXHJcblxyXG5cdC8vIENvcHkgdGhlIHRleHQgaW5zaWRlIHRoZSB0ZXh0IGZpZWxkLlxyXG5cdHZhciBpc19jb3BpZWQgPSB3cGJjX2NvcHlfdGV4dF90b19jbGlwYnJkKCBjb3B5VGV4dC52YWx1ZSApO1xyXG5cdGlmICggISBpc19jb3BpZWQgKSB7XHJcblx0XHRjb25zb2xlLmVycm9yKCAnT29wcywgdW5hYmxlIHRvIGNvcHknLCBjb3B5VGV4dC52YWx1ZSApO1xyXG5cdH1cclxuXHRyZXR1cm4gaXNfY29waWVkO1xyXG59XHJcblxyXG4vKipcclxuICogQ29weSB0eHQgdG8gY2xpcGJyZC5cclxuICpcclxuICogQHBhcmFtIHRleHRcclxuICogQHJldHVybnMge2Jvb2xlYW59XHJcbiAqL1xyXG5mdW5jdGlvbiB3cGJjX2NvcHlfdGV4dF90b19jbGlwYnJkKHRleHQpIHtcclxuXHJcblx0aWYgKCAhIG5hdmlnYXRvci5jbGlwYm9hcmQgKSB7XHJcblx0XHRyZXR1cm4gd3BiY19mYWxsYmFja19jb3B5X3RleHRfdG9fY2xpcGJyZCggdGV4dCApO1xyXG5cdH1cclxuXHJcblx0bmF2aWdhdG9yLmNsaXBib2FyZC53cml0ZVRleHQoIHRleHQgKS50aGVuKFxyXG5cdFx0ZnVuY3Rpb24gKCkge1xyXG5cdFx0XHQvLyBjb25zb2xlLmxvZyggJ0FzeW5jOiBDb3B5aW5nIHRvIGNsaXBib2FyZCB3YXMgc3VjY2Vzc2Z1bCEnICk7LlxyXG5cdFx0XHRyZXR1cm4gIHRydWU7XHJcblx0XHR9LFxyXG5cdFx0ZnVuY3Rpb24gKGVycikge1xyXG5cdFx0XHQvLyBjb25zb2xlLmVycm9yKCAnQXN5bmM6IENvdWxkIG5vdCBjb3B5IHRleHQ6ICcsIGVyciApOy5cclxuXHRcdFx0cmV0dXJuICBmYWxzZTtcclxuXHRcdH1cclxuXHQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ29weSB0eHQgdG8gY2xpcGJyZCAtIGRlcHJpY2F0ZWQgbWV0aG9kLlxyXG4gKlxyXG4gKiBAcGFyYW0gdGV4dFxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cclxuICovXHJcbmZ1bmN0aW9uIHdwYmNfZmFsbGJhY2tfY29weV90ZXh0X3RvX2NsaXBicmQoIHRleHQgKSB7XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0Ly8gdmFyIHRleHRBcmVhICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcInRleHRhcmVhXCIgKTtcclxuXHQvLyB0ZXh0QXJlYS52YWx1ZSA9IHRleHQ7XHJcblx0Ly9cclxuXHQvLyAvLyBBdm9pZCBzY3JvbGxpbmcgdG8gYm90dG9tLlxyXG5cdC8vIHRleHRBcmVhLnN0eWxlLnRvcCAgICAgID0gXCIwXCI7XHJcblx0Ly8gdGV4dEFyZWEuc3R5bGUubGVmdCAgICAgPSBcIjBcIjtcclxuXHQvLyB0ZXh0QXJlYS5zdHlsZS5wb3NpdGlvbiA9IFwiZml4ZWRcIjtcclxuXHQvLyB0ZXh0QXJlYS5zdHlsZS56SW5kZXggICA9IFwiOTk5OTk5OTk5XCI7XHJcblx0Ly8gZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZCggdGV4dEFyZWEgKTtcclxuXHQvLyB0ZXh0QXJlYS5mb2N1cygpO1xyXG5cdC8vIHRleHRBcmVhLnNlbGVjdCgpO1xyXG5cclxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdC8vIE5vdyBnZXQgaXQgYXMgSFRNTCAgKG9yaWdpbmFsIGhlcmUgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzQxOTE3ODAvamF2YXNjcmlwdC1jb3B5LXN0cmluZy10by1jbGlwYm9hcmQtYXMtdGV4dC1odG1sICkuXHJcblxyXG5cdC8vIFsxXSAtIENyZWF0ZSBjb250YWluZXIgZm9yIHRoZSBIVE1MLlxyXG5cdHZhciBjb250YWluZXIgICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xyXG5cdGNvbnRhaW5lci5pbm5lckhUTUwgPSB0ZXh0O1xyXG5cclxuXHQvLyBbMl0gLSBIaWRlIGVsZW1lbnQuXHJcblx0Y29udGFpbmVyLnN0eWxlLnBvc2l0aW9uICAgICAgPSAnZml4ZWQnO1xyXG5cdGNvbnRhaW5lci5zdHlsZS5wb2ludGVyRXZlbnRzID0gJ25vbmUnO1xyXG5cdGNvbnRhaW5lci5zdHlsZS5vcGFjaXR5ICAgICAgID0gMDtcclxuXHJcblx0Ly8gRGV0ZWN0IGFsbCBzdHlsZSBzaGVldHMgb2YgdGhlIHBhZ2UuXHJcblx0dmFyIGFjdGl2ZVNoZWV0cyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKCBkb2N1bWVudC5zdHlsZVNoZWV0cyApLmZpbHRlcihcclxuXHRcdGZ1bmN0aW9uIChzaGVldCkge1xyXG5cdFx0XHRyZXR1cm4gISBzaGVldC5kaXNhYmxlZDtcclxuXHRcdH1cclxuXHQpO1xyXG5cclxuXHQvLyBbM10gLSBNb3VudCB0aGUgY29udGFpbmVyIHRvIHRoZSBET00gdG8gbWFrZSBgY29udGVudFdpbmRvd2AgYXZhaWxhYmxlLlxyXG5cdGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoIGNvbnRhaW5lciApO1xyXG5cclxuXHQvLyBbNF0gLSBDb3B5IHRvIGNsaXBib2FyZC5cclxuXHR3aW5kb3cuZ2V0U2VsZWN0aW9uKCkucmVtb3ZlQWxsUmFuZ2VzKCk7XHJcblxyXG5cdHZhciByYW5nZSA9IGRvY3VtZW50LmNyZWF0ZVJhbmdlKCk7XHJcblx0cmFuZ2Uuc2VsZWN0Tm9kZSggY29udGFpbmVyICk7XHJcblx0d2luZG93LmdldFNlbGVjdGlvbigpLmFkZFJhbmdlKCByYW5nZSApO1xyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5cdHZhciByZXN1bHQgPSBmYWxzZTtcclxuXHJcblx0dHJ5IHtcclxuXHRcdHJlc3VsdCA9IGRvY3VtZW50LmV4ZWNDb21tYW5kKCAnY29weScgKTtcclxuXHRcdC8vIGNvbnNvbGUubG9nKCAnRmFsbGJhY2s6IENvcHlpbmcgdGV4dCBjb21tYW5kIHdhcyAnICsgbXNnICk7IC8vLlxyXG5cdH0gY2F0Y2ggKCBlcnIgKSB7XHJcblx0XHQvLyBjb25zb2xlLmVycm9yKCAnRmFsbGJhY2s6IE9vcHMsIHVuYWJsZSB0byBjb3B5JywgZXJyICk7IC8vLlxyXG5cdH1cclxuXHQvLyBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKCB0ZXh0QXJlYSApOyAvLy5cclxuXHJcblx0Ly8gWzUuNF0gLSBFbmFibGUgQ1NTLlxyXG5cdHZhciBhY3RpdmVTaGVldHNfbGVuZ3RoID0gYWN0aXZlU2hlZXRzLmxlbmd0aDtcclxuXHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhY3RpdmVTaGVldHNfbGVuZ3RoOyBpKysgKSB7XHJcblx0XHRhY3RpdmVTaGVldHNbaV0uZGlzYWJsZWQgPSBmYWxzZTtcclxuXHR9XHJcblxyXG5cdC8vIFs2XSAtIFJlbW92ZSB0aGUgY29udGFpbmVyXHJcblx0ZG9jdW1lbnQuYm9keS5yZW1vdmVDaGlsZCggY29udGFpbmVyICk7XHJcblxyXG5cdHJldHVybiAgcmVzdWx0O1xyXG59IiwiLyoqXHJcbiAqIFdQQkMgQ29sbGFwc2libGUgR3JvdXBzXHJcbiAqXHJcbiAqIFVuaXZlcnNhbCwgZGVwZW5kZW5jeS1mcmVlIGNvbnRyb2xsZXIgZm9yIGV4cGFuZGluZy9jb2xsYXBzaW5nIGdyb3VwZWQgc2VjdGlvbnMgaW4gcmlnaHQtc2lkZSBwYW5lbHMgKEluc3BlY3Rvci9MaWJyYXJ5L0Zvcm0gU2V0dGluZ3MsIG9yIGFueSBvdGhlciBXUEJDIHBhZ2UpLlxyXG4gKlxyXG4gKiBcdFx0PT09IEhvdyB0byB1c2UgaXQgKHF1aWNrKSA/ID09PVxyXG4gKlxyXG4gKlx0XHQtLSAxLiBNYXJrdXAgKGluZGVwZW5kZW50IG1vZGU6IG11bHRpcGxlIG9wZW4gYWxsb3dlZCkgLS1cclxuICpcdFx0XHQ8ZGl2IGNsYXNzPVwid3BiY19jb2xsYXBzaWJsZVwiPlxyXG4gKlx0XHRcdCAgPHNlY3Rpb24gY2xhc3M9XCJ3cGJjX3VpX19jb2xsYXBzaWJsZV9ncm91cCBpcy1vcGVuXCI+XHJcbiAqXHRcdFx0XHQ8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImdyb3VwX19oZWFkZXJcIj48aDM+R2VuZXJhbDwvaDM+PC9idXR0b24+XHJcbiAqXHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZ3JvdXBfX2ZpZWxkc1wiPuKApjwvZGl2PlxyXG4gKlx0XHRcdCAgPC9zZWN0aW9uPlxyXG4gKlx0XHRcdCAgPHNlY3Rpb24gY2xhc3M9XCJ3cGJjX3VpX19jb2xsYXBzaWJsZV9ncm91cFwiPlxyXG4gKlx0XHRcdFx0PGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJncm91cF9faGVhZGVyXCI+PGgzPkFkdmFuY2VkPC9oMz48L2J1dHRvbj5cclxuICpcdFx0XHRcdDxkaXYgY2xhc3M9XCJncm91cF9fZmllbGRzXCI+4oCmPC9kaXY+XHJcbiAqXHRcdFx0ICA8L3NlY3Rpb24+XHJcbiAqXHRcdFx0PC9kaXY+XHJcbiAqXHJcbiAqXHRcdC0tIDIuIEV4Y2x1c2l2ZS9hY2NvcmRpb24gbW9kZSAob25lIG9wZW4gYXQgYSB0aW1lKSAtLVxyXG4gKlx0XHRcdDxkaXYgY2xhc3M9XCJ3cGJjX2NvbGxhcHNpYmxlIHdwYmNfY29sbGFwc2libGUtLWV4Y2x1c2l2ZVwiPuKApjwvZGl2PlxyXG4gKlxyXG4gKlx0XHQtLSAzLiBBdXRvLWluaXQgLS1cclxuICpcdFx0XHRUaGUgc2NyaXB0IGF1dG8taW5pdGlhbGl6ZXMgb24gRE9NQ29udGVudExvYWRlZC4gTm8gZXh0cmEgY29kZSBuZWVkZWQuXHJcbiAqXHJcbiAqXHRcdC0tIDQuIFByb2dyYW1tYXRpYyBjb250cm9sIChvcHRpb25hbClcclxuICpcdFx0XHRjb25zdCByb290ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3dwYmNfYmZiX19pbnNwZWN0b3InKTtcclxuICpcdFx0XHRjb25zdCBhcGkgID0gcm9vdC5fX3dwYmNfY29sbGFwc2libGVfaW5zdGFuY2U7IC8vIHNldCBieSBhdXRvLWluaXRcclxuICpcclxuICpcdFx0XHRhcGkub3Blbl9ieV9oZWFkaW5nKCdWYWxpZGF0aW9uJyk7IC8vIG9wZW4gYnkgaGVhZGluZyB0ZXh0XHJcbiAqXHRcdFx0YXBpLm9wZW5fYnlfaW5kZXgoMCk7ICAgICAgICAgICAgICAvLyBvcGVuIHRoZSBmaXJzdCBncm91cFxyXG4gKlxyXG4gKlx0XHQtLSA1Lkxpc3RlbiB0byBldmVudHMgKGUuZy4sIHRvIHBlcnNpc3Qg4oCcb3BlbiBncm91cOKAnSBzdGF0ZSkgLS1cclxuICpcdFx0XHRyb290LmFkZEV2ZW50TGlzdGVuZXIoJ3dwYmM6Y29sbGFwc2libGU6b3BlbicsICAoZSkgPT4geyBjb25zb2xlLmxvZyggIGUuZGV0YWlsLmdyb3VwICk7IH0pO1xyXG4gKlx0XHRcdHJvb3QuYWRkRXZlbnRMaXN0ZW5lcignd3BiYzpjb2xsYXBzaWJsZTpjbG9zZScsIChlKSA9PiB7IGNvbnNvbGUubG9nKCAgZS5kZXRhaWwuZ3JvdXAgKTsgfSk7XHJcbiAqXHJcbiAqXHJcbiAqXHJcbiAqIE1hcmt1cCBleHBlY3RhdGlvbnMgKG1pbmltYWwpOlxyXG4gKiAgPGRpdiBjbGFzcz1cIndwYmNfY29sbGFwc2libGUgW3dwYmNfY29sbGFwc2libGUtLWV4Y2x1c2l2ZV1cIj5cclxuICogICAgPHNlY3Rpb24gY2xhc3M9XCJ3cGJjX3VpX19jb2xsYXBzaWJsZV9ncm91cCBbaXMtb3Blbl1cIj5cclxuICogICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImdyb3VwX19oZWFkZXJcIj4gLi4uIDwvYnV0dG9uPlxyXG4gKiAgICAgIDxkaXYgY2xhc3M9XCJncm91cF9fZmllbGRzXCI+IC4uLiA8L2Rpdj5cclxuICogICAgPC9zZWN0aW9uPlxyXG4gKiAgICAuLi4gbW9yZSA8c2VjdGlvbj4gLi4uXHJcbiAqICA8L2Rpdj5cclxuICpcclxuICogTm90ZXM6XHJcbiAqICAtIEFkZCBgaXMtb3BlbmAgdG8gYW55IHNlY3Rpb24geW91IHdhbnQgaW5pdGlhbGx5IGV4cGFuZGVkLlxyXG4gKiAgLSBBZGQgYHdwYmNfY29sbGFwc2libGUtLWV4Y2x1c2l2ZWAgdG8gdGhlIGNvbnRhaW5lciBmb3IgXCJvcGVuIG9uZSBhdCBhIHRpbWVcIiBiZWhhdmlvci5cclxuICogIC0gV29ya3Mgd2l0aCB5b3VyIGV4aXN0aW5nIEJGQiBtYXJrdXAgKGNsYXNzZXMgdXNlZCB0aGVyZSBhcmUgdGhlIGRlZmF1bHRzKS5cclxuICpcclxuICogQWNjZXNzaWJpbGl0eTpcclxuICogIC0gU2V0cyBhcmlhLWV4cGFuZGVkIG9uIC5ncm91cF9faGVhZGVyXHJcbiAqICAtIFNldHMgYXJpYS1oaWRkZW4gKyBbaGlkZGVuXSBvbiAuZ3JvdXBfX2ZpZWxkc1xyXG4gKiAgLSBBcnJvd1VwL0Fycm93RG93biBtb3ZlIGZvY3VzIGJldHdlZW4gaGVhZGVyczsgRW50ZXIvU3BhY2UgdG9nZ2xlc1xyXG4gKlxyXG4gKiBFdmVudHMgKGJ1YmJsZXMgZnJvbSB0aGUgPHNlY3Rpb24+KTpcclxuICogIC0gJ3dwYmM6Y29sbGFwc2libGU6b3BlbicgIChkZXRhaWw6IHsgZ3JvdXAsIHJvb3QsIGluc3RhbmNlIH0pXHJcbiAqICAtICd3cGJjOmNvbGxhcHNpYmxlOmNsb3NlJyAoZGV0YWlsOiB7IGdyb3VwLCByb290LCBpbnN0YW5jZSB9KVxyXG4gKlxyXG4gKiBQdWJsaWMgQVBJIChpbnN0YW5jZSBtZXRob2RzKTpcclxuICogIC0gaW5pdCgpLCBkZXN0cm95KCksIHJlZnJlc2goKVxyXG4gKiAgLSBleHBhbmQoZ3JvdXAsIFtleGNsdXNpdmVdKSwgY29sbGFwc2UoZ3JvdXApLCB0b2dnbGUoZ3JvdXApXHJcbiAqICAtIG9wZW5fYnlfaW5kZXgoaW5kZXgpLCBvcGVuX2J5X2hlYWRpbmcodGV4dClcclxuICogIC0gaXNfZXhjbHVzaXZlKCksIGlzX29wZW4oZ3JvdXApXHJcbiAqXHJcbiAqIEB2ZXJzaW9uIDIwMjUtMDgtMjZcclxuICogQHNpbmNlIDIwMjUtMDgtMjZcclxuICovXHJcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4vLyA9PSBGaWxlICAvY29sbGFwc2libGVfZ3JvdXBzLmpzID09IFRpbWUgcG9pbnQ6IDIwMjUtMDgtMjYgMTQ6MTNcclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbihmdW5jdGlvbiAodywgZCkge1xyXG5cdCd1c2Ugc3RyaWN0JztcclxuXHJcblx0Y2xhc3MgV1BCQ19Db2xsYXBzaWJsZV9Hcm91cHMge1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQ3JlYXRlIGEgY29sbGFwc2libGUgY29udHJvbGxlciBmb3IgYSBjb250YWluZXIuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudHxzdHJpbmd9IHJvb3RfZWxcclxuXHRcdCAqICAgICAgICBUaGUgY29udGFpbmVyIGVsZW1lbnQgKG9yIENTUyBzZWxlY3RvcikgdGhhdCB3cmFwcyBjb2xsYXBzaWJsZSBncm91cHMuXHJcblx0XHQgKiAgICAgICAgVGhlIGNvbnRhaW5lciB1c3VhbGx5IGhhcyB0aGUgY2xhc3MgYC53cGJjX2NvbGxhcHNpYmxlYC5cclxuXHRcdCAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0cz17fV1cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSAgW29wdHMuZ3JvdXBfc2VsZWN0b3I9Jy53cGJjX3VpX19jb2xsYXBzaWJsZV9ncm91cCddXHJcblx0XHQgKiAgICAgICAgU2VsZWN0b3IgZm9yIGVhY2ggY29sbGFwc2libGUgZ3JvdXAgaW5zaWRlIHRoZSBjb250YWluZXIuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gIFtvcHRzLmhlYWRlcl9zZWxlY3Rvcj0nLmdyb3VwX19oZWFkZXInXVxyXG5cdFx0ICogICAgICAgIFNlbGVjdG9yIGZvciB0aGUgY2xpY2thYmxlIGhlYWRlciBpbnNpZGUgYSBncm91cC5cclxuXHRcdCAqIEBwYXJhbSB7c3RyaW5nfSAgW29wdHMuZmllbGRzX3NlbGVjdG9yPScuZ3JvdXBfX2ZpZWxkcyddXHJcblx0XHQgKiAgICAgICAgU2VsZWN0b3IgZm9yIHRoZSBjb250ZW50L3BhbmVsIGVsZW1lbnQgaW5zaWRlIGEgZ3JvdXAuXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gIFtvcHRzLm9wZW5fY2xhc3M9J2lzLW9wZW4nXVxyXG5cdFx0ICogICAgICAgIENsYXNzIG5hbWUgdGhhdCBpbmRpY2F0ZXMgdGhlIGdyb3VwIGlzIG9wZW4uXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmV4Y2x1c2l2ZT1mYWxzZV1cclxuXHRcdCAqICAgICAgICBJZiB0cnVlLCBvbmx5IG9uZSBncm91cCBjYW4gYmUgb3BlbiBhdCBhIHRpbWUgaW4gdGhpcyBjb250YWluZXIuXHJcblx0XHQgKlxyXG5cdFx0ICogQGNvbnN0cnVjdG9yXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRjb25zdHJ1Y3Rvcihyb290X2VsLCBvcHRzID0ge30pIHtcclxuXHRcdFx0dGhpcy5yb290ID0gKHR5cGVvZiByb290X2VsID09PSAnc3RyaW5nJykgPyBkLnF1ZXJ5U2VsZWN0b3IoIHJvb3RfZWwgKSA6IHJvb3RfZWw7XHJcblx0XHRcdHRoaXMub3B0cyA9IE9iamVjdC5hc3NpZ24oIHtcclxuXHRcdFx0XHRncm91cF9zZWxlY3RvciA6ICcud3BiY191aV9fY29sbGFwc2libGVfZ3JvdXAnLFxyXG5cdFx0XHRcdGhlYWRlcl9zZWxlY3RvcjogJy5ncm91cF9faGVhZGVyJyxcclxuXHRcdFx0XHRmaWVsZHNfc2VsZWN0b3I6ICcuZ3JvdXBfX2ZpZWxkcywuZ3JvdXBfX2NvbnRlbnQnLFxyXG5cdFx0XHRcdG9wZW5fY2xhc3MgICAgIDogJ2lzLW9wZW4nLFxyXG5cdFx0XHRcdGV4Y2x1c2l2ZSAgICAgIDogZmFsc2VcclxuXHRcdFx0fSwgb3B0cyApO1xyXG5cclxuXHRcdFx0Ly8gQm91bmQgaGFuZGxlcnMgKGZvciBhZGQvcmVtb3ZlRXZlbnRMaXN0ZW5lciBzeW1tZXRyeSkuXHJcblx0XHRcdC8qKiBAcHJpdmF0ZSAqL1xyXG5cdFx0XHR0aGlzLl9vbl9jbGljayA9IHRoaXMuX29uX2NsaWNrLmJpbmQoIHRoaXMgKTtcclxuXHRcdFx0LyoqIEBwcml2YXRlICovXHJcblx0XHRcdHRoaXMuX29uX2tleWRvd24gPSB0aGlzLl9vbl9rZXlkb3duLmJpbmQoIHRoaXMgKTtcclxuXHJcblx0XHRcdC8qKiBAdHlwZSB7SFRNTEVsZW1lbnRbXX0gQHByaXZhdGUgKi9cclxuXHRcdFx0dGhpcy5fZ3JvdXBzID0gW107XHJcblx0XHRcdC8qKiBAdHlwZSB7TXV0YXRpb25PYnNlcnZlcnxudWxsfSBAcHJpdmF0ZSAqL1xyXG5cdFx0XHR0aGlzLl9vYnNlcnZlciA9IG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBJbml0aWFsaXplIHRoZSBjb250cm9sbGVyOiBjYWNoZSBncm91cHMsIGF0dGFjaCBsaXN0ZW5lcnMsIHNldCBBUklBLFxyXG5cdFx0ICogYW5kIHN0YXJ0IG9ic2VydmluZyBET00gY2hhbmdlcyBpbnNpZGUgdGhlIGNvbnRhaW5lci5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJucyB7V1BCQ19Db2xsYXBzaWJsZV9Hcm91cHN9IFRoZSBpbnN0YW5jZSAoY2hhaW5hYmxlKS5cclxuXHRcdCAqIEBsaXN0ZW5zIGNsaWNrXHJcblx0XHQgKiBAbGlzdGVucyBrZXlkb3duXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRpbml0KCkge1xyXG5cdFx0XHRpZiAoICF0aGlzLnJvb3QgKSB7XHJcblx0XHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fZ3JvdXBzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoXHJcblx0XHRcdFx0dGhpcy5yb290LnF1ZXJ5U2VsZWN0b3JBbGwoIHRoaXMub3B0cy5ncm91cF9zZWxlY3RvciApXHJcblx0XHRcdCk7XHJcblx0XHRcdHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCB0aGlzLl9vbl9jbGljaywgZmFsc2UgKTtcclxuXHRcdFx0dGhpcy5yb290LmFkZEV2ZW50TGlzdGVuZXIoICdrZXlkb3duJywgdGhpcy5fb25fa2V5ZG93biwgZmFsc2UgKTtcclxuXHJcblx0XHRcdC8vIE9ic2VydmUgZHluYW1pYyBpbnNlcnRzL3JlbW92YWxzIChJbnNwZWN0b3IgcmUtcmVuZGVycykuXHJcblx0XHRcdHRoaXMuX29ic2VydmVyID0gbmV3IE11dGF0aW9uT2JzZXJ2ZXIoICgpID0+IHtcclxuXHRcdFx0XHR0aGlzLnJlZnJlc2goKTtcclxuXHRcdFx0fSApO1xyXG5cdFx0XHR0aGlzLl9vYnNlcnZlci5vYnNlcnZlKCB0aGlzLnJvb3QsIHsgY2hpbGRMaXN0OiB0cnVlLCBzdWJ0cmVlOiB0cnVlIH0gKTtcclxuXHJcblx0XHRcdHRoaXMuX3N5bmNfYWxsX2FyaWEoKTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBUZWFyIGRvd24gdGhlIGNvbnRyb2xsZXI6IGRldGFjaCBsaXN0ZW5lcnMsIHN0b3AgdGhlIG9ic2VydmVyLFxyXG5cdFx0ICogYW5kIGRyb3AgaW50ZXJuYWwgcmVmZXJlbmNlcy5cclxuXHRcdCAqXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdGRlc3Ryb3koKSB7XHJcblx0XHRcdGlmICggIXRoaXMucm9vdCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5yb290LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdjbGljaycsIHRoaXMuX29uX2NsaWNrLCBmYWxzZSApO1xyXG5cdFx0XHR0aGlzLnJvb3QucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2tleWRvd24nLCB0aGlzLl9vbl9rZXlkb3duLCBmYWxzZSApO1xyXG5cdFx0XHRpZiAoIHRoaXMuX29ic2VydmVyICkge1xyXG5cdFx0XHRcdHRoaXMuX29ic2VydmVyLmRpc2Nvbm5lY3QoKTtcclxuXHRcdFx0XHR0aGlzLl9vYnNlcnZlciA9IG51bGw7XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fZ3JvdXBzID0gW107XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBSZS1zY2FuIHRoZSBET00gZm9yIGN1cnJlbnQgZ3JvdXBzIGFuZCByZS1hcHBseSBBUklBIHRvIGFsbCBvZiB0aGVtLlxyXG5cdFx0ICogVXNlZnVsIGFmdGVyIGR5bmFtaWMgKHJlKXJlbmRlcnMuXHJcblx0XHQgKlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRyZWZyZXNoKCkge1xyXG5cdFx0XHRpZiAoICF0aGlzLnJvb3QgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdHRoaXMuX2dyb3VwcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKFxyXG5cdFx0XHRcdHRoaXMucm9vdC5xdWVyeVNlbGVjdG9yQWxsKCB0aGlzLm9wdHMuZ3JvdXBfc2VsZWN0b3IgKVxyXG5cdFx0XHQpO1xyXG5cdFx0XHR0aGlzLl9zeW5jX2FsbF9hcmlhKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBDaGVjayB3aGV0aGVyIHRoZSBjb250YWluZXIgaXMgaW4gZXhjbHVzaXZlIChhY2NvcmRpb24pIG1vZGUuXHJcblx0XHQgKlxyXG5cdFx0ICogT3JkZXIgb2YgcHJlY2VkZW5jZTpcclxuXHRcdCAqICAxKSBFeHBsaWNpdCBvcHRpb24gYG9wdHMuZXhjbHVzaXZlYFxyXG5cdFx0ICogIDIpIENvbnRhaW5lciBoYXMgY2xhc3MgYC53cGJjX2NvbGxhcHNpYmxlLS1leGNsdXNpdmVgXHJcblx0XHQgKiAgMykgQ29udGFpbmVyIG1hdGNoZXMgYFtkYXRhLXdwYmMtYWNjb3JkaW9uPVwiZXhjbHVzaXZlXCJdYFxyXG5cdFx0ICpcclxuXHRcdCAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIGV4Y2x1c2l2ZSBtb2RlIGlzIGFjdGl2ZS5cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdGlzX2V4Y2x1c2l2ZSgpIHtcclxuXHRcdFx0cmV0dXJuICEhKFxyXG5cdFx0XHRcdHRoaXMub3B0cy5leGNsdXNpdmUgfHxcclxuXHRcdFx0XHR0aGlzLnJvb3QuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19jb2xsYXBzaWJsZS0tZXhjbHVzaXZlJyApIHx8XHJcblx0XHRcdFx0dGhpcy5yb290Lm1hdGNoZXMoICdbZGF0YS13cGJjLWFjY29yZGlvbj1cImV4Y2x1c2l2ZVwiXScgKVxyXG5cdFx0XHQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogRGV0ZXJtaW5lIHdoZXRoZXIgYSBzcGVjaWZpYyBncm91cCBpcyBvcGVuLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGdyb3VwIFRoZSBncm91cCBlbGVtZW50IHRvIHRlc3QuXHJcblx0XHQgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgZ3JvdXAgaXMgY3VycmVudGx5IG9wZW4uXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRpc19vcGVuKGdyb3VwKSB7XHJcblx0XHRcdHJldHVybiBncm91cC5jbGFzc0xpc3QuY29udGFpbnMoIHRoaXMub3B0cy5vcGVuX2NsYXNzICk7XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBPcGVuIGEgZ3JvdXAuIEhvbm9ycyBleGNsdXNpdmUgbW9kZSBieSBjb2xsYXBzaW5nIGFsbCBzaWJsaW5nIGdyb3Vwc1xyXG5cdFx0ICogKHF1ZXJpZWQgZnJvbSB0aGUgbGl2ZSBET00gYXQgY2FsbC10aW1lKS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBncm91cCBUaGUgZ3JvdXAgZWxlbWVudCB0byBvcGVuLlxyXG5cdFx0ICogQHBhcmFtIHtib29sZWFufSBbZXhjbHVzaXZlXVxyXG5cdFx0ICogICAgICAgIElmIHByb3ZpZGVkLCBvdmVycmlkZXMgY29udGFpbmVyIG1vZGUgZm9yIHRoaXMgYWN0aW9uIG9ubHkuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBmaXJlcyBDdXN0b21FdmVudCN3cGJjOmNvbGxhcHNpYmxlOm9wZW5cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdGV4cGFuZChncm91cCwgZXhjbHVzaXZlKSB7XHJcblx0XHRcdGlmICggIWdyb3VwICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBkb19leGNsdXNpdmUgPSAodHlwZW9mIGV4Y2x1c2l2ZSA9PT0gJ2Jvb2xlYW4nKSA/IGV4Y2x1c2l2ZSA6IHRoaXMuaXNfZXhjbHVzaXZlKCk7XHJcblx0XHRcdGlmICggZG9fZXhjbHVzaXZlICkge1xyXG5cdFx0XHRcdC8vIEFsd2F5cyB1c2UgdGhlIGxpdmUgRE9NLCBub3QgdGhlIGNhY2hlZCBsaXN0LlxyXG5cdFx0XHRcdEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoXHJcblx0XHRcdFx0XHR0aGlzLnJvb3QucXVlcnlTZWxlY3RvckFsbCggdGhpcy5vcHRzLmdyb3VwX3NlbGVjdG9yICksXHJcblx0XHRcdFx0XHQoZykgPT4ge1xyXG5cdFx0XHRcdFx0XHRpZiAoIGcgIT09IGdyb3VwICkge1xyXG5cdFx0XHRcdFx0XHRcdHRoaXMuX3NldF9vcGVuKCBnLCBmYWxzZSApO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0KTtcclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLl9zZXRfb3BlbiggZ3JvdXAsIHRydWUgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIENsb3NlIGEgZ3JvdXAuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZ3JvdXAgVGhlIGdyb3VwIGVsZW1lbnQgdG8gY2xvc2UuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBmaXJlcyBDdXN0b21FdmVudCN3cGJjOmNvbGxhcHNpYmxlOmNsb3NlXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRjb2xsYXBzZShncm91cCkge1xyXG5cdFx0XHRpZiAoICFncm91cCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fc2V0X29wZW4oIGdyb3VwLCBmYWxzZSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogVG9nZ2xlIGEgZ3JvdXAncyBvcGVuL2Nsb3NlZCBzdGF0ZS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBncm91cCBUaGUgZ3JvdXAgZWxlbWVudCB0byB0b2dnbGUuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdHRvZ2dsZShncm91cCkge1xyXG5cdFx0XHRpZiAoICFncm91cCApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpc1t0aGlzLmlzX29wZW4oIGdyb3VwICkgPyAnY29sbGFwc2UnIDogJ2V4cGFuZCddKCBncm91cCApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogT3BlbiBhIGdyb3VwIGJ5IGl0cyBpbmRleCB3aXRoaW4gdGhlIGNvbnRhaW5lciAoMC1iYXNlZCkuXHJcblx0XHQgKlxyXG5cdFx0ICogQHBhcmFtIHtudW1iZXJ9IGluZGV4IFplcm8tYmFzZWQgaW5kZXggb2YgdGhlIGdyb3VwLlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRvcGVuX2J5X2luZGV4KGluZGV4KSB7XHJcblx0XHRcdGNvbnN0IGdyb3VwID0gdGhpcy5fZ3JvdXBzW2luZGV4XTtcclxuXHRcdFx0aWYgKCBncm91cCApIHtcclxuXHRcdFx0XHR0aGlzLmV4cGFuZCggZ3JvdXAgKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogT3BlbiBhIGdyb3VwIGJ5IG1hdGNoaW5nIHRleHQgY29udGFpbmVkIHdpdGhpbiB0aGUgPGgzPiBpbnNpZGUgdGhlIGhlYWRlci5cclxuXHRcdCAqIFRoZSBjb21wYXJpc29uIGlzIGNhc2UtaW5zZW5zaXRpdmUgYW5kIHN1YnN0cmluZy1iYXNlZC5cclxuXHRcdCAqXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gdGV4dCBUZXh0IHRvIG1hdGNoIGFnYWluc3QgdGhlIGhlYWRpbmcgY29udGVudHMuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdG9wZW5fYnlfaGVhZGluZyh0ZXh0KSB7XHJcblx0XHRcdGlmICggIXRleHQgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGNvbnN0IHQgICAgID0gU3RyaW5nKCB0ZXh0ICkudG9Mb3dlckNhc2UoKTtcclxuXHRcdFx0Y29uc3QgbWF0Y2ggPSB0aGlzLl9ncm91cHMuZmluZCggKGcpID0+IHtcclxuXHRcdFx0XHRjb25zdCBoID0gZy5xdWVyeVNlbGVjdG9yKCB0aGlzLm9wdHMuaGVhZGVyX3NlbGVjdG9yICsgJyBoMycgKTtcclxuXHRcdFx0XHRyZXR1cm4gaCAmJiBoLnRleHRDb250ZW50LnRvTG93ZXJDYXNlKCkuaW5kZXhPZiggdCApICE9PSAtMTtcclxuXHRcdFx0fSApO1xyXG5cdFx0XHRpZiAoIG1hdGNoICkge1xyXG5cdFx0XHRcdHRoaXMuZXhwYW5kKCBtYXRjaCApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdFx0Ly8gSW50ZXJuYWxcclxuXHRcdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIERlbGVnYXRlZCBjbGljayBoYW5kbGVyIGZvciBoZWFkZXJzLlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwcml2YXRlXHJcblx0XHQgKiBAcGFyYW0ge01vdXNlRXZlbnR9IGV2IFRoZSBjbGljayBldmVudC5cclxuXHRcdCAqIEByZXR1cm5zIHt2b2lkfVxyXG5cdFx0ICogQHNpbmNlIDIwMjUtMDgtMjZcclxuXHRcdCAqL1xyXG5cdFx0X29uX2NsaWNrKGV2KSB7XHJcblx0XHRcdGNvbnN0IGJ0biA9IGV2LnRhcmdldC5jbG9zZXN0KCB0aGlzLm9wdHMuaGVhZGVyX3NlbGVjdG9yICk7XHJcblx0XHRcdGlmICggIWJ0biB8fCAhdGhpcy5yb290LmNvbnRhaW5zKCBidG4gKSApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0ZXYucHJldmVudERlZmF1bHQoKTtcclxuXHRcdFx0ZXYuc3RvcFByb3BhZ2F0aW9uKCk7XHJcblx0XHRcdGNvbnN0IGdyb3VwID0gYnRuLmNsb3Nlc3QoIHRoaXMub3B0cy5ncm91cF9zZWxlY3RvciApO1xyXG5cdFx0XHRpZiAoIGdyb3VwICkge1xyXG5cdFx0XHRcdHRoaXMudG9nZ2xlKCBncm91cCApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBLZXlib2FyZCBoYW5kbGVyIGZvciBoZWFkZXIgaW50ZXJhY3Rpb25zIGFuZCByb3ZpbmcgZm9jdXM6XHJcblx0XHQgKiAgLSBFbnRlci9TcGFjZSB0b2dnbGVzIHRoZSBhY3RpdmUgZ3JvdXAuXHJcblx0XHQgKiAgLSBBcnJvd1VwL0Fycm93RG93biBtb3ZlcyBmb2N1cyBiZXR3ZWVuIGdyb3VwIGhlYWRlcnMuXHJcblx0XHQgKlxyXG5cdFx0ICogQHByaXZhdGVcclxuXHRcdCAqIEBwYXJhbSB7S2V5Ym9hcmRFdmVudH0gZXYgVGhlIGtleWJvYXJkIGV2ZW50LlxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRfb25fa2V5ZG93bihldikge1xyXG5cdFx0XHRjb25zdCBidG4gPSBldi50YXJnZXQuY2xvc2VzdCggdGhpcy5vcHRzLmhlYWRlcl9zZWxlY3RvciApO1xyXG5cdFx0XHRpZiAoICFidG4gKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRjb25zdCBrZXkgPSBldi5rZXk7XHJcblxyXG5cdFx0XHQvLyBUb2dnbGUgb24gRW50ZXIgLyBTcGFjZS5cclxuXHRcdFx0aWYgKCBrZXkgPT09ICdFbnRlcicgfHwga2V5ID09PSAnICcgKSB7XHJcblx0XHRcdFx0ZXYucHJldmVudERlZmF1bHQoKTtcclxuXHRcdFx0XHRjb25zdCBncm91cCA9IGJ0bi5jbG9zZXN0KCB0aGlzLm9wdHMuZ3JvdXBfc2VsZWN0b3IgKTtcclxuXHRcdFx0XHRpZiAoIGdyb3VwICkge1xyXG5cdFx0XHRcdFx0dGhpcy50b2dnbGUoIGdyb3VwICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTW92ZSBmb2N1cyB3aXRoIEFycm93VXAvQXJyb3dEb3duIGJldHdlZW4gaGVhZGVycyBpbiB0aGlzIGNvbnRhaW5lci5cclxuXHRcdFx0aWYgKCBrZXkgPT09ICdBcnJvd1VwJyB8fCBrZXkgPT09ICdBcnJvd0Rvd24nICkge1xyXG5cdFx0XHRcdGV2LnByZXZlbnREZWZhdWx0KCk7XHJcblx0XHRcdFx0Y29uc3QgaGVhZGVycyA9IEFycmF5LnByb3RvdHlwZS5tYXAuY2FsbChcclxuXHRcdFx0XHRcdHRoaXMucm9vdC5xdWVyeVNlbGVjdG9yQWxsKCB0aGlzLm9wdHMuZ3JvdXBfc2VsZWN0b3IgKSxcclxuXHRcdFx0XHRcdChnKSA9PiBnLnF1ZXJ5U2VsZWN0b3IoIHRoaXMub3B0cy5oZWFkZXJfc2VsZWN0b3IgKVxyXG5cdFx0XHRcdCkuZmlsdGVyKCBCb29sZWFuICk7XHJcblx0XHRcdFx0Y29uc3QgaWR4ICAgICA9IGhlYWRlcnMuaW5kZXhPZiggYnRuICk7XHJcblx0XHRcdFx0aWYgKCBpZHggIT09IC0xICkge1xyXG5cdFx0XHRcdFx0Y29uc3QgbmV4dF9pZHggPSAoa2V5ID09PSAnQXJyb3dEb3duJylcclxuXHRcdFx0XHRcdFx0PyBNYXRoLm1pbiggaGVhZGVycy5sZW5ndGggLSAxLCBpZHggKyAxIClcclxuXHRcdFx0XHRcdFx0OiBNYXRoLm1heCggMCwgaWR4IC0gMSApO1xyXG5cdFx0XHRcdFx0aGVhZGVyc1tuZXh0X2lkeF0uZm9jdXMoKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEFwcGx5IEFSSUEgc3luY2hyb25pemF0aW9uIHRvIGFsbCBrbm93biBncm91cHMgYmFzZWQgb24gdGhlaXIgb3BlbiBzdGF0ZS5cclxuXHRcdCAqXHJcblx0XHQgKiBAcHJpdmF0ZVxyXG5cdFx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRfc3luY19hbGxfYXJpYSgpIHtcclxuXHRcdFx0dGhpcy5fZ3JvdXBzLmZvckVhY2goIChnKSA9PiB0aGlzLl9zeW5jX2dyb3VwX2FyaWEoIGcgKSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogU3luYyBBUklBIGF0dHJpYnV0ZXMgYW5kIHZpc2liaWxpdHkgb24gYSBzaW5nbGUgZ3JvdXAuXHJcblx0XHQgKlxyXG5cdFx0ICogQHByaXZhdGVcclxuXHRcdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGdyb3VwIFRoZSBncm91cCBlbGVtZW50IHRvIHN5bmMuXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBzaW5jZSAyMDI1LTA4LTI2XHJcblx0XHQgKi9cclxuXHRcdF9zeW5jX2dyb3VwX2FyaWEoZ3JvdXApIHtcclxuXHRcdFx0Y29uc3QgaXNfb3BlbiA9IHRoaXMuaXNfb3BlbiggZ3JvdXAgKTtcclxuXHRcdFx0Y29uc3QgaGVhZGVyICA9IGdyb3VwLnF1ZXJ5U2VsZWN0b3IoIHRoaXMub3B0cy5oZWFkZXJfc2VsZWN0b3IgKTtcclxuXHRcdFx0Ly8gT25seSBkaXJlY3QgY2hpbGRyZW4gdGhhdCBtYXRjaC5cclxuXHRcdFx0Y29uc3QgcGFuZWxzID0gQXJyYXkucHJvdG90eXBlLmZpbHRlci5jYWxsKCBncm91cC5jaGlsZHJlbiwgKGVsKSA9PiBlbC5tYXRjaGVzKCB0aGlzLm9wdHMuZmllbGRzX3NlbGVjdG9yICkgKTtcclxuXHJcblx0XHRcdC8vIEhlYWRlciBBUklBLlxyXG5cdFx0XHRpZiAoIGhlYWRlciApIHtcclxuXHRcdFx0XHRoZWFkZXIuc2V0QXR0cmlidXRlKCAncm9sZScsICdidXR0b24nICk7XHJcblx0XHRcdFx0aGVhZGVyLnNldEF0dHJpYnV0ZSggJ2FyaWEtZXhwYW5kZWQnLCBpc19vcGVuID8gJ3RydWUnIDogJ2ZhbHNlJyApO1xyXG5cclxuXHRcdFx0XHRpZiAoIHBhbmVscy5sZW5ndGggKSB7XHJcblx0XHRcdFx0XHQvLyBFbnN1cmUgZWFjaCBwYW5lbCBoYXMgYW4gaWQ7IHRoZW4gd2lyZSBhcmlhLWNvbnRyb2xzIHdpdGggc3BhY2Utc2VwYXJhdGVkIGlkcy5cclxuXHRcdFx0XHRcdGNvbnN0IGlkcyA9IHBhbmVscy5tYXAoIChwKSA9PiB7XHJcblx0XHRcdFx0XHRcdGlmICggIXAuaWQgKSBwLmlkID0gdGhpcy5fZ2VuZXJhdGVfaWQoICd3cGJjX2NvbGxhcHNpYmxlX3BhbmVsJyApO1xyXG5cdFx0XHRcdFx0XHRyZXR1cm4gcC5pZDtcclxuXHRcdFx0XHRcdH0gKTtcclxuXHRcdFx0XHRcdGhlYWRlci5zZXRBdHRyaWJ1dGUoICdhcmlhLWNvbnRyb2xzJywgaWRzLmpvaW4oICcgJyApICk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyAoMykgUGFuZWxzIEFSSUEgKyB2aXNpYmlsaXR5LlxyXG5cdFx0XHRwYW5lbHMuZm9yRWFjaCggKHApID0+IHtcclxuXHRcdFx0XHRwLmhpZGRlbiA9ICFpc19vcGVuOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhY3R1YWwgdmlzaWJpbGl0eS5cclxuXHRcdFx0XHRwLnNldEF0dHJpYnV0ZSggJ2FyaWEtaGlkZGVuJywgaXNfb3BlbiA/ICdmYWxzZScgOiAndHJ1ZScgKTsgLy8gQVJJQS5cclxuXHRcdFx0fSApO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogSW50ZXJuYWwgc3RhdGUgY2hhbmdlOiBzZXQgYSBncm91cCdzIG9wZW4vY2xvc2VkIHN0YXRlLCBzeW5jIEFSSUEsXHJcblx0XHQgKiBtYW5hZ2UgZm9jdXMgb24gY29sbGFwc2UsIGFuZCBlbWl0IGEgY3VzdG9tIGV2ZW50LlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwcml2YXRlXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBncm91cCBUaGUgZ3JvdXAgZWxlbWVudCB0byBtdXRhdGUuXHJcblx0XHQgKiBAcGFyYW0ge2Jvb2xlYW59IG9wZW4gV2hldGhlciB0aGUgZ3JvdXAgc2hvdWxkIGJlIG9wZW4uXHJcblx0XHQgKiBAcmV0dXJucyB7dm9pZH1cclxuXHRcdCAqIEBmaXJlcyBDdXN0b21FdmVudCN3cGJjOmNvbGxhcHNpYmxlOm9wZW5cclxuXHRcdCAqIEBmaXJlcyBDdXN0b21FdmVudCN3cGJjOmNvbGxhcHNpYmxlOmNsb3NlXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRfc2V0X29wZW4oZ3JvdXAsIG9wZW4pIHtcclxuXHRcdFx0aWYgKCAhb3BlbiAmJiBncm91cC5jb250YWlucyggZG9jdW1lbnQuYWN0aXZlRWxlbWVudCApICkge1xyXG5cdFx0XHRcdGNvbnN0IGhlYWRlciA9IGdyb3VwLnF1ZXJ5U2VsZWN0b3IoIHRoaXMub3B0cy5oZWFkZXJfc2VsZWN0b3IgKTtcclxuXHRcdFx0XHRoZWFkZXIgJiYgaGVhZGVyLmZvY3VzKCk7XHJcblx0XHRcdH1cclxuXHRcdFx0Z3JvdXAuY2xhc3NMaXN0LnRvZ2dsZSggdGhpcy5vcHRzLm9wZW5fY2xhc3MsIG9wZW4gKTtcclxuXHRcdFx0dGhpcy5fc3luY19ncm91cF9hcmlhKCBncm91cCApO1xyXG5cdFx0XHRjb25zdCBldl9uYW1lID0gb3BlbiA/ICd3cGJjOmNvbGxhcHNpYmxlOm9wZW4nIDogJ3dwYmM6Y29sbGFwc2libGU6Y2xvc2UnO1xyXG5cdFx0XHRncm91cC5kaXNwYXRjaEV2ZW50KCBuZXcgQ3VzdG9tRXZlbnQoIGV2X25hbWUsIHtcclxuXHRcdFx0XHRidWJibGVzOiB0cnVlLFxyXG5cdFx0XHRcdGRldGFpbCA6IHsgZ3JvdXAsIHJvb3Q6IHRoaXMucm9vdCwgaW5zdGFuY2U6IHRoaXMgfVxyXG5cdFx0XHR9ICkgKTtcclxuXHRcdH1cclxuXHJcblx0XHQvKipcclxuXHRcdCAqIEdlbmVyYXRlIGEgdW5pcXVlIERPTSBpZCB3aXRoIHRoZSBzcGVjaWZpZWQgcHJlZml4LlxyXG5cdFx0ICpcclxuXHRcdCAqIEBwcml2YXRlXHJcblx0XHQgKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IFRoZSBpZCBwcmVmaXggdG8gdXNlLlxyXG5cdFx0ICogQHJldHVybnMge3N0cmluZ30gQSB1bmlxdWUgZWxlbWVudCBpZCBub3QgcHJlc2VudCBpbiB0aGUgZG9jdW1lbnQuXHJcblx0XHQgKiBAc2luY2UgMjAyNS0wOC0yNlxyXG5cdFx0ICovXHJcblx0XHRfZ2VuZXJhdGVfaWQocHJlZml4KSB7XHJcblx0XHRcdGxldCBpID0gMTtcclxuXHRcdFx0bGV0IGlkO1xyXG5cdFx0XHRkbyB7XHJcblx0XHRcdFx0aWQgPSBwcmVmaXggKyAnXycgKyAoaSsrKTtcclxuXHRcdFx0fVxyXG5cdFx0XHR3aGlsZSAoIGQuZ2V0RWxlbWVudEJ5SWQoIGlkICkgKTtcclxuXHRcdFx0cmV0dXJuIGlkO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogQXV0by1pbml0aWFsaXplIGNvbGxhcHNpYmxlIGNvbnRyb2xsZXJzIG9uIHRoZSBwYWdlLlxyXG5cdCAqIEZpbmRzIHRvcC1sZXZlbCBgLndwYmNfY29sbGFwc2libGVgIGNvbnRhaW5lcnMgKGlnbm9yaW5nIG5lc3RlZCBvbmVzKSxcclxuXHQgKiBhbmQgaW5zdGFudGlhdGVzIHtAbGluayBXUEJDX0NvbGxhcHNpYmxlX0dyb3Vwc30gb24gZWFjaC5cclxuXHQgKlxyXG5cdCAqIEBmdW5jdGlvbiBXUEJDX0NvbGxhcHNpYmxlX0F1dG9Jbml0XHJcblx0ICogQHJldHVybnMge3ZvaWR9XHJcblx0ICogQHNpbmNlIDIwMjUtMDgtMjZcclxuXHQgKiBAZXhhbXBsZVxyXG5cdCAqIC8vIFJ1bnMgYXV0b21hdGljYWxseSBvbiBET01Db250ZW50TG9hZGVkOyBjYW4gYWxzbyBiZSBjYWxsZWQgbWFudWFsbHk6XHJcblx0ICogV1BCQ19Db2xsYXBzaWJsZV9BdXRvSW5pdCgpO1xyXG5cdCAqL1xyXG5cdGZ1bmN0aW9uIHdwYmNfY29sbGFwc2libGVfX2F1dG9faW5pdCgpIHtcclxuXHRcdHZhciBST09UICA9ICcud3BiY19jb2xsYXBzaWJsZSc7XHJcblx0XHR2YXIgbm9kZXMgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCggZC5xdWVyeVNlbGVjdG9yQWxsKCBST09UICkgKVxyXG5cdFx0XHQuZmlsdGVyKCBmdW5jdGlvbiAobikge1xyXG5cdFx0XHRcdHJldHVybiAhbi5wYXJlbnRFbGVtZW50IHx8ICFuLnBhcmVudEVsZW1lbnQuY2xvc2VzdCggUk9PVCApO1xyXG5cdFx0XHR9ICk7XHJcblxyXG5cdFx0bm9kZXMuZm9yRWFjaCggZnVuY3Rpb24gKG5vZGUpIHtcclxuXHRcdFx0aWYgKCBub2RlLl9fd3BiY19jb2xsYXBzaWJsZV9pbnN0YW5jZSApIHtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0dmFyIGV4Y2x1c2l2ZSA9IG5vZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnd3BiY19jb2xsYXBzaWJsZS0tZXhjbHVzaXZlJyApIHx8IG5vZGUubWF0Y2hlcyggJ1tkYXRhLXdwYmMtYWNjb3JkaW9uPVwiZXhjbHVzaXZlXCJdJyApO1xyXG5cclxuXHRcdFx0bm9kZS5fX3dwYmNfY29sbGFwc2libGVfaW5zdGFuY2UgPSBuZXcgV1BCQ19Db2xsYXBzaWJsZV9Hcm91cHMoIG5vZGUsIHsgZXhjbHVzaXZlIH0gKS5pbml0KCk7XHJcblx0XHR9ICk7XHJcblx0fVxyXG5cclxuXHQvLyBFeHBvcnQgdG8gZ2xvYmFsIGZvciBtYW51YWwgY29udHJvbCBpZiBuZWVkZWQuXHJcblx0dy5XUEJDX0NvbGxhcHNpYmxlX0dyb3VwcyAgID0gV1BCQ19Db2xsYXBzaWJsZV9Hcm91cHM7XHJcblx0dy5XUEJDX0NvbGxhcHNpYmxlX0F1dG9Jbml0ID0gd3BiY19jb2xsYXBzaWJsZV9fYXV0b19pbml0O1xyXG5cclxuXHQvLyBET00tcmVhZHkgYXV0byBpbml0LlxyXG5cdGlmICggZC5yZWFkeVN0YXRlID09PSAnbG9hZGluZycgKSB7XHJcblx0XHRkLmFkZEV2ZW50TGlzdGVuZXIoICdET01Db250ZW50TG9hZGVkJywgd3BiY19jb2xsYXBzaWJsZV9fYXV0b19pbml0LCB7IG9uY2U6IHRydWUgfSApO1xyXG5cdH0gZWxzZSB7XHJcblx0XHR3cGJjX2NvbGxhcHNpYmxlX19hdXRvX2luaXQoKTtcclxuXHR9XHJcbn0pKCB3aW5kb3csIGRvY3VtZW50ICk7XHJcbiIsIi8qIGdsb2JhbHMgd2luZG93LCBkb2N1bWVudCAqL1xyXG4vKipcclxuICogV1BCQyBTbGlkZXIgTGVuZ3RoIEdyb3Vwc1xyXG4gKlxyXG4gKiBVbml2ZXJzYWwsIGRlcGVuZGVuY3ktZnJlZSBjb250cm9sbGVyIHRoYXQga2VlcHMgYSBcImxlbmd0aFwiIGNvbnRyb2wgaW4gc3luYzpcclxuICogIC0gbnVtYmVyIGlucHV0ICAoZGF0YS13cGJjX3NsaWRlcl9sZW5fdmFsdWUpXHJcbiAqICAtIHVuaXQgc2VsZWN0ICAgKGRhdGEtd3BiY19zbGlkZXJfbGVuX3VuaXQpXHJcbiAqICAtIHJhbmdlIHNsaWRlciAgKGRhdGEtd3BiY19zbGlkZXJfbGVuX3JhbmdlKVxyXG4gKiAgLSB3cml0ZXIgaW5wdXQgIChkYXRhLXdwYmNfc2xpZGVyX2xlbl93cml0ZXIpICBbb3B0aW9uYWwgYnV0IHJlY29tbWVuZGVkXVxyXG4gKlxyXG4gKiBUaGUgXCJ3cml0ZXJcIiBzdG9yZXMgdGhlIGNvbWJpbmVkIHZhbHVlIGxpa2U6IFwiMTAwJVwiLCBcIjQyMHB4XCIsIFwiMTIuNXJlbVwiLlxyXG4gKiBXaGVuIG51bWJlci91bml0L3NsaWRlciBjaGFuZ2UgLT4gd3JpdGVyIHVwZGF0ZXMgYW5kIGVtaXRzICdpbnB1dCcgKGJ1YmJsZXMpLlxyXG4gKiBXaGVuIHdyaXRlciBpcyBjaGFuZ2VkIGV4dGVybmFsbHkgKGFwcGx5LWZyb20tSlNPTiwgZXRjKSAtPiBVSSB1cGRhdGVzLlxyXG4gKlxyXG4gKiBNYXJrdXAgZXhwZWN0YXRpb25zIChtaW5pbWFsKTpcclxuICogIDxkaXYgY2xhc3M9XCJ3cGJjX3NsaWRlcl9sZW5fZ3JvdXBcIlxyXG4gKiAgICAgICBkYXRhLXdwYmNfc2xpZGVyX2xlbl9ib3VuZHNfbWFwPSd7XCIlXCI6e1wibWluXCI6MzAsXCJtYXhcIjoxMDAsXCJzdGVwXCI6MX0sXCJweFwiOntcIm1pblwiOjMwMCxcIm1heFwiOjIwMDAsXCJzdGVwXCI6MTB9fSdcclxuICogICAgICAgZGF0YS13cGJjX3NsaWRlcl9sZW5fZGVmYXVsdF91bml0PVwiJVwiPlxyXG4gKiAgICA8aW5wdXQgdHlwZT1cIm51bWJlclwiIGRhdGEtd3BiY19zbGlkZXJfbGVuX3ZhbHVlPlxyXG4gKiAgICA8c2VsZWN0IGRhdGEtd3BiY19zbGlkZXJfbGVuX3VuaXQ+Li4uPC9zZWxlY3Q+XHJcbiAqICAgIDxpbnB1dCB0eXBlPVwicmFuZ2VcIiBkYXRhLXdwYmNfc2xpZGVyX2xlbl9yYW5nZT5cclxuICogICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgZGF0YS13cGJjX3NsaWRlcl9sZW5fd3JpdGVyIHN0eWxlPVwiZGlzcGxheTpub25lO1wiPlxyXG4gKiAgPC9kaXY+XHJcbiAqXHJcbiAqIFBlcmZvcm1hbmNlIG5vdGVzOlxyXG4gKiAtIE11dGF0aW9uT2JzZXJ2ZXIgaXMgRElTQUJMRUQgYnkgZGVmYXVsdCAocHJldmVudHMgcGVyZm9ybWFuY2UgaXNzdWVzKS5cclxuICogLSBJZiB5b3VyIFVJIHJlLXJlbmRlcnMgYW5kIGluc2VydHMgbmV3IGdyb3VwcyBkeW5hbWljYWxseSwgY2FsbDpcclxuICogICAgIFdQQkNfU2xpZGVyX0xlbl9BdXRvSW5pdCgpOyAgT1IgaW5zdGFuY2UucmVmcmVzaCgpO1xyXG4gKiAgIE9yIGVuYWJsZSBvYnNlcnZlciB2aWE6IG5ldyBXUEJDX1NsaWRlcl9MZW5fR3JvdXBzKHJvb3QsIHsgZW5hYmxlX29ic2VydmVyOnRydWUgfSkuaW5pdCgpO1xyXG4gKlxyXG4gKiBQdWJsaWMgQVBJIChpbnN0YW5jZSBtZXRob2RzKTpcclxuICogIC0gaW5pdCgpLCBkZXN0cm95KCksIHJlZnJlc2goKVxyXG4gKlxyXG4gKiBAdmVyc2lvbiAyMDI2LTAxLTI1XHJcbiAqIEBzaW5jZSAgIDIwMjYtMDEtMjVcclxuICogQGZpbGUgICAgLi4vaW5jbHVkZXMvX19qcy9hZG1pbi9zbGlkZXJfZ3JvdXBzL3dwYmNfbGVuX2dyb3Vwcy5qc1xyXG4gKi9cclxuKGZ1bmN0aW9uICh3LCBkKSB7XHJcblx0J3VzZSBzdHJpY3QnO1xyXG5cclxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0Ly8gSGVscGVyc1xyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRmdW5jdGlvbiBjbGFtcF9udW0odiwgbWluLCBtYXgpIHtcclxuXHRcdGlmICh0eXBlb2YgbWluID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obWluKSkgdiA9IE1hdGgubWF4KG1pbiwgdik7XHJcblx0XHRpZiAodHlwZW9mIG1heCA9PT0gJ251bWJlcicgJiYgIWlzTmFOKG1heCkpIHYgPSBNYXRoLm1pbihtYXgsIHYpO1xyXG5cdFx0cmV0dXJuIHY7XHJcblx0fVxyXG5cclxuXHRmdW5jdGlvbiBwYXJzZV9mbG9hdCh2KSB7XHJcblx0XHR2YXIgbiA9IHBhcnNlRmxvYXQodik7XHJcblx0XHRyZXR1cm4gaXNOYU4obikgPyBudWxsIDogbjtcclxuXHR9XHJcblxyXG5cdGZ1bmN0aW9uIHNhZmVfanNvbl9wYXJzZShzdHIpIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdHJldHVybiBKU09OLnBhcnNlKHN0cik7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0ZnVuY3Rpb24gcGFyc2VfbGVuX2NvbWJpbmVkKHJhdywgZGVmYXVsdF91bml0KSB7XHJcblx0XHR2YXIgcyA9IChyYXcgPT0gbnVsbCkgPyAnJyA6IFN0cmluZyhyYXcpLnRyaW0oKTtcclxuXHRcdGlmICghcykgcmV0dXJuIHsgbnVtOiAnJywgdW5pdDogZGVmYXVsdF91bml0IHx8ICclJyB9O1xyXG5cclxuXHRcdHZhciBtID0gcy5tYXRjaCgvXlxccyooW1xcLV0/XFxkKyg/OlxcLlxcZCspPylcXHMqKFthLXolXSopXFxzKiQvaSk7XHJcblx0XHRpZiAoIW0pIHtcclxuXHRcdFx0Ly8gSWYgaXQncyBub3QgcGFyc2VhYmxlLCB0cmVhdCBhcyBudW1iZXIgYW5kIGtlZXAgZGVmYXVsdCB1bml0LlxyXG5cdFx0XHRyZXR1cm4geyBudW06IHMsIHVuaXQ6IGRlZmF1bHRfdW5pdCB8fCAnJScgfTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgbnVtICA9IG1bMV0gPyBTdHJpbmcobVsxXSkgOiAnJztcclxuXHRcdHZhciB1bml0ID0gbVsyXSA/IFN0cmluZyhtWzJdKSA6ICcnO1xyXG5cdFx0aWYgKCF1bml0KSB1bml0ID0gZGVmYXVsdF91bml0IHx8ICclJztcclxuXHJcblx0XHRyZXR1cm4geyBudW06IG51bSwgdW5pdDogdW5pdCB9O1xyXG5cdH1cclxuXHJcblx0ZnVuY3Rpb24gYnVpbGRfY29tYmluZWQobnVtLCB1bml0KSB7XHJcblx0XHRpZiAobnVtID09IG51bGwgfHwgU3RyaW5nKG51bSkudHJpbSgpID09PSAnJykgcmV0dXJuICcnO1xyXG5cdFx0cmV0dXJuIFN0cmluZyhudW0pICsgU3RyaW5nKHVuaXQgfHwgJycpO1xyXG5cdH1cclxuXHJcblx0ZnVuY3Rpb24gZW1pdF9pbnB1dChlbCkge1xyXG5cdFx0aWYgKCFlbCkgcmV0dXJuO1xyXG5cdFx0ZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0pKTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHQvLyBDb250cm9sbGVyXHJcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdGNsYXNzIFdQQkNfU2xpZGVyX0xlbl9Hcm91cHMge1xyXG5cclxuXHRcdC8qKlxyXG5cdFx0ICogQHBhcmFtIHtIVE1MRWxlbWVudHxzdHJpbmd9IHJvb3RfZWwgQ29udGFpbmVyIChvciBzZWxlY3RvcikuIElmIG9taXR0ZWQsIHVzZXMgZG9jdW1lbnQuXHJcblx0XHQgKiBAcGFyYW0ge09iamVjdH0gW29wdHM9e31dXHJcblx0XHQgKi9cclxuXHRcdGNvbnN0cnVjdG9yKHJvb3RfZWwsIG9wdHMpIHtcclxuXHRcdFx0dGhpcy5yb290ID0gcm9vdF9lbFxyXG5cdFx0XHRcdD8gKCh0eXBlb2Ygcm9vdF9lbCA9PT0gJ3N0cmluZycpID8gZC5xdWVyeVNlbGVjdG9yKHJvb3RfZWwpIDogcm9vdF9lbClcclxuXHRcdFx0XHQ6IGQ7XHJcblxyXG5cdFx0XHR0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHtcclxuXHRcdFx0XHQvLyBTdHJpY3Qgc2VsZWN0b3JzIChOTyBiYWNrd2FyZCBjb21wYXRpYmlsaXR5KS5cclxuXHRcdFx0XHRncm91cF9zZWxlY3RvciAgOiAnLndwYmNfc2xpZGVyX2xlbl9ncm91cCcsXHJcblx0XHRcdFx0dmFsdWVfc2VsZWN0b3IgIDogJ1tkYXRhLXdwYmNfc2xpZGVyX2xlbl92YWx1ZV0nLFxyXG5cdFx0XHRcdHVuaXRfc2VsZWN0b3IgICA6ICdbZGF0YS13cGJjX3NsaWRlcl9sZW5fdW5pdF0nLFxyXG5cdFx0XHRcdHJhbmdlX3NlbGVjdG9yICA6ICdbZGF0YS13cGJjX3NsaWRlcl9sZW5fcmFuZ2VdJyxcclxuXHRcdFx0XHR3cml0ZXJfc2VsZWN0b3IgOiAnW2RhdGEtd3BiY19zbGlkZXJfbGVuX3dyaXRlcl0nLFxyXG5cclxuXHRcdFx0XHRkZWZhdWx0X3VuaXQgICAgOiAnJScsXHJcblxyXG5cdFx0XHRcdGZhbGxiYWNrX2JvdW5kcyA6IHtcclxuXHRcdFx0XHRcdCdweCcgOiB7IG1pbjogMCwgICBtYXg6IDUxMiwgIHN0ZXA6IDEgICB9LFxyXG5cdFx0XHRcdFx0JyUnICA6IHsgbWluOiAwLCAgIG1heDogMTAwLCAgc3RlcDogMSAgIH0sXHJcblx0XHRcdFx0XHQncmVtJzogeyBtaW46IDAsICAgbWF4OiAxMCwgICBzdGVwOiAwLjEgfSxcclxuXHRcdFx0XHRcdCdlbScgOiB7IG1pbjogMCwgICBtYXg6IDEwLCAgIHN0ZXA6IDAuMSB9XHJcblx0XHRcdFx0fSxcclxuXHJcblx0XHRcdFx0Ly8gRGlzYWJsZWQgYnkgZGVmYXVsdCBmb3IgcGVyZm9ybWFuY2UuXHJcblx0XHRcdFx0ZW5hYmxlX29ic2VydmVyICAgICA6IGZhbHNlLFxyXG5cdFx0XHRcdG9ic2VydmVyX2RlYm91bmNlX21zOiAxNTBcclxuXHRcdFx0fSwgb3B0cyB8fCB7fSk7XHJcblxyXG5cdFx0XHR0aGlzLl9vbl9pbnB1dCAgPSB0aGlzLl9vbl9pbnB1dC5iaW5kKHRoaXMpO1xyXG5cdFx0XHR0aGlzLl9vbl9jaGFuZ2UgPSB0aGlzLl9vbl9jaGFuZ2UuYmluZCh0aGlzKTtcclxuXHJcblx0XHRcdHRoaXMuX2JvdW5kc19jYWNoZSA9IG5ldyBXZWFrTWFwKCk7IC8vIGdyb3VwIC0+IGJvdW5kc19tYXBfb2JqZWN0XHJcblx0XHRcdHRoaXMuX29ic2VydmVyICAgICA9IG51bGw7XHJcblx0XHRcdHRoaXMuX3JlZnJlc2hfdG1yICA9IG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0aW5pdCgpIHtcclxuXHRcdFx0aWYgKCF0aGlzLnJvb3QpIHJldHVybiB0aGlzO1xyXG5cclxuXHRcdFx0dGhpcy5yb290LmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgIHRoaXMuX29uX2lucHV0LCAgdHJ1ZSk7XHJcblx0XHRcdHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB0aGlzLl9vbl9jaGFuZ2UsIHRydWUpO1xyXG5cclxuXHRcdFx0aWYgKHRoaXMub3B0cy5lbmFibGVfb2JzZXJ2ZXIgJiYgdy5NdXRhdGlvbk9ic2VydmVyKSB7XHJcblx0XHRcdFx0dGhpcy5fb2JzZXJ2ZXIgPSBuZXcgTXV0YXRpb25PYnNlcnZlcigoKSA9PiB7IHRoaXMuX2RlYm91bmNlZF9yZWZyZXNoKCk7IH0pO1xyXG5cdFx0XHRcdHRoaXMuX29ic2VydmVyLm9ic2VydmUodGhpcy5yb290ID09PSBkID8gZC5kb2N1bWVudEVsZW1lbnQgOiB0aGlzLnJvb3QsIHsgY2hpbGRMaXN0OiB0cnVlLCBzdWJ0cmVlOiB0cnVlIH0pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLnJlZnJlc2goKTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0ZGVzdHJveSgpIHtcclxuXHRcdFx0aWYgKCF0aGlzLnJvb3QpIHJldHVybjtcclxuXHJcblx0XHRcdHRoaXMucm9vdC5yZW1vdmVFdmVudExpc3RlbmVyKCdpbnB1dCcsICB0aGlzLl9vbl9pbnB1dCwgIHRydWUpO1xyXG5cdFx0XHR0aGlzLnJvb3QucmVtb3ZlRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy5fb25fY2hhbmdlLCB0cnVlKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLl9vYnNlcnZlcikge1xyXG5cdFx0XHRcdHRoaXMuX29ic2VydmVyLmRpc2Nvbm5lY3QoKTtcclxuXHRcdFx0XHR0aGlzLl9vYnNlcnZlciA9IG51bGw7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGlmICh0aGlzLl9yZWZyZXNoX3Rtcikge1xyXG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9yZWZyZXNoX3Rtcik7XHJcblx0XHRcdFx0dGhpcy5fcmVmcmVzaF90bXIgPSBudWxsO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmVmcmVzaCgpIHtcclxuXHRcdFx0aWYgKCF0aGlzLnJvb3QpIHJldHVybjtcclxuXHJcblx0XHRcdHZhciBzY29wZSAgPSAodGhpcy5yb290ID09PSBkID8gZCA6IHRoaXMucm9vdCk7XHJcblx0XHRcdHZhciBncm91cHMgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChzY29wZS5xdWVyeVNlbGVjdG9yQWxsKHRoaXMub3B0cy5ncm91cF9zZWxlY3RvcikpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCBncm91cHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0XHR0aGlzLl9zeW5jX2dyb3VwX2Zyb21fd3JpdGVyKGdyb3Vwc1tpXSk7XHJcblx0XHRcdFx0dGhpcy5fYXBwbHlfYm91bmRzX2Zvcl9jdXJyZW50X3VuaXQoZ3JvdXBzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRcdC8vIEludGVybmFsXHJcblx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHRfZGVib3VuY2VkX3JlZnJlc2goKSB7XHJcblx0XHRcdGlmICh0aGlzLl9yZWZyZXNoX3RtcikgY2xlYXJUaW1lb3V0KHRoaXMuX3JlZnJlc2hfdG1yKTtcclxuXHRcdFx0dGhpcy5fcmVmcmVzaF90bXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuXHRcdFx0XHR0aGlzLl9yZWZyZXNoX3RtciA9IG51bGw7XHJcblx0XHRcdFx0dGhpcy5yZWZyZXNoKCk7XHJcblx0XHRcdH0sIE51bWJlcih0aGlzLm9wdHMub2JzZXJ2ZXJfZGVib3VuY2VfbXMpIHx8IDApO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9maW5kX2dyb3VwKGVsKSB7XHJcblx0XHRcdHJldHVybiAoZWwgJiYgZWwuY2xvc2VzdCkgPyBlbC5jbG9zZXN0KHRoaXMub3B0cy5ncm91cF9zZWxlY3RvcikgOiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9nZXRfcGFydHMoZ3JvdXApIHtcclxuXHRcdFx0aWYgKCFncm91cCkgcmV0dXJuIG51bGw7XHJcblx0XHRcdHJldHVybiB7XHJcblx0XHRcdFx0Z3JvdXAgOiBncm91cCxcclxuXHRcdFx0XHRudW0gICA6IGdyb3VwLnF1ZXJ5U2VsZWN0b3IodGhpcy5vcHRzLnZhbHVlX3NlbGVjdG9yKSxcclxuXHRcdFx0XHR1bml0ICA6IGdyb3VwLnF1ZXJ5U2VsZWN0b3IodGhpcy5vcHRzLnVuaXRfc2VsZWN0b3IpLFxyXG5cdFx0XHRcdHJhbmdlIDogZ3JvdXAucXVlcnlTZWxlY3Rvcih0aGlzLm9wdHMucmFuZ2Vfc2VsZWN0b3IpLFxyXG5cdFx0XHRcdHdyaXRlcjogZ3JvdXAucXVlcnlTZWxlY3Rvcih0aGlzLm9wdHMud3JpdGVyX3NlbGVjdG9yKVxyXG5cdFx0XHR9O1xyXG5cdFx0fVxyXG5cclxuXHRcdF9nZXRfZGVmYXVsdF91bml0KGdyb3VwKSB7XHJcblx0XHRcdHZhciBkdSA9IChncm91cCAmJiBncm91cC5nZXRBdHRyaWJ1dGUpXHJcblx0XHRcdFx0PyBncm91cC5nZXRBdHRyaWJ1dGUoJ2RhdGEtd3BiY19zbGlkZXJfbGVuX2RlZmF1bHRfdW5pdCcpXHJcblx0XHRcdFx0OiAnJztcclxuXHRcdFx0cmV0dXJuIGR1ID8gU3RyaW5nKGR1KSA6IHRoaXMub3B0cy5kZWZhdWx0X3VuaXQ7XHJcblx0XHR9XHJcblxyXG5cdFx0X2dldF9ib3VuZHNfbWFwKGdyb3VwKSB7XHJcblx0XHRcdGlmICghZ3JvdXApIHJldHVybiBudWxsO1xyXG5cdFx0XHRpZiAodGhpcy5fYm91bmRzX2NhY2hlLmhhcyhncm91cCkpIHtcclxuXHRcdFx0XHRyZXR1cm4gdGhpcy5fYm91bmRzX2NhY2hlLmdldChncm91cCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHZhciByYXcgPSBncm91cC5nZXRBdHRyaWJ1dGUoJ2RhdGEtd3BiY19zbGlkZXJfbGVuX2JvdW5kc19tYXAnKTtcclxuXHRcdFx0dmFyIG1hcCA9IHJhdyA/IHNhZmVfanNvbl9wYXJzZShyYXcpIDogbnVsbDtcclxuXHRcdFx0aWYgKCFtYXAgfHwgdHlwZW9mIG1hcCAhPT0gJ29iamVjdCcpIG1hcCA9IG51bGw7XHJcblxyXG5cdFx0XHR0aGlzLl9ib3VuZHNfY2FjaGUuc2V0KGdyb3VwLCBtYXApO1xyXG5cdFx0XHRyZXR1cm4gbWFwO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9nZXRfYm91bmRzX2Zvcl91bml0KGdyb3VwLCB1bml0KSB7XHJcblx0XHRcdHZhciBtYXAgPSB0aGlzLl9nZXRfYm91bmRzX21hcChncm91cCk7XHJcblx0XHRcdGlmIChtYXAgJiYgdW5pdCAmJiBtYXBbdW5pdF0pIHtcclxuXHRcdFx0XHRyZXR1cm4gbWFwW3VuaXRdO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzLm9wdHMuZmFsbGJhY2tfYm91bmRzW3VuaXRdIHx8IHRoaXMub3B0cy5mYWxsYmFja19ib3VuZHNbJ3B4J107XHJcblx0XHR9XHJcblxyXG5cdFx0X2FwcGx5X2JvdW5kcyhwYXJ0cywgYm91bmRzKSB7XHJcblx0XHRcdGlmICghcGFydHMgfHwgIWJvdW5kcykgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIG1pbiAgPSAoYm91bmRzLm1pbiAgIT0gbnVsbCkgPyBOdW1iZXIoYm91bmRzLm1pbikgIDogbnVsbDtcclxuXHRcdFx0dmFyIG1heCAgPSAoYm91bmRzLm1heCAgIT0gbnVsbCkgPyBOdW1iZXIoYm91bmRzLm1heCkgIDogbnVsbDtcclxuXHRcdFx0dmFyIHN0ZXAgPSAoYm91bmRzLnN0ZXAgIT0gbnVsbCkgPyBOdW1iZXIoYm91bmRzLnN0ZXApIDogbnVsbDtcclxuXHJcblx0XHRcdGlmIChwYXJ0cy5yYW5nZSkge1xyXG5cdFx0XHRcdGlmICghaXNOYU4obWluKSkgIHBhcnRzLnJhbmdlLm1pbiAgPSBTdHJpbmcobWluKTtcclxuXHRcdFx0XHRpZiAoIWlzTmFOKG1heCkpICBwYXJ0cy5yYW5nZS5tYXggID0gU3RyaW5nKG1heCk7XHJcblx0XHRcdFx0aWYgKCFpc05hTihzdGVwKSkgcGFydHMucmFuZ2Uuc3RlcCA9IFN0cmluZyhzdGVwKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRpZiAocGFydHMubnVtKSB7XHJcblx0XHRcdFx0aWYgKCFpc05hTihtaW4pKSAgcGFydHMubnVtLm1pbiAgPSBTdHJpbmcobWluKTtcclxuXHRcdFx0XHRpZiAoIWlzTmFOKG1heCkpICBwYXJ0cy5udW0ubWF4ICA9IFN0cmluZyhtYXgpO1xyXG5cdFx0XHRcdGlmICghaXNOYU4oc3RlcCkpIHBhcnRzLm51bS5zdGVwID0gU3RyaW5nKHN0ZXApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0X2FwcGx5X2JvdW5kc19mb3JfY3VycmVudF91bml0KGdyb3VwKSB7XHJcblx0XHRcdHZhciBwYXJ0cyA9IHRoaXMuX2dldF9wYXJ0cyhncm91cCk7XHJcblx0XHRcdGlmICghcGFydHMgfHwgIXBhcnRzLnVuaXQpIHJldHVybjtcclxuXHJcblx0XHRcdHZhciB1bml0ID0gcGFydHMudW5pdC52YWx1ZSB8fCB0aGlzLl9nZXRfZGVmYXVsdF91bml0KGdyb3VwKTtcclxuXHRcdFx0dmFyIGIgICAgPSB0aGlzLl9nZXRfYm91bmRzX2Zvcl91bml0KGdyb3VwLCB1bml0KTtcclxuXHJcblx0XHRcdHRoaXMuX2FwcGx5X2JvdW5kcyhwYXJ0cywgYik7XHJcblxyXG5cdFx0XHQvLyBDbGFtcCBjdXJyZW50IHZhbHVlIHRvIG5ldyBib3VuZHMuXHJcblx0XHRcdHZhciB2ID0gcGFyc2VfZmxvYXQocGFydHMubnVtICYmIHBhcnRzLm51bS52YWx1ZSA/IHBhcnRzLm51bS52YWx1ZSA6IChwYXJ0cy5yYW5nZSA/IHBhcnRzLnJhbmdlLnZhbHVlIDogJycpKTtcclxuXHRcdFx0aWYgKHYgPT0gbnVsbCkgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIG1pbiA9IChiICYmIGIubWluICE9IG51bGwpID8gTnVtYmVyKGIubWluKSA6IG51bGw7XHJcblx0XHRcdHZhciBtYXggPSAoYiAmJiBiLm1heCAhPSBudWxsKSA/IE51bWJlcihiLm1heCkgOiBudWxsO1xyXG5cdFx0XHR2ID0gY2xhbXBfbnVtKHYsIGlzTmFOKG1pbikgPyBudWxsIDogbWluLCBpc05hTihtYXgpID8gbnVsbCA6IG1heCk7XHJcblxyXG5cdFx0XHRpZiAocGFydHMubnVtKSAgIHBhcnRzLm51bS52YWx1ZSAgID0gU3RyaW5nKHYpO1xyXG5cdFx0XHRpZiAocGFydHMucmFuZ2UpIHBhcnRzLnJhbmdlLnZhbHVlID0gU3RyaW5nKHYpO1xyXG5cclxuXHRcdFx0dGhpcy5fd3JpdGVfY29tYmluZWQocGFydHMsIFN0cmluZyh2KSwgdW5pdCwgLyplbWl0Ki8gZmFsc2UpO1xyXG5cdFx0fVxyXG5cclxuXHRcdF93cml0ZV9jb21iaW5lZChwYXJ0cywgbnVtLCB1bml0LCBlbWl0KSB7XHJcblx0XHRcdGlmICghcGFydHMpIHJldHVybjtcclxuXHJcblx0XHRcdHZhciBjb21iaW5lZCA9IGJ1aWxkX2NvbWJpbmVkKG51bSwgdW5pdCk7XHJcblxyXG5cdFx0XHRpZiAocGFydHMud3JpdGVyKSB7XHJcblx0XHRcdFx0Ly8gQXZvaWQgcmVjdXJzaW9uOiBtYXJrIGFzIGludGVybmFsIHdyaXRlLlxyXG5cdFx0XHRcdHBhcnRzLndyaXRlci5fX3dwYmNfc2xpZGVyX2xlbl9pbnRlcm5hbCA9IHRydWU7XHJcblx0XHRcdFx0cGFydHMud3JpdGVyLnZhbHVlID0gY29tYmluZWQ7XHJcblx0XHRcdFx0aWYgKGVtaXQpIGVtaXRfaW5wdXQocGFydHMud3JpdGVyKTtcclxuXHRcdFx0XHRwYXJ0cy53cml0ZXIuX193cGJjX3NsaWRlcl9sZW5faW50ZXJuYWwgPSBmYWxzZTtcclxuXHRcdFx0fSBlbHNlIGlmIChwYXJ0cy5udW0pIHtcclxuXHRcdFx0XHQvLyBJZiB3cml0ZXIgaXMgbWlzc2luZywgYXQgbGVhc3Qgbm90aWZ5IHZpYSBudW1iZXIgaW5wdXQuXHJcblx0XHRcdFx0aWYgKGVtaXQpIGVtaXRfaW5wdXQocGFydHMubnVtKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdF9zeW5jX2dyb3VwX2Zyb21fd3JpdGVyKGdyb3VwKSB7XHJcblx0XHRcdHZhciBwYXJ0cyA9IHRoaXMuX2dldF9wYXJ0cyhncm91cCk7XHJcblx0XHRcdGlmICghcGFydHMgfHwgIXBhcnRzLndyaXRlcikgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIHJhdyA9IFN0cmluZyhwYXJ0cy53cml0ZXIudmFsdWUgfHwgJycpLnRyaW0oKTtcclxuXHRcdFx0aWYgKCFyYXcpIHJldHVybjtcclxuXHJcblx0XHRcdHZhciBkdSA9IHRoaXMuX2dldF9kZWZhdWx0X3VuaXQoZ3JvdXApO1xyXG5cdFx0XHR2YXIgcCAgPSBwYXJzZV9sZW5fY29tYmluZWQocmF3LCBkdSk7XHJcblxyXG5cdFx0XHRpZiAocGFydHMudW5pdCkgIHBhcnRzLnVuaXQudmFsdWUgID0gcC51bml0O1xyXG5cdFx0XHRpZiAocGFydHMubnVtKSAgIHBhcnRzLm51bS52YWx1ZSAgID0gcC5udW07XHJcblx0XHRcdGlmIChwYXJ0cy5yYW5nZSkgcGFydHMucmFuZ2UudmFsdWUgPSBwLm51bTtcclxuXHRcdH1cclxuXHJcblx0XHRfb25faW5wdXQoZXYpIHtcclxuXHRcdFx0dmFyIHQgPSBldi50YXJnZXQ7XHJcblx0XHRcdGlmICghdCkgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIGdyb3VwID0gdGhpcy5fZmluZF9ncm91cCh0KTtcclxuXHRcdFx0aWYgKCFncm91cCkgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIHBhcnRzID0gdGhpcy5fZ2V0X3BhcnRzKGdyb3VwKTtcclxuXHRcdFx0aWYgKCFwYXJ0cykgcmV0dXJuO1xyXG5cclxuXHRcdFx0Ly8gV3JpdGVyIGNoYW5nZWQgZXh0ZXJuYWxseSAtPiB1cGRhdGUgVUkuXHJcblx0XHRcdGlmIChwYXJ0cy53cml0ZXIgJiYgdCA9PT0gcGFydHMud3JpdGVyKSB7XHJcblx0XHRcdFx0aWYgKHQuX193cGJjX3NsaWRlcl9sZW5faW50ZXJuYWwpIHJldHVybjtcclxuXHRcdFx0XHR0aGlzLl9zeW5jX2dyb3VwX2Zyb21fd3JpdGVyKGdyb3VwKTtcclxuXHRcdFx0XHR0aGlzLl9hcHBseV9ib3VuZHNfZm9yX2N1cnJlbnRfdW5pdChncm91cCk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBTbGlkZXIgbW92ZWQgLT4gdXBkYXRlIG51bWJlciArIHdyaXRlci5cclxuXHRcdFx0aWYgKHQubWF0Y2hlcyAmJiB0Lm1hdGNoZXModGhpcy5vcHRzLnJhbmdlX3NlbGVjdG9yKSkge1xyXG5cdFx0XHRcdGlmIChwYXJ0cy5udW0pIHBhcnRzLm51bS52YWx1ZSA9IHQudmFsdWU7XHJcblxyXG5cdFx0XHRcdHZhciB1bml0ID0gKHBhcnRzLnVuaXQgJiYgcGFydHMudW5pdC52YWx1ZSkgPyBwYXJ0cy51bml0LnZhbHVlIDogdGhpcy5fZ2V0X2RlZmF1bHRfdW5pdChncm91cCk7XHJcblx0XHRcdFx0dGhpcy5fd3JpdGVfY29tYmluZWQocGFydHMsIHQudmFsdWUsIHVuaXQsIC8qZW1pdCovIHRydWUpO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTnVtYmVyIHR5cGVkIC0+IHVwZGF0ZSBzbGlkZXIgKyB3cml0ZXIgKGNsYW1wIGlmIHNsaWRlciBoYXMgYm91bmRzKS5cclxuXHRcdFx0aWYgKHQubWF0Y2hlcyAmJiB0Lm1hdGNoZXModGhpcy5vcHRzLnZhbHVlX3NlbGVjdG9yKSkge1xyXG5cdFx0XHRcdHZhciB2ID0gcGFyc2VfZmxvYXQodC52YWx1ZSk7XHJcblxyXG5cdFx0XHRcdGlmICh2ICE9IG51bGwgJiYgcGFydHMucmFuZ2UpIHtcclxuXHRcdFx0XHRcdHZhciBybWluID0gTnVtYmVyKHBhcnRzLnJhbmdlLm1pbik7XHJcblx0XHRcdFx0XHR2YXIgcm1heCA9IE51bWJlcihwYXJ0cy5yYW5nZS5tYXgpO1xyXG5cdFx0XHRcdFx0diA9IGNsYW1wX251bSh2LCBpc05hTihybWluKSA/IG51bGwgOiBybWluLCBpc05hTihybWF4KSA/IG51bGwgOiBybWF4KTtcclxuXHJcblx0XHRcdFx0XHRwYXJ0cy5yYW5nZS52YWx1ZSA9IFN0cmluZyh2KTtcclxuXHRcdFx0XHRcdGlmIChTdHJpbmcodikgIT09IHQudmFsdWUpIHQudmFsdWUgPSBTdHJpbmcodik7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHR2YXIgdW5pdDIgPSAocGFydHMudW5pdCAmJiBwYXJ0cy51bml0LnZhbHVlKSA/IHBhcnRzLnVuaXQudmFsdWUgOiB0aGlzLl9nZXRfZGVmYXVsdF91bml0KGdyb3VwKTtcclxuXHRcdFx0XHR0aGlzLl93cml0ZV9jb21iaW5lZChwYXJ0cywgdC52YWx1ZSwgdW5pdDIsIC8qZW1pdCovIHRydWUpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0X29uX2NoYW5nZShldikge1xyXG5cdFx0XHR2YXIgdCA9IGV2LnRhcmdldDtcclxuXHRcdFx0aWYgKCF0KSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgZ3JvdXAgPSB0aGlzLl9maW5kX2dyb3VwKHQpO1xyXG5cdFx0XHRpZiAoIWdyb3VwKSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgcGFydHMgPSB0aGlzLl9nZXRfcGFydHMoZ3JvdXApO1xyXG5cdFx0XHRpZiAoIXBhcnRzKSByZXR1cm47XHJcblxyXG5cdFx0XHQvLyBVbml0IGNoYW5nZWQgLT4gdXBkYXRlIGJvdW5kcyArIHdyaXRlci5cclxuXHRcdFx0aWYgKHQubWF0Y2hlcyAmJiB0Lm1hdGNoZXModGhpcy5vcHRzLnVuaXRfc2VsZWN0b3IpKSB7XHJcblx0XHRcdFx0dGhpcy5fYXBwbHlfYm91bmRzX2Zvcl9jdXJyZW50X3VuaXQoZ3JvdXApO1xyXG5cclxuXHRcdFx0XHR2YXIgbnVtICA9IHBhcnRzLm51bSA/IHBhcnRzLm51bS52YWx1ZSA6IChwYXJ0cy5yYW5nZSA/IHBhcnRzLnJhbmdlLnZhbHVlIDogJycpO1xyXG5cdFx0XHRcdHZhciB1bml0ID0gdC52YWx1ZSB8fCB0aGlzLl9nZXRfZGVmYXVsdF91bml0KGdyb3VwKTtcclxuXHRcdFx0XHR0aGlzLl93cml0ZV9jb21iaW5lZChwYXJ0cywgbnVtLCB1bml0LCAvKmVtaXQqLyB0cnVlKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdC8vIEF1dG8taW5pdFxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRmdW5jdGlvbiB3cGJjX3NsaWRlcl9sZW5fZ3JvdXBzX19hdXRvX2luaXQoKSB7XHJcblx0XHR2YXIgUk9PVCAgPSAnLndwYmNfc2xpZGVyX2xlbl9ncm91cHMnO1xyXG5cdFx0dmFyIG5vZGVzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZC5xdWVyeVNlbGVjdG9yQWxsKFJPT1QpKVxyXG5cdFx0XHQuZmlsdGVyKGZ1bmN0aW9uIChuKSB7IHJldHVybiAhbi5wYXJlbnRFbGVtZW50IHx8ICFuLnBhcmVudEVsZW1lbnQuY2xvc2VzdChST09UKTsgfSk7XHJcblxyXG5cdFx0Ly8gSWYgbm8gZXhwbGljaXQgY29udGFpbmVycywgaW5zdGFsbCBhIHNpbmdsZSBkb2N1bWVudC1yb290IGluc3RhbmNlLlxyXG5cdFx0aWYgKCFub2Rlcy5sZW5ndGgpIHtcclxuXHRcdFx0aWYgKCFkLl9fd3BiY19zbGlkZXJfbGVuX2dyb3Vwc19nbG9iYWxfaW5zdGFuY2UpIHtcclxuXHRcdFx0XHRkLl9fd3BiY19zbGlkZXJfbGVuX2dyb3Vwc19nbG9iYWxfaW5zdGFuY2UgPSBuZXcgV1BCQ19TbGlkZXJfTGVuX0dyb3VwcyhkKS5pbml0KCk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdG5vZGVzLmZvckVhY2goZnVuY3Rpb24gKG5vZGUpIHtcclxuXHRcdFx0aWYgKG5vZGUuX193cGJjX3NsaWRlcl9sZW5fZ3JvdXBzX2luc3RhbmNlKSByZXR1cm47XHJcblx0XHRcdG5vZGUuX193cGJjX3NsaWRlcl9sZW5fZ3JvdXBzX2luc3RhbmNlID0gbmV3IFdQQkNfU2xpZGVyX0xlbl9Hcm91cHMobm9kZSkuaW5pdCgpO1xyXG5cdFx0fSk7XHJcblx0fVxyXG5cclxuXHQvLyBFeHBvcnQgZ2xvYmFscyAobWFudWFsIGNvbnRyb2wgaWYgbmVlZGVkKS5cclxuXHR3LldQQkNfU2xpZGVyX0xlbl9Hcm91cHMgICA9IFdQQkNfU2xpZGVyX0xlbl9Hcm91cHM7XHJcblx0dy5XUEJDX1NsaWRlcl9MZW5fQXV0b0luaXQgPSB3cGJjX3NsaWRlcl9sZW5fZ3JvdXBzX19hdXRvX2luaXQ7XHJcblxyXG5cdC8vIERPTS1yZWFkeSBhdXRvIGluaXQuXHJcblx0aWYgKGQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnKSB7XHJcblx0XHRkLmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCB3cGJjX3NsaWRlcl9sZW5fZ3JvdXBzX19hdXRvX2luaXQsIHsgb25jZTogdHJ1ZSB9KTtcclxuXHR9IGVsc2Uge1xyXG5cdFx0d3BiY19zbGlkZXJfbGVuX2dyb3Vwc19fYXV0b19pbml0KCk7XHJcblx0fVxyXG5cclxufSkod2luZG93LCBkb2N1bWVudCk7XHJcbiIsIi8qIGdsb2JhbHMgd2luZG93LCBkb2N1bWVudCAqL1xyXG4vKipcclxuICogV1BCQyBTbGlkZXIgUmFuZ2UgR3JvdXBzXHJcbiAqXHJcbiAqIFVuaXZlcnNhbCwgZGVwZW5kZW5jeS1mcmVlIGNvbnRyb2xsZXIgdGhhdCBrZWVwcyBhIFwicmFuZ2UgKyBudW1iZXJcIiBwYWlyIGluIHN5bmM6XHJcbiAqICAtIG51bWJlciBpbnB1dCAgKGRhdGEtd3BiY19zbGlkZXJfcmFuZ2VfdmFsdWUpXHJcbiAqICAtIHJhbmdlIHNsaWRlciAgKGRhdGEtd3BiY19zbGlkZXJfcmFuZ2VfcmFuZ2UpXHJcbiAqICAtIHdyaXRlciBpbnB1dCAgKGRhdGEtd3BiY19zbGlkZXJfcmFuZ2Vfd3JpdGVyKSBbb3B0aW9uYWxdXHJcbiAqXHJcbiAqIElmIHdyaXRlciBleGlzdHM6IG51bWJlci9zbGlkZXIgdXBkYXRlIHdyaXRlciBhbmQgZW1pdCAnaW5wdXQnIG9uIHdyaXRlciAoYnViYmxlcykuXHJcbiAqIElmIHdyaXRlciBpcyBtaXNzaW5nOiBlbWl0cyAnaW5wdXQnIG9uIHRoZSBudW1iZXIgaW5wdXQuXHJcbiAqIElmIHdyaXRlciBjaGFuZ2VzIGV4dGVybmFsbHk6IHVwZGF0ZXMgbnVtYmVyL3NsaWRlci5cclxuICpcclxuICogTWFya3VwIGV4cGVjdGF0aW9ucyAobWluaW1hbCk6XHJcbiAqICA8ZGl2IGNsYXNzPVwid3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBcIj5cclxuICogICAgPGlucHV0IHR5cGU9XCJudW1iZXJcIiBkYXRhLXdwYmNfc2xpZGVyX3JhbmdlX3ZhbHVlPlxyXG4gKiAgICA8aW5wdXQgdHlwZT1cInJhbmdlXCIgIGRhdGEtd3BiY19zbGlkZXJfcmFuZ2VfcmFuZ2U+XHJcbiAqICAgIDwhLS0gb3B0aW9uYWwgLS0+XHJcbiAqICAgIDxpbnB1dCB0eXBlPVwidGV4dFwiIGRhdGEtd3BiY19zbGlkZXJfcmFuZ2Vfd3JpdGVyIHN0eWxlPVwiZGlzcGxheTpub25lO1wiPlxyXG4gKiAgPC9kaXY+XHJcbiAqXHJcbiAqIFBlcmZvcm1hbmNlIG5vdGVzOlxyXG4gKiAtIE11dGF0aW9uT2JzZXJ2ZXIgaXMgRElTQUJMRUQgYnkgZGVmYXVsdC5cclxuICogLSBJZiB5b3VyIFVJIHJlLXJlbmRlcnMgYW5kIGluc2VydHMgbmV3IGdyb3VwcyBkeW5hbWljYWxseSwgY2FsbDpcclxuICogICAgIFdQQkNfU2xpZGVyX1JhbmdlX0F1dG9Jbml0KCk7IE9SIGluc3RhbmNlLnJlZnJlc2goKTtcclxuICogICBPciBlbmFibGUgb2JzZXJ2ZXIgdmlhOiBuZXcgV1BCQ19TbGlkZXJfUmFuZ2VfR3JvdXBzKHJvb3QsIHsgZW5hYmxlX29ic2VydmVyOnRydWUgfSkuaW5pdCgpO1xyXG4gKlxyXG4gKiBQdWJsaWMgQVBJIChpbnN0YW5jZSBtZXRob2RzKTpcclxuICogIC0gaW5pdCgpLCBkZXN0cm95KCksIHJlZnJlc2goKVxyXG4gKlxyXG4gKiBAdmVyc2lvbiAyMDI2LTAxLTI1XHJcbiAqIEBzaW5jZSAgIDIwMjYtMDEtMjVcclxuICogQGZpbGUgICAgLi4vaW5jbHVkZXMvX19qcy9hZG1pbi9zbGlkZXJfZ3JvdXBzL3dwYmNfcmFuZ2VfZ3JvdXBzLmpzXHJcbiAqL1xyXG4oZnVuY3Rpb24gKHcsIGQpIHtcclxuXHQndXNlIHN0cmljdCc7XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHQvLyBIZWxwZXJzXHJcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdGZ1bmN0aW9uIGNsYW1wX251bSh2LCBtaW4sIG1heCkge1xyXG5cdFx0aWYgKHR5cGVvZiBtaW4gPT09ICdudW1iZXInICYmICFpc05hTihtaW4pKSB2ID0gTWF0aC5tYXgobWluLCB2KTtcclxuXHRcdGlmICh0eXBlb2YgbWF4ID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obWF4KSkgdiA9IE1hdGgubWluKG1heCwgdik7XHJcblx0XHRyZXR1cm4gdjtcclxuXHR9XHJcblxyXG5cdGZ1bmN0aW9uIHBhcnNlX2Zsb2F0KHYpIHtcclxuXHRcdHZhciBuID0gcGFyc2VGbG9hdCh2KTtcclxuXHRcdHJldHVybiBpc05hTihuKSA/IG51bGwgOiBuO1xyXG5cdH1cclxuXHJcblx0ZnVuY3Rpb24gZW1pdF9pbnB1dChlbCkge1xyXG5cdFx0aWYgKCFlbCkgcmV0dXJuO1xyXG5cdFx0ZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2lucHV0JywgeyBidWJibGVzOiB0cnVlIH0pKTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHQvLyBDb250cm9sbGVyXHJcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdGNsYXNzIFdQQkNfU2xpZGVyX1JhbmdlX0dyb3VwcyB7XHJcblxyXG5cdFx0LyoqXHJcblx0XHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fHN0cmluZ30gcm9vdF9lbCBDb250YWluZXIgKG9yIHNlbGVjdG9yKS4gSWYgb21pdHRlZCwgdXNlcyBkb2N1bWVudC5cclxuXHRcdCAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0cz17fV1cclxuXHRcdCAqL1xyXG5cdFx0Y29uc3RydWN0b3Iocm9vdF9lbCwgb3B0cykge1xyXG5cdFx0XHR0aGlzLnJvb3QgPSByb290X2VsXHJcblx0XHRcdFx0PyAoKHR5cGVvZiByb290X2VsID09PSAnc3RyaW5nJykgPyBkLnF1ZXJ5U2VsZWN0b3Iocm9vdF9lbCkgOiByb290X2VsKVxyXG5cdFx0XHRcdDogZDtcclxuXHJcblx0XHRcdHRoaXMub3B0cyA9IE9iamVjdC5hc3NpZ24oe1xyXG5cdFx0XHRcdC8vIFN0cmljdCBzZWxlY3RvcnMgKE5PIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkpLlxyXG5cdFx0XHRcdGdyb3VwX3NlbGVjdG9yICA6ICcud3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXAnLFxyXG5cdFx0XHRcdHZhbHVlX3NlbGVjdG9yICA6ICdbZGF0YS13cGJjX3NsaWRlcl9yYW5nZV92YWx1ZV0nLFxyXG5cdFx0XHRcdHJhbmdlX3NlbGVjdG9yICA6ICdbZGF0YS13cGJjX3NsaWRlcl9yYW5nZV9yYW5nZV0nLFxyXG5cdFx0XHRcdHdyaXRlcl9zZWxlY3RvciA6ICdbZGF0YS13cGJjX3NsaWRlcl9yYW5nZV93cml0ZXJdJyxcclxuXHJcblx0XHRcdFx0Ly8gRGlzYWJsZWQgYnkgZGVmYXVsdCBmb3IgcGVyZm9ybWFuY2UuXHJcblx0XHRcdFx0ZW5hYmxlX29ic2VydmVyICAgICA6IGZhbHNlLFxyXG5cdFx0XHRcdG9ic2VydmVyX2RlYm91bmNlX21zOiAxNTBcclxuXHRcdFx0fSwgb3B0cyB8fCB7fSk7XHJcblxyXG5cdFx0XHR0aGlzLl9vbl9pbnB1dCAgPSB0aGlzLl9vbl9pbnB1dC5iaW5kKHRoaXMpO1xyXG5cdFx0XHR0aGlzLl9vbl9jaGFuZ2UgPSB0aGlzLl9vbl9jaGFuZ2UuYmluZCh0aGlzKTtcclxuXHJcblx0XHRcdHRoaXMuX29ic2VydmVyICAgID0gbnVsbDtcclxuXHRcdFx0dGhpcy5fcmVmcmVzaF90bXIgPSBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdGluaXQoKSB7XHJcblx0XHRcdGlmICghdGhpcy5yb290KSByZXR1cm4gdGhpcztcclxuXHJcblx0XHRcdHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsICB0aGlzLl9vbl9pbnB1dCwgIHRydWUpO1xyXG5cdFx0XHR0aGlzLnJvb3QuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy5fb25fY2hhbmdlLCB0cnVlKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLm9wdHMuZW5hYmxlX29ic2VydmVyICYmIHcuTXV0YXRpb25PYnNlcnZlcikge1xyXG5cdFx0XHRcdHRoaXMuX29ic2VydmVyID0gbmV3IE11dGF0aW9uT2JzZXJ2ZXIoKCkgPT4geyB0aGlzLl9kZWJvdW5jZWRfcmVmcmVzaCgpOyB9KTtcclxuXHRcdFx0XHR0aGlzLl9vYnNlcnZlci5vYnNlcnZlKHRoaXMucm9vdCA9PT0gZCA/IGQuZG9jdW1lbnRFbGVtZW50IDogdGhpcy5yb290LCB7IGNoaWxkTGlzdDogdHJ1ZSwgc3VidHJlZTogdHJ1ZSB9KTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0dGhpcy5yZWZyZXNoKCk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGRlc3Ryb3koKSB7XHJcblx0XHRcdGlmICghdGhpcy5yb290KSByZXR1cm47XHJcblxyXG5cdFx0XHR0aGlzLnJvb3QucmVtb3ZlRXZlbnRMaXN0ZW5lcignaW5wdXQnLCAgdGhpcy5fb25faW5wdXQsICB0cnVlKTtcclxuXHRcdFx0dGhpcy5yb290LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHRoaXMuX29uX2NoYW5nZSwgdHJ1ZSk7XHJcblxyXG5cdFx0XHRpZiAodGhpcy5fb2JzZXJ2ZXIpIHtcclxuXHRcdFx0XHR0aGlzLl9vYnNlcnZlci5kaXNjb25uZWN0KCk7XHJcblx0XHRcdFx0dGhpcy5fb2JzZXJ2ZXIgPSBudWxsO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAodGhpcy5fcmVmcmVzaF90bXIpIHtcclxuXHRcdFx0XHRjbGVhclRpbWVvdXQodGhpcy5fcmVmcmVzaF90bXIpO1xyXG5cdFx0XHRcdHRoaXMuX3JlZnJlc2hfdG1yID0gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJlZnJlc2goKSB7XHJcblx0XHRcdGlmICghdGhpcy5yb290KSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgc2NvcGUgID0gKHRoaXMucm9vdCA9PT0gZCA/IGQgOiB0aGlzLnJvb3QpO1xyXG5cdFx0XHR2YXIgZ3JvdXBzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoc2NvcGUucXVlcnlTZWxlY3RvckFsbCh0aGlzLm9wdHMuZ3JvdXBfc2VsZWN0b3IpKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwOyBpIDwgZ3JvdXBzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fc3luY19mcm9tX3dyaXRlcihncm91cHNbaV0pO1xyXG5cdFx0XHRcdHRoaXMuX2NsYW1wX3RvX3JhbmdlKGdyb3Vwc1tpXSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblx0XHQvLyBJbnRlcm5hbFxyXG5cdFx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdFx0X2RlYm91bmNlZF9yZWZyZXNoKCkge1xyXG5cdFx0XHRpZiAodGhpcy5fcmVmcmVzaF90bXIpIGNsZWFyVGltZW91dCh0aGlzLl9yZWZyZXNoX3Rtcik7XHJcblx0XHRcdHRoaXMuX3JlZnJlc2hfdG1yID0gc2V0VGltZW91dCgoKSA9PiB7XHJcblx0XHRcdFx0dGhpcy5fcmVmcmVzaF90bXIgPSBudWxsO1xyXG5cdFx0XHRcdHRoaXMucmVmcmVzaCgpO1xyXG5cdFx0XHR9LCBOdW1iZXIodGhpcy5vcHRzLm9ic2VydmVyX2RlYm91bmNlX21zKSB8fCAwKTtcclxuXHRcdH1cclxuXHJcblx0XHRfZmluZF9ncm91cChlbCkge1xyXG5cdFx0XHRyZXR1cm4gKGVsICYmIGVsLmNsb3Nlc3QpID8gZWwuY2xvc2VzdCh0aGlzLm9wdHMuZ3JvdXBfc2VsZWN0b3IpIDogbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRfZ2V0X3BhcnRzKGdyb3VwKSB7XHJcblx0XHRcdGlmICghZ3JvdXApIHJldHVybiBudWxsO1xyXG5cdFx0XHRyZXR1cm4ge1xyXG5cdFx0XHRcdGdyb3VwIDogZ3JvdXAsXHJcblx0XHRcdFx0bnVtICAgOiBncm91cC5xdWVyeVNlbGVjdG9yKHRoaXMub3B0cy52YWx1ZV9zZWxlY3RvciksXHJcblx0XHRcdFx0cmFuZ2UgOiBncm91cC5xdWVyeVNlbGVjdG9yKHRoaXMub3B0cy5yYW5nZV9zZWxlY3RvciksXHJcblx0XHRcdFx0d3JpdGVyOiBncm91cC5xdWVyeVNlbGVjdG9yKHRoaXMub3B0cy53cml0ZXJfc2VsZWN0b3IpXHJcblx0XHRcdH07XHJcblx0XHR9XHJcblxyXG5cdFx0X3dyaXRlKHBhcnRzLCB2YWx1ZSwgZW1pdCkge1xyXG5cdFx0XHRpZiAoIXBhcnRzKSByZXR1cm47XHJcblxyXG5cdFx0XHRpZiAocGFydHMud3JpdGVyKSB7XHJcblx0XHRcdFx0cGFydHMud3JpdGVyLl9fd3BiY19zbGlkZXJfcmFuZ2VfaW50ZXJuYWwgPSB0cnVlO1xyXG5cdFx0XHRcdHBhcnRzLndyaXRlci52YWx1ZSA9IFN0cmluZyh2YWx1ZSk7XHJcblx0XHRcdFx0aWYgKGVtaXQpIGVtaXRfaW5wdXQocGFydHMud3JpdGVyKTtcclxuXHRcdFx0XHRwYXJ0cy53cml0ZXIuX193cGJjX3NsaWRlcl9yYW5nZV9pbnRlcm5hbCA9IGZhbHNlO1xyXG5cdFx0XHR9IGVsc2UgaWYgKHBhcnRzLm51bSkge1xyXG5cdFx0XHRcdC8vIElmIHdyaXRlciBpcyBtaXNzaW5nLCBhdCBsZWFzdCBub3RpZnkgdmlhIG51bWJlciBpbnB1dC5cclxuXHRcdFx0XHRpZiAoZW1pdCkgZW1pdF9pbnB1dChwYXJ0cy5udW0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0X3N5bmNfZnJvbV93cml0ZXIoZ3JvdXApIHtcclxuXHRcdFx0dmFyIHBhcnRzID0gdGhpcy5fZ2V0X3BhcnRzKGdyb3VwKTtcclxuXHRcdFx0aWYgKCFwYXJ0cyB8fCAhcGFydHMud3JpdGVyKSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgcmF3ID0gU3RyaW5nKHBhcnRzLndyaXRlci52YWx1ZSB8fCAnJykudHJpbSgpO1xyXG5cdFx0XHRpZiAoIXJhdykgcmV0dXJuO1xyXG5cclxuXHRcdFx0aWYgKHBhcnRzLm51bSkgICBwYXJ0cy5udW0udmFsdWUgICA9IHJhdztcclxuXHRcdFx0aWYgKHBhcnRzLnJhbmdlKSBwYXJ0cy5yYW5nZS52YWx1ZSA9IHJhdztcclxuXHRcdH1cclxuXHJcblx0XHRfY2xhbXBfdG9fcmFuZ2UoZ3JvdXApIHtcclxuXHRcdFx0dmFyIHBhcnRzID0gdGhpcy5fZ2V0X3BhcnRzKGdyb3VwKTtcclxuXHRcdFx0aWYgKCFwYXJ0cyB8fCAhcGFydHMucmFuZ2UgfHwgIXBhcnRzLm51bSkgcmV0dXJuO1xyXG5cclxuXHRcdFx0dmFyIHYgPSBwYXJzZV9mbG9hdChwYXJ0cy5udW0udmFsdWUpO1xyXG5cdFx0XHRpZiAodiA9PSBudWxsKSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgbWluID0gTnVtYmVyKHBhcnRzLnJhbmdlLm1pbik7XHJcblx0XHRcdHZhciBtYXggPSBOdW1iZXIocGFydHMucmFuZ2UubWF4KTtcclxuXHRcdFx0dmFyIHZ2ICA9IGNsYW1wX251bSh2LCBpc05hTihtaW4pID8gbnVsbCA6IG1pbiwgaXNOYU4obWF4KSA/IG51bGwgOiBtYXgpO1xyXG5cclxuXHRcdFx0aWYgKFN0cmluZyh2dikgIT09IHBhcnRzLm51bS52YWx1ZSkgcGFydHMubnVtLnZhbHVlID0gU3RyaW5nKHZ2KTtcclxuXHRcdFx0cGFydHMucmFuZ2UudmFsdWUgPSBTdHJpbmcodnYpO1xyXG5cdFx0fVxyXG5cclxuXHRcdF9vbl9pbnB1dChldikge1xyXG5cdFx0XHR2YXIgdCA9IGV2LnRhcmdldDtcclxuXHRcdFx0aWYgKCF0KSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgZ3JvdXAgPSB0aGlzLl9maW5kX2dyb3VwKHQpO1xyXG5cdFx0XHRpZiAoIWdyb3VwKSByZXR1cm47XHJcblxyXG5cdFx0XHR2YXIgcGFydHMgPSB0aGlzLl9nZXRfcGFydHMoZ3JvdXApO1xyXG5cdFx0XHRpZiAoIXBhcnRzKSByZXR1cm47XHJcblxyXG5cdFx0XHQvLyBXcml0ZXIgY2hhbmdlZCBleHRlcm5hbGx5IC0+IHVwZGF0ZSBVSS5cclxuXHRcdFx0aWYgKHBhcnRzLndyaXRlciAmJiB0ID09PSBwYXJ0cy53cml0ZXIpIHtcclxuXHRcdFx0XHRpZiAodC5fX3dwYmNfc2xpZGVyX3JhbmdlX2ludGVybmFsKSByZXR1cm47XHJcblx0XHRcdFx0dGhpcy5fc3luY19mcm9tX3dyaXRlcihncm91cCk7XHJcblx0XHRcdFx0dGhpcy5fY2xhbXBfdG9fcmFuZ2UoZ3JvdXApO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gUmFuZ2UgbW92ZWQgLT4gdXBkYXRlIG51bWJlciArIHdyaXRlci5cclxuXHRcdFx0aWYgKHQubWF0Y2hlcyAmJiB0Lm1hdGNoZXModGhpcy5vcHRzLnJhbmdlX3NlbGVjdG9yKSkge1xyXG5cdFx0XHRcdGlmIChwYXJ0cy5udW0pIHBhcnRzLm51bS52YWx1ZSA9IHQudmFsdWU7XHJcblx0XHRcdFx0dGhpcy5fd3JpdGUocGFydHMsIHQudmFsdWUsIC8qZW1pdCovIHRydWUpO1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gTnVtYmVyIHR5cGVkIC0+IHVwZGF0ZSByYW5nZSArIHdyaXRlciAoY2xhbXAgYnkgc2xpZGVyIGJvdW5kcykuXHJcblx0XHRcdGlmICh0Lm1hdGNoZXMgJiYgdC5tYXRjaGVzKHRoaXMub3B0cy52YWx1ZV9zZWxlY3RvcikpIHtcclxuXHRcdFx0XHRpZiAocGFydHMucmFuZ2UpIHtcclxuXHRcdFx0XHRcdHZhciB2ID0gcGFyc2VfZmxvYXQodC52YWx1ZSk7XHJcblx0XHRcdFx0XHRpZiAodiAhPSBudWxsKSB7XHJcblx0XHRcdFx0XHRcdHZhciBtaW4gPSBOdW1iZXIocGFydHMucmFuZ2UubWluKTtcclxuXHRcdFx0XHRcdFx0dmFyIG1heCA9IE51bWJlcihwYXJ0cy5yYW5nZS5tYXgpO1xyXG5cdFx0XHRcdFx0XHR2ID0gY2xhbXBfbnVtKHYsIGlzTmFOKG1pbikgPyBudWxsIDogbWluLCBpc05hTihtYXgpID8gbnVsbCA6IG1heCk7XHJcblxyXG5cdFx0XHRcdFx0XHRwYXJ0cy5yYW5nZS52YWx1ZSA9IFN0cmluZyh2KTtcclxuXHRcdFx0XHRcdFx0aWYgKFN0cmluZyh2KSAhPT0gdC52YWx1ZSkgdC52YWx1ZSA9IFN0cmluZyh2KTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0dGhpcy5fd3JpdGUocGFydHMsIHQudmFsdWUsIC8qZW1pdCovIHRydWUpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0X29uX2NoYW5nZShldikge1xyXG5cdFx0XHQvLyBObyBzcGVjaWFsIFwiY2hhbmdlXCIgaGFuZGxpbmcgbmVlZGVkIGN1cnJlbnRseTsga2VwdCBmb3Igc3ltbWV0cnkvZnV0dXJlLlxyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cdC8vIEF1dG8taW5pdFxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHRmdW5jdGlvbiB3cGJjX3NsaWRlcl9yYW5nZV9ncm91cHNfX2F1dG9faW5pdCgpIHtcclxuXHRcdHZhciBST09UICA9ICcud3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzJztcclxuXHRcdHZhciBub2RlcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGQucXVlcnlTZWxlY3RvckFsbChST09UKSlcclxuXHRcdFx0LmZpbHRlcihmdW5jdGlvbiAobikgeyByZXR1cm4gIW4ucGFyZW50RWxlbWVudCB8fCAhbi5wYXJlbnRFbGVtZW50LmNsb3Nlc3QoUk9PVCk7IH0pO1xyXG5cclxuXHRcdGlmICghbm9kZXMubGVuZ3RoKSB7XHJcblx0XHRcdGlmICghZC5fX3dwYmNfc2xpZGVyX3JhbmdlX2dyb3Vwc19nbG9iYWxfaW5zdGFuY2UpIHtcclxuXHRcdFx0XHRkLl9fd3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzX2dsb2JhbF9pbnN0YW5jZSA9IG5ldyBXUEJDX1NsaWRlcl9SYW5nZV9Hcm91cHMoZCkuaW5pdCgpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRub2Rlcy5mb3JFYWNoKGZ1bmN0aW9uIChub2RlKSB7XHJcblx0XHRcdGlmIChub2RlLl9fd3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzX2luc3RhbmNlKSByZXR1cm47XHJcblx0XHRcdG5vZGUuX193cGJjX3NsaWRlcl9yYW5nZV9ncm91cHNfaW5zdGFuY2UgPSBuZXcgV1BCQ19TbGlkZXJfUmFuZ2VfR3JvdXBzKG5vZGUpLmluaXQoKTtcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0Ly8gRXhwb3J0IGdsb2JhbHMuXHJcblx0dy5XUEJDX1NsaWRlcl9SYW5nZV9Hcm91cHMgICA9IFdQQkNfU2xpZGVyX1JhbmdlX0dyb3VwcztcclxuXHR3LldQQkNfU2xpZGVyX1JhbmdlX0F1dG9Jbml0ID0gd3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzX19hdXRvX2luaXQ7XHJcblxyXG5cdGlmIChkLnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJykge1xyXG5cdFx0ZC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgd3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzX19hdXRvX2luaXQsIHsgb25jZTogdHJ1ZSB9KTtcclxuXHR9IGVsc2Uge1xyXG5cdFx0d3BiY19zbGlkZXJfcmFuZ2VfZ3JvdXBzX19hdXRvX2luaXQoKTtcclxuXHR9XHJcblxyXG59KSh3aW5kb3csIGRvY3VtZW50KTtcclxuIiwiLyoqXHJcbiAqIEJvb2tpbmcgQ2FsZW5kYXIg4oCUIEdlbmVyaWMgVUkgVGFicyBVdGlsaXR5IChKUylcclxuICpcclxuICogUHVycG9zZTogTGlnaHR3ZWlnaHQsIGRlcGVuZGVuY3ktZnJlZSB0YWJzIGNvbnRyb2xsZXIgZm9yIGFueSBzbWFsbCB0YWIgZ3JvdXAgaW4gYWRtaW4gVUlzLlxyXG4gKiAtIEF1dG8taW5pdGlhbGl6ZXMgZ3JvdXBzIG1hcmtlZCB3aXRoIGRhdGEtd3BiYy10YWJzLlxyXG4gKiAtIEFzc2lnbnMgQVJJQSByb2xlcyBhbmQgdG9nZ2xlcyBhcmlhLXNlbGVjdGVkL2FyaWEtaGlkZGVuL3RhYmluZGV4LlxyXG4gKiAtIFN1cHBvcnRzIGtleWJvYXJkIG5hdmlnYXRpb24gKExlZnQvUmlnaHQvSG9tZS9FbmQpLlxyXG4gKiAtIFB1YmxpYyBBUEk6IHdpbmRvdy53cGJjX3VpX3RhYnMue2luaXRfb24sIGluaXRfZ3JvdXAsIHNldF9hY3RpdmV9XHJcbiAqIC0gRW1pdHMgJ3dwYmM6dGFiczpjaGFuZ2UnIG9uIHRoZSBncm91cCByb290IHdoZW4gdGhlIGFjdGl2ZSB0YWIgY2hhbmdlcy5cclxuICpcclxuICogTWFya3VwIGNvbnRyYWN0OlxyXG4gKiAtIFJvb3Q6ICAgW2RhdGEtd3BiYy10YWJzXVxyXG4gKiAtIFRhYnM6ICAgW2RhdGEtd3BiYy10YWIta2V5PVwiS1wiXVxyXG4gKiAtIFBhbmVsczogW2RhdGEtd3BiYy10YWItcGFuZWw9XCJLXCJdXHJcbiAqXHJcbiAqIEBwYWNrYWdlICAgQm9va2luZyBDYWxlbmRhclxyXG4gKiBAc3VicGFja2FnZSBBZG1pblxcVUlcclxuICogQHNpbmNlICAgICAxMS4wLjBcclxuICogQHZlcnNpb24gICAxLjAuMFxyXG4gKiBAc2VlICAgICAgIC9pbmNsdWRlcy9fX2pzL2FkbWluL3VpX3RhYnMvdWlfdGFicy5qc1xyXG4gKlxyXG4gKlxyXG4gKiBIb3cgaXQgd29ya3M6XHJcbiAqIC0gUm9vdCBub2RlIG11c3QgaGF2ZSBbZGF0YS13cGJjLXRhYnNdIGF0dHJpYnV0ZSAoYW55IHZhbHVlKS5cclxuICogLSBUYWIgYnV0dG9ucyBtdXN0IGNhcnJ5IFtkYXRhLXdwYmMtdGFiLWtleT1cIi4uLlwiXSAodW5pcXVlIHBlciBncm91cCkuXHJcbiAqIC0gUGFuZWxzIG11c3QgY2FycnkgW2RhdGEtd3BiYy10YWItcGFuZWw9XCIuLi5cIl0gd2l0aCBtYXRjaGluZyBrZXlzLlxyXG4gKiAtIEFkZHMgV0FJLUFSSUEgcm9sZXMgYW5kIGFyaWEtc2VsZWN0ZWQvaGlkZGVuIHdpcmluZy5cclxuICpcclxuICogPGRpdiBkYXRhLXdwYmMtdGFicz1cImNvbHVtbi1zdHlsZXNcIiBkYXRhLXdwYmMtdGFiLWFjdGl2ZT1cIjFcIiAgICBjbGFzcz1cIndwYmNfdWlfdGFic19yb290XCIgPlxyXG4gKiAgICA8IS0tIFRvcCBUYWJzIC0tPlxyXG4gKiAgICA8ZGl2IGRhdGEtd3BiYy10YWJsaXN0PVwiXCIgcm9sZT1cInRhYmxpc3RcIiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCIgd3BiY191aV9lbF9faG9yaXNfdG9wX2Jhcl9fd3JhcHBlclwiID5cclxuICogICAgICAgIDxkaXYgY2xhc3M9XCJ3cGJjX3VpX2VsX19ob3Jpc190b3BfYmFyX19jb250ZW50XCI+XHJcbiAqICAgICAgICAgICAgPGgyIGNsYXNzPVwid3BiY191aV9lbF9faG9yaXNfbmF2X2xhYmVsXCI+Q29sdW1uOjwvaDI+XHJcbiAqXHJcbiAqICAgICAgICAgICAgPGRpdiBjbGFzcz1cIndwYmNfdWlfZWxfX2hvcmlzX25hdl9pdGVtIHdwYmNfdWlfZWxfX2hvcmlzX25hdl9pdGVtX18xXCI+XHJcbiAqICAgICAgICAgICAgICAgIDxhXHJcbiAqICAgICAgICAgICAgICAgICAgICBkYXRhLXdwYmMtdGFiLWtleT1cIjFcIlxyXG4gKiAgICAgICAgICAgICAgICAgICAgYXJpYS1zZWxlY3RlZD1cInRydWVcIiByb2xlPVwidGFiXCIgdGFiaW5kZXg9XCIwXCIgYXJpYS1jb250cm9scz1cIndwYmNfdGFiX3BhbmVsX2NvbF8xXCJcclxuICpcclxuICogICAgICAgICAgICAgICAgICAgICAgICBocmVmPVwiamF2YXNjcmlwdDp2b2lkKDApO1wiXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJ3cGJjX3VpX2VsX19ob3Jpc19uYXZfaXRlbV9fYSB3cGJjX3VpX2VsX19ob3Jpc19uYXZfaXRlbV9fc2luZ2xlXCJcclxuICogICAgICAgICAgICAgICAgICAgICAgICBpZD1cIndwYmNfdGFiX2NvbF8xXCJcclxuICogICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1cIkNvbHVtbiAxXCJcclxuICogICAgICAgICAgICAgICAgPjxzcGFuIGNsYXNzPVwid3BiY191aV9lbF9faG9yaXNfbmF2X3RpdGxlXCI+VGl0bGUgMTwvc3Bhbj48L2E+XHJcbiAqICAgICAgICAgICAgPC9kaXY+XHJcbiAqICAgICAgICAgICAgLi4uXHJcbiAqICAgICAgICA8L2Rpdj5cclxuICogICAgPC9kaXY+XHJcbiAqICAgIDwhLS0gVGFicyBDb250ZW50IC0tPlxyXG4gKiAgICA8ZGl2IGNsYXNzPVwid3BiY190YWJfX3BhbmVsIGdyb3VwX19maWVsZHNcIiBkYXRhLXdwYmMtdGFiLXBhbmVsPVwiMVwiIGlkPVwid3BiY190YWJfcGFuZWxfY29sXzFcIiByb2xlPVwidGFicGFuZWxcIiBhcmlhLWxhYmVsbGVkYnk9XCJ3cGJjX3RhYl9jb2xfMVwiPlxyXG4gKiAgICAgICAgLi4uXHJcbiAqICAgIDwvZGl2PlxyXG4gKiAgICAuLi5cclxuICogPC9kaXY+XHJcbiAqXHJcbiAqIFB1YmxpYyBBUEk6XHJcbiAqICAgLSB3cGJjX3VpX3RhYnMuaW5pdF9vbihyb290X29yX3NlbGVjdG9yKSAgIC8vIGZpbmQgYW5kIGluaXQgZ3JvdXBzIHdpdGhpbiBhIGNvbnRhaW5lclxyXG4gKiAgIC0gd3BiY191aV90YWJzLmluaXRfZ3JvdXAocm9vdF9lbCkgICAgICAgICAvLyBpbml0IGEgc2luZ2xlIGdyb3VwIHJvb3RcclxuICogICAtIHdwYmNfdWlfdGFicy5zZXRfYWN0aXZlKHJvb3RfZWwsIGtleSkgICAgLy8gcHJvZ3JhbW1hdGljYWxseSBjaGFuZ2UgYWN0aXZlIHRhYlxyXG4gKlxyXG4gKiBFdmVudHM6XHJcbiAqICAgLSBEaXNwYXRjaGVzIEN1c3RvbUV2ZW50ICd3cGJjOnRhYnM6Y2hhbmdlJyBvbiByb290IHdoZW4gdGFiIGNoYW5nZXM6XHJcbiAqICAgICAgIGRldGFpbDogeyBhY3RpdmVfa2V5OiAnMicsIHByZXZfa2V5OiAnMScgfVxyXG4gKlxyXG4gKiBTd2l0Y2ggYSBsb2NhbCAoZ2VuZXJpYykgdGFicyBncm91cCB0byB0YWIgMzogICAgIHZhciBncm91cCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ1tkYXRhLXdwYmMtdGFicz1cImNvbHVtbi1zdHlsZXNcIl0nKTsgaWYgKCBncm91cCApIHsgd3BiY191aV90YWJzLnNldF9hY3RpdmUoZ3JvdXAsICczJyk7IH1cclxuICovXHJcbihmdW5jdGlvbiAoIHcgKSB7XHJcblx0J3VzZSBzdHJpY3QnO1xyXG5cclxuXHRpZiAoIHcud3BiY191aV90YWJzICkge1xyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSW50ZXJuYWw6IHRvZ2dsZSBhY3RpdmUgc3RhdGUuXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSByb290X2VsXHJcblx0ICogQHBhcmFtIHtzdHJpbmd9ICAgICAga2V5XHJcblx0ICogQHBhcmFtIHtib29sZWFufSAgICAgc2hvdWxkX2VtaXRcclxuXHQgKi9cclxuXHRmdW5jdGlvbiBzZXRfYWN0aXZlX2ludGVybmFsKCByb290X2VsLCBrZXksIHNob3VsZF9lbWl0ICkge1xyXG5cdFx0dmFyIHRhYl9idG5zID0gcm9vdF9lbC5xdWVyeVNlbGVjdG9yQWxsKCAnW2RhdGEtd3BiYy10YWIta2V5XScgKTtcclxuXHRcdHZhciBwYW5lbHMgICA9IHJvb3RfZWwucXVlcnlTZWxlY3RvckFsbCggJ1tkYXRhLXdwYmMtdGFiLXBhbmVsXScgKTtcclxuXHJcblx0XHR2YXIgcHJldl9rZXkgPSByb290X2VsLmdldEF0dHJpYnV0ZSggJ2RhdGEtd3BiYy10YWItYWN0aXZlJyApIHx8IG51bGw7XHJcblx0XHRpZiAoIFN0cmluZyggcHJldl9rZXkgKSA9PT0gU3RyaW5nKCBrZXkgKSApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEJ1dHRvbnM6IGFyaWEgKyBjbGFzc1xyXG5cdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgdGFiX2J0bnMubGVuZ3RoOyBpKysgKSB7XHJcblx0XHRcdHZhciBidG4gICA9IHRhYl9idG5zW2ldO1xyXG5cdFx0XHR2YXIgYl9rZXkgPSBidG4uZ2V0QXR0cmlidXRlKCAnZGF0YS13cGJjLXRhYi1rZXknICk7XHJcblx0XHRcdHZhciBpc19vbiA9IFN0cmluZyggYl9rZXkgKSA9PT0gU3RyaW5nKCBrZXkgKTtcclxuXHJcblx0XHRcdGJ0bi5zZXRBdHRyaWJ1dGUoICdyb2xlJywgJ3RhYicgKTtcclxuXHRcdFx0YnRuLnNldEF0dHJpYnV0ZSggJ2FyaWEtc2VsZWN0ZWQnLCBpc19vbiA/ICd0cnVlJyA6ICdmYWxzZScgKTtcclxuXHRcdFx0YnRuLnNldEF0dHJpYnV0ZSggJ3RhYmluZGV4JywgaXNfb24gPyAnMCcgOiAnLTEnICk7XHJcblxyXG5cdFx0XHRpZiAoIGlzX29uICkge1xyXG5cdFx0XHRcdGJ0bi5jbGFzc0xpc3QuYWRkKCAnYWN0aXZlJyApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGJ0bi5jbGFzc0xpc3QucmVtb3ZlKCAnYWN0aXZlJyApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gUGFuZWxzOiBhcmlhICsgdmlzaWJpbGl0eVxyXG5cdFx0Zm9yICggdmFyIGogPSAwOyBqIDwgcGFuZWxzLmxlbmd0aDsgaisrICkge1xyXG5cdFx0XHR2YXIgcG4gICA9IHBhbmVsc1tqXTtcclxuXHRcdFx0dmFyIHBrZXkgPSBwbi5nZXRBdHRyaWJ1dGUoICdkYXRhLXdwYmMtdGFiLXBhbmVsJyApO1xyXG5cdFx0XHR2YXIgc2hvdyA9IFN0cmluZyggcGtleSApID09PSBTdHJpbmcoIGtleSApO1xyXG5cclxuXHRcdFx0cG4uc2V0QXR0cmlidXRlKCAncm9sZScsICd0YWJwYW5lbCcgKTtcclxuXHRcdFx0cG4uc2V0QXR0cmlidXRlKCAnYXJpYS1oaWRkZW4nLCBzaG93ID8gJ2ZhbHNlJyA6ICd0cnVlJyApO1xyXG5cdFx0XHRpZiAoIHNob3cgKSB7XHJcblx0XHRcdFx0cG4ucmVtb3ZlQXR0cmlidXRlKCAnaGlkZGVuJyApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHBuLnNldEF0dHJpYnV0ZSggJ2hpZGRlbicsICcnICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyb290X2VsLnNldEF0dHJpYnV0ZSggJ2RhdGEtd3BiYy10YWItYWN0aXZlJywgU3RyaW5nKCBrZXkgKSApO1xyXG5cclxuXHRcdGlmICggc2hvdWxkX2VtaXQgKSB7XHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0dmFyIGV2ID0gbmV3IHcuQ3VzdG9tRXZlbnQoICd3cGJjOnRhYnM6Y2hhbmdlJywge1xyXG5cdFx0XHRcdFx0YnViYmxlcyA6IHRydWUsXHJcblx0XHRcdFx0XHRkZXRhaWwgIDogeyBhY3RpdmVfa2V5IDogU3RyaW5nKCBrZXkgKSwgcHJldl9rZXkgOiBwcmV2X2tleSB9XHJcblx0XHRcdFx0fSApO1xyXG5cdFx0XHRcdHJvb3RfZWwuZGlzcGF0Y2hFdmVudCggZXYgKTtcclxuXHRcdFx0fSBjYXRjaCAoIF9lICkge31cclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIEludGVybmFsOiBnZXQgb3JkZXJlZCBrZXlzIGZyb20gYnV0dG9ucy5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvb3RfZWxcclxuXHQgKiBAcmV0dXJucyB7c3RyaW5nW119XHJcblx0ICovXHJcblx0ZnVuY3Rpb24gZ2V0X2tleXMoIHJvb3RfZWwgKSB7XHJcblx0XHR2YXIgbGlzdCA9IFtdO1xyXG5cdFx0dmFyIGJ0bnMgPSByb290X2VsLnF1ZXJ5U2VsZWN0b3JBbGwoICdbZGF0YS13cGJjLXRhYi1rZXldJyApO1xyXG5cdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgYnRucy5sZW5ndGg7IGkrKyApIHtcclxuXHRcdFx0dmFyIGsgPSBidG5zW2ldLmdldEF0dHJpYnV0ZSggJ2RhdGEtd3BiYy10YWIta2V5JyApO1xyXG5cdFx0XHRpZiAoIGsgIT0gbnVsbCAmJiBrICE9PSAnJyApIHtcclxuXHRcdFx0XHRsaXN0LnB1c2goIFN0cmluZyggayApICk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdHJldHVybiBsaXN0O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSW50ZXJuYWw6IG1vdmUgZm9jdXMgYmV0d2VlbiB0YWJzIHVzaW5nIGtleWJvYXJkLlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcm9vdF9lbFxyXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSAgICAgIGRpciAgKzEgKG5leHQpIC8gLTEgKHByZXYpXHJcblx0ICovXHJcblx0ZnVuY3Rpb24gZm9jdXNfcmVsYXRpdmUoIHJvb3RfZWwsIGRpciApIHtcclxuXHRcdHZhciBrZXlzICAgID0gZ2V0X2tleXMoIHJvb3RfZWwgKTtcclxuXHRcdHZhciBjdXJyZW50ID0gcm9vdF9lbC5nZXRBdHRyaWJ1dGUoICdkYXRhLXdwYmMtdGFiLWFjdGl2ZScgKSB8fCBrZXlzWzBdIHx8IG51bGw7XHJcblx0XHR2YXIgaWR4ICAgICA9IE1hdGgubWF4KCAwLCBrZXlzLmluZGV4T2YoIFN0cmluZyggY3VycmVudCApICkgKTtcclxuXHRcdHZhciBuZXh0ICAgID0ga2V5c1sgKCBpZHggKyAoIGRpciA+IDAgPyAxIDoga2V5cy5sZW5ndGggLSAxICkgKSAlIGtleXMubGVuZ3RoIF07XHJcblxyXG5cdFx0dmFyIG5leHRfYnRuID0gcm9vdF9lbC5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtd3BiYy10YWIta2V5PVwiJyArIG5leHQgKyAnXCJdJyApO1xyXG5cdFx0aWYgKCBuZXh0X2J0biApIHtcclxuXHRcdFx0bmV4dF9idG4uZm9jdXMoKTtcclxuXHRcdFx0c2V0X2FjdGl2ZV9pbnRlcm5hbCggcm9vdF9lbCwgbmV4dCwgdHJ1ZSApO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSW5pdGlhbGl6ZSBhIHNpbmdsZSB0YWJzIGdyb3VwIHJvb3QuXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSByb290X2VsXHJcblx0ICovXHJcblx0ZnVuY3Rpb24gaW5pdF9ncm91cCggcm9vdF9lbCApIHtcclxuXHRcdGlmICggISByb290X2VsIHx8IHJvb3RfZWwuX193cGJjX3RhYnNfaW5pdGVkICkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHRyb290X2VsLl9fd3BiY190YWJzX2luaXRlZCA9IHRydWU7XHJcblxyXG5cdFx0Ly8gUm9sZXNcclxuXHRcdHZhciB0YWJsaXN0ID0gcm9vdF9lbC5xdWVyeVNlbGVjdG9yKCAnW2RhdGEtd3BiYy10YWJsaXN0XScgKSB8fCByb290X2VsO1xyXG5cdFx0dGFibGlzdC5zZXRBdHRyaWJ1dGUoICdyb2xlJywgJ3RhYmxpc3QnICk7XHJcblxyXG5cdFx0Ly8gRGVmYXVsdCBhY3RpdmU6IGZyb20gYXR0cmlidXRlIG9yIGZpcnN0IGJ1dHRvblxyXG5cdFx0dmFyIGtleXMgPSBnZXRfa2V5cyggcm9vdF9lbCApO1xyXG5cdFx0dmFyIGRlZiAgPSByb290X2VsLmdldEF0dHJpYnV0ZSggJ2RhdGEtd3BiYy10YWItYWN0aXZlJyApIHx8ICgga2V5c1swXSB8fCAnMScgKTtcclxuXHRcdHNldF9hY3RpdmVfaW50ZXJuYWwoIHJvb3RfZWwsIGRlZiwgZmFsc2UgKTtcclxuXHJcblx0XHQvLyBDbGlja3NcclxuXHRcdHJvb3RfZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24gKCBlICkge1xyXG5cdFx0XHR2YXIgYnRuID0gZS50YXJnZXQuY2xvc2VzdCA/IGUudGFyZ2V0LmNsb3Nlc3QoICdbZGF0YS13cGJjLXRhYi1rZXldJyApIDogbnVsbDtcclxuXHRcdFx0aWYgKCAhIGJ0biB8fCAhIHJvb3RfZWwuY29udGFpbnMoIGJ0biApICkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XHJcblx0XHRcdHZhciBrZXkgPSBidG4uZ2V0QXR0cmlidXRlKCAnZGF0YS13cGJjLXRhYi1rZXknICk7XHJcblx0XHRcdGlmICgga2V5ICE9IG51bGwgKSB7XHJcblx0XHRcdFx0c2V0X2FjdGl2ZV9pbnRlcm5hbCggcm9vdF9lbCwga2V5LCB0cnVlICk7XHJcblx0XHRcdH1cclxuXHRcdH0sIHRydWUgKTtcclxuXHJcblx0XHQvLyBLZXlib2FyZCAoTGVmdC9SaWdodC9Ib21lL0VuZClcclxuXHRcdHJvb3RfZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2tleWRvd24nLCBmdW5jdGlvbiAoIGUgKSB7XHJcblx0XHRcdHZhciB0Z3QgPSBlLnRhcmdldDtcclxuXHRcdFx0aWYgKCAhIHRndCB8fCAhIHRndC5oYXNBdHRyaWJ1dGUgfHwgISB0Z3QuaGFzQXR0cmlidXRlKCAnZGF0YS13cGJjLXRhYi1rZXknICkgKSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdHN3aXRjaCAoIGUua2V5ICkge1xyXG5cdFx0XHRjYXNlICdBcnJvd0xlZnQnOlxyXG5cdFx0XHRcdGUucHJldmVudERlZmF1bHQoKTsgZm9jdXNfcmVsYXRpdmUoIHJvb3RfZWwsIC0xICk7IGJyZWFrO1xyXG5cdFx0XHRjYXNlICdBcnJvd1JpZ2h0JzpcclxuXHRcdFx0XHRlLnByZXZlbnREZWZhdWx0KCk7IGZvY3VzX3JlbGF0aXZlKCByb290X2VsLCArMSApOyBicmVhaztcclxuXHRcdFx0Y2FzZSAnSG9tZSc6XHJcblx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpOyBzZXRfYWN0aXZlX2ludGVybmFsKCByb290X2VsLCAoIGdldF9rZXlzKCByb290X2VsIClbMF0gfHwgJzEnICksIHRydWUgKTsgYnJlYWs7XHJcblx0XHRcdGNhc2UgJ0VuZCc6XHJcblx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpOyB2YXIga3MgPSBnZXRfa2V5cyggcm9vdF9lbCApOyBzZXRfYWN0aXZlX2ludGVybmFsKCByb290X2VsLCAoIGtzWyBrcy5sZW5ndGggLSAxIF0gfHwgJzEnICksIHRydWUgKTsgYnJlYWs7XHJcblx0XHRcdH1cclxuXHRcdH0sIHRydWUgKTtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIEluaXRpYWxpemUgYWxsIGdyb3VwcyB3aXRoaW4gYSBjb250YWluZXIgKG9yIGRvY3VtZW50KS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR8c3RyaW5nfG51bGx9IGNvbnRhaW5lclxyXG5cdCAqL1xyXG5cdGZ1bmN0aW9uIGluaXRfb24oIGNvbnRhaW5lciApIHtcclxuXHRcdHZhciBjdHggPSBjb250YWluZXIgPyAoIHR5cGVvZiBjb250YWluZXIgPT09ICdzdHJpbmcnID8gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggY29udGFpbmVyICkgOiBjb250YWluZXIgKSA6IGRvY3VtZW50O1xyXG5cdFx0aWYgKCAhIGN0eCApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0dmFyIGdyb3VwcyA9IGN0eC5xdWVyeVNlbGVjdG9yQWxsKCAnW2RhdGEtd3BiYy10YWJzXScgKTtcclxuXHRcdGZvciAoIHZhciBpID0gMDsgaSA8IGdyb3Vwcy5sZW5ndGg7IGkrKyApIHtcclxuXHRcdFx0aW5pdF9ncm91cCggZ3JvdXBzW2ldICk7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBQcm9ncmFtbWF0aWNhbGx5IHNldCBhY3RpdmUgdGFiIGJ5IGtleS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHJvb3RfZWxcclxuXHQgKiBAcGFyYW0ge3N0cmluZ3xudW1iZXJ9IGtleVxyXG5cdCAqL1xyXG5cdGZ1bmN0aW9uIHNldF9hY3RpdmUoIHJvb3RfZWwsIGtleSApIHtcclxuXHRcdGlmICggcm9vdF9lbCAmJiByb290X2VsLmhhc0F0dHJpYnV0ZSAmJiByb290X2VsLmhhc0F0dHJpYnV0ZSggJ2RhdGEtd3BiYy10YWJzJyApICkge1xyXG5cdFx0XHRzZXRfYWN0aXZlX2ludGVybmFsKCByb290X2VsLCBTdHJpbmcoIGtleSApLCB0cnVlICk7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvLyBQdWJsaWMgQVBJIChzbmFrZV9jYXNlKVxyXG5cdHcud3BiY191aV90YWJzID0ge1xyXG5cdFx0aW5pdF9vbiAgICA6IGluaXRfb24sXHJcblx0XHRpbml0X2dyb3VwIDogaW5pdF9ncm91cCxcclxuXHRcdHNldF9hY3RpdmUgOiBzZXRfYWN0aXZlXHJcblx0fTtcclxuXHJcblx0Ly8gQXV0by1pbml0IG9uIERPTSByZWFkeVxyXG5cdGlmICggZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnICkge1xyXG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ0RPTUNvbnRlbnRMb2FkZWQnLCBmdW5jdGlvbiAoKSB7IGluaXRfb24oIGRvY3VtZW50ICk7IH0gKTtcclxuXHR9IGVsc2Uge1xyXG5cdFx0aW5pdF9vbiggZG9jdW1lbnQgKTtcclxuXHR9XHJcblxyXG59KSggd2luZG93ICk7XHJcbiJdfQ==
2339