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


Posted in Javascript onApril 27, 2007

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> 

Javascript 相关文章推荐
用jquery实现下拉菜单效果的代码
Jul 25 Javascript
说说JSON和JSONP 也许你会豁然开朗
Sep 02 Javascript
读取input:file的路径并显示本地图片的方法
Sep 23 Javascript
jQuery删除节点的三个方法即remove()detach()和empty()
Dec 27 Javascript
Javascript和Java获取各种form表单信息的简单实例
Feb 14 Javascript
jquery scroll()区分横向纵向滚动条的方法
Apr 04 Javascript
即将发布的jQuery 3 有哪些新特性
Apr 14 Javascript
实例详解JavaScript中setTimeout函数的执行顺序
Jul 12 Javascript
Vue Socket.io源码解读
Feb 07 Javascript
vue配置font-awesome5的方法步骤
Jan 27 Javascript
react项目如何使用iconfont的方法步骤
Mar 13 Javascript
vue之封装多个组件调用同一接口的案例
Aug 11 Javascript
改版了网上的一个js操作userdata
Apr 27 #Javascript
用 JSON 处理缓存
Apr 27 #Javascript
转一个日期输入控件,支持FF
Apr 27 #Javascript
学习jquery之一
Apr 27 #Javascript
JavaScript与函数式编程解释
Apr 27 #Javascript
漂亮的widgets,支持换肤和后期开发新皮肤(2007-4-27已更新1.7alpha)
Apr 27 #Javascript
从sohu弄下来的flash中展示图片的代码
Apr 27 #Javascript
You might like
德生BCL3000的电路分析和打磨
2021/03/02 无线电
德生PL990,目前市面上唯一一款便携式插卡蓝牙全波段高性能收音机
2021/03/02 无线电
PHP 获取远程网页内容的代码(fopen,curl已测)
2011/06/06 PHP
PHP中模拟链表和链表的基本操作示例
2016/02/27 PHP
PHP使用new StdClass()创建空对象的方法分析
2017/06/06 PHP
js复制到剪切板的实例方法
2013/06/28 Javascript
javascript自然分类法算法实现代码
2013/10/11 Javascript
json属性名为什么要双引号(个人猜测)
2014/07/31 Javascript
基于jquery实现一个滚动的分步注册向导-附源码
2015/08/26 Javascript
Javascript实现商品秒杀倒计时(时间与服务器时间同步)
2015/09/16 Javascript
jQuery实现右下角可缩放大小的层完整实例
2016/06/20 Javascript
Javascript 基础---Ajax入门必看
2016/07/06 Javascript
JavaScript中日期函数的相关操作知识
2016/08/03 Javascript
JS简单判断函数是否存在的方法
2017/02/13 Javascript
Vue之Watcher源码解析(2)
2017/07/19 Javascript
react中使用swiper的具体方法
2018/05/15 Javascript
小程序显示弹窗时禁止下层的内容滚动实现方法
2019/03/20 Javascript
使用vue重构资讯页面的实例代码解析
2019/11/26 Javascript
小谈angular ng deploy的实现
2020/04/07 Javascript
es6函数name属性功能与用法实例分析
2020/04/18 Javascript
JavaScript实现放大镜效果代码示例
2020/04/29 Javascript
Python深入学习之对象的属性
2014/08/31 Python
举例讲解Python的Tornado框架实现数据可视化的教程
2015/05/02 Python
玩转python爬虫之cookie使用方法
2016/02/17 Python
python Django批量导入不重复数据
2016/03/25 Python
详解python OpenCV学习笔记之直方图均衡化
2018/02/08 Python
Python判断变量名是否合法的方法示例
2019/01/28 Python
python数据库编程 ODBC方式实现通讯录
2020/03/27 Python
利用html5 file api读取本地文件示例(如图片、PDF等)
2018/03/07 HTML / CSS
女装和独特珠宝:Sundance Catalog
2018/09/19 全球购物
亚马逊新加坡官方网站:Amazon.sg
2020/03/25 全球购物
法国购买二手电子产品网站:Asgoodasnew
2020/03/27 全球购物
解释一下钝化(Swap out)
2016/12/26 面试题
校园摄影活动策划方案
2014/02/05 职场文书
mybatis3中@SelectProvider传递参数方式
2021/08/04 Java/Android
python解析json数据
2022/04/29 Python