仿服务器端脚本方式的JS模板实现方法

  http://bbs.51js.com/thread-65160-1-1.html

  <html xmlns="http://www.w3.org/1999/xht...

  <head>

  <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />

  <title>jssp演示</title>

  <script language="javascript">

  /**

  *  @description:

  *  使用javascript模仿JSP的页面解析和运行,运行于客户端

  *  允许应用人员象开发JSP页面一样使用<%..%>

  *  允许页面动态包括子页面(同步读取页面)

  *

  **/

  //@--------------------------------------------------------------------- JSSP声明

  var jssp=function(){};

  /**

  * 页面缓存管理实例对象

  */

  jssp.cacheInstance=null;

  /**

  * 页面加载实例对象

  */

  jssp.pageLoaderInstance=null;

  /**

  * 在指定dom插入pagePath的执行后的页面内容

  */

  jssp.render=function(pagePath,dom){

  if(typeof dom=="string") dom=document.getElementById(dom);

  var content=jssp.Core.run(pagePath);

  dom.innerHTML=content;

  }

  //@------------------------------------------------------------------------ JSSP运行配置

  /**

  * 引擎运行全局配置

  */

  jssp.Config={};

  /**

  * 如果在客户端运行,是否缓存解析的页面

  */

  jssp.Config.cachable=true;

  /**

  * 当jssp.Config.cacheable为true且在

  */

  jssp.Config.cacheClass="jssp.Cache.DefaultCache";

  /**

  * 页面内容读取器

  */

  jssp.Config.pageLoaderClass="jssp.Core.PageLoader.Ajax";

  //@------------------------------------------------------------------------ JSSP页面缓存类

  /**

  * 页面缓存类

  */

  jssp.Cache=function(){}

  /**

  * 设置缓存

  */

  jssp.Cache.prototype.set=function(key,cache){}

  /**

  * 得到缓存

  */

  jssp.Cache.prototype.get=function(key){}

  /**

  * 默认的缓存实现类

  */

  jssp.Cache.DefaultCache=function(){

  this.caches={};

  }

  jssp.Cache.DefaultCache.prototype.set=function(key,cache){

  this.caches[key]=cache;

  }

  jssp.Cache.DefaultCache.prototype.get=function(key){

  return this.caches[key];

  }

  //@----------------------------------------------------------------------- JSSP运行上下文类

  /**

  * jssp页面的执行上下文对象

  * @member params 请求参数数组

  * @member cookies 操作cookies对象 jssp.Cookies

  * @member out     页面流输出对象   jssp.Out

  * @method setAttribute  设置上下文参数

  * @method getAttribute  得到上下文参数

  * @method removeAttribute 删除上下文参数

  * @method include         动态包含子页面

  * @method getCookies,getParameter,getParameters,getOut

  * @param pageUrl  运行的上下文参数

  * @param context  父页面的上下文对象

  */

  jssp.Context=function(pageUrl,context){

  this.params=this._resolveParam(pageUrl);

  if(!context){

  this.cookies=jssp.Cookies;

  this.out=new jssp.Out();

  this.attributes=[];

  }else{

  this.context=context;

  this.isIncluded=true;

  }

  }

  /**

  * 解析页面后缀参数

  */

  jssp.Context.prototype._resolveParam=function(pageUrl){

  var i1=pageUrl.indexOf("?");

  if(i1<=0) return [];

  pageUrl=pageUrl.substring(i1+1);

  var s1=pageUrl.split("&");

  var params=[];

  for(var i=0;i<s1.length;i++){

  var s2=s1[i].split("=");

  var key=s2[0];var value=s2[1];

  var ps=params[key];

  if(!ps) ps=[];

  ps[ps.length]=value;

  params[key]=ps;

  }

  return params;

  }

  /**

  * 设置参数值

  */

  jssp.Context.prototype.setAttribute=function(key,value){

  if(!this.context)

  this.attributes[key]=value;

  else

  this.context.setAttribute(key,value);

  }

  /**

  * 得到参数值

  */

  jssp.Context.prototype.getAttribute=function(key){

  if(!this.context)

  return this.attributes[key];

  else

  return this.context.getAttribute(key);

  }

  /**

  * 删除指定键的参数值

  */

  jssp.Context.prototype.removeAttribute=function(key){

  if(!this.context)

  this.attributes[key]=undefined;

  else

  this.context.removeAttribute(key);

  }

  /**

  * 得到请求参数值

  */

  jssp.Context.prototype.getParameter=function(key){

  var ps=this.params[key];

  if(!ps) return this.context?this.context.getParameter(key):undefined;

  return ps.join(",");

  }

  /**

  * 得到有重复参数的值

  */

  jssp.Context.prototype.getParameters=function(key){

  var pss=this.params[key];

  if(!pss) pss=this.context?this.context.getParameters(key):undefined;

  return pss;

  }

  /**

  * 得到cookies对象

  */

  jssp.Context.prototype.getCookies=function(){

  if(!this.context)

  return this.cookies;

  else

  return this.context.getCookies();

  }

  /**

  * 得到输出流OUT对象

  */

  jssp.Context.prototype.getOut=function(){

  if(!this.context)

  return this.out;

  else

  return this.context.getOut();

  }

  /**

  * 动态包含子页面

  */

  jssp.Context.prototype.include=function(childPageUrl){

  this.getOut().print(jssp.Core.run(childPageUrl,this));

  }

  jssp.Context.prototype.isIncluded=false;//判断当前页面是否被包含的

  //@-----------------------------------------------------------------------JSSP运行cookies操作类

  /**

  * 简单操纵cookies方法

  */

  jssp.Cookies=function(){}

  /**

  * 设置cookie项

  */

  jssp.Cookies.set=function(key,value){

  document.cookie=key+"="+escape(value)+";";

  }

  /**

  * 得到cookie项

  */

  jssp.Cookies.get=function(key){

  var aCookie=document.cookie.split("; ");

  for(var i=0;i<aCookie.length;i++){

  var aCrumb=aCookie[i].split("=");

  if(key==aCrumb[0])

  return unescape(aCrumb[1]);

  }

  }

  /**

  * 删除cookies项

  */

  jssp.Cookies.remove=function(key){

  document.cookie=key+"=null; expires=Fri, 31 Dec 1999 23:59:59 GMT;";

  }

  //@------------------------------------------------------------------------ JSSP页面运行输出流类

  /**

  * 页面流输出对象

  */

  jssp.Out=function(){

  this.datas=[];//数据流片断

  this._index=0;

  }

  /**

  * 把页面流片断放入缓冲区

  */

  jssp.Out.prototype.print=function(s){

  this.datas[this._index++]=s;

  }

  /**

  * 输出缓冲区里的数据

  */

  jssp.Out.prototype.flush=function(){

  var data=this.datas.join("");

  this.datas=[];this._index=0;

  return data;

  }

  //@--------------------------------------------------------------------------JSSP页面核心类声明

  jssp.Core=function(){}

  //@--------------------------------------------------------------------------JSSP页面解析实现类

  /**

  * 页面解析

  * @param pageContent JSSP页面内容

  */

  jssp.Core.parse=function(pageContent){

  var strBuffer=[];//解析后文本存放的缓冲区

  var point=0;//缓冲区指针

  var lineNumber=1;//解析的当前行

  try{

  var betweenPerc=false;

  var isPrint=false;

  strBuffer[point++]="function($context){\n";

  strBuffer[point++]="var $out=$context.getOut();\n";

  strBuffer[point++]="var $cookies=$context.getCookies();\n";

  strBuffer[point++]="try{\n";

  strBuffer[point++]="$out.print(unescape('";

  var line="";

  var value=pageContent;

  var len=value.length;

  for(var i=0;i<len;i++){

  var nextTwo="";

  if(i<=len-2) nextTwo=value.charAt(i)+value.charAt(i+1);

  var nextThree="";

  if(i<=len-3) nextThree=nextTwo+value.charAt(i+2);

  if(nextTwo=="<%"&&nextThree!="<%="&&nextThree!="<%@"){

  strBuffer[point++]="'));\n";

  betweenPerc=true;

  i+=1;

  }else if(nextTwo=="<%"&&nextThree=="<%="&&nextThree!="<%@"){

  strBuffer[point++]=escape(line)+"'));\n";

  line="";

  strBuffer[point++]="   $out.print( ";

  betweenPerc=true;

  isPrint=true;

  i+=2;

  }else if(nextTwo=="<%"&&nextThree!="<%="&&nextThree=="<%@"){

  i+=3;

  var directive="";

  while(nextTwo!="%>"){

  directive+=value.charAt(i);

  i++;

  if(i<=value.length-2){

  nextTwo=value.charAt(i)+value.charAt(i+1);

  }

  }

  strBuffer[point++]=escape(line)+"'));\n";

  line="";

  strBuffer[point++]=jssp.Core.parse._handleDirective(directive);

  strBuffer[point++]="   $out.print(unescape('";

  i++;

  }else if(nextTwo=="%>"){

  strBuffer[point++]=(isPrint?");":"")+"\n   $out.print(unescape('";

  if(!betweenPerc) throw new jssp.Core.parse.ParseException("解析错误","必须用'%>'作为结束标签");

  betweenPerc=false;

  isPrint=false;

  i+=1;

  }else if(value.charAt(i)==String.fromCharCode(10)){

  if(!betweenPerc){

  strBuffer[point++]=escape(line)+"\\n'));\n"+"   $out.print(unescape('";

  line="";

  lineNumber++;

  }

  }else if(value.charAt(i)==String.fromCharCode(13)){

  if(betweenPerc) strBuffer[point++]="\n";

  }else{

  if(betweenPerc)

  strBuffer[point++]=value.charAt(i);

  else

  line+=value.charAt(i);

  }

  }

  strBuffer[point++]=escape(line)+"'));\n";

  strBuffer[point++]="}catch(e){\n";

  strBuffer[point++]="return '"+"执行页面发生异常.异常类型:'+e.name+'. 错误消息: '+e.message;\n";

  strBuffer[point++]="}\n";

  strBuffer[point++]="if(!$context.isIncluded) return $out.flush();\n";

  strBuffer[point++]="}\n";

  }catch(e){

  point=0;

  strBuffer=[];

  strBuffer[point++]="function($context){\n";

  strBuffer[point++]="return \""+"An exception occurred while parsing on line "+lineNumber+". Error type: "+e.name+". Error message: "+e.message+"\";";

  strBuffer[point++]="}";

  }

  var out=strBuffer.join("");

  return out;

  }

  /**

  * 解析指示头

  */

  jssp.Core.parse._handleDirective=function(directive){

  var i = 0;

  var tolkenIndex = 0;

  var tolken = new Array();

  //Skip first spaces;

  while ( directive.charAt(i) == ' ' ) {

  i++;

  }

  tolken[tolkenIndex] = "";

  while ( directive.charAt(i) != ' ' && i <= directive.length ) {

  tolken[tolkenIndex] += directive.charAt(i);

  i++;

  }

  tolkenIndex++;

  //Skip first spaces;

  while ( directive.charAt(i) == ' ' ) {

  i++;

  }

  tolken[tolkenIndex] = "";

  while ( directive.charAt(i) != ' ' && directive.charAt(i) != '=' && i <= directive.length ) {

  tolken[tolkenIndex] += directive.charAt(i);

  i++;

  }

  tolkenIndex++;

  //Skip first spaces;

  while ( directive.charAt(i) == ' ' ) {

  i++;

  }

  if( directive.charAt(i) != '=' )

  throw new jssp.Core.parse.ParseException("Sintax error", "Tolken = expected attribute");

  i++

  //Skip first spaces;

  while ( directive.charAt(i) == ' ' ) {

  i++;

  }

  tolken[tolkenIndex] = "";

  while ( directive.charAt(i) != ' ' && i <= directive.length ) {

  tolken[tolkenIndex] += directive.charAt(i);

  i++;

  }

  tolkenIndex++;

  //Skip first spaces;

  while ( directive.charAt(i) == ' ' &&  i <= directive.length ) {

  i++;

  }

  tolken[tolkenIndex] = "";

  while ( directive.charAt(i) != ' ' && directive.charAt(i) != '=' && i <= directive.length && i <= directive.length ) {

  tolken[tolkenIndex] += directive.charAt(i);

  i++;

  }

  tolkenIndex++;

  if( directive.charAt(i) != '='  && i <= directive.length  )

  throw  new jssp.Core.parse.ParseException("Sintax error", "Tolken = expected after attribute" );

  i++

  tolken[tolkenIndex] = "";

  while ( directive.charAt(i) != ' ' && i <= directive.length  && i <= directive.length ) {

  tolken[tolkenIndex] += directive.charAt(i);

  i++;

  }

  var file = "";

  var context = "";

  if ( tolken[0] != "include" )

  throw new jssp.Core.parse.ParseException("Sintax error","Directive " + tolken[0] + " unknown.") ;

  if ( tolken[1] != "file" )

  throw new jssp.Core.parse.ParseException("Sintax error", "Attribute file expected after include." );

  else file = tolken[2];

  if ( tolken[3] != "context" && tolken[3] != "" )

  throw new jssp.Core.parse.ParseException( "Sintax error", "Attribute context expected after file.");

  else if ( tolken[3] == "context" )

  context = tolken[4]

  else

  context = "$context";

  var out = "$context.include(" + file + ");\n";

  return out;

  }

  /**

  * 解析异常

  */

  jssp.Core.parse.ParseException=function(name,message) {

  this.name=name;

  this.message=message;

  }

  //@--------------------------------------------------------------------------------页面内容加载接口定义

  /**

  * 页面内容加载类接口定义

  */

  jssp.Core.PageLoader=function(){}

  /**

  * 读取页面文本

  */

  jssp.Core.PageLoader.prototype.loadPage=function(pagePath){throw "不能直接调用接口或您还未实现此方法!";}

  //@--------------------------------------------------------------------------------页面运行实现方法

  /**

  * @param pagePath 加载页面

  * @parma context 上下文对象

  */

  jssp.Core.run=function(pagePath,context){

  if(!jssp.pageLoaderInstance){

  //jssp引擎初始化

  if(jssp.Config.cachable) jssp.cacheInstance=eval("new "+jssp.Config.cacheClass+"();");

  jssp.pageLoaderInstance=eval("new "+jssp.Config.pageLoaderClass+"();");

  }

  var key=pagePath;if(key.indexOf("?")>0) key=key.substring(0,key.indexOf("?"));

  var processer=jssp.Config.cachable?jssp.cacheInstance.get(key):null;

  if(!processer){

  eval("processer="+jssp.Core.parse(jssp.pageLoaderInstance.loadPage(pagePath)));

  if(jssp.Config.cachable) jssp.cacheInstance.set(key,processer);

  }else{

  //alert("cache")

  }

  if(!context)

  context=new jssp.Context(pagePath);

  else

  context=new jssp.Context(pagePath,context);

  return processer(context);

  }

  //@-----------------------------------------------------------------------------------AJAX加载页面实现

  jssp.Core.PageLoader.Ajax=function(){}

  jssp.Core.PageLoader.Ajax.prototype.loadPage=function(pagePath){

  var content=jssp.Ajax.send(pagePath,"GET",false);

  if(!content) {

  alert("请求页面:"+pagePath+" 返回为null!");return null;

  }

  return content;

  }

  //@-----------------------------------------------------------------------------------AJAX操作实现

  jssp.Ajax=function(){}

  /**

  * 建立HTTP连接

  */

  jssp.Ajax.createHttpRequest=function(){

  if(window.XMLHttpRequest)

  return new XMLHttpRequest();

  var request=null;

  try{

  request=new ActiveXObject("Msxml2.XMLHTTP.4.0");

  }catch(e){

  try{

  request=new ActiveXObject("Msxml2.XMLHTTP");

  }catch(e){

  try{

  request=new ActiveXObject("microsoft.XMLHTTP");

  }catch(e){

  throw "XMLHTTPRequest组件客户端不支持!";

  }

  }

  }

  return request;

  }

  /**

  * 发送AJAX请求

  * @param url 请求页面

  * @param method 请求方法 get or post

  * @param async  是否为异步调用

  * @param callback 回调函数

  * @param preHook 调用前执行函数

  * @param postHook 调用后请求返回执行函数

  */

  jssp.Ajax.send=function(url,method,async,callback,preHook,postHook){

  method=method.toUpperCase();

  if(typeof preHook=="function") preHook();

  var request=jssp.Ajax.createHttpRequest();

  request.open(method,url,async);

  if(async){

  if(typeof callback!="function") throw "必须要设置回调函数";

  request.onreadystatechange=function(){

  jssp.Ajax.callback(request,callback,postHook);

  };

  }

  request.send(null);

  if(!async) {

  if(request.status==200||request.status==304)

  return jssp.Ajax._chartset(request);

  else

  return null;

  }

  }

  /**

  * 接受响应,调用自定义回调函数

  */

  jssp.Ajax.callback=function(response,callback,postHook){

  if(response.readyState!=4) return;

  var text;

  if(response.status==200||response.status==304){

  text=jssp.Ajax._chartset(response);

  }

  callback(text);

  if(typeof postHook=="function") postHook();

  }

  /**

  * 中文乱码处理

  */

  jssp.Ajax._chartset=function(r){

  var t=bytes2BSTR(r.responseBody);

  return t;

  }

  </script>

  <script language="javascript">

  jssp.Config.pageLoaderClass="jssp.Core.PageLoader.CustomerInput";//设置页面读取接口

  jssp.Config.cachable=false;

  jssp.Core.PageLoader.CustomerInput=function(){}

  jssp.Core.PageLoader.CustomerInput.prototype.loadPage=function(pagePath){

  if(pagePath.substring(0,10)!="hello.jssp") return "测试包含子页面,路径:"+pagePath;

  return document.getElementById("pageContent").value;

  }

  function showPage(){

  jssp.render("hello.jssp?name="+Math.random(),"pageArea");

  }

  </script>

  <style type="text/css">

  <!--

  .STYLE1 {color: #FFFFFF}

  -->

  </style>

  </head>

  <body>

  输入JSSP脚本内容:

  <textarea id="pageContent" style="width:100%;" rows="15">

  <table width="100%" border="0" align="center" cellpadding="4" cellspacing="2">

  <tr>

  <td align="center" valign="middle" bgcolor="#666699"><span class="STYLE1">order</span></td>

  <td align="center" valign="middle" bgcolor="#666699"><span class="STYLE1">number1</span></td>

  <td align="center" valign="middle" bgcolor="#666699"><span class="STYLE1">number2</span></td>

  <td align="center" valign="middle" bgcolor="#666699"><span class="STYLE1">number3</span></td>

  </tr>

  <%

  var beginTime=new Date();

  for(var i=0;i<100;i++){

  var color=i%2?"#eeeeee":"#aaaaaa";

  %>

  <tr bgcolor="<%=color%>">

  <td align="center" valign="middle" ><%=i%></td>

  <td align="center" valign="middle" ><%=Math.random()%></td>

  <td align="center" valign="middle" ><%=Math.random()%></td>

  <td align="center" valign="middle" ><%=Math.random()%></td>

  </tr>

  <%}%>

  </table>

  <%

  window.alert("耗时:"+(new Date()-beginTime)+"ms");

  %>

  </textarea>

  <button onClick="showPage()">显示内容</button>

  <hr>

  <div id="pageArea"></div>

  </hr>

  </body>

  </html>