• 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: unknown): unknown;
20}
21//@ts-ignore
22(HTMLTemplateElement as unknown).prototype.render = function (data: unknown): HTMLElement {
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: unknown) => {
32      //@ts-ignore
33      const strFor = el.getAttribute(`${rule}for`);
34      const { isArray, items, params } = parseFor(strFor); //@ts-ignore
35      el.before(
36        '${Object.entries(' +
37          items +
38          ').map(function([' +
39          `${isArray ? '$index$' : params[1] || 'name'},${params[0] || (isArray ? 'item' : 'value')}],${
40            params[2] || 'index'
41          }` +
42          '){ return `'
43      ); //@ts-ignore
44      el.removeAttribute(`${rule}for`); //@ts-ignore
45      el.after('`}).join("")}');
46    });
47
48    // v-if Conditional rendering
49    // <div v-if="if"></div>   =>    ${ if ? '<div></div>' : '' }
50    const ifEls = this.$fragment.content.querySelectorAll(`[\\${rule}if]`);
51    ifEls.forEach((el: unknown) => {
52      //@ts-ignore
53      const ifs = el.getAttribute(`${rule}if`); //@ts-ignore
54      el.before('${' + ifs + '?`'); //@ts-ignore
55      el.removeAttribute(`${rule}if`); //@ts-ignore
56      el.after('`:`<!--if:' + el.tagName + '-->`}');
57    });
58
59    // fragment   <fragment>aa</fragment>   =>  aa
60    const fragments = this.$fragment.content.querySelectorAll('fragment,block');
61    fragments.forEach((el: unknown) => {
62      //@ts-ignore
63      el.after(el.innerHTML); //@ts-ignore
64      el.parentNode.removeChild(el);
65    });
66  }
67  this.fragment.innerHTML = this.$fragment.innerHTML.interpolate(data);
68
69  // props
70  const propsEls = this.fragment.content.querySelectorAll(`[${propsMap.join('],[')}]`);
71  propsEls.forEach((el: unknown) => {
72    propsMap.forEach((props: unknown) => {
73      // If these attribute values are false, they are removed directly
74      //@ts-ignore
75      if (el.getAttribute(props) === 'false') {
76        //@ts-ignore
77        el.removeAttribute(props);
78      }
79    });
80  });
81  return this.fragment;
82};
83
84function parseFor(strFor: String): { isArray: boolean; items: string | String; params: string[] } {
85  // Whether it is an object
86  const isObject = strFor.includes(' of ');
87  const reg = /\s(?:in|of)\s/g;
88  const [keys, obj] = strFor.match(reg) ? strFor.split(reg) : ['item', strFor];
89  const items = Number(obj) > 0 ? `[${'null,'.repeat(Number(obj) - 1)}null]` : obj;
90  const params = keys.split(/[\(|\)|,\s?]/g).filter(Boolean);
91  return { isArray: !isObject, items, params };
92}
93
94// String to template string
95//@ts-ignore
96(String as unknown).prototype.interpolate = function (params: unknown): Function {
97  //@ts-ignore
98  const names = Object.keys(params);
99  // @ts-ignore
100  const vals = Object.values(params);
101  const str = this.replace(/\{\{([^\}]+)\}\}/g, (all: unknown, s: unknown) => `\${${s}}`);
102  // @ts-ignore
103  return new Function(...names, `return \`${escape2Html(str)}\`;`)(...vals);
104};
105
106// HTML Character inversion meaning   &lt;  =>  <
107function escape2Html(str: string): string {
108  let arrEntities: unknown = { lt: '<', gt: '>', nbsp: ' ', amp: '&', quot: '"' };
109  return str.replace(/&(lt|gt|nbsp|amp|quot);/gi, function (all, t) {
110    //@ts-ignore
111    return arrEntities[t];
112  });
113}
114
115export function replacePlaceholders(str: string, ...args: string[]): string {
116  return str.replace(/\{(\d+)\}/g, (match, placeholderIndex) => {
117    const argIndex = parseInt(placeholderIndex, 10);
118    const replacement = args[argIndex - 1];
119    return replacement || match;
120  });
121}
122