import { mergeArrays, fromDefinitionOrDefault, pascalCase, fromAnnotationOrTypeOrDefault, fromAnnotationOrDefinitionOrTypeOrDefault, emptyArray, resourceBaseName, getResourceKeyFor, isFunction, isString, isSymbol, } from '@aurelia/kernel'; import { Bindable } from '../bindable'; import { getEffectiveParentNode } from '../dom'; import { refs } from '../dom.node'; import { Watch } from '../watch'; import { clearMetadata, defineMetadata, getAnnotationKeyFor, getMetadata, hasMetadata } from '../utilities-metadata'; import { def, objectAssign, objectFreeze } from '../utilities'; import { aliasRegistration, singletonRegistration } from '../utilities-di'; import type { Constructable, IContainer, ResourceType, PartialResourceDefinition, Key, ResourceDefinition, IResolver, Writable, StaticResourceType, InterfaceSymbol, } from '@aurelia/kernel'; import type { BindableDefinition } from '../bindable'; import type { INode } from '../dom.node'; import type { Controller, ICustomElementViewModel, ICustomElementController } from '../templating/controller'; import { ProcessContentHook, type IElementComponentDefinition, IInstruction } from '@aurelia/template-compiler'; import type { IWatchDefinition } from '../watch'; import { ErrorNames, createMappedError } from '../errors'; import { dtElement, getDefinitionFromStaticAu, clearStaticAuDefinition, type IResourceKind } from './resources-shared'; export type PartialCustomElementDefinition = PartialResourceDefinition, 'type'> & { /** * An semi internal property used to signal the rendering process not to try to compile the template again */ readonly injectable?: InterfaceSymbol | null; readonly enhance?: boolean; readonly watches?: IWatchDefinition[]; readonly strict?: boolean; }>; export type CustomElementStaticAuDefinition = PartialCustomElementDefinition & { type: 'custom-element'; }; export type CustomElementType = ResourceType ? P : object), PartialCustomElementDefinition>; export type CustomElementKind = IResourceKind & { /** * Returns the closest controller that is associated with either this node (if it is a custom element) or the first * parent node (including containerless) that is a custom element. * * As long as the provided node was directly or indirectly created by Aurelia, this method is guaranteed to return a controller. * * @param node - The node relative to which to get the closest controller. * @param searchParents - Also search the parent nodes (including containerless). * @returns The closest controller relative to the provided node. * @throws - If neither the node or any of its effective parent nodes host a custom element, an error will be thrown. */ for(node: Node, opts: { searchParents: true }): ICustomElementController; /** * Returns the controller that is associated with this node, if it is a custom element with the provided name. * * @param node - The node to retrieve the controller for, if it is a custom element with the provided name. * @returns The controller associated with the provided node, if it is a custom element with the provided name, or otherwise `undefined`. * @throws - If the node does not host a custom element, an error will be thrown. */ for(node: Node, opts: { name: string }): ICustomElementController | undefined; /** * Returns the closest controller that is associated with either this node (if it is a custom element) or the first * parent node (including containerless) that is a custom element with the provided name. * * @param node - The node relative to which to get the closest controller of a custom element with the provided name. * @param searchParents - Also search the parent nodes (including containerless). * @returns The closest controller of a custom element with the provided name, relative to the provided node, if one can be found, or otherwise `undefined`. * @throws - If neither the node or any of its effective parent nodes host a custom element, an error will be thrown. */ for(node: Node, opts: { name: string; searchParents: true }): ICustomElementController | undefined; /** * Returns the controller that is associated with this node, if it is a custom element. * * @param node - The node to retrieve the controller for, if it is a custom element. * @param optional - If `true`, do not throw if the provided node is not a custom element. * @returns The controller associated with the provided node, if it is a custom element * @throws - If the node does not host a custom element, an error will be thrown. */ for(node: Node): ICustomElementController; /** * Returns the controller that is associated with this node, if it is a custom element. * * @param node - The node to retrieve the controller for, if it is a custom element. * @param optional - If `true`, do not throw if the provided node is not a custom element. * @returns The controller associated with the provided node, if it is a custom element, otherwise `null` */ for(node: Node, opts: { optional: true }): ICustomElementController | null; isType(value: C, context?: DecoratorContext): value is (C extends Constructable ? CustomElementType : never); define(name: string, Type: C): CustomElementType; define(def: PartialCustomElementDefinition, Type: C): CustomElementType; define(def: PartialCustomElementDefinition, Type?: null): CustomElementType; define(nameOrDef: string | PartialCustomElementDefinition, Type: C): CustomElementType; getDefinition(Type: C, context?: DecoratorContext | null): CustomElementDefinition; // eslint-disable-next-line getDefinition(Type: Function, context?: DecoratorContext | null): CustomElementDefinition; annotate(Type: Constructable, prop: K, value: PartialCustomElementDefinition[K], context: DecoratorContext): void; getAnnotation(Type: Constructable, prop: K, context: DecoratorContext | null): PartialCustomElementDefinition[K] | undefined; generateName(): string; createInjectable(): InterfaceSymbol; generateType

( name: string, proto?: P, ): CustomElementType>; find(container: IContainer, name: string): CustomElementDefinition | null; /** * Clear cached definition for a custom element type. * Used by SSR to reset definition cache before re-rendering. * * @param Type - The component class to clear cached definitions for */ clearDefinition(Type: Constructable): void; }; export type CustomElementDecorator = (Type: T, context: ClassDecoratorContext) => CustomElementType; /** * Decorator: Indicates that the decorated class is a custom element. */ export function customElement(definition: PartialCustomElementDefinition): CustomElementDecorator; export function customElement(name: string): CustomElementDecorator; export function customElement(nameOrDef: string | PartialCustomElementDefinition): CustomElementDecorator; export function customElement(nameOrDef: string | PartialCustomElementDefinition): CustomElementDecorator { return function (target: T, context: ClassDecoratorContext) { context.addInitializer(function (this) { defineElement(nameOrDef, this as Constructable); }); return target as CustomElementType; }; } type ShadowOptions = PartialCustomElementDefinition['shadowOptions']; /** * Decorator: Indicates that the custom element should render its view in ShadowDOM. */ export function useShadowDOM(options?: ShadowOptions): (target: Constructable, context: ClassDecoratorContext) => void; /** * Decorator: Indicates that the custom element should render its view in ShadowDOM. */ export function useShadowDOM(target: Constructable): void; export function useShadowDOM(targetOrOptions?: Constructable | ShadowOptions, context?: ClassDecoratorContext): void | ((target: Constructable, context: ClassDecoratorContext) => void) { if (targetOrOptions === void 0) { return function ($target: Constructable, context: ClassDecoratorContext) { context.addInitializer(function (this) { annotateElementMetadata(this as Constructable, 'shadowOptions', { mode: 'open' }); }); }; } if (!isFunction(targetOrOptions)) { return function ($target: Constructable, context: ClassDecoratorContext) { context.addInitializer(function (this) { annotateElementMetadata(this as Constructable, 'shadowOptions', targetOrOptions); }); }; } context!.addInitializer(function (this) { annotateElementMetadata(this as Constructable, 'shadowOptions', { mode: 'open' }); }); } /** * Decorator: Indicates that the custom element should be rendered without its element container. */ export function containerless(target: Constructable, context: ClassDecoratorContext): void; /** * Decorator: Indicates that the custom element should be rendered without its element container. */ export function containerless(): (target: Constructable, context: ClassDecoratorContext) => void; export function containerless(target?: Constructable, context?: ClassDecoratorContext): void | ((target: Constructable, context: ClassDecoratorContext) => void) { if (target === void 0) { return function ($target: Constructable, $context: ClassDecoratorContext) { $context.addInitializer(function (this) { markContainerless($target); }); }; } context!.addInitializer(function (this) { markContainerless(target); }); } /** Manipulates the `containerless` property of the custom element definition for the type, when present else it annotates the type. */ function markContainerless(target: Constructable) { const def = getMetadata(elementBaseName, target); if(def === void 0) { annotateElementMetadata(target, 'containerless', true); return; } (def as Writable).containerless = true; } const definitionLookup = new WeakMap(); export class CustomElementDefinition implements ResourceDefinition { public get type(): 'custom-element' { return dtElement; } private constructor( public readonly Type: CustomElementType, public readonly name: string, public readonly aliases: string[], public readonly key: string, public readonly capture: boolean | ((attr: string) => boolean), public readonly template: null | string | Node, public readonly instructions: readonly IInstruction[][], public readonly dependencies: Key[], public readonly injectable: InterfaceSymbol | null, public readonly needsCompile: boolean, public readonly surrogates: readonly IInstruction[], public readonly bindables: Record, public readonly containerless: boolean, public readonly shadowOptions: { mode: 'open' | 'closed' } | null, /** * Indicates whether the custom element has in its template */ public readonly hasSlots: boolean, public readonly enhance: boolean, public readonly watches: IWatchDefinition[], public readonly strict: boolean | undefined, public readonly processContent: ProcessContentHook | null, ) { } public static create( def: PartialCustomElementDefinition, Type?: null, ): CustomElementDefinition; public static create( name: string, Type: CustomElementType, ): CustomElementDefinition; public static create( nameOrDef: string | PartialCustomElementDefinition, Type?: CustomElementType | null, ): CustomElementDefinition; public static create( nameOrDef: string | PartialCustomElementDefinition, Type: CustomElementType | null = null, ): CustomElementDefinition { if (Type === null) { const def = nameOrDef; if (isString(def)) { throw createMappedError(ErrorNames.element_only_name, nameOrDef); } const name = fromDefinitionOrDefault('name', def, generateElementName); if (isFunction((def as CustomElementDefinition).Type)) { // This needs to be a clone (it will usually be the compiler calling this signature) // TODO: we need to make sure it's documented that passing in the type via the definition (while passing in null // as the "Type" parameter) effectively skips type analysis, so it should only be used this way for cloning purposes. Type = (def as CustomElementDefinition).Type; } else { Type = generateElementType(pascalCase(name)); } for(const bindable of Object.values(Bindable.from(def.bindables))) { Bindable._add(bindable, Type); } return new CustomElementDefinition( Type, name, mergeArrays(def.aliases), fromDefinitionOrDefault('key', def as CustomElementDefinition, () => getElementKeyFrom(name)), fromAnnotationOrDefinitionOrTypeOrDefault('capture', def, Type, returnFalse), fromAnnotationOrDefinitionOrTypeOrDefault('template', def, Type, returnNull), mergeArrays(def.instructions), mergeArrays(getElementAnnotation(Type, 'dependencies'), def.dependencies), fromDefinitionOrDefault('injectable', def, returnNull), fromDefinitionOrDefault('needsCompile', def, returnTrue), mergeArrays(def.surrogates), Bindable.from(getElementAnnotation(Type, 'bindables'), def.bindables), fromAnnotationOrDefinitionOrTypeOrDefault('containerless', def, Type, returnFalse), fromDefinitionOrDefault('shadowOptions', def, returnNull), fromDefinitionOrDefault('hasSlots', def, returnFalse), fromDefinitionOrDefault('enhance', def, returnFalse), fromDefinitionOrDefault('watches', def as CustomElementDefinition, returnEmptyArray), // casting is incorrect, but it's good enough fromDefinitionOrDefault('strict', def, returnUndefined as () => boolean), fromAnnotationOrTypeOrDefault('processContent', Type, returnNull as () => ProcessContentHook | null), ); } // If a type is passed in, we ignore the Type property on the definition if it exists. // TODO: document this behavior if (isString(nameOrDef)) { return new CustomElementDefinition( Type, nameOrDef, mergeArrays(getElementAnnotation(Type, 'aliases'), Type.aliases), getElementKeyFrom(nameOrDef), fromAnnotationOrTypeOrDefault('capture', Type, returnFalse), fromAnnotationOrTypeOrDefault('template', Type, returnNull as () => string | Node | null), mergeArrays(getElementAnnotation(Type, 'instructions'), Type.instructions), mergeArrays(getElementAnnotation(Type, 'dependencies'), Type.dependencies), fromAnnotationOrTypeOrDefault('injectable', Type, returnNull as () => InterfaceSymbol | null), fromAnnotationOrTypeOrDefault('needsCompile', Type, returnTrue), mergeArrays(getElementAnnotation(Type, 'surrogates'), Type.surrogates), Bindable.from( ...Bindable.getAll(Type), getElementAnnotation(Type, 'bindables'), Type.bindables, ), fromAnnotationOrTypeOrDefault('containerless', Type, returnFalse), fromAnnotationOrTypeOrDefault('shadowOptions', Type, returnNull as () => { mode: 'open' | 'closed' } | null), fromAnnotationOrTypeOrDefault('hasSlots', Type, returnFalse), fromAnnotationOrTypeOrDefault('enhance', Type, returnFalse), mergeArrays(Watch.getDefinitions(Type), Type.watches), fromAnnotationOrTypeOrDefault('strict', Type, returnUndefined as () => boolean), fromAnnotationOrTypeOrDefault('processContent', Type, returnNull as () => ProcessContentHook | null), ); } // This is the typical default behavior, e.g. from regular CustomElement.define invocations or from @customElement deco // The ViewValueConverter also uses this signature and passes in a definition where everything except for the 'hooks' // property needs to be copied. So we have that exception for 'hooks', but we may need to revisit that default behavior // if this turns out to be too opinionated. const name = fromDefinitionOrDefault('name', nameOrDef, generateElementName); for(const bindable of Object.values(Bindable.from(nameOrDef.bindables))) { Bindable._add(bindable, Type); } return new CustomElementDefinition( Type, name, mergeArrays(getElementAnnotation(Type, 'aliases'), nameOrDef.aliases, Type.aliases), getElementKeyFrom(name), fromAnnotationOrDefinitionOrTypeOrDefault('capture', nameOrDef, Type, returnFalse), fromAnnotationOrDefinitionOrTypeOrDefault('template', nameOrDef, Type, returnNull), mergeArrays(getElementAnnotation(Type, 'instructions'), nameOrDef.instructions, Type.instructions), mergeArrays(getElementAnnotation(Type, 'dependencies'), nameOrDef.dependencies, Type.dependencies), fromAnnotationOrDefinitionOrTypeOrDefault('injectable', nameOrDef, Type, returnNull), fromAnnotationOrDefinitionOrTypeOrDefault('needsCompile', nameOrDef, Type, returnTrue), mergeArrays(getElementAnnotation(Type, 'surrogates'), nameOrDef.surrogates, Type.surrogates), Bindable.from( ...Bindable.getAll(Type), getElementAnnotation(Type, 'bindables'), Type.bindables, nameOrDef.bindables, ), fromAnnotationOrDefinitionOrTypeOrDefault('containerless', nameOrDef, Type, returnFalse), fromAnnotationOrDefinitionOrTypeOrDefault('shadowOptions', nameOrDef, Type, returnNull), fromAnnotationOrDefinitionOrTypeOrDefault('hasSlots', nameOrDef, Type, returnFalse), fromAnnotationOrDefinitionOrTypeOrDefault('enhance', nameOrDef, Type, returnFalse), mergeArrays(nameOrDef.watches, Watch.getDefinitions(Type), Type.watches), fromAnnotationOrDefinitionOrTypeOrDefault('strict', nameOrDef, Type, returnUndefined as () => boolean), fromAnnotationOrDefinitionOrTypeOrDefault('processContent', nameOrDef, Type, returnNull), ); } public static getOrCreate(partialDefinition: PartialCustomElementDefinition): CustomElementDefinition { if (partialDefinition instanceof CustomElementDefinition) { return partialDefinition; } if (definitionLookup.has(partialDefinition)) { return definitionLookup.get(partialDefinition)!; } const definition = CustomElementDefinition.create(partialDefinition); definitionLookup.set(partialDefinition, definition); // Make sure the full definition can be retrieved from dynamically created classes as well defineMetadata(definition, definition.Type, elementBaseName); return definition; } public register(container: IContainer, aliasName?: string | undefined): void { const $Type = this.Type; const key = typeof aliasName === 'string' ? getElementKeyFrom(aliasName) : this.key; const aliases = this.aliases; /* istanbul ignore next */ if (container.has(key, false)) { // eslint-disable-next-line no-console console.warn(createMappedError(ErrorNames.element_existed, this.name)); return; } container.register( singletonRegistration(key, $Type), ...aliases.map(alias => aliasRegistration(key, getElementKeyFrom(alias))) ); } public toString() { return `au:ce:${this.name}`; } } type ForOpts = { name?: string; searchParents?: boolean; optional?: boolean; }; const defaultForOpts: ForOpts = { name: undefined, searchParents: false, optional: false, }; const returnNull = (): T | null => null; const returnUndefined = (): T | undefined => void 0; const returnFalse = () => false; const returnTrue = () => true; const returnEmptyArray = () => emptyArray; /** @internal */ export const elementTypeName = 'custom-element'; /** @internal */ export const elementBaseName = /*@__PURE__*/getResourceKeyFor(elementTypeName); /** @internal */ export const getElementKeyFrom = (name: string): string => `${elementBaseName}:${name}`; /** @internal */ export const generateElementName = /*@__PURE__*/(id => () => `unnamed-${++id}`)(0); const annotateElementMetadata = (Type: Constructable, prop: K, value: PartialCustomElementDefinition[K]): void => { defineMetadata(value, Type, getAnnotationKeyFor(prop)); }; /** @internal */ export const defineElement = (nameOrDef: string | PartialCustomElementDefinition, Type: C | null): CustomElementType => { const definition = CustomElementDefinition.create(nameOrDef, Type as CustomElementType); const $Type = definition.Type; // this is the case, where the APi is invoked directly without a decorator // registration of resource name is a requirement for the resource system in kernel (module-loader) defineMetadata(definition, $Type, elementBaseName, resourceBaseName); return $Type; }; /** @internal */ export const isElementType = (value: C): value is (C extends Constructable ? CustomElementType : never) => { return isFunction(value) && (hasMetadata(elementBaseName, value) || (value as StaticResourceType).$au?.type === elementTypeName ); }; /** @internal */ export const findElementControllerFor = (node: Node, opts: ForOpts = defaultForOpts): ICustomElementController => { if (opts.name === void 0 && opts.searchParents !== true) { const controller = refs.get(node, elementBaseName) as Controller | null; if (controller === null) { if (opts.optional === true) { return null!; } throw createMappedError(ErrorNames.node_is_not_a_host, node); } return controller as unknown as ICustomElementController; } if (opts.name !== void 0) { if (opts.searchParents !== true) { const controller = refs.get(node, elementBaseName) as Controller | null; if (controller === null) { throw createMappedError(ErrorNames.node_is_not_a_host2, node); } if (controller.is(opts.name)) { return controller as unknown as ICustomElementController; } return (void 0)!; } let cur = node as INode | null; let foundAController = false; while (cur !== null) { const controller = refs.get(cur, elementBaseName) as Controller | null; if (controller !== null) { foundAController = true; if (controller.is(opts.name)) { return controller as unknown as ICustomElementController; } } cur = getEffectiveParentNode(cur); } if (foundAController) { return (void 0)!; } throw createMappedError(ErrorNames.node_is_not_part_of_aurelia_app, node); } let cur = node as INode | null; while (cur !== null) { const controller = refs.get(cur, elementBaseName) as Controller | null; if (controller !== null) { return controller as unknown as ICustomElementController; } cur = getEffectiveParentNode(cur); } throw createMappedError(ErrorNames.node_is_not_part_of_aurelia_app2, node); }; const getElementAnnotation = ( Type: Constructable, prop: K, ): PartialCustomElementDefinition[K] | undefined => getMetadata(getAnnotationKeyFor(prop), Type); /** @internal */ // eslint-disable-next-line @typescript-eslint/ban-types export const getElementDefinition = (Type: C | Function): CustomElementDefinition => { const def: CustomElementDefinition = getMetadata>(elementBaseName, Type) ?? getDefinitionFromStaticAu>(Type as CustomElementType, elementTypeName, CustomElementDefinition.create); if (def == null) { throw createMappedError(ErrorNames.element_def_not_found, Type); } return def; }; /** @internal */ export const createElementInjectable = (): InterfaceSymbol => { const $injectable = { // Old code is kept around. Needs to be refactored when TC39 supports argument decorator. // function(target: Injectable | AbstractInjectable, property: string | symbol | undefined, index?: number): Injectable | AbstractInjectable { // const annotationParamtypes = DI.getOrCreateAnnotationParamTypes(target as Constructable); // annotationParamtypes[index!] = $injectable; // return target; // }, $isInterface: false, register(): IResolver { return { $isResolver: true, resolve(container, requestor) { if (requestor.has($injectable, true)) { return requestor.get($injectable); } else { return null; } } }; } }; return $injectable; }; /** @internal */ export const generateElementType = /*@__PURE__*/(function () { const nameDescriptor: PropertyDescriptor = { value: '', writable: false, enumerable: false, configurable: true, }; const defaultProto = {}; return function

( name: string, proto: P = defaultProto as P, ): CustomElementType> { // Anonymous class ensures that minification cannot cause unintended side-effects, and keeps the class // looking similarly from the outside (when inspected via debugger, etc). const Type = class Anonymous { } as CustomElementType>; // Define the name property so that Type.name can be used by end users / plugin authors if they really need to, // even when minified. nameDescriptor.value = name; def(Type, 'name', nameDescriptor); // Assign anything from the prototype that was passed in if (proto !== defaultProto) { objectAssign(Type.prototype, proto); } return Type; }; })(); /** * Clear cached definition for a custom element type. * Used by SSR to reset definition cache before re-rendering. * * This clears: * - elementBaseName metadata (primary definition cache) * - '__au_static_resource__' metadata (static $au cache) * * Note: The definitionLookup WeakMap cannot be cleared directly without * the key, but clearing metadata is sufficient as it's the primary cache. */ const clearElementDefinition = (Type: Constructable): void => { clearMetadata(Type, elementBaseName); clearStaticAuDefinition(Type); }; export const CustomElement = /*@__PURE__*/ objectFreeze({ name: elementBaseName, keyFrom: getElementKeyFrom, isType: isElementType, for: findElementControllerFor, define: defineElement, getDefinition: getElementDefinition, annotate: annotateElementMetadata, getAnnotation: getElementAnnotation, generateName: generateElementName, createInjectable: createElementInjectable, generateType: generateElementType, find(c, name) { const Type = c.find(elementTypeName, name); return Type == null ? null : getMetadata(elementBaseName, Type) ?? getDefinitionFromStaticAu(Type, elementTypeName, CustomElementDefinition.create) ?? null; }, clearDefinition: clearElementDefinition, }); // eslint-disable-next-line @typescript-eslint/ban-types type DecoratorFactoryMethod = (target: Function, context: ClassMethodDecoratorContext) => void; const pcHookMetadataProperty = /*@__PURE__*/getAnnotationKeyFor('processContent'); export function processContent(hook: ProcessContentHook | string | symbol): CustomElementDecorator; export function processContent(): DecoratorFactoryMethod; export function processContent(hook?: ProcessContentHook | string | symbol): CustomElementDecorator | DecoratorFactoryMethod { return hook === void 0 // eslint-disable-next-line @typescript-eslint/ban-types ? function (target: Function, context: ClassMethodDecoratorContext) { if (!context.static || context.kind !== 'method') throw createMappedError(ErrorNames.invalid_process_content_hook, target); // As the method is ensured to be static, the following initializer will be invoked in static fashion, before executing the initializers added via a class decorator. // Refer: https://tinyurl.com/ts-static-method-deco context.addInitializer(function (this) { defineMetadata(target, this, pcHookMetadataProperty); }); } : function (target: TClass, context: ClassDecoratorContext) { context.addInitializer(function (this) { if (isString(hook) || isSymbol(hook)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-explicit-any hook = (this as any)[hook] as ProcessContentHook; } if (!isFunction(hook)) throw createMappedError(ErrorNames.invalid_process_content_hook, hook); const def = getMetadata(elementBaseName, this); if (def !== void 0) { (def as Writable).processContent = hook; } else { defineMetadata(hook, this, pcHookMetadataProperty); } }); return target as CustomElementType; } as CustomElementDecorator; } /** * Decorator: Indicates that the custom element should capture all attributes and bindings that are not template controllers or bindables */ export function capture(filter: (attr: string) => boolean): (target: Constructable, context: ClassDecoratorContext) => void; /** * Decorator: Indicates that the custom element should be rendered with the strict binding option. undefined/null -> 0 or '' based on type */ export function capture(): (target: Constructable, context: ClassDecoratorContext) => void ; export function capture(targetOrFilter?: (attr: string) => boolean): ((target: Constructable, context: ClassDecoratorContext) => void) { return function ($target: Constructable, context: ClassDecoratorContext) { const value = isFunction(targetOrFilter) ? targetOrFilter : true; context.addInitializer(function (this) { annotateElementMetadata(this as Constructable, 'capture', value); // also do this to make order of the decorator irrelevant if (isElementType(this)) { (getElementDefinition(this) as Writable).capture = value; } }); }; }