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 < => < 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