##// END OF EJS Templates
Movin the observable implementation to the class
Movin the observable implementation to the class

File last commit:

r109:4a375b9c654a default
r153:8ae7abbb3114 default
Show More
declare.ts
255 lines | 10.0 KiB | video/mp2t | TypeScriptLexer
cin
Converted to subproject djx, removed dojo-typings
r65 import declare = require("dojo/_base/declare");
import { each } from "@implab/core-amd/safe";
import { Constructor } from "@implab/core-amd/interfaces";
// declare const declare: any;
type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
cin
Working on WatchForRendition
r107 export interface AbstractConstructor<T = object> {
cin
Converted to subproject djx, removed dojo-typings
r65 prototype: T;
}
cin
Working on WatchForRendition
r107 interface DjMockConstructor<T = object> {
new(...args: unknown[]): T;
cin
Converted to subproject djx, removed dojo-typings
r65 mock: boolean;
bases: AbstractConstructor[];
}
cin
Working on WatchForRendition
r107 export function djbase<T>(): DeclareConstructor<T>;
cin
Converted to subproject djx, removed dojo-typings
r65 export function djbase<T>(
cin
Working on WatchForRendition
r107 b0: AbstractConstructor<T>
cin
Converted to subproject djx, removed dojo-typings
r65 ): DeclareConstructor<T>;
export function djbase<T0, T1>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>
): DeclareConstructor<T0 & T1>;
export function djbase<T0, T1, T2>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>
): DeclareConstructor<T0 & T1 & T2>;
export function djbase<T0, T1, T2, T3>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>,
b3: AbstractConstructor<T3>
): DeclareConstructor<T0 & T1 & T2 & T3>;
export function djbase<T0, T1, T2, T3, T4>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>,
b3: AbstractConstructor<T3>,
b4: AbstractConstructor<T4>
): DeclareConstructor<T0 & T1 & T2 & T3 & T4>;
export function djbase<T0, T1, T2, T3, T4, T5>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>,
b3: AbstractConstructor<T3>,
b4: AbstractConstructor<T4>,
b5: AbstractConstructor<T5>
): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>;
export function djbase<T0, T1, T2, T3, T4, T5, T6>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>,
b3: AbstractConstructor<T3>,
b4: AbstractConstructor<T4>,
b5: AbstractConstructor<T5>,
b6: AbstractConstructor<T6>
): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>;
export function djbase<T0, T1, T2, T3, T4, T5, T6, T7>(
b0: AbstractConstructor<T0>,
b1: AbstractConstructor<T1>,
b2: AbstractConstructor<T2>,
b3: AbstractConstructor<T3>,
b4: AbstractConstructor<T4>,
b5: AbstractConstructor<T5>,
b6: AbstractConstructor<T6>,
b7: AbstractConstructor<T7>
): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>;
/** Создает конструктор-заглушку из списка базовых классов, используется
* для объявления классов при помощи `dojo/_base/declare`.
*
* Создает пустой конструктор, с пустым стандартным прототипом, это нужно,
* поскольку в унаследованном классе конструктор обязательно должен вызвать
* `super(...)`, таким образом он вызовет пустую функцию.
*
* Созданный конструктор хранит в себе список базовых классов, который будет
* использован декоратором `djclass`, который вернет класс, объявленный при
* помощи `dojo/_base/declare`.
*
* @param bases список базовых классов, от которых требуется унаследовать
* новый класс.
*
*/
cin
Working on WatchForRendition
r107 export function djbase(...bases: AbstractConstructor[]): Constructor {
cin
Converted to subproject djx, removed dojo-typings
r65
const t = class {
static mock: boolean;
static bases: AbstractConstructor[];
};
t.mock = true;
t.bases = bases;
cin
Working on WatchForRendition
r107 return t as Constructor;
cin
Converted to subproject djx, removed dojo-typings
r65 }
cin
Working on WatchForRendition
r107 function isMockConstructor<T extends object>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
cin
Converted to subproject djx, removed dojo-typings
r65 return v && "mock" in v;
}
/** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс
* должен быть унаследован от `djbase(...)`.
*
* @param target Класс, который нужно объявить при помощи `dojo/_base/declare`
*/
export function djclass<T extends AbstractConstructor>(target: T): T {
// получаем базовый конструктор и его прототип
cin
Working on WatchForRendition
r107 const bp = target && !!target.prototype && Object.getPrototypeOf(target.prototype) as object;
cin
Converted to subproject djx, removed dojo-typings
r65 const bc = bp && bp.constructor;
// проверка того, что класс унаследован от специальной заглушки
if (isMockConstructor(bc)) {
cin
fixed tslint errors, added support for private methods to @on() decorator
r79 // bc.bases - базовый класс, объявленный при помощи dojo/_base/declare
cin
Working on WatchForRendition
r107 const cls = declare(bc.bases, target.prototype) as unknown as T;
cin
Converted to subproject djx, removed dojo-typings
r65
// bc - базовый класс, bc.prototype используется как super
// при вызове базовых методов. Нужно создать bc.prototype
// таким образом, чтобы он вызывал this.inherited().
cin
Working on WatchForRendition
r107 // создаем новый прототип, он не в цепочке прототипов у текущего
cin
Converted to subproject djx, removed dojo-typings
r65 // класса, но super.some_method будет использовать именно его.
// в этом объекте будут размещены прокси для переопределенных
// методов.
cin
Working on WatchForRendition
r107 const nbp = bc.prototype = Object.create(cls.prototype) as Record<string, unknown>;
nbp.constructor = bc;
cin
Converted to subproject djx, removed dojo-typings
r65
// proxy - фабрика для создания прокси-методов, которые внутри
// себя вызовут this.inherited с правильными параметрами.
cin
linting
r109 const proxy = (m: (...args: unknown[]) => unknown) => function (this: dojo._base.DeclareCreatedObject) {
const f = this.getInherited({ callee: m } as unknown as IArguments);
// eslint-disable-next-line prefer-rest-params
return f ? f.apply(this, arguments) as unknown : undefined;
cin
Converted to subproject djx, removed dojo-typings
r65
// так сделать можно только dojo 1.15+
// return this.inherited(m, arguments);
};
// у текущего класса прототип содержит методы, объявленные в этом
// классе и его конструктор. Нужно пройти по всем методам и
// создать для них прокси.
// При этом только те, методы, которые есть в базовых классах
// могут быть переопределены.
cin
Working on WatchForRendition
r107 each(target.prototype, (m: unknown, p: string) => {
cin
Converted to subproject djx, removed dojo-typings
r65 if (typeof m === "function" &&
p !== "constructor" &&
cin
linting
r109 Object.prototype.hasOwnProperty.call(target.prototype, p)
cin
Converted to subproject djx, removed dojo-typings
r65 ) {
cin
Working on WatchForRendition
r107 nbp[p] = proxy(m as (...args: unknown[]) => unknown);
cin
Converted to subproject djx, removed dojo-typings
r65 }
});
// TODO mixin static members
cin
Working on WatchForRendition
r107 return cls;
cin
Converted to subproject djx, removed dojo-typings
r65 } else {
cin
Working on WatchForRendition
r107 return target;
cin
Converted to subproject djx, removed dojo-typings
r65 }
}
function makeSetterName(prop: string) {
return [
"_set",
prop.replace(/^./, x => x.toUpperCase()),
"Attr"
].join("");
}
function makeGetterName(prop: string) {
return [
"_get",
prop.replace(/^./, x => x.toUpperCase()),
"Attr"
].join("");
}
interface NodeBindSpec {
node: string;
type: "attribute" | "innerText" | "textContent" | "innerHTML" | "class" | "toggleClass";
attribute?: string;
className?: string;
}
/**
* Описание привязки свойства виджета к свойству внутреннего объекта.
*/
interface MemberBindSpec {
/**
* Имя свойства со ссылкой на объект, к которому .
*/
member: string;
/**
* Свойство объекта к которому нужно осуществить привязку.
*/
property: string;
/**
* Привязка осуществляется не только на запись но и на чтение свойства.
*/
getter?: boolean;
}
cin
Working on WatchForRendition
r107 function isNodeBindSpec(v: object): v is NodeBindSpec {
cin
Converted to subproject djx, removed dojo-typings
r65 return "node" in v;
}
/** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM
* элементам, либо свойству внутреннего объекта.
*
* @param {NodeBindSpec | MemberBindSpec} params Параметры связывания.
*/
export function bind(params: NodeBindSpec | MemberBindSpec) {
if (isNodeBindSpec(params)) {
cin
linting
r108 return <K extends string>(target: Record<K, unknown>, name: K) => {
target[makeSetterName(name) as K /** hack to not go insane) */] = params;
cin
Converted to subproject djx, removed dojo-typings
r65 };
} else {
cin
linting
r109 return <K extends string>(target: Record<K | "_set", unknown>, name: K) => {
target[name] = undefined;
target[makeSetterName(name) as K] = function (this: typeof target, v: unknown) {
(this._set as (n: K, v: unknown) => void)(name, v);
const inner = this[params.member as K] as Record<string, unknown>;
cin
linting
r108 if (typeof inner.set === "function")
cin
Working on WatchForRendition
r107 inner.set(params.property, v);
cin
linting
r109 };
cin
Converted to subproject djx, removed dojo-typings
r65 if (params.getter)
cin
linting
r109 target[makeGetterName(name) as K] = function (this: typeof target) {
const inner = this[params.member as K] as Record<string, unknown>;
cin
linting
r108 if (typeof inner.get === "function")
cin
Working on WatchForRendition
r107 return inner.get(params.property) as unknown;
cin
Converted to subproject djx, removed dojo-typings
r65 };
};
}
}
/** Создает в прототипе свойство с указанным значением.
* @param value Значение, которое будет указано в прототипе
*/
export function prototype<T>(value: T): <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => void;
export function prototype<T>(value?: T) {
cin
linting
r108 return (p: { [m in string]: T | undefined }, name: string) => {
p[name] = value;
cin
Converted to subproject djx, removed dojo-typings
r65 };
}