_.template()の引数にHTMLのコードスニペットを入れ(差し替える部分は<%- span_val %>のように括る)返された関数に指定のkey値を持ったObjectを入れるとコードスニペットに値が括られたHTMLが返される。
■使用例
var span_template = _.template("<span><%- span_val %></span>");
// re = "<span>TM Network : CAROL</span>";
var re = span_template( {span_val: "TM Network : CAROL"} );
■内部構造
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
var noMatch = /(.)^/;
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
_.template = function(text, settings, oldSettings) {
// 第2引数が null で第3引数がある場合。
if (!settings && oldSettings) settings = oldSettings;
// 第2引数と「_.templateSettings」に含まれている全てのプロパティを空のObjectにコピー、それが settings になる。
settings = _.defaults({}, settings, _.templateSettings);
// (settings.escape || noMatch).source = <%-([\s\S]+?)%>;
var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join("|") + "|$", "g");
// matcher = /<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$/g;
var index = 0;
var source = "__p+='";
// source に テンプレートタグをエスケープ処理して文字列連結する。
// CMS tool のエラーでリプレイスの綴りを変えています。
text.r(e)place(matcher, function(match, escape, interpolate, evaluate, offset) {
// 上の使用例だと1回目は match = <%- span_val %>; escape = span_val; interpolate = undefined; evaluate = undefined; offset = 6;
// 2回目は match = ; escape = undefined; interpolate = undefined; evaluate = undefined; offset = 28;
// エラーになる" u2028 " などをエスケープ処理する。
source += text.slice(index, offset).r(e)place(escaper, escapeChar);
index = offset + match.length;
// source にマッチした文字の結果を文字列連結する。
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} else if (evaluate) {
source += "'\n" + evaluate + "\n__p+='";
}
return match;
});
source += "'\n";
// 上の使用例だと
source = __p+='<span>'+
((__t=( span_val ))==null?'':_.escape(__t))+
'</span>';
// settings.variable は第2引数で意図的にセットしないと入らないはずなので、ここの If 文は実行される。
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
// source = with(obj||{}){
__p+='<span>'+
((__t=( span_val ))==null?'':_.escape(__t))+
'</span>';
}
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
// source = var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='<span>'+
((__t=( span_val ))==null?'':_.escape(__t))+
'</span>';
}
return __p;
// テンプレートを設定する関数を新規作成。
try {
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
// render = function anonymous(obj, _) {
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='<span>'+
((__t=( span_val ))==null?'':_.escape(__t))+
'</span>';
}
return __p;
}
var template = function(data) {
return render.call(this, data, _);
};
var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
// template.source = function(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='<span>'+
((__t=( span_val ))==null?'':_.escape(__t))+
'</span>';
}
return __p;
}
// render関数が返される。
return template;
};
// 返されるrender関数を解析。
render = function (obj, _) {
// 解析するため、_関数の外で関数を実行しているので少し改造。_を定義。
var _ = window._;
var __t,
__p = "",
__j = Array.prototype.join,
// この「print」の定義の文がなくても このrender関数は動く。
print = function(){ __p+=__j.call( arguments,"" ); };
// with(obj){ ttt = span_val; } と書いた場合は ttt = TM Network : CAROL;
// with構文の独特な使い方。
with(obj||{}){
__p+= "<span>" + ( (__t = ( span_val ) ) == null ? "" : _.escape(__t) ) + "</span>";
}
return __p;
}
// re = "<span>TM Network : CAROL</span>";
var re = render( { span_val : "TM Network : CAROL" } );