├── .editorconfig
├── .gitattributes
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── configs
├── tsconfig-build-amd.json
├── tsconfig-build-commonjs.json
├── tsconfig-build-es2015.json
├── tsconfig-build-es2017.json
├── tsconfig-build-native-modules.json
├── tsconfig-build-system.json
├── tsconfig-build.json
├── tsconfig-demo.json
├── tsconfig-projectroot.json
├── tsconfig-test.json
├── tsconfig-webpack.json
└── tsconfig.json
├── demo
├── app.css
├── app.html
├── app.ts
├── index.ejs
├── main.ts
├── resources
│ ├── elements
│ │ ├── dynamic-html.ts
│ │ ├── monaco-editor.css
│ │ ├── monaco-editor.html
│ │ └── monaco-editor.ts
│ └── index.ts
└── tsconfig.json
├── dist
├── amd
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
├── commonjs
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
├── es2015
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
├── es2017
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
├── native-modules
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
└── system
│ ├── aurelia-dynamic-html.d.ts
│ ├── aurelia-dynamic-html.js
│ ├── dynamic-html.d.ts
│ ├── dynamic-html.js
│ ├── interfaces.d.ts
│ └── interfaces.js
├── karma.conf.ts
├── package-scripts.js
├── package.json
├── rollup.config.js
├── src
├── aurelia-dynamic-html.ts
├── dynamic-html.ts
└── interfaces.ts
├── test
├── setup.ts
└── unit
│ ├── aurelia-dynamic-html.spec.ts
│ ├── dynamic-html.spec.ts
│ └── util.ts
├── tsconfig.json
├── tslint.json
└── webpack.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = space
11 | indent_size = 2
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ## GITATTRIBUTES FOR WEB PROJECTS
2 | #
3 | # These settings are for any web project.
4 | #
5 | # Details per file setting:
6 | # text These files should be normalized (i.e. convert CRLF to LF).
7 | # binary These files are binary and should be left untouched.
8 | #
9 | # Note that binary is a macro for -text -diff.
10 | ######################################################################
11 |
12 | ## AUTO-DETECT
13 | ## Handle line endings automatically for files detected as
14 | ## text and leave all files detected as binary untouched.
15 | ## This will handle all files NOT defined below.
16 | * text=auto
17 |
18 | ## SOURCE CODE
19 | *.bat text eol=crlf
20 | *.coffee text
21 | *.css text
22 | *.htm text
23 | *.html text
24 | *.inc text
25 | *.ini text
26 | *.js text
27 | *.json text
28 | *.jsx text
29 | *.less text
30 | *.od text
31 | *.onlydata text
32 | *.php text
33 | *.pl text
34 | *.py text
35 | *.rb text
36 | *.sass text
37 | *.scm text
38 | *.scss text
39 | *.sh text eol=lf
40 | *.sql text
41 | *.styl text
42 | *.tag text
43 | *.ts text
44 | *.tsx text
45 | *.xml text
46 | *.xhtml text
47 |
48 | ## DOCKER
49 | *.dockerignore text
50 | Dockerfile text
51 |
52 | ## DOCUMENTATION
53 | *.markdown text
54 | *.md text
55 | *.mdwn text
56 | *.mdown text
57 | *.mkd text
58 | *.mkdn text
59 | *.mdtxt text
60 | *.mdtext text
61 | *.txt text
62 | AUTHORS text
63 | CHANGELOG text
64 | CHANGES text
65 | CONTRIBUTING text
66 | COPYING text
67 | copyright text
68 | *COPYRIGHT* text
69 | INSTALL text
70 | license text
71 | LICENSE text
72 | NEWS text
73 | readme text
74 | *README* text
75 | TODO text
76 |
77 | ## TEMPLATES
78 | *.dot text
79 | *.ejs text
80 | *.haml text
81 | *.handlebars text
82 | *.hbs text
83 | *.hbt text
84 | *.jade text
85 | *.latte text
86 | *.mustache text
87 | *.njk text
88 | *.phtml text
89 | *.tmpl text
90 | *.tpl text
91 | *.twig text
92 |
93 | ## LINTERS
94 | .csslintrc text
95 | .eslintrc text
96 | .htmlhintrc text
97 | .jscsrc text
98 | .jshintrc text
99 | .jshintignore text
100 | .stylelintrc text
101 |
102 | ## CONFIGS
103 | *.bowerrc text
104 | *.cnf text
105 | *.conf text
106 | *.config text
107 | .browserslistrc text
108 | .editorconfig text
109 | .gitattributes text
110 | .gitconfig text
111 | .htaccess text
112 | *.npmignore text
113 | *.yaml text
114 | *.yml text
115 | browserslist text
116 | Makefile text
117 | makefile text
118 |
119 | ## HEROKU
120 | Procfile text
121 | .slugignore text
122 |
123 | ## GRAPHICS
124 | *.ai binary
125 | *.bmp binary
126 | *.eps binary
127 | *.gif binary
128 | *.ico binary
129 | *.jng binary
130 | *.jp2 binary
131 | *.jpg binary
132 | *.jpeg binary
133 | *.jpx binary
134 | *.jxr binary
135 | *.pdf binary
136 | *.png binary
137 | *.psb binary
138 | *.psd binary
139 | *.svg text
140 | *.svgz binary
141 | *.tif binary
142 | *.tiff binary
143 | *.wbmp binary
144 | *.webp binary
145 |
146 | ## AUDIO
147 | *.kar binary
148 | *.m4a binary
149 | *.mid binary
150 | *.midi binary
151 | *.mp3 binary
152 | *.ogg binary
153 | *.ra binary
154 |
155 | ## VIDEO
156 | *.3gpp binary
157 | *.3gp binary
158 | *.as binary
159 | *.asf binary
160 | *.asx binary
161 | *.fla binary
162 | *.flv binary
163 | *.m4v binary
164 | *.mng binary
165 | *.mov binary
166 | *.mp4 binary
167 | *.mpeg binary
168 | *.mpg binary
169 | *.ogv binary
170 | *.swc binary
171 | *.swf binary
172 | *.webm binary
173 |
174 | ## ARCHIVES
175 | *.7z binary
176 | *.gz binary
177 | *.jar binary
178 | *.rar binary
179 | *.tar binary
180 | *.zip binary
181 |
182 | ## FONTS
183 | *.ttf binary
184 | *.eot binary
185 | *.otf binary
186 | *.woff binary
187 | *.woff2 binary
188 |
189 | ## EXECUTABLES
190 | *.exe binary
191 | *.pyc binary
192 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /scripts
3 | coverage
4 | yarn-error.log
5 | .rollupcache
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "AureliaEffect.aurelia",
4 | "behzad88.Aurelia",
5 | "christian-kohler.path-intellisense",
6 | "EditorConfig.EditorConfig",
7 | "esbenp.prettier-vscode",
8 | "joelday.docthis",
9 | "steoates.autoimport"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "attach",
10 | "name": "Attach Karma Chrome",
11 | "address": "localhost",
12 | "port": 9333,
13 | "sourceMaps": true,
14 | "webRoot": "${workspaceRoot}",
15 | "sourceMapPathOverrides": {
16 | "webpack:///*": "${webRoot}/*"
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "typescript.tsdk": "./node_modules/typescript/lib",
4 | "tslint.nodePath": "./node_modules/tslint/bin/tslint",
5 | "html.suggest.angular1": false,
6 | "html.suggest.ionic": false,
7 | "html.format.endWithNewline": true,
8 | "tslint.exclude": [
9 | "**/node_modules/**",
10 | "**/dist/**"
11 | ],
12 | "emmet.triggerExpansionOnTab": true,
13 | "search.exclude": {
14 | "**/node_modules": true,
15 | "**/dist": true,
16 | "**/doc": true,
17 | "**/.vscode": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | ## [0.3.1](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.3.0...v0.3.1) (2018-05-07)
7 |
8 |
9 | ### Bug Fixes
10 |
11 | * **build:** back to tsc for multi-file output ([26c5638](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/26c5638))
12 |
13 |
14 |
15 |
16 | # [0.3.0](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.5...v0.3.0) (2018-05-07)
17 |
18 |
19 | ### Features
20 |
21 | * **dynamic-html:** dispatch compiled event when compilation is done ([2518c8c](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/2518c8c))
22 |
23 |
24 |
25 |
26 | ## [0.2.5](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.4...v0.2.5) (2018-05-07)
27 |
28 |
29 |
30 |
31 | ## [0.2.4](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.3...v0.2.4) (2018-05-07)
32 |
33 |
34 |
35 |
36 | ## [0.2.3](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.2...v0.2.3) (2018-05-06)
37 |
38 |
39 |
40 |
41 | ## [0.2.2](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.1...v0.2.2) (2018-05-06)
42 |
43 |
44 |
45 |
46 | ## [0.2.1](https://github.com/aurelia-contrib/aurelia-dynamic-html/compare/v0.2.0...v0.2.1) (2018-05-06)
47 |
48 |
49 | ### Bug Fixes
50 |
51 | * split out interfaces in separate file and export everything via index ([dd80902](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/dd80902))
52 |
53 |
54 |
55 |
56 | # 0.2.0 (2018-05-06)
57 |
58 |
59 | ### Bug Fixes
60 |
61 | * **package-scripts:** checkout master after setting up gh-pages ([63f596a](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/63f596a))
62 | * **package-scripts:** fix ghpages-setup script ([6b32d97](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/6b32d97))
63 |
64 |
65 | ### Features
66 |
67 | * **demo:** add something to render for demo app ([f4df84c](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/f4df84c))
68 | * **dynamic-html:** initial implementation ([833d8b2](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/833d8b2))
69 | * **rollup:** switch to rollup (but retain tsc as an alternative build setup) ([b7923f9](https://github.com/aurelia-contrib/aurelia-dynamic-html/commit/b7923f9))
70 |
71 |
72 |
73 | # Change Log
74 |
75 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
76 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Fred Kleuver.
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # aurelia-dynamic-html
2 |
3 | Aurelia custom element that takes (server- or client side) generated html and compiles it into a fully functional Aurelia View.
4 |
5 | View [LIVE DEMO](https://aurelia-contrib.github.io/aurelia-dynamic-html/)
6 |
7 | ## Installation
8 | Install the npm dependency via
9 |
10 | ```bash
11 | npm i aurelia-dynamic-html
12 | ```
13 |
14 | or via the Aurelia CLI
15 |
16 | ```bash
17 | au install aurelia-dynamic-html
18 | ```
19 |
20 | ## Aurelia-CLI
21 |
22 | For Aurelia-CLI projects based on RequireJS or SystemJS, the following will install and declare the dependency in your aurelia.json:
23 |
24 | ```bash
25 | au install aurelia-dynamic-html
26 | ```
27 |
28 | or if you have already installed and only need to add the dependency to aurelia.json:
29 |
30 | ```bash
31 | au import aurelia-dynamic-html
32 | ```
33 |
34 | alternatively you can manually add the dependency to your vendor.bundles:
35 |
36 | ```json
37 | "dependencies": [
38 | {
39 | "name": "aurelia-dynamic-html",
40 | "path": "../node_modules/aurelia-dynamic-html/dist/amd",
41 | "main": "aurelia-dynamic-html"
42 | }
43 | ]
44 | ```
45 |
46 | ## Configuration
47 |
48 | ```typescript
49 | import { Aurelia } from "aurelia-framework";
50 |
51 | export function configure(au: Aurelia) {
52 | au.use.standardConfiguration();
53 |
54 | au.use.plugin("aurelia-dynamic-html"); // don't forget PLATFORM.moduleName if you're using webpack
55 |
56 | au.start().then(() => au.setRoot());
57 | }
58 | ```
59 |
60 | ## Usage
61 |
62 | ### Inline html, implicit $this context
63 |
64 | * Input
65 |
66 | ```ts
67 | export class App {
68 | message = "Hello world!";
69 | }
70 | ```
71 |
72 | ```html
73 |
74 |
75 |
76 | ```
77 |
78 | * Output
79 |
80 | ```html
81 |
Hello world!
82 | ```
83 |
84 | Note: the variants below also apply to inline html, but are omitted for brevity
85 |
86 | ### Html variable, implicit $this context
87 |
88 | * Input
89 |
90 | ```ts
91 | export class App {
92 | message = "Hello world!";
93 | html = "${message}";
94 | }
95 | ```
96 |
97 | ```html
98 |
99 |
100 |
101 | ```
102 |
103 | * Output
104 |
105 | ```html
106 | Hello world!
107 | ```
108 |
109 | ### Html variable, explicit $this context
110 |
111 | * Input
112 |
113 | ```ts
114 | export class App {
115 | message = "Hello world!";
116 | html = "${message}";
117 | }
118 | ```
119 |
120 | ```html
121 |
122 |
123 |
124 | ```
125 |
126 | * Output
127 |
128 | ```html
129 | Hello world!
130 | ```
131 |
132 |
133 | ### Html variable, context variable
134 |
135 | * Input
136 |
137 | ```ts
138 | export class App {
139 | context = { message: "Hello world!" };
140 | html = "${message}";
141 | }
142 | ```
143 |
144 | ```html
145 |
146 |
147 |
148 | ```
149 |
150 | * Output
151 |
152 | ```html
153 | Hello world!
154 | ```
155 |
156 |
157 | ### Html variable, context variable (complex / nested)
158 |
159 | The html and context can come from any source, be of arbitrary complexity, and work for any Aurelia feature.
160 |
161 | * Input
162 |
163 | ```ts
164 | export class App {
165 | context = { message: "Hello world!", html: "${message}" };
166 | html = "";
167 | }
168 | ```
169 |
170 | ```html
171 |
172 |
173 |
174 | ```
175 |
176 | * Output
177 |
178 | ```html
179 | Hello world!
180 | ```
181 |
182 | ### Erroneous html, do not render errors
183 |
184 | * Input
185 |
186 | ```ts
187 | export class App {
188 | context = { message: "Hello world!" };
189 | html = "${message"; // missing closing brace
190 | }
191 | ```
192 |
193 | ```html
194 |
195 |
196 |
197 | ```
198 |
199 | * Output
200 |
201 | ```html
202 |
203 | ```
204 |
205 | ### Erroneous html, render errors
206 |
207 | * Input
208 |
209 | ```ts
210 | export class App {
211 | context = { message: "Hello world!" };
212 | html = "${message"; // missing closing brace
213 | }
214 | ```
215 |
216 | ```html
217 |
218 |
219 |
220 | ```
221 |
222 | * Output
223 |
224 | ```html
225 | Parser Error: Missing expected token } (...)
226 | ```
227 |
228 | ## Building The Code
229 |
230 |
231 | 1. From the project folder, execute the following command:
232 |
233 | ```
234 | yarn/npm install
235 | ```
236 | 2. To build the code:
237 |
238 | ```
239 | npm run build
240 | ```
241 |
242 | ## Running The Tests
243 |
244 | 1. To run the tests
245 |
246 | ```
247 | npm run test
248 | ```
249 |
250 | 2. To continuously run the tests
251 |
252 | ```
253 | npm run develop
254 | ```
255 |
256 |
257 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-amd.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/amd",
5 | "module": "amd"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-commonjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/commonjs",
5 | "module": "commonjs"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-es2015.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/es2015"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-es2017.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/es2017",
5 | "target": "es2017"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-native-modules.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/native-modules"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/configs/tsconfig-build-system.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-build.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/system",
5 | "module": "system"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/configs/tsconfig-build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "noUnusedLocals": true,
6 | "noUnusedParameters": true,
7 | "strict": true,
8 | "stripInternal": true
9 | },
10 | "include": ["../src"]
11 | }
12 |
--------------------------------------------------------------------------------
/configs/tsconfig-demo.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["../src", "../demo"]
4 | }
5 |
--------------------------------------------------------------------------------
/configs/tsconfig-projectroot.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noUnusedLocals": true,
5 | "noUnusedParameters": true,
6 | "strict": true,
7 | "module": "commonjs",
8 | "target": "es5"
9 | },
10 | "include": ["../src", "../test"]
11 | }
12 |
--------------------------------------------------------------------------------
/configs/tsconfig-test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["../src", "../test"],
4 | "compilerOptions": {
5 | "module": "esnext",
6 | "target": "es5",
7 | "sourceMap": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/configs/tsconfig-webpack.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "target": "es5"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/configs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | //"allowJs": false,
5 | "allowSyntheticDefaultImports": true, //default: module === "system" or --esModuleInterop
6 | //"allowUnreachableCode": false,
7 | //"allowUnusedLabels": false,
8 | //"alwaysStrict": false,
9 | //"baseUrl": "",
10 | //"charset": "utf8",
11 | //"checkJs": false,
12 | //"declaration": false,
13 | //"declarationDir": "",
14 | //"diagnostics": false,
15 | //"disableSizeLimit": false,
16 | "downlevelIteration": true, //default: false
17 | //"emitBOM": false,
18 | "emitDecoratorMetadata": true, //default: false
19 | "esModuleInterop": true, //default: false
20 | "experimentalDecorators": true, //default: false
21 | "forceConsistentCasingInFileNames": true, //default: false
22 | //"importHelpers": false,
23 | //"inlineSourceMap": false,
24 | //"inlineSources": false,
25 | //"jsxFactory": "React.createElement",
26 | "lib": ["dom", "es2017"], //default: --target ES5: DOM,ES5,ScriptHost; --target ES6: DOM,ES6,DOM.Iterable,ScriptHost
27 | //"listEmittedFiles": false,
28 | //"listFiles": false,
29 | //"mapRoot": "",
30 | //"maxNodeModuleJsDepth": 0,
31 | "module": "es2015", //default: target === "ES3" or "ES5" ? "CommonJS" : "ES6"
32 | "moduleResolution": "node", //default: module === "AMD" or "System" or "ES6" ? "Classic" : "Node",
33 | "newLine": "lf", //default: (platform specific)
34 | //"noEmit": false,
35 | //"noEmitHelpers": false,
36 | //"noEmitOnError": false,
37 | //"noFallthroughCasesInSwitch": false,
38 | //"noImplicitAny": false,
39 | "noImplicitReturns": true, //default: false
40 | //"noImplicitThis": false,
41 | //"noImplicitUseStrict": false,
42 | //"noLib": false,
43 | //"noResolve": false,
44 | //"noStrictGenericChecks": false,
45 | //"noUnusedLocals": false,
46 | //"noUnusedParameters": false,
47 | //"outDir": "",
48 | //"outFile": "",
49 | //"paths": {},
50 | "preserveConstEnums": true, //default: false
51 | //"preserveSymlinks": false,
52 | //"pretty": false,
53 | //"removeComments": false,
54 | //"rootDir": "",
55 | //"rootDirs": [],
56 | "skipLibCheck": true, //default: false
57 | //"sourceMap": false,
58 | //"sourceRoot": "",
59 | //"strict": false,
60 | //"strictFunctionTypes": false,
61 | //"strictPropertyInitialization": false,
62 | //"strictNullChecks": false,
63 | //"stripInternal": false,
64 | //"suppressExcessPropertyErrors": false,
65 | "target": "es2015", //default: es3
66 | //"traceResolution": false,
67 | //"typeRoots": [],
68 | //"watch": false
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/demo/app.css:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | [aurelia-app] {
4 | width: 100vw;
5 | height: 100vh;
6 | margin: 0;
7 | border: 0;
8 | padding: 0;
9 | overflow-y: hidden;
10 | overflow-x: hidden;
11 | }
12 |
13 | [aurelia-app] {
14 | display: grid;
15 | grid-template-columns: 1fr 1fr;
16 | grid-template-rows: 1fr 1fr;
17 | grid-template-areas:
18 | 'tleft right'
19 | 'bleft right';
20 | }
21 |
22 | .area-tleft {
23 | grid-area: tleft;
24 | }
25 |
26 | .area-bleft {
27 | grid-area: bleft;
28 | }
29 |
30 | .area-right {
31 | grid-area: right;
32 | padding: 12px;
33 | overflow-y: auto;
34 | }
35 |
36 | .content-area {
37 | border: 1px solid grey;
38 | }
39 |
--------------------------------------------------------------------------------
/demo/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demo/app.ts:
--------------------------------------------------------------------------------
1 | import { observable } from "aurelia-binding";
2 |
3 | export class App {
4 | @observable() public jsRaw: string;
5 |
6 | public html: string;
7 | public context: any;
8 |
9 | constructor() {
10 | this.jsRaw = initialJs;
11 | this.html = initialHTML;
12 | }
13 |
14 | public jsRawChanged(newValue: string, oldValue: string): void {
15 | const functionBody = `return ${newValue}`;
16 | // tslint:disable-next-line:no-function-constructor-with-string-args
17 | const ctorFactory = new Function(functionBody);
18 | const ctor = ctorFactory();
19 | this.context = new ctor();
20 | }
21 | }
22 |
23 | // tslint:disable:no-multiline-string
24 | const initialJs = `class Foo {
25 | constructor() {
26 | this.firstName = 'John';
27 | this.lastName = 'Doe';
28 | }
29 |
30 | submit() {
31 | alert('You submitted "' + this.firstName + ' ' + this.lastName + '"');
32 | }
33 | }`;
34 |
35 | const initialHTML = ``;
50 |
--------------------------------------------------------------------------------
/demo/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%- htmlWebpackPlugin.options.metadata.title %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <% if (htmlWebpackPlugin.options.metadata.server) { %>
26 |
27 |
28 | <% } %>
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo/main.ts:
--------------------------------------------------------------------------------
1 | import { Aurelia } from "aurelia-framework";
2 | import { PLATFORM } from "aurelia-pal";
3 |
4 | export async function configure(au: Aurelia): Promise {
5 | au.use.standardConfiguration();
6 |
7 | au.use.feature(PLATFORM.moduleName("resources/index"));
8 |
9 | await au.start();
10 |
11 | const host = document.querySelector("[aurelia-app]");
12 |
13 | await au.setRoot(PLATFORM.moduleName("app"), host);
14 | }
15 |
--------------------------------------------------------------------------------
/demo/resources/elements/dynamic-html.ts:
--------------------------------------------------------------------------------
1 | import { bindingMode, createOverrideContext, OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { getLogger } from "aurelia-logging";
4 | import { TaskQueue } from "aurelia-task-queue";
5 | import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
6 | import { IBindingContext, IOverrideContext } from "../../../src/interfaces";
7 |
8 |
9 | const logger = getLogger("dynamic-html");
10 |
11 | @customElement("dynamic-html")
12 | @inlineView("")
13 | export class DynamicHtml {
14 | @bindable({ defaultBindingMode: bindingMode.toView })
15 | public html: string | null;
16 |
17 | @bindable({ defaultBindingMode: bindingMode.toView })
18 | public context: IBindingContext | null;
19 |
20 | @bindable({ defaultBindingMode: bindingMode.toView })
21 | public renderErrors: boolean;
22 |
23 | public slot: ViewSlot | null;
24 | public bindingContext: IBindingContext | null;
25 | public overrideContext: IOverrideContext | OverrideContext | null;
26 | public isAttached: boolean;
27 | public isBound: boolean;
28 | public isCompiled: boolean;
29 | public isCleanedUp: boolean;
30 | public isInitialized: boolean;
31 |
32 | public el: HTMLElement;
33 | protected tq: TaskQueue;
34 | protected container: Container;
35 | protected viewCompiler: ViewCompiler;
36 |
37 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler) {
38 | this.el = el as HTMLElement;
39 | this.tq = tq;
40 | this.container = container;
41 | this.viewCompiler = viewCompiler;
42 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
43 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
44 | this.isCleanedUp = true;
45 | }
46 |
47 | public bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void {
48 | this.isBound = true;
49 |
50 | this.bindingContext = this.context || bindingContext.context || bindingContext;
51 | this.overrideContext = createOverrideContext(bindingContext, overrideContext);
52 |
53 | this.htmlChanged(this.html);
54 | }
55 |
56 | public unbind(): void {
57 | this.isBound = false;
58 |
59 | this.bindingContext = null;
60 | this.overrideContext = null;
61 | }
62 |
63 | public attached(): void {
64 | this.isAttached = true;
65 | this.isInitialized = true;
66 |
67 | this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
68 |
69 | this.tq.queueMicroTask(() => {
70 | this.tryCompile();
71 | });
72 | }
73 |
74 | public detached(): void {
75 | this.isAttached = false;
76 |
77 | if (this.isCompiled) {
78 | this.cleanUp();
79 | }
80 | this.slot = null;
81 | }
82 |
83 | protected htmlChanged(newValue: string | null, __?: string): void {
84 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
85 | this.cleanUp();
86 | } else if (this.isBound || this.isInitialized) {
87 | this.tq.queueMicroTask(() => {
88 | this.tryCompile();
89 | });
90 | }
91 | }
92 |
93 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void {
94 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
95 | this.bindingContext = this.overrideContext.bindingContext;
96 | } else {
97 | this.bindingContext = newValue;
98 | }
99 | if (this.isBound || this.isInitialized) {
100 | this.tq.queueMicroTask(() => {
101 | this.tryCompile();
102 | });
103 | }
104 | }
105 |
106 | protected tryCompile(): void {
107 | if (this.isAttached) {
108 | if (!this.isCleanedUp) {
109 | this.cleanUp();
110 | }
111 | try {
112 | this.tq.queueMicroTask(() => {
113 | this.compile();
114 | });
115 | } catch (e) {
116 | logger.warn(e.message);
117 | if (this.renderErrors) {
118 | this.tq.queueMicroTask(() => {
119 | this.compile(`${e.message}`);
120 | });
121 | }
122 | }
123 | }
124 | }
125 |
126 | protected cleanUp(): void {
127 | this.isCompiled = false;
128 |
129 | logger.debug("Cleaning up");
130 |
131 | const slot = this.slot as ViewSlot;
132 | try {
133 | slot.detached();
134 | } catch (e) {
135 | logger.error(e.message);
136 | }
137 | try {
138 | slot.unbind();
139 | } catch (e) {
140 | logger.error(e.message);
141 | }
142 | try {
143 | slot.removeAll();
144 | } catch (e) {
145 | logger.error(e.message);
146 | }
147 |
148 | this.isCleanedUp = true;
149 | }
150 |
151 | protected compile(message?: string): void {
152 | if (!this.isCleanedUp) {
153 | this.cleanUp();
154 | }
155 | if ((this.html === null || this.html === undefined) && message === undefined) {
156 | logger.debug("Skipping compilation because no html value is set");
157 |
158 | return;
159 | }
160 | this.isCleanedUp = false;
161 |
162 | const template = `${message || this.html}`;
163 |
164 | logger.debug("Compiling", template, this.bindingContext);
165 |
166 | const viewResources = this.container.get(ViewResources) as ViewResources;
167 | const childContainer = this.container.createChild();
168 | const factory = this.viewCompiler.compile(template, viewResources);
169 | const view = factory.create(childContainer);
170 | const slot = this.slot as ViewSlot;
171 |
172 | slot.add(view);
173 | slot.bind(this.bindingContext as IBindingContext, this.overrideContext as IOverrideContext);
174 | slot.attached();
175 |
176 | this.dispatchCompiledEvent();
177 |
178 | this.isCompiled = true;
179 | }
180 |
181 | protected dispatchCompiledEvent(): void {
182 | const event = new CustomEvent("compiled", {
183 | cancelable: true,
184 | bubbles: true,
185 | detail: this
186 | });
187 | this.el.dispatchEvent(event);
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/demo/resources/elements/monaco-editor.css:
--------------------------------------------------------------------------------
1 | .monaco-editor-host {
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | .ui.dimmer {
7 | position: relative !important;
8 | }
9 |
--------------------------------------------------------------------------------
/demo/resources/elements/monaco-editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/resources/elements/monaco-editor.ts:
--------------------------------------------------------------------------------
1 | import { bindingMode } from "aurelia-binding";
2 | import { bindable, customElement } from "aurelia-framework";
3 | import { PLATFORM } from "aurelia-pal";
4 | import { TaskQueue } from "aurelia-task-queue";
5 | import * as monaco from "monaco-editor";
6 |
7 | @customElement("monaco-editor")
8 | export class MonacoEditor {
9 | @bindable({ defaultBindingMode: bindingMode.toView })
10 | public options: monaco.editor.IEditorConstructionOptions;
11 |
12 | @bindable({ defaultBindingMode: bindingMode.fromView })
13 | public editor: monaco.editor.IStandaloneCodeEditor;
14 |
15 | @bindable({ defaultBindingMode: bindingMode.twoWay })
16 | public code: string;
17 |
18 | public editorHost: HTMLElement;
19 | private tq: TaskQueue;
20 |
21 | private isLoaded: boolean;
22 | private needsUpdateEditorValue: boolean;
23 | private isUpdatingCode: boolean;
24 | private isUpdatingEditorValue: boolean;
25 |
26 | constructor(tq: TaskQueue) {
27 | this.tq = tq;
28 | this.isLoaded = false;
29 | this.isUpdatingCode = false;
30 | this.isUpdatingEditorValue = false;
31 | }
32 |
33 | public attached(): void {
34 | PLATFORM.addEventListener("resize", this.handleResize);
35 |
36 | this.editor = monaco.editor.create(this.editorHost, this.options);
37 | this.isLoaded = true;
38 | if (this.needsUpdateEditorValue) {
39 | this.needsUpdateEditorValue = false;
40 | this.updateEditorValue();
41 | }
42 | this.editor.onDidChangeModelContent((): void => {
43 | this.updateCode();
44 | });
45 | }
46 |
47 | public detached(): void {
48 | PLATFORM.removeEventListener("resize", this.handleResize);
49 |
50 | this.editor.dispose();
51 | this.isLoaded = false;
52 | }
53 |
54 | protected codeChanged(): void {
55 | this.updateEditorValue();
56 | }
57 |
58 | private updateCode(): void {
59 | if (!this.isUpdatingEditorValue) {
60 | this.isUpdatingCode = true;
61 | this.code = this.editor.getValue();
62 | this.tq.queueMicroTask(() => {
63 | this.isUpdatingCode = false;
64 | });
65 | }
66 | }
67 |
68 | private updateEditorValue(): void {
69 | if (!this.isUpdatingCode && /String/.test(Object.prototype.toString.call(this.code))) {
70 | if (this.isLoaded) {
71 | this.isUpdatingEditorValue = true;
72 | this.editor.setValue(this.code);
73 | this.tq.queueTask(() => {
74 | this.isUpdatingEditorValue = false;
75 | });
76 | } else {
77 | this.needsUpdateEditorValue = true;
78 | }
79 | }
80 | }
81 |
82 | private handleResize = (): void => {
83 | if (!(this.editor === null || this.editor === undefined)) {
84 | this.editor.layout();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/demo/resources/index.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable:no-implicit-dependencies
2 | import { FrameworkConfiguration } from "aurelia-framework";
3 | import { PLATFORM } from "aurelia-pal";
4 |
5 | export function configure(config: FrameworkConfiguration): void {
6 | config.globalResources([
7 | PLATFORM.moduleName("resources/elements/monaco-editor"),
8 | PLATFORM.moduleName("resources/elements/dynamic-html")
9 | ]);
10 | }
11 |
--------------------------------------------------------------------------------
/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../configs/tsconfig-demo.json"
3 | }
4 |
--------------------------------------------------------------------------------
/dist/amd/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/amd/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | define(["require", "exports", "aurelia-pal", "./dynamic-html"], function (require, exports, aurelia_pal_1, dynamic_html_1) {
2 | "use strict";
3 | function __export(m) {
4 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
5 | }
6 | Object.defineProperty(exports, "__esModule", { value: true });
7 | function configure(config) {
8 | config.globalResources(aurelia_pal_1.PLATFORM.moduleName("./dynamic-html"));
9 | }
10 | exports.configure = configure;
11 | __export(dynamic_html_1);
12 | });
13 |
--------------------------------------------------------------------------------
/dist/amd/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/amd/dynamic-html.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | var __metadata = (this && this.__metadata) || function (k, v) {
8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9 | };
10 | define(["require", "exports", "aurelia-binding", "aurelia-dependency-injection", "aurelia-logging", "aurelia-task-queue", "aurelia-templating"], function (require, exports, aurelia_binding_1, aurelia_dependency_injection_1, aurelia_logging_1, aurelia_task_queue_1, aurelia_templating_1) {
11 | "use strict";
12 | Object.defineProperty(exports, "__esModule", { value: true });
13 | const logger = aurelia_logging_1.getLogger("dynamic-html");
14 | let DynamicHtml = class DynamicHtml {
15 | constructor(el, tq, container, viewCompiler) {
16 | this.el = el;
17 | this.tq = tq;
18 | this.container = container;
19 | this.viewCompiler = viewCompiler;
20 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
21 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
22 | this.isCleanedUp = true;
23 | }
24 | bind(bindingContext, overrideContext) {
25 | this.isBound = true;
26 | this.bindingContext = this.context || bindingContext.context || bindingContext;
27 | this.overrideContext = aurelia_binding_1.createOverrideContext(bindingContext, overrideContext);
28 | this.htmlChanged(this.html);
29 | }
30 | unbind() {
31 | this.isBound = false;
32 | this.bindingContext = null;
33 | this.overrideContext = null;
34 | }
35 | attached() {
36 | this.isAttached = true;
37 | this.isInitialized = true;
38 | this.slot = new aurelia_templating_1.ViewSlot(this.el.firstElementChild || this.el, true);
39 | this.tq.queueMicroTask(() => {
40 | this.tryCompile();
41 | });
42 | }
43 | detached() {
44 | this.isAttached = false;
45 | if (this.isCompiled) {
46 | this.cleanUp();
47 | }
48 | this.slot = null;
49 | }
50 | htmlChanged(newValue, __) {
51 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
52 | this.cleanUp();
53 | }
54 | else if (this.isBound || this.isInitialized) {
55 | this.tq.queueMicroTask(() => {
56 | this.tryCompile();
57 | });
58 | }
59 | }
60 | contextChanged(newValue, _) {
61 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
62 | this.bindingContext = this.overrideContext.bindingContext;
63 | }
64 | else {
65 | this.bindingContext = newValue;
66 | }
67 | if (this.isBound || this.isInitialized) {
68 | this.tq.queueMicroTask(() => {
69 | this.tryCompile();
70 | });
71 | }
72 | }
73 | tryCompile() {
74 | if (this.isAttached) {
75 | if (!this.isCleanedUp) {
76 | this.cleanUp();
77 | }
78 | try {
79 | this.tq.queueMicroTask(() => {
80 | this.compile();
81 | });
82 | }
83 | catch (e) {
84 | logger.warn(e.message);
85 | if (this.renderErrors) {
86 | this.tq.queueMicroTask(() => {
87 | this.compile(`${e.message}`);
88 | });
89 | }
90 | }
91 | }
92 | }
93 | cleanUp() {
94 | this.isCompiled = false;
95 | logger.debug("Cleaning up");
96 | const slot = this.slot;
97 | try {
98 | slot.detached();
99 | }
100 | catch (e) {
101 | logger.error(e.message);
102 | }
103 | try {
104 | slot.unbind();
105 | }
106 | catch (e) {
107 | logger.error(e.message);
108 | }
109 | try {
110 | slot.removeAll();
111 | }
112 | catch (e) {
113 | logger.error(e.message);
114 | }
115 | this.isCleanedUp = true;
116 | }
117 | compile(message) {
118 | if (!this.isCleanedUp) {
119 | this.cleanUp();
120 | }
121 | if ((this.html === null || this.html === undefined) && message === undefined) {
122 | logger.debug("Skipping compilation because no html value is set");
123 | return;
124 | }
125 | this.isCleanedUp = false;
126 | const template = `${message || this.html}`;
127 | logger.debug("Compiling", template, this.bindingContext);
128 | const viewResources = this.container.get(aurelia_templating_1.ViewResources);
129 | const childContainer = this.container.createChild();
130 | const factory = this.viewCompiler.compile(template, viewResources);
131 | const view = factory.create(childContainer);
132 | const slot = this.slot;
133 | slot.add(view);
134 | slot.bind(this.bindingContext, this.overrideContext);
135 | slot.attached();
136 | this.dispatchCompiledEvent();
137 | this.isCompiled = true;
138 | }
139 | dispatchCompiledEvent() {
140 | const event = new CustomEvent("compiled", {
141 | cancelable: true,
142 | bubbles: true,
143 | detail: this
144 | });
145 | this.el.dispatchEvent(event);
146 | }
147 | };
148 | __decorate([
149 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
150 | __metadata("design:type", Object)
151 | ], DynamicHtml.prototype, "html", void 0);
152 | __decorate([
153 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
154 | __metadata("design:type", Object)
155 | ], DynamicHtml.prototype, "context", void 0);
156 | __decorate([
157 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
158 | __metadata("design:type", Boolean)
159 | ], DynamicHtml.prototype, "renderErrors", void 0);
160 | DynamicHtml = __decorate([
161 | aurelia_templating_1.customElement("dynamic-html"),
162 | aurelia_templating_1.inlineView(""),
163 | __metadata("design:paramtypes", [Element, aurelia_task_queue_1.TaskQueue, aurelia_dependency_injection_1.Container, aurelia_templating_1.ViewCompiler])
164 | ], DynamicHtml);
165 | exports.DynamicHtml = DynamicHtml;
166 | });
167 |
--------------------------------------------------------------------------------
/dist/amd/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/amd/interfaces.js:
--------------------------------------------------------------------------------
1 | define(["require", "exports"], function (require, exports) {
2 | "use strict";
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | });
5 |
--------------------------------------------------------------------------------
/dist/commonjs/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/commonjs/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | function __export(m) {
3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
4 | }
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const aurelia_pal_1 = require("aurelia-pal");
7 | function configure(config) {
8 | config.globalResources(aurelia_pal_1.PLATFORM.moduleName("./dynamic-html"));
9 | }
10 | exports.configure = configure;
11 | __export(require("./dynamic-html"));
12 |
--------------------------------------------------------------------------------
/dist/commonjs/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/commonjs/dynamic-html.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 | return c > 3 && r && Object.defineProperty(target, key, r), r;
7 | };
8 | var __metadata = (this && this.__metadata) || function (k, v) {
9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10 | };
11 | Object.defineProperty(exports, "__esModule", { value: true });
12 | const aurelia_binding_1 = require("aurelia-binding");
13 | const aurelia_dependency_injection_1 = require("aurelia-dependency-injection");
14 | const aurelia_logging_1 = require("aurelia-logging");
15 | const aurelia_task_queue_1 = require("aurelia-task-queue");
16 | const aurelia_templating_1 = require("aurelia-templating");
17 | const logger = aurelia_logging_1.getLogger("dynamic-html");
18 | let DynamicHtml = class DynamicHtml {
19 | constructor(el, tq, container, viewCompiler) {
20 | this.el = el;
21 | this.tq = tq;
22 | this.container = container;
23 | this.viewCompiler = viewCompiler;
24 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
25 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
26 | this.isCleanedUp = true;
27 | }
28 | bind(bindingContext, overrideContext) {
29 | this.isBound = true;
30 | this.bindingContext = this.context || bindingContext.context || bindingContext;
31 | this.overrideContext = aurelia_binding_1.createOverrideContext(bindingContext, overrideContext);
32 | this.htmlChanged(this.html);
33 | }
34 | unbind() {
35 | this.isBound = false;
36 | this.bindingContext = null;
37 | this.overrideContext = null;
38 | }
39 | attached() {
40 | this.isAttached = true;
41 | this.isInitialized = true;
42 | this.slot = new aurelia_templating_1.ViewSlot(this.el.firstElementChild || this.el, true);
43 | this.tq.queueMicroTask(() => {
44 | this.tryCompile();
45 | });
46 | }
47 | detached() {
48 | this.isAttached = false;
49 | if (this.isCompiled) {
50 | this.cleanUp();
51 | }
52 | this.slot = null;
53 | }
54 | htmlChanged(newValue, __) {
55 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
56 | this.cleanUp();
57 | }
58 | else if (this.isBound || this.isInitialized) {
59 | this.tq.queueMicroTask(() => {
60 | this.tryCompile();
61 | });
62 | }
63 | }
64 | contextChanged(newValue, _) {
65 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
66 | this.bindingContext = this.overrideContext.bindingContext;
67 | }
68 | else {
69 | this.bindingContext = newValue;
70 | }
71 | if (this.isBound || this.isInitialized) {
72 | this.tq.queueMicroTask(() => {
73 | this.tryCompile();
74 | });
75 | }
76 | }
77 | tryCompile() {
78 | if (this.isAttached) {
79 | if (!this.isCleanedUp) {
80 | this.cleanUp();
81 | }
82 | try {
83 | this.tq.queueMicroTask(() => {
84 | this.compile();
85 | });
86 | }
87 | catch (e) {
88 | logger.warn(e.message);
89 | if (this.renderErrors) {
90 | this.tq.queueMicroTask(() => {
91 | this.compile(`${e.message}`);
92 | });
93 | }
94 | }
95 | }
96 | }
97 | cleanUp() {
98 | this.isCompiled = false;
99 | logger.debug("Cleaning up");
100 | const slot = this.slot;
101 | try {
102 | slot.detached();
103 | }
104 | catch (e) {
105 | logger.error(e.message);
106 | }
107 | try {
108 | slot.unbind();
109 | }
110 | catch (e) {
111 | logger.error(e.message);
112 | }
113 | try {
114 | slot.removeAll();
115 | }
116 | catch (e) {
117 | logger.error(e.message);
118 | }
119 | this.isCleanedUp = true;
120 | }
121 | compile(message) {
122 | if (!this.isCleanedUp) {
123 | this.cleanUp();
124 | }
125 | if ((this.html === null || this.html === undefined) && message === undefined) {
126 | logger.debug("Skipping compilation because no html value is set");
127 | return;
128 | }
129 | this.isCleanedUp = false;
130 | const template = `${message || this.html}`;
131 | logger.debug("Compiling", template, this.bindingContext);
132 | const viewResources = this.container.get(aurelia_templating_1.ViewResources);
133 | const childContainer = this.container.createChild();
134 | const factory = this.viewCompiler.compile(template, viewResources);
135 | const view = factory.create(childContainer);
136 | const slot = this.slot;
137 | slot.add(view);
138 | slot.bind(this.bindingContext, this.overrideContext);
139 | slot.attached();
140 | this.dispatchCompiledEvent();
141 | this.isCompiled = true;
142 | }
143 | dispatchCompiledEvent() {
144 | const event = new CustomEvent("compiled", {
145 | cancelable: true,
146 | bubbles: true,
147 | detail: this
148 | });
149 | this.el.dispatchEvent(event);
150 | }
151 | };
152 | __decorate([
153 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
154 | __metadata("design:type", Object)
155 | ], DynamicHtml.prototype, "html", void 0);
156 | __decorate([
157 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
158 | __metadata("design:type", Object)
159 | ], DynamicHtml.prototype, "context", void 0);
160 | __decorate([
161 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
162 | __metadata("design:type", Boolean)
163 | ], DynamicHtml.prototype, "renderErrors", void 0);
164 | DynamicHtml = __decorate([
165 | aurelia_templating_1.customElement("dynamic-html"),
166 | aurelia_templating_1.inlineView(""),
167 | __metadata("design:paramtypes", [Element, aurelia_task_queue_1.TaskQueue, aurelia_dependency_injection_1.Container, aurelia_templating_1.ViewCompiler])
168 | ], DynamicHtml);
169 | exports.DynamicHtml = DynamicHtml;
170 |
--------------------------------------------------------------------------------
/dist/commonjs/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/commonjs/interfaces.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 |
--------------------------------------------------------------------------------
/dist/es2015/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/es2015/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | import { PLATFORM } from "aurelia-pal";
2 | export function configure(config) {
3 | config.globalResources(PLATFORM.moduleName("./dynamic-html"));
4 | }
5 | export * from "./dynamic-html";
6 |
--------------------------------------------------------------------------------
/dist/es2015/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/es2015/dynamic-html.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | var __metadata = (this && this.__metadata) || function (k, v) {
8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9 | };
10 | import { bindingMode, createOverrideContext } from "aurelia-binding";
11 | import { Container } from "aurelia-dependency-injection";
12 | import { getLogger } from "aurelia-logging";
13 | import { TaskQueue } from "aurelia-task-queue";
14 | import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
15 | const logger = getLogger("dynamic-html");
16 | let DynamicHtml = class DynamicHtml {
17 | constructor(el, tq, container, viewCompiler) {
18 | this.el = el;
19 | this.tq = tq;
20 | this.container = container;
21 | this.viewCompiler = viewCompiler;
22 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
23 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
24 | this.isCleanedUp = true;
25 | }
26 | bind(bindingContext, overrideContext) {
27 | this.isBound = true;
28 | this.bindingContext = this.context || bindingContext.context || bindingContext;
29 | this.overrideContext = createOverrideContext(bindingContext, overrideContext);
30 | this.htmlChanged(this.html);
31 | }
32 | unbind() {
33 | this.isBound = false;
34 | this.bindingContext = null;
35 | this.overrideContext = null;
36 | }
37 | attached() {
38 | this.isAttached = true;
39 | this.isInitialized = true;
40 | this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
41 | this.tq.queueMicroTask(() => {
42 | this.tryCompile();
43 | });
44 | }
45 | detached() {
46 | this.isAttached = false;
47 | if (this.isCompiled) {
48 | this.cleanUp();
49 | }
50 | this.slot = null;
51 | }
52 | htmlChanged(newValue, __) {
53 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
54 | this.cleanUp();
55 | }
56 | else if (this.isBound || this.isInitialized) {
57 | this.tq.queueMicroTask(() => {
58 | this.tryCompile();
59 | });
60 | }
61 | }
62 | contextChanged(newValue, _) {
63 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
64 | this.bindingContext = this.overrideContext.bindingContext;
65 | }
66 | else {
67 | this.bindingContext = newValue;
68 | }
69 | if (this.isBound || this.isInitialized) {
70 | this.tq.queueMicroTask(() => {
71 | this.tryCompile();
72 | });
73 | }
74 | }
75 | tryCompile() {
76 | if (this.isAttached) {
77 | if (!this.isCleanedUp) {
78 | this.cleanUp();
79 | }
80 | try {
81 | this.tq.queueMicroTask(() => {
82 | this.compile();
83 | });
84 | }
85 | catch (e) {
86 | logger.warn(e.message);
87 | if (this.renderErrors) {
88 | this.tq.queueMicroTask(() => {
89 | this.compile(`${e.message}`);
90 | });
91 | }
92 | }
93 | }
94 | }
95 | cleanUp() {
96 | this.isCompiled = false;
97 | logger.debug("Cleaning up");
98 | const slot = this.slot;
99 | try {
100 | slot.detached();
101 | }
102 | catch (e) {
103 | logger.error(e.message);
104 | }
105 | try {
106 | slot.unbind();
107 | }
108 | catch (e) {
109 | logger.error(e.message);
110 | }
111 | try {
112 | slot.removeAll();
113 | }
114 | catch (e) {
115 | logger.error(e.message);
116 | }
117 | this.isCleanedUp = true;
118 | }
119 | compile(message) {
120 | if (!this.isCleanedUp) {
121 | this.cleanUp();
122 | }
123 | if ((this.html === null || this.html === undefined) && message === undefined) {
124 | logger.debug("Skipping compilation because no html value is set");
125 | return;
126 | }
127 | this.isCleanedUp = false;
128 | const template = `${message || this.html}`;
129 | logger.debug("Compiling", template, this.bindingContext);
130 | const viewResources = this.container.get(ViewResources);
131 | const childContainer = this.container.createChild();
132 | const factory = this.viewCompiler.compile(template, viewResources);
133 | const view = factory.create(childContainer);
134 | const slot = this.slot;
135 | slot.add(view);
136 | slot.bind(this.bindingContext, this.overrideContext);
137 | slot.attached();
138 | this.dispatchCompiledEvent();
139 | this.isCompiled = true;
140 | }
141 | dispatchCompiledEvent() {
142 | const event = new CustomEvent("compiled", {
143 | cancelable: true,
144 | bubbles: true,
145 | detail: this
146 | });
147 | this.el.dispatchEvent(event);
148 | }
149 | };
150 | __decorate([
151 | bindable({ defaultBindingMode: bindingMode.toView }),
152 | __metadata("design:type", Object)
153 | ], DynamicHtml.prototype, "html", void 0);
154 | __decorate([
155 | bindable({ defaultBindingMode: bindingMode.toView }),
156 | __metadata("design:type", Object)
157 | ], DynamicHtml.prototype, "context", void 0);
158 | __decorate([
159 | bindable({ defaultBindingMode: bindingMode.toView }),
160 | __metadata("design:type", Boolean)
161 | ], DynamicHtml.prototype, "renderErrors", void 0);
162 | DynamicHtml = __decorate([
163 | customElement("dynamic-html"),
164 | inlineView(""),
165 | __metadata("design:paramtypes", [Element, TaskQueue, Container, ViewCompiler])
166 | ], DynamicHtml);
167 | export { DynamicHtml };
168 |
--------------------------------------------------------------------------------
/dist/es2015/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/es2015/interfaces.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aurelia-contrib/aurelia-dynamic-html/e05b31c3d3c25c92b3afb03dae77b9a9983bd07f/dist/es2015/interfaces.js
--------------------------------------------------------------------------------
/dist/es2017/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/es2017/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | import { PLATFORM } from "aurelia-pal";
2 | export function configure(config) {
3 | config.globalResources(PLATFORM.moduleName("./dynamic-html"));
4 | }
5 | export * from "./dynamic-html";
6 |
--------------------------------------------------------------------------------
/dist/es2017/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/es2017/dynamic-html.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | var __metadata = (this && this.__metadata) || function (k, v) {
8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9 | };
10 | import { bindingMode, createOverrideContext } from "aurelia-binding";
11 | import { Container } from "aurelia-dependency-injection";
12 | import { getLogger } from "aurelia-logging";
13 | import { TaskQueue } from "aurelia-task-queue";
14 | import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
15 | const logger = getLogger("dynamic-html");
16 | let DynamicHtml = class DynamicHtml {
17 | constructor(el, tq, container, viewCompiler) {
18 | this.el = el;
19 | this.tq = tq;
20 | this.container = container;
21 | this.viewCompiler = viewCompiler;
22 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
23 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
24 | this.isCleanedUp = true;
25 | }
26 | bind(bindingContext, overrideContext) {
27 | this.isBound = true;
28 | this.bindingContext = this.context || bindingContext.context || bindingContext;
29 | this.overrideContext = createOverrideContext(bindingContext, overrideContext);
30 | this.htmlChanged(this.html);
31 | }
32 | unbind() {
33 | this.isBound = false;
34 | this.bindingContext = null;
35 | this.overrideContext = null;
36 | }
37 | attached() {
38 | this.isAttached = true;
39 | this.isInitialized = true;
40 | this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
41 | this.tq.queueMicroTask(() => {
42 | this.tryCompile();
43 | });
44 | }
45 | detached() {
46 | this.isAttached = false;
47 | if (this.isCompiled) {
48 | this.cleanUp();
49 | }
50 | this.slot = null;
51 | }
52 | htmlChanged(newValue, __) {
53 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
54 | this.cleanUp();
55 | }
56 | else if (this.isBound || this.isInitialized) {
57 | this.tq.queueMicroTask(() => {
58 | this.tryCompile();
59 | });
60 | }
61 | }
62 | contextChanged(newValue, _) {
63 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
64 | this.bindingContext = this.overrideContext.bindingContext;
65 | }
66 | else {
67 | this.bindingContext = newValue;
68 | }
69 | if (this.isBound || this.isInitialized) {
70 | this.tq.queueMicroTask(() => {
71 | this.tryCompile();
72 | });
73 | }
74 | }
75 | tryCompile() {
76 | if (this.isAttached) {
77 | if (!this.isCleanedUp) {
78 | this.cleanUp();
79 | }
80 | try {
81 | this.tq.queueMicroTask(() => {
82 | this.compile();
83 | });
84 | }
85 | catch (e) {
86 | logger.warn(e.message);
87 | if (this.renderErrors) {
88 | this.tq.queueMicroTask(() => {
89 | this.compile(`${e.message}`);
90 | });
91 | }
92 | }
93 | }
94 | }
95 | cleanUp() {
96 | this.isCompiled = false;
97 | logger.debug("Cleaning up");
98 | const slot = this.slot;
99 | try {
100 | slot.detached();
101 | }
102 | catch (e) {
103 | logger.error(e.message);
104 | }
105 | try {
106 | slot.unbind();
107 | }
108 | catch (e) {
109 | logger.error(e.message);
110 | }
111 | try {
112 | slot.removeAll();
113 | }
114 | catch (e) {
115 | logger.error(e.message);
116 | }
117 | this.isCleanedUp = true;
118 | }
119 | compile(message) {
120 | if (!this.isCleanedUp) {
121 | this.cleanUp();
122 | }
123 | if ((this.html === null || this.html === undefined) && message === undefined) {
124 | logger.debug("Skipping compilation because no html value is set");
125 | return;
126 | }
127 | this.isCleanedUp = false;
128 | const template = `${message || this.html}`;
129 | logger.debug("Compiling", template, this.bindingContext);
130 | const viewResources = this.container.get(ViewResources);
131 | const childContainer = this.container.createChild();
132 | const factory = this.viewCompiler.compile(template, viewResources);
133 | const view = factory.create(childContainer);
134 | const slot = this.slot;
135 | slot.add(view);
136 | slot.bind(this.bindingContext, this.overrideContext);
137 | slot.attached();
138 | this.dispatchCompiledEvent();
139 | this.isCompiled = true;
140 | }
141 | dispatchCompiledEvent() {
142 | const event = new CustomEvent("compiled", {
143 | cancelable: true,
144 | bubbles: true,
145 | detail: this
146 | });
147 | this.el.dispatchEvent(event);
148 | }
149 | };
150 | __decorate([
151 | bindable({ defaultBindingMode: bindingMode.toView }),
152 | __metadata("design:type", Object)
153 | ], DynamicHtml.prototype, "html", void 0);
154 | __decorate([
155 | bindable({ defaultBindingMode: bindingMode.toView }),
156 | __metadata("design:type", Object)
157 | ], DynamicHtml.prototype, "context", void 0);
158 | __decorate([
159 | bindable({ defaultBindingMode: bindingMode.toView }),
160 | __metadata("design:type", Boolean)
161 | ], DynamicHtml.prototype, "renderErrors", void 0);
162 | DynamicHtml = __decorate([
163 | customElement("dynamic-html"),
164 | inlineView(""),
165 | __metadata("design:paramtypes", [Element, TaskQueue, Container, ViewCompiler])
166 | ], DynamicHtml);
167 | export { DynamicHtml };
168 |
--------------------------------------------------------------------------------
/dist/es2017/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/es2017/interfaces.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aurelia-contrib/aurelia-dynamic-html/e05b31c3d3c25c92b3afb03dae77b9a9983bd07f/dist/es2017/interfaces.js
--------------------------------------------------------------------------------
/dist/native-modules/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/native-modules/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | import { PLATFORM } from "aurelia-pal";
2 | export function configure(config) {
3 | config.globalResources(PLATFORM.moduleName("./dynamic-html"));
4 | }
5 | export * from "./dynamic-html";
6 |
--------------------------------------------------------------------------------
/dist/native-modules/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/native-modules/dynamic-html.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | var __metadata = (this && this.__metadata) || function (k, v) {
8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9 | };
10 | import { bindingMode, createOverrideContext } from "aurelia-binding";
11 | import { Container } from "aurelia-dependency-injection";
12 | import { getLogger } from "aurelia-logging";
13 | import { TaskQueue } from "aurelia-task-queue";
14 | import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
15 | const logger = getLogger("dynamic-html");
16 | let DynamicHtml = class DynamicHtml {
17 | constructor(el, tq, container, viewCompiler) {
18 | this.el = el;
19 | this.tq = tq;
20 | this.container = container;
21 | this.viewCompiler = viewCompiler;
22 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
23 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
24 | this.isCleanedUp = true;
25 | }
26 | bind(bindingContext, overrideContext) {
27 | this.isBound = true;
28 | this.bindingContext = this.context || bindingContext.context || bindingContext;
29 | this.overrideContext = createOverrideContext(bindingContext, overrideContext);
30 | this.htmlChanged(this.html);
31 | }
32 | unbind() {
33 | this.isBound = false;
34 | this.bindingContext = null;
35 | this.overrideContext = null;
36 | }
37 | attached() {
38 | this.isAttached = true;
39 | this.isInitialized = true;
40 | this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
41 | this.tq.queueMicroTask(() => {
42 | this.tryCompile();
43 | });
44 | }
45 | detached() {
46 | this.isAttached = false;
47 | if (this.isCompiled) {
48 | this.cleanUp();
49 | }
50 | this.slot = null;
51 | }
52 | htmlChanged(newValue, __) {
53 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
54 | this.cleanUp();
55 | }
56 | else if (this.isBound || this.isInitialized) {
57 | this.tq.queueMicroTask(() => {
58 | this.tryCompile();
59 | });
60 | }
61 | }
62 | contextChanged(newValue, _) {
63 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
64 | this.bindingContext = this.overrideContext.bindingContext;
65 | }
66 | else {
67 | this.bindingContext = newValue;
68 | }
69 | if (this.isBound || this.isInitialized) {
70 | this.tq.queueMicroTask(() => {
71 | this.tryCompile();
72 | });
73 | }
74 | }
75 | tryCompile() {
76 | if (this.isAttached) {
77 | if (!this.isCleanedUp) {
78 | this.cleanUp();
79 | }
80 | try {
81 | this.tq.queueMicroTask(() => {
82 | this.compile();
83 | });
84 | }
85 | catch (e) {
86 | logger.warn(e.message);
87 | if (this.renderErrors) {
88 | this.tq.queueMicroTask(() => {
89 | this.compile(`${e.message}`);
90 | });
91 | }
92 | }
93 | }
94 | }
95 | cleanUp() {
96 | this.isCompiled = false;
97 | logger.debug("Cleaning up");
98 | const slot = this.slot;
99 | try {
100 | slot.detached();
101 | }
102 | catch (e) {
103 | logger.error(e.message);
104 | }
105 | try {
106 | slot.unbind();
107 | }
108 | catch (e) {
109 | logger.error(e.message);
110 | }
111 | try {
112 | slot.removeAll();
113 | }
114 | catch (e) {
115 | logger.error(e.message);
116 | }
117 | this.isCleanedUp = true;
118 | }
119 | compile(message) {
120 | if (!this.isCleanedUp) {
121 | this.cleanUp();
122 | }
123 | if ((this.html === null || this.html === undefined) && message === undefined) {
124 | logger.debug("Skipping compilation because no html value is set");
125 | return;
126 | }
127 | this.isCleanedUp = false;
128 | const template = `${message || this.html}`;
129 | logger.debug("Compiling", template, this.bindingContext);
130 | const viewResources = this.container.get(ViewResources);
131 | const childContainer = this.container.createChild();
132 | const factory = this.viewCompiler.compile(template, viewResources);
133 | const view = factory.create(childContainer);
134 | const slot = this.slot;
135 | slot.add(view);
136 | slot.bind(this.bindingContext, this.overrideContext);
137 | slot.attached();
138 | this.dispatchCompiledEvent();
139 | this.isCompiled = true;
140 | }
141 | dispatchCompiledEvent() {
142 | const event = new CustomEvent("compiled", {
143 | cancelable: true,
144 | bubbles: true,
145 | detail: this
146 | });
147 | this.el.dispatchEvent(event);
148 | }
149 | };
150 | __decorate([
151 | bindable({ defaultBindingMode: bindingMode.toView }),
152 | __metadata("design:type", Object)
153 | ], DynamicHtml.prototype, "html", void 0);
154 | __decorate([
155 | bindable({ defaultBindingMode: bindingMode.toView }),
156 | __metadata("design:type", Object)
157 | ], DynamicHtml.prototype, "context", void 0);
158 | __decorate([
159 | bindable({ defaultBindingMode: bindingMode.toView }),
160 | __metadata("design:type", Boolean)
161 | ], DynamicHtml.prototype, "renderErrors", void 0);
162 | DynamicHtml = __decorate([
163 | customElement("dynamic-html"),
164 | inlineView(""),
165 | __metadata("design:paramtypes", [Element, TaskQueue, Container, ViewCompiler])
166 | ], DynamicHtml);
167 | export { DynamicHtml };
168 |
--------------------------------------------------------------------------------
/dist/native-modules/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/native-modules/interfaces.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aurelia-contrib/aurelia-dynamic-html/e05b31c3d3c25c92b3afb03dae77b9a9983bd07f/dist/native-modules/interfaces.js
--------------------------------------------------------------------------------
/dist/system/aurelia-dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | export declare function configure(config: any): void;
2 | export * from "./dynamic-html";
3 | export * from "./interfaces";
4 |
--------------------------------------------------------------------------------
/dist/system/aurelia-dynamic-html.js:
--------------------------------------------------------------------------------
1 | System.register(["aurelia-pal", "./dynamic-html"], function (exports_1, context_1) {
2 | "use strict";
3 | var __moduleName = context_1 && context_1.id;
4 | function configure(config) {
5 | config.globalResources(aurelia_pal_1.PLATFORM.moduleName("./dynamic-html"));
6 | }
7 | exports_1("configure", configure);
8 | var aurelia_pal_1;
9 | var exportedNames_1 = {
10 | "configure": true
11 | };
12 | function exportStar_1(m) {
13 | var exports = {};
14 | for (var n in m) {
15 | if (n !== "default" && !exportedNames_1.hasOwnProperty(n)) exports[n] = m[n];
16 | }
17 | exports_1(exports);
18 | }
19 | return {
20 | setters: [
21 | function (aurelia_pal_1_1) {
22 | aurelia_pal_1 = aurelia_pal_1_1;
23 | },
24 | function (dynamic_html_1_1) {
25 | exportStar_1(dynamic_html_1_1);
26 | }
27 | ],
28 | execute: function () {
29 | }
30 | };
31 | });
32 |
--------------------------------------------------------------------------------
/dist/system/dynamic-html.d.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { TaskQueue } from "aurelia-task-queue";
4 | import { ViewCompiler, ViewSlot } from "aurelia-templating";
5 | import { IBindingContext, IOverrideContext } from "./interfaces";
6 | export declare class DynamicHtml {
7 | html: string | null;
8 | context: IBindingContext | null;
9 | renderErrors: boolean;
10 | slot: ViewSlot | null;
11 | bindingContext: IBindingContext | null;
12 | overrideContext: IOverrideContext | OverrideContext | null;
13 | isAttached: boolean;
14 | isBound: boolean;
15 | isCompiled: boolean;
16 | isCleanedUp: boolean;
17 | isInitialized: boolean;
18 | el: HTMLElement;
19 | protected tq: TaskQueue;
20 | protected container: Container;
21 | protected viewCompiler: ViewCompiler;
22 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler);
23 | bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void;
24 | unbind(): void;
25 | attached(): void;
26 | detached(): void;
27 | protected htmlChanged(newValue: string | null, __?: string): void;
28 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void;
29 | protected tryCompile(): void;
30 | protected cleanUp(): void;
31 | protected compile(message?: string): void;
32 | protected dispatchCompiledEvent(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/dist/system/dynamic-html.js:
--------------------------------------------------------------------------------
1 | System.register(["aurelia-binding", "aurelia-dependency-injection", "aurelia-logging", "aurelia-task-queue", "aurelia-templating"], function (exports_1, context_1) {
2 | "use strict";
3 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
4 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
7 | return c > 3 && r && Object.defineProperty(target, key, r), r;
8 | };
9 | var __metadata = (this && this.__metadata) || function (k, v) {
10 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
11 | };
12 | var __moduleName = context_1 && context_1.id;
13 | var aurelia_binding_1, aurelia_dependency_injection_1, aurelia_logging_1, aurelia_task_queue_1, aurelia_templating_1, logger, DynamicHtml;
14 | return {
15 | setters: [
16 | function (aurelia_binding_1_1) {
17 | aurelia_binding_1 = aurelia_binding_1_1;
18 | },
19 | function (aurelia_dependency_injection_1_1) {
20 | aurelia_dependency_injection_1 = aurelia_dependency_injection_1_1;
21 | },
22 | function (aurelia_logging_1_1) {
23 | aurelia_logging_1 = aurelia_logging_1_1;
24 | },
25 | function (aurelia_task_queue_1_1) {
26 | aurelia_task_queue_1 = aurelia_task_queue_1_1;
27 | },
28 | function (aurelia_templating_1_1) {
29 | aurelia_templating_1 = aurelia_templating_1_1;
30 | }
31 | ],
32 | execute: function () {
33 | logger = aurelia_logging_1.getLogger("dynamic-html");
34 | DynamicHtml = class DynamicHtml {
35 | constructor(el, tq, container, viewCompiler) {
36 | this.el = el;
37 | this.tq = tq;
38 | this.container = container;
39 | this.viewCompiler = viewCompiler;
40 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
41 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
42 | this.isCleanedUp = true;
43 | }
44 | bind(bindingContext, overrideContext) {
45 | this.isBound = true;
46 | this.bindingContext = this.context || bindingContext.context || bindingContext;
47 | this.overrideContext = aurelia_binding_1.createOverrideContext(bindingContext, overrideContext);
48 | this.htmlChanged(this.html);
49 | }
50 | unbind() {
51 | this.isBound = false;
52 | this.bindingContext = null;
53 | this.overrideContext = null;
54 | }
55 | attached() {
56 | this.isAttached = true;
57 | this.isInitialized = true;
58 | this.slot = new aurelia_templating_1.ViewSlot(this.el.firstElementChild || this.el, true);
59 | this.tq.queueMicroTask(() => {
60 | this.tryCompile();
61 | });
62 | }
63 | detached() {
64 | this.isAttached = false;
65 | if (this.isCompiled) {
66 | this.cleanUp();
67 | }
68 | this.slot = null;
69 | }
70 | htmlChanged(newValue, __) {
71 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
72 | this.cleanUp();
73 | }
74 | else if (this.isBound || this.isInitialized) {
75 | this.tq.queueMicroTask(() => {
76 | this.tryCompile();
77 | });
78 | }
79 | }
80 | contextChanged(newValue, _) {
81 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
82 | this.bindingContext = this.overrideContext.bindingContext;
83 | }
84 | else {
85 | this.bindingContext = newValue;
86 | }
87 | if (this.isBound || this.isInitialized) {
88 | this.tq.queueMicroTask(() => {
89 | this.tryCompile();
90 | });
91 | }
92 | }
93 | tryCompile() {
94 | if (this.isAttached) {
95 | if (!this.isCleanedUp) {
96 | this.cleanUp();
97 | }
98 | try {
99 | this.tq.queueMicroTask(() => {
100 | this.compile();
101 | });
102 | }
103 | catch (e) {
104 | logger.warn(e.message);
105 | if (this.renderErrors) {
106 | this.tq.queueMicroTask(() => {
107 | this.compile(`${e.message}`);
108 | });
109 | }
110 | }
111 | }
112 | }
113 | cleanUp() {
114 | this.isCompiled = false;
115 | logger.debug("Cleaning up");
116 | const slot = this.slot;
117 | try {
118 | slot.detached();
119 | }
120 | catch (e) {
121 | logger.error(e.message);
122 | }
123 | try {
124 | slot.unbind();
125 | }
126 | catch (e) {
127 | logger.error(e.message);
128 | }
129 | try {
130 | slot.removeAll();
131 | }
132 | catch (e) {
133 | logger.error(e.message);
134 | }
135 | this.isCleanedUp = true;
136 | }
137 | compile(message) {
138 | if (!this.isCleanedUp) {
139 | this.cleanUp();
140 | }
141 | if ((this.html === null || this.html === undefined) && message === undefined) {
142 | logger.debug("Skipping compilation because no html value is set");
143 | return;
144 | }
145 | this.isCleanedUp = false;
146 | const template = `${message || this.html}`;
147 | logger.debug("Compiling", template, this.bindingContext);
148 | const viewResources = this.container.get(aurelia_templating_1.ViewResources);
149 | const childContainer = this.container.createChild();
150 | const factory = this.viewCompiler.compile(template, viewResources);
151 | const view = factory.create(childContainer);
152 | const slot = this.slot;
153 | slot.add(view);
154 | slot.bind(this.bindingContext, this.overrideContext);
155 | slot.attached();
156 | this.dispatchCompiledEvent();
157 | this.isCompiled = true;
158 | }
159 | dispatchCompiledEvent() {
160 | const event = new CustomEvent("compiled", {
161 | cancelable: true,
162 | bubbles: true,
163 | detail: this
164 | });
165 | this.el.dispatchEvent(event);
166 | }
167 | };
168 | __decorate([
169 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
170 | __metadata("design:type", Object)
171 | ], DynamicHtml.prototype, "html", void 0);
172 | __decorate([
173 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
174 | __metadata("design:type", Object)
175 | ], DynamicHtml.prototype, "context", void 0);
176 | __decorate([
177 | aurelia_templating_1.bindable({ defaultBindingMode: aurelia_binding_1.bindingMode.toView }),
178 | __metadata("design:type", Boolean)
179 | ], DynamicHtml.prototype, "renderErrors", void 0);
180 | DynamicHtml = __decorate([
181 | aurelia_templating_1.customElement("dynamic-html"),
182 | aurelia_templating_1.inlineView(""),
183 | __metadata("design:paramtypes", [Element, aurelia_task_queue_1.TaskQueue, aurelia_dependency_injection_1.Container, aurelia_templating_1.ViewCompiler])
184 | ], DynamicHtml);
185 | exports_1("DynamicHtml", DynamicHtml);
186 | }
187 | };
188 | });
189 |
--------------------------------------------------------------------------------
/dist/system/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 | export interface IOverrideContext {
5 | parentOverrideContext: IOverrideContext;
6 | bindingContext: IBindingContext;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/system/interfaces.js:
--------------------------------------------------------------------------------
1 | System.register([], function (exports_1, context_1) {
2 | "use strict";
3 | var __moduleName = context_1 && context_1.id;
4 | return {
5 | setters: [],
6 | execute: function () {
7 | }
8 | };
9 | });
10 |
--------------------------------------------------------------------------------
/karma.conf.ts:
--------------------------------------------------------------------------------
1 | import * as karma from "karma";
2 | import * as path from "path";
3 | import * as webpack from "webpack";
4 |
5 | export interface IKarmaConfig extends karma.Config, IKarmaConfigOptions {
6 | transpileOnly?: boolean;
7 | noInfo?: boolean;
8 | coverage?: boolean;
9 | tsconfig?: string;
10 | set(config: IKarmaConfigOptions): void;
11 | }
12 |
13 | export interface IKarmaConfigOptions extends karma.ConfigOptions {
14 | webpack: webpack.Configuration;
15 | coverageIstanbulReporter?: any;
16 | webpackServer: any;
17 | customLaunchers: any;
18 | }
19 |
20 | export default (config: IKarmaConfig): void => {
21 | const rules: webpack.Rule[] = [];
22 |
23 | const options: IKarmaConfigOptions = {
24 | basePath: config.basePath || "./",
25 | frameworks: ["jasmine"],
26 | files: ["test/setup.ts"],
27 | preprocessors: {
28 | "test/setup.ts": ["webpack", "sourcemap"]
29 | },
30 | webpack: {
31 | mode: "development",
32 | resolve: {
33 | extensions: [".ts", ".js"],
34 | modules: ["src", "node_modules"],
35 | alias: {
36 | src: path.resolve(__dirname, "src")
37 | }
38 | },
39 | devtool: "cheap-module-eval-source-map",
40 | module: {
41 | rules: [
42 | {
43 | test: /\.ts$/,
44 | loader: "ts-loader",
45 | exclude: /node_modules/,
46 | options: {
47 | configFile: config.tsconfig,
48 | transpileOnly: config.transpileOnly
49 | }
50 | }
51 | ]
52 | }
53 | },
54 | mime: {
55 | "text/x-typescript": ["ts"]
56 | },
57 | reporters: ["mocha", "progress"],
58 | webpackServer: { noInfo: config.noInfo },
59 | browsers: config.browsers || ["Chrome"],
60 | customLaunchers: {
61 | ChromeDebugging: {
62 | base: "Chrome",
63 | flags: ["--remote-debugging-port=9333"],
64 | debug: true
65 | }
66 | }
67 | };
68 |
69 | if (config.coverage) {
70 | options.webpack.module.rules.push({
71 | enforce: "post",
72 | exclude: /(node_modules|\.spec\.ts$)/,
73 | loader: "istanbul-instrumenter-loader",
74 | options: { esModules: true },
75 | test: /src[\/\\].+\.ts$/
76 | });
77 | options.reporters.push("coverage-istanbul");
78 | options.coverageIstanbulReporter = {
79 | reports: ["html", "lcovonly", "text-summary"],
80 | fixWebpackSourcePaths: true
81 | };
82 | }
83 |
84 | config.set(options);
85 | };
86 |
--------------------------------------------------------------------------------
/package-scripts.js:
--------------------------------------------------------------------------------
1 | const { concurrent, copy, crossEnv, rimraf, series } = require("nps-utils");
2 |
3 | function config(name) {
4 | return `configs/tsconfig-${name}.json`;
5 | }
6 |
7 | function tsc(tsconfig) {
8 | return package(`tsc --project ${config(tsconfig)}`);
9 | }
10 |
11 | function webpack(tool, arg) {
12 | return crossEnv(`TS_NODE_PROJECT=\"${config("webpack")}\" ${tool} --config webpack.config.ts ${arg}`);
13 | }
14 |
15 | function package(script) {
16 | return crossEnv(`./node_modules/.bin/${script}`);
17 | }
18 |
19 | function karma(single, watch, browsers, transpileOnly, noInfo, coverage, tsconfig, logLevel, devtool) {
20 | return package(
21 | "karma start"
22 | .concat(single !== null ? ` --single-run=${single}` : "")
23 | .concat(watch !== null ? ` --auto-watch=${watch}` : "")
24 | .concat(browsers !== null ? ` --browsers=${browsers}` : "")
25 | .concat(transpileOnly !== null ? ` --transpile-only=${transpileOnly}` : "")
26 | .concat(noInfo !== null ? ` --no-info=${noInfo}` : "")
27 | .concat(coverage !== null ? ` --coverage=${coverage}` : "")
28 | .concat(tsconfig !== null ? ` --tsconfig=${tsconfig}` : "")
29 | .concat(logLevel !== null ? ` --log-level=${logLevel}` : "")
30 | .concat(devtool !== null ? ` --devtool=${devtool}` : "")
31 | );
32 | }
33 |
34 | function release(version, dry) {
35 | /**
36 | * Dry prevents anything irreversible from happening.
37 | * It will do pretty much do the same thing except it won't push to git or publish to npm.
38 | * Just remember to "unbump" the version in package.json after a dry run (see explanation below)
39 | */
40 | const variant = dry ? "dry" : "default";
41 | return {
42 | default: series.nps(
43 | `release.${version}.${variant}.before`,
44 | `release.${version}.${variant}.version`,
45 | `release.${version}.${variant}.after`
46 | ),
47 | before: series.nps(
48 | `release.${version}.${variant}.build`,
49 | `release.${version}.${variant}.bump`,
50 | `release.${version}.${variant}.git.stage`
51 | ),
52 | after: series.nps(`release.${version}.${variant}.git.push`, `release.${version}.${variant}.npm.publish`),
53 | bump: crossEnv(`npm --no-git-tag-version version ${version}`),
54 | /**
55 | * Normally, standard-version looks for certain keywords in the commit log and automatically assigns
56 | * major/minor/patch based on the contents of those logs.
57 | *
58 | * --first-release disables that behavior and does not change the version, which allows us to manually
59 | * decide the version and bump it with npm version (see right above) instead.
60 | *
61 | * The downside is that we have to bump the version in package.json even in a dry run, because
62 | * standard-version wouldn't report what it would do otherwise.
63 | *
64 | * Therefore, always remember to manually "unbump" the version number in package.json after doing a dry run!
65 | * If you forget this, you'll end up bumping the version twice which gives you one release without changes.
66 | */
67 | version: `standard-version --first-release --commit-all${dry ? " --dry-run" : ""}`,
68 | build: series.nps("test", "build.dist"),
69 | git: {
70 | stage: "git add package.json dist",
71 | push: `git push --follow-tags origin master${dry ? " -n" : ""}`
72 | },
73 | npm: {
74 | publish: `npm ${dry ? "pack" : "publish"}`
75 | }
76 | };
77 | }
78 |
79 | module.exports = {
80 | scripts: {
81 | lint: package(`tslint --project ${config("build")}`),
82 | test: {
83 | default: package("nps test.single"),
84 | single: karma(true, false, "ChromeHeadless", true, true, true, config("test"), null, null),
85 | watch: {
86 | default: package("nps test.watch.dev"),
87 | dev: karma(false, true, "ChromeHeadless", true, true, true, config("test"), null, null),
88 | debug: karma(false, true, "ChromeDebugging", true, false, null, config("test"), "debug", null)
89 | }
90 | },
91 | build: {
92 | demo: {
93 | default: "nps build.demo.development",
94 | development: {
95 | default: webpack("webpack-dev-server", "--hot --env.server")
96 | },
97 | production: {
98 | default: webpack("webpack", "--env.production")
99 | }
100 | },
101 | dist: {
102 | default: "nps build.dist.tsc",
103 | before: series.nps("lint", "build.dist.clean"),
104 | clean: rimraf("dist"),
105 | rollup: {
106 | default: series.nps("build.dist.before", "build.dist.rollup.all"),
107 | all: `${package("rollup")} -c`
108 | },
109 | tsc: {
110 | default: series.nps("build.dist.before", "build.dist.tsc.all"),
111 | all: concurrent.nps(
112 | "build.dist.tsc.amd",
113 | "build.dist.tsc.commonjs",
114 | "build.dist.tsc.es2017",
115 | "build.dist.tsc.es2015",
116 | "build.dist.tsc.nativeModules",
117 | "build.dist.tsc.system"
118 | ),
119 | amd: tsc("build-amd"),
120 | commonjs: tsc("build-commonjs"),
121 | es2017: tsc("build-es2017"),
122 | es2015: tsc("build-es2015"),
123 | nativeModules: tsc("build-native-modules"),
124 | system: tsc("build-system")
125 | }
126 | }
127 | },
128 | release: {
129 | patch: {
130 | default: release("patch"),
131 | dry: release("patch", true)
132 | },
133 | minor: {
134 | default: release("minor"),
135 | dry: release("minor", true)
136 | },
137 | major: {
138 | default: release("major"),
139 | dry: release("major", true)
140 | }
141 | },
142 | /**
143 | * Make sure to run "npm run ghpages-setup" before "npm run ghpages" the very first time,
144 | * or manually create the gh-pages branch and set the remote.
145 | *
146 | * There is no dry run variant for this because it's not really that harmful if the demo page goes bad
147 | * and it doesn't affect the master branch.
148 | */
149 | ghpages: {
150 | default: series(
151 | "git checkout gh-pages",
152 | "git merge master --no-edit",
153 | rimraf("*.bundle.js *.worker.js"),
154 | package("nps build.demo.production"),
155 | "git add index.html *.bundle.js *.worker.js",
156 | 'git commit -m "doc(demo): build demo"',
157 | "git push",
158 | "git checkout master"
159 | ),
160 | setup: series(
161 | "git checkout -b gh-pages",
162 | "git push -u origin gh-pages",
163 | "git checkout master"
164 | )
165 | }
166 | }
167 | };
168 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aurelia-dynamic-html",
3 | "version": "0.3.1",
4 | "description": "Aurelia custom element that takes (server- or client side) generated html and compiles it into a fully functional Aurelia View",
5 | "keywords": [
6 | "aurelia",
7 | "dynamic",
8 | "generate",
9 | "html",
10 | "runtime",
11 | "composition",
12 | "enhance",
13 | "compile"
14 | ],
15 | "homepage": "https://github.com/aurelia-contrib/aurelia-dynamic-html",
16 | "bugs": {
17 | "url": "https://github.com/aurelia-contrib/aurelia-dynamic-html/issues"
18 | },
19 | "license": "MIT",
20 | "author": "Fred Kleuver ",
21 | "main": "dist/commonjs/aurelia-dynamic-html.js",
22 | "typings": "dist/commonjs/aurelia-dynamic-html.d.ts",
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/aurelia-contrib/aurelia-dynamic-html"
26 | },
27 | "scripts": {
28 | "lint": "cross-env ./node_modules/.bin/nps lint",
29 | "test": "cross-env ./node_modules/.bin/nps test",
30 | "test-watch": "cross-env ./node_modules/.bin/nps test.watch",
31 | "test-debug": "cross-env ./node_modules/.bin/nps test.watch.debug",
32 | "build": "cross-env ./node_modules/.bin/nps build.dist",
33 | "demo-dev": "cross-env ./node_modules/.bin/nps build.demo",
34 | "demo-prod": "cross-env ./node_modules/.bin/nps build.demo.production",
35 | "release-patch": "cross-env ./node_modules/.bin/nps release.patch",
36 | "release-minor": "cross-env ./node_modules/.bin/nps release.minor",
37 | "release-major": "cross-env ./node_modules/.bin/nps release.major",
38 | "release-patch-dry": "cross-env ./node_modules/.bin/nps release.patch.dry",
39 | "release-minor-dry": "cross-env ./node_modules/.bin/nps release.minor.dry",
40 | "release-major-dry": "cross-env ./node_modules/.bin/nps release.major.dry",
41 | "ghpages": "cross-env ./node_modules/.bin/nps ghpages",
42 | "ghpages-setup": "cross-env ./node_modules/.bin/nps ghpages.setup"
43 | },
44 | "dependencies": {
45 | "aurelia-binding": "^1.0.0",
46 | "aurelia-dependency-injection": "^1.0.0",
47 | "aurelia-logging": "^1.0.0",
48 | "aurelia-pal": "^1.0.0",
49 | "aurelia-task-queue": "^1.0.0",
50 | "aurelia-templating": "^1.0.0"
51 | },
52 | "devDependencies": {
53 | "@types/bluebird-global": "^3.5.5",
54 | "@types/extract-text-webpack-plugin": "^3.0.2",
55 | "@types/html-webpack-plugin": "^2.30.3",
56 | "@types/jasmine": "^2.8.6",
57 | "@types/karma": "^1.7.3",
58 | "@types/node": "^9.6.4",
59 | "@types/webpack": "^4.1.3",
60 | "@types/webpack-env": "^1.13.6",
61 | "aurelia-bootstrapper": "^2.2.0",
62 | "aurelia-framework": "^1.2.0",
63 | "aurelia-pal-browser": "^1.7.0",
64 | "aurelia-polyfills": "^1.3.0",
65 | "aurelia-testing": "^1.0.0-beta.4.0.0",
66 | "aurelia-webpack-plugin": "^3.0.0-rc.1",
67 | "bluebird": "^3.5.1",
68 | "cross-env": "^5.1.4",
69 | "css-loader": "^0.28.11",
70 | "expose-loader": "^0.7.5",
71 | "extract-loader": "^2.0.1",
72 | "extract-text-webpack-plugin": "^3.0.2",
73 | "fork-ts-checker-webpack-plugin": "^0.4.1",
74 | "html-loader": "^0.5.5",
75 | "html-webpack-plugin": "^3.2.0",
76 | "istanbul-instrumenter-loader": "^3.0.1",
77 | "jasmine-core": "^3.1.0",
78 | "karma": "^2.0.0",
79 | "karma-chrome-launcher": "^2.2.0",
80 | "karma-coverage": "^1.1.1",
81 | "karma-coverage-istanbul-reporter": "^1.4.2",
82 | "karma-firefox-launcher": "^1.1.0",
83 | "karma-ie-launcher": "^1.0.0",
84 | "karma-jasmine": "^1.1.1",
85 | "karma-mocha-reporter": "^2.2.5",
86 | "karma-sourcemap-loader": "^0.3.7",
87 | "karma-webpack": "^3.0.0",
88 | "monaco-editor": "^0.12.0",
89 | "monaco-editor-webpack-plugin": "^1.1.0",
90 | "nps": "^5.9.0",
91 | "nps-utils": "^1.5.0",
92 | "rollup": "^0.57.1",
93 | "rollup-plugin-commonjs": "^9.1.0",
94 | "rollup-plugin-node-resolve": "^3.3.0",
95 | "rollup-plugin-typescript2": "^0.13.0",
96 | "standard-version": "^4.3.0",
97 | "style-loader": "^0.20.3",
98 | "ts-loader": "^4.2.0",
99 | "ts-node": "^5.0.1",
100 | "tsconfig-paths": "^3.2.0",
101 | "tslint": "^5.9.1",
102 | "tslint-microsoft-contrib": "^5.0.3",
103 | "typescript": "^2.8.1",
104 | "webpack": "^4.5.0",
105 | "webpack-cli": "^2.0.14",
106 | "webpack-dev-server": "^3.1.3"
107 | },
108 | "jspm": {
109 | "registry": "npm",
110 | "jspmPackage": true,
111 | "main": "index",
112 | "format": "amd",
113 | "directories": {
114 | "dist": "dist/amd"
115 | },
116 | "dependencies": {
117 | "aurelia-binding": "^1.0.0",
118 | "aurelia-dependency-injection": "^1.0.0",
119 | "aurelia-logging": "^1.0.0",
120 | "aurelia-pal": "^1.0.0",
121 | "aurelia-task-queue": "^1.0.0",
122 | "aurelia-templating": "^1.0.0"
123 | },
124 | "peerDependencies": {
125 | "aurelia-binding": "^1.0.0",
126 | "aurelia-dependency-injection": "^1.0.0",
127 | "aurelia-logging": "^1.0.0",
128 | "aurelia-pal": "^1.0.0",
129 | "aurelia-task-queue": "^1.0.0",
130 | "aurelia-templating": "^1.0.0"
131 | }
132 | },
133 | "aurelia": {
134 | "import": {
135 | "dependencies": [
136 | {
137 | "name": "aurelia-dynamic-html",
138 | "path": "../node_modules/aurelia-dynamic-html/dist/amd",
139 | "main": "aurelia-dynamic-html"
140 | }
141 | ]
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { readFileSync } from "fs";
2 | import ts from "rollup-plugin-typescript2";
3 | import resolve from "rollup-plugin-node-resolve";
4 | import commonJS from "rollup-plugin-commonjs";
5 |
6 | const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
7 |
8 | function output(config, format, opts = {}) {
9 | return {
10 | input: `src/${pkg.name}.ts`,
11 | output: { ...{ file: `dist/${config}/${pkg.name}.js`, format }, ...opts },
12 | plugins: [
13 | resolve(),
14 | commonJS({
15 | include: "node_modules/**"
16 | }),
17 | ts({
18 | tsconfig: `configs/tsconfig-build-${config}.json`,
19 | tsconfigOverride: {
20 | compilerOptions: {
21 | module: "ES2015"
22 | }
23 | },
24 | cacheRoot: ".rollupcache"
25 | })
26 | ],
27 | external: [
28 | "aurelia-binding",
29 | "aurelia-dependency-injection",
30 | "aurelia-logging",
31 | "aurelia-pal",
32 | "aurelia-task-queue",
33 | "aurelia-templating"
34 | ]
35 | };
36 | }
37 |
38 | const outputs = [
39 | output("amd", "amd", { amd: { id: pkg.name } }),
40 | output("commonjs", "cjs"),
41 | output("es2017", "es"),
42 | output("es2015", "es"),
43 | output("native-modules", "es"),
44 | output("system", "system")
45 | ];
46 |
47 | export default outputs;
48 |
--------------------------------------------------------------------------------
/src/aurelia-dynamic-html.ts:
--------------------------------------------------------------------------------
1 | import { PLATFORM } from "aurelia-pal";
2 |
3 | export function configure(config: any): void {
4 | config.globalResources(PLATFORM.moduleName("./dynamic-html"));
5 | }
6 |
7 | export * from "./dynamic-html";
8 | export * from "./interfaces";
9 |
--------------------------------------------------------------------------------
/src/dynamic-html.ts:
--------------------------------------------------------------------------------
1 | import { bindingMode, createOverrideContext, OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { getLogger } from "aurelia-logging";
4 | import { TaskQueue } from "aurelia-task-queue";
5 | import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
6 | import { IBindingContext, IOverrideContext } from "./interfaces";
7 |
8 | const logger = getLogger("dynamic-html");
9 |
10 | @customElement("dynamic-html")
11 | @inlineView("")
12 | export class DynamicHtml {
13 | @bindable({ defaultBindingMode: bindingMode.toView })
14 | public html: string | null;
15 |
16 | @bindable({ defaultBindingMode: bindingMode.toView })
17 | public context: IBindingContext | null;
18 |
19 | @bindable({ defaultBindingMode: bindingMode.toView })
20 | public renderErrors: boolean;
21 |
22 | public slot: ViewSlot | null;
23 | public bindingContext: IBindingContext | null;
24 | public overrideContext: IOverrideContext | OverrideContext | null;
25 | public isAttached: boolean;
26 | public isBound: boolean;
27 | public isCompiled: boolean;
28 | public isCleanedUp: boolean;
29 | public isInitialized: boolean;
30 |
31 | public el: HTMLElement;
32 | protected tq: TaskQueue;
33 | protected container: Container;
34 | protected viewCompiler: ViewCompiler;
35 |
36 | constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler) {
37 | this.el = el as HTMLElement;
38 | this.tq = tq;
39 | this.container = container;
40 | this.viewCompiler = viewCompiler;
41 | this.html = this.context = this.slot = this.bindingContext = this.overrideContext = null;
42 | this.renderErrors = this.isAttached = this.isBound = this.isCompiled = this.isInitialized = false;
43 | this.isCleanedUp = true;
44 | }
45 |
46 | public bind(bindingContext: IBindingContext, overrideContext: IOverrideContext): void {
47 | this.isBound = true;
48 |
49 | this.bindingContext = this.context || bindingContext.context || bindingContext;
50 | this.overrideContext = createOverrideContext(bindingContext, overrideContext);
51 |
52 | this.htmlChanged(this.html);
53 | }
54 |
55 | public unbind(): void {
56 | this.isBound = false;
57 |
58 | this.bindingContext = null;
59 | this.overrideContext = null;
60 | }
61 |
62 | public attached(): void {
63 | this.isAttached = true;
64 | this.isInitialized = true;
65 |
66 | this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
67 |
68 | this.tq.queueMicroTask(() => {
69 | this.tryCompile();
70 | });
71 | }
72 |
73 | public detached(): void {
74 | this.isAttached = false;
75 |
76 | if (this.isCompiled) {
77 | this.cleanUp();
78 | }
79 | this.slot = null;
80 | }
81 |
82 | protected htmlChanged(newValue: string | null, __?: string): void {
83 | if ((newValue === null || newValue === undefined) && !this.isCleanedUp) {
84 | this.cleanUp();
85 | } else if (this.isBound || this.isInitialized) {
86 | this.tq.queueMicroTask(() => {
87 | this.tryCompile();
88 | });
89 | }
90 | }
91 |
92 | protected contextChanged(newValue: IBindingContext | null, _?: IBindingContext): void {
93 | if ((newValue === null || newValue === undefined) && !!this.overrideContext) {
94 | this.bindingContext = this.overrideContext.bindingContext;
95 | } else {
96 | this.bindingContext = newValue;
97 | }
98 | if (this.isBound || this.isInitialized) {
99 | this.tq.queueMicroTask(() => {
100 | this.tryCompile();
101 | });
102 | }
103 | }
104 |
105 | protected tryCompile(): void {
106 | if (this.isAttached) {
107 | if (!this.isCleanedUp) {
108 | this.cleanUp();
109 | }
110 | try {
111 | this.tq.queueMicroTask(() => {
112 | this.compile();
113 | });
114 | } catch (e) {
115 | logger.warn(e.message);
116 | if (this.renderErrors) {
117 | this.tq.queueMicroTask(() => {
118 | this.compile(`${e.message}`);
119 | });
120 | }
121 | }
122 | }
123 | }
124 |
125 | protected cleanUp(): void {
126 | this.isCompiled = false;
127 |
128 | logger.debug("Cleaning up");
129 |
130 | const slot = this.slot as ViewSlot;
131 | try {
132 | slot.detached();
133 | } catch (e) {
134 | logger.error(e.message);
135 | }
136 | try {
137 | slot.unbind();
138 | } catch (e) {
139 | logger.error(e.message);
140 | }
141 | try {
142 | slot.removeAll();
143 | } catch (e) {
144 | logger.error(e.message);
145 | }
146 |
147 | this.isCleanedUp = true;
148 | }
149 |
150 | protected compile(message?: string): void {
151 | if (!this.isCleanedUp) {
152 | this.cleanUp();
153 | }
154 | if ((this.html === null || this.html === undefined) && message === undefined) {
155 | logger.debug("Skipping compilation because no html value is set");
156 |
157 | return;
158 | }
159 | this.isCleanedUp = false;
160 |
161 | const template = `${message || this.html}`;
162 |
163 | logger.debug("Compiling", template, this.bindingContext);
164 |
165 | const viewResources = this.container.get(ViewResources) as ViewResources;
166 | const childContainer = this.container.createChild();
167 | const factory = this.viewCompiler.compile(template, viewResources);
168 | const view = factory.create(childContainer);
169 | const slot = this.slot as ViewSlot;
170 |
171 | slot.add(view);
172 | slot.bind(this.bindingContext as IBindingContext, this.overrideContext as IOverrideContext);
173 | slot.attached();
174 |
175 | this.dispatchCompiledEvent();
176 |
177 | this.isCompiled = true;
178 | }
179 |
180 | protected dispatchCompiledEvent(): void {
181 | const event = new CustomEvent("compiled", {
182 | cancelable: true,
183 | bubbles: true,
184 | detail: this
185 | });
186 | this.el.dispatchEvent(event);
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface IBindingContext {
2 | [key: string]: any;
3 | }
4 |
5 | export interface IOverrideContext {
6 | parentOverrideContext: IOverrideContext;
7 | bindingContext: IBindingContext;
8 | }
9 |
--------------------------------------------------------------------------------
/test/setup.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable
2 | import "aurelia-loader-webpack";
3 | import "aurelia-polyfills";
4 | import { initialize } from "aurelia-pal-browser";
5 | // tslint:enable
6 |
7 | initialize();
8 | Error.stackTraceLimit = Infinity;
9 |
10 | const testContext: any = (require as any).context("./unit", true, /\.spec/);
11 | testContext.keys().forEach(testContext);
12 |
--------------------------------------------------------------------------------
/test/unit/aurelia-dynamic-html.spec.ts:
--------------------------------------------------------------------------------
1 | import { configure } from "../../src/aurelia-dynamic-html";
2 |
3 | describe("configure()", () => {
4 | it("should call globalResources", () => {
5 | let moduleName: string = null as any;
6 | const config: any = {
7 | globalResources: (name: string) => {
8 | moduleName = name;
9 | }
10 | };
11 |
12 | configure(config);
13 |
14 | expect(moduleName).toEqual("./dynamic-html");
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/unit/dynamic-html.spec.ts:
--------------------------------------------------------------------------------
1 | import { OverrideContext } from "aurelia-binding";
2 | import { Container } from "aurelia-dependency-injection";
3 | import { DOM } from "aurelia-pal";
4 | import { TaskQueue } from "aurelia-task-queue";
5 | import { BindingLanguage, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
6 | import { DynamicHtml } from "../../src/dynamic-html";
7 | import { IBindingContext, IOverrideContext } from "../../src/interfaces";
8 | import { getAllProperties, getAllPropertyNames } from "./util";
9 |
10 | // tslint:disable:mocha-no-side-effect-code
11 | // tslint:disable:variable-name
12 | // tslint:disable:no-empty
13 | // tslint:disable:max-line-length
14 |
15 | class SutProps {
16 | public __metadata__: any = null;
17 | public html: string | null = null;
18 | public context: IBindingContext | null = null;
19 | public renderErrors: boolean = false;
20 |
21 | public slot: ViewSlot | null = null;
22 | public bindingContext: IBindingContext | null = null;
23 | public overrideContext: IOverrideContext | OverrideContext | null = null;
24 | public isAttached: boolean = false;
25 | public isBound: boolean = false;
26 | public isCompiled: boolean = false;
27 | public isCleanedUp: boolean = true;
28 | public isInitialized: boolean = false;
29 |
30 | public el: HTMLElement = DOM.createElement("div");
31 | public tq: TaskQueue = new TaskQueue();
32 | public container: Container = new Container();
33 | public viewCompiler: ViewCompiler = new ViewCompiler(new BindingLanguage(), new ViewResources());
34 |
35 | public bind(_: any, __: any): void {}
36 | public unbind(): void {}
37 | public attached(): void {}
38 | public detached(): void {}
39 | public htmlChanged(_: any, __?: any): void {}
40 | public contextChanged(_: any, __?: any): void {}
41 | public tryCompile(): void {}
42 | public cleanUp(): void {}
43 | public compile(_: any): void {}
44 | public dispatchCompiledEvent(_: any): void {}
45 | }
46 |
47 | describe("SutProps", () => {
48 | const sutActual = new (DynamicHtml as any)();
49 | const sutProps = new SutProps();
50 |
51 | it("should have property", () => {
52 | const actualKeys = getAllPropertyNames(sutActual).sort();
53 | const expectedKeys = getAllPropertyNames(sutProps).sort();
54 |
55 | expect(actualKeys).toEqual(expectedKeys);
56 | });
57 | });
58 |
59 | describe("constructor()", () => {
60 | let actualSut: SutProps;
61 | const expectedSut = new SutProps();
62 | const expectedProps = getAllProperties(expectedSut).filter(p => !(p.value instanceof Function));
63 | const expectedPropsCount = expectedProps.length;
64 |
65 | beforeEach(() => {
66 | actualSut = new DynamicHtml(expectedSut.el, expectedSut.tq, expectedSut.container, expectedSut.viewCompiler) as any;
67 | });
68 |
69 | describe("should set property", () => {
70 | for (let i = 0; i < expectedPropsCount; i++) {
71 | it(expectedProps[i].key, () => {
72 | expect(actualSut[expectedProps[i].key]).toEqual(expectedProps[i].value);
73 | });
74 | }
75 | });
76 |
77 | describe("should not leave property undefined", () => {
78 | for (let i = 0; i < expectedPropsCount; i++) {
79 | it(expectedProps[i].key, () => {
80 | expect(actualSut[expectedProps[i].key]).not.toBeUndefined();
81 | });
82 | }
83 | });
84 | });
85 |
86 | describe("bind()", () => {
87 | let sut: SutProps;
88 | let bc: any;
89 | let oc: any;
90 | const props = new SutProps();
91 |
92 | beforeEach(() => {
93 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
94 | bc = {};
95 | oc = {};
96 | });
97 |
98 | describe("should set property", () => {
99 | it("isBound", () => {
100 | sut.bind(bc, oc);
101 | expect(sut.isBound).toBe(true);
102 | });
103 |
104 | it("bindingContext when this.context is null and bindingContext.context is undefined", () => {
105 | sut.bind(bc, oc);
106 | expect(sut.bindingContext).toBe(bc);
107 | });
108 |
109 | it("bindingContext when this.context is null and bindingContext.context is an object", () => {
110 | bc.context = {};
111 | sut.bind(bc, oc);
112 | expect(sut.bindingContext).toBe(bc.context);
113 | });
114 |
115 | it("bindingContext when this.context is an object", () => {
116 | sut.context = {};
117 | delete sut.contextChanged;
118 | sut.bind(bc, oc);
119 | expect(sut.bindingContext).toBe(sut.context);
120 | });
121 | });
122 |
123 | describe("should not set property", () => {
124 | const excludedProps = ["context", "isBound", "bindingContext", "overrideContext", "tq", "viewCompiler"];
125 | const expectedSut = new SutProps();
126 | const expectedProps = getAllProperties(expectedSut).filter(
127 | p => !(p.value instanceof Function) && excludedProps.indexOf(p.key) === -1
128 | );
129 | const expectedPropsCount = expectedProps.length;
130 |
131 | for (let i = 0; i < expectedPropsCount; i++) {
132 | it(expectedProps[i].key, () => {
133 | sut.bind(bc, oc);
134 | expect((sut as any)[expectedProps[i].key]).toEqual((expectedSut as any)[expectedProps[i].key]);
135 | });
136 | }
137 | });
138 | });
139 |
140 | describe("unbind()", () => {
141 | let sut: SutProps;
142 | const props = new SutProps();
143 |
144 | beforeEach(() => {
145 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
146 | });
147 |
148 | describe("should set property", () => {
149 | beforeEach(() => {
150 | sut.bind({}, {});
151 | });
152 |
153 | it("bindingContext", () => {
154 | sut.unbind();
155 | expect(sut.bindingContext).toBeNull();
156 | });
157 |
158 | it("overrideContext", () => {
159 | sut.unbind();
160 | expect(sut.overrideContext).toBeNull();
161 | });
162 |
163 | it("isBound", () => {
164 | sut.unbind();
165 | expect(sut.isBound).toBe(false);
166 | });
167 | });
168 |
169 | describe("should not set property", () => {
170 | const excludedProps = ["isBound", "bindingContext", "overrideContext"];
171 | const expectedSut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
172 | const expectedProps = getAllProperties(expectedSut).filter(
173 | p => !(p.value instanceof Function) && excludedProps.indexOf(p.key) === -1
174 | );
175 | const expectedPropsCount = expectedProps.length;
176 |
177 | beforeEach(() => {
178 | const bc = {};
179 | const oc = {};
180 | sut.bind(bc, oc);
181 | expectedSut.bind(bc, oc);
182 | });
183 |
184 | for (let i = 0; i < expectedPropsCount; i++) {
185 | it(expectedProps[i].key, () => {
186 | sut.unbind();
187 | expect((sut as any)[expectedProps[i].key]).toEqual((expectedSut as any)[expectedProps[i].key]);
188 | });
189 | }
190 | });
191 | });
192 |
193 | describe("attached()", () => {
194 | let sut: SutProps;
195 | const props = new SutProps();
196 |
197 | beforeEach(() => {
198 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
199 | });
200 |
201 | describe("should set property", () => {
202 | it("isAttached", () => {
203 | sut.attached();
204 | expect(sut.isAttached).toBe(true);
205 | });
206 |
207 | it("isInitialized", () => {
208 | sut.attached();
209 | expect(sut.isInitialized).toBe(true);
210 | });
211 |
212 | it("slot", () => {
213 | sut.attached();
214 | expect(sut.slot).toEqual(jasmine.any(ViewSlot));
215 | });
216 | });
217 |
218 | describe("should not set property", () => {
219 | const excludedProps = ["isAttached", "isInitialized", "slot", "tq", "viewCompiler"];
220 | const expectedSut = new SutProps();
221 | const expectedProps = getAllProperties(expectedSut).filter(
222 | p => !(p.value instanceof Function) && excludedProps.indexOf(p.key) === -1
223 | );
224 | const expectedPropsCount = expectedProps.length;
225 |
226 | for (let i = 0; i < expectedPropsCount; i++) {
227 | it(expectedProps[i].key, () => {
228 | sut.attached();
229 | expect((sut as any)[expectedProps[i].key]).toEqual((expectedSut as any)[expectedProps[i].key]);
230 | });
231 | }
232 | });
233 | });
234 |
235 | describe("detached()", () => {
236 | let sut: SutProps;
237 | const props = new SutProps();
238 |
239 | beforeEach(() => {
240 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
241 | sut.attached();
242 | });
243 |
244 | describe("should set property", () => {
245 | it("isAttached", () => {
246 | sut.detached();
247 | expect(sut.isAttached).toBe(false);
248 | });
249 |
250 | it("slot", () => {
251 | sut.detached();
252 | expect(sut.slot).toBeNull();
253 | });
254 | });
255 |
256 | describe("should not set property", () => {
257 | const excludedProps = ["isAttached", "slot"];
258 | const expectedSut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
259 | const expectedProps = getAllProperties(expectedSut).filter(
260 | p => !(p.value instanceof Function) && excludedProps.indexOf(p.key) === -1
261 | );
262 | const expectedPropsCount = expectedProps.length;
263 |
264 | beforeEach(() => {
265 | sut.attached();
266 | expectedSut.attached();
267 | });
268 |
269 | for (let i = 0; i < expectedPropsCount; i++) {
270 | it(expectedProps[i].key, () => {
271 | sut.detached();
272 | expect((sut as any)[expectedProps[i].key]).toEqual((expectedSut as any)[expectedProps[i].key]);
273 | });
274 | }
275 | });
276 | });
277 |
278 | describe("htmlChanged()", () => {
279 | let sut: SutProps;
280 | const props = new SutProps();
281 |
282 | beforeEach(() => {
283 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
284 | });
285 |
286 | const newValueValues = [undefined, null, "", "asdf"];
287 | const isCleanedUpValues = [true, false];
288 | const isBoundValues = [true, false];
289 | const isInitializedValues = [true, false];
290 |
291 | const newValueValuesLength = newValueValues.length;
292 | const isCleanedUpValuesLength = isCleanedUpValues.length;
293 | const isBoundValuesLength = isBoundValues.length;
294 | const isInitializedValuesLength = isInitializedValues.length;
295 |
296 | for (let i = 0; i < newValueValuesLength; i++) {
297 | const newValue = newValueValues[i];
298 |
299 | for (let j = 0; j < isCleanedUpValuesLength; j++) {
300 | const isCleanedUp = isCleanedUpValues[j];
301 |
302 | for (let k = 0; k < isBoundValuesLength; k++) {
303 | const isBound = isBoundValues[k];
304 |
305 | for (let l = 0; l < isInitializedValuesLength; l++) {
306 | const isInitialized = isInitializedValues[l];
307 |
308 | // tslint:disable-next-line:max-line-length
309 | it(`newValue=${!newValue ? typeof newValue : newValue}, isCleanedUp=${isCleanedUp}, isBound=${isBound}, isInitialized=${isInitialized}`, done => {
310 | sut.isCleanedUp = isCleanedUp;
311 | sut.isBound = isBound;
312 | sut.isInitialized = isInitialized;
313 |
314 | const cleanUpSpy = spyOn(sut, "cleanUp").and.callThrough();
315 | const tryCompileSpy = spyOn(sut, "tryCompile").and.callThrough();
316 |
317 | sut.htmlChanged(newValue);
318 |
319 | setTimeout(() => {
320 | if ((newValue === null || newValue === undefined) && isCleanedUp === false) {
321 | expect(cleanUpSpy.calls.count()).toBe(1, "cleanUp() should have been called");
322 | expect(tryCompileSpy.calls.count()).toBe(0, "tryCompile() should NOT have been called");
323 | } else if (isBound === true || isInitialized === true) {
324 | expect(cleanUpSpy.calls.count()).toBe(0, "cleanUp() should NOT have been called");
325 | expect(tryCompileSpy.calls.count()).toBe(1, "tryCompile() should have been called");
326 | } else {
327 | expect(cleanUpSpy.calls.count()).toBe(0, "tryCompile() should NOT have been called");
328 | expect(tryCompileSpy.calls.count()).toBe(0, "tryCompile() should NOT have been called");
329 | }
330 | done();
331 | }, 0);
332 | });
333 | }
334 | }
335 | }
336 | }
337 | });
338 |
339 | describe("contextChanged()", () => {
340 | let sut: SutProps;
341 | const props = new SutProps();
342 |
343 | beforeEach(() => {
344 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
345 | });
346 |
347 | const newValueValues = [undefined, null, {}];
348 | const overrideContextValues = [undefined, null, {bindingContext: {}}];
349 | const isBoundValues = [true, false];
350 | const isInitializedValues = [true, false];
351 |
352 | const newValueValuesLength = newValueValues.length;
353 | const overrideContextValuesLength = overrideContextValues.length;
354 | const isBoundValuesLength = isBoundValues.length;
355 | const isInitializedValuesLength = isInitializedValues.length;
356 |
357 | for (let i = 0; i < newValueValuesLength; i++) {
358 | const newValue = newValueValues[i];
359 |
360 | for (let j = 0; j < overrideContextValuesLength; j++) {
361 | const overrideContext = overrideContextValues[j];
362 |
363 | for (let k = 0; k < isBoundValuesLength; k++) {
364 | const isBound = isBoundValues[k];
365 |
366 | for (let l = 0; l < isInitializedValuesLength; l++) {
367 | const isInitialized = isInitializedValues[l];
368 |
369 | // tslint:disable-next-line:max-line-length
370 | it(`newValue=${typeof newValue}, overrideContext=${typeof overrideContext}, isBound=${isBound}, isInitialized=${isInitialized}`, done => {
371 | sut.overrideContext = overrideContext as any;
372 | sut.isBound = isBound;
373 | sut.isInitialized = isInitialized;
374 |
375 | const tryCompileSpy = spyOn(sut, "tryCompile").and.callThrough();
376 | const initialBindingContext = sut.bindingContext = {};
377 |
378 | sut.contextChanged(newValue);
379 |
380 | setTimeout(() => {
381 | if (newValue === null || newValue === undefined) {
382 | if (!!overrideContext) {
383 | expect(sut.bindingContext).toBe(overrideContext.bindingContext, "bindingContext should have been set to overrideContext.bindingContext");
384 | } else {
385 | expect(sut.bindingContext).toBe(newValue as any, "bindingContext should have been set to newValue");
386 | }
387 | } else {
388 | expect(sut.bindingContext).toBe(newValue as any, "bindingContext should have been set to newValue");
389 | }
390 | expect(sut.bindingContext).not.toBe(initialBindingContext, "bindingContext should never be the same as its original value");
391 | if (isBound === true || isInitialized === true) {
392 | expect(tryCompileSpy.calls.count()).toBe(1, "tryCompile() should have been called");
393 | } else {
394 | expect(tryCompileSpy.calls.count()).toBe(0, "tryCompile() should NOT have been called");
395 | }
396 | done();
397 | }, 0);
398 | });
399 | }
400 | }
401 | }
402 | }
403 | });
404 |
405 | describe("tryCompile()", () => {
406 | let sut: SutProps;
407 | const props = new SutProps();
408 |
409 | beforeEach(() => {
410 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
411 | });
412 |
413 | const isAttachedValues = [true, false];
414 | const isCleanedUpValues = [true, false];
415 |
416 | const isAttachedValuesLength = isAttachedValues.length;
417 | const isCleanedUpValuesLength = isCleanedUpValues.length;
418 |
419 | for (let i = 0; i < isAttachedValuesLength; i++) {
420 | const isAttached = isAttachedValues[i];
421 |
422 | for (let j = 0; j < isCleanedUpValuesLength; j++) {
423 | const isCleanedUp = isCleanedUpValues[j];
424 |
425 | // tslint:disable-next-line:max-line-length
426 | it(`isAttached=${isAttached}, isCleanedUp=${isCleanedUp}`, done => {
427 | sut.isAttached = isAttached;
428 | sut.isCleanedUp = isCleanedUp;
429 |
430 | const cleanUpSpy = spyOn(sut, "cleanUp").and.callThrough();
431 |
432 | sut.tryCompile();
433 |
434 | setTimeout(() => {
435 | if (isAttached === true && isCleanedUp === false) {
436 | expect(cleanUpSpy.calls.count()).toBe(1, "cleanUp() should have been called");
437 | } else {
438 | expect(cleanUpSpy.calls.count()).toBe(0, "cleanUp() should NOT have been called");
439 | }
440 | done();
441 | }, 0);
442 | });
443 | }
444 | }
445 | });
446 |
447 | describe("cleanUp()", () => {
448 | let sut: any;
449 | const props = new SutProps();
450 |
451 | beforeEach(() => {
452 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
453 | });
454 |
455 | it("should always call slot.detached()", () => {
456 | sut.slot = jasmine.createSpyObj(["detached"]);
457 | sut.cleanUp();
458 | expect(sut.slot.detached.calls.count()).toBe(1);
459 | });
460 |
461 | it("should always call slot.unbind()", () => {
462 | sut.slot = jasmine.createSpyObj(["unbind"]);
463 | sut.cleanUp();
464 | expect(sut.slot.unbind.calls.count()).toBe(1);
465 | });
466 |
467 | it("should always call slot.removeAll()", () => {
468 | sut.slot = jasmine.createSpyObj(["removeAll"]);
469 | sut.cleanUp();
470 | expect(sut.slot.removeAll.calls.count()).toBe(1);
471 | });
472 |
473 | it("should always set isCompiled to false", () => {
474 | sut.isCompiled = true;
475 | sut.cleanUp();
476 | expect(sut.isCompiled).toBe(false);
477 | });
478 |
479 | it("should always set isCleanedUp to true", () => {
480 | sut.isCleanedUp = false;
481 | sut.cleanUp();
482 | expect(sut.isCleanedUp).toBe(true);
483 | });
484 | });
485 |
486 | describe("compile()", () => {
487 | let sut: any;
488 | const props = new SutProps();
489 |
490 | beforeEach(() => {
491 | sut = new DynamicHtml(props.el, props.tq, props.container, props.viewCompiler) as any;
492 | });
493 |
494 | it("should call cleanUp() when isCleanedUp is false", done => {
495 | sut.slot = jasmine.createSpyObj(["add", "bind", "attached"]);
496 | sut.isCleanedUp = false;
497 |
498 | const cleanUpSpy = spyOn(sut, "cleanUp").and.callThrough();
499 |
500 | sut.compile();
501 |
502 | setTimeout(() => {
503 | expect(cleanUpSpy.calls.count()).toBe(1);
504 | done();
505 | }, 0);
506 | });
507 |
508 | it("should NOT call cleanUp() when isCleanedUp is true", done => {
509 | sut.slot = jasmine.createSpyObj(["add", "bind", "attached"]);
510 | sut.isCleanedUp = true;
511 |
512 | const cleanUpSpy = spyOn(sut, "cleanUp").and.callThrough();
513 |
514 | sut.compile();
515 |
516 | setTimeout(() => {
517 | expect(cleanUpSpy.calls.count()).toBe(0);
518 | done();
519 | }, 0);
520 | });
521 |
522 | it("should always set isCleanedUp to false if html has a value", () => {
523 | sut.isCleanedUp = true;
524 | delete sut.htmlChanged;
525 | sut.html = "";
526 |
527 | try {
528 | sut.compile();
529 | } catch (e) { }
530 |
531 | expect(sut.isCleanedUp).toBe(false);
532 | });
533 |
534 | it("should not proceed with compilation if html is null", () => {
535 | sut.isCleanedUp = true;
536 | delete sut.htmlChanged;
537 | sut.html = null;
538 |
539 | sut.compile();
540 |
541 | expect(sut.isCleanedUp).toBe(true);
542 | });
543 |
544 | it("should not proceed with compilation if html is undefined", () => {
545 | sut.isCleanedUp = true;
546 | delete sut.htmlChanged;
547 | sut.html = undefined;
548 |
549 | sut.compile();
550 |
551 | expect(sut.isCleanedUp).toBe(true);
552 | });
553 |
554 | it("should proceed with compilation if html has a value", () => {
555 | sut.isCleanedUp = true;
556 | delete sut.htmlChanged;
557 | sut.html = "";
558 | spyOn(sut.container, "get").and.callFake(new Function());
559 | spyOn(sut.container, "createChild").and.callFake(new Function());
560 | const mockFactory = jasmine.createSpyObj(["create"]);
561 | spyOn(sut.viewCompiler, "compile").and.returnValue(mockFactory);
562 | sut.slot = jasmine.createSpyObj(["add", "bind", "attached"]);
563 |
564 | sut.compile();
565 |
566 | expect(sut.container.get.calls.count()).toBe(1);
567 | expect(sut.container.createChild.calls.count()).toBe(1);
568 | expect(mockFactory.create.calls.count()).toBe(1);
569 | expect(sut.viewCompiler.compile.calls.count()).toBe(1);
570 | expect(sut.slot.add.calls.count()).toBe(1);
571 | expect(sut.slot.bind.calls.count()).toBe(1);
572 | expect(sut.slot.attached.calls.count()).toBe(1);
573 |
574 | expect(sut.isCompiled).toBe(true);
575 | });
576 | });
577 |
--------------------------------------------------------------------------------
/test/unit/util.ts:
--------------------------------------------------------------------------------
1 | export function getAllProperties(obj: T): { key: keyof T; value: T[keyof T] }[] {
2 | return (getAllPropertyNames(obj) as (keyof T)[]).map(key => ({ key, value: obj[key] }));
3 | }
4 |
5 | export function getAllPropertyNames(obj: any): string[] {
6 | const allNames = ["__metadata__"];
7 | let proto = obj;
8 | for (; proto !== Object.prototype; proto = Object.getPrototypeOf(proto)) {
9 | const ownNames = Object.getOwnPropertyNames(proto);
10 | const length = ownNames.length;
11 | for (let i = 0; i < length; i++) {
12 | const propertyName = ownNames[i];
13 | if (allNames.indexOf(propertyName) === -1) {
14 | allNames.push(propertyName);
15 | }
16 | }
17 | }
18 |
19 | return allNames.slice(1);
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./configs/tsconfig-projectroot.json"
3 | }
4 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | /**
4 | * Security Rules. The following rules should be turned on because they find security issues
5 | * or are recommended in the Microsoft Secure Development Lifecycle (SDL)
6 | */
7 | "insecure-random": true,
8 | "no-banned-terms": true,
9 | "no-cookies": true,
10 | "no-delete-expression": true,
11 | "no-disable-auto-sanitization": true,
12 | "no-document-domain": true,
13 | "no-document-write": true,
14 | "no-eval": true,
15 | "no-exec-script": true,
16 | "no-function-constructor-with-string-args": true,
17 | "no-http-string": [true, "http://www.example.com/?.*", "http://www.examples.com/?.*"],
18 | "no-inner-html": true,
19 | "no-octal-literal": true,
20 | "no-reserved-keywords": true,
21 | "no-string-based-set-immediate": true,
22 | "no-string-based-set-interval": true,
23 | "no-string-based-set-timeout": true,
24 | "non-literal-require": true,
25 | "possible-timing-attack": true,
26 | "react-anchor-blank-noopener": true,
27 | "react-iframe-missing-sandbox": true,
28 | "react-no-dangerous-html": true,
29 |
30 | /**
31 | * Common Bugs and Correctness. The following rules should be turned on because they find
32 | * common bug patterns in the code or enforce type safety.
33 | */
34 | "await-promise": true,
35 | "forin": true,
36 | "jquery-deferred-must-complete": true,
37 | "label-position": true,
38 | "match-default-export-name": true,
39 | "mocha-avoid-only": true,
40 | "mocha-no-side-effect-code": true,
41 | "no-any": false, // fkleuver: we often need to use external libraries with incomplete/outdated type definitions, and casting "as any" is the only reasonable way to work around type-checking errors
42 | "no-arg": true,
43 | "no-backbone-get-set-outside-model": true,
44 | "no-bitwise": false, // fkleuver: we need bitwise and we know what we're doing..
45 | "no-conditional-assignment": true,
46 | "no-console": [true, "debug", "info", "log", "time", "timeEnd", "trace"],
47 | "no-constant-condition": true,
48 | "no-control-regex": true,
49 | "no-debugger": true,
50 | "no-duplicate-super": true,
51 | "no-duplicate-switch-case": true,
52 | "no-duplicate-variable": true,
53 | "no-empty": true,
54 | "no-floating-promises": true,
55 | "no-for-in-array": true,
56 | "no-implicit-dependencies": false, // fkleuver: we can't make internal dependencies explicit as far as I know
57 | "no-import-side-effect": true,
58 | "no-increment-decrement": false, // fkleuver: this is a well-known, well-documented shorthand which is present in (and works consistently across) multiple strongly-typed languages (C#, Java..), and in many cases improves readability of code, so let's not disallow this
59 | "no-invalid-regexp": true,
60 | "no-invalid-template-strings": true,
61 | "no-invalid-this": true,
62 | "no-jquery-raw-elements": true,
63 | "no-misused-new": true,
64 | "no-non-null-assertion": true,
65 | "no-object-literal-type-assertion": true,
66 | "no-parameter-reassignment": true,
67 | "no-reference-import": true,
68 | "no-regex-spaces": true,
69 | "no-sparse-arrays": true,
70 | "no-string-literal": true,
71 | "no-string-throw": true,
72 | "no-submodule-imports": false, // fkleuver: this will also warn on importing internal non-relative modules and that's not worth the hassle
73 | "no-unnecessary-bind": true,
74 | "no-unnecessary-callback-wrapper": true,
75 | "no-unnecessary-initializer": true,
76 | "no-unnecessary-override": true,
77 | "no-unsafe-any": false, // fkleuver: checking "any" arguments for null or undefined should be up to the author, as in many cases this leads to redundant checks
78 | "no-unsafe-finally": true,
79 | "no-unused-expression": true,
80 | "no-use-before-declare": false, // fkleuver: only useful when using the "var" keyword which we really never use anymore in TypeScript, and slow to compute, so let's turn it off
81 | "no-with-statement": true,
82 | "promise-function-async": true,
83 | "promise-must-complete": true,
84 | "radix": true,
85 | "react-this-binding-issue": true,
86 | "react-unused-props-and-state": true,
87 | "restrict-plus-operands": true, // the plus operand should really only be used for strings and numbers
88 | "strict-boolean-expressions": [
89 | true,
90 | "allow-null-union",
91 | "allow-undefined-union",
92 | "allow-string",
93 | "allow-number",
94 | "allow-mix"
95 | ], // fkleuver: allowing these is a productivity tradeoff between time spent looking for bugs caused by unintentional uses, and time saved by writing shorter code
96 | "switch-default": true,
97 | "switch-final-break": true,
98 | "triple-equals": [true, "allow-null-check"],
99 | "use-isnan": true,
100 | "use-named-parameter": true,
101 |
102 | /**
103 | * Code Clarity. The following rules should be turned on because they make the code
104 | * generally more clear to the reader.
105 | */
106 | "adjacent-overload-signatures": true,
107 | "array-type": [true, "array"],
108 | "arrow-parens": false, // for simple functions the parens on arrow functions are not needed
109 | "ban-comma-operator": true, // possibly controversial
110 | "binary-expression-operand-order": true,
111 | "callable-types": true,
112 | "chai-prefer-contains-to-index-of": true,
113 | "chai-vague-errors": true,
114 | "class-name": true,
115 | "comment-format": true,
116 | "completed-docs": [true, "classes"],
117 | "export-name": false, // fkleuver: impractical for aurelia projects due to naming conventions for exported html resources, features, etc
118 | "function-name": true,
119 | "import-name": true,
120 | "interface-name": true,
121 | "jsdoc-format": true,
122 | "max-classes-per-file": [true, 3], // we generally recommend making one public class per file
123 | "max-file-line-count": true,
124 | "max-func-body-length": [true, 100, { "ignore-parameters-to-function-regex": "describe" }],
125 | "max-line-length": [true, 140],
126 | "member-access": true,
127 | "member-ordering": [true, { "order": "fields-first" }],
128 | "missing-jsdoc": false, // fkleuver: we don't want to enforce documentation on every single file (due to aurelia conventions being fairly self-explanatory), furthermore, for internal projects code ought to be written in such a way that it documents itself, whilst applying common sense to document critical and/or complex parts of the application
129 | "mocha-unneeded-done": true,
130 | "new-parens": true,
131 | "no-construct": true,
132 | "no-default-export": false, // fkleuver: common practise in many aurelia projects including the generated output from aurelia-cli (environment.ts), so we're leaving this off to prevent nagging just about every aurelia-cli project
133 | "no-duplicate-imports": true,
134 | "no-empty-interface": true,
135 | "no-for-in": true,
136 | "no-function-expression": true,
137 | "no-inferrable-types": false, // turn no-inferrable-types off in order to make the code consistent in its use of type decorations
138 | "no-multiline-string": true, // multiline-strings often introduce unnecessary whitespace into the string literals
139 | "no-null-keyword": false, // turn no-null-keyword off and use undefined to mean not initialized and null to mean without a value
140 | "no-parameter-properties": true,
141 | "no-redundant-jsdoc": true,
142 | "no-relative-imports": false, // fkleuver: relative imports are a standard practise in the vast majority of aurelia projects and examples; I believe that the benefits of consistency between aurelia projects outweigh the benefits of consistency between typescript projects
143 | "no-require-imports": true,
144 | "no-return-await": true,
145 | "no-shadowed-variable": true,
146 | "no-suspicious-comment": true,
147 | "no-this-assignment": true,
148 | "no-typeof-undefined": true,
149 | "no-unnecessary-class": false, // fkleuver: tslint is kind of bad at deciding when a class is "unnecessary"
150 | "no-unnecessary-field-initialization": true,
151 | "no-unnecessary-local-variable": true,
152 | "no-unnecessary-qualifier": true,
153 | "no-unnecessary-type-assertion": true,
154 | "no-unsupported-browser-code": true,
155 | "no-useless-files": true,
156 | "no-var-keyword": true,
157 | "no-var-requires": true,
158 | "no-void-expression": true,
159 | "number-literal-format": true,
160 | "object-literal-sort-keys": false, // turn object-literal-sort-keys off and sort keys in a meaningful manner
161 | "one-variable-per-declaration": true,
162 | "only-arrow-functions": false, // there are many valid reasons to declare a function
163 | "ordered-imports": true,
164 | "prefer-array-literal": true,
165 | "prefer-const": true,
166 | "prefer-for-of": true,
167 | "prefer-method-signature": true,
168 | "prefer-object-spread": true,
169 | "prefer-template": true,
170 | "type-literal-delimiter": true,
171 | "typedef": [
172 | true,
173 | "call-signature",
174 | //"arrow-call-signature", // fkleuver: this can lead to incredibly verbose code that could otherwise be very succinct; leave this up to the developer where it makes sense
175 | "parameter",
176 | //"arrow-parameter", // fkleuver: this can lead to incredibly verbose code that could otherwise be very succinct; leave this up to the developer where it makes sense
177 | "property-declaration",
178 | //"variable-declaration", // fkleuver: just like with "var" in C#, a variable without a typedef must be directly initialized (the no-implicit-any rule will ensure this); requiring a typedef in those cases leads to redundancy in type keywords and, ultimately, less readable code
179 | "member-variable-declaration"
180 | ],
181 | "underscore-consistent-invocation": true,
182 | "unified-signatures": true,
183 | "use-default-type-parameter": true,
184 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"], // fkleuver: allowing leading underscore allows for pragmatic enforcement of compiler rule "no-unused-locals"
185 |
186 | /**
187 | * Accessibility. The following rules should be turned on to guarantee the best user
188 | * experience for keyboard and screen reader users.
189 | */
190 | "react-a11y-anchors": true,
191 | "react-a11y-aria-unsupported-elements": true,
192 | "react-a11y-event-has-role": true,
193 | "react-a11y-image-button-has-alt": true,
194 | "react-a11y-img-has-alt": true,
195 | "react-a11y-lang": true,
196 | "react-a11y-meta": true,
197 | "react-a11y-props": true,
198 | "react-a11y-proptypes": true,
199 | "react-a11y-role": true,
200 | "react-a11y-role-has-required-aria-props": true,
201 | "react-a11y-role-supports-aria-props": true,
202 | "react-a11y-tabindex-no-positive": true,
203 | "react-a11y-titles": true,
204 |
205 | /**
206 | * Whitespace related rules. The only recommended whitespace strategy is to pick a single format and
207 | * be consistent.
208 | */
209 | "align": [true, "parameters", "arguments", "statements"],
210 | "curly": true,
211 | "encoding": true,
212 | "eofline": true,
213 | "import-spacing": true,
214 | "indent": [true, "spaces"],
215 | "linebreak-style": true,
216 | "newline-before-return": true,
217 | "no-consecutive-blank-lines": true,
218 | "no-empty-line-after-opening-brace": false,
219 | "no-irregular-whitespace": true,
220 | "no-single-line-block-comment": true,
221 | "no-trailing-whitespace": true,
222 | "no-unnecessary-semicolons": true,
223 | "object-literal-key-quotes": [true, "consistent"], // fkleuver: either all or none, makes for more readable (and pleasant to look at) code
224 | "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"],
225 | "quotemark": [true, "double"],
226 | "semicolon": [true, "always"],
227 | "space-within-parens": true,
228 | "trailing-comma": [true, { "singleline": "never", "multiline": "never" }], // forcing trailing commas for multi-line
229 | // lists results in lists that are easier to reorder and version control diffs that are more clear.
230 | // Many teams like to have multiline be 'always'. There is no clear consensus on this rule but the
231 | // internal MS JavaScript coding standard does discourage it.
232 | "typedef-whitespace": false,
233 | "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"],
234 |
235 | /**
236 | * Controversial/Configurable rules.
237 | */
238 | "ban": false, // only enable this if you have some code pattern that you want to ban
239 | "ban-types": true,
240 | "cyclomatic-complexity": true,
241 | "deprecation": false, // deprecated APIs are sometimes unavoidable
242 | "file-header": false, // enable this rule only if you are legally required to add a file header
243 | "import-blacklist": false, // enable and configure this as you desire
244 | "interface-over-type-literal": false, // there are plenty of reasons to prefer interfaces
245 | "no-angle-bracket-type-assertion": false, // pick either type-cast format and use it consistently
246 | "no-inferred-empty-object-type": false, // if the compiler is satisfied then this is probably not an issue
247 | "no-internal-module": false, // only enable this if you are not using internal modules
248 | "no-magic-numbers": false, // by default it will find too many false positives
249 | "no-mergeable-namespace": false, // your project may require mergeable namespaces
250 | "no-namespace": false, // only enable this if you are not using modules/namespaces
251 | "no-reference": true, // in general you should use a module system and not /// reference imports
252 | "no-unexternalized-strings": false, // the VS Code team has a specific localization process that this rule enforces
253 | "object-literal-shorthand": false, // object-literal-shorthand offers an abbreviation not an abstraction
254 | "prefer-conditional-expression": false, // unnecessarily strict
255 | "prefer-switch": false, // more of a style preference
256 | "prefer-type-cast": false, // pick either type-cast format and use it consistently // fkleuver: as-cast seems to be the most popular flavor at the moment, and I suppose with a purely style preference thing such as this, it's best to just go mainstream
257 | "return-undefined": false, // this actually affect the readability of the code
258 | "space-before-function-paren": false, // turn this on if this is really your coding standard
259 |
260 | /**
261 | * Deprecated rules. The following rules are deprecated for various reasons.
262 | */
263 | "missing-optional-annotation": false, // now supported by TypeScript compiler
264 | //"no-duplicate-case": true, // fkleuver: deprecated and replaced with no-duplicate-switch-case
265 | "no-duplicate-parameter-names": false, // now supported by TypeScript compiler
266 | "no-empty-interfaces": false, // use tslint no-empty-interface rule instead
267 | "no-missing-visibility-modifiers": false, // use tslint member-access rule instead
268 | "no-multiple-var-decl": false, // use tslint one-variable-per-declaration rule instead
269 | //"no-stateless-class": true, // fkleuver: deprecated and replaced with no-unnecessary-class
270 | "no-switch-case-fall-through": false, // now supported by TypeScript compiler
271 | //"no-var-self": true, // fkleuver: deprecated and replaced with no-this-assignment
272 | "react-tsx-curly-spacing": true,
273 | "typeof-compare": false, // the valid-typeof rule is currently superior to this version
274 | "valid-typeof": true
275 | },
276 | "rulesDirectory": "node_modules/tslint-microsoft-contrib",
277 | "defaultSeverity": "warning"
278 | }
279 |
--------------------------------------------------------------------------------
/webpack.config.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable:no-implicit-dependencies
2 | // tslint:disable:import-name
3 | import { AureliaPlugin, ModuleDependenciesPlugin } from "aurelia-webpack-plugin";
4 | import { readFileSync } from "fs";
5 | import HtmlWebpackPlugin from "html-webpack-plugin";
6 | import MonacoWebpackPlugin from "monaco-editor-webpack-plugin";
7 | import * as path from "path";
8 | import * as webpack from "webpack";
9 |
10 | const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
11 |
12 | const title = "Aurelia Dynamic HTML Plugin";
13 | interface IEnv {
14 | server?: boolean;
15 | production?: boolean;
16 | }
17 |
18 | const devBaseUrl: string = "/";
19 | const prodBaseUrl: string = `/${pkg.name}/`;
20 | export default (env: IEnv = {}): webpack.Configuration => {
21 | const alias = {
22 | "bluebird": path.resolve(__dirname, "node_modules/bluebird/js/browser/bluebird.core")
23 | };
24 |
25 | return {
26 | mode: "development",
27 | resolve: {
28 | extensions: [".ts", ".js"],
29 | modules: [path.resolve(__dirname, "src"), path.resolve(__dirname, "demo"), "node_modules"],
30 | alias
31 | },
32 | entry: {
33 | app: ["aurelia-bootstrapper"],
34 | vendor: ["bluebird"]
35 | },
36 | output: {
37 | path: path.resolve(__dirname),
38 | publicPath: env.production ? prodBaseUrl : devBaseUrl,
39 | filename: "[name].bundle.js"
40 | },
41 | devtool: "cheap-module-eval-source-map",
42 | devServer: {
43 | historyApiFallback: true,
44 | lazy: false,
45 | open: true
46 | },
47 | module: {
48 | rules: [
49 | {
50 | test: /\.css$/,
51 | use: [{ loader: "style-loader" }, { loader: "css-loader" }],
52 | issuer: [{ not: [{ test: /\.html$/i }] }]
53 | },
54 | {
55 | test: /\.css$/,
56 | use: [{ loader: "css-loader" }],
57 | issuer: [{ test: /\.html$/i }]
58 | },
59 | {
60 | test: /\.html$/,
61 | use: [{ loader: "html-loader" }]
62 | },
63 | {
64 | test: /\.ts$/,
65 | loader: "ts-loader",
66 | exclude: /node_modules/,
67 | options: {
68 | configFile: path.resolve(__dirname, "configs/tsconfig-demo.json")
69 | }
70 | },
71 | {
72 | test: /[\/\\]node_modules[\/\\]bluebird[\/\\].+\.js$/,
73 | use: [{ loader: "expose-loader?Promise" }]
74 | }
75 | ]
76 | },
77 | plugins: [
78 | new HtmlWebpackPlugin({
79 | template: "demo/index.ejs",
80 | metadata: {
81 | title,
82 | server: env.server,
83 | baseUrl: env.production ? prodBaseUrl : devBaseUrl
84 | }
85 | }),
86 | new AureliaPlugin(),
87 | new webpack.ProvidePlugin({
88 | Promise: "bluebird"
89 | }),
90 | new MonacoWebpackPlugin(),
91 | new webpack.IgnorePlugin(
92 | /^((fs)|(path)|(os)|(crypto)|(source-map-support))$/,
93 | /vs(\/|\\)language(\/|\\)typescript(\/|\\)lib/
94 | )
95 | ]
96 | };
97 | };
98 |
--------------------------------------------------------------------------------