nornj
Version:
A powerful template engine that can works with React, JSX enhancement or alternative tools.
284 lines (244 loc) • 6.43 kB
text/typescript
import nj from '../core';
import * as tools from '../utils/tools';
import { Template } from '../interface';
const REGEX_NUM = /^(-?([0-9]+[.]?[0-9]+)|[0-9])$/;
//提取style内参数
export function styleProps(obj) {
//If the parameter is a style object,then direct return.
if (tools.isObject(obj) || tools.isArray(obj) || tools.isNumber(obj)) {
return obj;
}
//参数为字符串
const pattern = /([^\s:]+)[\s]?:[\s]?([^;]+)[;]?/g;
let matchArr, ret;
while ((matchArr = pattern.exec(obj))) {
let key = matchArr[1];
const value = matchArr[2];
if (!ret) {
ret = {};
}
//Convert to lowercase when style name is all capital.
if (/^[A-Z-]+$/.test(key)) {
key = key.toLowerCase();
}
//将连字符转为驼峰命名
key = tools.camelCase(key);
ret[key] = REGEX_NUM.test(value) ? Number(value) : value;
}
return ret;
}
//Get value from multiple datas
export function getData(this: Template.Context | void, prop, data, hasSource?) {
let value, obj;
if (!data) {
data = (this as Template.Context).data;
}
for (let i = 0, l = data.length; i < l; i++) {
obj = data[i];
if (obj) {
value = obj[prop];
if (value !== undefined) {
if (hasSource) {
return {
source: obj,
value,
prop,
_njSrc: true
};
}
return value;
}
}
}
}
export function getAccessorData(fn, context) {
if (fn == null) {
return fn;
}
if (fn._njTmpl) {
//模板函数
return fn.call(context);
} else {
//普通函数
return fn.call(context.$this, context);
}
}
export function getElement(name, global, nameO, context, subName?) {
let element;
if (!context.icp) {
element = global.cp[name];
} else {
element = getData(nameO, context.icp);
if (!element) {
element = global.cp[name];
}
}
if (subName != null && element) {
element = element[subName];
}
return element ? element : nameO;
}
export function getElementRefer(refer, name, global, nameO, context) {
return refer != null
? tools.isString(refer)
? getElement(refer.toLowerCase(), global, refer, context)
: refer
: getElement(name, global, nameO, context);
}
export function getElementName(refer, name) {
return refer != null && refer !== '' ? refer : name;
}
export function addArgs(props, dataRefer) {
const { args } = props;
if (args) {
for (let i = args.length; i--; ) {
dataRefer.unshift(args[i]);
}
}
}
//Rebuild local variables in the new context
export function newContext(context, params) {
if (!params) {
return context;
}
return tools.assign({}, context, {
data: params.data ? tools.arrayPush(params.data, context.data) : context.data,
parent: params.newParent ? context : context.parent,
index: params.index != null ? params.index : context.index,
item: params.item != null ? params.item : context.item
});
}
//修正属性名
export function fixPropName(name) {
switch (name) {
case 'class':
name = 'className';
break;
case 'for':
name = 'htmlFor';
break;
}
return name;
}
//合并字符串属性
export function assignStrProps(target, ...params) {
let ret = '';
const retObj = tools.assign(target, ...params);
for (const k in retObj) {
const v = retObj[k];
ret += ' ' + k + (k !== v ? '="' + v + '"' : ' ');
}
return ret;
}
//创建扩展标签子节点函数
export function exRet(global, context, fn) {
return function(param) {
return fn(global, context, param);
};
}
function _getLocalComponents(localConfigs) {
let icp;
if (localConfigs && localConfigs.components) {
icp = localConfigs.components;
if (!tools.isArray(icp)) {
icp = [icp];
}
}
return icp;
}
//构建可运行的模板函数
export function tmplWrap(configs, main) {
return function(this: Template.Context, param1, param2) {
const ctx = this,
data = tools.arraySlice(arguments);
return main(
configs,
ctx && ctx._njCtx
? tools.assign({}, ctx, {
data: tools.arrayPush(data, ctx.data)
})
: {
data,
getData,
get $this() {
return this.data[this.data.length - 1];
},
d: getData,
icp: _getLocalComponents(param1 && param1._njParam ? param2 : param1),
_njCtx: true
}
);
};
}
function levelSpace(context) {
if (context.level == null) {
return '';
}
let ret = '';
for (let i = 0; i < context.level; i++) {
ret += ' ';
}
return ret;
}
function firstNewline(context) {
return context.index == null ? '' : context.index == 0 ? '' : '\n';
}
function createElementApply(p) {
return nj.createElement.apply(null, p);
}
function callFilter(filter) {
return filter.source ? filter.value.bind(filter.source) : filter;
}
//创建模板函数
export function template(fns, tmplKey?) {
const configs: any = {
tmplKey,
us: fns.useString,
x: nj.extensions,
f: nj.filters,
np: tools.noop,
tf: tools.throwIf,
wn: tools.warn,
n: newContext,
c: getAccessorData,
sp: styleProps,
r: exRet,
e: getElement,
er: getElementRefer,
en: getElementName,
aa: addArgs,
an: tools.assign,
g: nj.global,
cf: callFilter
};
if (!configs.us) {
configs.h = nj.createElement;
configs.H = createElementApply;
configs.cp = nj.components;
} else {
configs.ans = assignStrProps;
configs.es = nj.escape;
configs.ls = levelSpace;
configs.fl = firstNewline;
}
tools.each(
fns,
(v, k) => {
if (k === 'main') {
//将每个主函数构建为可运行的模板函数
configs[k] = tmplWrap(configs, v);
tools.defineProps(configs[k], {
_njTmpl: {
value: true
}
});
configs['_' + k] = v;
} else if (k.indexOf('fn') === 0) {
//扩展标签函数
configs[k] = v;
}
},
false
);
return configs;
}