Last active
July 30, 2016 01:04
-
-
Save ninabreznik/9afe8f54387de8603daae73796a0f22b to your computer and use it in GitHub Desktop.
requirebin sketch
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 | |
--------------------------------------------------------------------------------*/ | |
var yo = require('yo-yo') | |
var csjs = require('csjs-inject') | |
var minixhr = require('minixhr') | |
var chart = require('chart.js') | |
/*-------------------------------------------------------------------------------- | |
LOADING COLORS | |
--------------------------------------------------------------------------------*/ | |
var yellow = "#C2B97F" | |
var white = "#F2F7F2" | |
var violet = "#8E5572" | |
var lightBrown = "#BCAA99" | |
var darkBrown = "#88665D" | |
/*-------------------------------------------------------------------------------- | |
LOADING FONT | |
--------------------------------------------------------------------------------*/ | |
var links = ['https://fonts.googleapis.com/css?family=Kaushan+Script'] | |
var font = yo`<link href=${links[0]} rel='stylesheet' type='text/css'>` | |
document.head.appendChild(font) | |
/*-------------------------------------------------------------------------------- | |
QUESTIONS | |
--------------------------------------------------------------------------------*/ | |
var questions = [ | |
` | |
Statement #1: | |
I had no previous knowledge of programming before eSova. | |
`, | |
` | |
Statement #2: | |
I started learning programming because I want to build my start-up. | |
`, | |
` | |
Statement #3: | |
I believe that with this knowledge I significantly improve my career opportunities. | |
`, | |
` | |
Statement #4: | |
I hope eSova team will offer a new tutorial for advanced learners. | |
`, | |
` | |
Statement #5: | |
I learned a lot during this tutorial. | |
`, | |
` | |
Statement #6: | |
I will definitely continue with coding. | |
`, | |
` | |
Statement #7: | |
I plan to continue learning programming. | |
` | |
] | |
/*-------------------------------------------------------------------------------- | |
VARIABLES | |
--------------------------------------------------------------------------------*/ | |
var i = 0 | |
var question = questions[i] | |
var result = "Click to see results" | |
var results = [] | |
var answerOptions = [1,2,3,4,5,6] | |
/*-------------------------------------------------------------------------------- QUIZ COMPONENT | |
--------------------------------------------------------------------------------*/ | |
function quizComponent () { | |
var css = csjs` | |
.quiz { | |
background-color: ${yellow}; | |
text-align: center; | |
font-family: 'Kaushan Script', cursive; | |
padding-bottom: 200px; | |
} | |
.welcome { | |
font-size: 4em; | |
padding: 50px; | |
color: ${darkBrown} | |
} | |
.question { | |
font-size: 2em; | |
color: ${white}; | |
padding: 5%; | |
margin: 0 15%; | |
} | |
.answers { | |
display: flex; | |
justify-content: center; | |
flex-wrap: wrap; | |
margin: 0 5%; | |
} | |
.answer { | |
background-color: ${violet}; | |
padding: 15px; | |
margin: 5px; | |
border: 2px solid ${white}; | |
border-radius: 30%; | |
} | |
.answer:hover { | |
background-color: ${lightBrown}; | |
cursor: pointer; | |
} | |
.instruction { | |
color: ${violet}; | |
font-size: 1em; | |
margin: 0 15%; | |
padding: 20px; | |
} | |
.results { | |
background-color: ${white}; | |
text-align: center; | |
font-family: 'Kaushan Script', cursive; | |
padding-bottom: 400px; | |
} | |
.resultTitle{ | |
font-size: 4em; | |
padding: 50px; | |
color: ${darkBrown} | |
} | |
.back { | |
display: flex; | |
justify-content: center; | |
} | |
.backImg { | |
height: 30px; | |
padding: 5px; | |
} | |
.backText { | |
color: ${white}; | |
font-size: 25px; | |
} | |
.showChart { | |
font-size: 2em; | |
color: ${violet}; | |
margin: 35px; | |
} | |
.showChart:hover { | |
color: ${yellow}; | |
cursor: pointer; | |
} | |
` | |
var html = template() | |
document.body.appendChild(html) | |
return html | |
function template () { | |
return yo` | |
<div class="${css.quiz}"> | |
<div class="${css.welcome}">Welcome to my quiz!</div> | |
<div class="${css.question}">${question}</div> | |
<div class="${css.answers}"> | |
${answerOptions.map(x=>yo` | |
<div class="${css.answer}" onclick=${nextQuestion(x)}>${x}</div> | |
`)} | |
</div> | |
<div class="${css.instruction}"> | |
Choose how strongly do you agree with the statement<br> | |
(1 - don't agree at all, 6 - completely agree) | |
</div> | |
<div class="${css.back}" onclick=${back}> | |
<img src="http://i.imgur.com/L6kXXEi.png" class="${css.backImg}"> | |
<div class="${css.backText}">Back</div> | |
</div> | |
</div> | |
` | |
} | |
function nextQuestion(id) { | |
return function () { | |
if (i < (questions.length-1)) { | |
results[i] = id | |
i = i+1 | |
question = questions[i] | |
yo.update(html, template()) | |
} else { | |
results[i] = id | |
sendData(results) | |
yo.update(html, seeResults(results)) | |
} | |
} | |
} | |
function seeResults(data) { | |
var ctx = yo` | |
<canvas class="${css.myChart}"></canvas> | |
` | |
return yo` | |
<div class="${css.results}"> | |
<div class="${css.resultTitle}">Compare your answers with others</div> | |
<div class="${css.showChart}" | |
onclick=${function(){createChart(ctx, data)}}> | |
Click to see the chart | |
</div> | |
${ctx} | |
</div> | |
` | |
} | |
function createChart(ctx, myData) { | |
minixhr('https://test-ceff2.firebaseio.com/results.json', responseHandler) | |
function responseHandler (data, response, xhr, header) { | |
var data = JSON.parse(data) | |
var keys = Object.keys(data) | |
var arrayOfAnswers = keys.map(x=>data[x]) | |
var stats = arrayOfAnswers.reduce(function(currentResult,answer,i) { | |
var newResult=currentResult.map((x,count)=>(x*(i+1)+answer[count])/(i+2)) | |
return newResult | |
}, myData) | |
var data = { | |
labels: [ | |
"Statement #1", "Statement #2", "Statement #3", | |
"Statement #4", "Statement #5", "Statement #6", "Statement #7" | |
], | |
datasets: [ | |
{ | |
label: "My statments", | |
backgroundColor: "rgba(179,181,198,0.2)", | |
borderColor: "rgba(179,181,198,1)", | |
pointBackgroundColor: "rgba(179,181,198,1)", | |
pointBorderColor: "#fff", | |
pointHoverBackgroundColor: "#fff", | |
pointHoverBorderColor: "rgba(179,181,198,1)", | |
data: myData | |
}, | |
{ | |
label: "Others' statements", | |
backgroundColor: "rgba(255,99,132,0.2)", | |
borderColor: "rgba(255,99,132,1)", | |
pointBackgroundColor: "rgba(255,99,132,1)", | |
pointBorderColor: "#fff", | |
pointHoverBackgroundColor: "#fff", | |
pointHoverBorderColor: "rgba(255,99,132,1)", | |
data: stats | |
} | |
] | |
} | |
var myChart = new Chart(ctx, { | |
type: 'radar', | |
data: data, | |
options: { | |
scale: { | |
scale: [1,2,3,4,5,6], | |
ticks: { | |
beginAtZero: true | |
} | |
} | |
} | |
}) | |
} | |
} | |
function back() { | |
if (i > 0) { | |
i = i-1 | |
question = questions[i] | |
yo.update(html, template()) | |
} | |
} | |
function sendData(results) { | |
var request = { | |
url : 'https://test-ceff2.firebaseio.com/results.json', | |
method : 'POST', | |
data : JSON.stringify(results) | |
} | |
minixhr(request) | |
} | |
} | |
quizComponent() |
This file has been truncated, but you can view the full file.
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
setTimeout(function(){ | |
;require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
},{}],2:[function(require,module,exports){ | |
var document = require('global/document') | |
var hyperx = require('hyperx') | |
var onload = require('on-load') | |
var SVGNS = 'http://www.w3.org/2000/svg' | |
var BOOL_PROPS = { | |
autofocus: 1, | |
checked: 1, | |
defaultchecked: 1, | |
disabled: 1, | |
formnovalidate: 1, | |
indeterminate: 1, | |
readonly: 1, | |
required: 1, | |
selected: 1, | |
willvalidate: 1 | |
} | |
var SVG_TAGS = [ | |
'svg', | |
'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', | |
'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', | |
'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', | |
'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', | |
'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', | |
'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', | |
'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', | |
'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', | |
'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', | |
'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', | |
'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', | |
'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', | |
'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', | |
'tspan', 'use', 'view', 'vkern' | |
] | |
function belCreateElement (tag, props, children) { | |
var el | |
// If an svg tag, it needs a namespace | |
if (SVG_TAGS.indexOf(tag) !== -1) { | |
props.namespace = SVGNS | |
} | |
// If we are using a namespace | |
var ns = false | |
if (props.namespace) { | |
ns = props.namespace | |
delete props.namespace | |
} | |
// Create the element | |
if (ns) { | |
el = document.createElementNS(ns, tag) | |
} else { | |
el = document.createElement(tag) | |
} | |
// If adding onload events | |
if (props.onload || props.onunload) { | |
var load = props.onload || function () {} | |
var unload = props.onunload || function () {} | |
onload(el, function bel_onload () { | |
load(el) | |
}, function bel_onunload () { | |
unload(el) | |
}, | |
// We have to use non-standard `caller` to find who invokes `belCreateElement` | |
belCreateElement.caller.caller.caller) | |
delete props.onload | |
delete props.onunload | |
} | |
// Create the properties | |
for (var p in props) { | |
if (props.hasOwnProperty(p)) { | |
var key = p.toLowerCase() | |
var val = props[p] | |
// Normalize className | |
if (key === 'classname') { | |
key = 'class' | |
p = 'class' | |
} | |
// The for attribute gets transformed to htmlFor, but we just set as for | |
if (p === 'htmlFor') { | |
p = 'for' | |
} | |
// If a property is boolean, set itself to the key | |
if (BOOL_PROPS[key]) { | |
if (val === 'true') val = key | |
else if (val === 'false') continue | |
} | |
// If a property prefers being set directly vs setAttribute | |
if (key.slice(0, 2) === 'on') { | |
el[p] = val | |
} else { | |
if (ns) { | |
el.setAttributeNS(null, p, val) | |
} else { | |
el.setAttribute(p, val) | |
} | |
} | |
} | |
} | |
function appendChild (childs) { | |
if (!Array.isArray(childs)) return | |
for (var i = 0; i < childs.length; i++) { | |
var node = childs[i] | |
if (Array.isArray(node)) { | |
appendChild(node) | |
continue | |
} | |
if (typeof node === 'number' || | |
typeof node === 'boolean' || | |
node instanceof Date || | |
node instanceof RegExp) { | |
node = node.toString() | |
} | |
if (typeof node === 'string') { | |
if (el.lastChild && el.lastChild.nodeName === '#text') { | |
el.lastChild.nodeValue += node | |
continue | |
} | |
node = document.createTextNode(node) | |
} | |
if (node && node.nodeType) { | |
el.appendChild(node) | |
} | |
} | |
} | |
appendChild(children) | |
return el | |
} | |
module.exports = hyperx(belCreateElement) | |
module.exports.createElement = belCreateElement | |
},{"global/document":3,"hyperx":5,"on-load":7}],3:[function(require,module,exports){ | |
(function (global){ | |
var topLevel = typeof global !== 'undefined' ? global : | |
typeof window !== 'undefined' ? window : {} | |
var minDoc = require('min-document'); | |
if (typeof document !== 'undefined') { | |
module.exports = document; | |
} else { | |
var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | |
if (!doccy) { | |
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | |
} | |
module.exports = doccy; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9iZWwvbm9kZV9tb2R1bGVzL2dsb2JhbC9kb2N1bWVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbInZhciB0b3BMZXZlbCA9IHR5cGVvZiBnbG9iYWwgIT09ICd1bmRlZmluZWQnID8gZ2xvYmFsIDpcbiAgICB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IHt9XG52YXIgbWluRG9jID0gcmVxdWlyZSgnbWluLWRvY3VtZW50Jyk7XG5cbmlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBkb2N1bWVudDtcbn0gZWxzZSB7XG4gICAgdmFyIGRvY2N5ID0gdG9wTGV2ZWxbJ19fR0xPQkFMX0RPQ1VNRU5UX0NBQ0hFQDQnXTtcblxuICAgIGlmICghZG9jY3kpIHtcbiAgICAgICAgZG9jY3kgPSB0b3BMZXZlbFsnX19HTE9CQUxfRE9DVU1FTlRfQ0FDSEVANCddID0gbWluRG9jO1xuICAgIH1cblxuICAgIG1vZHVsZS5leHBvcnRzID0gZG9jY3k7XG59XG4iXX0= | |
},{"min-document":1}],4:[function(require,module,exports){ | |
(function (global){ | |
if (typeof window !== "undefined") { | |
module.exports = window; | |
} else if (typeof global !== "undefined") { | |
module.exports = global; | |
} else if (typeof self !== "undefined"){ | |
module.exports = self; | |
} else { | |
module.exports = {}; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9iZWwvbm9kZV9tb2R1bGVzL2dsb2JhbC93aW5kb3cuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIG1vZHVsZS5leHBvcnRzID0gd2luZG93O1xufSBlbHNlIGlmICh0eXBlb2YgZ2xvYmFsICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBnbG9iYWw7XG59IGVsc2UgaWYgKHR5cGVvZiBzZWxmICE9PSBcInVuZGVmaW5lZFwiKXtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHNlbGY7XG59IGVsc2Uge1xuICAgIG1vZHVsZS5leHBvcnRzID0ge307XG59XG4iXX0= | |
},{}],5:[function(require,module,exports){ | |
var attrToProp = require('hyperscript-attribute-to-property') | |
var VAR = 0, TEXT = 1, OPEN = 2, CLOSE = 3, ATTR = 4 | |
var ATTR_KEY = 5, ATTR_KEY_W = 6 | |
var ATTR_VALUE_W = 7, ATTR_VALUE = 8 | |
var ATTR_VALUE_SQ = 9, ATTR_VALUE_DQ = 10 | |
var ATTR_EQ = 11, ATTR_BREAK = 12 | |
module.exports = function (h, opts) { | |
h = attrToProp(h) | |
if (!opts) opts = {} | |
var concat = opts.concat || function (a, b) { | |
return String(a) + String(b) | |
} | |
return function (strings) { | |
var state = TEXT, reg = '' | |
var arglen = arguments.length | |
var parts = [] | |
for (var i = 0; i < strings.length; i++) { | |
if (i < arglen - 1) { | |
var arg = arguments[i+1] | |
var p = parse(strings[i]) | |
var xstate = state | |
if (xstate === ATTR_VALUE_DQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_SQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_W) xstate = ATTR_VALUE | |
if (xstate === ATTR) xstate = ATTR_KEY | |
p.push([ VAR, xstate, arg ]) | |
parts.push.apply(parts, p) | |
} else parts.push.apply(parts, parse(strings[i])) | |
} | |
var tree = [null,{},[]] | |
var stack = [[tree,-1]] | |
for (var i = 0; i < parts.length; i++) { | |
var cur = stack[stack.length-1][0] | |
var p = parts[i], s = p[0] | |
if (s === OPEN && /^\//.test(p[1])) { | |
var ix = stack[stack.length-1][1] | |
if (stack.length > 1) { | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === OPEN) { | |
var c = [p[1],{},[]] | |
cur[2].push(c) | |
stack.push([c,cur[2].length-1]) | |
} else if (s === ATTR_KEY || (s === VAR && p[1] === ATTR_KEY)) { | |
var key = '' | |
var copyKey | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_KEY) { | |
key = concat(key, parts[i][1]) | |
} else if (parts[i][0] === VAR && parts[i][1] === ATTR_KEY) { | |
if (typeof parts[i][2] === 'object' && !key) { | |
for (copyKey in parts[i][2]) { | |
if (parts[i][2].hasOwnProperty(copyKey) && !cur[1][copyKey]) { | |
cur[1][copyKey] = parts[i][2][copyKey] | |
} | |
} | |
} else { | |
key = concat(key, parts[i][2]) | |
} | |
} else break | |
} | |
if (parts[i][0] === ATTR_EQ) i++ | |
var j = i | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_VALUE || parts[i][0] === ATTR_KEY) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][1]) | |
else cur[1][key] = concat(cur[1][key], parts[i][1]) | |
} else if (parts[i][0] === VAR | |
&& (parts[i][1] === ATTR_VALUE || parts[i][1] === ATTR_KEY)) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][2]) | |
else cur[1][key] = concat(cur[1][key], parts[i][2]) | |
} else { | |
if (key.length && !cur[1][key] && i === j | |
&& (parts[i][0] === CLOSE || parts[i][0] === ATTR_BREAK)) { | |
// https://html.spec.whatwg.org/multipage/infrastructure.html#boolean-attributes | |
// empty string is falsy, not well behaved value in browser | |
cur[1][key] = key.toLowerCase() | |
} | |
break | |
} | |
} | |
} else if (s === ATTR_KEY) { | |
cur[1][p[1]] = true | |
} else if (s === VAR && p[1] === ATTR_KEY) { | |
cur[1][p[2]] = true | |
} else if (s === CLOSE) { | |
if (selfClosing(cur[0]) && stack.length) { | |
var ix = stack[stack.length-1][1] | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === VAR && p[1] === TEXT) { | |
if (p[2] === undefined || p[2] === null) p[2] = '' | |
else if (!p[2]) p[2] = concat('', p[2]) | |
if (Array.isArray(p[2][0])) { | |
cur[2].push.apply(cur[2], p[2]) | |
} else { | |
cur[2].push(p[2]) | |
} | |
} else if (s === TEXT) { | |
cur[2].push(p[1]) | |
} else if (s === ATTR_EQ || s === ATTR_BREAK) { | |
// no-op | |
} else { | |
throw new Error('unhandled: ' + s) | |
} | |
} | |
if (tree[2].length > 1 && /^\s*$/.test(tree[2][0])) { | |
tree[2].shift() | |
} | |
if (tree[2].length > 2 | |
|| (tree[2].length === 2 && /\S/.test(tree[2][1]))) { | |
throw new Error( | |
'multiple root elements must be wrapped in an enclosing tag' | |
) | |
} | |
if (Array.isArray(tree[2][0]) && typeof tree[2][0][0] === 'string' | |
&& Array.isArray(tree[2][0][2])) { | |
tree[2][0] = h(tree[2][0][0], tree[2][0][1], tree[2][0][2]) | |
} | |
return tree[2][0] | |
function parse (str) { | |
var res = [] | |
if (state === ATTR_VALUE_W) state = ATTR | |
for (var i = 0; i < str.length; i++) { | |
var c = str.charAt(i) | |
if (state === TEXT && c === '<') { | |
if (reg.length) res.push([TEXT, reg]) | |
reg = '' | |
state = OPEN | |
} else if (c === '>' && !quot(state)) { | |
if (state === OPEN) { | |
res.push([OPEN,reg]) | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
} | |
res.push([CLOSE]) | |
reg = '' | |
state = TEXT | |
} else if (state === TEXT) { | |
reg += c | |
} else if (state === OPEN && /\s/.test(c)) { | |
res.push([OPEN, reg]) | |
reg = '' | |
state = ATTR | |
} else if (state === OPEN) { | |
reg += c | |
} else if (state === ATTR && /[\w-]/.test(c)) { | |
state = ATTR_KEY | |
reg = c | |
} else if (state === ATTR && /\s/.test(c)) { | |
if (reg.length) res.push([ATTR_KEY,reg]) | |
res.push([ATTR_BREAK]) | |
} else if (state === ATTR_KEY && /\s/.test(c)) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
state = ATTR_KEY_W | |
} else if (state === ATTR_KEY && c === '=') { | |
res.push([ATTR_KEY,reg],[ATTR_EQ]) | |
reg = '' | |
state = ATTR_VALUE_W | |
} else if (state === ATTR_KEY) { | |
reg += c | |
} else if ((state === ATTR_KEY_W || state === ATTR) && c === '=') { | |
res.push([ATTR_EQ]) | |
state = ATTR_VALUE_W | |
} else if ((state === ATTR_KEY_W || state === ATTR) && !/\s/.test(c)) { | |
res.push([ATTR_BREAK]) | |
if (/[\w-]/.test(c)) { | |
reg += c | |
state = ATTR_KEY | |
} else state = ATTR | |
} else if (state === ATTR_VALUE_W && c === '"') { | |
state = ATTR_VALUE_DQ | |
} else if (state === ATTR_VALUE_W && c === "'") { | |
state = ATTR_VALUE_SQ | |
} else if (state === ATTR_VALUE_DQ && c === '"') { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_SQ && c === "'") { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_W && !/\s/.test(c)) { | |
state = ATTR_VALUE | |
i-- | |
} else if (state === ATTR_VALUE && /\s/.test(c)) { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE || state === ATTR_VALUE_SQ | |
|| state === ATTR_VALUE_DQ) { | |
reg += c | |
} | |
} | |
if (state === TEXT && reg.length) { | |
res.push([TEXT,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_DQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_SQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
} | |
return res | |
} | |
} | |
function strfn (x) { | |
if (typeof x === 'function') return x | |
else if (typeof x === 'string') return x | |
else if (x && typeof x === 'object') return x | |
else return concat('', x) | |
} | |
} | |
function quot (state) { | |
return state === ATTR_VALUE_SQ || state === ATTR_VALUE_DQ | |
} | |
var hasOwn = Object.prototype.hasOwnProperty | |
function has (obj, key) { return hasOwn.call(obj, key) } | |
var closeRE = RegExp('^(' + [ | |
'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', | |
'frame', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', | |
'source', 'track', 'wbr', | |
// SVG TAGS | |
'animate', 'animateTransform', 'circle', 'cursor', 'desc', 'ellipse', | |
'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', | |
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', | |
'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', | |
'feGaussianBlur', 'feImage', 'feMergeNode', 'feMorphology', | |
'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', | |
'feTurbulence', 'font-face-format', 'font-face-name', 'font-face-uri', | |
'glyph', 'glyphRef', 'hkern', 'image', 'line', 'missing-glyph', 'mpath', | |
'path', 'polygon', 'polyline', 'rect', 'set', 'stop', 'tref', 'use', 'view', | |
'vkern' | |
].join('|') + ')(?:[\.#][a-zA-Z0-9\u007F-\uFFFF_:-]+)*$') | |
function selfClosing (tag) { return closeRE.test(tag) } | |
},{"hyperscript-attribute-to-property":6}],6:[function(require,module,exports){ | |
module.exports = attributeToProperty | |
var transform = { | |
'class': 'className', | |
'for': 'htmlFor', | |
'http-equiv': 'httpEquiv' | |
} | |
function attributeToProperty (h) { | |
return function (tagName, attrs, children) { | |
for (var attr in attrs) { | |
if (attr in transform) { | |
attrs[transform[attr]] = attrs[attr] | |
delete attrs[attr] | |
} | |
} | |
return h(tagName, attrs, children) | |
} | |
} | |
},{}],7:[function(require,module,exports){ | |
/* global MutationObserver */ | |
var document = require('global/document') | |
var window = require('global/window') | |
var watch = Object.create(null) | |
var KEY_ID = 'onloadid' + (new Date() % 9e6).toString(36) | |
var KEY_ATTR = 'data-' + KEY_ID | |
var INDEX = 0 | |
if (window && window.MutationObserver) { | |
var observer = new MutationObserver(function (mutations) { | |
if (Object.keys(watch).length < 1) return | |
for (var i = 0; i < mutations.length; i++) { | |
if (mutations[i].attributeName === KEY_ATTR) { | |
eachAttr(mutations[i], turnon, turnoff) | |
continue | |
} | |
eachMutation(mutations[i].removedNodes, turnoff) | |
eachMutation(mutations[i].addedNodes, turnon) | |
} | |
}) | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true, | |
attributes: true, | |
attributeOldValue: true, | |
attributeFilter: [KEY_ATTR] | |
}) | |
} | |
module.exports = function onload (el, on, off, caller) { | |
on = on || function () {} | |
off = off || function () {} | |
el.setAttribute(KEY_ATTR, 'o' + INDEX) | |
watch['o' + INDEX] = [on, off, 0, caller || onload.caller] | |
INDEX += 1 | |
return el | |
} | |
function turnon (index, el) { | |
if (watch[index][0] && watch[index][2] === 0) { | |
watch[index][0](el) | |
watch[index][2] = 1 | |
} | |
} | |
function turnoff (index, el) { | |
if (watch[index][1] && watch[index][2] === 1) { | |
watch[index][1](el) | |
watch[index][2] = 0 | |
} | |
} | |
function eachAttr (mutation, on, off) { | |
var newValue = mutation.target.getAttribute(KEY_ATTR) | |
if (sameOrigin(mutation.oldValue, newValue)) { | |
watch[newValue] = watch[mutation.oldValue] | |
return | |
} | |
if (watch[mutation.oldValue]) { | |
off(mutation.oldValue, mutation.target) | |
} | |
if (watch[newValue]) { | |
on(newValue, mutation.target) | |
} | |
} | |
function sameOrigin (oldValue, newValue) { | |
if (!oldValue || !newValue) return false | |
return watch[oldValue][3] === watch[newValue][3] | |
} | |
function eachMutation (nodes, fn) { | |
var keys = Object.keys(watch) | |
for (var i = 0; i < nodes.length; i++) { | |
if (nodes[i] && nodes[i].getAttribute && nodes[i].getAttribute(KEY_ATTR)) { | |
var onloadid = nodes[i].getAttribute(KEY_ATTR) | |
keys.forEach(function (k) { | |
if (onloadid === k) { | |
fn(k, nodes[i]) | |
} | |
}) | |
} | |
if (nodes[i].childNodes.length > 0) { | |
eachMutation(nodes[i].childNodes, fn) | |
} | |
} | |
} | |
},{"global/document":3,"global/window":4}],8:[function(require,module,exports){ | |
// Create a range object for efficently rendering strings to elements. | |
var range; | |
var testEl = (typeof document !== 'undefined') ? | |
document.body || document.createElement('div') : | |
{}; | |
var XHTML = 'http://www.w3.org/1999/xhtml'; | |
var ELEMENT_NODE = 1; | |
var TEXT_NODE = 3; | |
var COMMENT_NODE = 8; | |
// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32> | |
// (IE7+ support) <=IE7 does not support el.hasAttribute(name) | |
var hasAttributeNS; | |
if (testEl.hasAttributeNS) { | |
hasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttributeNS(namespaceURI, name); | |
}; | |
} else if (testEl.hasAttribute) { | |
hasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttribute(name); | |
}; | |
} else { | |
hasAttributeNS = function(el, namespaceURI, name) { | |
return !!el.getAttributeNode(name); | |
}; | |
} | |
function empty(o) { | |
for (var k in o) { | |
if (o.hasOwnProperty(k)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function toElement(str) { | |
if (!range && document.createRange) { | |
range = document.createRange(); | |
range.selectNode(document.body); | |
} | |
var fragment; | |
if (range && range.createContextualFragment) { | |
fragment = range.createContextualFragment(str); | |
} else { | |
fragment = document.createElement('body'); | |
fragment.innerHTML = str; | |
} | |
return fragment.childNodes[0]; | |
} | |
var specialElHandlers = { | |
/** | |
* Needed for IE. Apparently IE doesn't think that "selected" is an | |
* attribute when reading over the attributes using selectEl.attributes | |
*/ | |
OPTION: function(fromEl, toEl) { | |
fromEl.selected = toEl.selected; | |
if (fromEl.selected) { | |
fromEl.setAttribute('selected', ''); | |
} else { | |
fromEl.removeAttribute('selected', ''); | |
} | |
}, | |
/** | |
* The "value" attribute is special for the <input> element since it sets | |
* the initial value. Changing the "value" attribute without changing the | |
* "value" property will have no effect since it is only used to the set the | |
* initial value. Similar for the "checked" attribute, and "disabled". | |
*/ | |
INPUT: function(fromEl, toEl) { | |
fromEl.checked = toEl.checked; | |
if (fromEl.checked) { | |
fromEl.setAttribute('checked', ''); | |
} else { | |
fromEl.removeAttribute('checked'); | |
} | |
if (fromEl.value !== toEl.value) { | |
fromEl.value = toEl.value; | |
} | |
if (!hasAttributeNS(toEl, null, 'value')) { | |
fromEl.removeAttribute('value'); | |
} | |
fromEl.disabled = toEl.disabled; | |
if (fromEl.disabled) { | |
fromEl.setAttribute('disabled', ''); | |
} else { | |
fromEl.removeAttribute('disabled'); | |
} | |
}, | |
TEXTAREA: function(fromEl, toEl) { | |
var newValue = toEl.value; | |
if (fromEl.value !== newValue) { | |
fromEl.value = newValue; | |
} | |
if (fromEl.firstChild) { | |
fromEl.firstChild.nodeValue = newValue; | |
} | |
} | |
}; | |
function noop() {} | |
/** | |
* Returns true if two node's names and namespace URIs are the same. | |
* | |
* @param {Element} a | |
* @param {Element} b | |
* @return {boolean} | |
*/ | |
var compareNodeNames = function(a, b) { | |
return a.nodeName === b.nodeName && | |
a.namespaceURI === b.namespaceURI; | |
}; | |
/** | |
* Create an element, optionally with a known namespace URI. | |
* | |
* @param {string} name the element name, e.g. 'div' or 'svg' | |
* @param {string} [namespaceURI] the element's namespace URI, i.e. the value of | |
* its `xmlns` attribute or its inferred namespace. | |
* | |
* @return {Element} | |
*/ | |
function createElementNS(name, namespaceURI) { | |
return !namespaceURI || namespaceURI === XHTML ? | |
document.createElement(name) : | |
document.createElementNS(namespaceURI, name); | |
} | |
/** | |
* Loop over all of the attributes on the target node and make sure the original | |
* DOM node has the same attributes. If an attribute found on the original node | |
* is not on the new node then remove it from the original node. | |
* | |
* @param {Element} fromNode | |
* @param {Element} toNode | |
*/ | |
function morphAttrs(fromNode, toNode) { | |
var attrs = toNode.attributes; | |
var i; | |
var attr; | |
var attrName; | |
var attrNamespaceURI; | |
var attrValue; | |
var fromValue; | |
for (i = attrs.length - 1; i >= 0; i--) { | |
attr = attrs[i]; | |
attrName = attr.name; | |
attrValue = attr.value; | |
attrNamespaceURI = attr.namespaceURI; | |
if (attrNamespaceURI) { | |
attrName = attr.localName || attrName; | |
fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); | |
} else { | |
fromValue = fromNode.getAttribute(attrName); | |
} | |
if (fromValue !== attrValue) { | |
if (attrNamespaceURI) { | |
fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); | |
} else { | |
fromNode.setAttribute(attrName, attrValue); | |
} | |
} | |
} | |
// Remove any extra attributes found on the original DOM element that | |
// weren't found on the target element. | |
attrs = fromNode.attributes; | |
for (i = attrs.length - 1; i >= 0; i--) { | |
attr = attrs[i]; | |
if (attr.specified !== false) { | |
attrName = attr.name; | |
attrNamespaceURI = attr.namespaceURI; | |
if (!hasAttributeNS(toNode, attrNamespaceURI, attrNamespaceURI ? attrName = attr.localName || attrName : attrName)) { | |
if (attrNamespaceURI) { | |
fromNode.removeAttributeNS(attrNamespaceURI, attr.localName); | |
} else { | |
fromNode.removeAttribute(attrName); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Copies the children of one DOM element to another DOM element | |
*/ | |
function moveChildren(fromEl, toEl) { | |
var curChild = fromEl.firstChild; | |
while (curChild) { | |
var nextChild = curChild.nextSibling; | |
toEl.appendChild(curChild); | |
curChild = nextChild; | |
} | |
return toEl; | |
} | |
function defaultGetNodeKey(node) { | |
return node.id; | |
} | |
function morphdom(fromNode, toNode, options) { | |
if (!options) { | |
options = {}; | |
} | |
if (typeof toNode === 'string') { | |
if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML') { | |
var toNodeHtml = toNode; | |
toNode = document.createElement('html'); | |
toNode.innerHTML = toNodeHtml; | |
} else { | |
toNode = toElement(toNode); | |
} | |
} | |
// XXX optimization: if the nodes are equal, don't morph them | |
/* | |
if (fromNode.isEqualNode(toNode)) { | |
return fromNode; | |
} | |
*/ | |
var savedEls = {}; // Used to save off DOM elements with IDs | |
var unmatchedEls = {}; | |
var getNodeKey = options.getNodeKey || defaultGetNodeKey; | |
var onBeforeNodeAdded = options.onBeforeNodeAdded || noop; | |
var onNodeAdded = options.onNodeAdded || noop; | |
var onBeforeElUpdated = options.onBeforeElUpdated || options.onBeforeMorphEl || noop; | |
var onElUpdated = options.onElUpdated || noop; | |
var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop; | |
var onNodeDiscarded = options.onNodeDiscarded || noop; | |
var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || options.onBeforeMorphElChildren || noop; | |
var childrenOnly = options.childrenOnly === true; | |
var movedEls = []; | |
function removeNodeHelper(node, nestedInSavedEl) { | |
var id = getNodeKey(node); | |
// If the node has an ID then save it off since we will want | |
// to reuse it in case the target DOM tree has a DOM element | |
// with the same ID | |
if (id) { | |
savedEls[id] = node; | |
} else if (!nestedInSavedEl) { | |
// If we are not nested in a saved element then we know that this node has been | |
// completely discarded and will not exist in the final DOM. | |
onNodeDiscarded(node); | |
} | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
removeNodeHelper(curChild, nestedInSavedEl || id); | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
function walkDiscardedChildNodes(node) { | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
if (!getNodeKey(curChild)) { | |
// We only want to handle nodes that don't have an ID to avoid double | |
// walking the same saved element. | |
onNodeDiscarded(curChild); | |
// Walk recursively | |
walkDiscardedChildNodes(curChild); | |
} | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
function removeNode(node, parentNode, alreadyVisited) { | |
if (onBeforeNodeDiscarded(node) === false) { | |
return; | |
} | |
parentNode.removeChild(node); | |
if (alreadyVisited) { | |
if (!getNodeKey(node)) { | |
onNodeDiscarded(node); | |
walkDiscardedChildNodes(node); | |
} | |
} else { | |
removeNodeHelper(node); | |
} | |
} | |
function morphEl(fromEl, toEl, alreadyVisited, childrenOnly) { | |
var toElKey = getNodeKey(toEl); | |
if (toElKey) { | |
// If an element with an ID is being morphed then it is will be in the final | |
// DOM so clear it out of the saved elements collection | |
delete savedEls[toElKey]; | |
} | |
if (!childrenOnly) { | |
if (onBeforeElUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
morphAttrs(fromEl, toEl); | |
onElUpdated(fromEl); | |
if (onBeforeElChildrenUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
} | |
if (fromEl.nodeName !== 'TEXTAREA') { | |
var curToNodeChild = toEl.firstChild; | |
var curFromNodeChild = fromEl.firstChild; | |
var curToNodeId; | |
var fromNextSibling; | |
var toNextSibling; | |
var savedEl; | |
var unmatchedEl; | |
outer: while (curToNodeChild) { | |
toNextSibling = curToNodeChild.nextSibling; | |
curToNodeId = getNodeKey(curToNodeChild); | |
while (curFromNodeChild) { | |
var curFromNodeId = getNodeKey(curFromNodeChild); | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if (!alreadyVisited) { | |
if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) { | |
unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl); | |
morphEl(curFromNodeChild, unmatchedEl, alreadyVisited); | |
curFromNodeChild = fromNextSibling; | |
continue; | |
} | |
} | |
var curFromNodeType = curFromNodeChild.nodeType; | |
if (curFromNodeType === curToNodeChild.nodeType) { | |
var isCompatible = false; | |
// Both nodes being compared are Element nodes | |
if (curFromNodeType === ELEMENT_NODE) { | |
if (compareNodeNames(curFromNodeChild, curToNodeChild)) { | |
// We have compatible DOM elements | |
if (curFromNodeId || curToNodeId) { | |
// If either DOM element has an ID then we | |
// handle those differently since we want to | |
// match up by ID | |
if (curToNodeId === curFromNodeId) { | |
isCompatible = true; | |
} | |
} else { | |
isCompatible = true; | |
} | |
} | |
if (isCompatible) { | |
// We found compatible DOM elements so transform | |
// the current "from" node to match the current | |
// target DOM node. | |
morphEl(curFromNodeChild, curToNodeChild, alreadyVisited); | |
} | |
// Both nodes being compared are Text or Comment nodes | |
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) { | |
isCompatible = true; | |
// Simply update nodeValue on the original node to | |
// change the text value | |
curFromNodeChild.nodeValue = curToNodeChild.nodeValue; | |
} | |
if (isCompatible) { | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
continue outer; | |
} | |
} | |
// No compatible match so remove the old node from the DOM | |
// and continue trying to find a match in the original DOM | |
removeNode(curFromNodeChild, fromEl, alreadyVisited); | |
curFromNodeChild = fromNextSibling; | |
} | |
if (curToNodeId) { | |
if ((savedEl = savedEls[curToNodeId])) { | |
if (compareNodeNames(savedEl, curToNodeChild)) { | |
morphEl(savedEl, curToNodeChild, true); | |
// We want to append the saved element instead | |
curToNodeChild = savedEl; | |
} else { | |
delete savedEls[curToNodeId]; | |
onNodeDiscarded(savedEl); | |
} | |
} else { | |
// The current DOM element in the target tree has an ID | |
// but we did not find a match in any of the | |
// corresponding siblings. We just put the target | |
// element in the old DOM tree but if we later find an | |
// element in the old DOM tree that has a matching ID | |
// then we will replace the target element with the | |
// corresponding old element and morph the old element | |
unmatchedEls[curToNodeId] = curToNodeChild; | |
} | |
} | |
// If we got this far then we did not find a candidate match for | |
// our "to node" and we exhausted all of the children "from" | |
// nodes. Therefore, we will just append the current "to node" | |
// to the end | |
if (onBeforeNodeAdded(curToNodeChild) !== false) { | |
fromEl.appendChild(curToNodeChild); | |
onNodeAdded(curToNodeChild); | |
} | |
if (curToNodeChild.nodeType === ELEMENT_NODE && | |
(curToNodeId || curToNodeChild.firstChild)) { | |
// The element that was just added to the original DOM may | |
// have some nested elements with a key/ID that needs to be | |
// matched up with other elements. We'll add the element to | |
// a list so that we can later process the nested elements | |
// if there are any unmatched keyed elements that were | |
// discarded | |
movedEls.push(curToNodeChild); | |
} | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
} | |
// We have processed all of the "to nodes". If curFromNodeChild is | |
// non-null then we still have some from nodes left over that need | |
// to be removed | |
while (curFromNodeChild) { | |
fromNextSibling = curFromNodeChild.nextSibling; | |
removeNode(curFromNodeChild, fromEl, alreadyVisited); | |
curFromNodeChild = fromNextSibling; | |
} | |
} | |
var specialElHandler = specialElHandlers[fromEl.nodeName]; | |
if (specialElHandler) { | |
specialElHandler(fromEl, toEl); | |
} | |
} // END: morphEl(...) | |
var morphedNode = fromNode; | |
var morphedNodeType = morphedNode.nodeType; | |
var toNodeType = toNode.nodeType; | |
if (!childrenOnly) { | |
// Handle the case where we are given two DOM nodes that are not | |
// compatible (e.g. <div> --> <span> or <div> --> TEXT) | |
if (morphedNodeType === ELEMENT_NODE) { | |
if (toNodeType === ELEMENT_NODE) { | |
if (!compareNodeNames(fromNode, toNode)) { | |
onNodeDiscarded(fromNode); | |
morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); | |
} | |
} else { | |
// Going from an element node to a text node | |
morphedNode = toNode; | |
} | |
} else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node | |
if (toNodeType === morphedNodeType) { | |
morphedNode.nodeValue = toNode.nodeValue; | |
return morphedNode; | |
} else { | |
// Text node to something else | |
morphedNode = toNode; | |
} | |
} | |
} | |
if (morphedNode === toNode) { | |
// The "to node" was not compatible with the "from node" so we had to | |
// toss out the "from node" and use the "to node" | |
onNodeDiscarded(fromNode); | |
} else { | |
morphEl(morphedNode, toNode, false, childrenOnly); | |
/** | |
* What we will do here is walk the tree for the DOM element that was | |
* moved from the target DOM tree to the original DOM tree and we will | |
* look for keyed elements that could be matched to keyed elements that | |
* were earlier discarded. If we find a match then we will move the | |
* saved element into the final DOM tree. | |
*/ | |
var handleMovedEl = function(el) { | |
var curChild = el.firstChild; | |
while (curChild) { | |
var nextSibling = curChild.nextSibling; | |
var key = getNodeKey(curChild); | |
if (key) { | |
var savedEl = savedEls[key]; | |
if (savedEl && compareNodeNames(curChild, savedEl)) { | |
curChild.parentNode.replaceChild(savedEl, curChild); | |
// true: already visited the saved el tree | |
morphEl(savedEl, curChild, true); | |
curChild = nextSibling; | |
if (empty(savedEls)) { | |
return false; | |
} | |
continue; | |
} | |
} | |
if (curChild.nodeType === ELEMENT_NODE) { | |
handleMovedEl(curChild); | |
} | |
curChild = nextSibling; | |
} | |
}; | |
// The loop below is used to possibly match up any discarded | |
// elements in the original DOM tree with elemenets from the | |
// target tree that were moved over without visiting their | |
// children | |
if (!empty(savedEls)) { | |
handleMovedElsLoop: | |
while (movedEls.length) { | |
var movedElsTemp = movedEls; | |
movedEls = []; | |
for (var i=0; i<movedElsTemp.length; i++) { | |
if (handleMovedEl(movedElsTemp[i]) === false) { | |
// There are no more unmatched elements so completely end | |
// the loop | |
break handleMovedElsLoop; | |
} | |
} | |
} | |
} | |
// Fire the "onNodeDiscarded" event for any saved elements | |
// that never found a new home in the morphed DOM | |
for (var savedElId in savedEls) { | |
if (savedEls.hasOwnProperty(savedElId)) { | |
var savedEl = savedEls[savedElId]; | |
onNodeDiscarded(savedEl); | |
walkDiscardedChildNodes(savedEl); | |
} | |
} | |
} | |
if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) { | |
// If we had to swap out the from node with a new node because the old | |
// node was not compatible with the target node then we need to | |
// replace the old DOM node in the original DOM tree. This is only | |
// possible if the original DOM node was part of a DOM tree which | |
// we know is the case if it has a parent node. | |
fromNode.parentNode.replaceChild(morphedNode, fromNode); | |
} | |
return morphedNode; | |
} | |
module.exports = morphdom; | |
},{}],9:[function(require,module,exports){ | |
module.exports = [ | |
// attribute events (can be set with attributes) | |
'onclick', | |
'ondblclick', | |
'onmousedown', | |
'onmouseup', | |
'onmouseover', | |
'onmousemove', | |
'onmouseout', | |
'ondragstart', | |
'ondrag', | |
'ondragenter', | |
'ondragleave', | |
'ondragover', | |
'ondrop', | |
'ondragend', | |
'onkeydown', | |
'onkeypress', | |
'onkeyup', | |
'onunload', | |
'onabort', | |
'onerror', | |
'onresize', | |
'onscroll', | |
'onselect', | |
'onchange', | |
'onsubmit', | |
'onreset', | |
'onfocus', | |
'onblur', | |
'oninput', | |
// other common events | |
'oncontextmenu', | |
'onfocusin', | |
'onfocusout' | |
] | |
},{}],"yo-yo":[function(require,module,exports){ | |
var bel = require('bel') // turns template tag into DOM elements | |
var morphdom = require('morphdom') // efficiently diffs + morphs two DOM elements | |
var defaultEvents = require('./update-events.js') // default events to be copied when dom elements update | |
module.exports = bel | |
// TODO move this + defaultEvents to a new module once we receive more feedback | |
module.exports.update = function (fromNode, toNode, opts) { | |
if (!opts) opts = {} | |
if (opts.events !== false) { | |
if (!opts.onBeforeMorphEl) opts.onBeforeMorphEl = copier | |
} | |
return morphdom(fromNode, toNode, opts) | |
// morphdom only copies attributes. we decided we also wanted to copy events | |
// that can be set via attributes | |
function copier (f, t) { | |
// copy events: | |
var events = opts.events || defaultEvents | |
for (var i = 0; i < events.length; i++) { | |
var ev = events[i] | |
if (t[ev]) { // if new element has a whitelisted attribute | |
f[ev] = t[ev] // update existing element | |
} else if (f[ev]) { // if existing element has it and new one doesnt | |
f[ev] = undefined // remove it from existing element | |
} | |
} | |
// copy values for form elements | |
if ((f.nodeName === 'INPUT' && f.type !== 'file') || f.nodeName === 'TEXTAREA' || f.nodeName === 'SELECT') { | |
if (t.getAttribute('value') === null) t.value = f.value | |
} | |
} | |
} | |
},{"./update-events.js":9,"bel":2,"morphdom":8}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcmVzb2x2ZS9lbXB0eS5qcyIsIm5vZGVfbW9kdWxlcy9iZWwvaW5kZXguanMiLCJub2RlX21vZHVsZXMvYmVsL25vZGVfbW9kdWxlcy9nbG9iYWwvZG9jdW1lbnQuanMiLCJub2RlX21vZHVsZXMvYmVsL25vZGVfbW9kdWxlcy9nbG9iYWwvd2luZG93LmpzIiwibm9kZV9tb2R1bGVzL2JlbC9ub2RlX21vZHVsZXMvaHlwZXJ4L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2JlbC9ub2RlX21vZHVsZXMvaHlwZXJ4L25vZGVfbW9kdWxlcy9oeXBlcnNjcmlwdC1hdHRyaWJ1dGUtdG8tcHJvcGVydHkvaW5kZXguanMiLCJub2RlX21vZHVsZXMvYmVsL25vZGVfbW9kdWxlcy9vbi1sb2FkL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL21vcnBoZG9tL2xpYi9pbmRleC5qcyIsInVwZGF0ZS1ldmVudHMuanMiLCJpbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBOztBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1SUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2UUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNya0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsbnVsbCwidmFyIGRvY3VtZW50ID0gcmVxdWlyZSgnZ2xvYmFsL2RvY3VtZW50JylcbnZhciBoeXBlcnggPSByZXF1aXJlKCdoeXBlcngnKVxudmFyIG9ubG9hZCA9IHJlcXVpcmUoJ29uLWxvYWQnKVxuXG52YXIgU1ZHTlMgPSAnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnXG52YXIgQk9PTF9QUk9QUyA9IHtcbiAgYXV0b2ZvY3VzOiAxLFxuICBjaGVja2VkOiAxLFxuICBkZWZhdWx0Y2hlY2tlZDogMSxcbiAgZGlzYWJsZWQ6IDEsXG4gIGZvcm1ub3ZhbGlkYXRlOiAxLFxuICBpbmRldGVybWluYXRlOiAxLFxuICByZWFkb25seTogMSxcbiAgcmVxdWlyZWQ6IDEsXG4gIHNlbGVjdGVkOiAxLFxuICB3aWxsdmFsaWRhdGU6IDFcbn1cbnZhciBTVkdfVEFHUyA9IFtcbiAgJ3N2ZycsXG4gICdhbHRHbHlwaCcsICdhbHRHbHlwaERlZicsICdhbHRHbHlwaEl0ZW0nLCAnYW5pbWF0ZScsICdhbmltYXRlQ29sb3InLFxuICAnYW5pbWF0ZU1vdGlvbicsICdhbmltYXRlVHJhbnNmb3JtJywgJ2NpcmNsZScsICdjbGlwUGF0aCcsICdjb2xvci1wcm9maWxlJyxcbiAgJ2N1cnNvcicsICdkZWZzJywgJ2Rlc2MnLCAnZWxsaXBzZScsICdmZUJsZW5kJywgJ2ZlQ29sb3JNYXRyaXgnLFxuICAnZmVDb21wb25lbnRUcmFuc2ZlcicsICdmZUNvbXBvc2l0ZScsICdmZUNvbnZvbHZlTWF0cml4JywgJ2ZlRGlmZnVzZUxpZ2h0aW5nJyxcbiAgJ2ZlRGlzcGxhY2VtZW50TWFwJywgJ2ZlRGlzdGFudExpZ2h0JywgJ2ZlRmxvb2QnLCAnZmVGdW5jQScsICdmZUZ1bmNCJyxcbiAgJ2ZlRnVuY0cnLCAnZmVGdW5jUicsICdmZUdhdXNzaWFuQmx1cicsICdmZUltYWdlJywgJ2ZlTWVyZ2UnLCAnZmVNZXJnZU5vZGUnLFxuICAnZmVNb3JwaG9sb2d5JywgJ2ZlT2Zmc2V0JywgJ2ZlUG9pbnRMaWdodCcsICdmZVNwZWN1bGFyTGlnaHRpbmcnLFxuICAnZmVTcG90TGlnaHQnLCAnZmVUaWxlJywgJ2ZlVHVyYnVsZW5jZScsICdmaWx0ZXInLCAnZm9udCcsICdmb250LWZhY2UnLFxuICAnZm9udC1mYWNlLWZvcm1hdCcsICdmb250LWZhY2UtbmFtZScsICdmb250LWZhY2Utc3JjJywgJ2ZvbnQtZmFjZS11cmknLFxuICAnZm9yZWlnbk9iamVjdCcsICdnJywgJ2dseXBoJywgJ2dseXBoUmVmJywgJ2hrZXJuJywgJ2ltYWdlJywgJ2xpbmUnLFxuICAnbGluZWFyR3JhZGllbnQnLCAnbWFya2VyJywgJ21hc2snLCAnbWV0YWRhdGEnLCAnbWlzc2luZy1nbHlwaCcsICdtcGF0aCcsXG4gICdwYXRoJywgJ3BhdHRlcm4nLCAncG9seWdvbicsICdwb2x5bGluZScsICdyYWRpYWxHcmFkaWVudCcsICdyZWN0JyxcbiAgJ3NldCcsICdzdG9wJywgJ3N3aXRjaCcsICdzeW1ib2wnLCAndGV4dCcsICd0ZXh0UGF0aCcsICd0aXRsZScsICd0cmVmJyxcbiAgJ3RzcGFuJywgJ3VzZScsICd2aWV3JywgJ3ZrZXJuJ1xuXVxuXG5mdW5jdGlvbiBiZWxDcmVhdGVFbGVtZW50ICh0YWcsIHByb3BzLCBjaGlsZHJlbikge1xuICB2YXIgZWxcblxuICAvLyBJZiBhbiBzdmcgdGFnLCBpdCBuZWVkcyBhIG5hbWVzcGFjZVxuICBpZiAoU1ZHX1RBR1MuaW5kZXhPZih0YWcpICE9PSAtMSkge1xuICAgIHByb3BzLm5hbWVzcGFjZSA9IFNWR05TXG4gIH1cblxuICAvLyBJZiB3ZSBhcmUgdXNpbmcgYSBuYW1lc3BhY2VcbiAgdmFyIG5zID0gZmFsc2VcbiAgaWYgKHByb3BzLm5hbWVzcGFjZSkge1xuICAgIG5zID0gcHJvcHMubmFtZXNwYWNlXG4gICAgZGVsZXRlIHByb3BzLm5hbWVzcGFjZVxuICB9XG5cbiAgLy8gQ3JlYXRlIHRoZSBlbGVtZW50XG4gIGlmIChucykge1xuICAgIGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKG5zLCB0YWcpXG4gIH0gZWxzZSB7XG4gICAgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRhZylcbiAgfVxuXG4gIC8vIElmIGFkZGluZyBvbmxvYWQgZXZlbnRzXG4gIGlmIChwcm9wcy5vbmxvYWQgfHwgcHJvcHMub251bmxvYWQpIHtcbiAgICB2YXIgbG9hZCA9IHByb3BzLm9ubG9hZCB8fCBmdW5jdGlvbiAoKSB7fVxuICAgIHZhciB1bmxvYWQgPSBwcm9wcy5vbnVubG9hZCB8fCBmdW5jdGlvbiAoKSB7fVxuICAgIG9ubG9hZChlbCwgZnVuY3Rpb24gYmVsX29ubG9hZCAoKSB7XG4gICAgICBsb2FkKGVsKVxuICAgIH0sIGZ1bmN0aW9uIGJlbF9vbnVubG9hZCAoKSB7XG4gICAgICB1bmxvYWQoZWwpXG4gICAgfSxcbiAgICAvLyBXZSBoYXZlIHRvIHVzZSBub24tc3RhbmRhcmQgYGNhbGxlcmAgdG8gZmluZCB3aG8gaW52b2tlcyBgYmVsQ3JlYXRlRWxlbWVudGBcbiAgICBiZWxDcmVhdGVFbGVtZW50LmNhbGxlci5jYWxsZXIuY2FsbGVyKVxuICAgIGRlbGV0ZSBwcm9wcy5vbmxvYWRcbiAgICBkZWxldGUgcHJvcHMub251bmxvYWRcbiAgfVxuXG4gIC8vIENyZWF0ZSB0aGUgcHJvcGVydGllc1xuICBmb3IgKHZhciBwIGluIHByb3BzKSB7XG4gICAgaWYgKHByb3BzLmhhc093blByb3BlcnR5KHApKSB7XG4gICAgICB2YXIga2V5ID0gcC50b0xvd2VyQ2FzZSgpXG4gICAgICB2YXIgdmFsID0gcHJvcHNbcF1cbiAgICAgIC8vIE5vcm1hbGl6ZSBjbGFzc05hbWVcbiAgICAgIGlmIChrZXkgPT09ICdjbGFzc25hbWUnKSB7XG4gICAgICAgIGtleSA9ICdjbGFzcydcbiAgICAgICAgcCA9ICdjbGFzcydcbiAgICAgIH1cbiAgICAgIC8vIFRoZSBmb3IgYXR0cmlidXRlIGdldHMgdHJhbnNmb3JtZWQgdG8gaHRtbEZvciwgYnV0IHdlIGp1c3Qgc2V0IGFzIGZvclxuICAgICAgaWYgKHAgPT09ICdodG1sRm9yJykge1xuICAgICAgICBwID0gJ2ZvcidcbiAgICAgIH1cbiAgICAgIC8vIElmIGEgcHJvcGVydHkgaXMgYm9vbGVhbiwgc2V0IGl0c2VsZiB0byB0aGUga2V5XG4gICAgICBpZiAoQk9PTF9QUk9QU1trZXldKSB7XG4gICAgICAgIGlmICh2YWwgPT09ICd0cnVlJykgdmFsID0ga2V5XG4gICAgICAgIGVsc2UgaWYgKHZhbCA9PT0gJ2ZhbHNlJykgY29udGludWVcbiAgICAgIH1cbiAgICAgIC8vIElmIGEgcHJvcGVydHkgcHJlZmVycyBiZWluZyBzZXQgZGlyZWN0bHkgdnMgc2V0QXR0cmlidXRlXG4gICAgICBpZiAoa2V5LnNsaWNlKDAsIDIpID09PSAnb24nKSB7XG4gICAgICAgIGVsW3BdID0gdmFsXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAobnMpIHtcbiAgICAgICAgICBlbC5zZXRBdHRyaWJ1dGVOUyhudWxsLCBwLCB2YWwpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZWwuc2V0QXR0cmlidXRlKHAsIHZhbClcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGFwcGVuZENoaWxkIChjaGlsZHMpIHtcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkoY2hpbGRzKSkgcmV0dXJuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjaGlsZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBub2RlID0gY2hpbGRzW2ldXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShub2RlKSkge1xuICAgICAgICBhcHBlbmRDaGlsZChub2RlKVxuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuXG4gICAgICBpZiAodHlwZW9mIG5vZGUgPT09ICdudW1iZXInIHx8XG4gICAgICAgIHR5cGVvZiBub2RlID09PSAnYm9vbGVhbicgfHxcbiAgICAgICAgbm9kZSBpbnN0YW5jZW9mIERhdGUgfHxcbiAgICAgICAgbm9kZSBpbnN0YW5jZW9mIFJlZ0V4cCkge1xuICAgICAgICBub2RlID0gbm9kZS50b1N0cmluZygpXG4gICAgICB9XG5cbiAgICAgIGlmICh0eXBlb2Ygbm9kZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgaWYgKGVsLmxhc3RDaGlsZCAmJiBlbC5sYXN0Q2hpbGQubm9kZU5hbWUgPT09ICcjdGV4dCcpIHtcbiAgICAgICAgICBlbC5sYXN0Q2hpbGQubm9kZVZhbHVlICs9IG5vZGVcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShub2RlKVxuICAgICAgfVxuXG4gICAgICBpZiAobm9kZSAmJiBub2RlLm5vZGVUeXBlKSB7XG4gICAgICAgIGVsLmFwcGVuZENoaWxkKG5vZGUpXG4gICAgICB9XG4gICAgfVxuICB9XG4gIGFwcGVuZENoaWxkKGNoaWxkcmVuKVxuXG4gIHJldHVybiBlbFxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGh5cGVyeChiZWxDcmVhdGVFbGVtZW50KVxubW9kdWxlLmV4cG9ydHMuY3JlYXRlRWxlbWVudCA9IGJlbENyZWF0ZUVsZW1lbnRcbiIsIihmdW5jdGlvbiAoZ2xvYmFsKXtcbnZhciB0b3BMZXZlbCA9IHR5cGVvZiBnbG9iYWwgIT09ICd1bmRlZmluZWQnID8gZ2xvYmFsIDpcbiAgICB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IHt9XG52YXIgbWluRG9jID0gcmVxdWlyZSgnbWluLWRvY3VtZW50Jyk7XG5cbmlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBkb2N1bWVudDtcbn0gZWxzZSB7XG4gICAgdmFyIGRvY2N5ID0gdG9wTGV2ZWxbJ19fR0xPQkFMX0RPQ1VNRU5UX0NBQ0hFQDQnXTtcblxuICAgIGlmICghZG9jY3kpIHtcbiAgICAgICAgZG9jY3kgPSB0b3BMZXZlbFsnX19HTE9CQUxfRE9DVU1FTlRfQ0FDSEVANCddID0gbWluRG9jO1xuICAgIH1cblxuICAgIG1vZHVsZS5leHBvcnRzID0gZG9jY3k7XG59XG5cbn0pLmNhbGwodGhpcyx0eXBlb2YgZ2xvYmFsICE9PSBcInVuZGVmaW5lZFwiID8gZ2xvYmFsIDogdHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIgPyBzZWxmIDogdHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiA/IHdpbmRvdyA6IHt9KVxuLy8jIHNvdXJjZU1hcHBpbmdVUkw9ZGF0YTphcHBsaWNhdGlvbi9qc29uO2NoYXJzZXQ6dXRmLTg7YmFzZTY0LGV5SjJaWEp6YVc5dUlqb3pMQ0p6YjNWeVkyVnpJanBiSW01dlpHVmZiVzlrZFd4bGN5OWlaV3d2Ym05a1pWOXRiMlIxYkdWekwyZHNiMkpoYkM5a2IyTjFiV1Z1ZEM1cWN5SmRMQ0p1WVcxbGN5STZXMTBzSW0xaGNIQnBibWR6SWpvaU8wRkJRVUU3UVVGRFFUdEJRVU5CTzBGQlEwRTdRVUZEUVR0QlFVTkJPMEZCUTBFN1FVRkRRVHRCUVVOQk8wRkJRMEU3UVVGRFFUdEJRVU5CTzBGQlEwRTdRVUZEUVR0QlFVTkJPMEZCUTBFaUxDSm1hV3hsSWpvaVoyVnVaWEpoZEdWa0xtcHpJaXdpYzI5MWNtTmxVbTl2ZENJNklpSXNJbk52ZFhKalpYTkRiMjUwWlc1MElqcGJJblpoY2lCMGIzQk1aWFpsYkNBOUlIUjVjR1Z2WmlCbmJHOWlZV3dnSVQwOUlDZDFibVJsWm1sdVpXUW5JRDhnWjJ4dlltRnNJRHBjYmlBZ0lDQjBlWEJsYjJZZ2QybHVaRzkzSUNFOVBTQW5kVzVrWldacGJtVmtKeUEvSUhkcGJtUnZkeUE2SUh0OVhHNTJZWElnYldsdVJHOWpJRDBnY21WeGRXbHlaU2duYldsdUxXUnZZM1Z0Wlc1MEp5azdYRzVjYm1sbUlDaDBlWEJsYjJZZ1pHOWpkVzFsYm5RZ0lUMDlJQ2QxYm1SbFptbHVaV1FuS1NCN1hHNGdJQ0FnYlc5a2RXeGxMbVY0Y0c5eWRITWdQU0JrYjJOMWJXVnVkRHRjYm4wZ1pXeHpaU0I3WEc0Z0lDQWdkbUZ5SUdSdlkyTjVJRDBnZEc5d1RHVjJaV3hiSjE5ZlIweFBRa0ZNWDBSUFExVk5SVTVVWDBOQlEwaEZRRFFuWFR0Y2JseHVJQ0FnSUdsbUlDZ2haRzlqWTNrcElIdGNiaUFnSUNBZ0lDQWdaRzlqWTNrZ1BTQjBiM0JNWlhabGJGc25YMTlIVEU5Q1FVeGZSRTlEVlUxRlRsUmZRMEZEU0VWQU5DZGRJRDBnYldsdVJHOWpPMXh1SUNBZ0lIMWNibHh1SUNBZ0lHMXZaSFZzWlM1bGVIQnZjblJ6SUQwZ1pHOWpZM2s3WEc1OVhHNGlYWDA9IiwiKGZ1bmN0aW9uIChnbG9iYWwpe1xuaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHdpbmRvdztcbn0gZWxzZSBpZiAodHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIG1vZHVsZS5leHBvcnRzID0gZ2xvYmFsO1xufSBlbHNlIGlmICh0eXBlb2Ygc2VsZiAhPT0gXCJ1bmRlZmluZWRcIil7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBzZWxmO1xufSBlbHNlIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHt9O1xufVxuXG59KS5jYWxsKHRoaXMsdHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIiA/IGdsb2JhbCA6IHR5cGVvZiBzZWxmICE9PSBcInVuZGVmaW5lZFwiID8gc2VsZiA6IHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgPyB3aW5kb3cgOiB7fSlcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWRhdGE6YXBwbGljYXRpb24vanNvbjtjaGFyc2V0OnV0Zi04O2Jhc2U2NCxleUoyWlhKemFXOXVJam96TENKemIzVnlZMlZ6SWpwYkltNXZaR1ZmYlc5a2RXeGxjeTlpWld3dmJtOWtaVjl0YjJSMWJHVnpMMmRzYjJKaGJDOTNhVzVrYjNjdWFuTWlYU3dpYm1GdFpYTWlPbHRkTENKdFlYQndhVzVuY3lJNklqdEJRVUZCTzBGQlEwRTdRVUZEUVR0QlFVTkJPMEZCUTBFN1FVRkRRVHRCUVVOQk8wRkJRMEU3UVVGRFFUdEJRVU5CSWl3aVptbHNaU0k2SW1kbGJtVnlZWFJsWkM1cWN5SXNJbk52ZFhKalpWSnZiM1FpT2lJaUxDSnpiM1Z5WTJWelEyOXVkR1Z1ZENJNld5SnBaaUFvZEhsd1pXOW1JSGRwYm1SdmR5QWhQVDBnWENKMWJtUmxabWx1WldSY0lpa2dlMXh1SUNBZ0lHMXZaSFZzWlM1bGVIQnZjblJ6SUQwZ2QybHVaRzkzTzF4dWZTQmxiSE5sSUdsbUlDaDBlWEJsYjJZZ1oyeHZZbUZzSUNFOVBTQmNJblZ1WkdWbWFXNWxaRndpS1NCN1hHNGdJQ0FnYlc5a2RXeGxMbVY0Y0c5eWRITWdQU0JuYkc5aVlXdzdYRzU5SUdWc2MyVWdhV1lnS0hSNWNHVnZaaUJ6Wld4bUlDRTlQU0JjSW5WdVpHVm1hVzVsWkZ3aUtYdGNiaUFnSUNCdGIyUjFiR1V1Wlhod2IzSjBjeUE5SUhObGJHWTdYRzU5SUdWc2MyVWdlMXh1SUNBZ0lHMXZaSFZzWlM1bGVIQnZjblJ6SUQwZ2UzMDdYRzU5WEc0aVhYMD0iLCJ2YXIgYXR0clRvUHJvcCA9IHJlcXVpcmUoJ2h5cGVyc2NyaXB0LWF0dHJpYnV0ZS10by1wcm9wZXJ0eScpXG5cbnZhciBWQVIgPSAwLCBURVhUID0gMSwgT1BFTiA9IDIsIENMT1NFID0gMywgQVRUUiA9IDRcbnZhciBBVFRSX0tFWSA9IDUsIEFUVFJfS0VZX1cgPSA2XG52YXIgQVRUUl9WQUxVRV9XID0gNywgQVRUUl9WQUxVRSA9IDhcbnZhciBBVFRSX1ZBTFVFX1NRID0gOSwgQVRUUl9WQUxVRV9EUSA9IDEwXG52YXIgQVRUUl9FUSA9IDExLCBBVFRSX0JSRUFLID0gMTJcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaCwgb3B0cykge1xuICBoID0gYXR0clRvUHJvcChoKVxuICBpZiAoIW9wdHMpIG9wdHMgPSB7fVxuICB2YXIgY29uY2F0ID0gb3B0cy5jb25jYXQgfHwgZnVuY3Rpb24gKGEsIGIpIHtcbiAgICByZXR1cm4gU3RyaW5nKGEpICsgU3RyaW5nKGIpXG4gIH1cblxuICByZXR1cm4gZnVuY3Rpb24gKHN0cmluZ3MpIHtcbiAgICB2YXIgc3RhdGUgPSBURVhULCByZWcgPSAnJ1xuICAgIHZhciBhcmdsZW4gPSBhcmd1bWVudHMubGVuZ3RoXG4gICAgdmFyIHBhcnRzID0gW11cblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc3RyaW5ncy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGkgPCBhcmdsZW4gLSAxKSB7XG4gICAgICAgIHZhciBhcmcgPSBhcmd1bWVudHNbaSsxXVxuICAgICAgICB2YXIgcCA9IHBhcnNlKHN0cmluZ3NbaV0pXG4gICAgICAgIHZhciB4c3RhdGUgPSBzdGF0ZVxuICAgICAgICBpZiAoeHN0YXRlID09PSBBVFRSX1ZBTFVFX0RRKSB4c3RhdGUgPSBBVFRSX1ZBTFVFXG4gICAgICAgIGlmICh4c3RhdGUgPT09IEFUVFJfVkFMVUVfU1EpIHhzdGF0ZSA9IEFUVFJfVkFMVUVcbiAgICAgICAgaWYgKHhzdGF0ZSA9PT0gQVRUUl9WQUxVRV9XKSB4c3RhdGUgPSBBVFRSX1ZBTFVFXG4gICAgICAgIGlmICh4c3RhdGUgPT09IEFUVFIpIHhzdGF0ZSA9IEFUVFJfS0VZXG4gICAgICAgIHAucHVzaChbIFZBUiwgeHN0YXRlLCBhcmcgXSlcbiAgICAgICAgcGFydHMucHVzaC5hcHBseShwYXJ0cywgcClcbiAgICAgIH0gZWxzZSBwYXJ0cy5wdXNoLmFwcGx5KHBhcnRzLCBwYXJzZShzdHJpbmdzW2ldKSlcbiAgICB9XG5cbiAgICB2YXIgdHJlZSA9IFtudWxsLHt9LFtdXVxuICAgIHZhciBzdGFjayA9IFtbdHJlZSwtMV1dXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGN1ciA9IHN0YWNrW3N0YWNrLmxlbmd0aC0xXVswXVxuICAgICAgdmFyIHAgPSBwYXJ0c1tpXSwgcyA9IHBbMF1cbiAgICAgIGlmIChzID09PSBPUEVOICYmIC9eXFwvLy50ZXN0KHBbMV0pKSB7XG4gICAgICAgIHZhciBpeCA9IHN0YWNrW3N0YWNrLmxlbmd0aC0xXVsxXVxuICAgICAgICBpZiAoc3RhY2subGVuZ3RoID4gMSkge1xuICAgICAgICAgIHN0YWNrLnBvcCgpXG4gICAgICAgICAgc3RhY2tbc3RhY2subGVuZ3RoLTFdWzBdWzJdW2l4XSA9IGgoXG4gICAgICAgICAgICBjdXJbMF0sIGN1clsxXSwgY3VyWzJdLmxlbmd0aCA/IGN1clsyXSA6IHVuZGVmaW5lZFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChzID09PSBPUEVOKSB7XG4gICAgICAgIHZhciBjID0gW3BbMV0se30sW11dXG4gICAgICAgIGN1clsyXS5wdXNoKGMpXG4gICAgICAgIHN0YWNrLnB1c2goW2MsY3VyWzJdLmxlbmd0aC0xXSlcbiAgICAgIH0gZWxzZSBpZiAocyA9PT0gQVRUUl9LRVkgfHwgKHMgPT09IFZBUiAmJiBwWzFdID09PSBBVFRSX0tFWSkpIHtcbiAgICAgICAgdmFyIGtleSA9ICcnXG4gICAgICAgIHZhciBjb3B5S2V5XG4gICAgICAgIGZvciAoOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBpZiAocGFydHNbaV1bMF0gPT09IEFUVFJfS0VZKSB7XG4gICAgICAgICAgICBrZXkgPSBjb25jYXQoa2V5LCBwYXJ0c1tpXVsxXSlcbiAgICAgICAgICB9IGVsc2UgaWYgKHBhcnRzW2ldWzBdID09PSBWQVIgJiYgcGFydHNbaV1bMV0gPT09IEFUVFJfS0VZKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHBhcnRzW2ldWzJdID09PSAnb2JqZWN0JyAmJiAha2V5KSB7XG4gICAgICAgICAgICAgIGZvciAoY29weUtleSBpbiBwYXJ0c1tpXVsyXSkge1xuICAgICAgICAgICAgICAgIGlmIChwYXJ0c1tpXVsyXS5oYXNPd25Qcm9wZXJ0eShjb3B5S2V5KSAmJiAhY3VyWzFdW2NvcHlLZXldKSB7XG4gICAgICAgICAgICAgICAgICBjdXJbMV1bY29weUtleV0gPSBwYXJ0c1tpXVsyXVtjb3B5S2V5XVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAga2V5ID0gY29uY2F0KGtleSwgcGFydHNbaV1bMl0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGJyZWFrXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHBhcnRzW2ldWzBdID09PSBBVFRSX0VRKSBpKytcbiAgICAgICAgdmFyIGogPSBpXG4gICAgICAgIGZvciAoOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBpZiAocGFydHNbaV1bMF0gPT09IEFUVFJfVkFMVUUgfHwgcGFydHNbaV1bMF0gPT09IEFUVFJfS0VZKSB7XG4gICAgICAgICAgICBpZiAoIWN1clsxXVtrZXldKSBjdXJbMV1ba2V5XSA9IHN0cmZuKHBhcnRzW2ldWzFdKVxuICAgICAgICAgICAgZWxzZSBjdXJbMV1ba2V5XSA9IGNvbmNhdChjdXJbMV1ba2V5XSwgcGFydHNbaV1bMV0pXG4gICAgICAgICAgfSBlbHNlIGlmIChwYXJ0c1tpXVswXSA9PT0gVkFSXG4gICAgICAgICAgJiYgKHBhcnRzW2ldWzFdID09PSBBVFRSX1ZBTFVFIHx8IHBhcnRzW2ldWzFdID09PSBBVFRSX0tFWSkpIHtcbiAgICAgICAgICAgIGlmICghY3VyWzFdW2tleV0pIGN1clsxXVtrZXldID0gc3RyZm4ocGFydHNbaV1bMl0pXG4gICAgICAgICAgICBlbHNlIGN1clsxXVtrZXldID0gY29uY2F0KGN1clsxXVtrZXldLCBwYXJ0c1tpXVsyXSlcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKGtleS5sZW5ndGggJiYgIWN1clsxXVtrZXldICYmIGkgPT09IGpcbiAgICAgICAgICAgICYmIChwYXJ0c1tpXVswXSA9PT0gQ0xPU0UgfHwgcGFydHNbaV1bMF0gPT09IEFUVFJfQlJFQUspKSB7XG4gICAgICAgICAgICAgIC8vIGh0dHBzOi8vaHRtbC5zcGVjLndoYXR3Zy5vcmcvbXVsdGlwYWdlL2luZnJhc3RydWN0dXJlLmh0bWwjYm9vbGVhbi1hdHRyaWJ1dGVzXG4gICAgICAgICAgICAgIC8vIGVtcHR5IHN0cmluZyBpcyBmYWxzeSwgbm90IHdlbGwgYmVoYXZlZCB2YWx1ZSBpbiBicm93c2VyXG4gICAgICAgICAgICAgIGN1clsxXVtrZXldID0ga2V5LnRvTG93ZXJDYXNlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHMgPT09IEFUVFJfS0VZKSB7XG4gICAgICAgIGN1clsxXVtwWzFdXSA9IHRydWVcbiAgICAgIH0gZWxzZSBpZiAocyA9PT0gVkFSICYmIHBbMV0gPT09IEFUVFJfS0VZKSB7XG4gICAgICAgIGN1clsxXVtwWzJdXSA9IHRydWVcbiAgICAgIH0gZWxzZSBpZiAocyA9PT0gQ0xPU0UpIHtcbiAgICAgICAgaWYgKHNlbGZDbG9zaW5nKGN1clswXSkgJiYgc3RhY2subGVuZ3RoKSB7XG4gICAgICAgICAgdmFyIGl4ID0gc3RhY2tbc3RhY2subGVuZ3RoLTFdWzFdXG4gICAgICAgICAgc3RhY2sucG9wKClcbiAgICAgICAgICBzdGFja1tzdGFjay5sZW5ndGgtMV1bMF1bMl1baXhdID0gaChcbiAgICAgICAgICAgIGN1clswXSwgY3VyWzFdLCBjdXJbMl0ubGVuZ3RoID8gY3VyWzJdIDogdW5kZWZpbmVkXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHMgPT09IFZBUiAmJiBwWzFdID09PSBURVhUKSB7XG4gICAgICAgIGlmIChwWzJdID09PSB1bmRlZmluZWQgfHwgcFsyXSA9PT0gbnVsbCkgcFsyXSA9ICcnXG4gICAgICAgIGVsc2UgaWYgKCFwWzJdKSBwWzJdID0gY29uY2F0KCcnLCBwWzJdKVxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShwWzJdWzBdKSkge1xuICAgICAgICAgIGN1clsyXS5wdXNoLmFwcGx5KGN1clsyXSwgcFsyXSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjdXJbMl0ucHVzaChwWzJdKVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHMgPT09IFRFWFQpIHtcbiAgICAgICAgY3VyWzJdLnB1c2gocFsxXSlcbiAgICAgIH0gZWxzZSBpZiAocyA9PT0gQVRUUl9FUSB8fCBzID09PSBBVFRSX0JSRUFLKSB7XG4gICAgICAgIC8vIG5vLW9wXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3VuaGFuZGxlZDogJyArIHMpXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHRyZWVbMl0ubGVuZ3RoID4gMSAmJiAvXlxccyokLy50ZXN0KHRyZWVbMl1bMF0pKSB7XG4gICAgICB0cmVlWzJdLnNoaWZ0KClcbiAgICB9XG5cbiAgICBpZiAodHJlZVsyXS5sZW5ndGggPiAyXG4gICAgfHwgKHRyZWVbMl0ubGVuZ3RoID09PSAyICYmIC9cXFMvLnRlc3QodHJlZVsyXVsxXSkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtdWx0aXBsZSByb290IGVsZW1lbnRzIG11c3QgYmUgd3JhcHBlZCBpbiBhbiBlbmNsb3NpbmcgdGFnJ1xuICAgICAgKVxuICAgIH1cbiAgICBpZiAoQXJyYXkuaXNBcnJheSh0cmVlWzJdWzBdKSAmJiB0eXBlb2YgdHJlZVsyXVswXVswXSA9PT0gJ3N0cmluZydcbiAgICAmJiBBcnJheS5pc0FycmF5KHRyZWVbMl1bMF1bMl0pKSB7XG4gICAgICB0cmVlWzJdWzBdID0gaCh0cmVlWzJdWzBdWzBdLCB0cmVlWzJdWzBdWzFdLCB0cmVlWzJdWzBdWzJdKVxuICAgIH1cbiAgICByZXR1cm4gdHJlZVsyXVswXVxuXG4gICAgZnVuY3Rpb24gcGFyc2UgKHN0cikge1xuICAgICAgdmFyIHJlcyA9IFtdXG4gICAgICBpZiAoc3RhdGUgPT09IEFUVFJfVkFMVUVfVykgc3RhdGUgPSBBVFRSXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuICAgICAgICB2YXIgYyA9IHN0ci5jaGFyQXQoaSlcbiAgICAgICAgaWYgKHN0YXRlID09PSBURVhUICYmIGMgPT09ICc8Jykge1xuICAgICAgICAgIGlmIChyZWcubGVuZ3RoKSByZXMucHVzaChbVEVYVCwgcmVnXSlcbiAgICAgICAgICByZWcgPSAnJ1xuICAgICAgICAgIHN0YXRlID0gT1BFTlxuICAgICAgICB9IGVsc2UgaWYgKGMgPT09ICc+JyAmJiAhcXVvdChzdGF0ZSkpIHtcbiAgICAgICAgICBpZiAoc3RhdGUgPT09IE9QRU4pIHtcbiAgICAgICAgICAgIHJlcy5wdXNoKFtPUEVOLHJlZ10pXG4gICAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9LRVkpIHtcbiAgICAgICAgICAgIHJlcy5wdXNoKFtBVFRSX0tFWSxyZWddKVxuICAgICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFJfVkFMVUUgJiYgcmVnLmxlbmd0aCkge1xuICAgICAgICAgICAgcmVzLnB1c2goW0FUVFJfVkFMVUUscmVnXSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmVzLnB1c2goW0NMT1NFXSlcbiAgICAgICAgICByZWcgPSAnJ1xuICAgICAgICAgIHN0YXRlID0gVEVYVFxuICAgICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBURVhUKSB7XG4gICAgICAgICAgcmVnICs9IGNcbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gT1BFTiAmJiAvXFxzLy50ZXN0KGMpKSB7XG4gICAgICAgICAgcmVzLnB1c2goW09QRU4sIHJlZ10pXG4gICAgICAgICAgcmVnID0gJydcbiAgICAgICAgICBzdGF0ZSA9IEFUVFJcbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gT1BFTikge1xuICAgICAgICAgIHJlZyArPSBjXG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFIgJiYgL1tcXHctXS8udGVzdChjKSkge1xuICAgICAgICAgIHN0YXRlID0gQVRUUl9LRVlcbiAgICAgICAgICByZWcgPSBjXG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFIgJiYgL1xccy8udGVzdChjKSkge1xuICAgICAgICAgIGlmIChyZWcubGVuZ3RoKSByZXMucHVzaChbQVRUUl9LRVkscmVnXSlcbiAgICAgICAgICByZXMucHVzaChbQVRUUl9CUkVBS10pXG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFJfS0VZICYmIC9cXHMvLnRlc3QoYykpIHtcbiAgICAgICAgICByZXMucHVzaChbQVRUUl9LRVkscmVnXSlcbiAgICAgICAgICByZWcgPSAnJ1xuICAgICAgICAgIHN0YXRlID0gQVRUUl9LRVlfV1xuICAgICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBBVFRSX0tFWSAmJiBjID09PSAnPScpIHtcbiAgICAgICAgICByZXMucHVzaChbQVRUUl9LRVkscmVnXSxbQVRUUl9FUV0pXG4gICAgICAgICAgcmVnID0gJydcbiAgICAgICAgICBzdGF0ZSA9IEFUVFJfVkFMVUVfV1xuICAgICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBBVFRSX0tFWSkge1xuICAgICAgICAgIHJlZyArPSBjXG4gICAgICAgIH0gZWxzZSBpZiAoKHN0YXRlID09PSBBVFRSX0tFWV9XIHx8IHN0YXRlID09PSBBVFRSKSAmJiBjID09PSAnPScpIHtcbiAgICAgICAgICByZXMucHVzaChbQVRUUl9FUV0pXG4gICAgICAgICAgc3RhdGUgPSBBVFRSX1ZBTFVFX1dcbiAgICAgICAgfSBlbHNlIGlmICgoc3RhdGUgPT09IEFUVFJfS0VZX1cgfHwgc3RhdGUgPT09IEFUVFIpICYmICEvXFxzLy50ZXN0KGMpKSB7XG4gICAgICAgICAgcmVzLnB1c2goW0FUVFJfQlJFQUtdKVxuICAgICAgICAgIGlmICgvW1xcdy1dLy50ZXN0KGMpKSB7XG4gICAgICAgICAgICByZWcgKz0gY1xuICAgICAgICAgICAgc3RhdGUgPSBBVFRSX0tFWVxuICAgICAgICAgIH0gZWxzZSBzdGF0ZSA9IEFUVFJcbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9WQUxVRV9XICYmIGMgPT09ICdcIicpIHtcbiAgICAgICAgICBzdGF0ZSA9IEFUVFJfVkFMVUVfRFFcbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9WQUxVRV9XICYmIGMgPT09IFwiJ1wiKSB7XG4gICAgICAgICAgc3RhdGUgPSBBVFRSX1ZBTFVFX1NRXG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFJfVkFMVUVfRFEgJiYgYyA9PT0gJ1wiJykge1xuICAgICAgICAgIHJlcy5wdXNoKFtBVFRSX1ZBTFVFLHJlZ10sW0FUVFJfQlJFQUtdKVxuICAgICAgICAgIHJlZyA9ICcnXG4gICAgICAgICAgc3RhdGUgPSBBVFRSXG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFJfVkFMVUVfU1EgJiYgYyA9PT0gXCInXCIpIHtcbiAgICAgICAgICByZXMucHVzaChbQVRUUl9WQUxVRSxyZWddLFtBVFRSX0JSRUFLXSlcbiAgICAgICAgICByZWcgPSAnJ1xuICAgICAgICAgIHN0YXRlID0gQVRUUlxuICAgICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBBVFRSX1ZBTFVFX1cgJiYgIS9cXHMvLnRlc3QoYykpIHtcbiAgICAgICAgICBzdGF0ZSA9IEFUVFJfVkFMVUVcbiAgICAgICAgICBpLS1cbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9WQUxVRSAmJiAvXFxzLy50ZXN0KGMpKSB7XG4gICAgICAgICAgcmVzLnB1c2goW0FUVFJfVkFMVUUscmVnXSxbQVRUUl9CUkVBS10pXG4gICAgICAgICAgcmVnID0gJydcbiAgICAgICAgICBzdGF0ZSA9IEFUVFJcbiAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9WQUxVRSB8fCBzdGF0ZSA9PT0gQVRUUl9WQUxVRV9TUVxuICAgICAgICB8fCBzdGF0ZSA9PT0gQVRUUl9WQUxVRV9EUSkge1xuICAgICAgICAgIHJlZyArPSBjXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZSA9PT0gVEVYVCAmJiByZWcubGVuZ3RoKSB7XG4gICAgICAgIHJlcy5wdXNoKFtURVhULHJlZ10pXG4gICAgICAgIHJlZyA9ICcnXG4gICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBBVFRSX1ZBTFVFICYmIHJlZy5sZW5ndGgpIHtcbiAgICAgICAgcmVzLnB1c2goW0FUVFJfVkFMVUUscmVnXSlcbiAgICAgICAgcmVnID0gJydcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IEFUVFJfVkFMVUVfRFEgJiYgcmVnLmxlbmd0aCkge1xuICAgICAgICByZXMucHVzaChbQVRUUl9WQUxVRSxyZWddKVxuICAgICAgICByZWcgPSAnJ1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gQVRUUl9WQUxVRV9TUSAmJiByZWcubGVuZ3RoKSB7XG4gICAgICAgIHJlcy5wdXNoKFtBVFRSX1ZBTFVFLHJlZ10pXG4gICAgICAgIHJlZyA9ICcnXG4gICAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBBVFRSX0tFWSkge1xuICAgICAgICByZXMucHVzaChbQVRUUl9LRVkscmVnXSlcbiAgICAgICAgcmVnID0gJydcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzdHJmbiAoeCkge1xuICAgIGlmICh0eXBlb2YgeCA9PT0gJ2Z1bmN0aW9uJykgcmV0dXJuIHhcbiAgICBlbHNlIGlmICh0eXBlb2YgeCA9PT0gJ3N0cmluZycpIHJldHVybiB4XG4gICAgZWxzZSBpZiAoeCAmJiB0eXBlb2YgeCA9PT0gJ29iamVjdCcpIHJldHVybiB4XG4gICAgZWxzZSByZXR1cm4gY29uY2F0KCcnLCB4KVxuICB9XG59XG5cbmZ1bmN0aW9uIHF1b3QgKHN0YXRlKSB7XG4gIHJldHVybiBzdGF0ZSA9PT0gQVRUUl9WQUxVRV9TUSB8fCBzdGF0ZSA9PT0gQVRUUl9WQUxVRV9EUVxufVxuXG52YXIgaGFzT3duID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eVxuZnVuY3Rpb24gaGFzIChvYmosIGtleSkgeyByZXR1cm4gaGFzT3duLmNhbGwob2JqLCBrZXkpIH1cblxudmFyIGNsb3NlUkUgPSBSZWdFeHAoJ14oJyArIFtcbiAgJ2FyZWEnLCAnYmFzZScsICdiYXNlZm9udCcsICdiZ3NvdW5kJywgJ2JyJywgJ2NvbCcsICdjb21tYW5kJywgJ2VtYmVkJyxcbiAgJ2ZyYW1lJywgJ2hyJywgJ2ltZycsICdpbnB1dCcsICdpc2luZGV4JywgJ2tleWdlbicsICdsaW5rJywgJ21ldGEnLCAncGFyYW0nLFxuICAnc291cmNlJywgJ3RyYWNrJywgJ3dicicsXG4gIC8vIFNWRyBUQUdTXG4gICdhbmltYXRlJywgJ2FuaW1hdGVUcmFuc2Zvcm0nLCAnY2lyY2xlJywgJ2N1cnNvcicsICdkZXNjJywgJ2VsbGlwc2UnLFxuICAnZmVCbGVuZCcsICdmZUNvbG9yTWF0cml4JywgJ2ZlQ29tcG9uZW50VHJhbnNmZXInLCAnZmVDb21wb3NpdGUnLFxuICAnZmVDb252b2x2ZU1hdHJpeCcsICdmZURpZmZ1c2VMaWdodGluZycsICdmZURpc3BsYWNlbWVudE1hcCcsXG4gICdmZURpc3RhbnRMaWdodCcsICdmZUZsb29kJywgJ2ZlRnVuY0EnLCAnZmVGdW5jQicsICdmZUZ1bmNHJywgJ2ZlRnVuY1InLFxuICAnZmVHYXVzc2lhbkJsdXInLCAnZmVJbWFnZScsICdmZU1lcmdlTm9kZScsICdmZU1vcnBob2xvZ3knLFxuICAnZmVPZmZzZXQnLCAnZmVQb2ludExpZ2h0JywgJ2ZlU3BlY3VsYXJMaWdodGluZycsICdmZVNwb3RMaWdodCcsICdmZVRpbGUnLFxuICAnZmVUdXJidWxlbmNlJywgJ2ZvbnQtZmFjZS1mb3JtYXQnLCAnZm9udC1mYWNlLW5hbWUnLCAnZm9udC1mYWNlLXVyaScsXG4gICdnbHlwaCcsICdnbHlwaFJlZicsICdoa2VybicsICdpbWFnZScsICdsaW5lJywgJ21pc3NpbmctZ2x5cGgnLCAnbXBhdGgnLFxuICAncGF0aCcsICdwb2x5Z29uJywgJ3BvbHlsaW5lJywgJ3JlY3QnLCAnc2V0JywgJ3N0b3AnLCAndHJlZicsICd1c2UnLCAndmlldycsXG4gICd2a2Vybidcbl0uam9pbignfCcpICsgJykoPzpbXFwuI11bYS16QS1aMC05XFx1MDA3Ri1cXHVGRkZGXzotXSspKiQnKVxuZnVuY3Rpb24gc2VsZkNsb3NpbmcgKHRhZykgeyByZXR1cm4gY2xvc2VSRS50ZXN0KHRhZykgfVxuIiwibW9kdWxlLmV4cG9ydHMgPSBhdHRyaWJ1dGVUb1Byb3BlcnR5XG5cbnZhciB0cmFuc2Zvcm0gPSB7XG4gICdjbGFzcyc6ICdjbGFzc05hbWUnLFxuICAnZm9yJzogJ2h0bWxGb3InLFxuICAnaHR0cC1lcXVpdic6ICdodHRwRXF1aXYnXG59XG5cbmZ1bmN0aW9uIGF0dHJpYnV0ZVRvUHJvcGVydHkgKGgpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uICh0YWdOYW1lLCBhdHRycywgY2hpbGRyZW4pIHtcbiAgICBmb3IgKHZhciBhdHRyIGluIGF0dHJzKSB7XG4gICAgICBpZiAoYXR0ciBpbiB0cmFuc2Zvcm0pIHtcbiAgICAgICAgYXR0cnNbdHJhbnNmb3JtW2F0dHJdXSA9IGF0dHJzW2F0dHJdXG4gICAgICAgIGRlbGV0ZSBhdHRyc1thdHRyXVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gaCh0YWdOYW1lLCBhdHRycywgY2hpbGRyZW4pXG4gIH1cbn1cbiIsIi8qIGdsb2JhbCBNdXRhdGlvbk9ic2VydmVyICovXG52YXIgZG9jdW1lbnQgPSByZXF1aXJlKCdnbG9iYWwvZG9jdW1lbnQnKVxudmFyIHdpbmRvdyA9IHJlcXVpcmUoJ2dsb2JhbC93aW5kb3cnKVxudmFyIHdhdGNoID0gT2JqZWN0LmNyZWF0ZShudWxsKVxudmFyIEtFWV9JRCA9ICdvbmxvYWRpZCcgKyAobmV3IERhdGUoKSAlIDllNikudG9TdHJpbmcoMzYpXG52YXIgS0VZX0FUVFIgPSAnZGF0YS0nICsgS0VZX0lEXG52YXIgSU5ERVggPSAwXG5cbmlmICh3aW5kb3cgJiYgd2luZG93Lk11dGF0aW9uT2JzZXJ2ZXIpIHtcbiAgdmFyIG9ic2VydmVyID0gbmV3IE11dGF0aW9uT2JzZXJ2ZXIoZnVuY3Rpb24gKG11dGF0aW9ucykge1xuICAgIGlmIChPYmplY3Qua2V5cyh3YXRjaCkubGVuZ3RoIDwgMSkgcmV0dXJuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtdXRhdGlvbnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChtdXRhdGlvbnNbaV0uYXR0cmlidXRlTmFtZSA9PT0gS0VZX0FUVFIpIHtcbiAgICAgICAgZWFjaEF0dHIobXV0YXRpb25zW2ldLCB0dXJub24sIHR1cm5vZmYpXG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG4gICAgICBlYWNoTXV0YXRpb24obXV0YXRpb25zW2ldLnJlbW92ZWROb2RlcywgdHVybm9mZilcbiAgICAgIGVhY2hNdXRhdGlvbihtdXRhdGlvbnNbaV0uYWRkZWROb2RlcywgdHVybm9uKVxuICAgIH1cbiAgfSlcbiAgb2JzZXJ2ZXIub2JzZXJ2ZShkb2N1bWVudC5ib2R5LCB7XG4gICAgY2hpbGRMaXN0OiB0cnVlLFxuICAgIHN1YnRyZWU6IHRydWUsXG4gICAgYXR0cmlidXRlczogdHJ1ZSxcbiAgICBhdHRyaWJ1dGVPbGRWYWx1ZTogdHJ1ZSxcbiAgICBhdHRyaWJ1dGVGaWx0ZXI6IFtLRVlfQVRUUl1cbiAgfSlcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBvbmxvYWQgKGVsLCBvbiwgb2ZmLCBjYWxsZXIpIHtcbiAgb24gPSBvbiB8fCBmdW5jdGlvbiAoKSB7fVxuICBvZmYgPSBvZmYgfHwgZnVuY3Rpb24gKCkge31cbiAgZWwuc2V0QXR0cmlidXRlKEtFWV9BVFRSLCAnbycgKyBJTkRFWClcbiAgd2F0Y2hbJ28nICsgSU5ERVhdID0gW29uLCBvZmYsIDAsIGNhbGxlciB8fCBvbmxvYWQuY2FsbGVyXVxuICBJTkRFWCArPSAxXG4gIHJldHVybiBlbFxufVxuXG5mdW5jdGlvbiB0dXJub24gKGluZGV4LCBlbCkge1xuICBpZiAod2F0Y2hbaW5kZXhdWzBdICYmIHdhdGNoW2luZGV4XVsyXSA9PT0gMCkge1xuICAgIHdhdGNoW2luZGV4XVswXShlbClcbiAgICB3YXRjaFtpbmRleF1bMl0gPSAxXG4gIH1cbn1cblxuZnVuY3Rpb24gdHVybm9mZiAoaW5kZXgsIGVsKSB7XG4gIGlmICh3YXRjaFtpbmRleF1bMV0gJiYgd2F0Y2hbaW5kZXhdWzJdID09PSAxKSB7XG4gICAgd2F0Y2hbaW5kZXhdWzFdKGVsKVxuICAgIHdhdGNoW2luZGV4XVsyXSA9IDBcbiAgfVxufVxuXG5mdW5jdGlvbiBlYWNoQXR0ciAobXV0YXRpb24sIG9uLCBvZmYpIHtcbiAgdmFyIG5ld1ZhbHVlID0gbXV0YXRpb24udGFyZ2V0LmdldEF0dHJpYnV0ZShLRVlfQVRUUilcbiAgaWYgKHNhbWVPcmlnaW4obXV0YXRpb24ub2xkVmFsdWUsIG5ld1ZhbHVlKSkge1xuICAgIHdhdGNoW25ld1ZhbHVlXSA9IHdhdGNoW211dGF0aW9uLm9sZFZhbHVlXVxuICAgIHJldHVyblxuICB9XG4gIGlmICh3YXRjaFttdXRhdGlvbi5vbGRWYWx1ZV0pIHtcbiAgICBvZmYobXV0YXRpb24ub2xkVmFsdWUsIG11dGF0aW9uLnRhcmdldClcbiAgfVxuICBpZiAod2F0Y2hbbmV3VmFsdWVdKSB7XG4gICAgb24obmV3VmFsdWUsIG11dGF0aW9uLnRhcmdldClcbiAgfVxufVxuXG5mdW5jdGlvbiBzYW1lT3JpZ2luIChvbGRWYWx1ZSwgbmV3VmFsdWUpIHtcbiAgaWYgKCFvbGRWYWx1ZSB8fCAhbmV3VmFsdWUpIHJldHVybiBmYWxzZVxuICByZXR1cm4gd2F0Y2hbb2xkVmFsdWVdWzNdID09PSB3YXRjaFtuZXdWYWx1ZV1bM11cbn1cblxuZnVuY3Rpb24gZWFjaE11dGF0aW9uIChub2RlcywgZm4pIHtcbiAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyh3YXRjaClcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBub2Rlcy5sZW5ndGg7IGkrKykge1xuICAgIGlmIChub2Rlc1tpXSAmJiBub2Rlc1tpXS5nZXRBdHRyaWJ1dGUgJiYgbm9kZXNbaV0uZ2V0QXR0cmlidXRlKEtFWV9BVFRSKSkge1xuICAgICAgdmFyIG9ubG9hZGlkID0gbm9kZXNbaV0uZ2V0QXR0cmlidXRlKEtFWV9BVFRSKVxuICAgICAga2V5cy5mb3JFYWNoKGZ1bmN0aW9uIChrKSB7XG4gICAgICAgIGlmIChvbmxvYWRpZCA9PT0gaykge1xuICAgICAgICAgIGZuKGssIG5vZGVzW2ldKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH1cbiAgICBpZiAobm9kZXNbaV0uY2hpbGROb2Rlcy5sZW5ndGggPiAwKSB7XG4gICAgICBlYWNoTXV0YXRpb24obm9kZXNbaV0uY2hpbGROb2RlcywgZm4pXG4gICAgfVxuICB9XG59XG4iLCIvLyBDcmVhdGUgYSByYW5nZSBvYmplY3QgZm9yIGVmZmljZW50bHkgcmVuZGVyaW5nIHN0cmluZ3MgdG8gZWxlbWVudHMuXG52YXIgcmFuZ2U7XG5cbnZhciB0ZXN0RWwgPSAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJykgP1xuICAgIGRvY3VtZW50LmJvZHkgfHwgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JykgOlxuICAgIHt9O1xuXG52YXIgWEhUTUwgPSAnaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCc7XG52YXIgRUxFTUVOVF9OT0RFID0gMTtcbnZhciBURVhUX05PREUgPSAzO1xudmFyIENPTU1FTlRfTk9ERSA9IDg7XG5cbi8vIEZpeGVzIDxodHRwczovL2dpdGh1Yi5jb20vcGF0cmljay1zdGVlbGUtaWRlbS9tb3JwaGRvbS9pc3N1ZXMvMzI+XG4vLyAoSUU3KyBzdXBwb3J0KSA8PUlFNyBkb2VzIG5vdCBzdXBwb3J0IGVsLmhhc0F0dHJpYnV0ZShuYW1lKVxudmFyIGhhc0F0dHJpYnV0ZU5TO1xuXG5pZiAodGVzdEVsLmhhc0F0dHJpYnV0ZU5TKSB7XG4gICAgaGFzQXR0cmlidXRlTlMgPSBmdW5jdGlvbihlbCwgbmFtZXNwYWNlVVJJLCBuYW1lKSB7XG4gICAgICAgIHJldHVybiBlbC5oYXNBdHRyaWJ1dGVOUyhuYW1lc3BhY2VVUkksIG5hbWUpO1xuICAgIH07XG59IGVsc2UgaWYgKHRlc3RFbC5oYXNBdHRyaWJ1dGUpIHtcbiAgICBoYXNBdHRyaWJ1dGVOUyA9IGZ1bmN0aW9uKGVsLCBuYW1lc3BhY2VVUkksIG5hbWUpIHtcbiAgICAgICAgcmV0dXJuIGVsLmhhc0F0dHJpYnV0ZShuYW1lKTtcbiAgICB9O1xufSBlbHNlIHtcbiAgICBoYXNBdHRyaWJ1dGVOUyA9IGZ1bmN0aW9uKGVsLCBuYW1lc3BhY2VVUkksIG5hbWUpIHtcbiAgICAgICAgcmV0dXJuICEhZWwuZ2V0QXR0cmlidXRlTm9kZShuYW1lKTtcbiAgICB9O1xufVxuXG5mdW5jdGlvbiBlbXB0eShvKSB7XG4gICAgZm9yICh2YXIgayBpbiBvKSB7XG4gICAgICAgIGlmIChvLmhhc093blByb3BlcnR5KGspKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5cbmZ1bmN0aW9uIHRvRWxlbWVudChzdHIpIHtcbiAgICBpZiAoIXJhbmdlICYmIGRvY3VtZW50LmNyZWF0ZVJhbmdlKSB7XG4gICAgICAgIHJhbmdlID0gZG9jdW1lbnQuY3JlYXRlUmFuZ2UoKTtcbiAgICAgICAgcmFuZ2Uuc2VsZWN0Tm9kZShkb2N1bWVudC5ib2R5KTtcbiAgICB9XG5cbiAgICB2YXIgZnJhZ21lbnQ7XG4gICAgaWYgKHJhbmdlICYmIHJhbmdlLmNyZWF0ZUNvbnRleHR1YWxGcmFnbWVudCkge1xuICAgICAgICBmcmFnbWVudCA9IHJhbmdlLmNyZWF0ZUNvbnRleHR1YWxGcmFnbWVudChzdHIpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIGZyYWdtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYm9keScpO1xuICAgICAgICBmcmFnbWVudC5pbm5lckhUTUwgPSBzdHI7XG4gICAgfVxuICAgIHJldHVybiBmcmFnbWVudC5jaGlsZE5vZGVzWzBdO1xufVxuXG52YXIgc3BlY2lhbEVsSGFuZGxlcnMgPSB7XG4gICAgLyoqXG4gICAgICogTmVlZGVkIGZvciBJRS4gQXBwYXJlbnRseSBJRSBkb2Vzbid0IHRoaW5rIHRoYXQgXCJzZWxlY3RlZFwiIGlzIGFuXG4gICAgICogYXR0cmlidXRlIHdoZW4gcmVhZGluZyBvdmVyIHRoZSBhdHRyaWJ1dGVzIHVzaW5nIHNlbGVjdEVsLmF0dHJpYnV0ZXNcbiAgICAgKi9cbiAgICBPUFRJT046IGZ1bmN0aW9uKGZyb21FbCwgdG9FbCkge1xuICAgICAgICBmcm9tRWwuc2VsZWN0ZWQgPSB0b0VsLnNlbGVjdGVkO1xuICAgICAgICBpZiAoZnJvbUVsLnNlbGVjdGVkKSB7XG4gICAgICAgICAgICBmcm9tRWwuc2V0QXR0cmlidXRlKCdzZWxlY3RlZCcsICcnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZyb21FbC5yZW1vdmVBdHRyaWJ1dGUoJ3NlbGVjdGVkJywgJycpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBUaGUgXCJ2YWx1ZVwiIGF0dHJpYnV0ZSBpcyBzcGVjaWFsIGZvciB0aGUgPGlucHV0PiBlbGVtZW50IHNpbmNlIGl0IHNldHNcbiAgICAgKiB0aGUgaW5pdGlhbCB2YWx1ZS4gQ2hhbmdpbmcgdGhlIFwidmFsdWVcIiBhdHRyaWJ1dGUgd2l0aG91dCBjaGFuZ2luZyB0aGVcbiAgICAgKiBcInZhbHVlXCIgcHJvcGVydHkgd2lsbCBoYXZlIG5vIGVmZmVjdCBzaW5jZSBpdCBpcyBvbmx5IHVzZWQgdG8gdGhlIHNldCB0aGVcbiAgICAgKiBpbml0aWFsIHZhbHVlLiAgU2ltaWxhciBmb3IgdGhlIFwiY2hlY2tlZFwiIGF0dHJpYnV0ZSwgYW5kIFwiZGlzYWJsZWRcIi5cbiAgICAgKi9cbiAgICBJTlBVVDogZnVuY3Rpb24oZnJvbUVsLCB0b0VsKSB7XG4gICAgICAgIGZyb21FbC5jaGVja2VkID0gdG9FbC5jaGVja2VkO1xuICAgICAgICBpZiAoZnJvbUVsLmNoZWNrZWQpIHtcbiAgICAgICAgICAgIGZyb21FbC5zZXRBdHRyaWJ1dGUoJ2NoZWNrZWQnLCAnJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmcm9tRWwucmVtb3ZlQXR0cmlidXRlKCdjaGVja2VkJyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZnJvbUVsLnZhbHVlICE9PSB0b0VsLnZhbHVlKSB7XG4gICAgICAgICAgICBmcm9tRWwudmFsdWUgPSB0b0VsLnZhbHVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFoYXNBdHRyaWJ1dGVOUyh0b0VsLCBudWxsLCAndmFsdWUnKSkge1xuICAgICAgICAgICAgZnJvbUVsLnJlbW92ZUF0dHJpYnV0ZSgndmFsdWUnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZyb21FbC5kaXNhYmxlZCA9IHRvRWwuZGlzYWJsZWQ7XG4gICAgICAgIGlmIChmcm9tRWwuZGlzYWJsZWQpIHtcbiAgICAgICAgICAgIGZyb21FbC5zZXRBdHRyaWJ1dGUoJ2Rpc2FibGVkJywgJycpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZnJvbUVsLnJlbW92ZUF0dHJpYnV0ZSgnZGlzYWJsZWQnKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBURVhUQVJFQTogZnVuY3Rpb24oZnJvbUVsLCB0b0VsKSB7XG4gICAgICAgIHZhciBuZXdWYWx1ZSA9IHRvRWwudmFsdWU7XG4gICAgICAgIGlmIChmcm9tRWwudmFsdWUgIT09IG5ld1ZhbHVlKSB7XG4gICAgICAgICAgICBmcm9tRWwudmFsdWUgPSBuZXdWYWx1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChmcm9tRWwuZmlyc3RDaGlsZCkge1xuICAgICAgICAgICAgZnJvbUVsLmZpcnN0Q2hpbGQubm9kZVZhbHVlID0gbmV3VmFsdWU7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5mdW5jdGlvbiBub29wKCkge31cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgdHdvIG5vZGUncyBuYW1lcyBhbmQgbmFtZXNwYWNlIFVSSXMgYXJlIHRoZSBzYW1lLlxuICpcbiAqIEBwYXJhbSB7RWxlbWVudH0gYVxuICogQHBhcmFtIHtFbGVtZW50fSBiXG4gKiBAcmV0dXJuIHtib29sZWFufVxuICovXG52YXIgY29tcGFyZU5vZGVOYW1lcyA9IGZ1bmN0aW9uKGEsIGIpIHtcbiAgICByZXR1cm4gYS5ub2RlTmFtZSA9PT0gYi5ub2RlTmFtZSAmJlxuICAgICAgICAgICBhLm5hbWVzcGFjZVVSSSA9PT0gYi5uYW1lc3BhY2VVUkk7XG59O1xuXG4vKipcbiAqIENyZWF0ZSBhbiBlbGVtZW50LCBvcHRpb25hbGx5IHdpdGggYSBrbm93biBuYW1lc3BhY2UgVVJJLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIHRoZSBlbGVtZW50IG5hbWUsIGUuZy4gJ2Rpdicgb3IgJ3N2ZydcbiAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZXNwYWNlVVJJXSB0aGUgZWxlbWVudCdzIG5hbWVzcGFjZSBVUkksIGkuZS4gdGhlIHZhbHVlIG9mXG4gKiBpdHMgYHhtbG5zYCBhdHRyaWJ1dGUgb3IgaXRzIGluZmVycmVkIG5hbWVzcGFjZS5cbiAqXG4gKiBAcmV0dXJuIHtFbGVtZW50fVxuICovXG5mdW5jdGlvbiBjcmVhdGVFbGVtZW50TlMobmFtZSwgbmFtZXNwYWNlVVJJKSB7XG4gICAgcmV0dXJuICFuYW1lc3BhY2VVUkkgfHwgbmFtZXNwYWNlVVJJID09PSBYSFRNTCA/XG4gICAgICAgIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQobmFtZSkgOlxuICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMobmFtZXNwYWNlVVJJLCBuYW1lKTtcbn1cblxuLyoqXG4gKiBMb29wIG92ZXIgYWxsIG9mIHRoZSBhdHRyaWJ1dGVzIG9uIHRoZSB0YXJnZXQgbm9kZSBhbmQgbWFrZSBzdXJlIHRoZSBvcmlnaW5hbFxuICogRE9NIG5vZGUgaGFzIHRoZSBzYW1lIGF0dHJpYnV0ZXMuIElmIGFuIGF0dHJpYnV0ZSBmb3VuZCBvbiB0aGUgb3JpZ2luYWwgbm9kZVxuICogaXMgbm90IG9uIHRoZSBuZXcgbm9kZSB0aGVuIHJlbW92ZSBpdCBmcm9tIHRoZSBvcmlnaW5hbCBub2RlLlxuICpcbiAqIEBwYXJhbSAge0VsZW1lbnR9IGZyb21Ob2RlXG4gKiBAcGFyYW0gIHtFbGVtZW50fSB0b05vZGVcbiAqL1xuZnVuY3Rpb24gbW9ycGhBdHRycyhmcm9tTm9kZSwgdG9Ob2RlKSB7XG4gICAgdmFyIGF0dHJzID0gdG9Ob2RlLmF0dHJpYnV0ZXM7XG4gICAgdmFyIGk7XG4gICAgdmFyIGF0dHI7XG4gICAgdmFyIGF0dHJOYW1lO1xuICAgIHZhciBhdHRyTmFtZXNwYWNlVVJJO1xuICAgIHZhciBhdHRyVmFsdWU7XG4gICAgdmFyIGZyb21WYWx1ZTtcblxuICAgIGZvciAoaSA9IGF0dHJzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgIGF0dHIgPSBhdHRyc1tpXTtcbiAgICAgICAgYXR0ck5hbWUgPSBhdHRyLm5hbWU7XG4gICAgICAgIGF0dHJWYWx1ZSA9IGF0dHIudmFsdWU7XG4gICAgICAgIGF0dHJOYW1lc3BhY2VVUkkgPSBhdHRyLm5hbWVzcGFjZVVSSTtcblxuICAgICAgICBpZiAoYXR0ck5hbWVzcGFjZVVSSSkge1xuICAgICAgICAgICAgYXR0ck5hbWUgPSBhdHRyLmxvY2FsTmFtZSB8fCBhdHRyTmFtZTtcbiAgICAgICAgICAgIGZyb21WYWx1ZSA9IGZyb21Ob2RlLmdldEF0dHJpYnV0ZU5TKGF0dHJOYW1lc3BhY2VVUkksIGF0dHJOYW1lKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZyb21WYWx1ZSA9IGZyb21Ob2RlLmdldEF0dHJpYnV0ZShhdHRyTmFtZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZnJvbVZhbHVlICE9PSBhdHRyVmFsdWUpIHtcbiAgICAgICAgICAgIGlmIChhdHRyTmFtZXNwYWNlVVJJKSB7XG4gICAgICAgICAgICAgICAgZnJvbU5vZGUuc2V0QXR0cmlidXRlTlMoYXR0ck5hbWVzcGFjZVVSSSwgYXR0ck5hbWUsIGF0dHJWYWx1ZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZyb21Ob2RlLnNldEF0dHJpYnV0ZShhdHRyTmFtZSwgYXR0clZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJlbW92ZSBhbnkgZXh0cmEgYXR0cmlidXRlcyBmb3VuZCBvbiB0aGUgb3JpZ2luYWwgRE9NIGVsZW1lbnQgdGhhdFxuICAgIC8vIHdlcmVuJ3QgZm91bmQgb24gdGhlIHRhcmdldCBlbGVtZW50LlxuICAgIGF0dHJzID0gZnJvbU5vZGUuYXR0cmlidXRlcztcblxuICAgIGZvciAoaSA9IGF0dHJzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgIGF0dHIgPSBhdHRyc1tpXTtcbiAgICAgICAgaWYgKGF0dHIuc3BlY2lmaWVkICE9PSBmYWxzZSkge1xuICAgICAgICAgICAgYXR0ck5hbWUgPSBhdHRyLm5hbWU7XG4gICAgICAgICAgICBhdHRyTmFtZXNwYWNlVVJJID0gYXR0ci5uYW1lc3BhY2VVUkk7XG5cbiAgICAgICAgICAgIGlmICghaGFzQXR0cmlidXRlTlModG9Ob2RlLCBhdHRyTmFtZXNwYWNlVVJJLCBhdHRyTmFtZXNwYWNlVVJJID8gYXR0ck5hbWUgPSBhdHRyLmxvY2FsTmFtZSB8fCBhdHRyTmFtZSA6IGF0dHJOYW1lKSkge1xuICAgICAgICAgICAgICAgIGlmIChhdHRyTmFtZXNwYWNlVVJJKSB7XG4gICAgICAgICAgICAgICAgICAgIGZyb21Ob2RlLnJlbW92ZUF0dHJpYnV0ZU5TKGF0dHJOYW1lc3BhY2VVUkksIGF0dHIubG9jYWxOYW1lKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBmcm9tTm9kZS5yZW1vdmVBdHRyaWJ1dGUoYXR0ck5hbWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBDb3BpZXMgdGhlIGNoaWxkcmVuIG9mIG9uZSBET00gZWxlbWVudCB0byBhbm90aGVyIERPTSBlbGVtZW50XG4gKi9cbmZ1bmN0aW9uIG1vdmVDaGlsZHJlbihmcm9tRWwsIHRvRWwpIHtcbiAgICB2YXIgY3VyQ2hpbGQgPSBmcm9tRWwuZmlyc3RDaGlsZDtcbiAgICB3aGlsZSAoY3VyQ2hpbGQpIHtcbiAgICAgICAgdmFyIG5leHRDaGlsZCA9IGN1ckNoaWxkLm5leHRTaWJsaW5nO1xuICAgICAgICB0b0VsLmFwcGVuZENoaWxkKGN1ckNoaWxkKTtcbiAgICAgICAgY3VyQ2hpbGQgPSBuZXh0Q2hpbGQ7XG4gICAgfVxuICAgIHJldHVybiB0b0VsO1xufVxuXG5mdW5jdGlvbiBkZWZhdWx0R2V0Tm9kZUtleShub2RlKSB7XG4gICAgcmV0dXJuIG5vZGUuaWQ7XG59XG5cbmZ1bmN0aW9uIG1vcnBoZG9tKGZyb21Ob2RlLCB0b05vZGUsIG9wdGlvbnMpIHtcbiAgICBpZiAoIW9wdGlvbnMpIHtcbiAgICAgICAgb3B0aW9ucyA9IHt9O1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdG9Ob2RlID09PSAnc3RyaW5nJykge1xuICAgICAgICBpZiAoZnJvbU5vZGUubm9kZU5hbWUgPT09ICcjZG9jdW1lbnQnIHx8IGZyb21Ob2RlLm5vZGVOYW1lID09PSAnSFRNTCcpIHtcbiAgICAgICAgICAgIHZhciB0b05vZGVIdG1sID0gdG9Ob2RlO1xuICAgICAgICAgICAgdG9Ob2RlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaHRtbCcpO1xuICAgICAgICAgICAgdG9Ob2RlLmlubmVySFRNTCA9IHRvTm9kZUh0bWw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0b05vZGUgPSB0b0VsZW1lbnQodG9Ob2RlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIFhYWCBvcHRpbWl6YXRpb246IGlmIHRoZSBub2RlcyBhcmUgZXF1YWwsIGRvbid0IG1vcnBoIHRoZW1cbiAgICAvKlxuICAgIGlmIChmcm9tTm9kZS5pc0VxdWFsTm9kZSh0b05vZGUpKSB7XG4gICAgICByZXR1cm4gZnJvbU5vZGU7XG4gICAgfVxuICAgICovXG5cbiAgICB2YXIgc2F2ZWRFbHMgPSB7fTsgLy8gVXNlZCB0byBzYXZlIG9mZiBET00gZWxlbWVudHMgd2l0aCBJRHNcbiAgICB2YXIgdW5tYXRjaGVkRWxzID0ge307XG4gICAgdmFyIGdldE5vZGVLZXkgPSBvcHRpb25zLmdldE5vZGVLZXkgfHwgZGVmYXVsdEdldE5vZGVLZXk7XG4gICAgdmFyIG9uQmVmb3JlTm9kZUFkZGVkID0gb3B0aW9ucy5vbkJlZm9yZU5vZGVBZGRlZCB8fCBub29wO1xuICAgIHZhciBvbk5vZGVBZGRlZCA9IG9wdGlvbnMub25Ob2RlQWRkZWQgfHwgbm9vcDtcbiAgICB2YXIgb25CZWZvcmVFbFVwZGF0ZWQgPSBvcHRpb25zLm9uQmVmb3JlRWxVcGRhdGVkIHx8IG9wdGlvbnMub25CZWZvcmVNb3JwaEVsIHx8IG5vb3A7XG4gICAgdmFyIG9uRWxVcGRhdGVkID0gb3B0aW9ucy5vbkVsVXBkYXRlZCB8fCBub29wO1xuICAgIHZhciBvbkJlZm9yZU5vZGVEaXNjYXJkZWQgPSBvcHRpb25zLm9uQmVmb3JlTm9kZURpc2NhcmRlZCB8fCBub29wO1xuICAgIHZhciBvbk5vZGVEaXNjYXJkZWQgPSBvcHRpb25zLm9uTm9kZURpc2NhcmRlZCB8fCBub29wO1xuICAgIHZhciBvbkJlZm9yZUVsQ2hpbGRyZW5VcGRhdGVkID0gb3B0aW9ucy5vbkJlZm9yZUVsQ2hpbGRyZW5VcGRhdGVkIHx8IG9wdGlvbnMub25CZWZvcmVNb3JwaEVsQ2hpbGRyZW4gfHwgbm9vcDtcbiAgICB2YXIgY2hpbGRyZW5Pbmx5ID0gb3B0aW9ucy5jaGlsZHJlbk9ubHkgPT09IHRydWU7XG4gICAgdmFyIG1vdmVkRWxzID0gW107XG5cbiAgICBmdW5jdGlvbiByZW1vdmVOb2RlSGVscGVyKG5vZGUsIG5lc3RlZEluU2F2ZWRFbCkge1xuICAgICAgICB2YXIgaWQgPSBnZXROb2RlS2V5KG5vZGUpO1xuICAgICAgICAvLyBJZiB0aGUgbm9kZSBoYXMgYW4gSUQgdGhlbiBzYXZlIGl0IG9mZiBzaW5jZSB3ZSB3aWxsIHdhbnRcbiAgICAgICAgLy8gdG8gcmV1c2UgaXQgaW4gY2FzZSB0aGUgdGFyZ2V0IERPTSB0cmVlIGhhcyBhIERPTSBlbGVtZW50XG4gICAgICAgIC8vIHdpdGggdGhlIHNhbWUgSURcbiAgICAgICAgaWYgKGlkKSB7XG4gICAgICAgICAgICBzYXZlZEVsc1tpZF0gPSBub2RlO1xuICAgICAgICB9IGVsc2UgaWYgKCFuZXN0ZWRJblNhdmVkRWwpIHtcbiAgICAgICAgICAgIC8vIElmIHdlIGFyZSBub3QgbmVzdGVkIGluIGEgc2F2ZWQgZWxlbWVudCB0aGVuIHdlIGtub3cgdGhhdCB0aGlzIG5vZGUgaGFzIGJlZW5cbiAgICAgICAgICAgIC8vIGNvbXBsZXRlbHkgZGlzY2FyZGVkIGFuZCB3aWxsIG5vdCBleGlzdCBpbiB0aGUgZmluYWwgRE9NLlxuICAgICAgICAgICAgb25Ob2RlRGlzY2FyZGVkKG5vZGUpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG5vZGUubm9kZVR5cGUgPT09IEVMRU1FTlRfTk9ERSkge1xuICAgICAgICAgICAgdmFyIGN1ckNoaWxkID0gbm9kZS5maXJzdENoaWxkO1xuICAgICAgICAgICAgd2hpbGUgKGN1ckNoaWxkKSB7XG4gICAgICAgICAgICAgICAgcmVtb3ZlTm9kZUhlbHBlcihjdXJDaGlsZCwgbmVzdGVkSW5TYXZlZEVsIHx8IGlkKTtcbiAgICAgICAgICAgICAgICBjdXJDaGlsZCA9IGN1ckNoaWxkLm5leHRTaWJsaW5nO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gd2Fsa0Rpc2NhcmRlZENoaWxkTm9kZXMobm9kZSkge1xuICAgICAgICBpZiAobm9kZS5ub2RlVHlwZSA9PT0gRUxFTUVOVF9OT0RFKSB7XG4gICAgICAgICAgICB2YXIgY3VyQ2hpbGQgPSBub2RlLmZpcnN0Q2hpbGQ7XG4gICAgICAgICAgICB3aGlsZSAoY3VyQ2hpbGQpIHtcblxuXG4gICAgICAgICAgICAgICAgaWYgKCFnZXROb2RlS2V5KGN1ckNoaWxkKSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBXZSBvbmx5IHdhbnQgdG8gaGFuZGxlIG5vZGVzIHRoYXQgZG9uJ3QgaGF2ZSBhbiBJRCB0byBhdm9pZCBkb3VibGVcbiAgICAgICAgICAgICAgICAgICAgLy8gd2Fsa2luZyB0aGUgc2FtZSBzYXZlZCBlbGVtZW50LlxuXG4gICAgICAgICAgICAgICAgICAgIG9uTm9kZURpc2NhcmRlZChjdXJDaGlsZCk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gV2FsayByZWN1cnNpdmVseVxuICAgICAgICAgICAgICAgICAgICB3YWxrRGlzY2FyZGVkQ2hpbGROb2RlcyhjdXJDaGlsZCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY3VyQ2hpbGQgPSBjdXJDaGlsZC5uZXh0U2libGluZztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlbW92ZU5vZGUobm9kZSwgcGFyZW50Tm9kZSwgYWxyZWFkeVZpc2l0ZWQpIHtcbiAgICAgICAgaWYgKG9uQmVmb3JlTm9kZURpc2NhcmRlZChub2RlKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHBhcmVudE5vZGUucmVtb3ZlQ2hpbGQobm9kZSk7XG4gICAgICAgIGlmIChhbHJlYWR5VmlzaXRlZCkge1xuICAgICAgICAgICAgaWYgKCFnZXROb2RlS2V5KG5vZGUpKSB7XG4gICAgICAgICAgICAgICAgb25Ob2RlRGlzY2FyZGVkKG5vZGUpO1xuICAgICAgICAgICAgICAgIHdhbGtEaXNjYXJkZWRDaGlsZE5vZGVzKG5vZGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVtb3ZlTm9kZUhlbHBlcihub2RlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIG1vcnBoRWwoZnJvbUVsLCB0b0VsLCBhbHJlYWR5VmlzaXRlZCwgY2hpbGRyZW5Pbmx5KSB7XG4gICAgICAgIHZhciB0b0VsS2V5ID0gZ2V0Tm9kZUtleSh0b0VsKTtcbiAgICAgICAgaWYgKHRvRWxLZXkpIHtcbiAgICAgICAgICAgIC8vIElmIGFuIGVsZW1lbnQgd2l0aCBhbiBJRCBpcyBiZWluZyBtb3JwaGVkIHRoZW4gaXQgaXMgd2lsbCBiZSBpbiB0aGUgZmluYWxcbiAgICAgICAgICAgIC8vIERPTSBzbyBjbGVhciBpdCBvdXQgb2YgdGhlIHNhdmVkIGVsZW1lbnRzIGNvbGxlY3Rpb25cbiAgICAgICAgICAgIGRlbGV0ZSBzYXZlZEVsc1t0b0VsS2V5XTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghY2hpbGRyZW5Pbmx5KSB7XG4gICAgICAgICAgICBpZiAob25CZWZvcmVFbFVwZGF0ZWQoZnJvbUVsLCB0b0VsKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG1vcnBoQXR0cnMoZnJvbUVsLCB0b0VsKTtcbiAgICAgICAgICAgIG9uRWxVcGRhdGVkKGZyb21FbCk7XG5cbiAgICAgICAgICAgIGlmIChvbkJlZm9yZUVsQ2hpbGRyZW5VcGRhdGVkKGZyb21FbCwgdG9FbCkgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGZyb21FbC5ub2RlTmFtZSAhPT0gJ1RFWFRBUkVBJykge1xuICAgICAgICAgICAgdmFyIGN1clRvTm9kZUNoaWxkID0gdG9FbC5maXJzdENoaWxkO1xuICAgICAgICAgICAgdmFyIGN1ckZyb21Ob2RlQ2hpbGQgPSBmcm9tRWwuZmlyc3RDaGlsZDtcbiAgICAgICAgICAgIHZhciBjdXJUb05vZGVJZDtcblxuICAgICAgICAgICAgdmFyIGZyb21OZXh0U2libGluZztcbiAgICAgICAgICAgIHZhciB0b05leHRTaWJsaW5nO1xuICAgICAgICAgICAgdmFyIHNhdmVkRWw7XG4gICAgICAgICAgICB2YXIgdW5tYXRjaGVkRWw7XG5cbiAgICAgICAgICAgIG91dGVyOiB3aGlsZSAoY3VyVG9Ob2RlQ2hpbGQpIHtcbiAgICAgICAgICAgICAgICB0b05leHRTaWJsaW5nID0gY3VyVG9Ob2RlQ2hpbGQubmV4dFNpYmxpbmc7XG4gICAgICAgICAgICAgICAgY3VyVG9Ob2RlSWQgPSBnZXROb2RlS2V5KGN1clRvTm9kZUNoaWxkKTtcblxuICAgICAgICAgICAgICAgIHdoaWxlIChjdXJGcm9tTm9kZUNoaWxkKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBjdXJGcm9tTm9kZUlkID0gZ2V0Tm9kZUtleShjdXJGcm9tTm9kZUNoaWxkKTtcbiAgICAgICAgICAgICAgICAgICAgZnJvbU5leHRTaWJsaW5nID0gY3VyRnJvbU5vZGVDaGlsZC5uZXh0U2libGluZztcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIWFscmVhZHlWaXNpdGVkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoY3VyRnJvbU5vZGVJZCAmJiAodW5tYXRjaGVkRWwgPSB1bm1hdGNoZWRFbHNbY3VyRnJvbU5vZGVJZF0pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5tYXRjaGVkRWwucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoY3VyRnJvbU5vZGVDaGlsZCwgdW5tYXRjaGVkRWwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vcnBoRWwoY3VyRnJvbU5vZGVDaGlsZCwgdW5tYXRjaGVkRWwsIGFscmVhZHlWaXNpdGVkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJGcm9tTm9kZUNoaWxkID0gZnJvbU5leHRTaWJsaW5nO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIGN1ckZyb21Ob2RlVHlwZSA9IGN1ckZyb21Ob2RlQ2hpbGQubm9kZVR5cGU7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGN1ckZyb21Ob2RlVHlwZSA9PT0gY3VyVG9Ob2RlQ2hpbGQubm9kZVR5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpc0NvbXBhdGlibGUgPSBmYWxzZTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gQm90aCBub2RlcyBiZWluZyBjb21wYXJlZCBhcmUgRWxlbWVudCBub2Rlc1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGN1ckZyb21Ob2RlVHlwZSA9PT0gRUxFTUVOVF9OT0RFKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvbXBhcmVOb2RlTmFtZXMoY3VyRnJvbU5vZGVDaGlsZCwgY3VyVG9Ob2RlQ2hpbGQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFdlIGhhdmUgY29tcGF0aWJsZSBET00gZWxlbWVudHNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGN1ckZyb21Ob2RlSWQgfHwgY3VyVG9Ob2RlSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIGVpdGhlciBET00gZWxlbWVudCBoYXMgYW4gSUQgdGhlbiB3ZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gaGFuZGxlIHRob3NlIGRpZmZlcmVudGx5IHNpbmNlIHdlIHdhbnQgdG9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG1hdGNoIHVwIGJ5IElEXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY3VyVG9Ob2RlSWQgPT09IGN1ckZyb21Ob2RlSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc0NvbXBhdGlibGUgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNDb21wYXRpYmxlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0NvbXBhdGlibGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gV2UgZm91bmQgY29tcGF0aWJsZSBET00gZWxlbWVudHMgc28gdHJhbnNmb3JtXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoZSBjdXJyZW50IFwiZnJvbVwiIG5vZGUgdG8gbWF0Y2ggdGhlIGN1cnJlbnRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGFyZ2V0IERPTSBub2RlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb3JwaEVsKGN1ckZyb21Ob2RlQ2hpbGQsIGN1clRvTm9kZUNoaWxkLCBhbHJlYWR5VmlzaXRlZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gQm90aCBub2RlcyBiZWluZyBjb21wYXJlZCBhcmUgVGV4dCBvciBDb21tZW50IG5vZGVzXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VyRnJvbU5vZGVUeXBlID09PSBURVhUX05PREUgfHwgY3VyRnJvbU5vZGVUeXBlID09IENPTU1FTlRfTk9ERSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzQ29tcGF0aWJsZSA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU2ltcGx5IHVwZGF0ZSBub2RlVmFsdWUgb24gdGhlIG9yaWdpbmFsIG5vZGUgdG9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaGFuZ2UgdGhlIHRleHQgdmFsdWVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJGcm9tTm9kZUNoaWxkLm5vZGVWYWx1ZSA9IGN1clRvTm9kZUNoaWxkLm5vZGVWYWx1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzQ29tcGF0aWJsZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1clRvTm9kZUNoaWxkID0gdG9OZXh0U2libGluZztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJGcm9tTm9kZUNoaWxkID0gZnJvbU5leHRTaWJsaW5nO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlIG91dGVyO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gTm8gY29tcGF0aWJsZSBtYXRjaCBzbyByZW1vdmUgdGhlIG9sZCBub2RlIGZyb20gdGhlIERPTVxuICAgICAgICAgICAgICAgICAgICAvLyBhbmQgY29udGludWUgdHJ5aW5nIHRvIGZpbmQgYSBtYXRjaCBpbiB0aGUgb3JpZ2luYWwgRE9NXG4gICAgICAgICAgICAgICAgICAgIHJlbW92ZU5vZGUoY3VyRnJvbU5vZGVDaGlsZCwgZnJvbUVsLCBhbHJlYWR5VmlzaXRlZCk7XG4gICAgICAgICAgICAgICAgICAgIGN1ckZyb21Ob2RlQ2hpbGQgPSBmcm9tTmV4dFNpYmxpbmc7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGN1clRvTm9kZUlkKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICgoc2F2ZWRFbCA9IHNhdmVkRWxzW2N1clRvTm9kZUlkXSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb21wYXJlTm9kZU5hbWVzKHNhdmVkRWwsIGN1clRvTm9kZUNoaWxkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vcnBoRWwoc2F2ZWRFbCwgY3VyVG9Ob2RlQ2hpbGQsIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFdlIHdhbnQgdG8gYXBwZW5kIHRoZSBzYXZlZCBlbGVtZW50IGluc3RlYWRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJUb05vZGVDaGlsZCA9IHNhdmVkRWw7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzYXZlZEVsc1tjdXJUb05vZGVJZF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb25Ob2RlRGlzY2FyZGVkKHNhdmVkRWwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGhlIGN1cnJlbnQgRE9NIGVsZW1lbnQgaW4gdGhlIHRhcmdldCB0cmVlIGhhcyBhbiBJRFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYnV0IHdlIGRpZCBub3QgZmluZCBhIG1hdGNoIGluIGFueSBvZiB0aGVcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNvcnJlc3BvbmRpbmcgc2libGluZ3MuIFdlIGp1c3QgcHV0IHRoZSB0YXJnZXRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGVsZW1lbnQgaW4gdGhlIG9sZCBET00gdHJlZSBidXQgaWYgd2UgbGF0ZXIgZmluZCBhblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gZWxlbWVudCBpbiB0aGUgb2xkIERPTSB0cmVlIHRoYXQgaGFzIGEgbWF0Y2hpbmcgSURcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoZW4gd2Ugd2lsbCByZXBsYWNlIHRoZSB0YXJnZXQgZWxlbWVudCB3aXRoIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gY29ycmVzcG9uZGluZyBvbGQgZWxlbWVudCBhbmQgbW9ycGggdGhlIG9sZCBlbGVtZW50XG4gICAgICAgICAgICAgICAgICAgICAgICB1bm1hdGNoZWRFbHNbY3VyVG9Ob2RlSWRdID0gY3VyVG9Ob2RlQ2hpbGQ7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBJZiB3ZSBnb3QgdGhpcyBmYXIgdGhlbiB3ZSBkaWQgbm90IGZpbmQgYSBjYW5kaWRhdGUgbWF0Y2ggZm9yXG4gICAgICAgICAgICAgICAgLy8gb3VyIFwidG8gbm9kZVwiIGFuZCB3ZSBleGhhdXN0ZWQgYWxsIG9mIHRoZSBjaGlsZHJlbiBcImZyb21cIlxuICAgICAgICAgICAgICAgIC8vIG5vZGVzLiBUaGVyZWZvcmUsIHdlIHdpbGwganVzdCBhcHBlbmQgdGhlIGN1cnJlbnQgXCJ0byBub2RlXCJcbiAgICAgICAgICAgICAgICAvLyB0byB0aGUgZW5kXG4gICAgICAgICAgICAgICAgaWYgKG9uQmVmb3JlTm9kZUFkZGVkKGN1clRvTm9kZUNoaWxkKSAhPT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgZnJvbUVsLmFwcGVuZENoaWxkKGN1clRvTm9kZUNoaWxkKTtcbiAgICAgICAgICAgICAgICAgICAgb25Ob2RlQWRkZWQoY3VyVG9Ob2RlQ2hpbGQpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChjdXJUb05vZGVDaGlsZC5ub2RlVHlwZSA9PT0gRUxFTUVOVF9OT0RFICYmXG4gICAgICAgICAgICAgICAgICAgIChjdXJUb05vZGVJZCB8fCBjdXJUb05vZGVDaGlsZC5maXJzdENoaWxkKSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUaGUgZWxlbWVudCB0aGF0IHdhcyBqdXN0IGFkZGVkIHRvIHRoZSBvcmlnaW5hbCBET00gbWF5XG4gICAgICAgICAgICAgICAgICAgIC8vIGhhdmUgc29tZSBuZXN0ZWQgZWxlbWVudHMgd2l0aCBhIGtleS9JRCB0aGF0IG5lZWRzIHRvIGJlXG4gICAgICAgICAgICAgICAgICAgIC8vIG1hdGNoZWQgdXAgd2l0aCBvdGhlciBlbGVtZW50cy4gV2UnbGwgYWRkIHRoZSBlbGVtZW50IHRvXG4gICAgICAgICAgICAgICAgICAgIC8vIGEgbGlzdCBzbyB0aGF0IHdlIGNhbiBsYXRlciBwcm9jZXNzIHRoZSBuZXN0ZWQgZWxlbWVudHNcbiAgICAgICAgICAgICAgICAgICAgLy8gaWYgdGhlcmUgYXJlIGFueSB1bm1hdGNoZWQga2V5ZWQgZWxlbWVudHMgdGhhdCB3ZXJlXG4gICAgICAgICAgICAgICAgICAgIC8vIGRpc2NhcmRlZFxuICAgICAgICAgICAgICAgICAgICBtb3ZlZEVscy5wdXNoKGN1clRvTm9kZUNoaWxkKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjdXJUb05vZGVDaGlsZCA9IHRvTmV4dFNpYmxpbmc7XG4gICAgICAgICAgICAgICAgY3VyRnJvbU5vZGVDaGlsZCA9IGZyb21OZXh0U2libGluZztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gV2UgaGF2ZSBwcm9jZXNzZWQgYWxsIG9mIHRoZSBcInRvIG5vZGVzXCIuIElmIGN1ckZyb21Ob2RlQ2hpbGQgaXNcbiAgICAgICAgICAgIC8vIG5vbi1udWxsIHRoZW4gd2Ugc3RpbGwgaGF2ZSBzb21lIGZyb20gbm9kZXMgbGVmdCBvdmVyIHRoYXQgbmVlZFxuICAgICAgICAgICAgLy8gdG8gYmUgcmVtb3ZlZFxuICAgICAgICAgICAgd2hpbGUgKGN1ckZyb21Ob2RlQ2hpbGQpIHtcbiAgICAgICAgICAgICAgICBmcm9tTmV4dFNpYmxpbmcgPSBjdXJGcm9tTm9kZUNoaWxkLm5leHRTaWJsaW5nO1xuICAgICAgICAgICAgICAgIHJlbW92ZU5vZGUoY3VyRnJvbU5vZGVDaGlsZCwgZnJvbUVsLCBhbHJlYWR5VmlzaXRlZCk7XG4gICAgICAgICAgICAgICAgY3VyRnJvbU5vZGVDaGlsZCA9IGZyb21OZXh0U2libGluZztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBzcGVjaWFsRWxIYW5kbGVyID0gc3BlY2lhbEVsSGFuZGxlcnNbZnJvbUVsLm5vZGVOYW1lXTtcbiAgICAgICAgaWYgKHNwZWNpYWxFbEhhbmRsZXIpIHtcbiAgICAgICAgICAgIHNwZWNpYWxFbEhhbmRsZXIoZnJvbUVsLCB0b0VsKTtcbiAgICAgICAgfVxuICAgIH0gLy8gRU5EOiBtb3JwaEVsKC4uLilcblxuICAgIHZhciBtb3JwaGVkTm9kZSA9IGZyb21Ob2RlO1xuICAgIHZhciBtb3JwaGVkTm9kZVR5cGUgPSBtb3JwaGVkTm9kZS5ub2RlVHlwZTtcbiAgICB2YXIgdG9Ob2RlVHlwZSA9IHRvTm9kZS5ub2RlVHlwZTtcblxuICAgIGlmICghY2hpbGRyZW5Pbmx5KSB7XG4gICAgICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB3ZSBhcmUgZ2l2ZW4gdHdvIERPTSBub2RlcyB0aGF0IGFyZSBub3RcbiAgICAgICAgLy8gY29tcGF0aWJsZSAoZS5nLiA8ZGl2PiAtLT4gPHNwYW4+IG9yIDxkaXY+IC0tPiBURVhUKVxuICAgICAgICBpZiAobW9ycGhlZE5vZGVUeXBlID09PSBFTEVNRU5UX05PREUpIHtcbiAgICAgICAgICAgIGlmICh0b05vZGVUeXBlID09PSBFTEVNRU5UX05PREUpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWNvbXBhcmVOb2RlTmFtZXMoZnJvbU5vZGUsIHRvTm9kZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgb25Ob2RlRGlzY2FyZGVkKGZyb21Ob2RlKTtcbiAgICAgICAgICAgICAgICAgICAgbW9ycGhlZE5vZGUgPSBtb3ZlQ2hpbGRyZW4oZnJvbU5vZGUsIGNyZWF0ZUVsZW1lbnROUyh0b05vZGUubm9kZU5hbWUsIHRvTm9kZS5uYW1lc3BhY2VVUkkpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIEdvaW5nIGZyb20gYW4gZWxlbWVudCBub2RlIHRvIGEgdGV4dCBub2RlXG4gICAgICAgICAgICAgICAgbW9ycGhlZE5vZGUgPSB0b05vZGU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAobW9ycGhlZE5vZGVUeXBlID09PSBURVhUX05PREUgfHwgbW9ycGhlZE5vZGVUeXBlID09PSBDT01NRU5UX05PREUpIHsgLy8gVGV4dCBvciBjb21tZW50IG5vZGVcbiAgICAgICAgICAgIGlmICh0b05vZGVUeXBlID09PSBtb3JwaGVkTm9kZVR5cGUpIHtcbiAgICAgICAgICAgICAgICBtb3JwaGVkTm9kZS5ub2RlVmFsdWUgPSB0b05vZGUubm9kZVZhbHVlO1xuICAgICAgICAgICAgICAgIHJldHVybiBtb3JwaGVkTm9kZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gVGV4dCBub2RlIHRvIHNvbWV0aGluZyBlbHNlXG4gICAgICAgICAgICAgICAgbW9ycGhlZE5vZGUgPSB0b05vZGU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobW9ycGhlZE5vZGUgPT09IHRvTm9kZSkge1xuICAgICAgICAvLyBUaGUgXCJ0byBub2RlXCIgd2FzIG5vdCBjb21wYXRpYmxlIHdpdGggdGhlIFwiZnJvbSBub2RlXCIgc28gd2UgaGFkIHRvXG4gICAgICAgIC8vIHRvc3Mgb3V0IHRoZSBcImZyb20gbm9kZVwiIGFuZCB1c2UgdGhlIFwidG8gbm9kZVwiXG4gICAgICAgIG9uTm9kZURpc2NhcmRlZChmcm9tTm9kZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbW9ycGhFbChtb3JwaGVkTm9kZSwgdG9Ob2RlLCBmYWxzZSwgY2hpbGRyZW5Pbmx5KTtcblxuICAgICAgICAvKipcbiAgICAgICAgICogV2hhdCB3ZSB3aWxsIGRvIGhlcmUgaXMgd2FsayB0aGUgdHJlZSBmb3IgdGhlIERPTSBlbGVtZW50IHRoYXQgd2FzXG4gICAgICAgICAqIG1vdmVkIGZyb20gdGhlIHRhcmdldCBET00gdHJlZSB0byB0aGUgb3JpZ2luYWwgRE9NIHRyZWUgYW5kIHdlIHdpbGxcbiAgICAgICAgICogbG9vayBmb3Iga2V5ZWQgZWxlbWVudHMgdGhhdCBjb3VsZCBiZSBtYXRjaGVkIHRvIGtleWVkIGVsZW1lbnRzIHRoYXRcbiAgICAgICAgICogd2VyZSBlYXJsaWVyIGRpc2NhcmRlZC4gIElmIHdlIGZpbmQgYSBtYXRjaCB0aGVuIHdlIHdpbGwgbW92ZSB0aGVcbiAgICAgICAgICogc2F2ZWQgZWxlbWVudCBpbnRvIHRoZSBmaW5hbCBET00gdHJlZS5cbiAgICAgICAgICovXG4gICAgICAgIHZhciBoYW5kbGVNb3ZlZEVsID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgICAgIHZhciBjdXJDaGlsZCA9IGVsLmZpcnN0Q2hpbGQ7XG4gICAgICAgICAgICB3aGlsZSAoY3VyQ2hpbGQpIHtcbiAgICAgICAgICAgICAgICB2YXIgbmV4dFNpYmxpbmcgPSBjdXJDaGlsZC5uZXh0U2libGluZztcblxuICAgICAgICAgICAgICAgIHZhciBrZXkgPSBnZXROb2RlS2V5KGN1ckNoaWxkKTtcbiAgICAgICAgICAgICAgICBpZiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBzYXZlZEVsID0gc2F2ZWRFbHNba2V5XTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNhdmVkRWwgJiYgY29tcGFyZU5vZGVOYW1lcyhjdXJDaGlsZCwgc2F2ZWRFbCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGN1ckNoaWxkLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKHNhdmVkRWwsIGN1ckNoaWxkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRydWU6IGFscmVhZHkgdmlzaXRlZCB0aGUgc2F2ZWQgZWwgdHJlZVxuICAgICAgICAgICAgICAgICAgICAgICAgbW9ycGhFbChzYXZlZEVsLCBjdXJDaGlsZCwgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjdXJDaGlsZCA9IG5leHRTaWJsaW5nO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVtcHR5KHNhdmVkRWxzKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGN1ckNoaWxkLm5vZGVUeXBlID09PSBFTEVNRU5UX05PREUpIHtcbiAgICAgICAgICAgICAgICAgICAgaGFuZGxlTW92ZWRFbChjdXJDaGlsZCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY3VyQ2hpbGQgPSBuZXh0U2libGluZztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICAvLyBUaGUgbG9vcCBiZWxvdyBpcyB1c2VkIHRvIHBvc3NpYmx5IG1hdGNoIHVwIGFueSBkaXNjYXJkZWRcbiAgICAgICAgLy8gZWxlbWVudHMgaW4gdGhlIG9yaWdpbmFsIERPTSB0cmVlIHdpdGggZWxlbWVuZXRzIGZyb20gdGhlXG4gICAgICAgIC8vIHRhcmdldCB0cmVlIHRoYXQgd2VyZSBtb3ZlZCBvdmVyIHdpdGhvdXQgdmlzaXRpbmcgdGhlaXJcbiAgICAgICAgLy8gY2hpbGRyZW5cbiAgICAgICAgaWYgKCFlbXB0eShzYXZlZEVscykpIHtcbiAgICAgICAgICAgIGhhbmRsZU1vdmVkRWxzTG9vcDpcbiAgICAgICAgICAgIHdoaWxlIChtb3ZlZEVscy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB2YXIgbW92ZWRFbHNUZW1wID0gbW92ZWRFbHM7XG4gICAgICAgICAgICAgICAgbW92ZWRFbHMgPSBbXTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpPTA7IGk8bW92ZWRFbHNUZW1wLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChoYW5kbGVNb3ZlZEVsKG1vdmVkRWxzVGVtcFtpXSkgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBUaGVyZSBhcmUgbm8gbW9yZSB1bm1hdGNoZWQgZWxlbWVudHMgc28gY29tcGxldGVseSBlbmRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoZSBsb29wXG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhayBoYW5kbGVNb3ZlZEVsc0xvb3A7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBGaXJlIHRoZSBcIm9uTm9kZURpc2NhcmRlZFwiIGV2ZW50IGZvciBhbnkgc2F2ZWQgZWxlbWVudHNcbiAgICAgICAgLy8gdGhhdCBuZXZlciBmb3VuZCBhIG5ldyBob21lIGluIHRoZSBtb3JwaGVkIERPTVxuICAgICAgICBmb3IgKHZhciBzYXZlZEVsSWQgaW4gc2F2ZWRFbHMpIHtcbiAgICAgICAgICAgIGlmIChzYXZlZEVscy5oYXNPd25Qcm9wZXJ0eShzYXZlZEVsSWQpKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNhdmVkRWwgPSBzYXZlZEVsc1tzYXZlZEVsSWRdO1xuICAgICAgICAgICAgICAgIG9uTm9kZURpc2NhcmRlZChzYXZlZEVsKTtcbiAgICAgICAgICAgICAgICB3YWxrRGlzY2FyZGVkQ2hpbGROb2RlcyhzYXZlZEVsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmICghY2hpbGRyZW5Pbmx5ICYmIG1vcnBoZWROb2RlICE9PSBmcm9tTm9kZSAmJiBmcm9tTm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIC8vIElmIHdlIGhhZCB0byBzd2FwIG91dCB0aGUgZnJvbSBub2RlIHdpdGggYSBuZXcgbm9kZSBiZWNhdXNlIHRoZSBvbGRcbiAgICAgICAgLy8gbm9kZSB3YXMgbm90IGNvbXBhdGlibGUgd2l0aCB0aGUgdGFyZ2V0IG5vZGUgdGhlbiB3ZSBuZWVkIHRvXG4gICAgICAgIC8vIHJlcGxhY2UgdGhlIG9sZCBET00gbm9kZSBpbiB0aGUgb3JpZ2luYWwgRE9NIHRyZWUuIFRoaXMgaXMgb25seVxuICAgICAgICAvLyBwb3NzaWJsZSBpZiB0aGUgb3JpZ2luYWwgRE9NIG5vZGUgd2FzIHBhcnQgb2YgYSBET00gdHJlZSB3aGljaFxuICAgICAgICAvLyB3ZSBrbm93IGlzIHRoZSBjYXNlIGlmIGl0IGhhcyBhIHBhcmVudCBub2RlLlxuICAgICAgICBmcm9tTm9kZS5wYXJlbnROb2RlLnJlcGxhY2VDaGlsZChtb3JwaGVkTm9kZSwgZnJvbU5vZGUpO1xuICAgIH1cblxuICAgIHJldHVybiBtb3JwaGVkTm9kZTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBtb3JwaGRvbTtcbiIsIm1vZHVsZS5leHBvcnRzID0gW1xuICAvLyBhdHRyaWJ1dGUgZXZlbnRzIChjYW4gYmUgc2V0IHdpdGggYXR0cmlidXRlcylcbiAgJ29uY2xpY2snLFxuICAnb25kYmxjbGljaycsXG4gICdvbm1vdXNlZG93bicsXG4gICdvbm1vdXNldXAnLFxuICAnb25tb3VzZW92ZXInLFxuICAnb25tb3VzZW1vdmUnLFxuICAnb25tb3VzZW91dCcsXG4gICdvbmRyYWdzdGFydCcsXG4gICdvbmRyYWcnLFxuICAnb25kcmFnZW50ZXInLFxuICAnb25kcmFnbGVhdmUnLFxuICAnb25kcmFnb3ZlcicsXG4gICdvbmRyb3AnLFxuICAnb25kcmFnZW5kJyxcbiAgJ29ua2V5ZG93bicsXG4gICdvbmtleXByZXNzJyxcbiAgJ29ua2V5dXAnLFxuICAnb251bmxvYWQnLFxuICAnb25hYm9ydCcsXG4gICdvbmVycm9yJyxcbiAgJ29ucmVzaXplJyxcbiAgJ29uc2Nyb2xsJyxcbiAgJ29uc2VsZWN0JyxcbiAgJ29uY2hhbmdlJyxcbiAgJ29uc3VibWl0JyxcbiAgJ29ucmVzZXQnLFxuICAnb25mb2N1cycsXG4gICdvbmJsdXInLFxuICAnb25pbnB1dCcsXG4gIC8vIG90aGVyIGNvbW1vbiBldmVudHNcbiAgJ29uY29udGV4dG1lbnUnLFxuICAnb25mb2N1c2luJyxcbiAgJ29uZm9jdXNvdXQnXG5dXG4iLCJ2YXIgYmVsID0gcmVxdWlyZSgnYmVsJykgLy8gdHVybnMgdGVtcGxhdGUgdGFnIGludG8gRE9NIGVsZW1lbnRzXG52YXIgbW9ycGhkb20gPSByZXF1aXJlKCdtb3JwaGRvbScpIC8vIGVmZmljaWVudGx5IGRpZmZzICsgbW9ycGhzIHR3byBET00gZWxlbWVudHNcbnZhciBkZWZhdWx0RXZlbnRzID0gcmVxdWlyZSgnLi91cGRhdGUtZXZlbnRzLmpzJykgLy8gZGVmYXVsdCBldmVudHMgdG8gYmUgY29waWVkIHdoZW4gZG9tIGVsZW1lbnRzIHVwZGF0ZVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJlbFxuXG4vLyBUT0RPIG1vdmUgdGhpcyArIGRlZmF1bHRFdmVudHMgdG8gYSBuZXcgbW9kdWxlIG9uY2Ugd2UgcmVjZWl2ZSBtb3JlIGZlZWRiYWNrXG5tb2R1bGUuZXhwb3J0cy51cGRhdGUgPSBmdW5jdGlvbiAoZnJvbU5vZGUsIHRvTm9kZSwgb3B0cykge1xuICBpZiAoIW9wdHMpIG9wdHMgPSB7fVxuICBpZiAob3B0cy5ldmVudHMgIT09IGZhbHNlKSB7XG4gICAgaWYgKCFvcHRzLm9uQmVmb3JlTW9ycGhFbCkgb3B0cy5vbkJlZm9yZU1vcnBoRWwgPSBjb3BpZXJcbiAgfVxuXG4gIHJldHVybiBtb3JwaGRvbShmcm9tTm9kZSwgdG9Ob2RlLCBvcHRzKVxuXG4gIC8vIG1vcnBoZG9tIG9ubHkgY29waWVzIGF0dHJpYnV0ZXMuIHdlIGRlY2lkZWQgd2UgYWxzbyB3YW50ZWQgdG8gY29weSBldmVudHNcbiAgLy8gdGhhdCBjYW4gYmUgc2V0IHZpYSBhdHRyaWJ1dGVzXG4gIGZ1bmN0aW9uIGNvcGllciAoZiwgdCkge1xuICAgIC8vIGNvcHkgZXZlbnRzOlxuICAgIHZhciBldmVudHMgPSBvcHRzLmV2ZW50cyB8fCBkZWZhdWx0RXZlbnRzXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBldmVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBldiA9IGV2ZW50c1tpXVxuICAgICAgaWYgKHRbZXZdKSB7IC8vIGlmIG5ldyBlbGVtZW50IGhhcyBhIHdoaXRlbGlzdGVkIGF0dHJpYnV0ZVxuICAgICAgICBmW2V2XSA9IHRbZXZdIC8vIHVwZGF0ZSBleGlzdGluZyBlbGVtZW50XG4gICAgICB9IGVsc2UgaWYgKGZbZXZdKSB7IC8vIGlmIGV4aXN0aW5nIGVsZW1lbnQgaGFzIGl0IGFuZCBuZXcgb25lIGRvZXNudFxuICAgICAgICBmW2V2XSA9IHVuZGVmaW5lZCAvLyByZW1vdmUgaXQgZnJvbSBleGlzdGluZyBlbGVtZW50XG4gICAgICB9XG4gICAgfVxuICAgIC8vIGNvcHkgdmFsdWVzIGZvciBmb3JtIGVsZW1lbnRzXG4gICAgaWYgKChmLm5vZGVOYW1lID09PSAnSU5QVVQnICYmIGYudHlwZSAhPT0gJ2ZpbGUnKSB8fCBmLm5vZGVOYW1lID09PSAnVEVYVEFSRUEnIHx8IGYubm9kZU5hbWUgPT09ICdTRUxFQ1QnKSB7XG4gICAgICBpZiAodC5nZXRBdHRyaWJ1dGUoJ3ZhbHVlJykgPT09IG51bGwpIHQudmFsdWUgPSBmLnZhbHVlXG4gICAgfVxuICB9XG59XG4iXX0= | |
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
var csjs = require('csjs'); | |
var insertCss = require('insert-css'); | |
function csjsInserter() { | |
var args = Array.prototype.slice.call(arguments); | |
var result = csjs.apply(null, args); | |
if (global.document) { | |
insertCss(csjs.getCss(result)); | |
} | |
return result; | |
} | |
module.exports = csjsInserter; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNzanMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbnZhciBjc2pzID0gcmVxdWlyZSgnY3NqcycpO1xudmFyIGluc2VydENzcyA9IHJlcXVpcmUoJ2luc2VydC1jc3MnKTtcblxuZnVuY3Rpb24gY3Nqc0luc2VydGVyKCkge1xuICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gIHZhciByZXN1bHQgPSBjc2pzLmFwcGx5KG51bGwsIGFyZ3MpO1xuICBpZiAoZ2xvYmFsLmRvY3VtZW50KSB7XG4gICAgaW5zZXJ0Q3NzKGNzanMuZ2V0Q3NzKHJlc3VsdCkpO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gY3Nqc0luc2VydGVyO1xuIl19 | |
},{"csjs":5,"insert-css":17}],2:[function(require,module,exports){ | |
'use strict'; | |
module.exports = require('csjs/get-css'); | |
},{"csjs/get-css":4}],3:[function(require,module,exports){ | |
'use strict'; | |
module.exports = require('./lib/csjs'); | |
},{"./lib/csjs":9}],4:[function(require,module,exports){ | |
'use strict'; | |
module.exports = require('./lib/get-css'); | |
},{"./lib/get-css":12}],5:[function(require,module,exports){ | |
'use strict'; | |
var csjs = require('./csjs'); | |
module.exports = csjs; | |
module.exports.csjs = csjs; | |
module.exports.getCss = require('./get-css'); | |
},{"./csjs":3,"./get-css":4}],6:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* base62 encode implementation based on base62 module: | |
* https://github.com/andrew/base62.js | |
*/ | |
var CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; | |
module.exports = function encode(integer) { | |
if (integer === 0) { | |
return '0'; | |
} | |
var str = ''; | |
while (integer > 0) { | |
str = CHARS[integer % 62] + str; | |
integer = Math.floor(integer / 62); | |
} | |
return str; | |
}; | |
},{}],7:[function(require,module,exports){ | |
'use strict'; | |
var makeComposition = require('./composition').makeComposition; | |
module.exports = function createExports(classes, keyframes, compositions) { | |
var keyframesObj = Object.keys(keyframes).reduce(function(acc, key) { | |
var val = keyframes[key]; | |
acc[val] = makeComposition([key], [val], true); | |
return acc; | |
}, {}); | |
var exports = Object.keys(classes).reduce(function(acc, key) { | |
var val = classes[key]; | |
var composition = compositions[key]; | |
var extended = composition ? getClassChain(composition) : []; | |
var allClasses = [key].concat(extended); | |
var unscoped = allClasses.map(function(name) { | |
return classes[name] ? classes[name] : name; | |
}); | |
acc[val] = makeComposition(allClasses, unscoped); | |
return acc; | |
}, keyframesObj); | |
return exports; | |
} | |
function getClassChain(obj) { | |
var visited = {}, acc = []; | |
function traverse(obj) { | |
return Object.keys(obj).forEach(function(key) { | |
if (!visited[key]) { | |
visited[key] = true; | |
acc.push(key); | |
traverse(obj[key]); | |
} | |
}); | |
} | |
traverse(obj); | |
return acc; | |
} | |
},{"./composition":8}],8:[function(require,module,exports){ | |
'use strict'; | |
module.exports = { | |
makeComposition: makeComposition, | |
isComposition: isComposition | |
}; | |
/** | |
* Returns an immutable composition object containing the given class names | |
* @param {array} classNames - The input array of class names | |
* @return {Composition} - An immutable object that holds multiple | |
* representations of the class composition | |
*/ | |
function makeComposition(classNames, unscoped, isAnimation) { | |
var classString = classNames.join(' '); | |
return Object.create(Composition.prototype, { | |
classNames: { // the original array of class names | |
value: Object.freeze(classNames), | |
configurable: false, | |
writable: false, | |
enumerable: true | |
}, | |
unscoped: { // the original array of class names | |
value: Object.freeze(unscoped), | |
configurable: false, | |
writable: false, | |
enumerable: true | |
}, | |
className: { // space-separated class string for use in HTML | |
value: classString, | |
configurable: false, | |
writable: false, | |
enumerable: true | |
}, | |
selector: { // comma-separated, period-prefixed string for use in CSS | |
value: classNames.map(function(name) { | |
return isAnimation ? name : '.' + name; | |
}).join(', '), | |
configurable: false, | |
writable: false, | |
enumerable: true | |
}, | |
toString: { // toString() method, returns class string for use in HTML | |
value: function() { | |
return classString; | |
}, | |
configurable: false, | |
writeable: false, | |
enumerable: false | |
} | |
}); | |
} | |
/** | |
* Returns whether the input value is a Composition | |
* @param value - value to check | |
* @return {boolean} - whether value is a Composition or not | |
*/ | |
function isComposition(value) { | |
return value instanceof Composition; | |
} | |
/** | |
* Private constructor for use in `instanceof` checks | |
*/ | |
function Composition() {} | |
},{}],9:[function(require,module,exports){ | |
'use strict'; | |
var extractExtends = require('./css-extract-extends'); | |
var isComposition = require('./composition').isComposition; | |
var buildExports = require('./build-exports'); | |
var scopify = require('./scopeify'); | |
var cssKey = require('./css-key'); | |
var mergeProperties = require('./merge-properties'); | |
module.exports = function csjsHandler(strings) { | |
// Fast path to prevent arguments deopt | |
var values = Array(arguments.length - 1); | |
for (var i = 1; i < arguments.length; i++) { | |
values[i - 1] = arguments[i]; | |
} | |
var css = joiner(strings, values.map(selectorize)); | |
var ignores = values.reduce(function(acc, val) { | |
if (isComposition(val)) { | |
val.classNames.forEach(function(name, i) { | |
acc[name] = val.unscoped[i]; | |
}); | |
} | |
return acc; | |
}, {}); | |
var scoped = scopify(css, ignores); | |
var hashes = mergeProperties(scoped.classes, scoped.keyframes); | |
var extracted = extractExtends(scoped.css, hashes); | |
var localClasses = without(scoped.classes, ignores); | |
var localKeyframes = without(scoped.keyframes, ignores); | |
var compositions = extracted.compositions; | |
var exports = buildExports(localClasses, localKeyframes, compositions); | |
return Object.defineProperty(exports, cssKey, { | |
enumerable: false, | |
configurable: false, | |
writeable: false, | |
value: extracted.css | |
}); | |
}; | |
/** | |
* Replaces class compositions with comma seperated class selectors | |
* @param value - the potential class composition | |
* @return - the original value or the selectorized class composition | |
*/ | |
function selectorize(value) { | |
return isComposition(value) ? value.selector : value; | |
} | |
/** | |
* Joins template string literals and values | |
* @param {array} strings - array of strings | |
* @param {array} values - array of values | |
* @return {string} - strings and values joined | |
*/ | |
function joiner(strings, values) { | |
return strings.map(function(str, i) { | |
return (i !== values.length) ? str + values[i] : str; | |
}).join(''); | |
} | |
/** | |
* Returns first object without keys of second | |
* @param {object} obj - source object | |
* @param {object} unwanted - object with unwanted keys | |
* @return {object} - first object without unwanted keys | |
*/ | |
function without(obj, unwanted) { | |
return Object.keys(obj).reduce(function(acc, key) { | |
if (!unwanted[key]) { | |
acc[key] = obj[key]; | |
} | |
return acc; | |
}, {}); | |
} | |
},{"./build-exports":7,"./composition":8,"./css-extract-extends":10,"./css-key":11,"./merge-properties":14,"./scopeify":16}],10:[function(require,module,exports){ | |
'use strict'; | |
var makeComposition = require('./composition').makeComposition; | |
var regex = /\.([^\s]+)(\s+)(extends\s+)(\.[^{]+)/g; | |
module.exports = function extractExtends(css, hashed) { | |
var found, matches = []; | |
while (found = regex.exec(css)) { | |
matches.unshift(found); | |
} | |
function extractCompositions(acc, match) { | |
var extendee = getClassName(match[1]); | |
var keyword = match[3]; | |
var extended = match[4]; | |
// remove from output css | |
var index = match.index + match[1].length + match[2].length; | |
var len = keyword.length + extended.length; | |
acc.css = acc.css.slice(0, index) + " " + acc.css.slice(index + len + 1); | |
var extendedClasses = splitter(extended); | |
extendedClasses.forEach(function(className) { | |
if (!acc.compositions[extendee]) { | |
acc.compositions[extendee] = {}; | |
} | |
if (!acc.compositions[className]) { | |
acc.compositions[className] = {}; | |
} | |
acc.compositions[extendee][className] = acc.compositions[className]; | |
}); | |
return acc; | |
} | |
return matches.reduce(extractCompositions, { | |
css: css, | |
compositions: {} | |
}); | |
}; | |
function splitter(match) { | |
return match.split(',').map(getClassName); | |
} | |
function getClassName(str) { | |
var trimmed = str.trim(); | |
return trimmed[0] === '.' ? trimmed.substr(1) : trimmed; | |
} | |
},{"./composition":8}],11:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* CSS identifiers with whitespace are invalid | |
* Hence this key will not cause a collision | |
*/ | |
module.exports = ' css '; | |
},{}],12:[function(require,module,exports){ | |
'use strict'; | |
var cssKey = require('./css-key'); | |
module.exports = function getCss(csjs) { | |
return csjs[cssKey]; | |
}; | |
},{"./css-key":11}],13:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* djb2 string hash implementation based on string-hash module: | |
* https://github.com/darkskyapp/string-hash | |
*/ | |
module.exports = function hashStr(str) { | |
var hash = 5381; | |
var i = str.length; | |
while (i) { | |
hash = (hash * 33) ^ str.charCodeAt(--i) | |
} | |
return hash >>> 0; | |
}; | |
},{}],14:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* Shallowly merges each argument's properties into a new object | |
* Does not modify source objects and does not check if hasOwnProperty | |
* @param {...object} - the objects to be merged | |
* @returns {object} - the new object | |
*/ | |
module.exports = function mergeProperties() { | |
var target = {}; | |
var i = arguments.length; | |
while (i--) { | |
var source = arguments[i]; | |
for (var key in source) { | |
target[key] = source[key]; | |
} | |
} | |
return target; | |
}; | |
},{}],15:[function(require,module,exports){ | |
'use strict'; | |
var encode = require('./base62-encode'); | |
var hash = require('./hash-string'); | |
module.exports = function fileScoper(fileSrc) { | |
var suffix = encode(hash(fileSrc)); | |
return function scopedName(name) { | |
return name + '_' + suffix; | |
} | |
}; | |
},{"./base62-encode":6,"./hash-string":13}],16:[function(require,module,exports){ | |
'use strict'; | |
var fileScoper = require('./scoped-name'); | |
var findClasses = /(\.)(?!\d)([^\s\.,{\[>+~#:)]*)(?![^{]*})/.source; | |
var findKeyframes = /(@\S*keyframes\s*)([^{\s]*)/.source; | |
var ignoreComments = /(?!(?:[^*/]|\*[^/]|\/[^*])*\*+\/)/.source; | |
var classRegex = new RegExp(findClasses + ignoreComments, 'g'); | |
var keyframesRegex = new RegExp(findKeyframes + ignoreComments, 'g'); | |
module.exports = scopify; | |
function scopify(css, ignores) { | |
var makeScopedName = fileScoper(css); | |
var replacers = { | |
classes: classRegex, | |
keyframes: keyframesRegex | |
}; | |
function scopeCss(result, key) { | |
var replacer = replacers[key]; | |
function replaceFn(fullMatch, prefix, name) { | |
var scopedName = ignores[name] ? name : makeScopedName(name); | |
result[key][scopedName] = name; | |
return prefix + scopedName; | |
} | |
return { | |
css: result.css.replace(replacer, replaceFn), | |
keyframes: result.keyframes, | |
classes: result.classes | |
}; | |
} | |
var result = Object.keys(replacers).reduce(scopeCss, { | |
css: css, | |
keyframes: {}, | |
classes: {} | |
}); | |
return replaceAnimations(result); | |
} | |
function replaceAnimations(result) { | |
var animations = Object.keys(result.keyframes).reduce(function(acc, key) { | |
acc[result.keyframes[key]] = key; | |
return acc; | |
}, {}); | |
var unscoped = Object.keys(animations); | |
if (unscoped.length) { | |
var regexStr = '((?:animation|animation-name)\\s*:[^};]*)(' | |
+ unscoped.join('|') + ')([;\\s])' + ignoreComments; | |
var regex = new RegExp(regexStr, 'g'); | |
var replaced = result.css.replace(regex, function(match, preamble, name, ending) { | |
return preamble + animations[name] + ending; | |
}); | |
return { | |
css: replaced, | |
keyframes: result.keyframes, | |
classes: result.classes | |
} | |
} | |
return result; | |
} | |
},{"./scoped-name":15}],17:[function(require,module,exports){ | |
var inserted = {}; | |
module.exports = function (css, options) { | |
if (inserted[css]) return; | |
inserted[css] = true; | |
var elem = document.createElement('style'); | |
elem.setAttribute('type', 'text/css'); | |
if ('textContent' in elem) { | |
elem.textContent = css; | |
} else { | |
elem.styleSheet.cssText = css; | |
} | |
var head = document.getElementsByTagName('head')[0]; | |
if (options && options.prepend) { | |
head.insertBefore(elem, head.childNodes[0]); | |
} else { | |
head.appendChild(elem); | |
} | |
}; | |
},{}],"csjs-inject":[function(require,module,exports){ | |
arguments[4][5][0].apply(exports,arguments) | |
},{"./csjs":1,"./get-css":2,"dup":5}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImNzanMuanMiLCJnZXQtY3NzLmpzIiwibm9kZV9tb2R1bGVzL2NzanMvY3Nqcy5qcyIsIm5vZGVfbW9kdWxlcy9jc2pzL2dldC1jc3MuanMiLCJub2RlX21vZHVsZXMvY3Nqcy9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9jc2pzL2xpYi9iYXNlNjItZW5jb2RlLmpzIiwibm9kZV9tb2R1bGVzL2NzanMvbGliL2J1aWxkLWV4cG9ydHMuanMiLCJub2RlX21vZHVsZXMvY3Nqcy9saWIvY29tcG9zaXRpb24uanMiLCJub2RlX21vZHVsZXMvY3Nqcy9saWIvY3Nqcy5qcyIsIm5vZGVfbW9kdWxlcy9jc2pzL2xpYi9jc3MtZXh0cmFjdC1leHRlbmRzLmpzIiwibm9kZV9tb2R1bGVzL2NzanMvbGliL2Nzcy1rZXkuanMiLCJub2RlX21vZHVsZXMvY3Nqcy9saWIvZ2V0LWNzcy5qcyIsIm5vZGVfbW9kdWxlcy9jc2pzL2xpYi9oYXNoLXN0cmluZy5qcyIsIm5vZGVfbW9kdWxlcy9jc2pzL2xpYi9tZXJnZS1wcm9wZXJ0aWVzLmpzIiwibm9kZV9tb2R1bGVzL2NzanMvbGliL3Njb3BlZC1uYW1lLmpzIiwibm9kZV9tb2R1bGVzL2NzanMvbGliL3Njb3BlaWZ5LmpzIiwibm9kZV9tb2R1bGVzL2luc2VydC1jc3MvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsQkE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiKGZ1bmN0aW9uIChnbG9iYWwpe1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgY3NqcyA9IHJlcXVpcmUoJ2NzanMnKTtcbnZhciBpbnNlcnRDc3MgPSByZXF1aXJlKCdpbnNlcnQtY3NzJyk7XG5cbmZ1bmN0aW9uIGNzanNJbnNlcnRlcigpIHtcbiAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpO1xuICB2YXIgcmVzdWx0ID0gY3Nqcy5hcHBseShudWxsLCBhcmdzKTtcbiAgaWYgKGdsb2JhbC5kb2N1bWVudCkge1xuICAgIGluc2VydENzcyhjc2pzLmdldENzcyhyZXN1bHQpKTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNzanNJbnNlcnRlcjtcblxufSkuY2FsbCh0aGlzLHR5cGVvZiBnbG9iYWwgIT09IFwidW5kZWZpbmVkXCIgPyBnbG9iYWwgOiB0eXBlb2Ygc2VsZiAhPT0gXCJ1bmRlZmluZWRcIiA/IHNlbGYgOiB0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiID8gd2luZG93IDoge30pXG4vLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldDp1dGYtODtiYXNlNjQsZXlKMlpYSnphVzl1SWpvekxDSnpiM1Z5WTJWeklqcGJJbU56YW5NdWFuTWlYU3dpYm1GdFpYTWlPbHRkTENKdFlYQndhVzVuY3lJNklqdEJRVUZCTzBGQlEwRTdRVUZEUVR0QlFVTkJPMEZCUTBFN1FVRkRRVHRCUVVOQk8wRkJRMEU3UVVGRFFUdEJRVU5CTzBGQlEwRTdRVUZEUVR0QlFVTkJPMEZCUTBFN1FVRkRRVHRCUVVOQklpd2labWxzWlNJNkltZGxibVZ5WVhSbFpDNXFjeUlzSW5OdmRYSmpaVkp2YjNRaU9pSWlMQ0p6YjNWeVkyVnpRMjl1ZEdWdWRDSTZXeUluZFhObElITjBjbWxqZENjN1hHNWNiblpoY2lCamMycHpJRDBnY21WeGRXbHlaU2duWTNOcWN5Y3BPMXh1ZG1GeUlHbHVjMlZ5ZEVOemN5QTlJSEpsY1hWcGNtVW9KMmx1YzJWeWRDMWpjM01uS1R0Y2JseHVablZ1WTNScGIyNGdZM05xYzBsdWMyVnlkR1Z5S0NrZ2UxeHVJQ0IyWVhJZ1lYSm5jeUE5SUVGeWNtRjVMbkJ5YjNSdmRIbHdaUzV6YkdsalpTNWpZV3hzS0dGeVozVnRaVzUwY3lrN1hHNGdJSFpoY2lCeVpYTjFiSFFnUFNCamMycHpMbUZ3Y0d4NUtHNTFiR3dzSUdGeVozTXBPMXh1SUNCcFppQW9aMnh2WW1Gc0xtUnZZM1Z0Wlc1MEtTQjdYRzRnSUNBZ2FXNXpaWEowUTNOektHTnphbk11WjJWMFEzTnpLSEpsYzNWc2RDa3BPMXh1SUNCOVhHNGdJSEpsZEhWeWJpQnlaWE4xYkhRN1hHNTlYRzVjYm0xdlpIVnNaUzVsZUhCdmNuUnpJRDBnWTNOcWMwbHVjMlZ5ZEdWeU8xeHVJbDE5IiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoJ2NzanMvZ2V0LWNzcycpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoJy4vbGliL2NzanMnKTtcbiIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL2xpYi9nZXQtY3NzJyk7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBjc2pzID0gcmVxdWlyZSgnLi9jc2pzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gY3Nqcztcbm1vZHVsZS5leHBvcnRzLmNzanMgPSBjc2pzO1xubW9kdWxlLmV4cG9ydHMuZ2V0Q3NzID0gcmVxdWlyZSgnLi9nZXQtY3NzJyk7XG4iLCIndXNlIHN0cmljdCc7XG5cbi8qKlxuICogYmFzZTYyIGVuY29kZSBpbXBsZW1lbnRhdGlvbiBiYXNlZCBvbiBiYXNlNjIgbW9kdWxlOlxuICogaHR0cHM6Ly9naXRodWIuY29tL2FuZHJldy9iYXNlNjIuanNcbiAqL1xuXG52YXIgQ0hBUlMgPSAnMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVonO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGVuY29kZShpbnRlZ2VyKSB7XG4gIGlmIChpbnRlZ2VyID09PSAwKSB7XG4gICAgcmV0dXJuICcwJztcbiAgfVxuICB2YXIgc3RyID0gJyc7XG4gIHdoaWxlIChpbnRlZ2VyID4gMCkge1xuICAgIHN0ciA9IENIQVJTW2ludGVnZXIgJSA2Ml0gKyBzdHI7XG4gICAgaW50ZWdlciA9IE1hdGguZmxvb3IoaW50ZWdlciAvIDYyKTtcbiAgfVxuICByZXR1cm4gc3RyO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIG1ha2VDb21wb3NpdGlvbiA9IHJlcXVpcmUoJy4vY29tcG9zaXRpb24nKS5tYWtlQ29tcG9zaXRpb247XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gY3JlYXRlRXhwb3J0cyhjbGFzc2VzLCBrZXlmcmFtZXMsIGNvbXBvc2l0aW9ucykge1xuICB2YXIga2V5ZnJhbWVzT2JqID0gT2JqZWN0LmtleXMoa2V5ZnJhbWVzKS5yZWR1Y2UoZnVuY3Rpb24oYWNjLCBrZXkpIHtcbiAgICB2YXIgdmFsID0ga2V5ZnJhbWVzW2tleV07XG4gICAgYWNjW3ZhbF0gPSBtYWtlQ29tcG9zaXRpb24oW2tleV0sIFt2YWxdLCB0cnVlKTtcbiAgICByZXR1cm4gYWNjO1xuICB9LCB7fSk7XG5cbiAgdmFyIGV4cG9ydHMgPSBPYmplY3Qua2V5cyhjbGFzc2VzKS5yZWR1Y2UoZnVuY3Rpb24oYWNjLCBrZXkpIHtcbiAgICB2YXIgdmFsID0gY2xhc3Nlc1trZXldO1xuICAgIHZhciBjb21wb3NpdGlvbiA9IGNvbXBvc2l0aW9uc1trZXldO1xuICAgIHZhciBleHRlbmRlZCA9IGNvbXBvc2l0aW9uID8gZ2V0Q2xhc3NDaGFpbihjb21wb3NpdGlvbikgOiBbXTtcbiAgICB2YXIgYWxsQ2xhc3NlcyA9IFtrZXldLmNvbmNhdChleHRlbmRlZCk7XG4gICAgdmFyIHVuc2NvcGVkID0gYWxsQ2xhc3Nlcy5tYXAoZnVuY3Rpb24obmFtZSkge1xuICAgICAgcmV0dXJuIGNsYXNzZXNbbmFtZV0gPyBjbGFzc2VzW25hbWVdIDogbmFtZTtcbiAgICB9KTtcbiAgICBhY2NbdmFsXSA9IG1ha2VDb21wb3NpdGlvbihhbGxDbGFzc2VzLCB1bnNjb3BlZCk7XG4gICAgcmV0dXJuIGFjYztcbiAgfSwga2V5ZnJhbWVzT2JqKTtcblxuICByZXR1cm4gZXhwb3J0cztcbn1cblxuZnVuY3Rpb24gZ2V0Q2xhc3NDaGFpbihvYmopIHtcbiAgdmFyIHZpc2l0ZWQgPSB7fSwgYWNjID0gW107XG5cbiAgZnVuY3Rpb24gdHJhdmVyc2Uob2JqKSB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKG9iaikuZm9yRWFjaChmdW5jdGlvbihrZXkpIHtcbiAgICAgIGlmICghdmlzaXRlZFtrZXldKSB7XG4gICAgICAgIHZpc2l0ZWRba2V5XSA9IHRydWU7XG4gICAgICAgIGFjYy5wdXNoKGtleSk7XG4gICAgICAgIHRyYXZlcnNlKG9ialtrZXldKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHRyYXZlcnNlKG9iaik7XG4gIHJldHVybiBhY2M7XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBtYWtlQ29tcG9zaXRpb246IG1ha2VDb21wb3NpdGlvbixcbiAgaXNDb21wb3NpdGlvbjogaXNDb21wb3NpdGlvblxufTtcblxuLyoqXG4gKiBSZXR1cm5zIGFuIGltbXV0YWJsZSBjb21wb3NpdGlvbiBvYmplY3QgY29udGFpbmluZyB0aGUgZ2l2ZW4gY2xhc3MgbmFtZXNcbiAqIEBwYXJhbSAge2FycmF5fSBjbGFzc05hbWVzIC0gVGhlIGlucHV0IGFycmF5IG9mIGNsYXNzIG5hbWVzXG4gKiBAcmV0dXJuIHtDb21wb3NpdGlvbn0gICAgICAtIEFuIGltbXV0YWJsZSBvYmplY3QgdGhhdCBob2xkcyBtdWx0aXBsZVxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXByZXNlbnRhdGlvbnMgb2YgdGhlIGNsYXNzIGNvbXBvc2l0aW9uXG4gKi9cbmZ1bmN0aW9uIG1ha2VDb21wb3NpdGlvbihjbGFzc05hbWVzLCB1bnNjb3BlZCwgaXNBbmltYXRpb24pIHtcbiAgdmFyIGNsYXNzU3RyaW5nID0gY2xhc3NOYW1lcy5qb2luKCcgJyk7XG4gIHJldHVybiBPYmplY3QuY3JlYXRlKENvbXBvc2l0aW9uLnByb3RvdHlwZSwge1xuICAgIGNsYXNzTmFtZXM6IHsgLy8gdGhlIG9yaWdpbmFsIGFycmF5IG9mIGNsYXNzIG5hbWVzXG4gICAgICB2YWx1ZTogT2JqZWN0LmZyZWV6ZShjbGFzc05hbWVzKSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gICAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlXG4gICAgfSxcbiAgICB1bnNjb3BlZDogeyAvLyB0aGUgb3JpZ2luYWwgYXJyYXkgb2YgY2xhc3MgbmFtZXNcbiAgICAgIHZhbHVlOiBPYmplY3QuZnJlZXplKHVuc2NvcGVkKSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gICAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlXG4gICAgfSxcbiAgICBjbGFzc05hbWU6IHsgLy8gc3BhY2Utc2VwYXJhdGVkIGNsYXNzIHN0cmluZyBmb3IgdXNlIGluIEhUTUxcbiAgICAgIHZhbHVlOiBjbGFzc1N0cmluZyxcbiAgICAgIGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gICAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlXG4gICAgfSxcbiAgICBzZWxlY3RvcjogeyAvLyBjb21tYS1zZXBhcmF0ZWQsIHBlcmlvZC1wcmVmaXhlZCBzdHJpbmcgZm9yIHVzZSBpbiBDU1NcbiAgICAgIHZhbHVlOiBjbGFzc05hbWVzLm1hcChmdW5jdGlvbihuYW1lKSB7XG4gICAgICAgIHJldHVybiBpc0FuaW1hdGlvbiA/IG5hbWUgOiAnLicgKyBuYW1lO1xuICAgICAgfSkuam9pbignLCAnKSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gICAgICB3cml0YWJsZTogZmFsc2UsXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlXG4gICAgfSxcbiAgICB0b1N0cmluZzogeyAvLyB0b1N0cmluZygpIG1ldGhvZCwgcmV0dXJucyBjbGFzcyBzdHJpbmcgZm9yIHVzZSBpbiBIVE1MXG4gICAgICB2YWx1ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBjbGFzc1N0cmluZztcbiAgICAgIH0sXG4gICAgICBjb25maWd1cmFibGU6IGZhbHNlLFxuICAgICAgd3JpdGVhYmxlOiBmYWxzZSxcbiAgICAgIGVudW1lcmFibGU6IGZhbHNlXG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHdoZXRoZXIgdGhlIGlucHV0IHZhbHVlIGlzIGEgQ29tcG9zaXRpb25cbiAqIEBwYXJhbSB2YWx1ZSAgICAgIC0gdmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm4ge2Jvb2xlYW59IC0gd2hldGhlciB2YWx1ZSBpcyBhIENvbXBvc2l0aW9uIG9yIG5vdFxuICovXG5mdW5jdGlvbiBpc0NvbXBvc2l0aW9uKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZSBpbnN0YW5jZW9mIENvbXBvc2l0aW9uO1xufVxuXG4vKipcbiAqIFByaXZhdGUgY29uc3RydWN0b3IgZm9yIHVzZSBpbiBgaW5zdGFuY2VvZmAgY2hlY2tzXG4gKi9cbmZ1bmN0aW9uIENvbXBvc2l0aW9uKCkge31cbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGV4dHJhY3RFeHRlbmRzID0gcmVxdWlyZSgnLi9jc3MtZXh0cmFjdC1leHRlbmRzJyk7XG52YXIgaXNDb21wb3NpdGlvbiA9IHJlcXVpcmUoJy4vY29tcG9zaXRpb24nKS5pc0NvbXBvc2l0aW9uO1xudmFyIGJ1aWxkRXhwb3J0cyA9IHJlcXVpcmUoJy4vYnVpbGQtZXhwb3J0cycpO1xudmFyIHNjb3BpZnkgPSByZXF1aXJlKCcuL3Njb3BlaWZ5Jyk7XG52YXIgY3NzS2V5ID0gcmVxdWlyZSgnLi9jc3Mta2V5Jyk7XG52YXIgbWVyZ2VQcm9wZXJ0aWVzID0gcmVxdWlyZSgnLi9tZXJnZS1wcm9wZXJ0aWVzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gY3Nqc0hhbmRsZXIoc3RyaW5ncykge1xuICAvLyBGYXN0IHBhdGggdG8gcHJldmVudCBhcmd1bWVudHMgZGVvcHRcbiAgdmFyIHZhbHVlcyA9IEFycmF5KGFyZ3VtZW50cy5sZW5ndGggLSAxKTtcbiAgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICB2YWx1ZXNbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICB9XG4gIHZhciBjc3MgPSBqb2luZXIoc3RyaW5ncywgdmFsdWVzLm1hcChzZWxlY3Rvcml6ZSkpO1xuXG4gIHZhciBpZ25vcmVzID0gdmFsdWVzLnJlZHVjZShmdW5jdGlvbihhY2MsIHZhbCkge1xuICAgIGlmIChpc0NvbXBvc2l0aW9uKHZhbCkpIHtcbiAgICAgIHZhbC5jbGFzc05hbWVzLmZvckVhY2goZnVuY3Rpb24obmFtZSwgaSkge1xuICAgICAgICBhY2NbbmFtZV0gPSB2YWwudW5zY29wZWRbaV07XG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIGFjYztcbiAgfSwge30pO1xuXG4gIHZhciBzY29wZWQgPSBzY29waWZ5KGNzcywgaWdub3Jlcyk7XG4gIHZhciBoYXNoZXMgPSBtZXJnZVByb3BlcnRpZXMoc2NvcGVkLmNsYXNzZXMsIHNjb3BlZC5rZXlmcmFtZXMpO1xuICB2YXIgZXh0cmFjdGVkID0gZXh0cmFjdEV4dGVuZHMoc2NvcGVkLmNzcywgaGFzaGVzKTtcblxuICB2YXIgbG9jYWxDbGFzc2VzID0gd2l0aG91dChzY29wZWQuY2xhc3NlcywgaWdub3Jlcyk7XG4gIHZhciBsb2NhbEtleWZyYW1lcyA9IHdpdGhvdXQoc2NvcGVkLmtleWZyYW1lcywgaWdub3Jlcyk7XG4gIHZhciBjb21wb3NpdGlvbnMgPSBleHRyYWN0ZWQuY29tcG9zaXRpb25zO1xuXG4gIHZhciBleHBvcnRzID0gYnVpbGRFeHBvcnRzKGxvY2FsQ2xhc3NlcywgbG9jYWxLZXlmcmFtZXMsIGNvbXBvc2l0aW9ucyk7XG5cbiAgcmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBjc3NLZXksIHtcbiAgICBlbnVtZXJhYmxlOiBmYWxzZSxcbiAgICBjb25maWd1cmFibGU6IGZhbHNlLFxuICAgIHdyaXRlYWJsZTogZmFsc2UsXG4gICAgdmFsdWU6IGV4dHJhY3RlZC5jc3NcbiAgfSk7XG59O1xuXG4vKipcbiAqIFJlcGxhY2VzIGNsYXNzIGNvbXBvc2l0aW9ucyB3aXRoIGNvbW1hIHNlcGVyYXRlZCBjbGFzcyBzZWxlY3RvcnNcbiAqIEBwYXJhbSAgdmFsdWUgLSB0aGUgcG90ZW50aWFsIGNsYXNzIGNvbXBvc2l0aW9uXG4gKiBAcmV0dXJuICAgICAgIC0gdGhlIG9yaWdpbmFsIHZhbHVlIG9yIHRoZSBzZWxlY3Rvcml6ZWQgY2xhc3MgY29tcG9zaXRpb25cbiAqL1xuZnVuY3Rpb24gc2VsZWN0b3JpemUodmFsdWUpIHtcbiAgcmV0dXJuIGlzQ29tcG9zaXRpb24odmFsdWUpID8gdmFsdWUuc2VsZWN0b3IgOiB2YWx1ZTtcbn1cblxuLyoqXG4gKiBKb2lucyB0ZW1wbGF0ZSBzdHJpbmcgbGl0ZXJhbHMgYW5kIHZhbHVlc1xuICogQHBhcmFtICB7YXJyYXl9IHN0cmluZ3MgLSBhcnJheSBvZiBzdHJpbmdzXG4gKiBAcGFyYW0gIHthcnJheX0gdmFsdWVzICAtIGFycmF5IG9mIHZhbHVlc1xuICogQHJldHVybiB7c3RyaW5nfSAgICAgICAgLSBzdHJpbmdzIGFuZCB2YWx1ZXMgam9pbmVkXG4gKi9cbmZ1bmN0aW9uIGpvaW5lcihzdHJpbmdzLCB2YWx1ZXMpIHtcbiAgcmV0dXJuIHN0cmluZ3MubWFwKGZ1bmN0aW9uKHN0ciwgaSkge1xuICAgIHJldHVybiAoaSAhPT0gdmFsdWVzLmxlbmd0aCkgPyBzdHIgKyB2YWx1ZXNbaV0gOiBzdHI7XG4gIH0pLmpvaW4oJycpO1xufVxuXG4vKipcbiAqIFJldHVybnMgZmlyc3Qgb2JqZWN0IHdpdGhvdXQga2V5cyBvZiBzZWNvbmRcbiAqIEBwYXJhbSAge29iamVjdH0gb2JqICAgICAgLSBzb3VyY2Ugb2JqZWN0XG4gKiBAcGFyYW0gIHtvYmplY3R9IHVud2FudGVkIC0gb2JqZWN0IHdpdGggdW53YW50ZWQga2V5c1xuICogQHJldHVybiB7b2JqZWN0fSAgICAgICAgICAtIGZpcnN0IG9iamVjdCB3aXRob3V0IHVud2FudGVkIGtleXNcbiAqL1xuZnVuY3Rpb24gd2l0aG91dChvYmosIHVud2FudGVkKSB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhvYmopLnJlZHVjZShmdW5jdGlvbihhY2MsIGtleSkge1xuICAgIGlmICghdW53YW50ZWRba2V5XSkge1xuICAgICAgYWNjW2tleV0gPSBvYmpba2V5XTtcbiAgICB9XG4gICAgcmV0dXJuIGFjYztcbiAgfSwge30pO1xufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgbWFrZUNvbXBvc2l0aW9uID0gcmVxdWlyZSgnLi9jb21wb3NpdGlvbicpLm1ha2VDb21wb3NpdGlvbjtcblxudmFyIHJlZ2V4ID0gL1xcLihbXlxcc10rKShcXHMrKShleHRlbmRzXFxzKykoXFwuW157XSspL2c7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gZXh0cmFjdEV4dGVuZHMoY3NzLCBoYXNoZWQpIHtcbiAgdmFyIGZvdW5kLCBtYXRjaGVzID0gW107XG4gIHdoaWxlIChmb3VuZCA9IHJlZ2V4LmV4ZWMoY3NzKSkge1xuICAgIG1hdGNoZXMudW5zaGlmdChmb3VuZCk7XG4gIH1cblxuICBmdW5jdGlvbiBleHRyYWN0Q29tcG9zaXRpb25zKGFjYywgbWF0Y2gpIHtcbiAgICB2YXIgZXh0ZW5kZWUgPSBnZXRDbGFzc05hbWUobWF0Y2hbMV0pO1xuICAgIHZhciBrZXl3b3JkID0gbWF0Y2hbM107XG4gICAgdmFyIGV4dGVuZGVkID0gbWF0Y2hbNF07XG5cbiAgICAvLyByZW1vdmUgZnJvbSBvdXRwdXQgY3NzXG4gICAgdmFyIGluZGV4ID0gbWF0Y2guaW5kZXggKyBtYXRjaFsxXS5sZW5ndGggKyBtYXRjaFsyXS5sZW5ndGg7XG4gICAgdmFyIGxlbiA9IGtleXdvcmQubGVuZ3RoICsgZXh0ZW5kZWQubGVuZ3RoO1xuICAgIGFjYy5jc3MgPSBhY2MuY3NzLnNsaWNlKDAsIGluZGV4KSArIFwiIFwiICsgYWNjLmNzcy5zbGljZShpbmRleCArIGxlbiArIDEpO1xuXG4gICAgdmFyIGV4dGVuZGVkQ2xhc3NlcyA9IHNwbGl0dGVyKGV4dGVuZGVkKTtcblxuICAgIGV4dGVuZGVkQ2xhc3Nlcy5mb3JFYWNoKGZ1bmN0aW9uKGNsYXNzTmFtZSkge1xuICAgICAgaWYgKCFhY2MuY29tcG9zaXRpb25zW2V4dGVuZGVlXSkge1xuICAgICAgICBhY2MuY29tcG9zaXRpb25zW2V4dGVuZGVlXSA9IHt9O1xuICAgICAgfVxuICAgICAgaWYgKCFhY2MuY29tcG9zaXRpb25zW2NsYXNzTmFtZV0pIHtcbiAgICAgICAgYWNjLmNvbXBvc2l0aW9uc1tjbGFzc05hbWVdID0ge307XG4gICAgICB9XG4gICAgICBhY2MuY29tcG9zaXRpb25zW2V4dGVuZGVlXVtjbGFzc05hbWVdID0gYWNjLmNvbXBvc2l0aW9uc1tjbGFzc05hbWVdO1xuICAgIH0pO1xuICAgIHJldHVybiBhY2M7XG4gIH1cblxuICByZXR1cm4gbWF0Y2hlcy5yZWR1Y2UoZXh0cmFjdENvbXBvc2l0aW9ucywge1xuICAgIGNzczogY3NzLFxuICAgIGNvbXBvc2l0aW9uczoge31cbiAgfSk7XG5cbn07XG5cbmZ1bmN0aW9uIHNwbGl0dGVyKG1hdGNoKSB7XG4gIHJldHVybiBtYXRjaC5zcGxpdCgnLCcpLm1hcChnZXRDbGFzc05hbWUpO1xufVxuXG5mdW5jdGlvbiBnZXRDbGFzc05hbWUoc3RyKSB7XG4gIHZhciB0cmltbWVkID0gc3RyLnRyaW0oKTtcbiAgcmV0dXJuIHRyaW1tZWRbMF0gPT09ICcuJyA/IHRyaW1tZWQuc3Vic3RyKDEpIDogdHJpbW1lZDtcbn1cbiIsIid1c2Ugc3RyaWN0JztcblxuLyoqXG4gKiBDU1MgaWRlbnRpZmllcnMgd2l0aCB3aGl0ZXNwYWNlIGFyZSBpbnZhbGlkXG4gKiBIZW5jZSB0aGlzIGtleSB3aWxsIG5vdCBjYXVzZSBhIGNvbGxpc2lvblxuICovXG5cbm1vZHVsZS5leHBvcnRzID0gJyBjc3MgJztcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGNzc0tleSA9IHJlcXVpcmUoJy4vY3NzLWtleScpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGdldENzcyhjc2pzKSB7XG4gIHJldHVybiBjc2pzW2Nzc0tleV07XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG4vKipcbiAqIGRqYjIgc3RyaW5nIGhhc2ggaW1wbGVtZW50YXRpb24gYmFzZWQgb24gc3RyaW5nLWhhc2ggbW9kdWxlOlxuICogaHR0cHM6Ly9naXRodWIuY29tL2Rhcmtza3lhcHAvc3RyaW5nLWhhc2hcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGhhc2hTdHIoc3RyKSB7XG4gIHZhciBoYXNoID0gNTM4MTtcbiAgdmFyIGkgPSBzdHIubGVuZ3RoO1xuXG4gIHdoaWxlIChpKSB7XG4gICAgaGFzaCA9IChoYXNoICogMzMpIF4gc3RyLmNoYXJDb2RlQXQoLS1pKVxuICB9XG4gIHJldHVybiBoYXNoID4+PiAwO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuLyoqXG4gKiBTaGFsbG93bHkgbWVyZ2VzIGVhY2ggYXJndW1lbnQncyBwcm9wZXJ0aWVzIGludG8gYSBuZXcgb2JqZWN0XG4gKiBEb2VzIG5vdCBtb2RpZnkgc291cmNlIG9iamVjdHMgYW5kIGRvZXMgbm90IGNoZWNrIGlmIGhhc093blByb3BlcnR5XG4gKiBAcGFyYW0gey4uLm9iamVjdH0gLSB0aGUgb2JqZWN0cyB0byBiZSBtZXJnZWRcbiAqIEByZXR1cm5zIHtvYmplY3R9ICAtIHRoZSBuZXcgb2JqZWN0XG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gbWVyZ2VQcm9wZXJ0aWVzKCkge1xuICB2YXIgdGFyZ2V0ID0ge307XG5cbiAgdmFyIGkgPSBhcmd1bWVudHMubGVuZ3RoO1xuICB3aGlsZSAoaS0tKSB7XG4gICAgdmFyIHNvdXJjZSA9IGFyZ3VtZW50c1tpXTtcbiAgICBmb3IgKHZhciBrZXkgaW4gc291cmNlKSB7XG4gICAgICB0YXJnZXRba2V5XSA9IHNvdXJjZVtrZXldO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0YXJnZXQ7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgZW5jb2RlID0gcmVxdWlyZSgnLi9iYXNlNjItZW5jb2RlJyk7XG52YXIgaGFzaCA9IHJlcXVpcmUoJy4vaGFzaC1zdHJpbmcnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBmaWxlU2NvcGVyKGZpbGVTcmMpIHtcbiAgdmFyIHN1ZmZpeCA9IGVuY29kZShoYXNoKGZpbGVTcmMpKTtcblxuICByZXR1cm4gZnVuY3Rpb24gc2NvcGVkTmFtZShuYW1lKSB7XG4gICAgcmV0dXJuIG5hbWUgKyAnXycgKyBzdWZmaXg7XG4gIH1cbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBmaWxlU2NvcGVyID0gcmVxdWlyZSgnLi9zY29wZWQtbmFtZScpO1xuXG52YXIgZmluZENsYXNzZXMgPSAvKFxcLikoPyFcXGQpKFteXFxzXFwuLHtcXFs+K34jOildKikoPyFbXntdKn0pLy5zb3VyY2U7XG52YXIgZmluZEtleWZyYW1lcyA9IC8oQFxcUyprZXlmcmFtZXNcXHMqKShbXntcXHNdKikvLnNvdXJjZTtcbnZhciBpZ25vcmVDb21tZW50cyA9IC8oPyEoPzpbXiovXXxcXCpbXi9dfFxcL1teKl0pKlxcKitcXC8pLy5zb3VyY2U7XG5cbnZhciBjbGFzc1JlZ2V4ID0gbmV3IFJlZ0V4cChmaW5kQ2xhc3NlcyArIGlnbm9yZUNvbW1lbnRzLCAnZycpO1xudmFyIGtleWZyYW1lc1JlZ2V4ID0gbmV3IFJlZ0V4cChmaW5kS2V5ZnJhbWVzICsgaWdub3JlQ29tbWVudHMsICdnJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gc2NvcGlmeTtcblxuZnVuY3Rpb24gc2NvcGlmeShjc3MsIGlnbm9yZXMpIHtcbiAgdmFyIG1ha2VTY29wZWROYW1lID0gZmlsZVNjb3Blcihjc3MpO1xuICB2YXIgcmVwbGFjZXJzID0ge1xuICAgIGNsYXNzZXM6IGNsYXNzUmVnZXgsXG4gICAga2V5ZnJhbWVzOiBrZXlmcmFtZXNSZWdleFxuICB9O1xuXG4gIGZ1bmN0aW9uIHNjb3BlQ3NzKHJlc3VsdCwga2V5KSB7XG4gICAgdmFyIHJlcGxhY2VyID0gcmVwbGFjZXJzW2tleV07XG4gICAgZnVuY3Rpb24gcmVwbGFjZUZuKGZ1bGxNYXRjaCwgcHJlZml4LCBuYW1lKSB7XG4gICAgICB2YXIgc2NvcGVkTmFtZSA9IGlnbm9yZXNbbmFtZV0gPyBuYW1lIDogbWFrZVNjb3BlZE5hbWUobmFtZSk7XG4gICAgICByZXN1bHRba2V5XVtzY29wZWROYW1lXSA9IG5hbWU7XG4gICAgICByZXR1cm4gcHJlZml4ICsgc2NvcGVkTmFtZTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGNzczogcmVzdWx0LmNzcy5yZXBsYWNlKHJlcGxhY2VyLCByZXBsYWNlRm4pLFxuICAgICAga2V5ZnJhbWVzOiByZXN1bHQua2V5ZnJhbWVzLFxuICAgICAgY2xhc3NlczogcmVzdWx0LmNsYXNzZXNcbiAgICB9O1xuICB9XG5cbiAgdmFyIHJlc3VsdCA9IE9iamVjdC5rZXlzKHJlcGxhY2VycykucmVkdWNlKHNjb3BlQ3NzLCB7XG4gICAgY3NzOiBjc3MsXG4gICAga2V5ZnJhbWVzOiB7fSxcbiAgICBjbGFzc2VzOiB7fVxuICB9KTtcblxuICByZXR1cm4gcmVwbGFjZUFuaW1hdGlvbnMocmVzdWx0KTtcbn1cblxuZnVuY3Rpb24gcmVwbGFjZUFuaW1hdGlvbnMocmVzdWx0KSB7XG4gIHZhciBhbmltYXRpb25zID0gT2JqZWN0LmtleXMocmVzdWx0LmtleWZyYW1lcykucmVkdWNlKGZ1bmN0aW9uKGFjYywga2V5KSB7XG4gICAgYWNjW3Jlc3VsdC5rZXlmcmFtZXNba2V5XV0gPSBrZXk7XG4gICAgcmV0dXJuIGFjYztcbiAgfSwge30pO1xuICB2YXIgdW5zY29wZWQgPSBPYmplY3Qua2V5cyhhbmltYXRpb25zKTtcblxuICBpZiAodW5zY29wZWQubGVuZ3RoKSB7XG4gICAgdmFyIHJlZ2V4U3RyID0gJygoPzphbmltYXRpb258YW5pbWF0aW9uLW5hbWUpXFxcXHMqOltefTtdKikoJ1xuICAgICAgKyB1bnNjb3BlZC5qb2luKCd8JykgKyAnKShbO1xcXFxzXSknICsgaWdub3JlQ29tbWVudHM7XG4gICAgdmFyIHJlZ2V4ID0gbmV3IFJlZ0V4cChyZWdleFN0ciwgJ2cnKTtcblxuICAgIHZhciByZXBsYWNlZCA9IHJlc3VsdC5jc3MucmVwbGFjZShyZWdleCwgZnVuY3Rpb24obWF0Y2gsIHByZWFtYmxlLCBuYW1lLCBlbmRpbmcpIHtcbiAgICAgIHJldHVybiBwcmVhbWJsZSArIGFuaW1hdGlvbnNbbmFtZV0gKyBlbmRpbmc7XG4gICAgfSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgY3NzOiByZXBsYWNlZCxcbiAgICAgIGtleWZyYW1lczogcmVzdWx0LmtleWZyYW1lcyxcbiAgICAgIGNsYXNzZXM6IHJlc3VsdC5jbGFzc2VzXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsInZhciBpbnNlcnRlZCA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjc3MsIG9wdGlvbnMpIHtcbiAgICBpZiAoaW5zZXJ0ZWRbY3NzXSkgcmV0dXJuO1xuICAgIGluc2VydGVkW2Nzc10gPSB0cnVlO1xuICAgIFxuICAgIHZhciBlbGVtID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3R5bGUnKTtcbiAgICBlbGVtLnNldEF0dHJpYnV0ZSgndHlwZScsICd0ZXh0L2NzcycpO1xuXG4gICAgaWYgKCd0ZXh0Q29udGVudCcgaW4gZWxlbSkge1xuICAgICAgZWxlbS50ZXh0Q29udGVudCA9IGNzcztcbiAgICB9IGVsc2Uge1xuICAgICAgZWxlbS5zdHlsZVNoZWV0LmNzc1RleHQgPSBjc3M7XG4gICAgfVxuICAgIFxuICAgIHZhciBoZWFkID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2hlYWQnKVswXTtcbiAgICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLnByZXBlbmQpIHtcbiAgICAgICAgaGVhZC5pbnNlcnRCZWZvcmUoZWxlbSwgaGVhZC5jaGlsZE5vZGVzWzBdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBoZWFkLmFwcGVuZENoaWxkKGVsZW0pO1xuICAgIH1cbn07XG4iXX0= | |
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
var xhrFactory = (function getXHRfactory (factories) { | |
for (var i=0, xhr, X, len=factories.length; i<len; i++) { | |
try { X = factories[i]; xhr = X(); | |
return window.XMLHttpRequest ? X : window.XMLHttpRequest = X; | |
} catch (e) { continue; } | |
} | |
})([ | |
function () {return new XMLHttpRequest();},// IE10+,FF,Chrome,Opera,Safari | |
function () {return new ActiveXObject("Msxml3.");}, // IE9 | |
function () {return new ActiveXObject("Msxml2.XMLHTTP.6.0");}, // IE8 | |
function () {return new ActiveXObject("Msxml2.XMLHTTP.3.0");}, // IE7 | |
function () {return new ActiveXObject("Msxml2.XMLHTTP");}, // IE6 | |
function () {return new ActiveXObject("Microsoft.XMLHTTP");}, // IE5 | |
function () {return null;} | |
]); | |
module.exports = function getXHR() { return xhrFactory(); } | |
},{}],"minixhr":[function(require,module,exports){ | |
var XMLHttpRequest = require('xhrpolyfill') | |
module.exports = function xhr2 (params, callback) { | |
var url = typeof params === 'string' ? params : params.url | |
var method = params.method || (params.data ? 'POST': 'GET') | |
var body = params.data | |
var H = params.headers ? params.headers : params.body ? { | |
'X-Requested-With' :'XMLHttpRequest', | |
'Content-Type' :'application/x-www-form-urlencoded' | |
} : {} | |
var xhr = XMLHttpRequest() | |
if (!xhr) throw new Error('No AJAX support') | |
xhr.open(method, url) | |
for (var key in H) xhr.setRequestHeader(key, H[key]) | |
xhr.onload = xhr.onerror = function (response) { | |
var Hjson = {}, h = xhr.getAllResponseHeaders() | |
;(h.match(/([^\n\r:]+):([^\n\r]+)/g)||[]).forEach(function(item){ | |
var tmp = item.split(': ') | |
Hjson[tmp[0]] = tmp[1] | |
}) | |
if (callback) callback(this.response, response, xhr, Hjson) | |
} | |
xhr.send(body||null) | |
} | |
},{"xhrpolyfill":1}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsIm5vZGVfbW9kdWxlcy94aHJwb2x5ZmlsbC9pbmRleC5qcyIsIm1pbml4aHIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwidmFyIHhockZhY3RvcnkgPSAoZnVuY3Rpb24gZ2V0WEhSZmFjdG9yeSAoZmFjdG9yaWVzKSB7XG4gIGZvciAodmFyIGk9MCwgeGhyLCBYLCBsZW49ZmFjdG9yaWVzLmxlbmd0aDsgaTxsZW47IGkrKykge1xuICAgIHRyeSB7IFggPSBmYWN0b3JpZXNbaV07IHhociA9IFgoKTtcbiAgICAgIHJldHVybiB3aW5kb3cuWE1MSHR0cFJlcXVlc3QgPyBYIDogd2luZG93LlhNTEh0dHBSZXF1ZXN0ID0gWDtcbiAgICB9IGNhdGNoIChlKSB7IGNvbnRpbnVlOyB9XG4gIH1cbn0pKFtcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgWE1MSHR0cFJlcXVlc3QoKTt9LC8vIElFMTArLEZGLENocm9tZSxPcGVyYSxTYWZhcmlcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgQWN0aXZlWE9iamVjdChcIk1zeG1sMy5cIik7fSwgICAgICAgICAgICAvLyBJRTlcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgQWN0aXZlWE9iamVjdChcIk1zeG1sMi5YTUxIVFRQLjYuMFwiKTt9LCAvLyBJRThcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgQWN0aXZlWE9iamVjdChcIk1zeG1sMi5YTUxIVFRQLjMuMFwiKTt9LCAvLyBJRTdcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgQWN0aXZlWE9iamVjdChcIk1zeG1sMi5YTUxIVFRQXCIpO30sICAgICAvLyBJRTZcbiAgZnVuY3Rpb24gKCkge3JldHVybiBuZXcgQWN0aXZlWE9iamVjdChcIk1pY3Jvc29mdC5YTUxIVFRQXCIpO30sICAvLyBJRTVcbiAgZnVuY3Rpb24gKCkge3JldHVybiBudWxsO31cbl0pO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBnZXRYSFIoKSB7IHJldHVybiB4aHJGYWN0b3J5KCk7IH1cbiIsInZhciBYTUxIdHRwUmVxdWVzdCAgPSByZXF1aXJlKCd4aHJwb2x5ZmlsbCcpXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIHhocjIgKHBhcmFtcywgY2FsbGJhY2spIHtcbiAgdmFyIHVybCA9IHR5cGVvZiBwYXJhbXMgPT09ICdzdHJpbmcnID8gcGFyYW1zIDogcGFyYW1zLnVybFxuICB2YXIgbWV0aG9kID0gcGFyYW1zLm1ldGhvZCB8fCAocGFyYW1zLmRhdGEgPyAnUE9TVCc6ICdHRVQnKVxuICB2YXIgYm9keSA9IHBhcmFtcy5kYXRhXG4gIHZhciBIID0gcGFyYW1zLmhlYWRlcnMgPyBwYXJhbXMuaGVhZGVycyA6IHBhcmFtcy5ib2R5ID8ge1xuICAgICdYLVJlcXVlc3RlZC1XaXRoJyA6J1hNTEh0dHBSZXF1ZXN0JyxcbiAgICAnQ29udGVudC1UeXBlJyAgICAgOidhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnXG4gIH0gOiB7fVxuICB2YXIgeGhyID0gWE1MSHR0cFJlcXVlc3QoKVxuICBpZiAoIXhocikgdGhyb3cgbmV3IEVycm9yKCdObyBBSkFYIHN1cHBvcnQnKVxuICB4aHIub3BlbihtZXRob2QsIHVybClcbiAgZm9yICh2YXIga2V5IGluIEgpIHhoci5zZXRSZXF1ZXN0SGVhZGVyKGtleSwgSFtrZXldKVxuICB4aHIub25sb2FkID0geGhyLm9uZXJyb3IgPSBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICB2YXIgSGpzb24gPSB7fSwgaCA9IHhoci5nZXRBbGxSZXNwb25zZUhlYWRlcnMoKVxuICAgIDsoaC5tYXRjaCgvKFteXFxuXFxyOl0rKTooW15cXG5cXHJdKykvZyl8fFtdKS5mb3JFYWNoKGZ1bmN0aW9uKGl0ZW0pe1xuICAgICAgdmFyIHRtcCA9IGl0ZW0uc3BsaXQoJzogJylcbiAgICAgIEhqc29uW3RtcFswXV0gPSB0bXBbMV1cbiAgICB9KVxuICAgIGlmIChjYWxsYmFjaykgY2FsbGJhY2sodGhpcy5yZXNwb25zZSwgcmVzcG9uc2UsIHhociwgSGpzb24pXG4gIH1cbiAgeGhyLnNlbmQoYm9keXx8bnVsbClcbn1cbiJdfQ== | |
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
/* MIT license */ | |
var convert = require('color-convert'); | |
var string = require('chartjs-color-string'); | |
var Color = function (obj) { | |
if (obj instanceof Color) { | |
return obj; | |
} | |
if (!(this instanceof Color)) { | |
return new Color(obj); | |
} | |
this.values = { | |
rgb: [0, 0, 0], | |
hsl: [0, 0, 0], | |
hsv: [0, 0, 0], | |
hwb: [0, 0, 0], | |
cmyk: [0, 0, 0, 0], | |
alpha: 1 | |
}; | |
// parse Color() argument | |
var vals; | |
if (typeof obj === 'string') { | |
vals = string.getRgba(obj); | |
if (vals) { | |
this.setValues('rgb', vals); | |
} else if (vals = string.getHsla(obj)) { | |
this.setValues('hsl', vals); | |
} else if (vals = string.getHwb(obj)) { | |
this.setValues('hwb', vals); | |
} else { | |
throw new Error('Unable to parse color from string "' + obj + '"'); | |
} | |
} else if (typeof obj === 'object') { | |
vals = obj; | |
if (vals.r !== undefined || vals.red !== undefined) { | |
this.setValues('rgb', vals); | |
} else if (vals.l !== undefined || vals.lightness !== undefined) { | |
this.setValues('hsl', vals); | |
} else if (vals.v !== undefined || vals.value !== undefined) { | |
this.setValues('hsv', vals); | |
} else if (vals.w !== undefined || vals.whiteness !== undefined) { | |
this.setValues('hwb', vals); | |
} else if (vals.c !== undefined || vals.cyan !== undefined) { | |
this.setValues('cmyk', vals); | |
} else { | |
throw new Error('Unable to parse color from object ' + JSON.stringify(obj)); | |
} | |
} | |
}; | |
Color.prototype = { | |
rgb: function () { | |
return this.setSpace('rgb', arguments); | |
}, | |
hsl: function () { | |
return this.setSpace('hsl', arguments); | |
}, | |
hsv: function () { | |
return this.setSpace('hsv', arguments); | |
}, | |
hwb: function () { | |
return this.setSpace('hwb', arguments); | |
}, | |
cmyk: function () { | |
return this.setSpace('cmyk', arguments); | |
}, | |
rgbArray: function () { | |
return this.values.rgb; | |
}, | |
hslArray: function () { | |
return this.values.hsl; | |
}, | |
hsvArray: function () { | |
return this.values.hsv; | |
}, | |
hwbArray: function () { | |
var values = this.values; | |
if (values.alpha !== 1) { | |
return values.hwb.concat([values.alpha]); | |
} | |
return values.hwb; | |
}, | |
cmykArray: function () { | |
return this.values.cmyk; | |
}, | |
rgbaArray: function () { | |
var values = this.values; | |
return values.rgb.concat([values.alpha]); | |
}, | |
hslaArray: function () { | |
var values = this.values; | |
return values.hsl.concat([values.alpha]); | |
}, | |
alpha: function (val) { | |
if (val === undefined) { | |
return this.values.alpha; | |
} | |
this.setValues('alpha', val); | |
return this; | |
}, | |
red: function (val) { | |
return this.setChannel('rgb', 0, val); | |
}, | |
green: function (val) { | |
return this.setChannel('rgb', 1, val); | |
}, | |
blue: function (val) { | |
return this.setChannel('rgb', 2, val); | |
}, | |
hue: function (val) { | |
if (val) { | |
val %= 360; | |
val = val < 0 ? 360 + val : val; | |
} | |
return this.setChannel('hsl', 0, val); | |
}, | |
saturation: function (val) { | |
return this.setChannel('hsl', 1, val); | |
}, | |
lightness: function (val) { | |
return this.setChannel('hsl', 2, val); | |
}, | |
saturationv: function (val) { | |
return this.setChannel('hsv', 1, val); | |
}, | |
whiteness: function (val) { | |
return this.setChannel('hwb', 1, val); | |
}, | |
blackness: function (val) { | |
return this.setChannel('hwb', 2, val); | |
}, | |
value: function (val) { | |
return this.setChannel('hsv', 2, val); | |
}, | |
cyan: function (val) { | |
return this.setChannel('cmyk', 0, val); | |
}, | |
magenta: function (val) { | |
return this.setChannel('cmyk', 1, val); | |
}, | |
yellow: function (val) { | |
return this.setChannel('cmyk', 2, val); | |
}, | |
black: function (val) { | |
return this.setChannel('cmyk', 3, val); | |
}, | |
hexString: function () { | |
return string.hexString(this.values.rgb); | |
}, | |
rgbString: function () { | |
return string.rgbString(this.values.rgb, this.values.alpha); | |
}, | |
rgbaString: function () { | |
return string.rgbaString(this.values.rgb, this.values.alpha); | |
}, | |
percentString: function () { | |
return string.percentString(this.values.rgb, this.values.alpha); | |
}, | |
hslString: function () { | |
return string.hslString(this.values.hsl, this.values.alpha); | |
}, | |
hslaString: function () { | |
return string.hslaString(this.values.hsl, this.values.alpha); | |
}, | |
hwbString: function () { | |
return string.hwbString(this.values.hwb, this.values.alpha); | |
}, | |
keyword: function () { | |
return string.keyword(this.values.rgb, this.values.alpha); | |
}, | |
rgbNumber: function () { | |
var rgb = this.values.rgb; | |
return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; | |
}, | |
luminosity: function () { | |
// http://www.w3.org/TR/WCAG20/#relativeluminancedef | |
var rgb = this.values.rgb; | |
var lum = []; | |
for (var i = 0; i < rgb.length; i++) { | |
var chan = rgb[i] / 255; | |
lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); | |
} | |
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; | |
}, | |
contrast: function (color2) { | |
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef | |
var lum1 = this.luminosity(); | |
var lum2 = color2.luminosity(); | |
if (lum1 > lum2) { | |
return (lum1 + 0.05) / (lum2 + 0.05); | |
} | |
return (lum2 + 0.05) / (lum1 + 0.05); | |
}, | |
level: function (color2) { | |
var contrastRatio = this.contrast(color2); | |
if (contrastRatio >= 7.1) { | |
return 'AAA'; | |
} | |
return (contrastRatio >= 4.5) ? 'AA' : ''; | |
}, | |
dark: function () { | |
// YIQ equation from http://24ways.org/2010/calculating-color-contrast | |
var rgb = this.values.rgb; | |
var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; | |
return yiq < 128; | |
}, | |
light: function () { | |
return !this.dark(); | |
}, | |
negate: function () { | |
var rgb = []; | |
for (var i = 0; i < 3; i++) { | |
rgb[i] = 255 - this.values.rgb[i]; | |
} | |
this.setValues('rgb', rgb); | |
return this; | |
}, | |
lighten: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[2] += hsl[2] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
darken: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[2] -= hsl[2] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
saturate: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[1] += hsl[1] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
desaturate: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[1] -= hsl[1] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
whiten: function (ratio) { | |
var hwb = this.values.hwb; | |
hwb[1] += hwb[1] * ratio; | |
this.setValues('hwb', hwb); | |
return this; | |
}, | |
blacken: function (ratio) { | |
var hwb = this.values.hwb; | |
hwb[2] += hwb[2] * ratio; | |
this.setValues('hwb', hwb); | |
return this; | |
}, | |
greyscale: function () { | |
var rgb = this.values.rgb; | |
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale | |
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; | |
this.setValues('rgb', [val, val, val]); | |
return this; | |
}, | |
clearer: function (ratio) { | |
var alpha = this.values.alpha; | |
this.setValues('alpha', alpha - (alpha * ratio)); | |
return this; | |
}, | |
opaquer: function (ratio) { | |
var alpha = this.values.alpha; | |
this.setValues('alpha', alpha + (alpha * ratio)); | |
return this; | |
}, | |
rotate: function (degrees) { | |
var hsl = this.values.hsl; | |
var hue = (hsl[0] + degrees) % 360; | |
hsl[0] = hue < 0 ? 360 + hue : hue; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
/** | |
* Ported from sass implementation in C | |
* https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 | |
*/ | |
mix: function (mixinColor, weight) { | |
var color1 = this; | |
var color2 = mixinColor; | |
var p = weight === undefined ? 0.5 : weight; | |
var w = 2 * p - 1; | |
var a = color1.alpha() - color2.alpha(); | |
var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; | |
var w2 = 1 - w1; | |
return this | |
.rgb( | |
w1 * color1.red() + w2 * color2.red(), | |
w1 * color1.green() + w2 * color2.green(), | |
w1 * color1.blue() + w2 * color2.blue() | |
) | |
.alpha(color1.alpha() * p + color2.alpha() * (1 - p)); | |
}, | |
toJSON: function () { | |
return this.rgb(); | |
}, | |
clone: function () { | |
// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, | |
// making the final build way to big to embed in Chart.js. So let's do it manually, | |
// assuming that values to clone are 1 dimension arrays containing only numbers, | |
// except 'alpha' which is a number. | |
var result = new Color(); | |
var source = this.values; | |
var target = result.values; | |
var value, type; | |
for (var prop in source) { | |
if (source.hasOwnProperty(prop)) { | |
value = source[prop]; | |
type = ({}).toString.call(value); | |
if (type === '[object Array]') { | |
target[prop] = value.slice(0); | |
} else if (type === '[object Number]') { | |
target[prop] = value; | |
} else { | |
console.error('unexpected color value:', value); | |
} | |
} | |
} | |
return result; | |
} | |
}; | |
Color.prototype.spaces = { | |
rgb: ['red', 'green', 'blue'], | |
hsl: ['hue', 'saturation', 'lightness'], | |
hsv: ['hue', 'saturation', 'value'], | |
hwb: ['hue', 'whiteness', 'blackness'], | |
cmyk: ['cyan', 'magenta', 'yellow', 'black'] | |
}; | |
Color.prototype.maxes = { | |
rgb: [255, 255, 255], | |
hsl: [360, 100, 100], | |
hsv: [360, 100, 100], | |
hwb: [360, 100, 100], | |
cmyk: [100, 100, 100, 100] | |
}; | |
Color.prototype.getValues = function (space) { | |
var values = this.values; | |
var vals = {}; | |
for (var i = 0; i < space.length; i++) { | |
vals[space.charAt(i)] = values[space][i]; | |
} | |
if (values.alpha !== 1) { | |
vals.a = values.alpha; | |
} | |
// {r: 255, g: 255, b: 255, a: 0.4} | |
return vals; | |
}; | |
Color.prototype.setValues = function (space, vals) { | |
var values = this.values; | |
var spaces = this.spaces; | |
var maxes = this.maxes; | |
var alpha = 1; | |
var i; | |
if (space === 'alpha') { | |
alpha = vals; | |
} else if (vals.length) { | |
// [10, 10, 10] | |
values[space] = vals.slice(0, space.length); | |
alpha = vals[space.length]; | |
} else if (vals[space.charAt(0)] !== undefined) { | |
// {r: 10, g: 10, b: 10} | |
for (i = 0; i < space.length; i++) { | |
values[space][i] = vals[space.charAt(i)]; | |
} | |
alpha = vals.a; | |
} else if (vals[spaces[space][0]] !== undefined) { | |
// {red: 10, green: 10, blue: 10} | |
var chans = spaces[space]; | |
for (i = 0; i < space.length; i++) { | |
values[space][i] = vals[chans[i]]; | |
} | |
alpha = vals.alpha; | |
} | |
values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); | |
if (space === 'alpha') { | |
return false; | |
} | |
var capped; | |
// cap values of the space prior converting all values | |
for (i = 0; i < space.length; i++) { | |
capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); | |
values[space][i] = Math.round(capped); | |
} | |
// convert to all the other color spaces | |
for (var sname in spaces) { | |
if (sname !== space) { | |
values[sname] = convert[space][sname](values[space]); | |
} | |
} | |
return true; | |
}; | |
Color.prototype.setSpace = function (space, args) { | |
var vals = args[0]; | |
if (vals === undefined) { | |
// color.rgb() | |
return this.getValues(space); | |
} | |
// color.rgb(10, 10, 10) | |
if (typeof vals === 'number') { | |
vals = Array.prototype.slice.call(args); | |
} | |
this.setValues(space, vals); | |
return this; | |
}; | |
Color.prototype.setChannel = function (space, index, val) { | |
var svalues = this.values[space]; | |
if (val === undefined) { | |
// color.red() | |
return svalues[index]; | |
} else if (val === svalues[index]) { | |
// color.red(color.red()) | |
return this; | |
} | |
// color.red(100) | |
svalues[index] = val; | |
this.setValues(space, svalues); | |
return this; | |
}; | |
if (typeof window !== 'undefined') { | |
window.Color = Color; | |
} | |
module.exports = Color; | |
},{"chartjs-color-string":2,"color-convert":5}],2:[function(require,module,exports){ | |
/* MIT license */ | |
var colorNames = require('color-name'); | |
module.exports = { | |
getRgba: getRgba, | |
getHsla: getHsla, | |
getRgb: getRgb, | |
getHsl: getHsl, | |
getHwb: getHwb, | |
getAlpha: getAlpha, | |
hexString: hexString, | |
rgbString: rgbString, | |
rgbaString: rgbaString, | |
percentString: percentString, | |
percentaString: percentaString, | |
hslString: hslString, | |
hslaString: hslaString, | |
hwbString: hwbString, | |
keyword: keyword | |
} | |
function getRgba(string) { | |
if (!string) { | |
return; | |
} | |
var abbr = /^#([a-fA-F0-9]{3})$/, | |
hex = /^#([a-fA-F0-9]{6})$/, | |
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, | |
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, | |
keyword = /(\w+)/; | |
var rgb = [0, 0, 0], | |
a = 1, | |
match = string.match(abbr); | |
if (match) { | |
match = match[1]; | |
for (var i = 0; i < rgb.length; i++) { | |
rgb[i] = parseInt(match[i] + match[i], 16); | |
} | |
} | |
else if (match = string.match(hex)) { | |
match = match[1]; | |
for (var i = 0; i < rgb.length; i++) { | |
rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); | |
} | |
} | |
else if (match = string.match(rgba)) { | |
for (var i = 0; i < rgb.length; i++) { | |
rgb[i] = parseInt(match[i + 1]); | |
} | |
a = parseFloat(match[4]); | |
} | |
else if (match = string.match(per)) { | |
for (var i = 0; i < rgb.length; i++) { | |
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); | |
} | |
a = parseFloat(match[4]); | |
} | |
else if (match = string.match(keyword)) { | |
if (match[1] == "transparent") { | |
return [0, 0, 0, 0]; | |
} | |
rgb = colorNames[match[1]]; | |
if (!rgb) { | |
return; | |
} | |
} | |
for (var i = 0; i < rgb.length; i++) { | |
rgb[i] = scale(rgb[i], 0, 255); | |
} | |
if (!a && a != 0) { | |
a = 1; | |
} | |
else { | |
a = scale(a, 0, 1); | |
} | |
rgb[3] = a; | |
return rgb; | |
} | |
function getHsla(string) { | |
if (!string) { | |
return; | |
} | |
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; | |
var match = string.match(hsl); | |
if (match) { | |
var alpha = parseFloat(match[4]); | |
var h = scale(parseInt(match[1]), 0, 360), | |
s = scale(parseFloat(match[2]), 0, 100), | |
l = scale(parseFloat(match[3]), 0, 100), | |
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); | |
return [h, s, l, a]; | |
} | |
} | |
function getHwb(string) { | |
if (!string) { | |
return; | |
} | |
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; | |
var match = string.match(hwb); | |
if (match) { | |
var alpha = parseFloat(match[4]); | |
var h = scale(parseInt(match[1]), 0, 360), | |
w = scale(parseFloat(match[2]), 0, 100), | |
b = scale(parseFloat(match[3]), 0, 100), | |
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); | |
return [h, w, b, a]; | |
} | |
} | |
function getRgb(string) { | |
var rgba = getRgba(string); | |
return rgba && rgba.slice(0, 3); | |
} | |
function getHsl(string) { | |
var hsla = getHsla(string); | |
return hsla && hsla.slice(0, 3); | |
} | |
function getAlpha(string) { | |
var vals = getRgba(string); | |
if (vals) { | |
return vals[3]; | |
} | |
else if (vals = getHsla(string)) { | |
return vals[3]; | |
} | |
else if (vals = getHwb(string)) { | |
return vals[3]; | |
} | |
} | |
// generators | |
function hexString(rgb) { | |
return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) | |
+ hexDouble(rgb[2]); | |
} | |
function rgbString(rgba, alpha) { | |
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { | |
return rgbaString(rgba, alpha); | |
} | |
return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; | |
} | |
function rgbaString(rgba, alpha) { | |
if (alpha === undefined) { | |
alpha = (rgba[3] !== undefined ? rgba[3] : 1); | |
} | |
return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] | |
+ ", " + alpha + ")"; | |
} | |
function percentString(rgba, alpha) { | |
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { | |
return percentaString(rgba, alpha); | |
} | |
var r = Math.round(rgba[0]/255 * 100), | |
g = Math.round(rgba[1]/255 * 100), | |
b = Math.round(rgba[2]/255 * 100); | |
return "rgb(" + r + "%, " + g + "%, " + b + "%)"; | |
} | |
function percentaString(rgba, alpha) { | |
var r = Math.round(rgba[0]/255 * 100), | |
g = Math.round(rgba[1]/255 * 100), | |
b = Math.round(rgba[2]/255 * 100); | |
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; | |
} | |
function hslString(hsla, alpha) { | |
if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { | |
return hslaString(hsla, alpha); | |
} | |
return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; | |
} | |
function hslaString(hsla, alpha) { | |
if (alpha === undefined) { | |
alpha = (hsla[3] !== undefined ? hsla[3] : 1); | |
} | |
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " | |
+ alpha + ")"; | |
} | |
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax | |
// (hwb have alpha optional & 1 is default value) | |
function hwbString(hwb, alpha) { | |
if (alpha === undefined) { | |
alpha = (hwb[3] !== undefined ? hwb[3] : 1); | |
} | |
return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" | |
+ (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; | |
} | |
function keyword(rgb) { | |
return reverseNames[rgb.slice(0, 3)]; | |
} | |
// helpers | |
function scale(num, min, max) { | |
return Math.min(Math.max(min, num), max); | |
} | |
function hexDouble(num) { | |
var str = num.toString(16).toUpperCase(); | |
return (str.length < 2) ? "0" + str : str; | |
} | |
//create a list of reverse color names | |
var reverseNames = {}; | |
for (var name in colorNames) { | |
reverseNames[colorNames[name]] = name; | |
} | |
},{"color-name":3}],3:[function(require,module,exports){ | |
module.exports = { | |
"aliceblue": [240, 248, 255], | |
"antiquewhite": [250, 235, 215], | |
"aqua": [0, 255, 255], | |
"aquamarine": [127, 255, 212], | |
"azure": [240, 255, 255], | |
"beige": [245, 245, 220], | |
"bisque": [255, 228, 196], | |
"black": [0, 0, 0], | |
"blanchedalmond": [255, 235, 205], | |
"blue": [0, 0, 255], | |
"blueviolet": [138, 43, 226], | |
"brown": [165, 42, 42], | |
"burlywood": [222, 184, 135], | |
"cadetblue": [95, 158, 160], | |
"chartreuse": [127, 255, 0], | |
"chocolate": [210, 105, 30], | |
"coral": [255, 127, 80], | |
"cornflowerblue": [100, 149, 237], | |
"cornsilk": [255, 248, 220], | |
"crimson": [220, 20, 60], | |
"cyan": [0, 255, 255], | |
"darkblue": [0, 0, 139], | |
"darkcyan": [0, 139, 139], | |
"darkgoldenrod": [184, 134, 11], | |
"darkgray": [169, 169, 169], | |
"darkgreen": [0, 100, 0], | |
"darkgrey": [169, 169, 169], | |
"darkkhaki": [189, 183, 107], | |
"darkmagenta": [139, 0, 139], | |
"darkolivegreen": [85, 107, 47], | |
"darkorange": [255, 140, 0], | |
"darkorchid": [153, 50, 204], | |
"darkred": [139, 0, 0], | |
"darksalmon": [233, 150, 122], | |
"darkseagreen": [143, 188, 143], | |
"darkslateblue": [72, 61, 139], | |
"darkslategray": [47, 79, 79], | |
"darkslategrey": [47, 79, 79], | |
"darkturquoise": [0, 206, 209], | |
"darkviolet": [148, 0, 211], | |
"deeppink": [255, 20, 147], | |
"deepskyblue": [0, 191, 255], | |
"dimgray": [105, 105, 105], | |
"dimgrey": [105, 105, 105], | |
"dodgerblue": [30, 144, 255], | |
"firebrick": [178, 34, 34], | |
"floralwhite": [255, 250, 240], | |
"forestgreen": [34, 139, 34], | |
"fuchsia": [255, 0, 255], | |
"gainsboro": [220, 220, 220], | |
"ghostwhite": [248, 248, 255], | |
"gold": [255, 215, 0], | |
"goldenrod": [218, 165, 32], | |
"gray": [128, 128, 128], | |
"green": [0, 128, 0], | |
"greenyellow": [173, 255, 47], | |
"grey": [128, 128, 128], | |
"honeydew": [240, 255, 240], | |
"hotpink": [255, 105, 180], | |
"indianred": [205, 92, 92], | |
"indigo": [75, 0, 130], | |
"ivory": [255, 255, 240], | |
"khaki": [240, 230, 140], | |
"lavender": [230, 230, 250], | |
"lavenderblush": [255, 240, 245], | |
"lawngreen": [124, 252, 0], | |
"lemonchiffon": [255, 250, 205], | |
"lightblue": [173, 216, 230], | |
"lightcoral": [240, 128, 128], | |
"lightcyan": [224, 255, 255], | |
"lightgoldenrodyellow": [250, 250, 210], | |
"lightgray": [211, 211, 211], | |
"lightgreen": [144, 238, 144], | |
"lightgrey": [211, 211, 211], | |
"lightpink": [255, 182, 193], | |
"lightsalmon": [255, 160, 122], | |
"lightseagreen": [32, 178, 170], | |
"lightskyblue": [135, 206, 250], | |
"lightslategray": [119, 136, 153], | |
"lightslategrey": [119, 136, 153], | |
"lightsteelblue": [176, 196, 222], | |
"lightyellow": [255, 255, 224], | |
"lime": [0, 255, 0], | |
"limegreen": [50, 205, 50], | |
"linen": [250, 240, 230], | |
"magenta": [255, 0, 255], | |
"maroon": [128, 0, 0], | |
"mediumaquamarine": [102, 205, 170], | |
"mediumblue": [0, 0, 205], | |
"mediumorchid": [186, 85, 211], | |
"mediumpurple": [147, 112, 219], | |
"mediumseagreen": [60, 179, 113], | |
"mediumslateblue": [123, 104, 238], | |
"mediumspringgreen": [0, 250, 154], | |
"mediumturquoise": [72, 209, 204], | |
"mediumvioletred": [199, 21, 133], | |
"midnightblue": [25, 25, 112], | |
"mintcream": [245, 255, 250], | |
"mistyrose": [255, 228, 225], | |
"moccasin": [255, 228, 181], | |
"navajowhite": [255, 222, 173], | |
"navy": [0, 0, 128], | |
"oldlace": [253, 245, 230], | |
"olive": [128, 128, 0], | |
"olivedrab": [107, 142, 35], | |
"orange": [255, 165, 0], | |
"orangered": [255, 69, 0], | |
"orchid": [218, 112, 214], | |
"palegoldenrod": [238, 232, 170], | |
"palegreen": [152, 251, 152], | |
"paleturquoise": [175, 238, 238], | |
"palevioletred": [219, 112, 147], | |
"papayawhip": [255, 239, 213], | |
"peachpuff": [255, 218, 185], | |
"peru": [205, 133, 63], | |
"pink": [255, 192, 203], | |
"plum": [221, 160, 221], | |
"powderblue": [176, 224, 230], | |
"purple": [128, 0, 128], | |
"rebeccapurple": [102, 51, 153], | |
"red": [255, 0, 0], | |
"rosybrown": [188, 143, 143], | |
"royalblue": [65, 105, 225], | |
"saddlebrown": [139, 69, 19], | |
"salmon": [250, 128, 114], | |
"sandybrown": [244, 164, 96], | |
"seagreen": [46, 139, 87], | |
"seashell": [255, 245, 238], | |
"sienna": [160, 82, 45], | |
"silver": [192, 192, 192], | |
"skyblue": [135, 206, 235], | |
"slateblue": [106, 90, 205], | |
"slategray": [112, 128, 144], | |
"slategrey": [112, 128, 144], | |
"snow": [255, 250, 250], | |
"springgreen": [0, 255, 127], | |
"steelblue": [70, 130, 180], | |
"tan": [210, 180, 140], | |
"teal": [0, 128, 128], | |
"thistle": [216, 191, 216], | |
"tomato": [255, 99, 71], | |
"turquoise": [64, 224, 208], | |
"violet": [238, 130, 238], | |
"wheat": [245, 222, 179], | |
"white": [255, 255, 255], | |
"whitesmoke": [245, 245, 245], | |
"yellow": [255, 255, 0], | |
"yellowgreen": [154, 205, 50] | |
}; | |
},{}],4:[function(require,module,exports){ | |
/* MIT license */ | |
module.exports = { | |
rgb2hsl: rgb2hsl, | |
rgb2hsv: rgb2hsv, | |
rgb2hwb: rgb2hwb, | |
rgb2cmyk: rgb2cmyk, | |
rgb2keyword: rgb2keyword, | |
rgb2xyz: rgb2xyz, | |
rgb2lab: rgb2lab, | |
rgb2lch: rgb2lch, | |
hsl2rgb: hsl2rgb, | |
hsl2hsv: hsl2hsv, | |
hsl2hwb: hsl2hwb, | |
hsl2cmyk: hsl2cmyk, | |
hsl2keyword: hsl2keyword, | |
hsv2rgb: hsv2rgb, | |
hsv2hsl: hsv2hsl, | |
hsv2hwb: hsv2hwb, | |
hsv2cmyk: hsv2cmyk, | |
hsv2keyword: hsv2keyword, | |
hwb2rgb: hwb2rgb, | |
hwb2hsl: hwb2hsl, | |
hwb2hsv: hwb2hsv, | |
hwb2cmyk: hwb2cmyk, | |
hwb2keyword: hwb2keyword, | |
cmyk2rgb: cmyk2rgb, | |
cmyk2hsl: cmyk2hsl, | |
cmyk2hsv: cmyk2hsv, | |
cmyk2hwb: cmyk2hwb, | |
cmyk2keyword: cmyk2keyword, | |
keyword2rgb: keyword2rgb, | |
keyword2hsl: keyword2hsl, | |
keyword2hsv: keyword2hsv, | |
keyword2hwb: keyword2hwb, | |
keyword2cmyk: keyword2cmyk, | |
keyword2lab: keyword2lab, | |
keyword2xyz: keyword2xyz, | |
xyz2rgb: xyz2rgb, | |
xyz2lab: xyz2lab, | |
xyz2lch: xyz2lch, | |
lab2xyz: lab2xyz, | |
lab2rgb: lab2rgb, | |
lab2lch: lab2lch, | |
lch2lab: lch2lab, | |
lch2xyz: lch2xyz, | |
lch2rgb: lch2rgb | |
} | |
function rgb2hsl(rgb) { | |
var r = rgb[0]/255, | |
g = rgb[1]/255, | |
b = rgb[2]/255, | |
min = Math.min(r, g, b), | |
max = Math.max(r, g, b), | |
delta = max - min, | |
h, s, l; | |
if (max == min) | |
h = 0; | |
else if (r == max) | |
h = (g - b) / delta; | |
else if (g == max) | |
h = 2 + (b - r) / delta; | |
else if (b == max) | |
h = 4 + (r - g)/ delta; | |
h = Math.min(h * 60, 360); | |
if (h < 0) | |
h += 360; | |
l = (min + max) / 2; | |
if (max == min) | |
s = 0; | |
else if (l <= 0.5) | |
s = delta / (max + min); | |
else | |
s = delta / (2 - max - min); | |
return [h, s * 100, l * 100]; | |
} | |
function rgb2hsv(rgb) { | |
var r = rgb[0], | |
g = rgb[1], | |
b = rgb[2], | |
min = Math.min(r, g, b), | |
max = Math.max(r, g, b), | |
delta = max - min, | |
h, s, v; | |
if (max == 0) | |
s = 0; | |
else | |
s = (delta/max * 1000)/10; | |
if (max == min) | |
h = 0; | |
else if (r == max) | |
h = (g - b) / delta; | |
else if (g == max) | |
h = 2 + (b - r) / delta; | |
else if (b == max) | |
h = 4 + (r - g) / delta; | |
h = Math.min(h * 60, 360); | |
if (h < 0) | |
h += 360; | |
v = ((max / 255) * 1000) / 10; | |
return [h, s, v]; | |
} | |
function rgb2hwb(rgb) { | |
var r = rgb[0], | |
g = rgb[1], | |
b = rgb[2], | |
h = rgb2hsl(rgb)[0], | |
w = 1/255 * Math.min(r, Math.min(g, b)), | |
b = 1 - 1/255 * Math.max(r, Math.max(g, b)); | |
return [h, w * 100, b * 100]; | |
} | |
function rgb2cmyk(rgb) { | |
var r = rgb[0] / 255, | |
g = rgb[1] / 255, | |
b = rgb[2] / 255, | |
c, m, y, k; | |
k = Math.min(1 - r, 1 - g, 1 - b); | |
c = (1 - r - k) / (1 - k) || 0; | |
m = (1 - g - k) / (1 - k) || 0; | |
y = (1 - b - k) / (1 - k) || 0; | |
return [c * 100, m * 100, y * 100, k * 100]; | |
} | |
function rgb2keyword(rgb) { | |
return reverseKeywords[JSON.stringify(rgb)]; | |
} | |
function rgb2xyz(rgb) { | |
var r = rgb[0] / 255, | |
g = rgb[1] / 255, | |
b = rgb[2] / 255; | |
// assume sRGB | |
r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); | |
g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); | |
b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); | |
var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); | |
var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); | |
var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); | |
return [x * 100, y *100, z * 100]; | |
} | |
function rgb2lab(rgb) { | |
var xyz = rgb2xyz(rgb), | |
x = xyz[0], | |
y = xyz[1], | |
z = xyz[2], | |
l, a, b; | |
x /= 95.047; | |
y /= 100; | |
z /= 108.883; | |
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); | |
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); | |
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); | |
l = (116 * y) - 16; | |
a = 500 * (x - y); | |
b = 200 * (y - z); | |
return [l, a, b]; | |
} | |
function rgb2lch(args) { | |
return lab2lch(rgb2lab(args)); | |
} | |
function hsl2rgb(hsl) { | |
var h = hsl[0] / 360, | |
s = hsl[1] / 100, | |
l = hsl[2] / 100, | |
t1, t2, t3, rgb, val; | |
if (s == 0) { | |
val = l * 255; | |
return [val, val, val]; | |
} | |
if (l < 0.5) | |
t2 = l * (1 + s); | |
else | |
t2 = l + s - l * s; | |
t1 = 2 * l - t2; | |
rgb = [0, 0, 0]; | |
for (var i = 0; i < 3; i++) { | |
t3 = h + 1 / 3 * - (i - 1); | |
t3 < 0 && t3++; | |
t3 > 1 && t3--; | |
if (6 * t3 < 1) | |
val = t1 + (t2 - t1) * 6 * t3; | |
else if (2 * t3 < 1) | |
val = t2; | |
else if (3 * t3 < 2) | |
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; | |
else | |
val = t1; | |
rgb[i] = val * 255; | |
} | |
return rgb; | |
} | |
function hsl2hsv(hsl) { | |
var h = hsl[0], | |
s = hsl[1] / 100, | |
l = hsl[2] / 100, | |
sv, v; | |
if(l === 0) { | |
// no need to do calc on black | |
// also avoids divide by 0 error | |
return [0, 0, 0]; | |
} | |
l *= 2; | |
s *= (l <= 1) ? l : 2 - l; | |
v = (l + s) / 2; | |
sv = (2 * s) / (l + s); | |
return [h, sv * 100, v * 100]; | |
} | |
function hsl2hwb(args) { | |
return rgb2hwb(hsl2rgb(args)); | |
} | |
function hsl2cmyk(args) { | |
return rgb2cmyk(hsl2rgb(args)); | |
} | |
function hsl2keyword(args) { | |
return rgb2keyword(hsl2rgb(args)); | |
} | |
function hsv2rgb(hsv) { | |
var h = hsv[0] / 60, | |
s = hsv[1] / 100, | |
v = hsv[2] / 100, | |
hi = Math.floor(h) % 6; | |
var f = h - Math.floor(h), | |
p = 255 * v * (1 - s), | |
q = 255 * v * (1 - (s * f)), | |
t = 255 * v * (1 - (s * (1 - f))), | |
v = 255 * v; | |
switch(hi) { | |
case 0: | |
return [v, t, p]; | |
case 1: | |
return [q, v, p]; | |
case 2: | |
return [p, v, t]; | |
case 3: | |
return [p, q, v]; | |
case 4: | |
return [t, p, v]; | |
case 5: | |
return [v, p, q]; | |
} | |
} | |
function hsv2hsl(hsv) { | |
var h = hsv[0], | |
s = hsv[1] / 100, | |
v = hsv[2] / 100, | |
sl, l; | |
l = (2 - s) * v; | |
sl = s * v; | |
sl /= (l <= 1) ? l : 2 - l; | |
sl = sl || 0; | |
l /= 2; | |
return [h, sl * 100, l * 100]; | |
} | |
function hsv2hwb(args) { | |
return rgb2hwb(hsv2rgb(args)) | |
} | |
function hsv2cmyk(args) { | |
return rgb2cmyk(hsv2rgb(args)); | |
} | |
function hsv2keyword(args) { | |
return rgb2keyword(hsv2rgb(args)); | |
} | |
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb | |
function hwb2rgb(hwb) { | |
var h = hwb[0] / 360, | |
wh = hwb[1] / 100, | |
bl = hwb[2] / 100, | |
ratio = wh + bl, | |
i, v, f, n; | |
// wh + bl cant be > 1 | |
if (ratio > 1) { | |
wh /= ratio; | |
bl /= ratio; | |
} | |
i = Math.floor(6 * h); | |
v = 1 - bl; | |
f = 6 * h - i; | |
if ((i & 0x01) != 0) { | |
f = 1 - f; | |
} | |
n = wh + f * (v - wh); // linear interpolation | |
switch (i) { | |
default: | |
case 6: | |
case 0: r = v; g = n; b = wh; break; | |
case 1: r = n; g = v; b = wh; break; | |
case 2: r = wh; g = v; b = n; break; | |
case 3: r = wh; g = n; b = v; break; | |
case 4: r = n; g = wh; b = v; break; | |
case 5: r = v; g = wh; b = n; break; | |
} | |
return [r * 255, g * 255, b * 255]; | |
} | |
function hwb2hsl(args) { | |
return rgb2hsl(hwb2rgb(args)); | |
} | |
function hwb2hsv(args) { | |
return rgb2hsv(hwb2rgb(args)); | |
} | |
function hwb2cmyk(args) { | |
return rgb2cmyk(hwb2rgb(args)); | |
} | |
function hwb2keyword(args) { | |
return rgb2keyword(hwb2rgb(args)); | |
} | |
function cmyk2rgb(cmyk) { | |
var c = cmyk[0] / 100, | |
m = cmyk[1] / 100, | |
y = cmyk[2] / 100, | |
k = cmyk[3] / 100, | |
r, g, b; | |
r = 1 - Math.min(1, c * (1 - k) + k); | |
g = 1 - Math.min(1, m * (1 - k) + k); | |
b = 1 - Math.min(1, y * (1 - k) + k); | |
return [r * 255, g * 255, b * 255]; | |
} | |
function cmyk2hsl(args) { | |
return rgb2hsl(cmyk2rgb(args)); | |
} | |
function cmyk2hsv(args) { | |
return rgb2hsv(cmyk2rgb(args)); | |
} | |
function cmyk2hwb(args) { | |
return rgb2hwb(cmyk2rgb(args)); | |
} | |
function cmyk2keyword(args) { | |
return rgb2keyword(cmyk2rgb(args)); | |
} | |
function xyz2rgb(xyz) { | |
var x = xyz[0] / 100, | |
y = xyz[1] / 100, | |
z = xyz[2] / 100, | |
r, g, b; | |
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); | |
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); | |
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); | |
// assume sRGB | |
r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) | |
: r = (r * 12.92); | |
g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) | |
: g = (g * 12.92); | |
b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) | |
: b = (b * 12.92); | |
r = Math.min(Math.max(0, r), 1); | |
g = Math.min(Math.max(0, g), 1); | |
b = Math.min(Math.max(0, b), 1); | |
return [r * 255, g * 255, b * 255]; | |
} | |
function xyz2lab(xyz) { | |
var x = xyz[0], | |
y = xyz[1], | |
z = xyz[2], | |
l, a, b; | |
x /= 95.047; | |
y /= 100; | |
z /= 108.883; | |
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); | |
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); | |
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); | |
l = (116 * y) - 16; | |
a = 500 * (x - y); | |
b = 200 * (y - z); | |
return [l, a, b]; | |
} | |
function xyz2lch(args) { | |
return lab2lch(xyz2lab(args)); | |
} | |
function lab2xyz(lab) { | |
var l = lab[0], | |
a = lab[1], | |
b = lab[2], | |
x, y, z, y2; | |
if (l <= 8) { | |
y = (l * 100) / 903.3; | |
y2 = (7.787 * (y / 100)) + (16 / 116); | |
} else { | |
y = 100 * Math.pow((l + 16) / 116, 3); | |
y2 = Math.pow(y / 100, 1/3); | |
} | |
x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); | |
z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); | |
return [x, y, z]; | |
} | |
function lab2lch(lab) { | |
var l = lab[0], | |
a = lab[1], | |
b = lab[2], | |
hr, h, c; | |
hr = Math.atan2(b, a); | |
h = hr * 360 / 2 / Math.PI; | |
if (h < 0) { | |
h += 360; | |
} | |
c = Math.sqrt(a * a + b * b); | |
return [l, c, h]; | |
} | |
function lab2rgb(args) { | |
return xyz2rgb(lab2xyz(args)); | |
} | |
function lch2lab(lch) { | |
var l = lch[0], | |
c = lch[1], | |
h = lch[2], | |
a, b, hr; | |
hr = h / 360 * 2 * Math.PI; | |
a = c * Math.cos(hr); | |
b = c * Math.sin(hr); | |
return [l, a, b]; | |
} | |
function lch2xyz(args) { | |
return lab2xyz(lch2lab(args)); | |
} | |
function lch2rgb(args) { | |
return lab2rgb(lch2lab(args)); | |
} | |
function keyword2rgb(keyword) { | |
return cssKeywords[keyword]; | |
} | |
function keyword2hsl(args) { | |
return rgb2hsl(keyword2rgb(args)); | |
} | |
function keyword2hsv(args) { | |
return rgb2hsv(keyword2rgb(args)); | |
} | |
function keyword2hwb(args) { | |
return rgb2hwb(keyword2rgb(args)); | |
} | |
function keyword2cmyk(args) { | |
return rgb2cmyk(keyword2rgb(args)); | |
} | |
function keyword2lab(args) { | |
return rgb2lab(keyword2rgb(args)); | |
} | |
function keyword2xyz(args) { | |
return rgb2xyz(keyword2rgb(args)); | |
} | |
var cssKeywords = { | |
aliceblue: [240,248,255], | |
antiquewhite: [250,235,215], | |
aqua: [0,255,255], | |
aquamarine: [127,255,212], | |
azure: [240,255,255], | |
beige: [245,245,220], | |
bisque: [255,228,196], | |
black: [0,0,0], | |
blanchedalmond: [255,235,205], | |
blue: [0,0,255], | |
blueviolet: [138,43,226], | |
brown: [165,42,42], | |
burlywood: [222,184,135], | |
cadetblue: [95,158,160], | |
chartreuse: [127,255,0], | |
chocolate: [210,105,30], | |
coral: [255,127,80], | |
cornflowerblue: [100,149,237], | |
cornsilk: [255,248,220], | |
crimson: [220,20,60], | |
cyan: [0,255,255], | |
darkblue: [0,0,139], | |
darkcyan: [0,139,139], | |
darkgoldenrod: [184,134,11], | |
darkgray: [169,169,169], | |
darkgreen: [0,100,0], | |
darkgrey: [169,169,169], | |
darkkhaki: [189,183,107], | |
darkmagenta: [139,0,139], | |
darkolivegreen: [85,107,47], | |
darkorange: [255,140,0], | |
darkorchid: [153,50,204], | |
darkred: [139,0,0], | |
darksalmon: [233,150,122], | |
darkseagreen: [143,188,143], | |
darkslateblue: [72,61,139], | |
darkslategray: [47,79,79], | |
darkslategrey: [47,79,79], | |
darkturquoise: [0,206,209], | |
darkviolet: [148,0,211], | |
deeppink: [255,20,147], | |
deepskyblue: [0,191,255], | |
dimgray: [105,105,105], | |
dimgrey: [105,105,105], | |
dodgerblue: [30,144,255], | |
firebrick: [178,34,34], | |
floralwhite: [255,250,240], | |
forestgreen: [34,139,34], | |
fuchsia: [255,0,255], | |
gainsboro: [220,220,220], | |
ghostwhite: [248,248,255], | |
gold: [255,215,0], | |
goldenrod: [218,165,32], | |
gray: [128,128,128], | |
green: [0,128,0], | |
greenyellow: [173,255,47], | |
grey: [128,128,128], | |
honeydew: [240,255,240], | |
hotpink: [255,105,180], | |
indianred: [205,92,92], | |
indigo: [75,0,130], | |
ivory: [255,255,240], | |
khaki: [240,230,140], | |
lavender: [230,230,250], | |
lavenderblush: [255,240,245], | |
lawngreen: [124,252,0], | |
lemonchiffon: [255,250,205], | |
lightblue: [173,216,230], | |
lightcoral: [240,128,128], | |
lightcyan: [224,255,255], | |
lightgoldenrodyellow: [250,250,210], | |
lightgray: [211,211,211], | |
lightgreen: [144,238,144], | |
lightgrey: [211,211,211], | |
lightpink: [255,182,193], | |
lightsalmon: [255,160,122], | |
lightseagreen: [32,178,170], | |
lightskyblue: [135,206,250], | |
lightslategray: [119,136,153], | |
lightslategrey: [119,136,153], | |
lightsteelblue: [176,196,222], | |
lightyellow: [255,255,224], | |
lime: [0,255,0], | |
limegreen: [50,205,50], | |
linen: [250,240,230], | |
magenta: [255,0,255], | |
maroon: [128,0,0], | |
mediumaquamarine: [102,205,170], | |
mediumblue: [0,0,205], | |
mediumorchid: [186,85,211], | |
mediumpurple: [147,112,219], | |
mediumseagreen: [60,179,113], | |
mediumslateblue: [123,104,238], | |
mediumspringgreen: [0,250,154], | |
mediumturquoise: [72,209,204], | |
mediumvioletred: [199,21,133], | |
midnightblue: [25,25,112], | |
mintcream: [245,255,250], | |
mistyrose: [255,228,225], | |
moccasin: [255,228,181], | |
navajowhite: [255,222,173], | |
navy: [0,0,128], | |
oldlace: [253,245,230], | |
olive: [128,128,0], | |
olivedrab: [107,142,35], | |
orange: [255,165,0], | |
orangered: [255,69,0], | |
orchid: [218,112,214], | |
palegoldenrod: [238,232,170], | |
palegreen: [152,251,152], | |
paleturquoise: [175,238,238], | |
palevioletred: [219,112,147], | |
papayawhip: [255,239,213], | |
peachpuff: [255,218,185], | |
peru: [205,133,63], | |
pink: [255,192,203], | |
plum: [221,160,221], | |
powderblue: [176,224,230], | |
purple: [128,0,128], | |
rebeccapurple: [102, 51, 153], | |
red: [255,0,0], | |
rosybrown: [188,143,143], | |
royalblue: [65,105,225], | |
saddlebrown: [139,69,19], | |
salmon: [250,128,114], | |
sandybrown: [244,164,96], | |
seagreen: [46,139,87], | |
seashell: [255,245,238], | |
sienna: [160,82,45], | |
silver: [192,192,192], | |
skyblue: [135,206,235], | |
slateblue: [106,90,205], | |
slategray: [112,128,144], | |
slategrey: [112,128,144], | |
snow: [255,250,250], | |
springgreen: [0,255,127], | |
steelblue: [70,130,180], | |
tan: [210,180,140], | |
teal: [0,128,128], | |
thistle: [216,191,216], | |
tomato: [255,99,71], | |
turquoise: [64,224,208], | |
violet: [238,130,238], | |
wheat: [245,222,179], | |
white: [255,255,255], | |
whitesmoke: [245,245,245], | |
yellow: [255,255,0], | |
yellowgreen: [154,205,50] | |
}; | |
var reverseKeywords = {}; | |
for (var key in cssKeywords) { | |
reverseKeywords[JSON.stringify(cssKeywords[key])] = key; | |
} | |
},{}],5:[function(require,module,exports){ | |
var conversions = require("./conversions"); | |
var convert = function() { | |
return new Converter(); | |
} | |
for (var func in conversions) { | |
// export Raw versions | |
convert[func + "Raw"] = (function(func) { | |
// accept array or plain args | |
return function(arg) { | |
if (typeof arg == "number") | |
arg = Array.prototype.slice.call(arguments); | |
return conversions[func](arg); | |
} | |
})(func); | |
var pair = /(\w+)2(\w+)/.exec(func), | |
from = pair[1], | |
to = pair[2]; | |
// export rgb2hsl and ["rgb"]["hsl"] | |
convert[from] = convert[from] || {}; | |
convert[from][to] = convert[func] = (function(func) { | |
return function(arg) { | |
if (typeof arg == "number") | |
arg = Array.prototype.slice.call(arguments); | |
var val = conversions[func](arg); | |
if (typeof val == "string" || val === undefined) | |
return val; // keyword | |
for (var i = 0; i < val.length; i++) | |
val[i] = Math.round(val[i]); | |
return val; | |
} | |
})(func); | |
} | |
/* Converter does lazy conversion and caching */ | |
var Converter = function() { | |
this.convs = {}; | |
}; | |
/* Either get the values for a space or | |
set the values for a space, depending on args */ | |
Converter.prototype.routeSpace = function(space, args) { | |
var values = args[0]; | |
if (values === undefined) { | |
// color.rgb() | |
return this.getValues(space); | |
} | |
// color.rgb(10, 10, 10) | |
if (typeof values == "number") { | |
values = Array.prototype.slice.call(args); | |
} | |
return this.setValues(space, values); | |
}; | |
/* Set the values for a space, invalidating cache */ | |
Converter.prototype.setValues = function(space, values) { | |
this.space = space; | |
this.convs = {}; | |
this.convs[space] = values; | |
return this; | |
}; | |
/* Get the values for a space. If there's already | |
a conversion for the space, fetch it, otherwise | |
compute it */ | |
Converter.prototype.getValues = function(space) { | |
var vals = this.convs[space]; | |
if (!vals) { | |
var fspace = this.space, | |
from = this.convs[fspace]; | |
vals = convert[fspace][space](from); | |
this.convs[space] = vals; | |
} | |
return vals; | |
}; | |
["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { | |
Converter.prototype[space] = function(vals) { | |
return this.routeSpace(space, arguments); | |
} | |
}); | |
module.exports = convert; | |
},{"./conversions":4}],6:[function(require,module,exports){ | |
//! moment.js | |
//! version : 2.14.1 | |
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors | |
//! license : MIT | |
//! momentjs.com | |
;(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | |
typeof define === 'function' && define.amd ? define(factory) : | |
global.moment = factory() | |
}(this, function () { 'use strict'; | |
var hookCallback; | |
function utils_hooks__hooks () { | |
return hookCallback.apply(null, arguments); | |
} | |
// This is done to register the method called with moment() | |
// without creating circular dependencies. | |
function setHookCallback (callback) { | |
hookCallback = callback; | |
} | |
function isArray(input) { | |
return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; | |
} | |
function isObject(input) { | |
return Object.prototype.toString.call(input) === '[object Object]'; | |
} | |
function isObjectEmpty(obj) { | |
var k; | |
for (k in obj) { | |
// even if its not own property I'd still call it non-empty | |
return false; | |
} | |
return true; | |
} | |
function isDate(input) { | |
return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; | |
} | |
function map(arr, fn) { | |
var res = [], i; | |
for (i = 0; i < arr.length; ++i) { | |
res.push(fn(arr[i], i)); | |
} | |
return res; | |
} | |
function hasOwnProp(a, b) { | |
return Object.prototype.hasOwnProperty.call(a, b); | |
} | |
function extend(a, b) { | |
for (var i in b) { | |
if (hasOwnProp(b, i)) { | |
a[i] = b[i]; | |
} | |
} | |
if (hasOwnProp(b, 'toString')) { | |
a.toString = b.toString; | |
} | |
if (hasOwnProp(b, 'valueOf')) { | |
a.valueOf = b.valueOf; | |
} | |
return a; | |
} | |
function create_utc__createUTC (input, format, locale, strict) { | |
return createLocalOrUTC(input, format, locale, strict, true).utc(); | |
} | |
function defaultParsingFlags() { | |
// We need to deep clone this object. | |
return { | |
empty : false, | |
unusedTokens : [], | |
unusedInput : [], | |
overflow : -2, | |
charsLeftOver : 0, | |
nullInput : false, | |
invalidMonth : null, | |
invalidFormat : false, | |
userInvalidated : false, | |
iso : false, | |
parsedDateParts : [], | |
meridiem : null | |
}; | |
} | |
function getParsingFlags(m) { | |
if (m._pf == null) { | |
m._pf = defaultParsingFlags(); | |
} | |
return m._pf; | |
} | |
var some; | |
if (Array.prototype.some) { | |
some = Array.prototype.some; | |
} else { | |
some = function (fun) { | |
var t = Object(this); | |
var len = t.length >>> 0; | |
for (var i = 0; i < len; i++) { | |
if (i in t && fun.call(this, t[i], i, t)) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
} | |
function valid__isValid(m) { | |
if (m._isValid == null) { | |
var flags = getParsingFlags(m); | |
var parsedParts = some.call(flags.parsedDateParts, function (i) { | |
return i != null; | |
}); | |
m._isValid = !isNaN(m._d.getTime()) && | |
flags.overflow < 0 && | |
!flags.empty && | |
!flags.invalidMonth && | |
!flags.invalidWeekday && | |
!flags.nullInput && | |
!flags.invalidFormat && | |
!flags.userInvalidated && | |
(!flags.meridiem || (flags.meridiem && parsedParts)); | |
if (m._strict) { | |
m._isValid = m._isValid && | |
flags.charsLeftOver === 0 && | |
flags.unusedTokens.length === 0 && | |
flags.bigHour === undefined; | |
} | |
} | |
return m._isValid; | |
} | |
function valid__createInvalid (flags) { | |
var m = create_utc__createUTC(NaN); | |
if (flags != null) { | |
extend(getParsingFlags(m), flags); | |
} | |
else { | |
getParsingFlags(m).userInvalidated = true; | |
} | |
return m; | |
} | |
function isUndefined(input) { | |
return input === void 0; | |
} | |
// Plugins that add properties should also add the key here (null value), | |
// so we can properly clone ourselves. | |
var momentProperties = utils_hooks__hooks.momentProperties = []; | |
function copyConfig(to, from) { | |
var i, prop, val; | |
if (!isUndefined(from._isAMomentObject)) { | |
to._isAMomentObject = from._isAMomentObject; | |
} | |
if (!isUndefined(from._i)) { | |
to._i = from._i; | |
} | |
if (!isUndefined(from._f)) { | |
to._f = from._f; | |
} | |
if (!isUndefined(from._l)) { | |
to._l = from._l; | |
} | |
if (!isUndefined(from._strict)) { | |
to._strict = from._strict; | |
} | |
if (!isUndefined(from._tzm)) { | |
to._tzm = from._tzm; | |
} | |
if (!isUndefined(from._isUTC)) { | |
to._isUTC = from._isUTC; | |
} | |
if (!isUndefined(from._offset)) { | |
to._offset = from._offset; | |
} | |
if (!isUndefined(from._pf)) { | |
to._pf = getParsingFlags(from); | |
} | |
if (!isUndefined(from._locale)) { | |
to._locale = from._locale; | |
} | |
if (momentProperties.length > 0) { | |
for (i in momentProperties) { | |
prop = momentProperties[i]; | |
val = from[prop]; | |
if (!isUndefined(val)) { | |
to[prop] = val; | |
} | |
} | |
} | |
return to; | |
} | |
var updateInProgress = false; | |
// Moment prototype object | |
function Moment(config) { | |
copyConfig(this, config); | |
this._d = new Date(config._d != null ? config._d.getTime() : NaN); | |
// Prevent infinite loop in case updateOffset creates new moment | |
// objects. | |
if (updateInProgress === false) { | |
updateInProgress = true; | |
utils_hooks__hooks.updateOffset(this); | |
updateInProgress = false; | |
} | |
} | |
function isMoment (obj) { | |
return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); | |
} | |
function absFloor (number) { | |
if (number < 0) { | |
// -0 -> 0 | |
return Math.ceil(number) || 0; | |
} else { | |
return Math.floor(number); | |
} | |
} | |
function toInt(argumentForCoercion) { | |
var coercedNumber = +argumentForCoercion, | |
value = 0; | |
if (coercedNumber !== 0 && isFinite(coercedNumber)) { | |
value = absFloor(coercedNumber); | |
} | |
return value; | |
} | |
// compare two arrays, return the number of differences | |
function compareArrays(array1, array2, dontConvert) { | |
var len = Math.min(array1.length, array2.length), | |
lengthDiff = Math.abs(array1.length - array2.length), | |
diffs = 0, | |
i; | |
for (i = 0; i < len; i++) { | |
if ((dontConvert && array1[i] !== array2[i]) || | |
(!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { | |
diffs++; | |
} | |
} | |
return diffs + lengthDiff; | |
} | |
function warn(msg) { | |
if (utils_hooks__hooks.suppressDeprecationWarnings === false && | |
(typeof console !== 'undefined') && console.warn) { | |
console.warn('Deprecation warning: ' + msg); | |
} | |
} | |
function deprecate(msg, fn) { | |
var firstTime = true; | |
return extend(function () { | |
if (utils_hooks__hooks.deprecationHandler != null) { | |
utils_hooks__hooks.deprecationHandler(null, msg); | |
} | |
if (firstTime) { | |
warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack); | |
firstTime = false; | |
} | |
return fn.apply(this, arguments); | |
}, fn); | |
} | |
var deprecations = {}; | |
function deprecateSimple(name, msg) { | |
if (utils_hooks__hooks.deprecationHandler != null) { | |
utils_hooks__hooks.deprecationHandler(name, msg); | |
} | |
if (!deprecations[name]) { | |
warn(msg); | |
deprecations[name] = true; | |
} | |
} | |
utils_hooks__hooks.suppressDeprecationWarnings = false; | |
utils_hooks__hooks.deprecationHandler = null; | |
function isFunction(input) { | |
return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; | |
} | |
function locale_set__set (config) { | |
var prop, i; | |
for (i in config) { | |
prop = config[i]; | |
if (isFunction(prop)) { | |
this[i] = prop; | |
} else { | |
this['_' + i] = prop; | |
} | |
} | |
this._config = config; | |
// Lenient ordinal parsing accepts just a number in addition to | |
// number + (possibly) stuff coming from _ordinalParseLenient. | |
this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source); | |
} | |
function mergeConfigs(parentConfig, childConfig) { | |
var res = extend({}, parentConfig), prop; | |
for (prop in childConfig) { | |
if (hasOwnProp(childConfig, prop)) { | |
if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { | |
res[prop] = {}; | |
extend(res[prop], parentConfig[prop]); | |
extend(res[prop], childConfig[prop]); | |
} else if (childConfig[prop] != null) { | |
res[prop] = childConfig[prop]; | |
} else { | |
delete res[prop]; | |
} | |
} | |
} | |
for (prop in parentConfig) { | |
if (hasOwnProp(parentConfig, prop) && | |
!hasOwnProp(childConfig, prop) && | |
isObject(parentConfig[prop])) { | |
// make sure changes to properties don't modify parent config | |
res[prop] = extend({}, res[prop]); | |
} | |
} | |
return res; | |
} | |
function Locale(config) { | |
if (config != null) { | |
this.set(config); | |
} | |
} | |
var keys; | |
if (Object.keys) { | |
keys = Object.keys; | |
} else { | |
keys = function (obj) { | |
var i, res = []; | |
for (i in obj) { | |
if (hasOwnProp(obj, i)) { | |
res.push(i); | |
} | |
} | |
return res; | |
}; | |
} | |
var defaultCalendar = { | |
sameDay : '[Today at] LT', | |
nextDay : '[Tomorrow at] LT', | |
nextWeek : 'dddd [at] LT', | |
lastDay : '[Yesterday at] LT', | |
lastWeek : '[Last] dddd [at] LT', | |
sameElse : 'L' | |
}; | |
function locale_calendar__calendar (key, mom, now) { | |
var output = this._calendar[key] || this._calendar['sameElse']; | |
return isFunction(output) ? output.call(mom, now) : output; | |
} | |
var defaultLongDateFormat = { | |
LTS : 'h:mm:ss A', | |
LT : 'h:mm A', | |
L : 'MM/DD/YYYY', | |
LL : 'MMMM D, YYYY', | |
LLL : 'MMMM D, YYYY h:mm A', | |
LLLL : 'dddd, MMMM D, YYYY h:mm A' | |
}; | |
function longDateFormat (key) { | |
var format = this._longDateFormat[key], | |
formatUpper = this._longDateFormat[key.toUpperCase()]; | |
if (format || !formatUpper) { | |
return format; | |
} | |
this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { | |
return val.slice(1); | |
}); | |
return this._longDateFormat[key]; | |
} | |
var defaultInvalidDate = 'Invalid date'; | |
function invalidDate () { | |
return this._invalidDate; | |
} | |
var defaultOrdinal = '%d'; | |
var defaultOrdinalParse = /\d{1,2}/; | |
function ordinal (number) { | |
return this._ordinal.replace('%d', number); | |
} | |
var defaultRelativeTime = { | |
future : 'in %s', | |
past : '%s ago', | |
s : 'a few seconds', | |
m : 'a minute', | |
mm : '%d minutes', | |
h : 'an hour', | |
hh : '%d hours', | |
d : 'a day', | |
dd : '%d days', | |
M : 'a month', | |
MM : '%d months', | |
y : 'a year', | |
yy : '%d years' | |
}; | |
function relative__relativeTime (number, withoutSuffix, string, isFuture) { | |
var output = this._relativeTime[string]; | |
return (isFunction(output)) ? | |
output(number, withoutSuffix, string, isFuture) : | |
output.replace(/%d/i, number); | |
} | |
function pastFuture (diff, output) { | |
var format = this._relativeTime[diff > 0 ? 'future' : 'past']; | |
return isFunction(format) ? format(output) : format.replace(/%s/i, output); | |
} | |
var aliases = {}; | |
function addUnitAlias (unit, shorthand) { | |
var lowerCase = unit.toLowerCase(); | |
aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; | |
} | |
function normalizeUnits(units) { | |
return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; | |
} | |
function normalizeObjectUnits(inputObject) { | |
var normalizedInput = {}, | |
normalizedProp, | |
prop; | |
for (prop in inputObject) { | |
if (hasOwnProp(inputObject, prop)) { | |
normalizedProp = normalizeUnits(prop); | |
if (normalizedProp) { | |
normalizedInput[normalizedProp] = inputObject[prop]; | |
} | |
} | |
} | |
return normalizedInput; | |
} | |
var priorities = {}; | |
function addUnitPriority(unit, priority) { | |
priorities[unit] = priority; | |
} | |
function getPrioritizedUnits(unitsObj) { | |
var units = []; | |
for (var u in unitsObj) { | |
units.push({unit: u, priority: priorities[u]}); | |
} | |
units.sort(function (a, b) { | |
return a.priority - b.priority; | |
}); | |
return units; | |
} | |
function makeGetSet (unit, keepTime) { | |
return function (value) { | |
if (value != null) { | |
get_set__set(this, unit, value); | |
utils_hooks__hooks.updateOffset(this, keepTime); | |
return this; | |
} else { | |
return get_set__get(this, unit); | |
} | |
}; | |
} | |
function get_set__get (mom, unit) { | |
return mom.isValid() ? | |
mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; | |
} | |
function get_set__set (mom, unit, value) { | |
if (mom.isValid()) { | |
mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); | |
} | |
} | |
// MOMENTS | |
function stringGet (units) { | |
units = normalizeUnits(units); | |
if (isFunction(this[units])) { | |
return this[units](); | |
} | |
return this; | |
} | |
function stringSet (units, value) { | |
if (typeof units === 'object') { | |
units = normalizeObjectUnits(units); | |
var prioritized = getPrioritizedUnits(units); | |
for (var i = 0; i < prioritized.length; i++) { | |
this[prioritized[i].unit](units[prioritized[i].unit]); | |
} | |
} else { | |
units = normalizeUnits(units); | |
if (isFunction(this[units])) { | |
return this[units](value); | |
} | |
} | |
return this; | |
} | |
function zeroFill(number, targetLength, forceSign) { | |
var absNumber = '' + Math.abs(number), | |
zerosToFill = targetLength - absNumber.length, | |
sign = number >= 0; | |
return (sign ? (forceSign ? '+' : '') : '-') + | |
Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; | |
} | |
var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; | |
var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; | |
var formatFunctions = {}; | |
var formatTokenFunctions = {}; | |
// token: 'M' | |
// padded: ['MM', 2] | |
// ordinal: 'Mo' | |
// callback: function () { this.month() + 1 } | |
function addFormatToken (token, padded, ordinal, callback) { | |
var func = callback; | |
if (typeof callback === 'string') { | |
func = function () { | |
return this[callback](); | |
}; | |
} | |
if (token) { | |
formatTokenFunctions[token] = func; | |
} | |
if (padded) { | |
formatTokenFunctions[padded[0]] = function () { | |
return zeroFill(func.apply(this, arguments), padded[1], padded[2]); | |
}; | |
} | |
if (ordinal) { | |
formatTokenFunctions[ordinal] = function () { | |
return this.localeData().ordinal(func.apply(this, arguments), token); | |
}; | |
} | |
} | |
function removeFormattingTokens(input) { | |
if (input.match(/\[[\s\S]/)) { | |
return input.replace(/^\[|\]$/g, ''); | |
} | |
return input.replace(/\\/g, ''); | |
} | |
function makeFormatFunction(format) { | |
var array = format.match(formattingTokens), i, length; | |
for (i = 0, length = array.length; i < length; i++) { | |
if (formatTokenFunctions[array[i]]) { | |
array[i] = formatTokenFunctions[array[i]]; | |
} else { | |
array[i] = removeFormattingTokens(array[i]); | |
} | |
} | |
return function (mom) { | |
var output = '', i; | |
for (i = 0; i < length; i++) { | |
output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; | |
} | |
return output; | |
}; | |
} | |
// format date using native date object | |
function formatMoment(m, format) { | |
if (!m.isValid()) { | |
return m.localeData().invalidDate(); | |
} | |
format = expandFormat(format, m.localeData()); | |
formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); | |
return formatFunctions[format](m); | |
} | |
function expandFormat(format, locale) { | |
var i = 5; | |
function replaceLongDateFormatTokens(input) { | |
return locale.longDateFormat(input) || input; | |
} | |
localFormattingTokens.lastIndex = 0; | |
while (i >= 0 && localFormattingTokens.test(format)) { | |
format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); | |
localFormattingTokens.lastIndex = 0; | |
i -= 1; | |
} | |
return format; | |
} | |
var match1 = /\d/; // 0 - 9 | |
var match2 = /\d\d/; // 00 - 99 | |
var match3 = /\d{3}/; // 000 - 999 | |
var match4 = /\d{4}/; // 0000 - 9999 | |
var match6 = /[+-]?\d{6}/; // -999999 - 999999 | |
var match1to2 = /\d\d?/; // 0 - 99 | |
var match3to4 = /\d\d\d\d?/; // 999 - 9999 | |
var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 | |
var match1to3 = /\d{1,3}/; // 0 - 999 | |
var match1to4 = /\d{1,4}/; // 0 - 9999 | |
var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 | |
var matchUnsigned = /\d+/; // 0 - inf | |
var matchSigned = /[+-]?\d+/; // -inf - inf | |
var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z | |
var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z | |
var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 | |
// any word (or two) characters or numbers including two/three word month in arabic. | |
// includes scottish gaelic two word and hyphenated months | |
var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; | |
var regexes = {}; | |
function addRegexToken (token, regex, strictRegex) { | |
regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { | |
return (isStrict && strictRegex) ? strictRegex : regex; | |
}; | |
} | |
function getParseRegexForToken (token, config) { | |
if (!hasOwnProp(regexes, token)) { | |
return new RegExp(unescapeFormat(token)); | |
} | |
return regexes[token](config._strict, config._locale); | |
} | |
// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript | |
function unescapeFormat(s) { | |
return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { | |
return p1 || p2 || p3 || p4; | |
})); | |
} | |
function regexEscape(s) { | |
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | |
} | |
var tokens = {}; | |
function addParseToken (token, callback) { | |
var i, func = callback; | |
if (typeof token === 'string') { | |
token = [token]; | |
} | |
if (typeof callback === 'number') { | |
func = function (input, array) { | |
array[callback] = toInt(input); | |
}; | |
} | |
for (i = 0; i < token.length; i++) { | |
tokens[token[i]] = func; | |
} | |
} | |
function addWeekParseToken (token, callback) { | |
addParseToken(token, function (input, array, config, token) { | |
config._w = config._w || {}; | |
callback(input, config._w, config, token); | |
}); | |
} | |
function addTimeToArrayFromToken(token, input, config) { | |
if (input != null && hasOwnProp(tokens, token)) { | |
tokens[token](input, config._a, config, token); | |
} | |
} | |
var YEAR = 0; | |
var MONTH = 1; | |
var DATE = 2; | |
var HOUR = 3; | |
var MINUTE = 4; | |
var SECOND = 5; | |
var MILLISECOND = 6; | |
var WEEK = 7; | |
var WEEKDAY = 8; | |
var indexOf; | |
if (Array.prototype.indexOf) { | |
indexOf = Array.prototype.indexOf; | |
} else { | |
indexOf = function (o) { | |
// I know | |
var i; | |
for (i = 0; i < this.length; ++i) { | |
if (this[i] === o) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
} | |
function daysInMonth(year, month) { | |
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); | |
} | |
// FORMATTING | |
addFormatToken('M', ['MM', 2], 'Mo', function () { | |
return this.month() + 1; | |
}); | |
addFormatToken('MMM', 0, 0, function (format) { | |
return this.localeData().monthsShort(this, format); | |
}); | |
addFormatToken('MMMM', 0, 0, function (format) { | |
return this.localeData().months(this, format); | |
}); | |
// ALIASES | |
addUnitAlias('month', 'M'); | |
// PRIORITY | |
addUnitPriority('month', 8); | |
// PARSING | |
addRegexToken('M', match1to2); | |
addRegexToken('MM', match1to2, match2); | |
addRegexToken('MMM', function (isStrict, locale) { | |
return locale.monthsShortRegex(isStrict); | |
}); | |
addRegexToken('MMMM', function (isStrict, locale) { | |
return locale.monthsRegex(isStrict); | |
}); | |
addParseToken(['M', 'MM'], function (input, array) { | |
array[MONTH] = toInt(input) - 1; | |
}); | |
addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { | |
var month = config._locale.monthsParse(input, token, config._strict); | |
// if we didn't find a month name, mark the date as invalid. | |
if (month != null) { | |
array[MONTH] = month; | |
} else { | |
getParsingFlags(config).invalidMonth = input; | |
} | |
}); | |
// LOCALES | |
var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/; | |
var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); | |
function localeMonths (m, format) { | |
return isArray(this._months) ? this._months[m.month()] : | |
this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; | |
} | |
var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); | |
function localeMonthsShort (m, format) { | |
return isArray(this._monthsShort) ? this._monthsShort[m.month()] : | |
this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; | |
} | |
function units_month__handleStrictParse(monthName, format, strict) { | |
var i, ii, mom, llc = monthName.toLocaleLowerCase(); | |
if (!this._monthsParse) { | |
// this is not used | |
this._monthsParse = []; | |
this._longMonthsParse = []; | |
this._shortMonthsParse = []; | |
for (i = 0; i < 12; ++i) { | |
mom = create_utc__createUTC([2000, i]); | |
this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); | |
this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); | |
} | |
} | |
if (strict) { | |
if (format === 'MMM') { | |
ii = indexOf.call(this._shortMonthsParse, llc); | |
return ii !== -1 ? ii : null; | |
} else { | |
ii = indexOf.call(this._longMonthsParse, llc); | |
return ii !== -1 ? ii : null; | |
} | |
} else { | |
if (format === 'MMM') { | |
ii = indexOf.call(this._shortMonthsParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._longMonthsParse, llc); | |
return ii !== -1 ? ii : null; | |
} else { | |
ii = indexOf.call(this._longMonthsParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._shortMonthsParse, llc); | |
return ii !== -1 ? ii : null; | |
} | |
} | |
} | |
function localeMonthsParse (monthName, format, strict) { | |
var i, mom, regex; | |
if (this._monthsParseExact) { | |
return units_month__handleStrictParse.call(this, monthName, format, strict); | |
} | |
if (!this._monthsParse) { | |
this._monthsParse = []; | |
this._longMonthsParse = []; | |
this._shortMonthsParse = []; | |
} | |
// TODO: add sorting | |
// Sorting makes sure if one month (or abbr) is a prefix of another | |
// see sorting in computeMonthsParse | |
for (i = 0; i < 12; i++) { | |
// make the regex if we don't have it already | |
mom = create_utc__createUTC([2000, i]); | |
if (strict && !this._longMonthsParse[i]) { | |
this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); | |
this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); | |
} | |
if (!strict && !this._monthsParse[i]) { | |
regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); | |
this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); | |
} | |
// test the regex | |
if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { | |
return i; | |
} else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { | |
return i; | |
} else if (!strict && this._monthsParse[i].test(monthName)) { | |
return i; | |
} | |
} | |
} | |
// MOMENTS | |
function setMonth (mom, value) { | |
var dayOfMonth; | |
if (!mom.isValid()) { | |
// No op | |
return mom; | |
} | |
if (typeof value === 'string') { | |
if (/^\d+$/.test(value)) { | |
value = toInt(value); | |
} else { | |
value = mom.localeData().monthsParse(value); | |
// TODO: Another silent failure? | |
if (typeof value !== 'number') { | |
return mom; | |
} | |
} | |
} | |
dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); | |
mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); | |
return mom; | |
} | |
function getSetMonth (value) { | |
if (value != null) { | |
setMonth(this, value); | |
utils_hooks__hooks.updateOffset(this, true); | |
return this; | |
} else { | |
return get_set__get(this, 'Month'); | |
} | |
} | |
function getDaysInMonth () { | |
return daysInMonth(this.year(), this.month()); | |
} | |
var defaultMonthsShortRegex = matchWord; | |
function monthsShortRegex (isStrict) { | |
if (this._monthsParseExact) { | |
if (!hasOwnProp(this, '_monthsRegex')) { | |
computeMonthsParse.call(this); | |
} | |
if (isStrict) { | |
return this._monthsShortStrictRegex; | |
} else { | |
return this._monthsShortRegex; | |
} | |
} else { | |
if (!hasOwnProp(this, '_monthsShortRegex')) { | |
this._monthsShortRegex = defaultMonthsShortRegex; | |
} | |
return this._monthsShortStrictRegex && isStrict ? | |
this._monthsShortStrictRegex : this._monthsShortRegex; | |
} | |
} | |
var defaultMonthsRegex = matchWord; | |
function monthsRegex (isStrict) { | |
if (this._monthsParseExact) { | |
if (!hasOwnProp(this, '_monthsRegex')) { | |
computeMonthsParse.call(this); | |
} | |
if (isStrict) { | |
return this._monthsStrictRegex; | |
} else { | |
return this._monthsRegex; | |
} | |
} else { | |
if (!hasOwnProp(this, '_monthsRegex')) { | |
this._monthsRegex = defaultMonthsRegex; | |
} | |
return this._monthsStrictRegex && isStrict ? | |
this._monthsStrictRegex : this._monthsRegex; | |
} | |
} | |
function computeMonthsParse () { | |
function cmpLenRev(a, b) { | |
return b.length - a.length; | |
} | |
var shortPieces = [], longPieces = [], mixedPieces = [], | |
i, mom; | |
for (i = 0; i < 12; i++) { | |
// make the regex if we don't have it already | |
mom = create_utc__createUTC([2000, i]); | |
shortPieces.push(this.monthsShort(mom, '')); | |
longPieces.push(this.months(mom, '')); | |
mixedPieces.push(this.months(mom, '')); | |
mixedPieces.push(this.monthsShort(mom, '')); | |
} | |
// Sorting makes sure if one month (or abbr) is a prefix of another it | |
// will match the longer piece. | |
shortPieces.sort(cmpLenRev); | |
longPieces.sort(cmpLenRev); | |
mixedPieces.sort(cmpLenRev); | |
for (i = 0; i < 12; i++) { | |
shortPieces[i] = regexEscape(shortPieces[i]); | |
longPieces[i] = regexEscape(longPieces[i]); | |
} | |
for (i = 0; i < 24; i++) { | |
mixedPieces[i] = regexEscape(mixedPieces[i]); | |
} | |
this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); | |
this._monthsShortRegex = this._monthsRegex; | |
this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); | |
this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); | |
} | |
// FORMATTING | |
addFormatToken('Y', 0, 0, function () { | |
var y = this.year(); | |
return y <= 9999 ? '' + y : '+' + y; | |
}); | |
addFormatToken(0, ['YY', 2], 0, function () { | |
return this.year() % 100; | |
}); | |
addFormatToken(0, ['YYYY', 4], 0, 'year'); | |
addFormatToken(0, ['YYYYY', 5], 0, 'year'); | |
addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); | |
// ALIASES | |
addUnitAlias('year', 'y'); | |
// PRIORITIES | |
addUnitPriority('year', 1); | |
// PARSING | |
addRegexToken('Y', matchSigned); | |
addRegexToken('YY', match1to2, match2); | |
addRegexToken('YYYY', match1to4, match4); | |
addRegexToken('YYYYY', match1to6, match6); | |
addRegexToken('YYYYYY', match1to6, match6); | |
addParseToken(['YYYYY', 'YYYYYY'], YEAR); | |
addParseToken('YYYY', function (input, array) { | |
array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input); | |
}); | |
addParseToken('YY', function (input, array) { | |
array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); | |
}); | |
addParseToken('Y', function (input, array) { | |
array[YEAR] = parseInt(input, 10); | |
}); | |
// HELPERS | |
function daysInYear(year) { | |
return isLeapYear(year) ? 366 : 365; | |
} | |
function isLeapYear(year) { | |
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; | |
} | |
// HOOKS | |
utils_hooks__hooks.parseTwoDigitYear = function (input) { | |
return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); | |
}; | |
// MOMENTS | |
var getSetYear = makeGetSet('FullYear', true); | |
function getIsLeapYear () { | |
return isLeapYear(this.year()); | |
} | |
function createDate (y, m, d, h, M, s, ms) { | |
//can't just apply() to create a date: | |
//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply | |
var date = new Date(y, m, d, h, M, s, ms); | |
//the date constructor remaps years 0-99 to 1900-1999 | |
if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { | |
date.setFullYear(y); | |
} | |
return date; | |
} | |
function createUTCDate (y) { | |
var date = new Date(Date.UTC.apply(null, arguments)); | |
//the Date.UTC function remaps years 0-99 to 1900-1999 | |
if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { | |
date.setUTCFullYear(y); | |
} | |
return date; | |
} | |
// start-of-first-week - start-of-year | |
function firstWeekOffset(year, dow, doy) { | |
var // first-week day -- which january is always in the first week (4 for iso, 1 for other) | |
fwd = 7 + dow - doy, | |
// first-week day local weekday -- which local weekday is fwd | |
fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; | |
return -fwdlw + fwd - 1; | |
} | |
//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday | |
function dayOfYearFromWeeks(year, week, weekday, dow, doy) { | |
var localWeekday = (7 + weekday - dow) % 7, | |
weekOffset = firstWeekOffset(year, dow, doy), | |
dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, | |
resYear, resDayOfYear; | |
if (dayOfYear <= 0) { | |
resYear = year - 1; | |
resDayOfYear = daysInYear(resYear) + dayOfYear; | |
} else if (dayOfYear > daysInYear(year)) { | |
resYear = year + 1; | |
resDayOfYear = dayOfYear - daysInYear(year); | |
} else { | |
resYear = year; | |
resDayOfYear = dayOfYear; | |
} | |
return { | |
year: resYear, | |
dayOfYear: resDayOfYear | |
}; | |
} | |
function weekOfYear(mom, dow, doy) { | |
var weekOffset = firstWeekOffset(mom.year(), dow, doy), | |
week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, | |
resWeek, resYear; | |
if (week < 1) { | |
resYear = mom.year() - 1; | |
resWeek = week + weeksInYear(resYear, dow, doy); | |
} else if (week > weeksInYear(mom.year(), dow, doy)) { | |
resWeek = week - weeksInYear(mom.year(), dow, doy); | |
resYear = mom.year() + 1; | |
} else { | |
resYear = mom.year(); | |
resWeek = week; | |
} | |
return { | |
week: resWeek, | |
year: resYear | |
}; | |
} | |
function weeksInYear(year, dow, doy) { | |
var weekOffset = firstWeekOffset(year, dow, doy), | |
weekOffsetNext = firstWeekOffset(year + 1, dow, doy); | |
return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; | |
} | |
// FORMATTING | |
addFormatToken('w', ['ww', 2], 'wo', 'week'); | |
addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); | |
// ALIASES | |
addUnitAlias('week', 'w'); | |
addUnitAlias('isoWeek', 'W'); | |
// PRIORITIES | |
addUnitPriority('week', 5); | |
addUnitPriority('isoWeek', 5); | |
// PARSING | |
addRegexToken('w', match1to2); | |
addRegexToken('ww', match1to2, match2); | |
addRegexToken('W', match1to2); | |
addRegexToken('WW', match1to2, match2); | |
addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { | |
week[token.substr(0, 1)] = toInt(input); | |
}); | |
// HELPERS | |
// LOCALES | |
function localeWeek (mom) { | |
return weekOfYear(mom, this._week.dow, this._week.doy).week; | |
} | |
var defaultLocaleWeek = { | |
dow : 0, // Sunday is the first day of the week. | |
doy : 6 // The week that contains Jan 1st is the first week of the year. | |
}; | |
function localeFirstDayOfWeek () { | |
return this._week.dow; | |
} | |
function localeFirstDayOfYear () { | |
return this._week.doy; | |
} | |
// MOMENTS | |
function getSetWeek (input) { | |
var week = this.localeData().week(this); | |
return input == null ? week : this.add((input - week) * 7, 'd'); | |
} | |
function getSetISOWeek (input) { | |
var week = weekOfYear(this, 1, 4).week; | |
return input == null ? week : this.add((input - week) * 7, 'd'); | |
} | |
// FORMATTING | |
addFormatToken('d', 0, 'do', 'day'); | |
addFormatToken('dd', 0, 0, function (format) { | |
return this.localeData().weekdaysMin(this, format); | |
}); | |
addFormatToken('ddd', 0, 0, function (format) { | |
return this.localeData().weekdaysShort(this, format); | |
}); | |
addFormatToken('dddd', 0, 0, function (format) { | |
return this.localeData().weekdays(this, format); | |
}); | |
addFormatToken('e', 0, 0, 'weekday'); | |
addFormatToken('E', 0, 0, 'isoWeekday'); | |
// ALIASES | |
addUnitAlias('day', 'd'); | |
addUnitAlias('weekday', 'e'); | |
addUnitAlias('isoWeekday', 'E'); | |
// PRIORITY | |
addUnitPriority('day', 11); | |
addUnitPriority('weekday', 11); | |
addUnitPriority('isoWeekday', 11); | |
// PARSING | |
addRegexToken('d', match1to2); | |
addRegexToken('e', match1to2); | |
addRegexToken('E', match1to2); | |
addRegexToken('dd', function (isStrict, locale) { | |
return locale.weekdaysMinRegex(isStrict); | |
}); | |
addRegexToken('ddd', function (isStrict, locale) { | |
return locale.weekdaysShortRegex(isStrict); | |
}); | |
addRegexToken('dddd', function (isStrict, locale) { | |
return locale.weekdaysRegex(isStrict); | |
}); | |
addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { | |
var weekday = config._locale.weekdaysParse(input, token, config._strict); | |
// if we didn't get a weekday name, mark the date as invalid | |
if (weekday != null) { | |
week.d = weekday; | |
} else { | |
getParsingFlags(config).invalidWeekday = input; | |
} | |
}); | |
addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { | |
week[token] = toInt(input); | |
}); | |
// HELPERS | |
function parseWeekday(input, locale) { | |
if (typeof input !== 'string') { | |
return input; | |
} | |
if (!isNaN(input)) { | |
return parseInt(input, 10); | |
} | |
input = locale.weekdaysParse(input); | |
if (typeof input === 'number') { | |
return input; | |
} | |
return null; | |
} | |
function parseIsoWeekday(input, locale) { | |
if (typeof input === 'string') { | |
return locale.weekdaysParse(input) % 7 || 7; | |
} | |
return isNaN(input) ? null : input; | |
} | |
// LOCALES | |
var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); | |
function localeWeekdays (m, format) { | |
return isArray(this._weekdays) ? this._weekdays[m.day()] : | |
this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; | |
} | |
var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); | |
function localeWeekdaysShort (m) { | |
return this._weekdaysShort[m.day()]; | |
} | |
var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); | |
function localeWeekdaysMin (m) { | |
return this._weekdaysMin[m.day()]; | |
} | |
function day_of_week__handleStrictParse(weekdayName, format, strict) { | |
var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); | |
if (!this._weekdaysParse) { | |
this._weekdaysParse = []; | |
this._shortWeekdaysParse = []; | |
this._minWeekdaysParse = []; | |
for (i = 0; i < 7; ++i) { | |
mom = create_utc__createUTC([2000, 1]).day(i); | |
this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); | |
this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); | |
this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); | |
} | |
} | |
if (strict) { | |
if (format === 'dddd') { | |
ii = indexOf.call(this._weekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} else if (format === 'ddd') { | |
ii = indexOf.call(this._shortWeekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} else { | |
ii = indexOf.call(this._minWeekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} | |
} else { | |
if (format === 'dddd') { | |
ii = indexOf.call(this._weekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._shortWeekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._minWeekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} else if (format === 'ddd') { | |
ii = indexOf.call(this._shortWeekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._weekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._minWeekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} else { | |
ii = indexOf.call(this._minWeekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._weekdaysParse, llc); | |
if (ii !== -1) { | |
return ii; | |
} | |
ii = indexOf.call(this._shortWeekdaysParse, llc); | |
return ii !== -1 ? ii : null; | |
} | |
} | |
} | |
function localeWeekdaysParse (weekdayName, format, strict) { | |
var i, mom, regex; | |
if (this._weekdaysParseExact) { | |
return day_of_week__handleStrictParse.call(this, weekdayName, format, strict); | |
} | |
if (!this._weekdaysParse) { | |
this._weekdaysParse = []; | |
this._minWeekdaysParse = []; | |
this._shortWeekdaysParse = []; | |
this._fullWeekdaysParse = []; | |
} | |
for (i = 0; i < 7; i++) { | |
// make the regex if we don't have it already | |
mom = create_utc__createUTC([2000, 1]).day(i); | |
if (strict && !this._fullWeekdaysParse[i]) { | |
this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); | |
this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); | |
this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); | |
} | |
if (!this._weekdaysParse[i]) { | |
regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); | |
this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); | |
} | |
// test the regex | |
if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { | |
return i; | |
} else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { | |
return i; | |
} else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { | |
return i; | |
} else if (!strict && this._weekdaysParse[i].test(weekdayName)) { | |
return i; | |
} | |
} | |
} | |
// MOMENTS | |
function getSetDayOfWeek (input) { | |
if (!this.isValid()) { | |
return input != null ? this : NaN; | |
} | |
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); | |
if (input != null) { | |
input = parseWeekday(input, this.localeData()); | |
return this.add(input - day, 'd'); | |
} else { | |
return day; | |
} | |
} | |
function getSetLocaleDayOfWeek (input) { | |
if (!this.isValid()) { | |
return input != null ? this : NaN; | |
} | |
var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; | |
return input == null ? weekday : this.add(input - weekday, 'd'); | |
} | |
function getSetISODayOfWeek (input) { | |
if (!this.isValid()) { | |
return input != null ? this : NaN; | |
} | |
// behaves the same as moment#day except | |
// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) | |
// as a setter, sunday should belong to the previous week. | |
if (input != null) { | |
var weekday = parseIsoWeekday(input, this.localeData()); | |
return this.day(this.day() % 7 ? weekday : weekday - 7); | |
} else { | |
return this.day() || 7; | |
} | |
} | |
var defaultWeekdaysRegex = matchWord; | |
function weekdaysRegex (isStrict) { | |
if (this._weekdaysParseExact) { | |
if (!hasOwnProp(this, '_weekdaysRegex')) { | |
computeWeekdaysParse.call(this); | |
} | |
if (isStrict) { | |
return this._weekdaysStrictRegex; | |
} else { | |
return this._weekdaysRegex; | |
} | |
} else { | |
if (!hasOwnProp(this, '_weekdaysRegex')) { | |
this._weekdaysRegex = defaultWeekdaysRegex; | |
} | |
return this._weekdaysStrictRegex && isStrict ? | |
this._weekdaysStrictRegex : this._weekdaysRegex; | |
} | |
} | |
var defaultWeekdaysShortRegex = matchWord; | |
function weekdaysShortRegex (isStrict) { | |
if (this._weekdaysParseExact) { | |
if (!hasOwnProp(this, '_weekdaysRegex')) { | |
computeWeekdaysParse.call(this); | |
} | |
if (isStrict) { | |
return this._weekdaysShortStrictRegex; | |
} else { | |
return this._weekdaysShortRegex; | |
} | |
} else { | |
if (!hasOwnProp(this, '_weekdaysShortRegex')) { | |
this._weekdaysShortRegex = defaultWeekdaysShortRegex; | |
} | |
return this._weekdaysShortStrictRegex && isStrict ? | |
this._weekdaysShortStrictRegex : this._weekdaysShortRegex; | |
} | |
} | |
var defaultWeekdaysMinRegex = matchWord; | |
function weekdaysMinRegex (isStrict) { | |
if (this._weekdaysParseExact) { | |
if (!hasOwnProp(this, '_weekdaysRegex')) { | |
computeWeekdaysParse.call(this); | |
} | |
if (isStrict) { | |
return this._weekdaysMinStrictRegex; | |
} else { | |
return this._weekdaysMinRegex; | |
} | |
} else { | |
if (!hasOwnProp(this, '_weekdaysMinRegex')) { | |
this._weekdaysMinRegex = defaultWeekdaysMinRegex; | |
} | |
return this._weekdaysMinStrictRegex && isStrict ? | |
this._weekdaysMinStrictRegex : this._weekdaysMinRegex; | |
} | |
} | |
function computeWeekdaysParse () { | |
function cmpLenRev(a, b) { | |
return b.length - a.length; | |
} | |
var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], | |
i, mom, minp, shortp, longp; | |
for (i = 0; i < 7; i++) { | |
// make the regex if we don't have it already | |
mom = create_utc__createUTC([2000, 1]).day(i); | |
minp = this.weekdaysMin(mom, ''); | |
shortp = this.weekdaysShort(mom, ''); | |
longp = this.weekdays(mom, ''); | |
minPieces.push(minp); | |
shortPieces.push(shortp); | |
longPieces.push(longp); | |
mixedPieces.push(minp); | |
mixedPieces.push(shortp); | |
mixedPieces.push(longp); | |
} | |
// Sorting makes sure if one weekday (or abbr) is a prefix of another it | |
// will match the longer piece. | |
minPieces.sort(cmpLenRev); | |
shortPieces.sort(cmpLenRev); | |
longPieces.sort(cmpLenRev); | |
mixedPieces.sort(cmpLenRev); | |
for (i = 0; i < 7; i++) { | |
shortPieces[i] = regexEscape(shortPieces[i]); | |
longPieces[i] = regexEscape(longPieces[i]); | |
mixedPieces[i] = regexEscape(mixedPieces[i]); | |
} | |
this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); | |
this._weekdaysShortRegex = this._weekdaysRegex; | |
this._weekdaysMinRegex = this._weekdaysRegex; | |
this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); | |
this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); | |
this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); | |
} | |
// FORMATTING | |
function hFormat() { | |
return this.hours() % 12 || 12; | |
} | |
function kFormat() { | |
return this.hours() || 24; | |
} | |
addFormatToken('H', ['HH', 2], 0, 'hour'); | |
addFormatToken('h', ['hh', 2], 0, hFormat); | |
addFormatToken('k', ['kk', 2], 0, kFormat); | |
addFormatToken('hmm', 0, 0, function () { | |
return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); | |
}); | |
addFormatToken('hmmss', 0, 0, function () { | |
return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + | |
zeroFill(this.seconds(), 2); | |
}); | |
addFormatToken('Hmm', 0, 0, function () { | |
return '' + this.hours() + zeroFill(this.minutes(), 2); | |
}); | |
addFormatToken('Hmmss', 0, 0, function () { | |
return '' + this.hours() + zeroFill(this.minutes(), 2) + | |
zeroFill(this.seconds(), 2); | |
}); | |
function meridiem (token, lowercase) { | |
addFormatToken(token, 0, 0, function () { | |
return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); | |
}); | |
} | |
meridiem('a', true); | |
meridiem('A', false); | |
// ALIASES | |
addUnitAlias('hour', 'h'); | |
// PRIORITY | |
addUnitPriority('hour', 13); | |
// PARSING | |
function matchMeridiem (isStrict, locale) { | |
return locale._meridiemParse; | |
} | |
addRegexToken('a', matchMeridiem); | |
addRegexToken('A', matchMeridiem); | |
addRegexToken('H', match1to2); | |
addRegexToken('h', match1to2); | |
addRegexToken('HH', match1to2, match2); | |
addRegexToken('hh', match1to2, match2); | |
addRegexToken('hmm', match3to4); | |
addRegexToken('hmmss', match5to6); | |
addRegexToken('Hmm', match3to4); | |
addRegexToken('Hmmss', match5to6); | |
addParseToken(['H', 'HH'], HOUR); | |
addParseToken(['a', 'A'], function (input, array, config) { | |
config._isPm = config._locale.isPM(input); | |
config._meridiem = input; | |
}); | |
addParseToken(['h', 'hh'], function (input, array, config) { | |
array[HOUR] = toInt(input); | |
getParsingFlags(config).bigHour = true; | |
}); | |
addParseToken('hmm', function (input, array, config) { | |
var pos = input.length - 2; | |
array[HOUR] = toInt(input.substr(0, pos)); | |
array[MINUTE] = toInt(input.substr(pos)); | |
getParsingFlags(config).bigHour = true; | |
}); | |
addParseToken('hmmss', function (input, array, config) { | |
var pos1 = input.length - 4; | |
var pos2 = input.length - 2; | |
array[HOUR] = toInt(input.substr(0, pos1)); | |
array[MINUTE] = toInt(input.substr(pos1, 2)); | |
array[SECOND] = toInt(input.substr(pos2)); | |
getParsingFlags(config).bigHour = true; | |
}); | |
addParseToken('Hmm', function (input, array, config) { | |
var pos = input.length - 2; | |
array[HOUR] = toInt(input.substr(0, pos)); | |
array[MINUTE] = toInt(input.substr(pos)); | |
}); | |
addParseToken('Hmmss', function (input, array, config) { | |
var pos1 = input.length - 4; | |
var pos2 = input.length - 2; | |
array[HOUR] = toInt(input.substr(0, pos1)); | |
array[MINUTE] = toInt(input.substr(pos1, 2)); | |
array[SECOND] = toInt(input.substr(pos2)); | |
}); | |
// LOCALES | |
function localeIsPM (input) { | |
// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays | |
// Using charAt should be more compatible. | |
return ((input + '').toLowerCase().charAt(0) === 'p'); | |
} | |
var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; | |
function localeMeridiem (hours, minutes, isLower) { | |
if (hours > 11) { | |
return isLower ? 'pm' : 'PM'; | |
} else { | |
return isLower ? 'am' : 'AM'; | |
} | |
} | |
// MOMENTS | |
// Setting the hour should keep the time, because the user explicitly | |
// specified which hour he wants. So trying to maintain the same hour (in | |
// a new timezone) makes sense. Adding/subtracting hours does not follow | |
// this rule. | |
var getSetHour = makeGetSet('Hours', true); | |
var baseConfig = { | |
calendar: defaultCalendar, | |
longDateFormat: defaultLongDateFormat, | |
invalidDate: defaultInvalidDate, | |
ordinal: defaultOrdinal, | |
ordinalParse: defaultOrdinalParse, | |
relativeTime: defaultRelativeTime, | |
months: defaultLocaleMonths, | |
monthsShort: defaultLocaleMonthsShort, | |
week: defaultLocaleWeek, | |
weekdays: defaultLocaleWeekdays, | |
weekdaysMin: defaultLocaleWeekdaysMin, | |
weekdaysShort: defaultLocaleWeekdaysShort, | |
meridiemParse: defaultLocaleMeridiemParse | |
}; | |
// internal storage for locale config files | |
var locales = {}; | |
var globalLocale; | |
function normalizeLocale(key) { | |
return key ? key.toLowerCase().replace('_', '-') : key; | |
} | |
// pick the locale from the array | |
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each | |
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root | |
function chooseLocale(names) { | |
var i = 0, j, next, locale, split; | |
while (i < names.length) { | |
split = normalizeLocale(names[i]).split('-'); | |
j = split.length; | |
next = normalizeLocale(names[i + 1]); | |
next = next ? next.split('-') : null; | |
while (j > 0) { | |
locale = loadLocale(split.slice(0, j).join('-')); | |
if (locale) { | |
return locale; | |
} | |
if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { | |
//the next array item is better than a shallower substring of this one | |
break; | |
} | |
j--; | |
} | |
i++; | |
} | |
return null; | |
} | |
function loadLocale(name) { | |
var oldLocale = null; | |
// TODO: Find a better way to register and load all the locales in Node | |
if (!locales[name] && (typeof module !== 'undefined') && | |
module && module.exports) { | |
try { | |
oldLocale = globalLocale._abbr; | |
require('./locale/' + name); | |
// because defineLocale currently also sets the global locale, we | |
// want to undo that for lazy loaded locales | |
locale_locales__getSetGlobalLocale(oldLocale); | |
} catch (e) { } | |
} | |
return locales[name]; | |
} | |
// This function will load locale and then set the global locale. If | |
// no arguments are passed in, it will simply return the current global | |
// locale key. | |
function locale_locales__getSetGlobalLocale (key, values) { | |
var data; | |
if (key) { | |
if (isUndefined(values)) { | |
data = locale_locales__getLocale(key); | |
} | |
else { | |
data = defineLocale(key, values); | |
} | |
if (data) { | |
// moment.duration._locale = moment._locale = data; | |
globalLocale = data; | |
} | |
} | |
return globalLocale._abbr; | |
} | |
function defineLocale (name, config) { | |
if (config !== null) { | |
var parentConfig = baseConfig; | |
config.abbr = name; | |
if (locales[name] != null) { | |
deprecateSimple('defineLocaleOverride', | |
'use moment.updateLocale(localeName, config) to change ' + | |
'an existing locale. moment.defineLocale(localeName, ' + | |
'config) should only be used for creating a new locale ' + | |
'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); | |
parentConfig = locales[name]._config; | |
} else if (config.parentLocale != null) { | |
if (locales[config.parentLocale] != null) { | |
parentConfig = locales[config.parentLocale]._config; | |
} else { | |
// treat as if there is no base config | |
deprecateSimple('parentLocaleUndefined', | |
'specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/'); | |
} | |
} | |
locales[name] = new Locale(mergeConfigs(parentConfig, config)); | |
// backwards compat for now: also set the locale | |
locale_locales__getSetGlobalLocale(name); | |
return locales[name]; | |
} else { | |
// useful for testing | |
delete locales[name]; | |
return null; | |
} | |
} | |
function updateLocale(name, config) { | |
if (config != null) { | |
var locale, parentConfig = baseConfig; | |
// MERGE | |
if (locales[name] != null) { | |
parentConfig = locales[name]._config; | |
} | |
config = mergeConfigs(parentConfig, config); | |
locale = new Locale(config); | |
locale.parentLocale = locales[name]; | |
locales[name] = locale; | |
// backwards compat for now: also set the locale | |
locale_locales__getSetGlobalLocale(name); | |
} else { | |
// pass null for config to unupdate, useful for tests | |
if (locales[name] != null) { | |
if (locales[name].parentLocale != null) { | |
locales[name] = locales[name].parentLocale; | |
} else if (locales[name] != null) { | |
delete locales[name]; | |
} | |
} | |
} | |
return locales[name]; | |
} | |
// returns locale data | |
function locale_locales__getLocale (key) { | |
var locale; | |
if (key && key._locale && key._locale._abbr) { | |
key = key._locale._abbr; | |
} | |
if (!key) { | |
return globalLocale; | |
} | |
if (!isArray(key)) { | |
//short-circuit everything else | |
locale = loadLocale(key); | |
if (locale) { | |
return locale; | |
} | |
key = [key]; | |
} | |
return chooseLocale(key); | |
} | |
function locale_locales__listLocales() { | |
return keys(locales); | |
} | |
function checkOverflow (m) { | |
var overflow; | |
var a = m._a; | |
if (a && getParsingFlags(m).overflow === -2) { | |
overflow = | |
a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : | |
a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : | |
a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : | |
a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : | |
a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : | |
a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : | |
-1; | |
if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { | |
overflow = DATE; | |
} | |
if (getParsingFlags(m)._overflowWeeks && overflow === -1) { | |
overflow = WEEK; | |
} | |
if (getParsingFlags(m)._overflowWeekday && overflow === -1) { | |
overflow = WEEKDAY; | |
} | |
getParsingFlags(m).overflow = overflow; | |
} | |
return m; | |
} | |
// iso 8601 regex | |
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) | |
var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; | |
var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; | |
var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; | |
var isoDates = [ | |
['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], | |
['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], | |
['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], | |
['GGGG-[W]WW', /\d{4}-W\d\d/, false], | |
['YYYY-DDD', /\d{4}-\d{3}/], | |
['YYYY-MM', /\d{4}-\d\d/, false], | |
['YYYYYYMMDD', /[+-]\d{10}/], | |
['YYYYMMDD', /\d{8}/], | |
// YYYYMM is NOT allowed by the standard | |
['GGGG[W]WWE', /\d{4}W\d{3}/], | |
['GGGG[W]WW', /\d{4}W\d{2}/, false], | |
['YYYYDDD', /\d{7}/] | |
]; | |
// iso time formats and regexes | |
var isoTimes = [ | |
['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], | |
['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], | |
['HH:mm:ss', /\d\d:\d\d:\d\d/], | |
['HH:mm', /\d\d:\d\d/], | |
['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], | |
['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], | |
['HHmmss', /\d\d\d\d\d\d/], | |
['HHmm', /\d\d\d\d/], | |
['HH', /\d\d/] | |
]; | |
var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; | |
// date from iso format | |
function configFromISO(config) { | |
var i, l, | |
string = config._i, | |
match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), | |
allowTime, dateFormat, timeFormat, tzFormat; | |
if (match) { | |
getParsingFlags(config).iso = true; | |
for (i = 0, l = isoDates.length; i < l; i++) { | |
if (isoDates[i][1].exec(match[1])) { | |
dateFormat = isoDates[i][0]; | |
allowTime = isoDates[i][2] !== false; | |
break; | |
} | |
} | |
if (dateFormat == null) { | |
config._isValid = false; | |
return; | |
} | |
if (match[3]) { | |
for (i = 0, l = isoTimes.length; i < l; i++) { | |
if (isoTimes[i][1].exec(match[3])) { | |
// match[2] should be 'T' or space | |
timeFormat = (match[2] || ' ') + isoTimes[i][0]; | |
break; | |
} | |
} | |
if (timeFormat == null) { | |
config._isValid = false; | |
return; | |
} | |
} | |
if (!allowTime && timeFormat != null) { | |
config._isValid = false; | |
return; | |
} | |
if (match[4]) { | |
if (tzRegex.exec(match[4])) { | |
tzFormat = 'Z'; | |
} else { | |
config._isValid = false; | |
return; | |
} | |
} | |
config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); | |
configFromStringAndFormat(config); | |
} else { | |
config._isValid = false; | |
} | |
} | |
// date from iso format or fallback | |
function configFromString(config) { | |
var matched = aspNetJsonRegex.exec(config._i); | |
if (matched !== null) { | |
config._d = new Date(+matched[1]); | |
return; | |
} | |
configFromISO(config); | |
if (config._isValid === false) { | |
delete config._isValid; | |
utils_hooks__hooks.createFromInputFallback(config); | |
} | |
} | |
utils_hooks__hooks.createFromInputFallback = deprecate( | |
'moment construction falls back to js Date. This is ' + | |
'discouraged and will be removed in upcoming major ' + | |
'release. Please refer to ' + | |
'http://momentjs.com/guides/#/warnings/js-date/ for more info.', | |
function (config) { | |
config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); | |
} | |
); | |
// Pick the first defined of two or three arguments. | |
function defaults(a, b, c) { | |
if (a != null) { | |
return a; | |
} | |
if (b != null) { | |
return b; | |
} | |
return c; | |
} | |
function currentDateArray(config) { | |
// hooks is actually the exported moment object | |
var nowValue = new Date(utils_hooks__hooks.now()); | |
if (config._useUTC) { | |
return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; | |
} | |
return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; | |
} | |
// convert an array to a date. | |
// the array should mirror the parameters below | |
// note: all values past the year are optional and will default to the lowest possible value. | |
// [year, month, day , hour, minute, second, millisecond] | |
function configFromArray (config) { | |
var i, date, input = [], currentDate, yearToUse; | |
if (config._d) { | |
return; | |
} | |
currentDate = currentDateArray(config); | |
//compute day of the year from weeks and weekdays | |
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { | |
dayOfYearFromWeekInfo(config); | |
} | |
//if the day of the year is set, figure out what it is | |
if (config._dayOfYear) { | |
yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); | |
if (config._dayOfYear > daysInYear(yearToUse)) { | |
getParsingFlags(config)._overflowDayOfYear = true; | |
} | |
date = createUTCDate(yearToUse, 0, config._dayOfYear); | |
config._a[MONTH] = date.getUTCMonth(); | |
config._a[DATE] = date.getUTCDate(); | |
} | |
// Default to current date. | |
// * if no year, month, day of month are given, default to today | |
// * if day of month is given, default month and year | |
// * if month is given, default only year | |
// * if year is given, don't default anything | |
for (i = 0; i < 3 && config._a[i] == null; ++i) { | |
config._a[i] = input[i] = currentDate[i]; | |
} | |
// Zero out whatever was not defaulted, including time | |
for (; i < 7; i++) { | |
config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; | |
} | |
// Check for 24:00:00.000 | |
if (config._a[HOUR] === 24 && | |
config._a[MINUTE] === 0 && | |
config._a[SECOND] === 0 && | |
config._a[MILLISECOND] === 0) { | |
config._nextDay = true; | |
config._a[HOUR] = 0; | |
} | |
config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); | |
// Apply timezone offset from input. The actual utcOffset can be changed | |
// with parseZone. | |
if (config._tzm != null) { | |
config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); | |
} | |
if (config._nextDay) { | |
config._a[HOUR] = 24; | |
} | |
} | |
function dayOfYearFromWeekInfo(config) { | |
var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; | |
w = config._w; | |
if (w.GG != null || w.W != null || w.E != null) { | |
dow = 1; | |
doy = 4; | |
// TODO: We need to take the current isoWeekYear, but that depends on | |
// how we interpret now (local, utc, fixed offset). So create | |
// a now version of current config (take local/utc/offset flags, and | |
// create now). | |
weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); | |
week = defaults(w.W, 1); | |
weekday = defaults(w.E, 1); | |
if (weekday < 1 || weekday > 7) { | |
weekdayOverflow = true; | |
} | |
} else { | |
dow = config._locale._week.dow; | |
doy = config._locale._week.doy; | |
weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); | |
week = defaults(w.w, 1); | |
if (w.d != null) { | |
// weekday -- low day numbers are considered next week | |
weekday = w.d; | |
if (weekday < 0 || weekday > 6) { | |
weekdayOverflow = true; | |
} | |
} else if (w.e != null) { | |
// local weekday -- counting starts from begining of week | |
weekday = w.e + dow; | |
if (w.e < 0 || w.e > 6) { | |
weekdayOverflow = true; | |
} | |
} else { | |
// default to begining of week | |
weekday = dow; | |
} | |
} | |
if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { | |
getParsingFlags(config)._overflowWeeks = true; | |
} else if (weekdayOverflow != null) { | |
getParsingFlags(config)._overflowWeekday = true; | |
} else { | |
temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); | |
config._a[YEAR] = temp.year; | |
config._dayOfYear = temp.dayOfYear; | |
} | |
} | |
// constant that refers to the ISO standard | |
utils_hooks__hooks.ISO_8601 = function () {}; | |
// date from string and format string | |
function configFromStringAndFormat(config) { | |
// TODO: Move this to another part of the creation flow to prevent circular deps | |
if (config._f === utils_hooks__hooks.ISO_8601) { | |
configFromISO(config); | |
return; | |
} | |
config._a = []; | |
getParsingFlags(config).empty = true; | |
// This array is used to make a Date, either with `new Date` or `Date.UTC` | |
var string = '' + config._i, | |
i, parsedInput, tokens, token, skipped, | |
stringLength = string.length, | |
totalParsedInputLength = 0; | |
tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; | |
for (i = 0; i < tokens.length; i++) { | |
token = tokens[i]; | |
parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; | |
// console.log('token', token, 'parsedInput', parsedInput, | |
// 'regex', getParseRegexForToken(token, config)); | |
if (parsedInput) { | |
skipped = string.substr(0, string.indexOf(parsedInput)); | |
if (skipped.length > 0) { | |
getParsingFlags(config).unusedInput.push(skipped); | |
} | |
string = string.slice(string.indexOf(parsedInput) + parsedInput.length); | |
totalParsedInputLength += parsedInput.length; | |
} | |
// don't parse if it's not a known token | |
if (formatTokenFunctions[token]) { | |
if (parsedInput) { | |
getParsingFlags(config).empty = false; | |
} | |
else { | |
getParsingFlags(config).unusedTokens.push(token); | |
} | |
addTimeToArrayFromToken(token, parsedInput, config); | |
} | |
else if (config._strict && !parsedInput) { | |
getParsingFlags(config).unusedTokens.push(token); | |
} | |
} | |
// add remaining unparsed input length to the string | |
getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; | |
if (string.length > 0) { | |
getParsingFlags(config).unusedInput.push(string); | |
} | |
// clear _12h flag if hour is <= 12 | |
if (config._a[HOUR] <= 12 && | |
getParsingFlags(config).bigHour === true && | |
config._a[HOUR] > 0) { | |
getParsingFlags(config).bigHour = undefined; | |
} | |
getParsingFlags(config).parsedDateParts = config._a.slice(0); | |
getParsingFlags(config).meridiem = config._meridiem; | |
// handle meridiem | |
config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); | |
configFromArray(config); | |
checkOverflow(config); | |
} | |
function meridiemFixWrap (locale, hour, meridiem) { | |
var isPm; | |
if (meridiem == null) { | |
// nothing to do | |
return hour; | |
} | |
if (locale.meridiemHour != null) { | |
return locale.meridiemHour(hour, meridiem); | |
} else if (locale.isPM != null) { | |
// Fallback | |
isPm = locale.isPM(meridiem); | |
if (isPm && hour < 12) { | |
hour += 12; | |
} | |
if (!isPm && hour === 12) { | |
hour = 0; | |
} | |
return hour; | |
} else { | |
// this is not supposed to happen | |
return hour; | |
} | |
} | |
// date from string and array of format strings | |
function configFromStringAndArray(config) { | |
var tempConfig, | |
bestMoment, | |
scoreToBeat, | |
i, | |
currentScore; | |
if (config._f.length === 0) { | |
getParsingFlags(config).invalidFormat = true; | |
config._d = new Date(NaN); | |
return; | |
} | |
for (i = 0; i < config._f.length; i++) { | |
currentScore = 0; | |
tempConfig = copyConfig({}, config); | |
if (config._useUTC != null) { | |
tempConfig._useUTC = config._useUTC; | |
} | |
tempConfig._f = config._f[i]; | |
configFromStringAndFormat(tempConfig); | |
if (!valid__isValid(tempConfig)) { | |
continue; | |
} | |
// if there is any input that was not parsed add a penalty for that format | |
currentScore += getParsingFlags(tempConfig).charsLeftOver; | |
//or tokens | |
currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; | |
getParsingFlags(tempConfig).score = currentScore; | |
if (scoreToBeat == null || currentScore < scoreToBeat) { | |
scoreToBeat = currentScore; | |
bestMoment = tempConfig; | |
} | |
} | |
extend(config, bestMoment || tempConfig); | |
} | |
function configFromObject(config) { | |
if (config._d) { | |
return; | |
} | |
var i = normalizeObjectUnits(config._i); | |
config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { | |
return obj && parseInt(obj, 10); | |
}); | |
configFromArray(config); | |
} | |
function createFromConfig (config) { | |
var res = new Moment(checkOverflow(prepareConfig(config))); | |
if (res._nextDay) { | |
// Adding is smart enough around DST | |
res.add(1, 'd'); | |
res._nextDay = undefined; | |
} | |
return res; | |
} | |
function prepareConfig (config) { | |
var input = config._i, | |
format = config._f; | |
config._locale = config._locale || locale_locales__getLocale(config._l); | |
if (input === null || (format === undefined && input === '')) { | |
return valid__createInvalid({nullInput: true}); | |
} | |
if (typeof input === 'string') { | |
config._i = input = config._locale.preparse(input); | |
} | |
if (isMoment(input)) { | |
return new Moment(checkOverflow(input)); | |
} else if (isArray(format)) { | |
configFromStringAndArray(config); | |
} else if (isDate(input)) { | |
config._d = input; | |
} else if (format) { | |
configFromStringAndFormat(config); | |
} else { | |
configFromInput(config); | |
} | |
if (!valid__isValid(config)) { | |
config._d = null; | |
} | |
return config; | |
} | |
function configFromInput(config) { | |
var input = config._i; | |
if (input === undefined) { | |
config._d = new Date(utils_hooks__hooks.now()); | |
} else if (isDate(input)) { | |
config._d = new Date(input.valueOf()); | |
} else if (typeof input === 'string') { | |
configFromString(config); | |
} else if (isArray(input)) { | |
config._a = map(input.slice(0), function (obj) { | |
return parseInt(obj, 10); | |
}); | |
configFromArray(config); | |
} else if (typeof(input) === 'object') { | |
configFromObject(config); | |
} else if (typeof(input) === 'number') { | |
// from milliseconds | |
config._d = new Date(input); | |
} else { | |
utils_hooks__hooks.createFromInputFallback(config); | |
} | |
} | |
function createLocalOrUTC (input, format, locale, strict, isUTC) { | |
var c = {}; | |
if (typeof(locale) === 'boolean') { | |
strict = locale; | |
locale = undefined; | |
} | |
if ((isObject(input) && isObjectEmpty(input)) || | |
(isArray(input) && input.length === 0)) { | |
input = undefined; | |
} | |
// object construction must be done this way. | |
// https://github.com/moment/moment/issues/1423 | |
c._isAMomentObject = true; | |
c._useUTC = c._isUTC = isUTC; | |
c._l = locale; | |
c._i = input; | |
c._f = format; | |
c._strict = strict; | |
return createFromConfig(c); | |
} | |
function local__createLocal (input, format, locale, strict) { | |
return createLocalOrUTC(input, format, locale, strict, false); | |
} | |
var prototypeMin = deprecate( | |
'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', | |
function () { | |
var other = local__createLocal.apply(null, arguments); | |
if (this.isValid() && other.isValid()) { | |
return other < this ? this : other; | |
} else { | |
return valid__createInvalid(); | |
} | |
} | |
); | |
var prototypeMax = deprecate( | |
'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', | |
function () { | |
var other = local__createLocal.apply(null, arguments); | |
if (this.isValid() && other.isValid()) { | |
return other > this ? this : other; | |
} else { | |
return valid__createInvalid(); | |
} | |
} | |
); | |
// Pick a moment m from moments so that m[fn](other) is true for all | |
// other. This relies on the function fn to be transitive. | |
// | |
// moments should either be an array of moment objects or an array, whose | |
// first element is an array of moment objects. | |
function pickBy(fn, moments) { | |
var res, i; | |
if (moments.length === 1 && isArray(moments[0])) { | |
moments = moments[0]; | |
} | |
if (!moments.length) { | |
return local__createLocal(); | |
} | |
res = moments[0]; | |
for (i = 1; i < moments.length; ++i) { | |
if (!moments[i].isValid() || moments[i][fn](res)) { | |
res = moments[i]; | |
} | |
} | |
return res; | |
} | |
// TODO: Use [].sort instead? | |
function min () { | |
var args = [].slice.call(arguments, 0); | |
return pickBy('isBefore', args); | |
} | |
function max () { | |
var args = [].slice.call(arguments, 0); | |
return pickBy('isAfter', args); | |
} | |
var now = function () { | |
return Date.now ? Date.now() : +(new Date()); | |
}; | |
function Duration (duration) { | |
var normalizedInput = normalizeObjectUnits(duration), | |
years = normalizedInput.year || 0, | |
quarters = normalizedInput.quarter || 0, | |
months = normalizedInput.month || 0, | |
weeks = normalizedInput.week || 0, | |
days = normalizedInput.day || 0, | |
hours = normalizedInput.hour || 0, | |
minutes = normalizedInput.minute || 0, | |
seconds = normalizedInput.second || 0, | |
milliseconds = normalizedInput.millisecond || 0; | |
// representation for dateAddRemove | |
this._milliseconds = +milliseconds + | |
seconds * 1e3 + // 1000 | |
minutes * 6e4 + // 1000 * 60 | |
hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 | |
// Because of dateAddRemove treats 24 hours as different from a | |
// day when working around DST, we need to store them separately | |
this._days = +days + | |
weeks * 7; | |
// It is impossible translate months into days without knowing | |
// which months you are are talking about, so we have to store | |
// it separately. | |
this._months = +months + | |
quarters * 3 + | |
years * 12; | |
this._data = {}; | |
this._locale = locale_locales__getLocale(); | |
this._bubble(); | |
} | |
function isDuration (obj) { | |
return obj instanceof Duration; | |
} | |
// FORMATTING | |
function offset (token, separator) { | |
addFormatToken(token, 0, 0, function () { | |
var offset = this.utcOffset(); | |
var sign = '+'; | |
if (offset < 0) { | |
offset = -offset; | |
sign = '-'; | |
} | |
return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); | |
}); | |
} | |
offset('Z', ':'); | |
offset('ZZ', ''); | |
// PARSING | |
addRegexToken('Z', matchShortOffset); | |
addRegexToken('ZZ', matchShortOffset); | |
addParseToken(['Z', 'ZZ'], function (input, array, config) { | |
config._useUTC = true; | |
config._tzm = offsetFromString(matchShortOffset, input); | |
}); | |
// HELPERS | |
// timezone chunker | |
// '+10:00' > ['10', '00'] | |
// '-1530' > ['-15', '30'] | |
var chunkOffset = /([\+\-]|\d\d)/gi; | |
function offsetFromString(matcher, string) { | |
var matches = ((string || '').match(matcher) || []); | |
var chunk = matches[matches.length - 1] || []; | |
var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; | |
var minutes = +(parts[1] * 60) + toInt(parts[2]); | |
return parts[0] === '+' ? minutes : -minutes; | |
} | |
// Return a moment from input, that is local/utc/zone equivalent to model. | |
function cloneWithOffset(input, model) { | |
var res, diff; | |
if (model._isUTC) { | |
res = model.clone(); | |
diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf(); | |
// Use low-level api, because this fn is low-level api. | |
res._d.setTime(res._d.valueOf() + diff); | |
utils_hooks__hooks.updateOffset(res, false); | |
return res; | |
} else { | |
return local__createLocal(input).local(); | |
} | |
} | |
function getDateOffset (m) { | |
// On Firefox.24 Date#getTimezoneOffset returns a floating point. | |
// https://github.com/moment/moment/pull/1871 | |
return -Math.round(m._d.getTimezoneOffset() / 15) * 15; | |
} | |
// HOOKS | |
// This function will be called whenever a moment is mutated. | |
// It is intended to keep the offset in sync with the timezone. | |
utils_hooks__hooks.updateOffset = function () {}; | |
// MOMENTS | |
// keepLocalTime = true means only change the timezone, without | |
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> | |
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset | |
// +0200, so we adjust the time as needed, to be valid. | |
// | |
// Keeping the time actually adds/subtracts (one hour) | |
// from the actual represented time. That is why we call updateOffset | |
// a second time. In case it wants us to change the offset again | |
// _changeInProgress == true case, then we have to adjust, because | |
// there is no such time in the given timezone. | |
function getSetOffset (input, keepLocalTime) { | |
var offset = this._offset || 0, | |
localAdjust; | |
if (!this.isValid()) { | |
return input != null ? this : NaN; | |
} | |
if (input != null) { | |
if (typeof input === 'string') { | |
input = offsetFromString(matchShortOffset, input); | |
} else if (Math.abs(input) < 16) { | |
input = input * 60; | |
} | |
if (!this._isUTC && keepLocalTime) { | |
localAdjust = getDateOffset(this); | |
} | |
this._offset = input; | |
this._isUTC = true; | |
if (localAdjust != null) { | |
this.add(localAdjust, 'm'); | |
} | |
if (offset !== input) { | |
if (!keepLocalTime || this._changeInProgress) { | |
add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); | |
} else if (!this._changeInProgress) { | |
this._changeInProgress = true; | |
utils_hooks__hooks.updateOffset(this, true); | |
this._changeInProgress = null; | |
} | |
} | |
return this; | |
} else { | |
return this._isUTC ? offset : getDateOffset(this); | |
} | |
} | |
function getSetZone (input, keepLocalTime) { | |
if (input != null) { | |
if (typeof input !== 'string') { | |
input = -input; | |
} | |
this.utcOffset(input, keepLocalTime); | |
return this; | |
} else { | |
return -this.utcOffset(); | |
} | |
} | |
function setOffsetToUTC (keepLocalTime) { | |
return this.utcOffset(0, keepLocalTime); | |
} | |
function setOffsetToLocal (keepLocalTime) { | |
if (this._isUTC) { | |
this.utcOffset(0, keepLocalTime); | |
this._isUTC = false; | |
if (keepLocalTime) { | |
this.subtract(getDateOffset(this), 'm'); | |
} | |
} | |
return this; | |
} | |
function setOffsetToParsedOffset () { | |
if (this._tzm) { | |
this.utcOffset(this._tzm); | |
} else if (typeof this._i === 'string') { | |
this.utcOffset(offsetFromString(matchOffset, this._i)); | |
} | |
return this; | |
} | |
function hasAlignedHourOffset (input) { | |
if (!this.isValid()) { | |
return false; | |
} | |
input = input ? local__createLocal(input).utcOffset() : 0; | |
return (this.utcOffset() - input) % 60 === 0; | |
} | |
function isDaylightSavingTime () { | |
return ( | |
this.utcOffset() > this.clone().month(0).utcOffset() || | |
this.utcOffset() > this.clone().month(5).utcOffset() | |
); | |
} | |
function isDaylightSavingTimeShifted () { | |
if (!isUndefined(this._isDSTShifted)) { | |
return this._isDSTShifted; | |
} | |
var c = {}; | |
copyConfig(c, this); | |
c = prepareConfig(c); | |
if (c._a) { | |
var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a); | |
this._isDSTShifted = this.isValid() && | |
compareArrays(c._a, other.toArray()) > 0; | |
} else { | |
this._isDSTShifted = false; | |
} | |
return this._isDSTShifted; | |
} | |
function isLocal () { | |
return this.isValid() ? !this._isUTC : false; | |
} | |
function isUtcOffset () { | |
return this.isValid() ? this._isUTC : false; | |
} | |
function isUtc () { | |
return this.isValid() ? this._isUTC && this._offset === 0 : false; | |
} | |
// ASP.NET json date format regex | |
var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/; | |
// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html | |
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere | |
// and further modified to allow for strings containing both week and day | |
var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; | |
function create__createDuration (input, key) { | |
var duration = input, | |
// matching against regexp is expensive, do it on demand | |
match = null, | |
sign, | |
ret, | |
diffRes; | |
if (isDuration(input)) { | |
duration = { | |
ms : input._milliseconds, | |
d : input._days, | |
M : input._months | |
}; | |
} else if (typeof input === 'number') { | |
duration = {}; | |
if (key) { | |
duration[key] = input; | |
} else { | |
duration.milliseconds = input; | |
} | |
} else if (!!(match = aspNetRegex.exec(input))) { | |
sign = (match[1] === '-') ? -1 : 1; | |
duration = { | |
y : 0, | |
d : toInt(match[DATE]) * sign, | |
h : toInt(match[HOUR]) * sign, | |
m : toInt(match[MINUTE]) * sign, | |
s : toInt(match[SECOND]) * sign, | |
ms : toInt(match[MILLISECOND]) * sign | |
}; | |
} else if (!!(match = isoRegex.exec(input))) { | |
sign = (match[1] === '-') ? -1 : 1; | |
duration = { | |
y : parseIso(match[2], sign), | |
M : parseIso(match[3], sign), | |
w : parseIso(match[4], sign), | |
d : parseIso(match[5], sign), | |
h : parseIso(match[6], sign), | |
m : parseIso(match[7], sign), | |
s : parseIso(match[8], sign) | |
}; | |
} else if (duration == null) {// checks for null or undefined | |
duration = {}; | |
} else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { | |
diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); | |
duration = {}; | |
duration.ms = diffRes.milliseconds; | |
duration.M = diffRes.months; | |
} | |
ret = new Duration(duration); | |
if (isDuration(input) && hasOwnProp(input, '_locale')) { | |
ret._locale = input._locale; | |
} | |
return ret; | |
} | |
create__createDuration.fn = Duration.prototype; | |
function parseIso (inp, sign) { | |
// We'd normally use ~~inp for this, but unfortunately it also | |
// converts floats to ints. | |
// inp may be undefined, so careful calling replace on it. | |
var res = inp && parseFloat(inp.replace(',', '.')); | |
// apply sign while we're at it | |
return (isNaN(res) ? 0 : res) * sign; | |
} | |
function positiveMomentsDifference(base, other) { | |
var res = {milliseconds: 0, months: 0}; | |
res.months = other.month() - base.month() + | |
(other.year() - base.year()) * 12; | |
if (base.clone().add(res.months, 'M').isAfter(other)) { | |
--res.months; | |
} | |
res.milliseconds = +other - +(base.clone().add(res.months, 'M')); | |
return res; | |
} | |
function momentsDifference(base, other) { | |
var res; | |
if (!(base.isValid() && other.isValid())) { | |
return {milliseconds: 0, months: 0}; | |
} | |
other = cloneWithOffset(other, base); | |
if (base.isBefore(other)) { | |
res = positiveMomentsDifference(base, other); | |
} else { | |
res = positiveMomentsDifference(other, base); | |
res.milliseconds = -res.milliseconds; | |
res.months = -res.months; | |
} | |
return res; | |
} | |
function absRound (number) { | |
if (number < 0) { | |
return Math.round(-1 * number) * -1; | |
} else { | |
return Math.round(number); | |
} | |
} | |
// TODO: remove 'name' arg after deprecation is removed | |
function createAdder(direction, name) { | |
return function (val, period) { | |
var dur, tmp; | |
//invert the arguments, but complain about it | |
if (period !== null && !isNaN(+period)) { | |
deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + | |
'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); | |
tmp = val; val = period; period = tmp; | |
} | |
val = typeof val === 'string' ? +val : val; | |
dur = create__createDuration(val, period); | |
add_subtract__addSubtract(this, dur, direction); | |
return this; | |
}; | |
} | |
function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { | |
var milliseconds = duration._milliseconds, | |
days = absRound(duration._days), | |
months = absRound(duration._months); | |
if (!mom.isValid()) { | |
// No op | |
return; | |
} | |
updateOffset = updateOffset == null ? true : updateOffset; | |
if (milliseconds) { | |
mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); | |
} | |
if (days) { | |
get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); | |
} | |
if (months) { | |
setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); | |
} | |
if (updateOffset) { | |
utils_hooks__hooks.updateOffset(mom, days || months); | |
} | |
} | |
var add_subtract__add = createAdder(1, 'add'); | |
var add_subtract__subtract = createAdder(-1, 'subtract'); | |
function getCalendarFormat(myMoment, now) { | |
var diff = myMoment.diff(now, 'days', true); | |
return diff < -6 ? 'sameElse' : | |
diff < -1 ? 'lastWeek' : | |
diff < 0 ? 'lastDay' : | |
diff < 1 ? 'sameDay' : | |
diff < 2 ? 'nextDay' : | |
diff < 7 ? 'nextWeek' : 'sameElse'; | |
} | |
function moment_calendar__calendar (time, formats) { | |
// We want to compare the start of today, vs this. | |
// Getting start-of-today depends on whether we're local/utc/offset or not. | |
var now = time || local__createLocal(), | |
sod = cloneWithOffset(now, this).startOf('day'), | |
format = utils_hooks__hooks.calendarFormat(this, sod) || 'sameElse'; | |
var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); | |
return this.format(output || this.localeData().calendar(format, this, local__createLocal(now))); | |
} | |
function clone () { | |
return new Moment(this); | |
} | |
function isAfter (input, units) { | |
var localInput = isMoment(input) ? input : local__createLocal(input); | |
if (!(this.isValid() && localInput.isValid())) { | |
return false; | |
} | |
units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); | |
if (units === 'millisecond') { | |
return this.valueOf() > localInput.valueOf(); | |
} else { | |
return localInput.valueOf() < this.clone().startOf(units).valueOf(); | |
} | |
} | |
function isBefore (input, units) { | |
var localInput = isMoment(input) ? input : local__createLocal(input); | |
if (!(this.isValid() && localInput.isValid())) { | |
return false; | |
} | |
units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); | |
if (units === 'millisecond') { | |
return this.valueOf() < localInput.valueOf(); | |
} else { | |
return this.clone().endOf(units).valueOf() < localInput.valueOf(); | |
} | |
} | |
function isBetween (from, to, units, inclusivity) { | |
inclusivity = inclusivity || '()'; | |
return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && | |
(inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); | |
} | |
function isSame (input, units) { | |
var localInput = isMoment(input) ? input : local__createLocal(input), | |
inputMs; | |
if (!(this.isValid() && localInput.isValid())) { | |
return false; | |
} | |
units = normalizeUnits(units || 'millisecond'); | |
if (units === 'millisecond') { | |
return this.valueOf() === localInput.valueOf(); | |
} else { | |
inputMs = localInput.valueOf(); | |
return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); | |
} | |
} | |
function isSameOrAfter (input, units) { | |
return this.isSame(input, units) || this.isAfter(input,units); | |
} | |
function isSameOrBefore (input, units) { | |
return this.isSame(input, units) || this.isBefore(input,units); | |
} | |
function diff (input, units, asFloat) { | |
var that, | |
zoneDelta, | |
delta, output; | |
if (!this.isValid()) { | |
return NaN; | |
} | |
that = cloneWithOffset(input, this); | |
if (!that.isValid()) { | |
return NaN; | |
} | |
zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; | |
units = normalizeUnits(units); | |
if (units === 'year' || units === 'month' || units === 'quarter') { | |
output = monthDiff(this, that); | |
if (units === 'quarter') { | |
output = output / 3; | |
} else if (units === 'year') { | |
output = output / 12; | |
} | |
} else { | |
delta = this - that; | |
output = units === 'second' ? delta / 1e3 : // 1000 | |
units === 'minute' ? delta / 6e4 : // 1000 * 60 | |
units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 | |
units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst | |
units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst | |
delta; | |
} | |
return asFloat ? output : absFloor(output); | |
} | |
function monthDiff (a, b) { | |
// difference in months | |
var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), | |
// b is in (anchor - 1 month, anchor + 1 month) | |
anchor = a.clone().add(wholeMonthDiff, 'months'), | |
anchor2, adjust; | |
if (b - anchor < 0) { | |
anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); | |
// linear across the month | |
adjust = (b - anchor) / (anchor - anchor2); | |
} else { | |
anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); | |
// linear across the month | |
adjust = (b - anchor) / (anchor2 - anchor); | |
} | |
//check for negative zero, return zero if negative zero | |
return -(wholeMonthDiff + adjust) || 0; | |
} | |
utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; | |
utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; | |
function toString () { | |
return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); | |
} | |
function moment_format__toISOString () { | |
var m = this.clone().utc(); | |
if (0 < m.year() && m.year() <= 9999) { | |
if (isFunction(Date.prototype.toISOString)) { | |
// native implementation is ~50x faster, use it when we can | |
return this.toDate().toISOString(); | |
} else { | |
return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); | |
} | |
} else { | |
return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); | |
} | |
} | |
function format (inputString) { | |
if (!inputString) { | |
inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat; | |
} | |
var output = formatMoment(this, inputString); | |
return this.localeData().postformat(output); | |
} | |
function from (time, withoutSuffix) { | |
if (this.isValid() && | |
((isMoment(time) && time.isValid()) || | |
local__createLocal(time).isValid())) { | |
return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); | |
} else { | |
return this.localeData().invalidDate(); | |
} | |
} | |
function fromNow (withoutSuffix) { | |
return this.from(local__createLocal(), withoutSuffix); | |
} | |
function to (time, withoutSuffix) { | |
if (this.isValid() && | |
((isMoment(time) && time.isValid()) || | |
local__createLocal(time).isValid())) { | |
return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); | |
} else { | |
return this.localeData().invalidDate(); | |
} | |
} | |
function toNow (withoutSuffix) { | |
return this.to(local__createLocal(), withoutSuffix); | |
} | |
// If passed a locale key, it will set the locale for this | |
// instance. Otherwise, it will return the locale configuration | |
// variables for this instance. | |
function locale (key) { | |
var newLocaleData; | |
if (key === undefined) { | |
return this._locale._abbr; | |
} else { | |
newLocaleData = locale_locales__getLocale(key); | |
if (newLocaleData != null) { | |
this._locale = newLocaleData; | |
} | |
return this; | |
} | |
} | |
var lang = deprecate( | |
'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', | |
function (key) { | |
if (key === undefined) { | |
return this.localeData(); | |
} else { | |
return this.locale(key); | |
} | |
} | |
); | |
function localeData () { | |
return this._locale; | |
} | |
function startOf (units) { | |
units = normalizeUnits(units); | |
// the following switch intentionally omits break keywords | |
// to utilize falling through the cases. | |
switch (units) { | |
case 'year': | |
this.month(0); | |
/* falls through */ | |
case 'quarter': | |
case 'month': | |
this.date(1); | |
/* falls through */ | |
case 'week': | |
case 'isoWeek': | |
case 'day': | |
case 'date': | |
this.hours(0); | |
/* falls through */ | |
case 'hour': | |
this.minutes(0); | |
/* falls through */ | |
case 'minute': | |
this.seconds(0); | |
/* falls through */ | |
case 'second': | |
this.milliseconds(0); | |
} | |
// weeks are a special case | |
if (units === 'week') { | |
this.weekday(0); | |
} | |
if (units === 'isoWeek') { | |
this.isoWeekday(1); | |
} | |
// quarters are also special | |
if (units === 'quarter') { | |
this.month(Math.floor(this.month() / 3) * 3); | |
} | |
return this; | |
} | |
function endOf (units) { | |
units = normalizeUnits(units); | |
if (units === undefined || units === 'millisecond') { | |
return this; | |
} | |
// 'date' is an alias for 'day', so it should be considered as such. | |
if (units === 'date') { | |
units = 'day'; | |
} | |
return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); | |
} | |
function to_type__valueOf () { | |
return this._d.valueOf() - ((this._offset || 0) * 60000); | |
} | |
function unix () { | |
return Math.floor(this.valueOf() / 1000); | |
} | |
function toDate () { | |
return new Date(this.valueOf()); | |
} | |
function toArray () { | |
var m = this; | |
return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; | |
} | |
function toObject () { | |
var m = this; | |
return { | |
years: m.year(), | |
months: m.month(), | |
date: m.date(), | |
hours: m.hours(), | |
minutes: m.minutes(), | |
seconds: m.seconds(), | |
milliseconds: m.milliseconds() | |
}; | |
} | |
function toJSON () { | |
// new Date(NaN).toJSON() === null | |
return this.isValid() ? this.toISOString() : null; | |
} | |
function moment_valid__isValid () { | |
return valid__isValid(this); | |
} | |
function parsingFlags () { | |
return extend({}, getParsingFlags(this)); | |
} | |
function invalidAt () { | |
return getParsingFlags(this).overflow; | |
} | |
function creationData() { | |
return { | |
input: this._i, | |
format: this._f, | |
locale: this._locale, | |
isUTC: this._isUTC, | |
strict: this._strict | |
}; | |
} | |
// FORMATTING | |
addFormatToken(0, ['gg', 2], 0, function () { | |
return this.weekYear() % 100; | |
}); | |
addFormatToken(0, ['GG', 2], 0, function () { | |
return this.isoWeekYear() % 100; | |
}); | |
function addWeekYearFormatToken (token, getter) { | |
addFormatToken(0, [token, token.length], 0, getter); | |
} | |
addWeekYearFormatToken('gggg', 'weekYear'); | |
addWeekYearFormatToken('ggggg', 'weekYear'); | |
addWeekYearFormatToken('GGGG', 'isoWeekYear'); | |
addWeekYearFormatToken('GGGGG', 'isoWeekYear'); | |
// ALIASES | |
addUnitAlias('weekYear', 'gg'); | |
addUnitAlias('isoWeekYear', 'GG'); | |
// PRIORITY | |
addUnitPriority('weekYear', 1); | |
addUnitPriority('isoWeekYear', 1); | |
// PARSING | |
addRegexToken('G', matchSigned); | |
addRegexToken('g', matchSigned); | |
addRegexToken('GG', match1to2, match2); | |
addRegexToken('gg', match1to2, match2); | |
addRegexToken('GGGG', match1to4, match4); | |
addRegexToken('gggg', match1to4, match4); | |
addRegexToken('GGGGG', match1to6, match6); | |
addRegexToken('ggggg', match1to6, match6); | |
addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { | |
week[token.substr(0, 2)] = toInt(input); | |
}); | |
addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { | |
week[token] = utils_hooks__hooks.parseTwoDigitYear(input); | |
}); | |
// MOMENTS | |
function getSetWeekYear (input) { | |
return getSetWeekYearHelper.call(this, | |
input, | |
this.week(), | |
this.weekday(), | |
this.localeData()._week.dow, | |
this.localeData()._week.doy); | |
} | |
function getSetISOWeekYear (input) { | |
return getSetWeekYearHelper.call(this, | |
input, this.isoWeek(), this.isoWeekday(), 1, 4); | |
} | |
function getISOWeeksInYear () { | |
return weeksInYear(this.year(), 1, 4); | |
} | |
function getWeeksInYear () { | |
var weekInfo = this.localeData()._week; | |
return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); | |
} | |
function getSetWeekYearHelper(input, week, weekday, dow, doy) { | |
var weeksTarget; | |
if (input == null) { | |
return weekOfYear(this, dow, doy).year; | |
} else { | |
weeksTarget = weeksInYear(input, dow, doy); | |
if (week > weeksTarget) { | |
week = weeksTarget; | |
} | |
return setWeekAll.call(this, input, week, weekday, dow, doy); | |
} | |
} | |
function setWeekAll(weekYear, week, weekday, dow, doy) { | |
var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), | |
date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); | |
this.year(date.getUTCFullYear()); | |
this.month(date.getUTCMonth()); | |
this.date(date.getUTCDate()); | |
return this; | |
} | |
// FORMATTING | |
addFormatToken('Q', 0, 'Qo', 'quarter'); | |
// ALIASES | |
addUnitAlias('quarter', 'Q'); | |
// PRIORITY | |
addUnitPriority('quarter', 7); | |
// PARSING | |
addRegexToken('Q', match1); | |
addParseToken('Q', function (input, array) { | |
array[MONTH] = (toInt(input) - 1) * 3; | |
}); | |
// MOMENTS | |
function getSetQuarter (input) { | |
return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); | |
} | |
// FORMATTING | |
addFormatToken('D', ['DD', 2], 'Do', 'date'); | |
// ALIASES | |
addUnitAlias('date', 'D'); | |
// PRIOROITY | |
addUnitPriority('date', 9); | |
// PARSING | |
addRegexToken('D', match1to2); | |
addRegexToken('DD', match1to2, match2); | |
addRegexToken('Do', function (isStrict, locale) { | |
return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; | |
}); | |
addParseToken(['D', 'DD'], DATE); | |
addParseToken('Do', function (input, array) { | |
array[DATE] = toInt(input.match(match1to2)[0], 10); | |
}); | |
// MOMENTS | |
var getSetDayOfMonth = makeGetSet('Date', true); | |
// FORMATTING | |
addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); | |
// ALIASES | |
addUnitAlias('dayOfYear', 'DDD'); | |
// PRIORITY | |
addUnitPriority('dayOfYear', 4); | |
// PARSING | |
addRegexToken('DDD', match1to3); | |
addRegexToken('DDDD', match3); | |
addParseToken(['DDD', 'DDDD'], function (input, array, config) { | |
config._dayOfYear = toInt(input); | |
}); | |
// HELPERS | |
// MOMENTS | |
function getSetDayOfYear (input) { | |
var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; | |
return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); | |
} | |
// FORMATTING | |
addFormatToken('m', ['mm', 2], 0, 'minute'); | |
// ALIASES | |
addUnitAlias('minute', 'm'); | |
// PRIORITY | |
addUnitPriority('minute', 14); | |
// PARSING | |
addRegexToken('m', match1to2); | |
addRegexToken('mm', match1to2, match2); | |
addParseToken(['m', 'mm'], MINUTE); | |
// MOMENTS | |
var getSetMinute = makeGetSet('Minutes', false); | |
// FORMATTING | |
addFormatToken('s', ['ss', 2], 0, 'second'); | |
// ALIASES | |
addUnitAlias('second', 's'); | |
// PRIORITY | |
addUnitPriority('second', 15); | |
// PARSING | |
addRegexToken('s', match1to2); | |
addRegexToken('ss', match1to2, match2); | |
addParseToken(['s', 'ss'], SECOND); | |
// MOMENTS | |
var getSetSecond = makeGetSet('Seconds', false); | |
// FORMATTING | |
addFormatToken('S', 0, 0, function () { | |
return ~~(this.millisecond() / 100); | |
}); | |
addFormatToken(0, ['SS', 2], 0, function () { | |
return ~~(this.millisecond() / 10); | |
}); | |
addFormatToken(0, ['SSS', 3], 0, 'millisecond'); | |
addFormatToken(0, ['SSSS', 4], 0, function () { | |
return this.millisecond() * 10; | |
}); | |
addFormatToken(0, ['SSSSS', 5], 0, function () { | |
return this.millisecond() * 100; | |
}); | |
addFormatToken(0, ['SSSSSS', 6], 0, function () { | |
return this.millisecond() * 1000; | |
}); | |
addFormatToken(0, ['SSSSSSS', 7], 0, function () { | |
return this.millisecond() * 10000; | |
}); | |
addFormatToken(0, ['SSSSSSSS', 8], 0, function () { | |
return this.millisecond() * 100000; | |
}); | |
addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { | |
return this.millisecond() * 1000000; | |
}); | |
// ALIASES | |
addUnitAlias('millisecond', 'ms'); | |
// PRIORITY | |
addUnitPriority('millisecond', 16); | |
// PARSING | |
addRegexToken('S', match1to3, match1); | |
addRegexToken('SS', match1to3, match2); | |
addRegexToken('SSS', match1to3, match3); | |
var token; | |
for (token = 'SSSS'; token.length <= 9; token += 'S') { | |
addRegexToken(token, matchUnsigned); | |
} | |
function parseMs(input, array) { | |
array[MILLISECOND] = toInt(('0.' + input) * 1000); | |
} | |
for (token = 'S'; token.length <= 9; token += 'S') { | |
addParseToken(token, parseMs); | |
} | |
// MOMENTS | |
var getSetMillisecond = makeGetSet('Milliseconds', false); | |
// FORMATTING | |
addFormatToken('z', 0, 0, 'zoneAbbr'); | |
addFormatToken('zz', 0, 0, 'zoneName'); | |
// MOMENTS | |
function getZoneAbbr () { | |
return this._isUTC ? 'UTC' : ''; | |
} | |
function getZoneName () { | |
return this._isUTC ? 'Coordinated Universal Time' : ''; | |
} | |
var momentPrototype__proto = Moment.prototype; | |
momentPrototype__proto.add = add_subtract__add; | |
momentPrototype__proto.calendar = moment_calendar__calendar; | |
momentPrototype__proto.clone = clone; | |
momentPrototype__proto.diff = diff; | |
momentPrototype__proto.endOf = endOf; | |
momentPrototype__proto.format = format; | |
momentPrototype__proto.from = from; | |
momentPrototype__proto.fromNow = fromNow; | |
momentPrototype__proto.to = to; | |
momentPrototype__proto.toNow = toNow; | |
momentPrototype__proto.get = stringGet; | |
momentPrototype__proto.invalidAt = invalidAt; | |
momentPrototype__proto.isAfter = isAfter; | |
momentPrototype__proto.isBefore = isBefore; | |
momentPrototype__proto.isBetween = isBetween; | |
momentPrototype__proto.isSame = isSame; | |
momentPrototype__proto.isSameOrAfter = isSameOrAfter; | |
momentPrototype__proto.isSameOrBefore = isSameOrBefore; | |
momentPrototype__proto.isValid = moment_valid__isValid; | |
momentPrototype__proto.lang = lang; | |
momentPrototype__proto.locale = locale; | |
momentPrototype__proto.localeData = localeData; | |
momentPrototype__proto.max = prototypeMax; | |
momentPrototype__proto.min = prototypeMin; | |
momentPrototype__proto.parsingFlags = parsingFlags; | |
momentPrototype__proto.set = stringSet; | |
momentPrototype__proto.startOf = startOf; | |
momentPrototype__proto.subtract = add_subtract__subtract; | |
momentPrototype__proto.toArray = toArray; | |
momentPrototype__proto.toObject = toObject; | |
momentPrototype__proto.toDate = toDate; | |
momentPrototype__proto.toISOString = moment_format__toISOString; | |
momentPrototype__proto.toJSON = toJSON; | |
momentPrototype__proto.toString = toString; | |
momentPrototype__proto.unix = unix; | |
momentPrototype__proto.valueOf = to_type__valueOf; | |
momentPrototype__proto.creationData = creationData; | |
// Year | |
momentPrototype__proto.year = getSetYear; | |
momentPrototype__proto.isLeapYear = getIsLeapYear; | |
// Week Year | |
momentPrototype__proto.weekYear = getSetWeekYear; | |
momentPrototype__proto.isoWeekYear = getSetISOWeekYear; | |
// Quarter | |
momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; | |
// Month | |
momentPrototype__proto.month = getSetMonth; | |
momentPrototype__proto.daysInMonth = getDaysInMonth; | |
// Week | |
momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; | |
momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; | |
momentPrototype__proto.weeksInYear = getWeeksInYear; | |
momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; | |
// Day | |
momentPrototype__proto.date = getSetDayOfMonth; | |
momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; | |
momentPrototype__proto.weekday = getSetLocaleDayOfWeek; | |
momentPrototype__proto.isoWeekday = getSetISODayOfWeek; | |
momentPrototype__proto.dayOfYear = getSetDayOfYear; | |
// Hour | |
momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; | |
// Minute | |
momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; | |
// Second | |
momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; | |
// Millisecond | |
momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; | |
// Offset | |
momentPrototype__proto.utcOffset = getSetOffset; | |
momentPrototype__proto.utc = setOffsetToUTC; | |
momentPrototype__proto.local = setOffsetToLocal; | |
momentPrototype__proto.parseZone = setOffsetToParsedOffset; | |
momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; | |
momentPrototype__proto.isDST = isDaylightSavingTime; | |
momentPrototype__proto.isLocal = isLocal; | |
momentPrototype__proto.isUtcOffset = isUtcOffset; | |
momentPrototype__proto.isUtc = isUtc; | |
momentPrototype__proto.isUTC = isUtc; | |
// Timezone | |
momentPrototype__proto.zoneAbbr = getZoneAbbr; | |
momentPrototype__proto.zoneName = getZoneName; | |
// Deprecations | |
momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); | |
momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); | |
momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); | |
momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); | |
momentPrototype__proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); | |
var momentPrototype = momentPrototype__proto; | |
function moment__createUnix (input) { | |
return local__createLocal(input * 1000); | |
} | |
function moment__createInZone () { | |
return local__createLocal.apply(null, arguments).parseZone(); | |
} | |
function preParsePostFormat (string) { | |
return string; | |
} | |
var prototype__proto = Locale.prototype; | |
prototype__proto.calendar = locale_calendar__calendar; | |
prototype__proto.longDateFormat = longDateFormat; | |
prototype__proto.invalidDate = invalidDate; | |
prototype__proto.ordinal = ordinal; | |
prototype__proto.preparse = preParsePostFormat; | |
prototype__proto.postformat = preParsePostFormat; | |
prototype__proto.relativeTime = relative__relativeTime; | |
prototype__proto.pastFuture = pastFuture; | |
prototype__proto.set = locale_set__set; | |
// Month | |
prototype__proto.months = localeMonths; | |
prototype__proto.monthsShort = localeMonthsShort; | |
prototype__proto.monthsParse = localeMonthsParse; | |
prototype__proto.monthsRegex = monthsRegex; | |
prototype__proto.monthsShortRegex = monthsShortRegex; | |
// Week | |
prototype__proto.week = localeWeek; | |
prototype__proto.firstDayOfYear = localeFirstDayOfYear; | |
prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; | |
// Day of Week | |
prototype__proto.weekdays = localeWeekdays; | |
prototype__proto.weekdaysMin = localeWeekdaysMin; | |
prototype__proto.weekdaysShort = localeWeekdaysShort; | |
prototype__proto.weekdaysParse = localeWeekdaysParse; | |
prototype__proto.weekdaysRegex = weekdaysRegex; | |
prototype__proto.weekdaysShortRegex = weekdaysShortRegex; | |
prototype__proto.weekdaysMinRegex = weekdaysMinRegex; | |
// Hours | |
prototype__proto.isPM = localeIsPM; | |
prototype__proto.meridiem = localeMeridiem; | |
function lists__get (format, index, field, setter) { | |
var locale = locale_locales__getLocale(); | |
var utc = create_utc__createUTC().set(setter, index); | |
return locale[field](utc, format); | |
} | |
function listMonthsImpl (format, index, field) { | |
if (typeof format === 'number') { | |
index = format; | |
format = undefined; | |
} | |
format = format || ''; | |
if (index != null) { | |
return lists__get(format, index, field, 'month'); | |
} | |
var i; | |
var out = []; | |
for (i = 0; i < 12; i++) { | |
out[i] = lists__get(format, i, field, 'month'); | |
} | |
return out; | |
} | |
// () | |
// (5) | |
// (fmt, 5) | |
// (fmt) | |
// (true) | |
// (true, 5) | |
// (true, fmt, 5) | |
// (true, fmt) | |
function listWeekdaysImpl (localeSorted, format, index, field) { | |
if (typeof localeSorted === 'boolean') { | |
if (typeof format === 'number') { | |
index = format; | |
format = undefined; | |
} | |
format = format || ''; | |
} else { | |
format = localeSorted; | |
index = format; | |
localeSorted = false; | |
if (typeof format === 'number') { | |
index = format; | |
format = undefined; | |
} | |
format = format || ''; | |
} | |
var locale = locale_locales__getLocale(), | |
shift = localeSorted ? locale._week.dow : 0; | |
if (index != null) { | |
return lists__get(format, (index + shift) % 7, field, 'day'); | |
} | |
var i; | |
var out = []; | |
for (i = 0; i < 7; i++) { | |
out[i] = lists__get(format, (i + shift) % 7, field, 'day'); | |
} | |
return out; | |
} | |
function lists__listMonths (format, index) { | |
return listMonthsImpl(format, index, 'months'); | |
} | |
function lists__listMonthsShort (format, index) { | |
return listMonthsImpl(format, index, 'monthsShort'); | |
} | |
function lists__listWeekdays (localeSorted, format, index) { | |
return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); | |
} | |
function lists__listWeekdaysShort (localeSorted, format, index) { | |
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); | |
} | |
function lists__listWeekdaysMin (localeSorted, format, index) { | |
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); | |
} | |
locale_locales__getSetGlobalLocale('en', { | |
ordinalParse: /\d{1,2}(th|st|nd|rd)/, | |
ordinal : function (number) { | |
var b = number % 10, | |
output = (toInt(number % 100 / 10) === 1) ? 'th' : | |
(b === 1) ? 'st' : | |
(b === 2) ? 'nd' : | |
(b === 3) ? 'rd' : 'th'; | |
return number + output; | |
} | |
}); | |
// Side effect imports | |
utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); | |
utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); | |
var mathAbs = Math.abs; | |
function duration_abs__abs () { | |
var data = this._data; | |
this._milliseconds = mathAbs(this._milliseconds); | |
this._days = mathAbs(this._days); | |
this._months = mathAbs(this._months); | |
data.milliseconds = mathAbs(data.milliseconds); | |
data.seconds = mathAbs(data.seconds); | |
data.minutes = mathAbs(data.minutes); | |
data.hours = mathAbs(data.hours); | |
data.months = mathAbs(data.months); | |
data.years = mathAbs(data.years); | |
return this; | |
} | |
function duration_add_subtract__addSubtract (duration, input, value, direction) { | |
var other = create__createDuration(input, value); | |
duration._milliseconds += direction * other._milliseconds; | |
duration._days += direction * other._days; | |
duration._months += direction * other._months; | |
return duration._bubble(); | |
} | |
// supports only 2.0-style add(1, 's') or add(duration) | |
function duration_add_subtract__add (input, value) { | |
return duration_add_subtract__addSubtract(this, input, value, 1); | |
} | |
// supports only 2.0-style subtract(1, 's') or subtract(duration) | |
function duration_add_subtract__subtract (input, value) { | |
return duration_add_subtract__addSubtract(this, input, value, -1); | |
} | |
function absCeil (number) { | |
if (number < 0) { | |
return Math.floor(number); | |
} else { | |
return Math.ceil(number); | |
} | |
} | |
function bubble () { | |
var milliseconds = this._milliseconds; | |
var days = this._days; | |
var months = this._months; | |
var data = this._data; | |
var seconds, minutes, hours, years, monthsFromDays; | |
// if we have a mix of positive and negative values, bubble down first | |
// check: https://github.com/moment/moment/issues/2166 | |
if (!((milliseconds >= 0 && days >= 0 && months >= 0) || | |
(milliseconds <= 0 && days <= 0 && months <= 0))) { | |
milliseconds += absCeil(monthsToDays(months) + days) * 864e5; | |
days = 0; | |
months = 0; | |
} | |
// The following code bubbles up values, see the tests for | |
// examples of what that means. | |
data.milliseconds = milliseconds % 1000; | |
seconds = absFloor(milliseconds / 1000); | |
data.seconds = seconds % 60; | |
minutes = absFloor(seconds / 60); | |
data.minutes = minutes % 60; | |
hours = absFloor(minutes / 60); | |
data.hours = hours % 24; | |
days += absFloor(hours / 24); | |
// convert days to months | |
monthsFromDays = absFloor(daysToMonths(days)); | |
months += monthsFromDays; | |
days -= absCeil(monthsToDays(monthsFromDays)); | |
// 12 months -> 1 year | |
years = absFloor(months / 12); | |
months %= 12; | |
data.days = days; | |
data.months = months; | |
data.years = years; | |
return this; | |
} | |
function daysToMonths (days) { | |
// 400 years have 146097 days (taking into account leap year rules) | |
// 400 years have 12 months === 4800 | |
return days * 4800 / 146097; | |
} | |
function monthsToDays (months) { | |
// the reverse of daysToMonths | |
return months * 146097 / 4800; | |
} | |
function as (units) { | |
var days; | |
var months; | |
var milliseconds = this._milliseconds; | |
units = normalizeUnits(units); | |
if (units === 'month' || units === 'year') { | |
days = this._days + milliseconds / 864e5; | |
months = this._months + daysToMonths(days); | |
return units === 'month' ? months : months / 12; | |
} else { | |
// handle milliseconds separately because of floating point math errors (issue #1867) | |
days = this._days + Math.round(monthsToDays(this._months)); | |
switch (units) { | |
case 'week' : return days / 7 + milliseconds / 6048e5; | |
case 'day' : return days + milliseconds / 864e5; | |
case 'hour' : return days * 24 + milliseconds / 36e5; | |
case 'minute' : return days * 1440 + milliseconds / 6e4; | |
case 'second' : return days * 86400 + milliseconds / 1000; | |
// Math.floor prevents floating point math errors here | |
case 'millisecond': return Math.floor(days * 864e5) + milliseconds; | |
default: throw new Error('Unknown unit ' + units); | |
} | |
} | |
} | |
// TODO: Use this.as('ms')? | |
function duration_as__valueOf () { | |
return ( | |
this._milliseconds + | |
this._days * 864e5 + | |
(this._months % 12) * 2592e6 + | |
toInt(this._months / 12) * 31536e6 | |
); | |
} | |
function makeAs (alias) { | |
return function () { | |
return this.as(alias); | |
}; | |
} | |
var asMilliseconds = makeAs('ms'); | |
var asSeconds = makeAs('s'); | |
var asMinutes = makeAs('m'); | |
var asHours = makeAs('h'); | |
var asDays = makeAs('d'); | |
var asWeeks = makeAs('w'); | |
var asMonths = makeAs('M'); | |
var asYears = makeAs('y'); | |
function duration_get__get (units) { | |
units = normalizeUnits(units); | |
return this[units + 's'](); | |
} | |
function makeGetter(name) { | |
return function () { | |
return this._data[name]; | |
}; | |
} | |
var milliseconds = makeGetter('milliseconds'); | |
var seconds = makeGetter('seconds'); | |
var minutes = makeGetter('minutes'); | |
var hours = makeGetter('hours'); | |
var days = makeGetter('days'); | |
var months = makeGetter('months'); | |
var years = makeGetter('years'); | |
function weeks () { | |
return absFloor(this.days() / 7); | |
} | |
var round = Math.round; | |
var thresholds = { | |
s: 45, // seconds to minute | |
m: 45, // minutes to hour | |
h: 22, // hours to day | |
d: 26, // days to month | |
M: 11 // months to year | |
}; | |
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize | |
function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { | |
return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); | |
} | |
function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { | |
var duration = create__createDuration(posNegDuration).abs(); | |
var seconds = round(duration.as('s')); | |
var minutes = round(duration.as('m')); | |
var hours = round(duration.as('h')); | |
var days = round(duration.as('d')); | |
var months = round(duration.as('M')); | |
var years = round(duration.as('y')); | |
var a = seconds < thresholds.s && ['s', seconds] || | |
minutes <= 1 && ['m'] || | |
minutes < thresholds.m && ['mm', minutes] || | |
hours <= 1 && ['h'] || | |
hours < thresholds.h && ['hh', hours] || | |
days <= 1 && ['d'] || | |
days < thresholds.d && ['dd', days] || | |
months <= 1 && ['M'] || | |
months < thresholds.M && ['MM', months] || | |
years <= 1 && ['y'] || ['yy', years]; | |
a[2] = withoutSuffix; | |
a[3] = +posNegDuration > 0; | |
a[4] = locale; | |
return substituteTimeAgo.apply(null, a); | |
} | |
// This function allows you to set the rounding function for relative time strings | |
function duration_humanize__getSetRelativeTimeRounding (roundingFunction) { | |
if (roundingFunction === undefined) { | |
return round; | |
} | |
if (typeof(roundingFunction) === 'function') { | |
round = roundingFunction; | |
return true; | |
} | |
return false; | |
} | |
// This function allows you to set a threshold for relative time strings | |
function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { | |
if (thresholds[threshold] === undefined) { | |
return false; | |
} | |
if (limit === undefined) { | |
return thresholds[threshold]; | |
} | |
thresholds[threshold] = limit; | |
return true; | |
} | |
function humanize (withSuffix) { | |
var locale = this.localeData(); | |
var output = duration_humanize__relativeTime(this, !withSuffix, locale); | |
if (withSuffix) { | |
output = locale.pastFuture(+this, output); | |
} | |
return locale.postformat(output); | |
} | |
var iso_string__abs = Math.abs; | |
function iso_string__toISOString() { | |
// for ISO strings we do not use the normal bubbling rules: | |
// * milliseconds bubble up until they become hours | |
// * days do not bubble at all | |
// * months bubble up until they become years | |
// This is because there is no context-free conversion between hours and days | |
// (think of clock changes) | |
// and also not between days and months (28-31 days per month) | |
var seconds = iso_string__abs(this._milliseconds) / 1000; | |
var days = iso_string__abs(this._days); | |
var months = iso_string__abs(this._months); | |
var minutes, hours, years; | |
// 3600 seconds -> 60 minutes -> 1 hour | |
minutes = absFloor(seconds / 60); | |
hours = absFloor(minutes / 60); | |
seconds %= 60; | |
minutes %= 60; | |
// 12 months -> 1 year | |
years = absFloor(months / 12); | |
months %= 12; | |
// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js | |
var Y = years; | |
var M = months; | |
var D = days; | |
var h = hours; | |
var m = minutes; | |
var s = seconds; | |
var total = this.asSeconds(); | |
if (!total) { | |
// this is the same as C#'s (Noda) and python (isodate)... | |
// but not other JS (goog.date) | |
return 'P0D'; | |
} | |
return (total < 0 ? '-' : '') + | |
'P' + | |
(Y ? Y + 'Y' : '') + | |
(M ? M + 'M' : '') + | |
(D ? D + 'D' : '') + | |
((h || m || s) ? 'T' : '') + | |
(h ? h + 'H' : '') + | |
(m ? m + 'M' : '') + | |
(s ? s + 'S' : ''); | |
} | |
var duration_prototype__proto = Duration.prototype; | |
duration_prototype__proto.abs = duration_abs__abs; | |
duration_prototype__proto.add = duration_add_subtract__add; | |
duration_prototype__proto.subtract = duration_add_subtract__subtract; | |
duration_prototype__proto.as = as; | |
duration_prototype__proto.asMilliseconds = asMilliseconds; | |
duration_prototype__proto.asSeconds = asSeconds; | |
duration_prototype__proto.asMinutes = asMinutes; | |
duration_prototype__proto.asHours = asHours; | |
duration_prototype__proto.asDays = asDays; | |
duration_prototype__proto.asWeeks = asWeeks; | |
duration_prototype__proto.asMonths = asMonths; | |
duration_prototype__proto.asYears = asYears; | |
duration_prototype__proto.valueOf = duration_as__valueOf; | |
duration_prototype__proto._bubble = bubble; | |
duration_prototype__proto.get = duration_get__get; | |
duration_prototype__proto.milliseconds = milliseconds; | |
duration_prototype__proto.seconds = seconds; | |
duration_prototype__proto.minutes = minutes; | |
duration_prototype__proto.hours = hours; | |
duration_prototype__proto.days = days; | |
duration_prototype__proto.weeks = weeks; | |
duration_prototype__proto.months = months; | |
duration_prototype__proto.years = years; | |
duration_prototype__proto.humanize = humanize; | |
duration_prototype__proto.toISOString = iso_string__toISOString; | |
duration_prototype__proto.toString = iso_string__toISOString; | |
duration_prototype__proto.toJSON = iso_string__toISOString; | |
duration_prototype__proto.locale = locale; | |
duration_prototype__proto.localeData = localeData; | |
// Deprecations | |
duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); | |
duration_prototype__proto.lang = lang; | |
// Side effect imports | |
// FORMATTING | |
addFormatToken('X', 0, 0, 'unix'); | |
addFormatToken('x', 0, 0, 'valueOf'); | |
// PARSING | |
addRegexToken('x', matchSigned); | |
addRegexToken('X', matchTimestamp); | |
addParseToken('X', function (input, array, config) { | |
config._d = new Date(parseFloat(input, 10) * 1000); | |
}); | |
addParseToken('x', function (input, array, config) { | |
config._d = new Date(toInt(input)); | |
}); | |
// Side effect imports | |
utils_hooks__hooks.version = '2.14.1'; | |
setHookCallback(local__createLocal); | |
utils_hooks__hooks.fn = momentPrototype; | |
utils_hooks__hooks.min = min; | |
utils_hooks__hooks.max = max; | |
utils_hooks__hooks.now = now; | |
utils_hooks__hooks.utc = create_utc__createUTC; | |
utils_hooks__hooks.unix = moment__createUnix; | |
utils_hooks__hooks.months = lists__listMonths; | |
utils_hooks__hooks.isDate = isDate; | |
utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; | |
utils_hooks__hooks.invalid = valid__createInvalid; | |
utils_hooks__hooks.duration = create__createDuration; | |
utils_hooks__hooks.isMoment = isMoment; | |
utils_hooks__hooks.weekdays = lists__listWeekdays; | |
utils_hooks__hooks.parseZone = moment__createInZone; | |
utils_hooks__hooks.localeData = locale_locales__getLocale; | |
utils_hooks__hooks.isDuration = isDuration; | |
utils_hooks__hooks.monthsShort = lists__listMonthsShort; | |
utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; | |
utils_hooks__hooks.defineLocale = defineLocale; | |
utils_hooks__hooks.updateLocale = updateLocale; | |
utils_hooks__hooks.locales = locale_locales__listLocales; | |
utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; | |
utils_hooks__hooks.normalizeUnits = normalizeUnits; | |
utils_hooks__hooks.relativeTimeRounding = duration_humanize__getSetRelativeTimeRounding; | |
utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; | |
utils_hooks__hooks.calendarFormat = getCalendarFormat; | |
utils_hooks__hooks.prototype = momentPrototype; | |
var _moment = utils_hooks__hooks; | |
return _moment; | |
})); | |
},{}],7:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.Bar = function(context, config) { | |
config.type = 'bar'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],8:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.Bubble = function(context, config) { | |
config.type = 'bubble'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],9:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.Doughnut = function(context, config) { | |
config.type = 'doughnut'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],10:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.Line = function(context, config) { | |
config.type = 'line'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],11:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.PolarArea = function(context, config) { | |
config.type = 'polarArea'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],12:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
Chart.Radar = function(context, config) { | |
config.options = Chart.helpers.configMerge({ aspectRatio: 1 }, config.options); | |
config.type = 'radar'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],13:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var defaultConfig = { | |
hover: { | |
mode: 'single' | |
}, | |
scales: { | |
xAxes: [{ | |
type: "linear", // scatter should not use a category axis | |
position: "bottom", | |
id: "x-axis-1" // need an ID so datasets can reference the scale | |
}], | |
yAxes: [{ | |
type: "linear", | |
position: "left", | |
id: "y-axis-1" | |
}] | |
}, | |
tooltips: { | |
callbacks: { | |
title: function() { | |
// Title doesn't make sense for scatter since we format the data as a point | |
return ''; | |
}, | |
label: function(tooltipItem) { | |
return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; | |
} | |
} | |
} | |
}; | |
// Register the default config for this type | |
Chart.defaults.scatter = defaultConfig; | |
// Scatter charts use line controllers | |
Chart.controllers.scatter = Chart.controllers.line; | |
Chart.Scatter = function(context, config) { | |
config.type = 'scatter'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],14:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.bar = { | |
hover: { | |
mode: "label" | |
}, | |
scales: { | |
xAxes: [{ | |
type: "category", | |
// Specific to Bar Controller | |
categoryPercentage: 0.8, | |
barPercentage: 0.9, | |
// grid line settings | |
gridLines: { | |
offsetGridLines: true | |
} | |
}], | |
yAxes: [{ | |
type: "linear" | |
}] | |
} | |
}; | |
Chart.controllers.bar = Chart.DatasetController.extend({ | |
dataElementType: Chart.elements.Rectangle, | |
initialize: function(chart, datasetIndex) { | |
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); | |
// Use this to indicate that this is a bar dataset. | |
this.getMeta().bar = true; | |
}, | |
// Get the number of datasets that display bars. We use this to correctly calculate the bar width | |
getBarCount: function() { | |
var me = this; | |
var barCount = 0; | |
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { | |
var meta = me.chart.getDatasetMeta(datasetIndex); | |
if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) { | |
++barCount; | |
} | |
}, me); | |
return barCount; | |
}, | |
update: function(reset) { | |
var me = this; | |
helpers.each(me.getMeta().data, function(rectangle, index) { | |
me.updateElement(rectangle, index, reset); | |
}, me); | |
}, | |
updateElement: function(rectangle, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var scaleBase = yScale.getBasePixel(); | |
var rectangleElementOptions = me.chart.options.elements.rectangle; | |
var custom = rectangle.custom || {}; | |
var dataset = me.getDataset(); | |
helpers.extend(rectangle, { | |
// Utility | |
_xScale: xScale, | |
_yScale: yScale, | |
_datasetIndex: me.index, | |
_index: index, | |
// Desired view properties | |
_model: { | |
x: me.calculateBarX(index, me.index), | |
y: reset ? scaleBase : me.calculateBarY(index, me.index), | |
// Tooltip | |
label: me.chart.data.labels[index], | |
datasetLabel: dataset.label, | |
// Appearance | |
base: reset ? scaleBase : me.calculateBarBase(me.index, index), | |
width: me.calculateBarWidth(index), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), | |
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, | |
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) | |
} | |
}); | |
rectangle.pivot(); | |
}, | |
calculateBarBase: function(datasetIndex, index) { | |
var me = this; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var base = 0; | |
if (yScale.options.stacked) { | |
var chart = me.chart; | |
var datasets = chart.data.datasets; | |
var value = Number(datasets[datasetIndex].data[index]); | |
for (var i = 0; i < datasetIndex; i++) { | |
var currentDs = datasets[i]; | |
var currentDsMeta = chart.getDatasetMeta(i); | |
if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { | |
var currentVal = Number(currentDs.data[index]); | |
base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); | |
} | |
} | |
return yScale.getPixelForValue(base); | |
} | |
return yScale.getBasePixel(); | |
}, | |
getRuler: function(index) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var datasetCount = me.getBarCount(); | |
var tickWidth; | |
if (xScale.options.type === 'category') { | |
tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index); | |
} else { | |
// Average width | |
tickWidth = xScale.width / xScale.ticks.length; | |
} | |
var categoryWidth = tickWidth * xScale.options.categoryPercentage; | |
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; | |
var fullBarWidth = categoryWidth / datasetCount; | |
if (xScale.ticks.length !== me.chart.data.labels.length) { | |
var perc = xScale.ticks.length / me.chart.data.labels.length; | |
fullBarWidth = fullBarWidth * perc; | |
} | |
var barWidth = fullBarWidth * xScale.options.barPercentage; | |
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); | |
return { | |
datasetCount: datasetCount, | |
tickWidth: tickWidth, | |
categoryWidth: categoryWidth, | |
categorySpacing: categorySpacing, | |
fullBarWidth: fullBarWidth, | |
barWidth: barWidth, | |
barSpacing: barSpacing | |
}; | |
}, | |
calculateBarWidth: function(index) { | |
var xScale = this.getScaleForId(this.getMeta().xAxisID); | |
if (xScale.options.barThickness) { | |
return xScale.options.barThickness; | |
} | |
var ruler = this.getRuler(index); | |
return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; | |
}, | |
// Get bar index from the given dataset index accounting for the fact that not all bars are visible | |
getBarIndex: function(datasetIndex) { | |
var barIndex = 0; | |
var meta, j; | |
for (j = 0; j < datasetIndex; ++j) { | |
meta = this.chart.getDatasetMeta(j); | |
if (meta.bar && this.chart.isDatasetVisible(j)) { | |
++barIndex; | |
} | |
} | |
return barIndex; | |
}, | |
calculateBarX: function(index, datasetIndex) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var barIndex = me.getBarIndex(datasetIndex); | |
var ruler = me.getRuler(index); | |
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); | |
leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0; | |
if (xScale.options.stacked) { | |
return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; | |
} | |
return leftTick + | |
(ruler.barWidth / 2) + | |
ruler.categorySpacing + | |
(ruler.barWidth * barIndex) + | |
(ruler.barSpacing / 2) + | |
(ruler.barSpacing * barIndex); | |
}, | |
calculateBarY: function(index, datasetIndex) { | |
var me = this; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var value = Number(me.getDataset().data[index]); | |
if (yScale.options.stacked) { | |
var sumPos = 0, | |
sumNeg = 0; | |
for (var i = 0; i < datasetIndex; i++) { | |
var ds = me.chart.data.datasets[i]; | |
var dsMeta = me.chart.getDatasetMeta(i); | |
if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) { | |
var stackedVal = Number(ds.data[index]); | |
if (stackedVal < 0) { | |
sumNeg += stackedVal || 0; | |
} else { | |
sumPos += stackedVal || 0; | |
} | |
} | |
} | |
if (value < 0) { | |
return yScale.getPixelForValue(sumNeg + value); | |
} else { | |
return yScale.getPixelForValue(sumPos + value); | |
} | |
} | |
return yScale.getPixelForValue(value); | |
}, | |
draw: function(ease) { | |
var me = this; | |
var easingDecimal = ease || 1; | |
helpers.each(me.getMeta().data, function(rectangle, index) { | |
var d = me.getDataset().data[index]; | |
if (d !== null && d !== undefined && !isNaN(d)) { | |
rectangle.transition(easingDecimal).draw(); | |
} | |
}, me); | |
}, | |
setHoverStyle: function(rectangle) { | |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; | |
var index = rectangle._index; | |
var custom = rectangle.custom || {}; | |
var model = rectangle._model; | |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(rectangle) { | |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; | |
var index = rectangle._index; | |
var custom = rectangle.custom || {}; | |
var model = rectangle._model; | |
var rectangleElementOptions = this.chart.options.elements.rectangle; | |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); | |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); | |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); | |
} | |
}); | |
// including horizontalBar in the bar file, instead of a file of its own | |
// it extends bar (like pie extends doughnut) | |
Chart.defaults.horizontalBar = { | |
hover: { | |
mode: "label" | |
}, | |
scales: { | |
xAxes: [{ | |
type: "linear", | |
position: "bottom" | |
}], | |
yAxes: [{ | |
position: "left", | |
type: "category", | |
// Specific to Horizontal Bar Controller | |
categoryPercentage: 0.8, | |
barPercentage: 0.9, | |
// grid line settings | |
gridLines: { | |
offsetGridLines: true | |
} | |
}] | |
}, | |
elements: { | |
rectangle: { | |
borderSkipped: 'left' | |
} | |
}, | |
tooltips: { | |
callbacks: { | |
title: function(tooltipItems, data) { | |
// Pick first xLabel for now | |
var title = ''; | |
if (tooltipItems.length > 0) { | |
if (tooltipItems[0].yLabel) { | |
title = tooltipItems[0].yLabel; | |
} else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { | |
title = data.labels[tooltipItems[0].index]; | |
} | |
} | |
return title; | |
}, | |
label: function(tooltipItem, data) { | |
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; | |
return datasetLabel + ': ' + tooltipItem.xLabel; | |
} | |
} | |
} | |
}; | |
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ | |
updateElement: function(rectangle, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var scaleBase = xScale.getBasePixel(); | |
var custom = rectangle.custom || {}; | |
var dataset = me.getDataset(); | |
var rectangleElementOptions = me.chart.options.elements.rectangle; | |
helpers.extend(rectangle, { | |
// Utility | |
_xScale: xScale, | |
_yScale: yScale, | |
_datasetIndex: me.index, | |
_index: index, | |
// Desired view properties | |
_model: { | |
x: reset ? scaleBase : me.calculateBarX(index, me.index), | |
y: me.calculateBarY(index, me.index), | |
// Tooltip | |
label: me.chart.data.labels[index], | |
datasetLabel: dataset.label, | |
// Appearance | |
base: reset ? scaleBase : me.calculateBarBase(me.index, index), | |
height: me.calculateBarHeight(index), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), | |
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, | |
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) | |
}, | |
draw: function () { | |
var ctx = this._chart.ctx; | |
var vm = this._view; | |
var halfHeight = vm.height / 2, | |
topY = vm.y - halfHeight, | |
bottomY = vm.y + halfHeight, | |
right = vm.base - (vm.base - vm.x), | |
halfStroke = vm.borderWidth / 2; | |
// Canvas doesn't allow us to stroke inside the width so we can | |
// adjust the sizes to fit if we're setting a stroke on the line | |
if (vm.borderWidth) { | |
topY += halfStroke; | |
bottomY -= halfStroke; | |
right += halfStroke; | |
} | |
ctx.beginPath(); | |
ctx.fillStyle = vm.backgroundColor; | |
ctx.strokeStyle = vm.borderColor; | |
ctx.lineWidth = vm.borderWidth; | |
// Corner points, from bottom-left to bottom-right clockwise | |
// | 1 2 | | |
// | 0 3 | | |
var corners = [ | |
[vm.base, bottomY], | |
[vm.base, topY], | |
[right, topY], | |
[right, bottomY] | |
]; | |
// Find first (starting) corner with fallback to 'bottom' | |
var borders = ['bottom', 'left', 'top', 'right']; | |
var startCorner = borders.indexOf(vm.borderSkipped, 0); | |
if (startCorner === -1) | |
startCorner = 0; | |
function cornerAt(index) { | |
return corners[(startCorner + index) % 4]; | |
} | |
// Draw rectangle from 'startCorner' | |
ctx.moveTo.apply(ctx, cornerAt(0)); | |
for (var i = 1; i < 4; i++) | |
ctx.lineTo.apply(ctx, cornerAt(i)); | |
ctx.fill(); | |
if (vm.borderWidth) { | |
ctx.stroke(); | |
} | |
}, | |
inRange: function (mouseX, mouseY) { | |
var vm = this._view; | |
var inRange = false; | |
if (vm) { | |
if (vm.x < vm.base) { | |
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base); | |
} else { | |
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x); | |
} | |
} | |
return inRange; | |
} | |
}); | |
rectangle.pivot(); | |
}, | |
calculateBarBase: function (datasetIndex, index) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var base = 0; | |
if (xScale.options.stacked) { | |
var chart = me.chart; | |
var datasets = chart.data.datasets; | |
var value = Number(datasets[datasetIndex].data[index]); | |
for (var i = 0; i < datasetIndex; i++) { | |
var currentDs = datasets[i]; | |
var currentDsMeta = chart.getDatasetMeta(i); | |
if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) { | |
var currentVal = Number(currentDs.data[index]); | |
base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); | |
} | |
} | |
return xScale.getPixelForValue(base); | |
} | |
return xScale.getBasePixel(); | |
}, | |
getRuler: function (index) { | |
var me = this; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var datasetCount = me.getBarCount(); | |
var tickHeight; | |
if (yScale.options.type === 'category') { | |
tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index); | |
} else { | |
// Average width | |
tickHeight = yScale.width / yScale.ticks.length; | |
} | |
var categoryHeight = tickHeight * yScale.options.categoryPercentage; | |
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2; | |
var fullBarHeight = categoryHeight / datasetCount; | |
if (yScale.ticks.length !== me.chart.data.labels.length) { | |
var perc = yScale.ticks.length / me.chart.data.labels.length; | |
fullBarHeight = fullBarHeight * perc; | |
} | |
var barHeight = fullBarHeight * yScale.options.barPercentage; | |
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage); | |
return { | |
datasetCount: datasetCount, | |
tickHeight: tickHeight, | |
categoryHeight: categoryHeight, | |
categorySpacing: categorySpacing, | |
fullBarHeight: fullBarHeight, | |
barHeight: barHeight, | |
barSpacing: barSpacing, | |
}; | |
}, | |
calculateBarHeight: function (index) { | |
var me = this; | |
var yScale = me.getScaleForId(me.getMeta().yAxisID); | |
if (yScale.options.barThickness) { | |
return yScale.options.barThickness; | |
} | |
var ruler = me.getRuler(index); | |
return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight; | |
}, | |
calculateBarX: function (index, datasetIndex) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var value = Number(me.getDataset().data[index]); | |
if (xScale.options.stacked) { | |
var sumPos = 0, | |
sumNeg = 0; | |
for (var i = 0; i < datasetIndex; i++) { | |
var ds = me.chart.data.datasets[i]; | |
var dsMeta = me.chart.getDatasetMeta(i); | |
if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { | |
var stackedVal = Number(ds.data[index]); | |
if (stackedVal < 0) { | |
sumNeg += stackedVal || 0; | |
} else { | |
sumPos += stackedVal || 0; | |
} | |
} | |
} | |
if (value < 0) { | |
return xScale.getPixelForValue(sumNeg + value); | |
} else { | |
return xScale.getPixelForValue(sumPos + value); | |
} | |
} | |
return xScale.getPixelForValue(value); | |
}, | |
calculateBarY: function (index, datasetIndex) { | |
var me = this; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var barIndex = me.getBarIndex(datasetIndex); | |
var ruler = me.getRuler(index); | |
var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); | |
topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0; | |
if (yScale.options.stacked) { | |
return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing; | |
} | |
return topTick + | |
(ruler.barHeight / 2) + | |
ruler.categorySpacing + | |
(ruler.barHeight * barIndex) + | |
(ruler.barSpacing / 2) + | |
(ruler.barSpacing * barIndex); | |
} | |
}); | |
}; | |
},{}],15:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.bubble = { | |
hover: { | |
mode: "single" | |
}, | |
scales: { | |
xAxes: [{ | |
type: "linear", // bubble should probably use a linear scale by default | |
position: "bottom", | |
id: "x-axis-0" // need an ID so datasets can reference the scale | |
}], | |
yAxes: [{ | |
type: "linear", | |
position: "left", | |
id: "y-axis-0" | |
}] | |
}, | |
tooltips: { | |
callbacks: { | |
title: function() { | |
// Title doesn't make sense for scatter since we format the data as a point | |
return ''; | |
}, | |
label: function(tooltipItem, data) { | |
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; | |
var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; | |
return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')'; | |
} | |
} | |
} | |
}; | |
Chart.controllers.bubble = Chart.DatasetController.extend({ | |
dataElementType: Chart.elements.Point, | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var points = meta.data; | |
// Update Points | |
helpers.each(points, function(point, index) { | |
me.updateElement(point, index, reset); | |
}); | |
}, | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var custom = point.custom || {}; | |
var dataset = me.getDataset(); | |
var data = dataset.data[index]; | |
var pointElementOptions = me.chart.options.elements.point; | |
var dsIndex = me.index; | |
helpers.extend(point, { | |
// Utility | |
_xScale: xScale, | |
_yScale: yScale, | |
_datasetIndex: dsIndex, | |
_index: index, | |
// Desired view properties | |
_model: { | |
x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), | |
y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), | |
// Appearance | |
radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), | |
// Tooltip | |
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) | |
} | |
}); | |
// Trick to reset the styles of the point | |
Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); | |
var model = point._model; | |
model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); | |
point.pivot(); | |
}, | |
getRadius: function(value) { | |
return value.r || this.chart.options.elements.point.radius; | |
}, | |
setHoverStyle: function(point) { | |
var me = this; | |
Chart.DatasetController.prototype.setHoverStyle.call(me, point); | |
// Radius | |
var dataset = me.chart.data.datasets[point._datasetIndex]; | |
var index = point._index; | |
var custom = point.custom || {}; | |
var model = point._model; | |
model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); | |
}, | |
removeHoverStyle: function(point) { | |
var me = this; | |
Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); | |
var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; | |
var custom = point.custom || {}; | |
var model = point._model; | |
model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); | |
} | |
}); | |
}; | |
},{}],16:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers, | |
defaults = Chart.defaults; | |
defaults.doughnut = { | |
animation: { | |
//Boolean - Whether we animate the rotation of the Doughnut | |
animateRotate: true, | |
//Boolean - Whether we animate scaling the Doughnut from the centre | |
animateScale: false | |
}, | |
aspectRatio: 1, | |
hover: { | |
mode: 'single' | |
}, | |
legendCallback: function(chart) { | |
var text = []; | |
text.push('<ul class="' + chart.id + '-legend">'); | |
var data = chart.data; | |
var datasets = data.datasets; | |
var labels = data.labels; | |
if (datasets.length) { | |
for (var i = 0; i < datasets[0].data.length; ++i) { | |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); | |
if (labels[i]) { | |
text.push(labels[i]); | |
} | |
text.push('</li>'); | |
} | |
} | |
text.push('</ul>'); | |
return text.join(""); | |
}, | |
legend: { | |
labels: { | |
generateLabels: function(chart) { | |
var data = chart.data; | |
if (data.labels.length && data.datasets.length) { | |
return data.labels.map(function(label, i) { | |
var meta = chart.getDatasetMeta(0); | |
var ds = data.datasets[0]; | |
var arc = meta.data[i]; | |
var custom = arc.custom || {}; | |
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; | |
var arcOpts = chart.options.elements.arc; | |
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); | |
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); | |
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); | |
return { | |
text: label, | |
fillStyle: fill, | |
strokeStyle: stroke, | |
lineWidth: bw, | |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, | |
// Extra data used for toggling the correct item | |
index: i | |
}; | |
}); | |
} else { | |
return []; | |
} | |
} | |
}, | |
onClick: function(e, legendItem) { | |
var index = legendItem.index; | |
var chart = this.chart; | |
var i, ilen, meta; | |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { | |
meta = chart.getDatasetMeta(i); | |
meta.data[index].hidden = !meta.data[index].hidden; | |
} | |
chart.update(); | |
} | |
}, | |
//The percentage of the chart that we cut out of the middle. | |
cutoutPercentage: 50, | |
//The rotation of the chart, where the first data arc begins. | |
rotation: Math.PI * -0.5, | |
//The total circumference of the chart. | |
circumference: Math.PI * 2.0, | |
// Need to override these to give a nice default | |
tooltips: { | |
callbacks: { | |
title: function() { | |
return ''; | |
}, | |
label: function(tooltipItem, data) { | |
return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; | |
} | |
} | |
} | |
}; | |
defaults.pie = helpers.clone(defaults.doughnut); | |
helpers.extend(defaults.pie, { | |
cutoutPercentage: 0 | |
}); | |
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ | |
dataElementType: Chart.elements.Arc, | |
linkScales: helpers.noop, | |
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly | |
getRingIndex: function(datasetIndex) { | |
var ringIndex = 0; | |
for (var j = 0; j < datasetIndex; ++j) { | |
if (this.chart.isDatasetVisible(j)) { | |
++ringIndex; | |
} | |
} | |
return ringIndex; | |
}, | |
update: function(reset) { | |
var me = this; | |
var chart = me.chart, | |
chartArea = chart.chartArea, | |
opts = chart.options, | |
arcOpts = opts.elements.arc, | |
availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, | |
availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, | |
minSize = Math.min(availableWidth, availableHeight), | |
offset = { | |
x: 0, | |
y: 0 | |
}, | |
meta = me.getMeta(), | |
cutoutPercentage = opts.cutoutPercentage, | |
circumference = opts.circumference; | |
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc | |
if (circumference < Math.PI * 2.0) { | |
var startAngle = opts.rotation % (Math.PI * 2.0); | |
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); | |
var endAngle = startAngle + circumference; | |
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; | |
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; | |
var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); | |
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); | |
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); | |
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); | |
var cutout = cutoutPercentage / 100.0; | |
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; | |
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; | |
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; | |
minSize = Math.min(availableWidth / size.width, availableHeight / size.height); | |
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; | |
} | |
chart.borderWidth = me.getMaxBorderWidth(meta.data); | |
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); | |
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0); | |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); | |
chart.offsetX = offset.x * chart.outerRadius; | |
chart.offsetY = offset.y * chart.outerRadius; | |
meta.total = me.calculateTotal(); | |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); | |
me.innerRadius = me.outerRadius - chart.radiusLength; | |
helpers.each(meta.data, function(arc, index) { | |
me.updateElement(arc, index, reset); | |
}); | |
}, | |
updateElement: function(arc, index, reset) { | |
var me = this; | |
var chart = me.chart, | |
chartArea = chart.chartArea, | |
opts = chart.options, | |
animationOpts = opts.animation, | |
centerX = (chartArea.left + chartArea.right) / 2, | |
centerY = (chartArea.top + chartArea.bottom) / 2, | |
startAngle = opts.rotation, // non reset case handled later | |
endAngle = opts.rotation, // non reset case handled later | |
dataset = me.getDataset(), | |
circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), | |
innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, | |
outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, | |
valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; | |
helpers.extend(arc, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
// Desired view properties | |
_model: { | |
x: centerX + chart.offsetX, | |
y: centerY + chart.offsetY, | |
startAngle: startAngle, | |
endAngle: endAngle, | |
circumference: circumference, | |
outerRadius: outerRadius, | |
innerRadius: innerRadius, | |
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) | |
} | |
}); | |
var model = arc._model; | |
// Resets the visual styles | |
this.removeHoverStyle(arc); | |
// Set correct angles if not resetting | |
if (!reset || !animationOpts.animateRotate) { | |
if (index === 0) { | |
model.startAngle = opts.rotation; | |
} else { | |
model.startAngle = me.getMeta().data[index - 1]._model.endAngle; | |
} | |
model.endAngle = model.startAngle + model.circumference; | |
} | |
arc.pivot(); | |
}, | |
removeHoverStyle: function(arc) { | |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); | |
}, | |
calculateTotal: function() { | |
var dataset = this.getDataset(); | |
var meta = this.getMeta(); | |
var total = 0; | |
var value; | |
helpers.each(meta.data, function(element, index) { | |
value = dataset.data[index]; | |
if (!isNaN(value) && !element.hidden) { | |
total += Math.abs(value); | |
} | |
}); | |
return total; | |
}, | |
calculateCircumference: function(value) { | |
var total = this.getMeta().total; | |
if (total > 0 && !isNaN(value)) { | |
return (Math.PI * 2.0) * (value / total); | |
} else { | |
return 0; | |
} | |
}, | |
//gets the max border or hover width to properly scale pie charts | |
getMaxBorderWidth: function (elements) { | |
var max = 0, | |
index = this.index, | |
length = elements.length, | |
borderWidth, | |
hoverWidth; | |
for (var i = 0; i < length; i++) { | |
borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; | |
hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; | |
max = borderWidth > max ? borderWidth : max; | |
max = hoverWidth > max ? hoverWidth : max; | |
} | |
return max; | |
} | |
}); | |
}; | |
},{}],17:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.line = { | |
showLines: true, | |
spanGaps: false, | |
hover: { | |
mode: "label" | |
}, | |
scales: { | |
xAxes: [{ | |
type: "category", | |
id: 'x-axis-0' | |
}], | |
yAxes: [{ | |
type: "linear", | |
id: 'y-axis-0' | |
}] | |
} | |
}; | |
function lineEnabled(dataset, options) { | |
return helpers.getValueOrDefault(dataset.showLine, options.showLines); | |
} | |
Chart.controllers.line = Chart.DatasetController.extend({ | |
datasetElementType: Chart.elements.Line, | |
dataElementType: Chart.elements.Point, | |
addElementAndReset: function(index) { | |
var me = this; | |
var options = me.chart.options; | |
var meta = me.getMeta(); | |
Chart.DatasetController.prototype.addElementAndReset.call(me, index); | |
// Make sure bezier control points are updated | |
if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) { | |
me.updateBezierControlPoints(); | |
} | |
}, | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var line = meta.dataset; | |
var points = meta.data || []; | |
var options = me.chart.options; | |
var lineElementOptions = options.elements.line; | |
var scale = me.getScaleForId(meta.yAxisID); | |
var i, ilen, custom; | |
var dataset = me.getDataset(); | |
var showLine = lineEnabled(dataset, options); | |
// Update Line | |
if (showLine) { | |
custom = line.custom || {}; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { | |
dataset.lineTension = dataset.tension; | |
} | |
// Utility | |
line._scale = scale; | |
line._datasetIndex = me.index; | |
// Data | |
line._children = points; | |
// Model | |
line._model = { | |
// Appearance | |
// The default behavior of lines is to break at null values, according | |
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 | |
// This option gives linse the ability to span gaps | |
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, | |
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), | |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), | |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), | |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), | |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), | |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), | |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), | |
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), | |
// Scale | |
scaleTop: scale.top, | |
scaleBottom: scale.bottom, | |
scaleZero: scale.getBasePixel() | |
}; | |
line.pivot(); | |
} | |
// Update Points | |
for (i=0, ilen=points.length; i<ilen; ++i) { | |
me.updateElement(points[i], i, reset); | |
} | |
if (showLine && line._model.tension !== 0) { | |
me.updateBezierControlPoints(); | |
} | |
// Now pivot the point for animation | |
for (i=0, ilen=points.length; i<ilen; ++i) { | |
points[i].pivot(); | |
} | |
}, | |
getPointBackgroundColor: function(point, index) { | |
var backgroundColor = this.chart.options.elements.point.backgroundColor; | |
var dataset = this.getDataset(); | |
var custom = point.custom || {}; | |
if (custom.backgroundColor) { | |
backgroundColor = custom.backgroundColor; | |
} else if (dataset.pointBackgroundColor) { | |
backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); | |
} else if (dataset.backgroundColor) { | |
backgroundColor = dataset.backgroundColor; | |
} | |
return backgroundColor; | |
}, | |
getPointBorderColor: function(point, index) { | |
var borderColor = this.chart.options.elements.point.borderColor; | |
var dataset = this.getDataset(); | |
var custom = point.custom || {}; | |
if (custom.borderColor) { | |
borderColor = custom.borderColor; | |
} else if (dataset.pointBorderColor) { | |
borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); | |
} else if (dataset.borderColor) { | |
borderColor = dataset.borderColor; | |
} | |
return borderColor; | |
}, | |
getPointBorderWidth: function(point, index) { | |
var borderWidth = this.chart.options.elements.point.borderWidth; | |
var dataset = this.getDataset(); | |
var custom = point.custom || {}; | |
if (custom.borderWidth) { | |
borderWidth = custom.borderWidth; | |
} else if (dataset.pointBorderWidth) { | |
borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); | |
} else if (dataset.borderWidth) { | |
borderWidth = dataset.borderWidth; | |
} | |
return borderWidth; | |
}, | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var custom = point.custom || {}; | |
var dataset = me.getDataset(); | |
var datasetIndex = me.index; | |
var value = dataset.data[index]; | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var pointOptions = me.chart.options.elements.point; | |
var x, y; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { | |
dataset.pointRadius = dataset.radius; | |
} | |
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { | |
dataset.pointHitRadius = dataset.hitRadius; | |
} | |
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, me.chart.isCombo); | |
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); | |
// Utility | |
point._xScale = xScale; | |
point._yScale = yScale; | |
point._datasetIndex = datasetIndex; | |
point._index = index; | |
// Desired view properties | |
point._model = { | |
x: x, | |
y: y, | |
skip: custom.skip || isNaN(x) || isNaN(y), | |
// Appearance | |
radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), | |
pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), | |
backgroundColor: me.getPointBackgroundColor(point, index), | |
borderColor: me.getPointBorderColor(point, index), | |
borderWidth: me.getPointBorderWidth(point, index), | |
tension: meta.dataset._model ? meta.dataset._model.tension : 0, | |
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, | |
// Tooltip | |
hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) | |
}; | |
}, | |
calculatePointY: function(value, index, datasetIndex) { | |
var me = this; | |
var chart = me.chart; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var sumPos = 0; | |
var sumNeg = 0; | |
var i, ds, dsMeta; | |
if (yScale.options.stacked) { | |
for (i = 0; i < datasetIndex; i++) { | |
ds = chart.data.datasets[i]; | |
dsMeta = chart.getDatasetMeta(i); | |
if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) { | |
var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); | |
if (stackedRightValue < 0) { | |
sumNeg += stackedRightValue || 0; | |
} else { | |
sumPos += stackedRightValue || 0; | |
} | |
} | |
} | |
var rightValue = Number(yScale.getRightValue(value)); | |
if (rightValue < 0) { | |
return yScale.getPixelForValue(sumNeg + rightValue); | |
} else { | |
return yScale.getPixelForValue(sumPos + rightValue); | |
} | |
} | |
return yScale.getPixelForValue(value); | |
}, | |
updateBezierControlPoints: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var area = me.chart.chartArea; | |
var points = meta.data || []; | |
var i, ilen, point, model, controlPoints; | |
var needToCap = me.chart.options.elements.line.capBezierPoints; | |
function capIfNecessary(pt, min, max) { | |
return needToCap ? Math.max(Math.min(pt, max), min) : pt; | |
} | |
for (i=0, ilen=points.length; i<ilen; ++i) { | |
point = points[i]; | |
model = point._model; | |
controlPoints = helpers.splineCurve( | |
helpers.previousItem(points, i)._model, | |
model, | |
helpers.nextItem(points, i)._model, | |
meta.dataset._model.tension | |
); | |
model.controlPointPreviousX = capIfNecessary(controlPoints.previous.x, area.left, area.right); | |
model.controlPointPreviousY = capIfNecessary(controlPoints.previous.y, area.top, area.bottom); | |
model.controlPointNextX = capIfNecessary(controlPoints.next.x, area.left, area.right); | |
model.controlPointNextY = capIfNecessary(controlPoints.next.y, area.top, area.bottom); | |
} | |
}, | |
draw: function(ease) { | |
var me = this; | |
var meta = me.getMeta(); | |
var points = meta.data || []; | |
var easingDecimal = ease || 1; | |
var i, ilen; | |
// Transition Point Locations | |
for (i=0, ilen=points.length; i<ilen; ++i) { | |
points[i].transition(easingDecimal); | |
} | |
// Transition and Draw the line | |
if (lineEnabled(me.getDataset(), me.chart.options)) { | |
meta.dataset.transition(easingDecimal).draw(); | |
} | |
// Draw the points | |
for (i=0, ilen=points.length; i<ilen; ++i) { | |
points[i].draw(); | |
} | |
}, | |
setHoverStyle: function(point) { | |
// Point | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var index = point._index; | |
var custom = point.custom || {}; | |
var model = point._model; | |
model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); | |
model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(point) { | |
var me = this; | |
var dataset = me.chart.data.datasets[point._datasetIndex]; | |
var index = point._index; | |
var custom = point.custom || {}; | |
var model = point._model; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { | |
dataset.pointRadius = dataset.radius; | |
} | |
model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); | |
model.backgroundColor = me.getPointBackgroundColor(point, index); | |
model.borderColor = me.getPointBorderColor(point, index); | |
model.borderWidth = me.getPointBorderWidth(point, index); | |
} | |
}); | |
}; | |
},{}],18:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.polarArea = { | |
scale: { | |
type: "radialLinear", | |
lineArc: true, // so that lines are circular | |
ticks: { | |
beginAtZero: true | |
} | |
}, | |
//Boolean - Whether to animate the rotation of the chart | |
animation: { | |
animateRotate: true, | |
animateScale: true | |
}, | |
startAngle: -0.5 * Math.PI, | |
aspectRatio: 1, | |
legendCallback: function(chart) { | |
var text = []; | |
text.push('<ul class="' + chart.id + '-legend">'); | |
var data = chart.data; | |
var datasets = data.datasets; | |
var labels = data.labels; | |
if (datasets.length) { | |
for (var i = 0; i < datasets[0].data.length; ++i) { | |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '">'); | |
if (labels[i]) { | |
text.push(labels[i]); | |
} | |
text.push('</span></li>'); | |
} | |
} | |
text.push('</ul>'); | |
return text.join(""); | |
}, | |
legend: { | |
labels: { | |
generateLabels: function(chart) { | |
var data = chart.data; | |
if (data.labels.length && data.datasets.length) { | |
return data.labels.map(function(label, i) { | |
var meta = chart.getDatasetMeta(0); | |
var ds = data.datasets[0]; | |
var arc = meta.data[i]; | |
var custom = arc.custom || {}; | |
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; | |
var arcOpts = chart.options.elements.arc; | |
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); | |
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); | |
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); | |
return { | |
text: label, | |
fillStyle: fill, | |
strokeStyle: stroke, | |
lineWidth: bw, | |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, | |
// Extra data used for toggling the correct item | |
index: i | |
}; | |
}); | |
} else { | |
return []; | |
} | |
} | |
}, | |
onClick: function(e, legendItem) { | |
var index = legendItem.index; | |
var chart = this.chart; | |
var i, ilen, meta; | |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { | |
meta = chart.getDatasetMeta(i); | |
meta.data[index].hidden = !meta.data[index].hidden; | |
} | |
chart.update(); | |
} | |
}, | |
// Need to override these to give a nice default | |
tooltips: { | |
callbacks: { | |
title: function() { | |
return ''; | |
}, | |
label: function(tooltipItem, data) { | |
return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; | |
} | |
} | |
} | |
}; | |
Chart.controllers.polarArea = Chart.DatasetController.extend({ | |
dataElementType: Chart.elements.Arc, | |
linkScales: helpers.noop, | |
update: function(reset) { | |
var me = this; | |
var chart = me.chart; | |
var chartArea = chart.chartArea; | |
var meta = me.getMeta(); | |
var opts = chart.options; | |
var arcOpts = opts.elements.arc; | |
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); | |
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); | |
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); | |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); | |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); | |
me.innerRadius = me.outerRadius - chart.radiusLength; | |
meta.count = me.countVisibleElements(); | |
helpers.each(meta.data, function(arc, index) { | |
me.updateElement(arc, index, reset); | |
}); | |
}, | |
updateElement: function(arc, index, reset) { | |
var me = this; | |
var chart = me.chart; | |
var dataset = me.getDataset(); | |
var opts = chart.options; | |
var animationOpts = opts.animation; | |
var scale = chart.scale; | |
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; | |
var labels = chart.data.labels; | |
var circumference = me.calculateCircumference(dataset.data[index]); | |
var centerX = scale.xCenter; | |
var centerY = scale.yCenter; | |
// If there is NaN data before us, we need to calculate the starting angle correctly. | |
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data | |
var visibleCount = 0; | |
var meta = me.getMeta(); | |
for (var i = 0; i < index; ++i) { | |
if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { | |
++visibleCount; | |
} | |
} | |
//var negHalfPI = -0.5 * Math.PI; | |
var datasetStartAngle = opts.startAngle; | |
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); | |
var startAngle = datasetStartAngle + (circumference * visibleCount); | |
var endAngle = startAngle + (arc.hidden ? 0 : circumference); | |
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); | |
helpers.extend(arc, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
_scale: scale, | |
// Desired view properties | |
_model: { | |
x: centerX, | |
y: centerY, | |
innerRadius: 0, | |
outerRadius: reset ? resetRadius : distance, | |
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, | |
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, | |
label: getValueAtIndexOrDefault(labels, index, labels[index]) | |
} | |
}); | |
// Apply border and fill style | |
me.removeHoverStyle(arc); | |
arc.pivot(); | |
}, | |
removeHoverStyle: function(arc) { | |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); | |
}, | |
countVisibleElements: function() { | |
var dataset = this.getDataset(); | |
var meta = this.getMeta(); | |
var count = 0; | |
helpers.each(meta.data, function(element, index) { | |
if (!isNaN(dataset.data[index]) && !element.hidden) { | |
count++; | |
} | |
}); | |
return count; | |
}, | |
calculateCircumference: function(value) { | |
var count = this.getMeta().count; | |
if (count > 0 && !isNaN(value)) { | |
return (2 * Math.PI) / count; | |
} else { | |
return 0; | |
} | |
} | |
}); | |
}; | |
},{}],19:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.radar = { | |
scale: { | |
type: "radialLinear" | |
}, | |
elements: { | |
line: { | |
tension: 0 // no bezier in radar | |
} | |
} | |
}; | |
Chart.controllers.radar = Chart.DatasetController.extend({ | |
datasetElementType: Chart.elements.Line, | |
dataElementType: Chart.elements.Point, | |
linkScales: helpers.noop, | |
addElementAndReset: function(index) { | |
Chart.DatasetController.prototype.addElementAndReset.call(this, index); | |
// Make sure bezier control points are updated | |
this.updateBezierControlPoints(); | |
}, | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var line = meta.dataset; | |
var points = meta.data; | |
var custom = line.custom || {}; | |
var dataset = me.getDataset(); | |
var lineElementOptions = me.chart.options.elements.line; | |
var scale = me.chart.scale; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { | |
dataset.lineTension = dataset.tension; | |
} | |
helpers.extend(meta.dataset, { | |
// Utility | |
_datasetIndex: me.index, | |
// Data | |
_children: points, | |
_loop: true, | |
// Model | |
_model: { | |
// Appearance | |
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), | |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), | |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), | |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), | |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), | |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), | |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), | |
// Scale | |
scaleTop: scale.top, | |
scaleBottom: scale.bottom, | |
scaleZero: scale.getBasePosition() | |
} | |
}); | |
meta.dataset.pivot(); | |
// Update Points | |
helpers.each(points, function(point, index) { | |
me.updateElement(point, index, reset); | |
}, me); | |
// Update bezier control points | |
me.updateBezierControlPoints(); | |
}, | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var custom = point.custom || {}; | |
var dataset = me.getDataset(); | |
var scale = me.chart.scale; | |
var pointElementOptions = me.chart.options.elements.point; | |
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); | |
helpers.extend(point, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
_scale: scale, | |
// Desired view properties | |
_model: { | |
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales | |
y: reset ? scale.yCenter : pointPosition.y, | |
// Appearance | |
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension), | |
radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), | |
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), | |
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), | |
// Tooltip | |
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) | |
} | |
}); | |
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); | |
}, | |
updateBezierControlPoints: function() { | |
var chartArea = this.chart.chartArea; | |
var meta = this.getMeta(); | |
helpers.each(meta.data, function(point, index) { | |
var model = point._model; | |
var controlPoints = helpers.splineCurve( | |
helpers.previousItem(meta.data, index, true)._model, | |
model, | |
helpers.nextItem(meta.data, index, true)._model, | |
model.tension | |
); | |
// Prevent the bezier going outside of the bounds of the graph | |
model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); | |
model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); | |
model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); | |
model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); | |
// Now pivot the point for animation | |
point.pivot(); | |
}); | |
}, | |
draw: function(ease) { | |
var meta = this.getMeta(); | |
var easingDecimal = ease || 1; | |
// Transition Point Locations | |
helpers.each(meta.data, function(point) { | |
point.transition(easingDecimal); | |
}); | |
// Transition and Draw the line | |
meta.dataset.transition(easingDecimal).draw(); | |
// Draw the points | |
helpers.each(meta.data, function(point) { | |
point.draw(); | |
}); | |
}, | |
setHoverStyle: function(point) { | |
// Point | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var custom = point.custom || {}; | |
var index = point._index; | |
var model = point._model; | |
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); | |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(point) { | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var custom = point.custom || {}; | |
var index = point._index; | |
var model = point._model; | |
var pointElementOptions = this.chart.options.elements.point; | |
model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius); | |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); | |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); | |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); | |
} | |
}); | |
}; | |
},{}],20:[function(require,module,exports){ | |
/*global window: false */ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.global.animation = { | |
duration: 1000, | |
easing: "easeOutQuart", | |
onProgress: helpers.noop, | |
onComplete: helpers.noop | |
}; | |
Chart.Animation = Chart.Element.extend({ | |
currentStep: null, // the current animation step | |
numSteps: 60, // default number of steps | |
easing: "", // the easing to use for this animation | |
render: null, // render function used by the animation service | |
onAnimationProgress: null, // user specified callback to fire on each step of the animation | |
onAnimationComplete: null // user specified callback to fire when the animation finishes | |
}); | |
Chart.animationService = { | |
frameDuration: 17, | |
animations: [], | |
dropFrames: 0, | |
request: null, | |
addAnimation: function(chartInstance, animationObject, duration, lazy) { | |
var me = this; | |
if (!lazy) { | |
chartInstance.animating = true; | |
} | |
for (var index = 0; index < me.animations.length; ++index) { | |
if (me.animations[index].chartInstance === chartInstance) { | |
// replacing an in progress animation | |
me.animations[index].animationObject = animationObject; | |
return; | |
} | |
} | |
me.animations.push({ | |
chartInstance: chartInstance, | |
animationObject: animationObject | |
}); | |
// If there are no animations queued, manually kickstart a digest, for lack of a better word | |
if (me.animations.length === 1) { | |
me.requestAnimationFrame(); | |
} | |
}, | |
// Cancel the animation for a given chart instance | |
cancelAnimation: function(chartInstance) { | |
var index = helpers.findIndex(this.animations, function(animationWrapper) { | |
return animationWrapper.chartInstance === chartInstance; | |
}); | |
if (index !== -1) { | |
this.animations.splice(index, 1); | |
chartInstance.animating = false; | |
} | |
}, | |
requestAnimationFrame: function() { | |
var me = this; | |
if (me.request === null) { | |
// Skip animation frame requests until the active one is executed. | |
// This can happen when processing mouse events, e.g. 'mousemove' | |
// and 'mouseout' events will trigger multiple renders. | |
me.request = helpers.requestAnimFrame.call(window, function() { | |
me.request = null; | |
me.startDigest(); | |
}); | |
} | |
}, | |
startDigest: function() { | |
var me = this; | |
var startTime = Date.now(); | |
var framesToDrop = 0; | |
if (me.dropFrames > 1) { | |
framesToDrop = Math.floor(me.dropFrames); | |
me.dropFrames = me.dropFrames % 1; | |
} | |
var i = 0; | |
while (i < me.animations.length) { | |
if (me.animations[i].animationObject.currentStep === null) { | |
me.animations[i].animationObject.currentStep = 0; | |
} | |
me.animations[i].animationObject.currentStep += 1 + framesToDrop; | |
if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) { | |
me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps; | |
} | |
me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject); | |
if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) { | |
me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]); | |
} | |
if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) { | |
if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) { | |
me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]); | |
} | |
// executed the last frame. Remove the animation. | |
me.animations[i].chartInstance.animating = false; | |
me.animations.splice(i, 1); | |
} else { | |
++i; | |
} | |
} | |
var endTime = Date.now(); | |
var dropFrames = (endTime - startTime) / me.frameDuration; | |
me.dropFrames += dropFrames; | |
// Do we have more stuff to animate? | |
if (me.animations.length > 0) { | |
me.requestAnimationFrame(); | |
} | |
} | |
}; | |
}; | |
},{}],21:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
// Global Chart canvas helpers object for drawing items to canvas | |
var helpers = Chart.canvasHelpers = {}; | |
helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { | |
var type, edgeLength, xOffset, yOffset, height, size; | |
if (typeof pointStyle === 'object') { | |
type = pointStyle.toString(); | |
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { | |
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); | |
return; | |
} | |
} | |
if (isNaN(radius) || radius <= 0) { | |
return; | |
} | |
switch (pointStyle) { | |
// Default includes circle | |
default: | |
ctx.beginPath(); | |
ctx.arc(x, y, radius, 0, Math.PI * 2); | |
ctx.closePath(); | |
ctx.fill(); | |
break; | |
case 'triangle': | |
ctx.beginPath(); | |
edgeLength = 3 * radius / Math.sqrt(3); | |
height = edgeLength * Math.sqrt(3) / 2; | |
ctx.moveTo(x - edgeLength / 2, y + height / 3); | |
ctx.lineTo(x + edgeLength / 2, y + height / 3); | |
ctx.lineTo(x, y - 2 * height / 3); | |
ctx.closePath(); | |
ctx.fill(); | |
break; | |
case 'rect': | |
size = 1 / Math.SQRT2 * radius; | |
ctx.beginPath(); | |
ctx.fillRect(x - size, y - size, 2 * size, 2 * size); | |
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); | |
break; | |
case 'rectRot': | |
size = 1 / Math.SQRT2 * radius; | |
ctx.beginPath(); | |
ctx.moveTo(x - size, y); | |
ctx.lineTo(x, y + size); | |
ctx.lineTo(x + size, y); | |
ctx.lineTo(x, y - size); | |
ctx.closePath(); | |
ctx.fill(); | |
break; | |
case 'cross': | |
ctx.beginPath(); | |
ctx.moveTo(x, y + radius); | |
ctx.lineTo(x, y - radius); | |
ctx.moveTo(x - radius, y); | |
ctx.lineTo(x + radius, y); | |
ctx.closePath(); | |
break; | |
case 'crossRot': | |
ctx.beginPath(); | |
xOffset = Math.cos(Math.PI / 4) * radius; | |
yOffset = Math.sin(Math.PI / 4) * radius; | |
ctx.moveTo(x - xOffset, y - yOffset); | |
ctx.lineTo(x + xOffset, y + yOffset); | |
ctx.moveTo(x - xOffset, y + yOffset); | |
ctx.lineTo(x + xOffset, y - yOffset); | |
ctx.closePath(); | |
break; | |
case 'star': | |
ctx.beginPath(); | |
ctx.moveTo(x, y + radius); | |
ctx.lineTo(x, y - radius); | |
ctx.moveTo(x - radius, y); | |
ctx.lineTo(x + radius, y); | |
xOffset = Math.cos(Math.PI / 4) * radius; | |
yOffset = Math.sin(Math.PI / 4) * radius; | |
ctx.moveTo(x - xOffset, y - yOffset); | |
ctx.lineTo(x + xOffset, y + yOffset); | |
ctx.moveTo(x - xOffset, y + yOffset); | |
ctx.lineTo(x + xOffset, y - yOffset); | |
ctx.closePath(); | |
break; | |
case 'line': | |
ctx.beginPath(); | |
ctx.moveTo(x - radius, y); | |
ctx.lineTo(x + radius, y); | |
ctx.closePath(); | |
break; | |
case 'dash': | |
ctx.beginPath(); | |
ctx.moveTo(x, y); | |
ctx.lineTo(x + radius, y); | |
ctx.closePath(); | |
break; | |
} | |
ctx.stroke(); | |
}; | |
}; | |
},{}],22:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
//Create a dictionary of chart types, to allow for extension of existing types | |
Chart.types = {}; | |
//Store a reference to each instance - allowing us to globally resize chart instances on window resize. | |
//Destroy method on the chart will remove the instance of the chart from this reference. | |
Chart.instances = {}; | |
// Controllers available for dataset visualization eg. bar, line, slice, etc. | |
Chart.controllers = {}; | |
/** | |
* @class Chart.Controller | |
* The main controller of a chart. | |
*/ | |
Chart.Controller = function(instance) { | |
this.chart = instance; | |
this.config = instance.config; | |
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {}); | |
this.id = helpers.uid(); | |
Object.defineProperty(this, 'data', { | |
get: function() { | |
return this.config.data; | |
} | |
}); | |
//Add the chart instance to the global namespace | |
Chart.instances[this.id] = this; | |
if (this.options.responsive) { | |
// Silent resize before chart draws | |
this.resize(true); | |
} | |
this.initialize(); | |
return this; | |
}; | |
helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ { | |
initialize: function() { | |
var me = this; | |
// Before init plugin notification | |
Chart.plugins.notify('beforeInit', [me]); | |
me.bindEvents(); | |
// Make sure controllers are built first so that each dataset is bound to an axis before the scales | |
// are built | |
me.ensureScalesHaveIDs(); | |
me.buildOrUpdateControllers(); | |
me.buildScales(); | |
me.updateLayout(); | |
me.resetElements(); | |
me.initToolTip(); | |
me.update(); | |
// After init plugin notification | |
Chart.plugins.notify('afterInit', [me]); | |
return me; | |
}, | |
clear: function() { | |
helpers.clear(this.chart); | |
return this; | |
}, | |
stop: function() { | |
// Stops any current animation loop occuring | |
Chart.animationService.cancelAnimation(this); | |
return this; | |
}, | |
resize: function resize(silent) { | |
var me = this; | |
var chart = me.chart; | |
var canvas = chart.canvas; | |
var newWidth = helpers.getMaximumWidth(canvas); | |
var aspectRatio = chart.aspectRatio; | |
var newHeight = (me.options.maintainAspectRatio && isNaN(aspectRatio) === false && isFinite(aspectRatio) && aspectRatio !== 0) ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas); | |
var sizeChanged = chart.width !== newWidth || chart.height !== newHeight; | |
if (!sizeChanged) { | |
return me; | |
} | |
canvas.width = chart.width = newWidth; | |
canvas.height = chart.height = newHeight; | |
helpers.retinaScale(chart); | |
// Notify any plugins about the resize | |
var newSize = { width: newWidth, height: newHeight }; | |
Chart.plugins.notify('resize', [me, newSize]); | |
// Notify of resize | |
if (me.options.onResize) { | |
me.options.onResize(me, newSize); | |
} | |
if (!silent) { | |
me.stop(); | |
me.update(me.options.responsiveAnimationDuration); | |
} | |
return me; | |
}, | |
ensureScalesHaveIDs: function() { | |
var options = this.options; | |
var scalesOptions = options.scales || {}; | |
var scaleOptions = options.scale; | |
helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { | |
xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); | |
}); | |
helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { | |
yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); | |
}); | |
if (scaleOptions) { | |
scaleOptions.id = scaleOptions.id || 'scale'; | |
} | |
}, | |
/** | |
* Builds a map of scale ID to scale object for future lookup. | |
*/ | |
buildScales: function() { | |
var me = this; | |
var options = me.options; | |
var scales = me.scales = {}; | |
var items = []; | |
if (options.scales) { | |
items = items.concat( | |
(options.scales.xAxes || []).map(function(xAxisOptions) { | |
return { options: xAxisOptions, dtype: 'category' }; }), | |
(options.scales.yAxes || []).map(function(yAxisOptions) { | |
return { options: yAxisOptions, dtype: 'linear' }; })); | |
} | |
if (options.scale) { | |
items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true }); | |
} | |
helpers.each(items, function(item) { | |
var scaleOptions = item.options; | |
var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); | |
var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); | |
if (!scaleClass) { | |
return; | |
} | |
var scale = new scaleClass({ | |
id: scaleOptions.id, | |
options: scaleOptions, | |
ctx: me.chart.ctx, | |
chart: me | |
}); | |
scales[scale.id] = scale; | |
// TODO(SB): I think we should be able to remove this custom case (options.scale) | |
// and consider it as a regular scale part of the "scales"" map only! This would | |
// make the logic easier and remove some useless? custom code. | |
if (item.isDefault) { | |
me.scale = scale; | |
} | |
}); | |
Chart.scaleService.addScalesToLayout(this); | |
}, | |
updateLayout: function() { | |
Chart.layoutService.update(this, this.chart.width, this.chart.height); | |
}, | |
buildOrUpdateControllers: function() { | |
var me = this; | |
var types = []; | |
var newControllers = []; | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
var meta = me.getDatasetMeta(datasetIndex); | |
if (!meta.type) { | |
meta.type = dataset.type || me.config.type; | |
} | |
types.push(meta.type); | |
if (meta.controller) { | |
meta.controller.updateIndex(datasetIndex); | |
} else { | |
meta.controller = new Chart.controllers[meta.type](me, datasetIndex); | |
newControllers.push(meta.controller); | |
} | |
}, me); | |
if (types.length > 1) { | |
for (var i = 1; i < types.length; i++) { | |
if (types[i] !== types[i - 1]) { | |
me.isCombo = true; | |
break; | |
} | |
} | |
} | |
return newControllers; | |
}, | |
resetElements: function() { | |
var me = this; | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
me.getDatasetMeta(datasetIndex).controller.reset(); | |
}, me); | |
}, | |
update: function update(animationDuration, lazy) { | |
var me = this; | |
Chart.plugins.notify('beforeUpdate', [me]); | |
// In case the entire data object changed | |
me.tooltip._data = me.data; | |
// Make sure dataset controllers are updated and new controllers are reset | |
var newControllers = me.buildOrUpdateControllers(); | |
// Make sure all dataset controllers have correct meta data counts | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); | |
}, me); | |
Chart.layoutService.update(me, me.chart.width, me.chart.height); | |
// Apply changes to the dataets that require the scales to have been calculated i.e BorderColor chages | |
Chart.plugins.notify('afterScaleUpdate', [me]); | |
// Can only reset the new controllers after the scales have been updated | |
helpers.each(newControllers, function(controller) { | |
controller.reset(); | |
}); | |
me.updateDatasets(); | |
// Do this before render so that any plugins that need final scale updates can use it | |
Chart.plugins.notify('afterUpdate', [me]); | |
me.render(animationDuration, lazy); | |
}, | |
/** | |
* @method beforeDatasetsUpdate | |
* @description Called before all datasets are updated. If a plugin returns false, | |
* the datasets update will be cancelled until another chart update is triggered. | |
* @param {Object} instance the chart instance being updated. | |
* @returns {Boolean} false to cancel the datasets update. | |
* @memberof Chart.PluginBase | |
* @since version 2.1.5 | |
* @instance | |
*/ | |
/** | |
* @method afterDatasetsUpdate | |
* @description Called after all datasets have been updated. Note that this | |
* extension will not be called if the datasets update has been cancelled. | |
* @param {Object} instance the chart instance being updated. | |
* @memberof Chart.PluginBase | |
* @since version 2.1.5 | |
* @instance | |
*/ | |
/** | |
* Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate | |
* extension, in which case no datasets will be updated and the afterDatasetsUpdate | |
* notification will be skipped. | |
* @protected | |
* @instance | |
*/ | |
updateDatasets: function() { | |
var me = this; | |
var i, ilen; | |
if (Chart.plugins.notify('beforeDatasetsUpdate', [ me ])) { | |
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { | |
me.getDatasetMeta(i).controller.update(); | |
} | |
Chart.plugins.notify('afterDatasetsUpdate', [ me ]); | |
} | |
}, | |
render: function render(duration, lazy) { | |
var me = this; | |
Chart.plugins.notify('beforeRender', [me]); | |
var animationOptions = me.options.animation; | |
if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { | |
var animation = new Chart.Animation(); | |
animation.numSteps = (duration || animationOptions.duration) / 16.66; //60 fps | |
animation.easing = animationOptions.easing; | |
// render function | |
animation.render = function(chartInstance, animationObject) { | |
var easingFunction = helpers.easingEffects[animationObject.easing]; | |
var stepDecimal = animationObject.currentStep / animationObject.numSteps; | |
var easeDecimal = easingFunction(stepDecimal); | |
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); | |
}; | |
// user events | |
animation.onAnimationProgress = animationOptions.onProgress; | |
animation.onAnimationComplete = animationOptions.onComplete; | |
Chart.animationService.addAnimation(me, animation, duration, lazy); | |
} else { | |
me.draw(); | |
if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) { | |
animationOptions.onComplete.call(me); | |
} | |
} | |
return me; | |
}, | |
draw: function(ease) { | |
var me = this; | |
var easingDecimal = ease || 1; | |
me.clear(); | |
Chart.plugins.notify('beforeDraw', [me, easingDecimal]); | |
// Draw all the scales | |
helpers.each(me.boxes, function(box) { | |
box.draw(me.chartArea); | |
}, me); | |
if (me.scale) { | |
me.scale.draw(); | |
} | |
Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]); | |
// Draw each dataset via its respective controller (reversed to support proper line stacking) | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
if (me.isDatasetVisible(datasetIndex)) { | |
me.getDatasetMeta(datasetIndex).controller.draw(ease); | |
} | |
}, me, true); | |
Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]); | |
// Finally draw the tooltip | |
me.tooltip.transition(easingDecimal).draw(); | |
Chart.plugins.notify('afterDraw', [me, easingDecimal]); | |
}, | |
// Get the single element that was clicked on | |
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw | |
getElementAtEvent: function(e) { | |
var me = this; | |
var eventPosition = helpers.getRelativePosition(e, me.chart); | |
var elementsArray = []; | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
if (me.isDatasetVisible(datasetIndex)) { | |
var meta = me.getDatasetMeta(datasetIndex); | |
helpers.each(meta.data, function(element) { | |
if (element.inRange(eventPosition.x, eventPosition.y)) { | |
elementsArray.push(element); | |
return elementsArray; | |
} | |
}); | |
} | |
}); | |
return elementsArray; | |
}, | |
getElementsAtEvent: function(e) { | |
var me = this; | |
var eventPosition = helpers.getRelativePosition(e, me.chart); | |
var elementsArray = []; | |
var found = (function() { | |
if (me.data.datasets) { | |
for (var i = 0; i < me.data.datasets.length; i++) { | |
var meta = me.getDatasetMeta(i); | |
if (me.isDatasetVisible(i)) { | |
for (var j = 0; j < meta.data.length; j++) { | |
if (meta.data[j].inRange(eventPosition.x, eventPosition.y)) { | |
return meta.data[j]; | |
} | |
} | |
} | |
} | |
} | |
}).call(me); | |
if (!found) { | |
return elementsArray; | |
} | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
if (me.isDatasetVisible(datasetIndex)) { | |
var meta = me.getDatasetMeta(datasetIndex), | |
element = meta.data[found._index]; | |
if(element && !element._view.skip){ | |
elementsArray.push(element); | |
} | |
} | |
}, me); | |
return elementsArray; | |
}, | |
getElementsAtXAxis: function(e){ | |
var me = this; | |
var eventPosition = helpers.getRelativePosition(e, me.chart); | |
var elementsArray = []; | |
var found = (function() { | |
if (me.data.datasets) { | |
for (var i = 0; i < me.data.datasets.length; i++) { | |
var meta = me.getDatasetMeta(i); | |
if (me.isDatasetVisible(i)) { | |
for (var j = 0; j < meta.data.length; j++) { | |
if (meta.data[j].inLabelRange(eventPosition.x, eventPosition.y)) { | |
return meta.data[j]; | |
} | |
} | |
} | |
} | |
} | |
}).call(me); | |
if (!found) { | |
return elementsArray; | |
} | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
if (me.isDatasetVisible(datasetIndex)) { | |
var meta = me.getDatasetMeta(datasetIndex); | |
if(!meta.data[found._index]._view.skip){ | |
elementsArray.push(meta.data[found._index]); | |
} | |
} | |
}, me); | |
return elementsArray; | |
}, | |
getElementsAtEventForMode: function(e, mode) { | |
var me = this; | |
switch (mode) { | |
case 'single': | |
return me.getElementAtEvent(e); | |
case 'label': | |
return me.getElementsAtEvent(e); | |
case 'dataset': | |
return me.getDatasetAtEvent(e); | |
case 'x-axis': | |
return me.getElementsAtXAxis(e); | |
default: | |
return e; | |
} | |
}, | |
getDatasetAtEvent: function(e) { | |
var elementsArray = this.getElementAtEvent(e); | |
if (elementsArray.length > 0) { | |
elementsArray = this.getDatasetMeta(elementsArray[0]._datasetIndex).data; | |
} | |
return elementsArray; | |
}, | |
getDatasetMeta: function(datasetIndex) { | |
var me = this; | |
var dataset = me.data.datasets[datasetIndex]; | |
if (!dataset._meta) { | |
dataset._meta = {}; | |
} | |
var meta = dataset._meta[me.id]; | |
if (!meta) { | |
meta = dataset._meta[me.id] = { | |
type: null, | |
data: [], | |
dataset: null, | |
controller: null, | |
hidden: null, // See isDatasetVisible() comment | |
xAxisID: null, | |
yAxisID: null | |
}; | |
} | |
return meta; | |
}, | |
getVisibleDatasetCount: function() { | |
var count = 0; | |
for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) { | |
if (this.isDatasetVisible(i)) { | |
count++; | |
} | |
} | |
return count; | |
}, | |
isDatasetVisible: function(datasetIndex) { | |
var meta = this.getDatasetMeta(datasetIndex); | |
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, | |
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. | |
return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden; | |
}, | |
generateLegend: function() { | |
return this.options.legendCallback(this); | |
}, | |
destroy: function() { | |
var me = this; | |
me.stop(); | |
me.clear(); | |
helpers.unbindEvents(me, me.events); | |
helpers.removeResizeListener(me.chart.canvas.parentNode); | |
// Reset canvas height/width attributes | |
var canvas = me.chart.canvas; | |
canvas.width = me.chart.width; | |
canvas.height = me.chart.height; | |
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here | |
if (me.chart.originalDevicePixelRatio !== undefined) { | |
me.chart.ctx.scale(1 / me.chart.originalDevicePixelRatio, 1 / me.chart.originalDevicePixelRatio); | |
} | |
// Reset to the old style since it may have been changed by the device pixel ratio changes | |
canvas.style.width = me.chart.originalCanvasStyleWidth; | |
canvas.style.height = me.chart.originalCanvasStyleHeight; | |
Chart.plugins.notify('destroy', [me]); | |
delete Chart.instances[me.id]; | |
}, | |
toBase64Image: function() { | |
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); | |
}, | |
initToolTip: function() { | |
var me = this; | |
me.tooltip = new Chart.Tooltip({ | |
_chart: me.chart, | |
_chartInstance: me, | |
_data: me.data, | |
_options: me.options.tooltips | |
}, me); | |
}, | |
bindEvents: function() { | |
var me = this; | |
helpers.bindEvents(me, me.options.events, function(evt) { | |
me.eventHandler(evt); | |
}); | |
}, | |
updateHoverStyle: function(elements, mode, enabled) { | |
var method = enabled? 'setHoverStyle' : 'removeHoverStyle'; | |
var element, i, ilen; | |
switch (mode) { | |
case 'single': | |
elements = [ elements[0] ]; | |
break; | |
case 'label': | |
case 'dataset': | |
case 'x-axis': | |
// elements = elements; | |
break; | |
default: | |
// unsupported mode | |
return; | |
} | |
for (i=0, ilen=elements.length; i<ilen; ++i) { | |
element = elements[i]; | |
if (element) { | |
this.getDatasetMeta(element._datasetIndex).controller[method](element); | |
} | |
} | |
}, | |
eventHandler: function eventHandler(e) { | |
var me = this; | |
var tooltip = me.tooltip; | |
var options = me.options || {}; | |
var hoverOptions = options.hover; | |
var tooltipsOptions = options.tooltips; | |
me.lastActive = me.lastActive || []; | |
me.lastTooltipActive = me.lastTooltipActive || []; | |
// Find Active Elements for hover and tooltips | |
if (e.type === 'mouseout') { | |
me.active = []; | |
me.tooltipActive = []; | |
} else { | |
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode); | |
me.tooltipActive = me.getElementsAtEventForMode(e, tooltipsOptions.mode); | |
} | |
// On Hover hook | |
if (hoverOptions.onHover) { | |
hoverOptions.onHover.call(me, me.active); | |
} | |
if (e.type === 'mouseup' || e.type === 'click') { | |
if (options.onClick) { | |
options.onClick.call(me, e, me.active); | |
} | |
if (me.legend && me.legend.handleEvent) { | |
me.legend.handleEvent(e); | |
} | |
} | |
// Remove styling for last active (even if it may still be active) | |
if (me.lastActive.length) { | |
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); | |
} | |
// Built in hover styling | |
if (me.active.length && hoverOptions.mode) { | |
me.updateHoverStyle(me.active, hoverOptions.mode, true); | |
} | |
// Built in Tooltips | |
if (tooltipsOptions.enabled || tooltipsOptions.custom) { | |
tooltip.initialize(); | |
tooltip._active = me.tooltipActive; | |
tooltip.update(true); | |
} | |
// Hover animations | |
tooltip.pivot(); | |
if (!me.animating) { | |
// If entering, leaving, or changing elements, animate the change via pivot | |
if (!helpers.arrayEquals(me.active, me.lastActive) || | |
!helpers.arrayEquals(me.tooltipActive, me.lastTooltipActive)) { | |
me.stop(); | |
if (tooltipsOptions.enabled || tooltipsOptions.custom) { | |
tooltip.update(true); | |
} | |
// We only need to render at this point. Updating will cause scales to be | |
// recomputed generating flicker & using more memory than necessary. | |
me.render(hoverOptions.animationDuration, true); | |
} | |
} | |
// Remember Last Actives | |
me.lastActive = me.active; | |
me.lastTooltipActive = me.tooltipActive; | |
return me; | |
} | |
}); | |
}; | |
},{}],23:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var noop = helpers.noop; | |
// Base class for all dataset controllers (line, bar, etc) | |
Chart.DatasetController = function(chart, datasetIndex) { | |
this.initialize.call(this, chart, datasetIndex); | |
}; | |
helpers.extend(Chart.DatasetController.prototype, { | |
/** | |
* Element type used to generate a meta dataset (e.g. Chart.element.Line). | |
* @type {Chart.core.element} | |
*/ | |
datasetElementType: null, | |
/** | |
* Element type used to generate a meta data (e.g. Chart.element.Point). | |
* @type {Chart.core.element} | |
*/ | |
dataElementType: null, | |
initialize: function(chart, datasetIndex) { | |
var me = this; | |
me.chart = chart; | |
me.index = datasetIndex; | |
me.linkScales(); | |
me.addElements(); | |
}, | |
updateIndex: function(datasetIndex) { | |
this.index = datasetIndex; | |
}, | |
linkScales: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var dataset = me.getDataset(); | |
if (meta.xAxisID === null) { | |
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; | |
} | |
if (meta.yAxisID === null) { | |
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; | |
} | |
}, | |
getDataset: function() { | |
return this.chart.data.datasets[this.index]; | |
}, | |
getMeta: function() { | |
return this.chart.getDatasetMeta(this.index); | |
}, | |
getScaleForId: function(scaleID) { | |
return this.chart.scales[scaleID]; | |
}, | |
reset: function() { | |
this.update(true); | |
}, | |
createMetaDataset: function() { | |
var me = this; | |
var type = me.datasetElementType; | |
return type && new type({ | |
_chart: me.chart.chart, | |
_datasetIndex: me.index | |
}); | |
}, | |
createMetaData: function(index) { | |
var me = this; | |
var type = me.dataElementType; | |
return type && new type({ | |
_chart: me.chart.chart, | |
_datasetIndex: me.index, | |
_index: index | |
}); | |
}, | |
addElements: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var data = me.getDataset().data || []; | |
var metaData = meta.data; | |
var i, ilen; | |
for (i=0, ilen=data.length; i<ilen; ++i) { | |
metaData[i] = metaData[i] || me.createMetaData(meta, i); | |
} | |
meta.dataset = meta.dataset || me.createMetaDataset(); | |
}, | |
addElementAndReset: function(index) { | |
var me = this; | |
var element = me.createMetaData(index); | |
me.getMeta().data.splice(index, 0, element); | |
me.updateElement(element, index, true); | |
}, | |
buildOrUpdateElements: function() { | |
// Handle the number of data points changing | |
var meta = this.getMeta(), | |
md = meta.data, | |
numData = this.getDataset().data.length, | |
numMetaData = md.length; | |
// Make sure that we handle number of datapoints changing | |
if (numData < numMetaData) { | |
// Remove excess bars for data points that have been removed | |
md.splice(numData, numMetaData - numData); | |
} else if (numData > numMetaData) { | |
// Add new elements | |
for (var index = numMetaData; index < numData; ++index) { | |
this.addElementAndReset(index); | |
} | |
} | |
}, | |
update: noop, | |
draw: function(ease) { | |
var easingDecimal = ease || 1; | |
helpers.each(this.getMeta().data, function(element) { | |
element.transition(easingDecimal).draw(); | |
}); | |
}, | |
removeHoverStyle: function(element, elementOpts) { | |
var dataset = this.chart.data.datasets[element._datasetIndex], | |
index = element._index, | |
custom = element.custom || {}, | |
valueOrDefault = helpers.getValueAtIndexOrDefault, | |
model = element._model; | |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); | |
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); | |
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); | |
}, | |
setHoverStyle: function(element) { | |
var dataset = this.chart.data.datasets[element._datasetIndex], | |
index = element._index, | |
custom = element.custom || {}, | |
valueOrDefault = helpers.getValueAtIndexOrDefault, | |
getHoverColor = helpers.getHoverColor, | |
model = element._model; | |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); | |
} | |
}); | |
Chart.DatasetController.extend = helpers.inherits; | |
}; | |
},{}],24:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.elements = {}; | |
Chart.Element = function(configuration) { | |
helpers.extend(this, configuration); | |
this.initialize.apply(this, arguments); | |
}; | |
helpers.extend(Chart.Element.prototype, { | |
initialize: function() { | |
this.hidden = false; | |
}, | |
pivot: function() { | |
var me = this; | |
if (!me._view) { | |
me._view = helpers.clone(me._model); | |
} | |
me._start = helpers.clone(me._view); | |
return me; | |
}, | |
transition: function(ease) { | |
var me = this; | |
if (!me._view) { | |
me._view = helpers.clone(me._model); | |
} | |
// No animation -> No Transition | |
if (ease === 1) { | |
me._view = me._model; | |
me._start = null; | |
return me; | |
} | |
if (!me._start) { | |
me.pivot(); | |
} | |
helpers.each(me._model, function(value, key) { | |
if (key[0] === '_') { | |
// Only non-underscored properties | |
} | |
// Init if doesn't exist | |
else if (!me._view.hasOwnProperty(key)) { | |
if (typeof value === 'number' && !isNaN(me._view[key])) { | |
me._view[key] = value * ease; | |
} else { | |
me._view[key] = value; | |
} | |
} | |
// No unnecessary computations | |
else if (value === me._view[key]) { | |
// It's the same! Woohoo! | |
} | |
// Color transitions if possible | |
else if (typeof value === 'string') { | |
try { | |
var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease); | |
me._view[key] = color.rgbString(); | |
} catch (err) { | |
me._view[key] = value; | |
} | |
} | |
// Number transitions | |
else if (typeof value === 'number') { | |
var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0; | |
me._view[key] = ((me._model[key] - startVal) * ease) + startVal; | |
} | |
// Everything else | |
else { | |
me._view[key] = value; | |
} | |
}, me); | |
return me; | |
}, | |
tooltipPosition: function() { | |
return { | |
x: this._model.x, | |
y: this._model.y | |
}; | |
}, | |
hasValue: function() { | |
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); | |
} | |
}); | |
Chart.Element.extend = helpers.inherits; | |
}; | |
},{}],25:[function(require,module,exports){ | |
/*global window: false */ | |
/*global document: false */ | |
"use strict"; | |
var color = require('chartjs-color'); | |
module.exports = function(Chart) { | |
//Global Chart helpers object for utility methods and classes | |
var helpers = Chart.helpers = {}; | |
//-- Basic js utility methods | |
helpers.each = function(loopable, callback, self, reverse) { | |
// Check to see if null or undefined firstly. | |
var i, len; | |
if (helpers.isArray(loopable)) { | |
len = loopable.length; | |
if (reverse) { | |
for (i = len - 1; i >= 0; i--) { | |
callback.call(self, loopable[i], i); | |
} | |
} else { | |
for (i = 0; i < len; i++) { | |
callback.call(self, loopable[i], i); | |
} | |
} | |
} else if (typeof loopable === 'object') { | |
var keys = Object.keys(loopable); | |
len = keys.length; | |
for (i = 0; i < len; i++) { | |
callback.call(self, loopable[keys[i]], keys[i]); | |
} | |
} | |
}; | |
helpers.clone = function(obj) { | |
var objClone = {}; | |
helpers.each(obj, function(value, key) { | |
if (helpers.isArray(value)) { | |
objClone[key] = value.slice(0); | |
} else if (typeof value === 'object' && value !== null) { | |
objClone[key] = helpers.clone(value); | |
} else { | |
objClone[key] = value; | |
} | |
}); | |
return objClone; | |
}; | |
helpers.extend = function(base) { | |
var setFn = function(value, key) { base[key] = value; }; | |
for (var i = 1, ilen = arguments.length; i < ilen; i++) { | |
helpers.each(arguments[i], setFn); | |
} | |
return base; | |
}; | |
// Need a special merge function to chart configs since they are now grouped | |
helpers.configMerge = function(_base) { | |
var base = helpers.clone(_base); | |
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { | |
helpers.each(extension, function(value, key) { | |
if (key === 'scales') { | |
// Scale config merging is complex. Add out own function here for that | |
base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value); | |
} else if (key === 'scale') { | |
// Used in polar area & radar charts since there is only one scale | |
base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value); | |
} else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) { | |
// In this case we have an array of objects replacing another array. Rather than doing a strict replace, | |
// merge. This allows easy scale option merging | |
var baseArray = base[key]; | |
helpers.each(value, function(valueObj, index) { | |
if (index < baseArray.length) { | |
if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) { | |
// Two objects are coming together. Do a merge of them. | |
baseArray[index] = helpers.configMerge(baseArray[index], valueObj); | |
} else { | |
// Just overwrite in this case since there is nothing to merge | |
baseArray[index] = valueObj; | |
} | |
} else { | |
baseArray.push(valueObj); // nothing to merge | |
} | |
}); | |
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { | |
// If we are overwriting an object with an object, do a merge of the properties. | |
base[key] = helpers.configMerge(base[key], value); | |
} else { | |
// can just overwrite the value in this case | |
base[key] = value; | |
} | |
}); | |
}); | |
return base; | |
}; | |
helpers.scaleMerge = function(_base, extension) { | |
var base = helpers.clone(_base); | |
helpers.each(extension, function(value, key) { | |
if (key === 'xAxes' || key === 'yAxes') { | |
// These properties are arrays of items | |
if (base.hasOwnProperty(key)) { | |
helpers.each(value, function(valueObj, index) { | |
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); | |
var axisDefaults = Chart.scaleService.getScaleDefaults(axisType); | |
if (index >= base[key].length || !base[key][index].type) { | |
base[key].push(helpers.configMerge(axisDefaults, valueObj)); | |
} else if (valueObj.type && valueObj.type !== base[key][index].type) { | |
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults | |
base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj); | |
} else { | |
// Type is the same | |
base[key][index] = helpers.configMerge(base[key][index], valueObj); | |
} | |
}); | |
} else { | |
base[key] = []; | |
helpers.each(value, function(valueObj) { | |
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); | |
base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj)); | |
}); | |
} | |
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { | |
// If we are overwriting an object with an object, do a merge of the properties. | |
base[key] = helpers.configMerge(base[key], value); | |
} else { | |
// can just overwrite the value in this case | |
base[key] = value; | |
} | |
}); | |
return base; | |
}; | |
helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) { | |
if (value === undefined || value === null) { | |
return defaultValue; | |
} | |
if (helpers.isArray(value)) { | |
return index < value.length ? value[index] : defaultValue; | |
} | |
return value; | |
}; | |
helpers.getValueOrDefault = function(value, defaultValue) { | |
return value === undefined ? defaultValue : value; | |
}; | |
helpers.indexOf = Array.prototype.indexOf? | |
function(array, item) { return array.indexOf(item); } : | |
function(array, item) { | |
for (var i = 0, ilen = array.length; i < ilen; ++i) { | |
if (array[i] === item) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
helpers.where = function(collection, filterCallback) { | |
if (helpers.isArray(collection) && Array.prototype.filter) { | |
return collection.filter(filterCallback); | |
} else { | |
var filtered = []; | |
helpers.each(collection, function(item) { | |
if (filterCallback(item)) { | |
filtered.push(item); | |
} | |
}); | |
return filtered; | |
} | |
}; | |
helpers.findIndex = Array.prototype.findIndex? | |
function(array, callback, scope) { return array.findIndex(callback, scope); } : | |
function(array, callback, scope) { | |
scope = scope === undefined? array : scope; | |
for (var i = 0, ilen = array.length; i < ilen; ++i) { | |
if (callback.call(scope, array[i], i, array)) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { | |
// Default to start of the array | |
if (startIndex === undefined || startIndex === null) { | |
startIndex = -1; | |
} | |
for (var i = startIndex + 1; i < arrayToSearch.length; i++) { | |
var currentItem = arrayToSearch[i]; | |
if (filterCallback(currentItem)) { | |
return currentItem; | |
} | |
} | |
}; | |
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { | |
// Default to end of the array | |
if (startIndex === undefined || startIndex === null) { | |
startIndex = arrayToSearch.length; | |
} | |
for (var i = startIndex - 1; i >= 0; i--) { | |
var currentItem = arrayToSearch[i]; | |
if (filterCallback(currentItem)) { | |
return currentItem; | |
} | |
} | |
}; | |
helpers.inherits = function(extensions) { | |
//Basic javascript inheritance based on the model created in Backbone.js | |
var parent = this; | |
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() { | |
return parent.apply(this, arguments); | |
}; | |
var Surrogate = function() { | |
this.constructor = ChartElement; | |
}; | |
Surrogate.prototype = parent.prototype; | |
ChartElement.prototype = new Surrogate(); | |
ChartElement.extend = helpers.inherits; | |
if (extensions) { | |
helpers.extend(ChartElement.prototype, extensions); | |
} | |
ChartElement.__super__ = parent.prototype; | |
return ChartElement; | |
}; | |
helpers.noop = function() {}; | |
helpers.uid = (function() { | |
var id = 0; | |
return function() { | |
return id++; | |
}; | |
})(); | |
//-- Math methods | |
helpers.isNumber = function(n) { | |
return !isNaN(parseFloat(n)) && isFinite(n); | |
}; | |
helpers.almostEquals = function(x, y, epsilon) { | |
return Math.abs(x - y) < epsilon; | |
}; | |
helpers.max = function(array) { | |
return array.reduce(function(max, value) { | |
if (!isNaN(value)) { | |
return Math.max(max, value); | |
} else { | |
return max; | |
} | |
}, Number.NEGATIVE_INFINITY); | |
}; | |
helpers.min = function(array) { | |
return array.reduce(function(min, value) { | |
if (!isNaN(value)) { | |
return Math.min(min, value); | |
} else { | |
return min; | |
} | |
}, Number.POSITIVE_INFINITY); | |
}; | |
helpers.sign = Math.sign? | |
function(x) { return Math.sign(x); } : | |
function(x) { | |
x = +x; // convert to a number | |
if (x === 0 || isNaN(x)) { | |
return x; | |
} | |
return x > 0 ? 1 : -1; | |
}; | |
helpers.log10 = Math.log10? | |
function(x) { return Math.log10(x); } : | |
function(x) { | |
return Math.log(x) / Math.LN10; | |
}; | |
helpers.toRadians = function(degrees) { | |
return degrees * (Math.PI / 180); | |
}; | |
helpers.toDegrees = function(radians) { | |
return radians * (180 / Math.PI); | |
}; | |
// Gets the angle from vertical upright to the point about a centre. | |
helpers.getAngleFromPoint = function(centrePoint, anglePoint) { | |
var distanceFromXCenter = anglePoint.x - centrePoint.x, | |
distanceFromYCenter = anglePoint.y - centrePoint.y, | |
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); | |
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); | |
if (angle < (-0.5 * Math.PI)) { | |
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] | |
} | |
return { | |
angle: angle, | |
distance: radialDistanceFromCenter | |
}; | |
}; | |
helpers.aliasPixel = function(pixelWidth) { | |
return (pixelWidth % 2 === 0) ? 0 : 0.5; | |
}; | |
helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { | |
//Props to Rob Spencer at scaled innovation for his post on splining between points | |
//http://scaledinnovation.com/analytics/splines/aboutSplines.html | |
// This function must also respect "skipped" points | |
var previous = firstPoint.skip ? middlePoint : firstPoint, | |
current = middlePoint, | |
next = afterPoint.skip ? middlePoint : afterPoint; | |
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); | |
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); | |
var s01 = d01 / (d01 + d12); | |
var s12 = d12 / (d01 + d12); | |
// If all points are the same, s01 & s02 will be inf | |
s01 = isNaN(s01) ? 0 : s01; | |
s12 = isNaN(s12) ? 0 : s12; | |
var fa = t * s01; // scaling factor for triangle Ta | |
var fb = t * s12; | |
return { | |
previous: { | |
x: current.x - fa * (next.x - previous.x), | |
y: current.y - fa * (next.y - previous.y) | |
}, | |
next: { | |
x: current.x + fb * (next.x - previous.x), | |
y: current.y + fb * (next.y - previous.y) | |
} | |
}; | |
}; | |
helpers.nextItem = function(collection, index, loop) { | |
if (loop) { | |
return index >= collection.length - 1 ? collection[0] : collection[index + 1]; | |
} | |
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; | |
}; | |
helpers.previousItem = function(collection, index, loop) { | |
if (loop) { | |
return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; | |
} | |
return index <= 0 ? collection[0] : collection[index - 1]; | |
}; | |
// Implementation of the nice number algorithm used in determining where axis labels will go | |
helpers.niceNum = function(range, round) { | |
var exponent = Math.floor(helpers.log10(range)); | |
var fraction = range / Math.pow(10, exponent); | |
var niceFraction; | |
if (round) { | |
if (fraction < 1.5) { | |
niceFraction = 1; | |
} else if (fraction < 3) { | |
niceFraction = 2; | |
} else if (fraction < 7) { | |
niceFraction = 5; | |
} else { | |
niceFraction = 10; | |
} | |
} else { | |
if (fraction <= 1.0) { | |
niceFraction = 1; | |
} else if (fraction <= 2) { | |
niceFraction = 2; | |
} else if (fraction <= 5) { | |
niceFraction = 5; | |
} else { | |
niceFraction = 10; | |
} | |
} | |
return niceFraction * Math.pow(10, exponent); | |
}; | |
//Easing functions adapted from Robert Penner's easing equations | |
//http://www.robertpenner.com/easing/ | |
var easingEffects = helpers.easingEffects = { | |
linear: function(t) { | |
return t; | |
}, | |
easeInQuad: function(t) { | |
return t * t; | |
}, | |
easeOutQuad: function(t) { | |
return -1 * t * (t - 2); | |
}, | |
easeInOutQuad: function(t) { | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * t * t; | |
} | |
return -1 / 2 * ((--t) * (t - 2) - 1); | |
}, | |
easeInCubic: function(t) { | |
return t * t * t; | |
}, | |
easeOutCubic: function(t) { | |
return 1 * ((t = t / 1 - 1) * t * t + 1); | |
}, | |
easeInOutCubic: function(t) { | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * t * t * t; | |
} | |
return 1 / 2 * ((t -= 2) * t * t + 2); | |
}, | |
easeInQuart: function(t) { | |
return t * t * t * t; | |
}, | |
easeOutQuart: function(t) { | |
return -1 * ((t = t / 1 - 1) * t * t * t - 1); | |
}, | |
easeInOutQuart: function(t) { | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * t * t * t * t; | |
} | |
return -1 / 2 * ((t -= 2) * t * t * t - 2); | |
}, | |
easeInQuint: function(t) { | |
return 1 * (t /= 1) * t * t * t * t; | |
}, | |
easeOutQuint: function(t) { | |
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); | |
}, | |
easeInOutQuint: function(t) { | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * t * t * t * t * t; | |
} | |
return 1 / 2 * ((t -= 2) * t * t * t * t + 2); | |
}, | |
easeInSine: function(t) { | |
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; | |
}, | |
easeOutSine: function(t) { | |
return 1 * Math.sin(t / 1 * (Math.PI / 2)); | |
}, | |
easeInOutSine: function(t) { | |
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); | |
}, | |
easeInExpo: function(t) { | |
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); | |
}, | |
easeOutExpo: function(t) { | |
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); | |
}, | |
easeInOutExpo: function(t) { | |
if (t === 0) { | |
return 0; | |
} | |
if (t === 1) { | |
return 1; | |
} | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * Math.pow(2, 10 * (t - 1)); | |
} | |
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); | |
}, | |
easeInCirc: function(t) { | |
if (t >= 1) { | |
return t; | |
} | |
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); | |
}, | |
easeOutCirc: function(t) { | |
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); | |
}, | |
easeInOutCirc: function(t) { | |
if ((t /= 1 / 2) < 1) { | |
return -1 / 2 * (Math.sqrt(1 - t * t) - 1); | |
} | |
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); | |
}, | |
easeInElastic: function(t) { | |
var s = 1.70158; | |
var p = 0; | |
var a = 1; | |
if (t === 0) { | |
return 0; | |
} | |
if ((t /= 1) === 1) { | |
return 1; | |
} | |
if (!p) { | |
p = 1 * 0.3; | |
} | |
if (a < Math.abs(1)) { | |
a = 1; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(1 / a); | |
} | |
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); | |
}, | |
easeOutElastic: function(t) { | |
var s = 1.70158; | |
var p = 0; | |
var a = 1; | |
if (t === 0) { | |
return 0; | |
} | |
if ((t /= 1) === 1) { | |
return 1; | |
} | |
if (!p) { | |
p = 1 * 0.3; | |
} | |
if (a < Math.abs(1)) { | |
a = 1; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(1 / a); | |
} | |
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; | |
}, | |
easeInOutElastic: function(t) { | |
var s = 1.70158; | |
var p = 0; | |
var a = 1; | |
if (t === 0) { | |
return 0; | |
} | |
if ((t /= 1 / 2) === 2) { | |
return 1; | |
} | |
if (!p) { | |
p = 1 * (0.3 * 1.5); | |
} | |
if (a < Math.abs(1)) { | |
a = 1; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(1 / a); | |
} | |
if (t < 1) { | |
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); | |
} | |
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; | |
}, | |
easeInBack: function(t) { | |
var s = 1.70158; | |
return 1 * (t /= 1) * t * ((s + 1) * t - s); | |
}, | |
easeOutBack: function(t) { | |
var s = 1.70158; | |
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); | |
}, | |
easeInOutBack: function(t) { | |
var s = 1.70158; | |
if ((t /= 1 / 2) < 1) { | |
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); | |
} | |
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); | |
}, | |
easeInBounce: function(t) { | |
return 1 - easingEffects.easeOutBounce(1 - t); | |
}, | |
easeOutBounce: function(t) { | |
if ((t /= 1) < (1 / 2.75)) { | |
return 1 * (7.5625 * t * t); | |
} else if (t < (2 / 2.75)) { | |
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); | |
} else if (t < (2.5 / 2.75)) { | |
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); | |
} else { | |
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); | |
} | |
}, | |
easeInOutBounce: function(t) { | |
if (t < 1 / 2) { | |
return easingEffects.easeInBounce(t * 2) * 0.5; | |
} | |
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; | |
} | |
}; | |
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
helpers.requestAnimFrame = (function() { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback) { | |
return window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
helpers.cancelAnimFrame = (function() { | |
return window.cancelAnimationFrame || | |
window.webkitCancelAnimationFrame || | |
window.mozCancelAnimationFrame || | |
window.oCancelAnimationFrame || | |
window.msCancelAnimationFrame || | |
function(callback) { | |
return window.clearTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
//-- DOM methods | |
helpers.getRelativePosition = function(evt, chart) { | |
var mouseX, mouseY; | |
var e = evt.originalEvent || evt, | |
canvas = evt.currentTarget || evt.srcElement, | |
boundingRect = canvas.getBoundingClientRect(); | |
var touches = e.touches; | |
if (touches && touches.length > 0) { | |
mouseX = touches[0].clientX; | |
mouseY = touches[0].clientY; | |
} else { | |
mouseX = e.clientX; | |
mouseY = e.clientY; | |
} | |
// Scale mouse coordinates into canvas coordinates | |
// by following the pattern laid out by 'jerryj' in the comments of | |
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ | |
var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); | |
var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); | |
var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); | |
var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); | |
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; | |
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; | |
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However | |
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here | |
mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); | |
mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); | |
return { | |
x: mouseX, | |
y: mouseY | |
}; | |
}; | |
helpers.addEvent = function(node, eventType, method) { | |
if (node.addEventListener) { | |
node.addEventListener(eventType, method); | |
} else if (node.attachEvent) { | |
node.attachEvent("on" + eventType, method); | |
} else { | |
node["on" + eventType] = method; | |
} | |
}; | |
helpers.removeEvent = function(node, eventType, handler) { | |
if (node.removeEventListener) { | |
node.removeEventListener(eventType, handler, false); | |
} else if (node.detachEvent) { | |
node.detachEvent("on" + eventType, handler); | |
} else { | |
node["on" + eventType] = helpers.noop; | |
} | |
}; | |
helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) { | |
// Create the events object if it's not already present | |
var events = chartInstance.events = chartInstance.events || {}; | |
helpers.each(arrayOfEvents, function(eventName) { | |
events[eventName] = function() { | |
handler.apply(chartInstance, arguments); | |
}; | |
helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]); | |
}); | |
}; | |
helpers.unbindEvents = function(chartInstance, arrayOfEvents) { | |
var canvas = chartInstance.chart.canvas; | |
helpers.each(arrayOfEvents, function(handler, eventName) { | |
helpers.removeEvent(canvas, eventName, handler); | |
}); | |
}; | |
// Private helper function to convert max-width/max-height values that may be percentages into a number | |
function parseMaxStyle(styleValue, node, parentProperty) { | |
var valueInPixels; | |
if (typeof(styleValue) === 'string') { | |
valueInPixels = parseInt(styleValue, 10); | |
if (styleValue.indexOf('%') != -1) { | |
// percentage * size in dimension | |
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; | |
} | |
} else { | |
valueInPixels = styleValue; | |
} | |
return valueInPixels; | |
} | |
/** | |
* Returns if the given value contains an effective constraint. | |
* @private | |
*/ | |
function isConstrainedValue(value) { | |
return value !== undefined && value !== null && value !== 'none'; | |
} | |
// Private helper to get a constraint dimension | |
// @param domNode : the node to check the constraint on | |
// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) | |
// @param percentageProperty : property of parent to use when calculating width as a percentage | |
// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser | |
function getConstraintDimension(domNode, maxStyle, percentageProperty) { | |
var view = document.defaultView; | |
var parentNode = domNode.parentNode; | |
var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; | |
var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; | |
var hasCNode = isConstrainedValue(constrainedNode); | |
var hasCContainer = isConstrainedValue(constrainedContainer); | |
var infinity = Number.POSITIVE_INFINITY; | |
if (hasCNode || hasCContainer) { | |
return Math.min( | |
hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, | |
hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); | |
} | |
return 'none'; | |
} | |
// returns Number or undefined if no constraint | |
helpers.getConstraintWidth = function(domNode) { | |
return getConstraintDimension(domNode, 'max-width', 'clientWidth'); | |
}; | |
// returns Number or undefined if no constraint | |
helpers.getConstraintHeight = function(domNode) { | |
return getConstraintDimension(domNode, 'max-height', 'clientHeight'); | |
}; | |
helpers.getMaximumWidth = function(domNode) { | |
var container = domNode.parentNode; | |
var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right')); | |
var w = container.clientWidth - padding; | |
var cw = helpers.getConstraintWidth(domNode); | |
return isNaN(cw)? w : Math.min(w, cw); | |
}; | |
helpers.getMaximumHeight = function(domNode) { | |
var container = domNode.parentNode; | |
var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom')); | |
var h = container.clientHeight - padding; | |
var ch = helpers.getConstraintHeight(domNode); | |
return isNaN(ch)? h : Math.min(h, ch); | |
}; | |
helpers.getStyle = function(el, property) { | |
return el.currentStyle ? | |
el.currentStyle[property] : | |
document.defaultView.getComputedStyle(el, null).getPropertyValue(property); | |
}; | |
helpers.retinaScale = function(chart) { | |
var ctx = chart.ctx; | |
var canvas = chart.canvas; | |
var width = canvas.width; | |
var height = canvas.height; | |
var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1; | |
if (pixelRatio !== 1) { | |
canvas.height = height * pixelRatio; | |
canvas.width = width * pixelRatio; | |
ctx.scale(pixelRatio, pixelRatio); | |
// Store the device pixel ratio so that we can go backwards in `destroy`. | |
// The devicePixelRatio changes with zoom, so there are no guarantees that it is the same | |
// when destroy is called | |
chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio; | |
} | |
canvas.style.width = width + 'px'; | |
canvas.style.height = height + 'px'; | |
}; | |
//-- Canvas methods | |
helpers.clear = function(chart) { | |
chart.ctx.clearRect(0, 0, chart.width, chart.height); | |
}; | |
helpers.fontString = function(pixelSize, fontStyle, fontFamily) { | |
return fontStyle + " " + pixelSize + "px " + fontFamily; | |
}; | |
helpers.longestText = function(ctx, font, arrayOfThings, cache) { | |
cache = cache || {}; | |
var data = cache.data = cache.data || {}; | |
var gc = cache.garbageCollect = cache.garbageCollect || []; | |
if (cache.font !== font) { | |
data = cache.data = {}; | |
gc = cache.garbageCollect = []; | |
cache.font = font; | |
} | |
ctx.font = font; | |
var longest = 0; | |
helpers.each(arrayOfThings, function(thing) { | |
// Undefined strings and arrays should not be measured | |
if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { | |
longest = helpers.measureText(ctx, data, gc, longest, thing); | |
} else if (helpers.isArray(thing)) { | |
// if it is an array lets measure each element | |
// to do maybe simplify this function a bit so we can do this more recursively? | |
helpers.each(thing, function(nestedThing) { | |
// Undefined strings and arrays should not be measured | |
if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { | |
longest = helpers.measureText(ctx, data, gc, longest, nestedThing); | |
} | |
}); | |
} | |
}); | |
var gcLen = gc.length / 2; | |
if (gcLen > arrayOfThings.length) { | |
for (var i = 0; i < gcLen; i++) { | |
delete data[gc[i]]; | |
} | |
gc.splice(0, gcLen); | |
} | |
return longest; | |
}; | |
helpers.measureText = function (ctx, data, gc, longest, string) { | |
var textWidth = data[string]; | |
if (!textWidth) { | |
textWidth = data[string] = ctx.measureText(string).width; | |
gc.push(string); | |
} | |
if (textWidth > longest) { | |
longest = textWidth; | |
} | |
return longest; | |
}; | |
helpers.numberOfLabelLines = function(arrayOfThings) { | |
var numberOfLines = 1; | |
helpers.each(arrayOfThings, function(thing) { | |
if (helpers.isArray(thing)) { | |
if (thing.length > numberOfLines) { | |
numberOfLines = thing.length; | |
} | |
} | |
}); | |
return numberOfLines; | |
}; | |
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { | |
ctx.beginPath(); | |
ctx.moveTo(x + radius, y); | |
ctx.lineTo(x + width - radius, y); | |
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); | |
ctx.lineTo(x + width, y + height - radius); | |
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); | |
ctx.lineTo(x + radius, y + height); | |
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); | |
ctx.lineTo(x, y + radius); | |
ctx.quadraticCurveTo(x, y, x + radius, y); | |
ctx.closePath(); | |
}; | |
helpers.color = function(c) { | |
if (!color) { | |
console.log('Color.js not found!'); | |
return c; | |
} | |
/* global CanvasGradient */ | |
if (c instanceof CanvasGradient) { | |
return color(Chart.defaults.global.defaultColor); | |
} | |
return color(c); | |
}; | |
helpers.addResizeListener = function(node, callback) { | |
// Hide an iframe before the node | |
var hiddenIframe = document.createElement('iframe'); | |
var hiddenIframeClass = 'chartjs-hidden-iframe'; | |
if (hiddenIframe.classlist) { | |
// can use classlist | |
hiddenIframe.classlist.add(hiddenIframeClass); | |
} else { | |
hiddenIframe.setAttribute('class', hiddenIframeClass); | |
} | |
// Set the style | |
var style = hiddenIframe.style; | |
style.width = '100%'; | |
style.display = 'block'; | |
style.border = 0; | |
style.height = 0; | |
style.margin = 0; | |
style.position = 'absolute'; | |
style.left = 0; | |
style.right = 0; | |
style.top = 0; | |
style.bottom = 0; | |
// Insert the iframe so that contentWindow is available | |
node.insertBefore(hiddenIframe, node.firstChild); | |
(hiddenIframe.contentWindow || hiddenIframe).onresize = function() { | |
if (callback) { | |
callback(); | |
} | |
}; | |
}; | |
helpers.removeResizeListener = function(node) { | |
var hiddenIframe = node.querySelector('.chartjs-hidden-iframe'); | |
// Remove the resize detect iframe | |
if (hiddenIframe) { | |
hiddenIframe.parentNode.removeChild(hiddenIframe); | |
} | |
}; | |
helpers.isArray = Array.isArray? | |
function(obj) { return Array.isArray(obj); } : | |
function(obj) { | |
return Object.prototype.toString.call(obj) === '[object Array]'; | |
}; | |
//! @see http://stackoverflow.com/a/14853974 | |
helpers.arrayEquals = function(a0, a1) { | |
var i, ilen, v0, v1; | |
if (!a0 || !a1 || a0.length != a1.length) { | |
return false; | |
} | |
for (i = 0, ilen=a0.length; i < ilen; ++i) { | |
v0 = a0[i]; | |
v1 = a1[i]; | |
if (v0 instanceof Array && v1 instanceof Array) { | |
if (!helpers.arrayEquals(v0, v1)) { | |
return false; | |
} | |
} else if (v0 != v1) { | |
// NOTE: two different object instances will never be equal: {x:20} != {x:20} | |
return false; | |
} | |
} | |
return true; | |
}; | |
helpers.callCallback = function(fn, args, _tArg) { | |
if (fn && typeof fn.call === 'function') { | |
fn.apply(_tArg, args); | |
} | |
}; | |
helpers.getHoverColor = function(color) { | |
/* global CanvasPattern */ | |
return (color instanceof CanvasPattern) ? | |
color : | |
helpers.color(color).saturate(0.5).darken(0.1).rgbString(); | |
}; | |
}; | |
},{"chartjs-color":1}],26:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function() { | |
//Occupy the global variable of Chart, and create a simple base class | |
var Chart = function(context, config) { | |
var me = this; | |
var helpers = Chart.helpers; | |
me.config = config || { | |
data: { | |
datasets: [] | |
} | |
}; | |
// Support a jQuery'd canvas element | |
if (context.length && context[0].getContext) { | |
context = context[0]; | |
} | |
// Support a canvas domnode | |
if (context.getContext) { | |
context = context.getContext("2d"); | |
} | |
me.ctx = context; | |
me.canvas = context.canvas; | |
context.canvas.style.display = context.canvas.style.display || 'block'; | |
// Figure out what the size of the chart will be. | |
// If the canvas has a specified width and height, we use those else | |
// we look to see if the canvas node has a CSS width and height. | |
// If there is still no height, fill the parent container | |
me.width = context.canvas.width || parseInt(helpers.getStyle(context.canvas, 'width'), 10) || helpers.getMaximumWidth(context.canvas); | |
me.height = context.canvas.height || parseInt(helpers.getStyle(context.canvas, 'height'), 10) || helpers.getMaximumHeight(context.canvas); | |
me.aspectRatio = me.width / me.height; | |
if (isNaN(me.aspectRatio) || isFinite(me.aspectRatio) === false) { | |
// If the canvas has no size, try and figure out what the aspect ratio will be. | |
// Some charts prefer square canvases (pie, radar, etc). If that is specified, use that | |
// else use the canvas default ratio of 2 | |
me.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2; | |
} | |
// Store the original style of the element so we can set it back | |
me.originalCanvasStyleWidth = context.canvas.style.width; | |
me.originalCanvasStyleHeight = context.canvas.style.height; | |
// High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. | |
helpers.retinaScale(me); | |
me.controller = new Chart.Controller(me); | |
// Always bind this so that if the responsive state changes we still work | |
helpers.addResizeListener(context.canvas.parentNode, function() { | |
if (me.controller && me.controller.config.options.responsive) { | |
me.controller.resize(); | |
} | |
}); | |
return me.controller ? me.controller : me; | |
}; | |
//Globally expose the defaults to allow for user updating/changing | |
Chart.defaults = { | |
global: { | |
responsive: true, | |
responsiveAnimationDuration: 0, | |
maintainAspectRatio: true, | |
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"], | |
hover: { | |
onHover: null, | |
mode: 'single', | |
animationDuration: 400 | |
}, | |
onClick: null, | |
defaultColor: 'rgba(0,0,0,0.1)', | |
defaultFontColor: '#666', | |
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", | |
defaultFontSize: 12, | |
defaultFontStyle: 'normal', | |
showLines: true, | |
// Element defaults defined in element extensions | |
elements: {}, | |
// Legend callback string | |
legendCallback: function(chart) { | |
var text = []; | |
text.push('<ul class="' + chart.id + '-legend">'); | |
for (var i = 0; i < chart.data.datasets.length; i++) { | |
text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); | |
if (chart.data.datasets[i].label) { | |
text.push(chart.data.datasets[i].label); | |
} | |
text.push('</li>'); | |
} | |
text.push('</ul>'); | |
return text.join(""); | |
} | |
} | |
}; | |
Chart.Chart = Chart; | |
return Chart; | |
}; | |
},{}],27:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
// The layout service is very self explanatory. It's responsible for the layout within a chart. | |
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need | |
// It is this service's responsibility of carrying out that layout. | |
Chart.layoutService = { | |
defaults: {}, | |
// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins. | |
addBox: function(chartInstance, box) { | |
if (!chartInstance.boxes) { | |
chartInstance.boxes = []; | |
} | |
chartInstance.boxes.push(box); | |
}, | |
removeBox: function(chartInstance, box) { | |
if (!chartInstance.boxes) { | |
return; | |
} | |
chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1); | |
}, | |
// The most important function | |
update: function(chartInstance, width, height) { | |
if (!chartInstance) { | |
return; | |
} | |
var xPadding = 0; | |
var yPadding = 0; | |
var leftBoxes = helpers.where(chartInstance.boxes, function(box) { | |
return box.options.position === "left"; | |
}); | |
var rightBoxes = helpers.where(chartInstance.boxes, function(box) { | |
return box.options.position === "right"; | |
}); | |
var topBoxes = helpers.where(chartInstance.boxes, function(box) { | |
return box.options.position === "top"; | |
}); | |
var bottomBoxes = helpers.where(chartInstance.boxes, function(box) { | |
return box.options.position === "bottom"; | |
}); | |
// Boxes that overlay the chartarea such as the radialLinear scale | |
var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) { | |
return box.options.position === "chartArea"; | |
}); | |
// Ensure that full width boxes are at the very top / bottom | |
topBoxes.sort(function(a, b) { | |
return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0); | |
}); | |
bottomBoxes.sort(function(a, b) { | |
return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0); | |
}); | |
// Essentially we now have any number of boxes on each of the 4 sides. | |
// Our canvas looks like the following. | |
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and | |
// B1 is the bottom axis | |
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays | |
// These locations are single-box locations only, when trying to register a chartArea location that is already taken, | |
// an error will be thrown. | |
// | |
// |----------------------------------------------------| | |
// | T1 (Full Width) | | |
// |----------------------------------------------------| | |
// | | | T2 | | | |
// | |----|-------------------------------------|----| | |
// | | | C1 | | C2 | | | |
// | | |----| |----| | | |
// | | | | | | |
// | L1 | L2 | ChartArea (C0) | R1 | | |
// | | | | | | |
// | | |----| |----| | | |
// | | | C3 | | C4 | | | |
// | |----|-------------------------------------|----| | |
// | | | B1 | | | |
// |----------------------------------------------------| | |
// | B2 (Full Width) | | |
// |----------------------------------------------------| | |
// | |
// What we do to find the best sizing, we do the following | |
// 1. Determine the minimum size of the chart area. | |
// 2. Split the remaining width equally between each vertical axis | |
// 3. Split the remaining height equally between each horizontal axis | |
// 4. Give each layout the maximum size it can be. The layout will return it's minimum size | |
// 5. Adjust the sizes of each axis based on it's minimum reported size. | |
// 6. Refit each axis | |
// 7. Position each axis in the final location | |
// 8. Tell the chart the final location of the chart area | |
// 9. Tell any axes that overlay the chart area the positions of the chart area | |
// Step 1 | |
var chartWidth = width - (2 * xPadding); | |
var chartHeight = height - (2 * yPadding); | |
var chartAreaWidth = chartWidth / 2; // min 50% | |
var chartAreaHeight = chartHeight / 2; // min 50% | |
// Step 2 | |
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); | |
// Step 3 | |
var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); | |
// Step 4 | |
var maxChartAreaWidth = chartWidth; | |
var maxChartAreaHeight = chartHeight; | |
var minBoxSizes = []; | |
helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); | |
function getMinimumBoxSize(box) { | |
var minSize; | |
var isHorizontal = box.isHorizontal(); | |
if (isHorizontal) { | |
minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); | |
maxChartAreaHeight -= minSize.height; | |
} else { | |
minSize = box.update(verticalBoxWidth, chartAreaHeight); | |
maxChartAreaWidth -= minSize.width; | |
} | |
minBoxSizes.push({ | |
horizontal: isHorizontal, | |
minSize: minSize, | |
box: box | |
}); | |
} | |
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could | |
// be if the axes are drawn at their minimum sizes. | |
// Steps 5 & 6 | |
var totalLeftBoxesWidth = xPadding; | |
var totalRightBoxesWidth = xPadding; | |
var totalTopBoxesHeight = yPadding; | |
var totalBottomBoxesHeight = yPadding; | |
// Update, and calculate the left and right margins for the horizontal boxes | |
helpers.each(leftBoxes.concat(rightBoxes), fitBox); | |
helpers.each(leftBoxes, function(box) { | |
totalLeftBoxesWidth += box.width; | |
}); | |
helpers.each(rightBoxes, function(box) { | |
totalRightBoxesWidth += box.width; | |
}); | |
// Set the Left and Right margins for the horizontal boxes | |
helpers.each(topBoxes.concat(bottomBoxes), fitBox); | |
// Function to fit a box | |
function fitBox(box) { | |
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { | |
return minBoxSize.box === box; | |
}); | |
if (minBoxSize) { | |
if (box.isHorizontal()) { | |
var scaleMargin = { | |
left: totalLeftBoxesWidth, | |
right: totalRightBoxesWidth, | |
top: 0, | |
bottom: 0 | |
}; | |
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends | |
// on the margin. Sometimes they need to increase in size slightly | |
box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); | |
} else { | |
box.update(minBoxSize.minSize.width, maxChartAreaHeight); | |
} | |
} | |
} | |
// Figure out how much margin is on the top and bottom of the vertical boxes | |
helpers.each(topBoxes, function(box) { | |
totalTopBoxesHeight += box.height; | |
}); | |
helpers.each(bottomBoxes, function(box) { | |
totalBottomBoxesHeight += box.height; | |
}); | |
// Let the left layout know the final margin | |
helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); | |
function finalFitVerticalBox(box) { | |
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { | |
return minBoxSize.box === box; | |
}); | |
var scaleMargin = { | |
left: 0, | |
right: 0, | |
top: totalTopBoxesHeight, | |
bottom: totalBottomBoxesHeight | |
}; | |
if (minBoxSize) { | |
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); | |
} | |
} | |
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) | |
totalLeftBoxesWidth = xPadding; | |
totalRightBoxesWidth = xPadding; | |
totalTopBoxesHeight = yPadding; | |
totalBottomBoxesHeight = yPadding; | |
helpers.each(leftBoxes, function(box) { | |
totalLeftBoxesWidth += box.width; | |
}); | |
helpers.each(rightBoxes, function(box) { | |
totalRightBoxesWidth += box.width; | |
}); | |
helpers.each(topBoxes, function(box) { | |
totalTopBoxesHeight += box.height; | |
}); | |
helpers.each(bottomBoxes, function(box) { | |
totalBottomBoxesHeight += box.height; | |
}); | |
// Figure out if our chart area changed. This would occur if the dataset layout label rotation | |
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do | |
// without calling `fit` again | |
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; | |
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; | |
if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { | |
helpers.each(leftBoxes, function(box) { | |
box.height = newMaxChartAreaHeight; | |
}); | |
helpers.each(rightBoxes, function(box) { | |
box.height = newMaxChartAreaHeight; | |
}); | |
helpers.each(topBoxes, function(box) { | |
if (!box.options.fullWidth) { | |
box.width = newMaxChartAreaWidth; | |
} | |
}); | |
helpers.each(bottomBoxes, function(box) { | |
if (!box.options.fullWidth) { | |
box.width = newMaxChartAreaWidth; | |
} | |
}); | |
maxChartAreaHeight = newMaxChartAreaHeight; | |
maxChartAreaWidth = newMaxChartAreaWidth; | |
} | |
// Step 7 - Position the boxes | |
var left = xPadding; | |
var top = yPadding; | |
helpers.each(leftBoxes.concat(topBoxes), placeBox); | |
// Account for chart width and height | |
left += maxChartAreaWidth; | |
top += maxChartAreaHeight; | |
helpers.each(rightBoxes, placeBox); | |
helpers.each(bottomBoxes, placeBox); | |
function placeBox(box) { | |
if (box.isHorizontal()) { | |
box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth; | |
box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth; | |
box.top = top; | |
box.bottom = top + box.height; | |
// Move to next point | |
top = box.bottom; | |
} else { | |
box.left = left; | |
box.right = left + box.width; | |
box.top = totalTopBoxesHeight; | |
box.bottom = totalTopBoxesHeight + maxChartAreaHeight; | |
// Move to next point | |
left = box.right; | |
} | |
} | |
// Step 8 | |
chartInstance.chartArea = { | |
left: totalLeftBoxesWidth, | |
top: totalTopBoxesHeight, | |
right: totalLeftBoxesWidth + maxChartAreaWidth, | |
bottom: totalTopBoxesHeight + maxChartAreaHeight | |
}; | |
// Step 9 | |
helpers.each(chartAreaBoxes, function(box) { | |
box.left = chartInstance.chartArea.left; | |
box.top = chartInstance.chartArea.top; | |
box.right = chartInstance.chartArea.right; | |
box.bottom = chartInstance.chartArea.bottom; | |
box.update(maxChartAreaWidth, maxChartAreaHeight); | |
}); | |
} | |
}; | |
}; | |
},{}],28:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var noop = helpers.noop; | |
Chart.defaults.global.legend = { | |
display: true, | |
position: 'top', | |
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) | |
reverse: false, | |
// a callback that will handle | |
onClick: function(e, legendItem) { | |
var index = legendItem.datasetIndex; | |
var ci = this.chart; | |
var meta = ci.getDatasetMeta(index); | |
// See controller.isDatasetVisible comment | |
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null; | |
// We hid a dataset ... rerender the chart | |
ci.update(); | |
}, | |
labels: { | |
boxWidth: 40, | |
padding: 10, | |
// Generates labels shown in the legend | |
// Valid properties to return: | |
// text : text to display | |
// fillStyle : fill of coloured box | |
// strokeStyle: stroke of coloured box | |
// hidden : if this legend item refers to a hidden item | |
// lineCap : cap style for line | |
// lineDash | |
// lineDashOffset : | |
// lineJoin : | |
// lineWidth : | |
generateLabels: function(chart) { | |
var data = chart.data; | |
return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { | |
return { | |
text: dataset.label, | |
fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), | |
hidden: !chart.isDatasetVisible(i), | |
lineCap: dataset.borderCapStyle, | |
lineDash: dataset.borderDash, | |
lineDashOffset: dataset.borderDashOffset, | |
lineJoin: dataset.borderJoinStyle, | |
lineWidth: dataset.borderWidth, | |
strokeStyle: dataset.borderColor, | |
pointStyle: dataset.pointStyle, | |
// Below is extra data used for toggling the datasets | |
datasetIndex: i | |
}; | |
}, this) : []; | |
} | |
} | |
}; | |
Chart.Legend = Chart.Element.extend({ | |
initialize: function(config) { | |
helpers.extend(this, config); | |
// Contains hit boxes for each dataset (in dataset order) | |
this.legendHitBoxes = []; | |
// Are we in doughnut mode which has a different data type | |
this.doughnutMode = false; | |
}, | |
// These methods are ordered by lifecyle. Utilities then follow. | |
// Any function defined here is inherited by all legend types. | |
// Any function can be extended by the legend type | |
beforeUpdate: noop, | |
update: function(maxWidth, maxHeight, margins) { | |
var me = this; | |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) | |
me.beforeUpdate(); | |
// Absorb the master measurements | |
me.maxWidth = maxWidth; | |
me.maxHeight = maxHeight; | |
me.margins = margins; | |
// Dimensions | |
me.beforeSetDimensions(); | |
me.setDimensions(); | |
me.afterSetDimensions(); | |
// Labels | |
me.beforeBuildLabels(); | |
me.buildLabels(); | |
me.afterBuildLabels(); | |
// Fit | |
me.beforeFit(); | |
me.fit(); | |
me.afterFit(); | |
// | |
me.afterUpdate(); | |
return me.minSize; | |
}, | |
afterUpdate: noop, | |
// | |
beforeSetDimensions: noop, | |
setDimensions: function() { | |
var me = this; | |
// Set the unconstrained dimension before label rotation | |
if (me.isHorizontal()) { | |
// Reset position before calculating rotation | |
me.width = me.maxWidth; | |
me.left = 0; | |
me.right = me.width; | |
} else { | |
me.height = me.maxHeight; | |
// Reset position before calculating rotation | |
me.top = 0; | |
me.bottom = me.height; | |
} | |
// Reset padding | |
me.paddingLeft = 0; | |
me.paddingTop = 0; | |
me.paddingRight = 0; | |
me.paddingBottom = 0; | |
// Reset minSize | |
me.minSize = { | |
width: 0, | |
height: 0 | |
}; | |
}, | |
afterSetDimensions: noop, | |
// | |
beforeBuildLabels: noop, | |
buildLabels: function() { | |
var me = this; | |
me.legendItems = me.options.labels.generateLabels.call(me, me.chart); | |
if(me.options.reverse){ | |
me.legendItems.reverse(); | |
} | |
}, | |
afterBuildLabels: noop, | |
// | |
beforeFit: noop, | |
fit: function() { | |
var me = this; | |
var opts = me.options; | |
var labelOpts = opts.labels; | |
var display = opts.display; | |
var ctx = me.ctx; | |
var globalDefault = Chart.defaults.global, | |
itemOrDefault = helpers.getValueOrDefault, | |
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), | |
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), | |
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), | |
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); | |
// Reset hit boxes | |
var hitboxes = me.legendHitBoxes = []; | |
var minSize = me.minSize; | |
var isHorizontal = me.isHorizontal(); | |
if (isHorizontal) { | |
minSize.width = me.maxWidth; // fill all the width | |
minSize.height = display ? 10 : 0; | |
} else { | |
minSize.width = display ? 10 : 0; | |
minSize.height = me.maxHeight; // fill all the height | |
} | |
// Increase sizes here | |
if (display) { | |
ctx.font = labelFont; | |
if (isHorizontal) { | |
// Labels | |
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one | |
var lineWidths = me.lineWidths = [0]; | |
var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; | |
ctx.textAlign = "left"; | |
ctx.textBaseline = 'top'; | |
helpers.each(me.legendItems, function(legendItem, i) { | |
var boxWidth = labelOpts.usePointStyle ? | |
fontSize * Math.sqrt(2) : | |
labelOpts.boxWidth; | |
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; | |
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { | |
totalHeight += fontSize + (labelOpts.padding); | |
lineWidths[lineWidths.length] = me.left; | |
} | |
// Store the hitbox width and height here. Final position will be updated in `draw` | |
hitboxes[i] = { | |
left: 0, | |
top: 0, | |
width: width, | |
height: fontSize | |
}; | |
lineWidths[lineWidths.length - 1] += width + labelOpts.padding; | |
}); | |
minSize.height += totalHeight; | |
} else { | |
var vPadding = labelOpts.padding; | |
var columnWidths = me.columnWidths = []; | |
var totalWidth = labelOpts.padding; | |
var currentColWidth = 0; | |
var currentColHeight = 0; | |
var itemHeight = fontSize + vPadding; | |
helpers.each(me.legendItems, function(legendItem, i) { | |
// If usePointStyle is set, multiple boxWidth by 2 since it represents | |
// the radius and not truly the width | |
var boxWidth = labelOpts.usePointStyle ? 2 * labelOpts.boxWidth : labelOpts.boxWidth; | |
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; | |
// If too tall, go to new column | |
if (currentColHeight + itemHeight > minSize.height) { | |
totalWidth += currentColWidth + labelOpts.padding; | |
columnWidths.push(currentColWidth); // previous column width | |
currentColWidth = 0; | |
currentColHeight = 0; | |
} | |
// Get max width | |
currentColWidth = Math.max(currentColWidth, itemWidth); | |
currentColHeight += itemHeight; | |
// Store the hitbox width and height here. Final position will be updated in `draw` | |
hitboxes[i] = { | |
left: 0, | |
top: 0, | |
width: itemWidth, | |
height: fontSize | |
}; | |
}); | |
totalWidth += currentColWidth; | |
columnWidths.push(currentColWidth); | |
minSize.width += totalWidth; | |
} | |
} | |
me.width = minSize.width; | |
me.height = minSize.height; | |
}, | |
afterFit: noop, | |
// Shared Methods | |
isHorizontal: function() { | |
return this.options.position === "top" || this.options.position === "bottom"; | |
}, | |
// Actualy draw the legend on the canvas | |
draw: function() { | |
var me = this; | |
var opts = me.options; | |
var labelOpts = opts.labels; | |
var globalDefault = Chart.defaults.global, | |
lineDefault = globalDefault.elements.line, | |
legendWidth = me.width, | |
lineWidths = me.lineWidths; | |
if (opts.display) { | |
var ctx = me.ctx, | |
cursor, | |
itemOrDefault = helpers.getValueOrDefault, | |
fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor), | |
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), | |
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), | |
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), | |
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); | |
// Canvas setup | |
ctx.textAlign = "left"; | |
ctx.textBaseline = 'top'; | |
ctx.lineWidth = 0.5; | |
ctx.strokeStyle = fontColor; // for strikethrough effect | |
ctx.fillStyle = fontColor; // render in correct colour | |
ctx.font = labelFont; | |
var boxWidth = labelOpts.boxWidth, | |
hitboxes = me.legendHitBoxes; | |
// current position | |
var drawLegendBox = function(x, y, legendItem) { | |
if (isNaN(boxWidth) || boxWidth <= 0) { | |
return; | |
} | |
// Set the ctx for the box | |
ctx.save(); | |
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor); | |
ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); | |
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); | |
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); | |
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth); | |
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); | |
if (ctx.setLineDash) { | |
// IE 9 and 10 do not support line dash | |
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash)); | |
} | |
if (opts.labels && opts.labels.usePointStyle) { | |
// Recalulate x and y for drawPoint() because its expecting | |
// x and y to be center of figure (instead of top left) | |
var radius = fontSize * Math.SQRT2 / 2; | |
var offSet = radius / Math.SQRT2; | |
var centerX = x + offSet; | |
var centerY = y + offSet; | |
// Draw pointStyle as legend symbol | |
Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); | |
} | |
else { | |
// Draw box as legend symbol | |
ctx.strokeRect(x, y, boxWidth, fontSize); | |
ctx.fillRect(x, y, boxWidth, fontSize); | |
} | |
ctx.restore(); | |
}; | |
var fillText = function(x, y, legendItem, textWidth) { | |
ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y); | |
if (legendItem.hidden) { | |
// Strikethrough the text if hidden | |
ctx.beginPath(); | |
ctx.lineWidth = 2; | |
ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2)); | |
ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2)); | |
ctx.stroke(); | |
} | |
}; | |
// Horizontal | |
var isHorizontal = me.isHorizontal(); | |
if (isHorizontal) { | |
cursor = { | |
x: me.left + ((legendWidth - lineWidths[0]) / 2), | |
y: me.top + labelOpts.padding, | |
line: 0 | |
}; | |
} else { | |
cursor = { | |
x: me.left + labelOpts.padding, | |
y: me.top + labelOpts.padding, | |
line: 0 | |
}; | |
} | |
var itemHeight = fontSize + labelOpts.padding; | |
helpers.each(me.legendItems, function(legendItem, i) { | |
var textWidth = ctx.measureText(legendItem.text).width, | |
width = labelOpts.usePointStyle ? | |
fontSize + (fontSize / 2) + textWidth : | |
boxWidth + (fontSize / 2) + textWidth, | |
x = cursor.x, | |
y = cursor.y; | |
if (isHorizontal) { | |
if (x + width >= legendWidth) { | |
y = cursor.y += itemHeight; | |
cursor.line++; | |
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); | |
} | |
} else { | |
if (y + itemHeight > me.bottom) { | |
x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; | |
y = cursor.y = me.top; | |
cursor.line++; | |
} | |
} | |
drawLegendBox(x, y, legendItem); | |
hitboxes[i].left = x; | |
hitboxes[i].top = y; | |
// Fill the actual label | |
fillText(x, y, legendItem, textWidth); | |
if (isHorizontal) { | |
cursor.x += width + (labelOpts.padding); | |
} else { | |
cursor.y += itemHeight; | |
} | |
}); | |
} | |
}, | |
// Handle an event | |
handleEvent: function(e) { | |
var me = this; | |
var position = helpers.getRelativePosition(e, me.chart.chart), | |
x = position.x, | |
y = position.y, | |
opts = me.options; | |
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { | |
// See if we are touching one of the dataset boxes | |
var lh = me.legendHitBoxes; | |
for (var i = 0; i < lh.length; ++i) { | |
var hitBox = lh[i]; | |
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { | |
// Touching an element | |
if (opts.onClick) { | |
opts.onClick.call(me, e, me.legendItems[i]); | |
} | |
break; | |
} | |
} | |
} | |
} | |
}); | |
// Register the legend plugin | |
Chart.plugins.register({ | |
beforeInit: function(chartInstance) { | |
var opts = chartInstance.options; | |
var legendOpts = opts.legend; | |
if (legendOpts) { | |
chartInstance.legend = new Chart.Legend({ | |
ctx: chartInstance.chart.ctx, | |
options: legendOpts, | |
chart: chartInstance | |
}); | |
Chart.layoutService.addBox(chartInstance, chartInstance.legend); | |
} | |
} | |
}); | |
}; | |
},{}],29:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var noop = Chart.helpers.noop; | |
/** | |
* The plugin service singleton | |
* @namespace Chart.plugins | |
* @since 2.1.0 | |
*/ | |
Chart.plugins = { | |
_plugins: [], | |
/** | |
* Registers the given plugin(s) if not already registered. | |
* @param {Array|Object} plugins plugin instance(s). | |
*/ | |
register: function(plugins) { | |
var p = this._plugins; | |
([]).concat(plugins).forEach(function(plugin) { | |
if (p.indexOf(plugin) === -1) { | |
p.push(plugin); | |
} | |
}); | |
}, | |
/** | |
* Unregisters the given plugin(s) only if registered. | |
* @param {Array|Object} plugins plugin instance(s). | |
*/ | |
unregister: function(plugins) { | |
var p = this._plugins; | |
([]).concat(plugins).forEach(function(plugin) { | |
var idx = p.indexOf(plugin); | |
if (idx !== -1) { | |
p.splice(idx, 1); | |
} | |
}); | |
}, | |
/** | |
* Remove all registered p^lugins. | |
* @since 2.1.5 | |
*/ | |
clear: function() { | |
this._plugins = []; | |
}, | |
/** | |
* Returns the number of registered plugins? | |
* @returns {Number} | |
* @since 2.1.5 | |
*/ | |
count: function() { | |
return this._plugins.length; | |
}, | |
/** | |
* Returns all registered plugin intances. | |
* @returns {Array} array of plugin objects. | |
* @since 2.1.5 | |
*/ | |
getAll: function() { | |
return this._plugins; | |
}, | |
/** | |
* Calls registered plugins on the specified extension, with the given args. This | |
* method immediately returns as soon as a plugin explicitly returns false. The | |
* returned value can be used, for instance, to interrupt the current action. | |
* @param {String} extension the name of the plugin method to call (e.g. 'beforeUpdate'). | |
* @param {Array} [args] extra arguments to apply to the extension call. | |
* @returns {Boolean} false if any of the plugins return false, else returns true. | |
*/ | |
notify: function(extension, args) { | |
var plugins = this._plugins; | |
var ilen = plugins.length; | |
var i, plugin; | |
for (i=0; i<ilen; ++i) { | |
plugin = plugins[i]; | |
if (typeof plugin[extension] === 'function') { | |
if (plugin[extension].apply(plugin, args || []) === false) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
}; | |
/** | |
* Plugin extension methods. | |
* @interface Chart.PluginBase | |
* @since 2.1.0 | |
*/ | |
Chart.PluginBase = Chart.Element.extend({ | |
// Called at start of chart init | |
beforeInit: noop, | |
// Called at end of chart init | |
afterInit: noop, | |
// Called at start of update | |
beforeUpdate: noop, | |
// Called at end of update | |
afterUpdate: noop, | |
// Called at start of draw | |
beforeDraw: noop, | |
// Called at end of draw | |
afterDraw: noop, | |
// Called during destroy | |
destroy: noop | |
}); | |
/** | |
* Provided for backward compatibility, use Chart.plugins instead | |
* @namespace Chart.pluginService | |
* @deprecated since version 2.1.5 | |
* @todo remove me at version 3 | |
*/ | |
Chart.pluginService = Chart.plugins; | |
}; | |
},{}],30:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.scale = { | |
display: true, | |
position: "left", | |
// grid line settings | |
gridLines: { | |
display: true, | |
color: "rgba(0, 0, 0, 0.1)", | |
lineWidth: 1, | |
drawBorder: true, | |
drawOnChartArea: true, | |
drawTicks: true, | |
tickMarkLength: 10, | |
zeroLineWidth: 1, | |
zeroLineColor: "rgba(0,0,0,0.25)", | |
offsetGridLines: false | |
}, | |
// scale label | |
scaleLabel: { | |
// actual label | |
labelString: '', | |
// display property | |
display: false | |
}, | |
// label settings | |
ticks: { | |
beginAtZero: false, | |
minRotation: 0, | |
maxRotation: 50, | |
mirror: false, | |
padding: 10, | |
reverse: false, | |
display: true, | |
autoSkip: true, | |
autoSkipPadding: 0, | |
labelOffset: 0, | |
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here. | |
callback: function(value) { | |
return helpers.isArray(value) ? value : '' + value; | |
} | |
} | |
}; | |
Chart.Scale = Chart.Element.extend({ | |
// These methods are ordered by lifecyle. Utilities then follow. | |
// Any function defined here is inherited by all scale types. | |
// Any function can be extended by the scale type | |
beforeUpdate: function() { | |
helpers.callCallback(this.options.beforeUpdate, [this]); | |
}, | |
update: function(maxWidth, maxHeight, margins) { | |
var me = this; | |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) | |
me.beforeUpdate(); | |
// Absorb the master measurements | |
me.maxWidth = maxWidth; | |
me.maxHeight = maxHeight; | |
me.margins = helpers.extend({ | |
left: 0, | |
right: 0, | |
top: 0, | |
bottom: 0 | |
}, margins); | |
// Dimensions | |
me.beforeSetDimensions(); | |
me.setDimensions(); | |
me.afterSetDimensions(); | |
// Data min/max | |
me.beforeDataLimits(); | |
me.determineDataLimits(); | |
me.afterDataLimits(); | |
// Ticks | |
me.beforeBuildTicks(); | |
me.buildTicks(); | |
me.afterBuildTicks(); | |
me.beforeTickToLabelConversion(); | |
me.convertTicksToLabels(); | |
me.afterTickToLabelConversion(); | |
// Tick Rotation | |
me.beforeCalculateTickRotation(); | |
me.calculateTickRotation(); | |
me.afterCalculateTickRotation(); | |
// Fit | |
me.beforeFit(); | |
me.fit(); | |
me.afterFit(); | |
// | |
me.afterUpdate(); | |
return me.minSize; | |
}, | |
afterUpdate: function() { | |
helpers.callCallback(this.options.afterUpdate, [this]); | |
}, | |
// | |
beforeSetDimensions: function() { | |
helpers.callCallback(this.options.beforeSetDimensions, [this]); | |
}, | |
setDimensions: function() { | |
var me = this; | |
// Set the unconstrained dimension before label rotation | |
if (me.isHorizontal()) { | |
// Reset position before calculating rotation | |
me.width = me.maxWidth; | |
me.left = 0; | |
me.right = me.width; | |
} else { | |
me.height = me.maxHeight; | |
// Reset position before calculating rotation | |
me.top = 0; | |
me.bottom = me.height; | |
} | |
// Reset padding | |
me.paddingLeft = 0; | |
me.paddingTop = 0; | |
me.paddingRight = 0; | |
me.paddingBottom = 0; | |
}, | |
afterSetDimensions: function() { | |
helpers.callCallback(this.options.afterSetDimensions, [this]); | |
}, | |
// Data limits | |
beforeDataLimits: function() { | |
helpers.callCallback(this.options.beforeDataLimits, [this]); | |
}, | |
determineDataLimits: helpers.noop, | |
afterDataLimits: function() { | |
helpers.callCallback(this.options.afterDataLimits, [this]); | |
}, | |
// | |
beforeBuildTicks: function() { | |
helpers.callCallback(this.options.beforeBuildTicks, [this]); | |
}, | |
buildTicks: helpers.noop, | |
afterBuildTicks: function() { | |
helpers.callCallback(this.options.afterBuildTicks, [this]); | |
}, | |
beforeTickToLabelConversion: function() { | |
helpers.callCallback(this.options.beforeTickToLabelConversion, [this]); | |
}, | |
convertTicksToLabels: function() { | |
var me = this; | |
// Convert ticks to strings | |
me.ticks = me.ticks.map(function(numericalTick, index, ticks) { | |
if (me.options.ticks.userCallback) { | |
return me.options.ticks.userCallback(numericalTick, index, ticks); | |
} | |
return me.options.ticks.callback(numericalTick, index, ticks); | |
}, | |
me); | |
}, | |
afterTickToLabelConversion: function() { | |
helpers.callCallback(this.options.afterTickToLabelConversion, [this]); | |
}, | |
// | |
beforeCalculateTickRotation: function() { | |
helpers.callCallback(this.options.beforeCalculateTickRotation, [this]); | |
}, | |
calculateTickRotation: function() { | |
var me = this; | |
var context = me.ctx; | |
var globalDefaults = Chart.defaults.global; | |
var optionTicks = me.options.ticks; | |
//Get the width of each grid by calculating the difference | |
//between x offsets between 0 and 1. | |
var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize); | |
var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle); | |
var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily); | |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); | |
context.font = tickLabelFont; | |
var firstWidth = context.measureText(me.ticks[0]).width; | |
var lastWidth = context.measureText(me.ticks[me.ticks.length - 1]).width; | |
var firstRotated; | |
me.labelRotation = optionTicks.minRotation || 0; | |
me.paddingRight = 0; | |
me.paddingLeft = 0; | |
if (me.options.display) { | |
if (me.isHorizontal()) { | |
me.paddingRight = lastWidth / 2 + 3; | |
me.paddingLeft = firstWidth / 2 + 3; | |
if (!me.longestTextCache) { | |
me.longestTextCache = {}; | |
} | |
var originalLabelWidth = helpers.longestText(context, tickLabelFont, me.ticks, me.longestTextCache); | |
var labelWidth = originalLabelWidth; | |
var cosRotation; | |
var sinRotation; | |
// Allow 3 pixels x2 padding either side for label readability | |
// only the index matters for a dataset scale, but we want a consistent interface between scales | |
var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; | |
//Max label rotation can be set or default to 90 - also act as a loop counter | |
while (labelWidth > tickWidth && me.labelRotation < optionTicks.maxRotation) { | |
cosRotation = Math.cos(helpers.toRadians(me.labelRotation)); | |
sinRotation = Math.sin(helpers.toRadians(me.labelRotation)); | |
firstRotated = cosRotation * firstWidth; | |
// We're right aligning the text now. | |
if (firstRotated + tickFontSize / 2 > me.yLabelWidth) { | |
me.paddingLeft = firstRotated + tickFontSize / 2; | |
} | |
me.paddingRight = tickFontSize / 2; | |
if (sinRotation * originalLabelWidth > me.maxHeight) { | |
// go back one step | |
me.labelRotation--; | |
break; | |
} | |
me.labelRotation++; | |
labelWidth = cosRotation * originalLabelWidth; | |
} | |
} | |
} | |
if (me.margins) { | |
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); | |
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); | |
} | |
}, | |
afterCalculateTickRotation: function() { | |
helpers.callCallback(this.options.afterCalculateTickRotation, [this]); | |
}, | |
// | |
beforeFit: function() { | |
helpers.callCallback(this.options.beforeFit, [this]); | |
}, | |
fit: function() { | |
var me = this; | |
// Reset | |
var minSize = me.minSize = { | |
width: 0, | |
height: 0 | |
}; | |
var opts = me.options; | |
var globalDefaults = Chart.defaults.global; | |
var tickOpts = opts.ticks; | |
var scaleLabelOpts = opts.scaleLabel; | |
var display = opts.display; | |
var isHorizontal = me.isHorizontal(); | |
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); | |
var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); | |
var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); | |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); | |
var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize); | |
var tickMarkLength = opts.gridLines.tickMarkLength; | |
// Width | |
if (isHorizontal) { | |
// subtract the margins to line up with the chartArea if we are a full width scale | |
minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; | |
} else { | |
minSize.width = display ? tickMarkLength : 0; | |
} | |
// height | |
if (isHorizontal) { | |
minSize.height = display ? tickMarkLength : 0; | |
} else { | |
minSize.height = me.maxHeight; // fill all the height | |
} | |
// Are we showing a title for the scale? | |
if (scaleLabelOpts.display && display) { | |
if (isHorizontal) { | |
minSize.height += (scaleLabelFontSize * 1.5); | |
} else { | |
minSize.width += (scaleLabelFontSize * 1.5); | |
} | |
} | |
if (tickOpts.display && display) { | |
// Don't bother fitting the ticks if we are not showing them | |
if (!me.longestTextCache) { | |
me.longestTextCache = {}; | |
} | |
var largestTextWidth = helpers.longestText(me.ctx, tickLabelFont, me.ticks, me.longestTextCache); | |
var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks); | |
var lineSpace = tickFontSize * 0.5; | |
if (isHorizontal) { | |
// A horizontal axis is more constrained by the height. | |
me.longestLabelWidth = largestTextWidth; | |
// TODO - improve this calculation | |
var labelHeight = (Math.sin(helpers.toRadians(me.labelRotation)) * me.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines); | |
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight); | |
me.ctx.font = tickLabelFont; | |
var firstLabelWidth = me.ctx.measureText(me.ticks[0]).width; | |
var lastLabelWidth = me.ctx.measureText(me.ticks[me.ticks.length - 1]).width; | |
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated | |
// by the font height | |
var cosRotation = Math.cos(helpers.toRadians(me.labelRotation)); | |
var sinRotation = Math.sin(helpers.toRadians(me.labelRotation)); | |
me.paddingLeft = me.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges | |
me.paddingRight = me.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated | |
} else { | |
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first | |
var maxLabelWidth = me.maxWidth - minSize.width; | |
// Account for padding | |
var mirror = tickOpts.mirror; | |
if (!mirror) { | |
largestTextWidth += me.options.ticks.padding; | |
} else { | |
// If mirrored text is on the inside so don't expand | |
largestTextWidth = 0; | |
} | |
if (largestTextWidth < maxLabelWidth) { | |
// We don't need all the room | |
minSize.width += largestTextWidth; | |
} else { | |
// Expand to max size | |
minSize.width = me.maxWidth; | |
} | |
me.paddingTop = tickFontSize / 2; | |
me.paddingBottom = tickFontSize / 2; | |
} | |
} | |
if (me.margins) { | |
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); | |
me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); | |
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); | |
me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); | |
} | |
me.width = minSize.width; | |
me.height = minSize.height; | |
}, | |
afterFit: function() { | |
helpers.callCallback(this.options.afterFit, [this]); | |
}, | |
// Shared Methods | |
isHorizontal: function() { | |
return this.options.position === "top" || this.options.position === "bottom"; | |
}, | |
isFullWidth: function() { | |
return (this.options.fullWidth); | |
}, | |
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not | |
getRightValue: function(rawValue) { | |
// Null and undefined values first | |
if (rawValue === null || typeof(rawValue) === 'undefined') { | |
return NaN; | |
} | |
// isNaN(object) returns true, so make sure NaN is checking for a number | |
if (typeof(rawValue) === 'number' && isNaN(rawValue)) { | |
return NaN; | |
} | |
// If it is in fact an object, dive in one more level | |
if (typeof(rawValue) === "object") { | |
if ((rawValue instanceof Date) || (rawValue.isValid)) { | |
return rawValue; | |
} else { | |
return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); | |
} | |
} | |
// Value is good, return it | |
return rawValue; | |
}, | |
// Used to get the value to display in the tooltip for the data at the given index | |
// function getLabelForIndex(index, datasetIndex) | |
getLabelForIndex: helpers.noop, | |
// Used to get data value locations. Value can either be an index or a numerical value | |
getPixelForValue: helpers.noop, | |
// Used to get the data value from a given pixel. This is the inverse of getPixelForValue | |
getValueForPixel: helpers.noop, | |
// Used for tick location, should | |
getPixelForTick: function(index, includeOffset) { | |
var me = this; | |
if (me.isHorizontal()) { | |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); | |
var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); | |
var pixel = (tickWidth * index) + me.paddingLeft; | |
if (includeOffset) { | |
pixel += tickWidth / 2; | |
} | |
var finalVal = me.left + Math.round(pixel); | |
finalVal += me.isFullWidth() ? me.margins.left : 0; | |
return finalVal; | |
} else { | |
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); | |
return me.top + (index * (innerHeight / (me.ticks.length - 1))); | |
} | |
}, | |
// Utility for getting the pixel location of a percentage of scale | |
getPixelForDecimal: function(decimal /*, includeOffset*/ ) { | |
var me = this; | |
if (me.isHorizontal()) { | |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); | |
var valueOffset = (innerWidth * decimal) + me.paddingLeft; | |
var finalVal = me.left + Math.round(valueOffset); | |
finalVal += me.isFullWidth() ? me.margins.left : 0; | |
return finalVal; | |
} else { | |
return me.top + (decimal * me.height); | |
} | |
}, | |
getBasePixel: function() { | |
var me = this; | |
var min = me.min; | |
var max = me.max; | |
return me.getPixelForValue( | |
me.beginAtZero? 0: | |
min < 0 && max < 0? max : | |
min > 0 && max > 0? min : | |
0); | |
}, | |
// Actualy draw the scale on the canvas | |
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on | |
draw: function(chartArea) { | |
var me = this; | |
var options = me.options; | |
if (!options.display) { | |
return; | |
} | |
var context = me.ctx; | |
var globalDefaults = Chart.defaults.global; | |
var optionTicks = options.ticks; | |
var gridLines = options.gridLines; | |
var scaleLabel = options.scaleLabel; | |
var isRotated = me.labelRotation !== 0; | |
var skipRatio; | |
var useAutoskipper = optionTicks.autoSkip; | |
var isHorizontal = me.isHorizontal(); | |
// figure out the maximum number of gridlines to show | |
var maxTicks; | |
if (optionTicks.maxTicksLimit) { | |
maxTicks = optionTicks.maxTicksLimit; | |
} | |
var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); | |
var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize); | |
var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle); | |
var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily); | |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); | |
var tl = gridLines.tickMarkLength; | |
var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); | |
var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize); | |
var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle); | |
var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily); | |
var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); | |
var labelRotationRadians = helpers.toRadians(me.labelRotation); | |
var cosRotation = Math.cos(labelRotationRadians); | |
var longestRotatedLabel = me.longestLabelWidth * cosRotation; | |
// Make sure we draw text in the correct color and font | |
context.fillStyle = tickFontColor; | |
var itemsToDraw = []; | |
if (isHorizontal) { | |
skipRatio = false; | |
// Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation | |
// See #2584 | |
if (isRotated) { | |
longestRotatedLabel /= 2; | |
} | |
if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) { | |
skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight))); | |
} | |
// if they defined a max number of optionTicks, | |
// increase skipRatio until that number is met | |
if (maxTicks && me.ticks.length > maxTicks) { | |
while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) { | |
if (!skipRatio) { | |
skipRatio = 1; | |
} | |
skipRatio += 1; | |
} | |
} | |
if (!useAutoskipper) { | |
skipRatio = false; | |
} | |
} | |
var xTickStart = options.position === "right" ? me.left : me.right - tl; | |
var xTickEnd = options.position === "right" ? me.left + tl : me.right; | |
var yTickStart = options.position === "bottom" ? me.top : me.bottom - tl; | |
var yTickEnd = options.position === "bottom" ? me.top + tl : me.bottom; | |
helpers.each(me.ticks, function(label, index) { | |
// If the callback returned a null or undefined value, do not draw this line | |
if (label === undefined || label === null) { | |
return; | |
} | |
var isLastTick = me.ticks.length === index + 1; | |
// Since we always show the last tick,we need may need to hide the last shown one before | |
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length); | |
if (shouldSkip && !isLastTick || (label === undefined || label === null)) { | |
return; | |
} | |
var lineWidth, lineColor; | |
if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) { | |
// Draw the first index specially | |
lineWidth = gridLines.zeroLineWidth; | |
lineColor = gridLines.zeroLineColor; | |
} else { | |
lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index); | |
lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index); | |
} | |
// Common properties | |
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; | |
var textAlign, textBaseline = 'middle'; | |
if (isHorizontal) { | |
if (!isRotated) { | |
textBaseline = options.position === 'top' ? 'bottom' : 'top'; | |
} | |
textAlign = isRotated ? 'right' : 'center'; | |
var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines | |
labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) | |
labelY = (isRotated) ? me.top + 12 : options.position === 'top' ? me.bottom - tl : me.top + tl; | |
tx1 = tx2 = x1 = x2 = xLineValue; | |
ty1 = yTickStart; | |
ty2 = yTickEnd; | |
y1 = chartArea.top; | |
y2 = chartArea.bottom; | |
} else { | |
if (options.position === 'left') { | |
if (optionTicks.mirror) { | |
labelX = me.right + optionTicks.padding; | |
textAlign = 'left'; | |
} else { | |
labelX = me.right - optionTicks.padding; | |
textAlign = 'right'; | |
} | |
} else { | |
// right side | |
if (optionTicks.mirror) { | |
labelX = me.left - optionTicks.padding; | |
textAlign = 'right'; | |
} else { | |
labelX = me.left + optionTicks.padding; | |
textAlign = 'left'; | |
} | |
} | |
var yLineValue = me.getPixelForTick(index); // xvalues for grid lines | |
yLineValue += helpers.aliasPixel(lineWidth); | |
labelY = me.getPixelForTick(index, gridLines.offsetGridLines); | |
tx1 = xTickStart; | |
tx2 = xTickEnd; | |
x1 = chartArea.left; | |
x2 = chartArea.right; | |
ty1 = ty2 = y1 = y2 = yLineValue; | |
} | |
itemsToDraw.push({ | |
tx1: tx1, | |
ty1: ty1, | |
tx2: tx2, | |
ty2: ty2, | |
x1: x1, | |
y1: y1, | |
x2: x2, | |
y2: y2, | |
labelX: labelX, | |
labelY: labelY, | |
glWidth: lineWidth, | |
glColor: lineColor, | |
rotation: -1 * labelRotationRadians, | |
label: label, | |
textBaseline: textBaseline, | |
textAlign: textAlign | |
}); | |
}); | |
// Draw all of the tick labels, tick marks, and grid lines at the correct places | |
helpers.each(itemsToDraw, function(itemToDraw) { | |
if (gridLines.display) { | |
context.lineWidth = itemToDraw.glWidth; | |
context.strokeStyle = itemToDraw.glColor; | |
context.beginPath(); | |
if (gridLines.drawTicks) { | |
context.moveTo(itemToDraw.tx1, itemToDraw.ty1); | |
context.lineTo(itemToDraw.tx2, itemToDraw.ty2); | |
} | |
if (gridLines.drawOnChartArea) { | |
context.moveTo(itemToDraw.x1, itemToDraw.y1); | |
context.lineTo(itemToDraw.x2, itemToDraw.y2); | |
} | |
context.stroke(); | |
} | |
if (optionTicks.display) { | |
context.save(); | |
context.translate(itemToDraw.labelX, itemToDraw.labelY); | |
context.rotate(itemToDraw.rotation); | |
context.font = tickLabelFont; | |
context.textBaseline = itemToDraw.textBaseline; | |
context.textAlign = itemToDraw.textAlign; | |
var label = itemToDraw.label; | |
if (helpers.isArray(label)) { | |
for (var i = 0, y = 0; i < label.length; ++i) { | |
// We just make sure the multiline element is a string here.. | |
context.fillText('' + label[i], 0, y); | |
// apply same lineSpacing as calculated @ L#320 | |
y += (tickFontSize * 1.5); | |
} | |
} else { | |
context.fillText(label, 0, 0); | |
} | |
context.restore(); | |
} | |
}); | |
if (scaleLabel.display) { | |
// Draw the scale label | |
var scaleLabelX; | |
var scaleLabelY; | |
var rotation = 0; | |
if (isHorizontal) { | |
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width | |
scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFontSize / 2) : me.top + (scaleLabelFontSize / 2); | |
} else { | |
var isLeft = options.position === 'left'; | |
scaleLabelX = isLeft ? me.left + (scaleLabelFontSize / 2) : me.right - (scaleLabelFontSize / 2); | |
scaleLabelY = me.top + ((me.bottom - me.top) / 2); | |
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; | |
} | |
context.save(); | |
context.translate(scaleLabelX, scaleLabelY); | |
context.rotate(rotation); | |
context.textAlign = 'center'; | |
context.textBaseline = 'middle'; | |
context.fillStyle = scaleLabelFontColor; // render in correct colour | |
context.font = scaleLabelFont; | |
context.fillText(scaleLabel.labelString, 0, 0); | |
context.restore(); | |
} | |
if (gridLines.drawBorder) { | |
// Draw the line at the edge of the axis | |
context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0); | |
context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0); | |
var x1 = me.left, | |
x2 = me.right, | |
y1 = me.top, | |
y2 = me.bottom; | |
var aliasPixel = helpers.aliasPixel(context.lineWidth); | |
if (isHorizontal) { | |
y1 = y2 = options.position === 'top' ? me.bottom : me.top; | |
y1 += aliasPixel; | |
y2 += aliasPixel; | |
} else { | |
x1 = x2 = options.position === 'left' ? me.right : me.left; | |
x1 += aliasPixel; | |
x2 += aliasPixel; | |
} | |
context.beginPath(); | |
context.moveTo(x1, y1); | |
context.lineTo(x2, y2); | |
context.stroke(); | |
} | |
} | |
}); | |
}; | |
},{}],31:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.scaleService = { | |
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then | |
// use the new chart options to grab the correct scale | |
constructors: {}, | |
// Use a registration function so that we can move to an ES6 map when we no longer need to support | |
// old browsers | |
// Scale config defaults | |
defaults: {}, | |
registerScaleType: function(type, scaleConstructor, defaults) { | |
this.constructors[type] = scaleConstructor; | |
this.defaults[type] = helpers.clone(defaults); | |
}, | |
getScaleConstructor: function(type) { | |
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; | |
}, | |
getScaleDefaults: function(type) { | |
// Return the scale defaults merged with the global settings so that we always use the latest ones | |
return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {}; | |
}, | |
updateScaleDefaults: function(type, additions) { | |
var defaults = this.defaults; | |
if (defaults.hasOwnProperty(type)) { | |
defaults[type] = helpers.extend(defaults[type], additions); | |
} | |
}, | |
addScalesToLayout: function(chartInstance) { | |
// Adds each scale to the chart.boxes array to be sized accordingly | |
helpers.each(chartInstance.scales, function(scale) { | |
Chart.layoutService.addBox(chartInstance, scale); | |
}); | |
} | |
}; | |
}; | |
},{}],32:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.global.title = { | |
display: false, | |
position: 'top', | |
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) | |
fontStyle: 'bold', | |
padding: 10, | |
// actual title | |
text: '' | |
}; | |
var noop = helpers.noop; | |
Chart.Title = Chart.Element.extend({ | |
initialize: function(config) { | |
var me = this; | |
helpers.extend(me, config); | |
me.options = helpers.configMerge(Chart.defaults.global.title, config.options); | |
// Contains hit boxes for each dataset (in dataset order) | |
me.legendHitBoxes = []; | |
}, | |
// These methods are ordered by lifecyle. Utilities then follow. | |
beforeUpdate: function () { | |
var chartOpts = this.chart.options; | |
if (chartOpts && chartOpts.title) { | |
this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title); | |
} | |
}, | |
update: function(maxWidth, maxHeight, margins) { | |
var me = this; | |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) | |
me.beforeUpdate(); | |
// Absorb the master measurements | |
me.maxWidth = maxWidth; | |
me.maxHeight = maxHeight; | |
me.margins = margins; | |
// Dimensions | |
me.beforeSetDimensions(); | |
me.setDimensions(); | |
me.afterSetDimensions(); | |
// Labels | |
me.beforeBuildLabels(); | |
me.buildLabels(); | |
me.afterBuildLabels(); | |
// Fit | |
me.beforeFit(); | |
me.fit(); | |
me.afterFit(); | |
// | |
me.afterUpdate(); | |
return me.minSize; | |
}, | |
afterUpdate: noop, | |
// | |
beforeSetDimensions: noop, | |
setDimensions: function() { | |
var me = this; | |
// Set the unconstrained dimension before label rotation | |
if (me.isHorizontal()) { | |
// Reset position before calculating rotation | |
me.width = me.maxWidth; | |
me.left = 0; | |
me.right = me.width; | |
} else { | |
me.height = me.maxHeight; | |
// Reset position before calculating rotation | |
me.top = 0; | |
me.bottom = me.height; | |
} | |
// Reset padding | |
me.paddingLeft = 0; | |
me.paddingTop = 0; | |
me.paddingRight = 0; | |
me.paddingBottom = 0; | |
// Reset minSize | |
me.minSize = { | |
width: 0, | |
height: 0 | |
}; | |
}, | |
afterSetDimensions: noop, | |
// | |
beforeBuildLabels: noop, | |
buildLabels: noop, | |
afterBuildLabels: noop, | |
// | |
beforeFit: noop, | |
fit: function() { | |
var me = this, | |
valueOrDefault = helpers.getValueOrDefault, | |
opts = me.options, | |
globalDefaults = Chart.defaults.global, | |
display = opts.display, | |
fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), | |
minSize = me.minSize; | |
if (me.isHorizontal()) { | |
minSize.width = me.maxWidth; // fill all the width | |
minSize.height = display ? fontSize + (opts.padding * 2) : 0; | |
} else { | |
minSize.width = display ? fontSize + (opts.padding * 2) : 0; | |
minSize.height = me.maxHeight; // fill all the height | |
} | |
me.width = minSize.width; | |
me.height = minSize.height; | |
}, | |
afterFit: noop, | |
// Shared Methods | |
isHorizontal: function() { | |
var pos = this.options.position; | |
return pos === "top" || pos === "bottom"; | |
}, | |
// Actualy draw the title block on the canvas | |
draw: function() { | |
var me = this, | |
ctx = me.ctx, | |
valueOrDefault = helpers.getValueOrDefault, | |
opts = me.options, | |
globalDefaults = Chart.defaults.global; | |
if (opts.display) { | |
var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), | |
fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle), | |
fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily), | |
titleFont = helpers.fontString(fontSize, fontStyle, fontFamily), | |
rotation = 0, | |
titleX, | |
titleY, | |
top = me.top, | |
left = me.left, | |
bottom = me.bottom, | |
right = me.right; | |
ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour | |
ctx.font = titleFont; | |
// Horizontal | |
if (me.isHorizontal()) { | |
titleX = left + ((right - left) / 2); // midpoint of the width | |
titleY = top + ((bottom - top) / 2); // midpoint of the height | |
} else { | |
titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2); | |
titleY = top + ((bottom - top) / 2); | |
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); | |
} | |
ctx.save(); | |
ctx.translate(titleX, titleY); | |
ctx.rotate(rotation); | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.fillText(opts.text, 0, 0); | |
ctx.restore(); | |
} | |
} | |
}); | |
// Register the title plugin | |
Chart.plugins.register({ | |
beforeInit: function(chartInstance) { | |
var opts = chartInstance.options; | |
var titleOpts = opts.title; | |
if (titleOpts) { | |
chartInstance.titleBlock = new Chart.Title({ | |
ctx: chartInstance.chart.ctx, | |
options: titleOpts, | |
chart: chartInstance | |
}); | |
Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock); | |
} | |
} | |
}); | |
}; | |
},{}],33:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
Chart.defaults.global.tooltips = { | |
enabled: true, | |
custom: null, | |
mode: 'single', | |
backgroundColor: "rgba(0,0,0,0.8)", | |
titleFontStyle: "bold", | |
titleSpacing: 2, | |
titleMarginBottom: 6, | |
titleFontColor: "#fff", | |
titleAlign: "left", | |
bodySpacing: 2, | |
bodyFontColor: "#fff", | |
bodyAlign: "left", | |
footerFontStyle: "bold", | |
footerSpacing: 2, | |
footerMarginTop: 6, | |
footerFontColor: "#fff", | |
footerAlign: "left", | |
yPadding: 6, | |
xPadding: 6, | |
yAlign : 'center', | |
xAlign : 'center', | |
caretSize: 5, | |
cornerRadius: 6, | |
multiKeyBackground: '#fff', | |
callbacks: { | |
// Args are: (tooltipItems, data) | |
beforeTitle: helpers.noop, | |
title: function(tooltipItems, data) { | |
// Pick first xLabel for now | |
var title = ''; | |
var labels = data.labels; | |
var labelCount = labels ? labels.length : 0; | |
if (tooltipItems.length > 0) { | |
var item = tooltipItems[0]; | |
if (item.xLabel) { | |
title = item.xLabel; | |
} else if (labelCount > 0 && item.index < labelCount) { | |
title = labels[item.index]; | |
} | |
} | |
return title; | |
}, | |
afterTitle: helpers.noop, | |
// Args are: (tooltipItems, data) | |
beforeBody: helpers.noop, | |
// Args are: (tooltipItem, data) | |
beforeLabel: helpers.noop, | |
label: function(tooltipItem, data) { | |
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; | |
return datasetLabel + ': ' + tooltipItem.yLabel; | |
}, | |
labelColor: function(tooltipItem, chartInstance) { | |
var meta = chartInstance.getDatasetMeta(tooltipItem.datasetIndex); | |
var activeElement = meta.data[tooltipItem.index]; | |
var view = activeElement._view; | |
return { | |
borderColor: view.borderColor, | |
backgroundColor: view.backgroundColor | |
}; | |
}, | |
afterLabel: helpers.noop, | |
// Args are: (tooltipItems, data) | |
afterBody: helpers.noop, | |
// Args are: (tooltipItems, data) | |
beforeFooter: helpers.noop, | |
footer: helpers.noop, | |
afterFooter: helpers.noop | |
} | |
}; | |
// Helper to push or concat based on if the 2nd parameter is an array or not | |
function pushOrConcat(base, toPush) { | |
if (toPush) { | |
if (helpers.isArray(toPush)) { | |
//base = base.concat(toPush); | |
Array.prototype.push.apply(base, toPush); | |
} else { | |
base.push(toPush); | |
} | |
} | |
return base; | |
} | |
function getAveragePosition(elements) { | |
if (!elements.length) { | |
return false; | |
} | |
var i, len; | |
var xPositions = []; | |
var yPositions = []; | |
for (i = 0, len = elements.length; i < len; ++i) { | |
var el = elements[i]; | |
if (el && el.hasValue()){ | |
var pos = el.tooltipPosition(); | |
xPositions.push(pos.x); | |
yPositions.push(pos.y); | |
} | |
} | |
var x = 0, | |
y = 0; | |
for (i = 0; i < xPositions.length; ++i) { | |
if (xPositions[ i ]) { | |
x += xPositions[i]; | |
y += yPositions[i]; | |
} | |
} | |
return { | |
x: Math.round(x / xPositions.length), | |
y: Math.round(y / xPositions.length) | |
}; | |
} | |
// Private helper to create a tooltip iteam model | |
// @param element : the chart element (point, arc, bar) to create the tooltip item for | |
// @return : new tooltip item | |
function createTooltipItem(element) { | |
var xScale = element._xScale; | |
var yScale = element._yScale || element._scale; // handle radar || polarArea charts | |
var index = element._index, | |
datasetIndex = element._datasetIndex; | |
return { | |
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', | |
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', | |
index: index, | |
datasetIndex: datasetIndex | |
}; | |
} | |
Chart.Tooltip = Chart.Element.extend({ | |
initialize: function() { | |
var me = this; | |
var globalDefaults = Chart.defaults.global; | |
var tooltipOpts = me._options; | |
var getValueOrDefault = helpers.getValueOrDefault; | |
helpers.extend(me, { | |
_model: { | |
// Positioning | |
xPadding: tooltipOpts.xPadding, | |
yPadding: tooltipOpts.yPadding, | |
xAlign : tooltipOpts.xAlign, | |
yAlign : tooltipOpts.yAlign, | |
// Body | |
bodyFontColor: tooltipOpts.bodyFontColor, | |
_bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), | |
_bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), | |
_bodyAlign: tooltipOpts.bodyAlign, | |
bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), | |
bodySpacing: tooltipOpts.bodySpacing, | |
// Title | |
titleFontColor: tooltipOpts.titleFontColor, | |
_titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), | |
_titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), | |
titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), | |
_titleAlign: tooltipOpts.titleAlign, | |
titleSpacing: tooltipOpts.titleSpacing, | |
titleMarginBottom: tooltipOpts.titleMarginBottom, | |
// Footer | |
footerFontColor: tooltipOpts.footerFontColor, | |
_footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), | |
_footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), | |
footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), | |
_footerAlign: tooltipOpts.footerAlign, | |
footerSpacing: tooltipOpts.footerSpacing, | |
footerMarginTop: tooltipOpts.footerMarginTop, | |
// Appearance | |
caretSize: tooltipOpts.caretSize, | |
cornerRadius: tooltipOpts.cornerRadius, | |
backgroundColor: tooltipOpts.backgroundColor, | |
opacity: 0, | |
legendColorBackground: tooltipOpts.multiKeyBackground | |
} | |
}); | |
}, | |
// Get the title | |
// Args are: (tooltipItem, data) | |
getTitle: function() { | |
var me = this; | |
var opts = me._options; | |
var callbacks = opts.callbacks; | |
var beforeTitle = callbacks.beforeTitle.apply(me, arguments), | |
title = callbacks.title.apply(me, arguments), | |
afterTitle = callbacks.afterTitle.apply(me, arguments); | |
var lines = []; | |
lines = pushOrConcat(lines, beforeTitle); | |
lines = pushOrConcat(lines, title); | |
lines = pushOrConcat(lines, afterTitle); | |
return lines; | |
}, | |
// Args are: (tooltipItem, data) | |
getBeforeBody: function() { | |
var lines = this._options.callbacks.beforeBody.apply(this, arguments); | |
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; | |
}, | |
// Args are: (tooltipItem, data) | |
getBody: function(tooltipItems, data) { | |
var me = this; | |
var callbacks = me._options.callbacks; | |
var bodyItems = []; | |
helpers.each(tooltipItems, function(tooltipItem) { | |
var bodyItem = { | |
before: [], | |
lines: [], | |
after: [] | |
}; | |
pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); | |
pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); | |
pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); | |
bodyItems.push(bodyItem); | |
}); | |
return bodyItems; | |
}, | |
// Args are: (tooltipItem, data) | |
getAfterBody: function() { | |
var lines = this._options.callbacks.afterBody.apply(this, arguments); | |
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; | |
}, | |
// Get the footer and beforeFooter and afterFooter lines | |
// Args are: (tooltipItem, data) | |
getFooter: function() { | |
var me = this; | |
var callbacks = me._options.callbacks; | |
var beforeFooter = callbacks.beforeFooter.apply(me, arguments); | |
var footer = callbacks.footer.apply(me, arguments); | |
var afterFooter = callbacks.afterFooter.apply(me, arguments); | |
var lines = []; | |
lines = pushOrConcat(lines, beforeFooter); | |
lines = pushOrConcat(lines, footer); | |
lines = pushOrConcat(lines, afterFooter); | |
return lines; | |
}, | |
update: function(changed) { | |
var me = this; | |
var opts = me._options; | |
var model = me._model; | |
var active = me._active; | |
var data = me._data; | |
var chartInstance = me._chartInstance; | |
var i, len; | |
if (active.length) { | |
model.opacity = 1; | |
var labelColors = [], | |
tooltipPosition = getAveragePosition(active); | |
var tooltipItems = []; | |
for (i = 0, len = active.length; i < len; ++i) { | |
tooltipItems.push(createTooltipItem(active[i])); | |
} | |
// If the user provided a sorting function, use it to modify the tooltip items | |
if (opts.itemSort) { | |
tooltipItems = tooltipItems.sort(opts.itemSort); | |
} | |
// If there is more than one item, show color items | |
if (active.length > 1) { | |
helpers.each(tooltipItems, function(tooltipItem) { | |
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance)); | |
}); | |
} | |
// Build the Text Lines | |
helpers.extend(model, { | |
title: me.getTitle(tooltipItems, data), | |
beforeBody: me.getBeforeBody(tooltipItems, data), | |
body: me.getBody(tooltipItems, data), | |
afterBody: me.getAfterBody(tooltipItems, data), | |
footer: me.getFooter(tooltipItems, data), | |
x: Math.round(tooltipPosition.x), | |
y: Math.round(tooltipPosition.y), | |
caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2), | |
labelColors: labelColors | |
}); | |
// We need to determine alignment of | |
var tooltipSize = me.getTooltipSize(model); | |
me.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas | |
helpers.extend(model, me.getBackgroundPoint(model, tooltipSize)); | |
} else { | |
me._model.opacity = 0; | |
} | |
if (changed && opts.custom) { | |
opts.custom.call(me, model); | |
} | |
return me; | |
}, | |
getTooltipSize: function(vm) { | |
var ctx = this._chart.ctx; | |
var size = { | |
height: vm.yPadding * 2, // Tooltip Padding | |
width: 0 | |
}; | |
// Count of all lines in the body | |
var body = vm.body; | |
var combinedBodyLength = body.reduce(function(count, bodyItem) { | |
return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; | |
}, 0); | |
combinedBodyLength += vm.beforeBody.length + vm.afterBody.length; | |
var titleLineCount = vm.title.length; | |
var footerLineCount = vm.footer.length; | |
var titleFontSize = vm.titleFontSize, | |
bodyFontSize = vm.bodyFontSize, | |
footerFontSize = vm.footerFontSize; | |
size.height += titleLineCount * titleFontSize; // Title Lines | |
size.height += (titleLineCount - 1) * vm.titleSpacing; // Title Line Spacing | |
size.height += titleLineCount ? vm.titleMarginBottom : 0; // Title's bottom Margin | |
size.height += combinedBodyLength * bodyFontSize; // Body Lines | |
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing | |
size.height += footerLineCount ? vm.footerMarginTop : 0; // Footer Margin | |
size.height += footerLineCount * (footerFontSize); // Footer Lines | |
size.height += footerLineCount ? (footerLineCount - 1) * vm.footerSpacing : 0; // Footer Line Spacing | |
// Title width | |
var widthPadding = 0; | |
var maxLineWidth = function(line) { | |
size.width = Math.max(size.width, ctx.measureText(line).width + widthPadding); | |
}; | |
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); | |
helpers.each(vm.title, maxLineWidth); | |
// Body width | |
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); | |
helpers.each(vm.beforeBody.concat(vm.afterBody), maxLineWidth); | |
// Body lines may include some extra width due to the color box | |
widthPadding = body.length > 1 ? (bodyFontSize + 2) : 0; | |
helpers.each(body, function(bodyItem) { | |
helpers.each(bodyItem.before, maxLineWidth); | |
helpers.each(bodyItem.lines, maxLineWidth); | |
helpers.each(bodyItem.after, maxLineWidth); | |
}); | |
// Reset back to 0 | |
widthPadding = 0; | |
// Footer width | |
ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); | |
helpers.each(vm.footer, maxLineWidth); | |
// Add padding | |
size.width += 2 * vm.xPadding; | |
return size; | |
}, | |
determineAlignment: function(size) { | |
var me = this; | |
var model = me._model; | |
var chart = me._chart; | |
var chartArea = me._chartInstance.chartArea; | |
if (model.y < size.height) { | |
model.yAlign = 'top'; | |
} else if (model.y > (chart.height - size.height)) { | |
model.yAlign = 'bottom'; | |
} | |
var lf, rf; // functions to determine left, right alignment | |
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart | |
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges | |
var midX = (chartArea.left + chartArea.right) / 2; | |
var midY = (chartArea.top + chartArea.bottom) / 2; | |
if (model.yAlign === 'center') { | |
lf = function(x) { | |
return x <= midX; | |
}; | |
rf = function(x) { | |
return x > midX; | |
}; | |
} else { | |
lf = function(x) { | |
return x <= (size.width / 2); | |
}; | |
rf = function(x) { | |
return x >= (chart.width - (size.width / 2)); | |
}; | |
} | |
olf = function(x) { | |
return x + size.width > chart.width; | |
}; | |
orf = function(x) { | |
return x - size.width < 0; | |
}; | |
yf = function(y) { | |
return y <= midY ? 'top' : 'bottom'; | |
}; | |
if (lf(model.x)) { | |
model.xAlign = 'left'; | |
// Is tooltip too wide and goes over the right side of the chart.? | |
if (olf(model.x)) { | |
model.xAlign = 'center'; | |
model.yAlign = yf(model.y); | |
} | |
} else if (rf(model.x)) { | |
model.xAlign = 'right'; | |
// Is tooltip too wide and goes outside left edge of canvas? | |
if (orf(model.x)) { | |
model.xAlign = 'center'; | |
model.yAlign = yf(model.y); | |
} | |
} | |
}, | |
getBackgroundPoint: function(vm, size) { | |
// Background Position | |
var pt = { | |
x: vm.x, | |
y: vm.y | |
}; | |
var caretSize = vm.caretSize, | |
caretPadding = vm.caretPadding, | |
cornerRadius = vm.cornerRadius, | |
xAlign = vm.xAlign, | |
yAlign = vm.yAlign, | |
paddingAndSize = caretSize + caretPadding, | |
radiusAndPadding = cornerRadius + caretPadding; | |
if (xAlign === 'right') { | |
pt.x -= size.width; | |
} else if (xAlign === 'center') { | |
pt.x -= (size.width / 2); | |
} | |
if (yAlign === 'top') { | |
pt.y += paddingAndSize; | |
} else if (yAlign === 'bottom') { | |
pt.y -= size.height + paddingAndSize; | |
} else { | |
pt.y -= (size.height / 2); | |
} | |
if (yAlign === 'center') { | |
if (xAlign === 'left') { | |
pt.x += paddingAndSize; | |
} else if (xAlign === 'right') { | |
pt.x -= paddingAndSize; | |
} | |
} else { | |
if (xAlign === 'left') { | |
pt.x -= radiusAndPadding; | |
} else if (xAlign === 'right') { | |
pt.x += radiusAndPadding; | |
} | |
} | |
return pt; | |
}, | |
drawCaret: function(tooltipPoint, size, opacity) { | |
var vm = this._view; | |
var ctx = this._chart.ctx; | |
var x1, x2, x3; | |
var y1, y2, y3; | |
var caretSize = vm.caretSize; | |
var cornerRadius = vm.cornerRadius; | |
var xAlign = vm.xAlign, | |
yAlign = vm.yAlign; | |
var ptX = tooltipPoint.x, | |
ptY = tooltipPoint.y; | |
var width = size.width, | |
height = size.height; | |
if (yAlign === 'center') { | |
// Left or right side | |
if (xAlign === 'left') { | |
x1 = ptX; | |
x2 = x1 - caretSize; | |
x3 = x1; | |
} else { | |
x1 = ptX + width; | |
x2 = x1 + caretSize; | |
x3 = x1; | |
} | |
y2 = ptY + (height / 2); | |
y1 = y2 - caretSize; | |
y3 = y2 + caretSize; | |
} else { | |
if (xAlign === 'left') { | |
x1 = ptX + cornerRadius; | |
x2 = x1 + caretSize; | |
x3 = x2 + caretSize; | |
} else if (xAlign === 'right') { | |
x1 = ptX + width - cornerRadius; | |
x2 = x1 - caretSize; | |
x3 = x2 - caretSize; | |
} else { | |
x2 = ptX + (width / 2); | |
x1 = x2 - caretSize; | |
x3 = x2 + caretSize; | |
} | |
if (yAlign === 'top') { | |
y1 = ptY; | |
y2 = y1 - caretSize; | |
y3 = y1; | |
} else { | |
y1 = ptY + height; | |
y2 = y1 + caretSize; | |
y3 = y1; | |
} | |
} | |
var bgColor = helpers.color(vm.backgroundColor); | |
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.lineTo(x3, y3); | |
ctx.closePath(); | |
ctx.fill(); | |
}, | |
drawTitle: function(pt, vm, ctx, opacity) { | |
var title = vm.title; | |
if (title.length) { | |
ctx.textAlign = vm._titleAlign; | |
ctx.textBaseline = "top"; | |
var titleFontSize = vm.titleFontSize, | |
titleSpacing = vm.titleSpacing; | |
var titleFontColor = helpers.color(vm.titleFontColor); | |
ctx.fillStyle = titleFontColor.alpha(opacity * titleFontColor.alpha()).rgbString(); | |
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); | |
var i, len; | |
for (i = 0, len = title.length; i < len; ++i) { | |
ctx.fillText(title[i], pt.x, pt.y); | |
pt.y += titleFontSize + titleSpacing; // Line Height and spacing | |
if (i + 1 === title.length) { | |
pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing | |
} | |
} | |
} | |
}, | |
drawBody: function(pt, vm, ctx, opacity) { | |
var bodyFontSize = vm.bodyFontSize; | |
var bodySpacing = vm.bodySpacing; | |
var body = vm.body; | |
ctx.textAlign = vm._bodyAlign; | |
ctx.textBaseline = "top"; | |
var bodyFontColor = helpers.color(vm.bodyFontColor); | |
var textColor = bodyFontColor.alpha(opacity * bodyFontColor.alpha()).rgbString(); | |
ctx.fillStyle = textColor; | |
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); | |
// Before Body | |
var xLinePadding = 0; | |
var fillLineOfText = function(line) { | |
ctx.fillText(line, pt.x + xLinePadding, pt.y); | |
pt.y += bodyFontSize + bodySpacing; | |
}; | |
// Before body lines | |
helpers.each(vm.beforeBody, fillLineOfText); | |
var drawColorBoxes = body.length > 1; | |
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; | |
// Draw body lines now | |
helpers.each(body, function(bodyItem, i) { | |
helpers.each(bodyItem.before, fillLineOfText); | |
helpers.each(bodyItem.lines, function(line) { | |
// Draw Legend-like boxes if needed | |
if (drawColorBoxes) { | |
// Fill a white rect so that colours merge nicely if the opacity is < 1 | |
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString(); | |
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); | |
// Border | |
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString(); | |
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); | |
// Inner square | |
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString(); | |
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); | |
ctx.fillStyle = textColor; | |
} | |
fillLineOfText(line); | |
}); | |
helpers.each(bodyItem.after, fillLineOfText); | |
}); | |
// Reset back to 0 for after body | |
xLinePadding = 0; | |
// After body lines | |
helpers.each(vm.afterBody, fillLineOfText); | |
pt.y -= bodySpacing; // Remove last body spacing | |
}, | |
drawFooter: function(pt, vm, ctx, opacity) { | |
var footer = vm.footer; | |
if (footer.length) { | |
pt.y += vm.footerMarginTop; | |
ctx.textAlign = vm._footerAlign; | |
ctx.textBaseline = "top"; | |
var footerFontColor = helpers.color(vm.footerFontColor); | |
ctx.fillStyle = footerFontColor.alpha(opacity * footerFontColor.alpha()).rgbString(); | |
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); | |
helpers.each(footer, function(line) { | |
ctx.fillText(line, pt.x, pt.y); | |
pt.y += vm.footerFontSize + vm.footerSpacing; | |
}); | |
} | |
}, | |
draw: function() { | |
var ctx = this._chart.ctx; | |
var vm = this._view; | |
if (vm.opacity === 0) { | |
return; | |
} | |
var tooltipSize = this.getTooltipSize(vm); | |
var pt = { | |
x: vm.x, | |
y: vm.y | |
}; | |
// IE11/Edge does not like very small opacities, so snap to 0 | |
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; | |
if (this._options.enabled) { | |
// Draw Background | |
var bgColor = helpers.color(vm.backgroundColor); | |
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); | |
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius); | |
ctx.fill(); | |
// Draw Caret | |
this.drawCaret(pt, tooltipSize, opacity); | |
// Draw Title, Body, and Footer | |
pt.x += vm.xPadding; | |
pt.y += vm.yPadding; | |
// Titles | |
this.drawTitle(pt, vm, ctx, opacity); | |
// Body | |
this.drawBody(pt, vm, ctx, opacity); | |
// Footer | |
this.drawFooter(pt, vm, ctx, opacity); | |
} | |
} | |
}); | |
}; | |
},{}],34:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers, | |
globalOpts = Chart.defaults.global; | |
globalOpts.elements.arc = { | |
backgroundColor: globalOpts.defaultColor, | |
borderColor: "#fff", | |
borderWidth: 2 | |
}; | |
Chart.elements.Arc = Chart.Element.extend({ | |
inLabelRange: function(mouseX) { | |
var vm = this._view; | |
if (vm) { | |
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); | |
} else { | |
return false; | |
} | |
}, | |
inRange: function(chartX, chartY) { | |
var vm = this._view; | |
if (vm) { | |
var pointRelativePosition = helpers.getAngleFromPoint(vm, { | |
x: chartX, | |
y: chartY | |
}), | |
angle = pointRelativePosition.angle, | |
distance = pointRelativePosition.distance; | |
//Sanitise angle range | |
var startAngle = vm.startAngle; | |
var endAngle = vm.endAngle; | |
while (endAngle < startAngle) { | |
endAngle += 2.0 * Math.PI; | |
} | |
while (angle > endAngle) { | |
angle -= 2.0 * Math.PI; | |
} | |
while (angle < startAngle) { | |
angle += 2.0 * Math.PI; | |
} | |
//Check if within the range of the open/close angle | |
var betweenAngles = (angle >= startAngle && angle <= endAngle), | |
withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); | |
return (betweenAngles && withinRadius); | |
} else { | |
return false; | |
} | |
}, | |
tooltipPosition: function() { | |
var vm = this._view; | |
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2), | |
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; | |
return { | |
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), | |
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) | |
}; | |
}, | |
draw: function() { | |
var ctx = this._chart.ctx, | |
vm = this._view, | |
sA = vm.startAngle, | |
eA = vm.endAngle; | |
ctx.beginPath(); | |
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); | |
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); | |
ctx.closePath(); | |
ctx.strokeStyle = vm.borderColor; | |
ctx.lineWidth = vm.borderWidth; | |
ctx.fillStyle = vm.backgroundColor; | |
ctx.fill(); | |
ctx.lineJoin = 'bevel'; | |
if (vm.borderWidth) { | |
ctx.stroke(); | |
} | |
} | |
}); | |
}; | |
},{}],35:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var globalDefaults = Chart.defaults.global; | |
Chart.defaults.global.elements.line = { | |
tension: 0.4, | |
backgroundColor: globalDefaults.defaultColor, | |
borderWidth: 3, | |
borderColor: globalDefaults.defaultColor, | |
borderCapStyle: 'butt', | |
borderDash: [], | |
borderDashOffset: 0.0, | |
borderJoinStyle: 'miter', | |
capBezierPoints: true, | |
fill: true // do we fill in the area between the line and its base axis | |
}; | |
Chart.elements.Line = Chart.Element.extend({ | |
lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) { | |
var me = this; | |
var ctx = me._chart.ctx; | |
var spanGaps = me._view ? me._view.spanGaps : false; | |
if (point._view.skip && !spanGaps) { | |
skipHandler.call(me, previousPoint, point, nextPoint); | |
} else if (previousPoint._view.skip && !spanGaps) { | |
previousSkipHandler.call(me, previousPoint, point, nextPoint); | |
} else if (point._view.steppedLine === true) { | |
ctx.lineTo(point._view.x, previousPoint._view.y); | |
ctx.lineTo(point._view.x, point._view.y); | |
} else if (point._view.tension === 0) { | |
ctx.lineTo(point._view.x, point._view.y); | |
} else { | |
// Line between points | |
ctx.bezierCurveTo( | |
previousPoint._view.controlPointNextX, | |
previousPoint._view.controlPointNextY, | |
point._view.controlPointPreviousX, | |
point._view.controlPointPreviousY, | |
point._view.x, | |
point._view.y | |
); | |
} | |
}, | |
draw: function() { | |
var me = this; | |
var vm = me._view; | |
var ctx = me._chart.ctx; | |
var first = me._children[0]; | |
var last = me._children[me._children.length - 1]; | |
function loopBackToStart(drawLineToCenter) { | |
if (!first._view.skip && !last._view.skip) { | |
// Draw a bezier line from last to first | |
ctx.bezierCurveTo( | |
last._view.controlPointNextX, | |
last._view.controlPointNextY, | |
first._view.controlPointPreviousX, | |
first._view.controlPointPreviousY, | |
first._view.x, | |
first._view.y | |
); | |
} else if (drawLineToCenter) { | |
// Go to center | |
ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y); | |
} | |
} | |
ctx.save(); | |
// If we had points and want to fill this line, do so. | |
if (me._children.length > 0 && vm.fill) { | |
// Draw the background first (so the border is always on top) | |
ctx.beginPath(); | |
helpers.each(me._children, function(point, index) { | |
var previous = helpers.previousItem(me._children, index); | |
var next = helpers.nextItem(me._children, index); | |
// First point moves to it's starting position no matter what | |
if (index === 0) { | |
if (me._loop) { | |
ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y); | |
} else { | |
ctx.moveTo(point._view.x, vm.scaleZero); | |
} | |
if (point._view.skip) { | |
if (!me._loop) { | |
ctx.moveTo(next._view.x, me._view.scaleZero); | |
} | |
} else { | |
ctx.lineTo(point._view.x, point._view.y); | |
} | |
} else { | |
me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { | |
if (me._loop) { | |
// Go to center | |
ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y); | |
} else { | |
ctx.lineTo(previousPoint._view.x, me._view.scaleZero); | |
ctx.moveTo(nextPoint._view.x, me._view.scaleZero); | |
} | |
}, function(previousPoint, point) { | |
// If we skipped the last point, draw a line to ourselves so that the fill is nice | |
ctx.lineTo(point._view.x, point._view.y); | |
}); | |
} | |
}, me); | |
// For radial scales, loop back around to the first point | |
if (me._loop) { | |
loopBackToStart(true); | |
} else { | |
//Round off the line by going to the base of the chart, back to the start, then fill. | |
ctx.lineTo(me._children[me._children.length - 1]._view.x, vm.scaleZero); | |
ctx.lineTo(me._children[0]._view.x, vm.scaleZero); | |
} | |
ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor; | |
ctx.closePath(); | |
ctx.fill(); | |
} | |
var globalOptionLineElements = globalDefaults.elements.line; | |
// Now draw the line between all the points with any borders | |
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; | |
// IE 9 and 10 do not support line dash | |
if (ctx.setLineDash) { | |
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); | |
} | |
ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; | |
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; | |
ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; | |
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; | |
ctx.beginPath(); | |
helpers.each(me._children, function(point, index) { | |
var previous = helpers.previousItem(me._children, index); | |
var next = helpers.nextItem(me._children, index); | |
if (index === 0) { | |
ctx.moveTo(point._view.x, point._view.y); | |
} else { | |
me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { | |
ctx.moveTo(nextPoint._view.x, nextPoint._view.y); | |
}, function(previousPoint, point) { | |
// If we skipped the last point, move up to our point preventing a line from being drawn | |
ctx.moveTo(point._view.x, point._view.y); | |
}); | |
} | |
}, me); | |
if (me._loop && me._children.length > 0) { | |
loopBackToStart(); | |
} | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
}); | |
}; | |
},{}],36:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers, | |
globalOpts = Chart.defaults.global, | |
defaultColor = globalOpts.defaultColor; | |
globalOpts.elements.point = { | |
radius: 3, | |
pointStyle: 'circle', | |
backgroundColor: defaultColor, | |
borderWidth: 1, | |
borderColor: defaultColor, | |
// Hover | |
hitRadius: 1, | |
hoverRadius: 4, | |
hoverBorderWidth: 1 | |
}; | |
Chart.elements.Point = Chart.Element.extend({ | |
inRange: function(mouseX, mouseY) { | |
var vm = this._view; | |
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; | |
}, | |
inLabelRange: function(mouseX) { | |
var vm = this._view; | |
return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; | |
}, | |
tooltipPosition: function() { | |
var vm = this._view; | |
return { | |
x: vm.x, | |
y: vm.y, | |
padding: vm.radius + vm.borderWidth | |
}; | |
}, | |
draw: function() { | |
var vm = this._view; | |
var ctx = this._chart.ctx; | |
var pointStyle = vm.pointStyle; | |
var radius = vm.radius; | |
var x = vm.x; | |
var y = vm.y; | |
if (vm.skip) { | |
return; | |
} | |
ctx.strokeStyle = vm.borderColor || defaultColor; | |
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth); | |
ctx.fillStyle = vm.backgroundColor || defaultColor; | |
Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y); | |
} | |
}); | |
}; | |
},{}],37:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var globalOpts = Chart.defaults.global; | |
globalOpts.elements.rectangle = { | |
backgroundColor: globalOpts.defaultColor, | |
borderWidth: 0, | |
borderColor: globalOpts.defaultColor, | |
borderSkipped: 'bottom' | |
}; | |
Chart.elements.Rectangle = Chart.Element.extend({ | |
draw: function() { | |
var ctx = this._chart.ctx; | |
var vm = this._view; | |
var halfWidth = vm.width / 2, | |
leftX = vm.x - halfWidth, | |
rightX = vm.x + halfWidth, | |
top = vm.base - (vm.base - vm.y), | |
halfStroke = vm.borderWidth / 2; | |
// Canvas doesn't allow us to stroke inside the width so we can | |
// adjust the sizes to fit if we're setting a stroke on the line | |
if (vm.borderWidth) { | |
leftX += halfStroke; | |
rightX -= halfStroke; | |
top += halfStroke; | |
} | |
ctx.beginPath(); | |
ctx.fillStyle = vm.backgroundColor; | |
ctx.strokeStyle = vm.borderColor; | |
ctx.lineWidth = vm.borderWidth; | |
// Corner points, from bottom-left to bottom-right clockwise | |
// | 1 2 | | |
// | 0 3 | | |
var corners = [ | |
[leftX, vm.base], | |
[leftX, top], | |
[rightX, top], | |
[rightX, vm.base] | |
]; | |
// Find first (starting) corner with fallback to 'bottom' | |
var borders = ['bottom', 'left', 'top', 'right']; | |
var startCorner = borders.indexOf(vm.borderSkipped, 0); | |
if (startCorner === -1) | |
startCorner = 0; | |
function cornerAt(index) { | |
return corners[(startCorner + index) % 4]; | |
} | |
// Draw rectangle from 'startCorner' | |
ctx.moveTo.apply(ctx, cornerAt(0)); | |
for (var i = 1; i < 4; i++) | |
ctx.lineTo.apply(ctx, cornerAt(i)); | |
ctx.fill(); | |
if (vm.borderWidth) { | |
ctx.stroke(); | |
} | |
}, | |
height: function() { | |
var vm = this._view; | |
return vm.base - vm.y; | |
}, | |
inRange: function(mouseX, mouseY) { | |
var vm = this._view; | |
return vm ? | |
(vm.y < vm.base ? | |
(mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base) : | |
(mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y)) : | |
false; | |
}, | |
inLabelRange: function(mouseX) { | |
var vm = this._view; | |
return vm ? (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) : false; | |
}, | |
tooltipPosition: function() { | |
var vm = this._view; | |
return { | |
x: vm.x, | |
y: vm.y | |
}; | |
} | |
}); | |
}; | |
},{}],38:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
// Default config for a category scale | |
var defaultConfig = { | |
position: "bottom" | |
}; | |
var DatasetScale = Chart.Scale.extend({ | |
/** | |
* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use tose | |
* else fall back to data.labels | |
* @private | |
*/ | |
getLabels: function() { | |
var data = this.chart.data; | |
return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; | |
}, | |
// Implement this so that | |
determineDataLimits: function() { | |
var me = this; | |
var labels = me.getLabels(); | |
me.minIndex = 0; | |
me.maxIndex = labels.length - 1; | |
var findIndex; | |
if (me.options.ticks.min !== undefined) { | |
// user specified min value | |
findIndex = helpers.indexOf(labels, me.options.ticks.min); | |
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; | |
} | |
if (me.options.ticks.max !== undefined) { | |
// user specified max value | |
findIndex = helpers.indexOf(labels, me.options.ticks.max); | |
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; | |
} | |
me.min = labels[me.minIndex]; | |
me.max = labels[me.maxIndex]; | |
}, | |
buildTicks: function() { | |
var me = this; | |
var labels = me.getLabels(); | |
// If we are viewing some subset of labels, slice the original array | |
me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); | |
}, | |
getLabelForIndex: function(index) { | |
return this.ticks[index]; | |
}, | |
// Used to get data value locations. Value can either be an index or a numerical value | |
getPixelForValue: function(value, index, datasetIndex, includeOffset) { | |
var me = this; | |
// 1 is added because we need the length but we have the indexes | |
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); | |
if (value !== undefined) { | |
var labels = me.getLabels(); | |
var idx = labels.indexOf(value); | |
index = idx !== -1 ? idx : index; | |
} | |
if (me.isHorizontal()) { | |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); | |
var valueWidth = innerWidth / offsetAmt; | |
var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft; | |
if (me.options.gridLines.offsetGridLines && includeOffset) { | |
widthOffset += (valueWidth / 2); | |
} | |
return me.left + Math.round(widthOffset); | |
} else { | |
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); | |
var valueHeight = innerHeight / offsetAmt; | |
var heightOffset = (valueHeight * (index - me.minIndex)) + me.paddingTop; | |
if (me.options.gridLines.offsetGridLines && includeOffset) { | |
heightOffset += (valueHeight / 2); | |
} | |
return me.top + Math.round(heightOffset); | |
} | |
}, | |
getPixelForTick: function(index, includeOffset) { | |
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset); | |
}, | |
getValueForPixel: function(pixel) { | |
var me = this; | |
var value; | |
var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); | |
var horz = me.isHorizontal(); | |
var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); | |
var valueDimension = innerDimension / offsetAmt; | |
pixel -= horz ? me.left : me.top; | |
if (me.options.gridLines.offsetGridLines) { | |
pixel -= (valueDimension / 2); | |
} | |
pixel -= horz ? me.paddingLeft : me.paddingTop; | |
if (pixel <= 0) { | |
value = 0; | |
} else { | |
value = Math.round(pixel / valueDimension); | |
} | |
return value; | |
}, | |
getBasePixel: function() { | |
return this.bottom; | |
} | |
}); | |
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig); | |
}; | |
},{}],39:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var defaultConfig = { | |
position: "left", | |
ticks: { | |
callback: function(tickValue, index, ticks) { | |
// If we have lots of ticks, don't use the ones | |
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; | |
// If we have a number like 2.5 as the delta, figure out how many decimal places we need | |
if (Math.abs(delta) > 1) { | |
if (tickValue !== Math.floor(tickValue)) { | |
// not an integer | |
delta = tickValue - Math.floor(tickValue); | |
} | |
} | |
var logDelta = helpers.log10(Math.abs(delta)); | |
var tickString = ''; | |
if (tickValue !== 0) { | |
var numDecimal = -1 * Math.floor(logDelta); | |
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places | |
tickString = tickValue.toFixed(numDecimal); | |
} else { | |
tickString = '0'; // never show decimal places for 0 | |
} | |
return tickString; | |
} | |
} | |
}; | |
var LinearScale = Chart.LinearScaleBase.extend({ | |
determineDataLimits: function() { | |
var me = this; | |
var opts = me.options; | |
var chart = me.chart; | |
var data = chart.data; | |
var datasets = data.datasets; | |
var isHorizontal = me.isHorizontal(); | |
function IDMatches(meta) { | |
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; | |
} | |
// First Calculate the range | |
me.min = null; | |
me.max = null; | |
if (opts.stacked) { | |
var valuesPerType = {}; | |
var hasPositiveValues = false; | |
var hasNegativeValues = false; | |
helpers.each(datasets, function(dataset, datasetIndex) { | |
var meta = chart.getDatasetMeta(datasetIndex); | |
if (valuesPerType[meta.type] === undefined) { | |
valuesPerType[meta.type] = { | |
positiveValues: [], | |
negativeValues: [] | |
}; | |
} | |
// Store these per type | |
var positiveValues = valuesPerType[meta.type].positiveValues; | |
var negativeValues = valuesPerType[meta.type].negativeValues; | |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { | |
helpers.each(dataset.data, function(rawValue, index) { | |
var value = +me.getRightValue(rawValue); | |
if (isNaN(value) || meta.data[index].hidden) { | |
return; | |
} | |
positiveValues[index] = positiveValues[index] || 0; | |
negativeValues[index] = negativeValues[index] || 0; | |
if (opts.relativePoints) { | |
positiveValues[index] = 100; | |
} else { | |
if (value < 0) { | |
hasNegativeValues = true; | |
negativeValues[index] += value; | |
} else { | |
hasPositiveValues = true; | |
positiveValues[index] += value; | |
} | |
} | |
}); | |
} | |
}); | |
helpers.each(valuesPerType, function(valuesForType) { | |
var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); | |
var minVal = helpers.min(values); | |
var maxVal = helpers.max(values); | |
me.min = me.min === null ? minVal : Math.min(me.min, minVal); | |
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); | |
}); | |
} else { | |
helpers.each(datasets, function(dataset, datasetIndex) { | |
var meta = chart.getDatasetMeta(datasetIndex); | |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { | |
helpers.each(dataset.data, function(rawValue, index) { | |
var value = +me.getRightValue(rawValue); | |
if (isNaN(value) || meta.data[index].hidden) { | |
return; | |
} | |
if (me.min === null) { | |
me.min = value; | |
} else if (value < me.min) { | |
me.min = value; | |
} | |
if (me.max === null) { | |
me.max = value; | |
} else if (value > me.max) { | |
me.max = value; | |
} | |
}); | |
} | |
}); | |
} | |
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero | |
this.handleTickRangeOptions(); | |
}, | |
getTickLimit: function() { | |
var maxTicks; | |
var me = this; | |
var tickOpts = me.options.ticks; | |
if (me.isHorizontal()) { | |
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); | |
} else { | |
// The factor of 2 used to scale the font size has been experimentally determined. | |
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize); | |
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); | |
} | |
return maxTicks; | |
}, | |
// Called after the ticks are built. We need | |
handleDirectionalChanges: function() { | |
if (!this.isHorizontal()) { | |
// We are in a vertical orientation. The top value is the highest. So reverse the array | |
this.ticks.reverse(); | |
} | |
}, | |
getLabelForIndex: function(index, datasetIndex) { | |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); | |
}, | |
// Utils | |
getPixelForValue: function(value) { | |
// This must be called after fit has been run so that | |
// this.left, this.top, this.right, and this.bottom have been defined | |
var me = this; | |
var paddingLeft = me.paddingLeft; | |
var paddingBottom = me.paddingBottom; | |
var start = me.start; | |
var rightValue = +me.getRightValue(value); | |
var pixel; | |
var innerDimension; | |
var range = me.end - start; | |
if (me.isHorizontal()) { | |
innerDimension = me.width - (paddingLeft + me.paddingRight); | |
pixel = me.left + (innerDimension / range * (rightValue - start)); | |
return Math.round(pixel + paddingLeft); | |
} else { | |
innerDimension = me.height - (me.paddingTop + paddingBottom); | |
pixel = (me.bottom - paddingBottom) - (innerDimension / range * (rightValue - start)); | |
return Math.round(pixel); | |
} | |
}, | |
getValueForPixel: function(pixel) { | |
var me = this; | |
var isHorizontal = me.isHorizontal(); | |
var paddingLeft = me.paddingLeft; | |
var paddingBottom = me.paddingBottom; | |
var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom); | |
var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension; | |
return me.start + ((me.end - me.start) * offset); | |
}, | |
getPixelForTick: function(index) { | |
return this.getPixelForValue(this.ticksAsNumbers[index]); | |
} | |
}); | |
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig); | |
}; | |
},{}],40:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers, | |
noop = helpers.noop; | |
Chart.LinearScaleBase = Chart.Scale.extend({ | |
handleTickRangeOptions: function() { | |
var me = this; | |
var opts = me.options; | |
var tickOpts = opts.ticks; | |
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart, | |
// do nothing since that would make the chart weird. If the user really wants a weird chart | |
// axis, they can manually override it | |
if (tickOpts.beginAtZero) { | |
var minSign = helpers.sign(me.min); | |
var maxSign = helpers.sign(me.max); | |
if (minSign < 0 && maxSign < 0) { | |
// move the top up to 0 | |
me.max = 0; | |
} else if (minSign > 0 && maxSign > 0) { | |
// move the botttom down to 0 | |
me.min = 0; | |
} | |
} | |
if (tickOpts.min !== undefined) { | |
me.min = tickOpts.min; | |
} else if (tickOpts.suggestedMin !== undefined) { | |
me.min = Math.min(me.min, tickOpts.suggestedMin); | |
} | |
if (tickOpts.max !== undefined) { | |
me.max = tickOpts.max; | |
} else if (tickOpts.suggestedMax !== undefined) { | |
me.max = Math.max(me.max, tickOpts.suggestedMax); | |
} | |
if (me.min === me.max) { | |
me.max++; | |
if (!tickOpts.beginAtZero) { | |
me.min--; | |
} | |
} | |
}, | |
getTickLimit: noop, | |
handleDirectionalChanges: noop, | |
buildTicks: function() { | |
var me = this; | |
var opts = me.options; | |
var ticks = me.ticks = []; | |
var tickOpts = opts.ticks; | |
var getValueOrDefault = helpers.getValueOrDefault; | |
// Figure out what the max number of ticks we can support it is based on the size of | |
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 | |
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on | |
// the graph | |
var maxTicks = me.getTickLimit(); | |
// Make sure we always have at least 2 ticks | |
maxTicks = Math.max(2, maxTicks); | |
// To get a "nice" value for the tick spacing, we will use the appropriately named | |
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks | |
// for details. | |
var spacing; | |
var fixedStepSizeSet = (tickOpts.fixedStepSize && tickOpts.fixedStepSize > 0) || (tickOpts.stepSize && tickOpts.stepSize > 0); | |
if (fixedStepSizeSet) { | |
spacing = getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize); | |
} else { | |
var niceRange = helpers.niceNum(me.max - me.min, false); | |
spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); | |
} | |
var niceMin = Math.floor(me.min / spacing) * spacing; | |
var niceMax = Math.ceil(me.max / spacing) * spacing; | |
var numSpaces = (niceMax - niceMin) / spacing; | |
// If very close to our rounded value, use it. | |
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { | |
numSpaces = Math.round(numSpaces); | |
} else { | |
numSpaces = Math.ceil(numSpaces); | |
} | |
// Put the values into the ticks array | |
ticks.push(tickOpts.min !== undefined ? tickOpts.min : niceMin); | |
for (var j = 1; j < numSpaces; ++j) { | |
ticks.push(niceMin + (j * spacing)); | |
} | |
ticks.push(tickOpts.max !== undefined ? tickOpts.max : niceMax); | |
me.handleDirectionalChanges(); | |
// At this point, we need to update our max and min given the tick values since we have expanded the | |
// range of the scale | |
me.max = helpers.max(ticks); | |
me.min = helpers.min(ticks); | |
if (tickOpts.reverse) { | |
ticks.reverse(); | |
me.start = me.max; | |
me.end = me.min; | |
} else { | |
me.start = me.min; | |
me.end = me.max; | |
} | |
}, | |
convertTicksToLabels: function() { | |
var me = this; | |
me.ticksAsNumbers = me.ticks.slice(); | |
me.zeroLineIndex = me.ticks.indexOf(0); | |
Chart.Scale.prototype.convertTicksToLabels.call(me); | |
}, | |
}); | |
}; | |
},{}],41:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var defaultConfig = { | |
position: "left", | |
// label settings | |
ticks: { | |
callback: function(value, index, arr) { | |
var remain = value / (Math.pow(10, Math.floor(helpers.log10(value)))); | |
if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) { | |
return value.toExponential(); | |
} else { | |
return ''; | |
} | |
} | |
} | |
}; | |
var LogarithmicScale = Chart.Scale.extend({ | |
determineDataLimits: function() { | |
var me = this; | |
var opts = me.options; | |
var tickOpts = opts.ticks; | |
var chart = me.chart; | |
var data = chart.data; | |
var datasets = data.datasets; | |
var getValueOrDefault = helpers.getValueOrDefault; | |
var isHorizontal = me.isHorizontal(); | |
function IDMatches(meta) { | |
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; | |
} | |
// Calculate Range | |
me.min = null; | |
me.max = null; | |
if (opts.stacked) { | |
var valuesPerType = {}; | |
helpers.each(datasets, function(dataset, datasetIndex) { | |
var meta = chart.getDatasetMeta(datasetIndex); | |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { | |
if (valuesPerType[meta.type] === undefined) { | |
valuesPerType[meta.type] = []; | |
} | |
helpers.each(dataset.data, function(rawValue, index) { | |
var values = valuesPerType[meta.type]; | |
var value = +me.getRightValue(rawValue); | |
if (isNaN(value) || meta.data[index].hidden) { | |
return; | |
} | |
values[index] = values[index] || 0; | |
if (opts.relativePoints) { | |
values[index] = 100; | |
} else { | |
// Don't need to split positive and negative since the log scale can't handle a 0 crossing | |
values[index] += value; | |
} | |
}); | |
} | |
}); | |
helpers.each(valuesPerType, function(valuesForType) { | |
var minVal = helpers.min(valuesForType); | |
var maxVal = helpers.max(valuesForType); | |
me.min = me.min === null ? minVal : Math.min(me.min, minVal); | |
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); | |
}); | |
} else { | |
helpers.each(datasets, function(dataset, datasetIndex) { | |
var meta = chart.getDatasetMeta(datasetIndex); | |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { | |
helpers.each(dataset.data, function(rawValue, index) { | |
var value = +me.getRightValue(rawValue); | |
if (isNaN(value) || meta.data[index].hidden) { | |
return; | |
} | |
if (me.min === null) { | |
me.min = value; | |
} else if (value < me.min) { | |
me.min = value; | |
} | |
if (me.max === null) { | |
me.max = value; | |
} else if (value > me.max) { | |
me.max = value; | |
} | |
}); | |
} | |
}); | |
} | |
me.min = getValueOrDefault(tickOpts.min, me.min); | |
me.max = getValueOrDefault(tickOpts.max, me.max); | |
if (me.min === me.max) { | |
if (me.min !== 0 && me.min !== null) { | |
me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); | |
me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); | |
} else { | |
me.min = 1; | |
me.max = 10; | |
} | |
} | |
}, | |
buildTicks: function() { | |
var me = this; | |
var opts = me.options; | |
var tickOpts = opts.ticks; | |
var getValueOrDefault = helpers.getValueOrDefault; | |
// Reset the ticks array. Later on, we will draw a grid line at these positions | |
// The array simply contains the numerical value of the spots where ticks will be | |
var ticks = me.ticks = []; | |
// Figure out what the max number of ticks we can support it is based on the size of | |
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 | |
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on | |
// the graph | |
var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(me.min)))); | |
while (tickVal < me.max) { | |
ticks.push(tickVal); | |
var exp = Math.floor(helpers.log10(tickVal)); | |
var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1; | |
if (significand === 10) { | |
significand = 1; | |
++exp; | |
} | |
tickVal = significand * Math.pow(10, exp); | |
} | |
var lastTick = getValueOrDefault(tickOpts.max, tickVal); | |
ticks.push(lastTick); | |
if (!me.isHorizontal()) { | |
// We are in a vertical orientation. The top value is the highest. So reverse the array | |
ticks.reverse(); | |
} | |
// At this point, we need to update our max and min given the tick values since we have expanded the | |
// range of the scale | |
me.max = helpers.max(ticks); | |
me.min = helpers.min(ticks); | |
if (tickOpts.reverse) { | |
ticks.reverse(); | |
me.start = me.max; | |
me.end = me.min; | |
} else { | |
me.start = me.min; | |
me.end = me.max; | |
} | |
}, | |
convertTicksToLabels: function() { | |
this.tickValues = this.ticks.slice(); | |
Chart.Scale.prototype.convertTicksToLabels.call(this); | |
}, | |
// Get the correct tooltip label | |
getLabelForIndex: function(index, datasetIndex) { | |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); | |
}, | |
getPixelForTick: function(index) { | |
return this.getPixelForValue(this.tickValues[index]); | |
}, | |
getPixelForValue: function(value) { | |
var me = this; | |
var innerDimension; | |
var pixel; | |
var start = me.start; | |
var newVal = +me.getRightValue(value); | |
var range = helpers.log10(me.end) - helpers.log10(start); | |
var paddingTop = me.paddingTop; | |
var paddingBottom = me.paddingBottom; | |
var paddingLeft = me.paddingLeft; | |
if (me.isHorizontal()) { | |
if (newVal === 0) { | |
pixel = me.left + paddingLeft; | |
} else { | |
innerDimension = me.width - (paddingLeft + me.paddingRight); | |
pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); | |
pixel += paddingLeft; | |
} | |
} else { | |
// Bottom - top since pixels increase downard on a screen | |
if (newVal === 0) { | |
pixel = me.top + paddingTop; | |
} else { | |
innerDimension = me.height - (paddingTop + paddingBottom); | |
pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); | |
} | |
} | |
return pixel; | |
}, | |
getValueForPixel: function(pixel) { | |
var me = this; | |
var range = helpers.log10(me.end) - helpers.log10(me.start); | |
var value, innerDimension; | |
if (me.isHorizontal()) { | |
innerDimension = me.width - (me.paddingLeft + me.paddingRight); | |
value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension); | |
} else { | |
innerDimension = me.height - (me.paddingTop + me.paddingBottom); | |
value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start; | |
} | |
return value; | |
} | |
}); | |
Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig); | |
}; | |
},{}],42:[function(require,module,exports){ | |
"use strict"; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var globalDefaults = Chart.defaults.global; | |
var defaultConfig = { | |
display: true, | |
//Boolean - Whether to animate scaling the chart from the centre | |
animate: true, | |
lineArc: false, | |
position: "chartArea", | |
angleLines: { | |
display: true, | |
color: "rgba(0, 0, 0, 0.1)", | |
lineWidth: 1 | |
}, | |
// label settings | |
ticks: { | |
//Boolean - Show a backdrop to the scale label | |
showLabelBackdrop: true, | |
//String - The colour of the label backdrop | |
backdropColor: "rgba(255,255,255,0.75)", | |
//Number - The backdrop padding above & below the label in pixels | |
backdropPaddingY: 2, | |
//Number - The backdrop padding to the side of the label in pixels | |
backdropPaddingX: 2 | |
}, | |
pointLabels: { | |
//Number - Point label font size in pixels | |
fontSize: 10, | |
//Function - Used to convert point labels | |
callback: function(label) { | |
return label; | |
} | |
} | |
}; | |
var LinearRadialScale = Chart.LinearScaleBase.extend({ | |
getValueCount: function() { | |
return this.chart.data.labels.length; | |
}, | |
setDimensions: function() { | |
var me = this; | |
var opts = me.options; | |
var tickOpts = opts.ticks; | |
// Set the unconstrained dimension before label rotation | |
me.width = me.maxWidth; | |
me.height = me.maxHeight; | |
me.xCenter = Math.round(me.width / 2); | |
me.yCenter = Math.round(me.height / 2); | |
var minSize = helpers.min([me.height, me.width]); | |
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); | |
me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); | |
}, | |
determineDataLimits: function() { | |
var me = this; | |
var chart = me.chart; | |
me.min = null; | |
me.max = null; | |
helpers.each(chart.data.datasets, function(dataset, datasetIndex) { | |
if (chart.isDatasetVisible(datasetIndex)) { | |
var meta = chart.getDatasetMeta(datasetIndex); | |
helpers.each(dataset.data, function(rawValue, index) { | |
var value = +me.getRightValue(rawValue); | |
if (isNaN(value) || meta.data[index].hidden) { | |
return; | |
} | |
if (me.min === null) { | |
me.min = value; | |
} else if (value < me.min) { | |
me.min = value; | |
} | |
if (me.max === null) { | |
me.max = value; | |
} else if (value > me.max) { | |
me.max = value; | |
} | |
}); | |
} | |
}); | |
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero | |
me.handleTickRangeOptions(); | |
}, | |
getTickLimit: function() { | |
var tickOpts = this.options.ticks; | |
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); | |
return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); | |
}, | |
convertTicksToLabels: function() { | |
var me = this; | |
Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); | |
// Point labels | |
me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); | |
}, | |
getLabelForIndex: function(index, datasetIndex) { | |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); | |
}, | |
fit: function() { | |
/* | |
* Right, this is really confusing and there is a lot of maths going on here | |
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 | |
* | |
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif | |
* | |
* Solution: | |
* | |
* We assume the radius of the polygon is half the size of the canvas at first | |
* at each index we check if the text overlaps. | |
* | |
* Where it does, we store that angle and that index. | |
* | |
* After finding the largest index and angle we calculate how much we need to remove | |
* from the shape radius to move the point inwards by that x. | |
* | |
* We average the left and right distances to get the maximum shape radius that can fit in the box | |
* along with labels. | |
* | |
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion | |
* on each side, removing that from the size, halving it and adding the left x protrusion width. | |
* | |
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels | |
* and position it in the most space efficient manner | |
* | |
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif | |
*/ | |
var pointLabels = this.options.pointLabels; | |
var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize); | |
var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle); | |
var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily); | |
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); | |
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. | |
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points | |
var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]), | |
pointPosition, | |
i, | |
textWidth, | |
halfTextWidth, | |
furthestRight = this.width, | |
furthestRightIndex, | |
furthestRightAngle, | |
furthestLeft = 0, | |
furthestLeftIndex, | |
furthestLeftAngle, | |
xProtrusionLeft, | |
xProtrusionRight, | |
radiusReductionRight, | |
radiusReductionLeft; | |
this.ctx.font = pointLabeFont; | |
for (i = 0; i < this.getValueCount(); i++) { | |
// 5px to space the text slightly out - similar to what we do in the draw function. | |
pointPosition = this.getPointPosition(i, largestPossibleRadius); | |
textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5; | |
// Add quarter circle to make degree 0 mean top of circle | |
var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); | |
var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; | |
if (angle === 0 || angle === 180) { | |
// At angle 0 and 180, we're at exactly the top/bottom | |
// of the radar chart, so text will be aligned centrally, so we'll half it and compare | |
// w/left and right text sizes | |
halfTextWidth = textWidth / 2; | |
if (pointPosition.x + halfTextWidth > furthestRight) { | |
furthestRight = pointPosition.x + halfTextWidth; | |
furthestRightIndex = i; | |
} | |
if (pointPosition.x - halfTextWidth < furthestLeft) { | |
furthestLeft = pointPosition.x - halfTextWidth; | |
furthestLeftIndex = i; | |
} | |
} else if (angle < 180) { | |
// Less than half the values means we'll left align the text | |
if (pointPosition.x + textWidth > furthestRight) { | |
furthestRight = pointPosition.x + textWidth; | |
furthestRightIndex = i; | |
} | |
} else { | |
// More than half the values means we'll right align the text | |
if (pointPosition.x - textWidth < furthestLeft) { | |
furthestLeft = pointPosition.x - textWidth; | |
furthestLeftIndex = i; | |
} | |
} | |
} | |
xProtrusionLeft = furthestLeft; | |
xProtrusionRight = Math.ceil(furthestRight - this.width); | |
furthestRightAngle = this.getIndexAngle(furthestRightIndex); | |
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); | |
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2); | |
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2); | |
// Ensure we actually need to reduce the size of the chart | |
radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0; | |
radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; | |
this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2); | |
this.setCenterPoint(radiusReductionLeft, radiusReductionRight); | |
}, | |
setCenterPoint: function(leftMovement, rightMovement) { | |
var me = this; | |
var maxRight = me.width - rightMovement - me.drawingArea, | |
maxLeft = leftMovement + me.drawingArea; | |
me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); | |
// Always vertically in the centre as the text height doesn't change | |
me.yCenter = Math.round((me.height / 2) + me.top); | |
}, | |
getIndexAngle: function(index) { | |
var angleMultiplier = (Math.PI * 2) / this.getValueCount(); | |
var startAngle = this.chart.options && this.chart.options.startAngle ? | |
this.chart.options.startAngle : | |
0; | |
var startAngleRadians = startAngle * Math.PI * 2 / 360; | |
// Start from the top instead of right, so remove a quarter of the circle | |
return index * angleMultiplier - (Math.PI / 2) + startAngleRadians; | |
}, | |
getDistanceFromCenterForValue: function(value) { | |
var me = this; | |
if (value === null) { | |
return 0; // null always in center | |
} | |
// Take into account half font size + the yPadding of the top value | |
var scalingFactor = me.drawingArea / (me.max - me.min); | |
if (me.options.reverse) { | |
return (me.max - value) * scalingFactor; | |
} else { | |
return (value - me.min) * scalingFactor; | |
} | |
}, | |
getPointPosition: function(index, distanceFromCenter) { | |
var me = this; | |
var thisAngle = me.getIndexAngle(index); | |
return { | |
x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, | |
y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter | |
}; | |
}, | |
getPointPositionForValue: function(index, value) { | |
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); | |
}, | |
getBasePosition: function() { | |
var me = this; | |
var min = me.min; | |
var max = me.max; | |
return me.getPointPositionForValue(0, | |
me.beginAtZero? 0: | |
min < 0 && max < 0? max : | |
min > 0 && max > 0? min : | |
0); | |
}, | |
draw: function() { | |
var me = this; | |
var opts = me.options; | |
var gridLineOpts = opts.gridLines; | |
var tickOpts = opts.ticks; | |
var angleLineOpts = opts.angleLines; | |
var pointLabelOpts = opts.pointLabels; | |
var getValueOrDefault = helpers.getValueOrDefault; | |
if (opts.display) { | |
var ctx = me.ctx; | |
// Tick Font | |
var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); | |
var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); | |
var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); | |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); | |
helpers.each(me.ticks, function(label, index) { | |
// Don't draw a centre value (if it is minimum) | |
if (index > 0 || opts.reverse) { | |
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); | |
var yHeight = me.yCenter - yCenterOffset; | |
// Draw circular lines around the scale | |
if (gridLineOpts.display && index !== 0) { | |
ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1); | |
ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); | |
if (opts.lineArc) { | |
// Draw circular arcs between the points | |
ctx.beginPath(); | |
ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2); | |
ctx.closePath(); | |
ctx.stroke(); | |
} else { | |
// Draw straight lines connecting each index | |
ctx.beginPath(); | |
for (var i = 0; i < me.getValueCount(); i++) { | |
var pointPosition = me.getPointPosition(i, yCenterOffset); | |
if (i === 0) { | |
ctx.moveTo(pointPosition.x, pointPosition.y); | |
} else { | |
ctx.lineTo(pointPosition.x, pointPosition.y); | |
} | |
} | |
ctx.closePath(); | |
ctx.stroke(); | |
} | |
} | |
if (tickOpts.display) { | |
var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); | |
ctx.font = tickLabelFont; | |
if (tickOpts.showLabelBackdrop) { | |
var labelWidth = ctx.measureText(label).width; | |
ctx.fillStyle = tickOpts.backdropColor; | |
ctx.fillRect( | |
me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX, | |
yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY, | |
labelWidth + tickOpts.backdropPaddingX * 2, | |
tickFontSize + tickOpts.backdropPaddingY * 2 | |
); | |
} | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = "middle"; | |
ctx.fillStyle = tickFontColor; | |
ctx.fillText(label, me.xCenter, yHeight); | |
} | |
} | |
}); | |
if (!opts.lineArc) { | |
ctx.lineWidth = angleLineOpts.lineWidth; | |
ctx.strokeStyle = angleLineOpts.color; | |
var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max); | |
// Point Label Font | |
var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize); | |
var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle); | |
var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily); | |
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); | |
for (var i = me.getValueCount() - 1; i >= 0; i--) { | |
if (angleLineOpts.display) { | |
var outerPosition = me.getPointPosition(i, outerDistance); | |
ctx.beginPath(); | |
ctx.moveTo(me.xCenter, me.yCenter); | |
ctx.lineTo(outerPosition.x, outerPosition.y); | |
ctx.stroke(); | |
ctx.closePath(); | |
} | |
// Extra 3px out for some label spacing | |
var pointLabelPosition = me.getPointPosition(i, outerDistance + 5); | |
// Keep this in loop since we may support array properties here | |
var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); | |
ctx.font = pointLabeFont; | |
ctx.fillStyle = pointLabelFontColor; | |
var pointLabels = me.pointLabels; | |
// Add quarter circle to make degree 0 mean top of circle | |
var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); | |
var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; | |
if (angle === 0 || angle === 180) { | |
ctx.textAlign = 'center'; | |
} else if (angle < 180) { | |
ctx.textAlign = 'left'; | |
} else { | |
ctx.textAlign = 'right'; | |
} | |
// Set the correct text baseline based on outer positioning | |
if (angle === 90 || angle === 270) { | |
ctx.textBaseline = 'middle'; | |
} else if (angle > 270 || angle < 90) { | |
ctx.textBaseline = 'bottom'; | |
} else { | |
ctx.textBaseline = 'top'; | |
} | |
ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y); | |
} | |
} | |
} | |
} | |
}); | |
Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig); | |
}; | |
},{}],43:[function(require,module,exports){ | |
/*global window: false */ | |
"use strict"; | |
var moment = require('moment'); | |
moment = typeof(moment) === 'function' ? moment : window.moment; | |
module.exports = function(Chart) { | |
var helpers = Chart.helpers; | |
var time = { | |
units: [{ | |
name: 'millisecond', | |
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] | |
}, { | |
name: 'second', | |
steps: [1, 2, 5, 10, 30] | |
}, { | |
name: 'minute', | |
steps: [1, 2, 5, 10, 30] | |
}, { | |
name: 'hour', | |
steps: [1, 2, 3, 6, 12] | |
}, { | |
name: 'day', | |
steps: [1, 2, 5] | |
}, { | |
name: 'week', | |
maxStep: 4 | |
}, { | |
name: 'month', | |
maxStep: 3 | |
}, { | |
name: 'quarter', | |
maxStep: 4 | |
}, { | |
name: 'year', | |
maxStep: false | |
}] | |
}; | |
var defaultConfig = { | |
position: "bottom", | |
time: { | |
parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment | |
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ | |
unit: false, // false == automatic or override with week, month, year, etc. | |
round: false, // none, or override with week, month, year, etc. | |
displayFormat: false, // DEPRECATED | |
isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ | |
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ | |
displayFormats: { | |
'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM, | |
'second': 'h:mm:ss a', // 11:20:01 AM | |
'minute': 'h:mm:ss a', // 11:20:01 AM | |
'hour': 'MMM D, hA', // Sept 4, 5PM | |
'day': 'll', // Sep 4 2015 | |
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ? | |
'month': 'MMM YYYY', // Sept 2015 | |
'quarter': '[Q]Q - YYYY', // Q3 | |
'year': 'YYYY' // 2015 | |
} | |
}, | |
ticks: { | |
autoSkip: false | |
} | |
}; | |
var TimeScale = Chart.Scale.extend({ | |
initialize: function() { | |
if (!moment) { | |
throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com'); | |
} | |
Chart.Scale.prototype.initialize.call(this); | |
}, | |
getLabelMoment: function(datasetIndex, index) { | |
return this.labelMoments[datasetIndex][index]; | |
}, | |
getMomentStartOf: function(tick) { | |
var me = this; | |
if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) { | |
return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday); | |
} else { | |
return tick.clone().startOf(me.tickUnit); | |
} | |
}, | |
determineDataLimits: function() { | |
var me = this; | |
me.labelMoments = []; | |
// Only parse these once. If the dataset does not have data as x,y pairs, we will use | |
// these | |
var scaleLabelMoments = []; | |
if (me.chart.data.labels && me.chart.data.labels.length > 0) { | |
helpers.each(me.chart.data.labels, function(label) { | |
var labelMoment = me.parseTime(label); | |
if (labelMoment.isValid()) { | |
if (me.options.time.round) { | |
labelMoment.startOf(me.options.time.round); | |
} | |
scaleLabelMoments.push(labelMoment); | |
} | |
}, me); | |
me.firstTick = moment.min.call(me, scaleLabelMoments); | |
me.lastTick = moment.max.call(me, scaleLabelMoments); | |
} else { | |
me.firstTick = null; | |
me.lastTick = null; | |
} | |
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { | |
var momentsForDataset = []; | |
var datasetVisible = me.chart.isDatasetVisible(datasetIndex); | |
if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) { | |
helpers.each(dataset.data, function(value) { | |
var labelMoment = me.parseTime(me.getRightValue(value)); | |
if (labelMoment.isValid()) { | |
if (me.options.time.round) { | |
labelMoment.startOf(me.options.time.round); | |
} | |
momentsForDataset.push(labelMoment); | |
if (datasetVisible) { | |
// May have gone outside the scale ranges, make sure we keep the first and last ticks updated | |
me.firstTick = me.firstTick !== null ? moment.min(me.firstTick, labelMoment) : labelMoment; | |
me.lastTick = me.lastTick !== null ? moment.max(me.lastTick, labelMoment) : labelMoment; | |
} | |
} | |
}, me); | |
} else { | |
// We have no labels. Use the ones from the scale | |
momentsForDataset = scaleLabelMoments; | |
} | |
me.labelMoments.push(momentsForDataset); | |
}, me); | |
// Set these after we've done all the data | |
if (me.options.time.min) { | |
me.firstTick = me.parseTime(me.options.time.min); | |
} | |
if (me.options.time.max) { | |
me.lastTick = me.parseTime(me.options.time.max); | |
} | |
// We will modify these, so clone for later | |
me.firstTick = (me.firstTick || moment()).clone(); | |
me.lastTick = (me.lastTick || moment()).clone(); | |
}, | |
buildTicks: function() { | |
var me = this; | |
me.ctx.save(); | |
var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); | |
var tickFontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); | |
var tickFontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily); | |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); | |
me.ctx.font = tickLabelFont; | |
me.ticks = []; | |
me.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step | |
me.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc) | |
// Set unit override if applicable | |
if (me.options.time.unit) { | |
me.tickUnit = me.options.time.unit || 'day'; | |
me.displayFormat = me.options.time.displayFormats[me.tickUnit]; | |
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); | |
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, 1); | |
} else { | |
// Determine the smallest needed unit of the time | |
var innerWidth = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); | |
// Crude approximation of what the label length might be | |
var tempFirstLabel = me.tickFormatFunction(me.firstTick, 0, []); | |
var tickLabelWidth = me.ctx.measureText(tempFirstLabel).width; | |
var cosRotation = Math.cos(helpers.toRadians(me.options.ticks.maxRotation)); | |
var sinRotation = Math.sin(helpers.toRadians(me.options.ticks.maxRotation)); | |
tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); | |
var labelCapacity = innerWidth / (tickLabelWidth); | |
// Start as small as possible | |
me.tickUnit = 'millisecond'; | |
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); | |
me.displayFormat = me.options.time.displayFormats[me.tickUnit]; | |
var unitDefinitionIndex = 0; | |
var unitDefinition = time.units[unitDefinitionIndex]; | |
// While we aren't ideal and we don't have units left | |
while (unitDefinitionIndex < time.units.length) { | |
// Can we scale this unit. If `false` we can scale infinitely | |
me.unitScale = 1; | |
if (helpers.isArray(unitDefinition.steps) && Math.ceil(me.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) { | |
// Use one of the prefedined steps | |
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) { | |
if (unitDefinition.steps[idx] >= Math.ceil(me.scaleSizeInUnits / labelCapacity)) { | |
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, unitDefinition.steps[idx]); | |
break; | |
} | |
} | |
break; | |
} else if ((unitDefinition.maxStep === false) || (Math.ceil(me.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) { | |
// We have a max step. Scale this unit | |
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, Math.ceil(me.scaleSizeInUnits / labelCapacity)); | |
break; | |
} else { | |
// Move to the next unit up | |
++unitDefinitionIndex; | |
unitDefinition = time.units[unitDefinitionIndex]; | |
me.tickUnit = unitDefinition.name; | |
var leadingUnitBuffer = me.firstTick.diff(me.getMomentStartOf(me.firstTick), me.tickUnit, true); | |
var trailingUnitBuffer = me.getMomentStartOf(me.lastTick.clone().add(1, me.tickUnit)).diff(me.lastTick, me.tickUnit, true); | |
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer; | |
me.displayFormat = me.options.time.displayFormats[unitDefinition.name]; | |
} | |
} | |
} | |
var roundedStart; | |
// Only round the first tick if we have no hard minimum | |
if (!me.options.time.min) { | |
me.firstTick = me.getMomentStartOf(me.firstTick); | |
roundedStart = me.firstTick; | |
} else { | |
roundedStart = me.getMomentStartOf(me.firstTick); | |
} | |
// Only round the last tick if we have no hard maximum | |
if (!me.options.time.max) { | |
var roundedEnd = me.getMomentStartOf(me.lastTick); | |
if (roundedEnd.diff(me.lastTick, me.tickUnit, true) !== 0) { | |
// Do not use end of because we need me to be in the next time unit | |
me.lastTick = me.getMomentStartOf(me.lastTick.add(1, me.tickUnit)); | |
} | |
} | |
me.smallestLabelSeparation = me.width; | |
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { | |
for (var i = 1; i < me.labelMoments[datasetIndex].length; i++) { | |
me.smallestLabelSeparation = Math.min(me.smallestLabelSeparation, me.labelMoments[datasetIndex][i].diff(me.labelMoments[datasetIndex][i - 1], me.tickUnit, true)); | |
} | |
}, me); | |
// Tick displayFormat override | |
if (me.options.time.displayFormat) { | |
me.displayFormat = me.options.time.displayFormat; | |
} | |
// first tick. will have been rounded correctly if options.time.min is not specified | |
me.ticks.push(me.firstTick.clone()); | |
// For every unit in between the first and last moment, create a moment and add it to the ticks tick | |
for (var i = 1; i <= me.scaleSizeInUnits; ++i) { | |
var newTick = roundedStart.clone().add(i, me.tickUnit); | |
// Are we greater than the max time | |
if (me.options.time.max && newTick.diff(me.lastTick, me.tickUnit, true) >= 0) { | |
break; | |
} | |
if (i % me.unitScale === 0) { | |
me.ticks.push(newTick); | |
} | |
} | |
// Always show the right tick | |
var diff = me.ticks[me.ticks.length - 1].diff(me.lastTick, me.tickUnit); | |
if (diff !== 0 || me.scaleSizeInUnits === 0) { | |
// this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart | |
// but the last tick was not rounded. | |
if (me.options.time.max) { | |
me.ticks.push(me.lastTick.clone()); | |
me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true); | |
} else { | |
me.ticks.push(me.lastTick.clone()); | |
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); | |
} | |
} | |
me.ctx.restore(); | |
}, | |
// Get tooltip label | |
getLabelForIndex: function(index, datasetIndex) { | |
var me = this; | |
var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : ''; | |
if (typeof me.chart.data.datasets[datasetIndex].data[0] === 'object') { | |
label = me.getRightValue(me.chart.data.datasets[datasetIndex].data[index]); | |
} | |
// Format nicely | |
if (me.options.time.tooltipFormat) { | |
label = me.parseTime(label).format(me.options.time.tooltipFormat); | |
} | |
return label; | |
}, | |
// Function to format an individual tick mark | |
tickFormatFunction: function(tick, index, ticks) { | |
var formattedTick = tick.format(this.displayFormat); | |
var tickOpts = this.options.ticks; | |
var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback); | |
if (callback) { | |
return callback(formattedTick, index, ticks); | |
} else { | |
return formattedTick; | |
} | |
}, | |
convertTicksToLabels: function() { | |
var me = this; | |
me.tickMoments = me.ticks; | |
me.ticks = me.ticks.map(me.tickFormatFunction, me); | |
}, | |
getPixelForValue: function(value, index, datasetIndex) { | |
var me = this; | |
if (!value || !value.isValid) { | |
// not already a moment object | |
value = moment(me.getRightValue(value)); | |
} | |
var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index); | |
if (labelMoment) { | |
var offset = labelMoment.diff(me.firstTick, me.tickUnit, true); | |
var decimal = offset !== 0 ? offset / me.scaleSizeInUnits : offset; | |
if (me.isHorizontal()) { | |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); | |
var valueOffset = (innerWidth * decimal) + me.paddingLeft; | |
return me.left + Math.round(valueOffset); | |
} else { | |
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); | |
var heightOffset = (innerHeight * decimal) + me.paddingTop; | |
return me.top + Math.round(heightOffset); | |
} | |
} | |
}, | |
getPixelForTick: function(index) { | |
return this.getPixelForValue(this.tickMoments[index], null, null); | |
}, | |
getValueForPixel: function(pixel) { | |
var me = this; | |
var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); | |
var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension; | |
offset *= me.scaleSizeInUnits; | |
return me.firstTick.clone().add(moment.duration(offset, me.tickUnit).asSeconds(), 'seconds'); | |
}, | |
parseTime: function(label) { | |
var me = this; | |
if (typeof me.options.time.parser === 'string') { | |
return moment(label, me.options.time.parser); | |
} | |
if (typeof me.options.time.parser === 'function') { | |
return me.options.time.parser(label); | |
} | |
// Date objects | |
if (typeof label.getMonth === 'function' || typeof label === 'number') { | |
return moment(label); | |
} | |
// Moment support | |
if (label.isValid && label.isValid()) { | |
return label; | |
} | |
// Custom parsing (return an instance of moment) | |
if (typeof me.options.time.format !== 'string' && me.options.time.format.call) { | |
console.warn("options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale"); | |
return me.options.time.format(label); | |
} | |
// Moment format parsing | |
return moment(label, me.options.time.format); | |
} | |
}); | |
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig); | |
}; | |
},{"moment":6}],"chart.js":[function(require,module,exports){ | |
/** | |
* @namespace Chart | |
*/ | |
var Chart = require('./core/core.js')(); | |
require('./core/core.helpers')(Chart); | |
require('./core/core.canvasHelpers')(Chart); | |
require('./core/core.element')(Chart); | |
require('./core/core.animation')(Chart); | |
require('./core/core.controller')(Chart); | |
require('./core/core.datasetController')(Chart); | |
require('./core/core.layoutService')(Chart); | |
require('./core/core.scaleService')(Chart); | |
require('./core/core.plugin.js')(Chart); | |
require('./core/core.scale')(Chart); | |
require('./core/core.title')(Chart); | |
require('./core/core.legend')(Chart); | |
require('./core/core.tooltip')(Chart); | |
require('./elements/element.arc')(Chart); | |
require('./elements/element.line')(Chart); | |
require('./elements/element.point')(Chart); | |
require('./elements/element.rectangle')(Chart); | |
require('./scales/scale.linearbase.js')(Chart); | |
require('./scales/scale.category')(Chart); | |
require('./scales/scale.linear')(Chart); | |
require('./scales/scale.logarithmic')(Chart); | |
require('./scales/scale.radialLinear')(Chart); | |
require('./scales/scale.time')(Chart); | |
// Controllers must be loaded after elements | |
// See Chart.core.datasetController.dataElementType | |
require('./controllers/controller.bar')(Chart); | |
require('./controllers/controller.bubble')(Chart); | |
require('./controllers/controller.doughnut')(Chart); | |
require('./controllers/controller.line')(Chart); | |
require('./controllers/controller.polarArea')(Chart); | |
require('./controllers/controller.radar')(Chart); | |
require('./charts/Chart.Bar')(Chart); | |
require('./charts/Chart.Bubble')(Chart); | |
require('./charts/Chart.Doughnut')(Chart); | |
require('./charts/Chart.Line')(Chart); | |
require('./charts/Chart.PolarArea')(Chart); | |
require('./charts/Chart.Radar')(Chart); | |
require('./charts/Chart.Scatter')(Chart); | |
window.Chart = module.exports = Chart; | |
},{"./charts/Chart.Bar":7,"./charts/Chart.Bubble":8,"./charts/Chart.Doughnut":9,"./charts/Chart.Line":10,"./charts/Chart.PolarArea":11,"./charts/Chart.Radar":12,"./charts/Chart.Scatter":13,"./controllers/controller.bar":14,"./controllers/controller.bubble":15,"./controllers/controller.doughnut":16,"./controllers/controller.line":17,"./controllers/controller.polarArea":18,"./controllers/controller.radar":19,"./core/core.animation":20,"./core/core.canvasHelpers":21,"./core/core.controller":22,"./core/core.datasetController":23,"./core/core.element":24,"./core/core.helpers":25,"./core/core.js":26,"./core/core.layoutService":27,"./core/core.legend":28,"./core/core.plugin.js":29,"./core/core.scale":30,"./core/core.scaleService":31,"./core/core.title":32,"./core/core.tooltip":33,"./elements/element.arc":34,"./elements/element.line":35,"./elements/element.point":36,"./elements/element.rectangle":37,"./scales/scale.category":38,"./scales/scale.linear":39,"./scales/scale.linearbase.js":40,"./scales/scale.logarithmic":41,"./scales/scale.radialLinear":42,"./scales/scale.time":43}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsIm5vZGVfbW9kdWxlcy9jaGFydGpzLWNvbG9yL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2NoYXJ0anMtY29sb3Ivbm9kZV9tb2R1bGVzL2NoYXJ0anMtY29sb3Itc3RyaW5nL2NvbG9yLXN0cmluZy5qcyIsIm5vZGVfbW9kdWxlcy9jaGFydGpzLWNvbG9yL25vZGVfbW9kdWxlcy9jaGFydGpzLWNvbG9yLXN0cmluZy9ub2RlX21vZHVsZXMvY29sb3ItbmFtZS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9jaGFydGpzLWNvbG9yL25vZGVfbW9kdWxlcy9jb2xvci1jb252ZXJ0L2NvbnZlcnNpb25zLmpzIiwibm9kZV9tb2R1bGVzL2NoYXJ0anMtY29sb3Ivbm9kZV9tb2R1bGVzL2NvbG9yLWNvbnZlcnQvaW5kZXguanMiLCJub2RlX21vZHVsZXMvbW9tZW50L21vbWVudC5qcyIsInNyYy9jaGFydHMvQ2hhcnQuQmFyLmpzIiwic3JjL2NoYXJ0cy9DaGFydC5CdWJibGUuanMiLCJzcmMvY2hhcnRzL0NoYXJ0LkRvdWdobnV0LmpzIiwic3JjL2NoYXJ0cy9DaGFydC5MaW5lLmpzIiwic3JjL2NoYXJ0cy9DaGFydC5Qb2xhckFyZWEuanMiLCJzcmMvY2hhcnRzL0NoYXJ0LlJhZGFyLmpzIiwic3JjL2NoYXJ0cy9DaGFydC5TY2F0dGVyLmpzIiwic3JjL2NvbnRyb2xsZXJzL2NvbnRyb2xsZXIuYmFyLmpzIiwic3JjL2NvbnRyb2xsZXJzL2NvbnRyb2xsZXIuYnViYmxlLmpzIiwic3JjL2NvbnRyb2xsZXJzL2NvbnRyb2xsZXIuZG91Z2hudXQuanMiLCJzcmMvY29udHJvbGxlcnMvY29udHJvbGxlci5saW5lLmpzIiwic3JjL2NvbnRyb2xsZXJzL2NvbnRyb2xsZXIucG9sYXJBcmVhLmpzIiwic3JjL2NvbnRyb2xsZXJzL2NvbnRyb2xsZXIucmFkYXIuanMiLCJzcmMvY29yZS9jb3JlLmFuaW1hdGlvbi5qcyIsInNyYy9jb3JlL2NvcmUuY2FudmFzSGVscGVycy5qcyIsInNyYy9jb3JlL2NvcmUuY29udHJvbGxlci5qcyIsInNyYy9jb3JlL2NvcmUuZGF0YXNldENvbnRyb2xsZXIuanMiLCJzcmMvY29yZS9jb3JlLmVsZW1lbnQuanMiLCJzcmMvY29yZS9jb3JlLmhlbHBlcnMuanMiLCJzcmMvY29yZS9jb3JlLmpzIiwic3JjL2NvcmUvY29yZS5sYXlvdXRTZXJ2aWNlLmpzIiwic3JjL2NvcmUvY29yZS5sZWdlbmQuanMiLCJzcmMvY29yZS9jb3JlLnBsdWdpbi5qcyIsInNyYy9jb3JlL2NvcmUuc2NhbGUuanMiLCJzcmMvY29yZS9jb3JlLnNjYWxlU2VydmljZS5qcyIsInNyYy9jb3JlL2NvcmUudGl0bGUuanMiLCJzcmMvY29yZS9jb3JlLnRvb2x0aXAuanMiLCJzcmMvZWxlbWVudHMvZWxlbWVudC5hcmMuanMiLCJzcmMvZWxlbWVudHMvZWxlbWVudC5saW5lLmpzIiwic3JjL2VsZW1lbnRzL2VsZW1lbnQucG9pbnQuanMiLCJzcmMvZWxlbWVudHMvZWxlbWVudC5yZWN0YW5nbGUuanMiLCJzcmMvc2NhbGVzL3NjYWxlLmNhdGVnb3J5LmpzIiwic3JjL3NjYWxlcy9zY2FsZS5saW5lYXIuanMiLCJzcmMvc2NhbGVzL3NjYWxlLmxpbmVhcmJhc2UuanMiLCJzcmMvc2NhbGVzL3NjYWxlLmxvZ2FyaXRobWljLmpzIiwic3JjL3NjYWxlcy9zY2FsZS5yYWRpYWxMaW5lYXIuanMiLCJzcmMvc2NhbGVzL3NjYWxlLnRpbWUuanMiLCJzcmMvY2hhcnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxckJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xtSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNUQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3akJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5UkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pVQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzcUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbjdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pVQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqZEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaklBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3dUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMXNCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdE1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDamFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdllBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyogTUlUIGxpY2Vuc2UgKi9cclxudmFyIGNvbnZlcnQgPSByZXF1aXJlKCdjb2xvci1jb252ZXJ0Jyk7XHJcbnZhciBzdHJpbmcgPSByZXF1aXJlKCdjaGFydGpzLWNvbG9yLXN0cmluZycpO1xyXG5cclxudmFyIENvbG9yID0gZnVuY3Rpb24gKG9iaikge1xyXG5cdGlmIChvYmogaW5zdGFuY2VvZiBDb2xvcikge1xyXG5cdFx0cmV0dXJuIG9iajtcclxuXHR9XHJcblx0aWYgKCEodGhpcyBpbnN0YW5jZW9mIENvbG9yKSkge1xyXG5cdFx0cmV0dXJuIG5ldyBDb2xvcihvYmopO1xyXG5cdH1cclxuXHJcblx0dGhpcy52YWx1ZXMgPSB7XHJcblx0XHRyZ2I6IFswLCAwLCAwXSxcclxuXHRcdGhzbDogWzAsIDAsIDBdLFxyXG5cdFx0aHN2OiBbMCwgMCwgMF0sXHJcblx0XHRod2I6IFswLCAwLCAwXSxcclxuXHRcdGNteWs6IFswLCAwLCAwLCAwXSxcclxuXHRcdGFscGhhOiAxXHJcblx0fTtcclxuXHJcblx0Ly8gcGFyc2UgQ29sb3IoKSBhcmd1bWVudFxyXG5cdHZhciB2YWxzO1xyXG5cdGlmICh0eXBlb2Ygb2JqID09PSAnc3RyaW5nJykge1xyXG5cdFx0dmFscyA9IHN0cmluZy5nZXRSZ2JhKG9iaik7XHJcblx0XHRpZiAodmFscykge1xyXG5cdFx0XHR0aGlzLnNldFZhbHVlcygncmdiJywgdmFscyk7XHJcblx0XHR9IGVsc2UgaWYgKHZhbHMgPSBzdHJpbmcuZ2V0SHNsYShvYmopKSB7XHJcblx0XHRcdHRoaXMuc2V0VmFsdWVzKCdoc2wnLCB2YWxzKTtcclxuXHRcdH0gZWxzZSBpZiAodmFscyA9IHN0cmluZy5nZXRId2Iob2JqKSkge1xyXG5cdFx0XHR0aGlzLnNldFZhbHVlcygnaHdiJywgdmFscyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1VuYWJsZSB0byBwYXJzZSBjb2xvciBmcm9tIHN0cmluZyBcIicgKyBvYmogKyAnXCInKTtcclxuXHRcdH1cclxuXHR9IGVsc2UgaWYgKHR5cGVvZiBvYmogPT09ICdvYmplY3QnKSB7XHJcblx0XHR2YWxzID0gb2JqO1xyXG5cdFx0aWYgKHZhbHMuciAhPT0gdW5kZWZpbmVkIHx8IHZhbHMucmVkICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dGhpcy5zZXRWYWx1ZXMoJ3JnYicsIHZhbHMpO1xyXG5cdFx0fSBlbHNlIGlmICh2YWxzLmwgIT09IHVuZGVmaW5lZCB8fCB2YWxzLmxpZ2h0bmVzcyAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuc2V0VmFsdWVzKCdoc2wnLCB2YWxzKTtcclxuXHRcdH0gZWxzZSBpZiAodmFscy52ICE9PSB1bmRlZmluZWQgfHwgdmFscy52YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuc2V0VmFsdWVzKCdoc3YnLCB2YWxzKTtcclxuXHRcdH0gZWxzZSBpZiAodmFscy53ICE9PSB1bmRlZmluZWQgfHwgdmFscy53aGl0ZW5lc3MgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLnNldFZhbHVlcygnaHdiJywgdmFscyk7XHJcblx0XHR9IGVsc2UgaWYgKHZhbHMuYyAhPT0gdW5kZWZpbmVkIHx8IHZhbHMuY3lhbiAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuc2V0VmFsdWVzKCdjbXlrJywgdmFscyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1VuYWJsZSB0byBwYXJzZSBjb2xvciBmcm9tIG9iamVjdCAnICsgSlNPTi5zdHJpbmdpZnkob2JqKSk7XHJcblx0XHR9XHJcblx0fVxyXG59O1xyXG5cclxuQ29sb3IucHJvdG90eXBlID0ge1xyXG5cdHJnYjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0U3BhY2UoJ3JnYicsIGFyZ3VtZW50cyk7XHJcblx0fSxcclxuXHRoc2w6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLnNldFNwYWNlKCdoc2wnLCBhcmd1bWVudHMpO1xyXG5cdH0sXHJcblx0aHN2OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRTcGFjZSgnaHN2JywgYXJndW1lbnRzKTtcclxuXHR9LFxyXG5cdGh3YjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0U3BhY2UoJ2h3YicsIGFyZ3VtZW50cyk7XHJcblx0fSxcclxuXHRjbXlrOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRTcGFjZSgnY215aycsIGFyZ3VtZW50cyk7XHJcblx0fSxcclxuXHJcblx0cmdiQXJyYXk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLnZhbHVlcy5yZ2I7XHJcblx0fSxcclxuXHRoc2xBcnJheTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMudmFsdWVzLmhzbDtcclxuXHR9LFxyXG5cdGhzdkFycmF5OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy52YWx1ZXMuaHN2O1xyXG5cdH0sXHJcblx0aHdiQXJyYXk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciB2YWx1ZXMgPSB0aGlzLnZhbHVlcztcclxuXHRcdGlmICh2YWx1ZXMuYWxwaGEgIT09IDEpIHtcclxuXHRcdFx0cmV0dXJuIHZhbHVlcy5od2IuY29uY2F0KFt2YWx1ZXMuYWxwaGFdKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB2YWx1ZXMuaHdiO1xyXG5cdH0sXHJcblx0Y215a0FycmF5OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy52YWx1ZXMuY215aztcclxuXHR9LFxyXG5cdHJnYmFBcnJheTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHZhbHVlcyA9IHRoaXMudmFsdWVzO1xyXG5cdFx0cmV0dXJuIHZhbHVlcy5yZ2IuY29uY2F0KFt2YWx1ZXMuYWxwaGFdKTtcclxuXHR9LFxyXG5cdGhzbGFBcnJheTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHZhbHVlcyA9IHRoaXMudmFsdWVzO1xyXG5cdFx0cmV0dXJuIHZhbHVlcy5oc2wuY29uY2F0KFt2YWx1ZXMuYWxwaGFdKTtcclxuXHR9LFxyXG5cdGFscGhhOiBmdW5jdGlvbiAodmFsKSB7XHJcblx0XHRpZiAodmFsID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMudmFsdWVzLmFscGhhO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2FscGhhJywgdmFsKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdHJlZDogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgncmdiJywgMCwgdmFsKTtcclxuXHR9LFxyXG5cdGdyZWVuOiBmdW5jdGlvbiAodmFsKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRDaGFubmVsKCdyZ2InLCAxLCB2YWwpO1xyXG5cdH0sXHJcblx0Ymx1ZTogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgncmdiJywgMiwgdmFsKTtcclxuXHR9LFxyXG5cdGh1ZTogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0aWYgKHZhbCkge1xyXG5cdFx0XHR2YWwgJT0gMzYwO1xyXG5cdFx0XHR2YWwgPSB2YWwgPCAwID8gMzYwICsgdmFsIDogdmFsO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnaHNsJywgMCwgdmFsKTtcclxuXHR9LFxyXG5cdHNhdHVyYXRpb246IGZ1bmN0aW9uICh2YWwpIHtcclxuXHRcdHJldHVybiB0aGlzLnNldENoYW5uZWwoJ2hzbCcsIDEsIHZhbCk7XHJcblx0fSxcclxuXHRsaWdodG5lc3M6IGZ1bmN0aW9uICh2YWwpIHtcclxuXHRcdHJldHVybiB0aGlzLnNldENoYW5uZWwoJ2hzbCcsIDIsIHZhbCk7XHJcblx0fSxcclxuXHRzYXR1cmF0aW9udjogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnaHN2JywgMSwgdmFsKTtcclxuXHR9LFxyXG5cdHdoaXRlbmVzczogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnaHdiJywgMSwgdmFsKTtcclxuXHR9LFxyXG5cdGJsYWNrbmVzczogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnaHdiJywgMiwgdmFsKTtcclxuXHR9LFxyXG5cdHZhbHVlOiBmdW5jdGlvbiAodmFsKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRDaGFubmVsKCdoc3YnLCAyLCB2YWwpO1xyXG5cdH0sXHJcblx0Y3lhbjogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnY215aycsIDAsIHZhbCk7XHJcblx0fSxcclxuXHRtYWdlbnRhOiBmdW5jdGlvbiAodmFsKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRDaGFubmVsKCdjbXlrJywgMSwgdmFsKTtcclxuXHR9LFxyXG5cdHllbGxvdzogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnY215aycsIDIsIHZhbCk7XHJcblx0fSxcclxuXHRibGFjazogZnVuY3Rpb24gKHZhbCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Q2hhbm5lbCgnY215aycsIDMsIHZhbCk7XHJcblx0fSxcclxuXHJcblx0aGV4U3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gc3RyaW5nLmhleFN0cmluZyh0aGlzLnZhbHVlcy5yZ2IpO1xyXG5cdH0sXHJcblx0cmdiU3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gc3RyaW5nLnJnYlN0cmluZyh0aGlzLnZhbHVlcy5yZ2IsIHRoaXMudmFsdWVzLmFscGhhKTtcclxuXHR9LFxyXG5cdHJnYmFTdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBzdHJpbmcucmdiYVN0cmluZyh0aGlzLnZhbHVlcy5yZ2IsIHRoaXMudmFsdWVzLmFscGhhKTtcclxuXHR9LFxyXG5cdHBlcmNlbnRTdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBzdHJpbmcucGVyY2VudFN0cmluZyh0aGlzLnZhbHVlcy5yZ2IsIHRoaXMudmFsdWVzLmFscGhhKTtcclxuXHR9LFxyXG5cdGhzbFN0cmluZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHN0cmluZy5oc2xTdHJpbmcodGhpcy52YWx1ZXMuaHNsLCB0aGlzLnZhbHVlcy5hbHBoYSk7XHJcblx0fSxcclxuXHRoc2xhU3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gc3RyaW5nLmhzbGFTdHJpbmcodGhpcy52YWx1ZXMuaHNsLCB0aGlzLnZhbHVlcy5hbHBoYSk7XHJcblx0fSxcclxuXHRod2JTdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBzdHJpbmcuaHdiU3RyaW5nKHRoaXMudmFsdWVzLmh3YiwgdGhpcy52YWx1ZXMuYWxwaGEpO1xyXG5cdH0sXHJcblx0a2V5d29yZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHN0cmluZy5rZXl3b3JkKHRoaXMudmFsdWVzLnJnYiwgdGhpcy52YWx1ZXMuYWxwaGEpO1xyXG5cdH0sXHJcblxyXG5cdHJnYk51bWJlcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHJnYiA9IHRoaXMudmFsdWVzLnJnYjtcclxuXHRcdHJldHVybiAocmdiWzBdIDw8IDE2KSB8IChyZ2JbMV0gPDwgOCkgfCByZ2JbMl07XHJcblx0fSxcclxuXHJcblx0bHVtaW5vc2l0eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gaHR0cDovL3d3dy53My5vcmcvVFIvV0NBRzIwLyNyZWxhdGl2ZWx1bWluYW5jZWRlZlxyXG5cdFx0dmFyIHJnYiA9IHRoaXMudmFsdWVzLnJnYjtcclxuXHRcdHZhciBsdW0gPSBbXTtcclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgcmdiLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHZhciBjaGFuID0gcmdiW2ldIC8gMjU1O1xyXG5cdFx0XHRsdW1baV0gPSAoY2hhbiA8PSAwLjAzOTI4KSA/IGNoYW4gLyAxMi45MiA6IE1hdGgucG93KCgoY2hhbiArIDAuMDU1KSAvIDEuMDU1KSwgMi40KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiAwLjIxMjYgKiBsdW1bMF0gKyAwLjcxNTIgKiBsdW1bMV0gKyAwLjA3MjIgKiBsdW1bMl07XHJcblx0fSxcclxuXHJcblx0Y29udHJhc3Q6IGZ1bmN0aW9uIChjb2xvcjIpIHtcclxuXHRcdC8vIGh0dHA6Ly93d3cudzMub3JnL1RSL1dDQUcyMC8jY29udHJhc3QtcmF0aW9kZWZcclxuXHRcdHZhciBsdW0xID0gdGhpcy5sdW1pbm9zaXR5KCk7XHJcblx0XHR2YXIgbHVtMiA9IGNvbG9yMi5sdW1pbm9zaXR5KCk7XHJcblx0XHRpZiAobHVtMSA+IGx1bTIpIHtcclxuXHRcdFx0cmV0dXJuIChsdW0xICsgMC4wNSkgLyAobHVtMiArIDAuMDUpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIChsdW0yICsgMC4wNSkgLyAobHVtMSArIDAuMDUpO1xyXG5cdH0sXHJcblxyXG5cdGxldmVsOiBmdW5jdGlvbiAoY29sb3IyKSB7XHJcblx0XHR2YXIgY29udHJhc3RSYXRpbyA9IHRoaXMuY29udHJhc3QoY29sb3IyKTtcclxuXHRcdGlmIChjb250cmFzdFJhdGlvID49IDcuMSkge1xyXG5cdFx0XHRyZXR1cm4gJ0FBQSc7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIChjb250cmFzdFJhdGlvID49IDQuNSkgPyAnQUEnIDogJyc7XHJcblx0fSxcclxuXHJcblx0ZGFyazogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gWUlRIGVxdWF0aW9uIGZyb20gaHR0cDovLzI0d2F5cy5vcmcvMjAxMC9jYWxjdWxhdGluZy1jb2xvci1jb250cmFzdFxyXG5cdFx0dmFyIHJnYiA9IHRoaXMudmFsdWVzLnJnYjtcclxuXHRcdHZhciB5aXEgPSAocmdiWzBdICogMjk5ICsgcmdiWzFdICogNTg3ICsgcmdiWzJdICogMTE0KSAvIDEwMDA7XHJcblx0XHRyZXR1cm4geWlxIDwgMTI4O1xyXG5cdH0sXHJcblxyXG5cdGxpZ2h0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gIXRoaXMuZGFyaygpO1xyXG5cdH0sXHJcblxyXG5cdG5lZ2F0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHJnYiA9IFtdO1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCAzOyBpKyspIHtcclxuXHRcdFx0cmdiW2ldID0gMjU1IC0gdGhpcy52YWx1ZXMucmdiW2ldO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ3JnYicsIHJnYik7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRsaWdodGVuOiBmdW5jdGlvbiAocmF0aW8pIHtcclxuXHRcdHZhciBoc2wgPSB0aGlzLnZhbHVlcy5oc2w7XHJcblx0XHRoc2xbMl0gKz0gaHNsWzJdICogcmF0aW87XHJcblx0XHR0aGlzLnNldFZhbHVlcygnaHNsJywgaHNsKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGRhcmtlbjogZnVuY3Rpb24gKHJhdGlvKSB7XHJcblx0XHR2YXIgaHNsID0gdGhpcy52YWx1ZXMuaHNsO1xyXG5cdFx0aHNsWzJdIC09IGhzbFsyXSAqIHJhdGlvO1xyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2hzbCcsIGhzbCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRzYXR1cmF0ZTogZnVuY3Rpb24gKHJhdGlvKSB7XHJcblx0XHR2YXIgaHNsID0gdGhpcy52YWx1ZXMuaHNsO1xyXG5cdFx0aHNsWzFdICs9IGhzbFsxXSAqIHJhdGlvO1xyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2hzbCcsIGhzbCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRkZXNhdHVyYXRlOiBmdW5jdGlvbiAocmF0aW8pIHtcclxuXHRcdHZhciBoc2wgPSB0aGlzLnZhbHVlcy5oc2w7XHJcblx0XHRoc2xbMV0gLT0gaHNsWzFdICogcmF0aW87XHJcblx0XHR0aGlzLnNldFZhbHVlcygnaHNsJywgaHNsKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdHdoaXRlbjogZnVuY3Rpb24gKHJhdGlvKSB7XHJcblx0XHR2YXIgaHdiID0gdGhpcy52YWx1ZXMuaHdiO1xyXG5cdFx0aHdiWzFdICs9IGh3YlsxXSAqIHJhdGlvO1xyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2h3YicsIGh3Yik7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRibGFja2VuOiBmdW5jdGlvbiAocmF0aW8pIHtcclxuXHRcdHZhciBod2IgPSB0aGlzLnZhbHVlcy5od2I7XHJcblx0XHRod2JbMl0gKz0gaHdiWzJdICogcmF0aW87XHJcblx0XHR0aGlzLnNldFZhbHVlcygnaHdiJywgaHdiKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdyZXlzY2FsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHJnYiA9IHRoaXMudmFsdWVzLnJnYjtcclxuXHRcdC8vIGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR3JheXNjYWxlI0NvbnZlcnRpbmdfY29sb3JfdG9fZ3JheXNjYWxlXHJcblx0XHR2YXIgdmFsID0gcmdiWzBdICogMC4zICsgcmdiWzFdICogMC41OSArIHJnYlsyXSAqIDAuMTE7XHJcblx0XHR0aGlzLnNldFZhbHVlcygncmdiJywgW3ZhbCwgdmFsLCB2YWxdKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGNsZWFyZXI6IGZ1bmN0aW9uIChyYXRpbykge1xyXG5cdFx0dmFyIGFscGhhID0gdGhpcy52YWx1ZXMuYWxwaGE7XHJcblx0XHR0aGlzLnNldFZhbHVlcygnYWxwaGEnLCBhbHBoYSAtIChhbHBoYSAqIHJhdGlvKSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRvcGFxdWVyOiBmdW5jdGlvbiAocmF0aW8pIHtcclxuXHRcdHZhciBhbHBoYSA9IHRoaXMudmFsdWVzLmFscGhhO1xyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2FscGhhJywgYWxwaGEgKyAoYWxwaGEgKiByYXRpbykpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0cm90YXRlOiBmdW5jdGlvbiAoZGVncmVlcykge1xyXG5cdFx0dmFyIGhzbCA9IHRoaXMudmFsdWVzLmhzbDtcclxuXHRcdHZhciBodWUgPSAoaHNsWzBdICsgZGVncmVlcykgJSAzNjA7XHJcblx0XHRoc2xbMF0gPSBodWUgPCAwID8gMzYwICsgaHVlIDogaHVlO1xyXG5cdFx0dGhpcy5zZXRWYWx1ZXMoJ2hzbCcsIGhzbCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvKipcclxuXHQgKiBQb3J0ZWQgZnJvbSBzYXNzIGltcGxlbWVudGF0aW9uIGluIENcclxuXHQgKiBodHRwczovL2dpdGh1Yi5jb20vc2Fzcy9saWJzYXNzL2Jsb2IvMGU2YjRhMjg1MDA5MjM1NmFhM2VjZTA3YzZiMjQ5ZjAyMjFjYWNlZC9mdW5jdGlvbnMuY3BwI0wyMDlcclxuXHQgKi9cclxuXHRtaXg6IGZ1bmN0aW9uIChtaXhpbkNvbG9yLCB3ZWlnaHQpIHtcclxuXHRcdHZhciBjb2xvcjEgPSB0aGlzO1xyXG5cdFx0dmFyIGNvbG9yMiA9IG1peGluQ29sb3I7XHJcblx0XHR2YXIgcCA9IHdlaWdodCA9PT0gdW5kZWZpbmVkID8gMC41IDogd2VpZ2h0O1xyXG5cclxuXHRcdHZhciB3ID0gMiAqIHAgLSAxO1xyXG5cdFx0dmFyIGEgPSBjb2xvcjEuYWxwaGEoKSAtIGNvbG9yMi5hbHBoYSgpO1xyXG5cclxuXHRcdHZhciB3MSA9ICgoKHcgKiBhID09PSAtMSkgPyB3IDogKHcgKyBhKSAvICgxICsgdyAqIGEpKSArIDEpIC8gMi4wO1xyXG5cdFx0dmFyIHcyID0gMSAtIHcxO1xyXG5cclxuXHRcdHJldHVybiB0aGlzXHJcblx0XHRcdC5yZ2IoXHJcblx0XHRcdFx0dzEgKiBjb2xvcjEucmVkKCkgKyB3MiAqIGNvbG9yMi5yZWQoKSxcclxuXHRcdFx0XHR3MSAqIGNvbG9yMS5ncmVlbigpICsgdzIgKiBjb2xvcjIuZ3JlZW4oKSxcclxuXHRcdFx0XHR3MSAqIGNvbG9yMS5ibHVlKCkgKyB3MiAqIGNvbG9yMi5ibHVlKClcclxuXHRcdFx0KVxyXG5cdFx0XHQuYWxwaGEoY29sb3IxLmFscGhhKCkgKiBwICsgY29sb3IyLmFscGhhKCkgKiAoMSAtIHApKTtcclxuXHR9LFxyXG5cclxuXHR0b0pTT046IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLnJnYigpO1xyXG5cdH0sXHJcblxyXG5cdGNsb25lOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBOT1RFKFNCKTogdXNpbmcgbm9kZS1jbG9uZSBjcmVhdGVzIGEgZGVwZW5kZW5jeSB0byBCdWZmZXIgd2hlbiB1c2luZyBicm93c2VyaWZ5LFxyXG5cdFx0Ly8gbWFraW5nIHRoZSBmaW5hbCBidWlsZCB3YXkgdG8gYmlnIHRvIGVtYmVkIGluIENoYXJ0LmpzLiBTbyBsZXQncyBkbyBpdCBtYW51YWxseSxcclxuXHRcdC8vIGFzc3VtaW5nIHRoYXQgdmFsdWVzIHRvIGNsb25lIGFyZSAxIGRpbWVuc2lvbiBhcnJheXMgY29udGFpbmluZyBvbmx5IG51bWJlcnMsXHJcblx0XHQvLyBleGNlcHQgJ2FscGhhJyB3aGljaCBpcyBhIG51bWJlci5cclxuXHRcdHZhciByZXN1bHQgPSBuZXcgQ29sb3IoKTtcclxuXHRcdHZhciBzb3VyY2UgPSB0aGlzLnZhbHVlcztcclxuXHRcdHZhciB0YXJnZXQgPSByZXN1bHQudmFsdWVzO1xyXG5cdFx0dmFyIHZhbHVlLCB0eXBlO1xyXG5cclxuXHRcdGZvciAodmFyIHByb3AgaW4gc291cmNlKSB7XHJcblx0XHRcdGlmIChzb3VyY2UuaGFzT3duUHJvcGVydHkocHJvcCkpIHtcclxuXHRcdFx0XHR2YWx1ZSA9IHNvdXJjZVtwcm9wXTtcclxuXHRcdFx0XHR0eXBlID0gKHt9KS50b1N0cmluZy5jYWxsKHZhbHVlKTtcclxuXHRcdFx0XHRpZiAodHlwZSA9PT0gJ1tvYmplY3QgQXJyYXldJykge1xyXG5cdFx0XHRcdFx0dGFyZ2V0W3Byb3BdID0gdmFsdWUuc2xpY2UoMCk7XHJcblx0XHRcdFx0fSBlbHNlIGlmICh0eXBlID09PSAnW29iamVjdCBOdW1iZXJdJykge1xyXG5cdFx0XHRcdFx0dGFyZ2V0W3Byb3BdID0gdmFsdWU7XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoJ3VuZXhwZWN0ZWQgY29sb3IgdmFsdWU6JywgdmFsdWUpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiByZXN1bHQ7XHJcblx0fVxyXG59O1xyXG5cclxuQ29sb3IucHJvdG90eXBlLnNwYWNlcyA9IHtcclxuXHRyZ2I6IFsncmVkJywgJ2dyZWVuJywgJ2JsdWUnXSxcclxuXHRoc2w6IFsnaHVlJywgJ3NhdHVyYXRpb24nLCAnbGlnaHRuZXNzJ10sXHJcblx0aHN2OiBbJ2h1ZScsICdzYXR1cmF0aW9uJywgJ3ZhbHVlJ10sXHJcblx0aHdiOiBbJ2h1ZScsICd3aGl0ZW5lc3MnLCAnYmxhY2tuZXNzJ10sXHJcblx0Y215azogWydjeWFuJywgJ21hZ2VudGEnLCAneWVsbG93JywgJ2JsYWNrJ11cclxufTtcclxuXHJcbkNvbG9yLnByb3RvdHlwZS5tYXhlcyA9IHtcclxuXHRyZ2I6IFsyNTUsIDI1NSwgMjU1XSxcclxuXHRoc2w6IFszNjAsIDEwMCwgMTAwXSxcclxuXHRoc3Y6IFszNjAsIDEwMCwgMTAwXSxcclxuXHRod2I6IFszNjAsIDEwMCwgMTAwXSxcclxuXHRjbXlrOiBbMTAwLCAxMDAsIDEwMCwgMTAwXVxyXG59O1xyXG5cclxuQ29sb3IucHJvdG90eXBlLmdldFZhbHVlcyA9IGZ1bmN0aW9uIChzcGFjZSkge1xyXG5cdHZhciB2YWx1ZXMgPSB0aGlzLnZhbHVlcztcclxuXHR2YXIgdmFscyA9IHt9O1xyXG5cclxuXHRmb3IgKHZhciBpID0gMDsgaSA8IHNwYWNlLmxlbmd0aDsgaSsrKSB7XHJcblx0XHR2YWxzW3NwYWNlLmNoYXJBdChpKV0gPSB2YWx1ZXNbc3BhY2VdW2ldO1xyXG5cdH1cclxuXHJcblx0aWYgKHZhbHVlcy5hbHBoYSAhPT0gMSkge1xyXG5cdFx0dmFscy5hID0gdmFsdWVzLmFscGhhO1xyXG5cdH1cclxuXHJcblx0Ly8ge3I6IDI1NSwgZzogMjU1LCBiOiAyNTUsIGE6IDAuNH1cclxuXHRyZXR1cm4gdmFscztcclxufTtcclxuXHJcbkNvbG9yLnByb3RvdHlwZS5zZXRWYWx1ZXMgPSBmdW5jdGlvbiAoc3BhY2UsIHZhbHMpIHtcclxuXHR2YXIgdmFsdWVzID0gdGhpcy52YWx1ZXM7XHJcblx0dmFyIHNwYWNlcyA9IHRoaXMuc3BhY2VzO1xyXG5cdHZhciBtYXhlcyA9IHRoaXMubWF4ZXM7XHJcblx0dmFyIGFscGhhID0gMTtcclxuXHR2YXIgaTtcclxuXHJcblx0aWYgKHNwYWNlID09PSAnYWxwaGEnKSB7XHJcblx0XHRhbHBoYSA9IHZhbHM7XHJcblx0fSBlbHNlIGlmICh2YWxzLmxlbmd0aCkge1xyXG5cdFx0Ly8gWzEwLCAxMCwgMTBdXHJcblx0XHR2YWx1ZXNbc3BhY2VdID0gdmFscy5zbGljZSgwLCBzcGFjZS5sZW5ndGgpO1xyXG5cdFx0YWxwaGEgPSB2YWxzW3NwYWNlLmxlbmd0aF07XHJcblx0fSBlbHNlIGlmICh2YWxzW3NwYWNlLmNoYXJBdCgwKV0gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0Ly8ge3I6IDEwLCBnOiAxMCwgYjogMTB9XHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgc3BhY2UubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dmFsdWVzW3NwYWNlXVtpXSA9IHZhbHNbc3BhY2UuY2hhckF0KGkpXTtcclxuXHRcdH1cclxuXHJcblx0XHRhbHBoYSA9IHZhbHMuYTtcclxuXHR9IGVsc2UgaWYgKHZhbHNbc3BhY2VzW3NwYWNlXVswXV0gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0Ly8ge3JlZDogMTAsIGdyZWVuOiAxMCwgYmx1ZTogMTB9XHJcblx0XHR2YXIgY2hhbnMgPSBzcGFjZXNbc3BhY2VdO1xyXG5cclxuXHRcdGZvciAoaSA9IDA7IGkgPCBzcGFjZS5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHR2YWx1ZXNbc3BhY2VdW2ldID0gdmFsc1tjaGFuc1tpXV07XHJcblx0XHR9XHJcblxyXG5cdFx0YWxwaGEgPSB2YWxzLmFscGhhO1xyXG5cdH1cclxuXHJcblx0dmFsdWVzLmFscGhhID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgKGFscGhhID09PSB1bmRlZmluZWQgPyB2YWx1ZXMuYWxwaGEgOiBhbHBoYSkpKTtcclxuXHJcblx0aWYgKHNwYWNlID09PSAnYWxwaGEnKSB7XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fVxyXG5cclxuXHR2YXIgY2FwcGVkO1xyXG5cclxuXHQvLyBjYXAgdmFsdWVzIG9mIHRoZSBzcGFjZSBwcmlvciBjb252ZXJ0aW5nIGFsbCB2YWx1ZXNcclxuXHRmb3IgKGkgPSAwOyBpIDwgc3BhY2UubGVuZ3RoOyBpKyspIHtcclxuXHRcdGNhcHBlZCA9IE1hdGgubWF4KDAsIE1hdGgubWluKG1heGVzW3NwYWNlXVtpXSwgdmFsdWVzW3NwYWNlXVtpXSkpO1xyXG5cdFx0dmFsdWVzW3NwYWNlXVtpXSA9IE1hdGgucm91bmQoY2FwcGVkKTtcclxuXHR9XHJcblxyXG5cdC8vIGNvbnZlcnQgdG8gYWxsIHRoZSBvdGhlciBjb2xvciBzcGFjZXNcclxuXHRmb3IgKHZhciBzbmFtZSBpbiBzcGFjZXMpIHtcclxuXHRcdGlmIChzbmFtZSAhPT0gc3BhY2UpIHtcclxuXHRcdFx0dmFsdWVzW3NuYW1lXSA9IGNvbnZlcnRbc3BhY2VdW3NuYW1lXSh2YWx1ZXNbc3BhY2VdKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdHJldHVybiB0cnVlO1xyXG59O1xyXG5cclxuQ29sb3IucHJvdG90eXBlLnNldFNwYWNlID0gZnVuY3Rpb24gKHNwYWNlLCBhcmdzKSB7XHJcblx0dmFyIHZhbHMgPSBhcmdzWzBdO1xyXG5cclxuXHRpZiAodmFscyA9PT0gdW5kZWZpbmVkKSB7XHJcblx0XHQvLyBjb2xvci5yZ2IoKVxyXG5cdFx0cmV0dXJuIHRoaXMuZ2V0VmFsdWVzKHNwYWNlKTtcclxuXHR9XHJcblxyXG5cdC8vIGNvbG9yLnJnYigxMCwgMTAsIDEwKVxyXG5cdGlmICh0eXBlb2YgdmFscyA9PT0gJ251bWJlcicpIHtcclxuXHRcdHZhbHMgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmdzKTtcclxuXHR9XHJcblxyXG5cdHRoaXMuc2V0VmFsdWVzKHNwYWNlLCB2YWxzKTtcclxuXHRyZXR1cm4gdGhpcztcclxufTtcclxuXHJcbkNvbG9yLnByb3RvdHlwZS5zZXRDaGFubmVsID0gZnVuY3Rpb24gKHNwYWNlLCBpbmRleCwgdmFsKSB7XHJcblx0dmFyIHN2YWx1ZXMgPSB0aGlzLnZhbHVlc1tzcGFjZV07XHJcblx0aWYgKHZhbCA9PT0gdW5kZWZpbmVkKSB7XHJcblx0XHQvLyBjb2xvci5yZWQoKVxyXG5cdFx0cmV0dXJuIHN2YWx1ZXNbaW5kZXhdO1xyXG5cdH0gZWxzZSBpZiAodmFsID09PSBzdmFsdWVzW2luZGV4XSkge1xyXG5cdFx0Ly8gY29sb3IucmVkKGNvbG9yLnJlZCgpKVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG5cclxuXHQvLyBjb2xvci5yZWQoMTAwKVxyXG5cdHN2YWx1ZXNbaW5kZXhdID0gdmFsO1xyXG5cdHRoaXMuc2V0VmFsdWVzKHNwYWNlLCBzdmFsdWVzKTtcclxuXHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxyXG5pZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuXHR3aW5kb3cuQ29sb3IgPSBDb2xvcjtcclxufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBDb2xvcjtcclxuIiwiLyogTUlUIGxpY2Vuc2UgKi9cclxudmFyIGNvbG9yTmFtZXMgPSByZXF1aXJlKCdjb2xvci1uYW1lJyk7XHJcblxyXG5tb2R1bGUuZXhwb3J0cyA9IHtcclxuICAgZ2V0UmdiYTogZ2V0UmdiYSxcclxuICAgZ2V0SHNsYTogZ2V0SHNsYSxcclxuICAgZ2V0UmdiOiBnZXRSZ2IsXHJcbiAgIGdldEhzbDogZ2V0SHNsLFxyXG4gICBnZXRId2I6IGdldEh3YixcclxuICAgZ2V0QWxwaGE6IGdldEFscGhhLFxyXG5cclxuICAgaGV4U3RyaW5nOiBoZXhTdHJpbmcsXHJcbiAgIHJnYlN0cmluZzogcmdiU3RyaW5nLFxyXG4gICByZ2JhU3RyaW5nOiByZ2JhU3RyaW5nLFxyXG4gICBwZXJjZW50U3RyaW5nOiBwZXJjZW50U3RyaW5nLFxyXG4gICBwZXJjZW50YVN0cmluZzogcGVyY2VudGFTdHJpbmcsXHJcbiAgIGhzbFN0cmluZzogaHNsU3RyaW5nLFxyXG4gICBoc2xhU3RyaW5nOiBoc2xhU3RyaW5nLFxyXG4gICBod2JTdHJpbmc6IGh3YlN0cmluZyxcclxuICAga2V5d29yZDoga2V5d29yZFxyXG59XHJcblxyXG5mdW5jdGlvbiBnZXRSZ2JhKHN0cmluZykge1xyXG4gICBpZiAoIXN0cmluZykge1xyXG4gICAgICByZXR1cm47XHJcbiAgIH1cclxuICAgdmFyIGFiYnIgPSAgL14jKFthLWZBLUYwLTldezN9KSQvLFxyXG4gICAgICAgaGV4ID0gIC9eIyhbYS1mQS1GMC05XXs2fSkkLyxcclxuICAgICAgIHJnYmEgPSAvXnJnYmE/XFwoXFxzKihbKy1dP1xcZCspXFxzKixcXHMqKFsrLV0/XFxkKylcXHMqLFxccyooWystXT9cXGQrKVxccyooPzosXFxzKihbKy1dP1tcXGRcXC5dKylcXHMqKT9cXCkkLyxcclxuICAgICAgIHBlciA9IC9ecmdiYT9cXChcXHMqKFsrLV0/W1xcZFxcLl0rKVxcJVxccyosXFxzKihbKy1dP1tcXGRcXC5dKylcXCVcXHMqLFxccyooWystXT9bXFxkXFwuXSspXFwlXFxzKig/OixcXHMqKFsrLV0/W1xcZFxcLl0rKVxccyopP1xcKSQvLFxyXG4gICAgICAga2V5d29yZCA9IC8oXFx3KykvO1xyXG5cclxuICAgdmFyIHJnYiA9IFswLCAwLCAwXSxcclxuICAgICAgIGEgPSAxLFxyXG4gICAgICAgbWF0Y2ggPSBzdHJpbmcubWF0Y2goYWJicik7XHJcbiAgIGlmIChtYXRjaCkge1xyXG4gICAgICBtYXRjaCA9IG1hdGNoWzFdO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJnYi5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICByZ2JbaV0gPSBwYXJzZUludChtYXRjaFtpXSArIG1hdGNoW2ldLCAxNik7XHJcbiAgICAgIH1cclxuICAgfVxyXG4gICBlbHNlIGlmIChtYXRjaCA9IHN0cmluZy5tYXRjaChoZXgpKSB7XHJcbiAgICAgIG1hdGNoID0gbWF0Y2hbMV07XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmdiLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgIHJnYltpXSA9IHBhcnNlSW50KG1hdGNoLnNsaWNlKGkgKiAyLCBpICogMiArIDIpLCAxNik7XHJcbiAgICAgIH1cclxuICAgfVxyXG4gICBlbHNlIGlmIChtYXRjaCA9IHN0cmluZy5tYXRjaChyZ2JhKSkge1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJnYi5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICByZ2JbaV0gPSBwYXJzZUludChtYXRjaFtpICsgMV0pO1xyXG4gICAgICB9XHJcbiAgICAgIGEgPSBwYXJzZUZsb2F0KG1hdGNoWzRdKTtcclxuICAgfVxyXG4gICBlbHNlIGlmIChtYXRjaCA9IHN0cmluZy5tYXRjaChwZXIpKSB7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmdiLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgIHJnYltpXSA9IE1hdGgucm91bmQocGFyc2VGbG9hdChtYXRjaFtpICsgMV0pICogMi41NSk7XHJcbiAgICAgIH1cclxuICAgICAgYSA9IHBhcnNlRmxvYXQobWF0Y2hbNF0pO1xyXG4gICB9XHJcbiAgIGVsc2UgaWYgKG1hdGNoID0gc3RyaW5nLm1hdGNoKGtleXdvcmQpKSB7XHJcbiAgICAgIGlmIChtYXRjaFsxXSA9PSBcInRyYW5zcGFyZW50XCIpIHtcclxuICAgICAgICAgcmV0dXJuIFswLCAwLCAwLCAwXTtcclxuICAgICAgfVxyXG4gICAgICByZ2IgPSBjb2xvck5hbWVzW21hdGNoWzFdXTtcclxuICAgICAgaWYgKCFyZ2IpIHtcclxuICAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcbiAgIH1cclxuXHJcbiAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmdiLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHJnYltpXSA9IHNjYWxlKHJnYltpXSwgMCwgMjU1KTtcclxuICAgfVxyXG4gICBpZiAoIWEgJiYgYSAhPSAwKSB7XHJcbiAgICAgIGEgPSAxO1xyXG4gICB9XHJcbiAgIGVsc2Uge1xyXG4gICAgICBhID0gc2NhbGUoYSwgMCwgMSk7XHJcbiAgIH1cclxuICAgcmdiWzNdID0gYTtcclxuICAgcmV0dXJuIHJnYjtcclxufVxyXG5cclxuZnVuY3Rpb24gZ2V0SHNsYShzdHJpbmcpIHtcclxuICAgaWYgKCFzdHJpbmcpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICB9XHJcbiAgIHZhciBoc2wgPSAvXmhzbGE/XFwoXFxzKihbKy1dP1xcZCspKD86ZGVnKT9cXHMqLFxccyooWystXT9bXFxkXFwuXSspJVxccyosXFxzKihbKy1dP1tcXGRcXC5dKyklXFxzKig/OixcXHMqKFsrLV0/W1xcZFxcLl0rKVxccyopP1xcKS87XHJcbiAgIHZhciBtYXRjaCA9IHN0cmluZy5tYXRjaChoc2wpO1xyXG4gICBpZiAobWF0Y2gpIHtcclxuICAgICAgdmFyIGFscGhhID0gcGFyc2VGbG9hdChtYXRjaFs0XSk7XHJcbiAgICAgIHZhciBoID0gc2NhbGUocGFyc2VJbnQobWF0Y2hbMV0pLCAwLCAzNjApLFxyXG4gICAgICAgICAgcyA9IHNjYWxlKHBhcnNlRmxvYXQobWF0Y2hbMl0pLCAwLCAxMDApLFxyXG4gICAgICAgICAgbCA9IHNjYWxlKHBhcnNlRmxvYXQobWF0Y2hbM10pLCAwLCAxMDApLFxyXG4gICAgICAgICAgYSA9IHNjYWxlKGlzTmFOKGFscGhhKSA/IDEgOiBhbHBoYSwgMCwgMSk7XHJcbiAgICAgIHJldHVybiBbaCwgcywgbCwgYV07XHJcbiAgIH1cclxufVxyXG5cclxuZnVuY3Rpb24gZ2V0SHdiKHN0cmluZykge1xyXG4gICBpZiAoIXN0cmluZykge1xyXG4gICAgICByZXR1cm47XHJcbiAgIH1cclxuICAgdmFyIGh3YiA9IC9eaHdiXFwoXFxzKihbKy1dP1xcZCspKD86ZGVnKT9cXHMqLFxccyooWystXT9bXFxkXFwuXSspJVxccyosXFxzKihbKy1dP1tcXGRcXC5dKyklXFxzKig/OixcXHMqKFsrLV0/W1xcZFxcLl0rKVxccyopP1xcKS87XHJcbiAgIHZhciBtYXRjaCA9IHN0cmluZy5tYXRjaChod2IpO1xyXG4gICBpZiAobWF0Y2gpIHtcclxuICAgIHZhciBhbHBoYSA9IHBhcnNlRmxvYXQobWF0Y2hbNF0pO1xyXG4gICAgICB2YXIgaCA9IHNjYWxlKHBhcnNlSW50KG1hdGNoWzFdKSwgMCwgMzYwKSxcclxuICAgICAgICAgIHcgPSBzY2FsZShwYXJzZUZsb2F0KG1hdGNoWzJdKSwgMCwgMTAwKSxcclxuICAgICAgICAgIGIgPSBzY2FsZShwYXJzZUZsb2F0KG1hdGNoWzNdKSwgMCwgMTAwKSxcclxuICAgICAgICAgIGEgPSBzY2FsZShpc05hTihhbHBoYSkgPyAxIDogYWxwaGEsIDAsIDEpO1xyXG4gICAgICByZXR1cm4gW2gsIHcsIGIsIGFdO1xyXG4gICB9XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGdldFJnYihzdHJpbmcpIHtcclxuICAgdmFyIHJnYmEgPSBnZXRSZ2JhKHN0cmluZyk7XHJcbiAgIHJldHVybiByZ2JhICYmIHJnYmEuc2xpY2UoMCwgMyk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGdldEhzbChzdHJpbmcpIHtcclxuICB2YXIgaHNsYSA9IGdldEhzbGEoc3RyaW5nKTtcclxuICByZXR1cm4gaHNsYSAmJiBoc2xhLnNsaWNlKDAsIDMpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBnZXRBbHBoYShzdHJpbmcpIHtcclxuICAgdmFyIHZhbHMgPSBnZXRSZ2JhKHN0cmluZyk7XHJcbiAgIGlmICh2YWxzKSB7XHJcbiAgICAgIHJldHVybiB2YWxzWzNdO1xyXG4gICB9XHJcbiAgIGVsc2UgaWYgKHZhbHMgPSBnZXRIc2xhKHN0cmluZykpIHtcclxuICAgICAgcmV0dXJuIHZhbHNbM107XHJcbiAgIH1cclxuICAgZWxzZSBpZiAodmFscyA9IGdldEh3YihzdHJpbmcpKSB7XHJcbiAgICAgIHJldHVybiB2YWxzWzNdO1xyXG4gICB9XHJcbn1cclxuXHJcbi8vIGdlbmVyYXRvcnNcclxuZnVuY3Rpb24gaGV4U3RyaW5nKHJnYikge1xyXG4gICByZXR1cm4gXCIjXCIgKyBoZXhEb3VibGUocmdiWzBdKSArIGhleERvdWJsZShyZ2JbMV0pXHJcbiAgICAgICAgICAgICAgKyBoZXhEb3VibGUocmdiWzJdKTtcclxufVxyXG5cclxuZnVuY3Rpb24gcmdiU3RyaW5nKHJnYmEsIGFscGhhKSB7XHJcbiAgIGlmIChhbHBoYSA8IDEgfHwgKHJnYmFbM10gJiYgcmdiYVszXSA8IDEpKSB7XHJcbiAgICAgIHJldHVybiByZ2JhU3RyaW5nKHJnYmEsIGFscGhhKTtcclxuICAgfVxyXG4gICByZXR1cm4gXCJyZ2IoXCIgKyByZ2JhWzBdICsgXCIsIFwiICsgcmdiYVsxXSArIFwiLCBcIiArIHJnYmFbMl0gKyBcIilcIjtcclxufVxyXG5cclxuZnVuY3Rpb24gcmdiYVN0cmluZyhyZ2JhLCBhbHBoYSkge1xyXG4gICBpZiAoYWxwaGEgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICBhbHBoYSA9IChyZ2JhWzNdICE9PSB1bmRlZmluZWQgPyByZ2JhWzNdIDogMSk7XHJcbiAgIH1cclxuICAgcmV0dXJuIFwicmdiYShcIiArIHJnYmFbMF0gKyBcIiwgXCIgKyByZ2JhWzFdICsgXCIsIFwiICsgcmdiYVsyXVxyXG4gICAgICAgICAgICsgXCIsIFwiICsgYWxwaGEgKyBcIilcIjtcclxufVxyXG5cclxuZnVuY3Rpb24gcGVyY2VudFN0cmluZyhyZ2JhLCBhbHBoYSkge1xyXG4gICBpZiAoYWxwaGEgPCAxIHx8IChyZ2JhWzNdICYmIHJnYmFbM10gPCAxKSkge1xyXG4gICAgICByZXR1cm4gcGVyY2VudGFTdHJpbmcocmdiYSwgYWxwaGEpO1xyXG4gICB9XHJcbiAgIHZhciByID0gTWF0aC5yb3VuZChyZ2JhWzBdLzI1NSAqIDEwMCksXHJcbiAgICAgICBnID0gTWF0aC5yb3VuZChyZ2JhWzFdLzI1NSAqIDEwMCksXHJcbiAgICAgICBiID0gTWF0aC5yb3VuZChyZ2JhWzJdLzI1NSAqIDEwMCk7XHJcblxyXG4gICByZXR1cm4gXCJyZ2IoXCIgKyByICsgXCIlLCBcIiArIGcgKyBcIiUsIFwiICsgYiArIFwiJSlcIjtcclxufVxyXG5cclxuZnVuY3Rpb24gcGVyY2VudGFTdHJpbmcocmdiYSwgYWxwaGEpIHtcclxuICAgdmFyIHIgPSBNYXRoLnJvdW5kKHJnYmFbMF0vMjU1ICogMTAwKSxcclxuICAgICAgIGcgPSBNYXRoLnJvdW5kKHJnYmFbMV0vMjU1ICogMTAwKSxcclxuICAgICAgIGIgPSBNYXRoLnJvdW5kKHJnYmFbMl0vMjU1ICogMTAwKTtcclxuICAgcmV0dXJuIFwicmdiYShcIiArIHIgKyBcIiUsIFwiICsgZyArIFwiJSwgXCIgKyBiICsgXCIlLCBcIiArIChhbHBoYSB8fCByZ2JhWzNdIHx8IDEpICsgXCIpXCI7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGhzbFN0cmluZyhoc2xhLCBhbHBoYSkge1xyXG4gICBpZiAoYWxwaGEgPCAxIHx8IChoc2xhWzNdICYmIGhzbGFbM10gPCAxKSkge1xyXG4gICAgICByZXR1cm4gaHNsYVN0cmluZyhoc2xhLCBhbHBoYSk7XHJcbiAgIH1cclxuICAgcmV0dXJuIFwiaHNsKFwiICsgaHNsYVswXSArIFwiLCBcIiArIGhzbGFbMV0gKyBcIiUsIFwiICsgaHNsYVsyXSArIFwiJSlcIjtcclxufVxyXG5cclxuZnVuY3Rpb24gaHNsYVN0cmluZyhoc2xhLCBhbHBoYSkge1xyXG4gICBpZiAoYWxwaGEgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICBhbHBoYSA9IChoc2xhWzNdICE9PSB1bmRlZmluZWQgPyBoc2xhWzNdIDogMSk7XHJcbiAgIH1cclxuICAgcmV0dXJuIFwiaHNsYShcIiArIGhzbGFbMF0gKyBcIiwgXCIgKyBoc2xhWzFdICsgXCIlLCBcIiArIGhzbGFbMl0gKyBcIiUsIFwiXHJcbiAgICAgICAgICAgKyBhbHBoYSArIFwiKVwiO1xyXG59XHJcblxyXG4vLyBod2IgaXMgYSBiaXQgZGlmZmVyZW50IHRoYW4gcmdiKGEpICYgaHNsKGEpIHNpbmNlIHRoZXJlIGlzIG5vIGFscGhhIHNwZWNpZmljIHN5bnRheFxyXG4vLyAoaHdiIGhhdmUgYWxwaGEgb3B0aW9uYWwgJiAxIGlzIGRlZmF1bHQgdmFsdWUpXHJcbmZ1bmN0aW9uIGh3YlN0cmluZyhod2IsIGFscGhhKSB7XHJcbiAgIGlmIChhbHBoYSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgIGFscGhhID0gKGh3YlszXSAhPT0gdW5kZWZpbmVkID8gaHdiWzNdIDogMSk7XHJcbiAgIH1cclxuICAgcmV0dXJuIFwiaHdiKFwiICsgaHdiWzBdICsgXCIsIFwiICsgaHdiWzFdICsgXCIlLCBcIiArIGh3YlsyXSArIFwiJVwiXHJcbiAgICAgICAgICAgKyAoYWxwaGEgIT09IHVuZGVmaW5lZCAmJiBhbHBoYSAhPT0gMSA/IFwiLCBcIiArIGFscGhhIDogXCJcIikgKyBcIilcIjtcclxufVxyXG5cclxuZnVuY3Rpb24ga2V5d29yZChyZ2IpIHtcclxuICByZXR1cm4gcmV2ZXJzZU5hbWVzW3JnYi5zbGljZSgwLCAzKV07XHJcbn1cclxuXHJcbi8vIGhlbHBlcnNcclxuZnVuY3Rpb24gc2NhbGUobnVtLCBtaW4sIG1heCkge1xyXG4gICByZXR1cm4gTWF0aC5taW4oTWF0aC5tYXgobWluLCBudW0pLCBtYXgpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBoZXhEb3VibGUobnVtKSB7XHJcbiAgdmFyIHN0ciA9IG51bS50b1N0cmluZygxNikudG9VcHBlckNhc2UoKTtcclxuICByZXR1cm4gKHN0ci5sZW5ndGggPCAyKSA/IFwiMFwiICsgc3RyIDogc3RyO1xyXG59XHJcblxyXG5cclxuLy9jcmVhdGUgYSBsaXN0IG9mIHJldmVyc2UgY29sb3IgbmFtZXNcclxudmFyIHJldmVyc2VOYW1lcyA9IHt9O1xyXG5mb3IgKHZhciBuYW1lIGluIGNvbG9yTmFtZXMpIHtcclxuICAgcmV2ZXJzZU5hbWVzW2NvbG9yTmFtZXNbbmFtZV1dID0gbmFtZTtcclxufVxyXG4iLCJtb2R1bGUuZXhwb3J0cyA9IHtcclxuXHRcImFsaWNlYmx1ZVwiOiBbMjQwLCAyNDgsIDI1NV0sXHJcblx0XCJhbnRpcXVld2hpdGVcIjogWzI1MCwgMjM1LCAyMTVdLFxyXG5cdFwiYXF1YVwiOiBbMCwgMjU1LCAyNTVdLFxyXG5cdFwiYXF1YW1hcmluZVwiOiBbMTI3LCAyNTUsIDIxMl0sXHJcblx0XCJhenVyZVwiOiBbMjQwLCAyNTUsIDI1NV0sXHJcblx0XCJiZWlnZVwiOiBbMjQ1LCAyNDUsIDIyMF0sXHJcblx0XCJiaXNxdWVcIjogWzI1NSwgMjI4LCAxOTZdLFxyXG5cdFwiYmxhY2tcIjogWzAsIDAsIDBdLFxyXG5cdFwiYmxhbmNoZWRhbG1vbmRcIjogWzI1NSwgMjM1LCAyMDVdLFxyXG5cdFwiYmx1ZVwiOiBbMCwgMCwgMjU1XSxcclxuXHRcImJsdWV2aW9sZXRcIjogWzEzOCwgNDMsIDIyNl0sXHJcblx0XCJicm93blwiOiBbMTY1LCA0MiwgNDJdLFxyXG5cdFwiYnVybHl3b29kXCI6IFsyMjIsIDE4NCwgMTM1XSxcclxuXHRcImNhZGV0Ymx1ZVwiOiBbOTUsIDE1OCwgMTYwXSxcclxuXHRcImNoYXJ0cmV1c2VcIjogWzEyNywgMjU1LCAwXSxcclxuXHRcImNob2NvbGF0ZVwiOiBbMjEwLCAxMDUsIDMwXSxcclxuXHRcImNvcmFsXCI6IFsyNTUsIDEyNywgODBdLFxyXG5cdFwiY29ybmZsb3dlcmJsdWVcIjogWzEwMCwgMTQ5LCAyMzddLFxyXG5cdFwiY29ybnNpbGtcIjogWzI1NSwgMjQ4LCAyMjBdLFxyXG5cdFwiY3JpbXNvblwiOiBbMjIwLCAyMCwgNjBdLFxyXG5cdFwiY3lhblwiOiBbMCwgMjU1LCAyNTVdLFxyXG5cdFwiZGFya2JsdWVcIjogWzAsIDAsIDEzOV0sXHJcblx0XCJkYXJrY3lhblwiOiBbMCwgMTM5LCAxMzldLFxyXG5cdFwiZGFya2dvbGRlbnJvZFwiOiBbMTg0LCAxMzQsIDExXSxcclxuXHRcImRhcmtncmF5XCI6IFsxNjksIDE2OSwgMTY5XSxcclxuXHRcImRhcmtncmVlblwiOiBbMCwgMTAwLCAwXSxcclxuXHRcImRhcmtncmV5XCI6IFsxNjksIDE2OSwgMTY5XSxcclxuXHRcImRhcmtraGFraVwiOiBbMTg5LCAxODMsIDEwN10sXHJcblx0XCJkYXJrbWFnZW50YVwiOiBbMTM5LCAwLCAxMzldLFxyXG5cdFwiZGFya29saXZlZ3JlZW5cIjogWzg1LCAxMDcsIDQ3XSxcclxuXHRcImRhcmtvcmFuZ2VcIjogWzI1NSwgMTQwLCAwXSxcclxuXHRcImRhcmtvcmNoaWRcIjogWzE1MywgNTAsIDIwNF0sXHJcblx0XCJkYXJrcmVkXCI6IFsxMzksIDAsIDBdLFxyXG5cdFwiZGFya3NhbG1vblwiOiBbMjMzLCAxNTAsIDEyMl0sXHJcblx0XCJkYXJrc2VhZ3JlZW5cIjogWzE0MywgMTg4LCAxNDNdLFxyXG5cdFwiZGFya3NsYXRlYmx1ZVwiOiBbNzIsIDYxLCAxMzldLFxyXG5cdFwiZGFya3NsYXRlZ3JheVwiOiBbNDcsIDc5LCA3OV0sXHJcblx0XCJkYXJrc2xhdGVncmV5XCI6IFs0NywgNzksIDc5XSxcclxuXHRcImRhcmt0dXJxdW9pc2VcIjogWzAsIDIwNiwgMjA5XSxcclxuXHRcImRhcmt2aW9sZXRcIjogWzE0OCwgMCwgMjExXSxcclxuXHRcImRlZXBwaW5rXCI6IFsyNTUsIDIwLCAxNDddLFxyXG5cdFwiZGVlcHNreWJsdWVcIjogWzAsIDE5MSwgMjU1XSxcclxuXHRcImRpbWdyYXlcIjogWzEwNSwgMTA1LCAxMDVdLFxyXG5cdFwiZGltZ3JleVwiOiBbMTA1LCAxMDUsIDEwNV0sXHJcblx0XCJkb2RnZXJibHVlXCI6IFszMCwgMTQ0LCAyNTVdLFxyXG5cdFwiZmlyZWJyaWNrXCI6IFsxNzgsIDM0LCAzNF0sXHJcblx0XCJmbG9yYWx3aGl0ZVwiOiBbMjU1LCAyNTAsIDI0MF0sXHJcblx0XCJmb3Jlc3RncmVlblwiOiBbMzQsIDEzOSwgMzRdLFxyXG5cdFwiZnVjaHNpYVwiOiBbMjU1LCAwLCAyNTVdLFxyXG5cdFwiZ2FpbnNib3JvXCI6IFsyMjAsIDIyMCwgMjIwXSxcclxuXHRcImdob3N0d2hpdGVcIjogWzI0OCwgMjQ4LCAyNTVdLFxyXG5cdFwiZ29sZFwiOiBbMjU1LCAyMTUsIDBdLFxyXG5cdFwiZ29sZGVucm9kXCI6IFsyMTgsIDE2NSwgMzJdLFxyXG5cdFwiZ3JheVwiOiBbMTI4LCAxMjgsIDEyOF0sXHJcblx0XCJncmVlblwiOiBbMCwgMTI4LCAwXSxcclxuXHRcImdyZWVueWVsbG93XCI6IFsxNzMsIDI1NSwgNDddLFxyXG5cdFwiZ3JleVwiOiBbMTI4LCAxMjgsIDEyOF0sXHJcblx0XCJob25leWRld1wiOiBbMjQwLCAyNTUsIDI0MF0sXHJcblx0XCJob3RwaW5rXCI6IFsyNTUsIDEwNSwgMTgwXSxcclxuXHRcImluZGlhbnJlZFwiOiBbMjA1LCA5MiwgOTJdLFxyXG5cdFwiaW5kaWdvXCI6IFs3NSwgMCwgMTMwXSxcclxuXHRcIml2b3J5XCI6IFsyNTUsIDI1NSwgMjQwXSxcclxuXHRcImtoYWtpXCI6IFsyNDAsIDIzMCwgMTQwXSxcclxuXHRcImxhdmVuZGVyXCI6IFsyMzAsIDIzMCwgMjUwXSxcclxuXHRcImxhdmVuZGVyYmx1c2hcIjogWzI1NSwgMjQwLCAyNDVdLFxyXG5cdFwibGF3bmdyZWVuXCI6IFsxMjQsIDI1MiwgMF0sXHJcblx0XCJsZW1vbmNoaWZmb25cIjogWzI1NSwgMjUwLCAyMDVdLFxyXG5cdFwibGlnaHRibHVlXCI6IFsxNzMsIDIxNiwgMjMwXSxcclxuXHRcImxpZ2h0Y29yYWxcIjogWzI0MCwgMTI4LCAxMjhdLFxyXG5cdFwibGlnaHRjeWFuXCI6IFsyMjQsIDI1NSwgMjU1XSxcclxuXHRcImxpZ2h0Z29sZGVucm9keWVsbG93XCI6IFsyNTAsIDI1MCwgMjEwXSxcclxuXHRcImxpZ2h0Z3JheVwiOiBbMjExLCAyMTEsIDIxMV0sXHJcblx0XCJsaWdodGdyZWVuXCI6IFsxNDQsIDIzOCwgMTQ0XSxcclxuXHRcImxpZ2h0Z3JleVwiOiBbMjExLCAyMTEsIDIxMV0sXHJcblx0XCJsaWdodHBpbmtcIjogWzI1NSwgMTgyLCAxOTNdLFxyXG5cdFwibGlnaHRzYWxtb25cIjogWzI1NSwgMTYwLCAxMjJdLFxyXG5cdFwibGlnaHRzZWFncmVlblwiOiBbMzIsIDE3OCwgMTcwXSxcclxuXHRcImxpZ2h0c2t5Ymx1ZVwiOiBbMTM1LCAyMDYsIDI1MF0sXHJcblx0XCJsaWdodHNsYXRlZ3JheVwiOiBbMTE5LCAxMzYsIDE1M10sXHJcblx0XCJsaWdodHNsYXRlZ3JleVwiOiBbMTE5LCAxMzYsIDE1M10sXHJcblx0XCJsaWdodHN0ZWVsYmx1ZVwiOiBbMTc2LCAxOTYsIDIyMl0sXHJcblx0XCJsaWdodHllbGxvd1wiOiBbMjU1LCAyNTUsIDIyNF0sXHJcblx0XCJsaW1lXCI6IFswLCAyNTUsIDBdLFxyXG5cdFwibGltZWdyZWVuXCI6IFs1MCwgMjA1LCA1MF0sXHJcblx0XCJsaW5lblwiOiBbMjUwLCAyNDAsIDIzMF0sXHJcblx0XCJtYWdlbnRhXCI6IFsyNTUsIDAsIDI1NV0sXHJcblx0XCJtYXJvb25cIjogWzEyOCwgMCwgMF0sXHJcblx0XCJtZWRpdW1hcXVhbWFyaW5lXCI6IFsxMDIsIDIwNSwgMTcwXSxcclxuXHRcIm1lZGl1bWJsdWVcIjogWzAsIDAsIDIwNV0sXHJcblx0XCJtZWRpdW1vcmNoaWRcIjogWzE4NiwgODUsIDIxMV0sXHJcblx0XCJtZWRpdW1wdXJwbGVcIjogWzE0NywgMTEyLCAyMTldLFxyXG5cdFwibWVkaXVtc2VhZ3JlZW5cIjogWzYwLCAxNzksIDExM10sXHJcblx0XCJtZWRpdW1zbGF0ZWJsdWVcIjogWzEyMywgMTA0LCAyMzhdLFxyXG5cdFwibWVkaXVtc3ByaW5nZ3JlZW5cIjogWzAsIDI1MCwgMTU0XSxcclxuXHRcIm1lZGl1bXR1cnF1b2lzZVwiOiBbNzIsIDIwOSwgMjA0XSxcclxuXHRcIm1lZGl1bXZpb2xldHJlZFwiOiBbMTk5LCAyMSwgMTMzXSxcclxuXHRcIm1pZG5pZ2h0Ymx1ZVwiOiBbMjUsIDI1LCAxMTJdLFxyXG5cdFwibWludGNyZWFtXCI6IFsyNDUsIDI1NSwgMjUwXSxcclxuXHRcIm1pc3R5cm9zZVwiOiBbMjU1LCAyMjgsIDIyNV0sXHJcblx0XCJtb2NjYXNpblwiOiBbMjU1LCAyMjgsIDE4MV0sXHJcblx0XCJuYXZham93aGl0ZVwiOiBbMjU1LCAyMjIsIDE3M10sXHJcblx0XCJuYXZ5XCI6IFswLCAwLCAxMjhdLFxyXG5cdFwib2xkbGFjZVwiOiBbMjUzLCAyNDUsIDIzMF0sXHJcblx0XCJvbGl2ZVwiOiBbMTI4LCAxMjgsIDBdLFxyXG5cdFwib2xpdmVkcmFiXCI6IFsxMDcsIDE0MiwgMzVdLFxyXG5cdFwib3JhbmdlXCI6IFsyNTUsIDE2NSwgMF0sXHJcblx0XCJvcmFuZ2VyZWRcIjogWzI1NSwgNjksIDBdLFxyXG5cdFwib3JjaGlkXCI6IFsyMTgsIDExMiwgMjE0XSxcclxuXHRcInBhbGVnb2xkZW5yb2RcIjogWzIzOCwgMjMyLCAxNzBdLFxyXG5cdFwicGFsZWdyZWVuXCI6IFsxNTIsIDI1MSwgMTUyXSxcclxuXHRcInBhbGV0dXJxdW9pc2VcIjogWzE3NSwgMjM4LCAyMzhdLFxyXG5cdFwicGFsZXZpb2xldHJlZFwiOiBbMjE5LCAxMTIsIDE0N10sXHJcblx0XCJwYXBheWF3aGlwXCI6IFsyNTUsIDIzOSwgMjEzXSxcclxuXHRcInBlYWNocHVmZlwiOiBbMjU1LCAyMTgsIDE4NV0sXHJcblx0XCJwZXJ1XCI6IFsyMDUsIDEzMywgNjNdLFxyXG5cdFwicGlua1wiOiBbMjU1LCAxOTIsIDIwM10sXHJcblx0XCJwbHVtXCI6IFsyMjEsIDE2MCwgMjIxXSxcclxuXHRcInBvd2RlcmJsdWVcIjogWzE3NiwgMjI0LCAyMzBdLFxyXG5cdFwicHVycGxlXCI6IFsxMjgsIDAsIDEyOF0sXHJcblx0XCJyZWJlY2NhcHVycGxlXCI6IFsxMDIsIDUxLCAxNTNdLFxyXG5cdFwicmVkXCI6IFsyNTUsIDAsIDBdLFxyXG5cdFwicm9zeWJyb3duXCI6IFsxODgsIDE0MywgMTQzXSxcclxuXHRcInJveWFsYmx1ZVwiOiBbNjUsIDEwNSwgMjI1XSxcclxuXHRcInNhZGRsZWJyb3duXCI6IFsxMzksIDY5LCAxOV0sXHJcblx0XCJzYWxtb25cIjogWzI1MCwgMTI4LCAxMTRdLFxyXG5cdFwic2FuZHlicm93blwiOiBbMjQ0LCAxNjQsIDk2XSxcclxuXHRcInNlYWdyZWVuXCI6IFs0NiwgMTM5LCA4N10sXHJcblx0XCJzZWFzaGVsbFwiOiBbMjU1LCAyNDUsIDIzOF0sXHJcblx0XCJzaWVubmFcIjogWzE2MCwgODIsIDQ1XSxcclxuXHRcInNpbHZlclwiOiBbMTkyLCAxOTIsIDE5Ml0sXHJcblx0XCJza3libHVlXCI6IFsxMzUsIDIwNiwgMjM1XSxcclxuXHRcInNsYXRlYmx1ZVwiOiBbMTA2LCA5MCwgMjA1XSxcclxuXHRcInNsYXRlZ3JheVwiOiBbMTEyLCAxMjgsIDE0NF0sXHJcblx0XCJzbGF0ZWdyZXlcIjogWzExMiwgMTI4LCAxNDRdLFxyXG5cdFwic25vd1wiOiBbMjU1LCAyNTAsIDI1MF0sXHJcblx0XCJzcHJpbmdncmVlblwiOiBbMCwgMjU1LCAxMjddLFxyXG5cdFwic3RlZWxibHVlXCI6IFs3MCwgMTMwLCAxODBdLFxyXG5cdFwidGFuXCI6IFsyMTAsIDE4MCwgMTQwXSxcclxuXHRcInRlYWxcIjogWzAsIDEyOCwgMTI4XSxcclxuXHRcInRoaXN0bGVcIjogWzIxNiwgMTkxLCAyMTZdLFxyXG5cdFwidG9tYXRvXCI6IFsyNTUsIDk5LCA3MV0sXHJcblx0XCJ0dXJxdW9pc2VcIjogWzY0LCAyMjQsIDIwOF0sXHJcblx0XCJ2aW9sZXRcIjogWzIzOCwgMTMwLCAyMzhdLFxyXG5cdFwid2hlYXRcIjogWzI0NSwgMjIyLCAxNzldLFxyXG5cdFwid2hpdGVcIjogWzI1NSwgMjU1LCAyNTVdLFxyXG5cdFwid2hpdGVzbW9rZVwiOiBbMjQ1LCAyNDUsIDI0NV0sXHJcblx0XCJ5ZWxsb3dcIjogWzI1NSwgMjU1LCAwXSxcclxuXHRcInllbGxvd2dyZWVuXCI6IFsxNTQsIDIwNSwgNTBdXHJcbn07IiwiLyogTUlUIGxpY2Vuc2UgKi9cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHJnYjJoc2w6IHJnYjJoc2wsXG4gIHJnYjJoc3Y6IHJnYjJoc3YsXG4gIHJnYjJod2I6IHJnYjJod2IsXG4gIHJnYjJjbXlrOiByZ2IyY215ayxcbiAgcmdiMmtleXdvcmQ6IHJnYjJrZXl3b3JkLFxuICByZ2IyeHl6OiByZ2IyeHl6LFxuICByZ2IybGFiOiByZ2IybGFiLFxuICByZ2IybGNoOiByZ2IybGNoLFxuXG4gIGhzbDJyZ2I6IGhzbDJyZ2IsXG4gIGhzbDJoc3Y6IGhzbDJoc3YsXG4gIGhzbDJod2I6IGhzbDJod2IsXG4gIGhzbDJjbXlrOiBoc2wyY215ayxcbiAgaHNsMmtleXdvcmQ6IGhzbDJrZXl3b3JkLFxuXG4gIGhzdjJyZ2I6IGhzdjJyZ2IsXG4gIGhzdjJoc2w6IGhzdjJoc2wsXG4gIGhzdjJod2I6IGhzdjJod2IsXG4gIGhzdjJjbXlrOiBoc3YyY215ayxcbiAgaHN2MmtleXdvcmQ6IGhzdjJrZXl3b3JkLFxuXG4gIGh3YjJyZ2I6IGh3YjJyZ2IsXG4gIGh3YjJoc2w6IGh3YjJoc2wsXG4gIGh3YjJoc3Y6IGh3YjJoc3YsXG4gIGh3YjJjbXlrOiBod2IyY215ayxcbiAgaHdiMmtleXdvcmQ6IGh3YjJrZXl3b3JkLFxuXG4gIGNteWsycmdiOiBjbXlrMnJnYixcbiAgY215azJoc2w6IGNteWsyaHNsLFxuICBjbXlrMmhzdjogY215azJoc3YsXG4gIGNteWsyaHdiOiBjbXlrMmh3YixcbiAgY215azJrZXl3b3JkOiBjbXlrMmtleXdvcmQsXG5cbiAga2V5d29yZDJyZ2I6IGtleXdvcmQycmdiLFxuICBrZXl3b3JkMmhzbDoga2V5d29yZDJoc2wsXG4gIGtleXdvcmQyaHN2OiBrZXl3b3JkMmhzdixcbiAga2V5d29yZDJod2I6IGtleXdvcmQyaHdiLFxuICBrZXl3b3JkMmNteWs6IGtleXdvcmQyY215ayxcbiAga2V5d29yZDJsYWI6IGtleXdvcmQybGFiLFxuICBrZXl3b3JkMnh5ejoga2V5d29yZDJ4eXosXG5cbiAgeHl6MnJnYjogeHl6MnJnYixcbiAgeHl6MmxhYjogeHl6MmxhYixcbiAgeHl6MmxjaDogeHl6MmxjaCxcblxuICBsYWIyeHl6OiBsYWIyeHl6LFxuICBsYWIycmdiOiBsYWIycmdiLFxuICBsYWIybGNoOiBsYWIybGNoLFxuXG4gIGxjaDJsYWI6IGxjaDJsYWIsXG4gIGxjaDJ4eXo6IGxjaDJ4eXosXG4gIGxjaDJyZ2I6IGxjaDJyZ2Jcbn1cblxuXG5mdW5jdGlvbiByZ2IyaHNsKHJnYikge1xuICB2YXIgciA9IHJnYlswXS8yNTUsXG4gICAgICBnID0gcmdiWzFdLzI1NSxcbiAgICAgIGIgPSByZ2JbMl0vMjU1LFxuICAgICAgbWluID0gTWF0aC5taW4ociwgZywgYiksXG4gICAgICBtYXggPSBNYXRoLm1heChyLCBnLCBiKSxcbiAgICAgIGRlbHRhID0gbWF4IC0gbWluLFxuICAgICAgaCwgcywgbDtcblxuICBpZiAobWF4ID09IG1pbilcbiAgICBoID0gMDtcbiAgZWxzZSBpZiAociA9PSBtYXgpXG4gICAgaCA9IChnIC0gYikgLyBkZWx0YTtcbiAgZWxzZSBpZiAoZyA9PSBtYXgpXG4gICAgaCA9IDIgKyAoYiAtIHIpIC8gZGVsdGE7XG4gIGVsc2UgaWYgKGIgPT0gbWF4KVxuICAgIGggPSA0ICsgKHIgLSBnKS8gZGVsdGE7XG5cbiAgaCA9IE1hdGgubWluKGggKiA2MCwgMzYwKTtcblxuICBpZiAoaCA8IDApXG4gICAgaCArPSAzNjA7XG5cbiAgbCA9IChtaW4gKyBtYXgpIC8gMjtcblxuICBpZiAobWF4ID09IG1pbilcbiAgICBzID0gMDtcbiAgZWxzZSBpZiAobCA8PSAwLjUpXG4gICAgcyA9IGRlbHRhIC8gKG1heCArIG1pbik7XG4gIGVsc2VcbiAgICBzID0gZGVsdGEgLyAoMiAtIG1heCAtIG1pbik7XG5cbiAgcmV0dXJuIFtoLCBzICogMTAwLCBsICogMTAwXTtcbn1cblxuZnVuY3Rpb24gcmdiMmhzdihyZ2IpIHtcbiAgdmFyIHIgPSByZ2JbMF0sXG4gICAgICBnID0gcmdiWzFdLFxuICAgICAgYiA9IHJnYlsyXSxcbiAgICAgIG1pbiA9IE1hdGgubWluKHIsIGcsIGIpLFxuICAgICAgbWF4ID0gTWF0aC5tYXgociwgZywgYiksXG4gICAgICBkZWx0YSA9IG1heCAtIG1pbixcbiAgICAgIGgsIHMsIHY7XG5cbiAgaWYgKG1heCA9PSAwKVxuICAgIHMgPSAwO1xuICBlbHNlXG4gICAgcyA9IChkZWx0YS9tYXggKiAxMDAwKS8xMDtcblxuICBpZiAobWF4ID09IG1pbilcbiAgICBoID0gMDtcbiAgZWxzZSBpZiAociA9PSBtYXgpXG4gICAgaCA9IChnIC0gYikgLyBkZWx0YTtcbiAgZWxzZSBpZiAoZyA9PSBtYXgpXG4gICAgaCA9IDIgKyAoYiAtIHIpIC8gZGVsdGE7XG4gIGVsc2UgaWYgKGIgPT0gbWF4KVxuICAgIGggPSA0ICsgKHIgLSBnKSAvIGRlbHRhO1xuXG4gIGggPSBNYXRoLm1pbihoICogNjAsIDM2MCk7XG5cbiAgaWYgKGggPCAwKVxuICAgIGggKz0gMzYwO1xuXG4gIHYgPSAoKG1heCAvIDI1NSkgKiAxMDAwKSAvIDEwO1xuXG4gIHJldHVybiBbaCwgcywgdl07XG59XG5cbmZ1bmN0aW9uIHJnYjJod2IocmdiKSB7XG4gIHZhciByID0gcmdiWzBdLFxuICAgICAgZyA9IHJnYlsxXSxcbiAgICAgIGIgPSByZ2JbMl0sXG4gICAgICBoID0gcmdiMmhzbChyZ2IpWzBdLFxuICAgICAgdyA9IDEvMjU1ICogTWF0aC5taW4ociwgTWF0aC5taW4oZywgYikpLFxuICAgICAgYiA9IDEgLSAxLzI1NSAqIE1hdGgubWF4KHIsIE1hdGgubWF4KGcsIGIpKTtcblxuICByZXR1cm4gW2gsIHcgKiAxMDAsIGIgKiAxMDBdO1xufVxuXG5mdW5jdGlvbiByZ2IyY215ayhyZ2IpIHtcbiAgdmFyIHIgPSByZ2JbMF0gLyAyNTUsXG4gICAgICBnID0gcmdiWzFdIC8gMjU1LFxuICAgICAgYiA9IHJnYlsyXSAvIDI1NSxcbiAgICAgIGMsIG0sIHksIGs7XG5cbiAgayA9IE1hdGgubWluKDEgLSByLCAxIC0gZywgMSAtIGIpO1xuICBjID0gKDEgLSByIC0gaykgLyAoMSAtIGspIHx8IDA7XG4gIG0gPSAoMSAtIGcgLSBrKSAvICgxIC0gaykgfHwgMDtcbiAgeSA9ICgxIC0gYiAtIGspIC8gKDEgLSBrKSB8fCAwO1xuICByZXR1cm4gW2MgKiAxMDAsIG0gKiAxMDAsIHkgKiAxMDAsIGsgKiAxMDBdO1xufVxuXG5mdW5jdGlvbiByZ2Iya2V5d29yZChyZ2IpIHtcbiAgcmV0dXJuIHJldmVyc2VLZXl3b3Jkc1tKU09OLnN0cmluZ2lmeShyZ2IpXTtcbn1cblxuZnVuY3Rpb24gcmdiMnh5eihyZ2IpIHtcbiAgdmFyIHIgPSByZ2JbMF0gLyAyNTUsXG4gICAgICBnID0gcmdiWzFdIC8gMjU1LFxuICAgICAgYiA9IHJnYlsyXSAvIDI1NTtcblxuICAvLyBhc3N1bWUgc1JHQlxuICByID0gciA+IDAuMDQwNDUgPyBNYXRoLnBvdygoKHIgKyAwLjA1NSkgLyAxLjA1NSksIDIuNCkgOiAociAvIDEyLjkyKTtcbiAgZyA9IGcgPiAwLjA0MDQ1ID8gTWF0aC5wb3coKChnICsgMC4wNTUpIC8gMS4wNTUpLCAyLjQpIDogKGcgLyAxMi45Mik7XG4gIGIgPSBiID4gMC4wNDA0NSA/IE1hdGgucG93KCgoYiArIDAuMDU1KSAvIDEuMDU1KSwgMi40KSA6IChiIC8gMTIuOTIpO1xuXG4gIHZhciB4ID0gKHIgKiAwLjQxMjQpICsgKGcgKiAwLjM1NzYpICsgKGIgKiAwLjE4MDUpO1xuICB2YXIgeSA9IChyICogMC4yMTI2KSArIChnICogMC43MTUyKSArIChiICogMC4wNzIyKTtcbiAgdmFyIHogPSAociAqIDAuMDE5MykgKyAoZyAqIDAuMTE5MikgKyAoYiAqIDAuOTUwNSk7XG5cbiAgcmV0dXJuIFt4ICogMTAwLCB5ICoxMDAsIHogKiAxMDBdO1xufVxuXG5mdW5jdGlvbiByZ2IybGFiKHJnYikge1xuICB2YXIgeHl6ID0gcmdiMnh5eihyZ2IpLFxuICAgICAgICB4ID0geHl6WzBdLFxuICAgICAgICB5ID0geHl6WzFdLFxuICAgICAgICB6ID0geHl6WzJdLFxuICAgICAgICBsLCBhLCBiO1xuXG4gIHggLz0gOTUuMDQ3O1xuICB5IC89IDEwMDtcbiAgeiAvPSAxMDguODgzO1xuXG4gIHggPSB4ID4gMC4wMDg4NTYgPyBNYXRoLnBvdyh4LCAxLzMpIDogKDcuNzg3ICogeCkgKyAoMTYgLyAxMTYpO1xuICB5ID0geSA+IDAuMDA4ODU2ID8gTWF0aC5wb3coeSwgMS8zKSA6ICg3Ljc4NyAqIHkpICsgKDE2IC8gMTE2KTtcbiAgeiA9IHogPiAwLjAwODg1NiA/IE1hdGgucG93KHosIDEvMykgOiAoNy43ODcgKiB6KSArICgxNiAvIDExNik7XG5cbiAgbCA9ICgxMTYgKiB5KSAtIDE2O1xuICBhID0gNTAwICogKHggLSB5KTtcbiAgYiA9IDIwMCAqICh5IC0geik7XG5cbiAgcmV0dXJuIFtsLCBhLCBiXTtcbn1cblxuZnVuY3Rpb24gcmdiMmxjaChhcmdzKSB7XG4gIHJldHVybiBsYWIybGNoKHJnYjJsYWIoYXJncykpO1xufVxuXG5mdW5jdGlvbiBoc2wycmdiKGhzbCkge1xuICB2YXIgaCA9IGhzbFswXSAvIDM2MCxcbiAgICAgIHMgPSBoc2xbMV0gLyAxMDAsXG4gICAgICBsID0gaHNsWzJdIC8gMTAwLFxuICAgICAgdDEsIHQyLCB0MywgcmdiLCB2YWw7XG5cbiAgaWYgKHMgPT0gMCkge1xuICAgIHZhbCA9IGwgKiAyNTU7XG4gICAgcmV0dXJuIFt2YWwsIHZhbCwgdmFsXTtcbiAgfVxuXG4gIGlmIChsIDwgMC41KVxuICAgIHQyID0gbCAqICgxICsgcyk7XG4gIGVsc2VcbiAgICB0MiA9IGwgKyBzIC0gbCAqIHM7XG4gIHQxID0gMiAqIGwgLSB0MjtcblxuICByZ2IgPSBbMCwgMCwgMF07XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgMzsgaSsrKSB7XG4gICAgdDMgPSBoICsgMSAvIDMgKiAtIChpIC0gMSk7XG4gICAgdDMgPCAwICYmIHQzKys7XG4gICAgdDMgPiAxICYmIHQzLS07XG5cbiAgICBpZiAoNiAqIHQzIDwgMSlcbiAgICAgIHZhbCA9IHQxICsgKHQyIC0gdDEpICogNiAqIHQzO1xuICAgIGVsc2UgaWYgKDIgKiB0MyA8IDEpXG4gICAgICB2YWwgPSB0MjtcbiAgICBlbHNlIGlmICgzICogdDMgPCAyKVxuICAgICAgdmFsID0gdDEgKyAodDIgLSB0MSkgKiAoMiAvIDMgLSB0MykgKiA2O1xuICAgIGVsc2VcbiAgICAgIHZhbCA9IHQxO1xuXG4gICAgcmdiW2ldID0gdmFsICogMjU1O1xuICB9XG5cbiAgcmV0dXJuIHJnYjtcbn1cblxuZnVuY3Rpb24gaHNsMmhzdihoc2wpIHtcbiAgdmFyIGggPSBoc2xbMF0sXG4gICAgICBzID0gaHNsWzFdIC8gMTAwLFxuICAgICAgbCA9IGhzbFsyXSAvIDEwMCxcbiAgICAgIHN2LCB2O1xuXG4gIGlmKGwgPT09IDApIHtcbiAgICAgIC8vIG5vIG5lZWQgdG8gZG8gY2FsYyBvbiBibGFja1xuICAgICAgLy8gYWxzbyBhdm9pZHMgZGl2aWRlIGJ5IDAgZXJyb3JcbiAgICAgIHJldHVybiBbMCwgMCwgMF07XG4gIH1cblxuICBsICo9IDI7XG4gIHMgKj0gKGwgPD0gMSkgPyBsIDogMiAtIGw7XG4gIHYgPSAobCArIHMpIC8gMjtcbiAgc3YgPSAoMiAqIHMpIC8gKGwgKyBzKTtcbiAgcmV0dXJuIFtoLCBzdiAqIDEwMCwgdiAqIDEwMF07XG59XG5cbmZ1bmN0aW9uIGhzbDJod2IoYXJncykge1xuICByZXR1cm4gcmdiMmh3Yihoc2wycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gaHNsMmNteWsoYXJncykge1xuICByZXR1cm4gcmdiMmNteWsoaHNsMnJnYihhcmdzKSk7XG59XG5cbmZ1bmN0aW9uIGhzbDJrZXl3b3JkKGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJrZXl3b3JkKGhzbDJyZ2IoYXJncykpO1xufVxuXG5cbmZ1bmN0aW9uIGhzdjJyZ2IoaHN2KSB7XG4gIHZhciBoID0gaHN2WzBdIC8gNjAsXG4gICAgICBzID0gaHN2WzFdIC8gMTAwLFxuICAgICAgdiA9IGhzdlsyXSAvIDEwMCxcbiAgICAgIGhpID0gTWF0aC5mbG9vcihoKSAlIDY7XG5cbiAgdmFyIGYgPSBoIC0gTWF0aC5mbG9vcihoKSxcbiAgICAgIHAgPSAyNTUgKiB2ICogKDEgLSBzKSxcbiAgICAgIHEgPSAyNTUgKiB2ICogKDEgLSAocyAqIGYpKSxcbiAgICAgIHQgPSAyNTUgKiB2ICogKDEgLSAocyAqICgxIC0gZikpKSxcbiAgICAgIHYgPSAyNTUgKiB2O1xuXG4gIHN3aXRjaChoaSkge1xuICAgIGNhc2UgMDpcbiAgICAgIHJldHVybiBbdiwgdCwgcF07XG4gICAgY2FzZSAxOlxuICAgICAgcmV0dXJuIFtxLCB2LCBwXTtcbiAgICBjYXNlIDI6XG4gICAgICByZXR1cm4gW3AsIHYsIHRdO1xuICAgIGNhc2UgMzpcbiAgICAgIHJldHVybiBbcCwgcSwgdl07XG4gICAgY2FzZSA0OlxuICAgICAgcmV0dXJuIFt0LCBwLCB2XTtcbiAgICBjYXNlIDU6XG4gICAgICByZXR1cm4gW3YsIHAsIHFdO1xuICB9XG59XG5cbmZ1bmN0aW9uIGhzdjJoc2woaHN2KSB7XG4gIHZhciBoID0gaHN2WzBdLFxuICAgICAgcyA9IGhzdlsxXSAvIDEwMCxcbiAgICAgIHYgPSBoc3ZbMl0gLyAxMDAsXG4gICAgICBzbCwgbDtcblxuICBsID0gKDIgLSBzKSAqIHY7XG4gIHNsID0gcyAqIHY7XG4gIHNsIC89IChsIDw9IDEpID8gbCA6IDIgLSBsO1xuICBzbCA9IHNsIHx8IDA7XG4gIGwgLz0gMjtcbiAgcmV0dXJuIFtoLCBzbCAqIDEwMCwgbCAqIDEwMF07XG59XG5cbmZ1bmN0aW9uIGhzdjJod2IoYXJncykge1xuICByZXR1cm4gcmdiMmh3Yihoc3YycmdiKGFyZ3MpKVxufVxuXG5mdW5jdGlvbiBoc3YyY215ayhhcmdzKSB7XG4gIHJldHVybiByZ2IyY215ayhoc3YycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gaHN2MmtleXdvcmQoYXJncykge1xuICByZXR1cm4gcmdiMmtleXdvcmQoaHN2MnJnYihhcmdzKSk7XG59XG5cbi8vIGh0dHA6Ly9kZXYudzMub3JnL2Nzc3dnL2Nzcy1jb2xvci8jaHdiLXRvLXJnYlxuZnVuY3Rpb24gaHdiMnJnYihod2IpIHtcbiAgdmFyIGggPSBod2JbMF0gLyAzNjAsXG4gICAgICB3aCA9IGh3YlsxXSAvIDEwMCxcbiAgICAgIGJsID0gaHdiWzJdIC8gMTAwLFxuICAgICAgcmF0aW8gPSB3aCArIGJsLFxuICAgICAgaSwgdiwgZiwgbjtcblxuICAvLyB3aCArIGJsIGNhbnQgYmUgPiAxXG4gIGlmIChyYXRpbyA+IDEpIHtcbiAgICB3aCAvPSByYXRpbztcbiAgICBibCAvPSByYXRpbztcbiAgfVxuXG4gIGkgPSBNYXRoLmZsb29yKDYgKiBoKTtcbiAgdiA9IDEgLSBibDtcbiAgZiA9IDYgKiBoIC0gaTtcbiAgaWYgKChpICYgMHgwMSkgIT0gMCkge1xuICAgIGYgPSAxIC0gZjtcbiAgfVxuICBuID0gd2ggKyBmICogKHYgLSB3aCk7ICAvLyBsaW5lYXIgaW50ZXJwb2xhdGlvblxuXG4gIHN3aXRjaCAoaSkge1xuICAgIGRlZmF1bHQ6XG4gICAgY2FzZSA2OlxuICAgIGNhc2UgMDogciA9IHY7IGcgPSBuOyBiID0gd2g7IGJyZWFrO1xuICAgIGNhc2UgMTogciA9IG47IGcgPSB2OyBiID0gd2g7IGJyZWFrO1xuICAgIGNhc2UgMjogciA9IHdoOyBnID0gdjsgYiA9IG47IGJyZWFrO1xuICAgIGNhc2UgMzogciA9IHdoOyBnID0gbjsgYiA9IHY7IGJyZWFrO1xuICAgIGNhc2UgNDogciA9IG47IGcgPSB3aDsgYiA9IHY7IGJyZWFrO1xuICAgIGNhc2UgNTogciA9IHY7IGcgPSB3aDsgYiA9IG47IGJyZWFrO1xuICB9XG5cbiAgcmV0dXJuIFtyICogMjU1LCBnICogMjU1LCBiICogMjU1XTtcbn1cblxuZnVuY3Rpb24gaHdiMmhzbChhcmdzKSB7XG4gIHJldHVybiByZ2IyaHNsKGh3YjJyZ2IoYXJncykpO1xufVxuXG5mdW5jdGlvbiBod2IyaHN2KGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJoc3YoaHdiMnJnYihhcmdzKSk7XG59XG5cbmZ1bmN0aW9uIGh3YjJjbXlrKGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJjbXlrKGh3YjJyZ2IoYXJncykpO1xufVxuXG5mdW5jdGlvbiBod2Iya2V5d29yZChhcmdzKSB7XG4gIHJldHVybiByZ2Iya2V5d29yZChod2IycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gY215azJyZ2IoY215aykge1xuICB2YXIgYyA9IGNteWtbMF0gLyAxMDAsXG4gICAgICBtID0gY215a1sxXSAvIDEwMCxcbiAgICAgIHkgPSBjbXlrWzJdIC8gMTAwLFxuICAgICAgayA9IGNteWtbM10gLyAxMDAsXG4gICAgICByLCBnLCBiO1xuXG4gIHIgPSAxIC0gTWF0aC5taW4oMSwgYyAqICgxIC0gaykgKyBrKTtcbiAgZyA9IDEgLSBNYXRoLm1pbigxLCBtICogKDEgLSBrKSArIGspO1xuICBiID0gMSAtIE1hdGgubWluKDEsIHkgKiAoMSAtIGspICsgayk7XG4gIHJldHVybiBbciAqIDI1NSwgZyAqIDI1NSwgYiAqIDI1NV07XG59XG5cbmZ1bmN0aW9uIGNteWsyaHNsKGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJoc2woY215azJyZ2IoYXJncykpO1xufVxuXG5mdW5jdGlvbiBjbXlrMmhzdihhcmdzKSB7XG4gIHJldHVybiByZ2IyaHN2KGNteWsycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gY215azJod2IoYXJncykge1xuICByZXR1cm4gcmdiMmh3YihjbXlrMnJnYihhcmdzKSk7XG59XG5cbmZ1bmN0aW9uIGNteWsya2V5d29yZChhcmdzKSB7XG4gIHJldHVybiByZ2Iya2V5d29yZChjbXlrMnJnYihhcmdzKSk7XG59XG5cblxuZnVuY3Rpb24geHl6MnJnYih4eXopIHtcbiAgdmFyIHggPSB4eXpbMF0gLyAxMDAsXG4gICAgICB5ID0geHl6WzFdIC8gMTAwLFxuICAgICAgeiA9IHh5elsyXSAvIDEwMCxcbiAgICAgIHIsIGcsIGI7XG5cbiAgciA9ICh4ICogMy4yNDA2KSArICh5ICogLTEuNTM3MikgKyAoeiAqIC0wLjQ5ODYpO1xuICBnID0gKHggKiAtMC45Njg5KSArICh5ICogMS44NzU4KSArICh6ICogMC4wNDE1KTtcbiAgYiA9ICh4ICogMC4wNTU3KSArICh5ICogLTAuMjA0MCkgKyAoeiAqIDEuMDU3MCk7XG5cbiAgLy8gYXNzdW1lIHNSR0JcbiAgciA9IHIgPiAwLjAwMzEzMDggPyAoKDEuMDU1ICogTWF0aC5wb3cociwgMS4wIC8gMi40KSkgLSAwLjA1NSlcbiAgICA6IHIgPSAociAqIDEyLjkyKTtcblxuICBnID0gZyA+IDAuMDAzMTMwOCA/ICgoMS4wNTUgKiBNYXRoLnBvdyhnLCAxLjAgLyAyLjQpKSAtIDAuMDU1KVxuICAgIDogZyA9IChnICogMTIuOTIpO1xuXG4gIGIgPSBiID4gMC4wMDMxMzA4ID8gKCgxLjA1NSAqIE1hdGgucG93KGIsIDEuMCAvIDIuNCkpIC0gMC4wNTUpXG4gICAgOiBiID0gKGIgKiAxMi45Mik7XG5cbiAgciA9IE1hdGgubWluKE1hdGgubWF4KDAsIHIpLCAxKTtcbiAgZyA9IE1hdGgubWluKE1hdGgubWF4KDAsIGcpLCAxKTtcbiAgYiA9IE1hdGgubWluKE1hdGgubWF4KDAsIGIpLCAxKTtcblxuICByZXR1cm4gW3IgKiAyNTUsIGcgKiAyNTUsIGIgKiAyNTVdO1xufVxuXG5mdW5jdGlvbiB4eXoybGFiKHh5eikge1xuICB2YXIgeCA9IHh5elswXSxcbiAgICAgIHkgPSB4eXpbMV0sXG4gICAgICB6ID0geHl6WzJdLFxuICAgICAgbCwgYSwgYjtcblxuICB4IC89IDk1LjA0NztcbiAgeSAvPSAxMDA7XG4gIHogLz0gMTA4Ljg4MztcblxuICB4ID0geCA+IDAuMDA4ODU2ID8gTWF0aC5wb3coeCwgMS8zKSA6ICg3Ljc4NyAqIHgpICsgKDE2IC8gMTE2KTtcbiAgeSA9IHkgPiAwLjAwODg1NiA/IE1hdGgucG93KHksIDEvMykgOiAoNy43ODcgKiB5KSArICgxNiAvIDExNik7XG4gIHogPSB6ID4gMC4wMDg4NTYgPyBNYXRoLnBvdyh6LCAxLzMpIDogKDcuNzg3ICogeikgKyAoMTYgLyAxMTYpO1xuXG4gIGwgPSAoMTE2ICogeSkgLSAxNjtcbiAgYSA9IDUwMCAqICh4IC0geSk7XG4gIGIgPSAyMDAgKiAoeSAtIHopO1xuXG4gIHJldHVybiBbbCwgYSwgYl07XG59XG5cbmZ1bmN0aW9uIHh5ejJsY2goYXJncykge1xuICByZXR1cm4gbGFiMmxjaCh4eXoybGFiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gbGFiMnh5eihsYWIpIHtcbiAgdmFyIGwgPSBsYWJbMF0sXG4gICAgICBhID0gbGFiWzFdLFxuICAgICAgYiA9IGxhYlsyXSxcbiAgICAgIHgsIHksIHosIHkyO1xuXG4gIGlmIChsIDw9IDgpIHtcbiAgICB5ID0gKGwgKiAxMDApIC8gOTAzLjM7XG4gICAgeTIgPSAoNy43ODcgKiAoeSAvIDEwMCkpICsgKDE2IC8gMTE2KTtcbiAgfSBlbHNlIHtcbiAgICB5ID0gMTAwICogTWF0aC5wb3coKGwgKyAxNikgLyAxMTYsIDMpO1xuICAgIHkyID0gTWF0aC5wb3coeSAvIDEwMCwgMS8zKTtcbiAgfVxuXG4gIHggPSB4IC8gOTUuMDQ3IDw9IDAuMDA4ODU2ID8geCA9ICg5NS4wNDcgKiAoKGEgLyA1MDApICsgeTIgLSAoMTYgLyAxMTYpKSkgLyA3Ljc4NyA6IDk1LjA0NyAqIE1hdGgucG93KChhIC8gNTAwKSArIHkyLCAzKTtcblxuICB6ID0geiAvIDEwOC44ODMgPD0gMC4wMDg4NTkgPyB6ID0gKDEwOC44ODMgKiAoeTIgLSAoYiAvIDIwMCkgLSAoMTYgLyAxMTYpKSkgLyA3Ljc4NyA6IDEwOC44ODMgKiBNYXRoLnBvdyh5MiAtIChiIC8gMjAwKSwgMyk7XG5cbiAgcmV0dXJuIFt4LCB5LCB6XTtcbn1cblxuZnVuY3Rpb24gbGFiMmxjaChsYWIpIHtcbiAgdmFyIGwgPSBsYWJbMF0sXG4gICAgICBhID0gbGFiWzFdLFxuICAgICAgYiA9IGxhYlsyXSxcbiAgICAgIGhyLCBoLCBjO1xuXG4gIGhyID0gTWF0aC5hdGFuMihiLCBhKTtcbiAgaCA9IGhyICogMzYwIC8gMiAvIE1hdGguUEk7XG4gIGlmIChoIDwgMCkge1xuICAgIGggKz0gMzYwO1xuICB9XG4gIGMgPSBNYXRoLnNxcnQoYSAqIGEgKyBiICogYik7XG4gIHJldHVybiBbbCwgYywgaF07XG59XG5cbmZ1bmN0aW9uIGxhYjJyZ2IoYXJncykge1xuICByZXR1cm4geHl6MnJnYihsYWIyeHl6KGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gbGNoMmxhYihsY2gpIHtcbiAgdmFyIGwgPSBsY2hbMF0sXG4gICAgICBjID0gbGNoWzFdLFxuICAgICAgaCA9IGxjaFsyXSxcbiAgICAgIGEsIGIsIGhyO1xuXG4gIGhyID0gaCAvIDM2MCAqIDIgKiBNYXRoLlBJO1xuICBhID0gYyAqIE1hdGguY29zKGhyKTtcbiAgYiA9IGMgKiBNYXRoLnNpbihocik7XG4gIHJldHVybiBbbCwgYSwgYl07XG59XG5cbmZ1bmN0aW9uIGxjaDJ4eXooYXJncykge1xuICByZXR1cm4gbGFiMnh5eihsY2gybGFiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24gbGNoMnJnYihhcmdzKSB7XG4gIHJldHVybiBsYWIycmdiKGxjaDJsYWIoYXJncykpO1xufVxuXG5mdW5jdGlvbiBrZXl3b3JkMnJnYihrZXl3b3JkKSB7XG4gIHJldHVybiBjc3NLZXl3b3Jkc1trZXl3b3JkXTtcbn1cblxuZnVuY3Rpb24ga2V5d29yZDJoc2woYXJncykge1xuICByZXR1cm4gcmdiMmhzbChrZXl3b3JkMnJnYihhcmdzKSk7XG59XG5cbmZ1bmN0aW9uIGtleXdvcmQyaHN2KGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJoc3Yoa2V5d29yZDJyZ2IoYXJncykpO1xufVxuXG5mdW5jdGlvbiBrZXl3b3JkMmh3YihhcmdzKSB7XG4gIHJldHVybiByZ2IyaHdiKGtleXdvcmQycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24ga2V5d29yZDJjbXlrKGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJjbXlrKGtleXdvcmQycmdiKGFyZ3MpKTtcbn1cblxuZnVuY3Rpb24ga2V5d29yZDJsYWIoYXJncykge1xuICByZXR1cm4gcmdiMmxhYihrZXl3b3JkMnJnYihhcmdzKSk7XG59XG5cbmZ1bmN0aW9uIGtleXdvcmQyeHl6KGFyZ3MpIHtcbiAgcmV0dXJuIHJnYjJ4eXooa2V5d29yZDJyZ2IoYXJncykpO1xufVxuXG52YXIgY3NzS2V5d29yZHMgPSB7XG4gIGFsaWNlYmx1ZTogIFsyNDAsMjQ4LDI1NV0sXG4gIGFudGlxdWV3aGl0ZTogWzI1MCwyMzUsMjE1XSxcbiAgYXF1YTogWzAsMjU1LDI1NV0sXG4gIGFxdWFtYXJpbmU6IFsxMjcsMjU1LDIxMl0sXG4gIGF6dXJlOiAgWzI0MCwyNTUsMjU1XSxcbiAgYmVpZ2U6ICBbMjQ1LDI0NSwyMjBdLFxuICBiaXNxdWU6IFsyNTUsMjI4LDE5Nl0sXG4gIGJsYWNrOiAgWzAsMCwwXSxcbiAgYmxhbmNoZWRhbG1vbmQ6IFsyNTUsMjM1LDIwNV0sXG4gIGJsdWU6IFswLDAsMjU1XSxcbiAgYmx1ZXZpb2xldDogWzEzOCw0MywyMjZdLFxuICBicm93bjogIFsxNjUsNDIsNDJdLFxuICBidXJseXdvb2Q6ICBbMjIyLDE4NCwxMzVdLFxuICBjYWRldGJsdWU6ICBbOTUsMTU4LDE2MF0sXG4gIGNoYXJ0cmV1c2U6IFsxMjcsMjU1LDBdLFxuICBjaG9jb2xhdGU6ICBbMjEwLDEwNSwzMF0sXG4gIGNvcmFsOiAgWzI1NSwxMjcsODBdLFxuICBjb3JuZmxvd2VyYmx1ZTogWzEwMCwxNDksMjM3XSxcbiAgY29ybnNpbGs6IFsyNTUsMjQ4LDIyMF0sXG4gIGNyaW1zb246ICBbMjIwLDIwLDYwXSxcbiAgY3lhbjogWzAsMjU1LDI1NV0sXG4gIGRhcmtibHVlOiBbMCwwLDEzOV0sXG4gIGRhcmtjeWFuOiBbMCwxMzksMTM5XSxcbiAgZGFya2dvbGRlbnJvZDogIFsxODQsMTM0LDExXSxcbiAgZGFya2dyYXk6IFsxNjksMTY5LDE2OV0sXG4gIGRhcmtncmVlbjogIFswLDEwMCwwXSxcbiAgZGFya2dyZXk6IFsxNjksMTY5LDE2OV0sXG4gIGRhcmtraGFraTogIFsxODksMTgzLDEwN10sXG4gIGRhcmttYWdlbnRhOiAgWzEzOSwwLDEzOV0sXG4gIGRhcmtvbGl2ZWdyZWVuOiBbODUsMTA3LDQ3XSxcbiAgZGFya29yYW5nZTogWzI1NSwxNDAsMF0sXG4gIGRhcmtvcmNoaWQ6IFsxNTMsNTAsMjA0XSxcbiAgZGFya3JlZDogIFsxMzksMCwwXSxcbiAgZGFya3NhbG1vbjogWzIzMywxNTAsMTIyXSxcbiAgZGFya3NlYWdyZWVuOiBbMTQzLDE4OCwxNDNdLFxuICBkYXJrc2xhdGVibHVlOiAgWzcyLDYxLDEzOV0sXG4gIGRhcmtzbGF0ZWdyYXk6ICBbNDcsNzksNzldLFxuICBkYXJrc2xhdGVncmV5OiAgWzQ3LDc5LDc5XSxcbiAgZGFya3R1cnF1b2lzZTogIFswLDIwNiwyMDldLFxuICBkYXJrdmlvbGV0OiBbMTQ4LDAsMjExXSxcbiAgZGVlcHBpbms6IFsyNTUsMjAsMTQ3XSxcbiAgZGVlcHNreWJsdWU6ICBbMCwxOTEsMjU1XSxcbiAgZGltZ3JheTogIFsxMDUsMTA1LDEwNV0sXG4gIGRpbWdyZXk6ICBbMTA1LDEwNSwxMDVdLFxuICBkb2RnZXJibHVlOiBbMzAsMTQ0LDI1NV0sXG4gIGZpcmVicmljazogIFsxNzgsMzQsMzRdLFxuICBmbG9yYWx3aGl0ZTogIFsyNTUsMjUwLDI0MF0sXG4gIGZvcmVzdGdyZWVuOiAgWzM0LDEzOSwzNF0sXG4gIGZ1Y2hzaWE6ICBbMjU1LDAsMjU1XSxcbiAgZ2FpbnNib3JvOiAgWzIyMCwyMjAsMjIwXSxcbiAgZ2hvc3R3aGl0ZTogWzI0OCwyNDgsMjU1XSxcbiAgZ29sZDogWzI1NSwyMTUsMF0sXG4gIGdvbGRlbnJvZDogIFsyMTgsMTY1LDMyXSxcbiAgZ3JheTogWzEyOCwxMjgsMTI4XSxcbiAgZ3JlZW46ICBbMCwxMjgsMF0sXG4gIGdyZWVueWVsbG93OiAgWzE3MywyNTUsNDddLFxuICBncmV5OiBbMTI4LDEyOCwxMjhdLFxuICBob25leWRldzogWzI0MCwyNTUsMjQwXSxcbiAgaG90cGluazogIFsyNTUsMTA1LDE4MF0sXG4gIGluZGlhbnJlZDogIFsyMDUsOTIsOTJdLFxuICBpbmRpZ286IFs3NSwwLDEzMF0sXG4gIGl2b3J5OiAgWzI1NSwyNTUsMjQwXSxcbiAga2hha2k6ICBbMjQwLDIzMCwxNDBdLFxuICBsYXZlbmRlcjogWzIzMCwyMzAsMjUwXSxcbiAgbGF2ZW5kZXJibHVzaDogIFsyNTUsMjQwLDI0NV0sXG4gIGxhd25ncmVlbjogIFsxMjQsMjUyLDBdLFxuICBsZW1vbmNoaWZmb246IFsyNTUsMjUwLDIwNV0sXG4gIGxpZ2h0Ymx1ZTogIFsxNzMsMjE2LDIzMF0sXG4gIGxpZ2h0Y29yYWw6IFsyNDAsMTI4LDEyOF0sXG4gIGxpZ2h0Y3lhbjogIFsyMjQsMjU1LDI1NV0sXG4gIGxpZ2h0Z29sZGVucm9keWVsbG93OiBbMjUwLDI1MCwyMTBdLFxuICBsaWdodGdyYXk6ICBbMjExLDIxMSwyMTFdLFxuICBsaWdodGdyZWVuOiBbMTQ0LDIzOCwxNDRdLFxuICBsaWdodGdyZXk6ICBbMjExLDIxMSwyMTFdLFxuICBsaWdodHBpbms6ICBbMjU1LDE4MiwxOTNdLFxuICBsaWdodHNhbG1vbjogIFsyNTUsMTYwLDEyMl0sXG4gIGxpZ2h0c2VhZ3JlZW46ICBbMzIsMTc4LDE3MF0sXG4gIGxpZ2h0c2t5Ymx1ZTogWzEzNSwyMDYsMjUwXSxcbiAgbGlnaHRzbGF0ZWdyYXk6IFsxMTksMTM2LDE1M10sXG4gIGxpZ2h0c2xhdGVncmV5OiBbMTE5LDEzNiwxNTNdLFxuICBsaWdodHN0ZWVsYmx1ZTogWzE3NiwxOTYsMjIyXSxcbiAgbGlnaHR5ZWxsb3c6ICBbMjU1LDI1NSwyMjRdLFxuICBsaW1lOiBbMCwyNTUsMF0sXG4gIGxpbWVncmVlbjogIFs1MCwyMDUsNTBdLFxuICBsaW5lbjogIFsyNTAsMjQwLDIzMF0sXG4gIG1hZ2VudGE6ICBbMjU1LDAsMjU1XSxcbiAgbWFyb29uOiBbMTI4LDAsMF0sXG4gIG1lZGl1bWFxdWFtYXJpbmU6IFsxMDIsMjA1LDE3MF0sXG4gIG1lZGl1bWJsdWU6IFswLDAsMjA1XSxcbiAgbWVkaXVtb3JjaGlkOiBbMTg2LDg1LDIxMV0sXG4gIG1lZGl1bXB1cnBsZTogWzE0NywxMTIsMjE5XSxcbiAgbWVkaXVtc2VhZ3JlZW46IFs2MCwxNzksMTEzXSxcbiAgbWVkaXVtc2xhdGVibHVlOiAgWzEyMywxMDQsMjM4XSxcbiAgbWVkaXVtc3ByaW5nZ3JlZW46ICBbMCwyNTAsMTU0XSxcbiAgbWVkaXVtdHVycXVvaXNlOiAgWzcyLDIwOSwyMDRdLFxuICBtZWRpdW12aW9sZXRyZWQ6ICBbMTk5LDIxLDEzM10sXG4gIG1pZG5pZ2h0Ymx1ZTogWzI1LDI1LDExMl0sXG4gIG1pbnRjcmVhbTogIFsyNDUsMjU1LDI1MF0sXG4gIG1pc3R5cm9zZTogIFsyNTUsMjI4LDIyNV0sXG4gIG1vY2Nhc2luOiBbMjU1LDIyOCwxODFdLFxuICBuYXZham93aGl0ZTogIFsyNTUsMjIyLDE3M10sXG4gIG5hdnk6IFswLDAsMTI4XSxcbiAgb2xkbGFjZTogIFsyNTMsMjQ1LDIzMF0sXG4gIG9saXZlOiAgWzEyOCwxMjgsMF0sXG4gIG9saXZlZHJhYjogIFsxMDcsMTQyLDM1XSxcbiAgb3JhbmdlOiBbMjU1LDE2NSwwXSxcbiAgb3JhbmdlcmVkOiAgWzI1NSw2OSwwXSxcbiAgb3JjaGlkOiBbMjE4LDExMiwyMTRdLFxuICBwYWxlZ29sZGVucm9kOiAgWzIzOCwyMzIsMTcwXSxcbiAgcGFsZWdyZWVuOiAgWzE1MiwyNTEsMTUyXSxcbiAgcGFsZXR1cnF1b2lzZTogIFsxNzUsMjM4LDIzOF0sXG4gIHBhbGV2aW9sZXRyZWQ6ICBbMjE5LDExMiwxNDddLFxuICBwYXBheWF3aGlwOiBbMjU1LDIzOSwyMTNdLFxuICBwZWFjaHB1ZmY6ICBbMjU1LDIxOCwxODVdLFxuICBwZXJ1OiBbMjA1LDEzMyw2M10sXG4gIHBpbms6IFsyNTUsMTkyLDIwM10sXG4gIHBsdW06IFsyMjEsMTYwLDIyMV0sXG4gIHBvd2RlcmJsdWU6IFsxNzYsMjI0LDIzMF0sXG4gIHB1cnBsZTogWzEyOCwwLDEyOF0sXG4gIHJlYmVjY2FwdXJwbGU6IFsxMDIsIDUxLCAxNTNdLFxuICByZWQ6ICBbMjU1LDAsMF0sXG4gIHJvc3licm93bjogIFsxODgsMTQzLDE0M10sXG4gIHJveWFsYmx1ZTogIFs2NSwxMDUsMjI1XSxcbiAgc2FkZGxlYnJvd246ICBbMTM5LDY5LDE5XSxcbiAgc2FsbW9uOiBbMjUwLDEyOCwxMTRdLFxuICBzYW5keWJyb3duOiBbMjQ0LDE2NCw5Nl0sXG4gIHNlYWdyZWVuOiBbNDYsMTM5LDg3XSxcbiAgc2Vhc2hlbGw6IFsyNTUsMjQ1LDIzOF0sXG4gIHNpZW5uYTogWzE2MCw4Miw0NV0sXG4gIHNpbHZlcjogWzE5MiwxOTIsMTkyXSxcbiAgc2t5Ymx1ZTogIFsxMzUsMjA2LDIzNV0sXG4gIHNsYXRlYmx1ZTogIFsxMDYsOTAsMjA1XSxcbiAgc2xhdGVncmF5OiAgWzExMiwxMjgsMTQ0XSxcbiAgc2xhdGVncmV5OiAgWzExMiwxMjgsMTQ0XSxcbiAgc25vdzogWzI1NSwyNTAsMjUwXSxcbiAgc3ByaW5nZ3JlZW46ICBbMCwyNTUsMTI3XSxcbiAgc3RlZWxibHVlOiAgWzcwLDEzMCwxODBdLFxuICB0YW46ICBbMjEwLDE4MCwxNDBdLFxuICB0ZWFsOiBbMCwxMjgsMTI4XSxcbiAgdGhpc3RsZTogIFsyMTYsMTkxLDIxNl0sXG4gIHRvbWF0bzogWzI1NSw5OSw3MV0sXG4gIHR1cnF1b2lzZTogIFs2NCwyMjQsMjA4XSxcbiAgdmlvbGV0OiBbMjM4LDEzMCwyMzhdLFxuICB3aGVhdDogIFsyNDUsMjIyLDE3OV0sXG4gIHdoaXRlOiAgWzI1NSwyNTUsMjU1XSxcbiAgd2hpdGVzbW9rZTogWzI0NSwyNDUsMjQ1XSxcbiAgeWVsbG93OiBbMjU1LDI1NSwwXSxcbiAgeWVsbG93Z3JlZW46ICBbMTU0LDIwNSw1MF1cbn07XG5cbnZhciByZXZlcnNlS2V5d29yZHMgPSB7fTtcbmZvciAodmFyIGtleSBpbiBjc3NLZXl3b3Jkcykge1xuICByZXZlcnNlS2V5d29yZHNbSlNPTi5zdHJpbmdpZnkoY3NzS2V5d29yZHNba2V5XSldID0ga2V5O1xufVxuIiwidmFyIGNvbnZlcnNpb25zID0gcmVxdWlyZShcIi4vY29udmVyc2lvbnNcIik7XG5cbnZhciBjb252ZXJ0ID0gZnVuY3Rpb24oKSB7XG4gICByZXR1cm4gbmV3IENvbnZlcnRlcigpO1xufVxuXG5mb3IgKHZhciBmdW5jIGluIGNvbnZlcnNpb25zKSB7XG4gIC8vIGV4cG9ydCBSYXcgdmVyc2lvbnNcbiAgY29udmVydFtmdW5jICsgXCJSYXdcIl0gPSAgKGZ1bmN0aW9uKGZ1bmMpIHtcbiAgICAvLyBhY2NlcHQgYXJyYXkgb3IgcGxhaW4gYXJnc1xuICAgIHJldHVybiBmdW5jdGlvbihhcmcpIHtcbiAgICAgIGlmICh0eXBlb2YgYXJnID09IFwibnVtYmVyXCIpXG4gICAgICAgIGFyZyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgICByZXR1cm4gY29udmVyc2lvbnNbZnVuY10oYXJnKTtcbiAgICB9XG4gIH0pKGZ1bmMpO1xuXG4gIHZhciBwYWlyID0gLyhcXHcrKTIoXFx3KykvLmV4ZWMoZnVuYyksXG4gICAgICBmcm9tID0gcGFpclsxXSxcbiAgICAgIHRvID0gcGFpclsyXTtcblxuICAvLyBleHBvcnQgcmdiMmhzbCBhbmQgW1wicmdiXCJdW1wiaHNsXCJdXG4gIGNvbnZlcnRbZnJvbV0gPSBjb252ZXJ0W2Zyb21dIHx8IHt9O1xuXG4gIGNvbnZlcnRbZnJvbV1bdG9dID0gY29udmVydFtmdW5jXSA9IChmdW5jdGlvbihmdW5jKSB7IFxuICAgIHJldHVybiBmdW5jdGlvbihhcmcpIHtcbiAgICAgIGlmICh0eXBlb2YgYXJnID09IFwibnVtYmVyXCIpXG4gICAgICAgIGFyZyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgICBcbiAgICAgIHZhciB2YWwgPSBjb252ZXJzaW9uc1tmdW5jXShhcmcpO1xuICAgICAgaWYgKHR5cGVvZiB2YWwgPT0gXCJzdHJpbmdcIiB8fCB2YWwgPT09IHVuZGVmaW5lZClcbiAgICAgICAgcmV0dXJuIHZhbDsgLy8ga2V5d29yZFxuXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHZhbC5sZW5ndGg7IGkrKylcbiAgICAgICAgdmFsW2ldID0gTWF0aC5yb3VuZCh2YWxbaV0pO1xuICAgICAgcmV0dXJuIHZhbDtcbiAgICB9XG4gIH0pKGZ1bmMpO1xufVxuXG5cbi8qIENvbnZlcnRlciBkb2VzIGxhenkgY29udmVyc2lvbiBhbmQgY2FjaGluZyAqL1xudmFyIENvbnZlcnRlciA9IGZ1bmN0aW9uKCkge1xuICAgdGhpcy5jb252cyA9IHt9O1xufTtcblxuLyogRWl0aGVyIGdldCB0aGUgdmFsdWVzIGZvciBhIHNwYWNlIG9yXG4gIHNldCB0aGUgdmFsdWVzIGZvciBhIHNwYWNlLCBkZXBlbmRpbmcgb24gYXJncyAqL1xuQ29udmVydGVyLnByb3RvdHlwZS5yb3V0ZVNwYWNlID0gZnVuY3Rpb24oc3BhY2UsIGFyZ3MpIHtcbiAgIHZhciB2YWx1ZXMgPSBhcmdzWzBdO1xuICAgaWYgKHZhbHVlcyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBjb2xvci5yZ2IoKVxuICAgICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKHNwYWNlKTtcbiAgIH1cbiAgIC8vIGNvbG9yLnJnYigxMCwgMTAsIDEwKVxuICAgaWYgKHR5cGVvZiB2YWx1ZXMgPT0gXCJudW1iZXJcIikge1xuICAgICAgdmFsdWVzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJncyk7ICAgICAgICBcbiAgIH1cblxuICAgcmV0dXJuIHRoaXMuc2V0VmFsdWVzKHNwYWNlLCB2YWx1ZXMpO1xufTtcbiAgXG4vKiBTZXQgdGhlIHZhbHVlcyBmb3IgYSBzcGFjZSwgaW52YWxpZGF0aW5nIGNhY2hlICovXG5Db252ZXJ0ZXIucHJvdG90eXBlLnNldFZhbHVlcyA9IGZ1bmN0aW9uKHNwYWNlLCB2YWx1ZXMpIHtcbiAgIHRoaXMuc3BhY2UgPSBzcGFjZTtcbiAgIHRoaXMuY29udnMgPSB7fTtcbiAgIHRoaXMuY29udnNbc3BhY2VdID0gdmFsdWVzO1xuICAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKiBHZXQgdGhlIHZhbHVlcyBmb3IgYSBzcGFjZS4gSWYgdGhlcmUncyBhbHJlYWR5XG4gIGEgY29udmVyc2lvbiBmb3IgdGhlIHNwYWNlLCBmZXRjaCBpdCwgb3RoZXJ3aXNlXG4gIGNvbXB1dGUgaXQgKi9cbkNvbnZlcnRlci5wcm90b3R5cGUuZ2V0VmFsdWVzID0gZnVuY3Rpb24oc3BhY2UpIHtcbiAgIHZhciB2YWxzID0gdGhpcy5jb252c1tzcGFjZV07XG4gICBpZiAoIXZhbHMpIHtcbiAgICAgIHZhciBmc3BhY2UgPSB0aGlzLnNwYWNlLFxuICAgICAgICAgIGZyb20gPSB0aGlzLmNvbnZzW2ZzcGFjZV07XG4gICAgICB2YWxzID0gY29udmVydFtmc3BhY2VdW3NwYWNlXShmcm9tKTtcblxuICAgICAgdGhpcy5jb252c1tzcGFjZV0gPSB2YWxzO1xuICAgfVxuICByZXR1cm4gdmFscztcbn07XG5cbltcInJnYlwiLCBcImhzbFwiLCBcImhzdlwiLCBcImNteWtcIiwgXCJrZXl3b3JkXCJdLmZvckVhY2goZnVuY3Rpb24oc3BhY2UpIHtcbiAgIENvbnZlcnRlci5wcm90b3R5cGVbc3BhY2VdID0gZnVuY3Rpb24odmFscykge1xuICAgICAgcmV0dXJuIHRoaXMucm91dGVTcGFjZShzcGFjZSwgYXJndW1lbnRzKTtcbiAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnZlcnQ7IiwiLy8hIG1vbWVudC5qc1xuLy8hIHZlcnNpb24gOiAyLjE0LjFcbi8vISBhdXRob3JzIDogVGltIFdvb2QsIElza3JlbiBDaGVybmV2LCBNb21lbnQuanMgY29udHJpYnV0b3JzXG4vLyEgbGljZW5zZSA6IE1JVFxuLy8hIG1vbWVudGpzLmNvbVxuXG47KGZ1bmN0aW9uIChnbG9iYWwsIGZhY3RvcnkpIHtcbiAgICB0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIG1vZHVsZSAhPT0gJ3VuZGVmaW5lZCcgPyBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKSA6XG4gICAgdHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kID8gZGVmaW5lKGZhY3RvcnkpIDpcbiAgICBnbG9iYWwubW9tZW50ID0gZmFjdG9yeSgpXG59KHRoaXMsIGZ1bmN0aW9uICgpIHsgJ3VzZSBzdHJpY3QnO1xuXG4gICAgdmFyIGhvb2tDYWxsYmFjaztcblxuICAgIGZ1bmN0aW9uIHV0aWxzX2hvb2tzX19ob29rcyAoKSB7XG4gICAgICAgIHJldHVybiBob29rQ2FsbGJhY2suYXBwbHkobnVsbCwgYXJndW1lbnRzKTtcbiAgICB9XG5cbiAgICAvLyBUaGlzIGlzIGRvbmUgdG8gcmVnaXN0ZXIgdGhlIG1ldGhvZCBjYWxsZWQgd2l0aCBtb21lbnQoKVxuICAgIC8vIHdpdGhvdXQgY3JlYXRpbmcgY2lyY3VsYXIgZGVwZW5kZW5jaWVzLlxuICAgIGZ1bmN0aW9uIHNldEhvb2tDYWxsYmFjayAoY2FsbGJhY2spIHtcbiAgICAgICAgaG9va0NhbGxiYWNrID0gY2FsbGJhY2s7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNBcnJheShpbnB1dCkge1xuICAgICAgICByZXR1cm4gaW5wdXQgaW5zdGFuY2VvZiBBcnJheSB8fCBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaW5wdXQpID09PSAnW29iamVjdCBBcnJheV0nO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzT2JqZWN0KGlucHV0KSB7XG4gICAgICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaW5wdXQpID09PSAnW29iamVjdCBPYmplY3RdJztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc09iamVjdEVtcHR5KG9iaikge1xuICAgICAgICB2YXIgaztcbiAgICAgICAgZm9yIChrIGluIG9iaikge1xuICAgICAgICAgICAgLy8gZXZlbiBpZiBpdHMgbm90IG93biBwcm9wZXJ0eSBJJ2Qgc3RpbGwgY2FsbCBpdCBub24tZW1wdHlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc0RhdGUoaW5wdXQpIHtcbiAgICAgICAgcmV0dXJuIGlucHV0IGluc3RhbmNlb2YgRGF0ZSB8fCBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaW5wdXQpID09PSAnW29iamVjdCBEYXRlXSc7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbWFwKGFyciwgZm4pIHtcbiAgICAgICAgdmFyIHJlcyA9IFtdLCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICByZXMucHVzaChmbihhcnJbaV0sIGkpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGhhc093blByb3AoYSwgYikge1xuICAgICAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGEsIGIpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGV4dGVuZChhLCBiKSB7XG4gICAgICAgIGZvciAodmFyIGkgaW4gYikge1xuICAgICAgICAgICAgaWYgKGhhc093blByb3AoYiwgaSkpIHtcbiAgICAgICAgICAgICAgICBhW2ldID0gYltpXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChoYXNPd25Qcm9wKGIsICd0b1N0cmluZycpKSB7XG4gICAgICAgICAgICBhLnRvU3RyaW5nID0gYi50b1N0cmluZztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChoYXNPd25Qcm9wKGIsICd2YWx1ZU9mJykpIHtcbiAgICAgICAgICAgIGEudmFsdWVPZiA9IGIudmFsdWVPZjtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBhO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNyZWF0ZV91dGNfX2NyZWF0ZVVUQyAoaW5wdXQsIGZvcm1hdCwgbG9jYWxlLCBzdHJpY3QpIHtcbiAgICAgICAgcmV0dXJuIGNyZWF0ZUxvY2FsT3JVVEMoaW5wdXQsIGZvcm1hdCwgbG9jYWxlLCBzdHJpY3QsIHRydWUpLnV0YygpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGRlZmF1bHRQYXJzaW5nRmxhZ3MoKSB7XG4gICAgICAgIC8vIFdlIG5lZWQgdG8gZGVlcCBjbG9uZSB0aGlzIG9iamVjdC5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGVtcHR5ICAgICAgICAgICA6IGZhbHNlLFxuICAgICAgICAgICAgdW51c2VkVG9rZW5zICAgIDogW10sXG4gICAgICAgICAgICB1bnVzZWRJbnB1dCAgICAgOiBbXSxcbiAgICAgICAgICAgIG92ZXJmbG93ICAgICAgICA6IC0yLFxuICAgICAgICAgICAgY2hhcnNMZWZ0T3ZlciAgIDogMCxcbiAgICAgICAgICAgIG51bGxJbnB1dCAgICAgICA6IGZhbHNlLFxuICAgICAgICAgICAgaW52YWxpZE1vbnRoICAgIDogbnVsbCxcbiAgICAgICAgICAgIGludmFsaWRGb3JtYXQgICA6IGZhbHNlLFxuICAgICAgICAgICAgdXNlckludmFsaWRhdGVkIDogZmFsc2UsXG4gICAgICAgICAgICBpc28gICAgICAgICAgICAgOiBmYWxzZSxcbiAgICAgICAgICAgIHBhcnNlZERhdGVQYXJ0cyA6IFtdLFxuICAgICAgICAgICAgbWVyaWRpZW0gICAgICAgIDogbnVsbFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFBhcnNpbmdGbGFncyhtKSB7XG4gICAgICAgIGlmIChtLl9wZiA9PSBudWxsKSB7XG4gICAgICAgICAgICBtLl9wZiA9IGRlZmF1bHRQYXJzaW5nRmxhZ3MoKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbS5fcGY7XG4gICAgfVxuXG4gICAgdmFyIHNvbWU7XG4gICAgaWYgKEFycmF5LnByb3RvdHlwZS5zb21lKSB7XG4gICAgICAgIHNvbWUgPSBBcnJheS5wcm90b3R5cGUuc29tZTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBzb21lID0gZnVuY3Rpb24gKGZ1bikge1xuICAgICAgICAgICAgdmFyIHQgPSBPYmplY3QodGhpcyk7XG4gICAgICAgICAgICB2YXIgbGVuID0gdC5sZW5ndGggPj4+IDA7XG5cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoaSBpbiB0ICYmIGZ1bi5jYWxsKHRoaXMsIHRbaV0sIGksIHQpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHZhbGlkX19pc1ZhbGlkKG0pIHtcbiAgICAgICAgaWYgKG0uX2lzVmFsaWQgPT0gbnVsbCkge1xuICAgICAgICAgICAgdmFyIGZsYWdzID0gZ2V0UGFyc2luZ0ZsYWdzKG0pO1xuICAgICAgICAgICAgdmFyIHBhcnNlZFBhcnRzID0gc29tZS5jYWxsKGZsYWdzLnBhcnNlZERhdGVQYXJ0cywgZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaSAhPSBudWxsO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBtLl9pc1ZhbGlkID0gIWlzTmFOKG0uX2QuZ2V0VGltZSgpKSAmJlxuICAgICAgICAgICAgICAgIGZsYWdzLm92ZXJmbG93IDwgMCAmJlxuICAgICAgICAgICAgICAgICFmbGFncy5lbXB0eSAmJlxuICAgICAgICAgICAgICAgICFmbGFncy5pbnZhbGlkTW9udGggJiZcbiAgICAgICAgICAgICAgICAhZmxhZ3MuaW52YWxpZFdlZWtkYXkgJiZcbiAgICAgICAgICAgICAgICAhZmxhZ3MubnVsbElucHV0ICYmXG4gICAgICAgICAgICAgICAgIWZsYWdzLmludmFsaWRGb3JtYXQgJiZcbiAgICAgICAgICAgICAgICAhZmxhZ3MudXNlckludmFsaWRhdGVkICYmXG4gICAgICAgICAgICAgICAgKCFmbGFncy5tZXJpZGllbSB8fCAoZmxhZ3MubWVyaWRpZW0gJiYgcGFyc2VkUGFydHMpKTtcblxuICAgICAgICAgICAgaWYgKG0uX3N0cmljdCkge1xuICAgICAgICAgICAgICAgIG0uX2lzVmFsaWQgPSBtLl9pc1ZhbGlkICYmXG4gICAgICAgICAgICAgICAgICAgIGZsYWdzLmNoYXJzTGVmdE92ZXIgPT09IDAgJiZcbiAgICAgICAgICAgICAgICAgICAgZmxhZ3MudW51c2VkVG9rZW5zLmxlbmd0aCA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgICBmbGFncy5iaWdIb3VyID09PSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG0uX2lzVmFsaWQ7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdmFsaWRfX2NyZWF0ZUludmFsaWQgKGZsYWdzKSB7XG4gICAgICAgIHZhciBtID0gY3JlYXRlX3V0Y19fY3JlYXRlVVRDKE5hTik7XG4gICAgICAgIGlmIChmbGFncyAhPSBudWxsKSB7XG4gICAgICAgICAgICBleHRlbmQoZ2V0UGFyc2luZ0ZsYWdzKG0pLCBmbGFncyk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MobSkudXNlckludmFsaWRhdGVkID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBtO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzVW5kZWZpbmVkKGlucHV0KSB7XG4gICAgICAgIHJldHVybiBpbnB1dCA9PT0gdm9pZCAwO1xuICAgIH1cblxuICAgIC8vIFBsdWdpbnMgdGhhdCBhZGQgcHJvcGVydGllcyBzaG91bGQgYWxzbyBhZGQgdGhlIGtleSBoZXJlIChudWxsIHZhbHVlKSxcbiAgICAvLyBzbyB3ZSBjYW4gcHJvcGVybHkgY2xvbmUgb3Vyc2VsdmVzLlxuICAgIHZhciBtb21lbnRQcm9wZXJ0aWVzID0gdXRpbHNfaG9va3NfX2hvb2tzLm1vbWVudFByb3BlcnRpZXMgPSBbXTtcblxuICAgIGZ1bmN0aW9uIGNvcHlDb25maWcodG8sIGZyb20pIHtcbiAgICAgICAgdmFyIGksIHByb3AsIHZhbDtcblxuICAgICAgICBpZiAoIWlzVW5kZWZpbmVkKGZyb20uX2lzQU1vbWVudE9iamVjdCkpIHtcbiAgICAgICAgICAgIHRvLl9pc0FNb21lbnRPYmplY3QgPSBmcm9tLl9pc0FNb21lbnRPYmplY3Q7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc1VuZGVmaW5lZChmcm9tLl9pKSkge1xuICAgICAgICAgICAgdG8uX2kgPSBmcm9tLl9pO1xuICAgICAgICB9XG4gICAgICAgIGlmICghaXNVbmRlZmluZWQoZnJvbS5fZikpIHtcbiAgICAgICAgICAgIHRvLl9mID0gZnJvbS5fZjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWlzVW5kZWZpbmVkKGZyb20uX2wpKSB7XG4gICAgICAgICAgICB0by5fbCA9IGZyb20uX2w7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc1VuZGVmaW5lZChmcm9tLl9zdHJpY3QpKSB7XG4gICAgICAgICAgICB0by5fc3RyaWN0ID0gZnJvbS5fc3RyaWN0O1xuICAgICAgICB9XG4gICAgICAgIGlmICghaXNVbmRlZmluZWQoZnJvbS5fdHptKSkge1xuICAgICAgICAgICAgdG8uX3R6bSA9IGZyb20uX3R6bTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWlzVW5kZWZpbmVkKGZyb20uX2lzVVRDKSkge1xuICAgICAgICAgICAgdG8uX2lzVVRDID0gZnJvbS5faXNVVEM7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc1VuZGVmaW5lZChmcm9tLl9vZmZzZXQpKSB7XG4gICAgICAgICAgICB0by5fb2Zmc2V0ID0gZnJvbS5fb2Zmc2V0O1xuICAgICAgICB9XG4gICAgICAgIGlmICghaXNVbmRlZmluZWQoZnJvbS5fcGYpKSB7XG4gICAgICAgICAgICB0by5fcGYgPSBnZXRQYXJzaW5nRmxhZ3MoZnJvbSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc1VuZGVmaW5lZChmcm9tLl9sb2NhbGUpKSB7XG4gICAgICAgICAgICB0by5fbG9jYWxlID0gZnJvbS5fbG9jYWxlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1vbWVudFByb3BlcnRpZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZm9yIChpIGluIG1vbWVudFByb3BlcnRpZXMpIHtcbiAgICAgICAgICAgICAgICBwcm9wID0gbW9tZW50UHJvcGVydGllc1tpXTtcbiAgICAgICAgICAgICAgICB2YWwgPSBmcm9tW3Byb3BdO1xuICAgICAgICAgICAgICAgIGlmICghaXNVbmRlZmluZWQodmFsKSkge1xuICAgICAgICAgICAgICAgICAgICB0b1twcm9wXSA9IHZhbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdG87XG4gICAgfVxuXG4gICAgdmFyIHVwZGF0ZUluUHJvZ3Jlc3MgPSBmYWxzZTtcblxuICAgIC8vIE1vbWVudCBwcm90b3R5cGUgb2JqZWN0XG4gICAgZnVuY3Rpb24gTW9tZW50KGNvbmZpZykge1xuICAgICAgICBjb3B5Q29uZmlnKHRoaXMsIGNvbmZpZyk7XG4gICAgICAgIHRoaXMuX2QgPSBuZXcgRGF0ZShjb25maWcuX2QgIT0gbnVsbCA/IGNvbmZpZy5fZC5nZXRUaW1lKCkgOiBOYU4pO1xuICAgICAgICAvLyBQcmV2ZW50IGluZmluaXRlIGxvb3AgaW4gY2FzZSB1cGRhdGVPZmZzZXQgY3JlYXRlcyBuZXcgbW9tZW50XG4gICAgICAgIC8vIG9iamVjdHMuXG4gICAgICAgIGlmICh1cGRhdGVJblByb2dyZXNzID09PSBmYWxzZSkge1xuICAgICAgICAgICAgdXBkYXRlSW5Qcm9ncmVzcyA9IHRydWU7XG4gICAgICAgICAgICB1dGlsc19ob29rc19faG9va3MudXBkYXRlT2Zmc2V0KHRoaXMpO1xuICAgICAgICAgICAgdXBkYXRlSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNNb21lbnQgKG9iaikge1xuICAgICAgICByZXR1cm4gb2JqIGluc3RhbmNlb2YgTW9tZW50IHx8IChvYmogIT0gbnVsbCAmJiBvYmouX2lzQU1vbWVudE9iamVjdCAhPSBudWxsKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBhYnNGbG9vciAobnVtYmVyKSB7XG4gICAgICAgIGlmIChudW1iZXIgPCAwKSB7XG4gICAgICAgICAgICAvLyAtMCAtPiAwXG4gICAgICAgICAgICByZXR1cm4gTWF0aC5jZWlsKG51bWJlcikgfHwgMDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBNYXRoLmZsb29yKG51bWJlcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiB0b0ludChhcmd1bWVudEZvckNvZXJjaW9uKSB7XG4gICAgICAgIHZhciBjb2VyY2VkTnVtYmVyID0gK2FyZ3VtZW50Rm9yQ29lcmNpb24sXG4gICAgICAgICAgICB2YWx1ZSA9IDA7XG5cbiAgICAgICAgaWYgKGNvZXJjZWROdW1iZXIgIT09IDAgJiYgaXNGaW5pdGUoY29lcmNlZE51bWJlcikpIHtcbiAgICAgICAgICAgIHZhbHVlID0gYWJzRmxvb3IoY29lcmNlZE51bWJlcik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgfVxuXG4gICAgLy8gY29tcGFyZSB0d28gYXJyYXlzLCByZXR1cm4gdGhlIG51bWJlciBvZiBkaWZmZXJlbmNlc1xuICAgIGZ1bmN0aW9uIGNvbXBhcmVBcnJheXMoYXJyYXkxLCBhcnJheTIsIGRvbnRDb252ZXJ0KSB7XG4gICAgICAgIHZhciBsZW4gPSBNYXRoLm1pbihhcnJheTEubGVuZ3RoLCBhcnJheTIubGVuZ3RoKSxcbiAgICAgICAgICAgIGxlbmd0aERpZmYgPSBNYXRoLmFicyhhcnJheTEubGVuZ3RoIC0gYXJyYXkyLmxlbmd0aCksXG4gICAgICAgICAgICBkaWZmcyA9IDAsXG4gICAgICAgICAgICBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgIGlmICgoZG9udENvbnZlcnQgJiYgYXJyYXkxW2ldICE9PSBhcnJheTJbaV0pIHx8XG4gICAgICAgICAgICAgICAgKCFkb250Q29udmVydCAmJiB0b0ludChhcnJheTFbaV0pICE9PSB0b0ludChhcnJheTJbaV0pKSkge1xuICAgICAgICAgICAgICAgIGRpZmZzKys7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRpZmZzICsgbGVuZ3RoRGlmZjtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB3YXJuKG1zZykge1xuICAgICAgICBpZiAodXRpbHNfaG9va3NfX2hvb2tzLnN1cHByZXNzRGVwcmVjYXRpb25XYXJuaW5ncyA9PT0gZmFsc2UgJiZcbiAgICAgICAgICAgICAgICAodHlwZW9mIGNvbnNvbGUgIT09ICAndW5kZWZpbmVkJykgJiYgY29uc29sZS53YXJuKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oJ0RlcHJlY2F0aW9uIHdhcm5pbmc6ICcgKyBtc2cpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZGVwcmVjYXRlKG1zZywgZm4pIHtcbiAgICAgICAgdmFyIGZpcnN0VGltZSA9IHRydWU7XG5cbiAgICAgICAgcmV0dXJuIGV4dGVuZChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBpZiAodXRpbHNfaG9va3NfX2hvb2tzLmRlcHJlY2F0aW9uSGFuZGxlciAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLmRlcHJlY2F0aW9uSGFuZGxlcihudWxsLCBtc2cpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGZpcnN0VGltZSkge1xuICAgICAgICAgICAgICAgIHdhcm4obXNnICsgJ1xcbkFyZ3VtZW50czogJyArIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykuam9pbignLCAnKSArICdcXG4nICsgKG5ldyBFcnJvcigpKS5zdGFjayk7XG4gICAgICAgICAgICAgICAgZmlyc3RUaW1lID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gZm4uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfSwgZm4pO1xuICAgIH1cblxuICAgIHZhciBkZXByZWNhdGlvbnMgPSB7fTtcblxuICAgIGZ1bmN0aW9uIGRlcHJlY2F0ZVNpbXBsZShuYW1lLCBtc2cpIHtcbiAgICAgICAgaWYgKHV0aWxzX2hvb2tzX19ob29rcy5kZXByZWNhdGlvbkhhbmRsZXIgIT0gbnVsbCkge1xuICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLmRlcHJlY2F0aW9uSGFuZGxlcihuYW1lLCBtc2cpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZGVwcmVjYXRpb25zW25hbWVdKSB7XG4gICAgICAgICAgICB3YXJuKG1zZyk7XG4gICAgICAgICAgICBkZXByZWNhdGlvbnNbbmFtZV0gPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXRpbHNfaG9va3NfX2hvb2tzLnN1cHByZXNzRGVwcmVjYXRpb25XYXJuaW5ncyA9IGZhbHNlO1xuICAgIHV0aWxzX2hvb2tzX19ob29rcy5kZXByZWNhdGlvbkhhbmRsZXIgPSBudWxsO1xuXG4gICAgZnVuY3Rpb24gaXNGdW5jdGlvbihpbnB1dCkge1xuICAgICAgICByZXR1cm4gaW5wdXQgaW5zdGFuY2VvZiBGdW5jdGlvbiB8fCBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaW5wdXQpID09PSAnW29iamVjdCBGdW5jdGlvbl0nO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGxvY2FsZV9zZXRfX3NldCAoY29uZmlnKSB7XG4gICAgICAgIHZhciBwcm9wLCBpO1xuICAgICAgICBmb3IgKGkgaW4gY29uZmlnKSB7XG4gICAgICAgICAgICBwcm9wID0gY29uZmlnW2ldO1xuICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24ocHJvcCkpIHtcbiAgICAgICAgICAgICAgICB0aGlzW2ldID0gcHJvcDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpc1snXycgKyBpXSA9IHByb3A7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fY29uZmlnID0gY29uZmlnO1xuICAgICAgICAvLyBMZW5pZW50IG9yZGluYWwgcGFyc2luZyBhY2NlcHRzIGp1c3QgYSBudW1iZXIgaW4gYWRkaXRpb24gdG9cbiAgICAgICAgLy8gbnVtYmVyICsgKHBvc3NpYmx5KSBzdHVmZiBjb21pbmcgZnJvbSBfb3JkaW5hbFBhcnNlTGVuaWVudC5cbiAgICAgICAgdGhpcy5fb3JkaW5hbFBhcnNlTGVuaWVudCA9IG5ldyBSZWdFeHAodGhpcy5fb3JkaW5hbFBhcnNlLnNvdXJjZSArICd8JyArICgvXFxkezEsMn0vKS5zb3VyY2UpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIG1lcmdlQ29uZmlncyhwYXJlbnRDb25maWcsIGNoaWxkQ29uZmlnKSB7XG4gICAgICAgIHZhciByZXMgPSBleHRlbmQoe30sIHBhcmVudENvbmZpZyksIHByb3A7XG4gICAgICAgIGZvciAocHJvcCBpbiBjaGlsZENvbmZpZykge1xuICAgICAgICAgICAgaWYgKGhhc093blByb3AoY2hpbGRDb25maWcsIHByb3ApKSB7XG4gICAgICAgICAgICAgICAgaWYgKGlzT2JqZWN0KHBhcmVudENvbmZpZ1twcm9wXSkgJiYgaXNPYmplY3QoY2hpbGRDb25maWdbcHJvcF0pKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc1twcm9wXSA9IHt9O1xuICAgICAgICAgICAgICAgICAgICBleHRlbmQocmVzW3Byb3BdLCBwYXJlbnRDb25maWdbcHJvcF0pO1xuICAgICAgICAgICAgICAgICAgICBleHRlbmQocmVzW3Byb3BdLCBjaGlsZENvbmZpZ1twcm9wXSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjaGlsZENvbmZpZ1twcm9wXSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc1twcm9wXSA9IGNoaWxkQ29uZmlnW3Byb3BdO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSByZXNbcHJvcF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZvciAocHJvcCBpbiBwYXJlbnRDb25maWcpIHtcbiAgICAgICAgICAgIGlmIChoYXNPd25Qcm9wKHBhcmVudENvbmZpZywgcHJvcCkgJiZcbiAgICAgICAgICAgICAgICAgICAgIWhhc093blByb3AoY2hpbGRDb25maWcsIHByb3ApICYmXG4gICAgICAgICAgICAgICAgICAgIGlzT2JqZWN0KHBhcmVudENvbmZpZ1twcm9wXSkpIHtcbiAgICAgICAgICAgICAgICAvLyBtYWtlIHN1cmUgY2hhbmdlcyB0byBwcm9wZXJ0aWVzIGRvbid0IG1vZGlmeSBwYXJlbnQgY29uZmlnXG4gICAgICAgICAgICAgICAgcmVzW3Byb3BdID0gZXh0ZW5kKHt9LCByZXNbcHJvcF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXM7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gTG9jYWxlKGNvbmZpZykge1xuICAgICAgICBpZiAoY29uZmlnICE9IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuc2V0KGNvbmZpZyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIga2V5cztcblxuICAgIGlmIChPYmplY3Qua2V5cykge1xuICAgICAgICBrZXlzID0gT2JqZWN0LmtleXM7XG4gICAgfSBlbHNlIHtcbiAgICAgICAga2V5cyA9IGZ1bmN0aW9uIChvYmopIHtcbiAgICAgICAgICAgIHZhciBpLCByZXMgPSBbXTtcbiAgICAgICAgICAgIGZvciAoaSBpbiBvYmopIHtcbiAgICAgICAgICAgICAgICBpZiAoaGFzT3duUHJvcChvYmosIGkpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlcy5wdXNoKGkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiByZXM7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRDYWxlbmRhciA9IHtcbiAgICAgICAgc2FtZURheSA6ICdbVG9kYXkgYXRdIExUJyxcbiAgICAgICAgbmV4dERheSA6ICdbVG9tb3Jyb3cgYXRdIExUJyxcbiAgICAgICAgbmV4dFdlZWsgOiAnZGRkZCBbYXRdIExUJyxcbiAgICAgICAgbGFzdERheSA6ICdbWWVzdGVyZGF5IGF0XSBMVCcsXG4gICAgICAgIGxhc3RXZWVrIDogJ1tMYXN0XSBkZGRkIFthdF0gTFQnLFxuICAgICAgICBzYW1lRWxzZSA6ICdMJ1xuICAgIH07XG5cbiAgICBmdW5jdGlvbiBsb2NhbGVfY2FsZW5kYXJfX2NhbGVuZGFyIChrZXksIG1vbSwgbm93KSB7XG4gICAgICAgIHZhciBvdXRwdXQgPSB0aGlzLl9jYWxlbmRhcltrZXldIHx8IHRoaXMuX2NhbGVuZGFyWydzYW1lRWxzZSddO1xuICAgICAgICByZXR1cm4gaXNGdW5jdGlvbihvdXRwdXQpID8gb3V0cHV0LmNhbGwobW9tLCBub3cpIDogb3V0cHV0O1xuICAgIH1cblxuICAgIHZhciBkZWZhdWx0TG9uZ0RhdGVGb3JtYXQgPSB7XG4gICAgICAgIExUUyAgOiAnaDptbTpzcyBBJyxcbiAgICAgICAgTFQgICA6ICdoOm1tIEEnLFxuICAgICAgICBMICAgIDogJ01NL0REL1lZWVknLFxuICAgICAgICBMTCAgIDogJ01NTU0gRCwgWVlZWScsXG4gICAgICAgIExMTCAgOiAnTU1NTSBELCBZWVlZIGg6bW0gQScsXG4gICAgICAgIExMTEwgOiAnZGRkZCwgTU1NTSBELCBZWVlZIGg6bW0gQSdcbiAgICB9O1xuXG4gICAgZnVuY3Rpb24gbG9uZ0RhdGVGb3JtYXQgKGtleSkge1xuICAgICAgICB2YXIgZm9ybWF0ID0gdGhpcy5fbG9uZ0RhdGVGb3JtYXRba2V5XSxcbiAgICAgICAgICAgIGZvcm1hdFVwcGVyID0gdGhpcy5fbG9uZ0RhdGVGb3JtYXRba2V5LnRvVXBwZXJDYXNlKCldO1xuXG4gICAgICAgIGlmIChmb3JtYXQgfHwgIWZvcm1hdFVwcGVyKSB7XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0O1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbG9uZ0RhdGVGb3JtYXRba2V5XSA9IGZvcm1hdFVwcGVyLnJlcGxhY2UoL01NTU18TU18RER8ZGRkZC9nLCBmdW5jdGlvbiAodmFsKSB7XG4gICAgICAgICAgICByZXR1cm4gdmFsLnNsaWNlKDEpO1xuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gdGhpcy5fbG9uZ0RhdGVGb3JtYXRba2V5XTtcbiAgICB9XG5cbiAgICB2YXIgZGVmYXVsdEludmFsaWREYXRlID0gJ0ludmFsaWQgZGF0ZSc7XG5cbiAgICBmdW5jdGlvbiBpbnZhbGlkRGF0ZSAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9pbnZhbGlkRGF0ZTtcbiAgICB9XG5cbiAgICB2YXIgZGVmYXVsdE9yZGluYWwgPSAnJWQnO1xuICAgIHZhciBkZWZhdWx0T3JkaW5hbFBhcnNlID0gL1xcZHsxLDJ9LztcblxuICAgIGZ1bmN0aW9uIG9yZGluYWwgKG51bWJlcikge1xuICAgICAgICByZXR1cm4gdGhpcy5fb3JkaW5hbC5yZXBsYWNlKCclZCcsIG51bWJlcik7XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRSZWxhdGl2ZVRpbWUgPSB7XG4gICAgICAgIGZ1dHVyZSA6ICdpbiAlcycsXG4gICAgICAgIHBhc3QgICA6ICclcyBhZ28nLFxuICAgICAgICBzICA6ICdhIGZldyBzZWNvbmRzJyxcbiAgICAgICAgbSAgOiAnYSBtaW51dGUnLFxuICAgICAgICBtbSA6ICclZCBtaW51dGVzJyxcbiAgICAgICAgaCAgOiAnYW4gaG91cicsXG4gICAgICAgIGhoIDogJyVkIGhvdXJzJyxcbiAgICAgICAgZCAgOiAnYSBkYXknLFxuICAgICAgICBkZCA6ICclZCBkYXlzJyxcbiAgICAgICAgTSAgOiAnYSBtb250aCcsXG4gICAgICAgIE1NIDogJyVkIG1vbnRocycsXG4gICAgICAgIHkgIDogJ2EgeWVhcicsXG4gICAgICAgIHl5IDogJyVkIHllYXJzJ1xuICAgIH07XG5cbiAgICBmdW5jdGlvbiByZWxhdGl2ZV9fcmVsYXRpdmVUaW1lIChudW1iZXIsIHdpdGhvdXRTdWZmaXgsIHN0cmluZywgaXNGdXR1cmUpIHtcbiAgICAgICAgdmFyIG91dHB1dCA9IHRoaXMuX3JlbGF0aXZlVGltZVtzdHJpbmddO1xuICAgICAgICByZXR1cm4gKGlzRnVuY3Rpb24ob3V0cHV0KSkgP1xuICAgICAgICAgICAgb3V0cHV0KG51bWJlciwgd2l0aG91dFN1ZmZpeCwgc3RyaW5nLCBpc0Z1dHVyZSkgOlxuICAgICAgICAgICAgb3V0cHV0LnJlcGxhY2UoLyVkL2ksIG51bWJlcik7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcGFzdEZ1dHVyZSAoZGlmZiwgb3V0cHV0KSB7XG4gICAgICAgIHZhciBmb3JtYXQgPSB0aGlzLl9yZWxhdGl2ZVRpbWVbZGlmZiA+IDAgPyAnZnV0dXJlJyA6ICdwYXN0J107XG4gICAgICAgIHJldHVybiBpc0Z1bmN0aW9uKGZvcm1hdCkgPyBmb3JtYXQob3V0cHV0KSA6IGZvcm1hdC5yZXBsYWNlKC8lcy9pLCBvdXRwdXQpO1xuICAgIH1cblxuICAgIHZhciBhbGlhc2VzID0ge307XG5cbiAgICBmdW5jdGlvbiBhZGRVbml0QWxpYXMgKHVuaXQsIHNob3J0aGFuZCkge1xuICAgICAgICB2YXIgbG93ZXJDYXNlID0gdW5pdC50b0xvd2VyQ2FzZSgpO1xuICAgICAgICBhbGlhc2VzW2xvd2VyQ2FzZV0gPSBhbGlhc2VzW2xvd2VyQ2FzZSArICdzJ10gPSBhbGlhc2VzW3Nob3J0aGFuZF0gPSB1bml0O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIG5vcm1hbGl6ZVVuaXRzKHVuaXRzKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgdW5pdHMgPT09ICdzdHJpbmcnID8gYWxpYXNlc1t1bml0c10gfHwgYWxpYXNlc1t1bml0cy50b0xvd2VyQ2FzZSgpXSA6IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBub3JtYWxpemVPYmplY3RVbml0cyhpbnB1dE9iamVjdCkge1xuICAgICAgICB2YXIgbm9ybWFsaXplZElucHV0ID0ge30sXG4gICAgICAgICAgICBub3JtYWxpemVkUHJvcCxcbiAgICAgICAgICAgIHByb3A7XG5cbiAgICAgICAgZm9yIChwcm9wIGluIGlucHV0T2JqZWN0KSB7XG4gICAgICAgICAgICBpZiAoaGFzT3duUHJvcChpbnB1dE9iamVjdCwgcHJvcCkpIHtcbiAgICAgICAgICAgICAgICBub3JtYWxpemVkUHJvcCA9IG5vcm1hbGl6ZVVuaXRzKHByb3ApO1xuICAgICAgICAgICAgICAgIGlmIChub3JtYWxpemVkUHJvcCkge1xuICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkSW5wdXRbbm9ybWFsaXplZFByb3BdID0gaW5wdXRPYmplY3RbcHJvcF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZWRJbnB1dDtcbiAgICB9XG5cbiAgICB2YXIgcHJpb3JpdGllcyA9IHt9O1xuXG4gICAgZnVuY3Rpb24gYWRkVW5pdFByaW9yaXR5KHVuaXQsIHByaW9yaXR5KSB7XG4gICAgICAgIHByaW9yaXRpZXNbdW5pdF0gPSBwcmlvcml0eTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRQcmlvcml0aXplZFVuaXRzKHVuaXRzT2JqKSB7XG4gICAgICAgIHZhciB1bml0cyA9IFtdO1xuICAgICAgICBmb3IgKHZhciB1IGluIHVuaXRzT2JqKSB7XG4gICAgICAgICAgICB1bml0cy5wdXNoKHt1bml0OiB1LCBwcmlvcml0eTogcHJpb3JpdGllc1t1XX0pO1xuICAgICAgICB9XG4gICAgICAgIHVuaXRzLnNvcnQoZnVuY3Rpb24gKGEsIGIpIHtcbiAgICAgICAgICAgIHJldHVybiBhLnByaW9yaXR5IC0gYi5wcmlvcml0eTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB1bml0cztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBtYWtlR2V0U2V0ICh1bml0LCBrZWVwVGltZSkge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICBpZiAodmFsdWUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGdldF9zZXRfX3NldCh0aGlzLCB1bml0LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLnVwZGF0ZU9mZnNldCh0aGlzLCBrZWVwVGltZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiBnZXRfc2V0X19nZXQodGhpcywgdW5pdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0X3NldF9fZ2V0IChtb20sIHVuaXQpIHtcbiAgICAgICAgcmV0dXJuIG1vbS5pc1ZhbGlkKCkgP1xuICAgICAgICAgICAgbW9tLl9kWydnZXQnICsgKG1vbS5faXNVVEMgPyAnVVRDJyA6ICcnKSArIHVuaXRdKCkgOiBOYU47XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0X3NldF9fc2V0IChtb20sIHVuaXQsIHZhbHVlKSB7XG4gICAgICAgIGlmIChtb20uaXNWYWxpZCgpKSB7XG4gICAgICAgICAgICBtb20uX2RbJ3NldCcgKyAobW9tLl9pc1VUQyA/ICdVVEMnIDogJycpICsgdW5pdF0odmFsdWUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgZnVuY3Rpb24gc3RyaW5nR2V0ICh1bml0cykge1xuICAgICAgICB1bml0cyA9IG5vcm1hbGl6ZVVuaXRzKHVuaXRzKTtcbiAgICAgICAgaWYgKGlzRnVuY3Rpb24odGhpc1t1bml0c10pKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpc1t1bml0c10oKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cblxuICAgIGZ1bmN0aW9uIHN0cmluZ1NldCAodW5pdHMsIHZhbHVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgdW5pdHMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICB1bml0cyA9IG5vcm1hbGl6ZU9iamVjdFVuaXRzKHVuaXRzKTtcbiAgICAgICAgICAgIHZhciBwcmlvcml0aXplZCA9IGdldFByaW9yaXRpemVkVW5pdHModW5pdHMpO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwcmlvcml0aXplZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHRoaXNbcHJpb3JpdGl6ZWRbaV0udW5pdF0odW5pdHNbcHJpb3JpdGl6ZWRbaV0udW5pdF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdW5pdHMgPSBub3JtYWxpemVVbml0cyh1bml0cyk7XG4gICAgICAgICAgICBpZiAoaXNGdW5jdGlvbih0aGlzW3VuaXRzXSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpc1t1bml0c10odmFsdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHplcm9GaWxsKG51bWJlciwgdGFyZ2V0TGVuZ3RoLCBmb3JjZVNpZ24pIHtcbiAgICAgICAgdmFyIGFic051bWJlciA9ICcnICsgTWF0aC5hYnMobnVtYmVyKSxcbiAgICAgICAgICAgIHplcm9zVG9GaWxsID0gdGFyZ2V0TGVuZ3RoIC0gYWJzTnVtYmVyLmxlbmd0aCxcbiAgICAgICAgICAgIHNpZ24gPSBudW1iZXIgPj0gMDtcbiAgICAgICAgcmV0dXJuIChzaWduID8gKGZvcmNlU2lnbiA/ICcrJyA6ICcnKSA6ICctJykgK1xuICAgICAgICAgICAgTWF0aC5wb3coMTAsIE1hdGgubWF4KDAsIHplcm9zVG9GaWxsKSkudG9TdHJpbmcoKS5zdWJzdHIoMSkgKyBhYnNOdW1iZXI7XG4gICAgfVxuXG4gICAgdmFyIGZvcm1hdHRpbmdUb2tlbnMgPSAvKFxcW1teXFxbXSpcXF0pfChcXFxcKT8oW0hoXW1tKHNzKT98TW98TU0/TT9NP3xEb3xERERvfEREP0Q/RD98ZGRkP2Q/fGRvP3x3W298d10/fFdbb3xXXT98UW8/fFlZWVlZWXxZWVlZWXxZWVlZfFlZfGdnKGdnZz8pP3xHRyhHR0c/KT98ZXxFfGF8QXxoaD98SEg/fGtrP3xtbT98c3M/fFN7MSw5fXx4fFh8eno/fFpaP3wuKS9nO1xuXG4gICAgdmFyIGxvY2FsRm9ybWF0dGluZ1Rva2VucyA9IC8oXFxbW15cXFtdKlxcXSl8KFxcXFwpPyhMVFN8TFR8TEw/TD9MP3xsezEsNH0pL2c7XG5cbiAgICB2YXIgZm9ybWF0RnVuY3Rpb25zID0ge307XG5cbiAgICB2YXIgZm9ybWF0VG9rZW5GdW5jdGlvbnMgPSB7fTtcblxuICAgIC8vIHRva2VuOiAgICAnTSdcbiAgICAvLyBwYWRkZWQ6ICAgWydNTScsIDJdXG4gICAgLy8gb3JkaW5hbDogICdNbydcbiAgICAvLyBjYWxsYmFjazogZnVuY3Rpb24gKCkgeyB0aGlzLm1vbnRoKCkgKyAxIH1cbiAgICBmdW5jdGlvbiBhZGRGb3JtYXRUb2tlbiAodG9rZW4sIHBhZGRlZCwgb3JkaW5hbCwgY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGZ1bmMgPSBjYWxsYmFjaztcbiAgICAgICAgaWYgKHR5cGVvZiBjYWxsYmFjayA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIGZ1bmMgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGJhY2tdKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIGlmICh0b2tlbikge1xuICAgICAgICAgICAgZm9ybWF0VG9rZW5GdW5jdGlvbnNbdG9rZW5dID0gZnVuYztcbiAgICAgICAgfVxuICAgICAgICBpZiAocGFkZGVkKSB7XG4gICAgICAgICAgICBmb3JtYXRUb2tlbkZ1bmN0aW9uc1twYWRkZWRbMF1dID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB6ZXJvRmlsbChmdW5jLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyksIHBhZGRlZFsxXSwgcGFkZGVkWzJdKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9yZGluYWwpIHtcbiAgICAgICAgICAgIGZvcm1hdFRva2VuRnVuY3Rpb25zW29yZGluYWxdID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmxvY2FsZURhdGEoKS5vcmRpbmFsKGZ1bmMuYXBwbHkodGhpcywgYXJndW1lbnRzKSwgdG9rZW4pO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlbW92ZUZvcm1hdHRpbmdUb2tlbnMoaW5wdXQpIHtcbiAgICAgICAgaWYgKGlucHV0Lm1hdGNoKC9cXFtbXFxzXFxTXS8pKSB7XG4gICAgICAgICAgICByZXR1cm4gaW5wdXQucmVwbGFjZSgvXlxcW3xcXF0kL2csICcnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaW5wdXQucmVwbGFjZSgvXFxcXC9nLCAnJyk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbWFrZUZvcm1hdEZ1bmN0aW9uKGZvcm1hdCkge1xuICAgICAgICB2YXIgYXJyYXkgPSBmb3JtYXQubWF0Y2goZm9ybWF0dGluZ1Rva2VucyksIGksIGxlbmd0aDtcblxuICAgICAgICBmb3IgKGkgPSAwLCBsZW5ndGggPSBhcnJheS5sZW5ndGg7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKGZvcm1hdFRva2VuRnVuY3Rpb25zW2FycmF5W2ldXSkge1xuICAgICAgICAgICAgICAgIGFycmF5W2ldID0gZm9ybWF0VG9rZW5GdW5jdGlvbnNbYXJyYXlbaV1dO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBhcnJheVtpXSA9IHJlbW92ZUZvcm1hdHRpbmdUb2tlbnMoYXJyYXlbaV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uIChtb20pIHtcbiAgICAgICAgICAgIHZhciBvdXRwdXQgPSAnJywgaTtcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIG91dHB1dCArPSBhcnJheVtpXSBpbnN0YW5jZW9mIEZ1bmN0aW9uID8gYXJyYXlbaV0uY2FsbChtb20sIGZvcm1hdCkgOiBhcnJheVtpXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBvdXRwdXQ7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gZm9ybWF0IGRhdGUgdXNpbmcgbmF0aXZlIGRhdGUgb2JqZWN0XG4gICAgZnVuY3Rpb24gZm9ybWF0TW9tZW50KG0sIGZvcm1hdCkge1xuICAgICAgICBpZiAoIW0uaXNWYWxpZCgpKSB7XG4gICAgICAgICAgICByZXR1cm4gbS5sb2NhbGVEYXRhKCkuaW52YWxpZERhdGUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvcm1hdCA9IGV4cGFuZEZvcm1hdChmb3JtYXQsIG0ubG9jYWxlRGF0YSgpKTtcbiAgICAgICAgZm9ybWF0RnVuY3Rpb25zW2Zvcm1hdF0gPSBmb3JtYXRGdW5jdGlvbnNbZm9ybWF0XSB8fCBtYWtlRm9ybWF0RnVuY3Rpb24oZm9ybWF0KTtcblxuICAgICAgICByZXR1cm4gZm9ybWF0RnVuY3Rpb25zW2Zvcm1hdF0obSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZXhwYW5kRm9ybWF0KGZvcm1hdCwgbG9jYWxlKSB7XG4gICAgICAgIHZhciBpID0gNTtcblxuICAgICAgICBmdW5jdGlvbiByZXBsYWNlTG9uZ0RhdGVGb3JtYXRUb2tlbnMoaW5wdXQpIHtcbiAgICAgICAgICAgIHJldHVybiBsb2NhbGUubG9uZ0RhdGVGb3JtYXQoaW5wdXQpIHx8IGlucHV0O1xuICAgICAgICB9XG5cbiAgICAgICAgbG9jYWxGb3JtYXR0aW5nVG9rZW5zLmxhc3RJbmRleCA9IDA7XG4gICAgICAgIHdoaWxlIChpID49IDAgJiYgbG9jYWxGb3JtYXR0aW5nVG9rZW5zLnRlc3QoZm9ybWF0KSkge1xuICAgICAgICAgICAgZm9ybWF0ID0gZm9ybWF0LnJlcGxhY2UobG9jYWxGb3JtYXR0aW5nVG9rZW5zLCByZXBsYWNlTG9uZ0RhdGVGb3JtYXRUb2tlbnMpO1xuICAgICAgICAgICAgbG9jYWxGb3JtYXR0aW5nVG9rZW5zLmxhc3RJbmRleCA9IDA7XG4gICAgICAgICAgICBpIC09IDE7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZm9ybWF0O1xuICAgIH1cblxuICAgIHZhciBtYXRjaDEgICAgICAgICA9IC9cXGQvOyAgICAgICAgICAgIC8vICAgICAgIDAgLSA5XG4gICAgdmFyIG1hdGNoMiAgICAgICAgID0gL1xcZFxcZC87ICAgICAgICAgIC8vICAgICAgMDAgLSA5OVxuICAgIHZhciBtYXRjaDMgICAgICAgICA9IC9cXGR7M30vOyAgICAgICAgIC8vICAgICAwMDAgLSA5OTlcbiAgICB2YXIgbWF0Y2g0ICAgICAgICAgPSAvXFxkezR9LzsgICAgICAgICAvLyAgICAwMDAwIC0gOTk5OVxuICAgIHZhciBtYXRjaDYgICAgICAgICA9IC9bKy1dP1xcZHs2fS87ICAgIC8vIC05OTk5OTkgLSA5OTk5OTlcbiAgICB2YXIgbWF0Y2gxdG8yICAgICAgPSAvXFxkXFxkPy87ICAgICAgICAgLy8gICAgICAgMCAtIDk5XG4gICAgdmFyIG1hdGNoM3RvNCAgICAgID0gL1xcZFxcZFxcZFxcZD8vOyAgICAgLy8gICAgIDk5OSAtIDk5OTlcbiAgICB2YXIgbWF0Y2g1dG82ICAgICAgPSAvXFxkXFxkXFxkXFxkXFxkXFxkPy87IC8vICAgOTk5OTkgLSA5OTk5OTlcbiAgICB2YXIgbWF0Y2gxdG8zICAgICAgPSAvXFxkezEsM30vOyAgICAgICAvLyAgICAgICAwIC0gOTk5XG4gICAgdmFyIG1hdGNoMXRvNCAgICAgID0gL1xcZHsxLDR9LzsgICAgICAgLy8gICAgICAgMCAtIDk5OTlcbiAgICB2YXIgbWF0Y2gxdG82ICAgICAgPSAvWystXT9cXGR7MSw2fS87ICAvLyAtOTk5OTk5IC0gOTk5OTk5XG5cbiAgICB2YXIgbWF0Y2hVbnNpZ25lZCAgPSAvXFxkKy87ICAgICAgICAgICAvLyAgICAgICAwIC0gaW5mXG4gICAgdmFyIG1hdGNoU2lnbmVkICAgID0gL1srLV0/XFxkKy87ICAgICAgLy8gICAgLWluZiAtIGluZlxuXG4gICAgdmFyIG1hdGNoT2Zmc2V0ICAgID0gL1p8WystXVxcZFxcZDo/XFxkXFxkL2dpOyAvLyArMDA6MDAgLTAwOjAwICswMDAwIC0wMDAwIG9yIFpcbiAgICB2YXIgbWF0Y2hTaG9ydE9mZnNldCA9IC9afFsrLV1cXGRcXGQoPzo6P1xcZFxcZCk/L2dpOyAvLyArMDAgLTAwICswMDowMCAtMDA6MDAgKzAwMDAgLTAwMDAgb3IgWlxuXG4gICAgdmFyIG1hdGNoVGltZXN0YW1wID0gL1srLV0/XFxkKyhcXC5cXGR7MSwzfSk/LzsgLy8gMTIzNDU2Nzg5IDEyMzQ1Njc4OS4xMjNcblxuICAgIC8vIGFueSB3b3JkIChvciB0d28pIGNoYXJhY3RlcnMgb3IgbnVtYmVycyBpbmNsdWRpbmcgdHdvL3RocmVlIHdvcmQgbW9udGggaW4gYXJhYmljLlxuICAgIC8vIGluY2x1ZGVzIHNjb3R0aXNoIGdhZWxpYyB0d28gd29yZCBhbmQgaHlwaGVuYXRlZCBtb250aHNcbiAgICB2YXIgbWF0Y2hXb3JkID0gL1swLTldKlsnYS16XFx1MDBBMC1cXHUwNUZGXFx1MDcwMC1cXHVEN0ZGXFx1RjkwMC1cXHVGRENGXFx1RkRGMC1cXHVGRkVGXSt8W1xcdTA2MDAtXFx1MDZGRlxcL10rKFxccyo/W1xcdTA2MDAtXFx1MDZGRl0rKXsxLDJ9L2k7XG5cblxuICAgIHZhciByZWdleGVzID0ge307XG5cbiAgICBmdW5jdGlvbiBhZGRSZWdleFRva2VuICh0b2tlbiwgcmVnZXgsIHN0cmljdFJlZ2V4KSB7XG4gICAgICAgIHJlZ2V4ZXNbdG9rZW5dID0gaXNGdW5jdGlvbihyZWdleCkgPyByZWdleCA6IGZ1bmN0aW9uIChpc1N0cmljdCwgbG9jYWxlRGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIChpc1N0cmljdCAmJiBzdHJpY3RSZWdleCkgPyBzdHJpY3RSZWdleCA6IHJlZ2V4O1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFBhcnNlUmVnZXhGb3JUb2tlbiAodG9rZW4sIGNvbmZpZykge1xuICAgICAgICBpZiAoIWhhc093blByb3AocmVnZXhlcywgdG9rZW4pKSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IFJlZ0V4cCh1bmVzY2FwZUZvcm1hdCh0b2tlbikpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlZ2V4ZXNbdG9rZW5dKGNvbmZpZy5fc3RyaWN0LCBjb25maWcuX2xvY2FsZSk7XG4gICAgfVxuXG4gICAgLy8gQ29kZSBmcm9tIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzU2MTQ5My9pcy10aGVyZS1hLXJlZ2V4cC1lc2NhcGUtZnVuY3Rpb24taW4tamF2YXNjcmlwdFxuICAgIGZ1bmN0aW9uIHVuZXNjYXBlRm9ybWF0KHMpIHtcbiAgICAgICAgcmV0dXJuIHJlZ2V4RXNjYXBlKHMucmVwbGFjZSgnXFxcXCcsICcnKS5yZXBsYWNlKC9cXFxcKFxcWyl8XFxcXChcXF0pfFxcWyhbXlxcXVxcW10qKVxcXXxcXFxcKC4pL2csIGZ1bmN0aW9uIChtYXRjaGVkLCBwMSwgcDIsIHAzLCBwNCkge1xuICAgICAgICAgICAgcmV0dXJuIHAxIHx8IHAyIHx8IHAzIHx8IHA0O1xuICAgICAgICB9KSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcmVnZXhFc2NhcGUocykge1xuICAgICAgICByZXR1cm4gcy5yZXBsYWNlKC9bLVxcL1xcXFxeJCorPy4oKXxbXFxde31dL2csICdcXFxcJCYnKTtcbiAgICB9XG5cbiAgICB2YXIgdG9rZW5zID0ge307XG5cbiAgICBmdW5jdGlvbiBhZGRQYXJzZVRva2VuICh0b2tlbiwgY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGksIGZ1bmMgPSBjYWxsYmFjaztcbiAgICAgICAgaWYgKHR5cGVvZiB0b2tlbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHRva2VuID0gW3Rva2VuXTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodHlwZW9mIGNhbGxiYWNrID09PSAnbnVtYmVyJykge1xuICAgICAgICAgICAgZnVuYyA9IGZ1bmN0aW9uIChpbnB1dCwgYXJyYXkpIHtcbiAgICAgICAgICAgICAgICBhcnJheVtjYWxsYmFja10gPSB0b0ludChpbnB1dCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCB0b2tlbi5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdG9rZW5zW3Rva2VuW2ldXSA9IGZ1bmM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBhZGRXZWVrUGFyc2VUb2tlbiAodG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgICAgIGFkZFBhcnNlVG9rZW4odG9rZW4sIGZ1bmN0aW9uIChpbnB1dCwgYXJyYXksIGNvbmZpZywgdG9rZW4pIHtcbiAgICAgICAgICAgIGNvbmZpZy5fdyA9IGNvbmZpZy5fdyB8fCB7fTtcbiAgICAgICAgICAgIGNhbGxiYWNrKGlucHV0LCBjb25maWcuX3csIGNvbmZpZywgdG9rZW4pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBhZGRUaW1lVG9BcnJheUZyb21Ub2tlbih0b2tlbiwgaW5wdXQsIGNvbmZpZykge1xuICAgICAgICBpZiAoaW5wdXQgIT0gbnVsbCAmJiBoYXNPd25Qcm9wKHRva2VucywgdG9rZW4pKSB7XG4gICAgICAgICAgICB0b2tlbnNbdG9rZW5dKGlucHV0LCBjb25maWcuX2EsIGNvbmZpZywgdG9rZW4pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdmFyIFlFQVIgPSAwO1xuICAgIHZhciBNT05USCA9IDE7XG4gICAgdmFyIERBVEUgPSAyO1xuICAgIHZhciBIT1VSID0gMztcbiAgICB2YXIgTUlOVVRFID0gNDtcbiAgICB2YXIgU0VDT05EID0gNTtcbiAgICB2YXIgTUlMTElTRUNPTkQgPSA2O1xuICAgIHZhciBXRUVLID0gNztcbiAgICB2YXIgV0VFS0RBWSA9IDg7XG5cbiAgICB2YXIgaW5kZXhPZjtcblxuICAgIGlmIChBcnJheS5wcm90b3R5cGUuaW5kZXhPZikge1xuICAgICAgICBpbmRleE9mID0gQXJyYXkucHJvdG90eXBlLmluZGV4T2Y7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgaW5kZXhPZiA9IGZ1bmN0aW9uIChvKSB7XG4gICAgICAgICAgICAvLyBJIGtub3dcbiAgICAgICAgICAgIHZhciBpO1xuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHRoaXMubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpc1tpXSA9PT0gbykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gLTE7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZGF5c0luTW9udGgoeWVhciwgbW9udGgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKERhdGUuVVRDKHllYXIsIG1vbnRoICsgMSwgMCkpLmdldFVUQ0RhdGUoKTtcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbignTScsIFsnTU0nLCAyXSwgJ01vJywgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tb250aCgpICsgMTtcbiAgICB9KTtcblxuICAgIGFkZEZvcm1hdFRva2VuKCdNTU0nLCAwLCAwLCBmdW5jdGlvbiAoZm9ybWF0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvY2FsZURhdGEoKS5tb250aHNTaG9ydCh0aGlzLCBmb3JtYXQpO1xuICAgIH0pO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ01NTU0nLCAwLCAwLCBmdW5jdGlvbiAoZm9ybWF0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvY2FsZURhdGEoKS5tb250aHModGhpcywgZm9ybWF0KTtcbiAgICB9KTtcblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnbW9udGgnLCAnTScpO1xuXG4gICAgLy8gUFJJT1JJVFlcblxuICAgIGFkZFVuaXRQcmlvcml0eSgnbW9udGgnLCA4KTtcblxuICAgIC8vIFBBUlNJTkdcblxuICAgIGFkZFJlZ2V4VG9rZW4oJ00nLCAgICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ01NJywgICBtYXRjaDF0bzIsIG1hdGNoMik7XG4gICAgYWRkUmVnZXhUb2tlbignTU1NJywgIGZ1bmN0aW9uIChpc1N0cmljdCwgbG9jYWxlKSB7XG4gICAgICAgIHJldHVybiBsb2NhbGUubW9udGhzU2hvcnRSZWdleChpc1N0cmljdCk7XG4gICAgfSk7XG4gICAgYWRkUmVnZXhUb2tlbignTU1NTScsIGZ1bmN0aW9uIChpc1N0cmljdCwgbG9jYWxlKSB7XG4gICAgICAgIHJldHVybiBsb2NhbGUubW9udGhzUmVnZXgoaXNTdHJpY3QpO1xuICAgIH0pO1xuXG4gICAgYWRkUGFyc2VUb2tlbihbJ00nLCAnTU0nXSwgZnVuY3Rpb24gKGlucHV0LCBhcnJheSkge1xuICAgICAgICBhcnJheVtNT05USF0gPSB0b0ludChpbnB1dCkgLSAxO1xuICAgIH0pO1xuXG4gICAgYWRkUGFyc2VUb2tlbihbJ01NTScsICdNTU1NJ10sIGZ1bmN0aW9uIChpbnB1dCwgYXJyYXksIGNvbmZpZywgdG9rZW4pIHtcbiAgICAgICAgdmFyIG1vbnRoID0gY29uZmlnLl9sb2NhbGUubW9udGhzUGFyc2UoaW5wdXQsIHRva2VuLCBjb25maWcuX3N0cmljdCk7XG4gICAgICAgIC8vIGlmIHdlIGRpZG4ndCBmaW5kIGEgbW9udGggbmFtZSwgbWFyayB0aGUgZGF0ZSBhcyBpbnZhbGlkLlxuICAgICAgICBpZiAobW9udGggIT0gbnVsbCkge1xuICAgICAgICAgICAgYXJyYXlbTU9OVEhdID0gbW9udGg7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5pbnZhbGlkTW9udGggPSBpbnB1dDtcbiAgICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gTE9DQUxFU1xuXG4gICAgdmFyIE1PTlRIU19JTl9GT1JNQVQgPSAvRFtvRF0/KFxcW1teXFxbXFxdXSpcXF18XFxzKykrTU1NTT8vO1xuICAgIHZhciBkZWZhdWx0TG9jYWxlTW9udGhzID0gJ0phbnVhcnlfRmVicnVhcnlfTWFyY2hfQXByaWxfTWF5X0p1bmVfSnVseV9BdWd1c3RfU2VwdGVtYmVyX09jdG9iZXJfTm92ZW1iZXJfRGVjZW1iZXInLnNwbGl0KCdfJyk7XG4gICAgZnVuY3Rpb24gbG9jYWxlTW9udGhzIChtLCBmb3JtYXQpIHtcbiAgICAgICAgcmV0dXJuIGlzQXJyYXkodGhpcy5fbW9udGhzKSA/IHRoaXMuX21vbnRoc1ttLm1vbnRoKCldIDpcbiAgICAgICAgICAgIHRoaXMuX21vbnRoc1sodGhpcy5fbW9udGhzLmlzRm9ybWF0IHx8IE1PTlRIU19JTl9GT1JNQVQpLnRlc3QoZm9ybWF0KSA/ICdmb3JtYXQnIDogJ3N0YW5kYWxvbmUnXVttLm1vbnRoKCldO1xuICAgIH1cblxuICAgIHZhciBkZWZhdWx0TG9jYWxlTW9udGhzU2hvcnQgPSAnSmFuX0ZlYl9NYXJfQXByX01heV9KdW5fSnVsX0F1Z19TZXBfT2N0X05vdl9EZWMnLnNwbGl0KCdfJyk7XG4gICAgZnVuY3Rpb24gbG9jYWxlTW9udGhzU2hvcnQgKG0sIGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gaXNBcnJheSh0aGlzLl9tb250aHNTaG9ydCkgPyB0aGlzLl9tb250aHNTaG9ydFttLm1vbnRoKCldIDpcbiAgICAgICAgICAgIHRoaXMuX21vbnRoc1Nob3J0W01PTlRIU19JTl9GT1JNQVQudGVzdChmb3JtYXQpID8gJ2Zvcm1hdCcgOiAnc3RhbmRhbG9uZSddW20ubW9udGgoKV07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdW5pdHNfbW9udGhfX2hhbmRsZVN0cmljdFBhcnNlKG1vbnRoTmFtZSwgZm9ybWF0LCBzdHJpY3QpIHtcbiAgICAgICAgdmFyIGksIGlpLCBtb20sIGxsYyA9IG1vbnRoTmFtZS50b0xvY2FsZUxvd2VyQ2FzZSgpO1xuICAgICAgICBpZiAoIXRoaXMuX21vbnRoc1BhcnNlKSB7XG4gICAgICAgICAgICAvLyB0aGlzIGlzIG5vdCB1c2VkXG4gICAgICAgICAgICB0aGlzLl9tb250aHNQYXJzZSA9IFtdO1xuICAgICAgICAgICAgdGhpcy5fbG9uZ01vbnRoc1BhcnNlID0gW107XG4gICAgICAgICAgICB0aGlzLl9zaG9ydE1vbnRoc1BhcnNlID0gW107XG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgMTI7ICsraSkge1xuICAgICAgICAgICAgICAgIG1vbSA9IGNyZWF0ZV91dGNfX2NyZWF0ZVVUQyhbMjAwMCwgaV0pO1xuICAgICAgICAgICAgICAgIHRoaXMuX3Nob3J0TW9udGhzUGFyc2VbaV0gPSB0aGlzLm1vbnRoc1Nob3J0KG1vbSwgJycpLnRvTG9jYWxlTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5fbG9uZ01vbnRoc1BhcnNlW2ldID0gdGhpcy5tb250aHMobW9tLCAnJykudG9Mb2NhbGVMb3dlckNhc2UoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChzdHJpY3QpIHtcbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT09ICdNTU0nKSB7XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fc2hvcnRNb250aHNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWkgIT09IC0xID8gaWkgOiBudWxsO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpaSA9IGluZGV4T2YuY2FsbCh0aGlzLl9sb25nTW9udGhzUGFyc2UsIGxsYyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGlpICE9PSAtMSA/IGlpIDogbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT09ICdNTU0nKSB7XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fc2hvcnRNb250aHNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICBpZiAoaWkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpaTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fbG9uZ01vbnRoc1BhcnNlLCBsbGMpO1xuICAgICAgICAgICAgICAgIHJldHVybiBpaSAhPT0gLTEgPyBpaSA6IG51bGw7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX2xvbmdNb250aHNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICBpZiAoaWkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpaTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fc2hvcnRNb250aHNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWkgIT09IC0xID8gaWkgOiBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbG9jYWxlTW9udGhzUGFyc2UgKG1vbnRoTmFtZSwgZm9ybWF0LCBzdHJpY3QpIHtcbiAgICAgICAgdmFyIGksIG1vbSwgcmVnZXg7XG5cbiAgICAgICAgaWYgKHRoaXMuX21vbnRoc1BhcnNlRXhhY3QpIHtcbiAgICAgICAgICAgIHJldHVybiB1bml0c19tb250aF9faGFuZGxlU3RyaWN0UGFyc2UuY2FsbCh0aGlzLCBtb250aE5hbWUsIGZvcm1hdCwgc3RyaWN0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy5fbW9udGhzUGFyc2UpIHtcbiAgICAgICAgICAgIHRoaXMuX21vbnRoc1BhcnNlID0gW107XG4gICAgICAgICAgICB0aGlzLl9sb25nTW9udGhzUGFyc2UgPSBbXTtcbiAgICAgICAgICAgIHRoaXMuX3Nob3J0TW9udGhzUGFyc2UgPSBbXTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRPRE86IGFkZCBzb3J0aW5nXG4gICAgICAgIC8vIFNvcnRpbmcgbWFrZXMgc3VyZSBpZiBvbmUgbW9udGggKG9yIGFiYnIpIGlzIGEgcHJlZml4IG9mIGFub3RoZXJcbiAgICAgICAgLy8gc2VlIHNvcnRpbmcgaW4gY29tcHV0ZU1vbnRoc1BhcnNlXG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCAxMjsgaSsrKSB7XG4gICAgICAgICAgICAvLyBtYWtlIHRoZSByZWdleCBpZiB3ZSBkb24ndCBoYXZlIGl0IGFscmVhZHlcbiAgICAgICAgICAgIG1vbSA9IGNyZWF0ZV91dGNfX2NyZWF0ZVVUQyhbMjAwMCwgaV0pO1xuICAgICAgICAgICAgaWYgKHN0cmljdCAmJiAhdGhpcy5fbG9uZ01vbnRoc1BhcnNlW2ldKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fbG9uZ01vbnRoc1BhcnNlW2ldID0gbmV3IFJlZ0V4cCgnXicgKyB0aGlzLm1vbnRocyhtb20sICcnKS5yZXBsYWNlKCcuJywgJycpICsgJyQnLCAnaScpO1xuICAgICAgICAgICAgICAgIHRoaXMuX3Nob3J0TW9udGhzUGFyc2VbaV0gPSBuZXcgUmVnRXhwKCdeJyArIHRoaXMubW9udGhzU2hvcnQobW9tLCAnJykucmVwbGFjZSgnLicsICcnKSArICckJywgJ2knKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghc3RyaWN0ICYmICF0aGlzLl9tb250aHNQYXJzZVtpXSkge1xuICAgICAgICAgICAgICAgIHJlZ2V4ID0gJ14nICsgdGhpcy5tb250aHMobW9tLCAnJykgKyAnfF4nICsgdGhpcy5tb250aHNTaG9ydChtb20sICcnKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9tb250aHNQYXJzZVtpXSA9IG5ldyBSZWdFeHAocmVnZXgucmVwbGFjZSgnLicsICcnKSwgJ2knKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIHRlc3QgdGhlIHJlZ2V4XG4gICAgICAgICAgICBpZiAoc3RyaWN0ICYmIGZvcm1hdCA9PT0gJ01NTU0nICYmIHRoaXMuX2xvbmdNb250aHNQYXJzZVtpXS50ZXN0KG1vbnRoTmFtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoc3RyaWN0ICYmIGZvcm1hdCA9PT0gJ01NTScgJiYgdGhpcy5fc2hvcnRNb250aHNQYXJzZVtpXS50ZXN0KG1vbnRoTmFtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIXN0cmljdCAmJiB0aGlzLl9tb250aHNQYXJzZVtpXS50ZXN0KG1vbnRoTmFtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIE1PTUVOVFNcblxuICAgIGZ1bmN0aW9uIHNldE1vbnRoIChtb20sIHZhbHVlKSB7XG4gICAgICAgIHZhciBkYXlPZk1vbnRoO1xuXG4gICAgICAgIGlmICghbW9tLmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgLy8gTm8gb3BcbiAgICAgICAgICAgIHJldHVybiBtb207XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgaWYgKC9eXFxkKyQvLnRlc3QodmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgdmFsdWUgPSB0b0ludCh2YWx1ZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHZhbHVlID0gbW9tLmxvY2FsZURhdGEoKS5tb250aHNQYXJzZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgLy8gVE9ETzogQW5vdGhlciBzaWxlbnQgZmFpbHVyZT9cbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnbnVtYmVyJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbW9tO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGRheU9mTW9udGggPSBNYXRoLm1pbihtb20uZGF0ZSgpLCBkYXlzSW5Nb250aChtb20ueWVhcigpLCB2YWx1ZSkpO1xuICAgICAgICBtb20uX2RbJ3NldCcgKyAobW9tLl9pc1VUQyA/ICdVVEMnIDogJycpICsgJ01vbnRoJ10odmFsdWUsIGRheU9mTW9udGgpO1xuICAgICAgICByZXR1cm4gbW9tO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFNldE1vbnRoICh2YWx1ZSkge1xuICAgICAgICBpZiAodmFsdWUgIT0gbnVsbCkge1xuICAgICAgICAgICAgc2V0TW9udGgodGhpcywgdmFsdWUpO1xuICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLnVwZGF0ZU9mZnNldCh0aGlzLCB0cnVlKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIGdldF9zZXRfX2dldCh0aGlzLCAnTW9udGgnKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldERheXNJbk1vbnRoICgpIHtcbiAgICAgICAgcmV0dXJuIGRheXNJbk1vbnRoKHRoaXMueWVhcigpLCB0aGlzLm1vbnRoKCkpO1xuICAgIH1cblxuICAgIHZhciBkZWZhdWx0TW9udGhzU2hvcnRSZWdleCA9IG1hdGNoV29yZDtcbiAgICBmdW5jdGlvbiBtb250aHNTaG9ydFJlZ2V4IChpc1N0cmljdCkge1xuICAgICAgICBpZiAodGhpcy5fbW9udGhzUGFyc2VFeGFjdCkge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfbW9udGhzUmVnZXgnKSkge1xuICAgICAgICAgICAgICAgIGNvbXB1dGVNb250aHNQYXJzZS5jYWxsKHRoaXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlzU3RyaWN0KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX21vbnRoc1Nob3J0U3RyaWN0UmVnZXg7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl9tb250aHNTaG9ydFJlZ2V4O1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfbW9udGhzU2hvcnRSZWdleCcpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fbW9udGhzU2hvcnRSZWdleCA9IGRlZmF1bHRNb250aHNTaG9ydFJlZ2V4O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX21vbnRoc1Nob3J0U3RyaWN0UmVnZXggJiYgaXNTdHJpY3QgP1xuICAgICAgICAgICAgICAgIHRoaXMuX21vbnRoc1Nob3J0U3RyaWN0UmVnZXggOiB0aGlzLl9tb250aHNTaG9ydFJlZ2V4O1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRNb250aHNSZWdleCA9IG1hdGNoV29yZDtcbiAgICBmdW5jdGlvbiBtb250aHNSZWdleCAoaXNTdHJpY3QpIHtcbiAgICAgICAgaWYgKHRoaXMuX21vbnRoc1BhcnNlRXhhY3QpIHtcbiAgICAgICAgICAgIGlmICghaGFzT3duUHJvcCh0aGlzLCAnX21vbnRoc1JlZ2V4JykpIHtcbiAgICAgICAgICAgICAgICBjb21wdXRlTW9udGhzUGFyc2UuY2FsbCh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl9tb250aHNTdHJpY3RSZWdleDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX21vbnRoc1JlZ2V4O1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfbW9udGhzUmVnZXgnKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX21vbnRoc1JlZ2V4ID0gZGVmYXVsdE1vbnRoc1JlZ2V4O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX21vbnRoc1N0cmljdFJlZ2V4ICYmIGlzU3RyaWN0ID9cbiAgICAgICAgICAgICAgICB0aGlzLl9tb250aHNTdHJpY3RSZWdleCA6IHRoaXMuX21vbnRoc1JlZ2V4O1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY29tcHV0ZU1vbnRoc1BhcnNlICgpIHtcbiAgICAgICAgZnVuY3Rpb24gY21wTGVuUmV2KGEsIGIpIHtcbiAgICAgICAgICAgIHJldHVybiBiLmxlbmd0aCAtIGEubGVuZ3RoO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHNob3J0UGllY2VzID0gW10sIGxvbmdQaWVjZXMgPSBbXSwgbWl4ZWRQaWVjZXMgPSBbXSxcbiAgICAgICAgICAgIGksIG1vbTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IDEyOyBpKyspIHtcbiAgICAgICAgICAgIC8vIG1ha2UgdGhlIHJlZ2V4IGlmIHdlIGRvbid0IGhhdmUgaXQgYWxyZWFkeVxuICAgICAgICAgICAgbW9tID0gY3JlYXRlX3V0Y19fY3JlYXRlVVRDKFsyMDAwLCBpXSk7XG4gICAgICAgICAgICBzaG9ydFBpZWNlcy5wdXNoKHRoaXMubW9udGhzU2hvcnQobW9tLCAnJykpO1xuICAgICAgICAgICAgbG9uZ1BpZWNlcy5wdXNoKHRoaXMubW9udGhzKG1vbSwgJycpKTtcbiAgICAgICAgICAgIG1peGVkUGllY2VzLnB1c2godGhpcy5tb250aHMobW9tLCAnJykpO1xuICAgICAgICAgICAgbWl4ZWRQaWVjZXMucHVzaCh0aGlzLm1vbnRoc1Nob3J0KG1vbSwgJycpKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTb3J0aW5nIG1ha2VzIHN1cmUgaWYgb25lIG1vbnRoIChvciBhYmJyKSBpcyBhIHByZWZpeCBvZiBhbm90aGVyIGl0XG4gICAgICAgIC8vIHdpbGwgbWF0Y2ggdGhlIGxvbmdlciBwaWVjZS5cbiAgICAgICAgc2hvcnRQaWVjZXMuc29ydChjbXBMZW5SZXYpO1xuICAgICAgICBsb25nUGllY2VzLnNvcnQoY21wTGVuUmV2KTtcbiAgICAgICAgbWl4ZWRQaWVjZXMuc29ydChjbXBMZW5SZXYpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgMTI7IGkrKykge1xuICAgICAgICAgICAgc2hvcnRQaWVjZXNbaV0gPSByZWdleEVzY2FwZShzaG9ydFBpZWNlc1tpXSk7XG4gICAgICAgICAgICBsb25nUGllY2VzW2ldID0gcmVnZXhFc2NhcGUobG9uZ1BpZWNlc1tpXSk7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChpID0gMDsgaSA8IDI0OyBpKyspIHtcbiAgICAgICAgICAgIG1peGVkUGllY2VzW2ldID0gcmVnZXhFc2NhcGUobWl4ZWRQaWVjZXNbaV0pO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbW9udGhzUmVnZXggPSBuZXcgUmVnRXhwKCdeKCcgKyBtaXhlZFBpZWNlcy5qb2luKCd8JykgKyAnKScsICdpJyk7XG4gICAgICAgIHRoaXMuX21vbnRoc1Nob3J0UmVnZXggPSB0aGlzLl9tb250aHNSZWdleDtcbiAgICAgICAgdGhpcy5fbW9udGhzU3RyaWN0UmVnZXggPSBuZXcgUmVnRXhwKCdeKCcgKyBsb25nUGllY2VzLmpvaW4oJ3wnKSArICcpJywgJ2knKTtcbiAgICAgICAgdGhpcy5fbW9udGhzU2hvcnRTdHJpY3RSZWdleCA9IG5ldyBSZWdFeHAoJ14oJyArIHNob3J0UGllY2VzLmpvaW4oJ3wnKSArICcpJywgJ2knKTtcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbignWScsIDAsIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHkgPSB0aGlzLnllYXIoKTtcbiAgICAgICAgcmV0dXJuIHkgPD0gOTk5OSA/ICcnICsgeSA6ICcrJyArIHk7XG4gICAgfSk7XG5cbiAgICBhZGRGb3JtYXRUb2tlbigwLCBbJ1lZJywgMl0sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMueWVhcigpICUgMTAwO1xuICAgIH0pO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oMCwgWydZWVlZJywgICA0XSwgICAgICAgMCwgJ3llYXInKTtcbiAgICBhZGRGb3JtYXRUb2tlbigwLCBbJ1lZWVlZJywgIDVdLCAgICAgICAwLCAneWVhcicpO1xuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnWVlZWVlZJywgNiwgdHJ1ZV0sIDAsICd5ZWFyJyk7XG5cbiAgICAvLyBBTElBU0VTXG5cbiAgICBhZGRVbml0QWxpYXMoJ3llYXInLCAneScpO1xuXG4gICAgLy8gUFJJT1JJVElFU1xuXG4gICAgYWRkVW5pdFByaW9yaXR5KCd5ZWFyJywgMSk7XG5cbiAgICAvLyBQQVJTSU5HXG5cbiAgICBhZGRSZWdleFRva2VuKCdZJywgICAgICBtYXRjaFNpZ25lZCk7XG4gICAgYWRkUmVnZXhUb2tlbignWVknLCAgICAgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ1lZWVknLCAgIG1hdGNoMXRvNCwgbWF0Y2g0KTtcbiAgICBhZGRSZWdleFRva2VuKCdZWVlZWScsICBtYXRjaDF0bzYsIG1hdGNoNik7XG4gICAgYWRkUmVnZXhUb2tlbignWVlZWVlZJywgbWF0Y2gxdG82LCBtYXRjaDYpO1xuXG4gICAgYWRkUGFyc2VUb2tlbihbJ1lZWVlZJywgJ1lZWVlZWSddLCBZRUFSKTtcbiAgICBhZGRQYXJzZVRva2VuKCdZWVlZJywgZnVuY3Rpb24gKGlucHV0LCBhcnJheSkge1xuICAgICAgICBhcnJheVtZRUFSXSA9IGlucHV0Lmxlbmd0aCA9PT0gMiA/IHV0aWxzX2hvb2tzX19ob29rcy5wYXJzZVR3b0RpZ2l0WWVhcihpbnB1dCkgOiB0b0ludChpbnB1dCk7XG4gICAgfSk7XG4gICAgYWRkUGFyc2VUb2tlbignWVknLCBmdW5jdGlvbiAoaW5wdXQsIGFycmF5KSB7XG4gICAgICAgIGFycmF5W1lFQVJdID0gdXRpbHNfaG9va3NfX2hvb2tzLnBhcnNlVHdvRGlnaXRZZWFyKGlucHV0KTtcbiAgICB9KTtcbiAgICBhZGRQYXJzZVRva2VuKCdZJywgZnVuY3Rpb24gKGlucHV0LCBhcnJheSkge1xuICAgICAgICBhcnJheVtZRUFSXSA9IHBhcnNlSW50KGlucHV0LCAxMCk7XG4gICAgfSk7XG5cbiAgICAvLyBIRUxQRVJTXG5cbiAgICBmdW5jdGlvbiBkYXlzSW5ZZWFyKHllYXIpIHtcbiAgICAgICAgcmV0dXJuIGlzTGVhcFllYXIoeWVhcikgPyAzNjYgOiAzNjU7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNMZWFwWWVhcih5ZWFyKSB7XG4gICAgICAgIHJldHVybiAoeWVhciAlIDQgPT09IDAgJiYgeWVhciAlIDEwMCAhPT0gMCkgfHwgeWVhciAlIDQwMCA9PT0gMDtcbiAgICB9XG5cbiAgICAvLyBIT09LU1xuXG4gICAgdXRpbHNfaG9va3NfX2hvb2tzLnBhcnNlVHdvRGlnaXRZZWFyID0gZnVuY3Rpb24gKGlucHV0KSB7XG4gICAgICAgIHJldHVybiB0b0ludChpbnB1dCkgKyAodG9JbnQoaW5wdXQpID4gNjggPyAxOTAwIDogMjAwMCk7XG4gICAgfTtcblxuICAgIC8vIE1PTUVOVFNcblxuICAgIHZhciBnZXRTZXRZZWFyID0gbWFrZUdldFNldCgnRnVsbFllYXInLCB0cnVlKTtcblxuICAgIGZ1bmN0aW9uIGdldElzTGVhcFllYXIgKCkge1xuICAgICAgICByZXR1cm4gaXNMZWFwWWVhcih0aGlzLnllYXIoKSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY3JlYXRlRGF0ZSAoeSwgbSwgZCwgaCwgTSwgcywgbXMpIHtcbiAgICAgICAgLy9jYW4ndCBqdXN0IGFwcGx5KCkgdG8gY3JlYXRlIGEgZGF0ZTpcbiAgICAgICAgLy9odHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE4MTM0OC9pbnN0YW50aWF0aW5nLWEtamF2YXNjcmlwdC1vYmplY3QtYnktY2FsbGluZy1wcm90b3R5cGUtY29uc3RydWN0b3ItYXBwbHlcbiAgICAgICAgdmFyIGRhdGUgPSBuZXcgRGF0ZSh5LCBtLCBkLCBoLCBNLCBzLCBtcyk7XG5cbiAgICAgICAgLy90aGUgZGF0ZSBjb25zdHJ1Y3RvciByZW1hcHMgeWVhcnMgMC05OSB0byAxOTAwLTE5OTlcbiAgICAgICAgaWYgKHkgPCAxMDAgJiYgeSA+PSAwICYmIGlzRmluaXRlKGRhdGUuZ2V0RnVsbFllYXIoKSkpIHtcbiAgICAgICAgICAgIGRhdGUuc2V0RnVsbFllYXIoeSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGU7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY3JlYXRlVVRDRGF0ZSAoeSkge1xuICAgICAgICB2YXIgZGF0ZSA9IG5ldyBEYXRlKERhdGUuVVRDLmFwcGx5KG51bGwsIGFyZ3VtZW50cykpO1xuXG4gICAgICAgIC8vdGhlIERhdGUuVVRDIGZ1bmN0aW9uIHJlbWFwcyB5ZWFycyAwLTk5IHRvIDE5MDAtMTk5OVxuICAgICAgICBpZiAoeSA8IDEwMCAmJiB5ID49IDAgJiYgaXNGaW5pdGUoZGF0ZS5nZXRVVENGdWxsWWVhcigpKSkge1xuICAgICAgICAgICAgZGF0ZS5zZXRVVENGdWxsWWVhcih5KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGF0ZTtcbiAgICB9XG5cbiAgICAvLyBzdGFydC1vZi1maXJzdC13ZWVrIC0gc3RhcnQtb2YteWVhclxuICAgIGZ1bmN0aW9uIGZpcnN0V2Vla09mZnNldCh5ZWFyLCBkb3csIGRveSkge1xuICAgICAgICB2YXIgLy8gZmlyc3Qtd2VlayBkYXkgLS0gd2hpY2ggamFudWFyeSBpcyBhbHdheXMgaW4gdGhlIGZpcnN0IHdlZWsgKDQgZm9yIGlzbywgMSBmb3Igb3RoZXIpXG4gICAgICAgICAgICBmd2QgPSA3ICsgZG93IC0gZG95LFxuICAgICAgICAgICAgLy8gZmlyc3Qtd2VlayBkYXkgbG9jYWwgd2Vla2RheSAtLSB3aGljaCBsb2NhbCB3ZWVrZGF5IGlzIGZ3ZFxuICAgICAgICAgICAgZndkbHcgPSAoNyArIGNyZWF0ZVVUQ0RhdGUoeWVhciwgMCwgZndkKS5nZXRVVENEYXkoKSAtIGRvdykgJSA3O1xuXG4gICAgICAgIHJldHVybiAtZndkbHcgKyBmd2QgLSAxO1xuICAgIH1cblxuICAgIC8vaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fd2Vla19kYXRlI0NhbGN1bGF0aW5nX2FfZGF0ZV9naXZlbl90aGVfeWVhci4yQ193ZWVrX251bWJlcl9hbmRfd2Vla2RheVxuICAgIGZ1bmN0aW9uIGRheU9mWWVhckZyb21XZWVrcyh5ZWFyLCB3ZWVrLCB3ZWVrZGF5LCBkb3csIGRveSkge1xuICAgICAgICB2YXIgbG9jYWxXZWVrZGF5ID0gKDcgKyB3ZWVrZGF5IC0gZG93KSAlIDcsXG4gICAgICAgICAgICB3ZWVrT2Zmc2V0ID0gZmlyc3RXZWVrT2Zmc2V0KHllYXIsIGRvdywgZG95KSxcbiAgICAgICAgICAgIGRheU9mWWVhciA9IDEgKyA3ICogKHdlZWsgLSAxKSArIGxvY2FsV2Vla2RheSArIHdlZWtPZmZzZXQsXG4gICAgICAgICAgICByZXNZZWFyLCByZXNEYXlPZlllYXI7XG5cbiAgICAgICAgaWYgKGRheU9mWWVhciA8PSAwKSB7XG4gICAgICAgICAgICByZXNZZWFyID0geWVhciAtIDE7XG4gICAgICAgICAgICByZXNEYXlPZlllYXIgPSBkYXlzSW5ZZWFyKHJlc1llYXIpICsgZGF5T2ZZZWFyO1xuICAgICAgICB9IGVsc2UgaWYgKGRheU9mWWVhciA+IGRheXNJblllYXIoeWVhcikpIHtcbiAgICAgICAgICAgIHJlc1llYXIgPSB5ZWFyICsgMTtcbiAgICAgICAgICAgIHJlc0RheU9mWWVhciA9IGRheU9mWWVhciAtIGRheXNJblllYXIoeWVhcik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXNZZWFyID0geWVhcjtcbiAgICAgICAgICAgIHJlc0RheU9mWWVhciA9IGRheU9mWWVhcjtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB5ZWFyOiByZXNZZWFyLFxuICAgICAgICAgICAgZGF5T2ZZZWFyOiByZXNEYXlPZlllYXJcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB3ZWVrT2ZZZWFyKG1vbSwgZG93LCBkb3kpIHtcbiAgICAgICAgdmFyIHdlZWtPZmZzZXQgPSBmaXJzdFdlZWtPZmZzZXQobW9tLnllYXIoKSwgZG93LCBkb3kpLFxuICAgICAgICAgICAgd2VlayA9IE1hdGguZmxvb3IoKG1vbS5kYXlPZlllYXIoKSAtIHdlZWtPZmZzZXQgLSAxKSAvIDcpICsgMSxcbiAgICAgICAgICAgIHJlc1dlZWssIHJlc1llYXI7XG5cbiAgICAgICAgaWYgKHdlZWsgPCAxKSB7XG4gICAgICAgICAgICByZXNZZWFyID0gbW9tLnllYXIoKSAtIDE7XG4gICAgICAgICAgICByZXNXZWVrID0gd2VlayArIHdlZWtzSW5ZZWFyKHJlc1llYXIsIGRvdywgZG95KTtcbiAgICAgICAgfSBlbHNlIGlmICh3ZWVrID4gd2Vla3NJblllYXIobW9tLnllYXIoKSwgZG93LCBkb3kpKSB7XG4gICAgICAgICAgICByZXNXZWVrID0gd2VlayAtIHdlZWtzSW5ZZWFyKG1vbS55ZWFyKCksIGRvdywgZG95KTtcbiAgICAgICAgICAgIHJlc1llYXIgPSBtb20ueWVhcigpICsgMTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJlc1llYXIgPSBtb20ueWVhcigpO1xuICAgICAgICAgICAgcmVzV2VlayA9IHdlZWs7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgd2VlazogcmVzV2VlayxcbiAgICAgICAgICAgIHllYXI6IHJlc1llYXJcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB3ZWVrc0luWWVhcih5ZWFyLCBkb3csIGRveSkge1xuICAgICAgICB2YXIgd2Vla09mZnNldCA9IGZpcnN0V2Vla09mZnNldCh5ZWFyLCBkb3csIGRveSksXG4gICAgICAgICAgICB3ZWVrT2Zmc2V0TmV4dCA9IGZpcnN0V2Vla09mZnNldCh5ZWFyICsgMSwgZG93LCBkb3kpO1xuICAgICAgICByZXR1cm4gKGRheXNJblllYXIoeWVhcikgLSB3ZWVrT2Zmc2V0ICsgd2Vla09mZnNldE5leHQpIC8gNztcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbigndycsIFsnd3cnLCAyXSwgJ3dvJywgJ3dlZWsnKTtcbiAgICBhZGRGb3JtYXRUb2tlbignVycsIFsnV1cnLCAyXSwgJ1dvJywgJ2lzb1dlZWsnKTtcblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnd2VlaycsICd3Jyk7XG4gICAgYWRkVW5pdEFsaWFzKCdpc29XZWVrJywgJ1cnKTtcblxuICAgIC8vIFBSSU9SSVRJRVNcblxuICAgIGFkZFVuaXRQcmlvcml0eSgnd2VlaycsIDUpO1xuICAgIGFkZFVuaXRQcmlvcml0eSgnaXNvV2VlaycsIDUpO1xuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgYWRkUmVnZXhUb2tlbigndycsICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ3d3JywgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ1cnLCAgbWF0Y2gxdG8yKTtcbiAgICBhZGRSZWdleFRva2VuKCdXVycsIG1hdGNoMXRvMiwgbWF0Y2gyKTtcblxuICAgIGFkZFdlZWtQYXJzZVRva2VuKFsndycsICd3dycsICdXJywgJ1dXJ10sIGZ1bmN0aW9uIChpbnB1dCwgd2VlaywgY29uZmlnLCB0b2tlbikge1xuICAgICAgICB3ZWVrW3Rva2VuLnN1YnN0cigwLCAxKV0gPSB0b0ludChpbnB1dCk7XG4gICAgfSk7XG5cbiAgICAvLyBIRUxQRVJTXG5cbiAgICAvLyBMT0NBTEVTXG5cbiAgICBmdW5jdGlvbiBsb2NhbGVXZWVrIChtb20pIHtcbiAgICAgICAgcmV0dXJuIHdlZWtPZlllYXIobW9tLCB0aGlzLl93ZWVrLmRvdywgdGhpcy5fd2Vlay5kb3kpLndlZWs7XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRMb2NhbGVXZWVrID0ge1xuICAgICAgICBkb3cgOiAwLCAvLyBTdW5kYXkgaXMgdGhlIGZpcnN0IGRheSBvZiB0aGUgd2Vlay5cbiAgICAgICAgZG95IDogNiAgLy8gVGhlIHdlZWsgdGhhdCBjb250YWlucyBKYW4gMXN0IGlzIHRoZSBmaXJzdCB3ZWVrIG9mIHRoZSB5ZWFyLlxuICAgIH07XG5cbiAgICBmdW5jdGlvbiBsb2NhbGVGaXJzdERheU9mV2VlayAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl93ZWVrLmRvdztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBsb2NhbGVGaXJzdERheU9mWWVhciAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl93ZWVrLmRveTtcbiAgICB9XG5cbiAgICAvLyBNT01FTlRTXG5cbiAgICBmdW5jdGlvbiBnZXRTZXRXZWVrIChpbnB1dCkge1xuICAgICAgICB2YXIgd2VlayA9IHRoaXMubG9jYWxlRGF0YSgpLndlZWsodGhpcyk7XG4gICAgICAgIHJldHVybiBpbnB1dCA9PSBudWxsID8gd2VlayA6IHRoaXMuYWRkKChpbnB1dCAtIHdlZWspICogNywgJ2QnKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTZXRJU09XZWVrIChpbnB1dCkge1xuICAgICAgICB2YXIgd2VlayA9IHdlZWtPZlllYXIodGhpcywgMSwgNCkud2VlaztcbiAgICAgICAgcmV0dXJuIGlucHV0ID09IG51bGwgPyB3ZWVrIDogdGhpcy5hZGQoKGlucHV0IC0gd2VlaykgKiA3LCAnZCcpO1xuICAgIH1cblxuICAgIC8vIEZPUk1BVFRJTkdcblxuICAgIGFkZEZvcm1hdFRva2VuKCdkJywgMCwgJ2RvJywgJ2RheScpO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ2RkJywgMCwgMCwgZnVuY3Rpb24gKGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gdGhpcy5sb2NhbGVEYXRhKCkud2Vla2RheXNNaW4odGhpcywgZm9ybWF0KTtcbiAgICB9KTtcblxuICAgIGFkZEZvcm1hdFRva2VuKCdkZGQnLCAwLCAwLCBmdW5jdGlvbiAoZm9ybWF0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvY2FsZURhdGEoKS53ZWVrZGF5c1Nob3J0KHRoaXMsIGZvcm1hdCk7XG4gICAgfSk7XG5cbiAgICBhZGRGb3JtYXRUb2tlbignZGRkZCcsIDAsIDAsIGZ1bmN0aW9uIChmb3JtYXQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9jYWxlRGF0YSgpLndlZWtkYXlzKHRoaXMsIGZvcm1hdCk7XG4gICAgfSk7XG5cbiAgICBhZGRGb3JtYXRUb2tlbignZScsIDAsIDAsICd3ZWVrZGF5Jyk7XG4gICAgYWRkRm9ybWF0VG9rZW4oJ0UnLCAwLCAwLCAnaXNvV2Vla2RheScpO1xuXG4gICAgLy8gQUxJQVNFU1xuXG4gICAgYWRkVW5pdEFsaWFzKCdkYXknLCAnZCcpO1xuICAgIGFkZFVuaXRBbGlhcygnd2Vla2RheScsICdlJyk7XG4gICAgYWRkVW5pdEFsaWFzKCdpc29XZWVrZGF5JywgJ0UnKTtcblxuICAgIC8vIFBSSU9SSVRZXG4gICAgYWRkVW5pdFByaW9yaXR5KCdkYXknLCAxMSk7XG4gICAgYWRkVW5pdFByaW9yaXR5KCd3ZWVrZGF5JywgMTEpO1xuICAgIGFkZFVuaXRQcmlvcml0eSgnaXNvV2Vla2RheScsIDExKTtcblxuICAgIC8vIFBBUlNJTkdcblxuICAgIGFkZFJlZ2V4VG9rZW4oJ2QnLCAgICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2UnLCAgICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ0UnLCAgICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2RkJywgICBmdW5jdGlvbiAoaXNTdHJpY3QsIGxvY2FsZSkge1xuICAgICAgICByZXR1cm4gbG9jYWxlLndlZWtkYXlzTWluUmVnZXgoaXNTdHJpY3QpO1xuICAgIH0pO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2RkZCcsICAgZnVuY3Rpb24gKGlzU3RyaWN0LCBsb2NhbGUpIHtcbiAgICAgICAgcmV0dXJuIGxvY2FsZS53ZWVrZGF5c1Nob3J0UmVnZXgoaXNTdHJpY3QpO1xuICAgIH0pO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2RkZGQnLCAgIGZ1bmN0aW9uIChpc1N0cmljdCwgbG9jYWxlKSB7XG4gICAgICAgIHJldHVybiBsb2NhbGUud2Vla2RheXNSZWdleChpc1N0cmljdCk7XG4gICAgfSk7XG5cbiAgICBhZGRXZWVrUGFyc2VUb2tlbihbJ2RkJywgJ2RkZCcsICdkZGRkJ10sIGZ1bmN0aW9uIChpbnB1dCwgd2VlaywgY29uZmlnLCB0b2tlbikge1xuICAgICAgICB2YXIgd2Vla2RheSA9IGNvbmZpZy5fbG9jYWxlLndlZWtkYXlzUGFyc2UoaW5wdXQsIHRva2VuLCBjb25maWcuX3N0cmljdCk7XG4gICAgICAgIC8vIGlmIHdlIGRpZG4ndCBnZXQgYSB3ZWVrZGF5IG5hbWUsIG1hcmsgdGhlIGRhdGUgYXMgaW52YWxpZFxuICAgICAgICBpZiAod2Vla2RheSAhPSBudWxsKSB7XG4gICAgICAgICAgICB3ZWVrLmQgPSB3ZWVrZGF5O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuaW52YWxpZFdlZWtkYXkgPSBpbnB1dDtcbiAgICAgICAgfVxuICAgIH0pO1xuXG4gICAgYWRkV2Vla1BhcnNlVG9rZW4oWydkJywgJ2UnLCAnRSddLCBmdW5jdGlvbiAoaW5wdXQsIHdlZWssIGNvbmZpZywgdG9rZW4pIHtcbiAgICAgICAgd2Vla1t0b2tlbl0gPSB0b0ludChpbnB1dCk7XG4gICAgfSk7XG5cbiAgICAvLyBIRUxQRVJTXG5cbiAgICBmdW5jdGlvbiBwYXJzZVdlZWtkYXkoaW5wdXQsIGxvY2FsZSkge1xuICAgICAgICBpZiAodHlwZW9mIGlucHV0ICE9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpc05hTihpbnB1dCkpIHtcbiAgICAgICAgICAgIHJldHVybiBwYXJzZUludChpbnB1dCwgMTApO1xuICAgICAgICB9XG5cbiAgICAgICAgaW5wdXQgPSBsb2NhbGUud2Vla2RheXNQYXJzZShpbnB1dCk7XG4gICAgICAgIGlmICh0eXBlb2YgaW5wdXQgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgICByZXR1cm4gaW5wdXQ7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBwYXJzZUlzb1dlZWtkYXkoaW5wdXQsIGxvY2FsZSkge1xuICAgICAgICBpZiAodHlwZW9mIGlucHV0ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIGxvY2FsZS53ZWVrZGF5c1BhcnNlKGlucHV0KSAlIDcgfHwgNztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaXNOYU4oaW5wdXQpID8gbnVsbCA6IGlucHV0O1xuICAgIH1cblxuICAgIC8vIExPQ0FMRVNcblxuICAgIHZhciBkZWZhdWx0TG9jYWxlV2Vla2RheXMgPSAnU3VuZGF5X01vbmRheV9UdWVzZGF5X1dlZG5lc2RheV9UaHVyc2RheV9GcmlkYXlfU2F0dXJkYXknLnNwbGl0KCdfJyk7XG4gICAgZnVuY3Rpb24gbG9jYWxlV2Vla2RheXMgKG0sIGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gaXNBcnJheSh0aGlzLl93ZWVrZGF5cykgPyB0aGlzLl93ZWVrZGF5c1ttLmRheSgpXSA6XG4gICAgICAgICAgICB0aGlzLl93ZWVrZGF5c1t0aGlzLl93ZWVrZGF5cy5pc0Zvcm1hdC50ZXN0KGZvcm1hdCkgPyAnZm9ybWF0JyA6ICdzdGFuZGFsb25lJ11bbS5kYXkoKV07XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRMb2NhbGVXZWVrZGF5c1Nob3J0ID0gJ1N1bl9Nb25fVHVlX1dlZF9UaHVfRnJpX1NhdCcuc3BsaXQoJ18nKTtcbiAgICBmdW5jdGlvbiBsb2NhbGVXZWVrZGF5c1Nob3J0IChtKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c1Nob3J0W20uZGF5KCldO1xuICAgIH1cblxuICAgIHZhciBkZWZhdWx0TG9jYWxlV2Vla2RheXNNaW4gPSAnU3VfTW9fVHVfV2VfVGhfRnJfU2EnLnNwbGl0KCdfJyk7XG4gICAgZnVuY3Rpb24gbG9jYWxlV2Vla2RheXNNaW4gKG0pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3dlZWtkYXlzTWluW20uZGF5KCldO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGRheV9vZl93ZWVrX19oYW5kbGVTdHJpY3RQYXJzZSh3ZWVrZGF5TmFtZSwgZm9ybWF0LCBzdHJpY3QpIHtcbiAgICAgICAgdmFyIGksIGlpLCBtb20sIGxsYyA9IHdlZWtkYXlOYW1lLnRvTG9jYWxlTG93ZXJDYXNlKCk7XG4gICAgICAgIGlmICghdGhpcy5fd2Vla2RheXNQYXJzZSkge1xuICAgICAgICAgICAgdGhpcy5fd2Vla2RheXNQYXJzZSA9IFtdO1xuICAgICAgICAgICAgdGhpcy5fc2hvcnRXZWVrZGF5c1BhcnNlID0gW107XG4gICAgICAgICAgICB0aGlzLl9taW5XZWVrZGF5c1BhcnNlID0gW107XG5cbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCA3OyArK2kpIHtcbiAgICAgICAgICAgICAgICBtb20gPSBjcmVhdGVfdXRjX19jcmVhdGVVVEMoWzIwMDAsIDFdKS5kYXkoaSk7XG4gICAgICAgICAgICAgICAgdGhpcy5fbWluV2Vla2RheXNQYXJzZVtpXSA9IHRoaXMud2Vla2RheXNNaW4obW9tLCAnJykudG9Mb2NhbGVMb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9zaG9ydFdlZWtkYXlzUGFyc2VbaV0gPSB0aGlzLndlZWtkYXlzU2hvcnQobW9tLCAnJykudG9Mb2NhbGVMb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICB0aGlzLl93ZWVrZGF5c1BhcnNlW2ldID0gdGhpcy53ZWVrZGF5cyhtb20sICcnKS50b0xvY2FsZUxvd2VyQ2FzZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHN0cmljdCkge1xuICAgICAgICAgICAgaWYgKGZvcm1hdCA9PT0gJ2RkZGQnKSB7XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fd2Vla2RheXNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWkgIT09IC0xID8gaWkgOiBudWxsO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChmb3JtYXQgPT09ICdkZGQnKSB7XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fc2hvcnRXZWVrZGF5c1BhcnNlLCBsbGMpO1xuICAgICAgICAgICAgICAgIHJldHVybiBpaSAhPT0gLTEgPyBpaSA6IG51bGw7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX21pbldlZWtkYXlzUGFyc2UsIGxsYyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGlpICE9PSAtMSA/IGlpIDogbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT09ICdkZGRkJykge1xuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX3dlZWtkYXlzUGFyc2UsIGxsYyk7XG4gICAgICAgICAgICAgICAgaWYgKGlpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaWk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX3Nob3J0V2Vla2RheXNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICBpZiAoaWkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpaTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fbWluV2Vla2RheXNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWkgIT09IC0xID8gaWkgOiBudWxsO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChmb3JtYXQgPT09ICdkZGQnKSB7XG4gICAgICAgICAgICAgICAgaWkgPSBpbmRleE9mLmNhbGwodGhpcy5fc2hvcnRXZWVrZGF5c1BhcnNlLCBsbGMpO1xuICAgICAgICAgICAgICAgIGlmIChpaSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGlpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpaSA9IGluZGV4T2YuY2FsbCh0aGlzLl93ZWVrZGF5c1BhcnNlLCBsbGMpO1xuICAgICAgICAgICAgICAgIGlmIChpaSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGlpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpaSA9IGluZGV4T2YuY2FsbCh0aGlzLl9taW5XZWVrZGF5c1BhcnNlLCBsbGMpO1xuICAgICAgICAgICAgICAgIHJldHVybiBpaSAhPT0gLTEgPyBpaSA6IG51bGw7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX21pbldlZWtkYXlzUGFyc2UsIGxsYyk7XG4gICAgICAgICAgICAgICAgaWYgKGlpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaWk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX3dlZWtkYXlzUGFyc2UsIGxsYyk7XG4gICAgICAgICAgICAgICAgaWYgKGlpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaWk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlpID0gaW5kZXhPZi5jYWxsKHRoaXMuX3Nob3J0V2Vla2RheXNQYXJzZSwgbGxjKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWkgIT09IC0xID8gaWkgOiBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbG9jYWxlV2Vla2RheXNQYXJzZSAod2Vla2RheU5hbWUsIGZvcm1hdCwgc3RyaWN0KSB7XG4gICAgICAgIHZhciBpLCBtb20sIHJlZ2V4O1xuXG4gICAgICAgIGlmICh0aGlzLl93ZWVrZGF5c1BhcnNlRXhhY3QpIHtcbiAgICAgICAgICAgIHJldHVybiBkYXlfb2Zfd2Vla19faGFuZGxlU3RyaWN0UGFyc2UuY2FsbCh0aGlzLCB3ZWVrZGF5TmFtZSwgZm9ybWF0LCBzdHJpY3QpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF0aGlzLl93ZWVrZGF5c1BhcnNlKSB7XG4gICAgICAgICAgICB0aGlzLl93ZWVrZGF5c1BhcnNlID0gW107XG4gICAgICAgICAgICB0aGlzLl9taW5XZWVrZGF5c1BhcnNlID0gW107XG4gICAgICAgICAgICB0aGlzLl9zaG9ydFdlZWtkYXlzUGFyc2UgPSBbXTtcbiAgICAgICAgICAgIHRoaXMuX2Z1bGxXZWVrZGF5c1BhcnNlID0gW107XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgNzsgaSsrKSB7XG4gICAgICAgICAgICAvLyBtYWtlIHRoZSByZWdleCBpZiB3ZSBkb24ndCBoYXZlIGl0IGFscmVhZHlcblxuICAgICAgICAgICAgbW9tID0gY3JlYXRlX3V0Y19fY3JlYXRlVVRDKFsyMDAwLCAxXSkuZGF5KGkpO1xuICAgICAgICAgICAgaWYgKHN0cmljdCAmJiAhdGhpcy5fZnVsbFdlZWtkYXlzUGFyc2VbaV0pIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9mdWxsV2Vla2RheXNQYXJzZVtpXSA9IG5ldyBSZWdFeHAoJ14nICsgdGhpcy53ZWVrZGF5cyhtb20sICcnKS5yZXBsYWNlKCcuJywgJ1xcLj8nKSArICckJywgJ2knKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9zaG9ydFdlZWtkYXlzUGFyc2VbaV0gPSBuZXcgUmVnRXhwKCdeJyArIHRoaXMud2Vla2RheXNTaG9ydChtb20sICcnKS5yZXBsYWNlKCcuJywgJ1xcLj8nKSArICckJywgJ2knKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9taW5XZWVrZGF5c1BhcnNlW2ldID0gbmV3IFJlZ0V4cCgnXicgKyB0aGlzLndlZWtkYXlzTWluKG1vbSwgJycpLnJlcGxhY2UoJy4nLCAnXFwuPycpICsgJyQnLCAnaScpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCF0aGlzLl93ZWVrZGF5c1BhcnNlW2ldKSB7XG4gICAgICAgICAgICAgICAgcmVnZXggPSAnXicgKyB0aGlzLndlZWtkYXlzKG1vbSwgJycpICsgJ3xeJyArIHRoaXMud2Vla2RheXNTaG9ydChtb20sICcnKSArICd8XicgKyB0aGlzLndlZWtkYXlzTWluKG1vbSwgJycpO1xuICAgICAgICAgICAgICAgIHRoaXMuX3dlZWtkYXlzUGFyc2VbaV0gPSBuZXcgUmVnRXhwKHJlZ2V4LnJlcGxhY2UoJy4nLCAnJyksICdpJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyB0ZXN0IHRoZSByZWdleFxuICAgICAgICAgICAgaWYgKHN0cmljdCAmJiBmb3JtYXQgPT09ICdkZGRkJyAmJiB0aGlzLl9mdWxsV2Vla2RheXNQYXJzZVtpXS50ZXN0KHdlZWtkYXlOYW1lKSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChzdHJpY3QgJiYgZm9ybWF0ID09PSAnZGRkJyAmJiB0aGlzLl9zaG9ydFdlZWtkYXlzUGFyc2VbaV0udGVzdCh3ZWVrZGF5TmFtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoc3RyaWN0ICYmIGZvcm1hdCA9PT0gJ2RkJyAmJiB0aGlzLl9taW5XZWVrZGF5c1BhcnNlW2ldLnRlc3Qod2Vla2RheU5hbWUpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCFzdHJpY3QgJiYgdGhpcy5fd2Vla2RheXNQYXJzZVtpXS50ZXN0KHdlZWtkYXlOYW1lKSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgZnVuY3Rpb24gZ2V0U2V0RGF5T2ZXZWVrIChpbnB1dCkge1xuICAgICAgICBpZiAoIXRoaXMuaXNWYWxpZCgpKSB7XG4gICAgICAgICAgICByZXR1cm4gaW5wdXQgIT0gbnVsbCA/IHRoaXMgOiBOYU47XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGRheSA9IHRoaXMuX2lzVVRDID8gdGhpcy5fZC5nZXRVVENEYXkoKSA6IHRoaXMuX2QuZ2V0RGF5KCk7XG4gICAgICAgIGlmIChpbnB1dCAhPSBudWxsKSB7XG4gICAgICAgICAgICBpbnB1dCA9IHBhcnNlV2Vla2RheShpbnB1dCwgdGhpcy5sb2NhbGVEYXRhKCkpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuYWRkKGlucHV0IC0gZGF5LCAnZCcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIGRheTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFNldExvY2FsZURheU9mV2VlayAoaW5wdXQpIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0ICE9IG51bGwgPyB0aGlzIDogTmFOO1xuICAgICAgICB9XG4gICAgICAgIHZhciB3ZWVrZGF5ID0gKHRoaXMuZGF5KCkgKyA3IC0gdGhpcy5sb2NhbGVEYXRhKCkuX3dlZWsuZG93KSAlIDc7XG4gICAgICAgIHJldHVybiBpbnB1dCA9PSBudWxsID8gd2Vla2RheSA6IHRoaXMuYWRkKGlucHV0IC0gd2Vla2RheSwgJ2QnKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTZXRJU09EYXlPZldlZWsgKGlucHV0KSB7XG4gICAgICAgIGlmICghdGhpcy5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBpbnB1dCAhPSBudWxsID8gdGhpcyA6IE5hTjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGJlaGF2ZXMgdGhlIHNhbWUgYXMgbW9tZW50I2RheSBleGNlcHRcbiAgICAgICAgLy8gYXMgYSBnZXR0ZXIsIHJldHVybnMgNyBpbnN0ZWFkIG9mIDAgKDEtNyByYW5nZSBpbnN0ZWFkIG9mIDAtNilcbiAgICAgICAgLy8gYXMgYSBzZXR0ZXIsIHN1bmRheSBzaG91bGQgYmVsb25nIHRvIHRoZSBwcmV2aW91cyB3ZWVrLlxuXG4gICAgICAgIGlmIChpbnB1dCAhPSBudWxsKSB7XG4gICAgICAgICAgICB2YXIgd2Vla2RheSA9IHBhcnNlSXNvV2Vla2RheShpbnB1dCwgdGhpcy5sb2NhbGVEYXRhKCkpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZGF5KHRoaXMuZGF5KCkgJSA3ID8gd2Vla2RheSA6IHdlZWtkYXkgLSA3KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRheSgpIHx8IDc7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgZGVmYXVsdFdlZWtkYXlzUmVnZXggPSBtYXRjaFdvcmQ7XG4gICAgZnVuY3Rpb24gd2Vla2RheXNSZWdleCAoaXNTdHJpY3QpIHtcbiAgICAgICAgaWYgKHRoaXMuX3dlZWtkYXlzUGFyc2VFeGFjdCkge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfd2Vla2RheXNSZWdleCcpKSB7XG4gICAgICAgICAgICAgICAgY29tcHV0ZVdlZWtkYXlzUGFyc2UuY2FsbCh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c1N0cmljdFJlZ2V4O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fd2Vla2RheXNSZWdleDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICghaGFzT3duUHJvcCh0aGlzLCAnX3dlZWtkYXlzUmVnZXgnKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3dlZWtkYXlzUmVnZXggPSBkZWZhdWx0V2Vla2RheXNSZWdleDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c1N0cmljdFJlZ2V4ICYmIGlzU3RyaWN0ID9cbiAgICAgICAgICAgICAgICB0aGlzLl93ZWVrZGF5c1N0cmljdFJlZ2V4IDogdGhpcy5fd2Vla2RheXNSZWdleDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHZhciBkZWZhdWx0V2Vla2RheXNTaG9ydFJlZ2V4ID0gbWF0Y2hXb3JkO1xuICAgIGZ1bmN0aW9uIHdlZWtkYXlzU2hvcnRSZWdleCAoaXNTdHJpY3QpIHtcbiAgICAgICAgaWYgKHRoaXMuX3dlZWtkYXlzUGFyc2VFeGFjdCkge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfd2Vla2RheXNSZWdleCcpKSB7XG4gICAgICAgICAgICAgICAgY29tcHV0ZVdlZWtkYXlzUGFyc2UuY2FsbCh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c1Nob3J0U3RyaWN0UmVnZXg7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c1Nob3J0UmVnZXg7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAoIWhhc093blByb3AodGhpcywgJ193ZWVrZGF5c1Nob3J0UmVnZXgnKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3dlZWtkYXlzU2hvcnRSZWdleCA9IGRlZmF1bHRXZWVrZGF5c1Nob3J0UmVnZXg7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fd2Vla2RheXNTaG9ydFN0cmljdFJlZ2V4ICYmIGlzU3RyaWN0ID9cbiAgICAgICAgICAgICAgICB0aGlzLl93ZWVrZGF5c1Nob3J0U3RyaWN0UmVnZXggOiB0aGlzLl93ZWVrZGF5c1Nob3J0UmVnZXg7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgZGVmYXVsdFdlZWtkYXlzTWluUmVnZXggPSBtYXRjaFdvcmQ7XG4gICAgZnVuY3Rpb24gd2Vla2RheXNNaW5SZWdleCAoaXNTdHJpY3QpIHtcbiAgICAgICAgaWYgKHRoaXMuX3dlZWtkYXlzUGFyc2VFeGFjdCkge1xuICAgICAgICAgICAgaWYgKCFoYXNPd25Qcm9wKHRoaXMsICdfd2Vla2RheXNSZWdleCcpKSB7XG4gICAgICAgICAgICAgICAgY29tcHV0ZVdlZWtkYXlzUGFyc2UuY2FsbCh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c01pblN0cmljdFJlZ2V4O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fd2Vla2RheXNNaW5SZWdleDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICghaGFzT3duUHJvcCh0aGlzLCAnX3dlZWtkYXlzTWluUmVnZXgnKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3dlZWtkYXlzTWluUmVnZXggPSBkZWZhdWx0V2Vla2RheXNNaW5SZWdleDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0aGlzLl93ZWVrZGF5c01pblN0cmljdFJlZ2V4ICYmIGlzU3RyaWN0ID9cbiAgICAgICAgICAgICAgICB0aGlzLl93ZWVrZGF5c01pblN0cmljdFJlZ2V4IDogdGhpcy5fd2Vla2RheXNNaW5SZWdleDtcbiAgICAgICAgfVxuICAgIH1cblxuXG4gICAgZnVuY3Rpb24gY29tcHV0ZVdlZWtkYXlzUGFyc2UgKCkge1xuICAgICAgICBmdW5jdGlvbiBjbXBMZW5SZXYoYSwgYikge1xuICAgICAgICAgICAgcmV0dXJuIGIubGVuZ3RoIC0gYS5sZW5ndGg7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgbWluUGllY2VzID0gW10sIHNob3J0UGllY2VzID0gW10sIGxvbmdQaWVjZXMgPSBbXSwgbWl4ZWRQaWVjZXMgPSBbXSxcbiAgICAgICAgICAgIGksIG1vbSwgbWlucCwgc2hvcnRwLCBsb25ncDtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IDc7IGkrKykge1xuICAgICAgICAgICAgLy8gbWFrZSB0aGUgcmVnZXggaWYgd2UgZG9uJ3QgaGF2ZSBpdCBhbHJlYWR5XG4gICAgICAgICAgICBtb20gPSBjcmVhdGVfdXRjX19jcmVhdGVVVEMoWzIwMDAsIDFdKS5kYXkoaSk7XG4gICAgICAgICAgICBtaW5wID0gdGhpcy53ZWVrZGF5c01pbihtb20sICcnKTtcbiAgICAgICAgICAgIHNob3J0cCA9IHRoaXMud2Vla2RheXNTaG9ydChtb20sICcnKTtcbiAgICAgICAgICAgIGxvbmdwID0gdGhpcy53ZWVrZGF5cyhtb20sICcnKTtcbiAgICAgICAgICAgIG1pblBpZWNlcy5wdXNoKG1pbnApO1xuICAgICAgICAgICAgc2hvcnRQaWVjZXMucHVzaChzaG9ydHApO1xuICAgICAgICAgICAgbG9uZ1BpZWNlcy5wdXNoKGxvbmdwKTtcbiAgICAgICAgICAgIG1peGVkUGllY2VzLnB1c2gobWlucCk7XG4gICAgICAgICAgICBtaXhlZFBpZWNlcy5wdXNoKHNob3J0cCk7XG4gICAgICAgICAgICBtaXhlZFBpZWNlcy5wdXNoKGxvbmdwKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTb3J0aW5nIG1ha2VzIHN1cmUgaWYgb25lIHdlZWtkYXkgKG9yIGFiYnIpIGlzIGEgcHJlZml4IG9mIGFub3RoZXIgaXRcbiAgICAgICAgLy8gd2lsbCBtYXRjaCB0aGUgbG9uZ2VyIHBpZWNlLlxuICAgICAgICBtaW5QaWVjZXMuc29ydChjbXBMZW5SZXYpO1xuICAgICAgICBzaG9ydFBpZWNlcy5zb3J0KGNtcExlblJldik7XG4gICAgICAgIGxvbmdQaWVjZXMuc29ydChjbXBMZW5SZXYpO1xuICAgICAgICBtaXhlZFBpZWNlcy5zb3J0KGNtcExlblJldik7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCA3OyBpKyspIHtcbiAgICAgICAgICAgIHNob3J0UGllY2VzW2ldID0gcmVnZXhFc2NhcGUoc2hvcnRQaWVjZXNbaV0pO1xuICAgICAgICAgICAgbG9uZ1BpZWNlc1tpXSA9IHJlZ2V4RXNjYXBlKGxvbmdQaWVjZXNbaV0pO1xuICAgICAgICAgICAgbWl4ZWRQaWVjZXNbaV0gPSByZWdleEVzY2FwZShtaXhlZFBpZWNlc1tpXSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl93ZWVrZGF5c1JlZ2V4ID0gbmV3IFJlZ0V4cCgnXignICsgbWl4ZWRQaWVjZXMuam9pbignfCcpICsgJyknLCAnaScpO1xuICAgICAgICB0aGlzLl93ZWVrZGF5c1Nob3J0UmVnZXggPSB0aGlzLl93ZWVrZGF5c1JlZ2V4O1xuICAgICAgICB0aGlzLl93ZWVrZGF5c01pblJlZ2V4ID0gdGhpcy5fd2Vla2RheXNSZWdleDtcblxuICAgICAgICB0aGlzLl93ZWVrZGF5c1N0cmljdFJlZ2V4ID0gbmV3IFJlZ0V4cCgnXignICsgbG9uZ1BpZWNlcy5qb2luKCd8JykgKyAnKScsICdpJyk7XG4gICAgICAgIHRoaXMuX3dlZWtkYXlzU2hvcnRTdHJpY3RSZWdleCA9IG5ldyBSZWdFeHAoJ14oJyArIHNob3J0UGllY2VzLmpvaW4oJ3wnKSArICcpJywgJ2knKTtcbiAgICAgICAgdGhpcy5fd2Vla2RheXNNaW5TdHJpY3RSZWdleCA9IG5ldyBSZWdFeHAoJ14oJyArIG1pblBpZWNlcy5qb2luKCd8JykgKyAnKScsICdpJyk7XG4gICAgfVxuXG4gICAgLy8gRk9STUFUVElOR1xuXG4gICAgZnVuY3Rpb24gaEZvcm1hdCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaG91cnMoKSAlIDEyIHx8IDEyO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGtGb3JtYXQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmhvdXJzKCkgfHwgMjQ7XG4gICAgfVxuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ0gnLCBbJ0hIJywgMl0sIDAsICdob3VyJyk7XG4gICAgYWRkRm9ybWF0VG9rZW4oJ2gnLCBbJ2hoJywgMl0sIDAsIGhGb3JtYXQpO1xuICAgIGFkZEZvcm1hdFRva2VuKCdrJywgWydraycsIDJdLCAwLCBrRm9ybWF0KTtcblxuICAgIGFkZEZvcm1hdFRva2VuKCdobW0nLCAwLCAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAnJyArIGhGb3JtYXQuYXBwbHkodGhpcykgKyB6ZXJvRmlsbCh0aGlzLm1pbnV0ZXMoKSwgMik7XG4gICAgfSk7XG5cbiAgICBhZGRGb3JtYXRUb2tlbignaG1tc3MnLCAwLCAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAnJyArIGhGb3JtYXQuYXBwbHkodGhpcykgKyB6ZXJvRmlsbCh0aGlzLm1pbnV0ZXMoKSwgMikgK1xuICAgICAgICAgICAgemVyb0ZpbGwodGhpcy5zZWNvbmRzKCksIDIpO1xuICAgIH0pO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ0htbScsIDAsIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuICcnICsgdGhpcy5ob3VycygpICsgemVyb0ZpbGwodGhpcy5taW51dGVzKCksIDIpO1xuICAgIH0pO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ0htbXNzJywgMCwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gJycgKyB0aGlzLmhvdXJzKCkgKyB6ZXJvRmlsbCh0aGlzLm1pbnV0ZXMoKSwgMikgK1xuICAgICAgICAgICAgemVyb0ZpbGwodGhpcy5zZWNvbmRzKCksIDIpO1xuICAgIH0pO1xuXG4gICAgZnVuY3Rpb24gbWVyaWRpZW0gKHRva2VuLCBsb3dlcmNhc2UpIHtcbiAgICAgICAgYWRkRm9ybWF0VG9rZW4odG9rZW4sIDAsIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmxvY2FsZURhdGEoKS5tZXJpZGllbSh0aGlzLmhvdXJzKCksIHRoaXMubWludXRlcygpLCBsb3dlcmNhc2UpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBtZXJpZGllbSgnYScsIHRydWUpO1xuICAgIG1lcmlkaWVtKCdBJywgZmFsc2UpO1xuXG4gICAgLy8gQUxJQVNFU1xuXG4gICAgYWRkVW5pdEFsaWFzKCdob3VyJywgJ2gnKTtcblxuICAgIC8vIFBSSU9SSVRZXG4gICAgYWRkVW5pdFByaW9yaXR5KCdob3VyJywgMTMpO1xuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgZnVuY3Rpb24gbWF0Y2hNZXJpZGllbSAoaXNTdHJpY3QsIGxvY2FsZSkge1xuICAgICAgICByZXR1cm4gbG9jYWxlLl9tZXJpZGllbVBhcnNlO1xuICAgIH1cblxuICAgIGFkZFJlZ2V4VG9rZW4oJ2EnLCAgbWF0Y2hNZXJpZGllbSk7XG4gICAgYWRkUmVnZXhUb2tlbignQScsICBtYXRjaE1lcmlkaWVtKTtcbiAgICBhZGRSZWdleFRva2VuKCdIJywgIG1hdGNoMXRvMik7XG4gICAgYWRkUmVnZXhUb2tlbignaCcsICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ0hIJywgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2hoJywgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuXG4gICAgYWRkUmVnZXhUb2tlbignaG1tJywgbWF0Y2gzdG80KTtcbiAgICBhZGRSZWdleFRva2VuKCdobW1zcycsIG1hdGNoNXRvNik7XG4gICAgYWRkUmVnZXhUb2tlbignSG1tJywgbWF0Y2gzdG80KTtcbiAgICBhZGRSZWdleFRva2VuKCdIbW1zcycsIG1hdGNoNXRvNik7XG5cbiAgICBhZGRQYXJzZVRva2VuKFsnSCcsICdISCddLCBIT1VSKTtcbiAgICBhZGRQYXJzZVRva2VuKFsnYScsICdBJ10sIGZ1bmN0aW9uIChpbnB1dCwgYXJyYXksIGNvbmZpZykge1xuICAgICAgICBjb25maWcuX2lzUG0gPSBjb25maWcuX2xvY2FsZS5pc1BNKGlucHV0KTtcbiAgICAgICAgY29uZmlnLl9tZXJpZGllbSA9IGlucHV0O1xuICAgIH0pO1xuICAgIGFkZFBhcnNlVG9rZW4oWydoJywgJ2hoJ10sIGZ1bmN0aW9uIChpbnB1dCwgYXJyYXksIGNvbmZpZykge1xuICAgICAgICBhcnJheVtIT1VSXSA9IHRvSW50KGlucHV0KTtcbiAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuYmlnSG91ciA9IHRydWU7XG4gICAgfSk7XG4gICAgYWRkUGFyc2VUb2tlbignaG1tJywgZnVuY3Rpb24gKGlucHV0LCBhcnJheSwgY29uZmlnKSB7XG4gICAgICAgIHZhciBwb3MgPSBpbnB1dC5sZW5ndGggLSAyO1xuICAgICAgICBhcnJheVtIT1VSXSA9IHRvSW50KGlucHV0LnN1YnN0cigwLCBwb3MpKTtcbiAgICAgICAgYXJyYXlbTUlOVVRFXSA9IHRvSW50KGlucHV0LnN1YnN0cihwb3MpKTtcbiAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuYmlnSG91ciA9IHRydWU7XG4gICAgfSk7XG4gICAgYWRkUGFyc2VUb2tlbignaG1tc3MnLCBmdW5jdGlvbiAoaW5wdXQsIGFycmF5LCBjb25maWcpIHtcbiAgICAgICAgdmFyIHBvczEgPSBpbnB1dC5sZW5ndGggLSA0O1xuICAgICAgICB2YXIgcG9zMiA9IGlucHV0Lmxlbmd0aCAtIDI7XG4gICAgICAgIGFycmF5W0hPVVJdID0gdG9JbnQoaW5wdXQuc3Vic3RyKDAsIHBvczEpKTtcbiAgICAgICAgYXJyYXlbTUlOVVRFXSA9IHRvSW50KGlucHV0LnN1YnN0cihwb3MxLCAyKSk7XG4gICAgICAgIGFycmF5W1NFQ09ORF0gPSB0b0ludChpbnB1dC5zdWJzdHIocG9zMikpO1xuICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5iaWdIb3VyID0gdHJ1ZTtcbiAgICB9KTtcbiAgICBhZGRQYXJzZVRva2VuKCdIbW0nLCBmdW5jdGlvbiAoaW5wdXQsIGFycmF5LCBjb25maWcpIHtcbiAgICAgICAgdmFyIHBvcyA9IGlucHV0Lmxlbmd0aCAtIDI7XG4gICAgICAgIGFycmF5W0hPVVJdID0gdG9JbnQoaW5wdXQuc3Vic3RyKDAsIHBvcykpO1xuICAgICAgICBhcnJheVtNSU5VVEVdID0gdG9JbnQoaW5wdXQuc3Vic3RyKHBvcykpO1xuICAgIH0pO1xuICAgIGFkZFBhcnNlVG9rZW4oJ0htbXNzJywgZnVuY3Rpb24gKGlucHV0LCBhcnJheSwgY29uZmlnKSB7XG4gICAgICAgIHZhciBwb3MxID0gaW5wdXQubGVuZ3RoIC0gNDtcbiAgICAgICAgdmFyIHBvczIgPSBpbnB1dC5sZW5ndGggLSAyO1xuICAgICAgICBhcnJheVtIT1VSXSA9IHRvSW50KGlucHV0LnN1YnN0cigwLCBwb3MxKSk7XG4gICAgICAgIGFycmF5W01JTlVURV0gPSB0b0ludChpbnB1dC5zdWJzdHIocG9zMSwgMikpO1xuICAgICAgICBhcnJheVtTRUNPTkRdID0gdG9JbnQoaW5wdXQuc3Vic3RyKHBvczIpKTtcbiAgICB9KTtcblxuICAgIC8vIExPQ0FMRVNcblxuICAgIGZ1bmN0aW9uIGxvY2FsZUlzUE0gKGlucHV0KSB7XG4gICAgICAgIC8vIElFOCBRdWlya3MgTW9kZSAmIElFNyBTdGFuZGFyZHMgTW9kZSBkbyBub3QgYWxsb3cgYWNjZXNzaW5nIHN0cmluZ3MgbGlrZSBhcnJheXNcbiAgICAgICAgLy8gVXNpbmcgY2hhckF0IHNob3VsZCBiZSBtb3JlIGNvbXBhdGlibGUuXG4gICAgICAgIHJldHVybiAoKGlucHV0ICsgJycpLnRvTG93ZXJDYXNlKCkuY2hhckF0KDApID09PSAncCcpO1xuICAgIH1cblxuICAgIHZhciBkZWZhdWx0TG9jYWxlTWVyaWRpZW1QYXJzZSA9IC9bYXBdXFwuP20/XFwuPy9pO1xuICAgIGZ1bmN0aW9uIGxvY2FsZU1lcmlkaWVtIChob3VycywgbWludXRlcywgaXNMb3dlcikge1xuICAgICAgICBpZiAoaG91cnMgPiAxMSkge1xuICAgICAgICAgICAgcmV0dXJuIGlzTG93ZXIgPyAncG0nIDogJ1BNJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBpc0xvd2VyID8gJ2FtJyA6ICdBTSc7XG4gICAgICAgIH1cbiAgICB9XG5cblxuICAgIC8vIE1PTUVOVFNcblxuICAgIC8vIFNldHRpbmcgdGhlIGhvdXIgc2hvdWxkIGtlZXAgdGhlIHRpbWUsIGJlY2F1c2UgdGhlIHVzZXIgZXhwbGljaXRseVxuICAgIC8vIHNwZWNpZmllZCB3aGljaCBob3VyIGhlIHdhbnRzLiBTbyB0cnlpbmcgdG8gbWFpbnRhaW4gdGhlIHNhbWUgaG91ciAoaW5cbiAgICAvLyBhIG5ldyB0aW1lem9uZSkgbWFrZXMgc2Vuc2UuIEFkZGluZy9zdWJ0cmFjdGluZyBob3VycyBkb2VzIG5vdCBmb2xsb3dcbiAgICAvLyB0aGlzIHJ1bGUuXG4gICAgdmFyIGdldFNldEhvdXIgPSBtYWtlR2V0U2V0KCdIb3VycycsIHRydWUpO1xuXG4gICAgdmFyIGJhc2VDb25maWcgPSB7XG4gICAgICAgIGNhbGVuZGFyOiBkZWZhdWx0Q2FsZW5kYXIsXG4gICAgICAgIGxvbmdEYXRlRm9ybWF0OiBkZWZhdWx0TG9uZ0RhdGVGb3JtYXQsXG4gICAgICAgIGludmFsaWREYXRlOiBkZWZhdWx0SW52YWxpZERhdGUsXG4gICAgICAgIG9yZGluYWw6IGRlZmF1bHRPcmRpbmFsLFxuICAgICAgICBvcmRpbmFsUGFyc2U6IGRlZmF1bHRPcmRpbmFsUGFyc2UsXG4gICAgICAgIHJlbGF0aXZlVGltZTogZGVmYXVsdFJlbGF0aXZlVGltZSxcblxuICAgICAgICBtb250aHM6IGRlZmF1bHRMb2NhbGVNb250aHMsXG4gICAgICAgIG1vbnRoc1Nob3J0OiBkZWZhdWx0TG9jYWxlTW9udGhzU2hvcnQsXG5cbiAgICAgICAgd2VlazogZGVmYXVsdExvY2FsZVdlZWssXG5cbiAgICAgICAgd2Vla2RheXM6IGRlZmF1bHRMb2NhbGVXZWVrZGF5cyxcbiAgICAgICAgd2Vla2RheXNNaW46IGRlZmF1bHRMb2NhbGVXZWVrZGF5c01pbixcbiAgICAgICAgd2Vla2RheXNTaG9ydDogZGVmYXVsdExvY2FsZVdlZWtkYXlzU2hvcnQsXG5cbiAgICAgICAgbWVyaWRpZW1QYXJzZTogZGVmYXVsdExvY2FsZU1lcmlkaWVtUGFyc2VcbiAgICB9O1xuXG4gICAgLy8gaW50ZXJuYWwgc3RvcmFnZSBmb3IgbG9jYWxlIGNvbmZpZyBmaWxlc1xuICAgIHZhciBsb2NhbGVzID0ge307XG4gICAgdmFyIGdsb2JhbExvY2FsZTtcblxuICAgIGZ1bmN0aW9uIG5vcm1hbGl6ZUxvY2FsZShrZXkpIHtcbiAgICAgICAgcmV0dXJuIGtleSA/IGtleS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoJ18nLCAnLScpIDoga2V5O1xuICAgIH1cblxuICAgIC8vIHBpY2sgdGhlIGxvY2FsZSBmcm9tIHRoZSBhcnJheVxuICAgIC8vIHRyeSBbJ2VuLWF1JywgJ2VuLWdiJ10gYXMgJ2VuLWF1JywgJ2VuLWdiJywgJ2VuJywgYXMgaW4gbW92ZSB0aHJvdWdoIHRoZSBsaXN0IHRyeWluZyBlYWNoXG4gICAgLy8gc3Vic3RyaW5nIGZyb20gbW9zdCBzcGVjaWZpYyB0byBsZWFzdCwgYnV0IG1vdmUgdG8gdGhlIG5leHQgYXJyYXkgaXRlbSBpZiBpdCdzIGEgbW9yZSBzcGVjaWZpYyB2YXJpYW50IHRoYW4gdGhlIGN1cnJlbnQgcm9vdFxuICAgIGZ1bmN0aW9uIGNob29zZUxvY2FsZShuYW1lcykge1xuICAgICAgICB2YXIgaSA9IDAsIGosIG5leHQsIGxvY2FsZSwgc3BsaXQ7XG5cbiAgICAgICAgd2hpbGUgKGkgPCBuYW1lcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHNwbGl0ID0gbm9ybWFsaXplTG9jYWxlKG5hbWVzW2ldKS5zcGxpdCgnLScpO1xuICAgICAgICAgICAgaiA9IHNwbGl0Lmxlbmd0aDtcbiAgICAgICAgICAgIG5leHQgPSBub3JtYWxpemVMb2NhbGUobmFtZXNbaSArIDFdKTtcbiAgICAgICAgICAgIG5leHQgPSBuZXh0ID8gbmV4dC5zcGxpdCgnLScpIDogbnVsbDtcbiAgICAgICAgICAgIHdoaWxlIChqID4gMCkge1xuICAgICAgICAgICAgICAgIGxvY2FsZSA9IGxvYWRMb2NhbGUoc3BsaXQuc2xpY2UoMCwgaikuam9pbignLScpKTtcbiAgICAgICAgICAgICAgICBpZiAobG9jYWxlKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBsb2NhbGU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChuZXh0ICYmIG5leHQubGVuZ3RoID49IGogJiYgY29tcGFyZUFycmF5cyhzcGxpdCwgbmV4dCwgdHJ1ZSkgPj0gaiAtIDEpIHtcbiAgICAgICAgICAgICAgICAgICAgLy90aGUgbmV4dCBhcnJheSBpdGVtIGlzIGJldHRlciB0aGFuIGEgc2hhbGxvd2VyIHN1YnN0cmluZyBvZiB0aGlzIG9uZVxuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgai0tO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaSsrO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGxvYWRMb2NhbGUobmFtZSkge1xuICAgICAgICB2YXIgb2xkTG9jYWxlID0gbnVsbDtcbiAgICAgICAgLy8gVE9ETzogRmluZCBhIGJldHRlciB3YXkgdG8gcmVnaXN0ZXIgYW5kIGxvYWQgYWxsIHRoZSBsb2NhbGVzIGluIE5vZGVcbiAgICAgICAgaWYgKCFsb2NhbGVzW25hbWVdICYmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJykgJiZcbiAgICAgICAgICAgICAgICBtb2R1bGUgJiYgbW9kdWxlLmV4cG9ydHMpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgb2xkTG9jYWxlID0gZ2xvYmFsTG9jYWxlLl9hYmJyO1xuICAgICAgICAgICAgICAgIHJlcXVpcmUoJy4vbG9jYWxlLycgKyBuYW1lKTtcbiAgICAgICAgICAgICAgICAvLyBiZWNhdXNlIGRlZmluZUxvY2FsZSBjdXJyZW50bHkgYWxzbyBzZXRzIHRoZSBnbG9iYWwgbG9jYWxlLCB3ZVxuICAgICAgICAgICAgICAgIC8vIHdhbnQgdG8gdW5kbyB0aGF0IGZvciBsYXp5IGxvYWRlZCBsb2NhbGVzXG4gICAgICAgICAgICAgICAgbG9jYWxlX2xvY2FsZXNfX2dldFNldEdsb2JhbExvY2FsZShvbGRMb2NhbGUpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkgeyB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxvY2FsZXNbbmFtZV07XG4gICAgfVxuXG4gICAgLy8gVGhpcyBmdW5jdGlvbiB3aWxsIGxvYWQgbG9jYWxlIGFuZCB0aGVuIHNldCB0aGUgZ2xvYmFsIGxvY2FsZS4gIElmXG4gICAgLy8gbm8gYXJndW1lbnRzIGFyZSBwYXNzZWQgaW4sIGl0IHdpbGwgc2ltcGx5IHJldHVybiB0aGUgY3VycmVudCBnbG9iYWxcbiAgICAvLyBsb2NhbGUga2V5LlxuICAgIGZ1bmN0aW9uIGxvY2FsZV9sb2NhbGVzX19nZXRTZXRHbG9iYWxMb2NhbGUgKGtleSwgdmFsdWVzKSB7XG4gICAgICAgIHZhciBkYXRhO1xuICAgICAgICBpZiAoa2V5KSB7XG4gICAgICAgICAgICBpZiAoaXNVbmRlZmluZWQodmFsdWVzKSkge1xuICAgICAgICAgICAgICAgIGRhdGEgPSBsb2NhbGVfbG9jYWxlc19fZ2V0TG9jYWxlKGtleSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBkYXRhID0gZGVmaW5lTG9jYWxlKGtleSwgdmFsdWVzKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgICAgICAvLyBtb21lbnQuZHVyYXRpb24uX2xvY2FsZSA9IG1vbWVudC5fbG9jYWxlID0gZGF0YTtcbiAgICAgICAgICAgICAgICBnbG9iYWxMb2NhbGUgPSBkYXRhO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGdsb2JhbExvY2FsZS5fYWJicjtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBkZWZpbmVMb2NhbGUgKG5hbWUsIGNvbmZpZykge1xuICAgICAgICBpZiAoY29uZmlnICE9PSBudWxsKSB7XG4gICAgICAgICAgICB2YXIgcGFyZW50Q29uZmlnID0gYmFzZUNvbmZpZztcbiAgICAgICAgICAgIGNvbmZpZy5hYmJyID0gbmFtZTtcbiAgICAgICAgICAgIGlmIChsb2NhbGVzW25hbWVdICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICBkZXByZWNhdGVTaW1wbGUoJ2RlZmluZUxvY2FsZU92ZXJyaWRlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICd1c2UgbW9tZW50LnVwZGF0ZUxvY2FsZShsb2NhbGVOYW1lLCBjb25maWcpIHRvIGNoYW5nZSAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICdhbiBleGlzdGluZyBsb2NhbGUuIG1vbWVudC5kZWZpbmVMb2NhbGUobG9jYWxlTmFtZSwgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnY29uZmlnKSBzaG91bGQgb25seSBiZSB1c2VkIGZvciBjcmVhdGluZyBhIG5ldyBsb2NhbGUgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnU2VlIGh0dHA6Ly9tb21lbnRqcy5jb20vZ3VpZGVzLyMvd2FybmluZ3MvZGVmaW5lLWxvY2FsZS8gZm9yIG1vcmUgaW5mby4nKTtcbiAgICAgICAgICAgICAgICBwYXJlbnRDb25maWcgPSBsb2NhbGVzW25hbWVdLl9jb25maWc7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGNvbmZpZy5wYXJlbnRMb2NhbGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGlmIChsb2NhbGVzW2NvbmZpZy5wYXJlbnRMb2NhbGVdICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgcGFyZW50Q29uZmlnID0gbG9jYWxlc1tjb25maWcucGFyZW50TG9jYWxlXS5fY29uZmlnO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHRyZWF0IGFzIGlmIHRoZXJlIGlzIG5vIGJhc2UgY29uZmlnXG4gICAgICAgICAgICAgICAgICAgIGRlcHJlY2F0ZVNpbXBsZSgncGFyZW50TG9jYWxlVW5kZWZpbmVkJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc3BlY2lmaWVkIHBhcmVudExvY2FsZSBpcyBub3QgZGVmaW5lZCB5ZXQuIFNlZSBodHRwOi8vbW9tZW50anMuY29tL2d1aWRlcy8jL3dhcm5pbmdzL3BhcmVudC1sb2NhbGUvJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbG9jYWxlc1tuYW1lXSA9IG5ldyBMb2NhbGUobWVyZ2VDb25maWdzKHBhcmVudENvbmZpZywgY29uZmlnKSk7XG5cbiAgICAgICAgICAgIC8vIGJhY2t3YXJkcyBjb21wYXQgZm9yIG5vdzogYWxzbyBzZXQgdGhlIGxvY2FsZVxuICAgICAgICAgICAgbG9jYWxlX2xvY2FsZXNfX2dldFNldEdsb2JhbExvY2FsZShuYW1lKTtcblxuICAgICAgICAgICAgcmV0dXJuIGxvY2FsZXNbbmFtZV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyB1c2VmdWwgZm9yIHRlc3RpbmdcbiAgICAgICAgICAgIGRlbGV0ZSBsb2NhbGVzW25hbWVdO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiB1cGRhdGVMb2NhbGUobmFtZSwgY29uZmlnKSB7XG4gICAgICAgIGlmIChjb25maWcgIT0gbnVsbCkge1xuICAgICAgICAgICAgdmFyIGxvY2FsZSwgcGFyZW50Q29uZmlnID0gYmFzZUNvbmZpZztcbiAgICAgICAgICAgIC8vIE1FUkdFXG4gICAgICAgICAgICBpZiAobG9jYWxlc1tuYW1lXSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgcGFyZW50Q29uZmlnID0gbG9jYWxlc1tuYW1lXS5fY29uZmlnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uZmlnID0gbWVyZ2VDb25maWdzKHBhcmVudENvbmZpZywgY29uZmlnKTtcbiAgICAgICAgICAgIGxvY2FsZSA9IG5ldyBMb2NhbGUoY29uZmlnKTtcbiAgICAgICAgICAgIGxvY2FsZS5wYXJlbnRMb2NhbGUgPSBsb2NhbGVzW25hbWVdO1xuICAgICAgICAgICAgbG9jYWxlc1tuYW1lXSA9IGxvY2FsZTtcblxuICAgICAgICAgICAgLy8gYmFja3dhcmRzIGNvbXBhdCBmb3Igbm93OiBhbHNvIHNldCB0aGUgbG9jYWxlXG4gICAgICAgICAgICBsb2NhbGVfbG9jYWxlc19fZ2V0U2V0R2xvYmFsTG9jYWxlKG5hbWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gcGFzcyBudWxsIGZvciBjb25maWcgdG8gdW51cGRhdGUsIHVzZWZ1bCBmb3IgdGVzdHNcbiAgICAgICAgICAgIGlmIChsb2NhbGVzW25hbWVdICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICBpZiAobG9jYWxlc1tuYW1lXS5wYXJlbnRMb2NhbGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBsb2NhbGVzW25hbWVdID0gbG9jYWxlc1tuYW1lXS5wYXJlbnRMb2NhbGU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChsb2NhbGVzW25hbWVdICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGxvY2FsZXNbbmFtZV07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsb2NhbGVzW25hbWVdO1xuICAgIH1cblxuICAgIC8vIHJldHVybnMgbG9jYWxlIGRhdGFcbiAgICBmdW5jdGlvbiBsb2NhbGVfbG9jYWxlc19fZ2V0TG9jYWxlIChrZXkpIHtcbiAgICAgICAgdmFyIGxvY2FsZTtcblxuICAgICAgICBpZiAoa2V5ICYmIGtleS5fbG9jYWxlICYmIGtleS5fbG9jYWxlLl9hYmJyKSB7XG4gICAgICAgICAgICBrZXkgPSBrZXkuX2xvY2FsZS5fYWJicjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgha2V5KSB7XG4gICAgICAgICAgICByZXR1cm4gZ2xvYmFsTG9jYWxlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpc0FycmF5KGtleSkpIHtcbiAgICAgICAgICAgIC8vc2hvcnQtY2lyY3VpdCBldmVyeXRoaW5nIGVsc2VcbiAgICAgICAgICAgIGxvY2FsZSA9IGxvYWRMb2NhbGUoa2V5KTtcbiAgICAgICAgICAgIGlmIChsb2NhbGUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbG9jYWxlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAga2V5ID0gW2tleV07XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY2hvb3NlTG9jYWxlKGtleSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbG9jYWxlX2xvY2FsZXNfX2xpc3RMb2NhbGVzKCkge1xuICAgICAgICByZXR1cm4ga2V5cyhsb2NhbGVzKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjaGVja092ZXJmbG93IChtKSB7XG4gICAgICAgIHZhciBvdmVyZmxvdztcbiAgICAgICAgdmFyIGEgPSBtLl9hO1xuXG4gICAgICAgIGlmIChhICYmIGdldFBhcnNpbmdGbGFncyhtKS5vdmVyZmxvdyA9PT0gLTIpIHtcbiAgICAgICAgICAgIG92ZXJmbG93ID1cbiAgICAgICAgICAgICAgICBhW01PTlRIXSAgICAgICA8IDAgfHwgYVtNT05USF0gICAgICAgPiAxMSAgPyBNT05USCA6XG4gICAgICAgICAgICAgICAgYVtEQVRFXSAgICAgICAgPCAxIHx8IGFbREFURV0gICAgICAgID4gZGF5c0luTW9udGgoYVtZRUFSXSwgYVtNT05USF0pID8gREFURSA6XG4gICAgICAgICAgICAgICAgYVtIT1VSXSAgICAgICAgPCAwIHx8IGFbSE9VUl0gICAgICAgID4gMjQgfHwgKGFbSE9VUl0gPT09IDI0ICYmIChhW01JTlVURV0gIT09IDAgfHwgYVtTRUNPTkRdICE9PSAwIHx8IGFbTUlMTElTRUNPTkRdICE9PSAwKSkgPyBIT1VSIDpcbiAgICAgICAgICAgICAgICBhW01JTlVURV0gICAgICA8IDAgfHwgYVtNSU5VVEVdICAgICAgPiA1OSAgPyBNSU5VVEUgOlxuICAgICAgICAgICAgICAgIGFbU0VDT05EXSAgICAgIDwgMCB8fCBhW1NFQ09ORF0gICAgICA+IDU5ICA/IFNFQ09ORCA6XG4gICAgICAgICAgICAgICAgYVtNSUxMSVNFQ09ORF0gPCAwIHx8IGFbTUlMTElTRUNPTkRdID4gOTk5ID8gTUlMTElTRUNPTkQgOlxuICAgICAgICAgICAgICAgIC0xO1xuXG4gICAgICAgICAgICBpZiAoZ2V0UGFyc2luZ0ZsYWdzKG0pLl9vdmVyZmxvd0RheU9mWWVhciAmJiAob3ZlcmZsb3cgPCBZRUFSIHx8IG92ZXJmbG93ID4gREFURSkpIHtcbiAgICAgICAgICAgICAgICBvdmVyZmxvdyA9IERBVEU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZ2V0UGFyc2luZ0ZsYWdzKG0pLl9vdmVyZmxvd1dlZWtzICYmIG92ZXJmbG93ID09PSAtMSkge1xuICAgICAgICAgICAgICAgIG92ZXJmbG93ID0gV0VFSztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChnZXRQYXJzaW5nRmxhZ3MobSkuX292ZXJmbG93V2Vla2RheSAmJiBvdmVyZmxvdyA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBvdmVyZmxvdyA9IFdFRUtEQVk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGdldFBhcnNpbmdGbGFncyhtKS5vdmVyZmxvdyA9IG92ZXJmbG93O1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG07XG4gICAgfVxuXG4gICAgLy8gaXNvIDg2MDEgcmVnZXhcbiAgICAvLyAwMDAwLTAwLTAwIDAwMDAtVzAwIG9yIDAwMDAtVzAwLTAgKyBUICsgMDAgb3IgMDA6MDAgb3IgMDA6MDA6MDAgb3IgMDA6MDA6MDAuMDAwICsgKzAwOjAwIG9yICswMDAwIG9yICswMClcbiAgICB2YXIgZXh0ZW5kZWRJc29SZWdleCA9IC9eXFxzKigoPzpbKy1dXFxkezZ9fFxcZHs0fSktKD86XFxkXFxkLVxcZFxcZHxXXFxkXFxkLVxcZHxXXFxkXFxkfFxcZFxcZFxcZHxcXGRcXGQpKSg/OihUfCApKFxcZFxcZCg/OjpcXGRcXGQoPzo6XFxkXFxkKD86Wy4sXVxcZCspPyk/KT8pKFtcXCtcXC1dXFxkXFxkKD86Oj9cXGRcXGQpP3xcXHMqWik/KT8vO1xuICAgIHZhciBiYXNpY0lzb1JlZ2V4ID0gL15cXHMqKCg/OlsrLV1cXGR7Nn18XFxkezR9KSg/OlxcZFxcZFxcZFxcZHxXXFxkXFxkXFxkfFdcXGRcXGR8XFxkXFxkXFxkfFxcZFxcZCkpKD86KFR8ICkoXFxkXFxkKD86XFxkXFxkKD86XFxkXFxkKD86Wy4sXVxcZCspPyk/KT8pKFtcXCtcXC1dXFxkXFxkKD86Oj9cXGRcXGQpP3xcXHMqWik/KT8vO1xuXG4gICAgdmFyIHR6UmVnZXggPSAvWnxbKy1dXFxkXFxkKD86Oj9cXGRcXGQpPy87XG5cbiAgICB2YXIgaXNvRGF0ZXMgPSBbXG4gICAgICAgIFsnWVlZWVlZLU1NLUREJywgL1srLV1cXGR7Nn0tXFxkXFxkLVxcZFxcZC9dLFxuICAgICAgICBbJ1lZWVktTU0tREQnLCAvXFxkezR9LVxcZFxcZC1cXGRcXGQvXSxcbiAgICAgICAgWydHR0dHLVtXXVdXLUUnLCAvXFxkezR9LVdcXGRcXGQtXFxkL10sXG4gICAgICAgIFsnR0dHRy1bV11XVycsIC9cXGR7NH0tV1xcZFxcZC8sIGZhbHNlXSxcbiAgICAgICAgWydZWVlZLURERCcsIC9cXGR7NH0tXFxkezN9L10sXG4gICAgICAgIFsnWVlZWS1NTScsIC9cXGR7NH0tXFxkXFxkLywgZmFsc2VdLFxuICAgICAgICBbJ1lZWVlZWU1NREQnLCAvWystXVxcZHsxMH0vXSxcbiAgICAgICAgWydZWVlZTU1ERCcsIC9cXGR7OH0vXSxcbiAgICAgICAgLy8gWVlZWU1NIGlzIE5PVCBhbGxvd2VkIGJ5IHRoZSBzdGFuZGFyZFxuICAgICAgICBbJ0dHR0dbV11XV0UnLCAvXFxkezR9V1xcZHszfS9dLFxuICAgICAgICBbJ0dHR0dbV11XVycsIC9cXGR7NH1XXFxkezJ9LywgZmFsc2VdLFxuICAgICAgICBbJ1lZWVlEREQnLCAvXFxkezd9L11cbiAgICBdO1xuXG4gICAgLy8gaXNvIHRpbWUgZm9ybWF0cyBhbmQgcmVnZXhlc1xuICAgIHZhciBpc29UaW1lcyA9IFtcbiAgICAgICAgWydISDptbTpzcy5TU1NTJywgL1xcZFxcZDpcXGRcXGQ6XFxkXFxkXFwuXFxkKy9dLFxuICAgICAgICBbJ0hIOm1tOnNzLFNTU1MnLCAvXFxkXFxkOlxcZFxcZDpcXGRcXGQsXFxkKy9dLFxuICAgICAgICBbJ0hIOm1tOnNzJywgL1xcZFxcZDpcXGRcXGQ6XFxkXFxkL10sXG4gICAgICAgIFsnSEg6bW0nLCAvXFxkXFxkOlxcZFxcZC9dLFxuICAgICAgICBbJ0hIbW1zcy5TU1NTJywgL1xcZFxcZFxcZFxcZFxcZFxcZFxcLlxcZCsvXSxcbiAgICAgICAgWydISG1tc3MsU1NTUycsIC9cXGRcXGRcXGRcXGRcXGRcXGQsXFxkKy9dLFxuICAgICAgICBbJ0hIbW1zcycsIC9cXGRcXGRcXGRcXGRcXGRcXGQvXSxcbiAgICAgICAgWydISG1tJywgL1xcZFxcZFxcZFxcZC9dLFxuICAgICAgICBbJ0hIJywgL1xcZFxcZC9dXG4gICAgXTtcblxuICAgIHZhciBhc3BOZXRKc29uUmVnZXggPSAvXlxcLz9EYXRlXFwoKFxcLT9cXGQrKS9pO1xuXG4gICAgLy8gZGF0ZSBmcm9tIGlzbyBmb3JtYXRcbiAgICBmdW5jdGlvbiBjb25maWdGcm9tSVNPKGNvbmZpZykge1xuICAgICAgICB2YXIgaSwgbCxcbiAgICAgICAgICAgIHN0cmluZyA9IGNvbmZpZy5faSxcbiAgICAgICAgICAgIG1hdGNoID0gZXh0ZW5kZWRJc29SZWdleC5leGVjKHN0cmluZykgfHwgYmFzaWNJc29SZWdleC5leGVjKHN0cmluZyksXG4gICAgICAgICAgICBhbGxvd1RpbWUsIGRhdGVGb3JtYXQsIHRpbWVGb3JtYXQsIHR6Rm9ybWF0O1xuXG4gICAgICAgIGlmIChtYXRjaCkge1xuICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuaXNvID0gdHJ1ZTtcblxuICAgICAgICAgICAgZm9yIChpID0gMCwgbCA9IGlzb0RhdGVzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgICAgICAgIGlmIChpc29EYXRlc1tpXVsxXS5leGVjKG1hdGNoWzFdKSkge1xuICAgICAgICAgICAgICAgICAgICBkYXRlRm9ybWF0ID0gaXNvRGF0ZXNbaV1bMF07XG4gICAgICAgICAgICAgICAgICAgIGFsbG93VGltZSA9IGlzb0RhdGVzW2ldWzJdICE9PSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGRhdGVGb3JtYXQgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGNvbmZpZy5faXNWYWxpZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChtYXRjaFszXSkge1xuICAgICAgICAgICAgICAgIGZvciAoaSA9IDAsIGwgPSBpc29UaW1lcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGlzb1RpbWVzW2ldWzFdLmV4ZWMobWF0Y2hbM10pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBtYXRjaFsyXSBzaG91bGQgYmUgJ1QnIG9yIHNwYWNlXG4gICAgICAgICAgICAgICAgICAgICAgICB0aW1lRm9ybWF0ID0gKG1hdGNoWzJdIHx8ICcgJykgKyBpc29UaW1lc1tpXVswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICh0aW1lRm9ybWF0ID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uZmlnLl9pc1ZhbGlkID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIWFsbG93VGltZSAmJiB0aW1lRm9ybWF0ICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICBjb25maWcuX2lzVmFsaWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobWF0Y2hbNF0pIHtcbiAgICAgICAgICAgICAgICBpZiAodHpSZWdleC5leGVjKG1hdGNoWzRdKSkge1xuICAgICAgICAgICAgICAgICAgICB0ekZvcm1hdCA9ICdaJztcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBjb25maWcuX2lzVmFsaWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbmZpZy5fZiA9IGRhdGVGb3JtYXQgKyAodGltZUZvcm1hdCB8fCAnJykgKyAodHpGb3JtYXQgfHwgJycpO1xuICAgICAgICAgICAgY29uZmlnRnJvbVN0cmluZ0FuZEZvcm1hdChjb25maWcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uZmlnLl9pc1ZhbGlkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBkYXRlIGZyb20gaXNvIGZvcm1hdCBvciBmYWxsYmFja1xuICAgIGZ1bmN0aW9uIGNvbmZpZ0Zyb21TdHJpbmcoY29uZmlnKSB7XG4gICAgICAgIHZhciBtYXRjaGVkID0gYXNwTmV0SnNvblJlZ2V4LmV4ZWMoY29uZmlnLl9pKTtcblxuICAgICAgICBpZiAobWF0Y2hlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgY29uZmlnLl9kID0gbmV3IERhdGUoK21hdGNoZWRbMV0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uZmlnRnJvbUlTTyhjb25maWcpO1xuICAgICAgICBpZiAoY29uZmlnLl9pc1ZhbGlkID09PSBmYWxzZSkge1xuICAgICAgICAgICAgZGVsZXRlIGNvbmZpZy5faXNWYWxpZDtcbiAgICAgICAgICAgIHV0aWxzX2hvb2tzX19ob29rcy5jcmVhdGVGcm9tSW5wdXRGYWxsYmFjayhjb25maWcpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXRpbHNfaG9va3NfX2hvb2tzLmNyZWF0ZUZyb21JbnB1dEZhbGxiYWNrID0gZGVwcmVjYXRlKFxuICAgICAgICAnbW9tZW50IGNvbnN0cnVjdGlvbiBmYWxscyBiYWNrIHRvIGpzIERhdGUuIFRoaXMgaXMgJyArXG4gICAgICAgICdkaXNjb3VyYWdlZCBhbmQgd2lsbCBiZSByZW1vdmVkIGluIHVwY29taW5nIG1ham9yICcgK1xuICAgICAgICAncmVsZWFzZS4gUGxlYXNlIHJlZmVyIHRvICcgK1xuICAgICAgICAnaHR0cDovL21vbWVudGpzLmNvbS9ndWlkZXMvIy93YXJuaW5ncy9qcy1kYXRlLyBmb3IgbW9yZSBpbmZvLicsXG4gICAgICAgIGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICAgICAgICAgIGNvbmZpZy5fZCA9IG5ldyBEYXRlKGNvbmZpZy5faSArIChjb25maWcuX3VzZVVUQyA/ICcgVVRDJyA6ICcnKSk7XG4gICAgICAgIH1cbiAgICApO1xuXG4gICAgLy8gUGljayB0aGUgZmlyc3QgZGVmaW5lZCBvZiB0d28gb3IgdGhyZWUgYXJndW1lbnRzLlxuICAgIGZ1bmN0aW9uIGRlZmF1bHRzKGEsIGIsIGMpIHtcbiAgICAgICAgaWYgKGEgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIGE7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGIgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIGI7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGM7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY3VycmVudERhdGVBcnJheShjb25maWcpIHtcbiAgICAgICAgLy8gaG9va3MgaXMgYWN0dWFsbHkgdGhlIGV4cG9ydGVkIG1vbWVudCBvYmplY3RcbiAgICAgICAgdmFyIG5vd1ZhbHVlID0gbmV3IERhdGUodXRpbHNfaG9va3NfX2hvb2tzLm5vdygpKTtcbiAgICAgICAgaWYgKGNvbmZpZy5fdXNlVVRDKSB7XG4gICAgICAgICAgICByZXR1cm4gW25vd1ZhbHVlLmdldFVUQ0Z1bGxZZWFyKCksIG5vd1ZhbHVlLmdldFVUQ01vbnRoKCksIG5vd1ZhbHVlLmdldFVUQ0RhdGUoKV07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtub3dWYWx1ZS5nZXRGdWxsWWVhcigpLCBub3dWYWx1ZS5nZXRNb250aCgpLCBub3dWYWx1ZS5nZXREYXRlKCldO1xuICAgIH1cblxuICAgIC8vIGNvbnZlcnQgYW4gYXJyYXkgdG8gYSBkYXRlLlxuICAgIC8vIHRoZSBhcnJheSBzaG91bGQgbWlycm9yIHRoZSBwYXJhbWV0ZXJzIGJlbG93XG4gICAgLy8gbm90ZTogYWxsIHZhbHVlcyBwYXN0IHRoZSB5ZWFyIGFyZSBvcHRpb25hbCBhbmQgd2lsbCBkZWZhdWx0IHRvIHRoZSBsb3dlc3QgcG9zc2libGUgdmFsdWUuXG4gICAgLy8gW3llYXIsIG1vbnRoLCBkYXkgLCBob3VyLCBtaW51dGUsIHNlY29uZCwgbWlsbGlzZWNvbmRdXG4gICAgZnVuY3Rpb24gY29uZmlnRnJvbUFycmF5IChjb25maWcpIHtcbiAgICAgICAgdmFyIGksIGRhdGUsIGlucHV0ID0gW10sIGN1cnJlbnREYXRlLCB5ZWFyVG9Vc2U7XG5cbiAgICAgICAgaWYgKGNvbmZpZy5fZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY3VycmVudERhdGUgPSBjdXJyZW50RGF0ZUFycmF5KGNvbmZpZyk7XG5cbiAgICAgICAgLy9jb21wdXRlIGRheSBvZiB0aGUgeWVhciBmcm9tIHdlZWtzIGFuZCB3ZWVrZGF5c1xuICAgICAgICBpZiAoY29uZmlnLl93ICYmIGNvbmZpZy5fYVtEQVRFXSA9PSBudWxsICYmIGNvbmZpZy5fYVtNT05USF0gPT0gbnVsbCkge1xuICAgICAgICAgICAgZGF5T2ZZZWFyRnJvbVdlZWtJbmZvKGNvbmZpZyk7XG4gICAgICAgIH1cblxuICAgICAgICAvL2lmIHRoZSBkYXkgb2YgdGhlIHllYXIgaXMgc2V0LCBmaWd1cmUgb3V0IHdoYXQgaXQgaXNcbiAgICAgICAgaWYgKGNvbmZpZy5fZGF5T2ZZZWFyKSB7XG4gICAgICAgICAgICB5ZWFyVG9Vc2UgPSBkZWZhdWx0cyhjb25maWcuX2FbWUVBUl0sIGN1cnJlbnREYXRlW1lFQVJdKTtcblxuICAgICAgICAgICAgaWYgKGNvbmZpZy5fZGF5T2ZZZWFyID4gZGF5c0luWWVhcih5ZWFyVG9Vc2UpKSB7XG4gICAgICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuX292ZXJmbG93RGF5T2ZZZWFyID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZGF0ZSA9IGNyZWF0ZVVUQ0RhdGUoeWVhclRvVXNlLCAwLCBjb25maWcuX2RheU9mWWVhcik7XG4gICAgICAgICAgICBjb25maWcuX2FbTU9OVEhdID0gZGF0ZS5nZXRVVENNb250aCgpO1xuICAgICAgICAgICAgY29uZmlnLl9hW0RBVEVdID0gZGF0ZS5nZXRVVENEYXRlKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBEZWZhdWx0IHRvIGN1cnJlbnQgZGF0ZS5cbiAgICAgICAgLy8gKiBpZiBubyB5ZWFyLCBtb250aCwgZGF5IG9mIG1vbnRoIGFyZSBnaXZlbiwgZGVmYXVsdCB0byB0b2RheVxuICAgICAgICAvLyAqIGlmIGRheSBvZiBtb250aCBpcyBnaXZlbiwgZGVmYXVsdCBtb250aCBhbmQgeWVhclxuICAgICAgICAvLyAqIGlmIG1vbnRoIGlzIGdpdmVuLCBkZWZhdWx0IG9ubHkgeWVhclxuICAgICAgICAvLyAqIGlmIHllYXIgaXMgZ2l2ZW4sIGRvbid0IGRlZmF1bHQgYW55dGhpbmdcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IDMgJiYgY29uZmlnLl9hW2ldID09IG51bGw7ICsraSkge1xuICAgICAgICAgICAgY29uZmlnLl9hW2ldID0gaW5wdXRbaV0gPSBjdXJyZW50RGF0ZVtpXTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFplcm8gb3V0IHdoYXRldmVyIHdhcyBub3QgZGVmYXVsdGVkLCBpbmNsdWRpbmcgdGltZVxuICAgICAgICBmb3IgKDsgaSA8IDc7IGkrKykge1xuICAgICAgICAgICAgY29uZmlnLl9hW2ldID0gaW5wdXRbaV0gPSAoY29uZmlnLl9hW2ldID09IG51bGwpID8gKGkgPT09IDIgPyAxIDogMCkgOiBjb25maWcuX2FbaV07XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayBmb3IgMjQ6MDA6MDAuMDAwXG4gICAgICAgIGlmIChjb25maWcuX2FbSE9VUl0gPT09IDI0ICYmXG4gICAgICAgICAgICAgICAgY29uZmlnLl9hW01JTlVURV0gPT09IDAgJiZcbiAgICAgICAgICAgICAgICBjb25maWcuX2FbU0VDT05EXSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgIGNvbmZpZy5fYVtNSUxMSVNFQ09ORF0gPT09IDApIHtcbiAgICAgICAgICAgIGNvbmZpZy5fbmV4dERheSA9IHRydWU7XG4gICAgICAgICAgICBjb25maWcuX2FbSE9VUl0gPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uZmlnLl9kID0gKGNvbmZpZy5fdXNlVVRDID8gY3JlYXRlVVRDRGF0ZSA6IGNyZWF0ZURhdGUpLmFwcGx5KG51bGwsIGlucHV0KTtcbiAgICAgICAgLy8gQXBwbHkgdGltZXpvbmUgb2Zmc2V0IGZyb20gaW5wdXQuIFRoZSBhY3R1YWwgdXRjT2Zmc2V0IGNhbiBiZSBjaGFuZ2VkXG4gICAgICAgIC8vIHdpdGggcGFyc2Vab25lLlxuICAgICAgICBpZiAoY29uZmlnLl90em0gIT0gbnVsbCkge1xuICAgICAgICAgICAgY29uZmlnLl9kLnNldFVUQ01pbnV0ZXMoY29uZmlnLl9kLmdldFVUQ01pbnV0ZXMoKSAtIGNvbmZpZy5fdHptKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb25maWcuX25leHREYXkpIHtcbiAgICAgICAgICAgIGNvbmZpZy5fYVtIT1VSXSA9IDI0O1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZGF5T2ZZZWFyRnJvbVdlZWtJbmZvKGNvbmZpZykge1xuICAgICAgICB2YXIgdywgd2Vla1llYXIsIHdlZWssIHdlZWtkYXksIGRvdywgZG95LCB0ZW1wLCB3ZWVrZGF5T3ZlcmZsb3c7XG5cbiAgICAgICAgdyA9IGNvbmZpZy5fdztcbiAgICAgICAgaWYgKHcuR0cgIT0gbnVsbCB8fCB3LlcgIT0gbnVsbCB8fCB3LkUgIT0gbnVsbCkge1xuICAgICAgICAgICAgZG93ID0gMTtcbiAgICAgICAgICAgIGRveSA9IDQ7XG5cbiAgICAgICAgICAgIC8vIFRPRE86IFdlIG5lZWQgdG8gdGFrZSB0aGUgY3VycmVudCBpc29XZWVrWWVhciwgYnV0IHRoYXQgZGVwZW5kcyBvblxuICAgICAgICAgICAgLy8gaG93IHdlIGludGVycHJldCBub3cgKGxvY2FsLCB1dGMsIGZpeGVkIG9mZnNldCkuIFNvIGNyZWF0ZVxuICAgICAgICAgICAgLy8gYSBub3cgdmVyc2lvbiBvZiBjdXJyZW50IGNvbmZpZyAodGFrZSBsb2NhbC91dGMvb2Zmc2V0IGZsYWdzLCBhbmRcbiAgICAgICAgICAgIC8vIGNyZWF0ZSBub3cpLlxuICAgICAgICAgICAgd2Vla1llYXIgPSBkZWZhdWx0cyh3LkdHLCBjb25maWcuX2FbWUVBUl0sIHdlZWtPZlllYXIobG9jYWxfX2NyZWF0ZUxvY2FsKCksIDEsIDQpLnllYXIpO1xuICAgICAgICAgICAgd2VlayA9IGRlZmF1bHRzKHcuVywgMSk7XG4gICAgICAgICAgICB3ZWVrZGF5ID0gZGVmYXVsdHMody5FLCAxKTtcbiAgICAgICAgICAgIGlmICh3ZWVrZGF5IDwgMSB8fCB3ZWVrZGF5ID4gNykge1xuICAgICAgICAgICAgICAgIHdlZWtkYXlPdmVyZmxvdyA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkb3cgPSBjb25maWcuX2xvY2FsZS5fd2Vlay5kb3c7XG4gICAgICAgICAgICBkb3kgPSBjb25maWcuX2xvY2FsZS5fd2Vlay5kb3k7XG5cbiAgICAgICAgICAgIHdlZWtZZWFyID0gZGVmYXVsdHMody5nZywgY29uZmlnLl9hW1lFQVJdLCB3ZWVrT2ZZZWFyKGxvY2FsX19jcmVhdGVMb2NhbCgpLCBkb3csIGRveSkueWVhcik7XG4gICAgICAgICAgICB3ZWVrID0gZGVmYXVsdHMody53LCAxKTtcblxuICAgICAgICAgICAgaWYgKHcuZCAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgLy8gd2Vla2RheSAtLSBsb3cgZGF5IG51bWJlcnMgYXJlIGNvbnNpZGVyZWQgbmV4dCB3ZWVrXG4gICAgICAgICAgICAgICAgd2Vla2RheSA9IHcuZDtcbiAgICAgICAgICAgICAgICBpZiAod2Vla2RheSA8IDAgfHwgd2Vla2RheSA+IDYpIHtcbiAgICAgICAgICAgICAgICAgICAgd2Vla2RheU92ZXJmbG93ID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHcuZSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgLy8gbG9jYWwgd2Vla2RheSAtLSBjb3VudGluZyBzdGFydHMgZnJvbSBiZWdpbmluZyBvZiB3ZWVrXG4gICAgICAgICAgICAgICAgd2Vla2RheSA9IHcuZSArIGRvdztcbiAgICAgICAgICAgICAgICBpZiAody5lIDwgMCB8fCB3LmUgPiA2KSB7XG4gICAgICAgICAgICAgICAgICAgIHdlZWtkYXlPdmVyZmxvdyA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBkZWZhdWx0IHRvIGJlZ2luaW5nIG9mIHdlZWtcbiAgICAgICAgICAgICAgICB3ZWVrZGF5ID0gZG93O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh3ZWVrIDwgMSB8fCB3ZWVrID4gd2Vla3NJblllYXIod2Vla1llYXIsIGRvdywgZG95KSkge1xuICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuX292ZXJmbG93V2Vla3MgPSB0cnVlO1xuICAgICAgICB9IGVsc2UgaWYgKHdlZWtkYXlPdmVyZmxvdyAhPSBudWxsKSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5fb3ZlcmZsb3dXZWVrZGF5ID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRlbXAgPSBkYXlPZlllYXJGcm9tV2Vla3Mod2Vla1llYXIsIHdlZWssIHdlZWtkYXksIGRvdywgZG95KTtcbiAgICAgICAgICAgIGNvbmZpZy5fYVtZRUFSXSA9IHRlbXAueWVhcjtcbiAgICAgICAgICAgIGNvbmZpZy5fZGF5T2ZZZWFyID0gdGVtcC5kYXlPZlllYXI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBjb25zdGFudCB0aGF0IHJlZmVycyB0byB0aGUgSVNPIHN0YW5kYXJkXG4gICAgdXRpbHNfaG9va3NfX2hvb2tzLklTT184NjAxID0gZnVuY3Rpb24gKCkge307XG5cbiAgICAvLyBkYXRlIGZyb20gc3RyaW5nIGFuZCBmb3JtYXQgc3RyaW5nXG4gICAgZnVuY3Rpb24gY29uZmlnRnJvbVN0cmluZ0FuZEZvcm1hdChjb25maWcpIHtcbiAgICAgICAgLy8gVE9ETzogTW92ZSB0aGlzIHRvIGFub3RoZXIgcGFydCBvZiB0aGUgY3JlYXRpb24gZmxvdyB0byBwcmV2ZW50IGNpcmN1bGFyIGRlcHNcbiAgICAgICAgaWYgKGNvbmZpZy5fZiA9PT0gdXRpbHNfaG9va3NfX2hvb2tzLklTT184NjAxKSB7XG4gICAgICAgICAgICBjb25maWdGcm9tSVNPKGNvbmZpZyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25maWcuX2EgPSBbXTtcbiAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuZW1wdHkgPSB0cnVlO1xuXG4gICAgICAgIC8vIFRoaXMgYXJyYXkgaXMgdXNlZCB0byBtYWtlIGEgRGF0ZSwgZWl0aGVyIHdpdGggYG5ldyBEYXRlYCBvciBgRGF0ZS5VVENgXG4gICAgICAgIHZhciBzdHJpbmcgPSAnJyArIGNvbmZpZy5faSxcbiAgICAgICAgICAgIGksIHBhcnNlZElucHV0LCB0b2tlbnMsIHRva2VuLCBza2lwcGVkLFxuICAgICAgICAgICAgc3RyaW5nTGVuZ3RoID0gc3RyaW5nLmxlbmd0aCxcbiAgICAgICAgICAgIHRvdGFsUGFyc2VkSW5wdXRMZW5ndGggPSAwO1xuXG4gICAgICAgIHRva2VucyA9IGV4cGFuZEZvcm1hdChjb25maWcuX2YsIGNvbmZpZy5fbG9jYWxlKS5tYXRjaChmb3JtYXR0aW5nVG9rZW5zKSB8fCBbXTtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdG9rZW5zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB0b2tlbiA9IHRva2Vuc1tpXTtcbiAgICAgICAgICAgIHBhcnNlZElucHV0ID0gKHN0cmluZy5tYXRjaChnZXRQYXJzZVJlZ2V4Rm9yVG9rZW4odG9rZW4sIGNvbmZpZykpIHx8IFtdKVswXTtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd0b2tlbicsIHRva2VuLCAncGFyc2VkSW5wdXQnLCBwYXJzZWRJbnB1dCxcbiAgICAgICAgICAgIC8vICAgICAgICAgJ3JlZ2V4JywgZ2V0UGFyc2VSZWdleEZvclRva2VuKHRva2VuLCBjb25maWcpKTtcbiAgICAgICAgICAgIGlmIChwYXJzZWRJbnB1dCkge1xuICAgICAgICAgICAgICAgIHNraXBwZWQgPSBzdHJpbmcuc3Vic3RyKDAsIHN0cmluZy5pbmRleE9mKHBhcnNlZElucHV0KSk7XG4gICAgICAgICAgICAgICAgaWYgKHNraXBwZWQubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS51bnVzZWRJbnB1dC5wdXNoKHNraXBwZWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBzdHJpbmcgPSBzdHJpbmcuc2xpY2Uoc3RyaW5nLmluZGV4T2YocGFyc2VkSW5wdXQpICsgcGFyc2VkSW5wdXQubGVuZ3RoKTtcbiAgICAgICAgICAgICAgICB0b3RhbFBhcnNlZElucHV0TGVuZ3RoICs9IHBhcnNlZElucHV0Lmxlbmd0aDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIGRvbid0IHBhcnNlIGlmIGl0J3Mgbm90IGEga25vd24gdG9rZW5cbiAgICAgICAgICAgIGlmIChmb3JtYXRUb2tlbkZ1bmN0aW9uc1t0b2tlbl0pIHtcbiAgICAgICAgICAgICAgICBpZiAocGFyc2VkSW5wdXQpIHtcbiAgICAgICAgICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykuZW1wdHkgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGdldFBhcnNpbmdGbGFncyhjb25maWcpLnVudXNlZFRva2Vucy5wdXNoKHRva2VuKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYWRkVGltZVRvQXJyYXlGcm9tVG9rZW4odG9rZW4sIHBhcnNlZElucHV0LCBjb25maWcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAoY29uZmlnLl9zdHJpY3QgJiYgIXBhcnNlZElucHV0KSB7XG4gICAgICAgICAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykudW51c2VkVG9rZW5zLnB1c2godG9rZW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gYWRkIHJlbWFpbmluZyB1bnBhcnNlZCBpbnB1dCBsZW5ndGggdG8gdGhlIHN0cmluZ1xuICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5jaGFyc0xlZnRPdmVyID0gc3RyaW5nTGVuZ3RoIC0gdG90YWxQYXJzZWRJbnB1dExlbmd0aDtcbiAgICAgICAgaWYgKHN0cmluZy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS51bnVzZWRJbnB1dC5wdXNoKHN0cmluZyk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBjbGVhciBfMTJoIGZsYWcgaWYgaG91ciBpcyA8PSAxMlxuICAgICAgICBpZiAoY29uZmlnLl9hW0hPVVJdIDw9IDEyICYmXG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5iaWdIb3VyID09PSB0cnVlICYmXG4gICAgICAgICAgICBjb25maWcuX2FbSE9VUl0gPiAwKSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5iaWdIb3VyID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG5cbiAgICAgICAgZ2V0UGFyc2luZ0ZsYWdzKGNvbmZpZykucGFyc2VkRGF0ZVBhcnRzID0gY29uZmlnLl9hLnNsaWNlKDApO1xuICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5tZXJpZGllbSA9IGNvbmZpZy5fbWVyaWRpZW07XG4gICAgICAgIC8vIGhhbmRsZSBtZXJpZGllbVxuICAgICAgICBjb25maWcuX2FbSE9VUl0gPSBtZXJpZGllbUZpeFdyYXAoY29uZmlnLl9sb2NhbGUsIGNvbmZpZy5fYVtIT1VSXSwgY29uZmlnLl9tZXJpZGllbSk7XG5cbiAgICAgICAgY29uZmlnRnJvbUFycmF5KGNvbmZpZyk7XG4gICAgICAgIGNoZWNrT3ZlcmZsb3coY29uZmlnKTtcbiAgICB9XG5cblxuICAgIGZ1bmN0aW9uIG1lcmlkaWVtRml4V3JhcCAobG9jYWxlLCBob3VyLCBtZXJpZGllbSkge1xuICAgICAgICB2YXIgaXNQbTtcblxuICAgICAgICBpZiAobWVyaWRpZW0gPT0gbnVsbCkge1xuICAgICAgICAgICAgLy8gbm90aGluZyB0byBkb1xuICAgICAgICAgICAgcmV0dXJuIGhvdXI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGxvY2FsZS5tZXJpZGllbUhvdXIgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIGxvY2FsZS5tZXJpZGllbUhvdXIoaG91ciwgbWVyaWRpZW0pO1xuICAgICAgICB9IGVsc2UgaWYgKGxvY2FsZS5pc1BNICE9IG51bGwpIHtcbiAgICAgICAgICAgIC8vIEZhbGxiYWNrXG4gICAgICAgICAgICBpc1BtID0gbG9jYWxlLmlzUE0obWVyaWRpZW0pO1xuICAgICAgICAgICAgaWYgKGlzUG0gJiYgaG91ciA8IDEyKSB7XG4gICAgICAgICAgICAgICAgaG91ciArPSAxMjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghaXNQbSAmJiBob3VyID09PSAxMikge1xuICAgICAgICAgICAgICAgIGhvdXIgPSAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGhvdXI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyB0aGlzIGlzIG5vdCBzdXBwb3NlZCB0byBoYXBwZW5cbiAgICAgICAgICAgIHJldHVybiBob3VyO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gZGF0ZSBmcm9tIHN0cmluZyBhbmQgYXJyYXkgb2YgZm9ybWF0IHN0cmluZ3NcbiAgICBmdW5jdGlvbiBjb25maWdGcm9tU3RyaW5nQW5kQXJyYXkoY29uZmlnKSB7XG4gICAgICAgIHZhciB0ZW1wQ29uZmlnLFxuICAgICAgICAgICAgYmVzdE1vbWVudCxcblxuICAgICAgICAgICAgc2NvcmVUb0JlYXQsXG4gICAgICAgICAgICBpLFxuICAgICAgICAgICAgY3VycmVudFNjb3JlO1xuXG4gICAgICAgIGlmIChjb25maWcuX2YubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3MoY29uZmlnKS5pbnZhbGlkRm9ybWF0ID0gdHJ1ZTtcbiAgICAgICAgICAgIGNvbmZpZy5fZCA9IG5ldyBEYXRlKE5hTik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgY29uZmlnLl9mLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjdXJyZW50U2NvcmUgPSAwO1xuICAgICAgICAgICAgdGVtcENvbmZpZyA9IGNvcHlDb25maWcoe30sIGNvbmZpZyk7XG4gICAgICAgICAgICBpZiAoY29uZmlnLl91c2VVVEMgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRlbXBDb25maWcuX3VzZVVUQyA9IGNvbmZpZy5fdXNlVVRDO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGVtcENvbmZpZy5fZiA9IGNvbmZpZy5fZltpXTtcbiAgICAgICAgICAgIGNvbmZpZ0Zyb21TdHJpbmdBbmRGb3JtYXQodGVtcENvbmZpZyk7XG5cbiAgICAgICAgICAgIGlmICghdmFsaWRfX2lzVmFsaWQodGVtcENvbmZpZykpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gaWYgdGhlcmUgaXMgYW55IGlucHV0IHRoYXQgd2FzIG5vdCBwYXJzZWQgYWRkIGEgcGVuYWx0eSBmb3IgdGhhdCBmb3JtYXRcbiAgICAgICAgICAgIGN1cnJlbnRTY29yZSArPSBnZXRQYXJzaW5nRmxhZ3ModGVtcENvbmZpZykuY2hhcnNMZWZ0T3ZlcjtcblxuICAgICAgICAgICAgLy9vciB0b2tlbnNcbiAgICAgICAgICAgIGN1cnJlbnRTY29yZSArPSBnZXRQYXJzaW5nRmxhZ3ModGVtcENvbmZpZykudW51c2VkVG9rZW5zLmxlbmd0aCAqIDEwO1xuXG4gICAgICAgICAgICBnZXRQYXJzaW5nRmxhZ3ModGVtcENvbmZpZykuc2NvcmUgPSBjdXJyZW50U2NvcmU7XG5cbiAgICAgICAgICAgIGlmIChzY29yZVRvQmVhdCA9PSBudWxsIHx8IGN1cnJlbnRTY29yZSA8IHNjb3JlVG9CZWF0KSB7XG4gICAgICAgICAgICAgICAgc2NvcmVUb0JlYXQgPSBjdXJyZW50U2NvcmU7XG4gICAgICAgICAgICAgICAgYmVzdE1vbWVudCA9IHRlbXBDb25maWc7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBleHRlbmQoY29uZmlnLCBiZXN0TW9tZW50IHx8IHRlbXBDb25maWcpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNvbmZpZ0Zyb21PYmplY3QoY29uZmlnKSB7XG4gICAgICAgIGlmIChjb25maWcuX2QpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBpID0gbm9ybWFsaXplT2JqZWN0VW5pdHMoY29uZmlnLl9pKTtcbiAgICAgICAgY29uZmlnLl9hID0gbWFwKFtpLnllYXIsIGkubW9udGgsIGkuZGF5IHx8IGkuZGF0ZSwgaS5ob3VyLCBpLm1pbnV0ZSwgaS5zZWNvbmQsIGkubWlsbGlzZWNvbmRdLCBmdW5jdGlvbiAob2JqKSB7XG4gICAgICAgICAgICByZXR1cm4gb2JqICYmIHBhcnNlSW50KG9iaiwgMTApO1xuICAgICAgICB9KTtcblxuICAgICAgICBjb25maWdGcm9tQXJyYXkoY29uZmlnKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjcmVhdGVGcm9tQ29uZmlnIChjb25maWcpIHtcbiAgICAgICAgdmFyIHJlcyA9IG5ldyBNb21lbnQoY2hlY2tPdmVyZmxvdyhwcmVwYXJlQ29uZmlnKGNvbmZpZykpKTtcbiAgICAgICAgaWYgKHJlcy5fbmV4dERheSkge1xuICAgICAgICAgICAgLy8gQWRkaW5nIGlzIHNtYXJ0IGVub3VnaCBhcm91bmQgRFNUXG4gICAgICAgICAgICByZXMuYWRkKDEsICdkJyk7XG4gICAgICAgICAgICByZXMuX25leHREYXkgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHByZXBhcmVDb25maWcgKGNvbmZpZykge1xuICAgICAgICB2YXIgaW5wdXQgPSBjb25maWcuX2ksXG4gICAgICAgICAgICBmb3JtYXQgPSBjb25maWcuX2Y7XG5cbiAgICAgICAgY29uZmlnLl9sb2NhbGUgPSBjb25maWcuX2xvY2FsZSB8fCBsb2NhbGVfbG9jYWxlc19fZ2V0TG9jYWxlKGNvbmZpZy5fbCk7XG5cbiAgICAgICAgaWYgKGlucHV0ID09PSBudWxsIHx8IChmb3JtYXQgPT09IHVuZGVmaW5lZCAmJiBpbnB1dCA9PT0gJycpKSB7XG4gICAgICAgICAgICByZXR1cm4gdmFsaWRfX2NyZWF0ZUludmFsaWQoe251bGxJbnB1dDogdHJ1ZX0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHR5cGVvZiBpbnB1dCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIGNvbmZpZy5faSA9IGlucHV0ID0gY29uZmlnLl9sb2NhbGUucHJlcGFyc2UoaW5wdXQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGlzTW9tZW50KGlucHV0KSkge1xuICAgICAgICAgICAgcmV0dXJuIG5ldyBNb21lbnQoY2hlY2tPdmVyZmxvdyhpbnB1dCkpO1xuICAgICAgICB9IGVsc2UgaWYgKGlzQXJyYXkoZm9ybWF0KSkge1xuICAgICAgICAgICAgY29uZmlnRnJvbVN0cmluZ0FuZEFycmF5KGNvbmZpZyk7XG4gICAgICAgIH0gZWxzZSBpZiAoaXNEYXRlKGlucHV0KSkge1xuICAgICAgICAgICAgY29uZmlnLl9kID0gaW5wdXQ7XG4gICAgICAgIH0gZWxzZSBpZiAoZm9ybWF0KSB7XG4gICAgICAgICAgICBjb25maWdGcm9tU3RyaW5nQW5kRm9ybWF0KGNvbmZpZyk7XG4gICAgICAgIH0gIGVsc2Uge1xuICAgICAgICAgICAgY29uZmlnRnJvbUlucHV0KGNvbmZpZyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXZhbGlkX19pc1ZhbGlkKGNvbmZpZykpIHtcbiAgICAgICAgICAgIGNvbmZpZy5fZCA9IG51bGw7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY29uZmlnO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNvbmZpZ0Zyb21JbnB1dChjb25maWcpIHtcbiAgICAgICAgdmFyIGlucHV0ID0gY29uZmlnLl9pO1xuICAgICAgICBpZiAoaW5wdXQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY29uZmlnLl9kID0gbmV3IERhdGUodXRpbHNfaG9va3NfX2hvb2tzLm5vdygpKTtcbiAgICAgICAgfSBlbHNlIGlmIChpc0RhdGUoaW5wdXQpKSB7XG4gICAgICAgICAgICBjb25maWcuX2QgPSBuZXcgRGF0ZShpbnB1dC52YWx1ZU9mKCkpO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBpbnB1dCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIGNvbmZpZ0Zyb21TdHJpbmcoY29uZmlnKTtcbiAgICAgICAgfSBlbHNlIGlmIChpc0FycmF5KGlucHV0KSkge1xuICAgICAgICAgICAgY29uZmlnLl9hID0gbWFwKGlucHV0LnNsaWNlKDApLCBmdW5jdGlvbiAob2JqKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHBhcnNlSW50KG9iaiwgMTApO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjb25maWdGcm9tQXJyYXkoY29uZmlnKTtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YoaW5wdXQpID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgY29uZmlnRnJvbU9iamVjdChjb25maWcpO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZihpbnB1dCkgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgICAvLyBmcm9tIG1pbGxpc2Vjb25kc1xuICAgICAgICAgICAgY29uZmlnLl9kID0gbmV3IERhdGUoaW5wdXQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLmNyZWF0ZUZyb21JbnB1dEZhbGxiYWNrKGNvbmZpZyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjcmVhdGVMb2NhbE9yVVRDIChpbnB1dCwgZm9ybWF0LCBsb2NhbGUsIHN0cmljdCwgaXNVVEMpIHtcbiAgICAgICAgdmFyIGMgPSB7fTtcblxuICAgICAgICBpZiAodHlwZW9mKGxvY2FsZSkgPT09ICdib29sZWFuJykge1xuICAgICAgICAgICAgc3RyaWN0ID0gbG9jYWxlO1xuICAgICAgICAgICAgbG9jYWxlID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKChpc09iamVjdChpbnB1dCkgJiYgaXNPYmplY3RFbXB0eShpbnB1dCkpIHx8XG4gICAgICAgICAgICAgICAgKGlzQXJyYXkoaW5wdXQpICYmIGlucHV0Lmxlbmd0aCA9PT0gMCkpIHtcbiAgICAgICAgICAgIGlucHV0ID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICAgIC8vIG9iamVjdCBjb25zdHJ1Y3Rpb24gbXVzdCBiZSBkb25lIHRoaXMgd2F5LlxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vbW9tZW50L21vbWVudC9pc3N1ZXMvMTQyM1xuICAgICAgICBjLl9pc0FNb21lbnRPYmplY3QgPSB0cnVlO1xuICAgICAgICBjLl91c2VVVEMgPSBjLl9pc1VUQyA9IGlzVVRDO1xuICAgICAgICBjLl9sID0gbG9jYWxlO1xuICAgICAgICBjLl9pID0gaW5wdXQ7XG4gICAgICAgIGMuX2YgPSBmb3JtYXQ7XG4gICAgICAgIGMuX3N0cmljdCA9IHN0cmljdDtcblxuICAgICAgICByZXR1cm4gY3JlYXRlRnJvbUNvbmZpZyhjKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBsb2NhbF9fY3JlYXRlTG9jYWwgKGlucHV0LCBmb3JtYXQsIGxvY2FsZSwgc3RyaWN0KSB7XG4gICAgICAgIHJldHVybiBjcmVhdGVMb2NhbE9yVVRDKGlucHV0LCBmb3JtYXQsIGxvY2FsZSwgc3RyaWN0LCBmYWxzZSk7XG4gICAgfVxuXG4gICAgdmFyIHByb3RvdHlwZU1pbiA9IGRlcHJlY2F0ZShcbiAgICAgICAgJ21vbWVudCgpLm1pbiBpcyBkZXByZWNhdGVkLCB1c2UgbW9tZW50Lm1heCBpbnN0ZWFkLiBodHRwOi8vbW9tZW50anMuY29tL2d1aWRlcy8jL3dhcm5pbmdzL21pbi1tYXgvJyxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIG90aGVyID0gbG9jYWxfX2NyZWF0ZUxvY2FsLmFwcGx5KG51bGwsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICBpZiAodGhpcy5pc1ZhbGlkKCkgJiYgb3RoZXIuaXNWYWxpZCgpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG90aGVyIDwgdGhpcyA/IHRoaXMgOiBvdGhlcjtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbGlkX19jcmVhdGVJbnZhbGlkKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICApO1xuXG4gICAgdmFyIHByb3RvdHlwZU1heCA9IGRlcHJlY2F0ZShcbiAgICAgICAgJ21vbWVudCgpLm1heCBpcyBkZXByZWNhdGVkLCB1c2UgbW9tZW50Lm1pbiBpbnN0ZWFkLiBodHRwOi8vbW9tZW50anMuY29tL2d1aWRlcy8jL3dhcm5pbmdzL21pbi1tYXgvJyxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIG90aGVyID0gbG9jYWxfX2NyZWF0ZUxvY2FsLmFwcGx5KG51bGwsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICBpZiAodGhpcy5pc1ZhbGlkKCkgJiYgb3RoZXIuaXNWYWxpZCgpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG90aGVyID4gdGhpcyA/IHRoaXMgOiBvdGhlcjtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbGlkX19jcmVhdGVJbnZhbGlkKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICApO1xuXG4gICAgLy8gUGljayBhIG1vbWVudCBtIGZyb20gbW9tZW50cyBzbyB0aGF0IG1bZm5dKG90aGVyKSBpcyB0cnVlIGZvciBhbGxcbiAgICAvLyBvdGhlci4gVGhpcyByZWxpZXMgb24gdGhlIGZ1bmN0aW9uIGZuIHRvIGJlIHRyYW5zaXRpdmUuXG4gICAgLy9cbiAgICAvLyBtb21lbnRzIHNob3VsZCBlaXRoZXIgYmUgYW4gYXJyYXkgb2YgbW9tZW50IG9iamVjdHMgb3IgYW4gYXJyYXksIHdob3NlXG4gICAgLy8gZmlyc3QgZWxlbWVudCBpcyBhbiBhcnJheSBvZiBtb21lbnQgb2JqZWN0cy5cbiAgICBmdW5jdGlvbiBwaWNrQnkoZm4sIG1vbWVudHMpIHtcbiAgICAgICAgdmFyIHJlcywgaTtcbiAgICAgICAgaWYgKG1vbWVudHMubGVuZ3RoID09PSAxICYmIGlzQXJyYXkobW9tZW50c1swXSkpIHtcbiAgICAgICAgICAgIG1vbWVudHMgPSBtb21lbnRzWzBdO1xuICAgICAgICB9XG4gICAgICAgIGlmICghbW9tZW50cy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHJldHVybiBsb2NhbF9fY3JlYXRlTG9jYWwoKTtcbiAgICAgICAgfVxuICAgICAgICByZXMgPSBtb21lbnRzWzBdO1xuICAgICAgICBmb3IgKGkgPSAxOyBpIDwgbW9tZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgaWYgKCFtb21lbnRzW2ldLmlzVmFsaWQoKSB8fCBtb21lbnRzW2ldW2ZuXShyZXMpKSB7XG4gICAgICAgICAgICAgICAgcmVzID0gbW9tZW50c1tpXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cblxuICAgIC8vIFRPRE86IFVzZSBbXS5zb3J0IGluc3RlYWQ/XG4gICAgZnVuY3Rpb24gbWluICgpIHtcbiAgICAgICAgdmFyIGFyZ3MgPSBbXS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMCk7XG5cbiAgICAgICAgcmV0dXJuIHBpY2tCeSgnaXNCZWZvcmUnLCBhcmdzKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBtYXggKCkge1xuICAgICAgICB2YXIgYXJncyA9IFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAwKTtcblxuICAgICAgICByZXR1cm4gcGlja0J5KCdpc0FmdGVyJywgYXJncyk7XG4gICAgfVxuXG4gICAgdmFyIG5vdyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIERhdGUubm93ID8gRGF0ZS5ub3coKSA6ICsobmV3IERhdGUoKSk7XG4gICAgfTtcblxuICAgIGZ1bmN0aW9uIER1cmF0aW9uIChkdXJhdGlvbikge1xuICAgICAgICB2YXIgbm9ybWFsaXplZElucHV0ID0gbm9ybWFsaXplT2JqZWN0VW5pdHMoZHVyYXRpb24pLFxuICAgICAgICAgICAgeWVhcnMgPSBub3JtYWxpemVkSW5wdXQueWVhciB8fCAwLFxuICAgICAgICAgICAgcXVhcnRlcnMgPSBub3JtYWxpemVkSW5wdXQucXVhcnRlciB8fCAwLFxuICAgICAgICAgICAgbW9udGhzID0gbm9ybWFsaXplZElucHV0Lm1vbnRoIHx8IDAsXG4gICAgICAgICAgICB3ZWVrcyA9IG5vcm1hbGl6ZWRJbnB1dC53ZWVrIHx8IDAsXG4gICAgICAgICAgICBkYXlzID0gbm9ybWFsaXplZElucHV0LmRheSB8fCAwLFxuICAgICAgICAgICAgaG91cnMgPSBub3JtYWxpemVkSW5wdXQuaG91ciB8fCAwLFxuICAgICAgICAgICAgbWludXRlcyA9IG5vcm1hbGl6ZWRJbnB1dC5taW51dGUgfHwgMCxcbiAgICAgICAgICAgIHNlY29uZHMgPSBub3JtYWxpemVkSW5wdXQuc2Vjb25kIHx8IDAsXG4gICAgICAgICAgICBtaWxsaXNlY29uZHMgPSBub3JtYWxpemVkSW5wdXQubWlsbGlzZWNvbmQgfHwgMDtcblxuICAgICAgICAvLyByZXByZXNlbnRhdGlvbiBmb3IgZGF0ZUFkZFJlbW92ZVxuICAgICAgICB0aGlzLl9taWxsaXNlY29uZHMgPSArbWlsbGlzZWNvbmRzICtcbiAgICAgICAgICAgIHNlY29uZHMgKiAxZTMgKyAvLyAxMDAwXG4gICAgICAgICAgICBtaW51dGVzICogNmU0ICsgLy8gMTAwMCAqIDYwXG4gICAgICAgICAgICBob3VycyAqIDEwMDAgKiA2MCAqIDYwOyAvL3VzaW5nIDEwMDAgKiA2MCAqIDYwIGluc3RlYWQgb2YgMzZlNSB0byBhdm9pZCBmbG9hdGluZyBwb2ludCByb3VuZGluZyBlcnJvcnMgaHR0cHM6Ly9naXRodWIuY29tL21vbWVudC9tb21lbnQvaXNzdWVzLzI5NzhcbiAgICAgICAgLy8gQmVjYXVzZSBvZiBkYXRlQWRkUmVtb3ZlIHRyZWF0cyAyNCBob3VycyBhcyBkaWZmZXJlbnQgZnJvbSBhXG4gICAgICAgIC8vIGRheSB3aGVuIHdvcmtpbmcgYXJvdW5kIERTVCwgd2UgbmVlZCB0byBzdG9yZSB0aGVtIHNlcGFyYXRlbHlcbiAgICAgICAgdGhpcy5fZGF5cyA9ICtkYXlzICtcbiAgICAgICAgICAgIHdlZWtzICogNztcbiAgICAgICAgLy8gSXQgaXMgaW1wb3NzaWJsZSB0cmFuc2xhdGUgbW9udGhzIGludG8gZGF5cyB3aXRob3V0IGtub3dpbmdcbiAgICAgICAgLy8gd2hpY2ggbW9udGhzIHlvdSBhcmUgYXJlIHRhbGtpbmcgYWJvdXQsIHNvIHdlIGhhdmUgdG8gc3RvcmVcbiAgICAgICAgLy8gaXQgc2VwYXJhdGVseS5cbiAgICAgICAgdGhpcy5fbW9udGhzID0gK21vbnRocyArXG4gICAgICAgICAgICBxdWFydGVycyAqIDMgK1xuICAgICAgICAgICAgeWVhcnMgKiAxMjtcblxuICAgICAgICB0aGlzLl9kYXRhID0ge307XG5cbiAgICAgICAgdGhpcy5fbG9jYWxlID0gbG9jYWxlX2xvY2FsZXNfX2dldExvY2FsZSgpO1xuXG4gICAgICAgIHRoaXMuX2J1YmJsZSgpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzRHVyYXRpb24gKG9iaikge1xuICAgICAgICByZXR1cm4gb2JqIGluc3RhbmNlb2YgRHVyYXRpb247XG4gICAgfVxuXG4gICAgLy8gRk9STUFUVElOR1xuXG4gICAgZnVuY3Rpb24gb2Zmc2V0ICh0b2tlbiwgc2VwYXJhdG9yKSB7XG4gICAgICAgIGFkZEZvcm1hdFRva2VuKHRva2VuLCAwLCAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgb2Zmc2V0ID0gdGhpcy51dGNPZmZzZXQoKTtcbiAgICAgICAgICAgIHZhciBzaWduID0gJysnO1xuICAgICAgICAgICAgaWYgKG9mZnNldCA8IDApIHtcbiAgICAgICAgICAgICAgICBvZmZzZXQgPSAtb2Zmc2V0O1xuICAgICAgICAgICAgICAgIHNpZ24gPSAnLSc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc2lnbiArIHplcm9GaWxsKH5+KG9mZnNldCAvIDYwKSwgMikgKyBzZXBhcmF0b3IgKyB6ZXJvRmlsbCh+fihvZmZzZXQpICUgNjAsIDIpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBvZmZzZXQoJ1onLCAnOicpO1xuICAgIG9mZnNldCgnWlonLCAnJyk7XG5cbiAgICAvLyBQQVJTSU5HXG5cbiAgICBhZGRSZWdleFRva2VuKCdaJywgIG1hdGNoU2hvcnRPZmZzZXQpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ1paJywgbWF0Y2hTaG9ydE9mZnNldCk7XG4gICAgYWRkUGFyc2VUb2tlbihbJ1onLCAnWlonXSwgZnVuY3Rpb24gKGlucHV0LCBhcnJheSwgY29uZmlnKSB7XG4gICAgICAgIGNvbmZpZy5fdXNlVVRDID0gdHJ1ZTtcbiAgICAgICAgY29uZmlnLl90em0gPSBvZmZzZXRGcm9tU3RyaW5nKG1hdGNoU2hvcnRPZmZzZXQsIGlucHV0KTtcbiAgICB9KTtcblxuICAgIC8vIEhFTFBFUlNcblxuICAgIC8vIHRpbWV6b25lIGNodW5rZXJcbiAgICAvLyAnKzEwOjAwJyA+IFsnMTAnLCAgJzAwJ11cbiAgICAvLyAnLTE1MzAnICA+IFsnLTE1JywgJzMwJ11cbiAgICB2YXIgY2h1bmtPZmZzZXQgPSAvKFtcXCtcXC1dfFxcZFxcZCkvZ2k7XG5cbiAgICBmdW5jdGlvbiBvZmZzZXRGcm9tU3RyaW5nKG1hdGNoZXIsIHN0cmluZykge1xuICAgICAgICB2YXIgbWF0Y2hlcyA9ICgoc3RyaW5nIHx8ICcnKS5tYXRjaChtYXRjaGVyKSB8fCBbXSk7XG4gICAgICAgIHZhciBjaHVuayAgID0gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdIHx8IFtdO1xuICAgICAgICB2YXIgcGFydHMgICA9IChjaHVuayArICcnKS5tYXRjaChjaHVua09mZnNldCkgfHwgWyctJywgMCwgMF07XG4gICAgICAgIHZhciBtaW51dGVzID0gKyhwYXJ0c1sxXSAqIDYwKSArIHRvSW50KHBhcnRzWzJdKTtcblxuICAgICAgICByZXR1cm4gcGFydHNbMF0gPT09ICcrJyA/IG1pbnV0ZXMgOiAtbWludXRlcztcbiAgICB9XG5cbiAgICAvLyBSZXR1cm4gYSBtb21lbnQgZnJvbSBpbnB1dCwgdGhhdCBpcyBsb2NhbC91dGMvem9uZSBlcXVpdmFsZW50IHRvIG1vZGVsLlxuICAgIGZ1bmN0aW9uIGNsb25lV2l0aE9mZnNldChpbnB1dCwgbW9kZWwpIHtcbiAgICAgICAgdmFyIHJlcywgZGlmZjtcbiAgICAgICAgaWYgKG1vZGVsLl9pc1VUQykge1xuICAgICAgICAgICAgcmVzID0gbW9kZWwuY2xvbmUoKTtcbiAgICAgICAgICAgIGRpZmYgPSAoaXNNb21lbnQoaW5wdXQpIHx8IGlzRGF0ZShpbnB1dCkgPyBpbnB1dC52YWx1ZU9mKCkgOiBsb2NhbF9fY3JlYXRlTG9jYWwoaW5wdXQpLnZhbHVlT2YoKSkgLSByZXMudmFsdWVPZigpO1xuICAgICAgICAgICAgLy8gVXNlIGxvdy1sZXZlbCBhcGksIGJlY2F1c2UgdGhpcyBmbiBpcyBsb3ctbGV2ZWwgYXBpLlxuICAgICAgICAgICAgcmVzLl9kLnNldFRpbWUocmVzLl9kLnZhbHVlT2YoKSArIGRpZmYpO1xuICAgICAgICAgICAgdXRpbHNfaG9va3NfX2hvb2tzLnVwZGF0ZU9mZnNldChyZXMsIGZhbHNlKTtcbiAgICAgICAgICAgIHJldHVybiByZXM7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gbG9jYWxfX2NyZWF0ZUxvY2FsKGlucHV0KS5sb2NhbCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0RGF0ZU9mZnNldCAobSkge1xuICAgICAgICAvLyBPbiBGaXJlZm94LjI0IERhdGUjZ2V0VGltZXpvbmVPZmZzZXQgcmV0dXJucyBhIGZsb2F0aW5nIHBvaW50LlxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vbW9tZW50L21vbWVudC9wdWxsLzE4NzFcbiAgICAgICAgcmV0dXJuIC1NYXRoLnJvdW5kKG0uX2QuZ2V0VGltZXpvbmVPZmZzZXQoKSAvIDE1KSAqIDE1O1xuICAgIH1cblxuICAgIC8vIEhPT0tTXG5cbiAgICAvLyBUaGlzIGZ1bmN0aW9uIHdpbGwgYmUgY2FsbGVkIHdoZW5ldmVyIGEgbW9tZW50IGlzIG11dGF0ZWQuXG4gICAgLy8gSXQgaXMgaW50ZW5kZWQgdG8ga2VlcCB0aGUgb2Zmc2V0IGluIHN5bmMgd2l0aCB0aGUgdGltZXpvbmUuXG4gICAgdXRpbHNfaG9va3NfX2hvb2tzLnVwZGF0ZU9mZnNldCA9IGZ1bmN0aW9uICgpIHt9O1xuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgLy8ga2VlcExvY2FsVGltZSA9IHRydWUgbWVhbnMgb25seSBjaGFuZ2UgdGhlIHRpbWV6b25lLCB3aXRob3V0XG4gICAgLy8gYWZmZWN0aW5nIHRoZSBsb2NhbCBob3VyLiBTbyA1OjMxOjI2ICswMzAwIC0tW3V0Y09mZnNldCgyLCB0cnVlKV0tLT5cbiAgICAvLyA1OjMxOjI2ICswMjAwIEl0IGlzIHBvc3NpYmxlIHRoYXQgNTozMToyNiBkb2Vzbid0IGV4aXN0IHdpdGggb2Zmc2V0XG4gICAgLy8gKzAyMDAsIHNvIHdlIGFkanVzdCB0aGUgdGltZSBhcyBuZWVkZWQsIHRvIGJlIHZhbGlkLlxuICAgIC8vXG4gICAgLy8gS2VlcGluZyB0aGUgdGltZSBhY3R1YWxseSBhZGRzL3N1YnRyYWN0cyAob25lIGhvdXIpXG4gICAgLy8gZnJvbSB0aGUgYWN0dWFsIHJlcHJlc2VudGVkIHRpbWUuIFRoYXQgaXMgd2h5IHdlIGNhbGwgdXBkYXRlT2Zmc2V0XG4gICAgLy8gYSBzZWNvbmQgdGltZS4gSW4gY2FzZSBpdCB3YW50cyB1cyB0byBjaGFuZ2UgdGhlIG9mZnNldCBhZ2FpblxuICAgIC8vIF9jaGFuZ2VJblByb2dyZXNzID09IHRydWUgY2FzZSwgdGhlbiB3ZSBoYXZlIHRvIGFkanVzdCwgYmVjYXVzZVxuICAgIC8vIHRoZXJlIGlzIG5vIHN1Y2ggdGltZSBpbiB0aGUgZ2l2ZW4gdGltZXpvbmUuXG4gICAgZnVuY3Rpb24gZ2V0U2V0T2Zmc2V0IChpbnB1dCwga2VlcExvY2FsVGltZSkge1xuICAgICAgICB2YXIgb2Zmc2V0ID0gdGhpcy5fb2Zmc2V0IHx8IDAsXG4gICAgICAgICAgICBsb2NhbEFkanVzdDtcbiAgICAgICAgaWYgKCF0aGlzLmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0ICE9IG51bGwgPyB0aGlzIDogTmFOO1xuICAgICAgICB9XG4gICAgICAgIGlmIChpbnB1dCAhPSBudWxsKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIGlucHV0ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIGlucHV0ID0gb2Zmc2V0RnJvbVN0cmluZyhtYXRjaFNob3J0T2Zmc2V0LCBpbnB1dCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKE1hdGguYWJzKGlucHV0KSA8IDE2KSB7XG4gICAgICAgICAgICAgICAgaW5wdXQgPSBpbnB1dCAqIDYwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCF0aGlzLl9pc1VUQyAmJiBrZWVwTG9jYWxUaW1lKSB7XG4gICAgICAgICAgICAgICAgbG9jYWxBZGp1c3QgPSBnZXREYXRlT2Zmc2V0KHRoaXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5fb2Zmc2V0ID0gaW5wdXQ7XG4gICAgICAgICAgICB0aGlzLl9pc1VUQyA9IHRydWU7XG4gICAgICAgICAgICBpZiAobG9jYWxBZGp1c3QgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRoaXMuYWRkKGxvY2FsQWRqdXN0LCAnbScpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG9mZnNldCAhPT0gaW5wdXQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWtlZXBMb2NhbFRpbWUgfHwgdGhpcy5fY2hhbmdlSW5Qcm9ncmVzcykge1xuICAgICAgICAgICAgICAgICAgICBhZGRfc3VidHJhY3RfX2FkZFN1YnRyYWN0KHRoaXMsIGNyZWF0ZV9fY3JlYXRlRHVyYXRpb24oaW5wdXQgLSBvZmZzZXQsICdtJyksIDEsIGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLl9jaGFuZ2VJblByb2dyZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2NoYW5nZUluUHJvZ3Jlc3MgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB1dGlsc19ob29rc19faG9va3MudXBkYXRlT2Zmc2V0KHRoaXMsIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jaGFuZ2VJblByb2dyZXNzID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9pc1VUQyA/IG9mZnNldCA6IGdldERhdGVPZmZzZXQodGhpcyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTZXRab25lIChpbnB1dCwga2VlcExvY2FsVGltZSkge1xuICAgICAgICBpZiAoaW5wdXQgIT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBpbnB1dCAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICBpbnB1dCA9IC1pbnB1dDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy51dGNPZmZzZXQoaW5wdXQsIGtlZXBMb2NhbFRpbWUpO1xuXG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiAtdGhpcy51dGNPZmZzZXQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHNldE9mZnNldFRvVVRDIChrZWVwTG9jYWxUaW1lKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnV0Y09mZnNldCgwLCBrZWVwTG9jYWxUaW1lKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBzZXRPZmZzZXRUb0xvY2FsIChrZWVwTG9jYWxUaW1lKSB7XG4gICAgICAgIGlmICh0aGlzLl9pc1VUQykge1xuICAgICAgICAgICAgdGhpcy51dGNPZmZzZXQoMCwga2VlcExvY2FsVGltZSk7XG4gICAgICAgICAgICB0aGlzLl9pc1VUQyA9IGZhbHNlO1xuXG4gICAgICAgICAgICBpZiAoa2VlcExvY2FsVGltZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuc3VidHJhY3QoZ2V0RGF0ZU9mZnNldCh0aGlzKSwgJ20nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBzZXRPZmZzZXRUb1BhcnNlZE9mZnNldCAoKSB7XG4gICAgICAgIGlmICh0aGlzLl90em0pIHtcbiAgICAgICAgICAgIHRoaXMudXRjT2Zmc2V0KHRoaXMuX3R6bSk7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHRoaXMuX2kgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICB0aGlzLnV0Y09mZnNldChvZmZzZXRGcm9tU3RyaW5nKG1hdGNoT2Zmc2V0LCB0aGlzLl9pKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaGFzQWxpZ25lZEhvdXJPZmZzZXQgKGlucHV0KSB7XG4gICAgICAgIGlmICghdGhpcy5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpbnB1dCA9IGlucHV0ID8gbG9jYWxfX2NyZWF0ZUxvY2FsKGlucHV0KS51dGNPZmZzZXQoKSA6IDA7XG5cbiAgICAgICAgcmV0dXJuICh0aGlzLnV0Y09mZnNldCgpIC0gaW5wdXQpICUgNjAgPT09IDA7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNEYXlsaWdodFNhdmluZ1RpbWUgKCkge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgdGhpcy51dGNPZmZzZXQoKSA+IHRoaXMuY2xvbmUoKS5tb250aCgwKS51dGNPZmZzZXQoKSB8fFxuICAgICAgICAgICAgdGhpcy51dGNPZmZzZXQoKSA+IHRoaXMuY2xvbmUoKS5tb250aCg1KS51dGNPZmZzZXQoKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzRGF5bGlnaHRTYXZpbmdUaW1lU2hpZnRlZCAoKSB7XG4gICAgICAgIGlmICghaXNVbmRlZmluZWQodGhpcy5faXNEU1RTaGlmdGVkKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2lzRFNUU2hpZnRlZDtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBjID0ge307XG5cbiAgICAgICAgY29weUNvbmZpZyhjLCB0aGlzKTtcbiAgICAgICAgYyA9IHByZXBhcmVDb25maWcoYyk7XG5cbiAgICAgICAgaWYgKGMuX2EpIHtcbiAgICAgICAgICAgIHZhciBvdGhlciA9IGMuX2lzVVRDID8gY3JlYXRlX3V0Y19fY3JlYXRlVVRDKGMuX2EpIDogbG9jYWxfX2NyZWF0ZUxvY2FsKGMuX2EpO1xuICAgICAgICAgICAgdGhpcy5faXNEU1RTaGlmdGVkID0gdGhpcy5pc1ZhbGlkKCkgJiZcbiAgICAgICAgICAgICAgICBjb21wYXJlQXJyYXlzKGMuX2EsIG90aGVyLnRvQXJyYXkoKSkgPiAwO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5faXNEU1RTaGlmdGVkID0gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcy5faXNEU1RTaGlmdGVkO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzTG9jYWwgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkKCkgPyAhdGhpcy5faXNVVEMgOiBmYWxzZTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc1V0Y09mZnNldCAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQoKSA/IHRoaXMuX2lzVVRDIDogZmFsc2U7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNVdGMgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkKCkgPyB0aGlzLl9pc1VUQyAmJiB0aGlzLl9vZmZzZXQgPT09IDAgOiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBBU1AuTkVUIGpzb24gZGF0ZSBmb3JtYXQgcmVnZXhcbiAgICB2YXIgYXNwTmV0UmVnZXggPSAvXihcXC0pPyg/OihcXGQqKVsuIF0pPyhcXGQrKVxcOihcXGQrKSg/OlxcOihcXGQrKVxcLj8oXFxkezN9KT9cXGQqKT8kLztcblxuICAgIC8vIGZyb20gaHR0cDovL2RvY3MuY2xvc3VyZS1saWJyYXJ5Lmdvb2dsZWNvZGUuY29tL2dpdC9jbG9zdXJlX2dvb2dfZGF0ZV9kYXRlLmpzLnNvdXJjZS5odG1sXG4gICAgLy8gc29tZXdoYXQgbW9yZSBpbiBsaW5lIHdpdGggNC40LjMuMiAyMDA0IHNwZWMsIGJ1dCBhbGxvd3MgZGVjaW1hbCBhbnl3aGVyZVxuICAgIC8vIGFuZCBmdXJ0aGVyIG1vZGlmaWVkIHRvIGFsbG93IGZvciBzdHJpbmdzIGNvbnRhaW5pbmcgYm90aCB3ZWVrIGFuZCBkYXlcbiAgICB2YXIgaXNvUmVnZXggPSAvXigtKT9QKD86KC0/WzAtOSwuXSopWSk/KD86KC0/WzAtOSwuXSopTSk/KD86KC0/WzAtOSwuXSopVyk/KD86KC0/WzAtOSwuXSopRCk/KD86VCg/OigtP1swLTksLl0qKUgpPyg/OigtP1swLTksLl0qKU0pPyg/OigtP1swLTksLl0qKVMpPyk/JC87XG5cbiAgICBmdW5jdGlvbiBjcmVhdGVfX2NyZWF0ZUR1cmF0aW9uIChpbnB1dCwga2V5KSB7XG4gICAgICAgIHZhciBkdXJhdGlvbiA9IGlucHV0LFxuICAgICAgICAgICAgLy8gbWF0Y2hpbmcgYWdhaW5zdCByZWdleHAgaXMgZXhwZW5zaXZlLCBkbyBpdCBvbiBkZW1hbmRcbiAgICAgICAgICAgIG1hdGNoID0gbnVsbCxcbiAgICAgICAgICAgIHNpZ24sXG4gICAgICAgICAgICByZXQsXG4gICAgICAgICAgICBkaWZmUmVzO1xuXG4gICAgICAgIGlmIChpc0R1cmF0aW9uKGlucHV0KSkge1xuICAgICAgICAgICAgZHVyYXRpb24gPSB7XG4gICAgICAgICAgICAgICAgbXMgOiBpbnB1dC5fbWlsbGlzZWNvbmRzLFxuICAgICAgICAgICAgICAgIGQgIDogaW5wdXQuX2RheXMsXG4gICAgICAgICAgICAgICAgTSAgOiBpbnB1dC5fbW9udGhzXG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBpbnB1dCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgIGR1cmF0aW9uID0ge307XG4gICAgICAgICAgICBpZiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgZHVyYXRpb25ba2V5XSA9IGlucHV0O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBkdXJhdGlvbi5taWxsaXNlY29uZHMgPSBpbnB1dDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICghIShtYXRjaCA9IGFzcE5ldFJlZ2V4LmV4ZWMoaW5wdXQpKSkge1xuICAgICAgICAgICAgc2lnbiA9IChtYXRjaFsxXSA9PT0gJy0nKSA/IC0xIDogMTtcbiAgICAgICAgICAgIGR1cmF0aW9uID0ge1xuICAgICAgICAgICAgICAgIHkgIDogMCxcbiAgICAgICAgICAgICAgICBkICA6IHRvSW50KG1hdGNoW0RBVEVdKSAgICAgICAgKiBzaWduLFxuICAgICAgICAgICAgICAgIGggIDogdG9JbnQobWF0Y2hbSE9VUl0pICAgICAgICAqIHNpZ24sXG4gICAgICAgICAgICAgICAgbSAgOiB0b0ludChtYXRjaFtNSU5VVEVdKSAgICAgICogc2lnbixcbiAgICAgICAgICAgICAgICBzICA6IHRvSW50KG1hdGNoW1NFQ09ORF0pICAgICAgKiBzaWduLFxuICAgICAgICAgICAgICAgIG1zIDogdG9JbnQobWF0Y2hbTUlMTElTRUNPTkRdKSAqIHNpZ25cbiAgICAgICAgICAgIH07XG4gICAgICAgIH0gZWxzZSBpZiAoISEobWF0Y2ggPSBpc29SZWdleC5leGVjKGlucHV0KSkpIHtcbiAgICAgICAgICAgIHNpZ24gPSAobWF0Y2hbMV0gPT09ICctJykgPyAtMSA6IDE7XG4gICAgICAgICAgICBkdXJhdGlvbiA9IHtcbiAgICAgICAgICAgICAgICB5IDogcGFyc2VJc28obWF0Y2hbMl0sIHNpZ24pLFxuICAgICAgICAgICAgICAgIE0gOiBwYXJzZUlzbyhtYXRjaFszXSwgc2lnbiksXG4gICAgICAgICAgICAgICAgdyA6IHBhcnNlSXNvKG1hdGNoWzRdLCBzaWduKSxcbiAgICAgICAgICAgICAgICBkIDogcGFyc2VJc28obWF0Y2hbNV0sIHNpZ24pLFxuICAgICAgICAgICAgICAgIGggOiBwYXJzZUlzbyhtYXRjaFs2XSwgc2lnbiksXG4gICAgICAgICAgICAgICAgbSA6IHBhcnNlSXNvKG1hdGNoWzddLCBzaWduKSxcbiAgICAgICAgICAgICAgICBzIDogcGFyc2VJc28obWF0Y2hbOF0sIHNpZ24pXG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGVsc2UgaWYgKGR1cmF0aW9uID09IG51bGwpIHsvLyBjaGVja3MgZm9yIG51bGwgb3IgdW5kZWZpbmVkXG4gICAgICAgICAgICBkdXJhdGlvbiA9IHt9O1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkdXJhdGlvbiA9PT0gJ29iamVjdCcgJiYgKCdmcm9tJyBpbiBkdXJhdGlvbiB8fCAndG8nIGluIGR1cmF0aW9uKSkge1xuICAgICAgICAgICAgZGlmZlJlcyA9IG1vbWVudHNEaWZmZXJlbmNlKGxvY2FsX19jcmVhdGVMb2NhbChkdXJhdGlvbi5mcm9tKSwgbG9jYWxfX2NyZWF0ZUxvY2FsKGR1cmF0aW9uLnRvKSk7XG5cbiAgICAgICAgICAgIGR1cmF0aW9uID0ge307XG4gICAgICAgICAgICBkdXJhdGlvbi5tcyA9IGRpZmZSZXMubWlsbGlzZWNvbmRzO1xuICAgICAgICAgICAgZHVyYXRpb24uTSA9IGRpZmZSZXMubW9udGhzO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0ID0gbmV3IER1cmF0aW9uKGR1cmF0aW9uKTtcblxuICAgICAgICBpZiAoaXNEdXJhdGlvbihpbnB1dCkgJiYgaGFzT3duUHJvcChpbnB1dCwgJ19sb2NhbGUnKSkge1xuICAgICAgICAgICAgcmV0Ll9sb2NhbGUgPSBpbnB1dC5fbG9jYWxlO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJldDtcbiAgICB9XG5cbiAgICBjcmVhdGVfX2NyZWF0ZUR1cmF0aW9uLmZuID0gRHVyYXRpb24ucHJvdG90eXBlO1xuXG4gICAgZnVuY3Rpb24gcGFyc2VJc28gKGlucCwgc2lnbikge1xuICAgICAgICAvLyBXZSdkIG5vcm1hbGx5IHVzZSB+fmlucCBmb3IgdGhpcywgYnV0IHVuZm9ydHVuYXRlbHkgaXQgYWxzb1xuICAgICAgICAvLyBjb252ZXJ0cyBmbG9hdHMgdG8gaW50cy5cbiAgICAgICAgLy8gaW5wIG1heSBiZSB1bmRlZmluZWQsIHNvIGNhcmVmdWwgY2FsbGluZyByZXBsYWNlIG9uIGl0LlxuICAgICAgICB2YXIgcmVzID0gaW5wICYmIHBhcnNlRmxvYXQoaW5wLnJlcGxhY2UoJywnLCAnLicpKTtcbiAgICAgICAgLy8gYXBwbHkgc2lnbiB3aGlsZSB3ZSdyZSBhdCBpdFxuICAgICAgICByZXR1cm4gKGlzTmFOKHJlcykgPyAwIDogcmVzKSAqIHNpZ247XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcG9zaXRpdmVNb21lbnRzRGlmZmVyZW5jZShiYXNlLCBvdGhlcikge1xuICAgICAgICB2YXIgcmVzID0ge21pbGxpc2Vjb25kczogMCwgbW9udGhzOiAwfTtcblxuICAgICAgICByZXMubW9udGhzID0gb3RoZXIubW9udGgoKSAtIGJhc2UubW9udGgoKSArXG4gICAgICAgICAgICAob3RoZXIueWVhcigpIC0gYmFzZS55ZWFyKCkpICogMTI7XG4gICAgICAgIGlmIChiYXNlLmNsb25lKCkuYWRkKHJlcy5tb250aHMsICdNJykuaXNBZnRlcihvdGhlcikpIHtcbiAgICAgICAgICAgIC0tcmVzLm1vbnRocztcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcy5taWxsaXNlY29uZHMgPSArb3RoZXIgLSArKGJhc2UuY2xvbmUoKS5hZGQocmVzLm1vbnRocywgJ00nKSk7XG5cbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBtb21lbnRzRGlmZmVyZW5jZShiYXNlLCBvdGhlcikge1xuICAgICAgICB2YXIgcmVzO1xuICAgICAgICBpZiAoIShiYXNlLmlzVmFsaWQoKSAmJiBvdGhlci5pc1ZhbGlkKCkpKSB7XG4gICAgICAgICAgICByZXR1cm4ge21pbGxpc2Vjb25kczogMCwgbW9udGhzOiAwfTtcbiAgICAgICAgfVxuXG4gICAgICAgIG90aGVyID0gY2xvbmVXaXRoT2Zmc2V0KG90aGVyLCBiYXNlKTtcbiAgICAgICAgaWYgKGJhc2UuaXNCZWZvcmUob3RoZXIpKSB7XG4gICAgICAgICAgICByZXMgPSBwb3NpdGl2ZU1vbWVudHNEaWZmZXJlbmNlKGJhc2UsIG90aGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJlcyA9IHBvc2l0aXZlTW9tZW50c0RpZmZlcmVuY2Uob3RoZXIsIGJhc2UpO1xuICAgICAgICAgICAgcmVzLm1pbGxpc2Vjb25kcyA9IC1yZXMubWlsbGlzZWNvbmRzO1xuICAgICAgICAgICAgcmVzLm1vbnRocyA9IC1yZXMubW9udGhzO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBhYnNSb3VuZCAobnVtYmVyKSB7XG4gICAgICAgIGlmIChudW1iZXIgPCAwKSB7XG4gICAgICAgICAgICByZXR1cm4gTWF0aC5yb3VuZCgtMSAqIG51bWJlcikgKiAtMTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBNYXRoLnJvdW5kKG51bWJlcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPOiByZW1vdmUgJ25hbWUnIGFyZyBhZnRlciBkZXByZWNhdGlvbiBpcyByZW1vdmVkXG4gICAgZnVuY3Rpb24gY3JlYXRlQWRkZXIoZGlyZWN0aW9uLCBuYW1lKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbiAodmFsLCBwZXJpb2QpIHtcbiAgICAgICAgICAgIHZhciBkdXIsIHRtcDtcbiAgICAgICAgICAgIC8vaW52ZXJ0IHRoZSBhcmd1bWVudHMsIGJ1dCBjb21wbGFpbiBhYm91dCBpdFxuICAgICAgICAgICAgaWYgKHBlcmlvZCAhPT0gbnVsbCAmJiAhaXNOYU4oK3BlcmlvZCkpIHtcbiAgICAgICAgICAgICAgICBkZXByZWNhdGVTaW1wbGUobmFtZSwgJ21vbWVudCgpLicgKyBuYW1lICArICcocGVyaW9kLCBudW1iZXIpIGlzIGRlcHJlY2F0ZWQuIFBsZWFzZSB1c2UgbW9tZW50KCkuJyArIG5hbWUgKyAnKG51bWJlciwgcGVyaW9kKS4gJyArXG4gICAgICAgICAgICAgICAgJ1NlZSBodHRwOi8vbW9tZW50anMuY29tL2d1aWRlcy8jL3dhcm5pbmdzL2FkZC1pbnZlcnRlZC1wYXJhbS8gZm9yIG1vcmUgaW5mby4nKTtcbiAgICAgICAgICAgICAgICB0bXAgPSB2YWw7IHZhbCA9IHBlcmlvZDsgcGVyaW9kID0gdG1wO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YWwgPSB0eXBlb2YgdmFsID09PSAnc3RyaW5nJyA/ICt2YWwgOiB2YWw7XG4gICAgICAgICAgICBkdXIgPSBjcmVhdGVfX2NyZWF0ZUR1cmF0aW9uKHZhbCwgcGVyaW9kKTtcbiAgICAgICAgICAgIGFkZF9zdWJ0cmFjdF9fYWRkU3VidHJhY3QodGhpcywgZHVyLCBkaXJlY3Rpb24pO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gYWRkX3N1YnRyYWN0X19hZGRTdWJ0cmFjdCAobW9tLCBkdXJhdGlvbiwgaXNBZGRpbmcsIHVwZGF0ZU9mZnNldCkge1xuICAgICAgICB2YXIgbWlsbGlzZWNvbmRzID0gZHVyYXRpb24uX21pbGxpc2Vjb25kcyxcbiAgICAgICAgICAgIGRheXMgPSBhYnNSb3VuZChkdXJhdGlvbi5fZGF5cyksXG4gICAgICAgICAgICBtb250aHMgPSBhYnNSb3VuZChkdXJhdGlvbi5fbW9udGhzKTtcblxuICAgICAgICBpZiAoIW1vbS5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIC8vIE5vIG9wXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB1cGRhdGVPZmZzZXQgPSB1cGRhdGVPZmZzZXQgPT0gbnVsbCA/IHRydWUgOiB1cGRhdGVPZmZzZXQ7XG5cbiAgICAgICAgaWYgKG1pbGxpc2Vjb25kcykge1xuICAgICAgICAgICAgbW9tLl9kLnNldFRpbWUobW9tLl9kLnZhbHVlT2YoKSArIG1pbGxpc2Vjb25kcyAqIGlzQWRkaW5nKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZGF5cykge1xuICAgICAgICAgICAgZ2V0X3NldF9fc2V0KG1vbSwgJ0RhdGUnLCBnZXRfc2V0X19nZXQobW9tLCAnRGF0ZScpICsgZGF5cyAqIGlzQWRkaW5nKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobW9udGhzKSB7XG4gICAgICAgICAgICBzZXRNb250aChtb20sIGdldF9zZXRfX2dldChtb20sICdNb250aCcpICsgbW9udGhzICogaXNBZGRpbmcpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh1cGRhdGVPZmZzZXQpIHtcbiAgICAgICAgICAgIHV0aWxzX2hvb2tzX19ob29rcy51cGRhdGVPZmZzZXQobW9tLCBkYXlzIHx8IG1vbnRocyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgYWRkX3N1YnRyYWN0X19hZGQgICAgICA9IGNyZWF0ZUFkZGVyKDEsICdhZGQnKTtcbiAgICB2YXIgYWRkX3N1YnRyYWN0X19zdWJ0cmFjdCA9IGNyZWF0ZUFkZGVyKC0xLCAnc3VidHJhY3QnKTtcblxuICAgIGZ1bmN0aW9uIGdldENhbGVuZGFyRm9ybWF0KG15TW9tZW50LCBub3cpIHtcbiAgICAgICAgdmFyIGRpZmYgPSBteU1vbWVudC5kaWZmKG5vdywgJ2RheXMnLCB0cnVlKTtcbiAgICAgICAgcmV0dXJuIGRpZmYgPCAtNiA/ICdzYW1lRWxzZScgOlxuICAgICAgICAgICAgICAgIGRpZmYgPCAtMSA/ICdsYXN0V2VlaycgOlxuICAgICAgICAgICAgICAgIGRpZmYgPCAwID8gJ2xhc3REYXknIDpcbiAgICAgICAgICAgICAgICBkaWZmIDwgMSA/ICdzYW1lRGF5JyA6XG4gICAgICAgICAgICAgICAgZGlmZiA8IDIgPyAnbmV4dERheScgOlxuICAgICAgICAgICAgICAgIGRpZmYgPCA3ID8gJ25leHRXZWVrJyA6ICdzYW1lRWxzZSc7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbW9tZW50X2NhbGVuZGFyX19jYWxlbmRhciAodGltZSwgZm9ybWF0cykge1xuICAgICAgICAvLyBXZSB3YW50IHRvIGNvbXBhcmUgdGhlIHN0YXJ0IG9mIHRvZGF5LCB2cyB0aGlzLlxuICAgICAgICAvLyBHZXR0aW5nIHN0YXJ0LW9mLXRvZGF5IGRlcGVuZHMgb24gd2hldGhlciB3ZSdyZSBsb2NhbC91dGMvb2Zmc2V0IG9yIG5vdC5cbiAgICAgICAgdmFyIG5vdyA9IHRpbWUgfHwgbG9jYWxfX2NyZWF0ZUxvY2FsKCksXG4gICAgICAgICAgICBzb2QgPSBjbG9uZVdpdGhPZmZzZXQobm93LCB0aGlzKS5zdGFydE9mKCdkYXknKSxcbiAgICAgICAgICAgIGZvcm1hdCA9IHV0aWxzX2hvb2tzX19ob29rcy5jYWxlbmRhckZvcm1hdCh0aGlzLCBzb2QpIHx8ICdzYW1lRWxzZSc7XG5cbiAgICAgICAgdmFyIG91dHB1dCA9IGZvcm1hdHMgJiYgKGlzRnVuY3Rpb24oZm9ybWF0c1tmb3JtYXRdKSA/IGZvcm1hdHNbZm9ybWF0XS5jYWxsKHRoaXMsIG5vdykgOiBmb3JtYXRzW2Zvcm1hdF0pO1xuXG4gICAgICAgIHJldHVybiB0aGlzLmZvcm1hdChvdXRwdXQgfHwgdGhpcy5sb2NhbGVEYXRhKCkuY2FsZW5kYXIoZm9ybWF0LCB0aGlzLCBsb2NhbF9fY3JlYXRlTG9jYWwobm93KSkpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNsb25lICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBNb21lbnQodGhpcyk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNBZnRlciAoaW5wdXQsIHVuaXRzKSB7XG4gICAgICAgIHZhciBsb2NhbElucHV0ID0gaXNNb21lbnQoaW5wdXQpID8gaW5wdXQgOiBsb2NhbF9fY3JlYXRlTG9jYWwoaW5wdXQpO1xuICAgICAgICBpZiAoISh0aGlzLmlzVmFsaWQoKSAmJiBsb2NhbElucHV0LmlzVmFsaWQoKSkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICB1bml0cyA9IG5vcm1hbGl6ZVVuaXRzKCFpc1VuZGVmaW5lZCh1bml0cykgPyB1bml0cyA6ICdtaWxsaXNlY29uZCcpO1xuICAgICAgICBpZiAodW5pdHMgPT09ICdtaWxsaXNlY29uZCcpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlT2YoKSA+IGxvY2FsSW5wdXQudmFsdWVPZigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIGxvY2FsSW5wdXQudmFsdWVPZigpIDwgdGhpcy5jbG9uZSgpLnN0YXJ0T2YodW5pdHMpLnZhbHVlT2YoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzQmVmb3JlIChpbnB1dCwgdW5pdHMpIHtcbiAgICAgICAgdmFyIGxvY2FsSW5wdXQgPSBpc01vbWVudChpbnB1dCkgPyBpbnB1dCA6IGxvY2FsX19jcmVhdGVMb2NhbChpbnB1dCk7XG4gICAgICAgIGlmICghKHRoaXMuaXNWYWxpZCgpICYmIGxvY2FsSW5wdXQuaXNWYWxpZCgpKSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHVuaXRzID0gbm9ybWFsaXplVW5pdHMoIWlzVW5kZWZpbmVkKHVuaXRzKSA/IHVuaXRzIDogJ21pbGxpc2Vjb25kJyk7XG4gICAgICAgIGlmICh1bml0cyA9PT0gJ21pbGxpc2Vjb25kJykge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVPZigpIDwgbG9jYWxJbnB1dC52YWx1ZU9mKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5jbG9uZSgpLmVuZE9mKHVuaXRzKS52YWx1ZU9mKCkgPCBsb2NhbElucHV0LnZhbHVlT2YoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGlzQmV0d2VlbiAoZnJvbSwgdG8sIHVuaXRzLCBpbmNsdXNpdml0eSkge1xuICAgICAgICBpbmNsdXNpdml0eSA9IGluY2x1c2l2aXR5IHx8ICcoKSc7XG4gICAgICAgIHJldHVybiAoaW5jbHVzaXZpdHlbMF0gPT09ICcoJyA/IHRoaXMuaXNBZnRlcihmcm9tLCB1bml0cykgOiAhdGhpcy5pc0JlZm9yZShmcm9tLCB1bml0cykpICYmXG4gICAgICAgICAgICAoaW5jbHVzaXZpdHlbMV0gPT09ICcpJyA/IHRoaXMuaXNCZWZvcmUodG8sIHVuaXRzKSA6ICF0aGlzLmlzQWZ0ZXIodG8sIHVuaXRzKSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNTYW1lIChpbnB1dCwgdW5pdHMpIHtcbiAgICAgICAgdmFyIGxvY2FsSW5wdXQgPSBpc01vbWVudChpbnB1dCkgPyBpbnB1dCA6IGxvY2FsX19jcmVhdGVMb2NhbChpbnB1dCksXG4gICAgICAgICAgICBpbnB1dE1zO1xuICAgICAgICBpZiAoISh0aGlzLmlzVmFsaWQoKSAmJiBsb2NhbElucHV0LmlzVmFsaWQoKSkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICB1bml0cyA9IG5vcm1hbGl6ZVVuaXRzKHVuaXRzIHx8ICdtaWxsaXNlY29uZCcpO1xuICAgICAgICBpZiAodW5pdHMgPT09ICdtaWxsaXNlY29uZCcpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlT2YoKSA9PT0gbG9jYWxJbnB1dC52YWx1ZU9mKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpbnB1dE1zID0gbG9jYWxJbnB1dC52YWx1ZU9mKCk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5jbG9uZSgpLnN0YXJ0T2YodW5pdHMpLnZhbHVlT2YoKSA8PSBpbnB1dE1zICYmIGlucHV0TXMgPD0gdGhpcy5jbG9uZSgpLmVuZE9mKHVuaXRzKS52YWx1ZU9mKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc1NhbWVPckFmdGVyIChpbnB1dCwgdW5pdHMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaXNTYW1lKGlucHV0LCB1bml0cykgfHwgdGhpcy5pc0FmdGVyKGlucHV0LHVuaXRzKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc1NhbWVPckJlZm9yZSAoaW5wdXQsIHVuaXRzKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmlzU2FtZShpbnB1dCwgdW5pdHMpIHx8IHRoaXMuaXNCZWZvcmUoaW5wdXQsdW5pdHMpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGRpZmYgKGlucHV0LCB1bml0cywgYXNGbG9hdCkge1xuICAgICAgICB2YXIgdGhhdCxcbiAgICAgICAgICAgIHpvbmVEZWx0YSxcbiAgICAgICAgICAgIGRlbHRhLCBvdXRwdXQ7XG5cbiAgICAgICAgaWYgKCF0aGlzLmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgcmV0dXJuIE5hTjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoYXQgPSBjbG9uZVdpdGhPZmZzZXQoaW5wdXQsIHRoaXMpO1xuXG4gICAgICAgIGlmICghdGhhdC5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBOYU47XG4gICAgICAgIH1cblxuICAgICAgICB6b25lRGVsdGEgPSAodGhhdC51dGNPZmZzZXQoKSAtIHRoaXMudXRjT2Zmc2V0KCkpICogNmU0O1xuXG4gICAgICAgIHVuaXRzID0gbm9ybWFsaXplVW5pdHModW5pdHMpO1xuXG4gICAgICAgIGlmICh1bml0cyA9PT0gJ3llYXInIHx8IHVuaXRzID09PSAnbW9udGgnIHx8IHVuaXRzID09PSAncXVhcnRlcicpIHtcbiAgICAgICAgICAgIG91dHB1dCA9IG1vbnRoRGlmZih0aGlzLCB0aGF0KTtcbiAgICAgICAgICAgIGlmICh1bml0cyA9PT0gJ3F1YXJ0ZXInKSB7XG4gICAgICAgICAgICAgICAgb3V0cHV0ID0gb3V0cHV0IC8gMztcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodW5pdHMgPT09ICd5ZWFyJykge1xuICAgICAgICAgICAgICAgIG91dHB1dCA9IG91dHB1dCAvIDEyO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGVsdGEgPSB0aGlzIC0gdGhhdDtcbiAgICAgICAgICAgIG91dHB1dCA9IHVuaXRzID09PSAnc2Vjb25kJyA/IGRlbHRhIC8gMWUzIDogLy8gMTAwMFxuICAgICAgICAgICAgICAgIHVuaXRzID09PSAnbWludXRlJyA/IGRlbHRhIC8gNmU0IDogLy8gMTAwMCAqIDYwXG4gICAgICAgICAgICAgICAgdW5pdHMgPT09ICdob3VyJyA/IGRlbHRhIC8gMzZlNSA6IC8vIDEwMDAgKiA2MCAqIDYwXG4gICAgICAgICAgICAgICAgdW5pdHMgPT09ICdkYXknID8gKGRlbHRhIC0gem9uZURlbHRhKSAvIDg2NGU1IDogLy8gMTAwMCAqIDYwICogNjAgKiAyNCwgbmVnYXRlIGRzdFxuICAgICAgICAgICAgICAgIHVuaXRzID09PSAnd2VlaycgPyAoZGVsdGEgLSB6b25lRGVsdGEpIC8gNjA0OGU1IDogLy8gMTAwMCAqIDYwICogNjAgKiAyNCAqIDcsIG5lZ2F0ZSBkc3RcbiAgICAgICAgICAgICAgICBkZWx0YTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXNGbG9hdCA/IG91dHB1dCA6IGFic0Zsb29yKG91dHB1dCk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbW9udGhEaWZmIChhLCBiKSB7XG4gICAgICAgIC8vIGRpZmZlcmVuY2UgaW4gbW9udGhzXG4gICAgICAgIHZhciB3aG9sZU1vbnRoRGlmZiA9ICgoYi55ZWFyKCkgLSBhLnllYXIoKSkgKiAxMikgKyAoYi5tb250aCgpIC0gYS5tb250aCgpKSxcbiAgICAgICAgICAgIC8vIGIgaXMgaW4gKGFuY2hvciAtIDEgbW9udGgsIGFuY2hvciArIDEgbW9udGgpXG4gICAgICAgICAgICBhbmNob3IgPSBhLmNsb25lKCkuYWRkKHdob2xlTW9udGhEaWZmLCAnbW9udGhzJyksXG4gICAgICAgICAgICBhbmNob3IyLCBhZGp1c3Q7XG5cbiAgICAgICAgaWYgKGIgLSBhbmNob3IgPCAwKSB7XG4gICAgICAgICAgICBhbmNob3IyID0gYS5jbG9uZSgpLmFkZCh3aG9sZU1vbnRoRGlmZiAtIDEsICdtb250aHMnKTtcbiAgICAgICAgICAgIC8vIGxpbmVhciBhY3Jvc3MgdGhlIG1vbnRoXG4gICAgICAgICAgICBhZGp1c3QgPSAoYiAtIGFuY2hvcikgLyAoYW5jaG9yIC0gYW5jaG9yMik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBhbmNob3IyID0gYS5jbG9uZSgpLmFkZCh3aG9sZU1vbnRoRGlmZiArIDEsICdtb250aHMnKTtcbiAgICAgICAgICAgIC8vIGxpbmVhciBhY3Jvc3MgdGhlIG1vbnRoXG4gICAgICAgICAgICBhZGp1c3QgPSAoYiAtIGFuY2hvcikgLyAoYW5jaG9yMiAtIGFuY2hvcik7XG4gICAgICAgIH1cblxuICAgICAgICAvL2NoZWNrIGZvciBuZWdhdGl2ZSB6ZXJvLCByZXR1cm4gemVybyBpZiBuZWdhdGl2ZSB6ZXJvXG4gICAgICAgIHJldHVybiAtKHdob2xlTW9udGhEaWZmICsgYWRqdXN0KSB8fCAwO1xuICAgIH1cblxuICAgIHV0aWxzX2hvb2tzX19ob29rcy5kZWZhdWx0Rm9ybWF0ID0gJ1lZWVktTU0tRERUSEg6bW06c3NaJztcbiAgICB1dGlsc19ob29rc19faG9va3MuZGVmYXVsdEZvcm1hdFV0YyA9ICdZWVlZLU1NLUREVEhIOm1tOnNzW1pdJztcblxuICAgIGZ1bmN0aW9uIHRvU3RyaW5nICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2xvbmUoKS5sb2NhbGUoJ2VuJykuZm9ybWF0KCdkZGQgTU1NIEREIFlZWVkgSEg6bW06c3MgW0dNVF1aWicpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIG1vbWVudF9mb3JtYXRfX3RvSVNPU3RyaW5nICgpIHtcbiAgICAgICAgdmFyIG0gPSB0aGlzLmNsb25lKCkudXRjKCk7XG4gICAgICAgIGlmICgwIDwgbS55ZWFyKCkgJiYgbS55ZWFyKCkgPD0gOTk5OSkge1xuICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24oRGF0ZS5wcm90b3R5cGUudG9JU09TdHJpbmcpKSB7XG4gICAgICAgICAgICAgICAgLy8gbmF0aXZlIGltcGxlbWVudGF0aW9uIGlzIH41MHggZmFzdGVyLCB1c2UgaXQgd2hlbiB3ZSBjYW5cbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy50b0RhdGUoKS50b0lTT1N0cmluZygpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZm9ybWF0TW9tZW50KG0sICdZWVlZLU1NLUREW1RdSEg6bW06c3MuU1NTW1pdJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0TW9tZW50KG0sICdZWVlZWVktTU0tRERbVF1ISDptbTpzcy5TU1NbWl0nKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGZvcm1hdCAoaW5wdXRTdHJpbmcpIHtcbiAgICAgICAgaWYgKCFpbnB1dFN0cmluZykge1xuICAgICAgICAgICAgaW5wdXRTdHJpbmcgPSB0aGlzLmlzVXRjKCkgPyB1dGlsc19ob29rc19faG9va3MuZGVmYXVsdEZvcm1hdFV0YyA6IHV0aWxzX2hvb2tzX19ob29rcy5kZWZhdWx0Rm9ybWF0O1xuICAgICAgICB9XG4gICAgICAgIHZhciBvdXRwdXQgPSBmb3JtYXRNb21lbnQodGhpcywgaW5wdXRTdHJpbmcpO1xuICAgICAgICByZXR1cm4gdGhpcy5sb2NhbGVEYXRhKCkucG9zdGZvcm1hdChvdXRwdXQpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGZyb20gKHRpbWUsIHdpdGhvdXRTdWZmaXgpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNWYWxpZCgpICYmXG4gICAgICAgICAgICAgICAgKChpc01vbWVudCh0aW1lKSAmJiB0aW1lLmlzVmFsaWQoKSkgfHxcbiAgICAgICAgICAgICAgICAgbG9jYWxfX2NyZWF0ZUxvY2FsKHRpbWUpLmlzVmFsaWQoKSkpIHtcbiAgICAgICAgICAgIHJldHVybiBjcmVhdGVfX2NyZWF0ZUR1cmF0aW9uKHt0bzogdGhpcywgZnJvbTogdGltZX0pLmxvY2FsZSh0aGlzLmxvY2FsZSgpKS5odW1hbml6ZSghd2l0aG91dFN1ZmZpeCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5sb2NhbGVEYXRhKCkuaW52YWxpZERhdGUoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGZyb21Ob3cgKHdpdGhvdXRTdWZmaXgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZnJvbShsb2NhbF9fY3JlYXRlTG9jYWwoKSwgd2l0aG91dFN1ZmZpeCk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdG8gKHRpbWUsIHdpdGhvdXRTdWZmaXgpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNWYWxpZCgpICYmXG4gICAgICAgICAgICAgICAgKChpc01vbWVudCh0aW1lKSAmJiB0aW1lLmlzVmFsaWQoKSkgfHxcbiAgICAgICAgICAgICAgICAgbG9jYWxfX2NyZWF0ZUxvY2FsKHRpbWUpLmlzVmFsaWQoKSkpIHtcbiAgICAgICAgICAgIHJldHVybiBjcmVhdGVfX2NyZWF0ZUR1cmF0aW9uKHtmcm9tOiB0aGlzLCB0bzogdGltZX0pLmxvY2FsZSh0aGlzLmxvY2FsZSgpKS5odW1hbml6ZSghd2l0aG91dFN1ZmZpeCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5sb2NhbGVEYXRhKCkuaW52YWxpZERhdGUoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHRvTm93ICh3aXRob3V0U3VmZml4KSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRvKGxvY2FsX19jcmVhdGVMb2NhbCgpLCB3aXRob3V0U3VmZml4KTtcbiAgICB9XG5cbiAgICAvLyBJZiBwYXNzZWQgYSBsb2NhbGUga2V5LCBpdCB3aWxsIHNldCB0aGUgbG9jYWxlIGZvciB0aGlzXG4gICAgLy8gaW5zdGFuY2UuICBPdGhlcndpc2UsIGl0IHdpbGwgcmV0dXJuIHRoZSBsb2NhbGUgY29uZmlndXJhdGlvblxuICAgIC8vIHZhcmlhYmxlcyBmb3IgdGhpcyBpbnN0YW5jZS5cbiAgICBmdW5jdGlvbiBsb2NhbGUgKGtleSkge1xuICAgICAgICB2YXIgbmV3TG9jYWxlRGF0YTtcblxuICAgICAgICBpZiAoa2V5ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9sb2NhbGUuX2FiYnI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBuZXdMb2NhbGVEYXRhID0gbG9jYWxlX2xvY2FsZXNfX2dldExvY2FsZShrZXkpO1xuICAgICAgICAgICAgaWYgKG5ld0xvY2FsZURhdGEgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRoaXMuX2xvY2FsZSA9IG5ld0xvY2FsZURhdGE7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHZhciBsYW5nID0gZGVwcmVjYXRlKFxuICAgICAgICAnbW9tZW50KCkubGFuZygpIGlzIGRlcHJlY2F0ZWQuIEluc3RlYWQsIHVzZSBtb21lbnQoKS5sb2NhbGVEYXRhKCkgdG8gZ2V0IHRoZSBsYW5ndWFnZSBjb25maWd1cmF0aW9uLiBVc2UgbW9tZW50KCkubG9jYWxlKCkgdG8gY2hhbmdlIGxhbmd1YWdlcy4nLFxuICAgICAgICBmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICBpZiAoa2V5ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5sb2NhbGVEYXRhKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmxvY2FsZShrZXkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgKTtcblxuICAgIGZ1bmN0aW9uIGxvY2FsZURhdGEgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fbG9jYWxlO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHN0YXJ0T2YgKHVuaXRzKSB7XG4gICAgICAgIHVuaXRzID0gbm9ybWFsaXplVW5pdHModW5pdHMpO1xuICAgICAgICAvLyB0aGUgZm9sbG93aW5nIHN3aXRjaCBpbnRlbnRpb25hbGx5IG9taXRzIGJyZWFrIGtleXdvcmRzXG4gICAgICAgIC8vIHRvIHV0aWxpemUgZmFsbGluZyB0aHJvdWdoIHRoZSBjYXNlcy5cbiAgICAgICAgc3dpdGNoICh1bml0cykge1xuICAgICAgICAgICAgY2FzZSAneWVhcic6XG4gICAgICAgICAgICAgICAgdGhpcy5tb250aCgwKTtcbiAgICAgICAgICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICAgICAgICBjYXNlICdxdWFydGVyJzpcbiAgICAgICAgICAgIGNhc2UgJ21vbnRoJzpcbiAgICAgICAgICAgICAgICB0aGlzLmRhdGUoMSk7XG4gICAgICAgICAgICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgICAgICAgICAgY2FzZSAnd2Vlayc6XG4gICAgICAgICAgICBjYXNlICdpc29XZWVrJzpcbiAgICAgICAgICAgIGNhc2UgJ2RheSc6XG4gICAgICAgICAgICBjYXNlICdkYXRlJzpcbiAgICAgICAgICAgICAgICB0aGlzLmhvdXJzKDApO1xuICAgICAgICAgICAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICAgICAgICAgIGNhc2UgJ2hvdXInOlxuICAgICAgICAgICAgICAgIHRoaXMubWludXRlcygwKTtcbiAgICAgICAgICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICAgICAgICBjYXNlICdtaW51dGUnOlxuICAgICAgICAgICAgICAgIHRoaXMuc2Vjb25kcygwKTtcbiAgICAgICAgICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICAgICAgICBjYXNlICdzZWNvbmQnOlxuICAgICAgICAgICAgICAgIHRoaXMubWlsbGlzZWNvbmRzKDApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gd2Vla3MgYXJlIGEgc3BlY2lhbCBjYXNlXG4gICAgICAgIGlmICh1bml0cyA9PT0gJ3dlZWsnKSB7XG4gICAgICAgICAgICB0aGlzLndlZWtkYXkoMCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHVuaXRzID09PSAnaXNvV2VlaycpIHtcbiAgICAgICAgICAgIHRoaXMuaXNvV2Vla2RheSgxKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHF1YXJ0ZXJzIGFyZSBhbHNvIHNwZWNpYWxcbiAgICAgICAgaWYgKHVuaXRzID09PSAncXVhcnRlcicpIHtcbiAgICAgICAgICAgIHRoaXMubW9udGgoTWF0aC5mbG9vcih0aGlzLm1vbnRoKCkgLyAzKSAqIDMpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZW5kT2YgKHVuaXRzKSB7XG4gICAgICAgIHVuaXRzID0gbm9ybWFsaXplVW5pdHModW5pdHMpO1xuICAgICAgICBpZiAodW5pdHMgPT09IHVuZGVmaW5lZCB8fCB1bml0cyA9PT0gJ21pbGxpc2Vjb25kJykge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cblxuICAgICAgICAvLyAnZGF0ZScgaXMgYW4gYWxpYXMgZm9yICdkYXknLCBzbyBpdCBzaG91bGQgYmUgY29uc2lkZXJlZCBhcyBzdWNoLlxuICAgICAgICBpZiAodW5pdHMgPT09ICdkYXRlJykge1xuICAgICAgICAgICAgdW5pdHMgPSAnZGF5JztcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzLnN0YXJ0T2YodW5pdHMpLmFkZCgxLCAodW5pdHMgPT09ICdpc29XZWVrJyA/ICd3ZWVrJyA6IHVuaXRzKSkuc3VidHJhY3QoMSwgJ21zJyk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdG9fdHlwZV9fdmFsdWVPZiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9kLnZhbHVlT2YoKSAtICgodGhpcy5fb2Zmc2V0IHx8IDApICogNjAwMDApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHVuaXggKCkge1xuICAgICAgICByZXR1cm4gTWF0aC5mbG9vcih0aGlzLnZhbHVlT2YoKSAvIDEwMDApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHRvRGF0ZSAoKSB7XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZSh0aGlzLnZhbHVlT2YoKSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdG9BcnJheSAoKSB7XG4gICAgICAgIHZhciBtID0gdGhpcztcbiAgICAgICAgcmV0dXJuIFttLnllYXIoKSwgbS5tb250aCgpLCBtLmRhdGUoKSwgbS5ob3VyKCksIG0ubWludXRlKCksIG0uc2Vjb25kKCksIG0ubWlsbGlzZWNvbmQoKV07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdG9PYmplY3QgKCkge1xuICAgICAgICB2YXIgbSA9IHRoaXM7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB5ZWFyczogbS55ZWFyKCksXG4gICAgICAgICAgICBtb250aHM6IG0ubW9udGgoKSxcbiAgICAgICAgICAgIGRhdGU6IG0uZGF0ZSgpLFxuICAgICAgICAgICAgaG91cnM6IG0uaG91cnMoKSxcbiAgICAgICAgICAgIG1pbnV0ZXM6IG0ubWludXRlcygpLFxuICAgICAgICAgICAgc2Vjb25kczogbS5zZWNvbmRzKCksXG4gICAgICAgICAgICBtaWxsaXNlY29uZHM6IG0ubWlsbGlzZWNvbmRzKClcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB0b0pTT04gKCkge1xuICAgICAgICAvLyBuZXcgRGF0ZShOYU4pLnRvSlNPTigpID09PSBudWxsXG4gICAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQoKSA/IHRoaXMudG9JU09TdHJpbmcoKSA6IG51bGw7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gbW9tZW50X3ZhbGlkX19pc1ZhbGlkICgpIHtcbiAgICAgICAgcmV0dXJuIHZhbGlkX19pc1ZhbGlkKHRoaXMpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHBhcnNpbmdGbGFncyAoKSB7XG4gICAgICAgIHJldHVybiBleHRlbmQoe30sIGdldFBhcnNpbmdGbGFncyh0aGlzKSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaW52YWxpZEF0ICgpIHtcbiAgICAgICAgcmV0dXJuIGdldFBhcnNpbmdGbGFncyh0aGlzKS5vdmVyZmxvdztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjcmVhdGlvbkRhdGEoKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBpbnB1dDogdGhpcy5faSxcbiAgICAgICAgICAgIGZvcm1hdDogdGhpcy5fZixcbiAgICAgICAgICAgIGxvY2FsZTogdGhpcy5fbG9jYWxlLFxuICAgICAgICAgICAgaXNVVEM6IHRoaXMuX2lzVVRDLFxuICAgICAgICAgICAgc3RyaWN0OiB0aGlzLl9zdHJpY3RcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbigwLCBbJ2dnJywgMl0sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMud2Vla1llYXIoKSAlIDEwMDtcbiAgICB9KTtcblxuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnR0cnLCAyXSwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5pc29XZWVrWWVhcigpICUgMTAwO1xuICAgIH0pO1xuXG4gICAgZnVuY3Rpb24gYWRkV2Vla1llYXJGb3JtYXRUb2tlbiAodG9rZW4sIGdldHRlcikge1xuICAgICAgICBhZGRGb3JtYXRUb2tlbigwLCBbdG9rZW4sIHRva2VuLmxlbmd0aF0sIDAsIGdldHRlcik7XG4gICAgfVxuXG4gICAgYWRkV2Vla1llYXJGb3JtYXRUb2tlbignZ2dnZycsICAgICAnd2Vla1llYXInKTtcbiAgICBhZGRXZWVrWWVhckZvcm1hdFRva2VuKCdnZ2dnZycsICAgICd3ZWVrWWVhcicpO1xuICAgIGFkZFdlZWtZZWFyRm9ybWF0VG9rZW4oJ0dHR0cnLCAgJ2lzb1dlZWtZZWFyJyk7XG4gICAgYWRkV2Vla1llYXJGb3JtYXRUb2tlbignR0dHR0cnLCAnaXNvV2Vla1llYXInKTtcblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnd2Vla1llYXInLCAnZ2cnKTtcbiAgICBhZGRVbml0QWxpYXMoJ2lzb1dlZWtZZWFyJywgJ0dHJyk7XG5cbiAgICAvLyBQUklPUklUWVxuXG4gICAgYWRkVW5pdFByaW9yaXR5KCd3ZWVrWWVhcicsIDEpO1xuICAgIGFkZFVuaXRQcmlvcml0eSgnaXNvV2Vla1llYXInLCAxKTtcblxuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgYWRkUmVnZXhUb2tlbignRycsICAgICAgbWF0Y2hTaWduZWQpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2cnLCAgICAgIG1hdGNoU2lnbmVkKTtcbiAgICBhZGRSZWdleFRva2VuKCdHRycsICAgICBtYXRjaDF0bzIsIG1hdGNoMik7XG4gICAgYWRkUmVnZXhUb2tlbignZ2cnLCAgICAgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ0dHR0cnLCAgIG1hdGNoMXRvNCwgbWF0Y2g0KTtcbiAgICBhZGRSZWdleFRva2VuKCdnZ2dnJywgICBtYXRjaDF0bzQsIG1hdGNoNCk7XG4gICAgYWRkUmVnZXhUb2tlbignR0dHR0cnLCAgbWF0Y2gxdG82LCBtYXRjaDYpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ2dnZ2dnJywgIG1hdGNoMXRvNiwgbWF0Y2g2KTtcblxuICAgIGFkZFdlZWtQYXJzZVRva2VuKFsnZ2dnZycsICdnZ2dnZycsICdHR0dHJywgJ0dHR0dHJ10sIGZ1bmN0aW9uIChpbnB1dCwgd2VlaywgY29uZmlnLCB0b2tlbikge1xuICAgICAgICB3ZWVrW3Rva2VuLnN1YnN0cigwLCAyKV0gPSB0b0ludChpbnB1dCk7XG4gICAgfSk7XG5cbiAgICBhZGRXZWVrUGFyc2VUb2tlbihbJ2dnJywgJ0dHJ10sIGZ1bmN0aW9uIChpbnB1dCwgd2VlaywgY29uZmlnLCB0b2tlbikge1xuICAgICAgICB3ZWVrW3Rva2VuXSA9IHV0aWxzX2hvb2tzX19ob29rcy5wYXJzZVR3b0RpZ2l0WWVhcihpbnB1dCk7XG4gICAgfSk7XG5cbiAgICAvLyBNT01FTlRTXG5cbiAgICBmdW5jdGlvbiBnZXRTZXRXZWVrWWVhciAoaW5wdXQpIHtcbiAgICAgICAgcmV0dXJuIGdldFNldFdlZWtZZWFySGVscGVyLmNhbGwodGhpcyxcbiAgICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgICAgICB0aGlzLndlZWsoKSxcbiAgICAgICAgICAgICAgICB0aGlzLndlZWtkYXkoKSxcbiAgICAgICAgICAgICAgICB0aGlzLmxvY2FsZURhdGEoKS5fd2Vlay5kb3csXG4gICAgICAgICAgICAgICAgdGhpcy5sb2NhbGVEYXRhKCkuX3dlZWsuZG95KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTZXRJU09XZWVrWWVhciAoaW5wdXQpIHtcbiAgICAgICAgcmV0dXJuIGdldFNldFdlZWtZZWFySGVscGVyLmNhbGwodGhpcyxcbiAgICAgICAgICAgICAgICBpbnB1dCwgdGhpcy5pc29XZWVrKCksIHRoaXMuaXNvV2Vla2RheSgpLCAxLCA0KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRJU09XZWVrc0luWWVhciAoKSB7XG4gICAgICAgIHJldHVybiB3ZWVrc0luWWVhcih0aGlzLnllYXIoKSwgMSwgNCk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0V2Vla3NJblllYXIgKCkge1xuICAgICAgICB2YXIgd2Vla0luZm8gPSB0aGlzLmxvY2FsZURhdGEoKS5fd2VlaztcbiAgICAgICAgcmV0dXJuIHdlZWtzSW5ZZWFyKHRoaXMueWVhcigpLCB3ZWVrSW5mby5kb3csIHdlZWtJbmZvLmRveSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0U2V0V2Vla1llYXJIZWxwZXIoaW5wdXQsIHdlZWssIHdlZWtkYXksIGRvdywgZG95KSB7XG4gICAgICAgIHZhciB3ZWVrc1RhcmdldDtcbiAgICAgICAgaWYgKGlucHV0ID09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiB3ZWVrT2ZZZWFyKHRoaXMsIGRvdywgZG95KS55ZWFyO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgd2Vla3NUYXJnZXQgPSB3ZWVrc0luWWVhcihpbnB1dCwgZG93LCBkb3kpO1xuICAgICAgICAgICAgaWYgKHdlZWsgPiB3ZWVrc1RhcmdldCkge1xuICAgICAgICAgICAgICAgIHdlZWsgPSB3ZWVrc1RhcmdldDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBzZXRXZWVrQWxsLmNhbGwodGhpcywgaW5wdXQsIHdlZWssIHdlZWtkYXksIGRvdywgZG95KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHNldFdlZWtBbGwod2Vla1llYXIsIHdlZWssIHdlZWtkYXksIGRvdywgZG95KSB7XG4gICAgICAgIHZhciBkYXlPZlllYXJEYXRhID0gZGF5T2ZZZWFyRnJvbVdlZWtzKHdlZWtZZWFyLCB3ZWVrLCB3ZWVrZGF5LCBkb3csIGRveSksXG4gICAgICAgICAgICBkYXRlID0gY3JlYXRlVVRDRGF0ZShkYXlPZlllYXJEYXRhLnllYXIsIDAsIGRheU9mWWVhckRhdGEuZGF5T2ZZZWFyKTtcblxuICAgICAgICB0aGlzLnllYXIoZGF0ZS5nZXRVVENGdWxsWWVhcigpKTtcbiAgICAgICAgdGhpcy5tb250aChkYXRlLmdldFVUQ01vbnRoKCkpO1xuICAgICAgICB0aGlzLmRhdGUoZGF0ZS5nZXRVVENEYXRlKCkpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbignUScsIDAsICdRbycsICdxdWFydGVyJyk7XG5cbiAgICAvLyBBTElBU0VTXG5cbiAgICBhZGRVbml0QWxpYXMoJ3F1YXJ0ZXInLCAnUScpO1xuXG4gICAgLy8gUFJJT1JJVFlcblxuICAgIGFkZFVuaXRQcmlvcml0eSgncXVhcnRlcicsIDcpO1xuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgYWRkUmVnZXhUb2tlbignUScsIG1hdGNoMSk7XG4gICAgYWRkUGFyc2VUb2tlbignUScsIGZ1bmN0aW9uIChpbnB1dCwgYXJyYXkpIHtcbiAgICAgICAgYXJyYXlbTU9OVEhdID0gKHRvSW50KGlucHV0KSAtIDEpICogMztcbiAgICB9KTtcblxuICAgIC8vIE1PTUVOVFNcblxuICAgIGZ1bmN0aW9uIGdldFNldFF1YXJ0ZXIgKGlucHV0KSB7XG4gICAgICAgIHJldHVybiBpbnB1dCA9PSBudWxsID8gTWF0aC5jZWlsKCh0aGlzLm1vbnRoKCkgKyAxKSAvIDMpIDogdGhpcy5tb250aCgoaW5wdXQgLSAxKSAqIDMgKyB0aGlzLm1vbnRoKCkgJSAzKTtcbiAgICB9XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbignRCcsIFsnREQnLCAyXSwgJ0RvJywgJ2RhdGUnKTtcblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnZGF0ZScsICdEJyk7XG5cbiAgICAvLyBQUklPUk9JVFlcbiAgICBhZGRVbml0UHJpb3JpdHkoJ2RhdGUnLCA5KTtcblxuICAgIC8vIFBBUlNJTkdcblxuICAgIGFkZFJlZ2V4VG9rZW4oJ0QnLCAgbWF0Y2gxdG8yKTtcbiAgICBhZGRSZWdleFRva2VuKCdERCcsIG1hdGNoMXRvMiwgbWF0Y2gyKTtcbiAgICBhZGRSZWdleFRva2VuKCdEbycsIGZ1bmN0aW9uIChpc1N0cmljdCwgbG9jYWxlKSB7XG4gICAgICAgIHJldHVybiBpc1N0cmljdCA/IGxvY2FsZS5fb3JkaW5hbFBhcnNlIDogbG9jYWxlLl9vcmRpbmFsUGFyc2VMZW5pZW50O1xuICAgIH0pO1xuXG4gICAgYWRkUGFyc2VUb2tlbihbJ0QnLCAnREQnXSwgREFURSk7XG4gICAgYWRkUGFyc2VUb2tlbignRG8nLCBmdW5jdGlvbiAoaW5wdXQsIGFycmF5KSB7XG4gICAgICAgIGFycmF5W0RBVEVdID0gdG9JbnQoaW5wdXQubWF0Y2gobWF0Y2gxdG8yKVswXSwgMTApO1xuICAgIH0pO1xuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgdmFyIGdldFNldERheU9mTW9udGggPSBtYWtlR2V0U2V0KCdEYXRlJywgdHJ1ZSk7XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbignREREJywgWydEREREJywgM10sICdERERvJywgJ2RheU9mWWVhcicpO1xuXG4gICAgLy8gQUxJQVNFU1xuXG4gICAgYWRkVW5pdEFsaWFzKCdkYXlPZlllYXInLCAnREREJyk7XG5cbiAgICAvLyBQUklPUklUWVxuICAgIGFkZFVuaXRQcmlvcml0eSgnZGF5T2ZZZWFyJywgNCk7XG5cbiAgICAvLyBQQVJTSU5HXG5cbiAgICBhZGRSZWdleFRva2VuKCdEREQnLCAgbWF0Y2gxdG8zKTtcbiAgICBhZGRSZWdleFRva2VuKCdEREREJywgbWF0Y2gzKTtcbiAgICBhZGRQYXJzZVRva2VuKFsnREREJywgJ0REREQnXSwgZnVuY3Rpb24gKGlucHV0LCBhcnJheSwgY29uZmlnKSB7XG4gICAgICAgIGNvbmZpZy5fZGF5T2ZZZWFyID0gdG9JbnQoaW5wdXQpO1xuICAgIH0pO1xuXG4gICAgLy8gSEVMUEVSU1xuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgZnVuY3Rpb24gZ2V0U2V0RGF5T2ZZZWFyIChpbnB1dCkge1xuICAgICAgICB2YXIgZGF5T2ZZZWFyID0gTWF0aC5yb3VuZCgodGhpcy5jbG9uZSgpLnN0YXJ0T2YoJ2RheScpIC0gdGhpcy5jbG9uZSgpLnN0YXJ0T2YoJ3llYXInKSkgLyA4NjRlNSkgKyAxO1xuICAgICAgICByZXR1cm4gaW5wdXQgPT0gbnVsbCA/IGRheU9mWWVhciA6IHRoaXMuYWRkKChpbnB1dCAtIGRheU9mWWVhciksICdkJyk7XG4gICAgfVxuXG4gICAgLy8gRk9STUFUVElOR1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oJ20nLCBbJ21tJywgMl0sIDAsICdtaW51dGUnKTtcblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnbWludXRlJywgJ20nKTtcblxuICAgIC8vIFBSSU9SSVRZXG5cbiAgICBhZGRVbml0UHJpb3JpdHkoJ21pbnV0ZScsIDE0KTtcblxuICAgIC8vIFBBUlNJTkdcblxuICAgIGFkZFJlZ2V4VG9rZW4oJ20nLCAgbWF0Y2gxdG8yKTtcbiAgICBhZGRSZWdleFRva2VuKCdtbScsIG1hdGNoMXRvMiwgbWF0Y2gyKTtcbiAgICBhZGRQYXJzZVRva2VuKFsnbScsICdtbSddLCBNSU5VVEUpO1xuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgdmFyIGdldFNldE1pbnV0ZSA9IG1ha2VHZXRTZXQoJ01pbnV0ZXMnLCBmYWxzZSk7XG5cbiAgICAvLyBGT1JNQVRUSU5HXG5cbiAgICBhZGRGb3JtYXRUb2tlbigncycsIFsnc3MnLCAyXSwgMCwgJ3NlY29uZCcpO1xuXG4gICAgLy8gQUxJQVNFU1xuXG4gICAgYWRkVW5pdEFsaWFzKCdzZWNvbmQnLCAncycpO1xuXG4gICAgLy8gUFJJT1JJVFlcblxuICAgIGFkZFVuaXRQcmlvcml0eSgnc2Vjb25kJywgMTUpO1xuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgYWRkUmVnZXhUb2tlbigncycsICBtYXRjaDF0bzIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ3NzJywgbWF0Y2gxdG8yLCBtYXRjaDIpO1xuICAgIGFkZFBhcnNlVG9rZW4oWydzJywgJ3NzJ10sIFNFQ09ORCk7XG5cbiAgICAvLyBNT01FTlRTXG5cbiAgICB2YXIgZ2V0U2V0U2Vjb25kID0gbWFrZUdldFNldCgnU2Vjb25kcycsIGZhbHNlKTtcblxuICAgIC8vIEZPUk1BVFRJTkdcblxuICAgIGFkZEZvcm1hdFRva2VuKCdTJywgMCwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gfn4odGhpcy5taWxsaXNlY29uZCgpIC8gMTAwKTtcbiAgICB9KTtcblxuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnU1MnLCAyXSwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gfn4odGhpcy5taWxsaXNlY29uZCgpIC8gMTApO1xuICAgIH0pO1xuXG4gICAgYWRkRm9ybWF0VG9rZW4oMCwgWydTU1MnLCAzXSwgMCwgJ21pbGxpc2Vjb25kJyk7XG4gICAgYWRkRm9ybWF0VG9rZW4oMCwgWydTU1NTJywgNF0sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWlsbGlzZWNvbmQoKSAqIDEwO1xuICAgIH0pO1xuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnU1NTU1MnLCA1XSwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5taWxsaXNlY29uZCgpICogMTAwO1xuICAgIH0pO1xuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnU1NTU1NTJywgNl0sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWlsbGlzZWNvbmQoKSAqIDEwMDA7XG4gICAgfSk7XG4gICAgYWRkRm9ybWF0VG9rZW4oMCwgWydTU1NTU1NTJywgN10sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWlsbGlzZWNvbmQoKSAqIDEwMDAwO1xuICAgIH0pO1xuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnU1NTU1NTU1MnLCA4XSwgMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5taWxsaXNlY29uZCgpICogMTAwMDAwO1xuICAgIH0pO1xuICAgIGFkZEZvcm1hdFRva2VuKDAsIFsnU1NTU1NTU1NTJywgOV0sIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWlsbGlzZWNvbmQoKSAqIDEwMDAwMDA7XG4gICAgfSk7XG5cblxuICAgIC8vIEFMSUFTRVNcblxuICAgIGFkZFVuaXRBbGlhcygnbWlsbGlzZWNvbmQnLCAnbXMnKTtcblxuICAgIC8vIFBSSU9SSVRZXG5cbiAgICBhZGRVbml0UHJpb3JpdHkoJ21pbGxpc2Vjb25kJywgMTYpO1xuXG4gICAgLy8gUEFSU0lOR1xuXG4gICAgYWRkUmVnZXhUb2tlbignUycsICAgIG1hdGNoMXRvMywgbWF0Y2gxKTtcbiAgICBhZGRSZWdleFRva2VuKCdTUycsICAgbWF0Y2gxdG8zLCBtYXRjaDIpO1xuICAgIGFkZFJlZ2V4VG9rZW4oJ1NTUycsICBtYXRjaDF0bzMsIG1hdGNoMyk7XG5cbiAgICB2YXIgdG9rZW47XG4gICAgZm9yICh0b2tlbiA9ICdTU1NTJzsgdG9rZW4ubGVuZ3RoIDw9IDk7IHRva2VuICs9ICdTJykge1xuICAgICAgICBhZGRSZWdleFRva2VuKHRva2VuLCBtYXRjaFVuc2lnbmVkKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBwYXJzZU1zKGlucHV0LCBhcnJheSkge1xuICAgICAgICBhcnJheVtNSUxMSVNFQ09ORF0gPSB0b0ludCgoJzAuJyArIGlucHV0KSAqIDEwMDApO1xuICAgIH1cblxuICAgIGZvciAodG9rZW4gPSAnUyc7IHRva2VuLmxlbmd0aCA8PSA5OyB0b2tlbiArPSAnUycpIHtcbiAgICAgICAgYWRkUGFyc2VUb2tlbih0b2tlbiwgcGFyc2VNcyk7XG4gICAgfVxuICAgIC8vIE1PTUVOVFNcblxuICAgIHZhciBnZXRTZXRNaWxsaXNlY29uZCA9IG1ha2VHZXRTZXQoJ01pbGxpc2Vjb25kcycsIGZhbHNlKTtcblxuICAgIC8vIEZPUk1BVFRJTkdcblxuICAgIGFkZEZvcm1hdFRva2VuKCd6JywgIDAsIDAsICd6b25lQWJicicpO1xuICAgIGFkZEZvcm1hdFRva2VuKCd6eicsIDAsIDAsICd6b25lTmFtZScpO1xuXG4gICAgLy8gTU9NRU5UU1xuXG4gICAgZnVuY3Rpb24gZ2V0Wm9uZUFiYnIgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5faXNVVEMgPyAnVVRDJyA6ICcnO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFpvbmVOYW1lICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2lzVVRDID8gJ0Nvb3JkaW5hdGVkIFVuaXZlcnNhbCBUaW1lJyA6ICcnO1xuICAgIH1cblxuICAgIHZhciBtb21lbnRQcm90b3R5cGVfX3Byb3RvID0gTW9tZW50LnByb3RvdHlwZTtcblxuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8uYWRkICAgICAgICAgICAgICAgPSBhZGRfc3VidHJhY3RfX2FkZDtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmNhbGVuZGFyICAgICAgICAgID0gbW9tZW50X2NhbGVuZGFyX19jYWxlbmRhcjtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmNsb25lICAgICAgICAgICAgID0gY2xvbmU7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5kaWZmICAgICAgICAgICAgICA9IGRpZmY7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5lbmRPZiAgICAgICAgICAgICA9IGVuZE9mO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8uZm9ybWF0ICAgICAgICAgICAgPSBmb3JtYXQ7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5mcm9tICAgICAgICAgICAgICA9IGZyb207XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5mcm9tTm93ICAgICAgICAgICA9IGZyb21Ob3c7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by50byAgICAgICAgICAgICAgICA9IHRvO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8udG9Ob3cgICAgICAgICAgICAgPSB0b05vdztcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmdldCAgICAgICAgICAgICAgID0gc3RyaW5nR2V0O1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8uaW52YWxpZEF0ICAgICAgICAgPSBpbnZhbGlkQXQ7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5pc0FmdGVyICAgICAgICAgICA9IGlzQWZ0ZXI7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5pc0JlZm9yZSAgICAgICAgICA9IGlzQmVmb3JlO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8uaXNCZXR3ZWVuICAgICAgICAgPSBpc0JldHdlZW47XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5pc1NhbWUgICAgICAgICAgICA9IGlzU2FtZTtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmlzU2FtZU9yQWZ0ZXIgICAgID0gaXNTYW1lT3JBZnRlcjtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmlzU2FtZU9yQmVmb3JlICAgID0gaXNTYW1lT3JCZWZvcmU7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5pc1ZhbGlkICAgICAgICAgICA9IG1vbWVudF92YWxpZF9faXNWYWxpZDtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmxhbmcgICAgICAgICAgICAgID0gbGFuZztcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLmxvY2FsZSAgICAgICAgICAgID0gbG9jYWxlO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8ubG9jYWxlRGF0YSAgICAgICAgPSBsb2NhbGVEYXRhO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8ubWF4ICAgICAgICAgICAgICAgPSBwcm90b3R5cGVNYXg7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5taW4gICAgICAgICAgICAgICA9IHByb3RvdHlwZU1pbjtcbiAgICBtb21lbnRQcm90b3R5cGVfX3Byb3RvLnBhcnNpbmdGbGFncyAgICAgID0gcGFyc2luZ0ZsYWdzO1xuICAgIG1vbWVudFByb3RvdHlwZV9fcHJvdG8uc2V0ICAgICAgICAgICAgICAgPSBzdHJpbmdTZXQ7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5zdGFydE9mICAgICAgICAgICA9IHN0YXJ0T2Y7XG4gICAgbW9tZW50UHJvdG90eXBlX19wcm90by5zdWJ0cmFjdCAgICAgICAgICA9IGFkZF9zdWJ0cmFjdF9fc3VidHJhY3Q7XG4gICAgbW9tZW50U |
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment