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