TPM.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. //ThinkTemplate 用js实现了ThinkPHP的模板引擎。
  2. //用户可以在手机客户端中用ThinkPHP的模板引擎。
  3. //@author luofei614<http://weibo.com/luofei614>
  4. //
  5. var ThinkTemplate={
  6. tags:['Include','Volist','Foreach','For','Empty','Notempty','Present','Notpresent','Compare','If','Elseif','Else','Swith','Case','Default','Var','Range'],
  7. parse:function(tplContent,vars){
  8. var render=function(){
  9. tplContent='<% var key,mod=0;%>'+tplContent;//定义模板中循环需要使用的到变量
  10. $.each(ThinkTemplate.tags,function(k,v){
  11. tplContent=ThinkTemplate['parse'+v](tplContent);
  12. });
  13. return ThinkTemplate.template(tplContent,vars);
  14. };
  15. return render();
  16. },
  17. //解析 <% %> 标签
  18. template:function(text,vars){
  19. var source="";
  20. var index=0;
  21. var escapes = {
  22. "'": "'",
  23. '\\': '\\',
  24. '\r': 'r',
  25. '\n': 'n',
  26. '\t': 't',
  27. '\u2028': 'u2028',
  28. '\u2029': 'u2029'
  29. };
  30. var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
  31. text.replace(/<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g,function(match,interpolate,evaluate,offset){
  32. var p=text.slice(index,offset).replace(escaper,function(match){
  33. return '\\'+escapes[match];
  34. });
  35. if(''!=$.trim(p)){
  36. source+="__p+='"+p+"';\n";
  37. }
  38. if(evaluate){
  39. source+=evaluate+"\n";
  40. }
  41. if(interpolate){
  42. source+="if( 'undefined'!=typeof("+interpolate+") && (__t=(" + interpolate + "))!=null) __p+=__t;\n";
  43. }
  44. index=offset+match.length;
  45. return match;
  46. });
  47. source+="__p+='"+text.slice(index).replace(escaper,function(match){ return '\\'+escapes[match]; })+"';\n";//拼接剩余的字符串
  48. source = "var __t,__p='',__j=Array.prototype.join," +
  49. "print=function(){__p+=__j.call(arguments,'');};\n" +
  50. "with(obj){\n"+
  51. source +
  52. "}\n"+
  53. "return __p;\n";
  54. try {
  55. render = new Function('obj', source);
  56. } catch (e) {
  57. e.source = source;
  58. throw e;
  59. }
  60. return render(vars);
  61. },
  62. parseVar:function(tplContent){
  63. var matcher=/\{\$(.*?)\}/g
  64. return tplContent.replace(matcher,function(match,varname,offset){
  65. //支持定义默认值
  66. if(varname.indexOf('|')!=-1){
  67. var arr=varname.split('|');
  68. var name=arr[0];
  69. var defaultvalue='""';
  70. arr[1].replace(/default=(.*?)$/ig,function(m,v,o){
  71. defaultvalue=v;
  72. });
  73. return '<% '+name+'?print('+name+'):print('+defaultvalue+'); %>';
  74. }
  75. return '<%='+varname+'%>';
  76. });
  77. },
  78. //include标签解析 路径需要写全,写为 Action:method, 暂不支持变量。
  79. parseInclude:function(tplContent){
  80. var include=/<include (.*?)\/?>/ig;
  81. tplContent=tplContent.replace(include,function(m,v,o){
  82. var $think=$('<think '+v+' />');
  83. var file=$think.attr('file').replace(':','/')+'.html';
  84. var content='';
  85. //加载模板
  86. $.ajax({
  87. dataType:'text',
  88. url:file,
  89. cache:false,
  90. async:false,//同步请求
  91. success:function(d,s,x){
  92. content=d;
  93. },
  94. error:function(){
  95. //pass
  96. }
  97. });
  98. return content;
  99. });
  100. tplContent=tplContent.replace('</include>','');//兼容浏览器中元素自动闭合的情况
  101. return tplContent;
  102. },
  103. //volist标签解析
  104. parseVolist:function(tplContent){
  105. var voliststart=/<volist (.*?)>/ig;
  106. var volistend=/<\/volist>/ig;
  107. //解析volist开始标签
  108. tplContent=tplContent.replace(voliststart,function(m,v,o){
  109. //属性分析
  110. var $think=$('<think '+v+' />');
  111. var name=$think.attr('name');
  112. var id=$think.attr('id');
  113. var empty=$think.attr('empty')||'';
  114. var key=$think.attr('key')||'i';
  115. var mod=$think.attr('mod')||'2';
  116. //替换为代码
  117. return '<% if("undefined"==typeof('+name+') || ThinkTemplate.empty('+name+')){'+
  118. ' print(\''+empty+'\');'+
  119. ' }else{ '+
  120. key+'=0;'+
  121. ' $.each('+name+',function(key,'+id+'){'+
  122. ' mod='+key+'%'+mod+';'+
  123. ' ++'+key+';'+
  124. ' %>';
  125. });
  126. //解析volist结束标签
  127. tplContent=tplContent.replace(volistend,'<% }); } %>');
  128. return tplContent;
  129. },
  130. //解析foreach标签
  131. parseForeach:function(tplContent){
  132. var foreachstart=/<foreach (.*?)>/ig;
  133. var foreachend=/<\/foreach>/i;
  134. tplContent=tplContent.replace(foreachstart,function(m,v,o){
  135. var $think=$('<think '+v+' />');
  136. var name=$think.attr('name');
  137. var item=$think.attr('item');
  138. var key=$think.attr('key')||'key';
  139. return '<% $.each('+name+',function('+key+','+item+'){ %>'
  140. });
  141. tplContent=tplContent.replace(foreachend,'<% }); %>');
  142. return tplContent;
  143. },
  144. parseFor:function(tplContent){
  145. var forstart=/<for (.*?)>/ig;
  146. var forend=/<\/for>/ig;
  147. tplContent=tplContent.replace(forstart,function(m,v,o){
  148. var $think=$('<think '+v+' />');
  149. var name=$think.attr('name') || 'i';
  150. var comparison=$think.attr('comparison') || 'lt';
  151. var start=$think.attr('start') || '0';
  152. if('$'==start.substr(0,1)){
  153. start=start.substr(1);
  154. }
  155. var end=$think.attr('end') || '0';
  156. if('$'==end.substr(0,1)){
  157. end=end.substr(1);
  158. }
  159. var step=$think.attr('step') || '1';
  160. if('$'==step.substr(0,1)){
  161. step=step.substr(1);
  162. }
  163. return '<% for(var '+name+'='+start+';'+ThinkTemplate.parseCondition(name+comparison+end)+';i=i+'+step+'){ %>'
  164. });
  165. tplContent=tplContent.replace(forend,'<% } %>');
  166. return tplContent;
  167. },
  168. //empty标签
  169. parseEmpty:function(tplContent){
  170. var emptystart=/<empty (.*?)>/ig;
  171. var emptyend=/<\/empty>/ig;
  172. tplContent=tplContent.replace(emptystart,function(m,v,o){
  173. var name=$('<think '+v+' />').attr('name');
  174. return '<% if("undefined"==typeof('+name+') || ThinkTemplate.empty('+name+')){ %>';
  175. });
  176. tplContent=tplContent.replace(emptyend,'<% } %>');
  177. return tplContent;
  178. },
  179. //notempty 标签解析
  180. parseNotempty:function(tplContent){
  181. var notemptystart=/<notempty (.*?)>/ig;
  182. var notemptyend=/<\/notempty>/ig;
  183. tplContent=tplContent.replace(notemptystart,function(m,v,o){
  184. var name=$('<think '+v+' />').attr('name');
  185. return '<% if("undefined"!=typeof('+name+') && !ThinkTemplate.empty('+name+')){ %>';
  186. });
  187. tplContent=tplContent.replace(notemptyend,'<% } %>');
  188. return tplContent;
  189. },
  190. //present标签解析
  191. parsePresent:function(tplContent){
  192. var presentstart=/<present (.*?)>/ig;
  193. var presentend=/<\/present>/ig;
  194. tplContent=tplContent.replace(presentstart,function(m,v,o){
  195. var name=$('<think '+v+' />').attr('name');
  196. return '<% if("undefined"!=typeof('+name+')){ %>';
  197. });
  198. tplContent=tplContent.replace(presentend,'<% } %>');
  199. return tplContent;
  200. },
  201. //notpresent 标签解析
  202. parseNotpresent:function(tplContent){
  203. var notpresentstart=/<notpresent (.*?)>/ig;
  204. var notpresentend=/<\/notpresent>/ig;
  205. tplContent=tplContent.replace(notpresentstart,function(m,v,o){
  206. var name=$('<think '+v+' />').attr('name');
  207. return '<% if("undefined"==typeof('+name+')){ %>';
  208. });
  209. tplContent=tplContent.replace(notpresentend,'<% } %>');
  210. return tplContent;
  211. },
  212. parseCompare:function(tplContent){
  213. var compares={
  214. "compare":"==",
  215. "eq":"==",
  216. "neq":"!=",
  217. "heq":"===",
  218. "nheq":"!==",
  219. "egt":">=",
  220. "gt":">",
  221. "elt":"<=",
  222. "lt":"<"
  223. };
  224. $.each(compares,function(type,sign){
  225. var start=new RegExp('<'+type+' (.*?)>','ig');
  226. var end=new RegExp('</'+type+'>','ig');
  227. tplContent=tplContent.replace(start,function(m,v,o){
  228. var $think=$('<think '+v+' />');
  229. var name=$think.attr('name');
  230. var value=$think.attr('value');
  231. if("compare"==type && $think.attr('type')){
  232. sign=compares[$think.attr('type')];
  233. }
  234. if('$'==value.substr(0,1)){
  235. //value支持变量
  236. value=value.substr(1);
  237. }else{
  238. value='"'+value+'"';
  239. }
  240. return '<% if('+name+sign+value+'){ %>';
  241. });
  242. tplContent=tplContent.replace(end,'<% } %>');
  243. });
  244. return tplContent;
  245. },
  246. //解析if标签
  247. parseIf:function(tplContent){
  248. var ifstart=/<if (.*?)>/ig;
  249. var ifend=/<\/if>/ig;
  250. tplContent=tplContent.replace(ifstart,function(m,v,o){
  251. var condition=$('<think '+v+' />').attr('condition');
  252. return '<% if('+ThinkTemplate.parseCondition(condition)+'){ %>';
  253. });
  254. tplContent=tplContent.replace(ifend,'<% } %>');
  255. return tplContent;
  256. },
  257. //解析elseif
  258. parseElseif:function(tplContent){
  259. var elseif=/<elseif (.*?)\/?>/ig;
  260. tplContent=tplContent.replace(elseif,function(m,v,o){
  261. var condition=$('<think '+v+' />').attr('condition');
  262. return '<% }else if('+ThinkTemplate.parseCondition(condition)+'){ %>';
  263. });
  264. tplContent=tplContent.replace('</elseif>','');
  265. return tplContent;
  266. },
  267. //解析else标签
  268. parseElse:function(tplContent){
  269. var el=/<else\s*\/?>/ig
  270. tplContent=tplContent.replace(el,'<% }else{ %>');
  271. tplContent=tplContent.replace('</else>','');
  272. return tplContent;
  273. },
  274. //解析swith标签
  275. parseSwith:function(tplContent){
  276. var switchstart=/<switch (.*?)>(\s*)/ig;
  277. var switchend=/<\/switch>/ig;
  278. tplContent=tplContent.replace(switchstart,function(m,v,s,o){
  279. var name=$('<think '+v+' >').attr('name');
  280. return '<% switch('+name+'){ %>';
  281. });
  282. tplContent=tplContent.replace(switchend,'<% } %>');
  283. return tplContent;
  284. },
  285. //解析case标签
  286. parseCase:function(tplContent){
  287. var casestart=/<case (.*?)>/ig;
  288. var caseend=/<\/case>/ig;
  289. var breakstr='';
  290. tplContent=tplContent.replace(casestart,function(m,v,o){
  291. var $think=$('<think '+v+' />');
  292. var value=$think.attr('value');
  293. if('$'==value.substr(0,1)){
  294. value=value.substr(1);
  295. }else{
  296. value='"'+value+'"';
  297. }
  298. if('false'!=$think.attr('break')){
  299. breakstr='<% break; %> ';
  300. }
  301. return '<% case '+value+': %>';
  302. });
  303. tplContent=tplContent.replace(caseend,breakstr);
  304. return tplContent;
  305. },
  306. //解析default标签
  307. parseDefault:function(tplContent){
  308. var defaulttag=/<default\s*\/?>/ig;
  309. tplContent=tplContent.replace(defaulttag,'<% default: %>');
  310. tplContent=tplContent.replace('</default>','');
  311. return tplContent;
  312. },
  313. //解析in,notin,between,notbetween 标签
  314. parseRange:function(tplContent){
  315. var ranges=['in','notin','between','notbetween'];
  316. $.each(ranges,function(k,tag){
  317. var start=new RegExp('<'+tag+' (.*?)>','ig');
  318. var end=new RegExp('</'+tag+'>','ig');
  319. tplContent=tplContent.replace(start,function(m,v,o){
  320. var $think=$('<think '+v+' />');
  321. var name=$think.attr('name');
  322. var value=$think.attr('value');
  323. if('$'==value.substr(0,1)){
  324. value=value.substr(1);
  325. }else{
  326. value='"'+value+'"';
  327. }
  328. switch(tag){
  329. case "in":
  330. var condition='ThinkTemplate.inArray('+name+','+value+')';
  331. break;
  332. case "notin":
  333. var condition='!ThinkTemplate.inArray('+name+','+value+')';
  334. break;
  335. case "between":
  336. var condition=name+'>='+value+'[0] && '+name+'<='+value+'[1]';
  337. break;
  338. case "notbetween":
  339. var condition=name+'<'+value+'[0] || '+name+'>'+value+'[1]';
  340. break;
  341. }
  342. return '<% if('+condition+'){ %>'
  343. });
  344. tplContent=tplContent.replace(end,'<% } %>')
  345. });
  346. return tplContent;
  347. },
  348. //扩展
  349. extend:function(name,cb){
  350. name=name.substr(0,1).toUpperCase()+name.substr(1);
  351. this.tags.push(name);
  352. this['parse'+name]=cb;
  353. },
  354. //判断是否在数组中,支持判断object类型的数据
  355. inArray:function(name,value){
  356. if('string'==$.type(value)){
  357. value=value.split(',');
  358. }
  359. var ret=false;
  360. $.each(value,function(k,v){
  361. if(v==name){
  362. ret=true;
  363. return false;
  364. }
  365. });
  366. return ret;
  367. },
  368. empty:function(data){
  369. if(!data)
  370. return true;
  371. if('array'==$.type(data) && 0==data.length)
  372. return true;
  373. if('object'==$.type(data) && 0==Object.keys(data).length)
  374. return true;
  375. return false;
  376. },
  377. parseCondition:function(condition){
  378. var conditions={
  379. "eq":"==",
  380. "neq":"!=",
  381. "heq":"===",
  382. "nheq":"!==",
  383. "egt":">=",
  384. "gt":">",
  385. "elt":"<=",
  386. "lt":"<",
  387. "or":"||",
  388. "and":"&&",
  389. "\\$":""
  390. };
  391. $.each(conditions,function(k,v){
  392. var matcher=new RegExp(k,'ig');
  393. condition=condition.replace(matcher,v);
  394. });
  395. return condition;
  396. }
  397. };
  398. //TPMobi框架
  399. //实现用ThinkPHP做手机客户端
  400. //@author luofei614<http://weibo.com/luofei614>
  401. var TPM={
  402. op:{
  403. api_base:'',//接口基地址,末尾不带斜杠
  404. api_index:'/Index/index',//首页请求地址
  405. main:"main",//主体层的ID
  406. routes:{}, //路由,支持参数如:id 支持通配符*
  407. error_handle:false,//错误接管函数
  408. _before:[],
  409. _ready:[],//UI回调函数集合
  410. single:true,//单一入口模式
  411. ajax_wait:".ajax_wait",//正在加载层的选择符
  412. ajax_timeout:15000,//ajax请求超时时间
  413. ajax_data_type:'',//请求接口类型 如json,jsonp
  414. ajax_jsonp_callback:'callback',//jsonp 传递的回调函数参数名词
  415. before_request_api:false,//请求接口之前的hook
  416. //请求接口之后的hook,处理TP的success和error
  417. after_request_api:function(data,url){
  418. if(data.info){
  419. TPM.info(data.info,function(){
  420. if(data.url){
  421. TPM.http(data.url);
  422. }else if(1==data.status){
  423. //如果success, 刷新数据
  424. TPM.reload(TPM.op.main);
  425. }
  426. });
  427. return false;
  428. }
  429. },
  430. anchor_move_speed:500, //移动到锚点的速度
  431. tpl_path_var:'_think_template_path',//接口指定模板
  432. tpl_parse_string:{
  433. '../Public':'./Public'
  434. },//模板替换变量
  435. //指定接口请求的header
  436. headers:{
  437. 'client':'PhoneClient',
  438. //跨域请求时,不会带X-Requested-with 的header,会导致服务认为不是ajax请求,所以这样手动加上这个header 。
  439. 'X-Requested-With':'XMLHttpRequest'
  440. },
  441. tpl:ThinkTemplate.parse//模板引擎
  442. },
  443. config:function(options){
  444. $.extend(this.op,options);
  445. },
  446. ready:function(fun){
  447. this.op._ready.push(fun);
  448. },
  449. before:function(fun){
  450. this.op._before.push(fun);
  451. },
  452. //输出错误
  453. error:function(errno,msg){
  454. TPM.alert('错误['+errno+']:'+msg);
  455. },
  456. info:function(msg,cb){
  457. if('undefined'==typeof(tpm_info)){
  458. alert(msg);
  459. if($.isFunction(cb)) cb();
  460. }else{
  461. tpm_info(msg,cb);
  462. }
  463. },
  464. alert:function(msg,cb,title){
  465. if('undefined'==typeof(tpm_alert)){
  466. alert(msg);
  467. if($.isFunction(cb)) cb();
  468. }else{
  469. tpm_alert(msg,cb,title);
  470. }
  471. },
  472. //初始化运行
  473. run:function(options,vars){
  474. if(!this.defined(window.jQuery) && !this.defined(window.Zepto)){
  475. this.error('-1','请加载jquery或zepto');
  476. return ;
  477. }
  478. //如果只设置api_base 可以只传递一个字符串。
  479. if('string'==$.type(options)){
  480. options={api_base:options};
  481. }
  482. //配置处理
  483. options=options||{};
  484. this.config(options);
  485. $.ajaxSetup({
  486. error:this.ajaxError,
  487. timeout:this.op.ajax_timeout || 5000,
  488. cache:false,
  489. headers:this.op.headers
  490. });
  491. var _self=this;
  492. //ajax加载状态
  493. window.TPMshowAjaxWait=true;
  494. $(document).ajaxStart(function(){
  495. //在程序中可以设置TPMshowAjaxWait为false,终止显示等待层。
  496. if(window.TPMshowAjaxWait) $(_self.op.ajax_wait).show();
  497. }
  498. ).ajaxStop(function(){
  499. $(_self.op.ajax_wait).hide();
  500. });
  501. $(document).ready(function(){
  502. //标签解析
  503. vars=vars||{};
  504. var render=function(vars){
  505. var tplcontent=$('body').html();
  506. tplcontent=tplcontent.replace(/&lt;%/g,'<%');
  507. tplcontent=tplcontent.replace(/%&gt;/g,'%>');
  508. var html=_self.parseTpl(tplcontent,vars);
  509. $('body').html(html);
  510. if(!_self.op.single){
  511. $.each(_self.op._ready,function(k,fun){
  512. fun($);
  513. });
  514. }
  515. }
  516. if('string'==$.type(vars)){
  517. _self.sendAjax(vars,{},'get',function(response){
  518. render(response);
  519. });
  520. }else{
  521. render(vars);
  522. }
  523. if(_self.op.single){
  524. //单一入口模式
  525. _self.initUI(document);
  526. var api_url=''!=location.hash?location.hash.substr(1):_self.op.api_index;
  527. _self.op._old_hash=location.hash;
  528. _self.http(api_url);
  529. //监听hash变化
  530. var listenHashChange=function(){
  531. if(location.hash!=_self.op._old_hash){
  532. var api_url=''!=location.hash?location.hash.substr(1):_self.op.api_index;
  533. _self.http(api_url);
  534. }
  535. setTimeout(listenHashChange,50);
  536. }
  537. listenHashChange();
  538. }
  539. });
  540. },
  541. //初始化界面
  542. initUI:function(_box){
  543. //调用自定义加载完成后的UI处理函数,自定义事件绑定先于系统绑定,可以控制系统绑定函数的触发。
  544. var selector=function(obj){
  545. var $obj=$(obj,_box)
  546. return $obj.size()>0?$obj:$(obj);
  547. };
  548. $.each(this.op._before,function(k,fun){
  549. fun(selector);
  550. })
  551. var _self=this;
  552. //A标签, 以斜杠开始的地址才会监听,不然会直接打开
  553. $('a[href^="/"],a[href^="./"]',_box).click(function(e){
  554. if(false===e.result) return ; //如果自定义事件return false了, 不再指向请求操作
  555. e.preventDefault();
  556. //如果有tpl属性,则光请求模板
  557. var url=$(this).attr('href');
  558. if(undefined!==$(this).attr('tpl')){
  559. url='.'+url+'.html';
  560. }
  561. //绝对地址的链接不过滤
  562. _self.http(url,$(this).attr('rel'));
  563. });
  564. //form标签的处理
  565. $('form[action^="/"],form[action^="./"]',_box).submit(function(e){
  566. if(false===e.result) return ; //如果自定义事件return false了, 不再指向请求操作
  567. e.preventDefault();
  568. var url=$(this).attr('action');
  569. if(undefined!==$(this).attr('tpl')){
  570. url='.'+url+'.html';
  571. }
  572. _self.http(url,$(this).attr('rel'),$(this).serializeArray(),$(this).attr('method'));
  573. });
  574. //锚点处理
  575. $('a[href^="#"]',_box).click(function(e){
  576. e.preventDefault();
  577. var anchor=$(this).attr('href').substr(1);
  578. if($('#'+anchor).size()>0){
  579. _self.scrollTop($('#'+anchor),_self.op.anchor_move_speed);
  580. }else if($('a[name="'+anchor+'"]').size()>0){
  581. _self.scrollTop($('a[name="'+anchor+'"]'),_self.op.anchor_move_speed);
  582. }else{
  583. _self.scrollTop(0,_self.op.anchor_move_speed);
  584. }
  585. });
  586. $.each(this.op._ready,function(k,fun){
  587. fun(selector);
  588. })
  589. },
  590. //请求接口, 支持情况:1, 请求接口同时渲染模板 2,只请求模板不请求接口 3,只请求接口不渲染模板, 如果有更复杂的逻辑可以自己封住函数,调TPM.sendAjax, TPM.render。
  591. http:function(url,rel,data,type){
  592. rel=rel||this.op.main;
  593. type=type || 'get';
  594. //分析url,如果./开始直接请求模板
  595. if('./'==url.substr(0,2)){
  596. this.render(url,rel);
  597. $('#'+rel).data('url',url);
  598. if(this.op.main==rel && 'get'==type.toLowerCase()) this.changeHash(url);
  599. return ;
  600. }
  601. //分析模板地址
  602. var tpl_path=this.route(url);
  603. //改变hash
  604. if(tpl_path && this.op.main==rel && 'get'==type.toLowerCase()) this.changeHash(url);
  605. //ajax请求
  606. var _self=this;
  607. this.sendAjax(url,data,type,function(response){
  608. if(!tpl_path && _self.defined(response[_self.op.tpl_path_var])){
  609. tpl_path=response[_self.op.tpl_path_var]; //接口可以指定模板
  610. //改变hash
  611. if(tpl_path && _self.op.main==rel && 'get'==type.toLowerCase()) _self.changeHash(url);
  612. }
  613. if(!tpl_path){
  614. //如果没有模板,默认只请求ajax,请求成后刷新rel
  615. if('false'!=rel.toLowerCase()) _self.reload(rel);
  616. }else{
  617. //模板渲染
  618. _self.render(tpl_path,rel,response);
  619. $('#'+rel).data('url',url);
  620. }
  621. });
  622. },
  623. sendAjax:function(url,data,type,cb,async,options){
  624. var _self=this;
  625. data=data||{};
  626. type=type||'get';
  627. options=options||{};
  628. api_options=$.extend({},_self.op,options);
  629. if(false!==async){
  630. async==true;
  631. }
  632. //请求接口之前hook(可以用做签名)
  633. if($.isFunction(api_options.before_request_api))
  634. data=api_options.before_request_api(data,url);
  635. //ajax请求
  636. //TODO ,以http开头的url,不加api_base
  637. var api_url=api_options.api_base+url;
  638. $.ajax(
  639. {
  640. type: type,
  641. url: api_url,
  642. data: data,
  643. dataType:api_options.ajax_data_type||'',
  644. jsonp:api_options.ajax_jsonp_callback|| 'callback',
  645. async:async,
  646. success: function(d,s,x){
  647. if(redirect=x.getResponseHeader('redirect')){
  648. //跳转
  649. if(api_options.single) _self.http(redirect);
  650. return ;
  651. }
  652. //接口数据分析
  653. try{
  654. var response='object'==$.type(d)?d:$.parseJSON(d);
  655. }catch(e){
  656. _self.error('-2','接口返回数据格式错误');
  657. return ;
  658. }
  659. //接口请求后的hook
  660. if($.isFunction(api_options.after_request_api)){
  661. var hook_result=api_options.after_request_api(response,url);
  662. if(undefined!=hook_result){
  663. response=hook_result;
  664. }
  665. }
  666. if(false!=response && $.isFunction(cb))
  667. cb(response);
  668. }
  669. }
  670. );
  671. },
  672. changeHash:function(url){
  673. if(url!=this.op.api_index){
  674. this.op._old_hash='#'+url;
  675. location.hash=url;
  676. }else{
  677. if(''!=this.op._old_hash) this.op._old_hash=this.isIE()?'#':'';//IE如果描点为# 获得值不为空
  678. if(''!=location.hash) location.hash='';//赋值为空其实浏览器会赋值为 #
  679. }
  680. },
  681. //渲染模板
  682. render:function(tpl_path,rel,vars){
  683. vars=vars||{};
  684. var _self=this;
  685. $.get(tpl_path,function(d,x,s){
  686. //模板解析
  687. var content=_self.parseTpl(d,vars);
  688. //解析模板替换变量
  689. $.each(_self.op.tpl_parse_string,function(find,replace){
  690. var matcher=new RegExp(find.replace(/[-[\]{}()+?.,\\^$|#\s]/g,'\\$&'),'g');
  691. content=content.replace(matcher,replace);
  692. });
  693. //分离js
  694. var ret=_self.stripScripts(content);
  695. var html=ret.text;
  696. var js=ret.scripts;
  697. $('#'+rel).empty().append(html);
  698. _self.initUI($('#'+rel));
  699. //执行页面js
  700. _self.execScript(js,$('#'+rel));
  701. },'text');
  702. },
  703. //重新加载区域内容
  704. reload:function(rel){
  705. var url=$('#'+rel).data('url');
  706. if(url){
  707. this.http(url,rel);
  708. }
  709. },
  710. //路由解析
  711. route:function(url){
  712. var tpl_path=false;
  713. var _self=this;
  714. $.each(this.op.routes,function(route,path){
  715. if(_self._routeToRegExp(route).test(url)){
  716. tpl_path=path;
  717. return false;
  718. }
  719. });
  720. return tpl_path;
  721. },
  722. _routeToRegExp: function(route) {
  723. var namedParam = /:\w+/g;
  724. var splatParam = /\*\w+/g;
  725. var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
  726. route = route.replace(escapeRegExp, '\\$&')
  727. .replace(namedParam, '([^\/]+)')
  728. .replace(splatParam, '(.*?)');
  729. return new RegExp('^' + route + '$');
  730. },
  731. //模板解析
  732. parseTpl:function(tplContent,vars){
  733. return this.op.tpl(tplContent,vars);
  734. },
  735. ajaxError: function(xhr, ajaxOptions, thrownError)
  736. {
  737. window.TPMshowAjaxWait=true;
  738. TPM.info('网络异常');
  739. },
  740. //------实用工具
  741. //判断是否为IE
  742. isIE:function(){
  743. return /msie [\w.]+/.exec(navigator.userAgent.toLowerCase());
  744. },
  745. //判断是否为IE7以下浏览器
  746. isOldIE:function(){
  747. return this.isIE() && (!docMode || docMode <= 7);
  748. },
  749. //移动滚动条,n可以是数字也可以是对象
  750. scrollTop:function(n,t,obj){
  751. t=t||0;
  752. obj=obj ||'html,body'
  753. num=$.type(n)!="number"?n.offset().top:n;
  754. $(obj).animate( {
  755. scrollTop: num
  756. }, t );
  757. },
  758. //分离js代码
  759. stripScripts:function(codes){
  760. var scripts = '';
  761. //将字符串去除script标签, 并获得script标签中的内容。
  762. var text = codes.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
  763. scripts += code + '\n';
  764. return '';
  765. });
  766. return {text:text,scripts:scripts}
  767. },
  768. //执行js代码
  769. execScript:function(scripts,_box){
  770. if(scripts!=''){
  771. //执行js代码, 在闭包中执行。改变$选择符。
  772. var e=new Function('$',scripts);
  773. var selector=function(obj){
  774. var $obj=$(obj,_box)
  775. return $obj.size()>0?$obj:$(obj);
  776. };
  777. e(selector);
  778. }
  779. },
  780. //判断变量是否定义
  781. defined:function(variable){
  782. return $.type(variable) == "undefined" ? false : true;
  783. },
  784. //获得get参数
  785. get:function(name){
  786. if('undefined'==$.type(this._gets)){
  787. var querystring=window.location.search.substring(1);
  788. var gets={};
  789. var vars=querystring.split('&')
  790. var param;
  791. for(var i=0;i<vars.length;i++){
  792. param=vars[i].split('=');
  793. gets[param[0]]=param[1];
  794. }
  795. this._gets=gets;
  796. }
  797. return this._gets[name];
  798. }
  799. };