├── .gitignore
├── dist
├── .gitignore
└── ng-staticize.js
├── webpack.config.js
├── HOW.MD
├── scripts
└── pack-demo.js
├── .eslintrc
├── package.json
├── LICENSE
├── src
├── index.js
├── parser.js
└── builder.js
├── README.MD
└── examples
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
--------------------------------------------------------------------------------
/dist/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 | !ng-staticize.js
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var plugins = [];
2 |
3 | if (process.env.COMPRESS) {
4 | plugins.push(
5 | new webpack.optimize.UglifyJsPlugin({})
6 | );
7 | }
8 |
9 | module.exports = {
10 | entry: './src/index.js',
11 | output: {
12 | path: './dist',
13 | filename: 'ng-staticize.js'
14 | },
15 | module: {
16 | preLoaders: [
17 | { test: /\.js$/, exclude: /node_modules|bower_components/, loader: 'eslint-loader' }
18 | ]
19 | },
20 | plugins: plugins
21 | };
--------------------------------------------------------------------------------
/HOW.MD:
--------------------------------------------------------------------------------
1 | ## 起因
2 | Angular的模板在数据量较大(比如渲染一个2000条数据表格)可能会遇到渲染速度比较慢的问题,[bindonce](https://github.com/Pasvaz/bindonce)提供了一个解决方案,用来解决页面中因为watcher数量较多造成的性能问题。
3 |
4 | 在IE8下,DOM操作是一个非常昂贵的操作,你可以阅读这个stack overflow上这个[问答](http://stackoverflow.com/questions/9639703/ie8-javascript-very-slow-to-load-large-list-of-options-in-select-element)来了解其中的细节。
5 |
6 | 简单来讲,在IE8下创建6000个option元素,需要花费16s。这个问题的解决办法就是使用innerHTML来创建option,不使用createElement来创建option。
7 |
8 | ## 解决办法
9 |
10 | ng-staticize做的事情非常简单,自己解析了Angular的模板,并通过解析后的模板构建出要输出的HTML。
--------------------------------------------------------------------------------
/scripts/pack-demo.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 | var useMin = require('usemin-lib');
4 | const EXAMPLES_PATH = './examples';
5 | const DIST_PATH = './dist/';
6 |
7 | var entries = ['index.html'];
8 |
9 | entries.map(function(fileName) {
10 | var entry = path.join(EXAMPLES_PATH, fileName);
11 | var content = fs.readFileSync(entry).toString();
12 | var blocks = useMin.getBlocks(entry, content, true);
13 | useMin.processBlocks(blocks, DIST_PATH);
14 | fs.writeFile(DIST_PATH + fileName, useMin.getHTML(content, blocks, false));
15 | });
16 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "indent": [
4 | 2,
5 | 2,
6 | {
7 | "SwitchCase": 1
8 | }
9 | ],
10 | "quotes": [
11 | 2,
12 | "single"
13 | ],
14 | "linebreak-style": [
15 | 2,
16 | "unix"
17 | ],
18 | "semi": [
19 | 2,
20 | "always"
21 | ]
22 | },
23 | "env": {
24 | "es6": true,
25 | "browser": true,
26 | "commonjs": true
27 | },
28 | "extends": "eslint:recommended",
29 | "ecmaFeatures": {
30 | "jsx": true
31 | },
32 | "globals": {
33 | "angular": true,
34 | "process": true,
35 | "webpack": true
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-staticize",
3 | "version": "0.1.0",
4 | "description": "Staticize your angular template. Zero watcher and fast as template engine.",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "dev": "node_modules/.bin/webpack --watch --devtool inline-source-map",
8 | "dist": "COMPRESS=1 node_modules/.bin/webpack"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/ElemeFE/ng-staticize.git"
13 | },
14 | "dependencies": {
15 | "domify": "^1.4.0"
16 | },
17 | "devDependencies": {
18 | "angular": "1.2.28",
19 | "bootstrap": "^3.3.5",
20 | "eslint": "^1.5.1",
21 | "eslint-loader": "^1.0.0",
22 | "jquery": "1.11.3",
23 | "mocha": "~2.2.5",
24 | "should": "~7.0.2",
25 | "sinon": "~1.10.2",
26 | "usemin-lib": "0.0.5",
27 | "webpack": "^1.9.10"
28 | },
29 | "author": "long.zhang",
30 | "license": "MIT"
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 饿了么前端
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 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | var parser = require('./parser');
2 | var builder = require('./builder');
3 |
4 | angular.module('ngStaticize', [])
5 | .factory('templateParser', parser)
6 | .factory('templateBuilder', builder)
7 | .directive('ngStaticize', ['templateParser', 'templateBuilder', '$parse', function(templateParser, templateBuilder) {
8 | return {
9 | restrict: 'EA',
10 | replace: true,
11 | terminal: true,
12 | priority: 1001,
13 | compile: function(element) {
14 | var html = element[0].innerHTML;
15 | var template = templateParser.parse(html);
16 | return {
17 | post: function(scope, element, attrs) {
18 | var reRender = function () {
19 | var result = templateBuilder.build(template, scope);
20 | element[0].innerHTML = result;
21 | };
22 |
23 | reRender();
24 |
25 | var watchExpr = attrs.ngStaticize;
26 |
27 | if (watchExpr) {
28 | scope.$watch(watchExpr, function() {
29 | reRender();
30 | }, true);
31 | }
32 | }
33 | };
34 | }
35 | };
36 | }]);
37 |
38 | module.exports = {};
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # Introduce
2 |
3 | ng-staticize 是一个把 Angular 模板静态化的 directive,使用之后 watcher 数量会显著降低,渲染速度(在数据量较大的情况下)在 IE8 上会有10-20x的性能提升。
4 |
5 | 你可以花几分钟查看 [这个例子](http://elemefe.github.io/ng-staticize/) 来体验一下 ng-staticize 带来的性能提升,在 IE8 或者 Firefox 下会看到更明显的性能提升。
6 |
7 | 从降低 Angular watcher 的角度来看,ng-staticize 是一个与 [bindonce](https://github.com/Pasvaz/bindonce) 类似的项目,区别在于 ng-staticize 无需改变模板中 directive 的定义。
8 |
9 | 从设计之初,ng-staticize 就存在以下缺陷,请知悉:
10 |
11 | 1. 应用 ng-staticize 的区域中不能使用 directive 兼容列表以外的 directive。
12 | 2. 渲染出的 DOM 不再动态,即在 Controller 中的数据变更后,应用了 ng-staticize 的区域不会重新渲染。不过你可以为 ng-staticize 指定一个表达式,在表达式变更后这个区域会重新渲染。
13 |
14 | 以下是 ng-staticize 适用场景:
15 |
16 | 1. 页面动态渲染比较少,类似于静态页面,只是使用了 Angular 的模板来描述数据绑定。
17 | 2. 页面中需要渲染的数据较多,需要做性能优化。
18 |
19 | 如果你对 ng-staticize 的实现细节感兴趣,你可以阅读 [这篇文档](HOW.MD)。
20 |
21 | # Usage
22 |
23 | ## Install
24 |
25 | 如果你使用 Browerify 或者 Webpack,可以使用 npm 来安装 ng-staticize:
26 |
27 | ```Bash
28 | npm install ng-staticize —save
29 | ```
30 |
31 | 如果没有使用 npm,则可以下载项目后,在项目中引用 dist 下的 ng-staticize.js。
32 |
33 | ## Include ngStaticize
34 |
35 | ```JavaScript
36 | angular.module('demo', [ 'ngStaticize' ]);
37 | ```
38 |
39 | ## Use
40 |
41 | 如果只是想把页面中的某一个页面中的某个区域进行静态化操作,只需要为这个区域的元素添加一个属性:ng-staticize。
42 |
43 | ```HTML
44 |
...
45 | ```
46 |
47 | 如果需要在某些数据变更之后重新渲染这块区域,为 ng-staticize 属性设置一个表达式,在该表达式变更之后这个区域会重新渲染。比如在 scope 中的 todos 属性发生了变化之后重新渲染,则这么定义:
48 |
49 | ```HTML
50 | ...
51 | ```
52 |
53 | > 如果需要兼容低版本浏 IE 览器(IE8、IE9),请不要在 table、tbody、tfoot、thead、title、tr 等元素上使用 ng-staticize,原因见 [此文章](http://w3help.org/zh-cn/causes/BX9046)。
54 |
55 | # Compatible Directive
56 | ng-staticize 只兼容了一些常见的 directive,列表如下:
57 |
58 | - ng-if
59 | - ng-repeat
60 | - ng-style
61 | - ng-class
62 | - ng-show
63 | - ng-hide
64 | - ng-html
65 | - ng-bind
66 | - ng-text
67 | - ng-src
68 | - ng-href
69 | - ng-alt
70 | - ng-title
71 | - ng-id
72 | - ng-disabled
73 | - ng-value
74 |
75 | # Fork
76 | 如果你想修改 ng-staticize 的代码,在项目文件夹下执行这两个命令:
77 |
78 | ```Bash
79 | npm install
80 | npm run dev
81 | ```
82 |
83 | # License
84 | MIT
85 |
--------------------------------------------------------------------------------
/src/parser.js:
--------------------------------------------------------------------------------
1 | var templateParser = ['$parse', '$interpolate', function($parse, $interpolate) {
2 | var needInterpolate = { 'ng-src': true, 'ng-href': true };
3 | var TEXT_NODE = 3;
4 | var ELEMENT_NODE = 1;
5 |
6 | function walk(el) {
7 | if (el.nodeType !== ELEMENT_NODE) return;
8 |
9 | var children = [];
10 | var node = { tagName: el.tagName.toLowerCase() };
11 | var attributes = el.attributes;
12 | var attrs, dirs, i, j;
13 |
14 | for (i = 0, j = attributes.length; i < j; i++) {
15 | var attribute = attributes[i];
16 | var name = attribute.name;
17 | var value = attribute.nodeValue;
18 | if (name && name.substr(0, 3) === 'ng-') {
19 | if (!dirs) {
20 | dirs = {};
21 | }
22 | if (name.length > 8 && name.substr(0, 8) === 'ng-attr-') {
23 | if (!attrs) {
24 | attrs = {};
25 | }
26 | attrs[name.substr(9)] = $parse(value);
27 | } else if (name === 'ng-repeat') {
28 | var matches = /(\w+)\s+in\s+(.*?)$/.exec(value);
29 | if (matches) {
30 | dirs[name] = {
31 | itemName: matches[1],
32 | getArray: $parse(matches[2])
33 | };
34 | }
35 | } else {
36 | dirs[name] = needInterpolate[name] ? $interpolate(value) : $parse(value);
37 | }
38 | } else {
39 | if (!attrs) {
40 | attrs = {};
41 | }
42 | if (/\s*({{\s*(.+?)\s*}})\s*/gi.test(value)) {
43 | attrs[name] = $interpolate(value);
44 | } else {
45 | attrs[name] = value;
46 | }
47 | }
48 | }
49 |
50 | var childNodes = el.childNodes;
51 | for (i = 0, j = childNodes.length; i < j; i++) {
52 | var child = childNodes[i];
53 | if (child.nodeType === TEXT_NODE) {
54 | var text = child.nodeValue;
55 | if (text) {
56 | if (/\s*({{\s*(.+?)\s*}})\s*/gi.test(text)) {
57 | children.push($interpolate(text));
58 | } else {
59 | text = text.replace(/(\r\n|\n|\r|\s)/gm, '');
60 | if (text.length) {
61 | children.push(text);
62 | }
63 | }
64 | }
65 | continue;
66 | }
67 | var parseResult = walk(child);
68 | if (parseResult) {
69 | children.push(parseResult);
70 | }
71 | }
72 |
73 | if (children.length > 0) {
74 | node.children = children;
75 | }
76 |
77 | if (attrs) {
78 | node.attrs = attrs;
79 | }
80 |
81 | if (dirs) {
82 | node.dirs = dirs;
83 | }
84 |
85 | return node;
86 | }
87 |
88 | return {
89 | parse: function(template) {
90 | return walk(require('domify')(template));
91 | }
92 | };
93 | }];
94 |
95 | module.exports = templateParser;
--------------------------------------------------------------------------------
/src/builder.js:
--------------------------------------------------------------------------------
1 | var trim = function (string) {
2 | return string.replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
3 | };
4 |
5 | var templateBuilder = [function() {
6 | function shallowClone(object) {
7 | var result = {};
8 | for (var prop in object) {
9 | if (object.hasOwnProperty(prop)) result[prop] = object[prop];
10 | }
11 | return result;
12 | }
13 |
14 | function newContext(context) {
15 | var empty = function() {};
16 | empty.prototype = context;
17 |
18 | return new empty();
19 | }
20 |
21 | function getStyle(object) {
22 | var cssText = '';
23 | for (var prop in object) {
24 | if (object.hasOwnProperty(prop)) {
25 | cssText += prop + ':' + object[prop] + ';';
26 | }
27 | }
28 | return cssText;
29 | }
30 |
31 | function getClasses(object) {
32 | var classes = [];
33 | for (var prop in object) {
34 | if (object.hasOwnProperty(prop) && !!object[prop]) {
35 | classes.push(prop);
36 | }
37 | }
38 | return classes.join(' ');
39 | }
40 |
41 | function cloneRepeatNode(node) {
42 | var cloneNode = shallowClone(node);
43 | cloneNode.dirs = shallowClone(node.dirs);
44 | cloneNode.dirs['ng-repeat'] = null;
45 | delete cloneNode.dirs['ng-repeat'];
46 |
47 | return cloneNode;
48 | }
49 |
50 | var ATTR_DIR_MAP = {
51 | 'ng-src': true,
52 | 'ng-href': true,
53 | 'ng-alt': true,
54 | 'ng-title': true,
55 | 'ng-id': true,
56 | 'ng-disabled': true,
57 | 'ng-value': true
58 | };
59 |
60 | var HTML_DIR_MAP = {
61 | 'ng-html': true,
62 | 'ng-bind': true,
63 | 'ng-text': true
64 | };
65 |
66 | var emptyArray = [];
67 |
68 | function toHTML(node, context) {
69 | var html, i, j;
70 | if (node instanceof Array) {
71 | html = '';
72 |
73 | for (i = 0, j = node.length; i < j; i++) {
74 | html += toHTML(node[i], context);
75 | }
76 |
77 | return html;
78 | }
79 |
80 | if (typeof node === 'string') return node;
81 | if (typeof node === 'function') return node(context);
82 |
83 | var tag = node.tagName;
84 | var children = node.children;
85 | var attrs = node.attrs;
86 | var dirs = node.dirs;
87 | var content = node.textContent;
88 | var classes = '';
89 | var cssText = '';
90 |
91 | if (dirs && dirs['ng-repeat']) {
92 | var cloneNode = cloneRepeatNode(node);
93 | var repeatDir = dirs['ng-repeat'];
94 | var array = repeatDir.getArray(context) || emptyArray;
95 | var itemName = repeatDir.itemName;
96 |
97 | html = '';
98 |
99 | for (i = 0, j = array.length; i < j; i++) {
100 | var subContext = newContext(context);
101 | subContext.$index = i;
102 | subContext.$first = i === 0;
103 | subContext.$last = i === j - 1;
104 | subContext.$middle = !(subContext.$first || subContext.$last);
105 | subContext[itemName] = array[i];
106 |
107 | html += toHTML(cloneNode, subContext);
108 | }
109 |
110 | return html;
111 | }
112 |
113 | html = '<' + tag;
114 |
115 | for (var dir in dirs) {
116 | if (dirs.hasOwnProperty(dir)) {
117 | var fn = dirs[dir];
118 | var value;
119 | if (typeof fn === 'function') {
120 | value = fn(context);
121 | }
122 |
123 | if (dir === 'ng-if') {
124 | if (!value) {
125 | return '';
126 | }
127 | } else if (ATTR_DIR_MAP[dir]) {
128 | html += ' ' + dir.substr(3) + '="' + value + '"';
129 | } else if (HTML_DIR_MAP[dir]) {
130 | content = value;
131 | } else if (dir === 'ng-style') {
132 | cssText = getStyle(value) + cssText;
133 | } else if (dir === 'ng-class') {
134 | classes += getClasses(value);
135 | } else if (dir === 'ng-show' || dir === 'ng-hide') {
136 | if (value !== null && value !== undefined) {
137 | classes += ' ' + dir;
138 | }
139 | }
140 | }
141 | }
142 |
143 | for (var attr in attrs) {
144 | if (attrs.hasOwnProperty(attr)) {
145 | if (attr === 'style') continue;
146 |
147 | var attrValue = attrs[attr];
148 | if (typeof attrValue === 'function') {
149 | attrValue = attrValue(context);
150 | }
151 | if (attr === 'class') {
152 | classes += ' ' + attrValue;
153 | } else {
154 | html += ' ' + attr + '="' + attrValue + '"';
155 | }
156 | }
157 | }
158 |
159 | if (classes) {
160 | html += ' class="' + trim(classes) + '"';
161 | }
162 |
163 | if (cssText) {
164 | html += ' style="' + cssText + '"';
165 | }
166 |
167 | html += '>';
168 |
169 | if (content) {
170 | html += content;
171 | }
172 |
173 | if (children) {
174 | for (i = 0, j = children.length; i < j; i++) {
175 | var child = children[i];
176 | html += toHTML(child, context);
177 | }
178 | }
179 |
180 | html += '' + tag + '>';
181 |
182 | return html;
183 | }
184 |
185 | return {
186 | build: toHTML
187 | };
188 | }];
189 |
190 | module.exports = templateBuilder;
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ng Staticize Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
72 |
73 |
74 |
75 |
76 |
77 |
172 |
173 |
174 |
175 |
ng-staticize demo
176 |
177 |
This demo demonstrates the render speed of ng-staticize. Switch tabs to view the render speed of using ng-staticize or not.
178 |
If you are using Chrome, you can use Angular watchers to view the watcher's change.
179 |
180 |
181 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | Load 500
193 | Load 1000
194 | Load 2000
195 | Load 5000
196 | Load 10000
197 |
198 |
199 |
200 | This tab is using ng-staticize, you can click buttons above to get the time of rendering.
201 |
...
202 |
203 |
204 |
205 |
206 |
207 | #
208 | First Name
209 | Last Name
210 | Age
211 | Gender
212 | Profile
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | Load 500
235 | Load 1000
236 | Load 2000
237 | Load 5000
238 | Load 10000
239 |
240 |
241 |
242 | This tab is
not using ng-staticze, you can click buttons above to get the time of rendering.
243 |
...
244 |
245 |
246 |
247 |
248 |
249 | #
250 | First Name
251 | Last Name
252 | Age
253 | Gender
254 | Profile
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/dist/ng-staticize.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 | /******/
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 | /******/
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 | /******/
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 | /******/
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 | /******/
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 | /******/
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 | /******/
29 | /******/
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 | /******/
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 | /******/
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 | /******/
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 | var parser = __webpack_require__(1);
48 | var builder = __webpack_require__(3);
49 |
50 | angular.module('ngStaticize', [])
51 | .factory('templateParser', parser)
52 | .factory('templateBuilder', builder)
53 | .directive('ngStaticize', ['templateParser', 'templateBuilder', '$parse', function(templateParser, templateBuilder) {
54 | return {
55 | restrict: 'EA',
56 | replace: true,
57 | terminal: true,
58 | priority: 1001,
59 | compile: function(element) {
60 | var html = element[0].innerHTML;
61 | var template = templateParser.parse(html);
62 | return {
63 | post: function(scope, element, attrs) {
64 | var reRender = function () {
65 | var result = templateBuilder.build(template, scope);
66 | element[0].innerHTML = result;
67 | };
68 |
69 | reRender();
70 |
71 | var watchExpr = attrs.ngStaticize;
72 |
73 | if (watchExpr) {
74 | scope.$watch(watchExpr, function() {
75 | reRender();
76 | }, true);
77 | }
78 | }
79 | };
80 | }
81 | };
82 | }]);
83 |
84 | module.exports = {};
85 |
86 | /***/ },
87 | /* 1 */
88 | /***/ function(module, exports, __webpack_require__) {
89 |
90 | var templateParser = ['$parse', '$interpolate', function($parse, $interpolate) {
91 | var needInterpolate = { 'ng-src': true, 'ng-href': true };
92 | var TEXT_NODE = 3;
93 | var ELEMENT_NODE = 1;
94 |
95 | function walk(el) {
96 | if (el.nodeType !== ELEMENT_NODE) return;
97 |
98 | var children = [];
99 | var node = { tagName: el.tagName.toLowerCase() };
100 | var attributes = el.attributes;
101 | var attrs, dirs, i, j;
102 |
103 | for (i = 0, j = attributes.length; i < j; i++) {
104 | var attribute = attributes[i];
105 | var name = attribute.name;
106 | var value = attribute.nodeValue;
107 | if (name && name.substr(0, 3) === 'ng-') {
108 | if (!dirs) {
109 | dirs = {};
110 | }
111 | if (name.length > 8 && name.substr(0, 8) === 'ng-attr-') {
112 | if (!attrs) {
113 | attrs = {};
114 | }
115 | attrs[name.substr(9)] = $parse(value);
116 | } else if (name === 'ng-repeat') {
117 | var matches = /(\w+)\s+in\s+(.*?)$/.exec(value);
118 | if (matches) {
119 | dirs[name] = {
120 | itemName: matches[1],
121 | getArray: $parse(matches[2])
122 | };
123 | }
124 | } else {
125 | dirs[name] = needInterpolate[name] ? $interpolate(value) : $parse(value);
126 | }
127 | } else {
128 | if (!attrs) {
129 | attrs = {};
130 | }
131 | if (/\s*({{\s*(.+?)\s*}})\s*/gi.test(value)) {
132 | attrs[name] = $interpolate(value);
133 | } else {
134 | attrs[name] = value;
135 | }
136 | }
137 | }
138 |
139 | var childNodes = el.childNodes;
140 | for (i = 0, j = childNodes.length; i < j; i++) {
141 | var child = childNodes[i];
142 | if (child.nodeType === TEXT_NODE) {
143 | var text = child.nodeValue;
144 | if (text) {
145 | if (/\s*({{\s*(.+?)\s*}})\s*/gi.test(text)) {
146 | children.push($interpolate(text));
147 | } else {
148 | text = text.replace(/(\r\n|\n|\r|\s)/gm, '');
149 | if (text.length) {
150 | children.push(text);
151 | }
152 | }
153 | }
154 | continue;
155 | }
156 | var parseResult = walk(child);
157 | if (parseResult) {
158 | children.push(parseResult);
159 | }
160 | }
161 |
162 | if (children.length > 0) {
163 | node.children = children;
164 | }
165 |
166 | if (attrs) {
167 | node.attrs = attrs;
168 | }
169 |
170 | if (dirs) {
171 | node.dirs = dirs;
172 | }
173 |
174 | return node;
175 | }
176 |
177 | return {
178 | parse: function(template) {
179 | return walk(__webpack_require__(2)(template));
180 | }
181 | };
182 | }];
183 |
184 | module.exports = templateParser;
185 |
186 | /***/ },
187 | /* 2 */
188 | /***/ function(module, exports) {
189 |
190 |
191 | /**
192 | * Expose `parse`.
193 | */
194 |
195 | module.exports = parse;
196 |
197 | /**
198 | * Tests for browser support.
199 | */
200 |
201 | var innerHTMLBug = false;
202 | var bugTestDiv;
203 | if (typeof document !== 'undefined') {
204 | bugTestDiv = document.createElement('div');
205 | // Setup
206 | bugTestDiv.innerHTML = ' a ';
207 | // Make sure that link elements get serialized correctly by innerHTML
208 | // This requires a wrapper element in IE
209 | innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
210 | bugTestDiv = undefined;
211 | }
212 |
213 | /**
214 | * Wrap map from jquery.
215 | */
216 |
217 | var map = {
218 | legend: [1, '', ' '],
219 | tr: [2, ''],
220 | col: [2, ''],
221 | // for script/link/style tags to work in IE6-8, you have to wrap
222 | // in a div with a non-whitespace character in front, ha!
223 | _default: innerHTMLBug ? [1, 'X', '
'] : [0, '', '']
224 | };
225 |
226 | map.td =
227 | map.th = [3, ''];
228 |
229 | map.option =
230 | map.optgroup = [1, '', ' '];
231 |
232 | map.thead =
233 | map.tbody =
234 | map.colgroup =
235 | map.caption =
236 | map.tfoot = [1, ''];
237 |
238 | map.polyline =
239 | map.ellipse =
240 | map.polygon =
241 | map.circle =
242 | map.text =
243 | map.line =
244 | map.path =
245 | map.rect =
246 | map.g = [1, '',' '];
247 |
248 | /**
249 | * Parse `html` and return a DOM Node instance, which could be a TextNode,
250 | * HTML DOM Node of some kind ( for example), or a DocumentFragment
251 | * instance, depending on the contents of the `html` string.
252 | *
253 | * @param {String} html - HTML string to "domify"
254 | * @param {Document} doc - The `document` instance to create the Node for
255 | * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
256 | * @api private
257 | */
258 |
259 | function parse(html, doc) {
260 | if ('string' != typeof html) throw new TypeError('String expected');
261 |
262 | // default to the global `document` object
263 | if (!doc) doc = document;
264 |
265 | // tag name
266 | var m = /<([\w:]+)/.exec(html);
267 | if (!m) return doc.createTextNode(html);
268 |
269 | html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
270 |
271 | var tag = m[1];
272 |
273 | // body support
274 | if (tag == 'body') {
275 | var el = doc.createElement('html');
276 | el.innerHTML = html;
277 | return el.removeChild(el.lastChild);
278 | }
279 |
280 | // wrap map
281 | var wrap = map[tag] || map._default;
282 | var depth = wrap[0];
283 | var prefix = wrap[1];
284 | var suffix = wrap[2];
285 | var el = doc.createElement('div');
286 | el.innerHTML = prefix + html + suffix;
287 | while (depth--) el = el.lastChild;
288 |
289 | // one element
290 | if (el.firstChild == el.lastChild) {
291 | return el.removeChild(el.firstChild);
292 | }
293 |
294 | // several elements
295 | var fragment = doc.createDocumentFragment();
296 | while (el.firstChild) {
297 | fragment.appendChild(el.removeChild(el.firstChild));
298 | }
299 |
300 | return fragment;
301 | }
302 |
303 |
304 | /***/ },
305 | /* 3 */
306 | /***/ function(module, exports) {
307 |
308 | var trim = function (string) {
309 | return string.replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
310 | };
311 |
312 | var templateBuilder = [function() {
313 | function shallowClone(object) {
314 | var result = {};
315 | for (var prop in object) {
316 | if (object.hasOwnProperty(prop)) result[prop] = object[prop];
317 | }
318 | return result;
319 | }
320 |
321 | function newContext(context) {
322 | var empty = function() {};
323 | empty.prototype = context;
324 |
325 | return new empty();
326 | }
327 |
328 | function getStyle(object) {
329 | var cssText = '';
330 | for (var prop in object) {
331 | if (object.hasOwnProperty(prop)) {
332 | cssText += prop + ':' + object[prop] + ';';
333 | }
334 | }
335 | return cssText;
336 | }
337 |
338 | function getClasses(object) {
339 | var classes = [];
340 | for (var prop in object) {
341 | if (object.hasOwnProperty(prop) && !!object[prop]) {
342 | classes.push(prop);
343 | }
344 | }
345 | return classes.join(' ');
346 | }
347 |
348 | function cloneRepeatNode(node) {
349 | var cloneNode = shallowClone(node);
350 | cloneNode.dirs = shallowClone(node.dirs);
351 | cloneNode.dirs['ng-repeat'] = null;
352 | delete cloneNode.dirs['ng-repeat'];
353 |
354 | return cloneNode;
355 | }
356 |
357 | var ATTR_DIR_MAP = {
358 | 'ng-src': true,
359 | 'ng-href': true,
360 | 'ng-alt': true,
361 | 'ng-title': true,
362 | 'ng-id': true,
363 | 'ng-disabled': true,
364 | 'ng-value': true
365 | };
366 |
367 | var HTML_DIR_MAP = {
368 | 'ng-html': true,
369 | 'ng-bind': true,
370 | 'ng-text': true
371 | };
372 |
373 | var emptyArray = [];
374 |
375 | function toHTML(node, context) {
376 | var html, i, j;
377 | if (node instanceof Array) {
378 | html = '';
379 |
380 | for (i = 0, j = node.length; i < j; i++) {
381 | html += toHTML(node[i], context);
382 | }
383 |
384 | return html;
385 | }
386 |
387 | if (typeof node === 'string') return node;
388 | if (typeof node === 'function') return node(context);
389 |
390 | var tag = node.tagName;
391 | var children = node.children;
392 | var attrs = node.attrs;
393 | var dirs = node.dirs;
394 | var content = node.textContent;
395 | var classes = '';
396 | var cssText = '';
397 |
398 | if (dirs && dirs['ng-repeat']) {
399 | var cloneNode = cloneRepeatNode(node);
400 | var repeatDir = dirs['ng-repeat'];
401 | var array = repeatDir.getArray(context) || emptyArray;
402 | var itemName = repeatDir.itemName;
403 |
404 | html = '';
405 |
406 | for (i = 0, j = array.length; i < j; i++) {
407 | var subContext = newContext(context);
408 | subContext.$index = i;
409 | subContext.$first = i === 0;
410 | subContext.$last = i === j - 1;
411 | subContext.$middle = !(subContext.$first || subContext.$last);
412 | subContext[itemName] = array[i];
413 |
414 | html += toHTML(cloneNode, subContext);
415 | }
416 |
417 | return html;
418 | }
419 |
420 | html = '<' + tag;
421 |
422 | for (var dir in dirs) {
423 | if (dirs.hasOwnProperty(dir)) {
424 | var fn = dirs[dir];
425 | var value;
426 | if (typeof fn === 'function') {
427 | value = fn(context);
428 | }
429 |
430 | if (dir === 'ng-if') {
431 | if (!value) {
432 | return '';
433 | }
434 | } else if (ATTR_DIR_MAP[dir]) {
435 | html += ' ' + dir.substr(3) + '="' + value + '"';
436 | } else if (HTML_DIR_MAP[dir]) {
437 | content = value;
438 | } else if (dir === 'ng-style') {
439 | cssText = getStyle(value) + cssText;
440 | } else if (dir === 'ng-class') {
441 | classes += getClasses(value);
442 | } else if (dir === 'ng-show' || dir === 'ng-hide') {
443 | if (value !== null && value !== undefined) {
444 | classes += ' ' + dir;
445 | }
446 | }
447 | }
448 | }
449 |
450 | for (var attr in attrs) {
451 | if (attrs.hasOwnProperty(attr)) {
452 | if (attr === 'style') continue;
453 |
454 | var attrValue = attrs[attr];
455 | if (typeof attrValue === 'function') {
456 | attrValue = attrValue(context);
457 | }
458 | if (attr === 'class') {
459 | classes += ' ' + attrValue;
460 | } else {
461 | html += ' ' + attr + '="' + attrValue + '"';
462 | }
463 | }
464 | }
465 |
466 | if (classes) {
467 | html += ' class="' + trim(classes) + '"';
468 | }
469 |
470 | if (cssText) {
471 | html += ' style="' + cssText + '"';
472 | }
473 |
474 | html += '>';
475 |
476 | if (content) {
477 | html += content;
478 | }
479 |
480 | if (children) {
481 | for (i = 0, j = children.length; i < j; i++) {
482 | var child = children[i];
483 | html += toHTML(child, context);
484 | }
485 | }
486 |
487 | html += '' + tag + '>';
488 |
489 | return html;
490 | }
491 |
492 | return {
493 | build: toHTML
494 | };
495 | }];
496 |
497 | module.exports = templateBuilder;
498 |
499 | /***/ }
500 | /******/ ]);
501 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZjdkNTNjOTk2N2FhMTA3MWQ2NzAiLCJ3ZWJwYWNrOi8vLy4vc3JjL2luZGV4LmpzIiwid2VicGFjazovLy8uL3NyYy9wYXJzZXIuanMiLCJ3ZWJwYWNrOi8vLy4vfi9kb21pZnkvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL2J1aWxkZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVCQUFlO0FBQ2Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7Ozs7Ozs7QUN0Q0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsZ0JBQWU7QUFDZjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBRzs7QUFFSCxxQjs7Ozs7O0FDckNBO0FBQ0EsMEJBQXlCO0FBQ3pCO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBLGlCQUFnQjtBQUNoQjtBQUNBOztBQUVBLHVDQUFzQyxPQUFPO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVM7QUFDVDtBQUNBO0FBQ0EsUUFBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLG9CQUFtQixhQUFhO0FBQ2hDO0FBQ0EsVUFBUztBQUNUO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsdUNBQXNDLE9BQU87QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBcUIsYUFBYTtBQUNsQztBQUNBLFlBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFDOztBQUVELGlDOzs7Ozs7O0FDN0ZBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVcsT0FBTztBQUNsQixZQUFXLFNBQVM7QUFDcEIsYUFBWSxRQUFRO0FBQ3BCO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSx5Q0FBd0M7O0FBRXhDOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7Ozs7OztBQy9HQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtEQUFpRDtBQUNqRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLG1DQUFrQyxPQUFPO0FBQ3pDO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUEsb0NBQW1DLE9BQU87QUFDMUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVM7QUFDVDtBQUNBLFVBQVM7QUFDVDtBQUNBLFVBQVM7QUFDVDtBQUNBLFVBQVM7QUFDVDtBQUNBLFVBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHVDQUFzQyxPQUFPO0FBQzdDO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsRUFBQzs7QUFFRCxrQyIsImZpbGUiOiJuZy1zdGF0aWNpemUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSlcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcblxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0ZXhwb3J0czoge30sXG4gXHRcdFx0aWQ6IG1vZHVsZUlkLFxuIFx0XHRcdGxvYWRlZDogZmFsc2VcbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubG9hZGVkID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXygwKTtcblxuXG5cbi8qKiBXRUJQQUNLIEZPT1RFUiAqKlxuICoqIHdlYnBhY2svYm9vdHN0cmFwIGY3ZDUzYzk5NjdhYTEwNzFkNjcwXG4gKiovIiwidmFyIHBhcnNlciA9IHJlcXVpcmUoJy4vcGFyc2VyJyk7XG52YXIgYnVpbGRlciA9IHJlcXVpcmUoJy4vYnVpbGRlcicpO1xuXG5hbmd1bGFyLm1vZHVsZSgnbmdTdGF0aWNpemUnLCBbXSlcbiAgLmZhY3RvcnkoJ3RlbXBsYXRlUGFyc2VyJywgcGFyc2VyKVxuICAuZmFjdG9yeSgndGVtcGxhdGVCdWlsZGVyJywgYnVpbGRlcilcbiAgLmRpcmVjdGl2ZSgnbmdTdGF0aWNpemUnLCBbJ3RlbXBsYXRlUGFyc2VyJywgJ3RlbXBsYXRlQnVpbGRlcicsICckcGFyc2UnLCBmdW5jdGlvbih0ZW1wbGF0ZVBhcnNlciwgdGVtcGxhdGVCdWlsZGVyKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHJlc3RyaWN0OiAnRUEnLFxuICAgICAgcmVwbGFjZTogdHJ1ZSxcbiAgICAgIHRlcm1pbmFsOiB0cnVlLFxuICAgICAgcHJpb3JpdHk6IDEwMDEsXG4gICAgICBjb21waWxlOiBmdW5jdGlvbihlbGVtZW50KSB7XG4gICAgICAgIHZhciBodG1sID0gZWxlbWVudFswXS5pbm5lckhUTUw7XG4gICAgICAgIHZhciB0ZW1wbGF0ZSA9IHRlbXBsYXRlUGFyc2VyLnBhcnNlKGh0bWwpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHBvc3Q6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtZW50LCBhdHRycykge1xuICAgICAgICAgICAgdmFyIHJlUmVuZGVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICB2YXIgcmVzdWx0ID0gdGVtcGxhdGVCdWlsZGVyLmJ1aWxkKHRlbXBsYXRlLCBzY29wZSk7XG4gICAgICAgICAgICAgIGVsZW1lbnRbMF0uaW5uZXJIVE1MID0gcmVzdWx0O1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgcmVSZW5kZXIoKTtcblxuICAgICAgICAgICAgdmFyIHdhdGNoRXhwciA9IGF0dHJzLm5nU3RhdGljaXplO1xuXG4gICAgICAgICAgICBpZiAod2F0Y2hFeHByKSB7XG4gICAgICAgICAgICAgIHNjb3BlLiR3YXRjaCh3YXRjaEV4cHIsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIHJlUmVuZGVyKCk7XG4gICAgICAgICAgICAgIH0sIHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9O1xuICB9XSk7XG5cbm1vZHVsZS5leHBvcnRzID0ge307XG5cblxuLyoqKioqKioqKioqKioqKioqXG4gKiogV0VCUEFDSyBGT09URVJcbiAqKiAuL3NyYy9pbmRleC5qc1xuICoqIG1vZHVsZSBpZCA9IDBcbiAqKiBtb2R1bGUgY2h1bmtzID0gMFxuICoqLyIsInZhciB0ZW1wbGF0ZVBhcnNlciA9IFsnJHBhcnNlJywgJyRpbnRlcnBvbGF0ZScsIGZ1bmN0aW9uKCRwYXJzZSwgJGludGVycG9sYXRlKSB7XG4gIHZhciBuZWVkSW50ZXJwb2xhdGUgPSB7ICduZy1zcmMnOiB0cnVlLCAnbmctaHJlZic6IHRydWUgfTtcbiAgdmFyIFRFWFRfTk9ERSA9IDM7XG4gIHZhciBFTEVNRU5UX05PREUgPSAxO1xuXG4gIGZ1bmN0aW9uIHdhbGsoZWwpIHtcbiAgICBpZiAoZWwubm9kZVR5cGUgIT09IEVMRU1FTlRfTk9ERSkgcmV0dXJuO1xuXG4gICAgdmFyIGNoaWxkcmVuID0gW107XG4gICAgdmFyIG5vZGUgPSB7IHRhZ05hbWU6IGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSB9O1xuICAgIHZhciBhdHRyaWJ1dGVzID0gZWwuYXR0cmlidXRlcztcbiAgICB2YXIgYXR0cnMsIGRpcnMsIGksIGo7XG5cbiAgICBmb3IgKGkgPSAwLCBqID0gYXR0cmlidXRlcy5sZW5ndGg7IGkgPCBqOyBpKyspIHtcbiAgICAgIHZhciBhdHRyaWJ1dGUgPSBhdHRyaWJ1dGVzW2ldO1xuICAgICAgdmFyIG5hbWUgPSBhdHRyaWJ1dGUubmFtZTtcbiAgICAgIHZhciB2YWx1ZSA9IGF0dHJpYnV0ZS5ub2RlVmFsdWU7XG4gICAgICBpZiAobmFtZSAmJiBuYW1lLnN1YnN0cigwLCAzKSA9PT0gJ25nLScpIHtcbiAgICAgICAgaWYgKCFkaXJzKSB7XG4gICAgICAgICAgZGlycyA9IHt9O1xuICAgICAgICB9XG4gICAgICAgIGlmIChuYW1lLmxlbmd0aCA+IDggJiYgbmFtZS5zdWJzdHIoMCwgOCkgPT09ICduZy1hdHRyLScpIHtcbiAgICAgICAgICBpZiAoIWF0dHJzKSB7XG4gICAgICAgICAgICBhdHRycyA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICBhdHRyc1tuYW1lLnN1YnN0cig5KV0gPSAkcGFyc2UodmFsdWUpO1xuICAgICAgICB9IGVsc2UgaWYgKG5hbWUgPT09ICduZy1yZXBlYXQnKSB7XG4gICAgICAgICAgdmFyIG1hdGNoZXMgPSAvKFxcdyspXFxzK2luXFxzKyguKj8pJC8uZXhlYyh2YWx1ZSk7XG4gICAgICAgICAgaWYgKG1hdGNoZXMpIHtcbiAgICAgICAgICAgIGRpcnNbbmFtZV0gPSB7XG4gICAgICAgICAgICAgIGl0ZW1OYW1lOiBtYXRjaGVzWzFdLFxuICAgICAgICAgICAgICBnZXRBcnJheTogJHBhcnNlKG1hdGNoZXNbMl0pXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBkaXJzW25hbWVdID0gbmVlZEludGVycG9sYXRlW25hbWVdID8gJGludGVycG9sYXRlKHZhbHVlKSA6ICRwYXJzZSh2YWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICghYXR0cnMpIHtcbiAgICAgICAgICBhdHRycyA9IHt9O1xuICAgICAgICB9XG4gICAgICAgIGlmICgvXFxzKih7e1xccyooLis/KVxccyp9fSlcXHMqL2dpLnRlc3QodmFsdWUpKSB7XG4gICAgICAgICAgYXR0cnNbbmFtZV0gPSAkaW50ZXJwb2xhdGUodmFsdWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGF0dHJzW25hbWVdID0gdmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgY2hpbGROb2RlcyA9IGVsLmNoaWxkTm9kZXM7XG4gICAgZm9yIChpID0gMCwgaiA9IGNoaWxkTm9kZXMubGVuZ3RoOyBpIDwgajsgaSsrKSB7XG4gICAgICB2YXIgY2hpbGQgPSBjaGlsZE5vZGVzW2ldO1xuICAgICAgaWYgKGNoaWxkLm5vZGVUeXBlID09PSBURVhUX05PREUpIHtcbiAgICAgICAgdmFyIHRleHQgPSBjaGlsZC5ub2RlVmFsdWU7XG4gICAgICAgIGlmICh0ZXh0KSB7XG4gICAgICAgICAgaWYgKC9cXHMqKHt7XFxzKiguKz8pXFxzKn19KVxccyovZ2kudGVzdCh0ZXh0KSkge1xuICAgICAgICAgICAgY2hpbGRyZW4ucHVzaCgkaW50ZXJwb2xhdGUodGV4dCkpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0ZXh0ID0gdGV4dC5yZXBsYWNlKC8oXFxyXFxufFxcbnxcXHJ8XFxzKS9nbSwgJycpO1xuICAgICAgICAgICAgaWYgKHRleHQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIGNoaWxkcmVuLnB1c2godGV4dCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdmFyIHBhcnNlUmVzdWx0ID0gd2FsayhjaGlsZCk7XG4gICAgICBpZiAocGFyc2VSZXN1bHQpIHtcbiAgICAgICAgY2hpbGRyZW4ucHVzaChwYXJzZVJlc3VsdCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgIG5vZGUuY2hpbGRyZW4gPSBjaGlsZHJlbjtcbiAgICB9XG5cbiAgICBpZiAoYXR0cnMpIHtcbiAgICAgIG5vZGUuYXR0cnMgPSBhdHRycztcbiAgICB9XG5cbiAgICBpZiAoZGlycykge1xuICAgICAgbm9kZS5kaXJzID0gZGlycztcbiAgICB9XG5cbiAgICByZXR1cm4gbm9kZTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgcGFyc2U6IGZ1bmN0aW9uKHRlbXBsYXRlKSB7XG4gICAgICByZXR1cm4gd2FsayhyZXF1aXJlKCdkb21pZnknKSh0ZW1wbGF0ZSkpO1xuICAgIH1cbiAgfTtcbn1dO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHRlbXBsYXRlUGFyc2VyO1xuXG5cbi8qKioqKioqKioqKioqKioqKlxuICoqIFdFQlBBQ0sgRk9PVEVSXG4gKiogLi9zcmMvcGFyc2VyLmpzXG4gKiogbW9kdWxlIGlkID0gMVxuICoqIG1vZHVsZSBjaHVua3MgPSAwXG4gKiovIiwiXG4vKipcbiAqIEV4cG9zZSBgcGFyc2VgLlxuICovXG5cbm1vZHVsZS5leHBvcnRzID0gcGFyc2U7XG5cbi8qKlxuICogVGVzdHMgZm9yIGJyb3dzZXIgc3VwcG9ydC5cbiAqL1xuXG52YXIgaW5uZXJIVE1MQnVnID0gZmFsc2U7XG52YXIgYnVnVGVzdERpdjtcbmlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnKSB7XG4gIGJ1Z1Rlc3REaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgLy8gU2V0dXBcbiAgYnVnVGVzdERpdi5pbm5lckhUTUwgPSAnICA8bGluay8+PHRhYmxlPjwvdGFibGU+PGEgaHJlZj1cIi9hXCI+YTwvYT48aW5wdXQgdHlwZT1cImNoZWNrYm94XCIvPic7XG4gIC8vIE1ha2Ugc3VyZSB0aGF0IGxpbmsgZWxlbWVudHMgZ2V0IHNlcmlhbGl6ZWQgY29ycmVjdGx5IGJ5IGlubmVySFRNTFxuICAvLyBUaGlzIHJlcXVpcmVzIGEgd3JhcHBlciBlbGVtZW50IGluIElFXG4gIGlubmVySFRNTEJ1ZyA9ICFidWdUZXN0RGl2LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJykubGVuZ3RoO1xuICBidWdUZXN0RGl2ID0gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFdyYXAgbWFwIGZyb20ganF1ZXJ5LlxuICovXG5cbnZhciBtYXAgPSB7XG4gIGxlZ2VuZDogWzEsICc8ZmllbGRzZXQ+JywgJzwvZmllbGRzZXQ+J10sXG4gIHRyOiBbMiwgJzx0YWJsZT48dGJvZHk+JywgJzwvdGJvZHk+PC90YWJsZT4nXSxcbiAgY29sOiBbMiwgJzx0YWJsZT48dGJvZHk+PC90Ym9keT48Y29sZ3JvdXA+JywgJzwvY29sZ3JvdXA+PC90YWJsZT4nXSxcbiAgLy8gZm9yIHNjcmlwdC9saW5rL3N0eWxlIHRhZ3MgdG8gd29yayBpbiBJRTYtOCwgeW91IGhhdmUgdG8gd3JhcFxuICAvLyBpbiBhIGRpdiB3aXRoIGEgbm9uLXdoaXRlc3BhY2UgY2hhcmFjdGVyIGluIGZyb250LCBoYSFcbiAgX2RlZmF1bHQ6IGlubmVySFRNTEJ1ZyA/IFsxLCAnWDxkaXY+JywgJzwvZGl2PiddIDogWzAsICcnLCAnJ11cbn07XG5cbm1hcC50ZCA9XG5tYXAudGggPSBbMywgJzx0YWJsZT48dGJvZHk+PHRyPicsICc8L3RyPjwvdGJvZHk+PC90YWJsZT4nXTtcblxubWFwLm9wdGlvbiA9XG5tYXAub3B0Z3JvdXAgPSBbMSwgJzxzZWxlY3QgbXVsdGlwbGU9XCJtdWx0aXBsZVwiPicsICc8L3NlbGVjdD4nXTtcblxubWFwLnRoZWFkID1cbm1hcC50Ym9keSA9XG5tYXAuY29sZ3JvdXAgPVxubWFwLmNhcHRpb24gPVxubWFwLnRmb290ID0gWzEsICc8dGFibGU+JywgJzwvdGFibGU+J107XG5cbm1hcC5wb2x5bGluZSA9XG5tYXAuZWxsaXBzZSA9XG5tYXAucG9seWdvbiA9XG5tYXAuY2lyY2xlID1cbm1hcC50ZXh0ID1cbm1hcC5saW5lID1cbm1hcC5wYXRoID1cbm1hcC5yZWN0ID1cbm1hcC5nID0gWzEsICc8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB2ZXJzaW9uPVwiMS4xXCI+JywnPC9zdmc+J107XG5cbi8qKlxuICogUGFyc2UgYGh0bWxgIGFuZCByZXR1cm4gYSBET00gTm9kZSBpbnN0YW5jZSwgd2hpY2ggY291bGQgYmUgYSBUZXh0Tm9kZSxcbiAqIEhUTUwgRE9NIE5vZGUgb2Ygc29tZSBraW5kICg8ZGl2PiBmb3IgZXhhbXBsZSksIG9yIGEgRG9jdW1lbnRGcmFnbWVudFxuICogaW5zdGFuY2UsIGRlcGVuZGluZyBvbiB0aGUgY29udGVudHMgb2YgdGhlIGBodG1sYCBzdHJpbmcuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGh0bWwgLSBIVE1MIHN0cmluZyB0byBcImRvbWlmeVwiXG4gKiBAcGFyYW0ge0RvY3VtZW50fSBkb2MgLSBUaGUgYGRvY3VtZW50YCBpbnN0YW5jZSB0byBjcmVhdGUgdGhlIE5vZGUgZm9yXG4gKiBAcmV0dXJuIHtET01Ob2RlfSB0aGUgVGV4dE5vZGUsIERPTSBOb2RlLCBvciBEb2N1bWVudEZyYWdtZW50IGluc3RhbmNlXG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG5mdW5jdGlvbiBwYXJzZShodG1sLCBkb2MpIHtcbiAgaWYgKCdzdHJpbmcnICE9IHR5cGVvZiBodG1sKSB0aHJvdyBuZXcgVHlwZUVycm9yKCdTdHJpbmcgZXhwZWN0ZWQnKTtcblxuICAvLyBkZWZhdWx0IHRvIHRoZSBnbG9iYWwgYGRvY3VtZW50YCBvYmplY3RcbiAgaWYgKCFkb2MpIGRvYyA9IGRvY3VtZW50O1xuXG4gIC8vIHRhZyBuYW1lXG4gIHZhciBtID0gLzwoW1xcdzpdKykvLmV4ZWMoaHRtbCk7XG4gIGlmICghbSkgcmV0dXJuIGRvYy5jcmVhdGVUZXh0Tm9kZShodG1sKTtcblxuICBodG1sID0gaHRtbC5yZXBsYWNlKC9eXFxzK3xcXHMrJC9nLCAnJyk7IC8vIFJlbW92ZSBsZWFkaW5nL3RyYWlsaW5nIHdoaXRlc3BhY2VcblxuICB2YXIgdGFnID0gbVsxXTtcblxuICAvLyBib2R5IHN1cHBvcnRcbiAgaWYgKHRhZyA9PSAnYm9keScpIHtcbiAgICB2YXIgZWwgPSBkb2MuY3JlYXRlRWxlbWVudCgnaHRtbCcpO1xuICAgIGVsLmlubmVySFRNTCA9IGh0bWw7XG4gICAgcmV0dXJuIGVsLnJlbW92ZUNoaWxkKGVsLmxhc3RDaGlsZCk7XG4gIH1cblxuICAvLyB3cmFwIG1hcFxuICB2YXIgd3JhcCA9IG1hcFt0YWddIHx8IG1hcC5fZGVmYXVsdDtcbiAgdmFyIGRlcHRoID0gd3JhcFswXTtcbiAgdmFyIHByZWZpeCA9IHdyYXBbMV07XG4gIHZhciBzdWZmaXggPSB3cmFwWzJdO1xuICB2YXIgZWwgPSBkb2MuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gIGVsLmlubmVySFRNTCA9IHByZWZpeCArIGh0bWwgKyBzdWZmaXg7XG4gIHdoaWxlIChkZXB0aC0tKSBlbCA9IGVsLmxhc3RDaGlsZDtcblxuICAvLyBvbmUgZWxlbWVudFxuICBpZiAoZWwuZmlyc3RDaGlsZCA9PSBlbC5sYXN0Q2hpbGQpIHtcbiAgICByZXR1cm4gZWwucmVtb3ZlQ2hpbGQoZWwuZmlyc3RDaGlsZCk7XG4gIH1cblxuICAvLyBzZXZlcmFsIGVsZW1lbnRzXG4gIHZhciBmcmFnbWVudCA9IGRvYy5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7XG4gIHdoaWxlIChlbC5maXJzdENoaWxkKSB7XG4gICAgZnJhZ21lbnQuYXBwZW5kQ2hpbGQoZWwucmVtb3ZlQ2hpbGQoZWwuZmlyc3RDaGlsZCkpO1xuICB9XG5cbiAgcmV0dXJuIGZyYWdtZW50O1xufVxuXG5cblxuLyoqKioqKioqKioqKioqKioqXG4gKiogV0VCUEFDSyBGT09URVJcbiAqKiAuL34vZG9taWZ5L2luZGV4LmpzXG4gKiogbW9kdWxlIGlkID0gMlxuICoqIG1vZHVsZSBjaHVua3MgPSAwXG4gKiovIiwidmFyIHRyaW0gPSBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gIHJldHVybiBzdHJpbmcucmVwbGFjZSgvXltcXHNcXHVGRUZGXSt8W1xcc1xcdUZFRkZdKyQvZywgJycpO1xufTtcblxudmFyIHRlbXBsYXRlQnVpbGRlciA9IFtmdW5jdGlvbigpIHtcbiAgZnVuY3Rpb24gc2hhbGxvd0Nsb25lKG9iamVjdCkge1xuICAgIHZhciByZXN1bHQgPSB7fTtcbiAgICBmb3IgKHZhciBwcm9wIGluIG9iamVjdCkge1xuICAgICAgaWYgKG9iamVjdC5oYXNPd25Qcm9wZXJ0eShwcm9wKSkgcmVzdWx0W3Byb3BdID0gb2JqZWN0W3Byb3BdO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbmV3Q29udGV4dChjb250ZXh0KSB7XG4gICAgdmFyIGVtcHR5ID0gZnVuY3Rpb24oKSB7fTtcbiAgICBlbXB0eS5wcm90b3R5cGUgPSBjb250ZXh0O1xuXG4gICAgcmV0dXJuIG5ldyBlbXB0eSgpO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0U3R5bGUob2JqZWN0KSB7XG4gICAgdmFyIGNzc1RleHQgPSAnJztcbiAgICBmb3IgKHZhciBwcm9wIGluIG9iamVjdCkge1xuICAgICAgaWYgKG9iamVjdC5oYXNPd25Qcm9wZXJ0eShwcm9wKSkge1xuICAgICAgICBjc3NUZXh0ICs9IHByb3AgKyAnOicgKyBvYmplY3RbcHJvcF0gKyAnOyc7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBjc3NUZXh0O1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0Q2xhc3NlcyhvYmplY3QpIHtcbiAgICB2YXIgY2xhc3NlcyA9IFtdO1xuICAgIGZvciAodmFyIHByb3AgaW4gb2JqZWN0KSB7XG4gICAgICBpZiAob2JqZWN0Lmhhc093blByb3BlcnR5KHByb3ApICYmICEhb2JqZWN0W3Byb3BdKSB7XG4gICAgICAgIGNsYXNzZXMucHVzaChwcm9wKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNsYXNzZXMuam9pbignICcpO1xuICB9XG5cbiAgZnVuY3Rpb24gY2xvbmVSZXBlYXROb2RlKG5vZGUpIHtcbiAgICB2YXIgY2xvbmVOb2RlID0gc2hhbGxvd0Nsb25lKG5vZGUpO1xuICAgIGNsb25lTm9kZS5kaXJzID0gc2hhbGxvd0Nsb25lKG5vZGUuZGlycyk7XG4gICAgY2xvbmVOb2RlLmRpcnNbJ25nLXJlcGVhdCddID0gbnVsbDtcbiAgICBkZWxldGUgY2xvbmVOb2RlLmRpcnNbJ25nLXJlcGVhdCddO1xuXG4gICAgcmV0dXJuIGNsb25lTm9kZTtcbiAgfVxuXG4gIHZhciBBVFRSX0RJUl9NQVAgPSB7XG4gICAgJ25nLXNyYyc6IHRydWUsXG4gICAgJ25nLWhyZWYnOiB0cnVlLFxuICAgICduZy1hbHQnOiB0cnVlLFxuICAgICduZy10aXRsZSc6IHRydWUsXG4gICAgJ25nLWlkJzogdHJ1ZSxcbiAgICAnbmctZGlzYWJsZWQnOiB0cnVlLFxuICAgICduZy12YWx1ZSc6IHRydWVcbiAgfTtcblxuICB2YXIgSFRNTF9ESVJfTUFQID0ge1xuICAgICduZy1odG1sJzogdHJ1ZSxcbiAgICAnbmctYmluZCc6IHRydWUsXG4gICAgJ25nLXRleHQnOiB0cnVlXG4gIH07XG5cbiAgdmFyIGVtcHR5QXJyYXkgPSBbXTtcblxuICBmdW5jdGlvbiB0b0hUTUwobm9kZSwgY29udGV4dCkge1xuICAgIHZhciBodG1sLCBpLCBqO1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICAgIGh0bWwgPSAnJztcblxuICAgICAgZm9yIChpID0gMCwgaiA9IG5vZGUubGVuZ3RoOyBpIDwgajsgaSsrKSB7XG4gICAgICAgIGh0bWwgKz0gdG9IVE1MKG5vZGVbaV0sIGNvbnRleHQpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gaHRtbDtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIG5vZGUgPT09ICdzdHJpbmcnKSByZXR1cm4gbm9kZTtcbiAgICBpZiAodHlwZW9mIG5vZGUgPT09ICdmdW5jdGlvbicpIHJldHVybiBub2RlKGNvbnRleHQpO1xuXG4gICAgdmFyIHRhZyA9IG5vZGUudGFnTmFtZTtcbiAgICB2YXIgY2hpbGRyZW4gPSBub2RlLmNoaWxkcmVuO1xuICAgIHZhciBhdHRycyA9IG5vZGUuYXR0cnM7XG4gICAgdmFyIGRpcnMgPSBub2RlLmRpcnM7XG4gICAgdmFyIGNvbnRlbnQgPSBub2RlLnRleHRDb250ZW50O1xuICAgIHZhciBjbGFzc2VzID0gJyc7XG4gICAgdmFyIGNzc1RleHQgPSAnJztcblxuICAgIGlmIChkaXJzICYmIGRpcnNbJ25nLXJlcGVhdCddKSB7XG4gICAgICB2YXIgY2xvbmVOb2RlID0gY2xvbmVSZXBlYXROb2RlKG5vZGUpO1xuICAgICAgdmFyIHJlcGVhdERpciA9IGRpcnNbJ25nLXJlcGVhdCddO1xuICAgICAgdmFyIGFycmF5ID0gcmVwZWF0RGlyLmdldEFycmF5KGNvbnRleHQpIHx8IGVtcHR5QXJyYXk7XG4gICAgICB2YXIgaXRlbU5hbWUgPSByZXBlYXREaXIuaXRlbU5hbWU7XG5cbiAgICAgIGh0bWwgPSAnJztcblxuICAgICAgZm9yIChpID0gMCwgaiA9IGFycmF5Lmxlbmd0aDsgaSA8IGo7IGkrKykge1xuICAgICAgICB2YXIgc3ViQ29udGV4dCA9IG5ld0NvbnRleHQoY29udGV4dCk7XG4gICAgICAgIHN1YkNvbnRleHQuJGluZGV4ID0gaTtcbiAgICAgICAgc3ViQ29udGV4dC4kZmlyc3QgPSBpID09PSAwO1xuICAgICAgICBzdWJDb250ZXh0LiRsYXN0ID0gaSA9PT0gaiAtIDE7XG4gICAgICAgIHN1YkNvbnRleHQuJG1pZGRsZSA9ICEoc3ViQ29udGV4dC4kZmlyc3QgfHwgc3ViQ29udGV4dC4kbGFzdCk7XG4gICAgICAgIHN1YkNvbnRleHRbaXRlbU5hbWVdID0gYXJyYXlbaV07XG5cbiAgICAgICAgaHRtbCArPSB0b0hUTUwoY2xvbmVOb2RlLCBzdWJDb250ZXh0KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGh0bWw7XG4gICAgfVxuXG4gICAgaHRtbCA9ICc8JyArIHRhZztcblxuICAgIGZvciAodmFyIGRpciBpbiBkaXJzKSB7XG4gICAgICBpZiAoZGlycy5oYXNPd25Qcm9wZXJ0eShkaXIpKSB7XG4gICAgICAgIHZhciBmbiA9IGRpcnNbZGlyXTtcbiAgICAgICAgdmFyIHZhbHVlO1xuICAgICAgICBpZiAodHlwZW9mIGZuID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgdmFsdWUgPSBmbihjb250ZXh0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChkaXIgPT09ICduZy1pZicpIHtcbiAgICAgICAgICBpZiAoIXZhbHVlKSB7XG4gICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKEFUVFJfRElSX01BUFtkaXJdKSB7XG4gICAgICAgICAgaHRtbCArPSAnICcgKyBkaXIuc3Vic3RyKDMpICsgJz1cIicgKyB2YWx1ZSArICdcIic7XG4gICAgICAgIH0gZWxzZSBpZiAoSFRNTF9ESVJfTUFQW2Rpcl0pIHtcbiAgICAgICAgICBjb250ZW50ID0gdmFsdWU7XG4gICAgICAgIH0gZWxzZSBpZiAoZGlyID09PSAnbmctc3R5bGUnKSB7XG4gICAgICAgICAgY3NzVGV4dCA9IGdldFN0eWxlKHZhbHVlKSArIGNzc1RleHQ7XG4gICAgICAgIH0gZWxzZSBpZiAoZGlyID09PSAnbmctY2xhc3MnKSB7XG4gICAgICAgICAgY2xhc3NlcyArPSBnZXRDbGFzc2VzKHZhbHVlKTtcbiAgICAgICAgfSBlbHNlIGlmIChkaXIgPT09ICduZy1zaG93JyB8fCBkaXIgPT09ICduZy1oaWRlJykge1xuICAgICAgICAgIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBjbGFzc2VzICs9ICcgJyArIGRpcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKHZhciBhdHRyIGluIGF0dHJzKSB7XG4gICAgICBpZiAoYXR0cnMuaGFzT3duUHJvcGVydHkoYXR0cikpIHtcbiAgICAgICAgaWYgKGF0dHIgPT09ICdzdHlsZScpIGNvbnRpbnVlO1xuXG4gICAgICAgIHZhciBhdHRyVmFsdWUgPSBhdHRyc1thdHRyXTtcbiAgICAgICAgaWYgKHR5cGVvZiBhdHRyVmFsdWUgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICBhdHRyVmFsdWUgPSBhdHRyVmFsdWUoY29udGV4dCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGF0dHIgPT09ICdjbGFzcycpIHtcbiAgICAgICAgICBjbGFzc2VzICs9ICcgJyArIGF0dHJWYWx1ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBodG1sICs9ICcgJyArIGF0dHIgKyAnPVwiJyArIGF0dHJWYWx1ZSArICdcIic7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoY2xhc3Nlcykge1xuICAgICAgaHRtbCArPSAnIGNsYXNzPVwiJyArIHRyaW0oY2xhc3NlcykgKyAnXCInO1xuICAgIH1cblxuICAgIGlmIChjc3NUZXh0KSB7XG4gICAgICBodG1sICs9ICcgc3R5bGU9XCInICsgY3NzVGV4dCArICdcIic7XG4gICAgfVxuXG4gICAgaHRtbCArPSAnPic7XG5cbiAgICBpZiAoY29udGVudCkge1xuICAgICAgaHRtbCArPSBjb250ZW50O1xuICAgIH1cblxuICAgIGlmIChjaGlsZHJlbikge1xuICAgICAgZm9yIChpID0gMCwgaiA9IGNoaWxkcmVuLmxlbmd0aDsgaSA8IGo7IGkrKykge1xuICAgICAgICB2YXIgY2hpbGQgPSBjaGlsZHJlbltpXTtcbiAgICAgICAgaHRtbCArPSB0b0hUTUwoY2hpbGQsIGNvbnRleHQpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGh0bWwgKz0gJzwvJyArIHRhZyArICc+JztcblxuICAgIHJldHVybiBodG1sO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBidWlsZDogdG9IVE1MXG4gIH07XG59XTtcblxubW9kdWxlLmV4cG9ydHMgPSB0ZW1wbGF0ZUJ1aWxkZXI7XG5cblxuLyoqKioqKioqKioqKioqKioqXG4gKiogV0VCUEFDSyBGT09URVJcbiAqKiAuL3NyYy9idWlsZGVyLmpzXG4gKiogbW9kdWxlIGlkID0gM1xuICoqIG1vZHVsZSBjaHVua3MgPSAwXG4gKiovIl0sInNvdXJjZVJvb3QiOiIifQ==
--------------------------------------------------------------------------------