declare.ts
256 lines
| 9.7 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r0 | import declare = require("dojo/_base/declare"); | ||
| import { each } from "@implab/core-amd/safe"; | ||||
| import { Constructor } from "@implab/core-amd/interfaces"; | ||||
|
|
r21 | import dojo = require("dojo/_base/kernel"); | ||
|
|
r4 | |||
|
|
r15 | // declare const declare: any; | ||
|
|
r21 | type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>; | ||
|
|
r0 | |||
| export interface AbstractConstructor<T = {}> { | ||||
| prototype: T; | ||||
| } | ||||
| interface DjMockConstructor<T = {}> { | ||||
| new(...args: any[]): T; | ||||
| mock: boolean; | ||||
|
|
r32 | bases: AbstractConstructor[]; | ||
|
|
r0 | } | ||
| export function djbase<T>( | ||||
|
|
r32 | b0?: AbstractConstructor<T> | ||
|
|
r2 | ): DeclareConstructor<T>; | ||
|
|
r0 | |||
| export function djbase<T0, T1>( | ||||
| b0: AbstractConstructor<T0>, | ||||
| b1: AbstractConstructor<T1> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1>; | ||
|
|
r0 | |||
| export function djbase<T0, T1, T2>( | ||||
| b0: AbstractConstructor<T0>, | ||||
| b1: AbstractConstructor<T1>, | ||||
| b2: AbstractConstructor<T2> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2>; | ||
|
|
r0 | |||
| export function djbase<T0, T1, T2, T3>( | ||||
| b0: AbstractConstructor<T0>, | ||||
| b1: AbstractConstructor<T1>, | ||||
| b2: AbstractConstructor<T2>, | ||||
| b3: AbstractConstructor<T3> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2 & T3>; | ||
|
|
r0 | |||
| export function djbase<T0, T1, T2, T3, T4>( | ||||
| b0: AbstractConstructor<T0>, | ||||
| b1: AbstractConstructor<T1>, | ||||
| b2: AbstractConstructor<T2>, | ||||
| b3: AbstractConstructor<T3>, | ||||
| b4: AbstractConstructor<T4> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2 & T3 & T4>; | ||
|
|
r0 | |||
| 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> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5>; | ||
|
|
r0 | |||
| 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> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6>; | ||
|
|
r0 | |||
| 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> | ||||
|
|
r2 | ): DeclareConstructor<T0 & T1 & T2 & T3 & T4 & T5 & T6 & T7>; | ||
|
|
r0 | |||
| /** Создает конструктор-заглушку из списка базовых классов, используется | ||||
| * для объявления классов при помощи `dojo/_base/declare`. | ||||
| * | ||||
|
|
r28 | * Создает пустой конструктор, с пустым стандартным прототипом, это нужно, | ||
|
|
r0 | * поскольку в унаследованном классе конструктор обязательно должен вызвать | ||
| * `super(...)`, таким образом он вызовет пустую функцию. | ||||
| * | ||||
|
|
r28 | * Созданный конструктор хранит в себе список базовых классов, который будет | ||
|
|
r0 | * использован декоратором `djclass`, который вернет класс, объявленный при | ||
| * помощи `dojo/_base/declare`. | ||||
| * | ||||
| * @param bases список базовых классов, от которых требуется унаследовать | ||||
| * новый класс. | ||||
| * | ||||
| */ | ||||
| export function djbase(...bases: any[]): Constructor { | ||||
| const t = class { | ||||
| static mock: boolean; | ||||
|
|
r32 | static bases: AbstractConstructor[]; | ||
|
|
r0 | }; | ||
| t.mock = true; | ||||
|
|
r32 | t.bases = bases; | ||
|
|
r0 | |||
| return t as any; | ||||
| } | ||||
| function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> { | ||||
| return v && "mock" in v; | ||||
| } | ||||
| /** Создает класс при помощи `dojo/_base/declare`. Для этого исходный класс | ||||
| * должен быть унаследован от `djbase(...)`. | ||||
| * | ||||
| * @param target Класс, который нужно объявить при помощи `dojo/_base/declare` | ||||
| */ | ||||
| export function djclass<T extends AbstractConstructor>(target: T): T { | ||||
| // получаем базовый конструктор и его прототип | ||||
| let bp = target && target.prototype && Object.getPrototypeOf(target.prototype); | ||||
| const bc = bp && bp.constructor; | ||||
| // проверка того, что класс унаследован от специальной заглушки | ||||
| if (isMockConstructor(bc)) { | ||||
| // t - базовый класс, объявленный при помощи dojo/_base/declare | ||||
|
|
r32 | const t = bc.bases; | ||
| const cls = declare(bc.bases, target.prototype); | ||||
|
|
r0 | |||
| // bc - базовый класс, bc.prototype используется как super | ||||
| // при вызове базовых методов. Нужно создать bc.prototype | ||||
| // таким образом, чтобы он вызывал this.inherited(). | ||||
| // создаем новый порототип, он не в цепочке прототипов у текущего | ||||
| // класса, но super.some_method будет использовать именно его. | ||||
| // в этом объекте будут размещены прокси для переопределенных | ||||
| // методов. | ||||
|
|
r32 | bp = bc.prototype = Object.create(cls.prototype); | ||
|
|
r0 | bp.constructor = bc; | ||
| // proxy - фабрика для создания прокси-методов, которые внутри | ||||
| // себя вызовут this.inherited с правильными параметрами. | ||||
|
|
r1 | const proxy = (m: (...args: any[]) => any) => function (this: any) { | ||
|
|
r11 | const f = this.getInherited({ callee: m }); | ||
| return f && f.apply(this, arguments); | ||||
| // так сделать можно только dojo 1.15+ | ||||
| // return this.inherited(m, arguments); | ||||
|
|
r0 | }; | ||
| // у текущего класса прототип содержит методы, объявленные в этом | ||||
| // классе и его конструктор. Нужно пройти по всем методам и | ||||
| // создать для них прокси. | ||||
| // При этом только те, методы, которые есть в базовых классах | ||||
| // могут быть переопределены. | ||||
| each(target.prototype, (m: any, p: string | number | symbol) => { | ||||
| if (typeof m === "function" && | ||||
| p !== "constructor" && | ||||
|
|
r32 | target.prototype.hasOwnProperty(p) | ||
|
|
r0 | ) { | ||
| bp[p] = proxy(m); | ||||
| } | ||||
| }); | ||||
| // TODO mixin static members | ||||
| return cls as any; | ||||
| } else { | ||||
| return target as any; | ||||
| } | ||||
| } | ||||
| 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; | ||||
|
|
r26 | type: "attribute" | "innerText" | "textContent" | "innerHTML" | "class" | "toggleClass"; | ||
| attribute?: string; | ||||
| className?: string; | ||||
|
|
r0 | } | ||
| /** | ||||
| * Описание привязки свойства виджета к свойству внутреннего объекта. | ||||
| */ | ||||
| interface MemberBindSpec { | ||||
| /** | ||||
| * Имя свойства со ссылкой на объект, к которому . | ||||
| */ | ||||
| member: string; | ||||
| /** | ||||
| * Свойство объекта к которому нужно осуществить привязку. | ||||
| */ | ||||
| property: string; | ||||
| /** | ||||
| * Привязка осуществляется не только на запись но и на чтение свойства. | ||||
| */ | ||||
| getter?: boolean; | ||||
| } | ||||
|
|
r1 | function isNodeBindSpec(v: any): v is NodeBindSpec { | ||
|
|
r0 | return "node" in v; | ||
| } | ||||
| /** Декорирует свойства виджета для привязки их к внутренним членам, либо DOM | ||||
| * элементам, либо свойству внутреннего объекта. | ||||
| * | ||||
| * @param {NodeBindSpec | MemberBindSpec} params Параметры связывания. | ||||
| */ | ||||
| export function bind(params: NodeBindSpec | MemberBindSpec) { | ||||
|
|
r28 | if (isNodeBindSpec(params)) { | ||
|
|
r0 | return (target: any, name: string) => { | ||
| target[makeSetterName(name)] = params; | ||||
| }; | ||||
|
|
r28 | } else { | ||
|
|
r0 | return (target: any, name: string) => { | ||
| target[name] = null; | ||||
|
|
r1 | target[makeSetterName(name)] = function (v: any) { | ||
|
|
r0 | this._set(name, v); | ||
| this[params.member].set(params.property, v); | ||||
| }; | ||||
| if (params.getter) | ||||
| target[makeGetterName(name)] = function () { | ||||
| return this[params.member].get(params.property); | ||||
| }; | ||||
| }; | ||||
| } | ||||
| } | ||||
|
|
r24 | |||
|
|
r30 | /** Создает в прототипе указанное свойство со значение `undefined`, данный | ||
|
|
r24 | * декоратор следует использовать для свойств, у которых нет значения по-умолчанию | ||
| * и они не могут быть `null | undefined` | ||||
| */ | ||||
| export function prototype(): (p: any, name: string) => void; | ||||
| /** Создает в прототипе свойство с указанным значением. | ||||
| * @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) { | ||||
| return <P extends { [m in K]: T }, K extends keyof P>(p: P, name: K) => { | ||||
| p[name] = value as any; | ||||
| }; | ||||
| } | ||||
