1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.h54s = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
2 | /*
|
3 | * h54s error constructor
|
4 | * @constructor
|
5 | *
|
6 | *@param {string} type - Error type
|
7 | *@param {string} message - Error message
|
8 | *@param {string} status - Error status returned from SAS
|
9 | *
|
10 | */
|
11 | function h54sError(type, message, status) {
|
12 | if(Error.captureStackTrace) {
|
13 | Error.captureStackTrace(this);
|
14 | }
|
15 | this.message = message;
|
16 | this.type = type;
|
17 | this.status = status;
|
18 | }
|
19 |
|
20 | h54sError.prototype = Object.create(Error.prototype, {
|
21 | constructor: {
|
22 | configurable: false,
|
23 | enumerable: false,
|
24 | writable: false,
|
25 | value: h54sError
|
26 | },
|
27 | name: {
|
28 | configurable: false,
|
29 | enumerable: false,
|
30 | writable: false,
|
31 | value: 'h54sError'
|
32 | }
|
33 | });
|
34 |
|
35 | module.exports = h54sError;
|
36 |
|
37 | },{}],2:[function(require,module,exports){
|
38 | const h54sError = require('../error.js');
|
39 |
|
40 | /**
|
41 | * h54s SAS Files object constructor
|
42 | * @constructor
|
43 | *
|
44 | *@param {file} file - File added when object is created
|
45 | *@param {string} macroName - macro name
|
46 | *
|
47 | */
|
48 | function Files(file, macroName) {
|
49 | this._files = {};
|
50 |
|
51 | Files.prototype.add.call(this, file, macroName);
|
52 | }
|
53 |
|
54 | /**
|
55 | * Add file to files object
|
56 | * @param {file} file - Instance of JavaScript File object
|
57 | * @param {string} macroName - Sas macro name
|
58 | *
|
59 | */
|
60 | Files.prototype.add = function(file, macroName) {
|
61 | if(file && macroName) {
|
62 | if(!(file instanceof File || file instanceof Blob)) {
|
63 | throw new h54sError('argumentError', 'First argument must be instance of File object');
|
64 | }
|
65 | if(typeof macroName !== 'string') {
|
66 | throw new h54sError('argumentError', 'Second argument must be string');
|
67 | }
|
68 | if(!isNaN(macroName[macroName.length - 1])) {
|
69 | throw new h54sError('argumentError', 'Macro name cannot have number at the end');
|
70 | }
|
71 | } else {
|
72 | throw new h54sError('argumentError', 'Missing arguments');
|
73 | }
|
74 |
|
75 | this._files[macroName] = [
|
76 | 'FILE',
|
77 | file
|
78 | ];
|
79 | };
|
80 |
|
81 | module.exports = Files;
|
82 |
|
83 | },{"../error.js":1}],3:[function(require,module,exports){
|
84 | const h54sError = require('./error.js');
|
85 |
|
86 | const sasVersionMap = {
|
87 | v9: {
|
88 | url: '/SASStoredProcess/do',
|
89 | loginUrl: '/SASLogon/login',
|
90 | logoutUrl: '/SASStoredProcess/do?_action=logoff',
|
91 | RESTAuthLoginUrl: '/SASLogon/v1/tickets'
|
92 | },
|
93 | viya: {
|
94 | url: '/SASJobExecution/',
|
95 | loginUrl: '/SASLogon/login.do',
|
96 | logoutUrl: '/SASLogon/logout.do?',
|
97 | RESTAuthLoginUrl: ''
|
98 | }
|
99 | }
|
100 |
|
101 | /**
|
102 | *
|
103 | * @constructor
|
104 | * @param {Object} config - Configuration object for the H54S SAS Adapter
|
105 | * @param {String} config.sasVersion - Version of SAS, either 'v9' or 'viya'
|
106 | * @param {Boolean} config.debug - Whether debug mode is enabled, sets _debug=131
|
107 | * @param {String} config.metadataRoot - Base path of all project services to be prepended to _program path
|
108 | * @param {String} config.url - URI of the job executor - SPWA or JES
|
109 | * @param {String} config.loginUrl - URI of the SASLogon web login path - overridden by form action
|
110 | * @param {String} config.logoutUrl - URI of the logout action
|
111 | * @param {String} config.RESTauth - Boolean to toggle use of REST authentication in SAS v9
|
112 | * @param {String} config.RESTauthLoginUrl - Address of SASLogon tickets endpoint for REST auth
|
113 | * @param {Boolean} config.retryAfterLogin - Whether to resume requests which were parked with login redirect after a successful re-login
|
114 | * @param {Number} config.maxXhrRetries - If a program call fails, attempt to call it again N times until it succeeds
|
115 | * @param {Number} config.ajaxTimeout - Number of milliseconds to wait for a response before closing the request
|
116 | * @param {Boolean} config.useMultipartFormData - Whether to use multipart for POST - for legacy backend support
|
117 | * @param {String} config.csrf - CSRF token for JES
|
118 | * @
|
119 | *
|
120 | */
|
121 | const h54s = module.exports = function(config) {
|
122 | // Default config values, overridden by anything in the config object
|
123 | this.sasVersion = (config && config.sasVersion) || 'v9' //use v9 as default=
|
124 | this.debug = (config && config.debug) || false;
|
125 | this.metadataRoot = (config && config.metadataRoot) || '';
|
126 | this.url = sasVersionMap[this.sasVersion].url;
|
127 | this.loginUrl = sasVersionMap[this.sasVersion].loginUrl;
|
128 | this.logoutUrl = sasVersionMap[this.sasVersion].logoutUrl;
|
129 | this.RESTauth = false;
|
130 | this.RESTauthLoginUrl = sasVersionMap[this.sasVersion].RESTAuthLoginUrl;
|
131 | this.retryAfterLogin = true;
|
132 | this.maxXhrRetries = 5;
|
133 | this.ajaxTimeout = (config && config.ajaxTimeout) || 300000;
|
134 | this.useMultipartFormData = (config && config.useMultipartFormData) || true;
|
135 | this.csrf = ''
|
136 | this.isViya = this.sasVersion === 'viya';
|
137 |
|
138 | // Initialising callback stacks for when authentication is paused
|
139 | this.remoteConfigUpdateCallbacks = [];
|
140 | this._pendingCalls = [];
|
141 | this._customPendingCalls = [];
|
142 | this._disableCalls = false
|
143 | this._ajax = require('./methods/ajax.js')();
|
144 |
|
145 | _setConfig.call(this, config);
|
146 |
|
147 | // If this instance was deployed with a standalone config external to the build use that
|
148 | if(config && config.isRemoteConfig) {
|
149 | const self = this;
|
150 |
|
151 | this._disableCalls = true;
|
152 |
|
153 | // 'h54sConfig.json' is for the testing with karma
|
154 | //replaced by gulp in dev build (defined in gulpfile under proxies)
|
155 | this._ajax.get('h54sConfig.json').success(function(res) {
|
156 | const remoteConfig = JSON.parse(res.responseText)
|
157 |
|
158 | // Save local config before updating it with remote config
|
159 | const localConfig = Object.assign({}, config)
|
160 | const oldMetadataRoot = localConfig.metadataRoot;
|
161 |
|
162 | for(let key in remoteConfig) {
|
163 | if(remoteConfig.hasOwnProperty(key) && key !== 'isRemoteConfig') {
|
164 | config[key] = remoteConfig[key];
|
165 | }
|
166 | }
|
167 |
|
168 | _setConfig.call(self, config);
|
169 |
|
170 | // Execute callbacks when overrides from remote config are applied
|
171 | for(let i = 0, n = self.remoteConfigUpdateCallbacks.length; i < n; i++) {
|
172 | const fn = self.remoteConfigUpdateCallbacks[i];
|
173 | fn();
|
174 | }
|
175 |
|
176 | // Execute sas calls disabled while waiting for the config
|
177 | self._disableCalls = false;
|
178 | while(self._pendingCalls.length > 0) {
|
179 | const pendingCall = self._pendingCalls.shift();
|
180 | const sasProgram = pendingCall.options.sasProgram;
|
181 | const callbackPending = pendingCall.options.callback;
|
182 | const params = pendingCall.params;
|
183 | //update debug because it may change in the meantime
|
184 | params._debug = self.debug ? 131 : 0;
|
185 |
|
186 | // Update program path with metadataRoot if it's not set
|
187 | if(self.metadataRoot && params._program.indexOf(self.metadataRoot) === -1) {
|
188 | params._program = self.metadataRoot.replace(/\/?$/, '/') + params._program.replace(oldMetadataRoot, '').replace(/^\//, '');
|
189 | }
|
190 |
|
191 | // Update debug because it may change in the meantime
|
192 | params._debug = self.debug ? 131 : 0;
|
193 |
|
194 | self.call(sasProgram, null, callbackPending, params);
|
195 | }
|
196 |
|
197 | // Execute custom calls that we made while waitinf for the config
|
198 | while(self._customPendingCalls.length > 0) {
|
199 | const pendingCall = self._customPendingCalls.shift()
|
200 | const callMethod = pendingCall.callMethod
|
201 | const _url = pendingCall._url
|
202 | const options = pendingCall.options;
|
203 | ///update program with metadataRoot if it's not set
|
204 | if(self.metadataRoot && options.params && options.params._program.indexOf(self.metadataRoot) === -1) {
|
205 | options.params._program = self.metadataRoot.replace(/\/?$/, '/') + options.params._program.replace(oldMetadataRoot, '').replace(/^\//, '');
|
206 | }
|
207 | //update debug because it also may have changed from remoteConfig
|
208 | if (options.params) {
|
209 | options.params._debug = self.debug ? 131 : 0;
|
210 | }
|
211 | self.managedRequest(callMethod, _url, options);
|
212 | }
|
213 | }).error(function (err) {
|
214 | throw new h54sError('ajaxError', 'Remote config file cannot be loaded. Http status code: ' + err.status);
|
215 | });
|
216 | }
|
217 |
|
218 | // private function to set h54s instance properties
|
219 | function _setConfig(config) {
|
220 | if(!config) {
|
221 | this._ajax.setTimeout(this.ajaxTimeout);
|
222 | return;
|
223 | } else if(typeof config !== 'object') {
|
224 | throw new h54sError('argumentError', 'First parameter should be config object');
|
225 | }
|
226 |
|
227 | //merge config object from parameter with this
|
228 | for(let key in config) {
|
229 | if(config.hasOwnProperty(key)) {
|
230 | if((key === 'url' || key === 'loginUrl') && config[key].charAt(0) !== '/') {
|
231 | config[key] = '/' + config[key];
|
232 | }
|
233 | this[key] = config[key];
|
234 | }
|
235 | }
|
236 |
|
237 | //if server is remote use the full server url
|
238 | //NOTE: This requires CORS and is here for legacy support
|
239 | if(config.hostUrl) {
|
240 | if(config.hostUrl.charAt(config.hostUrl.length - 1) === '/') {
|
241 | config.hostUrl = config.hostUrl.slice(0, -1);
|
242 | }
|
243 | this.hostUrl = config.hostUrl;
|
244 | if (!this.url.includes(this.hostUrl)) {
|
245 | this.url = config.hostUrl + this.url;
|
246 | }
|
247 | if (!this.loginUrl.includes(this.hostUrl)) {
|
248 | this.loginUrl = config.hostUrl + this.loginUrl;
|
249 | }
|
250 | if (!this.RESTauthLoginUrl.includes(this.hostUrl)) {
|
251 | this.RESTauthLoginUrl = config.hostUrl + this.RESTauthLoginUrl;
|
252 | }
|
253 | }
|
254 |
|
255 | this._ajax.setTimeout(this.ajaxTimeout);
|
256 | }
|
257 | };
|
258 |
|
259 | // replaced by gulp with real version at build time
|
260 | h54s.version = '2.2.5';
|
261 |
|
262 |
|
263 | h54s.prototype = require('./methods');
|
264 |
|
265 | h54s.Tables = require('./tables');
|
266 | h54s.Files = require('./files');
|
267 | h54s.SasData = require('./sasData.js');
|
268 |
|
269 | h54s.fromSasDateTime = require('./methods/utils.js').fromSasDateTime;
|
270 | h54s.toSasDateTime = require('./tables/utils.js').toSasDateTime;
|
271 |
|
272 | //self invoked function module
|
273 | require('./ie_polyfills.js');
|
274 |
|
275 | },{"./error.js":1,"./files":2,"./ie_polyfills.js":4,"./methods":7,"./methods/ajax.js":6,"./methods/utils.js":8,"./sasData.js":9,"./tables":10,"./tables/utils.js":11}],4:[function(require,module,exports){
|
276 | module.exports = function() {
|
277 | if (!Object.create) {
|
278 | Object.create = function(proto, props) {
|
279 | if (typeof props !== "undefined") {
|
280 | throw "The multiple-argument version of Object.create is not provided by this browser and cannot be shimmed.";
|
281 | }
|
282 | function ctor() { }
|
283 | ctor.prototype = proto;
|
284 | return new ctor();
|
285 | };
|
286 | }
|
287 |
|
288 |
|
289 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
290 | if (!Object.keys) {
|
291 | Object.keys = (function () {
|
292 | ;
|
293 | var hasOwnProperty = Object.prototype.hasOwnProperty,
|
294 | hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
295 | dontEnums = [
|
296 | 'toString',
|
297 | 'toLocaleString',
|
298 | 'valueOf',
|
299 | 'hasOwnProperty',
|
300 | 'isPrototypeOf',
|
301 | 'propertyIsEnumerable',
|
302 | 'constructor'
|
303 | ],
|
304 | dontEnumsLength = dontEnums.length;
|
305 |
|
306 | return function (obj) {
|
307 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
|
308 | throw new TypeError('Object.keys called on non-object');
|
309 | }
|
310 |
|
311 | var result = [], prop, i;
|
312 |
|
313 | for (prop in obj) {
|
314 | if (hasOwnProperty.call(obj, prop)) {
|
315 | result.push(prop);
|
316 | }
|
317 | }
|
318 |
|
319 | if (hasDontEnumBug) {
|
320 | for (i = 0; i < dontEnumsLength; i++) {
|
321 | if (hasOwnProperty.call(obj, dontEnums[i])) {
|
322 | result.push(dontEnums[i]);
|
323 | }
|
324 | }
|
325 | }
|
326 | return result;
|
327 | };
|
328 | }());
|
329 | }
|
330 |
|
331 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
|
332 | if (!Array.prototype.lastIndexOf) {
|
333 | Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
|
334 | ;
|
335 |
|
336 | if (this === void 0 || this === null) {
|
337 | throw new TypeError();
|
338 | }
|
339 |
|
340 | var n, k,
|
341 | t = Object(this),
|
342 | len = t.length >>> 0;
|
343 | if (len === 0) {
|
344 | return -1;
|
345 | }
|
346 |
|
347 | n = len - 1;
|
348 | if (arguments.length > 1) {
|
349 | n = Number(arguments[1]);
|
350 | if (n != n) {
|
351 | n = 0;
|
352 | }
|
353 | else if (n !== 0 && n != (1 / 0) && n != -(1 / 0)) {
|
354 | n = (n > 0 || -1) * Math.floor(Math.abs(n));
|
355 | }
|
356 | }
|
357 |
|
358 | for (k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); k >= 0; k--) {
|
359 | if (k in t && t[k] === searchElement) {
|
360 | return k;
|
361 | }
|
362 | }
|
363 | return -1;
|
364 | };
|
365 | }
|
366 | }();
|
367 |
|
368 | if (window.NodeList && !NodeList.prototype.forEach) {
|
369 | NodeList.prototype.forEach = Array.prototype.forEach;
|
370 | }
|
371 | },{}],5:[function(require,module,exports){
|
372 | const logs = {
|
373 | applicationLogs: [],
|
374 | debugData: [],
|
375 | sasErrors: [],
|
376 | failedRequests: []
|
377 | };
|
378 |
|
379 | const limits = {
|
380 | applicationLogs: 100,
|
381 | debugData: 20,
|
382 | failedRequests: 20,
|
383 | sasErrors: 100
|
384 | };
|
385 |
|
386 | module.exports.get = {
|
387 | getSasErrors: function() {
|
388 | return logs.sasErrors;
|
389 | },
|
390 | getApplicationLogs: function() {
|
391 | return logs.applicationLogs;
|
392 | },
|
393 | getDebugData: function() {
|
394 | return logs.debugData;
|
395 | },
|
396 | getFailedRequests: function() {
|
397 | return logs.failedRequests;
|
398 | },
|
399 | getAllLogs: function () {
|
400 | return {
|
401 | sasErrors: logs.sasErrors,
|
402 | applicationLogs: logs.applicationLogs,
|
403 | debugData: logs.debugData,
|
404 | failedRequests: logs.failedRequests
|
405 | }
|
406 | }
|
407 | };
|
408 |
|
409 | module.exports.clear = {
|
410 | clearApplicationLogs: function() {
|
411 | logs.applicationLogs.splice(0, logs.applicationLogs.length);
|
412 | },
|
413 | clearDebugData: function() {
|
414 | logs.debugData.splice(0, logs.debugData.length);
|
415 | },
|
416 | clearSasErrors: function() {
|
417 | logs.sasErrors.splice(0, logs.sasErrors.length);
|
418 | },
|
419 | clearFailedRequests: function() {
|
420 | logs.failedRequests.splice(0, logs.failedRequests.length);
|
421 | },
|
422 | clearAllLogs: function() {
|
423 | this.clearApplicationLogs();
|
424 | this.clearDebugData();
|
425 | this.clearSasErrors();
|
426 | this.clearFailedRequests();
|
427 | }
|
428 | };
|
429 |
|
430 | /**
|
431 | * Adds application logs to an array of logs
|
432 | *
|
433 | * @param {String} message - Message to add to applicationLogs
|
434 | * @param {String} sasProgram - Header - which request did message come from
|
435 | *
|
436 | */
|
437 | module.exports.addApplicationLog = function(message, sasProgram) {
|
438 | if(message === 'blank') {
|
439 | return;
|
440 | }
|
441 | const log = {
|
442 | message: message,
|
443 | time: new Date(),
|
444 | sasProgram: sasProgram
|
445 | };
|
446 | logs.applicationLogs.push(log);
|
447 |
|
448 | if(logs.applicationLogs.length > limits.applicationLogs) {
|
449 | logs.applicationLogs.shift();
|
450 | }
|
451 | };
|
452 |
|
453 | /**
|
454 | * Adds debug data to an array of logs
|
455 | *
|
456 | * @param {String} htmlData - Full html log from executor
|
457 | * @param {String} debugText - Debug text that came after data output
|
458 | * @param {String} sasProgram - Which program request did message come from
|
459 | * @param {String} params - Web app params that were received
|
460 | *
|
461 | */
|
462 | module.exports.addDebugData = function(htmlData, debugText, sasProgram, params) {
|
463 | logs.debugData.push({
|
464 | debugHtml: htmlData,
|
465 | debugText: debugText,
|
466 | sasProgram: sasProgram,
|
467 | params: params,
|
468 | time: new Date()
|
469 | });
|
470 |
|
471 | if(logs.debugData.length > limits.debugData) {
|
472 | logs.debugData.shift();
|
473 | }
|
474 | };
|
475 |
|
476 | /**
|
477 | * Adds failed requests to an array of failed request logs
|
478 | *
|
479 | * @param {String} responseText - Full html output from executor
|
480 | * @param {String} debugText - Debug text that came after data output
|
481 | * @param {String} sasProgram - Which program request did message come from
|
482 | *
|
483 | */
|
484 | module.exports.addFailedRequest = function(responseText, debugText, sasProgram) {
|
485 | logs.failedRequests.push({
|
486 | responseHtml: responseText,
|
487 | responseText: debugText,
|
488 | sasProgram: sasProgram,
|
489 | time: new Date()
|
490 | });
|
491 |
|
492 | //max 20 failed requests
|
493 | if(logs.failedRequests.length > limits.failedRequests) {
|
494 | logs.failedRequests.shift();
|
495 | }
|
496 | };
|
497 |
|
498 | /**
|
499 | * Adds SAS errors to an array of logs
|
500 | *
|
501 | * @param {Array} errors - Array of errors to concat to main log
|
502 | *
|
503 | */
|
504 | module.exports.addSasErrors = function(errors) {
|
505 | logs.sasErrors = logs.sasErrors.concat(errors);
|
506 |
|
507 | while(logs.sasErrors.length > limits.sasErrors) {
|
508 | logs.sasErrors.shift();
|
509 | }
|
510 | };
|
511 |
|
512 | },{}],6:[function(require,module,exports){
|
513 | module.exports = function () {
|
514 | let timeout = 30000;
|
515 | let timeoutHandle;
|
516 |
|
517 | const xhr = function (type, url, data, multipartFormData, headers = {}) {
|
518 | const methods = {
|
519 | success: function () {
|
520 | },
|
521 | error: function () {
|
522 | }
|
523 | };
|
524 |
|
525 | const XHR = XMLHttpRequest;
|
526 | const request = new XHR('MSXML2.XMLHTTP.3.0');
|
527 |
|
528 | request.open(type, url, true);
|
529 |
|
530 | //multipart/form-data is set automatically so no need for else block
|
531 | // Content-Type header has to be explicitly set up
|
532 | if (!multipartFormData) {
|
533 | if (headers['Content-Type']) {
|
534 | request.setRequestHeader('Content-Type', headers['Content-Type'])
|
535 | } else {
|
536 | request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
537 | }
|
538 | }
|
539 | Object.keys(headers).forEach(key => {
|
540 | if (key !== 'Content-Type') {
|
541 | request.setRequestHeader(key, headers[key])
|
542 | }
|
543 | })
|
544 | request.onreadystatechange = function () {
|
545 | if (request.readyState === 4) {
|
546 | clearTimeout(timeoutHandle);
|
547 | if (request.status >= 200 && request.status < 300) {
|
548 | methods.success.call(methods, request);
|
549 | } else {
|
550 | methods.error.call(methods, request);
|
551 | }
|
552 | }
|
553 | };
|
554 |
|
555 | if (timeout > 0) {
|
556 | timeoutHandle = setTimeout(function () {
|
557 | request.abort();
|
558 | }, timeout);
|
559 | }
|
560 |
|
561 | request.send(data);
|
562 |
|
563 | return {
|
564 | success: function (callback) {
|
565 | methods.success = callback;
|
566 | return this;
|
567 | },
|
568 | error: function (callback) {
|
569 | methods.error = callback;
|
570 | return this;
|
571 | }
|
572 | };
|
573 | };
|
574 |
|
575 | const serialize = function (obj) {
|
576 | const str = [];
|
577 | for (let p in obj) {
|
578 | if (obj.hasOwnProperty(p)) {
|
579 | if (obj[p] instanceof Array) {
|
580 | for (let i = 0, n = obj[p].length; i < n; i++) {
|
581 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]));
|
582 | }
|
583 | } else {
|
584 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
585 | }
|
586 | }
|
587 | }
|
588 | return str.join("&");
|
589 | };
|
590 |
|
591 | const createMultipartFormDataPayload = function (obj) {
|
592 | let data = new FormData();
|
593 | for (let p in obj) {
|
594 | if (obj.hasOwnProperty(p)) {
|
595 | if (obj[p] instanceof Array && p !== 'file') {
|
596 | for (let i = 0, n = obj[p].length; i < n; i++) {
|
597 | data.append(p, obj[p][i]);
|
598 | }
|
599 | } else if (p === 'file') {
|
600 | data.append(p, obj[p][0], obj[p][1]);
|
601 | } else {
|
602 | data.append(p, obj[p]);
|
603 | }
|
604 | }
|
605 | }
|
606 | return data;
|
607 | };
|
608 |
|
609 | return {
|
610 | get: function (url, data, multipartFormData, headers) {
|
611 | let dataStr;
|
612 | if (typeof data === 'object') {
|
613 | dataStr = serialize(data);
|
614 | }
|
615 | const urlWithParams = dataStr ? (url + '?' + dataStr) : url;
|
616 | return xhr('GET', urlWithParams, null, multipartFormData, headers);
|
617 | },
|
618 | post: function(url, data, multipartFormData, headers) {
|
619 | let payload = data;
|
620 | if(typeof data === 'object') {
|
621 | if(multipartFormData) {
|
622 | payload = createMultipartFormDataPayload(data);
|
623 | } else {
|
624 | payload = serialize(data);
|
625 | }
|
626 | }
|
627 | return xhr('POST', url, payload, multipartFormData, headers);
|
628 | },
|
629 | put: function(url, data, multipartFormData, headers) {
|
630 | let payload = data;
|
631 | if(typeof data === 'object') {
|
632 | if(multipartFormData) {
|
633 | payload = createMultipartFormDataPayload(data);
|
634 | }
|
635 | }
|
636 | return xhr('PUT', url, payload, multipartFormData, headers);
|
637 | },
|
638 | delete: function(url, payload, multipartFormData, headers) {
|
639 | return xhr('DELETE', url, payload, null, headers);
|
640 | },
|
641 | patch: function(url, data, multipartFormData, headers) {
|
642 | let payload = data;
|
643 | if(typeof data === 'object') {
|
644 | if(multipartFormData) {
|
645 | payload = createMultipartFormDataPayload(data);
|
646 | }
|
647 | }
|
648 | return xhr('PATCH', url, payload, multipartFormData, headers);
|
649 | },
|
650 | setTimeout: function (t) {
|
651 | timeout = t;
|
652 | },
|
653 | serialize
|
654 | };
|
655 | };
|
656 |
|
657 | },{}],7:[function(require,module,exports){
|
658 | const h54sError = require('../error.js');
|
659 | const logs = require('../logs.js');
|
660 | const Tables = require('../tables');
|
661 | const SasData = require('../sasData.js');
|
662 | const Files = require('../files');
|
663 |
|
664 | /**
|
665 | * Call Sas program
|
666 | *
|
667 | * @param {string} sasProgram - Path of the sas program
|
668 | * @param {Object} dataObj - Instance of Tables object with data added
|
669 | * @param {function} callback - Callback function called when ajax call is finished
|
670 | * @param {Object} params - object containing additional program parameters
|
671 | *
|
672 | */
|
673 | module.exports.call = function (sasProgram, dataObj, callback, params) {
|
674 | const self = this;
|
675 | let retryCount = 0;
|
676 | const dbg = this.debug
|
677 | const csrf = this.csrf;
|
678 |
|
679 | if (!callback || typeof callback !== 'function') {
|
680 | throw new h54sError('argumentError', 'You must provide a callback');
|
681 | }
|
682 | if (!sasProgram) {
|
683 | throw new h54sError('argumentError', 'You must provide Sas program file path');
|
684 | }
|
685 | if (typeof sasProgram !== 'string') {
|
686 | throw new h54sError('argumentError', 'First parameter should be string');
|
687 | }
|
688 | if (this.useMultipartFormData === false && !(dataObj instanceof Tables)) {
|
689 | throw new h54sError('argumentError', 'Cannot send files using application/x-www-form-urlencoded. Please use Tables or default value for useMultipartFormData');
|
690 | }
|
691 |
|
692 | if (!params) {
|
693 | params = {
|
694 | _program: this._utils.getFullProgramPath(this.metadataRoot, sasProgram),
|
695 | _debug: this.debug ? 131 : 0,
|
696 | _service: 'default',
|
697 | _csrf: csrf
|
698 | };
|
699 | } else {
|
700 | params = Object.assign({}, params, {_csrf: csrf})
|
701 | }
|
702 |
|
703 | if (dataObj) {
|
704 | let key, dataProvider;
|
705 | if (dataObj instanceof Tables) {
|
706 | dataProvider = dataObj._tables;
|
707 | } else if (dataObj instanceof Files || dataObj instanceof SasData) {
|
708 | dataProvider = dataObj._files;
|
709 | } else {
|
710 | console.log(new h54sError('argumentError', 'Wrong type of tables object'))
|
711 | }
|
712 | for (key in dataProvider) {
|
713 | if (dataProvider.hasOwnProperty(key)) {
|
714 | params[key] = dataProvider[key];
|
715 | }
|
716 | }
|
717 | }
|
718 |
|
719 | if (this._disableCalls) {
|
720 | this._pendingCalls.push({
|
721 | params,
|
722 | options: {
|
723 | sasProgram,
|
724 | dataObj,
|
725 | callback
|
726 | }
|
727 | });
|
728 | return;
|
729 | }
|
730 |
|
731 | this._ajax.post(this.url, params, this.useMultipartFormData).success(async function (res) {
|
732 | if (self._utils.needToLogin.call(self, res)) {
|
733 | //remember the call for latter use
|
734 | self._pendingCalls.push({
|
735 | params,
|
736 | options: {
|
737 | sasProgram,
|
738 | dataObj,
|
739 | callback
|
740 | }
|
741 | });
|
742 |
|
743 | //there's no need to continue if previous call returned login error
|
744 | if (self._disableCalls) {
|
745 | return;
|
746 | } else {
|
747 | self._disableCalls = true;
|
748 | }
|
749 |
|
750 | callback(new h54sError('notLoggedinError', 'You are not logged in'));
|
751 | } else {
|
752 | let resObj, unescapedResObj, err;
|
753 | let done = false;
|
754 |
|
755 | if (!dbg) {
|
756 | try {
|
757 | resObj = self._utils.parseRes(res.responseText, sasProgram, params);
|
758 | logs.addApplicationLog(resObj.logmessage, sasProgram);
|
759 |
|
760 | if (dataObj instanceof Tables) {
|
761 | unescapedResObj = self._utils.unescapeValues(resObj);
|
762 | } else {
|
763 | unescapedResObj = resObj;
|
764 | }
|
765 |
|
766 | if (resObj.status !== 'success') {
|
767 | err = new h54sError('programError', resObj.errormessage, resObj.status);
|
768 | }
|
769 |
|
770 | done = true;
|
771 | } catch (e) {
|
772 | if (e instanceof SyntaxError) {
|
773 | if (retryCount < self.maxXhrRetries) {
|
774 | done = false;
|
775 | self._ajax.post(self.url, params, self.useMultipartFormData).success(this.success).error(this.error);
|
776 | retryCount++;
|
777 | logs.addApplicationLog("Retrying #" + retryCount, sasProgram);
|
778 | } else {
|
779 | self._utils.parseErrorResponse(res.responseText, sasProgram);
|
780 | self._utils.addFailedResponse(res.responseText, sasProgram);
|
781 | err = new h54sError('parseError', 'Unable to parse response json');
|
782 | done = true;
|
783 | }
|
784 | } else if (e instanceof h54sError) {
|
785 | self._utils.parseErrorResponse(res.responseText, sasProgram);
|
786 | self._utils.addFailedResponse(res.responseText, sasProgram);
|
787 | err = e;
|
788 | done = true;
|
789 | } else {
|
790 | self._utils.parseErrorResponse(res.responseText, sasProgram);
|
791 | self._utils.addFailedResponse(res.responseText, sasProgram);
|
792 | err = new h54sError('unknownError', e.message);
|
793 | err.stack = e.stack;
|
794 | done = true;
|
795 | }
|
796 | } finally {
|
797 | if (done) {
|
798 | callback(err, unescapedResObj);
|
799 | }
|
800 | }
|
801 | } else {
|
802 | try {
|
803 | resObj = await self._utils.parseDebugRes(res.responseText, sasProgram, params, self.hostUrl, self.isViya);
|
804 | logs.addApplicationLog(resObj.logmessage, sasProgram);
|
805 |
|
806 | if (dataObj instanceof Tables) {
|
807 | unescapedResObj = self._utils.unescapeValues(resObj);
|
808 | } else {
|
809 | unescapedResObj = resObj;
|
810 | }
|
811 |
|
812 | if (resObj.status !== 'success') {
|
813 | err = new h54sError('programError', resObj.errormessage, resObj.status);
|
814 | }
|
815 |
|
816 | done = true;
|
817 | } catch (e) {
|
818 | if (e instanceof SyntaxError) {
|
819 | err = new h54sError('parseError', e.message);
|
820 | done = true;
|
821 | } else if (e instanceof h54sError) {
|
822 | if (e.type === 'parseError' && retryCount < 1) {
|
823 | done = false;
|
824 | self._ajax.post(self.url, params, self.useMultipartFormData).success(this.success).error(this.error);
|
825 | retryCount++;
|
826 | logs.addApplicationLog("Retrying #" + retryCount, sasProgram);
|
827 | } else {
|
828 | if (e instanceof h54sError) {
|
829 | err = e;
|
830 | } else {
|
831 | err = new h54sError('parseError', 'Unable to parse response json');
|
832 | }
|
833 | done = true;
|
834 | }
|
835 | } else {
|
836 | err = new h54sError('unknownError', e.message);
|
837 | err.stack = e.stack;
|
838 | done = true;
|
839 | }
|
840 | } finally {
|
841 | if (done) {
|
842 | callback(err, unescapedResObj);
|
843 | }
|
844 | }
|
845 | }
|
846 | }
|
847 | }).error(function (res) {
|
848 | let _csrf
|
849 | if (res.status === 449 || (res.status === 403 && (res.responseText.includes('_csrf') || res.getResponseHeader('X-Forbidden-Reason') === 'CSRF') && (_csrf = res.getResponseHeader(res.getResponseHeader('X-CSRF-HEADER'))))) {
|
850 | params['_csrf'] = _csrf;
|
851 | self.csrf = _csrf
|
852 | if (retryCount < self.maxXhrRetries) {
|
853 | self._ajax.post(self.url, params, true).success(this.success).error(this.error);
|
854 | retryCount++;
|
855 | logs.addApplicationLog("Retrying #" + retryCount, sasProgram);
|
856 | } else {
|
857 | self._utils.parseErrorResponse(res.responseText, sasProgram);
|
858 | self._utils.addFailedResponse(res.responseText, sasProgram);
|
859 | callback(new h54sError('parseError', 'Unable to parse response json'));
|
860 | }
|
861 | } else {
|
862 | logs.addApplicationLog('Request failed with status: ' + res.status, sasProgram);
|
863 | // if request has error text else callback
|
864 | callback(new h54sError('httpError', res.statusText));
|
865 | }
|
866 | });
|
867 | };
|
868 |
|
869 | /**
|
870 | * Login method
|
871 | *
|
872 | * @param {string} user - Login username
|
873 | * @param {string} pass - Login password
|
874 | * @param {function} callback - Callback function called when ajax call is finished
|
875 | *
|
876 | * OR
|
877 | *
|
878 | * @param {function} callback - Callback function called when ajax call is finished
|
879 | *
|
880 | */
|
881 | module.exports.login = function (user, pass, callback) {
|
882 | if (!user || !pass) {
|
883 | throw new h54sError('argumentError', 'Credentials not set');
|
884 | }
|
885 | if (typeof user !== 'string' || typeof pass !== 'string') {
|
886 | throw new h54sError('argumentError', 'User and pass parameters must be strings');
|
887 | }
|
888 | //NOTE: callback optional?
|
889 | if (!callback || typeof callback !== 'function') {
|
890 | throw new h54sError('argumentError', 'You must provide callback');
|
891 | }
|
892 |
|
893 | if (!this.RESTauth) {
|
894 | handleSasLogon.call(this, user, pass, callback);
|
895 | } else {
|
896 | handleRestLogon.call(this, user, pass, callback);
|
897 | }
|
898 | };
|
899 |
|
900 | /**
|
901 | * ManagedRequest method
|
902 | *
|
903 | * @param {string} callMethod - get, post,
|
904 | * @param {string} _url - URL to make request to
|
905 | * @param {object} options - callback function as callback paramter in options object is required
|
906 | *
|
907 | */
|
908 | module.exports.managedRequest = function (callMethod = 'get', _url, options = {
|
909 | callback: () => console.log('Missing callback function')
|
910 | }) {
|
911 | const self = this;
|
912 | const csrf = this.csrf;
|
913 | let retryCount = 0;
|
914 | const {useMultipartFormData, sasProgram, dataObj, params, callback, headers} = options
|
915 |
|
916 | if (sasProgram) {
|
917 | return self.call(sasProgram, dataObj, callback, params)
|
918 | }
|
919 |
|
920 | let url = _url
|
921 | if (!_url.startsWith('http')) {
|
922 | url = self.hostUrl + _url
|
923 | }
|
924 |
|
925 | const _headers = Object.assign({}, headers, {
|
926 | 'X-CSRF-TOKEN': csrf
|
927 | })
|
928 | const _options = Object.assign({}, options, {
|
929 | headers: _headers
|
930 | })
|
931 |
|
932 | if (this._disableCalls) {
|
933 | this._customPendingCalls.push({
|
934 | callMethod,
|
935 | _url,
|
936 | options: _options
|
937 | });
|
938 | return;
|
939 | }
|
940 |
|
941 | self._ajax[callMethod](url, params, useMultipartFormData, _headers).success(function (res) {
|
942 | if (self._utils.needToLogin.call(self, res)) {
|
943 | //remember the call for latter use
|
944 | self._customPendingCalls.push({
|
945 | callMethod,
|
946 | _url,
|
947 | options: _options
|
948 | });
|
949 |
|
950 | //there's no need to continue if previous call returned login error
|
951 | if (self._disableCalls) {
|
952 | return;
|
953 | } else {
|
954 | self._disableCalls = true;
|
955 | }
|
956 |
|
957 | callback(new h54sError('notLoggedinError', 'You are not logged in'));
|
958 | } else {
|
959 | let resObj, err;
|
960 | let done = false;
|
961 |
|
962 | try {
|
963 | const arr = res.getAllResponseHeaders().split('\r\n');
|
964 | const resHeaders = arr.reduce(function (acc, current, i) {
|
965 | let parts = current.split(': ');
|
966 | acc[parts[0]] = parts[1];
|
967 | return acc;
|
968 | }, {});
|
969 | let body = res.responseText
|
970 | try {
|
971 | body = JSON.parse(body)
|
972 | } catch (e) {
|
973 | console.log('response is not JSON string')
|
974 | } finally {
|
975 | resObj = Object.assign({}, {
|
976 | headers: resHeaders,
|
977 | status: res.status,
|
978 | statusText: res.statusText,
|
979 | body
|
980 | })
|
981 | done = true;
|
982 | }
|
983 | } catch (e) {
|
984 | err = new h54sError('unknownError', e.message);
|
985 | err.stack = e.stack;
|
986 | done = true;
|
987 |
|
988 | } finally {
|
989 | if (done) {
|
990 | callback(err, resObj)
|
991 | }
|
992 | }
|
993 | }
|
994 | }).error(function (res) {
|
995 | let _csrf
|
996 | if (res.status == 449 || (res.status == 403 && (res.responseText.includes('_csrf') || res.getResponseHeader('X-Forbidden-Reason') === 'CSRF') && (_csrf = res.getResponseHeader(res.getResponseHeader('X-CSRF-HEADER'))))) {
|
997 | self.csrf = _csrf
|
998 | const _headers = Object.assign({}, headers, {[res.getResponseHeader('X-CSRF-HEADER')]: _csrf})
|
999 | if (retryCount < self.maxXhrRetries) {
|
1000 | self._ajax[callMethod](url, params, useMultipartFormData, _headers).success(this.success).error(this.error);
|
1001 | retryCount++;
|
1002 | } else {
|
1003 | callback(new h54sError('parseError', 'Unable to parse response json'));
|
1004 | }
|
1005 | } else {
|
1006 | logs.addApplicationLog('Managed request failed with status: ' + res.status, _url);
|
1007 | // if request has error text else callback
|
1008 | callback(new h54sError('httpError', res.responseText, res.status));
|
1009 | }
|
1010 | });
|
1011 | }
|
1012 |
|
1013 | /**
|
1014 | * Log on to SAS if we are asked to
|
1015 | * @param {String} user - Username of user
|
1016 | * @param {String} pass - Password of user
|
1017 | * @param {function} callback - what to do after
|
1018 | */
|
1019 | function handleSasLogon(user, pass, callback) {
|
1020 | const self = this;
|
1021 |
|
1022 | const loginParams = {
|
1023 | _service: 'default',
|
1024 | //for SAS 9.4,
|
1025 | username: user,
|
1026 | password: pass
|
1027 | };
|
1028 |
|
1029 | for (let key in this._aditionalLoginParams) {
|
1030 | loginParams[key] = this._aditionalLoginParams[key];
|
1031 | }
|
1032 |
|
1033 | this._loginAttempts = 0;
|
1034 |
|
1035 | this._ajax.post(this.loginUrl, loginParams)
|
1036 | .success(handleSasLogonSuccess)
|
1037 | .error(handleSasLogonError);
|
1038 |
|
1039 | function handleSasLogonError(res) {
|
1040 | if (res.status == 449) {
|
1041 | handleSasLogonSuccess(res);
|
1042 | return;
|
1043 | }
|
1044 |
|
1045 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1046 | callback(res.status);
|
1047 | }
|
1048 |
|
1049 | function handleSasLogonSuccess(res) {
|
1050 | if (++self._loginAttempts === 3) {
|
1051 | return callback(-2);
|
1052 | }
|
1053 | if (self._utils.needToLogin.call(self, res)) {
|
1054 | //we are getting form again after redirect
|
1055 | //and need to login again using the new url
|
1056 | //_loginChanged is set in needToLogin function
|
1057 | //but if login url is not different, we are checking if there are aditional parameters
|
1058 | if (self._loginChanged || (self._isNewLoginPage && !self._aditionalLoginParams)) {
|
1059 | delete self._loginChanged;
|
1060 | const inputs = res.responseText.match(/<input.*"hidden"[^>]*>/g);
|
1061 | if (inputs) {
|
1062 | inputs.forEach(function (inputStr) {
|
1063 | const valueMatch = inputStr.match(/name="([^"]*)"\svalue="([^"]*)/);
|
1064 | loginParams[valueMatch[1]] = valueMatch[2];
|
1065 | });
|
1066 | }
|
1067 | self._ajax.post(self.loginUrl, loginParams).success(function () {
|
1068 | //we need this get request because of the sas 9.4 security checks
|
1069 | self._ajax.get(self.url).success(handleSasLogonSuccess).error(handleSasLogonError);
|
1070 | }).error(handleSasLogonError);
|
1071 | }
|
1072 | else {
|
1073 | //getting form again, but it wasn't a redirect
|
1074 | logs.addApplicationLog('Wrong username or password');
|
1075 | callback(-1);
|
1076 | }
|
1077 | }
|
1078 | else {
|
1079 | self._disableCalls = false;
|
1080 | callback(res.status);
|
1081 | while (self._pendingCalls.length > 0) {
|
1082 | const pendingCall = self._pendingCalls.shift();
|
1083 | const method = pendingCall.method || self.call.bind(self);
|
1084 | const sasProgram = pendingCall.options.sasProgram;
|
1085 | const callbackPending = pendingCall.options.callback;
|
1086 | const params = pendingCall.params;
|
1087 | //update debug because it may change in the meantime
|
1088 | params._debug = self.debug ? 131 : 0;
|
1089 | if (self.retryAfterLogin) {
|
1090 | method(sasProgram, null, callbackPending, params);
|
1091 | }
|
1092 | }
|
1093 | }
|
1094 | }
|
1095 | }
|
1096 | /**
|
1097 | * REST logon for 9.4 v1 ticket based auth
|
1098 | * @param {String} user -
|
1099 | * @param {String} pass
|
1100 | * @param {function} callback
|
1101 | */
|
1102 | function handleRestLogon(user, pass, callback) {
|
1103 | const self = this;
|
1104 |
|
1105 | const loginParams = {
|
1106 | username: user,
|
1107 | password: pass
|
1108 | };
|
1109 |
|
1110 | this._ajax.post(this.RESTauthLoginUrl, loginParams).success(function (res) {
|
1111 | const location = res.getResponseHeader('Location');
|
1112 |
|
1113 | self._ajax.post(location, {
|
1114 | service: self.url
|
1115 | }).success(function (res) {
|
1116 | if (self.url.indexOf('?') === -1) {
|
1117 | self.url += '?ticket=' + res.responseText;
|
1118 | } else {
|
1119 | if (self.url.indexOf('ticket') !== -1) {
|
1120 | self.url = self.url.replace(/ticket=[^&]+/, 'ticket=' + res.responseText);
|
1121 | } else {
|
1122 | self.url += '&ticket=' + res.responseText;
|
1123 | }
|
1124 | }
|
1125 |
|
1126 | callback(res.status);
|
1127 | }).error(function (res) {
|
1128 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1129 | callback(res.status);
|
1130 | });
|
1131 | }).error(function (res) {
|
1132 | if (res.responseText === 'error.authentication.credentials.bad') {
|
1133 | callback(-1);
|
1134 | } else {
|
1135 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1136 | callback(res.status);
|
1137 | }
|
1138 | });
|
1139 | }
|
1140 |
|
1141 | /**
|
1142 | * Logout method
|
1143 | *
|
1144 | * @param {function} callback - Callback function called when logout is done
|
1145 | *
|
1146 | */
|
1147 |
|
1148 | module.exports.logout = function (callback) {
|
1149 | const baseUrl = this.hostUrl || '';
|
1150 | const url = baseUrl + this.logoutUrl;
|
1151 |
|
1152 | this._ajax.get(url).success(function (res) {
|
1153 | this._disableCalls = true
|
1154 | callback();
|
1155 | }).error(function (res) {
|
1156 | logs.addApplicationLog('Logout failed with status code: ' + res.status);
|
1157 | callback(res.status);
|
1158 | });
|
1159 | };
|
1160 |
|
1161 | /*
|
1162 | * Enter debug mode
|
1163 | *
|
1164 | */
|
1165 | module.exports.setDebugMode = function () {
|
1166 | this.debug = true;
|
1167 | };
|
1168 |
|
1169 | /*
|
1170 | * Exit debug mode and clear logs
|
1171 | *
|
1172 | */
|
1173 | module.exports.unsetDebugMode = function () {
|
1174 | this.debug = false;
|
1175 | };
|
1176 |
|
1177 | for (let key in logs.get) {
|
1178 | if (logs.get.hasOwnProperty(key)) {
|
1179 | module.exports[key] = logs.get[key];
|
1180 | }
|
1181 | }
|
1182 |
|
1183 | for (let key in logs.clear) {
|
1184 | if (logs.clear.hasOwnProperty(key)) {
|
1185 | module.exports[key] = logs.clear[key];
|
1186 | }
|
1187 | }
|
1188 |
|
1189 | /*
|
1190 | * Add callback functions executed when properties are updated with remote config
|
1191 | *
|
1192 | *@callback - callback pushed to array
|
1193 | *
|
1194 | */
|
1195 | module.exports.onRemoteConfigUpdate = function (callback) {
|
1196 | this.remoteConfigUpdateCallbacks.push(callback);
|
1197 | };
|
1198 |
|
1199 | module.exports._utils = require('./utils.js');
|
1200 |
|
1201 | /**
|
1202 | * Login call which returns a promise
|
1203 | * @param {String} user - Username
|
1204 | * @param {String} pass - Password
|
1205 | */
|
1206 | module.exports.promiseLogin = function (user, pass) {
|
1207 | return new Promise((resolve, reject) => {
|
1208 | if (!user || !pass) {
|
1209 | reject(new h54sError('argumentError', 'Credentials not set'))
|
1210 | }
|
1211 | if (typeof user !== 'string' || typeof pass !== 'string') {
|
1212 | reject(new h54sError('argumentError', 'User and pass parameters must be strings'))
|
1213 | }
|
1214 | if (!this.RESTauth) {
|
1215 | customHandleSasLogon.call(this, user, pass, resolve);
|
1216 | } else {
|
1217 | customHandleRestLogon.call(this, user, pass, resolve);
|
1218 | }
|
1219 | })
|
1220 | }
|
1221 |
|
1222 | /**
|
1223 | *
|
1224 | * @param {String} user - Username
|
1225 | * @param {String} pass - Password
|
1226 | * @param {function} callback - function to call when successful
|
1227 | */
|
1228 | function customHandleSasLogon(user, pass, callback) {
|
1229 | const self = this;
|
1230 | let loginParams = {
|
1231 | _service: 'default',
|
1232 | //for SAS 9.4,
|
1233 | username: user,
|
1234 | password: pass
|
1235 | };
|
1236 |
|
1237 | for (let key in this._aditionalLoginParams) {
|
1238 | loginParams[key] = this._aditionalLoginParams[key];
|
1239 | }
|
1240 |
|
1241 | this._loginAttempts = 0;
|
1242 | loginParams = this._ajax.serialize(loginParams)
|
1243 |
|
1244 | this._ajax.post(this.loginUrl, loginParams)
|
1245 | .success(handleSasLogonSuccess)
|
1246 | .error(handleSasLogonError);
|
1247 |
|
1248 | function handleSasLogonError(res) {
|
1249 | if (res.status == 449) {
|
1250 | handleSasLogonSuccess(res);
|
1251 | // resolve(res.status);
|
1252 | } else {
|
1253 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1254 | callback(res.status);
|
1255 | }
|
1256 | }
|
1257 |
|
1258 | function handleSasLogonSuccess(res) {
|
1259 | if (++self._loginAttempts === 3) {
|
1260 | callback(-2);
|
1261 | }
|
1262 |
|
1263 | if (self._utils.needToLogin.call(self, res)) {
|
1264 | //we are getting form again after redirect
|
1265 | //and need to login again using the new url
|
1266 | //_loginChanged is set in needToLogin function
|
1267 | //but if login url is not different, we are checking if there are aditional parameters
|
1268 | if (self._loginChanged || (self._isNewLoginPage && !self._aditionalLoginParams)) {
|
1269 | delete self._loginChanged;
|
1270 | const inputs = res.responseText.match(/<input.*"hidden"[^>]*>/g);
|
1271 | if (inputs) {
|
1272 | inputs.forEach(function (inputStr) {
|
1273 | const valueMatch = inputStr.match(/name="([^"]*)"\svalue="([^"]*)/);
|
1274 | loginParams[valueMatch[1]] = valueMatch[2];
|
1275 | });
|
1276 | }
|
1277 | self._ajax.post(self.loginUrl, loginParams).success(function () {
|
1278 | handleSasLogonSuccess()
|
1279 | }).error(handleSasLogonError);
|
1280 | }
|
1281 | else {
|
1282 | //getting form again, but it wasn't a redirect
|
1283 | logs.addApplicationLog('Wrong username or password');
|
1284 | callback(-1);
|
1285 | }
|
1286 | }
|
1287 | else {
|
1288 | self._disableCalls = false;
|
1289 | callback(res.status);
|
1290 | while (self._customPendingCalls.length > 0) {
|
1291 | const pendingCall = self._customPendingCalls.shift()
|
1292 | const method = pendingCall.method || self.managedRequest.bind(self);
|
1293 | const callMethod = pendingCall.callMethod
|
1294 | const _url = pendingCall._url
|
1295 | const options = pendingCall.options;
|
1296 | //update debug because it may change in the meantime
|
1297 | if (options.params) {
|
1298 | options.params._debug = self.debug ? 131 : 0;
|
1299 | }
|
1300 | if (self.retryAfterLogin) {
|
1301 | method(callMethod, _url, options);
|
1302 | }
|
1303 | }
|
1304 |
|
1305 | while (self._pendingCalls.length > 0) {
|
1306 | const pendingCall = self._pendingCalls.shift();
|
1307 | const method = pendingCall.method || self.call.bind(self);
|
1308 | const sasProgram = pendingCall.options.sasProgram;
|
1309 | const callbackPending = pendingCall.options.callback;
|
1310 | const params = pendingCall.params;
|
1311 | //update debug because it may change in the meantime
|
1312 | params._debug = self.debug ? 131 : 0;
|
1313 | if (self.retryAfterLogin) {
|
1314 | method(sasProgram, null, callbackPending, params);
|
1315 | }
|
1316 | }
|
1317 | }
|
1318 | };
|
1319 | }
|
1320 |
|
1321 | /**
|
1322 | * To be used with future managed metadata calls
|
1323 | * @param {String} user - Username
|
1324 | * @param {String} pass - Password
|
1325 | * @param {function} callback - what to call after
|
1326 | * @param {String} callbackUrl - where to navigate after getting ticket
|
1327 | */
|
1328 | function customHandleRestLogon(user, pass, callback, callbackUrl) {
|
1329 | const self = this;
|
1330 |
|
1331 | const loginParams = {
|
1332 | username: user,
|
1333 | password: pass
|
1334 | };
|
1335 |
|
1336 | this._ajax.post(this.RESTauthLoginUrl, loginParams).success(function (res) {
|
1337 | const location = res.getResponseHeader('Location');
|
1338 |
|
1339 | self._ajax.post(location, {
|
1340 | service: callbackUrl
|
1341 | }).success(function (res) {
|
1342 | if (callbackUrl.indexOf('?') === -1) {
|
1343 | callbackUrl += '?ticket=' + res.responseText;
|
1344 | } else {
|
1345 | if (callbackUrl.indexOf('ticket') !== -1) {
|
1346 | callbackUrl = callbackUrl.replace(/ticket=[^&]+/, 'ticket=' + res.responseText);
|
1347 | } else {
|
1348 | callbackUrl += '&ticket=' + res.responseText;
|
1349 | }
|
1350 | }
|
1351 |
|
1352 | callback(res.status);
|
1353 | }).error(function (res) {
|
1354 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1355 | callback(res.status);
|
1356 | });
|
1357 | }).error(function (res) {
|
1358 | if (res.responseText === 'error.authentication.credentials.bad') {
|
1359 | callback(-1);
|
1360 | } else {
|
1361 | logs.addApplicationLog('Login failed with status code: ' + res.status);
|
1362 | callback(res.status);
|
1363 | }
|
1364 | });
|
1365 | }
|
1366 |
|
1367 |
|
1368 | // Utilility functions for handling files and folders on VIYA
|
1369 | /**
|
1370 | * Returns the details of a folder from folder service
|
1371 | * @param {String} folderName - Full path of folder to be found
|
1372 | * @param {Object} options - Options object for managedRequest
|
1373 | */
|
1374 | module.exports.getFolderDetails = function (folderName, options) {
|
1375 | // First call to get folder's id
|
1376 | let url = "/folders/folders/@item?path=" + folderName
|
1377 | return this.managedRequest('get', url, options);
|
1378 | }
|
1379 |
|
1380 | /**
|
1381 | * Returns the details of a file from files service
|
1382 | * @param {String} fileUri - Full path of file to be found
|
1383 | * @param {Object} options - Options object for managedRequest: cacheBust forces browser to fetch new file
|
1384 | */
|
1385 | module.exports.getFileDetails = function (fileUri, options) {
|
1386 | const cacheBust = options.cacheBust
|
1387 | if (cacheBust) {
|
1388 | fileUri += '?cacheBust=' + new Date().getTime()
|
1389 | }
|
1390 | return this.managedRequest('get', fileUri, options);
|
1391 | }
|
1392 |
|
1393 | /**
|
1394 | * Returns the contents of a file from files service
|
1395 | * @param {String} fileUri - Full path of file to be downloaded
|
1396 | * @param {Object} options - Options object for managedRequest: cacheBust forces browser to fetch new file
|
1397 | */
|
1398 | module.exports.getFileContent = function (fileUri, options) {
|
1399 | const cacheBust = options.cacheBust
|
1400 | let uri = fileUri + '/content'
|
1401 | if (cacheBust) {
|
1402 | uri += '?cacheBust=' + new Date().getTime()
|
1403 | }
|
1404 | return this.managedRequest('get', uri, options);
|
1405 | }
|
1406 |
|
1407 |
|
1408 | // Util functions for working with files and folders
|
1409 | /**
|
1410 | * Returns details about folder it self and it's members with details
|
1411 | * @param {String} folderName - Full path of folder to be found
|
1412 | * @param {Object} options - Options object for managedRequest
|
1413 | */
|
1414 | module.exports.getFolderContents = async function (folderName, options) {
|
1415 | const self = this
|
1416 | const {callback} = options
|
1417 |
|
1418 | // Second call to get folder's memebers
|
1419 | const _callback = (err, data) => {
|
1420 | // handle error of the first call
|
1421 | if(err) {
|
1422 | callback(err, data)
|
1423 | return
|
1424 | }
|
1425 | let id = data.body.id
|
1426 | let membersUrl = '/folders/folders/' + id + '/members' + '/?limit=10000000';
|
1427 | return self.managedRequest('get', membersUrl, {callback})
|
1428 | }
|
1429 |
|
1430 | // First call to get folder's id
|
1431 | let url = "/folders/folders/@item?path=" + folderName
|
1432 | const optionsObj = Object.assign({}, options, {
|
1433 | callback: _callback
|
1434 | })
|
1435 | this.managedRequest('get', url, optionsObj)
|
1436 | }
|
1437 |
|
1438 | /**
|
1439 | * Creates a folder
|
1440 | * @param {String} parentUri - The uri of the folder where the new child is being created
|
1441 | * @param {String} folderName - Full path of folder to be found
|
1442 | * @param {Object} options - Options object for managedRequest
|
1443 | */
|
1444 | module.exports.createNewFolder = function (parentUri, folderName, options) {
|
1445 | const headers = {
|
1446 | 'Accept': 'application/json, text/javascript, */*; q=0.01',
|
1447 | 'Content-Type': 'application/json',
|
1448 | }
|
1449 |
|
1450 | const url = '/folders/folders?parentFolderUri=' + parentUri;
|
1451 | const data = {
|
1452 | 'name': folderName,
|
1453 | 'type': "folder"
|
1454 | }
|
1455 |
|
1456 | const optionsObj = Object.assign({}, options, {
|
1457 | params: JSON.stringify(data),
|
1458 | headers,
|
1459 | useMultipartFormData: false
|
1460 | })
|
1461 |
|
1462 | return this.managedRequest('post', url, optionsObj);
|
1463 | }
|
1464 |
|
1465 | /**
|
1466 | * Deletes a folder
|
1467 | * @param {String} folderId - Full URI of folder to be deleted
|
1468 | * @param {Object} options - Options object for managedRequest
|
1469 | */
|
1470 | module.exports.deleteFolderById = function (folderId, options) {
|
1471 | const url = '/folders/folders/' + folderId;
|
1472 | return this.managedRequest('delete', url, options)
|
1473 | }
|
1474 |
|
1475 | /**
|
1476 | * Creates a new file
|
1477 | * @param {String} fileName - Name of the file being created
|
1478 | * @param {String} fileBlob - Content of the file
|
1479 | * @param {String} parentFOlderUri - URI of the parent folder where the file is to be created
|
1480 | * @param {Object} options - Options object for managedRequest
|
1481 | */
|
1482 | module.exports.createNewFile = function (fileName, fileBlob, parentFolderUri, options) {
|
1483 | let url = "/files/files#multipartUpload";
|
1484 | let dataObj = {
|
1485 | file: [fileBlob, fileName],
|
1486 | parentFolderUri
|
1487 | }
|
1488 |
|
1489 | const optionsObj = Object.assign({}, options, {
|
1490 | params: dataObj,
|
1491 | useMultipartFormData: true,
|
1492 | })
|
1493 | return this.managedRequest('post', url, optionsObj);
|
1494 | }
|
1495 |
|
1496 | /**
|
1497 | * Generic delete function that deletes by URI
|
1498 | * @param {String} itemUri - Name of the item being deleted
|
1499 | * @param {Object} options - Options object for managedRequest
|
1500 | */
|
1501 | module.exports.deleteItem = function (itemUri, options) {
|
1502 | return this.managedRequest('delete', itemUri, options)
|
1503 | }
|
1504 |
|
1505 |
|
1506 | /**
|
1507 | * Updates contents of a file
|
1508 | * @param {String} fileName - Name of the file being updated
|
1509 | * @param {Object | Blob} dataObj - New content of the file (Object must contain file key)
|
1510 | * Object example {
|
1511 | * file: [<blob>, <fileName>]
|
1512 | * }
|
1513 | * @param {String} lastModified - the last-modified header string that matches that of file being overwritten
|
1514 | * @param {Object} options - Options object for managedRequest
|
1515 | */
|
1516 | module.exports.updateFile = function (itemUri, dataObj, lastModified, options) {
|
1517 | const url = itemUri + '/content'
|
1518 | console.log('URL', url)
|
1519 | let headers = {
|
1520 | 'Content-Type': 'application/vnd.sas.file',
|
1521 | 'If-Unmodified-Since': lastModified
|
1522 | }
|
1523 | const isBlob = dataObj instanceof Blob
|
1524 | const useMultipartFormData = !isBlob // set useMultipartFormData to true if dataObj is not Blob
|
1525 |
|
1526 | const optionsObj = Object.assign({}, options, {
|
1527 | params: dataObj,
|
1528 | headers,
|
1529 | useMultipartFormData
|
1530 | })
|
1531 | return this.managedRequest('put', url, optionsObj);
|
1532 | }
|
1533 |
|
1534 | /**
|
1535 | Updates file Metadata
|
1536 | * @param {String} fileName - Name of the file being updated
|
1537 | * @param {String} lastModified - the last-modified header string that matches that of file being updated
|
1538 | * @param {Object | Blob} dataObj - objects containing the fields that are being changed
|
1539 | * @param {Object} options - Options object for managedRequest
|
1540 | */
|
1541 | module.exports.updateFileMetadata = function (itemUri, dataObj, lastModified, options) {
|
1542 | let headers = {
|
1543 | 'Content-Type':'application/vnd.sas.file+json',
|
1544 | 'If-Unmodified-Since': lastModified
|
1545 | }
|
1546 | const isBlob = dataObj instanceof Blob
|
1547 | const useMultipartFormData = !isBlob // set useMultipartFormData to true if dataObj is not Blob
|
1548 |
|
1549 | const optionsObj = Object.assign({}, options, {
|
1550 | params: dataObj,
|
1551 | headers,
|
1552 | useMultipartFormData
|
1553 | })
|
1554 |
|
1555 | return this.managedRequest('patch', itemUri, optionsObj);
|
1556 | }
|
1557 |
|
1558 | /**
|
1559 | * Updates folder info
|
1560 | * @param {String} folderUri - uri of the folder that is being changed
|
1561 | * @param {String} lastModified - the last-modified header string that matches that of the folder being updated
|
1562 | * @param {Object | Blob} dataObj - object thats is either the whole folder or partial data
|
1563 | * @param {Object} options - Options object for managedRequest
|
1564 | */
|
1565 | module.exports.updateFolderMetadata = function (folderUri, dataObj, lastModified, options) {
|
1566 |
|
1567 | /**
|
1568 | @constant {Boolean} partialData - indicates wether dataObj containts all the data that needs to be send to the server
|
1569 | or partial data which contatins only the fields that need to be updated, in which case a call needs to be made to the server for
|
1570 | the rest of the data before the update can be done
|
1571 | */
|
1572 | const {partialData} = options;
|
1573 |
|
1574 | const headers = {
|
1575 | 'Content-Type': "application/vnd.sas.content.folder+json",
|
1576 | 'If-Unmodified-Since': lastModified,
|
1577 | }
|
1578 |
|
1579 | if (partialData) {
|
1580 |
|
1581 | const _callback = (err, res) => {
|
1582 | if (res) {
|
1583 |
|
1584 | const folder = Object.assign({}, res.body, dataObj);
|
1585 |
|
1586 | let forBlob = JSON.stringify(folder);
|
1587 | let data = new Blob([forBlob], {type: "octet/stream"});
|
1588 |
|
1589 | const optionsObj = Object.assign({}, options, {
|
1590 | params: data,
|
1591 | headers,
|
1592 | useMultipartFormData : false,
|
1593 | })
|
1594 |
|
1595 | return this.managedRequest('put', folderUri, optionsObj);
|
1596 | }
|
1597 |
|
1598 | return options.callback(err);
|
1599 | }
|
1600 | const getOptionsObj = Object.assign({}, options, {
|
1601 | headers: {'Content-Type': "application/vnd.sas.content.folder+json"},
|
1602 | callback: _callback
|
1603 | })
|
1604 |
|
1605 | return this.managedRequest('get', folderUri, getOptionsObj);
|
1606 | }
|
1607 | else {
|
1608 | if ( !(dataObj instanceof Blob)) {
|
1609 | let forBlob = JSON.stringify(dataObj);
|
1610 | dataObj = new Blob([forBlob], {type: "octet/stream"});
|
1611 | }
|
1612 |
|
1613 | const optionsObj = Object.assign({}, options, {
|
1614 | params: dataObj,
|
1615 | headers,
|
1616 | useMultipartFormData : false,
|
1617 | })
|
1618 | return this.managedRequest('put', folderUri, optionsObj);
|
1619 | }
|
1620 | }
|
1621 | },{"../error.js":1,"../files":2,"../logs.js":5,"../sasData.js":9,"../tables":10,"./utils.js":8}],8:[function(require,module,exports){
|
1622 | const logs = require('../logs.js');
|
1623 | const h54sError = require('../error.js');
|
1624 |
|
1625 | const programNotFoundPatt = /<title>(Stored Process Error|SASStoredProcess)<\/title>[\s\S]*<h2>(Stored process not found:.*|.*not a valid stored process path.)<\/h2>/;
|
1626 | const badJobDefinition = "<h2>Parameter Error <br/>Unable to get job definition.</h2>";
|
1627 |
|
1628 | const responseReplace = function(res) {
|
1629 | return res
|
1630 | };
|
1631 |
|
1632 | /**
|
1633 | * Parse response from server
|
1634 | *
|
1635 | * @param {object} responseText - response html from the server
|
1636 | * @param {string} sasProgram - sas program path
|
1637 | * @param {object} params - params sent to sas program with addTable
|
1638 | *
|
1639 | */
|
1640 | module.exports.parseRes = function(responseText, sasProgram, params) {
|
1641 | const matches = responseText.match(programNotFoundPatt);
|
1642 | if(matches) {
|
1643 | throw new h54sError('programNotFound', 'You have not been granted permission to perform this action, or the STP is missing.');
|
1644 | }
|
1645 | //remove new lines in json response
|
1646 | //replace \\(d) with \(d) - SAS json parser is escaping it
|
1647 | return JSON.parse(responseReplace(responseText));
|
1648 | };
|
1649 |
|
1650 | /**
|
1651 | * Parse response from server in debug mode
|
1652 | *
|
1653 | * @param {object} responseText - response html from the server
|
1654 | * @param {string} sasProgram - sas program path
|
1655 | * @param {object} params - params sent to sas program with addTable
|
1656 | * @param {string} hostUrl - same as in h54s constructor
|
1657 | * @param {bool} isViya - same as in h54s constructor
|
1658 | *
|
1659 | */
|
1660 | module.exports.parseDebugRes = function (responseText, sasProgram, params, hostUrl, isViya) {
|
1661 | const self = this
|
1662 | let matches = responseText.match(programNotFoundPatt);
|
1663 | if (matches) {
|
1664 | throw new h54sError('programNotFound', 'Sas program completed with errors');
|
1665 | }
|
1666 |
|
1667 | if (isViya) {
|
1668 | const matchesWrongJob = responseText.match(badJobDefinition);
|
1669 | if (matchesWrongJob) {
|
1670 | throw new h54sError('programNotFound', 'Sas program completed with errors. Unable to get job definition.');
|
1671 | }
|
1672 | }
|
1673 |
|
1674 | //find json
|
1675 | let patt = isViya ? /^(.?<iframe.*src=")([^"]+)(.*iframe>)/m : /^(.?--h54s-data-start--)([\S\s]*?)(--h54s-data-end--)/m;
|
1676 | matches = responseText.match(patt);
|
1677 |
|
1678 | const page = responseText.replace(patt, '');
|
1679 | const htmlBodyPatt = /<body.*>([\s\S]*)<\/body>/;
|
1680 | const bodyMatches = page.match(htmlBodyPatt);
|
1681 | //remove html tags
|
1682 | let debugText = bodyMatches[1].replace(/<[^>]*>/g, '');
|
1683 | debugText = this.decodeHTMLEntities(debugText);
|
1684 |
|
1685 | logs.addDebugData(bodyMatches[1], debugText, sasProgram, params);
|
1686 |
|
1687 | if (isViya && this.parseErrorResponse(responseText, sasProgram)) {
|
1688 | throw new h54sError('sasError', 'Sas program completed with errors');
|
1689 | }
|
1690 | if (!matches) {
|
1691 | throw new h54sError('parseError', 'Unable to parse response json');
|
1692 | }
|
1693 |
|
1694 |
|
1695 | const promise = new Promise(function (resolve, reject) {
|
1696 | let jsonObj
|
1697 | if (isViya) {
|
1698 | const xhr = new XMLHttpRequest();
|
1699 | const baseUrl = hostUrl || "";
|
1700 | xhr.open("GET", baseUrl + matches[2]);
|
1701 | xhr.onload = function () {
|
1702 | if (this.status >= 200 && this.status < 300) {
|
1703 | resolve(JSON.parse(xhr.responseText.replace(/(\r\n|\r|\n)/g, '')));
|
1704 | } else {
|
1705 | reject(new h54sError('fetchError', xhr.statusText, this.status))
|
1706 | }
|
1707 | };
|
1708 | xhr.onerror = function () {
|
1709 | reject(new h54sError('fetchError', xhr.statusText))
|
1710 | };
|
1711 | xhr.send();
|
1712 | } else {
|
1713 | try {
|
1714 | jsonObj = JSON.parse(responseReplace(matches[2]));
|
1715 | } catch (e) {
|
1716 | reject(new h54sError('parseError', 'Unable to parse response json'))
|
1717 | }
|
1718 |
|
1719 | if (jsonObj && jsonObj.h54sAbort) {
|
1720 | resolve(jsonObj);
|
1721 | } else if (self.parseErrorResponse(responseText, sasProgram)) {
|
1722 | reject(new h54sError('sasError', 'Sas program completed with errors'))
|
1723 | } else {
|
1724 | resolve(jsonObj);
|
1725 | }
|
1726 | }
|
1727 | });
|
1728 |
|
1729 | return promise;
|
1730 | };
|
1731 |
|
1732 | /**
|
1733 | * Add failed response to logs - used only if debug=false
|
1734 | *
|
1735 | * @param {string} responseText - response html from the server
|
1736 | * @param {string} sasProgram - sas program path
|
1737 | *
|
1738 | */
|
1739 | module.exports.addFailedResponse = function(responseText, sasProgram) {
|
1740 | const patt = /<script([\s\S]*)\/form>/;
|
1741 | const patt2 = /display\s?:\s?none;?\s?/;
|
1742 | //remove script with form for toggling the logs and "display:none" from style
|
1743 | responseText = responseText.replace(patt, '').replace(patt2, '');
|
1744 | let debugText = responseText.replace(/<[^>]*>/g, '');
|
1745 | debugText = this.decodeHTMLEntities(debugText);
|
1746 |
|
1747 | logs.addFailedRequest(responseText, debugText, sasProgram);
|
1748 | };
|
1749 |
|
1750 | /**
|
1751 | * Unescape all string values in returned object
|
1752 | *
|
1753 | * @param {object} obj
|
1754 | *
|
1755 | */
|
1756 | module.exports.unescapeValues = function(obj) {
|
1757 | for (let key in obj) {
|
1758 | if (typeof obj[key] === 'string') {
|
1759 | obj[key] = decodeURIComponent(obj[key]);
|
1760 | } else if(typeof obj === 'object') {
|
1761 | this.unescapeValues(obj[key]);
|
1762 | }
|
1763 | }
|
1764 | return obj;
|
1765 | };
|
1766 |
|
1767 | /**
|
1768 | * Parse error response from server and save errors in memory
|
1769 | *
|
1770 | * @param {string} res - server response
|
1771 | * @param {string} sasProgram - sas program which returned the response
|
1772 | *
|
1773 | */
|
1774 | module.exports.parseErrorResponse = function(res, sasProgram) {
|
1775 | //capture 'ERROR: [text].' or 'ERROR xx [text].'
|
1776 | const patt = /^ERROR(:\s|\s\d\d)(.*\.|.*\n.*\.)/gm;
|
1777 | let errors = res.replace(/(<([^>]+)>)/ig, '').match(patt);
|
1778 | if(!errors) {
|
1779 | return;
|
1780 | }
|
1781 |
|
1782 | let errMessage;
|
1783 | for(let i = 0, n = errors.length; i < n; i++) {
|
1784 | errMessage = errors[i].replace(/<[^>]*>/g, '').replace(/(\n|\s{2,})/g, ' ');
|
1785 | errMessage = this.decodeHTMLEntities(errMessage);
|
1786 | errors[i] = {
|
1787 | sasProgram: sasProgram,
|
1788 | message: errMessage,
|
1789 | time: new Date()
|
1790 | };
|
1791 | }
|
1792 |
|
1793 | logs.addSasErrors(errors);
|
1794 |
|
1795 | return true;
|
1796 | };
|
1797 |
|
1798 | /**
|
1799 | * Decode HTML entities - old utility function
|
1800 | *
|
1801 | * @param {string} res - server response
|
1802 | *
|
1803 | */
|
1804 | module.exports.decodeHTMLEntities = function (html) {
|
1805 | const tempElement = document.createElement('span');
|
1806 | let str = html.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z]+);/gi,
|
1807 | function (str) {
|
1808 | tempElement.innerHTML = str;
|
1809 | str = tempElement.textContent || tempElement.innerText;
|
1810 | return str;
|
1811 | }
|
1812 | );
|
1813 | return str;
|
1814 | };
|
1815 |
|
1816 | /**
|
1817 | * Convert sas time to javascript date
|
1818 | *
|
1819 | * @param {number} sasDate - sas Tate object
|
1820 | *
|
1821 | */
|
1822 | module.exports.fromSasDateTime = function (sasDate) {
|
1823 | const basedate = new Date("January 1, 1960 00:00:00");
|
1824 | const currdate = sasDate;
|
1825 |
|
1826 | // offsets for UTC and timezones and BST
|
1827 | const baseOffset = basedate.getTimezoneOffset(); // in minutes
|
1828 |
|
1829 | // convert sas datetime to a current valid javascript date
|
1830 | const basedateMs = basedate.getTime(); // in ms
|
1831 | const currdateMs = currdate * 1000; // to ms
|
1832 | const sasDatetime = currdateMs + basedateMs;
|
1833 | const jsDate = new Date();
|
1834 | jsDate.setTime(sasDatetime); // first time to get offset BST daylight savings etc
|
1835 | const currOffset = jsDate.getTimezoneOffset(); // adjust for offset in minutes
|
1836 | const offsetVar = (baseOffset - currOffset) * 60 * 1000; // difference in milliseconds
|
1837 | const offsetTime = sasDatetime - offsetVar; // finding BST and daylight savings
|
1838 | jsDate.setTime(offsetTime); // update with offset
|
1839 | return jsDate;
|
1840 | };
|
1841 |
|
1842 | /**
|
1843 | * Checks whether response object is a login redirect
|
1844 | * @param {Object} responseObj xhr response to be checked for logon redirect
|
1845 | */
|
1846 | module.exports.needToLogin = function(responseObj) {
|
1847 | const isSASLogon = responseObj.responseURL && responseObj.responseURL.includes('SASLogon')
|
1848 | if (isSASLogon === false) {
|
1849 | return false
|
1850 | }
|
1851 |
|
1852 | const patt = /<form.+action="(.*Logon[^"]*).*>/;
|
1853 | const matches = patt.exec(responseObj.responseText);
|
1854 | let newLoginUrl;
|
1855 |
|
1856 | if(!matches) {
|
1857 | //there's no form, we are in. hooray!
|
1858 | return false;
|
1859 | } else {
|
1860 | const actionUrl = matches[1].replace(/\?.*/, '');
|
1861 | if(actionUrl.charAt(0) === '/') {
|
1862 | newLoginUrl = this.hostUrl ? this.hostUrl + actionUrl : actionUrl;
|
1863 | if(newLoginUrl !== this.loginUrl) {
|
1864 | this._loginChanged = true;
|
1865 | this.loginUrl = newLoginUrl;
|
1866 | }
|
1867 | } else {
|
1868 | //relative path
|
1869 |
|
1870 | const lastIndOfSlash = responseObj.responseURL.lastIndexOf('/') + 1;
|
1871 | //remove everything after the last slash, and everything until the first
|
1872 | const relativeLoginUrl = responseObj.responseURL.substr(0, lastIndOfSlash).replace(/.*\/{2}[^\/]*/, '') + actionUrl;
|
1873 | newLoginUrl = this.hostUrl ? this.hostUrl + relativeLoginUrl : relativeLoginUrl;
|
1874 | if(newLoginUrl !== this.loginUrl) {
|
1875 | this._loginChanged = true;
|
1876 | this.loginUrl = newLoginUrl;
|
1877 | }
|
1878 | }
|
1879 |
|
1880 | //save parameters from hidden form fields
|
1881 | const parser = new DOMParser();
|
1882 | const doc = parser.parseFromString(responseObj.responseText,"text/html");
|
1883 | const res = doc.querySelectorAll("input[type='hidden']");
|
1884 | const hiddenFormParams = {};
|
1885 | if(res) {
|
1886 | //it's new login page if we have these additional parameters
|
1887 | this._isNewLoginPage = true;
|
1888 | res.forEach(function(node) {
|
1889 | hiddenFormParams[node.name] = node.value;
|
1890 | });
|
1891 | this._aditionalLoginParams = hiddenFormParams;
|
1892 | }
|
1893 | return true;
|
1894 | }
|
1895 | };
|
1896 |
|
1897 | /**
|
1898 | * Get full program path from metadata root and relative path
|
1899 | *
|
1900 | * @param {string} metadataRoot - Metadata root (path where all programs for the project are located)
|
1901 | * @param {string} sasProgramPath - Sas program path
|
1902 | *
|
1903 | */
|
1904 | module.exports.getFullProgramPath = function(metadataRoot, sasProgramPath) {
|
1905 | return metadataRoot ? metadataRoot.replace(/\/?$/, '/') + sasProgramPath.replace(/^\//, '') : sasProgramPath;
|
1906 | };
|
1907 |
|
1908 | // Returns object where table rows are groupped by key
|
1909 | module.exports.getObjOfTable = function (table, key, value = null) {
|
1910 | const obj = {}
|
1911 | table.forEach(row => {
|
1912 | if (!obj[row[key]]) {
|
1913 | obj[row[key]] = []
|
1914 | obj[row[key]].push(value ? row[value] : row)
|
1915 | } else {
|
1916 | obj[row[key]].push(value ? row[value] : row)
|
1917 | }
|
1918 | })
|
1919 | return obj
|
1920 | }
|
1921 |
|
1922 | // Returns self uri out of links array
|
1923 | module.exports.getSelfUri = function (links) {
|
1924 | return links
|
1925 | .filter(e => e.rel === 'self')
|
1926 | .map(e => e.uri)
|
1927 | .shift();
|
1928 | }
|
1929 |
|
1930 | },{"../error.js":1,"../logs.js":5}],9:[function(require,module,exports){
|
1931 | const h54sError = require('./error.js');
|
1932 | const logs = require('./logs.js');
|
1933 | const Tables = require('./tables');
|
1934 | const Files = require('./files');
|
1935 | const toSasDateTime = require('./tables/utils.js').toSasDateTime;
|
1936 |
|
1937 | /**
|
1938 | * Checks whether a given table name is a valid SAS macro name
|
1939 | * @param {String} macroName The SAS macro name to be given to this table
|
1940 | */
|
1941 | function validateMacro(macroName) {
|
1942 | if(macroName.length > 32) {
|
1943 | throw new h54sError('argumentError', 'Table name too long. Maximum is 32 characters');
|
1944 | }
|
1945 |
|
1946 | const charCodeAt0 = macroName.charCodeAt(0);
|
1947 | // validate it starts with A-Z, a-z, or _
|
1948 | if((charCodeAt0 < 65 || charCodeAt0 > 90) && (charCodeAt0 < 97 || charCodeAt0 > 122) && macroName[0] !== '_') {
|
1949 | throw new h54sError('argumentError', 'Table name starting with number or special characters');
|
1950 | }
|
1951 |
|
1952 | for(let i = 0; i < macroName.length; i++) {
|
1953 | const charCode = macroName.charCodeAt(i);
|
1954 |
|
1955 | if((charCode < 48 || charCode > 57) &&
|
1956 | (charCode < 65 || charCode > 90) &&
|
1957 | (charCode < 97 || charCode > 122) &&
|
1958 | macroName[i] !== '_')
|
1959 | {
|
1960 | throw new h54sError('argumentError', 'Table name has unsupported characters');
|
1961 | }
|
1962 | }
|
1963 | }
|
1964 |
|
1965 | /**
|
1966 | * h54s SAS data object constructor
|
1967 | * @constructor
|
1968 | *
|
1969 | * @param {array|file} data - Table or file added when object is created
|
1970 | * @param {String} macroName The SAS macro name to be given to this table
|
1971 | * @param {number} parameterThreshold - size of data objects sent to SAS (legacy)
|
1972 | *
|
1973 | */
|
1974 | function SasData(data, macroName, specs) {
|
1975 | if(data instanceof Array) {
|
1976 | this._files = {};
|
1977 | this.addTable(data, macroName, specs);
|
1978 | } else if(data instanceof File || data instanceof Blob) {
|
1979 | Files.call(this, data, macroName);
|
1980 | } else {
|
1981 | throw new h54sError('argumentError', 'Data argument wrong type or missing');
|
1982 | }
|
1983 | }
|
1984 |
|
1985 | /**
|
1986 | * Add table to tables object
|
1987 | * @param {array} table - Array of table objects
|
1988 | * @param {String} macroName The SAS macro name to be given to this table
|
1989 | *
|
1990 | */
|
1991 | SasData.prototype.addTable = function(table, macroName, specs) {
|
1992 | const isSpecsProvided = !!specs;
|
1993 | if(table && macroName) {
|
1994 | if(!(table instanceof Array)) {
|
1995 | throw new h54sError('argumentError', 'First argument must be array');
|
1996 | }
|
1997 | if(typeof macroName !== 'string') {
|
1998 | throw new h54sError('argumentError', 'Second argument must be string');
|
1999 | }
|
2000 |
|
2001 | validateMacro(macroName);
|
2002 | } else {
|
2003 | throw new h54sError('argumentError', 'Missing arguments');
|
2004 | }
|
2005 |
|
2006 | if (typeof table !== 'object' || !(table instanceof Array)) {
|
2007 | throw new h54sError('argumentError', 'Table argument is not an array');
|
2008 | }
|
2009 |
|
2010 | let key;
|
2011 | if(specs) {
|
2012 | if(specs.constructor !== Object) {
|
2013 | throw new h54sError('argumentError', 'Specs data type wrong. Object expected.');
|
2014 | }
|
2015 | for(key in table[0]) {
|
2016 | if(!specs[key]) {
|
2017 | throw new h54sError('argumentError', 'Missing columns in specs data.');
|
2018 | }
|
2019 | }
|
2020 | for(key in specs) {
|
2021 | if(specs[key].constructor !== Object) {
|
2022 | throw new h54sError('argumentError', 'Wrong column descriptor in specs data.');
|
2023 | }
|
2024 | if(!specs[key].colType || !specs[key].colLength) {
|
2025 | throw new h54sError('argumentError', 'Missing columns in specs descriptor.');
|
2026 | }
|
2027 | }
|
2028 | }
|
2029 |
|
2030 | let i, j, //counters used latter in code
|
2031 | row, val, type,
|
2032 | specKeys = [];
|
2033 | const specialChars = ['"', '\\', '/', '\n', '\t', '\f', '\r', '\b'];
|
2034 |
|
2035 | if(!specs) {
|
2036 | specs = {};
|
2037 |
|
2038 | for (i = 0; i < table.length; i++) {
|
2039 | row = table[i];
|
2040 |
|
2041 | if(typeof row !== 'object') {
|
2042 | throw new h54sError('argumentError', 'Table item is not an object');
|
2043 | }
|
2044 |
|
2045 | for(key in row) {
|
2046 | if(row.hasOwnProperty(key)) {
|
2047 | val = row[key];
|
2048 | type = typeof val;
|
2049 |
|
2050 | if(specs[key] === undefined) {
|
2051 | specKeys.push(key);
|
2052 | specs[key] = {};
|
2053 |
|
2054 | if (type === 'number') {
|
2055 | if(val < Number.MIN_SAFE_INTEGER || val > Number.MAX_SAFE_INTEGER) {
|
2056 | logs.addApplicationLog('Object[' + i + '].' + key + ' - This value exceeds expected numeric precision.');
|
2057 | }
|
2058 | specs[key].colType = 'num';
|
2059 | specs[key].colLength = 8;
|
2060 | } else if (type === 'string' && !(val instanceof Date)) { // straightforward string
|
2061 | specs[key].colType = 'string';
|
2062 | specs[key].colLength = val.length;
|
2063 | } else if(val instanceof Date) {
|
2064 | specs[key].colType = 'date';
|
2065 | specs[key].colLength = 8;
|
2066 | } else if (type === 'object') {
|
2067 | specs[key].colType = 'json';
|
2068 | specs[key].colLength = JSON.stringify(val).length;
|
2069 | }
|
2070 | }
|
2071 | }
|
2072 | }
|
2073 | }
|
2074 | } else {
|
2075 | specKeys = Object.keys(specs);
|
2076 | }
|
2077 |
|
2078 | let sasCsv = '';
|
2079 |
|
2080 | // we need two loops - the first one is creating specs and validating
|
2081 | for (i = 0; i < table.length; i++) {
|
2082 | row = table[i];
|
2083 | for(j = 0; j < specKeys.length; j++) {
|
2084 | key = specKeys[j];
|
2085 | if(row.hasOwnProperty(key)) {
|
2086 | val = row[key];
|
2087 | type = typeof val;
|
2088 |
|
2089 | if(type === 'number' && isNaN(val)) {
|
2090 | throw new h54sError('typeError', 'NaN value in one of the values (columns) is not allowed');
|
2091 | }
|
2092 | if(val === -Infinity || val === Infinity) {
|
2093 | throw new h54sError('typeError', val.toString() + ' value in one of the values (columns) is not allowed');
|
2094 | }
|
2095 | if(val === true || val === false) {
|
2096 | throw new h54sError('typeError', 'Boolean value in one of the values (columns) is not allowed');
|
2097 | }
|
2098 | if(type === 'string' && val.indexOf('\r\n') !== -1) {
|
2099 | throw new h54sError('typeError', 'New line character is not supported');
|
2100 | }
|
2101 |
|
2102 | // convert null to '.' for numbers and to '' for strings
|
2103 | if(val === null) {
|
2104 | if(specs[key].colType === 'string') {
|
2105 | val = '';
|
2106 | type = 'string';
|
2107 | } else if(specs[key].colType === 'num') {
|
2108 | val = '.';
|
2109 | type = 'number';
|
2110 | } else {
|
2111 | throw new h54sError('typeError', 'Cannot convert null value');
|
2112 | }
|
2113 | }
|
2114 |
|
2115 |
|
2116 | if ((type === 'number' && specs[key].colType !== 'num' && val !== '.') ||
|
2117 | ((type === 'string' && !(val instanceof Date) && specs[key].colType !== 'string') &&
|
2118 | (type === 'string' && specs[key].colType == 'num' && val !== '.')) ||
|
2119 | (val instanceof Date && specs[key].colType !== 'date') ||
|
2120 | ((type === 'object' && val.constructor !== Date) && specs[key].colType !== 'json'))
|
2121 | {
|
2122 | throw new h54sError('typeError', 'There is a specs type mismatch in the array between values (columns) of the same name.' +
|
2123 | ' type/colType/val = ' + type +'/' + specs[key].colType + '/' + val );
|
2124 | } else if(!isSpecsProvided && type === 'string' && specs[key].colLength < val.length) {
|
2125 | specs[key].colLength = val.length;
|
2126 | } else if((type === 'string' && specs[key].colLength < val.length) || (type !== 'string' && specs[key].colLength !== 8)) {
|
2127 | throw new h54sError('typeError', 'There is a specs length mismatch in the array between values (columns) of the same name.' +
|
2128 | ' type/colType/val = ' + type +'/' + specs[key].colType + '/' + val );
|
2129 | }
|
2130 |
|
2131 | if (val instanceof Date) {
|
2132 | val = toSasDateTime(val);
|
2133 | }
|
2134 |
|
2135 | switch(specs[key].colType) {
|
2136 | case 'num':
|
2137 | case 'date':
|
2138 | sasCsv += val;
|
2139 | break;
|
2140 | case 'string':
|
2141 | sasCsv += '"' + val.replace(/"/g, '""') + '"';
|
2142 | let colLength = val.length;
|
2143 | for(let k = 0; k < val.length; k++) {
|
2144 | if(specialChars.indexOf(val[k]) !== -1) {
|
2145 | colLength++;
|
2146 | } else {
|
2147 | let code = val.charCodeAt(k);
|
2148 | if(code > 0xffff) {
|
2149 | colLength += 3;
|
2150 | } else if(code > 0x7ff) {
|
2151 | colLength += 2;
|
2152 | } else if(code > 0x7f) {
|
2153 | colLength += 1;
|
2154 | }
|
2155 | }
|
2156 | }
|
2157 | // use maximum value between max previous, current value and 1 (first two can be 0 wich is not supported)
|
2158 | specs[key].colLength = Math.max(specs[key].colLength, colLength, 1);
|
2159 | break;
|
2160 | case 'object':
|
2161 | sasCsv += '"' + JSON.stringify(val).replace(/"/g, '""') + '"';
|
2162 | break;
|
2163 | }
|
2164 | }
|
2165 | // do not insert if it's the last column
|
2166 | if(j < specKeys.length - 1) {
|
2167 | sasCsv += ',';
|
2168 | }
|
2169 | }
|
2170 | if(i < table.length - 1) {
|
2171 | sasCsv += '\r\n';
|
2172 | }
|
2173 | }
|
2174 |
|
2175 | //convert specs to csv with pipes
|
2176 | const specString = specKeys.map(function(key) {
|
2177 | return key + ',' + specs[key].colType + ',' + specs[key].colLength;
|
2178 | }).join('|');
|
2179 |
|
2180 | this._files[macroName] = [
|
2181 | specString,
|
2182 | new Blob([sasCsv], {type: 'text/csv;charset=UTF-8'})
|
2183 | ];
|
2184 | };
|
2185 |
|
2186 | /**
|
2187 | * Add file as a verbatim blob file uplaod
|
2188 | * @param {Blob} file - the blob that will be uploaded as file
|
2189 | * @param {String} macroName - the SAS webin name given to this file
|
2190 | */
|
2191 | SasData.prototype.addFile = function(file, macroName) {
|
2192 | Files.prototype.add.call(this, file, macroName);
|
2193 | };
|
2194 |
|
2195 | module.exports = SasData;
|
2196 |
|
2197 | },{"./error.js":1,"./files":2,"./logs.js":5,"./tables":10,"./tables/utils.js":11}],10:[function(require,module,exports){
|
2198 | const h54sError = require('../error.js');
|
2199 |
|
2200 | /*
|
2201 | * h54s tables object constructor
|
2202 | * @constructor
|
2203 | *
|
2204 | *@param {array} table - Table added when object is created
|
2205 | *@param {string} macroName - macro name
|
2206 | *@param {number} parameterThreshold - size of data objects sent to SAS
|
2207 | *
|
2208 | */
|
2209 | function Tables(table, macroName, parameterThreshold) {
|
2210 | this._tables = {};
|
2211 | this._parameterThreshold = parameterThreshold || 30000;
|
2212 |
|
2213 | Tables.prototype.add.call(this, table, macroName);
|
2214 | }
|
2215 |
|
2216 | /*
|
2217 | * Add table to tables object
|
2218 | * @param {array} table - Array of table objects
|
2219 | * @param {string} macroName - Sas macro name
|
2220 | *
|
2221 | */
|
2222 | Tables.prototype.add = function(table, macroName) {
|
2223 | if(table && macroName) {
|
2224 | if(!(table instanceof Array)) {
|
2225 | throw new h54sError('argumentError', 'First argument must be array');
|
2226 | }
|
2227 | if(typeof macroName !== 'string') {
|
2228 | throw new h54sError('argumentError', 'Second argument must be string');
|
2229 | }
|
2230 | if(!isNaN(macroName[macroName.length - 1])) {
|
2231 | throw new h54sError('argumentError', 'Macro name cannot have number at the end');
|
2232 | }
|
2233 | } else {
|
2234 | throw new h54sError('argumentError', 'Missing arguments');
|
2235 | }
|
2236 |
|
2237 | const result = this._utils.convertTableObject(table, this._parameterThreshold);
|
2238 |
|
2239 | const tableArray = [];
|
2240 | tableArray.push(JSON.stringify(result.spec));
|
2241 | for (let numberOfTables = 0; numberOfTables < result.data.length; numberOfTables++) {
|
2242 | const outString = JSON.stringify(result.data[numberOfTables]);
|
2243 | tableArray.push(outString);
|
2244 | }
|
2245 | this._tables[macroName] = tableArray;
|
2246 | };
|
2247 |
|
2248 | Tables.prototype._utils = require('./utils.js');
|
2249 |
|
2250 | module.exports = Tables;
|
2251 |
|
2252 | },{"../error.js":1,"./utils.js":11}],11:[function(require,module,exports){
|
2253 | const h54sError = require('../error.js');
|
2254 | const logs = require('../logs.js');
|
2255 |
|
2256 | /*
|
2257 | * Convert table object to Sas readable object
|
2258 | *
|
2259 | * @param {object} inObject - Object to convert
|
2260 | *
|
2261 | */
|
2262 | module.exports.convertTableObject = function(inObject, chunkThreshold) {
|
2263 | const self = this;
|
2264 |
|
2265 | if(chunkThreshold > 30000) {
|
2266 | console.warn('You should not set threshold larger than 30kb because of the SAS limitations');
|
2267 | }
|
2268 |
|
2269 | // first check that the object is an array
|
2270 | if (typeof (inObject) !== 'object') {
|
2271 | throw new h54sError('argumentError', 'The parameter passed to checkAndGetTypeObject is not an object');
|
2272 | }
|
2273 |
|
2274 | const arrayLength = inObject.length;
|
2275 | if (typeof (arrayLength) !== 'number') {
|
2276 | throw new h54sError('argumentError', 'The parameter passed to checkAndGetTypeObject does not have a valid length and is most likely not an array');
|
2277 | }
|
2278 |
|
2279 | const existingCols = {}; // this is just to make lookup easier rather than traversing array each time. Will transform after
|
2280 |
|
2281 | // function checkAndSetArray - this will check an inObject current key against the existing typeArray and either return -1 if there
|
2282 | // is a type mismatch or add an element and update/increment the length if needed
|
2283 |
|
2284 | function checkAndIncrement(colSpec) {
|
2285 | if (typeof (existingCols[colSpec.colName]) === 'undefined') {
|
2286 | existingCols[colSpec.colName] = {};
|
2287 | existingCols[colSpec.colName].colName = colSpec.colName;
|
2288 | existingCols[colSpec.colName].colType = colSpec.colType;
|
2289 | existingCols[colSpec.colName].colLength = colSpec.colLength > 0 ? colSpec.colLength : 1;
|
2290 | return 0; // all ok
|
2291 | }
|
2292 | // check type match
|
2293 | if (existingCols[colSpec.colName].colType !== colSpec.colType) {
|
2294 | return -1; // there is a fudge in the typing
|
2295 | }
|
2296 | if (existingCols[colSpec.colName].colLength < colSpec.colLength) {
|
2297 | existingCols[colSpec.colName].colLength = colSpec.colLength > 0 ? colSpec.colLength : 1; // increment the max length of this column
|
2298 | return 0;
|
2299 | }
|
2300 | }
|
2301 | let chunkArrayCount = 0; // this is for keeping tabs on how long the current array string would be
|
2302 | const targetArray = []; // this is the array of target arrays
|
2303 | let currentTarget = 0;
|
2304 | targetArray[currentTarget] = [];
|
2305 | let j = 0;
|
2306 | for (let i = 0; i < inObject.length; i++) {
|
2307 | targetArray[currentTarget][j] = {};
|
2308 | let chunkRowCount = 0;
|
2309 |
|
2310 | for (let key in inObject[i]) {
|
2311 | const thisSpec = {};
|
2312 | const thisValue = inObject[i][key];
|
2313 |
|
2314 | //skip undefined values
|
2315 | if(thisValue === undefined || thisValue === null) {
|
2316 | continue;
|
2317 | }
|
2318 |
|
2319 | //throw an error if there's NaN value
|
2320 | if(typeof thisValue === 'number' && isNaN(thisValue)) {
|
2321 | throw new h54sError('typeError', 'NaN value in one of the values (columns) is not allowed');
|
2322 | }
|
2323 |
|
2324 | if(thisValue === -Infinity || thisValue === Infinity) {
|
2325 | throw new h54sError('typeError', thisValue.toString() + ' value in one of the values (columns) is not allowed');
|
2326 | }
|
2327 |
|
2328 | if(thisValue === true || thisValue === false) {
|
2329 | throw new h54sError('typeError', 'Boolean value in one of the values (columns) is not allowed');
|
2330 | }
|
2331 |
|
2332 | // get type... if it is an object then convert it to json and store as a string
|
2333 | const thisType = typeof (thisValue);
|
2334 |
|
2335 | if (thisType === 'number') { // straightforward number
|
2336 | if(thisValue < Number.MIN_SAFE_INTEGER || thisValue > Number.MAX_SAFE_INTEGER) {
|
2337 | logs.addApplicationLog('Object[' + i + '].' + key + ' - This value exceeds expected numeric precision.');
|
2338 | }
|
2339 | thisSpec.colName = key;
|
2340 | thisSpec.colType = 'num';
|
2341 | thisSpec.colLength = 8;
|
2342 | thisSpec.encodedLength = thisValue.toString().length;
|
2343 | targetArray[currentTarget][j][key] = thisValue;
|
2344 | } else if (thisType === 'string') {
|
2345 | thisSpec.colName = key;
|
2346 | thisSpec.colType = 'string';
|
2347 | thisSpec.colLength = thisValue.length;
|
2348 |
|
2349 | if (thisValue === "") {
|
2350 | targetArray[currentTarget][j][key] = " ";
|
2351 | } else {
|
2352 | targetArray[currentTarget][j][key] = encodeURIComponent(thisValue).replace(/'/g, '%27');
|
2353 | }
|
2354 | thisSpec.encodedLength = targetArray[currentTarget][j][key].length;
|
2355 | } else if(thisValue instanceof Date) {
|
2356 | console.log("ERROR VALUE ", thisValue)
|
2357 | console.log("TYPEOF VALUE ", typeof thisValue)
|
2358 | throw new h54sError('typeError', 'Date type not supported. Please use h54s.toSasDateTime function to convert it');
|
2359 | } else if (thisType == 'object') {
|
2360 | thisSpec.colName = key;
|
2361 | thisSpec.colType = 'json';
|
2362 | thisSpec.colLength = JSON.stringify(thisValue).length;
|
2363 | targetArray[currentTarget][j][key] = encodeURIComponent(JSON.stringify(thisValue)).replace(/'/g, '%27');
|
2364 | thisSpec.encodedLength = targetArray[currentTarget][j][key].length;
|
2365 | }
|
2366 |
|
2367 | chunkRowCount = chunkRowCount + 6 + key.length + thisSpec.encodedLength;
|
2368 |
|
2369 | if (checkAndIncrement(thisSpec) == -1) {
|
2370 | throw new h54sError('typeError', 'There is a type mismatch in the array between values (columns) of the same name.');
|
2371 | }
|
2372 | }
|
2373 |
|
2374 | //remove last added row if it's empty
|
2375 | if(Object.keys(targetArray[currentTarget][j]).length === 0) {
|
2376 | targetArray[currentTarget].splice(j, 1);
|
2377 | continue;
|
2378 | }
|
2379 |
|
2380 | if (chunkRowCount > chunkThreshold) {
|
2381 | throw new h54sError('argumentError', 'Row ' + j + ' exceeds size limit of 32kb');
|
2382 | } else if(chunkArrayCount + chunkRowCount > chunkThreshold) {
|
2383 | //create new array if this one is full and move the last item to the new array
|
2384 | const lastRow = targetArray[currentTarget].pop(); // get rid of that last row
|
2385 | currentTarget++; // move onto the next array
|
2386 | targetArray[currentTarget] = [lastRow]; // make it an array
|
2387 | j = 0; // initialise new row counter for new array - it will be incremented at the end of the function
|
2388 | chunkArrayCount = chunkRowCount; // this is the new chunk max size
|
2389 | } else {
|
2390 | chunkArrayCount = chunkArrayCount + chunkRowCount;
|
2391 | }
|
2392 | j++;
|
2393 | }
|
2394 |
|
2395 | // reformat existingCols into an array so sas can parse it;
|
2396 | const specArray = [];
|
2397 | for (let k in existingCols) {
|
2398 | specArray.push(existingCols[k]);
|
2399 | }
|
2400 | return {
|
2401 | spec: specArray,
|
2402 | data: targetArray,
|
2403 | jsonLength: chunkArrayCount
|
2404 | }; // the spec will be the macro[0], with the data split into arrays of macro[1-n]
|
2405 | // means in terms of dojo xhr object at least they need to go into the same array
|
2406 | };
|
2407 |
|
2408 | /*
|
2409 | * Convert javascript date to sas time
|
2410 | *
|
2411 | * @param {object} jsDate - javascript Date object
|
2412 | *
|
2413 | */
|
2414 | module.exports.toSasDateTime = function (jsDate) {
|
2415 | const basedate = new Date("January 1, 1960 00:00:00");
|
2416 | const currdate = jsDate;
|
2417 |
|
2418 | // offsets for UTC and timezones and BST
|
2419 | const baseOffset = basedate.getTimezoneOffset(); // in minutes
|
2420 | const currOffset = currdate.getTimezoneOffset(); // in minutes
|
2421 |
|
2422 | // convert currdate to a sas datetime
|
2423 | const offsetSecs = (currOffset - baseOffset) * 60; // offsetDiff is in minutes to start with
|
2424 | const baseDateSecs = basedate.getTime() / 1000; // get rid of ms
|
2425 | const currdateSecs = currdate.getTime() / 1000; // get rid of ms
|
2426 | const sasDatetime = Math.round(currdateSecs - baseDateSecs - offsetSecs); // adjust
|
2427 |
|
2428 | return sasDatetime;
|
2429 | };
|
2430 |
|
2431 | },{"../error.js":1,"../logs.js":5}]},{},[3])(3)
|
2432 | });
|
2433 |
|
2434 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvZXJyb3IuanMiLCJzcmMvZmlsZXMvaW5kZXguanMiLCJzcmMvaDU0cy5qcyIsInNyYy9pZV9wb2x5ZmlsbHMuanMiLCJzcmMvbG9ncy5qcyIsInNyYy9tZXRob2RzL2FqYXguanMiLCJzcmMvbWV0aG9kcy9pbmRleC5qcyIsInNyYy9tZXRob2RzL3V0aWxzLmpzIiwic3JjL3Nhc0RhdGEuanMiLCJzcmMvdGFibGVzL2luZGV4LmpzIiwic3JjL3RhYmxlcy91dGlscy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9JQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbDhCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25UQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pRQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiByKGUsbix0KXtmdW5jdGlvbiBvKGksZil7aWYoIW5baV0pe2lmKCFlW2ldKXt2YXIgYz1cImZ1bmN0aW9uXCI9PXR5cGVvZiByZXF1aXJlJiZyZXF1aXJlO2lmKCFmJiZjKXJldHVybiBjKGksITApO2lmKHUpcmV0dXJuIHUoaSwhMCk7dmFyIGE9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitpK1wiJ1wiKTt0aHJvdyBhLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsYX12YXIgcD1uW2ldPXtleHBvcnRzOnt9fTtlW2ldWzBdLmNhbGwocC5leHBvcnRzLGZ1bmN0aW9uKHIpe3ZhciBuPWVbaV1bMV1bcl07cmV0dXJuIG8obnx8cil9LHAscC5leHBvcnRzLHIsZSxuLHQpfXJldHVybiBuW2ldLmV4cG9ydHN9Zm9yKHZhciB1PVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmUsaT0wO2k8dC5sZW5ndGg7aSsrKW8odFtpXSk7cmV0dXJuIG99cmV0dXJuIHJ9KSgpIiwiLypcbiogaDU0cyBlcnJvciBjb25zdHJ1Y3RvclxuKiBAY29uc3RydWN0b3JcbipcbipAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEVycm9yIHR5cGVcbipAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIEVycm9yIG1lc3NhZ2VcbipAcGFyYW0ge3N0cmluZ30gc3RhdHVzIC0gRXJyb3Igc3RhdHVzIHJldHVybmVkIGZyb20gU0FTXG4qXG4qL1xuZnVuY3Rpb24gaDU0c0Vycm9yKHR5cGUsIG1lc3NhZ2UsIHN0YXR1cykge1xuICBpZihFcnJvci5jYXB0dXJlU3RhY2tUcmFjZSkge1xuICAgIEVycm9yLmNhcHR1cmVTdGFja1RyYWNlKHRoaXMpO1xuICB9XG4gIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XG4gIHRoaXMudHlwZSAgICA9IHR5cGU7XG4gIHRoaXMuc3RhdHVzICA9IHN0YXR1cztcbn1cblxuaDU0c0Vycm9yLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoRXJyb3IucHJvdG90eXBlLCB7XG4gIGNvbnN0cnVjdG9yOiB7XG4gICAgY29uZmlndXJhYmxlOiBmYWxzZSxcbiAgICBlbnVtZXJhYmxlOiBmYWxzZSxcbiAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgdmFsdWU6IGg1NHNFcnJvclxuICB9LFxuICBuYW1lOiB7XG4gICAgY29uZmlndXJhYmxlOiBmYWxzZSxcbiAgICBlbnVtZXJhYmxlOiBmYWxzZSxcbiAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgdmFsdWU6ICdoNTRzRXJyb3InXG4gIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGg1NHNFcnJvcjtcbiIsImNvbnN0IGg1NHNFcnJvciA9IHJlcXVpcmUoJy4uL2Vycm9yLmpzJyk7XG5cbi8qKlxuKiBoNTRzIFNBUyBGaWxlcyBvYmplY3QgY29uc3RydWN0b3JcbiogQGNvbnN0cnVjdG9yXG4qXG4qQHBhcmFtIHtmaWxlfSBmaWxlIC0gRmlsZSBhZGRlZCB3aGVuIG9iamVjdCBpcyBjcmVhdGVkXG4qQHBhcmFtIHtzdHJpbmd9IG1hY3JvTmFtZSAtIG1hY3JvIG5hbWVcbipcbiovXG5mdW5jdGlvbiBGaWxlcyhmaWxlLCBtYWNyb05hbWUpIHtcbiAgdGhpcy5fZmlsZXMgPSB7fTtcblxuICBGaWxlcy5wcm90b3R5cGUuYWRkLmNhbGwodGhpcywgZmlsZSwgbWFjcm9OYW1lKTtcbn1cblxuLyoqXG4qIEFkZCBmaWxlIHRvIGZpbGVzIG9iamVjdFxuKiBAcGFyYW0ge2ZpbGV9IGZpbGUgLSBJbnN0YW5jZSBvZiBKYXZhU2NyaXB0IEZpbGUgb2JqZWN0XG4qIEBwYXJhbSB7c3RyaW5nfSBtYWNyb05hbWUgLSBTYXMgbWFjcm8gbmFtZVxuKlxuKi9cbkZpbGVzLnByb3RvdHlwZS5hZGQgPSBmdW5jdGlvbihmaWxlLCBtYWNyb05hbWUpIHtcbiAgaWYoZmlsZSAmJiBtYWNyb05hbWUpIHtcbiAgICBpZighKGZpbGUgaW5zdGFuY2VvZiBGaWxlIHx8IGZpbGUgaW5zdGFuY2VvZiBCbG9iKSkge1xuICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdGaXJzdCBhcmd1bWVudCBtdXN0IGJlIGluc3RhbmNlIG9mIEZpbGUgb2JqZWN0Jyk7XG4gICAgfVxuICAgIGlmKHR5cGVvZiBtYWNyb05hbWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1NlY29uZCBhcmd1bWVudCBtdXN0IGJlIHN0cmluZycpO1xuICAgIH1cbiAgICBpZighaXNOYU4obWFjcm9OYW1lW21hY3JvTmFtZS5sZW5ndGggLSAxXSkpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnTWFjcm8gbmFtZSBjYW5ub3QgaGF2ZSBudW1iZXIgYXQgdGhlIGVuZCcpO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ01pc3NpbmcgYXJndW1lbnRzJyk7XG4gIH1cblxuICB0aGlzLl9maWxlc1ttYWNyb05hbWVdID0gW1xuICAgICdGSUxFJyxcbiAgICBmaWxlXG4gIF07XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEZpbGVzO1xuIiwiY29uc3QgaDU0c0Vycm9yID0gcmVxdWlyZSgnLi9lcnJvci5qcycpO1xuXG5jb25zdCBzYXNWZXJzaW9uTWFwID0ge1xuXHR2OToge1xuICAgIHVybDogJy9TQVNTdG9yZWRQcm9jZXNzL2RvJyxcbiAgICBsb2dpblVybDogJy9TQVNMb2dvbi9sb2dpbicsXG5cdFx0bG9nb3V0VXJsOiAnL1NBU1N0b3JlZFByb2Nlc3MvZG8/X2FjdGlvbj1sb2dvZmYnLFxuICAgIFJFU1RBdXRoTG9naW5Vcmw6ICcvU0FTTG9nb24vdjEvdGlja2V0cydcblx0fSxcblx0dml5YToge1xuXHRcdHVybDogJy9TQVNKb2JFeGVjdXRpb24vJyxcbiAgICBsb2dpblVybDogJy9TQVNMb2dvbi9sb2dpbi5kbycsXG5cdFx0bG9nb3V0VXJsOiAnL1NBU0xvZ29uL2xvZ291dC5kbz8nLFxuICAgIFJFU1RBdXRoTG9naW5Vcmw6ICcnXG5cdH1cbn1cblxuLyoqXG4qXG4qIEBjb25zdHJ1Y3RvclxuKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gQ29uZmlndXJhdGlvbiBvYmplY3QgZm9yIHRoZSBINTRTIFNBUyBBZGFwdGVyXG4qIEBwYXJhbSB7U3RyaW5nfSBjb25maWcuc2FzVmVyc2lvbiAtIFZlcnNpb24gb2YgU0FTLCBlaXRoZXIgJ3Y5JyBvciAndml5YSdcbiogQHBhcmFtIHtCb29sZWFufSBjb25maWcuZGVidWcgLSBXaGV0aGVyIGRlYnVnIG1vZGUgaXMgZW5hYmxlZCwgc2V0cyBfZGVidWc9MTMxXG4qIEBwYXJhbSB7U3RyaW5nfSBjb25maWcubWV0YWRhdGFSb290IC0gQmFzZSBwYXRoIG9mIGFsbCBwcm9qZWN0IHNlcnZpY2VzIHRvIGJlIHByZXBlbmRlZCB0byBfcHJvZ3JhbSBwYXRoXG4qIEBwYXJhbSB7U3RyaW5nfSBjb25maWcudXJsIC0gVVJJIG9mIHRoZSBqb2IgZXhlY3V0b3IgLSBTUFdBIG9yIEpFU1xuKiBAcGFyYW0ge1N0cmluZ30gY29uZmlnLmxvZ2luVXJsIC0gVVJJIG9mIHRoZSBTQVNMb2dvbiB3ZWIgbG9naW4gcGF0aCAtIG92ZXJyaWRkZW4gYnkgZm9ybSBhY3Rpb25cbiogQHBhcmFtIHtTdHJpbmd9IGNvbmZpZy5sb2dvdXRVcmwgLSBVUkkgb2YgdGhlIGxvZ291dCBhY3Rpb25cbiogQHBhcmFtIHtTdHJpbmd9IGNvbmZpZy5SRVNUYXV0aCAtIEJvb2xlYW4gdG8gdG9nZ2xlIHVzZSBvZiBSRVNUIGF1dGhlbnRpY2F0aW9uIGluIFNBUyB2OVxuKiBAcGFyYW0ge1N0cmluZ30gY29uZmlnLlJFU1RhdXRoTG9naW5VcmwgLSBBZGRyZXNzIG9mIFNBU0xvZ29uIHRpY2tldHMgZW5kcG9pbnQgZm9yIFJFU1QgYXV0aFxuKiBAcGFyYW0ge0Jvb2xlYW59IGNvbmZpZy5yZXRyeUFmdGVyTG9naW4gLSBXaGV0aGVyIHRvIHJlc3VtZSByZXF1ZXN0cyB3aGljaCB3ZXJlIHBhcmtlZCB3aXRoIGxvZ2luIHJlZGlyZWN0IGFmdGVyIGEgc3VjY2Vzc2Z1bCByZS1sb2dpblxuKiBAcGFyYW0ge051bWJlcn0gY29uZmlnLm1heFhoclJldHJpZXMgLSBJZiBhIHByb2dyYW0gY2FsbCBmYWlscywgYXR0ZW1wdCB0byBjYWxsIGl0IGFnYWluIE4gdGltZXMgdW50aWwgaXQgc3VjY2VlZHNcbiogQHBhcmFtIHtOdW1iZXJ9IGNvbmZpZy5hamF4VGltZW91dCAtIE51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYSByZXNwb25zZSBiZWZvcmUgY2xvc2luZyB0aGUgcmVxdWVzdFxuKiBAcGFyYW0ge0Jvb2xlYW59IGNvbmZpZy51c2VNdWx0aXBhcnRGb3JtRGF0YSAtIFdoZXRoZXIgdG8gdXNlIG11bHRpcGFydCBmb3IgUE9TVCAtIGZvciBsZWdhY3kgYmFja2VuZCBzdXBwb3J0XG4qIEBwYXJhbSB7U3RyaW5nfSBjb25maWcuY3NyZiAtIENTUkYgdG9rZW4gZm9yIEpFU1xuKiBAXG4qXG4qL1xuY29uc3QgaDU0cyA9IG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oY29uZmlnKSB7XG4gIC8vIERlZmF1bHQgY29uZmlnIHZhbHVlcywgb3ZlcnJpZGRlbiBieSBhbnl0aGluZyBpbiB0aGUgY29uZmlnIG9iamVjdFxuXHR0aGlzLnNhc1ZlcnNpb24gICAgICAgICAgID0gKGNvbmZpZyAmJiBjb25maWcuc2FzVmVyc2lvbikgfHwgJ3Y5JyAvL3VzZSB2OSBhcyBkZWZhdWx0PVxuICB0aGlzLmRlYnVnICAgICAgICAgICAgICAgID0gKGNvbmZpZyAmJiBjb25maWcuZGVidWcpIHx8IGZhbHNlO1xuICB0aGlzLm1ldGFkYXRhUm9vdFx0XHRcdFx0XHQ9IChjb25maWcgJiYgY29uZmlnLm1ldGFkYXRhUm9vdCkgfHwgJyc7XG4gIHRoaXMudXJsICAgICAgICAgICAgICAgICAgPSBzYXNWZXJzaW9uTWFwW3RoaXMuc2FzVmVyc2lvbl0udXJsO1xuICB0aGlzLmxvZ2luVXJsICAgICAgICAgICAgID0gc2FzVmVyc2lvbk1hcFt0aGlzLnNhc1ZlcnNpb25dLmxvZ2luVXJsO1xuICB0aGlzLmxvZ291dFVybCAgICAgICAgICAgID0gc2FzVmVyc2lvbk1hcFt0aGlzLnNhc1ZlcnNpb25dLmxvZ291dFVybDtcbiAgdGhpcy5SRVNUYXV0aCAgICAgICAgICAgICA9IGZhbHNlO1xuICB0aGlzLlJFU1RhdXRoTG9naW5VcmwgICAgID0gc2FzVmVyc2lvbk1hcFt0aGlzLnNhc1ZlcnNpb25dLlJFU1RBdXRoTG9naW5Vcmw7XG4gIHRoaXMucmV0cnlBZnRlckxvZ2luICAgICAgPSB0cnVlO1xuICB0aGlzLm1heFhoclJldHJpZXMgICAgICAgID0gNTtcbiAgdGhpcy5hamF4VGltZW91dCAgICAgICAgICA9IChjb25maWcgJiYgY29uZmlnLmFqYXhUaW1lb3V0KSB8fCAzMDAwMDA7XG4gIHRoaXMudXNlTXVsdGlwYXJ0Rm9ybURhdGEgPSAoY29uZmlnICYmIGNvbmZpZy51c2VNdWx0aXBhcnRGb3JtRGF0YSkgfHwgdHJ1ZTtcbiAgdGhpcy5jc3JmICAgICAgICAgICAgICAgICA9ICcnXG4gIHRoaXMuaXNWaXlhXHRcdFx0XHRcdFx0XHRcdD0gdGhpcy5zYXNWZXJzaW9uID09PSAndml5YSc7XG5cbiAgLy8gSW5pdGlhbGlzaW5nIGNhbGxiYWNrIHN0YWNrcyBmb3Igd2hlbiBhdXRoZW50aWNhdGlvbiBpcyBwYXVzZWRcbiAgdGhpcy5yZW1vdGVDb25maWdVcGRhdGVDYWxsYmFja3MgPSBbXTtcbiAgdGhpcy5fcGVuZGluZ0NhbGxzID0gW107XG4gIHRoaXMuX2N1c3RvbVBlbmRpbmdDYWxscyA9IFtdO1xuICB0aGlzLl9kaXNhYmxlQ2FsbHMgPSBmYWxzZVxuICB0aGlzLl9hamF4ID0gcmVxdWlyZSgnLi9tZXRob2RzL2FqYXguanMnKSgpO1xuXG4gIF9zZXRDb25maWcuY2FsbCh0aGlzLCBjb25maWcpO1xuXG4gIC8vIElmIHRoaXMgaW5zdGFuY2Ugd2FzIGRlcGxveWVkIHdpdGggYSBzdGFuZGFsb25lIGNvbmZpZyBleHRlcm5hbCB0byB0aGUgYnVpbGQgdXNlIHRoYXRcbiAgaWYoY29uZmlnICYmIGNvbmZpZy5pc1JlbW90ZUNvbmZpZykge1xuICAgIGNvbnN0IHNlbGYgPSB0aGlzO1xuXG4gICAgdGhpcy5fZGlzYWJsZUNhbGxzID0gdHJ1ZTtcblxuICAgIC8vICdoNTRzQ29uZmlnLmpzb24nIGlzIGZvciB0aGUgdGVzdGluZyB3aXRoIGthcm1hXG4gICAgLy9yZXBsYWNlZCBieSBndWxwIGluIGRldiBidWlsZCAoZGVmaW5lZCBpbiBndWxwZmlsZSB1bmRlciBwcm94aWVzKVxuICAgIHRoaXMuX2FqYXguZ2V0KCdoNTRzQ29uZmlnLmpzb24nKS5zdWNjZXNzKGZ1bmN0aW9uKHJlcykge1xuICAgICAgY29uc3QgcmVtb3RlQ29uZmlnID0gSlNPTi5wYXJzZShyZXMucmVzcG9uc2VUZXh0KVxuXG5cdFx0XHQvLyBTYXZlIGxvY2FsIGNvbmZpZyBiZWZvcmUgdXBkYXRpbmcgaXQgd2l0aCByZW1vdGUgY29uZmlnXG5cdFx0XHRjb25zdCBsb2NhbENvbmZpZyA9IE9iamVjdC5hc3NpZ24oe30sIGNvbmZpZylcblx0XHRcdGNvbnN0IG9sZE1ldGFkYXRhUm9vdCA9IGxvY2FsQ29uZmlnLm1ldGFkYXRhUm9vdDtcblxuICAgICAgZm9yKGxldCBrZXkgaW4gcmVtb3RlQ29uZmlnKSB7XG4gICAgICAgIGlmKHJlbW90ZUNvbmZpZy5oYXNPd25Qcm9wZXJ0eShrZXkpICYmIGtleSAhPT0gJ2lzUmVtb3RlQ29uZmlnJykge1xuICAgICAgICAgIGNvbmZpZ1trZXldID0gcmVtb3RlQ29uZmlnW2tleV07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgX3NldENvbmZpZy5jYWxsKHNlbGYsIGNvbmZpZyk7XG5cbiAgICAgIC8vIEV4ZWN1dGUgY2FsbGJhY2tzIHdoZW4gb3ZlcnJpZGVzIGZyb20gcmVtb3RlIGNvbmZpZyBhcmUgYXBwbGllZFxuICAgICAgZm9yKGxldCBpID0gMCwgbiA9IHNlbGYucmVtb3RlQ29uZmlnVXBkYXRlQ2FsbGJhY2tzLmxlbmd0aDsgaSA8IG47IGkrKykge1xuICAgICAgICBjb25zdCBmbiA9IHNlbGYucmVtb3RlQ29uZmlnVXBkYXRlQ2FsbGJhY2tzW2ldO1xuICAgICAgICBmbigpO1xuICAgICAgfVxuXG4gICAgICAvLyBFeGVjdXRlIHNhcyBjYWxscyBkaXNhYmxlZCB3aGlsZSB3YWl0aW5nIGZvciB0aGUgY29uZmlnXG4gICAgICBzZWxmLl9kaXNhYmxlQ2FsbHMgPSBmYWxzZTtcbiAgICAgIHdoaWxlKHNlbGYuX3BlbmRpbmdDYWxscy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IHBlbmRpbmdDYWxsID0gc2VsZi5fcGVuZGluZ0NhbGxzLnNoaWZ0KCk7XG5cdFx0XHRcdGNvbnN0IHNhc1Byb2dyYW0gPSBwZW5kaW5nQ2FsbC5vcHRpb25zLnNhc1Byb2dyYW07XG5cdFx0XHRcdGNvbnN0IGNhbGxiYWNrUGVuZGluZyA9IHBlbmRpbmdDYWxsLm9wdGlvbnMuY2FsbGJhY2s7XG5cdFx0XHRcdGNvbnN0IHBhcmFtcyA9IHBlbmRpbmdDYWxsLnBhcmFtcztcblx0XHRcdFx0Ly91cGRhdGUgZGVidWcgYmVjYXVzZSBpdCBtYXkgY2hhbmdlIGluIHRoZSBtZWFudGltZVxuXHRcdFx0XHRwYXJhbXMuX2RlYnVnID0gc2VsZi5kZWJ1ZyA/IDEzMSA6IDA7XG5cbiAgICAgICAgLy8gVXBkYXRlIHByb2dyYW0gcGF0aCB3aXRoIG1ldGFkYXRhUm9vdCBpZiBpdCdzIG5vdCBzZXRcbiAgICAgICAgaWYoc2VsZi5tZXRhZGF0YVJvb3QgJiYgcGFyYW1zLl9wcm9ncmFtLmluZGV4T2Yoc2VsZi5tZXRhZGF0YVJvb3QpID09PSAtMSkge1xuICAgICAgICAgIHBhcmFtcy5fcHJvZ3JhbSA9IHNlbGYubWV0YWRhdGFSb290LnJlcGxhY2UoL1xcLz8kLywgJy8nKSArIHBhcmFtcy5fcHJvZ3JhbS5yZXBsYWNlKG9sZE1ldGFkYXRhUm9vdCwgJycpLnJlcGxhY2UoL15cXC8vLCAnJyk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVcGRhdGUgZGVidWcgYmVjYXVzZSBpdCBtYXkgY2hhbmdlIGluIHRoZSBtZWFudGltZVxuICAgICAgICBwYXJhbXMuX2RlYnVnID0gc2VsZi5kZWJ1ZyA/IDEzMSA6IDA7XG5cbiAgICAgICAgc2VsZi5jYWxsKHNhc1Byb2dyYW0sIG51bGwsIGNhbGxiYWNrUGVuZGluZywgcGFyYW1zKTtcbiAgICAgIH1cblxuICAgICAgLy8gRXhlY3V0ZSBjdXN0b20gY2FsbHMgdGhhdCB3ZSBtYWRlIHdoaWxlIHdhaXRpbmYgZm9yIHRoZSBjb25maWdcbiAgICAgICB3aGlsZShzZWxmLl9jdXN0b21QZW5kaW5nQ2FsbHMubGVuZ3RoID4gMCkge1xuICAgICAgXHRjb25zdCBwZW5kaW5nQ2FsbCA9IHNlbGYuX2N1c3RvbVBlbmRpbmdDYWxscy5zaGlmdCgpXG5cdFx0XHRcdGNvbnN0IGNhbGxNZXRob2QgPSBwZW5kaW5nQ2FsbC5jYWxsTWV0aG9kXG5cdFx0XHRcdGNvbnN0IF91cmwgPSBwZW5kaW5nQ2FsbC5fdXJsXG5cdFx0XHRcdGNvbnN0IG9wdGlvbnMgPSBwZW5kaW5nQ2FsbC5vcHRpb25zO1xuICAgICAgICAvLy91cGRhdGUgcHJvZ3JhbSB3aXRoIG1ldGFkYXRhUm9vdCBpZiBpdCdzIG5vdCBzZXRcbiAgICAgICAgaWYoc2VsZi5tZXRhZGF0YVJvb3QgJiYgb3B0aW9ucy5wYXJhbXMgJiYgb3B0aW9ucy5wYXJhbXMuX3Byb2dyYW0uaW5kZXhPZihzZWxmLm1ldGFkYXRhUm9vdCkgPT09IC0xKSB7XG4gICAgICAgICAgb3B0aW9ucy5wYXJhbXMuX3Byb2dyYW0gPSBzZWxmLm1ldGFkYXRhUm9vdC5yZXBsYWNlKC9cXC8/JC8sICcvJykgKyBvcHRpb25zLnBhcmFtcy5fcHJvZ3JhbS5yZXBsYWNlKG9sZE1ldGFkYXRhUm9vdCwgJycpLnJlcGxhY2UoL15cXC8vLCAnJyk7XG4gICAgICAgIH1cbiAgICAgICAgLy91cGRhdGUgZGVidWcgYmVjYXVzZSBpdCBhbHNvIG1heSBoYXZlIGNoYW5nZWQgZnJvbSByZW1vdGVDb25maWdcblx0XHRcdFx0aWYgKG9wdGlvbnMucGFyYW1zKSB7XG5cdFx0XHRcdFx0b3B0aW9ucy5wYXJhbXMuX2RlYnVnID0gc2VsZi5kZWJ1ZyA/IDEzMSA6IDA7XG5cdFx0XHRcdH1cblx0XHRcdFx0c2VsZi5tYW5hZ2VkUmVxdWVzdChjYWxsTWV0aG9kLCBfdXJsLCBvcHRpb25zKTtcbiAgICAgIH1cbiAgICB9KS5lcnJvcihmdW5jdGlvbiAoZXJyKSB7XG4gICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhamF4RXJyb3InLCAnUmVtb3RlIGNvbmZpZyBmaWxlIGNhbm5vdCBiZSBsb2FkZWQuIEh0dHAgc3RhdHVzIGNvZGU6ICcgKyBlcnIuc3RhdHVzKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vIHByaXZhdGUgZnVuY3Rpb24gdG8gc2V0IGg1NHMgaW5zdGFuY2UgcHJvcGVydGllc1xuICBmdW5jdGlvbiBfc2V0Q29uZmlnKGNvbmZpZykge1xuICAgIGlmKCFjb25maWcpIHtcbiAgICAgIHRoaXMuX2FqYXguc2V0VGltZW91dCh0aGlzLmFqYXhUaW1lb3V0KTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2UgaWYodHlwZW9mIGNvbmZpZyAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnRmlyc3QgcGFyYW1ldGVyIHNob3VsZCBiZSBjb25maWcgb2JqZWN0Jyk7XG4gICAgfVxuXG4gICAgLy9tZXJnZSBjb25maWcgb2JqZWN0IGZyb20gcGFyYW1ldGVyIHdpdGggdGhpc1xuICAgIGZvcihsZXQga2V5IGluIGNvbmZpZykge1xuICAgICAgaWYoY29uZmlnLmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgaWYoKGtleSA9PT0gJ3VybCcgfHwga2V5ID09PSAnbG9naW5VcmwnKSAmJiBjb25maWdba2V5XS5jaGFyQXQoMCkgIT09ICcvJykge1xuICAgICAgICAgIGNvbmZpZ1trZXldID0gJy8nICsgY29uZmlnW2tleV07XG4gICAgICAgIH1cbiAgICAgICAgdGhpc1trZXldID0gY29uZmlnW2tleV07XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy9pZiBzZXJ2ZXIgaXMgcmVtb3RlIHVzZSB0aGUgZnVsbCBzZXJ2ZXIgdXJsXG4gICAgLy9OT1RFOiBUaGlzIHJlcXVpcmVzIENPUlMgYW5kIGlzIGhlcmUgZm9yIGxlZ2FjeSBzdXBwb3J0XG4gICAgaWYoY29uZmlnLmhvc3RVcmwpIHtcbiAgICAgIGlmKGNvbmZpZy5ob3N0VXJsLmNoYXJBdChjb25maWcuaG9zdFVybC5sZW5ndGggLSAxKSA9PT0gJy8nKSB7XG4gICAgICAgIGNvbmZpZy5ob3N0VXJsID0gY29uZmlnLmhvc3RVcmwuc2xpY2UoMCwgLTEpO1xuICAgICAgfVxuICAgICAgdGhpcy5ob3N0VXJsID0gY29uZmlnLmhvc3RVcmw7XG4gICAgICBpZiAoIXRoaXMudXJsLmluY2x1ZGVzKHRoaXMuaG9zdFVybCkpIHtcblx0XHRcdFx0dGhpcy51cmwgPSBjb25maWcuaG9zdFVybCArIHRoaXMudXJsO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCF0aGlzLmxvZ2luVXJsLmluY2x1ZGVzKHRoaXMuaG9zdFVybCkpIHtcblx0XHRcdFx0dGhpcy5sb2dpblVybCA9IGNvbmZpZy5ob3N0VXJsICsgdGhpcy5sb2dpblVybDtcblx0XHRcdH1cblx0XHRcdGlmICghdGhpcy5SRVNUYXV0aExvZ2luVXJsLmluY2x1ZGVzKHRoaXMuaG9zdFVybCkpIHtcblx0XHRcdFx0dGhpcy5SRVNUYXV0aExvZ2luVXJsID0gY29uZmlnLmhvc3RVcmwgKyB0aGlzLlJFU1RhdXRoTG9naW5Vcmw7XG5cdFx0XHR9XG4gICAgfVxuXG4gICAgdGhpcy5fYWpheC5zZXRUaW1lb3V0KHRoaXMuYWpheFRpbWVvdXQpO1xuICB9XG59O1xuXG4vLyByZXBsYWNlZCBieSBndWxwIHdpdGggcmVhbCB2ZXJzaW9uIGF0IGJ1aWxkIHRpbWVcbmg1NHMudmVyc2lvbiA9ICdfX3ZlcnNpb25fXyc7XG5cblxuaDU0cy5wcm90b3R5cGUgPSByZXF1aXJlKCcuL21ldGhvZHMnKTtcblxuaDU0cy5UYWJsZXMgPSByZXF1aXJlKCcuL3RhYmxlcycpO1xuaDU0cy5GaWxlcyA9IHJlcXVpcmUoJy4vZmlsZXMnKTtcbmg1NHMuU2FzRGF0YSA9IHJlcXVpcmUoJy4vc2FzRGF0YS5qcycpO1xuXG5oNTRzLmZyb21TYXNEYXRlVGltZSA9IHJlcXVpcmUoJy4vbWV0aG9kcy91dGlscy5qcycpLmZyb21TYXNEYXRlVGltZTtcbmg1NHMudG9TYXNEYXRlVGltZSA9IHJlcXVpcmUoJy4vdGFibGVzL3V0aWxzLmpzJykudG9TYXNEYXRlVGltZTtcblxuLy9zZWxmIGludm9rZWQgZnVuY3Rpb24gbW9kdWxlXG5yZXF1aXJlKCcuL2llX3BvbHlmaWxscy5qcycpO1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCFPYmplY3QuY3JlYXRlKSB7XG4gICAgT2JqZWN0LmNyZWF0ZSA9IGZ1bmN0aW9uKHByb3RvLCBwcm9wcykge1xuICAgICAgaWYgKHR5cGVvZiBwcm9wcyAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgICAgICB0aHJvdyBcIlRoZSBtdWx0aXBsZS1hcmd1bWVudCB2ZXJzaW9uIG9mIE9iamVjdC5jcmVhdGUgaXMgbm90IHByb3ZpZGVkIGJ5IHRoaXMgYnJvd3NlciBhbmQgY2Fubm90IGJlIHNoaW1tZWQuXCI7XG4gICAgICB9XG4gICAgICBmdW5jdGlvbiBjdG9yKCkgeyB9XG4gICAgICBjdG9yLnByb3RvdHlwZSA9IHByb3RvO1xuICAgICAgcmV0dXJuIG5ldyBjdG9yKCk7XG4gICAgfTtcbiAgfVxuXG5cbiAgLy8gRnJvbSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9PYmplY3Qva2V5c1xuICBpZiAoIU9iamVjdC5rZXlzKSB7XG4gICAgT2JqZWN0LmtleXMgPSAoZnVuY3Rpb24gKCkge1xuICAgICAgJ3VzZSBzdHJpY3QnO1xuICAgICAgdmFyIGhhc093blByb3BlcnR5ID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eSxcbiAgICAgICAgICBoYXNEb250RW51bUJ1ZyA9ICEoe3RvU3RyaW5nOiBudWxsfSkucHJvcGVydHlJc0VudW1lcmFibGUoJ3RvU3RyaW5nJyksXG4gICAgICAgICAgZG9udEVudW1zID0gW1xuICAgICAgICAgICAgJ3RvU3RyaW5nJyxcbiAgICAgICAgICAgICd0b0xvY2FsZVN0cmluZycsXG4gICAgICAgICAgICAndmFsdWVPZicsXG4gICAgICAgICAgICAnaGFzT3duUHJvcGVydHknLFxuICAgICAgICAgICAgJ2lzUHJvdG90eXBlT2YnLFxuICAgICAgICAgICAgJ3Byb3BlcnR5SXNFbnVtZXJhYmxlJyxcbiAgICAgICAgICAgICdjb25zdHJ1Y3RvcidcbiAgICAgICAgICBdLFxuICAgICAgICAgIGRvbnRFbnVtc0xlbmd0aCA9IGRvbnRFbnVtcy5sZW5ndGg7XG5cbiAgICAgIHJldHVybiBmdW5jdGlvbiAob2JqKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygb2JqICE9PSAnb2JqZWN0JyAmJiAodHlwZW9mIG9iaiAhPT0gJ2Z1bmN0aW9uJyB8fCBvYmogPT09IG51bGwpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignT2JqZWN0LmtleXMgY2FsbGVkIG9uIG5vbi1vYmplY3QnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciByZXN1bHQgPSBbXSwgcHJvcCwgaTtcblxuICAgICAgICBmb3IgKHByb3AgaW4gb2JqKSB7XG4gICAgICAgICAgaWYgKGhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkge1xuICAgICAgICAgICAgcmVzdWx0LnB1c2gocHJvcCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGhhc0RvbnRFbnVtQnVnKSB7XG4gICAgICAgICAgZm9yIChpID0gMDsgaSA8IGRvbnRFbnVtc0xlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoaGFzT3duUHJvcGVydHkuY2FsbChvYmosIGRvbnRFbnVtc1tpXSkpIHtcbiAgICAgICAgICAgICAgcmVzdWx0LnB1c2goZG9udEVudW1zW2ldKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH07XG4gICAgfSgpKTtcbiAgfVxuXG4gIC8vIEZyb20gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvbGFzdEluZGV4T2ZcbiAgaWYgKCFBcnJheS5wcm90b3R5cGUubGFzdEluZGV4T2YpIHtcbiAgICBBcnJheS5wcm90b3R5cGUubGFzdEluZGV4T2YgPSBmdW5jdGlvbihzZWFyY2hFbGVtZW50IC8qLCBmcm9tSW5kZXgqLykge1xuICAgICAgJ3VzZSBzdHJpY3QnO1xuXG4gICAgICBpZiAodGhpcyA9PT0gdm9pZCAwIHx8IHRoaXMgPT09IG51bGwpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigpO1xuICAgICAgfVxuXG4gICAgICB2YXIgbiwgayxcbiAgICAgICAgdCA9IE9iamVjdCh0aGlzKSxcbiAgICAgICAgbGVuID0gdC5sZW5ndGggPj4+IDA7XG4gICAgICBpZiAobGVuID09PSAwKSB7XG4gICAgICAgIHJldHVybiAtMTtcbiAgICAgIH1cblxuICAgICAgbiA9IGxlbiAtIDE7XG4gICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgbiA9IE51bWJlcihhcmd1bWVudHNbMV0pO1xuICAgICAgICBpZiAobiAhPSBuKSB7XG4gICAgICAgICAgbiA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAobiAhPT0gMCAmJiBuICE9ICgxIC8gMCkgJiYgbiAhPSAtKDEgLyAwKSkge1xuICAgICAgICAgIG4gPSAobiA+IDAgfHwgLTEpICogTWF0aC5mbG9vcihNYXRoLmFicyhuKSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZm9yIChrID0gbiA+PSAwID8gTWF0aC5taW4obiwgbGVuIC0gMSkgOiBsZW4gLSBNYXRoLmFicyhuKTsgayA+PSAwOyBrLS0pIHtcbiAgICAgICAgaWYgKGsgaW4gdCAmJiB0W2tdID09PSBzZWFyY2hFbGVtZW50KSB7XG4gICAgICAgICAgcmV0dXJuIGs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiAtMTtcbiAgICB9O1xuICB9XG59KCk7XG5cbmlmICh3aW5kb3cuTm9kZUxpc3QgJiYgIU5vZGVMaXN0LnByb3RvdHlwZS5mb3JFYWNoKSB7XG4gICBOb2RlTGlzdC5wcm90b3R5cGUuZm9yRWFjaCA9IEFycmF5LnByb3RvdHlwZS5mb3JFYWNoO1xufSIsImNvbnN0IGxvZ3MgPSB7XG4gIGFwcGxpY2F0aW9uTG9nczogW10sXG4gIGRlYnVnRGF0YTogW10sXG4gIHNhc0Vycm9yczogW10sXG4gIGZhaWxlZFJlcXVlc3RzOiBbXVxufTtcblxuY29uc3QgbGltaXRzID0ge1xuICBhcHBsaWNhdGlvbkxvZ3M6IDEwMCxcbiAgZGVidWdEYXRhOiAyMCxcbiAgZmFpbGVkUmVxdWVzdHM6IDIwLFxuICBzYXNFcnJvcnM6IDEwMFxufTtcblxubW9kdWxlLmV4cG9ydHMuZ2V0ID0ge1xuICBnZXRTYXNFcnJvcnM6IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBsb2dzLnNhc0Vycm9ycztcbiAgfSxcbiAgZ2V0QXBwbGljYXRpb25Mb2dzOiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gbG9ncy5hcHBsaWNhdGlvbkxvZ3M7XG4gIH0sXG4gIGdldERlYnVnRGF0YTogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIGxvZ3MuZGVidWdEYXRhO1xuICB9LFxuICBnZXRGYWlsZWRSZXF1ZXN0czogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIGxvZ3MuZmFpbGVkUmVxdWVzdHM7XG4gIH0sXG4gIGdldEFsbExvZ3M6IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4ge1xuICAgICAgc2FzRXJyb3JzOiBsb2dzLnNhc0Vycm9ycyxcbiAgICAgIGFwcGxpY2F0aW9uTG9nczogbG9ncy5hcHBsaWNhdGlvbkxvZ3MsXG4gICAgICBkZWJ1Z0RhdGE6IGxvZ3MuZGVidWdEYXRhLFxuICAgICAgZmFpbGVkUmVxdWVzdHM6IGxvZ3MuZmFpbGVkUmVxdWVzdHNcbiAgICB9XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzLmNsZWFyID0ge1xuICBjbGVhckFwcGxpY2F0aW9uTG9nczogZnVuY3Rpb24oKSB7XG4gICAgbG9ncy5hcHBsaWNhdGlvbkxvZ3Muc3BsaWNlKDAsIGxvZ3MuYXBwbGljYXRpb25Mb2dzLmxlbmd0aCk7XG4gIH0sXG4gIGNsZWFyRGVidWdEYXRhOiBmdW5jdGlvbigpIHtcbiAgICBsb2dzLmRlYnVnRGF0YS5zcGxpY2UoMCwgbG9ncy5kZWJ1Z0RhdGEubGVuZ3RoKTtcbiAgfSxcbiAgY2xlYXJTYXNFcnJvcnM6IGZ1bmN0aW9uKCkge1xuICAgIGxvZ3Muc2FzRXJyb3JzLnNwbGljZSgwLCBsb2dzLnNhc0Vycm9ycy5sZW5ndGgpO1xuICB9LFxuICBjbGVhckZhaWxlZFJlcXVlc3RzOiBmdW5jdGlvbigpIHtcbiAgICBsb2dzLmZhaWxlZFJlcXVlc3RzLnNwbGljZSgwLCBsb2dzLmZhaWxlZFJlcXVlc3RzLmxlbmd0aCk7XG4gIH0sXG4gIGNsZWFyQWxsTG9nczogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5jbGVhckFwcGxpY2F0aW9uTG9ncygpO1xuICAgIHRoaXMuY2xlYXJEZWJ1Z0RhdGEoKTtcbiAgICB0aGlzLmNsZWFyU2FzRXJyb3JzKCk7XG4gICAgdGhpcy5jbGVhckZhaWxlZFJlcXVlc3RzKCk7XG4gIH1cbn07XG5cbi8qKlxuKiAgQWRkcyBhcHBsaWNhdGlvbiBsb2dzIHRvIGFuIGFycmF5IG9mIGxvZ3NcbipcbiogQHBhcmFtIHtTdHJpbmd9IG1lc3NhZ2UgLSBNZXNzYWdlIHRvIGFkZCB0byBhcHBsaWNhdGlvbkxvZ3NcbiogQHBhcmFtIHtTdHJpbmd9IHNhc1Byb2dyYW0gLSBIZWFkZXIgLSB3aGljaCByZXF1ZXN0IGRpZCBtZXNzYWdlIGNvbWUgZnJvbVxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmFkZEFwcGxpY2F0aW9uTG9nID0gZnVuY3Rpb24obWVzc2FnZSwgc2FzUHJvZ3JhbSkge1xuICBpZihtZXNzYWdlID09PSAnYmxhbmsnKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IGxvZyA9IHtcbiAgICBtZXNzYWdlOiAgICBtZXNzYWdlLFxuICAgIHRpbWU6ICAgICAgIG5ldyBEYXRlKCksXG4gICAgc2FzUHJvZ3JhbTogc2FzUHJvZ3JhbVxuICB9O1xuICBsb2dzLmFwcGxpY2F0aW9uTG9ncy5wdXNoKGxvZyk7XG5cbiAgaWYobG9ncy5hcHBsaWNhdGlvbkxvZ3MubGVuZ3RoID4gbGltaXRzLmFwcGxpY2F0aW9uTG9ncykge1xuICAgIGxvZ3MuYXBwbGljYXRpb25Mb2dzLnNoaWZ0KCk7XG4gIH1cbn07XG5cbi8qKlxuKiBBZGRzIGRlYnVnIGRhdGEgdG8gYW4gYXJyYXkgb2YgbG9nc1xuKlxuKiBAcGFyYW0ge1N0cmluZ30gaHRtbERhdGEgLSBGdWxsIGh0bWwgbG9nIGZyb20gZXhlY3V0b3JcbiogQHBhcmFtIHtTdHJpbmd9IGRlYnVnVGV4dCAtIERlYnVnIHRleHQgdGhhdCBjYW1lIGFmdGVyIGRhdGEgb3V0cHV0XG4qIEBwYXJhbSB7U3RyaW5nfSBzYXNQcm9ncmFtIC0gV2hpY2ggcHJvZ3JhbSByZXF1ZXN0IGRpZCBtZXNzYWdlIGNvbWUgZnJvbVxuKiBAcGFyYW0ge1N0cmluZ30gcGFyYW1zIC0gV2ViIGFwcCBwYXJhbXMgdGhhdCB3ZXJlIHJlY2VpdmVkXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMuYWRkRGVidWdEYXRhID0gZnVuY3Rpb24oaHRtbERhdGEsIGRlYnVnVGV4dCwgc2FzUHJvZ3JhbSwgcGFyYW1zKSB7XG4gIGxvZ3MuZGVidWdEYXRhLnB1c2goe1xuICAgIGRlYnVnSHRtbDogIGh0bWxEYXRhLFxuICAgIGRlYnVnVGV4dDogIGRlYnVnVGV4dCxcbiAgICBzYXNQcm9ncmFtOiBzYXNQcm9ncmFtLFxuICAgIHBhcmFtczogICAgIHBhcmFtcyxcbiAgICB0aW1lOiAgICAgICBuZXcgRGF0ZSgpXG4gIH0pO1xuXG4gIGlmKGxvZ3MuZGVidWdEYXRhLmxlbmd0aCA+IGxpbWl0cy5kZWJ1Z0RhdGEpIHtcbiAgICBsb2dzLmRlYnVnRGF0YS5zaGlmdCgpO1xuICB9XG59O1xuXG4vKipcbiogQWRkcyBmYWlsZWQgcmVxdWVzdHMgdG8gYW4gYXJyYXkgb2YgZmFpbGVkIHJlcXVlc3QgbG9nc1xuKlxuKiBAcGFyYW0ge1N0cmluZ30gcmVzcG9uc2VUZXh0IC0gRnVsbCBodG1sIG91dHB1dCBmcm9tIGV4ZWN1dG9yXG4qIEBwYXJhbSB7U3RyaW5nfSBkZWJ1Z1RleHQgLSBEZWJ1ZyB0ZXh0IHRoYXQgY2FtZSBhZnRlciBkYXRhIG91dHB1dFxuKiBAcGFyYW0ge1N0cmluZ30gc2FzUHJvZ3JhbSAtIFdoaWNoIHByb2dyYW0gcmVxdWVzdCBkaWQgbWVzc2FnZSBjb21lIGZyb21cbipcbiovXG5tb2R1bGUuZXhwb3J0cy5hZGRGYWlsZWRSZXF1ZXN0ID0gZnVuY3Rpb24ocmVzcG9uc2VUZXh0LCBkZWJ1Z1RleHQsIHNhc1Byb2dyYW0pIHtcbiAgbG9ncy5mYWlsZWRSZXF1ZXN0cy5wdXNoKHtcbiAgICByZXNwb25zZUh0bWw6IHJlc3BvbnNlVGV4dCxcbiAgICByZXNwb25zZVRleHQ6IGRlYnVnVGV4dCxcbiAgICBzYXNQcm9ncmFtOiAgIHNhc1Byb2dyYW0sXG4gICAgdGltZTogICAgICAgICBuZXcgRGF0ZSgpXG4gIH0pO1xuXG4gIC8vbWF4IDIwIGZhaWxlZCByZXF1ZXN0c1xuICBpZihsb2dzLmZhaWxlZFJlcXVlc3RzLmxlbmd0aCA+IGxpbWl0cy5mYWlsZWRSZXF1ZXN0cykge1xuICAgIGxvZ3MuZmFpbGVkUmVxdWVzdHMuc2hpZnQoKTtcbiAgfVxufTtcblxuLyoqXG4qIEFkZHMgU0FTIGVycm9ycyB0byBhbiBhcnJheSBvZiBsb2dzXG4qXG4qIEBwYXJhbSB7QXJyYXl9IGVycm9ycyAtIEFycmF5IG9mIGVycm9ycyB0byBjb25jYXQgdG8gbWFpbiBsb2dcbipcbiovXG5tb2R1bGUuZXhwb3J0cy5hZGRTYXNFcnJvcnMgPSBmdW5jdGlvbihlcnJvcnMpIHtcbiAgbG9ncy5zYXNFcnJvcnMgPSBsb2dzLnNhc0Vycm9ycy5jb25jYXQoZXJyb3JzKTtcblxuICB3aGlsZShsb2dzLnNhc0Vycm9ycy5sZW5ndGggPiBsaW1pdHMuc2FzRXJyb3JzKSB7XG4gICAgbG9ncy5zYXNFcnJvcnMuc2hpZnQoKTtcbiAgfVxufTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkge1xuICBsZXQgdGltZW91dCA9IDMwMDAwO1xuICBsZXQgdGltZW91dEhhbmRsZTtcblxuICBjb25zdCB4aHIgPSBmdW5jdGlvbiAodHlwZSwgdXJsLCBkYXRhLCBtdWx0aXBhcnRGb3JtRGF0YSwgaGVhZGVycyA9IHt9KSB7XG4gICAgY29uc3QgbWV0aG9kcyA9IHtcbiAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uICgpIHtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogZnVuY3Rpb24gKCkge1xuICAgICAgfVxuICAgIH07XG5cbiAgICBjb25zdCBYSFIgPSBYTUxIdHRwUmVxdWVzdDtcbiAgICBjb25zdCByZXF1ZXN0ID0gbmV3IFhIUignTVNYTUwyLlhNTEhUVFAuMy4wJyk7XG5cbiAgICByZXF1ZXN0Lm9wZW4odHlwZSwgdXJsLCB0cnVlKTtcblxuICAgIC8vbXVsdGlwYXJ0L2Zvcm0tZGF0YSBpcyBzZXQgYXV0b21hdGljYWxseSBzbyBubyBuZWVkIGZvciBlbHNlIGJsb2NrXG4gICAgLy8gQ29udGVudC1UeXBlIGhlYWRlciBoYXMgdG8gYmUgZXhwbGljaXRseSBzZXQgdXBcbiAgICBpZiAoIW11bHRpcGFydEZvcm1EYXRhKSB7XG4gICAgICBpZiAoaGVhZGVyc1snQ29udGVudC1UeXBlJ10pIHtcbiAgICAgICAgcmVxdWVzdC5zZXRSZXF1ZXN0SGVhZGVyKCdDb250ZW50LVR5cGUnLCBoZWFkZXJzWydDb250ZW50LVR5cGUnXSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcignQ29udGVudC1UeXBlJywgJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpO1xuICAgICAgfVxuICAgIH1cbiAgICBPYmplY3Qua2V5cyhoZWFkZXJzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBpZiAoa2V5ICE9PSAnQ29udGVudC1UeXBlJykge1xuICAgICAgICByZXF1ZXN0LnNldFJlcXVlc3RIZWFkZXIoa2V5LCBoZWFkZXJzW2tleV0pXG4gICAgICB9XG4gICAgfSlcbiAgICByZXF1ZXN0Lm9ucmVhZHlzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIGlmIChyZXF1ZXN0LnJlYWR5U3RhdGUgPT09IDQpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRIYW5kbGUpO1xuICAgICAgICBpZiAocmVxdWVzdC5zdGF0dXMgPj0gMjAwICYmIHJlcXVlc3Quc3RhdHVzIDwgMzAwKSB7XG4gICAgICAgICAgbWV0aG9kcy5zdWNjZXNzLmNhbGwobWV0aG9kcywgcmVxdWVzdCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbWV0aG9kcy5lcnJvci5jYWxsKG1ldGhvZHMsIHJlcXVlc3QpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIGlmICh0aW1lb3V0ID4gMCkge1xuICAgICAgdGltZW91dEhhbmRsZSA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICByZXF1ZXN0LmFib3J0KCk7XG4gICAgICB9LCB0aW1lb3V0KTtcbiAgICB9XG5cbiAgICByZXF1ZXN0LnNlbmQoZGF0YSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgICAgIG1ldGhvZHMuc3VjY2VzcyA9IGNhbGxiYWNrO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgIH0sXG4gICAgICBlcnJvcjogZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgICAgIG1ldGhvZHMuZXJyb3IgPSBjYWxsYmFjaztcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfTtcbiAgfTtcblxuICBjb25zdCBzZXJpYWxpemUgPSBmdW5jdGlvbiAob2JqKSB7XG4gICAgY29uc3Qgc3RyID0gW107XG4gICAgZm9yIChsZXQgcCBpbiBvYmopIHtcbiAgICAgIGlmIChvYmouaGFzT3duUHJvcGVydHkocCkpIHtcbiAgICAgICAgaWYgKG9ialtwXSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDAsIG4gPSBvYmpbcF0ubGVuZ3RoOyBpIDwgbjsgaSsrKSB7XG4gICAgICAgICAgICBzdHIucHVzaChlbmNvZGVVUklDb21wb25lbnQocCkgKyBcIj1cIiArIGVuY29kZVVSSUNvbXBvbmVudChvYmpbcF1baV0pKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RyLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHApICsgXCI9XCIgKyBlbmNvZGVVUklDb21wb25lbnQob2JqW3BdKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHN0ci5qb2luKFwiJlwiKTtcbiAgfTtcblxuICBjb25zdCBjcmVhdGVNdWx0aXBhcnRGb3JtRGF0YVBheWxvYWQgPSBmdW5jdGlvbiAob2JqKSB7XG4gICAgbGV0IGRhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICBmb3IgKGxldCBwIGluIG9iaikge1xuICAgICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShwKSkge1xuICAgICAgICBpZiAob2JqW3BdIGluc3RhbmNlb2YgQXJyYXkgJiYgcCAhPT0gJ2ZpbGUnKSB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDAsIG4gPSBvYmpbcF0ubGVuZ3RoOyBpIDwgbjsgaSsrKSB7XG4gICAgICAgICAgICBkYXRhLmFwcGVuZChwLCBvYmpbcF1baV0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChwID09PSAnZmlsZScpIHtcbiAgICAgICAgICBkYXRhLmFwcGVuZChwLCBvYmpbcF1bMF0sIG9ialtwXVsxXSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZGF0YS5hcHBlbmQocCwgb2JqW3BdKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZGF0YTtcbiAgfTtcblxuICByZXR1cm4ge1xuICAgIGdldDogZnVuY3Rpb24gKHVybCwgZGF0YSwgbXVsdGlwYXJ0Rm9ybURhdGEsIGhlYWRlcnMpIHtcbiAgICAgIGxldCBkYXRhU3RyO1xuICAgICAgaWYgKHR5cGVvZiBkYXRhID09PSAnb2JqZWN0Jykge1xuICAgICAgICBkYXRhU3RyID0gc2VyaWFsaXplKGRhdGEpO1xuICAgICAgfVxuICAgICAgY29uc3QgdXJsV2l0aFBhcmFtcyA9IGRhdGFTdHIgPyAodXJsICsgJz8nICsgZGF0YVN0cikgOiB1cmw7XG4gICAgICByZXR1cm4geGhyKCdHRVQnLCB1cmxXaXRoUGFyYW1zLCBudWxsLCBtdWx0aXBhcnRGb3JtRGF0YSwgaGVhZGVycyk7XG4gICAgfSxcblx0XHRwb3N0OiBmdW5jdGlvbih1cmwsIGRhdGEsIG11bHRpcGFydEZvcm1EYXRhLCBoZWFkZXJzKSB7XG4gICAgICBsZXQgcGF5bG9hZCA9IGRhdGE7XG4gICAgICBpZih0eXBlb2YgZGF0YSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgaWYobXVsdGlwYXJ0Rm9ybURhdGEpIHtcbiAgICAgICAgICBwYXlsb2FkID0gY3JlYXRlTXVsdGlwYXJ0Rm9ybURhdGFQYXlsb2FkKGRhdGEpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHBheWxvYWQgPSBzZXJpYWxpemUoZGF0YSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB4aHIoJ1BPU1QnLCB1cmwsIHBheWxvYWQsIG11bHRpcGFydEZvcm1EYXRhLCBoZWFkZXJzKTtcbiAgICB9LFxuICAgIHB1dDogZnVuY3Rpb24odXJsLCBkYXRhLCBtdWx0aXBhcnRGb3JtRGF0YSwgaGVhZGVycykge1xuICAgICAgbGV0IHBheWxvYWQgPSBkYXRhO1xuICAgICAgaWYodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnKSB7XG4gICAgICAgIGlmKG11bHRpcGFydEZvcm1EYXRhKSB7XG4gICAgICAgICAgcGF5bG9hZCA9IGNyZWF0ZU11bHRpcGFydEZvcm1EYXRhUGF5bG9hZChkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHhocignUFVUJywgdXJsLCBwYXlsb2FkLCBtdWx0aXBhcnRGb3JtRGF0YSwgaGVhZGVycyk7XG4gICAgfSxcblx0XHRkZWxldGU6IGZ1bmN0aW9uKHVybCwgcGF5bG9hZCwgbXVsdGlwYXJ0Rm9ybURhdGEsIGhlYWRlcnMpIHtcbiAgICBcdHJldHVybiB4aHIoJ0RFTEVURScsIHVybCwgcGF5bG9hZCwgbnVsbCwgaGVhZGVycyk7XG4gICAgfSxcbiAgICBwYXRjaDogZnVuY3Rpb24odXJsLCBkYXRhLCBtdWx0aXBhcnRGb3JtRGF0YSwgaGVhZGVycykge1xuICAgICAgbGV0IHBheWxvYWQgPSBkYXRhO1xuICAgICAgaWYodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnKSB7XG4gICAgICAgIGlmKG11bHRpcGFydEZvcm1EYXRhKSB7XG4gICAgICAgICAgcGF5bG9hZCA9IGNyZWF0ZU11bHRpcGFydEZvcm1EYXRhUGF5bG9hZChkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHhocignUEFUQ0gnLCB1cmwsIHBheWxvYWQsIG11bHRpcGFydEZvcm1EYXRhLCBoZWFkZXJzKTtcbiAgICB9LFxuICAgIHNldFRpbWVvdXQ6IGZ1bmN0aW9uICh0KSB7XG4gICAgICB0aW1lb3V0ID0gdDtcbiAgICB9LFxuICAgIHNlcmlhbGl6ZVxuICB9O1xufTtcbiIsImNvbnN0IGg1NHNFcnJvciA9IHJlcXVpcmUoJy4uL2Vycm9yLmpzJyk7XG5jb25zdCBsb2dzID0gcmVxdWlyZSgnLi4vbG9ncy5qcycpO1xuY29uc3QgVGFibGVzID0gcmVxdWlyZSgnLi4vdGFibGVzJyk7XG5jb25zdCBTYXNEYXRhID0gcmVxdWlyZSgnLi4vc2FzRGF0YS5qcycpO1xuY29uc3QgRmlsZXMgPSByZXF1aXJlKCcuLi9maWxlcycpO1xuXG4vKipcbiogQ2FsbCBTYXMgcHJvZ3JhbVxuKlxuKiBAcGFyYW0ge3N0cmluZ30gc2FzUHJvZ3JhbSAtIFBhdGggb2YgdGhlIHNhcyBwcm9ncmFtXG4qIEBwYXJhbSB7T2JqZWN0fSBkYXRhT2JqIC0gSW5zdGFuY2Ugb2YgVGFibGVzIG9iamVjdCB3aXRoIGRhdGEgYWRkZWRcbiogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgLSBDYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiBhamF4IGNhbGwgaXMgZmluaXNoZWRcbiogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyAtIG9iamVjdCBjb250YWluaW5nIGFkZGl0aW9uYWwgcHJvZ3JhbSBwYXJhbWV0ZXJzXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMuY2FsbCA9IGZ1bmN0aW9uIChzYXNQcm9ncmFtLCBkYXRhT2JqLCBjYWxsYmFjaywgcGFyYW1zKSB7XG5cdGNvbnN0IHNlbGYgPSB0aGlzO1xuXHRsZXQgcmV0cnlDb3VudCA9IDA7XG5cdGNvbnN0IGRiZyA9IHRoaXMuZGVidWdcblx0Y29uc3QgY3NyZiA9IHRoaXMuY3NyZjtcblxuXHRpZiAoIWNhbGxiYWNrIHx8IHR5cGVvZiBjYWxsYmFjayAhPT0gJ2Z1bmN0aW9uJykge1xuXHRcdHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnWW91IG11c3QgcHJvdmlkZSBhIGNhbGxiYWNrJyk7XG5cdH1cblx0aWYgKCFzYXNQcm9ncmFtKSB7XG5cdFx0dGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdZb3UgbXVzdCBwcm92aWRlIFNhcyBwcm9ncmFtIGZpbGUgcGF0aCcpO1xuXHR9XG5cdGlmICh0eXBlb2Ygc2FzUHJvZ3JhbSAhPT0gJ3N0cmluZycpIHtcblx0XHR0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ0ZpcnN0IHBhcmFtZXRlciBzaG91bGQgYmUgc3RyaW5nJyk7XG5cdH1cblx0aWYgKHRoaXMudXNlTXVsdGlwYXJ0Rm9ybURhdGEgPT09IGZhbHNlICYmICEoZGF0YU9iaiBpbnN0YW5jZW9mIFRhYmxlcykpIHtcblx0XHR0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ0Nhbm5vdCBzZW5kIGZpbGVzIHVzaW5nIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZC4gUGxlYXNlIHVzZSBUYWJsZXMgb3IgZGVmYXVsdCB2YWx1ZSBmb3IgdXNlTXVsdGlwYXJ0Rm9ybURhdGEnKTtcblx0fVxuXG5cdGlmICghcGFyYW1zKSB7XG5cdFx0cGFyYW1zID0ge1xuXHRcdFx0X3Byb2dyYW06IHRoaXMuX3V0aWxzLmdldEZ1bGxQcm9ncmFtUGF0aCh0aGlzLm1ldGFkYXRhUm9vdCwgc2FzUHJvZ3JhbSksXG5cdFx0XHRfZGVidWc6IHRoaXMuZGVidWcgPyAxMzEgOiAwLFxuXHRcdFx0X3NlcnZpY2U6ICdkZWZhdWx0Jyxcblx0XHRcdF9jc3JmOiBjc3JmXG5cdFx0fTtcblx0fSBlbHNlIHtcblx0XHRwYXJhbXMgPSBPYmplY3QuYXNzaWduKHt9LCBwYXJhbXMsIHtfY3NyZjogY3NyZn0pXG5cdH1cblxuXHRpZiAoZGF0YU9iaikge1xuXHRcdGxldCBrZXksIGRhdGFQcm92aWRlcjtcblx0XHRpZiAoZGF0YU9iaiBpbnN0YW5jZW9mIFRhYmxlcykge1xuXHRcdFx0ZGF0YVByb3ZpZGVyID0gZGF0YU9iai5fdGFibGVzO1xuXHRcdH0gZWxzZSBpZiAoZGF0YU9iaiBpbnN0YW5jZW9mIEZpbGVzIHx8IGRhdGFPYmogaW5zdGFuY2VvZiBTYXNEYXRhKSB7XG5cdFx0XHRkYXRhUHJvdmlkZXIgPSBkYXRhT2JqLl9maWxlcztcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y29uc29sZS5sb2cobmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdXcm9uZyB0eXBlIG9mIHRhYmxlcyBvYmplY3QnKSlcblx0XHR9XG5cdFx0Zm9yIChrZXkgaW4gZGF0YVByb3ZpZGVyKSB7XG5cdFx0XHRpZiAoZGF0YVByb3ZpZGVyLmhhc093blByb3BlcnR5KGtleSkpIHtcblx0XHRcdFx0cGFyYW1zW2tleV0gPSBkYXRhUHJvdmlkZXJba2V5XTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRpZiAodGhpcy5fZGlzYWJsZUNhbGxzKSB7XG5cdFx0dGhpcy5fcGVuZGluZ0NhbGxzLnB1c2goe1xuXHRcdFx0cGFyYW1zLFxuXHRcdFx0b3B0aW9uczoge1xuXHRcdFx0XHRzYXNQcm9ncmFtLFxuXHRcdFx0XHRkYXRhT2JqLFxuXHRcdFx0XHRjYWxsYmFja1xuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHRoaXMuX2FqYXgucG9zdCh0aGlzLnVybCwgcGFyYW1zLCB0aGlzLnVzZU11bHRpcGFydEZvcm1EYXRhKS5zdWNjZXNzKGFzeW5jIGZ1bmN0aW9uIChyZXMpIHtcblx0XHRpZiAoc2VsZi5fdXRpbHMubmVlZFRvTG9naW4uY2FsbChzZWxmLCByZXMpKSB7XG5cdFx0XHQvL3JlbWVtYmVyIHRoZSBjYWxsIGZvciBsYXR0ZXIgdXNlXG5cdFx0XHRzZWxmLl9wZW5kaW5nQ2FsbHMucHVzaCh7XG5cdFx0XHRcdHBhcmFtcyxcblx0XHRcdFx0b3B0aW9uczoge1xuXHRcdFx0XHRcdHNhc1Byb2dyYW0sXG5cdFx0XHRcdFx0ZGF0YU9iaixcblx0XHRcdFx0XHRjYWxsYmFja1xuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblxuXHRcdFx0Ly90aGVyZSdzIG5vIG5lZWQgdG8gY29udGludWUgaWYgcHJldmlvdXMgY2FsbCByZXR1cm5lZCBsb2dpbiBlcnJvclxuXHRcdFx0aWYgKHNlbGYuX2Rpc2FibGVDYWxscykge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRzZWxmLl9kaXNhYmxlQ2FsbHMgPSB0cnVlO1xuXHRcdFx0fVxuXG5cdFx0XHRjYWxsYmFjayhuZXcgaDU0c0Vycm9yKCdub3RMb2dnZWRpbkVycm9yJywgJ1lvdSBhcmUgbm90IGxvZ2dlZCBpbicpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bGV0IHJlc09iaiwgdW5lc2NhcGVkUmVzT2JqLCBlcnI7XG5cdFx0XHRsZXQgZG9uZSA9IGZhbHNlO1xuXG5cdFx0XHRpZiAoIWRiZykge1xuXHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdHJlc09iaiA9IHNlbGYuX3V0aWxzLnBhcnNlUmVzKHJlcy5yZXNwb25zZVRleHQsIHNhc1Byb2dyYW0sIHBhcmFtcyk7XG5cdFx0XHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZyhyZXNPYmoubG9nbWVzc2FnZSwgc2FzUHJvZ3JhbSk7XG5cblx0XHRcdFx0XHRpZiAoZGF0YU9iaiBpbnN0YW5jZW9mIFRhYmxlcykge1xuXHRcdFx0XHRcdFx0dW5lc2NhcGVkUmVzT2JqID0gc2VsZi5fdXRpbHMudW5lc2NhcGVWYWx1ZXMocmVzT2JqKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0dW5lc2NhcGVkUmVzT2JqID0gcmVzT2JqO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmIChyZXNPYmouc3RhdHVzICE9PSAnc3VjY2VzcycpIHtcblx0XHRcdFx0XHRcdGVyciA9IG5ldyBoNTRzRXJyb3IoJ3Byb2dyYW1FcnJvcicsIHJlc09iai5lcnJvcm1lc3NhZ2UsIHJlc09iai5zdGF0dXMpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGRvbmUgPSB0cnVlO1xuXHRcdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdFx0aWYgKGUgaW5zdGFuY2VvZiBTeW50YXhFcnJvcikge1xuXHRcdFx0XHRcdFx0aWYgKHJldHJ5Q291bnQgPCBzZWxmLm1heFhoclJldHJpZXMpIHtcblx0XHRcdFx0XHRcdFx0ZG9uZSA9IGZhbHNlO1xuXHRcdFx0XHRcdFx0XHRzZWxmLl9hamF4LnBvc3Qoc2VsZi51cmwsIHBhcmFtcywgc2VsZi51c2VNdWx0aXBhcnRGb3JtRGF0YSkuc3VjY2Vzcyh0aGlzLnN1Y2Nlc3MpLmVycm9yKHRoaXMuZXJyb3IpO1xuXHRcdFx0XHRcdFx0XHRyZXRyeUNvdW50Kys7XG5cdFx0XHRcdFx0XHRcdGxvZ3MuYWRkQXBwbGljYXRpb25Mb2coXCJSZXRyeWluZyAjXCIgKyByZXRyeUNvdW50LCBzYXNQcm9ncmFtKTtcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdHNlbGYuX3V0aWxzLnBhcnNlRXJyb3JSZXNwb25zZShyZXMucmVzcG9uc2VUZXh0LCBzYXNQcm9ncmFtKTtcblx0XHRcdFx0XHRcdFx0c2VsZi5fdXRpbHMuYWRkRmFpbGVkUmVzcG9uc2UocmVzLnJlc3BvbnNlVGV4dCwgc2FzUHJvZ3JhbSk7XG5cdFx0XHRcdFx0XHRcdGVyciA9IG5ldyBoNTRzRXJyb3IoJ3BhcnNlRXJyb3InLCAnVW5hYmxlIHRvIHBhcnNlIHJlc3BvbnNlIGpzb24nKTtcblx0XHRcdFx0XHRcdFx0ZG9uZSA9IHRydWU7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSBlbHNlIGlmIChlIGluc3RhbmNlb2YgaDU0c0Vycm9yKSB7XG5cdFx0XHRcdFx0XHRzZWxmLl91dGlscy5wYXJzZUVycm9yUmVzcG9uc2UocmVzLnJlc3BvbnNlVGV4dCwgc2FzUHJvZ3JhbSk7XG5cdFx0XHRcdFx0XHRzZWxmLl91dGlscy5hZGRGYWlsZWRSZXNwb25zZShyZXMucmVzcG9uc2VUZXh0LCBzYXNQcm9ncmFtKTtcblx0XHRcdFx0XHRcdGVyciA9IGU7XG5cdFx0XHRcdFx0XHRkb25lID0gdHJ1ZTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0c2VsZi5fdXRpbHMucGFyc2VFcnJvclJlc3BvbnNlKHJlcy5yZXNwb25zZVRleHQsIHNhc1Byb2dyYW0pO1xuXHRcdFx0XHRcdFx0c2VsZi5fdXRpbHMuYWRkRmFpbGVkUmVzcG9uc2UocmVzLnJlc3BvbnNlVGV4dCwgc2FzUHJvZ3JhbSk7XG5cdFx0XHRcdFx0XHRlcnIgPSBuZXcgaDU0c0Vycm9yKCd1bmtub3duRXJyb3InLCBlLm1lc3NhZ2UpO1xuXHRcdFx0XHRcdFx0ZXJyLnN0YWNrID0gZS5zdGFjaztcblx0XHRcdFx0XHRcdGRvbmUgPSB0cnVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSBmaW5hbGx5IHtcblx0XHRcdFx0XHRpZiAoZG9uZSkge1xuXHRcdFx0XHRcdFx0Y2FsbGJhY2soZXJyLCB1bmVzY2FwZWRSZXNPYmopO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRyZXNPYmogPSBhd2FpdCBzZWxmLl91dGlscy5wYXJzZURlYnVnUmVzKHJlcy5yZXNwb25zZVRleHQsIHNhc1Byb2dyYW0sIHBhcmFtcywgc2VsZi5ob3N0VXJsLCBzZWxmLmlzVml5YSk7XG5cdFx0XHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZyhyZXNPYmoubG9nbWVzc2FnZSwgc2FzUHJvZ3JhbSk7XG5cblx0XHRcdFx0XHRpZiAoZGF0YU9iaiBpbnN0YW5jZW9mIFRhYmxlcykge1xuXHRcdFx0XHRcdFx0dW5lc2NhcGVkUmVzT2JqID0gc2VsZi5fdXRpbHMudW5lc2NhcGVWYWx1ZXMocmVzT2JqKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0dW5lc2NhcGVkUmVzT2JqID0gcmVzT2JqO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmIChyZXNPYmouc3RhdHVzICE9PSAnc3VjY2VzcycpIHtcblx0XHRcdFx0XHRcdGVyciA9IG5ldyBoNTRzRXJyb3IoJ3Byb2dyYW1FcnJvcicsIHJlc09iai5lcnJvcm1lc3NhZ2UsIHJlc09iai5zdGF0dXMpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGRvbmUgPSB0cnVlO1xuXHRcdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdFx0aWYgKGUgaW5zdGFuY2VvZiBTeW50YXhFcnJvcikge1xuXHRcdFx0XHRcdFx0ZXJyID0gbmV3IGg1NHNFcnJvcigncGFyc2VFcnJvcicsIGUubWVzc2FnZSk7XG5cdFx0XHRcdFx0XHRkb25lID0gdHJ1ZTtcblx0XHRcdFx0XHR9IGVsc2UgaWYgKGUgaW5zdGFuY2VvZiBoNTRzRXJyb3IpIHtcblx0XHRcdFx0XHRcdGlmIChlLnR5cGUgPT09ICdwYXJzZUVycm9yJyAmJiByZXRyeUNvdW50IDwgMSkge1xuXHRcdFx0XHRcdFx0XHRkb25lID0gZmFsc2U7XG5cdFx0XHRcdFx0XHRcdHNlbGYuX2FqYXgucG9zdChzZWxmLnVybCwgcGFyYW1zLCBzZWxmLnVzZU11bHRpcGFydEZvcm1EYXRhKS5zdWNjZXNzKHRoaXMuc3VjY2VzcykuZXJyb3IodGhpcy5lcnJvcik7XG5cdFx0XHRcdFx0XHRcdHJldHJ5Q291bnQrKztcblx0XHRcdFx0XHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZyhcIlJldHJ5aW5nICNcIiArIHJldHJ5Q291bnQsIHNhc1Byb2dyYW0pO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0aWYgKGUgaW5zdGFuY2VvZiBoNTRzRXJyb3IpIHtcblx0XHRcdFx0XHRcdFx0XHRlcnIgPSBlO1xuXHRcdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHRcdGVyciA9IG5ldyBoNTRzRXJyb3IoJ3BhcnNlRXJyb3InLCAnVW5hYmxlIHRvIHBhcnNlIHJlc3BvbnNlIGpzb24nKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRkb25lID0gdHJ1ZTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0ZXJyID0gbmV3IGg1NHNFcnJvcigndW5rbm93bkVycm9yJywgZS5tZXNzYWdlKTtcblx0XHRcdFx0XHRcdGVyci5zdGFjayA9IGUuc3RhY2s7XG5cdFx0XHRcdFx0XHRkb25lID0gdHJ1ZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdFx0aWYgKGRvbmUpIHtcblx0XHRcdFx0XHRcdGNhbGxiYWNrKGVyciwgdW5lc2NhcGVkUmVzT2JqKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0pLmVycm9yKGZ1bmN0aW9uIChyZXMpIHtcblx0XHRsZXQgX2NzcmZcblx0XHRpZiAocmVzLnN0YXR1cyA9PT0gNDQ5IHx8IChyZXMuc3RhdHVzID09PSA0MDMgJiYgKHJlcy5yZXNwb25zZVRleHQuaW5jbHVkZXMoJ19jc3JmJykgfHwgcmVzLmdldFJlc3BvbnNlSGVhZGVyKCdYLUZvcmJpZGRlbi1SZWFzb24nKSA9PT0gJ0NTUkYnKSAmJiAoX2NzcmYgPSByZXMuZ2V0UmVzcG9uc2VIZWFkZXIocmVzLmdldFJlc3BvbnNlSGVhZGVyKCdYLUNTUkYtSEVBREVSJykpKSkpIHtcblx0XHRcdHBhcmFtc1snX2NzcmYnXSA9IF9jc3JmO1xuXHRcdFx0c2VsZi5jc3JmID0gX2NzcmZcblx0XHRcdGlmIChyZXRyeUNvdW50IDwgc2VsZi5tYXhYaHJSZXRyaWVzKSB7XG5cdFx0XHRcdHNlbGYuX2FqYXgucG9zdChzZWxmLnVybCwgcGFyYW1zLCB0cnVlKS5zdWNjZXNzKHRoaXMuc3VjY2VzcykuZXJyb3IodGhpcy5lcnJvcik7XG5cdFx0XHRcdHJldHJ5Q291bnQrKztcblx0XHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZyhcIlJldHJ5aW5nICNcIiArIHJldHJ5Q291bnQsIHNhc1Byb2dyYW0pO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0c2VsZi5fdXRpbHMucGFyc2VFcnJvclJlc3BvbnNlKHJlcy5yZXNwb25zZVRleHQsIHNhc1Byb2dyYW0pO1xuXHRcdFx0XHRzZWxmLl91dGlscy5hZGRGYWlsZWRSZXNwb25zZShyZXMucmVzcG9uc2VUZXh0LCBzYXNQcm9ncmFtKTtcblx0XHRcdFx0Y2FsbGJhY2sobmV3IGg1NHNFcnJvcigncGFyc2VFcnJvcicsICdVbmFibGUgdG8gcGFyc2UgcmVzcG9uc2UganNvbicpKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZygnUmVxdWVzdCBmYWlsZWQgd2l0aCBzdGF0dXM6ICcgKyByZXMuc3RhdHVzLCBzYXNQcm9ncmFtKTtcblx0XHRcdC8vIGlmIHJlcXVlc3QgaGFzIGVycm9yIHRleHQgZWxzZSBjYWxsYmFja1xuXHRcdFx0Y2FsbGJhY2sobmV3IGg1NHNFcnJvcignaHR0cEVycm9yJywgcmVzLnN0YXR1c1RleHQpKTtcblx0XHR9XG5cdH0pO1xufTtcblxuLyoqXG4qIExvZ2luIG1ldGhvZFxuKlxuKiBAcGFyYW0ge3N0cmluZ30gdXNlciAtIExvZ2luIHVzZXJuYW1lXG4qIEBwYXJhbSB7c3RyaW5nfSBwYXNzIC0gTG9naW4gcGFzc3dvcmRcbiogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgLSBDYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiBhamF4IGNhbGwgaXMgZmluaXNoZWRcbipcbiogT1JcbipcbiogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgLSBDYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiBhamF4IGNhbGwgaXMgZmluaXNoZWRcbipcbiovXG5tb2R1bGUuZXhwb3J0cy5sb2dpbiA9IGZ1bmN0aW9uICh1c2VyLCBwYXNzLCBjYWxsYmFjaykge1xuXHRpZiAoIXVzZXIgfHwgIXBhc3MpIHtcblx0XHR0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ0NyZWRlbnRpYWxzIG5vdCBzZXQnKTtcblx0fVxuXHRpZiAodHlwZW9mIHVzZXIgIT09ICdzdHJpbmcnIHx8IHR5cGVvZiBwYXNzICE9PSAnc3RyaW5nJykge1xuXHRcdHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnVXNlciBhbmQgcGFzcyBwYXJhbWV0ZXJzIG11c3QgYmUgc3RyaW5ncycpO1xuXHR9XG5cdC8vTk9URTogY2FsbGJhY2sgb3B0aW9uYWw/XG5cdGlmICghY2FsbGJhY2sgfHwgdHlwZW9mIGNhbGxiYWNrICE9PSAnZnVuY3Rpb24nKSB7XG5cdFx0dGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdZb3UgbXVzdCBwcm92aWRlIGNhbGxiYWNrJyk7XG5cdH1cblxuXHRpZiAoIXRoaXMuUkVTVGF1dGgpIHtcblx0XHRoYW5kbGVTYXNMb2dvbi5jYWxsKHRoaXMsIHVzZXIsIHBhc3MsIGNhbGxiYWNrKTtcblx0fSBlbHNlIHtcblx0XHRoYW5kbGVSZXN0TG9nb24uY2FsbCh0aGlzLCB1c2VyLCBwYXNzLCBjYWxsYmFjayk7XG5cdH1cbn07XG5cbi8qKlxuKiBNYW5hZ2VkUmVxdWVzdCBtZXRob2RcbipcbiogQHBhcmFtIHtzdHJpbmd9IGNhbGxNZXRob2QgLSBnZXQsIHBvc3QsXG4qIEBwYXJhbSB7c3RyaW5nfSBfdXJsIC0gVVJMIHRvIG1ha2UgcmVxdWVzdCB0b1xuKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIGNhbGxiYWNrIGZ1bmN0aW9uIGFzIGNhbGxiYWNrIHBhcmFtdGVyIGluIG9wdGlvbnMgb2JqZWN0IGlzIHJlcXVpcmVkXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMubWFuYWdlZFJlcXVlc3QgPSBmdW5jdGlvbiAoY2FsbE1ldGhvZCA9ICdnZXQnLCBfdXJsLCBvcHRpb25zID0ge1xuXHRjYWxsYmFjazogKCkgPT4gY29uc29sZS5sb2coJ01pc3NpbmcgY2FsbGJhY2sgZnVuY3Rpb24nKVxufSkge1xuXHRjb25zdCBzZWxmID0gdGhpcztcblx0Y29uc3QgY3NyZiA9IHRoaXMuY3NyZjtcblx0bGV0IHJldHJ5Q291bnQgPSAwO1xuXHRjb25zdCB7dXNlTXVsdGlwYXJ0Rm9ybURhdGEsIHNhc1Byb2dyYW0sIGRhdGFPYmosIHBhcmFtcywgY2FsbGJhY2ssIGhlYWRlcnN9ID0gb3B0aW9uc1xuXG5cdGlmIChzYXNQcm9ncmFtKSB7XG5cdFx0cmV0dXJuIHNlbGYuY2FsbChzYXNQcm9ncmFtLCBkYXRhT2JqLCBjYWxsYmFjaywgcGFyYW1zKVxuXHR9XG5cblx0bGV0IHVybCA9IF91cmxcblx0aWYgKCFfdXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xuXHRcdHVybCA9IHNlbGYuaG9zdFVybCArIF91cmxcblx0fVxuXG5cdGNvbnN0IF9oZWFkZXJzID0gT2JqZWN0LmFzc2lnbih7fSwgaGVhZGVycywge1xuXHRcdCdYLUNTUkYtVE9LRU4nOiBjc3JmXG5cdH0pXG5cdGNvbnN0IF9vcHRpb25zID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0aW9ucywge1xuXHRcdGhlYWRlcnM6IF9oZWFkZXJzXG5cdH0pXG5cblx0aWYgKHRoaXMuX2Rpc2FibGVDYWxscykge1xuXHRcdHRoaXMuX2N1c3RvbVBlbmRpbmdDYWxscy5wdXNoKHtcblx0XHRcdGNhbGxNZXRob2QsXG5cdFx0XHRfdXJsLFxuXHRcdFx0b3B0aW9uczogX29wdGlvbnNcblx0XHR9KTtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRzZWxmLl9hamF4W2NhbGxNZXRob2RdKHVybCwgcGFyYW1zLCB1c2VNdWx0aXBhcnRGb3JtRGF0YSwgX2hlYWRlcnMpLnN1Y2Nlc3MoZnVuY3Rpb24gKHJlcykge1xuXHRcdGlmIChzZWxmLl91dGlscy5uZWVkVG9Mb2dpbi5jYWxsKHNlbGYsIHJlcykpIHtcblx0XHRcdC8vcmVtZW1iZXIgdGhlIGNhbGwgZm9yIGxhdHRlciB1c2Vcblx0XHRcdHNlbGYuX2N1c3RvbVBlbmRpbmdDYWxscy5wdXNoKHtcblx0XHRcdFx0Y2FsbE1ldGhvZCxcblx0XHRcdFx0X3VybCxcblx0XHRcdFx0b3B0aW9uczogX29wdGlvbnNcblx0XHRcdH0pO1xuXG5cdFx0XHQvL3RoZXJlJ3Mgbm8gbmVlZCB0byBjb250aW51ZSBpZiBwcmV2aW91cyBjYWxsIHJldHVybmVkIGxvZ2luIGVycm9yXG5cdFx0XHRpZiAoc2VsZi5fZGlzYWJsZUNhbGxzKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHNlbGYuX2Rpc2FibGVDYWxscyA9IHRydWU7XG5cdFx0XHR9XG5cblx0XHRcdGNhbGxiYWNrKG5ldyBoNTRzRXJyb3IoJ25vdExvZ2dlZGluRXJyb3InLCAnWW91IGFyZSBub3QgbG9nZ2VkIGluJykpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRsZXQgcmVzT2JqLCBlcnI7XG5cdFx0XHRsZXQgZG9uZSA9IGZhbHNlO1xuXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCBhcnIgPSByZXMuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkuc3BsaXQoJ1xcclxcbicpO1xuXHRcdFx0XHRjb25zdCByZXNIZWFkZXJzID0gYXJyLnJlZHVjZShmdW5jdGlvbiAoYWNjLCBjdXJyZW50LCBpKSB7XG5cdFx0XHRcdFx0bGV0IHBhcnRzID0gY3VycmVudC5zcGxpdCgnOiAnKTtcblx0XHRcdFx0XHRhY2NbcGFydHNbMF1dID0gcGFydHNbMV07XG5cdFx0XHRcdFx0cmV0dXJuIGFjYztcblx0XHRcdFx0fSwge30pO1xuXHRcdFx0XHRsZXQgYm9keSA9IHJlcy5yZXNwb25zZVRleHRcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRib2R5ID0gSlNPTi5wYXJzZShib2R5KVxuXHRcdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdFx0Y29uc29sZS5sb2coJ3Jlc3BvbnNlIGlzIG5vdCBKU09OIHN0cmluZycpXG5cdFx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdFx0cmVzT2JqID0gT2JqZWN0LmFzc2lnbih7fSwge1xuXHRcdFx0XHRcdFx0aGVhZGVyczogcmVzSGVhZGVycyxcblx0XHRcdFx0XHRcdHN0YXR1czogcmVzLnN0YXR1cyxcblx0XHRcdFx0XHRcdHN0YXR1c1RleHQ6IHJlcy5zdGF0dXNUZXh0LFxuXHRcdFx0XHRcdFx0Ym9keVxuXHRcdFx0XHRcdH0pXG5cdFx0XHRcdFx0ZG9uZSA9IHRydWU7XG5cdFx0XHRcdH1cblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0ZXJyID0gbmV3IGg1NHNFcnJvcigndW5rbm93bkVycm9yJywgZS5tZXNzYWdlKTtcblx0XHRcdFx0ZXJyLnN0YWNrID0gZS5zdGFjaztcblx0XHRcdFx0ZG9uZSA9IHRydWU7XG5cblx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdGlmIChkb25lKSB7XG5cdFx0XHRcdFx0Y2FsbGJhY2soZXJyLCByZXNPYmopXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0pLmVycm9yKGZ1bmN0aW9uIChyZXMpIHtcblx0XHRsZXQgX2NzcmZcblx0XHRpZiAocmVzLnN0YXR1cyA9PSA0NDkgfHwgKHJlcy5zdGF0dXMgPT0gNDAzICYmIChyZXMucmVzcG9uc2VUZXh0LmluY2x1ZGVzKCdfY3NyZicpIHx8IHJlcy5nZXRSZXNwb25zZUhlYWRlcignWC1Gb3JiaWRkZW4tUmVhc29uJykgPT09ICdDU1JGJykgJiYgKF9jc3JmID0gcmVzLmdldFJlc3BvbnNlSGVhZGVyKHJlcy5nZXRSZXNwb25zZUhlYWRlcignWC1DU1JGLUhFQURFUicpKSkpKSB7XG5cdFx0XHRzZWxmLmNzcmYgPSBfY3NyZlxuXHRcdFx0Y29uc3QgX2hlYWRlcnMgPSBPYmplY3QuYXNzaWduKHt9LCBoZWFkZXJzLCB7W3Jlcy5nZXRSZXNwb25zZUhlYWRlcignWC1DU1JGLUhFQURFUicpXTogX2NzcmZ9KVxuXHRcdFx0aWYgKHJldHJ5Q291bnQgPCBzZWxmLm1heFhoclJldHJpZXMpIHtcblx0XHRcdFx0c2VsZi5fYWpheFtjYWxsTWV0aG9kXSh1cmwsIHBhcmFtcywgdXNlTXVsdGlwYXJ0Rm9ybURhdGEsIF9oZWFkZXJzKS5zdWNjZXNzKHRoaXMuc3VjY2VzcykuZXJyb3IodGhpcy5lcnJvcik7XG5cdFx0XHRcdHJldHJ5Q291bnQrKztcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNhbGxiYWNrKG5ldyBoNTRzRXJyb3IoJ3BhcnNlRXJyb3InLCAnVW5hYmxlIHRvIHBhcnNlIHJlc3BvbnNlIGpzb24nKSk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGxvZ3MuYWRkQXBwbGljYXRpb25Mb2coJ01hbmFnZWQgcmVxdWVzdCBmYWlsZWQgd2l0aCBzdGF0dXM6ICcgKyByZXMuc3RhdHVzLCBfdXJsKTtcblx0XHRcdC8vIGlmIHJlcXVlc3QgaGFzIGVycm9yIHRleHQgZWxzZSBjYWxsYmFja1xuXHRcdFx0Y2FsbGJhY2sobmV3IGg1NHNFcnJvcignaHR0cEVycm9yJywgcmVzLnJlc3BvbnNlVGV4dCwgcmVzLnN0YXR1cykpO1xuXHRcdH1cblx0fSk7XG59XG5cbi8qKlxuICogTG9nIG9uIHRvIFNBUyBpZiB3ZSBhcmUgYXNrZWQgdG9cbiAqIEBwYXJhbSB7U3RyaW5nfSB1c2VyIC0gVXNlcm5hbWUgb2YgdXNlclxuICogQHBhcmFtIHtTdHJpbmd9IHBhc3MgLSBQYXNzd29yZCBvZiB1c2VyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBjYWxsYmFjayAtIHdoYXQgdG8gZG8gYWZ0ZXJcbiAqL1xuZnVuY3Rpb24gaGFuZGxlU2FzTG9nb24odXNlciwgcGFzcywgY2FsbGJhY2spIHtcblx0Y29uc3Qgc2VsZiA9IHRoaXM7XG5cblx0Y29uc3QgbG9naW5QYXJhbXMgPSB7XG5cdFx0X3NlcnZpY2U6ICdkZWZhdWx0Jyxcblx0XHQvL2ZvciBTQVMgOS40LFxuXHRcdHVzZXJuYW1lOiB1c2VyLFxuXHRcdHBhc3N3b3JkOiBwYXNzXG5cdH07XG5cblx0Zm9yIChsZXQga2V5IGluIHRoaXMuX2FkaXRpb25hbExvZ2luUGFyYW1zKSB7XG5cdFx0bG9naW5QYXJhbXNba2V5XSA9IHRoaXMuX2FkaXRpb25hbExvZ2luUGFyYW1zW2tleV07XG5cdH1cblxuXHR0aGlzLl9sb2dpbkF0dGVtcHRzID0gMDtcblxuXHR0aGlzLl9hamF4LnBvc3QodGhpcy5sb2dpblVybCwgbG9naW5QYXJhbXMpXG5cdFx0LnN1Y2Nlc3MoaGFuZGxlU2FzTG9nb25TdWNjZXNzKVxuXHRcdC5lcnJvcihoYW5kbGVTYXNMb2dvbkVycm9yKTtcblxuXHRmdW5jdGlvbiBoYW5kbGVTYXNMb2dvbkVycm9yKHJlcykge1xuXHRcdGlmIChyZXMuc3RhdHVzID09IDQ0OSkge1xuXHRcdFx0aGFuZGxlU2FzTG9nb25TdWNjZXNzKHJlcyk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZygnTG9naW4gZmFpbGVkIHdpdGggc3RhdHVzIGNvZGU6ICcgKyByZXMuc3RhdHVzKTtcblx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0fVxuXG5cdGZ1bmN0aW9uIGhhbmRsZVNhc0xvZ29uU3VjY2VzcyhyZXMpIHtcblx0XHRpZiAoKytzZWxmLl9sb2dpbkF0dGVtcHRzID09PSAzKSB7XG5cdFx0XHRyZXR1cm4gY2FsbGJhY2soLTIpO1xuXHRcdH1cblx0XHRpZiAoc2VsZi5fdXRpbHMubmVlZFRvTG9naW4uY2FsbChzZWxmLCByZXMpKSB7XG5cdFx0XHQvL3dlIGFyZSBnZXR0aW5nIGZvcm0gYWdhaW4gYWZ0ZXIgcmVkaXJlY3Rcblx0XHRcdC8vYW5kIG5lZWQgdG8gbG9naW4gYWdhaW4gdXNpbmcgdGhlIG5ldyB1cmxcblx0XHRcdC8vX2xvZ2luQ2hhbmdlZCBpcyBzZXQgaW4gbmVlZFRvTG9naW4gZnVuY3Rpb25cblx0XHRcdC8vYnV0IGlmIGxvZ2luIHVybCBpcyBub3QgZGlmZmVyZW50LCB3ZSBhcmUgY2hlY2tpbmcgaWYgdGhlcmUgYXJlIGFkaXRpb25hbCBwYXJhbWV0ZXJzXG5cdFx0XHRpZiAoc2VsZi5fbG9naW5DaGFuZ2VkIHx8IChzZWxmLl9pc05ld0xvZ2luUGFnZSAmJiAhc2VsZi5fYWRpdGlvbmFsTG9naW5QYXJhbXMpKSB7XG5cdFx0XHRcdGRlbGV0ZSBzZWxmLl9sb2dpbkNoYW5nZWQ7XG5cdFx0XHRcdGNvbnN0IGlucHV0cyA9IHJlcy5yZXNwb25zZVRleHQubWF0Y2goLzxpbnB1dC4qXCJoaWRkZW5cIltePl0qPi9nKTtcblx0XHRcdFx0aWYgKGlucHV0cykge1xuXHRcdFx0XHRcdGlucHV0cy5mb3JFYWNoKGZ1bmN0aW9uIChpbnB1dFN0cikge1xuXHRcdFx0XHRcdFx0Y29uc3QgdmFsdWVNYXRjaCA9IGlucHV0U3RyLm1hdGNoKC9uYW1lPVwiKFteXCJdKilcIlxcc3ZhbHVlPVwiKFteXCJdKikvKTtcblx0XHRcdFx0XHRcdGxvZ2luUGFyYW1zW3ZhbHVlTWF0Y2hbMV1dID0gdmFsdWVNYXRjaFsyXTtcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRzZWxmLl9hamF4LnBvc3Qoc2VsZi5sb2dpblVybCwgbG9naW5QYXJhbXMpLnN1Y2Nlc3MoZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRcdC8vd2UgbmVlZCB0aGlzIGdldCByZXF1ZXN0IGJlY2F1c2Ugb2YgdGhlIHNhcyA5LjQgc2VjdXJpdHkgY2hlY2tzXG5cdFx0XHRcdFx0c2VsZi5fYWpheC5nZXQoc2VsZi51cmwpLnN1Y2Nlc3MoaGFuZGxlU2FzTG9nb25TdWNjZXNzKS5lcnJvcihoYW5kbGVTYXNMb2dvbkVycm9yKTtcblx0XHRcdFx0fSkuZXJyb3IoaGFuZGxlU2FzTG9nb25FcnJvcik7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0Ly9nZXR0aW5nIGZvcm0gYWdhaW4sIGJ1dCBpdCB3YXNuJ3QgYSByZWRpcmVjdFxuXHRcdFx0XHRsb2dzLmFkZEFwcGxpY2F0aW9uTG9nKCdXcm9uZyB1c2VybmFtZSBvciBwYXNzd29yZCcpO1xuXHRcdFx0XHRjYWxsYmFjaygtMSk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0c2VsZi5fZGlzYWJsZUNhbGxzID0gZmFsc2U7XG5cdFx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0XHRcdHdoaWxlIChzZWxmLl9wZW5kaW5nQ2FsbHMubGVuZ3RoID4gMCkge1xuXHRcdFx0XHRjb25zdCBwZW5kaW5nQ2FsbCA9IHNlbGYuX3BlbmRpbmdDYWxscy5zaGlmdCgpO1xuXHRcdFx0XHRjb25zdCBtZXRob2QgPSBwZW5kaW5nQ2FsbC5tZXRob2QgfHwgc2VsZi5jYWxsLmJpbmQoc2VsZik7XG5cdFx0XHRcdGNvbnN0IHNhc1Byb2dyYW0gPSBwZW5kaW5nQ2FsbC5vcHRpb25zLnNhc1Byb2dyYW07XG5cdFx0XHRcdGNvbnN0IGNhbGxiYWNrUGVuZGluZyA9IHBlbmRpbmdDYWxsLm9wdGlvbnMuY2FsbGJhY2s7XG5cdFx0XHRcdGNvbnN0IHBhcmFtcyA9IHBlbmRpbmdDYWxsLnBhcmFtcztcblx0XHRcdFx0Ly91cGRhdGUgZGVidWcgYmVjYXVzZSBpdCBtYXkgY2hhbmdlIGluIHRoZSBtZWFudGltZVxuXHRcdFx0XHRwYXJhbXMuX2RlYnVnID0gc2VsZi5kZWJ1ZyA/IDEzMSA6IDA7XG5cdFx0XHRcdGlmIChzZWxmLnJldHJ5QWZ0ZXJMb2dpbikge1xuXHRcdFx0XHRcdG1ldGhvZChzYXNQcm9ncmFtLCBudWxsLCBjYWxsYmFja1BlbmRpbmcsIHBhcmFtcyk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cbn1cbi8qKlxuICogUkVTVCBsb2dvbiBmb3IgOS40IHYxIHRpY2tldCBiYXNlZCBhdXRoXG4gKiBAcGFyYW0ge1N0cmluZ30gdXNlciAtXG4gKiBAcGFyYW0ge1N0cmluZ30gcGFzc1xuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2tcbiAqL1xuZnVuY3Rpb24gaGFuZGxlUmVzdExvZ29uKHVzZXIsIHBhc3MsIGNhbGxiYWNrKSB7XG5cdGNvbnN0IHNlbGYgPSB0aGlzO1xuXG5cdGNvbnN0IGxvZ2luUGFyYW1zID0ge1xuXHRcdHVzZXJuYW1lOiB1c2VyLFxuXHRcdHBhc3N3b3JkOiBwYXNzXG5cdH07XG5cblx0dGhpcy5fYWpheC5wb3N0KHRoaXMuUkVTVGF1dGhMb2dpblVybCwgbG9naW5QYXJhbXMpLnN1Y2Nlc3MoZnVuY3Rpb24gKHJlcykge1xuXHRcdGNvbnN0IGxvY2F0aW9uID0gcmVzLmdldFJlc3BvbnNlSGVhZGVyKCdMb2NhdGlvbicpO1xuXG5cdFx0c2VsZi5fYWpheC5wb3N0KGxvY2F0aW9uLCB7XG5cdFx0XHRzZXJ2aWNlOiBzZWxmLnVybFxuXHRcdH0pLnN1Y2Nlc3MoZnVuY3Rpb24gKHJlcykge1xuXHRcdFx0aWYgKHNlbGYudXJsLmluZGV4T2YoJz8nKSA9PT0gLTEpIHtcblx0XHRcdFx0c2VsZi51cmwgKz0gJz90aWNrZXQ9JyArIHJlcy5yZXNwb25zZVRleHQ7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRpZiAoc2VsZi51cmwuaW5kZXhPZigndGlja2V0JykgIT09IC0xKSB7XG5cdFx0XHRcdFx0c2VsZi51cmwgPSBzZWxmLnVybC5yZXBsYWNlKC90aWNrZXQ9W14mXSsvLCAndGlja2V0PScgKyByZXMucmVzcG9uc2VUZXh0KTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRzZWxmLnVybCArPSAnJnRpY2tldD0nICsgcmVzLnJlc3BvbnNlVGV4dDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0XHR9KS5lcnJvcihmdW5jdGlvbiAocmVzKSB7XG5cdFx0XHRsb2dzLmFkZEFwcGxpY2F0aW9uTG9nKCdMb2dpbiBmYWlsZWQgd2l0aCBzdGF0dXMgY29kZTogJyArIHJlcy5zdGF0dXMpO1xuXHRcdFx0Y2FsbGJhY2socmVzLnN0YXR1cyk7XG5cdFx0fSk7XG5cdH0pLmVycm9yKGZ1bmN0aW9uIChyZXMpIHtcblx0XHRpZiAocmVzLnJlc3BvbnNlVGV4dCA9PT0gJ2Vycm9yLmF1dGhlbnRpY2F0aW9uLmNyZWRlbnRpYWxzLmJhZCcpIHtcblx0XHRcdGNhbGxiYWNrKC0xKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bG9ncy5hZGRBcHBsaWNhdGlvbkxvZygnTG9naW4gZmFpbGVkIHdpdGggc3RhdHVzIGNvZGU6ICcgKyByZXMuc3RhdHVzKTtcblx0XHRcdGNhbGxiYWNrKHJlcy5zdGF0dXMpO1xuXHRcdH1cblx0fSk7XG59XG5cbi8qKlxuKiBMb2dvdXQgbWV0aG9kXG4qXG4qIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrIC0gQ2FsbGJhY2sgZnVuY3Rpb24gY2FsbGVkIHdoZW4gbG9nb3V0IGlzIGRvbmVcbipcbiovXG5cbm1vZHVsZS5leHBvcnRzLmxvZ291dCA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuXHRjb25zdCBiYXNlVXJsID0gdGhpcy5ob3N0VXJsIHx8ICcnO1xuXHRjb25zdCB1cmwgPSBiYXNlVXJsICsgdGhpcy5sb2dvdXRVcmw7XG5cblx0dGhpcy5fYWpheC5nZXQodXJsKS5zdWNjZXNzKGZ1bmN0aW9uIChyZXMpIHtcblx0XHR0aGlzLl9kaXNhYmxlQ2FsbHMgPSB0cnVlXG5cdFx0Y2FsbGJhY2soKTtcblx0fSkuZXJyb3IoZnVuY3Rpb24gKHJlcykge1xuXHRcdGxvZ3MuYWRkQXBwbGljYXRpb25Mb2coJ0xvZ291dCBmYWlsZWQgd2l0aCBzdGF0dXMgY29kZTogJyArIHJlcy5zdGF0dXMpO1xuXHRcdGNhbGxiYWNrKHJlcy5zdGF0dXMpO1xuXHR9KTtcbn07XG5cbi8qXG4qIEVudGVyIGRlYnVnIG1vZGVcbipcbiovXG5tb2R1bGUuZXhwb3J0cy5zZXREZWJ1Z01vZGUgPSBmdW5jdGlvbiAoKSB7XG5cdHRoaXMuZGVidWcgPSB0cnVlO1xufTtcblxuLypcbiogRXhpdCBkZWJ1ZyBtb2RlIGFuZCBjbGVhciBsb2dzXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMudW5zZXREZWJ1Z01vZGUgPSBmdW5jdGlvbiAoKSB7XG5cdHRoaXMuZGVidWcgPSBmYWxzZTtcbn07XG5cbmZvciAobGV0IGtleSBpbiBsb2dzLmdldCkge1xuXHRpZiAobG9ncy5nZXQuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuXHRcdG1vZHVsZS5leHBvcnRzW2tleV0gPSBsb2dzLmdldFtrZXldO1xuXHR9XG59XG5cbmZvciAobGV0IGtleSBpbiBsb2dzLmNsZWFyKSB7XG5cdGlmIChsb2dzLmNsZWFyLmhhc093blByb3BlcnR5KGtleSkpIHtcblx0XHRtb2R1bGUuZXhwb3J0c1trZXldID0gbG9ncy5jbGVhcltrZXldO1xuXHR9XG59XG5cbi8qXG4qIEFkZCBjYWxsYmFjayBmdW5jdGlvbnMgZXhlY3V0ZWQgd2hlbiBwcm9wZXJ0aWVzIGFyZSB1cGRhdGVkIHdpdGggcmVtb3RlIGNvbmZpZ1xuKlxuKkBjYWxsYmFjayAtIGNhbGxiYWNrIHB1c2hlZCB0byBhcnJheVxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLm9uUmVtb3RlQ29uZmlnVXBkYXRlID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG5cdHRoaXMucmVtb3RlQ29uZmlnVXBkYXRlQ2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xufTtcblxubW9kdWxlLmV4cG9ydHMuX3V0aWxzID0gcmVxdWlyZSgnLi91dGlscy5qcycpO1xuXG4vKipcbiAqIExvZ2luIGNhbGwgd2hpY2ggcmV0dXJucyBhIHByb21pc2VcbiAqIEBwYXJhbSB7U3RyaW5nfSB1c2VyIC0gVXNlcm5hbWVcbiAqIEBwYXJhbSB7U3RyaW5nfSBwYXNzIC0gUGFzc3dvcmRcbiAqL1xubW9kdWxlLmV4cG9ydHMucHJvbWlzZUxvZ2luID0gZnVuY3Rpb24gKHVzZXIsIHBhc3MpIHtcblx0cmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRpZiAoIXVzZXIgfHwgIXBhc3MpIHtcblx0XHRcdHJlamVjdChuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ0NyZWRlbnRpYWxzIG5vdCBzZXQnKSlcblx0XHR9XG5cdFx0aWYgKHR5cGVvZiB1c2VyICE9PSAnc3RyaW5nJyB8fCB0eXBlb2YgcGFzcyAhPT0gJ3N0cmluZycpIHtcblx0XHRcdHJlamVjdChuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1VzZXIgYW5kIHBhc3MgcGFyYW1ldGVycyBtdXN0IGJlIHN0cmluZ3MnKSlcblx0XHR9XG5cdFx0aWYgKCF0aGlzLlJFU1RhdXRoKSB7XG5cdFx0XHRjdXN0b21IYW5kbGVTYXNMb2dvbi5jYWxsKHRoaXMsIHVzZXIsIHBhc3MsIHJlc29sdmUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjdXN0b21IYW5kbGVSZXN0TG9nb24uY2FsbCh0aGlzLCB1c2VyLCBwYXNzLCByZXNvbHZlKTtcblx0XHR9XG5cdH0pXG59XG5cbi8qKlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSB1c2VyIC0gVXNlcm5hbWVcbiAqIEBwYXJhbSB7U3RyaW5nfSBwYXNzIC0gUGFzc3dvcmRcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrIC0gZnVuY3Rpb24gdG8gY2FsbCB3aGVuIHN1Y2Nlc3NmdWxcbiAqL1xuZnVuY3Rpb24gY3VzdG9tSGFuZGxlU2FzTG9nb24odXNlciwgcGFzcywgY2FsbGJhY2spIHtcblx0Y29uc3Qgc2VsZiA9IHRoaXM7XG5cdGxldCBsb2dpblBhcmFtcyA9IHtcblx0XHRfc2VydmljZTogJ2RlZmF1bHQnLFxuXHRcdC8vZm9yIFNBUyA5LjQsXG5cdFx0dXNlcm5hbWU6IHVzZXIsXG5cdFx0cGFzc3dvcmQ6IHBhc3Ncblx0fTtcblxuXHRmb3IgKGxldCBrZXkgaW4gdGhpcy5fYWRpdGlvbmFsTG9naW5QYXJhbXMpIHtcblx0XHRsb2dpblBhcmFtc1trZXldID0gdGhpcy5fYWRpdGlvbmFsTG9naW5QYXJhbXNba2V5XTtcblx0fVxuXG5cdHRoaXMuX2xvZ2luQXR0ZW1wdHMgPSAwO1xuXHRsb2dpblBhcmFtcyA9IHRoaXMuX2FqYXguc2VyaWFsaXplKGxvZ2luUGFyYW1zKVxuXG5cdHRoaXMuX2FqYXgucG9zdCh0aGlzLmxvZ2luVXJsLCBsb2dpblBhcmFtcylcblx0XHQuc3VjY2VzcyhoYW5kbGVTYXNMb2dvblN1Y2Nlc3MpXG5cdFx0LmVycm9yKGhhbmRsZVNhc0xvZ29uRXJyb3IpO1xuXG5cdGZ1bmN0aW9uIGhhbmRsZVNhc0xvZ29uRXJyb3IocmVzKSB7XG5cdFx0aWYgKHJlcy5zdGF0dXMgPT0gNDQ5KSB7XG5cdFx0XHRoYW5kbGVTYXNMb2dvblN1Y2Nlc3MocmVzKTtcblx0XHRcdC8vIHJlc29sdmUocmVzLnN0YXR1cyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGxvZ3MuYWRkQXBwbGljYXRpb25Mb2coJ0xvZ2luIGZhaWxlZCB3aXRoIHN0YXR1cyBjb2RlOiAnICsgcmVzLnN0YXR1cyk7XG5cdFx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiBoYW5kbGVTYXNMb2dvblN1Y2Nlc3MocmVzKSB7XG5cdFx0aWYgKCsrc2VsZi5fbG9naW5BdHRlbXB0cyA9PT0gMykge1xuXHRcdFx0Y2FsbGJhY2soLTIpO1xuXHRcdH1cblxuXHRcdGlmIChzZWxmLl91dGlscy5uZWVkVG9Mb2dpbi5jYWxsKHNlbGYsIHJlcykpIHtcblx0XHRcdC8vd2UgYXJlIGdldHRpbmcgZm9ybSBhZ2FpbiBhZnRlciByZWRpcmVjdFxuXHRcdFx0Ly9hbmQgbmVlZCB0byBsb2dpbiBhZ2FpbiB1c2luZyB0aGUgbmV3IHVybFxuXHRcdFx0Ly9fbG9naW5DaGFuZ2VkIGlzIHNldCBpbiBuZWVkVG9Mb2dpbiBmdW5jdGlvblxuXHRcdFx0Ly9idXQgaWYgbG9naW4gdXJsIGlzIG5vdCBkaWZmZXJlbnQsIHdlIGFyZSBjaGVja2luZyBpZiB0aGVyZSBhcmUgYWRpdGlvbmFsIHBhcmFtZXRlcnNcblx0XHRcdGlmIChzZWxmLl9sb2dpbkNoYW5nZWQgfHwgKHNlbGYuX2lzTmV3TG9naW5QYWdlICYmICFzZWxmLl9hZGl0aW9uYWxMb2dpblBhcmFtcykpIHtcblx0XHRcdFx0ZGVsZXRlIHNlbGYuX2xvZ2luQ2hhbmdlZDtcblx0XHRcdFx0Y29uc3QgaW5wdXRzID0gcmVzLnJlc3BvbnNlVGV4dC5tYXRjaCgvPGlucHV0LipcImhpZGRlblwiW14+XSo+L2cpO1xuXHRcdFx0XHRpZiAoaW5wdXRzKSB7XG5cdFx0XHRcdFx0aW5wdXRzLmZvckVhY2goZnVuY3Rpb24gKGlucHV0U3RyKSB7XG5cdFx0XHRcdFx0XHRjb25zdCB2YWx1ZU1hdGNoID0gaW5wdXRTdHIubWF0Y2goL25hbWU9XCIoW15cIl0qKVwiXFxzdmFsdWU9XCIoW15cIl0qKS8pO1xuXHRcdFx0XHRcdFx0bG9naW5QYXJhbXNbdmFsdWVNYXRjaFsxXV0gPSB2YWx1ZU1hdGNoWzJdO1xuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHNlbGYuX2FqYXgucG9zdChzZWxmLmxvZ2luVXJsLCBsb2dpblBhcmFtcykuc3VjY2VzcyhmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0aGFuZGxlU2FzTG9nb25TdWNjZXNzKClcblx0XHRcdFx0fSkuZXJyb3IoaGFuZGxlU2FzTG9nb25FcnJvcik7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0Ly9nZXR0aW5nIGZvcm0gYWdhaW4sIGJ1dCBpdCB3YXNuJ3QgYSByZWRpcmVjdFxuXHRcdFx0XHRsb2dzLmFkZEFwcGxpY2F0aW9uTG9nKCdXcm9uZyB1c2VybmFtZSBvciBwYXNzd29yZCcpO1xuXHRcdFx0XHRjYWxsYmFjaygtMSk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0c2VsZi5fZGlzYWJsZUNhbGxzID0gZmFsc2U7XG5cdFx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0XHRcdHdoaWxlIChzZWxmLl9jdXN0b21QZW5kaW5nQ2FsbHMubGVuZ3RoID4gMCkge1xuXHRcdFx0XHRjb25zdCBwZW5kaW5nQ2FsbCA9IHNlbGYuX2N1c3RvbVBlbmRpbmdDYWxscy5zaGlmdCgpXG5cdFx0XHRcdGNvbnN0IG1ldGhvZCA9IHBlbmRpbmdDYWxsLm1ldGhvZCB8fCBzZWxmLm1hbmFnZWRSZXF1ZXN0LmJpbmQoc2VsZik7XG5cdFx0XHRcdGNvbnN0IGNhbGxNZXRob2QgPSBwZW5kaW5nQ2FsbC5jYWxsTWV0aG9kXG5cdFx0XHRcdGNvbnN0IF91cmwgPSBwZW5kaW5nQ2FsbC5fdXJsXG5cdFx0XHRcdGNvbnN0IG9wdGlvbnMgPSBwZW5kaW5nQ2FsbC5vcHRpb25zO1xuXHRcdFx0XHQvL3VwZGF0ZSBkZWJ1ZyBiZWNhdXNlIGl0IG1heSBjaGFuZ2UgaW4gdGhlIG1lYW50aW1lXG5cdFx0XHRcdGlmIChvcHRpb25zLnBhcmFtcykge1xuXHRcdFx0XHRcdG9wdGlvbnMucGFyYW1zLl9kZWJ1ZyA9IHNlbGYuZGVidWcgPyAxMzEgOiAwO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmIChzZWxmLnJldHJ5QWZ0ZXJMb2dpbikge1xuXHRcdFx0XHRcdG1ldGhvZChjYWxsTWV0aG9kLCBfdXJsLCBvcHRpb25zKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHR3aGlsZSAoc2VsZi5fcGVuZGluZ0NhbGxzLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0Y29uc3QgcGVuZGluZ0NhbGwgPSBzZWxmLl9wZW5kaW5nQ2FsbHMuc2hpZnQoKTtcblx0XHRcdFx0Y29uc3QgbWV0aG9kID0gcGVuZGluZ0NhbGwubWV0aG9kIHx8IHNlbGYuY2FsbC5iaW5kKHNlbGYpO1xuXHRcdFx0XHRjb25zdCBzYXNQcm9ncmFtID0gcGVuZGluZ0NhbGwub3B0aW9ucy5zYXNQcm9ncmFtO1xuXHRcdFx0XHRjb25zdCBjYWxsYmFja1BlbmRpbmcgPSBwZW5kaW5nQ2FsbC5vcHRpb25zLmNhbGxiYWNrO1xuXHRcdFx0XHRjb25zdCBwYXJhbXMgPSBwZW5kaW5nQ2FsbC5wYXJhbXM7XG5cdFx0XHRcdC8vdXBkYXRlIGRlYnVnIGJlY2F1c2UgaXQgbWF5IGNoYW5nZSBpbiB0aGUgbWVhbnRpbWVcblx0XHRcdFx0cGFyYW1zLl9kZWJ1ZyA9IHNlbGYuZGVidWcgPyAxMzEgOiAwO1xuXHRcdFx0XHRpZiAoc2VsZi5yZXRyeUFmdGVyTG9naW4pIHtcblx0XHRcdFx0XHRtZXRob2Qoc2FzUHJvZ3JhbSwgbnVsbCwgY2FsbGJhY2tQZW5kaW5nLCBwYXJhbXMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9O1xufVxuXG4vKipcbiAqIFRvIGJlIHVzZWQgd2l0aCBmdXR1cmUgbWFuYWdlZCBtZXRhZGF0YSBjYWxsc1xuICogQHBhcmFtIHtTdHJpbmd9IHVzZXIgLSBVc2VybmFtZVxuICogQHBhcmFtIHtTdHJpbmd9IHBhc3MgLSBQYXNzd29yZFxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgLSB3aGF0IHRvIGNhbGwgYWZ0ZXJcbiAqIEBwYXJhbSB7U3RyaW5nfSBjYWxsYmFja1VybCAtIHdoZXJlIHRvIG5hdmlnYXRlIGFmdGVyIGdldHRpbmcgdGlja2V0XG4gKi9cbmZ1bmN0aW9uIGN1c3RvbUhhbmRsZVJlc3RMb2dvbih1c2VyLCBwYXNzLCBjYWxsYmFjaywgY2FsbGJhY2tVcmwpIHtcblx0Y29uc3Qgc2VsZiA9IHRoaXM7XG5cblx0Y29uc3QgbG9naW5QYXJhbXMgPSB7XG5cdFx0dXNlcm5hbWU6IHVzZXIsXG5cdFx0cGFzc3dvcmQ6IHBhc3Ncblx0fTtcblxuXHR0aGlzLl9hamF4LnBvc3QodGhpcy5SRVNUYXV0aExvZ2luVXJsLCBsb2dpblBhcmFtcykuc3VjY2VzcyhmdW5jdGlvbiAocmVzKSB7XG5cdFx0Y29uc3QgbG9jYXRpb24gPSByZXMuZ2V0UmVzcG9uc2VIZWFkZXIoJ0xvY2F0aW9uJyk7XG5cblx0XHRzZWxmLl9hamF4LnBvc3QobG9jYXRpb24sIHtcblx0XHRcdHNlcnZpY2U6IGNhbGxiYWNrVXJsXG5cdFx0fSkuc3VjY2VzcyhmdW5jdGlvbiAocmVzKSB7XG5cdFx0XHRpZiAoY2FsbGJhY2tVcmwuaW5kZXhPZignPycpID09PSAtMSkge1xuXHRcdFx0XHRjYWxsYmFja1VybCArPSAnP3RpY2tldD0nICsgcmVzLnJlc3BvbnNlVGV4dDtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmIChjYWxsYmFja1VybC5pbmRleE9mKCd0aWNrZXQnKSAhPT0gLTEpIHtcblx0XHRcdFx0XHRjYWxsYmFja1VybCA9IGNhbGxiYWNrVXJsLnJlcGxhY2UoL3RpY2tldD1bXiZdKy8sICd0aWNrZXQ9JyArIHJlcy5yZXNwb25zZVRleHQpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGNhbGxiYWNrVXJsICs9ICcmdGlja2V0PScgKyByZXMucmVzcG9uc2VUZXh0O1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGNhbGxiYWNrKHJlcy5zdGF0dXMpO1xuXHRcdH0pLmVycm9yKGZ1bmN0aW9uIChyZXMpIHtcblx0XHRcdGxvZ3MuYWRkQXBwbGljYXRpb25Mb2coJ0xvZ2luIGZhaWxlZCB3aXRoIHN0YXR1cyBjb2RlOiAnICsgcmVzLnN0YXR1cyk7XG5cdFx0XHRjYWxsYmFjayhyZXMuc3RhdHVzKTtcblx0XHR9KTtcblx0fSkuZXJyb3IoZnVuY3Rpb24gKHJlcykge1xuXHRcdGlmIChyZXMucmVzcG9uc2VUZXh0ID09PSAnZXJyb3IuYXV0aGVudGljYXRpb24uY3JlZGVudGlhbHMuYmFkJykge1xuXHRcdFx0Y2FsbGJhY2soLTEpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRsb2dzLmFkZEFwcGxpY2F0aW9uTG9nKCdMb2dpbiBmYWlsZWQgd2l0aCBzdGF0dXMgY29kZTogJyArIHJlcy5zdGF0dXMpO1xuXHRcdFx0Y2FsbGJhY2socmVzLnN0YXR1cyk7XG5cdFx0fVxuXHR9KTtcbn1cblxuXG4vLyBVdGlsaWxpdHkgZnVuY3Rpb25zIGZvciBoYW5kbGluZyBmaWxlcyBhbmQgZm9sZGVycyBvbiBWSVlBXG4vKipcbiAqIFJldHVybnMgdGhlIGRldGFpbHMgb2YgYSBmb2xkZXIgZnJvbSBmb2xkZXIgc2VydmljZVxuICogQHBhcmFtIHtTdHJpbmd9IGZvbGRlck5hbWUgLSBGdWxsIHBhdGggb2YgZm9sZGVyIHRvIGJlIGZvdW5kXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9wdGlvbnMgb2JqZWN0IGZvciBtYW5hZ2VkUmVxdWVzdFxuICovXG5tb2R1bGUuZXhwb3J0cy5nZXRGb2xkZXJEZXRhaWxzID0gZnVuY3Rpb24gKGZvbGRlck5hbWUsIG9wdGlvbnMpIHtcblx0Ly8gRmlyc3QgY2FsbCB0byBnZXQgZm9sZGVyJ3MgaWRcblx0bGV0IHVybCA9IFwiL2ZvbGRlcnMvZm9sZGVycy9AaXRlbT9wYXRoPVwiICsgZm9sZGVyTmFtZVxuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgnZ2V0JywgdXJsLCBvcHRpb25zKTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBkZXRhaWxzIG9mIGEgZmlsZSBmcm9tIGZpbGVzIHNlcnZpY2VcbiAqIEBwYXJhbSB7U3RyaW5nfSBmaWxlVXJpIC0gRnVsbCBwYXRoIG9mIGZpbGUgdG8gYmUgZm91bmRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0OiBjYWNoZUJ1c3QgZm9yY2VzIGJyb3dzZXIgdG8gZmV0Y2ggbmV3IGZpbGVcbiAqL1xubW9kdWxlLmV4cG9ydHMuZ2V0RmlsZURldGFpbHMgPSBmdW5jdGlvbiAoZmlsZVVyaSwgb3B0aW9ucykge1xuXHRjb25zdCBjYWNoZUJ1c3QgPSBvcHRpb25zLmNhY2hlQnVzdFxuXHRpZiAoY2FjaGVCdXN0KSB7XG5cdFx0ZmlsZVVyaSArPSAnP2NhY2hlQnVzdD0nICsgbmV3IERhdGUoKS5nZXRUaW1lKClcblx0fVxuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgnZ2V0JywgZmlsZVVyaSwgb3B0aW9ucyk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgY29udGVudHMgb2YgYSBmaWxlIGZyb20gZmlsZXMgc2VydmljZVxuICogQHBhcmFtIHtTdHJpbmd9IGZpbGVVcmkgLSBGdWxsIHBhdGggb2YgZmlsZSB0byBiZSBkb3dubG9hZGVkXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9wdGlvbnMgb2JqZWN0IGZvciBtYW5hZ2VkUmVxdWVzdDogY2FjaGVCdXN0IGZvcmNlcyBicm93c2VyIHRvIGZldGNoIG5ldyBmaWxlXG4gKi9cbm1vZHVsZS5leHBvcnRzLmdldEZpbGVDb250ZW50ID0gZnVuY3Rpb24gKGZpbGVVcmksIG9wdGlvbnMpIHtcblx0Y29uc3QgY2FjaGVCdXN0ID0gb3B0aW9ucy5jYWNoZUJ1c3Rcblx0bGV0IHVyaSA9IGZpbGVVcmkgKyAnL2NvbnRlbnQnXG5cdGlmIChjYWNoZUJ1c3QpIHtcblx0XHR1cmkgKz0gJz9jYWNoZUJ1c3Q9JyArIG5ldyBEYXRlKCkuZ2V0VGltZSgpXG5cdH1cblx0cmV0dXJuIHRoaXMubWFuYWdlZFJlcXVlc3QoJ2dldCcsIHVyaSwgb3B0aW9ucyk7XG59XG5cblxuLy8gVXRpbCBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBmaWxlcyBhbmQgZm9sZGVyc1xuLyoqXG4gKiBSZXR1cm5zIGRldGFpbHMgYWJvdXQgZm9sZGVyIGl0IHNlbGYgYW5kIGl0J3MgbWVtYmVycyB3aXRoIGRldGFpbHNcbiAqIEBwYXJhbSB7U3RyaW5nfSBmb2xkZXJOYW1lIC0gRnVsbCBwYXRoIG9mIGZvbGRlciB0byBiZSBmb3VuZFxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPcHRpb25zIG9iamVjdCBmb3IgbWFuYWdlZFJlcXVlc3RcbiAqL1xubW9kdWxlLmV4cG9ydHMuZ2V0Rm9sZGVyQ29udGVudHMgPSBhc3luYyBmdW5jdGlvbiAoZm9sZGVyTmFtZSwgb3B0aW9ucykge1xuXHRjb25zdCBzZWxmID0gdGhpc1xuXHRjb25zdCB7Y2FsbGJhY2t9ID0gb3B0aW9uc1xuXG5cdC8vIFNlY29uZCBjYWxsIHRvIGdldCBmb2xkZXIncyBtZW1lYmVyc1xuXHRjb25zdCBfY2FsbGJhY2sgPSAoZXJyLCBkYXRhKSA9PiB7XG5cdFx0Ly8gaGFuZGxlIGVycm9yIG9mIHRoZSBmaXJzdCBjYWxsXG5cdFx0aWYoZXJyKSB7XG5cdFx0XHRjYWxsYmFjayhlcnIsIGRhdGEpXG5cdFx0XHRyZXR1cm5cblx0XHR9XG5cdFx0bGV0IGlkID0gZGF0YS5ib2R5LmlkXG5cdFx0bGV0IG1lbWJlcnNVcmwgPSAnL2ZvbGRlcnMvZm9sZGVycy8nICsgaWQgKyAnL21lbWJlcnMnICsgJy8/bGltaXQ9MTAwMDAwMDAnO1xuXHRcdHJldHVybiBzZWxmLm1hbmFnZWRSZXF1ZXN0KCdnZXQnLCBtZW1iZXJzVXJsLCB7Y2FsbGJhY2t9KVxuXHR9XG5cblx0Ly8gRmlyc3QgY2FsbCB0byBnZXQgZm9sZGVyJ3MgaWRcblx0bGV0IHVybCA9IFwiL2ZvbGRlcnMvZm9sZGVycy9AaXRlbT9wYXRoPVwiICsgZm9sZGVyTmFtZVxuXHRjb25zdCBvcHRpb25zT2JqID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0aW9ucywge1xuXHRcdGNhbGxiYWNrOiBfY2FsbGJhY2tcblx0fSlcblx0dGhpcy5tYW5hZ2VkUmVxdWVzdCgnZ2V0JywgdXJsLCBvcHRpb25zT2JqKVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBmb2xkZXJcbiAqIEBwYXJhbSB7U3RyaW5nfSBwYXJlbnRVcmkgLSBUaGUgdXJpIG9mIHRoZSBmb2xkZXIgd2hlcmUgdGhlIG5ldyBjaGlsZCBpcyBiZWluZyBjcmVhdGVkXG4gKiBAcGFyYW0ge1N0cmluZ30gZm9sZGVyTmFtZSAtIEZ1bGwgcGF0aCBvZiBmb2xkZXIgdG8gYmUgZm91bmRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLmNyZWF0ZU5ld0ZvbGRlciA9IGZ1bmN0aW9uIChwYXJlbnRVcmksIGZvbGRlck5hbWUsIG9wdGlvbnMpIHtcblx0Y29uc3QgaGVhZGVycyA9IHtcblx0XHQnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24sIHRleHQvamF2YXNjcmlwdCwgKi8qOyBxPTAuMDEnLFxuXHRcdCdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG5cdH1cblxuXHRjb25zdCB1cmwgPSAnL2ZvbGRlcnMvZm9sZGVycz9wYXJlbnRGb2xkZXJVcmk9JyArIHBhcmVudFVyaTtcblx0Y29uc3QgZGF0YSA9IHtcblx0XHQnbmFtZSc6IGZvbGRlck5hbWUsXG5cdFx0J3R5cGUnOiBcImZvbGRlclwiXG5cdH1cblxuXHRjb25zdCBvcHRpb25zT2JqID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0aW9ucywge1xuXHRcdHBhcmFtczogSlNPTi5zdHJpbmdpZnkoZGF0YSksXG5cdFx0aGVhZGVycyxcblx0XHR1c2VNdWx0aXBhcnRGb3JtRGF0YTogZmFsc2Vcblx0fSlcblxuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgncG9zdCcsIHVybCwgb3B0aW9uc09iaik7XG59XG5cbi8qKlxuICogRGVsZXRlcyBhIGZvbGRlclxuICogQHBhcmFtIHtTdHJpbmd9IGZvbGRlcklkIC0gRnVsbCBVUkkgb2YgZm9sZGVyIHRvIGJlIGRlbGV0ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLmRlbGV0ZUZvbGRlckJ5SWQgPSBmdW5jdGlvbiAoZm9sZGVySWQsIG9wdGlvbnMpIHtcblx0Y29uc3QgdXJsID0gJy9mb2xkZXJzL2ZvbGRlcnMvJyArIGZvbGRlcklkO1xuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgnZGVsZXRlJywgdXJsLCBvcHRpb25zKVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBuZXcgZmlsZVxuICogQHBhcmFtIHtTdHJpbmd9IGZpbGVOYW1lIC0gTmFtZSBvZiB0aGUgZmlsZSBiZWluZyBjcmVhdGVkXG4gKiBAcGFyYW0ge1N0cmluZ30gZmlsZUJsb2IgLSBDb250ZW50IG9mIHRoZSBmaWxlXG4gKiBAcGFyYW0ge1N0cmluZ30gcGFyZW50Rk9sZGVyVXJpIC0gVVJJIG9mIHRoZSBwYXJlbnQgZm9sZGVyIHdoZXJlIHRoZSBmaWxlIGlzIHRvIGJlIGNyZWF0ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLmNyZWF0ZU5ld0ZpbGUgPSBmdW5jdGlvbiAoZmlsZU5hbWUsIGZpbGVCbG9iLCBwYXJlbnRGb2xkZXJVcmksIG9wdGlvbnMpIHtcblx0bGV0IHVybCA9IFwiL2ZpbGVzL2ZpbGVzI211bHRpcGFydFVwbG9hZFwiO1xuXHRsZXQgZGF0YU9iaiA9IHtcblx0XHRmaWxlOiBbZmlsZUJsb2IsIGZpbGVOYW1lXSxcblx0XHRwYXJlbnRGb2xkZXJVcmlcblx0fVxuXG5cdGNvbnN0IG9wdGlvbnNPYmogPSBPYmplY3QuYXNzaWduKHt9LCBvcHRpb25zLCB7XG5cdFx0cGFyYW1zOiBkYXRhT2JqLFxuXHRcdHVzZU11bHRpcGFydEZvcm1EYXRhOiB0cnVlLFxuXHR9KVxuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgncG9zdCcsIHVybCwgb3B0aW9uc09iaik7XG59XG5cbi8qKlxuICogR2VuZXJpYyBkZWxldGUgZnVuY3Rpb24gdGhhdCBkZWxldGVzIGJ5IFVSSVxuICogQHBhcmFtIHtTdHJpbmd9IGl0ZW1VcmkgLSBOYW1lIG9mIHRoZSBpdGVtIGJlaW5nIGRlbGV0ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLmRlbGV0ZUl0ZW0gPSBmdW5jdGlvbiAoaXRlbVVyaSwgb3B0aW9ucykge1xuXHRyZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgnZGVsZXRlJywgaXRlbVVyaSwgb3B0aW9ucylcbn1cblxuXG4vKipcbiAqIFVwZGF0ZXMgY29udGVudHMgb2YgYSBmaWxlXG4gKiBAcGFyYW0ge1N0cmluZ30gZmlsZU5hbWUgLSBOYW1lIG9mIHRoZSBmaWxlIGJlaW5nIHVwZGF0ZWRcbiAqIEBwYXJhbSB7T2JqZWN0IHwgQmxvYn0gZGF0YU9iaiAtIE5ldyBjb250ZW50IG9mIHRoZSBmaWxlIChPYmplY3QgbXVzdCBjb250YWluIGZpbGUga2V5KVxuICogT2JqZWN0IGV4YW1wbGUge1xuICogICBmaWxlOiBbPGJsb2I+LCA8ZmlsZU5hbWU+XVxuICogfVxuICogQHBhcmFtIHtTdHJpbmd9IGxhc3RNb2RpZmllZCAtIHRoZSBsYXN0LW1vZGlmaWVkIGhlYWRlciBzdHJpbmcgdGhhdCBtYXRjaGVzIHRoYXQgb2YgZmlsZSBiZWluZyBvdmVyd3JpdHRlblxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPcHRpb25zIG9iamVjdCBmb3IgbWFuYWdlZFJlcXVlc3RcbiAqL1xubW9kdWxlLmV4cG9ydHMudXBkYXRlRmlsZSA9IGZ1bmN0aW9uIChpdGVtVXJpLCBkYXRhT2JqLCBsYXN0TW9kaWZpZWQsIG9wdGlvbnMpIHtcblx0Y29uc3QgdXJsID0gaXRlbVVyaSArICcvY29udGVudCdcblx0Y29uc29sZS5sb2coJ1VSTCcsIHVybClcblx0bGV0IGhlYWRlcnMgPSB7XG5cdFx0J0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi92bmQuc2FzLmZpbGUnLFxuXHRcdCdJZi1Vbm1vZGlmaWVkLVNpbmNlJzogbGFzdE1vZGlmaWVkXG5cdH1cblx0Y29uc3QgaXNCbG9iID0gZGF0YU9iaiBpbnN0YW5jZW9mIEJsb2Jcblx0Y29uc3QgdXNlTXVsdGlwYXJ0Rm9ybURhdGEgPSAhaXNCbG9iIC8vIHNldCB1c2VNdWx0aXBhcnRGb3JtRGF0YSB0byB0cnVlIGlmIGRhdGFPYmogaXMgbm90IEJsb2JcblxuXHRjb25zdCBvcHRpb25zT2JqID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0aW9ucywge1xuXHRcdHBhcmFtczogZGF0YU9iaixcblx0XHRoZWFkZXJzLFxuXHRcdHVzZU11bHRpcGFydEZvcm1EYXRhXG5cdH0pXG5cdHJldHVybiB0aGlzLm1hbmFnZWRSZXF1ZXN0KCdwdXQnLCB1cmwsIG9wdGlvbnNPYmopO1xufVxuXG4vKipcbiBVcGRhdGVzIGZpbGUgTWV0YWRhdGEgXG4gKiBAcGFyYW0ge1N0cmluZ30gZmlsZU5hbWUgLSBOYW1lIG9mIHRoZSBmaWxlIGJlaW5nIHVwZGF0ZWRcbiAqIEBwYXJhbSB7U3RyaW5nfSBsYXN0TW9kaWZpZWQgLSB0aGUgbGFzdC1tb2RpZmllZCBoZWFkZXIgc3RyaW5nIHRoYXQgbWF0Y2hlcyB0aGF0IG9mIGZpbGUgYmVpbmcgdXBkYXRlZFxuICogQHBhcmFtIHtPYmplY3QgfCBCbG9ifSBkYXRhT2JqIC0gb2JqZWN0cyBjb250YWluaW5nIHRoZSBmaWVsZHMgdGhhdCBhcmUgYmVpbmcgY2hhbmdlZFxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPcHRpb25zIG9iamVjdCBmb3IgbWFuYWdlZFJlcXVlc3RcbiAqL1xubW9kdWxlLmV4cG9ydHMudXBkYXRlRmlsZU1ldGFkYXRhID0gZnVuY3Rpb24gKGl0ZW1VcmksIGRhdGFPYmosIGxhc3RNb2RpZmllZCwgb3B0aW9ucykge1xuICBsZXQgaGVhZGVycyA9IHtcbiAgICAnQ29udGVudC1UeXBlJzonYXBwbGljYXRpb24vdm5kLnNhcy5maWxlK2pzb24nLFxuXHRcdCdJZi1Vbm1vZGlmaWVkLVNpbmNlJzogbGFzdE1vZGlmaWVkXG4gIH1cbiAgY29uc3QgaXNCbG9iID0gZGF0YU9iaiBpbnN0YW5jZW9mIEJsb2JcbiAgY29uc3QgdXNlTXVsdGlwYXJ0Rm9ybURhdGEgPSAhaXNCbG9iIC8vIHNldCB1c2VNdWx0aXBhcnRGb3JtRGF0YSB0byB0cnVlIGlmIGRhdGFPYmogaXMgbm90IEJsb2JcbiAgXG4gIGNvbnN0IG9wdGlvbnNPYmogPSBPYmplY3QuYXNzaWduKHt9LCBvcHRpb25zLCB7XG4gICAgcGFyYW1zOiBkYXRhT2JqLFxuICAgIGhlYWRlcnMsXG4gICAgdXNlTXVsdGlwYXJ0Rm9ybURhdGFcbiAgfSlcblxuICByZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgncGF0Y2gnLCBpdGVtVXJpLCBvcHRpb25zT2JqKTtcbn1cblxuLyoqXG4gKiBVcGRhdGVzIGZvbGRlciBpbmZvXG4gKiBAcGFyYW0ge1N0cmluZ30gZm9sZGVyVXJpIC0gdXJpIG9mIHRoZSBmb2xkZXIgdGhhdCBpcyBiZWluZyBjaGFuZ2VkXG4gKiBAcGFyYW0ge1N0cmluZ30gbGFzdE1vZGlmaWVkIC0gdGhlIGxhc3QtbW9kaWZpZWQgaGVhZGVyIHN0cmluZyB0aGF0IG1hdGNoZXMgdGhhdCBvZiB0aGUgZm9sZGVyIGJlaW5nIHVwZGF0ZWRcbiAqIEBwYXJhbSB7T2JqZWN0IHwgQmxvYn0gZGF0YU9iaiAtIG9iamVjdCB0aGF0cyBpcyBlaXRoZXIgdGhlIHdob2xlIGZvbGRlciBvciBwYXJ0aWFsIGRhdGFcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3B0aW9ucyBvYmplY3QgZm9yIG1hbmFnZWRSZXF1ZXN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLnVwZGF0ZUZvbGRlck1ldGFkYXRhID0gZnVuY3Rpb24gKGZvbGRlclVyaSwgZGF0YU9iaiwgbGFzdE1vZGlmaWVkLCBvcHRpb25zKSB7XG5cbiAgLyoqXG4gICAgQGNvbnN0YW50IHtCb29sZWFufSBwYXJ0aWFsRGF0YSAtIGluZGljYXRlcyB3ZXRoZXIgZGF0YU9iaiBjb250YWludHMgYWxsIHRoZSBkYXRhIHRoYXQgbmVlZHMgdG8gYmUgc2VuZCB0byB0aGUgc2VydmVyXG4gICAgb3IgcGFydGlhbCBkYXRhIHdoaWNoIGNvbnRhdGlucyBvbmx5IHRoZSBmaWVsZHMgdGhhdCBuZWVkIHRvIGJlIHVwZGF0ZWQsIGluIHdoaWNoIGNhc2UgYSBjYWxsIG5lZWRzIHRvIGJlIG1hZGUgdG8gdGhlIHNlcnZlciBmb3IgXG4gICAgdGhlIHJlc3Qgb2YgdGhlIGRhdGEgYmVmb3JlIHRoZSB1cGRhdGUgY2FuIGJlIGRvbmVcbiAgICovXG4gIGNvbnN0IHtwYXJ0aWFsRGF0YX0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IGhlYWRlcnMgPSB7XG4gICAgJ0NvbnRlbnQtVHlwZSc6IFwiYXBwbGljYXRpb24vdm5kLnNhcy5jb250ZW50LmZvbGRlcitqc29uXCIsXG4gICAgJ0lmLVVubW9kaWZpZWQtU2luY2UnOiBsYXN0TW9kaWZpZWQsXG4gIH1cblxuICBpZiAocGFydGlhbERhdGEpIHtcblxuICAgIGNvbnN0IF9jYWxsYmFjayA9IChlcnIsIHJlcykgPT4ge1xuICAgICAgaWYgKHJlcykge1xuXG4gICAgICAgIGNvbnN0IGZvbGRlciA9IE9iamVjdC5hc3NpZ24oe30sIHJlcy5ib2R5LCBkYXRhT2JqKTtcblxuICAgICAgICBsZXQgZm9yQmxvYiA9IEpTT04uc3RyaW5naWZ5KGZvbGRlcik7XG4gICAgICAgIGxldCBkYXRhID0gbmV3IEJsb2IoW2ZvckJsb2JdLCB7dHlwZTogXCJvY3RldC9zdHJlYW1cIn0pO1xuXG4gICAgICAgIGNvbnN0IG9wdGlvbnNPYmogPSBPYmplY3QuYXNzaWduKHt9LCBvcHRpb25zLCB7XG4gICAgICAgICAgcGFyYW1zOiBkYXRhLFxuICAgICAgICAgIGhlYWRlcnMsXG4gICAgICAgICAgdXNlTXVsdGlwYXJ0Rm9ybURhdGEgOiBmYWxzZSxcbiAgICAgICAgfSlcblxuICAgICAgICByZXR1cm4gdGhpcy5tYW5hZ2VkUmVxdWVzdCgncHV0JywgZm9sZGVyVXJpLCBvcHRpb25zT2JqKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgcmV0dXJuIG9wdGlvbnMuY2FsbGJhY2soZXJyKTtcbiAgICB9XG4gICAgY29uc3QgZ2V0T3B0aW9uc09iaiA9IE9iamVjdC5hc3NpZ24oe30sIG9wdGlvbnMsIHtcbiAgICAgIGhlYWRlcnM6IHsnQ29udGVudC1UeXBlJzogXCJhcHBsaWNhdGlvbi92bmQuc2FzLmNvbnRlbnQuZm9sZGVyK2pzb25cIn0sXG4gICAgICBjYWxsYmFjazogX2NhbGxiYWNrXG4gICAgfSlcblxuICAgIHJldHVybiB0aGlzLm1hbmFnZWRSZXF1ZXN0KCdnZXQnLCBmb2xkZXJVcmksIGdldE9wdGlvbnNPYmopO1xuICB9XG4gIGVsc2Uge1xuICAgIGlmICggIShkYXRhT2JqIGluc3RhbmNlb2YgQmxvYikpIHtcbiAgICAgIGxldCBmb3JCbG9iID0gSlNPTi5zdHJpbmdpZnkoZGF0YU9iaik7XG4gICAgICBkYXRhT2JqID0gbmV3IEJsb2IoW2ZvckJsb2JdLCB7dHlwZTogXCJvY3RldC9zdHJlYW1cIn0pO1xuICAgIH1cblxuICAgIGNvbnN0IG9wdGlvbnNPYmogPSBPYmplY3QuYXNzaWduKHt9LCBvcHRpb25zLCB7XG4gICAgICBwYXJhbXM6IGRhdGFPYmosXG4gICAgICBoZWFkZXJzLFxuICAgICAgdXNlTXVsdGlwYXJ0Rm9ybURhdGEgOiBmYWxzZSxcbiAgICB9KVxuICAgIHJldHVybiB0aGlzLm1hbmFnZWRSZXF1ZXN0KCdwdXQnLCBmb2xkZXJVcmksIG9wdGlvbnNPYmopO1xuICB9XG59IiwiY29uc3QgbG9ncyA9IHJlcXVpcmUoJy4uL2xvZ3MuanMnKTtcbmNvbnN0IGg1NHNFcnJvciA9IHJlcXVpcmUoJy4uL2Vycm9yLmpzJyk7XG5cbmNvbnN0IHByb2dyYW1Ob3RGb3VuZFBhdHQgPSAvPHRpdGxlPihTdG9yZWQgUHJvY2VzcyBFcnJvcnxTQVNTdG9yZWRQcm9jZXNzKTxcXC90aXRsZT5bXFxzXFxTXSo8aDI+KFN0b3JlZCBwcm9jZXNzIG5vdCBmb3VuZDouKnwuKm5vdCBhIHZhbGlkIHN0b3JlZCBwcm9jZXNzIHBhdGguKTxcXC9oMj4vO1xuY29uc3QgYmFkSm9iRGVmaW5pdGlvbiA9IFwiPGgyPlBhcmFtZXRlciBFcnJvciA8YnIvPlVuYWJsZSB0byBnZXQgam9iIGRlZmluaXRpb24uPC9oMj5cIjtcblxuY29uc3QgcmVzcG9uc2VSZXBsYWNlID0gZnVuY3Rpb24ocmVzKSB7XG4gIHJldHVybiByZXNcbn07XG5cbi8qKlxuKiBQYXJzZSByZXNwb25zZSBmcm9tIHNlcnZlclxuKlxuKiBAcGFyYW0ge29iamVjdH0gcmVzcG9uc2VUZXh0IC0gcmVzcG9uc2UgaHRtbCBmcm9tIHRoZSBzZXJ2ZXJcbiogQHBhcmFtIHtzdHJpbmd9IHNhc1Byb2dyYW0gLSBzYXMgcHJvZ3JhbSBwYXRoXG4qIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgLSBwYXJhbXMgc2VudCB0byBzYXMgcHJvZ3JhbSB3aXRoIGFkZFRhYmxlXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMucGFyc2VSZXMgPSBmdW5jdGlvbihyZXNwb25zZVRleHQsIHNhc1Byb2dyYW0sIHBhcmFtcykge1xuICBjb25zdCBtYXRjaGVzID0gcmVzcG9uc2VUZXh0Lm1hdGNoKHByb2dyYW1Ob3RGb3VuZFBhdHQpO1xuICBpZihtYXRjaGVzKSB7XG4gICAgdGhyb3cgbmV3IGg1NHNFcnJvcigncHJvZ3JhbU5vdEZvdW5kJywgJ1lvdSBoYXZlIG5vdCBiZWVuIGdyYW50ZWQgcGVybWlzc2lvbiB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLCBvciB0aGUgU1RQIGlzIG1pc3NpbmcuJyk7XG4gIH1cbiAgLy9yZW1vdmUgbmV3IGxpbmVzIGluIGpzb24gcmVzcG9uc2VcbiAgLy9yZXBsYWNlIFxcXFwoZCkgd2l0aCBcXChkKSAtIFNBUyBqc29uIHBhcnNlciBpcyBlc2NhcGluZyBpdFxuICByZXR1cm4gSlNPTi5wYXJzZShyZXNwb25zZVJlcGxhY2UocmVzcG9uc2VUZXh0KSk7XG59O1xuXG4vKipcbiogUGFyc2UgcmVzcG9uc2UgZnJvbSBzZXJ2ZXIgaW4gZGVidWcgbW9kZVxuKlxuKiBAcGFyYW0ge29iamVjdH0gcmVzcG9uc2VUZXh0IC0gcmVzcG9uc2UgaHRtbCBmcm9tIHRoZSBzZXJ2ZXJcbiogQHBhcmFtIHtzdHJpbmd9IHNhc1Byb2dyYW0gLSBzYXMgcHJvZ3JhbSBwYXRoXG4qIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgLSBwYXJhbXMgc2VudCB0byBzYXMgcHJvZ3JhbSB3aXRoIGFkZFRhYmxlXG4qIEBwYXJhbSB7c3RyaW5nfSBob3N0VXJsIC0gc2FtZSBhcyBpbiBoNTRzIGNvbnN0cnVjdG9yXG4qIEBwYXJhbSB7Ym9vbH0gaXNWaXlhIC0gc2FtZSBhcyBpbiBoNTRzIGNvbnN0cnVjdG9yXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMucGFyc2VEZWJ1Z1JlcyA9IGZ1bmN0aW9uIChyZXNwb25zZVRleHQsIHNhc1Byb2dyYW0sIHBhcmFtcywgaG9zdFVybCwgaXNWaXlhKSB7XG5cdGNvbnN0IHNlbGYgPSB0aGlzXG5cdGxldCBtYXRjaGVzID0gcmVzcG9uc2VUZXh0Lm1hdGNoKHByb2dyYW1Ob3RGb3VuZFBhdHQpO1xuXHRpZiAobWF0Y2hlcykge1xuXHRcdHRocm93IG5ldyBoNTRzRXJyb3IoJ3Byb2dyYW1Ob3RGb3VuZCcsICdTYXMgcHJvZ3JhbSBjb21wbGV0ZWQgd2l0aCBlcnJvcnMnKTtcblx0fVxuXG5cdGlmIChpc1ZpeWEpIHtcblx0XHRjb25zdCBtYXRjaGVzV3JvbmdKb2IgPSByZXNwb25zZVRleHQubWF0Y2goYmFkSm9iRGVmaW5pdGlvbik7XG5cdFx0aWYgKG1hdGNoZXNXcm9uZ0pvYikge1xuXHRcdFx0dGhyb3cgbmV3IGg1NHNFcnJvcigncHJvZ3JhbU5vdEZvdW5kJywgJ1NhcyBwcm9ncmFtIGNvbXBsZXRlZCB3aXRoIGVycm9ycy4gVW5hYmxlIHRvIGdldCBqb2IgZGVmaW5pdGlvbi4nKTtcblx0XHR9XG5cdH1cblxuXHQvL2ZpbmQganNvblxuXHRsZXQgcGF0dCA9IGlzVml5YSA/IC9eKC4/PGlmcmFtZS4qc3JjPVwiKShbXlwiXSspKC4qaWZyYW1lPikvbSA6IC9eKC4/LS1oNTRzLWRhdGEtc3RhcnQtLSkoW1xcU1xcc10qPykoLS1oNTRzLWRhdGEtZW5kLS0pL207XG5cdG1hdGNoZXMgPSByZXNwb25zZVRleHQubWF0Y2gocGF0dCk7XG5cblx0Y29uc3QgcGFnZSA9IHJlc3BvbnNlVGV4dC5yZXBsYWNlKHBhdHQsICcnKTtcblx0Y29uc3QgaHRtbEJvZHlQYXR0ID0gLzxib2R5Lio+KFtcXHNcXFNdKik8XFwvYm9keT4vO1xuXHRjb25zdCBib2R5TWF0Y2hlcyA9IHBhZ2UubWF0Y2goaHRtbEJvZHlQYXR0KTtcblx0Ly9yZW1vdmUgaHRtbCB0YWdzXG5cdGxldCBkZWJ1Z1RleHQgPSBib2R5TWF0Y2hlc1sxXS5yZXBsYWNlKC88W14+XSo+L2csICcnKTtcblx0ZGVidWdUZXh0ID0gdGhpcy5kZWNvZGVIVE1MRW50aXRpZXMoZGVidWdUZXh0KTtcblxuXHRsb2dzLmFkZERlYnVnRGF0YShib2R5TWF0Y2hlc1sxXSwgZGVidWdUZXh0LCBzYXNQcm9ncmFtLCBwYXJhbXMpO1xuXG4gIGlmIChpc1ZpeWEgJiYgdGhpcy5wYXJzZUVycm9yUmVzcG9uc2UocmVzcG9uc2VUZXh0LCBzYXNQcm9ncmFtKSkge1xuXHRcdHRocm93IG5ldyBoNTRzRXJyb3IoJ3Nhc0Vycm9yJywgJ1NhcyBwcm9ncmFtIGNvbXBsZXRlZCB3aXRoIGVycm9ycycpO1xuXHR9XG5cdGlmICghbWF0Y2hlcykge1xuXHRcdHRocm93IG5ldyBoNTRzRXJyb3IoJ3BhcnNlRXJyb3InLCAnVW5hYmxlIHRvIHBhcnNlIHJlc3BvbnNlIGpzb24nKTtcblx0fVxuXG5cblx0Y29uc3QgcHJvbWlzZSA9IG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcblx0XHRsZXQganNvbk9ialxuXHRcdGlmIChpc1ZpeWEpIHtcblx0XHRcdGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuXHRcdFx0Y29uc3QgYmFzZVVybCA9IGhvc3RVcmwgfHwgXCJcIjtcblx0XHRcdHhoci5vcGVuKFwiR0VUXCIsIGJhc2VVcmwgKyBtYXRjaGVzWzJdKTtcblx0XHRcdHhoci5vbmxvYWQgPSBmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdGlmICh0aGlzLnN0YXR1cyA+PSAyMDAgJiYgdGhpcy5zdGF0dXMgPCAzMDApIHtcblx0XHRcdFx0XHRyZXNvbHZlKEpTT04ucGFyc2UoeGhyLnJlc3BvbnNlVGV4dC5yZXBsYWNlKC8oXFxyXFxufFxccnxcXG4pL2csICcnKSkpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHJlamVjdChuZXcgaDU0c0Vycm9yKCdmZXRjaEVycm9yJywgeGhyLnN0YXR1c1RleHQsIHRoaXMuc3RhdHVzKSlcblx0XHRcdFx0fVxuXHRcdFx0fTtcblx0XHRcdHhoci5vbmVycm9yID0gZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRyZWplY3QobmV3IGg1NHNFcnJvcignZmV0Y2hFcnJvcicsIHhoci5zdGF0dXNUZXh0KSlcblx0XHRcdH07XG5cdFx0XHR4aHIuc2VuZCgpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRqc29uT2JqID0gSlNPTi5wYXJzZShyZXNwb25zZVJlcGxhY2UobWF0Y2hlc1syXSkpO1xuXHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHRyZWplY3QobmV3IGg1NHNFcnJvcigncGFyc2VFcnJvcicsICdVbmFibGUgdG8gcGFyc2UgcmVzcG9uc2UganNvbicpKVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoanNvbk9iaiAmJiBqc29uT2JqLmg1NHNBYm9ydCkge1xuXHRcdFx0XHRyZXNvbHZlKGpzb25PYmopO1xuXHRcdFx0fSBlbHNlIGlmIChzZWxmLnBhcnNlRXJyb3JSZXNwb25zZShyZXNwb25zZVRleHQsIHNhc1Byb2dyYW0pKSB7XG5cdFx0XHRcdHJlamVjdChuZXcgaDU0c0Vycm9yKCdzYXNFcnJvcicsICdTYXMgcHJvZ3JhbSBjb21wbGV0ZWQgd2l0aCBlcnJvcnMnKSlcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlc29sdmUoanNvbk9iaik7XG5cdFx0XHR9XG5cdFx0fVxuXHR9KTtcblxuXHRyZXR1cm4gcHJvbWlzZTtcbn07XG5cbi8qKlxuKiBBZGQgZmFpbGVkIHJlc3BvbnNlIHRvIGxvZ3MgLSB1c2VkIG9ubHkgaWYgZGVidWc9ZmFsc2VcbipcbiogQHBhcmFtIHtzdHJpbmd9IHJlc3BvbnNlVGV4dCAtIHJlc3BvbnNlIGh0bWwgZnJvbSB0aGUgc2VydmVyXG4qIEBwYXJhbSB7c3RyaW5nfSBzYXNQcm9ncmFtIC0gc2FzIHByb2dyYW0gcGF0aFxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmFkZEZhaWxlZFJlc3BvbnNlID0gZnVuY3Rpb24ocmVzcG9uc2VUZXh0LCBzYXNQcm9ncmFtKSB7XG4gIGNvbnN0IHBhdHQgICAgICA9IC88c2NyaXB0KFtcXHNcXFNdKilcXC9mb3JtPi87XG4gIGNvbnN0IHBhdHQyICAgICA9IC9kaXNwbGF5XFxzPzpcXHM/bm9uZTs/XFxzPy87XG4gIC8vcmVtb3ZlIHNjcmlwdCB3aXRoIGZvcm0gZm9yIHRvZ2dsaW5nIHRoZSBsb2dzIGFuZCBcImRpc3BsYXk6bm9uZVwiIGZyb20gc3R5bGVcbiAgcmVzcG9uc2VUZXh0ICA9IHJlc3BvbnNlVGV4dC5yZXBsYWNlKHBhdHQsICcnKS5yZXBsYWNlKHBhdHQyLCAnJyk7XG4gIGxldCBkZWJ1Z1RleHQgPSByZXNwb25zZVRleHQucmVwbGFjZSgvPFtePl0qPi9nLCAnJyk7XG4gIGRlYnVnVGV4dCA9IHRoaXMuZGVjb2RlSFRNTEVudGl0aWVzKGRlYnVnVGV4dCk7XG5cbiAgbG9ncy5hZGRGYWlsZWRSZXF1ZXN0KHJlc3BvbnNlVGV4dCwgZGVidWdUZXh0LCBzYXNQcm9ncmFtKTtcbn07XG5cbi8qKlxuKiBVbmVzY2FwZSBhbGwgc3RyaW5nIHZhbHVlcyBpbiByZXR1cm5lZCBvYmplY3RcbipcbiogQHBhcmFtIHtvYmplY3R9IG9ialxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLnVuZXNjYXBlVmFsdWVzID0gZnVuY3Rpb24ob2JqKSB7XG4gIGZvciAobGV0IGtleSBpbiBvYmopIHtcbiAgICBpZiAodHlwZW9mIG9ialtrZXldID09PSAnc3RyaW5nJykge1xuICAgICAgb2JqW2tleV0gPSBkZWNvZGVVUklDb21wb25lbnQob2JqW2tleV0pO1xuICAgIH0gZWxzZSBpZih0eXBlb2Ygb2JqID09PSAnb2JqZWN0Jykge1xuICAgICAgdGhpcy51bmVzY2FwZVZhbHVlcyhvYmpba2V5XSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBvYmo7XG59O1xuXG4vKipcbiogUGFyc2UgZXJyb3IgcmVzcG9uc2UgZnJvbSBzZXJ2ZXIgYW5kIHNhdmUgZXJyb3JzIGluIG1lbW9yeVxuKlxuKiBAcGFyYW0ge3N0cmluZ30gcmVzIC0gc2VydmVyIHJlc3BvbnNlXG4qIEBwYXJhbSB7c3RyaW5nfSBzYXNQcm9ncmFtIC0gc2FzIHByb2dyYW0gd2hpY2ggcmV0dXJuZWQgdGhlIHJlc3BvbnNlXG4qXG4qL1xubW9kdWxlLmV4cG9ydHMucGFyc2VFcnJvclJlc3BvbnNlID0gZnVuY3Rpb24ocmVzLCBzYXNQcm9ncmFtKSB7XG4gIC8vY2FwdHVyZSAnRVJST1I6IFt0ZXh0XS4nIG9yICdFUlJPUiB4eCBbdGV4dF0uJ1xuICBjb25zdCBwYXR0ICAgID0gL15FUlJPUig6XFxzfFxcc1xcZFxcZCkoLipcXC58LipcXG4uKlxcLikvZ207XG4gIGxldCBlcnJvcnMgID0gcmVzLnJlcGxhY2UoLyg8KFtePl0rKT4pL2lnLCAnJykubWF0Y2gocGF0dCk7XG4gIGlmKCFlcnJvcnMpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBsZXQgZXJyTWVzc2FnZTtcbiAgZm9yKGxldCBpID0gMCwgbiA9IGVycm9ycy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcbiAgICBlcnJNZXNzYWdlICA9IGVycm9yc1tpXS5yZXBsYWNlKC88W14+XSo+L2csICcnKS5yZXBsYWNlKC8oXFxufFxcc3syLH0pL2csICcgJyk7XG4gICAgZXJyTWVzc2FnZSAgPSB0aGlzLmRlY29kZUhUTUxFbnRpdGllcyhlcnJNZXNzYWdlKTtcbiAgICBlcnJvcnNbaV0gICA9IHtcbiAgICAgIHNhc1Byb2dyYW06IHNhc1Byb2dyYW0sXG4gICAgICBtZXNzYWdlOiAgICBlcnJNZXNzYWdlLFxuICAgICAgdGltZTogICAgICAgbmV3IERhdGUoKVxuICAgIH07XG4gIH1cblxuICBsb2dzLmFkZFNhc0Vycm9ycyhlcnJvcnMpO1xuXG4gIHJldHVybiB0cnVlO1xufTtcblxuLyoqXG4qIERlY29kZSBIVE1MIGVudGl0aWVzIC0gb2xkIHV0aWxpdHkgZnVuY3Rpb25cbipcbiogQHBhcmFtIHtzdHJpbmd9IHJlcyAtIHNlcnZlciByZXNwb25zZVxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmRlY29kZUhUTUxFbnRpdGllcyA9IGZ1bmN0aW9uIChodG1sKSB7XG4gIGNvbnN0IHRlbXBFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICBsZXQgc3RyXHQ9IGh0bWwucmVwbGFjZSgvJigjKD86eFswLTlhLWZdK3xcXGQrKXxbYS16XSspOy9naSxcbiAgICBmdW5jdGlvbiAoc3RyKSB7XG4gICAgICB0ZW1wRWxlbWVudC5pbm5lckhUTUwgPSBzdHI7XG4gICAgICBzdHIgPSB0ZW1wRWxlbWVudC50ZXh0Q29udGVudCB8fCB0ZW1wRWxlbWVudC5pbm5lclRleHQ7XG4gICAgICByZXR1cm4gc3RyO1xuICAgIH1cbiAgKTtcbiAgcmV0dXJuIHN0cjtcbn07XG5cbi8qKlxuKiBDb252ZXJ0IHNhcyB0aW1lIHRvIGphdmFzY3JpcHQgZGF0ZVxuKlxuKiBAcGFyYW0ge251bWJlcn0gc2FzRGF0ZSAtIHNhcyBUYXRlIG9iamVjdFxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmZyb21TYXNEYXRlVGltZSA9IGZ1bmN0aW9uIChzYXNEYXRlKSB7XG4gIGNvbnN0IGJhc2VkYXRlID0gbmV3IERhdGUoXCJKYW51YXJ5IDEsIDE5NjAgMDA6MDA6MDBcIik7XG4gIGNvbnN0IGN1cnJkYXRlID0gc2FzRGF0ZTtcblxuICAvLyBvZmZzZXRzIGZvciBVVEMgYW5kIHRpbWV6b25lcyBhbmQgQlNUXG4gIGNvbnN0IGJhc2VPZmZzZXQgPSBiYXNlZGF0ZS5nZXRUaW1lem9uZU9mZnNldCgpOyAvLyBpbiBtaW51dGVzXG5cbiAgLy8gY29udmVydCBzYXMgZGF0ZXRpbWUgdG8gYSBjdXJyZW50IHZhbGlkIGphdmFzY3JpcHQgZGF0ZVxuICBjb25zdCBiYXNlZGF0ZU1zICA9IGJhc2VkYXRlLmdldFRpbWUoKTsgLy8gaW4gbXNcbiAgY29uc3QgY3VycmRhdGVNcyAgPSBjdXJyZGF0ZSAqIDEwMDA7IC8vIHRvIG1zXG4gIGNvbnN0IHNhc0RhdGV0aW1lID0gY3VycmRhdGVNcyArIGJhc2VkYXRlTXM7XG4gIGNvbnN0IGpzRGF0ZSAgICAgID0gbmV3IERhdGUoKTtcbiAganNEYXRlLnNldFRpbWUoc2FzRGF0ZXRpbWUpOyAvLyBmaXJzdCB0aW1lIHRvIGdldCBvZmZzZXQgQlNUIGRheWxpZ2h0IHNhdmluZ3MgZXRjXG4gIGNvbnN0IGN1cnJPZmZzZXQgID0ganNEYXRlLmdldFRpbWV6b25lT2Zmc2V0KCk7IC8vIGFkanVzdCBmb3Igb2Zmc2V0IGluIG1pbnV0ZXNcbiAgY29uc3Qgb2Zmc2V0VmFyICAgPSAoYmFzZU9mZnNldCAtIGN1cnJPZmZzZXQpICogNjAgKiAxMDAwOyAvLyBkaWZmZXJlbmNlIGluIG1pbGxpc2Vjb25kc1xuICBjb25zdCBvZmZzZXRUaW1lICA9IHNhc0RhdGV0aW1lIC0gb2Zmc2V0VmFyOyAvLyBmaW5kaW5nIEJTVCBhbmQgZGF5bGlnaHQgc2F2aW5nc1xuICBqc0RhdGUuc2V0VGltZShvZmZzZXRUaW1lKTsgLy8gdXBkYXRlIHdpdGggb2Zmc2V0XG4gIHJldHVybiBqc0RhdGU7XG59O1xuXG4vKipcbiAqIENoZWNrcyB3aGV0aGVyIHJlc3BvbnNlIG9iamVjdCBpcyBhIGxvZ2luIHJlZGlyZWN0XG4gKiBAcGFyYW0ge09iamVjdH0gcmVzcG9uc2VPYmogeGhyIHJlc3BvbnNlIHRvIGJlIGNoZWNrZWQgZm9yIGxvZ29uIHJlZGlyZWN0XG4gKi9cbm1vZHVsZS5leHBvcnRzLm5lZWRUb0xvZ2luID0gZnVuY3Rpb24ocmVzcG9uc2VPYmopIHtcblx0Y29uc3QgaXNTQVNMb2dvbiA9IHJlc3BvbnNlT2JqLnJlc3BvbnNlVVJMICYmIHJlc3BvbnNlT2JqLnJlc3BvbnNlVVJMLmluY2x1ZGVzKCdTQVNMb2dvbicpXG5cdGlmIChpc1NBU0xvZ29uID09PSBmYWxzZSkge1xuXHRcdHJldHVybiBmYWxzZVxuXHR9XG5cbiAgY29uc3QgcGF0dCA9IC88Zm9ybS4rYWN0aW9uPVwiKC4qTG9nb25bXlwiXSopLio+LztcbiAgY29uc3QgbWF0Y2hlcyA9IHBhdHQuZXhlYyhyZXNwb25zZU9iai5yZXNwb25zZVRleHQpO1xuICBsZXQgbmV3TG9naW5Vcmw7XG5cbiAgaWYoIW1hdGNoZXMpIHtcbiAgICAvL3RoZXJlJ3Mgbm8gZm9ybSwgd2UgYXJlIGluLiBob29yYXkhXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9IGVsc2Uge1xuICAgIGNvbnN0IGFjdGlvblVybCA9IG1hdGNoZXNbMV0ucmVwbGFjZSgvXFw/LiovLCAnJyk7XG4gICAgaWYoYWN0aW9uVXJsLmNoYXJBdCgwKSA9PT0gJy8nKSB7XG4gICAgICBuZXdMb2dpblVybCA9IHRoaXMuaG9zdFVybCA/IHRoaXMuaG9zdFVybCArIGFjdGlvblVybCA6IGFjdGlvblVybDtcbiAgICAgIGlmKG5ld0xvZ2luVXJsICE9PSB0aGlzLmxvZ2luVXJsKSB7XG4gICAgICAgIHRoaXMuX2xvZ2luQ2hhbmdlZCA9IHRydWU7XG4gICAgICAgIHRoaXMubG9naW5VcmwgPSBuZXdMb2dpblVybDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy9yZWxhdGl2ZSBwYXRoXG5cbiAgICAgIGNvbnN0IGxhc3RJbmRPZlNsYXNoID0gcmVzcG9uc2VPYmoucmVzcG9uc2VVUkwubGFzdEluZGV4T2YoJy8nKSArIDE7XG4gICAgICAvL3JlbW92ZSBldmVyeXRoaW5nIGFmdGVyIHRoZSBsYXN0IHNsYXNoLCBhbmQgZXZlcnl0aGluZyB1bnRpbCB0aGUgZmlyc3RcbiAgICAgIGNvbnN0IHJlbGF0aXZlTG9naW5VcmwgPSByZXNwb25zZU9iai5yZXNwb25zZVVSTC5zdWJzdHIoMCwgbGFzdEluZE9mU2xhc2gpLnJlcGxhY2UoLy4qXFwvezJ9W15cXC9dKi8sICcnKSArIGFjdGlvblVybDtcbiAgICAgIG5ld0xvZ2luVXJsID0gdGhpcy5ob3N0VXJsID8gdGhpcy5ob3N0VXJsICsgcmVsYXRpdmVMb2dpblVybCA6IHJlbGF0aXZlTG9naW5Vcmw7XG4gICAgICBpZihuZXdMb2dpblVybCAhPT0gdGhpcy5sb2dpblVybCkge1xuICAgICAgICB0aGlzLl9sb2dpbkNoYW5nZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmxvZ2luVXJsID0gbmV3TG9naW5Vcmw7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy9zYXZlIHBhcmFtZXRlcnMgZnJvbSBoaWRkZW4gZm9ybSBmaWVsZHNcbiAgICBjb25zdCBwYXJzZXIgPSBuZXcgRE9NUGFyc2VyKCk7XG4gICAgY29uc3QgZG9jID0gcGFyc2VyLnBhcnNlRnJvbVN0cmluZyhyZXNwb25zZU9iai5yZXNwb25zZVRleHQsXCJ0ZXh0L2h0bWxcIik7XG4gICAgY29uc3QgcmVzID0gZG9jLnF1ZXJ5U2VsZWN0b3JBbGwoXCJpbnB1dFt0eXBlPSdoaWRkZW4nXVwiKTtcbiAgICBjb25zdCBoaWRkZW5Gb3JtUGFyYW1zID0ge307XG4gICAgaWYocmVzKSB7XG4gICAgICAvL2l0J3MgbmV3IGxvZ2luIHBhZ2UgaWYgd2UgaGF2ZSB0aGVzZSBhZGRpdGlvbmFsIHBhcmFtZXRlcnNcbiAgICAgIHRoaXMuX2lzTmV3TG9naW5QYWdlID0gdHJ1ZTtcbiAgICAgIHJlcy5mb3JFYWNoKGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgaGlkZGVuRm9ybVBhcmFtc1tub2RlLm5hbWVdID0gbm9kZS52YWx1ZTtcbiAgICAgIH0pO1xuICAgICAgdGhpcy5fYWRpdGlvbmFsTG9naW5QYXJhbXMgPSBoaWRkZW5Gb3JtUGFyYW1zO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxufTtcblxuLyoqXG4qIEdldCBmdWxsIHByb2dyYW0gcGF0aCBmcm9tIG1ldGFkYXRhIHJvb3QgYW5kIHJlbGF0aXZlIHBhdGhcbipcbiogQHBhcmFtIHtzdHJpbmd9IG1ldGFkYXRhUm9vdCAtIE1ldGFkYXRhIHJvb3QgKHBhdGggd2hlcmUgYWxsIHByb2dyYW1zIGZvciB0aGUgcHJvamVjdCBhcmUgbG9jYXRlZClcbiogQHBhcmFtIHtzdHJpbmd9IHNhc1Byb2dyYW1QYXRoIC0gU2FzIHByb2dyYW0gcGF0aFxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmdldEZ1bGxQcm9ncmFtUGF0aCA9IGZ1bmN0aW9uKG1ldGFkYXRhUm9vdCwgc2FzUHJvZ3JhbVBhdGgpIHtcbiAgcmV0dXJuIG1ldGFkYXRhUm9vdCA/IG1ldGFkYXRhUm9vdC5yZXBsYWNlKC9cXC8/JC8sICcvJykgKyBzYXNQcm9ncmFtUGF0aC5yZXBsYWNlKC9eXFwvLywgJycpIDogc2FzUHJvZ3JhbVBhdGg7XG59O1xuXG4vLyBSZXR1cm5zIG9iamVjdCB3aGVyZSB0YWJsZSByb3dzIGFyZSBncm91cHBlZCBieSBrZXlcbm1vZHVsZS5leHBvcnRzLmdldE9iak9mVGFibGUgPSBmdW5jdGlvbiAodGFibGUsIGtleSwgdmFsdWUgPSBudWxsKSB7XG5cdGNvbnN0IG9iaiA9IHt9XG5cdHRhYmxlLmZvckVhY2gocm93ID0+IHtcblx0XHRpZiAoIW9ialtyb3dba2V5XV0pIHtcblx0XHRcdG9ialtyb3dba2V5XV0gPSBbXVxuXHRcdFx0b2JqW3Jvd1trZXldXS5wdXNoKHZhbHVlID8gcm93W3ZhbHVlXSA6IHJvdylcblx0XHR9IGVsc2Uge1xuXHRcdFx0b2JqW3Jvd1trZXldXS5wdXNoKHZhbHVlID8gcm93W3ZhbHVlXSA6IHJvdylcblx0XHR9XG5cdH0pXG5cdHJldHVybiBvYmpcbn1cblxuLy8gUmV0dXJucyBzZWxmIHVyaSBvdXQgb2YgbGlua3MgYXJyYXlcbm1vZHVsZS5leHBvcnRzLmdldFNlbGZVcmkgPSBmdW5jdGlvbiAobGlua3MpIHtcblx0cmV0dXJuIGxpbmtzXG5cdFx0LmZpbHRlcihlID0+IGUucmVsID09PSAnc2VsZicpXG5cdFx0Lm1hcChlID0+IGUudXJpKVxuXHRcdC5zaGlmdCgpO1xufVxuIiwiY29uc3QgaDU0c0Vycm9yID0gcmVxdWlyZSgnLi9lcnJvci5qcycpO1xuY29uc3QgbG9ncyAgICAgID0gcmVxdWlyZSgnLi9sb2dzLmpzJyk7XG5jb25zdCBUYWJsZXMgICAgPSByZXF1aXJlKCcuL3RhYmxlcycpO1xuY29uc3QgRmlsZXMgICAgID0gcmVxdWlyZSgnLi9maWxlcycpO1xuY29uc3QgdG9TYXNEYXRlVGltZSA9IHJlcXVpcmUoJy4vdGFibGVzL3V0aWxzLmpzJykudG9TYXNEYXRlVGltZTtcblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciBhIGdpdmVuIHRhYmxlIG5hbWUgaXMgYSB2YWxpZCBTQVMgbWFjcm8gbmFtZVxuICogQHBhcmFtIHtTdHJpbmd9IG1hY3JvTmFtZSBUaGUgU0FTIG1hY3JvIG5hbWUgdG8gYmUgZ2l2ZW4gdG8gdGhpcyB0YWJsZVxuICovXG5mdW5jdGlvbiB2YWxpZGF0ZU1hY3JvKG1hY3JvTmFtZSkge1xuICBpZihtYWNyb05hbWUubGVuZ3RoID4gMzIpIHtcbiAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1RhYmxlIG5hbWUgdG9vIGxvbmcuIE1heGltdW0gaXMgMzIgY2hhcmFjdGVycycpO1xuICB9XG5cbiAgY29uc3QgY2hhckNvZGVBdDAgPSBtYWNyb05hbWUuY2hhckNvZGVBdCgwKTtcbiAgLy8gdmFsaWRhdGUgaXQgc3RhcnRzIHdpdGggQS1aLCBhLXosIG9yIF9cbiAgaWYoKGNoYXJDb2RlQXQwIDwgNjUgfHwgY2hhckNvZGVBdDAgPiA5MCkgJiYgKGNoYXJDb2RlQXQwIDwgOTcgfHwgY2hhckNvZGVBdDAgPiAxMjIpICYmIG1hY3JvTmFtZVswXSAhPT0gJ18nKSB7XG4gICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdUYWJsZSBuYW1lIHN0YXJ0aW5nIHdpdGggbnVtYmVyIG9yIHNwZWNpYWwgY2hhcmFjdGVycycpO1xuICB9XG5cbiAgZm9yKGxldCBpID0gMDsgaSA8IG1hY3JvTmFtZS5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGNoYXJDb2RlID0gbWFjcm9OYW1lLmNoYXJDb2RlQXQoaSk7XG5cbiAgICBpZigoY2hhckNvZGUgPCA0OCB8fCBjaGFyQ29kZSA+IDU3KSAmJlxuICAgICAgKGNoYXJDb2RlIDwgNjUgfHwgY2hhckNvZGUgPiA5MCkgJiZcbiAgICAgIChjaGFyQ29kZSA8IDk3IHx8IGNoYXJDb2RlID4gMTIyKSAmJlxuICAgICAgbWFjcm9OYW1lW2ldICE9PSAnXycpXG4gICAge1xuICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdUYWJsZSBuYW1lIGhhcyB1bnN1cHBvcnRlZCBjaGFyYWN0ZXJzJyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuKiBoNTRzIFNBUyBkYXRhIG9iamVjdCBjb25zdHJ1Y3RvclxuKiBAY29uc3RydWN0b3JcbipcbiogQHBhcmFtIHthcnJheXxmaWxlfSBkYXRhIC0gVGFibGUgb3IgZmlsZSBhZGRlZCB3aGVuIG9iamVjdCBpcyBjcmVhdGVkXG4qIEBwYXJhbSB7U3RyaW5nfSBtYWNyb05hbWUgVGhlIFNBUyBtYWNybyBuYW1lIHRvIGJlIGdpdmVuIHRvIHRoaXMgdGFibGVcbiogQHBhcmFtIHtudW1iZXJ9IHBhcmFtZXRlclRocmVzaG9sZCAtIHNpemUgb2YgZGF0YSBvYmplY3RzIHNlbnQgdG8gU0FTIChsZWdhY3kpXG4qXG4qL1xuZnVuY3Rpb24gU2FzRGF0YShkYXRhLCBtYWNyb05hbWUsIHNwZWNzKSB7XG4gIGlmKGRhdGEgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgIHRoaXMuX2ZpbGVzID0ge307XG4gICAgdGhpcy5hZGRUYWJsZShkYXRhLCBtYWNyb05hbWUsIHNwZWNzKTtcbiAgfSBlbHNlIGlmKGRhdGEgaW5zdGFuY2VvZiBGaWxlIHx8IGRhdGEgaW5zdGFuY2VvZiBCbG9iKSB7XG4gICAgRmlsZXMuY2FsbCh0aGlzLCBkYXRhLCBtYWNyb05hbWUpO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnRGF0YSBhcmd1bWVudCB3cm9uZyB0eXBlIG9yIG1pc3NpbmcnKTtcbiAgfVxufVxuXG4vKipcbiogQWRkIHRhYmxlIHRvIHRhYmxlcyBvYmplY3RcbiogQHBhcmFtIHthcnJheX0gdGFibGUgLSBBcnJheSBvZiB0YWJsZSBvYmplY3RzXG4qIEBwYXJhbSB7U3RyaW5nfSBtYWNyb05hbWUgVGhlIFNBUyBtYWNybyBuYW1lIHRvIGJlIGdpdmVuIHRvIHRoaXMgdGFibGVcbipcbiovXG5TYXNEYXRhLnByb3RvdHlwZS5hZGRUYWJsZSA9IGZ1bmN0aW9uKHRhYmxlLCBtYWNyb05hbWUsIHNwZWNzKSB7XG4gIGNvbnN0IGlzU3BlY3NQcm92aWRlZCA9ICEhc3BlY3M7XG4gIGlmKHRhYmxlICYmIG1hY3JvTmFtZSkge1xuICAgIGlmKCEodGFibGUgaW5zdGFuY2VvZiBBcnJheSkpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnRmlyc3QgYXJndW1lbnQgbXVzdCBiZSBhcnJheScpO1xuICAgIH1cbiAgICBpZih0eXBlb2YgbWFjcm9OYW1lICE9PSAnc3RyaW5nJykge1xuICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdTZWNvbmQgYXJndW1lbnQgbXVzdCBiZSBzdHJpbmcnKTtcbiAgICB9XG5cbiAgICB2YWxpZGF0ZU1hY3JvKG1hY3JvTmFtZSk7XG4gIH0gZWxzZSB7XG4gICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdNaXNzaW5nIGFyZ3VtZW50cycpO1xuICB9XG5cbiAgaWYgKHR5cGVvZiB0YWJsZSAhPT0gJ29iamVjdCcgfHwgISh0YWJsZSBpbnN0YW5jZW9mIEFycmF5KSkge1xuICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnVGFibGUgYXJndW1lbnQgaXMgbm90IGFuIGFycmF5Jyk7XG4gIH1cblxuICBsZXQga2V5O1xuICBpZihzcGVjcykge1xuICAgIGlmKHNwZWNzLmNvbnN0cnVjdG9yICE9PSBPYmplY3QpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnU3BlY3MgZGF0YSB0eXBlIHdyb25nLiBPYmplY3QgZXhwZWN0ZWQuJyk7XG4gICAgfVxuICAgIGZvcihrZXkgaW4gdGFibGVbMF0pIHtcbiAgICAgIGlmKCFzcGVjc1trZXldKSB7XG4gICAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnTWlzc2luZyBjb2x1bW5zIGluIHNwZWNzIGRhdGEuJyk7XG4gICAgICB9XG4gICAgfVxuICAgIGZvcihrZXkgaW4gc3BlY3MpIHtcbiAgICAgIGlmKHNwZWNzW2tleV0uY29uc3RydWN0b3IgIT09IE9iamVjdCkge1xuICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1dyb25nIGNvbHVtbiBkZXNjcmlwdG9yIGluIHNwZWNzIGRhdGEuJyk7XG4gICAgICB9XG4gICAgICBpZighc3BlY3Nba2V5XS5jb2xUeXBlIHx8ICFzcGVjc1trZXldLmNvbExlbmd0aCkge1xuICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ01pc3NpbmcgY29sdW1ucyBpbiBzcGVjcyBkZXNjcmlwdG9yLicpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGxldCBpLCBqLCAvL2NvdW50ZXJzIHVzZWQgbGF0dGVyIGluIGNvZGVcbiAgICAgIHJvdywgdmFsLCB0eXBlLFxuICAgICAgc3BlY0tleXMgPSBbXTtcblx0Y29uc3Qgc3BlY2lhbENoYXJzID0gWydcIicsICdcXFxcJywgJy8nLCAnXFxuJywgJ1xcdCcsICdcXGYnLCAnXFxyJywgJ1xcYiddO1xuXG4gIGlmKCFzcGVjcykge1xuICAgIHNwZWNzID0ge307XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgdGFibGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIHJvdyA9IHRhYmxlW2ldO1xuXG4gICAgICBpZih0eXBlb2Ygcm93ICE9PSAnb2JqZWN0Jykge1xuICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1RhYmxlIGl0ZW0gaXMgbm90IGFuIG9iamVjdCcpO1xuICAgICAgfVxuXG4gICAgICBmb3Ioa2V5IGluIHJvdykge1xuICAgICAgICBpZihyb3cuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAgIHZhbCAgPSByb3dba2V5XTtcbiAgICAgICAgICB0eXBlID0gdHlwZW9mIHZhbDtcblxuICAgICAgICAgIGlmKHNwZWNzW2tleV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgc3BlY0tleXMucHVzaChrZXkpO1xuICAgICAgICAgICAgc3BlY3Nba2V5XSA9IHt9O1xuXG4gICAgICAgICAgICBpZiAodHlwZSA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgaWYodmFsIDwgTnVtYmVyLk1JTl9TQUZFX0lOVEVHRVIgfHwgdmFsID4gTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIpIHtcbiAgICAgICAgICAgICAgICBsb2dzLmFkZEFwcGxpY2F0aW9uTG9nKCdPYmplY3RbJyArIGkgKyAnXS4nICsga2V5ICsgJyAtIFRoaXMgdmFsdWUgZXhjZWVkcyBleHBlY3RlZCBudW1lcmljIHByZWNpc2lvbi4nKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBzcGVjc1trZXldLmNvbFR5cGUgICA9ICdudW0nO1xuICAgICAgICAgICAgICBzcGVjc1trZXldLmNvbExlbmd0aCA9IDg7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdzdHJpbmcnICYmICEodmFsIGluc3RhbmNlb2YgRGF0ZSkpIHsgLy8gc3RyYWlnaHRmb3J3YXJkIHN0cmluZ1xuICAgICAgICAgICAgICBzcGVjc1trZXldLmNvbFR5cGUgICAgPSAnc3RyaW5nJztcbiAgICAgICAgICAgICAgc3BlY3Nba2V5XS5jb2xMZW5ndGggID0gdmFsLmxlbmd0aDtcbiAgICAgICAgICAgIH0gZWxzZSBpZih2YWwgaW5zdGFuY2VvZiBEYXRlKSB7XG4gICAgICAgICAgICAgIHNwZWNzW2tleV0uY29sVHlwZSAgID0gJ2RhdGUnO1xuICAgICAgICAgICAgICBzcGVjc1trZXldLmNvbExlbmd0aCA9IDg7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgIHNwZWNzW2tleV0uY29sVHlwZSAgID0gJ2pzb24nO1xuICAgICAgICAgICAgICBzcGVjc1trZXldLmNvbExlbmd0aCA9IEpTT04uc3RyaW5naWZ5KHZhbCkubGVuZ3RoO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBzcGVjS2V5cyA9IE9iamVjdC5rZXlzKHNwZWNzKTtcbiAgfVxuXG4gIGxldCBzYXNDc3YgPSAnJztcblxuICAvLyB3ZSBuZWVkIHR3byBsb29wcyAtIHRoZSBmaXJzdCBvbmUgaXMgY3JlYXRpbmcgc3BlY3MgYW5kIHZhbGlkYXRpbmdcbiAgZm9yIChpID0gMDsgaSA8IHRhYmxlLmxlbmd0aDsgaSsrKSB7XG4gICAgcm93ID0gdGFibGVbaV07XG4gICAgZm9yKGogPSAwOyBqIDwgc3BlY0tleXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGtleSA9IHNwZWNLZXlzW2pdO1xuICAgICAgaWYocm93Lmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgdmFsICA9IHJvd1trZXldO1xuICAgICAgICB0eXBlID0gdHlwZW9mIHZhbDtcblxuICAgICAgICBpZih0eXBlID09PSAnbnVtYmVyJyAmJiBpc05hTih2YWwpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ05hTiB2YWx1ZSBpbiBvbmUgb2YgdGhlIHZhbHVlcyAoY29sdW1ucykgaXMgbm90IGFsbG93ZWQnKTtcbiAgICAgICAgfVxuICAgICAgICBpZih2YWwgPT09IC1JbmZpbml0eSB8fCB2YWwgPT09IEluZmluaXR5KSB7XG4gICAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgdmFsLnRvU3RyaW5nKCkgKyAnIHZhbHVlIGluIG9uZSBvZiB0aGUgdmFsdWVzIChjb2x1bW5zKSBpcyBub3QgYWxsb3dlZCcpO1xuICAgICAgICB9XG4gICAgICAgIGlmKHZhbCA9PT0gdHJ1ZSB8fCB2YWwgPT09IGZhbHNlKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ0Jvb2xlYW4gdmFsdWUgaW4gb25lIG9mIHRoZSB2YWx1ZXMgKGNvbHVtbnMpIGlzIG5vdCBhbGxvd2VkJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYodHlwZSA9PT0gJ3N0cmluZycgJiYgdmFsLmluZGV4T2YoJ1xcclxcbicpICE9PSAtMSkge1xuICAgICAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ3R5cGVFcnJvcicsICdOZXcgbGluZSBjaGFyYWN0ZXIgaXMgbm90IHN1cHBvcnRlZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY29udmVydCBudWxsIHRvICcuJyBmb3IgbnVtYmVycyBhbmQgdG8gJycgZm9yIHN0cmluZ3NcbiAgICAgICAgaWYodmFsID09PSBudWxsKSB7XG4gICAgICAgICAgaWYoc3BlY3Nba2V5XS5jb2xUeXBlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgdmFsID0gJyc7XG4gICAgICAgICAgICB0eXBlID0gJ3N0cmluZyc7XG4gICAgICAgICAgfSBlbHNlIGlmKHNwZWNzW2tleV0uY29sVHlwZSA9PT0gJ251bScpIHtcbiAgICAgICAgICAgIHZhbCA9ICcuJztcbiAgICAgICAgICAgIHR5cGUgPSAnbnVtYmVyJztcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ0Nhbm5vdCBjb252ZXJ0IG51bGwgdmFsdWUnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuXG4gICAgICAgIGlmICgodHlwZSA9PT0gJ251bWJlcicgJiYgc3BlY3Nba2V5XS5jb2xUeXBlICE9PSAnbnVtJyAmJiB2YWwgIT09ICcuJykgfHxcbiAgICAgICAgICAoKHR5cGUgPT09ICdzdHJpbmcnICYmICEodmFsIGluc3RhbmNlb2YgRGF0ZSkgJiYgc3BlY3Nba2V5XS5jb2xUeXBlICE9PSAnc3RyaW5nJykgJiZcbiAgICAgICAgICAodHlwZSA9PT0gJ3N0cmluZycgJiYgc3BlY3Nba2V5XS5jb2xUeXBlID09ICdudW0nICYmIHZhbCAhPT0gJy4nKSkgfHxcbiAgICAgICAgICAodmFsIGluc3RhbmNlb2YgRGF0ZSAmJiBzcGVjc1trZXldLmNvbFR5cGUgIT09ICdkYXRlJykgfHxcbiAgICAgICAgICAoKHR5cGUgPT09ICdvYmplY3QnICYmIHZhbC5jb25zdHJ1Y3RvciAhPT0gRGF0ZSkgJiYgc3BlY3Nba2V5XS5jb2xUeXBlICE9PSAnanNvbicpKVxuICAgICAgICB7XG4gICAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ1RoZXJlIGlzIGEgc3BlY3MgdHlwZSBtaXNtYXRjaCBpbiB0aGUgYXJyYXkgYmV0d2VlbiB2YWx1ZXMgKGNvbHVtbnMpIG9mIHRoZSBzYW1lIG5hbWUuJyArXG4gICAgICAgICAgICAnIHR5cGUvY29sVHlwZS92YWwgPSAnICsgdHlwZSArJy8nICsgc3BlY3Nba2V5XS5jb2xUeXBlICsgJy8nICsgdmFsICk7XG4gICAgICAgIH0gZWxzZSBpZighaXNTcGVjc1Byb3ZpZGVkICYmIHR5cGUgPT09ICdzdHJpbmcnICYmIHNwZWNzW2tleV0uY29sTGVuZ3RoIDwgdmFsLmxlbmd0aCkge1xuICAgICAgICAgIHNwZWNzW2tleV0uY29sTGVuZ3RoID0gdmFsLmxlbmd0aDtcbiAgICAgICAgfSBlbHNlIGlmKCh0eXBlID09PSAnc3RyaW5nJyAmJiBzcGVjc1trZXldLmNvbExlbmd0aCA8IHZhbC5sZW5ndGgpIHx8ICh0eXBlICE9PSAnc3RyaW5nJyAmJiBzcGVjc1trZXldLmNvbExlbmd0aCAhPT0gOCkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCd0eXBlRXJyb3InLCAnVGhlcmUgaXMgYSBzcGVjcyBsZW5ndGggbWlzbWF0Y2ggaW4gdGhlIGFycmF5IGJldHdlZW4gdmFsdWVzIChjb2x1bW5zKSBvZiB0aGUgc2FtZSBuYW1lLicgK1xuICAgICAgICAgICAgJyB0eXBlL2NvbFR5cGUvdmFsID0gJyArIHR5cGUgKycvJyArIHNwZWNzW2tleV0uY29sVHlwZSArICcvJyArIHZhbCApO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHZhbCBpbnN0YW5jZW9mIERhdGUpIHtcbiAgICAgICAgICB2YWwgPSB0b1Nhc0RhdGVUaW1lKHZhbCk7XG4gICAgICAgIH1cblxuICAgICAgICBzd2l0Y2goc3BlY3Nba2V5XS5jb2xUeXBlKSB7XG4gICAgICAgICAgY2FzZSAnbnVtJzpcbiAgICAgICAgICBjYXNlICdkYXRlJzpcbiAgICAgICAgICAgIHNhc0NzdiArPSB2YWw7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdzdHJpbmcnOlxuICAgICAgICAgICAgc2FzQ3N2ICs9ICdcIicgKyB2YWwucmVwbGFjZSgvXCIvZywgJ1wiXCInKSArICdcIic7XG4gICAgICAgICAgICBsZXQgY29sTGVuZ3RoID0gdmFsLmxlbmd0aDtcbiAgICAgICAgICAgIGZvcihsZXQgayA9IDA7IGsgPCB2YWwubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgaWYoc3BlY2lhbENoYXJzLmluZGV4T2YodmFsW2tdKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICBjb2xMZW5ndGgrKztcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBsZXQgY29kZSA9IHZhbC5jaGFyQ29kZUF0KGspO1xuICAgICAgICAgICAgICAgIGlmKGNvZGUgPiAweGZmZmYpIHtcbiAgICAgICAgICAgICAgICAgIGNvbExlbmd0aCArPSAzO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZihjb2RlID4gMHg3ZmYpIHtcbiAgICAgICAgICAgICAgICAgIGNvbExlbmd0aCArPSAyO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZihjb2RlID4gMHg3Zikge1xuICAgICAgICAgICAgICAgICAgY29sTGVuZ3RoICs9IDE7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyB1c2UgbWF4aW11bSB2YWx1ZSBiZXR3ZWVuIG1heCBwcmV2aW91cywgY3VycmVudCB2YWx1ZSBhbmQgMSAoZmlyc3QgdHdvIGNhbiBiZSAwIHdpY2ggaXMgbm90IHN1cHBvcnRlZClcbiAgICAgICAgICAgIHNwZWNzW2tleV0uY29sTGVuZ3RoID0gTWF0aC5tYXgoc3BlY3Nba2V5XS5jb2xMZW5ndGgsIGNvbExlbmd0aCwgMSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdvYmplY3QnOlxuICAgICAgICAgICAgc2FzQ3N2ICs9ICdcIicgKyBKU09OLnN0cmluZ2lmeSh2YWwpLnJlcGxhY2UoL1wiL2csICdcIlwiJykgKyAnXCInO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIGRvIG5vdCBpbnNlcnQgaWYgaXQncyB0aGUgbGFzdCBjb2x1bW5cbiAgICAgIGlmKGogPCBzcGVjS2V5cy5sZW5ndGggLSAxKSB7XG4gICAgICAgIHNhc0NzdiArPSAnLCc7XG4gICAgICB9XG4gICAgfVxuICAgIGlmKGkgPCB0YWJsZS5sZW5ndGggLSAxKSB7XG4gICAgICBzYXNDc3YgKz0gJ1xcclxcbic7XG4gICAgfVxuICB9XG5cbiAgLy9jb252ZXJ0IHNwZWNzIHRvIGNzdiB3aXRoIHBpcGVzXG4gIGNvbnN0IHNwZWNTdHJpbmcgPSBzcGVjS2V5cy5tYXAoZnVuY3Rpb24oa2V5KSB7XG4gICAgcmV0dXJuIGtleSArICcsJyArIHNwZWNzW2tleV0uY29sVHlwZSArICcsJyArIHNwZWNzW2tleV0uY29sTGVuZ3RoO1xuICB9KS5qb2luKCd8Jyk7XG5cbiAgdGhpcy5fZmlsZXNbbWFjcm9OYW1lXSA9IFtcbiAgICBzcGVjU3RyaW5nLFxuICAgIG5ldyBCbG9iKFtzYXNDc3ZdLCB7dHlwZTogJ3RleHQvY3N2O2NoYXJzZXQ9VVRGLTgnfSlcbiAgXTtcbn07XG5cbi8qKlxuICogQWRkIGZpbGUgYXMgYSB2ZXJiYXRpbSBibG9iIGZpbGUgdXBsYW9kXG4gKiBAcGFyYW0ge0Jsb2J9IGZpbGUgLSB0aGUgYmxvYiB0aGF0IHdpbGwgYmUgdXBsb2FkZWQgYXMgZmlsZVxuICogQHBhcmFtIHtTdHJpbmd9IG1hY3JvTmFtZSAtIHRoZSBTQVMgd2ViaW4gbmFtZSBnaXZlbiB0byB0aGlzIGZpbGVcbiAqL1xuU2FzRGF0YS5wcm90b3R5cGUuYWRkRmlsZSAgPSBmdW5jdGlvbihmaWxlLCBtYWNyb05hbWUpIHtcbiAgRmlsZXMucHJvdG90eXBlLmFkZC5jYWxsKHRoaXMsIGZpbGUsIG1hY3JvTmFtZSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNhc0RhdGE7XG4iLCJjb25zdCBoNTRzRXJyb3IgPSByZXF1aXJlKCcuLi9lcnJvci5qcycpO1xuXG4vKlxuKiBoNTRzIHRhYmxlcyBvYmplY3QgY29uc3RydWN0b3JcbiogQGNvbnN0cnVjdG9yXG4qXG4qQHBhcmFtIHthcnJheX0gdGFibGUgLSBUYWJsZSBhZGRlZCB3aGVuIG9iamVjdCBpcyBjcmVhdGVkXG4qQHBhcmFtIHtzdHJpbmd9IG1hY3JvTmFtZSAtIG1hY3JvIG5hbWVcbipAcGFyYW0ge251bWJlcn0gcGFyYW1ldGVyVGhyZXNob2xkIC0gc2l6ZSBvZiBkYXRhIG9iamVjdHMgc2VudCB0byBTQVNcbipcbiovXG5mdW5jdGlvbiBUYWJsZXModGFibGUsIG1hY3JvTmFtZSwgcGFyYW1ldGVyVGhyZXNob2xkKSB7XG4gIHRoaXMuX3RhYmxlcyA9IHt9O1xuICB0aGlzLl9wYXJhbWV0ZXJUaHJlc2hvbGQgPSBwYXJhbWV0ZXJUaHJlc2hvbGQgfHwgMzAwMDA7XG5cbiAgVGFibGVzLnByb3RvdHlwZS5hZGQuY2FsbCh0aGlzLCB0YWJsZSwgbWFjcm9OYW1lKTtcbn1cblxuLypcbiogQWRkIHRhYmxlIHRvIHRhYmxlcyBvYmplY3RcbiogQHBhcmFtIHthcnJheX0gdGFibGUgLSBBcnJheSBvZiB0YWJsZSBvYmplY3RzXG4qIEBwYXJhbSB7c3RyaW5nfSBtYWNyb05hbWUgLSBTYXMgbWFjcm8gbmFtZVxuKlxuKi9cblRhYmxlcy5wcm90b3R5cGUuYWRkID0gZnVuY3Rpb24odGFibGUsIG1hY3JvTmFtZSkge1xuICBpZih0YWJsZSAmJiBtYWNyb05hbWUpIHtcbiAgICBpZighKHRhYmxlIGluc3RhbmNlb2YgQXJyYXkpKSB7XG4gICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ0ZpcnN0IGFyZ3VtZW50IG11c3QgYmUgYXJyYXknKTtcbiAgICB9XG4gICAgaWYodHlwZW9mIG1hY3JvTmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnU2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgc3RyaW5nJyk7XG4gICAgfVxuICAgIGlmKCFpc05hTihtYWNyb05hbWVbbWFjcm9OYW1lLmxlbmd0aCAtIDFdKSkge1xuICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdNYWNybyBuYW1lIGNhbm5vdCBoYXZlIG51bWJlciBhdCB0aGUgZW5kJyk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnTWlzc2luZyBhcmd1bWVudHMnKTtcbiAgfVxuXG4gIGNvbnN0IHJlc3VsdCA9IHRoaXMuX3V0aWxzLmNvbnZlcnRUYWJsZU9iamVjdCh0YWJsZSwgdGhpcy5fcGFyYW1ldGVyVGhyZXNob2xkKTtcblxuICBjb25zdCB0YWJsZUFycmF5ID0gW107XG4gIHRhYmxlQXJyYXkucHVzaChKU09OLnN0cmluZ2lmeShyZXN1bHQuc3BlYykpO1xuICBmb3IgKGxldCBudW1iZXJPZlRhYmxlcyA9IDA7IG51bWJlck9mVGFibGVzIDwgcmVzdWx0LmRhdGEubGVuZ3RoOyBudW1iZXJPZlRhYmxlcysrKSB7XG4gICAgY29uc3Qgb3V0U3RyaW5nID0gSlNPTi5zdHJpbmdpZnkocmVzdWx0LmRhdGFbbnVtYmVyT2ZUYWJsZXNdKTtcbiAgICB0YWJsZUFycmF5LnB1c2gob3V0U3RyaW5nKTtcbiAgfVxuICB0aGlzLl90YWJsZXNbbWFjcm9OYW1lXSA9IHRhYmxlQXJyYXk7XG59O1xuXG5UYWJsZXMucHJvdG90eXBlLl91dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMuanMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBUYWJsZXM7XG4iLCJjb25zdCBoNTRzRXJyb3IgPSByZXF1aXJlKCcuLi9lcnJvci5qcycpO1xuY29uc3QgbG9ncyA9IHJlcXVpcmUoJy4uL2xvZ3MuanMnKTtcblxuLypcbiogQ29udmVydCB0YWJsZSBvYmplY3QgdG8gU2FzIHJlYWRhYmxlIG9iamVjdFxuKlxuKiBAcGFyYW0ge29iamVjdH0gaW5PYmplY3QgLSBPYmplY3QgdG8gY29udmVydFxuKlxuKi9cbm1vZHVsZS5leHBvcnRzLmNvbnZlcnRUYWJsZU9iamVjdCA9IGZ1bmN0aW9uKGluT2JqZWN0LCBjaHVua1RocmVzaG9sZCkge1xuICBjb25zdCBzZWxmICAgICAgICAgICAgPSB0aGlzO1xuXG4gIGlmKGNodW5rVGhyZXNob2xkID4gMzAwMDApIHtcbiAgICBjb25zb2xlLndhcm4oJ1lvdSBzaG91bGQgbm90IHNldCB0aHJlc2hvbGQgbGFyZ2VyIHRoYW4gMzBrYiBiZWNhdXNlIG9mIHRoZSBTQVMgbGltaXRhdGlvbnMnKTtcbiAgfVxuXG4gIC8vIGZpcnN0IGNoZWNrIHRoYXQgdGhlIG9iamVjdCBpcyBhbiBhcnJheVxuICBpZiAodHlwZW9mIChpbk9iamVjdCkgIT09ICdvYmplY3QnKSB7XG4gICAgdGhyb3cgbmV3IGg1NHNFcnJvcignYXJndW1lbnRFcnJvcicsICdUaGUgcGFyYW1ldGVyIHBhc3NlZCB0byBjaGVja0FuZEdldFR5cGVPYmplY3QgaXMgbm90IGFuIG9iamVjdCcpO1xuICB9XG5cbiAgY29uc3QgYXJyYXlMZW5ndGggPSBpbk9iamVjdC5sZW5ndGg7XG4gIGlmICh0eXBlb2YgKGFycmF5TGVuZ3RoKSAhPT0gJ251bWJlcicpIHtcbiAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCdhcmd1bWVudEVycm9yJywgJ1RoZSBwYXJhbWV0ZXIgcGFzc2VkIHRvIGNoZWNrQW5kR2V0VHlwZU9iamVjdCBkb2VzIG5vdCBoYXZlIGEgdmFsaWQgbGVuZ3RoIGFuZCBpcyBtb3N0IGxpa2VseSBub3QgYW4gYXJyYXknKTtcbiAgfVxuXG4gIGNvbnN0IGV4aXN0aW5nQ29scyA9IHt9OyAvLyB0aGlzIGlzIGp1c3QgdG8gbWFrZSBsb29rdXAgZWFzaWVyIHJhdGhlciB0aGFuIHRyYXZlcnNpbmcgYXJyYXkgZWFjaCB0aW1lLiBXaWxsIHRyYW5zZm9ybSBhZnRlclxuXG4gIC8vIGZ1bmN0aW9uIGNoZWNrQW5kU2V0QXJyYXkgLSB0aGlzIHdpbGwgY2hlY2sgYW4gaW5PYmplY3QgY3VycmVudCBrZXkgYWdhaW5zdCB0aGUgZXhpc3RpbmcgdHlwZUFycmF5IGFuZCBlaXRoZXIgcmV0dXJuIC0xIGlmIHRoZXJlXG4gIC8vIGlzIGEgdHlwZSBtaXNtYXRjaCBvciBhZGQgYW4gZWxlbWVudCBhbmQgdXBkYXRlL2luY3JlbWVudCB0aGUgbGVuZ3RoIGlmIG5lZWRlZFxuXG4gIGZ1bmN0aW9uIGNoZWNrQW5kSW5jcmVtZW50KGNvbFNwZWMpIHtcbiAgICBpZiAodHlwZW9mIChleGlzdGluZ0NvbHNbY29sU3BlYy5jb2xOYW1lXSkgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBleGlzdGluZ0NvbHNbY29sU3BlYy5jb2xOYW1lXSAgICAgICAgICAgPSB7fTtcbiAgICAgIGV4aXN0aW5nQ29sc1tjb2xTcGVjLmNvbE5hbWVdLmNvbE5hbWUgICA9IGNvbFNwZWMuY29sTmFtZTtcbiAgICAgIGV4aXN0aW5nQ29sc1tjb2xTcGVjLmNvbE5hbWVdLmNvbFR5cGUgICA9IGNvbFNwZWMuY29sVHlwZTtcbiAgICAgIGV4aXN0aW5nQ29sc1tjb2xTcGVjLmNvbE5hbWVdLmNvbExlbmd0aCA9IGNvbFNwZWMuY29sTGVuZ3RoID4gMCA/IGNvbFNwZWMuY29sTGVuZ3RoIDogMTtcbiAgICAgIHJldHVybiAwOyAvLyBhbGwgb2tcbiAgICB9XG4gICAgLy8gY2hlY2sgdHlwZSBtYXRjaFxuICAgIGlmIChleGlzdGluZ0NvbHNbY29sU3BlYy5jb2xOYW1lXS5jb2xUeXBlICE9PSBjb2xTcGVjLmNvbFR5cGUpIHtcbiAgICAgIHJldHVybiAtMTsgLy8gdGhlcmUgaXMgYSBmdWRnZSBpbiB0aGUgdHlwaW5nXG4gICAgfVxuICAgIGlmIChleGlzdGluZ0NvbHNbY29sU3BlYy5jb2xOYW1lXS5jb2xMZW5ndGggPCBjb2xTcGVjLmNvbExlbmd0aCkge1xuICAgICAgZXhpc3RpbmdDb2xzW2NvbFNwZWMuY29sTmFtZV0uY29sTGVuZ3RoID0gY29sU3BlYy5jb2xMZW5ndGggPiAwID8gY29sU3BlYy5jb2xMZW5ndGggOiAxOyAvLyBpbmNyZW1lbnQgdGhlIG1heCBsZW5ndGggb2YgdGhpcyBjb2x1bW5cbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgfVxuICBsZXQgY2h1bmtBcnJheUNvdW50ICAgICAgICAgPSAwOyAvLyB0aGlzIGlzIGZvciBrZWVwaW5nIHRhYnMgb24gaG93IGxvbmcgdGhlIGN1cnJlbnQgYXJyYXkgc3RyaW5nIHdvdWxkIGJlXG4gIGNvbnN0IHRhcmdldEFycmF5ICAgICAgICAgICA9IFtdOyAvLyB0aGlzIGlzIHRoZSBhcnJheSBvZiB0YXJnZXQgYXJyYXlzXG4gIGxldCBjdXJyZW50VGFyZ2V0ICAgICAgICAgICA9IDA7XG4gIHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdICA9IFtdO1xuICBsZXQgaiAgICAgICAgICAgICAgICAgICAgICAgPSAwO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGluT2JqZWN0Lmxlbmd0aDsgaSsrKSB7XG4gICAgdGFyZ2V0QXJyYXlbY3VycmVudFRhcmdldF1bal0gPSB7fTtcbiAgICBsZXQgY2h1bmtSb3dDb3VudCAgICAgICAgICAgICA9IDA7XG5cbiAgICBmb3IgKGxldCBrZXkgaW4gaW5PYmplY3RbaV0pIHtcbiAgICAgIGNvbnN0IHRoaXNTcGVjICA9IHt9O1xuICAgICAgY29uc3QgdGhpc1ZhbHVlID0gaW5PYmplY3RbaV1ba2V5XTtcblxuICAgICAgLy9za2lwIHVuZGVmaW5lZCB2YWx1ZXNcbiAgICAgIGlmKHRoaXNWYWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHRoaXNWYWx1ZSA9PT0gbnVsbCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy90aHJvdyBhbiBlcnJvciBpZiB0aGVyZSdzIE5hTiB2YWx1ZVxuICAgICAgaWYodHlwZW9mIHRoaXNWYWx1ZSA9PT0gJ251bWJlcicgJiYgaXNOYU4odGhpc1ZhbHVlKSkge1xuICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCd0eXBlRXJyb3InLCAnTmFOIHZhbHVlIGluIG9uZSBvZiB0aGUgdmFsdWVzIChjb2x1bW5zKSBpcyBub3QgYWxsb3dlZCcpO1xuICAgICAgfVxuXG4gICAgICBpZih0aGlzVmFsdWUgPT09IC1JbmZpbml0eSB8fCB0aGlzVmFsdWUgPT09IEluZmluaXR5KSB7XG4gICAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ3R5cGVFcnJvcicsIHRoaXNWYWx1ZS50b1N0cmluZygpICsgJyB2YWx1ZSBpbiBvbmUgb2YgdGhlIHZhbHVlcyAoY29sdW1ucykgaXMgbm90IGFsbG93ZWQnKTtcbiAgICAgIH1cblxuICAgICAgaWYodGhpc1ZhbHVlID09PSB0cnVlIHx8IHRoaXNWYWx1ZSA9PT0gZmFsc2UpIHtcbiAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ0Jvb2xlYW4gdmFsdWUgaW4gb25lIG9mIHRoZSB2YWx1ZXMgKGNvbHVtbnMpIGlzIG5vdCBhbGxvd2VkJyk7XG4gICAgICB9XG5cbiAgICAgIC8vIGdldCB0eXBlLi4uIGlmIGl0IGlzIGFuIG9iamVjdCB0aGVuIGNvbnZlcnQgaXQgdG8ganNvbiBhbmQgc3RvcmUgYXMgYSBzdHJpbmdcbiAgICAgIGNvbnN0IHRoaXNUeXBlICA9IHR5cGVvZiAodGhpc1ZhbHVlKTtcblxuICAgICAgaWYgKHRoaXNUeXBlID09PSAnbnVtYmVyJykgeyAvLyBzdHJhaWdodGZvcndhcmQgbnVtYmVyXG4gICAgICAgIGlmKHRoaXNWYWx1ZSA8IE51bWJlci5NSU5fU0FGRV9JTlRFR0VSIHx8IHRoaXNWYWx1ZSA+IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSKSB7XG4gICAgICAgICAgbG9ncy5hZGRBcHBsaWNhdGlvbkxvZygnT2JqZWN0WycgKyBpICsgJ10uJyArIGtleSArICcgLSBUaGlzIHZhbHVlIGV4Y2VlZHMgZXhwZWN0ZWQgbnVtZXJpYyBwcmVjaXNpb24uJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpc1NwZWMuY29sTmFtZSAgICAgICAgICAgICAgICAgICAgPSBrZXk7XG4gICAgICAgIHRoaXNTcGVjLmNvbFR5cGUgICAgICAgICAgICAgICAgICAgID0gJ251bSc7XG4gICAgICAgIHRoaXNTcGVjLmNvbExlbmd0aCAgICAgICAgICAgICAgICAgID0gODtcbiAgICAgICAgdGhpc1NwZWMuZW5jb2RlZExlbmd0aCAgICAgICAgICAgICAgPSB0aGlzVmFsdWUudG9TdHJpbmcoKS5sZW5ndGg7XG4gICAgICAgIHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdW2pdW2tleV0gID0gdGhpc1ZhbHVlO1xuICAgICAgfSBlbHNlIGlmICh0aGlzVHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgdGhpc1NwZWMuY29sTmFtZSAgICA9IGtleTtcbiAgICAgICAgdGhpc1NwZWMuY29sVHlwZSAgICA9ICdzdHJpbmcnO1xuICAgICAgICB0aGlzU3BlYy5jb2xMZW5ndGggID0gdGhpc1ZhbHVlLmxlbmd0aDtcblxuICAgICAgICBpZiAodGhpc1ZhbHVlID09PSBcIlwiKSB7XG4gICAgICAgICAgdGFyZ2V0QXJyYXlbY3VycmVudFRhcmdldF1bal1ba2V5XSA9IFwiIFwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdW2pdW2tleV0gPSBlbmNvZGVVUklDb21wb25lbnQodGhpc1ZhbHVlKS5yZXBsYWNlKC8nL2csICclMjcnKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzU3BlYy5lbmNvZGVkTGVuZ3RoID0gdGFyZ2V0QXJyYXlbY3VycmVudFRhcmdldF1bal1ba2V5XS5sZW5ndGg7XG4gICAgICB9IGVsc2UgaWYodGhpc1ZhbHVlIGluc3RhbmNlb2YgRGF0ZSkge1xuICAgICAgXHRjb25zb2xlLmxvZyhcIkVSUk9SIFZBTFVFIFwiLCB0aGlzVmFsdWUpXG4gICAgICBcdGNvbnNvbGUubG9nKFwiVFlQRU9GIFZBTFVFIFwiLCB0eXBlb2YgdGhpc1ZhbHVlKVxuICAgICAgICB0aHJvdyBuZXcgaDU0c0Vycm9yKCd0eXBlRXJyb3InLCAnRGF0ZSB0eXBlIG5vdCBzdXBwb3J0ZWQuIFBsZWFzZSB1c2UgaDU0cy50b1Nhc0RhdGVUaW1lIGZ1bmN0aW9uIHRvIGNvbnZlcnQgaXQnKTtcbiAgICAgIH0gZWxzZSBpZiAodGhpc1R5cGUgPT0gJ29iamVjdCcpIHtcbiAgICAgICAgdGhpc1NwZWMuY29sTmFtZSAgICAgICAgICAgICAgICAgICAgPSBrZXk7XG4gICAgICAgIHRoaXNTcGVjLmNvbFR5cGUgICAgICAgICAgICAgICAgICAgID0gJ2pzb24nO1xuICAgICAgICB0aGlzU3BlYy5jb2xMZW5ndGggICAgICAgICAgICAgICAgICA9IEpTT04uc3RyaW5naWZ5KHRoaXNWYWx1ZSkubGVuZ3RoO1xuICAgICAgICB0YXJnZXRBcnJheVtjdXJyZW50VGFyZ2V0XVtqXVtrZXldICA9IGVuY29kZVVSSUNvbXBvbmVudChKU09OLnN0cmluZ2lmeSh0aGlzVmFsdWUpKS5yZXBsYWNlKC8nL2csICclMjcnKTtcbiAgICAgICAgdGhpc1NwZWMuZW5jb2RlZExlbmd0aCAgICAgICAgICAgICAgPSB0YXJnZXRBcnJheVtjdXJyZW50VGFyZ2V0XVtqXVtrZXldLmxlbmd0aDtcbiAgICAgIH1cblxuICAgICAgY2h1bmtSb3dDb3VudCA9IGNodW5rUm93Q291bnQgKyA2ICsga2V5Lmxlbmd0aCArIHRoaXNTcGVjLmVuY29kZWRMZW5ndGg7XG5cbiAgICAgIGlmIChjaGVja0FuZEluY3JlbWVudCh0aGlzU3BlYykgPT0gLTEpIHtcbiAgICAgICAgdGhyb3cgbmV3IGg1NHNFcnJvcigndHlwZUVycm9yJywgJ1RoZXJlIGlzIGEgdHlwZSBtaXNtYXRjaCBpbiB0aGUgYXJyYXkgYmV0d2VlbiB2YWx1ZXMgKGNvbHVtbnMpIG9mIHRoZSBzYW1lIG5hbWUuJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy9yZW1vdmUgbGFzdCBhZGRlZCByb3cgaWYgaXQncyBlbXB0eVxuICAgIGlmKE9iamVjdC5rZXlzKHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdW2pdKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdLnNwbGljZShqLCAxKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChjaHVua1Jvd0NvdW50ID4gY2h1bmtUaHJlc2hvbGQpIHtcbiAgICAgIHRocm93IG5ldyBoNTRzRXJyb3IoJ2FyZ3VtZW50RXJyb3InLCAnUm93ICcgKyBqICsgJyBleGNlZWRzIHNpemUgbGltaXQgb2YgMzJrYicpO1xuICAgIH0gZWxzZSBpZihjaHVua0FycmF5Q291bnQgKyBjaHVua1Jvd0NvdW50ID4gY2h1bmtUaHJlc2hvbGQpIHtcbiAgICAgIC8vY3JlYXRlIG5ldyBhcnJheSBpZiB0aGlzIG9uZSBpcyBmdWxsIGFuZCBtb3ZlIHRoZSBsYXN0IGl0ZW0gdG8gdGhlIG5ldyBhcnJheVxuICAgICAgY29uc3QgbGFzdFJvdyA9IHRhcmdldEFycmF5W2N1cnJlbnRUYXJnZXRdLnBvcCgpOyAvLyBnZXQgcmlkIG9mIHRoYXQgbGFzdCByb3dcbiAgICAgIGN1cnJlbnRUYXJnZXQrKzsgLy8gbW92ZSBvbnRvIHRoZSBuZXh0IGFycmF5XG4gICAgICB0YXJnZXRBcnJheVtjdXJyZW50VGFyZ2V0XSAgPSBbbGFzdFJvd107IC8vIG1ha2UgaXQgYW4gYXJyYXlcbiAgICAgIGogICAgICAgICAgICAgICAgICAgICAgICAgICA9IDA7IC8vIGluaXRpYWxpc2UgbmV3IHJvdyBjb3VudGVyIGZvciBuZXcgYXJyYXkgLSBpdCB3aWxsIGJlIGluY3JlbWVudGVkIGF0IHRoZSBlbmQgb2YgdGhlIGZ1bmN0aW9uXG4gICAgICBjaHVua0FycmF5Q291bnQgICAgICAgICAgICAgPSBjaHVua1Jvd0NvdW50OyAvLyB0aGlzIGlzIHRoZSBuZXcgY2h1bmsgbWF4IHNpemVcbiAgICB9IGVsc2Uge1xuICAgICAgY2h1bmtBcnJheUNvdW50ID0gY2h1bmtBcnJheUNvdW50ICsgY2h1bmtSb3dDb3VudDtcbiAgICB9XG4gICAgaisrO1xuICB9XG5cbiAgLy8gcmVmb3JtYXQgZXhpc3RpbmdDb2xzIGludG8gYW4gYXJyYXkgc28gc2FzIGNhbiBwYXJzZSBpdDtcbiAgY29uc3Qgc3BlY0FycmF5ID0gW107XG4gIGZvciAobGV0IGsgaW4gZXhpc3RpbmdDb2xzKSB7XG4gICAgc3BlY0FycmF5LnB1c2goZXhpc3RpbmdDb2xzW2tdKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIHNwZWM6ICAgICAgIHNwZWNBcnJheSxcbiAgICBkYXRhOiAgICAgICB0YXJnZXRBcnJheSxcbiAgICBqc29uTGVuZ3RoOiBjaHVua0FycmF5Q291bnRcbiAgfTsgLy8gdGhlIHNwZWMgd2lsbCBiZSB0aGUgbWFjcm9bMF0sIHdpdGggdGhlIGRhdGEgc3BsaXQgaW50byBhcnJheXMgb2YgbWFjcm9bMS1uXVxuICAvLyBtZWFucyBpbiB0ZXJtcyBvZiBkb2pvIHhociBvYmplY3QgYXQgbGVhc3QgdGhleSBuZWVkIHRvIGdvIGludG8gdGhlIHNhbWUgYXJyYXlcbn07XG5cbi8qXG4qIENvbnZlcnQgamF2YXNjcmlwdCBkYXRlIHRvIHNhcyB0aW1lXG4qXG4qIEBwYXJhbSB7b2JqZWN0fSBqc0RhdGUgLSBqYXZhc2NyaXB0IERhdGUgb2JqZWN0XG4qXG4qL1xubW9kdWxlLmV4cG9ydHMudG9TYXNEYXRlVGltZSA9IGZ1bmN0aW9uIChqc0RhdGUpIHtcbiAgY29uc3QgYmFzZWRhdGUgPSBuZXcgRGF0ZShcIkphbnVhcnkgMSwgMTk2MCAwMDowMDowMFwiKTtcbiAgY29uc3QgY3VycmRhdGUgPSBqc0RhdGU7XG5cbiAgLy8gb2Zmc2V0cyBmb3IgVVRDIGFuZCB0aW1lem9uZXMgYW5kIEJTVFxuICBjb25zdCBiYXNlT2Zmc2V0ID0gYmFzZWRhdGUuZ2V0VGltZXpvbmVPZmZzZXQoKTsgLy8gaW4gbWludXRlc1xuICBjb25zdCBjdXJyT2Zmc2V0ID0gY3VycmRhdGUuZ2V0VGltZXpvbmVPZmZzZXQoKTsgLy8gaW4gbWludXRlc1xuXG4gIC8vIGNvbnZlcnQgY3VycmRhdGUgdG8gYSBzYXMgZGF0ZXRpbWVcbiAgY29uc3Qgb2Zmc2V0U2VjcyAgICA9IChjdXJyT2Zmc2V0IC0gYmFzZU9mZnNldCkgKiA2MDsgLy8gb2Zmc2V0RGlmZiBpcyBpbiBtaW51dGVzIHRvIHN0YXJ0IHdpdGhcbiAgY29uc3QgYmFzZURhdGVTZWNzICA9IGJhc2VkYXRlLmdldFRpbWUoKSAvIDEwMDA7IC8vIGdldCByaWQgb2YgbXNcbiAgY29uc3QgY3VycmRhdGVTZWNzICA9IGN1cnJkYXRlLmdldFRpbWUoKSAvIDEwMDA7IC8vIGdldCByaWQgb2YgbXNcbiAgY29uc3Qgc2FzRGF0ZXRpbWUgICA9IE1hdGgucm91bmQoY3VycmRhdGVTZWNzIC0gYmFzZURhdGVTZWNzIC0gb2Zmc2V0U2Vjcyk7IC8vIGFkanVzdFxuXG4gIHJldHVybiBzYXNEYXRldGltZTtcbn07XG4iXX0=
|