Created
July 6, 2013 19:50
-
-
Save mikolalysenko/5941042 to your computer and use it in GitHub Desktop.
made with requirebin.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style type='text/css'> html, body { margin: 0; padding: 0; border: 0; } </style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var shell = require("game-shell")() | |
var context | |
, player_x = 250 | |
, player_y = 250 | |
//Bind keyboard commands | |
shell.bind("move-left", "left", "A") | |
shell.bind("move-right", "right", "D") | |
shell.bind("move-up", "up", "W") | |
shell.bind("move-down", "down", "S") | |
//Fired when document is loaded | |
shell.on("init", function() { | |
var canvas = document.createElement("canvas") | |
canvas.width = 500 | |
canvas.height = 500 | |
shell.element.appendChild(canvas) | |
context = canvas.getContext("2d") | |
}) | |
//Fired once per game tick | |
shell.on("tick", function() { | |
if(shell.wasDown("move-left")) { | |
player_x -= 1 | |
} | |
if(shell.wasDown("move-right")) { | |
player_x += 1 | |
} | |
if(shell.wasDown("move-up")) { | |
player_y -= 1 | |
} | |
if(shell.wasDown("move-down")) { | |
player_y += 1 | |
} | |
}) | |
//Render a frame | |
shell.on("render", function(frame_time) { | |
context.fillStyle = "#000" | |
context.fillRect(0, 0, 500, 500) | |
context.fillStyle = "#f00" | |
context.fillRect(player_x-10, player_y-10, 20, 20) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require=(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){ | |
// shim for using process in browser | |
var process = module.exports = {}; | |
process.nextTick = (function () { | |
var canSetImmediate = typeof window !== 'undefined' | |
&& window.setImmediate; | |
var canPost = typeof window !== 'undefined' | |
&& window.postMessage && window.addEventListener | |
; | |
if (canSetImmediate) { | |
return function (f) { return window.setImmediate(f) }; | |
} | |
if (canPost) { | |
var queue = []; | |
window.addEventListener('message', function (ev) { | |
if (ev.source === window && ev.data === 'process-tick') { | |
ev.stopPropagation(); | |
if (queue.length > 0) { | |
var fn = queue.shift(); | |
fn(); | |
} | |
} | |
}, true); | |
return function nextTick(fn) { | |
queue.push(fn); | |
window.postMessage('process-tick', '*'); | |
}; | |
} | |
return function nextTick(fn) { | |
setTimeout(fn, 0); | |
}; | |
})(); | |
process.title = 'browser'; | |
process.browser = true; | |
process.env = {}; | |
process.argv = []; | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
} | |
// TODO(shtylman) | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
},{}],2:[function(require,module,exports){ | |
(function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; | |
var EventEmitter = exports.EventEmitter = process.EventEmitter; | |
var isArray = typeof Array.isArray === 'function' | |
? Array.isArray | |
: function (xs) { | |
return Object.prototype.toString.call(xs) === '[object Array]' | |
} | |
; | |
function indexOf (xs, x) { | |
if (xs.indexOf) return xs.indexOf(x); | |
for (var i = 0; i < xs.length; i++) { | |
if (x === xs[i]) return i; | |
} | |
return -1; | |
} | |
// By default EventEmitters will print a warning if more than | |
// 10 listeners are added to it. This is a useful default which | |
// helps finding memory leaks. | |
// | |
// Obviously not all Emitters should be limited to 10. This function allows | |
// that to be increased. Set to zero for unlimited. | |
var defaultMaxListeners = 10; | |
EventEmitter.prototype.setMaxListeners = function(n) { | |
if (!this._events) this._events = {}; | |
this._events.maxListeners = n; | |
}; | |
EventEmitter.prototype.emit = function(type) { | |
// If there is no 'error' event listener then throw. | |
if (type === 'error') { | |
if (!this._events || !this._events.error || | |
(isArray(this._events.error) && !this._events.error.length)) | |
{ | |
if (arguments[1] instanceof Error) { | |
throw arguments[1]; // Unhandled 'error' event | |
} else { | |
throw new Error("Uncaught, unspecified 'error' event."); | |
} | |
return false; | |
} | |
} | |
if (!this._events) return false; | |
var handler = this._events[type]; | |
if (!handler) return false; | |
if (typeof handler == 'function') { | |
switch (arguments.length) { | |
// fast cases | |
case 1: | |
handler.call(this); | |
break; | |
case 2: | |
handler.call(this, arguments[1]); | |
break; | |
case 3: | |
handler.call(this, arguments[1], arguments[2]); | |
break; | |
// slower | |
default: | |
var args = Array.prototype.slice.call(arguments, 1); | |
handler.apply(this, args); | |
} | |
return true; | |
} else if (isArray(handler)) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
var listeners = handler.slice(); | |
for (var i = 0, l = listeners.length; i < l; i++) { | |
listeners[i].apply(this, args); | |
} | |
return true; | |
} else { | |
return false; | |
} | |
}; | |
// EventEmitter is defined in src/node_events.cc | |
// EventEmitter.prototype.emit() is also defined there. | |
EventEmitter.prototype.addListener = function(type, listener) { | |
if ('function' !== typeof listener) { | |
throw new Error('addListener only takes instances of Function'); | |
} | |
if (!this._events) this._events = {}; | |
// To avoid recursion in the case that type == "newListeners"! Before | |
// adding it to the listeners, first emit "newListeners". | |
this.emit('newListener', type, listener); | |
if (!this._events[type]) { | |
// Optimize the case of one listener. Don't need the extra array object. | |
this._events[type] = listener; | |
} else if (isArray(this._events[type])) { | |
// Check for listener leak | |
if (!this._events[type].warned) { | |
var m; | |
if (this._events.maxListeners !== undefined) { | |
m = this._events.maxListeners; | |
} else { | |
m = defaultMaxListeners; | |
} | |
if (m && m > 0 && this._events[type].length > m) { | |
this._events[type].warned = true; | |
console.error('(node) warning: possible EventEmitter memory ' + | |
'leak detected. %d listeners added. ' + | |
'Use emitter.setMaxListeners() to increase limit.', | |
this._events[type].length); | |
console.trace(); | |
} | |
} | |
// If we've already got an array, just append. | |
this._events[type].push(listener); | |
} else { | |
// Adding the second element, need to change to array. | |
this._events[type] = [this._events[type], listener]; | |
} | |
return this; | |
}; | |
EventEmitter.prototype.on = EventEmitter.prototype.addListener; | |
EventEmitter.prototype.once = function(type, listener) { | |
var self = this; | |
self.on(type, function g() { | |
self.removeListener(type, g); | |
listener.apply(this, arguments); | |
}); | |
return this; | |
}; | |
EventEmitter.prototype.removeListener = function(type, listener) { | |
if ('function' !== typeof listener) { | |
throw new Error('removeListener only takes instances of Function'); | |
} | |
// does not use listeners(), so no side effect of creating _events[type] | |
if (!this._events || !this._events[type]) return this; | |
var list = this._events[type]; | |
if (isArray(list)) { | |
var i = indexOf(list, listener); | |
if (i < 0) return this; | |
list.splice(i, 1); | |
if (list.length == 0) | |
delete this._events[type]; | |
} else if (this._events[type] === listener) { | |
delete this._events[type]; | |
} | |
return this; | |
}; | |
EventEmitter.prototype.removeAllListeners = function(type) { | |
if (arguments.length === 0) { | |
this._events = {}; | |
return this; | |
} | |
// does not use listeners(), so no side effect of creating _events[type] | |
if (type && this._events && this._events[type]) this._events[type] = null; | |
return this; | |
}; | |
EventEmitter.prototype.listeners = function(type) { | |
if (!this._events) this._events = {}; | |
if (!this._events[type]) this._events[type] = []; | |
if (!isArray(this._events[type])) { | |
this._events[type] = [this._events[type]]; | |
} | |
return this._events[type]; | |
}; | |
})(require("__browserify_process")) | |
},{"__browserify_process":1}],3:[function(require,module,exports){ | |
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel | |
// MIT license | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] | |
|| window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) | |
window.requestAnimationFrame = function(callback, element) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | |
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | |
timeToCall); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
if (!window.cancelAnimationFrame) | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
},{}],4:[function(require,module,exports){ | |
var events = require('events'); | |
exports.isArray = isArray; | |
exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'}; | |
exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'}; | |
exports.print = function () {}; | |
exports.puts = function () {}; | |
exports.debug = function() {}; | |
exports.inspect = function(obj, showHidden, depth, colors) { | |
var seen = []; | |
var stylize = function(str, styleType) { | |
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics | |
var styles = | |
{ 'bold' : [1, 22], | |
'italic' : [3, 23], | |
'underline' : [4, 24], | |
'inverse' : [7, 27], | |
'white' : [37, 39], | |
'grey' : [90, 39], | |
'black' : [30, 39], | |
'blue' : [34, 39], | |
'cyan' : [36, 39], | |
'green' : [32, 39], | |
'magenta' : [35, 39], | |
'red' : [31, 39], | |
'yellow' : [33, 39] }; | |
var style = | |
{ 'special': 'cyan', | |
'number': 'blue', | |
'boolean': 'yellow', | |
'undefined': 'grey', | |
'null': 'bold', | |
'string': 'green', | |
'date': 'magenta', | |
// "name": intentionally not styling | |
'regexp': 'red' }[styleType]; | |
if (style) { | |
return '\033[' + styles[style][0] + 'm' + str + | |
'\033[' + styles[style][1] + 'm'; | |
} else { | |
return str; | |
} | |
}; | |
if (! colors) { | |
stylize = function(str, styleType) { return str; }; | |
} | |
function format(value, recurseTimes) { | |
// Provide a hook for user-specified inspect functions. | |
// Check that value is an object with an inspect function on it | |
if (value && typeof value.inspect === 'function' && | |
// Filter out the util module, it's inspect function is special | |
value !== exports && | |
// Also filter out any prototype objects using the circular check. | |
!(value.constructor && value.constructor.prototype === value)) { | |
return value.inspect(recurseTimes); | |
} | |
// Primitive types cannot have properties | |
switch (typeof value) { | |
case 'undefined': | |
return stylize('undefined', 'undefined'); | |
case 'string': | |
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | |
.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') + '\''; | |
return stylize(simple, 'string'); | |
case 'number': | |
return stylize('' + value, 'number'); | |
case 'boolean': | |
return stylize('' + value, 'boolean'); | |
} | |
// For some reason typeof null is "object", so special case here. | |
if (value === null) { | |
return stylize('null', 'null'); | |
} | |
// Look up the keys of the object. | |
var visible_keys = Object_keys(value); | |
var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys; | |
// Functions without properties can be shortcutted. | |
if (typeof value === 'function' && keys.length === 0) { | |
if (isRegExp(value)) { | |
return stylize('' + value, 'regexp'); | |
} else { | |
var name = value.name ? ': ' + value.name : ''; | |
return stylize('[Function' + name + ']', 'special'); | |
} | |
} | |
// Dates without properties can be shortcutted | |
if (isDate(value) && keys.length === 0) { | |
return stylize(value.toUTCString(), 'date'); | |
} | |
var base, type, braces; | |
// Determine the object type | |
if (isArray(value)) { | |
type = 'Array'; | |
braces = ['[', ']']; | |
} else { | |
type = 'Object'; | |
braces = ['{', '}']; | |
} | |
// Make functions say that they are functions | |
if (typeof value === 'function') { | |
var n = value.name ? ': ' + value.name : ''; | |
base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; | |
} else { | |
base = ''; | |
} | |
// Make dates with properties first say the date | |
if (isDate(value)) { | |
base = ' ' + value.toUTCString(); | |
} | |
if (keys.length === 0) { | |
return braces[0] + base + braces[1]; | |
} | |
if (recurseTimes < 0) { | |
if (isRegExp(value)) { | |
return stylize('' + value, 'regexp'); | |
} else { | |
return stylize('[Object]', 'special'); | |
} | |
} | |
seen.push(value); | |
var output = keys.map(function(key) { | |
var name, str; | |
if (value.__lookupGetter__) { | |
if (value.__lookupGetter__(key)) { | |
if (value.__lookupSetter__(key)) { | |
str = stylize('[Getter/Setter]', 'special'); | |
} else { | |
str = stylize('[Getter]', 'special'); | |
} | |
} else { | |
if (value.__lookupSetter__(key)) { | |
str = stylize('[Setter]', 'special'); | |
} | |
} | |
} | |
if (visible_keys.indexOf(key) < 0) { | |
name = '[' + key + ']'; | |
} | |
if (!str) { | |
if (seen.indexOf(value[key]) < 0) { | |
if (recurseTimes === null) { | |
str = format(value[key]); | |
} else { | |
str = format(value[key], recurseTimes - 1); | |
} | |
if (str.indexOf('\n') > -1) { | |
if (isArray(value)) { | |
str = str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n').substr(2); | |
} else { | |
str = '\n' + str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n'); | |
} | |
} | |
} else { | |
str = stylize('[Circular]', 'special'); | |
} | |
} | |
if (typeof name === 'undefined') { | |
if (type === 'Array' && key.match(/^\d+$/)) { | |
return str; | |
} | |
name = JSON.stringify('' + key); | |
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { | |
name = name.substr(1, name.length - 2); | |
name = stylize(name, 'name'); | |
} else { | |
name = name.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') | |
.replace(/(^"|"$)/g, "'"); | |
name = stylize(name, 'string'); | |
} | |
} | |
return name + ': ' + str; | |
}); | |
seen.pop(); | |
var numLinesEst = 0; | |
var length = output.reduce(function(prev, cur) { | |
numLinesEst++; | |
if (cur.indexOf('\n') >= 0) numLinesEst++; | |
return prev + cur.length + 1; | |
}, 0); | |
if (length > 50) { | |
output = braces[0] + | |
(base === '' ? '' : base + '\n ') + | |
' ' + | |
output.join(',\n ') + | |
' ' + | |
braces[1]; | |
} else { | |
output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; | |
} | |
return output; | |
} | |
return format(obj, (typeof depth === 'undefined' ? 2 : depth)); | |
}; | |
function isArray(ar) { | |
return ar instanceof Array || | |
Array.isArray(ar) || | |
(ar && ar !== Object.prototype && isArray(ar.__proto__)); | |
} | |
function isRegExp(re) { | |
return re instanceof RegExp || | |
(typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]'); | |
} | |
function isDate(d) { | |
if (d instanceof Date) return true; | |
if (typeof d !== 'object') return false; | |
var properties = Date.prototype && Object_getOwnPropertyNames(Date.prototype); | |
var proto = d.__proto__ && Object_getOwnPropertyNames(d.__proto__); | |
return JSON.stringify(proto) === JSON.stringify(properties); | |
} | |
function pad(n) { | |
return n < 10 ? '0' + n.toString(10) : n.toString(10); | |
} | |
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', | |
'Oct', 'Nov', 'Dec']; | |
// 26 Feb 16:19:34 | |
function timestamp() { | |
var d = new Date(); | |
var time = [pad(d.getHours()), | |
pad(d.getMinutes()), | |
pad(d.getSeconds())].join(':'); | |
return [d.getDate(), months[d.getMonth()], time].join(' '); | |
} | |
exports.log = function (msg) {}; | |
exports.pump = null; | |
var Object_keys = Object.keys || function (obj) { | |
var res = []; | |
for (var key in obj) res.push(key); | |
return res; | |
}; | |
var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) { | |
var res = []; | |
for (var key in obj) { | |
if (Object.hasOwnProperty.call(obj, key)) res.push(key); | |
} | |
return res; | |
}; | |
var Object_create = Object.create || function (prototype, properties) { | |
// from es5-shim | |
var object; | |
if (prototype === null) { | |
object = { '__proto__' : null }; | |
} | |
else { | |
if (typeof prototype !== 'object') { | |
throw new TypeError( | |
'typeof prototype[' + (typeof prototype) + '] != \'object\'' | |
); | |
} | |
var Type = function () {}; | |
Type.prototype = prototype; | |
object = new Type(); | |
object.__proto__ = prototype; | |
} | |
if (typeof properties !== 'undefined' && Object.defineProperties) { | |
Object.defineProperties(object, properties); | |
} | |
return object; | |
}; | |
exports.inherits = function(ctor, superCtor) { | |
ctor.super_ = superCtor; | |
ctor.prototype = Object_create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
var formatRegExp = /%[sdj%]/g; | |
exports.format = function(f) { | |
if (typeof f !== 'string') { | |
var objects = []; | |
for (var i = 0; i < arguments.length; i++) { | |
objects.push(exports.inspect(arguments[i])); | |
} | |
return objects.join(' '); | |
} | |
var i = 1; | |
var args = arguments; | |
var len = args.length; | |
var str = String(f).replace(formatRegExp, function(x) { | |
if (x === '%%') return '%'; | |
if (i >= len) return x; | |
switch (x) { | |
case '%s': return String(args[i++]); | |
case '%d': return Number(args[i++]); | |
case '%j': return JSON.stringify(args[i++]); | |
default: | |
return x; | |
} | |
}); | |
for(var x = args[i]; i < len; x = args[++i]){ | |
if (x === null || typeof x !== 'object') { | |
str += ' ' + x; | |
} else { | |
str += ' ' + exports.inspect(x); | |
} | |
} | |
return str; | |
}; | |
},{"events":2}],"game-shell":[function(require,module,exports){ | |
module.exports=require('IZUbLl'); | |
},{}],"IZUbLl":[function(require,module,exports){ | |
"use strict" | |
var EventEmitter = require("events").EventEmitter | |
, util = require("util") | |
, domready = require("domready") | |
, vkey = require("vkey") | |
, invert = require("invert-hash") | |
, uniq = require("uniq") | |
, lowerBound = require("lower-bound") | |
, iota = require("iota-array") | |
, min = Math.min | |
require("./lib/raf-polyfill.js") | |
//Remove angle braces and other useless crap | |
var filtered_vkey = (function() { | |
var result = new Array(256) | |
, i, j, k | |
for(i=0; i<256; ++i) { | |
result[i] = "UNK" | |
} | |
for(i in vkey) { | |
k = vkey[i] | |
if(k.charAt(0) === '<' && k.charAt(k.length-1) === '>') { | |
k = k.substring(1, k.length-1) | |
} | |
k = k.replace(/\s/g, "-") | |
result[parseInt(i)] = k | |
} | |
return result | |
})() | |
//Compute minimal common set of keyboard functions | |
var keyNames = uniq(Object.keys(invert(filtered_vkey))) | |
//Translates a virtual keycode to a normalized keycode | |
function virtualKeyCode(key) { | |
var idx = lowerBound(keyNames, key) | |
if(idx < 0 || idx >= keyNames.length) { | |
return -1 | |
} | |
return idx | |
} | |
//Maps a physical keycode to a normalized keycode | |
function physicalKeyCode(key) { | |
return virtualKeyCode(filtered_vkey[key]) | |
} | |
//Game shell | |
function GameShell() { | |
EventEmitter.call(this) | |
this._curKeyState = new Array(keyNames.length) | |
this._pressCount = new Array(keyNames.length) | |
this._releaseCount = new Array(keyNames.length) | |
this._tickInterval = null | |
this._tickRate = 0 | |
this._lastTick = Date.now() | |
this._frameTime = 0.0 | |
this._paused = false | |
this._render = render.bind(undefined, this) | |
for(var i=0; i<keyNames.length; ++i) { | |
this._curKeyState[i] = false | |
this._pressCount[i] = this._releaseCount[i] = 0 | |
} | |
//Public members | |
this.element = null | |
this.bindings = {} | |
this.frameSkip = 100.0 | |
this.tickCount = 0 | |
this.frameCount = 0 | |
this.startTime = Date.now() | |
this.tickTime = this._tickRate | |
this.frameTime = 10.0 | |
//Mouse state | |
this.mouseX = 0 | |
this.mouseY = 0 | |
this.prevMouseX = 0 | |
this.prevMouseY = 0 | |
} | |
util.inherits(GameShell, EventEmitter) | |
//Bind keynames | |
GameShell.prototype.keyNames = keyNames | |
//Binds a virtual keyboard event to a physical key | |
GameShell.prototype.bind = function(virtual_key) { | |
//Look up previous key bindings | |
var arr | |
if(virtual_key in this.bindings) { | |
arr = this.bindings[virtual_key] | |
} else { | |
arr = [] | |
} | |
//Add keys to list | |
var physical_key | |
for(var i=1, n=arguments.length; i<n; ++i) { | |
physical_key = arguments[i] | |
if(virtualKeyCode(physical_key) >= 0) { | |
arr.push(physical_key) | |
} | |
} | |
//Remove any duplicate keys | |
arr = uniq(arr) | |
if(arr.length > 0) { | |
this.bindings[virtual_key] = arr | |
} | |
} | |
//Unbinds a virtual keyboard event | |
GameShell.prototype.unbind = function(virtual_key) { | |
if(virtual_key in this.bindings) { | |
delete this.bindings[virtual_key] | |
} | |
} | |
//Checks if a key is set in a given state | |
function lookupKey(state, bindings, key) { | |
if(key in bindings) { | |
var arr = bindings[key] | |
for(var i=0, n=arr.length; i<n; ++i) { | |
if(state[virtualKeyCode(arr[i])]) { | |
return true | |
} | |
} | |
return false | |
} | |
var kc = virtualKeyCode(key) | |
if(kc >= 0) { | |
return state[kc] | |
} | |
return false | |
} | |
//Checks if a key is set in a given state | |
function lookupCount(state, bindings, key) { | |
if(key in bindings) { | |
var arr = bindings[key], r = 0 | |
for(var i=0, n=arr.length; i<n; ++i) { | |
r += state[virtualKeyCode(arr[i])] | |
} | |
return r | |
} | |
var kc = virtualKeyCode(key) | |
if(kc >= 0) { | |
return state[kc] | |
} | |
return 0 | |
} | |
//Checks if a key (either physical or virtual) is currently held down | |
GameShell.prototype.down = function(key) { | |
return lookupKey(this._curKeyState, this.bindings, key) | |
} | |
//Checks if a key was ever down | |
GameShell.prototype.wasDown = function(key) { | |
return this.down(key) || this.press(key) | |
} | |
//Opposite of down | |
GameShell.prototype.up = function(key) { | |
return !this.down(key) | |
} | |
GameShell.prototype.wasUp = function(key) { | |
return this.up(key) || this.release(key) | |
} | |
//Returns the number of times a key was pressed since last tick | |
GameShell.prototype.press = function(key) { | |
return lookupCount(this._pressCount, this.bindings, key) | |
} | |
//Returns the number of times a key was released since last tick | |
GameShell.prototype.release = function(key) { | |
return lookupCount(this._releaseCount, this.bindings, key) | |
} | |
//Pause/unpause the game loop | |
Object.defineProperty(GameShell.prototype, "paused", { | |
get: function() { | |
return this._paused | |
}, | |
set: function(p) { | |
if(p) { | |
if(!this._paused) { | |
this._paused = true | |
this._frameTime = min(1.0, (Date.now() - this._lastTick) / this._tickRate) | |
} | |
} else if(this._paused) { | |
this._paused = false | |
this._lastTick = Date.now() - Math.floor(this._frameTime * this._tickRate) | |
} | |
} | |
}) | |
//Set key state | |
function setKeyState(shell, key, state) { | |
var ps = shell._curKeyState[key] | |
if(ps !== state) { | |
if(state) { | |
shell._pressCount[key]++ | |
} else { | |
shell._releaseCount[key]++ | |
} | |
shell._curKeyState[key] = state | |
} | |
} | |
//Ticks the game state one update | |
function tick(shell) { | |
var skip = Date.now() + shell.frameSkip | |
, pCount = shell._pressCount | |
, rCount = shell._releaseCount | |
, i, s, t | |
, tr = shell._tickRate | |
, n = keyNames.length | |
while(!shell._paused && | |
Date.now() >= shell._lastTick + tr) { | |
//Skip a frame if we are over budget | |
if(Date.now() > skip) { | |
shell._lastTick = Date.now() + tr | |
return | |
} | |
//Update counters and time | |
++shell.tickCount | |
shell._lastTick += tr | |
//Tick the game | |
s = Date.now() | |
shell.emit("tick") | |
t = Date.now() | |
shell.tickTime = shell.tickTime * 0.3 + (t - s) * 0.7 | |
//Shift input state | |
for(i=0; i<n; ++i) { | |
pCount[i] = rCount[i] = 0 | |
} | |
shell.prevMouseX = shell.mouseX | |
shell.prevMouseY = shell.mouseY | |
} | |
} | |
//Render stuff | |
function render(shell) { | |
//Tick the shell | |
tick(shell) | |
//Compute frame time | |
var dt | |
if(shell._paused) { | |
dt = shell._frameTime | |
} else { | |
dt = min(1.0, (Date.now() - shell._lastTick) / shell._tickRate) | |
} | |
//Draw a frame | |
++shell.frameCount | |
var s = Date.now() | |
shell.emit("render", dt) | |
var t = Date.now() | |
shell.frameTime = shell.frameTime * 0.3 + (t - s) * 0.7 | |
//Request next frame | |
requestAnimationFrame(shell._render) | |
} | |
//Set key up | |
function handleKeyUp(shell, ev) { | |
var kc = physicalKeyCode(ev.keyCode || ev.which || ev.charCode) | |
if(kc >= 0) { | |
setKeyState(shell, kc, false) | |
} | |
} | |
//Set key down | |
function handleKeyDown(shell, ev) { | |
var kc = physicalKeyCode(ev.keyCode || ev.char || ev.which || ev.charCode) | |
if(kc >= 0) { | |
setKeyState(shell, kc, true) | |
} | |
} | |
var mouseCodes = iota(5).map(function(n) { | |
return virtualKeyCode("mouse-" + (n+1)) | |
}) | |
function setMouseButtons(shell, buttons) { | |
for(var i=0; i<5; ++i) { | |
setKeyState(shell, mouseCodes[i], !!(buttons & (1<<i))) | |
} | |
} | |
function handleMouseMove(shell, ev) { | |
if(ev.which !== undefined) { | |
setMouseButtons(shell, ev.which) | |
} | |
if(ev.buttons !== undefined) { | |
setMouseButtons(shell, ev.buttons) | |
} | |
shell.mouseX = ev.clientX | |
shell.mouseY = ev.clientY | |
} | |
function handleMouseDown(shell, ev) { | |
handleMouseMove(shell, ev) | |
setKeyState(shell, mouseCodes[ev.button], true) | |
} | |
function handleMouseUp(shell, ev) { | |
handleMouseMove(shell, ev) | |
setKeyState(shell, mouseCodes[ev.button], false) | |
} | |
function handleMouseEnter(shell, ev) { | |
handleMouseMove(shell, ev) | |
shell.prevMouseX = shell.mouseX = ev.clientX | |
shell.prevMouseY = shell.mouseY = ev.clientY | |
} | |
function handleMouseLeave(shell, ev) { | |
for(var i=0; i<5; ++i) { | |
setKeyState(shell, mouseCodes[i], false) | |
} | |
} | |
function handleBlur(shell, ev) { | |
var n = keyNames.length | |
, c = shell._curKeyState | |
, i | |
for(i=0; i<n; ++i) { | |
c[i] = false | |
} | |
} | |
function makeDefaultContainer() { | |
var container = document.createElement("div") | |
container.style.position = "absolute" | |
container.style.left = "0px" | |
container.style.right = "0px" | |
container.style.top = "0px" | |
container.style.bottom = "0px" | |
document.body.appendChild(container) | |
return container | |
} | |
function createShell(options) { | |
options = options || {} | |
//Create initial shell | |
var shell = new GameShell() | |
shell._tickRate = options.tickRate || 30 | |
shell.frameSkip = options.frameSkip || (shell._tickRate+5) * 5 | |
//Set bindings | |
if(options.bindings) { | |
shell.bindings = bindings | |
} | |
//Wait for dom to intiailize | |
setTimeout(function() { domready(function() { | |
//Retrieve element | |
var element = options.element | |
if(typeof element === "string") { | |
var e = document.getElementById(element) | |
if(!e) { | |
e = document.querySelector(element) | |
} | |
if(!e) { | |
e = document.getElementByClass(element)[0] | |
} | |
if(!e) { | |
e = makeDefaultContainer() | |
} | |
shell.element = e | |
} else if(typeof element === "object" && !!element) { | |
shell.element = element | |
} else if(typeof element === "function") { | |
shell.element = element() | |
} else { | |
shell.element = makeDefaultContainer() | |
} | |
//Disable user-select | |
if(shell.element.style) { | |
shell.element.style["-webkit-touch-callout"] = "none" | |
shell.element.style["-webkit-user-select"] = "none" | |
shell.element.style["-khtml-user-select"] = "none" | |
shell.element.style["-moz-user-select"] = "none" | |
shell.element.style["-ms-user-select"] = "none" | |
shell.element.style["user-select"] = "none" | |
} | |
//Hook input listeners | |
window.addEventListener("keydown", handleKeyDown.bind(undefined, shell), true) | |
window.addEventListener("keyup", handleKeyUp.bind(undefined, shell), true) | |
window.addEventListener("mousedown", handleMouseDown.bind(undefined, shell), true) | |
window.addEventListener("mouseup", handleMouseUp.bind(undefined, shell), true) | |
window.addEventListener("mousemove", handleMouseMove.bind(undefined, shell), true) | |
window.addEventListener("mouseleave", handleMouseLeave.bind(undefined, shell), true) | |
window.addEventListener("mouseenter", handleMouseEnter.bind(undefined, shell), true) | |
window.addEventListener("blur", handleBlur.bind(undefined, shell), true) | |
//Initialize tick counter | |
shell._lastTick = Date.now() | |
shell._paused = false | |
shell.startTime = Date.now() | |
//Set up a tick interval | |
shell._tickInterval = setInterval(tick, shell._tickRate, shell) | |
//Create an animation frame handler | |
requestAnimationFrame(shell._render) | |
//Emit initialize event | |
shell.emit("init") | |
})}, 0) | |
return shell | |
} | |
module.exports = createShell | |
},{"events":2,"util":4,"./lib/raf-polyfill.js":3,"domready":5,"vkey":6,"invert-hash":7,"uniq":8,"lower-bound":9,"iota-array":10}],5:[function(require,module,exports){ | |
/*! | |
* domready (c) Dustin Diaz 2012 - License MIT | |
*/ | |
!function (name, definition) { | |
if (typeof module != 'undefined') module.exports = definition() | |
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition) | |
else this[name] = definition() | |
}('domready', function (ready) { | |
var fns = [], fn, f = false | |
, doc = document | |
, testEl = doc.documentElement | |
, hack = testEl.doScroll | |
, domContentLoaded = 'DOMContentLoaded' | |
, addEventListener = 'addEventListener' | |
, onreadystatechange = 'onreadystatechange' | |
, readyState = 'readyState' | |
, loaded = /^loade|c/.test(doc[readyState]) | |
function flush(f) { | |
loaded = 1 | |
while (f = fns.shift()) f() | |
} | |
doc[addEventListener] && doc[addEventListener](domContentLoaded, fn = function () { | |
doc.removeEventListener(domContentLoaded, fn, f) | |
flush() | |
}, f) | |
hack && doc.attachEvent(onreadystatechange, fn = function () { | |
if (/^c/.test(doc[readyState])) { | |
doc.detachEvent(onreadystatechange, fn) | |
flush() | |
} | |
}) | |
return (ready = hack ? | |
function (fn) { | |
self != top ? | |
loaded ? fn() : fns.push(fn) : | |
function () { | |
try { | |
testEl.doScroll('left') | |
} catch (e) { | |
return setTimeout(function() { ready(fn) }, 50) | |
} | |
fn() | |
}() | |
} : | |
function (fn) { | |
loaded ? fn() : fns.push(fn) | |
}) | |
}) | |
},{}],6:[function(require,module,exports){ | |
(function(){var ua = typeof window !== 'undefined' ? window.navigator.userAgent : '' | |
, isOSX = /OS X/.test(ua) | |
, isOpera = /Opera/.test(ua) | |
, maybeFirefox = !/like Gecko/.test(ua) && !isOpera | |
var i, output = module.exports = { | |
0: isOSX ? '<menu>' : '<UNK>' | |
, 1: '<mouse 1>' | |
, 2: '<mouse 2>' | |
, 3: '<break>' | |
, 4: '<mouse 3>' | |
, 5: '<mouse 4>' | |
, 6: '<mouse 5>' | |
, 8: '<backspace>' | |
, 9: '<tab>' | |
, 12: '<clear>' | |
, 13: '<enter>' | |
, 16: '<shift>' | |
, 17: '<control>' | |
, 18: '<alt>' | |
, 19: '<pause>' | |
, 20: '<caps-lock>' | |
, 21: '<ime-hangul>' | |
, 23: '<ime-junja>' | |
, 24: '<ime-final>' | |
, 25: '<ime-kanji>' | |
, 27: '<escape>' | |
, 28: '<ime-convert>' | |
, 29: '<ime-nonconvert>' | |
, 30: '<ime-accept>' | |
, 31: '<ime-mode-change>' | |
, 27: '<escape>' | |
, 32: '<space>' | |
, 33: '<page-up>' | |
, 34: '<page-down>' | |
, 35: '<end>' | |
, 36: '<home>' | |
, 37: '<left>' | |
, 38: '<up>' | |
, 39: '<right>' | |
, 40: '<down>' | |
, 41: '<select>' | |
, 42: '<print>' | |
, 43: '<execute>' | |
, 44: '<snapshot>' | |
, 45: '<insert>' | |
, 46: '<delete>' | |
, 47: '<help>' | |
, 91: '<meta>' // meta-left -- no one handles left and right properly, so we coerce into one. | |
, 92: '<meta>' // meta-right | |
, 93: isOSX ? '<meta>' : '<menu>' // chrome,opera,safari all report this for meta-right (osx mbp). | |
, 95: '<sleep>' | |
, 106: '<num-*>' | |
, 107: '<num-+>' | |
, 108: '<num-enter>' | |
, 109: '<num-->' | |
, 110: '<num-.>' | |
, 111: '<num-/>' | |
, 144: '<num-lock>' | |
, 145: '<scroll-lock>' | |
, 160: '<shift-left>' | |
, 161: '<shift-right>' | |
, 162: '<control-left>' | |
, 163: '<control-right>' | |
, 164: '<alt-left>' | |
, 165: '<alt-right>' | |
, 166: '<browser-back>' | |
, 167: '<browser-forward>' | |
, 168: '<browser-refresh>' | |
, 169: '<browser-stop>' | |
, 170: '<browser-search>' | |
, 171: '<browser-favorites>' | |
, 172: '<browser-home>' | |
// ff/osx reports '<volume-mute>' for '-' | |
, 173: isOSX && maybeFirefox ? '-' : '<volume-mute>' | |
, 174: '<volume-down>' | |
, 175: '<volume-up>' | |
, 176: '<next-track>' | |
, 177: '<prev-track>' | |
, 178: '<stop>' | |
, 179: '<play-pause>' | |
, 180: '<launch-mail>' | |
, 181: '<launch-media-select>' | |
, 182: '<launch-app 1>' | |
, 183: '<launch-app 2>' | |
, 186: ';' | |
, 187: '=' | |
, 188: ',' | |
, 189: '-' | |
, 190: '.' | |
, 191: '/' | |
, 192: '`' | |
, 219: '[' | |
, 220: '\\' | |
, 221: ']' | |
, 222: "'" | |
, 223: '<meta>' | |
, 224: '<meta>' // firefox reports meta here. | |
, 226: '<alt-gr>' | |
, 229: '<ime-process>' | |
, 231: isOpera ? '`' : '<unicode>' | |
, 246: '<attention>' | |
, 247: '<crsel>' | |
, 248: '<exsel>' | |
, 249: '<erase-eof>' | |
, 250: '<play>' | |
, 251: '<zoom>' | |
, 252: '<no-name>' | |
, 253: '<pa-1>' | |
, 254: '<clear>' | |
} | |
for(i = 58; i < 65; ++i) { | |
output[i] = String.fromCharCode(i) | |
} | |
// 0-9 | |
for(i = 48; i < 58; ++i) { | |
output[i] = (i - 48)+'' | |
} | |
// A-Z | |
for(i = 65; i < 91; ++i) { | |
output[i] = String.fromCharCode(i) | |
} | |
// num0-9 | |
for(i = 96; i < 107; ++i) { | |
output[i] = '<num-'+(i - 96)+'>' | |
} | |
// F1-F24 | |
for(i = 112; i < 136; ++i) { | |
output[i] = 'F'+(i-111) | |
} | |
})() | |
},{}],7:[function(require,module,exports){ | |
"use strict" | |
function invert(hash) { | |
var result = {} | |
for(var i in hash) { | |
if(hash.hasOwnProperty(i)) { | |
result[hash[i]] = i | |
} | |
} | |
return result | |
} | |
module.exports = invert | |
},{}],8:[function(require,module,exports){ | |
"use strict" | |
function unique_pred(list, compare) { | |
var ptr = 1 | |
, len = list.length | |
, a=list[0], b=list[0] | |
for(var i=1; i<len; ++i) { | |
b = a | |
a = list[i] | |
if(compare(a, b)) { | |
if(i === ptr) { | |
ptr++ | |
continue | |
} | |
list[ptr++] = a | |
} | |
} | |
list.length = ptr | |
return list | |
} | |
function unique_eq(list) { | |
var ptr = 1 | |
, len = list.length | |
, a=list[0], b = list[0] | |
for(var i=1; i<len; ++i, b=a) { | |
b = a | |
a = list[i] | |
if(a !== b) { | |
if(i === ptr) { | |
ptr++ | |
continue | |
} | |
list[ptr++] = a | |
} | |
} | |
list.length = ptr | |
return list | |
} | |
function unique(list, compare, sorted) { | |
if(list.length === 0) { | |
return [] | |
} | |
if(compare) { | |
if(!sorted) { | |
list.sort(compare) | |
} | |
return unique_pred(list, compare) | |
} | |
if(!sorted) { | |
list.sort() | |
} | |
return unique_eq(list) | |
} | |
module.exports = unique | |
},{}],9:[function(require,module,exports){ | |
"use strict" | |
function lowerBound_cmp(array, value, compare, lo, hi) { | |
lo = lo|0 | |
hi = hi|0 | |
while(lo < hi) { | |
var m = (lo + hi) >>> 1 | |
, v = compare(value, array[m]) | |
if(v < 0) { | |
hi = m-1 | |
} else if(v > 0) { | |
lo = m+1 | |
} else { | |
hi = m | |
} | |
} | |
if(compare(array[lo], value) <= 0) { | |
return lo | |
} | |
return lo - 1 | |
} | |
function lowerBound_def(array, value, lo, hi) { | |
lo = lo|0 | |
hi = hi|0 | |
while(lo < hi) { | |
var m = (lo + hi) >>> 1 | |
if(value < array[m]) { | |
hi = m-1 | |
} else if(value > array[m]) { | |
lo = m+1 | |
} else { | |
hi = m | |
} | |
} | |
if(array[lo] <= value) { | |
return lo | |
} | |
return lo - 1 | |
} | |
function lowerBound(array, value, compare, lo, hi) { | |
if(!lo) { | |
lo = 0 | |
} | |
if(typeof(hi) !== "number") { | |
hi = array.length-1 | |
} | |
if(compare) { | |
return lowerBound_cmp(array, value, compare, lo, hi) | |
} | |
return lowerBound_def(array, value, lo, hi) | |
} | |
module.exports = lowerBound | |
},{}],10:[function(require,module,exports){ | |
"use strict" | |
function iota(n) { | |
var result = new Array(n) | |
for(var i=0; i<n; ++i) { | |
result[i] = i | |
} | |
return result | |
} | |
module.exports = iota | |
},{}]},{},[]) | |
//@ sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlcyI6WyIvdXNyL2xvY2FsL2xpYi9ub2RlX21vZHVsZXMvYnJvd3NlcmlmeS9ub2RlX21vZHVsZXMvaW5zZXJ0LW1vZHVsZS1nbG9iYWxzL25vZGVfbW9kdWxlcy9wcm9jZXNzL2Jyb3dzZXIuanMiLCIvdXNyL2xvY2FsL2xpYi9ub2RlX21vZHVsZXMvYnJvd3NlcmlmeS9ub2RlX21vZHVsZXMvYnJvd3Nlci1idWlsdGlucy9idWlsdGluL2V2ZW50cy5qcyIsIi90bXAvZ2FtZS1zaGVsbDExMzY2LTExMjgxLW40eXIwOS9ub2RlX21vZHVsZXMvZ2FtZS1zaGVsbC9saWIvcmFmLXBvbHlmaWxsLmpzIiwiL3Vzci9sb2NhbC9saWIvbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItYnVpbHRpbnMvYnVpbHRpbi91dGlsLmpzIiwiL3RtcC9nYW1lLXNoZWxsMTEzNjYtMTEyODEtbjR5cjA5L25vZGVfbW9kdWxlcy9nYW1lLXNoZWxsL3NoZWxsLmpzIiwiL3RtcC9nYW1lLXNoZWxsMTEzNjYtMTEyODEtbjR5cjA5L25vZGVfbW9kdWxlcy9nYW1lLXNoZWxsL25vZGVfbW9kdWxlcy9kb21yZWFkeS9yZWFkeS5qcyIsIi90bXAvZ2FtZS1zaGVsbDExMzY2LTExMjgxLW40eXIwOS9ub2RlX21vZHVsZXMvZ2FtZS1zaGVsbC9ub2RlX21vZHVsZXMvdmtleS9pbmRleC5qcyIsIi90bXAvZ2FtZS1zaGVsbDExMzY2LTExMjgxLW40eXIwOS9ub2RlX21vZHVsZXMvZ2FtZS1zaGVsbC9ub2RlX21vZHVsZXMvaW52ZXJ0LWhhc2gvaW52ZXJ0LmpzIiwiL3RtcC9nYW1lLXNoZWxsMTEzNjYtMTEyODEtbjR5cjA5L25vZGVfbW9kdWxlcy9nYW1lLXNoZWxsL25vZGVfbW9kdWxlcy91bmlxL3VuaXEuanMiLCIvdG1wL2dhbWUtc2hlbGwxMTM2Ni0xMTI4MS1uNHlyMDkvbm9kZV9tb2R1bGVzL2dhbWUtc2hlbGwvbm9kZV9tb2R1bGVzL2xvd2VyLWJvdW5kL2xiLmpzIiwiL3RtcC9nYW1lLXNoZWxsMTEzNjYtMTEyODEtbjR5cjA5L25vZGVfbW9kdWxlcy9nYW1lLXNoZWxsL25vZGVfbW9kdWxlcy9pb3RhLWFycmF5L2lvdGEuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDL1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3REQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlc0NvbnRlbnQiOlsiLy8gc2hpbSBmb3IgdXNpbmcgcHJvY2VzcyBpbiBicm93c2VyXG5cbnZhciBwcm9jZXNzID0gbW9kdWxlLmV4cG9ydHMgPSB7fTtcblxucHJvY2Vzcy5uZXh0VGljayA9IChmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGNhblNldEltbWVkaWF0ZSA9IHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnXG4gICAgJiYgd2luZG93LnNldEltbWVkaWF0ZTtcbiAgICB2YXIgY2FuUG9zdCA9IHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnXG4gICAgJiYgd2luZG93LnBvc3RNZXNzYWdlICYmIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyXG4gICAgO1xuXG4gICAgaWYgKGNhblNldEltbWVkaWF0ZSkge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24gKGYpIHsgcmV0dXJuIHdpbmRvdy5zZXRJbW1lZGlhdGUoZikgfTtcbiAgICB9XG5cbiAgICBpZiAoY2FuUG9zdCkge1xuICAgICAgICB2YXIgcXVldWUgPSBbXTtcbiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBmdW5jdGlvbiAoZXYpIHtcbiAgICAgICAgICAgIGlmIChldi5zb3VyY2UgPT09IHdpbmRvdyAmJiBldi5kYXRhID09PSAncHJvY2Vzcy10aWNrJykge1xuICAgICAgICAgICAgICAgIGV2LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIGlmIChxdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBmbiA9IHF1ZXVlLnNoaWZ0KCk7XG4gICAgICAgICAgICAgICAgICAgIGZuKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LCB0cnVlKTtcblxuICAgICAgICByZXR1cm4gZnVuY3Rpb24gbmV4dFRpY2soZm4pIHtcbiAgICAgICAgICAgIHF1ZXVlLnB1c2goZm4pO1xuICAgICAgICAgICAgd2luZG93LnBvc3RNZXNzYWdlKCdwcm9jZXNzLXRpY2snLCAnKicpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiBmdW5jdGlvbiBuZXh0VGljayhmbikge1xuICAgICAgICBzZXRUaW1lb3V0KGZuLCAwKTtcbiAgICB9O1xufSkoKTtcblxucHJvY2Vzcy50aXRsZSA9ICdicm93c2VyJztcbnByb2Nlc3MuYnJvd3NlciA9IHRydWU7XG5wcm9jZXNzLmVudiA9IHt9O1xucHJvY2Vzcy5hcmd2ID0gW107XG5cbnByb2Nlc3MuYmluZGluZyA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmJpbmRpbmcgaXMgbm90IHN1cHBvcnRlZCcpO1xufVxuXG4vLyBUT0RPKHNodHlsbWFuKVxucHJvY2Vzcy5jd2QgPSBmdW5jdGlvbiAoKSB7IHJldHVybiAnLycgfTtcbnByb2Nlc3MuY2hkaXIgPSBmdW5jdGlvbiAoZGlyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmNoZGlyIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG4iLCIoZnVuY3Rpb24ocHJvY2Vzcyl7aWYgKCFwcm9jZXNzLkV2ZW50RW1pdHRlcikgcHJvY2Vzcy5FdmVudEVtaXR0ZXIgPSBmdW5jdGlvbiAoKSB7fTtcblxudmFyIEV2ZW50RW1pdHRlciA9IGV4cG9ydHMuRXZlbnRFbWl0dGVyID0gcHJvY2Vzcy5FdmVudEVtaXR0ZXI7XG52YXIgaXNBcnJheSA9IHR5cGVvZiBBcnJheS5pc0FycmF5ID09PSAnZnVuY3Rpb24nXG4gICAgPyBBcnJheS5pc0FycmF5XG4gICAgOiBmdW5jdGlvbiAoeHMpIHtcbiAgICAgICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbCh4cykgPT09ICdbb2JqZWN0IEFycmF5XSdcbiAgICB9XG47XG5mdW5jdGlvbiBpbmRleE9mICh4cywgeCkge1xuICAgIGlmICh4cy5pbmRleE9mKSByZXR1cm4geHMuaW5kZXhPZih4KTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHhzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmICh4ID09PSB4c1tpXSkgcmV0dXJuIGk7XG4gICAgfVxuICAgIHJldHVybiAtMTtcbn1cblxuLy8gQnkgZGVmYXVsdCBFdmVudEVtaXR0ZXJzIHdpbGwgcHJpbnQgYSB3YXJuaW5nIGlmIG1vcmUgdGhhblxuLy8gMTAgbGlzdGVuZXJzIGFyZSBhZGRlZCB0byBpdC4gVGhpcyBpcyBhIHVzZWZ1bCBkZWZhdWx0IHdoaWNoXG4vLyBoZWxwcyBmaW5kaW5nIG1lbW9yeSBsZWFrcy5cbi8vXG4vLyBPYnZpb3VzbHkgbm90IGFsbCBFbWl0dGVycyBzaG91bGQgYmUgbGltaXRlZCB0byAxMC4gVGhpcyBmdW5jdGlvbiBhbGxvd3Ncbi8vIHRoYXQgdG8gYmUgaW5jcmVhc2VkLiBTZXQgdG8gemVybyBmb3IgdW5saW1pdGVkLlxudmFyIGRlZmF1bHRNYXhMaXN0ZW5lcnMgPSAxMDtcbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuc2V0TWF4TGlzdGVuZXJzID0gZnVuY3Rpb24obikge1xuICBpZiAoIXRoaXMuX2V2ZW50cykgdGhpcy5fZXZlbnRzID0ge307XG4gIHRoaXMuX2V2ZW50cy5tYXhMaXN0ZW5lcnMgPSBuO1xufTtcblxuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmVtaXQgPSBmdW5jdGlvbih0eXBlKSB7XG4gIC8vIElmIHRoZXJlIGlzIG5vICdlcnJvcicgZXZlbnQgbGlzdGVuZXIgdGhlbiB0aHJvdy5cbiAgaWYgKHR5cGUgPT09ICdlcnJvcicpIHtcbiAgICBpZiAoIXRoaXMuX2V2ZW50cyB8fCAhdGhpcy5fZXZlbnRzLmVycm9yIHx8XG4gICAgICAgIChpc0FycmF5KHRoaXMuX2V2ZW50cy5lcnJvcikgJiYgIXRoaXMuX2V2ZW50cy5lcnJvci5sZW5ndGgpKVxuICAgIHtcbiAgICAgIGlmIChhcmd1bWVudHNbMV0gaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICB0aHJvdyBhcmd1bWVudHNbMV07IC8vIFVuaGFuZGxlZCAnZXJyb3InIGV2ZW50XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJVbmNhdWdodCwgdW5zcGVjaWZpZWQgJ2Vycm9yJyBldmVudC5cIik7XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMpIHJldHVybiBmYWxzZTtcbiAgdmFyIGhhbmRsZXIgPSB0aGlzLl9ldmVudHNbdHlwZV07XG4gIGlmICghaGFuZGxlcikgcmV0dXJuIGZhbHNlO1xuXG4gIGlmICh0eXBlb2YgaGFuZGxlciA9PSAnZnVuY3Rpb24nKSB7XG4gICAgc3dpdGNoIChhcmd1bWVudHMubGVuZ3RoKSB7XG4gICAgICAvLyBmYXN0IGNhc2VzXG4gICAgICBjYXNlIDE6XG4gICAgICAgIGhhbmRsZXIuY2FsbCh0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIGhhbmRsZXIuY2FsbCh0aGlzLCBhcmd1bWVudHNbMV0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMsIGFyZ3VtZW50c1sxXSwgYXJndW1lbnRzWzJdKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICAvLyBzbG93ZXJcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKTtcbiAgICAgICAgaGFuZGxlci5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG5cbiAgfSBlbHNlIGlmIChpc0FycmF5KGhhbmRsZXIpKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xuXG4gICAgdmFyIGxpc3RlbmVycyA9IGhhbmRsZXIuc2xpY2UoKTtcbiAgICBmb3IgKHZhciBpID0gMCwgbCA9IGxpc3RlbmVycy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgIGxpc3RlbmVyc1tpXS5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG5cbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn07XG5cbi8vIEV2ZW50RW1pdHRlciBpcyBkZWZpbmVkIGluIHNyYy9ub2RlX2V2ZW50cy5jY1xuLy8gRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5lbWl0KCkgaXMgYWxzbyBkZWZpbmVkIHRoZXJlLlxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIGlmICgnZnVuY3Rpb24nICE9PSB0eXBlb2YgbGlzdGVuZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2FkZExpc3RlbmVyIG9ubHkgdGFrZXMgaW5zdGFuY2VzIG9mIEZ1bmN0aW9uJyk7XG4gIH1cblxuICBpZiAoIXRoaXMuX2V2ZW50cykgdGhpcy5fZXZlbnRzID0ge307XG5cbiAgLy8gVG8gYXZvaWQgcmVjdXJzaW9uIGluIHRoZSBjYXNlIHRoYXQgdHlwZSA9PSBcIm5ld0xpc3RlbmVyc1wiISBCZWZvcmVcbiAgLy8gYWRkaW5nIGl0IHRvIHRoZSBsaXN0ZW5lcnMsIGZpcnN0IGVtaXQgXCJuZXdMaXN0ZW5lcnNcIi5cbiAgdGhpcy5lbWl0KCduZXdMaXN0ZW5lcicsIHR5cGUsIGxpc3RlbmVyKTtcblxuICBpZiAoIXRoaXMuX2V2ZW50c1t0eXBlXSkge1xuICAgIC8vIE9wdGltaXplIHRoZSBjYXNlIG9mIG9uZSBsaXN0ZW5lci4gRG9uJ3QgbmVlZCB0aGUgZXh0cmEgYXJyYXkgb2JqZWN0LlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IGxpc3RlbmVyO1xuICB9IGVsc2UgaWYgKGlzQXJyYXkodGhpcy5fZXZlbnRzW3R5cGVdKSkge1xuXG4gICAgLy8gQ2hlY2sgZm9yIGxpc3RlbmVyIGxlYWtcbiAgICBpZiAoIXRoaXMuX2V2ZW50c1t0eXBlXS53YXJuZWQpIHtcbiAgICAgIHZhciBtO1xuICAgICAgaWYgKHRoaXMuX2V2ZW50cy5tYXhMaXN0ZW5lcnMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBtID0gdGhpcy5fZXZlbnRzLm1heExpc3RlbmVycztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG0gPSBkZWZhdWx0TWF4TGlzdGVuZXJzO1xuICAgICAgfVxuXG4gICAgICBpZiAobSAmJiBtID4gMCAmJiB0aGlzLl9ldmVudHNbdHlwZV0ubGVuZ3RoID4gbSkge1xuICAgICAgICB0aGlzLl9ldmVudHNbdHlwZV0ud2FybmVkID0gdHJ1ZTtcbiAgICAgICAgY29uc29sZS5lcnJvcignKG5vZGUpIHdhcm5pbmc6IHBvc3NpYmxlIEV2ZW50RW1pdHRlciBtZW1vcnkgJyArXG4gICAgICAgICAgICAgICAgICAgICAgJ2xlYWsgZGV0ZWN0ZWQuICVkIGxpc3RlbmVycyBhZGRlZC4gJyArXG4gICAgICAgICAgICAgICAgICAgICAgJ1VzZSBlbWl0dGVyLnNldE1heExpc3RlbmVycygpIHRvIGluY3JlYXNlIGxpbWl0LicsXG4gICAgICAgICAgICAgICAgICAgICAgdGhpcy5fZXZlbnRzW3R5cGVdLmxlbmd0aCk7XG4gICAgICAgIGNvbnNvbGUudHJhY2UoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiB3ZSd2ZSBhbHJlYWR5IGdvdCBhbiBhcnJheSwganVzdCBhcHBlbmQuXG4gICAgdGhpcy5fZXZlbnRzW3R5cGVdLnB1c2gobGlzdGVuZXIpO1xuICB9IGVsc2Uge1xuICAgIC8vIEFkZGluZyB0aGUgc2Vjb25kIGVsZW1lbnQsIG5lZWQgdG8gY2hhbmdlIHRvIGFycmF5LlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IFt0aGlzLl9ldmVudHNbdHlwZV0sIGxpc3RlbmVyXTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbiA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUuYWRkTGlzdGVuZXI7XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub25jZSA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgc2VsZi5vbih0eXBlLCBmdW5jdGlvbiBnKCkge1xuICAgIHNlbGYucmVtb3ZlTGlzdGVuZXIodHlwZSwgZyk7XG4gICAgbGlzdGVuZXIuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgfSk7XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUxpc3RlbmVyID0gZnVuY3Rpb24odHlwZSwgbGlzdGVuZXIpIHtcbiAgaWYgKCdmdW5jdGlvbicgIT09IHR5cGVvZiBsaXN0ZW5lcikge1xuICAgIHRocm93IG5ldyBFcnJvcigncmVtb3ZlTGlzdGVuZXIgb25seSB0YWtlcyBpbnN0YW5jZXMgb2YgRnVuY3Rpb24nKTtcbiAgfVxuXG4gIC8vIGRvZXMgbm90IHVzZSBsaXN0ZW5lcnMoKSwgc28gbm8gc2lkZSBlZmZlY3Qgb2YgY3JlYXRpbmcgX2V2ZW50c1t0eXBlXVxuICBpZiAoIXRoaXMuX2V2ZW50cyB8fCAhdGhpcy5fZXZlbnRzW3R5cGVdKSByZXR1cm4gdGhpcztcblxuICB2YXIgbGlzdCA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcblxuICBpZiAoaXNBcnJheShsaXN0KSkge1xuICAgIHZhciBpID0gaW5kZXhPZihsaXN0LCBsaXN0ZW5lcik7XG4gICAgaWYgKGkgPCAwKSByZXR1cm4gdGhpcztcbiAgICBsaXN0LnNwbGljZShpLCAxKTtcbiAgICBpZiAobGlzdC5sZW5ndGggPT0gMClcbiAgICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gIH0gZWxzZSBpZiAodGhpcy5fZXZlbnRzW3R5cGVdID09PSBsaXN0ZW5lcikge1xuICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlQWxsTGlzdGVuZXJzID0gZnVuY3Rpb24odHlwZSkge1xuICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLy8gZG9lcyBub3QgdXNlIGxpc3RlbmVycygpLCBzbyBubyBzaWRlIGVmZmVjdCBvZiBjcmVhdGluZyBfZXZlbnRzW3R5cGVdXG4gIGlmICh0eXBlICYmIHRoaXMuX2V2ZW50cyAmJiB0aGlzLl9ldmVudHNbdHlwZV0pIHRoaXMuX2V2ZW50c1t0eXBlXSA9IG51bGw7XG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbih0eXBlKSB7XG4gIGlmICghdGhpcy5fZXZlbnRzKSB0aGlzLl9ldmVudHMgPSB7fTtcbiAgaWYgKCF0aGlzLl9ldmVudHNbdHlwZV0pIHRoaXMuX2V2ZW50c1t0eXBlXSA9IFtdO1xuICBpZiAoIWlzQXJyYXkodGhpcy5fZXZlbnRzW3R5cGVdKSkge1xuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IFt0aGlzLl9ldmVudHNbdHlwZV1dO1xuICB9XG4gIHJldHVybiB0aGlzLl9ldmVudHNbdHlwZV07XG59O1xuXG59KShyZXF1aXJlKFwiX19icm93c2VyaWZ5X3Byb2Nlc3NcIikpIiwiLy8gaHR0cDovL3BhdWxpcmlzaC5jb20vMjAxMS9yZXF1ZXN0YW5pbWF0aW9uZnJhbWUtZm9yLXNtYXJ0LWFuaW1hdGluZy9cbi8vIGh0dHA6Ly9teS5vcGVyYS5jb20vZW1vbGxlci9ibG9nLzIwMTEvMTIvMjAvcmVxdWVzdGFuaW1hdGlvbmZyYW1lLWZvci1zbWFydC1lci1hbmltYXRpbmdcbiBcbi8vIHJlcXVlc3RBbmltYXRpb25GcmFtZSBwb2x5ZmlsbCBieSBFcmlrIE3DtmxsZXIuIGZpeGVzIGZyb20gUGF1bCBJcmlzaCBhbmQgVGlubyBaaWpkZWxcbiBcbi8vIE1JVCBsaWNlbnNlXG52YXIgbGFzdFRpbWUgPSAwO1xudmFyIHZlbmRvcnMgPSBbJ21zJywgJ21veicsICd3ZWJraXQnLCAnbyddO1xuZm9yKHZhciB4ID0gMDsgeCA8IHZlbmRvcnMubGVuZ3RoICYmICF3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lOyArK3gpIHtcbiAgICB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lID0gd2luZG93W3ZlbmRvcnNbeF0rJ1JlcXVlc3RBbmltYXRpb25GcmFtZSddO1xuICAgIHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSA9IHdpbmRvd1t2ZW5kb3JzW3hdKydDYW5jZWxBbmltYXRpb25GcmFtZSddIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IHdpbmRvd1t2ZW5kb3JzW3hdKydDYW5jZWxSZXF1ZXN0QW5pbWF0aW9uRnJhbWUnXTtcbn1cblxuaWYgKCF3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lKVxuICAgIHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbihjYWxsYmFjaywgZWxlbWVudCkge1xuICAgICAgICB2YXIgY3VyclRpbWUgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcbiAgICAgICAgdmFyIHRpbWVUb0NhbGwgPSBNYXRoLm1heCgwLCAxNiAtIChjdXJyVGltZSAtIGxhc3RUaW1lKSk7XG4gICAgICAgIHZhciBpZCA9IHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBjYWxsYmFjayhjdXJyVGltZSArIHRpbWVUb0NhbGwpOyB9LCBcbiAgICAgICAgICB0aW1lVG9DYWxsKTtcbiAgICAgICAgbGFzdFRpbWUgPSBjdXJyVGltZSArIHRpbWVUb0NhbGw7XG4gICAgICAgIHJldHVybiBpZDtcbiAgICB9O1xuXG5pZiAoIXdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSlcbiAgICB3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbihpZCkge1xuICAgICAgICBjbGVhclRpbWVvdXQoaWQpO1xuICAgIH07XG4iLCJ2YXIgZXZlbnRzID0gcmVxdWlyZSgnZXZlbnRzJyk7XG5cbmV4cG9ydHMuaXNBcnJheSA9IGlzQXJyYXk7XG5leHBvcnRzLmlzRGF0ZSA9IGZ1bmN0aW9uKG9iail7cmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopID09PSAnW29iamVjdCBEYXRlXSd9O1xuZXhwb3J0cy5pc1JlZ0V4cCA9IGZ1bmN0aW9uKG9iail7cmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopID09PSAnW29iamVjdCBSZWdFeHBdJ307XG5cblxuZXhwb3J0cy5wcmludCA9IGZ1bmN0aW9uICgpIHt9O1xuZXhwb3J0cy5wdXRzID0gZnVuY3Rpb24gKCkge307XG5leHBvcnRzLmRlYnVnID0gZnVuY3Rpb24oKSB7fTtcblxuZXhwb3J0cy5pbnNwZWN0ID0gZnVuY3Rpb24ob2JqLCBzaG93SGlkZGVuLCBkZXB0aCwgY29sb3JzKSB7XG4gIHZhciBzZWVuID0gW107XG5cbiAgdmFyIHN0eWxpemUgPSBmdW5jdGlvbihzdHIsIHN0eWxlVHlwZSkge1xuICAgIC8vIGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQU5TSV9lc2NhcGVfY29kZSNncmFwaGljc1xuICAgIHZhciBzdHlsZXMgPVxuICAgICAgICB7ICdib2xkJyA6IFsxLCAyMl0sXG4gICAgICAgICAgJ2l0YWxpYycgOiBbMywgMjNdLFxuICAgICAgICAgICd1bmRlcmxpbmUnIDogWzQsIDI0XSxcbiAgICAgICAgICAnaW52ZXJzZScgOiBbNywgMjddLFxuICAgICAgICAgICd3aGl0ZScgOiBbMzcsIDM5XSxcbiAgICAgICAgICAnZ3JleScgOiBbOTAsIDM5XSxcbiAgICAgICAgICAnYmxhY2snIDogWzMwLCAzOV0sXG4gICAgICAgICAgJ2JsdWUnIDogWzM0LCAzOV0sXG4gICAgICAgICAgJ2N5YW4nIDogWzM2LCAzOV0sXG4gICAgICAgICAgJ2dyZWVuJyA6IFszMiwgMzldLFxuICAgICAgICAgICdtYWdlbnRhJyA6IFszNSwgMzldLFxuICAgICAgICAgICdyZWQnIDogWzMxLCAzOV0sXG4gICAgICAgICAgJ3llbGxvdycgOiBbMzMsIDM5XSB9O1xuXG4gICAgdmFyIHN0eWxlID1cbiAgICAgICAgeyAnc3BlY2lhbCc6ICdjeWFuJyxcbiAgICAgICAgICAnbnVtYmVyJzogJ2JsdWUnLFxuICAgICAgICAgICdib29sZWFuJzogJ3llbGxvdycsXG4gICAgICAgICAgJ3VuZGVmaW5lZCc6ICdncmV5JyxcbiAgICAgICAgICAnbnVsbCc6ICdib2xkJyxcbiAgICAgICAgICAnc3RyaW5nJzogJ2dyZWVuJyxcbiAgICAgICAgICAnZGF0ZSc6ICdtYWdlbnRhJyxcbiAgICAgICAgICAvLyBcIm5hbWVcIjogaW50ZW50aW9uYWxseSBub3Qgc3R5bGluZ1xuICAgICAgICAgICdyZWdleHAnOiAncmVkJyB9W3N0eWxlVHlwZV07XG5cbiAgICBpZiAoc3R5bGUpIHtcbiAgICAgIHJldHVybiAnXFwwMzNbJyArIHN0eWxlc1tzdHlsZV1bMF0gKyAnbScgKyBzdHIgK1xuICAgICAgICAgICAgICdcXDAzM1snICsgc3R5bGVzW3N0eWxlXVsxXSArICdtJztcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHN0cjtcbiAgICB9XG4gIH07XG4gIGlmICghIGNvbG9ycykge1xuICAgIHN0eWxpemUgPSBmdW5jdGlvbihzdHIsIHN0eWxlVHlwZSkgeyByZXR1cm4gc3RyOyB9O1xuICB9XG5cbiAgZnVuY3Rpb24gZm9ybWF0KHZhbHVlLCByZWN1cnNlVGltZXMpIHtcbiAgICAvLyBQcm92aWRlIGEgaG9vayBmb3IgdXNlci1zcGVjaWZpZWQgaW5zcGVjdCBmdW5jdGlvbnMuXG4gICAgLy8gQ2hlY2sgdGhhdCB2YWx1ZSBpcyBhbiBvYmplY3Qgd2l0aCBhbiBpbnNwZWN0IGZ1bmN0aW9uIG9uIGl0XG4gICAgaWYgKHZhbHVlICYmIHR5cGVvZiB2YWx1ZS5pbnNwZWN0ID09PSAnZnVuY3Rpb24nICYmXG4gICAgICAgIC8vIEZpbHRlciBvdXQgdGhlIHV0aWwgbW9kdWxlLCBpdCdzIGluc3BlY3QgZnVuY3Rpb24gaXMgc3BlY2lhbFxuICAgICAgICB2YWx1ZSAhPT0gZXhwb3J0cyAmJlxuICAgICAgICAvLyBBbHNvIGZpbHRlciBvdXQgYW55IHByb3RvdHlwZSBvYmplY3RzIHVzaW5nIHRoZSBjaXJjdWxhciBjaGVjay5cbiAgICAgICAgISh2YWx1ZS5jb25zdHJ1Y3RvciAmJiB2YWx1ZS5jb25zdHJ1Y3Rvci5wcm90b3R5cGUgPT09IHZhbHVlKSkge1xuICAgICAgcmV0dXJuIHZhbHVlLmluc3BlY3QocmVjdXJzZVRpbWVzKTtcbiAgICB9XG5cbiAgICAvLyBQcmltaXRpdmUgdHlwZXMgY2Fubm90IGhhdmUgcHJvcGVydGllc1xuICAgIHN3aXRjaCAodHlwZW9mIHZhbHVlKSB7XG4gICAgICBjYXNlICd1bmRlZmluZWQnOlxuICAgICAgICByZXR1cm4gc3R5bGl6ZSgndW5kZWZpbmVkJywgJ3VuZGVmaW5lZCcpO1xuXG4gICAgICBjYXNlICdzdHJpbmcnOlxuICAgICAgICB2YXIgc2ltcGxlID0gJ1xcJycgKyBKU09OLnN0cmluZ2lmeSh2YWx1ZSkucmVwbGFjZSgvXlwifFwiJC9nLCAnJylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCBcIlxcXFwnXCIpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoL1xcXFxcIi9nLCAnXCInKSArICdcXCcnO1xuICAgICAgICByZXR1cm4gc3R5bGl6ZShzaW1wbGUsICdzdHJpbmcnKTtcblxuICAgICAgY2FzZSAnbnVtYmVyJzpcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJycgKyB2YWx1ZSwgJ251bWJlcicpO1xuXG4gICAgICBjYXNlICdib29sZWFuJzpcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJycgKyB2YWx1ZSwgJ2Jvb2xlYW4nKTtcbiAgICB9XG4gICAgLy8gRm9yIHNvbWUgcmVhc29uIHR5cGVvZiBudWxsIGlzIFwib2JqZWN0XCIsIHNvIHNwZWNpYWwgY2FzZSBoZXJlLlxuICAgIGlmICh2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHN0eWxpemUoJ251bGwnLCAnbnVsbCcpO1xuICAgIH1cblxuICAgIC8vIExvb2sgdXAgdGhlIGtleXMgb2YgdGhlIG9iamVjdC5cbiAgICB2YXIgdmlzaWJsZV9rZXlzID0gT2JqZWN0X2tleXModmFsdWUpO1xuICAgIHZhciBrZXlzID0gc2hvd0hpZGRlbiA/IE9iamVjdF9nZXRPd25Qcm9wZXJ0eU5hbWVzKHZhbHVlKSA6IHZpc2libGVfa2V5cztcblxuICAgIC8vIEZ1bmN0aW9ucyB3aXRob3V0IHByb3BlcnRpZXMgY2FuIGJlIHNob3J0Y3V0dGVkLlxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgJiYga2V5cy5sZW5ndGggPT09IDApIHtcbiAgICAgIGlmIChpc1JlZ0V4cCh2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJycgKyB2YWx1ZSwgJ3JlZ2V4cCcpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIG5hbWUgPSB2YWx1ZS5uYW1lID8gJzogJyArIHZhbHVlLm5hbWUgOiAnJztcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJ1tGdW5jdGlvbicgKyBuYW1lICsgJ10nLCAnc3BlY2lhbCcpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIERhdGVzIHdpdGhvdXQgcHJvcGVydGllcyBjYW4gYmUgc2hvcnRjdXR0ZWRcbiAgICBpZiAoaXNEYXRlKHZhbHVlKSAmJiBrZXlzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHN0eWxpemUodmFsdWUudG9VVENTdHJpbmcoKSwgJ2RhdGUnKTtcbiAgICB9XG5cbiAgICB2YXIgYmFzZSwgdHlwZSwgYnJhY2VzO1xuICAgIC8vIERldGVybWluZSB0aGUgb2JqZWN0IHR5cGVcbiAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgIHR5cGUgPSAnQXJyYXknO1xuICAgICAgYnJhY2VzID0gWydbJywgJ10nXTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHlwZSA9ICdPYmplY3QnO1xuICAgICAgYnJhY2VzID0gWyd7JywgJ30nXTtcbiAgICB9XG5cbiAgICAvLyBNYWtlIGZ1bmN0aW9ucyBzYXkgdGhhdCB0aGV5IGFyZSBmdW5jdGlvbnNcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICB2YXIgbiA9IHZhbHVlLm5hbWUgPyAnOiAnICsgdmFsdWUubmFtZSA6ICcnO1xuICAgICAgYmFzZSA9IChpc1JlZ0V4cCh2YWx1ZSkpID8gJyAnICsgdmFsdWUgOiAnIFtGdW5jdGlvbicgKyBuICsgJ10nO1xuICAgIH0gZWxzZSB7XG4gICAgICBiYXNlID0gJyc7XG4gICAgfVxuXG4gICAgLy8gTWFrZSBkYXRlcyB3aXRoIHByb3BlcnRpZXMgZmlyc3Qgc2F5IHRoZSBkYXRlXG4gICAgaWYgKGlzRGF0ZSh2YWx1ZSkpIHtcbiAgICAgIGJhc2UgPSAnICcgKyB2YWx1ZS50b1VUQ1N0cmluZygpO1xuICAgIH1cblxuICAgIGlmIChrZXlzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIGJyYWNlc1swXSArIGJhc2UgKyBicmFjZXNbMV07XG4gICAgfVxuXG4gICAgaWYgKHJlY3Vyc2VUaW1lcyA8IDApIHtcbiAgICAgIGlmIChpc1JlZ0V4cCh2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJycgKyB2YWx1ZSwgJ3JlZ2V4cCcpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHN0eWxpemUoJ1tPYmplY3RdJywgJ3NwZWNpYWwnKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzZWVuLnB1c2godmFsdWUpO1xuXG4gICAgdmFyIG91dHB1dCA9IGtleXMubWFwKGZ1bmN0aW9uKGtleSkge1xuICAgICAgdmFyIG5hbWUsIHN0cjtcbiAgICAgIGlmICh2YWx1ZS5fX2xvb2t1cEdldHRlcl9fKSB7XG4gICAgICAgIGlmICh2YWx1ZS5fX2xvb2t1cEdldHRlcl9fKGtleSkpIHtcbiAgICAgICAgICBpZiAodmFsdWUuX19sb29rdXBTZXR0ZXJfXyhrZXkpKSB7XG4gICAgICAgICAgICBzdHIgPSBzdHlsaXplKCdbR2V0dGVyL1NldHRlcl0nLCAnc3BlY2lhbCcpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzdHIgPSBzdHlsaXplKCdbR2V0dGVyXScsICdzcGVjaWFsJyk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmICh2YWx1ZS5fX2xvb2t1cFNldHRlcl9fKGtleSkpIHtcbiAgICAgICAgICAgIHN0ciA9IHN0eWxpemUoJ1tTZXR0ZXJdJywgJ3NwZWNpYWwnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh2aXNpYmxlX2tleXMuaW5kZXhPZihrZXkpIDwgMCkge1xuICAgICAgICBuYW1lID0gJ1snICsga2V5ICsgJ10nO1xuICAgICAgfVxuICAgICAgaWYgKCFzdHIpIHtcbiAgICAgICAgaWYgKHNlZW4uaW5kZXhPZih2YWx1ZVtrZXldKSA8IDApIHtcbiAgICAgICAgICBpZiAocmVjdXJzZVRpbWVzID09PSBudWxsKSB7XG4gICAgICAgICAgICBzdHIgPSBmb3JtYXQodmFsdWVba2V5XSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHN0ciA9IGZvcm1hdCh2YWx1ZVtrZXldLCByZWN1cnNlVGltZXMgLSAxKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHN0ci5pbmRleE9mKCdcXG4nKSA+IC0xKSB7XG4gICAgICAgICAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgc3RyID0gc3RyLnNwbGl0KCdcXG4nKS5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiAnICAnICsgbGluZTtcbiAgICAgICAgICAgICAgfSkuam9pbignXFxuJykuc3Vic3RyKDIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc3RyID0gJ1xcbicgKyBzdHIuc3BsaXQoJ1xcbicpLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcgICAnICsgbGluZTtcbiAgICAgICAgICAgICAgfSkuam9pbignXFxuJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHN0ciA9IHN0eWxpemUoJ1tDaXJjdWxhcl0nLCAnc3BlY2lhbCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIG5hbWUgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIGlmICh0eXBlID09PSAnQXJyYXknICYmIGtleS5tYXRjaCgvXlxcZCskLykpIHtcbiAgICAgICAgICByZXR1cm4gc3RyO1xuICAgICAgICB9XG4gICAgICAgIG5hbWUgPSBKU09OLnN0cmluZ2lmeSgnJyArIGtleSk7XG4gICAgICAgIGlmIChuYW1lLm1hdGNoKC9eXCIoW2EtekEtWl9dW2EtekEtWl8wLTldKilcIiQvKSkge1xuICAgICAgICAgIG5hbWUgPSBuYW1lLnN1YnN0cigxLCBuYW1lLmxlbmd0aCAtIDIpO1xuICAgICAgICAgIG5hbWUgPSBzdHlsaXplKG5hbWUsICduYW1lJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbmFtZSA9IG5hbWUucmVwbGFjZSgvJy9nLCBcIlxcXFwnXCIpXG4gICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgvXFxcXFwiL2csICdcIicpXG4gICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgvKF5cInxcIiQpL2csIFwiJ1wiKTtcbiAgICAgICAgICBuYW1lID0gc3R5bGl6ZShuYW1lLCAnc3RyaW5nJyk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG5hbWUgKyAnOiAnICsgc3RyO1xuICAgIH0pO1xuXG4gICAgc2Vlbi5wb3AoKTtcblxuICAgIHZhciBudW1MaW5lc0VzdCA9IDA7XG4gICAgdmFyIGxlbmd0aCA9IG91dHB1dC5yZWR1Y2UoZnVuY3Rpb24ocHJldiwgY3VyKSB7XG4gICAgICBudW1MaW5lc0VzdCsrO1xuICAgICAgaWYgKGN1ci5pbmRleE9mKCdcXG4nKSA+PSAwKSBudW1MaW5lc0VzdCsrO1xuICAgICAgcmV0dXJuIHByZXYgKyBjdXIubGVuZ3RoICsgMTtcbiAgICB9LCAwKTtcblxuICAgIGlmIChsZW5ndGggPiA1MCkge1xuICAgICAgb3V0cHV0ID0gYnJhY2VzWzBdICtcbiAgICAgICAgICAgICAgIChiYXNlID09PSAnJyA/ICcnIDogYmFzZSArICdcXG4gJykgK1xuICAgICAgICAgICAgICAgJyAnICtcbiAgICAgICAgICAgICAgIG91dHB1dC5qb2luKCcsXFxuICAnKSArXG4gICAgICAgICAgICAgICAnICcgK1xuICAgICAgICAgICAgICAgYnJhY2VzWzFdO1xuXG4gICAgfSBlbHNlIHtcbiAgICAgIG91dHB1dCA9IGJyYWNlc1swXSArIGJhc2UgKyAnICcgKyBvdXRwdXQuam9pbignLCAnKSArICcgJyArIGJyYWNlc1sxXTtcbiAgICB9XG5cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG4gIHJldHVybiBmb3JtYXQob2JqLCAodHlwZW9mIGRlcHRoID09PSAndW5kZWZpbmVkJyA/IDIgOiBkZXB0aCkpO1xufTtcblxuXG5mdW5jdGlvbiBpc0FycmF5KGFyKSB7XG4gIHJldHVybiBhciBpbnN0YW5jZW9mIEFycmF5IHx8XG4gICAgICAgICBBcnJheS5pc0FycmF5KGFyKSB8fFxuICAgICAgICAgKGFyICYmIGFyICE9PSBPYmplY3QucHJvdG90eXBlICYmIGlzQXJyYXkoYXIuX19wcm90b19fKSk7XG59XG5cblxuZnVuY3Rpb24gaXNSZWdFeHAocmUpIHtcbiAgcmV0dXJuIHJlIGluc3RhbmNlb2YgUmVnRXhwIHx8XG4gICAgKHR5cGVvZiByZSA9PT0gJ29iamVjdCcgJiYgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHJlKSA9PT0gJ1tvYmplY3QgUmVnRXhwXScpO1xufVxuXG5cbmZ1bmN0aW9uIGlzRGF0ZShkKSB7XG4gIGlmIChkIGluc3RhbmNlb2YgRGF0ZSkgcmV0dXJuIHRydWU7XG4gIGlmICh0eXBlb2YgZCAhPT0gJ29iamVjdCcpIHJldHVybiBmYWxzZTtcbiAgdmFyIHByb3BlcnRpZXMgPSBEYXRlLnByb3RvdHlwZSAmJiBPYmplY3RfZ2V0T3duUHJvcGVydHlOYW1lcyhEYXRlLnByb3RvdHlwZSk7XG4gIHZhciBwcm90byA9IGQuX19wcm90b19fICYmIE9iamVjdF9nZXRPd25Qcm9wZXJ0eU5hbWVzKGQuX19wcm90b19fKTtcbiAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHByb3RvKSA9PT0gSlNPTi5zdHJpbmdpZnkocHJvcGVydGllcyk7XG59XG5cbmZ1bmN0aW9uIHBhZChuKSB7XG4gIHJldHVybiBuIDwgMTAgPyAnMCcgKyBuLnRvU3RyaW5nKDEwKSA6IG4udG9TdHJpbmcoMTApO1xufVxuXG52YXIgbW9udGhzID0gWydKYW4nLCAnRmViJywgJ01hcicsICdBcHInLCAnTWF5JywgJ0p1bicsICdKdWwnLCAnQXVnJywgJ1NlcCcsXG4gICAgICAgICAgICAgICdPY3QnLCAnTm92JywgJ0RlYyddO1xuXG4vLyAyNiBGZWIgMTY6MTk6MzRcbmZ1bmN0aW9uIHRpbWVzdGFtcCgpIHtcbiAgdmFyIGQgPSBuZXcgRGF0ZSgpO1xuICB2YXIgdGltZSA9IFtwYWQoZC5nZXRIb3VycygpKSxcbiAgICAgICAgICAgICAgcGFkKGQuZ2V0TWludXRlcygpKSxcbiAgICAgICAgICAgICAgcGFkKGQuZ2V0U2Vjb25kcygpKV0uam9pbignOicpO1xuICByZXR1cm4gW2QuZ2V0RGF0ZSgpLCBtb250aHNbZC5nZXRNb250aCgpXSwgdGltZV0uam9pbignICcpO1xufVxuXG5leHBvcnRzLmxvZyA9IGZ1bmN0aW9uIChtc2cpIHt9O1xuXG5leHBvcnRzLnB1bXAgPSBudWxsO1xuXG52YXIgT2JqZWN0X2tleXMgPSBPYmplY3Qua2V5cyB8fCBmdW5jdGlvbiAob2JqKSB7XG4gICAgdmFyIHJlcyA9IFtdO1xuICAgIGZvciAodmFyIGtleSBpbiBvYmopIHJlcy5wdXNoKGtleSk7XG4gICAgcmV0dXJuIHJlcztcbn07XG5cbnZhciBPYmplY3RfZ2V0T3duUHJvcGVydHlOYW1lcyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzIHx8IGZ1bmN0aW9uIChvYmopIHtcbiAgICB2YXIgcmVzID0gW107XG4gICAgZm9yICh2YXIga2V5IGluIG9iaikge1xuICAgICAgICBpZiAoT2JqZWN0Lmhhc093blByb3BlcnR5LmNhbGwob2JqLCBrZXkpKSByZXMucHVzaChrZXkpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzO1xufTtcblxudmFyIE9iamVjdF9jcmVhdGUgPSBPYmplY3QuY3JlYXRlIHx8IGZ1bmN0aW9uIChwcm90b3R5cGUsIHByb3BlcnRpZXMpIHtcbiAgICAvLyBmcm9tIGVzNS1zaGltXG4gICAgdmFyIG9iamVjdDtcbiAgICBpZiAocHJvdG90eXBlID09PSBudWxsKSB7XG4gICAgICAgIG9iamVjdCA9IHsgJ19fcHJvdG9fXycgOiBudWxsIH07XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBpZiAodHlwZW9mIHByb3RvdHlwZSAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXG4gICAgICAgICAgICAgICAgJ3R5cGVvZiBwcm90b3R5cGVbJyArICh0eXBlb2YgcHJvdG90eXBlKSArICddICE9IFxcJ29iamVjdFxcJydcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIFR5cGUgPSBmdW5jdGlvbiAoKSB7fTtcbiAgICAgICAgVHlwZS5wcm90b3R5cGUgPSBwcm90b3R5cGU7XG4gICAgICAgIG9iamVjdCA9IG5ldyBUeXBlKCk7XG4gICAgICAgIG9iamVjdC5fX3Byb3RvX18gPSBwcm90b3R5cGU7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgcHJvcGVydGllcyAhPT0gJ3VuZGVmaW5lZCcgJiYgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMpIHtcbiAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMob2JqZWN0LCBwcm9wZXJ0aWVzKTtcbiAgICB9XG4gICAgcmV0dXJuIG9iamVjdDtcbn07XG5cbmV4cG9ydHMuaW5oZXJpdHMgPSBmdW5jdGlvbihjdG9yLCBzdXBlckN0b3IpIHtcbiAgY3Rvci5zdXBlcl8gPSBzdXBlckN0b3I7XG4gIGN0b3IucHJvdG90eXBlID0gT2JqZWN0X2NyZWF0ZShzdXBlckN0b3IucHJvdG90eXBlLCB7XG4gICAgY29uc3RydWN0b3I6IHtcbiAgICAgIHZhbHVlOiBjdG9yLFxuICAgICAgZW51bWVyYWJsZTogZmFsc2UsXG4gICAgICB3cml0YWJsZTogdHJ1ZSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH1cbiAgfSk7XG59O1xuXG52YXIgZm9ybWF0UmVnRXhwID0gLyVbc2RqJV0vZztcbmV4cG9ydHMuZm9ybWF0ID0gZnVuY3Rpb24oZikge1xuICBpZiAodHlwZW9mIGYgIT09ICdzdHJpbmcnKSB7XG4gICAgdmFyIG9iamVjdHMgPSBbXTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgb2JqZWN0cy5wdXNoKGV4cG9ydHMuaW5zcGVjdChhcmd1bWVudHNbaV0pKTtcbiAgICB9XG4gICAgcmV0dXJuIG9iamVjdHMuam9pbignICcpO1xuICB9XG5cbiAgdmFyIGkgPSAxO1xuICB2YXIgYXJncyA9IGFyZ3VtZW50cztcbiAgdmFyIGxlbiA9IGFyZ3MubGVuZ3RoO1xuICB2YXIgc3RyID0gU3RyaW5nKGYpLnJlcGxhY2UoZm9ybWF0UmVnRXhwLCBmdW5jdGlvbih4KSB7XG4gICAgaWYgKHggPT09ICclJScpIHJldHVybiAnJSc7XG4gICAgaWYgKGkgPj0gbGVuKSByZXR1cm4geDtcbiAgICBzd2l0Y2ggKHgpIHtcbiAgICAgIGNhc2UgJyVzJzogcmV0dXJuIFN0cmluZyhhcmdzW2krK10pO1xuICAgICAgY2FzZSAnJWQnOiByZXR1cm4gTnVtYmVyKGFyZ3NbaSsrXSk7XG4gICAgICBjYXNlICclaic6IHJldHVybiBKU09OLnN0cmluZ2lmeShhcmdzW2krK10pO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIHg7XG4gICAgfVxuICB9KTtcbiAgZm9yKHZhciB4ID0gYXJnc1tpXTsgaSA8IGxlbjsgeCA9IGFyZ3NbKytpXSl7XG4gICAgaWYgKHggPT09IG51bGwgfHwgdHlwZW9mIHggIT09ICdvYmplY3QnKSB7XG4gICAgICBzdHIgKz0gJyAnICsgeDtcbiAgICB9IGVsc2Uge1xuICAgICAgc3RyICs9ICcgJyArIGV4cG9ydHMuaW5zcGVjdCh4KTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHN0cjtcbn07XG4iLCJcInVzZSBzdHJpY3RcIlxuXG52YXIgRXZlbnRFbWl0dGVyID0gcmVxdWlyZShcImV2ZW50c1wiKS5FdmVudEVtaXR0ZXJcbiAgLCB1dGlsICAgICAgICAgPSByZXF1aXJlKFwidXRpbFwiKVxuICAsIGRvbXJlYWR5ICAgICA9IHJlcXVpcmUoXCJkb21yZWFkeVwiKVxuICAsIHZrZXkgICAgICAgICA9IHJlcXVpcmUoXCJ2a2V5XCIpXG4gICwgaW52ZXJ0ICAgICAgID0gcmVxdWlyZShcImludmVydC1oYXNoXCIpXG4gICwgdW5pcSAgICAgICAgID0gcmVxdWlyZShcInVuaXFcIilcbiAgLCBsb3dlckJvdW5kICAgPSByZXF1aXJlKFwibG93ZXItYm91bmRcIilcbiAgLCBpb3RhICAgICAgICAgPSByZXF1aXJlKFwiaW90YS1hcnJheVwiKVxuICAsIG1pbiAgICAgICAgICA9IE1hdGgubWluXG5cbnJlcXVpcmUoXCIuL2xpYi9yYWYtcG9seWZpbGwuanNcIilcblxuLy9SZW1vdmUgYW5nbGUgYnJhY2VzIGFuZCBvdGhlciB1c2VsZXNzIGNyYXBcbnZhciBmaWx0ZXJlZF92a2V5ID0gKGZ1bmN0aW9uKCkge1xuICB2YXIgcmVzdWx0ID0gbmV3IEFycmF5KDI1NilcbiAgICAsIGksIGosIGtcbiAgZm9yKGk9MDsgaTwyNTY7ICsraSkge1xuICAgIHJlc3VsdFtpXSA9IFwiVU5LXCJcbiAgfVxuICBmb3IoaSBpbiB2a2V5KSB7XG4gICAgayA9IHZrZXlbaV1cbiAgICBpZihrLmNoYXJBdCgwKSA9PT0gJzwnICYmIGsuY2hhckF0KGsubGVuZ3RoLTEpID09PSAnPicpIHtcbiAgICAgIGsgPSBrLnN1YnN0cmluZygxLCBrLmxlbmd0aC0xKVxuICAgIH1cbiAgICBrID0gay5yZXBsYWNlKC9cXHMvZywgXCItXCIpXG4gICAgcmVzdWx0W3BhcnNlSW50KGkpXSA9IGtcbiAgfVxuICByZXR1cm4gcmVzdWx0XG59KSgpXG5cbi8vQ29tcHV0ZSBtaW5pbWFsIGNvbW1vbiBzZXQgb2Yga2V5Ym9hcmQgZnVuY3Rpb25zXG52YXIga2V5TmFtZXMgPSB1bmlxKE9iamVjdC5rZXlzKGludmVydChmaWx0ZXJlZF92a2V5KSkpXG5cbi8vVHJhbnNsYXRlcyBhIHZpcnR1YWwga2V5Y29kZSB0byBhIG5vcm1hbGl6ZWQga2V5Y29kZVxuZnVuY3Rpb24gdmlydHVhbEtleUNvZGUoa2V5KSB7XG4gIHZhciBpZHggPSBsb3dlckJvdW5kKGtleU5hbWVzLCBrZXkpXG4gIGlmKGlkeCA8IDAgfHwgaWR4ID49IGtleU5hbWVzLmxlbmd0aCkge1xuICAgIHJldHVybiAtMVxuICB9XG4gIHJldHVybiBpZHhcbn1cblxuLy9NYXBzIGEgcGh5c2ljYWwga2V5Y29kZSB0byBhIG5vcm1hbGl6ZWQga2V5Y29kZVxuZnVuY3Rpb24gcGh5c2ljYWxLZXlDb2RlKGtleSkge1xuICByZXR1cm4gdmlydHVhbEtleUNvZGUoZmlsdGVyZWRfdmtleVtrZXldKVxufVxuXG4vL0dhbWUgc2hlbGxcbmZ1bmN0aW9uIEdhbWVTaGVsbCgpIHtcbiAgRXZlbnRFbWl0dGVyLmNhbGwodGhpcylcbiAgdGhpcy5fY3VyS2V5U3RhdGUgID0gbmV3IEFycmF5KGtleU5hbWVzLmxlbmd0aClcbiAgdGhpcy5fcHJlc3NDb3VudCAgID0gbmV3IEFycmF5KGtleU5hbWVzLmxlbmd0aClcbiAgdGhpcy5fcmVsZWFzZUNvdW50ID0gbmV3IEFycmF5KGtleU5hbWVzLmxlbmd0aClcbiAgXG4gIHRoaXMuX3RpY2tJbnRlcnZhbCA9IG51bGxcbiAgdGhpcy5fdGlja1JhdGUgPSAwXG4gIHRoaXMuX2xhc3RUaWNrID0gRGF0ZS5ub3coKVxuICB0aGlzLl9mcmFtZVRpbWUgPSAwLjBcbiAgdGhpcy5fcGF1c2VkID0gZmFsc2VcbiAgXG4gIHRoaXMuX3JlbmRlciA9IHJlbmRlci5iaW5kKHVuZGVmaW5lZCwgdGhpcylcbiAgXG4gIGZvcih2YXIgaT0wOyBpPGtleU5hbWVzLmxlbmd0aDsgKytpKSB7XG4gICAgdGhpcy5fY3VyS2V5U3RhdGVbaV0gPSBmYWxzZVxuICAgIHRoaXMuX3ByZXNzQ291bnRbaV0gPSB0aGlzLl9yZWxlYXNlQ291bnRbaV0gPSAwXG4gIH1cbiAgXG4gIC8vUHVibGljIG1lbWJlcnNcbiAgdGhpcy5lbGVtZW50ID0gbnVsbFxuICB0aGlzLmJpbmRpbmdzID0ge31cbiAgdGhpcy5mcmFtZVNraXAgPSAxMDAuMFxuICB0aGlzLnRpY2tDb3VudCA9IDBcbiAgdGhpcy5mcmFtZUNvdW50ID0gMFxuICB0aGlzLnN0YXJ0VGltZSA9IERhdGUubm93KClcbiAgdGhpcy50aWNrVGltZSA9IHRoaXMuX3RpY2tSYXRlXG4gIHRoaXMuZnJhbWVUaW1lID0gMTAuMFxuICBcbiAgLy9Nb3VzZSBzdGF0ZVxuICB0aGlzLm1vdXNlWCA9IDBcbiAgdGhpcy5tb3VzZVkgPSAwXG4gIHRoaXMucHJldk1vdXNlWCA9IDBcbiAgdGhpcy5wcmV2TW91c2VZID0gMFxufVxuXG51dGlsLmluaGVyaXRzKEdhbWVTaGVsbCwgRXZlbnRFbWl0dGVyKVxuXG4vL0JpbmQga2V5bmFtZXNcbkdhbWVTaGVsbC5wcm90b3R5cGUua2V5TmFtZXMgPSBrZXlOYW1lc1xuXG4vL0JpbmRzIGEgdmlydHVhbCBrZXlib2FyZCBldmVudCB0byBhIHBoeXNpY2FsIGtleVxuR2FtZVNoZWxsLnByb3RvdHlwZS5iaW5kID0gZnVuY3Rpb24odmlydHVhbF9rZXkpIHtcbiAgLy9Mb29rIHVwIHByZXZpb3VzIGtleSBiaW5kaW5nc1xuICB2YXIgYXJyXG4gIGlmKHZpcnR1YWxfa2V5IGluIHRoaXMuYmluZGluZ3MpIHtcbiAgICBhcnIgPSB0aGlzLmJpbmRpbmdzW3ZpcnR1YWxfa2V5XVxuICB9IGVsc2Uge1xuICAgIGFyciA9IFtdXG4gIH1cbiAgLy9BZGQga2V5cyB0byBsaXN0XG4gIHZhciBwaHlzaWNhbF9rZXlcbiAgZm9yKHZhciBpPTEsIG49YXJndW1lbnRzLmxlbmd0aDsgaTxuOyArK2kpIHtcbiAgICBwaHlzaWNhbF9rZXkgPSBhcmd1bWVudHNbaV1cbiAgICBpZih2aXJ0dWFsS2V5Q29kZShwaHlzaWNhbF9rZXkpID49IDApIHtcbiAgICAgIGFyci5wdXNoKHBoeXNpY2FsX2tleSlcbiAgICB9XG4gIH1cbiAgLy9SZW1vdmUgYW55IGR1cGxpY2F0ZSBrZXlzXG4gIGFyciA9IHVuaXEoYXJyKVxuICBpZihhcnIubGVuZ3RoID4gMCkge1xuICAgIHRoaXMuYmluZGluZ3NbdmlydHVhbF9rZXldID0gYXJyXG4gIH1cbn1cblxuLy9VbmJpbmRzIGEgdmlydHVhbCBrZXlib2FyZCBldmVudFxuR2FtZVNoZWxsLnByb3RvdHlwZS51bmJpbmQgPSBmdW5jdGlvbih2aXJ0dWFsX2tleSkge1xuICBpZih2aXJ0dWFsX2tleSBpbiB0aGlzLmJpbmRpbmdzKSB7XG4gICAgZGVsZXRlIHRoaXMuYmluZGluZ3NbdmlydHVhbF9rZXldXG4gIH1cbn1cblxuLy9DaGVja3MgaWYgYSBrZXkgaXMgc2V0IGluIGEgZ2l2ZW4gc3RhdGVcbmZ1bmN0aW9uIGxvb2t1cEtleShzdGF0ZSwgYmluZGluZ3MsIGtleSkge1xuICBpZihrZXkgaW4gYmluZGluZ3MpIHtcbiAgICB2YXIgYXJyID0gYmluZGluZ3Nba2V5XVxuICAgIGZvcih2YXIgaT0wLCBuPWFyci5sZW5ndGg7IGk8bjsgKytpKSB7XG4gICAgICBpZihzdGF0ZVt2aXJ0dWFsS2V5Q29kZShhcnJbaV0pXSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuICB2YXIga2MgPSB2aXJ0dWFsS2V5Q29kZShrZXkpXG4gIGlmKGtjID49IDApIHtcbiAgICByZXR1cm4gc3RhdGVba2NdXG4gIH1cbiAgcmV0dXJuIGZhbHNlXG59XG5cbi8vQ2hlY2tzIGlmIGEga2V5IGlzIHNldCBpbiBhIGdpdmVuIHN0YXRlXG5mdW5jdGlvbiBsb29rdXBDb3VudChzdGF0ZSwgYmluZGluZ3MsIGtleSkge1xuICBpZihrZXkgaW4gYmluZGluZ3MpIHtcbiAgICB2YXIgYXJyID0gYmluZGluZ3Nba2V5XSwgciA9IDBcbiAgICBmb3IodmFyIGk9MCwgbj1hcnIubGVuZ3RoOyBpPG47ICsraSkge1xuICAgICAgciArPSBzdGF0ZVt2aXJ0dWFsS2V5Q29kZShhcnJbaV0pXVxuICAgIH1cbiAgICByZXR1cm4gclxuICB9XG4gIHZhciBrYyA9IHZpcnR1YWxLZXlDb2RlKGtleSlcbiAgaWYoa2MgPj0gMCkge1xuICAgIHJldHVybiBzdGF0ZVtrY11cbiAgfVxuICByZXR1cm4gMFxufVxuXG4vL0NoZWNrcyBpZiBhIGtleSAoZWl0aGVyIHBoeXNpY2FsIG9yIHZpcnR1YWwpIGlzIGN1cnJlbnRseSBoZWxkIGRvd25cbkdhbWVTaGVsbC5wcm90b3R5cGUuZG93biA9IGZ1bmN0aW9uKGtleSkge1xuICByZXR1cm4gbG9va3VwS2V5KHRoaXMuX2N1cktleVN0YXRlLCB0aGlzLmJpbmRpbmdzLCBrZXkpXG59XG5cbi8vQ2hlY2tzIGlmIGEga2V5IHdhcyBldmVyIGRvd25cbkdhbWVTaGVsbC5wcm90b3R5cGUud2FzRG93biA9IGZ1bmN0aW9uKGtleSkge1xuICByZXR1cm4gdGhpcy5kb3duKGtleSkgfHwgdGhpcy5wcmVzcyhrZXkpXG59XG5cbi8vT3Bwb3NpdGUgb2YgZG93blxuR2FtZVNoZWxsLnByb3RvdHlwZS51cCA9IGZ1bmN0aW9uKGtleSkge1xuICByZXR1cm4gIXRoaXMuZG93bihrZXkpXG59XG5cbkdhbWVTaGVsbC5wcm90b3R5cGUud2FzVXAgPSBmdW5jdGlvbihrZXkpIHtcbiAgcmV0dXJuIHRoaXMudXAoa2V5KSB8fCB0aGlzLnJlbGVhc2Uoa2V5KVxufVxuXG4vL1JldHVybnMgdGhlIG51bWJlciBvZiB0aW1lcyBhIGtleSB3YXMgcHJlc3NlZCBzaW5jZSBsYXN0IHRpY2tcbkdhbWVTaGVsbC5wcm90b3R5cGUucHJlc3MgPSBmdW5jdGlvbihrZXkpIHtcbiAgcmV0dXJuIGxvb2t1cENvdW50KHRoaXMuX3ByZXNzQ291bnQsIHRoaXMuYmluZGluZ3MsIGtleSlcbn1cblxuLy9SZXR1cm5zIHRoZSBudW1iZXIgb2YgdGltZXMgYSBrZXkgd2FzIHJlbGVhc2VkIHNpbmNlIGxhc3QgdGlja1xuR2FtZVNoZWxsLnByb3RvdHlwZS5yZWxlYXNlID0gZnVuY3Rpb24oa2V5KSB7XG4gIHJldHVybiBsb29rdXBDb3VudCh0aGlzLl9yZWxlYXNlQ291bnQsIHRoaXMuYmluZGluZ3MsIGtleSlcbn1cblxuLy9QYXVzZS91bnBhdXNlIHRoZSBnYW1lIGxvb3Bcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShHYW1lU2hlbGwucHJvdG90eXBlLCBcInBhdXNlZFwiLCB7XG4gIGdldDogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BhdXNlZFxuICB9LFxuICBzZXQ6IGZ1bmN0aW9uKHApIHtcbiAgICBpZihwKSB7XG4gICAgICBpZighdGhpcy5fcGF1c2VkKSB7XG4gICAgICAgIHRoaXMuX3BhdXNlZCA9IHRydWVcbiAgICAgICAgdGhpcy5fZnJhbWVUaW1lID0gbWluKDEuMCwgKERhdGUubm93KCkgLSB0aGlzLl9sYXN0VGljaykgLyB0aGlzLl90aWNrUmF0ZSlcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYodGhpcy5fcGF1c2VkKSB7XG4gICAgICB0aGlzLl9wYXVzZWQgPSBmYWxzZVxuICAgICAgdGhpcy5fbGFzdFRpY2sgPSBEYXRlLm5vdygpIC0gTWF0aC5mbG9vcih0aGlzLl9mcmFtZVRpbWUgKiB0aGlzLl90aWNrUmF0ZSlcbiAgICB9XG4gIH1cbn0pXG5cbi8vU2V0IGtleSBzdGF0ZVxuZnVuY3Rpb24gc2V0S2V5U3RhdGUoc2hlbGwsIGtleSwgc3RhdGUpIHtcbiAgdmFyIHBzID0gc2hlbGwuX2N1cktleVN0YXRlW2tleV1cbiAgaWYocHMgIT09IHN0YXRlKSB7XG4gICAgaWYoc3RhdGUpIHtcbiAgICAgIHNoZWxsLl9wcmVzc0NvdW50W2tleV0rK1xuICAgIH0gZWxzZSB7XG4gICAgICBzaGVsbC5fcmVsZWFzZUNvdW50W2tleV0rK1xuICAgIH1cbiAgICBzaGVsbC5fY3VyS2V5U3RhdGVba2V5XSA9IHN0YXRlXG4gIH1cbn1cblxuLy9UaWNrcyB0aGUgZ2FtZSBzdGF0ZSBvbmUgdXBkYXRlXG5mdW5jdGlvbiB0aWNrKHNoZWxsKSB7XG4gIHZhciBza2lwID0gRGF0ZS5ub3coKSArIHNoZWxsLmZyYW1lU2tpcFxuICAgICwgcENvdW50ID0gc2hlbGwuX3ByZXNzQ291bnRcbiAgICAsIHJDb3VudCA9IHNoZWxsLl9yZWxlYXNlQ291bnRcbiAgICAsIGksIHMsIHRcbiAgICAsIHRyID0gc2hlbGwuX3RpY2tSYXRlXG4gICAgLCBuID0ga2V5TmFtZXMubGVuZ3RoXG4gIHdoaWxlKCFzaGVsbC5fcGF1c2VkICYmXG4gICAgICAgIERhdGUubm93KCkgPj0gc2hlbGwuX2xhc3RUaWNrICsgdHIpIHtcbiAgICAvL1NraXAgYSBmcmFtZSBpZiB3ZSBhcmUgb3ZlciBidWRnZXRcbiAgICBpZihEYXRlLm5vdygpID4gc2tpcCkge1xuICAgICAgc2hlbGwuX2xhc3RUaWNrID0gRGF0ZS5ub3coKSArIHRyXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgXG4gICAgLy9VcGRhdGUgY291bnRlcnMgYW5kIHRpbWVcbiAgICArK3NoZWxsLnRpY2tDb3VudFxuICAgIHNoZWxsLl9sYXN0VGljayArPSB0clxuICAgIFxuICAgIC8vVGljayB0aGUgZ2FtZVxuICAgIHMgPSBEYXRlLm5vdygpXG4gICAgc2hlbGwuZW1pdChcInRpY2tcIilcbiAgICB0ID0gRGF0ZS5ub3coKVxuICAgIHNoZWxsLnRpY2tUaW1lID0gc2hlbGwudGlja1RpbWUgKiAwLjMgKyAodCAtIHMpICogMC43XG4gICAgXG4gICAgLy9TaGlmdCBpbnB1dCBzdGF0ZVxuICAgIGZvcihpPTA7IGk8bjsgKytpKSB7XG4gICAgICBwQ291bnRbaV0gPSByQ291bnRbaV0gPSAwXG4gICAgfVxuICAgIHNoZWxsLnByZXZNb3VzZVggPSBzaGVsbC5tb3VzZVhcbiAgICBzaGVsbC5wcmV2TW91c2VZID0gc2hlbGwubW91c2VZXG4gIH1cbn1cblxuLy9SZW5kZXIgc3R1ZmZcbmZ1bmN0aW9uIHJlbmRlcihzaGVsbCkge1xuICAvL1RpY2sgdGhlIHNoZWxsXG4gIHRpY2soc2hlbGwpXG4gIFxuICAvL0NvbXB1dGUgZnJhbWUgdGltZVxuICB2YXIgZHRcbiAgaWYoc2hlbGwuX3BhdXNlZCkge1xuICAgIGR0ID0gc2hlbGwuX2ZyYW1lVGltZVxuICB9IGVsc2Uge1xuICAgIGR0ID0gbWluKDEuMCwgKERhdGUubm93KCkgLSBzaGVsbC5fbGFzdFRpY2spIC8gc2hlbGwuX3RpY2tSYXRlKVxuICB9XG4gIFxuICAvL0RyYXcgYSBmcmFtZVxuICArK3NoZWxsLmZyYW1lQ291bnRcbiAgdmFyIHMgPSBEYXRlLm5vdygpXG4gIHNoZWxsLmVtaXQoXCJyZW5kZXJcIiwgZHQpXG4gIHZhciB0ID0gRGF0ZS5ub3coKVxuICBzaGVsbC5mcmFtZVRpbWUgPSBzaGVsbC5mcmFtZVRpbWUgKiAwLjMgKyAodCAtIHMpICogMC43XG4gIFxuICAvL1JlcXVlc3QgbmV4dCBmcmFtZVxuICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoc2hlbGwuX3JlbmRlcilcbn1cblxuLy9TZXQga2V5IHVwXG5mdW5jdGlvbiBoYW5kbGVLZXlVcChzaGVsbCwgZXYpIHtcbiAgdmFyIGtjID0gcGh5c2ljYWxLZXlDb2RlKGV2LmtleUNvZGUgfHwgZXYud2hpY2ggfHwgZXYuY2hhckNvZGUpXG4gIGlmKGtjID49IDApIHtcbiAgICBzZXRLZXlTdGF0ZShzaGVsbCwga2MsIGZhbHNlKVxuICB9XG59XG5cbi8vU2V0IGtleSBkb3duXG5mdW5jdGlvbiBoYW5kbGVLZXlEb3duKHNoZWxsLCBldikge1xuICB2YXIga2MgPSBwaHlzaWNhbEtleUNvZGUoZXYua2V5Q29kZSB8fCBldi5jaGFyIHx8IGV2LndoaWNoIHx8IGV2LmNoYXJDb2RlKVxuICBpZihrYyA+PSAwKSB7XG4gICAgc2V0S2V5U3RhdGUoc2hlbGwsIGtjLCB0cnVlKVxuICB9XG59XG5cbnZhciBtb3VzZUNvZGVzID0gaW90YSg1KS5tYXAoZnVuY3Rpb24obikge1xuICByZXR1cm4gdmlydHVhbEtleUNvZGUoXCJtb3VzZS1cIiArIChuKzEpKVxufSlcblxuZnVuY3Rpb24gc2V0TW91c2VCdXR0b25zKHNoZWxsLCBidXR0b25zKSB7XG4gIGZvcih2YXIgaT0wOyBpPDU7ICsraSkge1xuICAgIHNldEtleVN0YXRlKHNoZWxsLCBtb3VzZUNvZGVzW2ldLCAhIShidXR0b25zICYgKDE8PGkpKSlcbiAgfVxufVxuXG5mdW5jdGlvbiBoYW5kbGVNb3VzZU1vdmUoc2hlbGwsIGV2KSB7XG4gIGlmKGV2LndoaWNoICE9PSB1bmRlZmluZWQpIHtcbiAgICBzZXRNb3VzZUJ1dHRvbnMoc2hlbGwsIGV2LndoaWNoKVxuICB9XG4gIGlmKGV2LmJ1dHRvbnMgIT09IHVuZGVmaW5lZCkge1xuICAgIHNldE1vdXNlQnV0dG9ucyhzaGVsbCwgZXYuYnV0dG9ucylcbiAgfVxuICBzaGVsbC5tb3VzZVggPSBldi5jbGllbnRYXG4gIHNoZWxsLm1vdXNlWSA9IGV2LmNsaWVudFlcbn1cblxuZnVuY3Rpb24gaGFuZGxlTW91c2VEb3duKHNoZWxsLCBldikge1xuICBoYW5kbGVNb3VzZU1vdmUoc2hlbGwsIGV2KVxuICBzZXRLZXlTdGF0ZShzaGVsbCwgbW91c2VDb2Rlc1tldi5idXR0b25dLCB0cnVlKVxufVxuXG5mdW5jdGlvbiBoYW5kbGVNb3VzZVVwKHNoZWxsLCBldikge1xuICBoYW5kbGVNb3VzZU1vdmUoc2hlbGwsIGV2KVxuICBzZXRLZXlTdGF0ZShzaGVsbCwgbW91c2VDb2Rlc1tldi5idXR0b25dLCBmYWxzZSlcbn1cblxuZnVuY3Rpb24gaGFuZGxlTW91c2VFbnRlcihzaGVsbCwgZXYpIHtcbiAgaGFuZGxlTW91c2VNb3ZlKHNoZWxsLCBldilcbiAgc2hlbGwucHJldk1vdXNlWCA9IHNoZWxsLm1vdXNlWCA9IGV2LmNsaWVudFhcbiAgc2hlbGwucHJldk1vdXNlWSA9IHNoZWxsLm1vdXNlWSA9IGV2LmNsaWVudFlcbn1cblxuZnVuY3Rpb24gaGFuZGxlTW91c2VMZWF2ZShzaGVsbCwgZXYpIHtcbiAgZm9yKHZhciBpPTA7IGk8NTsgKytpKSB7XG4gICAgc2V0S2V5U3RhdGUoc2hlbGwsIG1vdXNlQ29kZXNbaV0sIGZhbHNlKVxuICB9XG59XG5cbmZ1bmN0aW9uIGhhbmRsZUJsdXIoc2hlbGwsIGV2KSB7XG4gIHZhciBuID0ga2V5TmFtZXMubGVuZ3RoXG4gICAgLCBjID0gc2hlbGwuX2N1cktleVN0YXRlXG4gICAgLCBpXG4gIGZvcihpPTA7IGk8bjsgKytpKSB7XG4gICAgY1tpXSA9IGZhbHNlXG4gIH1cbn1cblxuZnVuY3Rpb24gbWFrZURlZmF1bHRDb250YWluZXIoKSB7XG4gIHZhciBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpXG4gIGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9IFwiYWJzb2x1dGVcIlxuICBjb250YWluZXIuc3R5bGUubGVmdCA9IFwiMHB4XCJcbiAgY29udGFpbmVyLnN0eWxlLnJpZ2h0ID0gXCIwcHhcIlxuICBjb250YWluZXIuc3R5bGUudG9wID0gXCIwcHhcIlxuICBjb250YWluZXIuc3R5bGUuYm90dG9tID0gXCIwcHhcIlxuICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcilcbiAgcmV0dXJuIGNvbnRhaW5lclxufVxuXG5mdW5jdGlvbiBjcmVhdGVTaGVsbChvcHRpb25zKSB7XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9XG4gIFxuICAvL0NyZWF0ZSBpbml0aWFsIHNoZWxsXG4gIHZhciBzaGVsbCA9IG5ldyBHYW1lU2hlbGwoKVxuICBzaGVsbC5fdGlja1JhdGUgPSBvcHRpb25zLnRpY2tSYXRlIHx8IDMwXG4gIHNoZWxsLmZyYW1lU2tpcCA9IG9wdGlvbnMuZnJhbWVTa2lwIHx8IChzaGVsbC5fdGlja1JhdGUrNSkgKiA1XG4gIFxuICAvL1NldCBiaW5kaW5nc1xuICBpZihvcHRpb25zLmJpbmRpbmdzKSB7XG4gICAgc2hlbGwuYmluZGluZ3MgPSBiaW5kaW5nc1xuICB9XG4gIFxuICAvL1dhaXQgZm9yIGRvbSB0byBpbnRpYWlsaXplXG4gIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IGRvbXJlYWR5KGZ1bmN0aW9uKCkge1xuICAgIFxuICAgIC8vUmV0cmlldmUgZWxlbWVudFxuICAgIHZhciBlbGVtZW50ID0gb3B0aW9ucy5lbGVtZW50XG4gICAgaWYodHlwZW9mIGVsZW1lbnQgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgIHZhciBlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZWxlbWVudClcbiAgICAgIGlmKCFlKSB7XG4gICAgICAgIGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGVsZW1lbnQpXG4gICAgICB9XG4gICAgICBpZighZSkge1xuICAgICAgICBlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5Q2xhc3MoZWxlbWVudClbMF1cbiAgICAgIH1cbiAgICAgIGlmKCFlKSB7XG4gICAgICAgIGUgPSBtYWtlRGVmYXVsdENvbnRhaW5lcigpXG4gICAgICB9XG4gICAgICBzaGVsbC5lbGVtZW50ID0gZVxuICAgIH0gZWxzZSBpZih0eXBlb2YgZWxlbWVudCA9PT0gXCJvYmplY3RcIiAmJiAhIWVsZW1lbnQpIHtcbiAgICAgIHNoZWxsLmVsZW1lbnQgPSBlbGVtZW50XG4gICAgfSBlbHNlIGlmKHR5cGVvZiBlbGVtZW50ID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHNoZWxsLmVsZW1lbnQgPSBlbGVtZW50KClcbiAgICB9IGVsc2Uge1xuICAgICAgc2hlbGwuZWxlbWVudCA9IG1ha2VEZWZhdWx0Q29udGFpbmVyKClcbiAgICB9XG4gICAgXG4gICAgLy9EaXNhYmxlIHVzZXItc2VsZWN0XG4gICAgaWYoc2hlbGwuZWxlbWVudC5zdHlsZSkge1xuICAgICAgc2hlbGwuZWxlbWVudC5zdHlsZVtcIi13ZWJraXQtdG91Y2gtY2FsbG91dFwiXSA9IFwibm9uZVwiXG4gICAgICBzaGVsbC5lbGVtZW50LnN0eWxlW1wiLXdlYmtpdC11c2VyLXNlbGVjdFwiXSA9IFwibm9uZVwiXG4gICAgICBzaGVsbC5lbGVtZW50LnN0eWxlW1wiLWtodG1sLXVzZXItc2VsZWN0XCJdID0gXCJub25lXCJcbiAgICAgIHNoZWxsLmVsZW1lbnQuc3R5bGVbXCItbW96LXVzZXItc2VsZWN0XCJdID0gXCJub25lXCJcbiAgICAgIHNoZWxsLmVsZW1lbnQuc3R5bGVbXCItbXMtdXNlci1zZWxlY3RcIl0gPSBcIm5vbmVcIlxuICAgICAgc2hlbGwuZWxlbWVudC5zdHlsZVtcInVzZXItc2VsZWN0XCJdID0gXCJub25lXCJcbiAgICB9XG4gICAgXG4gICAgLy9Ib29rIGlucHV0IGxpc3RlbmVyc1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwia2V5ZG93blwiLCBoYW5kbGVLZXlEb3duLmJpbmQodW5kZWZpbmVkLCBzaGVsbCksIHRydWUpXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJrZXl1cFwiLCBoYW5kbGVLZXlVcC5iaW5kKHVuZGVmaW5lZCwgc2hlbGwpLCB0cnVlKVxuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwibW91c2Vkb3duXCIsIGhhbmRsZU1vdXNlRG93bi5iaW5kKHVuZGVmaW5lZCwgc2hlbGwpLCB0cnVlKVxuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwibW91c2V1cFwiLCBoYW5kbGVNb3VzZVVwLmJpbmQodW5kZWZpbmVkLCBzaGVsbCksIHRydWUpXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJtb3VzZW1vdmVcIiwgaGFuZGxlTW91c2VNb3ZlLmJpbmQodW5kZWZpbmVkLCBzaGVsbCksIHRydWUpXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJtb3VzZWxlYXZlXCIsIGhhbmRsZU1vdXNlTGVhdmUuYmluZCh1bmRlZmluZWQsIHNoZWxsKSwgdHJ1ZSlcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1vdXNlZW50ZXJcIiwgaGFuZGxlTW91c2VFbnRlci5iaW5kKHVuZGVmaW5lZCwgc2hlbGwpLCB0cnVlKVxuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwiYmx1clwiLCBoYW5kbGVCbHVyLmJpbmQodW5kZWZpbmVkLCBzaGVsbCksIHRydWUpXG4gICAgXG4gICAgLy9Jbml0aWFsaXplIHRpY2sgY291bnRlclxuICAgIHNoZWxsLl9sYXN0VGljayA9IERhdGUubm93KClcbiAgICBzaGVsbC5fcGF1c2VkID0gZmFsc2VcbiAgICBzaGVsbC5zdGFydFRpbWUgPSBEYXRlLm5vdygpXG4gICAgXG4gICAgLy9TZXQgdXAgYSB0aWNrIGludGVydmFsXG4gICAgc2hlbGwuX3RpY2tJbnRlcnZhbCA9IHNldEludGVydmFsKHRpY2ssIHNoZWxsLl90aWNrUmF0ZSwgc2hlbGwpXG4gICAgXG4gICAgLy9DcmVhdGUgYW4gYW5pbWF0aW9uIGZyYW1lIGhhbmRsZXJcbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoc2hlbGwuX3JlbmRlcilcbiAgICBcbiAgICAvL0VtaXQgaW5pdGlhbGl6ZSBldmVudFxuICAgIHNoZWxsLmVtaXQoXCJpbml0XCIpXG4gIH0pfSwgMClcbiAgXG4gIHJldHVybiBzaGVsbFxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNyZWF0ZVNoZWxsIiwiLyohXG4gICogZG9tcmVhZHkgKGMpIER1c3RpbiBEaWF6IDIwMTIgLSBMaWNlbnNlIE1JVFxuICAqL1xuIWZ1bmN0aW9uIChuYW1lLCBkZWZpbml0aW9uKSB7XG4gIGlmICh0eXBlb2YgbW9kdWxlICE9ICd1bmRlZmluZWQnKSBtb2R1bGUuZXhwb3J0cyA9IGRlZmluaXRpb24oKVxuICBlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgdHlwZW9mIGRlZmluZS5hbWQgPT0gJ29iamVjdCcpIGRlZmluZShkZWZpbml0aW9uKVxuICBlbHNlIHRoaXNbbmFtZV0gPSBkZWZpbml0aW9uKClcbn0oJ2RvbXJlYWR5JywgZnVuY3Rpb24gKHJlYWR5KSB7XG5cbiAgdmFyIGZucyA9IFtdLCBmbiwgZiA9IGZhbHNlXG4gICAgLCBkb2MgPSBkb2N1bWVudFxuICAgICwgdGVzdEVsID0gZG9jLmRvY3VtZW50RWxlbWVudFxuICAgICwgaGFjayA9IHRlc3RFbC5kb1Njcm9sbFxuICAgICwgZG9tQ29udGVudExvYWRlZCA9ICdET01Db250ZW50TG9hZGVkJ1xuICAgICwgYWRkRXZlbnRMaXN0ZW5lciA9ICdhZGRFdmVudExpc3RlbmVyJ1xuICAgICwgb25yZWFkeXN0YXRlY2hhbmdlID0gJ29ucmVhZHlzdGF0ZWNoYW5nZSdcbiAgICAsIHJlYWR5U3RhdGUgPSAncmVhZHlTdGF0ZSdcbiAgICAsIGxvYWRlZCA9IC9ebG9hZGV8Yy8udGVzdChkb2NbcmVhZHlTdGF0ZV0pXG5cbiAgZnVuY3Rpb24gZmx1c2goZikge1xuICAgIGxvYWRlZCA9IDFcbiAgICB3aGlsZSAoZiA9IGZucy5zaGlmdCgpKSBmKClcbiAgfVxuXG4gIGRvY1thZGRFdmVudExpc3RlbmVyXSAmJiBkb2NbYWRkRXZlbnRMaXN0ZW5lcl0oZG9tQ29udGVudExvYWRlZCwgZm4gPSBmdW5jdGlvbiAoKSB7XG4gICAgZG9jLnJlbW92ZUV2ZW50TGlzdGVuZXIoZG9tQ29udGVudExvYWRlZCwgZm4sIGYpXG4gICAgZmx1c2goKVxuICB9LCBmKVxuXG5cbiAgaGFjayAmJiBkb2MuYXR0YWNoRXZlbnQob25yZWFkeXN0YXRlY2hhbmdlLCBmbiA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAoL15jLy50ZXN0KGRvY1tyZWFkeVN0YXRlXSkpIHtcbiAgICAgIGRvYy5kZXRhY2hFdmVudChvbnJlYWR5c3RhdGVjaGFuZ2UsIGZuKVxuICAgICAgZmx1c2goKVxuICAgIH1cbiAgfSlcblxuICByZXR1cm4gKHJlYWR5ID0gaGFjayA/XG4gICAgZnVuY3Rpb24gKGZuKSB7XG4gICAgICBzZWxmICE9IHRvcCA/XG4gICAgICAgIGxvYWRlZCA/IGZuKCkgOiBmbnMucHVzaChmbikgOlxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRlc3RFbC5kb1Njcm9sbCgnbGVmdCcpXG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IHJlYWR5KGZuKSB9LCA1MClcbiAgICAgICAgICB9XG4gICAgICAgICAgZm4oKVxuICAgICAgICB9KClcbiAgICB9IDpcbiAgICBmdW5jdGlvbiAoZm4pIHtcbiAgICAgIGxvYWRlZCA/IGZuKCkgOiBmbnMucHVzaChmbilcbiAgICB9KVxufSkiLCIoZnVuY3Rpb24oKXt2YXIgdWEgPSB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdy5uYXZpZ2F0b3IudXNlckFnZW50IDogJydcbiAgLCBpc09TWCA9IC9PUyBYLy50ZXN0KHVhKVxuICAsIGlzT3BlcmEgPSAvT3BlcmEvLnRlc3QodWEpXG4gICwgbWF5YmVGaXJlZm94ID0gIS9saWtlIEdlY2tvLy50ZXN0KHVhKSAmJiAhaXNPcGVyYVxuXG52YXIgaSwgb3V0cHV0ID0gbW9kdWxlLmV4cG9ydHMgPSB7XG4gIDA6ICBpc09TWCA/ICc8bWVudT4nIDogJzxVTks+J1xuLCAxOiAgJzxtb3VzZSAxPidcbiwgMjogICc8bW91c2UgMj4nXG4sIDM6ICAnPGJyZWFrPidcbiwgNDogICc8bW91c2UgMz4nXG4sIDU6ICAnPG1vdXNlIDQ+J1xuLCA2OiAgJzxtb3VzZSA1PidcbiwgODogICc8YmFja3NwYWNlPidcbiwgOTogICc8dGFiPidcbiwgMTI6ICc8Y2xlYXI+J1xuLCAxMzogJzxlbnRlcj4nXG4sIDE2OiAnPHNoaWZ0PidcbiwgMTc6ICc8Y29udHJvbD4nXG4sIDE4OiAnPGFsdD4nXG4sIDE5OiAnPHBhdXNlPidcbiwgMjA6ICc8Y2Fwcy1sb2NrPidcbiwgMjE6ICc8aW1lLWhhbmd1bD4nXG4sIDIzOiAnPGltZS1qdW5qYT4nXG4sIDI0OiAnPGltZS1maW5hbD4nXG4sIDI1OiAnPGltZS1rYW5qaT4nXG4sIDI3OiAnPGVzY2FwZT4nXG4sIDI4OiAnPGltZS1jb252ZXJ0PidcbiwgMjk6ICc8aW1lLW5vbmNvbnZlcnQ+J1xuLCAzMDogJzxpbWUtYWNjZXB0PidcbiwgMzE6ICc8aW1lLW1vZGUtY2hhbmdlPidcbiwgMjc6ICc8ZXNjYXBlPidcbiwgMzI6ICc8c3BhY2U+J1xuLCAzMzogJzxwYWdlLXVwPidcbiwgMzQ6ICc8cGFnZS1kb3duPidcbiwgMzU6ICc8ZW5kPidcbiwgMzY6ICc8aG9tZT4nXG4sIDM3OiAnPGxlZnQ+J1xuLCAzODogJzx1cD4nXG4sIDM5OiAnPHJpZ2h0PidcbiwgNDA6ICc8ZG93bj4nXG4sIDQxOiAnPHNlbGVjdD4nXG4sIDQyOiAnPHByaW50PidcbiwgNDM6ICc8ZXhlY3V0ZT4nXG4sIDQ0OiAnPHNuYXBzaG90PidcbiwgNDU6ICc8aW5zZXJ0PidcbiwgNDY6ICc8ZGVsZXRlPidcbiwgNDc6ICc8aGVscD4nXG4sIDkxOiAnPG1ldGE+JyAgLy8gbWV0YS1sZWZ0IC0tIG5vIG9uZSBoYW5kbGVzIGxlZnQgYW5kIHJpZ2h0IHByb3Blcmx5LCBzbyB3ZSBjb2VyY2UgaW50byBvbmUuXG4sIDkyOiAnPG1ldGE+JyAgLy8gbWV0YS1yaWdodFxuLCA5MzogaXNPU1ggPyAnPG1ldGE+JyA6ICc8bWVudT4nICAgICAgLy8gY2hyb21lLG9wZXJhLHNhZmFyaSBhbGwgcmVwb3J0IHRoaXMgZm9yIG1ldGEtcmlnaHQgKG9zeCBtYnApLlxuLCA5NTogJzxzbGVlcD4nXG4sIDEwNjogJzxudW0tKj4nXG4sIDEwNzogJzxudW0tKz4nXG4sIDEwODogJzxudW0tZW50ZXI+J1xuLCAxMDk6ICc8bnVtLS0+J1xuLCAxMTA6ICc8bnVtLS4+J1xuLCAxMTE6ICc8bnVtLS8+J1xuLCAxNDQ6ICc8bnVtLWxvY2s+J1xuLCAxNDU6ICc8c2Nyb2xsLWxvY2s+J1xuLCAxNjA6ICc8c2hpZnQtbGVmdD4nXG4sIDE2MTogJzxzaGlmdC1yaWdodD4nXG4sIDE2MjogJzxjb250cm9sLWxlZnQ+J1xuLCAxNjM6ICc8Y29udHJvbC1yaWdodD4nXG4sIDE2NDogJzxhbHQtbGVmdD4nXG4sIDE2NTogJzxhbHQtcmlnaHQ+J1xuLCAxNjY6ICc8YnJvd3Nlci1iYWNrPidcbiwgMTY3OiAnPGJyb3dzZXItZm9yd2FyZD4nXG4sIDE2ODogJzxicm93c2VyLXJlZnJlc2g+J1xuLCAxNjk6ICc8YnJvd3Nlci1zdG9wPidcbiwgMTcwOiAnPGJyb3dzZXItc2VhcmNoPidcbiwgMTcxOiAnPGJyb3dzZXItZmF2b3JpdGVzPidcbiwgMTcyOiAnPGJyb3dzZXItaG9tZT4nXG5cbiAgLy8gZmYvb3N4IHJlcG9ydHMgJzx2b2x1bWUtbXV0ZT4nIGZvciAnLSdcbiwgMTczOiBpc09TWCAmJiBtYXliZUZpcmVmb3ggPyAnLScgOiAnPHZvbHVtZS1tdXRlPidcbiwgMTc0OiAnPHZvbHVtZS1kb3duPidcbiwgMTc1OiAnPHZvbHVtZS11cD4nXG4sIDE3NjogJzxuZXh0LXRyYWNrPidcbiwgMTc3OiAnPHByZXYtdHJhY2s+J1xuLCAxNzg6ICc8c3RvcD4nXG4sIDE3OTogJzxwbGF5LXBhdXNlPidcbiwgMTgwOiAnPGxhdW5jaC1tYWlsPidcbiwgMTgxOiAnPGxhdW5jaC1tZWRpYS1zZWxlY3Q+J1xuLCAxODI6ICc8bGF1bmNoLWFwcCAxPidcbiwgMTgzOiAnPGxhdW5jaC1hcHAgMj4nXG4sIDE4NjogJzsnXG4sIDE4NzogJz0nXG4sIDE4ODogJywnXG4sIDE4OTogJy0nXG4sIDE5MDogJy4nXG4sIDE5MTogJy8nXG4sIDE5MjogJ2AnXG4sIDIxOTogJ1snXG4sIDIyMDogJ1xcXFwnXG4sIDIyMTogJ10nXG4sIDIyMjogXCInXCJcbiwgMjIzOiAnPG1ldGE+J1xuLCAyMjQ6ICc8bWV0YT4nICAgICAgIC8vIGZpcmVmb3ggcmVwb3J0cyBtZXRhIGhlcmUuXG4sIDIyNjogJzxhbHQtZ3I+J1xuLCAyMjk6ICc8aW1lLXByb2Nlc3M+J1xuLCAyMzE6IGlzT3BlcmEgPyAnYCcgOiAnPHVuaWNvZGU+J1xuLCAyNDY6ICc8YXR0ZW50aW9uPidcbiwgMjQ3OiAnPGNyc2VsPidcbiwgMjQ4OiAnPGV4c2VsPidcbiwgMjQ5OiAnPGVyYXNlLWVvZj4nXG4sIDI1MDogJzxwbGF5PidcbiwgMjUxOiAnPHpvb20+J1xuLCAyNTI6ICc8bm8tbmFtZT4nXG4sIDI1MzogJzxwYS0xPidcbiwgMjU0OiAnPGNsZWFyPidcbn1cblxuZm9yKGkgPSA1ODsgaSA8IDY1OyArK2kpIHtcbiAgb3V0cHV0W2ldID0gU3RyaW5nLmZyb21DaGFyQ29kZShpKVxufVxuXG4vLyAwLTlcbmZvcihpID0gNDg7IGkgPCA1ODsgKytpKSB7XG4gIG91dHB1dFtpXSA9IChpIC0gNDgpKycnXG59XG5cbi8vIEEtWlxuZm9yKGkgPSA2NTsgaSA8IDkxOyArK2kpIHtcbiAgb3V0cHV0W2ldID0gU3RyaW5nLmZyb21DaGFyQ29kZShpKVxufVxuXG4vLyBudW0wLTlcbmZvcihpID0gOTY7IGkgPCAxMDc7ICsraSkge1xuICBvdXRwdXRbaV0gPSAnPG51bS0nKyhpIC0gOTYpKyc+J1xufVxuXG4vLyBGMS1GMjRcbmZvcihpID0gMTEyOyBpIDwgMTM2OyArK2kpIHtcbiAgb3V0cHV0W2ldID0gJ0YnKyhpLTExMSlcbn1cblxufSkoKSIsIlwidXNlIHN0cmljdFwiXG5cbmZ1bmN0aW9uIGludmVydChoYXNoKSB7XG4gIHZhciByZXN1bHQgPSB7fVxuICBmb3IodmFyIGkgaW4gaGFzaCkge1xuICAgIGlmKGhhc2guaGFzT3duUHJvcGVydHkoaSkpIHtcbiAgICAgIHJlc3VsdFtoYXNoW2ldXSA9IGlcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGludmVydCIsIlwidXNlIHN0cmljdFwiXG5cbmZ1bmN0aW9uIHVuaXF1ZV9wcmVkKGxpc3QsIGNvbXBhcmUpIHtcbiAgdmFyIHB0ciA9IDFcbiAgICAsIGxlbiA9IGxpc3QubGVuZ3RoXG4gICAgLCBhPWxpc3RbMF0sIGI9bGlzdFswXVxuICBmb3IodmFyIGk9MTsgaTxsZW47ICsraSkge1xuICAgIGIgPSBhXG4gICAgYSA9IGxpc3RbaV1cbiAgICBpZihjb21wYXJlKGEsIGIpKSB7XG4gICAgICBpZihpID09PSBwdHIpIHtcbiAgICAgICAgcHRyKytcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIGxpc3RbcHRyKytdID0gYVxuICAgIH1cbiAgfVxuICBsaXN0Lmxlbmd0aCA9IHB0clxuICByZXR1cm4gbGlzdFxufVxuXG5mdW5jdGlvbiB1bmlxdWVfZXEobGlzdCkge1xuICB2YXIgcHRyID0gMVxuICAgICwgbGVuID0gbGlzdC5sZW5ndGhcbiAgICAsIGE9bGlzdFswXSwgYiA9IGxpc3RbMF1cbiAgZm9yKHZhciBpPTE7IGk8bGVuOyArK2ksIGI9YSkge1xuICAgIGIgPSBhXG4gICAgYSA9IGxpc3RbaV1cbiAgICBpZihhICE9PSBiKSB7XG4gICAgICBpZihpID09PSBwdHIpIHtcbiAgICAgICAgcHRyKytcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIGxpc3RbcHRyKytdID0gYVxuICAgIH1cbiAgfVxuICBsaXN0Lmxlbmd0aCA9IHB0clxuICByZXR1cm4gbGlzdFxufVxuXG5mdW5jdGlvbiB1bmlxdWUobGlzdCwgY29tcGFyZSwgc29ydGVkKSB7XG4gIGlmKGxpc3QubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIFtdXG4gIH1cbiAgaWYoY29tcGFyZSkge1xuICAgIGlmKCFzb3J0ZWQpIHtcbiAgICAgIGxpc3Quc29ydChjb21wYXJlKVxuICAgIH1cbiAgICByZXR1cm4gdW5pcXVlX3ByZWQobGlzdCwgY29tcGFyZSlcbiAgfVxuICBpZighc29ydGVkKSB7XG4gICAgbGlzdC5zb3J0KClcbiAgfVxuICByZXR1cm4gdW5pcXVlX2VxKGxpc3QpXG59XG5cbm1vZHVsZS5leHBvcnRzID0gdW5pcXVlIiwiXCJ1c2Ugc3RyaWN0XCJcblxuZnVuY3Rpb24gbG93ZXJCb3VuZF9jbXAoYXJyYXksIHZhbHVlLCBjb21wYXJlLCBsbywgaGkpIHtcbiAgbG8gPSBsb3wwXG4gIGhpID0gaGl8MFxuICB3aGlsZShsbyA8IGhpKSB7XG4gICAgdmFyIG0gPSAobG8gKyBoaSkgPj4+IDFcbiAgICAgICwgdiA9IGNvbXBhcmUodmFsdWUsIGFycmF5W21dKVxuICAgIGlmKHYgPCAwKSB7XG4gICAgICBoaSA9IG0tMVxuICAgIH0gZWxzZSBpZih2ID4gMCkge1xuICAgICAgbG8gPSBtKzFcbiAgICB9IGVsc2Uge1xuICAgICAgaGkgPSBtXG4gICAgfVxuICB9XG4gIGlmKGNvbXBhcmUoYXJyYXlbbG9dLCB2YWx1ZSkgPD0gMCkge1xuICAgIHJldHVybiBsb1xuICB9XG4gIHJldHVybiBsbyAtIDFcbn1cblxuZnVuY3Rpb24gbG93ZXJCb3VuZF9kZWYoYXJyYXksIHZhbHVlLCBsbywgaGkpIHtcbiAgbG8gPSBsb3wwXG4gIGhpID0gaGl8MFxuICB3aGlsZShsbyA8IGhpKSB7XG4gICAgdmFyIG0gPSAobG8gKyBoaSkgPj4+IDFcbiAgICBpZih2YWx1ZSA8IGFycmF5W21dKSB7XG4gICAgICBoaSA9IG0tMVxuICAgIH0gZWxzZSBpZih2YWx1ZSA+IGFycmF5W21dKSB7XG4gICAgICBsbyA9IG0rMVxuICAgIH0gZWxzZSB7XG4gICAgICBoaSA9IG1cbiAgICB9XG4gIH1cbiAgaWYoYXJyYXlbbG9dIDw9IHZhbHVlKSB7XG4gICAgcmV0dXJuIGxvXG4gIH1cbiAgcmV0dXJuIGxvIC0gMVxufVxuXG5mdW5jdGlvbiBsb3dlckJvdW5kKGFycmF5LCB2YWx1ZSwgY29tcGFyZSwgbG8sIGhpKSB7XG4gIGlmKCFsbykge1xuICAgIGxvID0gMFxuICB9XG4gIGlmKHR5cGVvZihoaSkgIT09IFwibnVtYmVyXCIpIHtcbiAgICBoaSA9IGFycmF5Lmxlbmd0aC0xXG4gIH1cbiAgaWYoY29tcGFyZSkge1xuICAgIHJldHVybiBsb3dlckJvdW5kX2NtcChhcnJheSwgdmFsdWUsIGNvbXBhcmUsIGxvLCBoaSlcbiAgfVxuICByZXR1cm4gbG93ZXJCb3VuZF9kZWYoYXJyYXksIHZhbHVlLCBsbywgaGkpXG59XG5cbm1vZHVsZS5leHBvcnRzID0gbG93ZXJCb3VuZCIsIlwidXNlIHN0cmljdFwiXG5cbmZ1bmN0aW9uIGlvdGEobikge1xuICB2YXIgcmVzdWx0ID0gbmV3IEFycmF5KG4pXG4gIGZvcih2YXIgaT0wOyBpPG47ICsraSkge1xuICAgIHJlc3VsdFtpXSA9IGlcbiAgfVxuICByZXR1cm4gcmVzdWx0XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaW90YSJdfQ== | |
;var shell = require("game-shell")() | |
var context | |
, player_x = 250 | |
, player_y = 250 | |
//Bind keyboard commands | |
shell.bind("move-left", "left", "A") | |
shell.bind("move-right", "right", "D") | |
shell.bind("move-up", "up", "W") | |
shell.bind("move-down", "down", "S") | |
//Fired when document is loaded | |
shell.on("init", function() { | |
var canvas = document.createElement("canvas") | |
canvas.width = 500 | |
canvas.height = 500 | |
shell.element.appendChild(canvas) | |
context = canvas.getContext("2d") | |
}) | |
//Fired once per game tick | |
shell.on("tick", function() { | |
if(shell.wasDown("move-left")) { | |
player_x -= 1 | |
} | |
if(shell.wasDown("move-right")) { | |
player_x += 1 | |
} | |
if(shell.wasDown("move-up")) { | |
player_y -= 1 | |
} | |
if(shell.wasDown("move-down")) { | |
player_y += 1 | |
} | |
}) | |
//Render a frame | |
shell.on("render", function(frame_time) { | |
context.fillStyle = "#000" | |
context.fillRect(0, 0, 500, 500) | |
context.fillStyle = "#f00" | |
context.fillRect(player_x-10, player_y-10, 20, 20) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment