仿服务器端脚本方式的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 相关文章推荐
接收键盘指令的脚本
Jun 26 Javascript
MooBox 基于Mootools的对话框插件
Jan 20 Javascript
纯javascript实现简单下拉刷新功能
Mar 13 Javascript
基于JS实现EOS隐藏错误提示层代码
Apr 25 Javascript
类似于QQ的右滑删除效果的实现方法
Oct 16 Javascript
AngularJS解决ng界面长表达式(ui-set)的方法分析
Nov 07 Javascript
微信小程序后台解密用户数据实例详解
Jun 28 Javascript
js数组常用最重要的方法
Feb 04 Javascript
JS实现的集合去重,交集,并集,差集功能示例
Mar 13 Javascript
微信小程序实现下拉菜单切换效果
Mar 30 Javascript
JS实现炫酷雪花飘落效果
Aug 19 Javascript
详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
Nov 27 Vue.js
改版了网上的一个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
PHP insert语法详解
2008/06/07 PHP
如何解决phpmyadmin导入数据库文件最大限制2048KB
2015/10/09 PHP
php 使用curl模拟登录人人(校内)网的简单实例
2016/06/06 PHP
Js基础学习资料
2010/11/23 Javascript
javascript中的void运算符语法及使用介绍
2013/03/10 Javascript
如何在JavaScript中实现私有属性的写类方式(二)
2013/12/04 Javascript
extjs4 treepanel动态改变行高度示例
2013/12/17 Javascript
jQuery 无限级菜单的简单实例
2014/02/21 Javascript
js调出上下文菜单的实例
2015/12/17 Javascript
JavaScript重载函数实例剖析
2016/05/13 Javascript
vue.js+boostrap项目实践(案例详解)
2016/09/21 Javascript
JavaScript你不知道的一些数组方法
2017/08/18 Javascript
vue中如何实现后台管理系统的权限控制的方法示例
2018/09/19 Javascript
vuex实现的简单购物车功能示例
2019/02/13 Javascript
mpvue性能优化实战技巧(小结)
2019/04/17 Javascript
JS实现字体背景跑马灯
2020/01/06 Javascript
[47:03]Ti4第二日主赛事败者组 LGD vs iG 2
2014/07/21 DOTA
Python中设置变量访问权限的方法
2015/04/27 Python
CentOS 7下Python 2.7升级至Python3.6.1的实战教程
2017/07/06 Python
Linux CentOS Python开发环境搭建教程
2018/11/28 Python
python双向链表原理与实现方法详解
2019/12/03 Python
解决python cv2.imread 读取中文路径的图片返回为None的问题
2020/06/02 Python
opencv+pyQt5实现图片阈值编辑器/寻色块阈值利器
2020/11/13 Python
Europcar英国:英国汽车和货车租赁
2017/01/21 全球购物
Invicta手表官方商店:百年制表历史的瑞士腕表品牌
2019/09/26 全球购物
建筑专业毕业生推荐信
2013/11/21 职场文书
婚礼证婚人证婚词
2014/01/08 职场文书
小学生获奖感言范文
2014/02/02 职场文书
党委班子对照检查材料
2014/08/19 职场文书
二人合伙经营协议书
2014/09/13 职场文书
工作年限证明范本
2015/06/15 职场文书
刚学完怎么用Python实现定时任务,转头就跑去撩妹!
2021/06/05 Python
安装配置mysql及Navicat prenium的详细流程
2021/06/10 MySQL
详解nginx进程锁的实现
2021/06/14 Servers
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis
Spring Boot实现文件上传下载
2022/08/14 Java/Android