PluginProbe
Booking Calendar / 11.8
Booking Calendar v11.8
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 10.11.2 All 203 releases
booking / _dist / all / _src / wpbc_all_admin.js

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

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