Skip to content

Instantly share code, notes, and snippets.

@mapiondev
Created June 11, 2010 02:40
Show Gist options
  • Save mapiondev/433968 to your computer and use it in GitHub Desktop.
Save mapiondev/433968 to your computer and use it in GitHub Desktop.
[JavaScript] テンプレート処理クラス
// UTF-8
/**
* @namespace
* @private
*/
window.MAPION=window.MAPION||{};
/**
* @namespace
* @private
*/
MAPION.tmpl=MAPION.tmpl||{};
/**
* Templateクラスのインスタンスを生成します。
* @class テンプレート関数生成クラス。
* @author <a href="http://labs.mapion.co.jp">mapion</a>
* @param {Object} [tagMap=null] テンプレートにおけるタグ⇔データ置換を定義するハッシュマップを指定します。指定が無ければソース文字列から自動抽出されます。
* @param {Object} [parameters] compileの挙動を指定するパラメータオブジェクトです。
* @param {Object} [parameters.addSfx=undefined] HTMLタグを解析しsfx属性を付与します。
*/
MAPION.tmpl.Template=function(tagMap,parameters){
this.idTagName=MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_START_MARKER+MAPION.tmpl.Template.DEFAULT_ID_TAG_VALUE+MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_END_MARKER;
this.idTagValue=MAPION.tmpl.Template.DEFAULT_ID_TAG_VALUE;
this.sfxAttributeName=MAPION.tmpl.Template.DEFAULT_SFX_ATTRIBUTE_NAME;
this.tagDirectiveStartMarker=MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_START_MARKER;
this.tagDirectiveEndMarker=MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_END_MARKER;
this.scriptDirectiveStartMarker=MAPION.tmpl.Template.DEFAULT_SCRIPT_DIRECTIVE_START_MARKER;
this.scriptDirectiveEndMarker=MAPION.tmpl.Template.DEFAULT_SCRIPT_DIRECTIVE_END_MARKER;
this.variableDirectiveStartMarker=MAPION.tmpl.Template.DEFAULT_VARIABLE_DIRECTIVE_START_MARKER;
this.variableDirectiveEndMarker=MAPION.tmpl.Template.DEFAULT_VARIABLE_DIRECTIVE_END_MARKER;
this.functionArgumentName=MAPION.tmpl.Template.DEFAULT_FUNCTION_ARGUMENT_NAME;
this.returnVarString=MAPION.tmpl.Template.DEFAULT_RETURN_VAR_STRING;
this.banpei=this.tagDirectiveStartMarker+this.tagDirectiveStartMarker+this.tagDirectiveStartMarker+this.tagDirectiveStartMarker;
this.tagMap=tagMap||null;
this.parameters=parameters;
};
/**
* @private
*/
MAPION.tmpl.Template.REV="$Rev$";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_START_MARKER = "@";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_TAG_DIRECTIVE_END_MARKER = "@";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_ID_TAG_VALUE = "id";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_SFX_ATTRIBUTE_NAME = "sfx";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_SCRIPT_DIRECTIVE_START_MARKER = "<!--$";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_SCRIPT_DIRECTIVE_END_MARKER = "$-->";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_VARIABLE_DIRECTIVE_START_MARKER = "%=";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_VARIABLE_DIRECTIVE_END_MARKER = "%";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_FUNCTION_ARGUMENT_NAME = "data";
/**
* @private
*/
MAPION.tmpl.Template.DEFAULT_RETURN_VAR_STRING = "___S___";
/**
* @private
*/
MAPION.tmpl.Template.randamString = function(){
return new Date().getTime()+"_"+parseInt(Math.random()*99999999);
};
/**
* テンプレート関数を生成します。
* @param {String} source テンプレートソースです。
* @param {Object} [parameters] compileの挙動を指定するパラメータオブジェクトです。
* @param {Object} [parameters.addSfx=undefined] HTMLタグを解析しsfx属性を付与します。
* @returns {Function} テンプレート関数です。
* @example
* var func = new MAPION.tmpl.Template({"@data@":"data"}).compile("&lt;p&gt;あなたの名前は@data@です&lt;/p&gt;");
* func({data:"hoge"}); // &lt;p&gt;あなたの名前はhogeです&lt;/p&gt;
*/
MAPION.tmpl.Template.prototype.compile=function(source,parameters){
if(typeof(source)!=="string")throw new Error("MAPION.tmpl.Template::compile() Illegal argumented error...");
parameters = parameters?parameters:this.parameters;
if(!parameters)parameters = {};
var addSfx = parameters.addSfx;
if(typeof(addSfx)==="undefined")addSfx=false;
var tagMap = this.tagMap||this.getTagMap(source);
tagMap[this.idTagName]=tagMap[this.idTagName]||this.idTagValue;
source = this.banpei + source + this.banpei;
var target="function_"+ MAPION.tmpl.Template.randamString();
var s1 = addSfx?this.addSfxTag(source):source;
var s2 = this.splitElement(s1,tagMap);
var s3 = this.buildFunctionString(s2);
var s4 = target + "=" + s3;
eval("var "+s4);
return eval(target);
};
/**
* @private
*/
MAPION.tmpl.Template.prototype.getTagMap=function(source){
var s=0,e=0,tagMap={},tdsm=this.tagDirectiveStartMarker,tdem=this.tagDirectiveEndMarker,lp;
var tdsml=tdsm.length,tdeml=tdem.length;
while(true){
s = source.indexOf(tdsm,s);
e = source.indexOf(tdem,s+tdsml);
if(s>-1&&e>-1){
if(tdem===")"){ // jsmf版でもaaa(bbb)で関数呼び出し出来るようにする超例外処理...
lp = source.indexOf("(",s+tdsml);
while(lp!==-1&&e!==-1&&lp<e){
e = source.indexOf(tdem,e+1);
lp = source.indexOf("(",lp+1);
};
if(e===-1)throw new Error("Illega format... near:"+source.substring(s,s+20)+"...");
};
var value = source.substring(s+tdsml,e);
tagMap[tdsm+value+tdem]=value;
s=e+tdeml;
}else{
return tagMap;
};
};
};
/**
* @private
*/
MAPION.tmpl.Template.prototype.addSfxTag=function(source){
var r = null;
r = source.match(/(?:<[^\/|!][^>]*[\/]?[^-]>|<[^\/|!|>|-]{1}>)/g);
if(r){
for(var i=0,l=r.length;i<l;i++){
var orgStr = r[i];
var idTag = this.idTagName;
if(orgStr.match(/\/>$/g)){
var repStr = orgStr.replace(/\/>/g," "+this.sfxAttributeName+"=\""+idTag+"\" />")
}else{
var repStr = orgStr.replace(/>/g," "+this.sfxAttributeName+"=\""+idTag+"\" >")
};
source = source.replace(orgStr,repStr);
};
};
return source;
};
/**
* @private
*/
MAPION.tmpl.Template.prototype.splitElement=function(source,tagMap){
// Tag
var i,l,i2,l2,ret,a,a1=[source],a2=[],s,si,ei,ps,s2,si2,ei2,ps2,
vdsm=this.variableDirectiveStartMarker,vdem=this.variableDirectiveEndMarker,
sdsm=this.scriptDirectiveStartMarker,sdem=this.scriptDirectiveEndMarker;
var vdsml=vdsm.length,vdeml=vdem.length,sdsml=sdsm.length,sdeml=sdem.length;
for(var tag in tagMap){
ret = [];
for(i=0,l=a1.length;i<l;i++){
s = a1[i];
if(s.t){
ret.push(s);
}else{
a = s.split(tag);
for(i2=0,l2=a.length;i2<l2;i2++){
ret[ret.length]=a[i2];
if(typeof(a[i2+1])!=="undefined"){
var str = tagMap[tag].toString();
var r1 = str.match(/[^()\s]+/g);
if(r1.length===2){
ret[ret.length]={t:"ft",sf:r1[0],st:r1[1],tag:tag};
}else{
var r2 = str.match(/[^\|\s]+/g);
if(r2.length===2){
ret[ret.length]={t:"ft",sf:r2[1],st:r2[0],tag:tag};
}else{
ret[ret.length]={t:"t",s:str,tag:tag};
};
};
};
};
};
};
a1 = ret;
};
// Variable
for(i=0,l=a1.length;i<l;i++){
s = a1[i];
if(s.t){
a2[a2.length]=s;
}else{
si=0,ei=0,ps=0;
while(si>-1){
ps = si;
si = s.indexOf(vdsm,si);
ei = s.indexOf(vdem,si+vdeml);
if(si>-1&&ei>-1){
// Script (this block is mostly same as below)
s2=s.substring(ps,si);
si2=0,ei2=0,ps2=0;
while(si2>-1){
ps2 = si2;
si2 = s2.indexOf(sdsm,si2);
ei2 = s2.indexOf(sdem,si2+sdeml);
if(si2>-1&&ei2>-1){
a2[a2.length]=s2.substring(ps2,si2);
a2[a2.length]={t:"s",s:s2.substring(si2+sdsml,ei2)};
si2=ei2+sdeml;
}else{
a2[a2.length]=s2.substring(ps2);
};
};
// end
a2[a2.length]={t:"v",s:s.substring(si+vdsml,ei)};
si=ei+vdeml;
}else{
// Script (this block is mostly same as above)
s2=s.substring(ps);
si2=0,ei2=0,ps2=0;
while(si2>-1){
ps2 = si2;
si2 = s2.indexOf(sdsm,si2);
ei2 = s2.indexOf(sdem,si2+sdeml);
if(si2>-1&&ei2>-1){
a2[a2.length]=s2.substring(ps2,si2);
a2[a2.length]={t:"s",s:s2.substring(si2+sdsml,ei2)};
si2=ei2+sdeml;
}else{
a2[a2.length]=s2.substring(ps2);
};
};
// end
};
};
};
};
return a2;
};
/**
* @private
*/
MAPION.tmpl.Template.prototype.buildFunctionString=function(elements){
var arg = this.functionArgumentName;
var sb = this.returnVarString;
var s=[];
var q=/'/g,q2=/"/g;
var regR=new RegExp("\r","g"),
regN=new RegExp("\n","g");
s[s.length]="function("+arg+"){var "+sb+"=[];";
for(var i=0,l=elements.length;i<l;i++){
var element = elements[i];
if(element===this.banpei)continue;
if(element.t==="s"){
s[s.length]=(element.s);
}else if(element.t==="v"){
s[s.length]=(sb+".push("+element.s+");");
}else if(element.t==="t"){
s[s.length]=("try{if(typeof "+arg+"."+element.s+"==='function'){"+sb+".push("+arg+"."+element.s+"());}else{"+sb+".push("+arg+"."+element.s+");};}catch(error){"+sb+".push('"+element.tag+"')};");
}else if(element.t==="ft"){
s[s.length]=("try{"+sb+".push("+arg+"."+element.sf+"("+arg+"."+element.st+"));}catch(error){"+sb+".push('"+element.tag+"')};");
}else{
while(element.indexOf(this.banpei)>-1)element=element.replace(this.banpei,"");
element = element.replace(q,"\\'").replace(q2,'\\"').replace(regR,"\\r").replace(regN,"\\n");
s[s.length]=sb+".push('"+element+"');";
};
};
s[s.length]="return "+sb+".join('');}";
return s.join("");
};
/**
* @private
*/
MAPION.tmpl.Template.jsmf=(function(){
var t=new MAPION.tmpl.Template();
t.tagDirectiveStartMarker="$(";
t.tagDirectiveEndMarker=")";
t.scriptDirectiveStartMarker="#$";
t.scriptDirectiveEndMarker="$#";
t.variableDirectiveStartMarker="#=";
t.variableDirectiveEndMarker="#";
return t;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment