├── .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 | ![From GifLoopCoder's Tumblr](http://45.media.tumblr.com/c1f10ae3e2f5d2d5f9866a7373bbba9b/tumblr_nyshbnUNIi1shpedgo1_400.gif) 6 | 7 | ![From GifLoopCoder's Tumblr](http://49.media.tumblr.com/551b4b27fdc2fabc01c11a4fda1ffbba/tumblr_nyk7efApif1sr8cguo1_400.gif) 8 | 9 | ![From GifLoopCoder's Tumblr](http://45.media.tumblr.com/4a0b2c4d4f7a6436799e4bd827598c30/tumblr_nyprotgWwe1tjryj4o1_400.gif) 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 | --------------------------------------------------------------------------------