You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

simulcast.bundle.js 95KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259
  1. !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.simulcast=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. /**
  3. *
  4. * @constructor
  5. */
  6. function SimulcastLogger(name, lvl) {
  7. this.name = name;
  8. this.lvl = lvl;
  9. }
  10. SimulcastLogger.prototype.log = function (text) {
  11. if (this.lvl) {
  12. console.log(text);
  13. }
  14. };
  15. SimulcastLogger.prototype.info = function (text) {
  16. if (this.lvl > 1) {
  17. console.info(text);
  18. }
  19. };
  20. SimulcastLogger.prototype.fine = function (text) {
  21. if (this.lvl > 2) {
  22. console.log(text);
  23. }
  24. };
  25. SimulcastLogger.prototype.error = function (text) {
  26. console.error(text);
  27. };
  28. module.exports = SimulcastLogger;
  29. },{}],2:[function(require,module,exports){
  30. var SimulcastLogger = require("./SimulcastLogger");
  31. var SimulcastUtils = require("./SimulcastUtils");
  32. function SimulcastReceiver() {
  33. this.simulcastUtils = new SimulcastUtils();
  34. this.logger = new SimulcastLogger('SimulcastReceiver', 1);
  35. }
  36. SimulcastReceiver.prototype._remoteVideoSourceCache = '';
  37. SimulcastReceiver.prototype._remoteMaps = {
  38. msid2Quality: {},
  39. ssrc2Msid: {},
  40. msid2ssrc: {},
  41. receivingVideoStreams: {}
  42. };
  43. SimulcastReceiver.prototype._cacheRemoteVideoSources = function (lines) {
  44. this._remoteVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
  45. };
  46. SimulcastReceiver.prototype._restoreRemoteVideoSources = function (lines) {
  47. this.simulcastUtils._replaceVideoSources(lines, this._remoteVideoSourceCache);
  48. };
  49. SimulcastReceiver.prototype._ensureGoogConference = function (lines) {
  50. var sb;
  51. this.logger.info('Ensuring x-google-conference flag...')
  52. if (this.simulcastUtils._indexOfArray('a=x-google-flag:conference', lines) === this.simulcastUtils._emptyCompoundIndex) {
  53. // TODO(gp) do that for the audio as well as suggested by fippo.
  54. // Add the google conference flag
  55. sb = this.simulcastUtils._getVideoSources(lines);
  56. sb = ['a=x-google-flag:conference'].concat(sb);
  57. this.simulcastUtils._replaceVideoSources(lines, sb);
  58. }
  59. };
  60. SimulcastReceiver.prototype._restoreSimulcastGroups = function (sb) {
  61. this._restoreRemoteVideoSources(sb);
  62. };
  63. /**
  64. * Restores the simulcast groups of the remote description. In
  65. * transformRemoteDescription we remove those in order for the set remote
  66. * description to succeed. The focus needs the signal the groups to new
  67. * participants.
  68. *
  69. * @param desc
  70. * @returns {*}
  71. */
  72. SimulcastReceiver.prototype.reverseTransformRemoteDescription = function (desc) {
  73. var sb;
  74. if (!this.simulcastUtils.isValidDescription(desc)) {
  75. return desc;
  76. }
  77. if (config.enableSimulcast) {
  78. sb = desc.sdp.split('\r\n');
  79. this._restoreSimulcastGroups(sb);
  80. desc = new RTCSessionDescription({
  81. type: desc.type,
  82. sdp: sb.join('\r\n')
  83. });
  84. }
  85. return desc;
  86. };
  87. SimulcastUtils.prototype._ensureOrder = function (lines) {
  88. var videoSources, sb;
  89. videoSources = this.parseMedia(lines, ['video'])[0];
  90. sb = this._compileVideoSources(videoSources);
  91. this._replaceVideoSources(lines, sb);
  92. };
  93. SimulcastReceiver.prototype._updateRemoteMaps = function (lines) {
  94. var remoteVideoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0],
  95. videoSource, quality;
  96. // (re) initialize the remote maps.
  97. this._remoteMaps.msid2Quality = {};
  98. this._remoteMaps.ssrc2Msid = {};
  99. this._remoteMaps.msid2ssrc = {};
  100. var self = this;
  101. if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
  102. remoteVideoSources.groups.forEach(function (group) {
  103. if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
  104. quality = 0;
  105. group.ssrcs.forEach(function (ssrc) {
  106. videoSource = remoteVideoSources.sources[ssrc];
  107. self._remoteMaps.msid2Quality[videoSource.msid] = quality++;
  108. self._remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
  109. self._remoteMaps.msid2ssrc[videoSource.msid] = videoSource.ssrc;
  110. });
  111. }
  112. });
  113. }
  114. };
  115. SimulcastReceiver.prototype._setReceivingVideoStream = function (resource, ssrc) {
  116. this._remoteMaps.receivingVideoStreams[resource] = ssrc;
  117. };
  118. /**
  119. * Returns a stream with single video track, the one currently being
  120. * received by this endpoint.
  121. *
  122. * @param stream the remote simulcast stream.
  123. * @returns {webkitMediaStream}
  124. */
  125. SimulcastReceiver.prototype.getReceivingVideoStream = function (stream) {
  126. var tracks, i, electedTrack, msid, quality = 0, receivingTrackId;
  127. var self = this;
  128. if (config.enableSimulcast) {
  129. stream.getVideoTracks().some(function (track) {
  130. return Object.keys(self._remoteMaps.receivingVideoStreams).some(function (resource) {
  131. var ssrc = self._remoteMaps.receivingVideoStreams[resource];
  132. var msid = self._remoteMaps.ssrc2Msid[ssrc];
  133. if (msid == [stream.id, track.id].join(' ')) {
  134. electedTrack = track;
  135. return true;
  136. }
  137. });
  138. });
  139. if (!electedTrack) {
  140. // we don't have an elected track, choose by initial quality.
  141. tracks = stream.getVideoTracks();
  142. for (i = 0; i < tracks.length; i++) {
  143. msid = [stream.id, tracks[i].id].join(' ');
  144. if (this._remoteMaps.msid2Quality[msid] === quality) {
  145. electedTrack = tracks[i];
  146. break;
  147. }
  148. }
  149. // TODO(gp) if the initialQuality could not be satisfied, lower
  150. // the requirement and try again.
  151. }
  152. }
  153. return (electedTrack)
  154. ? new webkitMediaStream([electedTrack])
  155. : stream;
  156. };
  157. SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
  158. var resource = Strophe.getResourceFromJid(jid);
  159. var ssrc = this._remoteMaps.receivingVideoStreams[resource];
  160. // If we haven't receiving a "changed" event yet, then we must be receiving
  161. // low quality (that the sender always streams).
  162. if(!ssrc)
  163. {
  164. var remoteStreamObject = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
  165. var remoteStream = remoteStreamObject.getOriginalStream();
  166. var tracks = remoteStream.getVideoTracks();
  167. if (tracks) {
  168. for (var k = 0; k < tracks.length; k++) {
  169. var track = tracks[k];
  170. var msid = [remoteStream.id, track.id].join(' ');
  171. var _ssrc = this._remoteMaps.msid2ssrc[msid];
  172. var quality = this._remoteMaps.msid2Quality[msid];
  173. if (quality == 0) {
  174. ssrc = _ssrc;
  175. }
  176. }
  177. }
  178. }
  179. return ssrc;
  180. };
  181. SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
  182. {
  183. var sid, electedStream;
  184. var i, j, k;
  185. var jid = ssrc2jid[ssrc];
  186. if(jid)
  187. {
  188. var remoteStreamObject = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
  189. var remoteStream = remoteStreamObject.getOriginalStream();
  190. var tracks = remoteStream.getVideoTracks();
  191. if (tracks) {
  192. for (k = 0; k < tracks.length; k++) {
  193. var track = tracks[k];
  194. var msid = [remoteStream.id, track.id].join(' ');
  195. var tmp = this._remoteMaps.msid2ssrc[msid];
  196. if (tmp == ssrc) {
  197. electedStream = new webkitMediaStream([track]);
  198. sid = remoteStreamObject.sid;
  199. // stream found, stop.
  200. break;
  201. }
  202. }
  203. }
  204. }
  205. return {
  206. sid: sid,
  207. stream: electedStream
  208. };
  209. };
  210. /**
  211. * Gets the fully qualified msid (stream.id + track.id) associated to the
  212. * SSRC.
  213. *
  214. * @param ssrc
  215. * @returns {*}
  216. */
  217. SimulcastReceiver.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
  218. return this._remoteMaps.ssrc2Msid[ssrc];
  219. };
  220. /**
  221. * Removes the ssrc-group:SIM from the remote description bacause Chrome
  222. * either gets confused and thinks this is an FID group or, if an FID group
  223. * is already present, it fails to set the remote description.
  224. *
  225. * @param desc
  226. * @returns {*}
  227. */
  228. SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
  229. if (desc && desc.sdp) {
  230. var sb = desc.sdp.split('\r\n');
  231. this._updateRemoteMaps(sb);
  232. this._cacheRemoteVideoSources(sb);
  233. // NOTE(gp) this needs to be called after updateRemoteMaps because we
  234. // need the simulcast group in the _updateRemoteMaps() method.
  235. this.simulcastUtils._removeSimulcastGroup(sb);
  236. if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
  237. // We don't need the goog conference flag if we're not doing
  238. // simulcast.
  239. this._ensureGoogConference(sb);
  240. }
  241. desc = new RTCSessionDescription({
  242. type: desc.type,
  243. sdp: sb.join('\r\n')
  244. });
  245. this.logger.fine(['Transformed remote description', desc.sdp].join(' '));
  246. }
  247. return desc;
  248. };
  249. module.exports = SimulcastReceiver;
  250. },{"./SimulcastLogger":1,"./SimulcastUtils":4}],3:[function(require,module,exports){
  251. var SimulcastLogger = require("./SimulcastLogger");
  252. var SimulcastUtils = require("./SimulcastUtils");
  253. function SimulcastSender() {
  254. this.simulcastUtils = new SimulcastUtils();
  255. this.logger = new SimulcastLogger('SimulcastSender', 1);
  256. }
  257. SimulcastSender.prototype.displayedLocalVideoStream = null;
  258. SimulcastSender.prototype._generateGuid = (function () {
  259. function s4() {
  260. return Math.floor((1 + Math.random()) * 0x10000)
  261. .toString(16)
  262. .substring(1);
  263. }
  264. return function () {
  265. return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
  266. s4() + '-' + s4() + s4() + s4();
  267. };
  268. }());
  269. // Returns a random integer between min (included) and max (excluded)
  270. // Using Math.round() gives a non-uniform distribution!
  271. SimulcastSender.prototype._generateRandomSSRC = function () {
  272. var min = 0, max = 0xffffffff;
  273. return Math.floor(Math.random() * (max - min)) + min;
  274. };
  275. SimulcastSender.prototype.getLocalVideoStream = function () {
  276. return (this.displayedLocalVideoStream != null)
  277. ? this.displayedLocalVideoStream
  278. // in case we have no simulcast at all, i.e. we didn't perform the GUM
  279. : RTC.localVideo.getOriginalStream();
  280. };
  281. function NativeSimulcastSender() {
  282. SimulcastSender.call(this); // call the super constructor.
  283. }
  284. NativeSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
  285. NativeSimulcastSender.prototype._localExplosionMap = {};
  286. NativeSimulcastSender.prototype._isUsingScreenStream = false;
  287. NativeSimulcastSender.prototype._localVideoSourceCache = '';
  288. NativeSimulcastSender.prototype.reset = function () {
  289. this._localExplosionMap = {};
  290. this._isUsingScreenStream = desktopsharing.isUsingScreenStream();
  291. };
  292. NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {
  293. this._localVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
  294. };
  295. NativeSimulcastSender.prototype._restoreLocalVideoSources = function (lines) {
  296. this.simulcastUtils._replaceVideoSources(lines, this._localVideoSourceCache);
  297. };
  298. NativeSimulcastSender.prototype._appendSimulcastGroup = function (lines) {
  299. var videoSources, ssrcGroup, simSSRC, numOfSubs = 2, i, sb, msid;
  300. this.logger.info('Appending simulcast group...');
  301. // Get the primary SSRC information.
  302. videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
  303. // Start building the SIM SSRC group.
  304. ssrcGroup = ['a=ssrc-group:SIM'];
  305. // The video source buffer.
  306. sb = [];
  307. // Create the simulcast sub-streams.
  308. for (i = 0; i < numOfSubs; i++) {
  309. // TODO(gp) prevent SSRC collision.
  310. simSSRC = this._generateRandomSSRC();
  311. ssrcGroup.push(simSSRC);
  312. sb.splice.apply(sb, [sb.length, 0].concat(
  313. [["a=ssrc:", simSSRC, " cname:", videoSources.base.cname].join(''),
  314. ["a=ssrc:", simSSRC, " msid:", videoSources.base.msid].join('')]
  315. ));
  316. this.logger.info(['Generated substream ', i, ' with SSRC ', simSSRC, '.'].join(''));
  317. }
  318. // Add the group sim layers.
  319. sb.splice(0, 0, ssrcGroup.join(' '))
  320. this.simulcastUtils._replaceVideoSources(lines, sb);
  321. };
  322. // Does the actual patching.
  323. NativeSimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
  324. this.logger.info('Ensuring simulcast group...');
  325. if (this.simulcastUtils._indexOfArray('a=ssrc-group:SIM', lines) === this.simulcastUtils._emptyCompoundIndex) {
  326. this._appendSimulcastGroup(lines);
  327. this._cacheLocalVideoSources(lines);
  328. } else {
  329. // verify that the ssrcs participating in the SIM group are present
  330. // in the SDP (needed for presence).
  331. this._restoreLocalVideoSources(lines);
  332. }
  333. };
  334. /**
  335. * Produces a single stream with multiple tracks for local video sources.
  336. *
  337. * @param lines
  338. * @private
  339. */
  340. NativeSimulcastSender.prototype._explodeSimulcastSenderSources = function (lines) {
  341. var sb, msid, sid, tid, videoSources, self;
  342. this.logger.info('Exploding local video sources...');
  343. videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
  344. self = this;
  345. if (videoSources.groups && videoSources.groups.length !== 0) {
  346. videoSources.groups.forEach(function (group) {
  347. if (group.semantics === 'SIM') {
  348. group.ssrcs.forEach(function (ssrc) {
  349. // Get the msid for this ssrc..
  350. if (self._localExplosionMap[ssrc]) {
  351. // .. either from the explosion map..
  352. msid = self._localExplosionMap[ssrc];
  353. } else {
  354. // .. or generate a new one (msid).
  355. sid = videoSources.sources[ssrc].msid
  356. .substring(0, videoSources.sources[ssrc].msid.indexOf(' '));
  357. tid = self._generateGuid();
  358. msid = [sid, tid].join(' ');
  359. self._localExplosionMap[ssrc] = msid;
  360. }
  361. // Assign it to the source object.
  362. videoSources.sources[ssrc].msid = msid;
  363. // TODO(gp) Change the msid of associated sources.
  364. });
  365. }
  366. });
  367. }
  368. sb = this.simulcastUtils._compileVideoSources(videoSources);
  369. this.simulcastUtils._replaceVideoSources(lines, sb);
  370. };
  371. /**
  372. * GUM for simulcast.
  373. *
  374. * @param constraints
  375. * @param success
  376. * @param err
  377. */
  378. NativeSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
  379. // There's nothing special to do for native simulcast, so just do a normal GUM.
  380. navigator.webkitGetUserMedia(constraints, function (hqStream) {
  381. success(hqStream);
  382. }, err);
  383. };
  384. /**
  385. * Prepares the local description for public usage (i.e. to be signaled
  386. * through Jingle to the focus).
  387. *
  388. * @param desc
  389. * @returns {RTCSessionDescription}
  390. */
  391. NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
  392. var sb;
  393. if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
  394. return desc;
  395. }
  396. sb = desc.sdp.split('\r\n');
  397. this._explodeSimulcastSenderSources(sb);
  398. desc = new RTCSessionDescription({
  399. type: desc.type,
  400. sdp: sb.join('\r\n')
  401. });
  402. this.logger.fine(['Exploded local video sources', desc.sdp].join(' '));
  403. return desc;
  404. };
  405. /**
  406. * Ensures that the simulcast group is present in the answer, _if_ native
  407. * simulcast is enabled,
  408. *
  409. * @param desc
  410. * @returns {*}
  411. */
  412. NativeSimulcastSender.prototype.transformAnswer = function (desc) {
  413. if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
  414. return desc;
  415. }
  416. var sb = desc.sdp.split('\r\n');
  417. // Even if we have enabled native simulcasting previously
  418. // (with a call to SLD with an appropriate SDP, for example),
  419. // createAnswer seems to consistently generate incomplete SDP
  420. // with missing SSRCS.
  421. //
  422. // So, subsequent calls to SLD will have missing SSRCS and presence
  423. // won't have the complete list of SRCs.
  424. this._ensureSimulcastGroup(sb);
  425. desc = new RTCSessionDescription({
  426. type: desc.type,
  427. sdp: sb.join('\r\n')
  428. });
  429. this.logger.fine(['Transformed answer', desc.sdp].join(' '));
  430. return desc;
  431. };
  432. /**
  433. *
  434. *
  435. * @param desc
  436. * @returns {*}
  437. */
  438. NativeSimulcastSender.prototype.transformLocalDescription = function (desc) {
  439. return desc;
  440. };
  441. NativeSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
  442. // Nothing to do here, native simulcast does that auto-magically.
  443. };
  444. NativeSimulcastSender.prototype.constructor = NativeSimulcastSender;
  445. function SimpleSimulcastSender() {
  446. SimulcastSender.call(this);
  447. }
  448. SimpleSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
  449. SimpleSimulcastSender.prototype.localStream = null;
  450. SimpleSimulcastSender.prototype._localMaps = {
  451. msids: [],
  452. msid2ssrc: {}
  453. };
  454. /**
  455. * Groups local video sources together in the ssrc-group:SIM group.
  456. *
  457. * @param lines
  458. * @private
  459. */
  460. SimpleSimulcastSender.prototype._groupLocalVideoSources = function (lines) {
  461. var sb, videoSources, ssrcs = [], ssrc;
  462. this.logger.info('Grouping local video sources...');
  463. videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
  464. for (ssrc in videoSources.sources) {
  465. // jitsi-meet destroys/creates streams at various places causing
  466. // the original local stream ids to change. The only thing that
  467. // remains unchanged is the trackid.
  468. this._localMaps.msid2ssrc[videoSources.sources[ssrc].msid.split(' ')[1]] = ssrc;
  469. }
  470. var self = this;
  471. // TODO(gp) add only "free" sources.
  472. this._localMaps.msids.forEach(function (msid) {
  473. ssrcs.push(self._localMaps.msid2ssrc[msid]);
  474. });
  475. if (!videoSources.groups) {
  476. videoSources.groups = [];
  477. }
  478. videoSources.groups.push({
  479. 'semantics': 'SIM',
  480. 'ssrcs': ssrcs
  481. });
  482. sb = this.simulcastUtils._compileVideoSources(videoSources);
  483. this.simulcastUtils._replaceVideoSources(lines, sb);
  484. };
  485. /**
  486. * GUM for simulcast.
  487. *
  488. * @param constraints
  489. * @param success
  490. * @param err
  491. */
  492. SimpleSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
  493. // TODO(gp) what if we request a resolution not supported by the hardware?
  494. // TODO(gp) make the lq stream configurable; although this wouldn't work with native simulcast
  495. var lqConstraints = {
  496. audio: false,
  497. video: {
  498. mandatory: {
  499. maxWidth: 320,
  500. maxHeight: 180,
  501. maxFrameRate: 15
  502. }
  503. }
  504. };
  505. this.logger.info('HQ constraints: ', constraints);
  506. this.logger.info('LQ constraints: ', lqConstraints);
  507. // NOTE(gp) if we request the lq stream first webkitGetUserMedia
  508. // fails randomly. Tested with Chrome 37. As fippo suggested, the
  509. // reason appears to be that Chrome only acquires the cam once and
  510. // then downscales the picture (https://code.google.com/p/chromium/issues/detail?id=346616#c11)
  511. var self = this;
  512. navigator.webkitGetUserMedia(constraints, function (hqStream) {
  513. self.localStream = hqStream;
  514. // reset local maps.
  515. self._localMaps.msids = [];
  516. self._localMaps.msid2ssrc = {};
  517. // add hq trackid to local map
  518. self._localMaps.msids.push(hqStream.getVideoTracks()[0].id);
  519. navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
  520. self.displayedLocalVideoStream = lqStream;
  521. // NOTE(gp) The specification says Array.forEach() will visit
  522. // the array elements in numeric order, and that it doesn't
  523. // visit elements that don't exist.
  524. // add lq trackid to local map
  525. self._localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
  526. self.localStream.addTrack(lqStream.getVideoTracks()[0]);
  527. success(self.localStream);
  528. }, err);
  529. }, err);
  530. };
  531. /**
  532. * Prepares the local description for public usage (i.e. to be signaled
  533. * through Jingle to the focus).
  534. *
  535. * @param desc
  536. * @returns {RTCSessionDescription}
  537. */
  538. SimpleSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
  539. var sb;
  540. if (!this.simulcastUtils.isValidDescription(desc)) {
  541. return desc;
  542. }
  543. sb = desc.sdp.split('\r\n');
  544. this._groupLocalVideoSources(sb);
  545. desc = new RTCSessionDescription({
  546. type: desc.type,
  547. sdp: sb.join('\r\n')
  548. });
  549. this.logger.fine('Grouped local video sources');
  550. this.logger.fine(desc.sdp);
  551. return desc;
  552. };
  553. /**
  554. * Ensures that the simulcast group is present in the answer, _if_ native
  555. * simulcast is enabled,
  556. *
  557. * @param desc
  558. * @returns {*}
  559. */
  560. SimpleSimulcastSender.prototype.transformAnswer = function (desc) {
  561. return desc;
  562. };
  563. /**
  564. *
  565. *
  566. * @param desc
  567. * @returns {*}
  568. */
  569. SimpleSimulcastSender.prototype.transformLocalDescription = function (desc) {
  570. var sb = desc.sdp.split('\r\n');
  571. this.simulcastUtils._removeSimulcastGroup(sb);
  572. desc = new RTCSessionDescription({
  573. type: desc.type,
  574. sdp: sb.join('\r\n')
  575. });
  576. this.logger.fine('Transformed local description');
  577. this.logger.fine(desc.sdp);
  578. return desc;
  579. };
  580. SimpleSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
  581. var trackid;
  582. var self = this;
  583. this.logger.log(['Requested to', enabled ? 'enable' : 'disable', ssrc].join(' '));
  584. if (Object.keys(this._localMaps.msid2ssrc).some(function (tid) {
  585. // Search for the track id that corresponds to the ssrc
  586. if (self._localMaps.msid2ssrc[tid] == ssrc) {
  587. trackid = tid;
  588. return true;
  589. }
  590. }) && self.localStream.getVideoTracks().some(function (track) {
  591. // Start/stop the track that corresponds to the track id
  592. if (track.id === trackid) {
  593. track.enabled = enabled;
  594. return true;
  595. }
  596. })) {
  597. this.logger.log([trackid, enabled ? 'enabled' : 'disabled'].join(' '));
  598. $(document).trigger(enabled
  599. ? 'simulcastlayerstarted'
  600. : 'simulcastlayerstopped');
  601. } else {
  602. this.logger.error("I don't have a local stream with SSRC " + ssrc);
  603. }
  604. };
  605. SimpleSimulcastSender.prototype.constructor = SimpleSimulcastSender;
  606. function NoSimulcastSender() {
  607. SimulcastSender.call(this);
  608. }
  609. NoSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
  610. /**
  611. * GUM for simulcast.
  612. *
  613. * @param constraints
  614. * @param success
  615. * @param err
  616. */
  617. NoSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
  618. navigator.webkitGetUserMedia(constraints, function (hqStream) {
  619. success(hqStream);
  620. }, err);
  621. };
  622. /**
  623. * Prepares the local description for public usage (i.e. to be signaled
  624. * through Jingle to the focus).
  625. *
  626. * @param desc
  627. * @returns {RTCSessionDescription}
  628. */
  629. NoSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
  630. return desc;
  631. };
  632. /**
  633. * Ensures that the simulcast group is present in the answer, _if_ native
  634. * simulcast is enabled,
  635. *
  636. * @param desc
  637. * @returns {*}
  638. */
  639. NoSimulcastSender.prototype.transformAnswer = function (desc) {
  640. return desc;
  641. };
  642. /**
  643. *
  644. *
  645. * @param desc
  646. * @returns {*}
  647. */
  648. NoSimulcastSender.prototype.transformLocalDescription = function (desc) {
  649. return desc;
  650. };
  651. NoSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
  652. };
  653. NoSimulcastSender.prototype.constructor = NoSimulcastSender;
  654. module.exports = {
  655. "native": NativeSimulcastSender,
  656. "no": NoSimulcastSender
  657. }
  658. },{"./SimulcastLogger":1,"./SimulcastUtils":4}],4:[function(require,module,exports){
  659. var SimulcastLogger = require("./SimulcastLogger");
  660. /**
  661. *
  662. * @constructor
  663. */
  664. function SimulcastUtils() {
  665. this.logger = new SimulcastLogger("SimulcastUtils", 1);
  666. }
  667. /**
  668. *
  669. * @type {{}}
  670. * @private
  671. */
  672. SimulcastUtils.prototype._emptyCompoundIndex = {};
  673. /**
  674. *
  675. * @param lines
  676. * @param videoSources
  677. * @private
  678. */
  679. SimulcastUtils.prototype._replaceVideoSources = function (lines, videoSources) {
  680. var i, inVideo = false, index = -1, howMany = 0;
  681. this.logger.info('Replacing video sources...');
  682. for (i = 0; i < lines.length; i++) {
  683. if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
  684. // Out of video.
  685. break;
  686. }
  687. if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
  688. // In video.
  689. inVideo = true;
  690. }
  691. if (inVideo && (lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:'
  692. || lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:')) {
  693. if (index === -1) {
  694. index = i;
  695. }
  696. howMany++;
  697. }
  698. }
  699. // efficiency baby ;)
  700. lines.splice.apply(lines,
  701. [index, howMany].concat(videoSources));
  702. };
  703. SimulcastUtils.prototype.isValidDescription = function (desc)
  704. {
  705. return desc && desc != null
  706. && desc.type && desc.type != ''
  707. && desc.sdp && desc.sdp != '';
  708. };
  709. SimulcastUtils.prototype._getVideoSources = function (lines) {
  710. var i, inVideo = false, sb = [];
  711. this.logger.info('Getting video sources...');
  712. for (i = 0; i < lines.length; i++) {
  713. if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
  714. // Out of video.
  715. break;
  716. }
  717. if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
  718. // In video.
  719. inVideo = true;
  720. }
  721. if (inVideo && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
  722. // In SSRC.
  723. sb.push(lines[i]);
  724. }
  725. if (inVideo && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
  726. sb.push(lines[i]);
  727. }
  728. }
  729. return sb;
  730. };
  731. SimulcastUtils.prototype.parseMedia = function (lines, mediatypes) {
  732. var i, res = [], type, cur_media, idx, ssrcs, cur_ssrc, ssrc,
  733. ssrc_attribute, group, semantics, skip = true;
  734. this.logger.info('Parsing media sources...');
  735. for (i = 0; i < lines.length; i++) {
  736. if (lines[i].substring(0, 'm='.length) === 'm=') {
  737. type = lines[i]
  738. .substr('m='.length, lines[i].indexOf(' ') - 'm='.length);
  739. skip = mediatypes !== undefined && mediatypes.indexOf(type) === -1;
  740. if (!skip) {
  741. cur_media = {
  742. 'type': type,
  743. 'sources': {},
  744. 'groups': []
  745. };
  746. res.push(cur_media);
  747. }
  748. } else if (!skip && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
  749. idx = lines[i].indexOf(' ');
  750. ssrc = lines[i].substring('a=ssrc:'.length, idx);
  751. if (cur_media.sources[ssrc] === undefined) {
  752. cur_ssrc = {'ssrc': ssrc};
  753. cur_media.sources[ssrc] = cur_ssrc;
  754. }
  755. ssrc_attribute = lines[i].substr(idx + 1).split(':', 2)[0];
  756. cur_ssrc[ssrc_attribute] = lines[i].substr(idx + 1).split(':', 2)[1];
  757. if (cur_media.base === undefined) {
  758. cur_media.base = cur_ssrc;
  759. }
  760. } else if (!skip && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
  761. idx = lines[i].indexOf(' ');
  762. semantics = lines[i].substr(0, idx).substr('a=ssrc-group:'.length);
  763. ssrcs = lines[i].substr(idx).trim().split(' ');
  764. group = {
  765. 'semantics': semantics,
  766. 'ssrcs': ssrcs
  767. };
  768. cur_media.groups.push(group);
  769. } else if (!skip && (lines[i].substring(0, 'a=sendrecv'.length) === 'a=sendrecv' ||
  770. lines[i].substring(0, 'a=recvonly'.length) === 'a=recvonly' ||
  771. lines[i].substring(0, 'a=sendonly'.length) === 'a=sendonly' ||
  772. lines[i].substring(0, 'a=inactive'.length) === 'a=inactive')) {
  773. cur_media.direction = lines[i].substring('a='.length);
  774. }
  775. }
  776. return res;
  777. };
  778. /**
  779. * The _indexOfArray() method returns the first a CompoundIndex at which a
  780. * given element can be found in the array, or _emptyCompoundIndex if it is
  781. * not present.
  782. *
  783. * Example:
  784. *
  785. * _indexOfArray('3', [ 'this is line 1', 'this is line 2', 'this is line 3' ])
  786. *
  787. * returns {row: 2, column: 14}
  788. *
  789. * @param needle
  790. * @param haystack
  791. * @param start
  792. * @returns {}
  793. * @private
  794. */
  795. SimulcastUtils.prototype._indexOfArray = function (needle, haystack, start) {
  796. var length = haystack.length, idx, i;
  797. if (!start) {
  798. start = 0;
  799. }
  800. for (i = start; i < length; i++) {
  801. idx = haystack[i].indexOf(needle);
  802. if (idx !== -1) {
  803. return {row: i, column: idx};
  804. }
  805. }
  806. return this._emptyCompoundIndex;
  807. };
  808. SimulcastUtils.prototype._removeSimulcastGroup = function (lines) {
  809. var i;
  810. for (i = lines.length - 1; i >= 0; i--) {
  811. if (lines[i].indexOf('a=ssrc-group:SIM') !== -1) {
  812. lines.splice(i, 1);
  813. }
  814. }
  815. };
  816. SimulcastUtils.prototype._compileVideoSources = function (videoSources) {
  817. var sb = [], ssrc, addedSSRCs = [];
  818. this.logger.info('Compiling video sources...');
  819. // Add the groups
  820. if (videoSources.groups && videoSources.groups.length !== 0) {
  821. videoSources.groups.forEach(function (group) {
  822. if (group.ssrcs && group.ssrcs.length !== 0) {
  823. sb.push([['a=ssrc-group:', group.semantics].join(''), group.ssrcs.join(' ')].join(' '));
  824. // if (group.semantics !== 'SIM') {
  825. group.ssrcs.forEach(function (ssrc) {
  826. addedSSRCs.push(ssrc);
  827. sb.splice.apply(sb, [sb.length, 0].concat([
  828. ["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
  829. ["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
  830. });
  831. //}
  832. }
  833. });
  834. }
  835. // Then add any free sources.
  836. if (videoSources.sources) {
  837. for (ssrc in videoSources.sources) {
  838. if (addedSSRCs.indexOf(ssrc) === -1) {
  839. sb.splice.apply(sb, [sb.length, 0].concat([
  840. ["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
  841. ["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
  842. }
  843. }
  844. }
  845. return sb;
  846. };
  847. module.exports = SimulcastUtils;
  848. },{"./SimulcastLogger":1}],5:[function(require,module,exports){
  849. /*jslint plusplus: true */
  850. /*jslint nomen: true*/
  851. var SimulcastSender = require("./SimulcastSender");
  852. var NoSimulcastSender = SimulcastSender["no"];
  853. var NativeSimulcastSender = SimulcastSender["native"];
  854. var SimulcastReceiver = require("./SimulcastReceiver");
  855. var SimulcastUtils = require("./SimulcastUtils");
  856. /**
  857. *
  858. * @constructor
  859. */
  860. function SimulcastManager() {
  861. // Create the simulcast utilities.
  862. this.simulcastUtils = new SimulcastUtils();
  863. // Create remote simulcast.
  864. this.simulcastReceiver = new SimulcastReceiver();
  865. // Initialize local simulcast.
  866. // TODO(gp) move into SimulcastManager.prototype.getUserMedia and take into
  867. // account constraints.
  868. if (!config.enableSimulcast) {
  869. this.simulcastSender = new NoSimulcastSender();
  870. } else {
  871. var isChromium = window.chrome,
  872. vendorName = window.navigator.vendor;
  873. if(isChromium !== null && isChromium !== undefined
  874. /* skip opera */
  875. && vendorName === "Google Inc."
  876. /* skip Chromium as suggested by fippo */
  877. && !window.navigator.appVersion.match(/Chromium\//) ) {
  878. var ver = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
  879. if (ver > 37) {
  880. this.simulcastSender = new NativeSimulcastSender();
  881. } else {
  882. this.simulcastSender = new NoSimulcastSender();
  883. }
  884. } else {
  885. this.simulcastSender = new NoSimulcastSender();
  886. }
  887. }
  888. }
  889. /**
  890. * Restores the simulcast groups of the remote description. In
  891. * transformRemoteDescription we remove those in order for the set remote
  892. * description to succeed. The focus needs the signal the groups to new
  893. * participants.
  894. *
  895. * @param desc
  896. * @returns {*}
  897. */
  898. SimulcastManager.prototype.reverseTransformRemoteDescription = function (desc) {
  899. return this.simulcastReceiver.reverseTransformRemoteDescription(desc);
  900. };
  901. /**
  902. * Removes the ssrc-group:SIM from the remote description bacause Chrome
  903. * either gets confused and thinks this is an FID group or, if an FID group
  904. * is already present, it fails to set the remote description.
  905. *
  906. * @param desc
  907. * @returns {*}
  908. */
  909. SimulcastManager.prototype.transformRemoteDescription = function (desc) {
  910. return this.simulcastReceiver.transformRemoteDescription(desc);
  911. };
  912. /**
  913. * Gets the fully qualified msid (stream.id + track.id) associated to the
  914. * SSRC.
  915. *
  916. * @param ssrc
  917. * @returns {*}
  918. */
  919. SimulcastManager.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
  920. return this.simulcastReceiver.getRemoteVideoStreamIdBySSRC(ssrc);
  921. };
  922. /**
  923. * Returns a stream with single video track, the one currently being
  924. * received by this endpoint.
  925. *
  926. * @param stream the remote simulcast stream.
  927. * @returns {webkitMediaStream}
  928. */
  929. SimulcastManager.prototype.getReceivingVideoStream = function (stream) {
  930. return this.simulcastReceiver.getReceivingVideoStream(stream);
  931. };
  932. /**
  933. *
  934. *
  935. * @param desc
  936. * @returns {*}
  937. */
  938. SimulcastManager.prototype.transformLocalDescription = function (desc) {
  939. return this.simulcastSender.transformLocalDescription(desc);
  940. };
  941. /**
  942. *
  943. * @returns {*}
  944. */
  945. SimulcastManager.prototype.getLocalVideoStream = function() {
  946. return this.simulcastSender.getLocalVideoStream();
  947. };
  948. /**
  949. * GUM for simulcast.
  950. *
  951. * @param constraints
  952. * @param success
  953. * @param err
  954. */
  955. SimulcastManager.prototype.getUserMedia = function (constraints, success, err) {
  956. this.simulcastSender.getUserMedia(constraints, success, err);
  957. };
  958. /**
  959. * Prepares the local description for public usage (i.e. to be signaled
  960. * through Jingle to the focus).
  961. *
  962. * @param desc
  963. * @returns {RTCSessionDescription}
  964. */
  965. SimulcastManager.prototype.reverseTransformLocalDescription = function (desc) {
  966. return this.simulcastSender.reverseTransformLocalDescription(desc);
  967. };
  968. /**
  969. * Ensures that the simulcast group is present in the answer, _if_ native
  970. * simulcast is enabled,
  971. *
  972. * @param desc
  973. * @returns {*}
  974. */
  975. SimulcastManager.prototype.transformAnswer = function (desc) {
  976. return this.simulcastSender.transformAnswer(desc);
  977. };
  978. SimulcastManager.prototype.getReceivingSSRC = function (jid) {
  979. return this.simulcastReceiver.getReceivingSSRC(jid);
  980. };
  981. SimulcastManager.prototype.getReceivingVideoStreamBySSRC = function (msid) {
  982. return this.simulcastReceiver.getReceivingVideoStreamBySSRC(msid);
  983. };
  984. /**
  985. *
  986. * @param lines
  987. * @param mediatypes
  988. * @returns {*}
  989. */
  990. SimulcastManager.prototype.parseMedia = function(lines, mediatypes) {
  991. var sb = lines.sdp.split('\r\n');
  992. return this.simulcastUtils.parseMedia(sb, mediatypes);
  993. };
  994. SimulcastManager.prototype._setReceivingVideoStream = function(resource, ssrc) {
  995. this.simulcastReceiver._setReceivingVideoStream(resource, ssrc);
  996. };
  997. SimulcastManager.prototype._setLocalVideoStreamEnabled = function(ssrc, enabled) {
  998. this.simulcastSender._setLocalVideoStreamEnabled(ssrc, enabled);
  999. };
  1000. SimulcastManager.prototype.resetSender = function() {
  1001. if (typeof this.simulcastSender.reset === 'function'){
  1002. this.simulcastSender.reset();
  1003. }
  1004. };
  1005. $(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {
  1006. endpointSimulcastLayers.forEach(function (esl) {
  1007. var ssrc = esl.simulcastLayer.primarySSRC;
  1008. simulcast._setReceivingVideoStream(esl.endpoint, ssrc);
  1009. });
  1010. });
  1011. $(document).bind('startsimulcastlayer', function (event, simulcastLayer) {
  1012. var ssrc = simulcastLayer.primarySSRC;
  1013. simulcast._setLocalVideoStreamEnabled(ssrc, true);
  1014. });
  1015. $(document).bind('stopsimulcastlayer', function (event, simulcastLayer) {
  1016. var ssrc = simulcastLayer.primarySSRC;
  1017. simulcast._setLocalVideoStreamEnabled(ssrc, false);
  1018. });
  1019. var simulcast = new SimulcastManager();
  1020. module.exports = simulcast;
  1021. },{"./SimulcastReceiver":2,"./SimulcastSender":3,"./SimulcastUtils":4}]},{},[5])(5)
  1022. });
  1023. //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RMb2dnZXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RSZWNlaXZlci5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFNlbmRlci5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFV0aWxzLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9zaW11bGNhc3Qvc2ltdWxjYXN0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0UUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdmdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hPQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qKlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBTaW11bGNhc3RMb2dnZXIobmFtZSwgbHZsKSB7XG4gICAgdGhpcy5uYW1lID0gbmFtZTtcbiAgICB0aGlzLmx2bCA9IGx2bDtcbn1cblxuU2ltdWxjYXN0TG9nZ2VyLnByb3RvdHlwZS5sb2cgPSBmdW5jdGlvbiAodGV4dCkge1xuICAgIGlmICh0aGlzLmx2bCkge1xuICAgICAgICBjb25zb2xlLmxvZyh0ZXh0KTtcbiAgICB9XG59O1xuXG5TaW11bGNhc3RMb2dnZXIucHJvdG90eXBlLmluZm8gPSBmdW5jdGlvbiAodGV4dCkge1xuICAgIGlmICh0aGlzLmx2bCA+IDEpIHtcbiAgICAgICAgY29uc29sZS5pbmZvKHRleHQpO1xuICAgIH1cbn07XG5cblNpbXVsY2FzdExvZ2dlci5wcm90b3R5cGUuZmluZSA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgaWYgKHRoaXMubHZsID4gMikge1xuICAgICAgICBjb25zb2xlLmxvZyh0ZXh0KTtcbiAgICB9XG59O1xuXG5TaW11bGNhc3RMb2dnZXIucHJvdG90eXBlLmVycm9yID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICBjb25zb2xlLmVycm9yKHRleHQpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTaW11bGNhc3RMb2dnZXI7IiwidmFyIFNpbXVsY2FzdExvZ2dlciA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdExvZ2dlclwiKTtcbnZhciBTaW11bGNhc3RVdGlscyA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdFV0aWxzXCIpO1xuXG5mdW5jdGlvbiBTaW11bGNhc3RSZWNlaXZlcigpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzID0gbmV3IFNpbXVsY2FzdFV0aWxzKCk7XG4gICAgdGhpcy5sb2dnZXIgPSBuZXcgU2ltdWxjYXN0TG9nZ2VyKCdTaW11bGNhc3RSZWNlaXZlcicsIDEpO1xufVxuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3JlbW90ZVZpZGVvU291cmNlQ2FjaGUgPSAnJztcblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fcmVtb3RlTWFwcyA9IHtcbiAgICBtc2lkMlF1YWxpdHk6IHt9LFxuICAgIHNzcmMyTXNpZDoge30sXG4gICAgbXNpZDJzc3JjOiB7fSxcbiAgICByZWNlaXZpbmdWaWRlb1N0cmVhbXM6IHt9XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX2NhY2hlUmVtb3RlVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdGhpcy5fcmVtb3RlVmlkZW9Tb3VyY2VDYWNoZSA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2dldFZpZGVvU291cmNlcyhsaW5lcyk7XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3Jlc3RvcmVSZW1vdGVWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCB0aGlzLl9yZW1vdGVWaWRlb1NvdXJjZUNhY2hlKTtcbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fZW5zdXJlR29vZ0NvbmZlcmVuY2UgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgc2I7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdFbnN1cmluZyB4LWdvb2dsZS1jb25mZXJlbmNlIGZsYWcuLi4nKVxuXG4gICAgaWYgKHRoaXMuc2ltdWxjYXN0VXRpbHMuX2luZGV4T2ZBcnJheSgnYT14LWdvb2dsZS1mbGFnOmNvbmZlcmVuY2UnLCBsaW5lcykgPT09IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2VtcHR5Q29tcG91bmRJbmRleCkge1xuICAgICAgICAvLyBUT0RPKGdwKSBkbyB0aGF0IGZvciB0aGUgYXVkaW8gYXMgd2VsbCBhcyBzdWdnZXN0ZWQgYnkgZmlwcG8uXG4gICAgICAgIC8vIEFkZCB0aGUgZ29vZ2xlIGNvbmZlcmVuY2UgZmxhZ1xuICAgICAgICBzYiA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2dldFZpZGVvU291cmNlcyhsaW5lcyk7XG4gICAgICAgIHNiID0gWydhPXgtZ29vZ2xlLWZsYWc6Y29uZmVyZW5jZSddLmNvbmNhdChzYik7XG4gICAgICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHNiKTtcbiAgICB9XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3Jlc3RvcmVTaW11bGNhc3RHcm91cHMgPSBmdW5jdGlvbiAoc2IpIHtcbiAgICB0aGlzLl9yZXN0b3JlUmVtb3RlVmlkZW9Tb3VyY2VzKHNiKTtcbn07XG5cbi8qKlxuICogUmVzdG9yZXMgdGhlIHNpbXVsY2FzdCBncm91cHMgb2YgdGhlIHJlbW90ZSBkZXNjcmlwdGlvbi4gSW5cbiAqIHRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uIHdlIHJlbW92ZSB0aG9zZSBpbiBvcmRlciBmb3IgdGhlIHNldCByZW1vdGVcbiAqIGRlc2NyaXB0aW9uIHRvIHN1Y2NlZWQuIFRoZSBmb2N1cyBuZWVkcyB0aGUgc2lnbmFsIHRoZSBncm91cHMgdG8gbmV3XG4gKiBwYXJ0aWNpcGFudHMuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICB2YXIgc2I7XG5cbiAgICBpZiAoIXRoaXMuc2ltdWxjYXN0VXRpbHMuaXNWYWxpZERlc2NyaXB0aW9uKGRlc2MpKSB7XG4gICAgICAgIHJldHVybiBkZXNjO1xuICAgIH1cblxuICAgIGlmIChjb25maWcuZW5hYmxlU2ltdWxjYXN0KSB7XG4gICAgICAgIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgICAgIHRoaXMuX3Jlc3RvcmVTaW11bGNhc3RHcm91cHMoc2IpO1xuXG4gICAgICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX2Vuc3VyZU9yZGVyID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHZpZGVvU291cmNlcywgc2I7XG5cbiAgICB2aWRlb1NvdXJjZXMgPSB0aGlzLnBhcnNlTWVkaWEobGluZXMsIFsndmlkZW8nXSlbMF07XG4gICAgc2IgPSB0aGlzLl9jb21waWxlVmlkZW9Tb3VyY2VzKHZpZGVvU291cmNlcyk7XG5cbiAgICB0aGlzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCBzYik7XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3VwZGF0ZVJlbW90ZU1hcHMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgcmVtb3RlVmlkZW9Tb3VyY2VzID0gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdLFxuICAgICAgICB2aWRlb1NvdXJjZSwgcXVhbGl0eTtcblxuICAgIC8vIChyZSkgaW5pdGlhbGl6ZSB0aGUgcmVtb3RlIG1hcHMuXG4gICAgdGhpcy5fcmVtb3RlTWFwcy5tc2lkMlF1YWxpdHkgPSB7fTtcbiAgICB0aGlzLl9yZW1vdGVNYXBzLnNzcmMyTXNpZCA9IHt9O1xuICAgIHRoaXMuX3JlbW90ZU1hcHMubXNpZDJzc3JjID0ge307XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHJlbW90ZVZpZGVvU291cmNlcy5ncm91cHMgJiYgcmVtb3RlVmlkZW9Tb3VyY2VzLmdyb3Vwcy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgcmVtb3RlVmlkZW9Tb3VyY2VzLmdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChncm91cCkge1xuICAgICAgICAgICAgaWYgKGdyb3VwLnNlbWFudGljcyA9PT0gJ1NJTScgJiYgZ3JvdXAuc3NyY3MgJiYgZ3JvdXAuc3NyY3MubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgcXVhbGl0eSA9IDA7XG4gICAgICAgICAgICAgICAgZ3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICB2aWRlb1NvdXJjZSA9IHJlbW90ZVZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLl9yZW1vdGVNYXBzLm1zaWQyUXVhbGl0eVt2aWRlb1NvdXJjZS5tc2lkXSA9IHF1YWxpdHkrKztcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5fcmVtb3RlTWFwcy5zc3JjMk1zaWRbdmlkZW9Tb3VyY2Uuc3NyY10gPSB2aWRlb1NvdXJjZS5tc2lkO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLl9yZW1vdGVNYXBzLm1zaWQyc3NyY1t2aWRlb1NvdXJjZS5tc2lkXSA9IHZpZGVvU291cmNlLnNzcmM7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fc2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0gPSBmdW5jdGlvbiAocmVzb3VyY2UsIHNzcmMpIHtcbiAgICB0aGlzLl9yZW1vdGVNYXBzLnJlY2VpdmluZ1ZpZGVvU3RyZWFtc1tyZXNvdXJjZV0gPSBzc3JjO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIGEgc3RyZWFtIHdpdGggc2luZ2xlIHZpZGVvIHRyYWNrLCB0aGUgb25lIGN1cnJlbnRseSBiZWluZ1xuICogcmVjZWl2ZWQgYnkgdGhpcyBlbmRwb2ludC5cbiAqXG4gKiBAcGFyYW0gc3RyZWFtIHRoZSByZW1vdGUgc2ltdWxjYXN0IHN0cmVhbS5cbiAqIEByZXR1cm5zIHt3ZWJraXRNZWRpYVN0cmVhbX1cbiAqL1xuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgIHZhciB0cmFja3MsIGksIGVsZWN0ZWRUcmFjaywgbXNpZCwgcXVhbGl0eSA9IDAsIHJlY2VpdmluZ1RyYWNrSWQ7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKGNvbmZpZy5lbmFibGVTaW11bGNhc3QpIHtcblxuICAgICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5zb21lKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKHNlbGYuX3JlbW90ZU1hcHMucmVjZWl2aW5nVmlkZW9TdHJlYW1zKS5zb21lKGZ1bmN0aW9uIChyZXNvdXJjZSkge1xuICAgICAgICAgICAgICAgIHZhciBzc3JjID0gc2VsZi5fcmVtb3RlTWFwcy5yZWNlaXZpbmdWaWRlb1N0cmVhbXNbcmVzb3VyY2VdO1xuICAgICAgICAgICAgICAgIHZhciBtc2lkID0gc2VsZi5fcmVtb3RlTWFwcy5zc3JjMk1zaWRbc3NyY107XG4gICAgICAgICAgICAgICAgaWYgKG1zaWQgPT0gW3N0cmVhbS5pZCwgdHJhY2suaWRdLmpvaW4oJyAnKSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVjdGVkVHJhY2sgPSB0cmFjaztcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghZWxlY3RlZFRyYWNrKSB7XG4gICAgICAgICAgICAvLyB3ZSBkb24ndCBoYXZlIGFuIGVsZWN0ZWQgdHJhY2ssIGNob29zZSBieSBpbml0aWFsIHF1YWxpdHkuXG4gICAgICAgICAgICB0cmFja3MgPSBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBtc2lkID0gW3N0cmVhbS5pZCwgdHJhY2tzW2ldLmlkXS5qb2luKCcgJyk7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuX3JlbW90ZU1hcHMubXNpZDJRdWFsaXR5W21zaWRdID09PSBxdWFsaXR5KSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZWN0ZWRUcmFjayA9IHRyYWNrc1tpXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBpZiB0aGUgaW5pdGlhbFF1YWxpdHkgY291bGQgbm90IGJlIHNhdGlzZmllZCwgbG93ZXJcbiAgICAgICAgICAgIC8vIHRoZSByZXF1aXJlbWVudCBhbmQgdHJ5IGFnYWluLlxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIChlbGVjdGVkVHJhY2spXG4gICAgICAgID8gbmV3IHdlYmtpdE1lZGlhU3RyZWFtKFtlbGVjdGVkVHJhY2tdKVxuICAgICAgICA6IHN0cmVhbTtcbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdTU1JDID0gZnVuY3Rpb24gKGppZCkge1xuICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgdmFyIHNzcmMgPSB0aGlzLl9yZW1vdGVNYXBzLnJlY2VpdmluZ1ZpZGVvU3RyZWFtc1tyZXNvdXJjZV07XG5cbiAgICAvLyBJZiB3ZSBoYXZlbid0IHJlY2VpdmluZyBhIFwiY2hhbmdlZFwiIGV2ZW50IHlldCwgdGhlbiB3ZSBtdXN0IGJlIHJlY2VpdmluZ1xuICAgIC8vIGxvdyBxdWFsaXR5ICh0aGF0IHRoZSBzZW5kZXIgYWx3YXlzIHN0cmVhbXMpLlxuICAgIGlmKCFzc3JjKVxuICAgIHtcbiAgICAgICAgdmFyIHJlbW90ZVN0cmVhbU9iamVjdCA9IFJUQy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtID0gcmVtb3RlU3RyZWFtT2JqZWN0LmdldE9yaWdpbmFsU3RyZWFtKCk7XG4gICAgICAgIHZhciB0cmFja3MgPSByZW1vdGVTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICAgICAgaWYgKHRyYWNrcykge1xuICAgICAgICAgICAgZm9yICh2YXIgayA9IDA7IGsgPCB0cmFja3MubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2sgPSB0cmFja3Nba107XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBbcmVtb3RlU3RyZWFtLmlkLCB0cmFjay5pZF0uam9pbignICcpO1xuICAgICAgICAgICAgICAgIHZhciBfc3NyYyA9IHRoaXMuX3JlbW90ZU1hcHMubXNpZDJzc3JjW21zaWRdO1xuICAgICAgICAgICAgICAgIHZhciBxdWFsaXR5ID0gdGhpcy5fcmVtb3RlTWFwcy5tc2lkMlF1YWxpdHlbbXNpZF07XG4gICAgICAgICAgICAgICAgaWYgKHF1YWxpdHkgPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBzc3JjID0gX3NzcmM7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNzcmM7XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW1CeVNTUkMgPSBmdW5jdGlvbiAoc3NyYylcbntcbiAgICB2YXIgc2lkLCBlbGVjdGVkU3RyZWFtO1xuICAgIHZhciBpLCBqLCBrO1xuICAgIHZhciBqaWQgPSBzc3JjMmppZFtzc3JjXTtcbiAgICBpZihqaWQpXG4gICAge1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtT2JqZWN0ID0gUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgIHZhciByZW1vdGVTdHJlYW0gPSByZW1vdGVTdHJlYW1PYmplY3QuZ2V0T3JpZ2luYWxTdHJlYW0oKTtcbiAgICAgICAgdmFyIHRyYWNrcyA9IHJlbW90ZVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICBpZiAodHJhY2tzKSB7XG4gICAgICAgICAgICBmb3IgKGsgPSAwOyBrIDwgdHJhY2tzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRyYWNrID0gdHJhY2tzW2tdO1xuICAgICAgICAgICAgICAgIHZhciBtc2lkID0gW3JlbW90ZVN0cmVhbS5pZCwgdHJhY2suaWRdLmpvaW4oJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIgdG1wID0gdGhpcy5fcmVtb3RlTWFwcy5tc2lkMnNzcmNbbXNpZF07XG4gICAgICAgICAgICAgICAgaWYgKHRtcCA9PSBzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZWN0ZWRTdHJlYW0gPSBuZXcgd2Via2l0TWVkaWFTdHJlYW0oW3RyYWNrXSk7XG4gICAgICAgICAgICAgICAgICAgIHNpZCA9IHJlbW90ZVN0cmVhbU9iamVjdC5zaWQ7XG4gICAgICAgICAgICAgICAgICAgIC8vIHN0cmVhbSBmb3VuZCwgc3RvcC5cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBzaWQ6IHNpZCxcbiAgICAgICAgc3RyZWFtOiBlbGVjdGVkU3RyZWFtXG4gICAgfTtcbn07XG5cbi8qKlxuICogR2V0cyB0aGUgZnVsbHkgcXVhbGlmaWVkIG1zaWQgKHN0cmVhbS5pZCArIHRyYWNrLmlkKSBhc3NvY2lhdGVkIHRvIHRoZVxuICogU1NSQy5cbiAqXG4gKiBAcGFyYW0gc3NyY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5nZXRSZW1vdGVWaWRlb1N0cmVhbUlkQnlTU1JDID0gZnVuY3Rpb24gKHNzcmMpIHtcbiAgICByZXR1cm4gdGhpcy5fcmVtb3RlTWFwcy5zc3JjMk1zaWRbc3NyY107XG59O1xuXG4vKipcbiAqIFJlbW92ZXMgdGhlIHNzcmMtZ3JvdXA6U0lNIGZyb20gdGhlIHJlbW90ZSBkZXNjcmlwdGlvbiBiYWNhdXNlIENocm9tZVxuICogZWl0aGVyIGdldHMgY29uZnVzZWQgYW5kIHRoaW5rcyB0aGlzIGlzIGFuIEZJRCBncm91cCBvciwgaWYgYW4gRklEIGdyb3VwXG4gKiBpcyBhbHJlYWR5IHByZXNlbnQsIGl0IGZhaWxzIHRvIHNldCB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLnRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcblxuICAgIGlmIChkZXNjICYmIGRlc2Muc2RwKSB7XG4gICAgICAgIHZhciBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgICAgICB0aGlzLl91cGRhdGVSZW1vdGVNYXBzKHNiKTtcbiAgICAgICAgdGhpcy5fY2FjaGVSZW1vdGVWaWRlb1NvdXJjZXMoc2IpO1xuXG4gICAgICAgIC8vIE5PVEUoZ3ApIHRoaXMgbmVlZHMgdG8gYmUgY2FsbGVkIGFmdGVyIHVwZGF0ZVJlbW90ZU1hcHMgYmVjYXVzZSB3ZVxuICAgICAgICAvLyBuZWVkIHRoZSBzaW11bGNhc3QgZ3JvdXAgaW4gdGhlIF91cGRhdGVSZW1vdGVNYXBzKCkgbWV0aG9kLlxuICAgICAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZW1vdmVTaW11bGNhc3RHcm91cChzYik7XG5cbiAgICAgICAgaWYgKGRlc2Muc2RwLmluZGV4T2YoJ2E9c3NyYy1ncm91cDpTSU0nKSAhPT0gLTEpIHtcbiAgICAgICAgICAgIC8vIFdlIGRvbid0IG5lZWQgdGhlIGdvb2cgY29uZmVyZW5jZSBmbGFnIGlmIHdlJ3JlIG5vdCBkb2luZ1xuICAgICAgICAgICAgLy8gc2ltdWxjYXN0LlxuICAgICAgICAgICAgdGhpcy5fZW5zdXJlR29vZ0NvbmZlcmVuY2Uoc2IpO1xuICAgICAgICB9XG5cbiAgICAgICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmxvZ2dlci5maW5lKFsnVHJhbnNmb3JtZWQgcmVtb3RlIGRlc2NyaXB0aW9uJywgZGVzYy5zZHBdLmpvaW4oJyAnKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNpbXVsY2FzdFJlY2VpdmVyOyIsInZhciBTaW11bGNhc3RMb2dnZXIgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RMb2dnZXJcIik7XG52YXIgU2ltdWxjYXN0VXRpbHMgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RVdGlsc1wiKTtcblxuZnVuY3Rpb24gU2ltdWxjYXN0U2VuZGVyKCkge1xuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMgPSBuZXcgU2ltdWxjYXN0VXRpbHMoKTtcbiAgICB0aGlzLmxvZ2dlciA9IG5ldyBTaW11bGNhc3RMb2dnZXIoJ1NpbXVsY2FzdFNlbmRlcicsIDEpO1xufVxuXG5TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmRpc3BsYXllZExvY2FsVmlkZW9TdHJlYW0gPSBudWxsO1xuXG5TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9nZW5lcmF0ZUd1aWQgPSAoZnVuY3Rpb24gKCkge1xuICAgIGZ1bmN0aW9uIHM0KCkge1xuICAgICAgICByZXR1cm4gTWF0aC5mbG9vcigoMSArIE1hdGgucmFuZG9tKCkpICogMHgxMDAwMClcbiAgICAgICAgICAgIC50b1N0cmluZygxNilcbiAgICAgICAgICAgIC5zdWJzdHJpbmcoMSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHM0KCkgKyBzNCgpICsgJy0nICsgczQoKSArICctJyArIHM0KCkgKyAnLScgK1xuICAgICAgICAgICAgczQoKSArICctJyArIHM0KCkgKyBzNCgpICsgczQoKTtcbiAgICB9O1xufSgpKTtcblxuLy8gUmV0dXJucyBhIHJhbmRvbSBpbnRlZ2VyIGJldHdlZW4gbWluIChpbmNsdWRlZCkgYW5kIG1heCAoZXhjbHVkZWQpXG4vLyBVc2luZyBNYXRoLnJvdW5kKCkgZ2l2ZXMgYSBub24tdW5pZm9ybSBkaXN0cmlidXRpb24hXG5TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9nZW5lcmF0ZVJhbmRvbVNTUkMgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIG1pbiA9IDAsIG1heCA9IDB4ZmZmZmZmZmY7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pKSArIG1pbjtcbn07XG5cblNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuZ2V0TG9jYWxWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gKHRoaXMuZGlzcGxheWVkTG9jYWxWaWRlb1N0cmVhbSAhPSBudWxsKVxuICAgICAgICA/IHRoaXMuZGlzcGxheWVkTG9jYWxWaWRlb1N0cmVhbVxuICAgICAgICAvLyBpbiBjYXNlIHdlIGhhdmUgbm8gc2ltdWxjYXN0IGF0IGFsbCwgaS5lLiB3ZSBkaWRuJ3QgcGVyZm9ybSB0aGUgR1VNXG4gICAgICAgIDogUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKTtcbn07XG5cbmZ1bmN0aW9uIE5hdGl2ZVNpbXVsY2FzdFNlbmRlcigpIHtcbiAgICBTaW11bGNhc3RTZW5kZXIuY2FsbCh0aGlzKTsgLy8gY2FsbCB0aGUgc3VwZXIgY29uc3RydWN0b3IuXG59XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKFNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUpO1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9sb2NhbEV4cGxvc2lvbk1hcCA9IHt9O1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5faXNVc2luZ1NjcmVlblN0cmVhbSA9IGZhbHNlO1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fbG9jYWxWaWRlb1NvdXJjZUNhY2hlID0gJyc7XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5fbG9jYWxFeHBsb3Npb25NYXAgPSB7fTtcbiAgICB0aGlzLl9pc1VzaW5nU2NyZWVuU3RyZWFtID0gZGVza3RvcHNoYXJpbmcuaXNVc2luZ1NjcmVlblN0cmVhbSgpO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fY2FjaGVMb2NhbFZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHRoaXMuX2xvY2FsVmlkZW9Tb3VyY2VDYWNoZSA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2dldFZpZGVvU291cmNlcyhsaW5lcyk7XG59O1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9yZXN0b3JlTG9jYWxWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCB0aGlzLl9sb2NhbFZpZGVvU291cmNlQ2FjaGUpO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fYXBwZW5kU2ltdWxjYXN0R3JvdXAgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgdmlkZW9Tb3VyY2VzLCBzc3JjR3JvdXAsIHNpbVNTUkMsIG51bU9mU3VicyA9IDIsIGksIHNiLCBtc2lkO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnQXBwZW5kaW5nIHNpbXVsY2FzdCBncm91cC4uLicpO1xuXG4gICAgLy8gR2V0IHRoZSBwcmltYXJ5IFNTUkMgaW5mb3JtYXRpb24uXG4gICAgdmlkZW9Tb3VyY2VzID0gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdO1xuXG4gICAgLy8gU3RhcnQgYnVpbGRpbmcgdGhlIFNJTSBTU1JDIGdyb3VwLlxuICAgIHNzcmNHcm91cCA9IFsnYT1zc3JjLWdyb3VwOlNJTSddO1xuXG4gICAgLy8gVGhlIHZpZGVvIHNvdXJjZSBidWZmZXIuXG4gICAgc2IgPSBbXTtcblxuICAgIC8vIENyZWF0ZSB0aGUgc2ltdWxjYXN0IHN1Yi1zdHJlYW1zLlxuICAgIGZvciAoaSA9IDA7IGkgPCBudW1PZlN1YnM7IGkrKykge1xuICAgICAgICAvLyBUT0RPKGdwKSBwcmV2ZW50IFNTUkMgY29sbGlzaW9uLlxuICAgICAgICBzaW1TU1JDID0gdGhpcy5fZ2VuZXJhdGVSYW5kb21TU1JDKCk7XG4gICAgICAgIHNzcmNHcm91cC5wdXNoKHNpbVNTUkMpO1xuXG4gICAgICAgIHNiLnNwbGljZS5hcHBseShzYiwgW3NiLmxlbmd0aCwgMF0uY29uY2F0KFxuICAgICAgICAgICAgW1tcImE9c3NyYzpcIiwgc2ltU1NSQywgXCIgY25hbWU6XCIsIHZpZGVvU291cmNlcy5iYXNlLmNuYW1lXS5qb2luKCcnKSxcbiAgICAgICAgICAgICAgICBbXCJhPXNzcmM6XCIsIHNpbVNTUkMsIFwiIG1zaWQ6XCIsIHZpZGVvU291cmNlcy5iYXNlLm1zaWRdLmpvaW4oJycpXVxuICAgICAgICApKTtcblxuICAgICAgICB0aGlzLmxvZ2dlci5pbmZvKFsnR2VuZXJhdGVkIHN1YnN0cmVhbSAnLCBpLCAnIHdpdGggU1NSQyAnLCBzaW1TU1JDLCAnLiddLmpvaW4oJycpKTtcblxuICAgIH1cblxuICAgIC8vIEFkZCB0aGUgZ3JvdXAgc2ltIGxheWVycy5cbiAgICBzYi5zcGxpY2UoMCwgMCwgc3NyY0dyb3VwLmpvaW4oJyAnKSlcblxuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHNiKTtcbn07XG5cbi8vIERvZXMgdGhlIGFjdHVhbCBwYXRjaGluZy5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2Vuc3VyZVNpbXVsY2FzdEdyb3VwID0gZnVuY3Rpb24gKGxpbmVzKSB7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdFbnN1cmluZyBzaW11bGNhc3QgZ3JvdXAuLi4nKTtcblxuICAgIGlmICh0aGlzLnNpbXVsY2FzdFV0aWxzLl9pbmRleE9mQXJyYXkoJ2E9c3NyYy1ncm91cDpTSU0nLCBsaW5lcykgPT09IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2VtcHR5Q29tcG91bmRJbmRleCkge1xuICAgICAgICB0aGlzLl9hcHBlbmRTaW11bGNhc3RHcm91cChsaW5lcyk7XG4gICAgICAgIHRoaXMuX2NhY2hlTG9jYWxWaWRlb1NvdXJjZXMobGluZXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIHZlcmlmeSB0aGF0IHRoZSBzc3JjcyBwYXJ0aWNpcGF0aW5nIGluIHRoZSBTSU0gZ3JvdXAgYXJlIHByZXNlbnRcbiAgICAgICAgLy8gaW4gdGhlIFNEUCAobmVlZGVkIGZvciBwcmVzZW5jZSkuXG4gICAgICAgIHRoaXMuX3Jlc3RvcmVMb2NhbFZpZGVvU291cmNlcyhsaW5lcyk7XG4gICAgfVxufTtcblxuLyoqXG4gKiBQcm9kdWNlcyBhIHNpbmdsZSBzdHJlYW0gd2l0aCBtdWx0aXBsZSB0cmFja3MgZm9yIGxvY2FsIHZpZGVvIHNvdXJjZXMuXG4gKlxuICogQHBhcmFtIGxpbmVzXG4gKiBAcHJpdmF0ZVxuICovXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9leHBsb2RlU2ltdWxjYXN0U2VuZGVyU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciBzYiwgbXNpZCwgc2lkLCB0aWQsIHZpZGVvU291cmNlcywgc2VsZjtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0V4cGxvZGluZyBsb2NhbCB2aWRlbyBzb3VyY2VzLi4uJyk7XG5cbiAgICB2aWRlb1NvdXJjZXMgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLnBhcnNlTWVkaWEobGluZXMsIFsndmlkZW8nXSlbMF07XG5cbiAgICBzZWxmID0gdGhpcztcbiAgICBpZiAodmlkZW9Tb3VyY2VzLmdyb3VwcyAmJiB2aWRlb1NvdXJjZXMuZ3JvdXBzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICB2aWRlb1NvdXJjZXMuZ3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKGdyb3VwKSB7XG4gICAgICAgICAgICBpZiAoZ3JvdXAuc2VtYW50aWNzID09PSAnU0lNJykge1xuICAgICAgICAgICAgICAgIGdyb3VwLnNzcmNzLmZvckVhY2goZnVuY3Rpb24gKHNzcmMpIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIG1zaWQgZm9yIHRoaXMgc3NyYy4uXG4gICAgICAgICAgICAgICAgICAgIGlmIChzZWxmLl9sb2NhbEV4cGxvc2lvbk1hcFtzc3JjXSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gLi4gZWl0aGVyIGZyb20gdGhlIGV4cGxvc2lvbiBtYXAuLlxuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IHNlbGYuX2xvY2FsRXhwbG9zaW9uTWFwW3NzcmNdO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gLi4gb3IgZ2VuZXJhdGUgYSBuZXcgb25lIChtc2lkKS5cbiAgICAgICAgICAgICAgICAgICAgICAgIHNpZCA9IHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAuc3Vic3RyaW5nKDAsIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWQuaW5kZXhPZignICcpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgdGlkID0gc2VsZi5fZ2VuZXJhdGVHdWlkKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gW3NpZCwgdGlkXS5qb2luKCcgJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEV4cGxvc2lvbk1hcFtzc3JjXSA9IG1zaWQ7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBBc3NpZ24gaXQgdG8gdGhlIHNvdXJjZSBvYmplY3QuXG4gICAgICAgICAgICAgICAgICAgIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWQgPSBtc2lkO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIFRPRE8oZ3ApIENoYW5nZSB0aGUgbXNpZCBvZiBhc3NvY2lhdGVkIHNvdXJjZXMuXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHNiID0gdGhpcy5zaW11bGNhc3RVdGlscy5fY29tcGlsZVZpZGVvU291cmNlcyh2aWRlb1NvdXJjZXMpO1xuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgc2IpO1xufTtcblxuLyoqXG4gKiBHVU0gZm9yIHNpbXVsY2FzdC5cbiAqXG4gKiBAcGFyYW0gY29uc3RyYWludHNcbiAqIEBwYXJhbSBzdWNjZXNzXG4gKiBAcGFyYW0gZXJyXG4gKi9cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhID0gZnVuY3Rpb24gKGNvbnN0cmFpbnRzLCBzdWNjZXNzLCBlcnIpIHtcblxuICAgIC8vIFRoZXJlJ3Mgbm90aGluZyBzcGVjaWFsIHRvIGRvIGZvciBuYXRpdmUgc2ltdWxjYXN0LCBzbyBqdXN0IGRvIGEgbm9ybWFsIEdVTS5cbiAgICBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLCBmdW5jdGlvbiAoaHFTdHJlYW0pIHtcbiAgICAgICAgc3VjY2VzcyhocVN0cmVhbSk7XG4gICAgfSwgZXJyKTtcbn07XG5cbi8qKlxuICogUHJlcGFyZXMgdGhlIGxvY2FsIGRlc2NyaXB0aW9uIGZvciBwdWJsaWMgdXNhZ2UgKGkuZS4gdG8gYmUgc2lnbmFsZWRcbiAqIHRocm91Z2ggSmluZ2xlIHRvIHRoZSBmb2N1cykuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHtSVENTZXNzaW9uRGVzY3JpcHRpb259XG4gKi9cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHZhciBzYjtcblxuICAgIGlmICghdGhpcy5zaW11bGNhc3RVdGlscy5pc1ZhbGlkRGVzY3JpcHRpb24oZGVzYykgfHwgdGhpcy5faXNVc2luZ1NjcmVlblN0cmVhbSkge1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cblxuICAgIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgdGhpcy5fZXhwbG9kZVNpbXVsY2FzdFNlbmRlclNvdXJjZXMoc2IpO1xuXG4gICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICB9KTtcblxuICAgIHRoaXMubG9nZ2VyLmZpbmUoWydFeHBsb2RlZCBsb2NhbCB2aWRlbyBzb3VyY2VzJywgZGVzYy5zZHBdLmpvaW4oJyAnKSk7XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cbi8qKlxuICogRW5zdXJlcyB0aGF0IHRoZSBzaW11bGNhc3QgZ3JvdXAgaXMgcHJlc2VudCBpbiB0aGUgYW5zd2VyLCBfaWZfIG5hdGl2ZVxuICogc2ltdWxjYXN0IGlzIGVuYWJsZWQsXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUFuc3dlciA9IGZ1bmN0aW9uIChkZXNjKSB7XG5cbiAgICBpZiAoIXRoaXMuc2ltdWxjYXN0VXRpbHMuaXNWYWxpZERlc2NyaXB0aW9uKGRlc2MpIHx8IHRoaXMuX2lzVXNpbmdTY3JlZW5TdHJlYW0pIHtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG4gICAgdmFyIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgLy8gRXZlbiBpZiB3ZSBoYXZlIGVuYWJsZWQgbmF0aXZlIHNpbXVsY2FzdGluZyBwcmV2aW91c2x5XG4gICAgLy8gKHdpdGggYSBjYWxsIHRvIFNMRCB3aXRoIGFuIGFwcHJvcHJpYXRlIFNEUCwgZm9yIGV4YW1wbGUpLFxuICAgIC8vIGNyZWF0ZUFuc3dlciBzZWVtcyB0byBjb25zaXN0ZW50bHkgZ2VuZXJhdGUgaW5jb21wbGV0ZSBTRFBcbiAgICAvLyB3aXRoIG1pc3NpbmcgU1NSQ1MuXG4gICAgLy9cbiAgICAvLyBTbywgc3Vic2VxdWVudCBjYWxscyB0byBTTEQgd2lsbCBoYXZlIG1pc3NpbmcgU1NSQ1MgYW5kIHByZXNlbmNlXG4gICAgLy8gd29uJ3QgaGF2ZSB0aGUgY29tcGxldGUgbGlzdCBvZiBTUkNzLlxuICAgIHRoaXMuX2Vuc3VyZVNpbXVsY2FzdEdyb3VwKHNiKTtcblxuICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgfSk7XG5cbiAgICB0aGlzLmxvZ2dlci5maW5lKFsnVHJhbnNmb3JtZWQgYW5zd2VyJywgZGVzYy5zZHBdLmpvaW4oJyAnKSk7XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cblxuLyoqXG4gKlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gZGVzYztcbn07XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkID0gZnVuY3Rpb24gKHNzcmMsIGVuYWJsZWQpIHtcbiAgICAvLyBOb3RoaW5nIHRvIGRvIGhlcmUsIG5hdGl2ZSBzaW11bGNhc3QgZG9lcyB0aGF0IGF1dG8tbWFnaWNhbGx5LlxufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE5hdGl2ZVNpbXVsY2FzdFNlbmRlcjtcblxuZnVuY3Rpb24gU2ltcGxlU2ltdWxjYXN0U2VuZGVyKCkge1xuICAgIFNpbXVsY2FzdFNlbmRlci5jYWxsKHRoaXMpO1xufVxuXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlKTtcblxuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5sb2NhbFN0cmVhbSA9IG51bGw7XG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9sb2NhbE1hcHMgPSB7XG4gICAgbXNpZHM6IFtdLFxuICAgIG1zaWQyc3NyYzoge31cbn07XG5cbi8qKlxuICogR3JvdXBzIGxvY2FsIHZpZGVvIHNvdXJjZXMgdG9nZXRoZXIgaW4gdGhlIHNzcmMtZ3JvdXA6U0lNIGdyb3VwLlxuICpcbiAqIEBwYXJhbSBsaW5lc1xuICogQHByaXZhdGVcbiAqL1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fZ3JvdXBMb2NhbFZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciBzYiwgdmlkZW9Tb3VyY2VzLCBzc3JjcyA9IFtdLCBzc3JjO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnR3JvdXBpbmcgbG9jYWwgdmlkZW8gc291cmNlcy4uLicpO1xuXG4gICAgdmlkZW9Tb3VyY2VzID0gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdO1xuXG4gICAgZm9yIChzc3JjIGluIHZpZGVvU291cmNlcy5zb3VyY2VzKSB7XG4gICAgICAgIC8vIGppdHNpLW1lZXQgZGVzdHJveXMvY3JlYXRlcyBzdHJlYW1zIGF0IHZhcmlvdXMgcGxhY2VzIGNhdXNpbmdcbiAgICAgICAgLy8gdGhlIG9yaWdpbmFsIGxvY2FsIHN0cmVhbSBpZHMgdG8gY2hhbmdlLiBUaGUgb25seSB0aGluZyB0aGF0XG4gICAgICAgIC8vIHJlbWFpbnMgdW5jaGFuZ2VkIGlzIHRoZSB0cmFja2lkLlxuICAgICAgICB0aGlzLl9sb2NhbE1hcHMubXNpZDJzc3JjW3ZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWQuc3BsaXQoJyAnKVsxXV0gPSBzc3JjO1xuICAgIH1cblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBUT0RPKGdwKSBhZGQgb25seSBcImZyZWVcIiBzb3VyY2VzLlxuICAgIHRoaXMuX2xvY2FsTWFwcy5tc2lkcy5mb3JFYWNoKGZ1bmN0aW9uIChtc2lkKSB7XG4gICAgICAgIHNzcmNzLnB1c2goc2VsZi5fbG9jYWxNYXBzLm1zaWQyc3NyY1ttc2lkXSk7XG4gICAgfSk7XG5cbiAgICBpZiAoIXZpZGVvU291cmNlcy5ncm91cHMpIHtcbiAgICAgICAgdmlkZW9Tb3VyY2VzLmdyb3VwcyA9IFtdO1xuICAgIH1cblxuICAgIHZpZGVvU291cmNlcy5ncm91cHMucHVzaCh7XG4gICAgICAgICdzZW1hbnRpY3MnOiAnU0lNJyxcbiAgICAgICAgJ3NzcmNzJzogc3NyY3NcbiAgICB9KTtcblxuICAgIHNiID0gdGhpcy5zaW11bGNhc3RVdGlscy5fY29tcGlsZVZpZGVvU291cmNlcyh2aWRlb1NvdXJjZXMpO1xuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgc2IpO1xufTtcblxuLyoqXG4gKiBHVU0gZm9yIHNpbXVsY2FzdC5cbiAqXG4gKiBAcGFyYW0gY29uc3RyYWludHNcbiAqIEBwYXJhbSBzdWNjZXNzXG4gKiBAcGFyYW0gZXJyXG4gKi9cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhID0gZnVuY3Rpb24gKGNvbnN0cmFpbnRzLCBzdWNjZXNzLCBlcnIpIHtcblxuICAgIC8vIFRPRE8oZ3ApIHdoYXQgaWYgd2UgcmVxdWVzdCBhIHJlc29sdXRpb24gbm90IHN1cHBvcnRlZCBieSB0aGUgaGFyZHdhcmU/XG4gICAgLy8gVE9ETyhncCkgbWFrZSB0aGUgbHEgc3RyZWFtIGNvbmZpZ3VyYWJsZTsgYWx0aG91Z2ggdGhpcyB3b3VsZG4ndCB3b3JrIHdpdGggbmF0aXZlIHNpbXVsY2FzdFxuICAgIHZhciBscUNvbnN0cmFpbnRzID0ge1xuICAgICAgICBhdWRpbzogZmFsc2UsXG4gICAgICAgIHZpZGVvOiB7XG4gICAgICAgICAgICBtYW5kYXRvcnk6IHtcbiAgICAgICAgICAgICAgICBtYXhXaWR0aDogMzIwLFxuICAgICAgICAgICAgICAgIG1heEhlaWdodDogMTgwLFxuICAgICAgICAgICAgICAgIG1heEZyYW1lUmF0ZTogMTVcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdIUSBjb25zdHJhaW50czogJywgY29uc3RyYWludHMpO1xuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0xRIGNvbnN0cmFpbnRzOiAnLCBscUNvbnN0cmFpbnRzKTtcblxuXG4gICAgLy8gTk9URShncCkgaWYgd2UgcmVxdWVzdCB0aGUgbHEgc3RyZWFtIGZpcnN0IHdlYmtpdEdldFVzZXJNZWRpYVxuICAgIC8vIGZhaWxzIHJhbmRvbWx5LiBUZXN0ZWQgd2l0aCBDaHJvbWUgMzcuIEFzIGZpcHBvIHN1Z2dlc3RlZCwgdGhlXG4gICAgLy8gcmVhc29uIGFwcGVhcnMgdG8gYmUgdGhhdCBDaHJvbWUgb25seSBhY3F1aXJlcyB0aGUgY2FtIG9uY2UgYW5kXG4gICAgLy8gdGhlbiBkb3duc2NhbGVzIHRoZSBwaWN0dXJlIChodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9MzQ2NjE2I2MxMSlcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLCBmdW5jdGlvbiAoaHFTdHJlYW0pIHtcblxuICAgICAgICBzZWxmLmxvY2FsU3RyZWFtID0gaHFTdHJlYW07XG5cbiAgICAgICAgLy8gcmVzZXQgbG9jYWwgbWFwcy5cbiAgICAgICAgc2VsZi5fbG9jYWxNYXBzLm1zaWRzID0gW107XG4gICAgICAgIHNlbGYuX2xvY2FsTWFwcy5tc2lkMnNzcmMgPSB7fTtcblxuICAgICAgICAvLyBhZGQgaHEgdHJhY2tpZCB0byBsb2NhbCBtYXBcbiAgICAgICAgc2VsZi5fbG9jYWxNYXBzLm1zaWRzLnB1c2goaHFTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKVswXS5pZCk7XG5cbiAgICAgICAgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYShscUNvbnN0cmFpbnRzLCBmdW5jdGlvbiAobHFTdHJlYW0pIHtcblxuICAgICAgICAgICAgc2VsZi5kaXNwbGF5ZWRMb2NhbFZpZGVvU3RyZWFtID0gbHFTdHJlYW07XG5cbiAgICAgICAgICAgIC8vIE5PVEUoZ3ApIFRoZSBzcGVjaWZpY2F0aW9uIHNheXMgQXJyYXkuZm9yRWFjaCgpIHdpbGwgdmlzaXRcbiAgICAgICAgICAgIC8vIHRoZSBhcnJheSBlbGVtZW50cyBpbiBudW1lcmljIG9yZGVyLCBhbmQgdGhhdCBpdCBkb2Vzbid0XG4gICAgICAgICAgICAvLyB2aXNpdCBlbGVtZW50cyB0aGF0IGRvbid0IGV4aXN0LlxuXG4gICAgICAgICAgICAvLyBhZGQgbHEgdHJhY2tpZCB0byBsb2NhbCBtYXBcbiAgICAgICAgICAgIHNlbGYuX2xvY2FsTWFwcy5tc2lkcy5zcGxpY2UoMCwgMCwgbHFTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKVswXS5pZCk7XG5cbiAgICAgICAgICAgIHNlbGYubG9jYWxTdHJlYW0uYWRkVHJhY2sobHFTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKVswXSk7XG4gICAgICAgICAgICBzdWNjZXNzKHNlbGYubG9jYWxTdHJlYW0pO1xuICAgICAgICB9LCBlcnIpO1xuICAgIH0sIGVycik7XG59O1xuXG4vKipcbiAqIFByZXBhcmVzIHRoZSBsb2NhbCBkZXNjcmlwdGlvbiBmb3IgcHVibGljIHVzYWdlIChpLmUuIHRvIGJlIHNpZ25hbGVkXG4gKiB0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7UlRDU2Vzc2lvbkRlc2NyaXB0aW9ufVxuICovXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICB2YXIgc2I7XG5cbiAgICBpZiAoIXRoaXMuc2ltdWxjYXN0VXRpbHMuaXNWYWxpZERlc2NyaXB0aW9uKGRlc2MpKSB7XG4gICAgICAgIHJldHVybiBkZXNjO1xuICAgIH1cblxuICAgIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgdGhpcy5fZ3JvdXBMb2NhbFZpZGVvU291cmNlcyhzYik7XG5cbiAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgIH0pO1xuXG4gICAgdGhpcy5sb2dnZXIuZmluZSgnR3JvdXBlZCBsb2NhbCB2aWRlbyBzb3VyY2VzJyk7XG4gICAgdGhpcy5sb2dnZXIuZmluZShkZXNjLnNkcCk7XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cbi8qKlxuICogRW5zdXJlcyB0aGF0IHRoZSBzaW11bGNhc3QgZ3JvdXAgaXMgcHJlc2VudCBpbiB0aGUgYW5zd2VyLCBfaWZfIG5hdGl2ZVxuICogc2ltdWxjYXN0IGlzIGVuYWJsZWQsXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUFuc3dlciA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5cbi8qKlxuICpcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG5cbiAgICB2YXIgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZW1vdmVTaW11bGNhc3RHcm91cChzYik7XG5cbiAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgIH0pO1xuXG4gICAgdGhpcy5sb2dnZXIuZmluZSgnVHJhbnNmb3JtZWQgbG9jYWwgZGVzY3JpcHRpb24nKTtcbiAgICB0aGlzLmxvZ2dlci5maW5lKGRlc2Muc2RwKTtcblxuICAgIHJldHVybiBkZXNjO1xufTtcblxuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQgPSBmdW5jdGlvbiAoc3NyYywgZW5hYmxlZCkge1xuICAgIHZhciB0cmFja2lkO1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMubG9nZ2VyLmxvZyhbJ1JlcXVlc3RlZCB0bycsIGVuYWJsZWQgPyAnZW5hYmxlJyA6ICdkaXNhYmxlJywgc3NyY10uam9pbignICcpKTtcbiAgICBpZiAoT2JqZWN0LmtleXModGhpcy5fbG9jYWxNYXBzLm1zaWQyc3NyYykuc29tZShmdW5jdGlvbiAodGlkKSB7XG4gICAgICAgIC8vIFNlYXJjaCBmb3IgdGhlIHRyYWNrIGlkIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIHNzcmNcbiAgICAgICAgaWYgKHNlbGYuX2xvY2FsTWFwcy5tc2lkMnNzcmNbdGlkXSA9PSBzc3JjKSB7XG4gICAgICAgICAgICB0cmFja2lkID0gdGlkO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9KSAmJiBzZWxmLmxvY2FsU3RyZWFtLmdldFZpZGVvVHJhY2tzKCkuc29tZShmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgLy8gU3RhcnQvc3RvcCB0aGUgdHJhY2sgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgdHJhY2sgaWRcbiAgICAgICAgaWYgKHRyYWNrLmlkID09PSB0cmFja2lkKSB7XG4gICAgICAgICAgICB0cmFjay5lbmFibGVkID0gZW5hYmxlZDtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgfSkpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKFt0cmFja2lkLCBlbmFibGVkID8gJ2VuYWJsZWQnIDogJ2Rpc2FibGVkJ10uam9pbignICcpKTtcbiAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihlbmFibGVkXG4gICAgICAgICAgICA/ICdzaW11bGNhc3RsYXllcnN0YXJ0ZWQnXG4gICAgICAgICAgICA6ICdzaW11bGNhc3RsYXllcnN0b3BwZWQnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihcIkkgZG9uJ3QgaGF2ZSBhIGxvY2FsIHN0cmVhbSB3aXRoIFNTUkMgXCIgKyBzc3JjKTtcbiAgICB9XG59O1xuXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gU2ltcGxlU2ltdWxjYXN0U2VuZGVyO1xuXG5mdW5jdGlvbiBOb1NpbXVsY2FzdFNlbmRlcigpIHtcbiAgICBTaW11bGNhc3RTZW5kZXIuY2FsbCh0aGlzKTtcbn1cblxuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlKTtcblxuLyoqXG4gKiBHVU0gZm9yIHNpbXVsY2FzdC5cbiAqXG4gKiBAcGFyYW0gY29uc3RyYWludHNcbiAqIEBwYXJhbSBzdWNjZXNzXG4gKiBAcGFyYW0gZXJyXG4gKi9cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5nZXRVc2VyTWVkaWEgPSBmdW5jdGlvbiAoY29uc3RyYWludHMsIHN1Y2Nlc3MsIGVycikge1xuICAgIG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIGZ1bmN0aW9uIChocVN0cmVhbSkge1xuICAgICAgICBzdWNjZXNzKGhxU3RyZWFtKTtcbiAgICB9LCBlcnIpO1xufTtcblxuLyoqXG4gKiBQcmVwYXJlcyB0aGUgbG9jYWwgZGVzY3JpcHRpb24gZm9yIHB1YmxpYyB1c2FnZSAoaS5lLiB0byBiZSBzaWduYWxlZFxuICogdGhyb3VnaCBKaW5nbGUgdG8gdGhlIGZvY3VzKS5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMge1JUQ1Nlc3Npb25EZXNjcmlwdGlvbn1cbiAqL1xuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gZGVzYztcbn07XG5cbi8qKlxuICogRW5zdXJlcyB0aGF0IHRoZSBzaW11bGNhc3QgZ3JvdXAgaXMgcHJlc2VudCBpbiB0aGUgYW5zd2VyLCBfaWZfIG5hdGl2ZVxuICogc2ltdWxjYXN0IGlzIGVuYWJsZWQsXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtQW5zd2VyID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gZGVzYztcbn07XG5cblxuLyoqXG4gKlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiBkZXNjO1xufTtcblxuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZCA9IGZ1bmN0aW9uIChzc3JjLCBlbmFibGVkKSB7XG5cbn07XG5cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE5vU2ltdWxjYXN0U2VuZGVyO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBcIm5hdGl2ZVwiOiBOYXRpdmVTaW11bGNhc3RTZW5kZXIsXG4gICAgXCJub1wiOiBOb1NpbXVsY2FzdFNlbmRlclxufVxuIiwidmFyIFNpbXVsY2FzdExvZ2dlciA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdExvZ2dlclwiKTtcblxuLyoqXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNpbXVsY2FzdFV0aWxzKCkge1xuICAgIHRoaXMubG9nZ2VyID0gbmV3IFNpbXVsY2FzdExvZ2dlcihcIlNpbXVsY2FzdFV0aWxzXCIsIDEpO1xufVxuXG4vKipcbiAqXG4gKiBAdHlwZSB7e319XG4gKiBAcHJpdmF0ZVxuICovXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX2VtcHR5Q29tcG91bmRJbmRleCA9IHt9O1xuXG4vKipcbiAqXG4gKiBAcGFyYW0gbGluZXNcbiAqIEBwYXJhbSB2aWRlb1NvdXJjZXNcbiAqIEBwcml2YXRlXG4gKi9cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fcmVwbGFjZVZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcywgdmlkZW9Tb3VyY2VzKSB7XG4gICAgdmFyIGksIGluVmlkZW8gPSBmYWxzZSwgaW5kZXggPSAtMSwgaG93TWFueSA9IDA7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdSZXBsYWNpbmcgdmlkZW8gc291cmNlcy4uLicpO1xuXG4gICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnbT0nLmxlbmd0aCkgPT09ICdtPScpIHtcbiAgICAgICAgICAgIC8vIE91dCBvZiB2aWRlby5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnbT12aWRlbyAnLmxlbmd0aCkgPT09ICdtPXZpZGVvICcpIHtcbiAgICAgICAgICAgIC8vIEluIHZpZGVvLlxuICAgICAgICAgICAgaW5WaWRlbyA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW5WaWRlbyAmJiAobGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmM6Jy5sZW5ndGgpID09PSAnYT1zc3JjOidcbiAgICAgICAgICAgIHx8IGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjLWdyb3VwOicubGVuZ3RoKSA9PT0gJ2E9c3NyYy1ncm91cDonKSkge1xuXG4gICAgICAgICAgICBpZiAoaW5kZXggPT09IC0xKSB7XG4gICAgICAgICAgICAgICAgaW5kZXggPSBpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBob3dNYW55Kys7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyAgZWZmaWNpZW5jeSBiYWJ5IDspXG4gICAgbGluZXMuc3BsaWNlLmFwcGx5KGxpbmVzLFxuICAgICAgICBbaW5kZXgsIGhvd01hbnldLmNvbmNhdCh2aWRlb1NvdXJjZXMpKTtcblxufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLmlzVmFsaWREZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKVxue1xuICAgIHJldHVybiBkZXNjICYmIGRlc2MgIT0gbnVsbFxuICAgICAgICAmJiBkZXNjLnR5cGUgJiYgZGVzYy50eXBlICE9ICcnXG4gICAgICAgICYmIGRlc2Muc2RwICYmIGRlc2Muc2RwICE9ICcnO1xufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9nZXRWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgaSwgaW5WaWRlbyA9IGZhbHNlLCBzYiA9IFtdO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnR2V0dGluZyB2aWRlbyBzb3VyY2VzLi4uJyk7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdtPScubGVuZ3RoKSA9PT0gJ209Jykge1xuICAgICAgICAgICAgLy8gT3V0IG9mIHZpZGVvLlxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdtPXZpZGVvICcubGVuZ3RoKSA9PT0gJ209dmlkZW8gJykge1xuICAgICAgICAgICAgLy8gSW4gdmlkZW8uXG4gICAgICAgICAgICBpblZpZGVvID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjOicubGVuZ3RoKSA9PT0gJ2E9c3NyYzonKSB7XG4gICAgICAgICAgICAvLyBJbiBTU1JDLlxuICAgICAgICAgICAgc2IucHVzaChsaW5lc1tpXSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYy1ncm91cDonLmxlbmd0aCkgPT09ICdhPXNzcmMtZ3JvdXA6Jykge1xuICAgICAgICAgICAgc2IucHVzaChsaW5lc1tpXSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gc2I7XG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUucGFyc2VNZWRpYSA9IGZ1bmN0aW9uIChsaW5lcywgbWVkaWF0eXBlcykge1xuICAgIHZhciBpLCByZXMgPSBbXSwgdHlwZSwgY3VyX21lZGlhLCBpZHgsIHNzcmNzLCBjdXJfc3NyYywgc3NyYyxcbiAgICAgICAgc3NyY19hdHRyaWJ1dGUsIGdyb3VwLCBzZW1hbnRpY3MsIHNraXAgPSB0cnVlO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnUGFyc2luZyBtZWRpYSBzb3VyY2VzLi4uJyk7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGxpbmVzW2ldLnN1YnN0cmluZygwLCAnbT0nLmxlbmd0aCkgPT09ICdtPScpIHtcblxuICAgICAgICAgICAgdHlwZSA9IGxpbmVzW2ldXG4gICAgICAgICAgICAgICAgLnN1YnN0cignbT0nLmxlbmd0aCwgbGluZXNbaV0uaW5kZXhPZignICcpIC0gJ209Jy5sZW5ndGgpO1xuICAgICAgICAgICAgc2tpcCA9IG1lZGlhdHlwZXMgIT09IHVuZGVmaW5lZCAmJiBtZWRpYXR5cGVzLmluZGV4T2YodHlwZSkgPT09IC0xO1xuXG4gICAgICAgICAgICBpZiAoIXNraXApIHtcbiAgICAgICAgICAgICAgICBjdXJfbWVkaWEgPSB7XG4gICAgICAgICAgICAgICAgICAgICd0eXBlJzogdHlwZSxcbiAgICAgICAgICAgICAgICAgICAgJ3NvdXJjZXMnOiB7fSxcbiAgICAgICAgICAgICAgICAgICAgJ2dyb3Vwcyc6IFtdXG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIHJlcy5wdXNoKGN1cl9tZWRpYSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfSBlbHNlIGlmICghc2tpcCAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYzonLmxlbmd0aCkgPT09ICdhPXNzcmM6Jykge1xuXG4gICAgICAgICAgICBpZHggPSBsaW5lc1tpXS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICBzc3JjID0gbGluZXNbaV0uc3Vic3RyaW5nKCdhPXNzcmM6Jy5sZW5ndGgsIGlkeCk7XG4gICAgICAgICAgICBpZiAoY3VyX21lZGlhLnNvdXJjZXNbc3NyY10gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGN1cl9zc3JjID0geydzc3JjJzogc3NyY307XG4gICAgICAgICAgICAgICAgY3VyX21lZGlhLnNvdXJjZXNbc3NyY10gPSBjdXJfc3NyYztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc3NyY19hdHRyaWJ1dGUgPSBsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVswXTtcbiAgICAgICAgICAgIGN1cl9zc3JjW3NzcmNfYXR0cmlidXRlXSA9IGxpbmVzW2ldLnN1YnN0cihpZHggKyAxKS5zcGxpdCgnOicsIDIpWzFdO1xuXG4gICAgICAgICAgICBpZiAoY3VyX21lZGlhLmJhc2UgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGN1cl9tZWRpYS5iYXNlID0gY3VyX3NzcmM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfSBlbHNlIGlmICghc2tpcCAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYy1ncm91cDonLmxlbmd0aCkgPT09ICdhPXNzcmMtZ3JvdXA6Jykge1xuICAgICAgICAgICAgaWR4ID0gbGluZXNbaV0uaW5kZXhPZignICcpO1xuICAgICAgICAgICAgc2VtYW50aWNzID0gbGluZXNbaV0uc3Vic3RyKDAsIGlkeCkuc3Vic3RyKCdhPXNzcmMtZ3JvdXA6Jy5sZW5ndGgpO1xuICAgICAgICAgICAgc3NyY3MgPSBsaW5lc1tpXS5zdWJzdHIoaWR4KS50cmltKCkuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIGdyb3VwID0ge1xuICAgICAgICAgICAgICAgICdzZW1hbnRpY3MnOiBzZW1hbnRpY3MsXG4gICAgICAgICAgICAgICAgJ3NzcmNzJzogc3NyY3NcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjdXJfbWVkaWEuZ3JvdXBzLnB1c2goZ3JvdXApO1xuICAgICAgICB9IGVsc2UgaWYgKCFza2lwICYmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c2VuZHJlY3YnLmxlbmd0aCkgPT09ICdhPXNlbmRyZWN2JyB8fFxuICAgICAgICAgICAgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXJlY3Zvbmx5Jy5sZW5ndGgpID09PSAnYT1yZWN2b25seScgfHxcbiAgICAgICAgICAgIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zZW5kb25seScubGVuZ3RoKSA9PT0gJ2E9c2VuZG9ubHknIHx8XG4gICAgICAgICAgICBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9aW5hY3RpdmUnLmxlbmd0aCkgPT09ICdhPWluYWN0aXZlJykpIHtcblxuICAgICAgICAgICAgY3VyX21lZGlhLmRpcmVjdGlvbiA9IGxpbmVzW2ldLnN1YnN0cmluZygnYT0nLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcmVzO1xufTtcblxuLyoqXG4gKiBUaGUgX2luZGV4T2ZBcnJheSgpIG1ldGhvZCByZXR1cm5zIHRoZSBmaXJzdCBhIENvbXBvdW5kSW5kZXggYXQgd2hpY2ggYVxuICogZ2l2ZW4gZWxlbWVudCBjYW4gYmUgZm91bmQgaW4gdGhlIGFycmF5LCBvciBfZW1wdHlDb21wb3VuZEluZGV4IGlmIGl0IGlzXG4gKiBub3QgcHJlc2VudC5cbiAqXG4gKiBFeGFtcGxlOlxuICpcbiAqIF9pbmRleE9mQXJyYXkoJzMnLCBbICd0aGlzIGlzIGxpbmUgMScsICd0aGlzIGlzIGxpbmUgMicsICd0aGlzIGlzIGxpbmUgMycgXSlcbiAqXG4gKiByZXR1cm5zIHtyb3c6IDIsIGNvbHVtbjogMTR9XG4gKlxuICogQHBhcmFtIG5lZWRsZVxuICogQHBhcmFtIGhheXN0YWNrXG4gKiBAcGFyYW0gc3RhcnRcbiAqIEByZXR1cm5zIHt9XG4gKiBAcHJpdmF0ZVxuICovXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX2luZGV4T2ZBcnJheSA9IGZ1bmN0aW9uIChuZWVkbGUsIGhheXN0YWNrLCBzdGFydCkge1xuICAgIHZhciBsZW5ndGggPSBoYXlzdGFjay5sZW5ndGgsIGlkeCwgaTtcblxuICAgIGlmICghc3RhcnQpIHtcbiAgICAgICAgc3RhcnQgPSAwO1xuICAgIH1cblxuICAgIGZvciAoaSA9IHN0YXJ0OyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWR4ID0gaGF5c3RhY2tbaV0uaW5kZXhPZihuZWVkbGUpO1xuICAgICAgICBpZiAoaWR4ICE9PSAtMSkge1xuICAgICAgICAgICAgcmV0dXJuIHtyb3c6IGksIGNvbHVtbjogaWR4fTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZW1wdHlDb21wb3VuZEluZGV4O1xufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9yZW1vdmVTaW11bGNhc3RHcm91cCA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciBpO1xuXG4gICAgZm9yIChpID0gbGluZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgICAgaWYgKGxpbmVzW2ldLmluZGV4T2YoJ2E9c3NyYy1ncm91cDpTSU0nKSAhPT0gLTEpIHtcbiAgICAgICAgICAgIGxpbmVzLnNwbGljZShpLCAxKTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fY29tcGlsZVZpZGVvU291cmNlcyA9IGZ1bmN0aW9uICh2aWRlb1NvdXJjZXMpIHtcbiAgICB2YXIgc2IgPSBbXSwgc3NyYywgYWRkZWRTU1JDcyA9IFtdO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnQ29tcGlsaW5nIHZpZGVvIHNvdXJjZXMuLi4nKTtcblxuICAgIC8vIEFkZCB0aGUgZ3JvdXBzXG4gICAgaWYgKHZpZGVvU291cmNlcy5ncm91cHMgJiYgdmlkZW9Tb3VyY2VzLmdyb3Vwcy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgdmlkZW9Tb3VyY2VzLmdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChncm91cCkge1xuICAgICAgICAgICAgaWYgKGdyb3VwLnNzcmNzICYmIGdyb3VwLnNzcmNzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgICAgIHNiLnB1c2goW1snYT1zc3JjLWdyb3VwOicsIGdyb3VwLnNlbWFudGljc10uam9pbignJyksIGdyb3VwLnNzcmNzLmpvaW4oJyAnKV0uam9pbignICcpKTtcblxuICAgICAgICAgICAgICAgIC8vIGlmIChncm91cC5zZW1hbnRpY3MgIT09ICdTSU0nKSB7XG4gICAgICAgICAgICAgICAgZ3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBhZGRlZFNTUkNzLnB1c2goc3NyYyk7XG4gICAgICAgICAgICAgICAgICAgIHNiLnNwbGljZS5hcHBseShzYiwgW3NiLmxlbmd0aCwgMF0uY29uY2F0KFtcbiAgICAgICAgICAgICAgICAgICAgICAgIFtcImE9c3NyYzpcIiwgc3NyYywgXCIgY25hbWU6XCIsIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLmNuYW1lXS5qb2luKCcnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIFtcImE9c3NyYzpcIiwgc3NyYywgXCIgbXNpZDpcIiwgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZF0uam9pbignJyldKSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgLy99XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIFRoZW4gYWRkIGFueSBmcmVlIHNvdXJjZXMuXG4gICAgaWYgKHZpZGVvU291cmNlcy5zb3VyY2VzKSB7XG4gICAgICAgIGZvciAoc3NyYyBpbiB2aWRlb1NvdXJjZXMuc291cmNlcykge1xuICAgICAgICAgICAgaWYgKGFkZGVkU1NSQ3MuaW5kZXhPZihzc3JjKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBzYi5zcGxpY2UuYXBwbHkoc2IsIFtzYi5sZW5ndGgsIDBdLmNvbmNhdChbXG4gICAgICAgICAgICAgICAgICAgIFtcImE9c3NyYzpcIiwgc3NyYywgXCIgY25hbWU6XCIsIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLmNuYW1lXS5qb2luKCcnKSxcbiAgICAgICAgICAgICAgICAgICAgW1wiYT1zc3JjOlwiLCBzc3JjLCBcIiBtc2lkOlwiLCB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkXS5qb2luKCcnKV0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzYjtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2ltdWxjYXN0VXRpbHM7IiwiLypqc2xpbnQgcGx1c3BsdXM6IHRydWUgKi9cbi8qanNsaW50IG5vbWVuOiB0cnVlKi9cblxudmFyIFNpbXVsY2FzdFNlbmRlciA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdFNlbmRlclwiKTtcbnZhciBOb1NpbXVsY2FzdFNlbmRlciA9IFNpbXVsY2FzdFNlbmRlcltcIm5vXCJdO1xudmFyIE5hdGl2ZVNpbXVsY2FzdFNlbmRlciA9IFNpbXVsY2FzdFNlbmRlcltcIm5hdGl2ZVwiXTtcbnZhciBTaW11bGNhc3RSZWNlaXZlciA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdFJlY2VpdmVyXCIpO1xudmFyIFNpbXVsY2FzdFV0aWxzID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0VXRpbHNcIik7XG5cblxuLyoqXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNpbXVsY2FzdE1hbmFnZXIoKSB7XG5cbiAgICAvLyBDcmVhdGUgdGhlIHNpbXVsY2FzdCB1dGlsaXRpZXMuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscyA9IG5ldyBTaW11bGNhc3RVdGlscygpO1xuXG4gICAgLy8gQ3JlYXRlIHJlbW90ZSBzaW11bGNhc3QuXG4gICAgdGhpcy5zaW11bGNhc3RSZWNlaXZlciA9IG5ldyBTaW11bGNhc3RSZWNlaXZlcigpO1xuXG4gICAgLy8gSW5pdGlhbGl6ZSBsb2NhbCBzaW11bGNhc3QuXG5cbiAgICAvLyBUT0RPKGdwKSBtb3ZlIGludG8gU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhIGFuZCB0YWtlIGludG9cbiAgICAvLyBhY2NvdW50IGNvbnN0cmFpbnRzLlxuICAgIGlmICghY29uZmlnLmVuYWJsZVNpbXVsY2FzdCkge1xuICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlciA9IG5ldyBOb1NpbXVsY2FzdFNlbmRlcigpO1xuICAgIH0gZWxzZSB7XG5cbiAgICAgICAgdmFyIGlzQ2hyb21pdW0gPSB3aW5kb3cuY2hyb21lLFxuICAgICAgICAgICAgdmVuZG9yTmFtZSA9IHdpbmRvdy5uYXZpZ2F0b3IudmVuZG9yO1xuICAgICAgICBpZihpc0Nocm9taXVtICE9PSBudWxsICYmIGlzQ2hyb21pdW0gIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgLyogc2tpcCBvcGVyYSAqL1xuICAgICAgICAgICAgJiYgdmVuZG9yTmFtZSA9PT0gXCJHb29nbGUgSW5jLlwiXG4gICAgICAgICAgICAvKiBza2lwIENocm9taXVtIGFzIHN1Z2dlc3RlZCBieSBmaXBwbyAqL1xuICAgICAgICAgICAgJiYgIXdpbmRvdy5uYXZpZ2F0b3IuYXBwVmVyc2lvbi5tYXRjaCgvQ2hyb21pdW1cXC8vKSApIHtcbiAgICAgICAgICAgIHZhciB2ZXIgPSBwYXJzZUludCh3aW5kb3cubmF2aWdhdG9yLmFwcFZlcnNpb24ubWF0Y2goL0Nocm9tZVxcLyhcXGQrKVxcLi8pWzFdLCAxMCk7XG4gICAgICAgICAgICBpZiAodmVyID4gMzcpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlciA9IG5ldyBOYXRpdmVTaW11bGNhc3RTZW5kZXIoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaW11bGNhc3RTZW5kZXIgPSBuZXcgTm9TaW11bGNhc3RTZW5kZXIoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyID0gbmV3IE5vU2ltdWxjYXN0U2VuZGVyKCk7XG4gICAgICAgIH1cblxuICAgIH1cbn1cblxuLyoqXG4gKiBSZXN0b3JlcyB0aGUgc2ltdWxjYXN0IGdyb3VwcyBvZiB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uLiBJblxuICogdHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gd2UgcmVtb3ZlIHRob3NlIGluIG9yZGVyIGZvciB0aGUgc2V0IHJlbW90ZVxuICogZGVzY3JpcHRpb24gdG8gc3VjY2VlZC4gVGhlIGZvY3VzIG5lZWRzIHRoZSBzaWduYWwgdGhlIGdyb3VwcyB0byBuZXdcbiAqIHBhcnRpY2lwYW50cy5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIucmV2ZXJzZVRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uKGRlc2MpO1xufTtcblxuLyoqXG4gKiBSZW1vdmVzIHRoZSBzc3JjLWdyb3VwOlNJTSBmcm9tIHRoZSByZW1vdGUgZGVzY3JpcHRpb24gYmFjYXVzZSBDaHJvbWVcbiAqIGVpdGhlciBnZXRzIGNvbmZ1c2VkIGFuZCB0aGlua3MgdGhpcyBpcyBhbiBGSUQgZ3JvdXAgb3IsIGlmIGFuIEZJRCBncm91cFxuICogaXMgYWxyZWFkeSBwcmVzZW50LCBpdCBmYWlscyB0byBzZXQgdGhlIHJlbW90ZSBkZXNjcmlwdGlvbi5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci50cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbihkZXNjKTtcbn07XG5cbi8qKlxuICogR2V0cyB0aGUgZnVsbHkgcXVhbGlmaWVkIG1zaWQgKHN0cmVhbS5pZCArIHRyYWNrLmlkKSBhc3NvY2lhdGVkIHRvIHRoZVxuICogU1NSQy5cbiAqXG4gKiBAcGFyYW0gc3NyY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMgPSBmdW5jdGlvbiAoc3NyYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMoc3NyYyk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgYSBzdHJlYW0gd2l0aCBzaW5nbGUgdmlkZW8gdHJhY2ssIHRoZSBvbmUgY3VycmVudGx5IGJlaW5nXG4gKiByZWNlaXZlZCBieSB0aGlzIGVuZHBvaW50LlxuICpcbiAqIEBwYXJhbSBzdHJlYW0gdGhlIHJlbW90ZSBzaW11bGNhc3Qgc3RyZWFtLlxuICogQHJldHVybnMge3dlYmtpdE1lZGlhU3RyZWFtfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShzdHJlYW0pO1xufTtcblxuLyoqXG4gKlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0U2VuZGVyLnRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24oZGVzYyk7XG59O1xuXG4vKipcbiAqXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0TG9jYWxWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFNlbmRlci5nZXRMb2NhbFZpZGVvU3RyZWFtKCk7XG59O1xuXG4vKipcbiAqIEdVTSBmb3Igc2ltdWxjYXN0LlxuICpcbiAqIEBwYXJhbSBjb25zdHJhaW50c1xuICogQHBhcmFtIHN1Y2Nlc3NcbiAqIEBwYXJhbSBlcnJcbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhID0gZnVuY3Rpb24gKGNvbnN0cmFpbnRzLCBzdWNjZXNzLCBlcnIpIHtcblxuICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyLmdldFVzZXJNZWRpYShjb25zdHJhaW50cywgc3VjY2VzcywgZXJyKTtcbn07XG5cbi8qKlxuICogUHJlcGFyZXMgdGhlIGxvY2FsIGRlc2NyaXB0aW9uIGZvciBwdWJsaWMgdXNhZ2UgKGkuZS4gdG8gYmUgc2lnbmFsZWRcbiAqIHRocm91Z2ggSmluZ2xlIHRvIHRoZSBmb2N1cykuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHtSVENTZXNzaW9uRGVzY3JpcHRpb259XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RTZW5kZXIucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24oZGVzYyk7XG59O1xuXG4vKipcbiAqIEVuc3VyZXMgdGhhdCB0aGUgc2ltdWxjYXN0IGdyb3VwIGlzIHByZXNlbnQgaW4gdGhlIGFuc3dlciwgX2lmXyBuYXRpdmVcbiAqIHNpbXVsY2FzdCBpcyBlbmFibGVkLFxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUudHJhbnNmb3JtQW5zd2VyID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RTZW5kZXIudHJhbnNmb3JtQW5zd2VyKGRlc2MpO1xufTtcblxuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nU1NSQyA9IGZ1bmN0aW9uIChqaWQpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci5nZXRSZWNlaXZpbmdTU1JDKGppZCk7XG59O1xuXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyA9IGZ1bmN0aW9uIChtc2lkKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW1CeVNTUkMobXNpZCk7XG59O1xuXG4vKipcbiAqXG4gKiBAcGFyYW0gbGluZXNcbiAqIEBwYXJhbSBtZWRpYXR5cGVzXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUucGFyc2VNZWRpYSA9IGZ1bmN0aW9uKGxpbmVzLCBtZWRpYXR5cGVzKSB7XG4gICAgdmFyIHNiID0gbGluZXMuc2RwLnNwbGl0KCdcXHJcXG4nKTtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKHNiLCBtZWRpYXR5cGVzKTtcbn07XG5cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLl9zZXRSZWNlaXZpbmdWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uKHJlc291cmNlLCBzc3JjKSB7XG4gICAgdGhpcy5zaW11bGNhc3RSZWNlaXZlci5fc2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0ocmVzb3VyY2UsIHNzcmMpO1xufTtcblxuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkID0gZnVuY3Rpb24oc3NyYywgZW5hYmxlZCkge1xuICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyLl9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZChzc3JjLCBlbmFibGVkKTtcbn07XG5cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnJlc2V0U2VuZGVyID0gZnVuY3Rpb24oKSB7XG4gICAgaWYgKHR5cGVvZiB0aGlzLnNpbXVsY2FzdFNlbmRlci5yZXNldCA9PT0gJ2Z1bmN0aW9uJyl7XG4gICAgICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyLnJlc2V0KCk7XG4gICAgfVxufTtcblxuJChkb2N1bWVudCkuYmluZCgnc2ltdWxjYXN0bGF5ZXJzY2hhbmdlZCcsIGZ1bmN0aW9uIChldmVudCwgZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpIHtcbiAgICBlbmRwb2ludFNpbXVsY2FzdExheWVycy5mb3JFYWNoKGZ1bmN0aW9uIChlc2wpIHtcbiAgICAgICAgdmFyIHNzcmMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG4gICAgICAgIHNpbXVsY2FzdC5fc2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0oZXNsLmVuZHBvaW50LCBzc3JjKTtcbiAgICB9KTtcbn0pO1xuXG4kKGRvY3VtZW50KS5iaW5kKCdzdGFydHNpbXVsY2FzdGxheWVyJywgZnVuY3Rpb24gKGV2ZW50LCBzaW11bGNhc3RMYXllcikge1xuICAgIHZhciBzc3JjID0gc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG4gICAgc2ltdWxjYXN0Ll9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZChzc3JjLCB0cnVlKTtcbn0pO1xuXG4kKGRvY3VtZW50KS5iaW5kKCdzdG9wc2ltdWxjYXN0bGF5ZXInLCBmdW5jdGlvbiAoZXZlbnQsIHNpbXVsY2FzdExheWVyKSB7XG4gICAgdmFyIHNzcmMgPSBzaW11bGNhc3RMYXllci5wcmltYXJ5U1NSQztcbiAgICBzaW11bGNhc3QuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkKHNzcmMsIGZhbHNlKTtcbn0pO1xuXG5cbnZhciBzaW11bGNhc3QgPSBuZXcgU2ltdWxjYXN0TWFuYWdlcigpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHNpbXVsY2FzdDsiXX0=