import global from './global'; export enum enum_render_type{ only_no_data, only_has_data, always } export interface IView_def{ name: string; data: string; parent: string; loop: boolean; autorender: boolean; rendertype: enum_render_type; static_tpl: boolean; } const ESCAPE_REGEXP = /(&|<|>|"|'|/)/g; const ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", '/': '/' //, // ' ':' ' }; function escape(str: string) { if (!str) { return ""; } return str.replace(ESCAPE_REGEXP, function(c) { return ESCAPE_MAP[c]; }); } import * as dom_construct from './dom-construct'; import * as dom_attr from './dom-attr'; import query = require('jquery'); import Data from './data'; import * as dot from 'dot'; export default class View{ private get_ref_node(p_name: string, domNode: HTMLElement): HTMLElement { let nl = query('[data-mm-presentation=' + p_name + ']', domNode); //presentation是否在donNode节点上 let is_root_node = dom_attr.get(domNode, 'data-mm-presentation') === p_name; if ((!nl || nl.length === 0) && !is_root_node) { console.error('cannot find dom to tpl! \n tpl_name:', p_name); return null; } // 如果 查询到有多个结点,只使用第一个(属于错误配置或调用) let ref_node = nl[0] || domNode; return ref_node; } private current_index: number; public render(domNode: HTMLElement, recursion?: boolean, position: string | number = 'only') : HTMLElement { if (!this.autorender || !domNode) { return null; } let name = this.name; if (!name) { return null; } let tpl = this.tpl; if (!tpl) { return null; } let ref_node: HTMLElement = this.get_ref_node(name, domNode); if (!ref_node) { return null; } //是静态页面 if (this.static_tpl) { let dom = dom_construct.to_dom(tpl); dom_construct.place(dom, ref_node, position); if (position === 'only' || position === 'first' || position === 'last') { dom = ref_node; } return dom; } let data = this.data; let row_count = data.row_count; if (this.rendertype === enum_render_type.only_no_data && row_count !== 0) { //清除遗留数据 ref_node.hasChildNodes() && dom_construct.empty(ref_node); return null; } else if (this.rendertype === enum_render_type.only_has_data && row_count === 0) { ref_node.hasChildNodes() && dom_construct.empty(ref_node); return null; } let loop = this.loop; let dom: HTMLElement; if (loop) { let win: Window = global; let doc = win.document; dom = doc.createDocumentFragment(); // 严格来讲,这里强制转换不正确,但js逻辑是正确的 let idx = data.index; this.current_index = 0; // 循环 data.reset(); if (this.rendertype === enum_render_type.always && row_count === 0) { dom = this._render(recursion); } while (data.next()) { let d = this._render(recursion); this.current_index++; if (d) { dom.appendChild(d); } } data.index = idx; delete this.current_index; } else { // 单行记录 dom = this._render(recursion); } dom_construct.place(dom, ref_node, position); if (position === 'only' || position === 'first' || position === 'last') { dom = ref_node; } return dom; } private _render(recursion: boolean): HTMLElement { let d = this.data; if(!d){ console.error('cannot find data'); return null; } let data = d.get_row(); if (!data) { data = { _id: '-1', __idx: 0, __len: 1 }; } else { data.__idx = d.index; data.__len = d.row_count; } let tpl = this.tpl; if (!tpl) { tpl = '
'; } let dd = {}; dd[this.name] = data; let parents = this.parents; parents.forEach(function(parent) { let name = parent.name; let reg = new RegExp('it.' + name + '.'); if (reg.test(tpl)) { let _data = parent.data.get_row(parent.current_index) || { _id: '-1' }; dd[name] = _data; } }); // this.children.forEach(function(child) { // let name = child.name; // let reg = new RegExp('it.' + name + '.'); // if (reg.test(tpl)) { // let _data = child.data.get_row() || []; // _data.__len = _data.length; // dd[name] = _data; // } // }); //抛出tpl模板中错误 try { let template = dot.template(tpl, null, dd); tpl = template(dd); } catch (e) { console.error(e.message, tpl); }; let dom = dom_construct.to_dom(tpl); //template.replace(/data-feidao-event-href/g, "href") if (recursion) { this.children.forEach(function(child) { child.render(dom, recursion); // 所有嵌套的子结点都是替换,不可能是追加 }); } return dom; } private parent:View; private _parents:View[]; private _children:View[] = []; private tpl: string; public get autorender(): boolean { return this.def.autorender; } public set autorender(autorender: boolean) { this.def.autorender = autorender; } private get name(): string { return this.def.name; } private get loop(): boolean { return this.def.loop; } // private get view_def(): string { // return this.def.view; // } private get rendertype(): enum_render_type{ return this.def.rendertype; } private get static_tpl(): boolean { return this.def.static_tpl; } private def: IView_def; private data: Data; constructor(def: IView_def, tpl: string, data: Data){ this.tpl = tpl; this.def = def; this.data = data; } public set_parent(parent: View): void { this.parent = parent; } public add_child(child: View): void { child && this.children.push(child); } public get parents(): View[]{ let parents = this._parents; if (!parents) { parents = this._parents = []; let parent = this.parent; if(parent){ parents.push(parent); parents = this._parents = parents.concat(parent.parents); } else { parents = this._parents = []; } } return parents; } private get children(): View[]{ return this._children; } public replace_field(): void { let tpl = this.tpl; let name = this.name; if(tpl && name){ tpl = escape(tpl); // 替换当前表名 let reg = new RegExp(name + '\\.', 'g'); // todo tpl = tpl.replace(reg, 'it.' + name + '.'); // p1.field => it.p1.field tpl = tpl.replace(/<\!--\s*{{/g, '{{'); // /g, '}}'); // }}--> => }} let parents = this.parents; // 替换父表名 parents.forEach((parent)=>{ let name = parent.name; reg = new RegExp(name + '\\.', 'g'); tpl = tpl.replace(reg, 'it.' + name + '.'); // p2.field => it.p2.field }); // 替换子表名???是否有必要??? // let children = this.children; // children.forEach((child)=>{ // let name = child.name; // reg = new RegExp(name + '\\.', 'g'); // tpl = tpl.replace(reg, 'it.' + name + '.'); // p2.field => it.p2.field 在父表渲染中,子表只有__len是可用的 // }); this.tpl = tpl; } } }