UNPKG

mframejs

Version:
261 lines (205 loc) 11 kB
import { subscribeClassProperty } from './property/subscribeClassProperty'; import { PropertyObserverHandler } from './property/propertyObserverHandler'; import { BindingEngine } from './bindingEngine'; import { IAttribute, CONSTANTS, IElement, IListener } from '../interface/exported'; import { createBindingContext } from './createBindingContext'; /** * callbacks changes in class if chnage method is added * */ const subscribeChangeBaseClass = class implements IListener { private key: string; private _class: IElement | IAttribute; private meta: any; public caller: PropertyObserverHandler; constructor(_class: IElement | IAttribute, key: string, meta: any) { this._class = _class; this.key = key; this.meta = meta; } /** * called on changes * */ public call(newValue: any, oldValue: any) { if (oldValue !== newValue) { const key = this.key; const _class = this._class; const META = this.meta; if (_class.$bindingContext && key in _class.$bindingContext.$context) { // I should call this before? if (_class.$bindingContext.$context[key] !== newValue) { _class.$bindingContext.$context[key] = newValue; } } if (_class[`${key}Changed`]) { _class[`${key}Changed`](newValue, oldValue); } if (_class[`attributesChanged`]) { // usefull so we dont need a handler for each _class[`attributesChanged`](key, newValue, oldValue); } if (META[key].options.changeHandler) { _class[META[key].options.changeHandler](key, newValue, oldValue); } } } }; /** * creates a observer if bindable meta data is added * */ export function subscribeClassMetaBinding(_class: IElement | IAttribute) { const META = (_class as any).__proto__[CONSTANTS.META_BINDABLE]; if (META) { const keys = Object.keys(META); for (const key of keys) { if (!(_class as any).__metaBinding) { (_class as any).__metaBinding = {}; } if (!(_class as any).__metaBinding[key]) { (_class as any).__metaBinding[key] = {}; (_class as any).__metaBinding[key].key = key; (_class as any).__metaBinding[key].options = {}; } const CLASSMETA = (_class as any).__metaBinding[key]; const subscribeInternal = new subscribeChangeBaseClass(_class, key, META); subscribeClassProperty(createBindingContext(_class), key, subscribeInternal); // save it for later so we can unsubscribe CLASSMETA.options.subscribeInternal = subscribeInternal; if (META[key].observableOnly === true) { // nothing atm } else { if ((_class as IElement).$attributes && !(_class as IAttribute).$attribute) { // custom element // we now check for if attribute have bind, else we just give the value if (_class.$element && _class.$bindingContext) { // need to make sure there is parent and element const el = _class.$element as Element; let att = `${META[key].options.attribute}.bind`; let attrValue = el.getAttribute(att); if (attrValue) { const subscribeExternal: IListener = Object.create({ call: (newValue: any, oldValue: any) => { if (oldValue !== newValue) { _class[key] = newValue; } } }); subscribeClassProperty(_class.$bindingContext, attrValue, subscribeExternal); // save it for later so we can unsubscribe CLASSMETA.options.subscribeExternal = subscribeExternal; } else { // just set value, no bind att = `${META[key].options.attribute}`; attrValue = el.getAttribute(att); if (attrValue) { if (attrValue.indexOf('${') !== -1 || attrValue.indexOf('@{') !== -1) { const val = BindingEngine.tokenizeParseAndTraverseAST(attrValue, _class.$bindingContext); if (val) { _class[key] = val; } } else { _class[key] = attrValue; } } } } } else { // custom attribute // - > need to extract values, they can be many in 1 if (_class.$element && _class.$bindingContext) { // many or 1 check, if just value we only want that if (keys.length === 1 && key === 'value') { // only 1 key and its value, then we only use this const el = _class.$element as Element; const att = (_class as IAttribute).$attribute.name; const haveBind = att.indexOf('.bind'); let attrValue = el.getAttribute(att); if (haveBind !== -1) { // binding const subscribeExternal: IListener = Object.create({ call: (newValue: any, oldValue: any) => { if (oldValue !== newValue) { _class[key] = newValue; } } }); subscribeClassProperty(_class.$bindingContext, attrValue, subscribeExternal); // save it for later so we can unsubscribe CLASSMETA.options.subscribeExternal = subscribeExternal; } else { // no bind, just get value attrValue = el.getAttribute(att); if (attrValue) { if (attrValue.indexOf('${') !== -1 || attrValue.indexOf('@{') !== -1) { const val = BindingEngine.tokenizeParseAndTraverseAST(attrValue, _class.$bindingContext); if (val) { _class[key] = val; } } else { _class[key] = attrValue; } } } } else { // not just value and 1 @bindable const el = _class.$element as Element; const attributeName = (_class as IAttribute).$attribute.name; let att = `${META[key].options.attribute}.bind`; const attrValues: any[] = el.getAttribute(attributeName) ? el.getAttribute(attributeName).split(';') : null; // do we have many values inside? if (attrValues) { // yes, lets get them and check for key/att // atm checking for .bind let attrValue: string; attrValues.forEach((value: any) => { const test = value.split(':'); if (test && test[0].trim() === att) { attrValue = test[1]; } }); if (attrValue) { const subscribeExternal: IListener = Object.create({ call: (newValue: any, oldValue: any) => { if (oldValue !== newValue) { _class[key] = newValue; } } }); subscribeClassProperty(_class.$bindingContext, attrValue, subscribeExternal); // save it for later so we can unsubscribe CLASSMETA.options.subscribeExternal = subscribeExternal; } else { // no .bind // just set value if it is there att = `${META[key].options.attribute}`; let attrValue: string; attrValues.forEach((value: any) => { const test = value.split(':'); if (test && test[0].trim() === att) { attrValue = test[1]; } }); if (attrValue) { if (attrValue.indexOf('${') !== -1 || attrValue.indexOf('@{') !== -1) { const val = BindingEngine.tokenizeParseAndTraverseAST(attrValue, _class.$bindingContext); if (val) { _class[key] = val; } } else { _class[key] = attrValue; } } } } } } } } } } }