--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/annot-server/static/js/pianoroll.js Mon Jan 12 17:23:05 2015 +0100
@@ -0,0 +1,311 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.pianoroll=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){
+/**
+ * scripts/main.js
+ *
+ * This is the starting point for your application.
+ * Take a look at http://browserify.org/ for more info
+ */
+
+'use strict';
+
+
+var PIXI = require('pixi');
+
+// Config vars
+var logger = false;
+var sceneWidth = 1920;
+var prHeight1 = 435;
+var prHeight2 = 645;
+var sceneHeight = prHeight1 + prHeight2;
+var sceneBgColor = 0xFFFFFF;
+var lineColor = 0x444444;
+var pixelsPerSecond1 = Math.floor(sceneWidth / 10); // nb of pixels per second
+var manualFramerate = pixelsPerSecond1 / 4;
+var pixelsPerSecond2 = Math.floor(sceneWidth / 60); // nb of pixels per second
+var lineInterval = 5000; // means line every 5 seconds
+var nbLines = -1;
+var noteHeight = 110;
+var noteColors = [0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991];
+var colorsReg = {};
+// Vars
+var noteDict = [];
+// Timecode method
+var timePageLoaded = Date.now();
+var offsetMusic = false;
+
+
+//create an new instance of a pixi stage
+var stage = new PIXI.Stage(sceneBgColor);
+
+//create a renderer instance.
+var renderer = PIXI.autoDetectRenderer(sceneWidth, sceneHeight);
+
+//add the renderer view element to the DOM
+document.getElementById('canvasContainer').appendChild(renderer.view);
+
+var uberContainer = new PIXI.DisplayObjectContainer();
+uberContainer.position.x = Math.floor(sceneWidth*9/10);
+uberContainer.position.y = 0;
+stage.addChild(uberContainer);
+
+var PianoRoll = require('./pianoroll.js')
+
+// Init containers
+var containerList = [];
+containerList.push(new PianoRoll(uberContainer, 0, 0, prHeight1, true, pixelsPerSecond1, sceneWidth, noteColors, colorsReg, lineColor, lineInterval));
+containerList.push(new PianoRoll(uberContainer, 0, prHeight1, prHeight2, false, pixelsPerSecond2, sceneWidth, noteColors, colorsReg, lineColor, lineInterval));
+
+// Line between two containers
+var graphics = new PIXI.Graphics();
+graphics.beginFill(0xFFFF00);
+graphics.lineStyle(1, lineColor);
+graphics.moveTo(0, prHeight1);
+graphics.lineTo(sceneWidth, prHeight1);
+graphics.endFill();
+stage.addChild(graphics);
+
+
+function replaceContainers(){
+ var diff = (Date.now() - timePageLoaded)/1000;// nb of seconds since page loaded
+ //console.log("replace ! diff1 = ", container1.x - Math.floor(-diff*pixelsPerSecond1), ", diff 2 = ", container2.x - Math.floor(-diff*pixelsPerSecond2));
+ for(var i=0;i<containerList.length;i++){
+ containerList[i].moveTo(-diff);
+ }
+ renderer.render(stage);
+}
+
+function addNotes(data){
+ if(!offsetMusic){
+ // get difference between the current note timecode and my zero to set the difference between the canvas's zero and the music's zero
+ // in order to place in real time
+ var now = Date.now();
+ var timeBetweenNowAndStart = now - timePageLoaded;
+ offsetMusic = timeBetweenNowAndStart - data.content[1];
+ //console.log("start: ", timePageLoaded, ", now: ", now, ", timeBetweenNowAndStart = ", timeBetweenNowAndStart, ", offsetMusic = ", offsetMusic);
+ }
+ var note = data.content[3];
+ var velocity = data.content[4];
+ if(velocity===0){
+ if(typeof noteDict[data.content[2]][note]!=='undefined'){
+ // We close the note in container one
+ //console.log("coucou 2", data);
+ var duration = data.content[1] - noteDict[data.content[2]][note].ts;
+ for(var i=0;i<containerList.length;i++){
+ // addNote(note, startTime, duration, velocity, canal)
+ containerList[i].addNote(note, noteDict[data.content[2]][note].ts, duration, noteDict[data.content[2]][note].velocity, data.content[2]);
+ }
+ // delete entry
+ delete noteDict[data.content[2]][note];
+ }
+ }
+ else{
+ if(typeof noteDict[data.content[2]]==='undefined'){
+ noteDict[data.content[2]] = {};
+ }
+ noteDict[data.content[2]][note] = {ts: data.content[1], velocity:velocity};
+ }
+}
+
+function addLine(){
+ nbLines++;
+ for(var i=0;i<containerList.length;i++){
+ containerList[i].addLine(nbLines);
+ }
+}
+
+
+
+// Socket management
+var sock = null;
+var ellog = null;
+function log(m) {
+ if(logger){
+ ellog.innerHTML += m + '\n';
+ ellog.scrollTop = ellog.scrollHeight;
+ }
+}
+window.onload = function(){
+
+ if(logger){
+ ellog = document.getElementById('log');
+ }
+ else{
+ document.body.removeChild(document.getElementById('log'));
+ }
+
+ var wsuri;
+ if (window.location.protocol === 'file:') {
+ wsuri = 'ws://127.0.0.1:8090/broadcast';
+ } else {
+ wsuri = 'ws://' + window.location.hostname + ':8090/broadcast';
+ }
+ wsuri = wsuri + '?channel=PIANOROLL&event_code='+eventCode;
+
+ if ('WebSocket' in window) {
+ sock = new WebSocket(wsuri);
+ } else if ('MozWebSocket' in window) {
+ sock = new MozWebSocket(wsuri);
+ } else {
+ log('Browser does not support WebSocket!');
+ window.location = 'http://autobahn.ws/unsupportedbrowser';
+ }
+
+ if (sock) {
+ sock.onopen = function(){
+ if(logger){
+ log('Connected to ' + wsuri);
+ }
+ };
+
+ sock.onclose = function(e) {
+ if(logger){
+ log('Connection closed (wasClean = ' + e.wasClean + ', code = ' + e.code + ', reason = \'' + e.reason + '\')');
+ }
+ sock = null;
+ };
+
+ sock.onmessage = function(e) {
+ if(logger){
+ log('Got message: ' + e.data);
+ }
+ addNotes(JSON.parse(e.data));
+ };
+ }
+};
+
+
+// Init page and intervals
+addLine();
+var moveInterval = window.setInterval(replaceContainers, 1000/manualFramerate);
+var verticalLinesInterval = window.setInterval(addLine, lineInterval);
+
+// Little inteval to show time
+var nbSec = 0;
+var mySpan = document.getElementById('myspan');
+function updateTime(){
+ nbSec++;
+ var hours = parseInt( nbSec / 3600 ) % 24;
+ var minutes = parseInt( nbSec / 60 ) % 60;
+ var seconds = nbSec % 60;
+ var timeStr = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
+ mySpan.innerHTML = timeStr;
+}
+var secondInterval = window.setInterval(updateTime, 1000);
+
+module.exports = {
+ moveInterval: moveInterval,
+ verticalLinesInterval: verticalLinesInterval,
+ secondInterval: secondInterval
+};
+
+},{"./pianoroll.js":2,"pixi":"pixi"}],2:[function(require,module,exports){
+/**
+* js/pianoroll.js
+*
+* pianoroll basic component
+*
+*/
+
+'use strict';
+
+var PIXI = require('pixi');
+var randomColor = require('randomColor');
+
+function PianoRoll(parentContainer, xInit, yInit, height, linesDown, pixelsPerSecond, width, noteColors, colorsReg, lineColor, lineInterval){
+ var _this = this;
+ this.container = new PIXI.DisplayObjectContainer();
+ this.container.position.x = xInit;
+ this.container.position.y = yInit;
+ parentContainer.addChild(this.container);
+
+ this.linesDown = linesDown;
+ this.height = height;
+ this.pixelsPerSecond = pixelsPerSecond;
+ this.width = width;
+ this.noteColors = noteColors;
+ this.colorsReg = colorsReg || {};
+ this.lineColor = lineColor;
+ this.lineInterval = lineInterval;
+
+ this.addNote = function(note, startTime, duration, velocity, canal){
+ //console.log("coucou 1", note, timeFromZero, ts, velocity, pixelsPerSecond, container, prHeight);
+ var beginX = (offsetMusic + startTime) * this.pixelsPerSecond / 1000;
+ var width = duration * this.pixelsPerSecond / 1000;
+ // We draw the rectangle
+ var graphics = new PIXI.Graphics();
+ //console.log("beginX = ", beginX, "canal = ", canal, "color = ", noteColor[canal], "width = ", width, "note = ", note, "velocity = ", velocity);
+ var color = this.colorsReg[canal];
+ if(typeof(color) === 'undefined') {
+ var colorsRegSize = Object.keys(this.colorsReg).length;
+ if(colorsRegSize < this.noteColors.length) {
+ color = this.colorsReg[canal] = this.noteColors[colorsRegSize];
+ }
+ else {
+ color = this.colorsReg[canal] = parseInt(randomColor({ luminosity: 'light', hue: 'random', format:'hex'}).replace(/^#/, ''), 16);
+ }
+ }
+ graphics.beginFill(color, (velocity / 128));
+ var y = (128-note) * this.height / 128; // (128-note) because y = 0 is for note = 128 and y = 128 for note = 0
+ graphics.drawRect(0, Math.floor(y - (noteHeight/2) + ((this.height / 128)/2)), width, noteHeight);
+ graphics.endFill();
+ graphics.x = beginX;
+ this.container.addChild(graphics);
+ };
+
+ this.addLine = function(lineNb){
+ var graphics = new PIXI.Graphics();
+ var x = -this.container.x;
+ graphics.beginFill(0xFFFF00);
+ graphics.lineStyle(1, this.lineColor);
+ var y = this.linesDown ? this.height - 20 : 0;
+ graphics.moveTo(x, y);
+ graphics.lineTo(x, y + 20);
+ graphics.endFill();
+ this.container.addChild(graphics);
+ // Add text
+ var totalSec = lineNb * this.lineInterval / 1000;
+ var hours = parseInt( totalSec / 3600 ) % 24;
+ var minutes = parseInt( totalSec / 60 ) % 60;
+ var seconds = totalSec % 60;
+ var timeStr = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
+ var fontObj = { font: '10pt Arial', fill: '#444444' };
+ var t = new PIXI.Text(timeStr, fontObj);
+ t.x = x + 2;
+ t.y = this.linesDown ? this.height - 15 : 2;
+ this.container.addChild(t);
+ };
+
+ this.moveTo = function(diffTime){
+ this.container.x = Math.floor(diffTime*this.pixelsPerSecond);
+ };
+
+ this.removePassedObjets = function(){
+ var nbChilds = _this.container.children.length;
+ var i = 0, childIsNowDisplayed = false;
+ while(i<nbChilds && !childIsNowDisplayed){
+ var child = _this.container.children[0];
+ //console.log("remove ? ", child.x, child.width, ((child.x + child.width) < (Math.abs(_this.container.x) - _this.width)));
+ if((child.x + child.width) < (Math.abs(_this.container.x) - _this.width)){
+ _this.container.removeChild(child);
+ //console.log(" remove !!!");
+ }
+ else{
+ childIsNowDisplayed = true;
+ //console.log(" childIsNowDisplayed");
+ }
+ i++;
+ }
+ //console.log("before : ", nbChilds, ", after : ", _this.container.children.length);
+ };
+
+ // remove notes each scene width
+ //var removeInterval = window.setInterval(this.removePassedObjets, 1000 * sceneWidth / this.pixelsPerSecond );
+ window.setInterval(this.removePassedObjets, 1000 * this.width / this.pixelsPerSecond );
+
+}
+
+module.exports = PianoRoll;
+
+},{"pixi":"pixi","randomColor":"randomColor"}]},{},[1])(1)
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIuL2FwcC9qcy9tYWluLmpzIiwiL1VzZXJzL3ltaC9kZXYvcHJvamVjdHMvbW9ucy9kZXYvY2xpZW50L3BpYW5vcm9sbC9hcHAvanMvcGlhbm9yb2xsLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdE1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoqXG4gKiBzY3JpcHRzL21haW4uanNcbiAqXG4gKiBUaGlzIGlzIHRoZSBzdGFydGluZyBwb2ludCBmb3IgeW91ciBhcHBsaWNhdGlvbi5cbiAqIFRha2UgYSBsb29rIGF0IGh0dHA6Ly9icm93c2VyaWZ5Lm9yZy8gZm9yIG1vcmUgaW5mb1xuICovXG5cbid1c2Ugc3RyaWN0JztcblxuXG52YXIgUElYSSA9IHJlcXVpcmUoJ3BpeGknKTtcblxuLy8gQ29uZmlnIHZhcnNcbnZhciBsb2dnZXIgPSBmYWxzZTtcbnZhciBzY2VuZVdpZHRoID0gMTkyMDtcbnZhciBwckhlaWdodDEgPSA0MzU7XG52YXIgcHJIZWlnaHQyID0gNjQ1O1xudmFyIHNjZW5lSGVpZ2h0ID0gcHJIZWlnaHQxICsgcHJIZWlnaHQyO1xudmFyIHNjZW5lQmdDb2xvciA9IDB4RkZGRkZGO1xudmFyIGxpbmVDb2xvciA9IDB4NDQ0NDQ0O1xudmFyIHBpeGVsc1BlclNlY29uZDEgPSBNYXRoLmZsb29yKHNjZW5lV2lkdGggLyAxMCk7IC8vIG5iIG9mIHBpeGVscyBwZXIgc2Vjb25kXG52YXIgbWFudWFsRnJhbWVyYXRlID0gcGl4ZWxzUGVyU2Vjb25kMSAvIDQ7XG52YXIgcGl4ZWxzUGVyU2Vjb25kMiA9IE1hdGguZmxvb3Ioc2NlbmVXaWR0aCAvIDYwKTsgLy8gbmIgb2YgcGl4ZWxzIHBlciBzZWNvbmRcbnZhciBsaW5lSW50ZXJ2YWwgPSA1MDAwOyAvLyBtZWFucyBsaW5lIGV2ZXJ5IDUgc2Vjb25kc1xudmFyIG5iTGluZXMgPSAtMTtcbnZhciBub3RlSGVpZ2h0ID0gMTEwO1xudmFyIG5vdGVDb2xvcnMgPSBbMHhCOTAwMDAsIDB4NEJERDcxLCAweEFGOTMxRSwgMHgxQzI4QkEsIDB4NTM2OTkxXTtcbnZhciBjb2xvcnNSZWcgPSB7fTtcbi8vIFZhcnNcbnZhciBub3RlRGljdCA9IFtdO1xuLy8gVGltZWNvZGUgbWV0aG9kXG52YXIgdGltZVBhZ2VMb2FkZWQgPSBEYXRlLm5vdygpO1xudmFyIG9mZnNldE11c2ljID0gZmFsc2U7XG5cblxuLy9jcmVhdGUgYW4gbmV3IGluc3RhbmNlIG9mIGEgcGl4aSBzdGFnZVxudmFyIHN0YWdlID0gbmV3IFBJWEkuU3RhZ2Uoc2NlbmVCZ0NvbG9yKTtcblxuLy9jcmVhdGUgYSByZW5kZXJlciBpbnN0YW5jZS5cbnZhciByZW5kZXJlciA9IFBJWEkuYXV0b0RldGVjdFJlbmRlcmVyKHNjZW5lV2lkdGgsIHNjZW5lSGVpZ2h0KTtcblxuLy9hZGQgdGhlIHJlbmRlcmVyIHZpZXcgZWxlbWVudCB0byB0aGUgRE9NXG5kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2FudmFzQ29udGFpbmVyJykuYXBwZW5kQ2hpbGQocmVuZGVyZXIudmlldyk7XG5cbnZhciB1YmVyQ29udGFpbmVyID0gbmV3IFBJWEkuRGlzcGxheU9iamVjdENvbnRhaW5lcigpO1xudWJlckNvbnRhaW5lci5wb3NpdGlvbi54ID0gTWF0aC5mbG9vcihzY2VuZVdpZHRoKjkvMTApO1xudWJlckNvbnRhaW5lci5wb3NpdGlvbi55ID0gMDtcbnN0YWdlLmFkZENoaWxkKHViZXJDb250YWluZXIpO1xuXG52YXIgUGlhbm9Sb2xsID0gcmVxdWlyZSgnLi9waWFub3JvbGwuanMnKVxuXG4vLyBJbml0IGNvbnRhaW5lcnNcbnZhciBjb250YWluZXJMaXN0ID0gW107XG5jb250YWluZXJMaXN0LnB1c2gobmV3IFBpYW5vUm9sbCh1YmVyQ29udGFpbmVyLCAwLCAwLCBwckhlaWdodDEsIHRydWUsIHBpeGVsc1BlclNlY29uZDEsIHNjZW5lV2lkdGgsIG5vdGVDb2xvcnMsIGNvbG9yc1JlZywgbGluZUNvbG9yLCBsaW5lSW50ZXJ2YWwpKTtcbmNvbnRhaW5lckxpc3QucHVzaChuZXcgUGlhbm9Sb2xsKHViZXJDb250YWluZXIsIDAsIHBySGVpZ2h0MSwgcHJIZWlnaHQyLCBmYWxzZSwgcGl4ZWxzUGVyU2Vjb25kMiwgc2NlbmVXaWR0aCwgbm90ZUNvbG9ycywgY29sb3JzUmVnLCBsaW5lQ29sb3IsIGxpbmVJbnRlcnZhbCkpO1xuXG4vLyBMaW5lIGJldHdlZW4gdHdvIGNvbnRhaW5lcnNcbnZhciBncmFwaGljcyA9IG5ldyBQSVhJLkdyYXBoaWNzKCk7XG5ncmFwaGljcy5iZWdpbkZpbGwoMHhGRkZGMDApO1xuZ3JhcGhpY3MubGluZVN0eWxlKDEsIGxpbmVDb2xvcik7XG5ncmFwaGljcy5tb3ZlVG8oMCwgcHJIZWlnaHQxKTtcbmdyYXBoaWNzLmxpbmVUbyhzY2VuZVdpZHRoLCBwckhlaWdodDEpO1xuZ3JhcGhpY3MuZW5kRmlsbCgpO1xuc3RhZ2UuYWRkQ2hpbGQoZ3JhcGhpY3MpO1xuXG5cbmZ1bmN0aW9uIHJlcGxhY2VDb250YWluZXJzKCl7XG4gICAgdmFyIGRpZmYgPSAoRGF0ZS5ub3coKSAtIHRpbWVQYWdlTG9hZGVkKS8xMDAwOy8vIG5iIG9mIHNlY29uZHMgc2luY2UgcGFnZSBsb2FkZWRcbiAgICAvL2NvbnNvbGUubG9nKFwicmVwbGFjZSAhIGRpZmYxID0gXCIsIGNvbnRhaW5lcjEueCAtIE1hdGguZmxvb3IoLWRpZmYqcGl4ZWxzUGVyU2Vjb25kMSksIFwiLCBkaWZmIDIgPSBcIiwgY29udGFpbmVyMi54IC0gTWF0aC5mbG9vcigtZGlmZipwaXhlbHNQZXJTZWNvbmQyKSk7XG4gICAgZm9yKHZhciBpPTA7aTxjb250YWluZXJMaXN0Lmxlbmd0aDtpKyspe1xuICAgICAgICBjb250YWluZXJMaXN0W2ldLm1vdmVUbygtZGlmZik7XG4gICAgfVxuICAgIHJlbmRlcmVyLnJlbmRlcihzdGFnZSk7XG59XG5cbmZ1bmN0aW9uIGFkZE5vdGVzKGRhdGEpe1xuICAgIGlmKCFvZmZzZXRNdXNpYyl7XG4gICAgICAgIC8vIGdldCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGN1cnJlbnQgbm90ZSB0aW1lY29kZSBhbmQgbXkgemVybyB0byBzZXQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY2FudmFzJ3MgemVybyBhbmQgdGhlIG11c2ljJ3MgemVyb1xuICAgICAgICAvLyBpbiBvcmRlciB0byBwbGFjZSBpbiByZWFsIHRpbWVcbiAgICAgICAgdmFyIG5vdyA9IERhdGUubm93KCk7XG4gICAgICAgIHZhciB0aW1lQmV0d2Vlbk5vd0FuZFN0YXJ0ID0gbm93IC0gdGltZVBhZ2VMb2FkZWQ7XG4gICAgICAgIG9mZnNldE11c2ljID0gdGltZUJldHdlZW5Ob3dBbmRTdGFydCAtIGRhdGEuY29udGVudFsxXTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcInN0YXJ0OiBcIiwgdGltZVBhZ2VMb2FkZWQsIFwiLCBub3c6IFwiLCBub3csIFwiLCB0aW1lQmV0d2Vlbk5vd0FuZFN0YXJ0ID0gXCIsIHRpbWVCZXR3ZWVuTm93QW5kU3RhcnQsIFwiLCBvZmZzZXRNdXNpYyA9IFwiLCBvZmZzZXRNdXNpYyk7XG4gICAgfVxuICAgIHZhciBub3RlID0gZGF0YS5jb250ZW50WzNdO1xuICAgIHZhciB2ZWxvY2l0eSA9IGRhdGEuY29udGVudFs0XTtcbiAgICBpZih2ZWxvY2l0eT09PTApe1xuICAgICAgICBpZih0eXBlb2Ygbm90ZURpY3RbZGF0YS5jb250ZW50WzJdXVtub3RlXSE9PSd1bmRlZmluZWQnKXtcbiAgICAgICAgICAgIC8vIFdlIGNsb3NlIHRoZSBub3RlIGluIGNvbnRhaW5lciBvbmVcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJjb3Vjb3UgMlwiLCBkYXRhKTtcbiAgICAgICAgICAgIHZhciBkdXJhdGlvbiA9IGRhdGEuY29udGVudFsxXSAtIG5vdGVEaWN0W2RhdGEuY29udGVudFsyXV1bbm90ZV0udHM7XG4gICAgICAgICAgICBmb3IodmFyIGk9MDtpPGNvbnRhaW5lckxpc3QubGVuZ3RoO2krKyl7XG4gICAgICAgICAgICAgICAgLy8gICAgICAgICAgICAgICBhZGROb3RlKG5vdGUsIHN0YXJ0VGltZSwgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uLCB2ZWxvY2l0eSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5hbClcbiAgICAgICAgICAgICAgICBjb250YWluZXJMaXN0W2ldLmFkZE5vdGUobm90ZSwgbm90ZURpY3RbZGF0YS5jb250ZW50WzJdXVtub3RlXS50cywgZHVyYXRpb24sIG5vdGVEaWN0W2RhdGEuY29udGVudFsyXV1bbm90ZV0udmVsb2NpdHksIGRhdGEuY29udGVudFsyXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBkZWxldGUgZW50cnlcbiAgICAgICAgICAgIGRlbGV0ZSBub3RlRGljdFtkYXRhLmNvbnRlbnRbMl1dW25vdGVdO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsc2V7XG4gICAgICAgIGlmKHR5cGVvZiBub3RlRGljdFtkYXRhLmNvbnRlbnRbMl1dPT09J3VuZGVmaW5lZCcpe1xuICAgICAgICAgICAgbm90ZURpY3RbZGF0YS5jb250ZW50WzJdXSA9IHt9O1xuICAgICAgICB9XG4gICAgICAgIG5vdGVEaWN0W2RhdGEuY29udGVudFsyXV1bbm90ZV0gPSB7dHM6IGRhdGEuY29udGVudFsxXSwgdmVsb2NpdHk6dmVsb2NpdHl9O1xuICAgIH1cbn1cblxuZnVuY3Rpb24gYWRkTGluZSgpe1xuICAgIG5iTGluZXMrKztcbiAgICBmb3IodmFyIGk9MDtpPGNvbnRhaW5lckxpc3QubGVuZ3RoO2krKyl7XG4gICAgICAgIGNvbnRhaW5lckxpc3RbaV0uYWRkTGluZShuYkxpbmVzKTtcbiAgICB9XG59XG5cblxuXG4vLyBTb2NrZXQgbWFuYWdlbWVudFxudmFyIHNvY2sgPSBudWxsO1xudmFyIGVsbG9nID0gbnVsbDtcbmZ1bmN0aW9uIGxvZyhtKSB7XG4gICAgaWYobG9nZ2VyKXtcbiAgICAgICAgZWxsb2cuaW5uZXJIVE1MICs9IG0gKyAnXFxuJztcbiAgICAgICAgZWxsb2cuc2Nyb2xsVG9wID0gZWxsb2cuc2Nyb2xsSGVpZ2h0O1xuICAgIH1cbn1cbndpbmRvdy5vbmxvYWQgPSBmdW5jdGlvbigpe1xuXG4gICAgaWYobG9nZ2VyKXtcbiAgICAgICAgZWxsb2cgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9nJyk7XG4gICAgfVxuICAgIGVsc2V7XG4gICAgICAgIGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvZycpKTtcbiAgICB9XG5cbiAgICB2YXIgd3N1cmk7XG4gICAgaWYgKHdpbmRvdy5sb2NhdGlvbi5wcm90b2NvbCA9PT0gJ2ZpbGU6Jykge1xuICAgICAgICB3c3VyaSA9ICd3czovLzEyNy4wLjAuMTo4MDkwL2Jyb2FkY2FzdCc7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgd3N1cmkgPSAnd3M6Ly8nICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4MDkwL2Jyb2FkY2FzdCc7XG4gICAgfVxuICAgIHdzdXJpID0gd3N1cmkgKyAnP2NoYW5uZWw9UElBTk9ST0xMJmV2ZW50X2NvZGU9JytldmVudENvZGU7XG5cbiAgICBpZiAoJ1dlYlNvY2tldCcgaW4gd2luZG93KSB7XG4gICAgICAgIHNvY2sgPSBuZXcgV2ViU29ja2V0KHdzdXJpKTtcbiAgICB9IGVsc2UgaWYgKCdNb3pXZWJTb2NrZXQnIGluIHdpbmRvdykge1xuICAgICAgICBzb2NrID0gbmV3IE1veldlYlNvY2tldCh3c3VyaSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbG9nKCdCcm93c2VyIGRvZXMgbm90IHN1cHBvcnQgV2ViU29ja2V0IScpO1xuICAgICAgICB3aW5kb3cubG9jYXRpb24gPSAnaHR0cDovL2F1dG9iYWhuLndzL3Vuc3VwcG9ydGVkYnJvd3Nlcic7XG4gICAgfVxuXG4gICAgaWYgKHNvY2spIHtcbiAgICAgICAgc29jay5vbm9wZW4gPSBmdW5jdGlvbigpe1xuICAgICAgICAgICAgaWYobG9nZ2VyKXtcbiAgICAgICAgICAgICAgICBsb2coJ0Nvbm5lY3RlZCB0byAnICsgd3N1cmkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHNvY2sub25jbG9zZSA9IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIGlmKGxvZ2dlcil7XG4gICAgICAgICAgICAgICAgbG9nKCdDb25uZWN0aW9uIGNsb3NlZCAod2FzQ2xlYW4gPSAnICsgZS53YXNDbGVhbiArICcsIGNvZGUgPSAnICsgZS5jb2RlICsgJywgcmVhc29uID0gXFwnJyArIGUucmVhc29uICsgJ1xcJyknKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNvY2sgPSBudWxsO1xuICAgICAgICB9O1xuXG4gICAgICAgIHNvY2sub25tZXNzYWdlID0gZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgaWYobG9nZ2VyKXtcbiAgICAgICAgICAgICAgICBsb2coJ0dvdCBtZXNzYWdlOiAnICsgZS5kYXRhKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGFkZE5vdGVzKEpTT04ucGFyc2UoZS5kYXRhKSk7XG4gICAgICAgIH07XG4gICAgfVxufTtcblxuXG4vLyBJbml0IHBhZ2UgYW5kIGludGVydmFsc1xuYWRkTGluZSgpO1xudmFyIG1vdmVJbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChyZXBsYWNlQ29udGFpbmVycywgMTAwMC9tYW51YWxGcmFtZXJhdGUpO1xudmFyIHZlcnRpY2FsTGluZXNJbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChhZGRMaW5lLCBsaW5lSW50ZXJ2YWwpO1xuXG4vLyBMaXR0bGUgaW50ZXZhbCB0byBzaG93IHRpbWVcbnZhciBuYlNlYyA9IDA7XG52YXIgbXlTcGFuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ215c3BhbicpO1xuZnVuY3Rpb24gdXBkYXRlVGltZSgpe1xuICAgIG5iU2VjKys7XG4gICAgdmFyIGhvdXJzID0gcGFyc2VJbnQoIG5iU2VjIC8gMzYwMCApICUgMjQ7XG4gICAgdmFyIG1pbnV0ZXMgPSBwYXJzZUludCggbmJTZWMgLyA2MCApICUgNjA7XG4gICAgdmFyIHNlY29uZHMgPSBuYlNlYyAlIDYwO1xuICAgIHZhciB0aW1lU3RyID0gKGhvdXJzIDwgMTAgPyAnMCcgKyBob3VycyA6IGhvdXJzKSArICc6JyArIChtaW51dGVzIDwgMTAgPyAnMCcgKyBtaW51dGVzIDogbWludXRlcykgKyAnOicgKyAoc2Vjb25kcyAgPCAxMCA/ICcwJyArIHNlY29uZHMgOiBzZWNvbmRzKTtcbiAgICBteVNwYW4uaW5uZXJIVE1MID0gdGltZVN0cjtcbn1cbnZhciBzZWNvbmRJbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbCh1cGRhdGVUaW1lLCAxMDAwKTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbW92ZUludGVydmFsOiBtb3ZlSW50ZXJ2YWwsXG4gICAgdmVydGljYWxMaW5lc0ludGVydmFsOiB2ZXJ0aWNhbExpbmVzSW50ZXJ2YWwsXG4gICAgc2Vjb25kSW50ZXJ2YWw6IHNlY29uZEludGVydmFsXG59O1xuIiwiLyoqXG4qIGpzL3BpYW5vcm9sbC5qc1xuKlxuKiBwaWFub3JvbGwgYmFzaWMgY29tcG9uZW50XG4qXG4qL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBQSVhJID0gcmVxdWlyZSgncGl4aScpO1xudmFyIHJhbmRvbUNvbG9yID0gcmVxdWlyZSgncmFuZG9tQ29sb3InKTtcblxuZnVuY3Rpb24gUGlhbm9Sb2xsKHBhcmVudENvbnRhaW5lciwgeEluaXQsIHlJbml0LCBoZWlnaHQsIGxpbmVzRG93biwgcGl4ZWxzUGVyU2Vjb25kLCB3aWR0aCwgbm90ZUNvbG9ycywgY29sb3JzUmVnLCBsaW5lQ29sb3IsIGxpbmVJbnRlcnZhbCl7XG4gICAgdmFyIF90aGlzID0gdGhpcztcbiAgICB0aGlzLmNvbnRhaW5lciA9IG5ldyBQSVhJLkRpc3BsYXlPYmplY3RDb250YWluZXIoKTtcbiAgICB0aGlzLmNvbnRhaW5lci5wb3NpdGlvbi54ID0geEluaXQ7XG4gICAgdGhpcy5jb250YWluZXIucG9zaXRpb24ueSA9IHlJbml0O1xuICAgIHBhcmVudENvbnRhaW5lci5hZGRDaGlsZCh0aGlzLmNvbnRhaW5lcik7XG5cbiAgICB0aGlzLmxpbmVzRG93biA9IGxpbmVzRG93bjtcbiAgICB0aGlzLmhlaWdodCA9IGhlaWdodDtcbiAgICB0aGlzLnBpeGVsc1BlclNlY29uZCA9IHBpeGVsc1BlclNlY29uZDtcbiAgICB0aGlzLndpZHRoID0gd2lkdGg7XG4gICAgdGhpcy5ub3RlQ29sb3JzID0gbm90ZUNvbG9ycztcbiAgICB0aGlzLmNvbG9yc1JlZyA9IGNvbG9yc1JlZyB8fCB7fTtcbiAgICB0aGlzLmxpbmVDb2xvciA9IGxpbmVDb2xvcjtcbiAgICB0aGlzLmxpbmVJbnRlcnZhbCA9IGxpbmVJbnRlcnZhbDtcblxuICAgIHRoaXMuYWRkTm90ZSA9IGZ1bmN0aW9uKG5vdGUsIHN0YXJ0VGltZSwgZHVyYXRpb24sIHZlbG9jaXR5LCBjYW5hbCl7XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJjb3Vjb3UgMVwiLCBub3RlLCB0aW1lRnJvbVplcm8sIHRzLCB2ZWxvY2l0eSwgcGl4ZWxzUGVyU2Vjb25kLCBjb250YWluZXIsIHBySGVpZ2h0KTtcbiAgICAgICAgdmFyIGJlZ2luWCA9IChvZmZzZXRNdXNpYyArIHN0YXJ0VGltZSkgKiB0aGlzLnBpeGVsc1BlclNlY29uZCAvIDEwMDA7XG4gICAgICAgIHZhciB3aWR0aCA9IGR1cmF0aW9uICogdGhpcy5waXhlbHNQZXJTZWNvbmQgLyAxMDAwO1xuICAgICAgICAvLyBXZSBkcmF3IHRoZSByZWN0YW5nbGVcbiAgICAgICAgdmFyIGdyYXBoaWNzID0gbmV3IFBJWEkuR3JhcGhpY3MoKTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcImJlZ2luWCA9IFwiLCBiZWdpblgsIFwiY2FuYWwgPSBcIiwgY2FuYWwsIFwiY29sb3IgPSBcIiwgbm90ZUNvbG9yW2NhbmFsXSwgXCJ3aWR0aCA9IFwiLCB3aWR0aCwgXCJub3RlID0gXCIsIG5vdGUsIFwidmVsb2NpdHkgPSBcIiwgdmVsb2NpdHkpO1xuICAgICAgICB2YXIgY29sb3IgPSB0aGlzLmNvbG9yc1JlZ1tjYW5hbF07XG4gICAgICAgIGlmKHR5cGVvZihjb2xvcikgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICB2YXIgY29sb3JzUmVnU2l6ZSA9IE9iamVjdC5rZXlzKHRoaXMuY29sb3JzUmVnKS5sZW5ndGg7XG4gICAgICAgICAgICBpZihjb2xvcnNSZWdTaXplIDwgdGhpcy5ub3RlQ29sb3JzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGNvbG9yID0gdGhpcy5jb2xvcnNSZWdbY2FuYWxdID0gdGhpcy5ub3RlQ29sb3JzW2NvbG9yc1JlZ1NpemVdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29sb3IgPSB0aGlzLmNvbG9yc1JlZ1tjYW5hbF0gPSBwYXJzZUludChyYW5kb21Db2xvcih7IGx1bWlub3NpdHk6ICdsaWdodCcsIGh1ZTogJ3JhbmRvbScsIGZvcm1hdDonaGV4J30pLnJlcGxhY2UoL14jLywgJycpLCAxNik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZ3JhcGhpY3MuYmVnaW5GaWxsKGNvbG9yLCAodmVsb2NpdHkgLyAxMjgpKTtcbiAgICAgICAgdmFyIHkgPSAoMTI4LW5vdGUpICogdGhpcy5oZWlnaHQgLyAxMjg7IC8vICgxMjgtbm90ZSkgYmVjYXVzZSB5ID0gMCBpcyBmb3Igbm90ZSA9IDEyOCBhbmQgeSA9IDEyOCBmb3Igbm90ZSA9IDBcbiAgICAgICAgZ3JhcGhpY3MuZHJhd1JlY3QoMCwgTWF0aC5mbG9vcih5IC0gKG5vdGVIZWlnaHQvMikgKyAoKHRoaXMuaGVpZ2h0IC8gMTI4KS8yKSksIHdpZHRoLCBub3RlSGVpZ2h0KTtcbiAgICAgICAgZ3JhcGhpY3MuZW5kRmlsbCgpO1xuICAgICAgICBncmFwaGljcy54ID0gYmVnaW5YO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRDaGlsZChncmFwaGljcyk7XG4gICAgfTtcblxuICAgIHRoaXMuYWRkTGluZSA9IGZ1bmN0aW9uKGxpbmVOYil7XG4gICAgICAgIHZhciBncmFwaGljcyA9IG5ldyBQSVhJLkdyYXBoaWNzKCk7XG4gICAgICAgIHZhciB4ID0gLXRoaXMuY29udGFpbmVyLng7XG4gICAgICAgIGdyYXBoaWNzLmJlZ2luRmlsbCgweEZGRkYwMCk7XG4gICAgICAgIGdyYXBoaWNzLmxpbmVTdHlsZSgxLCB0aGlzLmxpbmVDb2xvcik7XG4gICAgICAgIHZhciB5ID0gdGhpcy5saW5lc0Rvd24gPyB0aGlzLmhlaWdodCAtIDIwIDogMDtcbiAgICAgICAgZ3JhcGhpY3MubW92ZVRvKHgsIHkpO1xuICAgICAgICBncmFwaGljcy5saW5lVG8oeCwgeSArIDIwKTtcbiAgICAgICAgZ3JhcGhpY3MuZW5kRmlsbCgpO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRDaGlsZChncmFwaGljcyk7XG4gICAgICAgIC8vIEFkZCB0ZXh0XG4gICAgICAgIHZhciB0b3RhbFNlYyA9IGxpbmVOYiAqIHRoaXMubGluZUludGVydmFsIC8gMTAwMDtcbiAgICAgICAgdmFyIGhvdXJzID0gcGFyc2VJbnQoIHRvdGFsU2VjIC8gMzYwMCApICUgMjQ7XG4gICAgICAgIHZhciBtaW51dGVzID0gcGFyc2VJbnQoIHRvdGFsU2VjIC8gNjAgKSAlIDYwO1xuICAgICAgICB2YXIgc2Vjb25kcyA9IHRvdGFsU2VjICUgNjA7XG4gICAgICAgIHZhciB0aW1lU3RyID0gKGhvdXJzIDwgMTAgPyAnMCcgKyBob3VycyA6IGhvdXJzKSArICc6JyArIChtaW51dGVzIDwgMTAgPyAnMCcgKyBtaW51dGVzIDogbWludXRlcykgKyAnOicgKyAoc2Vjb25kcyAgPCAxMCA/ICcwJyArIHNlY29uZHMgOiBzZWNvbmRzKTtcbiAgICAgICAgdmFyIGZvbnRPYmogPSB7IGZvbnQ6ICcxMHB0IEFyaWFsJywgZmlsbDogJyM0NDQ0NDQnIH07XG4gICAgICAgIHZhciB0ID0gbmV3IFBJWEkuVGV4dCh0aW1lU3RyLCBmb250T2JqKTtcbiAgICAgICAgdC54ID0geCArIDI7XG4gICAgICAgIHQueSA9IHRoaXMubGluZXNEb3duID8gdGhpcy5oZWlnaHQgLSAxNSA6IDI7XG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFkZENoaWxkKHQpO1xuICAgIH07XG5cbiAgICB0aGlzLm1vdmVUbyA9IGZ1bmN0aW9uKGRpZmZUaW1lKXtcbiAgICAgICAgdGhpcy5jb250YWluZXIueCA9IE1hdGguZmxvb3IoZGlmZlRpbWUqdGhpcy5waXhlbHNQZXJTZWNvbmQpO1xuICAgIH07XG5cbiAgICB0aGlzLnJlbW92ZVBhc3NlZE9iamV0cyA9IGZ1bmN0aW9uKCl7XG4gICAgICAgIHZhciBuYkNoaWxkcyA9IF90aGlzLmNvbnRhaW5lci5jaGlsZHJlbi5sZW5ndGg7XG4gICAgICAgIHZhciBpID0gMCwgY2hpbGRJc05vd0Rpc3BsYXllZCA9IGZhbHNlO1xuICAgICAgICB3aGlsZShpPG5iQ2hpbGRzICYmICFjaGlsZElzTm93RGlzcGxheWVkKXtcbiAgICAgICAgICAgIHZhciBjaGlsZCA9IF90aGlzLmNvbnRhaW5lci5jaGlsZHJlblswXTtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJyZW1vdmUgPyBcIiwgY2hpbGQueCwgY2hpbGQud2lkdGgsICgoY2hpbGQueCArIGNoaWxkLndpZHRoKSA8IChNYXRoLmFicyhfdGhpcy5jb250YWluZXIueCkgLSBfdGhpcy53aWR0aCkpKTtcbiAgICAgICAgICAgIGlmKChjaGlsZC54ICsgY2hpbGQud2lkdGgpIDwgKE1hdGguYWJzKF90aGlzLmNvbnRhaW5lci54KSAtIF90aGlzLndpZHRoKSl7XG4gICAgICAgICAgICAgICAgX3RoaXMuY29udGFpbmVyLnJlbW92ZUNoaWxkKGNoaWxkKTtcbiAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwiICAgIHJlbW92ZSAhISFcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNle1xuICAgICAgICAgICAgICAgIGNoaWxkSXNOb3dEaXNwbGF5ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCIgICAgY2hpbGRJc05vd0Rpc3BsYXllZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGkrKztcbiAgICAgICAgfVxuICAgICAgICAvL2NvbnNvbGUubG9nKFwiYmVmb3JlIDogXCIsIG5iQ2hpbGRzLCBcIiwgYWZ0ZXIgOiBcIiwgX3RoaXMuY29udGFpbmVyLmNoaWxkcmVuLmxlbmd0aCk7XG4gICAgfTtcblxuICAgIC8vIHJlbW92ZSBub3RlcyBlYWNoIHNjZW5lIHdpZHRoXG4gICAgLy92YXIgcmVtb3ZlSW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwodGhpcy5yZW1vdmVQYXNzZWRPYmpldHMsIDEwMDAgKiBzY2VuZVdpZHRoIC8gdGhpcy5waXhlbHNQZXJTZWNvbmQgKTtcbiAgICB3aW5kb3cuc2V0SW50ZXJ2YWwodGhpcy5yZW1vdmVQYXNzZWRPYmpldHMsIDEwMDAgKiB0aGlzLndpZHRoIC8gdGhpcy5waXhlbHNQZXJTZWNvbmQgKTtcblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFBpYW5vUm9sbDtcbiJdfQ==