sequenceserver 0.8.9 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sequenceserver might be problematic. Click here for more details.

Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/{README.txt → README.md} +2 -0
  3. data/bin/sequenceserver +255 -55
  4. data/config.ru +2 -4
  5. data/lib/sequenceserver.rb +293 -447
  6. data/lib/sequenceserver/blast.rb +464 -64
  7. data/lib/sequenceserver/database.rb +185 -19
  8. data/lib/sequenceserver/links.rb +114 -0
  9. data/lib/sequenceserver/logger.rb +27 -0
  10. data/lib/sequenceserver/sequence.rb +141 -0
  11. data/public/css/bootstrap.min.css +8 -413
  12. data/public/css/custom.css +363 -122
  13. data/public/css/font-awesome.min.css +4 -0
  14. data/public/fonts/FontAwesome.otf +0 -0
  15. data/public/fonts/fontawesome-webfont.eot +0 -0
  16. data/public/fonts/fontawesome-webfont.svg +565 -0
  17. data/public/fonts/fontawesome-webfont.ttf +0 -0
  18. data/public/fonts/fontawesome-webfont.woff +0 -0
  19. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  20. data/public/js/bootstrap.min.js +11 -0
  21. data/public/js/d3.v3.min.js +5 -0
  22. data/public/js/html5shiv.min.js +4 -0
  23. data/public/js/jquery.scrollspy.js +74 -0
  24. data/public/js/jquery.t.js +353 -0
  25. data/public/js/sequence.js +2419 -0
  26. data/public/js/sequenceserver.blast.js +29 -30
  27. data/public/js/sequenceserver.js +544 -120
  28. data/public/js/underscore.min.js +6 -0
  29. data/public/js/webshims/polyfiller.js +1 -0
  30. data/public/js/webshims/shims/FlashCanvas/canvas2png.js +1 -0
  31. data/public/js/webshims/shims/FlashCanvas/flashcanvas.js +1 -0
  32. data/public/js/webshims/shims/FlashCanvas/flashcanvas.swf +0 -0
  33. data/public/js/webshims/shims/FlashCanvasPro/canvas2png.js +1 -0
  34. data/public/js/webshims/shims/FlashCanvasPro/flash10canvas.swf +0 -0
  35. data/public/js/webshims/shims/FlashCanvasPro/flash9canvas.swf +0 -0
  36. data/public/js/webshims/shims/FlashCanvasPro/flashcanvas.js +1 -0
  37. data/public/js/webshims/shims/canvas-blob.js +1 -0
  38. data/public/js/webshims/shims/color-picker.js +2 -0
  39. data/public/js/webshims/shims/combos/1.js +6 -0
  40. data/public/js/webshims/shims/combos/10.js +2 -0
  41. data/public/js/webshims/shims/combos/11.js +2 -0
  42. data/public/js/webshims/shims/combos/12.js +6 -0
  43. data/public/js/webshims/shims/combos/13.js +1 -0
  44. data/public/js/webshims/shims/combos/14.js +1 -0
  45. data/public/js/webshims/shims/combos/15.js +2 -0
  46. data/public/js/webshims/shims/combos/16.js +7 -0
  47. data/public/js/webshims/shims/combos/17.js +2 -0
  48. data/public/js/webshims/shims/combos/18.js +3 -0
  49. data/public/js/webshims/shims/combos/2.js +7 -0
  50. data/public/js/webshims/shims/combos/21.js +2 -0
  51. data/public/js/webshims/shims/combos/22.js +1 -0
  52. data/public/js/webshims/shims/combos/23.js +6 -0
  53. data/public/js/webshims/shims/combos/25.js +2 -0
  54. data/public/js/webshims/shims/combos/27.js +1 -0
  55. data/public/js/webshims/shims/combos/28.js +1 -0
  56. data/public/js/webshims/shims/combos/29.js +1 -0
  57. data/public/js/webshims/shims/combos/3.js +1 -0
  58. data/public/js/webshims/shims/combos/30.js +2 -0
  59. data/public/js/webshims/shims/combos/31.js +1 -0
  60. data/public/js/webshims/shims/combos/33.js +1 -0
  61. data/public/js/webshims/shims/combos/34.js +1 -0
  62. data/public/js/webshims/shims/combos/4.js +1 -0
  63. data/public/js/webshims/shims/combos/5.js +2 -0
  64. data/public/js/webshims/shims/combos/6.js +2 -0
  65. data/public/js/webshims/shims/combos/7.js +7 -0
  66. data/public/js/webshims/shims/combos/8.js +7 -0
  67. data/public/js/webshims/shims/combos/9.js +2 -0
  68. data/public/js/webshims/shims/combos/97.js +1 -0
  69. data/public/js/webshims/shims/combos/98.js +1 -0
  70. data/public/js/webshims/shims/combos/99.js +1 -0
  71. data/public/js/webshims/shims/details.js +1 -0
  72. data/public/js/webshims/shims/dom-extend.js +1 -0
  73. data/public/js/webshims/shims/es5.js +1 -0
  74. data/public/js/webshims/shims/es6.js +1 -0
  75. data/public/js/webshims/shims/excanvas.js +1 -0
  76. data/public/js/webshims/shims/filereader-xhr.js +1 -0
  77. data/public/js/webshims/shims/form-combat.js +1 -0
  78. data/public/js/webshims/shims/form-core.js +1 -0
  79. data/public/js/webshims/shims/form-datalist-lazy.js +1 -0
  80. data/public/js/webshims/shims/form-datalist.js +1 -0
  81. data/public/js/webshims/shims/form-fixrangechange.js +1 -0
  82. data/public/js/webshims/shims/form-inputmode.js +1 -0
  83. data/public/js/webshims/shims/form-message.js +1 -0
  84. data/public/js/webshims/shims/form-native-extend.js +1 -0
  85. data/public/js/webshims/shims/form-number-date-api.js +1 -0
  86. data/public/js/webshims/shims/form-number-date-ui.js +1 -0
  87. data/public/js/webshims/shims/form-shim-extend.js +1 -0
  88. data/public/js/webshims/shims/form-shim-extend2.js +1 -0
  89. data/public/js/webshims/shims/form-validation.js +1 -0
  90. data/public/js/webshims/shims/form-validators.js +1 -0
  91. data/public/js/webshims/shims/forms-picker.js +1 -0
  92. data/public/js/webshims/shims/geolocation.js +1 -0
  93. data/public/js/webshims/shims/i18n/formcfg-ar.js +1 -0
  94. data/public/js/webshims/shims/i18n/formcfg-ch-CN.js +1 -0
  95. data/public/js/webshims/shims/i18n/formcfg-cs.js +1 -0
  96. data/public/js/webshims/shims/i18n/formcfg-de.js +1 -0
  97. data/public/js/webshims/shims/i18n/formcfg-el.js +1 -0
  98. data/public/js/webshims/shims/i18n/formcfg-en.js +1 -0
  99. data/public/js/webshims/shims/i18n/formcfg-es.js +1 -0
  100. data/public/js/webshims/shims/i18n/formcfg-fa.js +1 -0
  101. data/public/js/webshims/shims/i18n/formcfg-fr.js +1 -0
  102. data/public/js/webshims/shims/i18n/formcfg-he.js +1 -0
  103. data/public/js/webshims/shims/i18n/formcfg-hi.js +1 -0
  104. data/public/js/webshims/shims/i18n/formcfg-hu.js +1 -0
  105. data/public/js/webshims/shims/i18n/formcfg-it.js +1 -0
  106. data/public/js/webshims/shims/i18n/formcfg-ja.js +1 -0
  107. data/public/js/webshims/shims/i18n/formcfg-lt.js +1 -0
  108. data/public/js/webshims/shims/i18n/formcfg-nl.js +1 -0
  109. data/public/js/webshims/shims/i18n/formcfg-pl.js +1 -0
  110. data/public/js/webshims/shims/i18n/formcfg-pt-BR.js +1 -0
  111. data/public/js/webshims/shims/i18n/formcfg-pt-PT.js +1 -0
  112. data/public/js/webshims/shims/i18n/formcfg-pt.js +1 -0
  113. data/public/js/webshims/shims/i18n/formcfg-ru.js +1 -0
  114. data/public/js/webshims/shims/i18n/formcfg-sv.js +1 -0
  115. data/public/js/webshims/shims/i18n/formcfg-zh-CN.js +1 -0
  116. data/public/js/webshims/shims/i18n/formcfg-zh-TW.js +1 -0
  117. data/public/js/webshims/shims/jme/alternate-media.js +1 -0
  118. data/public/js/webshims/shims/jme/base.js +1 -0
  119. data/public/js/webshims/shims/jme/controls.css +1 -0
  120. data/public/js/webshims/shims/jme/jme.eot +0 -0
  121. data/public/js/webshims/shims/jme/jme.svg +36 -0
  122. data/public/js/webshims/shims/jme/jme.ttf +0 -0
  123. data/public/js/webshims/shims/jme/jme.woff +0 -0
  124. data/public/js/webshims/shims/jme/mediacontrols-lazy.js +1 -0
  125. data/public/js/webshims/shims/jme/mediacontrols.js +1 -0
  126. data/public/js/webshims/shims/jme/playlist.js +1 -0
  127. data/public/js/webshims/shims/jpicker/images/AlphaBar.png +0 -0
  128. data/public/js/webshims/shims/jpicker/images/Bars.png +0 -0
  129. data/public/js/webshims/shims/jpicker/images/Maps.png +0 -0
  130. data/public/js/webshims/shims/jpicker/images/NoColor.png +0 -0
  131. data/public/js/webshims/shims/jpicker/images/bar-opacity.png +0 -0
  132. data/public/js/webshims/shims/jpicker/images/map-opacity.png +0 -0
  133. data/public/js/webshims/shims/jpicker/images/mappoint.gif +0 -0
  134. data/public/js/webshims/shims/jpicker/images/picker.gif +0 -0
  135. data/public/js/webshims/shims/jpicker/images/preview-opacity.png +0 -0
  136. data/public/js/webshims/shims/jpicker/images/rangearrows.gif +0 -0
  137. data/public/js/webshims/shims/jpicker/jpicker.css +1 -0
  138. data/public/js/webshims/shims/matchMedia.js +3 -0
  139. data/public/js/webshims/shims/mediacapture-picker.js +1 -0
  140. data/public/js/webshims/shims/mediacapture.js +1 -0
  141. data/public/js/webshims/shims/mediaelement-core.js +1 -0
  142. data/public/js/webshims/shims/mediaelement-debug.js +1 -0
  143. data/public/js/webshims/shims/mediaelement-jaris.js +1 -0
  144. data/public/js/webshims/shims/mediaelement-native-fix.js +1 -0
  145. data/public/js/webshims/shims/mediaelement-yt.js +1 -0
  146. data/public/js/webshims/shims/moxie/flash/Moxie.cdn.swf +0 -0
  147. data/public/js/webshims/shims/moxie/flash/Moxie.min.swf +0 -0
  148. data/public/js/webshims/shims/moxie/js/moxie-html4.js +3 -0
  149. data/public/js/webshims/shims/moxie/js/moxie-swf.js +2 -0
  150. data/public/js/webshims/shims/picture.js +1 -0
  151. data/public/js/webshims/shims/plugins/jquery.ui.position.js +11 -0
  152. data/public/js/webshims/shims/range-ui.js +1 -0
  153. data/public/js/webshims/shims/sizzle.js +11 -0
  154. data/public/js/webshims/shims/sticky.js +1 -0
  155. data/public/js/webshims/shims/styles/color-picker.png +0 -0
  156. data/public/js/webshims/shims/styles/forms-ext.css +1 -0
  157. data/public/js/webshims/shims/styles/forms-picker.css +1 -0
  158. data/public/js/webshims/shims/styles/progress.gif +0 -0
  159. data/public/js/webshims/shims/styles/progress.png +0 -0
  160. data/public/js/webshims/shims/styles/shim-ext.css +1 -0
  161. data/public/js/webshims/shims/styles/shim.css +1 -0
  162. data/public/js/webshims/shims/styles/transparent.png +0 -0
  163. data/public/js/webshims/shims/styles/widget.eot +0 -0
  164. data/public/js/webshims/shims/styles/widget.svg +12 -0
  165. data/public/js/webshims/shims/styles/widget.ttf +0 -0
  166. data/public/js/webshims/shims/styles/widget.woff +0 -0
  167. data/public/js/webshims/shims/swf/JarisFLVPlayer.swf +0 -0
  168. data/public/js/webshims/shims/swfmini-embed.js +1 -0
  169. data/public/js/webshims/shims/swfmini.js +6 -0
  170. data/public/js/webshims/shims/track-ui.js +1 -0
  171. data/public/js/webshims/shims/track.js +1 -0
  172. data/public/js/webshims/shims/url.js +1 -0
  173. data/public/js/webshims/shims/usermedia-core.js +1 -0
  174. data/public/js/webshims/shims/usermedia-shim.js +1 -0
  175. data/sequenceserver.gemspec +16 -13
  176. data/views/400.erb +28 -0
  177. data/views/500.erb +35 -19
  178. data/views/_options.erb +6 -15
  179. data/views/result.erb +218 -0
  180. data/views/search.erb +354 -151
  181. metadata +254 -62
  182. data/example.config.yml +0 -39
  183. data/lib/sequenceserver/customisation.rb +0 -60
  184. data/lib/sequenceserver/database_formatter.rb +0 -190
  185. data/lib/sequenceserver/helpers.rb +0 -136
  186. data/lib/sequenceserver/sequencehelpers.rb +0 -93
  187. data/lib/sequenceserver/sinatralikeloggerformatter.rb +0 -12
  188. data/lib/sequenceserver/version.rb +0 -9
  189. data/public/css/beige.css.css +0 -254
  190. data/public/css/bootstrap.dropdown.css +0 -29
  191. data/public/css/bootstrap.icons.css +0 -155
  192. data/public/css/bootstrap.modal.css +0 -28
  193. data/public/js/bootstrap.dropdown.js +0 -92
  194. data/public/js/bootstrap.modal.js +0 -7
  195. data/public/js/bootstrap.transition.js +0 -7
  196. data/public/js/jquery-scrollspy.js +0 -98
  197. data/public/js/jquery.activity.js +0 -10
  198. data/public/js/jquery.enablePlaceholder.min.js +0 -10
  199. data/public/js/store.min.js +0 -2
  200. data/public/sequence.html +0 -28
  201. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta +0 -5486
  202. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  203. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  204. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  205. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta +0 -6449
  206. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  207. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  208. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.psq +0 -0
  209. data/tests/run +0 -26
  210. data/tests/test_sequencehelpers.rb +0 -77
  211. data/tests/test_sequenceserver_blast.rb +0 -60
  212. data/tests/test_ui.rb +0 -104
  213. data/tests/ui.specs.todo +0 -10
