##// END OF EJS Templates
fixed tslint errors, added support for private methods to @on() decorator
cin -
r79:e5bb5e80ce96 v1.2.3 default
parent child
Show More
@@ -0,0 +1,25
1 {
2 "env": {
3 "browser": true,
4 "amd": true
5 },
6 "parserOptions": {
7 "ecmaFeatures": {
8 "jsx": true
9 },
10 "sourceType": "script"
11 },
12 "extends": "eslint:recommended",
13 "rules": {
14 "no-const-assign": "warn",
15 "no-this-before-super": "warn",
16 "no-undef": "error",
17 "no-unreachable": "warn",
18 "no-unused-vars": "warn",
19 "constructor-super": "warn",
20 "valid-typeof": "warn",
21 "semi" : "warn",
22 "no-invalid-this" : "error",
23 "no-console": "off"
24 }
25 } No newline at end of file
@@ -0,0 +1,48
1 {
2 "extends": "tslint:recommended",
3 "defaultSeverity": "warn",
4 "rules": {
5 "align": [
6 true,
7 "parameters",
8 "statements"
9 ],
10 "interface-name": [
11 false
12 ],
13 "max-line-length": [
14 true,
15 185
16 ],
17 "quotemark": [true, "double", "avoid-escape"],
18 "member-access": false,
19 "semicolon": [true, "always", "ignore-bound-class-methods"],
20 "no-bitwise": false,
21 "no-empty": false,
22 "no-namespace": false,
23 "ordered-imports": false,
24 "no-return-await": true,
25 "no-floating-promises": true,
26 "one-line": [
27 true,
28 "check-open-brace",
29 "check-catch",
30 "check-whitespace"
31 ],
32 "object-literal-sort-keys": false,
33 "trailing-comma": [
34 true,
35 {
36 "singleline": "never",
37 "multiline": "never"
38 }
39 ],
40 "variable-name": false,
41 "curly": false,
42 "array-type": false,
43 "arrow-parens": [
44 true,
45 "ban-single-arg-parens"
46 ]
47 }
48 } No newline at end of file
@@ -1,77 +1,78
1 plugins {
1 plugins {
2 id "org.implab.gradle-typescript" version "1.3.3"
2 id "org.implab.gradle-typescript" version "1.3.3"
3 id "ivy-publish"
3 id "ivy-publish"
4 }
4 }
5
5
6 typescript {
6 typescript {
7 compilerOptions {
7 compilerOptions {
8 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
8 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
9 //listFiles = true
9 //listFiles = true
10 declaration = true
10 declaration = true
11 strict = true
11 strict = true
12 types = []
12 types = []
13 module = "amd"
13 module = "amd"
14 it.target = "es5"
14 it.target = "es5"
15 experimentalDecorators = true
15 experimentalDecorators = true
16 noUnusedLocals = true
16 jsx = "react"
17 jsx = "react"
17 jsxFactory = "createElement"
18 jsxFactory = "createElement"
18 moduleResolution = "node"
19 moduleResolution = "node"
19 // dojo-typings are sick
20 // dojo-typings are sick
20 skipLibCheck = true
21 skipLibCheck = true
21 // traceResolution = true
22 // traceResolution = true
22 // baseUrl = "./"
23 // baseUrl = "./"
23 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
24 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
24 // baseUrl = "$projectDir/src/typings"
25 // baseUrl = "$projectDir/src/typings"
25 // typeRoots = ["$projectDir/src/typings"]
26 // typeRoots = ["$projectDir/src/typings"]
26 }
27 }
27
28
28 tscCmd = "$projectDir/node_modules/.bin/tsc"
29 tscCmd = "$projectDir/node_modules/.bin/tsc"
29 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
30 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
30 esLintCmd = "$projectDir/node_modules/.bin/eslint"
31 esLintCmd = "$projectDir/node_modules/.bin/eslint"
31 }
32 }
32
33
33 configureTsMain {
34 configureTsMain {
34 sourceFiles {
35 sourceFiles {
35 from sources.main.typings
36 from sources.main.typings
36 }
37 }
37 compilerOptions {
38 compilerOptions {
38 // baseUrl = "$projectDir/src"
39 // baseUrl = "$projectDir/src"
39 /*paths = [
40 /*paths = [
40 "dojo/*" : [ "typings/dojo/*" ],
41 "dojo/*" : [ "typings/dojo/*" ],
41 "dijit/*" : [ "typings/dijit/*" ]
42 "dijit/*" : [ "typings/dijit/*" ]
42 ]*/
43 ]*/
43 types = ["requirejs", "@implab/dojo-typings"]
44 types = ["requirejs", "@implab/dojo-typings"]
44 }
45 }
45 }
46 }
46
47
47 configureTsTest {
48 configureTsTest {
48 compilerOptions {
49 compilerOptions {
49 typeRoots = []
50 typeRoots = []
50 types = ["requirejs", sources.main.output.typingsDir.get().toString() ]
51 types = ["requirejs", sources.main.output.typingsDir.get().toString() ]
51 }
52 }
52 }
53 }
53
54
54 npmPackMeta {
55 npmPackMeta {
55 meta {
56 meta {
56 name = "@$npmScope/$project.name"
57 name = "@$npmScope/$project.name"
57 }
58 }
58 }
59 }
59
60
60 task npmPackTypings(type: Copy) {
61 task npmPackTypings(type: Copy) {
61 dependsOn typings
62 dependsOn typings
62
63
63 npmPackContents.dependsOn it
64 npmPackContents.dependsOn it
64
65
65 from typescript.typingsDir
66 from typescript.typingsDir
66 into npm.packageDir
67 into npm.packageDir
67 }
68 }
68
69
69 task printVersion {
70 task printVersion {
70 doLast {
71 doLast {
71 println "packageName: ${npmPackMeta.metadata.get().name}";
72 println "packageName: ${npmPackMeta.metadata.get().name}";
72 println "version: $version";
73 println "version: $version";
73 println "target: $typescript.compilerOptions.target";
74 println "target: $typescript.compilerOptions.target";
74 println "module: $typescript.compilerOptions.module";
75 println "module: $typescript.compilerOptions.module";
75 println "symbols: $symbols";
76 println "symbols: $symbols";
76 }
77 }
77 } No newline at end of file
78 }
@@ -1,81 +1,73
1 import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces";
1 import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces";
2 import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe";
2 import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe";
3 import { id as mid } from "module";
4 import { TraceSource } from "@implab/core-amd/log/TraceSource";
5
6 const trace = TraceSource.get(mid);
7
3
8 export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>;
4 export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>;
9
5
10 type ResolveCallback<T> = () => PromiseOrValue<T>;
6 type ResolveCallback<T> = () => PromiseOrValue<T>;
11
7
12 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
8 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
13 return isPromise(value) ?
9 return isPromise(value) ?
14 value.then(cb) :
10 value.then(cb) :
15 cb(value);
11 cb(value);
16 }
12 }
17
13
18 function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> {
14 function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> {
19 return typeof v === "function";
15 return typeof v === "function";
20 }
16 }
21
17
22 function defaultResolver(module: string) {
23 return import(module).then(x => x && x.default ? x.default : x);
24 }
25
26 function chainObjects<T extends object>(o1: T, o2: T) {
18 function chainObjects<T extends object>(o1: T, o2: T) {
27 if (!o1)
19 if (!o1)
28 return o2;
20 return o2;
29 if (!o2)
21 if (!o2)
30 return o1;
22 return o1;
31
23
32 return mixin(Object.create(o1) as T, o2);
24 return mixin(Object.create(o1) as T, o2);
33 }
25 }
34
26
35 export class NlsBundle<T extends object> {
27 export class NlsBundle<T extends object> {
36 private _locales: MapOf<LocaleProvider<Partial<T>>>;
28 private _locales: MapOf<LocaleProvider<Partial<T>>>;
37
29
38 private _default: T;
30 private _default: T;
39
31
40 private _cache: MapOf<PromiseOrValue<T>>;
32 private _cache: MapOf<PromiseOrValue<T>>;
41
33
42 constructor(defNls: T, locales?: MapOf<LocaleProvider<Partial<T>>>) {
34 constructor(defNls: T, locales?: MapOf<LocaleProvider<Partial<T>>>) {
43 this._default = defNls;
35 this._default = defNls;
44 this._locales = locales || {};
36 this._locales = locales || {};
45 this._cache = {};
37 this._cache = {};
46 }
38 }
47
39
48 getLocale(locale: string) {
40 getLocale(locale: string) {
49 argumentNotEmptyString(locale, "locale");
41 argumentNotEmptyString(locale, "locale");
50 const _loc = locale;
42 const _loc = locale;
51
43
52 // en-US => ["en", "en-US"]
44 // en-US => ["en", "en-US"]
53 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
45 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
54 return this._resolveLocale(locales);
46 return this._resolveLocale(locales);
55 }
47 }
56
48
57 _resolveLocale(locales: string[]): PromiseOrValue<T> {
49 _resolveLocale(locales: string[]): PromiseOrValue<T> {
58 if (!locales.length)
50 if (!locales.length)
59 return this._default;
51 return this._default;
60
52
61 const locale = locales.pop();
53 const locale = locales.pop();
62 if (!locale)
54 if (!locale)
63 throw new Error("The locale can't be empty");
55 throw new Error("The locale can't be empty");
64
56
65 if (this._cache[locale])
57 if (this._cache[locale])
66 return this._cache[locale];
58 return this._cache[locale];
67
59
68 const data = this._loadPackage(this._locales[locale]);
60 const data = this._loadPackage(this._locales[locale]);
69 const parent = this._resolveLocale(locales);
61 const parent = this._resolveLocale(locales);
70
62
71 return this._cache[locale] = when(data, x => {
63 return this._cache[locale] = when(data, x => {
72 return when(parent, y => this._cache[locale] = chainObjects(y, x));
64 return when(parent, y => this._cache[locale] = chainObjects(y, x));
73 });
65 });
74 }
66 }
75
67
76 _loadPackage(localeData: any) {
68 _loadPackage(localeData: any) {
77 if (isCallback(localeData))
69 if (isCallback(localeData))
78 return when(localeData(), data => data && "default" in data ? data.default : data);
70 return when(localeData(), data => data && "default" in data ? data.default : data);
79 return localeData;
71 return localeData;
80 }
72 }
81 }
73 }
@@ -1,256 +1,253
1 import declare = require("dojo/_base/declare");
1 import declare = require("dojo/_base/declare");
2 import { each } from "@implab/core-amd/safe";
2 import { each } from "@implab/core-amd/safe";
3 import { Constructor } from "@implab/core-amd/interfaces";
3 import { Constructor } from "@implab/core-amd/interfaces";
4 import dojo = require("dojo/_base/kernel");
5
4
6 // declare const declare: any;
5 // declare const declare: any;
7
6
8 type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
7 type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
9
8
10 export interface AbstractConstructor<T = {}> {
9 export interface AbstractConstructor<T = {}> {
11 prototype: T;
10 prototype: T;
12 }
11 }
13
12
14 interface DjMockConstructor<T = {}> {
13 interface DjMockConstructor<T = {}> {
15 new(...args: any[]): T;
14 new(...args: any[]): T;
16 mock: boolean;
15 mock: boolean;
17 bases: AbstractConstructor[];
16 bases: AbstractConstructor[];
18 }
17 }
19
18
20 export function djbase<T>(
19 export function djbase<T>(
21 b0?: AbstractConstructor<T>
20 b0?: AbstractConstructor<T>
22 ): DeclareConstructor<T>;
21 ): DeclareConstructor<T>;
23
22
24 export function djbase<T0, T1>(
23 export function djbase<T0, T1>(
25 b0: AbstractConstructor<T0>,
24 b0: AbstractConstructor<T0>,
26 b1: AbstractConstructor<T1>
25 b1: AbstractConstructor<T1>
27 ): DeclareConstructor<T0 & T1>;
26 ): DeclareConstructor<T0 & T1>;
28
27
29 export function djbase<T0, T1, T2>(
28 export function djbase<T0, T1, T2>(
30 b0: AbstractConstructor<T0>,
29 b0: AbstractConstructor<T0>,
31 b1: AbstractConstructor<T1>,
30 b1: AbstractConstructor<T1>,
32 b2: AbstractConstructor<T2>
31 b2: AbstractConstructor<T2>
33 ): DeclareConstructor<T0 & T1 & T2>;
32 ): DeclareConstructor<T0 & T1 & T2>;
34
33
35 export function djbase<T0, T1, T2, T3>(
34 export function djbase<T0, T1, T2, T3>(
36 b0: AbstractConstructor<T0>,
35 b0: AbstractConstructor<T0>,
37 b1: AbstractConstructor<T1>,
36 b1: AbstractConstructor<T1>,
38 b2: AbstractConstructor<T2>,
37 b2: AbstractConstructor<T2>,
39 b3: AbstractConstructor<T3>
38 b3: AbstractConstructor<T3>
40 ): DeclareConstructor<T0 & T1 & T2 & T3>;
39 ): DeclareConstructor<T0 & T1 & T2 & T3>;
41
40
42 export function djbase<T0, T1, T2, T3, T4>(
41 export function djbase<T0, T1, T2, T3, T4>(
43 b0: AbstractConstructor<T0>,
42 b0: AbstractConstructor<T0>,
44 b1: AbstractConstructor<T1>,
43 b1: AbstractConstructor<T1>,
45 b2: AbstractConstructor<T2>,
44 b2: AbstractConstructor<T2>,
46 b3: AbstractConstructor<T3>,
45 b3: AbstractConstructor<T3>,
47 b4: AbstractConstructor<T4>
46 b4: AbstractConstructor<T4>
48 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4>;
47 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4>;
49
48
50 export function djbase<T0, T1, T2, T3, T4, T5>(
49 export function djbase<T0, T1, T2, T3, T4, T5>(
51 b0: AbstractConstructor<T0>,
50 b0: AbstractConstructor<T0>,
52 b1: AbstractConstructor<T1>,
51 b1: AbstractConstructor<T1>,
53 b2: AbstractConstructor<T2>,
52 b2: AbstractConstructor<T2>,
54 b3: AbstractConstructor<T3>,
53 b3: AbstractConstructor<T3>,
55 b4: AbstractConstructor<T4>,
54 b4: AbstractConstructor<T4>,
56 b5: AbstractConstructor<T5>
55 b5: AbstractConstructor<T5>
57 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>;
56 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>;
58
57
59 export function djbase<T0, T1, T2, T3, T4, T5, T6>(
58 export function djbase<T0, T1, T2, T3, T4, T5, T6>(
60 b0: AbstractConstructor<T0>,
59 b0: AbstractConstructor<T0>,
61 b1: AbstractConstructor<T1>,
60 b1: AbstractConstructor<T1>,
62 b2: AbstractConstructor<T2>,
61 b2: AbstractConstructor<T2>,
63 b3: AbstractConstructor<T3>,
62 b3: AbstractConstructor<T3>,
64 b4: AbstractConstructor<T4>,
63 b4: AbstractConstructor<T4>,
65 b5: AbstractConstructor<T5>,
64 b5: AbstractConstructor<T5>,
66 b6: AbstractConstructor<T6>
65 b6: AbstractConstructor<T6>
67 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>;
66 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>;
68
67
69 export function djbase<T0, T1, T2, T3, T4, T5, T6, T7>(
68 export function djbase<T0, T1, T2, T3, T4, T5, T6, T7>(
70 b0: AbstractConstructor<T0>,
69 b0: AbstractConstructor<T0>,
71 b1: AbstractConstructor<T1>,
70 b1: AbstractConstructor<T1>,
72 b2: AbstractConstructor<T2>,
71 b2: AbstractConstructor<T2>,
73 b3: AbstractConstructor<T3>,
72 b3: AbstractConstructor<T3>,
74 b4: AbstractConstructor<T4>,
73 b4: AbstractConstructor<T4>,
75 b5: AbstractConstructor<T5>,
74 b5: AbstractConstructor<T5>,
76 b6: AbstractConstructor<T6>,
75 b6: AbstractConstructor<T6>,
77 b7: AbstractConstructor<T7>
76 b7: AbstractConstructor<T7>
78 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>;
77 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>;
79
78
80 /** Создает конструктор-заглушку из списка базовых классов, используется
79 /** Создает конструктор-заглушку из списка базовых классов, используется
81 * для объявления классов при помощи `dojo/_base/declare`.
80 * для объявления классов при помощи `dojo/_base/declare`.
82 *
81 *
83 * Создает пустой конструктор, с пустым стандартным прототипом, это нужно,
82 * Создает пустой конструктор, с пустым стандартным прототипом, это нужно,
84 * поскольку в унаследованном классе конструктор обязательно должен вызвать
83 * поскольку в унаследованном классе конструктор обязательно должен вызвать
85 * `super(...)`, таким образом он вызовет пустую функцию.
84 * `super(...)`, таким образом он вызовет пустую функцию.
86 *
85 *
87 * Созданный конструктор хранит в себе список базовых классов, который будет
86 * Созданный конструктор хранит в себе список базовых классов, который будет
88 * использован декоратором `djclass`, который вернет класс, объявленный при
87 * использован декоратором `djclass`, который вернет класс, объявленный при
89 * помощи `dojo/_base/declare`.
88 * помощи `dojo/_base/declare`.
90 *
89 *
91 * @param bases список базовых классов, от которых требуется унаследовать
90 * @param bases список базовых классов, от которых требуется унаследовать
92 * новый класс.
91 * новый класс.
93 *
92 *
94 */
93 */
95 export function djbase(...bases: any[]): Constructor {
94 export function djbase(...bases: any[]): Constructor {
96
95
97 const t = class {
96 const t = class {
98 static mock: boolean;
97 static mock: boolean;
99 static bases: AbstractConstructor[];
98 static bases: AbstractConstructor[];
100 };
99 };
101
100
102 t.mock = true;
101 t.mock = true;
103 t.bases = bases;
102 t.bases = bases;
104
103
105 return t as any;
104 return t as any;
106 }
105 }
107
106
108 function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
107 function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
109 return v && "mock" in v;
108 return v && "mock" in v;
110 }
109 }
111
110
112 /** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс
111 /** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс
113 * должен быть унаследован от `djbase(...)`.
112 * должен быть унаследован от `djbase(...)`.
114 *
113 *
115 * @param target Класс, который нужно объявить при помощи `dojo/_base/declare`
114 * @param target Класс, который нужно объявить при помощи `dojo/_base/declare`
116 */
115 */
117 export function djclass<T extends AbstractConstructor>(target: T): T {
116 export function djclass<T extends AbstractConstructor>(target: T): T {
118 // получаем базовый конструктор и его прототип
117 // получаем базовый конструктор и его прототип
119 let bp = target && target.prototype && Object.getPrototypeOf(target.prototype);
118 let bp = target && target.prototype && Object.getPrototypeOf(target.prototype);
120 const bc = bp && bp.constructor;
119 const bc = bp && bp.constructor;
121
120
122 // проверка того, что класс унаследован от специальной заглушки
121 // проверка того, что класс унаследован от специальной заглушки
123 if (isMockConstructor(bc)) {
122 if (isMockConstructor(bc)) {
124 // t - базовый класс, объявленный при помощи dojo/_base/declare
123 // bc.bases - базовый класс, объявленный при помощи dojo/_base/declare
125 const t = bc.bases;
126
127 const cls = declare<any>(bc.bases, target.prototype);
124 const cls = declare<any>(bc.bases, target.prototype);
128
125
129 // bc - базовый класс, bc.prototype используется как super
126 // bc - базовый класс, bc.prototype используется как super
130 // при вызове базовых методов. Нужно создать bc.prototype
127 // при вызове базовых методов. Нужно создать bc.prototype
131 // таким образом, чтобы он вызывал this.inherited().
128 // таким образом, чтобы он вызывал this.inherited().
132
129
133 // создаем новый порототип, он не в цепочке прототипов у текущего
130 // создаем новый порототип, он не в цепочке прототипов у текущего
134 // класса, но super.some_method будет использовать именно его.
131 // класса, но super.some_method будет использовать именно его.
135 // в этом объекте будут размещены прокси для переопределенных
132 // в этом объекте будут размещены прокси для переопределенных
136 // методов.
133 // методов.
137 bp = bc.prototype = Object.create(cls.prototype);
134 bp = bc.prototype = Object.create(cls.prototype);
138 bp.constructor = bc;
135 bp.constructor = bc;
139
136
140 // proxy - фабрика для создания прокси-методов, которые внутри
137 // proxy - фабрика для создания прокси-методов, которые внутри
141 // себя вызовут this.inherited с правильными параметрами.
138 // себя вызовут this.inherited с правильными параметрами.
142 const proxy = (m: (...args: any[]) => any) => function (this: any) {
139 const proxy = (m: (...args: any[]) => any) => function (this: any) {
143 const f = this.getInherited({ callee: m });
140 const f = this.getInherited({ callee: m });
144 return f && f.apply(this, arguments);
141 return f && f.apply(this, arguments);
145
142
146 // так сделать можно только dojo 1.15+
143 // так сделать можно только dojo 1.15+
147 // return this.inherited(m, arguments);
144 // return this.inherited(m, arguments);
148 };
145 };
149
146
150 // у текущего класса прототип содержит методы, объявленные в этом
147 // у текущего класса прототип содержит методы, объявленные в этом
151 // классе и его конструктор. Нужно пройти по всем методам и
148 // классе и его конструктор. Нужно пройти по всем методам и
152 // создать для них прокси.
149 // создать для них прокси.
153 // При этом только те, методы, которые есть в базовых классах
150 // При этом только те, методы, которые есть в базовых классах
154 // могут быть переопределены.
151 // могут быть переопределены.
155 each(target.prototype, (m: any, p: string | number | symbol) => {
152 each(target.prototype, (m: any, p: string | number | symbol) => {
156 if (typeof m === "function" &&
153 if (typeof m === "function" &&
157 p !== "constructor" &&
154 p !== "constructor" &&
158 target.prototype.hasOwnProperty(p)
155 target.prototype.hasOwnProperty(p)
159 ) {
156 ) {
160 bp[p] = proxy(m);
157 bp[p] = proxy(m);
161 }
158 }
162 });
159 });
163
160
164 // TODO mixin static members
161 // TODO mixin static members
165 return cls as any;
162 return cls as any;
166 } else {
163 } else {
167 return target as any;
164 return target as any;
168 }
165 }
169 }
166 }
170
167
171 function makeSetterName(prop: string) {
168 function makeSetterName(prop: string) {
172 return [
169 return [
173 "_set",
170 "_set",
174 prop.replace(/^./, x => x.toUpperCase()),
171 prop.replace(/^./, x => x.toUpperCase()),
175 "Attr"
172 "Attr"
176 ].join("");
173 ].join("");
177 }
174 }
178
175
179 function makeGetterName(prop: string) {
176 function makeGetterName(prop: string) {
180 return [
177 return [
181 "_get",
178 "_get",
182 prop.replace(/^./, x => x.toUpperCase()),
179 prop.replace(/^./, x => x.toUpperCase()),
183 "Attr"
180 "Attr"
184 ].join("");
181 ].join("");
185 }
182 }
186
183
187 interface NodeBindSpec {
184 interface NodeBindSpec {
188 node: string;
185 node: string;
189 type: "attribute" | "innerText" | "textContent" | "innerHTML" | "class" | "toggleClass";
186 type: "attribute" | "innerText" | "textContent" | "innerHTML" | "class" | "toggleClass";
190 attribute?: string;
187 attribute?: string;
191
188
192 className?: string;
189 className?: string;
193 }
190 }
194
191
195 /**
192 /**
196 * Описание привязки свойства виджета к свойству внутреннего объекта.
193 * Описание привязки свойства виджета к свойству внутреннего объекта.
197 */
194 */
198 interface MemberBindSpec {
195 interface MemberBindSpec {
199 /**
196 /**
200 * Имя свойства со ссылкой на объект, к которому .
197 * Имя свойства со ссылкой на объект, к которому .
201 */
198 */
202 member: string;
199 member: string;
203 /**
200 /**
204 * Свойство объекта к которому нужно осуществить привязку.
201 * Свойство объекта к которому нужно осуществить привязку.
205 */
202 */
206 property: string;
203 property: string;
207
204
208 /**
205 /**
209 * Привязка осуществляется не только на запись но и на чтение свойства.
206 * Привязка осуществляется не только на запись но и на чтение свойства.
210 */
207 */
211 getter?: boolean;
208 getter?: boolean;
212 }
209 }
213
210
214 function isNodeBindSpec(v: any): v is NodeBindSpec {
211 function isNodeBindSpec(v: any): v is NodeBindSpec {
215 return "node" in v;
212 return "node" in v;
216 }
213 }
217
214
218 /** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM
215 /** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM
219 * элементам, либо свойству внутреннего объекта.
216 * элементам, либо свойству внутреннего объекта.
220 *
217 *
221 * @param {NodeBindSpec | MemberBindSpec} params Параметры связывания.
218 * @param {NodeBindSpec | MemberBindSpec} params Параметры связывания.
222 */
219 */
223 export function bind(params: NodeBindSpec | MemberBindSpec) {
220 export function bind(params: NodeBindSpec | MemberBindSpec) {
224 if (isNodeBindSpec(params)) {
221 if (isNodeBindSpec(params)) {
225 return (target: any, name: string) => {
222 return (target: any, name: string) => {
226 target[makeSetterName(name)] = params;
223 target[makeSetterName(name)] = params;
227 };
224 };
228 } else {
225 } else {
229 return (target: any, name: string) => {
226 return (target: any, name: string) => {
230 target[name] = null;
227 target[name] = null;
231 target[makeSetterName(name)] = function (v: any) {
228 target[makeSetterName(name)] = function (v: any) {
232 this._set(name, v);
229 this._set(name, v);
233 this[params.member].set(params.property, v);
230 this[params.member].set(params.property, v);
234 };
231 };
235 if (params.getter)
232 if (params.getter)
236 target[makeGetterName(name)] = function () {
233 target[makeGetterName(name)] = function () {
237 return this[params.member].get(params.property);
234 return this[params.member].get(params.property);
238 };
235 };
239 };
236 };
240 }
237 }
241 }
238 }
242
239
243 /** Создает в прототипе указанное свойство со значение `undefined`, данный
240 /** Создает в прототипе указанное свойство со значение `undefined`, данный
244 * декоратор следует использовать для свойств, у которых нет значения по-умолчанию
241 * декоратор следует использовать для свойств, у которых нет значения по-умолчанию
245 * и они не могут быть `null | undefined`
242 * и они не могут быть `null | undefined`
246 */
243 */
247 export function prototype(): (p: any, name: string) => void;
244 export function prototype(): (p: any, name: string) => void;
248 /** Создает в прототипе свойство с указанным значением.
245 /** Создает в прототипе свойство с указанным значением.
249 * @param value Значение, которое будет указано в прототипе
246 * @param value Значение, которое будет указано в прототипе
250 */
247 */
251 export function prototype<T>(value: T): <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => void;
248 export function prototype<T>(value: T): <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => void;
252 export function prototype<T>(value?: T) {
249 export function prototype<T>(value?: T) {
253 return <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => {
250 return <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => {
254 p[name] = value as any;
251 p[name] = value as any;
255 };
252 };
256 }
253 }
@@ -1,100 +1,100
1 import { id as mid } from "module";
1 import { id as mid } from "module";
2 import { TraceSource } from "@implab/core-amd/log/TraceSource";
2 import { TraceSource } from "@implab/core-amd/log/TraceSource";
3 import { MapOf } from "@implab/core-amd/interfaces";
3 import { MapOf } from "@implab/core-amd/interfaces";
4 import { mixin } from "@implab/core-amd/safe";
4 import { mixin } from "@implab/core-amd/safe";
5
5
6 const trace = TraceSource.get(mid);
6 const trace = TraceSource.get(mid);
7
7
8
8
9 function on<T extends keyof HTMLElementEventMap>(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => any): () => void {
9 function on<T extends keyof HTMLElementEventMap>(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => any): () => void {
10 // Add an event listener to a DOM node
10 // Add an event listener to a DOM node
11 node.addEventListener(eventName, handler, false);
11 node.addEventListener(eventName, handler, false);
12
12
13 return () => node.removeEventListener(eventName, handler, false);
13 return () => node.removeEventListener(eventName, handler, false);
14 }
14 }
15
15
16 interface NodeLoadResult {
16 interface NodeLoadResult {
17 node: HTMLElement;
17 node: HTMLElement;
18 }
18 }
19
19
20 class DomInject {
20 class DomInject {
21 injectionPoint?: HTMLElement;
21 injectionPoint?: HTMLElement;
22 injectAfter?: HTMLElement;
22 injectAfter?: HTMLElement;
23
23
24 _map: MapOf<Promise<NodeLoadResult>> = {};
24 _map: MapOf<Promise<NodeLoadResult>> = {};
25
25
26 _inject<T extends keyof HTMLElementTagNameMap>(name: T, attr: Partial<HTMLElementTagNameMap[T]>) {
26 _inject<T extends keyof HTMLElementTagNameMap>(name: T, attr: Partial<HTMLElementTagNameMap[T]>) {
27 const node = document.createElement(name);
27 const node = document.createElement(name);
28
28
29 return new Promise<NodeLoadResult>((ok, fail) => {
29 return new Promise<NodeLoadResult>((ok, fail) => {
30
30
31 const cleanup = () => {
31 const cleanup = () => {
32 noerr();
32 noerr();
33 noload();
33 noload();
34 };
34 };
35
35
36 const noload = on(node, "load", () => {
36 const noload = on(node, "load", () => {
37 ok({ node });
37 ok({ node });
38 cleanup();
38 cleanup();
39 });
39 });
40
40
41 const noerr = on(node, "error", e => {
41 const noerr = on(node, "error", e => {
42 fail({
42 fail({
43 error: e,
43 error: e,
44 node
44 node
45 });
45 });
46 cleanup();
46 cleanup();
47 });
47 });
48
48
49 mixin(node, attr);
49 mixin(node, attr);
50
50
51 const _injectionPoint = this.injectionPoint || document.head;
51 const _injectionPoint = this.injectionPoint || document.head;
52 const _injectBefore = this.injectAfter ? this.injectAfter.nextSibling : null;
52 const _injectBefore = this.injectAfter ? this.injectAfter.nextSibling : null;
53
53
54 _injectionPoint.insertBefore(node, _injectBefore);
54 _injectionPoint.insertBefore(node, _injectBefore);
55 });
55 });
56 }
56 }
57
57
58 async injectScript(url: string) {
58 async injectScript(url: string) {
59 let d = this._map[url];
59 let d = this._map[url];
60 if (!d) {
60 if (!d) {
61 trace.log("js {0}", url);
61 trace.log("js {0}", url);
62 d = this._inject("script", {
62 d = this._inject("script", {
63 type: "text/javascript",
63 type: "text/javascript",
64 charset: "utf-8",
64 charset: "utf-8",
65 src: url
65 src: url
66 });
66 });
67 this._map[url] = d;
67 this._map[url] = d;
68 }
68 }
69 try {
69 try {
70 await d;
70 await d;
71 trace.log("done {0}", url);
71 trace.log("done {0}", url);
72 } catch (e) {
72 } catch (e) {
73 trace.error("failed {0}: {1}", url, e);
73 trace.error("failed {0}: {1}", url, e);
74 throw e;
74 throw e;
75 }
75 }
76 }
76 }
77
77
78 async injectStylesheet(url: string) {
78 async injectStylesheet(url: string) {
79 let d = this._map[url];
79 let d = this._map[url];
80 if (!d) {
80 if (!d) {
81 trace.log("js {0}", url);
81 trace.log("js {0}", url);
82 d = this._inject("link", {
82 d = this._inject("link", {
83 type: "text/css",
83 type: "text/css",
84 rel: "stylesheet",
84 rel: "stylesheet",
85 href: url
85 href: url
86 });
86 });
87 this._map[url] = d;
87 this._map[url] = d;
88 }
88 }
89 try {
89 try {
90 await d;
90 await d;
91 trace.log("done {0}", url);
91 trace.log("done {0}", url);
92 } catch (e) {
92 } catch (e) {
93 trace.error("failed {0}: {1}", url, e);
93 trace.error("failed {0}: {1}", url, e);
94 throw e;
94 throw e;
95 }
95 }
96 }
96 }
97 };
97 }
98
98
99 const instance = new DomInject();
99 const instance = new DomInject();
100 export default instance;
100 export default instance;
@@ -1,110 +1,110
1 import { AbstractConstructor, djbase, djclass } from "../declare";
1 import { djbase, djclass } from "../declare";
2 import _WidgetBase = require("dijit/_WidgetBase");
2 import _WidgetBase = require("dijit/_WidgetBase");
3 import _AttachMixin = require("dijit/_AttachMixin");
3 import _AttachMixin = require("dijit/_AttachMixin");
4 import { Rendition, isNode, startupWidgets } from "./traits";
4 import { Rendition, isNode } from "./traits";
5 import registry = require("dijit/registry");
5 import registry = require("dijit/registry");
6
6
7 // type Handle = dojo.Handle;
7 // type Handle = dojo.Handle;
8
8
9 export interface EventArgs {
9 export interface EventArgs {
10 bubbles?: boolean;
10 bubbles?: boolean;
11
11
12 cancelable?: boolean;
12 cancelable?: boolean;
13
13
14 composed?: boolean;
14 composed?: boolean;
15 }
15 }
16
16
17 export interface DjxWidgetBase<Attrs = {}, Events extends { [name in keyof Events]: Event } = {}> extends
17 export interface DjxWidgetBase<Attrs = {}, Events extends { [name in keyof Events]: Event } = {}> extends
18 _WidgetBase<Events> {
18 _WidgetBase<Events> {
19
19
20 /** This property is declared only for type inference to work, it is never assigned
20 /** This property is declared only for type inference to work, it is never assigned
21 * and should not be used.
21 * and should not be used.
22 */
22 */
23 readonly _eventMap: Events & GlobalEventHandlersEventMap;
23 readonly _eventMap: Events & GlobalEventHandlersEventMap;
24 }
24 }
25
25
26 type _super = {
26 type _super = {
27 startup(): void;
27 startup(): void;
28 }
28 };
29
29
30 @djclass
30 @djclass
31 export abstract class DjxWidgetBase<Attrs = {}, Events = {}> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
31 export abstract class DjxWidgetBase<Attrs = {}, Events = {}> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
32
32
33 /** The list of pairs of event and method names. When the widget is created all methods from
33 /** The list of pairs of event and method names. When the widget is created all methods from
34 * this list will be connected to corresponding events.
34 * this list will be connected to corresponding events.
35 */
35 */
36 _eventHandlers: Array<{
36 _eventHandlers: Array<{
37 eventName: string,
37 eventName: string,
38 handlerMethod: keyof any;
38 handlerMethod: keyof any;
39 }> = [];
39 }> = [];
40
40
41 buildRendering() {
41 buildRendering() {
42 this.domNode = this.render().getDomNode();
42 this.domNode = this.render().getDomNode();
43 super.buildRendering();
43 super.buildRendering();
44
44
45 // now we should get assigned data-dojo-attach-points
45 // now we should get assigned data-dojo-attach-points
46 // place the contents of the original srcNode to the containerNode
46 // place the contents of the original srcNode to the containerNode
47 const src = this.srcNodeRef;
47 const src = this.srcNodeRef;
48 const dest = this.containerNode;
48 const dest = this.containerNode;
49
49
50 // the donNode is constructed now we need to connect event handlers
50 // the donNode is constructed now we need to connect event handlers
51 this._connectEventHandlers();
51 this._connectEventHandlers();
52
52
53 if (src && dest) {
53 if (src && dest) {
54 while (src.firstChild)
54 while (src.firstChild)
55 dest.appendChild(src.firstChild);
55 dest.appendChild(src.firstChild);
56 }
56 }
57 }
57 }
58
58
59 abstract render(): Rendition<HTMLElement>;
59 abstract render(): Rendition<HTMLElement>;
60
60
61 private _connectEventHandlers() {
61 private _connectEventHandlers() {
62 this._eventHandlers.forEach(({eventName, handlerMethod}) => {
62 this._eventHandlers.forEach(({eventName, handlerMethod}) => {
63 const handler = this[handlerMethod as keyof this];
63 const handler = this[handlerMethod as keyof this];
64 if (typeof handler === "function")
64 if (typeof handler === "function")
65 this.on(eventName, handler.bind(this));
65 this.on(eventName, handler.bind(this));
66 });
66 });
67 }
67 }
68
68
69 _processTemplateNode<T extends (Element | Node | _WidgetBase)>(
69 _processTemplateNode<T extends (Element | Node | _WidgetBase)>(
70 baseNode: T,
70 baseNode: T,
71 getAttrFunc: (baseNode: T, attr: string) => any,
71 getAttrFunc: (baseNode: T, attr: string) => any,
72 // tslint:disable-next-line: ban-types
72 // tslint:disable-next-line: ban-types
73 attachFunc: (node: T, type: string, func?: Function) => dojo.Handle
73 attachFunc: (node: T, type: string, func?: Function) => dojo.Handle
74 ): boolean {
74 ): boolean {
75 if (isNode(baseNode)) {
75 if (isNode(baseNode)) {
76 const w = registry.byNode(baseNode);
76 const w = registry.byNode(baseNode);
77 if (w) {
77 if (w) {
78 // from dijit/_WidgetsInTemplateMixin
78 // from dijit/_WidgetsInTemplateMixin
79 this._processTemplateNode(w,
79 this._processTemplateNode(w,
80 (n, p) => n.get(p as any), // callback to get a property of a widget
80 (n, p) => n.get(p as any), // callback to get a property of a widget
81 (widget, type, callback) => {
81 (widget, type, callback) => {
82 if (!callback)
82 if (!callback)
83 throw new Error("The callback must be specified");
83 throw new Error("The callback must be specified");
84
84
85 // callback to do data-dojo-attach-event to a widget
85 // callback to do data-dojo-attach-event to a widget
86 if (type in widget) {
86 if (type in widget) {
87 // back-compat, remove for 2.0
87 // back-compat, remove for 2.0
88 return widget.connect(widget, type, callback as EventListener);
88 return widget.connect(widget, type, callback as EventListener);
89 } else {
89 } else {
90 // 1.x may never hit this branch, but it's the default for 2.0
90 // 1.x may never hit this branch, but it's the default for 2.0
91 return widget.on(type, callback);
91 return widget.on(type, callback);
92 }
92 }
93
93
94 });
94 });
95 // don't process widgets internals
95 // don't process widgets internals
96 return false;
96 return false;
97 }
97 }
98 }
98 }
99 return super._processTemplateNode(baseNode, getAttrFunc, attachFunc);
99 return super._processTemplateNode(baseNode, getAttrFunc, attachFunc);
100 }
100 }
101
101
102 /** Starts current widget and all its supporting widgets (placed outside
102 /** Starts current widget and all its supporting widgets (placed outside
103 * `containerNode`) and child widgets (placed inside `containerNode`)
103 * `containerNode`) and child widgets (placed inside `containerNode`)
104 */
104 */
105 startup() {
105 startup() {
106 // startup supporting widgets
106 // startup supporting widgets
107 registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
107 registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
108 super.startup();
108 super.startup();
109 }
109 }
110 }
110 }
@@ -1,116 +1,116
1 import { isNull, mixin } from "@implab/core-amd/safe";
1 import { isNull, mixin } from "@implab/core-amd/safe";
2 import { isPlainObject, isNode, isRendition, DojoNodePosition, Rendition, isInPage, isWidget, isDocumentFragmentNode, startupWidgets } from "./traits";
2 import { isPlainObject, isNode, isRendition, DojoNodePosition, Rendition, isInPage, isWidget, isDocumentFragmentNode, startupWidgets } from "./traits";
3
3
4 import dom = require("dojo/dom-construct");
4 import dom = require("dojo/dom-construct");
5 import registry = require("dijit/registry");
5 import registry = require("dijit/registry");
6
6
7
7
8 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
8 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
9 private _attrs = {};
9 private _attrs = {};
10
10
11 private _children = new Array();
11 private _children = new Array();
12
12
13 private _created: boolean = false;
13 private _created: boolean = false;
14
14
15 visitNext(v: any) {
15 visitNext(v: any) {
16 if (this._created)
16 if (this._created)
17 throw new Error("The Element is already created");
17 throw new Error("The Element is already created");
18
18
19 if (isNull(v) || typeof v === "boolean")
19 if (isNull(v) || typeof v === "boolean")
20 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
20 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
21 return;
21 return;
22
22
23 if (isPlainObject(v)) {
23 if (isPlainObject(v)) {
24 mixin(this._attrs, v);
24 mixin(this._attrs, v);
25 } else if (v instanceof Array) {
25 } else if (v instanceof Array) {
26 v.forEach(x => this.visitNext(x));
26 v.forEach(x => this.visitNext(x));
27 } else {
27 } else {
28 this._children.push(v);
28 this._children.push(v);
29 }
29 }
30 }
30 }
31
31
32 protected getItemDom(v: any) {
32 protected getItemDom(v: any) {
33 const tv = typeof v;
33 const tv = typeof v;
34
34
35 if (tv === "string" || tv === "number" || v instanceof RegExp || v instanceof Date) {
35 if (tv === "string" || tv === "number" || v instanceof RegExp || v instanceof Date) {
36 // primitive types converted to the text nodes
36 // primitive types converted to the text nodes
37 return document.createTextNode(v.toString());
37 return document.createTextNode(v.toString());
38 } else if (isNode(v)) {
38 } else if (isNode(v)) {
39 // nodes are kept as is
39 // nodes are kept as is
40 return v;
40 return v;
41 } else if (isRendition(v)) {
41 } else if (isRendition(v)) {
42 // renditions as instantinated
42 // renditions as instantinated
43 return v.getDomNode();
43 return v.getDomNode();
44 } else if (isWidget(v)) {
44 } else if (isWidget(v)) {
45 // widgets are converted to it's markup
45 // widgets are converted to it's markup
46 return v.domNode;
46 return v.domNode;
47 } else if (tv === "boolean" || v === null || v === undefined) {
47 } else if (tv === "boolean" || v === null || v === undefined) {
48 // null | undefined | boolean are removed, converted to comments
48 // null | undefined | boolean are removed, converted to comments
49 return document.createComment(`[${tv} ${String(v)}]`);
49 return document.createComment(`[${tv} ${String(v)}]`);
50 } else {
50 } else {
51 // bug: explicit error otherwise
51 // bug: explicit error otherwise
52 throw new Error("Invalid parameter: " + v);
52 throw new Error("Invalid parameter: " + v);
53 }
53 }
54 }
54 }
55
55
56 ensureCreated() {
56 ensureCreated() {
57 if (!this._created) {
57 if (!this._created) {
58 this._create(this._attrs, this._children);
58 this._create(this._attrs, this._children);
59 this._children = [];
59 this._children = [];
60 this._attrs = {};
60 this._attrs = {};
61 this._created = true;
61 this._created = true;
62 }
62 }
63 }
63 }
64
64
65 /** @deprecated will be removed in 1.0.0, use getDomNode() */
65 /** @deprecated will be removed in 1.0.0, use getDomNode() */
66 getDomElement() {
66 getDomElement() {
67 return this.getDomNode();
67 return this.getDomNode();
68 }
68 }
69
69
70 /** Creates DOM node if not created. No additional actions are taken. */
70 /** Creates DOM node if not created. No additional actions are taken. */
71 getDomNode() {
71 getDomNode() {
72 this.ensureCreated();
72 this.ensureCreated();
73 return this._getDomNode();
73 return this._getDomNode();
74 }
74 }
75
75
76 /** Creates DOM node if not created, places it to the specified position
76 /** Creates DOM node if not created, places it to the specified position
77 * and calls startup() method for all widgets contained by this node.
77 * and calls startup() method for all widgets contained by this node.
78 *
78 *
79 * @param {string | Node} refNode The reference node where the created
79 * @param {string | Node} refNode The reference node where the created
80 * DOM should be placed.
80 * DOM should be placed.
81 * @param {DojoNodePosition} position Optional parameter, specifies the
81 * @param {DojoNodePosition} position Optional parameter, specifies the
82 * position relative to refNode. Default is "last" (i.e. last child).
82 * position relative to refNode. Default is "last" (i.e. last child).
83 */
83 */
84 placeAt(refNode: string | Node, position?: DojoNodePosition) {
84 placeAt(refNode: string | Node, position?: DojoNodePosition) {
85 const domNode = this.getDomNode();
85 const domNode = this.getDomNode();
86
86
87 const collect = (collection: HTMLCollection) => {
87 const collect = (collection: HTMLCollection) => {
88 const items = [];
88 const items = [];
89 for (let i = 0, n = collection.length; i < n; i++) {
89 for (let i = 0, n = collection.length; i < n; i++) {
90 items.push(collection[i]);
90 items.push(collection[i]);
91 }
91 }
92 return items;
92 return items;
93 }
93 };
94
94
95 const startup = (node: Node) => {
95 const startup = (node: Node) => {
96 if (node.parentNode) {
96 if (node.parentNode) {
97 const parentWidget = registry.getEnclosingWidget(node.parentNode);
97 const parentWidget = registry.getEnclosingWidget(node.parentNode);
98 if (parentWidget && parentWidget._started)
98 if (parentWidget && parentWidget._started)
99 return startupWidgets(node);
99 return startupWidgets(node);
100 }
100 }
101 if (isInPage(node))
101 if (isInPage(node))
102 startupWidgets(node);
102 startupWidgets(node);
103 }
103 };
104
104
105 const startupPending = isDocumentFragmentNode(domNode) ? collect(domNode.children) : [domNode]
105 const startupPending = isDocumentFragmentNode(domNode) ? collect(domNode.children) : [domNode];
106
106
107 dom.place(domNode, refNode, position);
107 dom.place(domNode, refNode, position);
108
108
109 startupPending.forEach(startup);
109 startupPending.forEach(startup);
110
110
111 }
111 }
112
112
113 protected abstract _create(attrs: object, children: any[]): void;
113 protected abstract _create(attrs: object, children: any[]): void;
114
114
115 protected abstract _getDomNode(): TNode;
115 protected abstract _getDomNode(): TNode;
116 }
116 }
@@ -1,121 +1,121
1 import dom = require("dojo/dom-construct");
1 import dom = require("dojo/dom-construct");
2 import { argumentNotNull } from "@implab/core-amd/safe";
2 import { argumentNotNull } from "@implab/core-amd/safe";
3 import { RenditionBase } from "./RenditionBase";
3 import { RenditionBase } from "./RenditionBase";
4 import { DojoNodePosition, isInPage, isWidget } from "./traits";
4 import { DojoNodePosition, isInPage, isWidget } from "./traits";
5 import registry = require("dijit/registry");
5 import registry = require("dijit/registry");
6 import ContentPane = require("dijit/layout/ContentPane");
6 import ContentPane = require("dijit/layout/ContentPane");
7
7
8 // tslint:disable-next-line: class-name
8 // tslint:disable-next-line: class-name
9 export interface _Widget {
9 export interface _Widget {
10 domNode: Node;
10 domNode: Node;
11
11
12 containerNode?: Node;
12 containerNode?: Node;
13
13
14 placeAt?(refNode: string | Node, position?: DojoNodePosition): void;
14 placeAt?(refNode: string | Node, position?: DojoNodePosition): void;
15 startup?(): void;
15 startup?(): void;
16
16
17 addChild?(widget: any, index?: number): void;
17 addChild?(widget: any, index?: number): void;
18 }
18 }
19
19
20 export type _WidgetCtor = new (attrs: any, srcNode?: string | Node) => _Widget;
20 export type _WidgetCtor = new (attrs: any, srcNode?: string | Node) => _Widget;
21
21
22 export class WidgetRendition extends RenditionBase<Node> {
22 export class WidgetRendition extends RenditionBase<Node> {
23 readonly widgetClass: _WidgetCtor;
23 readonly widgetClass: _WidgetCtor;
24
24
25 _instance: _Widget | undefined;
25 _instance: _Widget | undefined;
26
26
27 constructor(widgetClass: _WidgetCtor) {
27 constructor(widgetClass: _WidgetCtor) {
28 super();
28 super();
29 argumentNotNull(widgetClass, "widgetClass");
29 argumentNotNull(widgetClass, "widgetClass");
30
30
31 this.widgetClass = widgetClass;
31 this.widgetClass = widgetClass;
32 }
32 }
33
33
34 _addChild(child: any): void {
34 _addChild(child: any): void {
35 const instance = this._getInstance();
35 const instance = this._getInstance();
36
36
37 if (instance.addChild) {
37 if (instance.addChild) {
38 if (child instanceof WidgetRendition) {
38 if (child instanceof WidgetRendition) {
39 // layout containers add custom logic to addChild methods
39 // layout containers add custom logic to addChild methods
40 instance.addChild(child.getWidgetInstance());
40 instance.addChild(child.getWidgetInstance());
41 } else if (isWidget(child)) {
41 } else if (isWidget(child)) {
42 instance.addChild(child);
42 instance.addChild(child);
43 } else {
43 } else {
44 if (!instance.containerNode)
44 if (!instance.containerNode)
45 throw new Error("The widget doesn't have neither addChild nor containerNode");
45 throw new Error("The widget doesn't have neither addChild nor containerNode");
46
46
47 // the current widget isn't started, it's children shouldn't start too
47 // the current widget isn't started, it's children shouldn't start too
48 dom.place(this.getItemDom(child), instance.containerNode);
48 dom.place(this.getItemDom(child), instance.containerNode);
49 }
49 }
50 } else {
50 } else {
51 if (!instance.containerNode)
51 if (!instance.containerNode)
52 throw new Error("The widget doesn't have neither addChild nor containerNode");
52 throw new Error("The widget doesn't have neither addChild nor containerNode");
53
53
54 // the current widget isn't started, it's children shouldn't start too
54 // the current widget isn't started, it's children shouldn't start too
55 dom.place(this.getItemDom(child), instance.containerNode);
55 dom.place(this.getItemDom(child), instance.containerNode);
56 }
56 }
57 }
57 }
58
58
59 protected _create(attrs: any, children: any[]) {
59 protected _create(attrs: any, children: any[]) {
60 if (this.widgetClass.prototype instanceof ContentPane) {
60 if (this.widgetClass.prototype instanceof ContentPane) {
61 // a special case for the ContentPane this is for
61 // a special case for the ContentPane this is for
62 // the compatibility with this heavy widget, all
62 // the compatibility with this heavy widget, all
63 // regular containers could be easily manipulated
63 // regular containers could be easily manipulated
64 // through `containerNode` property or `addChild` method.
64 // through `containerNode` property or `addChild` method.
65
65
66 // render children to the DocumentFragment
66 // render children to the DocumentFragment
67 const content = document.createDocumentFragment();
67 const content = document.createDocumentFragment();
68 children.forEach(child => content.appendChild(this.getItemDom(child)));
68 children.forEach(child => content.appendChild(this.getItemDom(child)));
69
69
70 // set the content property to the parameters of the widget
70 // set the content property to the parameters of the widget
71 const _attrs = { ...attrs, content };
71 const _attrs = { ...attrs, content };
72 this._instance = new this.widgetClass(_attrs);
72 this._instance = new this.widgetClass(_attrs);
73 } else {
73 } else {
74 this._instance = new this.widgetClass(attrs);
74 this._instance = new this.widgetClass(attrs);
75 children.forEach(x => this._addChild(x));
75 children.forEach(x => this._addChild(x));
76 }
76 }
77
77
78 }
78 }
79
79
80 private _getInstance() {
80 private _getInstance() {
81 if (!this._instance)
81 if (!this._instance)
82 throw new Error("The instance of the widget isn't created");
82 throw new Error("The instance of the widget isn't created");
83 return this._instance;
83 return this._instance;
84 }
84 }
85
85
86 protected _getDomNode() {
86 protected _getDomNode() {
87 if (!this._instance)
87 if (!this._instance)
88 throw new Error("The instance of the widget isn't created");
88 throw new Error("The instance of the widget isn't created");
89 return this._instance.domNode;
89 return this._instance.domNode;
90 }
90 }
91
91
92 /** Overrides default placeAt implementation. Calls placeAt of the
92 /** Overrides default placeAt implementation. Calls placeAt of the
93 * widget and then starts it.
93 * widget and then starts it.
94 *
94 *
95 * @param refNode A node or id of the node where the widget should be placed.
95 * @param refNode A node or id of the node where the widget should be placed.
96 * @param position A position relative to refNode.
96 * @param position A position relative to refNode.
97 */
97 */
98 placeAt(refNode: string | Node, position?: DojoNodePosition) {
98 placeAt(refNode: string | Node, position?: DojoNodePosition) {
99 this.ensureCreated();
99 this.ensureCreated();
100 const instance = this._getInstance();
100 const instance = this._getInstance();
101 if (typeof instance.placeAt === "function") {
101 if (typeof instance.placeAt === "function") {
102 instance.placeAt(refNode, position);
102 instance.placeAt(refNode, position);
103
103
104 // fix the dojo startup behavior when the widget is placed
104 // fix the dojo startup behavior when the widget is placed
105 // directly to the document and doesn't have any enclosing widgets
105 // directly to the document and doesn't have any enclosing widgets
106 const parentWidget = instance.domNode.parentNode ?
106 const parentWidget = instance.domNode.parentNode ?
107 registry.getEnclosingWidget(instance.domNode.parentNode) : null
107 registry.getEnclosingWidget(instance.domNode.parentNode) : null;
108 if (!parentWidget && isInPage(instance.domNode) && typeof instance.startup === "function")
108 if (!parentWidget && isInPage(instance.domNode) && typeof instance.startup === "function")
109 instance.startup();
109 instance.startup();
110 } else {
110 } else {
111 // the widget doesn't have a placeAt method, strange but whatever
111 // the widget doesn't have a placeAt method, strange but whatever
112 super.placeAt(refNode, position);
112 super.placeAt(refNode, position);
113 }
113 }
114 }
114 }
115
115
116 getWidgetInstance() {
116 getWidgetInstance() {
117 this.ensureCreated();
117 this.ensureCreated();
118 return this._getInstance();
118 return this._getInstance();
119 }
119 }
120
120
121 }
121 }
@@ -1,227 +1,227
1 import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
1 import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
2 import { isDestroyable } from "@implab/core-amd/safe";
2 import { isDestroyable } from "@implab/core-amd/safe";
3 import _WidgetBase = require("dijit/_WidgetBase");
3 import _WidgetBase = require("dijit/_WidgetBase");
4 import registry = require("dijit/registry");
4 import registry = require("dijit/registry");
5 import dom = require("dojo/dom-construct");
5 import dom = require("dojo/dom-construct");
6 import Stateful = require("dojo/Stateful");
6 import Stateful = require("dojo/Stateful");
7 import { FunctionRendition } from "./FunctionRendition";
7 import { FunctionRendition } from "./FunctionRendition";
8 import { DjxWidgetBase } from "./DjxWidgetBase";
8 import { DjxWidgetBase } from "./DjxWidgetBase";
9
9
10 type _WidgetBaseConstructor = typeof _WidgetBase;
10 type _WidgetBaseConstructor = typeof _WidgetBase;
11
11
12 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
12 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
13
13
14 export interface Rendition<TNode extends Node = Node> {
14 export interface Rendition<TNode extends Node = Node> {
15 getDomNode(): TNode;
15 getDomNode(): TNode;
16
16
17 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
17 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
18 }
18 }
19
19
20 /**
20 /**
21 * @deprecated use Rendition
21 * @deprecated use Rendition
22 */
22 */
23 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
23 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
24
24
25 export interface IRecursivelyDestroyable {
25 export interface IRecursivelyDestroyable {
26 destroyRecursive(): void;
26 destroyRecursive(): void;
27 }
27 }
28
28
29 export function isNode(el: any): el is Node {
29 export function isNode(el: any): el is Node {
30 return el && el.nodeName && el.nodeType;
30 return el && el.nodeName && el.nodeType;
31 }
31 }
32
32
33 export function isElementNode(el: any): el is Element {
33 export function isElementNode(el: any): el is Element {
34 return isNode(el) && el.nodeType === 1;
34 return isNode(el) && el.nodeType === 1;
35 }
35 }
36
36
37 export function isTextNode(el: any): el is Text {
37 export function isTextNode(el: any): el is Text {
38 return isNode(el) && el.nodeType === 3;
38 return isNode(el) && el.nodeType === 3;
39 }
39 }
40
40
41 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
41 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
42 return isNode(el) && el.nodeType === 7;
42 return isNode(el) && el.nodeType === 7;
43 }
43 }
44
44
45 export function isCommentNode(el: any): el is Comment {
45 export function isCommentNode(el: any): el is Comment {
46 return isNode(el) && el.nodeType === 8;
46 return isNode(el) && el.nodeType === 8;
47 }
47 }
48
48
49 export function isDocumentNode(el: any): el is Document {
49 export function isDocumentNode(el: any): el is Document {
50 return isNode(el) && el.nodeType === 9;
50 return isNode(el) && el.nodeType === 9;
51 }
51 }
52
52
53 export function isDocumentTypeNode(el: any): el is DocumentType {
53 export function isDocumentTypeNode(el: any): el is DocumentType {
54 return isNode(el) && el.nodeType === 10;
54 return isNode(el) && el.nodeType === 10;
55 }
55 }
56
56
57 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
57 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
58 return isNode(el) && el.nodeType === 11;
58 return isNode(el) && el.nodeType === 11;
59 }
59 }
60
60
61 export function isWidget(v: any): v is _WidgetBase {
61 export function isWidget(v: any): v is _WidgetBase {
62 return v && "domNode" in v;
62 return v && "domNode" in v;
63 }
63 }
64
64
65 export function isRendition(v: any): v is Rendition {
65 export function isRendition(v: any): v is Rendition {
66 return typeof v === "object" && typeof v.getDomElement === "function";
66 return typeof v === "object" && typeof v.getDomElement === "function";
67 }
67 }
68
68
69 /**
69 /**
70 * @deprecated use isRendition
70 * @deprecated use isRendition
71 */
71 */
72 export const isBuildContext = isRendition;
72 export const isBuildContext = isRendition;
73
73
74 export function isPlainObject(v: object) {
74 export function isPlainObject(v: object) {
75 if (typeof v !== "object")
75 if (typeof v !== "object")
76 return false;
76 return false;
77
77
78 const vp = Object.getPrototypeOf(v);
78 const vp = Object.getPrototypeOf(v);
79 return !vp || vp === Object.prototype;
79 return !vp || vp === Object.prototype;
80 }
80 }
81
81
82 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
82 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
83 return typeof v === "function" && v.prototype && (
83 return typeof v === "function" && v.prototype && (
84 "domNode" in v.prototype ||
84 "domNode" in v.prototype ||
85 "buildRendering" in v.prototype
85 "buildRendering" in v.prototype
86 );
86 );
87 }
87 }
88
88
89 /** Tests whether the specified node is placed in visible dom.
89 /** Tests whether the specified node is placed in visible dom.
90 * @param {Node} node The node to test
90 * @param {Node} node The node to test
91 */
91 */
92 export function isInPage(node: Node) {
92 export function isInPage(node: Node) {
93 return (node === document.body) ? false : document.body.contains(node);
93 return (node === document.body) ? false : document.body.contains(node);
94 }
94 }
95
95
96 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
96 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
97 return target && typeof target.destroyRecursive === "function";
97 return target && typeof target.destroyRecursive === "function";
98 }
98 }
99
99
100
100
101 /** Destroys DOM Node with all contained widgets.
101 /** Destroys DOM Node with all contained widgets.
102 * If the specified node is the root node of a widget, then the
102 * If the specified node is the root node of a widget, then the
103 * widget will be destroyed.
103 * widget will be destroyed.
104 *
104 *
105 * @param target DOM Node or widget to destroy
105 * @param target DOM Node or widget to destroy
106 */
106 */
107 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
107 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
108 if (isRecursivelyDestroyable(target)) {
108 if (isRecursivelyDestroyable(target)) {
109 target.destroyRecursive();
109 target.destroyRecursive();
110 } else if (isDestroyable(target)) {
110 } else if (isDestroyable(target)) {
111 target.destroy();
111 target.destroy();
112 } else if (isNode(target)) {
112 } else if (isNode(target)) {
113 const w = isElementNode(target) ? registry.byNode(target) : undefined;
113 const w = isElementNode(target) ? registry.byNode(target) : undefined;
114 if (w) {
114 if (w) {
115 w.destroyRecursive();
115 w.destroyRecursive();
116 } else {
116 } else {
117 registry.findWidgets(target).forEach(destroy);
117 registry.findWidgets(target).forEach(destroy);
118 dom.destroy(target);
118 dom.destroy(target);
119 }
119 }
120 }
120 }
121 }
121 }
122
122
123 /** Empties a content of the specified node and destroys all contained widgets.
123 /** Empties a content of the specified node and destroys all contained widgets.
124 *
124 *
125 * @param target DOM node to .
125 * @param target DOM node to .
126 */
126 */
127 export function emptyNode(target: Node) {
127 export function emptyNode(target: Node) {
128 registry.findWidgets(target).forEach(destroy);
128 registry.findWidgets(target).forEach(destroy);
129 dom.empty(target);
129 dom.empty(target);
130 }
130 }
131
131
132 /** This function starts all widgets inside the DOM node if the target is a node
132 /** This function starts all widgets inside the DOM node if the target is a node
133 * or starts widget itself if the target is the widget. If the specified node
133 * or starts widget itself if the target is the widget. If the specified node
134 * associated with the widget that widget will be started.
134 * associated with the widget that widget will be started.
135 *
135 *
136 * @param target DOM node to find and start widgets or the widget itself.
136 * @param target DOM node to find and start widgets or the widget itself.
137 */
137 */
138 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
138 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
139 if (isNode(target)) {
139 if (isNode(target)) {
140 const w = isElementNode(target) ? registry.byNode(target) : undefined;
140 const w = isElementNode(target) ? registry.byNode(target) : undefined;
141 if (w) {
141 if (w) {
142 if (w.startup)
142 if (w.startup)
143 w.startup();
143 w.startup();
144 } else {
144 } else {
145 registry.findWidgets(target, skipNode).forEach(x => x.startup());
145 registry.findWidgets(target, skipNode).forEach(x => x.startup());
146 }
146 }
147 } else {
147 } else {
148 if (target.startup)
148 if (target.startup)
149 target.startup();
149 target.startup();
150 }
150 }
151 }
151 }
152
152
153
153
154 type StatefulProps<T> = T extends Stateful<infer A> ? A : never;
154 type StatefulProps<T> = T extends Stateful<infer A> ? A : never;
155
155
156 type CleanFn = (instance: IRemovable | IDestroyable) => void;
156 type CleanFn = (instance: IRemovable | IDestroyable) => void;
157
157
158 /**
158 /**
159 * Observers the property and calls render callback each change.
159 * Observers the property and calls render callback each change.
160 *
160 *
161 * @param target The target object which property will be observed.
161 * @param target The target object which property will be observed.
162 * @param prop The name of the property.
162 * @param prop The name of the property.
163 * @param render The callback which will be called every time the value is changed
163 * @param render The callback which will be called every time the value is changed
164 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
164 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
165 * @returns Rendition which is created instantly
165 * @returns Rendition which is created instantly
166 */
166 */
167 export function watch<W extends _WidgetBase, K extends keyof W>(
167 export function watch<W extends _WidgetBase, K extends keyof W>(
168 target: W,
168 target: W,
169 prop: K,
169 prop: K,
170 render: (model: W[K]) => any,
170 render: (model: W[K]) => any,
171 cleanupOrOwner?: { own: CleanFn } | CleanFn
171 cleanupOrOwner?: { own: CleanFn } | CleanFn
172 ): Rendition;
172 ): Rendition;
173 /**
173 /**
174 * Observers the property and calls render callback each change.
174 * Observers the property and calls render callback each change.
175 *
175 *
176 * @param target The target object which property will be observed.
176 * @param target The target object which property will be observed.
177 * @param prop The name of the property.
177 * @param prop The name of the property.
178 * @param render The callback which will be called every time the value is changed
178 * @param render The callback which will be called every time the value is changed
179 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
179 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
180 * @returns Rendition which is created instantly
180 * @returns Rendition which is created instantly
181 */
181 */
182 export function watch<T extends Stateful, K extends keyof StatefulProps<T>>(
182 export function watch<T extends Stateful, K extends keyof StatefulProps<T>>(
183 target: T,
183 target: T,
184 prop: K,
184 prop: K,
185 render: (model: StatefulProps<T>[K]) => any,
185 render: (model: StatefulProps<T>[K]) => any,
186 cleanupOrOwner?: { own: CleanFn } | CleanFn
186 cleanupOrOwner?: { own: CleanFn } | CleanFn
187 ): Rendition;
187 ): Rendition;
188 export function watch<T extends Stateful, K extends keyof StatefulProps<T> & string>(
188 export function watch<T extends Stateful, K extends keyof StatefulProps<T> & string>(
189 target: T,
189 target: T,
190 prop: K,
190 prop: K,
191 render: (model: StatefulProps<T>[K]) => any,
191 render: (model: StatefulProps<T>[K]) => any,
192 cleanupOrOwner: { own: CleanFn } | CleanFn = () => { }
192 cleanupOrOwner: { own: CleanFn } | CleanFn = () => { }
193 ) {
193 ) {
194 let rendition = new FunctionRendition(() => render(target.get(prop)));
194 let rendition = new FunctionRendition(() => render(target.get(prop)));
195 const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x)
195 const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x);
196 _own(target.watch(prop, (_name, oldValue, newValue) => {
196 _own(target.watch(prop, (_name, oldValue, newValue) => {
197 if (oldValue !== newValue) {
197 if (oldValue !== newValue) {
198 const newRendition = new FunctionRendition(() => render(newValue));
198 const newRendition = new FunctionRendition(() => render(newValue));
199 newRendition.placeAt(rendition.getDomNode(), "replace");
199 newRendition.placeAt(rendition.getDomNode(), "replace");
200 destroy(rendition.getDomNode());
200 destroy(rendition.getDomNode());
201 rendition = newRendition;
201 rendition = newRendition;
202 }
202 }
203 }));
203 }));
204 return rendition;
204 return rendition;
205 }
205 }
206
206
207 /** Decorates the method which will be registered as the handle for the specified event.
207 /** Decorates the method which will be registered as the handle for the specified event.
208 * This decorator can be applied to DjxWidgetBase subclass methods.
208 * This decorator can be applied to DjxWidgetBase subclass methods.
209 *
209 *
210 * ```
210 * ```
211 * @on("click")
211 * @on("click")
212 * _onClick(eventObj: MouseEvent) {
212 * _onClick(eventObj: MouseEvent) {
213 * // ...
213 * // ...
214 * }
214 * }
215 * ```
215 * ```
216 */
216 */
217 export const on = <E extends string>(eventName: E) =>
217 export const on = <E extends string>(eventName: E) =>
218 <K extends keyof T,
218 <K extends string,
219 T extends DjxWidgetBase<any, { [p in E]: EV }>,
219 T extends DjxWidgetBase<any, { [p in E]: EV }>,
220 EV extends Event
220 EV extends Event
221 >(
221 >(
222 target: T,
222 target: T,
223 key: K,
223 key: K,
224 descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
224 _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
225 ): any => {
225 ): any => {
226 target._eventHandlers.push({ eventName, handlerMethod: key });
226 target._eventHandlers.push({ eventName, handlerMethod: key });
227 };
227 };
@@ -1,10 +1,12
1 import { Baz } from "./mock/Baz";
1 import { Baz } from "./mock/Baz";
2
2
3 // tslint:disable-next-line: no-console
3 console.log("Declare tests");
4 console.log("Declare tests");
4
5
5 const baz = new Baz();
6 const baz = new Baz();
6
7
7 const data: string[] = [];
8 const data: string[] = [];
8 baz.writeHello(data);
9 baz.writeHello(data);
9
10
11 // tslint:disable-next-line: no-console
10 console.log(data.join("\n"));
12 console.log(data.join("\n"));
@@ -1,17 +1,16
1 import { test } from "./TestTraits";
1 import { test } from "./TestTraits";
2 import { delay } from "@implab/core-amd/safe";
2 import { delay } from "@implab/core-amd/safe";
3 import { assert } from "chai";
3 import { assert } from "chai";
4 import css = require("@implab/djx/css!my.css");
5
4
6 test("simple", (ok, fail, log) => {
5 test("simple", (ok, fail, log) => {
7 setTimeout(() => {
6 setTimeout(() => {
8 // end should be called after the last assertion
7 // end should be called after the last assertion
9 ok("async assert");
8 ok("async assert");
10 }, 100);
9 }, 100);
11 });
10 });
12
11
13 test("simple", async (log, fail) => {
12 test("simple", async (log, fail) => {
14 await delay(0);
13 await delay(0);
15
14
16 assert.ok(true); // everything is fine
15 assert.ok(true); // everything is fine
17 });
16 });
@@ -1,71 +1,71
1 import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare";
1 import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare";
2
2
3 import { DjxWidgetBase } from "../tsx/DjxWidgetBase";
3 import { DjxWidgetBase } from "../tsx/DjxWidgetBase";
4 import { createElement } from "../tsx";
4 import { createElement } from "../tsx";
5 import { on } from "../tsx/traits";
5 import { on } from "../tsx/traits";
6
6
7 interface MyWidgetAttrs {
7 interface MyWidgetAttrs {
8 title: string;
8 title: string;
9
9
10 counter: number;
10 counter: number;
11 }
11 }
12
12
13 interface MyWidgetEvents {
13 interface MyWidgetEvents {
14 "count-inc": Event & {
14 "count-inc": Event & {
15 detail: number;
15 detail: number;
16 };
16 };
17
17
18 "count-dec": Event & {
18 "count-dec": Event & {
19 detail: number;
19 detail: number;
20 };
20 };
21 }
21 }
22
22
23
23
24 @djclass
24 @djclass
25 export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) {
25 export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) {
26
26
27 @bind({ node: "titleNode", type: "innerHTML" })
27 @bind({ node: "titleNode", type: "innerHTML" })
28 title = "";
28 title = "";
29
29
30 @prototype()
30 @prototype()
31 counter = 0;
31 counter = 0;
32
32
33 render() {
33 render() {
34 const Frame = (props: any) => <div>{props.children}</div>;
34 const Frame = (props: any) => <div>{props.children}</div>;
35 return <div className="myWidget" onsubmit={e => this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} >
35 return <div className="myWidget" onsubmit={e => this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} >
36 <h1 data-dojo-attach-point="titleNode"></h1>
36 <h1 data-dojo-attach-point="titleNode"></h1>
37 <Frame>
37 <Frame>
38 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
38 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
39 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
39 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
40 </Frame>
40 </Frame>
41 </div>;
41 </div>;
42 }
42 }
43
43
44 postCreate() {
44 postCreate() {
45 super.postCreate();
45 super.postCreate();
46
46
47 this.on("click", () => {});
47 this.on("click", () => {});
48 }
48 }
49
49
50 _onSubmit(e: Event) {
50 _onSubmit(e: Event) {
51 }
51 }
52
52
53 _onIncClick(e: MouseEvent) {
53 _onIncClick(e: MouseEvent) {
54 this.set("counter", this.counter + 1);
54 this.set("counter", this.counter + 1);
55
55
56 this.emit("count-inc", { bubbles: false });
56 this.emit("count-inc", { bubbles: false });
57 }
57 }
58
58
59 _onDecClick() {
59 _onDecClick() {
60 this.emit("count-dec", { bubbles: false, detail: this.counter });
60 this.emit("count-dec", { bubbles: false, detail: this.counter });
61 }
61 }
62
62
63 @on("count-inc")
63 @on("count-inc")
64 _onCounterInc(evt: Event & { detail: number; x?: number; }) {
64 _onCounterInc(evt: Event & { detail: number; x?: number; }) {
65 }
65 }
66
66
67 @on("click")
67 @on("click")
68 _onClick() {
68 protected _onClick() {
69
69
70 }
70 }
71 } No newline at end of file
71 }
@@ -1,14 +1,15
1 {
1 {
2 "compilerOptions": {
2 "compilerOptions": {
3 "moduleResolution": "node",
3 "moduleResolution": "node",
4 "noEmitOnError": true,
4 "noEmitOnError": true,
5 "strict": true,
5 "strict": true,
6 "types": [],
6 "types": [],
7 "experimentalDecorators": true,
7 "experimentalDecorators": true,
8 "jsxFactory": "createElement",
8 "jsxFactory": "createElement",
9 "target": "ES5",
9 "target": "ES5",
10 //"skipLibCheck": true,
10 //"skipLibCheck": true,
11 "jsx": "react",
11 "jsx": "react",
12 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]
12 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"],
13 "noUnusedLocals": true
13 }
14 }
14 } No newline at end of file
15 }
General Comments 0
You need to be logged in to leave comments. Login now