Created
June 11, 2010 02:40
-
-
Save mapiondev/433968 to your computer and use it in GitHub Desktop.
[JavaScript] テンプレート処理クラス
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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("<p>あなたの名前は@data@です</p>"); | |
* func({data:"hoge"}); // <p>あなたの名前はhogeです</p> | |
*/ | |
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