├── .gitignore
├── shapes
├── line.js
├── ray.js
├── curve.js
├── beziercurve.js
├── rect.js
├── grid.js
├── poly.js
├── raysegment.js
├── circle.js
├── path.js
├── star.js
├── segment.js
├── heart.js
├── oval.js
├── arcSegment.js
├── arrow.js
├── spiral.js
├── curvesegment.js
├── text.js
├── beziersegment.js
├── gear.js
└── cube.js
├── package.json
├── LICENSE
├── index.html
├── model.js
├── renderList.js
├── shapeList.js
├── scheduler.js
├── main.js
├── libs
├── valueParser.js
├── color.js
└── colorParser.js
├── README.md
├── shape.js
└── dist
├── glc.min.js
└── glc.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | .DS_Store
--------------------------------------------------------------------------------
/shapes/line.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x0 = this.getNumber("x0", t, 0),
6 | y0 = this.getNumber("y0", t, 0),
7 | x1 = this.getNumber("x1", t, 100),
8 | y1 = this.getNumber("y1", t, 100);
9 |
10 | context.moveTo(x0, y0);
11 | context.lineTo(x1, y1);
12 |
13 | this.drawFillAndStroke(context, t, false, true);
14 | }
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/shapes/ray.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | angle = this.getNumber("angle", t, 0) * Math.PI / 180,
8 | length = this.getNumber("length", t, 100);
9 |
10 | context.translate(x, y);
11 | context.rotate(angle);
12 | context.moveTo(0, 0);
13 | context.lineTo(length, 0);
14 |
15 | this.drawFillAndStroke(context, t, false, true);
16 | }
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/shapes/curve.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x0 = this.getNumber("x0", t, 20),
6 | y0 = this.getNumber("y0", t, 10),
7 | x1 = this.getNumber("x1", t, 100),
8 | y1 = this.getNumber("y1", t, 200),
9 | x2 = this.getNumber("x2", t, 180),
10 | y2 = this.getNumber("y2", t, 10);
11 |
12 | context.moveTo(x0, y0);
13 | context.quadraticCurveTo(x1, y1, x2, y2);
14 |
15 | this.drawFillAndStroke(context, t, false, true);
16 | }
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/shapes/beziercurve.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x0 = this.getNumber("x0", t, 50),
6 | y0 = this.getNumber("y0", t, 10),
7 | x1 = this.getNumber("x1", t, 200),
8 | y1 = this.getNumber("y1", t, 100),
9 | x2 = this.getNumber("x2", t, 0),
10 | y2 = this.getNumber("y2", t, 100),
11 | x3 = this.getNumber("x3", t, 150),
12 | y3 = this.getNumber("y3", t, 10);
13 |
14 | context.moveTo(x0, y0);
15 | context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
16 |
17 | this.drawFillAndStroke(context, t, false, true);
18 | }
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/shapes/rect.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | w = this.getNumber("w", t, 100),
8 | h = this.getNumber("h", t, 100);
9 |
10 | context.translate(x, y);
11 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
12 | if(this.getBool("drawFromCenter", t, true)) {
13 | context.rect(-w * 0.5, -h * 0.5, w, h);
14 | }
15 | else {
16 | context.rect(0, 0, w, h);
17 | }
18 |
19 | this.drawFillAndStroke(context, t, true, false);
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/shapes/grid.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 0),
6 | y = this.getNumber("y", t, 0),
7 | w = this.getNumber("w", t, 100),
8 | h = this.getNumber("h", t, 100),
9 | gridSize = this.getNumber("gridSize", t, 20);
10 |
11 | for(var i = y; i <= y + h; i += gridSize) {
12 | context.moveTo(x, i);
13 | context.lineTo(x + w, i);
14 | }
15 | for(i = x; i <= x + w; i += gridSize) {
16 | context.moveTo(i, y);
17 | context.lineTo(i, y + h);
18 | }
19 |
20 | this.drawFillAndStroke(context, t, false, true);
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/shapes/poly.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | radius = this.getNumber("radius", t, 50),
8 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
9 | sides = this.getNumber("sides", t, 5);
10 |
11 | context.translate(x, y);
12 | context.rotate(rotation);
13 | context.moveTo(radius, 0);
14 | for(var i = 1; i < sides; i++) {
15 | var angle = Math.PI * 2 / sides * i;
16 | context.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
17 | }
18 | context.lineTo(radius, 0);
19 |
20 | this.drawFillAndStroke(context, t, true, false);
21 | }
22 | }
23 | };
24 |
25 |
--------------------------------------------------------------------------------
/shapes/raysegment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | angle = this.getNumber("angle", t, 0) * Math.PI / 180,
8 | length = this.getNumber("length", t, 100),
9 | segmentLength = this.getNumber("segmentLength", t, 50),
10 | start = -0.01,
11 | end = (length + segmentLength) * t;
12 |
13 | if(end > segmentLength) {
14 | start = end - segmentLength;
15 | }
16 | if(end > length) {
17 | end = length + 0.01;
18 | }
19 |
20 | context.translate(x, y);
21 | context.rotate(angle);
22 | context.moveTo(start, 0);
23 | context.lineTo(end, 0);
24 |
25 | this.drawFillAndStroke(context, t, false, true);
26 | }
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/shapes/circle.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | return {
3 | draw: function(context, t) {
4 | var x = this.getNumber("x", t, 100),
5 | y = this.getNumber("y", t, 100),
6 | radius = this.getNumber("radius", t, 50),
7 | startAngle = this.getNumber("startAngle", t, 0),
8 | endAngle = this.getNumber("endAngle", t, 360),
9 | drawFromCenter = this.getBool("drawFromCenter", t, false);
10 |
11 | context.translate(x, y);
12 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
13 | if(drawFromCenter) {
14 | context.moveTo(0, 0);
15 | }
16 | context.arc(0, 0, radius, startAngle * Math.PI / 180, endAngle * Math.PI / 180);
17 | if(drawFromCenter) {
18 | context.closePath();
19 | }
20 |
21 | this.drawFillAndStroke(context, t, true, false);
22 | }
23 | }
24 | };
--------------------------------------------------------------------------------
/shapes/path.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var path = this.getArray("path", t, []),
6 | startPercent = this.getNumber("startPercent", t, 0),
7 | endPercent = this.getNumber("endPercent", t, 1),
8 | startPoint = Math.floor(path.length / 2 * startPercent),
9 | endPoint = Math.floor(path.length / 2 * endPercent),
10 | startIndex = startPoint * 2,
11 | endIndex = endPoint * 2;
12 |
13 | if(startIndex > endIndex) {
14 | var temp = startIndex;
15 | startIndex = endIndex;
16 | endIndex = temp;
17 | }
18 |
19 | context.moveTo(path[startIndex], path[startIndex + 1]);
20 |
21 | for(var i = startIndex + 2; i < endIndex - 1; i += 2) {
22 | context.lineTo(path[i], path[i + 1]);
23 | }
24 |
25 | this.drawFillAndStroke(context, t, false, true); }
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/shapes/star.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | innerRadius = this.getNumber("innerRadius", t, 25),
8 | outerRadius = this.getNumber("outerRadius", t, 50),
9 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
10 | points = this.getNumber("points", t, 5);
11 |
12 | context.translate(x, y);
13 | context.rotate(rotation);
14 | context.moveTo(outerRadius, 0);
15 | for(var i = 1; i < points * 2; i++) {
16 | var angle = Math.PI * 2 / points / 2 * i,
17 | r = i % 2 ? innerRadius : outerRadius;
18 | context.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
19 | }
20 | context.lineTo(outerRadius, 0);
21 |
22 |
23 | this.drawFillAndStroke(context, t, true, false);
24 | }
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/shapes/segment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x0 = this.getNumber("x0", t, 0),
6 | y0 = this.getNumber("y0", t, 0),
7 | x1 = this.getNumber("x1", t, 100),
8 | y1 = this.getNumber("y1", t, 100),
9 | segmentLength = this.getNumber("segmentLength", t, 50),
10 | dx = x1 - x0,
11 | dy = y1 - y0,
12 | angle = Math.atan2(dy, dx),
13 | dist = Math.sqrt(dx * dx + dy * dy),
14 | start = -0.01,
15 | end = (dist + segmentLength) * t;
16 |
17 | if(end > segmentLength) {
18 | start = end - segmentLength;
19 | }
20 | if(end > dist) {
21 | end = dist + 0.01;
22 | }
23 |
24 | context.translate(x0, y0);
25 | context.rotate(angle);
26 | context.moveTo(start, 0);
27 | context.lineTo(end, 0);
28 |
29 | this.drawFillAndStroke(context, t, false, true);
30 | }
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/shapes/heart.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return {
3 | draw: function(context, t) {
4 | var x = this.getNumber("x", t, 100),
5 | y = this.getNumber("y", t, 100),
6 | w = this.getNumber("w", t, 50),
7 | h = this.getNumber("h", t, 50);
8 |
9 | var x0 = 0,
10 | y0 = -.25,
11 | x1 = .2,
12 | y1 = -.8,
13 | x2 = 1.1,
14 | y2 = -.2,
15 | x3 = 0,
16 | y3 = .5;
17 |
18 | context.save();
19 | context.translate(x, y);
20 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
21 | context.save();
22 | context.scale(w, h);
23 | context.moveTo(x0, y0);
24 | context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
25 | context.bezierCurveTo(-x2, y2, -x1, y1, -x0, y0);
26 | context.restore();
27 | this.drawFillAndStroke(context, t, true, false);
28 | context.restore();
29 | }
30 | }
31 | };
--------------------------------------------------------------------------------
/shapes/oval.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | rx = this.getNumber("rx", t, 50),
8 | ry = this.getNumber("ry", t, 50),
9 | startAngle = this.getNumber("startAngle", t, 0),
10 | endAngle = this.getNumber("endAngle", t, 360),
11 | drawFromCenter = this.getBool("drawFromCenter", t, false);
12 |
13 | context.translate(x, y);
14 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
15 | context.save();
16 | context.scale(rx / 100, ry / 100);
17 | if(drawFromCenter) {
18 | context.moveTo(0, 0);
19 | }
20 | context.arc(0, 0, 100, startAngle * Math.PI / 180, endAngle * Math.PI / 180);
21 | if(drawFromCenter) {
22 | context.closePath();
23 | }
24 | context.restore();
25 |
26 | this.drawFillAndStroke(context, t, true, false);
27 | }
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gifloopcoder",
3 | "version": "0.0.3",
4 | "description": "Animation Library for creating GIF-like loops",
5 | "main": "main.js",
6 | "unpkg": "dist/glc.min.js",
7 | "dependencies": {},
8 | "devDependencies": {
9 | "browserify": "^12.0.1",
10 | "uglify-js": "^2.6.1",
11 | "watchify": "^3.6.1"
12 | },
13 | "scripts": {
14 | "build-debug": "browserify main.js -d --s GLC > dist/glc.js",
15 | "build-min": "browserify main.js --s GLC | uglifyjs -c > dist/glc.min.js",
16 | "build": "npm run build-debug && npm run build-min",
17 | "watch": "watchify main.js -d --s GLC -o dist/glc.js -v"
18 | },
19 | "keywords": [
20 | "glc",
21 | "animation",
22 | "gifloopcoder",
23 | "gif"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/msurguy/gifloopcoder.git"
28 | },
29 | "author": "Keith Peters, Maks Surguy",
30 | "license": "MIT"
31 | }
32 |
--------------------------------------------------------------------------------
/shapes/arcSegment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | return {
3 | draw: function(context, t) {
4 | var x = this.getNumber("x", t, 100),
5 | y = this.getNumber("y", t, 100),
6 | radius = this.getNumber("radius", t, 50),
7 | startAngle = this.getNumber("startAngle", t, 0),
8 | endAngle = this.getNumber("endAngle", t, 360);
9 |
10 | if(startAngle > endAngle) {
11 | var temp = startAngle;
12 | startAngle = endAngle;
13 | endAngle = temp;
14 | }
15 | var arc = this.getNumber("arc", t, 20),
16 | start = startAngle - 1,
17 | end = startAngle + t * (endAngle - startAngle + arc);
18 |
19 | if(end > startAngle + arc) {
20 | start = end - arc;
21 | }
22 | if(end > endAngle) {
23 | end = endAngle + 1;
24 | }
25 |
26 | context.translate(x, y);
27 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
28 | context.arc(0, 0, radius, start * Math.PI / 180, end * Math.PI / 180);
29 |
30 | this.drawFillAndStroke(context, t, false, true);
31 | }
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/shapes/arrow.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | return {
3 | draw: function(context, t) {
4 | var x = this.getNumber("x", t, 100),
5 | y = this.getNumber("y", t, 100),
6 | w = this.getNumber("w", t, 100),
7 | h = this.getNumber("h", t, 100),
8 | pointPercent = this.getNumber("pointPercent", t, 0.5),
9 | shaftPercent = this.getNumber("shaftPercent", t, 0.5);
10 |
11 | context.translate(x, y);
12 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
13 |
14 | // context.translate(-w / 2, 0);
15 |
16 | context.moveTo(-w / 2, -h * shaftPercent * 0.5);
17 | context.lineTo(w / 2 - w * pointPercent, -h * shaftPercent * 0.5);
18 | context.lineTo(w / 2 - w * pointPercent, -h * 0.5);
19 | context.lineTo(w / 2, 0);
20 | context.lineTo(w / 2 - w * pointPercent, h * 0.5);
21 | context.lineTo(w / 2 - w * pointPercent, h * shaftPercent * 0.5);
22 | context.lineTo(-w / 2, h * shaftPercent * 0.5);
23 | context.lineTo(-w / 2, -h * shaftPercent * 0.5);
24 |
25 | this.drawFillAndStroke(context, t, true, false);
26 | }
27 | }
28 | };
--------------------------------------------------------------------------------
/shapes/spiral.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, "100"),
6 | y = this.getNumber("y", t, "100"),
7 | innerRadius = this.getNumber("innerRadius", t, 10),
8 | outerRadius = this.getNumber("outerRadius", t, 90),
9 | turns = this.getNumber("turns", t, 6),
10 | res = this.getNumber("res", t, 1) * Math.PI / 180,
11 | fullAngle = Math.PI * 2 * turns;
12 |
13 | context.translate(x, y);
14 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
15 |
16 |
17 | if(fullAngle > 0) {
18 | for(var a = 0; a < fullAngle; a += res) {
19 | var r = innerRadius + (outerRadius - innerRadius) * a / fullAngle;
20 | context.lineTo(Math.cos(a) * r, Math.sin(a) * r);
21 | }
22 | }
23 | else {
24 | for(var a = 0; a > fullAngle; a -= res) {
25 | var r = innerRadius + (outerRadius - innerRadius) * a / fullAngle;
26 | context.lineTo(Math.cos(a) * r, Math.sin(a) * r);
27 | }
28 | }
29 | this.drawFillAndStroke(context, t, false, true);
30 | }
31 | };
32 |
33 | };
--------------------------------------------------------------------------------
/shapes/curvesegment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | function quadratic(t, v0, v1, v2) {
4 | return (1 - t) * (1 - t) * v0 + 2 * (1 - t) * t * v1 + t * t * v2;
5 | }
6 |
7 | return {
8 | draw: function(context, t) {
9 | var x0 = this.getNumber("x0", t, 20),
10 | y0 = this.getNumber("y0", t, 20),
11 | x1 = this.getNumber("x1", t, 100),
12 | y1 = this.getNumber("y1", t, 200),
13 | x2 = this.getNumber("x2", t, 180),
14 | y2 = this.getNumber("y2", t, 20),
15 | percent = this.getNumber("percent", t, 0.1),
16 | t1 = t * (1 + percent),
17 | t0 = t1 - percent,
18 | res = 0.01,
19 | x,
20 | y;
21 |
22 | t1 = Math.min(t1, 1);
23 | t0 = Math.max(t0, 0);
24 |
25 | for(var i = t0; i < t1; i += res) {
26 | x = quadratic(i, x0, x1, x2);
27 | y = quadratic(i, y0, y1, y2);
28 | if(i === t0) {
29 | context.moveTo(x, y);
30 | }
31 | else {
32 | context.lineTo(x, y);
33 | }
34 | }
35 | x = quadratic(t1, x0, x1, x2);
36 | y = quadratic(t1, y0, y1, y2);
37 | context.lineTo(x, y);
38 |
39 | this.drawFillAndStroke(context, t, false, true); }
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Maksim Surguy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Gif Loop Coder Demo
6 |
7 |
8 |
9 |
10 |
11 |
46 |
47 |
--------------------------------------------------------------------------------
/shapes/text.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | text = this.getString("text", t, "hello"),
8 | fontSize = this.getNumber("fontSize", t, 20),
9 | fontWeight = this.getString("fontWeight", t, "normal");
10 | fontFamily = this.getString("fontFamily", t, "sans-serif");
11 | fontStyle = this.getString("fontStyle", t, "normal");
12 |
13 | context.font = fontWeight + " " + fontStyle + " " + fontSize + "px " + fontFamily;
14 | var width = context.measureText(text).width;
15 | context.translate(x, y);
16 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
17 | var shadowsSet = false;
18 | context.save();
19 | if(this.getBool("fill", t, true)) {
20 | this.setShadowParams(context, t);
21 | shadowsSet = true;
22 | context.fillText(text, -width / 2, fontSize * 0.4);
23 | }
24 | context.restore();
25 | if(this.getBool("stroke", t, false)) {
26 | if(!shadowsSet) {
27 | this.setShadowParams(context, t);
28 | }
29 | context.strokeText(text, -width / 2, fontSize * 0.4);
30 | }
31 | }
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/shapes/beziersegment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | function bezier(t, v0, v1, v2, v3) {
4 | return (1 - t) * (1 - t) * (1 - t) * v0 + 3 * (1 - t) * (1 - t) * t * v1 + 3 * (1 - t) * t * t * v2 + t * t * t * v3;
5 | }
6 |
7 | return {
8 | draw: function(context, t) {
9 | var x0 = this.getNumber("x0", t, 50),
10 | y0 = this.getNumber("y0", t, 10),
11 | x1 = this.getNumber("x1", t, 200),
12 | y1 = this.getNumber("y1", t, 100),
13 | x2 = this.getNumber("x2", t, 0),
14 | y2 = this.getNumber("y2", t, 100),
15 | x3 = this.getNumber("x3", t, 150),
16 | y3 = this.getNumber("y3", t, 10),
17 | percent = this.getNumber("percent", t, 0.1),
18 | t1 = t * (1 + percent),
19 | t0 = t1 - percent,
20 | res = 0.01,
21 | x,
22 | y;
23 |
24 | t1 = Math.min(t1, 1.001);
25 | t0 = Math.max(t0, -0.001);
26 |
27 | for(var i = t0; i < t1; i += res) {
28 | x = bezier(i, x0, x1, x2, x3);
29 | y = bezier(i, y0, y1, y2, y3);
30 | if(i === t0) {
31 | context.moveTo(x, y);
32 | }
33 | else {
34 | context.lineTo(x, y);
35 | }
36 | }
37 | x = bezier(t1, x0, x1, x2, x3);
38 | y = bezier(t1, y0, y1, y2, y3);
39 | context.lineTo(x, y);
40 |
41 | this.drawFillAndStroke(context, t, false, true);
42 | }
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/shapes/gear.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | radius = this.getNumber("radius", t, 50),
8 | toothHeight = this.getNumber("toothHeight", t, 10),
9 | hub = this.getNumber("hub", t, 10),
10 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
11 | teeth = this.getNumber("teeth", t, 10),
12 | toothAngle = this.getNumber("toothAngle", t, 0.3),
13 | face = 0.5 - toothAngle / 2,
14 | side = 0.5 - face,
15 | innerRadius = radius - toothHeight;
16 |
17 | context.translate(x, y);
18 | context.rotate(rotation);
19 | context.save();
20 | context.moveTo(radius, 0);
21 | var angle = Math.PI * 2 / teeth;
22 |
23 | for(var i = 0; i < teeth; i++) {
24 | context.rotate(angle * face);
25 | context.lineTo(radius, 0);
26 | context.rotate(angle * side);
27 | context.lineTo(innerRadius, 0);
28 | context.rotate(angle * face);
29 | context.lineTo(innerRadius, 0);
30 | context.rotate(angle * side);
31 | context.lineTo(radius, 0);
32 | }
33 | context.lineTo(radius, 0);
34 | context.restore();
35 |
36 | context.moveTo(hub, 0);
37 | context.arc(0, 0, hub, 0, Math.PI * 2, true);
38 |
39 | this.drawFillAndStroke(context, t, true, false);
40 | }
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/model.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple model for the Canvas renderer
3 | * @type {{init, loop, playOnce, stop, isRunning, setDuration, getDuration, setFPS, getFPS}|*}
4 | */
5 | var Scheduler = require('./scheduler');
6 |
7 | module.exports = function(){
8 | var scheduler = new Scheduler();
9 |
10 | var styles = {
11 | backgroundColor: "#ffffff",
12 | lineWidth: 5,
13 | strokeStyle: "#000000",
14 | fillStyle: "#000000",
15 | lineCap: "round",
16 | lineJoin: "miter",
17 | lineDash: [],
18 | miterLimit: 10,
19 | shadowColor: null,
20 | shadowOffsetX: 0,
21 | shadowOffsetY: 0,
22 | shadowBlur: 0,
23 | globalAlpha: 1,
24 | translationX: 0,
25 | translationY: 0,
26 | shake: 0,
27 | blendMode: "source-over"
28 | };
29 |
30 | return {
31 | interpolation: {
32 | mode: "bounce",
33 | easing: true
34 | },
35 | maxColors: 256,
36 | w: 400,
37 | h: 400,
38 | capture: false,
39 | styles: styles,
40 | scheduler : scheduler,
41 | playOnce : function() {
42 | return scheduler.playOnce();
43 | },
44 | loop : function() {
45 | return scheduler.loop();
46 | },
47 | getDuration: function() {
48 | return scheduler.getDuration();
49 | },
50 | setDuration: function(value) {
51 | scheduler.setDuration(value);
52 | },
53 | getFPS: function() {
54 | return scheduler.getFPS();
55 | },
56 | setFPS: function(value) {
57 | scheduler.setFPS(value);
58 | },
59 | getIsRunning: function() {
60 | return scheduler.isRunning();
61 | }
62 | }
63 | };
--------------------------------------------------------------------------------
/renderList.js:
--------------------------------------------------------------------------------
1 | var Shape = require('./shape');
2 |
3 | module.exports = function(){
4 | var shape = new Shape();
5 |
6 | var canvas = null,
7 | context = null,
8 | width = 0,
9 | height = 0,
10 | list = [],
11 | styles = null;
12 |
13 | function init(w, h, stylesValue, interpolation) {
14 | canvas = document.createElement("canvas");
15 | width = canvas.width = w;
16 | height = canvas.height = h;
17 | context = canvas.getContext("2d");
18 | styles = stylesValue;
19 | shape.styles = styles;
20 | shape.interpolation = interpolation;
21 | }
22 |
23 | function size(w, h) {
24 | width = canvas.width = w;
25 | height = canvas.height = h;
26 | }
27 |
28 | function addShape(newShape, props) {
29 | var item = shape.create(newShape, props);
30 | list.push(item);
31 | render(0);
32 | }
33 |
34 | function clear() {
35 | list.length = 0;
36 | }
37 |
38 | function render(t) {
39 | if(styles.backgroundColor === "transparent") {
40 | context.clearRect(0, 0, width, height);
41 | }
42 | else {
43 | context.fillStyle = styles.backgroundColor;
44 | context.fillRect(0, 0, width, height);
45 | }
46 | for(var i = 0; i < list.length; i++) {
47 | list[i].render(context, t);
48 | }
49 | }
50 |
51 | function getCanvas() {
52 | return canvas;
53 | }
54 |
55 | function getContext() {
56 | return context;
57 | }
58 |
59 | return {
60 | init: init,
61 | size: size,
62 | getCanvas: getCanvas,
63 | getContext: getContext,
64 | addShape: addShape,
65 | clear: clear,
66 | render: render
67 | };
68 | };
--------------------------------------------------------------------------------
/shapeList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Include all default shapes
3 | * @type {{draw}|*}
4 | */
5 | var circle = require('./shapes/circle')();
6 | var heart = require('./shapes/heart')();
7 | var arrow = require('./shapes/arrow')();
8 | var arcSegment = require('./shapes/arcSegment')();
9 | var bezierCurve = require('./shapes/bezierCurve')();
10 | var bezierSegment = require('./shapes/bezierSegment')();
11 | var cube = require('./shapes/cube')();
12 | var curve = require('./shapes/curve')();
13 | var curveSegment = require('./shapes/curveSegment')();
14 | var gear = require('./shapes/gear')();
15 | var line = require('./shapes/line')();
16 | var oval = require('./shapes/oval')();
17 | var path = require('./shapes/path')();
18 | var poly = require('./shapes/poly')();
19 | var raySegment = require('./shapes/raySegment')();
20 | var rect = require('./shapes/rect')();
21 | var segment = require('./shapes/segment')();
22 | var spiral = require('./shapes/spiral')();
23 | var star = require('./shapes/star')();
24 | var text = require('./shapes/text')();
25 |
26 | module.exports = {
27 | circle : circle,
28 | arrow : arrow,
29 | arcSegment: arcSegment,
30 | bezierCurve : bezierCurve,
31 | bezierSegment : bezierSegment,
32 | cube : cube,
33 | curve : curve,
34 | curveSegment : curveSegment,
35 | gear : gear,
36 | line : line,
37 | oval : oval,
38 | path: path,
39 | poly : poly,
40 | raySegment : raySegment,
41 | rect : rect,
42 | segment : segment,
43 | spiral : spiral,
44 | star : star,
45 | text : text,
46 | heart : heart
47 | };
--------------------------------------------------------------------------------
/scheduler.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | var t = 0,
3 | duration = 2,
4 | fps = 30,
5 | running = false,
6 | looping = false,
7 | renderCallback = null,
8 | completeCallback = null;
9 |
10 | function init(onRender, onComplete) {
11 | renderCallback = onRender;
12 | completeCallback = onComplete;
13 | }
14 |
15 | function render() {
16 | if(running) {
17 | if(renderCallback) {
18 | renderCallback(t);
19 | }
20 | advance();
21 | setTimeout(onTimeout, 1000 / fps);
22 | }
23 | else if(completeCallback) {
24 | completeCallback();
25 | }
26 | }
27 |
28 | function onTimeout() {
29 | requestAnimationFrame(render);
30 | }
31 |
32 | function advance() {
33 | var numFrames = duration * fps,
34 | speed = 1 / numFrames;
35 | t += speed;
36 | if(Math.round(t * 10000) / 10000 >= 1) {
37 | if(looping) {
38 | t -= 1;
39 | }
40 | else {
41 | t = 0;
42 | stop();
43 | }
44 | }
45 | }
46 |
47 | function loop() {
48 | if(!running) {
49 | t = 0;
50 | looping = true;
51 | running = true;
52 | render();
53 | }
54 | }
55 |
56 | function stop() {
57 | running = false;
58 | looping = false;
59 | t = 0;
60 | }
61 |
62 | function playOnce() {
63 | if(!running) {
64 | t = 0;
65 | looping = false;
66 | running = true;
67 | render();
68 | }
69 | }
70 |
71 | function isRunning() {
72 | return running;
73 | }
74 |
75 | function setDuration(value) {
76 | duration = value;
77 | return duration;
78 | }
79 |
80 | function getDuration() {
81 | return duration;
82 | }
83 |
84 | function setFPS(value) {
85 | fps = value;
86 | return fps;
87 | }
88 |
89 | function getFPS() {
90 | return fps;
91 | }
92 |
93 | return {
94 | init: init,
95 | loop: loop,
96 | playOnce: playOnce,
97 | stop: stop,
98 | isRunning: isRunning,
99 | setDuration: setDuration,
100 | getDuration: getDuration,
101 | setFPS: setFPS,
102 | getFPS: getFPS
103 | };
104 | };
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | var Model = require('./model');
2 | var RenderList = require('./renderList');
3 |
4 | var colorLib = require('./libs/color')();
5 | var shapeList = require('./shapeList');
6 |
7 | module.exports = function(canvasWrapper, renderCallback, completeCallback){
8 | var model = new Model();
9 | var renderList = new RenderList();
10 |
11 | function onRender(t) {
12 | if (typeof renderCallback === 'function'){
13 | renderCallback();
14 | }
15 | renderList.render(t);
16 | }
17 |
18 | function onComplete() {
19 | if (typeof completeCallback === 'function'){
20 | completeCallback();
21 | }
22 | }
23 |
24 | // Initialize render list
25 | renderList.init(model.w, model.h, model.styles, model.interpolation);
26 |
27 | // Add all shapes from the shape list
28 | for (var key in shapeList) {
29 | if (shapeList.hasOwnProperty(key)) {
30 | var shapeName = key;
31 | renderList['add' + shapeName[0].toUpperCase() + shapeName.slice(1)] = (function(shapeName, shapeList){
32 | return function(props){
33 | renderList.addShape(shapeList[shapeName], props);
34 | }
35 | })(shapeName, shapeList);
36 | }
37 | }
38 |
39 | // Initialize scheduler
40 | model.scheduler.init(onRender, onComplete);
41 | var canvasEl = canvasWrapper.appendChild(renderList.getCanvas());
42 |
43 | return {
44 | w: model.w,
45 | h: model.h,
46 | model : model,
47 | renderList: renderList,
48 | size: function(width, height) {
49 | this.w = model.w = width;
50 | this.h = model.h = height;
51 | renderList.size(model.w, model.h);
52 | },
53 | styles : model.styles,
54 | playOnce: function(){
55 | return model.playOnce();
56 | },
57 | loop: function() {
58 | return model.loop();
59 | },
60 | getDuration : function(){
61 | return model.getDuration();
62 | },
63 | setFPS: function(value){
64 | model.setFPS(value);
65 | },
66 | setDuration: function(value) {
67 | model.setDuration(value);
68 | },
69 | setMode: function(value) {
70 | model.interpolation.mode = value;
71 | },
72 | setEasing: function (value) {
73 | model.interpolation.easing = value;
74 | },
75 | setMaxColors: function(value) {
76 | model.maxColors = value;
77 | },
78 | color: colorLib,
79 | canvasEl : canvasEl
80 | };
81 | };
--------------------------------------------------------------------------------
/libs/valueParser.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return {
3 |
4 | getNumber: function(prop, t, def) {
5 | if(typeof(prop) === "number") {
6 | return prop;
7 | }
8 | else if(typeof(prop) === "function") {
9 | return prop(t);
10 | }
11 | else if(prop && prop.length === 2) {
12 | var start = prop[0],
13 | end = prop[1];
14 | return start + (end - start) * t;
15 | }
16 | else if(prop && prop.length) {
17 | return prop[Math.round(t * (prop.length - 1))];
18 | }
19 | return def;
20 | },
21 |
22 |
23 | getString: function(prop, t, def) {
24 | if(prop === undefined) {
25 | return def;
26 | }
27 | else if(typeof(prop) === "string") {
28 | return prop;
29 | }
30 | else if(typeof(prop) === "function") {
31 | return prop(t);
32 | }
33 | else if(prop && prop.length) {
34 | return prop[Math.round(t * (prop.length - 1))];
35 | }
36 | return prop;
37 | },
38 |
39 | getBool: function(prop, t, def) {
40 | if(prop === undefined) {
41 | return def;
42 | }
43 | else if(typeof(prop) === "function") {
44 | return prop(t);
45 | }
46 | else if(prop && prop.length) {
47 | return prop[Math.round(t * (prop.length - 1))];
48 | }
49 | return prop;
50 | },
51 |
52 | getArray: function(prop, t, def) {
53 | // string will have length, but is useless
54 | if(typeof(prop) === "string") {
55 | return def;
56 | }
57 | else if(typeof(prop) === "function") {
58 | return prop(t);
59 | }
60 | else if(prop && (prop.length == 2) && prop[0].length && prop[1].length) {
61 | // we seem to have an array of arrays
62 | var arr0 = prop[0],
63 | arr1 = prop[1],
64 | len = Math.min(arr0.length, arr1.length),
65 | result = [];
66 |
67 | for(var i = 0; i < len; i++) {
68 | var v0 = arr0[i],
69 | v1 = arr1[i];
70 | result.push(v0 + (v1 - v0) * t);
71 | }
72 | return result;
73 |
74 | }
75 | else if(prop && prop.length > 1) {
76 | return prop;
77 | }
78 | return def;
79 | },
80 |
81 | getObject: function(prop, t, def) {
82 | if(prop === undefined) {
83 | return def;
84 | }
85 | else if(typeof(prop) === "function") {
86 | return prop(t);
87 | }
88 | else if(prop && prop.length) {
89 | return prop[Math.round(t * (prop.length - 1))];
90 | }
91 | return prop;
92 | }
93 |
94 | }
95 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gif Loop Coder library
2 |
3 | GLC lirary can be used to create animations like these:
4 |
5 | 
6 |
7 | 
8 |
9 | 
10 |
11 | ## Installation
12 | This is a standalone version of Keith Peters' [GifLoopCoder](http://www.gifloopcoder.com/) library to be used on your websites. It does not include the GIF export functionality but rather allows you to drop the library on [Codepen](http://codepen.io/msurguy/pen/WrNxdN), JSFiddle, or any other website to quickly prototype your animations that can be then exported from original GLC tool.
13 |
14 | You can either download the library with NPM or use a free CDN:
15 |
16 | ### Using a CDN:
17 |
18 | ```html
19 |
20 | ```
21 |
22 | ### Installation with NPM:
23 |
24 | `npm install -s gifloopcoder`
25 |
26 | or you can download this repo and get the `glc.min.js` file in "/dist" folder
27 |
28 | ## Usage
29 |
30 | When you have the GLC library on the page you can use it as follows: create an element that will contain the animation canvas, for example a `` and place it in your HTML page. Then initialize a new GLC object, passing that canvas container that you created earlier. That's it. You can now use GLC as you would use `onGLC` function in Keith Peters' GLC tool:
31 |
32 | ```html
33 |
34 | ...
35 |
36 |
71 | ```
72 |
73 | ## License
74 |
75 | MIT
76 |
--------------------------------------------------------------------------------
/shapes/cube.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 |
3 | return {
4 | draw: function(context, t) {
5 | var x = this.getNumber("x", t, 100),
6 | y = this.getNumber("y", t, 100),
7 | z = this.getNumber("z", t, 0),
8 | size = this.getNumber("size", t, 100),
9 | rotationX = this.getNumber("rotationX", t, 0) * Math.PI / 180,
10 | rotationY = this.getNumber("rotationY", t, 0) * Math.PI / 180,
11 | rotationZ = this.getNumber("rotationZ", t, 0) * Math.PI / 180;
12 |
13 | var points = makePoints();
14 | scale(points, size / 2);
15 | rotateX(points, rotationX);
16 | rotateY(points, rotationY);
17 | rotateZ(points, rotationZ);
18 | project(points, z);
19 |
20 | context.lineJoin = this.getString("lineJoin", t, "round");
21 | context.lineWidth = this.getNumber("lineWidth", t, 1);
22 |
23 | context.translate(x, y);
24 |
25 | context.moveTo(points[0].sx, points[0].sy);
26 | context.lineTo(points[1].sx, points[1].sy);
27 | context.lineTo(points[2].sx, points[2].sy);
28 | context.lineTo(points[3].sx, points[3].sy);
29 | context.lineTo(points[0].sx, points[0].sy);
30 |
31 | context.moveTo(points[4].sx, points[4].sy);
32 | context.lineTo(points[5].sx, points[5].sy);
33 | context.lineTo(points[6].sx, points[6].sy);
34 | context.lineTo(points[7].sx, points[7].sy);
35 | context.lineTo(points[4].sx, points[4].sy);
36 |
37 | context.moveTo(points[0].sx, points[0].sy);
38 | context.lineTo(points[4].sx, points[4].sy);
39 |
40 | context.moveTo(points[1].sx, points[1].sy);
41 | context.lineTo(points[5].sx, points[5].sy);
42 |
43 | context.moveTo(points[2].sx, points[2].sy);
44 | context.lineTo(points[6].sx, points[6].sy);
45 |
46 | context.moveTo(points[3].sx, points[3].sy);
47 | context.lineTo(points[7].sx, points[7].sy);
48 |
49 | this.setShadowParams(context, t);
50 | context.stroke();
51 | }
52 | }
53 |
54 | function scale(points, size) {
55 | for(var i = 0; i < points.length; i++) {
56 | var p = points[i];
57 | p.x *= size;
58 | p.y *= size;
59 | p.z *= size;
60 | }
61 | }
62 |
63 | function rotateX(points, angle) {
64 | var cos = Math.cos(angle),
65 | sin = Math.sin(angle);
66 | for(var i = 0; i < points.length; i++) {
67 | var p = points[i],
68 | y = p.y * cos - p.z * sin,
69 | z = p.z * cos + p.y * sin;
70 | p.y = y;
71 | p.z = z;
72 | }
73 | }
74 |
75 | function rotateY(points, angle) {
76 | var cos = Math.cos(angle),
77 | sin = Math.sin(angle);
78 | for(var i = 0; i < points.length; i++) {
79 | var p = points[i],
80 | x = p.x * cos - p.z * sin,
81 | z = p.z * cos + p.x * sin;
82 | p.x = x;
83 | p.z = z;
84 | }
85 | }
86 |
87 | function rotateZ(points, angle) {
88 | var cos = Math.cos(angle),
89 | sin = Math.sin(angle);
90 | for(var i = 0; i < points.length; i++) {
91 | var p = points[i],
92 | x = p.x * cos - p.y * sin,
93 | y = p.y * cos + p.x * sin;
94 | p.x = x;
95 | p.y = y;
96 | }
97 | }
98 |
99 | function project(points, z) {
100 | var fl = 300;
101 | for(var i = 0; i < points.length; i++) {
102 | var p = points[i],
103 | scale = fl / (fl + p.z + z);
104 | p.sx = p.x * scale;
105 | p.sy = p.y * scale;
106 | }
107 | }
108 |
109 | function makePoints() {
110 | return [
111 | {
112 | x: -1,
113 | y: -1,
114 | z: -1
115 | },
116 | {
117 | x: 1,
118 | y: -1,
119 | z: -1
120 | },
121 | {
122 | x: 1,
123 | y: 1,
124 | z: -1
125 | },
126 | {
127 | x: -1,
128 | y: 1,
129 | z: -1
130 | },
131 | {
132 | x: -1,
133 | y: -1,
134 | z: 1
135 | },
136 | {
137 | x: 1,
138 | y: -1,
139 | z: 1
140 | },
141 | {
142 | x: 1,
143 | y: 1,
144 | z: 1
145 | },
146 | {
147 | x: -1,
148 | y: 1,
149 | z: 1
150 | }
151 | ];
152 | }
153 | };
154 |
--------------------------------------------------------------------------------
/libs/color.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 |
3 | function rgba(r, g, b, a) {
4 | var clr = Object.create(color);
5 | clr.setRGBA(r, g, b, a);
6 | return clr.toString();
7 | }
8 |
9 | function rgb(r, g, b) {
10 | return rgba(r, g, b, 1);
11 | }
12 |
13 | function randomRGB(min, max) {
14 | min = min || 0;
15 | max = max || 256;
16 | return rgb(
17 | Math.floor(min + Math.random() * (max - min)),
18 | Math.floor(min + Math.random() * (max - min)),
19 | Math.floor(min + Math.random() * (max - min))
20 | );
21 | }
22 |
23 | function randomGray(min, max) {
24 | min = min || 0;
25 | max = max || 256;
26 | return gray(Math.floor(min + Math.random() * (max - min)));
27 | }
28 |
29 | function gray(shade) {
30 | return rgb(shade, shade, shade);
31 | }
32 |
33 | function num(num) {
34 | var red = num >> 16,
35 | green = num >> 8 & 0xff,
36 | blue = num & 0xff;
37 | return rgb(red, green, blue);
38 | }
39 |
40 | function randomHSV(minH, maxH, minS, maxS, minV, maxV) {
41 | var h = minH + Math.random() * (maxH - minH),
42 | s = minS + Math.random() * (maxS - minS),
43 | v = minV + Math.random() * (maxV - minV);
44 | return hsv(h, s, v);
45 | }
46 |
47 | function hsva(h, s, v, a) {
48 | var r, g, b,
49 | i = Math.floor(h / 60),
50 | f = h / 60 - i,
51 | p = v * (1 - s),
52 | q = v * (1 - f * s),
53 | t = v * (1 - (1 - f) * s);
54 | switch (i % 6) {
55 | case 0: r = v, g = t, b = p; break;
56 | case 1: r = q, g = v, b = p; break;
57 | case 2: r = p, g = v, b = t; break;
58 | case 3: r = p, g = q, b = v; break;
59 | case 4: r = t, g = p, b = v; break;
60 | case 5: r = v, g = p, b = q; break;
61 | }
62 | return rgba(
63 | Math.floor(r * 255),
64 | Math.floor(g * 255),
65 | Math.floor(b * 255),
66 | a
67 | );
68 | }
69 |
70 | function hsv(h, s, v) {
71 | return hsva(h, s, v, 1);
72 | }
73 |
74 | function animHSVA(startH, endH, startS, endS, startV, endV, startA, endA) {
75 | return function(t) {
76 | var h = startH + t * (endH - startH),
77 | s = startS + t * (endS - startS),
78 | v = startV + t * (endV - startV),
79 | a = startA + t * (endA - startA);
80 | return hsva(h, s, v, a);
81 | }
82 | }
83 |
84 | function animHSV(startH, endH, startS, endS, startV, endV) {
85 | return animHSVA(startH, endH, startS, endS, startV, endV, 1, 1);
86 | }
87 |
88 |
89 |
90 |
91 |
92 | /////////////////////
93 | // gradients
94 | /////////////////////
95 | function createLinearGradient(x0, y0, x1, y1) {
96 | var g = {
97 | type: "linearGradient",
98 | x0: x0,
99 | y0: y0,
100 | x1: x1,
101 | y1: y1,
102 | colorStops: [],
103 | addColorStop: function(position, color) {
104 | this.colorStops.push({
105 | position: position,
106 | color: color
107 | });
108 | }
109 | };
110 | return g;
111 | }
112 |
113 | function createRadialGradient(x0, y0, r0, x1, y1, r1) {
114 | var g = {
115 | type: "radialGradient",
116 | x0: x0,
117 | y0: y0,
118 | r0: r0,
119 | x1: x1,
120 | y1: y1,
121 | r1: r1,
122 | colorStops: [],
123 | addColorStop: function(position, color) {
124 | this.colorStops.push({
125 | position: position,
126 | color: color
127 | });
128 | }
129 | };
130 | return g;
131 | }
132 |
133 | var color = {
134 | r: 255,
135 | g: 255,
136 | b: 255,
137 | a: 1,
138 |
139 | setRGBA: function(r, g, b, a) {
140 | this.r = r;
141 | this.g = g;
142 | this.b = b;
143 | this.a = a;
144 | return this;
145 | },
146 |
147 | toString: function() {
148 | return "rgba(" + Math.floor(this.r) + "," + Math.floor(this.g) + "," + Math.floor(this.b) + "," + this.a + ")";
149 | }
150 | };
151 |
152 | return {
153 | rgb: rgb,
154 | rgba: rgba,
155 | randomRGB: randomRGB,
156 | randomGray: randomGray,
157 | gray: gray,
158 | num: num,
159 | hsv: hsv,
160 | hsva: hsva,
161 | animHSV: animHSV,
162 | animHSVA: animHSVA,
163 | randomHSV: randomHSV,
164 | createLinearGradient: createLinearGradient,
165 | createRadialGradient: createRadialGradient
166 | };
167 | };
--------------------------------------------------------------------------------
/shape.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Shape prototype object
3 | * @type {{getNumber, getString, getBool, getArray, getObject}|*}
4 | */
5 | var valueParser = require('./libs/valueParser')();
6 | var colorParser = require('./libs/colorParser')();
7 |
8 | module.exports = function() {
9 |
10 | return {
11 | styles: null,
12 | interpolation: null,
13 |
14 | create: function (type, props) {
15 | var obj = Object.create(this);
16 | obj.init(type, props || {});
17 | return obj;
18 | },
19 |
20 | init: function (type, props) {
21 | this.props = props;
22 | for(var prop in props) {
23 | var p = props[prop];
24 | if(typeof p === "function") {
25 | props[prop] = p.bind(props);
26 | }
27 | }
28 | this.draw = type.draw;
29 | },
30 |
31 | render: function (context, time) {
32 | var t = this.interpolate(time);
33 |
34 | this.startDraw(context, t);
35 | this.draw(context, t);
36 | this.endDraw(context, t);
37 | },
38 |
39 | interpolate: function (t) {
40 | t *= this.props.speedMult || 1;
41 | t += this.props.phase || 0;
42 |
43 | switch (this.interpolation.mode) {
44 | case "bounce":
45 | if (this.interpolation.easing) {
46 | var a = t * Math.PI * 2;
47 | return 0.5 - Math.cos(a) * 0.5;
48 | }
49 | else {
50 | t = t % 1;
51 | return t < 0.5 ? t * 2 : t = (1 - t) * 2;
52 | }
53 | break;
54 |
55 | case "single":
56 | if (t > 1) {
57 | t %= 1;
58 | }
59 | if (this.interpolation.easing) {
60 | var a = t * Math.PI;
61 | return 0.5 - Math.cos(a) * 0.5;
62 | }
63 | else {
64 | return t;
65 | }
66 | }
67 |
68 | },
69 |
70 | startDraw: function (context, t) {
71 | context.save();
72 | context.lineWidth = this.getNumber("lineWidth", t, this.styles.lineWidth);
73 | context.strokeStyle = this.getColor("strokeStyle", t, this.styles.strokeStyle);
74 | context.fillStyle = this.getColor("fillStyle", t, this.styles.fillStyle);
75 | context.lineCap = this.getString("lineCap", t, this.styles.lineCap);
76 | context.lineJoin = this.getString("lineJoin", t, this.styles.lineJoin);
77 | context.miterLimit = this.getString("miterLimit", t, this.styles.miterLimit);
78 | context.globalAlpha = this.getNumber("globalAlpha", t, this.styles.globalAlpha);
79 | context.translate(this.getNumber("translationX", t, this.styles.translationX), this.getNumber("translationY", t, this.styles.translationY));
80 | context.globalCompositeOperation = this.getString("blendMode", t, this.styles.blendMode);
81 | var shake = this.getNumber("shake", t, this.styles.shake);
82 | context.translate(Math.random() * shake - shake / 2, Math.random() * shake - shake / 2);
83 |
84 | var lineDash = this.getArray("lineDash", t, this.styles.lineDash);
85 | if (lineDash) {
86 | context.setLineDash(lineDash);
87 | }
88 | context.beginPath();
89 | },
90 |
91 | drawFillAndStroke: function (context, t, doFill, doStroke) {
92 | var fill = this.getBool("fill", t, doFill),
93 | stroke = this.getBool("stroke", t, doStroke);
94 |
95 | context.save();
96 | if (fill) {
97 | this.setShadowParams(context, t);
98 | context.fill();
99 | }
100 | context.restore();
101 | if (stroke) {
102 | if (!fill) {
103 | this.setShadowParams(context, t);
104 | }
105 | context.stroke();
106 | }
107 | },
108 |
109 | setShadowParams: function (context, t) {
110 | context.shadowColor = this.getColor("shadowColor", t, this.styles.shadowColor);
111 | context.shadowOffsetX = this.getNumber("shadowOffsetX", t, this.styles.shadowOffsetX);
112 | context.shadowOffsetY = this.getNumber("shadowOffsetY", t, this.styles.shadowOffsetY);
113 | context.shadowBlur = this.getNumber("shadowBlur", t, this.styles.shadowBlur);
114 | },
115 |
116 | endDraw: function (context) {
117 | context.restore();
118 | },
119 |
120 | getNumber: function (prop, t, def) {
121 | return valueParser.getNumber(this.props[prop], t, def);
122 | },
123 |
124 | getColor: function (prop, t, def) {
125 | return colorParser.getColor(this.props[prop], t, def);
126 | },
127 |
128 | getString: function (prop, t, def) {
129 | return valueParser.getString(this.props[prop], t, def);
130 | },
131 |
132 | getBool: function (prop, t, def) {
133 | return valueParser.getBool(this.props[prop], t, def);
134 | },
135 |
136 | getArray: function (prop, t, def) {
137 | return valueParser.getArray(this.props[prop], t, def);
138 | },
139 |
140 | getObject: function (prop, t, def) {
141 | return valueParser.getObject(this.props[prop], t, def);
142 | },
143 |
144 | getPosition: function (prop, t, def) {
145 | return valueParser.getPosition(this.props[prop], t, def);
146 | }
147 | }
148 | };
149 |
--------------------------------------------------------------------------------
/libs/colorParser.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | var context = document.createElement("canvas").getContext("2d");
3 |
4 | function getColor(prop, t, def) {
5 | if(prop === undefined) {
6 | return def;
7 | }
8 | if(typeof(prop) === "string") {
9 | if(prop.charAt(0) === "#" && prop.length > 7) {
10 | var obj = getColorObj(prop);
11 | return getColorString(obj);
12 | }
13 | return prop;
14 | }
15 | else if(typeof(prop) === "function") {
16 | return prop(t);
17 | }
18 | else if(prop && prop.length === 2) {
19 | if(isLinearGradient(prop)) {
20 | return parseLinearGradient(prop, t);
21 | }
22 | if(isRadialGradient(prop)) {
23 | return parseRadialGradient(prop, t);
24 | }
25 | var c0 = getColorObj(prop[0]),
26 | c1 = getColorObj(prop[1]);
27 | return interpolateColor([c0, c1], t);
28 | }
29 | else if(prop && prop.length) {
30 | return prop[Math.round(t * (prop.length - 1))];
31 | }
32 | if(prop.type === "linearGradient") {
33 | var g = context.createLinearGradient(prop.x0, prop.y0, prop.x1, prop.y1);
34 | for(var i = 0; i < prop.colorStops.length; i++) {
35 | var stop = prop.colorStops[i];
36 | g.addColorStop(stop.position, stop.color);
37 | }
38 | return g;
39 | }
40 | if(prop.type === "radialGradient") {
41 | var g = context.createRadialGradient(prop.x0, prop.y0, prop.r0, prop.x1, prop.y1, prop.r1);
42 | for(var i = 0; i < prop.colorStops.length; i++) {
43 | var stop = prop.colorStops[i];
44 | g.addColorStop(stop.position, stop.color);
45 | }
46 | return g;
47 | }
48 | return def;
49 | }
50 |
51 | function isLinearGradient(prop) {
52 | return prop[0].type === "linearGradient" && prop[1].type === "linearGradient";
53 | }
54 |
55 | function parseLinearGradient(prop, t) {
56 | var g0 = prop[0],
57 | g1 = prop[1],
58 | x0 = g0.x0 + (g1.x0 - g0.x0) * t,
59 | y0 = g0.y0 + (g1.y0 - g0.y0) * t,
60 | x1 = g0.x1 + (g1.x1 - g0.x1) * t,
61 | y1 = g0.y1 + (g1.y1 - g0.y1) * t;
62 |
63 | var g = context.createLinearGradient(x0, y0, x1, y1);
64 | for(var i = 0; i < g0.colorStops.length; i++) {
65 | var stopA = g0.colorStops[i],
66 | stopB = g1.colorStops[i],
67 | position = stopA.position + (stopB.position - stopA.position) * t,
68 | colorA = getColorObj(stopA.color),
69 | colorB = getColorObj(stopB.color),
70 | color = interpolateColor([colorA, colorB], t);
71 | g.addColorStop(position, color);
72 | }
73 | return g;
74 | }
75 |
76 | function isRadialGradient(prop) {
77 | return prop[0].type === "radialGradient" && prop[1].type === "radialGradient";
78 | }
79 |
80 | function parseRadialGradient(prop, t) {
81 | var g0 = prop[0],
82 | g1 = prop[1],
83 | x0 = g0.x0 + (g1.x0 - g0.x0) * t,
84 | y0 = g0.y0 + (g1.y0 - g0.y0) * t,
85 | r0 = g0.r0 + (g1.r0 - g0.r0) * t,
86 | x1 = g0.x1 + (g1.x1 - g0.x1) * t,
87 | y1 = g0.y1 + (g1.y1 - g0.y1) * t,
88 | r1 = g0.r1 + (g1.r1 - g0.r1) * t;
89 |
90 | var g = context.createRadialGradient(x0, y0, r0, x1, y1, r1);
91 | for(var i = 0; i < g0.colorStops.length; i++) {
92 | var stopA = g0.colorStops[i],
93 | stopB = g1.colorStops[i],
94 | position = stopA.position + (stopB.position - stopA.position) * t,
95 | colorA = getColorObj(stopA.color),
96 | colorB = getColorObj(stopB.color),
97 | color = interpolateColor([colorA, colorB], t);
98 | g.addColorStop(position, color);
99 | }
100 | return g;
101 | }
102 |
103 | function getColorString(obj) {
104 | return "rgba(" + obj.r + "," + obj.g + "," + obj.b + "," + (obj.a / 255) + ")";
105 | }
106 |
107 | function getColorObj(color) {
108 | if(color.charAt(0) === "#") {
109 | if(color.length === 7) { // #rrggbb
110 | return {
111 | a: 255,
112 | r: parseInt(color.substring(1, 3), 16),
113 | g: parseInt(color.substring(3, 5), 16),
114 | b: parseInt(color.substring(5, 7), 16)
115 | }
116 | }
117 | else if(color.length === 9) { // #aarrggbb
118 | return {
119 | a: parseInt(color.substring(1, 3), 16),
120 | r: parseInt(color.substring(3, 5), 16),
121 | g: parseInt(color.substring(5, 7), 16),
122 | b: parseInt(color.substring(7, 9), 16)
123 | }
124 | }
125 | else { // #rgb
126 | var r = color.charAt(1),
127 | g = color.charAt(2),
128 | b = color.charAt(3);
129 |
130 | return {
131 | a: 255,
132 | r: parseInt(r + r, 16),
133 | g: parseInt(g + g, 16),
134 | b: parseInt(b + b, 16)
135 | }
136 | }
137 | }
138 | else if(color.substring(0, 4) === "rgb(") {
139 | var s = color.indexOf("(") + 1,
140 | e = color.indexOf(")"),
141 | channels = color.substring(s, e).split(",");
142 | return {
143 | a: 255,
144 | r: parseInt(channels[0], 10),
145 | g: parseInt(channels[1], 10),
146 | b: parseInt(channels[2], 10)
147 | }
148 | }
149 | else if(color.substring(0, 4) === "rgba") {
150 | var s = color.indexOf("(") + 1,
151 | e = color.indexOf(")"),
152 | channels = color.substring(s, e).split(",");
153 | return {
154 | a: parseFloat(channels[3]) * 255,
155 | r: parseInt(channels[0], 10),
156 | g: parseInt(channels[1], 10),
157 | b: parseInt(channels[2], 10)
158 | }
159 | }
160 | else {
161 | color = color.toLowerCase();
162 | if(namedColors[color] != null) {
163 | return getColorObj(namedColors[color]);
164 | }
165 | }
166 | return 0;
167 | }
168 |
169 | function interpolateColor(arr, t) {
170 | var c0 = arr[0],
171 | c1 = arr[1];
172 |
173 | var alpha = c0.a + (c1.a - c0.a) * t,
174 | red = Math.round(c0.r + (c1.r - c0.r) * t),
175 | green = Math.round(c0.g + (c1.g - c0.g) * t),
176 | blue = Math.round(c0.b + (c1.b - c0.b) * t);
177 | return "rgba(" + red + "," + green + "," + blue + "," + (alpha / 255) + ")";
178 | }
179 |
180 | var namedColors = {
181 | aliceblue: "#f0f8ff",
182 | antiquewhite: "#faebd7",
183 | aqua: "#00ffff",
184 | aquamarine: "#7fffd4",
185 | azure: "#f0ffff",
186 | beige: "#f5f5dc",
187 | bisque: "#ffe4c4",
188 | black: "#000000",
189 | blanchedalmond: "#ffebcd",
190 | blue: "#0000ff",
191 | blueviolet: "#8a2be2",
192 | brown: "#a52a2a",
193 | burlywood: "#deb887",
194 | cadetblue: "#5f9ea0",
195 | chartreuse: "#7fff00",
196 | chocolate: "#d2691e",
197 | coral: "#ff7f50",
198 | cornflowerblue: "#6495ed",
199 | cornsilk: "#fff8dc",
200 | crimson: "#dc143c",
201 | cyan: "#00ffff",
202 | darkblue: "#00008b",
203 | darkcyan: "#008b8b",
204 | darkgoldenrod: "#b8860b",
205 | darkgray: "#a9a9a9",
206 | darkgrey: "#a9a9a9",
207 | darkgreen: "#006400",
208 | darkkhaki: "#bdb76b",
209 | darkmagenta: "#8b008b",
210 | darkolivegreen: "#556b2f",
211 | darkorange: "#ff8c00",
212 | darkorchid: "#9932cc",
213 | darkred: "#8b0000",
214 | darksalmon: "#e9967a",
215 | darkseagreen: "#8fbc8f",
216 | darkslateblue: "#483d8b",
217 | darkslategray: "#2f4f4f",
218 | darkslategrey: "#2f4f4f",
219 | darkturquoise: "#00ced1",
220 | darkviolet: "#9400d3",
221 | deeppink: "#ff1493",
222 | deepskyblue: "#00bfff",
223 | dimgray: "#696969",
224 | dimgrey: "#696969",
225 | dodgerblue: "#1e90ff",
226 | firebrick: "#b22222",
227 | floralwhite: "#fffaf0",
228 | forestgreen: "#228b22",
229 | fuchsia: "#ff00ff",
230 | gainsboro: "#dcdcdc",
231 | ghostwhite: "#f8f8ff",
232 | gold: "#ffd700",
233 | goldenrod: "#daa520",
234 | gray: "#808080",
235 | grey: "#808080",
236 | green: "#008000",
237 | greenyellow: "#adff2f",
238 | honeydew: "#f0fff0",
239 | hotpink: "#ff69b4",
240 | indianred: "#cd5c5c",
241 | indigo: "#4b0082",
242 | ivory: "#fffff0",
243 | khaki: "#f0e68c",
244 | lavender: "#e6e6fa",
245 | lavenderblush: "#fff0f5",
246 | lawngreen: "#7cfc00",
247 | lemonchiffon: "#fffacd",
248 | lightblue: "#add8e6",
249 | lightcoral: "#f08080",
250 | lightcyan: "#e0ffff",
251 | lightgoldenrodyellow: "#fafad2",
252 | lightgray: "#d3d3d3",
253 | lightgrey: "#d3d3d3",
254 | lightgreen: "#90ee90",
255 | lightpink: "#ffb6c1",
256 | lightsalmon: "#ffa07a",
257 | lightseagreen: "#20b2aa",
258 | lightskyblue: "#87cefa",
259 | lightslategray: "#778899",
260 | lightslategrey: "#778899",
261 | lightsteelblue: "#b0c4de",
262 | lightyellow: "#ffffe0",
263 | lime: "#00ff00",
264 | limegreen: "#32cd32",
265 | linen: "#faf0e6",
266 | magenta: "#ff00ff",
267 | maroon: "#800000",
268 | mediumaquamarine: "#66cdaa",
269 | mediumblue: "#0000cd",
270 | mediumorchid: "#ba55d3",
271 | mediumpurple: "#9370d8",
272 | mediumseagreen: "#3cb371",
273 | mediumslateblue: "#7b68ee",
274 | mediumspringgreen: "#00fa9a",
275 | mediumturquoise: "#48d1cc",
276 | mediumvioletred: "#c71585",
277 | midnightblue: "#191970",
278 | mintcream: "#f5fffa",
279 | mistyrose: "#ffe4e1",
280 | moccasin: "#ffe4b5",
281 | navajowhite: "#ffdead",
282 | navy: "#000080",
283 | oldlace: "#fdf5e6",
284 | olive: "#808000",
285 | olivedrab: "#6b8e23",
286 | orange: "#ffa500",
287 | orangered: "#ff4500",
288 | orchid: "#da70d6",
289 | palegoldenrod: "#eee8aa",
290 | palegreen: "#98fb98",
291 | paleturquoise: "#afeeee",
292 | palevioletred: "#d87093",
293 | papayawhip: "#ffefd5",
294 | peachpuff: "#ffdab9",
295 | peru: "#cd853f",
296 | pink: "#ffc0cb",
297 | plum: "#dda0dd",
298 | powderblue: "#b0e0e6",
299 | purple: "#800080",
300 | red: "#ff0000",
301 | rosybrown: "#bc8f8f",
302 | royalblue: "#4169e1",
303 | saddlebrown: "#8b4513",
304 | salmon: "#fa8072",
305 | sandybrown: "#f4a460",
306 | seagreen: "#2e8b57",
307 | seashell: "#fff5ee",
308 | sienna: "#a0522d",
309 | silver: "#c0c0c0",
310 | skyblue: "#87ceeb",
311 | slateblue: "#6a5acd",
312 | slategray: "#708090",
313 | slategrey: "#708090",
314 | snow: "#fffafa",
315 | springgreen: "#00ff7f",
316 | steelblue: "#4682b4",
317 | tan: "#d2b48c",
318 | teal: "#008080",
319 | thistle: "#d8bfd8",
320 | tomato: "#ff6347",
321 | turquoise: "#40e0d0",
322 | violet: "#ee82ee",
323 | wheat: "#f5deb3",
324 | white: "#ffffff",
325 | whitesmoke: "#f5f5f5",
326 | yellow: "#ffff00",
327 | yellowgreen: "#9acd32"
328 | };
329 |
330 | return {
331 | getColor: getColor
332 | };
333 | };
--------------------------------------------------------------------------------
/dist/glc.min.js:
--------------------------------------------------------------------------------
1 | !function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{var g;g="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,g.GLC=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&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}for(var i="function"==typeof require&&require,o=0;o>16,green=num>>8&255,blue=255#return rgb(red,green,blue)}function randomHSV(minH,maxH,minS,maxS,minV,maxV){var h=minH+Math.random()*(maxH-minH),s=minS+Math.random()*(maxS-minS),v=minV+Math.random()*(maxV-minV);return hsv(h,s,v)}function hsva(h,s,v,a){var r,g,b,i=Math.floor(h/60),f=h/60-i,p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);switch(i%6){case 0:r=v,g=t,b=p;break;case 1:r=q,g=v,b=p;break;case 2:r=p,g=v,b=t;break;case 3:r=p,g=q,b=v;break;case 4:r=t,g=p,b=v;break;case 5:r=v,g=p,b=q}return rgba(Math.floor(255*r),Math.floor(255*g),Math.floor(255*b),a)}function hsv(h,s,v){return hsva(h,s,v,1)}function animHSVA(startH,endH,startS,endS,startV,endV,startA,endA){return function(t){var h=startH+t*(endH-startH),s=startS+t*(endS-startS),v=startV+t*(endV-startV),a=startA+t*(endA-startA);return hsva(h,s,v,a)}}function animHSV(startH,endH,startS,endS,startV,endV){return animHSVA(startH,endH,startS,endS,startV,endV,1,1)}function createLinearGradient(x0,y0,x1,y1){var g={type:"linearGradient",x0:x0,y0:y0,x1:x1,y1:y1,colorStops:[],addColorStop:function(position,color){this.colorStops.push({position:position,color:color})}};return g}function createRadialGradient(x0,y0,r0,x1,y1,r1){var g={type:"radialGradient",x0:x0,y0:y0,r0:r0,x1:x1,y1:y1,r1:r1,colorStops:[],addColorStop:function(position,color){this.colorStops.push({position:position,color:color})}};return g}var color={r:255,g:255,b:255,a:1,setRGBA:function(r,g,b,a){return this.r=r,this.g=g,this.b=b,this.a=a,this},toString:function(){return"rgba("+Math.floor(this.r)+","+Math.floor(this.g)+","+Math.floor(this.b)+","+this.a+")"}};return{rgb:rgb,rgba:rgba,randomRGB:randomRGB,randomGray:randomGray,gray:gray,num:num,hsv:hsv,hsva:hsva,animHSV:animHSV,animHSVA:animHSVA,randomHSV:randomHSV,createLinearGradient:createLinearGradient,createRadialGradient:createRadialGradient}}},{}],2:[function(require,module,exports){module.exports=function(){function getColor(prop,t,def){if(void 0===prop)return def;if("string"==typeof prop){if("#"===prop.charAt(0)&&prop.length>7){var obj=getColorObj(prop);return getColorString(obj)}return prop}if("function"==typeof prop)return prop(t);if(prop&&2===prop.length){if(isLinearGradient(prop))return parseLinearGradient(prop,t);if(isRadialGradient(prop))return parseRadialGradient(prop,t);var c0=getColorObj(prop[0]),c1=getColorObj(prop[1]);return interpolateColor([c0,c1],t)}if(prop&&prop.length)return prop[Math.round(t*(prop.length-1))];if("linearGradient"===prop.type){for(var g=context.createLinearGradient(prop.x0,prop.y0,prop.x1,prop.y1),i=0;ii;i++){var v0=arr0[i],v1=arr1[i];result.push(v0+(v1-v0)*t)}return result}return prop&&prop.length>1?prop:def},getObject:function(prop,t,def){return void 0===prop?def:"function"==typeof prop?prop(t):prop&&prop.length?prop[Math.round(t*(prop.length-1))]:prop}}}},{}],4:[function(require,module,exports){var Model=require("./model"),RenderList=require("./renderList"),colorLib=require("./libs/color")(),shapeList=require("./shapeList");module.exports=function(canvasWrapper,renderCallback,completeCallback){function onRender(t){"function"==typeof renderCallback&&renderCallback(),renderList.render(t)}function onComplete(){"function"==typeof completeCallback&&completeCallback()}var model=new Model,renderList=new RenderList;renderList.init(model.w,model.h,model.styles,model.interpolation);for(var key in shapeList)if(shapeList.hasOwnProperty(key)){var shapeName=key;renderList["add"+shapeName[0].toUpperCase()+shapeName.slice(1)]=function(shapeName,shapeList){return function(props){renderList.addShape(shapeList[shapeName],props)}}(shapeName,shapeList)}model.scheduler.init(onRender,onComplete);var canvasEl=canvasWrapper.appendChild(renderList.getCanvas());return{w:model.w,h:model.h,model:model,renderList:renderList,size:function(width,height){this.w=model.w=width,this.h=model.h=height,renderList.size(model.w,model.h)},styles:model.styles,playOnce:function(){return model.playOnce()},loop:function(){return model.loop()},getDuration:function(){return model.getDuration()},setFPS:function(value){model.setFPS(value)},setDuration:function(value){model.setDuration(value)},setMode:function(value){model.interpolation.mode=value},setEasing:function(value){model.interpolation.easing=value},setMaxColors:function(value){model.maxColors=value},color:colorLib,canvasEl:canvasEl}}},{"./libs/color":1,"./model":5,"./renderList":6,"./shapeList":9}],5:[function(require,module,exports){var Scheduler=require("./scheduler");module.exports=function(){var scheduler=new Scheduler,styles={backgroundColor:"#ffffff",lineWidth:5,strokeStyle:"#000000",fillStyle:"#000000",lineCap:"round",lineJoin:"miter",lineDash:[],miterLimit:10,shadowColor:null,shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0,globalAlpha:1,translationX:0,translationY:0,shake:0,blendMode:"source-over"};return{interpolation:{mode:"bounce",easing:!0},maxColors:256,w:400,h:400,capture:!1,styles:styles,scheduler:scheduler,playOnce:function(){return scheduler.playOnce()},loop:function(){return scheduler.loop()},getDuration:function(){return scheduler.getDuration()},setDuration:function(value){scheduler.setDuration(value)},getFPS:function(){return scheduler.getFPS()},setFPS:function(value){scheduler.setFPS(value)},getIsRunning:function(){return scheduler.isRunning()}}}},{"./scheduler":7}],6:[function(require,module,exports){var Shape=require("./shape");module.exports=function(){function init(w,h,stylesValue,interpolation){canvas=document.createElement("canvas"),width=canvas.width=w,height=canvas.height=h,context=canvas.getContext("2d"),styles=stylesValue,shape.styles=styles,shape.interpolation=interpolation}function size(w,h){width=canvas.width=w,height=canvas.height=h}function addShape(newShape,props){var item=shape.create(newShape,props);list.push(item),render(0)}function clear(){list.length=0}function render(t){"transparent"===styles.backgroundColor?context.clearRect(0,0,width,height):(context.fillStyle=styles.backgroundColor,context.fillRect(0,0,width,height));for(var i=0;i=1&&(looping?t-=1:(t=0,stop()))}function loop(){running||(t=0,looping=!0,running=!0,render())}function stop(){running=!1,looping=!1,t=0}function playOnce(){running||(t=0,looping=!1,running=!0,render())}function isRunning(){return running}function setDuration(value){return duration=value}function getDuration(){return duration}function setFPS(value){return fps=value}function getFPS(){return fps}var t=0,duration=2,fps=30,running=!1,looping=!1,renderCallback=null,completeCallback=null;return{init:init,loop:loop,playOnce:playOnce,stop:stop,isRunning:isRunning,setDuration:setDuration,getDuration:getDuration,setFPS:setFPS,getFPS:getFPS}}},{}],8:[function(require,module,exports){var valueParser=require("./libs/valueParser")(),colorParser=require("./libs/colorParser")();module.exports=function(){return{styles:null,interpolation:null,create:function(type,props){var obj=Object.create(this);return obj.init(type,props||{}),obj},init:function(type,props){this.props=props;for(var prop in props){var p=props[prop];"function"==typeof p&&(props[prop]=p.bind(props))}this.draw=type.draw},render:function(context,time){var t=this.interpolate(time);this.startDraw(context,t),this.draw(context,t),this.endDraw(context,t)},interpolate:function(t){switch(t*=this.props.speedMult||1,t+=this.props.phase||0,this.interpolation.mode){case"bounce":if(this.interpolation.easing){var a=t*Math.PI*2;return.5-.5*Math.cos(a)}return t%=1,.5>t?2*t:t=2*(1-t);case"single":if(t>1&&(t%=1),this.interpolation.easing){var a=t*Math.PI;return.5-.5*Math.cos(a)}return t}},startDraw:function(context,t){context.save(),context.lineWidth=this.getNumber("lineWidth",t,this.styles.lineWidth),context.strokeStyle=this.getColor("strokeStyle",t,this.styles.strokeStyle),context.fillStyle=this.getColor("fillStyle",t,this.styles.fillStyle),context.lineCap=this.getString("lineCap",t,this.styles.lineCap),context.lineJoin=this.getString("lineJoin",t,this.styles.lineJoin),context.miterLimit=this.getString("miterLimit",t,this.styles.miterLimit),context.globalAlpha=this.getNumber("globalAlpha",t,this.styles.globalAlpha),context.translate(this.getNumber("translationX",t,this.styles.translationX),this.getNumber("translationY",t,this.styles.translationY)),context.globalCompositeOperation=this.getString("blendMode",t,this.styles.blendMode);var shake=this.getNumber("shake",t,this.styles.shake);context.translate(Math.random()*shake-shake/2,Math.random()*shake-shake/2);var lineDash=this.getArray("lineDash",t,this.styles.lineDash);lineDash&&context.setLineDash(lineDash),context.beginPath()},drawFillAndStroke:function(context,t,doFill,doStroke){var fill=this.getBool("fill",t,doFill),stroke=this.getBool("stroke",t,doStroke);context.save(),fill&&(this.setShadowParams(context,t),context.fill()),context.restore(),stroke&&(fill||this.setShadowParams(context,t),context.stroke())},setShadowParams:function(context,t){context.shadowColor=this.getColor("shadowColor",t,this.styles.shadowColor),context.shadowOffsetX=this.getNumber("shadowOffsetX",t,this.styles.shadowOffsetX),context.shadowOffsetY=this.getNumber("shadowOffsetY",t,this.styles.shadowOffsetY),context.shadowBlur=this.getNumber("shadowBlur",t,this.styles.shadowBlur)},endDraw:function(context){context.restore()},getNumber:function(prop,t,def){return valueParser.getNumber(this.props[prop],t,def)},getColor:function(prop,t,def){return colorParser.getColor(this.props[prop],t,def)},getString:function(prop,t,def){return valueParser.getString(this.props[prop],t,def)},getBool:function(prop,t,def){return valueParser.getBool(this.props[prop],t,def)},getArray:function(prop,t,def){return valueParser.getArray(this.props[prop],t,def)},getObject:function(prop,t,def){return valueParser.getObject(this.props[prop],t,def)},getPosition:function(prop,t,def){return valueParser.getPosition(this.props[prop],t,def)}}}},{"./libs/colorParser":2,"./libs/valueParser":3}],9:[function(require,module,exports){var circle=require("./shapes/circle")(),heart=require("./shapes/heart")(),arrow=require("./shapes/arrow")(),arcSegment=require("./shapes/arcSegment")(),bezierCurve=require("./shapes/bezierCurve")(),bezierSegment=require("./shapes/bezierSegment")(),cube=require("./shapes/cube")(),curve=require("./shapes/curve")(),curveSegment=require("./shapes/curveSegment")(),gear=require("./shapes/gear")(),line=require("./shapes/line")(),oval=require("./shapes/oval")(),path=require("./shapes/path")(),poly=require("./shapes/poly")(),raySegment=require("./shapes/raySegment")(),rect=require("./shapes/rect")(),segment=require("./shapes/segment")(),spiral=require("./shapes/spiral")(),star=require("./shapes/star")(),text=require("./shapes/text")();module.exports={circle:circle,arrow:arrow,arcSegment:arcSegment,bezierCurve:bezierCurve,bezierSegment:bezierSegment,cube:cube,curve:curve,curveSegment:curveSegment,gear:gear,line:line,oval:oval,path:path,poly:poly,raySegment:raySegment,rect:rect,segment:segment,spiral:spiral,star:star,text:text,heart:heart}},{"./shapes/arcSegment":10,"./shapes/arrow":11,"./shapes/bezierCurve":12,"./shapes/bezierSegment":13,"./shapes/circle":14,"./shapes/cube":15,"./shapes/curve":16,"./shapes/curveSegment":17,"./shapes/gear":18,"./shapes/heart":19,"./shapes/line":20,"./shapes/oval":21,"./shapes/path":22,"./shapes/poly":23,"./shapes/raySegment":24,"./shapes/rect":25,"./shapes/segment":26,"./shapes/spiral":27,"./shapes/star":28,"./shapes/text":29}],10:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),radius=this.getNumber("radius",t,50),startAngle=this.getNumber("startAngle",t,0),endAngle=this.getNumber("endAngle",t,360);if(startAngle>endAngle){var temp=startAngle;startAngle=endAngle,endAngle=temp}var arc=this.getNumber("arc",t,20),start=startAngle-1,end=startAngle+t*(endAngle-startAngle+arc);end>startAngle+arc&&(start=end-arc),end>endAngle&&(end=endAngle+1),context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),context.arc(0,0,radius,start*Math.PI/180,end*Math.PI/180),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],11:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),w=this.getNumber("w",t,100),h=this.getNumber("h",t,100),pointPercent=this.getNumber("pointPercent",t,.5),shaftPercent=this.getNumber("shaftPercent",t,.5);context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),context.moveTo(-w/2,-h*shaftPercent*.5),context.lineTo(w/2-w*pointPercent,-h*shaftPercent*.5),context.lineTo(w/2-w*pointPercent,.5*-h),context.lineTo(w/2,0),context.lineTo(w/2-w*pointPercent,.5*h),context.lineTo(w/2-w*pointPercent,h*shaftPercent*.5),context.lineTo(-w/2,h*shaftPercent*.5),context.lineTo(-w/2,-h*shaftPercent*.5),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],12:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x0=this.getNumber("x0",t,50),y0=this.getNumber("y0",t,10),x1=this.getNumber("x1",t,200),y1=this.getNumber("y1",t,100),x2=this.getNumber("x2",t,0),y2=this.getNumber("y2",t,100),x3=this.getNumber("x3",t,150),y3=this.getNumber("y3",t,10);context.moveTo(x0,y0),context.bezierCurveTo(x1,y1,x2,y2,x3,y3),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],13:[function(require,module,exports){module.exports=function(){function bezier(t,v0,v1,v2,v3){return(1-t)*(1-t)*(1-t)*v0+3*(1-t)*(1-t)*t*v1+3*(1-t)*t*t*v2+t*t*t*v3}return{draw:function(context,t){var x,y,x0=this.getNumber("x0",t,50),y0=this.getNumber("y0",t,10),x1=this.getNumber("x1",t,200),y1=this.getNumber("y1",t,100),x2=this.getNumber("x2",t,0),y2=this.getNumber("y2",t,100),x3=this.getNumber("x3",t,150),y3=this.getNumber("y3",t,10),percent=this.getNumber("percent",t,.1),t1=t*(1+percent),t0=t1-percent,res=.01;t1=Math.min(t1,1.001),t0=Math.max(t0,-.001);for(var i=t0;t1>i;i+=res)x=bezier(i,x0,x1,x2,x3),y=bezier(i,y0,y1,y2,y3),i===t0?context.moveTo(x,y):context.lineTo(x,y);x=bezier(t1,x0,x1,x2,x3),y=bezier(t1,y0,y1,y2,y3),context.lineTo(x,y),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],14:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),radius=this.getNumber("radius",t,50),startAngle=this.getNumber("startAngle",t,0),endAngle=this.getNumber("endAngle",t,360),drawFromCenter=this.getBool("drawFromCenter",t,!1);context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),drawFromCenter&&context.moveTo(0,0),context.arc(0,0,radius,startAngle*Math.PI/180,endAngle*Math.PI/180),drawFromCenter&&context.closePath(),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],15:[function(require,module,exports){module.exports=function(){function scale(points,size){for(var i=0;ii;i+=res)x=quadratic(i,x0,x1,x2),y=quadratic(i,y0,y1,y2),i===t0?context.moveTo(x,y):context.lineTo(x,y);x=quadratic(t1,x0,x1,x2),y=quadratic(t1,y0,y1,y2),context.lineTo(x,y),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],18:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),radius=this.getNumber("radius",t,50),toothHeight=this.getNumber("toothHeight",t,10),hub=this.getNumber("hub",t,10),rotation=this.getNumber("rotation",t,0)*Math.PI/180,teeth=this.getNumber("teeth",t,10),toothAngle=this.getNumber("toothAngle",t,.3),face=.5-toothAngle/2,side=.5-face,innerRadius=radius-toothHeight;context.translate(x,y),context.rotate(rotation),context.save(),context.moveTo(radius,0);for(var angle=2*Math.PI/teeth,i=0;teeth>i;i++)context.rotate(angle*face),context.lineTo(radius,0),context.rotate(angle*side),context.lineTo(innerRadius,0),context.rotate(angle*face),context.lineTo(innerRadius,0),context.rotate(angle*side),context.lineTo(radius,0);context.lineTo(radius,0),context.restore(),context.moveTo(hub,0),context.arc(0,0,hub,0,2*Math.PI,!0),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],19:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),w=this.getNumber("w",t,50),h=this.getNumber("h",t,50),x0=0,y0=-.25,x1=.2,y1=-.8,x2=1.1,y2=-.2,x3=0,y3=.5;context.save(),context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),context.save(),context.scale(w,h),context.moveTo(x0,y0),context.bezierCurveTo(x1,y1,x2,y2,x3,y3),context.bezierCurveTo(-x2,y2,-x1,y1,-x0,y0),context.restore(),this.drawFillAndStroke(context,t,!0,!1),context.restore()}}}},{}],20:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x0=this.getNumber("x0",t,0),y0=this.getNumber("y0",t,0),x1=this.getNumber("x1",t,100),y1=this.getNumber("y1",t,100);context.moveTo(x0,y0),context.lineTo(x1,y1),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],21:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),rx=this.getNumber("rx",t,50),ry=this.getNumber("ry",t,50),startAngle=this.getNumber("startAngle",t,0),endAngle=this.getNumber("endAngle",t,360),drawFromCenter=this.getBool("drawFromCenter",t,!1);context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),context.save(),context.scale(rx/100,ry/100),drawFromCenter&&context.moveTo(0,0),context.arc(0,0,100,startAngle*Math.PI/180,endAngle*Math.PI/180),drawFromCenter&&context.closePath(),context.restore(),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],22:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var path=this.getArray("path",t,[]),startPercent=this.getNumber("startPercent",t,0),endPercent=this.getNumber("endPercent",t,1),startPoint=Math.floor(path.length/2*startPercent),endPoint=Math.floor(path.length/2*endPercent),startIndex=2*startPoint,endIndex=2*endPoint;if(startIndex>endIndex){var temp=startIndex;startIndex=endIndex,endIndex=temp}context.moveTo(path[startIndex],path[startIndex+1]);for(var i=startIndex+2;endIndex-1>i;i+=2)context.lineTo(path[i],path[i+1]);this.drawFillAndStroke(context,t,!1,!0)}}}},{}],23:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),radius=this.getNumber("radius",t,50),rotation=this.getNumber("rotation",t,0)*Math.PI/180,sides=this.getNumber("sides",t,5);context.translate(x,y),context.rotate(rotation),context.moveTo(radius,0);for(var i=1;sides>i;i++){var angle=2*Math.PI/sides*i;context.lineTo(Math.cos(angle)*radius,Math.sin(angle)*radius)}context.lineTo(radius,0),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],24:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),angle=this.getNumber("angle",t,0)*Math.PI/180,length=this.getNumber("length",t,100),segmentLength=this.getNumber("segmentLength",t,50),start=-.01,end=(length+segmentLength)*t;end>segmentLength&&(start=end-segmentLength),end>length&&(end=length+.01),context.translate(x,y),context.rotate(angle),context.moveTo(start,0),context.lineTo(end,0),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],25:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),w=this.getNumber("w",t,100),h=this.getNumber("h",t,100);context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),this.getBool("drawFromCenter",t,!0)?context.rect(.5*-w,.5*-h,w,h):context.rect(0,0,w,h),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],26:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x0=this.getNumber("x0",t,0),y0=this.getNumber("y0",t,0),x1=this.getNumber("x1",t,100),y1=this.getNumber("y1",t,100),segmentLength=this.getNumber("segmentLength",t,50),dx=x1-x0,dy=y1-y0,angle=Math.atan2(dy,dx),dist=Math.sqrt(dx*dx+dy*dy),start=-.01,end=(dist+segmentLength)*t;
2 | end>segmentLength&&(start=end-segmentLength),end>dist&&(end=dist+.01),context.translate(x0,y0),context.rotate(angle),context.moveTo(start,0),context.lineTo(end,0),this.drawFillAndStroke(context,t,!1,!0)}}}},{}],27:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,"100"),y=this.getNumber("y",t,"100"),innerRadius=this.getNumber("innerRadius",t,10),outerRadius=this.getNumber("outerRadius",t,90),turns=this.getNumber("turns",t,6),res=this.getNumber("res",t,1)*Math.PI/180,fullAngle=2*Math.PI*turns;if(context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180),fullAngle>0)for(var a=0;fullAngle>a;a+=res){var r=innerRadius+(outerRadius-innerRadius)*a/fullAngle;context.lineTo(Math.cos(a)*r,Math.sin(a)*r)}else for(var a=0;a>fullAngle;a-=res){var r=innerRadius+(outerRadius-innerRadius)*a/fullAngle;context.lineTo(Math.cos(a)*r,Math.sin(a)*r)}this.drawFillAndStroke(context,t,!1,!0)}}}},{}],28:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),innerRadius=this.getNumber("innerRadius",t,25),outerRadius=this.getNumber("outerRadius",t,50),rotation=this.getNumber("rotation",t,0)*Math.PI/180,points=this.getNumber("points",t,5);context.translate(x,y),context.rotate(rotation),context.moveTo(outerRadius,0);for(var i=1;2*points>i;i++){var angle=2*Math.PI/points/2*i,r=i%2?innerRadius:outerRadius;context.lineTo(Math.cos(angle)*r,Math.sin(angle)*r)}context.lineTo(outerRadius,0),this.drawFillAndStroke(context,t,!0,!1)}}}},{}],29:[function(require,module,exports){module.exports=function(){return{draw:function(context,t){var x=this.getNumber("x",t,100),y=this.getNumber("y",t,100),text=this.getString("text",t,"hello"),fontSize=this.getNumber("fontSize",t,20),fontWeight=this.getString("fontWeight",t,"normal");fontFamily=this.getString("fontFamily",t,"sans-serif"),fontStyle=this.getString("fontStyle",t,"normal"),context.font=fontWeight+" "+fontStyle+" "+fontSize+"px "+fontFamily;var width=context.measureText(text).width;context.translate(x,y),context.rotate(this.getNumber("rotation",t,0)*Math.PI/180);var shadowsSet=!1;context.save(),this.getBool("fill",t,!0)&&(this.setShadowParams(context,t),shadowsSet=!0,context.fillText(text,-width/2,.4*fontSize)),context.restore(),this.getBool("stroke",t,!1)&&(shadowsSet||this.setShadowParams(context,t),context.strokeText(text,-width/2,.4*fontSize))}}}},{}]},{},[4])(4)});
3 |
--------------------------------------------------------------------------------
/dist/glc.js:
--------------------------------------------------------------------------------
1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GLC = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 16,
36 | green = num >> 8 & 0xff,
37 | blue = num & 0xff;
38 | return rgb(red, green, blue);
39 | }
40 |
41 | function randomHSV(minH, maxH, minS, maxS, minV, maxV) {
42 | var h = minH + Math.random() * (maxH - minH),
43 | s = minS + Math.random() * (maxS - minS),
44 | v = minV + Math.random() * (maxV - minV);
45 | return hsv(h, s, v);
46 | }
47 |
48 | function hsva(h, s, v, a) {
49 | var r, g, b,
50 | i = Math.floor(h / 60),
51 | f = h / 60 - i,
52 | p = v * (1 - s),
53 | q = v * (1 - f * s),
54 | t = v * (1 - (1 - f) * s);
55 | switch (i % 6) {
56 | case 0: r = v, g = t, b = p; break;
57 | case 1: r = q, g = v, b = p; break;
58 | case 2: r = p, g = v, b = t; break;
59 | case 3: r = p, g = q, b = v; break;
60 | case 4: r = t, g = p, b = v; break;
61 | case 5: r = v, g = p, b = q; break;
62 | }
63 | return rgba(
64 | Math.floor(r * 255),
65 | Math.floor(g * 255),
66 | Math.floor(b * 255),
67 | a
68 | );
69 | }
70 |
71 | function hsv(h, s, v) {
72 | return hsva(h, s, v, 1);
73 | }
74 |
75 | function animHSVA(startH, endH, startS, endS, startV, endV, startA, endA) {
76 | return function(t) {
77 | var h = startH + t * (endH - startH),
78 | s = startS + t * (endS - startS),
79 | v = startV + t * (endV - startV),
80 | a = startA + t * (endA - startA);
81 | return hsva(h, s, v, a);
82 | }
83 | }
84 |
85 | function animHSV(startH, endH, startS, endS, startV, endV) {
86 | return animHSVA(startH, endH, startS, endS, startV, endV, 1, 1);
87 | }
88 |
89 |
90 |
91 |
92 |
93 | /////////////////////
94 | // gradients
95 | /////////////////////
96 | function createLinearGradient(x0, y0, x1, y1) {
97 | var g = {
98 | type: "linearGradient",
99 | x0: x0,
100 | y0: y0,
101 | x1: x1,
102 | y1: y1,
103 | colorStops: [],
104 | addColorStop: function(position, color) {
105 | this.colorStops.push({
106 | position: position,
107 | color: color
108 | });
109 | }
110 | };
111 | return g;
112 | }
113 |
114 | function createRadialGradient(x0, y0, r0, x1, y1, r1) {
115 | var g = {
116 | type: "radialGradient",
117 | x0: x0,
118 | y0: y0,
119 | r0: r0,
120 | x1: x1,
121 | y1: y1,
122 | r1: r1,
123 | colorStops: [],
124 | addColorStop: function(position, color) {
125 | this.colorStops.push({
126 | position: position,
127 | color: color
128 | });
129 | }
130 | };
131 | return g;
132 | }
133 |
134 | var color = {
135 | r: 255,
136 | g: 255,
137 | b: 255,
138 | a: 1,
139 |
140 | setRGBA: function(r, g, b, a) {
141 | this.r = r;
142 | this.g = g;
143 | this.b = b;
144 | this.a = a;
145 | return this;
146 | },
147 |
148 | toString: function() {
149 | return "rgba(" + Math.floor(this.r) + "," + Math.floor(this.g) + "," + Math.floor(this.b) + "," + this.a + ")";
150 | }
151 | };
152 |
153 | return {
154 | rgb: rgb,
155 | rgba: rgba,
156 | randomRGB: randomRGB,
157 | randomGray: randomGray,
158 | gray: gray,
159 | num: num,
160 | hsv: hsv,
161 | hsva: hsva,
162 | animHSV: animHSV,
163 | animHSVA: animHSVA,
164 | randomHSV: randomHSV,
165 | createLinearGradient: createLinearGradient,
166 | createRadialGradient: createRadialGradient
167 | };
168 | };
169 | },{}],2:[function(require,module,exports){
170 | module.exports = function() {
171 | var context = document.createElement("canvas").getContext("2d");
172 |
173 | function getColor(prop, t, def) {
174 | if(prop === undefined) {
175 | return def;
176 | }
177 | if(typeof(prop) === "string") {
178 | if(prop.charAt(0) === "#" && prop.length > 7) {
179 | var obj = getColorObj(prop);
180 | return getColorString(obj);
181 | }
182 | return prop;
183 | }
184 | else if(typeof(prop) === "function") {
185 | return prop(t);
186 | }
187 | else if(prop && prop.length === 2) {
188 | if(isLinearGradient(prop)) {
189 | return parseLinearGradient(prop, t);
190 | }
191 | if(isRadialGradient(prop)) {
192 | return parseRadialGradient(prop, t);
193 | }
194 | var c0 = getColorObj(prop[0]),
195 | c1 = getColorObj(prop[1]);
196 | return interpolateColor([c0, c1], t);
197 | }
198 | else if(prop && prop.length) {
199 | return prop[Math.round(t * (prop.length - 1))];
200 | }
201 | if(prop.type === "linearGradient") {
202 | var g = context.createLinearGradient(prop.x0, prop.y0, prop.x1, prop.y1);
203 | for(var i = 0; i < prop.colorStops.length; i++) {
204 | var stop = prop.colorStops[i];
205 | g.addColorStop(stop.position, stop.color);
206 | }
207 | return g;
208 | }
209 | if(prop.type === "radialGradient") {
210 | var g = context.createRadialGradient(prop.x0, prop.y0, prop.r0, prop.x1, prop.y1, prop.r1);
211 | for(var i = 0; i < prop.colorStops.length; i++) {
212 | var stop = prop.colorStops[i];
213 | g.addColorStop(stop.position, stop.color);
214 | }
215 | return g;
216 | }
217 | return def;
218 | }
219 |
220 | function isLinearGradient(prop) {
221 | return prop[0].type === "linearGradient" && prop[1].type === "linearGradient";
222 | }
223 |
224 | function parseLinearGradient(prop, t) {
225 | var g0 = prop[0],
226 | g1 = prop[1],
227 | x0 = g0.x0 + (g1.x0 - g0.x0) * t,
228 | y0 = g0.y0 + (g1.y0 - g0.y0) * t,
229 | x1 = g0.x1 + (g1.x1 - g0.x1) * t,
230 | y1 = g0.y1 + (g1.y1 - g0.y1) * t;
231 |
232 | var g = context.createLinearGradient(x0, y0, x1, y1);
233 | for(var i = 0; i < g0.colorStops.length; i++) {
234 | var stopA = g0.colorStops[i],
235 | stopB = g1.colorStops[i],
236 | position = stopA.position + (stopB.position - stopA.position) * t,
237 | colorA = getColorObj(stopA.color),
238 | colorB = getColorObj(stopB.color),
239 | color = interpolateColor([colorA, colorB], t);
240 | g.addColorStop(position, color);
241 | }
242 | return g;
243 | }
244 |
245 | function isRadialGradient(prop) {
246 | return prop[0].type === "radialGradient" && prop[1].type === "radialGradient";
247 | }
248 |
249 | function parseRadialGradient(prop, t) {
250 | var g0 = prop[0],
251 | g1 = prop[1],
252 | x0 = g0.x0 + (g1.x0 - g0.x0) * t,
253 | y0 = g0.y0 + (g1.y0 - g0.y0) * t,
254 | r0 = g0.r0 + (g1.r0 - g0.r0) * t,
255 | x1 = g0.x1 + (g1.x1 - g0.x1) * t,
256 | y1 = g0.y1 + (g1.y1 - g0.y1) * t,
257 | r1 = g0.r1 + (g1.r1 - g0.r1) * t;
258 |
259 | var g = context.createRadialGradient(x0, y0, r0, x1, y1, r1);
260 | for(var i = 0; i < g0.colorStops.length; i++) {
261 | var stopA = g0.colorStops[i],
262 | stopB = g1.colorStops[i],
263 | position = stopA.position + (stopB.position - stopA.position) * t,
264 | colorA = getColorObj(stopA.color),
265 | colorB = getColorObj(stopB.color),
266 | color = interpolateColor([colorA, colorB], t);
267 | g.addColorStop(position, color);
268 | }
269 | return g;
270 | }
271 |
272 | function getColorString(obj) {
273 | return "rgba(" + obj.r + "," + obj.g + "," + obj.b + "," + (obj.a / 255) + ")";
274 | }
275 |
276 | function getColorObj(color) {
277 | if(color.charAt(0) === "#") {
278 | if(color.length === 7) { // #rrggbb
279 | return {
280 | a: 255,
281 | r: parseInt(color.substring(1, 3), 16),
282 | g: parseInt(color.substring(3, 5), 16),
283 | b: parseInt(color.substring(5, 7), 16)
284 | }
285 | }
286 | else if(color.length === 9) { // #aarrggbb
287 | return {
288 | a: parseInt(color.substring(1, 3), 16),
289 | r: parseInt(color.substring(3, 5), 16),
290 | g: parseInt(color.substring(5, 7), 16),
291 | b: parseInt(color.substring(7, 9), 16)
292 | }
293 | }
294 | else { // #rgb
295 | var r = color.charAt(1),
296 | g = color.charAt(2),
297 | b = color.charAt(3);
298 |
299 | return {
300 | a: 255,
301 | r: parseInt(r + r, 16),
302 | g: parseInt(g + g, 16),
303 | b: parseInt(b + b, 16)
304 | }
305 | }
306 | }
307 | else if(color.substring(0, 4) === "rgb(") {
308 | var s = color.indexOf("(") + 1,
309 | e = color.indexOf(")"),
310 | channels = color.substring(s, e).split(",");
311 | return {
312 | a: 255,
313 | r: parseInt(channels[0], 10),
314 | g: parseInt(channels[1], 10),
315 | b: parseInt(channels[2], 10)
316 | }
317 | }
318 | else if(color.substring(0, 4) === "rgba") {
319 | var s = color.indexOf("(") + 1,
320 | e = color.indexOf(")"),
321 | channels = color.substring(s, e).split(",");
322 | return {
323 | a: parseFloat(channels[3]) * 255,
324 | r: parseInt(channels[0], 10),
325 | g: parseInt(channels[1], 10),
326 | b: parseInt(channels[2], 10)
327 | }
328 | }
329 | else {
330 | color = color.toLowerCase();
331 | if(namedColors[color] != null) {
332 | return getColorObj(namedColors[color]);
333 | }
334 | }
335 | return 0;
336 | }
337 |
338 | function interpolateColor(arr, t) {
339 | var c0 = arr[0],
340 | c1 = arr[1];
341 |
342 | var alpha = c0.a + (c1.a - c0.a) * t,
343 | red = Math.round(c0.r + (c1.r - c0.r) * t),
344 | green = Math.round(c0.g + (c1.g - c0.g) * t),
345 | blue = Math.round(c0.b + (c1.b - c0.b) * t);
346 | return "rgba(" + red + "," + green + "," + blue + "," + (alpha / 255) + ")";
347 | }
348 |
349 | var namedColors = {
350 | aliceblue: "#f0f8ff",
351 | antiquewhite: "#faebd7",
352 | aqua: "#00ffff",
353 | aquamarine: "#7fffd4",
354 | azure: "#f0ffff",
355 | beige: "#f5f5dc",
356 | bisque: "#ffe4c4",
357 | black: "#000000",
358 | blanchedalmond: "#ffebcd",
359 | blue: "#0000ff",
360 | blueviolet: "#8a2be2",
361 | brown: "#a52a2a",
362 | burlywood: "#deb887",
363 | cadetblue: "#5f9ea0",
364 | chartreuse: "#7fff00",
365 | chocolate: "#d2691e",
366 | coral: "#ff7f50",
367 | cornflowerblue: "#6495ed",
368 | cornsilk: "#fff8dc",
369 | crimson: "#dc143c",
370 | cyan: "#00ffff",
371 | darkblue: "#00008b",
372 | darkcyan: "#008b8b",
373 | darkgoldenrod: "#b8860b",
374 | darkgray: "#a9a9a9",
375 | darkgrey: "#a9a9a9",
376 | darkgreen: "#006400",
377 | darkkhaki: "#bdb76b",
378 | darkmagenta: "#8b008b",
379 | darkolivegreen: "#556b2f",
380 | darkorange: "#ff8c00",
381 | darkorchid: "#9932cc",
382 | darkred: "#8b0000",
383 | darksalmon: "#e9967a",
384 | darkseagreen: "#8fbc8f",
385 | darkslateblue: "#483d8b",
386 | darkslategray: "#2f4f4f",
387 | darkslategrey: "#2f4f4f",
388 | darkturquoise: "#00ced1",
389 | darkviolet: "#9400d3",
390 | deeppink: "#ff1493",
391 | deepskyblue: "#00bfff",
392 | dimgray: "#696969",
393 | dimgrey: "#696969",
394 | dodgerblue: "#1e90ff",
395 | firebrick: "#b22222",
396 | floralwhite: "#fffaf0",
397 | forestgreen: "#228b22",
398 | fuchsia: "#ff00ff",
399 | gainsboro: "#dcdcdc",
400 | ghostwhite: "#f8f8ff",
401 | gold: "#ffd700",
402 | goldenrod: "#daa520",
403 | gray: "#808080",
404 | grey: "#808080",
405 | green: "#008000",
406 | greenyellow: "#adff2f",
407 | honeydew: "#f0fff0",
408 | hotpink: "#ff69b4",
409 | indianred: "#cd5c5c",
410 | indigo: "#4b0082",
411 | ivory: "#fffff0",
412 | khaki: "#f0e68c",
413 | lavender: "#e6e6fa",
414 | lavenderblush: "#fff0f5",
415 | lawngreen: "#7cfc00",
416 | lemonchiffon: "#fffacd",
417 | lightblue: "#add8e6",
418 | lightcoral: "#f08080",
419 | lightcyan: "#e0ffff",
420 | lightgoldenrodyellow: "#fafad2",
421 | lightgray: "#d3d3d3",
422 | lightgrey: "#d3d3d3",
423 | lightgreen: "#90ee90",
424 | lightpink: "#ffb6c1",
425 | lightsalmon: "#ffa07a",
426 | lightseagreen: "#20b2aa",
427 | lightskyblue: "#87cefa",
428 | lightslategray: "#778899",
429 | lightslategrey: "#778899",
430 | lightsteelblue: "#b0c4de",
431 | lightyellow: "#ffffe0",
432 | lime: "#00ff00",
433 | limegreen: "#32cd32",
434 | linen: "#faf0e6",
435 | magenta: "#ff00ff",
436 | maroon: "#800000",
437 | mediumaquamarine: "#66cdaa",
438 | mediumblue: "#0000cd",
439 | mediumorchid: "#ba55d3",
440 | mediumpurple: "#9370d8",
441 | mediumseagreen: "#3cb371",
442 | mediumslateblue: "#7b68ee",
443 | mediumspringgreen: "#00fa9a",
444 | mediumturquoise: "#48d1cc",
445 | mediumvioletred: "#c71585",
446 | midnightblue: "#191970",
447 | mintcream: "#f5fffa",
448 | mistyrose: "#ffe4e1",
449 | moccasin: "#ffe4b5",
450 | navajowhite: "#ffdead",
451 | navy: "#000080",
452 | oldlace: "#fdf5e6",
453 | olive: "#808000",
454 | olivedrab: "#6b8e23",
455 | orange: "#ffa500",
456 | orangered: "#ff4500",
457 | orchid: "#da70d6",
458 | palegoldenrod: "#eee8aa",
459 | palegreen: "#98fb98",
460 | paleturquoise: "#afeeee",
461 | palevioletred: "#d87093",
462 | papayawhip: "#ffefd5",
463 | peachpuff: "#ffdab9",
464 | peru: "#cd853f",
465 | pink: "#ffc0cb",
466 | plum: "#dda0dd",
467 | powderblue: "#b0e0e6",
468 | purple: "#800080",
469 | red: "#ff0000",
470 | rosybrown: "#bc8f8f",
471 | royalblue: "#4169e1",
472 | saddlebrown: "#8b4513",
473 | salmon: "#fa8072",
474 | sandybrown: "#f4a460",
475 | seagreen: "#2e8b57",
476 | seashell: "#fff5ee",
477 | sienna: "#a0522d",
478 | silver: "#c0c0c0",
479 | skyblue: "#87ceeb",
480 | slateblue: "#6a5acd",
481 | slategray: "#708090",
482 | slategrey: "#708090",
483 | snow: "#fffafa",
484 | springgreen: "#00ff7f",
485 | steelblue: "#4682b4",
486 | tan: "#d2b48c",
487 | teal: "#008080",
488 | thistle: "#d8bfd8",
489 | tomato: "#ff6347",
490 | turquoise: "#40e0d0",
491 | violet: "#ee82ee",
492 | wheat: "#f5deb3",
493 | white: "#ffffff",
494 | whitesmoke: "#f5f5f5",
495 | yellow: "#ffff00",
496 | yellowgreen: "#9acd32"
497 | };
498 |
499 | return {
500 | getColor: getColor
501 | };
502 | };
503 | },{}],3:[function(require,module,exports){
504 | module.exports = function() {
505 | return {
506 |
507 | getNumber: function(prop, t, def) {
508 | if(typeof(prop) === "number") {
509 | return prop;
510 | }
511 | else if(typeof(prop) === "function") {
512 | return prop(t);
513 | }
514 | else if(prop && prop.length === 2) {
515 | var start = prop[0],
516 | end = prop[1];
517 | return start + (end - start) * t;
518 | }
519 | else if(prop && prop.length) {
520 | return prop[Math.round(t * (prop.length - 1))];
521 | }
522 | return def;
523 | },
524 |
525 |
526 | getString: function(prop, t, def) {
527 | if(prop === undefined) {
528 | return def;
529 | }
530 | else if(typeof(prop) === "string") {
531 | return prop;
532 | }
533 | else if(typeof(prop) === "function") {
534 | return prop(t);
535 | }
536 | else if(prop && prop.length) {
537 | return prop[Math.round(t * (prop.length - 1))];
538 | }
539 | return prop;
540 | },
541 |
542 | getBool: function(prop, t, def) {
543 | if(prop === undefined) {
544 | return def;
545 | }
546 | else if(typeof(prop) === "function") {
547 | return prop(t);
548 | }
549 | else if(prop && prop.length) {
550 | return prop[Math.round(t * (prop.length - 1))];
551 | }
552 | return prop;
553 | },
554 |
555 | getArray: function(prop, t, def) {
556 | // string will have length, but is useless
557 | if(typeof(prop) === "string") {
558 | return def;
559 | }
560 | else if(typeof(prop) === "function") {
561 | return prop(t);
562 | }
563 | else if(prop && (prop.length == 2) && prop[0].length && prop[1].length) {
564 | // we seem to have an array of arrays
565 | var arr0 = prop[0],
566 | arr1 = prop[1],
567 | len = Math.min(arr0.length, arr1.length),
568 | result = [];
569 |
570 | for(var i = 0; i < len; i++) {
571 | var v0 = arr0[i],
572 | v1 = arr1[i];
573 | result.push(v0 + (v1 - v0) * t);
574 | }
575 | return result;
576 |
577 | }
578 | else if(prop && prop.length > 1) {
579 | return prop;
580 | }
581 | return def;
582 | },
583 |
584 | getObject: function(prop, t, def) {
585 | if(prop === undefined) {
586 | return def;
587 | }
588 | else if(typeof(prop) === "function") {
589 | return prop(t);
590 | }
591 | else if(prop && prop.length) {
592 | return prop[Math.round(t * (prop.length - 1))];
593 | }
594 | return prop;
595 | }
596 |
597 | }
598 | };
599 | },{}],4:[function(require,module,exports){
600 | var Model = require('./model');
601 | var RenderList = require('./renderList');
602 |
603 | var colorLib = require('./libs/color')();
604 | var shapeList = require('./shapeList');
605 |
606 | module.exports = function(canvasWrapper, renderCallback, completeCallback){
607 | var model = new Model();
608 | var renderList = new RenderList();
609 |
610 | function onRender(t) {
611 | if (typeof renderCallback === 'function'){
612 | renderCallback();
613 | }
614 | renderList.render(t);
615 | }
616 |
617 | function onComplete() {
618 | if (typeof completeCallback === 'function'){
619 | completeCallback();
620 | }
621 | }
622 |
623 | // Initialize render list
624 | renderList.init(model.w, model.h, model.styles, model.interpolation);
625 |
626 | // Add all shapes from the shape list
627 | for (var key in shapeList) {
628 | if (shapeList.hasOwnProperty(key)) {
629 | var shapeName = key;
630 | renderList['add' + shapeName[0].toUpperCase() + shapeName.slice(1)] = (function(shapeName, shapeList){
631 | return function(props){
632 | renderList.addShape(shapeList[shapeName], props);
633 | }
634 | })(shapeName, shapeList);
635 | }
636 | }
637 |
638 | // Initialize scheduler
639 | model.scheduler.init(onRender, onComplete);
640 | var canvasEl = canvasWrapper.appendChild(renderList.getCanvas());
641 |
642 | return {
643 | w: model.w,
644 | h: model.h,
645 | model : model,
646 | renderList: renderList,
647 | size: function(width, height) {
648 | this.w = model.w = width;
649 | this.h = model.h = height;
650 | renderList.size(model.w, model.h);
651 | },
652 | styles : model.styles,
653 | playOnce: function(){
654 | return model.playOnce();
655 | },
656 | loop: function() {
657 | return model.loop();
658 | },
659 | getDuration : function(){
660 | return model.getDuration();
661 | },
662 | setFPS: function(value){
663 | model.setFPS(value);
664 | },
665 | setDuration: function(value) {
666 | model.setDuration(value);
667 | },
668 | setMode: function(value) {
669 | model.interpolation.mode = value;
670 | },
671 | setEasing: function (value) {
672 | model.interpolation.easing = value;
673 | },
674 | setMaxColors: function(value) {
675 | model.maxColors = value;
676 | },
677 | color: colorLib,
678 | canvasEl : canvasEl
679 | };
680 | };
681 | },{"./libs/color":1,"./model":5,"./renderList":6,"./shapeList":9}],5:[function(require,module,exports){
682 | /**
683 | * A simple model for the Canvas renderer
684 | * @type {{init, loop, playOnce, stop, isRunning, setDuration, getDuration, setFPS, getFPS}|*}
685 | */
686 | var Scheduler = require('./scheduler');
687 |
688 | module.exports = function(){
689 | var scheduler = new Scheduler();
690 |
691 | var styles = {
692 | backgroundColor: "#ffffff",
693 | lineWidth: 5,
694 | strokeStyle: "#000000",
695 | fillStyle: "#000000",
696 | lineCap: "round",
697 | lineJoin: "miter",
698 | lineDash: [],
699 | miterLimit: 10,
700 | shadowColor: null,
701 | shadowOffsetX: 0,
702 | shadowOffsetY: 0,
703 | shadowBlur: 0,
704 | globalAlpha: 1,
705 | translationX: 0,
706 | translationY: 0,
707 | shake: 0,
708 | blendMode: "source-over"
709 | };
710 |
711 | return {
712 | interpolation: {
713 | mode: "bounce",
714 | easing: true
715 | },
716 | maxColors: 256,
717 | w: 400,
718 | h: 400,
719 | capture: false,
720 | styles: styles,
721 | scheduler : scheduler,
722 | playOnce : function() {
723 | return scheduler.playOnce();
724 | },
725 | loop : function() {
726 | return scheduler.loop();
727 | },
728 | getDuration: function() {
729 | return scheduler.getDuration();
730 | },
731 | setDuration: function(value) {
732 | scheduler.setDuration(value);
733 | },
734 | getFPS: function() {
735 | return scheduler.getFPS();
736 | },
737 | setFPS: function(value) {
738 | scheduler.setFPS(value);
739 | },
740 | getIsRunning: function() {
741 | return scheduler.isRunning();
742 | }
743 | }
744 | };
745 | },{"./scheduler":7}],6:[function(require,module,exports){
746 | var Shape = require('./shape');
747 |
748 | module.exports = function(){
749 | var shape = new Shape();
750 |
751 | var canvas = null,
752 | context = null,
753 | width = 0,
754 | height = 0,
755 | list = [],
756 | styles = null;
757 |
758 | function init(w, h, stylesValue, interpolation) {
759 | canvas = document.createElement("canvas");
760 | width = canvas.width = w;
761 | height = canvas.height = h;
762 | context = canvas.getContext("2d");
763 | styles = stylesValue;
764 | shape.styles = styles;
765 | shape.interpolation = interpolation;
766 | }
767 |
768 | function size(w, h) {
769 | width = canvas.width = w;
770 | height = canvas.height = h;
771 | }
772 |
773 | function addShape(newShape, props) {
774 | var item = shape.create(newShape, props);
775 | list.push(item);
776 | render(0);
777 | }
778 |
779 | function clear() {
780 | list.length = 0;
781 | }
782 |
783 | function render(t) {
784 | if(styles.backgroundColor === "transparent") {
785 | context.clearRect(0, 0, width, height);
786 | }
787 | else {
788 | context.fillStyle = styles.backgroundColor;
789 | context.fillRect(0, 0, width, height);
790 | }
791 | for(var i = 0; i < list.length; i++) {
792 | list[i].render(context, t);
793 | }
794 | }
795 |
796 | function getCanvas() {
797 | return canvas;
798 | }
799 |
800 | function getContext() {
801 | return context;
802 | }
803 |
804 | return {
805 | init: init,
806 | size: size,
807 | getCanvas: getCanvas,
808 | getContext: getContext,
809 | addShape: addShape,
810 | clear: clear,
811 | render: render
812 | };
813 | };
814 | },{"./shape":8}],7:[function(require,module,exports){
815 | module.exports = function() {
816 | var t = 0,
817 | duration = 2,
818 | fps = 30,
819 | running = false,
820 | looping = false,
821 | renderCallback = null,
822 | completeCallback = null;
823 |
824 | function init(onRender, onComplete) {
825 | renderCallback = onRender;
826 | completeCallback = onComplete;
827 | }
828 |
829 | function render() {
830 | if(running) {
831 | if(renderCallback) {
832 | renderCallback(t);
833 | }
834 | advance();
835 | setTimeout(onTimeout, 1000 / fps);
836 | }
837 | else if(completeCallback) {
838 | completeCallback();
839 | }
840 | }
841 |
842 | function onTimeout() {
843 | requestAnimationFrame(render);
844 | }
845 |
846 | function advance() {
847 | var numFrames = duration * fps,
848 | speed = 1 / numFrames;
849 | t += speed;
850 | if(Math.round(t * 10000) / 10000 >= 1) {
851 | if(looping) {
852 | t -= 1;
853 | }
854 | else {
855 | t = 0;
856 | stop();
857 | }
858 | }
859 | }
860 |
861 | function loop() {
862 | if(!running) {
863 | t = 0;
864 | looping = true;
865 | running = true;
866 | render();
867 | }
868 | }
869 |
870 | function stop() {
871 | running = false;
872 | looping = false;
873 | t = 0;
874 | }
875 |
876 | function playOnce() {
877 | if(!running) {
878 | t = 0;
879 | looping = false;
880 | running = true;
881 | render();
882 | }
883 | }
884 |
885 | function isRunning() {
886 | return running;
887 | }
888 |
889 | function setDuration(value) {
890 | duration = value;
891 | return duration;
892 | }
893 |
894 | function getDuration() {
895 | return duration;
896 | }
897 |
898 | function setFPS(value) {
899 | fps = value;
900 | return fps;
901 | }
902 |
903 | function getFPS() {
904 | return fps;
905 | }
906 |
907 | return {
908 | init: init,
909 | loop: loop,
910 | playOnce: playOnce,
911 | stop: stop,
912 | isRunning: isRunning,
913 | setDuration: setDuration,
914 | getDuration: getDuration,
915 | setFPS: setFPS,
916 | getFPS: getFPS
917 | };
918 | };
919 | },{}],8:[function(require,module,exports){
920 | /**
921 | * Shape prototype object
922 | * @type {{getNumber, getString, getBool, getArray, getObject}|*}
923 | */
924 | var valueParser = require('./libs/valueParser')();
925 | var colorParser = require('./libs/colorParser')();
926 |
927 | module.exports = function() {
928 |
929 | return {
930 | styles: null,
931 | interpolation: null,
932 |
933 | create: function (type, props) {
934 | var obj = Object.create(this);
935 | obj.init(type, props || {});
936 | return obj;
937 | },
938 |
939 | init: function (type, props) {
940 | this.props = props;
941 | for(var prop in props) {
942 | var p = props[prop];
943 | if(typeof p === "function") {
944 | props[prop] = p.bind(props);
945 | }
946 | }
947 | this.draw = type.draw;
948 | },
949 |
950 | render: function (context, time) {
951 | var t = this.interpolate(time);
952 |
953 | this.startDraw(context, t);
954 | this.draw(context, t);
955 | this.endDraw(context, t);
956 | },
957 |
958 | interpolate: function (t) {
959 | t *= this.props.speedMult || 1;
960 | t += this.props.phase || 0;
961 |
962 | switch (this.interpolation.mode) {
963 | case "bounce":
964 | if (this.interpolation.easing) {
965 | var a = t * Math.PI * 2;
966 | return 0.5 - Math.cos(a) * 0.5;
967 | }
968 | else {
969 | t = t % 1;
970 | return t < 0.5 ? t * 2 : t = (1 - t) * 2;
971 | }
972 | break;
973 |
974 | case "single":
975 | if (t > 1) {
976 | t %= 1;
977 | }
978 | if (this.interpolation.easing) {
979 | var a = t * Math.PI;
980 | return 0.5 - Math.cos(a) * 0.5;
981 | }
982 | else {
983 | return t;
984 | }
985 | }
986 |
987 | },
988 |
989 | startDraw: function (context, t) {
990 | context.save();
991 | context.lineWidth = this.getNumber("lineWidth", t, this.styles.lineWidth);
992 | context.strokeStyle = this.getColor("strokeStyle", t, this.styles.strokeStyle);
993 | context.fillStyle = this.getColor("fillStyle", t, this.styles.fillStyle);
994 | context.lineCap = this.getString("lineCap", t, this.styles.lineCap);
995 | context.lineJoin = this.getString("lineJoin", t, this.styles.lineJoin);
996 | context.miterLimit = this.getString("miterLimit", t, this.styles.miterLimit);
997 | context.globalAlpha = this.getNumber("globalAlpha", t, this.styles.globalAlpha);
998 | context.translate(this.getNumber("translationX", t, this.styles.translationX), this.getNumber("translationY", t, this.styles.translationY));
999 | context.globalCompositeOperation = this.getString("blendMode", t, this.styles.blendMode);
1000 | var shake = this.getNumber("shake", t, this.styles.shake);
1001 | context.translate(Math.random() * shake - shake / 2, Math.random() * shake - shake / 2);
1002 |
1003 | var lineDash = this.getArray("lineDash", t, this.styles.lineDash);
1004 | if (lineDash) {
1005 | context.setLineDash(lineDash);
1006 | }
1007 | context.beginPath();
1008 | },
1009 |
1010 | drawFillAndStroke: function (context, t, doFill, doStroke) {
1011 | var fill = this.getBool("fill", t, doFill),
1012 | stroke = this.getBool("stroke", t, doStroke);
1013 |
1014 | context.save();
1015 | if (fill) {
1016 | this.setShadowParams(context, t);
1017 | context.fill();
1018 | }
1019 | context.restore();
1020 | if (stroke) {
1021 | if (!fill) {
1022 | this.setShadowParams(context, t);
1023 | }
1024 | context.stroke();
1025 | }
1026 | },
1027 |
1028 | setShadowParams: function (context, t) {
1029 | context.shadowColor = this.getColor("shadowColor", t, this.styles.shadowColor);
1030 | context.shadowOffsetX = this.getNumber("shadowOffsetX", t, this.styles.shadowOffsetX);
1031 | context.shadowOffsetY = this.getNumber("shadowOffsetY", t, this.styles.shadowOffsetY);
1032 | context.shadowBlur = this.getNumber("shadowBlur", t, this.styles.shadowBlur);
1033 | },
1034 |
1035 | endDraw: function (context) {
1036 | context.restore();
1037 | },
1038 |
1039 | getNumber: function (prop, t, def) {
1040 | return valueParser.getNumber(this.props[prop], t, def);
1041 | },
1042 |
1043 | getColor: function (prop, t, def) {
1044 | return colorParser.getColor(this.props[prop], t, def);
1045 | },
1046 |
1047 | getString: function (prop, t, def) {
1048 | return valueParser.getString(this.props[prop], t, def);
1049 | },
1050 |
1051 | getBool: function (prop, t, def) {
1052 | return valueParser.getBool(this.props[prop], t, def);
1053 | },
1054 |
1055 | getArray: function (prop, t, def) {
1056 | return valueParser.getArray(this.props[prop], t, def);
1057 | },
1058 |
1059 | getObject: function (prop, t, def) {
1060 | return valueParser.getObject(this.props[prop], t, def);
1061 | },
1062 |
1063 | getPosition: function (prop, t, def) {
1064 | return valueParser.getPosition(this.props[prop], t, def);
1065 | }
1066 | }
1067 | };
1068 |
1069 | },{"./libs/colorParser":2,"./libs/valueParser":3}],9:[function(require,module,exports){
1070 | /**
1071 | * Include all default shapes
1072 | * @type {{draw}|*}
1073 | */
1074 | var circle = require('./shapes/circle')();
1075 | var heart = require('./shapes/heart')();
1076 | var arrow = require('./shapes/arrow')();
1077 | var arcSegment = require('./shapes/arcSegment')();
1078 | var bezierCurve = require('./shapes/bezierCurve')();
1079 | var bezierSegment = require('./shapes/bezierSegment')();
1080 | var cube = require('./shapes/cube')();
1081 | var curve = require('./shapes/curve')();
1082 | var curveSegment = require('./shapes/curveSegment')();
1083 | var gear = require('./shapes/gear')();
1084 | var line = require('./shapes/line')();
1085 | var oval = require('./shapes/oval')();
1086 | var path = require('./shapes/path')();
1087 | var poly = require('./shapes/poly')();
1088 | var raySegment = require('./shapes/raySegment')();
1089 | var rect = require('./shapes/rect')();
1090 | var segment = require('./shapes/segment')();
1091 | var spiral = require('./shapes/spiral')();
1092 | var star = require('./shapes/star')();
1093 | var text = require('./shapes/text')();
1094 |
1095 | module.exports = {
1096 | circle : circle,
1097 | arrow : arrow,
1098 | arcSegment: arcSegment,
1099 | bezierCurve : bezierCurve,
1100 | bezierSegment : bezierSegment,
1101 | cube : cube,
1102 | curve : curve,
1103 | curveSegment : curveSegment,
1104 | gear : gear,
1105 | line : line,
1106 | oval : oval,
1107 | path: path,
1108 | poly : poly,
1109 | raySegment : raySegment,
1110 | rect : rect,
1111 | segment : segment,
1112 | spiral : spiral,
1113 | star : star,
1114 | text : text,
1115 | heart : heart
1116 | };
1117 | },{"./shapes/arcSegment":10,"./shapes/arrow":11,"./shapes/bezierCurve":12,"./shapes/bezierSegment":13,"./shapes/circle":14,"./shapes/cube":15,"./shapes/curve":16,"./shapes/curveSegment":17,"./shapes/gear":18,"./shapes/heart":19,"./shapes/line":20,"./shapes/oval":21,"./shapes/path":22,"./shapes/poly":23,"./shapes/raySegment":24,"./shapes/rect":25,"./shapes/segment":26,"./shapes/spiral":27,"./shapes/star":28,"./shapes/text":29}],10:[function(require,module,exports){
1118 | module.exports = function(){
1119 | return {
1120 | draw: function(context, t) {
1121 | var x = this.getNumber("x", t, 100),
1122 | y = this.getNumber("y", t, 100),
1123 | radius = this.getNumber("radius", t, 50),
1124 | startAngle = this.getNumber("startAngle", t, 0),
1125 | endAngle = this.getNumber("endAngle", t, 360);
1126 |
1127 | if(startAngle > endAngle) {
1128 | var temp = startAngle;
1129 | startAngle = endAngle;
1130 | endAngle = temp;
1131 | }
1132 | var arc = this.getNumber("arc", t, 20),
1133 | start = startAngle - 1,
1134 | end = startAngle + t * (endAngle - startAngle + arc);
1135 |
1136 | if(end > startAngle + arc) {
1137 | start = end - arc;
1138 | }
1139 | if(end > endAngle) {
1140 | end = endAngle + 1;
1141 | }
1142 |
1143 | context.translate(x, y);
1144 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1145 | context.arc(0, 0, radius, start * Math.PI / 180, end * Math.PI / 180);
1146 |
1147 | this.drawFillAndStroke(context, t, false, true);
1148 | }
1149 | }
1150 | };
1151 |
1152 | },{}],11:[function(require,module,exports){
1153 | module.exports = function(){
1154 | return {
1155 | draw: function(context, t) {
1156 | var x = this.getNumber("x", t, 100),
1157 | y = this.getNumber("y", t, 100),
1158 | w = this.getNumber("w", t, 100),
1159 | h = this.getNumber("h", t, 100),
1160 | pointPercent = this.getNumber("pointPercent", t, 0.5),
1161 | shaftPercent = this.getNumber("shaftPercent", t, 0.5);
1162 |
1163 | context.translate(x, y);
1164 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1165 |
1166 | // context.translate(-w / 2, 0);
1167 |
1168 | context.moveTo(-w / 2, -h * shaftPercent * 0.5);
1169 | context.lineTo(w / 2 - w * pointPercent, -h * shaftPercent * 0.5);
1170 | context.lineTo(w / 2 - w * pointPercent, -h * 0.5);
1171 | context.lineTo(w / 2, 0);
1172 | context.lineTo(w / 2 - w * pointPercent, h * 0.5);
1173 | context.lineTo(w / 2 - w * pointPercent, h * shaftPercent * 0.5);
1174 | context.lineTo(-w / 2, h * shaftPercent * 0.5);
1175 | context.lineTo(-w / 2, -h * shaftPercent * 0.5);
1176 |
1177 | this.drawFillAndStroke(context, t, true, false);
1178 | }
1179 | }
1180 | };
1181 | },{}],12:[function(require,module,exports){
1182 | module.exports = function(){
1183 |
1184 | return {
1185 | draw: function(context, t) {
1186 | var x0 = this.getNumber("x0", t, 50),
1187 | y0 = this.getNumber("y0", t, 10),
1188 | x1 = this.getNumber("x1", t, 200),
1189 | y1 = this.getNumber("y1", t, 100),
1190 | x2 = this.getNumber("x2", t, 0),
1191 | y2 = this.getNumber("y2", t, 100),
1192 | x3 = this.getNumber("x3", t, 150),
1193 | y3 = this.getNumber("y3", t, 10);
1194 |
1195 | context.moveTo(x0, y0);
1196 | context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
1197 |
1198 | this.drawFillAndStroke(context, t, false, true);
1199 | }
1200 | }
1201 | };
1202 |
1203 | },{}],13:[function(require,module,exports){
1204 | module.exports = function(){
1205 |
1206 | function bezier(t, v0, v1, v2, v3) {
1207 | return (1 - t) * (1 - t) * (1 - t) * v0 + 3 * (1 - t) * (1 - t) * t * v1 + 3 * (1 - t) * t * t * v2 + t * t * t * v3;
1208 | }
1209 |
1210 | return {
1211 | draw: function(context, t) {
1212 | var x0 = this.getNumber("x0", t, 50),
1213 | y0 = this.getNumber("y0", t, 10),
1214 | x1 = this.getNumber("x1", t, 200),
1215 | y1 = this.getNumber("y1", t, 100),
1216 | x2 = this.getNumber("x2", t, 0),
1217 | y2 = this.getNumber("y2", t, 100),
1218 | x3 = this.getNumber("x3", t, 150),
1219 | y3 = this.getNumber("y3", t, 10),
1220 | percent = this.getNumber("percent", t, 0.1),
1221 | t1 = t * (1 + percent),
1222 | t0 = t1 - percent,
1223 | res = 0.01,
1224 | x,
1225 | y;
1226 |
1227 | t1 = Math.min(t1, 1.001);
1228 | t0 = Math.max(t0, -0.001);
1229 |
1230 | for(var i = t0; i < t1; i += res) {
1231 | x = bezier(i, x0, x1, x2, x3);
1232 | y = bezier(i, y0, y1, y2, y3);
1233 | if(i === t0) {
1234 | context.moveTo(x, y);
1235 | }
1236 | else {
1237 | context.lineTo(x, y);
1238 | }
1239 | }
1240 | x = bezier(t1, x0, x1, x2, x3);
1241 | y = bezier(t1, y0, y1, y2, y3);
1242 | context.lineTo(x, y);
1243 |
1244 | this.drawFillAndStroke(context, t, false, true);
1245 | }
1246 | }
1247 | };
1248 |
1249 | },{}],14:[function(require,module,exports){
1250 | module.exports = function(){
1251 | return {
1252 | draw: function(context, t) {
1253 | var x = this.getNumber("x", t, 100),
1254 | y = this.getNumber("y", t, 100),
1255 | radius = this.getNumber("radius", t, 50),
1256 | startAngle = this.getNumber("startAngle", t, 0),
1257 | endAngle = this.getNumber("endAngle", t, 360),
1258 | drawFromCenter = this.getBool("drawFromCenter", t, false);
1259 |
1260 | context.translate(x, y);
1261 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1262 | if(drawFromCenter) {
1263 | context.moveTo(0, 0);
1264 | }
1265 | context.arc(0, 0, radius, startAngle * Math.PI / 180, endAngle * Math.PI / 180);
1266 | if(drawFromCenter) {
1267 | context.closePath();
1268 | }
1269 |
1270 | this.drawFillAndStroke(context, t, true, false);
1271 | }
1272 | }
1273 | };
1274 | },{}],15:[function(require,module,exports){
1275 | module.exports = function(){
1276 |
1277 | return {
1278 | draw: function(context, t) {
1279 | var x = this.getNumber("x", t, 100),
1280 | y = this.getNumber("y", t, 100),
1281 | z = this.getNumber("z", t, 0),
1282 | size = this.getNumber("size", t, 100),
1283 | rotationX = this.getNumber("rotationX", t, 0) * Math.PI / 180,
1284 | rotationY = this.getNumber("rotationY", t, 0) * Math.PI / 180,
1285 | rotationZ = this.getNumber("rotationZ", t, 0) * Math.PI / 180;
1286 |
1287 | var points = makePoints();
1288 | scale(points, size / 2);
1289 | rotateX(points, rotationX);
1290 | rotateY(points, rotationY);
1291 | rotateZ(points, rotationZ);
1292 | project(points, z);
1293 |
1294 | context.lineJoin = this.getString("lineJoin", t, "round");
1295 | context.lineWidth = this.getNumber("lineWidth", t, 1);
1296 |
1297 | context.translate(x, y);
1298 |
1299 | context.moveTo(points[0].sx, points[0].sy);
1300 | context.lineTo(points[1].sx, points[1].sy);
1301 | context.lineTo(points[2].sx, points[2].sy);
1302 | context.lineTo(points[3].sx, points[3].sy);
1303 | context.lineTo(points[0].sx, points[0].sy);
1304 |
1305 | context.moveTo(points[4].sx, points[4].sy);
1306 | context.lineTo(points[5].sx, points[5].sy);
1307 | context.lineTo(points[6].sx, points[6].sy);
1308 | context.lineTo(points[7].sx, points[7].sy);
1309 | context.lineTo(points[4].sx, points[4].sy);
1310 |
1311 | context.moveTo(points[0].sx, points[0].sy);
1312 | context.lineTo(points[4].sx, points[4].sy);
1313 |
1314 | context.moveTo(points[1].sx, points[1].sy);
1315 | context.lineTo(points[5].sx, points[5].sy);
1316 |
1317 | context.moveTo(points[2].sx, points[2].sy);
1318 | context.lineTo(points[6].sx, points[6].sy);
1319 |
1320 | context.moveTo(points[3].sx, points[3].sy);
1321 | context.lineTo(points[7].sx, points[7].sy);
1322 |
1323 | this.setShadowParams(context, t);
1324 | context.stroke();
1325 | }
1326 | }
1327 |
1328 | function scale(points, size) {
1329 | for(var i = 0; i < points.length; i++) {
1330 | var p = points[i];
1331 | p.x *= size;
1332 | p.y *= size;
1333 | p.z *= size;
1334 | }
1335 | }
1336 |
1337 | function rotateX(points, angle) {
1338 | var cos = Math.cos(angle),
1339 | sin = Math.sin(angle);
1340 | for(var i = 0; i < points.length; i++) {
1341 | var p = points[i],
1342 | y = p.y * cos - p.z * sin,
1343 | z = p.z * cos + p.y * sin;
1344 | p.y = y;
1345 | p.z = z;
1346 | }
1347 | }
1348 |
1349 | function rotateY(points, angle) {
1350 | var cos = Math.cos(angle),
1351 | sin = Math.sin(angle);
1352 | for(var i = 0; i < points.length; i++) {
1353 | var p = points[i],
1354 | x = p.x * cos - p.z * sin,
1355 | z = p.z * cos + p.x * sin;
1356 | p.x = x;
1357 | p.z = z;
1358 | }
1359 | }
1360 |
1361 | function rotateZ(points, angle) {
1362 | var cos = Math.cos(angle),
1363 | sin = Math.sin(angle);
1364 | for(var i = 0; i < points.length; i++) {
1365 | var p = points[i],
1366 | x = p.x * cos - p.y * sin,
1367 | y = p.y * cos + p.x * sin;
1368 | p.x = x;
1369 | p.y = y;
1370 | }
1371 | }
1372 |
1373 | function project(points, z) {
1374 | var fl = 300;
1375 | for(var i = 0; i < points.length; i++) {
1376 | var p = points[i],
1377 | scale = fl / (fl + p.z + z);
1378 | p.sx = p.x * scale;
1379 | p.sy = p.y * scale;
1380 | }
1381 | }
1382 |
1383 | function makePoints() {
1384 | return [
1385 | {
1386 | x: -1,
1387 | y: -1,
1388 | z: -1
1389 | },
1390 | {
1391 | x: 1,
1392 | y: -1,
1393 | z: -1
1394 | },
1395 | {
1396 | x: 1,
1397 | y: 1,
1398 | z: -1
1399 | },
1400 | {
1401 | x: -1,
1402 | y: 1,
1403 | z: -1
1404 | },
1405 | {
1406 | x: -1,
1407 | y: -1,
1408 | z: 1
1409 | },
1410 | {
1411 | x: 1,
1412 | y: -1,
1413 | z: 1
1414 | },
1415 | {
1416 | x: 1,
1417 | y: 1,
1418 | z: 1
1419 | },
1420 | {
1421 | x: -1,
1422 | y: 1,
1423 | z: 1
1424 | }
1425 | ];
1426 | }
1427 | };
1428 |
1429 | },{}],16:[function(require,module,exports){
1430 | module.exports = function(){
1431 |
1432 | return {
1433 | draw: function(context, t) {
1434 | var x0 = this.getNumber("x0", t, 20),
1435 | y0 = this.getNumber("y0", t, 10),
1436 | x1 = this.getNumber("x1", t, 100),
1437 | y1 = this.getNumber("y1", t, 200),
1438 | x2 = this.getNumber("x2", t, 180),
1439 | y2 = this.getNumber("y2", t, 10);
1440 |
1441 | context.moveTo(x0, y0);
1442 | context.quadraticCurveTo(x1, y1, x2, y2);
1443 |
1444 | this.drawFillAndStroke(context, t, false, true);
1445 | }
1446 | }
1447 | };
1448 |
1449 | },{}],17:[function(require,module,exports){
1450 | module.exports = function(){
1451 |
1452 | function quadratic(t, v0, v1, v2) {
1453 | return (1 - t) * (1 - t) * v0 + 2 * (1 - t) * t * v1 + t * t * v2;
1454 | }
1455 |
1456 | return {
1457 | draw: function(context, t) {
1458 | var x0 = this.getNumber("x0", t, 20),
1459 | y0 = this.getNumber("y0", t, 20),
1460 | x1 = this.getNumber("x1", t, 100),
1461 | y1 = this.getNumber("y1", t, 200),
1462 | x2 = this.getNumber("x2", t, 180),
1463 | y2 = this.getNumber("y2", t, 20),
1464 | percent = this.getNumber("percent", t, 0.1),
1465 | t1 = t * (1 + percent),
1466 | t0 = t1 - percent,
1467 | res = 0.01,
1468 | x,
1469 | y;
1470 |
1471 | t1 = Math.min(t1, 1);
1472 | t0 = Math.max(t0, 0);
1473 |
1474 | for(var i = t0; i < t1; i += res) {
1475 | x = quadratic(i, x0, x1, x2);
1476 | y = quadratic(i, y0, y1, y2);
1477 | if(i === t0) {
1478 | context.moveTo(x, y);
1479 | }
1480 | else {
1481 | context.lineTo(x, y);
1482 | }
1483 | }
1484 | x = quadratic(t1, x0, x1, x2);
1485 | y = quadratic(t1, y0, y1, y2);
1486 | context.lineTo(x, y);
1487 |
1488 | this.drawFillAndStroke(context, t, false, true); }
1489 | }
1490 | };
1491 |
1492 | },{}],18:[function(require,module,exports){
1493 | module.exports = function(){
1494 |
1495 | return {
1496 | draw: function(context, t) {
1497 | var x = this.getNumber("x", t, 100),
1498 | y = this.getNumber("y", t, 100),
1499 | radius = this.getNumber("radius", t, 50),
1500 | toothHeight = this.getNumber("toothHeight", t, 10),
1501 | hub = this.getNumber("hub", t, 10),
1502 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
1503 | teeth = this.getNumber("teeth", t, 10),
1504 | toothAngle = this.getNumber("toothAngle", t, 0.3),
1505 | face = 0.5 - toothAngle / 2,
1506 | side = 0.5 - face,
1507 | innerRadius = radius - toothHeight;
1508 |
1509 | context.translate(x, y);
1510 | context.rotate(rotation);
1511 | context.save();
1512 | context.moveTo(radius, 0);
1513 | var angle = Math.PI * 2 / teeth;
1514 |
1515 | for(var i = 0; i < teeth; i++) {
1516 | context.rotate(angle * face);
1517 | context.lineTo(radius, 0);
1518 | context.rotate(angle * side);
1519 | context.lineTo(innerRadius, 0);
1520 | context.rotate(angle * face);
1521 | context.lineTo(innerRadius, 0);
1522 | context.rotate(angle * side);
1523 | context.lineTo(radius, 0);
1524 | }
1525 | context.lineTo(radius, 0);
1526 | context.restore();
1527 |
1528 | context.moveTo(hub, 0);
1529 | context.arc(0, 0, hub, 0, Math.PI * 2, true);
1530 |
1531 | this.drawFillAndStroke(context, t, true, false);
1532 | }
1533 | }
1534 | };
1535 |
1536 | },{}],19:[function(require,module,exports){
1537 | module.exports = function() {
1538 | return {
1539 | draw: function(context, t) {
1540 | var x = this.getNumber("x", t, 100),
1541 | y = this.getNumber("y", t, 100),
1542 | w = this.getNumber("w", t, 50),
1543 | h = this.getNumber("h", t, 50);
1544 |
1545 | var x0 = 0,
1546 | y0 = -.25,
1547 | x1 = .2,
1548 | y1 = -.8,
1549 | x2 = 1.1,
1550 | y2 = -.2,
1551 | x3 = 0,
1552 | y3 = .5;
1553 |
1554 | context.save();
1555 | context.translate(x, y);
1556 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1557 | context.save();
1558 | context.scale(w, h);
1559 | context.moveTo(x0, y0);
1560 | context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
1561 | context.bezierCurveTo(-x2, y2, -x1, y1, -x0, y0);
1562 | context.restore();
1563 | this.drawFillAndStroke(context, t, true, false);
1564 | context.restore();
1565 | }
1566 | }
1567 | };
1568 | },{}],20:[function(require,module,exports){
1569 | module.exports = function(){
1570 |
1571 | return {
1572 | draw: function(context, t) {
1573 | var x0 = this.getNumber("x0", t, 0),
1574 | y0 = this.getNumber("y0", t, 0),
1575 | x1 = this.getNumber("x1", t, 100),
1576 | y1 = this.getNumber("y1", t, 100);
1577 |
1578 | context.moveTo(x0, y0);
1579 | context.lineTo(x1, y1);
1580 |
1581 | this.drawFillAndStroke(context, t, false, true);
1582 | }
1583 | }
1584 | };
1585 |
1586 | },{}],21:[function(require,module,exports){
1587 | module.exports = function(){
1588 |
1589 | return {
1590 | draw: function(context, t) {
1591 | var x = this.getNumber("x", t, 100),
1592 | y = this.getNumber("y", t, 100),
1593 | rx = this.getNumber("rx", t, 50),
1594 | ry = this.getNumber("ry", t, 50),
1595 | startAngle = this.getNumber("startAngle", t, 0),
1596 | endAngle = this.getNumber("endAngle", t, 360),
1597 | drawFromCenter = this.getBool("drawFromCenter", t, false);
1598 |
1599 | context.translate(x, y);
1600 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1601 | context.save();
1602 | context.scale(rx / 100, ry / 100);
1603 | if(drawFromCenter) {
1604 | context.moveTo(0, 0);
1605 | }
1606 | context.arc(0, 0, 100, startAngle * Math.PI / 180, endAngle * Math.PI / 180);
1607 | if(drawFromCenter) {
1608 | context.closePath();
1609 | }
1610 | context.restore();
1611 |
1612 | this.drawFillAndStroke(context, t, true, false);
1613 | }
1614 | }
1615 | };
1616 |
1617 | },{}],22:[function(require,module,exports){
1618 | module.exports = function(){
1619 |
1620 | return {
1621 | draw: function(context, t) {
1622 | var path = this.getArray("path", t, []),
1623 | startPercent = this.getNumber("startPercent", t, 0),
1624 | endPercent = this.getNumber("endPercent", t, 1),
1625 | startPoint = Math.floor(path.length / 2 * startPercent),
1626 | endPoint = Math.floor(path.length / 2 * endPercent),
1627 | startIndex = startPoint * 2,
1628 | endIndex = endPoint * 2;
1629 |
1630 | if(startIndex > endIndex) {
1631 | var temp = startIndex;
1632 | startIndex = endIndex;
1633 | endIndex = temp;
1634 | }
1635 |
1636 | context.moveTo(path[startIndex], path[startIndex + 1]);
1637 |
1638 | for(var i = startIndex + 2; i < endIndex - 1; i += 2) {
1639 | context.lineTo(path[i], path[i + 1]);
1640 | }
1641 |
1642 | this.drawFillAndStroke(context, t, false, true); }
1643 | }
1644 | };
1645 |
1646 | },{}],23:[function(require,module,exports){
1647 | module.exports = function(){
1648 |
1649 | return {
1650 | draw: function(context, t) {
1651 | var x = this.getNumber("x", t, 100),
1652 | y = this.getNumber("y", t, 100),
1653 | radius = this.getNumber("radius", t, 50),
1654 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
1655 | sides = this.getNumber("sides", t, 5);
1656 |
1657 | context.translate(x, y);
1658 | context.rotate(rotation);
1659 | context.moveTo(radius, 0);
1660 | for(var i = 1; i < sides; i++) {
1661 | var angle = Math.PI * 2 / sides * i;
1662 | context.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
1663 | }
1664 | context.lineTo(radius, 0);
1665 |
1666 | this.drawFillAndStroke(context, t, true, false);
1667 | }
1668 | }
1669 | };
1670 |
1671 |
1672 | },{}],24:[function(require,module,exports){
1673 | module.exports = function(){
1674 |
1675 | return {
1676 | draw: function(context, t) {
1677 | var x = this.getNumber("x", t, 100),
1678 | y = this.getNumber("y", t, 100),
1679 | angle = this.getNumber("angle", t, 0) * Math.PI / 180,
1680 | length = this.getNumber("length", t, 100),
1681 | segmentLength = this.getNumber("segmentLength", t, 50),
1682 | start = -0.01,
1683 | end = (length + segmentLength) * t;
1684 |
1685 | if(end > segmentLength) {
1686 | start = end - segmentLength;
1687 | }
1688 | if(end > length) {
1689 | end = length + 0.01;
1690 | }
1691 |
1692 | context.translate(x, y);
1693 | context.rotate(angle);
1694 | context.moveTo(start, 0);
1695 | context.lineTo(end, 0);
1696 |
1697 | this.drawFillAndStroke(context, t, false, true);
1698 | }
1699 | }
1700 | };
1701 |
1702 | },{}],25:[function(require,module,exports){
1703 | module.exports = function(){
1704 |
1705 | return {
1706 | draw: function(context, t) {
1707 | var x = this.getNumber("x", t, 100),
1708 | y = this.getNumber("y", t, 100),
1709 | w = this.getNumber("w", t, 100),
1710 | h = this.getNumber("h", t, 100);
1711 |
1712 | context.translate(x, y);
1713 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1714 | if(this.getBool("drawFromCenter", t, true)) {
1715 | context.rect(-w * 0.5, -h * 0.5, w, h);
1716 | }
1717 | else {
1718 | context.rect(0, 0, w, h);
1719 | }
1720 |
1721 | this.drawFillAndStroke(context, t, true, false);
1722 | }
1723 | }
1724 | };
1725 |
1726 | },{}],26:[function(require,module,exports){
1727 | module.exports = function(){
1728 |
1729 | return {
1730 | draw: function(context, t) {
1731 | var x0 = this.getNumber("x0", t, 0),
1732 | y0 = this.getNumber("y0", t, 0),
1733 | x1 = this.getNumber("x1", t, 100),
1734 | y1 = this.getNumber("y1", t, 100),
1735 | segmentLength = this.getNumber("segmentLength", t, 50),
1736 | dx = x1 - x0,
1737 | dy = y1 - y0,
1738 | angle = Math.atan2(dy, dx),
1739 | dist = Math.sqrt(dx * dx + dy * dy),
1740 | start = -0.01,
1741 | end = (dist + segmentLength) * t;
1742 |
1743 | if(end > segmentLength) {
1744 | start = end - segmentLength;
1745 | }
1746 | if(end > dist) {
1747 | end = dist + 0.01;
1748 | }
1749 |
1750 | context.translate(x0, y0);
1751 | context.rotate(angle);
1752 | context.moveTo(start, 0);
1753 | context.lineTo(end, 0);
1754 |
1755 | this.drawFillAndStroke(context, t, false, true);
1756 | }
1757 | }
1758 | };
1759 |
1760 | },{}],27:[function(require,module,exports){
1761 | module.exports = function(){
1762 |
1763 | return {
1764 | draw: function(context, t) {
1765 | var x = this.getNumber("x", t, "100"),
1766 | y = this.getNumber("y", t, "100"),
1767 | innerRadius = this.getNumber("innerRadius", t, 10),
1768 | outerRadius = this.getNumber("outerRadius", t, 90),
1769 | turns = this.getNumber("turns", t, 6),
1770 | res = this.getNumber("res", t, 1) * Math.PI / 180,
1771 | fullAngle = Math.PI * 2 * turns;
1772 |
1773 | context.translate(x, y);
1774 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1775 |
1776 |
1777 | if(fullAngle > 0) {
1778 | for(var a = 0; a < fullAngle; a += res) {
1779 | var r = innerRadius + (outerRadius - innerRadius) * a / fullAngle;
1780 | context.lineTo(Math.cos(a) * r, Math.sin(a) * r);
1781 | }
1782 | }
1783 | else {
1784 | for(var a = 0; a > fullAngle; a -= res) {
1785 | var r = innerRadius + (outerRadius - innerRadius) * a / fullAngle;
1786 | context.lineTo(Math.cos(a) * r, Math.sin(a) * r);
1787 | }
1788 | }
1789 | this.drawFillAndStroke(context, t, false, true);
1790 | }
1791 | };
1792 |
1793 | };
1794 | },{}],28:[function(require,module,exports){
1795 | module.exports = function(){
1796 |
1797 | return {
1798 | draw: function(context, t) {
1799 | var x = this.getNumber("x", t, 100),
1800 | y = this.getNumber("y", t, 100),
1801 | innerRadius = this.getNumber("innerRadius", t, 25),
1802 | outerRadius = this.getNumber("outerRadius", t, 50),
1803 | rotation = this.getNumber("rotation", t, 0) * Math.PI / 180,
1804 | points = this.getNumber("points", t, 5);
1805 |
1806 | context.translate(x, y);
1807 | context.rotate(rotation);
1808 | context.moveTo(outerRadius, 0);
1809 | for(var i = 1; i < points * 2; i++) {
1810 | var angle = Math.PI * 2 / points / 2 * i,
1811 | r = i % 2 ? innerRadius : outerRadius;
1812 | context.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
1813 | }
1814 | context.lineTo(outerRadius, 0);
1815 |
1816 |
1817 | this.drawFillAndStroke(context, t, true, false);
1818 | }
1819 | }
1820 | };
1821 |
1822 | },{}],29:[function(require,module,exports){
1823 | module.exports = function(){
1824 |
1825 | return {
1826 | draw: function(context, t) {
1827 | var x = this.getNumber("x", t, 100),
1828 | y = this.getNumber("y", t, 100),
1829 | text = this.getString("text", t, "hello"),
1830 | fontSize = this.getNumber("fontSize", t, 20),
1831 | fontWeight = this.getString("fontWeight", t, "normal");
1832 | fontFamily = this.getString("fontFamily", t, "sans-serif");
1833 | fontStyle = this.getString("fontStyle", t, "normal");
1834 |
1835 | context.font = fontWeight + " " + fontStyle + " " + fontSize + "px " + fontFamily;
1836 | var width = context.measureText(text).width;
1837 | context.translate(x, y);
1838 | context.rotate(this.getNumber("rotation", t, 0) * Math.PI / 180);
1839 | var shadowsSet = false;
1840 | context.save();
1841 | if(this.getBool("fill", t, true)) {
1842 | this.setShadowParams(context, t);
1843 | shadowsSet = true;
1844 | context.fillText(text, -width / 2, fontSize * 0.4);
1845 | }
1846 | context.restore();
1847 | if(this.getBool("stroke", t, false)) {
1848 | if(!shadowsSet) {
1849 | this.setShadowParams(context, t);
1850 | }
1851 | context.strokeText(text, -width / 2, fontSize * 0.4);
1852 | }
1853 | }
1854 | }
1855 | };
1856 |
1857 | },{}]},{},[4])(4)
1858 | });
1859 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWJzL2NvbG9yLmpzIiwibGlicy9jb2xvclBhcnNlci5qcyIsImxpYnMvdmFsdWVQYXJzZXIuanMiLCJtYWluLmpzIiwibW9kZWwuanMiLCJyZW5kZXJMaXN0LmpzIiwic2NoZWR1bGVyLmpzIiwic2hhcGUuanMiLCJzaGFwZUxpc3QuanMiLCJzaGFwZXMvYXJjU2VnbWVudC5qcyIsInNoYXBlcy9hcnJvdy5qcyIsInNoYXBlcy9iZXppZXJDdXJ2ZS5qcyIsInNoYXBlcy9iZXppZXJTZWdtZW50LmpzIiwic2hhcGVzL2NpcmNsZS5qcyIsInNoYXBlcy9jdWJlLmpzIiwic2hhcGVzL2N1cnZlLmpzIiwic2hhcGVzL2N1cnZlU2VnbWVudC5qcyIsInNoYXBlcy9nZWFyLmpzIiwic2hhcGVzL2hlYXJ0LmpzIiwic2hhcGVzL2xpbmUuanMiLCJzaGFwZXMvb3ZhbC5qcyIsInNoYXBlcy9wYXRoLmpzIiwic2hhcGVzL3BvbHkuanMiLCJzaGFwZXMvcmF5U2VnbWVudC5qcyIsInNoYXBlcy9yZWN0LmpzIiwic2hhcGVzL3NlZ21lbnQuanMiLCJzaGFwZXMvc3BpcmFsLmpzIiwic2hhcGVzL3N0YXIuanMiLCJzaGFwZXMvdGV4dC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1VUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG5cbiAgZnVuY3Rpb24gcmdiYShyLCBnLCBiLCBhKSB7XG4gICAgdmFyIGNsciA9IE9iamVjdC5jcmVhdGUoY29sb3IpO1xuICAgIGNsci5zZXRSR0JBKHIsIGcsIGIsIGEpO1xuICAgIHJldHVybiBjbHIudG9TdHJpbmcoKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJnYihyLCBnLCBiKSB7XG4gICAgcmV0dXJuIHJnYmEociwgZywgYiwgMSk7XG4gIH1cblxuICBmdW5jdGlvbiByYW5kb21SR0IobWluLCBtYXgpIHtcbiAgICBtaW4gPSBtaW4gfHwgMDtcbiAgICBtYXggPSBtYXggfHwgMjU2O1xuICAgIHJldHVybiByZ2IoXG4gICAgICBNYXRoLmZsb29yKG1pbiArIE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSksXG4gICAgICBNYXRoLmZsb29yKG1pbiArIE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSksXG4gICAgICBNYXRoLmZsb29yKG1pbiArIE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSlcbiAgICApO1xuICB9XG5cbiAgZnVuY3Rpb24gcmFuZG9tR3JheShtaW4sIG1heCkge1xuICAgIG1pbiA9IG1pbiB8fCAwO1xuICAgIG1heCA9IG1heCB8fCAyNTY7XG4gICAgcmV0dXJuIGdyYXkoTWF0aC5mbG9vcihtaW4gKyBNYXRoLnJhbmRvbSgpICogKG1heCAtIG1pbikpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGdyYXkoc2hhZGUpIHtcbiAgICByZXR1cm4gcmdiKHNoYWRlLCBzaGFkZSwgc2hhZGUpO1xuICB9XG5cbiAgZnVuY3Rpb24gbnVtKG51bSkge1xuICAgIHZhciByZWQgPSBudW0gPj4gMTYsXG4gICAgICBncmVlbiA9IG51bSA+PiA4ICYgMHhmZixcbiAgICAgIGJsdWUgPSBudW0gJiAweGZmO1xuICAgIHJldHVybiByZ2IocmVkLCBncmVlbiwgYmx1ZSk7XG4gIH1cblxuICBmdW5jdGlvbiByYW5kb21IU1YobWluSCwgbWF4SCwgbWluUywgbWF4UywgbWluViwgbWF4Vikge1xuICAgIHZhciBoID0gbWluSCArIE1hdGgucmFuZG9tKCkgKiAobWF4SCAtIG1pbkgpLFxuICAgICAgcyA9IG1pblMgKyBNYXRoLnJhbmRvbSgpICogKG1heFMgLSBtaW5TKSxcbiAgICAgIHYgPSBtaW5WICsgTWF0aC5yYW5kb20oKSAqIChtYXhWIC0gbWluVik7XG4gICAgcmV0dXJuIGhzdihoLCBzLCB2KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGhzdmEoaCwgcywgdiwgYSkge1xuICAgIHZhciByLCBnLCBiLFxuICAgICAgaSA9IE1hdGguZmxvb3IoaCAvIDYwKSxcbiAgICAgIGYgPSBoIC8gNjAgLSBpLFxuICAgICAgcCA9IHYgKiAoMSAtIHMpLFxuICAgICAgcSA9IHYgKiAoMSAtIGYgKiBzKSxcbiAgICAgIHQgPSB2ICogKDEgLSAoMSAtIGYpICogcyk7XG4gICAgc3dpdGNoIChpICUgNikge1xuICAgICAgY2FzZSAwOiByID0gdiwgZyA9IHQsIGIgPSBwOyBicmVhaztcbiAgICAgIGNhc2UgMTogciA9IHEsIGcgPSB2LCBiID0gcDsgYnJlYWs7XG4gICAgICBjYXNlIDI6IHIgPSBwLCBnID0gdiwgYiA9IHQ7IGJyZWFrO1xuICAgICAgY2FzZSAzOiByID0gcCwgZyA9IHEsIGIgPSB2OyBicmVhaztcbiAgICAgIGNhc2UgNDogciA9IHQsIGcgPSBwLCBiID0gdjsgYnJlYWs7XG4gICAgICBjYXNlIDU6IHIgPSB2LCBnID0gcCwgYiA9IHE7IGJyZWFrO1xuICAgIH1cbiAgICByZXR1cm4gcmdiYShcbiAgICAgIE1hdGguZmxvb3IociAqIDI1NSksXG4gICAgICBNYXRoLmZsb29yKGcgKiAyNTUpLFxuICAgICAgTWF0aC5mbG9vcihiICogMjU1KSxcbiAgICAgIGFcbiAgICApO1xuICB9XG5cbiAgZnVuY3Rpb24gaHN2KGgsIHMsIHYpIHtcbiAgICByZXR1cm4gaHN2YShoLCBzLCB2LCAxKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGFuaW1IU1ZBKHN0YXJ0SCwgZW5kSCwgc3RhcnRTLCBlbmRTLCBzdGFydFYsIGVuZFYsIHN0YXJ0QSwgZW5kQSkge1xuICAgIHJldHVybiBmdW5jdGlvbih0KSB7XG4gICAgICB2YXIgaCA9IHN0YXJ0SCArIHQgKiAoZW5kSCAtIHN0YXJ0SCksXG4gICAgICAgIHMgPSBzdGFydFMgKyB0ICogKGVuZFMgLSBzdGFydFMpLFxuICAgICAgICB2ID0gc3RhcnRWICsgdCAqIChlbmRWIC0gc3RhcnRWKSxcbiAgICAgICAgYSA9IHN0YXJ0QSArIHQgKiAoZW5kQSAtIHN0YXJ0QSk7XG4gICAgICByZXR1cm4gaHN2YShoLCBzLCB2LCBhKTtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBhbmltSFNWKHN0YXJ0SCwgZW5kSCwgc3RhcnRTLCBlbmRTLCBzdGFydFYsIGVuZFYpIHtcbiAgICByZXR1cm4gYW5pbUhTVkEoc3RhcnRILCBlbmRILCBzdGFydFMsIGVuZFMsIHN0YXJ0ViwgZW5kViwgMSwgMSk7XG4gIH1cblxuXG5cblxuXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBncmFkaWVudHNcbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIGZ1bmN0aW9uIGNyZWF0ZUxpbmVhckdyYWRpZW50KHgwLCB5MCwgeDEsIHkxKSB7XG4gICAgdmFyIGcgPSB7XG4gICAgICB0eXBlOiBcImxpbmVhckdyYWRpZW50XCIsXG4gICAgICB4MDogeDAsXG4gICAgICB5MDogeTAsXG4gICAgICB4MTogeDEsXG4gICAgICB5MTogeTEsXG4gICAgICBjb2xvclN0b3BzOiBbXSxcbiAgICAgIGFkZENvbG9yU3RvcDogZnVuY3Rpb24ocG9zaXRpb24sIGNvbG9yKSB7XG4gICAgICAgIHRoaXMuY29sb3JTdG9wcy5wdXNoKHtcbiAgICAgICAgICBwb3NpdGlvbjogcG9zaXRpb24sXG4gICAgICAgICAgY29sb3I6IGNvbG9yXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gICAgcmV0dXJuIGc7XG4gIH1cblxuICBmdW5jdGlvbiBjcmVhdGVSYWRpYWxHcmFkaWVudCh4MCwgeTAsIHIwLCB4MSwgeTEsIHIxKSB7XG4gICAgdmFyIGcgPSB7XG4gICAgICB0eXBlOiBcInJhZGlhbEdyYWRpZW50XCIsXG4gICAgICB4MDogeDAsXG4gICAgICB5MDogeTAsXG4gICAgICByMDogcjAsXG4gICAgICB4MTogeDEsXG4gICAgICB5MTogeTEsXG4gICAgICByMTogcjEsXG4gICAgICBjb2xvclN0b3BzOiBbXSxcbiAgICAgIGFkZENvbG9yU3RvcDogZnVuY3Rpb24ocG9zaXRpb24sIGNvbG9yKSB7XG4gICAgICAgIHRoaXMuY29sb3JTdG9wcy5wdXNoKHtcbiAgICAgICAgICBwb3NpdGlvbjogcG9zaXRpb24sXG4gICAgICAgICAgY29sb3I6IGNvbG9yXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gICAgcmV0dXJuIGc7XG4gIH1cblxuICB2YXIgY29sb3IgPSB7XG4gICAgcjogMjU1LFxuICAgIGc6IDI1NSxcbiAgICBiOiAyNTUsXG4gICAgYTogMSxcblxuICAgIHNldFJHQkE6IGZ1bmN0aW9uKHIsIGcsIGIsIGEpIHtcbiAgICAgIHRoaXMuciA9IHI7XG4gICAgICB0aGlzLmcgPSBnO1xuICAgICAgdGhpcy5iID0gYjtcbiAgICAgIHRoaXMuYSA9IGE7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgdG9TdHJpbmc6IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIFwicmdiYShcIiArIE1hdGguZmxvb3IodGhpcy5yKSArIFwiLFwiICsgTWF0aC5mbG9vcih0aGlzLmcpICsgXCIsXCIgKyBNYXRoLmZsb29yKHRoaXMuYikgKyBcIixcIiArIHRoaXMuYSArIFwiKVwiO1xuICAgIH1cbiAgfTtcblxuICByZXR1cm4ge1xuICAgIHJnYjogcmdiLFxuICAgIHJnYmE6IHJnYmEsXG4gICAgcmFuZG9tUkdCOiByYW5kb21SR0IsXG4gICAgcmFuZG9tR3JheTogcmFuZG9tR3JheSxcbiAgICBncmF5OiBncmF5LFxuICAgIG51bTogbnVtLFxuICAgIGhzdjogaHN2LFxuICAgIGhzdmE6IGhzdmEsXG4gICAgYW5pbUhTVjogYW5pbUhTVixcbiAgICBhbmltSFNWQTogYW5pbUhTVkEsXG4gICAgcmFuZG9tSFNWOiByYW5kb21IU1YsXG4gICAgY3JlYXRlTGluZWFyR3JhZGllbnQ6IGNyZWF0ZUxpbmVhckdyYWRpZW50LFxuICAgIGNyZWF0ZVJhZGlhbEdyYWRpZW50OiBjcmVhdGVSYWRpYWxHcmFkaWVudFxuICB9O1xufTsiLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgY29udGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJjYW52YXNcIikuZ2V0Q29udGV4dChcIjJkXCIpO1xuXG4gIGZ1bmN0aW9uIGdldENvbG9yKHByb3AsIHQsIGRlZikge1xuICAgIGlmKHByb3AgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGRlZjtcbiAgICB9XG4gICAgaWYodHlwZW9mKHByb3ApID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBpZihwcm9wLmNoYXJBdCgwKSA9PT0gXCIjXCIgJiYgcHJvcC5sZW5ndGggPiA3KSB7XG4gICAgICAgIHZhciBvYmogPSBnZXRDb2xvck9iaihwcm9wKTtcbiAgICAgICAgcmV0dXJuIGdldENvbG9yU3RyaW5nKG9iaik7XG4gICAgICB9XG4gICAgICByZXR1cm4gcHJvcDtcbiAgICB9XG4gICAgZWxzZSBpZih0eXBlb2YocHJvcCkgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgcmV0dXJuIHByb3AodCk7XG4gICAgfVxuICAgIGVsc2UgaWYocHJvcCAmJiBwcm9wLmxlbmd0aCA9PT0gMikge1xuICAgICAgaWYoaXNMaW5lYXJHcmFkaWVudChwcm9wKSkge1xuICAgICAgICByZXR1cm4gcGFyc2VMaW5lYXJHcmFkaWVudChwcm9wLCB0KTtcbiAgICAgIH1cbiAgICAgIGlmKGlzUmFkaWFsR3JhZGllbnQocHJvcCkpIHtcbiAgICAgICAgcmV0dXJuIHBhcnNlUmFkaWFsR3JhZGllbnQocHJvcCwgdCk7XG4gICAgICB9XG4gICAgICB2YXIgYzAgPSBnZXRDb2xvck9iaihwcm9wWzBdKSxcbiAgICAgICAgYzEgPSBnZXRDb2xvck9iaihwcm9wWzFdKTtcbiAgICAgIHJldHVybiBpbnRlcnBvbGF0ZUNvbG9yKFtjMCwgYzFdLCB0KTtcbiAgICB9XG4gICAgZWxzZSBpZihwcm9wICYmIHByb3AubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gIHByb3BbTWF0aC5yb3VuZCh0ICogKHByb3AubGVuZ3RoIC0gMSkpXTtcbiAgICB9XG4gICAgaWYocHJvcC50eXBlID09PSBcImxpbmVhckdyYWRpZW50XCIpIHtcbiAgICAgIHZhciBnID0gY29udGV4dC5jcmVhdGVMaW5lYXJHcmFkaWVudChwcm9wLngwLCBwcm9wLnkwLCBwcm9wLngxLCBwcm9wLnkxKTtcbiAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBwcm9wLmNvbG9yU3RvcHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIHN0b3AgPSBwcm9wLmNvbG9yU3RvcHNbaV07XG4gICAgICAgIGcuYWRkQ29sb3JTdG9wKHN0b3AucG9zaXRpb24sIHN0b3AuY29sb3IpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGc7XG4gICAgfVxuICAgIGlmKHByb3AudHlwZSA9PT0gXCJyYWRpYWxHcmFkaWVudFwiKSB7XG4gICAgICB2YXIgZyA9IGNvbnRleHQuY3JlYXRlUmFkaWFsR3JhZGllbnQocHJvcC54MCwgcHJvcC55MCwgcHJvcC5yMCwgcHJvcC54MSwgcHJvcC55MSwgcHJvcC5yMSk7XG4gICAgICBmb3IodmFyIGkgPSAwOyBpIDwgcHJvcC5jb2xvclN0b3BzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBzdG9wID0gcHJvcC5jb2xvclN0b3BzW2ldO1xuICAgICAgICBnLmFkZENvbG9yU3RvcChzdG9wLnBvc2l0aW9uLCBzdG9wLmNvbG9yKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBnO1xuICAgIH1cbiAgICByZXR1cm4gZGVmO1xuICB9XG5cbiAgZnVuY3Rpb24gaXNMaW5lYXJHcmFkaWVudChwcm9wKSB7XG4gICAgcmV0dXJuIHByb3BbMF0udHlwZSA9PT0gXCJsaW5lYXJHcmFkaWVudFwiICYmIHByb3BbMV0udHlwZSA9PT0gXCJsaW5lYXJHcmFkaWVudFwiO1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VMaW5lYXJHcmFkaWVudChwcm9wLCB0KSB7XG4gICAgdmFyIGcwID0gcHJvcFswXSxcbiAgICAgIGcxID0gcHJvcFsxXSxcbiAgICAgIHgwID0gZzAueDAgKyAoZzEueDAgLSBnMC54MCkgKiB0LFxuICAgICAgeTAgPSBnMC55MCArIChnMS55MCAtIGcwLnkwKSAqIHQsXG4gICAgICB4MSA9IGcwLngxICsgKGcxLngxIC0gZzAueDEpICogdCxcbiAgICAgIHkxID0gZzAueTEgKyAoZzEueTEgLSBnMC55MSkgKiB0O1xuXG4gICAgdmFyIGcgPSBjb250ZXh0LmNyZWF0ZUxpbmVhckdyYWRpZW50KHgwLCB5MCwgeDEsIHkxKTtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgZzAuY29sb3JTdG9wcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIHN0b3BBID0gZzAuY29sb3JTdG9wc1tpXSxcbiAgICAgICAgc3RvcEIgPSBnMS5jb2xvclN0b3BzW2ldLFxuICAgICAgICBwb3NpdGlvbiA9IHN0b3BBLnBvc2l0aW9uICsgKHN0b3BCLnBvc2l0aW9uIC0gc3RvcEEucG9zaXRpb24pICogdCxcbiAgICAgICAgY29sb3JBID0gZ2V0Q29sb3JPYmooc3RvcEEuY29sb3IpLFxuICAgICAgICBjb2xvckIgPSBnZXRDb2xvck9iaihzdG9wQi5jb2xvciksXG4gICAgICAgIGNvbG9yID0gaW50ZXJwb2xhdGVDb2xvcihbY29sb3JBLCBjb2xvckJdLCB0KTtcbiAgICAgIGcuYWRkQ29sb3JTdG9wKHBvc2l0aW9uLCBjb2xvcik7XG4gICAgfVxuICAgIHJldHVybiBnO1xuICB9XG5cbiAgZnVuY3Rpb24gaXNSYWRpYWxHcmFkaWVudChwcm9wKSB7XG4gICAgcmV0dXJuIHByb3BbMF0udHlwZSA9PT0gXCJyYWRpYWxHcmFkaWVudFwiICYmIHByb3BbMV0udHlwZSA9PT0gXCJyYWRpYWxHcmFkaWVudFwiO1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VSYWRpYWxHcmFkaWVudChwcm9wLCB0KSB7XG4gICAgdmFyIGcwID0gcHJvcFswXSxcbiAgICAgIGcxID0gcHJvcFsxXSxcbiAgICAgIHgwID0gZzAueDAgKyAoZzEueDAgLSBnMC54MCkgKiB0LFxuICAgICAgeTAgPSBnMC55MCArIChnMS55MCAtIGcwLnkwKSAqIHQsXG4gICAgICByMCA9IGcwLnIwICsgKGcxLnIwIC0gZzAucjApICogdCxcbiAgICAgIHgxID0gZzAueDEgKyAoZzEueDEgLSBnMC54MSkgKiB0LFxuICAgICAgeTEgPSBnMC55MSArIChnMS55MSAtIGcwLnkxKSAqIHQsXG4gICAgICByMSA9IGcwLnIxICsgKGcxLnIxIC0gZzAucjEpICogdDtcblxuICAgIHZhciBnID0gY29udGV4dC5jcmVhdGVSYWRpYWxHcmFkaWVudCh4MCwgeTAsIHIwLCB4MSwgeTEsIHIxKTtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgZzAuY29sb3JTdG9wcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIHN0b3BBID0gZzAuY29sb3JTdG9wc1tpXSxcbiAgICAgICAgc3RvcEIgPSBnMS5jb2xvclN0b3BzW2ldLFxuICAgICAgICBwb3NpdGlvbiA9IHN0b3BBLnBvc2l0aW9uICsgKHN0b3BCLnBvc2l0aW9uIC0gc3RvcEEucG9zaXRpb24pICogdCxcbiAgICAgICAgY29sb3JBID0gZ2V0Q29sb3JPYmooc3RvcEEuY29sb3IpLFxuICAgICAgICBjb2xvckIgPSBnZXRDb2xvck9iaihzdG9wQi5jb2xvciksXG4gICAgICAgIGNvbG9yID0gaW50ZXJwb2xhdGVDb2xvcihbY29sb3JBLCBjb2xvckJdLCB0KTtcbiAgICAgIGcuYWRkQ29sb3JTdG9wKHBvc2l0aW9uLCBjb2xvcik7XG4gICAgfVxuICAgIHJldHVybiBnO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0Q29sb3JTdHJpbmcob2JqKSB7XG4gICAgcmV0dXJuIFwicmdiYShcIiArIG9iai5yICsgXCIsXCIgKyBvYmouZyArIFwiLFwiICsgb2JqLmIgKyBcIixcIiArIChvYmouYSAvIDI1NSkgKyBcIilcIjtcbiAgfVxuXG4gIGZ1bmN0aW9uIGdldENvbG9yT2JqKGNvbG9yKSB7XG4gICAgaWYoY29sb3IuY2hhckF0KDApID09PSBcIiNcIikge1xuICAgICAgaWYoY29sb3IubGVuZ3RoID09PSA3KSB7IC8vICNycmdnYmJcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBhOiAyNTUsXG4gICAgICAgICAgcjogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDEsIDMpLCAxNiksXG4gICAgICAgICAgZzogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDMsIDUpLCAxNiksXG4gICAgICAgICAgYjogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDUsIDcpLCAxNilcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZWxzZSBpZihjb2xvci5sZW5ndGggPT09IDkpIHsgLy8gI2FhcnJnZ2JiXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYTogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDEsIDMpLCAxNiksXG4gICAgICAgICAgcjogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDMsIDUpLCAxNiksXG4gICAgICAgICAgZzogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDUsIDcpLCAxNiksXG4gICAgICAgICAgYjogcGFyc2VJbnQoY29sb3Iuc3Vic3RyaW5nKDcsIDkpLCAxNilcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZWxzZSB7IC8vICNyZ2JcbiAgICAgICAgdmFyIHIgPSBjb2xvci5jaGFyQXQoMSksXG4gICAgICAgICAgZyA9IGNvbG9yLmNoYXJBdCgyKSxcbiAgICAgICAgICBiID0gY29sb3IuY2hhckF0KDMpO1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYTogMjU1LFxuICAgICAgICAgIHI6IHBhcnNlSW50KHIgKyByLCAxNiksXG4gICAgICAgICAgZzogcGFyc2VJbnQoZyArIGcsIDE2KSxcbiAgICAgICAgICBiOiBwYXJzZUludChiICsgYiwgMTYpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgZWxzZSBpZihjb2xvci5zdWJzdHJpbmcoMCwgNCkgPT09IFwicmdiKFwiKSB7XG4gICAgICB2YXIgcyA9IGNvbG9yLmluZGV4T2YoXCIoXCIpICsgMSxcbiAgICAgICAgZSA9IGNvbG9yLmluZGV4T2YoXCIpXCIpLFxuICAgICAgICBjaGFubmVscyA9IGNvbG9yLnN1YnN0cmluZyhzLCBlKS5zcGxpdChcIixcIik7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBhOiAyNTUsXG4gICAgICAgIHI6IHBhcnNlSW50KGNoYW5uZWxzWzBdLCAxMCksXG4gICAgICAgIGc6IHBhcnNlSW50KGNoYW5uZWxzWzFdLCAxMCksXG4gICAgICAgIGI6IHBhcnNlSW50KGNoYW5uZWxzWzJdLCAxMClcbiAgICAgIH1cbiAgICB9XG4gICAgZWxzZSBpZihjb2xvci5zdWJzdHJpbmcoMCwgNCkgPT09IFwicmdiYVwiKSB7XG4gICAgICB2YXIgcyA9IGNvbG9yLmluZGV4T2YoXCIoXCIpICsgMSxcbiAgICAgICAgZSA9IGNvbG9yLmluZGV4T2YoXCIpXCIpLFxuICAgICAgICBjaGFubmVscyA9IGNvbG9yLnN1YnN0cmluZyhzLCBlKS5zcGxpdChcIixcIik7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBhOiBwYXJzZUZsb2F0KGNoYW5uZWxzWzNdKSAqIDI1NSxcbiAgICAgICAgcjogcGFyc2VJbnQoY2hhbm5lbHNbMF0sIDEwKSxcbiAgICAgICAgZzogcGFyc2VJbnQoY2hhbm5lbHNbMV0sIDEwKSxcbiAgICAgICAgYjogcGFyc2VJbnQoY2hhbm5lbHNbMl0sIDEwKVxuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIGNvbG9yID0gY29sb3IudG9Mb3dlckNhc2UoKTtcbiAgICAgIGlmKG5hbWVkQ29sb3JzW2NvbG9yXSAhPSBudWxsKSB7XG4gICAgICAgIHJldHVybiBnZXRDb2xvck9iaihuYW1lZENvbG9yc1tjb2xvcl0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gMDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGludGVycG9sYXRlQ29sb3IoYXJyLCB0KSB7XG4gICAgdmFyIGMwID0gYXJyWzBdLFxuICAgICAgYzEgPSBhcnJbMV07XG5cbiAgICB2YXIgYWxwaGEgPSBjMC5hICsgKGMxLmEgLSBjMC5hKSAqIHQsXG4gICAgICByZWQgPSBNYXRoLnJvdW5kKGMwLnIgKyAoYzEuciAtIGMwLnIpICogdCksXG4gICAgICBncmVlbiA9IE1hdGgucm91bmQoYzAuZyArIChjMS5nIC0gYzAuZykgKiB0KSxcbiAgICAgIGJsdWUgPSBNYXRoLnJvdW5kKGMwLmIgKyAoYzEuYiAtIGMwLmIpICogdCk7XG4gICAgcmV0dXJuIFwicmdiYShcIiArIHJlZCArIFwiLFwiICsgZ3JlZW4gKyBcIixcIiArIGJsdWUgKyBcIixcIiArIChhbHBoYSAvIDI1NSkgKyBcIilcIjtcbiAgfVxuXG4gIHZhciBuYW1lZENvbG9ycyA9IHtcbiAgICBhbGljZWJsdWU6IFwiI2YwZjhmZlwiLFxuICAgIGFudGlxdWV3aGl0ZTogXCIjZmFlYmQ3XCIsXG4gICAgYXF1YTogXCIjMDBmZmZmXCIsXG4gICAgYXF1YW1hcmluZTogXCIjN2ZmZmQ0XCIsXG4gICAgYXp1cmU6IFwiI2YwZmZmZlwiLFxuICAgIGJlaWdlOiBcIiNmNWY1ZGNcIixcbiAgICBiaXNxdWU6IFwiI2ZmZTRjNFwiLFxuICAgIGJsYWNrOiBcIiMwMDAwMDBcIixcbiAgICBibGFuY2hlZGFsbW9uZDogXCIjZmZlYmNkXCIsXG4gICAgYmx1ZTogXCIjMDAwMGZmXCIsXG4gICAgYmx1ZXZpb2xldDogXCIjOGEyYmUyXCIsXG4gICAgYnJvd246IFwiI2E1MmEyYVwiLFxuICAgIGJ1cmx5d29vZDogXCIjZGViODg3XCIsXG4gICAgY2FkZXRibHVlOiBcIiM1ZjllYTBcIixcbiAgICBjaGFydHJldXNlOiBcIiM3ZmZmMDBcIixcbiAgICBjaG9jb2xhdGU6IFwiI2QyNjkxZVwiLFxuICAgIGNvcmFsOiBcIiNmZjdmNTBcIixcbiAgICBjb3JuZmxvd2VyYmx1ZTogXCIjNjQ5NWVkXCIsXG4gICAgY29ybnNpbGs6IFwiI2ZmZjhkY1wiLFxuICAgIGNyaW1zb246IFwiI2RjMTQzY1wiLFxuICAgIGN5YW46IFwiIzAwZmZmZlwiLFxuICAgIGRhcmtibHVlOiBcIiMwMDAwOGJcIixcbiAgICBkYXJrY3lhbjogXCIjMDA4YjhiXCIsXG4gICAgZGFya2dvbGRlbnJvZDogXCIjYjg4NjBiXCIsXG4gICAgZGFya2dyYXk6IFwiI2E5YTlhOVwiLFxuICAgIGRhcmtncmV5OiBcIiNhOWE5YTlcIixcbiAgICBkYXJrZ3JlZW46IFwiIzAwNjQwMFwiLFxuICAgIGRhcmtraGFraTogXCIjYmRiNzZiXCIsXG4gICAgZGFya21hZ2VudGE6IFwiIzhiMDA4YlwiLFxuICAgIGRhcmtvbGl2ZWdyZWVuOiBcIiM1NTZiMmZcIixcbiAgICBkYXJrb3JhbmdlOiBcIiNmZjhjMDBcIixcbiAgICBkYXJrb3JjaGlkOiBcIiM5OTMyY2NcIixcbiAgICBkYXJrcmVkOiBcIiM4YjAwMDBcIixcbiAgICBkYXJrc2FsbW9uOiBcIiNlOTk2N2FcIixcbiAgICBkYXJrc2VhZ3JlZW46IFwiIzhmYmM4ZlwiLFxuICAgIGRhcmtzbGF0ZWJsdWU6IFwiIzQ4M2Q4YlwiLFxuICAgIGRhcmtzbGF0ZWdyYXk6IFwiIzJmNGY0ZlwiLFxuICAgIGRhcmtzbGF0ZWdyZXk6IFwiIzJmNGY0ZlwiLFxuICAgIGRhcmt0dXJxdW9pc2U6IFwiIzAwY2VkMVwiLFxuICAgIGRhcmt2aW9sZXQ6IFwiIzk0MDBkM1wiLFxuICAgIGRlZXBwaW5rOiBcIiNmZjE0OTNcIixcbiAgICBkZWVwc2t5Ymx1ZTogXCIjMDBiZmZmXCIsXG4gICAgZGltZ3JheTogXCIjNjk2OTY5XCIsXG4gICAgZGltZ3JleTogXCIjNjk2OTY5XCIsXG4gICAgZG9kZ2VyYmx1ZTogXCIjMWU5MGZmXCIsXG4gICAgZmlyZWJyaWNrOiBcIiNiMjIyMjJcIixcbiAgICBmbG9yYWx3aGl0ZTogXCIjZmZmYWYwXCIsXG4gICAgZm9yZXN0Z3JlZW46IFwiIzIyOGIyMlwiLFxuICAgIGZ1Y2hzaWE6IFwiI2ZmMDBmZlwiLFxuICAgIGdhaW5zYm9ybzogXCIjZGNkY2RjXCIsXG4gICAgZ2hvc3R3aGl0ZTogXCIjZjhmOGZmXCIsXG4gICAgZ29sZDogXCIjZmZkNzAwXCIsXG4gICAgZ29sZGVucm9kOiBcIiNkYWE1MjBcIixcbiAgICBncmF5OiBcIiM4MDgwODBcIixcbiAgICBncmV5OiBcIiM4MDgwODBcIixcbiAgICBncmVlbjogXCIjMDA4MDAwXCIsXG4gICAgZ3JlZW55ZWxsb3c6IFwiI2FkZmYyZlwiLFxuICAgIGhvbmV5ZGV3OiBcIiNmMGZmZjBcIixcbiAgICBob3RwaW5rOiBcIiNmZjY5YjRcIixcbiAgICBpbmRpYW5yZWQ6IFwiI2NkNWM1Y1wiLFxuICAgIGluZGlnbzogXCIjNGIwMDgyXCIsXG4gICAgaXZvcnk6IFwiI2ZmZmZmMFwiLFxuICAgIGtoYWtpOiBcIiNmMGU2OGNcIixcbiAgICBsYXZlbmRlcjogXCIjZTZlNmZhXCIsXG4gICAgbGF2ZW5kZXJibHVzaDogXCIjZmZmMGY1XCIsXG4gICAgbGF3bmdyZWVuOiBcIiM3Y2ZjMDBcIixcbiAgICBsZW1vbmNoaWZmb246IFwiI2ZmZmFjZFwiLFxuICAgIGxpZ2h0Ymx1ZTogXCIjYWRkOGU2XCIsXG4gICAgbGlnaHRjb3JhbDogXCIjZjA4MDgwXCIsXG4gICAgbGlnaHRjeWFuOiBcIiNlMGZmZmZcIixcbiAgICBsaWdodGdvbGRlbnJvZHllbGxvdzogXCIjZmFmYWQyXCIsXG4gICAgbGlnaHRncmF5OiBcIiNkM2QzZDNcIixcbiAgICBsaWdodGdyZXk6IFwiI2QzZDNkM1wiLFxuICAgIGxpZ2h0Z3JlZW46IFwiIzkwZWU5MFwiLFxuICAgIGxpZ2h0cGluazogXCIjZmZiNmMxXCIsXG4gICAgbGlnaHRzYWxtb246IFwiI2ZmYTA3YVwiLFxuICAgIGxpZ2h0c2VhZ3JlZW46IFwiIzIwYjJhYVwiLFxuICAgIGxpZ2h0c2t5Ymx1ZTogXCIjODdjZWZhXCIsXG4gICAgbGlnaHRzbGF0ZWdyYXk6IFwiIzc3ODg5OVwiLFxuICAgIGxpZ2h0c2xhdGVncmV5OiBcIiM3Nzg4OTlcIixcbiAgICBsaWdodHN0ZWVsYmx1ZTogXCIjYjBjNGRlXCIsXG4gICAgbGlnaHR5ZWxsb3c6IFwiI2ZmZmZlMFwiLFxuICAgIGxpbWU6IFwiIzAwZmYwMFwiLFxuICAgIGxpbWVncmVlbjogXCIjMzJjZDMyXCIsXG4gICAgbGluZW46IFwiI2ZhZjBlNlwiLFxuICAgIG1hZ2VudGE6IFwiI2ZmMDBmZlwiLFxuICAgIG1hcm9vbjogXCIjODAwMDAwXCIsXG4gICAgbWVkaXVtYXF1YW1hcmluZTogXCIjNjZjZGFhXCIsXG4gICAgbWVkaXVtYmx1ZTogXCIjMDAwMGNkXCIsXG4gICAgbWVkaXVtb3JjaGlkOiBcIiNiYTU1ZDNcIixcbiAgICBtZWRpdW1wdXJwbGU6IFwiIzkzNzBkOFwiLFxuICAgIG1lZGl1bXNlYWdyZWVuOiBcIiMzY2IzNzFcIixcbiAgICBtZWRpdW1zbGF0ZWJsdWU6IFwiIzdiNjhlZVwiLFxuICAgIG1lZGl1bXNwcmluZ2dyZWVuOiBcIiMwMGZhOWFcIixcbiAgICBtZWRpdW10dXJxdW9pc2U6IFwiIzQ4ZDFjY1wiLFxuICAgIG1lZGl1bXZpb2xldHJlZDogXCIjYzcxNTg1XCIsXG4gICAgbWlkbmlnaHRibHVlOiBcIiMxOTE5NzBcIixcbiAgICBtaW50Y3JlYW06IFwiI2Y1ZmZmYVwiLFxuICAgIG1pc3R5cm9zZTogXCIjZmZlNGUxXCIsXG4gICAgbW9jY2FzaW46IFwiI2ZmZTRiNVwiLFxuICAgIG5hdmFqb3doaXRlOiBcIiNmZmRlYWRcIixcbiAgICBuYXZ5OiBcIiMwMDAwODBcIixcbiAgICBvbGRsYWNlOiBcIiNmZGY1ZTZcIixcbiAgICBvbGl2ZTogXCIjODA4MDAwXCIsXG4gICAgb2xpdmVkcmFiOiBcIiM2YjhlMjNcIixcbiAgICBvcmFuZ2U6IFwiI2ZmYTUwMFwiLFxuICAgIG9yYW5nZXJlZDogXCIjZmY0NTAwXCIsXG4gICAgb3JjaGlkOiBcIiNkYTcwZDZcIixcbiAgICBwYWxlZ29sZGVucm9kOiBcIiNlZWU4YWFcIixcbiAgICBwYWxlZ3JlZW46IFwiIzk4ZmI5OFwiLFxuICAgIHBhbGV0dXJxdW9pc2U6IFwiI2FmZWVlZVwiLFxuICAgIHBhbGV2aW9sZXRyZWQ6IFwiI2Q4NzA5M1wiLFxuICAgIHBhcGF5YXdoaXA6IFwiI2ZmZWZkNVwiLFxuICAgIHBlYWNocHVmZjogXCIjZmZkYWI5XCIsXG4gICAgcGVydTogXCIjY2Q4NTNmXCIsXG4gICAgcGluazogXCIjZmZjMGNiXCIsXG4gICAgcGx1bTogXCIjZGRhMGRkXCIsXG4gICAgcG93ZGVyYmx1ZTogXCIjYjBlMGU2XCIsXG4gICAgcHVycGxlOiBcIiM4MDAwODBcIixcbiAgICByZWQ6IFwiI2ZmMDAwMFwiLFxuICAgIHJvc3licm93bjogXCIjYmM4ZjhmXCIsXG4gICAgcm95YWxibHVlOiBcIiM0MTY5ZTFcIixcbiAgICBzYWRkbGVicm93bjogXCIjOGI0NTEzXCIsXG4gICAgc2FsbW9uOiBcIiNmYTgwNzJcIixcbiAgICBzYW5keWJyb3duOiBcIiNmNGE0NjBcIixcbiAgICBzZWFncmVlbjogXCIjMmU4YjU3XCIsXG4gICAgc2Vhc2hlbGw6IFwiI2ZmZjVlZVwiLFxuICAgIHNpZW5uYTogXCIjYTA1MjJkXCIsXG4gICAgc2lsdmVyOiBcIiNjMGMwYzBcIixcbiAgICBza3libHVlOiBcIiM4N2NlZWJcIixcbiAgICBzbGF0ZWJsdWU6IFwiIzZhNWFjZFwiLFxuICAgIHNsYXRlZ3JheTogXCIjNzA4MDkwXCIsXG4gICAgc2xhdGVncmV5OiBcIiM3MDgwOTBcIixcbiAgICBzbm93OiBcIiNmZmZhZmFcIixcbiAgICBzcHJpbmdncmVlbjogXCIjMDBmZjdmXCIsXG4gICAgc3RlZWxibHVlOiBcIiM0NjgyYjRcIixcbiAgICB0YW46IFwiI2QyYjQ4Y1wiLFxuICAgIHRlYWw6IFwiIzAwODA4MFwiLFxuICAgIHRoaXN0bGU6IFwiI2Q4YmZkOFwiLFxuICAgIHRvbWF0bzogXCIjZmY2MzQ3XCIsXG4gICAgdHVycXVvaXNlOiBcIiM0MGUwZDBcIixcbiAgICB2aW9sZXQ6IFwiI2VlODJlZVwiLFxuICAgIHdoZWF0OiBcIiNmNWRlYjNcIixcbiAgICB3aGl0ZTogXCIjZmZmZmZmXCIsXG4gICAgd2hpdGVzbW9rZTogXCIjZjVmNWY1XCIsXG4gICAgeWVsbG93OiBcIiNmZmZmMDBcIixcbiAgICB5ZWxsb3dncmVlbjogXCIjOWFjZDMyXCJcbiAgfTtcblxuICByZXR1cm4ge1xuICAgIGdldENvbG9yOiBnZXRDb2xvclxuICB9O1xufTsiLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4ge1xuXG4gICAgZ2V0TnVtYmVyOiBmdW5jdGlvbihwcm9wLCB0LCBkZWYpIHtcbiAgICAgIGlmKHR5cGVvZihwcm9wKSA9PT0gXCJudW1iZXJcIikge1xuICAgICAgICByZXR1cm4gcHJvcDtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYodHlwZW9mKHByb3ApID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgcmV0dXJuIHByb3AodCk7XG4gICAgICB9XG4gICAgICBlbHNlIGlmKHByb3AgJiYgcHJvcC5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgdmFyIHN0YXJ0ID0gcHJvcFswXSxcbiAgICAgICAgICBlbmQgPSBwcm9wWzFdO1xuICAgICAgICByZXR1cm4gc3RhcnQgKyAoZW5kIC0gc3RhcnQpICogdDtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYocHJvcCAmJiBwcm9wLmxlbmd0aCkge1xuICAgICAgICByZXR1cm4gcHJvcFtNYXRoLnJvdW5kKHQgKiAocHJvcC5sZW5ndGggLSAxKSldO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRlZjtcbiAgICB9LFxuXG5cbiAgICBnZXRTdHJpbmc6IGZ1bmN0aW9uKHByb3AsIHQsIGRlZikge1xuICAgICAgaWYocHJvcCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBkZWY7XG4gICAgICB9XG4gICAgICBlbHNlIGlmKHR5cGVvZihwcm9wKSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICByZXR1cm4gcHJvcDtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYodHlwZW9mKHByb3ApID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgcmV0dXJuIHByb3AodCk7XG4gICAgICB9XG4gICAgICBlbHNlIGlmKHByb3AgJiYgcHJvcC5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIHByb3BbTWF0aC5yb3VuZCh0ICogKHByb3AubGVuZ3RoIC0gMSkpXTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBwcm9wO1xuICAgIH0sXG5cbiAgICBnZXRCb29sOiBmdW5jdGlvbihwcm9wLCB0LCBkZWYpIHtcbiAgICAgIGlmKHByb3AgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZGVmO1xuICAgICAgfVxuICAgICAgZWxzZSBpZih0eXBlb2YocHJvcCkgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gcHJvcCh0KTtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYocHJvcCAmJiBwcm9wLmxlbmd0aCkge1xuICAgICAgICByZXR1cm4gcHJvcFtNYXRoLnJvdW5kKHQgKiAocHJvcC5sZW5ndGggLSAxKSldO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByb3A7XG4gICAgfSxcblxuICAgIGdldEFycmF5OiBmdW5jdGlvbihwcm9wLCB0LCBkZWYpIHtcbiAgICAgIC8vIHN0cmluZyB3aWxsIGhhdmUgbGVuZ3RoLCBidXQgaXMgdXNlbGVzc1xuICAgICAgaWYodHlwZW9mKHByb3ApID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIHJldHVybiBkZWY7XG4gICAgICB9XG4gICAgICBlbHNlIGlmKHR5cGVvZihwcm9wKSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIHJldHVybiBwcm9wKHQpO1xuICAgICAgfVxuICAgICAgZWxzZSBpZihwcm9wICYmIChwcm9wLmxlbmd0aCA9PSAyKSAmJiBwcm9wWzBdLmxlbmd0aCAmJiBwcm9wWzFdLmxlbmd0aCkge1xuICAgICAgICAvLyB3ZSBzZWVtIHRvIGhhdmUgYW4gYXJyYXkgb2YgYXJyYXlzXG4gICAgICAgIHZhciBhcnIwID0gcHJvcFswXSxcbiAgICAgICAgICBhcnIxID0gcHJvcFsxXSxcbiAgICAgICAgICBsZW4gPSBNYXRoLm1pbihhcnIwLmxlbmd0aCwgYXJyMS5sZW5ndGgpLFxuICAgICAgICAgIHJlc3VsdCA9IFtdO1xuXG4gICAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgICAgIHZhciB2MCA9IGFycjBbaV0sXG4gICAgICAgICAgICB2MSA9IGFycjFbaV07XG4gICAgICAgICAgcmVzdWx0LnB1c2godjAgKyAodjEgLSB2MCkgKiB0KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0O1xuXG4gICAgICB9XG4gICAgICBlbHNlIGlmKHByb3AgJiYgcHJvcC5sZW5ndGggPiAxKSB7XG4gICAgICAgIHJldHVybiBwcm9wO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRlZjtcbiAgICB9LFxuXG4gICAgZ2V0T2JqZWN0OiBmdW5jdGlvbihwcm9wLCB0LCBkZWYpIHtcbiAgICAgIGlmKHByb3AgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZGVmO1xuICAgICAgfVxuICAgICAgZWxzZSBpZih0eXBlb2YocHJvcCkgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gcHJvcCh0KTtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYocHJvcCAmJiBwcm9wLmxlbmd0aCkge1xuICAgICAgICByZXR1cm4gcHJvcFtNYXRoLnJvdW5kKHQgKiAocHJvcC5sZW5ndGggLSAxKSldO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByb3A7XG4gICAgfVxuXG4gIH1cbn07IiwidmFyIE1vZGVsID0gcmVxdWlyZSgnLi9tb2RlbCcpO1xudmFyIFJlbmRlckxpc3QgPSByZXF1aXJlKCcuL3JlbmRlckxpc3QnKTtcblxudmFyIGNvbG9yTGliID0gcmVxdWlyZSgnLi9saWJzL2NvbG9yJykoKTtcbnZhciBzaGFwZUxpc3QgPSByZXF1aXJlKCcuL3NoYXBlTGlzdCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKGNhbnZhc1dyYXBwZXIsIHJlbmRlckNhbGxiYWNrLCBjb21wbGV0ZUNhbGxiYWNrKXtcbiAgdmFyIG1vZGVsID0gbmV3IE1vZGVsKCk7XG4gIHZhciByZW5kZXJMaXN0ID0gbmV3IFJlbmRlckxpc3QoKTtcblxuICBmdW5jdGlvbiBvblJlbmRlcih0KSB7XG4gICAgaWYgKHR5cGVvZiByZW5kZXJDYWxsYmFjayA9PT0gJ2Z1bmN0aW9uJyl7XG4gICAgICByZW5kZXJDYWxsYmFjaygpO1xuICAgIH1cbiAgICByZW5kZXJMaXN0LnJlbmRlcih0KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIG9uQ29tcGxldGUoKSB7XG4gICAgaWYgKHR5cGVvZiBjb21wbGV0ZUNhbGxiYWNrID09PSAnZnVuY3Rpb24nKXtcbiAgICAgIGNvbXBsZXRlQ2FsbGJhY2soKTtcbiAgICB9XG4gIH1cblxuICAvLyBJbml0aWFsaXplIHJlbmRlciBsaXN0XG4gIHJlbmRlckxpc3QuaW5pdChtb2RlbC53LCBtb2RlbC5oLCBtb2RlbC5zdHlsZXMsIG1vZGVsLmludGVycG9sYXRpb24pO1xuXG4gIC8vIEFkZCBhbGwgc2hhcGVzIGZyb20gdGhlIHNoYXBlIGxpc3RcbiAgZm9yICh2YXIga2V5IGluIHNoYXBlTGlzdCkge1xuICAgIGlmIChzaGFwZUxpc3QuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgdmFyIHNoYXBlTmFtZSA9IGtleTtcbiAgICAgIHJlbmRlckxpc3RbJ2FkZCcgKyBzaGFwZU5hbWVbMF0udG9VcHBlckNhc2UoKSArIHNoYXBlTmFtZS5zbGljZSgxKV0gPSAoZnVuY3Rpb24oc2hhcGVOYW1lLCBzaGFwZUxpc3Qpe1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24ocHJvcHMpe1xuICAgICAgICAgIHJlbmRlckxpc3QuYWRkU2hhcGUoc2hhcGVMaXN0W3NoYXBlTmFtZV0sIHByb3BzKTtcbiAgICAgICAgfVxuICAgICAgfSkoc2hhcGVOYW1lLCBzaGFwZUxpc3QpO1xuICAgIH1cbiAgfVxuXG4gIC8vIEluaXRpYWxpemUgc2NoZWR1bGVyXG4gIG1vZGVsLnNjaGVkdWxlci5pbml0KG9uUmVuZGVyLCBvbkNvbXBsZXRlKTtcbiAgdmFyIGNhbnZhc0VsID0gY2FudmFzV3JhcHBlci5hcHBlbmRDaGlsZChyZW5kZXJMaXN0LmdldENhbnZhcygpKTtcblxuICByZXR1cm4ge1xuICAgIHc6IG1vZGVsLncsXG4gICAgaDogbW9kZWwuaCxcbiAgICBtb2RlbCA6IG1vZGVsLFxuICAgIHJlbmRlckxpc3Q6IHJlbmRlckxpc3QsXG4gICAgc2l6ZTogZnVuY3Rpb24od2lkdGgsIGhlaWdodCkge1xuICAgICAgdGhpcy53ID0gbW9kZWwudyA9IHdpZHRoO1xuICAgICAgdGhpcy5oID0gbW9kZWwuaCA9IGhlaWdodDtcbiAgICAgIHJlbmRlckxpc3Quc2l6ZShtb2RlbC53LCBtb2RlbC5oKTtcbiAgICB9LFxuICAgIHN0eWxlcyA6IG1vZGVsLnN0eWxlcyxcbiAgICBwbGF5T25jZTogZnVuY3Rpb24oKXtcbiAgICAgIHJldHVybiBtb2RlbC5wbGF5T25jZSgpO1xuICAgIH0sXG4gICAgbG9vcDogZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gbW9kZWwubG9vcCgpO1xuICAgIH0sXG4gICAgZ2V0RHVyYXRpb24gOiBmdW5jdGlvbigpe1xuICAgICAgcmV0dXJuIG1vZGVsLmdldER1cmF0aW9uKCk7XG4gICAgfSxcbiAgICBzZXRGUFM6IGZ1bmN0aW9uKHZhbHVlKXtcbiAgICAgIG1vZGVsLnNldEZQUyh2YWx1ZSk7XG4gICAgfSxcbiAgICBzZXREdXJhdGlvbjogZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgIG1vZGVsLnNldER1cmF0aW9uKHZhbHVlKTtcbiAgICB9LFxuICAgIHNldE1vZGU6IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBtb2RlbC5pbnRlcnBvbGF0aW9uLm1vZGUgPSB2YWx1ZTtcbiAgICB9LFxuICAgIHNldEVhc2luZzogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICBtb2RlbC5pbnRlcnBvbGF0aW9uLmVhc2luZyA9IHZhbHVlO1xuICAgIH0sXG4gICAgc2V0TWF4Q29sb3JzOiBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgbW9kZWwubWF4Q29sb3JzID0gdmFsdWU7XG4gICAgfSxcbiAgICBjb2xvcjogY29sb3JMaWIsXG4gICAgY2FudmFzRWwgOiBjYW52YXNFbFxuICB9O1xufTsiLCIvKipcbiAqIEEgc2ltcGxlIG1vZGVsIGZvciB0aGUgQ2FudmFzIHJlbmRlcmVyXG4gKiBAdHlwZSB7e2luaXQsIGxvb3AsIHBsYXlPbmNlLCBzdG9wLCBpc1J1bm5pbmcsIHNldER1cmF0aW9uLCBnZXREdXJhdGlvbiwgc2V0RlBTLCBnZXRGUFN9fCp9XG4gKi9cbnZhciBTY2hlZHVsZXIgPSByZXF1aXJlKCcuL3NjaGVkdWxlcicpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG4gIHZhciBzY2hlZHVsZXIgPSBuZXcgU2NoZWR1bGVyKCk7XG5cbiAgdmFyIHN0eWxlcyA9IHtcbiAgICBiYWNrZ3JvdW5kQ29sb3I6IFwiI2ZmZmZmZlwiLFxuICAgIGxpbmVXaWR0aDogNSxcbiAgICBzdHJva2VTdHlsZTogXCIjMDAwMDAwXCIsXG4gICAgZmlsbFN0eWxlOiBcIiMwMDAwMDBcIixcbiAgICBsaW5lQ2FwOiBcInJvdW5kXCIsXG4gICAgbGluZUpvaW46IFwibWl0ZXJcIixcbiAgICBsaW5lRGFzaDogW10sXG4gICAgbWl0ZXJMaW1pdDogMTAsXG4gICAgc2hhZG93Q29sb3I6IG51bGwsXG4gICAgc2hhZG93T2Zmc2V0WDogMCxcbiAgICBzaGFkb3dPZmZzZXRZOiAwLFxuICAgIHNoYWRvd0JsdXI6IDAsXG4gICAgZ2xvYmFsQWxwaGE6IDEsXG4gICAgdHJhbnNsYXRpb25YOiAwLFxuICAgIHRyYW5zbGF0aW9uWTogMCxcbiAgICBzaGFrZTogMCxcbiAgICBibGVuZE1vZGU6IFwic291cmNlLW92ZXJcIlxuICB9O1xuXG4gIHJldHVybiB7XG4gICAgaW50ZXJwb2xhdGlvbjoge1xuICAgICAgbW9kZTogXCJib3VuY2VcIixcbiAgICAgIGVhc2luZzogdHJ1ZVxuICAgIH0sXG4gICAgbWF4Q29sb3JzOiAyNTYsXG4gICAgdzogNDAwLFxuICAgIGg6IDQwMCxcbiAgICBjYXB0dXJlOiBmYWxzZSxcbiAgICBzdHlsZXM6IHN0eWxlcyxcbiAgICBzY2hlZHVsZXIgOiBzY2hlZHVsZXIsXG4gICAgcGxheU9uY2UgOiBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBzY2hlZHVsZXIucGxheU9uY2UoKTtcbiAgICB9LFxuICAgIGxvb3AgOiBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBzY2hlZHVsZXIubG9vcCgpO1xuICAgIH0sXG4gICAgZ2V0RHVyYXRpb246IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHNjaGVkdWxlci5nZXREdXJhdGlvbigpO1xuICAgIH0sXG4gICAgc2V0RHVyYXRpb246IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBzY2hlZHVsZXIuc2V0RHVyYXRpb24odmFsdWUpO1xuICAgIH0sXG4gICAgZ2V0RlBTOiBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBzY2hlZHVsZXIuZ2V0RlBTKCk7XG4gICAgfSxcbiAgICBzZXRGUFM6IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBzY2hlZHVsZXIuc2V0RlBTKHZhbHVlKTtcbiAgICB9LFxuICAgIGdldElzUnVubmluZzogZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gc2NoZWR1bGVyLmlzUnVubmluZygpO1xuICAgIH1cbiAgfVxufTsiLCJ2YXIgU2hhcGUgPSByZXF1aXJlKCcuL3NoYXBlJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKXtcbiAgdmFyIHNoYXBlID0gbmV3IFNoYXBlKCk7XG5cbiAgdmFyIGNhbnZhcyA9IG51bGwsXG4gICAgY29udGV4dCA9IG51bGwsXG4gICAgd2lkdGggPSAwLFxuICAgIGhlaWdodCA9IDAsXG4gICAgbGlzdCA9IFtdLFxuICAgIHN0eWxlcyA9IG51bGw7XG5cbiAgZnVuY3Rpb24gaW5pdCh3LCBoLCBzdHlsZXNWYWx1ZSwgaW50ZXJwb2xhdGlvbikge1xuICAgIGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJjYW52YXNcIik7XG4gICAgd2lkdGggPSBjYW52YXMud2lkdGggPSB3O1xuICAgIGhlaWdodCA9IGNhbnZhcy5oZWlnaHQgPSBoO1xuICAgIGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dChcIjJkXCIpO1xuICAgIHN0eWxlcyA9IHN0eWxlc1ZhbHVlO1xuICAgIHNoYXBlLnN0eWxlcyA9IHN0eWxlcztcbiAgICBzaGFwZS5pbnRlcnBvbGF0aW9uID0gaW50ZXJwb2xhdGlvbjtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNpemUodywgaCkge1xuICAgIHdpZHRoID0gY2FudmFzLndpZHRoID0gdztcbiAgICBoZWlnaHQgPSBjYW52YXMuaGVpZ2h0ID0gaDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGFkZFNoYXBlKG5ld1NoYXBlLCBwcm9wcykge1xuICAgIHZhciBpdGVtID0gc2hhcGUuY3JlYXRlKG5ld1NoYXBlLCBwcm9wcyk7XG4gICAgbGlzdC5wdXNoKGl0ZW0pO1xuICAgIHJlbmRlcigwKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNsZWFyKCkge1xuICAgIGxpc3QubGVuZ3RoID0gMDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbmRlcih0KSB7XG4gICAgaWYoc3R5bGVzLmJhY2tncm91bmRDb2xvciA9PT0gXCJ0cmFuc3BhcmVudFwiKSB7XG4gICAgICBjb250ZXh0LmNsZWFyUmVjdCgwLCAwLCB3aWR0aCwgaGVpZ2h0KTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBjb250ZXh0LmZpbGxTdHlsZSA9IHN0eWxlcy5iYWNrZ3JvdW5kQ29sb3I7XG4gICAgICBjb250ZXh0LmZpbGxSZWN0KDAsIDAsIHdpZHRoLCBoZWlnaHQpO1xuICAgIH1cbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgbGlzdFtpXS5yZW5kZXIoY29udGV4dCwgdCk7XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZ2V0Q2FudmFzKCkge1xuICAgIHJldHVybiBjYW52YXM7XG4gIH1cblxuICBmdW5jdGlvbiBnZXRDb250ZXh0KCkge1xuICAgIHJldHVybiBjb250ZXh0O1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBpbml0OiBpbml0LFxuICAgIHNpemU6IHNpemUsXG4gICAgZ2V0Q2FudmFzOiBnZXRDYW52YXMsXG4gICAgZ2V0Q29udGV4dDogZ2V0Q29udGV4dCxcbiAgICBhZGRTaGFwZTogYWRkU2hhcGUsXG4gICAgY2xlYXI6IGNsZWFyLFxuICAgIHJlbmRlcjogcmVuZGVyXG4gIH07XG59OyIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gIHZhciB0ID0gMCxcbiAgICBkdXJhdGlvbiA9IDIsXG4gICAgZnBzID0gMzAsXG4gICAgcnVubmluZyA9IGZhbHNlLFxuICAgIGxvb3BpbmcgPSBmYWxzZSxcbiAgICByZW5kZXJDYWxsYmFjayA9IG51bGwsXG4gICAgY29tcGxldGVDYWxsYmFjayA9IG51bGw7XG5cbiAgZnVuY3Rpb24gaW5pdChvblJlbmRlciwgb25Db21wbGV0ZSkge1xuICAgIHJlbmRlckNhbGxiYWNrID0gb25SZW5kZXI7XG4gICAgY29tcGxldGVDYWxsYmFjayA9IG9uQ29tcGxldGU7XG4gIH1cblxuICBmdW5jdGlvbiByZW5kZXIoKSB7XG4gICAgaWYocnVubmluZykge1xuICAgICAgaWYocmVuZGVyQ2FsbGJhY2spIHtcbiAgICAgICAgcmVuZGVyQ2FsbGJhY2sodCk7XG4gICAgICB9XG4gICAgICBhZHZhbmNlKCk7XG4gICAgICBzZXRUaW1lb3V0KG9uVGltZW91dCwgMTAwMCAvIGZwcyk7XG4gICAgfVxuICAgIGVsc2UgaWYoY29tcGxldGVDYWxsYmFjaykge1xuICAgICAgY29tcGxldGVDYWxsYmFjaygpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIG9uVGltZW91dCgpIHtcbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVuZGVyKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGFkdmFuY2UoKSB7XG4gICAgdmFyIG51bUZyYW1lcyA9IGR1cmF0aW9uICogZnBzLFxuICAgICAgc3BlZWQgPSAxIC8gbnVtRnJhbWVzO1xuICAgIHQgKz0gc3BlZWQ7XG4gICAgaWYoTWF0aC5yb3VuZCh0ICogMTAwMDApIC8gMTAwMDAgPj0gMSkge1xuICAgICAgaWYobG9vcGluZykge1xuICAgICAgICB0IC09IDE7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdCA9IDA7XG4gICAgICAgIHN0b3AoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBsb29wKCkge1xuICAgIGlmKCFydW5uaW5nKSB7XG4gICAgICB0ID0gMDtcbiAgICAgIGxvb3BpbmcgPSB0cnVlO1xuICAgICAgcnVubmluZyA9IHRydWU7XG4gICAgICByZW5kZXIoKTtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzdG9wKCkge1xuICAgIHJ1bm5pbmcgPSBmYWxzZTtcbiAgICBsb29waW5nID0gZmFsc2U7XG4gICAgdCA9IDA7XG4gIH1cblxuICBmdW5jdGlvbiBwbGF5T25jZSgpIHtcbiAgICBpZighcnVubmluZykge1xuICAgICAgdCA9IDA7XG4gICAgICBsb29waW5nID0gZmFsc2U7XG4gICAgICBydW5uaW5nID0gdHJ1ZTtcbiAgICAgIHJlbmRlcigpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGlzUnVubmluZygpIHtcbiAgICByZXR1cm4gcnVubmluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIHNldER1cmF0aW9uKHZhbHVlKSB7XG4gICAgZHVyYXRpb24gPSB2YWx1ZTtcbiAgICByZXR1cm4gZHVyYXRpb247XG4gIH1cblxuICBmdW5jdGlvbiBnZXREdXJhdGlvbigpIHtcbiAgICByZXR1cm4gZHVyYXRpb247XG4gIH1cblxuICBmdW5jdGlvbiBzZXRGUFModmFsdWUpIHtcbiAgICBmcHMgPSB2YWx1ZTtcbiAgICByZXR1cm4gZnBzO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0RlBTKCkge1xuICAgIHJldHVybiBmcHM7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGluaXQ6IGluaXQsXG4gICAgbG9vcDogbG9vcCxcbiAgICBwbGF5T25jZTogcGxheU9uY2UsXG4gICAgc3RvcDogc3RvcCxcbiAgICBpc1J1bm5pbmc6IGlzUnVubmluZyxcbiAgICBzZXREdXJhdGlvbjogc2V0RHVyYXRpb24sXG4gICAgZ2V0RHVyYXRpb246IGdldER1cmF0aW9uLFxuICAgIHNldEZQUzogc2V0RlBTLFxuICAgIGdldEZQUzogZ2V0RlBTXG4gIH07XG59OyIsIi8qKlxuICogU2hhcGUgcHJvdG90eXBlIG9iamVjdFxuICogQHR5cGUge3tnZXROdW1iZXIsIGdldFN0cmluZywgZ2V0Qm9vbCwgZ2V0QXJyYXksIGdldE9iamVjdH18Kn1cbiAqL1xudmFyIHZhbHVlUGFyc2VyID0gcmVxdWlyZSgnLi9saWJzL3ZhbHVlUGFyc2VyJykoKTtcbnZhciBjb2xvclBhcnNlciA9IHJlcXVpcmUoJy4vbGlicy9jb2xvclBhcnNlcicpKCk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG5cbiAgcmV0dXJuIHtcbiAgICBzdHlsZXM6IG51bGwsXG4gICAgaW50ZXJwb2xhdGlvbjogbnVsbCxcblxuICAgIGNyZWF0ZTogZnVuY3Rpb24gKHR5cGUsIHByb3BzKSB7XG4gICAgICB2YXIgb2JqID0gT2JqZWN0LmNyZWF0ZSh0aGlzKTtcbiAgICAgIG9iai5pbml0KHR5cGUsIHByb3BzIHx8IHt9KTtcbiAgICAgIHJldHVybiBvYmo7XG4gICAgfSxcblxuICAgIGluaXQ6IGZ1bmN0aW9uICh0eXBlLCBwcm9wcykge1xuICAgICAgdGhpcy5wcm9wcyA9IHByb3BzO1xuICAgICAgZm9yKHZhciBwcm9wIGluIHByb3BzKSB7XG4gICAgICAgIHZhciBwID0gcHJvcHNbcHJvcF07XG4gICAgICAgIGlmKHR5cGVvZiBwID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICBwcm9wc1twcm9wXSA9IHAuYmluZChwcm9wcyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMuZHJhdyA9IHR5cGUuZHJhdztcbiAgICB9LFxuXG4gICAgcmVuZGVyOiBmdW5jdGlvbiAoY29udGV4dCwgdGltZSkge1xuICAgICAgdmFyIHQgPSB0aGlzLmludGVycG9sYXRlKHRpbWUpO1xuXG4gICAgICB0aGlzLnN0YXJ0RHJhdyhjb250ZXh0LCB0KTtcbiAgICAgIHRoaXMuZHJhdyhjb250ZXh0LCB0KTtcbiAgICAgIHRoaXMuZW5kRHJhdyhjb250ZXh0LCB0KTtcbiAgICB9LFxuXG4gICAgaW50ZXJwb2xhdGU6IGZ1bmN0aW9uICh0KSB7XG4gICAgICB0ICo9IHRoaXMucHJvcHMuc3BlZWRNdWx0IHx8IDE7XG4gICAgICB0ICs9IHRoaXMucHJvcHMucGhhc2UgfHwgMDtcblxuICAgICAgc3dpdGNoICh0aGlzLmludGVycG9sYXRpb24ubW9kZSkge1xuICAgICAgICBjYXNlIFwiYm91bmNlXCI6XG4gICAgICAgICAgaWYgKHRoaXMuaW50ZXJwb2xhdGlvbi5lYXNpbmcpIHtcbiAgICAgICAgICAgIHZhciBhID0gdCAqIE1hdGguUEkgKiAyO1xuICAgICAgICAgICAgcmV0dXJuIDAuNSAtIE1hdGguY29zKGEpICogMC41O1xuICAgICAgICAgIH1cbiAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHQgPSB0ICUgMTtcbiAgICAgICAgICAgIHJldHVybiB0IDwgMC41ID8gdCAqIDIgOiB0ID0gKDEgLSB0KSAqIDI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNhc2UgXCJzaW5nbGVcIjpcbiAgICAgICAgICBpZiAodCA+IDEpIHtcbiAgICAgICAgICAgIHQgJT0gMTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHRoaXMuaW50ZXJwb2xhdGlvbi5lYXNpbmcpIHtcbiAgICAgICAgICAgIHZhciBhID0gdCAqIE1hdGguUEk7XG4gICAgICAgICAgICByZXR1cm4gMC41IC0gTWF0aC5jb3MoYSkgKiAwLjU7XG4gICAgICAgICAgfVxuICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHQ7XG4gICAgICAgICAgfVxuICAgICAgfVxuXG4gICAgfSxcblxuICAgIHN0YXJ0RHJhdzogZnVuY3Rpb24gKGNvbnRleHQsIHQpIHtcbiAgICAgIGNvbnRleHQuc2F2ZSgpO1xuICAgICAgY29udGV4dC5saW5lV2lkdGggPSB0aGlzLmdldE51bWJlcihcImxpbmVXaWR0aFwiLCB0LCB0aGlzLnN0eWxlcy5saW5lV2lkdGgpO1xuICAgICAgY29udGV4dC5zdHJva2VTdHlsZSA9IHRoaXMuZ2V0Q29sb3IoXCJzdHJva2VTdHlsZVwiLCB0LCB0aGlzLnN0eWxlcy5zdHJva2VTdHlsZSk7XG4gICAgICBjb250ZXh0LmZpbGxTdHlsZSA9IHRoaXMuZ2V0Q29sb3IoXCJmaWxsU3R5bGVcIiwgdCwgdGhpcy5zdHlsZXMuZmlsbFN0eWxlKTtcbiAgICAgIGNvbnRleHQubGluZUNhcCA9IHRoaXMuZ2V0U3RyaW5nKFwibGluZUNhcFwiLCB0LCB0aGlzLnN0eWxlcy5saW5lQ2FwKTtcbiAgICAgIGNvbnRleHQubGluZUpvaW4gPSB0aGlzLmdldFN0cmluZyhcImxpbmVKb2luXCIsIHQsIHRoaXMuc3R5bGVzLmxpbmVKb2luKTtcbiAgICAgIGNvbnRleHQubWl0ZXJMaW1pdCA9IHRoaXMuZ2V0U3RyaW5nKFwibWl0ZXJMaW1pdFwiLCB0LCB0aGlzLnN0eWxlcy5taXRlckxpbWl0KTtcbiAgICAgIGNvbnRleHQuZ2xvYmFsQWxwaGEgPSB0aGlzLmdldE51bWJlcihcImdsb2JhbEFscGhhXCIsIHQsIHRoaXMuc3R5bGVzLmdsb2JhbEFscGhhKTtcbiAgICAgIGNvbnRleHQudHJhbnNsYXRlKHRoaXMuZ2V0TnVtYmVyKFwidHJhbnNsYXRpb25YXCIsIHQsIHRoaXMuc3R5bGVzLnRyYW5zbGF0aW9uWCksIHRoaXMuZ2V0TnVtYmVyKFwidHJhbnNsYXRpb25ZXCIsIHQsIHRoaXMuc3R5bGVzLnRyYW5zbGF0aW9uWSkpO1xuICAgICAgY29udGV4dC5nbG9iYWxDb21wb3NpdGVPcGVyYXRpb24gPSB0aGlzLmdldFN0cmluZyhcImJsZW5kTW9kZVwiLCB0LCB0aGlzLnN0eWxlcy5ibGVuZE1vZGUpO1xuICAgICAgdmFyIHNoYWtlID0gdGhpcy5nZXROdW1iZXIoXCJzaGFrZVwiLCB0LCB0aGlzLnN0eWxlcy5zaGFrZSk7XG4gICAgICBjb250ZXh0LnRyYW5zbGF0ZShNYXRoLnJhbmRvbSgpICogc2hha2UgLSBzaGFrZSAvIDIsIE1hdGgucmFuZG9tKCkgKiBzaGFrZSAtIHNoYWtlIC8gMik7XG5cbiAgICAgIHZhciBsaW5lRGFzaCA9IHRoaXMuZ2V0QXJyYXkoXCJsaW5lRGFzaFwiLCB0LCB0aGlzLnN0eWxlcy5saW5lRGFzaCk7XG4gICAgICBpZiAobGluZURhc2gpIHtcbiAgICAgICAgY29udGV4dC5zZXRMaW5lRGFzaChsaW5lRGFzaCk7XG4gICAgICB9XG4gICAgICBjb250ZXh0LmJlZ2luUGF0aCgpO1xuICAgIH0sXG5cbiAgICBkcmF3RmlsbEFuZFN0cm9rZTogZnVuY3Rpb24gKGNvbnRleHQsIHQsIGRvRmlsbCwgZG9TdHJva2UpIHtcbiAgICAgIHZhciBmaWxsID0gdGhpcy5nZXRCb29sKFwiZmlsbFwiLCB0LCBkb0ZpbGwpLFxuICAgICAgICBzdHJva2UgPSB0aGlzLmdldEJvb2woXCJzdHJva2VcIiwgdCwgZG9TdHJva2UpO1xuXG4gICAgICBjb250ZXh0LnNhdmUoKTtcbiAgICAgIGlmIChmaWxsKSB7XG4gICAgICAgIHRoaXMuc2V0U2hhZG93UGFyYW1zKGNvbnRleHQsIHQpO1xuICAgICAgICBjb250ZXh0LmZpbGwoKTtcbiAgICAgIH1cbiAgICAgIGNvbnRleHQucmVzdG9yZSgpO1xuICAgICAgaWYgKHN0cm9rZSkge1xuICAgICAgICBpZiAoIWZpbGwpIHtcbiAgICAgICAgICB0aGlzLnNldFNoYWRvd1BhcmFtcyhjb250ZXh0LCB0KTtcbiAgICAgICAgfVxuICAgICAgICBjb250ZXh0LnN0cm9rZSgpO1xuICAgICAgfVxuICAgIH0sXG5cbiAgICBzZXRTaGFkb3dQYXJhbXM6IGZ1bmN0aW9uIChjb250ZXh0LCB0KSB7XG4gICAgICBjb250ZXh0LnNoYWRvd0NvbG9yID0gdGhpcy5nZXRDb2xvcihcInNoYWRvd0NvbG9yXCIsIHQsIHRoaXMuc3R5bGVzLnNoYWRvd0NvbG9yKTtcbiAgICAgIGNvbnRleHQuc2hhZG93T2Zmc2V0WCA9IHRoaXMuZ2V0TnVtYmVyKFwic2hhZG93T2Zmc2V0WFwiLCB0LCB0aGlzLnN0eWxlcy5zaGFkb3dPZmZzZXRYKTtcbiAgICAgIGNvbnRleHQuc2hhZG93T2Zmc2V0WSA9IHRoaXMuZ2V0TnVtYmVyKFwic2hhZG93T2Zmc2V0WVwiLCB0LCB0aGlzLnN0eWxlcy5zaGFkb3dPZmZzZXRZKTtcbiAgICAgIGNvbnRleHQuc2hhZG93Qmx1ciA9IHRoaXMuZ2V0TnVtYmVyKFwic2hhZG93Qmx1clwiLCB0LCB0aGlzLnN0eWxlcy5zaGFkb3dCbHVyKTtcbiAgICB9LFxuXG4gICAgZW5kRHJhdzogZnVuY3Rpb24gKGNvbnRleHQpIHtcbiAgICAgIGNvbnRleHQucmVzdG9yZSgpO1xuICAgIH0sXG5cbiAgICBnZXROdW1iZXI6IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiB2YWx1ZVBhcnNlci5nZXROdW1iZXIodGhpcy5wcm9wc1twcm9wXSwgdCwgZGVmKTtcbiAgICB9LFxuXG4gICAgZ2V0Q29sb3I6IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiBjb2xvclBhcnNlci5nZXRDb2xvcih0aGlzLnByb3BzW3Byb3BdLCB0LCBkZWYpO1xuICAgIH0sXG5cbiAgICBnZXRTdHJpbmc6IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiB2YWx1ZVBhcnNlci5nZXRTdHJpbmcodGhpcy5wcm9wc1twcm9wXSwgdCwgZGVmKTtcbiAgICB9LFxuXG4gICAgZ2V0Qm9vbDogZnVuY3Rpb24gKHByb3AsIHQsIGRlZikge1xuICAgICAgcmV0dXJuIHZhbHVlUGFyc2VyLmdldEJvb2wodGhpcy5wcm9wc1twcm9wXSwgdCwgZGVmKTtcbiAgICB9LFxuXG4gICAgZ2V0QXJyYXk6IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiB2YWx1ZVBhcnNlci5nZXRBcnJheSh0aGlzLnByb3BzW3Byb3BdLCB0LCBkZWYpO1xuICAgIH0sXG5cbiAgICBnZXRPYmplY3Q6IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiB2YWx1ZVBhcnNlci5nZXRPYmplY3QodGhpcy5wcm9wc1twcm9wXSwgdCwgZGVmKTtcbiAgICB9LFxuXG4gICAgZ2V0UG9zaXRpb246IGZ1bmN0aW9uIChwcm9wLCB0LCBkZWYpIHtcbiAgICAgIHJldHVybiB2YWx1ZVBhcnNlci5nZXRQb3NpdGlvbih0aGlzLnByb3BzW3Byb3BdLCB0LCBkZWYpO1xuICAgIH1cbiAgfVxufTtcbiIsIi8qKlxuICogSW5jbHVkZSBhbGwgZGVmYXVsdCBzaGFwZXNcbiAqIEB0eXBlIHt7ZHJhd318Kn1cbiAqL1xudmFyIGNpcmNsZSAgICAgICAgICA9IHJlcXVpcmUoJy4vc2hhcGVzL2NpcmNsZScpKCk7XG52YXIgaGVhcnQgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvaGVhcnQnKSgpO1xudmFyIGFycm93ICAgICAgICAgICA9IHJlcXVpcmUoJy4vc2hhcGVzL2Fycm93JykoKTtcbnZhciBhcmNTZWdtZW50ICAgICAgPSByZXF1aXJlKCcuL3NoYXBlcy9hcmNTZWdtZW50JykoKTtcbnZhciBiZXppZXJDdXJ2ZSAgICAgPSByZXF1aXJlKCcuL3NoYXBlcy9iZXppZXJDdXJ2ZScpKCk7XG52YXIgYmV6aWVyU2VnbWVudCAgID0gcmVxdWlyZSgnLi9zaGFwZXMvYmV6aWVyU2VnbWVudCcpKCk7XG52YXIgY3ViZSAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvY3ViZScpKCk7XG52YXIgY3VydmUgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvY3VydmUnKSgpO1xudmFyIGN1cnZlU2VnbWVudCAgICA9IHJlcXVpcmUoJy4vc2hhcGVzL2N1cnZlU2VnbWVudCcpKCk7XG52YXIgZ2VhciAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvZ2VhcicpKCk7XG52YXIgbGluZSAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvbGluZScpKCk7XG52YXIgb3ZhbCAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvb3ZhbCcpKCk7XG52YXIgcGF0aCAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvcGF0aCcpKCk7XG52YXIgcG9seSAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvcG9seScpKCk7XG52YXIgcmF5U2VnbWVudCAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvcmF5U2VnbWVudCcpKCk7XG52YXIgcmVjdCAgICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvcmVjdCcpKCk7XG52YXIgc2VnbWVudCAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvc2VnbWVudCcpKCk7XG52YXIgc3BpcmFsICAgICAgICAgID0gcmVxdWlyZSgnLi9zaGFwZXMvc3BpcmFsJykoKTtcbnZhciBzdGFyICAgICAgICAgICAgPSByZXF1aXJlKCcuL3NoYXBlcy9zdGFyJykoKTtcbnZhciB0ZXh0ICAgICAgICAgICAgPSByZXF1aXJlKCcuL3NoYXBlcy90ZXh0JykoKTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIGNpcmNsZSA6IGNpcmNsZSxcbiAgYXJyb3cgOiBhcnJvdyxcbiAgYXJjU2VnbWVudDogYXJjU2VnbWVudCxcbiAgYmV6aWVyQ3VydmUgOiBiZXppZXJDdXJ2ZSxcbiAgYmV6aWVyU2VnbWVudCA6IGJlemllclNlZ21lbnQsXG4gIGN1YmUgOiBjdWJlLFxuICBjdXJ2ZSA6IGN1cnZlLFxuICBjdXJ2ZVNlZ21lbnQgOiBjdXJ2ZVNlZ21lbnQsXG4gIGdlYXIgOiBnZWFyLFxuICBsaW5lIDogbGluZSxcbiAgb3ZhbCA6IG92YWwsXG4gIHBhdGg6IHBhdGgsXG4gIHBvbHkgOiBwb2x5LFxuICByYXlTZWdtZW50IDogcmF5U2VnbWVudCxcbiAgcmVjdCA6IHJlY3QsXG4gIHNlZ21lbnQgOiBzZWdtZW50LFxuICBzcGlyYWwgOiBzcGlyYWwsXG4gIHN0YXIgOiBzdGFyLFxuICB0ZXh0IDogdGV4dCxcbiAgaGVhcnQgOiBoZWFydFxufTsiLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHRyYWRpdXMgPSB0aGlzLmdldE51bWJlcihcInJhZGl1c1wiLCB0LCA1MCksXG5cdFx0XHRcdHN0YXJ0QW5nbGUgPSB0aGlzLmdldE51bWJlcihcInN0YXJ0QW5nbGVcIiwgdCwgMCksXG5cdFx0XHRcdGVuZEFuZ2xlID0gdGhpcy5nZXROdW1iZXIoXCJlbmRBbmdsZVwiLCB0LCAzNjApO1xuXG5cdFx0XHRpZihzdGFydEFuZ2xlID4gZW5kQW5nbGUpIHtcblx0XHRcdFx0dmFyIHRlbXAgPSBzdGFydEFuZ2xlO1xuXHRcdFx0XHRzdGFydEFuZ2xlID0gZW5kQW5nbGU7XG5cdFx0XHRcdGVuZEFuZ2xlID0gdGVtcDtcblx0XHRcdH1cblx0XHRcdHZhciBhcmMgPSB0aGlzLmdldE51bWJlcihcImFyY1wiLCB0LCAyMCksXG5cdFx0XHRcdHN0YXJ0ID0gc3RhcnRBbmdsZSAtIDEsXG5cdFx0XHRcdGVuZCA9IHN0YXJ0QW5nbGUgKyB0ICogKGVuZEFuZ2xlIC0gc3RhcnRBbmdsZSArIGFyYyk7XG5cblx0XHRcdGlmKGVuZCA+IHN0YXJ0QW5nbGUgKyBhcmMpIHtcblx0XHRcdFx0c3RhcnQgPSBlbmQgLSBhcmM7XG5cdFx0XHR9XG5cdFx0XHRpZihlbmQgPiBlbmRBbmdsZSkge1xuXHRcdFx0XHRlbmQgPSBlbmRBbmdsZSArIDE7XG5cdFx0XHR9XG5cblx0XHRcdGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuXHRcdFx0Y29udGV4dC5yb3RhdGUodGhpcy5nZXROdW1iZXIoXCJyb3RhdGlvblwiLCB0LCAwKSAqIE1hdGguUEkgLyAxODApO1xuXHRcdFx0Y29udGV4dC5hcmMoMCwgMCwgcmFkaXVzLCBzdGFydCAqIE1hdGguUEkgLyAxODAsIGVuZCAqIE1hdGguUEkgLyAxODApO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIGZhbHNlLCB0cnVlKTtcdFx0XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXHRyZXR1cm4ge1xuXHRcdGRyYXc6IGZ1bmN0aW9uKGNvbnRleHQsIHQpIHtcblx0XHRcdHZhciB4ID0gdGhpcy5nZXROdW1iZXIoXCJ4XCIsIHQsIDEwMCksXG5cdFx0XHRcdHkgPSB0aGlzLmdldE51bWJlcihcInlcIiwgdCwgMTAwKSxcblx0XHRcdFx0dyA9IHRoaXMuZ2V0TnVtYmVyKFwid1wiLCB0LCAxMDApLFxuXHRcdFx0XHRoID0gdGhpcy5nZXROdW1iZXIoXCJoXCIsIHQsIDEwMCksXG5cdFx0XHRcdHBvaW50UGVyY2VudCA9IHRoaXMuZ2V0TnVtYmVyKFwicG9pbnRQZXJjZW50XCIsIHQsIDAuNSksXG5cdFx0XHRcdHNoYWZ0UGVyY2VudCA9IHRoaXMuZ2V0TnVtYmVyKFwic2hhZnRQZXJjZW50XCIsIHQsIDAuNSk7XG5cdFx0XHRcdFxuXHRcdFx0Y29udGV4dC50cmFuc2xhdGUoeCwgeSk7XG5cdFx0XHRjb250ZXh0LnJvdGF0ZSh0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCk7XG5cblx0XHRcdC8vIGNvbnRleHQudHJhbnNsYXRlKC13IC8gMiwgMCk7XG5cblx0XHRcdGNvbnRleHQubW92ZVRvKC13IC8gMiwgLWggKiBzaGFmdFBlcmNlbnQgKiAwLjUpO1xuXHRcdFx0Y29udGV4dC5saW5lVG8odyAvIDIgLSB3ICogcG9pbnRQZXJjZW50LCAtaCAqIHNoYWZ0UGVyY2VudCAqIDAuNSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyh3IC8gMiAtIHcgKiBwb2ludFBlcmNlbnQsIC1oICogMC41KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHcgLyAyLCAwKTtcblx0XHRcdGNvbnRleHQubGluZVRvKHcgLyAyIC0gdyAqIHBvaW50UGVyY2VudCwgaCAqIDAuNSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyh3IC8gMiAtIHcgKiBwb2ludFBlcmNlbnQsIGggKiBzaGFmdFBlcmNlbnQgKiAwLjUpO1xuXHRcdFx0Y29udGV4dC5saW5lVG8oLXcgLyAyLCBoICogc2hhZnRQZXJjZW50ICogMC41KTtcblx0XHRcdGNvbnRleHQubGluZVRvKC13IC8gMiwgLWggKiBzaGFmdFBlcmNlbnQgKiAwLjUpO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIHRydWUsIGZhbHNlKTtcblx0XHR9XG5cdH1cbn07IiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHgwID0gdGhpcy5nZXROdW1iZXIoXCJ4MFwiLCB0LCA1MCksXG5cdFx0XHRcdHkwID0gdGhpcy5nZXROdW1iZXIoXCJ5MFwiLCB0LCAxMCksXG5cdFx0XHRcdHgxID0gdGhpcy5nZXROdW1iZXIoXCJ4MVwiLCB0LCAyMDApLFxuXHRcdFx0XHR5MSA9IHRoaXMuZ2V0TnVtYmVyKFwieTFcIiwgdCwgMTAwKSxcblx0XHRcdFx0eDIgPSB0aGlzLmdldE51bWJlcihcIngyXCIsIHQsIDApLFxuXHRcdFx0XHR5MiA9IHRoaXMuZ2V0TnVtYmVyKFwieTJcIiwgdCwgMTAwKSxcblx0XHRcdFx0eDMgPSB0aGlzLmdldE51bWJlcihcIngzXCIsIHQsIDE1MCksXG5cdFx0XHRcdHkzID0gdGhpcy5nZXROdW1iZXIoXCJ5M1wiLCB0LCAxMCk7XG5cblx0XHQgICAgY29udGV4dC5tb3ZlVG8oeDAsIHkwKTtcblx0XHQgICAgY29udGV4dC5iZXppZXJDdXJ2ZVRvKHgxLCB5MSwgeDIsIHkyLCB4MywgeTMpO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIGZhbHNlLCB0cnVlKTtcdFxuXHRcdH1cblx0fVxufTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKXtcblxuXHRmdW5jdGlvbiBiZXppZXIodCwgdjAsIHYxLCB2MiwgdjMpIHtcblx0XHRyZXR1cm4gKDEgLSB0KSAqICgxIC0gdCkgKiAoMSAtIHQpICogdjAgKyAzICogKDEgLSB0KSAqICgxIC0gdCkgKiB0ICogdjEgKyAzICogKDEgLSB0KSAqIHQgKiB0ICogdjIgKyB0ICogdCAqIHQgKiB2Mztcblx0fVxuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHgwID0gdGhpcy5nZXROdW1iZXIoXCJ4MFwiLCB0LCA1MCksXG5cdFx0XHRcdHkwID0gdGhpcy5nZXROdW1iZXIoXCJ5MFwiLCB0LCAxMCksXG5cdFx0XHRcdHgxID0gdGhpcy5nZXROdW1iZXIoXCJ4MVwiLCB0LCAyMDApLFxuXHRcdFx0XHR5MSA9IHRoaXMuZ2V0TnVtYmVyKFwieTFcIiwgdCwgMTAwKSxcblx0XHRcdFx0eDIgPSB0aGlzLmdldE51bWJlcihcIngyXCIsIHQsIDApLFxuXHRcdFx0XHR5MiA9IHRoaXMuZ2V0TnVtYmVyKFwieTJcIiwgdCwgMTAwKSxcblx0XHRcdFx0eDMgPSB0aGlzLmdldE51bWJlcihcIngzXCIsIHQsIDE1MCksXG5cdFx0XHRcdHkzID0gdGhpcy5nZXROdW1iZXIoXCJ5M1wiLCB0LCAxMCksXG5cdFx0XHRcdHBlcmNlbnQgPSB0aGlzLmdldE51bWJlcihcInBlcmNlbnRcIiwgdCwgMC4xKSxcblx0XHRcdFx0dDEgPSB0ICogKDEgKyBwZXJjZW50KSxcblx0XHRcdFx0dDAgPSB0MSAtIHBlcmNlbnQsXG5cdFx0XHRcdHJlcyA9IDAuMDEsXG5cdFx0XHRcdHgsXG5cdFx0XHRcdHk7XG5cblx0XHRcdHQxID0gTWF0aC5taW4odDEsIDEuMDAxKTtcblx0XHRcdHQwID0gTWF0aC5tYXgodDAsIC0wLjAwMSk7XG5cblx0XHRcdGZvcih2YXIgaSA9IHQwOyBpIDwgdDE7IGkgKz0gcmVzKSB7XG5cdFx0XHRcdHggPSBiZXppZXIoaSwgeDAsIHgxLCB4MiwgeDMpO1xuXHRcdFx0XHR5ID0gYmV6aWVyKGksIHkwLCB5MSwgeTIsIHkzKTtcblx0XHRcdFx0aWYoaSA9PT0gdDApIHtcblx0XHRcdFx0ICAgIGNvbnRleHQubW92ZVRvKHgsIHkpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2Uge1xuXHRcdCAgICBcdFx0Y29udGV4dC5saW5lVG8oeCwgeSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHggPSBiZXppZXIodDEsIHgwLCB4MSwgeDIsIHgzKTtcblx0XHRcdHkgPSBiZXppZXIodDEsIHkwLCB5MSwgeTIsIHkzKTtcblx0ICAgXHRcdGNvbnRleHQubGluZVRvKHgsIHkpO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIGZhbHNlLCB0cnVlKTtcblx0XHR9XG5cdH1cbn07XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG4gIHJldHVybiB7XG4gICAgZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuICAgICAgdmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcbiAgICAgICAgeSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuICAgICAgICByYWRpdXMgPSB0aGlzLmdldE51bWJlcihcInJhZGl1c1wiLCB0LCA1MCksXG4gICAgICAgIHN0YXJ0QW5nbGUgPSB0aGlzLmdldE51bWJlcihcInN0YXJ0QW5nbGVcIiwgdCwgMCksXG4gICAgICAgIGVuZEFuZ2xlID0gdGhpcy5nZXROdW1iZXIoXCJlbmRBbmdsZVwiLCB0LCAzNjApLFxuICAgICAgICBkcmF3RnJvbUNlbnRlciA9IHRoaXMuZ2V0Qm9vbChcImRyYXdGcm9tQ2VudGVyXCIsIHQsIGZhbHNlKTtcblxuICAgICAgY29udGV4dC50cmFuc2xhdGUoeCwgeSk7XG4gICAgICBjb250ZXh0LnJvdGF0ZSh0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCk7XG4gICAgICBpZihkcmF3RnJvbUNlbnRlcikge1xuICAgICAgICBjb250ZXh0Lm1vdmVUbygwLCAwKTtcbiAgICAgIH1cbiAgICAgIGNvbnRleHQuYXJjKDAsIDAsIHJhZGl1cywgc3RhcnRBbmdsZSAqIE1hdGguUEkgLyAxODAsIGVuZEFuZ2xlICogTWF0aC5QSSAvIDE4MCk7XG4gICAgICBpZihkcmF3RnJvbUNlbnRlcikge1xuICAgICAgICBjb250ZXh0LmNsb3NlUGF0aCgpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIHRydWUsIGZhbHNlKTtcbiAgICB9XG4gIH1cbn07IiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHR6ID0gdGhpcy5nZXROdW1iZXIoXCJ6XCIsIHQsIDApLFxuXHRcdFx0XHRzaXplID0gdGhpcy5nZXROdW1iZXIoXCJzaXplXCIsIHQsIDEwMCksXG5cdFx0XHRcdHJvdGF0aW9uWCA9IHRoaXMuZ2V0TnVtYmVyKFwicm90YXRpb25YXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCxcblx0XHRcdFx0cm90YXRpb25ZID0gdGhpcy5nZXROdW1iZXIoXCJyb3RhdGlvbllcIiwgdCwgMCkgKiBNYXRoLlBJIC8gMTgwLFxuXHRcdFx0XHRyb3RhdGlvblogPSB0aGlzLmdldE51bWJlcihcInJvdGF0aW9uWlwiLCB0LCAwKSAqIE1hdGguUEkgLyAxODA7XG5cblx0XHRcdHZhciBwb2ludHMgPSBtYWtlUG9pbnRzKCk7XG5cdFx0XHRzY2FsZShwb2ludHMsIHNpemUgLyAyKTtcblx0XHRcdHJvdGF0ZVgocG9pbnRzLCByb3RhdGlvblgpO1xuXHRcdFx0cm90YXRlWShwb2ludHMsIHJvdGF0aW9uWSk7XG5cdFx0XHRyb3RhdGVaKHBvaW50cywgcm90YXRpb25aKTtcblx0XHRcdHByb2plY3QocG9pbnRzLCB6KTtcblxuXHRcdFx0Y29udGV4dC5saW5lSm9pbiA9IHRoaXMuZ2V0U3RyaW5nKFwibGluZUpvaW5cIiwgdCwgXCJyb3VuZFwiKTtcblx0XHRcdGNvbnRleHQubGluZVdpZHRoID0gdGhpcy5nZXROdW1iZXIoXCJsaW5lV2lkdGhcIiwgdCwgMSk7XG5cblx0XHRcdGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuXG5cdFx0XHRjb250ZXh0Lm1vdmVUbyhwb2ludHNbMF0uc3gsIHBvaW50c1swXS5zeSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhwb2ludHNbMV0uc3gsIHBvaW50c1sxXS5zeSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhwb2ludHNbMl0uc3gsIHBvaW50c1syXS5zeSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhwb2ludHNbM10uc3gsIHBvaW50c1szXS5zeSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhwb2ludHNbMF0uc3gsIHBvaW50c1swXS5zeSk7XG5cblx0XHRcdGNvbnRleHQubW92ZVRvKHBvaW50c1s0XS5zeCwgcG9pbnRzWzRdLnN5KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHBvaW50c1s1XS5zeCwgcG9pbnRzWzVdLnN5KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHBvaW50c1s2XS5zeCwgcG9pbnRzWzZdLnN5KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHBvaW50c1s3XS5zeCwgcG9pbnRzWzddLnN5KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHBvaW50c1s0XS5zeCwgcG9pbnRzWzRdLnN5KTtcblxuXHRcdFx0Y29udGV4dC5tb3ZlVG8ocG9pbnRzWzBdLnN4LCBwb2ludHNbMF0uc3kpO1xuXHRcdFx0Y29udGV4dC5saW5lVG8ocG9pbnRzWzRdLnN4LCBwb2ludHNbNF0uc3kpO1xuXG5cdFx0XHRjb250ZXh0Lm1vdmVUbyhwb2ludHNbMV0uc3gsIHBvaW50c1sxXS5zeSk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhwb2ludHNbNV0uc3gsIHBvaW50c1s1XS5zeSk7XG5cblx0XHRcdGNvbnRleHQubW92ZVRvKHBvaW50c1syXS5zeCwgcG9pbnRzWzJdLnN5KTtcblx0XHRcdGNvbnRleHQubGluZVRvKHBvaW50c1s2XS5zeCwgcG9pbnRzWzZdLnN5KTtcblxuXHRcdFx0Y29udGV4dC5tb3ZlVG8ocG9pbnRzWzNdLnN4LCBwb2ludHNbM10uc3kpO1xuXHRcdFx0Y29udGV4dC5saW5lVG8ocG9pbnRzWzddLnN4LCBwb2ludHNbN10uc3kpO1xuXG5cdFx0XHR0aGlzLnNldFNoYWRvd1BhcmFtcyhjb250ZXh0LCB0KTtcblx0XHRcdGNvbnRleHQuc3Ryb2tlKCk7XHRcdFxuXHRcdH1cblx0fVxuXHRcblx0ZnVuY3Rpb24gc2NhbGUocG9pbnRzLCBzaXplKSB7XG5cdFx0Zm9yKHZhciBpID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkrKykge1xuXHRcdFx0dmFyIHAgPSBwb2ludHNbaV07XG5cdFx0XHRwLnggKj0gc2l6ZTtcblx0XHRcdHAueSAqPSBzaXplO1xuXHRcdFx0cC56ICo9IHNpemU7XG5cdFx0fVxuXHR9XG5cblx0ZnVuY3Rpb24gcm90YXRlWChwb2ludHMsIGFuZ2xlKSB7XG5cdFx0dmFyIGNvcyA9IE1hdGguY29zKGFuZ2xlKSxcblx0XHRcdHNpbiA9IE1hdGguc2luKGFuZ2xlKTtcblx0XHRmb3IodmFyIGkgPSAwOyBpIDwgcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHR2YXIgcCA9IHBvaW50c1tpXSxcblx0XHRcdFx0eSA9IHAueSAqIGNvcyAtIHAueiAqIHNpbixcblx0XHRcdFx0eiA9IHAueiAqIGNvcyArIHAueSAqIHNpbjtcblx0XHRcdHAueSA9IHk7XG5cdFx0XHRwLnogPSB6O1xuXHRcdH1cblx0fVxuXG5cdGZ1bmN0aW9uIHJvdGF0ZVkocG9pbnRzLCBhbmdsZSkge1xuXHRcdHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSksXG5cdFx0XHRzaW4gPSBNYXRoLnNpbihhbmdsZSk7XG5cdFx0Zm9yKHZhciBpID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkrKykge1xuXHRcdFx0dmFyIHAgPSBwb2ludHNbaV0sXG5cdFx0XHRcdHggPSBwLnggKiBjb3MgLSBwLnogKiBzaW4sXG5cdFx0XHRcdHogPSBwLnogKiBjb3MgKyBwLnggKiBzaW47XG5cdFx0XHRwLnggPSB4O1xuXHRcdFx0cC56ID0gejtcblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiByb3RhdGVaKHBvaW50cywgYW5nbGUpIHtcblx0XHR2YXIgY29zID0gTWF0aC5jb3MoYW5nbGUpLFxuXHRcdFx0c2luID0gTWF0aC5zaW4oYW5nbGUpO1xuXHRcdGZvcih2YXIgaSA9IDA7IGkgPCBwb2ludHMubGVuZ3RoOyBpKyspIHtcblx0XHRcdHZhciBwID0gcG9pbnRzW2ldLFxuXHRcdFx0XHR4ID0gcC54ICogY29zIC0gcC55ICogc2luLFxuXHRcdFx0XHR5ID0gcC55ICogY29zICsgcC54ICogc2luO1xuXHRcdFx0cC54ID0geDtcblx0XHRcdHAueSA9IHk7XG5cdFx0fVxuXHR9XG5cblx0ZnVuY3Rpb24gcHJvamVjdChwb2ludHMsIHopIHtcblx0XHR2YXIgZmwgPSAzMDA7XG5cdFx0Zm9yKHZhciBpID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkrKykge1xuXHRcdFx0dmFyIHAgPSBwb2ludHNbaV0sXG5cdFx0XHRcdHNjYWxlID0gZmwgLyAoZmwgKyBwLnogKyB6KTtcblx0XHRcdHAuc3ggPSBwLnggKiBzY2FsZTtcblx0XHRcdHAuc3kgPSBwLnkgKiBzY2FsZTtcblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiBtYWtlUG9pbnRzKCkge1xuXHRcdHJldHVybiBbXG5cdFx0XHR7XG5cdFx0XHRcdHg6IC0xLFxuXHRcdFx0XHR5OiAtMSxcblx0XHRcdFx0ejogLTFcblx0XHRcdH0sXG5cdFx0XHR7XG5cdFx0XHRcdHg6IDEsXG5cdFx0XHRcdHk6IC0xLFxuXHRcdFx0XHR6OiAtMVxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0eDogMSxcblx0XHRcdFx0eTogMSxcblx0XHRcdFx0ejogLTFcblx0XHRcdH0sXG5cdFx0XHR7XG5cdFx0XHRcdHg6IC0xLFxuXHRcdFx0XHR5OiAxLFxuXHRcdFx0XHR6OiAtMVxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0eDogLTEsXG5cdFx0XHRcdHk6IC0xLFxuXHRcdFx0XHR6OiAxXG5cdFx0XHR9LFxuXHRcdFx0e1xuXHRcdFx0XHR4OiAxLFxuXHRcdFx0XHR5OiAtMSxcblx0XHRcdFx0ejogMVxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0eDogMSxcblx0XHRcdFx0eTogMSxcblx0XHRcdFx0ejogMVxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0eDogLTEsXG5cdFx0XHRcdHk6IDEsXG5cdFx0XHRcdHo6IDFcblx0XHRcdH1cblx0XHRdO1xuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHgwID0gdGhpcy5nZXROdW1iZXIoXCJ4MFwiLCB0LCAyMCksXG5cdFx0XHRcdHkwID0gdGhpcy5nZXROdW1iZXIoXCJ5MFwiLCB0LCAxMCksXG5cdFx0XHRcdHgxID0gdGhpcy5nZXROdW1iZXIoXCJ4MVwiLCB0LCAxMDApLFxuXHRcdFx0XHR5MSA9IHRoaXMuZ2V0TnVtYmVyKFwieTFcIiwgdCwgMjAwKSxcblx0XHRcdFx0eDIgPSB0aGlzLmdldE51bWJlcihcIngyXCIsIHQsIDE4MCksXG5cdFx0XHRcdHkyID0gdGhpcy5nZXROdW1iZXIoXCJ5MlwiLCB0LCAxMCk7XG5cblx0XHQgICAgY29udGV4dC5tb3ZlVG8oeDAsIHkwKTtcblx0XHQgICAgY29udGV4dC5xdWFkcmF0aWNDdXJ2ZVRvKHgxLCB5MSwgeDIsIHkyKTtcblxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCBmYWxzZSwgdHJ1ZSk7XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdGZ1bmN0aW9uIHF1YWRyYXRpYyh0LCB2MCwgdjEsIHYyKSB7XG5cdFx0cmV0dXJuICgxIC0gdCkgKiAoMSAtIHQpICogdjAgKyAyICogKDEgLSB0KSAqIHQgKiB2MSArIHQgKiB0ICogdjI7XG5cdH1cblxuXHRyZXR1cm4ge1xuXHRcdGRyYXc6IGZ1bmN0aW9uKGNvbnRleHQsIHQpIHtcblx0XHRcdHZhciB4MCA9IHRoaXMuZ2V0TnVtYmVyKFwieDBcIiwgdCwgMjApLFxuXHRcdFx0XHR5MCA9IHRoaXMuZ2V0TnVtYmVyKFwieTBcIiwgdCwgMjApLFxuXHRcdFx0XHR4MSA9IHRoaXMuZ2V0TnVtYmVyKFwieDFcIiwgdCwgMTAwKSxcblx0XHRcdFx0eTEgPSB0aGlzLmdldE51bWJlcihcInkxXCIsIHQsIDIwMCksXG5cdFx0XHRcdHgyID0gdGhpcy5nZXROdW1iZXIoXCJ4MlwiLCB0LCAxODApLFxuXHRcdFx0XHR5MiA9IHRoaXMuZ2V0TnVtYmVyKFwieTJcIiwgdCwgMjApLFxuXHRcdFx0XHRwZXJjZW50ID0gdGhpcy5nZXROdW1iZXIoXCJwZXJjZW50XCIsIHQsIDAuMSksXG5cdFx0XHRcdHQxID0gdCAqICgxICsgcGVyY2VudCksXG5cdFx0XHRcdHQwID0gdDEgLSBwZXJjZW50LFxuXHRcdFx0XHRyZXMgPSAwLjAxLFxuXHRcdFx0XHR4LFxuXHRcdFx0XHR5O1xuXG5cdFx0XHR0MSA9IE1hdGgubWluKHQxLCAxKTtcblx0XHRcdHQwID0gTWF0aC5tYXgodDAsIDApO1xuXG5cdFx0XHRmb3IodmFyIGkgPSB0MDsgaSA8IHQxOyBpICs9IHJlcykge1xuXHRcdFx0XHR4ID0gcXVhZHJhdGljKGksIHgwLCB4MSwgeDIpO1xuXHRcdFx0XHR5ID0gcXVhZHJhdGljKGksIHkwLCB5MSwgeTIpO1xuXHRcdFx0XHRpZihpID09PSB0MCkge1xuXHRcdFx0XHQgICAgY29udGV4dC5tb3ZlVG8oeCwgeSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSB7XG5cdFx0ICAgIFx0XHRjb250ZXh0LmxpbmVUbyh4LCB5KTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0eCA9IHF1YWRyYXRpYyh0MSwgeDAsIHgxLCB4Mik7XG5cdFx0XHR5ID0gcXVhZHJhdGljKHQxLCB5MCwgeTEsIHkyKTtcblx0ICAgXHRcdGNvbnRleHQubGluZVRvKHgsIHkpO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIGZhbHNlLCB0cnVlKTtcdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHRyYWRpdXMgPSB0aGlzLmdldE51bWJlcihcInJhZGl1c1wiLCB0LCA1MCksXG5cdFx0XHRcdHRvb3RoSGVpZ2h0ID0gdGhpcy5nZXROdW1iZXIoXCJ0b290aEhlaWdodFwiLCB0LCAxMCksXG5cdFx0XHRcdGh1YiA9IHRoaXMuZ2V0TnVtYmVyKFwiaHViXCIsIHQsIDEwKSxcblx0XHRcdFx0cm90YXRpb24gPSB0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCxcblx0XHRcdFx0dGVldGggPSB0aGlzLmdldE51bWJlcihcInRlZXRoXCIsIHQsIDEwKSxcblx0XHRcdFx0dG9vdGhBbmdsZSA9IHRoaXMuZ2V0TnVtYmVyKFwidG9vdGhBbmdsZVwiLCB0LCAwLjMpLFxuXHRcdFx0XHRmYWNlID0gMC41IC0gdG9vdGhBbmdsZSAvIDIsXG5cdFx0XHRcdHNpZGUgPSAwLjUgLSBmYWNlLFxuXHRcdFx0XHRpbm5lclJhZGl1cyA9IHJhZGl1cyAtIHRvb3RoSGVpZ2h0O1xuXG5cdFx0XHRjb250ZXh0LnRyYW5zbGF0ZSh4LCB5KTtcblx0XHRcdGNvbnRleHQucm90YXRlKHJvdGF0aW9uKTtcblx0XHRcdGNvbnRleHQuc2F2ZSgpO1xuXHRcdFx0Y29udGV4dC5tb3ZlVG8ocmFkaXVzLCAwKTtcblx0XHRcdHZhciBhbmdsZSA9IE1hdGguUEkgKiAyIC8gdGVldGg7XG5cblx0XHRcdGZvcih2YXIgaSA9IDA7IGkgPCB0ZWV0aDsgaSsrKSB7XG5cdFx0XHRcdGNvbnRleHQucm90YXRlKGFuZ2xlICogZmFjZSk7XG5cdFx0XHRcdGNvbnRleHQubGluZVRvKHJhZGl1cywgMCk7XG5cdFx0XHRcdGNvbnRleHQucm90YXRlKGFuZ2xlICogc2lkZSk7XG5cdFx0XHRcdGNvbnRleHQubGluZVRvKGlubmVyUmFkaXVzLCAwKTtcblx0XHRcdFx0Y29udGV4dC5yb3RhdGUoYW5nbGUgKiBmYWNlKTtcblx0XHRcdFx0Y29udGV4dC5saW5lVG8oaW5uZXJSYWRpdXMsIDApO1xuXHRcdFx0XHRjb250ZXh0LnJvdGF0ZShhbmdsZSAqIHNpZGUpO1xuXHRcdFx0XHRjb250ZXh0LmxpbmVUbyhyYWRpdXMsIDApO1xuXHRcdFx0fVxuXHRcdFx0Y29udGV4dC5saW5lVG8ocmFkaXVzLCAwKTtcblx0XHRcdGNvbnRleHQucmVzdG9yZSgpO1xuXG5cdFx0XHRjb250ZXh0Lm1vdmVUbyhodWIsIDApO1xuXHRcdFx0Y29udGV4dC5hcmMoMCwgMCwgaHViLCAwLCBNYXRoLlBJICogMiwgdHJ1ZSk7XG5cblx0XHRcdHRoaXMuZHJhd0ZpbGxBbmRTdHJva2UoY29udGV4dCwgdCwgdHJ1ZSwgZmFsc2UpO1xuXHRcdH1cblx0fVxufTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuICAgICAgdmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcbiAgICAgICAgeSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuICAgICAgICB3ID0gdGhpcy5nZXROdW1iZXIoXCJ3XCIsIHQsIDUwKSxcbiAgICAgICAgaCA9IHRoaXMuZ2V0TnVtYmVyKFwiaFwiLCB0LCA1MCk7XG5cbiAgICAgIHZhciB4MCA9IDAsXG4gICAgICAgIHkwID0gLS4yNSxcbiAgICAgICAgeDEgPSAuMixcbiAgICAgICAgeTEgPSAtLjgsXG4gICAgICAgIHgyID0gMS4xLFxuICAgICAgICB5MiA9IC0uMixcbiAgICAgICAgeDMgPSAwLFxuICAgICAgICB5MyA9IC41O1xuXG4gICAgICBjb250ZXh0LnNhdmUoKTtcbiAgICAgIGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuICAgICAgY29udGV4dC5yb3RhdGUodGhpcy5nZXROdW1iZXIoXCJyb3RhdGlvblwiLCB0LCAwKSAqIE1hdGguUEkgLyAxODApO1xuICAgICAgY29udGV4dC5zYXZlKCk7XG4gICAgICBjb250ZXh0LnNjYWxlKHcsIGgpO1xuICAgICAgY29udGV4dC5tb3ZlVG8oeDAsIHkwKTtcbiAgICAgIGNvbnRleHQuYmV6aWVyQ3VydmVUbyh4MSwgeTEsIHgyLCB5MiwgeDMsIHkzKTtcbiAgICAgIGNvbnRleHQuYmV6aWVyQ3VydmVUbygteDIsIHkyLCAteDEsIHkxLCAteDAsIHkwKTtcbiAgICAgIGNvbnRleHQucmVzdG9yZSgpO1xuICAgICAgdGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCB0cnVlLCBmYWxzZSk7XG4gICAgICBjb250ZXh0LnJlc3RvcmUoKTtcbiAgICB9XG4gIH1cbn07IiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHgwID0gdGhpcy5nZXROdW1iZXIoXCJ4MFwiLCB0LCAwKSxcblx0XHRcdFx0eTAgPSB0aGlzLmdldE51bWJlcihcInkwXCIsIHQsIDApLFxuXHRcdFx0XHR4MSA9IHRoaXMuZ2V0TnVtYmVyKFwieDFcIiwgdCwgMTAwKSxcblx0XHRcdFx0eTEgPSB0aGlzLmdldE51bWJlcihcInkxXCIsIHQsIDEwMCk7XG5cdFx0XHRcdFxuXHRcdFx0Y29udGV4dC5tb3ZlVG8oeDAsIHkwKTtcblx0XHRcdGNvbnRleHQubGluZVRvKHgxLCB5MSk7XG5cblx0XHRcdHRoaXMuZHJhd0ZpbGxBbmRTdHJva2UoY29udGV4dCwgdCwgZmFsc2UsIHRydWUpO1x0XHRcblx0XHR9XG5cdH1cbn07XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG5cblx0cmV0dXJuIHtcblx0XHRkcmF3OiBmdW5jdGlvbihjb250ZXh0LCB0KSB7XG5cdFx0XHR2YXIgeCA9IHRoaXMuZ2V0TnVtYmVyKFwieFwiLCB0LCAxMDApLFxuXHRcdFx0XHR5ID0gdGhpcy5nZXROdW1iZXIoXCJ5XCIsIHQsIDEwMCksXG5cdFx0XHRcdHJ4ID0gdGhpcy5nZXROdW1iZXIoXCJyeFwiLCB0LCA1MCksXG5cdFx0XHRcdHJ5ID0gdGhpcy5nZXROdW1iZXIoXCJyeVwiLCB0LCA1MCksXG5cdFx0XHRcdHN0YXJ0QW5nbGUgPSB0aGlzLmdldE51bWJlcihcInN0YXJ0QW5nbGVcIiwgdCwgMCksXG5cdFx0XHRcdGVuZEFuZ2xlID0gdGhpcy5nZXROdW1iZXIoXCJlbmRBbmdsZVwiLCB0LCAzNjApLFxuXHRcdFx0XHRkcmF3RnJvbUNlbnRlciA9IHRoaXMuZ2V0Qm9vbChcImRyYXdGcm9tQ2VudGVyXCIsIHQsIGZhbHNlKTtcblxuXHRcdFx0Y29udGV4dC50cmFuc2xhdGUoeCwgeSk7XG5cdFx0XHRjb250ZXh0LnJvdGF0ZSh0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCk7XG5cdFx0XHRjb250ZXh0LnNhdmUoKTtcblx0XHRcdGNvbnRleHQuc2NhbGUocnggLyAxMDAsIHJ5IC8gMTAwKTtcblx0XHRcdGlmKGRyYXdGcm9tQ2VudGVyKSB7XG5cdFx0XHRcdGNvbnRleHQubW92ZVRvKDAsIDApO1xuXHRcdFx0fVxuXHRcdFx0Y29udGV4dC5hcmMoMCwgMCwgMTAwLCBzdGFydEFuZ2xlICogTWF0aC5QSSAvIDE4MCwgZW5kQW5nbGUgKiBNYXRoLlBJIC8gMTgwKTtcblx0XHRcdGlmKGRyYXdGcm9tQ2VudGVyKSB7XG5cdFx0XHRcdGNvbnRleHQuY2xvc2VQYXRoKCk7XG5cdFx0XHR9XG5cdFx0XHRjb250ZXh0LnJlc3RvcmUoKTtcblxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCB0cnVlLCBmYWxzZSk7XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHBhdGggPSB0aGlzLmdldEFycmF5KFwicGF0aFwiLCB0LCBbXSksXG5cdFx0XHRcdHN0YXJ0UGVyY2VudCA9IHRoaXMuZ2V0TnVtYmVyKFwic3RhcnRQZXJjZW50XCIsIHQsIDApLFxuXHRcdFx0XHRlbmRQZXJjZW50ID0gdGhpcy5nZXROdW1iZXIoXCJlbmRQZXJjZW50XCIsIHQsIDEpLFxuXHRcdFx0XHRzdGFydFBvaW50ID0gTWF0aC5mbG9vcihwYXRoLmxlbmd0aCAvIDIgKiBzdGFydFBlcmNlbnQpLFxuXHRcdFx0XHRlbmRQb2ludCA9IE1hdGguZmxvb3IocGF0aC5sZW5ndGggLyAyICogZW5kUGVyY2VudCksXG5cdFx0XHRcdHN0YXJ0SW5kZXggPSBzdGFydFBvaW50ICogMixcblx0XHRcdFx0ZW5kSW5kZXggPSBlbmRQb2ludCAqIDI7XG5cblx0XHRcdGlmKHN0YXJ0SW5kZXggPiBlbmRJbmRleCkge1xuXHRcdFx0XHR2YXIgdGVtcCA9IHN0YXJ0SW5kZXg7XG5cdFx0XHRcdHN0YXJ0SW5kZXggPSBlbmRJbmRleDtcblx0XHRcdFx0ZW5kSW5kZXggPSB0ZW1wO1xuXHRcdFx0fVxuXG5cdFx0ICAgIGNvbnRleHQubW92ZVRvKHBhdGhbc3RhcnRJbmRleF0sIHBhdGhbc3RhcnRJbmRleCArIDFdKTtcblxuXHRcdCAgICBmb3IodmFyIGkgPSBzdGFydEluZGV4ICsgMjsgaSA8IGVuZEluZGV4IC0gMTsgaSArPSAyKSB7XG5cdFx0ICAgIFx0Y29udGV4dC5saW5lVG8ocGF0aFtpXSwgcGF0aFtpICsgMV0pO1xuXHRcdCAgICB9XG5cblx0XHRcdHRoaXMuZHJhd0ZpbGxBbmRTdHJva2UoY29udGV4dCwgdCwgZmFsc2UsIHRydWUpO1x0XHR9XG5cdH1cbn07XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG5cblx0cmV0dXJuIHtcblx0XHRkcmF3OiBmdW5jdGlvbihjb250ZXh0LCB0KSB7XG5cdFx0XHR2YXIgeCA9IHRoaXMuZ2V0TnVtYmVyKFwieFwiLCB0LCAxMDApLFxuXHRcdFx0XHR5ID0gdGhpcy5nZXROdW1iZXIoXCJ5XCIsIHQsIDEwMCksXG5cdFx0XHRcdHJhZGl1cyA9IHRoaXMuZ2V0TnVtYmVyKFwicmFkaXVzXCIsIHQsIDUwKSxcblx0XHRcdFx0cm90YXRpb24gPSB0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCxcblx0XHRcdFx0c2lkZXMgPSB0aGlzLmdldE51bWJlcihcInNpZGVzXCIsIHQsIDUpO1xuXG5cdFx0XHRjb250ZXh0LnRyYW5zbGF0ZSh4LCB5KTtcblx0XHRcdGNvbnRleHQucm90YXRlKHJvdGF0aW9uKTtcblx0XHRcdGNvbnRleHQubW92ZVRvKHJhZGl1cywgMCk7XG5cdFx0XHRmb3IodmFyIGkgPSAxOyBpIDwgc2lkZXM7IGkrKykge1xuXHRcdFx0XHR2YXIgYW5nbGUgPSBNYXRoLlBJICogMiAvIHNpZGVzICogaTtcblx0XHRcdFx0Y29udGV4dC5saW5lVG8oTWF0aC5jb3MoYW5nbGUpICogcmFkaXVzLCBNYXRoLnNpbihhbmdsZSkgKiByYWRpdXMpO1xuXHRcdFx0fVxuXHRcdFx0Y29udGV4dC5saW5lVG8ocmFkaXVzLCAwKTtcblxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCB0cnVlLCBmYWxzZSk7XG5cdFx0fVxuXHR9XG59O1xuXHRcdFxuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHRhbmdsZSA9IHRoaXMuZ2V0TnVtYmVyKFwiYW5nbGVcIiwgdCwgMCkgKiBNYXRoLlBJIC8gMTgwLFxuXHRcdFx0XHRsZW5ndGggPSB0aGlzLmdldE51bWJlcihcImxlbmd0aFwiLCB0LCAxMDApLFxuXHRcdFx0XHRzZWdtZW50TGVuZ3RoID0gdGhpcy5nZXROdW1iZXIoXCJzZWdtZW50TGVuZ3RoXCIsIHQsIDUwKSxcdFx0XHRcdFxuXHRcdCAgICBcdHN0YXJ0ID0gLTAuMDEsXG5cdFx0ICAgIFx0ZW5kID0gKGxlbmd0aCArIHNlZ21lbnRMZW5ndGgpICogdDtcblxuXHRcdCAgICBpZihlbmQgPiBzZWdtZW50TGVuZ3RoKSB7XG5cdFx0ICAgICAgc3RhcnQgPSBlbmQgLSBzZWdtZW50TGVuZ3RoO1xuXHRcdCAgICB9XG5cdFx0ICAgIGlmKGVuZCA+IGxlbmd0aCkge1xuXHRcdCAgICAgIGVuZCA9IGxlbmd0aCArIDAuMDE7XG5cdFx0ICAgIH1cblxuXHRcdCAgICBjb250ZXh0LnRyYW5zbGF0ZSh4LCB5KTtcblx0XHQgICAgY29udGV4dC5yb3RhdGUoYW5nbGUpO1xuXHRcdFx0Y29udGV4dC5tb3ZlVG8oc3RhcnQsIDApO1xuXHRcdFx0Y29udGV4dC5saW5lVG8oZW5kLCAwKTtcblxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCBmYWxzZSwgdHJ1ZSk7XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHR3ID0gdGhpcy5nZXROdW1iZXIoXCJ3XCIsIHQsIDEwMCksXG5cdFx0XHRcdGggPSB0aGlzLmdldE51bWJlcihcImhcIiwgdCwgMTAwKTtcblx0XHRcdFx0XG5cdFx0XHRjb250ZXh0LnRyYW5zbGF0ZSh4LCB5KTtcblx0XHRcdGNvbnRleHQucm90YXRlKHRoaXMuZ2V0TnVtYmVyKFwicm90YXRpb25cIiwgdCwgMCkgKiBNYXRoLlBJIC8gMTgwKTtcblx0XHRcdGlmKHRoaXMuZ2V0Qm9vbChcImRyYXdGcm9tQ2VudGVyXCIsIHQsIHRydWUpKSB7XG5cdFx0XHRcdGNvbnRleHQucmVjdCgtdyAqIDAuNSwgLWggKiAwLjUsIHcsIGgpO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdGNvbnRleHQucmVjdCgwLCAwLCB3LCBoKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCB0cnVlLCBmYWxzZSk7XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHgwID0gdGhpcy5nZXROdW1iZXIoXCJ4MFwiLCB0LCAwKSxcblx0XHRcdFx0eTAgPSB0aGlzLmdldE51bWJlcihcInkwXCIsIHQsIDApLFxuXHRcdFx0XHR4MSA9IHRoaXMuZ2V0TnVtYmVyKFwieDFcIiwgdCwgMTAwKSxcblx0XHRcdFx0eTEgPSB0aGlzLmdldE51bWJlcihcInkxXCIsIHQsIDEwMCksXG5cdFx0XHRcdHNlZ21lbnRMZW5ndGggPSB0aGlzLmdldE51bWJlcihcInNlZ21lbnRMZW5ndGhcIiwgdCwgNTApLFxuXHRcdFx0XHRkeCA9IHgxIC0geDAsXG5cdFx0XHQgICAgZHkgPSB5MSAtIHkwLFxuXHRcdFx0ICAgIGFuZ2xlID0gTWF0aC5hdGFuMihkeSwgZHgpLFxuXHRcdFx0ICAgIGRpc3QgPSBNYXRoLnNxcnQoZHggKiBkeCArIGR5ICogZHkpLFxuXHRcdCAgICBcdHN0YXJ0ID0gLTAuMDEsXG5cdFx0ICAgIFx0ZW5kID0gKGRpc3QgKyBzZWdtZW50TGVuZ3RoKSAqIHQ7XG5cblx0XHQgICAgaWYoZW5kID4gc2VnbWVudExlbmd0aCkge1xuXHRcdCAgICAgIHN0YXJ0ID0gZW5kIC0gc2VnbWVudExlbmd0aDtcblx0XHQgICAgfVxuXHRcdCAgICBpZihlbmQgPiBkaXN0KSB7XG5cdFx0ICAgICAgZW5kID0gZGlzdCArIDAuMDE7XG5cdFx0ICAgIH1cblxuXHRcdCAgICBjb250ZXh0LnRyYW5zbGF0ZSh4MCwgeTApO1xuXHRcdCAgICBjb250ZXh0LnJvdGF0ZShhbmdsZSk7XG5cdFx0XHRjb250ZXh0Lm1vdmVUbyhzdGFydCwgMCk7XG5cdFx0XHRjb250ZXh0LmxpbmVUbyhlbmQsIDApO1xuXG5cdFx0XHR0aGlzLmRyYXdGaWxsQW5kU3Ryb2tlKGNvbnRleHQsIHQsIGZhbHNlLCB0cnVlKTtcblx0XHR9XG5cdH1cbn07XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCl7XG5cblx0cmV0dXJuIHtcblx0XHRkcmF3OiBmdW5jdGlvbihjb250ZXh0LCB0KSB7XG5cdFx0XHR2YXIgeCA9IHRoaXMuZ2V0TnVtYmVyKFwieFwiLCB0LCBcIjEwMFwiKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCBcIjEwMFwiKSxcblx0XHRcdFx0aW5uZXJSYWRpdXMgPSB0aGlzLmdldE51bWJlcihcImlubmVyUmFkaXVzXCIsIHQsIDEwKSxcblx0XHRcdFx0b3V0ZXJSYWRpdXMgPSB0aGlzLmdldE51bWJlcihcIm91dGVyUmFkaXVzXCIsIHQsIDkwKSxcblx0XHRcdFx0dHVybnMgPSB0aGlzLmdldE51bWJlcihcInR1cm5zXCIsIHQsIDYpLFxuXHRcdFx0XHRyZXMgPSB0aGlzLmdldE51bWJlcihcInJlc1wiLCB0LCAxKSAqIE1hdGguUEkgLyAxODAsXG5cdFx0XHRcdGZ1bGxBbmdsZSA9IE1hdGguUEkgKiAyICogdHVybnM7XG5cblx0XHRcdGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuXHRcdFx0Y29udGV4dC5yb3RhdGUodGhpcy5nZXROdW1iZXIoXCJyb3RhdGlvblwiLCB0LCAwKSAqIE1hdGguUEkgLyAxODApO1xuXG5cblx0XHRcdGlmKGZ1bGxBbmdsZSA+IDApIHtcblx0XHRcdFx0Zm9yKHZhciBhID0gMDsgYSA8IGZ1bGxBbmdsZTsgYSArPSByZXMpIHtcblx0XHRcdFx0XHR2YXIgciA9IGlubmVyUmFkaXVzICsgKG91dGVyUmFkaXVzIC0gaW5uZXJSYWRpdXMpICogYSAvIGZ1bGxBbmdsZTtcblx0XHRcdFx0XHRjb250ZXh0LmxpbmVUbyhNYXRoLmNvcyhhKSAqIHIsIE1hdGguc2luKGEpICogcik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHRmb3IodmFyIGEgPSAwOyBhID4gZnVsbEFuZ2xlOyBhIC09IHJlcykge1xuXHRcdFx0XHRcdHZhciByID0gaW5uZXJSYWRpdXMgKyAob3V0ZXJSYWRpdXMgLSBpbm5lclJhZGl1cykgKiBhIC8gZnVsbEFuZ2xlO1xuXHRcdFx0XHRcdGNvbnRleHQubGluZVRvKE1hdGguY29zKGEpICogciwgTWF0aC5zaW4oYSkgKiByKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0dGhpcy5kcmF3RmlsbEFuZFN0cm9rZShjb250ZXh0LCB0LCBmYWxzZSwgdHJ1ZSk7XG5cdFx0fVxuXHR9O1xuXG59OyIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKXtcblxuXHRyZXR1cm4ge1xuXHRcdGRyYXc6IGZ1bmN0aW9uKGNvbnRleHQsIHQpIHtcblx0XHRcdHZhciB4ID0gdGhpcy5nZXROdW1iZXIoXCJ4XCIsIHQsIDEwMCksXG5cdFx0XHRcdHkgPSB0aGlzLmdldE51bWJlcihcInlcIiwgdCwgMTAwKSxcblx0XHRcdFx0aW5uZXJSYWRpdXMgPSB0aGlzLmdldE51bWJlcihcImlubmVyUmFkaXVzXCIsIHQsIDI1KSxcblx0XHRcdFx0b3V0ZXJSYWRpdXMgPSB0aGlzLmdldE51bWJlcihcIm91dGVyUmFkaXVzXCIsIHQsIDUwKSxcblx0XHRcdFx0cm90YXRpb24gPSB0aGlzLmdldE51bWJlcihcInJvdGF0aW9uXCIsIHQsIDApICogTWF0aC5QSSAvIDE4MCxcblx0XHRcdFx0cG9pbnRzID0gdGhpcy5nZXROdW1iZXIoXCJwb2ludHNcIiwgdCwgNSk7XG5cblx0XHRcdGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuXHRcdFx0Y29udGV4dC5yb3RhdGUocm90YXRpb24pO1xuXHRcdFx0Y29udGV4dC5tb3ZlVG8ob3V0ZXJSYWRpdXMsIDApO1xuXHRcdFx0Zm9yKHZhciBpID0gMTsgaSA8IHBvaW50cyAqIDI7IGkrKykge1xuXHRcdFx0XHR2YXIgYW5nbGUgPSBNYXRoLlBJICogMiAvIHBvaW50cyAvIDIgKiBpLFxuXHRcdFx0XHRcdHIgPSBpICUgMiA/IGlubmVyUmFkaXVzIDogb3V0ZXJSYWRpdXM7XG5cdFx0XHRcdGNvbnRleHQubGluZVRvKE1hdGguY29zKGFuZ2xlKSAqIHIsIE1hdGguc2luKGFuZ2xlKSAqIHIpO1xuXHRcdFx0fVxuXHRcdFx0Y29udGV4dC5saW5lVG8ob3V0ZXJSYWRpdXMsIDApO1xuXG5cblx0XHRcdHRoaXMuZHJhd0ZpbGxBbmRTdHJva2UoY29udGV4dCwgdCwgdHJ1ZSwgZmFsc2UpO1x0XG5cdFx0fVxuXHR9XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpe1xuXG5cdHJldHVybiB7XG5cdFx0ZHJhdzogZnVuY3Rpb24oY29udGV4dCwgdCkge1xuXHRcdFx0dmFyIHggPSB0aGlzLmdldE51bWJlcihcInhcIiwgdCwgMTAwKSxcblx0XHRcdFx0eSA9IHRoaXMuZ2V0TnVtYmVyKFwieVwiLCB0LCAxMDApLFxuXHRcdFx0XHR0ZXh0ID0gdGhpcy5nZXRTdHJpbmcoXCJ0ZXh0XCIsIHQsIFwiaGVsbG9cIiksXG5cdFx0XHRcdGZvbnRTaXplID0gdGhpcy5nZXROdW1iZXIoXCJmb250U2l6ZVwiLCB0LCAyMCksXG5cdFx0XHRcdGZvbnRXZWlnaHQgPSB0aGlzLmdldFN0cmluZyhcImZvbnRXZWlnaHRcIiwgdCwgXCJub3JtYWxcIik7XG5cdFx0XHRcdGZvbnRGYW1pbHkgPSB0aGlzLmdldFN0cmluZyhcImZvbnRGYW1pbHlcIiwgdCwgXCJzYW5zLXNlcmlmXCIpO1xuXHRcdFx0XHRmb250U3R5bGUgPSB0aGlzLmdldFN0cmluZyhcImZvbnRTdHlsZVwiLCB0LCBcIm5vcm1hbFwiKTtcblxuXHRcdFx0Y29udGV4dC5mb250ID0gZm9udFdlaWdodCArIFwiIFwiICsgZm9udFN0eWxlICsgXCIgXCIgKyBmb250U2l6ZSArIFwicHggXCIgKyBmb250RmFtaWx5O1xuXHRcdFx0dmFyIHdpZHRoID0gY29udGV4dC5tZWFzdXJlVGV4dCh0ZXh0KS53aWR0aDtcblx0XHRcdGNvbnRleHQudHJhbnNsYXRlKHgsIHkpO1xuXHRcdFx0Y29udGV4dC5yb3RhdGUodGhpcy5nZXROdW1iZXIoXCJyb3RhdGlvblwiLCB0LCAwKSAqIE1hdGguUEkgLyAxODApO1xuXHRcdFx0dmFyIHNoYWRvd3NTZXQgPSBmYWxzZTtcblx0XHRcdGNvbnRleHQuc2F2ZSgpO1xuXHRcdFx0aWYodGhpcy5nZXRCb29sKFwiZmlsbFwiLCB0LCB0cnVlKSkge1xuXHRcdFx0XHR0aGlzLnNldFNoYWRvd1BhcmFtcyhjb250ZXh0LCB0KTtcblx0XHRcdFx0c2hhZG93c1NldCA9IHRydWU7XG5cdFx0XHRcdGNvbnRleHQuZmlsbFRleHQodGV4dCwgLXdpZHRoIC8gMiwgZm9udFNpemUgKiAwLjQpO1xuXHRcdFx0fVxuXHRcdFx0Y29udGV4dC5yZXN0b3JlKCk7XG5cdFx0XHRpZih0aGlzLmdldEJvb2woXCJzdHJva2VcIiwgdCwgZmFsc2UpKSB7XG5cdFx0XHRcdGlmKCFzaGFkb3dzU2V0KSB7XG5cdFx0XHRcdFx0dGhpcy5zZXRTaGFkb3dQYXJhbXMoY29udGV4dCwgdCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0Y29udGV4dC5zdHJva2VUZXh0KHRleHQsIC13aWR0aCAvIDIsIGZvbnRTaXplICogMC40KTtcblx0XHRcdH1cblx0XHR9XG5cdH1cbn07XG4iXX0=
1860 |
--------------------------------------------------------------------------------