@@ -0,0 +1,2419 @@
1
+ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ // legacy!!
3
+ $.browser = require("jquery-browser-plugin");
4
+
5
+ /**
6
+ * Sequence component
7
+ *
8
+ * @class
9
+ * @extends Biojs
10
+ *
11
+ * @author <a href="mailto:johncar@gmail.com">John Gomez</a>, <a href="mailto:secevalliv@gmail.com">Jose Villaveces</a>
12
+ * @version 1.0.0
13
+ * @category 3
14
+ *
15
+ * @requires <a href='http://blog.jquery.com/2011/09/12/jquery-1-6-4-released/'>jQuery Core 1.6.4</a>
16
+ * @dependency <script language="JavaScript" type="text/javascript" src="../biojs/dependencies/jquery/jquery-1.4.2.min.js"></script>
17
+ *
18
+ * @requires <a href='http://jqueryui.com/download'>jQuery UI 1.8.16</a>
19
+ * @dependency <script language="JavaScript" type="text/javascript" src="../biojs/dependencies/jquery/jquery-ui-1.8.2.custom.min.js"></script>
20
+ *
21
+ * @requires <a href='Biojs.Tooltip.css'>Biojs.Tooltip</a>
22
+ * @dependency <script language="JavaScript" type="text/javascript" src="src/Biojs.Tooltip.js"></script>
23
+ *
24
+ * @param {Object} options An object with the options for Sequence component.
25
+ *
26
+ * @option {string} target
27
+ * Identifier of the DIV tag where the component should be displayed.
28
+ *
29
+ * @option {string} sequence
30
+ * The sequence to be displayed.
31
+ *
32
+ * @option {string} [id]
33
+ * Sequence identifier if apply.
34
+ *
35
+ * @option {string} [format="FASTA"]
36
+ * The display format for the sequence representation.
37
+ *
38
+ * @option {Object[]} [highlights]
39
+ * For highlighting multiple regions.
40
+ * <pre class="brush: js" title="Syntax:">
41
+ * [
42
+ * // Highlight aminoacids from 'start' to 'end' of the current strand using the specified 'color' (optional) and 'background' (optional).
43
+ * { start: &lt;startVal1&gt;, end: &lt;endVal1&gt; [, id:&lt;idVal1&gt;] [, color: &lt;HTMLColor&gt;] [, background: &lt;HTMLColor&gt;]},
44
+ * //
45
+ * // Any others highlights
46
+ * ...,
47
+ * //
48
+ * { start: &lt;startValN&gt;, end: &lt;endValN&gt; [, id:&lt;idValN&gt;] [, color: &lt;HTMLColor&gt;] [, background: &lt;HTMLColor&gt;]}
49
+ * ]</pre>
50
+ *
51
+ * <pre class="brush: js" title="Example:">
52
+ * highlights : [
53
+ * { start:30, end:42, color:"white", background:"green", id:"spin1" },
54
+ * { start:139, end:140 },
55
+ * { start:631, end:633, color:"white", background:"blue" }
56
+ * ]
57
+ * </pre>
58
+ *
59
+ * @option {Object} [columns={size:40,spacedEach:10}]
60
+ * Options for displaying the columns. Syntax: { size: &lt;numCols&gt;, spacedEach: &lt;numCols&gt;}
61
+ *
62
+ * @option {Object} [selection]
63
+ * Positions for the current selected region. Syntax: { start: &lt;startValue&gt;, end: &lt;endValue&gt;}
64
+ *
65
+ * @option {Object[]} [annotations]
66
+ * Set of overlapping annotations. Must be an array of objects following the syntax:
67
+ * <pre class="brush: js" title="Syntax:">
68
+ * [
69
+ * // An annotation:
70
+ * { name: &lt;name&gt;,
71
+ * html: &lt;message&gt;,
72
+ * color: &lt;color_code&gt;,
73
+ * regions: [{ start: &lt;startVal1&gt;, end: &lt;endVal1&gt; color: &lt;HTMLColor&gt;}, ...,{ start: &lt;startValN&gt;, end: &lt;endValN&gt;, color: &lt;HTMLColor&gt;}]
74
+ * },
75
+ *
76
+ * // ...
77
+ * // more annotations here
78
+ * // ...
79
+ * ]
80
+ * </pre>
81
+ * where:
82
+ * <ul>
83
+ * <li><b>name</b> is the unique name for the annotation</li>
84
+ * <li><b>html</b> is the message (can be HTML) to be displayed in the tool tip.</li>
85
+ * <li><b>color</b> is the default HTML color code for all the regions.</li>
86
+ * <li><b>regions</b> array of objects defining the intervals which belongs to the annotation.</li>
87
+ * <li><b>regions[i].start</b> is the starting character for the i-th interval.</li>
88
+ * <li><b>regions[i].end</b> is the ending character for the i-th interval.</li>
89
+ * <li><b>regions[i].color</b> is an optional color for the i-th interval.
90
+ * </ul>
91
+ *
92
+ * @option {Object} [formatOptions={title:true, footer:true}]
93
+ * Options for displaying the title. by now just affecting the CODATA format.
94
+ * <pre class="brush: js" title="Syntax:">
95
+ * formatOptions : {
96
+ * title:false,
97
+ * footer:false
98
+ * }
99
+ * </pre>
100
+ *
101
+ * @example
102
+ * var theSequence = "METLCQRLNVCQDKILTHYENDSTDLRDHIDYWKHMRLECAIYYKAREMGFKHINHQVVPTLAVSKNKALQAIELQLTLETIYNSQYSNEKWTLQDVSLEVYLTAPTGCIKKHGYTVEVQFDGDICNTMHYTNWTHIYICEEAojs SVTVVEGQVDYYGLYYVHEGIRTYFVQFKDDAEKYSKNKVWEVHAGGQVILCPTSVFSSNEVSSPEIIRQHLANHPAATHTKAVALGTEETQTTIQRPRSEPDTGNPCHTTKLLHRDSVDSAPILTAFNSSHKGRINCNSNTTPIVHLKGDANTLKCLRYRFKKHCTLYTAVSSTWHWTGHNVKHKSAIVTLTYDSEWQRDQFLSQVKIPKTITVSTGFMSI";
103
+ * var mySequence = new Sequence({
104
+ * sequence : theSequence,
105
+ * target : "YourOwnDivId",
106
+ * format : 'CODATA',
107
+ * id : 'P918283',
108
+ * annotations: [
109
+ * { name:"CATH",
110
+ * color:"#F0F020",
111
+ * html: "Using color code #F0F020 ",
112
+ * regions: [{start: 122, end: 135}]
113
+ * },
114
+ * { name:"TEST",
115
+ * html:"&lt;br&gt; Example of &lt;b&gt;HTML&lt;/b&gt;",
116
+ * color:"green",
117
+ * regions: [
118
+ * {start: 285, end: 292},
119
+ * {start: 293, end: 314, color: "#2E4988"}]
120
+ * }
121
+ * ],
122
+ * highlights : [
123
+ * { start:30, end:42, color:"white", background:"green", id:"spin1" },
124
+ * { start:139, end:140 },
125
+ * { start:631, end:633, color:"white", background:"blue" }
126
+ * ]
127
+ * });
128
+ *
129
+ */
130
+
131
+ var Class = require('js-class');
132
+
133
+ var EVT_ON_SELECTION_CHANGE= "onSelectionChange";
134
+ var EVT_ON_SELECTION_CHANGED= "onSelectionChanged";
135
+ var EVT_ON_ANNOTATION_CLICKED= "onAnnotationClicked";
136
+
137
+ Sequence = Class(
138
+ /** @lends Sequence# */
139
+ {
140
+ constructor: function (options) {
141
+ var self = this;
142
+
143
+ this.opt = jQuery.extend(this.opt,options);
144
+
145
+ this._container = jQuery(this.opt.target );
146
+
147
+ // legacy support (target id without '#')
148
+ if(this._container.length == 0){
149
+ this._container = jQuery( "#" + this.opt.target )
150
+ }
151
+
152
+ if(this._container.length == 0){
153
+ console.log("empty target container");
154
+ }
155
+
156
+ // legacy: copy target id
157
+ this.opt.target = this._container[0].id;
158
+
159
+ // Lazy initialization
160
+ this._container.ready(function() {
161
+ self._initialize();
162
+ });
163
+ },
164
+
165
+ /**
166
+ * Default values for the options
167
+ * @name Sequence-opt
168
+ */
169
+ opt : {
170
+
171
+ sequence : "",
172
+ id : "",
173
+ target : "",
174
+ format : "FASTA",
175
+ selection: { start: 0, end: 0 },
176
+ columns: { size: 35, spacedEach: 10 },
177
+ highlights : [],
178
+ annotations: [],
179
+ sequenceUrl: 'http://www.ebi.ac.uk/das-srv/uniprot/das/uniprot/sequence',
180
+
181
+ // Styles
182
+ selectionColor : 'Yellow',
183
+ selectionFontColor : 'black',
184
+ highlightFontColor : 'red',
185
+ highlightBackgroundColor : 'white',
186
+ fontColor : 'inherit',
187
+ backgroundColor : 'inherit',
188
+ width: undefined,
189
+ height: undefined,
190
+ formatSelectorVisible: true
191
+ },
192
+
193
+ /**
194
+ * Array containing the supported event names
195
+ * @name Sequence-eventTypes
196
+ */
197
+ eventTypes : [
198
+ /**
199
+ * @name Sequence#onSelectionChanged
200
+ * @event
201
+ * @param {function} actionPerformed An function which receives an {@link Biojs.Event} object as argument.
202
+ * @eventData {Object} source The component which did triggered the event.
203
+ * @eventData {string} type The name of the event.
204
+ * @eventData {int} start A number indicating the start of the selection.
205
+ * @eventData {int} end A number indicating the ending of selection.
206
+ * @example
207
+ * mySequence.onSelectionChanged(
208
+ * function( objEvent ) {
209
+ * alert("Selected: " + objEvent.start + ", " + objEvent.end );
210
+ * }
211
+ * );
212
+ *
213
+ * */
214
+ "onSelectionChanged",
215
+
216
+ /**
217
+ * @name Sequence#onSelectionChange
218
+ * @event
219
+ * @param {function} actionPerformed An function which receives an {@link Biojs.Event} object as argument.
220
+ * @eventData {Object} source The component which did triggered the event.
221
+ * @eventData {string} type The name of the event.
222
+ * @eventData {int} start A number indicating the start of the selection.
223
+ * @eventData {int} end A number indicating the ending of selection.
224
+ * @example
225
+ * mySequence.onSelectionChange(
226
+ * function( objEvent ) {
227
+ * alert("Selection in progress: " + objEvent.start + ", " + objEvent.end );
228
+ * }
229
+ * );
230
+ *
231
+ *
232
+ * */
233
+ "onSelectionChange",
234
+
235
+ /**
236
+ * @name Sequence#onAnnotationClicked
237
+ * @event
238
+ * @param {function} actionPerformed An function which receives an {@link Biojs.Event} object as argument.
239
+ * @eventData {Object} source The component which did triggered the event.
240
+ * @eventData {string} type The name of the event.
241
+ * @eventData {string} name The name of the selected annotation.
242
+ * @eventData {int} pos A number indicating the position of the selected amino acid.
243
+ * @example
244
+ * mySequence.onAnnotationClicked(
245
+ * function( objEvent ) {
246
+ * alert("Clicked " + objEvent.name + " on position " + objEvent.pos );
247
+ * }
248
+ * );
249
+ *
250
+ * */
251
+ "onAnnotationClicked"
252
+ ],
253
+
254
+ getId : function () {
255
+ return this.opt.id;
256
+ },
257
+
258
+ // internal members
259
+ _headerDiv : null,
260
+ _contentDiv : null,
261
+
262
+ // Methods
263
+
264
+ _initialize: function () {
265
+
266
+ if ( this.opt.width !== undefined ) {
267
+ this._container.width( this.opt.width );
268
+ }
269
+
270
+ if ( this.opt.height !== undefined ) {
271
+ this._container.height( this.opt.height );
272
+ }
273
+
274
+ // DIV for the format selector
275
+ this._buildFormatSelector();
276
+
277
+ // DIV for the sequence
278
+ this._contentDiv = jQuery('<div></div>').appendTo(this._container);
279
+ this._contentDiv.css({
280
+ 'text-align': 'left'
281
+ });
282
+
283
+ // Initialize highlighting
284
+ this._highlights = this.opt.highlights;
285
+
286
+ // Initialize annotations
287
+ this._annotations = this.opt.annotations;
288
+
289
+ //Initialize tooltip
290
+ var tooltip = "sequenceTip" + this.opt.target ;
291
+ jQuery('<div id="' + tooltip + '"></div>')
292
+ .css({
293
+ 'position': "absolute",
294
+ 'z-index': "999999",
295
+ 'color': "#fff",
296
+ 'font-size': "12px",
297
+ 'width': "auto",
298
+ 'display': 'none'
299
+ })
300
+ .addClass("tooltip")
301
+ .appendTo("body")
302
+ .hide();
303
+ this.opt._tooltip = document.getElementById(tooltip);
304
+
305
+ if ( (this.opt.sequence) ) {
306
+ this._redraw();
307
+
308
+ } else if ( (this.opt.id) ) {
309
+ this._requestSequence( this.opt.id );
310
+
311
+ } else {
312
+ this.clearSequence("No sequence available", "../biojs/css/images/warning_icon.png");
313
+ }
314
+
315
+ },
316
+
317
+
318
+ /**
319
+ * Shows the columns indicated by the indexes array.
320
+ * @param {string} seq The sequence strand.
321
+ * @param {string} [identifier] Sequence identifier.
322
+ *
323
+ * @example
324
+ * mySequence.setSequence("P99999");
325
+ *
326
+ */
327
+ setSequence: function ( seq, identifier ) {
328
+
329
+ if ( seq.match(/^([A-N,R-Z][0-9][A-Z][A-Z, 0-9][A-Z, 0-9][0-9])|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\.\d+)?$/i) ) {
330
+ this._requestSequence( arguments[0] );
331
+
332
+ } else {
333
+ this.opt.sequence = seq;
334
+ this.opt.id = identifier;
335
+ this._highlights = [];
336
+ this._highlightsCount = 0;
337
+ this.opt.selection = { start: 0, end: 0 };
338
+ this._annotations = [];
339
+
340
+ this._contentDiv.children().remove();
341
+ this._redraw();
342
+ }
343
+ },
344
+
345
+ _requestSequence: function ( accession ) {
346
+ var self = this;
347
+
348
+ console.log("Requesting sequence for: " + accession );
349
+
350
+ jQuery.ajax({
351
+ url: self.opt.sequenceUrl,
352
+ dataType: "xml",
353
+ data: { segment: accession },
354
+ success: function ( xml ) {
355
+ try {
356
+
357
+ var sequenceNode = jQuery(xml).find('SEQUENCE:first');
358
+ self.setSequence( sequenceNode.text(), sequenceNode.attr("id"), sequenceNode.attr("label") );
359
+
360
+ } catch (e) {
361
+ console.log("Error decoding response data: " + e.message );
362
+ self.clearSequence("No sequence available", "../biojs/css/images/warning_icon.png");
363
+ }
364
+
365
+ },
366
+ error: function (jqXHR, textStatus, errorThrown) {
367
+ console.log("Error decoding response data: " + textStatus );
368
+ self.clearSequence("Error requesting the sequence to the server " + this.url , "../biojs/css/images/warning_icon.png");
369
+ }
370
+ });
371
+ },
372
+
373
+ /**
374
+ * Shows the columns indicated by the indexes array.
375
+ * @param {string} [showMessage] Message to be showed.
376
+ * @param {string} [icon] Icon to be showed a side of the message
377
+ *
378
+ * @example
379
+ * mySequence.clearSequence("No sequence available", "../biojs/css/images/warning_icon.png");
380
+ *
381
+ */
382
+ clearSequence: function ( showMessage, icon ) {
383
+
384
+ var message = undefined;
385
+
386
+ this.opt.sequence = "";
387
+ this.opt.id = "";
388
+ this._highlights = [];
389
+ this._highlightsCount = 0;
390
+ this.opt.selection = { start: 0, end: 0 };
391
+ this._annotations = [];
392
+ this._contentDiv.children().remove();
393
+
394
+ this._headerDiv.hide();
395
+
396
+ if ( undefined !== showMessage ) {
397
+ message = jQuery('<div>' + showMessage + '</div>')
398
+ .appendTo(this._contentDiv)
399
+ .addClass("message");
400
+
401
+ if ( undefined !== icon ) {
402
+ message.css({
403
+ 'background': 'transparent url("' + icon + '") no-repeat center left',
404
+ 'padding-left': '20px'
405
+ });
406
+ }
407
+ }
408
+ },
409
+
410
+ /**
411
+ * Set the current selection in the sequence causing the event {@link Sequence#onSelectionChanged}
412
+ *
413
+ * @example
414
+ * // set selection from the position 100 to 150
415
+ * mySequence.setSelection(100, 150);
416
+ *
417
+ * @param {int} start The starting character of the selection.
418
+ * @param {int} end The ending character of the selection
419
+ */
420
+ setSelection : function(start, end) {
421
+ if(start > end) {
422
+ var aux = end;
423
+ end = start;
424
+ start = aux;
425
+
426
+ }
427
+
428
+ if(start != this.opt.selection.start || end != this.opt.selection.end) {
429
+ this._setSelection(start, end);
430
+ this.trigger(
431
+ EVT_ON_SELECTION_CHANGED,
432
+ { "start" : start, "end" : end }
433
+ );
434
+ }
435
+ },
436
+
437
+ _buildFormatSelector: function () {
438
+ var self = this;
439
+
440
+ this._headerDiv = jQuery('<div></div>').appendTo(this._container);
441
+ this._headerDiv.append('Format: ');
442
+
443
+ this._formatSelector = jQuery('<select> '+
444
+ '<option value="FASTA">FASTA</option>'+
445
+ '<option value="CODATA">CODATA</option>'+
446
+ '<option value="PRIDE">PRIDE</option>'+
447
+ '<option value="RAW">RAW</option></select>').appendTo(self._headerDiv);
448
+
449
+ this._formatSelector.change(function(e) {
450
+ self.opt.format = jQuery(this).val();
451
+ self._redraw();
452
+ });
453
+
454
+ this._formatSelector.val(self.opt.format);
455
+
456
+ this.formatSelectorVisible( this.opt.formatSelectorVisible );
457
+ },
458
+
459
+ /**
460
+ * Highlights a region using the font color defined in {Sequence#highlightFontColor} by default is red.
461
+ *
462
+ * @example
463
+ * // highlight the characters within the position 100 to 150, included.
464
+ * mySequence.addHighlight( { "start": 100, "end": 150, "color": "white", "background": "red", "id": "aaa" } );
465
+ *
466
+ * @param {Object} h The highlight defined as follows:
467
+ *
468
+ *
469
+ * @return {int} representing the id of the highlight on the internal array. Returns -1 on failure
470
+ */
471
+ addHighlight : function ( h ) {
472
+ var id = '-1';
473
+ var color = "";
474
+ var background = "";
475
+ var highlight = {};
476
+
477
+ if ( h instanceof Object && h.start <= h.end ) {
478
+
479
+ color = ( "string" == typeof h.color )? h.color : this.opt.highlightFontColor;
480
+ background = ( "string" == typeof h.background )? h.background : this.opt.highlightBackgroundColor;
481
+ id = ( "string" == typeof h.id )? h.id : (new Number(this._highlightsCount++)).toString();
482
+
483
+ highlight = { "start": h.start, "end": h.end, "color": color, "background": background, "id": id };
484
+
485
+ this._highlights.push(highlight);
486
+ this._applyHighlight(highlight);
487
+ this._restoreSelection(h.start,h.end);
488
+ }
489
+
490
+ return id;
491
+ },
492
+ /*
493
+ * Function: Sequence._applyHighlight
494
+ * Purpose: Apply the specified color and background to a region between 'start' and 'end'.
495
+ * Returns: -
496
+ * Inputs: highlight -> {Object} An object containing the fields start (int), end (int),
497
+ * color (HTML color string) and background (HTML color string).
498
+ */
499
+ _applyHighlight: function ( highlight ) {
500
+ var seq = this._contentDiv.find('.sequence');
501
+ for ( var i = highlight.start - 1; i < highlight.end; i++ ){
502
+ zindex = jQuery(seq[i]).css("z-index");
503
+ if (zindex=="auto"){
504
+ z = 1;
505
+ o = 1;
506
+ }
507
+ else{
508
+ z = 0;
509
+ o = 0.5;
510
+ }
511
+ jQuery(seq[i])
512
+ .css({
513
+ "color": highlight.color,
514
+ "background-color": highlight.background,
515
+ "z-index": z,
516
+ "opacity": o
517
+ })
518
+ .addClass("highlighted");
519
+ }
520
+ },
521
+ /*
522
+ * Function: Sequence._applyHighlights
523
+ * Purpose: Apply the specified highlights.
524
+ * Returns: -
525
+ * Inputs: highlights -> {Object[]} An array containing the highlights to be applied.
526
+ */
527
+ _applyHighlights: function ( highlights ) {
528
+ for ( var i in highlights ) {
529
+ this._applyHighlight(highlights[i]);
530
+ }
531
+ },
532
+ /*
533
+ * Function: Sequence._restoreHighlights
534
+ * Purpose: Repaint the highlights in the specified region.
535
+ * Returns: -
536
+ * Inputs: start -> {int} Start of the region to be restored.
537
+ * end -> {int} End of the region to be restored.
538
+ */
539
+ _restoreHighlights: function ( start, end ) {
540
+ var h = this._highlights;
541
+ // paint the region using default blank settings
542
+ this._applyHighlight({
543
+ "start": start,
544
+ "end": end,
545
+ "color": this.opt.fontColor,
546
+ "background": this.opt.backgroundColor
547
+ });
548
+ // restore highlights in that region
549
+ for ( var i in h ) {
550
+ // interval intersects with highlight i ?
551
+ if ( !( h[i].start > end || h[i].end < start ) ) {
552
+ a = ( h[i].start < start ) ? start : h[i].start;
553
+ b = ( h[i].end > end ) ? end : h[i].end;
554
+ this._applyHighlight({
555
+ "start": a,
556
+ "end": b,
557
+ "color": h[i].color,
558
+ "background": h[i].background
559
+ });
560
+ }
561
+ }
562
+ },
563
+ /*
564
+ * Function: Sequence._restoreSelection
565
+ * Purpose: Repaint the current selection in the specified region.
566
+ * It is used in the case of any highlight do overriding of the current selection.
567
+ * Returns: -
568
+ * Inputs: start -> {int} Start of the region to be restored.
569
+ * end -> {int} End of the region to be restored.
570
+ */
571
+ _restoreSelection: function ( start, end ) {
572
+ var sel = this.opt.selection;
573
+ // interval intersects with current selection ?
574
+ // restore selection
575
+ if ( !( start > sel.end || end < sel.start ) ) {
576
+ a = ( start < sel.start ) ? sel.start : start;
577
+ b = ( end > sel.end ) ? sel.end : end;
578
+
579
+ this._applyHighlight({
580
+ "start": a,
581
+ "end": b,
582
+ "color": this.opt.selectionFontColor,
583
+ "background": this.opt.selectionColor,
584
+ });
585
+ }
586
+ },
587
+
588
+ /**
589
+ * Remove a highlight.
590
+ *
591
+ * @example
592
+ * // Clear the highlighted characters within the position 100 to 150, included.
593
+ * mySequence.removeHighlight("spin1");
594
+ *
595
+ * @param {string} id The id of the highlight on the internal array. This value is returned by method highlight.
596
+ */
597
+ removeHighlight : function (id) {
598
+ var h = this._highlights;
599
+ for ( i in h ) {
600
+ if ( h[i].id == id ) {
601
+ start = h[i].start;
602
+ end = h[i].end;
603
+ h.splice(i,1);
604
+
605
+ this._restoreHighlights(start,end);
606
+ this._restoreSelection(start,end);
607
+
608
+ break;
609
+ }
610
+ }
611
+ },
612
+
613
+ /**
614
+ * Remove all the highlights of whole sequence.
615
+ *
616
+ * @example
617
+ * mySequence.removeAllHighlights();
618
+ */
619
+ removeAllHighlights : function () {
620
+ this._highlights = [];
621
+ this._restoreHighlights(1,this.opt.sequence.length);
622
+ this._restoreSelection(1,this.opt.sequence.length);
623
+ },
624
+
625
+ /**
626
+ * Changes the current displaying format of the sequence.
627
+ *
628
+ * @example
629
+ * // Set format to 'FASTA'.
630
+ * mySequence.setFormat('FASTA');
631
+ *
632
+ * @param {string} format The format for the sequence to be displayed.
633
+ */
634
+ setFormat : function(format) {
635
+ if ( this.opt.format != format.toUpperCase() ) {
636
+ this.opt.format = format.toUpperCase();
637
+ this._redraw();
638
+ }
639
+
640
+ var self = this;
641
+ // Changes the option in the combo box
642
+ this._headerDiv.find('option').each(function() {
643
+ if(jQuery(this).val() == self.opt.format.toUpperCase()) {
644
+ jQuery(this).attr('selected', 'selected');
645
+ }
646
+ });
647
+ },
648
+
649
+ /**
650
+ * Changes the current number of columns in the displayed sequence.
651
+ *
652
+ * @example
653
+ * // Set the number of columns to 70.
654
+ * mySequence.setNumCols(70);
655
+ *
656
+ * @param {int} numCols The number of columns.
657
+ */
658
+ setNumCols : function(numCols) {
659
+ this.opt.columns.size = numCols;
660
+ this._redraw();
661
+ },
662
+
663
+ /**
664
+ * Set the visibility of the drop-down list of formats.
665
+ *
666
+ * @param {boolean} visible true: show; false: hide.
667
+ */
668
+ formatSelectorVisible : function (visible){
669
+ if (visible) {
670
+ this._headerDiv.show();
671
+ } else {
672
+ this._headerDiv.hide();
673
+ }
674
+ },
675
+
676
+ /**
677
+ * This is similar to a {Biojs.Protein3D#formatSelectorVisible} with the 'true' argument.
678
+ *
679
+ * @example
680
+ * // Shows the format selector.
681
+ * mySequence.showFormatSelector();
682
+ *
683
+ */
684
+ showFormatSelector : function() {
685
+ this._headerDiv.show();
686
+ },
687
+
688
+ /**
689
+ * This is similar to a {Biojs.Protein3D#formatSelectorVisible} with the 'false' argument.
690
+ *
691
+ * @example
692
+ * // Hides the format selector.
693
+ * mySequence.hideFormatSelector();
694
+ *
695
+ */
696
+ hideFormatSelector : function() {
697
+ this._headerDiv.hide();
698
+ },
699
+
700
+ /**
701
+ * Hides the whole component.
702
+ *
703
+ */
704
+ hide : function () {
705
+ this._headerDiv.hide();
706
+ this._contentDiv.hide();
707
+ },
708
+
709
+ /**
710
+ * Shows the whole component.
711
+ *
712
+ */
713
+ show : function () {
714
+ this._headerDiv.show();
715
+ this._contentDiv.show();
716
+ },
717
+ /*
718
+ * Function: Sequence._setSelection
719
+ * Purpose: Update the current selection.
720
+ * Returns: -
721
+ * Inputs: start -> {int} Start of the region to be selected.
722
+ * end -> {int} End of the region to be selected.
723
+ */
724
+ _setSelection : function(start, end) {
725
+ //alert("adsas");
726
+
727
+ var current = this.opt.selection;
728
+ var change = {};
729
+
730
+ // Which is the change on selection?
731
+ if ( current.start == start ) {
732
+ // forward?
733
+ if ( current.end < end ) {
734
+ change.start = current.end;
735
+ change.end = end;
736
+ } else {
737
+ this._restoreHighlights(end+1, current.end);
738
+ }
739
+ } else if ( current.end == end ) {
740
+ // forward?
741
+ if ( current.start > start ) {
742
+ change.start = start;
743
+ change.end = current.start;
744
+ } else {
745
+ this._restoreHighlights(current.start, start-1);
746
+ }
747
+ } else {
748
+ this._restoreHighlights(current.start, current.end);
749
+ change.start = start;
750
+ change.end = end;
751
+ }
752
+
753
+ current.start = start;
754
+ current.end = end;
755
+ },
756
+
757
+ /*
758
+ * Function: Sequence._repaintSelection
759
+ * Purpose: Repaint the whole current selection.
760
+ * Returns: -
761
+ * Inputs: -
762
+ */
763
+ _repaintSelection: function(){
764
+ var s = this.opt.selection;
765
+ this._setSelection(0,0);
766
+ this._setSelection(s.start,s.end);
767
+ },
768
+
769
+ /*
770
+ * Function: Sequence._redraw
771
+ * Purpose: Repaint the current sequence.
772
+ * Returns: -
773
+ * Inputs: -
774
+ */
775
+ _redraw : function() {
776
+ var i = 0;
777
+ var self = this;
778
+
779
+ // Reset the content
780
+ //this._contentDiv.text('');
781
+ this._contentDiv.children().remove();
782
+
783
+ // Rebuild the spans of the sequence
784
+ // according to format
785
+ if(this.opt.format == 'RAW') {
786
+ this._drawRaw();
787
+ } else if(this.opt.format == 'CODATA') {
788
+ this._drawCodata();
789
+ } else if (this.opt.format == 'FASTA'){
790
+ this._drawFasta();
791
+ } else {
792
+ this.opt.format = 'PRIDE';
793
+ this._drawPride();
794
+ }
795
+
796
+ // Restore the highlighted regions
797
+ this._applyHighlights(this._highlights);
798
+ this._repaintSelection();
799
+ this._addSpanEvents();
800
+ },
801
+ /*
802
+ * Function: Sequence._drawFasta
803
+ * Purpose: Repaint the current sequence using FASTA format.
804
+ * Returns: -
805
+ * Inputs: -
806
+ */
807
+ _drawFasta : function() {
808
+ var self = this;
809
+ var a = this.opt.sequence.toUpperCase().split('');
810
+ var pre = jQuery('<pre></pre>').appendTo(this._contentDiv);
811
+
812
+ var i = 1;
813
+ var arr = [];
814
+ var str = '>' + this.opt.id + ' ' + a.length + ' bp<br/>';
815
+
816
+ /* Correct column size in case the sequence is as small peptide */
817
+ var numCols = this.opt.columns.size;
818
+ if ( this.opt.sequence.length < this.opt.columns.size ) {
819
+ numCols = this.opt.sequence.length;
820
+ }
821
+
822
+ var opt = {
823
+ numCols: numCols,
824
+ numColsForSpace: 0
825
+ };
826
+
827
+ str += this._drawSequence(a, opt);
828
+ pre.html(str);
829
+
830
+ this._drawAnnotations(opt);
831
+ },
832
+ /*
833
+ * Function: Sequence._drawCodata
834
+ * Purpose: Repaint the current sequence using CODATA format.
835
+ * Returns: -
836
+ * Inputs: -
837
+ */
838
+ _drawCodata : function() {
839
+
840
+ var self = this;
841
+ var a = this.opt.sequence.toUpperCase().split('');
842
+ var pre = jQuery('<pre/>').appendTo(this._contentDiv);
843
+
844
+ var i = 0;
845
+ var str = 'ENTRY ' + this.opt.id + '<br/>';
846
+ str += 'SEQUENCE<br/>';
847
+ if ( this.opt.formatOptions !== undefined ){
848
+ if(this.opt.formatOptions.title !== undefined ){
849
+ if (this.opt.formatOptions.title == false) {
850
+ str = '';
851
+ }
852
+ }
853
+ }
854
+
855
+ /* Correct column size in case the sequence is as small peptide */
856
+ var numCols = this.opt.columns.size;
857
+ if ( this.opt.sequence.length < this.opt.columns.size ) {
858
+ numCols = this.opt.sequence.length;
859
+ }
860
+
861
+ var opt = {
862
+ numLeft: true,
863
+ numLeftSize: 7,
864
+ numLeftPad:' ',
865
+ numTop: true,
866
+ numTopEach: 5,
867
+ numCols: numCols,
868
+ numColsForSpace: 0,
869
+ spaceBetweenChars: true
870
+ };
871
+
872
+ str += this._drawSequence(a, opt);
873
+
874
+ var footer = '<br/>///';
875
+ if (this.opt.formatOptions !== undefined) {
876
+ if (this.opt.formatOptions.footer !== undefined) {
877
+ if (this.opt.formatOptions.footer == false) {
878
+ footer = '';
879
+ }
880
+ }
881
+ }
882
+ str += footer;
883
+ pre.html(str);
884
+
885
+ this._drawAnnotations(opt);
886
+ },
887
+ /*
888
+ * Function: Sequence._drawAnnotations
889
+ * Purpose: Paint the annotations on the sequence.
890
+ * Returns: -
891
+ * Inputs: settings -> {object}
892
+ */
893
+ _drawAnnotations: function ( settings ){
894
+
895
+ var self = this;
896
+ var a = this.opt.sequence.toLowerCase().split('');
897
+ var annotations = this._annotations;
898
+ var leftSpaces = '';
899
+ var row = '';
900
+ var annot = '';
901
+
902
+ // Index at the left?
903
+ if ( settings.numLeft ) {
904
+ leftSpaces += this._formatIndex(' ', settings.numLeftSize+2, ' ');
905
+ }
906
+
907
+ for ( var i = 0; i < a.length; i += settings.numCols ){
908
+ row = '';
909
+ for ( var key in annotations ){
910
+ annotations[key].id = this.getId() + "_" + key;
911
+ annot = this._getHTMLRowAnnot(i+1, annotations[key], settings);
912
+ if (annot.length > 0) {
913
+ row += '<br/>';
914
+ row += leftSpaces;
915
+ row += annot;
916
+ row += '<br/>';
917
+ }
918
+ }
919
+
920
+ var numCols = settings.numCols;
921
+ var charRemaining = a.length-i;
922
+ if(charRemaining < numCols){
923
+ numCols = charRemaining;
924
+ }
925
+
926
+ if ( settings.numRight ) {
927
+ jQuery(row).insertAfter('div#'+self.opt.target+' div pre span#numRight_' + this.getId() + '_' + (i + numCols) );
928
+ } else {
929
+ jQuery(row).insertAfter('div#'+self.opt.target+' div pre span#'+ this.getId() + '_' + (i + numCols) );
930
+ }
931
+ }
932
+
933
+ // add tool tips and background' coloring effect
934
+ jQuery(this._contentDiv).find('.annotation').each( function(){
935
+ self._addToolTip( this, function() {
936
+ return self._getAnnotationString( jQuery(this).attr("id") );
937
+ });
938
+
939
+ jQuery(this).mouseover(function(e) {
940
+ jQuery('.annotation.'+jQuery(e.target).attr("id")).each(function(){
941
+ jQuery(this).css("background-color", jQuery(this).attr("color") );
942
+ });
943
+ }).mouseout(function() {
944
+ jQuery('.annotation').css("background-color", "transparent");
945
+
946
+ }).click(function(e) {
947
+ var name = undefined;
948
+ var id = jQuery(e.target).attr("id");
949
+ for(var i =0; i < self._annotations.length;i++){
950
+ if(self._annotations[i].id == id){
951
+ name = self._annotations[i].name;
952
+ continue;
953
+ }
954
+ }
955
+ self.trigger( EVT_ON_ANNOTATION_CLICKED, {
956
+ "name": name,
957
+ //"pos": parseInt( jQuery(e.target).attr("pos") )
958
+ });
959
+ });
960
+
961
+ });
962
+
963
+ },
964
+ /*
965
+ * Function: Sequence._getAnnotationString
966
+ * Purpose: Get the annotation text message for the tooltip
967
+ * Returns: {string} Annotation text for the annotation
968
+ * Inputs: id -> {int} index of the internal annotation array
969
+ */
970
+ _getAnnotationString: function ( id ) {
971
+ var annotation = this._annotations[id.substr(id.indexOf("_") + 1)];
972
+ return annotation.name + "<br/>" + ((annotation.html)? annotation.html : '');
973
+ },
974
+
975
+ /*
976
+ * Function: Sequence._getHTMLRowAnnot
977
+ * Purpose: Build an annotation
978
+ * Returns: HTML of the annotation
979
+ * Inputs: currentPos -> {int}
980
+ * annotation -> {Object}
981
+ * settings -> {Object}
982
+ */
983
+ _getHTMLRowAnnot : function (currentPos, annotation, settings) {
984
+ var styleBegin = 'border-left:1px solid; border-bottom:1px solid; border-color:';
985
+ var styleOn = 'border-bottom:1px solid; border-color:';
986
+ var styleEnd = 'border-bottom:1px solid; border-right:1px solid; border-color:';
987
+ var styleBeginAndEnd = 'border-left:1px solid; border-right:1px solid; border-bottom:1px solid; border-color:';
988
+
989
+ var row = [];
990
+ var end = (currentPos + settings.numCols);
991
+ var spaceBetweenChars = (settings.spaceBetweenChars)? ' ' : '';
992
+ var defaultColor = annotation.color;
993
+ var id = annotation.id;
994
+ for ( var pos=currentPos; pos < end ; pos++ ) {
995
+ // regions
996
+ for ( var r in annotation.regions ) {
997
+ region = annotation.regions[r];
998
+
999
+ spaceAfter = '';
1000
+ spaceAfter += (pos % settings.numColsForSpace == 0 )? ' ' : '';
1001
+ spaceAfter += spaceBetweenChars;
1002
+
1003
+ color = ((region.color)? region.color : defaultColor);
1004
+ data = 'class="annotation '+id+'" id="'+id+'" color="'+color+'" pos="'+pos+'"';
1005
+
1006
+ if ( pos == region.start && pos == region.end) {
1007
+ row[pos] = '<span style="'+styleBeginAndEnd+color+'" '+data+'> ';
1008
+ row[pos] += spaceAfter;
1009
+ row[pos] += '</span>';
1010
+ } else if ( pos == region.start ) {
1011
+ row[pos] = '<span style="'+styleBegin+color+'" '+data+'> ';
1012
+ row[pos] += spaceAfter;
1013
+ row[pos] += '</span>';
1014
+ } else if ( pos == region.end ) {
1015
+ row[pos] = '<span style="'+styleEnd+color+' " '+data+'> ';
1016
+ //row[pos] += spaceAfter;
1017
+ row[pos] += '</span>';
1018
+ } else if ( pos > region.start && pos < region.end ) {
1019
+ row[pos] = '<span style="'+styleOn+color+'" '+data+'> ';
1020
+ row[pos] += spaceAfter;
1021
+ row[pos] += '</span>';
1022
+ } else if (!row[pos]) {
1023
+ row[pos] = ' ';
1024
+ row[pos] += spaceAfter;
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ var str = row.join("");
1030
+
1031
+ return ( str.indexOf("span") == -1 )? "" : str;
1032
+ },
1033
+ /*
1034
+ * Function: Sequence._drawRaw
1035
+ * Purpose: Repaint the current sequence using RAW format.
1036
+ * Returns: -
1037
+ * Inputs: -
1038
+ */
1039
+ _drawRaw : function() {
1040
+ var self = this;
1041
+ var a = this.opt.sequence.toLowerCase().split('');
1042
+ var i = 0;
1043
+ var arr = [];
1044
+ var pre = jQuery('<pre></pre>').appendTo(this._contentDiv);
1045
+
1046
+ /* Correct column size in case the sequence is as small peptide */
1047
+ var numCols = this.opt.columns.size;
1048
+ if ( this.opt.sequence.length < this.opt.columns.size ) {
1049
+ numCols = this.opt.sequence.length;
1050
+ }
1051
+
1052
+ var opt = {
1053
+ numCols: numCols
1054
+ };
1055
+
1056
+ pre.html(
1057
+ this._drawSequence(a, opt)
1058
+ );
1059
+
1060
+ this._drawAnnotations(opt);
1061
+ },
1062
+ /*
1063
+ * Function: Sequence._drawPride
1064
+ * Purpose: Repaint the current sequence using PRIDE format.
1065
+ * Returns: -
1066
+ * Inputs: -
1067
+ */
1068
+ _drawPride : function() {
1069
+ var self = this;
1070
+ var a = this.opt.sequence.toUpperCase().split('');
1071
+ var pre = jQuery('<pre></pre>').appendTo(this._contentDiv);
1072
+
1073
+ /* Correct column size in case the sequence is as small peptide */
1074
+ var numCols = this.opt.columns.size;
1075
+ if ( this.opt.sequence.length < this.opt.columns.size ) {
1076
+ numCols = this.opt.sequence.length;
1077
+ }
1078
+
1079
+ opt = {
1080
+ numLeft: true,
1081
+ numLeftSize: 5,
1082
+ numLeftPad:'0',
1083
+ numRight: true,
1084
+ numRightSize: 5,
1085
+ numRightPad: '0',
1086
+ numCols: numCols,
1087
+ numColsForSpace: self.opt.columns.spacedEach
1088
+ };
1089
+
1090
+ pre.html(
1091
+ this._drawSequence(a, opt)
1092
+ );
1093
+
1094
+ this._drawAnnotations(opt);
1095
+ },
1096
+ /*
1097
+ * Function: Sequence._drawSequence
1098
+ * Purpose: Repaint the current sequence using CUSTOM format.
1099
+ * Returns: -
1100
+ * Inputs: a -> {char[]} a The sequence strand.
1101
+ * opt -> {Object} opt The CUSTOM format.
1102
+ */
1103
+ _drawSequence : function(a, opt) {
1104
+ var str = '';
1105
+
1106
+ // Index at top?
1107
+ if( opt.numTop )
1108
+ {
1109
+ str += '<span class="numTop pos-marker">'
1110
+ var size = (opt.spaceBetweenChars)? opt.numTopEach*2: opt.numTopEach;
1111
+
1112
+ if (opt.numLeft) {
1113
+ str += this._formatIndex(' ', opt.numLeftSize, ' ');
1114
+ }
1115
+
1116
+ str += this._formatIndex(' ', size, ' ');
1117
+
1118
+ for(var x = opt.numTopEach; x < opt.numCols; x += opt.numTopEach) {
1119
+ str += this._formatIndex(x, size, ' ', true);
1120
+ }
1121
+ str += '</span>'
1122
+ }
1123
+
1124
+
1125
+ // Index at the left?
1126
+ if (opt.numLeft) {
1127
+ str += '<span id="numLeft_' + this.getId() + '_' + 0 + '"';
1128
+ str += 'class="pos-marker">'
1129
+ str += this._formatIndex(1, opt.numLeftSize, opt.numLeftPad);
1130
+ str += ' ';
1131
+ str += '</span>';
1132
+ }
1133
+
1134
+ var j=1;
1135
+ for (var i=1; i <= a.length; i++) {
1136
+
1137
+ if( i % opt.numCols == 0) {
1138
+ str += '<span class="sequence" id="' + this.getId() + '_' + i + '">' + a[i-1] + '</span>';
1139
+
1140
+ if (opt.numRight) {
1141
+ str += '<span id="numRight_' + this.getId() + '_' + i + '"';
1142
+ str += 'class="pos-marker">'
1143
+ str += ' ';
1144
+ str += this._formatIndex(i, opt.numRightSize, opt.numRightPad);
1145
+ str += '</span>';
1146
+ }
1147
+
1148
+ str += '<br/>';
1149
+
1150
+ var aaRemaining = a.length - i;
1151
+ if (opt.numLeft && aaRemaining > 0) {
1152
+ str += '<span id="numLeft_' + this.getId() + '_' + i + '"';
1153
+ str += 'class="pos-marker">'
1154
+ str += this._formatIndex(i+1, opt.numLeftSize, opt.numLeftPad);
1155
+ str += ' ';
1156
+ str += '</span>';
1157
+ }
1158
+
1159
+ j = 1;
1160
+
1161
+ } else {
1162
+ str += '<span class="sequence" id="' + this.getId() + '_' + i + '">' + a[i-1];
1163
+ str += ( j % opt.numColsForSpace == 0)? ' ' : '';
1164
+ str += (opt.spaceBetweenChars)? ' ' : '';
1165
+ str += '</span>';
1166
+ j++;
1167
+ }
1168
+ }
1169
+
1170
+ str += '<br/>'
1171
+
1172
+ if (jQuery.browser.msie) {
1173
+ str = "<pre>" + str + "</pre>";
1174
+ }
1175
+
1176
+ return str;
1177
+ },
1178
+ /*
1179
+ * Function: Sequence._formatIndex
1180
+ * Purpose: Build the HTML corresponding to counting numbers (top, left, right) in the strand.
1181
+ * Returns: -
1182
+ * Inputs: number -> {int} The number
1183
+ * size -> {int} Number of bins to suit the number.
1184
+ * fillingChar -> {char} Character to be used for filling out blank bins.
1185
+ * alignLeft -> {bool} Tell if aligned to the left.
1186
+ */
1187
+ _formatIndex : function( number, size, fillingChar, alignLeft) {
1188
+ var str = number.toString();
1189
+ var filling = '';
1190
+ var padding = size - str.length;
1191
+ if ( padding > 0 ) {
1192
+ while ( padding-- > 0 ) {
1193
+ filling += ("<span>"+fillingChar+"</span>");
1194
+ }
1195
+ if (alignLeft){
1196
+ str = number+filling;
1197
+ } else {
1198
+ str = filling+number;
1199
+ }
1200
+ }
1201
+ return str;
1202
+ },
1203
+ /*
1204
+ * Function: Sequence._addSpanEvents
1205
+ * Purpose: Add the event handlers to the strand.
1206
+ * Returns: -
1207
+ * Inputs: -
1208
+ */
1209
+ _addSpanEvents : function() {
1210
+ var self = this;
1211
+ var isMouseDown = false;
1212
+ var clickPos;
1213
+ var currentPos;
1214
+
1215
+ self._contentDiv.find('.sequence').each( function () {
1216
+
1217
+ // Register the starting position
1218
+ jQuery(this).mousedown(function() {
1219
+ var id = jQuery(this).attr('id');
1220
+ currentPos = parseInt(id.substr(id.indexOf("_") + 1));
1221
+ clickPos = currentPos;
1222
+ self._setSelection(clickPos,currentPos);
1223
+ isMouseDown = true;
1224
+
1225
+ // Selection is happening, raise an event
1226
+ self.trigger(
1227
+ EVT_ON_SELECTION_CHANGE,
1228
+ {
1229
+ "start" : self.opt.selection.start,
1230
+ "end" : self.opt.selection.end
1231
+ }
1232
+ );
1233
+
1234
+ }).mouseover(function() {
1235
+ // Update selection
1236
+ // Show tooltip containing the position
1237
+ var id = jQuery(this).attr('id');
1238
+ currentPos = parseInt(id.substr(id.indexOf("_") + 1));
1239
+
1240
+ if(isMouseDown) {
1241
+ if( currentPos > clickPos ) {
1242
+ self._setSelection(clickPos, currentPos);
1243
+ } else {
1244
+ self._setSelection(currentPos, clickPos);
1245
+ }
1246
+
1247
+ // Selection is happening, raise an event
1248
+ self.trigger( EVT_ON_SELECTION_CHANGE, {
1249
+ "start" : self.opt.selection.start,
1250
+ "end" : self.opt.selection.end
1251
+ });
1252
+ }
1253
+
1254
+ }).mouseup(function() {
1255
+ isMouseDown = false;
1256
+ // Selection is done, raise an event
1257
+ self.trigger( EVT_ON_SELECTION_CHANGED, {
1258
+ "start" : self.opt.selection.start,
1259
+ "end" : self.opt.selection.end
1260
+ });
1261
+ });
1262
+
1263
+ // Add a tooltip for this sequence base.
1264
+ self._addToolTip.call( self, this, function( ) {
1265
+ if (isMouseDown) {
1266
+ return "[" + self.opt.selection.start +", " + self.opt.selection.end + "]";
1267
+ } else {
1268
+ return currentPos;
1269
+ }
1270
+ });
1271
+
1272
+ })
1273
+ .css('cursor', 'pointer');
1274
+ },
1275
+ /*
1276
+ * Function: Sequence._addTooltip
1277
+ * Purpose: Add a tooltip around the target DOM element provided as argument
1278
+ * Returns: -
1279
+ * Inputs: target -> {Element} DOM element wich is the targeted focus for the tooltip.
1280
+ * cbGetMessageFunction -> {function} A callback function wich returns the message to be displayed in the tip.
1281
+ */
1282
+ _addToolTip : function ( target, cbGetMessageFunction ) {
1283
+
1284
+ var tipId = this.opt._tooltip;
1285
+
1286
+ jQuery(target).mouseover(function(e) {
1287
+
1288
+ var offset = jQuery(e.target).offset();
1289
+
1290
+ if ( ! jQuery( tipId ).is(':visible') ) {
1291
+ jQuery( tipId )
1292
+ .css({
1293
+ 'background-color': "#000",
1294
+ 'padding': "3px 10px 3px 10px",
1295
+ 'top': offset.top + jQuery(e.target).height() + "px",
1296
+ 'left': offset.left + jQuery(e.target).width() + "px"
1297
+ })
1298
+ .animate( {opacity: '0.85'}, 10)
1299
+ .html( cbGetMessageFunction.call( target ) )
1300
+ .show();
1301
+ }
1302
+
1303
+ }).mouseout(function() {
1304
+ //Remove the appended tooltip template
1305
+ jQuery( tipId ).hide();
1306
+ });
1307
+ },
1308
+
1309
+ /**
1310
+ * Annotate a set of intervals provided in the argument.
1311
+ *
1312
+ * @example
1313
+ * // Annotations using regions with different colors.
1314
+ * mySequence.addAnnotation({
1315
+ * name:"UNIPROT",
1316
+ * html:"&lt;br&gt; Example of &lt;b&gt;HTML&lt;/b&gt;",
1317
+ * color:"green",
1318
+ * regions: [
1319
+ * {start: 540, end: 560},
1320
+ * {start: 561, end:580, color: "#FFA010"},
1321
+ * {start: 581, end:590, color: "red"},
1322
+ * {start: 690, end:710}]
1323
+ * });
1324
+ *
1325
+ *
1326
+ * @param {Object} annotation The intervals belonging to the same annotation.
1327
+ * Syntax: { name: &lt;value&gt;, color: &lt;HTMLColorCode&gt;, html: &lt;HTMLString&gt;, regions: [{ start: &lt;startVal1&gt;, end: &lt;endVal1&gt;}, ..., { start: &lt;startValN&gt;, end: &lt;endValN&gt;}] }
1328
+ */
1329
+ addAnnotation: function ( annotation ) {
1330
+ this._annotations.push(annotation);
1331
+ this._redraw();
1332
+ },
1333
+
1334
+ /**
1335
+ * Removes an annotation by means of its name.
1336
+ *
1337
+ * @example
1338
+ * // Remove the UNIPROT annotation.
1339
+ * mySequence.removeAnnotation('UNIPROT');
1340
+ *
1341
+ * @param {string} name The name of the annotation to be removed.
1342
+ *
1343
+ */
1344
+ removeAnnotation: function ( name ) {
1345
+ for (var i=0; i < this._annotations.length ; i++ ){
1346
+ if(name != this._annotations[i].name){
1347
+ this._annotations.splice(i,1);
1348
+ this._redraw();
1349
+ break;
1350
+ }
1351
+ }
1352
+ },
1353
+ /**
1354
+ * Removes all the current annotations.
1355
+ *
1356
+ * @example
1357
+ * mySequence.removeAllAnnotations();
1358
+ *
1359
+ */
1360
+ removeAllAnnotations: function () {
1361
+ this._annotations = [];
1362
+ this._redraw();
1363
+ },
1364
+
1365
+
1366
+ });
1367
+
1368
+ require("biojs-events").mixin(Sequence.prototype);
1369
+ module.exports = Sequence;
1370
+
1371
+ },{"biojs-events":2,"jquery-browser-plugin":20,"js-class":22}],2:[function(require,module,exports){
1372
+ var events = require("backbone-events-standalone");
1373
+
1374
+ events.onAll = function(callback,context){
1375
+ this.on("all", callback,context);
1376
+ return this;
1377
+ };
1378
+
1379
+ // Mixin utility
1380
+ events.oldMixin = events.mixin;
1381
+ events.mixin = function(proto) {
1382
+ events.oldMixin(proto);
1383
+ // add custom onAll
1384
+ var exports = ['onAll'];
1385
+ for(var i=0; i < exports.length;i++){
1386
+ var name = exports[i];
1387
+ proto[name] = this[name];
1388
+ }
1389
+ return proto;
1390
+ };
1391
+
1392
+ module.exports = events;
1393
+
1394
+ },{"backbone-events-standalone":4}],3:[function(require,module,exports){
1395
+ /**
1396
+ * Standalone extraction of Backbone.Events, no external dependency required.
1397
+ * Degrades nicely when Backone/underscore are already available in the current
1398
+ * global context.
1399
+ *
1400
+ * Note that docs suggest to use underscore's `_.extend()` method to add Events
1401
+ * support to some given object. A `mixin()` method has been added to the Events
1402
+ * prototype to avoid using underscore for that sole purpose:
1403
+ *
1404
+ * var myEventEmitter = BackboneEvents.mixin({});
1405
+ *
1406
+ * Or for a function constructor:
1407
+ *
1408
+ * function MyConstructor(){}
1409
+ * MyConstructor.prototype.foo = function(){}
1410
+ * BackboneEvents.mixin(MyConstructor.prototype);
1411
+ *
1412
+ * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
1413
+ * (c) 2013 Nicolas Perriault
1414
+ */
1415
+ /* global exports:true, define, module */
1416
+ (function() {
1417
+ var root = this,
1418
+ breaker = {},
1419
+ nativeForEach = Array.prototype.forEach,
1420
+ hasOwnProperty = Object.prototype.hasOwnProperty,
1421
+ slice = Array.prototype.slice,
1422
+ idCounter = 0;
1423
+
1424
+ // Returns a partial implementation matching the minimal API subset required
1425
+ // by Backbone.Events
1426
+ function miniscore() {
1427
+ return {
1428
+ keys: Object.keys || function (obj) {
1429
+ if (typeof obj !== "object" && typeof obj !== "function" || obj === null) {
1430
+ throw new TypeError("keys() called on a non-object");
1431
+ }
1432
+ var key, keys = [];
1433
+ for (key in obj) {
1434
+ if (obj.hasOwnProperty(key)) {
1435
+ keys[keys.length] = key;
1436
+ }
1437
+ }
1438
+ return keys;
1439
+ },
1440
+
1441
+ uniqueId: function(prefix) {
1442
+ var id = ++idCounter + '';
1443
+ return prefix ? prefix + id : id;
1444
+ },
1445
+
1446
+ has: function(obj, key) {
1447
+ return hasOwnProperty.call(obj, key);
1448
+ },
1449
+
1450
+ each: function(obj, iterator, context) {
1451
+ if (obj == null) return;
1452
+ if (nativeForEach && obj.forEach === nativeForEach) {
1453
+ obj.forEach(iterator, context);
1454
+ } else if (obj.length === +obj.length) {
1455
+ for (var i = 0, l = obj.length; i < l; i++) {
1456
+ if (iterator.call(context, obj[i], i, obj) === breaker) return;
1457
+ }
1458
+ } else {
1459
+ for (var key in obj) {
1460
+ if (this.has(obj, key)) {
1461
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
1462
+ }
1463
+ }
1464
+ }
1465
+ },
1466
+
1467
+ once: function(func) {
1468
+ var ran = false, memo;
1469
+ return function() {
1470
+ if (ran) return memo;
1471
+ ran = true;
1472
+ memo = func.apply(this, arguments);
1473
+ func = null;
1474
+ return memo;
1475
+ };
1476
+ }
1477
+ };
1478
+ }
1479
+
1480
+ var _ = miniscore(), Events;
1481
+
1482
+ // Backbone.Events
1483
+ // ---------------
1484
+
1485
+ // A module that can be mixed in to *any object* in order to provide it with
1486
+ // custom events. You may bind with `on` or remove with `off` callback
1487
+ // functions to an event; `trigger`-ing an event fires all callbacks in
1488
+ // succession.
1489
+ //
1490
+ // var object = {};
1491
+ // _.extend(object, Backbone.Events);
1492
+ // object.on('expand', function(){ alert('expanded'); });
1493
+ // object.trigger('expand');
1494
+ //
1495
+ Events = {
1496
+
1497
+ // Bind an event to a `callback` function. Passing `"all"` will bind
1498
+ // the callback to all events fired.
1499
+ on: function(name, callback, context) {
1500
+ if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
1501
+ this._events || (this._events = {});
1502
+ var events = this._events[name] || (this._events[name] = []);
1503
+ events.push({callback: callback, context: context, ctx: context || this});
1504
+ return this;
1505
+ },
1506
+
1507
+ // Bind an event to only be triggered a single time. After the first time
1508
+ // the callback is invoked, it will be removed.
1509
+ once: function(name, callback, context) {
1510
+ if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
1511
+ var self = this;
1512
+ var once = _.once(function() {
1513
+ self.off(name, once);
1514
+ callback.apply(this, arguments);
1515
+ });
1516
+ once._callback = callback;
1517
+ return this.on(name, once, context);
1518
+ },
1519
+
1520
+ // Remove one or many callbacks. If `context` is null, removes all
1521
+ // callbacks with that function. If `callback` is null, removes all
1522
+ // callbacks for the event. If `name` is null, removes all bound
1523
+ // callbacks for all events.
1524
+ off: function(name, callback, context) {
1525
+ var retain, ev, events, names, i, l, j, k;
1526
+ if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
1527
+ if (!name && !callback && !context) {
1528
+ this._events = {};
1529
+ return this;
1530
+ }
1531
+
1532
+ names = name ? [name] : _.keys(this._events);
1533
+ for (i = 0, l = names.length; i < l; i++) {
1534
+ name = names[i];
1535
+ if (events = this._events[name]) {
1536
+ this._events[name] = retain = [];
1537
+ if (callback || context) {
1538
+ for (j = 0, k = events.length; j < k; j++) {
1539
+ ev = events[j];
1540
+ if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
1541
+ (context && context !== ev.context)) {
1542
+ retain.push(ev);
1543
+ }
1544
+ }
1545
+ }
1546
+ if (!retain.length) delete this._events[name];
1547
+ }
1548
+ }
1549
+
1550
+ return this;
1551
+ },
1552
+
1553
+ // Trigger one or many events, firing all bound callbacks. Callbacks are
1554
+ // passed the same arguments as `trigger` is, apart from the event name
1555
+ // (unless you're listening on `"all"`, which will cause your callback to
1556
+ // receive the true name of the event as the first argument).
1557
+ trigger: function(name) {
1558
+ if (!this._events) return this;
1559
+ var args = slice.call(arguments, 1);
1560
+ if (!eventsApi(this, 'trigger', name, args)) return this;
1561
+ var events = this._events[name];
1562
+ var allEvents = this._events.all;
1563
+ if (events) triggerEvents(events, args);
1564
+ if (allEvents) triggerEvents(allEvents, arguments);
1565
+ return this;
1566
+ },
1567
+
1568
+ // Tell this object to stop listening to either specific events ... or
1569
+ // to every object it's currently listening to.
1570
+ stopListening: function(obj, name, callback) {
1571
+ var listeners = this._listeners;
1572
+ if (!listeners) return this;
1573
+ var deleteListener = !name && !callback;
1574
+ if (typeof name === 'object') callback = this;
1575
+ if (obj) (listeners = {})[obj._listenerId] = obj;
1576
+ for (var id in listeners) {
1577
+ listeners[id].off(name, callback, this);
1578
+ if (deleteListener) delete this._listeners[id];
1579
+ }
1580
+ return this;
1581
+ }
1582
+
1583
+ };
1584
+
1585
+ // Regular expression used to split event strings.
1586
+ var eventSplitter = /\s+/;
1587
+
1588
+ // Implement fancy features of the Events API such as multiple event
1589
+ // names `"change blur"` and jQuery-style event maps `{change: action}`
1590
+ // in terms of the existing API.
1591
+ var eventsApi = function(obj, action, name, rest) {
1592
+ if (!name) return true;
1593
+
1594
+ // Handle event maps.
1595
+ if (typeof name === 'object') {
1596
+ for (var key in name) {
1597
+ obj[action].apply(obj, [key, name[key]].concat(rest));
1598
+ }
1599
+ return false;
1600
+ }
1601
+
1602
+ // Handle space separated event names.
1603
+ if (eventSplitter.test(name)) {
1604
+ var names = name.split(eventSplitter);
1605
+ for (var i = 0, l = names.length; i < l; i++) {
1606
+ obj[action].apply(obj, [names[i]].concat(rest));
1607
+ }
1608
+ return false;
1609
+ }
1610
+
1611
+ return true;
1612
+ };
1613
+
1614
+ // A difficult-to-believe, but optimized internal dispatch function for
1615
+ // triggering events. Tries to keep the usual cases speedy (most internal
1616
+ // Backbone events have 3 arguments).
1617
+ var triggerEvents = function(events, args) {
1618
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
1619
+ switch (args.length) {
1620
+ case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
1621
+ case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
1622
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
1623
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
1624
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
1625
+ }
1626
+ };
1627
+
1628
+ var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
1629
+
1630
+ // Inversion-of-control versions of `on` and `once`. Tell *this* object to
1631
+ // listen to an event in another object ... keeping track of what it's
1632
+ // listening to.
1633
+ _.each(listenMethods, function(implementation, method) {
1634
+ Events[method] = function(obj, name, callback) {
1635
+ var listeners = this._listeners || (this._listeners = {});
1636
+ var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
1637
+ listeners[id] = obj;
1638
+ if (typeof name === 'object') callback = this;
1639
+ obj[implementation](name, callback, this);
1640
+ return this;
1641
+ };
1642
+ });
1643
+
1644
+ // Aliases for backwards compatibility.
1645
+ Events.bind = Events.on;
1646
+ Events.unbind = Events.off;
1647
+
1648
+ // Mixin utility
1649
+ Events.mixin = function(proto) {
1650
+ var exports = ['on', 'once', 'off', 'trigger', 'stopListening', 'listenTo',
1651
+ 'listenToOnce', 'bind', 'unbind'];
1652
+ _.each(exports, function(name) {
1653
+ proto[name] = this[name];
1654
+ }, this);
1655
+ return proto;
1656
+ };
1657
+
1658
+ // Export Events as BackboneEvents depending on current context
1659
+ if (typeof define === "function") {
1660
+ define(function() {
1661
+ return Events;
1662
+ });
1663
+ } else if (typeof exports !== 'undefined') {
1664
+ if (typeof module !== 'undefined' && module.exports) {
1665
+ exports = module.exports = Events;
1666
+ }
1667
+ exports.BackboneEvents = Events;
1668
+ } else {
1669
+ root.BackboneEvents = Events;
1670
+ }
1671
+ })(this);
1672
+
1673
+ },{}],4:[function(require,module,exports){
1674
+ module.exports = require('./backbone-events-standalone');
1675
+
1676
+ },{"./backbone-events-standalone":3}],5:[function(require,module,exports){
1677
+ // Generated by CoffeeScript 1.8.0
1678
+ var GenericReader, xhr;
1679
+
1680
+ xhr = require('nets');
1681
+
1682
+ module.exports = GenericReader = (function() {
1683
+ function GenericReader() {}
1684
+
1685
+ GenericReader.read = function(url, callback) {
1686
+ var onret;
1687
+ onret = (function(_this) {
1688
+ return function(err, response, text) {
1689
+ return _this._onRetrieval(text, callback);
1690
+ };
1691
+ })(this);
1692
+ return xhr(url, onret);
1693
+ };
1694
+
1695
+ GenericReader._onRetrieval = function(text, callback) {
1696
+ var rText;
1697
+ rText = this.parse(text);
1698
+ return callback(rText);
1699
+ };
1700
+
1701
+ return GenericReader;
1702
+
1703
+ })();
1704
+
1705
+ },{"nets":12}],6:[function(require,module,exports){
1706
+ // Generated by CoffeeScript 1.8.0
1707
+ var Fasta, GenericReader, Seq, Str,
1708
+ __hasProp = {}.hasOwnProperty,
1709
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1710
+
1711
+ Str = require("./strings");
1712
+
1713
+ GenericReader = require("./generic_reader");
1714
+
1715
+ Seq = require("biojs-model").seq;
1716
+
1717
+ module.exports = Fasta = (function(_super) {
1718
+ __extends(Fasta, _super);
1719
+
1720
+ function Fasta() {
1721
+ return Fasta.__super__.constructor.apply(this, arguments);
1722
+ }
1723
+
1724
+ Fasta.parse = function(text) {
1725
+ var currentSeq, database, databaseID, identifiers, k, label, line, seqs, _i, _len;
1726
+ seqs = [];
1727
+ if (Object.prototype.toString.call(text) !== '[object Array]') {
1728
+ text = text.split("\n");
1729
+ }
1730
+ for (_i = 0, _len = text.length; _i < _len; _i++) {
1731
+ line = text[_i];
1732
+ if (line[0] === ">" || line[0] === ";") {
1733
+ label = line.slice(1);
1734
+ currentSeq = new Seq("", label, seqs.length);
1735
+ seqs.push(currentSeq);
1736
+ if (Str.contains("|", line)) {
1737
+ identifiers = label.split("|");
1738
+ k = 1;
1739
+ while (k < identifiers.length) {
1740
+ database = identifiers[k];
1741
+ databaseID = identifiers[k + 1];
1742
+ currentSeq.meta[database] = databaseID;
1743
+ k += 2;
1744
+ }
1745
+ currentSeq.name = identifiers[identifiers.length - 1];
1746
+ }
1747
+ } else {
1748
+ currentSeq.seq += line;
1749
+ }
1750
+ }
1751
+ return seqs;
1752
+ };
1753
+
1754
+ return Fasta;
1755
+
1756
+ })(GenericReader);
1757
+
1758
+ },{"./generic_reader":5,"./strings":7,"biojs-model":10}],7:[function(require,module,exports){
1759
+ // Generated by CoffeeScript 1.8.0
1760
+ var strings;
1761
+
1762
+ strings = {
1763
+ contains: function(text, search) {
1764
+ return ''.indexOf.call(text, search, 0) !== -1;
1765
+ }
1766
+ };
1767
+
1768
+ module.exports = strings;
1769
+
1770
+ },{}],8:[function(require,module,exports){
1771
+ // Generated by CoffeeScript 1.8.0
1772
+ var Utils;
1773
+
1774
+ Utils = {};
1775
+
1776
+ Utils.splitNChars = function(txt, num) {
1777
+ var i, result, _i, _ref;
1778
+ result = [];
1779
+ for (i = _i = 0, _ref = txt.length - 1; num > 0 ? _i <= _ref : _i >= _ref; i = _i += num) {
1780
+ result.push(txt.substr(i, num));
1781
+ }
1782
+ return result;
1783
+ };
1784
+
1785
+ module.exports = Utils;
1786
+
1787
+ },{}],9:[function(require,module,exports){
1788
+ // Generated by CoffeeScript 1.8.0
1789
+ var FastaExporter, Utils;
1790
+
1791
+ Utils = require("./utils");
1792
+
1793
+ module.exports = FastaExporter = (function() {
1794
+ function FastaExporter() {}
1795
+
1796
+ FastaExporter["export"] = function(seqs, access) {
1797
+ var seq, text, _i, _len;
1798
+ text = "";
1799
+ for (_i = 0, _len = seqs.length; _i < _len; _i++) {
1800
+ seq = seqs[_i];
1801
+ if (access != null) {
1802
+ seq = access(seq);
1803
+ }
1804
+ text += ">" + seq.name + "\n";
1805
+ text += (Utils.splitNChars(seq.seq, 80)).join("\n");
1806
+ text += "\n";
1807
+ }
1808
+ return text;
1809
+ };
1810
+
1811
+ return FastaExporter;
1812
+
1813
+ })();
1814
+
1815
+ },{"./utils":8}],10:[function(require,module,exports){
1816
+ module.exports.seq = require("./seq");
1817
+
1818
+ },{"./seq":11}],11:[function(require,module,exports){
1819
+ module.exports = function(seq, name, id) {
1820
+ this.seq = seq;
1821
+ this.name = name;
1822
+ this.id = id;
1823
+ this.meta = {};
1824
+ };
1825
+
1826
+ },{}],12:[function(require,module,exports){
1827
+ var req = require('request')
1828
+
1829
+ module.exports = Nets
1830
+
1831
+ function Nets(uri, opts, cb) {
1832
+ req(uri, opts, cb)
1833
+ }
1834
+ },{"request":13}],13:[function(require,module,exports){
1835
+ var window = require("global/window")
1836
+ var once = require("once")
1837
+ var parseHeaders = require('parse-headers')
1838
+
1839
+ var messages = {
1840
+ "0": "Internal XMLHttpRequest Error",
1841
+ "4": "4xx Client Error",
1842
+ "5": "5xx Server Error"
1843
+ }
1844
+
1845
+ var XHR = window.XMLHttpRequest || noop
1846
+ var XDR = "withCredentials" in (new XHR()) ? XHR : window.XDomainRequest
1847
+
1848
+ module.exports = createXHR
1849
+
1850
+ function createXHR(options, callback) {
1851
+ if (typeof options === "string") {
1852
+ options = { uri: options }
1853
+ }
1854
+
1855
+ options = options || {}
1856
+ callback = once(callback)
1857
+
1858
+ var xhr = options.xhr || null
1859
+
1860
+ if (!xhr) {
1861
+ if (options.cors || options.useXDR) {
1862
+ xhr = new XDR()
1863
+ }else{
1864
+ xhr = new XHR()
1865
+ }
1866
+ }
1867
+
1868
+ var uri = xhr.url = options.uri || options.url
1869
+ var method = xhr.method = options.method || "GET"
1870
+ var body = options.body || options.data
1871
+ var headers = xhr.headers = options.headers || {}
1872
+ var sync = !!options.sync
1873
+ var isJson = false
1874
+ var key
1875
+ var load = options.response ? loadResponse : loadXhr
1876
+
1877
+ if ("json" in options) {
1878
+ isJson = true
1879
+ headers["Accept"] = "application/json"
1880
+ if (method !== "GET" && method !== "HEAD") {
1881
+ headers["Content-Type"] = "application/json"
1882
+ body = JSON.stringify(options.json)
1883
+ }
1884
+ }
1885
+
1886
+ xhr.onreadystatechange = readystatechange
1887
+ xhr.onload = load
1888
+ xhr.onerror = error
1889
+ // IE9 must have onprogress be set to a unique function.
1890
+ xhr.onprogress = function () {
1891
+ // IE must die
1892
+ }
1893
+ // hate IE
1894
+ xhr.ontimeout = noop
1895
+ xhr.open(method, uri, !sync)
1896
+ //backward compatibility
1897
+ if (options.withCredentials || (options.cors && options.withCredentials !== false)) {
1898
+ xhr.withCredentials = true
1899
+ }
1900
+
1901
+ // Cannot set timeout with sync request
1902
+ if (!sync) {
1903
+ xhr.timeout = "timeout" in options ? options.timeout : 5000
1904
+ }
1905
+
1906
+ if (xhr.setRequestHeader) {
1907
+ for(key in headers){
1908
+ if(headers.hasOwnProperty(key)){
1909
+ xhr.setRequestHeader(key, headers[key])
1910
+ }
1911
+ }
1912
+ } else if (options.headers) {
1913
+ throw new Error("Headers cannot be set on an XDomainRequest object")
1914
+ }
1915
+
1916
+ if ("responseType" in options) {
1917
+ xhr.responseType = options.responseType
1918
+ }
1919
+
1920
+ if ("beforeSend" in options &&
1921
+ typeof options.beforeSend === "function"
1922
+ ) {
1923
+ options.beforeSend(xhr)
1924
+ }
1925
+
1926
+ xhr.send(body)
1927
+
1928
+ return xhr
1929
+
1930
+ function readystatechange() {
1931
+ if (xhr.readyState === 4) {
1932
+ load()
1933
+ }
1934
+ }
1935
+
1936
+ function getBody() {
1937
+ // Chrome with requestType=blob throws errors arround when even testing access to responseText
1938
+ var body = null
1939
+
1940
+ if (xhr.response) {
1941
+ body = xhr.response
1942
+ } else if (xhr.responseType === 'text' || !xhr.responseType) {
1943
+ body = xhr.responseText || xhr.responseXML
1944
+ }
1945
+
1946
+ if (isJson) {
1947
+ try {
1948
+ body = JSON.parse(body)
1949
+ } catch (e) {}
1950
+ }
1951
+
1952
+ return body
1953
+ }
1954
+
1955
+ function getStatusCode() {
1956
+ return xhr.status === 1223 ? 204 : xhr.status
1957
+ }
1958
+
1959
+ // if we're getting a none-ok statusCode, build & return an error
1960
+ function errorFromStatusCode(status, body) {
1961
+ var error = null
1962
+ if (status === 0 || (status >= 400 && status < 600)) {
1963
+ var message = (typeof body === "string" ? body : false) ||
1964
+ messages[String(status).charAt(0)]
1965
+ error = new Error(message)
1966
+ error.statusCode = status
1967
+ }
1968
+
1969
+ return error
1970
+ }
1971
+
1972
+ // will load the data & process the response in a special response object
1973
+ function loadResponse() {
1974
+ var status = getStatusCode()
1975
+ var body = getBody()
1976
+ var error = errorFromStatusCode(status, body)
1977
+ var response = {
1978
+ body: body,
1979
+ statusCode: status,
1980
+ statusText: xhr.statusText,
1981
+ raw: xhr
1982
+ }
1983
+ if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
1984
+ response.headers = parseHeaders(xhr.getAllResponseHeaders())
1985
+ } else {
1986
+ response.headers = {}
1987
+ }
1988
+
1989
+ callback(error, response, response.body)
1990
+ }
1991
+
1992
+ // will load the data and add some response properties to the source xhr
1993
+ // and then respond with that
1994
+ function loadXhr() {
1995
+ var status = getStatusCode()
1996
+ var error = errorFromStatusCode(status)
1997
+
1998
+ xhr.status = xhr.statusCode = status
1999
+ xhr.body = getBody()
2000
+ xhr.headers = parseHeaders(xhr.getAllResponseHeaders())
2001
+
2002
+ callback(error, xhr, xhr.body)
2003
+ }
2004
+
2005
+ function error(evt) {
2006
+ callback(evt, xhr)
2007
+ }
2008
+ }
2009
+
2010
+
2011
+ function noop() {}
2012
+
2013
+ },{"global/window":14,"once":15,"parse-headers":19}],14:[function(require,module,exports){
2014
+ (function (global){
2015
+ if (typeof window !== "undefined") {
2016
+ module.exports = window;
2017
+ } else if (typeof global !== "undefined") {
2018
+ module.exports = global;
2019
+ } else if (typeof self !== "undefined"){
2020
+ module.exports = self;
2021
+ } else {
2022
+ module.exports = {};
2023
+ }
2024
+
2025
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2026
+
2027
+ },{}],15:[function(require,module,exports){
2028
+ module.exports = once
2029
+
2030
+ once.proto = once(function () {
2031
+ Object.defineProperty(Function.prototype, 'once', {
2032
+ value: function () {
2033
+ return once(this)
2034
+ },
2035
+ configurable: true
2036
+ })
2037
+ })
2038
+
2039
+ function once (fn) {
2040
+ var called = false
2041
+ return function () {
2042
+ if (called) return
2043
+ called = true
2044
+ return fn.apply(this, arguments)
2045
+ }
2046
+ }
2047
+
2048
+ },{}],16:[function(require,module,exports){
2049
+ var isFunction = require('is-function')
2050
+
2051
+ module.exports = forEach
2052
+
2053
+ var toString = Object.prototype.toString
2054
+ var hasOwnProperty = Object.prototype.hasOwnProperty
2055
+
2056
+ function forEach(list, iterator, context) {
2057
+ if (!isFunction(iterator)) {
2058
+ throw new TypeError('iterator must be a function')
2059
+ }
2060
+
2061
+ if (arguments.length < 3) {
2062
+ context = this
2063
+ }
2064
+
2065
+ if (toString.call(list) === '[object Array]')
2066
+ forEachArray(list, iterator, context)
2067
+ else if (typeof list === 'string')
2068
+ forEachString(list, iterator, context)
2069
+ else
2070
+ forEachObject(list, iterator, context)
2071
+ }
2072
+
2073
+ function forEachArray(array, iterator, context) {
2074
+ for (var i = 0, len = array.length; i < len; i++) {
2075
+ if (hasOwnProperty.call(array, i)) {
2076
+ iterator.call(context, array[i], i, array)
2077
+ }
2078
+ }
2079
+ }
2080
+
2081
+ function forEachString(string, iterator, context) {
2082
+ for (var i = 0, len = string.length; i < len; i++) {
2083
+ // no such thing as a sparse string.
2084
+ iterator.call(context, string.charAt(i), i, string)
2085
+ }
2086
+ }
2087
+
2088
+ function forEachObject(object, iterator, context) {
2089
+ for (var k in object) {
2090
+ if (hasOwnProperty.call(object, k)) {
2091
+ iterator.call(context, object[k], k, object)
2092
+ }
2093
+ }
2094
+ }
2095
+
2096
+ },{"is-function":17}],17:[function(require,module,exports){
2097
+ module.exports = isFunction
2098
+
2099
+ var toString = Object.prototype.toString
2100
+
2101
+ function isFunction (fn) {
2102
+ var string = toString.call(fn)
2103
+ return string === '[object Function]' ||
2104
+ (typeof fn === 'function' && string !== '[object RegExp]') ||
2105
+ (typeof window !== 'undefined' &&
2106
+ // IE8 and below
2107
+ (fn === window.setTimeout ||
2108
+ fn === window.alert ||
2109
+ fn === window.confirm ||
2110
+ fn === window.prompt))
2111
+ };
2112
+
2113
+ },{}],18:[function(require,module,exports){
2114
+
2115
+ exports = module.exports = trim;
2116
+
2117
+ function trim(str){
2118
+ return str.replace(/^\s*|\s*$/g, '');
2119
+ }
2120
+
2121
+ exports.left = function(str){
2122
+ return str.replace(/^\s*/, '');
2123
+ };
2124
+
2125
+ exports.right = function(str){
2126
+ return str.replace(/\s*$/, '');
2127
+ };
2128
+
2129
+ },{}],19:[function(require,module,exports){
2130
+ var trim = require('trim')
2131
+ , forEach = require('for-each')
2132
+ , isArray = function(arg) {
2133
+ return Object.prototype.toString.call(arg) === '[object Array]';
2134
+ }
2135
+
2136
+ module.exports = function (headers) {
2137
+ if (!headers)
2138
+ return {}
2139
+
2140
+ var result = {}
2141
+
2142
+ forEach(
2143
+ trim(headers).split('\n')
2144
+ , function (row) {
2145
+ var index = row.indexOf(':')
2146
+ , key = trim(row.slice(0, index)).toLowerCase()
2147
+ , value = trim(row.slice(index + 1))
2148
+
2149
+ if (typeof(result[key]) === 'undefined') {
2150
+ result[key] = value
2151
+ } else if (isArray(result[key])) {
2152
+ result[key].push(value)
2153
+ } else {
2154
+ result[key] = [ result[key], value ]
2155
+ }
2156
+ }
2157
+ )
2158
+
2159
+ return result
2160
+ }
2161
+ },{"for-each":16,"trim":18}],20:[function(require,module,exports){
2162
+ module.exports = require('./jquery.browser');
2163
+
2164
+ },{"./jquery.browser":21}],21:[function(require,module,exports){
2165
+ /*!
2166
+ * jQuery Browser Plugin v0.0.6
2167
+ * https://github.com/gabceb/jquery-browser-plugin
2168
+ *
2169
+ * Original jquery-browser code Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
2170
+ * http://jquery.org/license
2171
+ *
2172
+ * Modifications Copyright 2013 Gabriel Cebrian
2173
+ * https://github.com/gabceb
2174
+ *
2175
+ * Released under the MIT license
2176
+ *
2177
+ * Date: 2013-07-29T17:23:27-07:00
2178
+ */
2179
+
2180
+
2181
+ var matched, browser;
2182
+
2183
+ var uaMatch = function( ua ) {
2184
+ ua = ua.toLowerCase();
2185
+
2186
+ var match = /(opr)[\/]([\w.]+)/.exec( ua ) ||
2187
+ /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
2188
+ /(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec( ua ) ||
2189
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
2190
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
2191
+ /(msie) ([\w.]+)/.exec( ua ) ||
2192
+ ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec( ua ) ||
2193
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
2194
+ [];
2195
+
2196
+ var platform_match = /(ipad)/.exec( ua ) ||
2197
+ /(iphone)/.exec( ua ) ||
2198
+ /(android)/.exec( ua ) ||
2199
+ /(windows phone)/.exec( ua ) ||
2200
+ /(win)/.exec( ua ) ||
2201
+ /(mac)/.exec( ua ) ||
2202
+ /(linux)/.exec( ua ) ||
2203
+ /(cros)/i.exec( ua ) ||
2204
+ [];
2205
+
2206
+ return {
2207
+ browser: match[ 3 ] || match[ 1 ] || "",
2208
+ version: match[ 2 ] || "0",
2209
+ platform: platform_match[ 0 ] || ""
2210
+ };
2211
+ };
2212
+
2213
+ matched = uaMatch( window.navigator.userAgent );
2214
+ browser = {};
2215
+ browser.uaMatch = uaMatch;
2216
+
2217
+ if ( matched.browser ) {
2218
+ browser[ matched.browser ] = true;
2219
+ browser.version = matched.version;
2220
+ browser.versionNumber = parseInt(matched.version);
2221
+ }
2222
+
2223
+ if ( matched.platform ) {
2224
+ browser[ matched.platform ] = true;
2225
+ }
2226
+
2227
+ // These are all considered mobile platforms, meaning they run a mobile browser
2228
+ if ( browser.android || browser.ipad || browser.iphone || browser[ "windows phone" ] ) {
2229
+ browser.mobile = true;
2230
+ }
2231
+
2232
+ // These are all considered desktop platforms, meaning they run a desktop browser
2233
+ if ( browser.cros || browser.mac || browser.linux || browser.win ) {
2234
+ browser.desktop = true;
2235
+ }
2236
+
2237
+ // Chrome, Opera 15+ and Safari are webkit based browsers
2238
+ if ( browser.chrome || browser.opr || browser.safari ) {
2239
+ browser.webkit = true;
2240
+ }
2241
+
2242
+ // IE11 has a new token so we will assign it msie to avoid breaking changes
2243
+ if ( browser.rv )
2244
+ {
2245
+ var ie = "msie";
2246
+
2247
+ matched.browser = ie;
2248
+ browser[ie] = true;
2249
+ }
2250
+
2251
+ // Opera 15+ are identified as opr
2252
+ if ( browser.opr )
2253
+ {
2254
+ var opera = "opera";
2255
+
2256
+ matched.browser = opera;
2257
+ browser[opera] = true;
2258
+ }
2259
+
2260
+ // Stock Android browsers are marked as Safari on Android.
2261
+ if ( browser.safari && browser.android )
2262
+ {
2263
+ var android = "android";
2264
+
2265
+ matched.browser = android;
2266
+ browser[android] = true;
2267
+ }
2268
+
2269
+ // Assign the name and platform variable
2270
+ browser.name = matched.browser;
2271
+ browser.platform = matched.platform;
2272
+
2273
+
2274
+ module.exports = browser;
2275
+
2276
+ },{}],22:[function(require,module,exports){
2277
+ (function (global){
2278
+ /** @preserve http://github.com/easeway/js-class */
2279
+
2280
+ // Class Definition using ECMA5 prototype chain
2281
+
2282
+ function inherit(dest, src, noParent) {
2283
+ while (src && src !== Object.prototype) {
2284
+ Object.getOwnPropertyNames(src).forEach(function (name) {
2285
+ if (name != '.class' && !dest.hasOwnProperty(name)) {
2286
+ var desc = Object.getOwnPropertyDescriptor(src, name);
2287
+ Object.defineProperty(dest, name, desc);
2288
+ }
2289
+ });
2290
+ if (noParent) {
2291
+ break;
2292
+ }
2293
+ src = src.__proto__;
2294
+ }
2295
+ return dest;
2296
+ }
2297
+
2298
+ var Class = function (base, proto, options) {
2299
+ if (typeof(base) != 'function') {
2300
+ options = proto;
2301
+ proto = base;
2302
+ base = Object;
2303
+ }
2304
+ if (!proto) {
2305
+ proto = {};
2306
+ }
2307
+ if (!options) {
2308
+ options = {};
2309
+ }
2310
+
2311
+ var meta = {
2312
+ name: options.name,
2313
+ base: base,
2314
+ implements: []
2315
+ }
2316
+ var classProto = Class.clone(proto);
2317
+ if (options.implements) {
2318
+ (Array.isArray(options.implements) ? options.implements : [options.implements])
2319
+ .forEach(function (implementedType) {
2320
+ if (typeof(implementedType) == 'function' && implementedType.prototype) {
2321
+ meta.implements.push(implementedType);
2322
+ Class.extend(classProto, implementedType.prototype);
2323
+ }
2324
+ });
2325
+ }
2326
+ classProto.__proto__ = base.prototype;
2327
+ var theClass = function () {
2328
+ if (typeof(this.constructor) == 'function') {
2329
+ this.constructor.apply(this, arguments);
2330
+ }
2331
+ };
2332
+ meta.type = theClass;
2333
+ theClass.prototype = classProto;
2334
+ Object.defineProperty(theClass, '.class.meta', { value: meta, enumerable: false, configurable: false, writable: false });
2335
+ Object.defineProperty(classProto, '.class', { value: theClass, enumerable: false, configurable: false, writable: false });
2336
+ if (options.statics) {
2337
+ Class.extend(theClass, options.statics);
2338
+ }
2339
+ return theClass;
2340
+ };
2341
+
2342
+ Class.extend = inherit;
2343
+
2344
+ Class.clone = function (object) {
2345
+ return inherit({}, object);
2346
+ };
2347
+
2348
+ function findType(meta, type) {
2349
+ while (meta) {
2350
+ if (meta.type.prototype === type.prototype) {
2351
+ return true;
2352
+ }
2353
+ for (var i in meta.implements) {
2354
+ var implType = meta.implements[i];
2355
+ var implMeta = implType['.class.meta'];
2356
+ if (implMeta) {
2357
+ if (findType(implMeta, type)) {
2358
+ return true;
2359
+ }
2360
+ } else {
2361
+ for (var proto = implType.prototype; proto; proto = proto.__proto__) {
2362
+ if (proto === type.prototype) {
2363
+ return true;
2364
+ }
2365
+ }
2366
+ }
2367
+ }
2368
+ meta = meta.base ? meta.base['.class.meta'] : undefined;
2369
+ }
2370
+ return false;
2371
+ }
2372
+
2373
+ var Checker = Class({
2374
+ constructor: function (object) {
2375
+ this.object = object;
2376
+ },
2377
+
2378
+ typeOf: function (type) {
2379
+ if (this.object instanceof type) {
2380
+ return true;
2381
+ }
2382
+ var meta = Class.typeInfo(this.object);
2383
+ return meta && findType(meta, type);
2384
+ }
2385
+ });
2386
+
2387
+ // aliases
2388
+ Checker.prototype.a = Checker.prototype.typeOf;
2389
+ Checker.prototype.an = Checker.prototype.typeOf;
2390
+
2391
+ Class.is = function (object) {
2392
+ return new Checker(object);
2393
+ };
2394
+
2395
+ Class.typeInfo = function (object) {
2396
+ var theClass = object.__proto__['.class'];
2397
+ return theClass ? theClass['.class.meta'] : undefined;
2398
+ };
2399
+
2400
+ Class.VERSION = [0, 0, 2];
2401
+
2402
+ if (module) {
2403
+ module.exports = Class;
2404
+ } else {
2405
+ global.Class = Class; // for browser
2406
+ }
2407
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2408
+
2409
+ },{}],"biojs-io-fasta":[function(require,module,exports){
2410
+ // Generated by CoffeeScript 1.8.0
2411
+ module.exports.parse = require("./parser");
2412
+
2413
+ module.exports.writer = require("./writer");
2414
+
2415
+ },{"./parser":6,"./writer":9}],"biojs-vis-sequence":[function(require,module,exports){
2416
+ module.exports = require("./lib/index");
2417
+
2418
+ },{"./lib/index":1}]},{},["biojs-vis-sequence"])
2419
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2UvbGliL2luZGV4LmpzIiwiL1VzZXJzL3ByaXlhbS9zZXF1ZW5jZXNlcnZlci9ub2RlX21vZHVsZXMvYmlvanMtdmlzLXNlcXVlbmNlL25vZGVfbW9kdWxlcy9iaW9qcy1ldmVudHMvaW5kZXguanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWV2ZW50cy9ub2RlX21vZHVsZXMvYmFja2JvbmUtZXZlbnRzLXN0YW5kYWxvbmUvYmFja2JvbmUtZXZlbnRzLXN0YW5kYWxvbmUuanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWV2ZW50cy9ub2RlX21vZHVsZXMvYmFja2JvbmUtZXZlbnRzLXN0YW5kYWxvbmUvaW5kZXguanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL2xpYi9nZW5lcmljX3JlYWRlci5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbGliL3BhcnNlci5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbGliL3N0cmluZ3MuanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL2xpYi91dGlscy5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbGliL3dyaXRlci5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbm9kZV9tb2R1bGVzL2Jpb2pzLW1vZGVsL3NyYy9pbmRleC5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbm9kZV9tb2R1bGVzL2Jpb2pzLW1vZGVsL3NyYy9zZXEuanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL25vZGVfbW9kdWxlcy9uZXRzL2luZGV4LmpzIiwiL1VzZXJzL3ByaXlhbS9zZXF1ZW5jZXNlcnZlci9ub2RlX21vZHVsZXMvYmlvanMtdmlzLXNlcXVlbmNlL25vZGVfbW9kdWxlcy9iaW9qcy1pby1mYXN0YS9ub2RlX21vZHVsZXMvbmV0cy9ub2RlX21vZHVsZXMveGhyL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL25vZGVfbW9kdWxlcy9uZXRzL25vZGVfbW9kdWxlcy94aHIvbm9kZV9tb2R1bGVzL2dsb2JhbC93aW5kb3cuanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL25vZGVfbW9kdWxlcy9uZXRzL25vZGVfbW9kdWxlcy94aHIvbm9kZV9tb2R1bGVzL29uY2Uvb25jZS5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbm9kZV9tb2R1bGVzL25ldHMvbm9kZV9tb2R1bGVzL3hoci9ub2RlX21vZHVsZXMvcGFyc2UtaGVhZGVycy9ub2RlX21vZHVsZXMvZm9yLWVhY2gvaW5kZXguanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2Jpb2pzLWlvLWZhc3RhL25vZGVfbW9kdWxlcy9uZXRzL25vZGVfbW9kdWxlcy94aHIvbm9kZV9tb2R1bGVzL3BhcnNlLWhlYWRlcnMvbm9kZV9tb2R1bGVzL2Zvci1lYWNoL25vZGVfbW9kdWxlcy9pcy1mdW5jdGlvbi9pbmRleC5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbm9kZV9tb2R1bGVzL25ldHMvbm9kZV9tb2R1bGVzL3hoci9ub2RlX21vZHVsZXMvcGFyc2UtaGVhZGVycy9ub2RlX21vZHVsZXMvdHJpbS9pbmRleC5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbm9kZV9tb2R1bGVzL25ldHMvbm9kZV9tb2R1bGVzL3hoci9ub2RlX21vZHVsZXMvcGFyc2UtaGVhZGVycy9wYXJzZS1oZWFkZXJzLmpzIiwiL1VzZXJzL3ByaXlhbS9zZXF1ZW5jZXNlcnZlci9ub2RlX21vZHVsZXMvYmlvanMtdmlzLXNlcXVlbmNlL25vZGVfbW9kdWxlcy9qcXVlcnktYnJvd3Nlci1wbHVnaW4vaW5kZXguanMiLCIvVXNlcnMvcHJpeWFtL3NlcXVlbmNlc2VydmVyL25vZGVfbW9kdWxlcy9iaW9qcy12aXMtc2VxdWVuY2Uvbm9kZV9tb2R1bGVzL2pxdWVyeS1icm93c2VyLXBsdWdpbi9qcXVlcnkuYnJvd3Nlci5qcyIsIm5vZGVfbW9kdWxlcy9qcy1jbGFzcy9jbGFzcy5qcyIsIi9Vc2Vycy9wcml5YW0vc2VxdWVuY2VzZXJ2ZXIvbm9kZV9tb2R1bGVzL2Jpb2pzLXZpcy1zZXF1ZW5jZS9ub2RlX21vZHVsZXMvYmlvanMtaW8tZmFzdGEvbGliL2luZGV4LmpzIiwiLi9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzc1Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDclJBO0FBQ0E7O0FDREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25EQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxQkE7QUFDQTs7QUNEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDakxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDVEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlCQTtBQUNBOztBQ0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDOUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ2hJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0pBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLy8gbGVnYWN5ISFcbiQuYnJvd3NlciA9IHJlcXVpcmUoXCJqcXVlcnktYnJvd3Nlci1wbHVnaW5cIik7XG5cbi8qKiBcbiAqIFNlcXVlbmNlIGNvbXBvbmVudCBcbiAqIFxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBCaW9qc1xuICogXG4gKiBAYXV0aG9yIDxhIGhyZWY9XCJtYWlsdG86am9obmNhckBnbWFpbC5jb21cIj5Kb2huIEdvbWV6PC9hPiwgPGEgaHJlZj1cIm1haWx0bzpzZWNldmFsbGl2QGdtYWlsLmNvbVwiPkpvc2UgVmlsbGF2ZWNlczwvYT5cbiAqIEB2ZXJzaW9uIDEuMC4wXG4gKiBAY2F0ZWdvcnkgM1xuICogXG4gKiBAcmVxdWlyZXMgPGEgaHJlZj0naHR0cDovL2Jsb2cuanF1ZXJ5LmNvbS8yMDExLzA5LzEyL2pxdWVyeS0xLTYtNC1yZWxlYXNlZC8nPmpRdWVyeSBDb3JlIDEuNi40PC9hPlxuICogQGRlcGVuZGVuY3kgPHNjcmlwdCBsYW5ndWFnZT1cIkphdmFTY3JpcHRcIiB0eXBlPVwidGV4dC9qYXZhc2NyaXB0XCIgc3JjPVwiLi4vYmlvanMvZGVwZW5kZW5jaWVzL2pxdWVyeS9qcXVlcnktMS40LjIubWluLmpzXCI+PC9zY3JpcHQ+XG4gKiBcbiAqIEByZXF1aXJlcyA8YSBocmVmPSdodHRwOi8vanF1ZXJ5dWkuY29tL2Rvd25sb2FkJz5qUXVlcnkgVUkgMS44LjE2PC9hPlxuICogQGRlcGVuZGVuY3kgPHNjcmlwdCBsYW5ndWFnZT1cIkphdmFTY3JpcHRcIiB0eXBlPVwidGV4dC9qYXZhc2NyaXB0XCIgc3JjPVwiLi4vYmlvanMvZGVwZW5kZW5jaWVzL2pxdWVyeS9qcXVlcnktdWktMS44LjIuY3VzdG9tLm1pbi5qc1wiPjwvc2NyaXB0PlxuICpcbiAqIEByZXF1aXJlcyA8YSBocmVmPSdCaW9qcy5Ub29sdGlwLmNzcyc+QmlvanMuVG9vbHRpcDwvYT5cbiAqIEBkZXBlbmRlbmN5IDxzY3JpcHQgbGFuZ3VhZ2U9XCJKYXZhU2NyaXB0XCIgdHlwZT1cInRleHQvamF2YXNjcmlwdFwiIHNyYz1cInNyYy9CaW9qcy5Ub29sdGlwLmpzXCI+PC9zY3JpcHQ+XG4gKiBcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIEFuIG9iamVjdCB3aXRoIHRoZSBvcHRpb25zIGZvciBTZXF1ZW5jZSBjb21wb25lbnQuXG4gKiAgICBcbiAqIEBvcHRpb24ge3N0cmluZ30gdGFyZ2V0IFxuICogICAgSWRlbnRpZmllciBvZiB0aGUgRElWIHRhZyB3aGVyZSB0aGUgY29tcG9uZW50IHNob3VsZCBiZSBkaXNwbGF5ZWQuXG4gKiAgICBcbiAqIEBvcHRpb24ge3N0cmluZ30gc2VxdWVuY2UgXG4gKiAgICBUaGUgc2VxdWVuY2UgdG8gYmUgZGlzcGxheWVkLlxuICogICAgXG4gKiBAb3B0aW9uIHtzdHJpbmd9IFtpZF0gXG4gKiAgICBTZXF1ZW5jZSBpZGVudGlmaWVyIGlmIGFwcGx5LlxuICogICAgXG4gKiBAb3B0aW9uIHtzdHJpbmd9IFtmb3JtYXQ9XCJGQVNUQVwiXSBcbiAqICAgIFRoZSBkaXNwbGF5IGZvcm1hdCBmb3IgdGhlIHNlcXVlbmNlIHJlcHJlc2VudGF0aW9uLlxuICogICAgXG4gKiBAb3B0aW9uIHtPYmplY3RbXX0gW2hpZ2hsaWdodHNdIFxuICogXHQgIEZvciBoaWdobGlnaHRpbmcgbXVsdGlwbGUgcmVnaW9ucy4gXG4gKiAgICA8cHJlIGNsYXNzPVwiYnJ1c2g6IGpzXCIgdGl0bGU9XCJTeW50YXg6XCI+IFxuICogICAgW1xuICogICAgXHQvLyBIaWdobGlnaHQgYW1pbm9hY2lkcyBmcm9tICdzdGFydCcgdG8gJ2VuZCcgb2YgdGhlIGN1cnJlbnQgc3RyYW5kIHVzaW5nIHRoZSBzcGVjaWZpZWQgJ2NvbG9yJyAob3B0aW9uYWwpIGFuZCAnYmFja2dyb3VuZCcgKG9wdGlvbmFsKS5cbiAqICAgIFx0eyBzdGFydDogJmx0O3N0YXJ0VmFsMSZndDssIGVuZDogJmx0O2VuZFZhbDEmZ3Q7IFssIGlkOiZsdDtpZFZhbDEmZ3Q7XSBbLCBjb2xvcjogJmx0O0hUTUxDb2xvciZndDtdIFssIGJhY2tncm91bmQ6ICZsdDtIVE1MQ29sb3ImZ3Q7XX0sIFxuICogICAgXHQvL1xuICogICAgXHQvLyBBbnkgb3RoZXJzIGhpZ2hsaWdodHNcbiAqICAgIFx0Li4uLCAgXG4gKiAgICBcdC8vIFxuICogICAgXHR7IHN0YXJ0OiAmbHQ7c3RhcnRWYWxOJmd0OywgZW5kOiAmbHQ7ZW5kVmFsTiZndDsgWywgaWQ6Jmx0O2lkVmFsTiZndDtdIFssIGNvbG9yOiAmbHQ7SFRNTENvbG9yJmd0O10gWywgYmFja2dyb3VuZDogJmx0O0hUTUxDb2xvciZndDtdfVxuICogICAgXTwvcHJlPlxuICogXG4gKiA8cHJlIGNsYXNzPVwiYnJ1c2g6IGpzXCIgdGl0bGU9XCJFeGFtcGxlOlwiPiBcbiAqIGhpZ2hsaWdodHMgOiBbXG4gKiBcdFx0eyBzdGFydDozMCwgZW5kOjQyLCBjb2xvcjpcIndoaXRlXCIsIGJhY2tncm91bmQ6XCJncmVlblwiLCBpZDpcInNwaW4xXCIgfSxcbiAqXHRcdHsgc3RhcnQ6MTM5LCBlbmQ6MTQwIH0sIFxuICpcdFx0eyBzdGFydDo2MzEsIGVuZDo2MzMsIGNvbG9yOlwid2hpdGVcIiwgYmFja2dyb3VuZDpcImJsdWVcIiB9XG4gKlx0XVxuICogPC9wcmU+XG4gKiBcbiAqIEBvcHRpb24ge09iamVjdH0gW2NvbHVtbnM9e3NpemU6NDAsc3BhY2VkRWFjaDoxMH1dIFxuICogXHQgIE9wdGlvbnMgZm9yIGRpc3BsYXlpbmcgdGhlIGNvbHVtbnMuIFN5bnRheDogeyBzaXplOiAmbHQ7bnVtQ29scyZndDssIHNwYWNlZEVhY2g6ICZsdDtudW1Db2xzJmd0O31cbiAqIFxuICogQG9wdGlvbiB7T2JqZWN0fSBbc2VsZWN0aW9uXSBcbiAqIFx0ICBQb3NpdGlvbnMgZm9yIHRoZSBjdXJyZW50IHNlbGVjdGVkIHJlZ2lvbi4gU3ludGF4OiB7IHN0YXJ0OiAmbHQ7c3RhcnRWYWx1ZSZndDssIGVuZDogJmx0O2VuZFZhbHVlJmd0O31cbiAqIFxuICogQG9wdGlvbiB7T2JqZWN0W119IFthbm5vdGF0aW9uc10gXG4gKiAgICBTZXQgb2Ygb3ZlcmxhcHBpbmcgYW5ub3RhdGlvbnMuIE11c3QgYmUgYW4gYXJyYXkgb2Ygb2JqZWN0cyBmb2xsb3dpbmcgdGhlIHN5bnRheDpcbiAqICAgICBcdFx0PHByZSBjbGFzcz1cImJydXNoOiBqc1wiIHRpdGxlPVwiU3ludGF4OlwiPlxuICogICAgICAgICAgICBbIFxuICogICAgICAgICAgICAgIC8vIEFuIGFubm90YXRpb246XG4gKiAgICAgICAgICAgICAgeyBuYW1lOiAmbHQ7bmFtZSZndDssIFxuICogICAgICAgICAgICAgICAgaHRtbDogJmx0O21lc3NhZ2UmZ3Q7LCBcbiAqICAgICAgICAgICAgICAgIGNvbG9yOiAmbHQ7Y29sb3JfY29kZSZndDssIFxuICogICAgICAgICAgICAgICAgcmVnaW9uczogW3sgc3RhcnQ6ICZsdDtzdGFydFZhbDEmZ3Q7LCBlbmQ6ICZsdDtlbmRWYWwxJmd0OyBjb2xvcjogJmx0O0hUTUxDb2xvciZndDt9LCAuLi4seyBzdGFydDogJmx0O3N0YXJ0VmFsTiZndDssIGVuZDogJmx0O2VuZFZhbE4mZ3Q7LCBjb2xvcjogJmx0O0hUTUxDb2xvciZndDt9XSBcbiAqICAgICAgICAgICAgICB9LCBcbiAqICAgICAgICAgICAgICBcbiAqICAgICAgICAgICAgICAvLyAuLi5cbiAqICAgICAgICAgICAgICAvLyBtb3JlIGFubm90YXRpb25zIGhlcmUgXG4gKiAgICAgICAgICAgICAgLy8gLi4uXG4gKiAgICAgICAgICAgIF1cbiAqICAgIFx0XHQgPC9wcmU+XG4gKiAgICB3aGVyZTpcbiAqICAgICAgPHVsPlxuICogICAgICAgIDxsaT48Yj5uYW1lPC9iPiBpcyB0aGUgdW5pcXVlIG5hbWUgZm9yIHRoZSBhbm5vdGF0aW9uPC9saT5cbiAqICAgICAgICA8bGk+PGI+aHRtbDwvYj4gaXMgdGhlIG1lc3NhZ2UgKGNhbiBiZSBIVE1MKSB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHRvb2wgdGlwLjwvbGk+XG4gKiAgICAgICAgPGxpPjxiPmNvbG9yPC9iPiBpcyB0aGUgZGVmYXVsdCBIVE1MIGNvbG9yIGNvZGUgZm9yIGFsbCB0aGUgcmVnaW9ucy48L2xpPlxuICogICAgICAgIDxsaT48Yj5yZWdpb25zPC9iPiBhcnJheSBvZiBvYmplY3RzIGRlZmluaW5nIHRoZSBpbnRlcnZhbHMgd2hpY2ggYmVsb25ncyB0byB0aGUgYW5ub3RhdGlvbi48L2xpPlxuICogICAgICAgIDxsaT48Yj5yZWdpb25zW2ldLnN0YXJ0PC9iPiBpcyB0aGUgc3RhcnRpbmcgY2hhcmFjdGVyIGZvciB0aGUgaS10aCBpbnRlcnZhbC48L2xpPlxuICogICAgICAgIDxsaT48Yj5yZWdpb25zW2ldLmVuZDwvYj4gaXMgdGhlIGVuZGluZyBjaGFyYWN0ZXIgZm9yIHRoZSBpLXRoIGludGVydmFsLjwvbGk+XG4gKiAgICAgICAgPGxpPjxiPnJlZ2lvbnNbaV0uY29sb3I8L2I+IGlzIGFuIG9wdGlvbmFsIGNvbG9yIGZvciB0aGUgaS10aCBpbnRlcnZhbC4gICBcbiAqICAgICAgPC91bD4gXG4gKiAgICAgIFxuICogQG9wdGlvbiB7T2JqZWN0fSBbZm9ybWF0T3B0aW9ucz17dGl0bGU6dHJ1ZSwgZm9vdGVyOnRydWV9XSBcbiAqIFx0ICBPcHRpb25zIGZvciBkaXNwbGF5aW5nIHRoZSB0aXRsZS4gYnkgbm93IGp1c3QgYWZmZWN0aW5nIHRoZSBDT0RBVEEgZm9ybWF0LlxuICogICAgPHByZSBjbGFzcz1cImJydXNoOiBqc1wiIHRpdGxlPVwiU3ludGF4OlwiPiBcbiAqIFx0XHRmb3JtYXRPcHRpb25zIDoge1xuICogXHRcdFx0dGl0bGU6ZmFsc2UsXG4gKiBcdFx0XHRmb290ZXI6ZmFsc2VcbiAqIFx0XHR9XG4gKiAgICA8L3ByZT5cbiAqICAgIFxuICogQGV4YW1wbGUgXG4gKiB2YXIgdGhlU2VxdWVuY2UgPSBcIk1FVExDUVJMTlZDUURLSUxUSFlFTkRTVERMUkRISURZV0tITVJMRUNBSVlZS0FSRU1HRktISU5IUVZWUFRMQVZTS05LQUxRQUlFTFFMVExFVElZTlNRWVNORUtXVExRRFZTTEVWWUxUQVBUR0NJS0tIR1lUVkVWUUZER0RJQ05UTUhZVE5XVEhJWUlDRUVBb2pzIFNWVFZWRUdRVkRZWUdMWVlWSEVHSVJUWUZWUUZLRERBRUtZU0tOS1ZXRVZIQUdHUVZJTENQVFNWRlNTTkVWU1NQRUlJUlFITEFOSFBBQVRIVEtBVkFMR1RFRVRRVFRJUVJQUlNFUERUR05QQ0hUVEtMTEhSRFNWRFNBUElMVEFGTlNTSEtHUklOQ05TTlRUUElWSExLR0RBTlRMS0NMUllSRktLSENUTFlUQVZTU1RXSFdUR0hOVktIS1NBSVZUTFRZRFNFV1FSRFFGTFNRVktJUEtUSVRWU1RHRk1TSVwiO1xuICogdmFyIG15U2VxdWVuY2UgPSBuZXcgU2VxdWVuY2Uoe1xuICogXHRcdHNlcXVlbmNlIDogdGhlU2VxdWVuY2UsXG4gKiBcdFx0dGFyZ2V0IDogXCJZb3VyT3duRGl2SWRcIixcbiAqIFx0XHRmb3JtYXQgOiAnQ09EQVRBJyxcbiAqIFx0XHRpZCA6ICdQOTE4MjgzJyxcbiAqIFx0XHRhbm5vdGF0aW9uczogW1xuICogICAgICAgIHsgbmFtZTpcIkNBVEhcIiwgXG4gKiBcdCAgXHRcdGNvbG9yOlwiI0YwRjAyMFwiLCBcbiAqIFx0ICBcdFx0aHRtbDogXCJVc2luZyBjb2xvciBjb2RlICNGMEYwMjAgXCIsIFxuICogXHQgIFx0XHRyZWdpb25zOiBbe3N0YXJ0OiAxMjIsIGVuZDogMTM1fV1cbiAqIFx0XHQgIH0sXG4gKiAgICAgICAgeyBuYW1lOlwiVEVTVFwiLCBcbiAqICAgICAgICAgIGh0bWw6XCImbHQ7YnImZ3Q7IEV4YW1wbGUgb2YgJmx0O2ImZ3Q7SFRNTCZsdDsvYiZndDtcIiwgXG4gKiAgICAgICAgICBjb2xvcjpcImdyZWVuXCIsIFxuICogICAgICAgICAgcmVnaW9uczogW1xuICogICAgICAgICAgICB7c3RhcnQ6IDI4NSwgZW5kOiAyOTJ9LFxuICogICAgICAgICAgICB7c3RhcnQ6IDI5MywgZW5kOiAzMTQsIGNvbG9yOiBcIiMyRTQ5ODhcIn1dXG4gKiAgICAgICAgfVxuICogICAgICBdLFxuICogICAgICBoaWdobGlnaHRzIDogW1xuICogICAgICBcdHsgc3RhcnQ6MzAsIGVuZDo0MiwgY29sb3I6XCJ3aGl0ZVwiLCBiYWNrZ3JvdW5kOlwiZ3JlZW5cIiwgaWQ6XCJzcGluMVwiIH0sXG4gKiAgICAgIFx0eyBzdGFydDoxMzksIGVuZDoxNDAgfSwgXG4gKiAgICAgIFx0eyBzdGFydDo2MzEsIGVuZDo2MzMsIGNvbG9yOlwid2hpdGVcIiwgYmFja2dyb3VuZDpcImJsdWVcIiB9XG4gKiAgICAgIF1cbiAqIH0pO1x0XG4gKiBcbiAqL1xuXG52YXIgQ2xhc3MgPSByZXF1aXJlKCdqcy1jbGFzcycpO1xuXG52YXIgRVZUX09OX1NFTEVDVElPTl9DSEFOR0U9IFwib25TZWxlY3Rpb25DaGFuZ2VcIjtcbnZhciBFVlRfT05fU0VMRUNUSU9OX0NIQU5HRUQ9IFwib25TZWxlY3Rpb25DaGFuZ2VkXCI7XG52YXIgRVZUX09OX0FOTk9UQVRJT05fQ0xJQ0tFRD0gXCJvbkFubm90YXRpb25DbGlja2VkXCI7XG5cblNlcXVlbmNlID0gQ2xhc3MoXG4vKiogQGxlbmRzIFNlcXVlbmNlIyAqL1xue1x0XG5cdGNvbnN0cnVjdG9yOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdHZhciBzZWxmID0gdGhpcztcblxuICAgIHRoaXMub3B0ID0galF1ZXJ5LmV4dGVuZCh0aGlzLm9wdCxvcHRpb25zKTtcblxuICAgIHRoaXMuX2NvbnRhaW5lciA9IGpRdWVyeSh0aGlzLm9wdC50YXJnZXQgKTtcbiAgICBcbiAgICAvLyBsZWdhY3kgc3VwcG9ydCAodGFyZ2V0IGlkIHdpdGhvdXQgJyMnKVxuICAgIGlmKHRoaXMuX2NvbnRhaW5lci5sZW5ndGggPT0gMCl7XG4gICAgICB0aGlzLl9jb250YWluZXIgPSBqUXVlcnkoIFwiI1wiICsgdGhpcy5vcHQudGFyZ2V0IClcbiAgICB9XG5cbiAgICBpZih0aGlzLl9jb250YWluZXIubGVuZ3RoID09IDApe1xuICAgICAgY29uc29sZS5sb2coXCJlbXB0eSB0YXJnZXQgY29udGFpbmVyXCIpO1xuICAgIH1cblxuICAgIC8vIGxlZ2FjeTogY29weSB0YXJnZXQgaWRcbiAgICB0aGlzLm9wdC50YXJnZXQgPSB0aGlzLl9jb250YWluZXJbMF0uaWQ7XG5cdFx0XG5cdFx0Ly8gTGF6eSBpbml0aWFsaXphdGlvbiBcblx0XHR0aGlzLl9jb250YWluZXIucmVhZHkoZnVuY3Rpb24oKSB7XG5cdFx0XHRzZWxmLl9pbml0aWFsaXplKCk7XG5cdFx0fSk7XG5cdH0sXG5cdFxuXHQvKipcblx0ICogRGVmYXVsdCB2YWx1ZXMgZm9yIHRoZSBvcHRpb25zXG5cdCAqIEBuYW1lIFNlcXVlbmNlLW9wdFxuXHQgKi9cblx0b3B0IDoge1xuXHRcdFxuXHRcdHNlcXVlbmNlIDogXCJcIixcblx0XHRpZCA6IFwiXCIsXG5cdFx0dGFyZ2V0IDogXCJcIixcblx0XHRmb3JtYXQgOiBcIkZBU1RBXCIsXG5cdFx0c2VsZWN0aW9uOiB7IHN0YXJ0OiAwLCBlbmQ6IDAgfSxcblx0XHRjb2x1bW5zOiB7IHNpemU6IDM1LCBzcGFjZWRFYWNoOiAxMCB9LFxuXHRcdGhpZ2hsaWdodHMgOiBbXSxcblx0XHRhbm5vdGF0aW9uczogW10sXG5cdFx0c2VxdWVuY2VVcmw6ICdodHRwOi8vd3d3LmViaS5hYy51ay9kYXMtc3J2L3VuaXByb3QvZGFzL3VuaXByb3Qvc2VxdWVuY2UnLFxuXHRcdFxuXHRcdC8vIFN0eWxlcyBcblx0XHRzZWxlY3Rpb25Db2xvciA6ICdZZWxsb3cnLFxuXHRcdHNlbGVjdGlvbkZvbnRDb2xvciA6ICdibGFjaycsXG5cdFx0aGlnaGxpZ2h0Rm9udENvbG9yIDogJ3JlZCcsXG5cdFx0aGlnaGxpZ2h0QmFja2dyb3VuZENvbG9yIDogJ3doaXRlJyxcblx0XHRmb250RmFtaWx5OiAnXCJBbmRhbGUgbW9ub1wiLCBjb3VyaWVyLCBtb25vc3BhY2UnLFxuXHRcdGZvbnRTaXplOiAnMTJweCcsXG5cdFx0Zm9udENvbG9yIDogJ2luaGVyaXQnLFxuXHRcdGJhY2tncm91bmRDb2xvciA6ICdpbmhlcml0Jyxcblx0XHR3aWR0aDogdW5kZWZpbmVkLFxuXHRcdGhlaWdodDogdW5kZWZpbmVkLFxuXHRcdGZvcm1hdFNlbGVjdG9yVmlzaWJsZTogdHJ1ZVxuXHR9LFxuXHRcblx0LyoqXG5cdCAqIEFycmF5IGNvbnRhaW5pbmcgdGhlIHN1cHBvcnRlZCBldmVudCBuYW1lc1xuXHQgKiBAbmFtZSBTZXF1ZW5jZS1ldmVudFR5cGVzXG5cdCAqL1xuXHRldmVudFR5cGVzIDogW1xuXHRcdC8qKlxuXHRcdCAqIEBuYW1lIFNlcXVlbmNlI29uU2VsZWN0aW9uQ2hhbmdlZFxuXHRcdCAqIEBldmVudFxuXHRcdCAqIEBwYXJhbSB7ZnVuY3Rpb259IGFjdGlvblBlcmZvcm1lZCBBbiBmdW5jdGlvbiB3aGljaCByZWNlaXZlcyBhbiB7QGxpbmsgQmlvanMuRXZlbnR9IG9iamVjdCBhcyBhcmd1bWVudC5cblx0XHQgKiBAZXZlbnREYXRhIHtPYmplY3R9IHNvdXJjZSBUaGUgY29tcG9uZW50IHdoaWNoIGRpZCB0cmlnZ2VyZWQgdGhlIGV2ZW50LlxuXHRcdCAqIEBldmVudERhdGEge3N0cmluZ30gdHlwZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQuXG5cdFx0ICogQGV2ZW50RGF0YSB7aW50fSBzdGFydCBBIG51bWJlciBpbmRpY2F0aW5nIHRoZSBzdGFydCBvZiB0aGUgc2VsZWN0aW9uLlxuXHRcdCAqIEBldmVudERhdGEge2ludH0gZW5kIEEgbnVtYmVyIGluZGljYXRpbmcgdGhlIGVuZGluZyBvZiBzZWxlY3Rpb24uXG5cdFx0ICogQGV4YW1wbGUgXG5cdFx0ICogbXlTZXF1ZW5jZS5vblNlbGVjdGlvbkNoYW5nZWQoXG5cdFx0ICogICAgZnVuY3Rpb24oIG9iakV2ZW50ICkge1xuXHRcdCAqICAgICAgIGFsZXJ0KFwiU2VsZWN0ZWQ6IFwiICsgb2JqRXZlbnQuc3RhcnQgKyBcIiwgXCIgKyBvYmpFdmVudC5lbmQgKTtcblx0XHQgKiAgICB9XG5cdFx0ICogKTsgXG5cdFx0ICogXG5cdFx0ICogKi9cblx0XHRcIm9uU2VsZWN0aW9uQ2hhbmdlZFwiLFxuXHRcdFxuXHRcdC8qKlxuXHRcdCAqIEBuYW1lIFNlcXVlbmNlI29uU2VsZWN0aW9uQ2hhbmdlXG5cdFx0ICogQGV2ZW50XG5cdFx0ICogQHBhcmFtIHtmdW5jdGlvbn0gYWN0aW9uUGVyZm9ybWVkIEFuIGZ1bmN0aW9uIHdoaWNoIHJlY2VpdmVzIGFuIHtAbGluayBCaW9qcy5FdmVudH0gb2JqZWN0IGFzIGFyZ3VtZW50LlxuXHRcdCAqIEBldmVudERhdGEge09iamVjdH0gc291cmNlIFRoZSBjb21wb25lbnQgd2hpY2ggZGlkIHRyaWdnZXJlZCB0aGUgZXZlbnQuXG5cdFx0ICogQGV2ZW50RGF0YSB7c3RyaW5nfSB0eXBlIFRoZSBuYW1lIG9mIHRoZSBldmVudC5cblx0XHQgKiBAZXZlbnREYXRhIHtpbnR9IHN0YXJ0IEEgbnVtYmVyIGluZGljYXRpbmcgdGhlIHN0YXJ0IG9mIHRoZSBzZWxlY3Rpb24uXG5cdFx0ICogQGV2ZW50RGF0YSB7aW50fSBlbmQgQSBudW1iZXIgaW5kaWNhdGluZyB0aGUgZW5kaW5nIG9mIHNlbGVjdGlvbi5cblx0XHQgKiBAZXhhbXBsZSBcblx0XHQgKiBteVNlcXVlbmNlLm9uU2VsZWN0aW9uQ2hhbmdlKFxuXHRcdCAqICAgIGZ1bmN0aW9uKCBvYmpFdmVudCApIHtcblx0XHQgKiAgICAgICBhbGVydChcIlNlbGVjdGlvbiBpbiBwcm9ncmVzczogXCIgKyBvYmpFdmVudC5zdGFydCArIFwiLCBcIiArIG9iakV2ZW50LmVuZCApO1xuXHRcdCAqICAgIH1cblx0XHQgKiApOyAgXG5cdFx0ICogXG5cdFx0ICogXG5cdFx0ICogKi9cblx0XHRcIm9uU2VsZWN0aW9uQ2hhbmdlXCIsXG5cdFx0XG5cdFx0LyoqXG5cdFx0ICogQG5hbWUgU2VxdWVuY2Ujb25Bbm5vdGF0aW9uQ2xpY2tlZFxuXHRcdCAqIEBldmVudFxuXHRcdCAqIEBwYXJhbSB7ZnVuY3Rpb259IGFjdGlvblBlcmZvcm1lZCBBbiBmdW5jdGlvbiB3aGljaCByZWNlaXZlcyBhbiB7QGxpbmsgQmlvanMuRXZlbnR9IG9iamVjdCBhcyBhcmd1bWVudC5cblx0XHQgKiBAZXZlbnREYXRhIHtPYmplY3R9IHNvdXJjZSBUaGUgY29tcG9uZW50IHdoaWNoIGRpZCB0cmlnZ2VyZWQgdGhlIGV2ZW50LlxuXHRcdCAqIEBldmVudERhdGEge3N0cmluZ30gdHlwZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQuXG5cdFx0ICogQGV2ZW50RGF0YSB7c3RyaW5nfSBuYW1lIFRoZSBuYW1lIG9mIHRoZSBzZWxlY3RlZCBhbm5vdGF0aW9uLlxuXHRcdCAqIEBldmVudERhdGEge2ludH0gcG9zIEEgbnVtYmVyIGluZGljYXRpbmcgdGhlIHBvc2l0aW9uIG9mIHRoZSBzZWxlY3RlZCBhbWlubyBhY2lkLlxuXHRcdCAqIEBleGFtcGxlIFxuXHRcdCAqIG15U2VxdWVuY2Uub25Bbm5vdGF0aW9uQ2xpY2tlZChcblx0XHQgKiAgICBmdW5jdGlvbiggb2JqRXZlbnQgKSB7XG5cdFx0ICogICAgICAgYWxlcnQoXCJDbGlja2VkIFwiICsgb2JqRXZlbnQubmFtZSArIFwiIG9uIHBvc2l0aW9uIFwiICsgb2JqRXZlbnQucG9zICk7XG5cdFx0ICogICAgfVxuXHRcdCAqICk7ICBcblx0XHQgKiBcblx0XHQgKiAqL1xuXHRcdFwib25Bbm5vdGF0aW9uQ2xpY2tlZFwiXG5cdF0sXG5cbiAgZ2V0SWQgOiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMub3B0LmlkO1xuICB9LFxuXG5cdC8vIGludGVybmFsIG1lbWJlcnNcblx0X2hlYWRlckRpdiA6IG51bGwsXG5cdF9jb250ZW50RGl2IDogbnVsbCxcblx0XG5cdC8vIE1ldGhvZHNcblxuXHRfaW5pdGlhbGl6ZTogZnVuY3Rpb24gKCkge1xuXHRcdFxuXHRcdGlmICggdGhpcy5vcHQud2lkdGggIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdHRoaXMuX2NvbnRhaW5lci53aWR0aCggdGhpcy5vcHQud2lkdGggKTtcblx0XHR9XG5cdFx0XG5cdFx0aWYgKCB0aGlzLm9wdC5oZWlnaHQgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdHRoaXMuX2NvbnRhaW5lci5oZWlnaHQoIHRoaXMub3B0LmhlaWdodCApO1xuXHRcdH1cblx0XHRcblx0XHQvLyBEaXNhYmxlIHRleHQgc2VsZWN0aW9uXG5cdFx0XG5cdFx0dGhpcy5fY29udGFpbmVyLmNzcyh7XG5cdFx0XHQnLW1vei11c2VyLXNlbGVjdCc6J25vbmUnLFxuXHRcdFx0Jy13ZWJraXQtdXNlci1zZWxlY3QnOidub25lJyxcblx0XHRcdCd1c2VyLXNlbGVjdCc6J25vbmUnXG4gICAgICAgIH0pO1xuXHRcdFxuXHRcdC8vIERJViBmb3IgdGhlIGZvcm1hdCBzZWxlY3RvclxuXHRcdHRoaXMuX2J1aWxkRm9ybWF0U2VsZWN0b3IoKTtcblx0XHRcblx0XHQvLyBESVYgZm9yIHRoZSBzZXF1ZW5jZVxuXHRcdHRoaXMuX2NvbnRlbnREaXYgPSBqUXVlcnkoJzxkaXY+PC9kaXY+JykuYXBwZW5kVG8odGhpcy5fY29udGFpbmVyKTtcblx0XHR0aGlzLl9jb250ZW50RGl2LmNzcyh7XG5cdFx0XHRcdCdmb250LWZhbWlseSc6IHRoaXMub3B0LmZvbnRGYW1pbHksXG5cdFx0XHRcdCdmb250LXNpemUnOiB0aGlzLm9wdC5mb250U2l6ZSxcblx0XHRcdFx0J3RleHQtYWxpZ24nOiAnbGVmdCdcblx0XHRcdH0pO1xuXHRcdFxuXHRcdC8vIEluaXRpYWxpemUgaGlnaGxpZ2h0aW5nIFxuXHRcdHRoaXMuX2hpZ2hsaWdodHMgPSB0aGlzLm9wdC5oaWdobGlnaHRzO1xuXHRcdFxuXHRcdC8vIEluaXRpYWxpemUgYW5ub3RhdGlvbnNcblx0XHR0aGlzLl9hbm5vdGF0aW9ucyA9IHRoaXMub3B0LmFubm90YXRpb25zO1xuXHRcdFxuXHRcdC8vSW5pdGlhbGl6ZSB0b29sdGlwXG5cdFx0dmFyIHRvb2x0aXAgPSBcInNlcXVlbmNlVGlwXCIgKyB0aGlzLm9wdC50YXJnZXQgO1xuXHRcdGpRdWVyeSgnPGRpdiBpZD1cIicgKyB0b29sdGlwICsgJ1wiPjwvZGl2PicpIFxuXHQgICAgICAgIC5jc3Moe1x0XG5cdCAgICAgICAgXHQncG9zaXRpb24nOiBcImFic29sdXRlXCIsXG5cdCAgICAgICAgXHQnei1pbmRleCc6IFwiOTk5OTk5XCIsXG5cdCAgICAgICAgXHQnY29sb3InOiBcIiNmZmZcIixcblx0ICAgICAgICBcdCdmb250LXNpemUnOiBcIjEycHhcIixcblx0ICAgICAgICBcdCd3aWR0aCc6IFwiYXV0b1wiLFxuXHQgICAgICAgIFx0J2Rpc3BsYXknOiAnbm9uZSdcblx0ICAgICAgICB9KVxuXHQgICAgICAgIC5hZGRDbGFzcyhcInRvb2x0aXBcIilcblx0ICAgICAgICAuYXBwZW5kVG8oXCJib2R5XCIpXG5cdCAgICAgICAgLmhpZGUoKTtcblx0XHR0aGlzLm9wdC5fdG9vbHRpcCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRvb2x0aXApO1xuXG5cdFx0aWYgKCAodGhpcy5vcHQuc2VxdWVuY2UpICkge1xuXHRcdFx0dGhpcy5fcmVkcmF3KCk7XG5cdFx0XHRcblx0XHR9IGVsc2UgaWYgKCAgKHRoaXMub3B0LmlkKSApIHtcblx0XHRcdHRoaXMuX3JlcXVlc3RTZXF1ZW5jZSggdGhpcy5vcHQuaWQgKTtcblx0XHRcdFxuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLmNsZWFyU2VxdWVuY2UoXCJObyBzZXF1ZW5jZSBhdmFpbGFibGVcIiwgXCIuLi9iaW9qcy9jc3MvaW1hZ2VzL3dhcm5pbmdfaWNvbi5wbmdcIik7XG5cdFx0fVxuXHRcdFxuXHR9LFxuXHRcblx0XG5cdC8qKlxuXHQgKiBTaG93cyB0aGUgY29sdW1ucyBpbmRpY2F0ZWQgYnkgdGhlIGluZGV4ZXMgYXJyYXkuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBzZXEgVGhlIHNlcXVlbmNlIHN0cmFuZC5cblx0ICogQHBhcmFtIHtzdHJpbmd9IFtpZGVudGlmaWVyXSBTZXF1ZW5jZSBpZGVudGlmaWVyLlxuXHQgKiBcblx0ICogQGV4YW1wbGUgXG5cdCAqIG15U2VxdWVuY2Uuc2V0U2VxdWVuY2UoXCJQOTk5OTlcIik7XG5cdCAqIFxuXHQgKi9cbiAgICBzZXRTZXF1ZW5jZTogZnVuY3Rpb24gKCBzZXEsIGlkZW50aWZpZXIgKSB7XG5cbiAgICBcdGlmICggc2VxLm1hdGNoKC9eKFtBLU4sUi1aXVswLTldW0EtWl1bQS1aLCAwLTldW0EtWiwgMC05XVswLTldKXwoW08sUCxRXVswLTldW0EtWiwgMC05XVtBLVosIDAtOV1bQS1aLCAwLTldWzAtOV0pKFxcLlxcZCspPyQvaSkgKSB7XG4gICAgXHRcdHRoaXMuX3JlcXVlc3RTZXF1ZW5jZSggYXJndW1lbnRzWzBdICk7XG4gICAgXHRcdFxuICAgIFx0fSBlbHNlIHtcbiAgICBcdFx0dGhpcy5vcHQuc2VxdWVuY2UgPSBzZXE7XG4gICAgICAgIFx0dGhpcy5vcHQuaWQgPSBpZGVudGlmaWVyOyBcbiAgICAgICAgXHR0aGlzLl9oaWdobGlnaHRzID0gW107XG4gICAgXHRcdHRoaXMuX2hpZ2hsaWdodHNDb3VudCA9IDA7XG4gICAgXHRcdHRoaXMub3B0LnNlbGVjdGlvbiA9IHsgc3RhcnQ6IDAsIGVuZDogMCB9O1xuICAgIFx0XHR0aGlzLl9hbm5vdGF0aW9ucyA9IFtdO1xuICAgIFx0XHRcbiAgICBcdFx0dGhpcy5fY29udGVudERpdi5jaGlsZHJlbigpLnJlbW92ZSgpO1xuICAgIFx0XHR0aGlzLl9yZWRyYXcoKTtcbiAgICBcdH1cbiAgICB9LFxuICAgIFxuICAgIF9yZXF1ZXN0U2VxdWVuY2U6IGZ1bmN0aW9uICggYWNjZXNzaW9uICkge1xuXHRcdHZhciBzZWxmID0gdGhpcztcbiAgICBcdFxuICAgIFx0Y29uc29sZS5sb2coXCJSZXF1ZXN0aW5nIHNlcXVlbmNlIGZvcjogXCIgKyBhY2Nlc3Npb24gKTtcblxuXHRcdGpRdWVyeS5hamF4KHsgXG5cdFx0XHR1cmw6IHNlbGYub3B0LnNlcXVlbmNlVXJsLFxuXHRcdFx0ZGF0YVR5cGU6IFwieG1sXCIsXG5cdFx0XHRkYXRhOiB7IHNlZ21lbnQ6IGFjY2Vzc2lvbiB9LFxuXHRcdFx0c3VjY2VzczogZnVuY3Rpb24gKCB4bWwgICkge1xuXHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFxuXHRcdFx0XHRcdHZhciBzZXF1ZW5jZU5vZGUgPSBqUXVlcnkoeG1sKS5maW5kKCdTRVFVRU5DRTpmaXJzdCcpO1xuXHRcdFx0XHRcdHNlbGYuc2V0U2VxdWVuY2UoIHNlcXVlbmNlTm9kZS50ZXh0KCksIHNlcXVlbmNlTm9kZS5hdHRyKFwiaWRcIiksIHNlcXVlbmNlTm9kZS5hdHRyKFwibGFiZWxcIikgKTtcblx0XHRcdFx0XHRcblx0XHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHRcdGNvbnNvbGUubG9nKFwiRXJyb3IgZGVjb2RpbmcgcmVzcG9uc2UgZGF0YTogXCIgKyBlLm1lc3NhZ2UgKTtcblx0XHRcdFx0XHRzZWxmLmNsZWFyU2VxdWVuY2UoXCJObyBzZXF1ZW5jZSBhdmFpbGFibGVcIiwgXCIuLi9iaW9qcy9jc3MvaW1hZ2VzL3dhcm5pbmdfaWNvbi5wbmdcIik7XG5cdFx0XHRcdH1cblxuXHRcdFx0fSxcblx0XHRcdGVycm9yOiBmdW5jdGlvbiAoanFYSFIsIHRleHRTdGF0dXMsIGVycm9yVGhyb3duKSB7XG5cdFx0XHRcdGNvbnNvbGUubG9nKFwiRXJyb3IgZGVjb2RpbmcgcmVzcG9uc2UgZGF0YTogXCIgKyB0ZXh0U3RhdHVzICk7XG5cdFx0XHRcdHNlbGYuY2xlYXJTZXF1ZW5jZShcIkVycm9yIHJlcXVlc3RpbmcgdGhlIHNlcXVlbmNlIHRvIHRoZSBzZXJ2ZXIgXCIgKyB0aGlzLnVybCAsIFwiLi4vYmlvanMvY3NzL2ltYWdlcy93YXJuaW5nX2ljb24ucG5nXCIpO1xuXHRcdFx0fVxuXHRcdH0pO1xuICAgIH0sXG5cdFxuICAgIC8qKlxuXHQgKiBTaG93cyB0aGUgY29sdW1ucyBpbmRpY2F0ZWQgYnkgdGhlIGluZGV4ZXMgYXJyYXkuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBbc2hvd01lc3NhZ2VdIE1lc3NhZ2UgdG8gYmUgc2hvd2VkLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gW2ljb25dIEljb24gdG8gYmUgc2hvd2VkIGEgc2lkZSBvZiB0aGUgbWVzc2FnZVxuXHQgKiBcblx0ICogQGV4YW1wbGUgXG5cdCAqIG15U2VxdWVuY2UuY2xlYXJTZXF1ZW5jZShcIk5vIHNlcXVlbmNlIGF2YWlsYWJsZVwiLCBcIi4uL2Jpb2pzL2Nzcy9pbWFnZXMvd2FybmluZ19pY29uLnBuZ1wiKTtcblx0ICogXG5cdCAqL1xuICAgIGNsZWFyU2VxdWVuY2U6IGZ1bmN0aW9uICggc2hvd01lc3NhZ2UsIGljb24gKSB7XG4gICAgXHRcbiAgICBcdHZhciBtZXNzYWdlID0gdW5kZWZpbmVkO1xuICAgIFx0XHRcbiAgICBcdHRoaXMub3B0LnNlcXVlbmNlID0gXCJcIjtcbiAgICBcdHRoaXMub3B0LmlkID0gXCJcIjsgXG4gICAgXHR0aGlzLl9oaWdobGlnaHRzID0gW107XG5cdFx0dGhpcy5faGlnaGxpZ2h0c0NvdW50ID0gMDtcblx0XHR0aGlzLm9wdC5zZWxlY3Rpb24gPSB7IHN0YXJ0OiAwLCBlbmQ6IDAgfTtcblx0XHR0aGlzLl9hbm5vdGF0aW9ucyA9IFtdO1xuXHRcdHRoaXMuX2NvbnRlbnREaXYuY2hpbGRyZW4oKS5yZW1vdmUoKTtcblx0XHRcblx0XHR0aGlzLl9oZWFkZXJEaXYuaGlkZSgpO1xuXHRcdFxuXHRcdGlmICggdW5kZWZpbmVkICE9PSBzaG93TWVzc2FnZSApIHtcblx0XHRcdG1lc3NhZ2UgPSBqUXVlcnkoJzxkaXY+JyArIHNob3dNZXNzYWdlICsgJzwvZGl2PicpXG5cdFx0XHRcdC5hcHBlbmRUbyh0aGlzLl9jb250ZW50RGl2KVxuXHRcdFx0XHQuYWRkQ2xhc3MoXCJtZXNzYWdlXCIpO1xuXHRcdFx0XG5cdFx0XHRpZiAoIHVuZGVmaW5lZCAhPT0gaWNvbiApIHtcblx0XHRcdFx0bWVzc2FnZS5jc3Moe1xuXHRcdFx0XHRcdCdiYWNrZ3JvdW5kJzogJ3RyYW5zcGFyZW50IHVybChcIicgKyBpY29uICsgJ1wiKSBuby1yZXBlYXQgY2VudGVyIGxlZnQnLFxuXHRcdFx0XHRcdCdwYWRkaW5nLWxlZnQnOiAnMjBweCdcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cdFx0fVxuICAgIH0sXG5cdFxuXHQvKipcbiAgICAqIFNldCB0aGUgY3VycmVudCBzZWxlY3Rpb24gaW4gdGhlIHNlcXVlbmNlIGNhdXNpbmcgdGhlIGV2ZW50IHtAbGluayBTZXF1ZW5jZSNvblNlbGVjdGlvbkNoYW5nZWR9XG4gICAgKlxuICAgICogQGV4YW1wbGVcbiAgICAqIC8vIHNldCBzZWxlY3Rpb24gZnJvbSB0aGUgcG9zaXRpb24gMTAwIHRvIDE1MCBcbiAgICAqIG15U2VxdWVuY2Uuc2V0U2VsZWN0aW9uKDEwMCwgMTUwKTtcbiAgICAqIFxuICAgICogQHBhcmFtIHtpbnR9IHN0YXJ0IFRoZSBzdGFydGluZyBjaGFyYWN0ZXIgb2YgdGhlIHNlbGVjdGlvbi5cbiAgICAqIEBwYXJhbSB7aW50fSBlbmQgVGhlIGVuZGluZyBjaGFyYWN0ZXIgb2YgdGhlIHNlbGVjdGlvblxuICAgICovXG5cdHNldFNlbGVjdGlvbiA6IGZ1bmN0aW9uKHN0YXJ0LCBlbmQpIHtcblx0XHRpZihzdGFydCA+IGVuZCkge1xuXHRcdFx0dmFyIGF1eCA9IGVuZDtcblx0XHRcdGVuZCA9IHN0YXJ0O1xuXHRcdFx0c3RhcnQgPSBhdXg7XG5cblx0XHR9XG5cblx0XHRpZihzdGFydCAhPSB0aGlzLm9wdC5zZWxlY3Rpb24uc3RhcnQgfHwgZW5kICE9IHRoaXMub3B0LnNlbGVjdGlvbi5lbmQpIHtcblx0XHRcdHRoaXMuX3NldFNlbGVjdGlvbihzdGFydCwgZW5kKTtcblx0XHRcdHRoaXMudHJpZ2dlcihcblx0XHRcdFx0XHRFVlRfT05fU0VMRUNUSU9OX0NIQU5HRUQsIFxuXHRcdFx0XHRcdHsgXCJzdGFydFwiIDogc3RhcnQsIFwiZW5kXCIgOiBlbmQgfVxuXHRcdFx0KTtcblx0XHR9XG5cdH0sXG5cdFxuXHRfYnVpbGRGb3JtYXRTZWxlY3RvcjogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzZWxmID0gdGhpcztcblx0XHRcblx0XHR0aGlzLl9oZWFkZXJEaXYgPSBqUXVlcnkoJzxkaXY+PC9kaXY+JykuYXBwZW5kVG8odGhpcy5fY29udGFpbmVyKTtcblx0XHR0aGlzLl9oZWFkZXJEaXYuY3NzKHtcblx0XHRcdCdmb250LWZhbWlseSc6ICdcIkhldmVsdGljYSBOZXVlXCIsIEFyaWFsLCBcInNhbnMgc2VyaWZcIicsXG5cdFx0XHQnZm9udC1zaXplJzogJzE0cHgnXHRcblx0XHR9KS5hcHBlbmQoJ0Zvcm1hdDogJyk7XG5cdFx0XG5cdFx0dGhpcy5fZm9ybWF0U2VsZWN0b3IgPSBqUXVlcnkoJzxzZWxlY3Q+ICcrXG5cdFx0XHRcdCc8b3B0aW9uIHZhbHVlPVwiRkFTVEFcIj5GQVNUQTwvb3B0aW9uPicrXG5cdFx0XHRcdCc8b3B0aW9uIHZhbHVlPVwiQ09EQVRBXCI+Q09EQVRBPC9vcHRpb24+Jytcblx0XHRcdFx0JzxvcHRpb24gdmFsdWU9XCJQUklERVwiPlBSSURFPC9vcHRpb24+Jytcblx0XHRcdFx0JzxvcHRpb24gdmFsdWU9XCJSQVdcIj5SQVc8L29wdGlvbj48L3NlbGVjdD4nKS5hcHBlbmRUbyhzZWxmLl9oZWFkZXJEaXYpO1xuXG5cdFx0dGhpcy5fZm9ybWF0U2VsZWN0b3IuY2hhbmdlKGZ1bmN0aW9uKGUpIHtcblx0XHRcdHNlbGYub3B0LmZvcm1hdCA9IGpRdWVyeSh0aGlzKS52YWwoKTtcblx0XHRcdHNlbGYuX3JlZHJhdygpO1xuXHRcdH0pO1xuXHRcdFxuXHRcdHRoaXMuX2Zvcm1hdFNlbGVjdG9yLnZhbChzZWxmLm9wdC5mb3JtYXQpO1x0XG5cdFx0XG5cdFx0dGhpcy5mb3JtYXRTZWxlY3RvclZpc2libGUoIHRoaXMub3B0LmZvcm1hdFNlbGVjdG9yVmlzaWJsZSApO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBIaWdobGlnaHRzIGEgcmVnaW9uIHVzaW5nIHRoZSBmb250IGNvbG9yIGRlZmluZWQgaW4ge0Jpb2pzLlByb3RlaW4zRCNoaWdobGlnaHRGb250Q29sb3J9IGJ5IGRlZmF1bHQgaXMgcmVkLlxuICAgICpcbiAgICAqIEBkZXByZWNhdGVkIHVzZSBhZGRIaWdobGlnaHQgaW5zdGVhZC5cbiAgICAqIFxuICAgICogQHBhcmFtIHtpbnR9IHN0YXJ0IFRoZSBzdGFydGluZyBjaGFyYWN0ZXIgb2YgdGhlIGhpZ2hsaWdodGluZy5cbiAgICAqIEBwYXJhbSB7aW50fSBlbmQgVGhlIGVuZGluZyBjaGFyYWN0ZXIgb2YgdGhlIGhpZ2hsaWdodGluZy5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBbY29sb3JdIEhUTUwgY29sb3IgY29kZS5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBbYmFja2dyb3VuZF0gSFRNTCBjb2xvciBjb2RlLlxuICAgICogQHBhcmFtIHtzdHJpbmd9IFtpZF0gQ3VzdG9tIGlkZW50aWZpZXIuXG4gICAgKiBcbiAgICAqIEByZXR1cm4ge2ludH0gcmVwcmVzZW50aW5nIHRoZSBpZCBvZiB0aGUgaGlnaGxpZ2h0IG9uIHRoZSBpbnRlcm5hbCBhcnJheS4gUmV0dXJucyAtMSBvbiBmYWlsdXJlICBcbiAgICAqL1xuXHRoaWdobGlnaHQgOiBmdW5jdGlvbiAoc3RhcnQsIGVuZCwgY29sb3IsIGJhY2tncm91bmQsIGlkICkge1xuXHRcdHJldHVybiB0aGlzLmFkZEhpZ2hsaWdodCh7IFwic3RhcnRcIjogc3RhcnQsIFwiZW5kXCI6IGVuZCwgXCJjb2xvclwiOiBjb2xvciwgXCJiYWNrZ3JvdW5kXCI6IGJhY2tncm91bmQsIFwiaWRcIjogaWQgfSk7XG5cdH0sXG5cdFxuXHQvKipcbiAgICAqIEhpZ2hsaWdodHMgYSByZWdpb24gdXNpbmcgdGhlIGZvbnQgY29sb3IgZGVmaW5lZCBpbiB7U2VxdWVuY2UjaGlnaGxpZ2h0Rm9udENvbG9yfSBieSBkZWZhdWx0IGlzIHJlZC5cbiAgICAqXG4gICAgKiBAZXhhbXBsZVxuICAgICogLy8gaGlnaGxpZ2h0IHRoZSBjaGFyYWN0ZXJzIHdpdGhpbiB0aGUgcG9zaXRpb24gMTAwIHRvIDE1MCwgaW5jbHVkZWQuXG4gICAgKiBteVNlcXVlbmNlLmFkZEhpZ2hsaWdodCggeyBcInN0YXJ0XCI6IDEwMCwgXCJlbmRcIjogMTUwLCBcImNvbG9yXCI6IFwid2hpdGVcIiwgXCJiYWNrZ3JvdW5kXCI6IFwicmVkXCIsIFwiaWRcIjogXCJhYWFcIiB9ICk7XG4gICAgKiBcbiAgICAqIEBwYXJhbSB7T2JqZWN0fSBoIFRoZSBoaWdobGlnaHQgZGVmaW5lZCBhcyBmb2xsb3dzOlxuICAgICogXHRcbiAgICAqIFxuICAgICogQHJldHVybiB7aW50fSByZXByZXNlbnRpbmcgdGhlIGlkIG9mIHRoZSBoaWdobGlnaHQgb24gdGhlIGludGVybmFsIGFycmF5LiBSZXR1cm5zIC0xIG9uIGZhaWx1cmUgIFxuICAgICovXG5cdGFkZEhpZ2hsaWdodCA6IGZ1bmN0aW9uICggaCApIHtcblx0XHR2YXIgaWQgPSAnLTEnO1xuXHRcdHZhciBjb2xvciA9IFwiXCI7XG5cdFx0dmFyIGJhY2tncm91bmQgPSBcIlwiO1xuXHRcdHZhciBoaWdobGlnaHQgPSB7fTtcblx0XHRcblx0XHRpZiAoIGggaW5zdGFuY2VvZiBPYmplY3QgJiYgaC5zdGFydCA8PSBoLmVuZCApIHtcblx0XHRcdFxuXHRcdFx0Y29sb3IgPSAoIFwic3RyaW5nXCIgPT0gdHlwZW9mIGguY29sb3IgKT8gaC5jb2xvciA6IHRoaXMub3B0LmhpZ2hsaWdodEZvbnRDb2xvcjtcblx0XHRcdGJhY2tncm91bmQgPSAoIFwic3RyaW5nXCIgPT0gdHlwZW9mIGguYmFja2dyb3VuZCApPyBoLmJhY2tncm91bmQgOiB0aGlzLm9wdC5oaWdobGlnaHRCYWNrZ3JvdW5kQ29sb3I7XG5cdFx0XHRpZCA9ICggXCJzdHJpbmdcIiA9PSB0eXBlb2YgaC5pZCApPyBoLmlkIDogKG5ldyBOdW1iZXIodGhpcy5faGlnaGxpZ2h0c0NvdW50KyspKS50b1N0cmluZygpO1xuXHRcdFx0XG5cdFx0XHRoaWdobGlnaHQgPSB7IFwic3RhcnRcIjogaC5zdGFydCwgXCJlbmRcIjogaC5lbmQsIFwiY29sb3JcIjogY29sb3IsIFwiYmFja2dyb3VuZFwiOiBiYWNrZ3JvdW5kLCBcImlkXCI6IGlkIH07XG5cdFx0XHRcblx0XHRcdHRoaXMuX2hpZ2hsaWdodHMucHVzaChoaWdobGlnaHQpO1xuXHRcdFx0dGhpcy5fYXBwbHlIaWdobGlnaHQoaGlnaGxpZ2h0KTtcblx0XHRcdHRoaXMuX3Jlc3RvcmVTZWxlY3Rpb24oaC5zdGFydCxoLmVuZCk7XG5cdFx0fSBcblx0XHRcblx0XHRyZXR1cm4gaWQ7XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fYXBwbHlIaWdobGlnaHRcbiAgICAgKiBQdXJwb3NlOiAgQXBwbHkgdGhlIHNwZWNpZmllZCBjb2xvciBhbmQgYmFja2dyb3VuZCB0byBhIHJlZ2lvbiBiZXR3ZWVuICdzdGFydCcgYW5kICdlbmQnLlxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiBoaWdobGlnaHQgLT4ge09iamVjdH0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGZpZWxkcyBzdGFydCAoaW50KSwgZW5kIChpbnQpLCBcbiAgICAgKiBcdFx0XHRcdFx0XHRjb2xvciAoSFRNTCBjb2xvciBzdHJpbmcpIGFuZCBiYWNrZ3JvdW5kIChIVE1MIGNvbG9yIHN0cmluZykuXG4gICAgICovXG5cdF9hcHBseUhpZ2hsaWdodDogZnVuY3Rpb24gKCBoaWdobGlnaHQgKSB7XHRcdFxuXHRcdHZhciBzZXEgPSB0aGlzLl9jb250ZW50RGl2LmZpbmQoJy5zZXF1ZW5jZScpO1xuXHRcdGZvciAoIHZhciBpID0gaGlnaGxpZ2h0LnN0YXJ0IC0gMTsgaSA8IGhpZ2hsaWdodC5lbmQ7IGkrKyApe1xuXHRcdFx0emluZGV4ID0galF1ZXJ5KHNlcVtpXSkuY3NzKFwiei1pbmRleFwiKTtcblx0XHRcdGlmICh6aW5kZXg9PVwiYXV0b1wiKXtcblx0XHRcdFx0IHogPSAxO1xuXHRcdFx0XHQgbyA9IDE7XG5cdFx0XHQgfVxuXHRcdFx0IGVsc2V7XG5cdFx0XHRcdCB6ID0gMDtcblx0XHRcdFx0IG8gPSAwLjU7XG5cdFx0XHQgfVxuXHRcdFx0alF1ZXJ5KHNlcVtpXSlcblx0XHRcdFx0LmNzcyh7IFxuXHRcdFx0XHRcdFwiY29sb3JcIjogaGlnaGxpZ2h0LmNvbG9yLFxuXHRcdFx0XHRcdFwiYmFja2dyb3VuZC1jb2xvclwiOiBoaWdobGlnaHQuYmFja2dyb3VuZCxcblx0XHRcdFx0XHRcInotaW5kZXhcIjogeixcblx0XHRcdFx0XHRcIm9wYWNpdHlcIjogb1xuXHRcdFx0XHRcdH0pXG5cdFx0XHRcdC5hZGRDbGFzcyhcImhpZ2hsaWdodGVkXCIpO1xuXHRcdH1cblx0fSxcblx0LyogXG4gICAgICogRnVuY3Rpb246IFNlcXVlbmNlLl9hcHBseUhpZ2hsaWdodHNcbiAgICAgKiBQdXJwb3NlOiAgQXBwbHkgdGhlIHNwZWNpZmllZCBoaWdobGlnaHRzLlxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiBoaWdobGlnaHRzIC0+IHtPYmplY3RbXX0gQW4gYXJyYXkgY29udGFpbmluZyB0aGUgaGlnaGxpZ2h0cyB0byBiZSBhcHBsaWVkLlxuICAgICAqL1xuXHRfYXBwbHlIaWdobGlnaHRzOiBmdW5jdGlvbiAoIGhpZ2hsaWdodHMgKSB7XG5cdFx0Zm9yICggdmFyIGkgaW4gaGlnaGxpZ2h0cyApIHtcblx0XHRcdHRoaXMuX2FwcGx5SGlnaGxpZ2h0KGhpZ2hsaWdodHNbaV0pO1xuXHRcdH1cblx0fSxcblx0LyogXG4gICAgICogRnVuY3Rpb246IFNlcXVlbmNlLl9yZXN0b3JlSGlnaGxpZ2h0c1xuICAgICAqIFB1cnBvc2U6ICBSZXBhaW50IHRoZSBoaWdobGlnaHRzIGluIHRoZSBzcGVjaWZpZWQgcmVnaW9uLlxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiBzdGFydCAtPiB7aW50fSBTdGFydCBvZiB0aGUgcmVnaW9uIHRvIGJlIHJlc3RvcmVkLlxuICAgICAqIFx0XHQgICBlbmQgLT4ge2ludH0gRW5kIG9mIHRoZSByZWdpb24gdG8gYmUgcmVzdG9yZWQuXG4gICAgICovXG5cdF9yZXN0b3JlSGlnaGxpZ2h0czogZnVuY3Rpb24gKCBzdGFydCwgZW5kICkge1xuXHRcdHZhciBoID0gdGhpcy5faGlnaGxpZ2h0cztcblx0XHQvLyBwYWludCB0aGUgcmVnaW9uIHVzaW5nIGRlZmF1bHQgYmxhbmsgc2V0dGluZ3Ncblx0XHR0aGlzLl9hcHBseUhpZ2hsaWdodCh7XG5cdFx0XHRcInN0YXJ0XCI6IHN0YXJ0LCBcblx0XHRcdFwiZW5kXCI6IGVuZCwgXG5cdFx0XHRcImNvbG9yXCI6IHRoaXMub3B0LmZvbnRDb2xvciwgXG5cdFx0XHRcImJhY2tncm91bmRcIjogdGhpcy5vcHQuYmFja2dyb3VuZENvbG9yIFxuXHRcdH0pO1xuXHRcdC8vIHJlc3RvcmUgaGlnaGxpZ2h0cyBpbiB0aGF0IHJlZ2lvblxuXHRcdGZvciAoIHZhciBpIGluIGggKSB7XG5cdFx0XHQvLyBpbnRlcnZhbCBpbnRlcnNlY3RzIHdpdGggaGlnaGxpZ2h0IGkgP1xuXHRcdFx0aWYgKCAhKCBoW2ldLnN0YXJ0ID4gZW5kIHx8IGhbaV0uZW5kIDwgc3RhcnQgKSApIHtcblx0XHRcdFx0YSA9ICggaFtpXS5zdGFydCA8IHN0YXJ0ICkgPyBzdGFydCA6IGhbaV0uc3RhcnQ7XG5cdFx0XHRcdGIgPSAoIGhbaV0uZW5kID4gZW5kICkgPyBlbmQgOiBoW2ldLmVuZDtcblx0XHRcdFx0dGhpcy5fYXBwbHlIaWdobGlnaHQoe1xuXHRcdFx0XHRcdFwic3RhcnRcIjogYSwgXG5cdFx0XHRcdFx0XCJlbmRcIjogYiwgXG5cdFx0XHRcdFx0XCJjb2xvclwiOiBoW2ldLmNvbG9yLCBcblx0XHRcdFx0XHRcImJhY2tncm91bmRcIjogaFtpXS5iYWNrZ3JvdW5kIFxuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fcmVzdG9yZVNlbGVjdGlvblxuICAgICAqIFB1cnBvc2U6ICBSZXBhaW50IHRoZSBjdXJyZW50IHNlbGVjdGlvbiBpbiB0aGUgc3BlY2lmaWVkIHJlZ2lvbi4gXG4gICAgICogXHRcdFx0IEl0IGlzIHVzZWQgaW4gdGhlIGNhc2Ugb2YgYW55IGhpZ2hsaWdodCBkbyBvdmVycmlkaW5nIG9mIHRoZSBjdXJyZW50IHNlbGVjdGlvbi4gXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IHN0YXJ0IC0+IHtpbnR9IFN0YXJ0IG9mIHRoZSByZWdpb24gdG8gYmUgcmVzdG9yZWQuXG4gICAgICogXHRcdCAgIGVuZCAtPiB7aW50fSBFbmQgb2YgdGhlIHJlZ2lvbiB0byBiZSByZXN0b3JlZC5cbiAgICAgKi9cblx0X3Jlc3RvcmVTZWxlY3Rpb246IGZ1bmN0aW9uICggc3RhcnQsIGVuZCApIHtcblx0XHR2YXIgc2VsID0gdGhpcy5vcHQuc2VsZWN0aW9uO1xuXHRcdC8vIGludGVydmFsIGludGVyc2VjdHMgd2l0aCBjdXJyZW50IHNlbGVjdGlvbiA/XG5cdFx0Ly8gcmVzdG9yZSBzZWxlY3Rpb25cblx0XHRpZiAoICEoIHN0YXJ0ID4gc2VsLmVuZCB8fCBlbmQgPCBzZWwuc3RhcnQgKSApIHtcblx0XHRcdGEgPSAoIHN0YXJ0IDwgc2VsLnN0YXJ0ICkgPyBzZWwuc3RhcnQgOiBzdGFydDtcblx0XHRcdGIgPSAoIGVuZCA+IHNlbC5lbmQgKSA/IHNlbC5lbmQgOiBlbmQ7XG5cdFx0XHRcblx0XHRcdHRoaXMuX2FwcGx5SGlnaGxpZ2h0KHtcblx0XHRcdFx0XCJzdGFydFwiOiBhLCBcblx0XHRcdFx0XCJlbmRcIjogYiwgXG5cdFx0XHRcdFwiY29sb3JcIjogdGhpcy5vcHQuc2VsZWN0aW9uRm9udENvbG9yLCBcblx0XHRcdFx0XCJiYWNrZ3JvdW5kXCI6IHRoaXMub3B0LnNlbGVjdGlvbkNvbG9yLFxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBDbGVhciBhIGhpZ2hsaWdodGVkIHJlZ2lvbiB1c2luZy5cbiAgICAqXG4gICAgKiBAZGVwcmVjYXRlZCB1c2UgcmVtb3ZlSGlnaGxpZ2h0IGluc3RlYWQuXG4gICAgKiBcbiAgICAqIEBwYXJhbSB7aW50fSBpZCBUaGUgaWQgb2YgdGhlIGhpZ2hsaWdodCBvbiB0aGUgaW50ZXJuYWwgYXJyYXkuIFRoaXMgdmFsdWUgaXMgcmV0dXJuZWQgYnkgbWV0aG9kIGhpZ2hsaWdodC5cbiAgICAqL1xuXHR1bkhpZ2hsaWdodCA6IGZ1bmN0aW9uIChpZCkge1x0XG5cdFx0dGhpcy5yZW1vdmVIaWdobGlnaHQoaWQpO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBSZW1vdmUgYSBoaWdobGlnaHQuXG4gICAgKlxuICAgICogQGV4YW1wbGVcbiAgICAqIC8vIENsZWFyIHRoZSBoaWdobGlnaHRlZCBjaGFyYWN0ZXJzIHdpdGhpbiB0aGUgcG9zaXRpb24gMTAwIHRvIDE1MCwgaW5jbHVkZWQuXG4gICAgKiBteVNlcXVlbmNlLnJlbW92ZUhpZ2hsaWdodChcInNwaW4xXCIpO1xuICAgICogXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gaWQgVGhlIGlkIG9mIHRoZSBoaWdobGlnaHQgb24gdGhlIGludGVybmFsIGFycmF5LiBUaGlzIHZhbHVlIGlzIHJldHVybmVkIGJ5IG1ldGhvZCBoaWdobGlnaHQuXG4gICAgKi9cblx0cmVtb3ZlSGlnaGxpZ2h0IDogZnVuY3Rpb24gKGlkKSB7XHRcblx0XHR2YXIgaCA9IHRoaXMuX2hpZ2hsaWdodHM7XG5cdFx0Zm9yICggaSBpbiBoICkge1xuXHRcdFx0aWYgKCBoW2ldLmlkID09IGlkICkge1xuXHRcdFx0XHRzdGFydCA9IGhbaV0uc3RhcnQ7XG5cdFx0XHRcdGVuZCA9IGhbaV0uZW5kO1xuXHRcdFx0XHRoLnNwbGljZShpLDEpO1xuXHRcdFx0XHRcblx0XHRcdFx0dGhpcy5fcmVzdG9yZUhpZ2hsaWdodHMoc3RhcnQsZW5kKTtcblx0XHRcdFx0dGhpcy5fcmVzdG9yZVNlbGVjdGlvbihzdGFydCxlbmQpO1xuXHRcdFx0XHRcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBDbGVhciB0aGUgaGlnaGxpZ2h0cyBvZiB3aG9sZSBzZXF1ZW5jZS5cbiAgICAqIEBkZXByZWNhdGVkIHVzZSByZW1vdmVBbGxIaWdobGlnaHRzIGluc3RlYWQuXG4gICAgKi9cblx0dW5IaWdobGlnaHRBbGwgOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5yZW1vdmVBbGxIaWdobGlnaHRzKCk7XG5cdH0sXG5cdFxuXHQvKipcbiAgICAqIFJlbW92ZSBhbGwgdGhlIGhpZ2hsaWdodHMgb2Ygd2hvbGUgc2VxdWVuY2UuXG4gICAgKlxuICAgICogQGV4YW1wbGVcbiAgICAqIG15U2VxdWVuY2UucmVtb3ZlQWxsSGlnaGxpZ2h0cygpO1xuICAgICovXG5cdHJlbW92ZUFsbEhpZ2hsaWdodHMgOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5faGlnaGxpZ2h0cyA9IFtdO1xuXHRcdHRoaXMuX3Jlc3RvcmVIaWdobGlnaHRzKDEsdGhpcy5vcHQuc2VxdWVuY2UubGVuZ3RoKTtcblx0XHR0aGlzLl9yZXN0b3JlU2VsZWN0aW9uKDEsdGhpcy5vcHQuc2VxdWVuY2UubGVuZ3RoKTtcblx0fSxcblx0XG5cdC8qKlxuICAgICogQ2hhbmdlcyB0aGUgY3VycmVudCBkaXNwbGF5aW5nIGZvcm1hdCBvZiB0aGUgc2VxdWVuY2UuXG4gICAgKlxuICAgICogQGV4YW1wbGVcbiAgICAqIC8vIFNldCBmb3JtYXQgdG8gJ0ZBU1RBJy5cbiAgICAqIG15U2VxdWVuY2Uuc2V0Rm9ybWF0KCdGQVNUQScpO1xuICAgICogXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gZm9ybWF0IFRoZSBmb3JtYXQgZm9yIHRoZSBzZXF1ZW5jZSB0byBiZSBkaXNwbGF5ZWQuXG4gICAgKi9cblx0c2V0Rm9ybWF0IDogZnVuY3Rpb24oZm9ybWF0KSB7XG5cdFx0aWYgKCB0aGlzLm9wdC5mb3JtYXQgIT0gZm9ybWF0LnRvVXBwZXJDYXNlKCkgKSB7XG5cdFx0XHR0aGlzLm9wdC5mb3JtYXQgPSBmb3JtYXQudG9VcHBlckNhc2UoKTtcblx0XHRcdHRoaXMuX3JlZHJhdygpO1xuXHRcdH1cblxuXHRcdHZhciBzZWxmID0gdGhpcztcblx0XHQvLyBDaGFuZ2VzIHRoZSBvcHRpb24gaW4gdGhlIGNvbWJvIGJveFxuXHRcdHRoaXMuX2hlYWRlckRpdi5maW5kKCdvcHRpb24nKS5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYoalF1ZXJ5KHRoaXMpLnZhbCgpID09IHNlbGYub3B0LmZvcm1hdC50b1VwcGVyQ2FzZSgpKSB7XG5cdFx0XHRcdGpRdWVyeSh0aGlzKS5hdHRyKCdzZWxlY3RlZCcsICdzZWxlY3RlZCcpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBDaGFuZ2VzIHRoZSBjdXJyZW50IG51bWJlciBvZiBjb2x1bW5zIGluIHRoZSBkaXNwbGF5ZWQgc2VxdWVuY2UuXG4gICAgKlxuICAgICogQGV4YW1wbGVcbiAgICAqIC8vIFNldCB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgdG8gNzAuXG4gICAgKiBteVNlcXVlbmNlLnNldE51bUNvbHMoNzApO1xuICAgICogXG4gICAgKiBAcGFyYW0ge2ludH0gbnVtQ29scyBUaGUgbnVtYmVyIG9mIGNvbHVtbnMuXG4gICAgKi9cblx0c2V0TnVtQ29scyA6IGZ1bmN0aW9uKG51bUNvbHMpIHtcblx0XHR0aGlzLm9wdC5jb2x1bW5zLnNpemUgPSBudW1Db2xzO1xuXHRcdHRoaXMuX3JlZHJhdygpO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBTZXQgdGhlIHZpc2liaWxpdHkgb2YgdGhlIGRyb3AtZG93biBsaXN0IG9mIGZvcm1hdHMuXG4gICAgKiBcbiAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gdmlzaWJsZSB0cnVlOiBzaG93OyBmYWxzZTogaGlkZS5cbiAgICAqL1xuXHRmb3JtYXRTZWxlY3RvclZpc2libGUgOiBmdW5jdGlvbiAodmlzaWJsZSl7XG5cdFx0aWYgKHZpc2libGUpIHtcblx0XHRcdHRoaXMuX2hlYWRlckRpdi5zaG93KCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2hlYWRlckRpdi5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBUaGlzIGlzIHNpbWlsYXIgdG8gYSB7QmlvanMuUHJvdGVpbjNEI2Zvcm1hdFNlbGVjdG9yVmlzaWJsZX0gd2l0aCB0aGUgJ3RydWUnIGFyZ3VtZW50LlxuICAgICpcbiAgICAqIEBleGFtcGxlXG4gICAgKiAvLyBTaG93cyB0aGUgZm9ybWF0IHNlbGVjdG9yLlxuICAgICogbXlTZXF1ZW5jZS5zaG93Rm9ybWF0U2VsZWN0b3IoKTtcbiAgICAqIFxuICAgICovXG5cdHNob3dGb3JtYXRTZWxlY3RvciA6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX2hlYWRlckRpdi5zaG93KCk7XG5cdH0sXG5cdFxuXHQvKipcbiAgICAqIFRoaXMgaXMgc2ltaWxhciB0byBhIHtCaW9qcy5Qcm90ZWluM0QjZm9ybWF0U2VsZWN0b3JWaXNpYmxlfSB3aXRoIHRoZSAnZmFsc2UnIGFyZ3VtZW50LlxuICAgICogXG4gICAgKiBAZXhhbXBsZVxuICAgICogLy8gSGlkZXMgdGhlIGZvcm1hdCBzZWxlY3Rvci5cbiAgICAqIG15U2VxdWVuY2UuaGlkZUZvcm1hdFNlbGVjdG9yKCk7XG4gICAgKiBcbiAgICAqL1xuXHRoaWRlRm9ybWF0U2VsZWN0b3IgOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLl9oZWFkZXJEaXYuaGlkZSgpO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBIaWRlcyB0aGUgd2hvbGUgY29tcG9uZW50LlxuICAgICogXG4gICAgKi9cblx0aGlkZSA6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9oZWFkZXJEaXYuaGlkZSgpO1xuXHRcdHRoaXMuX2NvbnRlbnREaXYuaGlkZSgpO1xuXHR9LFxuXG5cdC8qKlxuICAgICogU2hvd3MgdGhlIHdob2xlIGNvbXBvbmVudC5cbiAgICAqIFxuICAgICovXG5cdHNob3cgOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5faGVhZGVyRGl2LnNob3coKTtcblx0XHR0aGlzLl9jb250ZW50RGl2LnNob3coKTtcblx0fSxcblx0LyogXG4gICAgICogRnVuY3Rpb246IFNlcXVlbmNlLl9zZXRTZWxlY3Rpb25cbiAgICAgKiBQdXJwb3NlOiAgVXBkYXRlIHRoZSBjdXJyZW50IHNlbGVjdGlvbi4gXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IHN0YXJ0IC0+IHtpbnR9IFN0YXJ0IG9mIHRoZSByZWdpb24gdG8gYmUgc2VsZWN0ZWQuXG4gICAgICogXHRcdCAgIGVuZCAtPiB7aW50fSBFbmQgb2YgdGhlIHJlZ2lvbiB0byBiZSBzZWxlY3RlZC5cbiAgICAgKi9cblx0X3NldFNlbGVjdGlvbiA6IGZ1bmN0aW9uKHN0YXJ0LCBlbmQpIHtcblx0XHQvL2FsZXJ0KFwiYWRzYXNcIik7XG5cdFx0XG5cdFx0dmFyIGN1cnJlbnQgPSB0aGlzLm9wdC5zZWxlY3Rpb247XG5cdFx0dmFyIGNoYW5nZSA9IHt9O1xuXHRcdFxuXHRcdC8vIFdoaWNoIGlzIHRoZSBjaGFuZ2Ugb24gc2VsZWN0aW9uP1xuXHRcdGlmICggY3VycmVudC5zdGFydCA9PSBzdGFydCApIHtcblx0XHRcdC8vIGZvcndhcmQ/XG5cdFx0XHRpZiAoIGN1cnJlbnQuZW5kIDwgZW5kICkge1xuXHRcdFx0XHRjaGFuZ2Uuc3RhcnQgPSBjdXJyZW50LmVuZDtcblx0XHRcdFx0Y2hhbmdlLmVuZCA9IGVuZDtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuX3Jlc3RvcmVIaWdobGlnaHRzKGVuZCsxLCBjdXJyZW50LmVuZCk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIGlmICggY3VycmVudC5lbmQgPT0gZW5kICkge1xuXHRcdFx0Ly8gZm9yd2FyZD9cblx0XHRcdGlmICggY3VycmVudC5zdGFydCA+IHN0YXJ0ICkge1xuXHRcdFx0XHRjaGFuZ2Uuc3RhcnQgPSBzdGFydDtcblx0XHRcdFx0Y2hhbmdlLmVuZCA9IGN1cnJlbnQuc3RhcnQ7XHRcdFx0XHRcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuX3Jlc3RvcmVIaWdobGlnaHRzKGN1cnJlbnQuc3RhcnQsIHN0YXJ0LTEpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9yZXN0b3JlSGlnaGxpZ2h0cyhjdXJyZW50LnN0YXJ0LCBjdXJyZW50LmVuZCk7XG5cdFx0XHRjaGFuZ2Uuc3RhcnQgPSBzdGFydDtcblx0XHRcdGNoYW5nZS5lbmQgPSBlbmQ7XG5cdFx0fVxuXG5cdFx0Y3VycmVudC5zdGFydCA9IHN0YXJ0O1xuXHRcdGN1cnJlbnQuZW5kID0gZW5kO1xuXG5cdFx0aWYgKCBjaGFuZ2Uuc3RhcnQgIT0gdW5kZWZpbmVkICkge1xuXHRcdFx0dGhpcy5fYXBwbHlIaWdobGlnaHQoe1xuXHRcdFx0XHRcInN0YXJ0XCI6IGNoYW5nZS5zdGFydCwgXG5cdFx0XHRcdFwiZW5kXCI6IGNoYW5nZS5lbmQsIFxuXHRcdFx0XHRcImNvbG9yXCI6IHRoaXMub3B0LnNlbGVjdGlvbkZvbnRDb2xvciwgXG5cdFx0XHRcdFwiYmFja2dyb3VuZFwiOiB0aGlzLm9wdC5zZWxlY3Rpb25Db2xvciBcblx0XHRcdH0pO1xuXHRcdH1cblx0XHRcblx0fSxcblx0XG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fcmVwYWludFNlbGVjdGlvblxuICAgICAqIFB1cnBvc2U6ICBSZXBhaW50IHRoZSB3aG9sZSBjdXJyZW50IHNlbGVjdGlvbi4gXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IC1cbiAgICAgKi9cblx0X3JlcGFpbnRTZWxlY3Rpb246IGZ1bmN0aW9uKCl7XG5cdFx0dmFyIHMgPSB0aGlzLm9wdC5zZWxlY3Rpb247XG5cdFx0dGhpcy5fc2V0U2VsZWN0aW9uKDAsMCk7XG5cdFx0dGhpcy5fc2V0U2VsZWN0aW9uKHMuc3RhcnQscy5lbmQpO1xuXHR9LFxuXHRcblx0LyogXG4gICAgICogRnVuY3Rpb246IFNlcXVlbmNlLl9yZWRyYXdcbiAgICAgKiBQdXJwb3NlOiAgUmVwYWludCB0aGUgY3VycmVudCBzZXF1ZW5jZS4gXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IC1cbiAgICAgKi9cblx0X3JlZHJhdyA6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBpID0gMDtcdFxuXHRcdHZhciBzZWxmID0gdGhpcztcblx0XHRcblx0XHQvLyBSZXNldCB0aGUgY29udGVudFxuXHRcdC8vdGhpcy5fY29udGVudERpdi50ZXh0KCcnKTtcblx0XHR0aGlzLl9jb250ZW50RGl2LmNoaWxkcmVuKCkucmVtb3ZlKCk7XG5cdFx0XG5cdFx0Ly8gUmVidWlsZCB0aGUgc3BhbnMgb2YgdGhlIHNlcXVlbmNlIFxuXHRcdC8vIGFjY29yZGluZyB0byBmb3JtYXRcblx0XHRpZih0aGlzLm9wdC5mb3JtYXQgPT0gJ1JBVycpIHtcblx0XHRcdHRoaXMuX2RyYXdSYXcoKTtcblx0XHR9IGVsc2UgaWYodGhpcy5vcHQuZm9ybWF0ID09ICdDT0RBVEEnKSB7XG5cdFx0XHR0aGlzLl9kcmF3Q29kYXRhKCk7XG5cdFx0fSBlbHNlIGlmICh0aGlzLm9wdC5mb3JtYXQgPT0gJ0ZBU1RBJyl7XG5cdFx0XHR0aGlzLl9kcmF3RmFzdGEoKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5vcHQuZm9ybWF0ID0gJ1BSSURFJztcblx0XHRcdHRoaXMuX2RyYXdQcmlkZSgpO1xuXHRcdH1cblx0XHRcblx0XHQvLyBSZXN0b3JlIHRoZSBoaWdobGlnaHRlZCByZWdpb25zXG5cdFx0dGhpcy5fYXBwbHlIaWdobGlnaHRzKHRoaXMuX2hpZ2hsaWdodHMpO1xuXHRcdHRoaXMuX3JlcGFpbnRTZWxlY3Rpb24oKTtcblx0XHR0aGlzLl9hZGRTcGFuRXZlbnRzKCk7XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZHJhd0Zhc3RhXG4gICAgICogUHVycG9zZTogIFJlcGFpbnQgdGhlIGN1cnJlbnQgc2VxdWVuY2UgdXNpbmcgRkFTVEEgZm9ybWF0LiAgXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IC1cbiAgICAgKi9cblx0X2RyYXdGYXN0YSA6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBzZWxmID0gdGhpcztcblx0XHR2YXIgYSA9IHRoaXMub3B0LnNlcXVlbmNlLnRvVXBwZXJDYXNlKCkuc3BsaXQoJycpO1xuXHRcdHZhciBwcmUgPSBqUXVlcnkoJzxwcmU+PC9wcmU+JykuYXBwZW5kVG8odGhpcy5fY29udGVudERpdik7XG5cblx0XHR2YXIgaSA9IDE7XG5cdFx0dmFyIGFyciA9IFtdO1xuXHQgICAgdmFyIHN0ciA9ICc+JyArIHRoaXMub3B0LmlkICsgJyAnICsgYS5sZW5ndGggKyAnIGJwPGJyLz4nO1xuXHRcdFxuXHRcdC8qIENvcnJlY3QgY29sdW1uIHNpemUgaW4gY2FzZSB0aGUgc2VxdWVuY2UgaXMgYXMgc21hbGwgcGVwdGlkZSAqL1xuXHRcdHZhciBudW1Db2xzID0gdGhpcy5vcHQuY29sdW1ucy5zaXplO1xuXHRcdGlmICggdGhpcy5vcHQuc2VxdWVuY2UubGVuZ3RoIDwgdGhpcy5vcHQuY29sdW1ucy5zaXplICkge1xuXHRcdFx0bnVtQ29scyA9IHRoaXMub3B0LnNlcXVlbmNlLmxlbmd0aDtcdFxuXHRcdH1cblx0XHRcblx0ICAgIHZhciBvcHQgPSB7XG5cdFx0XHRudW1Db2xzOiBudW1Db2xzLFxuXHRcdCAgICBudW1Db2xzRm9yU3BhY2U6IDBcblx0XHR9O1xuXG5cdFx0c3RyICs9IHRoaXMuX2RyYXdTZXF1ZW5jZShhLCBvcHQpO1xuXHRcdHByZS5odG1sKHN0cik7XG5cdFx0XG5cdFx0dGhpcy5fZHJhd0Fubm90YXRpb25zKG9wdCk7XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZHJhd0NvZGF0YVxuICAgICAqIFB1cnBvc2U6ICBSZXBhaW50IHRoZSBjdXJyZW50IHNlcXVlbmNlIHVzaW5nIENPREFUQSBmb3JtYXQuICBcbiAgICAgKiBSZXR1cm5zOiAgLVxuICAgICAqIElucHV0czogLVxuICAgICAqL1xuXHRfZHJhd0NvZGF0YSA6IGZ1bmN0aW9uKCkge1xuXHRcdFxuXHRcdHZhciBzZWxmID0gdGhpcztcblx0XHR2YXIgYSA9IHRoaXMub3B0LnNlcXVlbmNlLnRvVXBwZXJDYXNlKCkuc3BsaXQoJycpO1xuXHRcdHZhciBwcmUgPSBqUXVlcnkoJzxwcmUgc3R5bGU9XCJ3aGl0ZS1zcGFjZTpwcmVcIj48L3ByZT4nKS5hcHBlbmRUbyh0aGlzLl9jb250ZW50RGl2KTtcblxuXHRcdHZhciBpID0gMDtcblx0XHR2YXIgc3RyID0gJ0VOVFJZICAgICAgICAgICAnICsgdGhpcy5vcHQuaWQgKyAnPGJyLz4nO1xuXHRcdHN0ciArPSAnU0VRVUVOQ0U8YnIvPic7XG5cdFx0aWYgKCB0aGlzLm9wdC5mb3JtYXRPcHRpb25zICE9PSB1bmRlZmluZWQgKXtcblx0XHRcdGlmKHRoaXMub3B0LmZvcm1hdE9wdGlvbnMudGl0bGUgIT09IHVuZGVmaW5lZCApe1xuXHRcdFx0XHRpZiAodGhpcy5vcHQuZm9ybWF0T3B0aW9ucy50aXRsZSA9PSBmYWxzZSkge1xuXHRcdFx0XHRcdHN0ciA9ICcnO1xuXHRcdFx0XHR9XHRcdFx0XG5cdFx0XHR9XG5cdFx0fSBcblx0XHRcblx0XHQvKiBDb3JyZWN0IGNvbHVtbiBzaXplIGluIGNhc2UgdGhlIHNlcXVlbmNlIGlzIGFzIHNtYWxsIHBlcHRpZGUgKi9cblx0XHR2YXIgbnVtQ29scyA9IHRoaXMub3B0LmNvbHVtbnMuc2l6ZTtcblx0XHRpZiAoIHRoaXMub3B0LnNlcXVlbmNlLmxlbmd0aCA8IHRoaXMub3B0LmNvbHVtbnMuc2l6ZSApIHtcblx0XHRcdG51bUNvbHMgPSB0aGlzLm9wdC5zZXF1ZW5jZS5sZW5ndGg7XHRcblx0XHR9XG5cdFx0XG5cdFx0dmFyIG9wdCA9IHtcblx0XHRcdFx0bnVtTGVmdDogdHJ1ZSxcblx0XHRcdFx0bnVtTGVmdFNpemU6IDcsXG5cdFx0XHRcdG51bUxlZnRQYWQ6JyAnLFxuXHRcdFx0XHRudW1Ub3A6IHRydWUsXG5cdFx0XHRcdG51bVRvcEVhY2g6IDUsXG5cdFx0XHRcdG51bUNvbHM6IG51bUNvbHMsXG5cdFx0XHQgICAgbnVtQ29sc0ZvclNwYWNlOiAwLFxuXHRcdFx0ICAgIHNwYWNlQmV0d2VlbkNoYXJzOiB0cnVlXG5cdFx0fTtcblx0XHRcblx0XHRzdHIgKz0gdGhpcy5fZHJhd1NlcXVlbmNlKGEsIG9wdCk7XG5cdFx0XG5cdFx0dmFyIGZvb3RlciA9ICc8YnIvPi8vLyc7XG5cdFx0aWYgKHRoaXMub3B0LmZvcm1hdE9wdGlvbnMgIT09IHVuZGVmaW5lZCkge1xuXHRcdFx0aWYgKHRoaXMub3B0LmZvcm1hdE9wdGlvbnMuZm9vdGVyICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0aWYgKHRoaXMub3B0LmZvcm1hdE9wdGlvbnMuZm9vdGVyID09IGZhbHNlKSB7XG5cdFx0XHRcdFx0Zm9vdGVyID0gJyc7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0c3RyICs9IGZvb3Rlcjtcblx0XHRwcmUuaHRtbChzdHIpO1xuXHRcdFxuXHRcdHRoaXMuX2RyYXdBbm5vdGF0aW9ucyhvcHQpO1xuXHR9LFxuXHQvKiBcbiAgICAgKiBGdW5jdGlvbjogU2VxdWVuY2UuX2RyYXdBbm5vdGF0aW9uc1xuICAgICAqIFB1cnBvc2U6ICBQYWludCB0aGUgYW5ub3RhdGlvbnMgb24gdGhlIHNlcXVlbmNlLiAgXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IHNldHRpbmdzIC0+IHtvYmplY3R9IFxuICAgICAqL1xuICAgIF9kcmF3QW5ub3RhdGlvbnM6IGZ1bmN0aW9uICggc2V0dGluZ3MgKXsgXG4gICAgXHRcbiAgICBcdHZhciBzZWxmID0gdGhpcztcbiAgICBcdHZhciBhID0gdGhpcy5vcHQuc2VxdWVuY2UudG9Mb3dlckNhc2UoKS5zcGxpdCgnJyk7ICAgIFx0XG4gICAgXHR2YXIgYW5ub3RhdGlvbnMgPSB0aGlzLl9hbm5vdGF0aW9ucztcbiAgICBcdHZhciBsZWZ0U3BhY2VzID0gJyc7XG4gICAgXHR2YXIgcm93ID0gJyc7XG4gICAgXHR2YXIgYW5ub3QgPSAnJztcbiAgICBcdFxuICAgIFx0Ly8gSW5kZXggYXQgdGhlIGxlZnQ/XG5cdFx0aWYgKCBzZXR0aW5ncy5udW1MZWZ0ICkge1xuXHRcdFx0bGVmdFNwYWNlcyArPSB0aGlzLl9mb3JtYXRJbmRleCgnICcsIHNldHRpbmdzLm51bUxlZnRTaXplKzIsICcgJyk7XG5cdFx0fVxuXG5cdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgYS5sZW5ndGg7IGkgKz0gc2V0dGluZ3MubnVtQ29scyApe1xuXHRcdFx0cm93ID0gJyc7XG5cdFx0XHRmb3IgKCB2YXIga2V5IGluIGFubm90YXRpb25zICl7XG5cdFx0XHRcdGFubm90YXRpb25zW2tleV0uaWQgPSB0aGlzLmdldElkKCkgKyBcIl9cIiArIGtleTtcblx0XHRcdFx0YW5ub3QgPSB0aGlzLl9nZXRIVE1MUm93QW5ub3QoaSsxLCBhbm5vdGF0aW9uc1trZXldLCBzZXR0aW5ncyk7XHRcdFx0XHRcblx0XHRcdFx0aWYgKGFubm90Lmxlbmd0aCA+IDApIHtcblx0XHRcdFx0XHRyb3cgKz0gJzxici8+Jztcblx0XHRcdFx0XHRyb3cgKz0gbGVmdFNwYWNlcztcblx0XHRcdFx0XHRyb3cgKz0gYW5ub3Q7XG5cdFx0XHRcdFx0cm93ICs9ICc8YnIvPic7XG5cdFx0XHRcdH0gXG5cdFx0XHR9XG5cdFx0XHRcblx0XHRcdHZhciBudW1Db2xzID0gc2V0dGluZ3MubnVtQ29scztcblx0XHRcdHZhciBjaGFyUmVtYWluaW5nID0gYS5sZW5ndGgtaTtcblx0XHRcdGlmKGNoYXJSZW1haW5pbmcgPCBudW1Db2xzKXtcblx0XHRcdFx0bnVtQ29sc1x0PSBjaGFyUmVtYWluaW5nO1xuXHRcdFx0fVxuXHRcdFx0XG5cdFx0XHRpZiAoIHNldHRpbmdzLm51bVJpZ2h0ICkge1xuXHRcdFx0XHRqUXVlcnkocm93KS5pbnNlcnRBZnRlcignZGl2Iycrc2VsZi5vcHQudGFyZ2V0KycgZGl2IHByZSBzcGFuI251bVJpZ2h0XycgKyB0aGlzLmdldElkKCkgKyAnXycgKyAoaSArIG51bUNvbHMpICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRqUXVlcnkocm93KS5pbnNlcnRBZnRlcignZGl2Iycrc2VsZi5vcHQudGFyZ2V0KycgZGl2IHByZSBzcGFuIycrIHRoaXMuZ2V0SWQoKSArICdfJyArIChpICsgbnVtQ29scykgKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0XG5cdFx0Ly8gYWRkIHRvb2wgdGlwcyBhbmQgYmFja2dyb3VuZCcgY29sb3JpbmcgZWZmZWN0XG5cdFx0alF1ZXJ5KHRoaXMuX2NvbnRlbnREaXYpLmZpbmQoJy5hbm5vdGF0aW9uJykuZWFjaCggZnVuY3Rpb24oKXtcblx0XHRcdHNlbGYuX2FkZFRvb2xUaXAoIHRoaXMsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gc2VsZi5fZ2V0QW5ub3RhdGlvblN0cmluZyggalF1ZXJ5KHRoaXMpLmF0dHIoXCJpZFwiKSApO1xuXHRcdFx0fSk7XG5cdFx0XHRcblx0XHRcdGpRdWVyeSh0aGlzKS5tb3VzZW92ZXIoZnVuY3Rpb24oZSkge1xuXHRcdFx0XHRqUXVlcnkoJy5hbm5vdGF0aW9uLicralF1ZXJ5KGUudGFyZ2V0KS5hdHRyKFwiaWRcIikpLmVhY2goZnVuY3Rpb24oKXtcblx0XHRcdFx0XHRqUXVlcnkodGhpcykuY3NzKFwiYmFja2dyb3VuZC1jb2xvclwiLCBqUXVlcnkodGhpcykuYXR0cihcImNvbG9yXCIpICk7XG5cdFx0XHRcdH0pO1xuXHRcdCAgICB9KS5tb3VzZW91dChmdW5jdGlvbigpIHtcblx0XHQgICAgXHRqUXVlcnkoJy5hbm5vdGF0aW9uJykuY3NzKFwiYmFja2dyb3VuZC1jb2xvclwiLCBcInRyYW5zcGFyZW50XCIpOyBcblx0XHQgICAgXHRcblx0XHQgICAgfSkuY2xpY2soZnVuY3Rpb24oZSkge1xuXHRcdCAgICBcdFx0dmFyIG5hbWUgPSB1bmRlZmluZWQ7XG5cdFx0ICAgIFx0XHR2YXIgaWQgPSBqUXVlcnkoZS50YXJnZXQpLmF0dHIoXCJpZFwiKTtcblx0XHQgICAgXHRcdGZvcih2YXIgaSA9MDsgaSA8IHNlbGYuX2Fubm90YXRpb25zLmxlbmd0aDtpKyspe1xuICAgICAgICAgICAgICBpZihzZWxmLl9hbm5vdGF0aW9uc1tpXS5pZCA9PSBpZCl7XG4gICAgICAgICAgICAgICAgbmFtZSA9IHNlbGYuX2Fubm90YXRpb25zW2ldLm5hbWU7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblx0XHQgICAgXHRzZWxmLnRyaWdnZXIoIEVWVF9PTl9BTk5PVEFUSU9OX0NMSUNLRUQsIHtcblx0ICAgIFx0XHRcIm5hbWVcIjogbmFtZSxcblx0XHQgICAgXHRcdC8vXCJwb3NcIjogcGFyc2VJbnQoIGpRdWVyeShlLnRhcmdldCkuYXR0cihcInBvc1wiKSApXG5cdFx0ICAgIFx0fSk7XG5cdFx0ICAgIH0pO1xuXHRcdFx0XG5cdFx0fSk7XG5cbiAgICB9LFxuICAgIC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZ2V0QW5ub3RhdGlvblN0cmluZ1xuICAgICAqIFB1cnBvc2U6ICBHZXQgdGhlIGFubm90YXRpb24gdGV4dCBtZXNzYWdlIGZvciB0aGUgdG9vbHRpcCBcbiAgICAgKiBSZXR1cm5zOiAge3N0cmluZ30gQW5ub3RhdGlvbiB0ZXh0IGZvciB0aGUgYW5ub3RhdGlvblxuICAgICAqIElucHV0czogICBpZCAtPiB7aW50fSBpbmRleCBvZiB0aGUgaW50ZXJuYWwgYW5ub3RhdGlvbiBhcnJheVxuICAgICAqL1xuICAgIF9nZXRBbm5vdGF0aW9uU3RyaW5nOiBmdW5jdGlvbiAoIGlkICkge1xuXHRcdHZhciBhbm5vdGF0aW9uID0gdGhpcy5fYW5ub3RhdGlvbnNbaWQuc3Vic3RyKGlkLmluZGV4T2YoXCJfXCIpICsgMSldO1xuXHRcdHJldHVybiBhbm5vdGF0aW9uLm5hbWUgKyBcIjxici8+XCIgKyAoKGFubm90YXRpb24uaHRtbCk/IGFubm90YXRpb24uaHRtbCA6ICcnKTtcbiAgICB9LFxuICAgIFxuICAgIC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZ2V0SFRNTFJvd0Fubm90XG4gICAgICogUHVycG9zZTogIEJ1aWxkIGFuIGFubm90YXRpb25cbiAgICAgKiBSZXR1cm5zOiAgSFRNTCBvZiB0aGUgYW5ub3RhdGlvblxuICAgICAqIElucHV0czogICBjdXJyZW50UG9zIC0+IHtpbnR9XG4gICAgICogXHRcdFx0IGFubm90YXRpb24gLT4ge09iamVjdH0gXG4gICAgICogIFx0XHQgc2V0dGluZ3MgLT4ge09iamVjdH1cbiAgICAgKi9cbiAgICBfZ2V0SFRNTFJvd0Fubm90IDogZnVuY3Rpb24gKGN1cnJlbnRQb3MsIGFubm90YXRpb24sIHNldHRpbmdzKSB7XG4gICAgXHR2YXIgc3R5bGVCZWdpbiA9ICdib3JkZXItbGVmdDoxcHggc29saWQ7IGJvcmRlci1ib3R0b206MXB4IHNvbGlkOyBib3JkZXItY29sb3I6JztcbiAgICBcdHZhciBzdHlsZU9uID0gJ2JvcmRlci1ib3R0b206MXB4IHNvbGlkOyBib3JkZXItY29sb3I6JztcbiAgICBcdHZhciBzdHlsZUVuZCA9ICdib3JkZXItYm90dG9tOjFweCBzb2xpZDsgYm9yZGVyLXJpZ2h0OjFweCBzb2xpZDsgYm9yZGVyLWNvbG9yOic7XG5cdFx0dmFyIHN0eWxlQmVnaW5BbmRFbmQgPSAnYm9yZGVyLWxlZnQ6MXB4IHNvbGlkOyBib3JkZXItcmlnaHQ6MXB4IHNvbGlkOyBib3JkZXItYm90dG9tOjFweCBzb2xpZDsgYm9yZGVyLWNvbG9yOic7XG4gICAgXHRcbiAgICBcdHZhciByb3cgPSBbXTtcbiAgICBcdHZhciBlbmQgPSAoY3VycmVudFBvcyArIHNldHRpbmdzLm51bUNvbHMpO1xuICAgIFx0dmFyIHNwYWNlQmV0d2VlbkNoYXJzID0gKHNldHRpbmdzLnNwYWNlQmV0d2VlbkNoYXJzKT8gJyAnIDogJyc7ICAgIFx0XG4gICAgXHR2YXIgZGVmYXVsdENvbG9yID0gYW5ub3RhdGlvbi5jb2xvcjtcbiAgICBcdHZhciBpZCA9IGFubm90YXRpb24uaWQ7XG4gICAgXHRmb3IgKCB2YXIgcG9zPWN1cnJlbnRQb3M7IHBvcyA8IGVuZCA7IHBvcysrICkge1xuXHRcdFx0Ly8gcmVnaW9uc1xuXHRcdFx0Zm9yICggdmFyIHIgaW4gYW5ub3RhdGlvbi5yZWdpb25zICkge1xuXHRcdFx0XHRyZWdpb24gPSBhbm5vdGF0aW9uLnJlZ2lvbnNbcl07XG5cdFx0XHRcdFxuXHRcdFx0XHRzcGFjZUFmdGVyID0gJyc7XG5cdFx0XHRcdHNwYWNlQWZ0ZXIgKz0gKHBvcyAlIHNldHRpbmdzLm51bUNvbHNGb3JTcGFjZSA9PSAwICk/ICcgJyA6ICcnO1xuXHRcdFx0XHRzcGFjZUFmdGVyICs9IHNwYWNlQmV0d2VlbkNoYXJzO1xuXHRcdFx0XHRcblx0XHRcdFx0Y29sb3IgPSAoKHJlZ2lvbi5jb2xvcik/IHJlZ2lvbi5jb2xvciA6IGRlZmF1bHRDb2xvcik7XG5cdFx0XHRcdGRhdGEgPSAnY2xhc3M9XCJhbm5vdGF0aW9uICcraWQrJ1wiIGlkPVwiJytpZCsnXCIgY29sb3I9XCInK2NvbG9yKydcIiBwb3M9XCInK3BvcysnXCInO1xuXHRcdFx0XHRcblx0XHRcdFx0aWYgKCBwb3MgPT0gcmVnaW9uLnN0YXJ0ICYmIHBvcyA9PSByZWdpb24uZW5kKSB7XG5cdFx0XHRcdFx0cm93W3Bvc10gPSAnPHNwYW4gc3R5bGU9XCInK3N0eWxlQmVnaW5BbmRFbmQrY29sb3IrJ1wiICcrZGF0YSsnPiAnO1xuXHRcdFx0XHRcdHJvd1twb3NdICs9IHNwYWNlQWZ0ZXI7XG5cdFx0XHRcdFx0cm93W3Bvc10gKz0gJzwvc3Bhbj4nO1xuXHRcdFx0XHR9IGVsc2UgaWYgKCBwb3MgPT0gcmVnaW9uLnN0YXJ0ICkge1xuXHRcdFx0XHRcdHJvd1twb3NdID0gJzxzcGFuIHN0eWxlPVwiJytzdHlsZUJlZ2luK2NvbG9yKydcIiAnK2RhdGErJz4gJztcblx0XHRcdFx0XHRyb3dbcG9zXSArPSBzcGFjZUFmdGVyO1xuXHRcdFx0XHRcdHJvd1twb3NdICs9ICc8L3NwYW4+Jztcblx0XHRcdFx0fSBlbHNlIGlmICggcG9zID09IHJlZ2lvbi5lbmQgKSB7XG5cdFx0XHRcdFx0cm93W3Bvc10gPSAnPHNwYW4gc3R5bGU9XCInK3N0eWxlRW5kK2NvbG9yKycgXCIgJytkYXRhKyc+ICc7XG5cdFx0XHRcdFx0Ly9yb3dbcG9zXSArPSBzcGFjZUFmdGVyO1xuXHRcdFx0XHRcdHJvd1twb3NdICs9ICc8L3NwYW4+Jztcblx0XHRcdFx0fSBlbHNlIGlmICggcG9zID4gcmVnaW9uLnN0YXJ0ICYmIHBvcyA8IHJlZ2lvbi5lbmQgKSB7XG5cdFx0XHRcdFx0cm93W3Bvc10gPSAnPHNwYW4gc3R5bGU9XCInK3N0eWxlT24rY29sb3IrJ1wiICcrZGF0YSsnPiAnO1xuXHRcdFx0XHRcdHJvd1twb3NdICs9IHNwYWNlQWZ0ZXI7XG5cdFx0XHRcdFx0cm93W3Bvc10gKz0gJzwvc3Bhbj4nO1xuXHRcdFx0XHR9IGVsc2UgaWYgKCFyb3dbcG9zXSkge1xuXHRcdFx0XHRcdHJvd1twb3NdID0gJyAnO1xuXHRcdFx0XHRcdHJvd1twb3NdICs9IHNwYWNlQWZ0ZXI7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cbiAgICAgICBcdHZhciBzdHIgPSByb3cuam9pbihcIlwiKTtcbiAgICBcdFxuICAgIFx0cmV0dXJuICggc3RyLmluZGV4T2YoXCJzcGFuXCIpID09IC0xICk/IFwiXCIgOiBzdHI7XG4gICAgfSxcbiAgICAvKiBcbiAgICAgKiBGdW5jdGlvbjogU2VxdWVuY2UuX2RyYXdSYXdcbiAgICAgKiBQdXJwb3NlOiAgUmVwYWludCB0aGUgY3VycmVudCBzZXF1ZW5jZSB1c2luZyBSQVcgZm9ybWF0LiAgXG4gICAgICogUmV0dXJuczogIC1cbiAgICAgKiBJbnB1dHM6IC1cbiAgICAgKi9cblx0X2RyYXdSYXcgOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgc2VsZiA9IHRoaXM7XG5cdFx0dmFyIGEgPSB0aGlzLm9wdC5zZXF1ZW5jZS50b0xvd2VyQ2FzZSgpLnNwbGl0KCcnKTtcblx0XHR2YXIgaSA9IDA7XG5cdFx0dmFyIGFyciA9IFtdO1xuXHRcdHZhciBwcmUgPSBqUXVlcnkoJzxwcmU+PC9wcmU+JykuYXBwZW5kVG8odGhpcy5fY29udGVudERpdik7XG5cdFx0XG5cdFx0LyogQ29ycmVjdCBjb2x1bW4gc2l6ZSBpbiBjYXNlIHRoZSBzZXF1ZW5jZSBpcyBhcyBzbWFsbCBwZXB0aWRlICovXG5cdFx0dmFyIG51bUNvbHMgPSB0aGlzLm9wdC5jb2x1bW5zLnNpemU7XG5cdFx0aWYgKCB0aGlzLm9wdC5zZXF1ZW5jZS5sZW5ndGggPCB0aGlzLm9wdC5jb2x1bW5zLnNpemUgKSB7XG5cdFx0XHRudW1Db2xzID0gdGhpcy5vcHQuc2VxdWVuY2UubGVuZ3RoO1x0XG5cdFx0fVxuXG5cdFx0dmFyIG9wdCA9IHtcblx0XHRcdG51bUNvbHM6IG51bUNvbHNcblx0XHR9O1xuXHRcdFxuXHRcdHByZS5odG1sKFxuXHRcdFx0dGhpcy5fZHJhd1NlcXVlbmNlKGEsIG9wdClcblx0XHQpO1xuXHRcdFxuXHRcdHRoaXMuX2RyYXdBbm5vdGF0aW9ucyhvcHQpO1xuXHR9LFxuXHQvKiBcbiAgICAgKiBGdW5jdGlvbjogU2VxdWVuY2UuX2RyYXdQcmlkZVxuICAgICAqIFB1cnBvc2U6ICBSZXBhaW50IHRoZSBjdXJyZW50IHNlcXVlbmNlIHVzaW5nIFBSSURFIGZvcm1hdC4gIFxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiAtXG4gICAgICovXG5cdF9kcmF3UHJpZGUgOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgc2VsZiA9IHRoaXM7XG5cdFx0dmFyIGEgPSB0aGlzLm9wdC5zZXF1ZW5jZS50b1VwcGVyQ2FzZSgpLnNwbGl0KCcnKTtcblx0XHR2YXIgcHJlID0galF1ZXJ5KCc8cHJlPjwvcHJlPicpLmFwcGVuZFRvKHRoaXMuX2NvbnRlbnREaXYpO1xuXHRcdFxuXHRcdC8qIENvcnJlY3QgY29sdW1uIHNpemUgaW4gY2FzZSB0aGUgc2VxdWVuY2UgaXMgYXMgc21hbGwgcGVwdGlkZSAqL1xuXHRcdHZhciBudW1Db2xzID0gdGhpcy5vcHQuY29sdW1ucy5zaXplO1xuXHRcdGlmICggdGhpcy5vcHQuc2VxdWVuY2UubGVuZ3RoIDwgdGhpcy5vcHQuY29sdW1ucy5zaXplICkge1xuXHRcdFx0bnVtQ29scyA9IHRoaXMub3B0LnNlcXVlbmNlLmxlbmd0aDtcdFxuXHRcdH1cblx0XG5cdFx0b3B0ID0ge1xuXHRcdFx0bnVtTGVmdDogdHJ1ZSxcblx0XHRcdG51bUxlZnRTaXplOiA1LFxuXHRcdFx0bnVtTGVmdFBhZDonMCcsXG5cdFx0XHRudW1SaWdodDogdHJ1ZSxcblx0XHRcdG51bVJpZ2h0U2l6ZTogNSxcblx0XHRcdG51bVJpZ2h0UGFkOiAnMCcsXG5cdFx0XHRudW1Db2xzOiBudW1Db2xzLFxuXHRcdCAgICBudW1Db2xzRm9yU3BhY2U6IHNlbGYub3B0LmNvbHVtbnMuc3BhY2VkRWFjaFxuXHRcdH07XG5cdFx0XG5cdFx0cHJlLmh0bWwoXG5cdFx0XHR0aGlzLl9kcmF3U2VxdWVuY2UoYSwgb3B0KVxuXHRcdCk7XG5cdFx0XG5cdFx0dGhpcy5fZHJhd0Fubm90YXRpb25zKG9wdCk7XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZHJhd1NlcXVlbmNlXG4gICAgICogUHVycG9zZTogIFJlcGFpbnQgdGhlIGN1cnJlbnQgc2VxdWVuY2UgdXNpbmcgQ1VTVE9NIGZvcm1hdC4gIFxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiAgIGEgLT4ge2NoYXJbXX0gYSBUaGUgc2VxdWVuY2Ugc3RyYW5kLlxuICAgICAqIFx0XHRcdCBvcHQgLT4ge09iamVjdH0gb3B0IFRoZSBDVVNUT00gZm9ybWF0LlxuICAgICAqL1xuXHRfZHJhd1NlcXVlbmNlIDogZnVuY3Rpb24oYSwgb3B0KSB7XG5cdFx0dmFyIHN0ciA9ICcnO1xuXG5cdFx0dmFyIHNwYWNlU3R5bGUgPSAgXCJ3aGl0ZS1zcGFjZTogcHJlO1wiO1xuXHRcdFxuXHRcdC8vIEluZGV4IGF0IHRvcD9cblx0XHRpZiggb3B0Lm51bVRvcCApXG5cdFx0e1xuXHRcdFx0c3RyICs9ICc8c3BhbiBzdHlsZT1cIicrc3BhY2VTdHlsZSsnXCIgY2xhc3M9XCJudW1Ub3BcIj4nXG5cdFx0XHR2YXIgc2l6ZSA9IChvcHQuc3BhY2VCZXR3ZWVuQ2hhcnMpPyBvcHQubnVtVG9wRWFjaCoyOiBvcHQubnVtVG9wRWFjaDtcblx0XHRcdFxuXHRcdFx0aWYgKG9wdC5udW1MZWZ0KSB7XG5cdFx0XHRcdHN0ciArPSB0aGlzLl9mb3JtYXRJbmRleCgnICcsIG9wdC5udW1MZWZ0U2l6ZSwgJyAnKTtcblx0XHRcdH1cblx0XHRcdFxuXHRcdFx0c3RyICs9IHRoaXMuX2Zvcm1hdEluZGV4KCcgJywgc2l6ZSwgJyAnKTtcblx0XHRcdFxuXHRcdFx0Zm9yKHZhciB4ID0gb3B0Lm51bVRvcEVhY2g7IHggPCBvcHQubnVtQ29sczsgeCArPSBvcHQubnVtVG9wRWFjaCkge1xuXHRcdFx0XHRzdHIgKz0gdGhpcy5fZm9ybWF0SW5kZXgoeCwgc2l6ZSwgJyAnLCB0cnVlKTtcblx0XHRcdH1cblx0XHRcdHN0ciArPSAnPC9zcGFuPjxici8+J1xuXHRcdH1cblx0XHRcblx0XHRcblx0XHQvLyBJbmRleCBhdCB0aGUgbGVmdD9cblx0XHRpZiAob3B0Lm51bUxlZnQpIHtcblx0XHRcdHN0ciArPSB0aGlzLl9mb3JtYXRJbmRleCgxLCBvcHQubnVtTGVmdFNpemUsIG9wdC5udW1MZWZ0UGFkKTtcblx0XHRcdHN0ciArPSAnICAnO1xuXHRcdH1cblxuXHRcdHZhciBqPTE7XG5cdFx0Zm9yICh2YXIgaT0xOyBpIDw9IGEubGVuZ3RoOyBpKyspIHtcblxuXHRcdFx0aWYoIGkgJSBvcHQubnVtQ29scyA9PSAwKSB7XHRcblx0XHRcdFx0c3RyICs9ICc8c3BhbiBjbGFzcz1cInNlcXVlbmNlXCIgaWQ9XCInICsgdGhpcy5nZXRJZCgpICsgJ18nICsgaSArICdcIj4nICsgYVtpLTFdICsgJzwvc3Bhbj4nO1xuXHRcdFx0XHRcblx0XHRcdFx0aWYgKG9wdC5udW1SaWdodCkge1xuXHRcdFx0XHRcdHN0ciArPSAnPHNwYW4gc3R5bGU9XCInK3NwYWNlU3R5bGUrJ1wiIGlkPVwibnVtUmlnaHRfJyArIHRoaXMuZ2V0SWQoKSArICdfJyArIGkgKyAnXCI+Jztcblx0XHRcdFx0XHRzdHIgKz0gJyAgJztcblx0XHRcdFx0XHRzdHIgKz0gdGhpcy5fZm9ybWF0SW5kZXgoaSwgb3B0Lm51bVJpZ2h0U2l6ZSwgb3B0Lm51bVJpZ2h0UGFkKTtcdFxuXHRcdFx0XHRcdHN0ciArPSAnPC9zcGFuPic7XG5cdFx0XHRcdH1cblx0XHRcdFx0XG5cdFx0XHRcdHN0ciArPSAnPGJyLz4nO1xuXHRcdFx0XHRcblx0XHRcdFx0dmFyIGFhUmVtYWluaW5nID0gYS5sZW5ndGggLSBpO1xuXHRcdFx0XHRpZiAob3B0Lm51bUxlZnQgJiYgYWFSZW1haW5pbmcgPiAwKSB7XG5cdFx0XHRcdFx0c3RyICs9ICc8c3BhbiBpZD1cIm51bUxlZnRfJyArIHRoaXMuZ2V0SWQoKSArICdfJyArIGkgKyAnXCI+Jztcblx0XHRcdFx0XHRzdHIgKz0gdGhpcy5fZm9ybWF0SW5kZXgoaSsxLCBvcHQubnVtTGVmdFNpemUsIG9wdC5udW1MZWZ0UGFkKTtcblx0XHRcdFx0XHRzdHIgKz0gJyAgJztcblx0XHRcdFx0XHRzdHIgKz0gJzwvc3Bhbj4nO1xuXHRcdFx0XHR9XG5cdFx0XHRcdFxuXHRcdFx0XHRqID0gMTtcblx0XHRcdFx0XG5cdFx0XHR9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHN0ciArPSAnPHNwYW4gY2xhc3M9XCJzZXF1ZW5jZVwiIHN0eWxlPVwiJytzcGFjZVN0eWxlKydcIiBpZD1cIicgKyB0aGlzLmdldElkKCkgKyAnXycgKyBpICsgJ1wiPicgKyBhW2ktMV07XG5cdFx0XHRcdHN0ciArPSAoIGogJSBvcHQubnVtQ29sc0ZvclNwYWNlID09IDApPyAnICcgOiAnJztcblx0XHRcdFx0c3RyICs9IChvcHQuc3BhY2VCZXR3ZWVuQ2hhcnMpPyAnICcgOiAnJztcblx0XHRcdFx0c3RyICs9ICc8L3NwYW4+Jztcblx0XHRcdFx0aisrO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRcblx0XHRzdHIgKz0gJzxici8+J1x0XG5cdFx0XHRcblx0XHRpZiAoalF1ZXJ5LmJyb3dzZXIubXNpZSkge1xuXHRcdFx0c3RyID0gXCI8cHJlPlwiICsgc3RyICsgXCI8L3ByZT5cIjtcblx0XHR9XHRcblx0XHRcdFxuXHRcdHJldHVybiBzdHI7XG5cdH0sXG5cdC8qIFxuICAgICAqIEZ1bmN0aW9uOiBTZXF1ZW5jZS5fZm9ybWF0SW5kZXhcbiAgICAgKiBQdXJwb3NlOiAgQnVpbGQgdGhlIEhUTUwgY29ycmVzcG9uZGluZyB0byBjb3VudGluZyBudW1iZXJzICh0b3AsIGxlZnQsIHJpZ2h0KSBpbiB0aGUgc3RyYW5kLlxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiAgIG51bWJlciAtPiB7aW50fSBUaGUgbnVtYmVyIFxuICAgICAqIFx0XHRcdCBzaXplIC0+IHtpbnR9IE51bWJlciBvZiBiaW5zIHRvIHN1aXQgdGhlIG51bWJlci5cbiAgICAgKiBcdFx0XHQgZmlsbGluZ0NoYXIgLT4ge2NoYXJ9IENoYXJhY3RlciB0byBiZSB1c2VkIGZvciBmaWxsaW5nIG91dCBibGFuayBiaW5zLlxuICAgICAqIFx0XHRcdCBhbGlnbkxlZnQgLT4ge2Jvb2x9IFRlbGwgaWYgYWxpZ25lZCB0byB0aGUgbGVmdC5cbiAgICAgKi9cblx0X2Zvcm1hdEluZGV4IDogZnVuY3Rpb24oIG51bWJlciwgc2l6ZSwgZmlsbGluZ0NoYXIsIGFsaWduTGVmdCkge1xuXHRcdHZhciBzdHIgPSBudW1iZXIudG9TdHJpbmcoKTtcblx0XHR2YXIgZmlsbGluZyA9ICcnO1xuXHRcdHZhciBwYWRkaW5nID0gc2l6ZSAtIHN0ci5sZW5ndGg7XHRcblx0XHRpZiAoIHBhZGRpbmcgPiAwICkge1xuXHRcdFx0d2hpbGUgKCBwYWRkaW5nLS0gPiAwICkge1xuXHRcdFx0XHRmaWxsaW5nICs9IChcIjxzcGFuPlwiK2ZpbGxpbmdDaGFyK1wiPC9zcGFuPlwiKTtcblx0XHRcdH1cblx0XHRcdGlmIChhbGlnbkxlZnQpe1xuXHRcdFx0XHRzdHIgPSBudW1iZXIrZmlsbGluZztcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHN0ciA9IGZpbGxpbmcrbnVtYmVyO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gc3RyO1xuXHR9LFxuXHQvKiBcbiAgICAgKiBGdW5jdGlvbjogU2VxdWVuY2UuX2FkZFNwYW5FdmVudHNcbiAgICAgKiBQdXJwb3NlOiAgQWRkIHRoZSBldmVudCBoYW5kbGVycyB0byB0aGUgc3RyYW5kLlxuICAgICAqIFJldHVybnM6ICAtXG4gICAgICogSW5wdXRzOiAgIC1cbiAgICAgKi9cblx0X2FkZFNwYW5FdmVudHMgOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgc2VsZiA9IHRoaXM7XG5cdFx0dmFyIGlzTW91c2VEb3duID0gZmFsc2U7XG5cdFx0dmFyIGN1cnJlbnRQb3M7XG5cblx0XHRzZWxmLl9jb250ZW50RGl2LmZpbmQoJy5zZXF1ZW5jZScpLmVhY2goIGZ1bmN0aW9uICgpIHtcdFxuXHRcdFx0XG5cdFx0XHQvLyBSZWdpc3RlciB0aGUgc3RhcnRpbmcgcG9zaXRpb25cblx0XHRcdGpRdWVyeSh0aGlzKS5tb3VzZWRvd24oZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBpZCA9IGpRdWVyeSh0aGlzKS5hdHRyKCdpZCcpO1xuXHRcdFx0XHRjdXJyZW50UG9zID0gcGFyc2VJbnQoaWQuc3Vic3RyKGlkLmluZGV4T2YoXCJfXCIpICsgMSkpO1xuXHRcdFx0XHRjbGlja1BvcyA9IGN1cnJlbnRQb3M7XG5cdFx0XHRcdHNlbGYuX3NldFNlbGVjdGlvbihjbGlja1BvcyxjdXJyZW50UG9zKTtcblx0XHRcdFx0aXNNb3VzZURvd24gPSB0cnVlO1xuXHRcdFx0XHRcblx0XHRcdFx0Ly8gU2VsZWN0aW9uIGlzIGhhcHBlbmluZywgcmFpc2UgYW4gZXZlbnRcblx0XHRcdFx0c2VsZi50cmlnZ2VyKFxuXHRcdFx0XHRcdEVWVF9PTl9TRUxFQ1RJT05fQ0hBTkdFLCBcblx0XHRcdFx0XHR7IFxuXHRcdFx0XHRcdFx0XCJzdGFydFwiIDogc2VsZi5vcHQuc2VsZWN0aW9uLnN0YXJ0LCBcblx0XHRcdFx0XHRcdFwiZW5kXCIgOiBzZWxmLm9wdC5zZWxlY3Rpb24uZW5kIFxuXHRcdFx0XHRcdH1cblx0XHRcdFx0KTtcblx0XHRcdFxuXHRcdFx0fSkubW91c2VvdmVyKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHQvLyBVcGRhdGUgc2VsZWN0aW9uXG5cdFx0XHRcdC8vIFNob3cgdG9vbHRpcCBjb250YWluaW5nIHRoZSBwb3NpdGlvblxuXHRcdFx0XHR2YXIgaWQgPSBqUXVlcnkodGhpcykuYXR0cignaWQnKTtcblx0XHRcdFx0Y3VycmVudFBvcyA9IHBhcnNlSW50KGlkLnN1YnN0cihpZC5pbmRleE9mKFwiX1wiKSArIDEpKTtcblx0XHRcdFx0XG5cdFx0XHRcdGlmKGlzTW91c2VEb3duKSB7XG5cdFx0XHRcdFx0aWYoIGN1cnJlbnRQb3MgPiBjbGlja1BvcyApIHtcblx0XHRcdFx0XHRcdHNlbGYuX3NldFNlbGVjdGlvbihjbGlja1BvcywgY3VycmVudFBvcyk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHNlbGYuX3NldFNlbGVjdGlvbihjdXJyZW50UG9zLCBjbGlja1Bvcyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFxuXHRcdFx0XHRcdC8vIFNlbGVjdGlvbiBpcyBoYXBwZW5pbmcsIHJhaXNlIGFuIGV2ZW50XG5cdFx0XHRcdFx0c2VsZi50cmlnZ2VyKCBFVlRfT05fU0VMRUNUSU9OX0NIQU5HRSwgeyBcblx0XHRcdFx0XHRcdFwic3RhcnRcIiA6IHNlbGYub3B0LnNlbGVjdGlvbi5zdGFydCwgXG5cdFx0XHRcdFx0XHRcImVuZFwiIDogc2VsZi5vcHQuc2VsZWN0aW9uLmVuZCBcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0fSBcblx0XHRcdFx0XG5cdFx0XHR9KS5tb3VzZXVwKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpc01vdXNlRG93biA9IGZhbHNlO1xuXHRcdFx0XHQvLyBTZWxlY3Rpb24gaXMgZG9uZSwgcmFpc2UgYW4gZXZlbnRcblx0XHRcdFx0c2VsZi50cmlnZ2VyKCBFVlRfT05fU0VMRUNUSU9OX0NIQU5HRUQsIHsgXG5cdFx0XHRcdFx0XCJzdGFydFwiIDogc2VsZi5vcHQuc2VsZWN0aW9uLnN0YXJ0LCBcblx0XHRcdFx0XHRcImVuZFwiIDogc2VsZi5vcHQuc2VsZWN0aW9uLmVuZCBcblx0XHRcdFx0fSk7XG5cdFx0XHR9KTtcblx0XHRcdFxuXHRcdFx0Ly8gQWRkIGEgdG9vbHRpcCBmb3IgdGhpcyBzZXF1ZW5jZSBiYXNlLlxuXHRcdFx0c2VsZi5fYWRkVG9vbFRpcC5jYWxsKCBzZWxmLCB0aGlzLCBmdW5jdGlvbiggKSB7XG5cdFx0XHRcdGlmIChpc01vdXNlRG93bikge1xuXHQgICAgIFx0XHRcdHJldHVybiBcIltcIiArIHNlbGYub3B0LnNlbGVjdGlvbi5zdGFydCArXCIsIFwiICsgc2VsZi5vcHQuc2VsZWN0aW9uLmVuZCArIFwiXVwiO1xuXHQgICAgIFx0XHR9IGVsc2Uge1xuXHQgICAgIFx0XHRcdHJldHVybiBjdXJyZW50UG9zO1xuXHQgICAgIFx0XHR9XG5cdFx0XHR9KTtcblx0XHRcdFxuXHRcdH0pXG5cdFx0LmNzcygnY3Vyc29yJywgJ3BvaW50ZXInKTtcblx0fSxcblx0LyogXG4gICAgICogRnVuY3Rpb246IFNlcXVlbmNlLl9hZGRUb29sdGlwXG4gICAgICogUHVycG9zZTogIEFkZCBhIHRvb2x0aXAgYXJvdW5kIHRoZSB0YXJnZXQgRE9NIGVsZW1lbnQgcHJvdmlkZWQgYXMgYXJndW1lbnRcbiAgICAgKiBSZXR1cm5zOiAgLVxuICAgICAqIElucHV0czogICB0YXJnZXQgLT4ge0VsZW1lbnR9IERPTSBlbGVtZW50IHdpY2ggaXMgdGhlIHRhcmdldGVkIGZvY3VzIGZvciB0aGUgdG9vbHRpcC5cbiAgICAgKiBcdFx0XHQgY2JHZXRNZXNzYWdlRnVuY3Rpb24gLT4ge2Z1bmN0aW9ufSBBIGNhbGxiYWNrIGZ1bmN0aW9uIHdpY2ggcmV0dXJucyB0aGUgbWVzc2FnZSB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHRpcC5cbiAgICAgKi9cblx0X2FkZFRvb2xUaXAgOiBmdW5jdGlvbiAoIHRhcmdldCwgY2JHZXRNZXNzYWdlRnVuY3Rpb24gKSB7XG5cdFx0XG4gXHRcdHZhciB0aXBJZCA9IHRoaXMub3B0Ll90b29sdGlwO1xuXHRcdFxuXHRcdGpRdWVyeSh0YXJnZXQpLm1vdXNlb3ZlcihmdW5jdGlvbihlKSB7XG5cdFx0XHRcblx0IFx0XHR2YXIgb2Zmc2V0ID0galF1ZXJ5KGUudGFyZ2V0KS5vZmZzZXQoKTtcblxuXHRcdFx0aWYgKCAhIGpRdWVyeSggdGlwSWQgKS5pcygnOnZpc2libGUnKSApIHtcblx0XHQgICAgICAgIGpRdWVyeSggdGlwSWQgKSBcblx0XHQgICAgICAgIFx0LmNzcyh7XG5cdFx0ICAgICAgICBcdFx0J2JhY2tncm91bmQtY29sb3InOiBcIiMwMDBcIixcblx0XHQgICAgICAgIFx0XHQncGFkZGluZyc6IFwiM3B4IDEwcHggM3B4IDEwcHhcIixcblx0XHQgICAgICAgIFx0XHQndG9wJzogb2Zmc2V0LnRvcCArIGpRdWVyeShlLnRhcmdldCkuaGVpZ2h0KCkgKyBcInB4XCIsXG5cdFx0ICAgICAgICBcdFx0J2xlZnQnOiBvZmZzZXQubGVmdCArIGpRdWVyeShlLnRhcmdldCkud2lkdGgoKSArIFwicHhcIlxuXHRcdCAgICAgICAgXHR9KVxuXHRcdFx0ICAgICAgICAuYW5pbWF0ZSgge29wYWNpdHk6ICcwLjg1J30sIDEwKVxuXHRcdFx0ICAgICAgICAuaHRtbCggY2JHZXRNZXNzYWdlRnVuY3Rpb24uY2FsbCggdGFyZ2V0ICkgKVxuXHRcdFx0ICAgICAgICAuc2hvdygpO1xuXHRcdFx0fVxuXG5cdCAgICB9KS5tb3VzZW91dChmdW5jdGlvbigpIHtcblx0ICAgICAgICAvL1JlbW92ZSB0aGUgYXBwZW5kZWQgdG9vbHRpcCB0ZW1wbGF0ZVxuXHQgICAgICAgIGpRdWVyeSggdGlwSWQgKS5oaWRlKCk7XHQgICAgICAgICBcblx0ICAgIH0pO1xuXHR9LFxuXHRcbiAgIC8qKlxuICAgICogQW5ub3RhdGUgYSBzZXQgb2YgaW50ZXJ2YWxzIHByb3ZpZGVkIGluIHRoZSBhcmd1bWVudC5cblx0KiBcblx0KiBAZGVwcmVjYXRlZCBVc2UgYWRkQW5ub3RhdGlvbigpIGluc3RlYWQuXG4gICAgKiBcbiAgICAqIEBwYXJhbSB7T2JqZWN0fSBhbm5vdGF0aW9uIFRoZSBpbnRlcnZhbHMgYmVsb25naW5nIHRvIHRoZSBzYW1lIGFubm90YXRpb24uIFxuICAgICogU3ludGF4OiB7IG5hbWU6ICZsdDt2YWx1ZSZndDssIGNvbG9yOiAmbHQ7SFRNTENvbG9yQ29kZSZndDssIGh0bWw6ICZsdDtIVE1MU3RyaW5nJmd0OywgcmVnaW9uczogW3sgc3RhcnQ6ICZsdDtzdGFydFZhbDEmZ3Q7LCBlbmQ6ICZsdDtlbmRWYWwxJmd0O30sIC4uLiwgIHsgc3RhcnQ6ICZsdDtzdGFydFZhbE4mZ3Q7LCBlbmQ6ICZsdDtlbmRWYWxOJmd0O31dIH1cbiAgICAqL1xuXHRzZXRBbm5vdGF0aW9uOiBmdW5jdGlvbiAoIGFubm90YXRpb24gKSB7XG5cdFx0dGhpcy5hZGRBbm5vdGF0aW9uKGFubm90YXRpb24pO1xuXHR9LFxuXHRcblx0LyoqXG4gICAgKiBBbm5vdGF0ZSBhIHNldCBvZiBpbnRlcnZhbHMgcHJvdmlkZWQgaW4gdGhlIGFyZ3VtZW50LlxuICAgICogXG4gICAgKiBAZXhhbXBsZVxuICAgICogLy8gQW5ub3RhdGlvbnMgdXNpbmcgcmVnaW9ucyB3aXRoIGRpZmZlcmVudCBjb2xvcnMuXG4gICAgKiBteVNlcXVlbmNlLmFkZEFubm90YXRpb24oe1xuXHQqICAgIG5hbWU6XCJVTklQUk9UXCIsIFxuXHQqICAgIGh0bWw6XCImbHQ7YnImZ3Q7IEV4YW1wbGUgb2YgJmx0O2ImZ3Q7SFRNTCZsdDsvYiZndDtcIiwgXG5cdCogICAgY29sb3I6XCJncmVlblwiLCBcblx0KiAgICByZWdpb25zOiBbXG5cdCogICAgICAge3N0YXJ0OiA1NDAsIGVuZDogNTYwfSxcblx0KiAgICAgICB7c3RhcnQ6IDU2MSwgZW5kOjU4MCwgY29sb3I6IFwiI0ZGQTAxMFwifSwgXG5cdCogICAgICAge3N0YXJ0OiA1ODEsIGVuZDo1OTAsIGNvbG9yOiBcInJlZFwifSwgXG5cdCogICAgICAge3N0YXJ0OiA2OTAsIGVuZDo3MTB9XVxuXHQqIH0pO1xuXHQqIFxuICAgICogXG4gICAgKiBAcGFyYW0ge09iamVjdH0gYW5ub3RhdGlvbiBUaGUgaW50ZXJ2YWxzIGJlbG9uZ2luZyB0byB0aGUgc2FtZSBhbm5vdGF0aW9uLiBcbiAgICAqIFN5bnRheDogeyBuYW1lOiAmbHQ7dmFsdWUmZ3Q7LCBjb2xvcjogJmx0O0hUTUxDb2xvckNvZGUmZ3Q7LCBodG1sOiAmbHQ7SFRNTFN0cmluZyZndDssIHJlZ2lvbnM6IFt7IHN0YXJ0OiAmbHQ7c3RhcnRWYWwxJmd0OywgZW5kOiAmbHQ7ZW5kVmFsMSZndDt9LCAuLi4sICB7IHN0YXJ0OiAmbHQ7c3RhcnRWYWxOJmd0OywgZW5kOiAmbHQ7ZW5kVmFsTiZndDt9XSB9XG4gICAgKi9cblx0YWRkQW5ub3RhdGlvbjogZnVuY3Rpb24gKCBhbm5vdGF0aW9uICkge1xuXHRcdHRoaXMuX2Fubm90YXRpb25zLnB1c2goYW5ub3RhdGlvbik7XG5cdFx0dGhpcy5fcmVkcmF3KCk7XG5cdH0sXG5cdFxuXHQvKipcbiAgICAqIFJlbW92ZXMgYW4gYW5ub3RhdGlvbiBieSBtZWFucyBvZiBpdHMgbmFtZS5cbiAgICAqIFxuICAgICogQGV4YW1wbGUgXG4gICAgKiAvLyBSZW1vdmUgdGhlIFVOSVBST1QgYW5ub3RhdGlvbi5cbiAgICAqIG15U2VxdWVuY2UucmVtb3ZlQW5ub3RhdGlvbignVU5JUFJPVCcpOyBcbiAgICAqIFxuICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgVGhlIG5hbWUgb2YgdGhlIGFubm90YXRpb24gdG8gYmUgcmVtb3ZlZC5cbiAgICAqIFxuICAgICovXG5cdHJlbW92ZUFubm90YXRpb246IGZ1bmN0aW9uICggbmFtZSApIHtcblx0XHRmb3IgKHZhciBpPTA7IGkgPCB0aGlzLl9hbm5vdGF0aW9ucy5sZW5ndGggOyBpKysgKXtcblx0XHRcdGlmKG5hbWUgIT0gdGhpcy5fYW5ub3RhdGlvbnNbaV0ubmFtZSl7XG5cdFx0XHRcdHRoaXMuX2Fubm90YXRpb25zLnNwbGljZShpLDEpO1xuXHRcdFx0XHR0aGlzLl9yZWRyYXcoKTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXHQvKipcbiAgICAqIFJlbW92ZXMgYWxsIHRoZSBjdXJyZW50IGFubm90YXRpb25zLlxuICAgICogXG4gICAgKiBAZXhhbXBsZSBcbiAgICAqIG15U2VxdWVuY2UucmVtb3ZlQWxsQW5ub3RhdGlvbnMoKTsgXG4gICAgKiBcbiAgICAqL1xuXHRyZW1vdmVBbGxBbm5vdGF0aW9uczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2Fubm90YXRpb25zID0gW107XG5cdFx0dGhpcy5fcmVkcmF3KCk7XG5cdH0sXG5cblx0XG59KTtcblxucmVxdWlyZShcImJpb2pzLWV2ZW50c1wiKS5taXhpbihTZXF1ZW5jZS5wcm90b3R5cGUpO1xubW9kdWxlLmV4cG9ydHMgPSBTZXF1ZW5jZTtcbiIsInZhciBldmVudHMgPSByZXF1aXJlKFwiYmFja2JvbmUtZXZlbnRzLXN0YW5kYWxvbmVcIik7XG5cbmV2ZW50cy5vbkFsbCA9IGZ1bmN0aW9uKGNhbGxiYWNrLGNvbnRleHQpe1xuICB0aGlzLm9uKFwiYWxsXCIsIGNhbGxiYWNrLGNvbnRleHQpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8vIE1peGluIHV0aWxpdHlcbmV2ZW50cy5vbGRNaXhpbiA9IGV2ZW50cy5taXhpbjtcbmV2ZW50cy5taXhpbiA9IGZ1bmN0aW9uKHByb3RvKSB7XG4gIGV2ZW50cy5vbGRNaXhpbihwcm90byk7XG4gIC8vIGFkZCBjdXN0b20gb25BbGxcbiAgdmFyIGV4cG9ydHMgPSBbJ29uQWxsJ107XG4gIGZvcih2YXIgaT0wOyBpIDwgZXhwb3J0cy5sZW5ndGg7aSsrKXtcbiAgICB2YXIgbmFtZSA9IGV4cG9ydHNbaV07XG4gICAgcHJvdG9bbmFtZV0gPSB0aGlzW25hbWVdO1xuICB9XG4gIHJldHVybiBwcm90bztcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZXZlbnRzO1xuIiwiLyoqXG4gKiBTdGFuZGFsb25lIGV4dHJhY3Rpb24gb2YgQmFja2JvbmUuRXZlbnRzLCBubyBleHRlcm5hbCBkZXBlbmRlbmN5IHJlcXVpcmVkLlxuICogRGVncmFkZXMgbmljZWx5IHdoZW4gQmFja29uZS91bmRlcnNjb3JlIGFyZSBhbHJlYWR5IGF2YWlsYWJsZSBpbiB0aGUgY3VycmVudFxuICogZ2xvYmFsIGNvbnRleHQuXG4gKlxuICogTm90ZSB0aGF0IGRvY3Mgc3VnZ2VzdCB0byB1c2UgdW5kZXJzY29yZSdzIGBfLmV4dGVuZCgpYCBtZXRob2QgdG8gYWRkIEV2ZW50c1xuICogc3VwcG9ydCB0byBzb21lIGdpdmVuIG9iamVjdC4gQSBgbWl4aW4oKWAgbWV0aG9kIGhhcyBiZWVuIGFkZGVkIHRvIHRoZSBFdmVudHNcbiAqIHByb3RvdHlwZSB0byBhdm9pZCB1c2luZyB1bmRlcnNjb3JlIGZvciB0aGF0IHNvbGUgcHVycG9zZTpcbiAqXG4gKiAgICAgdmFyIG15RXZlbnRFbWl0dGVyID0gQmFja2JvbmVFdmVudHMubWl4aW4oe30pO1xuICpcbiAqIE9yIGZvciBhIGZ1bmN0aW9uIGNvbnN0cnVjdG9yOlxuICpcbiAqICAgICBmdW5jdGlvbiBNeUNvbnN0cnVjdG9yKCl7fVxuICogICAgIE15Q29uc3RydWN0b3IucHJvdG90eXBlLmZvbyA9IGZ1bmN0aW9uKCl7fVxuICogICAgIEJhY2tib25lRXZlbnRzLm1peGluKE15Q29uc3RydWN0b3IucHJvdG90eXBlKTtcbiAqXG4gKiAoYykgMjAwOS0yMDEzIEplcmVteSBBc2hrZW5hcywgRG9jdW1lbnRDbG91ZCBJbmMuXG4gKiAoYykgMjAxMyBOaWNvbGFzIFBlcnJpYXVsdFxuICovXG4vKiBnbG9iYWwgZXhwb3J0czp0cnVlLCBkZWZpbmUsIG1vZHVsZSAqL1xuKGZ1bmN0aW9uKCkge1xuICB2YXIgcm9vdCA9IHRoaXMsXG4gICAgICBicmVha2VyID0ge30sXG4gICAgICBuYXRpdmVGb3JFYWNoID0gQXJyYXkucHJvdG90eXBlLmZvckVhY2gsXG4gICAgICBoYXNPd25Qcm9wZXJ0eSA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHksXG4gICAgICBzbGljZSA9IEFycmF5LnByb3RvdHlwZS5zbGljZSxcbiAgICAgIGlkQ291bnRlciA9IDA7XG5cbiAgLy8gUmV0dXJucyBhIHBhcnRpYWwgaW1wbGVtZW50YXRpb24gbWF0Y2hpbmcgdGhlIG1pbmltYWwgQVBJIHN1YnNldCByZXF1aXJlZFxuICAvLyBieSBCYWNrYm9uZS5FdmVudHNcbiAgZnVuY3Rpb24gbWluaXNjb3JlKCkge1xuICAgIHJldHVybiB7XG4gICAgICBrZXlzOiBPYmplY3Qua2V5cyB8fCBmdW5jdGlvbiAob2JqKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygb2JqICE9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBvYmogIT09IFwiZnVuY3Rpb25cIiB8fCBvYmogPT09IG51bGwpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwia2V5cygpIGNhbGxlZCBvbiBhIG5vbi1vYmplY3RcIik7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGtleSwga2V5cyA9IFtdO1xuICAgICAgICBmb3IgKGtleSBpbiBvYmopIHtcbiAgICAgICAgICBpZiAob2JqLmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgICAgIGtleXNba2V5cy5sZW5ndGhdID0ga2V5O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4ga2V5cztcbiAgICAgIH0sXG5cbiAgICAgIHVuaXF1ZUlkOiBmdW5jdGlvbihwcmVmaXgpIHtcbiAgICAgICAgdmFyIGlkID0gKytpZENvdW50ZXIgKyAnJztcbiAgICAgICAgcmV0dXJuIHByZWZpeCA/IHByZWZpeCArIGlkIDogaWQ7XG4gICAgICB9LFxuXG4gICAgICBoYXM6IGZ1bmN0aW9uKG9iaiwga2V5KSB7XG4gICAgICAgIHJldHVybiBoYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KTtcbiAgICAgIH0sXG5cbiAgICAgIGVhY2g6IGZ1bmN0aW9uKG9iaiwgaXRlcmF0b3IsIGNvbnRleHQpIHtcbiAgICAgICAgaWYgKG9iaiA9PSBudWxsKSByZXR1cm47XG4gICAgICAgIGlmIChuYXRpdmVGb3JFYWNoICYmIG9iai5mb3JFYWNoID09PSBuYXRpdmVGb3JFYWNoKSB7XG4gICAgICAgICAgb2JqLmZvckVhY2goaXRlcmF0b3IsIGNvbnRleHQpO1xuICAgICAgICB9IGVsc2UgaWYgKG9iai5sZW5ndGggPT09ICtvYmoubGVuZ3RoKSB7XG4gICAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBvYmoubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoaXRlcmF0b3IuY2FsbChjb250ZXh0LCBvYmpbaV0sIGksIG9iaikgPT09IGJyZWFrZXIpIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZm9yICh2YXIga2V5IGluIG9iaikge1xuICAgICAgICAgICAgaWYgKHRoaXMuaGFzKG9iaiwga2V5KSkge1xuICAgICAgICAgICAgICBpZiAoaXRlcmF0b3IuY2FsbChjb250ZXh0LCBvYmpba2V5XSwga2V5LCBvYmopID09PSBicmVha2VyKSByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICBvbmNlOiBmdW5jdGlvbihmdW5jKSB7XG4gICAgICAgIHZhciByYW4gPSBmYWxzZSwgbWVtbztcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGlmIChyYW4pIHJldHVybiBtZW1vO1xuICAgICAgICAgIHJhbiA9IHRydWU7XG4gICAgICAgICAgbWVtbyA9IGZ1bmMuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICBmdW5jID0gbnVsbDtcbiAgICAgICAgICByZXR1cm4gbWVtbztcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgdmFyIF8gPSBtaW5pc2NvcmUoKSwgRXZlbnRzO1xuXG4gIC8vIEJhY2tib25lLkV2ZW50c1xuICAvLyAtLS0tLS0tLS0tLS0tLS1cblxuICAvLyBBIG1vZHVsZSB0aGF0IGNhbiBiZSBtaXhlZCBpbiB0byAqYW55IG9iamVjdCogaW4gb3JkZXIgdG8gcHJvdmlkZSBpdCB3aXRoXG4gIC8vIGN1c3RvbSBldmVudHMuIFlvdSBtYXkgYmluZCB3aXRoIGBvbmAgb3IgcmVtb3ZlIHdpdGggYG9mZmAgY2FsbGJhY2tcbiAgLy8gZnVuY3Rpb25zIHRvIGFuIGV2ZW50OyBgdHJpZ2dlcmAtaW5nIGFuIGV2ZW50IGZpcmVzIGFsbCBjYWxsYmFja3MgaW5cbiAgLy8gc3VjY2Vzc2lvbi5cbiAgLy9cbiAgLy8gICAgIHZhciBvYmplY3QgPSB7fTtcbiAgLy8gICAgIF8uZXh0ZW5kKG9iamVjdCwgQmFja2JvbmUuRXZlbnRzKTtcbiAgLy8gICAgIG9iamVjdC5vbignZXhwYW5kJywgZnVuY3Rpb24oKXsgYWxlcnQoJ2V4cGFuZGVkJyk7IH0pO1xuICAvLyAgICAgb2JqZWN0LnRyaWdnZXIoJ2V4cGFuZCcpO1xuICAvL1xuICBFdmVudHMgPSB7XG5cbiAgICAvLyBCaW5kIGFuIGV2ZW50IHRvIGEgYGNhbGxiYWNrYCBmdW5jdGlvbi4gUGFzc2luZyBgXCJhbGxcImAgd2lsbCBiaW5kXG4gICAgLy8gdGhlIGNhbGxiYWNrIHRvIGFsbCBldmVudHMgZmlyZWQuXG4gICAgb246IGZ1bmN0aW9uKG5hbWUsIGNhbGxiYWNrLCBjb250ZXh0KSB7XG4gICAgICBpZiAoIWV2ZW50c0FwaSh0aGlzLCAnb24nLCBuYW1lLCBbY2FsbGJhY2ssIGNvbnRleHRdKSB8fCAhY2FsbGJhY2spIHJldHVybiB0aGlzO1xuICAgICAgdGhpcy5fZXZlbnRzIHx8ICh0aGlzLl9ldmVudHMgPSB7fSk7XG4gICAgICB2YXIgZXZlbnRzID0gdGhpcy5fZXZlbnRzW25hbWVdIHx8ICh0aGlzLl9ldmVudHNbbmFtZV0gPSBbXSk7XG4gICAgICBldmVudHMucHVzaCh7Y2FsbGJhY2s6IGNhbGxiYWNrLCBjb250ZXh0OiBjb250ZXh0LCBjdHg6IGNvbnRleHQgfHwgdGhpc30pO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8vIEJpbmQgYW4gZXZlbnQgdG8gb25seSBiZSB0cmlnZ2VyZWQgYSBzaW5nbGUgdGltZS4gQWZ0ZXIgdGhlIGZpcnN0IHRpbWVcbiAgICAvLyB0aGUgY2FsbGJhY2sgaXMgaW52b2tlZCwgaXQgd2lsbCBiZSByZW1vdmVkLlxuICAgIG9uY2U6IGZ1bmN0aW9uKG5hbWUsIGNhbGxiYWNrLCBjb250ZXh0KSB7XG4gICAgICBpZiAoIWV2ZW50c0FwaSh0aGlzLCAnb25jZScsIG5hbWUsIFtjYWxsYmFjaywgY29udGV4dF0pIHx8ICFjYWxsYmFjaykgcmV0dXJuIHRoaXM7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICB2YXIgb25jZSA9IF8ub25jZShmdW5jdGlvbigpIHtcbiAgICAgICAgc2VsZi5vZmYobmFtZSwgb25jZSk7XG4gICAgICAgIGNhbGxiYWNrLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICB9KTtcbiAgICAgIG9uY2UuX2NhbGxiYWNrID0gY2FsbGJhY2s7XG4gICAgICByZXR1cm4gdGhpcy5vbihuYW1lLCBvbmNlLCBjb250ZXh0KTtcbiAgICB9LFxuXG4gICAgLy8gUmVtb3ZlIG9uZSBvciBtYW55IGNhbGxiYWNrcy4gSWYgYGNvbnRleHRgIGlzIG51bGwsIHJlbW92ZXMgYWxsXG4gICAgLy8gY2FsbGJhY2tzIHdpdGggdGhhdCBmdW5jdGlvbi4gSWYgYGNhbGxiYWNrYCBpcyBudWxsLCByZW1vdmVzIGFsbFxuICAgIC8vIGNhbGxiYWNrcyBmb3IgdGhlIGV2ZW50LiBJZiBgbmFtZWAgaXMgbnVsbCwgcmVtb3ZlcyBhbGwgYm91bmRcbiAgICAvLyBjYWxsYmFja3MgZm9yIGFsbCBldmVudHMuXG4gICAgb2ZmOiBmdW5jdGlvbihuYW1lLCBjYWxsYmFjaywgY29udGV4dCkge1xuICAgICAgdmFyIHJldGFpbiwgZXYsIGV2ZW50cywgbmFtZXMsIGksIGwsIGosIGs7XG4gICAgICBpZiAoIXRoaXMuX2V2ZW50cyB8fCAhZXZlbnRzQXBpKHRoaXMsICdvZmYnLCBuYW1lLCBbY2FsbGJhY2ssIGNvbnRleHRdKSkgcmV0dXJuIHRoaXM7XG4gICAgICBpZiAoIW5hbWUgJiYgIWNhbGxiYWNrICYmICFjb250ZXh0KSB7XG4gICAgICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgIH1cblxuICAgICAgbmFtZXMgPSBuYW1lID8gW25hbWVdIDogXy5rZXlzKHRoaXMuX2V2ZW50cyk7XG4gICAgICBmb3IgKGkgPSAwLCBsID0gbmFtZXMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgIG5hbWUgPSBuYW1lc1tpXTtcbiAgICAgICAgaWYgKGV2ZW50cyA9IHRoaXMuX2V2ZW50c1tuYW1lXSkge1xuICAgICAgICAgIHRoaXMuX2V2ZW50c1tuYW1lXSA9IHJldGFpbiA9IFtdO1xuICAgICAgICAgIGlmIChjYWxsYmFjayB8fCBjb250ZXh0KSB7XG4gICAgICAgICAgICBmb3IgKGogPSAwLCBrID0gZXZlbnRzLmxlbmd0aDsgaiA8IGs7IGorKykge1xuICAgICAgICAgICAgICBldiA9IGV2ZW50c1tqXTtcbiAgICAgICAgICAgICAgaWYgKChjYWxsYmFjayAmJiBjYWxsYmFjayAhPT0gZXYuY2FsbGJhY2sgJiYgY2FsbGJhY2sgIT09IGV2LmNhbGxiYWNrLl9jYWxsYmFjaykgfHxcbiAgICAgICAgICAgICAgICAgIChjb250ZXh0ICYmIGNvbnRleHQgIT09IGV2LmNvbnRleHQpKSB7XG4gICAgICAgICAgICAgICAgcmV0YWluLnB1c2goZXYpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghcmV0YWluLmxlbmd0aCkgZGVsZXRlIHRoaXMuX2V2ZW50c1tuYW1lXTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgLy8gVHJpZ2dlciBvbmUgb3IgbWFueSBldmVudHMsIGZpcmluZyBhbGwgYm91bmQgY2FsbGJhY2tzLiBDYWxsYmFja3MgYXJlXG4gICAgLy8gcGFzc2VkIHRoZSBzYW1lIGFyZ3VtZW50cyBhcyBgdHJpZ2dlcmAgaXMsIGFwYXJ0IGZyb20gdGhlIGV2ZW50IG5hbWVcbiAgICAvLyAodW5sZXNzIHlvdSdyZSBsaXN0ZW5pbmcgb24gYFwiYWxsXCJgLCB3aGljaCB3aWxsIGNhdXNlIHlvdXIgY2FsbGJhY2sgdG9cbiAgICAvLyByZWNlaXZlIHRoZSB0cnVlIG5hbWUgb2YgdGhlIGV2ZW50IGFzIHRoZSBmaXJzdCBhcmd1bWVudCkuXG4gICAgdHJpZ2dlcjogZnVuY3Rpb24obmFtZSkge1xuICAgICAgaWYgKCF0aGlzLl9ldmVudHMpIHJldHVybiB0aGlzO1xuICAgICAgdmFyIGFyZ3MgPSBzbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XG4gICAgICBpZiAoIWV2ZW50c0FwaSh0aGlzLCAndHJpZ2dlcicsIG5hbWUsIGFyZ3MpKSByZXR1cm4gdGhpcztcbiAgICAgIHZhciBldmVudHMgPSB0aGlzLl9ldmVudHNbbmFtZV07XG4gICAgICB2YXIgYWxsRXZlbnRzID0gdGhpcy5fZXZlbnRzLmFsbDtcbiAgICAgIGlmIChldmVudHMpIHRyaWdnZXJFdmVudHMoZXZlbnRzLCBhcmdzKTtcbiAgICAgIGlmIChhbGxFdmVudHMpIHRyaWdnZXJFdmVudHMoYWxsRXZlbnRzLCBhcmd1bWVudHMpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8vIFRlbGwgdGhpcyBvYmplY3QgdG8gc3RvcCBsaXN0ZW5pbmcgdG8gZWl0aGVyIHNwZWNpZmljIGV2ZW50cyAuLi4gb3JcbiAgICAvLyB0byBldmVyeSBvYmplY3QgaXQncyBjdXJyZW50bHkgbGlzdGVuaW5nIHRvLlxuICAgIHN0b3BMaXN0ZW5pbmc6IGZ1bmN0aW9uKG9iaiwgbmFtZSwgY2FsbGJhY2spIHtcbiAgICAgIHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9saXN0ZW5lcnM7XG4gICAgICBpZiAoIWxpc3RlbmVycykgcmV0dXJuIHRoaXM7XG4gICAgICB2YXIgZGVsZXRlTGlzdGVuZXIgPSAhbmFtZSAmJiAhY2FsbGJhY2s7XG4gICAgICBpZiAodHlwZW9mIG5hbWUgPT09ICdvYmplY3QnKSBjYWxsYmFjayA9IHRoaXM7XG4gICAgICBpZiAob2JqKSAobGlzdGVuZXJzID0ge30pW29iai5fbGlzdGVuZXJJZF0gPSBvYmo7XG4gICAgICBmb3IgKHZhciBpZCBpbiBsaXN0ZW5lcnMpIHtcbiAgICAgICAgbGlzdGVuZXJzW2lkXS5vZmYobmFtZSwgY2FsbGJhY2ssIHRoaXMpO1xuICAgICAgICBpZiAoZGVsZXRlTGlzdGVuZXIpIGRlbGV0ZSB0aGlzLl9saXN0ZW5lcnNbaWRdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gIH07XG5cbiAgLy8gUmVndWxhciBleHByZXNzaW9uIHVzZWQgdG8gc3BsaXQgZXZlbnQgc3RyaW5ncy5cbiAgdmFyIGV2ZW50U3BsaXR0ZXIgPSAvXFxzKy87XG5cbiAgLy8gSW1wbGVtZW50IGZhbmN5IGZlYXR1cmVzIG9mIHRoZSBFdmVudHMgQVBJIHN1Y2ggYXMgbXVsdGlwbGUgZXZlbnRcbiAgLy8gbmFtZXMgYFwiY2hhbmdlIGJsdXJcImAgYW5kIGpRdWVyeS1zdHlsZSBldmVudCBtYXBzIGB7Y2hhbmdlOiBhY3Rpb259YFxuICAvLyBpbiB0ZXJtcyBvZiB0aGUgZXhpc3RpbmcgQVBJLlxuICB2YXIgZXZlbnRzQXBpID0gZnVuY3Rpb24ob2JqLCBhY3Rpb24sIG5hbWUsIHJlc3QpIHtcbiAgICBpZiAoIW5hbWUpIHJldHVybiB0cnVlO1xuXG4gICAgLy8gSGFuZGxlIGV2ZW50IG1hcHMuXG4gICAgaWYgKHR5cGVvZiBuYW1lID09PSAnb2JqZWN0Jykge1xuICAgICAgZm9yICh2YXIga2V5IGluIG5hbWUpIHtcbiAgICAgICAgb2JqW2FjdGlvbl0uYXBwbHkob2JqLCBba2V5LCBuYW1lW2tleV1dLmNvbmNhdChyZXN0KSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIHNwYWNlIHNlcGFyYXRlZCBldmVudCBuYW1lcy5cbiAgICBpZiAoZXZlbnRTcGxpdHRlci50ZXN0KG5hbWUpKSB7XG4gICAgICB2YXIgbmFtZXMgPSBuYW1lLnNwbGl0KGV2ZW50U3BsaXR0ZXIpO1xuICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBuYW1lcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgb2JqW2FjdGlvbl0uYXBwbHkob2JqLCBbbmFtZXNbaV1dLmNvbmNhdChyZXN0KSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH07XG5cbiAgLy8gQSBkaWZmaWN1bHQtdG8tYmVsaWV2ZSwgYnV0IG9wdGltaXplZCBpbnRlcm5hbCBkaXNwYXRjaCBmdW5jdGlvbiBmb3JcbiAgLy8gdHJpZ2dlcmluZyBldmVudHMuIFRyaWVzIHRvIGtlZXAgdGhlIHVzdWFsIGNhc2VzIHNwZWVkeSAobW9zdCBpbnRlcm5hbFxuICAvLyBCYWNrYm9uZSBldmVudHMgaGF2ZSAzIGFyZ3VtZW50cykuXG4gIHZhciB0cmlnZ2VyRXZlbnRzID0gZnVuY3Rpb24oZXZlbnRzLCBhcmdzKSB7XG4gICAgdmFyIGV2LCBpID0gLTEsIGwgPSBldmVudHMubGVuZ3RoLCBhMSA9IGFyZ3NbMF0sIGEyID0gYXJnc1sxXSwgYTMgPSBhcmdzWzJdO1xuICAgIHN3aXRjaCAoYXJncy5sZW5ndGgpIHtcbiAgICAgIGNhc2UgMDogd2hpbGUgKCsraSA8IGwpIChldiA9IGV2ZW50c1tpXSkuY2FsbGJhY2suY2FsbChldi5jdHgpOyByZXR1cm47XG4gICAgICBjYXNlIDE6IHdoaWxlICgrK2kgPCBsKSAoZXYgPSBldmVudHNbaV0pLmNhbGxiYWNrLmNhbGwoZXYuY3R4LCBhMSk7IHJldHVybjtcbiAgICAgIGNhc2UgMjogd2hpbGUgKCsraSA8IGwpIChldiA9IGV2ZW50c1tpXSkuY2FsbGJhY2suY2FsbChldi5jdHgsIGExLCBhMik7IHJldHVybjtcbiAgICAgIGNhc2UgMzogd2hpbGUgKCsraSA8IGwpIChldiA9IGV2ZW50c1tpXSkuY2FsbGJhY2suY2FsbChldi5jdHgsIGExLCBhMiwgYTMpOyByZXR1cm47XG4gICAgICBkZWZhdWx0OiB3aGlsZSAoKytpIDwgbCkgKGV2ID0gZXZlbnRzW2ldKS5jYWxsYmFjay5hcHBseShldi5jdHgsIGFyZ3MpO1xuICAgIH1cbiAgfTtcblxuICB2YXIgbGlzdGVuTWV0aG9kcyA9IHtsaXN0ZW5UbzogJ29uJywgbGlzdGVuVG9PbmNlOiAnb25jZSd9O1xuXG4gIC8vIEludmVyc2lvbi1vZi1jb250cm9sIHZlcnNpb25zIG9mIGBvbmAgYW5kIGBvbmNlYC4gVGVsbCAqdGhpcyogb2JqZWN0IHRvXG4gIC8vIGxpc3RlbiB0byBhbiBldmVudCBpbiBhbm90aGVyIG9iamVjdCAuLi4ga2VlcGluZyB0cmFjayBvZiB3aGF0IGl0J3NcbiAgLy8gbGlzdGVuaW5nIHRvLlxuICBfLmVhY2gobGlzdGVuTWV0aG9kcywgZnVuY3Rpb24oaW1wbGVtZW50YXRpb24sIG1ldGhvZCkge1xuICAgIEV2ZW50c1ttZXRob2RdID0gZnVuY3Rpb24ob2JqLCBuYW1lLCBjYWxsYmFjaykge1xuICAgICAgdmFyIGxpc3RlbmVycyA9IHRoaXMuX2xpc3RlbmVycyB8fCAodGhpcy5fbGlzdGVuZXJzID0ge30pO1xuICAgICAgdmFyIGlkID0gb2JqLl9saXN0ZW5lcklkIHx8IChvYmouX2xpc3RlbmVySWQgPSBfLnVuaXF1ZUlkKCdsJykpO1xuICAgICAgbGlzdGVuZXJzW2lkXSA9IG9iajtcbiAgICAgIGlmICh0eXBlb2YgbmFtZSA9PT0gJ29iamVjdCcpIGNhbGxiYWNrID0gdGhpcztcbiAgICAgIG9ialtpbXBsZW1lbnRhdGlvbl0obmFtZSwgY2FsbGJhY2ssIHRoaXMpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfTtcbiAgfSk7XG5cbiAgLy8gQWxpYXNlcyBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkuXG4gIEV2ZW50cy5iaW5kICAgPSBFdmVudHMub247XG4gIEV2ZW50cy51bmJpbmQgPSBFdmVudHMub2ZmO1xuXG4gIC8vIE1peGluIHV0aWxpdHlcbiAgRXZlbnRzLm1peGluID0gZnVuY3Rpb24ocHJvdG8pIHtcbiAgICB2YXIgZXhwb3J0cyA9IFsnb24nLCAnb25jZScsICdvZmYnLCAndHJpZ2dlcicsICdzdG9wTGlzdGVuaW5nJywgJ2xpc3RlblRvJyxcbiAgICAgICAgICAgICAgICAgICAnbGlzdGVuVG9PbmNlJywgJ2JpbmQnLCAndW5iaW5kJ107XG4gICAgXy5lYWNoKGV4cG9ydHMsIGZ1bmN0aW9uKG5hbWUpIHtcbiAgICAgIHByb3RvW25hbWVdID0gdGhpc1tuYW1lXTtcbiAgICB9LCB0aGlzKTtcbiAgICByZXR1cm4gcHJvdG87XG4gIH07XG5cbiAgLy8gRXhwb3J0IEV2ZW50cyBhcyBCYWNrYm9uZUV2ZW50cyBkZXBlbmRpbmcgb24gY3VycmVudCBjb250ZXh0XG4gIGlmICh0eXBlb2YgZGVmaW5lID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICBkZWZpbmUoZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gRXZlbnRzO1xuICAgIH0pO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBleHBvcnRzICE9PSAndW5kZWZpbmVkJykge1xuICAgIGlmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJyAmJiBtb2R1bGUuZXhwb3J0cykge1xuICAgICAgZXhwb3J0cyA9IG1vZHVsZS5leHBvcnRzID0gRXZlbnRzO1xuICAgIH1cbiAgICBleHBvcnRzLkJhY2tib25lRXZlbnRzID0gRXZlbnRzO1xuICB9IGVsc2Uge1xuICAgIHJvb3QuQmFja2JvbmVFdmVudHMgPSBFdmVudHM7XG4gIH1cbn0pKHRoaXMpO1xuIiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL2JhY2tib25lLWV2ZW50cy1zdGFuZGFsb25lJyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuOC4wXG52YXIgR2VuZXJpY1JlYWRlciwgeGhyO1xuXG54aHIgPSByZXF1aXJlKCduZXRzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gR2VuZXJpY1JlYWRlciA9IChmdW5jdGlvbigpIHtcbiAgZnVuY3Rpb24gR2VuZXJpY1JlYWRlcigpIHt9XG5cbiAgR2VuZXJpY1JlYWRlci5yZWFkID0gZnVuY3Rpb24odXJsLCBjYWxsYmFjaykge1xuICAgIHZhciBvbnJldDtcbiAgICBvbnJldCA9IChmdW5jdGlvbihfdGhpcykge1xuICAgICAgcmV0dXJuIGZ1bmN0aW9uKGVyciwgcmVzcG9uc2UsIHRleHQpIHtcbiAgICAgICAgcmV0dXJuIF90aGlzLl9vblJldHJpZXZhbCh0ZXh0LCBjYWxsYmFjayk7XG4gICAgICB9O1xuICAgIH0pKHRoaXMpO1xuICAgIHJldHVybiB4aHIodXJsLCBvbnJldCk7XG4gIH07XG5cbiAgR2VuZXJpY1JlYWRlci5fb25SZXRyaWV2YWwgPSBmdW5jdGlvbih0ZXh0LCBjYWxsYmFjaykge1xuICAgIHZhciByVGV4dDtcbiAgICByVGV4dCA9IHRoaXMucGFyc2UodGV4dCk7XG4gICAgcmV0dXJuIGNhbGxiYWNrKHJUZXh0KTtcbiAgfTtcblxuICByZXR1cm4gR2VuZXJpY1JlYWRlcjtcblxufSkoKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS44LjBcbnZhciBGYXN0YSwgR2VuZXJpY1JlYWRlciwgU2VxLCBTdHIsXG4gIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuU3RyID0gcmVxdWlyZShcIi4vc3RyaW5nc1wiKTtcblxuR2VuZXJpY1JlYWRlciA9IHJlcXVpcmUoXCIuL2dlbmVyaWNfcmVhZGVyXCIpO1xuXG5TZXEgPSByZXF1aXJlKFwiYmlvanMtbW9kZWxcIikuc2VxO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEZhc3RhID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICBfX2V4dGVuZHMoRmFzdGEsIF9zdXBlcik7XG5cbiAgZnVuY3Rpb24gRmFzdGEoKSB7XG4gICAgcmV0dXJuIEZhc3RhLl9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICB9XG5cbiAgRmFzdGEucGFyc2UgPSBmdW5jdGlvbih0ZXh0KSB7XG4gICAgdmFyIGN1cnJlbnRTZXEsIGRhdGFiYXNlLCBkYXRhYmFzZUlELCBpZGVudGlmaWVycywgaywgbGFiZWwsIGxpbmUsIHNlcXMsIF9pLCBfbGVuO1xuICAgIHNlcXMgPSBbXTtcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHRleHQpICE9PSAnW29iamVjdCBBcnJheV0nKSB7XG4gICAgICB0ZXh0ID0gdGV4dC5zcGxpdChcIlxcblwiKTtcbiAgICB9XG4gICAgZm9yIChfaSA9IDAsIF9sZW4gPSB0ZXh0Lmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICBsaW5lID0gdGV4dFtfaV07XG4gICAgICBpZiAobGluZVswXSA9PT0gXCI+XCIgfHwgbGluZVswXSA9PT0gXCI7XCIpIHtcbiAgICAgICAgbGFiZWwgPSBsaW5lLnNsaWNlKDEpO1xuICAgICAgICBjdXJyZW50U2VxID0gbmV3IFNlcShcIlwiLCBsYWJlbCwgc2Vxcy5sZW5ndGgpO1xuICAgICAgICBzZXFzLnB1c2goY3VycmVudFNlcSk7XG4gICAgICAgIGlmIChTdHIuY29udGFpbnMoXCJ8XCIsIGxpbmUpKSB7XG4gICAgICAgICAgaWRlbnRpZmllcnMgPSBsYWJlbC5zcGxpdChcInxcIik7XG4gICAgICAgICAgayA9IDE7XG4gICAgICAgICAgd2hpbGUgKGsgPCBpZGVudGlmaWVycy5sZW5ndGgpIHtcbiAgICAgICAgICAgIGRhdGFiYXNlID0gaWRlbnRpZmllcnNba107XG4gICAgICAgICAgICBkYXRhYmFzZUlEID0gaWRlbnRpZmllcnNbayArIDFdO1xuICAgICAgICAgICAgY3VycmVudFNlcS5tZXRhW2RhdGFiYXNlXSA9IGRhdGFiYXNlSUQ7XG4gICAgICAgICAgICBrICs9IDI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGN1cnJlbnRTZXEubmFtZSA9IGlkZW50aWZpZXJzW2lkZW50aWZpZXJzLmxlbmd0aCAtIDFdO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjdXJyZW50U2VxLnNlcSArPSBsaW5lO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gc2VxcztcbiAgfTtcblxuICByZXR1cm4gRmFzdGE7XG5cbn0pKEdlbmVyaWNSZWFkZXIpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjguMFxudmFyIHN0cmluZ3M7XG5cbnN0cmluZ3MgPSB7XG4gIGNvbnRhaW5zOiBmdW5jdGlvbih0ZXh0LCBzZWFyY2gpIHtcbiAgICByZXR1cm4gJycuaW5kZXhPZi5jYWxsKHRleHQsIHNlYXJjaCwgMCkgIT09IC0xO1xuICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHN0cmluZ3M7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuOC4wXG52YXIgVXRpbHM7XG5cblV0aWxzID0ge307XG5cblV0aWxzLnNwbGl0TkNoYXJzID0gZnVuY3Rpb24odHh0LCBudW0pIHtcbiAgdmFyIGksIHJlc3VsdCwgX2ksIF9yZWY7XG4gIHJlc3VsdCA9IFtdO1xuICBmb3IgKGkgPSBfaSA9IDAsIF9yZWYgPSB0eHQubGVuZ3RoIC0gMTsgbnVtID4gMCA/IF9pIDw9IF9yZWYgOiBfaSA+PSBfcmVmOyBpID0gX2kgKz0gbnVtKSB7XG4gICAgcmVzdWx0LnB1c2godHh0LnN1YnN0cihpLCBudW0pKTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBVdGlscztcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS44LjBcbnZhciBGYXN0YUV4cG9ydGVyLCBVdGlscztcblxuVXRpbHMgPSByZXF1aXJlKFwiLi91dGlsc1wiKTtcblxubW9kdWxlLmV4cG9ydHMgPSBGYXN0YUV4cG9ydGVyID0gKGZ1bmN0aW9uKCkge1xuICBmdW5jdGlvbiBGYXN0YUV4cG9ydGVyKCkge31cblxuICBGYXN0YUV4cG9ydGVyW1wiZXhwb3J0XCJdID0gZnVuY3Rpb24oc2VxcywgYWNjZXNzKSB7XG4gICAgdmFyIHNlcSwgdGV4dCwgX2ksIF9sZW47XG4gICAgdGV4dCA9IFwiXCI7XG4gICAgZm9yIChfaSA9IDAsIF9sZW4gPSBzZXFzLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICBzZXEgPSBzZXFzW19pXTtcbiAgICAgIGlmIChhY2Nlc3MgIT0gbnVsbCkge1xuICAgICAgICBzZXEgPSBhY2Nlc3Moc2VxKTtcbiAgICAgIH1cbiAgICAgIHRleHQgKz0gXCI+XCIgKyBzZXEubmFtZSArIFwiXFxuXCI7XG4gICAgICB0ZXh0ICs9IChVdGlscy5zcGxpdE5DaGFycyhzZXEuc2VxLCA4MCkpLmpvaW4oXCJcXG5cIik7XG4gICAgICB0ZXh0ICs9IFwiXFxuXCI7XG4gICAgfVxuICAgIHJldHVybiB0ZXh0O1xuICB9O1xuXG4gIHJldHVybiBGYXN0YUV4cG9ydGVyO1xuXG59KSgpO1xuIiwibW9kdWxlLmV4cG9ydHMuc2VxID0gcmVxdWlyZShcIi4vc2VxXCIpO1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihzZXEsIG5hbWUsIGlkKSB7XG4gICAgdGhpcy5zZXEgPSBzZXE7XG4gICAgdGhpcy5uYW1lID0gbmFtZTtcbiAgICB0aGlzLmlkID0gaWQ7XG4gICAgdGhpcy5tZXRhID0ge307XG59O1xuIiwidmFyIHJlcSA9IHJlcXVpcmUoJ3JlcXVlc3QnKVxuXG5tb2R1bGUuZXhwb3J0cyA9IE5ldHNcblxuZnVuY3Rpb24gTmV0cyh1cmksIG9wdHMsIGNiKSB7XG4gIHJlcSh1cmksIG9wdHMsIGNiKVxufSIsInZhciB3aW5kb3cgPSByZXF1aXJlKFwiZ2xvYmFsL3dpbmRvd1wiKVxudmFyIG9uY2UgPSByZXF1aXJlKFwib25jZVwiKVxudmFyIHBhcnNlSGVhZGVycyA9IHJlcXVpcmUoJ3BhcnNlLWhlYWRlcnMnKVxuXG52YXIgbWVzc2FnZXMgPSB7XG4gICAgXCIwXCI6IFwiSW50ZXJuYWwgWE1MSHR0cFJlcXVlc3QgRXJyb3JcIixcbiAgICBcIjRcIjogXCI0eHggQ2xpZW50IEVycm9yXCIsXG4gICAgXCI1XCI6IFwiNXh4IFNlcnZlciBFcnJvclwiXG59XG5cbnZhciBYSFIgPSB3aW5kb3cuWE1MSHR0cFJlcXVlc3QgfHwgbm9vcFxudmFyIFhEUiA9IFwid2l0aENyZWRlbnRpYWxzXCIgaW4gKG5ldyBYSFIoKSkgPyBYSFIgOiB3aW5kb3cuWERvbWFpblJlcXVlc3RcblxubW9kdWxlLmV4cG9ydHMgPSBjcmVhdGVYSFJcblxuZnVuY3Rpb24gY3JlYXRlWEhSKG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIG9wdGlvbnMgPSB7IHVyaTogb3B0aW9ucyB9XG4gICAgfVxuXG4gICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge31cbiAgICBjYWxsYmFjayA9IG9uY2UoY2FsbGJhY2spXG5cbiAgICB2YXIgeGhyID0gb3B0aW9ucy54aHIgfHwgbnVsbFxuXG4gICAgaWYgKCF4aHIpIHtcbiAgICAgICAgaWYgKG9wdGlvbnMuY29ycyB8fCBvcHRpb25zLnVzZVhEUikge1xuICAgICAgICAgICAgeGhyID0gbmV3IFhEUigpXG4gICAgICAgIH1lbHNle1xuICAgICAgICAgICAgeGhyID0gbmV3IFhIUigpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgdXJpID0geGhyLnVybCA9IG9wdGlvbnMudXJpIHx8IG9wdGlvbnMudXJsXG4gICAgdmFyIG1ldGhvZCA9IHhoci5tZXRob2QgPSBvcHRpb25zLm1ldGhvZCB8fCBcIkdFVFwiXG4gICAgdmFyIGJvZHkgPSBvcHRpb25zLmJvZHkgfHwgb3B0aW9ucy5kYXRhXG4gICAgdmFyIGhlYWRlcnMgPSB4aHIuaGVhZGVycyA9IG9wdGlvbnMuaGVhZGVycyB8fCB7fVxuICAgIHZhciBzeW5jID0gISFvcHRpb25zLnN5bmNcbiAgICB2YXIgaXNKc29uID0gZmFsc2VcbiAgICB2YXIga2V5XG4gICAgdmFyIGxvYWQgPSBvcHRpb25zLnJlc3BvbnNlID8gbG9hZFJlc3BvbnNlIDogbG9hZFhoclxuXG4gICAgaWYgKFwianNvblwiIGluIG9wdGlvbnMpIHtcbiAgICAgICAgaXNKc29uID0gdHJ1ZVxuICAgICAgICBoZWFkZXJzW1wiQWNjZXB0XCJdID0gXCJhcHBsaWNhdGlvbi9qc29uXCJcbiAgICAgICAgaWYgKG1ldGhvZCAhPT0gXCJHRVRcIiAmJiBtZXRob2QgIT09IFwiSEVBRFwiKSB7XG4gICAgICAgICAgICBoZWFkZXJzW1wiQ29udGVudC1UeXBlXCJdID0gXCJhcHBsaWNhdGlvbi9qc29uXCJcbiAgICAgICAgICAgIGJvZHkgPSBKU09OLnN0cmluZ2lmeShvcHRpb25zLmpzb24pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gcmVhZHlzdGF0ZWNoYW5nZVxuICAgIHhoci5vbmxvYWQgPSBsb2FkXG4gICAgeGhyLm9uZXJyb3IgPSBlcnJvclxuICAgIC8vIElFOSBtdXN0IGhhdmUgb25wcm9ncmVzcyBiZSBzZXQgdG8gYSB1bmlxdWUgZnVuY3Rpb24uXG4gICAgeGhyLm9ucHJvZ3Jlc3MgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIElFIG11c3QgZGllXG4gICAgfVxuICAgIC8vIGhhdGUgSUVcbiAgICB4aHIub250aW1lb3V0ID0gbm9vcFxuICAgIHhoci5vcGVuKG1ldGhvZCwgdXJpLCAhc3luYylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vYmFja3dhcmQgY29tcGF0aWJpbGl0eVxuICAgIGlmIChvcHRpb25zLndpdGhDcmVkZW50aWFscyB8fCAob3B0aW9ucy5jb3JzICYmIG9wdGlvbnMud2l0aENyZWRlbnRpYWxzICE9PSBmYWxzZSkpIHtcbiAgICAgICAgeGhyLndpdGhDcmVkZW50aWFscyA9IHRydWVcbiAgICB9XG5cbiAgICAvLyBDYW5ub3Qgc2V0IHRpbWVvdXQgd2l0aCBzeW5jIHJlcXVlc3RcbiAgICBpZiAoIXN5bmMpIHtcbiAgICAgICAgeGhyLnRpbWVvdXQgPSBcInRpbWVvdXRcIiBpbiBvcHRpb25zID8gb3B0aW9ucy50aW1lb3V0IDogNTAwMFxuICAgIH1cblxuICAgIGlmICh4aHIuc2V0UmVxdWVzdEhlYWRlcikge1xuICAgICAgICBmb3Ioa2V5IGluIGhlYWRlcnMpe1xuICAgICAgICAgICAgaWYoaGVhZGVycy5oYXNPd25Qcm9wZXJ0eShrZXkpKXtcbiAgICAgICAgICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihrZXksIGhlYWRlcnNba2V5XSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0gZWxzZSBpZiAob3B0aW9ucy5oZWFkZXJzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkhlYWRlcnMgY2Fubm90IGJlIHNldCBvbiBhbiBYRG9tYWluUmVxdWVzdCBvYmplY3RcIilcbiAgICB9XG5cbiAgICBpZiAoXCJyZXNwb25zZVR5cGVcIiBpbiBvcHRpb25zKSB7XG4gICAgICAgIHhoci5yZXNwb25zZVR5cGUgPSBvcHRpb25zLnJlc3BvbnNlVHlwZVxuICAgIH1cbiAgICBcbiAgICBpZiAoXCJiZWZvcmVTZW5kXCIgaW4gb3B0aW9ucyAmJiBcbiAgICAgICAgdHlwZW9mIG9wdGlvbnMuYmVmb3JlU2VuZCA9PT0gXCJmdW5jdGlvblwiXG4gICAgKSB7XG4gICAgICAgIG9wdGlvbnMuYmVmb3JlU2VuZCh4aHIpXG4gICAgfVxuXG4gICAgeGhyLnNlbmQoYm9keSlcblxuICAgIHJldHVybiB4aHJcblxuICAgIGZ1bmN0aW9uIHJlYWR5c3RhdGVjaGFuZ2UoKSB7XG4gICAgICAgIGlmICh4aHIucmVhZHlTdGF0ZSA9PT0gNCkge1xuICAgICAgICAgICAgbG9hZCgpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRCb2R5KCkge1xuICAgICAgICAvLyBDaHJvbWUgd2l0aCByZXF1ZXN0VHlwZT1ibG9iIHRocm93cyBlcnJvcnMgYXJyb3VuZCB3aGVuIGV2ZW4gdGVzdGluZyBhY2Nlc3MgdG8gcmVzcG9uc2VUZXh0XG4gICAgICAgIHZhciBib2R5ID0gbnVsbFxuXG4gICAgICAgIGlmICh4aHIucmVzcG9uc2UpIHtcbiAgICAgICAgICAgIGJvZHkgPSB4aHIucmVzcG9uc2VcbiAgICAgICAgfSBlbHNlIGlmICh4aHIucmVzcG9uc2VUeXBlID09PSAndGV4dCcgfHwgIXhoci5yZXNwb25zZVR5cGUpIHtcbiAgICAgICAgICAgIGJvZHkgPSB4aHIucmVzcG9uc2VUZXh0IHx8IHhoci5yZXNwb25zZVhNTFxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGlzSnNvbikge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBib2R5ID0gSlNPTi5wYXJzZShib2R5KVxuICAgICAgICAgICAgfSBjYXRjaCAoZSkge31cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBib2R5XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0U3RhdHVzQ29kZSgpIHtcbiAgICAgICAgcmV0dXJuIHhoci5zdGF0dXMgPT09IDEyMjMgPyAyMDQgOiB4aHIuc3RhdHVzXG4gICAgfVxuXG4gICAgLy8gaWYgd2UncmUgZ2V0dGluZyBhIG5vbmUtb2sgc3RhdHVzQ29kZSwgYnVpbGQgJiByZXR1cm4gYW4gZXJyb3JcbiAgICBmdW5jdGlvbiBlcnJvckZyb21TdGF0dXNDb2RlKHN0YXR1cywgYm9keSkge1xuICAgICAgICB2YXIgZXJyb3IgPSBudWxsXG4gICAgICAgIGlmIChzdGF0dXMgPT09IDAgfHwgKHN0YXR1cyA+PSA0MDAgJiYgc3RhdHVzIDwgNjAwKSkge1xuICAgICAgICAgICAgdmFyIG1lc3NhZ2UgPSAodHlwZW9mIGJvZHkgPT09IFwic3RyaW5nXCIgPyBib2R5IDogZmFsc2UpIHx8XG4gICAgICAgICAgICAgICAgbWVzc2FnZXNbU3RyaW5nKHN0YXR1cykuY2hhckF0KDApXVxuICAgICAgICAgICAgZXJyb3IgPSBuZXcgRXJyb3IobWVzc2FnZSlcbiAgICAgICAgICAgIGVycm9yLnN0YXR1c0NvZGUgPSBzdGF0dXNcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBlcnJvclxuICAgIH1cblxuICAgIC8vIHdpbGwgbG9hZCB0aGUgZGF0YSAmIHByb2Nlc3MgdGhlIHJlc3BvbnNlIGluIGEgc3BlY2lhbCByZXNwb25zZSBvYmplY3RcbiAgICBmdW5jdGlvbiBsb2FkUmVzcG9uc2UoKSB7XG4gICAgICAgIHZhciBzdGF0dXMgPSBnZXRTdGF0dXNDb2RlKClcbiAgICAgICAgdmFyIGJvZHkgPSBnZXRCb2R5KClcbiAgICAgICAgdmFyIGVycm9yID0gZXJyb3JGcm9tU3RhdHVzQ29kZShzdGF0dXMsIGJvZHkpXG4gICAgICAgIHZhciByZXNwb25zZSA9IHtcbiAgICAgICAgICAgIGJvZHk6IGJvZHksXG4gICAgICAgICAgICBzdGF0dXNDb2RlOiBzdGF0dXMsXG4gICAgICAgICAgICBzdGF0dXNUZXh0OiB4aHIuc3RhdHVzVGV4dCxcbiAgICAgICAgICAgIHJhdzogeGhyXG4gICAgICAgIH1cbiAgICAgICAgaWYoeGhyLmdldEFsbFJlc3BvbnNlSGVhZGVycyl7IC8vcmVtZW1iZXIgeGhyIGNhbiBpbiBmYWN0IGJlIFhEUiBmb3IgQ09SUyBpbiBJRVxuICAgICAgICAgICAgcmVzcG9uc2UuaGVhZGVycyA9IHBhcnNlSGVhZGVycyh4aHIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXNwb25zZS5oZWFkZXJzID0ge31cbiAgICAgICAgfVxuXG4gICAgICAgIGNhbGxiYWNrKGVycm9yLCByZXNwb25zZSwgcmVzcG9uc2UuYm9keSlcbiAgICB9XG5cbiAgICAvLyB3aWxsIGxvYWQgdGhlIGRhdGEgYW5kIGFkZCBzb21lIHJlc3BvbnNlIHByb3BlcnRpZXMgdG8gdGhlIHNvdXJjZSB4aHJcbiAgICAvLyBhbmQgdGhlbiByZXNwb25kIHdpdGggdGhhdFxuICAgIGZ1bmN0aW9uIGxvYWRYaHIoKSB7XG4gICAgICAgIHZhciBzdGF0dXMgPSBnZXRTdGF0dXNDb2RlKClcbiAgICAgICAgdmFyIGVycm9yID0gZXJyb3JGcm9tU3RhdHVzQ29kZShzdGF0dXMpXG5cbiAgICAgICAgeGhyLnN0YXR1cyA9IHhoci5zdGF0dXNDb2RlID0gc3RhdHVzXG4gICAgICAgIHhoci5ib2R5ID0gZ2V0Qm9keSgpXG4gICAgICAgIHhoci5oZWFkZXJzID0gcGFyc2VIZWFkZXJzKHhoci5nZXRBbGxSZXNwb25zZUhlYWRlcnMoKSlcblxuICAgICAgICBjYWxsYmFjayhlcnJvciwgeGhyLCB4aHIuYm9keSlcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBlcnJvcihldnQpIHtcbiAgICAgICAgY2FsbGJhY2soZXZ0LCB4aHIpXG4gICAgfVxufVxuXG5cbmZ1bmN0aW9uIG5vb3AoKSB7fVxuIiwiaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHdpbmRvdztcbn0gZWxzZSBpZiAodHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIG1vZHVsZS5leHBvcnRzID0gZ2xvYmFsO1xufSBlbHNlIGlmICh0eXBlb2Ygc2VsZiAhPT0gXCJ1bmRlZmluZWRcIil7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBzZWxmO1xufSBlbHNlIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHt9O1xufVxuIiwibW9kdWxlLmV4cG9ydHMgPSBvbmNlXG5cbm9uY2UucHJvdG8gPSBvbmNlKGZ1bmN0aW9uICgpIHtcbiAgT2JqZWN0LmRlZmluZVByb3BlcnR5KEZ1bmN0aW9uLnByb3RvdHlwZSwgJ29uY2UnLCB7XG4gICAgdmFsdWU6IGZ1bmN0aW9uICgpIHtcbiAgICAgIHJldHVybiBvbmNlKHRoaXMpXG4gICAgfSxcbiAgICBjb25maWd1cmFibGU6IHRydWVcbiAgfSlcbn0pXG5cbmZ1bmN0aW9uIG9uY2UgKGZuKSB7XG4gIHZhciBjYWxsZWQgPSBmYWxzZVxuICByZXR1cm4gZnVuY3Rpb24gKCkge1xuICAgIGlmIChjYWxsZWQpIHJldHVyblxuICAgIGNhbGxlZCA9IHRydWVcbiAgICByZXR1cm4gZm4uYXBwbHkodGhpcywgYXJndW1lbnRzKVxuICB9XG59XG4iLCJ2YXIgaXNGdW5jdGlvbiA9IHJlcXVpcmUoJ2lzLWZ1bmN0aW9uJylcblxubW9kdWxlLmV4cG9ydHMgPSBmb3JFYWNoXG5cbnZhciB0b1N0cmluZyA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmdcbnZhciBoYXNPd25Qcm9wZXJ0eSA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHlcblxuZnVuY3Rpb24gZm9yRWFjaChsaXN0LCBpdGVyYXRvciwgY29udGV4dCkge1xuICAgIGlmICghaXNGdW5jdGlvbihpdGVyYXRvcikpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaXRlcmF0b3IgbXVzdCBiZSBhIGZ1bmN0aW9uJylcbiAgICB9XG5cbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA8IDMpIHtcbiAgICAgICAgY29udGV4dCA9IHRoaXNcbiAgICB9XG4gICAgXG4gICAgaWYgKHRvU3RyaW5nLmNhbGwobGlzdCkgPT09ICdbb2JqZWN0IEFycmF5XScpXG4gICAgICAgIGZvckVhY2hBcnJheShsaXN0LCBpdGVyYXRvciwgY29udGV4dClcbiAgICBlbHNlIGlmICh0eXBlb2YgbGlzdCA9PT0gJ3N0cmluZycpXG4gICAgICAgIGZvckVhY2hTdHJpbmcobGlzdCwgaXRlcmF0b3IsIGNvbnRleHQpXG4gICAgZWxzZVxuICAgICAgICBmb3JFYWNoT2JqZWN0KGxpc3QsIGl0ZXJhdG9yLCBjb250ZXh0KVxufVxuXG5mdW5jdGlvbiBmb3JFYWNoQXJyYXkoYXJyYXksIGl0ZXJhdG9yLCBjb250ZXh0KSB7XG4gICAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IGFycmF5Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgIGlmIChoYXNPd25Qcm9wZXJ0eS5jYWxsKGFycmF5LCBpKSkge1xuICAgICAgICAgICAgaXRlcmF0b3IuY2FsbChjb250ZXh0LCBhcnJheVtpXSwgaSwgYXJyYXkpXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1bmN0aW9uIGZvckVhY2hTdHJpbmcoc3RyaW5nLCBpdGVyYXRvciwgY29udGV4dCkge1xuICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBzdHJpbmcubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgLy8gbm8gc3VjaCB0aGluZyBhcyBhIHNwYXJzZSBzdHJpbmcuXG4gICAgICAgIGl0ZXJhdG9yLmNhbGwoY29udGV4dCwgc3RyaW5nLmNoYXJBdChpKSwgaSwgc3RyaW5nKVxuICAgIH1cbn1cblxuZnVuY3Rpb24gZm9yRWFjaE9iamVjdChvYmplY3QsIGl0ZXJhdG9yLCBjb250ZXh0KSB7XG4gICAgZm9yICh2YXIgayBpbiBvYmplY3QpIHtcbiAgICAgICAgaWYgKGhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBrKSkge1xuICAgICAgICAgICAgaXRlcmF0b3IuY2FsbChjb250ZXh0LCBvYmplY3Rba10sIGssIG9iamVjdClcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsIm1vZHVsZS5leHBvcnRzID0gaXNGdW5jdGlvblxuXG52YXIgdG9TdHJpbmcgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nXG5cbmZ1bmN0aW9uIGlzRnVuY3Rpb24gKGZuKSB7XG4gIHZhciBzdHJpbmcgPSB0b1N0cmluZy5jYWxsKGZuKVxuICByZXR1cm4gc3RyaW5nID09PSAnW29iamVjdCBGdW5jdGlvbl0nIHx8XG4gICAgKHR5cGVvZiBmbiA9PT0gJ2Z1bmN0aW9uJyAmJiBzdHJpbmcgIT09ICdbb2JqZWN0IFJlZ0V4cF0nKSB8fFxuICAgICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAvLyBJRTggYW5kIGJlbG93XG4gICAgIChmbiA9PT0gd2luZG93LnNldFRpbWVvdXQgfHxcbiAgICAgIGZuID09PSB3aW5kb3cuYWxlcnQgfHxcbiAgICAgIGZuID09PSB3aW5kb3cuY29uZmlybSB8fFxuICAgICAgZm4gPT09IHdpbmRvdy5wcm9tcHQpKVxufTtcbiIsIlxuZXhwb3J0cyA9IG1vZHVsZS5leHBvcnRzID0gdHJpbTtcblxuZnVuY3Rpb24gdHJpbShzdHIpe1xuICByZXR1cm4gc3RyLnJlcGxhY2UoL15cXHMqfFxccyokL2csICcnKTtcbn1cblxuZXhwb3J0cy5sZWZ0ID0gZnVuY3Rpb24oc3RyKXtcbiAgcmV0dXJuIHN0ci5yZXBsYWNlKC9eXFxzKi8sICcnKTtcbn07XG5cbmV4cG9ydHMucmlnaHQgPSBmdW5jdGlvbihzdHIpe1xuICByZXR1cm4gc3RyLnJlcGxhY2UoL1xccyokLywgJycpO1xufTtcbiIsInZhciB0cmltID0gcmVxdWlyZSgndHJpbScpXG4gICwgZm9yRWFjaCA9IHJlcXVpcmUoJ2Zvci1lYWNoJylcbiAgLCBpc0FycmF5ID0gZnVuY3Rpb24oYXJnKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGFyZykgPT09ICdbb2JqZWN0IEFycmF5XSc7XG4gICAgfVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChoZWFkZXJzKSB7XG4gIGlmICghaGVhZGVycylcbiAgICByZXR1cm4ge31cblxuICB2YXIgcmVzdWx0ID0ge31cblxuICBmb3JFYWNoKFxuICAgICAgdHJpbShoZWFkZXJzKS5zcGxpdCgnXFxuJylcbiAgICAsIGZ1bmN0aW9uIChyb3cpIHtcbiAgICAgICAgdmFyIGluZGV4ID0gcm93LmluZGV4T2YoJzonKVxuICAgICAgICAgICwga2V5ID0gdHJpbShyb3cuc2xpY2UoMCwgaW5kZXgpKS50b0xvd2VyQ2FzZSgpXG4gICAgICAgICAgLCB2YWx1ZSA9IHRyaW0ocm93LnNsaWNlKGluZGV4ICsgMSkpXG5cbiAgICAgICAgaWYgKHR5cGVvZihyZXN1bHRba2V5XSkgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgcmVzdWx0W2tleV0gPSB2YWx1ZVxuICAgICAgICB9IGVsc2UgaWYgKGlzQXJyYXkocmVzdWx0W2tleV0pKSB7XG4gICAgICAgICAgcmVzdWx0W2tleV0ucHVzaCh2YWx1ZSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXN1bHRba2V5XSA9IFsgcmVzdWx0W2tleV0sIHZhbHVlIF1cbiAgICAgICAgfVxuICAgICAgfVxuICApXG5cbiAgcmV0dXJuIHJlc3VsdFxufSIsIm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgnLi9qcXVlcnkuYnJvd3NlcicpO1xuIiwiLyohXG4gKiBqUXVlcnkgQnJvd3NlciBQbHVnaW4gdjAuMC42XG4gKiBodHRwczovL2dpdGh1Yi5jb20vZ2FiY2ViL2pxdWVyeS1icm93c2VyLXBsdWdpblxuICpcbiAqIE9yaWdpbmFsIGpxdWVyeS1icm93c2VyIGNvZGUgQ29weXJpZ2h0IDIwMDUsIDIwMTMgalF1ZXJ5IEZvdW5kYXRpb24sIEluYy4gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICpcbiAqIE1vZGlmaWNhdGlvbnMgQ29weXJpZ2h0IDIwMTMgR2FicmllbCBDZWJyaWFuXG4gKiBodHRwczovL2dpdGh1Yi5jb20vZ2FiY2ViXG4gKlxuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlXG4gKlxuICogRGF0ZTogMjAxMy0wNy0yOVQxNzoyMzoyNy0wNzowMFxuICovXG5cblxudmFyIG1hdGNoZWQsIGJyb3dzZXI7XG5cbnZhciB1YU1hdGNoID0gZnVuY3Rpb24oIHVhICkge1xuICB1YSA9IHVhLnRvTG93ZXJDYXNlKCk7XG5cbiAgdmFyIG1hdGNoID0gLyhvcHIpW1xcL10oW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcbiAgICAvKGNocm9tZSlbIFxcL10oW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcbiAgICAvKHZlcnNpb24pWyBcXC9dKFtcXHcuXSspLiooc2FmYXJpKVsgXFwvXShbXFx3Ll0rKS8uZXhlYyggdWEgKSB8fFxuICAgIC8od2Via2l0KVsgXFwvXShbXFx3Ll0rKS8uZXhlYyggdWEgKSB8fFxuICAgIC8ob3BlcmEpKD86Lip2ZXJzaW9ufClbIFxcL10oW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcbiAgICAvKG1zaWUpIChbXFx3Ll0rKS8uZXhlYyggdWEgKSB8fFxuICAgIHVhLmluZGV4T2YoXCJ0cmlkZW50XCIpID49IDAgJiYgLyhydikoPzo6fCApKFtcXHcuXSspLy5leGVjKCB1YSApIHx8XG4gICAgdWEuaW5kZXhPZihcImNvbXBhdGlibGVcIikgPCAwICYmIC8obW96aWxsYSkoPzouKj8gcnY6KFtcXHcuXSspfCkvLmV4ZWMoIHVhICkgfHxcbiAgICBbXTtcblxuICB2YXIgcGxhdGZvcm1fbWF0Y2ggPSAvKGlwYWQpLy5leGVjKCB1YSApIHx8XG4gICAgLyhpcGhvbmUpLy5leGVjKCB1YSApIHx8XG4gICAgLyhhbmRyb2lkKS8uZXhlYyggdWEgKSB8fFxuICAgIC8od2luZG93cyBwaG9uZSkvLmV4ZWMoIHVhICkgfHxcbiAgICAvKHdpbikvLmV4ZWMoIHVhICkgfHxcbiAgICAvKG1hYykvLmV4ZWMoIHVhICkgfHxcbiAgICAvKGxpbnV4KS8uZXhlYyggdWEgKSB8fFxuICAgIC8oY3JvcykvaS5leGVjKCB1YSApIHx8XG4gICAgW107XG5cbiAgcmV0dXJuIHtcbiAgICBicm93c2VyOiBtYXRjaFsgMyBdIHx8IG1hdGNoWyAxIF0gfHwgXCJcIixcbiAgICB2ZXJzaW9uOiBtYXRjaFsgMiBdIHx8IFwiMFwiLFxuICAgIHBsYXRmb3JtOiBwbGF0Zm9ybV9tYXRjaFsgMCBdIHx8IFwiXCJcbiAgfTtcbn07XG5cbm1hdGNoZWQgPSB1YU1hdGNoKCB3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudCApO1xuYnJvd3NlciA9IHt9O1xuYnJvd3Nlci51YU1hdGNoID0gdWFNYXRjaDtcblxuaWYgKCBtYXRjaGVkLmJyb3dzZXIgKSB7XG4gIGJyb3dzZXJbIG1hdGNoZWQuYnJvd3NlciBdID0gdHJ1ZTtcbiAgYnJvd3Nlci52ZXJzaW9uID0gbWF0Y2hlZC52ZXJzaW9uO1xuICBicm93c2VyLnZlcnNpb25OdW1iZXIgPSBwYXJzZUludChtYXRjaGVkLnZlcnNpb24pO1xufVxuXG5pZiAoIG1hdGNoZWQucGxhdGZvcm0gKSB7XG4gIGJyb3dzZXJbIG1hdGNoZWQucGxhdGZvcm0gXSA9IHRydWU7XG59XG5cbi8vIFRoZXNlIGFyZSBhbGwgY29uc2lkZXJlZCBtb2JpbGUgcGxhdGZvcm1zLCBtZWFuaW5nIHRoZXkgcnVuIGEgbW9iaWxlIGJyb3dzZXJcbmlmICggYnJvd3Nlci5hbmRyb2lkIHx8IGJyb3dzZXIuaXBhZCB8fCBicm93c2VyLmlwaG9uZSB8fCBicm93c2VyWyBcIndpbmRvd3MgcGhvbmVcIiBdICkge1xuICBicm93c2VyLm1vYmlsZSA9IHRydWU7XG59XG5cbi8vIFRoZXNlIGFyZSBhbGwgY29uc2lkZXJlZCBkZXNrdG9wIHBsYXRmb3JtcywgbWVhbmluZyB0aGV5IHJ1biBhIGRlc2t0b3AgYnJvd3NlclxuaWYgKCBicm93c2VyLmNyb3MgfHwgYnJvd3Nlci5tYWMgfHwgYnJvd3Nlci5saW51eCB8fCBicm93c2VyLndpbiApIHtcbiAgYnJvd3Nlci5kZXNrdG9wID0gdHJ1ZTtcbn1cblxuLy8gQ2hyb21lLCBPcGVyYSAxNSsgYW5kIFNhZmFyaSBhcmUgd2Via2l0IGJhc2VkIGJyb3dzZXJzXG5pZiAoIGJyb3dzZXIuY2hyb21lIHx8IGJyb3dzZXIub3ByIHx8IGJyb3dzZXIuc2FmYXJpICkge1xuICBicm93c2VyLndlYmtpdCA9IHRydWU7XG59XG5cbi8vIElFMTEgaGFzIGEgbmV3IHRva2VuIHNvIHdlIHdpbGwgYXNzaWduIGl0IG1zaWUgdG8gYXZvaWQgYnJlYWtpbmcgY2hhbmdlc1xuaWYgKCBicm93c2VyLnJ2IClcbntcbiAgdmFyIGllID0gXCJtc2llXCI7XG5cbiAgbWF0Y2hlZC5icm93c2VyID0gaWU7XG4gIGJyb3dzZXJbaWVdID0gdHJ1ZTtcbn1cblxuLy8gT3BlcmEgMTUrIGFyZSBpZGVudGlmaWVkIGFzIG9wclxuaWYgKCBicm93c2VyLm9wciApXG57XG4gIHZhciBvcGVyYSA9IFwib3BlcmFcIjtcblxuICBtYXRjaGVkLmJyb3dzZXIgPSBvcGVyYTtcbiAgYnJvd3NlcltvcGVyYV0gPSB0cnVlO1xufVxuXG4vLyBTdG9jayBBbmRyb2lkIGJyb3dzZXJzIGFyZSBtYXJrZWQgYXMgU2FmYXJpIG9uIEFuZHJvaWQuXG5pZiAoIGJyb3dzZXIuc2FmYXJpICYmIGJyb3dzZXIuYW5kcm9pZCApXG57XG4gIHZhciBhbmRyb2lkID0gXCJhbmRyb2lkXCI7XG5cbiAgbWF0Y2hlZC5icm93c2VyID0gYW5kcm9pZDtcbiAgYnJvd3NlclthbmRyb2lkXSA9IHRydWU7XG59XG5cbi8vIEFzc2lnbiB0aGUgbmFtZSBhbmQgcGxhdGZvcm0gdmFyaWFibGVcbmJyb3dzZXIubmFtZSA9IG1hdGNoZWQuYnJvd3NlcjtcbmJyb3dzZXIucGxhdGZvcm0gPSBtYXRjaGVkLnBsYXRmb3JtO1xuXG5cbm1vZHVsZS5leHBvcnRzID0gYnJvd3NlcjtcbiIsIi8qKiBAcHJlc2VydmUgaHR0cDovL2dpdGh1Yi5jb20vZWFzZXdheS9qcy1jbGFzcyAqL1xuXG4vLyBDbGFzcyBEZWZpbml0aW9uIHVzaW5nIEVDTUE1IHByb3RvdHlwZSBjaGFpblxuXG5mdW5jdGlvbiBpbmhlcml0KGRlc3QsIHNyYywgbm9QYXJlbnQpIHtcbiAgICB3aGlsZSAoc3JjICYmIHNyYyAhPT0gT2JqZWN0LnByb3RvdHlwZSkge1xuICAgICAgICBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhzcmMpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgIGlmIChuYW1lICE9ICcuY2xhc3MnICYmICFkZXN0Lmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHNyYywgbmFtZSk7XG4gICAgICAgICAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGRlc3QsIG5hbWUsIGRlc2MpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgaWYgKG5vUGFyZW50KSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBzcmMgPSBzcmMuX19wcm90b19fO1xuICAgIH1cbiAgICByZXR1cm4gZGVzdDtcbn1cblxudmFyIENsYXNzID0gZnVuY3Rpb24gKGJhc2UsIHByb3RvLCBvcHRpb25zKSB7XG4gICAgaWYgKHR5cGVvZihiYXNlKSAhPSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIG9wdGlvbnMgPSBwcm90bztcbiAgICAgICAgcHJvdG8gPSBiYXNlO1xuICAgICAgICBiYXNlID0gT2JqZWN0O1xuICAgIH1cbiAgICBpZiAoIXByb3RvKSB7XG4gICAgICAgIHByb3RvID0ge307XG4gICAgfVxuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgICBvcHRpb25zID0ge307XG4gICAgfVxuICAgIFxuICAgIHZhciBtZXRhID0ge1xuICAgICAgICBuYW1lOiBvcHRpb25zLm5hbWUsXG4gICAgICAgIGJhc2U6IGJhc2UsXG4gICAgICAgIGltcGxlbWVudHM6IFtdXG4gICAgfVxuICAgIHZhciBjbGFzc1Byb3RvID0gQ2xhc3MuY2xvbmUocHJvdG8pO1xuICAgIGlmIChvcHRpb25zLmltcGxlbWVudHMpIHtcbiAgICAgICAgKEFycmF5LmlzQXJyYXkob3B0aW9ucy5pbXBsZW1lbnRzKSA/IG9wdGlvbnMuaW1wbGVtZW50cyA6IFtvcHRpb25zLmltcGxlbWVudHNdKVxuICAgICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24gKGltcGxlbWVudGVkVHlwZSkge1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YoaW1wbGVtZW50ZWRUeXBlKSA9PSAnZnVuY3Rpb24nICYmIGltcGxlbWVudGVkVHlwZS5wcm90b3R5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgbWV0YS5pbXBsZW1lbnRzLnB1c2goaW1wbGVtZW50ZWRUeXBlKTtcbiAgICAgICAgICAgICAgICAgICAgQ2xhc3MuZXh0ZW5kKGNsYXNzUHJvdG8sIGltcGxlbWVudGVkVHlwZS5wcm90b3R5cGUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgIH1cbiAgICBjbGFzc1Byb3RvLl9fcHJvdG9fXyA9IGJhc2UucHJvdG90eXBlO1xuICAgIHZhciB0aGVDbGFzcyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKHR5cGVvZih0aGlzLmNvbnN0cnVjdG9yKSA9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICB0aGlzLmNvbnN0cnVjdG9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIG1ldGEudHlwZSA9IHRoZUNsYXNzO1xuICAgIHRoZUNsYXNzLnByb3RvdHlwZSA9IGNsYXNzUHJvdG87XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRoZUNsYXNzLCAnLmNsYXNzLm1ldGEnLCB7IHZhbHVlOiBtZXRhLCBlbnVtZXJhYmxlOiBmYWxzZSwgY29uZmlndXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IGZhbHNlIH0pO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjbGFzc1Byb3RvLCAnLmNsYXNzJywgeyB2YWx1ZTogdGhlQ2xhc3MsIGVudW1lcmFibGU6IGZhbHNlLCBjb25maWd1cmFibGU6IGZhbHNlLCB3cml0YWJsZTogZmFsc2UgfSk7XG4gICAgaWYgKG9wdGlvbnMuc3RhdGljcykge1xuICAgICAgICBDbGFzcy5leHRlbmQodGhlQ2xhc3MsIG9wdGlvbnMuc3RhdGljcyk7XG4gICAgfVxuICAgIHJldHVybiB0aGVDbGFzcztcbn07XG5cbkNsYXNzLmV4dGVuZCA9IGluaGVyaXQ7XG5cbkNsYXNzLmNsb25lID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiBpbmhlcml0KHt9LCBvYmplY3QpO1xufTtcblxuZnVuY3Rpb24gZmluZFR5cGUobWV0YSwgdHlwZSkge1xuICAgIHdoaWxlIChtZXRhKSB7XG4gICAgICAgIGlmIChtZXRhLnR5cGUucHJvdG90eXBlID09PSB0eXBlLnByb3RvdHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgZm9yICh2YXIgaSBpbiBtZXRhLmltcGxlbWVudHMpIHtcbiAgICAgICAgICAgIHZhciBpbXBsVHlwZSA9IG1ldGEuaW1wbGVtZW50c1tpXTtcbiAgICAgICAgICAgIHZhciBpbXBsTWV0YSA9IGltcGxUeXBlWycuY2xhc3MubWV0YSddO1xuICAgICAgICAgICAgaWYgKGltcGxNZXRhKSB7XG4gICAgICAgICAgICAgICAgaWYgKGZpbmRUeXBlKGltcGxNZXRhLCB0eXBlKSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIHByb3RvID0gaW1wbFR5cGUucHJvdG90eXBlOyBwcm90bzsgcHJvdG8gPSBwcm90by5fX3Byb3RvX18pIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHByb3RvID09PSB0eXBlLnByb3RvdHlwZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbWV0YSA9IG1ldGEuYmFzZSA/IG1ldGEuYmFzZVsnLmNsYXNzLm1ldGEnXSA6IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuXG52YXIgQ2hlY2tlciA9IENsYXNzKHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKG9iamVjdCkge1xuICAgICAgICB0aGlzLm9iamVjdCA9IG9iamVjdDtcbiAgICB9LFxuICAgIFxuICAgIHR5cGVPZjogZnVuY3Rpb24gKHR5cGUpIHtcbiAgICAgICAgaWYgKHRoaXMub2JqZWN0IGluc3RhbmNlb2YgdHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIG1ldGEgPSBDbGFzcy50eXBlSW5mbyh0aGlzLm9iamVjdCk7XG4gICAgICAgIHJldHVybiBtZXRhICYmIGZpbmRUeXBlKG1ldGEsIHR5cGUpO1xuICAgIH1cbn0pO1xuXG4vLyBhbGlhc2VzXG5DaGVja2VyLnByb3RvdHlwZS5hID0gQ2hlY2tlci5wcm90b3R5cGUudHlwZU9mO1xuQ2hlY2tlci5wcm90b3R5cGUuYW4gPSBDaGVja2VyLnByb3RvdHlwZS50eXBlT2Y7XG5cbkNsYXNzLmlzID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiBuZXcgQ2hlY2tlcihvYmplY3QpO1xufTtcblxuQ2xhc3MudHlwZUluZm8gPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgdmFyIHRoZUNsYXNzID0gb2JqZWN0Ll9fcHJvdG9fX1snLmNsYXNzJ107XG4gICAgcmV0dXJuIHRoZUNsYXNzID8gdGhlQ2xhc3NbJy5jbGFzcy5tZXRhJ10gOiB1bmRlZmluZWQ7XG59O1xuXG5DbGFzcy5WRVJTSU9OID0gWzAsIDAsIDJdO1xuXG5pZiAobW9kdWxlKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBDbGFzcztcbn0gZWxzZSB7XG4gICAgZ2xvYmFsLkNsYXNzID0gQ2xhc3M7ICAgLy8gZm9yIGJyb3dzZXJcbn0iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuOC4wXG5tb2R1bGUuZXhwb3J0cy5wYXJzZSA9IHJlcXVpcmUoXCIuL3BhcnNlclwiKTtcblxubW9kdWxlLmV4cG9ydHMud3JpdGVyID0gcmVxdWlyZShcIi4vd3JpdGVyXCIpO1xuIiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKFwiLi9saWIvaW5kZXhcIik7XG4iXX0=