import { inject , bindingMode , ObserverLocator , InternalPropertyObserver } from 'aurelia-framework' ; import { SyntaxInterpreter } from 'aurelia-templating-binding' ; export type GetElementObserver = ( obj : Element , propertyName : string , observerLocator : ObserverLocator , descriptor ? : PropertyDescriptor | null ) => InternalPropertyObserver | null ; export interface MdcElementObserverAdapter { tagName : string ; properties : Record < string , MdcElementPropertyObserver > ; } export interface MdcElementPropertyObserver { defaultBindingMode : bindingMode ; getObserver : GetElementObserver ; } @ inject ( ObserverLocator ) export class MdcComponentAdapters { private adaptersCreated : boolean = false ; private adapters : Record < string , MdcElementObserverAdapter > = { } ; private bindingModeIntercepted : boolean ; constructor ( private observerLocator ) { } private createAdapter ( ) { this . observerLocator . addAdapter ( { getObserver : ( obj , propertyName , descriptor ) => { if ( obj instanceof Element ) { const tagName = obj . getAttribute ( 'as-element' ) ?? obj . tagName ; const elAdapters = this . adapters [ tagName ] ; if ( ! elAdapters ) { return null ; } const propertyAdapter = elAdapters . properties [ propertyName ] ; if ( propertyAdapter ) { const observer = propertyAdapter . getObserver ( obj , propertyName , this . observerLocator , descriptor ) ; if ( observer ) { return observer ; } } } return null ; } } ) ; } private getOrCreateMdcElementAdapters ( tagName ) { if ( ! this . adaptersCreated ) { this . createAdapter ( ) ; this . adaptersCreated = true ; } const adapters = this . adapters ; let elementAdapters = adapters [ tagName ] || adapters [ tagName . toLowerCase ( ) ] ; if ( ! elementAdapters ) { elementAdapters = adapters [ tagName ] = adapters [ tagName . toLowerCase ( ) ] = { tagName , properties : { } } ; } return elementAdapters ; } private interceptDetermineDefaultBindingMode ( ) { const mdc = this ; const originalFn = SyntaxInterpreter . prototype . determineDefaultBindingMode ; SyntaxInterpreter . prototype . determineDefaultBindingMode = function ( this , element , attrName , context ? ) { const tagName = element . getAttribute ( 'as-element' ) ?? element . tagName ; const elAdapters = mdc . adapters [ tagName ] ; if ( elAdapters ) { const propertyAdapter = elAdapters . properties [ attrName ] ; if ( propertyAdapter ) { return propertyAdapter . defaultBindingMode ; } } return originalFn . call ( this , element , attrName , context ) ; } ; } public addMdcElementObserverAdapter ( tagName , properties ) { if ( ! this . adaptersCreated ) { this . createAdapter ( ) ; this . adaptersCreated = true ; } const elementAdapters = this . getOrCreateMdcElementAdapters ( tagName ) ; Object . assign ( elementAdapters . properties , properties ) ; } public registerMdcElementConfig ( observerAdapter ) { if ( ! this . bindingModeIntercepted ) { this . interceptDetermineDefaultBindingMode ( ) ; this . bindingModeIntercepted = true ; } this . addMdcElementObserverAdapter ( observerAdapter . tagName . toUpperCase ( ) , observerAdapter . properties ) ; } }