/*
* Copyright (C) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const propsMap: string[] = ['disabled', 'hidden', 'checked', 'selected', 'required', 'open', 'readonly'];
declare interface HTMLTemplateElement {
render(data: unknown): unknown;
}
//@ts-ignore
(HTMLTemplateElement as unknown).prototype.render = function (data: unknown): HTMLElement {
if (!this.$fragment) {
const rule = this.getAttribute('rule') || 'v-';
this.$fragment = this.cloneNode(true);
this.fragment = document.createElement('TEMPLATE');
// v-for Loop rendering
//
=> ${ list.map(function(item,index){ return '' }).join('') }
const repeatEls = this.$fragment.content.querySelectorAll(`[\\${rule}for]`);
repeatEls.forEach((el: unknown) => {
//@ts-ignore
const strFor = el.getAttribute(`${rule}for`);
const { isArray, items, params } = parseFor(strFor); //@ts-ignore
el.before(
'${Object.entries(' +
items +
').map(function([' +
`${isArray ? '$index$' : params[1] || 'name'},${params[0] || (isArray ? 'item' : 'value')}],${
params[2] || 'index'
}` +
'){ return `'
); //@ts-ignore
el.removeAttribute(`${rule}for`); //@ts-ignore
el.after('`}).join("")}');
});
// v-if Conditional rendering
// => ${ if ? '' : '' }
const ifEls = this.$fragment.content.querySelectorAll(`[\\${rule}if]`);
ifEls.forEach((el: unknown) => {
//@ts-ignore
const ifs = el.getAttribute(`${rule}if`); //@ts-ignore
el.before('${' + ifs + '?`'); //@ts-ignore
el.removeAttribute(`${rule}if`); //@ts-ignore
el.after('`:``}');
});
// fragment aa => aa
const fragments = this.$fragment.content.querySelectorAll('fragment,block');
fragments.forEach((el: unknown) => {
//@ts-ignore
el.after(el.innerHTML); //@ts-ignore
el.parentNode.removeChild(el);
});
}
this.fragment.innerHTML = this.$fragment.innerHTML.interpolate(data);
// props
const propsEls = this.fragment.content.querySelectorAll(`[${propsMap.join('],[')}]`);
propsEls.forEach((el: unknown) => {
propsMap.forEach((props: unknown) => {
// If these attribute values are false, they are removed directly
//@ts-ignore
if (el.getAttribute(props) === 'false') {
//@ts-ignore
el.removeAttribute(props);
}
});
});
return this.fragment;
};
function parseFor(strFor: String): { isArray: boolean; items: string | String; params: string[] } {
// Whether it is an object
const isObject = strFor.includes(' of ');
const reg = /\s(?:in|of)\s/g;
const [keys, obj] = strFor.match(reg) ? strFor.split(reg) : ['item', strFor];
const items = Number(obj) > 0 ? `[${'null,'.repeat(Number(obj) - 1)}null]` : obj;
const params = keys.split(/[\(|\)|,\s?]/g).filter(Boolean);
return { isArray: !isObject, items, params };
}
// String to template string
//@ts-ignore
(String as unknown).prototype.interpolate = function (params: unknown): Function {
//@ts-ignore
const names = Object.keys(params);
// @ts-ignore
const vals = Object.values(params);
const str = this.replace(/\{\{([^\}]+)\}\}/g, (all: unknown, s: unknown) => `\${${s}}`);
// @ts-ignore
return new Function(...names, `return \`${escape2Html(str)}\`;`)(...vals);
};
// HTML Character inversion meaning < => <
function escape2Html(str: string): string {
let arrEntities: unknown = { lt: '<', gt: '>', nbsp: ' ', amp: '&', quot: '"' };
return str.replace(/&(lt|gt|nbsp|amp|quot);/gi, function (all, t) {
//@ts-ignore
return arrEntities[t];
});
}
export function replacePlaceholders(str: string, ...args: string[]): string {
return str.replace(/\{(\d+)\}/g, (match, placeholderIndex) => {
const argIndex = parseInt(placeholderIndex, 10);
const replacement = args[argIndex - 1];
return replacement || match;
});
}