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,3332 +1,845 @@
1
1
  /*! (c) gotoAndPlay | All rights reserved */
2
2
  (window["webpackJsonpapplication"] = window["webpackJsonpapplication"] || []).push([["module-13"],{
3
3
 
4
- /***/ "./node_modules/moment/locale sync recursive ^\\.\\/.*$":
5
- /*!**************************************************!*\
6
- !*** ./node_modules/moment/locale sync ^\.\/.*$ ***!
7
- \**************************************************/
8
- /*! no static exports found */
9
- /***/ (function(module, exports, __webpack_require__) {
10
-
11
- var map = {
12
- "./af": "./node_modules/moment/locale/af.js",
13
- "./af.js": "./node_modules/moment/locale/af.js",
14
- "./ar": "./node_modules/moment/locale/ar.js",
15
- "./ar-dz": "./node_modules/moment/locale/ar-dz.js",
16
- "./ar-dz.js": "./node_modules/moment/locale/ar-dz.js",
17
- "./ar-kw": "./node_modules/moment/locale/ar-kw.js",
18
- "./ar-kw.js": "./node_modules/moment/locale/ar-kw.js",
19
- "./ar-ly": "./node_modules/moment/locale/ar-ly.js",
20
- "./ar-ly.js": "./node_modules/moment/locale/ar-ly.js",
21
- "./ar-ma": "./node_modules/moment/locale/ar-ma.js",
22
- "./ar-ma.js": "./node_modules/moment/locale/ar-ma.js",
23
- "./ar-sa": "./node_modules/moment/locale/ar-sa.js",
24
- "./ar-sa.js": "./node_modules/moment/locale/ar-sa.js",
25
- "./ar-tn": "./node_modules/moment/locale/ar-tn.js",
26
- "./ar-tn.js": "./node_modules/moment/locale/ar-tn.js",
27
- "./ar.js": "./node_modules/moment/locale/ar.js",
28
- "./az": "./node_modules/moment/locale/az.js",
29
- "./az.js": "./node_modules/moment/locale/az.js",
30
- "./be": "./node_modules/moment/locale/be.js",
31
- "./be.js": "./node_modules/moment/locale/be.js",
32
- "./bg": "./node_modules/moment/locale/bg.js",
33
- "./bg.js": "./node_modules/moment/locale/bg.js",
34
- "./bm": "./node_modules/moment/locale/bm.js",
35
- "./bm.js": "./node_modules/moment/locale/bm.js",
36
- "./bn": "./node_modules/moment/locale/bn.js",
37
- "./bn.js": "./node_modules/moment/locale/bn.js",
38
- "./bo": "./node_modules/moment/locale/bo.js",
39
- "./bo.js": "./node_modules/moment/locale/bo.js",
40
- "./br": "./node_modules/moment/locale/br.js",
41
- "./br.js": "./node_modules/moment/locale/br.js",
42
- "./bs": "./node_modules/moment/locale/bs.js",
43
- "./bs.js": "./node_modules/moment/locale/bs.js",
44
- "./ca": "./node_modules/moment/locale/ca.js",
45
- "./ca.js": "./node_modules/moment/locale/ca.js",
46
- "./cs": "./node_modules/moment/locale/cs.js",
47
- "./cs.js": "./node_modules/moment/locale/cs.js",
48
- "./cv": "./node_modules/moment/locale/cv.js",
49
- "./cv.js": "./node_modules/moment/locale/cv.js",
50
- "./cy": "./node_modules/moment/locale/cy.js",
51
- "./cy.js": "./node_modules/moment/locale/cy.js",
52
- "./da": "./node_modules/moment/locale/da.js",
53
- "./da.js": "./node_modules/moment/locale/da.js",
54
- "./de": "./node_modules/moment/locale/de.js",
55
- "./de-at": "./node_modules/moment/locale/de-at.js",
56
- "./de-at.js": "./node_modules/moment/locale/de-at.js",
57
- "./de-ch": "./node_modules/moment/locale/de-ch.js",
58
- "./de-ch.js": "./node_modules/moment/locale/de-ch.js",
59
- "./de.js": "./node_modules/moment/locale/de.js",
60
- "./dv": "./node_modules/moment/locale/dv.js",
61
- "./dv.js": "./node_modules/moment/locale/dv.js",
62
- "./el": "./node_modules/moment/locale/el.js",
63
- "./el.js": "./node_modules/moment/locale/el.js",
64
- "./en-SG": "./node_modules/moment/locale/en-SG.js",
65
- "./en-SG.js": "./node_modules/moment/locale/en-SG.js",
66
- "./en-au": "./node_modules/moment/locale/en-au.js",
67
- "./en-au.js": "./node_modules/moment/locale/en-au.js",
68
- "./en-ca": "./node_modules/moment/locale/en-ca.js",
69
- "./en-ca.js": "./node_modules/moment/locale/en-ca.js",
70
- "./en-gb": "./node_modules/moment/locale/en-gb.js",
71
- "./en-gb.js": "./node_modules/moment/locale/en-gb.js",
72
- "./en-ie": "./node_modules/moment/locale/en-ie.js",
73
- "./en-ie.js": "./node_modules/moment/locale/en-ie.js",
74
- "./en-il": "./node_modules/moment/locale/en-il.js",
75
- "./en-il.js": "./node_modules/moment/locale/en-il.js",
76
- "./en-nz": "./node_modules/moment/locale/en-nz.js",
77
- "./en-nz.js": "./node_modules/moment/locale/en-nz.js",
78
- "./eo": "./node_modules/moment/locale/eo.js",
79
- "./eo.js": "./node_modules/moment/locale/eo.js",
80
- "./es": "./node_modules/moment/locale/es.js",
81
- "./es-do": "./node_modules/moment/locale/es-do.js",
82
- "./es-do.js": "./node_modules/moment/locale/es-do.js",
83
- "./es-us": "./node_modules/moment/locale/es-us.js",
84
- "./es-us.js": "./node_modules/moment/locale/es-us.js",
85
- "./es.js": "./node_modules/moment/locale/es.js",
86
- "./et": "./node_modules/moment/locale/et.js",
87
- "./et.js": "./node_modules/moment/locale/et.js",
88
- "./eu": "./node_modules/moment/locale/eu.js",
89
- "./eu.js": "./node_modules/moment/locale/eu.js",
90
- "./fa": "./node_modules/moment/locale/fa.js",
91
- "./fa.js": "./node_modules/moment/locale/fa.js",
92
- "./fi": "./node_modules/moment/locale/fi.js",
93
- "./fi.js": "./node_modules/moment/locale/fi.js",
94
- "./fo": "./node_modules/moment/locale/fo.js",
95
- "./fo.js": "./node_modules/moment/locale/fo.js",
96
- "./fr": "./node_modules/moment/locale/fr.js",
97
- "./fr-ca": "./node_modules/moment/locale/fr-ca.js",
98
- "./fr-ca.js": "./node_modules/moment/locale/fr-ca.js",
99
- "./fr-ch": "./node_modules/moment/locale/fr-ch.js",
100
- "./fr-ch.js": "./node_modules/moment/locale/fr-ch.js",
101
- "./fr.js": "./node_modules/moment/locale/fr.js",
102
- "./fy": "./node_modules/moment/locale/fy.js",
103
- "./fy.js": "./node_modules/moment/locale/fy.js",
104
- "./ga": "./node_modules/moment/locale/ga.js",
105
- "./ga.js": "./node_modules/moment/locale/ga.js",
106
- "./gd": "./node_modules/moment/locale/gd.js",
107
- "./gd.js": "./node_modules/moment/locale/gd.js",
108
- "./gl": "./node_modules/moment/locale/gl.js",
109
- "./gl.js": "./node_modules/moment/locale/gl.js",
110
- "./gom-latn": "./node_modules/moment/locale/gom-latn.js",
111
- "./gom-latn.js": "./node_modules/moment/locale/gom-latn.js",
112
- "./gu": "./node_modules/moment/locale/gu.js",
113
- "./gu.js": "./node_modules/moment/locale/gu.js",
114
- "./he": "./node_modules/moment/locale/he.js",
115
- "./he.js": "./node_modules/moment/locale/he.js",
116
- "./hi": "./node_modules/moment/locale/hi.js",
117
- "./hi.js": "./node_modules/moment/locale/hi.js",
118
- "./hr": "./node_modules/moment/locale/hr.js",
119
- "./hr.js": "./node_modules/moment/locale/hr.js",
120
- "./hu": "./node_modules/moment/locale/hu.js",
121
- "./hu.js": "./node_modules/moment/locale/hu.js",
122
- "./hy-am": "./node_modules/moment/locale/hy-am.js",
123
- "./hy-am.js": "./node_modules/moment/locale/hy-am.js",
124
- "./id": "./node_modules/moment/locale/id.js",
125
- "./id.js": "./node_modules/moment/locale/id.js",
126
- "./is": "./node_modules/moment/locale/is.js",
127
- "./is.js": "./node_modules/moment/locale/is.js",
128
- "./it": "./node_modules/moment/locale/it.js",
129
- "./it-ch": "./node_modules/moment/locale/it-ch.js",
130
- "./it-ch.js": "./node_modules/moment/locale/it-ch.js",
131
- "./it.js": "./node_modules/moment/locale/it.js",
132
- "./ja": "./node_modules/moment/locale/ja.js",
133
- "./ja.js": "./node_modules/moment/locale/ja.js",
134
- "./jv": "./node_modules/moment/locale/jv.js",
135
- "./jv.js": "./node_modules/moment/locale/jv.js",
136
- "./ka": "./node_modules/moment/locale/ka.js",
137
- "./ka.js": "./node_modules/moment/locale/ka.js",
138
- "./kk": "./node_modules/moment/locale/kk.js",
139
- "./kk.js": "./node_modules/moment/locale/kk.js",
140
- "./km": "./node_modules/moment/locale/km.js",
141
- "./km.js": "./node_modules/moment/locale/km.js",
142
- "./kn": "./node_modules/moment/locale/kn.js",
143
- "./kn.js": "./node_modules/moment/locale/kn.js",
144
- "./ko": "./node_modules/moment/locale/ko.js",
145
- "./ko.js": "./node_modules/moment/locale/ko.js",
146
- "./ku": "./node_modules/moment/locale/ku.js",
147
- "./ku.js": "./node_modules/moment/locale/ku.js",
148
- "./ky": "./node_modules/moment/locale/ky.js",
149
- "./ky.js": "./node_modules/moment/locale/ky.js",
150
- "./lb": "./node_modules/moment/locale/lb.js",
151
- "./lb.js": "./node_modules/moment/locale/lb.js",
152
- "./lo": "./node_modules/moment/locale/lo.js",
153
- "./lo.js": "./node_modules/moment/locale/lo.js",
154
- "./lt": "./node_modules/moment/locale/lt.js",
155
- "./lt.js": "./node_modules/moment/locale/lt.js",
156
- "./lv": "./node_modules/moment/locale/lv.js",
157
- "./lv.js": "./node_modules/moment/locale/lv.js",
158
- "./me": "./node_modules/moment/locale/me.js",
159
- "./me.js": "./node_modules/moment/locale/me.js",
160
- "./mi": "./node_modules/moment/locale/mi.js",
161
- "./mi.js": "./node_modules/moment/locale/mi.js",
162
- "./mk": "./node_modules/moment/locale/mk.js",
163
- "./mk.js": "./node_modules/moment/locale/mk.js",
164
- "./ml": "./node_modules/moment/locale/ml.js",
165
- "./ml.js": "./node_modules/moment/locale/ml.js",
166
- "./mn": "./node_modules/moment/locale/mn.js",
167
- "./mn.js": "./node_modules/moment/locale/mn.js",
168
- "./mr": "./node_modules/moment/locale/mr.js",
169
- "./mr.js": "./node_modules/moment/locale/mr.js",
170
- "./ms": "./node_modules/moment/locale/ms.js",
171
- "./ms-my": "./node_modules/moment/locale/ms-my.js",
172
- "./ms-my.js": "./node_modules/moment/locale/ms-my.js",
173
- "./ms.js": "./node_modules/moment/locale/ms.js",
174
- "./mt": "./node_modules/moment/locale/mt.js",
175
- "./mt.js": "./node_modules/moment/locale/mt.js",
176
- "./my": "./node_modules/moment/locale/my.js",
177
- "./my.js": "./node_modules/moment/locale/my.js",
178
- "./nb": "./node_modules/moment/locale/nb.js",
179
- "./nb.js": "./node_modules/moment/locale/nb.js",
180
- "./ne": "./node_modules/moment/locale/ne.js",
181
- "./ne.js": "./node_modules/moment/locale/ne.js",
182
- "./nl": "./node_modules/moment/locale/nl.js",
183
- "./nl-be": "./node_modules/moment/locale/nl-be.js",
184
- "./nl-be.js": "./node_modules/moment/locale/nl-be.js",
185
- "./nl.js": "./node_modules/moment/locale/nl.js",
186
- "./nn": "./node_modules/moment/locale/nn.js",
187
- "./nn.js": "./node_modules/moment/locale/nn.js",
188
- "./pa-in": "./node_modules/moment/locale/pa-in.js",
189
- "./pa-in.js": "./node_modules/moment/locale/pa-in.js",
190
- "./pl": "./node_modules/moment/locale/pl.js",
191
- "./pl.js": "./node_modules/moment/locale/pl.js",
192
- "./pt": "./node_modules/moment/locale/pt.js",
193
- "./pt-br": "./node_modules/moment/locale/pt-br.js",
194
- "./pt-br.js": "./node_modules/moment/locale/pt-br.js",
195
- "./pt.js": "./node_modules/moment/locale/pt.js",
196
- "./ro": "./node_modules/moment/locale/ro.js",
197
- "./ro.js": "./node_modules/moment/locale/ro.js",
198
- "./ru": "./node_modules/moment/locale/ru.js",
199
- "./ru.js": "./node_modules/moment/locale/ru.js",
200
- "./sd": "./node_modules/moment/locale/sd.js",
201
- "./sd.js": "./node_modules/moment/locale/sd.js",
202
- "./se": "./node_modules/moment/locale/se.js",
203
- "./se.js": "./node_modules/moment/locale/se.js",
204
- "./si": "./node_modules/moment/locale/si.js",
205
- "./si.js": "./node_modules/moment/locale/si.js",
206
- "./sk": "./node_modules/moment/locale/sk.js",
207
- "./sk.js": "./node_modules/moment/locale/sk.js",
208
- "./sl": "./node_modules/moment/locale/sl.js",
209
- "./sl.js": "./node_modules/moment/locale/sl.js",
210
- "./sq": "./node_modules/moment/locale/sq.js",
211
- "./sq.js": "./node_modules/moment/locale/sq.js",
212
- "./sr": "./node_modules/moment/locale/sr.js",
213
- "./sr-cyrl": "./node_modules/moment/locale/sr-cyrl.js",
214
- "./sr-cyrl.js": "./node_modules/moment/locale/sr-cyrl.js",
215
- "./sr.js": "./node_modules/moment/locale/sr.js",
216
- "./ss": "./node_modules/moment/locale/ss.js",
217
- "./ss.js": "./node_modules/moment/locale/ss.js",
218
- "./sv": "./node_modules/moment/locale/sv.js",
219
- "./sv.js": "./node_modules/moment/locale/sv.js",
220
- "./sw": "./node_modules/moment/locale/sw.js",
221
- "./sw.js": "./node_modules/moment/locale/sw.js",
222
- "./ta": "./node_modules/moment/locale/ta.js",
223
- "./ta.js": "./node_modules/moment/locale/ta.js",
224
- "./te": "./node_modules/moment/locale/te.js",
225
- "./te.js": "./node_modules/moment/locale/te.js",
226
- "./tet": "./node_modules/moment/locale/tet.js",
227
- "./tet.js": "./node_modules/moment/locale/tet.js",
228
- "./tg": "./node_modules/moment/locale/tg.js",
229
- "./tg.js": "./node_modules/moment/locale/tg.js",
230
- "./th": "./node_modules/moment/locale/th.js",
231
- "./th.js": "./node_modules/moment/locale/th.js",
232
- "./tl-ph": "./node_modules/moment/locale/tl-ph.js",
233
- "./tl-ph.js": "./node_modules/moment/locale/tl-ph.js",
234
- "./tlh": "./node_modules/moment/locale/tlh.js",
235
- "./tlh.js": "./node_modules/moment/locale/tlh.js",
236
- "./tr": "./node_modules/moment/locale/tr.js",
237
- "./tr.js": "./node_modules/moment/locale/tr.js",
238
- "./tzl": "./node_modules/moment/locale/tzl.js",
239
- "./tzl.js": "./node_modules/moment/locale/tzl.js",
240
- "./tzm": "./node_modules/moment/locale/tzm.js",
241
- "./tzm-latn": "./node_modules/moment/locale/tzm-latn.js",
242
- "./tzm-latn.js": "./node_modules/moment/locale/tzm-latn.js",
243
- "./tzm.js": "./node_modules/moment/locale/tzm.js",
244
- "./ug-cn": "./node_modules/moment/locale/ug-cn.js",
245
- "./ug-cn.js": "./node_modules/moment/locale/ug-cn.js",
246
- "./uk": "./node_modules/moment/locale/uk.js",
247
- "./uk.js": "./node_modules/moment/locale/uk.js",
248
- "./ur": "./node_modules/moment/locale/ur.js",
249
- "./ur.js": "./node_modules/moment/locale/ur.js",
250
- "./uz": "./node_modules/moment/locale/uz.js",
251
- "./uz-latn": "./node_modules/moment/locale/uz-latn.js",
252
- "./uz-latn.js": "./node_modules/moment/locale/uz-latn.js",
253
- "./uz.js": "./node_modules/moment/locale/uz.js",
254
- "./vi": "./node_modules/moment/locale/vi.js",
255
- "./vi.js": "./node_modules/moment/locale/vi.js",
256
- "./x-pseudo": "./node_modules/moment/locale/x-pseudo.js",
257
- "./x-pseudo.js": "./node_modules/moment/locale/x-pseudo.js",
258
- "./yo": "./node_modules/moment/locale/yo.js",
259
- "./yo.js": "./node_modules/moment/locale/yo.js",
260
- "./zh-cn": "./node_modules/moment/locale/zh-cn.js",
261
- "./zh-cn.js": "./node_modules/moment/locale/zh-cn.js",
262
- "./zh-hk": "./node_modules/moment/locale/zh-hk.js",
263
- "./zh-hk.js": "./node_modules/moment/locale/zh-hk.js",
264
- "./zh-tw": "./node_modules/moment/locale/zh-tw.js",
265
- "./zh-tw.js": "./node_modules/moment/locale/zh-tw.js"
266
- };
267
-
268
-
269
- function webpackContext(req) {
270
- var id = webpackContextResolve(req);
271
- return __webpack_require__(id);
272
- }
273
- function webpackContextResolve(req) {
274
- if(!__webpack_require__.o(map, req)) {
275
- var e = new Error("Cannot find module '" + req + "'");
276
- e.code = 'MODULE_NOT_FOUND';
277
- throw e;
278
- }
279
- return map[req];
280
- }
281
- webpackContext.keys = function webpackContextKeys() {
282
- return Object.keys(map);
283
- };
284
- webpackContext.resolve = webpackContextResolve;
285
- module.exports = webpackContext;
286
- webpackContext.id = "./node_modules/moment/locale sync recursive ^\\.\\/.*$";
287
-
288
- /***/ }),
289
-
290
- /***/ "./src/components/charts/chart-utils.js":
291
- /*!**********************************************!*\
292
- !*** ./src/components/charts/chart-utils.js ***!
293
- \**********************************************/
294
- /*! exports provided: getFormattedDateTime, getBasicSharedTemplate, getMemorySharedTemplate, getMemoryValueAxisLabelTemplate, getNetworkSharedTemplate, getNetworkValueAxisLabelTemplate, getUserCountSharedTemplate, getTimeRangeTooltipTemplate, getWRBytesLabelTemplate */
4
+ /***/ "./src/components/module-specific/ssl-certificate-manager.js":
5
+ /*!*******************************************************************!*\
6
+ !*** ./src/components/module-specific/ssl-certificate-manager.js ***!
7
+ \*******************************************************************/
8
+ /*! exports provided: SslCertificateManager */
295
9
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
296
10
 
297
11
  "use strict";
298
12
  __webpack_require__.r(__webpack_exports__);
299
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getFormattedDateTime", function() { return getFormattedDateTime; });
300
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBasicSharedTemplate", function() { return getBasicSharedTemplate; });
301
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getMemorySharedTemplate", function() { return getMemorySharedTemplate; });
302
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getMemoryValueAxisLabelTemplate", function() { return getMemoryValueAxisLabelTemplate; });
303
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getNetworkSharedTemplate", function() { return getNetworkSharedTemplate; });
304
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getNetworkValueAxisLabelTemplate", function() { return getNetworkValueAxisLabelTemplate; });
305
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getUserCountSharedTemplate", function() { return getUserCountSharedTemplate; });
306
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getTimeRangeTooltipTemplate", function() { return getTimeRangeTooltipTemplate; });
307
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getWRBytesLabelTemplate", function() { return getWRBytesLabelTemplate; });
13
+ /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SslCertificateManager", function() { return SslCertificateManager; });
308
14
  /* harmony import */ var _utils_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../utils/utilities */ "./src/utils/utilities.js");
15
+ /* harmony import */ var aes_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! aes-js */ "./node_modules/aes-js/index.js");
16
+ /* harmony import */ var aes_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(aes_js__WEBPACK_IMPORTED_MODULE_1__);
309
17
 
310
18
 
311
- function getFormattedDateTime(date, format = 'dd MMM HH:mm:ss')
312
- {
313
- return kendo.toString(date, format);
314
- }
315
-
316
- function getBasicSharedTemplate(valueFormat = '{0}') {
317
- return kendo.template(`
318
- <div class="chart-tooltip-category"><strong>#: category #</strong></div>
319
- # for (var i = 0; i < points.length; i++) { #
320
- <div>
321
- <i class="fas fa-square" style="color: #: points[i].color # "></i>
322
- #: points[i].series.name# : #: kendo.format('${valueFormat}', points[i].value) #
323
- </div>
324
- # } #
325
- `);
326
- }
327
-
328
- function getMemorySharedTemplate() {
329
- return function(dataItem) {
330
- dataItem.bytesToSize = _utils_utilities__WEBPACK_IMPORTED_MODULE_0__["bytesToSize"]; // Pass bytesToSize utility function to template
331
- return kendo.template(`
332
- <div class="chart-tooltip-category"><strong>#: category #</strong></div>
333
- # for (var i = 0; i < points.length; i++) { #
334
- <div>
335
- <i class="fas fa-square" style="color: #: points[i].color # "></i>
336
- #: points[i].series.name# : #: bytesToSize(points[i].value) #
337
- </div>
338
- # } #
339
- `)(dataItem);
340
- };
341
- }
342
-
343
- function getMemoryValueAxisLabelTemplate() {
344
- return function(dataItem) {
345
- dataItem.bytesToSize = _utils_utilities__WEBPACK_IMPORTED_MODULE_0__["bytesToSize"]; // Pass bytesToSize utility function to template
346
- return kendo.template(`
347
- #: bytesToSize(value) #
348
- `)(dataItem);
349
- };
350
- }
351
-
352
- function getNetworkSharedTemplate(categoryFormat = 'dd MMM HH:mm:ss') {
353
- return function(dataItem) {
354
- dataItem.kBytesToSize = _utils_utilities__WEBPACK_IMPORTED_MODULE_0__["kBytesToSize"]; // Pass kBytesToSize utility function to template
355
- return kendo.template(`
356
- <div class="chart-tooltip-category"><strong>#: kendo.toString(category, '${categoryFormat}') #</strong></div>
357
- # for (var i = 0; i < points.length; i++) { #
358
- <div>
359
- <i class="fas fa-square" style="color: #: points[i].color # "></i>
360
- #: points[i].series.name# : #: kBytesToSize(points[i].value, 1, '', '/s') #
361
- </div>
362
- # } #
363
- `)(dataItem);
364
- };
365
- }
366
-
367
- function getNetworkValueAxisLabelTemplate() {
368
- return function(dataItem) {
369
- dataItem.kBytesToSize = _utils_utilities__WEBPACK_IMPORTED_MODULE_0__["kBytesToSize"]; // Pass kBytesToSize utility function to template
370
- return kendo.template(`
371
- #: kBytesToSize(value, 1, '', '/s') #
372
- `)(dataItem);
373
- };
374
- }
375
-
376
- function getUserCountSharedTemplate(categoryFormat = 'dd MMM HH:mm:ss') {
377
- return function(dataItem) {
378
- return kendo.template(`
379
- <div class="chart-tooltip-category"><strong>#: kendo.toString(category, '${categoryFormat}') #</strong></div>
380
- # for (var i = 0; i < points.length; i++) { #
381
- <div>
382
- <i class="fas fa-square" style="color: #: points[i].color # "></i>
383
- #: points[i].series.name# : #: points[i].value #
384
- </div>
385
- # } #
386
- `)(dataItem);
387
- };
388
- }
389
-
390
- function getTimeRangeTooltipTemplate() {
391
- return function(dataItem) {
392
- dataItem.getTooltipString = function(v1, v2) {
393
- if (v1 == v2)
394
- {
395
- if (v2 == -24)
396
- v2 = -23;
397
-
398
- v1 = v2 - 1;
399
- }
400
-
401
- let start = Math.abs(v1);
402
- let end = Math.abs(v2);
403
-
404
- return `From ${start} hour${start != 1 ? 's' : ''} ago to ${end > 0 ? ('to ' + end + ' hour' + (end != 1 ? 's' : '') + ' ago') : 'now'}`;
405
- }; // Pass utility function to template
406
- return kendo.template(`
407
- #: getTooltipString(selectionStart, selectionEnd) #
408
- `)(dataItem);
409
- };
410
- }
411
-
412
- function getWRBytesLabelTemplate() {
413
- return function(dataItem) {
414
- return kendo.template(`
415
- #: value # #: dataItem.unit #
416
- `)(dataItem);
417
- };
418
- }
419
-
420
-
421
- /***/ }),
422
-
423
- /***/ "./src/components/charts/user-count-chart.js":
424
- /*!***************************************************!*\
425
- !*** ./src/components/charts/user-count-chart.js ***!
426
- \***************************************************/
427
- /*! exports provided: UserCountChart */
428
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
429
-
430
- "use strict";
431
- __webpack_require__.r(__webpack_exports__);
432
- /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UserCountChart", function() { return UserCountChart; });
433
- /* harmony import */ var _chart_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chart-utils */ "./src/components/charts/chart-utils.js");
434
- /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! moment */ "./node_modules/moment/moment.js");
435
- /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_1__);
436
-
437
-
438
-
439
- class UserCountChart extends HTMLElement
440
- {
441
- constructor()
442
- {
443
- super();
444
-
445
- // Create chart html
446
- let chartHtml = $('<div>');
447
- $(this).append(chartHtml);
448
-
449
- // Initialize chart
450
- this._chartWidget = this._initChartWidget(chartHtml);
451
- }
452
-
453
- _initChartWidget(chartHtml)
454
- {
455
- return chartHtml.kendoChart({
456
- transitions: false,
457
- chartArea: {
458
- height: 270,
459
- background: 'transparent'
460
- },
461
- legend: {
462
- visible: false
463
- },
464
- seriesDefaults: {
465
- type: 'area',
466
- labels: {
467
- visible: false,
468
- format: '{0}',
469
- background: 'transparent'
470
- },
471
- },
472
- series: [{
473
- name: 'Users',
474
- field: 'users',
475
- categoryField: 'time',
476
- color: '#fd7d24',
477
- line: {
478
- width: 2
479
- }
480
- }],
481
- valueAxis: {
482
- labels: {
483
- format: '{0}',
484
- },
485
- line: {
486
- visible: false
487
- },
488
- min: 0,
489
- },
490
- categoryAxis: {
491
- majorGridLines: {
492
- visible: true,
493
- step: 60
494
- },
495
- majorTicks: {
496
- visible: true,
497
- step: 60
498
- },
499
- labels: {
500
- visible: true,
501
- step: 60
502
- },
503
- baseUnit: 'minutes',
504
- type: 'date',
505
- },
506
- tooltip: {
507
- shared: true,
508
- visible: true,
509
- sharedTemplate: Object(_chart_utils__WEBPACK_IMPORTED_MODULE_0__["getUserCountSharedTemplate"])('dd MMM HH:mm'),
510
- background: '#e4e4e4'
511
- }
512
- }).data('kendoChart');
513
- }
514
-
515
- redraw()
516
- {
517
- this._chartWidget.redraw();
518
- }
519
-
520
- set range(values)
521
- {
522
- this._range = values;
523
-
524
- this._updateXAxisLimits();
525
- }
526
-
527
- get range()
528
- {
529
- return this._range;
530
- }
531
-
532
- addHistoryEntries(userCountData, samplingRateSeconds)
533
- {
534
- let date = new Date();
535
- let start = moment__WEBPACK_IMPORTED_MODULE_1__(date);
536
- start.subtract((samplingRateSeconds * userCountData.length), 's')
537
-
538
- let dataSource = new kendo.data.DataSource();
539
-
540
- for (let i = 1; i < userCountData.length; i++)
541
- {
542
- let date = start.clone();
543
- date.add(i * samplingRateSeconds, 's');
544
-
545
- this.addEntry(userCountData[i], dataSource, date.toDate(), false);
546
- }
547
-
548
- // Assign datasource to chart
549
- this._chartWidget.setDataSource(dataSource);
550
-
551
- // Update axis
552
- this._updateXAxisLimits();
553
- }
554
-
555
- addEntry(userCount, dataSource = null, date = null, updateAxis = true)
556
- {
557
- if (date == null)
558
- date = new Date();
559
-
560
- let newEntry = {
561
- time: date,
562
- users: userCount
563
- };
564
-
565
- // Add entry to data source
566
- if (dataSource == null)
567
- dataSource = this._dataSource;
568
-
569
- dataSource.add(newEntry);
570
-
571
- if (updateAxis)
572
- this._updateXAxisLimits();
573
- }
574
-
575
- _updateXAxisLimits()
576
- {
577
- let ds = this._dataSource;
578
-
579
- if (ds.total() > 0)
580
- {
581
- const chartMinimum = moment__WEBPACK_IMPORTED_MODULE_1__(this._lastDate).add(this._range[0], 'h');
582
- const chartMaximum = moment__WEBPACK_IMPORTED_MODULE_1__(this._lastDate).add(this._range[1], 'h');
583
-
584
- this._chartWidget.options.categoryAxis.min = chartMinimum.toDate();
585
- this._chartWidget.options.categoryAxis.max = chartMaximum.toDate();
586
-
587
- this._chartWidget.redraw();
588
- }
589
- }
590
-
591
- get _dataSource()
592
- {
593
- return this._chartWidget.dataSource;
594
- }
595
- }
596
-
597
- // DEFINE COMPONENT
598
- if (!window.customElements.get('user-count-chart'))
599
- window.customElements.define('user-count-chart', UserCountChart);
600
-
601
- /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
602
-
603
- /***/ }),
604
-
605
- /***/ "./src/components/module-specific/match-expression-builder.js":
606
- /*!********************************************************************!*\
607
- !*** ./src/components/module-specific/match-expression-builder.js ***!
608
- \********************************************************************/
609
- /*! exports provided: MatchExpressionBuilder */
610
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
611
19
 
612
- "use strict";
613
- __webpack_require__.r(__webpack_exports__);
614
- /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MatchExpressionBuilder", function() { return MatchExpressionBuilder; });
615
- class MatchExpressionBuilder extends HTMLElement
20
+ class SslCertificateManager extends HTMLElement
616
21
  {
617
22
  constructor()
618
23
  {
619
24
  super();
620
25
 
621
- this.EXPRESSION_UPDATED_EVENT = 'expressionUpdated';
622
-
623
- this._dialog = $(`
624
- <div class="modal" id="matchExprBuilderModal" tabindex="-1" role="dialog" aria-labelledby="matchExprBuilderModalTitle" aria-hidden="true">
625
- <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
26
+ this._modalHtml = `
27
+ <div class="modal" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalTitle" aria-hidden="true">
28
+ <div class="modal-dialog modal-dialog-centered" role="document">
626
29
  <div class="modal-content">
627
30
  <div class="modal-header">
628
- <h5 class="modal-title text-primary" id="matchExprBuilderModalTitle">...</h5>
31
+ <h5 class="modal-title text-primary" id="uploadModalTitle">SSL Certificate Manager</h5>
32
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
33
+ <span aria-hidden="true">&times;</span>
34
+ </button>
629
35
  </div>
630
- <div class="modal-body p-0">
631
- <form class="bg-color-default p-3">
632
- <div class="d-inline-flex align-items-center radio-button-group inline-form-groups">
633
- <div class="custom-control custom-radio custom-control-inline mr-2">
634
- <input type="radio" id="andOperatorRB" name="operatorRBG" class="custom-control-input" value="AND" checked disabled>
635
- <label class="custom-control-label" for="andOperatorRB">AND</label>
636
- </div>
637
- <div class="custom-control custom-radio custom-control-inline mr-2">
638
- <input type="radio" id="orOperatorRB" name="operatorRBG" class="custom-control-input" value="OR" disabled>
639
- <label class="custom-control-label" for="orOperatorRB">OR</label>
36
+ <div class="modal-body in-flow-invalid-msg">
37
+ <fieldset id="uploadFieldset">
38
+ <div id="uploaderSubform">
39
+ <div class="form-group">
40
+ <div class="col-form-label form-label-container">
41
+ <label for="uploader" class="form-label">Certificate keystore (jks) <i class="fas fa-question-circle text-muted help" title="SSL certificate's protected keystore file to be uploaded to the server, in jks format"></i></label>
42
+ </div>
43
+ <div class="inner-widget">
44
+ <input type="file" id="uploader" name="uploader" accept=".jks" data-upload-msg="Select a file">
45
+ <span class="k-invalid-msg position-static" data-for="uploader"></span>
46
+ </div>
640
47
  </div>
641
48
  </div>
642
- <div class="d-inline-block">
643
- <div class="inline-form-groups my-1">
644
- <input id="varNameCB"/>
49
+ <div id="passwordsSubform">
50
+ <div class="form-row">
51
+ <div class="form-group col">
52
+ <div class="col-form-label form-label-container">
53
+ <label for="ksPassword" class="form-label">Keystore password <i class="fas fa-question-circle text-muted help" title="Password used to protect the certificate keystore"></i></label>
54
+ </div>
55
+ <div class="inner-widget">
56
+ <input type="password" id="ksPassword" name="ksPassword" class="form-control k-textbox" autocomplete="off" required data-required-msg="Required" />
57
+ <span class="k-invalid-msg position-static" data-for="ksPassword"></span>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="form-group col">
62
+ <div class="col-form-label form-label-container">
63
+ <label for="confirmKsPassword" class="form-label">Confirm password <i class="fas fa-question-circle text-muted help" title="Keystore password confirmation"></i></label>
64
+ </div>
65
+ <div class="inner-widget">
66
+ <input type="password" id="confirmKsPassword" name="confirmKsPassword" class="form-control k-textbox" autocomplete="off" required data-required-msg="Required" />
67
+ <span class="k-invalid-msg position-static" data-for="confirmKsPassword"></span>
68
+ </div>
69
+ </div>
645
70
  </div>
646
- <div class="inline-form-groups my-1">
647
- <input id="typeDD" class="matchTypeDropdown"/>
648
- <input id="conditionDD" class="matchConditionDropdown"/>
71
+
72
+ <p><em>For additional security, enter again and confirm your SFS2X administration password.</em></p>
73
+
74
+ <div class="form-row">
75
+ <div class="form-group col">
76
+ <div class="col-form-label form-label-container">
77
+ <label for="adminPassword" class="form-label">Admin password <i class="fas fa-question-circle text-muted help" title="SmartFoxServer 2X remote administration password"></i></label>
78
+ </div>
79
+ <div class="inner-widget">
80
+ <input type="password" id="adminPassword" name="adminPassword" class="form-control k-textbox" autocomplete="off" required data-required-msg="Required" />
81
+ <span class="k-invalid-msg position-static" data-for="adminPassword"></span>
82
+ </div>
83
+ </div>
84
+
85
+ <div class="form-group col">
86
+ <div class="col-form-label form-label-container">
87
+ <label for="confirmAdminPassword" class="form-label">Confirm password <i class="fas fa-question-circle text-muted help" title="SmartFoxServer 2X remote administration password confirmation"></i></label>
88
+ </div>
89
+ <div class="inner-widget">
90
+ <input type="password" id="confirmAdminPassword" name="confirmAdminPassword" class="form-control k-textbox" autocomplete="off" required data-required-msg="Required" />
91
+ <span class="k-invalid-msg position-static" data-for="confirmAdminPassword"></span>
92
+ </div>
93
+ </div>
649
94
  </div>
650
95
  </div>
651
- <div class="d-inline inline-form-groups">
652
- <input id="valueIn" class="k-textbox mt-1" placeholder="Value"/>
653
- <button id="matchExprAddBt" type="button" class="k-button k-secondary mt-1"><i class="fas fa-plus mr-1"></i>Add</button>
654
- </div>
655
- </form>
656
-
657
- <div id="matchExprGrid" class="m-3"></div>
96
+ </fieldset>
658
97
  </div>
659
- <div class="modal-footer d-flex">
660
- <div class="flex-grow-1 text-left">
661
- <button id="matchExprApplyBt" type="button" class="k-button k-primary mr-2"><i class="fas fa-check-circle mr-1"></i>Apply</button>
662
- </div>
663
- <div class="flex-grow-1 text-right">
664
- <button id="matchExprRemoveBt" type="button" class="k-button k-secondary" disabled><i class="fas fa-minus-circle mr-1"></i>Remove selected</button>
665
- <button id="matchExprClearBt" type="button" class="k-button k-secondary"><i class="fas fa-times-circle mr-1"></i>Clear all</button>
98
+ <div class="modal-footer flex-column">
99
+ <div class="d-flex w-100">
100
+ <div class="flex-grow-1 text-left">
101
+ <button id="uploadSslButton" type="button" class="k-button k-primary"><i class="fas fa-upload mr-1"></i>Upload certificate</button>
102
+ <i id="uploadSpinner" class="fas fa-circle-notch fa-spin text-primary align-middle ml-1"></i>
103
+ </div>
104
+ <div class="flex-grow-1 text-right">
105
+ <button type="button" class="k-button k-secondary" data-dismiss="modal">Cancel</button>
106
+ </div>
666
107
  </div>
108
+ <div id="uploadErrorMsg" class="text-danger mt-3"></div>
667
109
  </div>
668
110
  </div>
669
111
  </div>
670
112
  </div>
671
- `);
672
-
673
- $('#matchExprAddBt', this._dialog).on('click', $.proxy(this._onAddBtClick, this));
674
- $('#matchExprApplyBt', this._dialog).on('click', $.proxy(this._onApplyBtClick, this));
675
- $('#matchExprRemoveBt', this._dialog).on('click', $.proxy(this._onRemoveBtClick, this));
676
- $('#matchExprClearBt', this._dialog).on('click', $.proxy(this._onClearBtClick, this));
677
-
678
- // INITIALIZE FORM ITEMS
679
-
680
- this._varNameCb = $('#varNameCB', this._dialog).kendoComboBox({
681
- dataTextField: 'label',
682
- dataValueField: 'value',
683
- placeholder: 'Variable name',
684
- }).data('kendoComboBox');
685
-
686
- // Set types dropdown data source
687
- // NOTE: in order to avoid XxxMatch objects to be wrapped by Kendo data source, we have to pass them using _ property names
688
- let typeDS = [];
689
-
690
- // Boolean ----------------
691
- typeDS.push({
692
- label: 'Boolean',
693
- _value: SFS2X.BoolMatch.EQUALS,
694
- conditions: [
695
- {
696
- label: SFS2X.BoolMatch.EQUALS.symbol,
697
- _value: SFS2X.BoolMatch.EQUALS,
698
- },
699
- {
700
- label: SFS2X.BoolMatch.NOT_EQUALS.symbol,
701
- _value: SFS2X.BoolMatch.NOT_EQUALS,
702
- }
703
- ]
704
- });
705
-
706
- // Number ----------------
707
- typeDS.push({
708
- label: 'Number',
709
- _value: SFS2X.NumberMatch.EQUALS,
710
- conditions: [
711
- {
712
- label: SFS2X.NumberMatch.EQUALS.symbol,
713
- _value: SFS2X.NumberMatch.EQUALS,
714
- },
715
- {
716
- label: SFS2X.NumberMatch.NOT_EQUALS.symbol,
717
- _value: SFS2X.NumberMatch.NOT_EQUALS,
718
- },
719
- {
720
- label: SFS2X.NumberMatch.GREATER_THAN.symbol,
721
- _value: SFS2X.NumberMatch.GREATER_THAN,
722
- },
723
- {
724
- label: SFS2X.NumberMatch.LESS_THAN.symbol,
725
- _value: SFS2X.NumberMatch.LESS_THAN,
726
- },
727
- {
728
- label: SFS2X.NumberMatch.GREATER_THAN_OR_EQUAL_TO.symbol,
729
- _value: SFS2X.NumberMatch.GREATER_THAN_OR_EQUAL_TO,
730
- },
731
- {
732
- label: SFS2X.NumberMatch.LESS_THAN_OR_EQUAL_TO.symbol,
733
- _value: SFS2X.NumberMatch.LESS_THAN_OR_EQUAL_TO,
734
- }
735
- ]
736
- });
737
-
738
- // String ----------------
739
- typeDS.push({
740
- label: 'String',
741
- _value: SFS2X.StringMatch.EQUALS,
742
- conditions: [
743
- {
744
- label: SFS2X.StringMatch.EQUALS.symbol,
745
- _value: SFS2X.StringMatch.EQUALS,
746
- },
747
- {
748
- label: SFS2X.StringMatch.NOT_EQUALS.symbol,
749
- _value: SFS2X.StringMatch.NOT_EQUALS,
750
- },
751
- {
752
- label: SFS2X.StringMatch.CONTAINS.symbol,
753
- _value: SFS2X.StringMatch.CONTAINS,
754
- },
755
- {
756
- label: SFS2X.StringMatch.STARTS_WITH.symbol,
757
- _value: SFS2X.StringMatch.STARTS_WITH,
758
- },
759
- {
760
- label: SFS2X.StringMatch.ENDS_WITH.symbol,
761
- _value: SFS2X.StringMatch.ENDS_WITH,
762
- }
763
- ]
764
- });
765
-
766
- this._typeDd = $('#typeDD', this._dialog).kendoDropDownList({
767
- dataSource: typeDS,
768
- dataTextField: 'label',
769
- dataValueField: '_value',
770
- optionLabel: {
771
- label: 'Type',
772
- _value: ''
773
- },
774
- optionLabelTemplate: '<span class="text-muted">#:label#</span>',
775
- change: $.proxy(function() {
776
- if (this._typeDd && this._typeDd.select())
777
- {
778
- let selectedType = this._typeDd.dataItem(this._typeDd.select());
779
- this._conditionDd.setDataSource(selectedType.conditions);
780
- this._conditionDd.select(1);
781
- }
782
- }, this)
783
- }).data('kendoDropDownList');
784
-
785
- this._conditionDd = $('#conditionDD', this._dialog).kendoDropDownList({
786
- dataTextField: 'label',
787
- dataValueField: '_value',
788
- optionLabel: {
789
- label: 'Condition',
790
- _value: ''
791
- },
792
- optionLabelTemplate: '<span class="text-muted">#:label#</span>',
793
- }).data('kendoDropDownList');
794
-
795
- this._valueIn = $('#valueIn', this._dialog);
796
-
797
- // INITIALIZE GRID
798
-
799
- this._grid = $('#matchExprGrid', this._dialog).kendoGrid({
800
- dataSource: [],
801
- resizable: true,
802
- selectable: 'row',
803
- change: $.proxy(this._onGridSelectionChange, this),
804
- noRecords: {
805
- template: 'No entries.'
806
- },
807
- columns: [
808
- {
809
- field: 'operator',
810
- title: 'Operator',
811
- width: 90
812
- },
813
- {
814
- field: 'label',
815
- title: 'Name',
816
- width: 120
817
- },
818
- {
819
- field: 'typeLabel',
820
- title: 'Type',
821
- width: 85
822
- },
823
- {
824
- field: 'symbol',
825
- title: 'Condition',
826
- width: 95
827
- },
828
- {
829
- field: 'value',
830
- title: 'Value',
831
- width: 200
832
- },
833
- ]
834
- }).data('kendoGrid');
835
- }
836
-
837
- get title()
838
- {
839
- return $('#matchExprBuilderModalTitle', this._dialog).text();
840
- }
841
-
842
- set title(value)
843
- {
844
- $('#matchExprBuilderModalTitle', this._dialog).text(value);
845
- }
846
-
847
- get predefinedVarNames()
848
- {
849
- return this._predefinedVarNames;
850
- }
851
-
852
- set predefinedVarNames(value)
853
- {
854
- this._predefinedVarNames = value;
855
-
856
- this._varNameCb.setDataSource(value);
857
- }
858
-
859
- get filterExpression()
860
- {
861
- return this._filterExpression;
862
- }
863
-
864
- show()
865
- {
866
- this._dialog.modal({
867
- backdrop: 'static',
868
- keyboard: false,
869
- show: true
870
- });
871
- }
872
-
873
- _onGridSelectionChange()
874
- {
875
- let selectedRows = this._grid.select();
876
- let selectedDataItems = [];
877
-
878
- for (let i = 0; i < selectedRows.length; i++)
879
- {
880
- let dataItem = this._grid.dataItem(selectedRows[i]);
881
- selectedDataItems.push(dataItem);
882
- }
883
-
884
- // Enable/disable remove button
885
- $('#matchExprRemoveBt', this._dialog).attr('disabled', selectedDataItems.length == 0);
886
- }
887
-
888
- _onAddBtClick()
889
- {
890
- if (this._varNameCb.value() != '' && this._typeDd.select())
891
- {
892
- // Variable name and label
893
- let varName, varLabel;
894
-
895
- if (this._varNameCb.select() > -1)
896
- {
897
- let dataItem = this._varNameCb.dataItem(this._varNameCb.select());
898
- varName = dataItem.value;
899
- varLabel = dataItem.label;
900
- }
901
- else
902
- varName = varLabel = this._varNameCb.value();
903
-
904
- // Variable type's label
905
- let typeLabel = this._typeDd.dataItem(this._typeDd.select()).label;
906
-
907
- // Match condition
908
- let condition = this._conditionDd.dataItem(this._conditionDd.select())._value;
909
-
910
- // Expression operator
911
- let operator = this._grid.dataSource.total() > 0 ? $('input[name=operatorRBG]:checked', this._dialog).val() : null;
912
-
913
- // Variable value
914
- let val = this._valueIn.val();
915
- let varValue;
916
-
917
- if (condition instanceof SFS2X.BoolMatch)
918
- varValue = (val == 'true' ? true : false);
919
- else if (condition instanceof SFS2X.NumberMatch)
920
- varValue = (isNaN(Number(val)) ? 0 : Number(val));
921
- else
922
- varValue = val;
923
-
924
- // Add item to grid
925
- let exprPart = this._getExpressionPart(varLabel, varName, varValue, typeLabel, condition, operator);
926
- this._grid.dataSource.add(exprPart);
927
-
928
- // Reset inputs
929
- this._varNameCb.value('');
930
- this._valueIn.val('');
931
-
932
- // Enable operator radio buttons
933
- this._enableOperatorRb(true);
934
- }
935
- }
936
-
937
- _onApplyBtClick()
938
- {
939
- // Hide dialog
940
- this._dialog.modal('hide');
941
-
942
- // Clear grid selection
943
- this._grid.clearSelection();
944
-
945
- // Build final match expression
946
- this._filterExpression = null;
947
-
948
- if (this._grid.dataSource.total() > 0)
949
- {
950
- let expPart = this._grid.dataSource.at(0);
951
- this._filterExpression = new SFS2X.MatchExpression(expPart.name, expPart._condition, expPart.value);
952
-
953
- for (let i = 1; i < this._grid.dataSource.total(); i++)
954
- {
955
- expPart = this._grid.dataSource.at(i);
956
-
957
- if (expPart.operator == SFS2X.LogicOperator.AND.id)
958
- this._filterExpression = this._filterExpression.and(expPart.name, expPart._condition, expPart.value);
959
- else if (expPart.operator == SFS2X.LogicOperator.OR.id)
960
- this._filterExpression = this._filterExpression.or(expPart.name, expPart._condition, expPart.value);
961
- }
962
- }
963
-
964
- // Fire event
965
- let evt = new CustomEvent(this.EXPRESSION_UPDATED_EVENT, {
966
- detail: null,
967
- bubbles: false,
968
- cancelable: false
969
- });
970
-
971
- this.dispatchEvent(evt);
972
- }
973
-
974
- _onRemoveBtClick()
975
- {
976
- let dataItem = this._grid.dataItem(this._grid.select());
977
- this._grid.dataSource.remove(dataItem);
978
-
979
- // Clear grid selection
980
- this._grid.clearSelection();
981
-
982
- if (this._grid.dataSource.total() == 0)
983
- {
984
- // Disable operator radio buttons
985
- this._enableOperatorRb(false);
986
- }
987
- else
988
- {
989
- // Reset operator on first entry
990
- this._grid.dataSource.at(0).operator = null;
991
- this._grid.refresh();
992
- }
993
- }
994
-
995
- _onClearBtClick()
996
- {
997
- // Reset form
998
- $('#andOperatorRB', this._dialog).attr('checked', true);
999
- $('#orOperatorRB', this._dialog).attr('checked', false);
1000
- this._enableOperatorRb(false);
1001
-
1002
- this._varNameCb.value('');
1003
- this._typeDd.select(0);
1004
- this._conditionDd.select(0);
1005
-
1006
- this._valueIn.val('');
1007
-
1008
- // Clear grid selection
1009
- this._grid.clearSelection();
1010
-
1011
- // Clear table
1012
- this._grid.setDataSource([]);
1013
- }
1014
-
1015
- _getExpressionPart(varLabel, varName, varValue, typeLabel, condition, operator)
1016
- {
1017
- return {
1018
- 'label': varLabel,
1019
- 'name': varName,
1020
- 'value': varValue,
1021
- 'typeLabel': typeLabel,
1022
- 'symbol': condition.symbol,
1023
- 'operator': operator,
1024
- '_condition': condition,
1025
- };
1026
- }
1027
-
1028
- _enableOperatorRb(enable)
1029
- {
1030
- $('#andOperatorRB', this._dialog).attr('disabled', !enable);
1031
- $('#orOperatorRB', this._dialog).attr('disabled', !enable);
1032
- }
1033
- }
1034
-
1035
- // DEFINE COMPONENT
1036
- if (!window.customElements.get('match-expression-builder'))
1037
- window.customElements.define('match-expression-builder', MatchExpressionBuilder);
1038
-
1039
- /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
1040
-
1041
- /***/ }),
1042
-
1043
- /***/ "./src/components/sidebar-layout.js":
1044
- /*!******************************************!*\
1045
- !*** ./src/components/sidebar-layout.js ***!
1046
- \******************************************/
1047
- /*! exports provided: SidebarLayout */
1048
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1049
-
1050
- "use strict";
1051
- __webpack_require__.r(__webpack_exports__);
1052
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SidebarLayout", function() { return SidebarLayout; });
1053
- class SidebarLayout extends HTMLElement
1054
- {
1055
- constructor()
1056
- {
1057
- super();
1058
-
1059
- // Attach a shadow root
1060
- const shadowRoot = this.attachShadow({mode: 'open'});
1061
- shadowRoot.innerHTML = `
1062
- <style>
1063
- :host {
1064
- display: flex;
1065
- flex-direction: row;
1066
- }
1067
-
1068
- @media (max-width: 575.98px) {
1069
- :host(.split-xs) ::slotted(:not([aria-selected="true"])) {
1070
- display: none !important;
1071
- }
1072
-
1073
- :host(.split-xs) ::slotted([aria-selected="true"]) {
1074
- flex-grow: 1;
1075
- }
1076
- }
1077
-
1078
- @media (max-width: 767.98px) {
1079
- :host(.split-sm) ::slotted(:not([aria-selected="true"])) {
1080
- display: none !important;
1081
- }
1082
-
1083
- :host(.split-sm) ::slotted([aria-selected="true"]) {
1084
- flex-grow: 1;
1085
- }
1086
- }
1087
-
1088
- @media (max-width: 991.98px) {
1089
- :host(.split-md) ::slotted(:not([aria-selected="true"])) {
1090
- display: none !important;
1091
- }
1092
-
1093
- :host(.split-md) ::slotted([aria-selected="true"]) {
1094
- flex-grow: 1;
1095
- }
1096
- }
1097
-
1098
- @media (max-width: 1199.98px) {
1099
- :host(.split-lg) ::slotted(:not([aria-selected="true"])) {
1100
- display: none !important;
1101
- }
1102
-
1103
- :host(.split-lg) ::slotted([aria-selected="true"]) {
1104
- flex-grow: 1;
1105
- }
1106
- }
1107
-
1108
- .side-col::slotted(*) {
1109
- }
1110
-
1111
- .main-col::slotted(*) {
1112
- flex-grow: 1;
1113
- }
1114
- </style>
1115
-
1116
- <slot class="side-col" name="side-column"></slot>
1117
- <slot class="main-col" name="main-column"></slot>
1118
113
  `;
1119
114
 
1120
- // Set initial selection
1121
- this.selectedIndex = 0;
1122
- }
1123
-
1124
- get selectedPanel()
1125
- {
1126
- return this._selectedPanel;
1127
- }
1128
-
1129
- set selectedPanel(element) // 'side' or 'main'
1130
- {
1131
- if (element != null && element.parentNode == this)
1132
- {
1133
- this._selectedPanel = element;
1134
-
1135
- for (let element of this.children)
1136
- {
1137
- if (element == this._selectedPanel)
1138
- element.setAttribute('aria-selected', 'true');
1139
- else
1140
- element.removeAttribute('aria-selected');
1141
- }
1142
- }
1143
- else
1144
- {
1145
- console.error('Element is not a child of SidebarLayout');
1146
- }
1147
- }
1148
-
1149
- get selectedIndex()
1150
- {
1151
- return Array.from(this.children).indexOf(this._selectedPanel);
1152
- }
1153
-
1154
- set selectedIndex(index)
1155
- {
1156
- if (this.children.length > 0)
1157
- {
1158
- if (this.children[index] == null)
1159
- {
1160
- console.error('Invalid SidebarLayout index');
1161
- return;
1162
- }
1163
-
1164
- let element = this.children[index];
1165
- this.selectedPanel = element;
1166
- }
1167
- }
1168
- }
1169
-
1170
- // DEFINE COMPONENT
1171
- if (!window.customElements.get('sidebar-layout'))
1172
- window.customElements.define('sidebar-layout', SidebarLayout);
1173
-
1174
-
1175
- /***/ }),
1176
-
1177
- /***/ "./src/modules/zone-monitor.js":
1178
- /*!*************************************!*\
1179
- !*** ./src/modules/zone-monitor.js ***!
1180
- \*************************************/
1181
- /*! exports provided: default */
1182
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1183
-
1184
- "use strict";
1185
- __webpack_require__.r(__webpack_exports__);
1186
- /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ZoneMonitor; });
1187
- /* harmony import */ var _base_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base-module */ "./src/modules/base-module.js");
1188
- /* harmony import */ var _components_sidebar_layout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/sidebar-layout */ "./src/components/sidebar-layout.js");
1189
- /* harmony import */ var _components_module_specific_match_expression_builder__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/module-specific/match-expression-builder */ "./src/components/module-specific/match-expression-builder.js");
1190
- /* 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");
1191
- /* harmony import */ var _utils_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/utilities */ "./src/utils/utilities.js");
1192
- /* harmony import */ var _utils_match_properties__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utils/match-properties */ "./src/utils/match-properties.js");
1193
- /* harmony import */ var _components_charts_user_count_chart__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../components/charts/user-count-chart */ "./src/components/charts/user-count-chart.js");
1194
- /* harmony import */ var _components_charts_chart_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../components/charts/chart-utils */ "./src/components/charts/chart-utils.js");
1195
-
1196
-
1197
-
1198
-
1199
-
1200
-
1201
-
1202
-
1203
-
1204
- class ZoneMonitor extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseModule"]
1205
- {
1206
- constructor()
1207
- {
1208
- super('zoneMonitor');
1209
-
1210
- this.MONITORED_TYPE_ZONE = 'zone';
1211
- this.MONITORED_TYPE_ROOM = 'room';
1212
- this.MONITORED_TYPE_USER = 'user';
1213
-
1214
- this.ANY_LABEL = '[any]';
1215
- this.DEFAULT_GROUP_NAME = 'default';
1216
- this.MAX_EXTENSION_LOG_SIZE = 300;
1217
- this.KICK_BAN_DEFAULT_DELAY = 5;
1218
-
1219
- // Outgoing requests
1220
- this.REQ_GET_DATA = 'getData';
1221
- this.REQ_SET_ZONE_SETTINGS = 'setZoneSettings';
1222
- this.REQ_SET_ROOM_SETTINGS = 'setRoomSettings';
1223
- this.REQ_SET_USER_SETTINGS = 'setUserSettings';
1224
- this.REQ_RELOAD_ZONE_EXT = 'reloadZoneExt';
1225
- this.REQ_RELOAD_WORDS = 'reloadWords';
1226
- this.REQ_DISCONNECT_USER = 'disconnUser';
1227
- this.REQ_KICK_USER = 'kickUser';
1228
- this.REQ_BAN_USER = 'banUser';
1229
- this.REQ_GET_ROOM_CONFIG = 'getRoomConfig';
1230
- this.REQ_CREATE_ROOM = 'createRoom';
1231
- this.REQ_REMOVE_ROOM = 'removeRoom';
1232
- this.REQ_ADMIN_MSG = 'adminMsg';
1233
-
1234
- // Incoming responses
1235
- this.RESP_DATA = 'data';
1236
- this.RESP_UNEXPECTED_ERROR = 'unexpError';
1237
- this.RESP_UPDATE_ERROR = 'updateError';
1238
- this.RESP_ROOM_CONFIG = 'roomConfig';
1239
- this.RESP_ROOM_CONFIG_ERROR = 'roomConfigErr';
1240
- this.RESP_ROOM_CREATED = 'roomCreated';
1241
- this.RESP_ROOM_CREATION_ERROR = 'roomError';
1242
- this.RESP_LOG_MESSAGES = 'log'; // This response doesn't have a corresponding request because it is managed by a server event listener
1243
-
1244
- this._currentRequestId = -1;
1245
- }
1246
-
1247
- //------------------------------------
1248
- // COMMON MODULE INTERFACE METHODS
1249
- // This members are used by the main controller
1250
- // to communicate with the module's controller.
1251
- //------------------------------------
1252
-
1253
- initialize(idData, shellController)
1254
- {
1255
- // Call super method
1256
- super.initialize(idData, shellController);
1257
-
1258
- // Set monitoring interface
1259
- this._setMonitoringInterface();
1260
-
1261
- // Initialize progress bar
1262
- $('#znm-progressBar').kendoProgressBar({
1263
- min: 0,
1264
- max: 100,
1265
- value: false,
1266
- type: 'value',
1267
- animation: {
1268
- duration: 400
1269
- }
1270
- });
1271
-
1272
- // LISTS
1273
-
1274
- // Initialize interval dropdown
1275
- this._intervalDropDown = $('#znm-intervalDD').kendoDropDownList({
1276
- valueTemplate: '<span class="text-muted pr-1">Interval:</span><span>#:data.text#</span>',
1277
- change: $.proxy(this._onUpdateIntervalChange, this)
1278
- }).data('kendoDropDownList');
1279
-
1280
- // Initialize pane bar
1281
- this._accordion = $('#znm-panelbar').kendoPanelBar({
1282
- expandMode: 'single',
1283
- change: $.proxy(this._onEntitiesPanelChange, this)
1284
- }).data('kendoPanelBar');
1285
-
1286
- // Initialize lists
1287
- this._zoneListBox = $("#znm-zoneList").kendoListBox({
1288
- template: '<div>#:data.name# (#:data.users#)</div>',
1289
- change: $.proxy(this._onZoneSelected, this)
1290
- }).data('kendoListBox');
1291
-
1292
- this._zoneListBox.wrapper.find('.k-list').on('dblclick', '.k-item', $.proxy(this._onMonitorSelectionBtClick, this));
1293
-
1294
- this._roomListBox = $("#znm-roomList").kendoListBox({
1295
- template: '<div>#:data.name# (#:data.users#)</div>',
1296
- change: $.proxy(this._onRoomSelected, this)
1297
- }).data('kendoListBox');
1298
-
1299
- this._roomListBox.wrapper.find('.k-list').on('dblclick', '.k-item', $.proxy(this._onMonitorSelectionBtClick, this));
1300
-
1301
- this._userListBox = $("#znm-userList").kendoListBox({
1302
- template: '<div>#:data.name#</div>',
1303
- change: $.proxy(this._onUserSelected, this)
1304
- }).data('kendoListBox');
1305
-
1306
- this._userListBox.wrapper.find('.k-list').on('dblclick', '.k-item', $.proxy(this._onMonitorSelectionBtClick, this));
1307
-
1308
- // Initialize Room Groups dropdown
1309
- this._groupsDropDown = $('#znm-roomGroupsDD').kendoDropDownList({
1310
- dataTextField: 'label',
1311
- dataValueField: 'name',
1312
- change: $.proxy(this._onGroupChange, this)
1313
- }).data('kendoDropDownList');
1314
-
1315
- // Build initial groups list
1316
- this._setGroupsDataProvider(null);
1317
-
1318
- // // Create interface builder instances
1319
- this._interfaceBuilder = new _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_3__["ConfigInterfaceBuilder"]();
1320
- this._roomCreationIBuilder = new _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_3__["ConfigInterfaceBuilder"]();
1321
-
1322
- // Add listener to show limit warning tooltips
1323
- $('#znm-panelbar').kendoTooltip({
1324
- filter: 'i[title].limit-warning',
1325
- position: 'right',
1326
- width: '250px',
1327
- content: function(e) {
1328
- return `<div class="help-tooltip">${e.target.data('title')}</div>`;
1329
- }
1330
- });
1331
-
1332
- // ROOM CREATION PANEL
1333
-
1334
- // Add click listener to Create & Remove Room buttons
1335
- $('#znm-createRoomBt').on('click', $.proxy(this._onCreateRoomBtClick, this));
1336
- $('#znm-removeRoomBt').on('click', $.proxy(this._onRemoveRoomBtClick, this));
1337
-
1338
- // Configure room creation panel
1339
- $('#znm-createRoomModal').modal({
1340
- backdrop: 'static',
1341
- keyboard: false,
1342
- show: false,
1343
- });
1344
-
1345
- // Add listener for room creation panel's create button click
1346
- $('#znm-roomCreatorCreateBt').on('click', $.proxy(this._onRoomCreatorCreateBtClick, this));
1347
-
1348
- // Add listener for room creation panel hide
1349
- $('#znm-createRoomModal').on('hidden.bs.modal', $.proxy(this._onCreateRoomModalHidden, this));
1350
-
1351
- // ROOM & USER FILTERING PANELS
1352
-
1353
- // Setup match expression builder panel for rooms
1354
- this._roomFilter = document.getElementById('znm-roomFilter');
1355
- this._roomFilter.title = 'Room Filter Expression Builder';
1356
- this._roomFilter.predefinedVarNames = _utils_match_properties__WEBPACK_IMPORTED_MODULE_5__["RoomPropertiesData"].propertiesArray;
1357
- $(this._roomFilter).on(this._roomFilter.EXPRESSION_UPDATED_EVENT, $.proxy(this._onRoomFilterUpdated, this));
1358
-
1359
- $('#znm-filterRoomBt').on('click', $.proxy(this._onShowRoomFilterBtClick, this));
1360
- $('#znm-applyRoomFilterCB').on('change', $.proxy(this._onApplyBtChange, this));
1361
-
1362
- // Setup match expression builder panel for users
1363
- this._userFilter = document.getElementById('znm-userFilter');
1364
- this._userFilter.title = 'User Filter Expression Builder';
1365
- this._userFilter.predefinedVarNames = _utils_match_properties__WEBPACK_IMPORTED_MODULE_5__["UserPropertiesData"].propertiesArray;
1366
- $(this._userFilter).on(this._userFilter.EXPRESSION_UPDATED_EVENT, $.proxy(this._onUserFilterUpdated, this));
1367
-
1368
- $('#znm-filterUserBt').on('click', $.proxy(this._onShowUserFilterBtClick, this));
1369
- $('#znm-applyUserFilterCB').on('change', $.proxy(this._onApplyBtChange, this));
1370
-
1371
- // MAIN CONTROLS
1372
-
1373
- $('#znm-monitorBt').on('click', $.proxy(this._onMonitorSelectionBtClick, this));
1374
- $('#znm-sendMessageBt').on('click', $.proxy(this._onSendAdminMsgBtClick, this));
1375
- $('#znm-messageIn').on('keyup', $.proxy(this._onSendAdminMsgInKeyUp, this));
1376
-
1377
- // MONITORING CONTROLS
1378
-
1379
- $('#znm-closeMonitorBt').on('click', $.proxy(this._onCloseMonitorBtClick, this));
1380
- $('#znm-editBt').on('click', $.proxy(this._onEditBtClick, this));
1381
- $('#znm-cancelBt').on('click', $.proxy(this._onCancelBtClick, this));
1382
- $('#znm-submitBt').on('click', $.proxy(this._onSubmitBtClick, this));
1383
-
1384
- // Hide edit controls
1385
- $('#znm-editControls').hide();
1386
-
1387
- // ZONE MONITOR ---------------------
1388
-
1389
- // Set reference to user count chart
1390
- this._userCountChart = document.getElementById('znm-userCountChart');
1391
-
1392
- // Initialize time range slider
1393
- this._timeSlider = $('#znm-timeSlider').kendoRangeSlider({
1394
- min: -24,
1395
- max: 0,
1396
- smallStep: 1,
1397
- largeStep: 0,
1398
- change: $.proxy(this._onTimeRangeChange, this),
1399
- tooltip: {
1400
- template: Object(_components_charts_chart_utils__WEBPACK_IMPORTED_MODULE_7__["getTimeRangeTooltipTemplate"])()
1401
- },
1402
- leftDragHandleTitle: 'Drag',
1403
- rightDragHandleTitle: 'Drag',
1404
- }).data('kendoRangeSlider');
1405
-
1406
- // Reset time range on user count chart
1407
- this._onTimeRangeChange();
1408
-
1409
- // Add button click listeners
1410
- $('#znm-reloadWordsBt').on('click', $.proxy(this._onWordsReloadBtClick, this));
1411
- $('#znm-reloadZoneExtBt').on('click', $.proxy(this._onZoneExtReloadBtClick, this));
1412
-
1413
- // Initialize extension log grid
1414
- this._zoneExtLogGrid = $('#znm-zoneExtLogGrid').kendoGrid({
1415
- dataSource: [],
1416
- resizable: true,
1417
- selectable: false,
1418
- noRecords: {
1419
- template: 'No entries.'
1420
- },
1421
- columns: [
1422
- {
1423
- field: 'timestamp',
1424
- title: 'Timestamp',
1425
- width: 180
1426
- },
1427
- {
1428
- field: 'level',
1429
- title: 'Level',
1430
- width: 100
1431
- },
1432
- {
1433
- field: 'message',
1434
- title: 'Message',
1435
- width: 400
1436
- },
1437
- ],
1438
- dataBound: function() {
1439
- this.content.scrollTop(this.tbody.height()); // Scroll to bottom
1440
- }
1441
- }).data('kendoGrid');
1442
-
1443
- // ROOM MONITOR ---------------------
1444
-
1445
- // Initialize extension log grid
1446
- this._roomExtLogGrid = $('#znm-roomExtLogGrid').kendoGrid({
1447
- dataSource: [],
1448
- resizable: true,
1449
- selectable: false,
1450
- noRecords: {
1451
- template: 'No entries.'
1452
- },
1453
- columns: [
1454
- {
1455
- field: 'timestamp',
1456
- title: 'Timestamp',
1457
- width: 180
1458
- },
1459
- {
1460
- field: 'level',
1461
- title: 'Level',
1462
- width: 100
1463
- },
1464
- {
1465
- field: 'message',
1466
- title: 'Message',
1467
- width: 400
1468
- },
1469
- ],
1470
- dataBound: function() {
1471
- this.content.scrollTop(this.tbody.height()); // Scroll to bottom
1472
- }
1473
- }).data('kendoGrid');
1474
-
1475
- // USER MONITOR ---------------------
1476
-
1477
- this._kickDelayIn = $('#znm-kickDelayNS').kendoNumericTextBox({
1478
- min: 0,
1479
- step: 1,
1480
- format: '#',
1481
- placeholder: 'Delay (s)'
1482
- }).data('kendoNumericTextBox');
1483
-
1484
- this._banModeDd = $('#znm-banModeDD').kendoDropDownList({
1485
- dataSource: ['IP', 'NAME'],
1486
- autoWidth: true,
1487
- optionLabel: {
1488
- text: 'Mode'
1489
- },
1490
- optionLabelTemplate: '<span class="text-muted">#:text#</span>',
1491
- }).data('kendoDropDownList');
1492
-
1493
- this._banDurUnitDd = $('#znm-banDurationUnitDD').kendoDropDownList({
1494
- autoWidth: true,
1495
- }).data('kendoDropDownList');
1496
-
1497
- this._banDurationIn = $('#znm-banDurationNS').kendoNumericTextBox({
1498
- min: 1,
1499
- step: 1,
1500
- format: '#',
1501
- placeholder: 'Duration'
1502
- }).data('kendoNumericTextBox');
1503
-
1504
- this._banDelayIn = $('#znm-banDelayNS').kendoNumericTextBox({
1505
- min: 0,
1506
- step: 1,
1507
- format: '#',
1508
- placeholder: 'Delay (s)'
1509
- }).data('kendoNumericTextBox');
1510
-
1511
- // Add button click listeners
1512
- $('#znm-disconnectBt').on('click', $.proxy(this._onDisconnectBtClick, this));
1513
- $('#znm-kickBt').on('click', $.proxy(this._onKickBtClick, this));
1514
- $('#znm-banBt').on('click', $.proxy(this._onBanBtClick, this));
1515
-
1516
- // Charts
1517
-
1518
- this._packetQueueChart = $('#znm-packetQueueChart').kendoChart({
1519
- transitions: false,
1520
- chartArea: {
1521
- height: 100
1522
- },
1523
- legend: {
1524
- visible: false
1525
- },
1526
- seriesDefaults: {
1527
- type: 'bar',
1528
- labels: {
1529
- visible: true,
1530
- format: '{0}%',
1531
- background: 'transparent',
1532
- position: 'insideBase'
1533
- },
1534
- gap: .5,
1535
- spacing: 0,
1536
- overlay: {
1537
- gradient: 'none'
1538
- },
1539
- border: {
1540
- width: 0
1541
- }
1542
- },
1543
- series: [
1544
- {
1545
- name: 'Packet queue',
1546
- field: 'value',
1547
- categoryField: 'category',
1548
- color: '#FB7D33'
1549
- }
1550
- ],
1551
- valueAxis: {
1552
- labels: {
1553
- format: '{0}%'
1554
- },
1555
- line: {
1556
- visible: false
1557
- },
1558
- min: 0,
1559
- max: 100
1560
- },
1561
- categoryAxis: {
1562
- majorGridLines: {
1563
- visible: false
1564
- },
1565
- majorTicks: {
1566
- visible: false
1567
- },
1568
- labels: {
1569
- visible: false
1570
- }
1571
- },
1572
- tooltip: {
1573
- visible: false,
1574
- }
1575
- }).data('kendoChart');
1576
-
1577
- this._droppedMsgChart = $('#znm-droppedMsgChart').kendoChart({
1578
- transitions: false,
1579
- chartArea: {
1580
- height: 100
1581
- },
1582
- legend: {
1583
- visible: false
1584
- },
1585
- seriesDefaults: {
1586
- type: 'bar',
1587
- labels: {
1588
- visible: true,
1589
- format: '{0}',
1590
- background: 'transparent',
1591
- position: 'insideBase',
1592
- },
1593
- gap: .5,
1594
- spacing: 0,
1595
- overlay: {
1596
- gradient: 'none'
1597
- },
1598
- border: {
1599
- width: 0
1600
- }
1601
- },
1602
- series: [
1603
- {
1604
- name: 'Dropped messages',
1605
- field: 'value',
1606
- categoryField: 'category',
1607
- color: '#FB7D33'
1608
- }
1609
- ],
1610
- valueAxis: {
1611
- labels: {
1612
- format: '{0}'
1613
- },
1614
- line: {
1615
- visible: false
1616
- },
1617
- min: 0,
1618
- },
1619
- categoryAxis: {
1620
- majorGridLines: {
1621
- visible: false
1622
- },
1623
- majorTicks: {
1624
- visible: false
1625
- },
1626
- labels: {
1627
- visible: false
1628
- }
1629
- },
1630
- tooltip: {
1631
- visible: false,
1632
- }
1633
- }).data('kendoChart');
1634
-
1635
- this._writtenDataChart = $('#znm-writtenDataChart').kendoChart({
1636
- transitions: false,
1637
- chartArea: {
1638
- height: 100
1639
- },
1640
- legend: {
1641
- visible: false
1642
- },
1643
- seriesDefaults: {
1644
- type: 'bar',
1645
- labels: {
1646
- visible: true,
1647
- format: '{0}',
1648
- background: 'transparent',
1649
- position: 'insideBase',
1650
- template: Object(_components_charts_chart_utils__WEBPACK_IMPORTED_MODULE_7__["getWRBytesLabelTemplate"])(),
1651
- },
1652
- gap: .5,
1653
- spacing: 0,
1654
- overlay: {
1655
- gradient: 'none'
1656
- },
1657
- border: {
1658
- width: 0
1659
- }
1660
- },
1661
- series: [
1662
- {
1663
- name: 'Written data',
1664
- field: 'value',
1665
- categoryField: 'category',
1666
- color: '#FB7D33'
1667
- }
1668
- ],
1669
- valueAxis: {
1670
- labels: {
1671
- format: '{0}'
1672
- },
1673
- line: {
1674
- visible: false
1675
- },
1676
- min: 0,
1677
- },
1678
- categoryAxis: {
1679
- majorGridLines: {
1680
- visible: false
1681
- },
1682
- majorTicks: {
1683
- visible: false
1684
- },
1685
- labels: {
1686
- visible: false
1687
- }
1688
- },
1689
- tooltip: {
1690
- visible: false,
1691
- }
1692
- }).data('kendoChart');
1693
-
1694
- this._readDataChart = $('#znm-readDataChart').kendoChart({
1695
- transitions: false,
1696
- chartArea: {
1697
- height: 100
1698
- },
1699
- legend: {
1700
- visible: false
1701
- },
1702
- seriesDefaults: {
1703
- type: 'bar',
1704
- labels: {
1705
- visible: true,
1706
- format: '{0}',
1707
- background: 'transparent',
1708
- position: 'insideBase',
1709
- template: Object(_components_charts_chart_utils__WEBPACK_IMPORTED_MODULE_7__["getWRBytesLabelTemplate"])(),
1710
- },
1711
- gap: .5,
1712
- spacing: 0,
1713
- overlay: {
1714
- gradient: 'none'
1715
- },
1716
- border: {
1717
- width: 0
1718
- }
1719
- },
1720
- series: [
1721
- {
1722
- name: 'Read data',
1723
- field: 'value',
1724
- categoryField: 'category',
1725
- color: '#FB7D33'
1726
- }
1727
- ],
1728
- valueAxis: {
1729
- labels: {
1730
- format: '{0}'
1731
- },
1732
- line: {
1733
- visible: false
1734
- },
1735
- min: 0,
1736
- },
1737
- categoryAxis: {
1738
- majorGridLines: {
1739
- visible: false
1740
- },
1741
- majorTicks: {
1742
- visible: false
1743
- },
1744
- labels: {
1745
- visible: false
1746
- }
1747
- },
1748
- tooltip: {
1749
- visible: false,
1750
- }
1751
- }).data('kendoChart');
1752
-
1753
- //-------------------------------
1754
-
1755
- // Add listener to redraw all charts in case of window resize
1756
- $(window).on('resize', $.proxy(this._onWindowResize, this));
1757
- this._onWindowResize(); // Also do it immediately
1758
-
1759
- // Request data to server
1760
- this._requestData(true);
1761
- }
1762
-
1763
- destroy()
1764
- {
1765
- // Call super method
1766
- super.destroy();
1767
-
1768
- // Close monitoring
1769
- this._onCloseMonitorBtClick();
1770
-
1771
- // Remove doubleclick event listeners
1772
- this._zoneListBox.wrapper.find('.k-list').off('dblclick');
1773
- this._roomListBox.wrapper.find('.k-list').off('dblclick');
1774
- this._userListBox.wrapper.find('.k-list').off('dblclick');
1775
-
1776
- // Remove other event listeners
1777
- $('#znm-createRoomBt').off('click');
1778
- $('#znm-removeRoomBt').off('click');
1779
- $('#znm-roomCreatorCreateBt').off('click');
1780
- $('#znm-filterRoomBt').off('click');
1781
- $('#znm-applyRoomFilterCB').off('change');
1782
- $('#znm-filterUserBt').off('click');
1783
- $('#znm-applyUserFilterCB').off('change');
1784
- $('#znm-monitorBt').off('click');
1785
- $('#znm-sendMessageBt').off('click');
1786
- $('#znm-messageIn').off('keyup');
1787
- $('#znm-closeMonitorBt').off('click');
1788
- $('#znm-editBt').off('click');
1789
- $('#znm-cancelBt').off('click');
1790
- $('#znm-submitBt').off('click');
1791
- $(window).off('resize');
1792
- $('#znm-reloadWordsBt').off('click');
1793
- $('#znm-reloadZoneExtBt').off('click');
1794
- $('#znm-disconnectBt').off('click');
1795
- $('#znm-kickBt').off('click');
1796
- $('#znm-banBt').off('click');
1797
-
1798
- // Clear request scheduling
1799
- clearTimeout(this._requestTimer);
1800
-
1801
- // Hide room creation panel
1802
- $('#znm-createRoomModal').modal('hide');
1803
-
1804
- // Remove room creation panel listener
1805
- $('#znm-createRoomModal').off('hidden.bs.modal');
1806
-
1807
- // Remove filter panels listeners
1808
- $(this._roomFilter).off(this._roomFilter.EXPRESSION_UPDATED_EVENT);
1809
- $(this._userFilter).off(this._userFilter.EXPRESSION_UPDATED_EVENT);
1810
- }
1811
-
1812
- onExtensionCommand(command, data)
1813
- {
1814
- // Data received
1815
- if (command == this.RESP_DATA)
1816
- {
1817
- // We have to check if the response id corresponds to the current request id;
1818
- // if not, the response is discarded as it refers to an outdatet request
1819
- const responseId = data.getInt('id');
1820
-
1821
- if (responseId == this._currentRequestId)
1822
- {
1823
- // --- ZONES LIST ---
1824
- if (data.containsKey('zones'))
1825
- {
1826
- this._setZonesDataProvider(data.getSFSArray('zones'));
1827
-
1828
- if (this._selectedZone)
1829
- this._setScopeLabel();
1830
- }
1831
-
1832
- // --- ROOMS LIST ---
1833
- if (data.containsKey('rooms'))
1834
- {
1835
- this._setGroupsDataProvider(data.getUtfStringArray('groups'));
1836
-
1837
- // -------------------------------
1838
-
1839
- // If a null rooms list is returned, the zone must have been deleted!
1840
- if (!data.isNull('rooms'))
1841
- {
1842
- this._setRoomsDataProvider(data.getSFSArray('rooms'));
1843
-
1844
- if (!this._selectedRoom)
1845
- this._setScopeLabel();
1846
-
1847
- // -------------------------------
1848
-
1849
- // Show warning if the rooms list exceeds the server-side limit
1850
- if (data.containsKey('rSize'))
1851
- this._showLimitExceededWarning($('#znm-roomListWarning'), 'The received list is incomplete, because its size (' + data.getInt('rSize') + ' Rooms) exceeded this client\'s limit of ' + data.getInt('rLimit') + '; you should refine your search');
1852
- else
1853
- this._showLimitExceededWarning($('#znm-roomListWarning'), '');
1854
- }
1855
- else
1856
- {
1857
- if (this._activePanelType == 'room')
1858
- {
1859
- this._accordion.select('[data-item-type="zone"]');
1860
- this._accordion.expand('[data-item-type="zone"]');
1861
- this._onZoneSelected();
1862
- this._onEntitiesPanelChange();
1863
- }
1864
- }
1865
- }
1866
-
1867
- // --- USERS LIST ---
1868
- if (data.containsKey('users'))
1869
- {
1870
- // If a null users list is returned, the room must have been deleted!
1871
- if (!data.isNull('users'))
1872
- {
1873
- this._setUsersDataProvider(data.getSFSArray('users'));
1874
-
1875
- // Show warning if the users list exceeds the server-side limit
1876
- if (data.containsKey('uSize'))
1877
- this._showLimitExceededWarning($('#znm-userListWarning'), 'The received list is incomplete, because its size (' + data.getInt('uSize') + ' users) exceeds this client\'s limit of ' + data.getInt('uLimit') + '; you should refine your search');
1878
- else
1879
- this._showLimitExceededWarning($('#znm-userListWarning'), '');
1880
- }
1881
- else
1882
- {
1883
- if (this._activePanelType == 'user')
1884
- {
1885
- this._accordion.select('[data-item-type="room"]');
1886
- this._accordion.expand('[data-item-type="room"]');
1887
- this._onRoomSelected();
1888
- this._onEntitiesPanelChange();
1889
- }
1890
- }
1891
- }
1892
-
1893
- // --- MONITORED DATA ---
1894
- if (data.containsKey('monitored'))
1895
- {
1896
- const zone = data.getSFSObject('monitored').getUtfString('zone');
1897
- const type = data.getSFSObject('monitored').getUtfString('type');
1898
- const name = data.getSFSObject('monitored').getUtfString('name');
1899
-
1900
- if (zone == this._monitoredZone && type == this._monitoredType && name == this._monitoredName)
1901
- {
1902
- if (!data.getSFSObject('monitored').isNull('params'))
1903
- {
1904
- const params = data.getSFSObject('monitored').getSFSArray('params');
1905
-
1906
- this._setMonitoringInterface();
1907
-
1908
- // Build interface
1909
- if (!this._isEditing)
1910
- {
1911
- this._interfaceBuilder.buildInterface(params, `znm-${this._monitoredType}TabNavigator`, true);
1912
-
1913
- if (!this._skipInitTabs)
1914
- {
1915
- $(`#znm-${this._monitoredType}TabNavigator #tabs`).scrollingTabs({
1916
- bootstrapVersion: 4,
1917
- scrollToTabEdge: true,
1918
- enableSwiping: true,
1919
- disableScrollArrowsOnFullyScrolled: true,
1920
- cssClassLeftArrow: 'fa fa-chevron-left',
1921
- cssClassRightArrow: 'fa fa-chevron-right'
1922
- });
1923
-
1924
- this._skipInitTabs = true;
1925
-
1926
- // Set first tab panel as active
1927
- this._interfaceBuilder.activateFirstTabPanel();
1928
-
1929
- // Redraw time range slider to set its width
1930
- this._timeSlider.resize();
1931
-
1932
- // Add listener to redraw all charts on tab change
1933
- // (to work around an issue with the chart resizing to default width when pane is hidden)
1934
- $('a[data-toggle="tab"]').on('shown.bs.tab', $.proxy(this._onWindowResize, this));
1935
- }
1936
- }
1937
-
1938
- // Disable categories
1939
- let disabledCat = (data.getSFSObject('monitored').containsKey('exclude') ? data.getSFSObject('monitored').getUtfStringArray('exclude') : []);
1940
-
1941
- for (let cat of disabledCat)
1942
- {
1943
- let navLink = $(`#znm-${this._monitoredType}TabNavigator #tabs #tab-${cat}`);
1944
-
1945
- navLink.addClass('disabled');
1946
- navLink.attr('tabindex', -1);
1947
- navLink.attr('aria-disabled', true);
1948
- }
1949
-
1950
- if (this._monitoredType == this.MONITORED_TYPE_ZONE)
1951
- this._showZoneTrafficData(data.getSFSObject('monitored').getSFSObject('traffic'));
1952
-
1953
- if (this._monitoredType == this.MONITORED_TYPE_USER)
1954
- {
1955
- // Stats
1956
- this._showUserStatsData(data.getSFSObject('monitored').getSFSObject('stats'));
1957
-
1958
- // NPC
1959
- const isNpc = data.getSFSObject('monitored').getBool('npc');
1960
-
1961
- // Disable disconnect, kick and ban buttons
1962
- $('#znm-disconnectBt').attr('disabled', isNpc);
1963
- $('#znm-kickBt').attr('disabled', isNpc);
1964
- $('#znm-banBt').attr('disabled', isNpc);
1965
-
1966
- // Geo-location
1967
- const geoLocData = data.getSFSObject('monitored').getSFSObject('geoLoc');
1968
- this._setGeoLocationUI(geoLocData);
1969
- }
1970
- }
1971
- else
1972
- {
1973
- // Show alert
1974
- this.shellCtrl.showSimpleAlert(`The ${Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["capitalizeFirst"])(this._monitoredType)} being monitored is no more available on the server, please select another one.`, false);
1975
-
1976
- // Cancel monitoring
1977
- this._onCloseMonitorBtClick();
1978
- }
1979
- }
1980
- }
1981
- }
1982
- }
1983
-
1984
- else if (command == this.RESP_UPDATE_ERROR)
1985
- {
1986
- if (data.getUtfString('zone') == this._monitoredZone
1987
- && data.getUtfString('type') == this._monitoredType
1988
- && data.getUtfString('name') == this._monitoredName)
1989
- {
1990
- // Show alert
1991
- this.shellCtrl.showSimpleAlert(`Unable to update ${Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["capitalizeFirst"])(this._monitoredType)} settings; the following error was reported: ${data.getUtfString('message')}.`);
1992
- }
1993
- }
1994
-
1995
- else if (command == this.RESP_ROOM_CONFIG)
1996
- {
1997
- // Re-enable Create button
1998
- $('#znm-createRoomBt').attr('disabled', false);
1999
-
2000
- // Show Room creation panel
2001
- this._showRoomCreationPanel(data.getSFSArray('settings'));
2002
- }
2003
-
2004
- else if (command == this.RESP_ROOM_CONFIG_ERROR)
2005
- {
2006
- // Re-enable Create button
2007
- $('#znm-createRoomBt').attr('disabled', false);
2008
-
2009
- // Show an alert
2010
- this.shellCtrl.showSimpleAlert(data.getUtfString('error'));
2011
- }
2012
-
2013
- else if (command == this.RESP_ROOM_CREATED)
2014
- {
2015
- // Re-enable room creation panel
2016
- this._enableCreateRoomPanel(true);
2017
-
2018
- // Hide room creation panel
2019
- $('#znm-createRoomModal').modal('hide');
2020
-
2021
- // Update rooms list
2022
- this._requestData(true);
2023
- }
2024
-
2025
- else if (command == this.RESP_ROOM_CREATION_ERROR)
2026
- {
2027
- // Display error message
2028
- $('#znm-createRoomError').text(data.getUtfString('error'));
2029
- $('#znm-createRoomError').show();
2030
-
2031
- // Re-enable room creation panel
2032
- this._enableCreateRoomPanel(true);
2033
- }
2034
-
2035
- // Unexpected error received
2036
- // This error is returned in case something went wrong when retrieving the monitored entity parameters,
2037
- // so we have to stop requesting the same entity
2038
- else if (command == this.RESP_UNEXPECTED_ERROR)
2039
- {
2040
- // Cancel monitoring
2041
- this._onCloseMonitorBtClick();
2042
-
2043
- // Show an alert
2044
- this.shellCtrl.showSimpleAlert(data.getUtfString('error'));
2045
- }
2046
-
2047
- // Extension (Zone or Room) log messages received
2048
- else if (command == this.RESP_LOG_MESSAGES)
2049
- {
2050
- let logEntries = data.getSFSArray('entries');
2051
-
2052
- for (let e = 0; e < logEntries.size(); e++)
2053
- {
2054
- let logEntry = logEntries.getSFSObject(e);
2055
-
2056
- const logZone = logEntry.getUtfString('zone');
2057
- const logRoom = logEntry.getUtfString('room');
2058
-
2059
- // Check if the zone is currently monitored
2060
- if (this._monitoredZone == logZone)
2061
- {
2062
- // console.log(logEntry.getLong('time'))
2063
- // console.log(new Date(logEntry.getLong('time'))
2064
-
2065
- let ele = {
2066
- timestamp: kendo.toString(new Date(logEntry.getLong('time')), 'dd/MM/yyyy HH:mm:ss'),
2067
- level: logEntry.getUtfString('level'),
2068
- message: logEntry.getUtfString('msg'),
2069
- };
2070
-
2071
- if (this._monitoredType == this.MONITORED_TYPE_ZONE && logRoom == '')
2072
- {
2073
- // Add to zone extension log
2074
- this._addLogEntryToGrid(this._zoneExtLogGrid, ele);
2075
- }
2076
- else
2077
- {
2078
- if (this._monitoredType == this.MONITORED_TYPE_ROOM && this._monitoredName == logRoom)
2079
- {
2080
- // Add to room extension log
2081
- this._addLogEntryToGrid(this._roomExtLogGrid, ele);
2082
- }
2083
- }
2084
- }
2085
- }
2086
- }
2087
- }
2088
-
2089
- //---------------------------------
2090
- // UI EVENT LISTENERS
2091
- //---------------------------------
2092
-
2093
- _onUpdateIntervalChange()
2094
- {
2095
- // Request data to server
2096
- this._requestData(false);
2097
- }
2098
-
2099
- _onEntitiesPanelChange()
2100
- {
2101
- // Set main controls state
2102
- this._setMainControlsEnabled();
2103
-
2104
- // Request data to server
2105
- this._requestData(true);
2106
- }
2107
-
2108
- /**
2109
- * Update the Scope label in the Rooms panel.
2110
- */
2111
- _onZoneSelected()
2112
- {
2113
- // Clear Room and User lists
2114
- this._resetRoomList();
2115
- this._resetUserList();
2116
-
2117
- // Set scope label
2118
- this._setScopeLabel();
2119
-
2120
- // Enable/disable Room and User tools
2121
- const disabled = this._selectedZone == undefined;
2122
-
2123
- // (NOTE: this enables/disables whole fieldset; this doesn't affect Kendo DropDown, so we have to do it manually)
2124
- $('#roomTools').attr('disabled', disabled);
2125
- $('#userTools').attr('disabled', disabled);
2126
-
2127
- this._groupsDropDown.enable(!disabled);
2128
-
2129
- // Set main controls state
2130
- this._setMainControlsEnabled();
2131
- }
2132
-
2133
- /**
2134
- * Update the Scope label in the Users panel.
2135
- */
2136
- _onRoomSelected()
2137
- {
2138
- // Clear User list
2139
- this._resetUserList();
2140
-
2141
- // Set scope label
2142
- this._setScopeLabel();
2143
-
2144
- // Enable Remove button
2145
- $('#znm-removeRoomBt').attr('disabled', this._selectedRoom == undefined);
2146
-
2147
- // Set main controls state
2148
- this._setMainControlsEnabled();
2149
- }
2150
-
2151
- /**
2152
- * Update the scope label in the Users panel.
2153
- */
2154
- _onGroupChange()
2155
- {
2156
- // Clear Room and User lists
2157
- this._resetRoomList();
2158
- this._resetUserList();
2159
-
2160
- // Set scope label
2161
- this._setScopeLabel();
2162
-
2163
- // Request data to server
2164
- this._requestData(true);
2165
-
2166
- // Set main controls state
2167
- this._setMainControlsEnabled();
2168
- }
2169
-
2170
- _onUserSelected()
2171
- {
2172
- // Set main controls state
2173
- this._setMainControlsEnabled();
2174
- }
2175
-
2176
- /**
2177
- * Show room creation panel.
2178
- */
2179
- _onCreateRoomBtClick()
2180
- {
2181
- // Disable Create button
2182
- $('#znm-createRoomBt').attr('disabled', true);
2183
-
2184
- // Request default room settings to extension
2185
- this.sendExtensionRequest(this.REQ_GET_ROOM_CONFIG);
2186
- }
2187
-
2188
- /**
2189
- * Remove existing room.
2190
- */
2191
- _onRemoveRoomBtClick()
2192
- {
2193
- this.shellCtrl.showConfirmWarning('Are you sure you want to remove the selected Room?', $.proxy(this._onRemoveRemoveConfirm, this));
2194
- }
2195
-
2196
- _onRemoveRemoveConfirm()
2197
- {
2198
- const selectedZone = this._zoneListBox.dataItem(this._zoneListBox.select());
2199
- let selectedRoom = this._roomListBox.dataItem(this._roomListBox.select());
2200
-
2201
- if (selectedRoom)
2202
- {
2203
- // Send request to server
2204
- let params = new SFS2X.SFSObject();
2205
- params.putUtfString('zone', selectedZone.name);
2206
- params.putUtfString('room', selectedRoom.name);
2207
-
2208
- this.sendExtensionRequest(this.REQ_REMOVE_ROOM, params);
2209
-
2210
- // Request data to server to update the rooms list
2211
- this._requestData(true);
2212
- }
2213
- }
2214
-
2215
- /**
2216
- * Remove content of room creation panel.
2217
- */
2218
- _onCreateRoomModalHidden(e)
2219
- {
2220
- // If a nested modal is opened, its closing causes the parent modal to be closed too (Bootstrap doesn't support nested modals)
2221
- // As a workaround, here we reset the css class 'modal-open' when the nested modal is closed and skip everything else
2222
- // In addition we had to use a custom listener on the close/cancel buttons of the nested modal (see config-grid.js for example)
2223
- if (e.target !== document.getElementById('znm-createRoomModal'))
2224
- {
2225
- $('body').addClass('modal-open')
2226
- return;
2227
- }
2228
-
2229
- // Destroy scrolling tabs in room creation panel
2230
- $('#znm-roomCreatorTabNav #tabs').scrollingTabs('destroy');
2231
-
2232
- // Remove all tab navigator content
2233
- this._roomCreationIBuilder.destroyInterface();
2234
-
2235
- // Remove listener for custom actions triggered by configuration interface
2236
- $('#znm-roomCreatorTabNav').off('value-set');
2237
-
2238
- // Reset and hide error message
2239
- this._resetRoomCreationError();
2240
- }
2241
-
2242
- _onConfigValueSet(e) // SAME METHOD DUPLICATED IN zone-configurator.js
2243
- {
2244
- const configParam = e.target.data;
2245
-
2246
- // Handle extension name/type dropdowns update and update the main class dropdown datasource accordingly
2247
- if (configParam.name == 'extension.name' || configParam.name == 'extension.type' || configParam.name == 'extension.filterClass')
2248
- {
2249
- // All involved ConfigFormItems must be available and initialized to proceed
2250
- const nameFormItem = this._roomCreationIBuilder.getConfigFormItem('extension.name');
2251
- const typeFormItem = this._roomCreationIBuilder.getConfigFormItem('extension.type');
2252
- const classFormItem = this._roomCreationIBuilder.getConfigFormItem('extension.file');
2253
- const filterFormItem = this._roomCreationIBuilder.getConfigFormItem('extension.filterClass');
2254
-
2255
- if (nameFormItem != null && typeFormItem != null && classFormItem != null && filterFormItem != null)
2256
- {
2257
- const source = nameFormItem.data;
2258
- let classesList = [];
2259
-
2260
- let data = source.triggerData;
2261
- for (let i = 0; i < data.size(); i++)
2262
- {
2263
- let ext = data.getSFSObject(i);
2264
-
2265
- if (ext.getUtfString('name') == nameFormItem.data.value && ext.getUtfString('type') == typeFormItem.data.value)
2266
- {
2267
- let classes = ext.getUtfString('classesString').split(',');
2268
-
2269
- if (filterFormItem.data.value == true)
2270
- {
2271
- let filteredClasses = classes.filter(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["filterClassName"]);
2272
- classes = filteredClasses;
2273
- }
2274
-
2275
- classesList = classesList.concat(classes);
2276
- }
2277
- }
2278
-
2279
- let currentClass = classFormItem.data.value;
2280
-
2281
- // If the classes list doesn't contain the current value, create an empty entry and reset the value
2282
- if (classesList.indexOf(currentClass) < 0)
2283
- {
2284
- if (classesList.length == 0)
2285
- {
2286
- classesList.push('');
2287
- currentClass = '';
2288
- }
2289
- else
2290
- currentClass = classesList[0];
2291
- }
2292
-
2293
- let mainClassDropDown = classFormItem._innerWidget;
2294
- mainClassDropDown.setDataSource(classesList);
2295
-
2296
- classFormItem.data.value = currentClass;
2297
- classFormItem._setWidgetValue();
2298
- }
2299
- }
2300
- }
2301
-
2302
- _onRoomCreatorCreateBtClick()
2303
- {
2304
- // Reset and hide error message
2305
- this._resetRoomCreationError();
2306
-
2307
- // Check validity
2308
- if (this._roomCreationIBuilder.checkIsValid())
2309
- {
2310
- let changes = this._roomCreationIBuilder.getChangedData();
2311
-
2312
- if (changes.size() > 0)
2313
- {
2314
- // Check the room name against the rooms list (duplicate names not allowed!)
2315
- if (true)//this._validateNewRoomName(changes))
2316
- {
2317
- // Disable room creation panel
2318
- this._enableCreateRoomPanel(false);
2319
-
2320
- // Send settings to server instance
2321
- let params = new SFS2X.SFSObject();
2322
- params.putSFSArray('settings', changes);
2323
- params.putUtfString('zName', this._zoneListBox.dataItem(this._zoneListBox.select()).name);
2324
- params.putBool('notify', $('#znm-notifyClientsCB').prop('checked'));
2325
-
2326
- // Send settings to extension
2327
- this.sendExtensionRequest(this.REQ_CREATE_ROOM, params);
2328
- }
2329
- else
2330
- {}
2331
- }
2332
- }
2333
- else
2334
- {
2335
- // Show alert
2336
- this.shellCtrl.showSimpleAlert('Unable to submit Room configuration due to an invalid value; please verify the highlighted form fields in all tabs.', true);
2337
- }
2338
- }
2339
-
2340
- _onShowRoomFilterBtClick()
2341
- {
2342
- this._roomFilter.show();
2343
- }
2344
-
2345
- _onShowUserFilterBtClick()
2346
- {
2347
- this._userFilter.show();
2348
- }
2349
-
2350
- _onApplyBtChange()
2351
- {
2352
- // Request data to server
2353
- this._requestData(true);
2354
- }
2355
-
2356
- _onRoomFilterUpdated()
2357
- {
2358
- // Enable/disable "Apply" checkbox
2359
- $('#znm-applyRoomFilterCB').attr('disabled', this._roomFilter.filterExpression == null);
2360
-
2361
- // Request data to server
2362
- this._requestData(true);
2363
- }
2364
-
2365
- _onUserFilterUpdated()
2366
- {
2367
- // Enable/disable "Apply" checkbox
2368
- $('#znm-applyUserFilterCB').attr('disabled', this._userFilter.filterExpression == null);
2369
-
2370
- // Request data to server
2371
- this._requestData(true);
2372
- }
2373
-
2374
- _onMonitorSelectionBtClick()
2375
- {
2376
- if (this._selectedZone)
2377
- {
2378
- // Cancel previous monitoring
2379
- this._onCloseMonitorBtClick();
2380
-
2381
- let getZoneUserCountHistory = false;
2382
-
2383
- this._monitoredZone = this._selectedZone.name;
2384
-
2385
- // Zone must be monitored
2386
- if (this._activePanelType == 'zone')
2387
- {
2388
- this._monitoredType = this.MONITORED_TYPE_ZONE;
2389
- this._monitoredName = this._monitoredZone;
2390
-
2391
- getZoneUserCountHistory = true;
2392
- }
2393
-
2394
- // Room must be monitored
2395
- if (this._activePanelType == 'room' && this._selectedRoom != undefined)
2396
- {
2397
- this._monitoredType = this.MONITORED_TYPE_ROOM;
2398
- this._monitoredName = this._selectedRoom.name;
2399
- }
2400
-
2401
- // User must be monitored
2402
- if (this._activePanelType == 'user' && this._selectedUser != undefined)
2403
- {
2404
- this._monitoredType = this.MONITORED_TYPE_USER;
2405
- this._monitoredName = this._selectedUser.name;
2406
- }
2407
-
2408
- // Show loading bar
2409
- this._switchView('znm-loading');
2410
-
2411
- // Request data to server
2412
- this._requestData(true, getZoneUserCountHistory);
2413
- }
2414
- }
2415
-
2416
- _onSendAdminMsgInKeyUp(event)
2417
- {
2418
- if (event.key !== 'Enter')
2419
- return;
2420
-
2421
- event.preventDefault();
2422
- this._onSendAdminMsgBtClick();
2423
- }
2424
-
2425
- _onSendAdminMsgBtClick()
2426
- {
2427
- if (this._selectedZone && $('#znm-messageIn').val() != '')
2428
- {
2429
- // Build request parameters
2430
- let params = new SFS2X.SFSObject();
2431
-
2432
- // Always include zone
2433
- params.putUtfString('zone', this._selectedZone.name);
2434
-
2435
- // Send message to room?
2436
- if (this._activePanelType == 'room' && this._selectedRoom != undefined)
2437
- params.putUtfString('room', this._selectedRoom.name);
2438
-
2439
- // Send message to user?
2440
- if (this._activePanelType == 'user' && this._selectedUser != undefined)
2441
- params.putUtfString('user', this._selectedUser.name);
2442
-
2443
- // Add message
2444
- params.putUtfString('msg', $('#znm-messageIn').val());
2445
-
2446
- // Send request to extension
2447
- this.sendExtensionRequest(this.REQ_ADMIN_MSG, params);
2448
-
2449
- // Clear text input
2450
- $('#znm-messageIn').val('');
2451
- }
2452
- }
2453
-
2454
- _onCloseMonitorBtClick()
2455
- {
2456
- // Save ref. for later usage
2457
- const monitoredType = this._monitoredType;
2458
-
2459
- this._monitoredZone = null;
2460
- this._monitoredType = null;
2461
- this._monitoredName = null;
2462
-
2463
- // Clear geolocation UI
2464
- this._clearGeoLocationUI();
2465
-
2466
- // Clear log datagrids dataproviders
2467
- // NOTE: a try-catch is used here because, for an unknown reason, Kendo is throwing an error when resetting the data sources when the module is destroyed
2468
- try
2469
- {
2470
- this._zoneExtLogGrid.setDataSource([]);
2471
- this._roomExtLogGrid.setDataSource([]);
2472
- }
2473
- catch (e) { /* Ignore */ }
2474
-
2475
- // Leave edit mode
2476
- this._onCancelBtClick();
2477
-
2478
- // Rearrange monitoring interface
2479
- this._setMonitoringInterface();
2480
-
2481
- // Clear tab navigator
2482
- this._clearTabs(monitoredType);
2483
- }
2484
-
2485
- _onEditBtClick()
2486
- {
2487
- if (!this._isEditing)
2488
- {
2489
- // Show edit controls
2490
- $('#znm-editControls').show();
2491
-
2492
- // Hide edit button
2493
- $('#znm-editBt').hide();
115
+ //-------------------------------------------
2494
116
 
2495
- // Enable editable fields
2496
- this._interfaceBuilder.disableInterface(false);
117
+ $(this).append(`
118
+ <div class="col-sm-5 col-lg-4 col-form-label form-label-container">
119
+ <label class="form-label">Upload certificate <i class="fas fa-question-circle text-muted help" title="Upload an SSL certificate's protected keystore to the server"></i></label>
120
+ </div>
121
+ <div class="inner-widget align-self-center align-self-sm-start col-auto">
122
+ <button id="manageSslButton" type="button" class="k-button k-primary" disabled><i class="fas fa-cog mr-1"></i>Manage</button>
123
+ </div>
124
+ `);
2497
125
 
2498
- this._isEditMode = true;
2499
- }
126
+ // Add listeners to Manage button click
127
+ $('#manageSslButton', $(this)).on('click', $.proxy(this._onManageSslClick, this));
2500
128
  }
2501
129
 
2502
- _onCancelBtClick()
130
+ destroy()
2503
131
  {
2504
- if (this._isEditing)
2505
- {
2506
- // Hide edit controls
2507
- $('#znm-editControls').hide();
2508
-
2509
- // Show edit button
2510
- $('#znm-editBt').show();
132
+ // Remove event listener
133
+ $('#manageSslButton', $(this)).off('click');
2511
134
 
2512
- // Disable editable fields
2513
- this._interfaceBuilder.disableInterface(true);
135
+ // Hide modal (which in turn destroys it)
136
+ let modalElement = $('#uploadModal', $(this));
2514
137
 
2515
- this._isEditMode = false;
2516
-
2517
- // Hide validation messages
2518
- this._interfaceBuilder.resetValidation();
2519
-
2520
- // Request data to update interface (updating suspended in edit mode)
2521
- this._requestData(true);
2522
- }
138
+ if (modalElement)
139
+ modalElement.modal('hide');
2523
140
  }
2524
141
 
2525
- _onSubmitBtClick()
142
+ get enabled()
2526
143
  {
2527
- if (this._isEditing)
2528
- {
2529
- // Check validity
2530
- if (this._interfaceBuilder.checkIsValid())
2531
- {
2532
- let changes = this._interfaceBuilder.getChangedData();
2533
-
2534
- if (changes.size() > 0)
2535
- {
2536
- //console.log(changes.getDump())
2537
-
2538
- // Send settings to server instance
2539
- let params = new SFS2X.SFSObject();
2540
- params.putSFSArray('settings', changes);
2541
- params.putUtfString('zone', this._monitoredZone);
2542
-
2543
- if (this._monitoredType == this.MONITORED_TYPE_ROOM)
2544
- {
2545
- // Submit room settings
2546
- params.putUtfString('room', this._monitoredName);
2547
- this.sendExtensionRequest(this.REQ_SET_ROOM_SETTINGS, params);
2548
- }
2549
- else if (this._monitoredType == this.MONITORED_TYPE_USER)
2550
- {
2551
- // Submit user settings
2552
- params.putUtfString('user', this._monitoredName);
2553
- this.sendExtensionRequest(this.REQ_SET_USER_SETTINGS, params);
2554
- }
2555
- else
2556
- {
2557
- // Submit zone settings
2558
- this.sendExtensionRequest(this.REQ_SET_ZONE_SETTINGS, params);
2559
- }
2560
-
2561
- // Leave edit mode
2562
- this._onCancelBtClick();
2563
- }
2564
- }
2565
- else
2566
- {
2567
- // Show alert
2568
- this.shellCtrl.showSimpleAlert('Unable to submit changes due to an invalid value; please verify the highlighted form fields in all tabs.', true);
2569
- }
2570
- }
144
+ return this._isEnabled;
2571
145
  }
2572
146
 
2573
- _onTimeRangeChange()
147
+ set enabled(value)
2574
148
  {
2575
- let values = this._timeSlider.value();
149
+ this._isEnabled = value;
150
+
151
+ // Enable/disable Manage button
152
+ $('#manageSslButton', $(this)).attr('disabled', !value);
2576
153
 
2577
- if (values[0] == values[1])
154
+ // Enable/disable modal
155
+ let modalElement = $('#uploadModal', $(this));
156
+
157
+ if (modalElement)
2578
158
  {
2579
- if (values[1] == -24)
2580
- values[1] = -23;
159
+ // Disable modal close buttons
160
+ $('button[data-dismiss="modal"]', modalElement).attr('disabled', !value);
2581
161
 
2582
- values[0] = values[1] - 1;
162
+ // Disable upload button
163
+ $('#uploadSslButton', modalElement).attr('disabled', !value);
2583
164
 
2584
- // Reset the time range slider value (we need to use setTimeout
2585
- // bacause doing it in the change event listener doesn't redraw the slider)
2586
- setTimeout($.proxy( function(values) { this._timeSlider.value(values); }, this), 10, values);
165
+ // Disable fieldset
166
+ $('#uploadFieldset', modalElement).attr('disabled', !value);
2587
167
  }
2588
-
2589
- this._userCountChart.range = values;
2590
168
  }
2591
169
 
2592
- _onWindowResize()
170
+ get uploadTargetConfig()
2593
171
  {
2594
- // Redraw charts
2595
- this._userCountChart.redraw();
2596
- this._packetQueueChart.redraw();
2597
- this._droppedMsgChart.redraw();
2598
- this._writtenDataChart.redraw();
2599
- this._readDataChart.redraw();
2600
-
2601
- // Redraw time range slider
2602
- this._timeSlider.resize();
172
+ return this._uploadTargetConfig;
2603
173
  }
2604
174
 
2605
- _onWordsReloadBtClick()
175
+ set uploadTargetConfig(data)
2606
176
  {
2607
- if (this._monitoredType == this.MONITORED_TYPE_ZONE)
2608
- {
2609
- // Send request to extension
2610
- let params = new SFS2X.SFSObject();
2611
- params.putUtfString('zone', this._monitoredName);
2612
-
2613
- this.sendExtensionRequest(this.REQ_RELOAD_WORDS, params);
2614
- }
177
+ this._uploadTargetConfig = data;
2615
178
  }
2616
179
 
2617
- _onZoneExtReloadBtClick()
180
+ _onManageSslClick()
2618
181
  {
2619
- if (this._monitoredType == this.MONITORED_TYPE_ZONE)
2620
- {
2621
- // Send request to extension
2622
- let params = new SFS2X.SFSObject();
2623
- params.putUtfString('zone', this._monitoredName);
2624
-
2625
- this.sendExtensionRequest(this.REQ_RELOAD_ZONE_EXT, params);
2626
- }
182
+ // Initialize and show modal
183
+ this._showModal();
2627
184
  }
2628
185
 
2629
- _onDisconnectBtClick()
186
+ _onUploadSslClick()
2630
187
  {
2631
- if (this._monitoredType == this.MONITORED_TYPE_USER)
2632
- {
2633
- // Send request to extension
2634
- let params = new SFS2X.SFSObject();
2635
- params.putUtfString('zone', this._monitoredZone);
2636
- params.putUtfString('user', this._monitoredName);
2637
-
2638
- this.sendExtensionRequest(this.REQ_DISCONNECT_USER, params);
2639
- }
188
+ if (this._validate())
189
+ this._startSslCertUpload();
2640
190
  }
2641
191
 
2642
- _onKickBtClick()
192
+ _showModal()
2643
193
  {
2644
- if (this._monitoredType == this.MONITORED_TYPE_USER)
2645
- {
2646
- // Send request to extension
2647
- let params = new SFS2X.SFSObject();
2648
- params.putUtfString('zone', this._monitoredZone);
2649
- params.putUtfString('user', this._monitoredName);
2650
- params.putUtfString('msg', $('#znm-kickMsgIn').val());
2651
- params.putInt('delay', (this._kickDelayIn.value() != null ? Number(this._kickDelayIn.value()) : this.KICK_BAN_DEFAULT_DELAY));
194
+ // Append modal html
195
+ $(this).append(this._modalHtml);
2652
196
 
2653
- this.sendExtensionRequest(this.REQ_KICK_USER, params);
197
+ let modalElement = $('#uploadModal', $(this));
2654
198
 
2655
- // Reset message
2656
- $('#znm-kickMsgIn').val('');
2657
- }
2658
- }
2659
-
2660
- _onBanBtClick()
2661
- {
2662
- if (this._monitoredType == this.MONITORED_TYPE_USER)
2663
- {
2664
- // Send request to extension
2665
- let params = new SFS2X.SFSObject();
2666
- params.putUtfString('zone', this._monitoredZone);
2667
- params.putUtfString('user', this._monitoredName);
2668
- params.putUtfString('msg', $('#znm-banMsgIn').val());
2669
- params.putUtfString('mode', this._banModeDd.select() > 0 ? this._banModeDd.value() : 'NAME');
2670
- params.putInt('delay', (this._banDelayIn.value() != null ? Number(this._banDelayIn.value()) : this.KICK_BAN_DEFAULT_DELAY));
2671
-
2672
- let duration = this._banDurationIn.value() != null ? Number(this._banDurationIn.value()) : 1;
199
+ // Hide SSL certificate upload spinner and error message container
200
+ $('#uploadSpinner', modalElement).hide();
201
+ $('#uploadErrorMsg', modalElement).hide();
202
+ $('#uploadErrorMsg', modalElement).text('');
2673
203
 
2674
- if (this._banDurUnitDd.value() == 'hours')
2675
- duration = duration * 60;
2676
- else if (this._banDurUnitDd.value() == 'days')
2677
- duration = duration * 60 * 24;
204
+ // Add listener to Upload button click
205
+ $('#uploadSslButton', modalElement).on('click', $.proxy(this._onUploadSslClick, this));
2678
206
 
2679
- params.putInt('duration', duration);
207
+ // Add listener to modal hide event
208
+ modalElement.on('hidden.bs.modal', $.proxy(this._destroyModal, this));
2680
209
 
2681
- this.sendExtensionRequest(this.REQ_BAN_USER, params);
210
+ // Initialize kendo uploader
211
+ this._uploadWidget = modalElement.find('#uploader').kendoUpload({
212
+ allowedExtensions: ['.jks'],
213
+ multiple: false,
214
+ template: function(dataItem) {
215
+ dataItem.bytesToSize = _utils_utilities__WEBPACK_IMPORTED_MODULE_0__["bytesToSize"]; // Pass bytesToSize utility function to template
216
+ return kendo.template(`
217
+ <span class='k-progress w-100'></span>
218
+ <span class="">
219
+ <span class="k-file-name" title="#=name#">#=name#</span>
220
+ <span class="k-file-size">Size: #=bytesToSize(size, 1, 'Bytes')#</span>
221
+ </span>
222
+ `)(dataItem);
223
+ },
224
+ localization: {
225
+ select: 'Select file...'
226
+ }
227
+ }).data('kendoUpload');
228
+
229
+ // Initialize kendo validation on uploader subform
230
+ // NOTE: we use separate validators to be able to disable 'validateOnBlur' on the uploader,
231
+ // because it causes the error message to appear as soon as the "Select file" button is clicked
232
+ this._validator1 = modalElement.find('#uploaderSubform').kendoValidator({
233
+ validateOnBlur: false,
234
+ rules: {
235
+ upload: function(input) {
236
+ let valid = false;
237
+ if (input.is('[type=file]'))
238
+ valid = input.closest('.k-upload').find('.k-file').length > 0;
239
+
240
+ return valid;
241
+ }
242
+ }
243
+ }).data('kendoValidator');
244
+
245
+ // Initialize kendo validation on passwords subform
246
+ this._validator2 = modalElement.find('#passwordsSubform').kendoValidator({
247
+ validateOnBlur: true,
248
+ rules: {
249
+ verifyKsPassword: $.proxy(function(input) {
250
+ let valid = true;
251
+ if (input.is('[name=confirmKsPassword]'))
252
+ valid = input.val() === $(this).find('#ksPassword').val();
253
+ return valid;
254
+ }, this),
255
+ verifyAdmPassword: $.proxy(function(input) {
256
+ let valid = true;
257
+ if (input.is('[name=confirmAdminPassword]'))
258
+ valid = input.val() === $(this).find('#adminPassword').val();
259
+ return valid;
260
+ }, this)
261
+ },
262
+ messages: {
263
+ verifyKsPassword: 'Password not matching',
264
+ verifyAdmPassword: 'Password not matching',
265
+ }
266
+ }).data('kendoValidator');
2682
267
 
2683
- // Reset message
2684
- $('#znm-banMsgIn').val('');
2685
- }
268
+ // Initialize bootstrap modal
269
+ modalElement.modal({
270
+ backdrop: 'static',
271
+ keyboard: false,
272
+ });
2686
273
  }
2687
274
 
2688
- //------------------------------------
2689
- // PRIVATE METHODS
2690
- //------------------------------------
2691
-
2692
- _setMonitoringInterface()
275
+ _destroyModal()
2693
276
  {
2694
- const enabled = (this._monitoredType != null && this._monitoredName != null);
277
+ let modalElement = $('#uploadModal', $(this));
2695
278
 
2696
- // Show/hide header and footer
2697
- if (enabled)
279
+ if (modalElement)
2698
280
  {
2699
- this._switchView(`znm-${this._monitoredType}Monitor`);
2700
-
2701
- // Header title
2702
- let title = 'Now monitoring ';
281
+ // Remove listeners
282
+ $('#uploadSslButton', modalElement).off('click');
283
+ modalElement.off('hidden.bs.modal');
2703
284
 
2704
- if (this._monitoredType == this.MONITORED_TYPE_USER)
2705
- title += `User: <strong>${this._monitoredName}</strong> (Zone: ${this._monitoredZone})`;
2706
- else if (this._monitoredType == this.MONITORED_TYPE_ROOM)
2707
- title += `Room: <strong>${this._monitoredName}</strong> (Zone: ${this._monitoredZone})`;
2708
- else
2709
- title += `Zone: <strong>${this._monitoredZone}</strong>`;
2710
-
2711
- $('#znm-monitoredHeader').html(title);
2712
- $('#znm-monitoredFooter').show();
2713
-
2714
- // Show panel
2715
- this._switchPanel('znm-mainPanel');
2716
- }
2717
- else
2718
- {
2719
- this._switchView('znm-select');
285
+ // Destroy everything Kendo
286
+ kendo.destroy(modalElement);
2720
287
 
2721
- $('#znm-monitoredHeader').html('');
2722
- $('#znm-monitoredFooter').hide();
288
+ // Dispose modal
289
+ modalElement.modal('dispose');
2723
290
 
2724
- // Show panel
2725
- this._switchPanel('znm-sidebarPanel');
291
+ // Remove html
292
+ modalElement.remove();
293
+ modalElement = null;
2726
294
  }
2727
295
  }
2728
296
 
2729
- _switchView(viewId)
297
+ _validate()
2730
298
  {
2731
- document.getElementById('znm-viewstack').selectedElement = document.getElementById(viewId);
2732
- }
299
+ let val1 = this._validator1.validate();
300
+ let val2 = this._validator2.validate();
2733
301
 
2734
- _switchPanel(panelId)
2735
- {
2736
- document.getElementById('znm-view').selectedPanel = document.getElementById(panelId);
302
+ return val1 && val2;
2737
303
  }
2738
304
 
2739
- /**
2740
- * Build the polling request to be sent to the server, based on the selections and other settings in the accordion.
2741
- */
2742
- _requestData(newId = false, getZoneUserCountHistory = false)
305
+ _startSslCertUpload()
2743
306
  {
2744
- // Clear previous request scheduling
2745
- clearTimeout(this._requestTimer);
2746
-
2747
- // Check if connection is still available
2748
- if (this.smartFox.isConnected)
2749
- {
2750
- if (newId)
2751
- this._currentRequestId++;
2752
-
2753
- // Build request parameters
2754
- let params = new SFS2X.SFSObject();
307
+ if (!this._uploadTargetConfig)
308
+ throw new Error('Upload target configuration not set');
2755
309
 
2756
- // The updated zones/rooms/users lists are requested if the corresponding panel is displayed only
2757
- const getZones = (this._activePanelType == 'zone');
2758
- const getRooms = (this._activePanelType == 'room' && this._selectedZone != undefined);
2759
- const getUsers = (this._activePanelType == 'user' && this._selectedZone != undefined);
310
+ let modalElement = $('#uploadModal', $(this));
2760
311
 
2761
- // --- ZONES ---
2762
- params.putBool('zones', getZones);
2763
-
2764
- // ROOMS & USERS
2765
- if (getRooms || getUsers)
2766
- {
2767
- // Common filtering params:
312
+ if (modalElement)
313
+ {
314
+ let certData = {};
315
+ certData.file = this._uploadWidget.getFiles()[0];
316
+ certData.ksPassword = $('#ksPassword', modalElement).val();
317
+ certData.adminPassword = $('#adminPassword', modalElement).val();
2768
318
 
2769
- // a) zone name
2770
- params.putUtfString('zone', this._selectedZone.name);
319
+ // Disable modal
320
+ this.enabled = false;
2771
321
 
2772
- // b) group name
2773
- params.putUtfString('group', this._selectedGroup.name);
2774
- }
322
+ // Hide previous error and show spinner
323
+ $('#uploadSpinner', modalElement).show();
324
+ $('#uploadErrorMsg', modalElement).hide();
325
+ $('#uploadErrorMsg', modalElement).text('');
2775
326
 
2776
- // ROOMS
2777
- params.putBool('rooms', getRooms);
2778
- if (getRooms)
2779
- {
2780
- // Filtering params:
327
+ //=================================================================
2781
328
 
2782
- // c) advanced filtering params
2783
- if ($('#znm-applyRoomFilterCB').prop('checked') && this._roomFilter.filterExpression != null)
2784
- params.putSFSArray('rFilter', this._roomFilter.filterExpression.toSFSArray());
2785
- }
329
+ // Generate Init Vector
330
+ let rngValues = [];
331
+ for (let i = 0; i < 16; i++)
332
+ rngValues.push(Math.floor(Math.random() * 256));
2786
333
 
2787
- // USERS
2788
- params.putBool('users', getUsers);
2789
- if (getUsers)
2790
- {
2791
- // Filtering params:
334
+ let iv = new Uint8Array(rngValues);
2792
335
 
2793
- // c) room name
2794
- if (this._selectedRoom)
2795
- params.putUtfString('room', this._selectedRoom.name);
336
+ // Generate secret key by MD5-encoding admin password + session token
337
+ let md5Pass = this._binaryMD5(certData.adminPassword + this._uploadTargetConfig.sessionToken);
2796
338
 
2797
- // d) advanced filtering params
2798
- if ($('#znm-applyUserFilterCB').prop('checked') && this._userFilter.filterExpression != null)
2799
- params.putSFSArray('uFilter', this._userFilter.filterExpression.toSFSArray());
2800
- }
339
+ // Encrypt keystore password via AES (128bit)
340
+ let encryptedPwd = this._aesEncrypt(certData.ksPassword, md5Pass, iv);
2801
341
 
2802
- // --- CURRENTLY MONITORED DATA ---
2803
- if (this._monitoredType != null && this._monitoredName != null)
2804
- {
2805
- let monitoredObj = this._getBasicMonitoredObject();
342
+ // Encode IV using Base64
343
+ let encodedIv = this._b64Encode(iv);
2806
344
 
2807
- if (this._monitoredType == this.MONITORED_TYPE_ZONE)
2808
- monitoredObj.putBool('uc', getZoneUserCountHistory);
345
+ // Encode encrypted password using Base64
346
+ let encodedPwd = this._b64Encode(encryptedPwd);
2809
347
 
2810
- params.putSFSObject('monitored', monitoredObj);
2811
- }
348
+ //=================================================================
2812
349
 
2813
- // Set request id; when a response is received, we check its id matches the current request id: if not, the response is discarded
2814
- params.putInt('id', this._currentRequestId);
350
+ // Set parameters to be sent with the certificate keystore file
351
+ const params = new FormData();
352
+ params.append('files[]', certData.file.rawFile);
353
+ params.append('__iv', encodedIv);
354
+ params.append('__password', encodedPwd);
355
+ params.append('__module', this._uploadTargetConfig.moduleId);
2815
356
 
2816
- // Send request to extension
2817
- this.sendExtensionRequest(this.REQ_GET_DATA, params);
357
+ // Set destination url
358
+ const url = this._uploadTargetConfig.protocol + '://' + this._uploadTargetConfig.host + ':' + this._uploadTargetConfig.port + '/BlueBox/SFS2XFileUpload?sessHashId=' + this._uploadTargetConfig.sessionToken;
2818
359
 
2819
- // Schedule next request
2820
- this._requestTimer = setTimeout($.proxy(this._requestData, this), Number(this._intervalDropDown.value()) * 1000);
360
+ // Start upload
361
+ fetch(url, {
362
+ method: 'POST',
363
+ body: params,
364
+ mode: 'no-cors'
365
+ }).then($.proxy(this._onSslCertUploadEnd, this));
2821
366
  }
2822
367
  }
2823
368
 
2824
- _getBasicMonitoredObject()
369
+ _onSslCertUploadEnd(response)
2825
370
  {
2826
- let monitoredObj = new SFS2X.SFSObject();
371
+ // Nothing to do: we have to wait the upload process completion to be signaled by the server through the dedicated Extension response
2827
372
 
2828
- monitoredObj.putUtfString('zone', this._monitoredZone);
2829
- monitoredObj.putUtfString('type', this._monitoredType);
2830
- monitoredObj.putUtfString('name', this._monitoredName);
373
+ //=================================================================
2831
374
 
2832
- return monitoredObj;
375
+ // TODO Should we handle this response in some way? For some unknown reason we always get ok=false and status=0
376
+ //console.log(response)
377
+ //console.log(response.ok)
378
+ //console.log(response.status)
2833
379
  }
2834
380
 
2835
- _setZonesDataProvider(zonesData)
381
+ /**
382
+ * Method called by parent when upload failed.
383
+ */
384
+ onSslCertUploadError(error)
2836
385
  {
2837
- this._setListDataProvider(this._zoneListBox, zonesData, true);
2838
- }
386
+ let modalElement = $('#uploadModal', $(this));
2839
387
 
2840
- _setRoomsDataProvider(roomsData)
2841
- {
2842
- this._setListDataProvider(this._roomListBox, roomsData, true);
388
+ if (modalElement)
389
+ {
390
+ // Enable modal
391
+ this.enabled = true;
2843
392
 
2844
- // Set Remove button state
2845
- $('#znm-removeRoomBt').attr('disabled', this._selectedRoom == undefined);
2846
- }
393
+ // Show upload error
394
+ $('#uploadErrorMsg', modalElement).show();
395
+ $('#uploadErrorMsg', modalElement).text(error + '.');
2847
396
 
2848
- _setUsersDataProvider(usersData)
2849
- {
2850
- this._setListDataProvider(this._userListBox, usersData);
397
+ // Hide spinner
398
+ $('#uploadSpinner', modalElement).hide();
399
+ }
2851
400
  }
2852
401
 
2853
- _setListDataProvider(listWidget, listData, countUsers = false)
402
+ /**
403
+ * Method called by parent when upload is completed successfully.
404
+ */
405
+ onSslCertUploadSuccess()
2854
406
  {
2855
- // Save reference to selected item name
2856
- // NOTE: as we are substituting the whole data source, we need it to re-select the same item after update
2857
- let selectedDataItem = listWidget.dataItem(listWidget.select());
2858
- let selectedDataItemName = selectedDataItem ? selectedDataItem.name : null;
407
+ let modalElement = $('#uploadModal', $(this));
2859
408
 
2860
- let dataArray = [];
2861
-
2862
- // Convert data coming from server
2863
- for (let i = 0; i < listData.size(); i++)
409
+ if (modalElement)
2864
410
  {
2865
- let itemData = listData.getSFSObject(i);
2866
-
2867
- let item = {};
2868
- item.name = itemData.getUtfString('name');
411
+ // Enable modal
412
+ this.enabled = true;
2869
413
 
2870
- if (countUsers)
2871
- item.users = itemData.getInt('users');
414
+ // Hide spinner
415
+ $('#uploadSpinner', modalElement).hide();
2872
416
 
2873
- dataArray.push(item);
417
+ // Hide modal
418
+ modalElement.modal('hide');
2874
419
  }
420
+ }
2875
421
 
2876
- // Create new data source
2877
- let listDS = new kendo.data.DataSource({
2878
- data: dataArray,
2879
- schema: {
2880
- model: {
2881
- id: 'name'
2882
- }
2883
- },
2884
- sort: {
2885
- field: 'name',
2886
- dir: 'asc'
2887
- }
2888
- });
2889
-
2890
- // Assign data source to list
2891
- listWidget.setDataSource(listDS);
2892
-
2893
- // Select again previously selected item
2894
- if (selectedDataItemName != null)
2895
- {
2896
- selectedDataItem = listDS.get(selectedDataItemName);
422
+ // *****************************************************************
2897
423
 
2898
- if (selectedDataItem)
2899
- {
2900
- let selectedElement = listWidget.wrapper.find('[data-uid="' + selectedDataItem.uid + '"]');
2901
- listWidget.select(selectedElement);
2902
- }
2903
- }
424
+ /*
425
+ * Takes a string and returns the MD5 as Uint8Array
426
+ */
427
+ _binaryMD5(str)
428
+ {
429
+ let MD5 = new SFS2X.MD5();
430
+ let hexStr = MD5.hex_md5(str);
2904
431
 
2905
- // Reset main control state
2906
- this._setMainControlsEnabled();
432
+ return this._hexByteStringToByteArray(hexStr);
2907
433
  }
2908
434
 
2909
- _setGroupsDataProvider(groupsData)
435
+ /*
436
+ * Hex bytes ---> Actual byte[] as Uint8Array
437
+ */
438
+ _hexByteStringToByteArray(hexByteString)
2910
439
  {
2911
- // Create Groups array, adding common entries
2912
- let groupsArray = [
2913
- this._getGroupObj('', this.ANY_LABEL),
2914
- this._getGroupObj(this.DEFAULT_GROUP_NAME)
2915
- ];
2916
-
2917
- // Save reference to selected group name
2918
- // NOTE: as we are substituting the whole data source, we need it to re-select the same item after update
2919
- let selectedDataItem = this._selectedGroup;
2920
- let selectedDataItemName = selectedDataItem ? selectedDataItem.name : null;
2921
-
2922
- if (groupsData != null)
2923
- {
2924
- for (let i = 0; i < groupsData.length; i++)
2925
- {
2926
- if (groupsData[i] != this.DEFAULT_GROUP_NAME) // Default group was added before
2927
- groupsArray.push(this._getGroupObj(groupsData[i]));
2928
- }
2929
- }
2930
-
2931
- // Create new data source
2932
- let groupDS = new kendo.data.DataSource({
2933
- data: groupsArray,
2934
- schema: {
2935
- model: {
2936
- id: 'name'
2937
- }
2938
- }
2939
- });
440
+ let bytes = new Uint8Array(16); // MD5 fixed output size
2940
441
 
2941
- // Assign data source to dropdown
2942
- this._groupsDropDown.setDataSource(groupDS);
442
+ for (let i = 0; i < hexByteString.length;)
443
+ {
444
+ let hexByte = hexByteString[i++] + hexByteString[i++];
445
+ let byte = parseInt(hexByte, 16);
2943
446
 
2944
- //--------------------------------
447
+ bytes[i / 2 - 1] = byte;
448
+ }
2945
449
 
2946
- // Select previously selected item
2947
- selectedDataItem = groupDS.get(selectedDataItemName);
2948
-
2949
- // If group is not found, select the default one
2950
- if (selectedDataItem == undefined)
2951
- selectedDataItem = groupDS.get(this.DEFAULT_GROUP_NAME);
2952
-
2953
- this._groupsDropDown.select(function(dataItem) {
2954
- return dataItem.name === selectedDataItem.name;
2955
- });
450
+ return bytes;
2956
451
  }
2957
452
 
2958
- _getGroupObj(name, label = null)
453
+ /*
454
+ * Encrypt using AES, mode CBC, PKCS#7 padding
455
+ *
456
+ * text -> the text we want to encode
457
+ * key -> the AES key
458
+ * iv -> the AES/CBC init vector
459
+ *
460
+ * Returns -> Uint8Array
461
+ */
462
+ _aesEncrypt(text, key, iv)
2959
463
  {
2960
- return {
2961
- name: name,
2962
- label: (label != null ? label : name)
2963
- }
464
+ let textBytes = aes_js__WEBPACK_IMPORTED_MODULE_1___default.a.utils.utf8.toBytes(text); // Get UTF-8 bytes
465
+ let aesCBC = new aes_js__WEBPACK_IMPORTED_MODULE_1___default.a.ModeOfOperation.cbc(key, iv); // Init CBC mode
466
+ textBytes = aes_js__WEBPACK_IMPORTED_MODULE_1___default.a.padding.pkcs7.pad(textBytes); // PKCS#7 padding
467
+
468
+ // Encrypt
469
+ return aesCBC.encrypt(textBytes);
2964
470
  }
2965
471
 
2966
- /**
2967
- * Update the Scope label in the Rooms panel and in the Users panel.
472
+ /*
473
+ * Encode passed byte array --> Base64 representation
474
+ * Returns --> string
2968
475
  */
2969
- _setScopeLabel()
476
+ _b64Encode(barray)
2970
477
  {
2971
- let roomScope = '';
2972
- let userScope = '';
478
+ return btoa(String.fromCharCode.apply(null, barray));
479
+ }
480
+ }
2973
481
 
2974
- if (this._selectedZone)
2975
- {
2976
- let zoneName = this._selectedZone.name;
2977
- roomScope = this._getScopeLabelPart('Zone:', zoneName);
482
+ // DEFINE COMPONENT
483
+ if (!window.customElements.get('ssl-certificate-manager'))
484
+ window.customElements.define('ssl-certificate-manager', SslCertificateManager);
2978
485
 
2979
- let selectedGroup = this._selectedGroup;
2980
- let groupName = (selectedGroup && selectedGroup.name != '') ? selectedGroup.name : this.ANY_LABEL;
2981
- let roomName = (this._selectedRoom) ? this._selectedRoom.name : this.ANY_LABEL;
486
+ /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
2982
487
 
2983
- userScope = this._getScopeLabelPart('Zone:', zoneName) + '<br>' + this._getScopeLabelPart('Group:', groupName) + '<br>' + this._getScopeLabelPart('Room:', roomName);
2984
- }
488
+ /***/ }),
2985
489
 
2986
- $('#znm-roomScopeLb').html(roomScope);
2987
- $('#znm-userScopeLb').html(userScope);
2988
- }
490
+ /***/ "./src/modules/server-configurator.js":
491
+ /*!********************************************!*\
492
+ !*** ./src/modules/server-configurator.js ***!
493
+ \********************************************/
494
+ /*! exports provided: default */
495
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
2989
496
 
2990
- _getScopeLabelPart(label, text)
2991
- {
2992
- return `<span class="text-muted">${label}</span> <span>${text}</span>`;
2993
- }
497
+ "use strict";
498
+ __webpack_require__.r(__webpack_exports__);
499
+ /* WEBPACK VAR INJECTION */(function($) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ServerConfigurator; });
500
+ /* harmony import */ var _base_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base-module */ "./src/modules/base-module.js");
501
+ /* harmony import */ var _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/uibuilder/config-interface-builder */ "./src/utils/uibuilder/config-interface-builder.js");
502
+ /* harmony import */ var _components_module_specific_ssl_certificate_manager__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/module-specific/ssl-certificate-manager */ "./src/components/module-specific/ssl-certificate-manager.js");
2994
503
 
2995
- _resetRoomList()
2996
- {
2997
- this._roomListBox.setDataSource([]);
2998
504
 
2999
- // Also disable Remove button
3000
- $('#znm-removeRoomBt').attr('disabled', true);
3001
- }
3002
505
 
3003
- _resetUserList()
3004
- {
3005
- this._userListBox.setDataSource([]);
3006
- }
3007
506
 
3008
- _setMainControlsEnabled()
507
+ class ServerConfigurator extends _base_module__WEBPACK_IMPORTED_MODULE_0__["BaseModule"]
508
+ {
509
+ constructor()
3009
510
  {
3010
- let enabled = (this._activePanelType == 'zone' && this._selectedZone != undefined)
3011
- || (this._activePanelType == 'room' && this._selectedRoom != undefined)
3012
- || (this._activePanelType == 'user' && this._selectedUser != undefined);
511
+ super('serverConfig');
3013
512
 
3014
- $('#znm-mainControls').attr('disabled', !enabled);
513
+ // Outgoing requests
514
+ this.REQ_INIT = 'init';
515
+ this.REQ_GET_CONFIG = 'getConfig';
516
+ this.REQ_UPDATE_CONFIG = 'updConfig';
517
+ this.REQ_UPDATE_GEO_DB = 'updGeoDb';
3015
518
 
3016
- $('#znm-monitorTargetLb').text(enabled ? Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["capitalizeFirst"])(this._activePanelType) : 'selection');
3017
- $('#znm-messageIn').attr('placeholder', 'Message to ' + (enabled ? Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["capitalizeFirst"])(this._activePanelType) : 'selection'));
519
+ // Incoming responses
520
+ this.RESP_INIT = 'init';
521
+ this.RESP_CONFIG = 'config';
522
+ this.RESP_CONFIG_UPDATE_CONFIRM = 'configUpdate';
523
+ this.RESP_CONFIG_CHANGED_ALERT = 'configAlert';
524
+ this.RESP_SSL_UPLOAD_ERROR = 'sslUploadError';
525
+ this.RESP_SSL_UPLOAD_CONFIRM = 'sslUpload';
526
+ this.RESP_UPDATE_GEO_DB = 'geoDbUpdate';
3018
527
  }
3019
528
 
3020
- _showLimitExceededWarning(icon, title)
529
+ //------------------------------------
530
+ // COMMON MODULE INTERFACE METHODS
531
+ // This members are used by the main controller
532
+ // to communicate with the module's controller.
533
+ // This methods override those in BaseModule class.
534
+ //------------------------------------
535
+
536
+ initialize(idData, shellController)
3021
537
  {
3022
- icon.attr('title', title);
538
+ // Call super method
539
+ super.initialize(idData, shellController);
3023
540
 
3024
- if (title != '')
3025
- icon.removeClass('hidden');
3026
- else
3027
- icon.addClass('hidden');
3028
- }
541
+ // Initialize progress bar
542
+ $('#src-progressBar').kendoProgressBar({
543
+ min: 0,
544
+ max: 100,
545
+ value: false,
546
+ type: 'value',
547
+ animation: {
548
+ duration: 400
549
+ }
550
+ });
3029
551
 
3030
- _showRoomCreationPanel(roomSettings)
3031
- {
3032
- // Build user interface based on passed data
3033
- this._roomCreationIBuilder.buildInterface(roomSettings, 'znm-roomCreatorTabNav', false, 'rc');
3034
-
3035
- // Enable scrolling tabs
3036
- $('#znm-roomCreatorTabNav > #tabs').scrollingTabs({
3037
- bootstrapVersion: 4,
3038
- scrollToTabEdge: true,
3039
- enableSwiping: true,
3040
- disableScrollArrowsOnFullyScrolled: true,
3041
- cssClassLeftArrow: 'fa fa-chevron-left',
3042
- cssClassRightArrow: 'fa fa-chevron-right'
3043
- });
552
+ // Create interface builder instance
553
+ this._interfaceBuilder = new _utils_uibuilder_config_interface_builder__WEBPACK_IMPORTED_MODULE_1__["ConfigInterfaceBuilder"]();
3044
554
 
3045
- // Reset Notify clients checkbox
3046
- $('#znm-notifyClientsCB').prop('checked', true);
555
+ // Add listener to GeoLite2 database update button
556
+ $('#src-updateGeolocDbButton').on('click', $.proxy(this._onUpdateGeolocDbClick, this));
3047
557
 
3048
- // Set listener for custom actions triggered by configuration interface
3049
- $('#znm-roomCreatorTabNav').on('value-set', $.proxy(this._onConfigValueSet, this));
558
+ // Add listener to interface buttons
559
+ $('#src-reloadButton').on('click', $.proxy(this._onReloadClick, this));
560
+ $('#src-submitButton').on('click', $.proxy(this._onSubmitClick, this));
3050
561
 
3051
- // Display panel
3052
- $('#znm-createRoomModal').modal('show');
562
+ // Save ref to SSL Manager
563
+ this._sslCertManager = document.getElementById('src-sslCertManager');
3053
564
 
3054
- // Reset and hide error message
3055
- this._resetRoomCreationError();
565
+ //-----------------------------------*/
566
+
567
+ // Send initialization request
568
+ this.sendExtensionRequest(this.REQ_INIT);
3056
569
  }
3057
570
 
3058
- _resetRoomCreationError()
571
+ destroy()
3059
572
  {
3060
- // Reset and hide error message
3061
- $('#znm-createRoomError').text('');
3062
- $('#znm-createRoomError').hide();
573
+ // Call super method
574
+ super.destroy();
575
+
576
+ // Destroy SSL Certificate Manager
577
+ this._sslCertManager.destroy();
578
+
579
+ // Remove interface buttons click listeners
580
+ $('#src-updateGeolocDbButton').off('click');
581
+ $('#src-reloadButton').off('click');
582
+ $('#src-submitButton').off('click');
583
+
584
+ // Clear tabs container
585
+ this._clearTabs();
3063
586
  }
3064
587
 
3065
- _validateNewRoomName(changes)
588
+ onExtensionCommand(command, data)
3066
589
  {
3067
- for (let i = 0; i < changes.size(); i++)
590
+ // Initialization data received
591
+ if (command == this.RESP_INIT)
3068
592
  {
3069
- const setting = changes.getSFSObject(i);
593
+ // Retrieve module id sent by the server (required because multiple modules use file uploading service)
594
+ const uploadModuleId = data.getUtfString('modId');
3070
595
 
3071
- if (setting.containsKey('name') && setting.getUtfString('name') == 'name')
3072
- {
3073
- // Get name value
3074
- const name = setting.getText('value');
596
+ // Set SSL upload manager target configuration
597
+ this._sslCertManager.uploadTargetConfig = {
598
+ sessionToken: this.smartFox.sessionToken,
599
+ host: this.smartFox.config.host,
600
+ port: this.smartFox.config.port,
601
+ moduleId: uploadModuleId,
602
+ protocol: this.smartFox.config.useSSL ? 'https' : 'http'
603
+ };
3075
604
 
3076
- // Get data source
3077
- const ds = this._roomListBox.dataSource.data();
605
+ // Server sends a flag indicating if file uploads are locked
606
+ // We use it to enable the "Manage SSL certificate" button
607
+ this._sslLocked = data.getBool('lock');
3078
608
 
3079
- // Check if name exists in data source
3080
- for (let j = 0; j < ds.length; j++)
3081
- {
3082
- if (ds[j].name == name)
3083
- return false;
3084
- }
609
+ if (!this._sslLocked)
610
+ $('#src-manageSslWarn').hide();
3085
611
 
3086
- break;
3087
- }
612
+ // Request configuration data to server instance
613
+ this.sendExtensionRequest(this.REQ_GET_CONFIG);
3088
614
  }
3089
615
 
3090
- return true;
3091
- }
616
+ // Server configuration data received
617
+ else if (command == this.RESP_CONFIG)
618
+ {
619
+ // Build user interface based on received data
620
+ this._interfaceBuilder.buildInterface(data.getSFSArray('settings'), 'src-tabNavigator', false);
3092
621
 
3093
- _enableCreateRoomPanel(enable)
3094
- {
3095
- let modalElement = $('#znm-createRoomModal');
622
+ // Enable buttons
623
+ this._enableButtons(true);
3096
624
 
3097
- // Enable modal close buttons
3098
- $('button[data-dismiss="modal"]', modalElement).attr('disabled', !enable);
625
+ // Initialize TabNavigator-ralated widgets
626
+ if (!this._tabNavInitialized)
627
+ {
628
+ // Enable scrolling tabs
629
+ $('#src-tabNavigator > #tabs').scrollingTabs({
630
+ bootstrapVersion: 4,
631
+ scrollToTabEdge: true,
632
+ enableSwiping: true,
633
+ disableScrollArrowsOnFullyScrolled: true,
634
+ cssClassLeftArrow: 'fa fa-chevron-left',
635
+ cssClassRightArrow: 'fa fa-chevron-right'
636
+ });
637
+
638
+ this._tabNavInitialized = true;
639
+ }
3099
640
 
3100
- // Enable create button
3101
- $('#znm-roomCreatorCreateBt', modalElement).attr('disabled', !enable);
641
+ // Run validation (to remove validation messages if data was reloaded)
642
+ this._interfaceBuilder.checkIsValid();
3102
643
 
3103
- // Enable checkbox
3104
- $('#znm-notifyClientsCB', modalElement).attr('disabled', !enable);
644
+ this._switchView('src-main');
645
+ }
3105
646
 
3106
- // Enable configuration interface
3107
- this._roomCreationIBuilder.disableInterface(!enable);
3108
- }
647
+ // Server configuration update confirmation
648
+ else if (command == this.RESP_CONFIG_UPDATE_CONFIRM)
649
+ {
650
+ // Enable buttons
651
+ this._enableButtons(true);
3109
652
 
3110
- _clearTabs(monitoredType)
3111
- {
3112
- // Destroy scrolling tabs
3113
- $(`#znm-${monitoredType}TabNavigator #tabs`).scrollingTabs('destroy');
653
+ // Enable form items
654
+ this._interfaceBuilder.disableInterface(false);
3114
655
 
3115
- // Remove all tab navigator content
3116
- this._interfaceBuilder.destroyInterface();
656
+ // If the current user is the updater, show a notification
657
+ // Otherwise, show a dialog box suggesting to reload
658
+ let updater = data.getUtfString('user');
3117
659
 
3118
- // Set all tab panels as inactive
3119
- $(`#znm-${monitoredType}TabNavigator #tabPanels .tab-pane`).removeClass('show active');
660
+ if (updater == this.smartFox.mySelf.name)
661
+ {
662
+ // Reset the 'modified' flag
663
+ this._interfaceBuilder.resetIsModified();
3120
664
 
3121
- // Remove tab change event listener
3122
- $('a[data-toggle="tab"]').off('shown.bs.tab');
665
+ // Display notification
666
+ this.shellCtrl.showNotification('Server settings updated', 'Immediate changes (<i class="fas fa-bolt"></i>) have been applied; all other changes will be active after next server restart');
667
+ }
668
+ else
669
+ {
670
+ // Show alert
671
+ this.shellCtrl.showSimpleAlert(`Administrator ${updater} has modified the server settings; please reload to update your view.`);
3123
672
 
3124
- this._skipInitTabs = false;
3125
- }
673
+ // Disable submit button
674
+ $('#src-submitButton').attr('disabled', true);
675
+ }
676
+ }
3126
677
 
3127
- _showZoneTrafficData(data)
3128
- {
3129
- // Build user count monitor history
3130
- if (data.containsKey('history'))
678
+ // Server configuration xml saved by an external process
679
+ else if (command == this.RESP_CONFIG_CHANGED_ALERT)
3131
680
  {
3132
- let userCountData = data.getIntArray('history');
3133
- let samplingRateMins = data.getInt('rate');
681
+ // Show alert
682
+ this.shellCtrl.showSimpleAlert(`The system has modified the server settings automatically; please reload to update your view.`);
3134
683
 
3135
- this._userCountChart.addHistoryEntries(userCountData, samplingRateMins * 60);
684
+ // Disable submit button
685
+ $('#src-submitButton').attr('disabled', true);
3136
686
  }
3137
687
 
3138
- // Add last user count value
3139
- this._userCountChart.addEntry(data.getInt('current'));
3140
- }
3141
-
3142
- _addLogEntryToGrid(grid, item)
3143
- {
3144
- let ds = grid.dataSource;
3145
-
3146
- // Check items limit
3147
- if (ds.total() == this.MAX_EXTENSION_LOG_SIZE)
3148
- ds.remove(ds.at(0));
688
+ // SSL certificate upload error
689
+ else if (command == this.RESP_SSL_UPLOAD_ERROR)
690
+ {
691
+ const error = data.getUtfString('error');
3149
692
 
3150
- ds.add(item);
3151
- }
693
+ // Log warning
694
+ this.shellCtrl.logMessage(error, 'error');
3152
695
 
3153
- _showUserStatsData(data)
3154
- {
3155
- // Packet queue filling %
3156
- let packetQueue = Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["roundToDecimals"])(data.getFloat('packet'), 2);
3157
- this._packetQueueChart.setDataSource([{category: 'Queue', value: packetQueue}]);
696
+ // Show error in manager window
697
+ this._sslCertManager.onSslCertUploadError(error);
698
+ }
3158
699
 
3159
- // Dropped messages
3160
- let droppedMsg = data.getInt('dropped');
3161
- this._droppedMsgChart.setDataSource([{category: 'Dropped', value: droppedMsg}]);
700
+ // SSL certificate upload confirmed
701
+ else if (command == this.RESP_SSL_UPLOAD_CONFIRM)
702
+ {
703
+ // Closw manager window
704
+ this._sslCertManager.onSslCertUploadSuccess();
3162
705
 
3163
- // Written data amount
3164
- let writtenDataObj = Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["scaleBytes"])(data.getLong('wBytes'), 2);
3165
- this._writtenDataChart.setDataSource([{category: 'Written', value: writtenDataObj.value, unit: writtenDataObj.unit}]);
706
+ let updater = data.getUtfString('user');
3166
707
 
3167
- // Read data amount
3168
- let readDataObj = Object(_utils_utilities__WEBPACK_IMPORTED_MODULE_4__["scaleBytes"])(data.getLong('rBytes'), 2);
3169
- this._readDataChart.setDataSource([{category: 'Read', value: readDataObj.value, unit: readDataObj.unit}]);
3170
- }
708
+ // Display notification
709
+ if (updater == this.smartFox.mySelf.name)
710
+ this.shellCtrl.showNotification('SSL certificate', 'SSL certificate keystore was uploaded successfully');
711
+ else
712
+ this.shellCtrl.showNotification('SSL certificate', `Administrator ${updater} has uploaded a new SSL certificate keystore`);
3171
713
 
3172
- _setGeoLocationUI(geoLocData)
3173
- {
3174
- const location = geoLocData.getUtfString('location');
3175
- const error = geoLocData.getUtfString('error');
3176
- const latitude = geoLocData.getUtfString('latitude');
3177
- const longitude = geoLocData.getUtfString('longitude');
714
+ // When a certificate is uploaded, HTTPS is also enabled automatically:
715
+ // we have to update the interface accordingly
716
+ this._updateConfigFormItemDisplayedValue('webServer.enableHttps', true);
717
+ }
3178
718
 
3179
- // Show location
3180
- $('#znm-geoLocationLb').text(location);
719
+ // Geolocation database update confirmation
720
+ else if (command == this.RESP_UPDATE_GEO_DB)
721
+ {
722
+ // Enable button
723
+ $('#src-updateGeolocDbButton').attr('disabled', false);
3181
724
 
3182
- // Log error
3183
- if (error != '')
3184
- this.shellCtrl.logMessage(error, 'warn');
725
+ // Check success
726
+ if (data.getBool('success'))
727
+ {
728
+ // Update displayed date
729
+ this._updateConfigFormItemDisplayedValue('adminHelper.geoDbReleaseDate', data.getUtfString('newRelDate'));
3185
730
 
3186
- // Enable/disable Show map button
3187
- if (latitude != '' && longitude != '')
3188
- {
3189
- $('#znm-showMapBt').attr('disabled', false);
731
+ // If the current user is the updater, also show a notification
732
+ let updater = data.getUtfString('user');
3190
733
 
3191
- const url = `https://www.google.com/maps/search/?api=1&query=${Number(latitude) + ',' + Number(longitude)}`;
3192
- $('#znm-showMapBt').attr('href', url);
734
+ if (updater == this.smartFox.mySelf.name)
735
+ this.shellCtrl.showNotification('Geolocation database updated', 'Latest release of the GeoLite2 Country database has been installed successfully');
736
+ }
737
+ else
738
+ {
739
+ // Show alert
740
+ this.shellCtrl.showSimpleAlert(data.getUtfString('error'));
741
+ }
3193
742
  }
3194
743
  }
3195
744
 
3196
- _clearGeoLocationUI()
745
+ //------------------------------------
746
+ // PRIVATE METHODS
747
+ //------------------------------------
748
+
749
+ _enableButtons(enabled)
3197
750
  {
3198
- $('#znm-geoLocationLb').text('');
3199
- $('#znm-showMapBt').attr('disabled', true);
3200
- $('#znm-showMapBt').attr('href', '#');
3201
- }
751
+ $('#src-reloadButton').attr('disabled', !enabled);
752
+ $('#src-submitButton').attr('disabled', !enabled);
753
+ $('#src-backupCheck').attr('disabled', !enabled);
3202
754
 
3203
- //---------------------------------
3204
- // PRIVATE GETTERS
3205
- //---------------------------------
755
+ $('#src-updateGeolocDbButton').attr('disabled', !enabled);
3206
756
 
3207
- get _activePanelType()
3208
- {
3209
- return this._accordion.select().data('itemType');
757
+ if (!this._sslLocked)
758
+ this._sslCertManager.enabled = enabled;
3210
759
  }
3211
760
 
3212
- get _selectedZone()
761
+ _switchView(viewId)
3213
762
  {
3214
- return this._zoneListBox.dataItem(this._zoneListBox.select());
763
+ document.getElementById('src-viewstack').selectedElement = document.getElementById(viewId);
3215
764
  }
3216
765
 
3217
- get _selectedGroup()
766
+ _clearTabs()
3218
767
  {
3219
- return this._groupsDropDown.dataItem(this._groupsDropDown.select());
3220
- }
768
+ // Destroy scrolling tabs
769
+ $('#src-tabNavigator #tabs').scrollingTabs('destroy');
3221
770
 
3222
- get _selectedRoom()
3223
- {
3224
- return this._roomListBox.dataItem(this._roomListBox.select());
771
+ // Remove all tab navigator content
772
+ this._interfaceBuilder.destroyInterface();
3225
773
  }
3226
774
 
3227
- get _selectedUser()
775
+ _onUpdateGeolocDbClick()
3228
776
  {
3229
- return this._userListBox.dataItem(this._userListBox.select());
3230
- }
777
+ // Disable button
778
+ $('#src-updateGeolocDbButton').attr('disabled', true);
3231
779
 
3232
- get _isEditing()
3233
- {
3234
- return (this._monitoredType != null && this._monitoredName != null && this._isEditMode);
780
+ // Send request to server
781
+ this.sendExtensionRequest(this.REQ_UPDATE_GEO_DB);
3235
782
  }
3236
- }
3237
-
3238
- /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
3239
783
 
3240
- /***/ }),
784
+ _onReloadClick()
785
+ {
786
+ // Disable buttons
787
+ this._enableButtons(false);
3241
788
 
3242
- /***/ "./src/utils/match-properties.js":
3243
- /*!***************************************!*\
3244
- !*** ./src/utils/match-properties.js ***!
3245
- \***************************************/
3246
- /*! exports provided: RoomPropertiesData, UserPropertiesData */
3247
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
789
+ // Switch to loading view
790
+ this._switchView('src-loading');
3248
791
 
3249
- "use strict";
3250
- __webpack_require__.r(__webpack_exports__);
3251
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RoomPropertiesData", function() { return RoomPropertiesData; });
3252
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UserPropertiesData", function() { return UserPropertiesData; });
3253
- class RoomPropertiesData
3254
- {
3255
- static get LABELS() {
3256
- let map = new Map();
3257
-
3258
- map.set(SFS2X.RoomProperties.NAME, 'name');
3259
- map.set(SFS2X.RoomProperties.GROUP_ID, 'groupId');
3260
- map.set(SFS2X.RoomProperties.MAX_USERS, 'maxUsers');
3261
- map.set(SFS2X.RoomProperties.MAX_SPECTATORS, 'maxSpectators');
3262
- map.set(SFS2X.RoomProperties.USER_COUNT, 'userCount');
3263
- map.set(SFS2X.RoomProperties.SPECTATOR_COUNT, 'spectatorCount');
3264
- map.set(SFS2X.RoomProperties.IS_GAME, 'isGame');
3265
- map.set(SFS2X.RoomProperties.IS_PRIVATE, 'isPrivate');
3266
- map.set(SFS2X.RoomProperties.HAS_FREE_PLAYER_SLOTS, 'hasFreePlayerSlots');
3267
- map.set(SFS2X.RoomProperties.IS_TYPE_SFSGAME, 'isSFSGameType');
3268
-
3269
- return map;
3270
- }
792
+ // Hide validation messages
793
+ this._interfaceBuilder.resetValidation();
3271
794
 
3272
- static getLabel(propName)
3273
- {
3274
- return 'Room.' + RoomPropertiesData.LABELS.get(propName);
795
+ // Request configuration data to server instance
796
+ this.sendExtensionRequest(this.REQ_GET_CONFIG);
3275
797
  }
3276
798
 
3277
- static get propertiesArray()
799
+ _onSubmitClick()
3278
800
  {
3279
- const arr = [];
3280
-
3281
- for (const [key, value] of RoomPropertiesData.LABELS.entries())
801
+ // Check validity
802
+ if (this._interfaceBuilder.checkIsValid())
3282
803
  {
3283
- arr.push({
3284
- label: RoomPropertiesData.getLabel(key),
3285
- value: key
3286
- });
3287
- }
3288
-
3289
- return arr;
3290
- }
3291
- }
804
+ let changes = this._interfaceBuilder.getChangedData();
3292
805
 
3293
- class UserPropertiesData
3294
- {
3295
- static get LABELS() {
3296
- let map = new Map();
806
+ if (changes.size() > 0)
807
+ {
808
+ // Disable buttons
809
+ this._enableButtons(false);
3297
810
 
3298
- map.set(SFS2X.UserProperties.NAME, 'name');
3299
- map.set(SFS2X.UserProperties.IS_PLAYER, 'isPlayer');
3300
- map.set(SFS2X.UserProperties.IS_SPECTATOR, 'isSpectator');
3301
- map.set(SFS2X.UserProperties.IS_NPC, 'isNPC');
3302
- map.set(SFS2X.UserProperties.PRIVILEGE_ID, 'privilegeId');
811
+ // Disable form items
812
+ this._interfaceBuilder.disableInterface(true);
3303
813
 
3304
- return map;
3305
- }
814
+ // Send updated settings to server instance
815
+ let params = new SFS2X.SFSObject();
816
+ params.putSFSArray('settings', changes);
817
+ params.putBool('backup', $('#src-backupCheck').prop('checked'));
3306
818
 
3307
- static getLabel(propName)
3308
- {
3309
- return 'User.' + UserPropertiesData.LABELS.get(propName);
819
+ this.sendExtensionRequest(this.REQ_UPDATE_CONFIG, params);
820
+ }
821
+ }
822
+ else
823
+ this.shellCtrl.showSimpleAlert('Unable to submit configuration changes due to an invalid value; please verify the highlighted form fields in all tabs.', true);
3310
824
  }
3311
825
 
3312
- static get propertiesArray()
826
+ _updateConfigFormItemDisplayedValue(configParamName, newValue)
3313
827
  {
3314
- const arr = [];
828
+ // Get the relevant Configuration Form Item
829
+ const configFormItem = this._interfaceBuilder.getConfigFormItem(configParamName);
3315
830
 
3316
- for (const [key, value] of UserPropertiesData.LABELS.entries())
3317
- {
3318
- arr.push({
3319
- label: UserPropertiesData.getLabel(key),
3320
- value: key
3321
- });
3322
- }
831
+ // Update Configuration Parameter associated with the Configuration Form Item
832
+ configFormItem.data.value = newValue;
833
+ configFormItem.data.resetIsModified(); // This is needed to avoid the Configuration Parameter to flagged as 'changed'
3323
834
 
3324
- return arr;
835
+ // Display the new value of the Configuration Form Item
836
+ configFormItem._setWidgetValue(); // Display the new value in the config form item
3325
837
  }
3326
838
  }
3327
839
 
840
+ /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "jquery")))
3328
841
 
3329
842
  /***/ })
3330
843
 
3331
844
  }]);
3332
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2pzL2NvcmUvbW9kdWxlcy9tb2R1bGUtMTMuYnVuZGxlLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvY29tcG9uZW50cy9jaGFydHMvY2hhcnQtdXRpbHMuanMiLCJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvY29tcG9uZW50cy9jaGFydHMvdXNlci1jb3VudC1jaGFydC5qcyIsIndlYnBhY2s6Ly9hcHBsaWNhdGlvbi8uL3NyYy9jb21wb25lbnRzL21vZHVsZS1zcGVjaWZpYy9tYXRjaC1leHByZXNzaW9uLWJ1aWxkZXIuanMiLCJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvY29tcG9uZW50cy9zaWRlYmFyLWxheW91dC5qcyIsIndlYnBhY2s6Ly9hcHBsaWNhdGlvbi8uL3NyYy9tb2R1bGVzL3pvbmUtbW9uaXRvci5qcyIsIndlYnBhY2s6Ly9hcHBsaWNhdGlvbi8uL3NyYy91dGlscy9tYXRjaC1wcm9wZXJ0aWVzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7Ynl0ZXNUb1NpemUsIGtCeXRlc1RvU2l6ZX0gZnJvbSAnLi4vLi4vdXRpbHMvdXRpbGl0aWVzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZvcm1hdHRlZERhdGVUaW1lKGRhdGUsIGZvcm1hdCA9ICdkZCBNTU0gSEg6bW06c3MnKVxue1xuXHRyZXR1cm4ga2VuZG8udG9TdHJpbmcoZGF0ZSwgZm9ybWF0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEJhc2ljU2hhcmVkVGVtcGxhdGUodmFsdWVGb3JtYXQgPSAnezB9Jykge1xuXHRyZXR1cm4ga2VuZG8udGVtcGxhdGUoYFxuXHRcdDxkaXYgY2xhc3M9XCJjaGFydC10b29sdGlwLWNhdGVnb3J5XCI+PHN0cm9uZz4jOiBjYXRlZ29yeSAjPC9zdHJvbmc+PC9kaXY+XG5cdFx0IyBmb3IgKHZhciBpID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkrKykgeyAjXG5cdFx0PGRpdj5cblx0XHRcdDxpIGNsYXNzPVwiZmFzIGZhLXNxdWFyZVwiIHN0eWxlPVwiY29sb3I6ICM6IHBvaW50c1tpXS5jb2xvciAjIFwiPjwvaT5cblx0XHRcdCM6IHBvaW50c1tpXS5zZXJpZXMubmFtZSMgOiAjOiBrZW5kby5mb3JtYXQoJyR7dmFsdWVGb3JtYXR9JywgcG9pbnRzW2ldLnZhbHVlKSAjXG5cdFx0PC9kaXY+XG5cdFx0IyB9ICNcblx0YCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNZW1vcnlTaGFyZWRUZW1wbGF0ZSgpIHtcblx0cmV0dXJuIGZ1bmN0aW9uKGRhdGFJdGVtKSB7XG5cdFx0ZGF0YUl0ZW0uYnl0ZXNUb1NpemUgPSBieXRlc1RvU2l6ZTsgLy8gUGFzcyBieXRlc1RvU2l6ZSB1dGlsaXR5IGZ1bmN0aW9uIHRvIHRlbXBsYXRlXG5cdFx0cmV0dXJuIGtlbmRvLnRlbXBsYXRlKGBcblx0XHRcdDxkaXYgY2xhc3M9XCJjaGFydC10b29sdGlwLWNhdGVnb3J5XCI+PHN0cm9uZz4jOiBjYXRlZ29yeSAjPC9zdHJvbmc+PC9kaXY+XG5cdFx0XHQjIGZvciAodmFyIGkgPSAwOyBpIDwgcG9pbnRzLmxlbmd0aDsgaSsrKSB7ICNcblx0XHRcdDxkaXY+XG5cdFx0XHRcdDxpIGNsYXNzPVwiZmFzIGZhLXNxdWFyZVwiIHN0eWxlPVwiY29sb3I6ICM6IHBvaW50c1tpXS5jb2xvciAjIFwiPjwvaT5cblx0XHRcdFx0IzogcG9pbnRzW2ldLnNlcmllcy5uYW1lIyA6ICM6IGJ5dGVzVG9TaXplKHBvaW50c1tpXS52YWx1ZSkgI1xuXHRcdFx0PC9kaXY+XG5cdFx0XHQjIH0gI1xuXHRcdGApKGRhdGFJdGVtKTtcblx0fTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE1lbW9yeVZhbHVlQXhpc0xhYmVsVGVtcGxhdGUoKSB7XG5cdHJldHVybiBmdW5jdGlvbihkYXRhSXRlbSkge1xuXHRcdGRhdGFJdGVtLmJ5dGVzVG9TaXplID0gYnl0ZXNUb1NpemU7IC8vIFBhc3MgYnl0ZXNUb1NpemUgdXRpbGl0eSBmdW5jdGlvbiB0byB0ZW1wbGF0ZVxuXHRcdHJldHVybiBrZW5kby50ZW1wbGF0ZShgXG5cdFx0XHQjOiBieXRlc1RvU2l6ZSh2YWx1ZSkgI1xuXHRcdGApKGRhdGFJdGVtKTtcblx0fTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ldHdvcmtTaGFyZWRUZW1wbGF0ZShjYXRlZ29yeUZvcm1hdCA9ICdkZCBNTU0gSEg6bW06c3MnKSB7XG5cdHJldHVybiBmdW5jdGlvbihkYXRhSXRlbSkge1xuXHRcdGRhdGFJdGVtLmtCeXRlc1RvU2l6ZSA9IGtCeXRlc1RvU2l6ZTsgLy8gUGFzcyBrQnl0ZXNUb1NpemUgdXRpbGl0eSBmdW5jdGlvbiB0byB0ZW1wbGF0ZVxuXHRcdHJldHVybiBrZW5kby50ZW1wbGF0ZShgXG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY2hhcnQtdG9vbHRpcC1jYXRlZ29yeVwiPjxzdHJvbmc+Izoga2VuZG8udG9TdHJpbmcoY2F0ZWdvcnksICcke2NhdGVnb3J5Rm9ybWF0fScpICM8L3N0cm9uZz48L2Rpdj5cblx0XHRcdCMgZm9yICh2YXIgaSA9IDA7IGkgPCBwb2ludHMubGVuZ3RoOyBpKyspIHsgI1xuXHRcdFx0PGRpdj5cblx0XHRcdFx0PGkgY2xhc3M9XCJmYXMgZmEtc3F1YXJlXCIgc3R5bGU9XCJjb2xvcjogIzogcG9pbnRzW2ldLmNvbG9yICMgXCI+PC9pPlxuXHRcdFx0XHQjOiBwb2ludHNbaV0uc2VyaWVzLm5hbWUjIDogIzoga0J5dGVzVG9TaXplKHBvaW50c1tpXS52YWx1ZSwgMSwgJycsICcvcycpICNcblx0XHRcdDwvZGl2PlxuXHRcdFx0IyB9ICNcblx0XHRgKShkYXRhSXRlbSk7XG5cdH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXROZXR3b3JrVmFsdWVBeGlzTGFiZWxUZW1wbGF0ZSgpIHtcblx0cmV0dXJuIGZ1bmN0aW9uKGRhdGFJdGVtKSB7XG5cdFx0ZGF0YUl0ZW0ua0J5dGVzVG9TaXplID0ga0J5dGVzVG9TaXplOyAvLyBQYXNzIGtCeXRlc1RvU2l6ZSB1dGlsaXR5IGZ1bmN0aW9uIHRvIHRlbXBsYXRlXG5cdFx0cmV0dXJuIGtlbmRvLnRlbXBsYXRlKGBcblx0XHRcdCM6IGtCeXRlc1RvU2l6ZSh2YWx1ZSwgMSwgJycsICcvcycpICNcblx0XHRgKShkYXRhSXRlbSk7XG5cdH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRVc2VyQ291bnRTaGFyZWRUZW1wbGF0ZShjYXRlZ29yeUZvcm1hdCA9ICdkZCBNTU0gSEg6bW06c3MnKSB7XG5cdHJldHVybiBmdW5jdGlvbihkYXRhSXRlbSkge1xuXHRcdHJldHVybiBrZW5kby50ZW1wbGF0ZShgXG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY2hhcnQtdG9vbHRpcC1jYXRlZ29yeVwiPjxzdHJvbmc+Izoga2VuZG8udG9TdHJpbmcoY2F0ZWdvcnksICcke2NhdGVnb3J5Rm9ybWF0fScpICM8L3N0cm9uZz48L2Rpdj5cblx0XHRcdCMgZm9yICh2YXIgaSA9IDA7IGkgPCBwb2ludHMubGVuZ3RoOyBpKyspIHsgI1xuXHRcdFx0PGRpdj5cblx0XHRcdFx0PGkgY2xhc3M9XCJmYXMgZmEtc3F1YXJlXCIgc3R5bGU9XCJjb2xvcjogIzogcG9pbnRzW2ldLmNvbG9yICMgXCI+PC9pPlxuXHRcdFx0XHQjOiBwb2ludHNbaV0uc2VyaWVzLm5hbWUjIDogIzogcG9pbnRzW2ldLnZhbHVlICNcblx0XHRcdDwvZGl2PlxuXHRcdFx0IyB9ICNcblx0XHRgKShkYXRhSXRlbSk7XG5cdH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUaW1lUmFuZ2VUb29sdGlwVGVtcGxhdGUoKSB7XG5cdHJldHVybiBmdW5jdGlvbihkYXRhSXRlbSkge1xuXHRcdGRhdGFJdGVtLmdldFRvb2x0aXBTdHJpbmcgPSBmdW5jdGlvbih2MSwgdjIpIHtcblx0XHRcdGlmICh2MSA9PSB2Milcblx0XHRcdHtcblx0XHRcdFx0aWYgKHYyID09IC0yNClcblx0XHRcdFx0XHR2MiA9IC0yMztcblxuXHRcdFx0XHR2MSA9IHYyIC0gMTtcblx0XHRcdH1cblxuXHRcdFx0bGV0IHN0YXJ0ID0gTWF0aC5hYnModjEpO1xuXHRcdFx0bGV0IGVuZCA9IE1hdGguYWJzKHYyKTtcblxuXHRcdFx0cmV0dXJuIGBGcm9tICR7c3RhcnR9IGhvdXIke3N0YXJ0ICE9IDEgPyAncycgOiAnJ30gYWdvIHRvICR7ZW5kID4gMCA/ICgndG8gJyArIGVuZCArICcgaG91cicgKyAoZW5kICE9IDEgPyAncycgOiAnJykgKyAnIGFnbycpIDogJ25vdyd9YDtcblx0XHR9OyAvLyBQYXNzIHV0aWxpdHkgZnVuY3Rpb24gdG8gdGVtcGxhdGVcblx0XHRyZXR1cm4ga2VuZG8udGVtcGxhdGUoYFxuXHRcdFx0IzogZ2V0VG9vbHRpcFN0cmluZyhzZWxlY3Rpb25TdGFydCwgc2VsZWN0aW9uRW5kKSAjXG5cdFx0YCkoZGF0YUl0ZW0pO1xuXHR9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0V1JCeXRlc0xhYmVsVGVtcGxhdGUoKSB7XG5cdHJldHVybiBmdW5jdGlvbihkYXRhSXRlbSkge1xuXHRcdHJldHVybiBrZW5kby50ZW1wbGF0ZShgXG5cdFx0XHQjOiB2YWx1ZSAjICM6IGRhdGFJdGVtLnVuaXQgI1xuXHRcdGApKGRhdGFJdGVtKTtcblx0fTtcbn1cbiIsImltcG9ydCB7Z2V0VXNlckNvdW50U2hhcmVkVGVtcGxhdGV9IGZyb20gJy4vY2hhcnQtdXRpbHMnO1xuaW1wb3J0ICogYXMgbW9tZW50IGZyb20gJ21vbWVudCc7XG5cbmV4cG9ydCBjbGFzcyBVc2VyQ291bnRDaGFydCBleHRlbmRzIEhUTUxFbGVtZW50XG57XG5cdGNvbnN0cnVjdG9yKClcblx0e1xuXHQgICAgc3VwZXIoKTtcblxuXHRcdC8vIENyZWF0ZSBjaGFydCBodG1sXG5cdFx0bGV0IGNoYXJ0SHRtbCA9ICQoJzxkaXY+Jyk7XG5cdFx0JCh0aGlzKS5hcHBlbmQoY2hhcnRIdG1sKTtcblxuXHRcdC8vIEluaXRpYWxpemUgY2hhcnRcblx0XHR0aGlzLl9jaGFydFdpZGdldCA9IHRoaXMuX2luaXRDaGFydFdpZGdldChjaGFydEh0bWwpO1xuXHR9XG5cblx0X2luaXRDaGFydFdpZGdldChjaGFydEh0bWwpXG5cdHtcblx0XHRyZXR1cm4gY2hhcnRIdG1sLmtlbmRvQ2hhcnQoe1xuXHRcdFx0dHJhbnNpdGlvbnM6IGZhbHNlLFxuXHRcdFx0Y2hhcnRBcmVhOiB7XG5cdFx0XHRcdGhlaWdodDogMjcwLFxuXHRcdFx0XHRiYWNrZ3JvdW5kOiAndHJhbnNwYXJlbnQnXG5cdFx0XHR9LFxuXHQgICAgICAgIGxlZ2VuZDoge1xuXHQgICAgICAgICAgICB2aXNpYmxlOiBmYWxzZVxuXHQgICAgICAgIH0sXG5cdCAgICAgICAgc2VyaWVzRGVmYXVsdHM6IHtcblx0ICAgICAgICAgICAgdHlwZTogJ2FyZWEnLFxuXHQgICAgICAgICAgICBsYWJlbHM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IGZhbHNlLFxuXHQgICAgICAgICAgICAgICAgZm9ybWF0OiAnezB9Jyxcblx0ICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6ICd0cmFuc3BhcmVudCdcblx0ICAgICAgICAgICAgfSxcblx0ICAgICAgICB9LFxuXHQgICAgICAgIHNlcmllczogW3tcblx0XHRcdFx0bmFtZTogJ1VzZXJzJyxcblx0ICAgICAgICAgICAgZmllbGQ6ICd1c2VycycsXG5cdCAgICAgICAgICAgIGNhdGVnb3J5RmllbGQ6ICd0aW1lJyxcblx0XHRcdFx0Y29sb3I6ICcjZmQ3ZDI0Jyxcblx0XHRcdFx0bGluZToge1xuXHRcdFx0XHRcdHdpZHRoOiAyXG5cdFx0XHRcdH1cblx0ICAgICAgICB9XSxcblx0ICAgICAgICB2YWx1ZUF4aXM6IHtcblx0ICAgICAgICAgICAgbGFiZWxzOiB7XG5cdCAgICAgICAgICAgICAgICBmb3JtYXQ6ICd7MH0nLFxuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICBsaW5lOiB7XG5cdCAgICAgICAgICAgICAgICB2aXNpYmxlOiBmYWxzZVxuXHQgICAgICAgICAgICB9LFxuXHRcdFx0XHRtaW46IDAsXG5cdCAgICAgICAgfSxcblx0ICAgICAgICBjYXRlZ29yeUF4aXM6IHtcblx0XHRcdFx0bWFqb3JHcmlkTGluZXM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IHRydWUsXG5cdFx0XHRcdFx0c3RlcDogNjBcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0bWFqb3JUaWNrczoge1xuXHRcdFx0XHRcdHZpc2libGU6IHRydWUsXG5cdFx0XHRcdFx0c3RlcDogNjBcblx0XHRcdFx0fSxcblx0XHRcdFx0bGFiZWxzOiB7XG5cdFx0XHRcdFx0dmlzaWJsZTogdHJ1ZSxcblx0XHRcdFx0XHRzdGVwOiA2MFxuXHRcdFx0XHR9LFxuXHRcdFx0XHRiYXNlVW5pdDogJ21pbnV0ZXMnLFxuXHRcdFx0XHR0eXBlOiAnZGF0ZScsXG5cdCAgICAgICAgfSxcblx0ICAgICAgICB0b29sdGlwOiB7XG5cdFx0XHRcdHNoYXJlZDogdHJ1ZSxcblx0ICAgICAgICAgICAgdmlzaWJsZTogdHJ1ZSxcblx0XHRcdFx0c2hhcmVkVGVtcGxhdGU6IGdldFVzZXJDb3VudFNoYXJlZFRlbXBsYXRlKCdkZCBNTU0gSEg6bW0nKSxcblx0XHRcdFx0YmFja2dyb3VuZDogJyNlNGU0ZTQnXG5cdCAgICAgICAgfVxuXHQgICAgfSkuZGF0YSgna2VuZG9DaGFydCcpO1xuXHR9XG5cblx0cmVkcmF3KClcblx0e1xuXHRcdHRoaXMuX2NoYXJ0V2lkZ2V0LnJlZHJhdygpO1xuXHR9XG5cblx0c2V0IHJhbmdlKHZhbHVlcylcblx0e1xuXHRcdHRoaXMuX3JhbmdlID0gdmFsdWVzO1xuXG5cdFx0dGhpcy5fdXBkYXRlWEF4aXNMaW1pdHMoKTtcblx0fVxuXG5cdGdldCByYW5nZSgpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5fcmFuZ2U7XG5cdH1cblxuXHRhZGRIaXN0b3J5RW50cmllcyh1c2VyQ291bnREYXRhLCBzYW1wbGluZ1JhdGVTZWNvbmRzKVxuXHR7XG5cdFx0bGV0IGRhdGUgPSBuZXcgRGF0ZSgpO1xuXHRcdGxldCBzdGFydCA9IG1vbWVudChkYXRlKTtcblx0XHRzdGFydC5zdWJ0cmFjdCgoc2FtcGxpbmdSYXRlU2Vjb25kcyAqIHVzZXJDb3VudERhdGEubGVuZ3RoKSwgJ3MnKVxuXG5cdFx0bGV0IGRhdGFTb3VyY2UgPSBuZXcga2VuZG8uZGF0YS5EYXRhU291cmNlKCk7XG5cblx0XHRmb3IgKGxldCBpID0gMTsgaSA8IHVzZXJDb3VudERhdGEubGVuZ3RoOyBpKyspXG5cdFx0e1xuXHRcdFx0bGV0IGRhdGUgPSBzdGFydC5jbG9uZSgpO1xuXHRcdFx0ZGF0ZS5hZGQoaSAqIHNhbXBsaW5nUmF0ZVNlY29uZHMsICdzJyk7XG5cblx0XHRcdHRoaXMuYWRkRW50cnkodXNlckNvdW50RGF0YVtpXSwgZGF0YVNvdXJjZSwgZGF0ZS50b0RhdGUoKSwgZmFsc2UpO1xuXHRcdH1cblxuXHRcdC8vIEFzc2lnbiBkYXRhc291cmNlIHRvIGNoYXJ0XG5cdFx0dGhpcy5fY2hhcnRXaWRnZXQuc2V0RGF0YVNvdXJjZShkYXRhU291cmNlKTtcblxuXHRcdC8vIFVwZGF0ZSBheGlzXG5cdFx0dGhpcy5fdXBkYXRlWEF4aXNMaW1pdHMoKTtcblx0fVxuXG5cdGFkZEVudHJ5KHVzZXJDb3VudCwgZGF0YVNvdXJjZSA9IG51bGwsIGRhdGUgPSBudWxsLCB1cGRhdGVBeGlzID0gdHJ1ZSlcblx0e1xuXHRcdGlmIChkYXRlID09IG51bGwpXG5cdFx0XHRkYXRlID0gbmV3IERhdGUoKTtcblxuXHRcdGxldCBuZXdFbnRyeSA9IHtcblx0XHRcdHRpbWU6IGRhdGUsXG5cdFx0XHR1c2VyczogdXNlckNvdW50XG5cdFx0fTtcblxuXHRcdC8vIEFkZCBlbnRyeSB0byBkYXRhIHNvdXJjZVxuXHRcdGlmIChkYXRhU291cmNlID09IG51bGwpXG5cdFx0XHRkYXRhU291cmNlID0gdGhpcy5fZGF0YVNvdXJjZTtcblxuXHRcdGRhdGFTb3VyY2UuYWRkKG5ld0VudHJ5KTtcblxuXHRcdGlmICh1cGRhdGVBeGlzKVxuXHRcdFx0dGhpcy5fdXBkYXRlWEF4aXNMaW1pdHMoKTtcblx0fVxuXG5cdF91cGRhdGVYQXhpc0xpbWl0cygpXG5cdHtcblx0XHRsZXQgZHMgPSB0aGlzLl9kYXRhU291cmNlO1xuXG5cdFx0aWYgKGRzLnRvdGFsKCkgPiAwKVxuXHRcdHtcblx0XHRcdGNvbnN0IGNoYXJ0TWluaW11bSA9IG1vbWVudCh0aGlzLl9sYXN0RGF0ZSkuYWRkKHRoaXMuX3JhbmdlWzBdLCAnaCcpO1xuXHRcdFx0Y29uc3QgY2hhcnRNYXhpbXVtID0gbW9tZW50KHRoaXMuX2xhc3REYXRlKS5hZGQodGhpcy5fcmFuZ2VbMV0sICdoJyk7XG5cblx0XHRcdHRoaXMuX2NoYXJ0V2lkZ2V0Lm9wdGlvbnMuY2F0ZWdvcnlBeGlzLm1pbiA9IGNoYXJ0TWluaW11bS50b0RhdGUoKTtcblx0XHRcdHRoaXMuX2NoYXJ0V2lkZ2V0Lm9wdGlvbnMuY2F0ZWdvcnlBeGlzLm1heCA9IGNoYXJ0TWF4aW11bS50b0RhdGUoKTtcblxuXHRcdFx0dGhpcy5fY2hhcnRXaWRnZXQucmVkcmF3KCk7XG5cdFx0fVxuXHR9XG5cblx0Z2V0IF9kYXRhU291cmNlKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl9jaGFydFdpZGdldC5kYXRhU291cmNlO1xuXHR9XG59XG5cbi8vIERFRklORSBDT01QT05FTlRcbmlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzLmdldCgndXNlci1jb3VudC1jaGFydCcpKVxuXHR3aW5kb3cuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd1c2VyLWNvdW50LWNoYXJ0JywgVXNlckNvdW50Q2hhcnQpO1xuIiwiZXhwb3J0IGNsYXNzIE1hdGNoRXhwcmVzc2lvbkJ1aWxkZXIgZXh0ZW5kcyBIVE1MRWxlbWVudFxue1xuXHRjb25zdHJ1Y3RvcigpXG5cdHtcblx0ICAgIHN1cGVyKCk7XG5cblx0XHR0aGlzLkVYUFJFU1NJT05fVVBEQVRFRF9FVkVOVCA9ICdleHByZXNzaW9uVXBkYXRlZCc7XG5cblx0XHR0aGlzLl9kaWFsb2cgPSAkKGBcblx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbFwiIGlkPVwibWF0Y2hFeHByQnVpbGRlck1vZGFsXCIgdGFiaW5kZXg9XCItMVwiIHJvbGU9XCJkaWFsb2dcIiBhcmlhLWxhYmVsbGVkYnk9XCJtYXRjaEV4cHJCdWlsZGVyTW9kYWxUaXRsZVwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuXHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtZGlhbG9nIG1vZGFsLWRpYWxvZy1jZW50ZXJlZCBtb2RhbC1sZ1wiIHJvbGU9XCJkb2N1bWVudFwiPlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbC1jb250ZW50XCI+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtaGVhZGVyXCI+XG5cdFx0XHRcdFx0XHRcdDxoNSBjbGFzcz1cIm1vZGFsLXRpdGxlIHRleHQtcHJpbWFyeVwiIGlkPVwibWF0Y2hFeHByQnVpbGRlck1vZGFsVGl0bGVcIj4uLi48L2g1PlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtYm9keSBwLTBcIj5cblx0XHRcdFx0XHRcdFx0PGZvcm0gY2xhc3M9XCJiZy1jb2xvci1kZWZhdWx0IHAtM1wiPlxuXHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJkLWlubGluZS1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciByYWRpby1idXR0b24tZ3JvdXAgaW5saW5lLWZvcm0tZ3JvdXBzXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY3VzdG9tLWNvbnRyb2wgY3VzdG9tLXJhZGlvIGN1c3RvbS1jb250cm9sLWlubGluZSBtci0yXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxpbnB1dCB0eXBlPVwicmFkaW9cIiBpZD1cImFuZE9wZXJhdG9yUkJcIiBuYW1lPVwib3BlcmF0b3JSQkdcIiBjbGFzcz1cImN1c3RvbS1jb250cm9sLWlucHV0XCIgdmFsdWU9XCJBTkRcIiBjaGVja2VkIGRpc2FibGVkPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8bGFiZWwgY2xhc3M9XCJjdXN0b20tY29udHJvbC1sYWJlbFwiIGZvcj1cImFuZE9wZXJhdG9yUkJcIj5BTkQ8L2xhYmVsPlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY3VzdG9tLWNvbnRyb2wgY3VzdG9tLXJhZGlvIGN1c3RvbS1jb250cm9sLWlubGluZSBtci0yXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxpbnB1dCB0eXBlPVwicmFkaW9cIiBpZD1cIm9yT3BlcmF0b3JSQlwiIG5hbWU9XCJvcGVyYXRvclJCR1wiIGNsYXNzPVwiY3VzdG9tLWNvbnRyb2wtaW5wdXRcIiB2YWx1ZT1cIk9SXCIgZGlzYWJsZWQ+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxsYWJlbCBjbGFzcz1cImN1c3RvbS1jb250cm9sLWxhYmVsXCIgZm9yPVwib3JPcGVyYXRvclJCXCI+T1I8L2xhYmVsPlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImQtaW5saW5lLWJsb2NrXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5saW5lLWZvcm0tZ3JvdXBzIG15LTFcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGlucHV0IGlkPVwidmFyTmFtZUNCXCIvPlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5saW5lLWZvcm0tZ3JvdXBzIG15LTFcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGlucHV0IGlkPVwidHlwZUREXCIgY2xhc3M9XCJtYXRjaFR5cGVEcm9wZG93blwiLz5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGlucHV0IGlkPVwiY29uZGl0aW9uRERcIiBjbGFzcz1cIm1hdGNoQ29uZGl0aW9uRHJvcGRvd25cIi8+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZC1pbmxpbmUgaW5saW5lLWZvcm0tZ3JvdXBzXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8aW5wdXQgaWQ9XCJ2YWx1ZUluXCIgY2xhc3M9XCJrLXRleHRib3ggbXQtMVwiIHBsYWNlaG9sZGVyPVwiVmFsdWVcIi8+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8YnV0dG9uIGlkPVwibWF0Y2hFeHByQWRkQnRcIiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJrLWJ1dHRvbiBrLXNlY29uZGFyeSBtdC0xXCI+PGkgY2xhc3M9XCJmYXMgZmEtcGx1cyBtci0xXCI+PC9pPkFkZDwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHQ8L2Zvcm0+XG5cblx0XHRcdFx0XHRcdFx0PGRpdiBpZD1cIm1hdGNoRXhwckdyaWRcIiBjbGFzcz1cIm0tM1wiPjwvZGl2PlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtZm9vdGVyIGQtZmxleFwiPlxuXHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZmxleC1ncm93LTEgdGV4dC1sZWZ0XCI+XG5cdFx0XHRcdFx0XHRcdFx0PGJ1dHRvbiBpZD1cIm1hdGNoRXhwckFwcGx5QnRcIiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJrLWJ1dHRvbiBrLXByaW1hcnkgbXItMlwiPjxpIGNsYXNzPVwiZmFzIGZhLWNoZWNrLWNpcmNsZSBtci0xXCI+PC9pPkFwcGx5PC9idXR0b24+XG5cdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZmxleC1ncm93LTEgdGV4dC1yaWdodFwiPlxuXHRcdFx0XHRcdFx0XHRcdDxidXR0b24gaWQ9XCJtYXRjaEV4cHJSZW1vdmVCdFwiIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImstYnV0dG9uIGstc2Vjb25kYXJ5XCIgZGlzYWJsZWQ+PGkgY2xhc3M9XCJmYXMgZmEtbWludXMtY2lyY2xlIG1yLTFcIj48L2k+UmVtb3ZlIHNlbGVjdGVkPC9idXR0b24+XG5cdFx0XHRcdFx0XHRcdFx0PGJ1dHRvbiBpZD1cIm1hdGNoRXhwckNsZWFyQnRcIiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJrLWJ1dHRvbiBrLXNlY29uZGFyeVwiPjxpIGNsYXNzPVwiZmFzIGZhLXRpbWVzLWNpcmNsZSBtci0xXCI+PC9pPkNsZWFyIGFsbDwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdDwvZGl2PlxuXHRcdGApO1xuXG5cdFx0JCgnI21hdGNoRXhwckFkZEJ0JywgdGhpcy5fZGlhbG9nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQWRkQnRDbGljaywgdGhpcykpO1xuXHRcdCQoJyNtYXRjaEV4cHJBcHBseUJ0JywgdGhpcy5fZGlhbG9nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQXBwbHlCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI21hdGNoRXhwclJlbW92ZUJ0JywgdGhpcy5fZGlhbG9nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uUmVtb3ZlQnRDbGljaywgdGhpcykpO1xuXHRcdCQoJyNtYXRjaEV4cHJDbGVhckJ0JywgdGhpcy5fZGlhbG9nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQ2xlYXJCdENsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBJTklUSUFMSVpFIEZPUk0gSVRFTVNcblxuXHRcdHRoaXMuX3Zhck5hbWVDYiA9ICQoJyN2YXJOYW1lQ0InLCB0aGlzLl9kaWFsb2cpLmtlbmRvQ29tYm9Cb3goe1xuXHRcdFx0ZGF0YVRleHRGaWVsZDogJ2xhYmVsJyxcblx0XHRcdGRhdGFWYWx1ZUZpZWxkOiAndmFsdWUnLFxuXHRcdFx0cGxhY2Vob2xkZXI6ICdWYXJpYWJsZSBuYW1lJyxcblx0XHR9KS5kYXRhKCdrZW5kb0NvbWJvQm94Jyk7XG5cblx0XHQvLyBTZXQgdHlwZXMgZHJvcGRvd24gZGF0YSBzb3VyY2Vcblx0XHQvLyBOT1RFOiBpbiBvcmRlciB0byBhdm9pZCBYeHhNYXRjaCBvYmplY3RzIHRvIGJlIHdyYXBwZWQgYnkgS2VuZG8gZGF0YSBzb3VyY2UsIHdlIGhhdmUgdG8gcGFzcyB0aGVtIHVzaW5nIF8gcHJvcGVydHkgbmFtZXNcblx0XHRsZXQgdHlwZURTID0gW107XG5cblx0XHQvLyBCb29sZWFuIC0tLS0tLS0tLS0tLS0tLS1cblx0XHR0eXBlRFMucHVzaCh7XG5cdFx0XHRsYWJlbDogJ0Jvb2xlYW4nLFxuXHRcdFx0X3ZhbHVlOiBTRlMyWC5Cb29sTWF0Y2guRVFVQUxTLFxuXHRcdFx0Y29uZGl0aW9uczogW1xuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLkJvb2xNYXRjaC5FUVVBTFMuc3ltYm9sLFxuXHRcdFx0XHRcdF92YWx1ZTogU0ZTMlguQm9vbE1hdGNoLkVRVUFMUyxcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxhYmVsOiBTRlMyWC5Cb29sTWF0Y2guTk9UX0VRVUFMUy5zeW1ib2wsXG5cdFx0XHRcdFx0X3ZhbHVlOiBTRlMyWC5Cb29sTWF0Y2guTk9UX0VRVUFMUyxcblx0XHRcdFx0fVxuXHRcdFx0XVxuXHRcdH0pO1xuXG5cdFx0Ly8gTnVtYmVyIC0tLS0tLS0tLS0tLS0tLS1cblx0XHR0eXBlRFMucHVzaCh7XG5cdFx0XHRsYWJlbDogJ051bWJlcicsXG5cdFx0XHRfdmFsdWU6IFNGUzJYLk51bWJlck1hdGNoLkVRVUFMUyxcblx0XHRcdGNvbmRpdGlvbnM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxhYmVsOiBTRlMyWC5OdW1iZXJNYXRjaC5FUVVBTFMuc3ltYm9sLFxuXHRcdFx0XHRcdF92YWx1ZTogU0ZTMlguTnVtYmVyTWF0Y2guRVFVQUxTLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLk51bWJlck1hdGNoLk5PVF9FUVVBTFMuc3ltYm9sLFxuXHRcdFx0XHRcdF92YWx1ZTogU0ZTMlguTnVtYmVyTWF0Y2guTk9UX0VRVUFMUyxcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxhYmVsOiBTRlMyWC5OdW1iZXJNYXRjaC5HUkVBVEVSX1RIQU4uc3ltYm9sLFxuXHRcdFx0XHRcdF92YWx1ZTogU0ZTMlguTnVtYmVyTWF0Y2guR1JFQVRFUl9USEFOLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLk51bWJlck1hdGNoLkxFU1NfVEhBTi5zeW1ib2wsXG5cdFx0XHRcdFx0X3ZhbHVlOiBTRlMyWC5OdW1iZXJNYXRjaC5MRVNTX1RIQU4sXG5cdFx0XHRcdH0sXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRsYWJlbDogU0ZTMlguTnVtYmVyTWF0Y2guR1JFQVRFUl9USEFOX09SX0VRVUFMX1RPLnN5bWJvbCxcblx0XHRcdFx0XHRfdmFsdWU6IFNGUzJYLk51bWJlck1hdGNoLkdSRUFURVJfVEhBTl9PUl9FUVVBTF9UTyxcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGxhYmVsOiBTRlMyWC5OdW1iZXJNYXRjaC5MRVNTX1RIQU5fT1JfRVFVQUxfVE8uc3ltYm9sLFxuXHRcdFx0XHRcdF92YWx1ZTogU0ZTMlguTnVtYmVyTWF0Y2guTEVTU19USEFOX09SX0VRVUFMX1RPLFxuXHRcdFx0XHR9XG5cdFx0XHRdXG5cdFx0fSk7XG5cblx0XHQvLyBTdHJpbmcgLS0tLS0tLS0tLS0tLS0tLVxuXHRcdHR5cGVEUy5wdXNoKHtcblx0XHRcdGxhYmVsOiAnU3RyaW5nJyxcblx0XHRcdF92YWx1ZTogU0ZTMlguU3RyaW5nTWF0Y2guRVFVQUxTLFxuXHRcdFx0Y29uZGl0aW9uczogW1xuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLlN0cmluZ01hdGNoLkVRVUFMUy5zeW1ib2wsXG5cdFx0XHRcdFx0X3ZhbHVlOiBTRlMyWC5TdHJpbmdNYXRjaC5FUVVBTFMsXG5cdFx0XHRcdH0sXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRsYWJlbDogU0ZTMlguU3RyaW5nTWF0Y2guTk9UX0VRVUFMUy5zeW1ib2wsXG5cdFx0XHRcdFx0X3ZhbHVlOiBTRlMyWC5TdHJpbmdNYXRjaC5OT1RfRVFVQUxTLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLlN0cmluZ01hdGNoLkNPTlRBSU5TLnN5bWJvbCxcblx0XHRcdFx0XHRfdmFsdWU6IFNGUzJYLlN0cmluZ01hdGNoLkNPTlRBSU5TLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLlN0cmluZ01hdGNoLlNUQVJUU19XSVRILnN5bWJvbCxcblx0XHRcdFx0XHRfdmFsdWU6IFNGUzJYLlN0cmluZ01hdGNoLlNUQVJUU19XSVRILFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGFiZWw6IFNGUzJYLlN0cmluZ01hdGNoLkVORFNfV0lUSC5zeW1ib2wsXG5cdFx0XHRcdFx0X3ZhbHVlOiBTRlMyWC5TdHJpbmdNYXRjaC5FTkRTX1dJVEgsXG5cdFx0XHRcdH1cblx0XHRcdF1cblx0XHR9KTtcblxuXHRcdHRoaXMuX3R5cGVEZCA9ICQoJyN0eXBlREQnLCB0aGlzLl9kaWFsb2cpLmtlbmRvRHJvcERvd25MaXN0KHtcblx0XHRcdGRhdGFTb3VyY2U6IHR5cGVEUyxcblx0XHRcdGRhdGFUZXh0RmllbGQ6ICdsYWJlbCcsXG5cdFx0XHRkYXRhVmFsdWVGaWVsZDogJ192YWx1ZScsXG5cdFx0XHRvcHRpb25MYWJlbDoge1xuXHRcdFx0XHRsYWJlbDogJ1R5cGUnLFxuXHRcdFx0XHRfdmFsdWU6ICcnXG5cdFx0XHR9LFxuXHRcdFx0b3B0aW9uTGFiZWxUZW1wbGF0ZTogJzxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPiM6bGFiZWwjPC9zcGFuPicsXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICh0aGlzLl90eXBlRGQgJiYgdGhpcy5fdHlwZURkLnNlbGVjdCgpKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGV0IHNlbGVjdGVkVHlwZSA9IHRoaXMuX3R5cGVEZC5kYXRhSXRlbSh0aGlzLl90eXBlRGQuc2VsZWN0KCkpO1xuXHRcdFx0XHRcdHRoaXMuX2NvbmRpdGlvbkRkLnNldERhdGFTb3VyY2Uoc2VsZWN0ZWRUeXBlLmNvbmRpdGlvbnMpO1xuXHRcdFx0XHRcdHRoaXMuX2NvbmRpdGlvbkRkLnNlbGVjdCgxKTtcblx0XHRcdFx0fVxuXHRcdFx0fSwgdGhpcylcblx0XHR9KS5kYXRhKCdrZW5kb0Ryb3BEb3duTGlzdCcpO1xuXG5cdFx0dGhpcy5fY29uZGl0aW9uRGQgPSAkKCcjY29uZGl0aW9uREQnLCB0aGlzLl9kaWFsb2cpLmtlbmRvRHJvcERvd25MaXN0KHtcblx0XHRcdGRhdGFUZXh0RmllbGQ6ICdsYWJlbCcsXG5cdFx0XHRkYXRhVmFsdWVGaWVsZDogJ192YWx1ZScsXG5cdFx0XHRvcHRpb25MYWJlbDoge1xuXHRcdFx0XHRsYWJlbDogJ0NvbmRpdGlvbicsXG5cdFx0XHRcdF92YWx1ZTogJydcblx0XHRcdH0sXG5cdFx0XHRvcHRpb25MYWJlbFRlbXBsYXRlOiAnPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+IzpsYWJlbCM8L3NwYW4+Jyxcblx0XHR9KS5kYXRhKCdrZW5kb0Ryb3BEb3duTGlzdCcpO1xuXG5cdFx0dGhpcy5fdmFsdWVJbiA9ICQoJyN2YWx1ZUluJywgdGhpcy5fZGlhbG9nKTtcblxuXHRcdC8vIElOSVRJQUxJWkUgR1JJRFxuXG5cdFx0dGhpcy5fZ3JpZCA9ICQoJyNtYXRjaEV4cHJHcmlkJywgdGhpcy5fZGlhbG9nKS5rZW5kb0dyaWQoe1xuXHRcdFx0ZGF0YVNvdXJjZTogW10sXG5cdFx0XHRyZXNpemFibGU6IHRydWUsXG5cdFx0XHRzZWxlY3RhYmxlOiAncm93Jyxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vbkdyaWRTZWxlY3Rpb25DaGFuZ2UsIHRoaXMpLFxuXHRcdFx0bm9SZWNvcmRzOiB7XG5cdFx0XHRcdHRlbXBsYXRlOiAnTm8gZW50cmllcy4nXG5cdFx0XHR9LFxuXHRcdFx0Y29sdW1uczogW1xuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdvcGVyYXRvcicsXG5cdFx0XHRcdFx0dGl0bGU6ICdPcGVyYXRvcicsXG5cdFx0XHRcdFx0d2lkdGg6IDkwXG5cdFx0XHRcdH0sXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRmaWVsZDogJ2xhYmVsJyxcblx0XHRcdFx0XHR0aXRsZTogJ05hbWUnLFxuXHRcdFx0XHRcdHdpZHRoOiAxMjBcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAndHlwZUxhYmVsJyxcblx0XHRcdFx0XHR0aXRsZTogJ1R5cGUnLFxuXHRcdFx0XHRcdHdpZHRoOiA4NVxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdzeW1ib2wnLFxuXHRcdFx0XHRcdHRpdGxlOiAnQ29uZGl0aW9uJyxcblx0XHRcdFx0XHR3aWR0aDogOTVcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAndmFsdWUnLFxuXHRcdFx0XHRcdHRpdGxlOiAnVmFsdWUnLFxuXHRcdFx0XHRcdHdpZHRoOiAyMDBcblx0XHRcdFx0fSxcblx0XHRcdF1cblx0XHR9KS5kYXRhKCdrZW5kb0dyaWQnKTtcblx0fVxuXG5cdGdldCB0aXRsZSgpXG5cdHtcblx0XHRyZXR1cm4gJCgnI21hdGNoRXhwckJ1aWxkZXJNb2RhbFRpdGxlJywgdGhpcy5fZGlhbG9nKS50ZXh0KCk7XG5cdH1cblxuXHRzZXQgdGl0bGUodmFsdWUpXG5cdHtcblx0XHQkKCcjbWF0Y2hFeHByQnVpbGRlck1vZGFsVGl0bGUnLCB0aGlzLl9kaWFsb2cpLnRleHQodmFsdWUpO1xuXHR9XG5cblx0Z2V0IHByZWRlZmluZWRWYXJOYW1lcygpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5fcHJlZGVmaW5lZFZhck5hbWVzO1xuXHR9XG5cblx0c2V0IHByZWRlZmluZWRWYXJOYW1lcyh2YWx1ZSlcblx0e1xuXHRcdHRoaXMuX3ByZWRlZmluZWRWYXJOYW1lcyA9IHZhbHVlO1xuXG5cdFx0dGhpcy5fdmFyTmFtZUNiLnNldERhdGFTb3VyY2UodmFsdWUpO1xuXHR9XG5cblx0Z2V0IGZpbHRlckV4cHJlc3Npb24oKVxuXHR7XG5cdFx0cmV0dXJuIHRoaXMuX2ZpbHRlckV4cHJlc3Npb247XG5cdH1cblxuXHRzaG93KClcblx0e1xuXHRcdHRoaXMuX2RpYWxvZy5tb2RhbCh7XG5cdFx0XHRiYWNrZHJvcDogJ3N0YXRpYycsXG5cdFx0XHRrZXlib2FyZDogZmFsc2UsXG5cdFx0XHRzaG93OiB0cnVlXG5cdFx0fSk7XG5cdH1cblxuXHRfb25HcmlkU2VsZWN0aW9uQ2hhbmdlKClcblx0e1xuXHRcdGxldCBzZWxlY3RlZFJvd3MgPSB0aGlzLl9ncmlkLnNlbGVjdCgpO1xuXHRcdGxldCBzZWxlY3RlZERhdGFJdGVtcyA9IFtdO1xuXG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBzZWxlY3RlZFJvd3MubGVuZ3RoOyBpKyspXG5cdFx0e1xuXHRcdFx0bGV0IGRhdGFJdGVtID0gdGhpcy5fZ3JpZC5kYXRhSXRlbShzZWxlY3RlZFJvd3NbaV0pO1xuXHRcdFx0c2VsZWN0ZWREYXRhSXRlbXMucHVzaChkYXRhSXRlbSk7XG5cdFx0fVxuXG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgcmVtb3ZlIGJ1dHRvblxuXHRcdCQoJyNtYXRjaEV4cHJSZW1vdmVCdCcsIHRoaXMuX2RpYWxvZykuYXR0cignZGlzYWJsZWQnLCBzZWxlY3RlZERhdGFJdGVtcy5sZW5ndGggPT0gMCk7XG5cdH1cblxuXHRfb25BZGRCdENsaWNrKClcblx0e1xuXHRcdGlmICh0aGlzLl92YXJOYW1lQ2IudmFsdWUoKSAhPSAnJyAmJiB0aGlzLl90eXBlRGQuc2VsZWN0KCkpXG5cdFx0e1xuXHRcdFx0Ly8gVmFyaWFibGUgbmFtZSBhbmQgbGFiZWxcblx0XHRcdGxldCB2YXJOYW1lLCB2YXJMYWJlbDtcblxuXHRcdFx0aWYgKHRoaXMuX3Zhck5hbWVDYi5zZWxlY3QoKSA+IC0xKVxuXHRcdFx0e1xuXHRcdFx0XHRsZXQgZGF0YUl0ZW0gPSB0aGlzLl92YXJOYW1lQ2IuZGF0YUl0ZW0odGhpcy5fdmFyTmFtZUNiLnNlbGVjdCgpKTtcblx0XHRcdFx0dmFyTmFtZSA9IGRhdGFJdGVtLnZhbHVlO1xuXHRcdFx0XHR2YXJMYWJlbCA9IGRhdGFJdGVtLmxhYmVsO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0XHR2YXJOYW1lID0gdmFyTGFiZWwgPSB0aGlzLl92YXJOYW1lQ2IudmFsdWUoKTtcblxuXHRcdFx0Ly8gVmFyaWFibGUgdHlwZSdzIGxhYmVsXG5cdFx0XHRsZXQgdHlwZUxhYmVsID0gdGhpcy5fdHlwZURkLmRhdGFJdGVtKHRoaXMuX3R5cGVEZC5zZWxlY3QoKSkubGFiZWw7XG5cblx0XHRcdC8vIE1hdGNoIGNvbmRpdGlvblxuXHRcdFx0bGV0IGNvbmRpdGlvbiA9IHRoaXMuX2NvbmRpdGlvbkRkLmRhdGFJdGVtKHRoaXMuX2NvbmRpdGlvbkRkLnNlbGVjdCgpKS5fdmFsdWU7XG5cblx0XHRcdC8vIEV4cHJlc3Npb24gb3BlcmF0b3Jcblx0XHRcdGxldCBvcGVyYXRvciA9IHRoaXMuX2dyaWQuZGF0YVNvdXJjZS50b3RhbCgpID4gMCA/ICQoJ2lucHV0W25hbWU9b3BlcmF0b3JSQkddOmNoZWNrZWQnLCB0aGlzLl9kaWFsb2cpLnZhbCgpIDogbnVsbDtcblxuXHRcdFx0Ly8gVmFyaWFibGUgdmFsdWVcblx0XHRcdGxldCB2YWwgPSB0aGlzLl92YWx1ZUluLnZhbCgpO1xuXHRcdFx0bGV0IHZhclZhbHVlO1xuXG5cdFx0XHRpZiAoY29uZGl0aW9uIGluc3RhbmNlb2YgU0ZTMlguQm9vbE1hdGNoKVxuXHRcdFx0XHR2YXJWYWx1ZSA9ICh2YWwgPT0gJ3RydWUnID8gdHJ1ZSA6IGZhbHNlKTtcblx0XHRcdGVsc2UgaWYgKGNvbmRpdGlvbiBpbnN0YW5jZW9mIFNGUzJYLk51bWJlck1hdGNoKVxuXHRcdFx0XHR2YXJWYWx1ZSA9IChpc05hTihOdW1iZXIodmFsKSkgPyAwIDogTnVtYmVyKHZhbCkpO1xuXHRcdFx0ZWxzZVxuXHRcdFx0XHR2YXJWYWx1ZSA9IHZhbDtcblxuXHRcdFx0Ly8gQWRkIGl0ZW0gdG8gZ3JpZFxuXHRcdFx0bGV0IGV4cHJQYXJ0ID0gdGhpcy5fZ2V0RXhwcmVzc2lvblBhcnQodmFyTGFiZWwsIHZhck5hbWUsIHZhclZhbHVlLCB0eXBlTGFiZWwsIGNvbmRpdGlvbiwgb3BlcmF0b3IpO1xuXHRcdFx0dGhpcy5fZ3JpZC5kYXRhU291cmNlLmFkZChleHByUGFydCk7XG5cblx0XHRcdC8vIFJlc2V0IGlucHV0c1xuXHRcdFx0dGhpcy5fdmFyTmFtZUNiLnZhbHVlKCcnKTtcblx0XHRcdHRoaXMuX3ZhbHVlSW4udmFsKCcnKTtcblxuXHRcdFx0Ly8gRW5hYmxlIG9wZXJhdG9yIHJhZGlvIGJ1dHRvbnNcblx0XHRcdHRoaXMuX2VuYWJsZU9wZXJhdG9yUmIodHJ1ZSk7XG5cdFx0fVxuXHR9XG5cblx0X29uQXBwbHlCdENsaWNrKClcblx0e1xuXHRcdC8vIEhpZGUgZGlhbG9nXG5cdFx0dGhpcy5fZGlhbG9nLm1vZGFsKCdoaWRlJyk7XG5cblx0XHQvLyBDbGVhciBncmlkIHNlbGVjdGlvblxuXHRcdHRoaXMuX2dyaWQuY2xlYXJTZWxlY3Rpb24oKTtcblxuXHRcdC8vIEJ1aWxkIGZpbmFsIG1hdGNoIGV4cHJlc3Npb25cblx0XHR0aGlzLl9maWx0ZXJFeHByZXNzaW9uID0gbnVsbDtcblxuXHRcdGlmICh0aGlzLl9ncmlkLmRhdGFTb3VyY2UudG90YWwoKSA+IDApXG5cdFx0e1xuXHRcdFx0bGV0IGV4cFBhcnQgPSB0aGlzLl9ncmlkLmRhdGFTb3VyY2UuYXQoMCk7XG5cdFx0XHR0aGlzLl9maWx0ZXJFeHByZXNzaW9uID0gbmV3IFNGUzJYLk1hdGNoRXhwcmVzc2lvbihleHBQYXJ0Lm5hbWUsIGV4cFBhcnQuX2NvbmRpdGlvbiwgZXhwUGFydC52YWx1ZSk7XG5cblx0XHRcdGZvciAobGV0IGkgPSAxOyBpIDwgdGhpcy5fZ3JpZC5kYXRhU291cmNlLnRvdGFsKCk7IGkrKylcblx0XHRcdHtcblx0XHRcdFx0ZXhwUGFydCA9IHRoaXMuX2dyaWQuZGF0YVNvdXJjZS5hdChpKTtcblxuXHRcdFx0XHRpZiAoZXhwUGFydC5vcGVyYXRvciA9PSBTRlMyWC5Mb2dpY09wZXJhdG9yLkFORC5pZClcblx0XHRcdFx0XHR0aGlzLl9maWx0ZXJFeHByZXNzaW9uID0gdGhpcy5fZmlsdGVyRXhwcmVzc2lvbi5hbmQoZXhwUGFydC5uYW1lLCBleHBQYXJ0Ll9jb25kaXRpb24sIGV4cFBhcnQudmFsdWUpO1xuXHRcdFx0XHRlbHNlIGlmIChleHBQYXJ0Lm9wZXJhdG9yID09IFNGUzJYLkxvZ2ljT3BlcmF0b3IuT1IuaWQpXG5cdFx0XHRcdFx0dGhpcy5fZmlsdGVyRXhwcmVzc2lvbiA9IHRoaXMuX2ZpbHRlckV4cHJlc3Npb24ub3IoZXhwUGFydC5uYW1lLCBleHBQYXJ0Ll9jb25kaXRpb24sIGV4cFBhcnQudmFsdWUpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIEZpcmUgZXZlbnRcblx0XHRsZXQgZXZ0ID0gbmV3IEN1c3RvbUV2ZW50KHRoaXMuRVhQUkVTU0lPTl9VUERBVEVEX0VWRU5ULCB7XG5cdFx0ICAgIFx0ZGV0YWlsOiBudWxsLFxuXHRcdFx0XHRidWJibGVzOiBmYWxzZSxcblx0XHRcdFx0Y2FuY2VsYWJsZTogZmFsc2Vcblx0XHRcdH0pO1xuXG5cdFx0dGhpcy5kaXNwYXRjaEV2ZW50KGV2dCk7XG5cdH1cblxuXHRfb25SZW1vdmVCdENsaWNrKClcblx0e1xuXHRcdGxldCBkYXRhSXRlbSA9IHRoaXMuX2dyaWQuZGF0YUl0ZW0odGhpcy5fZ3JpZC5zZWxlY3QoKSk7XG5cdFx0dGhpcy5fZ3JpZC5kYXRhU291cmNlLnJlbW92ZShkYXRhSXRlbSk7XG5cblx0XHQvLyBDbGVhciBncmlkIHNlbGVjdGlvblxuXHRcdHRoaXMuX2dyaWQuY2xlYXJTZWxlY3Rpb24oKTtcblxuXHRcdGlmICh0aGlzLl9ncmlkLmRhdGFTb3VyY2UudG90YWwoKSA9PSAwKVxuXHRcdHtcblx0XHRcdC8vIERpc2FibGUgb3BlcmF0b3IgcmFkaW8gYnV0dG9uc1xuXHRcdFx0dGhpcy5fZW5hYmxlT3BlcmF0b3JSYihmYWxzZSk7XG5cdFx0fVxuXHRcdGVsc2Vcblx0XHR7XG5cdFx0XHQvLyBSZXNldCBvcGVyYXRvciBvbiBmaXJzdCBlbnRyeVxuXHRcdFx0dGhpcy5fZ3JpZC5kYXRhU291cmNlLmF0KDApLm9wZXJhdG9yID0gbnVsbDtcblx0XHRcdHRoaXMuX2dyaWQucmVmcmVzaCgpO1xuXHRcdH1cblx0fVxuXG5cdF9vbkNsZWFyQnRDbGljaygpXG5cdHtcblx0XHQvLyBSZXNldCBmb3JtXG5cdFx0JCgnI2FuZE9wZXJhdG9yUkInLCB0aGlzLl9kaWFsb2cpLmF0dHIoJ2NoZWNrZWQnLCB0cnVlKTtcblx0XHQkKCcjb3JPcGVyYXRvclJCJywgdGhpcy5fZGlhbG9nKS5hdHRyKCdjaGVja2VkJywgZmFsc2UpO1xuXHRcdHRoaXMuX2VuYWJsZU9wZXJhdG9yUmIoZmFsc2UpO1xuXG5cdFx0dGhpcy5fdmFyTmFtZUNiLnZhbHVlKCcnKTtcblx0XHR0aGlzLl90eXBlRGQuc2VsZWN0KDApO1xuXHRcdHRoaXMuX2NvbmRpdGlvbkRkLnNlbGVjdCgwKTtcblxuXHRcdHRoaXMuX3ZhbHVlSW4udmFsKCcnKTtcblxuXHRcdC8vIENsZWFyIGdyaWQgc2VsZWN0aW9uXG5cdFx0dGhpcy5fZ3JpZC5jbGVhclNlbGVjdGlvbigpO1xuXG5cdFx0Ly8gQ2xlYXIgdGFibGVcblx0XHR0aGlzLl9ncmlkLnNldERhdGFTb3VyY2UoW10pO1xuXHR9XG5cblx0X2dldEV4cHJlc3Npb25QYXJ0KHZhckxhYmVsLCB2YXJOYW1lLCB2YXJWYWx1ZSwgdHlwZUxhYmVsLCBjb25kaXRpb24sIG9wZXJhdG9yKVxuXHR7XG5cdFx0cmV0dXJuIHtcblx0XHRcdCdsYWJlbCc6IHZhckxhYmVsLFxuXHRcdFx0J25hbWUnOiB2YXJOYW1lLFxuXHRcdFx0J3ZhbHVlJzogdmFyVmFsdWUsXG5cdFx0XHQndHlwZUxhYmVsJzogdHlwZUxhYmVsLFxuXHRcdFx0J3N5bWJvbCc6IGNvbmRpdGlvbi5zeW1ib2wsXG5cdFx0XHQnb3BlcmF0b3InOiBvcGVyYXRvcixcblx0XHRcdCdfY29uZGl0aW9uJzogY29uZGl0aW9uLFxuXHRcdH07XG5cdH1cblxuXHRfZW5hYmxlT3BlcmF0b3JSYihlbmFibGUpXG5cdHtcblx0XHQkKCcjYW5kT3BlcmF0b3JSQicsIHRoaXMuX2RpYWxvZykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlKTtcblx0XHQkKCcjb3JPcGVyYXRvclJCJywgdGhpcy5fZGlhbG9nKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGUpO1xuXHR9XG59XG5cbi8vIERFRklORSBDT01QT05FTlRcbmlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzLmdldCgnbWF0Y2gtZXhwcmVzc2lvbi1idWlsZGVyJykpXG5cdHdpbmRvdy5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ21hdGNoLWV4cHJlc3Npb24tYnVpbGRlcicsIE1hdGNoRXhwcmVzc2lvbkJ1aWxkZXIpO1xuIiwiZXhwb3J0IGNsYXNzIFNpZGViYXJMYXlvdXQgZXh0ZW5kcyBIVE1MRWxlbWVudFxue1xuXHRjb25zdHJ1Y3RvcigpXG5cdHtcblx0ICAgIHN1cGVyKCk7XG5cblx0XHQvLyBBdHRhY2ggYSBzaGFkb3cgcm9vdFxuXHRcdGNvbnN0IHNoYWRvd1Jvb3QgPSB0aGlzLmF0dGFjaFNoYWRvdyh7bW9kZTogJ29wZW4nfSk7XG5cdFx0c2hhZG93Um9vdC5pbm5lckhUTUwgPSBgXG5cdFx0XHQ8c3R5bGU+XG5cdFx0XHRcdDpob3N0IHtcblx0XHRcdFx0XHRkaXNwbGF5OiBmbGV4O1xuXHRcdFx0XHRcdGZsZXgtZGlyZWN0aW9uOiByb3c7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRAbWVkaWEgKG1heC13aWR0aDogNTc1Ljk4cHgpIHtcblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQteHMpIDo6c2xvdHRlZCg6bm90KFthcmlhLXNlbGVjdGVkPVwidHJ1ZVwiXSkpIHtcblx0XHRcdFx0XHRcdGRpc3BsYXk6IG5vbmUgIWltcG9ydGFudDtcblx0XHRcdFx0ICAgIH1cblxuXHRcdFx0XHRcdDpob3N0KC5zcGxpdC14cykgOjpzbG90dGVkKFthcmlhLXNlbGVjdGVkPVwidHJ1ZVwiXSkge1xuXHRcdFx0XHRcdFx0ZmxleC1ncm93OiAxO1xuXHRcdFx0XHQgICAgfVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0QG1lZGlhIChtYXgtd2lkdGg6IDc2Ny45OHB4KSB7XG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LXNtKSA6OnNsb3R0ZWQoOm5vdChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pKSB7XG5cdFx0XHRcdFx0XHRkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7XG5cdFx0XHRcdCAgICB9XG5cblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQtc20pIDo6c2xvdHRlZChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pIHtcblx0XHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0ICAgIH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdEBtZWRpYSAobWF4LXdpZHRoOiA5OTEuOThweCkge1xuXHRcdFx0XHRcdDpob3N0KC5zcGxpdC1tZCkgOjpzbG90dGVkKDpub3QoW2FyaWEtc2VsZWN0ZWQ9XCJ0cnVlXCJdKSkge1xuXHRcdFx0XHRcdFx0ZGlzcGxheTogbm9uZSAhaW1wb3J0YW50O1xuXHRcdFx0XHQgICAgfVxuXG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LW1kKSA6OnNsb3R0ZWQoW2FyaWEtc2VsZWN0ZWQ9XCJ0cnVlXCJdKSB7XG5cdFx0XHRcdFx0XHRmbGV4LWdyb3c6IDE7XG5cdFx0XHRcdCAgICB9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRAbWVkaWEgKG1heC13aWR0aDogMTE5OS45OHB4KSB7XG5cdFx0XHRcdFx0Omhvc3QoLnNwbGl0LWxnKSA6OnNsb3R0ZWQoOm5vdChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pKSB7XG5cdFx0XHRcdFx0XHRkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7XG5cdFx0XHRcdCAgICB9XG5cblx0XHRcdFx0XHQ6aG9zdCguc3BsaXQtbGcpIDo6c2xvdHRlZChbYXJpYS1zZWxlY3RlZD1cInRydWVcIl0pIHtcblx0XHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0ICAgIH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC5zaWRlLWNvbDo6c2xvdHRlZCgqKSB7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQubWFpbi1jb2w6OnNsb3R0ZWQoKikge1xuXHRcdFx0XHRcdGZsZXgtZ3JvdzogMTtcblx0XHRcdFx0fVxuXHRcdFx0PC9zdHlsZT5cblxuXHRcdFx0PHNsb3QgY2xhc3M9XCJzaWRlLWNvbFwiIG5hbWU9XCJzaWRlLWNvbHVtblwiPjwvc2xvdD5cblx0XHRcdDxzbG90IGNsYXNzPVwibWFpbi1jb2xcIiBuYW1lPVwibWFpbi1jb2x1bW5cIj48L3Nsb3Q+XG5cdFx0YDtcblxuXHRcdC8vIFNldCBpbml0aWFsIHNlbGVjdGlvblxuXHRcdHRoaXMuc2VsZWN0ZWRJbmRleCA9IDA7XG5cdH1cblxuXHRnZXQgc2VsZWN0ZWRQYW5lbCgpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5fc2VsZWN0ZWRQYW5lbDtcblx0fVxuXG5cdHNldCBzZWxlY3RlZFBhbmVsKGVsZW1lbnQpIC8vICdzaWRlJyBvciAnbWFpbidcblx0e1xuXHRcdGlmIChlbGVtZW50ICE9IG51bGwgJiYgZWxlbWVudC5wYXJlbnROb2RlID09IHRoaXMpXG5cdFx0e1xuXHRcdFx0dGhpcy5fc2VsZWN0ZWRQYW5lbCA9IGVsZW1lbnQ7XG5cblx0XHRcdGZvciAobGV0IGVsZW1lbnQgb2YgdGhpcy5jaGlsZHJlbilcblx0XHRcdHtcblx0XHRcdFx0aWYgKGVsZW1lbnQgPT0gdGhpcy5fc2VsZWN0ZWRQYW5lbClcblx0XHRcdFx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZSgnYXJpYS1zZWxlY3RlZCcsICd0cnVlJyk7XG5cdFx0XHRcdGVsc2Vcblx0XHRcdFx0XHRlbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSgnYXJpYS1zZWxlY3RlZCcpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRlbHNlXG5cdFx0e1xuXHRcdFx0Y29uc29sZS5lcnJvcignRWxlbWVudCBpcyBub3QgYSBjaGlsZCBvZiBTaWRlYmFyTGF5b3V0Jyk7XG5cdFx0fVxuXHR9XG5cblx0Z2V0IHNlbGVjdGVkSW5kZXgoKVxuXHR7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20odGhpcy5jaGlsZHJlbikuaW5kZXhPZih0aGlzLl9zZWxlY3RlZFBhbmVsKTtcblx0fVxuXG5cdHNldCBzZWxlY3RlZEluZGV4KGluZGV4KVxuXHR7XG5cdFx0aWYgKHRoaXMuY2hpbGRyZW4ubGVuZ3RoID4gMClcblx0XHR7XG5cdFx0XHRpZiAodGhpcy5jaGlsZHJlbltpbmRleF0gPT0gbnVsbClcblx0XHRcdHtcblx0XHRcdFx0Y29uc29sZS5lcnJvcignSW52YWxpZCBTaWRlYmFyTGF5b3V0IGluZGV4Jyk7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0bGV0IGVsZW1lbnQgPSB0aGlzLmNoaWxkcmVuW2luZGV4XTtcblx0XHRcdHRoaXMuc2VsZWN0ZWRQYW5lbCA9IGVsZW1lbnQ7XG5cdFx0fVxuXHR9XG59XG5cbi8vIERFRklORSBDT01QT05FTlRcbmlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzLmdldCgnc2lkZWJhci1sYXlvdXQnKSlcblx0d2luZG93LmN1c3RvbUVsZW1lbnRzLmRlZmluZSgnc2lkZWJhci1sYXlvdXQnLCBTaWRlYmFyTGF5b3V0KTtcbiIsImltcG9ydCB7QmFzZU1vZHVsZX0gZnJvbSAnLi9iYXNlLW1vZHVsZSc7XG5pbXBvcnQge1NpZGViYXJMYXlvdXR9IGZyb20gJy4uL2NvbXBvbmVudHMvc2lkZWJhci1sYXlvdXQnO1xuaW1wb3J0IHtNYXRjaEV4cHJlc3Npb25CdWlsZGVyfSBmcm9tICcuLi9jb21wb25lbnRzL21vZHVsZS1zcGVjaWZpYy9tYXRjaC1leHByZXNzaW9uLWJ1aWxkZXInO1xuaW1wb3J0IHtDb25maWdJbnRlcmZhY2VCdWlsZGVyfSBmcm9tICcuLi91dGlscy91aWJ1aWxkZXIvY29uZmlnLWludGVyZmFjZS1idWlsZGVyJztcbmltcG9ydCB7Y2FwaXRhbGl6ZUZpcnN0LCBmaWx0ZXJDbGFzc05hbWUsIHJvdW5kVG9EZWNpbWFscywgc2NhbGVCeXRlc30gZnJvbSAnLi4vdXRpbHMvdXRpbGl0aWVzJztcbmltcG9ydCB7Um9vbVByb3BlcnRpZXNEYXRhLFVzZXJQcm9wZXJ0aWVzRGF0YX0gZnJvbSAnLi4vdXRpbHMvbWF0Y2gtcHJvcGVydGllcyc7XG5pbXBvcnQge1VzZXJDb3VudENoYXJ0fSBmcm9tICcuLi9jb21wb25lbnRzL2NoYXJ0cy91c2VyLWNvdW50LWNoYXJ0JztcbmltcG9ydCB7Z2V0VGltZVJhbmdlVG9vbHRpcFRlbXBsYXRlLCBnZXRXUkJ5dGVzTGFiZWxUZW1wbGF0ZX0gZnJvbSAnLi4vY29tcG9uZW50cy9jaGFydHMvY2hhcnQtdXRpbHMnO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBab25lTW9uaXRvciBleHRlbmRzIEJhc2VNb2R1bGVcbntcblx0Y29uc3RydWN0b3IoKVxuXHR7XG5cdCAgICBzdXBlcignem9uZU1vbml0b3InKTtcblxuXHRcdHRoaXMuTU9OSVRPUkVEX1RZUEVfWk9ORSA9ICd6b25lJztcblx0XHR0aGlzLk1PTklUT1JFRF9UWVBFX1JPT00gPSAncm9vbSc7XG5cdFx0dGhpcy5NT05JVE9SRURfVFlQRV9VU0VSID0gJ3VzZXInO1xuXG5cdFx0dGhpcy5BTllfTEFCRUwgPSAnW2FueV0nO1xuXHRcdHRoaXMuREVGQVVMVF9HUk9VUF9OQU1FID0gJ2RlZmF1bHQnO1xuXHRcdHRoaXMuTUFYX0VYVEVOU0lPTl9MT0dfU0laRSA9IDMwMDtcblx0XHR0aGlzLktJQ0tfQkFOX0RFRkFVTFRfREVMQVkgPSA1O1xuXG5cdFx0Ly8gT3V0Z29pbmcgcmVxdWVzdHNcblx0XHR0aGlzLlJFUV9HRVRfREFUQSA9ICdnZXREYXRhJztcblx0XHR0aGlzLlJFUV9TRVRfWk9ORV9TRVRUSU5HUyA9ICdzZXRab25lU2V0dGluZ3MnO1xuXHRcdHRoaXMuUkVRX1NFVF9ST09NX1NFVFRJTkdTID0gJ3NldFJvb21TZXR0aW5ncyc7XG5cdFx0dGhpcy5SRVFfU0VUX1VTRVJfU0VUVElOR1MgPSAnc2V0VXNlclNldHRpbmdzJztcblx0XHR0aGlzLlJFUV9SRUxPQURfWk9ORV9FWFQgPSAncmVsb2FkWm9uZUV4dCc7XG5cdFx0dGhpcy5SRVFfUkVMT0FEX1dPUkRTID0gJ3JlbG9hZFdvcmRzJztcblx0XHR0aGlzLlJFUV9ESVNDT05ORUNUX1VTRVIgPSAnZGlzY29ublVzZXInO1xuXHRcdHRoaXMuUkVRX0tJQ0tfVVNFUiA9ICdraWNrVXNlcic7XG5cdFx0dGhpcy5SRVFfQkFOX1VTRVIgPSAnYmFuVXNlcic7XG5cdFx0dGhpcy5SRVFfR0VUX1JPT01fQ09ORklHID0gJ2dldFJvb21Db25maWcnO1xuXHRcdHRoaXMuUkVRX0NSRUFURV9ST09NID0gJ2NyZWF0ZVJvb20nO1xuXHRcdHRoaXMuUkVRX1JFTU9WRV9ST09NID0gJ3JlbW92ZVJvb20nO1xuXHRcdHRoaXMuUkVRX0FETUlOX01TRyA9ICdhZG1pbk1zZyc7XG5cblx0XHQvLyBJbmNvbWluZyByZXNwb25zZXNcblx0XHR0aGlzLlJFU1BfREFUQSA9ICdkYXRhJztcblx0XHR0aGlzLlJFU1BfVU5FWFBFQ1RFRF9FUlJPUiA9ICd1bmV4cEVycm9yJztcblx0XHR0aGlzLlJFU1BfVVBEQVRFX0VSUk9SID0gJ3VwZGF0ZUVycm9yJztcblx0XHR0aGlzLlJFU1BfUk9PTV9DT05GSUcgPSAncm9vbUNvbmZpZyc7XG5cdFx0dGhpcy5SRVNQX1JPT01fQ09ORklHX0VSUk9SID0gJ3Jvb21Db25maWdFcnInO1xuXHRcdHRoaXMuUkVTUF9ST09NX0NSRUFURUQgPSAncm9vbUNyZWF0ZWQnO1xuXHRcdHRoaXMuUkVTUF9ST09NX0NSRUFUSU9OX0VSUk9SID0gJ3Jvb21FcnJvcic7XG5cdFx0dGhpcy5SRVNQX0xPR19NRVNTQUdFUyA9ICdsb2cnOyAvLyBUaGlzIHJlc3BvbnNlIGRvZXNuJ3QgaGF2ZSBhIGNvcnJlc3BvbmRpbmcgcmVxdWVzdCBiZWNhdXNlIGl0IGlzIG1hbmFnZWQgYnkgYSBzZXJ2ZXIgZXZlbnQgbGlzdGVuZXJcblxuXHRcdHRoaXMuX2N1cnJlbnRSZXF1ZXN0SWQgPSAtMTtcblx0fVxuXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIENPTU1PTiBNT0RVTEUgSU5URVJGQUNFIE1FVEhPRFNcblx0Ly8gVGhpcyBtZW1iZXJzIGFyZSB1c2VkIGJ5IHRoZSBtYWluIGNvbnRyb2xsZXJcblx0Ly8gdG8gY29tbXVuaWNhdGUgd2l0aCB0aGUgbW9kdWxlJ3MgY29udHJvbGxlci5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRpbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKVxuXHR7XG5cdFx0Ly8gQ2FsbCBzdXBlciBtZXRob2Rcblx0XHRzdXBlci5pbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKTtcblxuXHRcdC8vIFNldCBtb25pdG9yaW5nIGludGVyZmFjZVxuXHRcdHRoaXMuX3NldE1vbml0b3JpbmdJbnRlcmZhY2UoKTtcblxuXHRcdC8vIEluaXRpYWxpemUgcHJvZ3Jlc3MgYmFyXG5cdFx0JCgnI3pubS1wcm9ncmVzc0JhcicpLmtlbmRvUHJvZ3Jlc3NCYXIoe1xuXHRcdFx0bWluOiAwLFxuICAgICAgICAgICAgbWF4OiAxMDAsXG5cdFx0XHR2YWx1ZTogZmFsc2UsXG4gICAgICAgICAgICB0eXBlOiAndmFsdWUnLFxuICAgICAgICAgICAgYW5pbWF0aW9uOiB7XG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDQwMFxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuXHRcdC8vIExJU1RTXG5cblx0XHQvLyBJbml0aWFsaXplIGludGVydmFsIGRyb3Bkb3duXG5cdFx0dGhpcy5faW50ZXJ2YWxEcm9wRG93biA9ICQoJyN6bm0taW50ZXJ2YWxERCcpLmtlbmRvRHJvcERvd25MaXN0KHtcblx0XHRcdHZhbHVlVGVtcGxhdGU6ICc8c3BhbiBjbGFzcz1cInRleHQtbXV0ZWQgcHItMVwiPkludGVydmFsOjwvc3Bhbj48c3Bhbj4jOmRhdGEudGV4dCM8L3NwYW4+Jyxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vblVwZGF0ZUludGVydmFsQ2hhbmdlLCB0aGlzKVxuXHRcdH0pLmRhdGEoJ2tlbmRvRHJvcERvd25MaXN0Jyk7XG5cblx0XHQvLyBJbml0aWFsaXplIHBhbmUgYmFyXG5cdFx0dGhpcy5fYWNjb3JkaW9uID0gJCgnI3pubS1wYW5lbGJhcicpLmtlbmRvUGFuZWxCYXIoe1xuICAgICAgICAgICAgZXhwYW5kTW9kZTogJ3NpbmdsZScsXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkodGhpcy5fb25FbnRpdGllc1BhbmVsQ2hhbmdlLCB0aGlzKVxuICAgICAgICB9KS5kYXRhKCdrZW5kb1BhbmVsQmFyJyk7XG5cblx0XHQvLyBJbml0aWFsaXplIGxpc3RzXG5cdFx0dGhpcy5fem9uZUxpc3RCb3ggPSAkKFwiI3pubS16b25lTGlzdFwiKS5rZW5kb0xpc3RCb3goe1xuXHRcdFx0dGVtcGxhdGU6ICc8ZGl2PiM6ZGF0YS5uYW1lIyAoIzpkYXRhLnVzZXJzIyk8L2Rpdj4nLFxuXHRcdFx0Y2hhbmdlOiAkLnByb3h5KHRoaXMuX29uWm9uZVNlbGVjdGVkLCB0aGlzKVxuXHRcdH0pLmRhdGEoJ2tlbmRvTGlzdEJveCcpO1xuXG5cdFx0dGhpcy5fem9uZUxpc3RCb3gud3JhcHBlci5maW5kKCcuay1saXN0Jykub24oJ2RibGNsaWNrJywgJy5rLWl0ZW0nLCAkLnByb3h5KHRoaXMuX29uTW9uaXRvclNlbGVjdGlvbkJ0Q2xpY2ssIHRoaXMpKTtcblxuXHRcdHRoaXMuX3Jvb21MaXN0Qm94ID0gJChcIiN6bm0tcm9vbUxpc3RcIikua2VuZG9MaXN0Qm94KHtcblx0XHRcdHRlbXBsYXRlOiAnPGRpdj4jOmRhdGEubmFtZSMgKCM6ZGF0YS51c2VycyMpPC9kaXY+Jyxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vblJvb21TZWxlY3RlZCwgdGhpcylcblx0XHR9KS5kYXRhKCdrZW5kb0xpc3RCb3gnKTtcblxuXHRcdHRoaXMuX3Jvb21MaXN0Qm94LndyYXBwZXIuZmluZCgnLmstbGlzdCcpLm9uKCdkYmxjbGljaycsICcuay1pdGVtJywgJC5wcm94eSh0aGlzLl9vbk1vbml0b3JTZWxlY3Rpb25CdENsaWNrLCB0aGlzKSk7XG5cblx0XHR0aGlzLl91c2VyTGlzdEJveCA9ICQoXCIjem5tLXVzZXJMaXN0XCIpLmtlbmRvTGlzdEJveCh7XG5cdFx0XHR0ZW1wbGF0ZTogJzxkaXY+IzpkYXRhLm5hbWUjPC9kaXY+Jyxcblx0XHRcdGNoYW5nZTogJC5wcm94eSh0aGlzLl9vblVzZXJTZWxlY3RlZCwgdGhpcylcblx0XHR9KS5kYXRhKCdrZW5kb0xpc3RCb3gnKTtcblxuXHRcdHRoaXMuX3VzZXJMaXN0Qm94LndyYXBwZXIuZmluZCgnLmstbGlzdCcpLm9uKCdkYmxjbGljaycsICcuay1pdGVtJywgJC5wcm94eSh0aGlzLl9vbk1vbml0b3JTZWxlY3Rpb25CdENsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBJbml0aWFsaXplIFJvb20gR3JvdXBzIGRyb3Bkb3duXG5cdFx0dGhpcy5fZ3JvdXBzRHJvcERvd24gPSAkKCcjem5tLXJvb21Hcm91cHNERCcpLmtlbmRvRHJvcERvd25MaXN0KHtcblx0XHRcdGRhdGFUZXh0RmllbGQ6ICdsYWJlbCcsXG4gIFx0XHRcdGRhdGFWYWx1ZUZpZWxkOiAnbmFtZScsXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkodGhpcy5fb25Hcm91cENoYW5nZSwgdGhpcylcblx0XHR9KS5kYXRhKCdrZW5kb0Ryb3BEb3duTGlzdCcpO1xuXG5cdFx0Ly8gQnVpbGQgaW5pdGlhbCBncm91cHMgbGlzdFxuXHRcdHRoaXMuX3NldEdyb3Vwc0RhdGFQcm92aWRlcihudWxsKTtcblxuXHRcdC8vIC8vIENyZWF0ZSBpbnRlcmZhY2UgYnVpbGRlciBpbnN0YW5jZXNcblx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyID0gbmV3IENvbmZpZ0ludGVyZmFjZUJ1aWxkZXIoKTtcblx0XHR0aGlzLl9yb29tQ3JlYXRpb25JQnVpbGRlciA9IG5ldyBDb25maWdJbnRlcmZhY2VCdWlsZGVyKCk7XG5cblx0XHQvLyBBZGQgbGlzdGVuZXIgdG8gc2hvdyBsaW1pdCB3YXJuaW5nIHRvb2x0aXBzXG5cdFx0JCgnI3pubS1wYW5lbGJhcicpLmtlbmRvVG9vbHRpcCh7XG5cdFx0XHRmaWx0ZXI6ICdpW3RpdGxlXS5saW1pdC13YXJuaW5nJyxcblx0XHRcdHBvc2l0aW9uOiAncmlnaHQnLFxuXHRcdFx0d2lkdGg6ICcyNTBweCcsXG5cdFx0XHRjb250ZW50OiBmdW5jdGlvbihlKSB7XG5cdFx0XHRcdHJldHVybiBgPGRpdiBjbGFzcz1cImhlbHAtdG9vbHRpcFwiPiR7ZS50YXJnZXQuZGF0YSgndGl0bGUnKX08L2Rpdj5gO1xuXHRcdFx0fVxuXHRcdH0pO1xuXG5cdFx0Ly8gUk9PTSBDUkVBVElPTiBQQU5FTFxuXG5cdFx0Ly8gQWRkIGNsaWNrIGxpc3RlbmVyIHRvIENyZWF0ZSAmIFJlbW92ZSBSb29tIGJ1dHRvbnNcblx0XHQkKCcjem5tLWNyZWF0ZVJvb21CdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25DcmVhdGVSb29tQnRDbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bm0tcmVtb3ZlUm9vbUJ0Jykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblJlbW92ZVJvb21CdENsaWNrLCB0aGlzKSk7XG5cblx0XHQvLyBDb25maWd1cmUgcm9vbSBjcmVhdGlvbiBwYW5lbFxuXHRcdCQoJyN6bm0tY3JlYXRlUm9vbU1vZGFsJykubW9kYWwoe1xuXHRcdFx0YmFja2Ryb3A6ICdzdGF0aWMnLFxuXHRcdFx0a2V5Ym9hcmQ6IGZhbHNlLFxuXHRcdFx0c2hvdzogZmFsc2UsXG5cdFx0fSk7XG5cblx0XHQvLyBBZGQgbGlzdGVuZXIgZm9yIHJvb20gY3JlYXRpb24gcGFuZWwncyBjcmVhdGUgYnV0dG9uIGNsaWNrXG5cdFx0JCgnI3pubS1yb29tQ3JlYXRvckNyZWF0ZUJ0Jykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblJvb21DcmVhdG9yQ3JlYXRlQnRDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIGZvciByb29tIGNyZWF0aW9uIHBhbmVsIGhpZGVcblx0XHQkKCcjem5tLWNyZWF0ZVJvb21Nb2RhbCcpLm9uKCdoaWRkZW4uYnMubW9kYWwnLCAkLnByb3h5KHRoaXMuX29uQ3JlYXRlUm9vbU1vZGFsSGlkZGVuLCB0aGlzKSk7XG5cblx0XHQvLyBST09NICYgVVNFUiBGSUxURVJJTkcgUEFORUxTXG5cblx0XHQvLyBTZXR1cCBtYXRjaCBleHByZXNzaW9uIGJ1aWxkZXIgcGFuZWwgZm9yIHJvb21zXG5cdFx0dGhpcy5fcm9vbUZpbHRlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd6bm0tcm9vbUZpbHRlcicpO1xuXHRcdHRoaXMuX3Jvb21GaWx0ZXIudGl0bGUgPSAnUm9vbSBGaWx0ZXIgRXhwcmVzc2lvbiBCdWlsZGVyJztcblx0XHR0aGlzLl9yb29tRmlsdGVyLnByZWRlZmluZWRWYXJOYW1lcyA9IFJvb21Qcm9wZXJ0aWVzRGF0YS5wcm9wZXJ0aWVzQXJyYXk7XG5cdFx0JCh0aGlzLl9yb29tRmlsdGVyKS5vbih0aGlzLl9yb29tRmlsdGVyLkVYUFJFU1NJT05fVVBEQVRFRF9FVkVOVCwgJC5wcm94eSh0aGlzLl9vblJvb21GaWx0ZXJVcGRhdGVkLCB0aGlzKSk7XG5cblx0XHQkKCcjem5tLWZpbHRlclJvb21CdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25TaG93Um9vbUZpbHRlckJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLWFwcGx5Um9vbUZpbHRlckNCJykub24oJ2NoYW5nZScsICQucHJveHkodGhpcy5fb25BcHBseUJ0Q2hhbmdlLCB0aGlzKSk7XG5cblx0XHQvLyBTZXR1cCBtYXRjaCBleHByZXNzaW9uIGJ1aWxkZXIgcGFuZWwgZm9yIHVzZXJzXG5cdFx0dGhpcy5fdXNlckZpbHRlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd6bm0tdXNlckZpbHRlcicpO1xuXHRcdHRoaXMuX3VzZXJGaWx0ZXIudGl0bGUgPSAnVXNlciBGaWx0ZXIgRXhwcmVzc2lvbiBCdWlsZGVyJztcblx0XHR0aGlzLl91c2VyRmlsdGVyLnByZWRlZmluZWRWYXJOYW1lcyA9IFVzZXJQcm9wZXJ0aWVzRGF0YS5wcm9wZXJ0aWVzQXJyYXk7XG5cdFx0JCh0aGlzLl91c2VyRmlsdGVyKS5vbih0aGlzLl91c2VyRmlsdGVyLkVYUFJFU1NJT05fVVBEQVRFRF9FVkVOVCwgJC5wcm94eSh0aGlzLl9vblVzZXJGaWx0ZXJVcGRhdGVkLCB0aGlzKSk7XG5cblx0XHQkKCcjem5tLWZpbHRlclVzZXJCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25TaG93VXNlckZpbHRlckJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLWFwcGx5VXNlckZpbHRlckNCJykub24oJ2NoYW5nZScsICQucHJveHkodGhpcy5fb25BcHBseUJ0Q2hhbmdlLCB0aGlzKSk7XG5cblx0XHQvLyBNQUlOIENPTlRST0xTXG5cblx0XHQkKCcjem5tLW1vbml0b3JCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25Nb25pdG9yU2VsZWN0aW9uQnRDbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bm0tc2VuZE1lc3NhZ2VCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25TZW5kQWRtaW5Nc2dCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI3pubS1tZXNzYWdlSW4nKS5vbigna2V5dXAnLCAkLnByb3h5KHRoaXMuX29uU2VuZEFkbWluTXNnSW5LZXlVcCwgdGhpcykpO1xuXG5cdFx0Ly8gTU9OSVRPUklORyBDT05UUk9MU1xuXG5cdFx0JCgnI3pubS1jbG9zZU1vbml0b3JCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25DbG9zZU1vbml0b3JCdENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI3pubS1lZGl0QnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uRWRpdEJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLWNhbmNlbEJ0Jykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vbkNhbmNlbEJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLXN1Ym1pdEJ0Jykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblN1Ym1pdEJ0Q2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIEhpZGUgZWRpdCBjb250cm9sc1xuXHRcdCQoJyN6bm0tZWRpdENvbnRyb2xzJykuaGlkZSgpO1xuXG5cdFx0Ly8gWk9ORSBNT05JVE9SIC0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdFx0Ly8gU2V0IHJlZmVyZW5jZSB0byB1c2VyIGNvdW50IGNoYXJ0XG5cdFx0dGhpcy5fdXNlckNvdW50Q2hhcnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnem5tLXVzZXJDb3VudENoYXJ0Jyk7XG5cblx0XHQvLyBJbml0aWFsaXplIHRpbWUgcmFuZ2Ugc2xpZGVyXG5cdFx0dGhpcy5fdGltZVNsaWRlciA9ICQoJyN6bm0tdGltZVNsaWRlcicpLmtlbmRvUmFuZ2VTbGlkZXIoe1xuXHRcdFx0bWluOiAtMjQsXG4gICAgICAgICAgICBtYXg6IDAsXG4gICAgICAgICAgICBzbWFsbFN0ZXA6IDEsXG4gICAgICAgICAgICBsYXJnZVN0ZXA6IDAsXG5cdFx0XHRjaGFuZ2U6ICQucHJveHkodGhpcy5fb25UaW1lUmFuZ2VDaGFuZ2UsIHRoaXMpLFxuXHRcdFx0dG9vbHRpcDoge1xuXHRcdFx0XHR0ZW1wbGF0ZTogZ2V0VGltZVJhbmdlVG9vbHRpcFRlbXBsYXRlKClcblx0XHRcdH0sXG5cdFx0XHRsZWZ0RHJhZ0hhbmRsZVRpdGxlOiAnRHJhZycsXG5cdFx0XHRyaWdodERyYWdIYW5kbGVUaXRsZTogJ0RyYWcnLFxuXHRcdH0pLmRhdGEoJ2tlbmRvUmFuZ2VTbGlkZXInKTtcblxuXHRcdC8vIFJlc2V0IHRpbWUgcmFuZ2Ugb24gdXNlciBjb3VudCBjaGFydFxuXHRcdHRoaXMuX29uVGltZVJhbmdlQ2hhbmdlKCk7XG5cblx0XHQvLyBBZGQgYnV0dG9uIGNsaWNrIGxpc3RlbmVyc1xuXHRcdCQoJyN6bm0tcmVsb2FkV29yZHNCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25Xb3Jkc1JlbG9hZEJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLXJlbG9hZFpvbmVFeHRCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25ab25lRXh0UmVsb2FkQnRDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBleHRlbnNpb24gbG9nIGdyaWRcblx0XHR0aGlzLl96b25lRXh0TG9nR3JpZCA9ICQoJyN6bm0tem9uZUV4dExvZ0dyaWQnKS5rZW5kb0dyaWQoe1xuXHRcdFx0ZGF0YVNvdXJjZTogW10sXG5cdFx0XHRyZXNpemFibGU6IHRydWUsXG5cdFx0XHRzZWxlY3RhYmxlOiBmYWxzZSxcblx0XHRcdG5vUmVjb3Jkczoge1xuXHRcdFx0XHR0ZW1wbGF0ZTogJ05vIGVudHJpZXMuJ1xuXHRcdFx0fSxcblx0XHRcdGNvbHVtbnM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAndGltZXN0YW1wJyxcblx0XHRcdFx0XHR0aXRsZTogJ1RpbWVzdGFtcCcsXG5cdFx0XHRcdFx0d2lkdGg6IDE4MFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdsZXZlbCcsXG5cdFx0XHRcdFx0dGl0bGU6ICdMZXZlbCcsXG5cdFx0XHRcdFx0d2lkdGg6IDEwMFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmllbGQ6ICdtZXNzYWdlJyxcblx0XHRcdFx0XHR0aXRsZTogJ01lc3NhZ2UnLFxuXHRcdFx0XHRcdHdpZHRoOiA0MDBcblx0XHRcdFx0fSxcblx0XHRcdF0sXG5cdFx0XHRkYXRhQm91bmQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0ICAgIHRoaXMuY29udGVudC5zY3JvbGxUb3AodGhpcy50Ym9keS5oZWlnaHQoKSk7IC8vIFNjcm9sbCB0byBib3R0b21cblx0XHRcdH1cblx0XHR9KS5kYXRhKCdrZW5kb0dyaWQnKTtcblxuXHRcdC8vIFJPT00gTU9OSVRPUiAtLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRcdC8vIEluaXRpYWxpemUgZXh0ZW5zaW9uIGxvZyBncmlkXG5cdFx0dGhpcy5fcm9vbUV4dExvZ0dyaWQgPSAkKCcjem5tLXJvb21FeHRMb2dHcmlkJykua2VuZG9HcmlkKHtcblx0XHRcdGRhdGFTb3VyY2U6IFtdLFxuXHRcdFx0cmVzaXphYmxlOiB0cnVlLFxuXHRcdFx0c2VsZWN0YWJsZTogZmFsc2UsXG5cdFx0XHRub1JlY29yZHM6IHtcblx0XHRcdFx0dGVtcGxhdGU6ICdObyBlbnRyaWVzLidcblx0XHRcdH0sXG5cdFx0XHRjb2x1bW5zOiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRmaWVsZDogJ3RpbWVzdGFtcCcsXG5cdFx0XHRcdFx0dGl0bGU6ICdUaW1lc3RhbXAnLFxuXHRcdFx0XHRcdHdpZHRoOiAxODBcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAnbGV2ZWwnLFxuXHRcdFx0XHRcdHRpdGxlOiAnTGV2ZWwnLFxuXHRcdFx0XHRcdHdpZHRoOiAxMDBcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpZWxkOiAnbWVzc2FnZScsXG5cdFx0XHRcdFx0dGl0bGU6ICdNZXNzYWdlJyxcblx0XHRcdFx0XHR3aWR0aDogNDAwXG5cdFx0XHRcdH0sXG5cdFx0XHRdLFxuXHRcdFx0ZGF0YUJvdW5kOiBmdW5jdGlvbigpIHtcblx0XHRcdCAgICB0aGlzLmNvbnRlbnQuc2Nyb2xsVG9wKHRoaXMudGJvZHkuaGVpZ2h0KCkpOyAvLyBTY3JvbGwgdG8gYm90dG9tXG5cdFx0XHR9XG5cdFx0fSkuZGF0YSgna2VuZG9HcmlkJyk7XG5cblx0XHQvLyBVU0VSIE1PTklUT1IgLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0XHR0aGlzLl9raWNrRGVsYXlJbiA9ICQoJyN6bm0ta2lja0RlbGF5TlMnKS5rZW5kb051bWVyaWNUZXh0Qm94KHtcblx0XHQgICAgbWluOiAwLFxuXHRcdCAgICBzdGVwOiAxLFxuXHRcdCAgICBmb3JtYXQ6ICcjJyxcblx0XHRcdHBsYWNlaG9sZGVyOiAnRGVsYXkgKHMpJ1xuXHRcdH0pLmRhdGEoJ2tlbmRvTnVtZXJpY1RleHRCb3gnKTtcblxuXHRcdHRoaXMuX2Jhbk1vZGVEZCA9ICQoJyN6bm0tYmFuTW9kZUREJykua2VuZG9Ecm9wRG93bkxpc3Qoe1xuXHRcdFx0ZGF0YVNvdXJjZTogWydJUCcsICdOQU1FJ10sXG5cdFx0XHRhdXRvV2lkdGg6IHRydWUsXG5cdFx0XHRvcHRpb25MYWJlbDoge1xuXHRcdFx0XHR0ZXh0OiAnTW9kZSdcblx0XHRcdH0sXG5cdFx0XHRvcHRpb25MYWJlbFRlbXBsYXRlOiAnPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+Izp0ZXh0Izwvc3Bhbj4nLFxuXHRcdH0pLmRhdGEoJ2tlbmRvRHJvcERvd25MaXN0Jyk7XG5cblx0XHR0aGlzLl9iYW5EdXJVbml0RGQgPSAkKCcjem5tLWJhbkR1cmF0aW9uVW5pdEREJykua2VuZG9Ecm9wRG93bkxpc3Qoe1xuXHRcdFx0YXV0b1dpZHRoOiB0cnVlLFxuXHRcdH0pLmRhdGEoJ2tlbmRvRHJvcERvd25MaXN0Jyk7XG5cblx0XHR0aGlzLl9iYW5EdXJhdGlvbkluID0gJCgnI3pubS1iYW5EdXJhdGlvbk5TJykua2VuZG9OdW1lcmljVGV4dEJveCh7XG5cdFx0ICAgIG1pbjogMSxcblx0XHQgICAgc3RlcDogMSxcblx0XHQgICAgZm9ybWF0OiAnIycsXG5cdFx0XHRwbGFjZWhvbGRlcjogJ0R1cmF0aW9uJ1xuXHRcdH0pLmRhdGEoJ2tlbmRvTnVtZXJpY1RleHRCb3gnKTtcblxuXHRcdHRoaXMuX2JhbkRlbGF5SW4gPSAkKCcjem5tLWJhbkRlbGF5TlMnKS5rZW5kb051bWVyaWNUZXh0Qm94KHtcblx0XHQgICAgbWluOiAwLFxuXHRcdCAgICBzdGVwOiAxLFxuXHRcdCAgICBmb3JtYXQ6ICcjJyxcblx0XHRcdHBsYWNlaG9sZGVyOiAnRGVsYXkgKHMpJ1xuXHRcdH0pLmRhdGEoJ2tlbmRvTnVtZXJpY1RleHRCb3gnKTtcblxuXHRcdC8vIEFkZCBidXR0b24gY2xpY2sgbGlzdGVuZXJzXG5cdFx0JCgnI3pubS1kaXNjb25uZWN0QnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uRGlzY29ubmVjdEJ0Q2xpY2ssIHRoaXMpKTtcblx0XHQkKCcjem5tLWtpY2tCdCcpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25LaWNrQnRDbGljaywgdGhpcykpO1xuXHRcdCQoJyN6bm0tYmFuQnQnKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uQmFuQnRDbGljaywgdGhpcykpO1xuXG5cdFx0Ly8gQ2hhcnRzXG5cblx0XHR0aGlzLl9wYWNrZXRRdWV1ZUNoYXJ0ID0gJCgnI3pubS1wYWNrZXRRdWV1ZUNoYXJ0Jykua2VuZG9DaGFydCh7XG5cdFx0XHR0cmFuc2l0aW9uczogZmFsc2UsXG5cdFx0XHRjaGFydEFyZWE6IHtcblx0XHRcdFx0aGVpZ2h0OiAxMDBcblx0XHRcdH0sXG5cdCAgICAgICAgbGVnZW5kOiB7XG5cdCAgICAgICAgICAgIHZpc2libGU6IGZhbHNlXG5cdCAgICAgICAgfSxcblx0ICAgICAgICBzZXJpZXNEZWZhdWx0czoge1xuXHQgICAgICAgICAgICB0eXBlOiAnYmFyJyxcblx0ICAgICAgICAgICAgbGFiZWxzOiB7XG5cdCAgICAgICAgICAgICAgICB2aXNpYmxlOiB0cnVlLFxuXHQgICAgICAgICAgICAgICAgZm9ybWF0OiAnezB9JScsXG5cdCAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kOiAndHJhbnNwYXJlbnQnLFxuXHRcdFx0XHRcdHBvc2l0aW9uOiAnaW5zaWRlQmFzZSdcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0Z2FwOiAuNSxcblx0XHRcdFx0c3BhY2luZzogMCxcblx0XHRcdFx0b3ZlcmxheToge1xuXHRcdFx0XHRcdGdyYWRpZW50OiAnbm9uZSdcblx0XHRcdFx0fSxcblx0XHRcdFx0Ym9yZGVyOiB7XG5cdFx0XHRcdFx0d2lkdGg6IDBcblx0XHRcdFx0fVxuXHQgICAgICAgIH0sXG5cdCAgICAgICAgc2VyaWVzOiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRuYW1lOiAnUGFja2V0IHF1ZXVlJyxcblx0XHQgICAgICAgICAgICBmaWVsZDogJ3ZhbHVlJyxcblx0XHQgICAgICAgICAgICBjYXRlZ29yeUZpZWxkOiAnY2F0ZWdvcnknLFxuXHRcdFx0XHRcdGNvbG9yOiAnI0ZCN0QzMydcblx0XHQgICAgICAgIH1cblx0XHRcdF0sXG5cdCAgICAgICAgdmFsdWVBeGlzOiB7XG5cdCAgICAgICAgICAgIGxhYmVsczoge1xuXHQgICAgICAgICAgICAgICAgZm9ybWF0OiAnezB9JSdcblx0ICAgICAgICAgICAgfSxcblx0ICAgICAgICAgICAgbGluZToge1xuXHQgICAgICAgICAgICAgICAgdmlzaWJsZTogZmFsc2Vcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0bWluOiAwLFxuXHQgICAgXHRcdG1heDogMTAwXG5cdCAgICAgICAgfSxcblx0ICAgICAgICBjYXRlZ29yeUF4aXM6IHtcblx0ICAgICAgICAgICAgbWFqb3JHcmlkTGluZXM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IGZhbHNlXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRcdG1ham9yVGlja3M6IHtcblx0XHRcdFx0XHR2aXNpYmxlOiBmYWxzZVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRsYWJlbHM6IHtcblx0XHRcdFx0XHR2aXNpYmxlOiBmYWxzZVxuXHRcdFx0XHR9XG5cdCAgICAgICAgfSxcblx0XHRcdHRvb2x0aXA6IHtcblx0XHRcdFx0dmlzaWJsZTogZmFsc2UsXG5cdCAgICAgICAgfVxuXHQgICAgfSkuZGF0YSgna2VuZG9DaGFydCcpO1xuXG5cdFx0dGhpcy5fZHJvcHBlZE1zZ0NoYXJ0ID0gJCgnI3pubS1kcm9wcGVkTXNnQ2hhcnQnKS5rZW5kb0NoYXJ0KHtcblx0XHRcdHRyYW5zaXRpb25zOiBmYWxzZSxcblx0XHRcdGNoYXJ0QXJlYToge1xuXHRcdFx0XHRoZWlnaHQ6IDEwMFxuXHRcdFx0fSxcblx0ICAgICAgICBsZWdlbmQ6IHtcblx0ICAgICAgICAgICAgdmlzaWJsZTogZmFsc2Vcblx0ICAgICAgICB9LFxuXHQgICAgICAgIHNlcmllc0RlZmF1bHRzOiB7XG5cdCAgICAgICAgICAgIHR5cGU6ICdiYXInLFxuXHQgICAgICAgICAgICBsYWJlbHM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IHRydWUsXG5cdCAgICAgICAgICAgICAgICBmb3JtYXQ6ICd7MH0nLFxuXHQgICAgICAgICAgICAgICAgYmFja2dyb3VuZDogJ3RyYW5zcGFyZW50Jyxcblx0XHRcdFx0XHRwb3NpdGlvbjogJ2luc2lkZUJhc2UnLFxuXHQgICAgICAgICAgICB9LFxuXHRcdFx0XHRnYXA6IC41LFxuXHRcdFx0XHRzcGFjaW5nOiAwLFxuXHRcdFx0XHRvdmVybGF5OiB7XG5cdFx0XHRcdFx0Z3JhZGllbnQ6ICdub25lJ1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRib3JkZXI6IHtcblx0XHRcdFx0XHR3aWR0aDogMFxuXHRcdFx0XHR9XG5cdCAgICAgICAgfSxcblx0ICAgICAgICBzZXJpZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdG5hbWU6ICdEcm9wcGVkIG1lc3NhZ2VzJyxcblx0XHQgICAgICAgICAgICBmaWVsZDogJ3ZhbHVlJyxcblx0XHQgICAgICAgICAgICBjYXRlZ29yeUZpZWxkOiAnY2F0ZWdvcnknLFxuXHRcdFx0XHRcdGNvbG9yOiAnI0ZCN0QzMydcblx0XHQgICAgICAgIH1cblx0XHRcdF0sXG5cdCAgICAgICAgdmFsdWVBeGlzOiB7XG5cdCAgICAgICAgICAgIGxhYmVsczoge1xuXHQgICAgICAgICAgICAgICAgZm9ybWF0OiAnezB9J1xuXHQgICAgICAgICAgICB9LFxuXHQgICAgICAgICAgICBsaW5lOiB7XG5cdCAgICAgICAgICAgICAgICB2aXNpYmxlOiBmYWxzZVxuXHQgICAgICAgICAgICB9LFxuXHRcdFx0XHRtaW46IDAsXG5cdCAgICAgICAgfSxcblx0ICAgICAgICBjYXRlZ29yeUF4aXM6IHtcblx0ICAgICAgICAgICAgbWFqb3JHcmlkTGluZXM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IGZhbHNlXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRcdG1ham9yVGlja3M6IHtcblx0XHRcdFx0XHR2aXNpYmxlOiBmYWxzZVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRsYWJlbHM6IHtcblx0XHRcdFx0XHR2aXNpYmxlOiBmYWxzZVxuXHRcdFx0XHR9XG5cdCAgICAgICAgfSxcblx0XHRcdHRvb2x0aXA6IHtcblx0XHRcdFx0dmlzaWJsZTogZmFsc2UsXG5cdCAgICAgICAgfVxuXHQgICAgfSkuZGF0YSgna2VuZG9DaGFydCcpO1xuXG5cdFx0dGhpcy5fd3JpdHRlbkRhdGFDaGFydCA9ICQoJyN6bm0td3JpdHRlbkRhdGFDaGFydCcpLmtlbmRvQ2hhcnQoe1xuXHRcdFx0dHJhbnNpdGlvbnM6IGZhbHNlLFxuXHRcdFx0Y2hhcnRBcmVhOiB7XG5cdFx0XHRcdGhlaWdodDogMTAwXG5cdFx0XHR9LFxuXHQgICAgICAgIGxlZ2VuZDoge1xuXHQgICAgICAgICAgICB2aXNpYmxlOiBmYWxzZVxuXHQgICAgICAgIH0sXG5cdCAgICAgICAgc2VyaWVzRGVmYXVsdHM6IHtcblx0ICAgICAgICAgICAgdHlwZTogJ2JhcicsXG5cdCAgICAgICAgICAgIGxhYmVsczoge1xuXHQgICAgICAgICAgICAgICAgdmlzaWJsZTogdHJ1ZSxcblx0ICAgICAgICAgICAgICAgIGZvcm1hdDogJ3swfScsXG5cdCAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kOiAndHJhbnNwYXJlbnQnLFxuXHRcdFx0XHRcdHBvc2l0aW9uOiAnaW5zaWRlQmFzZScsXG5cdFx0XHRcdFx0dGVtcGxhdGU6IGdldFdSQnl0ZXNMYWJlbFRlbXBsYXRlKCksXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRcdGdhcDogLjUsXG5cdFx0XHRcdHNwYWNpbmc6IDAsXG5cdFx0XHRcdG92ZXJsYXk6IHtcblx0XHRcdFx0XHRncmFkaWVudDogJ25vbmUnXG5cdFx0XHRcdH0sXG5cdFx0XHRcdGJvcmRlcjoge1xuXHRcdFx0XHRcdHdpZHRoOiAwXG5cdFx0XHRcdH1cblx0ICAgICAgICB9LFxuXHQgICAgICAgIHNlcmllczogW1xuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bmFtZTogJ1dyaXR0ZW4gZGF0YScsXG5cdFx0ICAgICAgICAgICAgZmllbGQ6ICd2YWx1ZScsXG5cdFx0ICAgICAgICAgICAgY2F0ZWdvcnlGaWVsZDogJ2NhdGVnb3J5Jyxcblx0XHRcdFx0XHRjb2xvcjogJyNGQjdEMzMnXG5cdFx0ICAgICAgICB9XG5cdFx0XHRdLFxuXHQgICAgICAgIHZhbHVlQXhpczoge1xuXHQgICAgICAgICAgICBsYWJlbHM6IHtcblx0ICAgICAgICAgICAgICAgIGZvcm1hdDogJ3swfSdcblx0ICAgICAgICAgICAgfSxcblx0ICAgICAgICAgICAgbGluZToge1xuXHQgICAgICAgICAgICAgICAgdmlzaWJsZTogZmFsc2Vcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0bWluOiAwLFxuXHQgICAgICAgIH0sXG5cdCAgICAgICAgY2F0ZWdvcnlBeGlzOiB7XG5cdCAgICAgICAgICAgIG1ham9yR3JpZExpbmVzOiB7XG5cdCAgICAgICAgICAgICAgICB2aXNpYmxlOiBmYWxzZVxuXHQgICAgICAgICAgICB9LFxuXHRcdFx0XHRtYWpvclRpY2tzOiB7XG5cdFx0XHRcdFx0dmlzaWJsZTogZmFsc2Vcblx0XHRcdFx0fSxcblx0XHRcdFx0bGFiZWxzOiB7XG5cdFx0XHRcdFx0dmlzaWJsZTogZmFsc2Vcblx0XHRcdFx0fVxuXHQgICAgICAgIH0sXG5cdFx0XHR0b29sdGlwOiB7XG5cdFx0XHRcdHZpc2libGU6IGZhbHNlLFxuXHQgICAgICAgIH1cblx0ICAgIH0pLmRhdGEoJ2tlbmRvQ2hhcnQnKTtcblxuXHRcdHRoaXMuX3JlYWREYXRhQ2hhcnQgPSAkKCcjem5tLXJlYWREYXRhQ2hhcnQnKS5rZW5kb0NoYXJ0KHtcblx0XHRcdHRyYW5zaXRpb25zOiBmYWxzZSxcblx0XHRcdGNoYXJ0QXJlYToge1xuXHRcdFx0XHRoZWlnaHQ6IDEwMFxuXHRcdFx0fSxcblx0ICAgICAgICBsZWdlbmQ6IHtcblx0ICAgICAgICAgICAgdmlzaWJsZTogZmFsc2Vcblx0ICAgICAgICB9LFxuXHQgICAgICAgIHNlcmllc0RlZmF1bHRzOiB7XG5cdCAgICAgICAgICAgIHR5cGU6ICdiYXInLFxuXHQgICAgICAgICAgICBsYWJlbHM6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IHRydWUsXG5cdCAgICAgICAgICAgICAgICBmb3JtYXQ6ICd7MH0nLFxuXHQgICAgICAgICAgICAgICAgYmFja2dyb3VuZDogJ3RyYW5zcGFyZW50Jyxcblx0XHRcdFx0XHRwb3NpdGlvbjogJ2luc2lkZUJhc2UnLFxuXHRcdFx0XHRcdHRlbXBsYXRlOiBnZXRXUkJ5dGVzTGFiZWxUZW1wbGF0ZSgpLFxuXHQgICAgICAgICAgICB9LFxuXHRcdFx0XHRnYXA6IC41LFxuXHRcdFx0XHRzcGFjaW5nOiAwLFxuXHRcdFx0XHRvdmVybGF5OiB7XG5cdFx0XHRcdFx0Z3JhZGllbnQ6ICdub25lJ1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRib3JkZXI6IHtcblx0XHRcdFx0XHR3aWR0aDogMFxuXHRcdFx0XHR9XG5cdCAgICAgICAgfSxcblx0ICAgICAgICBzZXJpZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdG5hbWU6ICdSZWFkIGRhdGEnLFxuXHRcdCAgICAgICAgICAgIGZpZWxkOiAndmFsdWUnLFxuXHRcdCAgICAgICAgICAgIGNhdGVnb3J5RmllbGQ6ICdjYXRlZ29yeScsXG5cdFx0XHRcdFx0Y29sb3I6ICcjRkI3RDMzJ1xuXHRcdCAgICAgICAgfVxuXHRcdFx0XSxcblx0ICAgICAgICB2YWx1ZUF4aXM6IHtcblx0ICAgICAgICAgICAgbGFiZWxzOiB7XG5cdCAgICAgICAgICAgICAgICBmb3JtYXQ6ICd7MH0nXG5cdCAgICAgICAgICAgIH0sXG5cdCAgICAgICAgICAgIGxpbmU6IHtcblx0ICAgICAgICAgICAgICAgIHZpc2libGU6IGZhbHNlXG5cdCAgICAgICAgICAgIH0sXG5cdFx0XHRcdG1pbjogMCxcblx0ICAgICAgICB9LFxuXHQgICAgICAgIGNhdGVnb3J5QXhpczoge1xuXHQgICAgICAgICAgICBtYWpvckdyaWRMaW5lczoge1xuXHQgICAgICAgICAgICAgICAgdmlzaWJsZTogZmFsc2Vcblx0ICAgICAgICAgICAgfSxcblx0XHRcdFx0bWFqb3JUaWNrczoge1xuXHRcdFx0XHRcdHZpc2libGU6IGZhbHNlXG5cdFx0XHRcdH0sXG5cdFx0XHRcdGxhYmVsczoge1xuXHRcdFx0XHRcdHZpc2libGU6IGZhbHNlXG5cdFx0XHRcdH1cblx0ICAgICAgICB9LFxuXHRcdFx0dG9vbHRpcDoge1xuXHRcdFx0XHR2aXNpYmxlOiBmYWxzZSxcblx0ICAgICAgICB9XG5cdCAgICB9KS5kYXRhKCdrZW5kb0NoYXJ0Jyk7XG5cblx0XHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRcdC8vIEFkZCBsaXN0ZW5lciB0byByZWRyYXcgYWxsIGNoYXJ0cyBpbiBjYXNlIG9mIHdpbmRvdyByZXNpemVcblx0XHQkKHdpbmRvdykub24oJ3Jlc2l6ZScsICQucHJveHkodGhpcy5fb25XaW5kb3dSZXNpemUsIHRoaXMpKTtcblx0XHR0aGlzLl9vbldpbmRvd1Jlc2l6ZSgpOyAvLyBBbHNvIGRvIGl0IGltbWVkaWF0ZWx5XG5cblx0XHQvLyBSZXF1ZXN0IGRhdGEgdG8gc2VydmVyXG5cdFx0dGhpcy5fcmVxdWVzdERhdGEodHJ1ZSk7XG5cdH1cblxuXHRkZXN0cm95KClcblx0e1xuXHRcdC8vIENhbGwgc3VwZXIgbWV0aG9kXG5cdFx0c3VwZXIuZGVzdHJveSgpO1xuXG5cdFx0Ly8gQ2xvc2UgbW9uaXRvcmluZ1xuXHRcdHRoaXMuX29uQ2xvc2VNb25pdG9yQnRDbGljaygpO1xuXG5cdFx0Ly8gUmVtb3ZlIGRvdWJsZWNsaWNrIGV2ZW50IGxpc3RlbmVyc1xuXHRcdHRoaXMuX3pvbmVMaXN0Qm94LndyYXBwZXIuZmluZCgnLmstbGlzdCcpLm9mZignZGJsY2xpY2snKTtcblx0XHR0aGlzLl9yb29tTGlzdEJveC53cmFwcGVyLmZpbmQoJy5rLWxpc3QnKS5vZmYoJ2RibGNsaWNrJyk7XG5cdFx0dGhpcy5fdXNlckxpc3RCb3gud3JhcHBlci5maW5kKCcuay1saXN0Jykub2ZmKCdkYmxjbGljaycpO1xuXG5cdFx0Ly8gUmVtb3ZlIG90aGVyIGV2ZW50IGxpc3RlbmVyc1xuXHRcdCQoJyN6bm0tY3JlYXRlUm9vbUJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tcmVtb3ZlUm9vbUJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tcm9vbUNyZWF0b3JDcmVhdGVCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLWZpbHRlclJvb21CdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLWFwcGx5Um9vbUZpbHRlckNCJykub2ZmKCdjaGFuZ2UnKTtcblx0XHQkKCcjem5tLWZpbHRlclVzZXJCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLWFwcGx5VXNlckZpbHRlckNCJykub2ZmKCdjaGFuZ2UnKTtcblx0XHQkKCcjem5tLW1vbml0b3JCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLXNlbmRNZXNzYWdlQnQnKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCgnI3pubS1tZXNzYWdlSW4nKS5vZmYoJ2tleXVwJyk7XG5cdFx0JCgnI3pubS1jbG9zZU1vbml0b3JCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLWVkaXRCdCcpLm9mZignY2xpY2snKTtcblx0XHQkKCcjem5tLWNhbmNlbEJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tc3VibWl0QnQnKS5vZmYoJ2NsaWNrJyk7XG5cdFx0JCh3aW5kb3cpLm9mZigncmVzaXplJyk7XG5cdFx0JCgnI3pubS1yZWxvYWRXb3Jkc0J0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tcmVsb2FkWm9uZUV4dEJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tZGlzY29ubmVjdEJ0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0ta2lja0J0Jykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyN6bm0tYmFuQnQnKS5vZmYoJ2NsaWNrJyk7XG5cblx0XHQvLyBDbGVhciByZXF1ZXN0IHNjaGVkdWxpbmdcblx0XHRjbGVhclRpbWVvdXQodGhpcy5fcmVxdWVzdFRpbWVyKTtcblxuXHRcdC8vIEhpZGUgcm9vbSBjcmVhdGlvbiBwYW5lbFxuXHRcdCQoJyN6bm0tY3JlYXRlUm9vbU1vZGFsJykubW9kYWwoJ2hpZGUnKTtcblxuXHRcdC8vIFJlbW92ZSByb29tIGNyZWF0aW9uIHBhbmVsIGxpc3RlbmVyXG5cdFx0JCgnI3pubS1jcmVhdGVSb29tTW9kYWwnKS5vZmYoJ2hpZGRlbi5icy5tb2RhbCcpO1xuXG5cdFx0Ly8gUmVtb3ZlIGZpbHRlciBwYW5lbHMgbGlzdGVuZXJzXG5cdFx0JCh0aGlzLl9yb29tRmlsdGVyKS5vZmYodGhpcy5fcm9vbUZpbHRlci5FWFBSRVNTSU9OX1VQREFURURfRVZFTlQpO1xuXHRcdCQodGhpcy5fdXNlckZpbHRlcikub2ZmKHRoaXMuX3VzZXJGaWx0ZXIuRVhQUkVTU0lPTl9VUERBVEVEX0VWRU5UKTtcblx0fVxuXG5cdG9uRXh0ZW5zaW9uQ29tbWFuZChjb21tYW5kLCBkYXRhKVxuXHR7XG5cdFx0Ly8gRGF0YSByZWNlaXZlZFxuXHRcdGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9EQVRBKVxuXHRcdHtcblx0XHRcdC8vIFdlIGhhdmUgdG8gY2hlY2sgaWYgdGhlIHJlc3BvbnNlIGlkIGNvcnJlc3BvbmRzIHRvIHRoZSBjdXJyZW50IHJlcXVlc3QgaWQ7XG5cdFx0XHQvLyBpZiBub3QsIHRoZSByZXNwb25zZSBpcyBkaXNjYXJkZWQgYXMgaXQgcmVmZXJzIHRvIGFuIG91dGRhdGV0IHJlcXVlc3Rcblx0XHRcdGNvbnN0IHJlc3BvbnNlSWQgPSBkYXRhLmdldEludCgnaWQnKTtcblxuXHRcdFx0aWYgKHJlc3BvbnNlSWQgPT0gdGhpcy5fY3VycmVudFJlcXVlc3RJZClcblx0XHRcdHtcblx0XHRcdFx0Ly8gLS0tIFpPTkVTIExJU1QgLS0tXG5cdFx0XHRcdGlmIChkYXRhLmNvbnRhaW5zS2V5KCd6b25lcycpKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0dGhpcy5fc2V0Wm9uZXNEYXRhUHJvdmlkZXIoZGF0YS5nZXRTRlNBcnJheSgnem9uZXMnKSk7XG5cblx0XHRcdFx0XHRpZiAodGhpcy5fc2VsZWN0ZWRab25lKVxuXHRcdFx0XHRcdFx0dGhpcy5fc2V0U2NvcGVMYWJlbCgpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gLS0tIFJPT01TIExJU1QgLS0tXG5cdFx0XHRcdGlmIChkYXRhLmNvbnRhaW5zS2V5KCdyb29tcycpKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0dGhpcy5fc2V0R3JvdXBzRGF0YVByb3ZpZGVyKGRhdGEuZ2V0VXRmU3RyaW5nQXJyYXkoJ2dyb3VwcycpKTtcblxuXHRcdFx0XHRcdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRcdFx0XHRcdC8vIElmIGEgbnVsbCByb29tcyBsaXN0IGlzIHJldHVybmVkLCB0aGUgem9uZSBtdXN0IGhhdmUgYmVlbiBkZWxldGVkIVxuXHRcdFx0XHRcdGlmICghZGF0YS5pc051bGwoJ3Jvb21zJykpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0dGhpcy5fc2V0Um9vbXNEYXRhUHJvdmlkZXIoZGF0YS5nZXRTRlNBcnJheSgncm9vbXMnKSk7XG5cblx0XHRcdFx0XHRcdGlmICghdGhpcy5fc2VsZWN0ZWRSb29tKVxuXHRcdFx0XHRcdFx0XHR0aGlzLl9zZXRTY29wZUxhYmVsKCk7XG5cblx0XHRcdFx0XHRcdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRcdFx0XHRcdFx0Ly8gU2hvdyB3YXJuaW5nIGlmIHRoZSByb29tcyBsaXN0IGV4Y2VlZHMgdGhlIHNlcnZlci1zaWRlIGxpbWl0XG5cdFx0XHRcdFx0XHRpZiAoZGF0YS5jb250YWluc0tleSgnclNpemUnKSlcblx0XHRcdFx0XHRcdFx0dGhpcy5fc2hvd0xpbWl0RXhjZWVkZWRXYXJuaW5nKCQoJyN6bm0tcm9vbUxpc3RXYXJuaW5nJyksICdUaGUgcmVjZWl2ZWQgbGlzdCBpcyBpbmNvbXBsZXRlLCBiZWNhdXNlIGl0cyBzaXplICgnICsgZGF0YS5nZXRJbnQoJ3JTaXplJykgKyAnIFJvb21zKSBleGNlZWRlZCB0aGlzIGNsaWVudFxcJ3MgbGltaXQgb2YgJyArIGRhdGEuZ2V0SW50KCdyTGltaXQnKSArICc7IHlvdSBzaG91bGQgcmVmaW5lIHlvdXIgc2VhcmNoJyk7XG5cdFx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0XHRcdHRoaXMuX3Nob3dMaW1pdEV4Y2VlZGVkV2FybmluZygkKCcjem5tLXJvb21MaXN0V2FybmluZycpLCAnJyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2Vcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRpZiAodGhpcy5fYWN0aXZlUGFuZWxUeXBlID09ICdyb29tJylcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0dGhpcy5fYWNjb3JkaW9uLnNlbGVjdCgnW2RhdGEtaXRlbS10eXBlPVwiem9uZVwiXScpO1xuXHRcdFx0XHRcdFx0XHR0aGlzLl9hY2NvcmRpb24uZXhwYW5kKCdbZGF0YS1pdGVtLXR5cGU9XCJ6b25lXCJdJyk7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX29uWm9uZVNlbGVjdGVkKCk7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX29uRW50aXRpZXNQYW5lbENoYW5nZSgpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIC0tLSBVU0VSUyBMSVNUIC0tLVxuXHRcdFx0XHRpZiAoZGF0YS5jb250YWluc0tleSgndXNlcnMnKSlcblx0XHRcdFx0e1xuXHRcdFx0XHRcdC8vIElmIGEgbnVsbCB1c2VycyBsaXN0IGlzIHJldHVybmVkLCB0aGUgcm9vbSBtdXN0IGhhdmUgYmVlbiBkZWxldGVkIVxuXHRcdFx0XHRcdGlmICghZGF0YS5pc051bGwoJ3VzZXJzJykpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0dGhpcy5fc2V0VXNlcnNEYXRhUHJvdmlkZXIoZGF0YS5nZXRTRlNBcnJheSgndXNlcnMnKSk7XG5cblx0XHRcdFx0XHRcdC8vIFNob3cgd2FybmluZyBpZiB0aGUgdXNlcnMgbGlzdCBleGNlZWRzIHRoZSBzZXJ2ZXItc2lkZSBsaW1pdFxuXHRcdFx0XHRcdFx0aWYgKGRhdGEuY29udGFpbnNLZXkoJ3VTaXplJykpXG5cdFx0XHRcdFx0XHRcdHRoaXMuX3Nob3dMaW1pdEV4Y2VlZGVkV2FybmluZygkKCcjem5tLXVzZXJMaXN0V2FybmluZycpLCAnVGhlIHJlY2VpdmVkIGxpc3QgaXMgaW5jb21wbGV0ZSwgYmVjYXVzZSBpdHMgc2l6ZSAoJyArIGRhdGEuZ2V0SW50KCd1U2l6ZScpICsgJyB1c2VycykgZXhjZWVkcyB0aGlzIGNsaWVudFxcJ3MgbGltaXQgb2YgJyArIGRhdGEuZ2V0SW50KCd1TGltaXQnKSArICc7IHlvdSBzaG91bGQgcmVmaW5lIHlvdXIgc2VhcmNoJyk7XG5cdFx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0XHRcdHRoaXMuX3Nob3dMaW1pdEV4Y2VlZGVkV2FybmluZygkKCcjem5tLXVzZXJMaXN0V2FybmluZycpLCAnJyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2Vcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRpZiAodGhpcy5fYWN0aXZlUGFuZWxUeXBlID09ICd1c2VyJylcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0dGhpcy5fYWNjb3JkaW9uLnNlbGVjdCgnW2RhdGEtaXRlbS10eXBlPVwicm9vbVwiXScpO1xuXHRcdFx0XHRcdFx0XHR0aGlzLl9hY2NvcmRpb24uZXhwYW5kKCdbZGF0YS1pdGVtLXR5cGU9XCJyb29tXCJdJyk7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX29uUm9vbVNlbGVjdGVkKCk7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX29uRW50aXRpZXNQYW5lbENoYW5nZSgpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIC0tLSBNT05JVE9SRUQgREFUQSAtLS1cblx0XHRcdFx0aWYgKGRhdGEuY29udGFpbnNLZXkoJ21vbml0b3JlZCcpKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Y29uc3Qgem9uZSA9IGRhdGEuZ2V0U0ZTT2JqZWN0KCdtb25pdG9yZWQnKS5nZXRVdGZTdHJpbmcoJ3pvbmUnKTtcblx0XHRcdFx0XHRjb25zdCB0eXBlID0gZGF0YS5nZXRTRlNPYmplY3QoJ21vbml0b3JlZCcpLmdldFV0ZlN0cmluZygndHlwZScpO1xuXHRcdFx0XHRcdGNvbnN0IG5hbWUgPSBkYXRhLmdldFNGU09iamVjdCgnbW9uaXRvcmVkJykuZ2V0VXRmU3RyaW5nKCduYW1lJyk7XG5cblx0XHRcdFx0XHRpZiAoem9uZSA9PSB0aGlzLl9tb25pdG9yZWRab25lICYmIHR5cGUgPT0gdGhpcy5fbW9uaXRvcmVkVHlwZSAmJiBuYW1lID09IHRoaXMuX21vbml0b3JlZE5hbWUpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0aWYgKCFkYXRhLmdldFNGU09iamVjdCgnbW9uaXRvcmVkJykuaXNOdWxsKCdwYXJhbXMnKSlcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0Y29uc3QgcGFyYW1zID0gZGF0YS5nZXRTRlNPYmplY3QoJ21vbml0b3JlZCcpLmdldFNGU0FycmF5KCdwYXJhbXMnKTtcblxuXHRcdFx0XHRcdFx0XHR0aGlzLl9zZXRNb25pdG9yaW5nSW50ZXJmYWNlKCk7XG5cblx0XHRcdFx0XHRcdFx0Ly8gQnVpbGQgaW50ZXJmYWNlXG5cdFx0XHRcdFx0XHRcdGlmICghdGhpcy5faXNFZGl0aW5nKVxuXHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0dGhpcy5faW50ZXJmYWNlQnVpbGRlci5idWlsZEludGVyZmFjZShwYXJhbXMsIGB6bm0tJHt0aGlzLl9tb25pdG9yZWRUeXBlfVRhYk5hdmlnYXRvcmAsIHRydWUpO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCF0aGlzLl9za2lwSW5pdFRhYnMpXG5cdFx0XHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHRcdFx0JChgI3pubS0ke3RoaXMuX21vbml0b3JlZFR5cGV9VGFiTmF2aWdhdG9yICN0YWJzYCkuc2Nyb2xsaW5nVGFicyh7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdGJvb3RzdHJhcFZlcnNpb246IDQsXG5cdFx0XHRcdFx0XHRcdFx0XHRcdHNjcm9sbFRvVGFiRWRnZTogdHJ1ZSxcblx0XHRcdFx0XHRcdFx0XHRcdFx0ZW5hYmxlU3dpcGluZzogdHJ1ZSxcblx0XHRcdFx0XHRcdFx0XHRcdFx0ZGlzYWJsZVNjcm9sbEFycm93c09uRnVsbHlTY3JvbGxlZDogdHJ1ZSxcblx0XHRcdFx0XHRcdFx0XHRcdFx0Y3NzQ2xhc3NMZWZ0QXJyb3c6ICdmYSBmYS1jaGV2cm9uLWxlZnQnLFxuXHRcdFx0XHRcdFx0XHRcdFx0XHRjc3NDbGFzc1JpZ2h0QXJyb3c6ICdmYSBmYS1jaGV2cm9uLXJpZ2h0J1xuXHRcdFx0XHRcdFx0XHRcdFx0fSk7XG5cblx0XHRcdFx0XHRcdFx0XHRcdHRoaXMuX3NraXBJbml0VGFicyA9IHRydWU7XG5cblx0XHRcdFx0XHRcdFx0XHRcdC8vIFNldCBmaXJzdCB0YWIgcGFuZWwgYXMgYWN0aXZlXG5cdFx0XHRcdFx0XHRcdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmFjdGl2YXRlRmlyc3RUYWJQYW5lbCgpO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHQvLyBSZWRyYXcgdGltZSByYW5nZSBzbGlkZXIgdG8gc2V0IGl0cyB3aWR0aFxuXHRcdFx0XHRcdFx0XHRcdFx0dGhpcy5fdGltZVNsaWRlci5yZXNpemUoKTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIHJlZHJhdyBhbGwgY2hhcnRzIG9uIHRhYiBjaGFuZ2Vcblx0XHRcdFx0XHRcdFx0XHRcdC8vICh0byB3b3JrIGFyb3VuZCBhbiBpc3N1ZSB3aXRoIHRoZSBjaGFydCByZXNpemluZyB0byBkZWZhdWx0IHdpZHRoIHdoZW4gcGFuZSBpcyBoaWRkZW4pXG5cdFx0XHRcdFx0XHRcdFx0XHQkKCdhW2RhdGEtdG9nZ2xlPVwidGFiXCJdJykub24oJ3Nob3duLmJzLnRhYicsICQucHJveHkodGhpcy5fb25XaW5kb3dSZXNpemUsIHRoaXMpKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHQvLyBEaXNhYmxlIGNhdGVnb3JpZXNcblx0XHRcdFx0XHRcdFx0bGV0IGRpc2FibGVkQ2F0ID0gKGRhdGEuZ2V0U0ZTT2JqZWN0KCdtb25pdG9yZWQnKS5jb250YWluc0tleSgnZXhjbHVkZScpID8gZGF0YS5nZXRTRlNPYmplY3QoJ21vbml0b3JlZCcpLmdldFV0ZlN0cmluZ0FycmF5KCdleGNsdWRlJykgOiBbXSk7XG5cblx0XHRcdFx0XHRcdFx0Zm9yIChsZXQgY2F0IG9mIGRpc2FibGVkQ2F0KVxuXHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0bGV0IG5hdkxpbmsgPSAkKGAjem5tLSR7dGhpcy5fbW9uaXRvcmVkVHlwZX1UYWJOYXZpZ2F0b3IgI3RhYnMgI3RhYi0ke2NhdH1gKTtcblxuXHRcdFx0XHRcdFx0XHRcdG5hdkxpbmsuYWRkQ2xhc3MoJ2Rpc2FibGVkJyk7XG5cdFx0XHRcdFx0XHRcdFx0bmF2TGluay5hdHRyKCd0YWJpbmRleCcsIC0xKTtcblx0XHRcdFx0XHRcdFx0XHRuYXZMaW5rLmF0dHIoJ2FyaWEtZGlzYWJsZWQnLCB0cnVlKTtcblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdGlmICh0aGlzLl9tb25pdG9yZWRUeXBlID09IHRoaXMuTU9OSVRPUkVEX1RZUEVfWk9ORSlcblx0XHRcdFx0XHRcdFx0XHR0aGlzLl9zaG93Wm9uZVRyYWZmaWNEYXRhKGRhdGEuZ2V0U0ZTT2JqZWN0KCdtb25pdG9yZWQnKS5nZXRTRlNPYmplY3QoJ3RyYWZmaWMnKSk7XG5cblx0XHRcdFx0XHRcdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9VU0VSKVxuXHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0Ly8gU3RhdHNcblx0XHRcdFx0XHRcdFx0XHR0aGlzLl9zaG93VXNlclN0YXRzRGF0YShkYXRhLmdldFNGU09iamVjdCgnbW9uaXRvcmVkJykuZ2V0U0ZTT2JqZWN0KCdzdGF0cycpKTtcblxuXHRcdFx0XHRcdFx0XHRcdC8vIE5QQ1xuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IGlzTnBjID0gZGF0YS5nZXRTRlNPYmplY3QoJ21vbml0b3JlZCcpLmdldEJvb2woJ25wYycpO1xuXG5cdFx0XHRcdFx0XHRcdFx0Ly8gRGlzYWJsZSBkaXNjb25uZWN0LCBraWNrIGFuZCBiYW4gYnV0dG9uc1xuXHRcdFx0XHRcdFx0XHRcdCQoJyN6bm0tZGlzY29ubmVjdEJ0JykuYXR0cignZGlzYWJsZWQnLCBpc05wYyk7XG5cdFx0XHRcdFx0XHRcdFx0JCgnI3pubS1raWNrQnQnKS5hdHRyKCdkaXNhYmxlZCcsIGlzTnBjKTtcblx0XHRcdFx0XHRcdFx0XHQkKCcjem5tLWJhbkJ0JykuYXR0cignZGlzYWJsZWQnLCBpc05wYyk7XG5cblx0XHRcdFx0XHRcdFx0XHQvLyBHZW8tbG9jYXRpb25cblx0XHRcdFx0XHRcdFx0XHRjb25zdCBnZW9Mb2NEYXRhID0gZGF0YS5nZXRTRlNPYmplY3QoJ21vbml0b3JlZCcpLmdldFNGU09iamVjdCgnZ2VvTG9jJyk7XG5cdFx0XHRcdFx0XHRcdFx0dGhpcy5fc2V0R2VvTG9jYXRpb25VSShnZW9Mb2NEYXRhKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0ZWxzZVxuXHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChgVGhlICR7Y2FwaXRhbGl6ZUZpcnN0KHRoaXMuX21vbml0b3JlZFR5cGUpfSBiZWluZyBtb25pdG9yZWQgaXMgbm8gbW9yZSBhdmFpbGFibGUgb24gdGhlIHNlcnZlciwgcGxlYXNlIHNlbGVjdCBhbm90aGVyIG9uZS5gLCBmYWxzZSk7XG5cblx0XHRcdFx0XHRcdFx0Ly8gQ2FuY2VsIG1vbml0b3Jpbmdcblx0XHRcdFx0XHRcdFx0dGhpcy5fb25DbG9zZU1vbml0b3JCdENsaWNrKCk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfVVBEQVRFX0VSUk9SKVxuXHRcdHtcblx0XHRcdGlmIChkYXRhLmdldFV0ZlN0cmluZygnem9uZScpID09IHRoaXMuX21vbml0b3JlZFpvbmVcblx0XHRcdFx0JiYgZGF0YS5nZXRVdGZTdHJpbmcoJ3R5cGUnKSA9PSAgdGhpcy5fbW9uaXRvcmVkVHlwZVxuXHRcdFx0XHQmJiBkYXRhLmdldFV0ZlN0cmluZygnbmFtZScpID09ICB0aGlzLl9tb25pdG9yZWROYW1lKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChgVW5hYmxlIHRvIHVwZGF0ZSAke2NhcGl0YWxpemVGaXJzdCh0aGlzLl9tb25pdG9yZWRUeXBlKX0gc2V0dGluZ3M7IHRoZSBmb2xsb3dpbmcgZXJyb3Igd2FzIHJlcG9ydGVkOiAke2RhdGEuZ2V0VXRmU3RyaW5nKCdtZXNzYWdlJyl9LmApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1JPT01fQ09ORklHKVxuXHRcdHtcblx0XHRcdC8vIFJlLWVuYWJsZSBDcmVhdGUgYnV0dG9uXG5cdFx0XHQkKCcjem5tLWNyZWF0ZVJvb21CdCcpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXG5cdFx0XHQvLyBTaG93IFJvb20gY3JlYXRpb24gcGFuZWxcblx0XHRcdHRoaXMuX3Nob3dSb29tQ3JlYXRpb25QYW5lbChkYXRhLmdldFNGU0FycmF5KCdzZXR0aW5ncycpKTtcblx0XHR9XG5cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ST09NX0NPTkZJR19FUlJPUilcblx0XHR7XG5cdFx0XHQvLyBSZS1lbmFibGUgQ3JlYXRlIGJ1dHRvblxuXHRcdFx0JCgnI3pubS1jcmVhdGVSb29tQnQnKS5hdHRyKCdkaXNhYmxlZCcsIGZhbHNlKTtcblxuXHRcdFx0Ly8gU2hvdyBhbiBhbGVydFxuXHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGRhdGEuZ2V0VXRmU3RyaW5nKCdlcnJvcicpKTtcblx0XHR9XG5cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ST09NX0NSRUFURUQpXG5cdFx0e1xuXHRcdFx0Ly8gUmUtZW5hYmxlIHJvb20gY3JlYXRpb24gcGFuZWxcblx0XHRcdHRoaXMuX2VuYWJsZUNyZWF0ZVJvb21QYW5lbCh0cnVlKTtcblxuXHRcdFx0Ly8gSGlkZSByb29tIGNyZWF0aW9uIHBhbmVsXG5cdFx0XHQkKCcjem5tLWNyZWF0ZVJvb21Nb2RhbCcpLm1vZGFsKCdoaWRlJyk7XG5cblx0XHRcdC8vIFVwZGF0ZSByb29tcyBsaXN0XG5cdFx0XHR0aGlzLl9yZXF1ZXN0RGF0YSh0cnVlKTtcblx0XHR9XG5cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9ST09NX0NSRUFUSU9OX0VSUk9SKVxuXHRcdHtcblx0XHRcdC8vIERpc3BsYXkgZXJyb3IgbWVzc2FnZVxuXHRcdFx0JCgnI3pubS1jcmVhdGVSb29tRXJyb3InKS50ZXh0KGRhdGEuZ2V0VXRmU3RyaW5nKCdlcnJvcicpKTtcblx0XHRcdCQoJyN6bm0tY3JlYXRlUm9vbUVycm9yJykuc2hvdygpO1xuXG5cdFx0XHQvLyBSZS1lbmFibGUgcm9vbSBjcmVhdGlvbiBwYW5lbFxuXHRcdFx0dGhpcy5fZW5hYmxlQ3JlYXRlUm9vbVBhbmVsKHRydWUpO1xuXHRcdH1cblxuXHRcdC8vIFVuZXhwZWN0ZWQgZXJyb3IgcmVjZWl2ZWRcblx0XHQvLyBUaGlzIGVycm9yIGlzIHJldHVybmVkIGluIGNhc2Ugc29tZXRoaW5nIHdlbnQgd3Jvbmcgd2hlbiByZXRyaWV2aW5nIHRoZSBtb25pdG9yZWQgZW50aXR5IHBhcmFtZXRlcnMsXG5cdFx0Ly8gc28gd2UgaGF2ZSB0byBzdG9wIHJlcXVlc3RpbmcgdGhlIHNhbWUgZW50aXR5XG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfVU5FWFBFQ1RFRF9FUlJPUilcblx0XHR7XG5cdFx0XHQvLyBDYW5jZWwgbW9uaXRvcmluZ1xuXHRcdFx0dGhpcy5fb25DbG9zZU1vbml0b3JCdENsaWNrKCk7XG5cblx0XHRcdC8vIFNob3cgYW4gYWxlcnRcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChkYXRhLmdldFV0ZlN0cmluZygnZXJyb3InKSk7XG5cdFx0fVxuXG5cdFx0Ly8gRXh0ZW5zaW9uIChab25lIG9yIFJvb20pIGxvZyBtZXNzYWdlcyByZWNlaXZlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX0xPR19NRVNTQUdFUylcblx0XHR7XG5cdFx0XHRsZXQgbG9nRW50cmllcyA9IGRhdGEuZ2V0U0ZTQXJyYXkoJ2VudHJpZXMnKTtcblxuXHRcdFx0Zm9yIChsZXQgZSA9IDA7IGUgPCBsb2dFbnRyaWVzLnNpemUoKTsgZSsrKVxuXHRcdFx0e1xuXHRcdFx0XHRsZXQgbG9nRW50cnkgPSBsb2dFbnRyaWVzLmdldFNGU09iamVjdChlKTtcblxuXHRcdFx0XHRjb25zdCBsb2dab25lID0gbG9nRW50cnkuZ2V0VXRmU3RyaW5nKCd6b25lJyk7XG5cdFx0XHRcdGNvbnN0IGxvZ1Jvb20gPSBsb2dFbnRyeS5nZXRVdGZTdHJpbmcoJ3Jvb20nKTtcblxuXHRcdFx0XHQvLyBDaGVjayBpZiB0aGUgem9uZSBpcyBjdXJyZW50bHkgbW9uaXRvcmVkXG5cdFx0XHRcdGlmICh0aGlzLl9tb25pdG9yZWRab25lID09IGxvZ1pvbmUpXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBjb25zb2xlLmxvZyhsb2dFbnRyeS5nZXRMb25nKCd0aW1lJykpXG5cdFx0XHRcdFx0Ly8gY29uc29sZS5sb2cobmV3IERhdGUobG9nRW50cnkuZ2V0TG9uZygndGltZScpKVxuXG5cdFx0XHRcdFx0bGV0IGVsZSA9IHtcblx0XHRcdFx0XHRcdHRpbWVzdGFtcDoga2VuZG8udG9TdHJpbmcobmV3IERhdGUobG9nRW50cnkuZ2V0TG9uZygndGltZScpKSwgJ2RkL01NL3l5eXkgSEg6bW06c3MnKSxcblx0XHRcdFx0XHRcdGxldmVsOiBsb2dFbnRyeS5nZXRVdGZTdHJpbmcoJ2xldmVsJyksXG5cdFx0XHRcdFx0XHRtZXNzYWdlOiBsb2dFbnRyeS5nZXRVdGZTdHJpbmcoJ21zZycpLFxuXHRcdFx0XHRcdH07XG5cblx0XHRcdFx0XHRpZiAodGhpcy5fbW9uaXRvcmVkVHlwZSA9PSB0aGlzLk1PTklUT1JFRF9UWVBFX1pPTkUgJiYgbG9nUm9vbSA9PSAnJylcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHQvLyBBZGQgdG8gem9uZSBleHRlbnNpb24gbG9nXG5cdFx0XHRcdFx0XHR0aGlzLl9hZGRMb2dFbnRyeVRvR3JpZCh0aGlzLl96b25lRXh0TG9nR3JpZCwgZWxlKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0ZWxzZVxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGlmICh0aGlzLl9tb25pdG9yZWRUeXBlID09IHRoaXMuTU9OSVRPUkVEX1RZUEVfUk9PTSAmJiB0aGlzLl9tb25pdG9yZWROYW1lID09IGxvZ1Jvb20pXG5cdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdC8vIEFkZCB0byByb29tIGV4dGVuc2lvbiBsb2dcblx0XHRcdFx0XHRcdFx0dGhpcy5fYWRkTG9nRW50cnlUb0dyaWQodGhpcy5fcm9vbUV4dExvZ0dyaWQsIGVsZSk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gVUkgRVZFTlQgTElTVEVORVJTXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0X29uVXBkYXRlSW50ZXJ2YWxDaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gUmVxdWVzdCBkYXRhIHRvIHNlcnZlclxuXHRcdHRoaXMuX3JlcXVlc3REYXRhKGZhbHNlKTtcblx0fVxuXG5cdF9vbkVudGl0aWVzUGFuZWxDaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gU2V0IG1haW4gY29udHJvbHMgc3RhdGVcblx0XHR0aGlzLl9zZXRNYWluQ29udHJvbHNFbmFibGVkKCk7XG5cblx0XHQvLyBSZXF1ZXN0IGRhdGEgdG8gc2VydmVyXG5cdFx0dGhpcy5fcmVxdWVzdERhdGEodHJ1ZSk7XG5cdH1cblxuXHQvKipcblx0ICogVXBkYXRlIHRoZSBTY29wZSBsYWJlbCBpbiB0aGUgUm9vbXMgcGFuZWwuXG5cdCAqL1xuXHRfb25ab25lU2VsZWN0ZWQoKVxuXHR7XG5cdFx0Ly8gQ2xlYXIgUm9vbSBhbmQgVXNlciBsaXN0c1xuXHRcdHRoaXMuX3Jlc2V0Um9vbUxpc3QoKTtcblx0XHR0aGlzLl9yZXNldFVzZXJMaXN0KCk7XG5cblx0XHQvLyBTZXQgc2NvcGUgbGFiZWxcblx0XHR0aGlzLl9zZXRTY29wZUxhYmVsKCk7XG5cblx0XHQvLyBFbmFibGUvZGlzYWJsZSBSb29tIGFuZCBVc2VyIHRvb2xzXG5cdFx0Y29uc3QgZGlzYWJsZWQgPSB0aGlzLl9zZWxlY3RlZFpvbmUgPT0gdW5kZWZpbmVkO1xuXG5cdFx0Ly8gKE5PVEU6IHRoaXMgZW5hYmxlcy9kaXNhYmxlcyB3aG9sZSBmaWVsZHNldDsgdGhpcyBkb2Vzbid0IGFmZmVjdCBLZW5kbyBEcm9wRG93biwgc28gd2UgaGF2ZSB0byBkbyBpdCBtYW51YWxseSlcblx0XHQkKCcjcm9vbVRvb2xzJykuYXR0cignZGlzYWJsZWQnLCBkaXNhYmxlZCk7XG5cdFx0JCgnI3VzZXJUb29scycpLmF0dHIoJ2Rpc2FibGVkJywgZGlzYWJsZWQpO1xuXG5cdFx0dGhpcy5fZ3JvdXBzRHJvcERvd24uZW5hYmxlKCFkaXNhYmxlZCk7XG5cblx0XHQvLyBTZXQgbWFpbiBjb250cm9scyBzdGF0ZVxuXHRcdHRoaXMuX3NldE1haW5Db250cm9sc0VuYWJsZWQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBVcGRhdGUgdGhlIFNjb3BlIGxhYmVsIGluIHRoZSBVc2VycyBwYW5lbC5cblx0ICovXG5cdF9vblJvb21TZWxlY3RlZCgpXG5cdHtcblx0XHQvLyBDbGVhciBVc2VyIGxpc3Rcblx0XHR0aGlzLl9yZXNldFVzZXJMaXN0KCk7XG5cblx0XHQvLyBTZXQgc2NvcGUgbGFiZWxcblx0XHR0aGlzLl9zZXRTY29wZUxhYmVsKCk7XG5cblx0XHQvLyBFbmFibGUgUmVtb3ZlIGJ1dHRvblxuXHRcdCQoJyN6bm0tcmVtb3ZlUm9vbUJ0JykuYXR0cignZGlzYWJsZWQnLCB0aGlzLl9zZWxlY3RlZFJvb20gPT0gdW5kZWZpbmVkKTtcblxuXHRcdC8vIFNldCBtYWluIGNvbnRyb2xzIHN0YXRlXG5cdFx0dGhpcy5fc2V0TWFpbkNvbnRyb2xzRW5hYmxlZCgpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFVwZGF0ZSB0aGUgc2NvcGUgbGFiZWwgaW4gdGhlIFVzZXJzIHBhbmVsLlxuXHQgKi9cblx0X29uR3JvdXBDaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gQ2xlYXIgUm9vbSBhbmQgVXNlciBsaXN0c1xuXHRcdHRoaXMuX3Jlc2V0Um9vbUxpc3QoKTtcblx0XHR0aGlzLl9yZXNldFVzZXJMaXN0KCk7XG5cblx0XHQvLyBTZXQgc2NvcGUgbGFiZWxcblx0XHR0aGlzLl9zZXRTY29wZUxhYmVsKCk7XG5cblx0XHQvLyBSZXF1ZXN0IGRhdGEgdG8gc2VydmVyXG5cdFx0dGhpcy5fcmVxdWVzdERhdGEodHJ1ZSk7XG5cblx0XHQvLyBTZXQgbWFpbiBjb250cm9scyBzdGF0ZVxuXHRcdHRoaXMuX3NldE1haW5Db250cm9sc0VuYWJsZWQoKTtcblx0fVxuXG5cdF9vblVzZXJTZWxlY3RlZCgpXG5cdHtcblx0XHQvLyBTZXQgbWFpbiBjb250cm9scyBzdGF0ZVxuXHRcdHRoaXMuX3NldE1haW5Db250cm9sc0VuYWJsZWQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBTaG93IHJvb20gY3JlYXRpb24gcGFuZWwuXG5cdCAqL1xuXHRfb25DcmVhdGVSb29tQnRDbGljaygpXG5cdHtcblx0XHQvLyBEaXNhYmxlIENyZWF0ZSBidXR0b25cblx0XHQkKCcjem5tLWNyZWF0ZVJvb21CdCcpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cblx0XHQvLyBSZXF1ZXN0IGRlZmF1bHQgcm9vbSBzZXR0aW5ncyB0byBleHRlbnNpb25cblx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0dFVF9ST09NX0NPTkZJRyk7XG5cdH1cblxuXHQvKipcblx0ICogUmVtb3ZlIGV4aXN0aW5nIHJvb20uXG5cdCAqL1xuXHRfb25SZW1vdmVSb29tQnRDbGljaygpXG5cdHtcblx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Q29uZmlybVdhcm5pbmcoJ0FyZSB5b3Ugc3VyZSB5b3Ugd2FudCB0byByZW1vdmUgdGhlIHNlbGVjdGVkIFJvb20/JywgJC5wcm94eSh0aGlzLl9vblJlbW92ZVJlbW92ZUNvbmZpcm0sIHRoaXMpKTtcblx0fVxuXG5cdF9vblJlbW92ZVJlbW92ZUNvbmZpcm0oKVxuXHR7XG5cdFx0Y29uc3Qgc2VsZWN0ZWRab25lID0gdGhpcy5fem9uZUxpc3RCb3guZGF0YUl0ZW0odGhpcy5fem9uZUxpc3RCb3guc2VsZWN0KCkpO1xuXHRcdGxldCBzZWxlY3RlZFJvb20gPSB0aGlzLl9yb29tTGlzdEJveC5kYXRhSXRlbSh0aGlzLl9yb29tTGlzdEJveC5zZWxlY3QoKSk7XG5cblx0XHRpZiAoc2VsZWN0ZWRSb29tKVxuXHRcdHtcblx0XHRcdC8vIFNlbmQgcmVxdWVzdCB0byBzZXJ2ZXJcblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgc2VsZWN0ZWRab25lLm5hbWUpO1xuXHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygncm9vbScsIHNlbGVjdGVkUm9vbS5uYW1lKTtcblxuXHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9SRU1PVkVfUk9PTSwgcGFyYW1zKTtcblxuXHRcdFx0Ly8gUmVxdWVzdCBkYXRhIHRvIHNlcnZlciB0byB1cGRhdGUgdGhlIHJvb21zIGxpc3Rcblx0XHRcdHRoaXMuX3JlcXVlc3REYXRhKHRydWUpO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBSZW1vdmUgY29udGVudCBvZiByb29tIGNyZWF0aW9uIHBhbmVsLlxuXHQgKi9cblx0X29uQ3JlYXRlUm9vbU1vZGFsSGlkZGVuKGUpXG5cdHtcblx0XHQvLyBJZiBhIG5lc3RlZCBtb2RhbCBpcyBvcGVuZWQsIGl0cyBjbG9zaW5nIGNhdXNlcyB0aGUgcGFyZW50IG1vZGFsIHRvIGJlIGNsb3NlZCB0b28gKEJvb3RzdHJhcCBkb2Vzbid0IHN1cHBvcnQgbmVzdGVkIG1vZGFscylcblx0XHQvLyBBcyBhIHdvcmthcm91bmQsIGhlcmUgd2UgcmVzZXQgdGhlIGNzcyBjbGFzcyAnbW9kYWwtb3Blbicgd2hlbiB0aGUgbmVzdGVkIG1vZGFsIGlzIGNsb3NlZCBhbmQgc2tpcCBldmVyeXRoaW5nIGVsc2Vcblx0XHQvLyBJbiBhZGRpdGlvbiB3ZSBoYWQgdG8gdXNlIGEgY3VzdG9tIGxpc3RlbmVyIG9uIHRoZSBjbG9zZS9jYW5jZWwgYnV0dG9ucyBvZiB0aGUgbmVzdGVkIG1vZGFsIChzZWUgY29uZmlnLWdyaWQuanMgZm9yIGV4YW1wbGUpXG5cdFx0aWYgKGUudGFyZ2V0ICE9PSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnem5tLWNyZWF0ZVJvb21Nb2RhbCcpKVxuXHRcdHtcblx0XHRcdCQoJ2JvZHknKS5hZGRDbGFzcygnbW9kYWwtb3BlbicpXG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRGVzdHJveSBzY3JvbGxpbmcgdGFicyBpbiByb29tIGNyZWF0aW9uIHBhbmVsXG5cdFx0JCgnI3pubS1yb29tQ3JlYXRvclRhYk5hdiAjdGFicycpLnNjcm9sbGluZ1RhYnMoJ2Rlc3Ryb3knKTtcblxuXHRcdC8vIFJlbW92ZSBhbGwgdGFiIG5hdmlnYXRvciBjb250ZW50XG5cdFx0dGhpcy5fcm9vbUNyZWF0aW9uSUJ1aWxkZXIuZGVzdHJveUludGVyZmFjZSgpO1xuXG5cdFx0Ly8gUmVtb3ZlIGxpc3RlbmVyIGZvciBjdXN0b20gYWN0aW9ucyB0cmlnZ2VyZWQgYnkgY29uZmlndXJhdGlvbiBpbnRlcmZhY2Vcblx0XHQkKCcjem5tLXJvb21DcmVhdG9yVGFiTmF2Jykub2ZmKCd2YWx1ZS1zZXQnKTtcblxuXHRcdC8vIFJlc2V0IGFuZCBoaWRlIGVycm9yIG1lc3NhZ2Vcblx0XHR0aGlzLl9yZXNldFJvb21DcmVhdGlvbkVycm9yKCk7XG5cdH1cblxuXHRfb25Db25maWdWYWx1ZVNldChlKSAvLyBTQU1FIE1FVEhPRCBEVVBMSUNBVEVEIElOIHpvbmUtY29uZmlndXJhdG9yLmpzXG5cdHtcblx0XHRjb25zdCBjb25maWdQYXJhbSA9IGUudGFyZ2V0LmRhdGE7XG5cblx0XHQvLyBIYW5kbGUgZXh0ZW5zaW9uIG5hbWUvdHlwZSBkcm9wZG93bnMgdXBkYXRlIGFuZCB1cGRhdGUgdGhlIG1haW4gY2xhc3MgZHJvcGRvd24gZGF0YXNvdXJjZSBhY2NvcmRpbmdseVxuXHRcdGlmIChjb25maWdQYXJhbS5uYW1lID09ICdleHRlbnNpb24ubmFtZScgfHwgY29uZmlnUGFyYW0ubmFtZSA9PSAnZXh0ZW5zaW9uLnR5cGUnIHx8IGNvbmZpZ1BhcmFtLm5hbWUgPT0gJ2V4dGVuc2lvbi5maWx0ZXJDbGFzcycpXG5cdFx0e1xuXHRcdFx0Ly8gQWxsIGludm9sdmVkIENvbmZpZ0Zvcm1JdGVtcyBtdXN0IGJlIGF2YWlsYWJsZSBhbmQgaW5pdGlhbGl6ZWQgdG8gcHJvY2VlZFxuXHRcdFx0Y29uc3QgbmFtZUZvcm1JdGVtID0gdGhpcy5fcm9vbUNyZWF0aW9uSUJ1aWxkZXIuZ2V0Q29uZmlnRm9ybUl0ZW0oJ2V4dGVuc2lvbi5uYW1lJyk7XG5cdFx0XHRjb25zdCB0eXBlRm9ybUl0ZW0gPSB0aGlzLl9yb29tQ3JlYXRpb25JQnVpbGRlci5nZXRDb25maWdGb3JtSXRlbSgnZXh0ZW5zaW9uLnR5cGUnKTtcblx0XHRcdGNvbnN0IGNsYXNzRm9ybUl0ZW0gPSB0aGlzLl9yb29tQ3JlYXRpb25JQnVpbGRlci5nZXRDb25maWdGb3JtSXRlbSgnZXh0ZW5zaW9uLmZpbGUnKTtcblx0XHRcdGNvbnN0IGZpbHRlckZvcm1JdGVtID0gdGhpcy5fcm9vbUNyZWF0aW9uSUJ1aWxkZXIuZ2V0Q29uZmlnRm9ybUl0ZW0oJ2V4dGVuc2lvbi5maWx0ZXJDbGFzcycpO1xuXG5cdFx0XHRpZiAobmFtZUZvcm1JdGVtICE9IG51bGwgJiYgdHlwZUZvcm1JdGVtICE9IG51bGwgJiYgY2xhc3NGb3JtSXRlbSAhPSBudWxsICYmIGZpbHRlckZvcm1JdGVtICE9IG51bGwpXG5cdFx0XHR7XG5cdFx0XHRcdGNvbnN0IHNvdXJjZSA9IG5hbWVGb3JtSXRlbS5kYXRhO1xuXHRcdFx0XHRsZXQgY2xhc3Nlc0xpc3QgPSBbXTtcblxuXHRcdFx0XHRsZXQgZGF0YSA9IHNvdXJjZS50cmlnZ2VyRGF0YTtcblx0XHRcdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhLnNpemUoKTsgaSsrKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0bGV0IGV4dCA9IGRhdGEuZ2V0U0ZTT2JqZWN0KGkpO1xuXG5cdFx0XHRcdFx0aWYgKGV4dC5nZXRVdGZTdHJpbmcoJ25hbWUnKSA9PSBuYW1lRm9ybUl0ZW0uZGF0YS52YWx1ZSAmJiBleHQuZ2V0VXRmU3RyaW5nKCd0eXBlJykgPT0gdHlwZUZvcm1JdGVtLmRhdGEudmFsdWUpXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0bGV0IGNsYXNzZXMgPSBleHQuZ2V0VXRmU3RyaW5nKCdjbGFzc2VzU3RyaW5nJykuc3BsaXQoJywnKTtcblxuXHRcdFx0XHRcdFx0aWYgKGZpbHRlckZvcm1JdGVtLmRhdGEudmFsdWUgPT0gdHJ1ZSlcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0bGV0IGZpbHRlcmVkQ2xhc3NlcyA9IGNsYXNzZXMuZmlsdGVyKGZpbHRlckNsYXNzTmFtZSk7XG5cdFx0XHRcdFx0XHRcdGNsYXNzZXMgPSBmaWx0ZXJlZENsYXNzZXM7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGNsYXNzZXNMaXN0ID0gY2xhc3Nlc0xpc3QuY29uY2F0KGNsYXNzZXMpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGxldCBjdXJyZW50Q2xhc3MgPSBjbGFzc0Zvcm1JdGVtLmRhdGEudmFsdWU7XG5cblx0XHRcdFx0Ly8gSWYgdGhlIGNsYXNzZXMgbGlzdCBkb2Vzbid0IGNvbnRhaW4gdGhlIGN1cnJlbnQgdmFsdWUsIGNyZWF0ZSBhbiBlbXB0eSBlbnRyeSBhbmQgcmVzZXQgdGhlIHZhbHVlXG5cdFx0XHRcdGlmIChjbGFzc2VzTGlzdC5pbmRleE9mKGN1cnJlbnRDbGFzcykgPCAwKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0aWYgKGNsYXNzZXNMaXN0Lmxlbmd0aCA9PSAwKVxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGNsYXNzZXNMaXN0LnB1c2goJycpO1xuXHRcdFx0XHRcdFx0Y3VycmVudENsYXNzID0gJyc7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2Vcblx0XHRcdFx0XHRcdGN1cnJlbnRDbGFzcyA9IGNsYXNzZXNMaXN0WzBdO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0bGV0IG1haW5DbGFzc0Ryb3BEb3duID0gY2xhc3NGb3JtSXRlbS5faW5uZXJXaWRnZXQ7XG5cdFx0XHRcdG1haW5DbGFzc0Ryb3BEb3duLnNldERhdGFTb3VyY2UoY2xhc3Nlc0xpc3QpO1xuXG5cdFx0XHRcdGNsYXNzRm9ybUl0ZW0uZGF0YS52YWx1ZSA9IGN1cnJlbnRDbGFzcztcblx0XHRcdFx0Y2xhc3NGb3JtSXRlbS5fc2V0V2lkZ2V0VmFsdWUoKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRfb25Sb29tQ3JlYXRvckNyZWF0ZUJ0Q2xpY2soKVxuXHR7XG5cdFx0Ly8gUmVzZXQgYW5kIGhpZGUgZXJyb3IgbWVzc2FnZVxuXHRcdHRoaXMuX3Jlc2V0Um9vbUNyZWF0aW9uRXJyb3IoKTtcblxuXHRcdC8vIENoZWNrIHZhbGlkaXR5XG5cdFx0aWYgKHRoaXMuX3Jvb21DcmVhdGlvbklCdWlsZGVyLmNoZWNrSXNWYWxpZCgpKVxuXHRcdHtcblx0XHRcdGxldCBjaGFuZ2VzID0gdGhpcy5fcm9vbUNyZWF0aW9uSUJ1aWxkZXIuZ2V0Q2hhbmdlZERhdGEoKTtcblxuXHRcdFx0aWYgKGNoYW5nZXMuc2l6ZSgpID4gMClcblx0XHRcdHtcblx0XHRcdFx0Ly8gQ2hlY2sgdGhlIHJvb20gbmFtZSBhZ2FpbnN0IHRoZSByb29tcyBsaXN0IChkdXBsaWNhdGUgbmFtZXMgbm90IGFsbG93ZWQhKVxuXHRcdFx0XHRpZiAodHJ1ZSkvL3RoaXMuX3ZhbGlkYXRlTmV3Um9vbU5hbWUoY2hhbmdlcykpXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvLyBEaXNhYmxlIHJvb20gY3JlYXRpb24gcGFuZWxcblx0XHRcdFx0XHR0aGlzLl9lbmFibGVDcmVhdGVSb29tUGFuZWwoZmFsc2UpO1xuXG5cdFx0XHRcdFx0Ly8gU2VuZCBzZXR0aW5ncyB0byBzZXJ2ZXIgaW5zdGFuY2Vcblx0XHRcdFx0XHRsZXQgcGFyYW1zID0gbmV3IFNGUzJYLlNGU09iamVjdCgpO1xuXHRcdFx0XHRcdHBhcmFtcy5wdXRTRlNBcnJheSgnc2V0dGluZ3MnLCBjaGFuZ2VzKTtcblx0XHRcdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6TmFtZScsIHRoaXMuX3pvbmVMaXN0Qm94LmRhdGFJdGVtKHRoaXMuX3pvbmVMaXN0Qm94LnNlbGVjdCgpKS5uYW1lKTtcblx0XHRcdFx0XHRwYXJhbXMucHV0Qm9vbCgnbm90aWZ5JywgJCgnI3pubS1ub3RpZnlDbGllbnRzQ0InKS5wcm9wKCdjaGVja2VkJykpO1xuXG5cdFx0XHRcdFx0Ly8gU2VuZCBzZXR0aW5ncyB0byBleHRlbnNpb25cblx0XHRcdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0NSRUFURV9ST09NLCBwYXJhbXMpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2Vcblx0XHRcdFx0e1xuXHRcdFx0XHRcdC8vIFNob3cgYWxlcnRcblx0XHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoJ1VuYWJsZSB0byBzdWJtaXQgY29uZmlndXJhdGlvbiBiZWNhdXNlIHRoZSBSb29tIG5hbWUgYWxyZWFkeSBleGlzdHM7IGR1cGxpY2F0ZSBuYW1lcyBhcmUgbm90IGFsbG93ZWQuJywgdHJ1ZSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0ZWxzZVxuXHRcdHtcblx0XHRcdC8vIFNob3cgYWxlcnRcblx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydCgnVW5hYmxlIHRvIHN1Ym1pdCBSb29tIGNvbmZpZ3VyYXRpb24gZHVlIHRvIGFuIGludmFsaWQgdmFsdWU7IHBsZWFzZSB2ZXJpZnkgdGhlIGhpZ2hsaWdodGVkIGZvcm0gZmllbGRzIGluIGFsbCB0YWJzLicsIHRydWUpO1xuXHRcdH1cblx0fVxuXG5cdF9vblNob3dSb29tRmlsdGVyQnRDbGljaygpXG5cdHtcblx0XHR0aGlzLl9yb29tRmlsdGVyLnNob3coKTtcblx0fVxuXG5cdF9vblNob3dVc2VyRmlsdGVyQnRDbGljaygpXG5cdHtcblx0XHR0aGlzLl91c2VyRmlsdGVyLnNob3coKTtcblx0fVxuXG5cdF9vbkFwcGx5QnRDaGFuZ2UoKVxuXHR7XG5cdFx0Ly8gUmVxdWVzdCBkYXRhIHRvIHNlcnZlclxuXHRcdHRoaXMuX3JlcXVlc3REYXRhKHRydWUpO1xuXHR9XG5cblx0X29uUm9vbUZpbHRlclVwZGF0ZWQoKVxuXHR7XG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgXCJBcHBseVwiIGNoZWNrYm94XG5cdFx0JCgnI3pubS1hcHBseVJvb21GaWx0ZXJDQicpLmF0dHIoJ2Rpc2FibGVkJywgdGhpcy5fcm9vbUZpbHRlci5maWx0ZXJFeHByZXNzaW9uID09IG51bGwpO1xuXG5cdFx0Ly8gUmVxdWVzdCBkYXRhIHRvIHNlcnZlclxuXHRcdHRoaXMuX3JlcXVlc3REYXRhKHRydWUpO1xuXHR9XG5cblx0X29uVXNlckZpbHRlclVwZGF0ZWQoKVxuXHR7XG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgXCJBcHBseVwiIGNoZWNrYm94XG5cdFx0JCgnI3pubS1hcHBseVVzZXJGaWx0ZXJDQicpLmF0dHIoJ2Rpc2FibGVkJywgdGhpcy5fdXNlckZpbHRlci5maWx0ZXJFeHByZXNzaW9uID09IG51bGwpO1xuXG5cdFx0Ly8gUmVxdWVzdCBkYXRhIHRvIHNlcnZlclxuXHRcdHRoaXMuX3JlcXVlc3REYXRhKHRydWUpO1xuXHR9XG5cblx0X29uTW9uaXRvclNlbGVjdGlvbkJ0Q2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX3NlbGVjdGVkWm9uZSlcblx0XHR7XG5cdFx0XHQvLyBDYW5jZWwgcHJldmlvdXMgbW9uaXRvcmluZ1xuXHRcdFx0dGhpcy5fb25DbG9zZU1vbml0b3JCdENsaWNrKCk7XG5cblx0XHRcdGxldCBnZXRab25lVXNlckNvdW50SGlzdG9yeSA9IGZhbHNlO1xuXG5cdFx0XHR0aGlzLl9tb25pdG9yZWRab25lID0gdGhpcy5fc2VsZWN0ZWRab25lLm5hbWU7XG5cblx0XHRcdC8vIFpvbmUgbXVzdCBiZSBtb25pdG9yZWRcblx0XHRcdGlmICh0aGlzLl9hY3RpdmVQYW5lbFR5cGUgPT0gJ3pvbmUnKVxuXHRcdFx0e1xuXHRcdFx0XHR0aGlzLl9tb25pdG9yZWRUeXBlID0gdGhpcy5NT05JVE9SRURfVFlQRV9aT05FO1xuXHRcdFx0XHR0aGlzLl9tb25pdG9yZWROYW1lID0gdGhpcy5fbW9uaXRvcmVkWm9uZTtcblxuXHRcdFx0XHRnZXRab25lVXNlckNvdW50SGlzdG9yeSA9IHRydWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFJvb20gbXVzdCBiZSBtb25pdG9yZWRcblx0XHRcdGlmICh0aGlzLl9hY3RpdmVQYW5lbFR5cGUgPT0gJ3Jvb20nICYmIHRoaXMuX3NlbGVjdGVkUm9vbSAhPSB1bmRlZmluZWQpXG5cdFx0XHR7XG5cdFx0XHRcdHRoaXMuX21vbml0b3JlZFR5cGUgPSB0aGlzLk1PTklUT1JFRF9UWVBFX1JPT007XG5cdFx0XHRcdHRoaXMuX21vbml0b3JlZE5hbWUgPSB0aGlzLl9zZWxlY3RlZFJvb20ubmFtZTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gVXNlciBtdXN0IGJlIG1vbml0b3JlZFxuXHRcdFx0aWYgKHRoaXMuX2FjdGl2ZVBhbmVsVHlwZSA9PSAndXNlcicgJiYgdGhpcy5fc2VsZWN0ZWRVc2VyICE9IHVuZGVmaW5lZClcblx0XHRcdHtcblx0XHRcdFx0dGhpcy5fbW9uaXRvcmVkVHlwZSA9IHRoaXMuTU9OSVRPUkVEX1RZUEVfVVNFUjtcblx0XHRcdFx0dGhpcy5fbW9uaXRvcmVkTmFtZSA9IHRoaXMuX3NlbGVjdGVkVXNlci5uYW1lO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTaG93IGxvYWRpbmcgYmFyXG5cdFx0XHR0aGlzLl9zd2l0Y2hWaWV3KCd6bm0tbG9hZGluZycpO1xuXG5cdFx0XHQvLyBSZXF1ZXN0IGRhdGEgdG8gc2VydmVyXG5cdFx0XHR0aGlzLl9yZXF1ZXN0RGF0YSh0cnVlLCBnZXRab25lVXNlckNvdW50SGlzdG9yeSk7XG5cdFx0fVxuXHR9XG5cblx0X29uU2VuZEFkbWluTXNnSW5LZXlVcChldmVudClcblx0e1xuXHRcdGlmIChldmVudC5rZXkgIT09ICdFbnRlcicpXG5cdFx0XHRyZXR1cm47XG5cblx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdHRoaXMuX29uU2VuZEFkbWluTXNnQnRDbGljaygpO1xuXHR9XG5cblx0X29uU2VuZEFkbWluTXNnQnRDbGljaygpXG5cdHtcblx0XHRpZiAodGhpcy5fc2VsZWN0ZWRab25lICYmICQoJyN6bm0tbWVzc2FnZUluJykudmFsKCkgIT0gJycpXG5cdFx0e1xuXHRcdFx0Ly8gQnVpbGQgcmVxdWVzdCBwYXJhbWV0ZXJzXG5cdFx0XHRsZXQgcGFyYW1zID0gbmV3IFNGUzJYLlNGU09iamVjdCgpO1xuXG5cdFx0XHQvLyBBbHdheXMgaW5jbHVkZSB6b25lXG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fc2VsZWN0ZWRab25lLm5hbWUpO1xuXG5cdFx0XHQvLyBTZW5kIG1lc3NhZ2UgdG8gcm9vbT9cblx0XHRcdGlmICh0aGlzLl9hY3RpdmVQYW5lbFR5cGUgPT0gJ3Jvb20nICYmIHRoaXMuX3NlbGVjdGVkUm9vbSAhPSB1bmRlZmluZWQpXG5cdFx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ3Jvb20nLCB0aGlzLl9zZWxlY3RlZFJvb20ubmFtZSk7XG5cblx0XHRcdC8vIFNlbmQgbWVzc2FnZSB0byB1c2VyP1xuXHRcdFx0aWYgKHRoaXMuX2FjdGl2ZVBhbmVsVHlwZSA9PSAndXNlcicgJiYgdGhpcy5fc2VsZWN0ZWRVc2VyICE9IHVuZGVmaW5lZClcblx0XHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygndXNlcicsIHRoaXMuX3NlbGVjdGVkVXNlci5uYW1lKTtcblxuXHRcdFx0Ly8gQWRkIG1lc3NhZ2Vcblx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ21zZycsICQoJyN6bm0tbWVzc2FnZUluJykudmFsKCkpO1xuXG5cdFx0XHQvLyBTZW5kIHJlcXVlc3QgdG8gZXh0ZW5zaW9uXG5cdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0FETUlOX01TRywgcGFyYW1zKTtcblxuXHRcdFx0Ly8gQ2xlYXIgdGV4dCBpbnB1dFxuXHRcdFx0JCgnI3pubS1tZXNzYWdlSW4nKS52YWwoJycpO1xuXHRcdH1cblx0fVxuXG5cdF9vbkNsb3NlTW9uaXRvckJ0Q2xpY2soKVxuXHR7XG5cdFx0Ly8gU2F2ZSByZWYuIGZvciBsYXRlciB1c2FnZVxuXHRcdGNvbnN0IG1vbml0b3JlZFR5cGUgPSB0aGlzLl9tb25pdG9yZWRUeXBlO1xuXG5cdFx0dGhpcy5fbW9uaXRvcmVkWm9uZSA9IG51bGw7XG5cdFx0dGhpcy5fbW9uaXRvcmVkVHlwZSA9IG51bGw7XG5cdFx0dGhpcy5fbW9uaXRvcmVkTmFtZSA9IG51bGw7XG5cblx0XHQvLyBDbGVhciBnZW9sb2NhdGlvbiBVSVxuXHRcdHRoaXMuX2NsZWFyR2VvTG9jYXRpb25VSSgpO1xuXG5cdFx0Ly8gQ2xlYXIgbG9nIGRhdGFncmlkcyBkYXRhcHJvdmlkZXJzXG5cdFx0Ly8gTk9URTogYSB0cnktY2F0Y2ggaXMgdXNlZCBoZXJlIGJlY2F1c2UsIGZvciBhbiB1bmtub3duIHJlYXNvbiwgS2VuZG8gaXMgdGhyb3dpbmcgYW4gZXJyb3Igd2hlbiByZXNldHRpbmcgdGhlIGRhdGEgc291cmNlcyB3aGVuIHRoZSBtb2R1bGUgaXMgZGVzdHJveWVkXG5cdFx0dHJ5XG5cdFx0e1xuXHRcdFx0dGhpcy5fem9uZUV4dExvZ0dyaWQuc2V0RGF0YVNvdXJjZShbXSk7XG5cdFx0XHR0aGlzLl9yb29tRXh0TG9nR3JpZC5zZXREYXRhU291cmNlKFtdKTtcblx0XHR9XG5cdFx0Y2F0Y2ggKGUpIHsgLyogSWdub3JlICovIH1cblxuXHRcdC8vIExlYXZlIGVkaXQgbW9kZVxuXHRcdHRoaXMuX29uQ2FuY2VsQnRDbGljaygpO1xuXG5cdFx0Ly8gUmVhcnJhbmdlIG1vbml0b3JpbmcgaW50ZXJmYWNlXG5cdFx0dGhpcy5fc2V0TW9uaXRvcmluZ0ludGVyZmFjZSgpO1xuXG5cdFx0Ly8gQ2xlYXIgdGFiIG5hdmlnYXRvclxuXHRcdHRoaXMuX2NsZWFyVGFicyhtb25pdG9yZWRUeXBlKTtcblx0fVxuXG5cdF9vbkVkaXRCdENsaWNrKClcblx0e1xuXHRcdGlmICghdGhpcy5faXNFZGl0aW5nKVxuXHRcdHtcblx0XHRcdC8vIFNob3cgZWRpdCBjb250cm9sc1xuXHRcdFx0JCgnI3pubS1lZGl0Q29udHJvbHMnKS5zaG93KCk7XG5cblx0XHRcdC8vIEhpZGUgZWRpdCBidXR0b25cblx0XHRcdCQoJyN6bm0tZWRpdEJ0JykuaGlkZSgpO1xuXG5cdFx0XHQvLyBFbmFibGUgZWRpdGFibGUgZmllbGRzXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmRpc2FibGVJbnRlcmZhY2UoZmFsc2UpO1xuXG5cdFx0XHR0aGlzLl9pc0VkaXRNb2RlID0gdHJ1ZTtcblx0XHR9XG5cdH1cblxuXHRfb25DYW5jZWxCdENsaWNrKClcblx0e1xuXHRcdGlmICh0aGlzLl9pc0VkaXRpbmcpXG5cdFx0e1xuXHRcdFx0Ly8gSGlkZSBlZGl0IGNvbnRyb2xzXG5cdFx0XHQkKCcjem5tLWVkaXRDb250cm9scycpLmhpZGUoKTtcblxuXHRcdFx0Ly8gU2hvdyBlZGl0IGJ1dHRvblxuXHRcdFx0JCgnI3pubS1lZGl0QnQnKS5zaG93KCk7XG5cblx0XHRcdC8vIERpc2FibGUgZWRpdGFibGUgZmllbGRzXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmRpc2FibGVJbnRlcmZhY2UodHJ1ZSk7XG5cblx0XHRcdHRoaXMuX2lzRWRpdE1vZGUgPSBmYWxzZTtcblxuXHRcdFx0Ly8gSGlkZSB2YWxpZGF0aW9uIG1lc3NhZ2VzXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLnJlc2V0VmFsaWRhdGlvbigpO1xuXG5cdFx0XHQvLyBSZXF1ZXN0IGRhdGEgdG8gdXBkYXRlIGludGVyZmFjZSAodXBkYXRpbmcgc3VzcGVuZGVkIGluIGVkaXQgbW9kZSlcblx0XHRcdHRoaXMuX3JlcXVlc3REYXRhKHRydWUpO1xuXHRcdH1cblx0fVxuXG5cdF9vblN1Ym1pdEJ0Q2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX2lzRWRpdGluZylcblx0XHR7XG5cdFx0XHQvLyBDaGVjayB2YWxpZGl0eVxuXHRcdFx0aWYgKHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuY2hlY2tJc1ZhbGlkKCkpXG5cdFx0XHR7XG5cdFx0XHRcdGxldCBjaGFuZ2VzID0gdGhpcy5faW50ZXJmYWNlQnVpbGRlci5nZXRDaGFuZ2VkRGF0YSgpO1xuXG5cdFx0XHRcdGlmIChjaGFuZ2VzLnNpemUoKSA+IDApXG5cdFx0XHRcdHtcblx0XHRcdFx0XHQvL2NvbnNvbGUubG9nKGNoYW5nZXMuZ2V0RHVtcCgpKVxuXG5cdFx0XHRcdFx0Ly8gU2VuZCBzZXR0aW5ncyB0byBzZXJ2ZXIgaW5zdGFuY2Vcblx0XHRcdFx0XHRsZXQgcGFyYW1zID0gbmV3IFNGUzJYLlNGU09iamVjdCgpO1xuXHRcdFx0XHRcdHBhcmFtcy5wdXRTRlNBcnJheSgnc2V0dGluZ3MnLCBjaGFuZ2VzKTtcblx0XHRcdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fbW9uaXRvcmVkWm9uZSk7XG5cblx0XHRcdFx0XHRpZiAodGhpcy5fbW9uaXRvcmVkVHlwZSA9PSB0aGlzLk1PTklUT1JFRF9UWVBFX1JPT00pXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0Ly8gU3VibWl0IHJvb20gc2V0dGluZ3Ncblx0XHRcdFx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ3Jvb20nLCB0aGlzLl9tb25pdG9yZWROYW1lKTtcblx0XHRcdFx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfU0VUX1JPT01fU0VUVElOR1MsIHBhcmFtcyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2UgaWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9VU0VSKVxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdC8vIFN1Ym1pdCB1c2VyIHNldHRpbmdzXG5cdFx0XHRcdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd1c2VyJywgdGhpcy5fbW9uaXRvcmVkTmFtZSk7XG5cdFx0XHRcdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX1NFVF9VU0VSX1NFVFRJTkdTLCBwYXJhbXMpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0Ly8gU3VibWl0IHpvbmUgc2V0dGluZ3Ncblx0XHRcdFx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfU0VUX1pPTkVfU0VUVElOR1MsIHBhcmFtcyk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gTGVhdmUgZWRpdCBtb2RlXG5cdFx0XHRcdFx0dGhpcy5fb25DYW5jZWxCdENsaWNrKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGVsc2Vcblx0XHRcdHtcblx0XHRcdFx0Ly8gU2hvdyBhbGVydFxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93U2ltcGxlQWxlcnQoJ1VuYWJsZSB0byBzdWJtaXQgY2hhbmdlcyBkdWUgdG8gYW4gaW52YWxpZCB2YWx1ZTsgcGxlYXNlIHZlcmlmeSB0aGUgaGlnaGxpZ2h0ZWQgZm9ybSBmaWVsZHMgaW4gYWxsIHRhYnMuJywgdHJ1ZSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0X29uVGltZVJhbmdlQ2hhbmdlKClcblx0e1xuXHRcdGxldCB2YWx1ZXMgPSB0aGlzLl90aW1lU2xpZGVyLnZhbHVlKCk7XG5cblx0XHRpZiAodmFsdWVzWzBdID09IHZhbHVlc1sxXSlcblx0XHR7XG5cdFx0XHRpZiAodmFsdWVzWzFdID09IC0yNClcblx0XHRcdFx0dmFsdWVzWzFdID0gLTIzO1xuXG5cdFx0XHR2YWx1ZXNbMF0gPSB2YWx1ZXNbMV0gLSAxO1xuXG5cdFx0XHQvLyBSZXNldCB0aGUgdGltZSByYW5nZSBzbGlkZXIgdmFsdWUgKHdlIG5lZWQgdG8gdXNlIHNldFRpbWVvdXRcblx0XHRcdC8vIGJhY2F1c2UgZG9pbmcgaXQgaW4gdGhlIGNoYW5nZSBldmVudCBsaXN0ZW5lciBkb2Vzbid0IHJlZHJhdyB0aGUgc2xpZGVyKVxuXHRcdFx0c2V0VGltZW91dCgkLnByb3h5KCBmdW5jdGlvbih2YWx1ZXMpIHsgdGhpcy5fdGltZVNsaWRlci52YWx1ZSh2YWx1ZXMpOyB9LCB0aGlzKSwgMTAsIHZhbHVlcyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fdXNlckNvdW50Q2hhcnQucmFuZ2UgPSB2YWx1ZXM7XG5cdH1cblxuXHRfb25XaW5kb3dSZXNpemUoKVxuXHR7XG5cdFx0Ly8gUmVkcmF3IGNoYXJ0c1xuXHRcdHRoaXMuX3VzZXJDb3VudENoYXJ0LnJlZHJhdygpO1xuXHRcdHRoaXMuX3BhY2tldFF1ZXVlQ2hhcnQucmVkcmF3KCk7XG5cdFx0dGhpcy5fZHJvcHBlZE1zZ0NoYXJ0LnJlZHJhdygpO1xuXHRcdHRoaXMuX3dyaXR0ZW5EYXRhQ2hhcnQucmVkcmF3KCk7XG5cdFx0dGhpcy5fcmVhZERhdGFDaGFydC5yZWRyYXcoKTtcblxuXHRcdC8vIFJlZHJhdyB0aW1lIHJhbmdlIHNsaWRlclxuXHRcdHRoaXMuX3RpbWVTbGlkZXIucmVzaXplKCk7XG5cdH1cblxuXHRfb25Xb3Jkc1JlbG9hZEJ0Q2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9aT05FKVxuXHRcdHtcblx0XHRcdC8vIFNlbmQgcmVxdWVzdCB0byBleHRlbnNpb25cblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fbW9uaXRvcmVkTmFtZSk7XG5cblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfUkVMT0FEX1dPUkRTLCBwYXJhbXMpO1xuXHRcdH1cblx0fVxuXG5cdF9vblpvbmVFeHRSZWxvYWRCdENsaWNrKClcblx0e1xuXHRcdGlmICh0aGlzLl9tb25pdG9yZWRUeXBlID09IHRoaXMuTU9OSVRPUkVEX1RZUEVfWk9ORSlcblx0XHR7XG5cdFx0XHQvLyBTZW5kIHJlcXVlc3QgdG8gZXh0ZW5zaW9uXG5cdFx0XHRsZXQgcGFyYW1zID0gbmV3IFNGUzJYLlNGU09iamVjdCgpO1xuXHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygnem9uZScsIHRoaXMuX21vbml0b3JlZE5hbWUpO1xuXG5cdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX1JFTE9BRF9aT05FX0VYVCwgcGFyYW1zKTtcblx0XHR9XG5cdH1cblxuXHRfb25EaXNjb25uZWN0QnRDbGljaygpXG5cdHtcblx0XHRpZiAodGhpcy5fbW9uaXRvcmVkVHlwZSA9PSB0aGlzLk1PTklUT1JFRF9UWVBFX1VTRVIpXG5cdFx0e1xuXHRcdFx0Ly8gU2VuZCByZXF1ZXN0IHRvIGV4dGVuc2lvblxuXHRcdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ3pvbmUnLCB0aGlzLl9tb25pdG9yZWRab25lKTtcblx0XHRcdHBhcmFtcy5wdXRVdGZTdHJpbmcoJ3VzZXInLCB0aGlzLl9tb25pdG9yZWROYW1lKTtcblxuXHRcdFx0dGhpcy5zZW5kRXh0ZW5zaW9uUmVxdWVzdCh0aGlzLlJFUV9ESVNDT05ORUNUX1VTRVIsIHBhcmFtcyk7XG5cdFx0fVxuXHR9XG5cblx0X29uS2lja0J0Q2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9VU0VSKVxuXHRcdHtcblx0XHRcdC8vIFNlbmQgcmVxdWVzdCB0byBleHRlbnNpb25cblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fbW9uaXRvcmVkWm9uZSk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd1c2VyJywgdGhpcy5fbW9uaXRvcmVkTmFtZSk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCdtc2cnLCAkKCcjem5tLWtpY2tNc2dJbicpLnZhbCgpKTtcblx0XHRcdHBhcmFtcy5wdXRJbnQoJ2RlbGF5JywgKHRoaXMuX2tpY2tEZWxheUluLnZhbHVlKCkgIT0gbnVsbCA/IE51bWJlcih0aGlzLl9raWNrRGVsYXlJbi52YWx1ZSgpKSA6IHRoaXMuS0lDS19CQU5fREVGQVVMVF9ERUxBWSkpO1xuXG5cdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0tJQ0tfVVNFUiwgcGFyYW1zKTtcblxuXHRcdFx0Ly8gUmVzZXQgbWVzc2FnZVxuXHRcdFx0JCgnI3pubS1raWNrTXNnSW4nKS52YWwoJycpO1xuXHRcdH1cblx0fVxuXG5cdF9vbkJhbkJ0Q2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9VU0VSKVxuXHRcdHtcblx0XHRcdC8vIFNlbmQgcmVxdWVzdCB0byBleHRlbnNpb25cblx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fbW9uaXRvcmVkWm9uZSk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd1c2VyJywgdGhpcy5fbW9uaXRvcmVkTmFtZSk7XG5cdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCdtc2cnLCAkKCcjem5tLWJhbk1zZ0luJykudmFsKCkpO1xuXHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygnbW9kZScsIHRoaXMuX2Jhbk1vZGVEZC5zZWxlY3QoKSA+IDAgPyB0aGlzLl9iYW5Nb2RlRGQudmFsdWUoKSA6ICdOQU1FJyk7XG5cdFx0XHRwYXJhbXMucHV0SW50KCdkZWxheScsICh0aGlzLl9iYW5EZWxheUluLnZhbHVlKCkgIT0gbnVsbCA/IE51bWJlcih0aGlzLl9iYW5EZWxheUluLnZhbHVlKCkpIDogdGhpcy5LSUNLX0JBTl9ERUZBVUxUX0RFTEFZKSk7XG5cblx0XHRcdGxldCBkdXJhdGlvbiA9IHRoaXMuX2JhbkR1cmF0aW9uSW4udmFsdWUoKSAhPSBudWxsID8gTnVtYmVyKHRoaXMuX2JhbkR1cmF0aW9uSW4udmFsdWUoKSkgOiAxO1xuXG5cdFx0XHRpZiAodGhpcy5fYmFuRHVyVW5pdERkLnZhbHVlKCkgPT0gJ2hvdXJzJylcblx0XHRcdFx0ZHVyYXRpb24gPSBkdXJhdGlvbiAqIDYwO1xuXHRcdFx0ZWxzZSBpZiAodGhpcy5fYmFuRHVyVW5pdERkLnZhbHVlKCkgPT0gJ2RheXMnKVxuXHRcdFx0XHRkdXJhdGlvbiA9IGR1cmF0aW9uICogNjAgKiAyNDtcblxuXHRcdFx0cGFyYW1zLnB1dEludCgnZHVyYXRpb24nLCBkdXJhdGlvbik7XG5cblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfQkFOX1VTRVIsIHBhcmFtcyk7XG5cblx0XHRcdC8vIFJlc2V0IG1lc3NhZ2Vcblx0XHRcdCQoJyN6bm0tYmFuTXNnSW4nKS52YWwoJycpO1xuXHRcdH1cblx0fVxuXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIFBSSVZBVEUgTUVUSE9EU1xuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdF9zZXRNb25pdG9yaW5nSW50ZXJmYWNlKClcblx0e1xuXHRcdGNvbnN0IGVuYWJsZWQgPSAodGhpcy5fbW9uaXRvcmVkVHlwZSAhPSBudWxsICYmIHRoaXMuX21vbml0b3JlZE5hbWUgIT0gbnVsbCk7XG5cblx0XHQvLyBTaG93L2hpZGUgaGVhZGVyIGFuZCBmb290ZXJcblx0XHRpZiAoZW5hYmxlZClcblx0XHR7XG5cdFx0XHR0aGlzLl9zd2l0Y2hWaWV3KGB6bm0tJHt0aGlzLl9tb25pdG9yZWRUeXBlfU1vbml0b3JgKTtcblxuXHRcdFx0Ly8gSGVhZGVyIHRpdGxlXG5cdFx0XHRsZXQgdGl0bGUgPSAnTm93IG1vbml0b3JpbmcgJztcblxuXHRcdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgPT0gdGhpcy5NT05JVE9SRURfVFlQRV9VU0VSKVxuXHRcdFx0XHR0aXRsZSArPSBgVXNlcjogPHN0cm9uZz4ke3RoaXMuX21vbml0b3JlZE5hbWV9PC9zdHJvbmc+IChab25lOiAke3RoaXMuX21vbml0b3JlZFpvbmV9KWA7XG5cdFx0XHRlbHNlIGlmICh0aGlzLl9tb25pdG9yZWRUeXBlID09IHRoaXMuTU9OSVRPUkVEX1RZUEVfUk9PTSlcblx0XHRcdFx0dGl0bGUgKz0gYFJvb206IDxzdHJvbmc+JHt0aGlzLl9tb25pdG9yZWROYW1lfTwvc3Ryb25nPiAoWm9uZTogJHt0aGlzLl9tb25pdG9yZWRab25lfSlgO1xuXHRcdFx0ZWxzZVxuXHRcdFx0XHR0aXRsZSArPSBgWm9uZTogPHN0cm9uZz4ke3RoaXMuX21vbml0b3JlZFpvbmV9PC9zdHJvbmc+YDtcblxuXHRcdFx0JCgnI3pubS1tb25pdG9yZWRIZWFkZXInKS5odG1sKHRpdGxlKTtcblx0XHRcdCQoJyN6bm0tbW9uaXRvcmVkRm9vdGVyJykuc2hvdygpO1xuXG5cdFx0XHQvLyBTaG93IHBhbmVsXG5cdFx0XHR0aGlzLl9zd2l0Y2hQYW5lbCgnem5tLW1haW5QYW5lbCcpO1xuXHRcdH1cblx0XHRlbHNlXG5cdFx0e1xuXHRcdFx0dGhpcy5fc3dpdGNoVmlldygnem5tLXNlbGVjdCcpO1xuXG5cdFx0XHQkKCcjem5tLW1vbml0b3JlZEhlYWRlcicpLmh0bWwoJycpO1xuXHRcdFx0JCgnI3pubS1tb25pdG9yZWRGb290ZXInKS5oaWRlKCk7XG5cblx0XHRcdC8vIFNob3cgcGFuZWxcblx0XHRcdHRoaXMuX3N3aXRjaFBhbmVsKCd6bm0tc2lkZWJhclBhbmVsJyk7XG5cdFx0fVxuXHR9XG5cblx0X3N3aXRjaFZpZXcodmlld0lkKVxuXHR7XG5cdFx0ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3pubS12aWV3c3RhY2snKS5zZWxlY3RlZEVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWV3SWQpO1xuXHR9XG5cblx0X3N3aXRjaFBhbmVsKHBhbmVsSWQpXG5cdHtcblx0XHRkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnem5tLXZpZXcnKS5zZWxlY3RlZFBhbmVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQocGFuZWxJZCk7XG5cdH1cblxuXHQvKipcblx0ICogQnVpbGQgdGhlIHBvbGxpbmcgcmVxdWVzdCB0byBiZSBzZW50IHRvIHRoZSBzZXJ2ZXIsIGJhc2VkIG9uIHRoZSBzZWxlY3Rpb25zIGFuZCBvdGhlciBzZXR0aW5ncyBpbiB0aGUgYWNjb3JkaW9uLlxuXHQgKi9cblx0X3JlcXVlc3REYXRhKG5ld0lkID0gZmFsc2UsIGdldFpvbmVVc2VyQ291bnRIaXN0b3J5ID0gZmFsc2UpXG5cdHtcblx0XHQvLyBDbGVhciBwcmV2aW91cyByZXF1ZXN0IHNjaGVkdWxpbmdcblx0XHRjbGVhclRpbWVvdXQodGhpcy5fcmVxdWVzdFRpbWVyKTtcblxuXHRcdC8vIENoZWNrIGlmIGNvbm5lY3Rpb24gaXMgc3RpbGwgYXZhaWxhYmxlXG5cdFx0aWYgKHRoaXMuc21hcnRGb3guaXNDb25uZWN0ZWQpXG5cdFx0e1xuXHRcdFx0aWYgKG5ld0lkKVxuXHRcdFx0XHR0aGlzLl9jdXJyZW50UmVxdWVzdElkKys7XG5cblx0XHRcdC8vIEJ1aWxkIHJlcXVlc3QgcGFyYW1ldGVyc1xuXHRcdFx0bGV0IHBhcmFtcyA9IG5ldyBTRlMyWC5TRlNPYmplY3QoKTtcblxuXHRcdFx0Ly8gVGhlIHVwZGF0ZWQgem9uZXMvcm9vbXMvdXNlcnMgbGlzdHMgYXJlIHJlcXVlc3RlZCBpZiB0aGUgY29ycmVzcG9uZGluZyBwYW5lbCBpcyBkaXNwbGF5ZWQgb25seVxuXHRcdFx0Y29uc3QgZ2V0Wm9uZXMgPSAodGhpcy5fYWN0aXZlUGFuZWxUeXBlID09ICd6b25lJyk7XG5cdFx0XHRjb25zdCBnZXRSb29tcyA9ICh0aGlzLl9hY3RpdmVQYW5lbFR5cGUgPT0gJ3Jvb20nICYmIHRoaXMuX3NlbGVjdGVkWm9uZSAhPSB1bmRlZmluZWQpO1xuXHRcdFx0Y29uc3QgZ2V0VXNlcnMgPSAodGhpcy5fYWN0aXZlUGFuZWxUeXBlID09ICd1c2VyJyAmJiB0aGlzLl9zZWxlY3RlZFpvbmUgIT0gdW5kZWZpbmVkKTtcblxuXHRcdFx0Ly8gLS0tIFpPTkVTIC0tLVxuXHRcdFx0cGFyYW1zLnB1dEJvb2woJ3pvbmVzJywgZ2V0Wm9uZXMpO1xuXG5cdFx0XHQvLyBST09NUyAmIFVTRVJTXG5cdFx0XHRpZiAoZ2V0Um9vbXMgfHwgZ2V0VXNlcnMpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIENvbW1vbiBmaWx0ZXJpbmcgcGFyYW1zOlxuXG5cdFx0XHRcdC8vIGEpIHpvbmUgbmFtZVxuXHRcdFx0XHRwYXJhbXMucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fc2VsZWN0ZWRab25lLm5hbWUpO1xuXG5cdFx0XHRcdC8vIGIpIGdyb3VwIG5hbWVcblx0XHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygnZ3JvdXAnLCB0aGlzLl9zZWxlY3RlZEdyb3VwLm5hbWUpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBST09NU1xuXHRcdFx0cGFyYW1zLnB1dEJvb2woJ3Jvb21zJywgZ2V0Um9vbXMpO1xuXHRcdFx0aWYgKGdldFJvb21zKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBGaWx0ZXJpbmcgcGFyYW1zOlxuXG5cdFx0XHRcdC8vIGMpIGFkdmFuY2VkIGZpbHRlcmluZyBwYXJhbXNcblx0XHRcdFx0aWYgKCQoJyN6bm0tYXBwbHlSb29tRmlsdGVyQ0InKS5wcm9wKCdjaGVja2VkJykgJiYgdGhpcy5fcm9vbUZpbHRlci5maWx0ZXJFeHByZXNzaW9uICE9IG51bGwpXG5cdFx0XHRcdFx0cGFyYW1zLnB1dFNGU0FycmF5KCdyRmlsdGVyJywgdGhpcy5fcm9vbUZpbHRlci5maWx0ZXJFeHByZXNzaW9uLnRvU0ZTQXJyYXkoKSk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFVTRVJTXG5cdFx0XHRwYXJhbXMucHV0Qm9vbCgndXNlcnMnLCBnZXRVc2Vycyk7XG5cdFx0XHRpZiAoZ2V0VXNlcnMpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIEZpbHRlcmluZyBwYXJhbXM6XG5cblx0XHRcdFx0Ly8gYykgcm9vbSBuYW1lXG5cdFx0XHRcdGlmICh0aGlzLl9zZWxlY3RlZFJvb20pXG5cdFx0XHRcdFx0cGFyYW1zLnB1dFV0ZlN0cmluZygncm9vbScsIHRoaXMuX3NlbGVjdGVkUm9vbS5uYW1lKTtcblxuXHRcdFx0XHQvLyBkKSBhZHZhbmNlZCBmaWx0ZXJpbmcgcGFyYW1zXG5cdFx0XHRcdGlmICgkKCcjem5tLWFwcGx5VXNlckZpbHRlckNCJykucHJvcCgnY2hlY2tlZCcpICYmIHRoaXMuX3VzZXJGaWx0ZXIuZmlsdGVyRXhwcmVzc2lvbiAhPSBudWxsKVxuXHRcdFx0XHRcdHBhcmFtcy5wdXRTRlNBcnJheSgndUZpbHRlcicsIHRoaXMuX3VzZXJGaWx0ZXIuZmlsdGVyRXhwcmVzc2lvbi50b1NGU0FycmF5KCkpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyAtLS0gQ1VSUkVOVExZIE1PTklUT1JFRCBEQVRBIC0tLVxuXHRcdFx0aWYgKHRoaXMuX21vbml0b3JlZFR5cGUgIT0gbnVsbCAmJiB0aGlzLl9tb25pdG9yZWROYW1lICE9IG51bGwpXG5cdFx0XHR7XG5cdFx0XHRcdGxldCBtb25pdG9yZWRPYmogPSB0aGlzLl9nZXRCYXNpY01vbml0b3JlZE9iamVjdCgpO1xuXG5cdFx0XHRcdGlmICh0aGlzLl9tb25pdG9yZWRUeXBlID09IHRoaXMuTU9OSVRPUkVEX1RZUEVfWk9ORSlcblx0XHRcdFx0XHRtb25pdG9yZWRPYmoucHV0Qm9vbCgndWMnLCBnZXRab25lVXNlckNvdW50SGlzdG9yeSk7XG5cblx0XHRcdFx0cGFyYW1zLnB1dFNGU09iamVjdCgnbW9uaXRvcmVkJywgbW9uaXRvcmVkT2JqKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gU2V0IHJlcXVlc3QgaWQ7IHdoZW4gYSByZXNwb25zZSBpcyByZWNlaXZlZCwgd2UgY2hlY2sgaXRzIGlkIG1hdGNoZXMgdGhlIGN1cnJlbnQgcmVxdWVzdCBpZDogaWYgbm90LCB0aGUgcmVzcG9uc2UgaXMgZGlzY2FyZGVkXG5cdFx0XHRwYXJhbXMucHV0SW50KCdpZCcsIHRoaXMuX2N1cnJlbnRSZXF1ZXN0SWQpO1xuXG5cdFx0XHQvLyBTZW5kIHJlcXVlc3QgdG8gZXh0ZW5zaW9uXG5cdFx0XHR0aGlzLnNlbmRFeHRlbnNpb25SZXF1ZXN0KHRoaXMuUkVRX0dFVF9EQVRBLCBwYXJhbXMpO1xuXG5cdFx0XHQvLyBTY2hlZHVsZSBuZXh0IHJlcXVlc3Rcblx0XHRcdHRoaXMuX3JlcXVlc3RUaW1lciA9IHNldFRpbWVvdXQoJC5wcm94eSh0aGlzLl9yZXF1ZXN0RGF0YSwgdGhpcyksIE51bWJlcih0aGlzLl9pbnRlcnZhbERyb3BEb3duLnZhbHVlKCkpICogMTAwMCk7XG5cdFx0fVxuXHR9XG5cblx0X2dldEJhc2ljTW9uaXRvcmVkT2JqZWN0KClcblx0e1xuXHRcdGxldCBtb25pdG9yZWRPYmogPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cblx0XHRtb25pdG9yZWRPYmoucHV0VXRmU3RyaW5nKCd6b25lJywgdGhpcy5fbW9uaXRvcmVkWm9uZSk7XG5cdFx0bW9uaXRvcmVkT2JqLnB1dFV0ZlN0cmluZygndHlwZScsIHRoaXMuX21vbml0b3JlZFR5cGUpO1xuXHRcdG1vbml0b3JlZE9iai5wdXRVdGZTdHJpbmcoJ25hbWUnLCB0aGlzLl9tb25pdG9yZWROYW1lKTtcblxuXHRcdHJldHVybiBtb25pdG9yZWRPYmo7XG5cdH1cblxuXHRfc2V0Wm9uZXNEYXRhUHJvdmlkZXIoem9uZXNEYXRhKVxuXHR7XG5cdFx0dGhpcy5fc2V0TGlzdERhdGFQcm92aWRlcih0aGlzLl96b25lTGlzdEJveCwgem9uZXNEYXRhLCB0cnVlKTtcblx0fVxuXG5cdF9zZXRSb29tc0RhdGFQcm92aWRlcihyb29tc0RhdGEpXG5cdHtcblx0XHR0aGlzLl9zZXRMaXN0RGF0YVByb3ZpZGVyKHRoaXMuX3Jvb21MaXN0Qm94LCByb29tc0RhdGEsIHRydWUpO1xuXG5cdFx0Ly8gU2V0IFJlbW92ZSBidXR0b24gc3RhdGVcblx0XHQkKCcjem5tLXJlbW92ZVJvb21CdCcpLmF0dHIoJ2Rpc2FibGVkJywgdGhpcy5fc2VsZWN0ZWRSb29tID09IHVuZGVmaW5lZCk7XG5cdH1cblxuXHRfc2V0VXNlcnNEYXRhUHJvdmlkZXIodXNlcnNEYXRhKVxuXHR7XG5cdFx0dGhpcy5fc2V0TGlzdERhdGFQcm92aWRlcih0aGlzLl91c2VyTGlzdEJveCwgdXNlcnNEYXRhKTtcblx0fVxuXG5cdF9zZXRMaXN0RGF0YVByb3ZpZGVyKGxpc3RXaWRnZXQsIGxpc3REYXRhLCBjb3VudFVzZXJzID0gZmFsc2UpXG5cdHtcblx0XHQvLyBTYXZlIHJlZmVyZW5jZSB0byBzZWxlY3RlZCBpdGVtIG5hbWVcblx0XHQvLyBOT1RFOiBhcyB3ZSBhcmUgc3Vic3RpdHV0aW5nIHRoZSB3aG9sZSBkYXRhIHNvdXJjZSwgd2UgbmVlZCBpdCB0byByZS1zZWxlY3QgdGhlIHNhbWUgaXRlbSBhZnRlciB1cGRhdGVcblx0XHRsZXQgc2VsZWN0ZWREYXRhSXRlbSA9IGxpc3RXaWRnZXQuZGF0YUl0ZW0obGlzdFdpZGdldC5zZWxlY3QoKSk7XG5cdFx0bGV0IHNlbGVjdGVkRGF0YUl0ZW1OYW1lID0gc2VsZWN0ZWREYXRhSXRlbSA/IHNlbGVjdGVkRGF0YUl0ZW0ubmFtZSA6IG51bGw7XG5cblx0XHRsZXQgZGF0YUFycmF5ID0gW107XG5cblx0XHQvLyBDb252ZXJ0IGRhdGEgY29taW5nIGZyb20gc2VydmVyXG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBsaXN0RGF0YS5zaXplKCk7IGkrKylcblx0XHR7XG5cdFx0XHRsZXQgaXRlbURhdGEgPSBsaXN0RGF0YS5nZXRTRlNPYmplY3QoaSk7XG5cblx0XHRcdGxldCBpdGVtID0ge307XG5cdFx0XHRpdGVtLm5hbWUgPSBpdGVtRGF0YS5nZXRVdGZTdHJpbmcoJ25hbWUnKTtcblxuXHRcdFx0aWYgKGNvdW50VXNlcnMpXG5cdFx0XHRcdGl0ZW0udXNlcnMgPSBpdGVtRGF0YS5nZXRJbnQoJ3VzZXJzJyk7XG5cblx0XHRcdGRhdGFBcnJheS5wdXNoKGl0ZW0pO1xuXHRcdH1cblxuXHRcdC8vIENyZWF0ZSBuZXcgZGF0YSBzb3VyY2Vcblx0XHRsZXQgbGlzdERTID0gbmV3IGtlbmRvLmRhdGEuRGF0YVNvdXJjZSh7XG5cdFx0XHRkYXRhOiBkYXRhQXJyYXksXG5cdFx0XHRzY2hlbWE6IHtcblx0XHRcdFx0bW9kZWw6IHtcblx0XHRcdFx0XHRpZDogJ25hbWUnXG5cdFx0XHRcdH1cblx0XHRcdH0sXG5cdFx0XHRzb3J0OiB7XG5cdFx0XHRcdGZpZWxkOiAnbmFtZScsXG5cdFx0XHRcdGRpcjogJ2FzYydcblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdC8vIEFzc2lnbiBkYXRhIHNvdXJjZSB0byBsaXN0XG5cdFx0bGlzdFdpZGdldC5zZXREYXRhU291cmNlKGxpc3REUyk7XG5cblx0XHQvLyBTZWxlY3QgYWdhaW4gcHJldmlvdXNseSBzZWxlY3RlZCBpdGVtXG5cdFx0aWYgKHNlbGVjdGVkRGF0YUl0ZW1OYW1lICE9IG51bGwpXG5cdFx0e1xuXHRcdFx0c2VsZWN0ZWREYXRhSXRlbSA9IGxpc3REUy5nZXQoc2VsZWN0ZWREYXRhSXRlbU5hbWUpO1xuXG5cdFx0XHRpZiAoc2VsZWN0ZWREYXRhSXRlbSlcblx0XHRcdHtcblx0XHRcdFx0bGV0IHNlbGVjdGVkRWxlbWVudCA9IGxpc3RXaWRnZXQud3JhcHBlci5maW5kKCdbZGF0YS11aWQ9XCInICsgc2VsZWN0ZWREYXRhSXRlbS51aWQgKyAnXCJdJyk7XG5cdFx0XHRcdGxpc3RXaWRnZXQuc2VsZWN0KHNlbGVjdGVkRWxlbWVudCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUmVzZXQgbWFpbiBjb250cm9sIHN0YXRlXG5cdFx0dGhpcy5fc2V0TWFpbkNvbnRyb2xzRW5hYmxlZCgpO1xuXHR9XG5cblx0X3NldEdyb3Vwc0RhdGFQcm92aWRlcihncm91cHNEYXRhKVxuXHR7XG5cdFx0Ly8gQ3JlYXRlIEdyb3VwcyBhcnJheSwgYWRkaW5nIGNvbW1vbiBlbnRyaWVzXG5cdFx0bGV0IGdyb3Vwc0FycmF5ID0gW1xuXHRcdFx0dGhpcy5fZ2V0R3JvdXBPYmooJycsIHRoaXMuQU5ZX0xBQkVMKSxcblx0XHRcdHRoaXMuX2dldEdyb3VwT2JqKHRoaXMuREVGQVVMVF9HUk9VUF9OQU1FKVxuXHRcdF07XG5cblx0XHQvLyBTYXZlIHJlZmVyZW5jZSB0byBzZWxlY3RlZCBncm91cCBuYW1lXG5cdFx0Ly8gTk9URTogYXMgd2UgYXJlIHN1YnN0aXR1dGluZyB0aGUgd2hvbGUgZGF0YSBzb3VyY2UsIHdlIG5lZWQgaXQgdG8gcmUtc2VsZWN0IHRoZSBzYW1lIGl0ZW0gYWZ0ZXIgdXBkYXRlXG5cdFx0bGV0IHNlbGVjdGVkRGF0YUl0ZW0gPSB0aGlzLl9zZWxlY3RlZEdyb3VwO1xuXHRcdGxldCBzZWxlY3RlZERhdGFJdGVtTmFtZSA9IHNlbGVjdGVkRGF0YUl0ZW0gPyBzZWxlY3RlZERhdGFJdGVtLm5hbWUgOiBudWxsO1xuXG5cdFx0aWYgKGdyb3Vwc0RhdGEgIT0gbnVsbClcblx0XHR7XG5cdFx0XHRmb3IgKGxldCBpID0gMDsgaSA8IGdyb3Vwc0RhdGEubGVuZ3RoOyBpKyspXG5cdFx0XHR7XG5cdFx0XHRcdGlmIChncm91cHNEYXRhW2ldICE9IHRoaXMuREVGQVVMVF9HUk9VUF9OQU1FKSAvLyBEZWZhdWx0IGdyb3VwIHdhcyBhZGRlZCBiZWZvcmVcblx0XHRcdFx0XHRncm91cHNBcnJheS5wdXNoKHRoaXMuX2dldEdyb3VwT2JqKGdyb3Vwc0RhdGFbaV0pKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBDcmVhdGUgbmV3IGRhdGEgc291cmNlXG5cdFx0bGV0IGdyb3VwRFMgPSBuZXcga2VuZG8uZGF0YS5EYXRhU291cmNlKHtcblx0XHRcdGRhdGE6IGdyb3Vwc0FycmF5LFxuXHRcdFx0c2NoZW1hOiB7XG5cdFx0XHRcdG1vZGVsOiB7XG5cdFx0XHRcdFx0aWQ6ICduYW1lJ1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0XHQvLyBBc3NpZ24gZGF0YSBzb3VyY2UgdG8gZHJvcGRvd25cblx0XHR0aGlzLl9ncm91cHNEcm9wRG93bi5zZXREYXRhU291cmNlKGdyb3VwRFMpO1xuXG5cdFx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdFx0Ly8gU2VsZWN0IHByZXZpb3VzbHkgc2VsZWN0ZWQgaXRlbVxuXHRcdHNlbGVjdGVkRGF0YUl0ZW0gPSBncm91cERTLmdldChzZWxlY3RlZERhdGFJdGVtTmFtZSk7XG5cblx0XHQvLyBJZiBncm91cCBpcyBub3QgZm91bmQsIHNlbGVjdCB0aGUgZGVmYXVsdCBvbmVcblx0XHRpZiAoc2VsZWN0ZWREYXRhSXRlbSA9PSB1bmRlZmluZWQpXG5cdFx0XHRzZWxlY3RlZERhdGFJdGVtID0gZ3JvdXBEUy5nZXQodGhpcy5ERUZBVUxUX0dST1VQX05BTUUpO1xuXG5cdFx0dGhpcy5fZ3JvdXBzRHJvcERvd24uc2VsZWN0KGZ1bmN0aW9uKGRhdGFJdGVtKSB7XG5cdFx0XHRyZXR1cm4gZGF0YUl0ZW0ubmFtZSA9PT0gc2VsZWN0ZWREYXRhSXRlbS5uYW1lO1xuXHRcdH0pO1xuXHR9XG5cblx0X2dldEdyb3VwT2JqKG5hbWUsIGxhYmVsID0gbnVsbClcblx0e1xuXHRcdHJldHVybiB7XG5cdFx0XHRuYW1lOiBuYW1lLFxuXHRcdFx0bGFiZWw6IChsYWJlbCAhPSBudWxsID8gbGFiZWwgOiBuYW1lKVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBVcGRhdGUgdGhlIFNjb3BlIGxhYmVsIGluIHRoZSBSb29tcyBwYW5lbCBhbmQgaW4gdGhlIFVzZXJzIHBhbmVsLlxuXHQgKi9cblx0X3NldFNjb3BlTGFiZWwoKVxuXHR7XG5cdFx0bGV0IHJvb21TY29wZSA9ICcnO1xuXHRcdGxldCB1c2VyU2NvcGUgPSAnJztcblxuXHRcdGlmICh0aGlzLl9zZWxlY3RlZFpvbmUpXG5cdFx0e1xuXHRcdFx0bGV0IHpvbmVOYW1lID0gdGhpcy5fc2VsZWN0ZWRab25lLm5hbWU7XG5cdFx0XHRyb29tU2NvcGUgPSB0aGlzLl9nZXRTY29wZUxhYmVsUGFydCgnWm9uZTonLCB6b25lTmFtZSk7XG5cblx0XHRcdGxldCBzZWxlY3RlZEdyb3VwID0gdGhpcy5fc2VsZWN0ZWRHcm91cDtcblx0XHRcdGxldCBncm91cE5hbWUgPSAoc2VsZWN0ZWRHcm91cCAmJiBzZWxlY3RlZEdyb3VwLm5hbWUgIT0gJycpID8gc2VsZWN0ZWRHcm91cC5uYW1lIDogdGhpcy5BTllfTEFCRUw7XG5cdFx0XHRsZXQgcm9vbU5hbWUgPSAodGhpcy5fc2VsZWN0ZWRSb29tKSA/IHRoaXMuX3NlbGVjdGVkUm9vbS5uYW1lIDogdGhpcy5BTllfTEFCRUw7XG5cblx0XHRcdHVzZXJTY29wZSA9IHRoaXMuX2dldFNjb3BlTGFiZWxQYXJ0KCdab25lOicsIHpvbmVOYW1lKSArICc8YnI+JyArIHRoaXMuX2dldFNjb3BlTGFiZWxQYXJ0KCdHcm91cDonLCBncm91cE5hbWUpICsgJzxicj4nICsgdGhpcy5fZ2V0U2NvcGVMYWJlbFBhcnQoJ1Jvb206Jywgcm9vbU5hbWUpO1xuXHRcdH1cblxuXHRcdCQoJyN6bm0tcm9vbVNjb3BlTGInKS5odG1sKHJvb21TY29wZSk7XG5cdFx0JCgnI3pubS11c2VyU2NvcGVMYicpLmh0bWwodXNlclNjb3BlKTtcblx0fVxuXG5cdF9nZXRTY29wZUxhYmVsUGFydChsYWJlbCwgdGV4dClcblx0e1xuXHRcdHJldHVybiBgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+JHtsYWJlbH08L3NwYW4+IDxzcGFuPiR7dGV4dH08L3NwYW4+YDtcblx0fVxuXG5cdF9yZXNldFJvb21MaXN0KClcblx0e1xuXHRcdHRoaXMuX3Jvb21MaXN0Qm94LnNldERhdGFTb3VyY2UoW10pO1xuXG5cdFx0Ly8gQWxzbyBkaXNhYmxlIFJlbW92ZSBidXR0b25cblx0XHQkKCcjem5tLXJlbW92ZVJvb21CdCcpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdH1cblxuXHRfcmVzZXRVc2VyTGlzdCgpXG5cdHtcblx0XHR0aGlzLl91c2VyTGlzdEJveC5zZXREYXRhU291cmNlKFtdKTtcblx0fVxuXG5cdF9zZXRNYWluQ29udHJvbHNFbmFibGVkKClcblx0e1xuXHRcdGxldCBlbmFibGVkID0gKHRoaXMuX2FjdGl2ZVBhbmVsVHlwZSA9PSAnem9uZScgJiYgdGhpcy5fc2VsZWN0ZWRab25lICE9IHVuZGVmaW5lZClcblx0XHRcdFx0XHR8fCAodGhpcy5fYWN0aXZlUGFuZWxUeXBlID09ICdyb29tJyAmJiB0aGlzLl9zZWxlY3RlZFJvb20gIT0gdW5kZWZpbmVkKVxuXHRcdFx0XHRcdHx8ICh0aGlzLl9hY3RpdmVQYW5lbFR5cGUgPT0gJ3VzZXInICYmIHRoaXMuX3NlbGVjdGVkVXNlciAhPSB1bmRlZmluZWQpO1xuXG5cdFx0JCgnI3pubS1tYWluQ29udHJvbHMnKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGVkKTtcblxuXHRcdCQoJyN6bm0tbW9uaXRvclRhcmdldExiJykudGV4dChlbmFibGVkID8gY2FwaXRhbGl6ZUZpcnN0KHRoaXMuX2FjdGl2ZVBhbmVsVHlwZSkgOiAnc2VsZWN0aW9uJyk7XG5cdFx0JCgnI3pubS1tZXNzYWdlSW4nKS5hdHRyKCdwbGFjZWhvbGRlcicsICdNZXNzYWdlIHRvICcgKyAoZW5hYmxlZCA/IGNhcGl0YWxpemVGaXJzdCh0aGlzLl9hY3RpdmVQYW5lbFR5cGUpIDogJ3NlbGVjdGlvbicpKTtcblx0fVxuXG5cdF9zaG93TGltaXRFeGNlZWRlZFdhcm5pbmcoaWNvbiwgdGl0bGUpXG5cdHtcblx0XHRpY29uLmF0dHIoJ3RpdGxlJywgdGl0bGUpO1xuXG5cdFx0aWYgKHRpdGxlICE9ICcnKVxuXHRcdFx0aWNvbi5yZW1vdmVDbGFzcygnaGlkZGVuJyk7XG5cdFx0ZWxzZVxuXHRcdFx0aWNvbi5hZGRDbGFzcygnaGlkZGVuJyk7XG5cdH1cblxuXHRfc2hvd1Jvb21DcmVhdGlvblBhbmVsKHJvb21TZXR0aW5ncylcblx0e1xuXHRcdC8vIEJ1aWxkIHVzZXIgaW50ZXJmYWNlIGJhc2VkIG9uIHBhc3NlZCBkYXRhXG5cdFx0dGhpcy5fcm9vbUNyZWF0aW9uSUJ1aWxkZXIuYnVpbGRJbnRlcmZhY2Uocm9vbVNldHRpbmdzLCAnem5tLXJvb21DcmVhdG9yVGFiTmF2JywgZmFsc2UsICdyYycpO1xuXG5cdFx0Ly8gRW5hYmxlIHNjcm9sbGluZyB0YWJzXG5cdFx0JCgnI3pubS1yb29tQ3JlYXRvclRhYk5hdiA+ICN0YWJzJykuc2Nyb2xsaW5nVGFicyh7XG5cdFx0XHRib290c3RyYXBWZXJzaW9uOiA0LFxuXHRcdFx0c2Nyb2xsVG9UYWJFZGdlOiB0cnVlLFxuXHRcdFx0ZW5hYmxlU3dpcGluZzogdHJ1ZSxcblx0XHRcdGRpc2FibGVTY3JvbGxBcnJvd3NPbkZ1bGx5U2Nyb2xsZWQ6IHRydWUsXG5cdFx0XHRjc3NDbGFzc0xlZnRBcnJvdzogJ2ZhIGZhLWNoZXZyb24tbGVmdCcsXG5cdFx0XHRjc3NDbGFzc1JpZ2h0QXJyb3c6ICdmYSBmYS1jaGV2cm9uLXJpZ2h0J1xuXHRcdH0pO1xuXG5cdFx0Ly8gUmVzZXQgTm90aWZ5IGNsaWVudHMgY2hlY2tib3hcblx0XHQkKCcjem5tLW5vdGlmeUNsaWVudHNDQicpLnByb3AoJ2NoZWNrZWQnLCB0cnVlKTtcblxuXHRcdC8vIFNldCBsaXN0ZW5lciBmb3IgY3VzdG9tIGFjdGlvbnMgdHJpZ2dlcmVkIGJ5IGNvbmZpZ3VyYXRpb24gaW50ZXJmYWNlXG5cdFx0JCgnI3pubS1yb29tQ3JlYXRvclRhYk5hdicpLm9uKCd2YWx1ZS1zZXQnLCAkLnByb3h5KHRoaXMuX29uQ29uZmlnVmFsdWVTZXQsIHRoaXMpKTtcblxuXHRcdC8vIERpc3BsYXkgcGFuZWxcblx0XHQkKCcjem5tLWNyZWF0ZVJvb21Nb2RhbCcpLm1vZGFsKCdzaG93Jyk7XG5cblx0XHQvLyBSZXNldCBhbmQgaGlkZSBlcnJvciBtZXNzYWdlXG5cdFx0dGhpcy5fcmVzZXRSb29tQ3JlYXRpb25FcnJvcigpO1xuXHR9XG5cblx0X3Jlc2V0Um9vbUNyZWF0aW9uRXJyb3IoKVxuXHR7XG5cdFx0Ly8gUmVzZXQgYW5kIGhpZGUgZXJyb3IgbWVzc2FnZVxuXHRcdCQoJyN6bm0tY3JlYXRlUm9vbUVycm9yJykudGV4dCgnJyk7XG5cdFx0JCgnI3pubS1jcmVhdGVSb29tRXJyb3InKS5oaWRlKCk7XG5cdH1cblxuXHRfdmFsaWRhdGVOZXdSb29tTmFtZShjaGFuZ2VzKVxuXHR7XG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBjaGFuZ2VzLnNpemUoKTsgaSsrKVxuXHRcdHtcblx0XHRcdGNvbnN0IHNldHRpbmcgPSBjaGFuZ2VzLmdldFNGU09iamVjdChpKTtcblxuXHRcdFx0aWYgKHNldHRpbmcuY29udGFpbnNLZXkoJ25hbWUnKSAmJiBzZXR0aW5nLmdldFV0ZlN0cmluZygnbmFtZScpID09ICduYW1lJylcblx0XHRcdHtcblx0XHRcdFx0Ly8gR2V0IG5hbWUgdmFsdWVcblx0XHRcdFx0Y29uc3QgbmFtZSA9IHNldHRpbmcuZ2V0VGV4dCgndmFsdWUnKTtcblxuXHRcdFx0XHQvLyBHZXQgZGF0YSBzb3VyY2Vcblx0XHRcdFx0Y29uc3QgZHMgPSB0aGlzLl9yb29tTGlzdEJveC5kYXRhU291cmNlLmRhdGEoKTtcblxuXHRcdFx0XHQvLyBDaGVjayBpZiBuYW1lIGV4aXN0cyBpbiBkYXRhIHNvdXJjZVxuXHRcdFx0XHRmb3IgKGxldCBqID0gMDsgaiA8IGRzLmxlbmd0aDsgaisrKVxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0aWYgKGRzW2pdLm5hbWUgPT0gbmFtZSlcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0cnVlO1xuXHR9XG5cblx0X2VuYWJsZUNyZWF0ZVJvb21QYW5lbChlbmFibGUpXG5cdHtcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI3pubS1jcmVhdGVSb29tTW9kYWwnKTtcblxuXHRcdC8vIEVuYWJsZSBtb2RhbCBjbG9zZSBidXR0b25zXG5cdFx0JCgnYnV0dG9uW2RhdGEtZGlzbWlzcz1cIm1vZGFsXCJdJywgbW9kYWxFbGVtZW50KS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGUpO1xuXG5cdFx0Ly8gRW5hYmxlIGNyZWF0ZSBidXR0b25cblx0XHQkKCcjem5tLXJvb21DcmVhdG9yQ3JlYXRlQnQnLCBtb2RhbEVsZW1lbnQpLmF0dHIoJ2Rpc2FibGVkJywgIWVuYWJsZSk7XG5cblx0XHQvLyBFbmFibGUgY2hlY2tib3hcblx0XHQkKCcjem5tLW5vdGlmeUNsaWVudHNDQicsIG1vZGFsRWxlbWVudCkuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlKTtcblxuXHRcdC8vIEVuYWJsZSBjb25maWd1cmF0aW9uIGludGVyZmFjZVxuXHRcdHRoaXMuX3Jvb21DcmVhdGlvbklCdWlsZGVyLmRpc2FibGVJbnRlcmZhY2UoIWVuYWJsZSk7XG5cdH1cblxuXHRfY2xlYXJUYWJzKG1vbml0b3JlZFR5cGUpXG5cdHtcblx0XHQvLyBEZXN0cm95IHNjcm9sbGluZyB0YWJzXG5cdFx0JChgI3pubS0ke21vbml0b3JlZFR5cGV9VGFiTmF2aWdhdG9yICN0YWJzYCkuc2Nyb2xsaW5nVGFicygnZGVzdHJveScpO1xuXG5cdFx0Ly8gUmVtb3ZlIGFsbCB0YWIgbmF2aWdhdG9yIGNvbnRlbnRcblx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmRlc3Ryb3lJbnRlcmZhY2UoKTtcblxuXHRcdC8vIFNldCBhbGwgdGFiIHBhbmVscyBhcyBpbmFjdGl2ZVxuXHRcdCQoYCN6bm0tJHttb25pdG9yZWRUeXBlfVRhYk5hdmlnYXRvciAjdGFiUGFuZWxzIC50YWItcGFuZWApLnJlbW92ZUNsYXNzKCdzaG93IGFjdGl2ZScpO1xuXG5cdFx0Ly8gUmVtb3ZlIHRhYiBjaGFuZ2UgZXZlbnQgbGlzdGVuZXJcblx0XHQkKCdhW2RhdGEtdG9nZ2xlPVwidGFiXCJdJykub2ZmKCdzaG93bi5icy50YWInKTtcblxuXHRcdHRoaXMuX3NraXBJbml0VGFicyA9IGZhbHNlO1xuXHR9XG5cblx0X3Nob3dab25lVHJhZmZpY0RhdGEoZGF0YSlcblx0e1xuXHRcdC8vIEJ1aWxkIHVzZXIgY291bnQgbW9uaXRvciBoaXN0b3J5XG5cdFx0aWYgKGRhdGEuY29udGFpbnNLZXkoJ2hpc3RvcnknKSlcblx0XHR7XG5cdFx0XHRsZXQgdXNlckNvdW50RGF0YSA9IGRhdGEuZ2V0SW50QXJyYXkoJ2hpc3RvcnknKTtcblx0XHRcdGxldCBzYW1wbGluZ1JhdGVNaW5zID0gZGF0YS5nZXRJbnQoJ3JhdGUnKTtcblxuXHRcdFx0dGhpcy5fdXNlckNvdW50Q2hhcnQuYWRkSGlzdG9yeUVudHJpZXModXNlckNvdW50RGF0YSwgc2FtcGxpbmdSYXRlTWlucyAqIDYwKTtcblx0XHR9XG5cblx0XHQvLyBBZGQgbGFzdCB1c2VyIGNvdW50IHZhbHVlXG5cdFx0dGhpcy5fdXNlckNvdW50Q2hhcnQuYWRkRW50cnkoZGF0YS5nZXRJbnQoJ2N1cnJlbnQnKSk7XG5cdH1cblxuXHRfYWRkTG9nRW50cnlUb0dyaWQoZ3JpZCwgaXRlbSlcblx0e1xuXHRcdGxldCBkcyA9IGdyaWQuZGF0YVNvdXJjZTtcblxuXHRcdC8vIENoZWNrIGl0ZW1zIGxpbWl0XG5cdFx0aWYgKGRzLnRvdGFsKCkgPT0gdGhpcy5NQVhfRVhURU5TSU9OX0xPR19TSVpFKVxuXHRcdFx0ZHMucmVtb3ZlKGRzLmF0KDApKTtcblxuXHRcdGRzLmFkZChpdGVtKTtcblx0fVxuXG5cdF9zaG93VXNlclN0YXRzRGF0YShkYXRhKVxuXHR7XG5cdFx0Ly8gUGFja2V0IHF1ZXVlIGZpbGxpbmcgJVxuXHRcdGxldCBwYWNrZXRRdWV1ZSA9IHJvdW5kVG9EZWNpbWFscyhkYXRhLmdldEZsb2F0KCdwYWNrZXQnKSwgMik7XG5cdFx0dGhpcy5fcGFja2V0UXVldWVDaGFydC5zZXREYXRhU291cmNlKFt7Y2F0ZWdvcnk6ICdRdWV1ZScsIHZhbHVlOiBwYWNrZXRRdWV1ZX1dKTtcblxuXHRcdC8vIERyb3BwZWQgbWVzc2FnZXNcblx0XHRsZXQgZHJvcHBlZE1zZyA9IGRhdGEuZ2V0SW50KCdkcm9wcGVkJyk7XG5cdFx0dGhpcy5fZHJvcHBlZE1zZ0NoYXJ0LnNldERhdGFTb3VyY2UoW3tjYXRlZ29yeTogJ0Ryb3BwZWQnLCB2YWx1ZTogZHJvcHBlZE1zZ31dKTtcblxuXHRcdC8vIFdyaXR0ZW4gZGF0YSBhbW91bnRcblx0XHRsZXQgd3JpdHRlbkRhdGFPYmogPSBzY2FsZUJ5dGVzKGRhdGEuZ2V0TG9uZygnd0J5dGVzJyksIDIpO1xuXHRcdHRoaXMuX3dyaXR0ZW5EYXRhQ2hhcnQuc2V0RGF0YVNvdXJjZShbe2NhdGVnb3J5OiAnV3JpdHRlbicsIHZhbHVlOiB3cml0dGVuRGF0YU9iai52YWx1ZSwgdW5pdDogd3JpdHRlbkRhdGFPYmoudW5pdH1dKTtcblxuXHRcdC8vIFJlYWQgZGF0YSBhbW91bnRcblx0XHRsZXQgcmVhZERhdGFPYmogPSBzY2FsZUJ5dGVzKGRhdGEuZ2V0TG9uZygnckJ5dGVzJyksIDIpO1xuXHRcdHRoaXMuX3JlYWREYXRhQ2hhcnQuc2V0RGF0YVNvdXJjZShbe2NhdGVnb3J5OiAnUmVhZCcsIHZhbHVlOiByZWFkRGF0YU9iai52YWx1ZSwgdW5pdDogcmVhZERhdGFPYmoudW5pdH1dKTtcblx0fVxuXG5cdF9zZXRHZW9Mb2NhdGlvblVJKGdlb0xvY0RhdGEpXG5cdHtcblx0XHRjb25zdCBsb2NhdGlvbiA9IGdlb0xvY0RhdGEuZ2V0VXRmU3RyaW5nKCdsb2NhdGlvbicpO1xuXHRcdGNvbnN0IGVycm9yID0gZ2VvTG9jRGF0YS5nZXRVdGZTdHJpbmcoJ2Vycm9yJyk7XG5cdFx0Y29uc3QgbGF0aXR1ZGUgPSBnZW9Mb2NEYXRhLmdldFV0ZlN0cmluZygnbGF0aXR1ZGUnKTtcblx0XHRjb25zdCBsb25naXR1ZGUgPSBnZW9Mb2NEYXRhLmdldFV0ZlN0cmluZygnbG9uZ2l0dWRlJyk7XG5cblx0XHQvLyBTaG93IGxvY2F0aW9uXG5cdFx0JCgnI3pubS1nZW9Mb2NhdGlvbkxiJykudGV4dChsb2NhdGlvbik7XG5cblx0XHQvLyBMb2cgZXJyb3Jcblx0XHRpZiAoZXJyb3IgIT0gJycpXG5cdFx0XHR0aGlzLnNoZWxsQ3RybC5sb2dNZXNzYWdlKGVycm9yLCAnd2FybicpO1xuXG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgU2hvdyBtYXAgYnV0dG9uXG5cdFx0aWYgKGxhdGl0dWRlICE9ICcnICYmIGxvbmdpdHVkZSAhPSAnJylcblx0XHR7XG5cdFx0XHQkKCcjem5tLXNob3dNYXBCdCcpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXG5cdFx0XHRjb25zdCB1cmwgPSBgaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9tYXBzL3NlYXJjaC8/YXBpPTEmcXVlcnk9JHtOdW1iZXIobGF0aXR1ZGUpICsgJywnICsgTnVtYmVyKGxvbmdpdHVkZSl9YDtcblx0XHRcdCQoJyN6bm0tc2hvd01hcEJ0JykuYXR0cignaHJlZicsIHVybCk7XG5cdFx0fVxuXHR9XG5cblx0X2NsZWFyR2VvTG9jYXRpb25VSSgpXG5cdHtcblx0XHQkKCcjem5tLWdlb0xvY2F0aW9uTGInKS50ZXh0KCcnKTtcblx0XHQkKCcjem5tLXNob3dNYXBCdCcpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0JCgnI3pubS1zaG93TWFwQnQnKS5hdHRyKCdocmVmJywgJyMnKTtcblx0fVxuXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIFBSSVZBVEUgR0VUVEVSU1xuXHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdGdldCBfYWN0aXZlUGFuZWxUeXBlKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl9hY2NvcmRpb24uc2VsZWN0KCkuZGF0YSgnaXRlbVR5cGUnKTtcblx0fVxuXG5cdGdldCBfc2VsZWN0ZWRab25lKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl96b25lTGlzdEJveC5kYXRhSXRlbSh0aGlzLl96b25lTGlzdEJveC5zZWxlY3QoKSk7XG5cdH1cblxuXHRnZXQgX3NlbGVjdGVkR3JvdXAoKVxuXHR7XG5cdFx0cmV0dXJuIHRoaXMuX2dyb3Vwc0Ryb3BEb3duLmRhdGFJdGVtKHRoaXMuX2dyb3Vwc0Ryb3BEb3duLnNlbGVjdCgpKTtcblx0fVxuXG5cdGdldCBfc2VsZWN0ZWRSb29tKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl9yb29tTGlzdEJveC5kYXRhSXRlbSh0aGlzLl9yb29tTGlzdEJveC5zZWxlY3QoKSk7XG5cdH1cblxuXHRnZXQgX3NlbGVjdGVkVXNlcigpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5fdXNlckxpc3RCb3guZGF0YUl0ZW0odGhpcy5fdXNlckxpc3RCb3guc2VsZWN0KCkpO1xuXHR9XG5cblx0Z2V0IF9pc0VkaXRpbmcoKVxuXHR7XG5cdFx0cmV0dXJuICh0aGlzLl9tb25pdG9yZWRUeXBlICE9IG51bGwgJiYgdGhpcy5fbW9uaXRvcmVkTmFtZSAhPSBudWxsICYmIHRoaXMuX2lzRWRpdE1vZGUpO1xuXHR9XG59XG4iLCJleHBvcnQgY2xhc3MgUm9vbVByb3BlcnRpZXNEYXRhXG57XG5cdHN0YXRpYyBnZXQgTEFCRUxTKCkge1xuXHRcdGxldCBtYXAgPSBuZXcgTWFwKCk7XG5cblx0XHRtYXAuc2V0KFNGUzJYLlJvb21Qcm9wZXJ0aWVzLk5BTUUsICduYW1lJyk7XG5cdFx0bWFwLnNldChTRlMyWC5Sb29tUHJvcGVydGllcy5HUk9VUF9JRCwgJ2dyb3VwSWQnKTtcblx0XHRtYXAuc2V0KFNGUzJYLlJvb21Qcm9wZXJ0aWVzLk1BWF9VU0VSUywgJ21heFVzZXJzJyk7XG5cdFx0bWFwLnNldChTRlMyWC5Sb29tUHJvcGVydGllcy5NQVhfU1BFQ1RBVE9SUywgJ21heFNwZWN0YXRvcnMnKTtcblx0XHRtYXAuc2V0KFNGUzJYLlJvb21Qcm9wZXJ0aWVzLlVTRVJfQ09VTlQsICd1c2VyQ291bnQnKTtcblx0XHRtYXAuc2V0KFNGUzJYLlJvb21Qcm9wZXJ0aWVzLlNQRUNUQVRPUl9DT1VOVCwgJ3NwZWN0YXRvckNvdW50Jyk7XG5cdFx0bWFwLnNldChTRlMyWC5Sb29tUHJvcGVydGllcy5JU19HQU1FLCAnaXNHYW1lJyk7XG5cdFx0bWFwLnNldChTRlMyWC5Sb29tUHJvcGVydGllcy5JU19QUklWQVRFLCAnaXNQcml2YXRlJyk7XG5cdFx0bWFwLnNldChTRlMyWC5Sb29tUHJvcGVydGllcy5IQVNfRlJFRV9QTEFZRVJfU0xPVFMsICdoYXNGcmVlUGxheWVyU2xvdHMnKTtcblx0XHRtYXAuc2V0KFNGUzJYLlJvb21Qcm9wZXJ0aWVzLklTX1RZUEVfU0ZTR0FNRSwgJ2lzU0ZTR2FtZVR5cGUnKTtcblxuXHRcdHJldHVybiBtYXA7XG5cdH1cblxuXHRzdGF0aWMgZ2V0TGFiZWwocHJvcE5hbWUpXG5cdHtcblx0XHRyZXR1cm4gJ1Jvb20uJyArIFJvb21Qcm9wZXJ0aWVzRGF0YS5MQUJFTFMuZ2V0KHByb3BOYW1lKTtcblx0fVxuXG5cdHN0YXRpYyBnZXQgcHJvcGVydGllc0FycmF5KClcblx0e1xuXHRcdGNvbnN0IGFyciA9IFtdO1xuXG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgUm9vbVByb3BlcnRpZXNEYXRhLkxBQkVMUy5lbnRyaWVzKCkpXG5cdFx0e1xuXHRcdFx0YXJyLnB1c2goe1xuXHRcdFx0XHRsYWJlbDogUm9vbVByb3BlcnRpZXNEYXRhLmdldExhYmVsKGtleSksXG5cdFx0XHRcdHZhbHVlOiBrZXlcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHJldHVybiBhcnI7XG5cdH1cbn1cblxuZXhwb3J0IGNsYXNzIFVzZXJQcm9wZXJ0aWVzRGF0YVxue1xuXHRzdGF0aWMgZ2V0IExBQkVMUygpIHtcblx0XHRsZXQgbWFwID0gbmV3IE1hcCgpO1xuXG5cdFx0bWFwLnNldChTRlMyWC5Vc2VyUHJvcGVydGllcy5OQU1FLCAnbmFtZScpO1xuXHRcdG1hcC5zZXQoU0ZTMlguVXNlclByb3BlcnRpZXMuSVNfUExBWUVSLCAnaXNQbGF5ZXInKTtcblx0XHRtYXAuc2V0KFNGUzJYLlVzZXJQcm9wZXJ0aWVzLklTX1NQRUNUQVRPUiwgJ2lzU3BlY3RhdG9yJyk7XG5cdFx0bWFwLnNldChTRlMyWC5Vc2VyUHJvcGVydGllcy5JU19OUEMsICdpc05QQycpO1xuXHRcdG1hcC5zZXQoU0ZTMlguVXNlclByb3BlcnRpZXMuUFJJVklMRUdFX0lELCAncHJpdmlsZWdlSWQnKTtcblxuXHRcdHJldHVybiBtYXA7XG5cdH1cblxuXHRzdGF0aWMgZ2V0TGFiZWwocHJvcE5hbWUpXG5cdHtcblx0XHRyZXR1cm4gJ1VzZXIuJyArIFVzZXJQcm9wZXJ0aWVzRGF0YS5MQUJFTFMuZ2V0KHByb3BOYW1lKTtcblx0fVxuXG5cdHN0YXRpYyBnZXQgcHJvcGVydGllc0FycmF5KClcblx0e1xuXHRcdGNvbnN0IGFyciA9IFtdO1xuXG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgVXNlclByb3BlcnRpZXNEYXRhLkxBQkVMUy5lbnRyaWVzKCkpXG5cdFx0e1xuXHRcdFx0YXJyLnB1c2goe1xuXHRcdFx0XHRsYWJlbDogVXNlclByb3BlcnRpZXNEYXRhLmdldExhYmVsKGtleSksXG5cdFx0XHRcdHZhbHVlOiBrZXlcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHJldHVybiBhcnI7XG5cdH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUM3R0E7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDbktBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDdGFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUN2SEE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDNS9EQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBIiwic291cmNlUm9vdCI6IiJ9
845
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2pzL2NvcmUvbW9kdWxlcy9tb2R1bGUtMTMuYnVuZGxlLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvY29tcG9uZW50cy9tb2R1bGUtc3BlY2lmaWMvc3NsLWNlcnRpZmljYXRlLW1hbmFnZXIuanMiLCJ3ZWJwYWNrOi8vYXBwbGljYXRpb24vLi9zcmMvbW9kdWxlcy9zZXJ2ZXItY29uZmlndXJhdG9yLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7Ynl0ZXNUb1NpemV9IGZyb20gJy4uLy4uL3V0aWxzL3V0aWxpdGllcyc7XG5pbXBvcnQgYWVzanMgZnJvbSAnYWVzLWpzJztcblxuZXhwb3J0IGNsYXNzIFNzbENlcnRpZmljYXRlTWFuYWdlciBleHRlbmRzIEhUTUxFbGVtZW50XG57XG5cdGNvbnN0cnVjdG9yKClcblx0e1xuXHQgICAgc3VwZXIoKTtcblxuXHRcdHRoaXMuX21vZGFsSHRtbCA9IGBcblx0XHRcdDxkaXYgY2xhc3M9XCJtb2RhbFwiIGlkPVwidXBsb2FkTW9kYWxcIiB0YWJpbmRleD1cIi0xXCIgcm9sZT1cImRpYWxvZ1wiIGFyaWEtbGFiZWxsZWRieT1cInVwbG9hZE1vZGFsVGl0bGVcIiBhcmlhLWhpZGRlbj1cInRydWVcIj5cblx0XHRcdFx0PGRpdiBjbGFzcz1cIm1vZGFsLWRpYWxvZyBtb2RhbC1kaWFsb2ctY2VudGVyZWRcIiByb2xlPVwiZG9jdW1lbnRcIj5cblx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtY29udGVudFwiPlxuXHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cIm1vZGFsLWhlYWRlclwiPlxuXHRcdFx0XHRcdFx0XHQ8aDUgY2xhc3M9XCJtb2RhbC10aXRsZSB0ZXh0LXByaW1hcnlcIiBpZD1cInVwbG9hZE1vZGFsVGl0bGVcIj5TU0wgQ2VydGlmaWNhdGUgTWFuYWdlcjwvaDU+XG5cdFx0XHRcdFx0XHRcdDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiY2xvc2VcIiBkYXRhLWRpc21pc3M9XCJtb2RhbFwiIGFyaWEtbGFiZWw9XCJDbG9zZVwiPlxuXHRcdFx0XHRcdFx0XHRcdDxzcGFuIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPiZ0aW1lczs8L3NwYW4+XG5cdFx0XHRcdFx0XHRcdDwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtYm9keSBpbi1mbG93LWludmFsaWQtbXNnXCI+XG5cdFx0XHRcdFx0XHRcdDxmaWVsZHNldCBpZD1cInVwbG9hZEZpZWxkc2V0XCI+XG5cdFx0XHRcdFx0XHRcdFx0PGRpdiBpZD1cInVwbG9hZGVyU3ViZm9ybVwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImZvcm0tZ3JvdXBcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNvbC1mb3JtLWxhYmVsIGZvcm0tbGFiZWwtY29udGFpbmVyXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGxhYmVsIGZvcj1cInVwbG9hZGVyXCIgY2xhc3M9XCJmb3JtLWxhYmVsXCI+Q2VydGlmaWNhdGUga2V5c3RvcmUgKGprcykgPGkgY2xhc3M9XCJmYXMgZmEtcXVlc3Rpb24tY2lyY2xlIHRleHQtbXV0ZWQgaGVscFwiIHRpdGxlPVwiU1NMIGNlcnRpZmljYXRlJ3MgcHJvdGVjdGVkIGtleXN0b3JlIGZpbGUgdG8gYmUgdXBsb2FkZWQgdG8gdGhlIHNlcnZlciwgaW4gamtzIGZvcm1hdFwiPjwvaT48L2xhYmVsPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImlubmVyLXdpZGdldFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxpbnB1dCB0eXBlPVwiZmlsZVwiIGlkPVwidXBsb2FkZXJcIiBuYW1lPVwidXBsb2FkZXJcIiBhY2NlcHQ9XCIuamtzXCIgZGF0YS11cGxvYWQtbXNnPVwiU2VsZWN0IGEgZmlsZVwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxzcGFuIGNsYXNzPVwiay1pbnZhbGlkLW1zZyBwb3NpdGlvbi1zdGF0aWNcIiBkYXRhLWZvcj1cInVwbG9hZGVyXCI+PC9zcGFuPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdDxkaXYgaWQ9XCJwYXNzd29yZHNTdWJmb3JtXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZm9ybS1yb3dcIj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImZvcm0tZ3JvdXAgY29sXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNvbC1mb3JtLWxhYmVsIGZvcm0tbGFiZWwtY29udGFpbmVyXCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8bGFiZWwgZm9yPVwia3NQYXNzd29yZFwiIGNsYXNzPVwiZm9ybS1sYWJlbFwiPktleXN0b3JlIHBhc3N3b3JkIDxpIGNsYXNzPVwiZmFzIGZhLXF1ZXN0aW9uLWNpcmNsZSB0ZXh0LW11dGVkIGhlbHBcIiB0aXRsZT1cIlBhc3N3b3JkIHVzZWQgdG8gcHJvdGVjdCB0aGUgY2VydGlmaWNhdGUga2V5c3RvcmVcIj48L2k+PC9sYWJlbD5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5uZXItd2lkZ2V0XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8aW5wdXQgdHlwZT1cInBhc3N3b3JkXCIgaWQ9XCJrc1Bhc3N3b3JkXCIgbmFtZT1cImtzUGFzc3dvcmRcIiBjbGFzcz1cImZvcm0tY29udHJvbCBrLXRleHRib3hcIiBhdXRvY29tcGxldGU9XCJvZmZcIiByZXF1aXJlZCBkYXRhLXJlcXVpcmVkLW1zZz1cIlJlcXVpcmVkXCIgLz5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxzcGFuIGNsYXNzPVwiay1pbnZhbGlkLW1zZyBwb3NpdGlvbi1zdGF0aWNcIiBkYXRhLWZvcj1cImtzUGFzc3dvcmRcIj48L3NwYW4+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJmb3JtLWdyb3VwIGNvbFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjb2wtZm9ybS1sYWJlbCBmb3JtLWxhYmVsLWNvbnRhaW5lclwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGxhYmVsIGZvcj1cImNvbmZpcm1Lc1Bhc3N3b3JkXCIgY2xhc3M9XCJmb3JtLWxhYmVsXCI+Q29uZmlybSBwYXNzd29yZCA8aSBjbGFzcz1cImZhcyBmYS1xdWVzdGlvbi1jaXJjbGUgdGV4dC1tdXRlZCBoZWxwXCIgdGl0bGU9XCJLZXlzdG9yZSBwYXNzd29yZCBjb25maXJtYXRpb25cIj48L2k+PC9sYWJlbD5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5uZXItd2lkZ2V0XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8aW5wdXQgdHlwZT1cInBhc3N3b3JkXCIgaWQ9XCJjb25maXJtS3NQYXNzd29yZFwiIG5hbWU9XCJjb25maXJtS3NQYXNzd29yZFwiIGNsYXNzPVwiZm9ybS1jb250cm9sIGstdGV4dGJveFwiIGF1dG9jb21wbGV0ZT1cIm9mZlwiIHJlcXVpcmVkIGRhdGEtcmVxdWlyZWQtbXNnPVwiUmVxdWlyZWRcIiAvPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0PHNwYW4gY2xhc3M9XCJrLWludmFsaWQtbXNnIHBvc2l0aW9uLXN0YXRpY1wiIGRhdGEtZm9yPVwiY29uZmlybUtzUGFzc3dvcmRcIj48L3NwYW4+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cblx0XHRcdFx0XHRcdFx0XHRcdDxwPjxlbT5Gb3IgYWRkaXRpb25hbCBzZWN1cml0eSwgZW50ZXIgYWdhaW4gYW5kIGNvbmZpcm0geW91ciBTRlMyWCBhZG1pbmlzdHJhdGlvbiBwYXNzd29yZC48L2VtPjwvcD5cblxuXHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImZvcm0tcm93XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJmb3JtLWdyb3VwIGNvbFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjb2wtZm9ybS1sYWJlbCBmb3JtLWxhYmVsLWNvbnRhaW5lclwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGxhYmVsIGZvcj1cImFkbWluUGFzc3dvcmRcIiBjbGFzcz1cImZvcm0tbGFiZWxcIj5BZG1pbiBwYXNzd29yZCA8aSBjbGFzcz1cImZhcyBmYS1xdWVzdGlvbi1jaXJjbGUgdGV4dC1tdXRlZCBoZWxwXCIgdGl0bGU9XCJTbWFydEZveFNlcnZlciAyWCByZW1vdGUgYWRtaW5pc3RyYXRpb24gcGFzc3dvcmRcIj48L2k+PC9sYWJlbD5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiaW5uZXItd2lkZ2V0XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHQ8aW5wdXQgdHlwZT1cInBhc3N3b3JkXCIgaWQ9XCJhZG1pblBhc3N3b3JkXCIgbmFtZT1cImFkbWluUGFzc3dvcmRcIiBjbGFzcz1cImZvcm0tY29udHJvbCBrLXRleHRib3hcIiBhdXRvY29tcGxldGU9XCJvZmZcIiByZXF1aXJlZCBkYXRhLXJlcXVpcmVkLW1zZz1cIlJlcXVpcmVkXCIgLz5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxzcGFuIGNsYXNzPVwiay1pbnZhbGlkLW1zZyBwb3NpdGlvbi1zdGF0aWNcIiBkYXRhLWZvcj1cImFkbWluUGFzc3dvcmRcIj48L3NwYW4+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXG5cdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJmb3JtLWdyb3VwIGNvbFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjb2wtZm9ybS1sYWJlbCBmb3JtLWxhYmVsLWNvbnRhaW5lclwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGxhYmVsIGZvcj1cImNvbmZpcm1BZG1pblBhc3N3b3JkXCIgY2xhc3M9XCJmb3JtLWxhYmVsXCI+Q29uZmlybSBwYXNzd29yZCA8aSBjbGFzcz1cImZhcyBmYS1xdWVzdGlvbi1jaXJjbGUgdGV4dC1tdXRlZCBoZWxwXCIgdGl0bGU9XCJTbWFydEZveFNlcnZlciAyWCByZW1vdGUgYWRtaW5pc3RyYXRpb24gcGFzc3dvcmQgY29uZmlybWF0aW9uXCI+PC9pPjwvbGFiZWw+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImlubmVyLXdpZGdldFwiPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0PGlucHV0IHR5cGU9XCJwYXNzd29yZFwiIGlkPVwiY29uZmlybUFkbWluUGFzc3dvcmRcIiBuYW1lPVwiY29uZmlybUFkbWluUGFzc3dvcmRcIiBjbGFzcz1cImZvcm0tY29udHJvbCBrLXRleHRib3hcIiBhdXRvY29tcGxldGU9XCJvZmZcIiByZXF1aXJlZCBkYXRhLXJlcXVpcmVkLW1zZz1cIlJlcXVpcmVkXCIgLz5cblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdDxzcGFuIGNsYXNzPVwiay1pbnZhbGlkLW1zZyBwb3NpdGlvbi1zdGF0aWNcIiBkYXRhLWZvcj1cImNvbmZpcm1BZG1pblBhc3N3b3JkXCI+PC9zcGFuPlxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHQ8L2ZpZWxkc2V0PlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwibW9kYWwtZm9vdGVyIGZsZXgtY29sdW1uXCI+XG5cdFx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJkLWZsZXggdy0xMDBcIj5cblx0XHRcdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZmxleC1ncm93LTEgdGV4dC1sZWZ0XCI+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8YnV0dG9uIGlkPVwidXBsb2FkU3NsQnV0dG9uXCIgdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1wcmltYXJ5XCI+PGkgY2xhc3M9XCJmYXMgZmEtdXBsb2FkIG1yLTFcIj48L2k+VXBsb2FkIGNlcnRpZmljYXRlPC9idXR0b24+XG5cdFx0XHRcdFx0XHRcdFx0XHQ8aSBpZD1cInVwbG9hZFNwaW5uZXJcIiBjbGFzcz1cImZhcyBmYS1jaXJjbGUtbm90Y2ggZmEtc3BpbiB0ZXh0LXByaW1hcnkgYWxpZ24tbWlkZGxlIG1sLTFcIj48L2k+XG5cdFx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImZsZXgtZ3Jvdy0xIHRleHQtcmlnaHRcIj5cblx0XHRcdFx0XHRcdFx0XHRcdDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiay1idXR0b24gay1zZWNvbmRhcnlcIiBkYXRhLWRpc21pc3M9XCJtb2RhbFwiPkNhbmNlbDwvYnV0dG9uPlxuXHRcdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdFx0PGRpdiBpZD1cInVwbG9hZEVycm9yTXNnXCIgY2xhc3M9XCJ0ZXh0LWRhbmdlciBtdC0zXCI+PC9kaXY+XG5cdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L2Rpdj5cblx0XHRgO1xuXG5cdFx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0XHQkKHRoaXMpLmFwcGVuZChgXG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY29sLXNtLTUgY29sLWxnLTQgY29sLWZvcm0tbGFiZWwgZm9ybS1sYWJlbC1jb250YWluZXJcIj5cblx0XHRcdFx0PGxhYmVsIGNsYXNzPVwiZm9ybS1sYWJlbFwiPlVwbG9hZCBjZXJ0aWZpY2F0ZSA8aSBjbGFzcz1cImZhcyBmYS1xdWVzdGlvbi1jaXJjbGUgdGV4dC1tdXRlZCBoZWxwXCIgdGl0bGU9XCJVcGxvYWQgYW4gU1NMIGNlcnRpZmljYXRlJ3MgcHJvdGVjdGVkIGtleXN0b3JlIHRvIHRoZSBzZXJ2ZXJcIj48L2k+PC9sYWJlbD5cblx0XHRcdDwvZGl2PlxuXHRcdFx0PGRpdiBjbGFzcz1cImlubmVyLXdpZGdldCBhbGlnbi1zZWxmLWNlbnRlciBhbGlnbi1zZWxmLXNtLXN0YXJ0IGNvbC1hdXRvXCI+XG5cdFx0XHRcdDxidXR0b24gaWQ9XCJtYW5hZ2VTc2xCdXR0b25cIiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJrLWJ1dHRvbiBrLXByaW1hcnlcIiBkaXNhYmxlZD48aSBjbGFzcz1cImZhcyBmYS1jb2cgbXItMVwiPjwvaT5NYW5hZ2U8L2J1dHRvbj5cblx0XHRcdDwvZGl2PlxuXHRcdGApO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVycyB0byBNYW5hZ2UgYnV0dG9uIGNsaWNrXG5cdFx0JCgnI21hbmFnZVNzbEJ1dHRvbicsICQodGhpcykpLm9uKCdjbGljaycsICQucHJveHkodGhpcy5fb25NYW5hZ2VTc2xDbGljaywgdGhpcykpO1xuXHR9XG5cblx0ZGVzdHJveSgpXG5cdHtcblx0XHQvLyBSZW1vdmUgZXZlbnQgbGlzdGVuZXJcblx0XHQkKCcjbWFuYWdlU3NsQnV0dG9uJywgJCh0aGlzKSkub2ZmKCdjbGljaycpO1xuXG5cdFx0Ly8gSGlkZSBtb2RhbCAod2hpY2ggaW4gdHVybiBkZXN0cm95cyBpdClcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI3VwbG9hZE1vZGFsJywgJCh0aGlzKSk7XG5cblx0XHRpZiAobW9kYWxFbGVtZW50KVxuXHRcdFx0bW9kYWxFbGVtZW50Lm1vZGFsKCdoaWRlJyk7XG5cdH1cblxuXHRnZXQgZW5hYmxlZCgpXG5cdHtcblx0XHRyZXR1cm4gdGhpcy5faXNFbmFibGVkO1xuXHR9XG5cblx0c2V0IGVuYWJsZWQodmFsdWUpXG5cdHtcblx0XHR0aGlzLl9pc0VuYWJsZWQgPSB2YWx1ZTtcblxuXHRcdC8vIEVuYWJsZS9kaXNhYmxlIE1hbmFnZSBidXR0b25cblx0XHQkKCcjbWFuYWdlU3NsQnV0dG9uJywgJCh0aGlzKSkuYXR0cignZGlzYWJsZWQnLCAhdmFsdWUpO1xuXG5cdFx0Ly8gRW5hYmxlL2Rpc2FibGUgbW9kYWxcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI3VwbG9hZE1vZGFsJywgJCh0aGlzKSk7XG5cblx0XHRpZiAobW9kYWxFbGVtZW50KVxuXHRcdHtcblx0XHRcdC8vIERpc2FibGUgbW9kYWwgY2xvc2UgYnV0dG9uc1xuXHRcdFx0JCgnYnV0dG9uW2RhdGEtZGlzbWlzcz1cIm1vZGFsXCJdJywgbW9kYWxFbGVtZW50KS5hdHRyKCdkaXNhYmxlZCcsICF2YWx1ZSk7XG5cblx0XHRcdC8vIERpc2FibGUgdXBsb2FkIGJ1dHRvblxuXHRcdFx0JCgnI3VwbG9hZFNzbEJ1dHRvbicsIG1vZGFsRWxlbWVudCkuYXR0cignZGlzYWJsZWQnLCAhdmFsdWUpO1xuXG5cdFx0XHQvLyBEaXNhYmxlIGZpZWxkc2V0XG5cdFx0XHQkKCcjdXBsb2FkRmllbGRzZXQnLCBtb2RhbEVsZW1lbnQpLmF0dHIoJ2Rpc2FibGVkJywgIXZhbHVlKTtcblx0XHR9XG5cdH1cblxuXHRnZXQgdXBsb2FkVGFyZ2V0Q29uZmlnKClcblx0e1xuXHRcdHJldHVybiB0aGlzLl91cGxvYWRUYXJnZXRDb25maWc7XG5cdH1cblxuXHRzZXQgdXBsb2FkVGFyZ2V0Q29uZmlnKGRhdGEpXG5cdHtcblx0XHR0aGlzLl91cGxvYWRUYXJnZXRDb25maWcgPSBkYXRhO1xuXHR9XG5cblx0X29uTWFuYWdlU3NsQ2xpY2soKVxuXHR7XG5cdFx0Ly8gSW5pdGlhbGl6ZSBhbmQgc2hvdyBtb2RhbFxuXHRcdHRoaXMuX3Nob3dNb2RhbCgpO1xuXHR9XG5cblx0X29uVXBsb2FkU3NsQ2xpY2soKVxuXHR7XG5cdFx0aWYgKHRoaXMuX3ZhbGlkYXRlKCkpXG5cdFx0XHR0aGlzLl9zdGFydFNzbENlcnRVcGxvYWQoKTtcblx0fVxuXG5cdF9zaG93TW9kYWwoKVxuXHR7XG5cdFx0Ly8gQXBwZW5kIG1vZGFsIGh0bWxcblx0XHQkKHRoaXMpLmFwcGVuZCh0aGlzLl9tb2RhbEh0bWwpO1xuXG5cdFx0bGV0IG1vZGFsRWxlbWVudCA9ICQoJyN1cGxvYWRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0Ly8gSGlkZSBTU0wgY2VydGlmaWNhdGUgdXBsb2FkIHNwaW5uZXIgYW5kIGVycm9yIG1lc3NhZ2UgY29udGFpbmVyXG5cdFx0JCgnI3VwbG9hZFNwaW5uZXInLCBtb2RhbEVsZW1lbnQpLmhpZGUoKTtcblx0XHQkKCcjdXBsb2FkRXJyb3JNc2cnLCBtb2RhbEVsZW1lbnQpLmhpZGUoKTtcblx0XHQkKCcjdXBsb2FkRXJyb3JNc2cnLCBtb2RhbEVsZW1lbnQpLnRleHQoJycpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIFVwbG9hZCBidXR0b24gY2xpY2tcblx0XHQkKCcjdXBsb2FkU3NsQnV0dG9uJywgbW9kYWxFbGVtZW50KS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uVXBsb2FkU3NsQ2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIEFkZCBsaXN0ZW5lciB0byBtb2RhbCBoaWRlIGV2ZW50XG5cdFx0bW9kYWxFbGVtZW50Lm9uKCdoaWRkZW4uYnMubW9kYWwnLCAkLnByb3h5KHRoaXMuX2Rlc3Ryb3lNb2RhbCwgdGhpcykpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBrZW5kbyB1cGxvYWRlclxuXHRcdHRoaXMuX3VwbG9hZFdpZGdldCA9IG1vZGFsRWxlbWVudC5maW5kKCcjdXBsb2FkZXInKS5rZW5kb1VwbG9hZCh7XG5cdFx0XHRhbGxvd2VkRXh0ZW5zaW9uczogWycuamtzJ10sXG5cdFx0XHRtdWx0aXBsZTogZmFsc2UsXG5cdFx0XHR0ZW1wbGF0ZTogZnVuY3Rpb24oZGF0YUl0ZW0pIHtcblx0XHRcdFx0ZGF0YUl0ZW0uYnl0ZXNUb1NpemUgPSBieXRlc1RvU2l6ZTsgLy8gUGFzcyBieXRlc1RvU2l6ZSB1dGlsaXR5IGZ1bmN0aW9uIHRvIHRlbXBsYXRlXG5cdFx0XHRcdHJldHVybiBrZW5kby50ZW1wbGF0ZShgXG5cdFx0XHRcdFx0PHNwYW4gY2xhc3M9J2stcHJvZ3Jlc3Mgdy0xMDAnPjwvc3Bhbj5cblx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cIlwiPlxuXHRcdFx0XHRcdFx0PHNwYW4gY2xhc3M9XCJrLWZpbGUtbmFtZVwiIHRpdGxlPVwiIz1uYW1lI1wiPiM9bmFtZSM8L3NwYW4+XG5cdFx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cImstZmlsZS1zaXplXCI+U2l6ZTogIz1ieXRlc1RvU2l6ZShzaXplLCAxLCAnQnl0ZXMnKSM8L3NwYW4+XG5cdFx0XHRcdFx0PC9zcGFuPlxuXHRcdFx0XHRgKShkYXRhSXRlbSk7XG5cdFx0XHR9LFxuXHQgICAgICAgIGxvY2FsaXphdGlvbjoge1xuXHQgICAgICAgICAgICBzZWxlY3Q6ICdTZWxlY3QgZmlsZS4uLidcblx0ICAgICAgICB9XG4gICAgICAgIH0pLmRhdGEoJ2tlbmRvVXBsb2FkJyk7XG5cblx0XHQvLyBJbml0aWFsaXplIGtlbmRvIHZhbGlkYXRpb24gb24gdXBsb2FkZXIgc3ViZm9ybVxuXHRcdC8vIE5PVEU6IHdlIHVzZSBzZXBhcmF0ZSB2YWxpZGF0b3JzIHRvIGJlIGFibGUgdG8gZGlzYWJsZSAndmFsaWRhdGVPbkJsdXInIG9uIHRoZSB1cGxvYWRlcixcblx0XHQvLyBiZWNhdXNlIGl0IGNhdXNlcyB0aGUgZXJyb3IgbWVzc2FnZSB0byBhcHBlYXIgYXMgc29vbiBhcyB0aGUgXCJTZWxlY3QgZmlsZVwiIGJ1dHRvbiBpcyBjbGlja2VkXG5cdFx0dGhpcy5fdmFsaWRhdG9yMSA9IG1vZGFsRWxlbWVudC5maW5kKCcjdXBsb2FkZXJTdWJmb3JtJykua2VuZG9WYWxpZGF0b3Ioe1xuXHRcdFx0dmFsaWRhdGVPbkJsdXI6IGZhbHNlLFxuXHRcdFx0cnVsZXM6IHtcblx0XHRcdFx0dXBsb2FkOiBmdW5jdGlvbihpbnB1dCkge1xuXHRcdFx0XHRcdGxldCB2YWxpZCA9IGZhbHNlO1xuXHRcdFx0XHRcdGlmIChpbnB1dC5pcygnW3R5cGU9ZmlsZV0nKSlcblx0XHRcdFx0XHRcdHZhbGlkID0gaW5wdXQuY2xvc2VzdCgnLmstdXBsb2FkJykuZmluZCgnLmstZmlsZScpLmxlbmd0aCA+IDA7XG5cblx0XHRcdFx0XHRyZXR1cm4gdmFsaWQ7XG5cdCAgICAgICAgICAgIH1cblx0XHRcdH1cblx0XHR9KS5kYXRhKCdrZW5kb1ZhbGlkYXRvcicpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBrZW5kbyB2YWxpZGF0aW9uIG9uIHBhc3N3b3JkcyBzdWJmb3JtXG5cdFx0dGhpcy5fdmFsaWRhdG9yMiA9IG1vZGFsRWxlbWVudC5maW5kKCcjcGFzc3dvcmRzU3ViZm9ybScpLmtlbmRvVmFsaWRhdG9yKHtcblx0XHRcdHZhbGlkYXRlT25CbHVyOiB0cnVlLFxuXHRcdFx0cnVsZXM6IHtcblx0XHRcdFx0dmVyaWZ5S3NQYXNzd29yZDogJC5wcm94eShmdW5jdGlvbihpbnB1dCkge1xuXHRcdFx0XHRcdGxldCB2YWxpZCA9IHRydWU7XG5cdFx0XHRcdFx0aWYgKGlucHV0LmlzKCdbbmFtZT1jb25maXJtS3NQYXNzd29yZF0nKSlcblx0XHRcdFx0XHRcdHZhbGlkID0gaW5wdXQudmFsKCkgPT09ICQodGhpcykuZmluZCgnI2tzUGFzc3dvcmQnKS52YWwoKTtcblx0XHRcdFx0XHRyZXR1cm4gdmFsaWQ7XG5cdFx0XHRcdH0sIHRoaXMpLFxuXHRcdFx0XHR2ZXJpZnlBZG1QYXNzd29yZDogJC5wcm94eShmdW5jdGlvbihpbnB1dCkge1xuXHRcdFx0XHRcdGxldCB2YWxpZCA9IHRydWU7XG5cdFx0XHRcdFx0aWYgKGlucHV0LmlzKCdbbmFtZT1jb25maXJtQWRtaW5QYXNzd29yZF0nKSlcblx0XHRcdFx0XHRcdHZhbGlkID0gaW5wdXQudmFsKCkgPT09ICQodGhpcykuZmluZCgnI2FkbWluUGFzc3dvcmQnKS52YWwoKTtcblx0XHRcdFx0XHRyZXR1cm4gdmFsaWQ7XG5cdFx0XHRcdH0sIHRoaXMpXG5cdFx0XHR9LFxuXHRcdFx0bWVzc2FnZXM6IHtcblx0XHRcdFx0dmVyaWZ5S3NQYXNzd29yZDogJ1Bhc3N3b3JkIG5vdCBtYXRjaGluZycsXG5cdFx0XHRcdHZlcmlmeUFkbVBhc3N3b3JkOiAnUGFzc3dvcmQgbm90IG1hdGNoaW5nJyxcblx0XHRcdH1cblx0XHR9KS5kYXRhKCdrZW5kb1ZhbGlkYXRvcicpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBib290c3RyYXAgbW9kYWxcblx0XHRtb2RhbEVsZW1lbnQubW9kYWwoe1xuXHRcdFx0YmFja2Ryb3A6ICdzdGF0aWMnLFxuXHRcdFx0a2V5Ym9hcmQ6IGZhbHNlLFxuXHRcdH0pO1xuXHR9XG5cblx0X2Rlc3Ryb3lNb2RhbCgpXG5cdHtcblx0XHRsZXQgbW9kYWxFbGVtZW50ID0gJCgnI3VwbG9hZE1vZGFsJywgJCh0aGlzKSk7XG5cblx0XHRpZiAobW9kYWxFbGVtZW50KVxuXHRcdHtcblx0XHRcdC8vIFJlbW92ZSBsaXN0ZW5lcnNcblx0XHRcdCQoJyN1cGxvYWRTc2xCdXR0b24nLCBtb2RhbEVsZW1lbnQpLm9mZignY2xpY2snKTtcblx0XHRcdG1vZGFsRWxlbWVudC5vZmYoJ2hpZGRlbi5icy5tb2RhbCcpO1xuXG5cdFx0XHQvLyBEZXN0cm95IGV2ZXJ5dGhpbmcgS2VuZG9cblx0XHRcdGtlbmRvLmRlc3Ryb3kobW9kYWxFbGVtZW50KTtcblxuXHRcdFx0Ly8gRGlzcG9zZSBtb2RhbFxuXHRcdFx0bW9kYWxFbGVtZW50Lm1vZGFsKCdkaXNwb3NlJyk7XG5cblx0XHRcdC8vIFJlbW92ZSBodG1sXG5cdFx0XHRtb2RhbEVsZW1lbnQucmVtb3ZlKCk7XG5cdFx0XHRtb2RhbEVsZW1lbnQgPSBudWxsO1xuXHRcdH1cblx0fVxuXG5cdF92YWxpZGF0ZSgpXG5cdHtcblx0XHRsZXQgdmFsMSA9IHRoaXMuX3ZhbGlkYXRvcjEudmFsaWRhdGUoKTtcblx0XHRsZXQgdmFsMiA9IHRoaXMuX3ZhbGlkYXRvcjIudmFsaWRhdGUoKTtcblxuXHRcdHJldHVybiB2YWwxICYmIHZhbDI7XG5cdH1cblxuXHRfc3RhcnRTc2xDZXJ0VXBsb2FkKClcblx0e1xuXHRcdGlmICghdGhpcy5fdXBsb2FkVGFyZ2V0Q29uZmlnKVxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdVcGxvYWQgdGFyZ2V0IGNvbmZpZ3VyYXRpb24gbm90IHNldCcpO1xuXG5cdFx0bGV0IG1vZGFsRWxlbWVudCA9ICQoJyN1cGxvYWRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0aWYgKG1vZGFsRWxlbWVudClcblx0XHR7XG5cdFx0XHRsZXQgY2VydERhdGEgPSB7fTtcblx0XHRcdGNlcnREYXRhLmZpbGUgPSB0aGlzLl91cGxvYWRXaWRnZXQuZ2V0RmlsZXMoKVswXTtcblx0XHRcdGNlcnREYXRhLmtzUGFzc3dvcmQgPSAkKCcja3NQYXNzd29yZCcsIG1vZGFsRWxlbWVudCkudmFsKCk7XG5cdFx0XHRjZXJ0RGF0YS5hZG1pblBhc3N3b3JkID0gJCgnI2FkbWluUGFzc3dvcmQnLCBtb2RhbEVsZW1lbnQpLnZhbCgpO1xuXG5cdFx0XHQvLyBEaXNhYmxlIG1vZGFsXG5cdFx0XHR0aGlzLmVuYWJsZWQgPSBmYWxzZTtcblxuXHRcdFx0Ly8gSGlkZSBwcmV2aW91cyBlcnJvciBhbmQgc2hvdyBzcGlubmVyXG5cdFx0XHQkKCcjdXBsb2FkU3Bpbm5lcicsIG1vZGFsRWxlbWVudCkuc2hvdygpO1xuXHRcdFx0JCgnI3VwbG9hZEVycm9yTXNnJywgbW9kYWxFbGVtZW50KS5oaWRlKCk7XG5cdFx0XHQkKCcjdXBsb2FkRXJyb3JNc2cnLCBtb2RhbEVsZW1lbnQpLnRleHQoJycpO1xuXG5cdFx0XHQvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cblx0XHRcdC8vIEdlbmVyYXRlIEluaXQgVmVjdG9yXG5cdFx0XHRsZXQgcm5nVmFsdWVzID0gW107XG5cdFx0XHRmb3IgKGxldCBpID0gMDsgaSA8IDE2OyBpKyspXG5cdFx0XHRcdHJuZ1ZhbHVlcy5wdXNoKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDI1NikpO1xuXG5cdFx0XHRsZXQgaXYgPSBuZXcgVWludDhBcnJheShybmdWYWx1ZXMpO1xuXG5cdFx0XHQvLyBHZW5lcmF0ZSBzZWNyZXQga2V5IGJ5IE1ENS1lbmNvZGluZyBhZG1pbiBwYXNzd29yZCArIHNlc3Npb24gdG9rZW5cblx0XHRcdGxldCBtZDVQYXNzID0gdGhpcy5fYmluYXJ5TUQ1KGNlcnREYXRhLmFkbWluUGFzc3dvcmQgKyB0aGlzLl91cGxvYWRUYXJnZXRDb25maWcuc2Vzc2lvblRva2VuKTtcblxuXHRcdFx0Ly8gRW5jcnlwdCBrZXlzdG9yZSBwYXNzd29yZCB2aWEgQUVTICgxMjhiaXQpXG5cdFx0XHRsZXQgZW5jcnlwdGVkUHdkID0gdGhpcy5fYWVzRW5jcnlwdChjZXJ0RGF0YS5rc1Bhc3N3b3JkLCBtZDVQYXNzLCBpdik7XG5cblx0XHRcdC8vIEVuY29kZSBJViB1c2luZyBCYXNlNjRcblx0XHRcdGxldCBlbmNvZGVkSXYgPSB0aGlzLl9iNjRFbmNvZGUoaXYpO1xuXG5cdFx0XHQvLyBFbmNvZGUgZW5jcnlwdGVkIHBhc3N3b3JkIHVzaW5nIEJhc2U2NFxuXHRcdFx0bGV0IGVuY29kZWRQd2QgPSB0aGlzLl9iNjRFbmNvZGUoZW5jcnlwdGVkUHdkKTtcblxuXHRcdFx0Ly89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5cdFx0XHQvLyBTZXQgcGFyYW1ldGVycyB0byBiZSBzZW50IHdpdGggdGhlIGNlcnRpZmljYXRlIGtleXN0b3JlIGZpbGVcblx0XHRcdGNvbnN0IHBhcmFtcyA9IG5ldyBGb3JtRGF0YSgpO1xuXHRcdFx0cGFyYW1zLmFwcGVuZCgnZmlsZXNbXScsIGNlcnREYXRhLmZpbGUucmF3RmlsZSk7XG5cdFx0XHRwYXJhbXMuYXBwZW5kKCdfX2l2JywgZW5jb2RlZEl2KTtcblx0XHRcdHBhcmFtcy5hcHBlbmQoJ19fcGFzc3dvcmQnLCBlbmNvZGVkUHdkKTtcblx0XHRcdHBhcmFtcy5hcHBlbmQoJ19fbW9kdWxlJywgdGhpcy5fdXBsb2FkVGFyZ2V0Q29uZmlnLm1vZHVsZUlkKTtcblxuXHRcdFx0Ly8gU2V0IGRlc3RpbmF0aW9uIHVybFxuXHRcdFx0Y29uc3QgdXJsID0gdGhpcy5fdXBsb2FkVGFyZ2V0Q29uZmlnLnByb3RvY29sICsgJzovLycgKyB0aGlzLl91cGxvYWRUYXJnZXRDb25maWcuaG9zdCArICc6JyArIHRoaXMuX3VwbG9hZFRhcmdldENvbmZpZy5wb3J0ICsgJy9CbHVlQm94L1NGUzJYRmlsZVVwbG9hZD9zZXNzSGFzaElkPScgKyB0aGlzLl91cGxvYWRUYXJnZXRDb25maWcuc2Vzc2lvblRva2VuO1xuXG5cdFx0XHQvLyBTdGFydCB1cGxvYWRcblx0XHRcdGZldGNoKHVybCwge1xuXHRcdFx0XHRtZXRob2Q6ICdQT1NUJyxcblx0XHRcdFx0Ym9keTogcGFyYW1zLFxuXHRcdFx0XHRtb2RlOiAnbm8tY29ycydcblx0XHRcdH0pLnRoZW4oJC5wcm94eSh0aGlzLl9vblNzbENlcnRVcGxvYWRFbmQsIHRoaXMpKTtcblx0XHR9XG5cdH1cblxuXHRfb25Tc2xDZXJ0VXBsb2FkRW5kKHJlc3BvbnNlKVxuXHR7XG5cdFx0Ly8gTm90aGluZyB0byBkbzogd2UgaGF2ZSB0byB3YWl0IHRoZSB1cGxvYWQgcHJvY2VzcyBjb21wbGV0aW9uIHRvIGJlIHNpZ25hbGVkIGJ5IHRoZSBzZXJ2ZXIgdGhyb3VnaCB0aGUgZGVkaWNhdGVkIEV4dGVuc2lvbiByZXNwb25zZVxuXG5cdFx0Ly89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5cdFx0Ly8gVE9ETyBTaG91bGQgd2UgaGFuZGxlIHRoaXMgcmVzcG9uc2UgaW4gc29tZSB3YXk/IEZvciBzb21lIHVua25vd24gcmVhc29uIHdlIGFsd2F5cyBnZXQgb2s9ZmFsc2UgYW5kIHN0YXR1cz0wXG5cdFx0Ly9jb25zb2xlLmxvZyhyZXNwb25zZSlcblx0XHQvL2NvbnNvbGUubG9nKHJlc3BvbnNlLm9rKVxuXHRcdC8vY29uc29sZS5sb2cocmVzcG9uc2Uuc3RhdHVzKVxuXHR9XG5cblx0LyoqXG5cdCAqIE1ldGhvZCBjYWxsZWQgYnkgcGFyZW50IHdoZW4gdXBsb2FkIGZhaWxlZC5cblx0ICovXG5cdG9uU3NsQ2VydFVwbG9hZEVycm9yKGVycm9yKVxuXHR7XG5cdFx0bGV0IG1vZGFsRWxlbWVudCA9ICQoJyN1cGxvYWRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0aWYgKG1vZGFsRWxlbWVudClcblx0XHR7XG5cdFx0XHQvLyBFbmFibGUgbW9kYWxcblx0XHRcdHRoaXMuZW5hYmxlZCA9IHRydWU7XG5cblx0XHRcdC8vIFNob3cgdXBsb2FkIGVycm9yXG5cdFx0XHQkKCcjdXBsb2FkRXJyb3JNc2cnLCBtb2RhbEVsZW1lbnQpLnNob3coKTtcblx0XHRcdCQoJyN1cGxvYWRFcnJvck1zZycsIG1vZGFsRWxlbWVudCkudGV4dChlcnJvciArICcuJyk7XG5cblx0XHRcdC8vIEhpZGUgc3Bpbm5lclxuXHRcdFx0JCgnI3VwbG9hZFNwaW5uZXInLCBtb2RhbEVsZW1lbnQpLmhpZGUoKTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogTWV0aG9kIGNhbGxlZCBieSBwYXJlbnQgd2hlbiB1cGxvYWQgaXMgY29tcGxldGVkIHN1Y2Nlc3NmdWxseS5cblx0ICovXG5cdG9uU3NsQ2VydFVwbG9hZFN1Y2Nlc3MoKVxuXHR7XG5cdFx0bGV0IG1vZGFsRWxlbWVudCA9ICQoJyN1cGxvYWRNb2RhbCcsICQodGhpcykpO1xuXG5cdFx0aWYgKG1vZGFsRWxlbWVudClcblx0XHR7XG5cdFx0XHQvLyBFbmFibGUgbW9kYWxcblx0XHRcdHRoaXMuZW5hYmxlZCA9IHRydWU7XG5cblx0XHRcdC8vIEhpZGUgc3Bpbm5lclxuXHRcdFx0JCgnI3VwbG9hZFNwaW5uZXInLCBtb2RhbEVsZW1lbnQpLmhpZGUoKTtcblxuXHRcdFx0Ly8gSGlkZSBtb2RhbFxuXHRcdFx0bW9kYWxFbGVtZW50Lm1vZGFsKCdoaWRlJyk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuXHQvKlxuXHQgKiBUYWtlcyBhIHN0cmluZyBhbmQgcmV0dXJucyB0aGUgTUQ1IGFzIFVpbnQ4QXJyYXlcblx0ICovXG5cdF9iaW5hcnlNRDUoc3RyKVxuXHR7XG5cdFx0bGV0IE1ENSA9IG5ldyBTRlMyWC5NRDUoKTtcblx0XHRsZXQgaGV4U3RyID0gTUQ1LmhleF9tZDUoc3RyKTtcblxuXHRcdHJldHVybiB0aGlzLl9oZXhCeXRlU3RyaW5nVG9CeXRlQXJyYXkoaGV4U3RyKTtcblx0fVxuXG5cdC8qXG5cdCAqIEhleCBieXRlcyAtLS0+IEFjdHVhbCBieXRlW10gYXMgVWludDhBcnJheVxuXHQgKi9cblx0X2hleEJ5dGVTdHJpbmdUb0J5dGVBcnJheShoZXhCeXRlU3RyaW5nKVxuXHR7XG5cdCAgICBsZXQgYnl0ZXMgPSBuZXcgVWludDhBcnJheSgxNik7IC8vIE1ENSBmaXhlZCBvdXRwdXQgc2l6ZVxuXG5cdCAgICBmb3IgKGxldCBpID0gMDsgaSA8IGhleEJ5dGVTdHJpbmcubGVuZ3RoOylcblx0ICAgIHtcblx0ICAgICAgICBsZXQgaGV4Qnl0ZSA9IGhleEJ5dGVTdHJpbmdbaSsrXSArIGhleEJ5dGVTdHJpbmdbaSsrXTtcblx0ICAgICAgICBsZXQgYnl0ZSA9IHBhcnNlSW50KGhleEJ5dGUsIDE2KTtcblxuXHQgICAgICAgIGJ5dGVzW2kgLyAyIC0gMV0gPSBieXRlO1xuXHQgICAgfVxuXG5cdCAgICByZXR1cm4gYnl0ZXM7XG5cdH1cblxuXHQvKlxuXHQgKiBFbmNyeXB0IHVzaW5nIEFFUywgbW9kZSBDQkMsIFBLQ1MjNyBwYWRkaW5nXG5cdCAqXG5cdCAqIHRleHQgXHRcdC0+IHRoZSB0ZXh0IHdlIHdhbnQgdG8gZW5jb2RlXG5cdCAqIGtleSBcdFx0LT4gdGhlIEFFUyBrZXlcblx0ICogaXYgIFx0XHQtPiB0aGUgQUVTL0NCQyBpbml0IHZlY3RvclxuXHQgKlxuXHQgKiBSZXR1cm5zIFx0LT4gVWludDhBcnJheVxuXHQgKi9cblx0X2Flc0VuY3J5cHQodGV4dCwga2V5LCBpdilcblx0e1xuXHRcdGxldCB0ZXh0Qnl0ZXMgPSBhZXNqcy51dGlscy51dGY4LnRvQnl0ZXModGV4dCk7IFx0XHQvLyBHZXQgVVRGLTggYnl0ZXNcblx0XHRsZXQgYWVzQ0JDID0gbmV3IGFlc2pzLk1vZGVPZk9wZXJhdGlvbi5jYmMoa2V5LCBpdik7XHQvLyBJbml0IENCQyBtb2RlXG5cdFx0dGV4dEJ5dGVzID0gYWVzanMucGFkZGluZy5wa2NzNy5wYWQodGV4dEJ5dGVzKTsgXHRcdC8vIFBLQ1MjNyBwYWRkaW5nXG5cblx0XHQvLyBFbmNyeXB0XG5cdFx0cmV0dXJuIGFlc0NCQy5lbmNyeXB0KHRleHRCeXRlcyk7XG5cdH1cblxuXHQvKlxuXHQgKiBFbmNvZGUgcGFzc2VkIGJ5dGUgYXJyYXkgLS0+IEJhc2U2NCByZXByZXNlbnRhdGlvblxuXHQgKiBSZXR1cm5zIC0tPiBzdHJpbmdcblx0ICovXG5cdF9iNjRFbmNvZGUoYmFycmF5KVxuXHR7XG5cdFx0cmV0dXJuIGJ0b2EoU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBiYXJyYXkpKTtcblx0fVxufVxuXG4vLyBERUZJTkUgQ09NUE9ORU5UXG5pZiAoIXdpbmRvdy5jdXN0b21FbGVtZW50cy5nZXQoJ3NzbC1jZXJ0aWZpY2F0ZS1tYW5hZ2VyJykpXG5cdHdpbmRvdy5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3NzbC1jZXJ0aWZpY2F0ZS1tYW5hZ2VyJywgU3NsQ2VydGlmaWNhdGVNYW5hZ2VyKTtcbiIsImltcG9ydCB7QmFzZU1vZHVsZX0gZnJvbSAnLi9iYXNlLW1vZHVsZSc7XG5pbXBvcnQge0NvbmZpZ0ludGVyZmFjZUJ1aWxkZXJ9IGZyb20gJy4uL3V0aWxzL3VpYnVpbGRlci9jb25maWctaW50ZXJmYWNlLWJ1aWxkZXInO1xuaW1wb3J0IHtTc2xDZXJ0aWZpY2F0ZU1hbmFnZXJ9IGZyb20gJy4uL2NvbXBvbmVudHMvbW9kdWxlLXNwZWNpZmljL3NzbC1jZXJ0aWZpY2F0ZS1tYW5hZ2VyJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2VydmVyQ29uZmlndXJhdG9yIGV4dGVuZHMgQmFzZU1vZHVsZVxue1xuXHRjb25zdHJ1Y3RvcigpXG5cdHtcblx0ICAgIHN1cGVyKCdzZXJ2ZXJDb25maWcnKTtcblxuXHRcdC8vIE91dGdvaW5nIHJlcXVlc3RzXG5cdFx0dGhpcy5SRVFfSU5JVCA9ICdpbml0Jztcblx0XHR0aGlzLlJFUV9HRVRfQ09ORklHID0gJ2dldENvbmZpZyc7XG5cdFx0dGhpcy5SRVFfVVBEQVRFX0NPTkZJRyA9ICd1cGRDb25maWcnO1xuXHRcdHRoaXMuUkVRX1VQREFURV9HRU9fREIgPSAndXBkR2VvRGInO1xuXG5cdFx0Ly8gSW5jb21pbmcgcmVzcG9uc2VzXG5cdFx0dGhpcy5SRVNQX0lOSVQgPSAnaW5pdCc7XG5cdFx0dGhpcy5SRVNQX0NPTkZJRyA9ICdjb25maWcnO1xuXHRcdHRoaXMuUkVTUF9DT05GSUdfVVBEQVRFX0NPTkZJUk0gPSAnY29uZmlnVXBkYXRlJztcblx0XHR0aGlzLlJFU1BfQ09ORklHX0NIQU5HRURfQUxFUlQgPSAnY29uZmlnQWxlcnQnO1xuXHRcdHRoaXMuUkVTUF9TU0xfVVBMT0FEX0VSUk9SID0gJ3NzbFVwbG9hZEVycm9yJztcblx0XHR0aGlzLlJFU1BfU1NMX1VQTE9BRF9DT05GSVJNID0gJ3NzbFVwbG9hZCc7XG5cdFx0dGhpcy5SRVNQX1VQREFURV9HRU9fREIgPSAnZ2VvRGJVcGRhdGUnO1xuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gQ09NTU9OIE1PRFVMRSBJTlRFUkZBQ0UgTUVUSE9EU1xuXHQvLyBUaGlzIG1lbWJlcnMgYXJlIHVzZWQgYnkgdGhlIG1haW4gY29udHJvbGxlclxuXHQvLyB0byBjb21tdW5pY2F0ZSB3aXRoIHRoZSBtb2R1bGUncyBjb250cm9sbGVyLlxuXHQvLyBUaGlzIG1ldGhvZHMgb3ZlcnJpZGUgdGhvc2UgaW4gQmFzZU1vZHVsZSBjbGFzcy5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRpbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKVxuXHR7XG5cdFx0Ly8gQ2FsbCBzdXBlciBtZXRob2Rcblx0XHRzdXBlci5pbml0aWFsaXplKGlkRGF0YSwgc2hlbGxDb250cm9sbGVyKTtcblxuXHRcdC8vIEluaXRpYWxpemUgcHJvZ3Jlc3MgYmFyXG5cdFx0JCgnI3NyYy1wcm9ncmVzc0JhcicpLmtlbmRvUHJvZ3Jlc3NCYXIoe1xuXHRcdFx0bWluOiAwLFxuICAgICAgICAgICAgbWF4OiAxMDAsXG5cdFx0XHR2YWx1ZTogZmFsc2UsXG4gICAgICAgICAgICB0eXBlOiAndmFsdWUnLFxuICAgICAgICAgICAgYW5pbWF0aW9uOiB7XG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDQwMFxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuXHRcdC8vIENyZWF0ZSBpbnRlcmZhY2UgYnVpbGRlciBpbnN0YW5jZVxuXHRcdHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIgPSBuZXcgQ29uZmlnSW50ZXJmYWNlQnVpbGRlcigpO1xuXG5cdFx0Ly8gQWRkIGxpc3RlbmVyIHRvIEdlb0xpdGUyIGRhdGFiYXNlIHVwZGF0ZSBidXR0b25cblx0XHQkKCcjc3JjLXVwZGF0ZUdlb2xvY0RiQnV0dG9uJykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblVwZGF0ZUdlb2xvY0RiQ2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIEFkZCBsaXN0ZW5lciB0byBpbnRlcmZhY2UgYnV0dG9uc1xuXHRcdCQoJyNzcmMtcmVsb2FkQnV0dG9uJykub24oJ2NsaWNrJywgJC5wcm94eSh0aGlzLl9vblJlbG9hZENsaWNrLCB0aGlzKSk7XG5cdFx0JCgnI3NyYy1zdWJtaXRCdXR0b24nKS5vbignY2xpY2snLCAkLnByb3h5KHRoaXMuX29uU3VibWl0Q2xpY2ssIHRoaXMpKTtcblxuXHRcdC8vIFNhdmUgcmVmIHRvIFNTTCBNYW5hZ2VyXG5cdFx0dGhpcy5fc3NsQ2VydE1hbmFnZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc3JjLXNzbENlcnRNYW5hZ2VyJyk7XG5cblx0XHQvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuXHRcdC8vIFNlbmQgaW5pdGlhbGl6YXRpb24gcmVxdWVzdFxuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfSU5JVCk7XG5cdH1cblxuXHRkZXN0cm95KClcblx0e1xuXHRcdC8vIENhbGwgc3VwZXIgbWV0aG9kXG5cdFx0c3VwZXIuZGVzdHJveSgpO1xuXG5cdFx0Ly8gRGVzdHJveSBTU0wgQ2VydGlmaWNhdGUgTWFuYWdlclxuXHRcdHRoaXMuX3NzbENlcnRNYW5hZ2VyLmRlc3Ryb3koKTtcblxuXHRcdC8vIFJlbW92ZSBpbnRlcmZhY2UgYnV0dG9ucyBjbGljayBsaXN0ZW5lcnNcblx0XHQkKCcjc3JjLXVwZGF0ZUdlb2xvY0RiQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyNzcmMtcmVsb2FkQnV0dG9uJykub2ZmKCdjbGljaycpO1xuXHRcdCQoJyNzcmMtc3VibWl0QnV0dG9uJykub2ZmKCdjbGljaycpO1xuXG5cdFx0Ly8gQ2xlYXIgdGFicyBjb250YWluZXJcblx0XHR0aGlzLl9jbGVhclRhYnMoKTtcblx0fVxuXG5cdG9uRXh0ZW5zaW9uQ29tbWFuZChjb21tYW5kLCBkYXRhKVxuXHR7XG5cdFx0Ly8gSW5pdGlhbGl6YXRpb24gZGF0YSByZWNlaXZlZFxuXHRcdGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9JTklUKVxuXHRcdHtcblx0XHRcdC8vIFJldHJpZXZlIG1vZHVsZSBpZCBzZW50IGJ5IHRoZSBzZXJ2ZXIgKHJlcXVpcmVkIGJlY2F1c2UgbXVsdGlwbGUgbW9kdWxlcyB1c2UgZmlsZSB1cGxvYWRpbmcgc2VydmljZSlcblx0XHRcdGNvbnN0IHVwbG9hZE1vZHVsZUlkID0gZGF0YS5nZXRVdGZTdHJpbmcoJ21vZElkJyk7XG5cblx0XHRcdC8vIFNldCBTU0wgdXBsb2FkIG1hbmFnZXIgdGFyZ2V0IGNvbmZpZ3VyYXRpb25cblx0XHRcdHRoaXMuX3NzbENlcnRNYW5hZ2VyLnVwbG9hZFRhcmdldENvbmZpZyA9IHtcblx0XHRcdFx0c2Vzc2lvblRva2VuOiB0aGlzLnNtYXJ0Rm94LnNlc3Npb25Ub2tlbixcblx0XHRcdFx0aG9zdDogdGhpcy5zbWFydEZveC5jb25maWcuaG9zdCxcblx0XHRcdFx0cG9ydDogdGhpcy5zbWFydEZveC5jb25maWcucG9ydCxcblx0XHRcdFx0bW9kdWxlSWQ6IHVwbG9hZE1vZHVsZUlkLFxuXHRcdFx0XHRwcm90b2NvbDogdGhpcy5zbWFydEZveC5jb25maWcudXNlU1NMID8gJ2h0dHBzJyA6ICdodHRwJ1xuXHRcdFx0fTtcblxuXHRcdFx0Ly8gU2VydmVyIHNlbmRzIGEgZmxhZyBpbmRpY2F0aW5nIGlmIGZpbGUgdXBsb2FkcyBhcmUgbG9ja2VkXG5cdFx0XHQvLyBXZSB1c2UgaXQgdG8gZW5hYmxlIHRoZSBcIk1hbmFnZSBTU0wgY2VydGlmaWNhdGVcIiBidXR0b25cblx0XHRcdHRoaXMuX3NzbExvY2tlZCA9IGRhdGEuZ2V0Qm9vbCgnbG9jaycpO1xuXG5cdFx0XHRpZiAoIXRoaXMuX3NzbExvY2tlZClcblx0XHRcdFx0JCgnI3NyYy1tYW5hZ2VTc2xXYXJuJykuaGlkZSgpO1xuXG5cdFx0XHQvLyBSZXF1ZXN0IGNvbmZpZ3VyYXRpb24gZGF0YSB0byBzZXJ2ZXIgaW5zdGFuY2Vcblx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfR0VUX0NPTkZJRyk7XG5cdFx0fVxuXG5cdFx0Ly8gU2VydmVyIGNvbmZpZ3VyYXRpb24gZGF0YSByZWNlaXZlZFxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX0NPTkZJRylcblx0XHR7XG5cdFx0XHQvLyBCdWlsZCB1c2VyIGludGVyZmFjZSBiYXNlZCBvbiByZWNlaXZlZCBkYXRhXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmJ1aWxkSW50ZXJmYWNlKGRhdGEuZ2V0U0ZTQXJyYXkoJ3NldHRpbmdzJyksICdzcmMtdGFiTmF2aWdhdG9yJywgZmFsc2UpO1xuXG5cdFx0XHQvLyBFbmFibGUgYnV0dG9uc1xuXHRcdFx0dGhpcy5fZW5hYmxlQnV0dG9ucyh0cnVlKTtcblxuXHRcdFx0Ly8gSW5pdGlhbGl6ZSBUYWJOYXZpZ2F0b3ItcmFsYXRlZCB3aWRnZXRzXG5cdFx0XHRpZiAoIXRoaXMuX3RhYk5hdkluaXRpYWxpemVkKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBFbmFibGUgc2Nyb2xsaW5nIHRhYnNcblx0XHRcdFx0JCgnI3NyYy10YWJOYXZpZ2F0b3IgPiAjdGFicycpLnNjcm9sbGluZ1RhYnMoe1xuXHRcdFx0XHRcdGJvb3RzdHJhcFZlcnNpb246IDQsXG5cdFx0XHRcdFx0c2Nyb2xsVG9UYWJFZGdlOiB0cnVlLFxuXHRcdFx0XHRcdGVuYWJsZVN3aXBpbmc6IHRydWUsXG5cdFx0XHRcdFx0ZGlzYWJsZVNjcm9sbEFycm93c09uRnVsbHlTY3JvbGxlZDogdHJ1ZSxcblx0XHRcdFx0XHRjc3NDbGFzc0xlZnRBcnJvdzogJ2ZhIGZhLWNoZXZyb24tbGVmdCcsXG5cdFx0XHRcdFx0Y3NzQ2xhc3NSaWdodEFycm93OiAnZmEgZmEtY2hldnJvbi1yaWdodCdcblx0XHRcdFx0fSk7XG5cblx0XHRcdFx0dGhpcy5fdGFiTmF2SW5pdGlhbGl6ZWQgPSB0cnVlO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBSdW4gdmFsaWRhdGlvbiAodG8gcmVtb3ZlIHZhbGlkYXRpb24gbWVzc2FnZXMgaWYgZGF0YSB3YXMgcmVsb2FkZWQpXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmNoZWNrSXNWYWxpZCgpO1xuXG5cdFx0XHR0aGlzLl9zd2l0Y2hWaWV3KCdzcmMtbWFpbicpO1xuXHRcdH1cblxuXHRcdC8vIFNlcnZlciBjb25maWd1cmF0aW9uIHVwZGF0ZSBjb25maXJtYXRpb25cblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9DT05GSUdfVVBEQVRFX0NPTkZJUk0pXG5cdFx0e1xuXHRcdFx0Ly8gRW5hYmxlIGJ1dHRvbnNcblx0XHRcdHRoaXMuX2VuYWJsZUJ1dHRvbnModHJ1ZSk7XG5cblx0XHRcdC8vIEVuYWJsZSBmb3JtIGl0ZW1zXG5cdFx0XHR0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmRpc2FibGVJbnRlcmZhY2UoZmFsc2UpO1xuXG5cdFx0XHQvLyBJZiB0aGUgY3VycmVudCB1c2VyIGlzIHRoZSB1cGRhdGVyLCBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHQvLyBPdGhlcndpc2UsIHNob3cgYSBkaWFsb2cgYm94IHN1Z2dlc3RpbmcgdG8gcmVsb2FkXG5cdFx0XHRsZXQgdXBkYXRlciA9IGRhdGEuZ2V0VXRmU3RyaW5nKCd1c2VyJyk7XG5cblx0XHRcdGlmICh1cGRhdGVyID09IHRoaXMuc21hcnRGb3gubXlTZWxmLm5hbWUpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIFJlc2V0IHRoZSAnbW9kaWZpZWQnIGZsYWdcblx0XHRcdFx0dGhpcy5faW50ZXJmYWNlQnVpbGRlci5yZXNldElzTW9kaWZpZWQoKTtcblxuXHRcdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0XHR0aGlzLnNoZWxsQ3RybC5zaG93Tm90aWZpY2F0aW9uKCdTZXJ2ZXIgc2V0dGluZ3MgdXBkYXRlZCcsICdJbW1lZGlhdGUgY2hhbmdlcyAoPGkgY2xhc3M9XCJmYXMgZmEtYm9sdFwiPjwvaT4pIGhhdmUgYmVlbiBhcHBsaWVkOyBhbGwgb3RoZXIgY2hhbmdlcyB3aWxsIGJlIGFjdGl2ZSBhZnRlciBuZXh0IHNlcnZlciByZXN0YXJ0Jyk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlXG5cdFx0XHR7XG5cdFx0XHRcdC8vIFNob3cgYWxlcnRcblx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGBBZG1pbmlzdHJhdG9yICR7dXBkYXRlcn0gaGFzIG1vZGlmaWVkIHRoZSBzZXJ2ZXIgc2V0dGluZ3M7IHBsZWFzZSByZWxvYWQgdG8gdXBkYXRlIHlvdXIgdmlldy5gKTtcblxuXHRcdFx0XHQvLyBEaXNhYmxlIHN1Ym1pdCBidXR0b25cblx0XHRcdFx0JCgnI3NyYy1zdWJtaXRCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIHRydWUpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFNlcnZlciBjb25maWd1cmF0aW9uIHhtbCBzYXZlZCBieSBhbiBleHRlcm5hbCBwcm9jZXNzXG5cdFx0ZWxzZSBpZiAoY29tbWFuZCA9PSB0aGlzLlJFU1BfQ09ORklHX0NIQU5HRURfQUxFUlQpXG5cdFx0e1xuXHRcdFx0Ly8gU2hvdyBhbGVydFxuXHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KGBUaGUgc3lzdGVtIGhhcyBtb2RpZmllZCB0aGUgc2VydmVyIHNldHRpbmdzIGF1dG9tYXRpY2FsbHk7IHBsZWFzZSByZWxvYWQgdG8gdXBkYXRlIHlvdXIgdmlldy5gKTtcblxuXHRcdFx0Ly8gRGlzYWJsZSBzdWJtaXQgYnV0dG9uXG5cdFx0XHQkKCcjc3JjLXN1Ym1pdEJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0Ly8gU1NMIGNlcnRpZmljYXRlIHVwbG9hZCBlcnJvclxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1NTTF9VUExPQURfRVJST1IpXG5cdFx0e1xuXHRcdFx0Y29uc3QgZXJyb3IgPSBkYXRhLmdldFV0ZlN0cmluZygnZXJyb3InKTtcblxuXHRcdFx0Ly8gTG9nIHdhcm5pbmdcblx0XHRcdHRoaXMuc2hlbGxDdHJsLmxvZ01lc3NhZ2UoZXJyb3IsICdlcnJvcicpO1xuXG5cdFx0XHQvLyBTaG93IGVycm9yIGluIG1hbmFnZXIgd2luZG93XG5cdFx0XHR0aGlzLl9zc2xDZXJ0TWFuYWdlci5vblNzbENlcnRVcGxvYWRFcnJvcihlcnJvcik7XG5cdFx0fVxuXG5cdFx0Ly8gU1NMIGNlcnRpZmljYXRlIHVwbG9hZCBjb25maXJtZWRcblx0XHRlbHNlIGlmIChjb21tYW5kID09IHRoaXMuUkVTUF9TU0xfVVBMT0FEX0NPTkZJUk0pXG5cdFx0e1xuXHRcdFx0Ly8gQ2xvc3cgbWFuYWdlciB3aW5kb3dcblx0XHRcdHRoaXMuX3NzbENlcnRNYW5hZ2VyLm9uU3NsQ2VydFVwbG9hZFN1Y2Nlc3MoKTtcblxuXHRcdFx0bGV0IHVwZGF0ZXIgPSBkYXRhLmdldFV0ZlN0cmluZygndXNlcicpO1xuXG5cdFx0XHQvLyBEaXNwbGF5IG5vdGlmaWNhdGlvblxuXHRcdFx0aWYgKHVwZGF0ZXIgPT0gdGhpcy5zbWFydEZveC5teVNlbGYubmFtZSlcblx0XHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd05vdGlmaWNhdGlvbignU1NMIGNlcnRpZmljYXRlJywgJ1NTTCBjZXJ0aWZpY2F0ZSBrZXlzdG9yZSB3YXMgdXBsb2FkZWQgc3VjY2Vzc2Z1bGx5Jyk7XG5cdFx0XHRlbHNlXG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ1NTTCBjZXJ0aWZpY2F0ZScsIGBBZG1pbmlzdHJhdG9yICR7dXBkYXRlcn0gaGFzIHVwbG9hZGVkIGEgbmV3IFNTTCBjZXJ0aWZpY2F0ZSBrZXlzdG9yZWApO1xuXG5cdFx0XHQvLyBXaGVuIGEgY2VydGlmaWNhdGUgaXMgdXBsb2FkZWQsIEhUVFBTIGlzIGFsc28gZW5hYmxlZCBhdXRvbWF0aWNhbGx5OlxuXHRcdFx0Ly8gd2UgaGF2ZSB0byB1cGRhdGUgdGhlIGludGVyZmFjZSBhY2NvcmRpbmdseVxuXHRcdFx0dGhpcy5fdXBkYXRlQ29uZmlnRm9ybUl0ZW1EaXNwbGF5ZWRWYWx1ZSgnd2ViU2VydmVyLmVuYWJsZUh0dHBzJywgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0Ly8gR2VvbG9jYXRpb24gZGF0YWJhc2UgdXBkYXRlIGNvbmZpcm1hdGlvblxuXHRcdGVsc2UgaWYgKGNvbW1hbmQgPT0gdGhpcy5SRVNQX1VQREFURV9HRU9fREIpXG5cdFx0e1xuXHRcdFx0Ly8gRW5hYmxlIGJ1dHRvblxuXHRcdFx0JCgnI3NyYy11cGRhdGVHZW9sb2NEYkJ1dHRvbicpLmF0dHIoJ2Rpc2FibGVkJywgZmFsc2UpO1xuXG5cdFx0XHQvLyBDaGVjayBzdWNjZXNzXG5cdFx0XHRpZiAoZGF0YS5nZXRCb29sKCdzdWNjZXNzJykpXG5cdFx0XHR7XG5cdFx0XHRcdC8vIFVwZGF0ZSBkaXNwbGF5ZWQgZGF0ZVxuXHRcdFx0XHR0aGlzLl91cGRhdGVDb25maWdGb3JtSXRlbURpc3BsYXllZFZhbHVlKCdhZG1pbkhlbHBlci5nZW9EYlJlbGVhc2VEYXRlJywgZGF0YS5nZXRVdGZTdHJpbmcoJ25ld1JlbERhdGUnKSk7XG5cblx0XHRcdFx0Ly8gSWYgdGhlIGN1cnJlbnQgdXNlciBpcyB0aGUgdXBkYXRlciwgYWxzbyBzaG93IGEgbm90aWZpY2F0aW9uXG5cdFx0XHRcdGxldCB1cGRhdGVyID0gZGF0YS5nZXRVdGZTdHJpbmcoJ3VzZXInKTtcblxuXHRcdFx0XHRpZiAodXBkYXRlciA9PSB0aGlzLnNtYXJ0Rm94Lm15U2VsZi5uYW1lKVxuXHRcdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dOb3RpZmljYXRpb24oJ0dlb2xvY2F0aW9uIGRhdGFiYXNlIHVwZGF0ZWQnLCAnTGF0ZXN0IHJlbGVhc2Ugb2YgdGhlIEdlb0xpdGUyIENvdW50cnkgZGF0YWJhc2UgaGFzIGJlZW4gaW5zdGFsbGVkIHN1Y2Nlc3NmdWxseScpO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBTaG93IGFsZXJ0XG5cdFx0XHRcdHRoaXMuc2hlbGxDdHJsLnNob3dTaW1wbGVBbGVydChkYXRhLmdldFV0ZlN0cmluZygnZXJyb3InKSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gUFJJVkFURSBNRVRIT0RTXG5cdC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0X2VuYWJsZUJ1dHRvbnMoZW5hYmxlZClcblx0e1xuXHRcdCQoJyNzcmMtcmVsb2FkQnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlZCk7XG5cdFx0JCgnI3NyYy1zdWJtaXRCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsICFlbmFibGVkKTtcblx0XHQkKCcjc3JjLWJhY2t1cENoZWNrJykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlZCk7XG5cblx0XHQkKCcjc3JjLXVwZGF0ZUdlb2xvY0RiQnV0dG9uJykuYXR0cignZGlzYWJsZWQnLCAhZW5hYmxlZCk7XG5cblx0XHRpZiAoIXRoaXMuX3NzbExvY2tlZClcblx0XHRcdHRoaXMuX3NzbENlcnRNYW5hZ2VyLmVuYWJsZWQgPSBlbmFibGVkO1xuXHR9XG5cblx0X3N3aXRjaFZpZXcodmlld0lkKVxuXHR7XG5cdFx0ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NyYy12aWV3c3RhY2snKS5zZWxlY3RlZEVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWV3SWQpO1xuXHR9XG5cblx0X2NsZWFyVGFicygpXG5cdHtcblx0XHQvLyBEZXN0cm95IHNjcm9sbGluZyB0YWJzXG5cdFx0JCgnI3NyYy10YWJOYXZpZ2F0b3IgI3RhYnMnKS5zY3JvbGxpbmdUYWJzKCdkZXN0cm95Jyk7XG5cblx0XHQvLyBSZW1vdmUgYWxsIHRhYiBuYXZpZ2F0b3IgY29udGVudFxuXHRcdHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuZGVzdHJveUludGVyZmFjZSgpO1xuXHR9XG5cblx0X29uVXBkYXRlR2VvbG9jRGJDbGljaygpXG5cdHtcblx0XHQvLyBEaXNhYmxlIGJ1dHRvblxuXHRcdCQoJyNzcmMtdXBkYXRlR2VvbG9jRGJCdXR0b24nKS5hdHRyKCdkaXNhYmxlZCcsIHRydWUpO1xuXG5cdFx0Ly8gU2VuZCByZXF1ZXN0IHRvIHNlcnZlclxuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfVVBEQVRFX0dFT19EQik7XG5cdH1cblxuXHRfb25SZWxvYWRDbGljaygpXG5cdHtcblx0XHQvLyBEaXNhYmxlIGJ1dHRvbnNcblx0XHR0aGlzLl9lbmFibGVCdXR0b25zKGZhbHNlKTtcblxuXHRcdC8vIFN3aXRjaCB0byBsb2FkaW5nIHZpZXdcblx0XHR0aGlzLl9zd2l0Y2hWaWV3KCdzcmMtbG9hZGluZycpO1xuXG5cdFx0Ly8gSGlkZSB2YWxpZGF0aW9uIG1lc3NhZ2VzXG5cdFx0dGhpcy5faW50ZXJmYWNlQnVpbGRlci5yZXNldFZhbGlkYXRpb24oKTtcblxuXHRcdC8vIFJlcXVlc3QgY29uZmlndXJhdGlvbiBkYXRhIHRvIHNlcnZlciBpbnN0YW5jZVxuXHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfR0VUX0NPTkZJRyk7XG5cdH1cblxuXHRfb25TdWJtaXRDbGljaygpXG5cdHtcblx0XHQvLyBDaGVjayB2YWxpZGl0eVxuXHRcdGlmICh0aGlzLl9pbnRlcmZhY2VCdWlsZGVyLmNoZWNrSXNWYWxpZCgpKVxuXHRcdHtcblx0XHRcdGxldCBjaGFuZ2VzID0gdGhpcy5faW50ZXJmYWNlQnVpbGRlci5nZXRDaGFuZ2VkRGF0YSgpO1xuXG5cdFx0XHRpZiAoY2hhbmdlcy5zaXplKCkgPiAwKVxuXHRcdFx0e1xuXHRcdFx0XHQvLyBEaXNhYmxlIGJ1dHRvbnNcblx0XHRcdFx0dGhpcy5fZW5hYmxlQnV0dG9ucyhmYWxzZSk7XG5cblx0XHRcdFx0Ly8gRGlzYWJsZSBmb3JtIGl0ZW1zXG5cdFx0XHRcdHRoaXMuX2ludGVyZmFjZUJ1aWxkZXIuZGlzYWJsZUludGVyZmFjZSh0cnVlKTtcblxuXHRcdFx0XHQvLyBTZW5kIHVwZGF0ZWQgc2V0dGluZ3MgdG8gc2VydmVyIGluc3RhbmNlXG5cdFx0XHRcdGxldCBwYXJhbXMgPSBuZXcgU0ZTMlguU0ZTT2JqZWN0KCk7XG5cdFx0XHRcdHBhcmFtcy5wdXRTRlNBcnJheSgnc2V0dGluZ3MnLCBjaGFuZ2VzKTtcblx0XHRcdFx0cGFyYW1zLnB1dEJvb2woJ2JhY2t1cCcsICQoJyNzcmMtYmFja3VwQ2hlY2snKS5wcm9wKCdjaGVja2VkJykpO1xuXG5cdFx0XHRcdHRoaXMuc2VuZEV4dGVuc2lvblJlcXVlc3QodGhpcy5SRVFfVVBEQVRFX0NPTkZJRywgcGFyYW1zKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0ZWxzZVxuXHRcdFx0dGhpcy5zaGVsbEN0cmwuc2hvd1NpbXBsZUFsZXJ0KCdVbmFibGUgdG8gc3VibWl0IGNvbmZpZ3VyYXRpb24gY2hhbmdlcyBkdWUgdG8gYW4gaW52YWxpZCB2YWx1ZTsgcGxlYXNlIHZlcmlmeSB0aGUgaGlnaGxpZ2h0ZWQgZm9ybSBmaWVsZHMgaW4gYWxsIHRhYnMuJywgdHJ1ZSk7XG5cdH1cblxuXHRfdXBkYXRlQ29uZmlnRm9ybUl0ZW1EaXNwbGF5ZWRWYWx1ZShjb25maWdQYXJhbU5hbWUsIG5ld1ZhbHVlKVxuXHR7XG5cdFx0Ly8gR2V0IHRoZSByZWxldmFudCBDb25maWd1cmF0aW9uIEZvcm0gSXRlbVxuXHRcdGNvbnN0IGNvbmZpZ0Zvcm1JdGVtID0gdGhpcy5faW50ZXJmYWNlQnVpbGRlci5nZXRDb25maWdGb3JtSXRlbShjb25maWdQYXJhbU5hbWUpO1xuXG5cdFx0Ly8gVXBkYXRlIENvbmZpZ3VyYXRpb24gUGFyYW1ldGVyIGFzc29jaWF0ZWQgd2l0aCB0aGUgQ29uZmlndXJhdGlvbiBGb3JtIEl0ZW1cblx0XHRjb25maWdGb3JtSXRlbS5kYXRhLnZhbHVlID0gbmV3VmFsdWU7XG5cdFx0Y29uZmlnRm9ybUl0ZW0uZGF0YS5yZXNldElzTW9kaWZpZWQoKTsgLy8gVGhpcyBpcyBuZWVkZWQgdG8gYXZvaWQgdGhlIENvbmZpZ3VyYXRpb24gUGFyYW1ldGVyIHRvIGZsYWdnZWQgYXMgJ2NoYW5nZWQnXG5cblx0XHQvLyBEaXNwbGF5IHRoZSBuZXcgdmFsdWUgb2YgdGhlIENvbmZpZ3VyYXRpb24gRm9ybSBJdGVtXG5cdFx0Y29uZmlnRm9ybUl0ZW0uX3NldFdpZGdldFZhbHVlKCk7IC8vIERpc3BsYXkgdGhlIG5ldyB2YWx1ZSBpbiB0aGUgY29uZmlnIGZvcm0gaXRlbVxuXHR9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUNuZEE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7O0EiLCJzb3VyY2VSb290IjoiIn0=