rubyfox-server 2.17.3.1 → 2.19.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rubyfox/server/data/config/admin/descriptors/config_room.txt +10 -1
  3. data/lib/rubyfox/server/data/config/admin/descriptors/config_server.txt +90 -20
  4. data/lib/rubyfox/server/data/config/admin/descriptors/config_zone.txt +9 -0
  5. data/lib/rubyfox/server/data/config/admin/descriptors/runtime_room.txt +11 -0
  6. data/lib/rubyfox/server/data/config/admin/descriptors/runtime_user.txt +3 -3
  7. data/lib/rubyfox/server/data/config/core.xml +4 -4
  8. data/lib/rubyfox/server/data/config/default.words.txt +11 -0
  9. data/lib/rubyfox/server/data/config/log4j.properties +1 -2
  10. data/lib/rubyfox/server/data/config/server.xml +1 -1
  11. data/lib/rubyfox/server/data/data/GeoLite2-Country.mmdb +0 -0
  12. data/lib/rubyfox/server/data/data/bannedusers/users.bin +0 -0
  13. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/bootstrap.jar +0 -0
  14. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/catalina-tasks.xml +39 -39
  15. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/catalina.sh +0 -0
  16. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/ciphers.sh +0 -0
  17. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/commons-daemon-native.tar.gz +0 -0
  18. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/commons-daemon.jar +0 -0
  19. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/configtest.sh +0 -0
  20. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/daemon.sh +0 -0
  21. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/digest.sh +0 -0
  22. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/makebase.sh +0 -0
  23. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/setclasspath.sh +0 -0
  24. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/shutdown.sh +0 -0
  25. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/startup.sh +0 -0
  26. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/tomcat-juli.jar +0 -0
  27. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/tomcat-native.tar.gz +0 -0
  28. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/tool-wrapper.sh +0 -0
  29. data/lib/rubyfox/server/data/lib/apache-tomcat/bin/version.sh +0 -0
  30. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/Catalina/localhost/rewrite.config +1 -1
  31. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/catalina.policy +263 -264
  32. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/catalina.properties +209 -207
  33. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/context.xml +31 -31
  34. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/jaspic-providers.xml +23 -23
  35. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/jaspic-providers.xsd +52 -52
  36. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/keystore.jks +0 -0
  37. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/server.xml +177 -161
  38. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/tomcat-users.xml +18 -7
  39. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/tomcat-users.xsd +59 -59
  40. data/lib/rubyfox/server/data/lib/apache-tomcat/conf/web.xml +4740 -4737
  41. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/annotations-api.jar +0 -0
  42. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina-ant.jar +0 -0
  43. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina-ha.jar +0 -0
  44. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina-ssi.jar +0 -0
  45. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina-storeconfig.jar +0 -0
  46. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina-tribes.jar +0 -0
  47. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/catalina.jar +0 -0
  48. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/el-api.jar +0 -0
  49. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/jasper-el.jar +0 -0
  50. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/jasper.jar +0 -0
  51. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/jaspic-api.jar +0 -0
  52. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/jsp-api.jar +0 -0
  53. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/servlet-api.jar +0 -0
  54. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/sfs2x-ws-helper.jar +0 -0
  55. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-api.jar +0 -0
  56. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-coyote.jar +0 -0
  57. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-dbcp.jar +0 -0
  58. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-cs.jar +0 -0
  59. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-de.jar +0 -0
  60. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-es.jar +0 -0
  61. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-fr.jar +0 -0
  62. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-ja.jar +0 -0
  63. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-ko.jar +0 -0
  64. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-pt-BR.jar +0 -0
  65. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-ru.jar +0 -0
  66. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-i18n-zh-CN.jar +0 -0
  67. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-jdbc.jar +0 -0
  68. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-jni.jar +0 -0
  69. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-util-scan.jar +0 -0
  70. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-util.jar +0 -0
  71. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/tomcat-websocket.jar +0 -0
  72. data/lib/rubyfox/server/data/lib/apache-tomcat/lib/websocket-api.jar +0 -0
  73. data/lib/rubyfox/server/data/lib/javax.activation-1.2.0.jar +0 -0
  74. data/lib/rubyfox/server/data/lib/javax.mail.jar +0 -0
  75. data/lib/rubyfox/server/data/lib/js/JSApi.js +2 -1
  76. data/lib/rubyfox/server/data/lib/js/LibApi.js +181 -48
  77. data/lib/rubyfox/server/data/lib/sfs2x-admin.jar +0 -0
  78. data/lib/rubyfox/server/data/lib/sfs2x-cluster.jar +0 -0
  79. data/lib/rubyfox/server/data/lib/sfs2x-core.jar +0 -0
  80. data/lib/rubyfox/server/data/lib/sfs2x.jar +0 -0
  81. data/lib/rubyfox/server/data/sfs2x-service +26 -30
  82. data/lib/rubyfox/server/data/www/BlueBox.war +0 -0
  83. data/lib/rubyfox/server/data/www/HelloServlet/WEB-INF/web.xml +1 -3
  84. data/lib/rubyfox/server/data/www/ROOT/_css_/default.css +14 -6
  85. data/lib/rubyfox/server/data/www/ROOT/admin/assets/css/style.css +44 -2
  86. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/application.bundle.js +98 -61
  87. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/endors~mod-0~mod-1~mod-11~mod-12~mod-17~mod-6~mod-7~mod-8~mod-9.bundle.js +17357 -0
  88. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-0.bundle.js +4 -4
  89. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-1.bundle.js +3 -3
  90. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-10.bundle.js +101 -66
  91. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-11.bundle.js +544 -8
  92. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-12.bundle.js +915 -1480
  93. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-12~module-15~module-16~module-4.bundle.js +2665 -0
  94. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-13.bundle.js +606 -3093
  95. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-13~module-16~module-17~module-4.bundle.js +2665 -0
  96. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-14.bundle.js +764 -0
  97. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-15.bundle.js +71 -0
  98. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-16.bundle.js +1787 -0
  99. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-17.bundle.js +3383 -0
  100. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-4.bundle.js +121 -1009
  101. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-5.bundle.js +1214 -1744
  102. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-6.bundle.js +398 -666
  103. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-7.bundle.js +717 -192
  104. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-8.bundle.js +2117 -665
  105. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-9.bundle.js +613 -690
  106. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/vendors~mod-0~mod-1~mod-10~mod-11~mod-16~mod-5~mod-6~mod-7~mod-8.bundle.js +17357 -0
  107. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/vendors~mod-0~mod-1~mod-11~mod-12~mod-17~mod-5~mod-6~mod-7~mod-8~mod-9.bundle.js +17357 -0
  108. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/{vendors~module-0~module-1~module-13~module-4~module-5~module-7~module-8.bundle.js → vendors~mod-0~mod-1~mod-11~mod-12~mod-17~mod-5~mod-7~mod-8~mod-9.bundle.js} +2 -2
  109. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/vendors~module-12.bundle.js +807 -0
  110. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/vendors~module-13.bundle.js +807 -0
  111. data/lib/rubyfox/server/data/www/ROOT/admin/modules/cluster-configurator.html +32 -0
  112. data/lib/rubyfox/server/data/www/ROOT/admin/modules/cluster-monitor.html +185 -0
  113. data/lib/rubyfox/server/data/www/ROOT/admin/modules/cluster-updater.html +47 -0
  114. data/lib/rubyfox/server/data/www/ROOT/admin/modules/extension-deployer.html +84 -0
  115. data/lib/rubyfox/server/data/www/ROOT/admin/modules/zone-monitor.html +15 -8
  116. data/lib/rubyfox/server/data/www/ROOT/index.html +13 -23
  117. data/lib/rubyfox/server/data/www/host-manager/META-INF/context.xml +2 -2
  118. data/lib/rubyfox/server/data/www/host-manager/WEB-INF/jsp/404.jsp +2 -2
  119. data/lib/rubyfox/server/data/www/host-manager/{manager.xml → WEB-INF/manager.xml} +5 -1
  120. data/lib/rubyfox/server/data/www/host-manager/WEB-INF/web.xml +17 -0
  121. data/lib/rubyfox/server/data/www/host-manager/css/manager.css +141 -0
  122. data/lib/rubyfox/server/data/www/host-manager/images/tomcat.svg +967 -0
  123. data/lib/rubyfox/server/data/www/manager/META-INF/context.xml +2 -0
  124. data/lib/rubyfox/server/data/www/manager/WEB-INF/jsp/connectorCerts.jsp +1 -1
  125. data/lib/rubyfox/server/data/www/manager/WEB-INF/jsp/connectorCiphers.jsp +1 -1
  126. data/lib/rubyfox/server/data/www/manager/WEB-INF/jsp/connectorTrustedCerts.jsp +1 -1
  127. data/lib/rubyfox/server/data/www/manager/WEB-INF/jsp/sessionDetail.jsp +3 -3
  128. data/lib/rubyfox/server/data/www/manager/WEB-INF/jsp/sessionsList.jsp +1 -1
  129. data/lib/rubyfox/server/data/www/manager/WEB-INF/web.xml +17 -0
  130. data/lib/rubyfox/server/data/www/manager/css/manager.css +141 -0
  131. data/lib/rubyfox/server/data/www/manager/images/tomcat.svg +967 -0
  132. data/lib/rubyfox/server/data/www/manager/xform.xsl +74 -59
  133. data/lib/rubyfox/server/version.rb +1 -1
  134. metadata +30 -31
  135. data/lib/rubyfox/server/data/config/admin/icons/Analytics.png +0 -0
  136. data/lib/rubyfox/server/data/config/admin/icons/BanManager.png +0 -0
  137. data/lib/rubyfox/server/data/config/admin/icons/BlueBoxMonitor.png +0 -0
  138. data/lib/rubyfox/server/data/config/admin/icons/Console.png +0 -0
  139. data/lib/rubyfox/server/data/config/admin/icons/Dashboard.png +0 -0
  140. data/lib/rubyfox/server/data/config/admin/icons/ExtensionManager.png +0 -0
  141. data/lib/rubyfox/server/data/config/admin/icons/LicenseManager.png +0 -0
  142. data/lib/rubyfox/server/data/config/admin/icons/LogViewer.png +0 -0
  143. data/lib/rubyfox/server/data/config/admin/icons/ServerConfigurator.png +0 -0
  144. data/lib/rubyfox/server/data/config/admin/icons/ServletManager.png +0 -0
  145. data/lib/rubyfox/server/data/config/admin/icons/ZoneConfigurator.png +0 -0
  146. data/lib/rubyfox/server/data/config/admin/icons/ZoneMonitor.png +0 -0
  147. data/lib/rubyfox/server/data/lib/BlueBox.war +0 -0
  148. data/lib/rubyfox/server/data/lib/apache-tomcat/LICENSE +0 -1061
  149. data/lib/rubyfox/server/data/lib/apache-tomcat/NOTICE +0 -68
  150. data/lib/rubyfox/server/data/lib/apache-tomcat/README.md +0 -81
  151. data/lib/rubyfox/server/data/lib/apache-tomcat/RELEASE-NOTES +0 -174
  152. data/lib/rubyfox/server/data/lib/imap.jar +0 -0
  153. data/lib/rubyfox/server/data/lib/mailapi.jar +0 -0
  154. data/lib/rubyfox/server/data/lib/pop3.jar +0 -0
  155. data/lib/rubyfox/server/data/lib/smtp.jar +0 -0
  156. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/module-12~module-13~module-9.bundle.js +0 -2634
  157. data/lib/rubyfox/server/data/www/ROOT/admin/assets/js/core/modules/vendors~module-9.bundle.js +0 -807
  158. data/lib/rubyfox/server/data/www/host-manager/images/tomcat.gif +0 -0
  159. data/lib/rubyfox/server/data/www/manager/images/tomcat.gif +0 -0
  160. /data/lib/rubyfox/server/data/data/buddylists/{BasicExamples/.keep → .keep} +0 -0
@@ -1,687 +1,449 @@
1
1
  /*! (c) gotoAndPlay | All rights reserved */
2
2
  (window["webpackJsonpapplication"] = window["webpackJsonpapplication"] || []).push([["module-12"],{
3
3
 
4
- /***/ "./src/components/module-specific/words-files-manager.js":
5
- /*!***************************************************************!*\
6
- !*** ./src/components/module-specific/words-files-manager.js ***!
7
- \***************************************************************/
8
- /*! exports provided: WordsFilesManager */
9
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
10
-
11
- "use strict";
12
- __webpack_require__.r(__webpack_exports__);
13
- /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WordsFilesManager", function() { return WordsFilesManager; });
14
- /* harmony import */ var _utils_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../utils/utilities */ "./src/utils/utilities.js");
15
-
16
-
17
- class WordsFilesManager extends HTMLElement
18
- {
19
- constructor()
20
- {
21
- super();
22
-
23
- this.REFRESH_WORDS_FILES_CLICK_EVENT = 'refreshWordsFilesClick';
24
- this.EDIT_WORDS_FILE_CLICK_EVENT = 'editWordsFileClick';
25
- this.SAVE_WORDS_FILE_CLICK_EVENT = 'saveWordsFileClick';
26
- this.REMOVE_WORDS_FILE_CLICK_EVENT = 'removeWordsFileClick';
27
- this.ASSIGN_WORDS_FILE_CLICK_EVENT = 'assingWordsFileClick';
28
-
29
- this.CONFIG_FOLDER = 'config/';
30
- this.WORDS_FILE_EXT = '.words.txt';
31
-
32
- this._modalHtml = `
33
- <div class="modal" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalTitle" aria-hidden="true">
34
- <div class="modal-dialog modal-dialog-centered" role="document">
35
- <div class="modal-content">
36
- <div class="modal-header">
37
- <h5 class="modal-title text-primary" id="editModalTitle">Word File Editor</h5>
38
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
39
- <span aria-hidden="true">&times;</span>
40
- </button>
41
- </div>
42
- <div class="modal-body in-flow-invalid-msg">
43
- <fieldset id="editFieldset">
44
- <div class="form-group">
45
- <div class="col-form-label form-label-container">
46
- <label for="filename" class="form-label">Filename <i class="fas fa-question-circle text-muted help" title="Name of the words file. The file will be saved in server's <em>config</em> folder."></i></label>
47
- </div>
48
- <div class="inner-widget">
49
- <div class="input-group">
50
- <input type="text" id="filename" name="filename" class="form-control k-textbox" autocomplete="off" required data-required-msg="Required" aria-describedby="extension" />
51
- <div class="input-group-append">
52
- <span class="input-group-text" id="extension">.words.txt</span>
53
- </div>
54
- </div>
55
- <span class="k-invalid-msg position-static" data-for="filename"></span>
56
- </div>
57
- </div>
58
- <div class="form-group">
59
- <div class="col-form-label form-label-container">
60
- <label for="content" class="form-label">Content <i class="fas fa-question-circle text-muted help" title="Enter a word or a valid regular expression per line. See configuration's <em>Words file</em> field description for additional information."></i></label>
61
- </div>
62
- <div class="inner-widget">
63
- <textarea id="content" name="content" class="form-control k-textarea w-100" rows="10"></textarea>
64
- <span class="k-invalid-msg position-static" data-for="content"></span>
65
- </div>
66
- </div>
67
- </fieldset>
68
- </div>
69
- <div class="modal-footer flex-column">
70
- <div class="d-flex w-100">
71
- <div class="flex-grow-1 text-left">
72
- <button id="saveWordFileButton" type="button" class="k-button k-primary"><i class="fas fa-save mr-1"></i>Save word file</button>
73
- <i id="saveSpinner" class="fas fa-circle-notch fa-spin text-primary align-middle ml-1"></i>
74
- </div>
75
- <div class="flex-grow-1 text-right">
76
- <button type="button" class="k-button k-secondary" data-dismiss="modal">Cancel</button>
77
- </div>
78
- </div>
79
- </div>
80
- </div>
81
- </div>
82
- </div>
83
- `;
84
-
85
- //-------------------------------------------
86
-
87
- $(this).append(`
88
- <div class="col-sm-5 col-lg-4 col-form-label form-label-container">
89
- <label class="form-label">Available words files <i class="fas fa-question-circle text-muted help" title="The list of words files found in server's <em>config</em> folder. Click on the Assign button to set the <strong>Words file</strong> field to the selected file. Configuration submission is then required to save the new value."></i></label>
90
- </div>
91
- <div class="inner-widget align-self-center col-sm">
92
- <div>
93
- <div id="wordsFiles" class="limited-height"></div>
94
- <div id="actionButtons" class="mt-2 text-right" disabled>
95
- <i id="actionSpinner" class="fas fa-circle-notch fa-spin text-primary align-middle"></i>
96
- <button id="refreshButton" type="button" class="k-button k-secondary ml-2" title="Refresh"><i class="fas fa-redo-alt"></i></button>
97
- <button id="addButton" type="button" class="k-button k-secondary ml-2" title="Add"><i class="fas fa-plus"></i></button>
98
- <button id="editButton" type="button" class="k-button k-secondary ml-2" title="Edit" disabled><i class="fas fa-pen"></i></button>
99
- <button id="removeButton" type="button" class="k-button k-secondary ml-2" title="Remove" disabled><i class="fas fa-times"></i></button>
100
- <button id="assignButton" type="button" class="k-button k-secondary ml-2" title="Assign" disabled><i class="fas fa-compress-arrows-alt"></i></button>
101
- </div>
102
- </div>
103
- </div>
104
- `);
105
-
106
- //-------------------------------------------
107
-
108
- // Initialize grid
109
- this._wordsFilesGrid = $('#wordsFiles', $(this)).kendoGrid({
110
- resizable: true,
111
- selectable: 'row',
112
- change: $.proxy(this._onWordsFilesGridSelectionChange, this),
113
- columns: [
114
- {
115
- field: 'name',
116
- title: 'Filename',
117
- width: 120
118
- },
119
- {
120
- field: 'date',
121
- title: 'Date',
122
- width: 80
123
- },
124
- {
125
- field: 'size',
126
- title: 'Size',
127
- width: 80
128
- }
129
- ],
130
- noRecords: {
131
- template: 'No files.'
132
- }
133
- }).data('kendoGrid');
134
-
135
- // Add listeners to button clicks
136
- $('#refreshButton', $(this)).on('click', $.proxy(this._onReloadClick, this));
137
- $('#addButton', $(this)).on('click', $.proxy(this._onAddClick, this));
138
- $('#editButton', $(this)).on('click', $.proxy(this._onEditClick, this));
139
- $('#removeButton', $(this)).on('click', $.proxy(this._onRemoveClick, this));
140
- $('#assignButton', $(this)).on('click', $.proxy(this._onAssignClick, this));
141
- }
142
-
143
- destroy()
144
- {
145
- // Destroy grid
146
- this._wordsFilesGrid.destroy();
147
-
148
- // Remove event listeners
149
- $('#refreshButton', $(this)).off('click');
150
- $('#addButton', $(this)).off('click');
151
- $('#editButton', $(this)).off('click');
152
- $('#removeButton', $(this)).off('click');
153
- $('#assignButton', $(this)).off('click');
154
-
155
- // Hide modal (which in turn destroys it)
156
- let modalElement = $('#editModal', $(this));
157
-
158
- if (modalElement)
159
- modalElement.modal('hide');
160
- }
161
-
162
- get enabled()
163
- {
164
- return this._isEnabled;
165
- }
166
-
167
- set enabled(value)
168
- {
169
- this._isEnabled = value;
170
-
171
- // Enable/disable buttons
172
- $('#actionButtons', this).attr('disabled', !value);
173
-
174
- // Hide spinner
175
- if (value)
176
- this.actionSpinnerVisible = false;
177
-
178
- // Enable/disable modal
179
- let modalElement = $('#editModal', $(this));
180
-
181
- if (modalElement)
182
- {
183
- // Disable modal close buttons
184
- $('button[data-dismiss="modal"]', modalElement).attr('disabled', !value);
185
-
186
- // Disable save button
187
- $('#saveWordFileButton', modalElement).attr('disabled', !value);
188
-
189
- // Disable fieldset
190
- $('#editFieldset', modalElement).attr('disabled', !value);
191
-
192
- // Hide spinner
193
- if (value)
194
- this.saveSpinnerVisible = false;
195
- }
196
- }
197
-
198
- set actionSpinnerVisible(value)
199
- {
200
- if (value)
201
- $('#actionSpinner', $(this)).show();
202
- else
203
- $('#actionSpinner', $(this)).hide();
204
- }
205
-
206
- set saveSpinnerVisible(value)
207
- {
208
- let modalElement = $('#editModal', $(this));
209
-
210
- if (modalElement)
211
- {
212
- if (value)
213
- $('#saveSpinner', modalElement).show();
214
- else
215
- $('#saveSpinner', modalElement).hide();
216
- }
217
- }
218
-
219
- refreshWordsFilesList(wordsFilesList, hideEditModal)
220
- {
221
- if (hideEditModal)
222
- {
223
- let modalElement = $('#editModal', $(this));
224
-
225
- if (modalElement)
226
- modalElement.modal('hide');
227
- }
228
-
229
- let files = [];
230
- this._existingFilenames = [];
231
-
232
- for (let f = 0; f < wordsFilesList.size(); f++)
233
- {
234
- const file = wordsFilesList.getSFSObject(f);
235
-
236
- const fileObj = {};
237
- fileObj.name = file.getUtfString('name');
238
- fileObj.date = file.getUtfString('date') + ' ' + file.getUtfString('time');
239
- fileObj.size = Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_0__["bytesToSize"])(file.getLong('size'), 2);
240
-
241
- // Populate files list
242
- files.push(fileObj);
243
-
244
- // Save ref to existing filenames, to check them when a new file is created
245
- this._existingFilenames.push(fileObj.name);
246
- }
247
-
248
- // Assign data source to grid
249
- this._setWordsFilesGridDataSource(files);
250
- this._onWordsFilesGridSelectionChange();
251
-
252
- // Enable
253
- this.enabled = true;
254
- }
255
-
256
- getSelectedWordsFileName()
257
- {
258
- if (this._wordsFilesGrid.select() != null)
259
- {
260
- let selectedIndex = this._wordsFilesGrid.select().index();
261
- return this._wordsFilesGrid.dataSource.at(selectedIndex).name;
262
- }
263
- else
264
- return null;
265
- }
266
-
267
- editWordsFile(filename, content)
268
- {
269
- this._isNewFile = false;
270
-
271
- this.enabled = true;
272
-
273
- // Show modal
274
- this._showModal();
275
-
276
- // Remove default extension from filename
277
- filename = filename.substring(0, filename.lastIndexOf(this.WORDS_FILE_EXT));
4
+ /***/ "./node_modules/file-saver/dist/FileSaver.min.js":
5
+ /*!*******************************************************!*\
6
+ !*** ./node_modules/file-saver/dist/FileSaver.min.js ***!
7
+ \*******************************************************/
8
+ /*! no static exports found */
9
+ /***/ (function(module, exports, __webpack_require__) {
278
10
 
279
- // Enter content filename and content in modal form
280
- $('#editModal #filename', $(this)).val(filename);
281
- $('#editModal #content', $(this)).val(content);
11
+ /* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(a,b){if(true)!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (b),
12
+ __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
13
+ (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
14
+ __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));else {}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(b,c,d){var e=new XMLHttpRequest;e.open("GET",b),e.responseType="blob",e.onload=function(){a(e.response,c,d)},e.onerror=function(){console.error("could not download file")},e.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(a,b,d,e){if(e=e||open("","_blank"),e&&(e.document.title=e.document.body.innerText="downloading..."),"string"==typeof a)return c(a,b,d);var g="application/octet-stream"===a.type,h=/constructor/i.test(f.HTMLElement)||f.safari,i=/CriOS\/[\d]+/.test(navigator.userAgent);if((i||g&&h)&&"object"==typeof FileReader){var j=new FileReader;j.onloadend=function(){var a=j.result;a=i?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),e?e.location.href=a:location=a,e=null},j.readAsDataURL(a)}else{var k=f.URL||f.webkitURL,l=k.createObjectURL(a);e?e.location=l:location.href=l,e=null,setTimeout(function(){k.revokeObjectURL(l)},4E4)}});f.saveAs=a.saveAs=a, true&&(module.exports=a)});
282
15
 
283
- // Set filename field as not editable and hide note
284
- $('#editModal #filename', $(this)).attr('disabled', true);
285
- $('#editModal #filenameNote', $(this)).hide();
286
- }
287
-
288
- getExistingFilenames()
289
- {
290
- return this._existingFilenames;
291
- }
292
-
293
- _setWordsFilesGridDataSource(ds)
294
- {
295
- // Read current horizontal scroll value
296
- const scrollLeft = $('#wordsFiles .k-grid-content', this._wordsFilesGrid.wrapper).scrollLeft();
297
-
298
- // Assign data source to grid
299
- this._wordsFilesGrid.setDataSource(ds);
300
-
301
- // Set horizontal scroll
302
- $('#wordsFiles .k-grid-content', this._wordsFilesGrid.wrapper).scrollLeft(scrollLeft);
303
- }
304
-
305
- _onWordsFilesGridSelectionChange()
306
- {
307
- // Enable/disable buttons
308
- const selectedRows = this._wordsFilesGrid.select();
309
- $('#editButton').attr('disabled', selectedRows.length == 0);
310
- $('#removeButton').attr('disabled', selectedRows.length == 0);
311
- $('#assignButton').attr('disabled', selectedRows.length == 0);
312
- }
313
-
314
- _onReloadClick()
315
- {
316
- // Fire event to request file content to server
317
- let evt = new CustomEvent(this.REFRESH_WORDS_FILES_CLICK_EVENT, {
318
- detail: null,
319
- bubbles: false,
320
- cancelable: false
321
- });
322
-
323
- this.dispatchEvent(evt);
324
- }
325
-
326
- _onAddClick()
327
- {
328
- this._isNewFile = true;
329
-
330
- // Show modal
331
- this._showModal();
332
- }
333
-
334
- _onEditClick()
335
- {
336
- // Disable buttons
337
- this.enabled = false;
338
- this.actionSpinnerVisible = true;
339
-
340
- // Fire event to request file content to server
341
- let evt = new CustomEvent(this.EDIT_WORDS_FILE_CLICK_EVENT, {
342
- detail: this.getSelectedWordsFileName(),
343
- bubbles: false,
344
- cancelable: false
345
- });
346
-
347
- this.dispatchEvent(evt);
348
- }
349
-
350
- _onRemoveClick()
351
- {
352
- // Fire event to request file removal to server
353
- let evt = new CustomEvent(this.REMOVE_WORDS_FILE_CLICK_EVENT, {
354
- detail: this.getSelectedWordsFileName(),
355
- bubbles: false,
356
- cancelable: false
357
- });
358
-
359
- this.dispatchEvent(evt);
360
- }
361
-
362
- _onAssignClick()
363
- {
364
- // Fire event to substitute path in configuration
365
- let evt = new CustomEvent(this.ASSIGN_WORDS_FILE_CLICK_EVENT, {
366
- detail: this.CONFIG_FOLDER + this.getSelectedWordsFileName(),
367
- bubbles: false,
368
- cancelable: false
369
- });
370
-
371
- this.dispatchEvent(evt);
372
- }
373
-
374
- _onSaveWordFileClick()
375
- {
376
- if (this._validator.validate())
377
- {
378
- // Show spinner
379
- $('#editModal #saveSpinner', $(this)).show();
380
-
381
- // Fire event to request file to be saved by server
382
- let evt = new CustomEvent(this.SAVE_WORDS_FILE_CLICK_EVENT, {
383
- detail: {
384
- filename: $('#editModal #filename', $(this)).val() + this.WORDS_FILE_EXT,
385
- isNew: this._isNewFile,
386
- content: $('#editModal #content', $(this)).val()
387
- },
388
- bubbles: false,
389
- cancelable: false
390
- });
391
-
392
- this.dispatchEvent(evt);
393
- }
394
- }
16
+ //# sourceMappingURL=FileSaver.min.js.map
17
+ /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
395
18
 
396
- _showModal()
397
- {
398
- // Append modal html
399
- $(this).append(this._modalHtml);
400
-
401
- let modalElement = $('#editModal', $(this));
402
-
403
- // Initialize kendo validation
404
- this._validator = modalElement.find('#editFieldset').kendoValidator({
405
- validateOnBlur: true,
406
- rules: {
407
- requiredFilename: $.proxy(function(input) {
408
- let valid = true;
409
- if (input.is('[name=filename]'))
410
- valid = input.val() !== '';
411
- return valid;
412
- }, this),
413
- validFilename: $.proxy(function(input) {
414
- let valid = true;
415
- if (input.is('[name=filename]'))
416
- valid = (!input.val().includes('\\') && !input.val().includes('/'));
417
- return valid;
418
- }, this)
419
- },
420
- messages: {
421
- requiredFilename: 'Required',
422
- validFilename: 'Contains invalid characters (slash, backslash)'
423
- }
424
- }).data('kendoValidator');
425
-
426
- // Hide save spinner
427
- $('#saveSpinner', modalElement).hide();
428
-
429
- // Add listener to Save button click
430
- $('#saveWordFileButton', modalElement).on('click', $.proxy(this._onSaveWordFileClick, this));
431
-
432
- // Add listener to modal hide event
433
- modalElement.on('hidden.bs.modal', $.proxy(this._destroyModal, this));
19
+ /***/ }),
434
20
 
435
- // Initialize bootstrap modal
436
- modalElement.modal({
437
- backdrop: 'static',
438
- keyboard: false,
439
- });
21
+ /***/ "./node_modules/moment/locale sync recursive ^\\.\\/.*$":
22
+ /*!**************************************************!*\
23
+ !*** ./node_modules/moment/locale sync ^\.\/.*$ ***!
24
+ \**************************************************/
25
+ /*! no static exports found */
26
+ /***/ (function(module, exports, __webpack_require__) {
27
+
28
+ var map = {
29
+ "./af": "./node_modules/moment/locale/af.js",
30
+ "./af.js": "./node_modules/moment/locale/af.js",
31
+ "./ar": "./node_modules/moment/locale/ar.js",
32
+ "./ar-dz": "./node_modules/moment/locale/ar-dz.js",
33
+ "./ar-dz.js": "./node_modules/moment/locale/ar-dz.js",
34
+ "./ar-kw": "./node_modules/moment/locale/ar-kw.js",
35
+ "./ar-kw.js": "./node_modules/moment/locale/ar-kw.js",
36
+ "./ar-ly": "./node_modules/moment/locale/ar-ly.js",
37
+ "./ar-ly.js": "./node_modules/moment/locale/ar-ly.js",
38
+ "./ar-ma": "./node_modules/moment/locale/ar-ma.js",
39
+ "./ar-ma.js": "./node_modules/moment/locale/ar-ma.js",
40
+ "./ar-sa": "./node_modules/moment/locale/ar-sa.js",
41
+ "./ar-sa.js": "./node_modules/moment/locale/ar-sa.js",
42
+ "./ar-tn": "./node_modules/moment/locale/ar-tn.js",
43
+ "./ar-tn.js": "./node_modules/moment/locale/ar-tn.js",
44
+ "./ar.js": "./node_modules/moment/locale/ar.js",
45
+ "./az": "./node_modules/moment/locale/az.js",
46
+ "./az.js": "./node_modules/moment/locale/az.js",
47
+ "./be": "./node_modules/moment/locale/be.js",
48
+ "./be.js": "./node_modules/moment/locale/be.js",
49
+ "./bg": "./node_modules/moment/locale/bg.js",
50
+ "./bg.js": "./node_modules/moment/locale/bg.js",
51
+ "./bm": "./node_modules/moment/locale/bm.js",
52
+ "./bm.js": "./node_modules/moment/locale/bm.js",
53
+ "./bn": "./node_modules/moment/locale/bn.js",
54
+ "./bn.js": "./node_modules/moment/locale/bn.js",
55
+ "./bo": "./node_modules/moment/locale/bo.js",
56
+ "./bo.js": "./node_modules/moment/locale/bo.js",
57
+ "./br": "./node_modules/moment/locale/br.js",
58
+ "./br.js": "./node_modules/moment/locale/br.js",
59
+ "./bs": "./node_modules/moment/locale/bs.js",
60
+ "./bs.js": "./node_modules/moment/locale/bs.js",
61
+ "./ca": "./node_modules/moment/locale/ca.js",
62
+ "./ca.js": "./node_modules/moment/locale/ca.js",
63
+ "./cs": "./node_modules/moment/locale/cs.js",
64
+ "./cs.js": "./node_modules/moment/locale/cs.js",
65
+ "./cv": "./node_modules/moment/locale/cv.js",
66
+ "./cv.js": "./node_modules/moment/locale/cv.js",
67
+ "./cy": "./node_modules/moment/locale/cy.js",
68
+ "./cy.js": "./node_modules/moment/locale/cy.js",
69
+ "./da": "./node_modules/moment/locale/da.js",
70
+ "./da.js": "./node_modules/moment/locale/da.js",
71
+ "./de": "./node_modules/moment/locale/de.js",
72
+ "./de-at": "./node_modules/moment/locale/de-at.js",
73
+ "./de-at.js": "./node_modules/moment/locale/de-at.js",
74
+ "./de-ch": "./node_modules/moment/locale/de-ch.js",
75
+ "./de-ch.js": "./node_modules/moment/locale/de-ch.js",
76
+ "./de.js": "./node_modules/moment/locale/de.js",
77
+ "./dv": "./node_modules/moment/locale/dv.js",
78
+ "./dv.js": "./node_modules/moment/locale/dv.js",
79
+ "./el": "./node_modules/moment/locale/el.js",
80
+ "./el.js": "./node_modules/moment/locale/el.js",
81
+ "./en-SG": "./node_modules/moment/locale/en-SG.js",
82
+ "./en-SG.js": "./node_modules/moment/locale/en-SG.js",
83
+ "./en-au": "./node_modules/moment/locale/en-au.js",
84
+ "./en-au.js": "./node_modules/moment/locale/en-au.js",
85
+ "./en-ca": "./node_modules/moment/locale/en-ca.js",
86
+ "./en-ca.js": "./node_modules/moment/locale/en-ca.js",
87
+ "./en-gb": "./node_modules/moment/locale/en-gb.js",
88
+ "./en-gb.js": "./node_modules/moment/locale/en-gb.js",
89
+ "./en-ie": "./node_modules/moment/locale/en-ie.js",
90
+ "./en-ie.js": "./node_modules/moment/locale/en-ie.js",
91
+ "./en-il": "./node_modules/moment/locale/en-il.js",
92
+ "./en-il.js": "./node_modules/moment/locale/en-il.js",
93
+ "./en-nz": "./node_modules/moment/locale/en-nz.js",
94
+ "./en-nz.js": "./node_modules/moment/locale/en-nz.js",
95
+ "./eo": "./node_modules/moment/locale/eo.js",
96
+ "./eo.js": "./node_modules/moment/locale/eo.js",
97
+ "./es": "./node_modules/moment/locale/es.js",
98
+ "./es-do": "./node_modules/moment/locale/es-do.js",
99
+ "./es-do.js": "./node_modules/moment/locale/es-do.js",
100
+ "./es-us": "./node_modules/moment/locale/es-us.js",
101
+ "./es-us.js": "./node_modules/moment/locale/es-us.js",
102
+ "./es.js": "./node_modules/moment/locale/es.js",
103
+ "./et": "./node_modules/moment/locale/et.js",
104
+ "./et.js": "./node_modules/moment/locale/et.js",
105
+ "./eu": "./node_modules/moment/locale/eu.js",
106
+ "./eu.js": "./node_modules/moment/locale/eu.js",
107
+ "./fa": "./node_modules/moment/locale/fa.js",
108
+ "./fa.js": "./node_modules/moment/locale/fa.js",
109
+ "./fi": "./node_modules/moment/locale/fi.js",
110
+ "./fi.js": "./node_modules/moment/locale/fi.js",
111
+ "./fo": "./node_modules/moment/locale/fo.js",
112
+ "./fo.js": "./node_modules/moment/locale/fo.js",
113
+ "./fr": "./node_modules/moment/locale/fr.js",
114
+ "./fr-ca": "./node_modules/moment/locale/fr-ca.js",
115
+ "./fr-ca.js": "./node_modules/moment/locale/fr-ca.js",
116
+ "./fr-ch": "./node_modules/moment/locale/fr-ch.js",
117
+ "./fr-ch.js": "./node_modules/moment/locale/fr-ch.js",
118
+ "./fr.js": "./node_modules/moment/locale/fr.js",
119
+ "./fy": "./node_modules/moment/locale/fy.js",
120
+ "./fy.js": "./node_modules/moment/locale/fy.js",
121
+ "./ga": "./node_modules/moment/locale/ga.js",
122
+ "./ga.js": "./node_modules/moment/locale/ga.js",
123
+ "./gd": "./node_modules/moment/locale/gd.js",
124
+ "./gd.js": "./node_modules/moment/locale/gd.js",
125
+ "./gl": "./node_modules/moment/locale/gl.js",
126
+ "./gl.js": "./node_modules/moment/locale/gl.js",
127
+ "./gom-latn": "./node_modules/moment/locale/gom-latn.js",
128
+ "./gom-latn.js": "./node_modules/moment/locale/gom-latn.js",
129
+ "./gu": "./node_modules/moment/locale/gu.js",
130
+ "./gu.js": "./node_modules/moment/locale/gu.js",
131
+ "./he": "./node_modules/moment/locale/he.js",
132
+ "./he.js": "./node_modules/moment/locale/he.js",
133
+ "./hi": "./node_modules/moment/locale/hi.js",
134
+ "./hi.js": "./node_modules/moment/locale/hi.js",
135
+ "./hr": "./node_modules/moment/locale/hr.js",
136
+ "./hr.js": "./node_modules/moment/locale/hr.js",
137
+ "./hu": "./node_modules/moment/locale/hu.js",
138
+ "./hu.js": "./node_modules/moment/locale/hu.js",
139
+ "./hy-am": "./node_modules/moment/locale/hy-am.js",
140
+ "./hy-am.js": "./node_modules/moment/locale/hy-am.js",
141
+ "./id": "./node_modules/moment/locale/id.js",
142
+ "./id.js": "./node_modules/moment/locale/id.js",
143
+ "./is": "./node_modules/moment/locale/is.js",
144
+ "./is.js": "./node_modules/moment/locale/is.js",
145
+ "./it": "./node_modules/moment/locale/it.js",
146
+ "./it-ch": "./node_modules/moment/locale/it-ch.js",
147
+ "./it-ch.js": "./node_modules/moment/locale/it-ch.js",
148
+ "./it.js": "./node_modules/moment/locale/it.js",
149
+ "./ja": "./node_modules/moment/locale/ja.js",
150
+ "./ja.js": "./node_modules/moment/locale/ja.js",
151
+ "./jv": "./node_modules/moment/locale/jv.js",
152
+ "./jv.js": "./node_modules/moment/locale/jv.js",
153
+ "./ka": "./node_modules/moment/locale/ka.js",
154
+ "./ka.js": "./node_modules/moment/locale/ka.js",
155
+ "./kk": "./node_modules/moment/locale/kk.js",
156
+ "./kk.js": "./node_modules/moment/locale/kk.js",
157
+ "./km": "./node_modules/moment/locale/km.js",
158
+ "./km.js": "./node_modules/moment/locale/km.js",
159
+ "./kn": "./node_modules/moment/locale/kn.js",
160
+ "./kn.js": "./node_modules/moment/locale/kn.js",
161
+ "./ko": "./node_modules/moment/locale/ko.js",
162
+ "./ko.js": "./node_modules/moment/locale/ko.js",
163
+ "./ku": "./node_modules/moment/locale/ku.js",
164
+ "./ku.js": "./node_modules/moment/locale/ku.js",
165
+ "./ky": "./node_modules/moment/locale/ky.js",
166
+ "./ky.js": "./node_modules/moment/locale/ky.js",
167
+ "./lb": "./node_modules/moment/locale/lb.js",
168
+ "./lb.js": "./node_modules/moment/locale/lb.js",
169
+ "./lo": "./node_modules/moment/locale/lo.js",
170
+ "./lo.js": "./node_modules/moment/locale/lo.js",
171
+ "./lt": "./node_modules/moment/locale/lt.js",
172
+ "./lt.js": "./node_modules/moment/locale/lt.js",
173
+ "./lv": "./node_modules/moment/locale/lv.js",
174
+ "./lv.js": "./node_modules/moment/locale/lv.js",
175
+ "./me": "./node_modules/moment/locale/me.js",
176
+ "./me.js": "./node_modules/moment/locale/me.js",
177
+ "./mi": "./node_modules/moment/locale/mi.js",
178
+ "./mi.js": "./node_modules/moment/locale/mi.js",
179
+ "./mk": "./node_modules/moment/locale/mk.js",
180
+ "./mk.js": "./node_modules/moment/locale/mk.js",
181
+ "./ml": "./node_modules/moment/locale/ml.js",
182
+ "./ml.js": "./node_modules/moment/locale/ml.js",
183
+ "./mn": "./node_modules/moment/locale/mn.js",
184
+ "./mn.js": "./node_modules/moment/locale/mn.js",
185
+ "./mr": "./node_modules/moment/locale/mr.js",
186
+ "./mr.js": "./node_modules/moment/locale/mr.js",
187
+ "./ms": "./node_modules/moment/locale/ms.js",
188
+ "./ms-my": "./node_modules/moment/locale/ms-my.js",
189
+ "./ms-my.js": "./node_modules/moment/locale/ms-my.js",
190
+ "./ms.js": "./node_modules/moment/locale/ms.js",
191
+ "./mt": "./node_modules/moment/locale/mt.js",
192
+ "./mt.js": "./node_modules/moment/locale/mt.js",
193
+ "./my": "./node_modules/moment/locale/my.js",
194
+ "./my.js": "./node_modules/moment/locale/my.js",
195
+ "./nb": "./node_modules/moment/locale/nb.js",
196
+ "./nb.js": "./node_modules/moment/locale/nb.js",
197
+ "./ne": "./node_modules/moment/locale/ne.js",
198
+ "./ne.js": "./node_modules/moment/locale/ne.js",
199
+ "./nl": "./node_modules/moment/locale/nl.js",
200
+ "./nl-be": "./node_modules/moment/locale/nl-be.js",
201
+ "./nl-be.js": "./node_modules/moment/locale/nl-be.js",
202
+ "./nl.js": "./node_modules/moment/locale/nl.js",
203
+ "./nn": "./node_modules/moment/locale/nn.js",
204
+ "./nn.js": "./node_modules/moment/locale/nn.js",
205
+ "./pa-in": "./node_modules/moment/locale/pa-in.js",
206
+ "./pa-in.js": "./node_modules/moment/locale/pa-in.js",
207
+ "./pl": "./node_modules/moment/locale/pl.js",
208
+ "./pl.js": "./node_modules/moment/locale/pl.js",
209
+ "./pt": "./node_modules/moment/locale/pt.js",
210
+ "./pt-br": "./node_modules/moment/locale/pt-br.js",
211
+ "./pt-br.js": "./node_modules/moment/locale/pt-br.js",
212
+ "./pt.js": "./node_modules/moment/locale/pt.js",
213
+ "./ro": "./node_modules/moment/locale/ro.js",
214
+ "./ro.js": "./node_modules/moment/locale/ro.js",
215
+ "./ru": "./node_modules/moment/locale/ru.js",
216
+ "./ru.js": "./node_modules/moment/locale/ru.js",
217
+ "./sd": "./node_modules/moment/locale/sd.js",
218
+ "./sd.js": "./node_modules/moment/locale/sd.js",
219
+ "./se": "./node_modules/moment/locale/se.js",
220
+ "./se.js": "./node_modules/moment/locale/se.js",
221
+ "./si": "./node_modules/moment/locale/si.js",
222
+ "./si.js": "./node_modules/moment/locale/si.js",
223
+ "./sk": "./node_modules/moment/locale/sk.js",
224
+ "./sk.js": "./node_modules/moment/locale/sk.js",
225
+ "./sl": "./node_modules/moment/locale/sl.js",
226
+ "./sl.js": "./node_modules/moment/locale/sl.js",
227
+ "./sq": "./node_modules/moment/locale/sq.js",
228
+ "./sq.js": "./node_modules/moment/locale/sq.js",
229
+ "./sr": "./node_modules/moment/locale/sr.js",
230
+ "./sr-cyrl": "./node_modules/moment/locale/sr-cyrl.js",
231
+ "./sr-cyrl.js": "./node_modules/moment/locale/sr-cyrl.js",
232
+ "./sr.js": "./node_modules/moment/locale/sr.js",
233
+ "./ss": "./node_modules/moment/locale/ss.js",
234
+ "./ss.js": "./node_modules/moment/locale/ss.js",
235
+ "./sv": "./node_modules/moment/locale/sv.js",
236
+ "./sv.js": "./node_modules/moment/locale/sv.js",
237
+ "./sw": "./node_modules/moment/locale/sw.js",
238
+ "./sw.js": "./node_modules/moment/locale/sw.js",
239
+ "./ta": "./node_modules/moment/locale/ta.js",
240
+ "./ta.js": "./node_modules/moment/locale/ta.js",
241
+ "./te": "./node_modules/moment/locale/te.js",
242
+ "./te.js": "./node_modules/moment/locale/te.js",
243
+ "./tet": "./node_modules/moment/locale/tet.js",
244
+ "./tet.js": "./node_modules/moment/locale/tet.js",
245
+ "./tg": "./node_modules/moment/locale/tg.js",
246
+ "./tg.js": "./node_modules/moment/locale/tg.js",
247
+ "./th": "./node_modules/moment/locale/th.js",
248
+ "./th.js": "./node_modules/moment/locale/th.js",
249
+ "./tl-ph": "./node_modules/moment/locale/tl-ph.js",
250
+ "./tl-ph.js": "./node_modules/moment/locale/tl-ph.js",
251
+ "./tlh": "./node_modules/moment/locale/tlh.js",
252
+ "./tlh.js": "./node_modules/moment/locale/tlh.js",
253
+ "./tr": "./node_modules/moment/locale/tr.js",
254
+ "./tr.js": "./node_modules/moment/locale/tr.js",
255
+ "./tzl": "./node_modules/moment/locale/tzl.js",
256
+ "./tzl.js": "./node_modules/moment/locale/tzl.js",
257
+ "./tzm": "./node_modules/moment/locale/tzm.js",
258
+ "./tzm-latn": "./node_modules/moment/locale/tzm-latn.js",
259
+ "./tzm-latn.js": "./node_modules/moment/locale/tzm-latn.js",
260
+ "./tzm.js": "./node_modules/moment/locale/tzm.js",
261
+ "./ug-cn": "./node_modules/moment/locale/ug-cn.js",
262
+ "./ug-cn.js": "./node_modules/moment/locale/ug-cn.js",
263
+ "./uk": "./node_modules/moment/locale/uk.js",
264
+ "./uk.js": "./node_modules/moment/locale/uk.js",
265
+ "./ur": "./node_modules/moment/locale/ur.js",
266
+ "./ur.js": "./node_modules/moment/locale/ur.js",
267
+ "./uz": "./node_modules/moment/locale/uz.js",
268
+ "./uz-latn": "./node_modules/moment/locale/uz-latn.js",
269
+ "./uz-latn.js": "./node_modules/moment/locale/uz-latn.js",
270
+ "./uz.js": "./node_modules/moment/locale/uz.js",
271
+ "./vi": "./node_modules/moment/locale/vi.js",
272
+ "./vi.js": "./node_modules/moment/locale/vi.js",
273
+ "./x-pseudo": "./node_modules/moment/locale/x-pseudo.js",
274
+ "./x-pseudo.js": "./node_modules/moment/locale/x-pseudo.js",
275
+ "./yo": "./node_modules/moment/locale/yo.js",
276
+ "./yo.js": "./node_modules/moment/locale/yo.js",
277
+ "./zh-cn": "./node_modules/moment/locale/zh-cn.js",
278
+ "./zh-cn.js": "./node_modules/moment/locale/zh-cn.js",
279
+ "./zh-hk": "./node_modules/moment/locale/zh-hk.js",
280
+ "./zh-hk.js": "./node_modules/moment/locale/zh-hk.js",
281
+ "./zh-tw": "./node_modules/moment/locale/zh-tw.js",
282
+ "./zh-tw.js": "./node_modules/moment/locale/zh-tw.js"
283
+ };
284
+
285
+
286
+ function webpackContext(req) {
287
+ var id = webpackContextResolve(req);
288
+ return __webpack_require__(id);
289
+ }
290
+ function webpackContextResolve(req) {
291
+ if(!__webpack_require__.o(map, req)) {
292
+ var e = new Error("Cannot find module '" + req + "'");
293
+ e.code = 'MODULE_NOT_FOUND';
294
+ throw e;
440
295
  }
296
+ return map[req];
297
+ }
298
+ webpackContext.keys = function webpackContextKeys() {
299
+ return Object.keys(map);
300
+ };
301
+ webpackContext.resolve = webpackContextResolve;
302
+ module.exports = webpackContext;
303
+ webpackContext.id = "./node_modules/moment/locale sync recursive ^\\.\\/.*$";
441
304
 
442
- _destroyModal()
443
- {
444
- let modalElement = $('#editModal', $(this));
445
-
446
- if (modalElement)
447
- {
448
- // Remove listeners
449
- $('#saveWordFileButton', modalElement).off('click');
450
- modalElement.off('hidden.bs.modal');
451
-
452
- // Destroy everything Kendo
453
- kendo.destroy(modalElement);
454
-
455
- // Dispose modal
456
- modalElement.modal('dispose');
305
+ /***/ }),
457
306
 
458
- // Remove html
459
- modalElement.remove();
460
- modalElement = null;
461
- }
462
- }
307
+ /***/ "./node_modules/webpack/buildin/global.js":
308
+ /*!***********************************!*\
309
+ !*** (webpack)/buildin/global.js ***!
310
+ \***********************************/
311
+ /*! no static exports found */
312
+ /***/ (function(module, exports) {
313
+
314
+ var g;
315
+
316
+ // This works in non-strict mode
317
+ g = (function() {
318
+ return this;
319
+ })();
320
+
321
+ try {
322
+ // This works if eval is allowed (see CSP)
323
+ g = g || new Function("return this")();
324
+ } catch (e) {
325
+ // This works if the window reference is available
326
+ if (typeof window === "object") g = window;
463
327
  }
464
328
 
465
- // DEFINE COMPONENT
466
- if (!window.customElements.get('words-files-manager'))
467
- window.customElements.define('words-files-manager', WordsFilesManager);
329
+ // g can still be undefined, but nothing to do about it...
330
+ // We return undefined, instead of nothing here, so it's
331
+ // easier to handle this case. if(!global) { ...}
332
+
333
+ module.exports = g;
468
334
 
469
- /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
470
335
 
471
336
  /***/ }),
472
337
 
473
- /***/ "./src/components/sidebar-layout.js":
474
- /*!******************************************!*\
475
- !*** ./src/components/sidebar-layout.js ***!
476
- \******************************************/
477
- /*! exports provided: SidebarLayout */
338
+ /***/ "./src/data/runtime-log-entry.js":
339
+ /*!***************************************!*\
340
+ !*** ./src/data/runtime-log-entry.js ***!
341
+ \***************************************/
342
+ /*! exports provided: RuntimeLogEntry */
478
343
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
479
344
 
480
345
  "use strict";
481
346
  __webpack_require__.r(__webpack_exports__);
482
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SidebarLayout", function() { return SidebarLayout; });
483
- class SidebarLayout extends HTMLElement
347
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RuntimeLogEntry", function() { return RuntimeLogEntry; });
348
+ class RuntimeLogEntry
484
349
  {
485
- constructor()
350
+ constructor(separator)
486
351
  {
487
- super();
488
-
489
- // Attach a shadow root
490
- const shadowRoot = this.attachShadow({mode: 'open'});
491
- shadowRoot.innerHTML = `
492
- <style>
493
- :host {
494
- display: flex;
495
- flex-direction: row;
496
- }
497
-
498
- @media (max-width: 575.98px) {
499
- :host(.split-xs) ::slotted(:not([aria-selected="true"])) {
500
- display: none !important;
501
- }
502
-
503
- :host(.split-xs) ::slotted([aria-selected="true"]) {
504
- flex-grow: 1;
505
- }
506
- }
507
-
508
- @media (max-width: 767.98px) {
509
- :host(.split-sm) ::slotted(:not([aria-selected="true"])) {
510
- display: none !important;
511
- }
512
-
513
- :host(.split-sm) ::slotted([aria-selected="true"]) {
514
- flex-grow: 1;
515
- }
516
- }
517
-
518
- @media (max-width: 991.98px) {
519
- :host(.split-md) ::slotted(:not([aria-selected="true"])) {
520
- display: none !important;
521
- }
522
-
523
- :host(.split-md) ::slotted([aria-selected="true"]) {
524
- flex-grow: 1;
525
- }
526
- }
527
-
528
- @media (max-width: 1199.98px) {
529
- :host(.split-lg) ::slotted(:not([aria-selected="true"])) {
530
- display: none !important;
531
- }
532
-
533
- :host(.split-lg) ::slotted([aria-selected="true"]) {
534
- flex-grow: 1;
535
- }
536
- }
537
-
538
- .side-col::slotted(*) {
539
- }
540
-
541
- .main-col::slotted(*) {
542
- flex-grow: 1;
543
- }
544
- </style>
545
-
546
- <slot class="side-col" name="side-column"></slot>
547
- <slot class="main-col" name="main-column"></slot>
548
- `;
549
-
550
- // Set initial selection
551
- this.selectedIndex = 0;
352
+ this.sep = separator
552
353
  }
553
354
 
554
- get selectedPanel()
355
+ static fromArray(separator, logEntryData)
555
356
  {
556
- return this._selectedPanel;
557
- }
357
+ let rle = new RuntimeLogEntry(separator);
558
358
 
559
- set selectedPanel(element) // 'side' or 'main'
560
- {
561
- if (element != null && element.parentNode == this)
562
- {
563
- this._selectedPanel = element;
359
+ rle.date = logEntryData[0];
360
+ rle.time = logEntryData[1];
361
+ rle.dateTime = rle._getDateTime();
362
+ rle.level = logEntryData[2].trim();
363
+ rle.thread = logEntryData[3];
364
+ rle.clazz = logEntryData[4];
365
+ rle.message = logEntryData[6];
564
366
 
565
- for (let element of this.children)
566
- {
567
- if (element == this._selectedPanel)
568
- element.setAttribute('aria-selected', 'true');
569
- else
570
- element.removeAttribute('aria-selected');
571
- }
572
- }
573
- else
574
- {
575
- console.error('Element is not a child of SidebarLayout');
576
- }
367
+ return rle;
577
368
  }
578
369
 
579
- get selectedIndex()
370
+ _getDateTime()
580
371
  {
581
- return Array.from(this.children).indexOf(this._selectedPanel);
582
- }
583
-
584
- set selectedIndex(index)
585
- {
586
- if (this.children.length > 0)
587
- {
588
- if (this.children[index] == null)
589
- {
590
- console.error('Invalid SidebarLayout index');
591
- return;
592
- }
593
-
594
- let element = this.children[index];
595
- this.selectedPanel = element;
596
- }
372
+ return this.date + '\n' + this.time;
597
373
  }
598
374
  }
599
375
 
600
- // DEFINE COMPONENT
601
- if (!window.customElements.get('sidebar-layout'))
602
- window.customElements.define('sidebar-layout', SidebarLayout);
603
-
604
376
 
605
377
  /***/ }),
606
378
 
607
- /***/ "./src/modules/zone-configurator.js":
608
- /*!******************************************!*\
609
- !*** ./src/modules/zone-configurator.js ***!
610
- \******************************************/
379
+ /***/ "./src/modules/log-viewer.js":
380
+ /*!***********************************!*\
381
+ !*** ./src/modules/log-viewer.js ***!
382
+ \***********************************/
611
383
  /*! exports provided: default */
612
384
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
613
385
 
614
386
  "use strict";
615
387
  __webpack_require__.r(__webpack_exports__);
616
- /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ZoneConfigurator; });
388
+ /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return LogViewer; });
617
389
  /* harmony import */ var _base_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base-module */ "./src/modules/base-module.js");
618
- /* harmony import */ var _components_view_stack__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/view-stack */ "./src/components/view-stack.js");
619
- /* harmony import */ var _components_sidebar_layout__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/sidebar-layout */ "./src/components/sidebar-layout.js");
620
- /* harmony import */ var _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../utils/uibuilder/config-interface-builder */ "./src/utils/uibuilder/config-interface-builder.js");
390
+ /* harmony import */ var _data_runtime_log_entry__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../data/runtime-log-entry */ "./src/data/runtime-log-entry.js");
391
+ /* harmony import */ var file_saver__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! file-saver */ "./node_modules/file-saver/dist/FileSaver.min.js");
392
+ /* harmony import */ var file_saver__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(file_saver__WEBPACK_IMPORTED_MODULE_2__);
393
+ /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! moment */ "./node_modules/moment/moment.js");
394
+ /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_3__);
621
395
  /* harmony import */ var _utils_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/utilities */ "./src/utils/utilities.js");
622
- /* harmony import */ var _components_module_specific_words_files_manager__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/module-specific/words-files-manager */ "./src/components/module-specific/words-files-manager.js");
623
396
 
624
397
 
625
398
 
626
399
 
627
400
 
628
401
 
629
-
630
- class ZoneConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseModule"]
402
+ class LogViewer extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseModule"]
631
403
  {
632
404
  constructor()
633
405
  {
634
- super('zoneConfig');
406
+ super('logViewer');
635
407
 
636
- this.ITEM_TYPE_ZONE = 'zone';
637
- this.ITEM_TYPE_ROOM = 'room';
408
+ this.COMMANDS_PREFIX = 'logViewer';
409
+ this.HELP_URL = '/admintool-LogViewer';
638
410
 
639
- // Outgoing requests
640
- this.REQ_GET_ZONES = 'getZones';
411
+ this.ANY_LEVEL = '[any]';
412
+ this.LEVELS = ['TRACE','DEBUG','INFO','WARN','ERROR'];
413
+ this.ANY_CLASS = '[any]';
641
414
 
642
- this.REQ_GET_ZONE_CONFIG = 'getZoneConfig';
643
- this.REQ_SAVE_ZONE_CONFIG = 'saveZoneConfig';
644
- this.REQ_NEW_ZONE_CONFIG = 'newZoneConfig';
645
- this.REQ_DELETE_ZONE_CONFIG = 'delZoneConfig';
646
- this.REQ_ACTIVATE_ZONE = 'actZone';
415
+ this.LOG_DATA_LABEL = 'lines';
647
416
 
648
- this.REQ_GET_ROOM_CONFIG = 'getRoomConfig';
649
- this.REQ_SAVE_ROOM_CONFIG = 'saveRoomConfig';
650
- this.REQ_NEW_ROOM_CONFIG = 'newRoomConfig';
651
- this.REQ_DELETE_ROOM_CONFIG = 'delRoomConfig';
417
+ this.BOOT_LOG_BACKUP_ID = 'SFS2X_BootLog';
418
+ this.RUNTIME_LOG_BACKUP_ID = 'SFS2X_RuntimeLog';
419
+ this.FULL_BACKUP_ID = 'SFS2X_Logs';
652
420
 
653
- this.REQ_REFRESH_WORDS_FILE = 'refreshWordsFiles';
654
- this.REQ_EDIT_WORDS_FILE = 'editWordsFile';
655
- this.REQ_SAVE_WORDS_FILE = 'saveWordsFile';
656
- this.REQ_DELETE_WORDS_FILE = 'delWordsFile';
421
+ // Outgoing requests
422
+ this.REQ_GET_RUNTIME_LOG = 'getRunLog';
423
+ this.REQ_GET_BOOT_LOG = 'getBootLog';
424
+ this.REQ_GET_BACKUPS_STATUS = 'getBakStatus';
425
+ this.REQ_BACKUP_BOOT_LOG = 'bakBootLog';
426
+ this.REQ_BACKUP_RUNTIME_LOG = 'bakRunLog';
427
+ this.REQ_BACKUP_FULL_LOGS = 'bakFullLogs';
428
+ this.REQ_DELETE_BACKUP = 'delBackup';
657
429
 
658
430
  // Incoming responses
659
- this.RESP_ZONES = 'zones';
660
-
661
- this.RESP_ZONE_CONFIG = 'zoneConfig';
662
- this.RESP_ZONE_CONFIG_UPDATE_CONFIRM = 'zoneCfgUpd';
663
- this.RESP_ZONE_ADDED = 'zoneAdded';
664
- this.RESP_ZONE_REFUSED = 'zoneRefused';
665
- this.RESP_ZONE_DELETED = 'zoneDel';
666
- this.RESP_ZONE_ACTIVATED = 'zoneAct';
667
- this.RESP_ZONE_ACTIVATION_ERROR = 'zoneActErr';
668
-
669
- this.RESP_ROOM_CONFIG = 'roomConfig';
670
- this.RESP_ROOM_CONFIG_UPDATE_CONFIRM = 'roomCfgUpd';
671
- this.RESP_ROOM_ADDED = 'roomAdded';
672
- this.RESP_ROOM_REFUSED = 'roomRefused';
673
- this.RESP_ROOM_DELETED = 'roomDel';
674
-
675
- this.RESP_REFRESH_WORDS_FILES = 'refreshWordsFiles';
676
- this.RESP_WORDS_FILE_CONTENT = 'wordsFile';
677
- this.RESP_WORDS_FILE_ERROR = 'wordsFileErr';
431
+ this.RESP_INIT_ERROR = 'initErr';
432
+ this.RESP_RUNTIME_LOG_ERROR = 'runLogErr';
433
+ this.RESP_RUNTIME_LOG_INVALID = 'runLogInv';
434
+ this.RESP_RUNTIME_LOG = 'runLog';
435
+ this.RESP_BOOT_LOG_ERROR = 'bootLogErr';
436
+ this.RESP_BOOT_LOG = 'bootLog';
437
+ this.RESP_BACKUPS_STATUS = 'bakStatus';
438
+ this.RESP_DELETE_BACKUP_FAILED = 'delBakFail';
439
+ this.RESP_BACKUP_ERROR = 'bakError';
440
+ this.RESP_BACKUP_WARNING = 'bakWarn';
678
441
  }
679
442
 
680
443
  //------------------------------------
681
444
  // COMMON MODULE INTERFACE METHODS
682
445
  // This members are used by the main controller
683
446
  // to communicate with the module's controller.
684
- // This methods override those in BaseModule class.
685
447
  //------------------------------------
686
448
 
687
449
  initialize(idData, shellController)
@@ -689,28 +451,29 @@ class ZoneConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseMo
689
451
  // Call super method
690
452
  super.initialize(idData, shellController);
691
453
 
692
- // Create interface builder instance
693
- this._interfaceBuilder = new _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_3__["ConfigInterfaceBuilder"]();
694
-
695
- // Set listener for custom actions triggered by configuration interface
696
- $('#znc-tabNavigator').on('value-set', $.proxy(this._onConfigValueSet, this));
454
+ // Initialize scrolling tabs
455
+ $('#lgv-tabNavigator > #tabs').scrollingTabs({
456
+ bootstrapVersion: 4,
457
+ scrollToTabEdge: true,
458
+ enableSwiping: true,
459
+ disableScrollArrowsOnFullyScrolled: true,
460
+ cssClassLeftArrow: 'fa fa-chevron-left',
461
+ cssClassRightArrow: 'fa fa-chevron-right'
462
+ });
697
463
 
698
- // Initialize Zones/Rooms treeview
699
- this._treeview = $('#znc-treeView').kendoTreeView({
700
- loadOnDemand: false,
701
- dataTextField: 'name',
702
- template: kendo.template('<span class="# if (!item.active) { # inactive-list-item # } #">#: item.name #</span>'),
703
- change: $.proxy(this._onZoneRoomChange, this),
704
- }).data('kendoTreeView');
464
+ // Add listener to tab shown event
465
+ $('a[data-toggle="tab"]').on('shown.bs.tab', $.proxy(this._onTabShown, this));
705
466
 
706
- // Listen to treeview double-click event
707
- $('#znc-treeView').on('dblclick', $.proxy(this._onTreeItemDoubleClick, this));
467
+ // Initialize log lines dropdown
468
+ this._logLinesDD = $('#lgv-logLinesDD').kendoDropDownList({
469
+ valueTemplate: '<span class="text-muted pr-1">Log entries:</span><span>#:data.text#</span>',
470
+ }).data('kendoDropDownList');
708
471
 
709
- // Request zones & rooms list to server instance
710
- this.sendExtensionRequest(this.REQ_GET_ZONES);
472
+ // Initialize load button
473
+ $('#lgv-loadBt').on('click', $.proxy(this._onRuntimeLogLoadBtClick, this));
711
474
 
712
- // Initialize progress bar
713
- $('#znc-progressBar').kendoProgressBar({
475
+ // Initialize progress bars
476
+ $('.progress-bar').kendoProgressBar({
714
477
  min: 0,
715
478
  max: 100,
716
479
  value: false,
@@ -720,25 +483,117 @@ class ZoneConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseMo
720
483
  }
721
484
  });
722
485
 
723
- // Add listeners to utility buttons
724
- $('#znc-addZoneButton').on('click', $.proxy(this._onAddZoneClick, this));
725
- $('#znc-addRoomButton').on('click', $.proxy(this._onAddRoomClick, this));
726
- $('#znc-editButton').on('click', $.proxy(this._onEditClick, this));
727
- $('#znc-removeButton').on('click', $.proxy(this._onRemoveClick, this));
728
- $('#znc-activateButton').on('click', $.proxy(this._onActivateClick, this));
729
-
730
- // Add listener to interface buttons
731
- $('#znc-cancelButton').on('click', $.proxy(this._onCancelClick, this));
732
- $('#znc-reloadButton').on('click', $.proxy(this._onReloadClick, this));
733
- $('#znc-submitButton').on('click', $.proxy(this._onSubmitClick, this));
734
-
735
- // Save ref to words files manager and add custom event listeners
736
- this._wordsFilesManager = document.getElementById('znc-wordsFilesManager');
737
- $(this._wordsFilesManager).on(this._wordsFilesManager.REFRESH_WORDS_FILES_CLICK_EVENT, $.proxy(this._onWordsFileReloadClick, this));
738
- $(this._wordsFilesManager).on(this._wordsFilesManager.EDIT_WORDS_FILE_CLICK_EVENT, $.proxy(this._onWordsFileEditClick, this));
739
- $(this._wordsFilesManager).on(this._wordsFilesManager.SAVE_WORDS_FILE_CLICK_EVENT, $.proxy(this._onWordsFileSaveClick, this));
740
- $(this._wordsFilesManager).on(this._wordsFilesManager.REMOVE_WORDS_FILE_CLICK_EVENT, $.proxy(this._onWordsFileRemoveClick, this));
741
- $(this._wordsFilesManager).on(this._wordsFilesManager.ASSIGN_WORDS_FILE_CLICK_EVENT, $.proxy(this._onWordsFileAssignClick, this));
486
+ // Initialize level filter dropdown
487
+ this._levelFilterDD = $('#lgv-levelDD').kendoDropDownList({
488
+ dataSource: [this.ANY_LEVEL].concat(this.LEVELS),
489
+ change: $.proxy(this._onFilterChange, this)
490
+ }).data('kendoDropDownList');
491
+
492
+ // Initialize class filter dropdown
493
+ this._classFilterDD = $('#lgv-classDD').kendoDropDownList({
494
+ change: $.proxy(this._onFilterChange, this)
495
+ }).data('kendoDropDownList');
496
+
497
+ // Initialize message filter input
498
+ $('#lgv-messageIn').on('input', $.proxy(this._onFilterChange, this));
499
+
500
+ // Initialize clear button
501
+ $('#lgv-clearFilterBt').on('click', $.proxy(this._onClearFilterClick, this));
502
+
503
+ // Initialize export button
504
+ $('#lgv-exportRuntimeLogBt').on('click', $.proxy(this._onExportRuntimeLogBtClick, this));
505
+
506
+ // Initialize runtime log grid
507
+ this._runtimeLogGrid = $('#lgv-runtimeLogGrid').kendoGrid({
508
+ scrollable: true,
509
+ sortable: false,
510
+ //resizable: true,
511
+ selectable: false,
512
+ columns:
513
+ [
514
+ {
515
+ field: 'dateTime',
516
+ width: 150,
517
+ title: 'Date/Time',
518
+ },
519
+ {
520
+ field: 'level',
521
+ width: 100,
522
+ title: 'Level',
523
+ },
524
+ {
525
+ field: 'thread',
526
+ width: 150,
527
+ title: 'Thread',
528
+ },
529
+ {
530
+ field: 'clazz',
531
+ width: 250,
532
+ title: 'Class',
533
+ },
534
+ {
535
+ field: 'message',
536
+ width: 1000,
537
+ title: 'Message',
538
+ },
539
+ ],
540
+ noRecords: {
541
+ template: 'No log entries to display.'
542
+ },
543
+ dataSource: []
544
+ }).data('kendoGrid');
545
+
546
+ // Initialize boot log view buttons
547
+ $('#lgv-exportBootLogBt').on('click', $.proxy(this._onExportBootLogBtClick, this));
548
+ $('#lgv-switchBootLogColorBt').on('click', $.proxy(this._onSwitchBootLogColorBtClick, this));
549
+
550
+ // Initialize generate backup buttons
551
+ $('#lgv-bootLogBackupCard .backup-button').on('click', $.proxy(this._onBootLogGenerateBtClick, this));
552
+ $('#lgv-runtimeLogBackupCard .backup-button').on('click', $.proxy(this._onRuntimeLogGenerateBtClick, this));
553
+ $('#lgv-fullLogBackupCard .backup-button').on('click', $.proxy(this._onFullLogsGenerateBtClick, this));
554
+
555
+ this._initBackupCard('#lgv-bootLogBackupCard');
556
+ this._initBackupCard('#lgv-runtimeLogBackupCard');
557
+ this._initBackupCard('#lgv-fullLogBackupCard');
558
+
559
+ // Initialize download grid
560
+ this._downloadGrid = $('#lgv-downloadGrid').kendoGrid({
561
+ scrollable: true,
562
+ sortable: true,
563
+ //resizable: true,
564
+ selectable: 'row',
565
+ columns:
566
+ [
567
+ {
568
+ field: 'date',
569
+ width: 100,
570
+ title: 'Date',
571
+ },
572
+ {
573
+ field: 'time',
574
+ width: 100,
575
+ title: 'Time',
576
+ },
577
+ {
578
+ field: 'name',
579
+ width: 300,
580
+ title: 'Filename',
581
+ },
582
+ {
583
+ field: 'size',
584
+ width: 100,
585
+ title: 'Size',
586
+ },
587
+ ],
588
+ noRecords: {
589
+ template: 'No backups available.'
590
+ },
591
+ change: $.proxy(this._onDownloadGridSelectionChange, this),
592
+ dataSource: []
593
+ }).data('kendoGrid');
594
+
595
+ // Initialize delete button
596
+ $('#lgv-deleteBt').on('click', $.proxy(this._onDeleteBtClick, this));
742
597
  }
743
598
 
744
599
  destroy()
@@ -746,1036 +601,616 @@ class ZoneConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseMo
746
601
  // Call super method
747
602
  super.destroy();
748
603
 
749
- // Remove tree view doubleclick listener
750
- $('#znc-treeView').off('dblclick');
751
-
752
- // Remove listener for custom actions triggered by configuration interface
753
- $('#znc-tabNavigator').off('value-set');
754
-
755
- // Remove listener for zone/room activation event
756
- $('#znc-addZoneButton').off('click');
757
- $('#znc-addRoomButton').off('click');
758
- $('#znc-editButton').off('click');
759
- $('#znc-removeButton').off('click');
760
- $('#znc-activateButton').off('click');
761
-
762
- // Remove interface buttons click listeners
763
- $('#znc-cancelButton').off('click');
764
- $('#znc-reloadButton').off('click');
765
- $('#znc-submitButton').off('click');
766
-
767
- // Remove listeners to words files manager
768
- $(this._wordsFilesManager).off(this._wordsFilesManager.REFRESH_WORDS_FILES_CLICK_EVENT);
769
- $(this._wordsFilesManager).off(this._wordsFilesManager.EDIT_WORDS_FILE_CLICK_EVENT);
770
- $(this._wordsFilesManager).off(this._wordsFilesManager.SAVE_WORDS_FILE_CLICK_EVENT);
771
- $(this._wordsFilesManager).off(this._wordsFilesManager.REMOVE_WORDS_FILE_CLICK_EVENT);
772
- $(this._wordsFilesManager).off(this._wordsFilesManager.ASSIGN_WORDS_FILE_CLICK_EVENT);
773
-
774
- // Clear tabs container
775
- this._clearTabs();
604
+ // Remove listener to tab shown event
605
+ $('a[data-toggle="tab"]').off('shown.bs.tab');
606
+
607
+ // Destroy scrolling tabs
608
+ $('#lgv-tabNavigator #tabs').scrollingTabs('destroy');
609
+
610
+ // Remove click listeners
611
+ $('#lgv-loadBt').off('click');
612
+ $('#lgv-exportBootLogBt').off('click');
613
+ $('#lgv-switchBootLogColorBt').off('click');
614
+ $('#lgv-bootLogBackupCard .backup-button').off('click');
615
+ $('#lgv-runtimeLogBackupCard .backup-button').off('click');
616
+ $('#lgv-clearFilterBt').off('click');
617
+ $('#lgv-deleteBt').off('click');
618
+
619
+ $('#lgv-messageIn').off('input');
776
620
  }
777
621
 
778
622
  onExtensionCommand(command, data)
779
623
  {
780
- const username = data.getUtfString('user');
781
-
782
- /****** ZONES & ROOMS ******/
624
+ // Error during initialization (unable to access log4j configuration file)
625
+ if (command == this.RESP_INIT_ERROR)
626
+ {
627
+ const error = data.getUtfString('error');
783
628
 
784
- // Zones & rooms list received
785
- if (command == this.RESP_ZONES)
786
- this._populateTree(data);
629
+ // Show an alert
630
+ this.shellCtrl.showSimpleAlert(error, true);
787
631
 
788
- // Zone or room configuration data received
789
- else if (command == this.RESP_ZONE_CONFIG || command == this.RESP_ROOM_CONFIG)
790
- {
791
- // Build user interface based on received data
792
- this._interfaceBuilder.buildInterface(data.getSFSArray('settings'), 'znc-tabNavigator', false);
632
+ // Set all tabs to show errors
633
+ this._switchBootViewStack('lgv-bootLogErrorView');
634
+ this._switchRuntimeViewStack('lgv-runtimeLogErrorView');
635
+ this._switchDownloadViewStack('lgv-downloadErrorView');
793
636
 
794
- // Enable scrolling tabs (if needed)
795
- if (this._reinitTabs)
796
- {
797
- $('#znc-tabNavigator #tabs').scrollingTabs({
798
- bootstrapVersion: 4,
799
- scrollToTabEdge: true,
800
- enableSwiping: true,
801
- disableScrollArrowsOnFullyScrolled: true,
802
- cssClassLeftArrow: 'fa fa-chevron-left',
803
- cssClassRightArrow: 'fa fa-chevron-right'
804
- });
805
- }
637
+ // Disable runtime log controls
638
+ this._enableRuntimeLogControls(false);
806
639
 
807
- // Enable interface
808
- this._enableConfigInterface(true);
640
+ this._initFailed = true;
809
641
  }
810
642
 
811
- /****** ZONES ******/
812
-
813
- // Zone configuration update confirmation
814
- else if (command == this.RESP_ZONE_CONFIG_UPDATE_CONFIRM)
643
+ // Error responses
644
+ else if (command == this.RESP_BOOT_LOG_ERROR || command == this.RESP_RUNTIME_LOG_ERROR)
815
645
  {
816
- // If a 'name' parameter is received, it means the zone name changed, and we have to update the zones list
817
- if (data.getUtfString('zName') != null)
818
- this._updateZoneNameInList(data.getInt('zId'), data.getUtfString('zName'));
646
+ const error = data.getUtfString('error');
819
647
 
820
- // If the current user is the updater, show a notification; otherwise, show a dialog box suggesting to reload
821
- if (username == this.smartFox.mySelf.name)
648
+ // Show an alert
649
+ this.shellCtrl.showSimpleAlert(error, true);
650
+
651
+ if (command == this.RESP_BOOT_LOG_ERROR)
822
652
  {
823
- // Enable interface
824
- this._enableConfigInterface(true);
653
+ // Set tab to show error
654
+ this._switchBootViewStack('lgv-bootLogErrorView');
825
655
 
826
- // Display notification
827
- this.shellCtrl.showNotification('Zone modified', `Zone settings updated successfully; changes will be applied on next <strong>server restart</strong>`);
656
+ // Disable boot log backup generation
657
+ this._bootLogBackupUnavailable = true;
658
+ this._disableBackupInterface(false);
828
659
 
829
- // Reset the 'modified' flag
830
- this._interfaceBuilder.resetIsModified();
660
+ this._bootLogRequested = true;
831
661
  }
832
- else
662
+
663
+ if (command == this.RESP_RUNTIME_LOG_ERROR)
833
664
  {
834
- // An alert box is displayed if the user is currently editing the same zone
835
- if (data.getInt('zId') == this._editedZoneId)
836
- {
837
- // Show alert
838
- this.shellCtrl.showSimpleAlert(`Administrator ${username} has modified the Zone you are currently editing; please reload to update your view.`);
665
+ // Disable controls
666
+ this._enableRuntimeLogControls(false);
839
667
 
840
- // Disable submit button
841
- $('#znc-submitButton').attr('disabled', true);
842
- }
843
- else
844
- {
845
- // Display notification
846
- if (data.getUtfString('zName') != null)
847
- this.shellCtrl.showNotification('Zone renamed', `Administrator ${username} has changed the name on one of the Zones`);
848
- }
668
+ // Remove loading bar (runtime log)
669
+ this._switchRuntimeViewStack('lgv-runtimeLogErrorView');
670
+
671
+ // Disable runtime log backup download
672
+ this._runtimeLogBackupUnavailable = true;
673
+ this._disableBackupInterface(false);
849
674
  }
850
675
  }
851
676
 
852
- // New zone added
853
- else if (command == this.RESP_ZONE_ADDED)
677
+ // Modified conversion pattern in the log4j properties file: unable to parse the log
678
+ else if (command == this.RESP_RUNTIME_LOG_INVALID)
854
679
  {
855
- const zoneName = data.getSFSObject('zone').getUtfString('name');
856
-
857
- // If the current user is the updater, reset the interface; otherwise, just show a notification
858
- if (username == this.smartFox.mySelf.name)
859
- {
860
- // Reset interface
861
- this._onCancelClick();
680
+ // Disable controls
681
+ this._enableRuntimeLogControls(false);
862
682
 
863
- // Display notification
864
- this.shellCtrl.showNotification('Zone added', `Zone '${zoneName}' created successfully`);
865
- }
866
- else
867
- {
868
- // Display notification
869
- this.shellCtrl.showNotification('Zone added', `Administrator ${username} created Zone '${zoneName}'`);
870
- }
683
+ // Fill in error message
684
+ $('#lgv-convPattName').text(data.getUtfString('param'));
685
+ $('#lgv-convPattVal').text(data.getUtfString('value'));
871
686
 
872
- // Add new zone to tree
873
- let zonesDS = this._treeview.dataSource;
874
- zonesDS.add(this._createZoneObject(data.getSFSObject('zone')));
875
- zonesDS.sync();
687
+ // Remove loading bar (runtime log)
688
+ this._switchRuntimeViewStack('lgv-invConvPattView');
876
689
  }
877
690
 
878
- // New zone creation refused due to invalid zone name
879
- else if (command == this.RESP_ZONE_REFUSED)
691
+ // Runtime log received
692
+ else if (command == this.RESP_RUNTIME_LOG)
880
693
  {
881
- // Re-enable interface
882
- this._enableConfigInterface(true);
694
+ let classes = [];
695
+ classes.push(this.ANY_CLASS);
883
696
 
884
- // Show warning
885
- this.shellCtrl.showSimpleAlert('Zone configuration can\'t be saved because another Zone with the same name already exists.', true);
886
- }
697
+ let logEntries = data.getUtfStringArray(this.LOG_DATA_LABEL);
698
+ let separator = data.getUtfString('sep');
699
+ let columns = data.getInt('cols');
700
+ let dsArr = [];
887
701
 
888
- // Existing zone deleted
889
- else if (command == this.RESP_ZONE_DELETED)
890
- {
891
- // If the current user is the deleter, reset the interface; otherwise, just show a notification
892
- if (username == this.smartFox.mySelf.name)
893
- {
894
- // Re-enable interface
895
- this._enableListInterface(true);
702
+ this._totalRuntimeLogEntries = logEntries.length;
896
703
 
897
- // Display notification
898
- this.shellCtrl.showNotification('Zone removed', `Zone '${data.getUtfString('zName')}' deleted successfully`);
899
- }
900
- else
704
+ // Parse log entries
705
+ // We can't use the split method because there could be instances of the separator in the log message too
706
+ for (let e = 0; e < logEntries.length; e++)
901
707
  {
902
- // An alert box is displayed if the user is currently editing the same zone
903
- if (data.getInt('zId') == this._editedZoneId)
904
- {
905
- // Show alert
906
- this.shellCtrl.showSimpleAlert(`Administrator ${username} has deleted the Zone you are currently modifying; you have to cancel your editing.`);
907
-
908
- // Disable submit and reload buttons
909
- $('#znc-reloadButton').attr('disabled', true);
910
- $('#znc-submitButton').attr('disabled', true);
911
- }
912
- else
913
- {
914
- // Display notification
915
- this.shellCtrl.showNotification('Zone removed', `Administrator ${username} deleted Zone '${data.getUtfString('zName')}'`);
916
- }
917
- }
708
+ const logEntry = logEntries[e];
918
709
 
919
- // Reset selection if the currently selected item or its parent is being removed
920
- let selectedNode = this._treeview.select();
921
- let selectedDataItem = this._treeview.dataItem(selectedNode);
922
- if (selectedDataItem)
923
- {
924
- if (selectedDataItem.type == this.ITEM_TYPE_ZONE && selectedDataItem.id == data.getInt('zId'))
925
- this._deselectTreeItem();
710
+ let logEntryData = [];
711
+ let startIndex = 0;
926
712
 
927
- if (selectedDataItem.type == this.ITEM_TYPE_ROOM)
713
+ for (let c = 0; c < columns - 1; c++)
928
714
  {
929
- let parentDataItem = this._treeview.dataItem(this._treeview.parent(selectedNode));
930
-
931
- if (parentDataItem.id == data.getInt('zId'))
932
- this._deselectTreeItem();
715
+ let endIndex = logEntry.indexOf(separator, startIndex);
716
+ logEntryData.push(logEntry.substring(startIndex, endIndex));
717
+ startIndex = endIndex + separator.length;
933
718
  }
934
- }
935
-
936
- // Remove zone from tree
937
- let dataItem = this._getZoneDataItemById(data.getInt('zId'));
938
- let zonesDS = this._treeview.dataSource;
939
- zonesDS.remove(dataItem);
940
- zonesDS.sync();
941
- }
942
719
 
943
- // Zone activated
944
- else if (command == this.RESP_ZONE_ACTIVATED)
945
- {
946
- // Set zone activation status
947
- const zoneName = this._setZoneActivationStatus(data.getInt('zId'), data.getUtfString('actRooms'), true);
720
+ if (startIndex < logEntry.length)
721
+ logEntryData.push(logEntry.substring(startIndex));
948
722
 
949
- // Display notification
950
- if (username == this.smartFox.mySelf.name)
951
- this.shellCtrl.showNotification('Zone activated', `Zone '${zoneName}' activated successfully`);
952
- else
953
- this.shellCtrl.showNotification('Zone activated', `Administrator ${username} activated Zone '${zoneName}'`);
954
- }
723
+ // Fill datagrid's dataprovider
724
+ let rle = _data_runtime_log_entry__WEBPACK_IMPORTED_MODULE_1__["RuntimeLogEntry"].fromArray(separator, logEntryData);
725
+ dsArr.push(rle);
955
726
 
956
- // Zone activation error
957
- else if (command == this.RESP_ZONE_ACTIVATION_ERROR)
958
- {
959
- // Set zone activation status
960
- this._setZoneActivationStatus(data.getInt('zId'), '', false);
727
+ // Add class to filtering dropdown
728
+ if (classes.indexOf(rle.clazz) < 0)
729
+ classes.push(rle.clazz);
730
+ }
961
731
 
962
- // Show alert
963
- this.shellCtrl.showSimpleAlert(data.getUtfString('error'), true);
964
- }
732
+ // Show classes list
733
+ classes.sort(function (a, b) {
734
+ return a.localeCompare(b);
735
+ });
965
736
 
966
- /****** ROOMS ******/
737
+ this._classFilterDD.setDataSource(classes);
738
+ this._classFilterDD.select(0);
967
739
 
968
- // Room configuration update confirmation
969
- else if (command == this.RESP_ROOM_CONFIG_UPDATE_CONFIRM)
970
- {
971
- if (data.getUtfString('rName') != null)
972
- this._updateRoomNameInList(data.getInt('zId'), data.getInt('rId'), data.getUtfString('rName'));
740
+ // Assign data source to grid
741
+ let ds = new kendo.data.DataSource({
742
+ data: dsArr
743
+ })
973
744
 
974
- // If the current user is the updater, show a notification; otherwise, show a dialog box suggesting to reload
975
- if (username == this.smartFox.mySelf.name)
976
- {
977
- // Enable interface
978
- this._enableConfigInterface(true);
979
-
980
- // Display notification
981
- this.shellCtrl.showNotification('Room modified', `Room settings updated successfully; changes will be applied on next <strong>server restart</strong>`);
745
+ this._setRuntimeLogDataSource(ds);
982
746
 
983
- // Reset the 'modified' flag
984
- this._interfaceBuilder.resetIsModified();
985
- }
986
- else
987
- {
988
- // An alert box is displayed if the user is currently editing the same room
989
- if (data.getInt('rId') == this._editedRoomId)
990
- {
991
- // Show alert
992
- this.shellCtrl.showSimpleAlert(`Administrator ${username} has modified the Room you are currently editing; please reload to update your view.`);
747
+ // Re-enable log loading controls
748
+ this._enableRuntimeLogControls(true);
993
749
 
994
- // Disable submit button
995
- $('#znc-submitButton').attr('disabled', true);
996
- }
997
- else
998
- {
999
- // Display notification
1000
- if (data.getUtfString('rName') != null)
1001
- this.shellCtrl.showNotification('Room renamed', `Administrator ${username} has changed the name on one of the Rooms`);
1002
- }
1003
- }
750
+ // Remove loading bar
751
+ this._switchRuntimeViewStack('lgv-runtimeLogView');
1004
752
  }
1005
753
 
1006
- // New room added
1007
- else if (command == this.RESP_ROOM_ADDED)
754
+ // Boot log received
755
+ else if (command == this.RESP_BOOT_LOG)
1008
756
  {
1009
- const roomData = data.getSFSObject('room');
1010
- const zoneId = data.getInt('zId');
1011
-
1012
- let zonesDS = this._treeview.dataSource;
1013
- let zoneItem = zonesDS.get(zoneId);
757
+ const bootLogEntries = data.getSFSArray(this.LOG_DATA_LABEL);
758
+ let text = '';
1014
759
 
1015
- // If the current user is the updater, reset the interface; otherwise, just show a notification
1016
- if (username == this.smartFox.mySelf.name)
1017
- {
1018
- // Reset interface
1019
- this._onCancelClick();
1020
-
1021
- // Display notification
1022
- this.shellCtrl.showNotification('Room added', `Room '${roomData.getUtfString('name')}' created successfully`);
1023
- }
1024
- else
1025
- {
1026
- // Display notification
1027
- this.shellCtrl.showNotification('Room added', `Administrator ${username} created Room '${roomData.getUtfString('name')}' in Zone '${zoneItem.name}'`);
1028
- }
1029
-
1030
- // Add new room to tree
1031
- zoneItem.append(this._createRoomObject(roomData, zoneId));
1032
- zonesDS.sync();
1033
-
1034
- // Expand zone node where room was added
1035
- this._treeview.expand(this._treeview.select());
1036
- }
760
+ for (let i = 0; i < bootLogEntries.size(); i++)
761
+ text += bootLogEntries.getUtfString(i) + '\n';
1037
762
 
1038
- // New room creation refused due to invalid room name
1039
- else if (command == this.RESP_ROOM_REFUSED)
1040
- {
1041
- // Re-enable interface
1042
- this._enableConfigInterface(true);
763
+ $('#lgv-bootLogText').text(text);
1043
764
 
1044
- // Show warning
1045
- this.shellCtrl.showSimpleAlert('Room configuration can\'t be saved because another Room with the same name already exists.', true);
765
+ // Remove loading bar
766
+ this._switchBootViewStack('lgv-bootLogView');
1046
767
  }
1047
768
 
1048
- // Existing room deleted
1049
- else if (command == this.RESP_ROOM_DELETED)
769
+ // Logs backups status received
770
+ else if (command == this.RESP_BACKUPS_STATUS)
1050
771
  {
1051
- let zoneItem = this._getZoneDataItemById(data.getInt('zId'));
1052
- let roomItem = this._getRoomDataItemById(data.getInt('zId'), data.getInt('rId'));
1053
-
1054
- // If the current user is the deleter, reset the interface; otherwise, just show a notification
1055
- if (username == this.smartFox.mySelf.name)
1056
- {
1057
- // Re-enable interface
1058
- this._enableListInterface(true);
772
+ // Show/hide operation in progress message
773
+ this._disableBackupInterface(data.getBool('running'), data.getUtfString('type'));
1059
774
 
1060
- // Display notification
1061
- this.shellCtrl.showNotification('Room removed', `Room '${roomItem.name}' deleted successfully`);
1062
- }
1063
- else
775
+ // Backup files list
776
+ if (data.containsKey('files'))
1064
777
  {
1065
- // An alert box is displayed if the user is currently editing the same room
1066
- if (data.getInt('rId') == this._editedRoomId)
1067
- {
1068
- // Show alert
1069
- this.shellCtrl.showSimpleAlert(`Administrator ${username} has deleted the Room you are currently modifying; you have to cancel your editing.`);
1070
-
1071
- // Disable submit and reload buttons
1072
- $('#znc-reloadButton').attr('disabled', true);
1073
- $('#znc-submitButton').attr('disabled', true);
1074
- }
1075
- else
1076
- {
1077
- // Display notification
1078
- this.shellCtrl.showNotification('Room removed', `Administrator ${username} deleted Room '${roomItem.name}' from Zone '${zoneItem.name}'`);
1079
- }
1080
- }
778
+ let files = data.getSFSArray('files');
1081
779
 
1082
- // Reset selection if the currently selected item or its parent is being removed
1083
- let selectedNode = this._treeview.select();
1084
- let selectedDataItem = this._treeview.dataItem(selectedNode);
1085
- if (selectedDataItem)
1086
- {
1087
- if (selectedDataItem.type == this.ITEM_TYPE_ROOM && selectedDataItem.id == data.getInt('rId'))
1088
- this._deselectTreeItem();
1089
- }
780
+ let lastBootLogBackupFound = false;
781
+ let lastRuntimeLogBackupFound = false;
782
+ let lastFullBackupFound = false;
1090
783
 
1091
- // Remove room from tree
1092
- zoneItem.children.remove(roomItem);
1093
- this._treeview.dataSource.sync();
1094
- }
784
+ let backupsList = [];
1095
785
 
1096
- /****** WORDS FILES ******/
786
+ const webServerProtocol = (data.containsKey('protocol') ? data.getUtfString('protocol') : 'http') + '://';
787
+ const webServerPort = (data.containsKey('port') ? ':' + data.getInt('port') : '');
1097
788
 
1098
- // Words files list received
1099
- else if (command == this.RESP_REFRESH_WORDS_FILES)
1100
- {
1101
- this._wordsFilesManager.refreshWordsFilesList(data.getSFSArray('wf'), username == this.smartFox.mySelf.name);
789
+ let totalSize = 0;
1102
790
 
1103
- // If another user caused a refresh (for example deleting a file, or adding a new one) show a notification
1104
- if (username != null && username != this.smartFox.mySelf.name && this._editedZoneId > -1)
1105
- this.shellCtrl.showNotification('Words files modified', `Administrator ${username} has added, modified or deleted a words file.`);
1106
- }
1107
-
1108
- // Words file content received
1109
- else if (command == this.RESP_WORDS_FILE_CONTENT)
1110
- {
1111
- this._wordsFilesManager.editWordsFile(data.getUtfString('filename'), data.getText('content'));
1112
- }
1113
-
1114
- // Words file error (edit/save)
1115
- else if (command == this.RESP_WORDS_FILE_ERROR)
1116
- {
1117
- // Enable buttons
1118
- this._wordsFilesManager.enabled = true;
791
+ for (let f = 0; f < files.size(); f++)
792
+ {
793
+ const file = files.getSFSObject(f);
1119
794
 
1120
- // Show alert
1121
- this.shellCtrl.showSimpleAlert(data.getUtfString('error'), true);
1122
- }
795
+ const filePath = file.getUtfString('path');
1123
796
 
1124
- // else if ()
1125
- }
797
+ const fileObj = {};
798
+ fileObj.path = filePath;
799
+ fileObj.url = webServerProtocol + this.smartFox.config.host + webServerPort + '/' + filePath;
800
+ fileObj.name = filePath.substr(filePath.lastIndexOf('/') + 1);
801
+ fileObj.date = file.getUtfString('date');
802
+ fileObj.time = file.getUtfString('time');
803
+ fileObj.size = Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["bytesToSize"])(file.getLong('size'), 2);
1126
804
 
1127
- //---------------------------------
1128
- // UI EVENT LISTENERS
1129
- //---------------------------------
805
+ totalSize += file.getLong('size');
1130
806
 
1131
- _onTreeItemDoubleClick(e)
1132
- {
1133
- // Get event target's closest tree node
1134
- let treeNode = $(e.target).closest('.k-item[role=treeitem]');
807
+ // Check if this is a boot log backup
808
+ if (!lastBootLogBackupFound)
809
+ {
810
+ if (fileObj.name.startsWith(this.BOOT_LOG_BACKUP_ID))
811
+ {
812
+ this._fillBackupCard('#lgv-bootLogBackupCard', fileObj);
1135
813
 
1136
- // Get associated data item
1137
- let dataItem = this._treeview.dataItem(treeNode);
814
+ lastBootLogBackupFound = true;
815
+ }
816
+ }
1138
817
 
1139
- // Load configuration
1140
- this._loadConfiguration(dataItem.type);
1141
- }
818
+ // Check if this is a runtime log backup
819
+ if (!lastRuntimeLogBackupFound)
820
+ {
821
+ if (fileObj.name.startsWith(this.RUNTIME_LOG_BACKUP_ID))
822
+ {
823
+ this._fillBackupCard('#lgv-runtimeLogBackupCard', fileObj);
1142
824
 
1143
- _onZoneRoomChange()
1144
- {
1145
- // Reset utility buttons
1146
- this._setUtilityButtonsState(this._selectedItem);
1147
- }
825
+ lastRuntimeLogBackupFound = true;
826
+ }
827
+ }
1148
828
 
1149
- // Utility buttons listeners
829
+ // Check if this is a full backup
830
+ if (!lastFullBackupFound)
831
+ {
832
+ if (fileObj.name.startsWith(this.FULL_BACKUP_ID))
833
+ {
834
+ this._fillBackupCard('#lgv-fullLogBackupCard', fileObj);
1150
835
 
1151
- _onAddZoneClick()
1152
- {
1153
- // Deselect list item
1154
- this._deselectTreeItem();
836
+ lastFullBackupFound = true;
837
+ }
838
+ }
1155
839
 
1156
- // Load configuration
1157
- this._loadConfiguration(this.ITEM_TYPE_ZONE);
1158
- }
840
+ // Populate logs list
841
+ backupsList.push(fileObj);
842
+ }
1159
843
 
1160
- _onAddRoomClick()
1161
- {
1162
- // Select parent list item
1163
- this._selectParentTreeItem();
844
+ // Show total backups size
845
+ $('#lgv-logSizeLb').html(`Total size: <strong>${Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["bytesToSize"])(totalSize, 2, 'KB')}</strong>`);
1164
846
 
1165
- // Load configuration
1166
- this._loadConfiguration(this.ITEM_TYPE_ROOM);
1167
- }
847
+ // Assign data source to grid
848
+ this._setDownloadGridDataSource(backupsList);
849
+ this._onDownloadGridSelectionChange();
1168
850
 
1169
- _onEditClick()
1170
- {
1171
- // Load configuration
1172
- this._loadConfiguration(this._selectedItem.type);
1173
- }
851
+ // Hide links to latest files if not available
852
+ if (!lastBootLogBackupFound)
853
+ this._fillBackupCard('#lgv-bootLogBackupCard', null);
1174
854
 
1175
- _onRemoveClick()
1176
- {
1177
- this.shellCtrl.showConfirmWarning(`Are you sure you want to delete the selected ${this._selectedItem.type == this.ITEM_TYPE_ZONE ? 'Zone' : 'Room'} configuration?`, $.proxy(this._onRemoveConfirm, this));
1178
- }
855
+ if (!lastRuntimeLogBackupFound)
856
+ this._fillBackupCard('#lgv-runtimeLogBackupCard', null);
1179
857
 
1180
- _onRemoveConfirm()
1181
- {
1182
- // Disable zone/room selection list
1183
- this._enableListInterface(false);
858
+ if (!lastFullBackupFound)
859
+ this._fillBackupCard('#lgv-fullLogBackupCard', null);
1184
860
 
1185
- let params = new SFS2X.SFSObject();
861
+ if (data.containsKey('message'))
862
+ {
863
+ // Display notification
864
+ this.shellCtrl.showNotification(`Log backup warning`, data.getUtfString('message'));
865
+ }
866
+ }
1186
867
 
1187
- // Request zone removal
1188
- if (this._selectedItem.type == this.ITEM_TYPE_ZONE)
1189
- {
1190
- params.putInt('zId', this._selectedItem.id);
1191
- this.sendExtensionRequest(this.REQ_DELETE_ZONE_CONFIG, params);
1192
- }
1193
- else
1194
- {
1195
- params.putInt('zId', this._selectedItemParent.id);
1196
- params.putInt('rId', this._selectedItem.id);
1197
- this.sendExtensionRequest(this.REQ_DELETE_ROOM_CONFIG, params);
868
+ // Set download view to main
869
+ this._switchDownloadViewStack('lgv-downloadView');
1198
870
  }
1199
- }
1200
871
 
1201
- _onActivateClick()
1202
- {
1203
- // Get selected data item
1204
- if (this._selectedItem.type == this.ITEM_TYPE_ZONE)
872
+ // Logs backup deletion failed
873
+ else if (command == this.RESP_DELETE_BACKUP_FAILED)
1205
874
  {
1206
- let params = new SFS2X.SFSObject();
1207
- params.putInt('zId', this._selectedItem.id);
875
+ const error = data.getUtfString('error');
1208
876
 
1209
- this.sendExtensionRequest(this.REQ_ACTIVATE_ZONE, params);
877
+ // Show an alert
878
+ this.shellCtrl.showSimpleAlert(error, true);
1210
879
  }
1211
- }
1212
880
 
1213
- // Configuration buttons listeners
1214
-
1215
- _onCancelClick()
1216
- {
1217
- // Enable zone/room selection lists
1218
- this._enableListInterface(true);
1219
-
1220
- // Disable configuration interface
1221
- this._enableConfigInterface(false);
881
+ // A blocking error occurred during backup operation
882
+ else if (command == this.RESP_BACKUP_ERROR)
883
+ {
884
+ const error = data.getUtfString('error');
1222
885
 
1223
- // Clear main container
1224
- this._resetTabsContainer(false, true);
886
+ // Show an alert
887
+ this.shellCtrl.showSimpleAlert(error, true);
888
+ }
1225
889
 
1226
- // Set isEditing flag
1227
- this._isEditing = false;
1228
- this._editedItemType = '';
890
+ // An non-blocking error occurred during backup operation
891
+ else if (command == this.RESP_BACKUP_WARNING)
892
+ {
893
+ let warn = data.getUtfString('warn');
1229
894
 
1230
- // Switch panel
1231
- this._switchPanel('znc-sidebarPanel');
895
+ // Display notification
896
+ this.shellCtrl.showNotification(`Log backup warning`, warn);
897
+ }
1232
898
  }
1233
899
 
1234
- _onReloadClick()
1235
- {
1236
- // Hide validation messages
1237
- this._interfaceBuilder.resetValidation();
1238
-
1239
- // Reload configuration
1240
- this._loadConfiguration(this._editedItemType, false);
1241
- }
900
+ //---------------------------------
901
+ // UI EVENT LISTENERS
902
+ //---------------------------------
1242
903
 
1243
- _onSubmitClick()
904
+ _onTabShown(e)
1244
905
  {
1245
- // Check validity
1246
- if (this._interfaceBuilder.checkIsValid())
906
+ if (!this._initFailed)
1247
907
  {
1248
- let changes = this._interfaceBuilder.getChangedData();
1249
-
1250
- if (changes.size() > 0)
908
+ // If boot log view was displayed...
909
+ if (e.target.id == 'lgv-bootLog-tab')
1251
910
  {
1252
- //console.log(changes.getDump())
1253
-
1254
- // In case the zone/room name changed, check it against the list (duplicate names not allowed!)
1255
- if (this._validateName(changes))
1256
- {
1257
- // Disable configuration interface
1258
- this._enableConfigInterface(false);
1259
-
1260
- // Send settings to server instance
1261
- let params = new SFS2X.SFSObject();
1262
- params.putSFSArray('settings', changes);
1263
- params.putBool('backup', $('#znc-backupCheck').prop('checked'));
1264
- params.putInt('zId', this._editedZoneId);
1265
- params.putInt('rId', this._editedRoomId);
1266
-
1267
- if (this._editedItemType == this.ITEM_TYPE_ZONE)
1268
- {
1269
- // Submit zone settings
1270
- if (this._editedZoneId > -1)
1271
- this.sendExtensionRequest(this.REQ_SAVE_ZONE_CONFIG, params);
1272
- else
1273
- this.sendExtensionRequest(this.REQ_NEW_ZONE_CONFIG, params);
1274
- }
1275
- else
1276
- {
1277
- // Submit room settings
1278
- if (this._editedRoomId > -1)
1279
- this.sendExtensionRequest(this.REQ_SAVE_ROOM_CONFIG, params);
1280
- else
1281
- this.sendExtensionRequest(this.REQ_NEW_ROOM_CONFIG, params);
1282
- }
1283
- }
1284
- else
911
+ // Load boot log the first time the tab is selected
912
+ if (!this._bootLogRequested)
1285
913
  {
1286
- // Show alert
1287
- this.shellCtrl.showSimpleAlert(`Unable to submit configuration because the ${Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["capitalizeFirst"])(this._editedItemType)} name already exists; duplicate names are not allowed.`, true);
914
+ this.sendExtensionRequest(this.REQ_GET_BOOT_LOG);
915
+ this._bootLogRequested = true;
1288
916
  }
1289
917
  }
1290
- }
1291
- else
1292
- {
1293
- // Show alert
1294
- this.shellCtrl.showSimpleAlert('Unable to submit configuration changes due to an invalid value; please verify the highlighted form fields in all tabs.', true);
1295
- }
1296
- }
1297
-
1298
- _onConfigValueSet(e) // SAME METHOD DUPLICATED IN zone-monitor.js
1299
- {
1300
- const configParam = e.target.data;
1301
-
1302
- // Handle extension name/type dropdowns update and update the main class dropdown datasource accordingly
1303
- if (configParam.name == 'extension.name' || configParam.name == 'extension.type' || configParam.name == 'extension.filterClass')
1304
- {
1305
- // All involved ConfigFormItems must be available and initialized to proceed
1306
- const nameFormItem = this._interfaceBuilder.getConfigFormItem('extension.name');
1307
- const typeFormItem = this._interfaceBuilder.getConfigFormItem('extension.type');
1308
- const classFormItem = this._interfaceBuilder.getConfigFormItem('extension.file');
1309
- const filterFormItem = this._interfaceBuilder.getConfigFormItem('extension.filterClass');
1310
918
 
1311
- if (nameFormItem != null && typeFormItem != null && classFormItem != null && filterFormItem != null)
919
+ // If backup&dowload view was displayed...
920
+ else if (e.target.id == 'lgv-logsDownload-tab')
1312
921
  {
1313
- const source = nameFormItem.data;
1314
- let classesList = [];
1315
-
1316
- let data = source.triggerData;
1317
- for (let i = 0; i < data.size(); i++)
922
+ // Request logs backup status the first time the tab is selected
923
+ if (!this._backupStatusRequested)
1318
924
  {
1319
- let ext = data.getSFSObject(i);
1320
-
1321
- if (ext.getUtfString('name') == nameFormItem.data.value && ext.getUtfString('type') == typeFormItem.data.value)
1322
- {
1323
- let classes = ext.getUtfString('classesString').split(',');
1324
-
1325
- if (filterFormItem.data.value == true)
1326
- {
1327
- let filteredClasses = classes.filter(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["filterClassName"]);
1328
- classes = filteredClasses;
1329
- }
1330
-
1331
- classesList = classesList.concat(classes);
1332
- }
925
+ this.sendExtensionRequest(this.REQ_GET_BACKUPS_STATUS);
926
+ this._backupStatusRequested = true;
1333
927
  }
1334
-
1335
- let currentClass = classFormItem.data.value;
1336
-
1337
- // If the classes list doesn't contain the current value, create an empty entry and reset the value
1338
- if (classesList.indexOf(currentClass) < 0)
1339
- {
1340
- if (classesList.length == 0)
1341
- {
1342
- classesList.push('');
1343
- currentClass = '';
1344
- }
1345
- else
1346
- currentClass = classesList[0];
1347
- }
1348
-
1349
- let mainClassDropDown = classFormItem._innerWidget;
1350
- mainClassDropDown.setDataSource(classesList);
1351
-
1352
- classFormItem.data.value = currentClass;
1353
- classFormItem._setWidgetValue();
1354
928
  }
1355
929
  }
1356
930
  }
1357
931
 
1358
- _onWordsFileReloadClick(evt)
932
+ _onRuntimeLogLoadBtClick()
1359
933
  {
1360
- // Send request to server
1361
- this.sendExtensionRequest(this.REQ_REFRESH_WORDS_FILE);
934
+ // Request log
935
+ this._loadRuntimeLog();
1362
936
  }
1363
937
 
1364
- _onWordsFileEditClick(evt)
938
+ _onExportBootLogBtClick()
1365
939
  {
1366
- // Send request to server
1367
- let params = new SFS2X.SFSObject();
1368
- params.putUtfString('filename', evt.detail);
1369
- this.sendExtensionRequest(this.REQ_EDIT_WORDS_FILE, params);
940
+ // Export log to file
941
+ this._exportLog($('#lgv-bootLogText').text(), this.BOOT_LOG_BACKUP_ID);
1370
942
  }
1371
943
 
1372
- _onWordsFileSaveClick(evt)
944
+ _onSwitchBootLogColorBtClick()
1373
945
  {
1374
- this._tempWordsFileData = evt.detail;
1375
-
1376
- // Check if a new file is being created
1377
- if (this._tempWordsFileData.isNew)
1378
- {
1379
- // If yes, check if name already exists
1380
- if (this._wordsFilesManager.getExistingFilenames().includes(this._tempWordsFileData.filename))
1381
- {
1382
- // Show confirm dialog
1383
- this.shellCtrl.showConfirmWarning('A words file with the entered name already exists; do you want to proceed anyway? The existing file will be overwritten.', $.proxy(this._onWordsFileSaveConfirm, this));
1384
- return;
1385
- }
1386
- }
1387
-
1388
- // Proceed
1389
- this._onWordsFileSaveConfirm();
946
+ if ($('#lgv-bootLogText').hasClass('invert'))
947
+ $('#lgv-bootLogText').removeClass('invert');
948
+ else
949
+ $('#lgv-bootLogText').addClass('invert');
1390
950
  }
1391
951
 
1392
- _onWordsFileSaveConfirm()
952
+ _onBootLogGenerateBtClick()
1393
953
  {
1394
- // Disable words files manager buttons
1395
- this._wordsFilesManager.enabled = false;
1396
- this._wordsFilesManager.saveSpinnerVisible = true;
954
+ // Show/hide operation in progress message
955
+ this._disableBackupInterface(true, this.BOOT_LOG_BACKUP_ID);
1397
956
 
1398
- // Send request to server
1399
- let params = new SFS2X.SFSObject();
1400
- params.putUtfString('filename', this._tempWordsFileData.filename);
1401
- params.putText('content', this._tempWordsFileData.content);
1402
- this.sendExtensionRequest(this.REQ_SAVE_WORDS_FILE, params);
957
+ // Request backup generation
958
+ this.sendExtensionRequest(this.REQ_BACKUP_BOOT_LOG);
1403
959
  }
1404
960
 
1405
- _onWordsFileRemoveClick(evt)
961
+ _onRuntimeLogGenerateBtClick()
1406
962
  {
1407
- this.shellCtrl.showConfirmWarning('Are you sure you want to delete the selected words file?', $.proxy(this._onWordsFileRemoveConfirm, this));
963
+ // Show/hide operation in progress message
964
+ this._disableBackupInterface(true, this.RUNTIME_LOG_BACKUP_ID);
965
+
966
+ // Request backup generation
967
+ this.sendExtensionRequest(this.REQ_BACKUP_RUNTIME_LOG);
1408
968
  }
1409
969
 
1410
- _onWordsFileRemoveConfirm()
970
+ _onFullLogsGenerateBtClick()
1411
971
  {
1412
- let wordsFile = this._wordsFilesManager.getSelectedWordsFileName();
972
+ // Show/hide operation in progress message
973
+ this._disableBackupInterface(true, this.FULL_BACKUP_ID);
1413
974
 
1414
- if (wordsFile != null)
1415
- {
1416
- // Disable words files manager buttons
1417
- this._wordsFilesManager.enabled = false;
1418
- this._wordsFilesManager.actionSpinnerVisible = true;
1419
-
1420
- // Send request to server
1421
- let params = new SFS2X.SFSObject();
1422
- params.putUtfString('filename', wordsFile);
1423
- this.sendExtensionRequest(this.REQ_DELETE_WORDS_FILE, params);
1424
- }
975
+ // Request backup generation
976
+ this.sendExtensionRequest(this.REQ_BACKUP_FULL_LOGS);
1425
977
  }
1426
978
 
1427
- _onWordsFileAssignClick(evt)
979
+ _onFilterChange()
1428
980
  {
1429
- let path = evt.detail;
1430
-
1431
- // Write path of the selected words file in "wordsFilter.wordsFile" dynamically created field
1432
- const wordsFileFormItem = this._interfaceBuilder.getConfigFormItem('wordsFilter.wordsFile');
1433
- wordsFileFormItem.data.value = path;
1434
- wordsFileFormItem._setWidgetValue();
981
+ // Set filters
982
+ this._setRuntimeLogDataSource(this._runtimeLogGrid.dataSource);
1435
983
  }
1436
984
 
1437
- //---------------------------------
1438
- // PRIVATE METHODS
1439
- //---------------------------------
1440
-
1441
- _enableListInterface(enabled)
985
+ _onClearFilterClick()
1442
986
  {
1443
- $('#znc-utilButtons').attr('disabled', !enabled);
1444
- $('#znc-treeView').attr('disabled', !enabled);
987
+ this._clearRuntimeLogFilters();
988
+ this._setRuntimeLogDataSource(this._runtimeLogGrid.dataSource);
1445
989
  }
1446
990
 
1447
- _setUtilityButtonsState(dataItem = null)
991
+ _onExportRuntimeLogBtClick()
1448
992
  {
1449
- let disable = true;
1450
-
1451
- if (dataItem)
1452
- {
1453
- // Enable 'activate zone' button if zone is inactive
1454
- $('#znc-activateButton').attr('disabled', (dataItem.type != this.ITEM_TYPE_ZONE || dataItem.active));
993
+ let log = '';
994
+ const entries = this._runtimeLogGrid.dataSource.view();
1455
995
 
1456
- disable = false;
1457
- }
1458
- else
996
+ for (let i = 0; i < entries.length; i++)
1459
997
  {
1460
- // Disable 'activate zone' button
1461
- $('#znc-activateButton').attr('disabled', true);
998
+ const item = entries[i];
999
+ log += [item.date, item.time, item.level, item.thread, item.clazz, item.message].join(item.sep) + '\n';
1462
1000
  }
1463
1001
 
1464
- // Enable/disable other utility buttons
1465
- $('#znc-addZoneButton').attr('disabled', false); // Always enabled
1466
- $('#znc-addRoomButton').attr('disabled', disable);
1467
- $('#znc-editButton').attr('disabled', disable);
1468
- $('#znc-removeButton').attr('disabled', disable);
1002
+ // Export log to file
1003
+ this._exportLog(log, this.RUNTIME_LOG_BACKUP_ID);
1469
1004
  }
1470
1005
 
1471
- _enableConfigInterface(enabled)
1006
+ _onDownloadGridSelectionChange()
1472
1007
  {
1473
- $('#znc-cancelButton').attr('disabled', !enabled);
1474
- $('#znc-reloadButton').attr('disabled', !enabled);
1475
- $('#znc-submitButton').attr('disabled', !enabled);
1476
- $('#znc-backupCheck').attr('disabled', !enabled);
1477
-
1478
- this._interfaceBuilder.disableInterface(!enabled);
1008
+ // Enable/disable buttons
1009
+ const selectedRows = this._downloadGrid.select();
1010
+ $('#lgv-downloadBt').attr('disabled', selectedRows.length == 0);
1011
+ $('#lgv-deleteBt').attr('disabled', selectedRows.length == 0);
1479
1012
 
1480
- // Also switch view when enabled
1481
- if (enabled)
1482
- this._switchView('znc-main');
1013
+ if (selectedRows.length > 0)
1014
+ {
1015
+ let dataItem = this._downloadGrid.dataItem(selectedRows[0]);
1016
+ $('#lgv-downloadBt').attr('href', dataItem.url);
1017
+ }
1018
+ else
1019
+ $('#lgv-downloadBt').attr('href', '#');
1483
1020
  }
1484
1021
 
1485
- _switchView(viewId)
1022
+ _onDeleteBtClick()
1486
1023
  {
1487
- document.getElementById('znc-viewstack').selectedElement = document.getElementById(viewId);
1488
- }
1024
+ let selectedRows = this._downloadGrid.select();
1489
1025
 
1490
- _clearTabs()
1491
- {
1492
- // Destroy scrolling tabs
1493
- $('#znc-tabNavigator #tabs').scrollingTabs('destroy');
1026
+ if (selectedRows.length > 0)
1027
+ {
1028
+ let dataItem = this._downloadGrid.dataItem(selectedRows[0]);
1494
1029
 
1495
- // Remove all tab navigator content
1496
- this._interfaceBuilder.destroyInterface();
1030
+ // Request backup deletion
1031
+ let params = new SFS2X.SFSObject();
1032
+ params.putUtfString('file', dataItem.name);
1497
1033
 
1498
- // Set flag to re-initialize tabs if needed
1499
- this._reinitTabs = true;
1034
+ this.sendExtensionRequest(this.REQ_DELETE_BACKUP, params);
1035
+ }
1500
1036
  }
1501
1037
 
1502
- _populateTree(data)
1503
- {
1504
- let zData = data.getSFSArray('zones');
1505
-
1506
- let zonesArr = [];
1507
- for (let z = 0; z < zData.size(); z++)
1508
- zonesArr.push( this._createZoneObject(zData.getSFSObject(z)) );
1509
-
1510
- // Create datasource
1511
- let zones = new kendo.data.HierarchicalDataSource({
1512
- data: zonesArr,
1513
- sort: {
1514
- field: 'name',
1515
- dir: 'asc'
1516
- },
1517
- schema: {
1518
- model: {
1519
- id: 'id',
1520
- children: {
1521
- schema: {
1522
- data: 'rooms',
1523
- sort: {
1524
- field: 'name',
1525
- dir: 'asc'
1526
- }
1527
- }
1528
- }
1529
- }
1530
- }
1531
- });
1532
-
1533
- // Set tree view dataprovider
1534
- this._treeview.setDataSource(zones);
1038
+ //------------------------------------
1039
+ // PRIVATE METHODS
1040
+ //------------------------------------
1535
1041
 
1536
- // Set utility buttons state (add, remove, edit, etc)
1537
- this._setUtilityButtonsState();
1042
+ _switchRuntimeViewStack(viewId)
1043
+ {
1044
+ document.getElementById('lgv-runtime-viewstack').selectedElement = document.getElementById(viewId);
1538
1045
  }
1539
1046
 
1540
- _createZoneObject(zoneData)
1047
+ _switchBootViewStack(viewId)
1541
1048
  {
1542
- let zone = {
1543
- type: this.ITEM_TYPE_ZONE,
1544
- name: zoneData.getUtfString('name'),
1545
- id: zoneData.getInt('id'),
1546
- active: zoneData.getBool('act')
1547
- }
1548
-
1549
- // Create rooms list dataprovider
1550
- let rData = zoneData.getSFSArray('rooms');
1551
-
1552
- let roomsArr = [];
1553
- for (let r = 0; r < rData.size(); r++)
1554
- roomsArr.push( this._createRoomObject(rData.getSFSObject(r), zoneData.getInt('id')) );
1555
-
1556
- zone.rooms = roomsArr;
1557
-
1558
- return zone;
1049
+ document.getElementById('lgv-boot-viewstack').selectedElement = document.getElementById(viewId);
1559
1050
  }
1560
1051
 
1561
- _createRoomObject(roomData, zoneId)
1052
+ _switchDownloadViewStack(viewId)
1562
1053
  {
1563
- let room = {
1564
- type: this.ITEM_TYPE_ROOM,
1565
- name: roomData.getUtfString('name'),
1566
- id: roomData.getInt('id'),
1567
- active: roomData.getBool('act'),
1568
- parentId: zoneId,
1569
- spriteCssClass: this._getRoomListIconCssClass(roomData.getBool('act'))
1570
- };
1571
-
1572
- return room;
1054
+ document.getElementById('lgv-download-viewstack').selectedElement = document.getElementById(viewId);
1573
1055
  }
1574
1056
 
1575
- _getRoomListIconCssClass(isActive)
1057
+ _enableRuntimeLogControls(enable)
1576
1058
  {
1577
- return isActive ? 'fas fa-door-open' : 'fas fa-door-closed inactive-list-item';
1059
+ $('#lgv-loadBt').attr('disabled', !enable);
1060
+ $('#lgv-exportRuntimeLogBt').attr('disabled', !enable);
1061
+ $('#lgv-filterBt').attr('disabled', !enable);
1578
1062
  }
1579
1063
 
1580
- _setZoneActivationStatus(zoneId, activeRooms, isActive)
1064
+ _loadRuntimeLog()
1581
1065
  {
1582
- let zoneDI = this._getZoneDataItemById(zoneId);
1066
+ // Disable controls to load log, so that impatient users can't send multiple requests
1067
+ // (it will be enabled again when a response is received)
1068
+ this._enableRuntimeLogControls(false);
1583
1069
 
1584
- zoneDI.active = isActive;
1070
+ // Clear filters
1071
+ this._clearRuntimeLogFilters();
1585
1072
 
1586
- let activeRoomsArr = activeRooms.split(',');
1073
+ // Show loading bar
1074
+ this._switchRuntimeViewStack('lgv-runtimeLogLoadingView');
1587
1075
 
1588
- if (zoneDI.hasChildren)
1589
- {
1590
- for (let i = 0; i < zoneDI.children.data().length; i++)
1591
- {
1592
- let room = zoneDI.children.data()[i];
1593
- room.active = (isActive && activeRoomsArr.indexOf(room.name) > -1);
1594
- room.spriteCssClass = this._getRoomListIconCssClass(room.active)
1595
- }
1596
- }
1597
-
1598
- // Refresh list
1599
- this._treeview.dataSource.sync();
1076
+ // Send request
1077
+ // (the number of lines to be retrieved is sent)
1078
+ let params = new SFS2X.SFSObject();
1079
+ params.putInt('numEntries', Number(this._logLinesDD.value()));
1600
1080
 
1601
- // Return zone name
1602
- return zoneDI.name;
1081
+ this.sendExtensionRequest(this.REQ_GET_RUNTIME_LOG, params);
1603
1082
  }
1604
1083
 
1605
- _deselectTreeItem()
1084
+ _clearRuntimeLogFilters()
1606
1085
  {
1607
- this._treeview.select($());
1086
+ this._levelFilterDD.select(0);
1087
+ this._classFilterDD.select(0);
1088
+ $('#lgv-messageIn').val('');
1608
1089
  }
1609
1090
 
1610
- _selectParentTreeItem()
1091
+ _exportLog(log, name)
1611
1092
  {
1612
- let selectedNode = this._treeview.select();
1613
- let selectedDataItem = this._treeview.dataItem(selectedNode);
1093
+ let blob = new Blob([log], {type: "text/plain;charset=utf-8"});
1094
+ let date = moment__WEBPACK_IMPORTED_MODULE_3__().format('YYYYMMDD_HHmmss');
1614
1095
 
1615
- if (selectedDataItem.type == this.ITEM_TYPE_ROOM)
1616
- {
1617
- let parentNode = this._treeview.parent(selectedNode);
1618
- this._treeview.select(parentNode);
1619
- }
1096
+ file_saver__WEBPACK_IMPORTED_MODULE_2__["saveAs"](blob, `${name}_${date}.log`);
1620
1097
  }
1621
1098
 
1622
- _loadConfiguration(type, resetTabs = true)
1099
+ _setRuntimeLogDataSource(ds)
1623
1100
  {
1624
- // Disable zone/room selection list
1625
- this._enableListInterface(false);
1626
-
1627
- // Disable configuration interface
1628
- this._enableConfigInterface(false);
1629
-
1630
- // Clear main container
1631
- this._resetTabsContainer(true, resetTabs);
1101
+ // Read current horizontal scroll value
1102
+ const scrollLeft = $('#lgv-runtimeLogGrid .k-grid-content', this._runtimeLogGrid.wrapper).scrollLeft();
1632
1103
 
1633
- // Set isEditing flag
1634
- this._isEditing = true;
1635
- this._editedItemType = type;
1104
+ // Assign data source to grid
1105
+ this._runtimeLogGrid.setDataSource(ds);
1636
1106
 
1637
- // Request zone or room configuration data to server instance
1638
- let params = new SFS2X.SFSObject();
1639
- params.putInt('zId', this._editedZoneId);
1640
- params.putInt('rId', this._editedRoomId);
1107
+ // Set filters
1108
+ this._setFilters(ds);
1641
1109
 
1642
- // If no room is selected, then we are editing a zone
1643
- if (this._editedItemType == this.ITEM_TYPE_ZONE)
1644
- this.sendExtensionRequest(this.REQ_GET_ZONE_CONFIG, params);
1645
- else
1646
- this.sendExtensionRequest(this.REQ_GET_ROOM_CONFIG, params);
1110
+ // Set horizontal scroll
1111
+ $('#lgv-runtimeLogGrid .k-grid-content', this._runtimeLogGrid.wrapper).scrollLeft(scrollLeft);
1647
1112
 
1648
- // Switch panel
1649
- this._switchPanel('znc-mainPanel');
1113
+ // Update counter
1114
+ $('#lgv-runtimeLogEntriesLb').text(`Log entries: ${this._totalRuntimeLogEntries} (${ds.total()} displayed)`);
1650
1115
  }
1651
1116
 
1652
- _resetTabsContainer(isLoading, resetTabs)
1117
+ _setFilters(ds)
1653
1118
  {
1654
- if (resetTabs)
1655
- this._clearTabs();
1656
- else
1657
- this._reinitTabs = false;
1119
+ let filters = [];
1658
1120
 
1659
- if (!isLoading)
1660
- this._switchView('znc-select');
1661
- else
1662
- this._switchView('znc-loading');
1663
- }
1121
+ // Level filtering
1122
+ if (this._levelFilterDD.select() > 0)
1123
+ filters.push({
1124
+ field: 'level', operator: 'eq', value: this._levelFilterDD.value()
1125
+ });
1664
1126
 
1665
- _switchPanel(panelId)
1666
- {
1667
- document.getElementById('znc-view').selectedPanel = document.getElementById(panelId);
1668
- }
1127
+ // Class filtering
1128
+ if (this._classFilterDD.select() > 0)
1129
+ filters.push({
1130
+ field: 'clazz', operator: 'eq', value: this._classFilterDD.value()
1131
+ });
1669
1132
 
1670
- _getZoneDataItemById(zoneId)
1671
- {
1672
- let zonesDS = this._treeview.dataSource;
1673
- return zonesDS.get(zoneId);
1133
+ // Message filtering
1134
+ if ($('#lgv-messageIn').val() != '')
1135
+ filters.push({
1136
+ field: 'message', operator: 'contains', value: $('#lgv-messageIn').val()
1137
+ });
1138
+
1139
+ // Set filters
1140
+ ds.filter(filters);
1674
1141
  }
1675
1142
 
1676
- _getRoomDataItemById(zoneId, roomId)
1143
+ _disableBackupInterface(disable, backupId = null)
1677
1144
  {
1678
- let zoneDI = this._getZoneDataItemById(zoneId);
1145
+ if (disable)
1146
+ {
1147
+ // Show proper progress bar
1148
+ if (backupId == this.BOOT_LOG_BACKUP_ID)
1149
+ $('#lgv-bootLogBackupCard .progress-bar').show();
1150
+ else if (backupId == this.RUNTIME_LOG_BACKUP_ID)
1151
+ $('#lgv-runtimeLogBackupCard .progress-bar').show();
1152
+ else if (backupId == this.FULL_BACKUP_ID)
1153
+ $('#lgv-fullLogBackupCard .progress-bar').show();
1154
+
1155
+ // Disable buttons
1156
+ $('#lgv-bootLogBackupCard .backup-button').attr('disabled', true);
1157
+ $('#lgv-runtimeLogBackupCard .backup-button').attr('disabled', true);
1158
+ $('#lgv-fullLogBackupCard .backup-button').attr('disabled', true);
1159
+ }
1160
+ else
1161
+ {
1162
+ // Hide all progress bar
1163
+ $('.card-body .progress-bar').hide();
1679
1164
 
1680
- if (zoneDI.hasChildren)
1681
- return zoneDI.children.get(roomId);
1165
+ // Enable buttons
1166
+ $('#lgv-fullLogBackupCard .backup-button').attr('disabled', false);
1682
1167
 
1683
- return null;
1684
- }
1168
+ if (!this._bootLogBackupUnavailable)
1169
+ $('#lgv-bootLogBackupCard .backup-button').attr('disabled', false);
1685
1170
 
1686
- _updateZoneNameInList(zoneId, zoneName)
1687
- {
1688
- this._getZoneDataItemById(zoneId).name = zoneName;
1689
- this._treeview.dataSource.sync();
1171
+ if (!this._runtimeLogBackupUnavailable)
1172
+ $('#lgv-runtimeLogBackupCard .backup-button').attr('disabled', false);
1173
+ }
1690
1174
  }
1691
1175
 
1692
- _updateRoomNameInList(zoneId, roomId, roomName)
1176
+ _initBackupCard(idSelector)
1693
1177
  {
1694
- this._getRoomDataItemById(zoneId, roomId).name = roomName;
1695
- this._treeview.dataSource.sync();
1178
+ $(idSelector + ' .backup-details').hide();
1179
+ $(idSelector + ' .progress-bar').hide();
1696
1180
  }
1697
1181
 
1698
- _validateName(changes)
1182
+ _fillBackupCard(idSelector, detailsObj = null)
1699
1183
  {
1700
- const zoneId = this._editedZoneId;
1701
-
1702
- for (let i = 0; i < changes.size(); i++)
1184
+ if (detailsObj == null)
1185
+ $(idSelector + ' .backup-details').hide();
1186
+ else
1703
1187
  {
1704
- const setting = changes.getSFSObject(i);
1705
-
1706
- if (setting.containsKey('name') && setting.getUtfString('name') == 'name')
1707
- {
1708
- // Get name value
1709
- const name = setting.getText('value');
1710
-
1711
- // Get data source
1712
- let ds = [];
1713
-
1714
- if (this._editedItemType == this.ITEM_TYPE_ZONE)
1715
- ds = this._treeview.dataSource.data();
1716
- else
1717
- {
1718
- if (this._getZoneDataItemById(zoneId).hasChildren)
1719
- ds = this._getZoneDataItemById(zoneId).children.data();
1720
- }
1721
-
1722
-
1723
- // Check if name exists in data source
1724
- for (let j = 0; j < ds.length; j++)
1725
- {
1726
- if (ds[j].name == name)
1727
- {
1728
- return false;
1729
- }
1730
- }
1188
+ $(idSelector + ' .backup-details').show();
1731
1189
 
1732
- break;
1733
- }
1190
+ $(idSelector + ' .backup-link').attr('href', detailsObj.url);
1191
+ $(idSelector + ' .backup-date').text(detailsObj.date);
1192
+ $(idSelector + ' .backup-time').text(detailsObj.time);
1193
+ $(idSelector + ' .backup-size').text(detailsObj.size);
1734
1194
  }
1735
-
1736
- return true;
1737
1195
  }
1738
1196
 
1739
- //---------------------------------
1740
- // PRIVATE GETTERS
1741
- //---------------------------------
1742
-
1743
- get _selectedItem()
1197
+ _setDownloadGridDataSource(ds)
1744
1198
  {
1745
- return this._treeview.dataItem(this._treeview.select());
1746
- }
1199
+ // Read current horizontal scroll value
1200
+ const scrollLeft = $('#lgv-downloadGrid .k-grid-content', this._downloadGrid.wrapper).scrollLeft();
1747
1201
 
1748
- get _selectedItemParent()
1749
- {
1750
- let selectedNode = this._treeview.select();
1751
- let parentNode = this._treeview.parent(selectedNode);
1202
+ // Assign data source to grid
1203
+ this._downloadGrid.setDataSource(ds);
1752
1204
 
1753
- return this._treeview.dataItem(parentNode);
1205
+ // Set horizontal scroll
1206
+ $('#lgv-downloadGrid .k-grid-content', this._downloadGrid.wrapper).scrollLeft(scrollLeft);
1754
1207
  }
1755
1208
 
1756
- get _editedZoneId()
1757
- {
1758
- if (this._isEditing && this._selectedItem)
1759
- {
1760
- if (this._selectedItem.type == this.ITEM_TYPE_ZONE)
1761
- return this._selectedItem.id;
1762
- else
1763
- return this._selectedItemParent.id;
1764
- }
1765
-
1766
- return -1;
1767
- }
1209
+ //---------------------------------
1210
+ // PRIVATE GETTERS
1211
+ //---------------------------------
1768
1212
 
1769
- get _editedRoomId()
1770
- {
1771
- if (this._isEditing && this._selectedItem)
1772
- {
1773
- if (this._selectedItem.type == this.ITEM_TYPE_ROOM)
1774
- return this._selectedItem.id;
1775
- }
1776
1213
 
1777
- return -1;
1778
- }
1779
1214
  }
1780
1215
 
1781
1216
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
@@ -1783,4 +1218,4 @@ class ZoneConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseMo
1783
1218
  /***/ })
1784
1219
 
1785
1220
  }]);
1786
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2pzL2NvcmUvbW9kdWxlcy9tb2R1bGUtMTIuYnVuZGxlLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvY29tcG9uZW50cy9tb2R1bGUtc3BlY2lmaWMvd29yZHMtZmlsZXMtbWFuYWdlci5qcyIsIndlYnBhY2s6Ly9hcHBsaWNhdGlvbi8uL3NyYy9jb21wb25lbnRzL3NpZGViYXItbGF5b3V0LmpzIiwid2VicGFjazovL2FwcGxpY2F0aW9uLy4vc3JjL21vZHVsZXMvem9uZS1jb25maWd1cmF0b3IuanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtieXRlc1RvU2l6ZX0gZnJvbSAnLi4vLi4vdXRpbHMvdXRpbGl0aWVzJztcblxuZXhwb3J0IGNsYXNzIFdvcmRzRmlsZXNNYW5hZ2VyIGV4dGVuZHMgSFRNTEVsZW1lbnRcbntcblx0Y29uc3RydWN0b3IoKVxuXHR7XG5cdCAgICBzdXBlcigpO1xuXG5cdFx0dGhpcy5SRUZSRVNIX1dPUkRTX0ZJTEVTX0NMSUNLX0VWRU5UID0gJ3JlZnJlc2hXb3Jkc0ZpbGVzQ2xpY2snO1xuXHRcdHRoaXMuRURJVF9XT1JEU19GSUxFX0NMSUNLX0VWRU5UID0gJ2VkaXRXb3Jkc0ZpbGVDbGljayc7XG5cdFx0dGhpcy5TQVZFX1dPUkRTX0ZJTEVfQ0xJQ0tfRVZFTlQgPSAnc2F2ZVdvcmRzRmlsZUNsaWNrJztcblx0XHR0aGlzLlJFTU9WRV9XT1JEU19GSUxFX0NMSUNLX0VWRU5UID0gJ3JlbW92ZVdvcmRzRmlsZUNsaWNrJztcblx0XHR0aGlzLkFTU0lHTl9XT1JEU19GSUxFX0NMSUNLX0VWRU5UID0gJ2Fzc2luZ1dvcmRzRmlsZUNsaWNrJztcblxuXHRcdHRoaXMuQ09ORklHX0ZPTERFUiA9ICdjb25maWcvJztcblx0XHR0aGlzLldPUkRTX0ZJTEVfRVhUID0gJy53b3Jkcy50eHQnO1xuXG5cdFx0dGhpcy5fbW9kYWxIdG1sID0gYFxuXHRcdFx0PGRpdiBjbGFzcz1cIm1vZGFsXCIgaWQ9XCJlZGl0TW9kYWxcIiB0YWJpbmRleD1cIi0xXCIgcm9sZT1cImRpYWxvZ1wiIGFyaWEtbGFiZWxsZWRieT1cImVkaXRNb2RhbFRpdGxlXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+XG5cdFx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbC1kaWFsb2cgbW9kYWwtZGlhbG9nLWNlbnRlcmVkXCIgcm9sZT1cImRvY3VtZW50XCI+XG5cdFx0XHRcdFx0PGRpdiBjbGFzcz1cIm1vZGFsLWNvbnRlbnRcIj5cblx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbC1oZWFkZXJcIj5cblx0XHRcdFx0XHRcdFx0PGg1IGNsYXNzPVwibW9kYWwtdGl0bGUgdGV4dC1wcmltYXJ5XCIgaWQ9XCJlZGl0TW9kYWxUaXRsZVwiPldvcmQgRmlsZSBFZGl0b3I8L2g1PlxuXHRcdFx0XHRcdFx0XHQ8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImNsb3NlXCIgZGF0YS1kaXNtaXNzPVwibW9kYWxcIiBhcmlhLWxhYmVsPVwiQ2xvc2VcIj5cblx0XHRcdFx0XHRcdFx0XHQ8c3BhbiBhcmlhLWhpZGRlbj1cInRydWVcIj4mdGltZXM7PC9zcGFuPlxuXHRcdFx0XHRcdFx0XHQ8L2J1dHRvbj5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cIm1vZGFsLWJvZHkgaW4tZmxvdy1pbnZhbGlkLW1zZ1wiPlxuXHRcdFx0XHRcdFx0XHQ8ZmllbGRzZXQgaWQ9XCJlZGl0RmllbGRzZXRcIj5cblx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZm9ybS1ncm91cFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNvbC1mb3JtLWxhYmVsIGZvcm0tbGFiZWwtY29udGFpbmVyXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxsYWJlbCBmb3I9XCJmaWxlbmFtZVwiIGNsYXNzPVwiZm9ybS1sYWJlbFwiPkZpbGVuYW1lIDxpIGNsYXNzPVwiZmFzIGZhLXF1ZXN0aW9uLWNpcmNsZSB0ZXh0LW11dGVkIGhlbHBcIiB0aXRsZT1cIk5hbWUgb2YgdGhlIHdvcmRzIGZpbGUuIFRoZSBmaWxlIHdpbGwgYmUgc2F2ZWQgaW4gc2VydmVyJ3MgPGVtPmNvbmZpZzwvZW0+IGZvbGRlci5cIj48L2k+PC9sYWJlbD5cblx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImlubmVyLXdpZGdldFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5wdXQtZ3JvdXBcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGlucHV0IHR5cGU9XCJ0ZXh0XCIgaWQ9XCJmaWxlbmFtZVwiIG5hbWU9XCJmaWxlbmFtZVwiIGNsYXNzPVwiZm9ybS1jb250cm9sIGstdGV4dGJveFwiIGF1dG9jb21wbGV0ZT1cIm9mZlwiIHJlcXVpcmVkIGRhdGEtcmVxdWlyZWQtbXNnPVwiUmVxdWlyZWRcIiBhcmlhLWRlc2NyaWJlZGJ5PVwiZXh0ZW5zaW9uXCIgLz5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5wdXQtZ3JvdXAtYXBwZW5kXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cImlucHV0LWdyb3VwLXRleHRcIiBpZD1cImV4dGVuc2lvblwiPi53b3Jkcy50eHQ8L3NwYW4+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cImstaW52YWxpZC1tc2cgcG9zaXRpb24tc3RhdGljXCIgZGF0YS1mb3I9XCJmaWxlbmFtZVwiPjwvc3Bhbj5cblx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJmb3JtLWdyb3VwXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY29sLWZvcm0tbGFiZWwgZm9ybS1sYWJlbC1jb250YWluZXJcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGxhYmVsIGZvcj1cImNvbnRlbnRcIiBjbGFzcz1cImZvcm0tbGFiZWxcIj5Db250ZW50IDxpIGNsYXNzPVwiZmFzIGZhLXF1ZXN0aW9uLWNpcmNsZSB0ZXh0LW11dGVkIGhlbHBcIiB0aXRsZT1cIkVudGVyIGEgd29yZCBvciBhIHZhbGlkIHJlZ3VsYXIgZXhwcmVzc2lvbiBwZXIgbGluZS4gU2VlIGNvbmZpZ3VyYXRpb24ncyA8ZW0+V29yZHMgZmlsZTwvZW0+IGZpZWxkIGRlc2NyaXB0aW9uIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uLlwiPjwvaT48L2xhYmVsPlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5uZXItd2lkZ2V0XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDx0ZXh0YXJlYSBpZD1cImNvbnRlbnRcIiBuYW1lPVwiY29udGVudFwiIGNsYXNzPVwiZm9ybS1jb250cm9sIGstdGV4dGFyZWEgdy0xMDBcIiByb3dzPVwiMTBcIj48L3RleHRhcmVhPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cImstaW52YWxpZC1tc2cgcG9zaXRpb24tc3RhdGljXCIgZGF0YS1mb3I9XCJjb250ZW50XCI+PC9zcGFuPlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdDwvZmllbGRzZXQ+XG5cdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbC1mb290ZXIgZmxleC1jb2x1bW5cIj5cblx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImQtZmxleCB3LTEwMFwiPlxuXHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJmbGV4LWdyb3ctMSB0ZXh0LWxlZnRcIj5cblx0XHRcdFx0XHRcdFx0XHRcdDxidXR0b24gaWQ9XCJzYXZlV29yZEZpbGVCdXR0b25cIiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJrLWJ1dHRvbiBrLXByaW1hcnlcIj48aSBjbGFzcz1cImZhcyBmYS1zYXZlIG1yLTFcIj48L2k+U2F2ZSB3b3JkIGZpbGU8L2J1dHRvbj5cblx0XHRcdFx0XHRcdFx0XHRcdDxpIGlkPVwic2F2ZVNwaW5uZXJcIiBjbGFzcz1cImZhcyBmYS1jaXJjbGUtbm90Y2ggZmEtc3BpbiB0ZXh0LXByaW1hcnkgYWxpZ24tbWlkZGxlIG1sLTFcIj48L2k+XG5cdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImZsZXgtZ3Jvdy0xIHRleHQtcmlnaHRcIj5cblx0XHRcdFx0XHRcdFx0XHRcdDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1zZWNvbmRhcnlcIiBkYXRhLWRpc21pc3M9XCJtb2RhbFwiPkNhbmNlbDwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdDwvZGl2PlxuXHRcdGA7XG5cblx0XHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRcdCQodGhpcykuYXBwZW5kKGBcblx0XHRcdDxkaXYgY2xhc3M9XCJjb2wtc20tNSBjb2wtbGctNCBjb2wtZm9ybS1sYWJlbCBmb3JtLWxhYmVsLWNvbnRhaW5lclwiPlxuXHRcdFx0XHQ8bGFiZWwgY2xhc3M9XCJmb3JtLWxhYmVsXCI+QXZhaWxhYmxlIHdvcmRzIGZpbGVzIDxpIGNsYXNzPVwiZmFzIGZhLXF1ZXN0aW9uLWNpcmNsZSB0ZXh0LW11dGVkIGhlbHBcIiB0aXRsZT1cIlRoZSBsaXN0IG9mIHdvcmRzIGZpbGVzIGZvdW5kIGluIHNlcnZlcidzIDxlbT5jb25maWc8L2VtPiBmb2xkZXIuIENsaWNrIG9uIHRoZSBBc3NpZ24gYnV0dG9uIHRvIHNldCB0aGUgPHN0cm9uZz5Xb3JkcyBmaWxlPC9zdHJvbmc+IGZpZWxkIHRvIHRoZSBzZWxlY3RlZCBmaWxlLiBDb25maWd1cmF0aW9uIHN1Ym1pc3Npb24gaXMgdGhlbiByZXF1aXJlZCB0byBzYXZlIHRoZSBuZXcgdmFsdWUuXCI+PC9pPjwvbGFiZWw+XG5cdFx0XHQ8L2Rpdj5cblx0XHRcdDxkaXYgY2xhc3M9XCJpbm5lci13aWRnZXQgYWxpZ24tc2VsZi1jZW50ZXIgY29sLXNtXCI+XG5cdFx0XHRcdDxkaXY+XG5cdFx0XHRcdFx0PGRpdiBpZD1cIndvcmRzRmlsZXNcIiBjbGFzcz1cImxpbWl0ZWQtaGVpZ2h0XCI+PC9kaXY+XG5cdFx0XHRcdFx0PGRpdiBpZD1cImFjdGlvbkJ1dHRvbnNcIiBjbGFzcz1cIm10LTIgdGV4dC1yaWdodFwiIGRpc2FibGVkPlxuXHRcdFx0XHRcdFx0PGkgaWQ9XCJhY3Rpb25TcGlubmVyXCIgY2xhc3M9XCJmYXMgZmEtY2lyY2xlLW5vdGNoIGZhLXNwaW4gdGV4dC1wcmltYXJ5IGFsaWduLW1pZGRsZVwiPjwvaT5cblx0XHRcdFx0XHRcdDxidXR0b24gaWQ9XCJyZWZyZXNoQnV0dG9uXCIgdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1zZWNvbmRhcnkgbWwtMlwiIHRpdGxlPVwiUmVmcmVzaFwiPjxpIGNsYXNzPVwiZmFzIGZhLXJlZG8tYWx0XCI+PC9pPjwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0PGJ1dHRvbiBpZD1cImFkZEJ1dHRvblwiIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImstYnV0dG9uIGstc2Vjb25kYXJ5IG1sLTJcIiB0aXRsZT1cIkFkZFwiPjxpIGNsYXNzPVwiZmFzIGZhLXBsdXNcIj48L2k+PC9idXR0b24+XG5cdFx0XHRcdFx0XHQ8YnV0dG9uIGlkPVwiZWRpdEJ1dHRvblwiIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImstYnV0dG9uIGstc2Vjb25kYXJ5IG1sLTJcIiB0aXRsZT1cIkVkaXRcIiBkaXNhYmxlZD48aSBjbGFzcz1cImZhcyBmYS1wZW5cIj48L2k+PC9idXR0b24+XG5cdFx0XHRcdFx0XHQ8YnV0dG9uIGlkPVwicmVtb3ZlQnV0dG9uXCIgdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1zZWNvbmRhcnkgbWwtMlwiIHRpdGxlPVwiUmVtb3ZlXCIgZGlzYWJsZWQ+PGkgY2xhc3M9XCJmYXMgZmEtdGltZXNcIj48L2k+PC9idXR0b24+XG5cdFx0XHRcdFx0XHQ8YnV0dG9uIGlkPVwiYXNzaWduQnV0dG9uXCIgdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1zZWNvbmRhcnkgbWwtMlwiIHRpdGxlPVwiQXNzaWduXCIgZGlzYWJsZWQ+PGkgY2xhc3M9XCJmYXMgZmEtY29tcHJlc3MtYXJyb3dzLWFsdFwiPjwvaT48L2J1dHRvbj5cblx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L2Rpdj5cblx0XHRgKTtcblxuXHRcdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBncmlkXG5cdFx0dGhpcy5fd29yZHNGaWxlc0dyaWQgPSAkKCcjd29yZHNGaWxlcycsICQodGhpcykpLmtlbmRvR3JpZCh7XG5cdFx0XHRyZXNpemFibGU6IHRydWUsXG5cdFx0XHRzZWxlY3RhYmxlOiAncm93Jyxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vbldvcmRzRmlsZXNHcmlkU2VsZWN0aW9uQ2hhbmdlLCB0aGlzKSxcblx0XHRcdGNvbHVtbnM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAnbmFtZScsXG5cdFx0XHRcdFx0dGl0bGU6ICdGaWxlbmFtZScsXG5cdFx0XHRcdFx0d2lkdGg6IDEyMFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdkYXRlJyxcblx0XHRcdFx0XHR0aXRsZTogJ0RhdGUnLFxuXHRcdFx0XHRcdHdpZHRoOiA4MFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdzaXplJyxcblx0XHRcdFx0XHR0aXRsZTogJ1NpemUnLFxuXHRcdFx0XHRcdHdpZHRoOiA4MFxuXHRcdFx0XHR9XG5cdFx0XHRdLFxuXHRcdFx0bm9SZWNvcmRzOiB7XG5cdFx0XHRcdHRlbXBsYXRlOiAnTm8gZmlsZXMuJ1xuXHRcdFx0fVxuXHRcdH0pLmRhdGEoJ2tlbmRvR3JpZCcpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVycyB0byBidXR0b24gY2xpY2tzXG5cdFx0JCgnI3JlZnJlc2hCdXR0b24nLCAkKHRoaXMpKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uUmVsb2FkQ2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjYWRkQnV0dG9uJywgJCh0aGlzKSkub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vbkFkZENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI2VkaXRCdXR0b24nLCAkKHRoaXMpKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uRWRpdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI3JlbW92ZUJ1dHRvbicsICQodGhpcykpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25SZW1vdmVDbGljaywgdGhpcykpO1xuXHRcdCQoJyNhc3NpZ25CdXR0b24nLCAkKHRoaXMpKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQXNzaWduQ2xpY2ssIHRoaXMpKTtcblx0fVxuXG5cdGRlc3Ryb3koKVxuXHR7XG5cdFx0Ly8gRGVzdHJveSBncmlkXG5cdFx0dGhpcy5fd29yZHNGaWxlc0dyaWQuZGVzdHJveSgpO1xuXG5cdFx0Ly8gUmVtb3ZlIGV2ZW50IGxpc3RlbmVyc1xuXHRcdCQoJyNyZWZyZXNoQnV0dG9uJywgJCh0aGlzKSkub2ZmKCdjbGljaycpO1xuXHRcdCQoJyNhZGRCdXR0b24nLCAkKHRoaXMpKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI2VkaXRCdXR0b24nLCAkKHRoaXMpKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI3JlbW92ZUJ1dHRvbicsICQodGhpcykpLm9mZignY2xpY2snKTtcblx0XHQkKCcjYXNzaWduQnV0dG9uJywgJCh0aGlzKSkub2ZmKCdjbGljaycpO1xuXG5cdFx0Ly8gSGlkZSBtb2RhbCAod2hpY2ggaW4gdHVybiBkZXN0cm95cyBpdClcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI2VkaXRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0aWYgKG1vZGFsRWxlbWVudClcblx0XHRcdG1vZGFsRWxlbWVudC5tb2RhbCgnaGlkZScpO1xuXHR9XG5cblx0Z2V0IGVuYWJsZWQoKVxuXHR7XG5cdFx0cmV0dXJuIHRoaXMuX2lzRW5hYmxlZDtcblx0fVxuXG5cdHNldCBlbmFibGVkKHZhbHVlKVxuXHR7XG5cdFx0dGhpcy5faXNFbmFibGVkID0gdmFsdWU7XG5cblx0XHQvLyBFbmFibGUvZGlzYWJsZSBidXR0b25zXG5cdFx0JCgnI2FjdGlvbkJ1dHRvbnMnLCB0aGlzKS5hdHRyKCdkaXNhYmxlZCcsICF2YWx1ZSk7XG5cblx0XHQvLyBIaWRlIHNwaW5uZXJcblx0XHRpZiAodmFsdWUpXG5cdFx0XHR0aGlzLmFjdGlvblNwaW5uZXJWaXNpYmxlID0gZmFsc2U7XG5cblx0XHQvLyBFbmFibGUvZGlzYWJsZSBtb2RhbFxuXHRcdGxldCBtb2RhbEVsZW1lbnQgPSAkKCcjZWRpdE1vZGFsJywgJCh0aGlzKSk7XG5cblx0XHRpZiAobW9kYWxFbGVtZW50KVxuXHRcdHtcblx0XHRcdC8vIERpc2FibGUgbW9kYWwgY2xvc2UgYnV0dG9uc1xuXHRcdFx0JCgnYnV0dG9uW2RhdGEtZGlzbWlzcz1cIm1vZGFsXCJdJywgbW9kYWxFbGVtZW50KS5hdHRyKCdkaXNhYmxlZCcsICF2YWx1ZSk7XG5cblx0XHRcdC8vIERpc2FibGUgc2F2ZSBidXR0b25cblx0XHRcdCQoJyNzYXZlV29yZEZpbGVCdXR0b24nLCBtb2RhbEVsZW1lbnQpLmF0dHIoJ2Rpc2FibGVkJywgIXZhbHVlKTtcblxuXHRcdFx0Ly8gRGlzYWJsZSBmaWVsZHNldFxuXHRcdFx0JCgnI2VkaXRGaWVsZHNldCcsIG1vZGFsRWxlbWVudCkuYXR0cignZGlzYWJsZWQnLCAhdmFsdWUpO1xuXG5cdFx0XHQvLyBIaWRlIHNwaW5uZXJcblx0XHRcdGlmICh2YWx1ZSlcblx0XHRcdFx0dGhpcy5zYXZlU3Bpbm5lclZpc2libGUgPSBmYWxzZTtcblx0XHR9XG5cdH1cblxuXHRzZXQgYWN0aW9uU3Bpbm5lclZpc2libGUodmFsdWUpXG5cdHtcblx0XHRpZiAodmFsdWUpXG5cdFx0XHQkKCcjYWN0aW9uU3Bpbm5lcicsICQodGhpcykpLnNob3coKTtcblx0XHRlbHNlXG5cdFx0XHQkKCcjYWN0aW9uU3Bpbm5lcicsICQodGhpcykpLmhpZGUoKTtcblx0fVxuXG5cdHNldCBzYXZlU3Bpbm5lclZpc2libGUodmFsdWUpXG5cdHtcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI2VkaXRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0aWYgKG1vZGFsRWxlbWVudClcblx0XHR7XG5cdFx0XHRpZiAodmFsdWUpXG5cdFx0XHRcdCQoJyNzYXZlU3Bpbm5lcicsIG1vZGFsRWxlbWVudCkuc2hvdygpO1xuXHRcdFx0ZWxzZVxuXHRcdFx0XHQkKCcjc2F2ZVNwaW5uZXInLCBtb2RhbEVsZW1lbnQpLmhpZGUoKTtcblx0XHR9XG5cdH1cblxuXHRyZWZyZXNoV29yZHNGaWxlc0xpc3Qod29yZHNGaWxlc0xpc3QsIGhpZGVFZGl0TW9kYWwpXG5cdHtcblx0XHRpZiAoaGlkZUVkaXRNb2RhbClcblx0XHR7XG5cdFx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI2VkaXRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0XHRpZiAobW9kYWxFbGVtZW50KVxuXHRcdFx0XHRtb2RhbEVsZW1lbnQubW9kYWwoJ2hpZGUnKTtcblx0XHR9XG5cblx0XHRsZXQgZmlsZXMgPSBbXTtcblx0XHR0aGlzLl9leGlzdGluZ0ZpbGVuYW1lcyA9IFtdO1xuXG5cdFx0Zm9yIChsZXQgZiA9IDA7IGYgPCB3b3Jkc0ZpbGVzTGlzdC5zaXplKCk7IGYrKylcblx0XHR7XG5cdFx0XHRjb25zdCBmaWxlID0gd29yZHNGaWxlc0xpc3QuZ2V0U0ZTT2JqZWN0KGYpO1xuXG5cdFx0XHRjb25zdCBmaWxlT2JqID0ge307XG5cdFx0XHRmaWxlT2JqLm5hbWUgPSBmaWxlLmdldFV0ZlN0cmluZygnbmFtZScpO1xuXHRcdFx0ZmlsZU9iai5kYXRlID0gZmlsZS5nZXRVdGZTdHJpbmcoJ2RhdGUnKSArICcgJyArIGZpbGUuZ2V0VXRmU3RyaW5nKCd0aW1lJyk7XG5cdFx0XHRmaWxlT2JqLnNpemUgPSBieXRlc1RvU2l6ZShmaWxlLmdldExvbmcoJ3NpemUnKSwgMik7XG5cblx0XHRcdC8vIFBvcHVsYXRlIGZpbGVzIGxpc3Rcblx0XHRcdGZpbGVzLnB1c2goZmlsZU9iaik7XG5cblx0XHRcdC8vIFNhdmUgcmVmIHRvIGV4aXN0aW5nIGZpbGVuYW1lcywgdG8gY2hlY2sgdGhlbSB3aGVuIGEgbmV3IGZpbGUgaXMgY3JlYXRlZFxuXHRcdFx0dGhpcy5fZXhpc3RpbmdGaWxlbmFtZXMucHVzaChmaWxlT2JqLm5hbWUpO1xuXHRcdH1cblxuXHRcdC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBncmlkXG5cdFx0dGhpcy5fc2V0V29yZHNGaWxlc0dyaWREYXRhU291cmNlKGZpbGVzKTtcblx0XHR0aGlzLl9vbldvcmRzRmlsZXNHcmlkU2VsZWN0aW9uQ2hhbmdlKCk7XG5cblx0XHQvLyBFbmFibGVcblx0XHR0aGlzLmVuYWJsZWQgPSB0cnVlO1xuXHR9XG5cblx0Z2V0U2VsZWN0ZWRXb3Jkc0ZpbGVOYW1lKClcblx0e1xuXHRcdGlmICh0aGlzLl93b3Jkc0ZpbGVzR3JpZC5zZWxlY3QoKSAhPSBudWxsKVxuXHRcdHtcblx0XHRcdGxldCBzZWxlY3RlZEluZGV4ID0gdGhpcy5fd29yZHNGaWxlc0dyaWQuc2VsZWN0KCkuaW5kZXgoKTtcblx0XHRcdHJldHVybiB0aGlzLl93b3Jkc0ZpbGVzR3JpZC5kYXRhU291cmNlLmF0KHNlbGVjdGVkSW5kZXgpLm5hbWU7XG5cdFx0fVxuXHRcdGVsc2Vcblx0XHRcdHJldHVybiBudWxsO1xuXHR9XG5cblx0ZWRpdFdvcmRzRmlsZShmaWxlbmFtZSwgY29udGVudClcblx0e1xuXHRcdHRoaXMuX2lzTmV3RmlsZSA9IGZhbHNlO1xuXG5cdFx0dGhpcy5lbmFibGVkID0gdHJ1ZTtcblxuXHRcdC8vIFNob3cgbW9kYWxcblx0XHR0aGlzLl9zaG93TW9kYWwoKTtcblxuXHRcdC8vIFJlbW92ZSBkZWZhdWx0IGV4dGVuc2lvbiBmcm9tIGZpbGVuYW1lXG5cdFx0ZmlsZW5hbWUgPSBmaWxlbmFtZS5zdWJzdHJpbmcoMCwgZmlsZW5hbWUubGFzdEluZGV4T2YodGhpcy5XT1JEU19GSUxFX0VYVCkpO1xuXG5cdFx0Ly8gRW50ZXIgY29udGVudCBmaWxlbmFtZSBhbmQgY29udGVudCBpbiBtb2RhbCBmb3JtXG5cdFx0JCgnI2VkaXRNb2RhbCAjZmlsZW5hbWUnLCAkKHRoaXMpKS52YWwoZmlsZW5hbWUpO1xuXHRcdCQoJyNlZGl0TW9kYWwgI2NvbnRlbnQnLCAkKHRoaXMpKS52YWwoY29udGVudCk7XG5cblx0XHQvLyBTZXQgZmlsZW5hbWUgZmllbGQgYXMgbm90IGVkaXRhYmxlIGFuZCBoaWRlIG5vdGVcblx0XHQkKCcjZWRpdE1vZGFsICNmaWxlbmFtZScsICQodGhpcykpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0JCgnI2VkaXRNb2RhbCAjZmlsZW5hbWVOb3RlJywgJCh0aGlzKSkuaGlkZSgpO1xuXHR9XG5cblx0Z2V0RXhpc3RpbmdGaWxlbmFtZXMoKVxuXHR7XG5cdFx0cmV0dXJuIHRoaXMuX2V4aXN0aW5nRmlsZW5hbWVzO1xuXHR9XG5cblx0X3NldFdvcmRzRmlsZXNHcmlkRGF0YVNvdXJjZShkcylcblx0e1xuXHRcdC8vIFJlYWQgY3VycmVudCBob3Jpem9udGFsIHNjcm9sbCB2YWx1ZVxuXHQgICBjb25zdCBzY3JvbGxMZWZ0ID0gJCgnI3dvcmRzRmlsZXMgLmstZ3JpZC1jb250ZW50JywgdGhpcy5fd29yZHNGaWxlc0dyaWQud3JhcHBlcikuc2Nyb2xsTGVmdCgpO1xuXG5cdCAgIC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBncmlkXG5cdCAgIHRoaXMuX3dvcmRzRmlsZXNHcmlkLnNldERhdGFTb3VyY2UoZHMpO1xuXG5cdCAgIC8vIFNldCBob3Jpem9udGFsIHNjcm9sbFxuXHQgICAkKCcjd29yZHNGaWxlcyAuay1ncmlkLWNvbnRlbnQnLCB0aGlzLl93b3Jkc0ZpbGVzR3JpZC53cmFwcGVyKS5zY3JvbGxMZWZ0KHNjcm9sbExlZnQpO1xuXHR9XG5cblx0X29uV29yZHNGaWxlc0dyaWRTZWxlY3Rpb25DaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgYnV0dG9uc1xuXHRcdGNvbnN0IHNlbGVjdGVkUm93cyA9IHRoaXMuX3dvcmRzRmlsZXNHcmlkLnNlbGVjdCgpO1xuXHRcdCQoJyNlZGl0QnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCBzZWxlY3RlZFJvd3MubGVuZ3RoID09IDApO1xuXHRcdCQoJyNyZW1vdmVCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIHNlbGVjdGVkUm93cy5sZW5ndGggPT0gMCk7XG5cdFx0JCgnI2Fzc2lnbkJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgc2VsZWN0ZWRSb3dzLmxlbmd0aCA9PSAwKTtcblx0fVxuXG5cdF9vblJlbG9hZENsaWNrKClcblx0e1xuXHRcdC8vIEZpcmUgZXZlbnQgdG8gcmVxdWVzdCBmaWxlIGNvbnRlbnQgdG8gc2VydmVyXG5cdFx0bGV0IGV2dCA9IG5ldyBDdXN0b21FdmVudCh0aGlzLlJFRlJFU0hfV09SRFNfRklMRVNfQ0xJQ0tfRVZFTlQsIHtcblx0ICAgIFx0ZGV0YWlsOiBudWxsLFxuXHRcdFx0YnViYmxlczogZmFsc2UsXG5cdFx0XHRjYW5jZWxhYmxlOiBmYWxzZVxuXHRcdH0pO1xuXG5cdFx0dGhpcy5kaXNwYXRjaEV2ZW50KGV2dCk7XG5cdH1cblxuXHRfb25BZGRDbGljaygpXG5cdHtcblx0XHR0aGlzLl9pc05ld0ZpbGUgPSB0cnVlO1xuXG5cdFx0Ly8gU2hvdyBtb2RhbFxuXHRcdHRoaXMuX3Nob3dNb2RhbCgpO1xuXHR9XG5cblx0X29uRWRpdENsaWNrKClcblx0e1xuXHRcdC8vIERpc2FibGUgYnV0dG9uc1xuXHRcdHRoaXMuZW5hYmxlZCA9IGZhbHNlO1xuXHRcdHRoaXMuYWN0aW9uU3Bpbm5lclZpc2libGUgPSB0cnVlO1xuXG5cdFx0Ly8gRmlyZSBldmVudCB0byByZXF1ZXN0IGZpbGUgY29udGVudCB0byBzZXJ2ZXJcblx0XHRsZXQgZXZ0ID0gbmV3IEN1c3RvbUV2ZW50KHRoaXMuRURJVF9XT1JEU19GSUxFX0NMSUNLX0VWRU5ULCB7XG5cdCAgICBcdGRldGFpbDogdGhpcy5nZXRTZWxlY3RlZFdvcmRzRmlsZU5hbWUoKSxcblx0XHRcdGJ1YmJsZXM6IGZhbHNlLFxuXHRcdFx0Y2FuY2VsYWJsZTogZmFsc2Vcblx0XHR9KTtcblxuXHRcdHRoaXMuZGlzcGF0Y2hFdmVudChldnQpO1xuXHR9XG5cblx0X29uUmVtb3ZlQ2xpY2soKVxuXHR7XG5cdFx0Ly8gRmlyZSBldmVudCB0byByZXF1ZXN0IGZpbGUgcmVtb3ZhbCB0byBzZXJ2ZXJcblx0XHRsZXQgZXZ0ID0gbmV3IEN1c3RvbUV2ZW50KHRoaXMuUkVNT1ZFX1dPUkRTX0ZJTEVfQ0xJQ0tfRVZFTlQsIHtcblx0ICAgIFx0ZGV0YWlsOiB0aGlzLmdldFNlbGVjdGVkV29yZHNGaWxlTmFtZSgpLFxuXHRcdFx0YnViYmxlczogZmFsc2UsXG5cdFx0XHRjYW5jZWxhYmxlOiBmYWxzZVxuXHRcdH0pO1xuXG5cdFx0dGhpcy5kaXNwYXRjaEV2ZW50KGV2dCk7XG5cdH1cblxuXHRfb25Bc3NpZ25DbGljaygpXG5cdHtcblx0XHQvLyBGaXJlIGV2ZW50IHRvIHN1YnN0aXR1dGUgcGF0aCBpbiBjb25maWd1cmF0aW9uXG5cdFx0bGV0IGV2dCA9IG5ldyBDdXN0b21FdmVudCh0aGlzLkFTU0lHTl9XT1JEU19GSUxFX0NMSUNLX0VWRU5ULCB7XG5cdCAgICBcdGRldGFpbDogdGhpcy5DT05GSUdfRk9MREVSICsgdGhpcy5nZXRTZWxlY3RlZFdvcmRzRmlsZU5hbWUoKSxcblx0XHRcdGJ1YmJsZXM6IGZhbHNlLFxuXHRcdFx0Y2FuY2VsYWJsZTogZmFsc2Vcblx0XHR9KTtcblxuXHRcdHRoaXMuZGlzcGF0Y2hFdmVudChldnQpO1xuXHR9XG5cblx0X29uU2F2ZVdvcmRGaWxlQ2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX3ZhbGlkYXRvci52YWxpZGF0ZSgpKVxuXHRcdHtcblx0XHRcdC8vIFNob3cgc3Bpbm5lclxuXHRcdFx0JCgnI2VkaXRNb2RhbCAjc2F2ZVNwaW5uZXInLCAkKHRoaXMpKS5zaG93KCk7XG5cblx0XHRcdC8vIEZpcmUgZXZlbnQgdG8gcmVxdWVzdCBmaWxlIHRvIGJlIHNhdmVkIGJ5IHNlcnZlclxuXHRcdFx0bGV0IGV2dCA9IG5ldyBDdXN0b21FdmVudCh0aGlzLlNBVkVfV09SRFNfRklMRV9DTElDS19FVkVOVCwge1xuXHRcdCAgICBcdGRldGFpbDoge1xuXHRcdFx0XHRcdGZpbGVuYW1lOiAkKCcjZWRpdE1vZGFsICNmaWxlbmFtZScsICQodGhpcykpLnZhbCgpICsgdGhpcy5XT1JEU19GSUxFX0VYVCxcblx0XHRcdFx0XHRpc05ldzogdGhpcy5faXNOZXdGaWxlLFxuXHRcdFx0XHRcdGNvbnRlbnQ6ICQoJyNlZGl0TW9kYWwgI2NvbnRlbnQnLCAkKHRoaXMpKS52YWwoKVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRidWJibGVzOiBmYWxzZSxcblx0XHRcdFx0Y2FuY2VsYWJsZTogZmFsc2Vcblx0XHRcdH0pO1xuXG5cdFx0XHR0aGlzLmRpc3BhdGNoRXZlbnQoZXZ0KTtcblx0XHR9XG5cdH1cblxuXHRfc2hvd01vZGFsKClcblx0e1xuXHRcdC8vIEFwcGVuZCBtb2RhbCBodG1sXG5cdFx0JCh0aGlzKS5hcHBlbmQodGhpcy5fbW9kYWxIdG1sKTtcblxuXHRcdGxldCBtb2RhbEVsZW1lbnQgPSAkKCcjZWRpdE1vZGFsJywgJCh0aGlzKSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGtlbmRvIHZhbGlkYXRpb25cblx0XHR0aGlzLl92YWxpZGF0b3IgPSBtb2RhbEVsZW1lbnQuZmluZCgnI2VkaXRGaWVsZHNldCcpLmtlbmRvVmFsaWRhdG9yKHtcblx0XHRcdHZhbGlkYXRlT25CbHVyOiB0cnVlLFxuXHRcdFx0cnVsZXM6IHtcblx0XHRcdFx0cmVxdWlyZWRGaWxlbmFtZTogJC5wcm94eShmdW5jdGlvbihpbnB1dCkge1xuXHRcdFx0XHRcdGxldCB2YWxpZCA9IHRydWU7XG5cdFx0XHRcdFx0aWYgKGlucHV0LmlzKCdbbmFtZT1maWxlbmFtZV0nKSlcblx0XHRcdFx0XHRcdHZhbGlkID0gaW5wdXQudmFsKCkgIT09ICcnO1xuXHRcdFx0XHRcdHJldHVybiB2YWxpZDtcblx0XHRcdFx0fSwgdGhpcyksXG5cdFx0XHRcdHZhbGlkRmlsZW5hbWU6ICQucHJveHkoZnVuY3Rpb24oaW5wdXQpIHtcblx0XHRcdFx0XHRsZXQgdmFsaWQgPSB0cnVlO1xuXHRcdFx0XHRcdGlmIChpbnB1dC5pcygnW25hbWU9ZmlsZW5hbWVdJykpXG5cdFx0XHRcdFx0XHR2YWxpZCA9ICghaW5wdXQudmFsKCkuaW5jbHVkZXMoJ1xcXFwnKSAmJiAhaW5wdXQudmFsKCkuaW5jbHVkZXMoJy8nKSk7XG5cdFx0XHRcdFx0cmV0dXJuIHZhbGlkO1xuXHRcdFx0XHR9LCB0aGlzKVxuXHRcdFx0fSxcblx0XHRcdG1lc3NhZ2VzOiB7XG5cdFx0XHRcdHJlcXVpcmVkRmlsZW5hbWU6ICdSZXF1aXJlZCcsXG5cdFx0XHRcdHZhbGlkRmlsZW5hbWU6ICdDb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMgKHNsYXNoLCBiYWNrc2xhc2gpJ1xuXHRcdFx0fVxuXHRcdH0pLmRhdGEoJ2tlbmRvVmFsaWRhdG9yJyk7XG5cblx0XHQvLyBIaWRlIHNhdmUgc3Bpbm5lclxuXHRcdCQoJyNzYXZlU3Bpbm5lcicsIG1vZGFsRWxlbWVudCkuaGlkZSgpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIFNhdmUgYnV0dG9uIGNsaWNrXG5cdFx0JCgnI3NhdmVXb3JkRmlsZUJ1dHRvbicsIG1vZGFsRWxlbWVudCkub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblNhdmVXb3JkRmlsZUNsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBBZGQgbGlzdGVuZXIgdG8gbW9kYWwgaGlkZSBldmVudFxuXHRcdG1vZGFsRWxlbWVudC5vbignaGlkZGVuLmJzLm1vZGFsJywgJC5wcm94eSh0aGlzLl9kZXN0cm95TW9kYWwsIHRoaXMpKTtcblxuXHRcdC8vIEluaXRpYWxpemUgYm9vdHN0cmFwIG1vZGFsXG5cdFx0bW9kYWxFbGVtZW50Lm1vZGFsKHtcblx0XHRcdGJhY2tkcm9wOiAnc3RhdGljJyxcblx0XHRcdGtleWJvYXJkOiBmYWxzZSxcblx0XHR9KTtcblx0fVxuXG5cdF9kZXN0cm95TW9kYWwoKVxuXHR7XG5cdFx0bGV0IG1vZGFsRWxlbWVudCA9ICQoJyNlZGl0TW9kYWwnLCAkKHRoaXMpKTtcblxuXHRcdGlmIChtb2RhbEVsZW1lbnQpXG5cdFx0e1xuXHRcdFx0Ly8gUmVtb3ZlIGxpc3RlbmVyc1xuXHRcdFx0JCgnI3NhdmVXb3JkRmlsZUJ1dHRvbicsIG1vZGFsRWxlbWVudCkub2ZmKCdjbGljaycpO1xuXHRcdFx0bW9kYWxFbGVtZW50Lm9mZignaGlkZGVuLmJzLm1vZGFsJyk7XG5cblx0XHRcdC8vIERlc3Ryb3kgZXZlcnl0aGluZyBLZW5kb1xuXHRcdFx0a2VuZG8uZGVzdHJveShtb2RhbEVsZW1lbnQpO1xuXG5cdFx0XHQvLyBEaXNwb3NlIG1vZGFsXG5cdFx0XHRtb2RhbEVsZW1lbnQubW9kYWwoJ2Rpc3Bvc2UnKTtcblxuXHRcdFx0Ly8gUmVtb3ZlIGh0bWxcblx0XHRcdG1vZGFsRWxlbWVudC5yZW1vdmUoKTtcblx0XHRcdG1vZGFsRWxlbWVudCA9IG51bGw7XG5cdFx0fVxuXHR9XG59XG5cbi8vIERFRklORSBDT01QT05FTlRcbmlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzLmdldCgnd29yZHMtZmlsZXMtbWFuYWdlcicpKVxuXHR3aW5kb3cuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd3b3Jkcy1maWxlcy1tYW5hZ2VyJywgV29yZHNGaWxlc01hbmFnZXIpO1xuIiwiZXhwb3J0IGNsYXNzIFNpZGViYXJMYXlvdXQgZXh0ZW5kcyBIVE1MRWxlbWVudFxue1xuXHRjb25zdHJ1Y3RvcigpXG5cdHtcblx0ICAgIHN1cGVyKCk7XG5cblx0XHQvLyBBdHRhY2ggYSBzaGFkb3cgcm9vdFxuXHRcdGNvbnN0IHNoYWRvd1Jvb3QgPSB0aGlzLmF0dGFjaFNoYWRvdyh7bW9kZTogJ29wZW4nfSk7XG5cdFx0c2hhZG93Um9vdC5pbm5lckhUTUwgPSBgXG5cdFx0XHQ8c3R5bGU+XG5cdFx0XHRcdDpob3N0IHtcblx0XHRcdFx0XHRkaXNwbGF5OiBmbGV4O1xuXHRcdFx0XHRcdGZsZXgtZGlyZWN0aW9uOiByb3c7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRAbWVkaWEgKG1heC13aWR0aDogNTc1Ljk4cHgpIHtcblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQteHMpIDo6c2xvdHRlZCg6bm90KFthcmlhLXNlbGVjdGVkPVwidHJ1ZVwiXSkpIHtcblx0XHRcdFx0XHRcdGRpc3BsYXk6IG5vbmUgIWltcG9ydGFudDtcblx0XHRcdFx0ICAgIH1cblxuXHRcdFx0XHRcdDpob3N0KC5zcGxpdC14cykgOjpzbG90dGVkKFthcmlhLXNlbGVjdGVkPVwidHJ1ZVwiXSkge1xuXHRcdFx0XHRcdFx0ZmxleC1ncm93OiAxO1xuXHRcdFx0XHQgICAgfVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0QG1lZGlhIChtYXgtd2lkdGg6IDc2Ny45OHB4KSB7XG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LXNtKSA6OnNsb3R0ZWQoOm5vdChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pKSB7XG5cdFx0XHRcdFx0XHRkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7XG5cdFx0XHRcdCAgICB9XG5cblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQtc20pIDo6c2xvdHRlZChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pIHtcblx0XHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0ICAgIH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdEBtZWRpYSAobWF4LXdpZHRoOiA5OTEuOThweCkge1xuXHRcdFx0XHRcdDpob3N0KC5zcGxpdC1tZCkgOjpzbG90dGVkKDpub3QoW2FyaWEtc2VsZWN0ZWQ9XCJ0cnVlXCJdKSkge1xuXHRcdFx0XHRcdFx0ZGlzcGxheTogbm9uZSAhaW1wb3J0YW50O1xuXHRcdFx0XHQgICAgfVxuXG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LW1kKSA6OnNsb3R0ZWQoW2FyaWEtc2VsZWN0ZWQ9XCJ0cnVlXCJdKSB7XG5cdFx0XHRcdFx0XHRmbGV4LWdyb3c6IDE7XG5cdFx0XHRcdCAgICB9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRAbWVkaWEgKG1heC13aWR0aDogMTE5OS45OHB4KSB7XG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LWxnKSA6OnNsb3R0ZWQoOm5vdChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pKSB7XG5cdFx0XHRcdFx0XHRkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7XG5cdFx0XHRcdCAgICB9XG5cblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQtbGcpIDo6c2xvdHRlZChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pIHtcblx0XHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0ICAgIH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC5zaWRlLWNvbDo6c2xvdHRlZCgqKSB7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQubWFpbi1jb2w6OnNsb3R0ZWQoKikge1xuXHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0fVxuXHRcdFx0PC9zdHlsZT5cblxuXHRcdFx0PHNsb3QgY2xhc3M9XCJzaWRlLWNvbFwiIG5hbWU9XCJzaWRlLWNvbHVtblwiPjwvc2xvdD5cblx0XHRcdDxzbG90IGNsYXNzPVwibWFpbi1jb2xcIiBuYW1lPVwibWFpbi1jb2x1bW5cIj48L3Nsb3Q+XG5cdFx0YDtcblxuXHRcdC8vIFNldCBpbml0aWFsIHNlbGVjdGlvblxuXHRcdHRoaXMuc2VsZWN0ZWRJbmRleCA9IDA7XG5cdH1cblxuXHRnZXQgc2VsZWN0ZWRQYW5lbCgpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5fc2VsZWN0ZWRQYW5lbDtcblx0fVxuXG5cdHNldCBzZWxlY3RlZFBhbmVsKGVsZW1lbnQpIC8vICdzaWRlJyBvciAnbWFpbidcblx0e1xuXHRcdGlmIChlbGVtZW50ICE9IG51bGwgJiYgZWxlbWVudC5wYXJlbnROb2RlID09IHRoaXMpXG5cdFx0e1xuXHRcdFx0dGhpcy5fc2VsZWN0ZWRQYW5lbCA9IGVsZW1lbnQ7XG5cblx0XHRcdGZvciAobGV0IGVsZW1lbnQgb2YgdGhpcy5jaGlsZHJlbilcblx0XHRcdHtcblx0XHRcdFx0aWYgKGVsZW1lbnQgPT0gdGhpcy5fc2VsZWN0ZWRQYW5lbClcblx0XHRcdFx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZSgnYXJpYS1zZWxlY3RlZCcsICd0cnVlJyk7XG5cdFx0XHRcdGVsc2Vcblx0XHRcdFx0XHRlbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSgnYXJpYS1zZWxlY3RlZCcpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRlbHNlXG5cdFx0e1xuXHRcdFx0Y29uc29sZS5lcnJvcignRWxlbWVudCBpcyBub3QgYSBjaGlsZCBvZiBTaWRlYmFyTGF5b3V0Jyk7XG5cdFx0fVxuXHR9XG5cblx0Z2V0IHNlbGVjdGVkSW5kZXgoKVxuXHR7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20odGhpcy5jaGlsZHJlbikuaW5kZXhPZih0aGlzLl9zZWxlY3RlZFBhbmVsKTtcblx0fVxuXG5cdHNldCBzZWxlY3RlZEluZGV4KGluZGV4KVxuXHR7XG5cdFx0aWYgKHRoaXMuY2hpbGRyZW4ubGVuZ3RoID4gMClcblx0XHR7XG5cdFx0XHRpZiAodGhpcy5jaGlsZHJlbltpbmRleF0gPT0gbnVsbClcblx0XHRcdHtcblx0XHRcdFx0Y29uc29sZS5lcnJvcignSW52YWxpZCBTaWRlYmFyTGF5b3V0IGluZGV4Jyk7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0bGV0IGVsZW1lbnQgPSB0aGlzLmNoaWxkcmVuW2luZGV4XTtcblx0XHRcdHRoaXMuc2VsZWN0ZWRQYW5lbCA9IGVsZW1lbnQ7XG5cdFx0fVxuXHR9XG59XG5cbi8vIERFRklORSBDT01QT05FTlRcbmlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzLmdldCgnc2lkZWJhci1sYXlvdXQnKSlcblx0d2luZG93LmN1c3RvbUVsZW1lbnRzLmRlZmluZSgnc2lkZWJhci1sYXlvdXQnLCBTaWRlYmFyTGF5b3V0KTtcbiIsImltcG9ydCB7QmFzZU1vZHVsZX0gZnJvbSAnLi9iYXNlLW1vZHVsZSc7XG5pbXBvcnQge1ZpZXdTdGFja30gZnJvbSAnLi4vY29tcG9uZW50cy92aWV3LXN0YWNrJztcbmltcG9ydCB7U2lkZWJhckxheW91dH0gZnJvbSAnLi4vY29tcG9uZW50cy9zaWRlYmFyLWxheW91dCc7XG5pbXBvcnQge0NvbmZpZ0ludGVyZmFjZUJ1aWxkZXJ9IGZyb20gJy4uL3V0aWxzL3VpYnVpbGRlci9jb25maWctaW50ZXJmYWNlLWJ1aWxkZXInO1xuaW1wb3J0IHtjYXBpdGFsaXplRmlyc3QsIGZpbHRlckNsYXNzTmFtZX0gZnJvbSAnLi4vdXRpbHMvdXRpbGl0aWVzJztcbmltcG9ydCB7V29yZHNGaWxlc01hbmFnZXJ9IGZyb20gJy4uL2NvbXBvbmVudHMvbW9kdWxlLXNwZWNpZmljL3dvcmRzLWZpbGVzLW1hbmFnZXInO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBab25lQ29uZmlndXJhdG9yIGV4dGVuZHMgQmFzZU1vZHVsZVxue1xuXHRjb25zdHJ1Y3RvcigpXG5cdHtcblx0ICAgIHN1cGVyKCd6b25lQ29uZmlnJyk7XG5cblx0XHR0aGlzLklURU1fVFlQRV9aT05FID0gJ3pvbmUnO1xuXHRcdHRoaXMuSVRFTV9UWVBFX1JPT00gPSAncm9vbSc7XG5cblx0XHQvLyBPdXRnb2luZyByZXF1ZXN0c1xuXHRcdHRoaXMuUkVRX0dFVF9aT05FUyA9ICdnZXRab25lcyc7XG5cblx0XHR0aGlzLlJFUV9HRVRfWk9ORV9DT05GSUcgPSAnZ2V0Wm9uZUNvbmZpZyc7XG5cdFx0dGhpcy5SRVFfU0FWRV9aT05FX0NPTkZJRyA9ICdzYXZlWm9uZUNvbmZpZyc7XG5cdFx0dGhpcy5SRVFfTkVXX1pPTkVfQ09ORklHID0gJ25ld1pvbmVDb25maWcnO1xuXHRcdHRoaXMuUkVRX0RFTEVURV9aT05FX0NPTkZJRyA9ICdkZWxab25lQ29uZmlnJztcblx0XHR0aGlzLlJFUV9BQ1RJVkFURV9aT05FID0gJ2FjdFpvbmUnO1xuXG5cdFx0dGhpcy5SRVFfR0VUX1JPT01fQ09ORklHID0gJ2dldFJvb21Db25maWcnO1xuXHRcdHRoaXMuUkVRX1NBVkVfUk9PTV9DT05GSUcgPSAnc2F2ZVJvb21Db25maWcnO1xuXHRcdHRoaXMuUkVRX05FV19ST09NX0NPTkZJRyA9ICduZXdSb29tQ29uZmlnJztcblx0XHR0aGlzLlJFUV9ERUxFVEVfUk9PTV9DT05GSUcgPSAnZGVsUm9vbUNvbmZpZyc7XG5cblx0XHR0aGlzLlJFUV9SRUZSRVNIX1dPUkRTX0ZJTEUgPSAncmVmcmVzaFdvcmRzRmlsZXMnO1xuXHRcdHRoaXMuUkVRX0VESVRfV09SRFNfRklMRSA9ICdlZGl0V29yZHNGaWxlJztcblx0XHR0aGlzLlJFUV9TQVZFX1dPUkRTX0ZJTEUgPSAnc2F2ZVdvcmRzRmlsZSc7XG5cdFx0dGhpcy5SRVFfREVMRVRFX1dPUkRTX0ZJTEUgPSAnZGVsV29yZHNGaWxlJztcblxuXHRcdC8vIEluY29taW5nIHJlc3BvbnNlc1xuXHRcdHRoaXMuUkVTUF9aT05FUyA9ICd6b25lcyc7XG5cblx0XHR0aGlzLlJFU1BfWk9ORV9DT05GSUcgPSAnem9uZUNvbmZpZyc7XG5cdFx0dGhpcy5SRVNQX1pPTkVfQ09ORklHX1VQREFURV9DT05GSVJNID0gJ3pvbmVDZmdVcGQnO1xuXHRcdHRoaXMuUkVTUF9aT05FX0FEREVEID0gJ3pvbmVBZGRlZCc7XG5cdFx0dGhpcy5SRVNQX1pPTkVfUkVGVVNFRCA9ICd6b25lUmVmdXNlZCc7XG5cdFx0dGhpcy5SRVNQX1pPTkVfREVMRVRFRCA9ICd6b25lRGVsJztcblx0XHR0aGlzLlJFU1BfWk9ORV9BQ1RJVkFURUQgPSAnem9uZUFjdCc7XG5cdFx0dGhpcy5SRVNQX1pPTkVfQUNUSVZBVElPTl9FUlJPUiA9ICd6b25lQWN0RXJyJztcblxuXHRcdHRoaXMuUkVTUF9ST09NX0NPTkZJRyA9ICdyb29tQ29uZmlnJztcblx0XHR0aGlzLlJFU1BfUk9PTV9DT05GSUdfVVBEQVRFX0NPTkZJUk0gPSAncm9vbUNmZ1VwZCc7XG5cdFx0dGhpcy5SRVNQX1JPT01fQURERUQgPSAncm9vbUFkZGVkJztcblx0XHR0aGlzLlJFU1BfUk9PTV9SRUZVU0VEID0gJ3Jvb21SZWZ1c2VkJztcblx0XHR0aGlzLlJFU1BfUk9PTV9ERUxFVEVEID0gJ3Jvb21EZWwnO1xuXG5cdFx0dGhpcy5SRVNQX1JFRlJFU0hfV09SRFNfRklMRVMgPSAncmVmcmVzaFdvcmRzRmlsZXMnO1xuXHRcdHRoaXMuUkVTUF9XT1JEU19GSUxFX0NPTlRFTlQgPSAnd29yZHNGaWxlJztcblx0XHR0aGlzLlJFU1BfV09SRFNfRklMRV9FUlJPUiA9ICd3b3Jkc0ZpbGVFcnInO1xuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gQ09NTU9OIE1PRFVMRSBJTlRFUkZBQ0UgTUVUSE9EU1xuXHQvLyBUaGlzIG1lbWJlcnMgYXJlIHVzZWQgYnkgdGhlIG1haW4gY29udHJvbGxlclxuXHQvLyB0byBjb21tdW5pY2F0ZSB3aXRoIHRoZSBtb2R1bGUncyBjb250cm9sbGVyLlxuXHQvLyBUaGlzIG1ldGhvZHMgb3ZlcnJpZGUgdGhvc2UgaW4gQmFzZU1vZHVsZSBjbGFzcy5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRpbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKVxuXHR7XG5cdFx0Ly8gQ2FsbCBzdXBlciBtZXRob2Rcblx0XHRzdXBlci5pbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKTtcblxuXHRcdC8vIENyZWF0ZSBpbnRlcmZhY2UgYnVpbGRlciBpbnN0YW5jZVxuXHRcdHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIgPSBuZXcgQ29uZmlnSW50ZXJmYWNlQnVpbGRlcigpO1xuXG5cdFx0Ly8gU2V0IGxpc3RlbmVyIGZvciBjdXN0b20gYWN0aW9ucyB0cmlnZ2VyZWQgYnkgY29uZmlndXJhdGlvbiBpbnRlcmZhY2Vcblx0XHQkKCcjem5jLXRhYk5hdmlnYXRvcicpLm9uKCd2YWx1ZS1zZXQnLCAkLnByb3h5KHRoaXMuX29uQ29uZmlnVmFsdWVTZXQsIHRoaXMpKTtcblxuXHRcdC8vIEluaXRpYWxpemUgWm9uZXMvUm9vbXMgdHJlZXZpZXdcblx0XHR0aGlzLl90cmVldmlldyA9ICQoJyN6bmMtdHJlZVZpZXcnKS5rZW5kb1RyZWVWaWV3KHtcblx0XHRcdGxvYWRPbkRlbWFuZDogZmFsc2UsXG5cdFx0XHRkYXRhVGV4dEZpZWxkOiAnbmFtZScsXG5cdFx0XHR0ZW1wbGF0ZToga2VuZG8udGVtcGxhdGUoJzxzcGFuIGNsYXNzPVwiIyBpZiAoIWl0ZW0uYWN0aXZlKSB7ICMgaW5hY3RpdmUtbGlzdC1pdGVtICMgfSAjXCI+IzogaXRlbS5uYW1lICM8L3NwYW4+JyksXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkodGhpcy5fb25ab25lUm9vbUNoYW5nZSwgdGhpcyksXG5cdFx0fSkuZGF0YSgna2VuZG9UcmVlVmlldycpO1xuXG5cdFx0Ly8gTGlzdGVuIHRvIHRyZWV2aWV3IGRvdWJsZS1jbGljayBldmVudFxuXHRcdCQoJyN6bmMtdHJlZVZpZXcnKS5vbignZGJsY2xpY2snLCAkLnByb3h5KHRoaXMuX29uVHJlZUl0ZW1Eb3VibGVDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gUmVxdWVzdCB6b25lcyAmIHJvb21zIGxpc3QgdG8gc2VydmVyIGluc3RhbmNlXG5cdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9HRVRfWk9ORVMpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBwcm9ncmVzcyBiYXJcblx0XHQkKCcjem5jLXByb2dyZXNzQmFyJykua2VuZG9Qcm9ncmVzc0Jhcih7XG5cdFx0XHRtaW46IDAsXG4gICAgICAgICAgICBtYXg6IDEwMCxcblx0XHRcdHZhbHVlOiBmYWxzZSxcbiAgICAgICAgICAgIHR5cGU6ICd2YWx1ZScsXG4gICAgICAgICAgICBhbmltYXRpb246IHtcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNDAwXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVycyB0byB1dGlsaXR5IGJ1dHRvbnNcblx0XHQkKCcjem5jLWFkZFpvbmVCdXR0b24nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQWRkWm9uZUNsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI3puYy1hZGRSb29tQnV0dG9uJykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vbkFkZFJvb21DbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bmMtZWRpdEJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25FZGl0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5jLXJlbW92ZUJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25SZW1vdmVDbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bmMtYWN0aXZhdGVCdXR0b24nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQWN0aXZhdGVDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIGludGVyZmFjZSBidXR0b25zXG5cdFx0JCgnI3puYy1jYW5jZWxCdXR0b24nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQ2FuY2VsQ2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5jLXJlbG9hZEJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25SZWxvYWRDbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bmMtc3VibWl0QnV0dG9uJykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblN1Ym1pdENsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBTYXZlIHJlZiB0byB3b3JkcyBmaWxlcyBtYW5hZ2VyIGFuZCBhZGQgY3VzdG9tIGV2ZW50IGxpc3RlbmVyc1xuXHRcdHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3puYy13b3Jkc0ZpbGVzTWFuYWdlcicpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9uKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLlJFRlJFU0hfV09SRFNfRklMRVNfQ0xJQ0tfRVZFTlQsICQucHJveHkodGhpcy5fb25Xb3Jkc0ZpbGVSZWxvYWRDbGljaywgdGhpcykpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9uKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLkVESVRfV09SRFNfRklMRV9DTElDS19FVkVOVCwgJC5wcm94eSh0aGlzLl9vbldvcmRzRmlsZUVkaXRDbGljaywgdGhpcykpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9uKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLlNBVkVfV09SRFNfRklMRV9DTElDS19FVkVOVCwgJC5wcm94eSh0aGlzLl9vbldvcmRzRmlsZVNhdmVDbGljaywgdGhpcykpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9uKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLlJFTU9WRV9XT1JEU19GSUxFX0NMSUNLX0VWRU5ULCAkLnByb3h5KHRoaXMuX29uV29yZHNGaWxlUmVtb3ZlQ2xpY2ssIHRoaXMpKTtcblx0XHQkKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyKS5vbih0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5BU1NJR05fV09SRFNfRklMRV9DTElDS19FVkVOVCwgJC5wcm94eSh0aGlzLl9vbldvcmRzRmlsZUFzc2lnbkNsaWNrLCB0aGlzKSk7XG5cdH1cblxuXHRkZXN0cm95KClcblx0e1xuXHRcdC8vIENhbGwgc3VwZXIgbWV0aG9kXG5cdFx0c3VwZXIuZGVzdHJveSgpO1xuXG5cdFx0Ly8gUmVtb3ZlIHRyZWUgdmlldyBkb3VibGVjbGljayBsaXN0ZW5lclxuXHRcdCQoJyN6bmMtdHJlZVZpZXcnKS5vZmYoJ2RibGNsaWNrJyk7XG5cblx0XHQvLyBSZW1vdmUgbGlzdGVuZXIgZm9yIGN1c3RvbSBhY3Rpb25zIHRyaWdnZXJlZCBieSBjb25maWd1cmF0aW9uIGludGVyZmFjZVxuXHRcdCQoJyN6bmMtdGFiTmF2aWdhdG9yJykub2ZmKCd2YWx1ZS1zZXQnKTtcblxuXHRcdC8vIFJlbW92ZSBsaXN0ZW5lciBmb3Igem9uZS9yb29tIGFjdGl2YXRpb24gZXZlbnRcblx0XHQkKCcjem5jLWFkZFpvbmVCdXR0b24nKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI3puYy1hZGRSb29tQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bmMtZWRpdEJ1dHRvbicpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5jLXJlbW92ZUJ1dHRvbicpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5jLWFjdGl2YXRlQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXG5cdFx0Ly8gUmVtb3ZlIGludGVyZmFjZSBidXR0b25zIGNsaWNrIGxpc3RlbmVyc1xuXHRcdCQoJyN6bmMtY2FuY2VsQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bmMtcmVsb2FkQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bmMtc3VibWl0QnV0dG9uJykub2ZmKCdjbGljaycpO1xuXG5cdFx0Ly8gUmVtb3ZlIGxpc3RlbmVycyB0byB3b3JkcyBmaWxlcyBtYW5hZ2VyXG5cdFx0JCh0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlcikub2ZmKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLlJFRlJFU0hfV09SRFNfRklMRVNfQ0xJQ0tfRVZFTlQpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9mZih0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5FRElUX1dPUkRTX0ZJTEVfQ0xJQ0tfRVZFTlQpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9mZih0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5TQVZFX1dPUkRTX0ZJTEVfQ0xJQ0tfRVZFTlQpO1xuXHRcdCQodGhpcy5fd29yZHNGaWxlc01hbmFnZXIpLm9mZih0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5SRU1PVkVfV09SRFNfRklMRV9DTElDS19FVkVOVCk7XG5cdFx0JCh0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlcikub2ZmKHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLkFTU0lHTl9XT1JEU19GSUxFX0NMSUNLX0VWRU5UKTtcblxuXHRcdC8vIENsZWFyIHRhYnMgY29udGFpbmVyXG5cdFx0dGhpcy5fY2xlYXJUYWJzKCk7XG5cdH1cblxuXHRvbkV4dGVuc2lvbkNvbW1hbmQoY29tbWFuZCwgZGF0YSlcblx0e1xuXHRcdGNvbnN0IHVzZXJuYW1lID0gZGF0YS5nZXRVdGZTdHJpbmcoJ3VzZXInKTtcblxuXHRcdC8qKioqKiogWk9ORVMgJiBST09NUyAqKioqKiovXG5cblx0XHQvLyBab25lcyAmIHJvb21zIGxpc3QgcmVjZWl2ZWRcblx0XHRpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfWk9ORVMpXG5cdFx0XHR0aGlzLl9wb3B1bGF0ZVRyZWUoZGF0YSk7XG5cblx0XHQvLyBab25lIG9yIHJvb20gY29uZmlndXJhdGlvbiBkYXRhIHJlY2VpdmVkXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfWk9ORV9DT05GSUcgfHwgY29tbWFuZCA9PSB0aGlzLlJFU1BfUk9PTV9DT05GSUcpXG5cdFx0e1xuXHRcdFx0Ly8gQnVpbGQgdXNlciBpbnRlcmZhY2UgYmFzZWQgb24gcmVjZWl2ZWQgZGF0YVxuXHRcdFx0dGhpcy5faW50ZXJmYWNlQnVpbGRlci5idWlsZEludGVyZmFjZShkYXRhLmdldFNGU0FycmF5KCdzZXR0aW5ncycpLCAnem5jLXRhYk5hdmlnYXRvcicsIGZhbHNlKTtcblxuXHRcdFx0Ly8gRW5hYmxlIHNjcm9sbGluZyB0YWJzIChpZiBuZWVkZWQpXG5cdFx0XHRpZiAodGhpcy5fcmVpbml0VGFicylcblx0XHRcdHtcblx0XHRcdFx0JCgnI3puYy10YWJOYXZpZ2F0b3IgI3RhYnMnKS5zY3JvbGxpbmdUYWJzKHtcblx0XHRcdFx0XHRib290c3RyYXBWZXJzaW9uOiA0LFxuXHRcdFx0XHRcdHNjcm9sbFRvVGFiRWRnZTogdHJ1ZSxcblx0XHRcdFx0XHRlbmFibGVTd2lwaW5nOiB0cnVlLFxuXHRcdFx0XHRcdGRpc2FibGVTY3JvbGxBcnJvd3NPbkZ1bGx5U2Nyb2xsZWQ6IHRydWUsXG5cdFx0XHRcdFx0Y3NzQ2xhc3NMZWZ0QXJyb3c6ICdmYSBmYS1jaGV2cm9uLWxlZnQnLFxuXHRcdFx0XHRcdGNzc0NsYXNzUmlnaHRBcnJvdzogJ2ZhIGZhLWNoZXZyb24tcmlnaHQnXG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBFbmFibGUgaW50ZXJmYWNlXG5cdFx0XHR0aGlzLl9lbmFibGVDb25maWdJbnRlcmZhY2UodHJ1ZSk7XG5cdFx0fVxuXG5cdFx0LyoqKioqKiBaT05FUyAqKioqKiovXG5cblx0XHQvLyBab25lIGNvbmZpZ3VyYXRpb24gdXBkYXRlIGNvbmZpcm1hdGlvblxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1pPTkVfQ09ORklHX1VQREFURV9DT05GSVJNKVxuXHRcdHtcblx0XHRcdC8vIElmIGEgJ25hbWUnIHBhcmFtZXRlciBpcyByZWNlaXZlZCwgaXQgbWVhbnMgdGhlIHpvbmUgbmFtZSBjaGFuZ2VkLCBhbmQgd2UgaGF2ZSB0byB1cGRhdGUgdGhlIHpvbmVzIGxpc3Rcblx0XHRcdGlmIChkYXRhLmdldFV0ZlN0cmluZygnek5hbWUnKSAhPSBudWxsKVxuXHRcdFx0XHR0aGlzLl91cGRhdGVab25lTmFtZUluTGlzdChkYXRhLmdldEludCgneklkJyksIGRhdGEuZ2V0VXRmU3RyaW5nKCd6TmFtZScpKTtcblxuXHRcdFx0Ly8gSWYgdGhlIGN1cnJlbnQgdXNlciBpcyB0aGUgdXBkYXRlciwgc2hvdyBhIG5vdGlmaWNhdGlvbjsgb3RoZXJ3aXNlLCBzaG93IGEgZGlhbG9nIGJveCBzdWdnZXN0aW5nIHRvIHJlbG9hZFxuXHRcdFx0aWYgKHVzZXJuYW1lID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIEVuYWJsZSBpbnRlcmZhY2Vcblx0XHRcdFx0dGhpcy5fZW5hYmxlQ29uZmlnSW50ZXJmYWNlKHRydWUpO1xuXG5cdFx0XHRcdC8vIERpc3BsYXkgbm90aWZpY2F0aW9uXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1pvbmUgbW9kaWZpZWQnLCBgWm9uZSBzZXR0aW5ncyB1cGRhdGVkIHN1Y2Nlc3NmdWxseTsgY2hhbmdlcyB3aWxsIGJlIGFwcGxpZWQgb24gbmV4dCA8c3Ryb25nPnNlcnZlciByZXN0YXJ0PC9zdHJvbmc+YCk7XG5cblx0XHRcdFx0Ly8gUmVzZXQgdGhlICdtb2RpZmllZCcgZmxhZ1xuXHRcdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLnJlc2V0SXNNb2RpZmllZCgpO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBBbiBhbGVydCBib3ggaXMgZGlzcGxheWVkIGlmIHRoZSB1c2VyIGlzIGN1cnJlbnRseSBlZGl0aW5nIHRoZSBzYW1lIHpvbmVcblx0XHRcdFx0aWYgKGRhdGEuZ2V0SW50KCd6SWQnKSA9PSB0aGlzLl9lZGl0ZWRab25lSWQpXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBtb2RpZmllZCB0aGUgWm9uZSB5b3UgYXJlIGN1cnJlbnRseSBlZGl0aW5nOyBwbGVhc2UgcmVsb2FkIHRvIHVwZGF0ZSB5b3VyIHZpZXcuYCk7XG5cblx0XHRcdFx0XHQvLyBEaXNhYmxlIHN1Ym1pdCBidXR0b25cblx0XHRcdFx0XHQkKCcjem5jLXN1Ym1pdEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Ly8gRGlzcGxheSBub3RpZmljYXRpb25cblx0XHRcdFx0XHRpZiAoZGF0YS5nZXRVdGZTdHJpbmcoJ3pOYW1lJykgIT0gbnVsbClcblx0XHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1pvbmUgcmVuYW1lZCcsIGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBjaGFuZ2VkIHRoZSBuYW1lIG9uIG9uZSBvZiB0aGUgWm9uZXNgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIE5ldyB6b25lIGFkZGVkXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfWk9ORV9BRERFRClcblx0XHR7XG5cdFx0XHRjb25zdCB6b25lTmFtZSA9IGRhdGEuZ2V0U0ZTT2JqZWN0KCd6b25lJykuZ2V0VXRmU3RyaW5nKCduYW1lJyk7XG5cblx0XHRcdC8vIElmIHRoZSBjdXJyZW50IHVzZXIgaXMgdGhlIHVwZGF0ZXIsIHJlc2V0IHRoZSBpbnRlcmZhY2U7IG90aGVyd2lzZSwganVzdCBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHRpZiAodXNlcm5hbWUgPT0gdGhpcy5zbWFydEZveC5teVNlbGYubmFtZSlcblx0XHRcdHtcblx0XHRcdFx0Ly8gUmVzZXQgaW50ZXJmYWNlXG5cdFx0XHRcdHRoaXMuX29uQ2FuY2VsQ2xpY2soKTtcblxuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdab25lIGFkZGVkJywgYFpvbmUgJyR7em9uZU5hbWV9JyBjcmVhdGVkIHN1Y2Nlc3NmdWxseWApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdab25lIGFkZGVkJywgYEFkbWluaXN0cmF0b3IgJHt1c2VybmFtZX0gY3JlYXRlZCBab25lICcke3pvbmVOYW1lfSdgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQWRkIG5ldyB6b25lIHRvIHRyZWVcblx0XHRcdGxldCB6b25lc0RTID0gdGhpcy5fdHJlZXZpZXcuZGF0YVNvdXJjZTtcblx0XHRcdHpvbmVzRFMuYWRkKHRoaXMuX2NyZWF0ZVpvbmVPYmplY3QoZGF0YS5nZXRTRlNPYmplY3QoJ3pvbmUnKSkpO1xuXHRcdFx0em9uZXNEUy5zeW5jKCk7XG5cdFx0fVxuXG5cdFx0Ly8gTmV3IHpvbmUgY3JlYXRpb24gcmVmdXNlZCBkdWUgdG8gaW52YWxpZCB6b25lIG5hbWVcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9aT05FX1JFRlVTRUQpXG5cdFx0e1xuXHRcdFx0Ly8gUmUtZW5hYmxlIGludGVyZmFjZVxuXHRcdFx0dGhpcy5fZW5hYmxlQ29uZmlnSW50ZXJmYWNlKHRydWUpO1xuXG5cdFx0XHQvLyBTaG93IHdhcm5pbmdcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydCgnWm9uZSBjb25maWd1cmF0aW9uIGNhblxcJ3QgYmUgc2F2ZWQgYmVjYXVzZSBhbm90aGVyIFpvbmUgd2l0aCB0aGUgc2FtZSBuYW1lIGFscmVhZHkgZXhpc3RzLicsIHRydWUpO1xuXHRcdH1cblxuXHRcdC8vIEV4aXN0aW5nIHpvbmUgZGVsZXRlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1pPTkVfREVMRVRFRClcblx0XHR7XG5cdFx0XHQvLyBJZiB0aGUgY3VycmVudCB1c2VyIGlzIHRoZSBkZWxldGVyLCByZXNldCB0aGUgaW50ZXJmYWNlOyBvdGhlcndpc2UsIGp1c3Qgc2hvdyBhIG5vdGlmaWNhdGlvblxuXHRcdFx0aWYgKHVzZXJuYW1lID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIFJlLWVuYWJsZSBpbnRlcmZhY2Vcblx0XHRcdFx0dGhpcy5fZW5hYmxlTGlzdEludGVyZmFjZSh0cnVlKTtcblxuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdab25lIHJlbW92ZWQnLCBgWm9uZSAnJHtkYXRhLmdldFV0ZlN0cmluZygnek5hbWUnKX0nIGRlbGV0ZWQgc3VjY2Vzc2Z1bGx5YCk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlXG5cdFx0XHR7XG5cdFx0XHRcdC8vIEFuIGFsZXJ0IGJveCBpcyBkaXNwbGF5ZWQgaWYgdGhlIHVzZXIgaXMgY3VycmVudGx5IGVkaXRpbmcgdGhlIHNhbWUgem9uZVxuXHRcdFx0XHRpZiAoZGF0YS5nZXRJbnQoJ3pJZCcpID09IHRoaXMuX2VkaXRlZFpvbmVJZClcblx0XHRcdFx0e1xuXHRcdFx0XHRcdC8vIFNob3cgYWxlcnRcblx0XHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoYEFkbWluaXN0cmF0b3IgJHt1c2VybmFtZX0gaGFzIGRlbGV0ZWQgdGhlIFpvbmUgeW91IGFyZSBjdXJyZW50bHkgbW9kaWZ5aW5nOyB5b3UgaGF2ZSB0byBjYW5jZWwgeW91ciBlZGl0aW5nLmApO1xuXG5cdFx0XHRcdFx0Ly8gRGlzYWJsZSBzdWJtaXQgYW5kIHJlbG9hZCBidXR0b25zXG5cdFx0XHRcdFx0JCgnI3puYy1yZWxvYWRCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIHRydWUpO1xuXHRcdFx0XHRcdCQoJyN6bmMtc3VibWl0QnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCB0cnVlKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbHNlXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1pvbmUgcmVtb3ZlZCcsIGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGRlbGV0ZWQgWm9uZSAnJHtkYXRhLmdldFV0ZlN0cmluZygnek5hbWUnKX0nYCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gUmVzZXQgc2VsZWN0aW9uIGlmIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgaXRlbSBvciBpdHMgcGFyZW50IGlzIGJlaW5nIHJlbW92ZWRcblx0XHRcdGxldCBzZWxlY3RlZE5vZGUgPSB0aGlzLl90cmVldmlldy5zZWxlY3QoKTtcblx0XHRcdGxldCBzZWxlY3RlZERhdGFJdGVtID0gdGhpcy5fdHJlZXZpZXcuZGF0YUl0ZW0oc2VsZWN0ZWROb2RlKTtcblx0XHRcdGlmIChzZWxlY3RlZERhdGFJdGVtKVxuXHRcdFx0e1xuXHRcdFx0XHRpZiAoc2VsZWN0ZWREYXRhSXRlbS50eXBlID09IHRoaXMuSVRFTV9UWVBFX1pPTkUgJiYgc2VsZWN0ZWREYXRhSXRlbS5pZCA9PSBkYXRhLmdldEludCgneklkJykpXG5cdFx0XHRcdFx0dGhpcy5fZGVzZWxlY3RUcmVlSXRlbSgpO1xuXG5cdFx0XHRcdGlmIChzZWxlY3RlZERhdGFJdGVtLnR5cGUgPT0gdGhpcy5JVEVNX1RZUEVfUk9PTSlcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxldCBwYXJlbnREYXRhSXRlbSA9IHRoaXMuX3RyZWV2aWV3LmRhdGFJdGVtKHRoaXMuX3RyZWV2aWV3LnBhcmVudChzZWxlY3RlZE5vZGUpKTtcblxuXHRcdFx0XHRcdGlmIChwYXJlbnREYXRhSXRlbS5pZCA9PSBkYXRhLmdldEludCgneklkJykpXG5cdFx0XHRcdFx0XHR0aGlzLl9kZXNlbGVjdFRyZWVJdGVtKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gUmVtb3ZlIHpvbmUgZnJvbSB0cmVlXG5cdFx0XHRsZXQgZGF0YUl0ZW0gPSB0aGlzLl9nZXRab25lRGF0YUl0ZW1CeUlkKGRhdGEuZ2V0SW50KCd6SWQnKSk7XG5cdFx0XHRsZXQgem9uZXNEUyA9IHRoaXMuX3RyZWV2aWV3LmRhdGFTb3VyY2U7XG5cdFx0XHR6b25lc0RTLnJlbW92ZShkYXRhSXRlbSk7XG5cdFx0XHR6b25lc0RTLnN5bmMoKTtcblx0XHR9XG5cblx0XHQvLyBab25lIGFjdGl2YXRlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1pPTkVfQUNUSVZBVEVEKVxuXHRcdHtcblx0XHRcdC8vIFNldCB6b25lIGFjdGl2YXRpb24gc3RhdHVzXG5cdFx0XHRjb25zdCB6b25lTmFtZSA9IHRoaXMuX3NldFpvbmVBY3RpdmF0aW9uU3RhdHVzKGRhdGEuZ2V0SW50KCd6SWQnKSwgZGF0YS5nZXRVdGZTdHJpbmcoJ2FjdFJvb21zJyksIHRydWUpO1xuXG5cdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0aWYgKHVzZXJuYW1lID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1pvbmUgYWN0aXZhdGVkJywgYFpvbmUgJyR7em9uZU5hbWV9JyBhY3RpdmF0ZWQgc3VjY2Vzc2Z1bGx5YCk7XG5cdFx0XHRlbHNlXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1pvbmUgYWN0aXZhdGVkJywgYEFkbWluaXN0cmF0b3IgJHt1c2VybmFtZX0gYWN0aXZhdGVkIFpvbmUgJyR7em9uZU5hbWV9J2ApO1xuXHRcdH1cblxuXHRcdC8vIFpvbmUgYWN0aXZhdGlvbiBlcnJvclxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1pPTkVfQUNUSVZBVElPTl9FUlJPUilcblx0XHR7XG5cdFx0XHQvLyBTZXQgem9uZSBhY3RpdmF0aW9uIHN0YXR1c1xuXHRcdFx0dGhpcy5fc2V0Wm9uZUFjdGl2YXRpb25TdGF0dXMoZGF0YS5nZXRJbnQoJ3pJZCcpLCAnJywgZmFsc2UpO1xuXG5cdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoZGF0YS5nZXRVdGZTdHJpbmcoJ2Vycm9yJyksIHRydWUpO1xuXHRcdH1cblxuXHRcdC8qKioqKiogUk9PTVMgKioqKioqL1xuXG5cdFx0Ly8gUm9vbSBjb25maWd1cmF0aW9uIHVwZGF0ZSBjb25maXJtYXRpb25cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ST09NX0NPTkZJR19VUERBVEVfQ09ORklSTSlcblx0XHR7XG5cdFx0XHRpZiAoZGF0YS5nZXRVdGZTdHJpbmcoJ3JOYW1lJykgIT0gbnVsbClcblx0XHRcdFx0dGhpcy5fdXBkYXRlUm9vbU5hbWVJbkxpc3QoZGF0YS5nZXRJbnQoJ3pJZCcpLCBkYXRhLmdldEludCgncklkJyksIGRhdGEuZ2V0VXRmU3RyaW5nKCdyTmFtZScpKTtcblxuXHRcdFx0Ly8gSWYgdGhlIGN1cnJlbnQgdXNlciBpcyB0aGUgdXBkYXRlciwgc2hvdyBhIG5vdGlmaWNhdGlvbjsgb3RoZXJ3aXNlLCBzaG93IGEgZGlhbG9nIGJveCBzdWdnZXN0aW5nIHRvIHJlbG9hZFxuXHRcdFx0aWYgKHVzZXJuYW1lID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIEVuYWJsZSBpbnRlcmZhY2Vcblx0XHRcdFx0dGhpcy5fZW5hYmxlQ29uZmlnSW50ZXJmYWNlKHRydWUpO1xuXG5cdFx0XHRcdC8vIERpc3BsYXkgbm90aWZpY2F0aW9uXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1Jvb20gbW9kaWZpZWQnLCBgUm9vbSBzZXR0aW5ncyB1cGRhdGVkIHN1Y2Nlc3NmdWxseTsgY2hhbmdlcyB3aWxsIGJlIGFwcGxpZWQgb24gbmV4dCA8c3Ryb25nPnNlcnZlciByZXN0YXJ0PC9zdHJvbmc+YCk7XG5cblx0XHRcdFx0Ly8gUmVzZXQgdGhlICdtb2RpZmllZCcgZmxhZ1xuXHRcdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLnJlc2V0SXNNb2RpZmllZCgpO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBBbiBhbGVydCBib3ggaXMgZGlzcGxheWVkIGlmIHRoZSB1c2VyIGlzIGN1cnJlbnRseSBlZGl0aW5nIHRoZSBzYW1lIHJvb21cblx0XHRcdFx0aWYgKGRhdGEuZ2V0SW50KCdySWQnKSA9PSB0aGlzLl9lZGl0ZWRSb29tSWQpXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBtb2RpZmllZCB0aGUgUm9vbSB5b3UgYXJlIGN1cnJlbnRseSBlZGl0aW5nOyBwbGVhc2UgcmVsb2FkIHRvIHVwZGF0ZSB5b3VyIHZpZXcuYCk7XG5cblx0XHRcdFx0XHQvLyBEaXNhYmxlIHN1Ym1pdCBidXR0b25cblx0XHRcdFx0XHQkKCcjem5jLXN1Ym1pdEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Ly8gRGlzcGxheSBub3RpZmljYXRpb25cblx0XHRcdFx0XHRpZiAoZGF0YS5nZXRVdGZTdHJpbmcoJ3JOYW1lJykgIT0gbnVsbClcblx0XHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1Jvb20gcmVuYW1lZCcsIGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBjaGFuZ2VkIHRoZSBuYW1lIG9uIG9uZSBvZiB0aGUgUm9vbXNgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIE5ldyByb29tIGFkZGVkXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfUk9PTV9BRERFRClcblx0XHR7XG5cdFx0XHRjb25zdCByb29tRGF0YSA9IGRhdGEuZ2V0U0ZTT2JqZWN0KCdyb29tJyk7XG5cdFx0XHRjb25zdCB6b25lSWQgPSBkYXRhLmdldEludCgneklkJyk7XG5cblx0XHRcdGxldCB6b25lc0RTID0gdGhpcy5fdHJlZXZpZXcuZGF0YVNvdXJjZTtcblx0XHRcdGxldCB6b25lSXRlbSA9IHpvbmVzRFMuZ2V0KHpvbmVJZCk7XG5cblx0XHRcdC8vIElmIHRoZSBjdXJyZW50IHVzZXIgaXMgdGhlIHVwZGF0ZXIsIHJlc2V0IHRoZSBpbnRlcmZhY2U7IG90aGVyd2lzZSwganVzdCBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHRpZiAodXNlcm5hbWUgPT0gdGhpcy5zbWFydEZveC5teVNlbGYubmFtZSlcblx0XHRcdHtcblx0XHRcdFx0Ly8gUmVzZXQgaW50ZXJmYWNlXG5cdFx0XHRcdHRoaXMuX29uQ2FuY2VsQ2xpY2soKTtcblxuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdSb29tIGFkZGVkJywgYFJvb20gJyR7cm9vbURhdGEuZ2V0VXRmU3RyaW5nKCduYW1lJyl9JyBjcmVhdGVkIHN1Y2Nlc3NmdWxseWApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdSb29tIGFkZGVkJywgYEFkbWluaXN0cmF0b3IgJHt1c2VybmFtZX0gY3JlYXRlZCBSb29tICcke3Jvb21EYXRhLmdldFV0ZlN0cmluZygnbmFtZScpfScgaW4gWm9uZSAnJHt6b25lSXRlbS5uYW1lfSdgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQWRkIG5ldyByb29tIHRvIHRyZWVcblx0XHRcdHpvbmVJdGVtLmFwcGVuZCh0aGlzLl9jcmVhdGVSb29tT2JqZWN0KHJvb21EYXRhLCB6b25lSWQpKTtcblx0XHRcdHpvbmVzRFMuc3luYygpO1xuXG5cdFx0XHQvLyBFeHBhbmQgem9uZSBub2RlIHdoZXJlIHJvb20gd2FzIGFkZGVkXG5cdFx0XHR0aGlzLl90cmVldmlldy5leHBhbmQodGhpcy5fdHJlZXZpZXcuc2VsZWN0KCkpO1xuXHRcdH1cblxuXHRcdC8vIE5ldyByb29tIGNyZWF0aW9uIHJlZnVzZWQgZHVlIHRvIGludmFsaWQgcm9vbSBuYW1lXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfUk9PTV9SRUZVU0VEKVxuXHRcdHtcblx0XHRcdC8vIFJlLWVuYWJsZSBpbnRlcmZhY2Vcblx0XHRcdHRoaXMuX2VuYWJsZUNvbmZpZ0ludGVyZmFjZSh0cnVlKTtcblxuXHRcdFx0Ly8gU2hvdyB3YXJuaW5nXG5cdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoJ1Jvb20gY29uZmlndXJhdGlvbiBjYW5cXCd0IGJlIHNhdmVkIGJlY2F1c2UgYW5vdGhlciBSb29tIHdpdGggdGhlIHNhbWUgbmFtZSBhbHJlYWR5IGV4aXN0cy4nLCB0cnVlKTtcblx0XHR9XG5cblx0XHQvLyBFeGlzdGluZyByb29tIGRlbGV0ZWRcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ST09NX0RFTEVURUQpXG5cdFx0e1xuXHRcdFx0bGV0IHpvbmVJdGVtID0gdGhpcy5fZ2V0Wm9uZURhdGFJdGVtQnlJZChkYXRhLmdldEludCgneklkJykpO1xuXHRcdFx0bGV0IHJvb21JdGVtID0gdGhpcy5fZ2V0Um9vbURhdGFJdGVtQnlJZChkYXRhLmdldEludCgneklkJyksIGRhdGEuZ2V0SW50KCdySWQnKSk7XG5cblx0XHRcdC8vIElmIHRoZSBjdXJyZW50IHVzZXIgaXMgdGhlIGRlbGV0ZXIsIHJlc2V0IHRoZSBpbnRlcmZhY2U7IG90aGVyd2lzZSwganVzdCBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHRpZiAodXNlcm5hbWUgPT0gdGhpcy5zbWFydEZveC5teVNlbGYubmFtZSlcblx0XHRcdHtcblx0XHRcdFx0Ly8gUmUtZW5hYmxlIGludGVyZmFjZVxuXHRcdFx0XHR0aGlzLl9lbmFibGVMaXN0SW50ZXJmYWNlKHRydWUpO1xuXG5cdFx0XHRcdC8vIERpc3BsYXkgbm90aWZpY2F0aW9uXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1Jvb20gcmVtb3ZlZCcsIGBSb29tICcke3Jvb21JdGVtLm5hbWV9JyBkZWxldGVkIHN1Y2Nlc3NmdWxseWApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBBbiBhbGVydCBib3ggaXMgZGlzcGxheWVkIGlmIHRoZSB1c2VyIGlzIGN1cnJlbnRseSBlZGl0aW5nIHRoZSBzYW1lIHJvb21cblx0XHRcdFx0aWYgKGRhdGEuZ2V0SW50KCdySWQnKSA9PSB0aGlzLl9lZGl0ZWRSb29tSWQpXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBkZWxldGVkIHRoZSBSb29tIHlvdSBhcmUgY3VycmVudGx5IG1vZGlmeWluZzsgeW91IGhhdmUgdG8gY2FuY2VsIHlvdXIgZWRpdGluZy5gKTtcblxuXHRcdFx0XHRcdC8vIERpc2FibGUgc3VibWl0IGFuZCByZWxvYWQgYnV0dG9uc1xuXHRcdFx0XHRcdCQoJyN6bmMtcmVsb2FkQnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCB0cnVlKTtcblx0XHRcdFx0XHQkKCcjem5jLXN1Ym1pdEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Ly8gRGlzcGxheSBub3RpZmljYXRpb25cblx0XHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdSb29tIHJlbW92ZWQnLCBgQWRtaW5pc3RyYXRvciAke3VzZXJuYW1lfSBkZWxldGVkIFJvb20gJyR7cm9vbUl0ZW0ubmFtZX0nIGZyb20gWm9uZSAnJHt6b25lSXRlbS5uYW1lfSdgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBSZXNldCBzZWxlY3Rpb24gaWYgdGhlIGN1cnJlbnRseSBzZWxlY3RlZCBpdGVtIG9yIGl0cyBwYXJlbnQgaXMgYmVpbmcgcmVtb3ZlZFxuXHRcdFx0bGV0IHNlbGVjdGVkTm9kZSA9IHRoaXMuX3RyZWV2aWV3LnNlbGVjdCgpO1xuXHRcdFx0bGV0IHNlbGVjdGVkRGF0YUl0ZW0gPSB0aGlzLl90cmVldmlldy5kYXRhSXRlbShzZWxlY3RlZE5vZGUpO1xuXHRcdFx0aWYgKHNlbGVjdGVkRGF0YUl0ZW0pXG5cdFx0XHR7XG5cdFx0XHRcdGlmIChzZWxlY3RlZERhdGFJdGVtLnR5cGUgPT0gdGhpcy5JVEVNX1RZUEVfUk9PTSAmJiBzZWxlY3RlZERhdGFJdGVtLmlkID09IGRhdGEuZ2V0SW50KCdySWQnKSlcblx0XHRcdFx0XHR0aGlzLl9kZXNlbGVjdFRyZWVJdGVtKCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFJlbW92ZSByb29tIGZyb20gdHJlZVxuXHRcdFx0em9uZUl0ZW0uY2hpbGRyZW4ucmVtb3ZlKHJvb21JdGVtKTtcblx0XHRcdHRoaXMuX3RyZWV2aWV3LmRhdGFTb3VyY2Uuc3luYygpO1xuXHRcdH1cblxuXHRcdC8qKioqKiogV09SRFMgRklMRVMgKioqKioqL1xuXG5cdFx0Ly8gV29yZHMgZmlsZXMgbGlzdCByZWNlaXZlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1JFRlJFU0hfV09SRFNfRklMRVMpXG5cdFx0e1xuXHRcdFx0dGhpcy5fd29yZHNGaWxlc01hbmFnZXIucmVmcmVzaFdvcmRzRmlsZXNMaXN0KGRhdGEuZ2V0U0ZTQXJyYXkoJ3dmJyksIHVzZXJuYW1lID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpO1xuXG5cdFx0XHQvLyBJZiBhbm90aGVyIHVzZXIgY2F1c2VkIGEgcmVmcmVzaCAoZm9yIGV4YW1wbGUgZGVsZXRpbmcgYSBmaWxlLCBvciBhZGRpbmcgYSBuZXcgb25lKSBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHRpZiAodXNlcm5hbWUgIT0gbnVsbCAmJiB1c2VybmFtZSAhPSB0aGlzLnNtYXJ0Rm94Lm15U2VsZi5uYW1lICYmIHRoaXMuX2VkaXRlZFpvbmVJZCA+IC0xKVxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdXb3JkcyBmaWxlcyBtb2RpZmllZCcsIGBBZG1pbmlzdHJhdG9yICR7dXNlcm5hbWV9IGhhcyBhZGRlZCwgbW9kaWZpZWQgb3IgZGVsZXRlZCBhIHdvcmRzIGZpbGUuYCk7XG5cdFx0fVxuXG5cdFx0Ly8gV29yZHMgZmlsZSBjb250ZW50IHJlY2VpdmVkXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfV09SRFNfRklMRV9DT05URU5UKVxuXHRcdHtcblx0XHRcdHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLmVkaXRXb3Jkc0ZpbGUoZGF0YS5nZXRVdGZTdHJpbmcoJ2ZpbGVuYW1lJyksIGRhdGEuZ2V0VGV4dCgnY29udGVudCcpKTtcblx0XHR9XG5cblx0XHQvLyBXb3JkcyBmaWxlIGVycm9yIChlZGl0L3NhdmUpXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfV09SRFNfRklMRV9FUlJPUilcblx0XHR7XG5cdFx0XHQvLyBFbmFibGUgYnV0dG9uc1xuXHRcdFx0dGhpcy5fd29yZHNGaWxlc01hbmFnZXIuZW5hYmxlZCA9IHRydWU7XG5cblx0XHRcdC8vIFNob3cgYWxlcnRcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChkYXRhLmdldFV0ZlN0cmluZygnZXJyb3InKSwgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0Ly8gZWxzZSBpZiAoKVxuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gVUkgRVZFTlQgTElTVEVORVJTXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0X29uVHJlZUl0ZW1Eb3VibGVDbGljayhlKVxuXHR7XG5cdFx0Ly8gR2V0IGV2ZW50IHRhcmdldCdzIGNsb3Nlc3QgdHJlZSBub2RlXG5cdFx0bGV0IHRyZWVOb2RlID0gJChlLnRhcmdldCkuY2xvc2VzdCgnLmstaXRlbVtyb2xlPXRyZWVpdGVtXScpO1xuXG5cdFx0Ly8gR2V0IGFzc29jaWF0ZWQgZGF0YSBpdGVtXG5cdFx0bGV0IGRhdGFJdGVtID0gdGhpcy5fdHJlZXZpZXcuZGF0YUl0ZW0odHJlZU5vZGUpO1xuXG5cdFx0Ly8gTG9hZCBjb25maWd1cmF0aW9uXG5cdFx0dGhpcy5fbG9hZENvbmZpZ3VyYXRpb24oZGF0YUl0ZW0udHlwZSk7XG5cdH1cblxuXHRfb25ab25lUm9vbUNoYW5nZSgpXG5cdHtcblx0XHQvLyBSZXNldCB1dGlsaXR5IGJ1dHRvbnNcblx0XHR0aGlzLl9zZXRVdGlsaXR5QnV0dG9uc1N0YXRlKHRoaXMuX3NlbGVjdGVkSXRlbSk7XG5cdH1cblxuXHQvLyBVdGlsaXR5IGJ1dHRvbnMgbGlzdGVuZXJzXG5cblx0X29uQWRkWm9uZUNsaWNrKClcblx0e1xuXHRcdC8vIERlc2VsZWN0IGxpc3QgaXRlbVxuXHRcdHRoaXMuX2Rlc2VsZWN0VHJlZUl0ZW0oKTtcblxuXHRcdC8vIExvYWQgY29uZmlndXJhdGlvblxuXHRcdHRoaXMuX2xvYWRDb25maWd1cmF0aW9uKHRoaXMuSVRFTV9UWVBFX1pPTkUpO1xuXHR9XG5cblx0X29uQWRkUm9vbUNsaWNrKClcblx0e1xuXHRcdC8vIFNlbGVjdCBwYXJlbnQgbGlzdCBpdGVtXG5cdFx0dGhpcy5fc2VsZWN0UGFyZW50VHJlZUl0ZW0oKTtcblxuXHRcdC8vIExvYWQgY29uZmlndXJhdGlvblxuXHRcdHRoaXMuX2xvYWRDb25maWd1cmF0aW9uKHRoaXMuSVRFTV9UWVBFX1JPT00pO1xuXHR9XG5cblx0X29uRWRpdENsaWNrKClcblx0e1xuXHRcdC8vIExvYWQgY29uZmlndXJhdGlvblxuXHRcdHRoaXMuX2xvYWRDb25maWd1cmF0aW9uKHRoaXMuX3NlbGVjdGVkSXRlbS50eXBlKTtcblx0fVxuXG5cdF9vblJlbW92ZUNsaWNrKClcblx0e1xuXHRcdHRoaXMuc2hlbGxDdHJsLnNob3dDb25maXJtV2FybmluZyhgQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSB0aGUgc2VsZWN0ZWQgJHt0aGlzLl9zZWxlY3RlZEl0ZW0udHlwZSA9PSB0aGlzLklURU1fVFlQRV9aT05FID8gJ1pvbmUnIDogJ1Jvb20nfSBjb25maWd1cmF0aW9uP2AsICQucHJveHkodGhpcy5fb25SZW1vdmVDb25maXJtLCB0aGlzKSk7XG5cdH1cblxuXHRfb25SZW1vdmVDb25maXJtKClcblx0e1xuXHRcdC8vIERpc2FibGUgem9uZS9yb29tIHNlbGVjdGlvbiBsaXN0XG5cdFx0dGhpcy5fZW5hYmxlTGlzdEludGVyZmFjZShmYWxzZSk7XG5cblx0XHRsZXQgcGFyYW1zID0gbmV3IFNGUzJYLlNGU09iamVjdCgpO1xuXG5cdFx0Ly8gUmVxdWVzdCB6b25lIHJlbW92YWxcblx0XHRpZiAodGhpcy5fc2VsZWN0ZWRJdGVtLnR5cGUgPT0gdGhpcy5JVEVNX1RZUEVfWk9ORSlcblx0XHR7XG5cdFx0XHRwYXJhbXMucHV0SW50KCd6SWQnLCB0aGlzLl9zZWxlY3RlZEl0ZW0uaWQpO1xuXHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9ERUxFVEVfWk9ORV9DT05GSUcsIHBhcmFtcyk7XG5cdFx0fVxuXHRcdGVsc2Vcblx0XHR7XG5cdFx0XHRwYXJhbXMucHV0SW50KCd6SWQnLCB0aGlzLl9zZWxlY3RlZEl0ZW1QYXJlbnQuaWQpO1xuXHRcdFx0cGFyYW1zLnB1dEludCgncklkJywgdGhpcy5fc2VsZWN0ZWRJdGVtLmlkKTtcblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfREVMRVRFX1JPT01fQ09ORklHLCBwYXJhbXMpO1xuXHRcdH1cblx0fVxuXG5cdF9vbkFjdGl2YXRlQ2xpY2soKVxuXHR7XG5cdFx0Ly8gR2V0IHNlbGVjdGVkIGRhdGEgaXRlbVxuXHRcdGlmICh0aGlzLl9zZWxlY3RlZEl0ZW0udHlwZSA9PSB0aGlzLklURU1fVFlQRV9aT05FKVxuXHRcdHtcblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0SW50KCd6SWQnLCB0aGlzLl9zZWxlY3RlZEl0ZW0uaWQpO1xuXG5cdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0FDVElWQVRFX1pPTkUsIHBhcmFtcyk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gQ29uZmlndXJhdGlvbiBidXR0b25zIGxpc3RlbmVyc1xuXG5cdF9vbkNhbmNlbENsaWNrKClcblx0e1xuXHRcdC8vIEVuYWJsZSB6b25lL3Jvb20gc2VsZWN0aW9uIGxpc3RzXG5cdFx0dGhpcy5fZW5hYmxlTGlzdEludGVyZmFjZSh0cnVlKTtcblxuXHRcdC8vIERpc2FibGUgY29uZmlndXJhdGlvbiBpbnRlcmZhY2Vcblx0XHR0aGlzLl9lbmFibGVDb25maWdJbnRlcmZhY2UoZmFsc2UpO1xuXG5cdFx0Ly8gQ2xlYXIgbWFpbiBjb250YWluZXJcblx0XHR0aGlzLl9yZXNldFRhYnNDb250YWluZXIoZmFsc2UsIHRydWUpO1xuXG5cdFx0Ly8gU2V0IGlzRWRpdGluZyBmbGFnXG5cdFx0dGhpcy5faXNFZGl0aW5nID0gZmFsc2U7XG5cdFx0dGhpcy5fZWRpdGVkSXRlbVR5cGUgPSAnJztcblxuXHRcdC8vIFN3aXRjaCBwYW5lbFxuXHRcdHRoaXMuX3N3aXRjaFBhbmVsKCd6bmMtc2lkZWJhclBhbmVsJyk7XG5cdH1cblxuXHRfb25SZWxvYWRDbGljaygpXG5cdHtcblx0XHQvLyBIaWRlIHZhbGlkYXRpb24gbWVzc2FnZXNcblx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLnJlc2V0VmFsaWRhdGlvbigpO1xuXG5cdFx0Ly8gUmVsb2FkIGNvbmZpZ3VyYXRpb25cblx0XHR0aGlzLl9sb2FkQ29uZmlndXJhdGlvbih0aGlzLl9lZGl0ZWRJdGVtVHlwZSwgZmFsc2UpO1xuXHR9XG5cblx0X29uU3VibWl0Q2xpY2soKVxuXHR7XG5cdFx0Ly8gQ2hlY2sgdmFsaWRpdHlcblx0XHRpZiAodGhpcy5faW50ZXJmYWNlQnVpbGRlci5jaGVja0lzVmFsaWQoKSlcblx0XHR7XG5cdFx0XHRsZXQgY2hhbmdlcyA9IHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuZ2V0Q2hhbmdlZERhdGEoKTtcblxuXHRcdFx0aWYgKGNoYW5nZXMuc2l6ZSgpID4gMClcblx0XHRcdHtcblx0XHRcdFx0Ly9jb25zb2xlLmxvZyhjaGFuZ2VzLmdldER1bXAoKSlcblxuXHRcdFx0XHQvLyBJbiBjYXNlIHRoZSB6b25lL3Jvb20gbmFtZSBjaGFuZ2VkLCBjaGVjayBpdCBhZ2FpbnN0IHRoZSBsaXN0IChkdXBsaWNhdGUgbmFtZXMgbm90IGFsbG93ZWQhKVxuXHRcdFx0XHRpZiAodGhpcy5fdmFsaWRhdGVOYW1lKGNoYW5nZXMpKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Ly8gRGlzYWJsZSBjb25maWd1cmF0aW9uIGludGVyZmFjZVxuXHRcdFx0XHRcdHRoaXMuX2VuYWJsZUNvbmZpZ0ludGVyZmFjZShmYWxzZSk7XG5cblx0XHRcdFx0XHQvLyBTZW5kIHNldHRpbmdzIHRvIHNlcnZlciBpbnN0YW5jZVxuXHRcdFx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRcdFx0cGFyYW1zLnB1dFNGU0FycmF5KCdzZXR0aW5ncycsIGNoYW5nZXMpO1xuXHRcdFx0XHRcdHBhcmFtcy5wdXRCb29sKCdiYWNrdXAnLCAkKCcjem5jLWJhY2t1cENoZWNrJykucHJvcCgnY2hlY2tlZCcpKTtcblx0XHRcdFx0XHRwYXJhbXMucHV0SW50KCd6SWQnLCB0aGlzLl9lZGl0ZWRab25lSWQpO1xuXHRcdFx0XHRcdHBhcmFtcy5wdXRJbnQoJ3JJZCcsIHRoaXMuX2VkaXRlZFJvb21JZCk7XG5cblx0XHRcdFx0XHRpZiAodGhpcy5fZWRpdGVkSXRlbVR5cGUgPT0gdGhpcy5JVEVNX1RZUEVfWk9ORSlcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHQvLyBTdWJtaXQgem9uZSBzZXR0aW5nc1xuXHRcdFx0XHRcdFx0aWYgKHRoaXMuX2VkaXRlZFpvbmVJZCA+IC0xKVxuXHRcdFx0XHRcdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX1NBVkVfWk9ORV9DT05GSUcsIHBhcmFtcyk7XG5cdFx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfTkVXX1pPTkVfQ09ORklHLCBwYXJhbXMpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0Ly8gU3VibWl0IHJvb20gc2V0dGluZ3Ncblx0XHRcdFx0XHRcdGlmICh0aGlzLl9lZGl0ZWRSb29tSWQgPiAtMSlcblx0XHRcdFx0XHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9TQVZFX1JPT01fQ09ORklHLCBwYXJhbXMpO1xuXHRcdFx0XHRcdFx0ZWxzZVxuXHRcdFx0XHRcdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX05FV19ST09NX0NPTkZJRywgcGFyYW1zKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Ly8gU2hvdyBhbGVydFxuXHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChgVW5hYmxlIHRvIHN1Ym1pdCBjb25maWd1cmF0aW9uIGJlY2F1c2UgdGhlICR7Y2FwaXRhbGl6ZUZpcnN0KHRoaXMuX2VkaXRlZEl0ZW1UeXBlKX0gbmFtZSBhbHJlYWR5IGV4aXN0czsgZHVwbGljYXRlIG5hbWVzIGFyZSBub3QgYWxsb3dlZC5gLCB0cnVlKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRlbHNlXG5cdFx0e1xuXHRcdFx0Ly8gU2hvdyBhbGVydFxuXHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KCdVbmFibGUgdG8gc3VibWl0IGNvbmZpZ3VyYXRpb24gY2hhbmdlcyBkdWUgdG8gYW4gaW52YWxpZCB2YWx1ZTsgcGxlYXNlIHZlcmlmeSB0aGUgaGlnaGxpZ2h0ZWQgZm9ybSBmaWVsZHMgaW4gYWxsIHRhYnMuJywgdHJ1ZSk7XG5cdFx0fVxuXHR9XG5cblx0X29uQ29uZmlnVmFsdWVTZXQoZSkgLy8gU0FNRSBNRVRIT0QgRFVQTElDQVRFRCBJTiB6b25lLW1vbml0b3IuanNcblx0e1xuXHRcdGNvbnN0IGNvbmZpZ1BhcmFtID0gZS50YXJnZXQuZGF0YTtcblxuXHRcdC8vIEhhbmRsZSBleHRlbnNpb24gbmFtZS90eXBlIGRyb3Bkb3ducyB1cGRhdGUgYW5kIHVwZGF0ZSB0aGUgbWFpbiBjbGFzcyBkcm9wZG93biBkYXRhc291cmNlIGFjY29yZGluZ2x5XG5cdFx0aWYgKGNvbmZpZ1BhcmFtLm5hbWUgPT0gJ2V4dGVuc2lvbi5uYW1lJyB8fCBjb25maWdQYXJhbS5uYW1lID09ICdleHRlbnNpb24udHlwZScgfHwgY29uZmlnUGFyYW0ubmFtZSA9PSAnZXh0ZW5zaW9uLmZpbHRlckNsYXNzJylcblx0XHR7XG5cdFx0XHQvLyBBbGwgaW52b2x2ZWQgQ29uZmlnRm9ybUl0ZW1zIG11c3QgYmUgYXZhaWxhYmxlIGFuZCBpbml0aWFsaXplZCB0byBwcm9jZWVkXG5cdFx0XHRjb25zdCBuYW1lRm9ybUl0ZW0gPSB0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmdldENvbmZpZ0Zvcm1JdGVtKCdleHRlbnNpb24ubmFtZScpO1xuXHRcdFx0Y29uc3QgdHlwZUZvcm1JdGVtID0gdGhpcy5faW50ZXJmYWNlQnVpbGRlci5nZXRDb25maWdGb3JtSXRlbSgnZXh0ZW5zaW9uLnR5cGUnKTtcblx0XHRcdGNvbnN0IGNsYXNzRm9ybUl0ZW0gPSB0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmdldENvbmZpZ0Zvcm1JdGVtKCdleHRlbnNpb24uZmlsZScpO1xuXHRcdFx0Y29uc3QgZmlsdGVyRm9ybUl0ZW0gPSB0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmdldENvbmZpZ0Zvcm1JdGVtKCdleHRlbnNpb24uZmlsdGVyQ2xhc3MnKTtcblxuXHRcdFx0aWYgKG5hbWVGb3JtSXRlbSAhPSBudWxsICYmIHR5cGVGb3JtSXRlbSAhPSBudWxsICYmIGNsYXNzRm9ybUl0ZW0gIT0gbnVsbCAmJiBmaWx0ZXJGb3JtSXRlbSAhPSBudWxsKVxuXHRcdFx0e1xuXHRcdFx0XHRjb25zdCBzb3VyY2UgPSBuYW1lRm9ybUl0ZW0uZGF0YTtcblx0XHRcdFx0bGV0IGNsYXNzZXNMaXN0ID0gW107XG5cblx0XHRcdFx0bGV0IGRhdGEgPSBzb3VyY2UudHJpZ2dlckRhdGE7XG5cdFx0XHRcdGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5zaXplKCk7IGkrKylcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxldCBleHQgPSBkYXRhLmdldFNGU09iamVjdChpKTtcblxuXHRcdFx0XHRcdGlmIChleHQuZ2V0VXRmU3RyaW5nKCduYW1lJykgPT0gbmFtZUZvcm1JdGVtLmRhdGEudmFsdWUgJiYgZXh0LmdldFV0ZlN0cmluZygndHlwZScpID09IHR5cGVGb3JtSXRlbS5kYXRhLnZhbHVlKVxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGxldCBjbGFzc2VzID0gZXh0LmdldFV0ZlN0cmluZygnY2xhc3Nlc1N0cmluZycpLnNwbGl0KCcsJyk7XG5cblx0XHRcdFx0XHRcdGlmIChmaWx0ZXJGb3JtSXRlbS5kYXRhLnZhbHVlID09IHRydWUpXG5cdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdGxldCBmaWx0ZXJlZENsYXNzZXMgPSBjbGFzc2VzLmZpbHRlcihmaWx0ZXJDbGFzc05hbWUpO1xuXHRcdFx0XHRcdFx0XHRjbGFzc2VzID0gZmlsdGVyZWRDbGFzc2VzO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRjbGFzc2VzTGlzdCA9IGNsYXNzZXNMaXN0LmNvbmNhdChjbGFzc2VzKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRsZXQgY3VycmVudENsYXNzID0gY2xhc3NGb3JtSXRlbS5kYXRhLnZhbHVlO1xuXG5cdFx0XHRcdC8vIElmIHRoZSBjbGFzc2VzIGxpc3QgZG9lc24ndCBjb250YWluIHRoZSBjdXJyZW50IHZhbHVlLCBjcmVhdGUgYW4gZW1wdHkgZW50cnkgYW5kIHJlc2V0IHRoZSB2YWx1ZVxuXHRcdFx0XHRpZiAoY2xhc3Nlc0xpc3QuaW5kZXhPZihjdXJyZW50Q2xhc3MpIDwgMClcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGlmIChjbGFzc2VzTGlzdC5sZW5ndGggPT0gMClcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRjbGFzc2VzTGlzdC5wdXNoKCcnKTtcblx0XHRcdFx0XHRcdGN1cnJlbnRDbGFzcyA9ICcnO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0XHRjdXJyZW50Q2xhc3MgPSBjbGFzc2VzTGlzdFswXTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGxldCBtYWluQ2xhc3NEcm9wRG93biA9IGNsYXNzRm9ybUl0ZW0uX2lubmVyV2lkZ2V0O1xuXHRcdFx0XHRtYWluQ2xhc3NEcm9wRG93bi5zZXREYXRhU291cmNlKGNsYXNzZXNMaXN0KTtcblxuXHRcdFx0XHRjbGFzc0Zvcm1JdGVtLmRhdGEudmFsdWUgPSBjdXJyZW50Q2xhc3M7XG5cdFx0XHRcdGNsYXNzRm9ybUl0ZW0uX3NldFdpZGdldFZhbHVlKCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0X29uV29yZHNGaWxlUmVsb2FkQ2xpY2soZXZ0KVxuXHR7XG5cdFx0Ly8gU2VuZCByZXF1ZXN0IHRvIHNlcnZlclxuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfUkVGUkVTSF9XT1JEU19GSUxFKTtcblx0fVxuXG5cdF9vbldvcmRzRmlsZUVkaXRDbGljayhldnQpXG5cdHtcblx0XHQvLyBTZW5kIHJlcXVlc3QgdG8gc2VydmVyXG5cdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCdmaWxlbmFtZScsIGV2dC5kZXRhaWwpO1xuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfRURJVF9XT1JEU19GSUxFLCBwYXJhbXMpO1xuXHR9XG5cblx0X29uV29yZHNGaWxlU2F2ZUNsaWNrKGV2dClcblx0e1xuXHRcdHRoaXMuX3RlbXBXb3Jkc0ZpbGVEYXRhID0gZXZ0LmRldGFpbDtcblxuXHRcdC8vIENoZWNrIGlmIGEgbmV3IGZpbGUgaXMgYmVpbmcgY3JlYXRlZFxuXHRcdGlmICh0aGlzLl90ZW1wV29yZHNGaWxlRGF0YS5pc05ldylcblx0XHR7XG5cdFx0XHQvLyBJZiB5ZXMsIGNoZWNrIGlmIG5hbWUgYWxyZWFkeSBleGlzdHNcblx0XHRcdGlmICh0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5nZXRFeGlzdGluZ0ZpbGVuYW1lcygpLmluY2x1ZGVzKHRoaXMuX3RlbXBXb3Jkc0ZpbGVEYXRhLmZpbGVuYW1lKSlcblx0XHRcdHtcblx0XHRcdFx0Ly8gU2hvdyBjb25maXJtIGRpYWxvZ1xuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Q29uZmlybVdhcm5pbmcoJ0Egd29yZHMgZmlsZSB3aXRoIHRoZSBlbnRlcmVkIG5hbWUgYWxyZWFkeSBleGlzdHM7IGRvIHlvdSB3YW50IHRvIHByb2NlZWQgYW55d2F5PyBUaGUgZXhpc3RpbmcgZmlsZSB3aWxsIGJlIG92ZXJ3cml0dGVuLicsICQucHJveHkodGhpcy5fb25Xb3Jkc0ZpbGVTYXZlQ29uZmlybSwgdGhpcykpO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUHJvY2VlZFxuXHRcdHRoaXMuX29uV29yZHNGaWxlU2F2ZUNvbmZpcm0oKTtcblx0fVxuXG5cdF9vbldvcmRzRmlsZVNhdmVDb25maXJtKClcblx0e1xuXHRcdC8vIERpc2FibGUgd29yZHMgZmlsZXMgbWFuYWdlciBidXR0b25zXG5cdFx0dGhpcy5fd29yZHNGaWxlc01hbmFnZXIuZW5hYmxlZCA9IGZhbHNlO1xuXHRcdHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLnNhdmVTcGlubmVyVmlzaWJsZSA9IHRydWU7XG5cblx0XHQvLyBTZW5kIHJlcXVlc3QgdG8gc2VydmVyXG5cdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCdmaWxlbmFtZScsIHRoaXMuX3RlbXBXb3Jkc0ZpbGVEYXRhLmZpbGVuYW1lKTtcblx0XHRwYXJhbXMucHV0VGV4dCgnY29udGVudCcsIHRoaXMuX3RlbXBXb3Jkc0ZpbGVEYXRhLmNvbnRlbnQpO1xuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfU0FWRV9XT1JEU19GSUxFLCBwYXJhbXMpO1xuXHR9XG5cblx0X29uV29yZHNGaWxlUmVtb3ZlQ2xpY2soZXZ0KVxuXHR7XG5cdFx0dGhpcy5zaGVsbEN0cmwuc2hvd0NvbmZpcm1XYXJuaW5nKCdBcmUgeW91IHN1cmUgeW91IHdhbnQgdG8gZGVsZXRlIHRoZSBzZWxlY3RlZCB3b3JkcyBmaWxlPycsICQucHJveHkodGhpcy5fb25Xb3Jkc0ZpbGVSZW1vdmVDb25maXJtLCB0aGlzKSk7XG5cdH1cblxuXHRfb25Xb3Jkc0ZpbGVSZW1vdmVDb25maXJtKClcblx0e1xuXHRcdGxldCB3b3Jkc0ZpbGUgPSB0aGlzLl93b3Jkc0ZpbGVzTWFuYWdlci5nZXRTZWxlY3RlZFdvcmRzRmlsZU5hbWUoKTtcblxuXHRcdGlmICh3b3Jkc0ZpbGUgIT0gbnVsbClcblx0XHR7XG5cdFx0XHQvLyBEaXNhYmxlIHdvcmRzIGZpbGVzIG1hbmFnZXIgYnV0dG9uc1xuIFx0XHRcdHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLmVuYWJsZWQgPSBmYWxzZTtcblx0XHRcdHRoaXMuX3dvcmRzRmlsZXNNYW5hZ2VyLmFjdGlvblNwaW5uZXJWaXNpYmxlID0gdHJ1ZTtcblxuXHRcdFx0Ly8gU2VuZCByZXF1ZXN0IHRvIHNlcnZlclxuXHRcdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ2ZpbGVuYW1lJywgd29yZHNGaWxlKTtcblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfREVMRVRFX1dPUkRTX0ZJTEUsIHBhcmFtcyk7XG5cdFx0fVxuXHR9XG5cblx0X29uV29yZHNGaWxlQXNzaWduQ2xpY2soZXZ0KVxuXHR7XG5cdFx0bGV0IHBhdGggPSBldnQuZGV0YWlsO1xuXG5cdFx0Ly8gV3JpdGUgcGF0aCBvZiB0aGUgc2VsZWN0ZWQgd29yZHMgZmlsZSBpbiBcIndvcmRzRmlsdGVyLndvcmRzRmlsZVwiIGR5bmFtaWNhbGx5IGNyZWF0ZWQgZmllbGRcblx0XHRjb25zdCB3b3Jkc0ZpbGVGb3JtSXRlbSA9IHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuZ2V0Q29uZmlnRm9ybUl0ZW0oJ3dvcmRzRmlsdGVyLndvcmRzRmlsZScpO1xuXHRcdHdvcmRzRmlsZUZvcm1JdGVtLmRhdGEudmFsdWUgPSBwYXRoO1xuXHRcdHdvcmRzRmlsZUZvcm1JdGVtLl9zZXRXaWRnZXRWYWx1ZSgpO1xuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gUFJJVkFURSBNRVRIT0RTXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0X2VuYWJsZUxpc3RJbnRlcmZhY2UoZW5hYmxlZClcblx0e1xuXHRcdCQoJyN6bmMtdXRpbEJ1dHRvbnMnKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGVkKTtcblx0XHQkKCcjem5jLXRyZWVWaWV3JykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlZCk7XG5cdH1cblxuXHRfc2V0VXRpbGl0eUJ1dHRvbnNTdGF0ZShkYXRhSXRlbSA9IG51bGwpXG5cdHtcblx0XHRsZXQgZGlzYWJsZSA9IHRydWU7XG5cblx0XHRpZiAoZGF0YUl0ZW0pXG5cdFx0e1xuXHRcdFx0Ly8gRW5hYmxlICdhY3RpdmF0ZSB6b25lJyBidXR0b24gaWYgem9uZSBpcyBpbmFjdGl2ZVxuXHRcdFx0JCgnI3puYy1hY3RpdmF0ZUJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgKGRhdGFJdGVtLnR5cGUgIT0gdGhpcy5JVEVNX1RZUEVfWk9ORSB8fCBkYXRhSXRlbS5hY3RpdmUpKTtcblxuXHRcdFx0ZGlzYWJsZSA9IGZhbHNlO1xuXHRcdH1cblx0XHRlbHNlXG5cdFx0e1xuXHRcdFx0Ly8gRGlzYWJsZSAnYWN0aXZhdGUgem9uZScgYnV0dG9uXG5cdFx0XHQkKCcjem5jLWFjdGl2YXRlQnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCB0cnVlKTtcblx0XHR9XG5cblx0XHQvLyBFbmFibGUvZGlzYWJsZSBvdGhlciB1dGlsaXR5IGJ1dHRvbnNcblx0XHQkKCcjem5jLWFkZFpvbmVCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIGZhbHNlKTsgLy8gQWx3YXlzIGVuYWJsZWRcblx0XHQkKCcjem5jLWFkZFJvb21CdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIGRpc2FibGUpO1xuXHRcdCQoJyN6bmMtZWRpdEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgZGlzYWJsZSk7XG5cdFx0JCgnI3puYy1yZW1vdmVCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIGRpc2FibGUpO1xuXHR9XG5cblx0X2VuYWJsZUNvbmZpZ0ludGVyZmFjZShlbmFibGVkKVxuXHR7XG5cdFx0JCgnI3puYy1jYW5jZWxCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGVkKTtcblx0XHQkKCcjem5jLXJlbG9hZEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgIWVuYWJsZWQpO1xuXHRcdCQoJyN6bmMtc3VibWl0QnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlZCk7XG5cdFx0JCgnI3puYy1iYWNrdXBDaGVjaycpLmF0dHIoJ2Rpc2FibGVkJywgIWVuYWJsZWQpO1xuXG5cdFx0dGhpcy5faW50ZXJmYWNlQnVpbGRlci5kaXNhYmxlSW50ZXJmYWNlKCFlbmFibGVkKTtcblxuXHRcdC8vIEFsc28gc3dpdGNoIHZpZXcgd2hlbiBlbmFibGVkXG5cdFx0aWYgKGVuYWJsZWQpXG5cdFx0XHR0aGlzLl9zd2l0Y2hWaWV3KCd6bmMtbWFpbicpO1xuXHR9XG5cblx0X3N3aXRjaFZpZXcodmlld0lkKVxuXHR7XG5cdFx0ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3puYy12aWV3c3RhY2snKS5zZWxlY3RlZEVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWV3SWQpO1xuXHR9XG5cblx0X2NsZWFyVGFicygpXG5cdHtcblx0XHQvLyBEZXN0cm95IHNjcm9sbGluZyB0YWJzXG5cdFx0JCgnI3puYy10YWJOYXZpZ2F0b3IgI3RhYnMnKS5zY3JvbGxpbmdUYWJzKCdkZXN0cm95Jyk7XG5cblx0XHQvLyBSZW1vdmUgYWxsIHRhYiBuYXZpZ2F0b3IgY29udGVudFxuXHRcdHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuZGVzdHJveUludGVyZmFjZSgpO1xuXG5cdFx0Ly8gU2V0IGZsYWcgdG8gcmUtaW5pdGlhbGl6ZSB0YWJzIGlmIG5lZWRlZFxuXHRcdHRoaXMuX3JlaW5pdFRhYnMgPSB0cnVlO1xuXHR9XG5cblx0X3BvcHVsYXRlVHJlZShkYXRhKVxuXHR7XG5cdFx0bGV0IHpEYXRhID0gZGF0YS5nZXRTRlNBcnJheSgnem9uZXMnKTtcblxuXHRcdGxldCB6b25lc0FyciA9IFtdO1xuXHRcdGZvciAobGV0IHogPSAwOyB6IDwgekRhdGEuc2l6ZSgpOyB6KyspXG5cdFx0XHR6b25lc0Fyci5wdXNoKCB0aGlzLl9jcmVhdGVab25lT2JqZWN0KHpEYXRhLmdldFNGU09iamVjdCh6KSkgKTtcblxuXHRcdC8vIENyZWF0ZSBkYXRhc291cmNlXG5cdFx0bGV0IHpvbmVzID0gbmV3IGtlbmRvLmRhdGEuSGllcmFyY2hpY2FsRGF0YVNvdXJjZSh7XG4gICAgICAgICAgICBkYXRhOiB6b25lc0Fycixcblx0XHRcdHNvcnQ6IHtcblx0XHRcdFx0ZmllbGQ6ICduYW1lJyxcblx0XHRcdFx0ZGlyOiAnYXNjJ1xuXHRcdFx0fSxcbiAgICAgICAgICAgIHNjaGVtYToge1xuICAgICAgICAgICAgICAgIG1vZGVsOiB7XG5cdFx0XHRcdFx0aWQ6ICdpZCcsXG4gICAgICAgICAgICAgICAgICAgIGNoaWxkcmVuOiB7XG5cdFx0XHRcdFx0XHRzY2hlbWE6IHtcblx0XHRcdFx0XHRcdFx0ZGF0YTogJ3Jvb21zJyxcblx0XHRcdFx0XHRcdFx0c29ydDoge1xuXHRcdFx0XHRcdFx0XHRcdGZpZWxkOiAnbmFtZScsXG5cdFx0XHRcdFx0XHRcdFx0ZGlyOiAnYXNjJ1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cblx0XHQvLyBTZXQgdHJlZSB2aWV3IGRhdGFwcm92aWRlclxuXHRcdHRoaXMuX3RyZWV2aWV3LnNldERhdGFTb3VyY2Uoem9uZXMpO1xuXG5cdFx0Ly8gU2V0IHV0aWxpdHkgYnV0dG9ucyBzdGF0ZSAoYWRkLCByZW1vdmUsIGVkaXQsIGV0Yylcblx0XHR0aGlzLl9zZXRVdGlsaXR5QnV0dG9uc1N0YXRlKCk7XG5cdH1cblxuXHRfY3JlYXRlWm9uZU9iamVjdCh6b25lRGF0YSlcblx0e1xuXHRcdGxldCB6b25lID0ge1xuXHRcdFx0dHlwZTogdGhpcy5JVEVNX1RZUEVfWk9ORSxcblx0XHRcdG5hbWU6IHpvbmVEYXRhLmdldFV0ZlN0cmluZygnbmFtZScpLFxuXHRcdFx0aWQ6IHpvbmVEYXRhLmdldEludCgnaWQnKSxcblx0XHRcdGFjdGl2ZTogem9uZURhdGEuZ2V0Qm9vbCgnYWN0Jylcblx0XHR9XG5cblx0XHQvLyBDcmVhdGUgcm9vbXMgbGlzdCBkYXRhcHJvdmlkZXJcblx0XHRsZXQgckRhdGEgPSB6b25lRGF0YS5nZXRTRlNBcnJheSgncm9vbXMnKTtcblxuXHRcdGxldCByb29tc0FyciA9IFtdO1xuXHRcdGZvciAobGV0IHIgPSAwOyByIDwgckRhdGEuc2l6ZSgpOyByKyspXG5cdFx0XHRyb29tc0Fyci5wdXNoKCB0aGlzLl9jcmVhdGVSb29tT2JqZWN0KHJEYXRhLmdldFNGU09iamVjdChyKSwgem9uZURhdGEuZ2V0SW50KCdpZCcpKSApO1xuXG5cdFx0em9uZS5yb29tcyA9IHJvb21zQXJyO1xuXG5cdFx0cmV0dXJuIHpvbmU7XG5cdH1cblxuXHRfY3JlYXRlUm9vbU9iamVjdChyb29tRGF0YSwgem9uZUlkKVxuXHR7XG5cdFx0bGV0IHJvb20gPSB7XG5cdFx0XHR0eXBlOiB0aGlzLklURU1fVFlQRV9ST09NLFxuXHRcdFx0bmFtZTogcm9vbURhdGEuZ2V0VXRmU3RyaW5nKCduYW1lJyksXG5cdFx0XHRpZDogcm9vbURhdGEuZ2V0SW50KCdpZCcpLFxuXHRcdFx0YWN0aXZlOiByb29tRGF0YS5nZXRCb29sKCdhY3QnKSxcblx0XHRcdHBhcmVudElkOiB6b25lSWQsXG5cdFx0XHRzcHJpdGVDc3NDbGFzczogdGhpcy5fZ2V0Um9vbUxpc3RJY29uQ3NzQ2xhc3Mocm9vbURhdGEuZ2V0Qm9vbCgnYWN0JykpXG5cdFx0fTtcblxuXHRcdHJldHVybiByb29tO1xuXHR9XG5cblx0X2dldFJvb21MaXN0SWNvbkNzc0NsYXNzKGlzQWN0aXZlKVxuXHR7XG5cdFx0cmV0dXJuIGlzQWN0aXZlID8gJ2ZhcyBmYS1kb29yLW9wZW4nIDogJ2ZhcyBmYS1kb29yLWNsb3NlZCBpbmFjdGl2ZS1saXN0LWl0ZW0nO1xuXHR9XG5cblx0X3NldFpvbmVBY3RpdmF0aW9uU3RhdHVzKHpvbmVJZCwgYWN0aXZlUm9vbXMsIGlzQWN0aXZlKVxuXHR7XG5cdFx0bGV0IHpvbmVESSA9IHRoaXMuX2dldFpvbmVEYXRhSXRlbUJ5SWQoem9uZUlkKTtcblxuXHRcdHpvbmVESS5hY3RpdmUgPSBpc0FjdGl2ZTtcblxuXHRcdGxldCBhY3RpdmVSb29tc0FyciA9IGFjdGl2ZVJvb21zLnNwbGl0KCcsJyk7XG5cblx0XHRpZiAoem9uZURJLmhhc0NoaWxkcmVuKVxuXHRcdHtcblx0XHRcdGZvciAobGV0IGkgPSAwOyBpIDwgem9uZURJLmNoaWxkcmVuLmRhdGEoKS5sZW5ndGg7IGkrKylcblx0XHRcdHtcblx0XHRcdFx0bGV0IHJvb20gPSB6b25lREkuY2hpbGRyZW4uZGF0YSgpW2ldO1xuXHRcdFx0XHRyb29tLmFjdGl2ZSA9IChpc0FjdGl2ZSAmJiBhY3RpdmVSb29tc0Fyci5pbmRleE9mKHJvb20ubmFtZSkgPiAtMSk7XG5cdFx0XHRcdHJvb20uc3ByaXRlQ3NzQ2xhc3MgPSB0aGlzLl9nZXRSb29tTGlzdEljb25Dc3NDbGFzcyhyb29tLmFjdGl2ZSlcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBSZWZyZXNoIGxpc3Rcblx0XHR0aGlzLl90cmVldmlldy5kYXRhU291cmNlLnN5bmMoKTtcblxuXHRcdC8vIFJldHVybiB6b25lIG5hbWVcblx0XHRyZXR1cm4gem9uZURJLm5hbWU7XG5cdH1cblxuXHRfZGVzZWxlY3RUcmVlSXRlbSgpXG5cdHtcblx0XHR0aGlzLl90cmVldmlldy5zZWxlY3QoJCgpKTtcblx0fVxuXG5cdF9zZWxlY3RQYXJlbnRUcmVlSXRlbSgpXG5cdHtcblx0XHRsZXQgc2VsZWN0ZWROb2RlID0gdGhpcy5fdHJlZXZpZXcuc2VsZWN0KCk7XG5cdFx0bGV0IHNlbGVjdGVkRGF0YUl0ZW0gPSB0aGlzLl90cmVldmlldy5kYXRhSXRlbShzZWxlY3RlZE5vZGUpO1xuXG5cdFx0aWYgKHNlbGVjdGVkRGF0YUl0ZW0udHlwZSA9PSB0aGlzLklURU1fVFlQRV9ST09NKVxuXHRcdHtcblx0XHRcdGxldCBwYXJlbnROb2RlID0gdGhpcy5fdHJlZXZpZXcucGFyZW50KHNlbGVjdGVkTm9kZSk7XG5cdFx0XHR0aGlzLl90cmVldmlldy5zZWxlY3QocGFyZW50Tm9kZSk7XG5cdFx0fVxuXHR9XG5cblx0X2xvYWRDb25maWd1cmF0aW9uKHR5cGUsIHJlc2V0VGFicyA9IHRydWUpXG5cdHtcblx0XHQvLyBEaXNhYmxlIHpvbmUvcm9vbSBzZWxlY3Rpb24gbGlzdFxuXHRcdHRoaXMuX2VuYWJsZUxpc3RJbnRlcmZhY2UoZmFsc2UpO1xuXG5cdFx0Ly8gRGlzYWJsZSBjb25maWd1cmF0aW9uIGludGVyZmFjZVxuXHRcdHRoaXMuX2VuYWJsZUNvbmZpZ0ludGVyZmFjZShmYWxzZSk7XG5cblx0XHQvLyBDbGVhciBtYWluIGNvbnRhaW5lclxuXHRcdHRoaXMuX3Jlc2V0VGFic0NvbnRhaW5lcih0cnVlLCByZXNldFRhYnMpO1xuXG5cdFx0Ly8gU2V0IGlzRWRpdGluZyBmbGFnXG5cdFx0dGhpcy5faXNFZGl0aW5nID0gdHJ1ZTtcblx0XHR0aGlzLl9lZGl0ZWRJdGVtVHlwZSA9IHR5cGU7XG5cblx0XHQvLyBSZXF1ZXN0IHpvbmUgb3Igcm9vbSBjb25maWd1cmF0aW9uIGRhdGEgdG8gc2VydmVyIGluc3RhbmNlXG5cdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRwYXJhbXMucHV0SW50KCd6SWQnLCB0aGlzLl9lZGl0ZWRab25lSWQpO1xuXHRcdHBhcmFtcy5wdXRJbnQoJ3JJZCcsIHRoaXMuX2VkaXRlZFJvb21JZCk7XG5cblx0XHQvLyBJZiBubyByb29tIGlzIHNlbGVjdGVkLCB0aGVuIHdlIGFyZSBlZGl0aW5nIGEgem9uZVxuXHRcdGlmICh0aGlzLl9lZGl0ZWRJdGVtVHlwZSA9PSB0aGlzLklURU1fVFlQRV9aT05FKVxuXHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9HRVRfWk9ORV9DT05GSUcsIHBhcmFtcyk7XG5cdFx0ZWxzZVxuXHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9HRVRfUk9PTV9DT05GSUcsIHBhcmFtcyk7XG5cblx0XHQvLyBTd2l0Y2ggcGFuZWxcblx0XHR0aGlzLl9zd2l0Y2hQYW5lbCgnem5jLW1haW5QYW5lbCcpO1xuXHR9XG5cblx0X3Jlc2V0VGFic0NvbnRhaW5lcihpc0xvYWRpbmcsIHJlc2V0VGFicylcblx0e1xuXHRcdGlmIChyZXNldFRhYnMpXG5cdFx0XHR0aGlzLl9jbGVhclRhYnMoKTtcblx0XHRlbHNlXG5cdFx0XHR0aGlzLl9yZWluaXRUYWJzID0gZmFsc2U7XG5cblx0XHRpZiAoIWlzTG9hZGluZylcblx0XHRcdHRoaXMuX3N3aXRjaFZpZXcoJ3puYy1zZWxlY3QnKTtcblx0XHRlbHNlXG5cdFx0XHR0aGlzLl9zd2l0Y2hWaWV3KCd6bmMtbG9hZGluZycpO1xuXHR9XG5cblx0X3N3aXRjaFBhbmVsKHBhbmVsSWQpXG5cdHtcblx0XHRkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnem5jLXZpZXcnKS5zZWxlY3RlZFBhbmVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQocGFuZWxJZCk7XG5cdH1cblxuXHRfZ2V0Wm9uZURhdGFJdGVtQnlJZCh6b25lSWQpXG5cdHtcblx0XHRsZXQgem9uZXNEUyA9IHRoaXMuX3RyZWV2aWV3LmRhdGFTb3VyY2U7XG5cdFx0cmV0dXJuIHpvbmVzRFMuZ2V0KHpvbmVJZCk7XG5cdH1cblxuXHRfZ2V0Um9vbURhdGFJdGVtQnlJZCh6b25lSWQsIHJvb21JZClcblx0e1xuXHRcdGxldCB6b25lREkgPSB0aGlzLl9nZXRab25lRGF0YUl0ZW1CeUlkKHpvbmVJZCk7XG5cblx0XHRpZiAoem9uZURJLmhhc0NoaWxkcmVuKVxuXHRcdFx0cmV0dXJuIHpvbmVESS5jaGlsZHJlbi5nZXQocm9vbUlkKTtcblxuXHRcdHJldHVybiBudWxsO1xuXHR9XG5cblx0X3VwZGF0ZVpvbmVOYW1lSW5MaXN0KHpvbmVJZCwgem9uZU5hbWUpXG5cdHtcblx0XHR0aGlzLl9nZXRab25lRGF0YUl0ZW1CeUlkKHpvbmVJZCkubmFtZSA9IHpvbmVOYW1lO1xuXHRcdHRoaXMuX3RyZWV2aWV3LmRhdGFTb3VyY2Uuc3luYygpO1xuXHR9XG5cblx0X3VwZGF0ZVJvb21OYW1lSW5MaXN0KHpvbmVJZCwgcm9vbUlkLCByb29tTmFtZSlcblx0e1xuXHRcdHRoaXMuX2dldFJvb21EYXRhSXRlbUJ5SWQoem9uZUlkLCByb29tSWQpLm5hbWUgPSByb29tTmFtZTtcblx0XHR0aGlzLl90cmVldmlldy5kYXRhU291cmNlLnN5bmMoKTtcblx0fVxuXG5cdF92YWxpZGF0ZU5hbWUoY2hhbmdlcylcblx0e1xuXHRcdGNvbnN0IHpvbmVJZCA9IHRoaXMuX2VkaXRlZFpvbmVJZDtcblxuXHRcdGZvciAobGV0IGkgPSAwOyBpIDwgY2hhbmdlcy5zaXplKCk7IGkrKylcblx0XHR7XG5cdFx0XHRjb25zdCBzZXR0aW5nID0gY2hhbmdlcy5nZXRTRlNPYmplY3QoaSk7XG5cblx0XHRcdGlmIChzZXR0aW5nLmNvbnRhaW5zS2V5KCduYW1lJykgJiYgc2V0dGluZy5nZXRVdGZTdHJpbmcoJ25hbWUnKSA9PSAnbmFtZScpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIEdldCBuYW1lIHZhbHVlXG5cdFx0XHRcdGNvbnN0IG5hbWUgPSBzZXR0aW5nLmdldFRleHQoJ3ZhbHVlJyk7XG5cblx0XHRcdFx0Ly8gR2V0IGRhdGEgc291cmNlXG5cdFx0XHRcdGxldCBkcyA9IFtdO1xuXG5cdFx0XHRcdGlmICh0aGlzLl9lZGl0ZWRJdGVtVHlwZSA9PSB0aGlzLklURU1fVFlQRV9aT05FKVxuXHRcdFx0XHRcdGRzID0gdGhpcy5fdHJlZXZpZXcuZGF0YVNvdXJjZS5kYXRhKCk7XG5cdFx0XHRcdGVsc2Vcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGlmICh0aGlzLl9nZXRab25lRGF0YUl0ZW1CeUlkKHpvbmVJZCkuaGFzQ2hpbGRyZW4pXG5cdFx0XHRcdFx0XHRkcyA9IHRoaXMuX2dldFpvbmVEYXRhSXRlbUJ5SWQoem9uZUlkKS5jaGlsZHJlbi5kYXRhKCk7XG5cdFx0XHRcdH1cblxuXG5cdFx0XHRcdC8vIENoZWNrIGlmIG5hbWUgZXhpc3RzIGluIGRhdGEgc291cmNlXG5cdFx0XHRcdGZvciAobGV0IGogPSAwOyBqIDwgZHMubGVuZ3RoOyBqKyspXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRpZiAoZHNbal0ubmFtZSA9PSBuYW1lKVxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIFBSSVZBVEUgR0VUVEVSU1xuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdGdldCBfc2VsZWN0ZWRJdGVtKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl90cmVldmlldy5kYXRhSXRlbSh0aGlzLl90cmVldmlldy5zZWxlY3QoKSk7XG5cdH1cblxuXHRnZXQgX3NlbGVjdGVkSXRlbVBhcmVudCgpXG5cdHtcblx0XHRsZXQgc2VsZWN0ZWROb2RlID0gdGhpcy5fdHJlZXZpZXcuc2VsZWN0KCk7XG5cdFx0bGV0IHBhcmVudE5vZGUgPSB0aGlzLl90cmVldmlldy5wYXJlbnQoc2VsZWN0ZWROb2RlKTtcblxuXHRcdHJldHVybiB0aGlzLl90cmVldmlldy5kYXRhSXRlbShwYXJlbnROb2RlKTtcblx0fVxuXG5cdGdldCBfZWRpdGVkWm9uZUlkKClcblx0e1xuXHRcdGlmICh0aGlzLl9pc0VkaXRpbmcgJiYgdGhpcy5fc2VsZWN0ZWRJdGVtKVxuXHRcdHtcblx0XHRcdGlmICh0aGlzLl9zZWxlY3RlZEl0ZW0udHlwZSA9PSB0aGlzLklURU1fVFlQRV9aT05FKVxuXHRcdFx0XHRyZXR1cm4gdGhpcy5fc2VsZWN0ZWRJdGVtLmlkO1xuXHRcdFx0ZWxzZVxuXHRcdFx0XHRyZXR1cm4gdGhpcy5fc2VsZWN0ZWRJdGVtUGFyZW50LmlkO1xuXHRcdH1cblxuXHRcdHJldHVybiAtMTtcblx0fVxuXG5cdGdldCBfZWRpdGVkUm9vbUlkKClcblx0e1xuXHRcdGlmICh0aGlzLl9pc0VkaXRpbmcgJiYgdGhpcy5fc2VsZWN0ZWRJdGVtKVxuXHRcdHtcblx0XHRcdGlmICh0aGlzLl9zZWxlY3RlZEl0ZW0udHlwZSA9PSB0aGlzLklURU1fVFlQRV9ST09NKVxuXHRcdFx0XHRyZXR1cm4gdGhpcy5fc2VsZWN0ZWRJdGVtLmlkO1xuXHRcdH1cblxuXHRcdHJldHVybiAtMTtcblx0fVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUNwY0E7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQ3ZIQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7O0EiLCJzb3VyY2VSb290IjoiIn0=
1221
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2pzL2NvcmUvbW9kdWxlcy9tb2R1bGUtMTIuYnVuZGxlLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9ub2RlX21vZHVsZXMvZmlsZS1zYXZlci9kaXN0L0ZpbGVTYXZlci5taW4uanMiLCJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovL2FwcGxpY2F0aW9uLy4vc3JjL2RhdGEvcnVudGltZS1sb2ctZW50cnkuanMiLCJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvbW9kdWxlcy9sb2ctdmlld2VyLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbihhLGIpe2lmKFwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZClkZWZpbmUoW10sYik7ZWxzZSBpZihcInVuZGVmaW5lZFwiIT10eXBlb2YgZXhwb3J0cyliKCk7ZWxzZXtiKCksYS5GaWxlU2F2ZXI9e2V4cG9ydHM6e319LmV4cG9ydHN9fSkodGhpcyxmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO2Z1bmN0aW9uIGIoYSxiKXtyZXR1cm5cInVuZGVmaW5lZFwiPT10eXBlb2YgYj9iPXthdXRvQm9tOiExfTpcIm9iamVjdFwiIT10eXBlb2YgYiYmKGNvbnNvbGUud2FybihcIkRlcHJlY2F0ZWQ6IEV4cGVjdGVkIHRoaXJkIGFyZ3VtZW50IHRvIGJlIGEgb2JqZWN0XCIpLGI9e2F1dG9Cb206IWJ9KSxiLmF1dG9Cb20mJi9eXFxzKig/OnRleHRcXC9cXFMqfGFwcGxpY2F0aW9uXFwveG1sfFxcUypcXC9cXFMqXFwreG1sKVxccyo7LipjaGFyc2V0XFxzKj1cXHMqdXRmLTgvaS50ZXN0KGEudHlwZSk/bmV3IEJsb2IoW1wiXFx1RkVGRlwiLGFdLHt0eXBlOmEudHlwZX0pOmF9ZnVuY3Rpb24gYyhiLGMsZCl7dmFyIGU9bmV3IFhNTEh0dHBSZXF1ZXN0O2Uub3BlbihcIkdFVFwiLGIpLGUucmVzcG9uc2VUeXBlPVwiYmxvYlwiLGUub25sb2FkPWZ1bmN0aW9uKCl7YShlLnJlc3BvbnNlLGMsZCl9LGUub25lcnJvcj1mdW5jdGlvbigpe2NvbnNvbGUuZXJyb3IoXCJjb3VsZCBub3QgZG93bmxvYWQgZmlsZVwiKX0sZS5zZW5kKCl9ZnVuY3Rpb24gZChhKXt2YXIgYj1uZXcgWE1MSHR0cFJlcXVlc3Q7Yi5vcGVuKFwiSEVBRFwiLGEsITEpO3RyeXtiLnNlbmQoKX1jYXRjaChhKXt9cmV0dXJuIDIwMDw9Yi5zdGF0dXMmJjI5OT49Yi5zdGF0dXN9ZnVuY3Rpb24gZShhKXt0cnl7YS5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KFwiY2xpY2tcIikpfWNhdGNoKGMpe3ZhciBiPWRvY3VtZW50LmNyZWF0ZUV2ZW50KFwiTW91c2VFdmVudHNcIik7Yi5pbml0TW91c2VFdmVudChcImNsaWNrXCIsITAsITAsd2luZG93LDAsMCwwLDgwLDIwLCExLCExLCExLCExLDAsbnVsbCksYS5kaXNwYXRjaEV2ZW50KGIpfX12YXIgZj1cIm9iamVjdFwiPT10eXBlb2Ygd2luZG93JiZ3aW5kb3cud2luZG93PT09d2luZG93P3dpbmRvdzpcIm9iamVjdFwiPT10eXBlb2Ygc2VsZiYmc2VsZi5zZWxmPT09c2VsZj9zZWxmOlwib2JqZWN0XCI9PXR5cGVvZiBnbG9iYWwmJmdsb2JhbC5nbG9iYWw9PT1nbG9iYWw/Z2xvYmFsOnZvaWQgMCxhPWYuc2F2ZUFzfHwoXCJvYmplY3RcIiE9dHlwZW9mIHdpbmRvd3x8d2luZG93IT09Zj9mdW5jdGlvbigpe306XCJkb3dubG9hZFwiaW4gSFRNTEFuY2hvckVsZW1lbnQucHJvdG90eXBlP2Z1bmN0aW9uKGIsZyxoKXt2YXIgaT1mLlVSTHx8Zi53ZWJraXRVUkwsaj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiYVwiKTtnPWd8fGIubmFtZXx8XCJkb3dubG9hZFwiLGouZG93bmxvYWQ9ZyxqLnJlbD1cIm5vb3BlbmVyXCIsXCJzdHJpbmdcIj09dHlwZW9mIGI/KGouaHJlZj1iLGoub3JpZ2luPT09bG9jYXRpb24ub3JpZ2luP2Uoaik6ZChqLmhyZWYpP2MoYixnLGgpOmUoaixqLnRhcmdldD1cIl9ibGFua1wiKSk6KGouaHJlZj1pLmNyZWF0ZU9iamVjdFVSTChiKSxzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7aS5yZXZva2VPYmplY3RVUkwoai5ocmVmKX0sNEU0KSxzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ZShqKX0sMCkpfTpcIm1zU2F2ZU9yT3BlbkJsb2JcImluIG5hdmlnYXRvcj9mdW5jdGlvbihmLGcsaCl7aWYoZz1nfHxmLm5hbWV8fFwiZG93bmxvYWRcIixcInN0cmluZ1wiIT10eXBlb2YgZiluYXZpZ2F0b3IubXNTYXZlT3JPcGVuQmxvYihiKGYsaCksZyk7ZWxzZSBpZihkKGYpKWMoZixnLGgpO2Vsc2V7dmFyIGk9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImFcIik7aS5ocmVmPWYsaS50YXJnZXQ9XCJfYmxhbmtcIixzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ZShpKX0pfX06ZnVuY3Rpb24oYSxiLGQsZSl7aWYoZT1lfHxvcGVuKFwiXCIsXCJfYmxhbmtcIiksZSYmKGUuZG9jdW1lbnQudGl0bGU9ZS5kb2N1bWVudC5ib2R5LmlubmVyVGV4dD1cImRvd25sb2FkaW5nLi4uXCIpLFwic3RyaW5nXCI9PXR5cGVvZiBhKXJldHVybiBjKGEsYixkKTt2YXIgZz1cImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiPT09YS50eXBlLGg9L2NvbnN0cnVjdG9yL2kudGVzdChmLkhUTUxFbGVtZW50KXx8Zi5zYWZhcmksaT0vQ3JpT1NcXC9bXFxkXSsvLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7aWYoKGl8fGcmJmgpJiZcIm9iamVjdFwiPT10eXBlb2YgRmlsZVJlYWRlcil7dmFyIGo9bmV3IEZpbGVSZWFkZXI7ai5vbmxvYWRlbmQ9ZnVuY3Rpb24oKXt2YXIgYT1qLnJlc3VsdDthPWk/YTphLnJlcGxhY2UoL15kYXRhOlteO10qOy8sXCJkYXRhOmF0dGFjaG1lbnQvZmlsZTtcIiksZT9lLmxvY2F0aW9uLmhyZWY9YTpsb2NhdGlvbj1hLGU9bnVsbH0sai5yZWFkQXNEYXRhVVJMKGEpfWVsc2V7dmFyIGs9Zi5VUkx8fGYud2Via2l0VVJMLGw9ay5jcmVhdGVPYmplY3RVUkwoYSk7ZT9lLmxvY2F0aW9uPWw6bG9jYXRpb24uaHJlZj1sLGU9bnVsbCxzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ay5yZXZva2VPYmplY3RVUkwobCl9LDRFNCl9fSk7Zi5zYXZlQXM9YS5zYXZlQXM9YSxcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlJiYobW9kdWxlLmV4cG9ydHM9YSl9KTtcblxuLy8jIHNvdXJjZU1hcHBpbmdVUkw9RmlsZVNhdmVyLm1pbi5qcy5tYXAiLCJ2YXIgZztcblxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcbmcgPSAoZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0aGlzO1xufSkoKTtcblxudHJ5IHtcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXG5cdGcgPSBnIHx8IG5ldyBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCk7XG59IGNhdGNoIChlKSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xufVxuXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGc7XG4iLCJleHBvcnQgY2xhc3MgUnVudGltZUxvZ0VudHJ5XG57XG5cdGNvbnN0cnVjdG9yKHNlcGFyYXRvcilcblx0e1xuXHRcdHRoaXMuc2VwID0gc2VwYXJhdG9yXG5cdH1cblxuXHRzdGF0aWMgZnJvbUFycmF5KHNlcGFyYXRvciwgbG9nRW50cnlEYXRhKVxuXHR7XG5cdFx0bGV0IHJsZSA9IG5ldyBSdW50aW1lTG9nRW50cnkoc2VwYXJhdG9yKTtcblxuXHRcdHJsZS5kYXRlID0gbG9nRW50cnlEYXRhWzBdO1xuXHRcdHJsZS50aW1lID0gbG9nRW50cnlEYXRhWzFdO1xuXHRcdHJsZS5kYXRlVGltZSA9IHJsZS5fZ2V0RGF0ZVRpbWUoKTtcblx0XHRybGUubGV2ZWwgPSBsb2dFbnRyeURhdGFbMl0udHJpbSgpO1xuXHRcdHJsZS50aHJlYWQgPSBsb2dFbnRyeURhdGFbM107XG5cdFx0cmxlLmNsYXp6ID0gbG9nRW50cnlEYXRhWzRdO1xuXHRcdHJsZS5tZXNzYWdlID0gbG9nRW50cnlEYXRhWzZdO1xuXG5cdFx0cmV0dXJuIHJsZTtcblx0fVxuXG5cdF9nZXREYXRlVGltZSgpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5kYXRlICsgJ1xcbicgKyB0aGlzLnRpbWU7XG5cdH1cbn1cbiIsImltcG9ydCB7QmFzZU1vZHVsZX0gZnJvbSAnLi9iYXNlLW1vZHVsZSc7XG5pbXBvcnQge1J1bnRpbWVMb2dFbnRyeX0gZnJvbSAnLi4vZGF0YS9ydW50aW1lLWxvZy1lbnRyeSc7XG5pbXBvcnQgKiBhcyBGaWxlU2F2ZXIgZnJvbSAnZmlsZS1zYXZlcic7XG5pbXBvcnQgKiBhcyBtb21lbnQgZnJvbSAnbW9tZW50JztcbmltcG9ydCB7Ynl0ZXNUb1NpemV9IGZyb20gJy4uL3V0aWxzL3V0aWxpdGllcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIExvZ1ZpZXdlciBleHRlbmRzIEJhc2VNb2R1bGVcbntcblx0Y29uc3RydWN0b3IoKVxuXHR7XG5cdCAgICBzdXBlcignbG9nVmlld2VyJyk7XG5cblx0XHR0aGlzLkNPTU1BTkRTX1BSRUZJWCA9ICdsb2dWaWV3ZXInO1xuXHRcdHRoaXMuSEVMUF9VUkwgPSAnL2FkbWludG9vbC1Mb2dWaWV3ZXInO1xuXG5cdFx0dGhpcy5BTllfTEVWRUwgPSAnW2FueV0nO1xuXHRcdHRoaXMuTEVWRUxTID0gWydUUkFDRScsJ0RFQlVHJywnSU5GTycsJ1dBUk4nLCdFUlJPUiddO1xuXHRcdHRoaXMuQU5ZX0NMQVNTID0gJ1thbnldJztcblxuXHRcdHRoaXMuTE9HX0RBVEFfTEFCRUwgPSAnbGluZXMnO1xuXG5cdFx0dGhpcy5CT09UX0xPR19CQUNLVVBfSUQgPSAnU0ZTMlhfQm9vdExvZyc7XG5cdFx0dGhpcy5SVU5USU1FX0xPR19CQUNLVVBfSUQgPSAnU0ZTMlhfUnVudGltZUxvZyc7XG5cdFx0dGhpcy5GVUxMX0JBQ0tVUF9JRCA9ICdTRlMyWF9Mb2dzJztcblxuXHRcdC8vIE91dGdvaW5nIHJlcXVlc3RzXG5cdFx0dGhpcy5SRVFfR0VUX1JVTlRJTUVfTE9HID0gJ2dldFJ1bkxvZyc7XG5cdFx0dGhpcy5SRVFfR0VUX0JPT1RfTE9HID0gJ2dldEJvb3RMb2cnO1xuXHRcdHRoaXMuUkVRX0dFVF9CQUNLVVBTX1NUQVRVUyA9ICdnZXRCYWtTdGF0dXMnO1xuXHRcdHRoaXMuUkVRX0JBQ0tVUF9CT09UX0xPRyA9ICdiYWtCb290TG9nJztcblx0XHR0aGlzLlJFUV9CQUNLVVBfUlVOVElNRV9MT0cgPSAnYmFrUnVuTG9nJztcblx0XHR0aGlzLlJFUV9CQUNLVVBfRlVMTF9MT0dTID0gJ2Jha0Z1bGxMb2dzJztcblx0XHR0aGlzLlJFUV9ERUxFVEVfQkFDS1VQID0gJ2RlbEJhY2t1cCc7XG5cblx0XHQvLyBJbmNvbWluZyByZXNwb25zZXNcblx0XHR0aGlzLlJFU1BfSU5JVF9FUlJPUiA9ICdpbml0RXJyJztcblx0XHR0aGlzLlJFU1BfUlVOVElNRV9MT0dfRVJST1IgPSAncnVuTG9nRXJyJztcblx0XHR0aGlzLlJFU1BfUlVOVElNRV9MT0dfSU5WQUxJRCA9ICdydW5Mb2dJbnYnO1xuXHRcdHRoaXMuUkVTUF9SVU5USU1FX0xPRyA9ICdydW5Mb2cnO1xuXHRcdHRoaXMuUkVTUF9CT09UX0xPR19FUlJPUiA9ICdib290TG9nRXJyJztcblx0XHR0aGlzLlJFU1BfQk9PVF9MT0cgPSAnYm9vdExvZyc7XG5cdFx0dGhpcy5SRVNQX0JBQ0tVUFNfU1RBVFVTID0gJ2Jha1N0YXR1cyc7XG5cdFx0dGhpcy5SRVNQX0RFTEVURV9CQUNLVVBfRkFJTEVEID0gJ2RlbEJha0ZhaWwnO1xuXHRcdHRoaXMuUkVTUF9CQUNLVVBfRVJST1IgPSAnYmFrRXJyb3InO1xuXHRcdHRoaXMuUkVTUF9CQUNLVVBfV0FSTklORyA9ICdiYWtXYXJuJztcblx0fVxuXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIENPTU1PTiBNT0RVTEUgSU5URVJGQUNFIE1FVEhPRFNcblx0Ly8gVGhpcyBtZW1iZXJzIGFyZSB1c2VkIGJ5IHRoZSBtYWluIGNvbnRyb2xsZXJcblx0Ly8gdG8gY29tbXVuaWNhdGUgd2l0aCB0aGUgbW9kdWxlJ3MgY29udHJvbGxlci5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRpbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKVxuXHR7XG5cdFx0Ly8gQ2FsbCBzdXBlciBtZXRob2Rcblx0XHRzdXBlci5pbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKTtcblxuXHRcdC8vIEluaXRpYWxpemUgc2Nyb2xsaW5nIHRhYnNcblx0XHQkKCcjbGd2LXRhYk5hdmlnYXRvciA+ICN0YWJzJykuc2Nyb2xsaW5nVGFicyh7XG5cdFx0XHRib290c3RyYXBWZXJzaW9uOiA0LFxuXHRcdFx0c2Nyb2xsVG9UYWJFZGdlOiB0cnVlLFxuXHRcdFx0ZW5hYmxlU3dpcGluZzogdHJ1ZSxcblx0XHRcdGRpc2FibGVTY3JvbGxBcnJvd3NPbkZ1bGx5U2Nyb2xsZWQ6IHRydWUsXG5cdFx0XHRjc3NDbGFzc0xlZnRBcnJvdzogJ2ZhIGZhLWNoZXZyb24tbGVmdCcsXG5cdFx0XHRjc3NDbGFzc1JpZ2h0QXJyb3c6ICdmYSBmYS1jaGV2cm9uLXJpZ2h0J1xuXHRcdH0pO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIHRhYiBzaG93biBldmVudFxuXHRcdCQoJ2FbZGF0YS10b2dnbGU9XCJ0YWJcIl0nKS5vbignc2hvd24uYnMudGFiJywgJC5wcm94eSh0aGlzLl9vblRhYlNob3duLCB0aGlzKSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGxvZyBsaW5lcyBkcm9wZG93blxuXHRcdHRoaXMuX2xvZ0xpbmVzREQgPSAkKCcjbGd2LWxvZ0xpbmVzREQnKS5rZW5kb0Ryb3BEb3duTGlzdCh7XG5cdFx0XHR2YWx1ZVRlbXBsYXRlOiAnPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIHByLTFcIj5Mb2cgZW50cmllczo8L3NwYW4+PHNwYW4+IzpkYXRhLnRleHQjPC9zcGFuPicsXG5cdFx0fSkuZGF0YSgna2VuZG9Ecm9wRG93bkxpc3QnKTtcblxuXHRcdC8vIEluaXRpYWxpemUgbG9hZCBidXR0b25cblx0XHQkKCcjbGd2LWxvYWRCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25SdW50aW1lTG9nTG9hZEJ0Q2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIEluaXRpYWxpemUgcHJvZ3Jlc3MgYmFyc1xuXHRcdCQoJy5wcm9ncmVzcy1iYXInKS5rZW5kb1Byb2dyZXNzQmFyKHtcblx0XHRcdG1pbjogMCxcbiAgICAgICAgICAgIG1heDogMTAwLFxuXHRcdFx0dmFsdWU6IGZhbHNlLFxuICAgICAgICAgICAgdHlwZTogJ3ZhbHVlJyxcbiAgICAgICAgICAgIGFuaW1hdGlvbjoge1xuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA0MDBcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGxldmVsIGZpbHRlciBkcm9wZG93blxuXHRcdHRoaXMuX2xldmVsRmlsdGVyREQgPSAkKCcjbGd2LWxldmVsREQnKS5rZW5kb0Ryb3BEb3duTGlzdCh7XG5cdFx0XHRkYXRhU291cmNlOiBbdGhpcy5BTllfTEVWRUxdLmNvbmNhdCh0aGlzLkxFVkVMUyksXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkodGhpcy5fb25GaWx0ZXJDaGFuZ2UsIHRoaXMpXG5cdFx0fSkuZGF0YSgna2VuZG9Ecm9wRG93bkxpc3QnKTtcblxuXHRcdC8vIEluaXRpYWxpemUgY2xhc3MgZmlsdGVyIGRyb3Bkb3duXG5cdFx0dGhpcy5fY2xhc3NGaWx0ZXJERCA9ICQoJyNsZ3YtY2xhc3NERCcpLmtlbmRvRHJvcERvd25MaXN0KHtcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vbkZpbHRlckNoYW5nZSwgdGhpcylcblx0XHR9KS5kYXRhKCdrZW5kb0Ryb3BEb3duTGlzdCcpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBtZXNzYWdlIGZpbHRlciBpbnB1dFxuXHRcdCQoJyNsZ3YtbWVzc2FnZUluJykub24oJ2lucHV0JywgJC5wcm94eSh0aGlzLl9vbkZpbHRlckNoYW5nZSwgdGhpcykpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBjbGVhciBidXR0b25cblx0XHQkKCcjbGd2LWNsZWFyRmlsdGVyQnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQ2xlYXJGaWx0ZXJDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBleHBvcnQgYnV0dG9uXG5cdFx0JCgnI2xndi1leHBvcnRSdW50aW1lTG9nQnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uRXhwb3J0UnVudGltZUxvZ0J0Q2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIEluaXRpYWxpemUgcnVudGltZSBsb2cgZ3JpZFxuXHRcdHRoaXMuX3J1bnRpbWVMb2dHcmlkID0gJCgnI2xndi1ydW50aW1lTG9nR3JpZCcpLmtlbmRvR3JpZCh7XG5cdFx0XHRzY3JvbGxhYmxlOiB0cnVlLFxuICAgICAgICAgICAgc29ydGFibGU6IGZhbHNlLFxuXHRcdFx0Ly9yZXNpemFibGU6IHRydWUsXG5cdFx0XHRzZWxlY3RhYmxlOiBmYWxzZSxcbiAgICAgICAgICAgIGNvbHVtbnM6XG4gICAgICAgICAgICBbXG5cdFx0XHRcdHtcblx0ICAgICAgICAgICAgICAgIGZpZWxkOiAnZGF0ZVRpbWUnLFxuXHQgICAgICAgICAgICAgICAgd2lkdGg6IDE1MCxcblx0XHRcdFx0XHR0aXRsZTogJ0RhdGUvVGltZScsXG5cdCAgICAgICAgICAgIH0sXG5cdCAgICAgICAgICAgIHtcblx0ICAgICAgICAgICAgICAgIGZpZWxkOiAnbGV2ZWwnLFxuXHQgICAgICAgICAgICAgICAgd2lkdGg6IDEwMCxcblx0ICAgICAgICAgICAgICAgIHRpdGxlOiAnTGV2ZWwnLFxuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICB7XG5cdCAgICAgICAgICAgICAgICBmaWVsZDogJ3RocmVhZCcsXG5cdCAgICAgICAgICAgICAgICB3aWR0aDogMTUwLFxuXHQgICAgICAgICAgICAgICAgdGl0bGU6ICdUaHJlYWQnLFxuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICB7XG5cdCAgICAgICAgICAgICAgICBmaWVsZDogJ2NsYXp6Jyxcblx0ICAgICAgICAgICAgICAgIHdpZHRoOiAyNTAsXG5cdCAgICAgICAgICAgICAgICB0aXRsZTogJ0NsYXNzJyxcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0e1xuXHQgICAgICAgICAgICAgICAgZmllbGQ6ICdtZXNzYWdlJyxcblx0ICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAwLFxuXHRcdFx0XHRcdHRpdGxlOiAnTWVzc2FnZScsXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRdLFxuXHRcdFx0bm9SZWNvcmRzOiB7XG5cdFx0XHRcdHRlbXBsYXRlOiAnTm8gbG9nIGVudHJpZXMgdG8gZGlzcGxheS4nXG5cdFx0XHR9LFxuXHRcdFx0ZGF0YVNvdXJjZTogW11cbiAgICAgICAgfSkuZGF0YSgna2VuZG9HcmlkJyk7XG5cblx0XHQvLyBJbml0aWFsaXplIGJvb3QgbG9nIHZpZXcgYnV0dG9uc1xuXHRcdCQoJyNsZ3YtZXhwb3J0Qm9vdExvZ0J0Jykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vbkV4cG9ydEJvb3RMb2dCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI2xndi1zd2l0Y2hCb290TG9nQ29sb3JCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25Td2l0Y2hCb290TG9nQ29sb3JCdENsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGdlbmVyYXRlIGJhY2t1cCBidXR0b25zXG5cdFx0JCgnI2xndi1ib290TG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25Cb290TG9nR2VuZXJhdGVCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25SdW50aW1lTG9nR2VuZXJhdGVCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI2xndi1mdWxsTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25GdWxsTG9nc0dlbmVyYXRlQnRDbGljaywgdGhpcykpO1xuXG5cdFx0dGhpcy5faW5pdEJhY2t1cENhcmQoJyNsZ3YtYm9vdExvZ0JhY2t1cENhcmQnKTtcblx0XHR0aGlzLl9pbml0QmFja3VwQ2FyZCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCcpO1xuXHRcdHRoaXMuX2luaXRCYWNrdXBDYXJkKCcjbGd2LWZ1bGxMb2dCYWNrdXBDYXJkJyk7XG5cblx0XHQvLyBJbml0aWFsaXplIGRvd25sb2FkIGdyaWRcblx0XHR0aGlzLl9kb3dubG9hZEdyaWQgPSAkKCcjbGd2LWRvd25sb2FkR3JpZCcpLmtlbmRvR3JpZCh7XG5cdFx0XHRzY3JvbGxhYmxlOiB0cnVlLFxuICAgICAgICAgICAgc29ydGFibGU6IHRydWUsXG5cdFx0XHQvL3Jlc2l6YWJsZTogdHJ1ZSxcblx0XHRcdHNlbGVjdGFibGU6ICdyb3cnLFxuICAgICAgICAgICAgY29sdW1uczpcbiAgICAgICAgICAgIFtcblx0XHRcdFx0e1xuXHQgICAgICAgICAgICAgICAgZmllbGQ6ICdkYXRlJyxcblx0ICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAsXG5cdFx0XHRcdFx0dGl0bGU6ICdEYXRlJyxcblx0ICAgICAgICAgICAgfSxcblx0ICAgICAgICAgICAge1xuXHQgICAgICAgICAgICAgICAgZmllbGQ6ICd0aW1lJyxcblx0ICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAsXG5cdCAgICAgICAgICAgICAgICB0aXRsZTogJ1RpbWUnLFxuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICB7XG5cdCAgICAgICAgICAgICAgICBmaWVsZDogJ25hbWUnLFxuXHQgICAgICAgICAgICAgICAgd2lkdGg6IDMwMCxcblx0ICAgICAgICAgICAgICAgIHRpdGxlOiAnRmlsZW5hbWUnLFxuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICB7XG5cdCAgICAgICAgICAgICAgICBmaWVsZDogJ3NpemUnLFxuXHQgICAgICAgICAgICAgICAgd2lkdGg6IDEwMCxcblx0ICAgICAgICAgICAgICAgIHRpdGxlOiAnU2l6ZScsXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRdLFxuXHRcdFx0bm9SZWNvcmRzOiB7XG5cdFx0XHRcdHRlbXBsYXRlOiAnTm8gYmFja3VwcyBhdmFpbGFibGUuJ1xuXHRcdFx0fSxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vbkRvd25sb2FkR3JpZFNlbGVjdGlvbkNoYW5nZSwgdGhpcyksXG5cdFx0XHRkYXRhU291cmNlOiBbXVxuICAgICAgICB9KS5kYXRhKCdrZW5kb0dyaWQnKTtcblxuXHRcdC8vIEluaXRpYWxpemUgZGVsZXRlIGJ1dHRvblxuXHRcdCQoJyNsZ3YtZGVsZXRlQnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uRGVsZXRlQnRDbGljaywgdGhpcykpO1xuXHR9XG5cblx0ZGVzdHJveSgpXG5cdHtcblx0XHQvLyBDYWxsIHN1cGVyIG1ldGhvZFxuXHRcdHN1cGVyLmRlc3Ryb3koKTtcblxuXHRcdC8vIFJlbW92ZSBsaXN0ZW5lciB0byB0YWIgc2hvd24gZXZlbnRcblx0XHQkKCdhW2RhdGEtdG9nZ2xlPVwidGFiXCJdJykub2ZmKCdzaG93bi5icy50YWInKTtcblxuXHRcdC8vIERlc3Ryb3kgc2Nyb2xsaW5nIHRhYnNcblx0XHQkKCcjbGd2LXRhYk5hdmlnYXRvciAjdGFicycpLnNjcm9sbGluZ1RhYnMoJ2Rlc3Ryb3knKTtcblxuXHRcdC8vIFJlbW92ZSBjbGljayBsaXN0ZW5lcnNcblx0XHQkKCcjbGd2LWxvYWRCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjbGd2LWV4cG9ydEJvb3RMb2dCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjbGd2LXN3aXRjaEJvb3RMb2dDb2xvckJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyNsZ3YtYm9vdExvZ0JhY2t1cENhcmQgLmJhY2t1cC1idXR0b24nKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLm9mZignY2xpY2snKTtcblx0XHQkKCcjbGd2LWNsZWFyRmlsdGVyQnQnKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI2xndi1kZWxldGVCdCcpLm9mZignY2xpY2snKTtcblxuXHRcdCQoJyNsZ3YtbWVzc2FnZUluJykub2ZmKCdpbnB1dCcpO1xuXHR9XG5cblx0b25FeHRlbnNpb25Db21tYW5kKGNvbW1hbmQsIGRhdGEpXG5cdHtcblx0XHQvLyBFcnJvciBkdXJpbmcgaW5pdGlhbGl6YXRpb24gKHVuYWJsZSB0byBhY2Nlc3MgbG9nNGogY29uZmlndXJhdGlvbiBmaWxlKVxuXHRcdGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9JTklUX0VSUk9SKVxuXHRcdHtcblx0XHRcdGNvbnN0IGVycm9yID0gZGF0YS5nZXRVdGZTdHJpbmcoJ2Vycm9yJyk7XG5cblx0XHRcdC8vIFNob3cgYW4gYWxlcnRcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChlcnJvciwgdHJ1ZSk7XG5cblx0XHRcdC8vIFNldCBhbGwgdGFicyB0byBzaG93IGVycm9yc1xuXHRcdFx0dGhpcy5fc3dpdGNoQm9vdFZpZXdTdGFjaygnbGd2LWJvb3RMb2dFcnJvclZpZXcnKTtcblx0XHRcdHRoaXMuX3N3aXRjaFJ1bnRpbWVWaWV3U3RhY2soJ2xndi1ydW50aW1lTG9nRXJyb3JWaWV3Jyk7XG5cdFx0XHR0aGlzLl9zd2l0Y2hEb3dubG9hZFZpZXdTdGFjaygnbGd2LWRvd25sb2FkRXJyb3JWaWV3Jyk7XG5cblx0XHRcdC8vIERpc2FibGUgcnVudGltZSBsb2cgY29udHJvbHNcblx0XHRcdHRoaXMuX2VuYWJsZVJ1bnRpbWVMb2dDb250cm9scyhmYWxzZSk7XG5cblx0XHRcdHRoaXMuX2luaXRGYWlsZWQgPSB0cnVlO1xuXHRcdH1cblxuXHRcdC8vIEVycm9yIHJlc3BvbnNlc1xuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX0JPT1RfTE9HX0VSUk9SIHx8IGNvbW1hbmQgPT0gdGhpcy5SRVNQX1JVTlRJTUVfTE9HX0VSUk9SKVxuXHRcdHtcblx0XHRcdGNvbnN0IGVycm9yID0gZGF0YS5nZXRVdGZTdHJpbmcoJ2Vycm9yJyk7XG5cblx0XHRcdC8vIFNob3cgYW4gYWxlcnRcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChlcnJvciwgdHJ1ZSk7XG5cblx0XHRcdGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9CT09UX0xPR19FUlJPUilcblx0XHRcdHtcblx0XHRcdFx0Ly8gU2V0IHRhYiB0byBzaG93IGVycm9yXG5cdFx0XHRcdHRoaXMuX3N3aXRjaEJvb3RWaWV3U3RhY2soJ2xndi1ib290TG9nRXJyb3JWaWV3Jyk7XG5cblx0XHRcdFx0Ly8gRGlzYWJsZSBib290IGxvZyBiYWNrdXAgZ2VuZXJhdGlvblxuXHRcdFx0XHR0aGlzLl9ib290TG9nQmFja3VwVW5hdmFpbGFibGUgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9kaXNhYmxlQmFja3VwSW50ZXJmYWNlKGZhbHNlKTtcblxuXHRcdFx0XHR0aGlzLl9ib290TG9nUmVxdWVzdGVkID0gdHJ1ZTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1JVTlRJTUVfTE9HX0VSUk9SKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBEaXNhYmxlIGNvbnRyb2xzXG5cdFx0XHRcdHRoaXMuX2VuYWJsZVJ1bnRpbWVMb2dDb250cm9scyhmYWxzZSk7XG5cblx0XHRcdFx0Ly8gUmVtb3ZlIGxvYWRpbmcgYmFyIChydW50aW1lIGxvZylcblx0XHRcdFx0dGhpcy5fc3dpdGNoUnVudGltZVZpZXdTdGFjaygnbGd2LXJ1bnRpbWVMb2dFcnJvclZpZXcnKTtcblxuXHRcdFx0XHQvLyBEaXNhYmxlIHJ1bnRpbWUgbG9nIGJhY2t1cCBkb3dubG9hZFxuXHRcdFx0XHR0aGlzLl9ydW50aW1lTG9nQmFja3VwVW5hdmFpbGFibGUgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9kaXNhYmxlQmFja3VwSW50ZXJmYWNlKGZhbHNlKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBNb2RpZmllZCBjb252ZXJzaW9uIHBhdHRlcm4gaW4gdGhlIGxvZzRqIHByb3BlcnRpZXMgZmlsZTogdW5hYmxlIHRvIHBhcnNlIHRoZSBsb2dcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9SVU5USU1FX0xPR19JTlZBTElEKVxuXHRcdHtcblx0XHRcdC8vIERpc2FibGUgY29udHJvbHNcblx0XHRcdHRoaXMuX2VuYWJsZVJ1bnRpbWVMb2dDb250cm9scyhmYWxzZSk7XG5cblx0XHRcdC8vIEZpbGwgaW4gZXJyb3IgbWVzc2FnZVxuXHRcdFx0JCgnI2xndi1jb252UGF0dE5hbWUnKS50ZXh0KGRhdGEuZ2V0VXRmU3RyaW5nKCdwYXJhbScpKTtcblx0XHRcdCQoJyNsZ3YtY29udlBhdHRWYWwnKS50ZXh0KGRhdGEuZ2V0VXRmU3RyaW5nKCd2YWx1ZScpKTtcblxuXHRcdFx0Ly8gUmVtb3ZlIGxvYWRpbmcgYmFyIChydW50aW1lIGxvZylcblx0XHRcdHRoaXMuX3N3aXRjaFJ1bnRpbWVWaWV3U3RhY2soJ2xndi1pbnZDb252UGF0dFZpZXcnKTtcblx0XHR9XG5cblx0XHQvLyBSdW50aW1lIGxvZyByZWNlaXZlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1JVTlRJTUVfTE9HKVxuXHRcdHtcblx0XHRcdGxldCBjbGFzc2VzID0gW107XG5cdFx0XHRjbGFzc2VzLnB1c2godGhpcy5BTllfQ0xBU1MpO1xuXG5cdFx0XHRsZXQgbG9nRW50cmllcyA9IGRhdGEuZ2V0VXRmU3RyaW5nQXJyYXkodGhpcy5MT0dfREFUQV9MQUJFTCk7XG5cdFx0XHRsZXQgc2VwYXJhdG9yID0gZGF0YS5nZXRVdGZTdHJpbmcoJ3NlcCcpO1xuXHRcdFx0bGV0IGNvbHVtbnMgPSBkYXRhLmdldEludCgnY29scycpO1xuXHRcdFx0bGV0IGRzQXJyID0gW107XG5cblx0XHRcdHRoaXMuX3RvdGFsUnVudGltZUxvZ0VudHJpZXMgPSBsb2dFbnRyaWVzLmxlbmd0aDtcblxuXHRcdFx0Ly8gUGFyc2UgbG9nIGVudHJpZXNcblx0XHRcdC8vIFdlIGNhbid0IHVzZSB0aGUgc3BsaXQgbWV0aG9kIGJlY2F1c2UgdGhlcmUgY291bGQgYmUgaW5zdGFuY2VzIG9mIHRoZSBzZXBhcmF0b3IgaW4gdGhlIGxvZyBtZXNzYWdlIHRvb1xuXHRcdFx0Zm9yIChsZXQgZSA9IDA7IGUgPCBsb2dFbnRyaWVzLmxlbmd0aDsgZSsrKVxuXHRcdFx0e1xuXHRcdFx0XHRjb25zdCBsb2dFbnRyeSA9IGxvZ0VudHJpZXNbZV07XG5cblx0XHRcdFx0bGV0IGxvZ0VudHJ5RGF0YSA9IFtdO1xuXHRcdFx0XHRsZXQgc3RhcnRJbmRleCA9IDA7XG5cblx0XHRcdFx0Zm9yIChsZXQgYyA9IDA7IGMgPCBjb2x1bW5zIC0gMTsgYysrKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGV0IGVuZEluZGV4ID0gbG9nRW50cnkuaW5kZXhPZihzZXBhcmF0b3IsIHN0YXJ0SW5kZXgpO1xuXHRcdFx0XHRcdGxvZ0VudHJ5RGF0YS5wdXNoKGxvZ0VudHJ5LnN1YnN0cmluZyhzdGFydEluZGV4LCBlbmRJbmRleCkpO1xuXHRcdFx0XHRcdHN0YXJ0SW5kZXggPSBlbmRJbmRleCArIHNlcGFyYXRvci5sZW5ndGg7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoc3RhcnRJbmRleCA8IGxvZ0VudHJ5Lmxlbmd0aClcblx0XHRcdFx0XHRsb2dFbnRyeURhdGEucHVzaChsb2dFbnRyeS5zdWJzdHJpbmcoc3RhcnRJbmRleCkpO1xuXG5cdFx0XHRcdC8vIEZpbGwgZGF0YWdyaWQncyBkYXRhcHJvdmlkZXJcblx0XHRcdFx0bGV0IHJsZSA9IFJ1bnRpbWVMb2dFbnRyeS5mcm9tQXJyYXkoc2VwYXJhdG9yLCBsb2dFbnRyeURhdGEpO1xuXHRcdFx0XHRkc0Fyci5wdXNoKHJsZSk7XG5cblx0XHRcdFx0Ly8gQWRkIGNsYXNzIHRvIGZpbHRlcmluZyBkcm9wZG93blxuXHRcdFx0XHRpZiAoY2xhc3Nlcy5pbmRleE9mKHJsZS5jbGF6eikgPCAwKVxuXHRcdFx0XHRcdGNsYXNzZXMucHVzaChybGUuY2xhenopO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTaG93IGNsYXNzZXMgbGlzdFxuXHRcdFx0Y2xhc3Nlcy5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7XG5cdFx0XHRcdHJldHVybiBhLmxvY2FsZUNvbXBhcmUoYik7XG5cdFx0XHR9KTtcblxuXHRcdFx0dGhpcy5fY2xhc3NGaWx0ZXJERC5zZXREYXRhU291cmNlKGNsYXNzZXMpO1xuXHRcdFx0dGhpcy5fY2xhc3NGaWx0ZXJERC5zZWxlY3QoMCk7XG5cblx0XHRcdC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBncmlkXG5cdFx0XHRsZXQgZHMgPSBuZXcga2VuZG8uZGF0YS5EYXRhU291cmNlKHtcblx0XHRcdFx0ZGF0YTogZHNBcnJcblx0XHRcdH0pXG5cblx0XHRcdHRoaXMuX3NldFJ1bnRpbWVMb2dEYXRhU291cmNlKGRzKTtcblxuXHRcdFx0Ly8gUmUtZW5hYmxlIGxvZyBsb2FkaW5nIGNvbnRyb2xzXG5cdFx0XHR0aGlzLl9lbmFibGVSdW50aW1lTG9nQ29udHJvbHModHJ1ZSk7XG5cblx0XHRcdC8vIFJlbW92ZSBsb2FkaW5nIGJhclxuXHRcdFx0dGhpcy5fc3dpdGNoUnVudGltZVZpZXdTdGFjaygnbGd2LXJ1bnRpbWVMb2dWaWV3Jyk7XG5cdFx0fVxuXG5cdFx0Ly8gQm9vdCBsb2cgcmVjZWl2ZWRcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9CT09UX0xPRylcblx0XHR7XG5cdFx0XHRjb25zdCBib290TG9nRW50cmllcyA9IGRhdGEuZ2V0U0ZTQXJyYXkodGhpcy5MT0dfREFUQV9MQUJFTCk7XG5cdFx0XHRsZXQgdGV4dCA9ICcnO1xuXG5cdFx0XHRmb3IgKGxldCBpID0gMDsgaSA8IGJvb3RMb2dFbnRyaWVzLnNpemUoKTsgaSsrKVxuXHRcdFx0XHR0ZXh0ICs9IGJvb3RMb2dFbnRyaWVzLmdldFV0ZlN0cmluZyhpKSArICdcXG4nO1xuXG5cdFx0XHQkKCcjbGd2LWJvb3RMb2dUZXh0JykudGV4dCh0ZXh0KTtcblxuXHRcdFx0Ly8gUmVtb3ZlIGxvYWRpbmcgYmFyXG5cdFx0XHR0aGlzLl9zd2l0Y2hCb290Vmlld1N0YWNrKCdsZ3YtYm9vdExvZ1ZpZXcnKTtcblx0XHR9XG5cblx0XHQvLyBMb2dzIGJhY2t1cHMgc3RhdHVzIHJlY2VpdmVkXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfQkFDS1VQU19TVEFUVVMpXG5cdFx0e1xuXHRcdFx0Ly8gU2hvdy9oaWRlIG9wZXJhdGlvbiBpbiBwcm9ncmVzcyBtZXNzYWdlXG5cdFx0XHR0aGlzLl9kaXNhYmxlQmFja3VwSW50ZXJmYWNlKGRhdGEuZ2V0Qm9vbCgncnVubmluZycpLCBkYXRhLmdldFV0ZlN0cmluZygndHlwZScpKTtcblxuXHRcdFx0Ly8gQmFja3VwIGZpbGVzIGxpc3Rcblx0XHRcdGlmIChkYXRhLmNvbnRhaW5zS2V5KCdmaWxlcycpKVxuXHRcdFx0e1xuXHRcdFx0XHRsZXQgZmlsZXMgPSBkYXRhLmdldFNGU0FycmF5KCdmaWxlcycpO1xuXG5cdFx0XHRcdGxldCBsYXN0Qm9vdExvZ0JhY2t1cEZvdW5kID0gZmFsc2U7XG5cdFx0XHRcdGxldCBsYXN0UnVudGltZUxvZ0JhY2t1cEZvdW5kID0gZmFsc2U7XG5cdFx0XHRcdGxldCBsYXN0RnVsbEJhY2t1cEZvdW5kID0gZmFsc2U7XG5cblx0XHRcdFx0bGV0IGJhY2t1cHNMaXN0ID0gW107XG5cblx0XHRcdFx0Y29uc3Qgd2ViU2VydmVyUHJvdG9jb2wgPSAoZGF0YS5jb250YWluc0tleSgncHJvdG9jb2wnKSA/IGRhdGEuZ2V0VXRmU3RyaW5nKCdwcm90b2NvbCcpIDogJ2h0dHAnKSArICc6Ly8nO1xuXHRcdFx0XHRjb25zdCB3ZWJTZXJ2ZXJQb3J0ID0gKGRhdGEuY29udGFpbnNLZXkoJ3BvcnQnKSA/ICc6JyArIGRhdGEuZ2V0SW50KCdwb3J0JykgOiAnJyk7XG5cblx0XHRcdFx0bGV0IHRvdGFsU2l6ZSA9IDA7XG5cblx0XHRcdFx0Zm9yIChsZXQgZiA9IDA7IGYgPCBmaWxlcy5zaXplKCk7IGYrKylcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGNvbnN0IGZpbGUgPSBmaWxlcy5nZXRTRlNPYmplY3QoZik7XG5cblx0XHRcdFx0XHRjb25zdCBmaWxlUGF0aCA9IGZpbGUuZ2V0VXRmU3RyaW5nKCdwYXRoJyk7XG5cblx0XHRcdFx0XHRjb25zdCBmaWxlT2JqID0ge307XG5cdFx0XHRcdFx0ZmlsZU9iai5wYXRoID0gZmlsZVBhdGg7XG5cdFx0XHRcdFx0ZmlsZU9iai51cmwgPSB3ZWJTZXJ2ZXJQcm90b2NvbCArIHRoaXMuc21hcnRGb3guY29uZmlnLmhvc3QgKyB3ZWJTZXJ2ZXJQb3J0ICsgJy8nICsgZmlsZVBhdGg7XG5cdFx0XHRcdFx0ZmlsZU9iai5uYW1lID0gZmlsZVBhdGguc3Vic3RyKGZpbGVQYXRoLmxhc3RJbmRleE9mKCcvJykgKyAxKTtcblx0XHRcdFx0XHRmaWxlT2JqLmRhdGUgPSBmaWxlLmdldFV0ZlN0cmluZygnZGF0ZScpO1xuXHRcdFx0XHRcdGZpbGVPYmoudGltZSA9IGZpbGUuZ2V0VXRmU3RyaW5nKCd0aW1lJyk7XG5cdFx0XHRcdFx0ZmlsZU9iai5zaXplID0gYnl0ZXNUb1NpemUoZmlsZS5nZXRMb25nKCdzaXplJyksIDIpO1xuXG5cdFx0XHRcdFx0dG90YWxTaXplICs9IGZpbGUuZ2V0TG9uZygnc2l6ZScpO1xuXG5cdFx0XHRcdFx0Ly8gQ2hlY2sgaWYgdGhpcyBpcyBhIGJvb3QgbG9nIGJhY2t1cFxuXHRcdFx0XHRcdGlmICghbGFzdEJvb3RMb2dCYWNrdXBGb3VuZClcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRpZiAoZmlsZU9iai5uYW1lLnN0YXJ0c1dpdGgodGhpcy5CT09UX0xPR19CQUNLVVBfSUQpKVxuXHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHR0aGlzLl9maWxsQmFja3VwQ2FyZCgnI2xndi1ib290TG9nQmFja3VwQ2FyZCcsIGZpbGVPYmopO1xuXG5cdFx0XHRcdFx0XHRcdGxhc3RCb290TG9nQmFja3VwRm91bmQgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIENoZWNrIGlmIHRoaXMgaXMgYSBydW50aW1lIGxvZyBiYWNrdXBcblx0XHRcdFx0XHRpZiAoIWxhc3RSdW50aW1lTG9nQmFja3VwRm91bmQpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0aWYgKGZpbGVPYmoubmFtZS5zdGFydHNXaXRoKHRoaXMuUlVOVElNRV9MT0dfQkFDS1VQX0lEKSlcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0dGhpcy5fZmlsbEJhY2t1cENhcmQoJyNsZ3YtcnVudGltZUxvZ0JhY2t1cENhcmQnLCBmaWxlT2JqKTtcblxuXHRcdFx0XHRcdFx0XHRsYXN0UnVudGltZUxvZ0JhY2t1cEZvdW5kID0gdHJ1ZTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBDaGVjayBpZiB0aGlzIGlzIGEgZnVsbCBiYWNrdXBcblx0XHRcdFx0XHRpZiAoIWxhc3RGdWxsQmFja3VwRm91bmQpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0aWYgKGZpbGVPYmoubmFtZS5zdGFydHNXaXRoKHRoaXMuRlVMTF9CQUNLVVBfSUQpKVxuXHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHR0aGlzLl9maWxsQmFja3VwQ2FyZCgnI2xndi1mdWxsTG9nQmFja3VwQ2FyZCcsIGZpbGVPYmopO1xuXG5cdFx0XHRcdFx0XHRcdGxhc3RGdWxsQmFja3VwRm91bmQgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIFBvcHVsYXRlIGxvZ3MgbGlzdFxuXHRcdFx0XHRcdGJhY2t1cHNMaXN0LnB1c2goZmlsZU9iaik7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBTaG93IHRvdGFsIGJhY2t1cHMgc2l6ZVxuXHRcdFx0XHQkKCcjbGd2LWxvZ1NpemVMYicpLmh0bWwoYFRvdGFsIHNpemU6IDxzdHJvbmc+JHtieXRlc1RvU2l6ZSh0b3RhbFNpemUsIDIsICdLQicpfTwvc3Ryb25nPmApO1xuXG5cdFx0ICAgXHRcdC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBncmlkXG5cdFx0XHRcdHRoaXMuX3NldERvd25sb2FkR3JpZERhdGFTb3VyY2UoYmFja3Vwc0xpc3QpO1xuXHRcdFx0XHR0aGlzLl9vbkRvd25sb2FkR3JpZFNlbGVjdGlvbkNoYW5nZSgpO1xuXG5cdFx0XHRcdC8vIEhpZGUgbGlua3MgdG8gbGF0ZXN0IGZpbGVzIGlmIG5vdCBhdmFpbGFibGVcblx0XHRcdFx0aWYgKCFsYXN0Qm9vdExvZ0JhY2t1cEZvdW5kKVxuXHRcdFx0XHRcdHRoaXMuX2ZpbGxCYWNrdXBDYXJkKCcjbGd2LWJvb3RMb2dCYWNrdXBDYXJkJywgbnVsbCk7XG5cblx0XHRcdFx0aWYgKCFsYXN0UnVudGltZUxvZ0JhY2t1cEZvdW5kKVxuXHRcdFx0XHRcdHRoaXMuX2ZpbGxCYWNrdXBDYXJkKCcjbGd2LXJ1bnRpbWVMb2dCYWNrdXBDYXJkJywgbnVsbCk7XG5cblx0XHRcdFx0aWYgKCFsYXN0RnVsbEJhY2t1cEZvdW5kKVxuXHRcdFx0XHRcdHRoaXMuX2ZpbGxCYWNrdXBDYXJkKCcjbGd2LWZ1bGxMb2dCYWNrdXBDYXJkJywgbnVsbCk7XG5cblx0XHRcdFx0aWYgKGRhdGEuY29udGFpbnNLZXkoJ21lc3NhZ2UnKSlcblx0XHRcdFx0e1xuXHRcdFx0XHRcdC8vIERpc3BsYXkgbm90aWZpY2F0aW9uXG5cdFx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd05vdGlmaWNhdGlvbihgTG9nIGJhY2t1cCB3YXJuaW5nYCwgZGF0YS5nZXRVdGZTdHJpbmcoJ21lc3NhZ2UnKSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gU2V0IGRvd25sb2FkIHZpZXcgdG8gbWFpblxuXHRcdFx0dGhpcy5fc3dpdGNoRG93bmxvYWRWaWV3U3RhY2soJ2xndi1kb3dubG9hZFZpZXcnKTtcblx0XHR9XG5cblx0XHQvLyBMb2dzIGJhY2t1cCBkZWxldGlvbiBmYWlsZWRcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ERUxFVEVfQkFDS1VQX0ZBSUxFRClcblx0XHR7XG5cdFx0XHRjb25zdCBlcnJvciA9IGRhdGEuZ2V0VXRmU3RyaW5nKCdlcnJvcicpO1xuXG5cdFx0XHQvLyBTaG93IGFuIGFsZXJ0XG5cdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoZXJyb3IsIHRydWUpO1xuXHRcdH1cblxuXHRcdC8vIEEgYmxvY2tpbmcgZXJyb3Igb2NjdXJyZWQgZHVyaW5nIGJhY2t1cCBvcGVyYXRpb25cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9CQUNLVVBfRVJST1IpXG5cdFx0e1xuXHRcdFx0Y29uc3QgZXJyb3IgPSBkYXRhLmdldFV0ZlN0cmluZygnZXJyb3InKTtcblxuXHRcdFx0Ly8gU2hvdyBhbiBhbGVydFxuXHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGVycm9yLCB0cnVlKTtcblx0XHR9XG5cblx0XHQvLyBBbiBub24tYmxvY2tpbmcgZXJyb3Igb2NjdXJyZWQgZHVyaW5nIGJhY2t1cCBvcGVyYXRpb25cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9CQUNLVVBfV0FSTklORylcblx0XHR7XG5cdFx0XHRsZXQgd2FybiA9IGRhdGEuZ2V0VXRmU3RyaW5nKCd3YXJuJyk7XG5cblx0XHRcdC8vIERpc3BsYXkgbm90aWZpY2F0aW9uXG5cdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKGBMb2cgYmFja3VwIHdhcm5pbmdgLCB3YXJuKTtcblx0XHR9XG5cdH1cblxuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXHQvLyBVSSBFVkVOVCBMSVNURU5FUlNcblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRfb25UYWJTaG93bihlKVxuXHR7XG5cdFx0aWYgKCF0aGlzLl9pbml0RmFpbGVkKVxuXHRcdHtcblx0XHRcdC8vIElmIGJvb3QgbG9nIHZpZXcgd2FzIGRpc3BsYXllZC4uLlxuXHRcdFx0aWYgKGUudGFyZ2V0LmlkID09ICdsZ3YtYm9vdExvZy10YWInKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBMb2FkIGJvb3QgbG9nIHRoZSBmaXJzdCB0aW1lIHRoZSB0YWIgaXMgc2VsZWN0ZWRcblx0XHRcdFx0aWYgKCF0aGlzLl9ib290TG9nUmVxdWVzdGVkKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9HRVRfQk9PVF9MT0cpO1xuXHRcdFx0XHRcdHRoaXMuX2Jvb3RMb2dSZXF1ZXN0ZWQgPSB0cnVlO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdC8vIElmIGJhY2t1cCZkb3dsb2FkIHZpZXcgd2FzIGRpc3BsYXllZC4uLlxuXHRcdFx0ZWxzZSBpZiAoZS50YXJnZXQuaWQgPT0gJ2xndi1sb2dzRG93bmxvYWQtdGFiJylcblx0XHRcdHtcblx0XHRcdFx0Ly8gUmVxdWVzdCBsb2dzIGJhY2t1cCBzdGF0dXMgdGhlIGZpcnN0IHRpbWUgdGhlIHRhYiBpcyBzZWxlY3RlZFxuXHRcdFx0XHRpZiAoIXRoaXMuX2JhY2t1cFN0YXR1c1JlcXVlc3RlZClcblx0XHRcdFx0e1xuXHRcdFx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfR0VUX0JBQ0tVUFNfU1RBVFVTKTtcblx0XHRcdFx0XHR0aGlzLl9iYWNrdXBTdGF0dXNSZXF1ZXN0ZWQgPSB0cnVlO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0X29uUnVudGltZUxvZ0xvYWRCdENsaWNrKClcblx0e1xuXHRcdC8vIFJlcXVlc3QgbG9nXG5cdFx0dGhpcy5fbG9hZFJ1bnRpbWVMb2coKTtcblx0fVxuXG5cdF9vbkV4cG9ydEJvb3RMb2dCdENsaWNrKClcblx0e1xuXHRcdC8vIEV4cG9ydCBsb2cgdG8gZmlsZVxuXHRcdHRoaXMuX2V4cG9ydExvZygkKCcjbGd2LWJvb3RMb2dUZXh0JykudGV4dCgpLCB0aGlzLkJPT1RfTE9HX0JBQ0tVUF9JRCk7XG5cdH1cblxuXHRfb25Td2l0Y2hCb290TG9nQ29sb3JCdENsaWNrKClcblx0e1xuXHRcdGlmICgkKCcjbGd2LWJvb3RMb2dUZXh0JykuaGFzQ2xhc3MoJ2ludmVydCcpKVxuXHRcdFx0JCgnI2xndi1ib290TG9nVGV4dCcpLnJlbW92ZUNsYXNzKCdpbnZlcnQnKTtcblx0XHRlbHNlXG5cdFx0XHQkKCcjbGd2LWJvb3RMb2dUZXh0JykuYWRkQ2xhc3MoJ2ludmVydCcpO1xuXHR9XG5cblx0X29uQm9vdExvZ0dlbmVyYXRlQnRDbGljaygpXG5cdHtcblx0XHQvLyBTaG93L2hpZGUgb3BlcmF0aW9uIGluIHByb2dyZXNzIG1lc3NhZ2Vcblx0XHR0aGlzLl9kaXNhYmxlQmFja3VwSW50ZXJmYWNlKHRydWUsIHRoaXMuQk9PVF9MT0dfQkFDS1VQX0lEKTtcblxuXHRcdC8vIFJlcXVlc3QgYmFja3VwIGdlbmVyYXRpb25cblx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0JBQ0tVUF9CT09UX0xPRyk7XG5cdH1cblxuXHRfb25SdW50aW1lTG9nR2VuZXJhdGVCdENsaWNrKClcblx0e1xuXHRcdC8vIFNob3cvaGlkZSBvcGVyYXRpb24gaW4gcHJvZ3Jlc3MgbWVzc2FnZVxuXHRcdHRoaXMuX2Rpc2FibGVCYWNrdXBJbnRlcmZhY2UodHJ1ZSwgdGhpcy5SVU5USU1FX0xPR19CQUNLVVBfSUQpO1xuXG5cdFx0Ly8gUmVxdWVzdCBiYWNrdXAgZ2VuZXJhdGlvblxuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfQkFDS1VQX1JVTlRJTUVfTE9HKTtcblx0fVxuXG5cdF9vbkZ1bGxMb2dzR2VuZXJhdGVCdENsaWNrKClcblx0e1xuXHRcdC8vIFNob3cvaGlkZSBvcGVyYXRpb24gaW4gcHJvZ3Jlc3MgbWVzc2FnZVxuXHRcdHRoaXMuX2Rpc2FibGVCYWNrdXBJbnRlcmZhY2UodHJ1ZSwgdGhpcy5GVUxMX0JBQ0tVUF9JRCk7XG5cblx0XHQvLyBSZXF1ZXN0IGJhY2t1cCBnZW5lcmF0aW9uXG5cdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9CQUNLVVBfRlVMTF9MT0dTKTtcblx0fVxuXG5cdF9vbkZpbHRlckNoYW5nZSgpXG5cdHtcblx0XHQvLyBTZXQgZmlsdGVyc1xuXHRcdHRoaXMuX3NldFJ1bnRpbWVMb2dEYXRhU291cmNlKHRoaXMuX3J1bnRpbWVMb2dHcmlkLmRhdGFTb3VyY2UpO1xuXHR9XG5cblx0X29uQ2xlYXJGaWx0ZXJDbGljaygpXG5cdHtcblx0XHR0aGlzLl9jbGVhclJ1bnRpbWVMb2dGaWx0ZXJzKCk7XG5cdFx0dGhpcy5fc2V0UnVudGltZUxvZ0RhdGFTb3VyY2UodGhpcy5fcnVudGltZUxvZ0dyaWQuZGF0YVNvdXJjZSk7XG5cdH1cblxuXHRfb25FeHBvcnRSdW50aW1lTG9nQnRDbGljaygpXG5cdHtcblx0XHRsZXQgbG9nID0gJyc7XG5cdFx0Y29uc3QgZW50cmllcyA9IHRoaXMuX3J1bnRpbWVMb2dHcmlkLmRhdGFTb3VyY2UudmlldygpO1xuXG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBlbnRyaWVzLmxlbmd0aDsgaSsrKVxuXHRcdHtcblx0XHRcdGNvbnN0IGl0ZW0gPSBlbnRyaWVzW2ldO1xuXHRcdFx0bG9nICs9IFtpdGVtLmRhdGUsIGl0ZW0udGltZSwgaXRlbS5sZXZlbCwgaXRlbS50aHJlYWQsIGl0ZW0uY2xhenosIGl0ZW0ubWVzc2FnZV0uam9pbihpdGVtLnNlcCkgKyAnXFxuJztcblx0XHR9XG5cblx0XHQvLyBFeHBvcnQgbG9nIHRvIGZpbGVcblx0XHR0aGlzLl9leHBvcnRMb2cobG9nLCB0aGlzLlJVTlRJTUVfTE9HX0JBQ0tVUF9JRCk7XG5cdH1cblxuXHRfb25Eb3dubG9hZEdyaWRTZWxlY3Rpb25DaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgYnV0dG9uc1xuXHRcdGNvbnN0IHNlbGVjdGVkUm93cyA9IHRoaXMuX2Rvd25sb2FkR3JpZC5zZWxlY3QoKTtcblx0XHQkKCcjbGd2LWRvd25sb2FkQnQnKS5hdHRyKCdkaXNhYmxlZCcsIHNlbGVjdGVkUm93cy5sZW5ndGggPT0gMCk7XG5cdFx0JCgnI2xndi1kZWxldGVCdCcpLmF0dHIoJ2Rpc2FibGVkJywgc2VsZWN0ZWRSb3dzLmxlbmd0aCA9PSAwKTtcblxuXHRcdGlmIChzZWxlY3RlZFJvd3MubGVuZ3RoID4gMClcblx0XHR7XG5cdFx0XHRsZXQgZGF0YUl0ZW0gPSB0aGlzLl9kb3dubG9hZEdyaWQuZGF0YUl0ZW0oc2VsZWN0ZWRSb3dzWzBdKTtcblx0XHRcdCQoJyNsZ3YtZG93bmxvYWRCdCcpLmF0dHIoJ2hyZWYnLCBkYXRhSXRlbS51cmwpO1xuXHRcdH1cblx0XHRlbHNlXG5cdFx0XHQkKCcjbGd2LWRvd25sb2FkQnQnKS5hdHRyKCdocmVmJywgJyMnKTtcblx0fVxuXG5cdF9vbkRlbGV0ZUJ0Q2xpY2soKVxuXHR7XG5cdFx0bGV0IHNlbGVjdGVkUm93cyA9IHRoaXMuX2Rvd25sb2FkR3JpZC5zZWxlY3QoKTtcblxuXHRcdGlmIChzZWxlY3RlZFJvd3MubGVuZ3RoID4gMClcblx0XHR7XG5cdFx0XHRsZXQgZGF0YUl0ZW0gPSB0aGlzLl9kb3dubG9hZEdyaWQuZGF0YUl0ZW0oc2VsZWN0ZWRSb3dzWzBdKTtcblxuXHRcdFx0Ly8gUmVxdWVzdCBiYWNrdXAgZGVsZXRpb25cblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCdmaWxlJywgZGF0YUl0ZW0ubmFtZSk7XG5cblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfREVMRVRFX0JBQ0tVUCwgcGFyYW1zKTtcblx0XHR9XG5cdH1cblxuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXHQvLyBQUklWQVRFIE1FVEhPRFNcblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRfc3dpdGNoUnVudGltZVZpZXdTdGFjayh2aWV3SWQpXG5cdHtcblx0XHRkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGd2LXJ1bnRpbWUtdmlld3N0YWNrJykuc2VsZWN0ZWRFbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodmlld0lkKTtcblx0fVxuXG5cdF9zd2l0Y2hCb290Vmlld1N0YWNrKHZpZXdJZClcblx0e1xuXHRcdGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsZ3YtYm9vdC12aWV3c3RhY2snKS5zZWxlY3RlZEVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWV3SWQpO1xuXHR9XG5cblx0X3N3aXRjaERvd25sb2FkVmlld1N0YWNrKHZpZXdJZClcblx0e1xuXHRcdGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsZ3YtZG93bmxvYWQtdmlld3N0YWNrJykuc2VsZWN0ZWRFbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodmlld0lkKTtcblx0fVxuXG5cdF9lbmFibGVSdW50aW1lTG9nQ29udHJvbHMoZW5hYmxlKVxuXHR7XG5cdFx0JCgnI2xndi1sb2FkQnQnKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGUpO1xuXHRcdCQoJyNsZ3YtZXhwb3J0UnVudGltZUxvZ0J0JykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlKTtcblx0XHQkKCcjbGd2LWZpbHRlckJ0JykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlKTtcblx0fVxuXG5cdF9sb2FkUnVudGltZUxvZygpXG5cdHtcblx0XHQvLyBEaXNhYmxlIGNvbnRyb2xzIHRvIGxvYWQgbG9nLCBzbyB0aGF0IGltcGF0aWVudCB1c2VycyBjYW4ndCBzZW5kIG11bHRpcGxlIHJlcXVlc3RzXG5cdFx0Ly8gKGl0IHdpbGwgYmUgZW5hYmxlZCBhZ2FpbiB3aGVuIGEgcmVzcG9uc2UgaXMgcmVjZWl2ZWQpXG5cdFx0dGhpcy5fZW5hYmxlUnVudGltZUxvZ0NvbnRyb2xzKGZhbHNlKTtcblxuXHRcdC8vIENsZWFyIGZpbHRlcnNcblx0XHR0aGlzLl9jbGVhclJ1bnRpbWVMb2dGaWx0ZXJzKCk7XG5cblx0XHQvLyBTaG93IGxvYWRpbmcgYmFyXG5cdFx0dGhpcy5fc3dpdGNoUnVudGltZVZpZXdTdGFjaygnbGd2LXJ1bnRpbWVMb2dMb2FkaW5nVmlldycpO1xuXG5cdFx0Ly8gU2VuZCByZXF1ZXN0XG5cdFx0Ly8gKHRoZSBudW1iZXIgb2YgbGluZXMgdG8gYmUgcmV0cmlldmVkIGlzIHNlbnQpXG5cdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRwYXJhbXMucHV0SW50KCdudW1FbnRyaWVzJywgTnVtYmVyKHRoaXMuX2xvZ0xpbmVzREQudmFsdWUoKSkpO1xuXG5cdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9HRVRfUlVOVElNRV9MT0csIHBhcmFtcyk7XG5cdH1cblxuXHRfY2xlYXJSdW50aW1lTG9nRmlsdGVycygpXG5cdHtcblx0XHR0aGlzLl9sZXZlbEZpbHRlckRELnNlbGVjdCgwKTtcblx0XHR0aGlzLl9jbGFzc0ZpbHRlckRELnNlbGVjdCgwKTtcblx0XHQkKCcjbGd2LW1lc3NhZ2VJbicpLnZhbCgnJyk7XG5cdH1cblxuXHRfZXhwb3J0TG9nKGxvZywgbmFtZSlcblx0e1xuXHRcdGxldCBibG9iID0gbmV3IEJsb2IoW2xvZ10sIHt0eXBlOiBcInRleHQvcGxhaW47Y2hhcnNldD11dGYtOFwifSk7XG5cdFx0bGV0IGRhdGUgPSBtb21lbnQoKS5mb3JtYXQoJ1lZWVlNTUREX0hIbW1zcycpO1xuXG5cdFx0RmlsZVNhdmVyLnNhdmVBcyhibG9iLCBgJHtuYW1lfV8ke2RhdGV9LmxvZ2ApO1xuXHR9XG5cblx0X3NldFJ1bnRpbWVMb2dEYXRhU291cmNlKGRzKVxuXHR7XG5cdFx0Ly8gUmVhZCBjdXJyZW50IGhvcml6b250YWwgc2Nyb2xsIHZhbHVlXG5cdFx0Y29uc3Qgc2Nyb2xsTGVmdCA9ICQoJyNsZ3YtcnVudGltZUxvZ0dyaWQgLmstZ3JpZC1jb250ZW50JywgdGhpcy5fcnVudGltZUxvZ0dyaWQud3JhcHBlcikuc2Nyb2xsTGVmdCgpO1xuXG5cdFx0Ly8gQXNzaWduIGRhdGEgc291cmNlIHRvIGdyaWRcblx0XHR0aGlzLl9ydW50aW1lTG9nR3JpZC5zZXREYXRhU291cmNlKGRzKTtcblxuXHRcdC8vIFNldCBmaWx0ZXJzXG5cdFx0dGhpcy5fc2V0RmlsdGVycyhkcyk7XG5cblx0XHQvLyBTZXQgaG9yaXpvbnRhbCBzY3JvbGxcblx0XHQkKCcjbGd2LXJ1bnRpbWVMb2dHcmlkIC5rLWdyaWQtY29udGVudCcsIHRoaXMuX3J1bnRpbWVMb2dHcmlkLndyYXBwZXIpLnNjcm9sbExlZnQoc2Nyb2xsTGVmdCk7XG5cblx0XHQvLyBVcGRhdGUgY291bnRlclxuXHRcdCQoJyNsZ3YtcnVudGltZUxvZ0VudHJpZXNMYicpLnRleHQoYExvZyBlbnRyaWVzOiAke3RoaXMuX3RvdGFsUnVudGltZUxvZ0VudHJpZXN9ICgke2RzLnRvdGFsKCl9IGRpc3BsYXllZClgKTtcblx0fVxuXG5cdF9zZXRGaWx0ZXJzKGRzKVxuXHR7XG5cdFx0bGV0IGZpbHRlcnMgPSBbXTtcblxuXHRcdC8vIExldmVsIGZpbHRlcmluZ1xuXHRcdGlmICh0aGlzLl9sZXZlbEZpbHRlckRELnNlbGVjdCgpID4gMClcblx0XHRcdGZpbHRlcnMucHVzaCh7XG5cdFx0XHRcdGZpZWxkOiAnbGV2ZWwnLCBvcGVyYXRvcjogJ2VxJywgdmFsdWU6IHRoaXMuX2xldmVsRmlsdGVyREQudmFsdWUoKVxuXHRcdFx0fSk7XG5cblx0XHQvLyBDbGFzcyBmaWx0ZXJpbmdcblx0XHRpZiAodGhpcy5fY2xhc3NGaWx0ZXJERC5zZWxlY3QoKSA+IDApXG5cdFx0XHRmaWx0ZXJzLnB1c2goe1xuXHRcdFx0XHRmaWVsZDogJ2NsYXp6Jywgb3BlcmF0b3I6ICdlcScsIHZhbHVlOiB0aGlzLl9jbGFzc0ZpbHRlckRELnZhbHVlKClcblx0XHRcdH0pO1xuXG5cdFx0Ly8gTWVzc2FnZSBmaWx0ZXJpbmdcblx0XHRpZiAoJCgnI2xndi1tZXNzYWdlSW4nKS52YWwoKSAhPSAnJylcblx0XHRcdGZpbHRlcnMucHVzaCh7XG5cdFx0XHRcdGZpZWxkOiAnbWVzc2FnZScsIG9wZXJhdG9yOiAnY29udGFpbnMnLCB2YWx1ZTogJCgnI2xndi1tZXNzYWdlSW4nKS52YWwoKVxuXHRcdFx0fSk7XG5cblx0XHQvLyBTZXQgZmlsdGVyc1xuXHRcdGRzLmZpbHRlcihmaWx0ZXJzKTtcblx0fVxuXG5cdF9kaXNhYmxlQmFja3VwSW50ZXJmYWNlKGRpc2FibGUsIGJhY2t1cElkID0gbnVsbClcblx0e1xuXHRcdGlmIChkaXNhYmxlKVxuXHRcdHtcblx0XHRcdC8vIFNob3cgcHJvcGVyIHByb2dyZXNzIGJhclxuXHRcdFx0aWYgKGJhY2t1cElkID09IHRoaXMuQk9PVF9MT0dfQkFDS1VQX0lEKVxuXHRcdFx0XHQkKCcjbGd2LWJvb3RMb2dCYWNrdXBDYXJkIC5wcm9ncmVzcy1iYXInKS5zaG93KCk7XG5cdFx0XHRlbHNlIGlmIChiYWNrdXBJZCA9PSB0aGlzLlJVTlRJTUVfTE9HX0JBQ0tVUF9JRClcblx0XHRcdFx0JCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCAucHJvZ3Jlc3MtYmFyJykuc2hvdygpO1xuXHRcdFx0ZWxzZSBpZiAoYmFja3VwSWQgPT0gdGhpcy5GVUxMX0JBQ0tVUF9JRClcblx0XHRcdFx0JCgnI2xndi1mdWxsTG9nQmFja3VwQ2FyZCAucHJvZ3Jlc3MtYmFyJykuc2hvdygpO1xuXG5cdFx0XHQvLyBEaXNhYmxlIGJ1dHRvbnNcblx0XHRcdCQoJyNsZ3YtYm9vdExvZ0JhY2t1cENhcmQgLmJhY2t1cC1idXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIHRydWUpO1xuXHRcdFx0JCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHQkKCcjbGd2LWZ1bGxMb2dCYWNrdXBDYXJkIC5iYWNrdXAtYnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCB0cnVlKTtcblx0XHR9XG5cdFx0ZWxzZVxuXHRcdHtcblx0XHRcdC8vIEhpZGUgYWxsIHByb2dyZXNzIGJhclxuXHRcdFx0JCgnLmNhcmQtYm9keSAucHJvZ3Jlc3MtYmFyJykuaGlkZSgpO1xuXG5cdFx0XHQvLyBFbmFibGUgYnV0dG9uc1xuXHRcdFx0JCgnI2xndi1mdWxsTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXG5cdFx0XHRpZiAoIXRoaXMuX2Jvb3RMb2dCYWNrdXBVbmF2YWlsYWJsZSlcblx0XHRcdFx0JCgnI2xndi1ib290TG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXG5cdFx0XHRpZiAoIXRoaXMuX3J1bnRpbWVMb2dCYWNrdXBVbmF2YWlsYWJsZSlcblx0XHRcdFx0JCgnI2xndi1ydW50aW1lTG9nQmFja3VwQ2FyZCAuYmFja3VwLWJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXHRcdH1cblx0fVxuXG5cdF9pbml0QmFja3VwQ2FyZChpZFNlbGVjdG9yKVxuXHR7XG5cdFx0JChpZFNlbGVjdG9yICsgJyAuYmFja3VwLWRldGFpbHMnKS5oaWRlKCk7XG5cdFx0JChpZFNlbGVjdG9yICsgJyAucHJvZ3Jlc3MtYmFyJykuaGlkZSgpO1xuXHR9XG5cblx0X2ZpbGxCYWNrdXBDYXJkKGlkU2VsZWN0b3IsIGRldGFpbHNPYmogPSBudWxsKVxuXHR7XG5cdFx0aWYgKGRldGFpbHNPYmogPT0gbnVsbClcblx0XHRcdCQoaWRTZWxlY3RvciArICcgLmJhY2t1cC1kZXRhaWxzJykuaGlkZSgpO1xuXHRcdGVsc2Vcblx0XHR7XG5cdFx0XHQkKGlkU2VsZWN0b3IgKyAnIC5iYWNrdXAtZGV0YWlscycpLnNob3coKTtcblxuXHRcdFx0JChpZFNlbGVjdG9yICsgJyAuYmFja3VwLWxpbmsnKS5hdHRyKCdocmVmJywgZGV0YWlsc09iai51cmwpO1xuXHRcdFx0JChpZFNlbGVjdG9yICsgJyAuYmFja3VwLWRhdGUnKS50ZXh0KGRldGFpbHNPYmouZGF0ZSk7XG5cdFx0XHQkKGlkU2VsZWN0b3IgKyAnIC5iYWNrdXAtdGltZScpLnRleHQoZGV0YWlsc09iai50aW1lKTtcblx0XHRcdCQoaWRTZWxlY3RvciArICcgLmJhY2t1cC1zaXplJykudGV4dChkZXRhaWxzT2JqLnNpemUpO1xuXHRcdH1cblx0fVxuXG5cdF9zZXREb3dubG9hZEdyaWREYXRhU291cmNlKGRzKVxuXHR7XG5cdFx0Ly8gUmVhZCBjdXJyZW50IGhvcml6b250YWwgc2Nyb2xsIHZhbHVlXG5cdCAgIGNvbnN0IHNjcm9sbExlZnQgPSAkKCcjbGd2LWRvd25sb2FkR3JpZCAuay1ncmlkLWNvbnRlbnQnLCB0aGlzLl9kb3dubG9hZEdyaWQud3JhcHBlcikuc2Nyb2xsTGVmdCgpO1xuXG5cdCAgIC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBncmlkXG5cdCAgIHRoaXMuX2Rvd25sb2FkR3JpZC5zZXREYXRhU291cmNlKGRzKTtcblxuXHQgICAvLyBTZXQgaG9yaXpvbnRhbCBzY3JvbGxcblx0ICAgJCgnI2xndi1kb3dubG9hZEdyaWQgLmstZ3JpZC1jb250ZW50JywgdGhpcy5fZG93bmxvYWRHcmlkLndyYXBwZXIpLnNjcm9sbExlZnQoc2Nyb2xsTGVmdCk7XG5cdH1cblxuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXHQvLyBQUklWQVRFIEdFVFRFUlNcblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FDbkJBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUMxQkE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7QSIsInNvdXJjZVJvb3QiOiIifQ==