##// END OF EJS Templates
Changed @djclass decorator to support legacy dojo versions (<1.15)
cin -
r11:5a2c44d8e1f3 v1.0.0-rc5 default
parent child
Show More
@@ -1,236 +1,240
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 { DeclareConstructor } from "dojo/_base/declare";
4 // import { DeclareConstructor } from "dojo/_base/declare";
5
5
6 type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
6 type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
7
7
8 export interface AbstractConstructor<T = {}> {
8 export interface AbstractConstructor<T = {}> {
9 prototype: T;
9 prototype: T;
10 }
10 }
11
11
12 interface DjMockConstructor<T = {}> {
12 interface DjMockConstructor<T = {}> {
13 new(...args: any[]): T;
13 new(...args: any[]): T;
14 mock: boolean;
14 mock: boolean;
15 base: AbstractConstructor;
15 base: AbstractConstructor;
16 }
16 }
17
17
18 export function djbase<T>(
18 export function djbase<T>(
19 b0: AbstractConstructor<T>
19 b0: AbstractConstructor<T>
20 ): DeclareConstructor<T>;
20 ): DeclareConstructor<T>;
21
21
22 export function djbase<T0, T1>(
22 export function djbase<T0, T1>(
23 b0: AbstractConstructor<T0>,
23 b0: AbstractConstructor<T0>,
24 b1: AbstractConstructor<T1>
24 b1: AbstractConstructor<T1>
25 ): DeclareConstructor<T0 & T1>;
25 ): DeclareConstructor<T0 & T1>;
26
26
27 export function djbase<T0, T1, T2>(
27 export function djbase<T0, T1, T2>(
28 b0: AbstractConstructor<T0>,
28 b0: AbstractConstructor<T0>,
29 b1: AbstractConstructor<T1>,
29 b1: AbstractConstructor<T1>,
30 b2: AbstractConstructor<T2>
30 b2: AbstractConstructor<T2>
31 ): DeclareConstructor<T0 & T1 & T2>;
31 ): DeclareConstructor<T0 & T1 & T2>;
32
32
33 export function djbase<T0, T1, T2, T3>(
33 export function djbase<T0, T1, T2, T3>(
34 b0: AbstractConstructor<T0>,
34 b0: AbstractConstructor<T0>,
35 b1: AbstractConstructor<T1>,
35 b1: AbstractConstructor<T1>,
36 b2: AbstractConstructor<T2>,
36 b2: AbstractConstructor<T2>,
37 b3: AbstractConstructor<T3>
37 b3: AbstractConstructor<T3>
38 ): DeclareConstructor<T0 & T1 & T2 & T3>;
38 ): DeclareConstructor<T0 & T1 & T2 & T3>;
39
39
40 export function djbase<T0, T1, T2, T3, T4>(
40 export function djbase<T0, T1, T2, T3, T4>(
41 b0: AbstractConstructor<T0>,
41 b0: AbstractConstructor<T0>,
42 b1: AbstractConstructor<T1>,
42 b1: AbstractConstructor<T1>,
43 b2: AbstractConstructor<T2>,
43 b2: AbstractConstructor<T2>,
44 b3: AbstractConstructor<T3>,
44 b3: AbstractConstructor<T3>,
45 b4: AbstractConstructor<T4>
45 b4: AbstractConstructor<T4>
46 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4>;
46 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4>;
47
47
48 export function djbase<T0, T1, T2, T3, T4, T5>(
48 export function djbase<T0, T1, T2, T3, T4, T5>(
49 b0: AbstractConstructor<T0>,
49 b0: AbstractConstructor<T0>,
50 b1: AbstractConstructor<T1>,
50 b1: AbstractConstructor<T1>,
51 b2: AbstractConstructor<T2>,
51 b2: AbstractConstructor<T2>,
52 b3: AbstractConstructor<T3>,
52 b3: AbstractConstructor<T3>,
53 b4: AbstractConstructor<T4>,
53 b4: AbstractConstructor<T4>,
54 b5: AbstractConstructor<T5>
54 b5: AbstractConstructor<T5>
55 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>;
55 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>;
56
56
57 export function djbase<T0, T1, T2, T3, T4, T5, T6>(
57 export function djbase<T0, T1, T2, T3, T4, T5, T6>(
58 b0: AbstractConstructor<T0>,
58 b0: AbstractConstructor<T0>,
59 b1: AbstractConstructor<T1>,
59 b1: AbstractConstructor<T1>,
60 b2: AbstractConstructor<T2>,
60 b2: AbstractConstructor<T2>,
61 b3: AbstractConstructor<T3>,
61 b3: AbstractConstructor<T3>,
62 b4: AbstractConstructor<T4>,
62 b4: AbstractConstructor<T4>,
63 b5: AbstractConstructor<T5>,
63 b5: AbstractConstructor<T5>,
64 b6: AbstractConstructor<T6>
64 b6: AbstractConstructor<T6>
65 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>;
65 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>;
66
66
67 export function djbase<T0, T1, T2, T3, T4, T5, T6, T7>(
67 export function djbase<T0, T1, T2, T3, T4, T5, T6, T7>(
68 b0: AbstractConstructor<T0>,
68 b0: AbstractConstructor<T0>,
69 b1: AbstractConstructor<T1>,
69 b1: AbstractConstructor<T1>,
70 b2: AbstractConstructor<T2>,
70 b2: AbstractConstructor<T2>,
71 b3: AbstractConstructor<T3>,
71 b3: AbstractConstructor<T3>,
72 b4: AbstractConstructor<T4>,
72 b4: AbstractConstructor<T4>,
73 b5: AbstractConstructor<T5>,
73 b5: AbstractConstructor<T5>,
74 b6: AbstractConstructor<T6>,
74 b6: AbstractConstructor<T6>,
75 b7: AbstractConstructor<T7>
75 b7: AbstractConstructor<T7>
76 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>;
76 ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>;
77
77
78 /** Создает конструктор-заглушку из списка базовых классов, используется
78 /** Создает конструктор-заглушку из списка базовых классов, используется
79 * для объявления классов при помощи `dojo/_base/declare`.
79 * для объявления классов при помощи `dojo/_base/declare`.
80 *
80 *
81 * Создает пустой конструтор, с пустым стандартным прототипом, это нужно,
81 * Создает пустой конструтор, с пустым стандартным прототипом, это нужно,
82 * поскольку в унаследованном классе конструктор обязательно должен вызвать
82 * поскольку в унаследованном классе конструктор обязательно должен вызвать
83 * `super(...)`, таким образом он вызовет пустую функцию.
83 * `super(...)`, таким образом он вызовет пустую функцию.
84 *
84 *
85 * Созданный конструтор хранит в себе список базовых классов, который будет
85 * Созданный конструтор хранит в себе список базовых классов, который будет
86 * использован декоратором `djclass`, который вернет класс, объявленный при
86 * использован декоратором `djclass`, который вернет класс, объявленный при
87 * помощи `dojo/_base/declare`.
87 * помощи `dojo/_base/declare`.
88 *
88 *
89 * @param bases список базовых классов, от которых требуется унаследовать
89 * @param bases список базовых классов, от которых требуется унаследовать
90 * новый класс.
90 * новый класс.
91 *
91 *
92 */
92 */
93 export function djbase(...bases: any[]): Constructor {
93 export function djbase(...bases: any[]): Constructor {
94
94
95 const t = class {
95 const t = class {
96 static mock: boolean;
96 static mock: boolean;
97 static base: AbstractConstructor;
97 static base: AbstractConstructor;
98 };
98 };
99
99
100 t.mock = true;
100 t.mock = true;
101 t.base = declare(bases);
101 t.base = declare(bases);
102
102
103 return t as any;
103 return t as any;
104 }
104 }
105
105
106 function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
106 function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
107 return v && "mock" in v;
107 return v && "mock" in v;
108 }
108 }
109
109
110 /** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс
110 /** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс
111 * должен быть унаследован от `djbase(...)`.
111 * должен быть унаследован от `djbase(...)`.
112 *
112 *
113 * @param target Класс, который нужно объявить при помощи `dojo/_base/declare`
113 * @param target Класс, который нужно объявить при помощи `dojo/_base/declare`
114 */
114 */
115 export function djclass<T extends AbstractConstructor>(target: T): T {
115 export function djclass<T extends AbstractConstructor>(target: T): T {
116 // получаем базовый конструктор и его прототип
116 // получаем базовый конструктор и его прототип
117 let bp = target && target.prototype && Object.getPrototypeOf(target.prototype);
117 let bp = target && target.prototype && Object.getPrototypeOf(target.prototype);
118 const bc = bp && bp.constructor;
118 const bc = bp && bp.constructor;
119
119
120 // проверка того, что класс унаследован от специальной заглушки
120 // проверка того, что класс унаследован от специальной заглушки
121 if (isMockConstructor(bc)) {
121 if (isMockConstructor(bc)) {
122 // t - базовый класс, объявленный при помощи dojo/_base/declare
122 // t - базовый класс, объявленный при помощи dojo/_base/declare
123 const t = bc.base;
123 const t = bc.base;
124
124
125 // bc - базовый класс, bc.prototype используется как super
125 // bc - базовый класс, bc.prototype используется как super
126 // при вызове базовых методов. Нужно создать bc.prototype
126 // при вызове базовых методов. Нужно создать bc.prototype
127 // таким образом, чтобы он вызывал this.inherited().
127 // таким образом, чтобы он вызывал this.inherited().
128
128
129 // создаем новый порототип, он не в цепочке прототипов у текущего
129 // создаем новый порототип, он не в цепочке прототипов у текущего
130 // класса, но super.some_method будет использовать именно его.
130 // класса, но super.some_method будет использовать именно его.
131 // в этом объекте будут размещены прокси для переопределенных
131 // в этом объекте будут размещены прокси для переопределенных
132 // методов.
132 // методов.
133 bp = bc.prototype = Object.create(t.prototype);
133 bp = bc.prototype = Object.create(t.prototype);
134 bp.constructor = bc;
134 bp.constructor = bc;
135
135
136 // proxy - фабрика для создания прокси-методов, которые внутри
136 // proxy - фабрика для создания прокси-методов, которые внутри
137 // себя вызовут this.inherited с правильными параметрами.
137 // себя вызовут this.inherited с правильными параметрами.
138 const proxy = (m: (...args: any[]) => any) => function (this: any) {
138 const proxy = (m: (...args: any[]) => any) => function (this: any) {
139 return this.inherited(m, arguments);
139 const f = this.getInherited({ callee: m });
140 return f && f.apply(this, arguments);
141
142 // так сделать можно только dojo 1.15+
143 // return this.inherited(m, arguments);
140 };
144 };
141
145
142 // у текущего класса прототип содержит методы, объявленные в этом
146 // у текущего класса прототип содержит методы, объявленные в этом
143 // классе и его конструктор. Нужно пройти по всем методам и
147 // классе и его конструктор. Нужно пройти по всем методам и
144 // создать для них прокси.
148 // создать для них прокси.
145 // При этом только те, методы, которые есть в базовых классах
149 // При этом только те, методы, которые есть в базовых классах
146 // могут быть переопределены.
150 // могут быть переопределены.
147 each(target.prototype, (m: any, p: string | number | symbol) => {
151 each(target.prototype, (m: any, p: string | number | symbol) => {
148 if (typeof m === "function" &&
152 if (typeof m === "function" &&
149 p !== "constructor" &&
153 p !== "constructor" &&
150 target.prototype.hasOwnProperty(p) &&
154 target.prototype.hasOwnProperty(p) &&
151 p in t.prototype
155 p in t.prototype
152 ) {
156 ) {
153 bp[p] = proxy(m);
157 bp[p] = proxy(m);
154 }
158 }
155 });
159 });
156
160
157 const cls = declare(t, target.prototype);
161 const cls = declare(t, target.prototype);
158 // TODO mixin static members
162 // TODO mixin static members
159 return cls as any;
163 return cls as any;
160 } else {
164 } else {
161 return target as any;
165 return target as any;
162 }
166 }
163 }
167 }
164
168
165 function makeSetterName(prop: string) {
169 function makeSetterName(prop: string) {
166 return [
170 return [
167 "_set",
171 "_set",
168 prop.replace(/^./, x => x.toUpperCase()),
172 prop.replace(/^./, x => x.toUpperCase()),
169 "Attr"
173 "Attr"
170 ].join("");
174 ].join("");
171 }
175 }
172
176
173 function makeGetterName(prop: string) {
177 function makeGetterName(prop: string) {
174 return [
178 return [
175 "_get",
179 "_get",
176 prop.replace(/^./, x => x.toUpperCase()),
180 prop.replace(/^./, x => x.toUpperCase()),
177 "Attr"
181 "Attr"
178 ].join("");
182 ].join("");
179 }
183 }
180
184
181 interface NodeBindSpec {
185 interface NodeBindSpec {
182 node: string;
186 node: string;
183 type: string;
187 type: string;
184 }
188 }
185
189
186 /**
190 /**
187 * Описание привязки свойства виджета к свойству внутреннего объекта.
191 * Описание привязки свойства виджета к свойству внутреннего объекта.
188 */
192 */
189 interface MemberBindSpec {
193 interface MemberBindSpec {
190 /**
194 /**
191 * Имя свойства со ссылкой на объект, к которому .
195 * Имя свойства со ссылкой на объект, к которому .
192 */
196 */
193 member: string;
197 member: string;
194 /**
198 /**
195 * Свойство объекта к которому нужно осуществить привязку.
199 * Свойство объекта к которому нужно осуществить привязку.
196 */
200 */
197 property: string;
201 property: string;
198
202
199 /**
203 /**
200 * Привязка осуществляется не только на запись но и на чтение свойства.
204 * Привязка осуществляется не только на запись но и на чтение свойства.
201 */
205 */
202 getter?: boolean;
206 getter?: boolean;
203 }
207 }
204
208
205 function isNodeBindSpec(v: any): v is NodeBindSpec {
209 function isNodeBindSpec(v: any): v is NodeBindSpec {
206 return "node" in v;
210 return "node" in v;
207 }
211 }
208
212
209 function isMemberBindSpec(v: any): v is MemberBindSpec {
213 function isMemberBindSpec(v: any): v is MemberBindSpec {
210 return "member" in v;
214 return "member" in v;
211 }
215 }
212
216
213 /** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM
217 /** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM
214 * элементам, либо свойству внутреннего объекта.
218 * элементам, либо свойству внутреннего объекта.
215 *
219 *
216 * @param {NodeBindSpec | MemberBindSpec} params Параметры связывания.
220 * @param {NodeBindSpec | MemberBindSpec} params Параметры связывания.
217 */
221 */
218 export function bind(params: NodeBindSpec | MemberBindSpec) {
222 export function bind(params: NodeBindSpec | MemberBindSpec) {
219 if (isNodeBindSpec(params))
223 if (isNodeBindSpec(params))
220 return (target: any, name: string) => {
224 return (target: any, name: string) => {
221 target[makeSetterName(name)] = params;
225 target[makeSetterName(name)] = params;
222 };
226 };
223 else if (isMemberBindSpec(params)) {
227 else if (isMemberBindSpec(params)) {
224 return (target: any, name: string) => {
228 return (target: any, name: string) => {
225 target[name] = null;
229 target[name] = null;
226 target[makeSetterName(name)] = function (v: any) {
230 target[makeSetterName(name)] = function (v: any) {
227 this._set(name, v);
231 this._set(name, v);
228 this[params.member].set(params.property, v);
232 this[params.member].set(params.property, v);
229 };
233 };
230 if (params.getter)
234 if (params.getter)
231 target[makeGetterName(name)] = function () {
235 target[makeGetterName(name)] = function () {
232 return this[params.member].get(params.property);
236 return this[params.member].get(params.property);
233 };
237 };
234 };
238 };
235 }
239 }
236 }
240 }
General Comments 0
You need to be logged in to leave comments. Login now