capivarajs
Version:
Um framework para criação de componentes.
419 lines (366 loc) • 13.5 kB
text/typescript
import { Common } from '../common';
import { Constants } from '../constants';
import { Capivara } from '../index';
import { CPAttr } from './directive/cp-attr';
import { CPBlur } from './directive/cp-blur';
import { CPChange } from './directive/cp-change';
import { CPClass } from './directive/cp-class';
import { CPClick } from './directive/cp-click';
import { CPDisabled } from './directive/cp-disabled';
import { CPElse } from './directive/cp-else';
import { CPElseIf } from './directive/cp-else-if';
import { CPFocus } from './directive/cp-focus';
import { CPHide } from './directive/cp-hide';
import { CPIf } from './directive/cp-if';
import { CPInit } from './directive/cp-init';
import { CPKey } from './directive/cp-key';
import { CPModel } from './directive/cp-model';
import { CPMouse } from './directive/cp-mouse';
import { CPRepeat } from './directive/cp-repeat';
import { CPShow } from './directive/cp-show';
import { CPStyle } from './directive/cp-style';
export class MapDom {
/**
* Elemento principal que está aplicado o escopo
*/
private readonly element: HTMLElement;
private directives = {
/**
* Mapa de atributos com os elementos que os observam.
*/
cpModelsElements: {},
/**
* Array com os cp-repeat
*/
cpIfs: [],
cpModels: [],
repeats: [],
cpShows: [],
cpElses: [],
cpElseIfs: [],
cpStyles: [],
cpClasses: [],
cpClicks: [],
cpInits: [],
cpKeys: [],
cpAttrs: [],
cpDisables: [],
cpFocus: [],
cpHide: [],
cpBlur: [],
cpMouse: [],
cpChange: [],
};
private readonly regexInterpolation;
/**
* @description variavel boleana que define se o HTML está renderizado na página.
*/
private renderedView: boolean;
constructor(_element: HTMLElement) {
this.element = _element;
this.regexInterpolation = new RegExp(/({{).*?(}})/g);
this.setRenderedView(false);
if (this.element) { this.$addScope(); }
}
/**
* @method void Percorre os elementos filhos do elemento principal criando os binds.
*/
public $addScope() {
this.createDirectives(this.element);
const recursiveBind = (element) => {
Array.from(element.children).forEach((child: any) => {
child[Constants.SCOPE_ATTRIBUTE_NAME] = Common.getScope(this.element);
this.createDirectives(child);
if (child.children) { recursiveBind(child); }
});
};
recursiveBind(this.element);
this.$directivesInit();
}
private setRenderedView(value: boolean) {
this.renderedView = value;
}
public $directivesInit() {
Common.getScope(this.element).$on('$onInit', () => {
Object.keys(this.directives).forEach((key) => {
const directives = this.directives[key];
if (Array.isArray(directives)) {
directives.forEach((directive) => directive.create());
}
});
this.$viewInit();
});
}
private $viewInit() {
this.setRenderedView(true);
if (this.element['$instance']) {
const ctrl = Common.getScope(this.element).scope[this.element['$instance'].config.controllerAs];
if (ctrl && ctrl['$onViewInit']) {
ctrl['$onViewInit']();
}
}
}
/**
* @method void Cria uma nova instancia de bind de acordo com o atributo declarado no elemento child.
* @param child Elemento que utiliza algum tipo de bind.
*/
public createDirectives(child) {
if (child.hasAttribute(Constants.MODEL_ATTRIBUTE_NAME)) { this.createCPModel(child); }
if (child.hasAttribute(Constants.IF_ATTRIBUTE_NAME)) { this.createCPIf(child); }
if (child.hasAttribute(Constants.CLICK_ATTRIBUTE_NAME) || child.hasAttribute(Constants.DBLCLICK_ATTRIBUTE_NAME)) { this.createCPClick(child); }
if (child.hasAttribute(Constants.REPEAT_ATTRIBUTE_NAME)) { this.createCPRepeat(child); }
if (child.hasAttribute(Constants.SHOW_ATTRIBUTE_NAME)) { this.createCPShow(child); }
if (child.hasAttribute(Constants.ELSE_ATTRIBUTE_NAME)) { this.createCPElse(child); }
if (child.hasAttribute(Constants.ELSE_IF_ATTRIBUTE_NAME)) { this.createCPElseIf(child); }
if (child.hasAttribute(Constants.INIT_ATTRIBUTE_NAME)) { this.createCPInit(child); }
if (child.hasAttribute(Constants.STYLE_ATTRIBUTE_NAME)) { this.createCPStyle(child); }
if (child.hasAttribute(Constants.CLASS_ATTRIBUTE_NAME)) { this.createCPClass(child); }
if (child.hasAttributeStartingWith(Constants.KEY_ATTRIBUTE_NAME)) { this.createCPKey(child); }
if (child.hasAttributeStartingWith(Constants.ATTR_ATTRIBUTE_NAME)) { this.createCPAttr(child); }
if (child.hasAttribute(Constants.DISABLE_ATTRIBUTE_NAME)) { this.createCPDisabled(child); }
if (child.hasAttribute(Constants.FOCUS_ATTRIBUTE_NAME)) { this.createCPFocus(child); }
if (child.hasAttribute(Constants.HIDE_ATTRIBUTE_NAME)) { this.createCPHide(child); }
if (child.hasAttribute(Constants.BLUR_ATTRIBUTE_NAME)) { this.createCPBlur(child); }
if (child.hasAttributeStartingWith(Constants.MOUSE_ATTRIBUTE_NAME)) { this.createCPmouse(child); }
if (child.hasAttribute(Constants.CHANGE_ATTRIBUTE_NAME)) { this.createCPChange(child); }
}
public reloadElementChildes(element, initialScope) {
if (element.children) {
Array.from(element.children).forEach((child: any) => {
const childScope = Common.getScope(child);
if (childScope && childScope.mapDom && childScope.id !== initialScope.id) {
childScope.mapDom.reloadDirectives();
}
this.reloadElementChildes(child, initialScope);
});
}
}
public reloadDirectives() {
// Update input values
Object.keys(this.directives.cpModelsElements)
.forEach((key) => this.directives.cpModelsElements[key].forEach((bind) => bind.applyModelInValue()));
// Update cp repeats
this.directives.repeats.forEach((repeat) => repeat.applyLoop());
// Update cp show
this.directives.cpShows.forEach((cpShow) => cpShow.init());
// Update cp if
this.directives.cpIfs.forEach((cpIf) => cpIf.init());
// Update cp else-if
this.directives.cpElseIfs.forEach((cpElseIf) => cpElseIf.init());
// Update cp else
this.directives.cpElses.forEach((cpElse) => cpElse.init());
// Update cp style
this.directives.cpStyles.forEach((cpStyle) => cpStyle.init());
// Update cp class
this.directives.cpClasses.forEach((cpClass) => cpClass.init());
// Update cp key
this.directives.cpKeys.forEach((cpKey) => cpKey.init());
// Update cp disable
this.directives.cpDisables.forEach((cpDisable) => cpDisable.init());
// Update cp focus
this.directives.cpFocus.forEach((cpFocus) => cpFocus.init());
// Update cp hide
this.directives.cpHide.forEach((cpHide) => cpHide.init());
// Update cp blur
this.directives.cpBlur.forEach((cpBlur) => cpBlur.init());
// Update cp Mouse
this.directives.cpMouse.forEach((cpMouse) => cpMouse.init());
// Update cp change
this.directives.cpChange.forEach((cpChange) => cpChange.init());
// Update cp attr
this.directives.cpAttrs.forEach((cpAttr) => cpAttr.init());
}
/**
* @method void Atualiza os valores dos elementos HTML de acordo com o atributo que está sendo observado.
*/
public reload() {
if (!this.renderedView) { return; }
this.reloadDirectives();
this.reloadElementChildes(this.element, Common.getScope(this.element));
this.processInterpolation();
}
/**
* @description Percorre os elementos para processar os interpolations.
* @param element
*/
public processInterpolation() {
this.getNodeTexts().forEach((nodeTextReference) => this.interpolation(nodeTextReference));
}
public getNodeTexts(): any {
let n;
const a = [],
walk = document.createTreeWalker(this.element, NodeFilter.SHOW_TEXT, null, false);
while (n = walk.nextNode()) { a.push(n); }
return a;
}
public getInterpolationValue(content, childNode) {
try {
return Common.evalInMultiContext(childNode, content.trim().startsWith(':') ? content.trim().slice(1) : content, undefined) + '';
} catch (e) {
return '';
}
}
/**
* @description Função que modifica o texto da interpolação pelo determinado valor.
* @param childNode
*/
public interpolation(childNode) {
if (!Common.parentHasIgnore(childNode)) {
childNode.originalValue = childNode.originalValue || childNode.nodeValue;
childNode.$immutableInterpolation = childNode.$immutableInterpolation || false;
if (childNode.$immutableInterpolation) { return; }
let nodeModified = childNode.originalValue, str = childNode.originalValue;
str = Capivara.replaceAll(str, Constants.START_INTERPOLATION, '{{');
str = Capivara.replaceAll(str, Constants.END_INTERPOLATION, '}}');
(str.match(this.regexInterpolation) || []).forEach((key) => {
const content = key.replace('{{', '').replace('}}', '');
if (!childNode.$immutableInterpolation) {
try {
const evalValue = this.getInterpolationValue(content, childNode);
key = Capivara.replaceAll(key, '{{', Constants.START_INTERPOLATION);
key = Capivara.replaceAll(key, '}}', Constants.END_INTERPOLATION);
nodeModified = nodeModified.replace(key, evalValue);
childNode.nodeValue = nodeModified;
} catch (e) { }
}
if (content.trim().startsWith(':') && !childNode.$immutableInterpolation) {
childNode.$immutableInterpolation = true;
}
});
childNode.nodeValue = childNode.nodeValue.replace(this.regexInterpolation, '');
}
}
/**
* @method void Retorna um mapa de atributos e elementos escutando alterações desse atributo.
*/
public getCpModels() {
return this.directives.cpModels;
}
/**
* @method void Adiciona um tipo de bind em um mapa, esse bind possui um elemento HTML que será atualizado quando o valor do atributo for alterado.
* @param capivaraBind Tipo de bind que será monitorado.
*/
public addCpModels(capivaraBind) {
this.directives.cpModelsElements[capivaraBind.attribute] = this.directives.cpModelsElements[capivaraBind.attribute] || [];
this.directives.cpModelsElements[capivaraBind.attribute].push(capivaraBind);
}
/**
*
* @param child Elemento que está sendo criado o bind de model
*/
public createCPModel(child) {
this.directives.cpModels.push(new CPModel(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind de click
*/
public createCPClick(child) {
this.directives.cpClicks.push(new CPClick(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind de show
*/
public createCPShow(child) {
this.directives.cpShows.push(new CPShow(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do if
*/
public createCPIf(child) {
this.directives.cpIfs.push(new CPIf(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do else
*/
public createCPElse(child) {
this.directives.cpElses.push(new CPElse(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do else if
*/
public createCPElseIf(child) {
this.directives.cpElseIfs.push(new CPElseIf(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind de repeat.
*/
public createCPRepeat(child) {
this.directives.repeats.push(new CPRepeat(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do init.
*/
public createCPInit(child) {
this.directives.cpInits.push(new CPInit(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do style.
*/
public createCPStyle(child) {
this.directives.cpStyles.push(new CPStyle(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do class.
*/
public createCPClass(child) {
this.directives.cpClasses.push(new CPClass(child, this));
}
/**
*
* @param child Elemento que está sendo criado o bind do key.
*/
public createCPKey(child) {
this.directives.cpKeys.push(new CPKey(child, this));
}
/**
* @param child Elemento que está criando a bind do attr.
*/
public createCPAttr(child) {
this.directives.cpAttrs.push(new CPAttr(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do disable.
*/
public createCPDisabled(child) {
this.directives.cpDisables.push(new CPDisabled(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do focus.
*/
public createCPFocus(child) {
this.directives.cpFocus.push(new CPFocus(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do hide.
*/
public createCPHide(child) {
this.directives.cpHide.push(new CPHide(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do blur.
*/
public createCPBlur(child) {
this.directives.cpBlur.push(new CPBlur(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do dbTitle.
*/
public createCPmouse(child) {
this.directives.cpMouse.push(new CPMouse(child, this));
}
/**
* @param child Elemento que está sendo criado o bind do placeholder.
*/
public createCPChange(child) {
this.directives.cpChange.push(new CPChange(child, this));
}
}