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 29 const repeatEls = this.$fragment.content.querySelectorAll(`[\\${rule}for]`); 30 repeatEls.forEach((el: any) => { 31 const strFor = el.getAttribute(`${rule}for`); 32 const {isArray, items, params} = parseFor(strFor); 33 el.before('${Object.entries(' + items + ').map(function([' + `${(isArray ? '$index$' : (params[1] || 'name'))},${params[0] || (isArray ? 'item' : 'value')}],${params[2] || 'index'}` + '){ return `'); 34 el.removeAttribute(`${rule}for`); 35 el.after('`}).join("")}'); 36 }) 37 38 // v-if 39 const ifEls = this.$fragment.content.querySelectorAll(`[\\${rule}if]`); 40 ifEls.forEach((el: any) => { 41 const ifs = el.getAttribute(`${rule}if`); 42 el.before('${' + ifs + '?`'); 43 el.removeAttribute(`${rule}if`); 44 el.after('`:`<!--if:' + el.tagName + '-->`}'); 45 }) 46 47 // fragment 48 const fragments = this.$fragment.content.querySelectorAll('fragment,block'); 49 fragments.forEach((el: any) => { 50 el.after(el.innerHTML); 51 el.parentNode.removeChild(el); 52 }) 53 } 54 this.fragment.innerHTML = this.$fragment.innerHTML.interpolate(data); 55 56 // props 57 const propsEls = this.fragment.content.querySelectorAll(`[${propsMap.join('],[')}]`); 58 propsEls.forEach((el: any) => { 59 propsMap.forEach((props: any) => { 60 if (el.getAttribute(props) === 'false') { 61 el.removeAttribute(props); 62 } 63 }) 64 }) 65 return this.fragment; 66} 67 68function parseFor(strFor: String) { 69 const isObject = strFor.includes(' of '); 70 const reg = /\s(?:in|of)\s/g; 71 const [keys, obj] = strFor.match(reg) ? strFor.split(reg) : ["item", strFor]; 72 const items = Number(obj) > 0 ? `[${'null,'.repeat(Number(obj) - 1)}null]` : obj; 73 const params = keys.split(/[\(|\)|,\s?]/g).filter(Boolean); 74 return {isArray: !isObject, items, params} 75} 76 77(String as any).prototype.interpolate = function (params: any) { 78 const names = Object.keys(params); 79 const vals = Object.values(params); 80 const str = this.replace(/\{\{([^\}]+)\}\}/g, (all: any, s: any) => `\${${s}}`); 81 return new Function(...names, `return \`${escape2Html(str)}\`;`)(...vals); 82}; 83 84function escape2Html(str: string) { 85 let arrEntities: any = {'lt': '<', 'gt': '>', 'nbsp': ' ', 'amp': '&', 'quot': '"'}; 86 return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, function (all, t) { 87 return arrEntities[t]; 88 }); 89} 90