• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const propsMap: string[] = ['disabled', 'hidden', 'checked', 'selected', 'required', 'open', 'readonly'];
17
18declare interface HTMLTemplateElement {
19    render(data: any): any
20}
21
22(HTMLTemplateElement as any).prototype.render = function (data: any) {
23    if (!this.$fragment) {
24        const rule = this.getAttribute("rule") || 'v-';
25        this.$fragment = this.cloneNode(true);
26        this.fragment = document.createElement('TEMPLATE');
27
28        // v-for Loop rendering
29        // <div v-for="list"></div>   =>    ${ list.map(function(item,index){ return '<div></div>' }).join('') }
30        const repeatEls = this.$fragment.content.querySelectorAll(`[\\${rule}for]`);
31        repeatEls.forEach((el: any) => {
32            const strFor = el.getAttribute(`${rule}for`);
33            const {isArray, items, params} = parseFor(strFor);
34            el.before('${Object.entries(' + items + ').map(function([' + `${(isArray ? '$index$' : (params[1] || 'name'))},${params[0] || (isArray ? 'item' : 'value')}],${params[2] || 'index'}` + '){ return `');
35            el.removeAttribute(`${rule}for`);
36            el.after('`}).join("")}');
37        })
38
39        // v-if Conditional rendering
40        // <div v-if="if"></div>   =>    ${ if ? '<div></div>' : '' }
41        const ifEls = this.$fragment.content.querySelectorAll(`[\\${rule}if]`);
42        ifEls.forEach((el: any) => {
43            const ifs = el.getAttribute(`${rule}if`);
44            el.before('${' + ifs + '?`');
45            el.removeAttribute(`${rule}if`);
46            el.after('`:`<!--if:' + el.tagName + '-->`}');
47        })
48
49        // fragment   <fragment>aa</fragment>   =>  aa
50        const fragments = this.$fragment.content.querySelectorAll('fragment,block');
51        fragments.forEach((el: any) => {
52            el.after(el.innerHTML);
53            el.parentNode.removeChild(el);
54        })
55    }
56    this.fragment.innerHTML = this.$fragment.innerHTML.interpolate(data);
57
58    // props
59    const propsEls = this.fragment.content.querySelectorAll(`[${propsMap.join('],[')}]`);
60    propsEls.forEach((el: any) => {
61        propsMap.forEach((props: any) => {
62            // If these attribute values are false, they are removed directly
63            if (el.getAttribute(props) === 'false') {
64                el.removeAttribute(props);
65            }
66        })
67    })
68    return this.fragment;
69}
70
71function parseFor(strFor: String) {
72    // Whether it is an object
73    const isObject = strFor.includes(' of ');
74    const reg = /\s(?:in|of)\s/g;
75    const [keys, obj] = strFor.match(reg) ? strFor.split(reg) : ["item", strFor];
76    const items = Number(obj) > 0 ? `[${'null,'.repeat(Number(obj) - 1)}null]` : obj;
77    const params = keys.split(/[\(|\)|,\s?]/g).filter(Boolean);
78    return {isArray: !isObject, items, params}
79}
80
81// String to template string
82(String as any).prototype.interpolate = function (params: any) {
83    const names = Object.keys(params);
84    const vals = Object.values(params);
85    const str = this.replace(/\{\{([^\}]+)\}\}/g, (all: any, s: any) => `\${${s}}`);
86    return new Function(...names, `return \`${escape2Html(str)}\`;`)(...vals);
87};
88
89// HTML Character inversion meaning   &lt;  =>  <
90function escape2Html(str: string) {
91    let arrEntities: any = {'lt': '<', 'gt': '>', 'nbsp': ' ', 'amp': '&', 'quot': '"'};
92    return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, function (all, t) {
93        return arrEntities[t];
94    });
95}
96