From b004546332884984208c1273c7657792be2abaf6 Mon Sep 17 00:00:00 2001 From: makoto kuwata Date: Sun, 1 May 2011 06:12:29 +0900 Subject: [PATCH] add 'website' directory and files --- website/app.yaml | 16 + website/config.py | 8 + website/css/images/img01.jpg | Bin 0 -> 1763 bytes website/css/images/img02.jpg | Bin 0 -> 329 bytes website/css/images/img03.jpg | Bin 0 -> 372 bytes website/css/images/img04.jpg | Bin 0 -> 3478 bytes website/css/license.txt | 63 ++ website/css/style.css | 388 +++++++++ website/htdocs/about.html.pyt | 6 + website/htdocs/blog.html.pyt | 35 + website/htdocs/contact.html.pyt | 6 + website/htdocs/index.html.pyt | 4 + website/htdocs/links.html.pyt | 6 + website/htdocs/photos.html.pyt | 6 + website/img/favicon.ico | Bin 0 -> 1406 bytes website/index.py | 38 + website/js/jquery-1.4.2.min.js | 154 ++++ website/lib/apptenjin.py | 171 ++++ website/lib/my_template.py | 278 +++++++ website/lib/tenjin.py | 1374 +++++++++++++++++++++++++++++++ website/myapp.py | 66 ++ website/tmpl/404.html.pyt | 14 + website/tmpl/_layout.html.pyt | 94 +++ website/tmpl/_sidebar.html.pyt | 57 ++ 24 files changed, 2784 insertions(+) create mode 100644 website/app.yaml create mode 100644 website/config.py create mode 100644 website/css/images/img01.jpg create mode 100644 website/css/images/img02.jpg create mode 100644 website/css/images/img03.jpg create mode 100644 website/css/images/img04.jpg create mode 100644 website/css/license.txt create mode 100644 website/css/style.css create mode 100644 website/htdocs/about.html.pyt create mode 100644 website/htdocs/blog.html.pyt create mode 100644 website/htdocs/contact.html.pyt create mode 100644 website/htdocs/index.html.pyt create mode 100644 website/htdocs/links.html.pyt create mode 100644 website/htdocs/photos.html.pyt create mode 100644 website/img/favicon.ico create mode 100644 website/index.py create mode 100644 website/js/jquery-1.4.2.min.js create mode 100644 website/lib/apptenjin.py create mode 100644 website/lib/my_template.py create mode 100644 website/lib/tenjin.py create mode 100644 website/myapp.py create mode 100644 website/tmpl/404.html.pyt create mode 100644 website/tmpl/_layout.html.pyt create mode 100644 website/tmpl/_sidebar.html.pyt diff --git a/website/app.yaml b/website/app.yaml new file mode 100644 index 0000000..0878e0a --- /dev/null +++ b/website/app.yaml @@ -0,0 +1,16 @@ +application: versionswitcher +version: 1 +runtime: python +api_version: 1 + +handlers: + - url: /css + static_dir: css + - url: /js + static_dir: js + - url: /img + static_dir: img + - url: /download + static_dir: download + - url: /.* + script: index.py diff --git a/website/config.py b/website/config.py new file mode 100644 index 0000000..53f3c57 --- /dev/null +++ b/website/config.py @@ -0,0 +1,8 @@ +## +## you can edit this file to change application configuration. +## + +encoding = 'utf-8' +layout_template = '_layout.html.pyt' +analytics_id = 'UA-3184311-6' +template_path = ['htdocs', 'tmpl'] diff --git a/website/css/images/img01.jpg b/website/css/images/img01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f44794beff065b421301a6e2104c3e18ca3855d GIT binary patch literal 1763 zcmex=R-tZb|xz{bJG4g_o*9GqNST$~&{V890g0&u{{ z%)-LP#>T_J!6U@S!zUyHk`WOE%L_s%0VoC6gTnZKfI*Oh(U~EMnNg5|Nsy6Qkn#T! zhBBbDnSluCP8eWfW?^Jw=U@Z_WEn;#W;RxK4gp36CMHHER#un>21da^Az{VD!iCH% zQ?9UvvP%duGBPkQbHWucFbIBNWDZPhoX8|}NmT6rEe0N-rA&g%f(-TypH{cOFu$sL zK%60lv49nY;`oxq?t~q3eG9n3+klE1!&TM2Eh#B9zkvP`?vG3u4AyE7X&g}d_U=I6-4o2-Io#gKocDwmD^v8=<~tGP zVzNiu3lp(0dXGi*h)XKOq%;;zVm;=8ovD4(Il+t>6|tC2zy;k=Rf08!?|Br}m2WYL z?Q^FB5mc|6%ThO|sghhFDVl*3urpWO;lPM}P+I7BTTr5a9rhk-MM*5sq{9JEb&RVhB^L~+%w%VEcGeZ}!vQ*LD2j<((U|>62~QvNr|xJ@v(P;cx2o@BI~o znpE}H_r`;IiGR=jtiSOue(x{k7ba$#gR=jenAV z&;GPO`Dfkt7ySz^+`M{PU-^1g-f`w1$G@9Dtvmkd@B54Ljte)ho)+i6?#kUh_K*GV z@=yKkfBO6WBD-ei^l+U${d92t!Eevz?wQB=U!! zJ%`NOCwP&i8kct!94>e?hs9z!BcJ90=dJ<)oE*W;oyT@NcPz2!!b!O86SsQJCp6)q zCQd@?$HG&)4tH@m6GCol70>R-WaL#)T*6|~&^AHf5|fmJvj+!UsQXLOu2~``8W{l> z7A|ylYHQ`?l7es-<*a}wT`UZ1hX4^)Mr2|s3J*;Z3-_&)VgRdyC_@M%3dOttZrlHx E05NM{RR910 literal 0 HcmV?d00001 diff --git a/website/css/images/img02.jpg b/website/css/images/img02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d12f46aea3933fe822c46c8feb64cc26b17d716 GIT binary patch literal 329 zcmY+8Jxc>Y5Qg8m* zB!c}q8zpkjz`nyX%da11@A+z4t1}|lSbBa`HlD&o1IRR z77&kpSSCRs#6sx-XCNZ|R-tZb|xz{bJG4g_o*9GqNST$~&{V890g0&u{{ z%)-LP#>T_J!6U@S!zUyHk`WOE%L_s%0VoC6gTnZKfI*OhL6w1vnNg5|Nsy6Qkn#T! zhC~KNAON}&0a(~rfg()I2oWHQk%ft!LjdS)HWYCt;Y6XJOU%KOMc4#^no#5z1Q`_* z|KDQZVFsEe$SlZU&v1K3iE|Jiqx7)=IRS@1p6rj<&3E@L$`j^$XVrR8LUXU!l22NP T+J0^EeX&)2Vi@n2`u{fpf$%vd literal 0 HcmV?d00001 diff --git a/website/css/images/img04.jpg b/website/css/images/img04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3453cadfc3141e2257eb5a7c951ca9c65b7c1454 GIT binary patch literal 3478 zcmY+Dc{tQv|Hr>G!(fcb*dk;b-FBfV%5ocpCfkfPYsj8u$lef9-IFCdBm2H)-zi~g zLRp4nU&|6vhC7r;_wV^V&-J{{b$vePyx-@2&iUsYPaMw!>{#vV+5iX)0Jl#DaQp>8 zUiEkM2nIj^0{{RwPNXdWp?TZe!3nti@AMP-bQp7!1b34F3njZ`l6>2*SVsg+k%X%y13_9Kpf$+u=g~<0JpCvH!dMC-Xm( zyJA(`>L2t%gkw1;;c+=v%e=UUprrOqe{$f0P2Uk=UO{4_%KFFvBy6QZ zDDtE-5@KFMKcFNkev1CJXD^V2aoI5F9Zaol##S!{cqj>rm&ypUMabkLVtYUtw^dsH zuITkV!$>lCHu`O}M|4Iov|=?EIq_#{Zze(7m#|~Yo9+kFvsE#6Oiz)cEzY;ojqyuY z>CtCJmL4kjtMIS)2ImRg?pf;=u-omM)9+lfCg*dxPDh#QKd;%P3vB;lO~tBU%~5jp zeZKDJ@p&Ny05zd@YlZ-Jqme_@_4i zO45hA^x?kIeCMF#{2Q%;sjP8H<#Jk|kl*Vfjsc&LccFgWccasN;~9091C&z^o^+Mg zDQj>_i8hP46NG=g{ie>l-3NalY~JCkgy(k7`9y0nOFxLNIdl<|ER1T&u1i=^qrIXY zSQ#iRwDNJU@v4Y1TA~H(I$#^CYn1ADt9sK!O|h2y6d$S{%|4c$l#^yjxO6yIB`JUt!F`C}lfC+bJyf@B zo!2Uyu&JY&p>5emIr3#OH+8>wVKA@%L9XQJa?JZFpO{CtCJ7l$%}N?kfhC++F;Alo z-Tgt2dy?1X-#*38_y>!#@W01sch4(S`xP|D-(q3aS_#{2EV;`@dNpZng}L2^P&tK? zfb4U)bAH{RI4mAqN8IHH`*YXxbyO#J;!18Q-%KIcH1;*v63> zXETd^epz}7ns4uqi#ok`BoQ#;^5@N7L4gGFQ%yX=k-}SAWz*06^+M}`+u0;)c^a(i zT70X`qY4*i0ZY2*DR_qGpN6gonQi=;OJ@BeX&U;!#Ms7s2I4f!b^9MfYPF{7*}OKb zl+M4$L_|9B*HRT`Jh%M?^WGS;H;D`L%oyA)EjP;^Y@zmv;EihplQ6v?#N3aVKG_Sj@AG(vi zjwk484{%m!<1cR-O^~qhElHQ@D>a{sG5n`_s{gcAe_q@d+MlJK zDIQUVDA_TNv<)^H5XY42`0OP`rF}n4e5%NM?qaQvVp|ITfNZ$p?bns5flqL};>|Of z`Pmj!Ou>3=R*KQwe2^Dg{xZhUlyB=9Qv)7X8@?6Cu>AS-eZ z3Xp(MxtaDJwZjjrkMw0O-(wF4{J{|#^$1n zJtYi(nz3xkMiNUP*5tQaBh^v32enML)U1j>#Cwj`stf7hwXQlE4mnzT>rj)hh)#>+HmNOA6&aEvQ=0Olg~}U9MOroUPl8!d z2w0hgzzB?YW%{*)2W#-1<@Mx|*I2VewI542!-iG`60OeYa=2W`&}zGUgTz<2B&~y! zV+c(q@&C1XX>?1xxzkP z7t?NA-72KOrQt;z}627RfIMH+KY2lgs4PV4+uU`q-srR|F)OTiQ*bNL3wS^>> zSh#PWW}JReP7sS2jOuD*v>Dt;A~bB6ZHJ*g7^h8p3>d~Ec<#1rNCv!&?IiN)S0qq_ zTxhM+y5H_kNvNR`i-MdYL+ZCZLdST-6rax~$8YIAjrh)+m}RsI8s|{;du24&Yp}B| z`B$Jae?0Y?Z@^w!?%SHSFqD7D7f5I0a_$xGuRUQEYyxM|-4I7{P4}L`&CAW~?+QtA z@_JBbP%HYIeDBb{15wIHnr{ zODl$ph5jnPm^AuyUux4n&vUY>h8||==MYN;=V^VZG%*R)=C+)@%&AA|y}h@YM9@xB zv8!}wF``|?{yDKEFASMpz;>n&)tA21K9@zcsuDkA>V32IdU)!(X&_!$Q9T`oXbT0i z7&deD(-vKqd&}ympT{B%qhbApcUFpn9D3qlGyX1$fn_?u8p{gLLYK4k2;!GKtoQ>M zq$|+^u{K2cy2(zi;dB{Kb2i^ML?@wbO4s<%UW-OswX)3?5o^a*!vKBf}gy4+?ft3(C%`d%qYASrcec<6k``W2lp)t0r|H}12> z%bldnK`!NJC!x#(8yfgKp3hY2I>tnrhA)VgTQi22;_~;@Hf$H(-H?_Lonl*)^PkM4OiAWc3`{h3}0Jk;X8O(c+dHgU?+lPA5?I+HOHr(mq(R~A4O|dn| zf8nN4=oAyPH*(c=VKzrc+V;G4`<%>f=ZZT~rkrzqpp`eOFgr04b`R)2PiHB3)yO}l zXi3?8d-E4ol`G8hOA1@}9rk;ecf4`cXm2w6gdl7pnrcC{Fr|EIMs~s*_%%9;Uy7J@Wc$9=&$S*R?I@P0l znkALI_~IDIO~Y%J4@cDBd@Y7Iu6<29eT%00LEjm|@$ir7b2jI$sD~DPo>etauKK(S zYH_rnpkvuwb6NPGS-YEc642b=v&AFnp{fC_sX;>N_onPpLtdcm9;^~Vp4kOHy1dxD z?eiUDo+d ze@Vn{XIy~v{zOu0kAX3@OW_yj>W1p_2_;26FP=PjZ#dp_Xg7N3H + + +

+ under construction +

diff --git a/website/htdocs/blog.html.pyt b/website/htdocs/blog.html.pyt new file mode 100644 index 0000000..2a94882 --- /dev/null +++ b/website/htdocs/blog.html.pyt @@ -0,0 +1,35 @@ + + + +
+

Welcome to Indication

+

September 10, 2009Posted by Someone

+
 
+
+

This is Indication , a free, fully standards-compliant CSS template designed by FreeCssTemplates for Free CSS Templates. This free template is released under a Creative Commons Attributions 2.5 license, so you’re pretty much free to do whatever you want with it (even use it commercially) provided you keep the links in the footer intact. Aside from that, have fun with it :)

+

Sed lacus. Donec lectus. Nullam pretium nibh ut turpis. Nam bibendum. In nulla tortor, elementum ipsum. Proin imperdiet est. Phasellus dapibus semper urna. Pellentesque ornare, orci in felis. Donec ut ante. In id eros. Suspendisse lacus turpis, cursus egestas at sem.

+ +
+
+ + +
+

Lorem ipsum sed aliquam

+

September 10, 2009Posted by Someone

+
 
+
+

Sed lacus. Donec lectus. Nullam pretium nibh ut turpis. Nam bibendum. In nulla tortor, elementum vel, tempor at, varius non, purus. Mauris vitae nisl nec metus placerat consectetuer. Donec ipsum. Proin imperdiet est. Phasellus dapibus semper urna. Pellentesque ornare, orci in consectetuer hendrerit, urna elit eleifend nunc, ut consectetuer nisl felis ac diam. Etiam non felis. Donec ut ante. In id eros. Suspendisse lacus turpis, cursus egestas at sem. Mauris quam enim, molestie in, rhoncus ut, lobortis a, est.

+ +
+
+ + +
+

Consecteteur hendrerit

+

September 10, 2009Posted by Someone

+
 
+
+

Sed lacus. Donec lectus. Nullam pretium nibh ut turpis. Nam bibendum. In nulla tortor, elementum vel, tempor at, varius non, purus. Mauris vitae nisl nec metus placerat consectetuer. Donec ipsum. Proin imperdiet est. Phasellus dapibus semper urna. Pellentesque ornare, orci in consectetuer hendrerit, urna elit eleifend nunc, ut consectetuer nisl felis ac diam. Etiam non felis. Donec ut ante. In id eros. Suspendisse lacus turpis, cursus egestas at sem. Mauris quam enim, molestie in, rhoncus ut, lobortis a, est.

+ +
+
diff --git a/website/htdocs/contact.html.pyt b/website/htdocs/contact.html.pyt new file mode 100644 index 0000000..023a866 --- /dev/null +++ b/website/htdocs/contact.html.pyt @@ -0,0 +1,6 @@ + + + +

+ under construction +

diff --git a/website/htdocs/index.html.pyt b/website/htdocs/index.html.pyt new file mode 100644 index 0000000..b916de9 --- /dev/null +++ b/website/htdocs/index.html.pyt @@ -0,0 +1,4 @@ + + + + diff --git a/website/htdocs/links.html.pyt b/website/htdocs/links.html.pyt new file mode 100644 index 0000000..92ac263 --- /dev/null +++ b/website/htdocs/links.html.pyt @@ -0,0 +1,6 @@ + + + +

+ under construction +

diff --git a/website/htdocs/photos.html.pyt b/website/htdocs/photos.html.pyt new file mode 100644 index 0000000..f58cecb --- /dev/null +++ b/website/htdocs/photos.html.pyt @@ -0,0 +1,6 @@ + + + +

+ under construction +

diff --git a/website/img/favicon.ico b/website/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d3e31962fa0234e185a33059a6a21f0526c509fa GIT binary patch literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&}eK~=G6?$+y5ivMp>gFFd71* zA%F-0Mg$YYVq`)QU}R!K765UP1Rz|v0E`P2KyV=f#PK1jz>WYhfh8cqMn*=EID|xX wHWuE0V6g|n|A6>UJp;oJdj^IN@(c_Q_!$@uurn|;Fr(swK(WXD5IrD$0Q%$?@c;k- literal 0 HcmV?d00001 diff --git a/website/index.py b/website/index.py new file mode 100644 index 0000000..2b5eef3 --- /dev/null +++ b/website/index.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +### +### $Release: $ +### $Copyright$ +### $License$ +### + +from __future__ import with_statement + +from google.appengine.ext.webapp import WSGIApplication +from google.appengine.ext.webapp.util import run_wsgi_app + +import sys, os + + +## add 'lib' into sys.path +sys.path.insert(0, os.getcwd() + '/lib') + +## load apptenjin.py, config.py, and myapp.py +import config, apptenjin, myapp +if config.encoding != 'utf-8': + import tenjin + apptenjin.GLOBAL['to_str'] = tenjin.helpers.generate_tostrfunc(encode=config.encoding) +apptenjin.AppTenjin.config = config + +## main application +routing = [ + ('/.*', myapp.MyRequestHandler), +] +application = WSGIApplication(routing, debug=True) + +## main routine +def main(): + run_wsgi_app(application) + +if __name__ == "__main__": + main() diff --git a/website/js/jquery-1.4.2.min.js b/website/js/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/website/js/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/website/lib/apptenjin.py b/website/lib/apptenjin.py new file mode 100644 index 0000000..a9c146a --- /dev/null +++ b/website/lib/apptenjin.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- + +### +### $Release: $ +### $Copyright$ +### $License$ +### + +from __future__ import with_statement + +from google.appengine.ext import webapp + +import sys, os, re, logging +from UserDict import UserDict + + +class AppTenjin(object): + try: + import config + except LoadError: + ## dummy class to simulate config object + class config(object): + encoding = 'utf-8' + layout_template = '_layout.html.pyt' + analytics_id = None + template_path = ['htdocs', 'tmpl'] + + root = os.getcwd() + version = '$Release: 0.0.0 $'.split(' ')[1] + is_dev = (os.getenv("SERVER_SOFTWARE") or "").startswith("Development") + if is_dev: + logging.basicConfig(level=logging.DEBUG) + logger = logging + +del logging + + +## load tenjin +import tenjin +from tenjin.helpers import * +from tenjin.helpers.html import * +from my_template import MyTemplate +#if AppTenjin.config.encoding != 'utf-8': +# to_str = tenjin.helpers.generate_tostrfunc(encode=AppTenjin.config.encoding) +tenjin.logger = AppTenjin.logger +shared_cache = tenjin.GaeMemcacheCacheStorage() +engine = tenjin.Engine(path=AppTenjin.config.template_path, cache=shared_cache, + layout=AppTenjin.config.layout_template, templateclass=MyTemplate) + + +## +class Script(tenjin.Template): + """execute script using Tenjin mechanism""" + + def convert(self, input, filename=None): + self._reset(input, filename) + self.script = input + return input + + +## change tenjin.Engine class to generate Script object for script (*.cgi) +def _create_template(self, filepath, _context, _globals): + if filepath and self.preprocess: + s = self._preprocess(filepath, _context, _globals) + template = self.templateclass(None, **self.kwargs) + template.convert(s, filepath) + else: + #template = self.templateclass(filepath, **self.kwargs) + klass = filepath.endswith('.cgi') and Script or self.templateclass + template = klass(filepath, **self.kwargs) + return template +tenjin.Engine._create_template = _create_template + + +## +class ExitException(Exception): + """dummy exception class to stop request handler gracefully""" + pass + + +## +class DefaultRequestHandler(webapp.RequestHandler): + """request handler class for AppTenjin""" + + def __init__(self): + webapp.RequestHandler.__init__(self) + self.engine = engine + self.context = UserDict() + self.context['self'] = self + + def before(self): + pass + + def after(self): + pass + + def handle(self): + self.before() + path = self.request.path # or path_info + assert path[0] == '/' + htdocs_path = AppTenjin.root + '/htdocs' + filepath = htdocs_path + path + if os.path.isdir(filepath): + if not filepath.endswith('/'): + permanent = True + self.redirect(path + '/', permanent) + self.after() + return + filepath += 'index.html' + html = None + if os.path.exists(filepath) and not path.endswith(('.pyt', '.cgi')): + with open(filepath) as f: + html = f.read() + elif os.path.exists(filepath + '.pyt'): + template_filename = filepath[len(htdocs_path)+1:] + '.pyt' + flag_layout = filepath.endswith('.html') # False when '.xml' or '.json' + html = engine.render(template_filename, self.context, layout=flag_layout) + else: + if path.endswith('.cgi') and os.path.exists(filepath): + script_path = filepath + else: + script_path = self.find_script('htdocs', path) + if script_path: + self.context['_engine'] = engine + tmpl = Script(script_path) + tmpl.render(self.context) + html = "" # script should send html by self.response.out.write() + else: + self.response.set_status(404) + self.context['message'] = "%s: not found" % self.request.path + html = engine.render("404.html.pyt", self.context) + ## response body + self.response.out.write(html) + self.after() + + def find_script(self, base, path): + _isfile = os.path.isfile + for item in path.split('/')[1:]: + base = '%s/%s' % (base, item) + script = base + '.cgi' + if _isfile(script): + return script + return None + + def exit(self): + raise ExitException() + + def handle_exception(self, exception, debug_mode): + if isinstance(exception, ExitException): + pass + else: + webapp.RequestHandler.handle_exception(self, exception, debug_mode) + self.after() + + def use_layout_template(self, layout_filename): + self.context['_layout'] = layout_filename + + #get = handle + #post = handle + #put = handle + #delete = handle + #head = handle + def get(self): self.handle() + def post(self): self.handle() + def put(self): self.handle() + def delete(self): self.handle() + def head(self): self.handle() + + +## global variables +GLOBAL = globals() diff --git a/website/lib/my_template.py b/website/lib/my_template.py new file mode 100644 index 0000000..fa9a171 --- /dev/null +++ b/website/lib/my_template.py @@ -0,0 +1,278 @@ +## +## parse template with recognizing '#endfor', '#endif', and so on. +## +## ex: +## import tenjin +## from tenjin.helpers import * +## from my_template import MyTemplate +## engine = tenjin.Engine(templateclass=MyTemplate) +## print("------------- script") +## print(engine.get_template("file.pyhtml").script) +## print("------------- output") +## print(engine.render("file.pyhtml") +## + +import re +import tenjin + + +class TemplateSyntaxError(Exception): + pass + + +def _args2dict(*args): + return dict([ (w, w) for w in args ]) + +START_WORDS = _args2dict('for', 'if', 'while', 'def', 'try:', 'with', 'class') +END_WORDS = _args2dict('#endfor', '#endif', '#endwhile', '#enddef', '#endtry', '#endwith', '#endclass') +CONT_WORDS = _args2dict('elif', 'else:', 'except', 'except:', 'finally:') + + +class MyTemplate(tenjin.Template): + + def parse_stmts(self, buf, input): + if not input: + return + rexp = self.stmt_pattern() + is_bol = True + index = 0 + for m in rexp.finditer(input): + mspace, code, rspace = m.groups() + #mspace, close, rspace = m.groups() + #code = input[m.start()+4+len(mspace):m.end()-len(close)-(rspace and len(rspace) or 0)] + text = input[index:m.start()] + index = m.end() + ## detect spaces at beginning of line + lspace = None + if text == '': + if is_bol: + lspace = '' + elif text[-1] == '\n': + lspace = '' + else: + rindex = text.rfind('\n') + if rindex < 0: + if is_bol and text.isspace(): + lspace = text + text = '' + else: + s = text[rindex+1:] + if s.isspace(): + lspace = s + text = text[:rindex+1] + #is_bol = rspace is not None + ## add text, spaces, and statement + self.parse_exprs(buf, text, is_bol) + is_bol = rspace is not None + #if lspace: + # buf.append(lspace) + #if mspace != " ": + # #buf.append(mspace) + # buf.append(mspace == "\t" and "\t" or "\n") # don't append "\r\n"! + if code: + code = self.statement_hook(code) + self.add_stmt(buf, code) + #self._set_spaces(code, lspace, mspace) + #if rspace: + # #buf.append(rspace) + # buf.append("\n") # don't append "\r\n"! + rest = input[index:] + if rest: + self.parse_exprs(buf, rest) + + def parse_exprs(self, buf, input, is_bol=False): + buf2 = [] + tenjin.Template.parse_exprs(self, buf2, input, is_bol) + if buf2: + buf.append(''.join(buf2)) + + def add_stmt(self, buf, code): + if not code: return + lines = code.splitlines(True) # keep "\n" + if lines[-1][-1] != "\n": + lines[-1] = lines[-1] + "\n" + buf.extend(lines) + + def after_convert(self, buf): + tenjin.Template.after_convert(self, buf) + block = self.parse_lines(buf) + buf[:] = [] + self._join_block(block, buf, 0) + + depth = -1 + + ## + ## ex. + ## input = r""" + ## if items: + ## _buf.extend(('
    \n', )) + ## i = 0 + ## for item in items: + ## i += 1 + ## _buf.extend(('
  • ', to_str(item), '
  • \n', )) + ## #endfor + ## _buf.extend(('
\n', )) + ## #endif + ## """[1:] + ## lines = input.splitlines() + ## block = self.parse_lines(lines) + ## #=> [ "if items:\n", + ## [ "_buf.extend(('
    \n', ))\n", + ## "i = 0\n", + ## "for item in items:\n", + ## [ "i += 1\n", + ## "_buf.extend(('
  • ', to_str(item), '
  • \n', ))\n", + ## ], + ## "#endfor\n", + ## "_buf.extend(('
\n', ))\n", + ## ], + ## "#endif\n", + ## ] + def parse_lines(self, lines): + block = [] + try: + self._parse_lines(lines.__iter__(), False, block) + except StopIteration: + if self.depth > 0: + raise TemplateSyntaxError("unexpected EOF.") + else: + #raise TemplateSyntaxError("unexpected syntax.") + pass + return block + + def _parse_lines(self, iter, end_block, block=None): + if block is None: block = [] + START_WORDS_ = START_WORDS + END_WORDS_ = END_WORDS + CONT_WORDS_ = CONT_WORDS + while True: + line = iter.next() + m = re.search(r'\S+', line) + if not m: + block.append(line) + continue + word = m.group(0) + if word in END_WORDS_: + if word != end_block: + raise TemplateSyntaxError("'%s' exptexted buf got '%s'." % (end_block, word)) + return block, line, False + elif line.endswith(':\n'): + if word in CONT_WORDS_: + return block, line, True + elif word in START_WORDS_: + block.append(line) + self.depth += 1 + child_block, line, has_sibling = self._parse_lines(iter, '#end'+word) + block.extend((child_block, line, )) + while has_sibling: + child_block, line, has_sibling = self._parse_lines(iter, '#end'+word) + block.extend((child_block, line, )) + self.depth -= 1 + else: + block.append(line) + else: + block.append(line) + assert "unreachable" + + #def join_block(self, block): + # buf = [] + # depth = 0 + # self._join_block(block, buf, depth) + # return ''.join(buf) + + def _join_block(self, block, buf, depth): + indent = ' ' * depth + for line in block: + if isinstance(line, list): + self._join_block(line, buf, depth+1) + else: + buf.append(indent + line.lstrip()) + + +if __name__ == '__main__': + import sys + if len(sys.argv) > 1: + filename = sys.argv[1] + template = MyTemplate(filename) + print(template.script) + else: # test + input = r""" + + + + + + + + + + + +

nothing.

+ +
#{i} + ${item} +
+ +

Not found.

+ + + +"""[1:] + expected = r""" +_buf.extend((''' + \n''', )); +if items: + _buf.extend((''' \n''', )); + i = 0 + for item in items: + i += 1 + klass = i % 2 and 'odd' or 'even' + _buf.extend((''' + \n''', )); + else: + _buf.extend(('''

nothing.

\n''', )); + #endfor + _buf.extend(('''
''', to_str(i), ''' + ''', escape(to_str(item)), ''' +
\n''', )); +else: + _buf.extend(('''

Not found.

\n''', )); +#endif +_buf.extend((''' +\n''', )); +"""[1:] + # + template = MyTemplate() + actual = template.convert(input) + assert expected == actual + #print(actual) + #import pprint + #pprint.pprint(result) + + ## if-statement + input = r""" + + 0: ?> +

Positive.

+ +

Negative.

+ +

Zero.

+ + +"""[1:] + expected = r""" +for x in nums: + if x > 0: + _buf.extend(('''

Positive.

\n''', )); + elif x < 0: + _buf.extend(('''

Negative.

\n''', )); + else: + _buf.extend(('''

Zero.

\n''', )); + #endif +#endfor +"""[1:] + actual = template.convert(input) + assert expected == actual + diff --git a/website/lib/tenjin.py b/website/lib/tenjin.py new file mode 100644 index 0000000..6e3a7ad --- /dev/null +++ b/website/lib/tenjin.py @@ -0,0 +1,1374 @@ +## +## $Release: 0.9-beta $ +## copyright(c) 2008-2010 kuwata-lab.com all rights reserved. +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal in the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## The above copyright notice and this permission notice shall be +## included in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +## + +"""Very fast and light-weight template engine based embedded Python. + See User's Guide, FAQ, and examples for details. + http://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html + http://www.kuwata-lab.com/tenjin/pytenjin-faq.html + http://www.kuwata-lab.com/tenjin/pytenjin-examples.html +""" + +__release__ = "0.9-beta" +__license__ = "MIT License" +__all__ = ['Template', 'Engine', 'helpers', 'html', ] + + +import re, sys, os, time +random = marshal = pickle = memcache = unquote = None # lazy import +python3 = sys.version_info[0] == 3 +python2 = sys.version_info[0] == 2 + +logger = None + + +## +## utilities +## + +def _write_binary_file(filename, content): + global random + f = None + try: + if random is None: from random import random + tmpfile = filename + str(random())[1:] + f = open(tmpfile, 'wb') + f.write(content) + finally: + if f: + f.close() + os.rename(tmpfile, filename) + +def _read_binary_file(filename): + f = None + try: + f = open(filename, 'rb') + return f.read() + finally: + if f: f.close() + +if python2: + + def _read_template_file(filename, encoding=None): + s = _read_binary_file(filename) ## binary(=str) + if encoding: s = s.decode(encoding) ## binary(=str) to unicode + return s + +elif python3: + + def _read_template_file(filename, encoding=None): + s = _read_binary_file(filename) ## binary + return s.decode(encoding or 'utf-8') ## binary to unicode(=str) + +def _create_module(module_name): + """ex. mod = _create_module('tenjin.util')""" + import types + mod = types.ModuleType(module_name) # or module_name.split('.')[-1] ? + mod.__file__ = __file__ + sys.modules[module_name] = mod + return mod + + + +## +## helper method's module +## + +if True: + + if python2: + def generate_tostrfunc(encode=None, decode=None): + """Generate 'to_str' function with encode or decode encoding. + ex. generate to_str() function which encodes unicode into binary(=str). + to_str = tenjin.generate_tostrfunc(encode='utf-8') + repr(to_str(u'hoge')) #=> 'hoge' (str) + ex. generate to_str() function which decodes binary(=str) into unicode. + to_str = tenjin.generate_tostrfunc(decode='utf-8') + repr(to_str('hoge')) #=> u'hoge' (unicode) + """ + if encode: + if decode: + raise ValueError("can't specify both encode and decode encoding.") + else: + def to_str(val, _str=str, _unicode=unicode, _isa=isinstance, _encode=encode): + """Convert val into string or return '' if None. Unicode will be encoded into binary(=str).""" + if _isa(val, _str): return val + if val is None: return '' + if _isa(val, _unicode): return val.encode(_encode) # unicode to binary(=str) + return _str(val) + else: + if decode: + def to_str(val, _str=str, _unicode=unicode, _isa=isinstance, _decode=decode): + """Convert val into string or return '' if None. Binary(=str) will be decoded into unicode.""" + if _isa(val, _str): return val.decode(_decode) # binary(=str) to unicode + if val is None: return '' + if _isa(val, _unicode): return val + return _unicode(val) + else: + def to_str(val, _str=str, _unicode=unicode, _isa=isinstance): + """Convert val into string or return '' if None. Both binary(=str) and unicode will be retruned as-is.""" + if _isa(val, _str): return val + if val is None: return '' + if _isa(val, _unicode): return val + return _str(val) + return to_str + + elif python3: + def generate_tostrfunc(decode=None, encode=None): + """Generate 'to_str' function with encode or decode encoding. + ex. generate to_str() function which encodes unicode(=str) into bytes + to_str = tenjin.generate_tostrfunc(encode='utf-8') + repr(to_str('hoge')) #=> b'hoge' (bytes) + ex. generate to_str() function which decodes bytes into unicode(=str). + to_str = tenjin.generate_tostrfunc(decode='utf-8') + repr(to_str(b'hoge')) #=> 'hoge' (str) + """ + if encode: + if decode: + raise ValueError("can't specify both encode and decode encoding.") + else: + def to_str(val, _str=str, _bytes=bytes, _isa=isinstance, _encode=encode): + """Convert val into string or return '' if None. Unicode(=str) will be encoded into bytes.""" + if _isa(val, _str): return val.encode(_encode) # unicode(=str) to binary + if val is None: return '' + if _isa(val, _bytes): return val + return _str(val).encode(_encode) + else: + if decode: + def to_str(val, _str=str, _bytes=bytes, _isa=isinstance, _decode=decode): + """Convert val into string or return '' if None. Bytes will be decoded into unicode(=str).""" + if _isa(val, _str): return val + if val is None: return '' + if _isa(val, _bytes): return val.decode(_decode) # binary to unicode(=str) + return _str(val) + else: + def to_str(val, _str=str, _bytes=bytes, _isa=isinstance): + """Convert val into string or return '' if None. Both bytes and unicode(=str) will be retruned as-is.""" + if _isa(val, _str): return val + if val is None: return '' + if _isa(val, _bytes): return val + return _str(val) + return to_str + + if python2: + to_str = generate_tostrfunc(encode='utf-8') # or encode=None? + elif python3: + to_str = generate_tostrfunc(decode='utf-8') + + def echo(string): + """add string value into _buf. this is equivarent to '#{string}'.""" + frame = sys._getframe(1) + context = frame.f_locals + context['_buf'].append(string) + + def start_capture(varname=None): + """start capturing with name.""" + frame = sys._getframe(1) + context = frame.f_locals + context['_buf_tmp'] = context['_buf'] + context['_capture_varname'] = varname + context['_buf'] = [] + + def stop_capture(store_to_context=True): + """stop capturing and return the result of capturing. + if store_to_context is True then the result is stored into _context[varname]. + """ + frame = sys._getframe(1) + context = frame.f_locals + result = ''.join(context['_buf']) + context['_buf'] = context.pop('_buf_tmp') + varname = context.pop('_capture_varname') + if varname: + context[varname] = result + if store_to_context: + context['_context'][varname] = result + return result + + def captured_as(name): + """helper method for layout template. + if captured string is found then append it to _buf and return True, + else return False. + """ + frame = sys._getframe(1) + context = frame.f_locals + if name in context: + _buf = context['_buf'] + _buf.append(context[name]) + return True + return False + + def cache_as(cache_key, *options): + def deco(func): + def f(): + f_locals = sys._getframe().f_locals + _buf = f_locals.get('_buf') + _context = f_locals.get('_context') + _datacache = _context.get('_datacache') + if not _datacache: + raise TypeError("data cache is required for cache_as()") + fragment = _datacache.get(cache_key, *options) + if fragment is None: + pos = len(_buf) + _context_block = _context.get('_context_block') + if _context_block: + values = _context_block(cache_key) + if isinstance(values, dict): + d.update(values) + fragment = ''.join(_buf[pos:]) + _datacache.set(cache_key, fragment, *options) + else: + _buf.append(fragment) + return f + return deco + + def cache_with(func, cache_key, *options): + frame = sys._getframe(1) + f_locals = frame.f_locals + _buf = f_locals.get('_buf') + _context = f_locals.get('_context') + _datacache = _context.get('_datacache') + if not _datacache: + raise TypeError("data cache is required for cache_with()") + fragment = _datacache.get(cache_key, *options) + if fragment is None: + pos = len(_buf) + vars = frame.f_globals.copy() + vars.update(frame.f_locals) + _context_block = _context.get('_context_block') + if _context_block: + values = _context_block(cache_key) + if isinstance(values, dict): + vars.update(values) + #f_locals.update(values) + sys.stderr.write("*** debug: f_globals.keys()=%s\n" % (repr(frame.f_globals.keys()))) + sys.stderr.write("*** debug: _context.keys()=%s\n" % (repr(_context.keys()))) + sys.stderr.write("*** debug: f_locals.keys()=%s\n" % (repr(f_locals.keys()))) + #exec(func.func_code, frame.f_globals, f_locals) + exec(func.func_code, vars, vars) + #func() + fragment = ''.join(_buf[pos:]) + _datacache.set(cache_key, fragment, *options) + else: + _buf.append(fragment) + + def _p(arg): + """ex. '/show/'+_p("item['id']") => "/show/#{item['id']}" """ + return '<`#%s#`>' % arg # decoded into #{...} by preprocessor + + def _P(arg): + """ex. '%s' % _P("item['id']") => "${item['id']}" """ + return '<`$%s$`>' % arg # decoded into ${...} by preprocessor + + def _decode_params(s): + """decode <`#...#`> and <`$...$`> into #{...} and ${...}""" + global unquote + if unquote is None: + import urllib + if python2: from urllib import unquote + elif python3: from urllib.parse import unquote + dct = { 'lt':'<', 'gt':'>', 'amp':'&', 'quot':'"', '#039':"'", } + def unescape(s): + #return s.replace('<', '<').replace('>', '>').replace('"', '"').replace(''', "'").replace('&', '&') + return re.sub(r'&(lt|gt|quot|amp|#039);', lambda m: dct[m.group(1)], s) + s = to_str(s) + s = re.sub(r'%3C%60%23(.*?)%23%60%3E', lambda m: '#{%s}' % unquote(m.group(1)), s) + s = re.sub(r'%3C%60%24(.*?)%24%60%3E', lambda m: '${%s}' % unquote(m.group(1)), s) + s = re.sub(r'<`#(.*?)#`>', lambda m: '#{%s}' % unescape(m.group(1)), s) + s = re.sub(r'<`\$(.*?)\$`>', lambda m: '${%s}' % unescape(m.group(1)), s) + s = re.sub(r'<`#(.*?)#`>', r'#{\1}', s) + s = re.sub(r'<`\$(.*?)\$`>', r'${\1}', s) + return s + + mod = _create_module('tenjin.helpers') + mod.to_str = to_str + mod.generate_tostrfunc = generate_tostrfunc + mod.echo = echo + mod.start_capture = start_capture + mod.stop_capture = stop_capture + mod.captured_as = captured_as + mod.cache_with = cache_with + mod._p = _p + mod._P = _P + mod._decode_params = _decode_params + mod.__all__ = ['escape', 'to_str', 'echo', 'generate_tostrfunc', + 'start_capture', 'stop_capture', 'captured_as', 'cache_with', + '_p', '_P', '_decode_params', + ] + +helpers = mod +del echo, start_capture, stop_capture, captured_as, _p, _P, _decode_params +#del to_str, generate_tostrfunc +del mod + + +## +## module for html +## +if True: + + #_escape_table = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } + #_escape_pattern = re.compile(r'[&<>"]') + ##_escape_callable = lambda m: _escape_table[m.group(0)] + ##_escape_callable = lambda m: _escape_table.__get__(m.group(0)) + #_escape_get = _escape_table.__getitem__ + #_escape_callable = lambda m: _escape_get(m.group(0)) + #_escape_sub = _escape_pattern.sub + + #def escape_xml(s): + # return s # 3.02 + + #def escape_xml(s): + # return _escape_pattern.sub(_escape_callable, s) # 6.31 + + #def escape_xml(s): + # return _escape_sub(_escape_callable, s) # 6.01 + + #def escape_xml(s, _p=_escape_pattern, _f=_escape_callable): + # return _p.sub(_f, s) # 6.27 + + #def escape_xml(s, _sub=_escape_pattern.sub, _callable=_escape_callable): + # return _sub(_callable, s) # 6.04 + + #def escape_xml(s): + # s = s.replace('&', '&') + # s = s.replace('<', '<') + # s = s.replace('>', '>') + # s = s.replace('"', '"') + # return s # 5.83 + + def escape_xml(s): + """Escape '&', '<', '>', '"' into '&', '<', '>', '"'.""" + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') # 5.72 + + + def tagattr(name, expr, value=None, escape=True): + """(experimental) Return ' name="value"' if expr is true value, else '' (empty string). + If value is not specified, expr is used as value instead.""" + if not expr: return '' + if value is None: value = expr + if escape: value = escape_xml(to_str(value)) + return ' %s="%s"' % (name, value) + + def tagattrs(**kwargs): + """(experimental) built html tag attribtes. + ex. + >>> tagattrs(klass='main', size=20) + ' class="main" size="20"' + >>> tagattrs(klass='', size=0) + '' + """ + if 'klass' in kwargs: kwargs['class'] = kwargs.pop('klass') + if 'checked' in kwargs: kwargs['checked'] = kwargs.pop('checked') and 'checked' or None + if 'selected' in kwargs: kwargs['selected'] = kwargs.pop('selected') and 'selected' or None + if 'disabled' in kwargs: kwargs['disabled'] = kwargs.pop('disabled') and 'disabled' or None + return ''.join([' %s="%s"' % (k, escape_xml(to_str(v))) for k, v in kwargs.items() if v]) + + def checked(expr): + """return ' checked="checked"' if expr is true.""" + return expr and ' checked="checked"' or '' + + def selected(expr): + """return ' selected="selected"' if expr is true.""" + return expr and ' selected="selected"' or '' + + def disabled(expr): + """return ' disabled="disabled"' if expr is true.""" + return expr and ' disabled="disabled"' or '' + + def nl2br(text): + """replace "\n" to "
\n" and return it.""" + if not text: + return '' + return text.replace('\n', '
\n') + + def text2html(text, _nl2br=nl2br): + """(experimental) escape xml characters, replace "\n" to "
\n", and return it.""" + if not text: + return '' + return _nl2br(escape_xml(text).replace(' ', '  ')) + + def nv(name, value, sep=None, _tagattrs=tagattrs, **kwargs): + """(experimental) Build name and value attributes. + ex. + >>> nv('rank', 'A') + 'name="rank" value="A"' + >>> nv('rank', 'A', '.') + 'name="rank" value="A" id="rank.A"' + >>> nv('rank', 'A', '.', checked=True) + 'name="rank" value="A" id="rank.A" checked="checked"' + >>> nv('rank', 'A', '.', klass='error', style='color:red') + 'name="rank" value="A" id="rank.A" class="error" style="color:red"' + """ + s = sep and 'name="%s" value="%s" id="%s"' % (name, value, name+sep+value) \ + or 'name="%s" value="%s"' % (name, escape_xml(value)) + return kwargs and s + _tagattrs(**kwargs) or s + + def new_cycle(*values): + """Generate cycle object. + ex. + cycle = new_cycle('odd', 'even') + print(cycle()) #=> 'odd' + print(cycle()) #=> 'even' + print(cycle()) #=> 'odd' + print(cycle()) #=> 'even' + """ + def gen(values): + n = len(values) + i = 0 + while True: + yield values[i] + i = (i + 1) % n + if python2: return gen(values).next + elif python3: return gen(values).__next__ + + mod = _create_module('tenjin.helpers.html') + #mod._escape_table = _escape_table + mod.escape_xml = escape_xml + mod.escape = escape_xml + mod.tagattr = tagattr + mod.tagattrs = tagattrs + mod.checked = checked + mod.selected = selected + mod.disabled = disabled + mod.nl2br = nl2br + mod.text2html = text2html + mod.nv = nv + mod.new_cycle = new_cycle + +helpers.html = mod +#del escape_xml +del tagattr, tagattrs, checked, selected, disabled, nl2br, text2html, nv, new_cycle +del mod +helpers.escape = helpers.html.escape_xml + + + +## +## Template class +## + +class Template(object): + """Convert and evaluate embedded python string. + See User's Guide, FAQ, and examples for details. + http://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html + http://www.kuwata-lab.com/tenjin/pytenjin-faq.html + http://www.kuwata-lab.com/tenjin/pytenjin-examples.html + """ + + ## default value of attributes + filename = None + encoding = None + escapefunc = 'escape' + tostrfunc = 'to_str' + indent = 4 + preamble = None + postamble = None # "_buf = []" + smarttrim = None # "print ''.join(_buf)" + args = None + timestamp = None + + def __init__(self, filename=None, encoding=None, escapefunc=None, tostrfunc=None, indent=None, preamble=None, postamble=None, smarttrim=None): + """Initailizer of Template class. + + filename:str (=None) + Filename to convert (optional). If None, no convert. + encoding:str (=None) + Encoding name. If specified, template string is converted into + unicode object internally. + Template.render() returns str object if encoding is None, + else returns unicode object if encoding name is specified. + escapefunc:str (='escape') + Escape function name. + tostrfunc:str (='to_str') + 'to_str' function name. + indent:int (=4) + Indent width. + preamble:str or bool (=None) + Preamble string which is inserted into python code. + If true, '_buf = []' is used insated. + postamble:str or bool (=None) + Postamble string which is appended to python code. + If true, 'print "".join(_buf)' is used instead. + smarttrim:bool (=None) + If True then "
\\n#{_context}\\n
" is parsed as + "
\\n#{_context}
". + """ + if encoding is not None: self.encoding = encoding + if escapefunc is not None: self.escapefunc = escapefunc + if tostrfunc is not None: self.tostrfunc = tostrfunc + if indent is not None: self.indent = indent + if preamble is not None: self.preamble = preamble + if postamble is not None: self.postamble = postamble + if smarttrim is not None: self.smarttrim = smarttrim + # + if preamble is True: self.preamble = "_buf = []" + if postamble is True: self.postamble = "print ''.join(_buf)" + if filename: + self.convert_file(filename) + else: + self._reset() + + def _reset(self, input=None, filename=None): + self._spaces = '' + self.script = None + self.bytecode = None + self.input = input + self.filename = filename + if input != None: + i = input.find("\n") + if i < 0: + self.newline = "\n" # or None + elif len(input) >= 2 and input[i-1] == "\r": + self.newline = "\r\n" + else: + self.newline = "\n" + self._stmt_not_added_yet = True + + def before_convert(self, buf): + #buf.append('_buf = []; ') + if self.preamble: + buf.append(self.preamble) + buf.append(self.input.startswith('([ \t]*\r?\n)?' % pi, re.S) + + STMT_PATTERN = None + + compile_stmt_pattern = staticmethod(compile_stmt_pattern) + + def stmt_pattern(self): + pat = Template.STMT_PATTERN + if not pat: # make re.compile() to be lazy (because it is heavy weight) + pat = Template.STMT_PATTERN = Template.compile_stmt_pattern('py') + return pat + + def parse_stmts(self, buf, input): + if not input: + return + rexp = self.stmt_pattern() + is_bol = True + index = 0 + for m in rexp.finditer(input): + mspace, code, rspace = m.groups() + #mspace, close, rspace = m.groups() + #code = input[m.start()+4+len(mspace):m.end()-len(close)-(rspace and len(rspace) or 0)] + text = input[index:m.start()] + index = m.end() + ## detect spaces at beginning of line + lspace = None + if text == '': + if is_bol: + lspace = '' + elif text[-1] == '\n': + lspace = '' + else: + rindex = text.rfind('\n') + if rindex < 0: + if is_bol and text.isspace(): + lspace = text + text = '' + else: + s = text[rindex+1:] + if s.isspace(): + lspace = s + text = text[:rindex+1] + #is_bol = rspace is not None + ## add text, spaces, and statement + self.parse_exprs(buf, text, is_bol) + is_bol = rspace is not None + if lspace: + buf.append(lspace) + if mspace != " ": + #buf.append(mspace) + buf.append(mspace == "\t" and "\t" or "\n") # don't append "\r\n"! + if code: + code = self.statement_hook(code) + self.add_stmt(buf, code) + self._set_spaces(code, lspace, mspace) + if rspace: + #buf.append(rspace) + buf.append("\n") # don't append "\r\n"! + rest = input[index:] + if rest: + self.parse_exprs(buf, rest) + + def statement_hook(self, stmt): + """expand macros and parse '#@ARGS' in a statement.""" + if self.args is None: + args_pattern = r'^ *#@ARGS(?:[ \t]+(.*?))?$' + m = re.match(args_pattern, stmt) + if m: + arr = (m.group(1) or '').split(',') + args = []; declares = [] + for s in arr: + arg = s.strip() + if not s: continue + if not re.match('^[a-zA-Z_]\w*$', arg): + raise ValueError("%s: invalid template argument." % arg) + args.append(arg) + declares.append("%s = _context.get('%s'); " % (arg, arg)) + self.args = args + return ''.join(declares) + ## + return stmt + + EXPR_PATTERN = None + + def expr_pattern(self): + pat = Template.EXPR_PATTERN + if not pat: # make re.compile() to be lazy (because it is heavy weight) + pat = Template.EXPR_PATTERN = re.compile(r'([#$])\{(.*?)\}', re.S) + return pat + + def get_expr_and_escapeflag(self, match): + return match.group(2), match.group(1) == '$' + + def parse_exprs(self, buf, input, is_bol=False): + if not input: + return + if self._spaces: + buf.append(self._spaces) + self.start_text_part(buf) + rexp = self.expr_pattern() + smarttrim = self.smarttrim + nl = self.newline + nl_len = len(nl) + pos = 0 + for m in rexp.finditer(input): + start = m.start() + text = input[pos:start] + pos = m.end() + expr, flag_escape = self.get_expr_and_escapeflag(m) + # + if text: + self.add_text(buf, text) + #if text[-1] == "\n": + # buf.append("\n") + # if self._spaces: + # buf.append(self._spaces) + self.add_expr(buf, expr, flag_escape) + # + if smarttrim: + flag_bol = text.endswith(nl) or not text and (start > 0 or is_bol) + if flag_bol and not flag_escape and input[pos:pos+nl_len] == nl: + pos += nl_len + buf.append("\n") + if smarttrim: + if buf and buf[-1] == "\n": + buf.pop() + rest = input[pos:] + if rest: + self.add_text(buf, rest, True) + self.stop_text_part(buf) + if input[-1] == '\n': + buf.append("\n") + + def start_text_part(self, buf): + buf.append("_buf.extend((") + + def stop_text_part(self, buf): + buf.append("));") + + _quote_rexp = None + + def add_text(self, buf, text, encode_newline=False): + if not text: + return; + if self.encoding and python2: + buf.append("u'''") + else: + buf.append("'''") + #text = re.sub(r"(['\\\\])", r"\\\1", text) + rexp = Template._quote_rexp + if not rexp: # make re.compile() to be lazy (because it is heavy weight) + rexp = Template._quote_rexp = re.compile(r"(['\\\\])") + text = rexp.sub(r"\\\1", text) + if not encode_newline or text[-1] != "\n": + buf.append(text) + buf.append("''', ") + elif len(text) >= 2 and text[-2] == "\r": + buf.append(text[0:-2]) + buf.append("\\r\\n''', ") + else: + buf.append(text[0:-1]) + buf.append("\\n''', ") + + _add_text = add_text + + def add_expr(self, buf, code, flag_escape=None): + if not code or code.isspace(): + return + if flag_escape is None: + buf.append(code); buf.append(", "); + elif flag_escape is False: + buf.extend((self.tostrfunc, "(", code, "), ")) + else: + buf.extend((self.escapefunc, "(", self.tostrfunc, "(", code, ")), ")) + + def add_stmt(self, buf, code): + if self._stmt_not_added_yet: + # insert dummy if-stmt between buf[-2] and buf[-1] + if buf and buf[-1] != "\n" and buf[-1].isspace(): + buf[-1:-1] = ("if True: ## dummy\n", ) + self._stmt_not_added_yet = False + if self.newline == "\r\n": + code = code.replace("\r\n", "\n") + buf.append(code) + #if code[-1] != '\n': + # buf.append(self.newline) + + def _set_spaces(self, code, lspace, mspace): + if lspace: + if mspace == " ": + code = lspace + code + elif mspace == "\t": + code = lspace + "\t" + code + #i = code.rstrip().rfind("\n") + #if i < 0: # i == -1 + # i = 0 + #else: + # i += 1 + i = code.rstrip().rfind("\n") + 1 + indent = 0 + n = len(code) + ch = None + while i < n: + ch = code[i] + if ch == " ": indent += 1 + elif ch == "\t": indent += 8 + else: break + i += 1 + if ch: + if code.rstrip()[-1] == ':': + indent += self.indent + self._spaces = ' ' * indent + + def render(self, context=None, globals=None, _buf=None): + """Evaluate python code with context dictionary. + If _buf is None then return the result of evaluation as str, + else return None. + + context:dict (=None) + Context object to evaluate. If None then new dict is created. + globals:dict (=None) + Global object. If None then globals() is used. + _buf:list (=None) + If None then new list is created. + """ + if context is None: + locals = context = {} + elif self.args is None: + locals = context.copy() + else: + locals = {} + if '_engine' in context: + context.get('_engine').hook_context(locals) + locals['_context'] = context + if globals is None: + globals = sys._getframe(1).f_globals + bufarg = _buf + if _buf is None: + _buf = [] + locals['_buf'] = _buf + if not self.bytecode: + self.compile() + exec(self.bytecode, globals, locals) + if bufarg is not None: + return bufarg + elif not logger: + return ''.join(_buf) + else: + try: + return ''.join(_buf) + except UnicodeDecodeError: + ex = sys.exc_info()[1] + logger.error("[tenjin.Template] " + str(ex)) + logger.error("[tenjin.Template] (_buf=%s)" % repr(_buf)) + raise + + def compile(self): + """compile self.script into self.bytecode""" + self.bytecode = compile(self.script, self.filename or '(tenjin)', 'exec') + + +## +## preprocessor class +## + +class Preprocessor(Template): + """Template class for preprocessing.""" + + STMT_PATTERN = None + + def stmt_pattern(self): + pat = Preprocessor.STMT_PATTERN + if not pat: # re.compile() is heavy weight, so make it lazy + pat = Preprocessor.STMT_PATTERN = Template.compile_stmt_pattern('PY') + return Preprocessor.STMT_PATTERN + + EXPR_PATTERN = None + + def expr_pattern(self): + pat = Preprocessor.EXPR_PATTERN + if not pat: # re.compile() is heavy weight, so make it lazy + pat = Preprocessor.EXPR_PATTERN = re.compile(r'([#$])\{\{(.*?)\}\}', re.S) + return Preprocessor.EXPR_PATTERN + + #def get_expr_and_escapeflag(self, match): + # return match.group(2), match.group(1) == '$' + + def add_expr(self, buf, code, flag_escape=None): + if not code or code.isspace(): + return + code = "_decode_params(%s)" % code + Template.add_expr(self, buf, code, flag_escape) + + +## +## cache storages +## + +class CacheStorage(object): + """[abstract] Template object cache class (in memory and/or file)""" + + def __init__(self, postfix='.cache'): + self.postfix = postfix + self.items = {} # key: full path, value: template object + + def get(self, fullpath, create_template): + """get template object. if not found, load attributes from cache file and restore template object.""" + template = self.items.get(fullpath) + if not template: + dict = self._load(fullpath) + if dict: + template = create_template() + for k, v in dict.items(): + setattr(template, k, v) + self.items[fullpath] = template + return template + + def set(self, fullpath, template): + """set template object and save template attributes into cache file.""" + self.items[fullpath] = template + dict = self._save_data_of(template) + return self._store(fullpath, dict) + + def _save_data_of(self, template): + return { 'args' : template.args, 'bytecode' : template.bytecode, + 'script': template.script, 'timestamp': template.timestamp } + + def unset(self, fullpath): + """remove template object from dict and cache file.""" + self.items.pop(fullpath, None) + return self._delete(fullpath) + + def clear(self): + """remove all template objects and attributes from dict and cache file.""" + for k, v in self.items.items(): + self._delete(k) + self.items.clear() + + def _load(self, fullpath): + """(abstract) load dict object which represents template object attributes from cache file.""" + raise NotImplementedError.new("%s#_load(): not implemented yet." % self.__class__.__name__) + + def _store(self, fullpath, template): + """(abstract) load dict object which represents template object attributes from cache file.""" + raise NotImplementedError.new("%s#_store(): not implemented yet." % self.__class__.__name__) + + def _delete(self, fullpath): + """(abstract) remove template object from cache file.""" + raise NotImplementedError.new("%s#_delete(): not implemented yet." % self.__class__.__name__) + + def _cachename(self, fullpath): + """change fullpath into cache file path.""" + return fullpath + self.postfix + + +class MemoryCacheStorage(CacheStorage): + + def _load(self, fullpath): + return None + + def _store(self, fullpath, template): + pass + + def _delete(self, fullpath): + pass + + +class FileCacheStorage(CacheStorage): + + def _delete(self, fullpath): + cachepath = self._cachename(fullpath) + if os.path.isfile(cachepath): os.unlink(cachepath) + + +class MarshalCacheStorage(FileCacheStorage): + + def __init__(self, postfix='.cache'): + global marshal + if marshal is None: import marshal + FileCacheStorage.__init__(self, postfix) + + def _load(self, fullpath): + cachepath = self._cachename(fullpath) + if not os.path.isfile(cachepath): return None + if logger: logger.info("[tenjin.MarshalCacheStorage] load cache (file=%s)" % repr(cachepath)) + dump = _read_binary_file(cachepath) + return marshal.loads(dump) + + def _store(self, fullpath, dict): + cachepath = self._cachename(fullpath) + if logger: logger.info("[tenjin.MarshalCacheStorage] store cache (file=%s)" % repr(cachepath)) + _write_binary_file(cachepath, marshal.dumps(dict)) + + +class PickleCacheStorage(FileCacheStorage): + + def __init__(self, postfix='.cache'): + global pickle + if pickle is None: + try: import cPickle as pickle + except: import pickle + FileCacheStorage.__init__(self, postfix) + + def _load(self, fullpath): + cachepath = self._cachename(fullpath) + if not os.path.isfile(cachepath): return None + if logger: logger.info("[tenjin.PickleCacheStorage] load cache (file=%s)" % repr(cachepath)) + dump = _read_binary_file(cachepath) + return pickle.loads(dump) + + def _store(self, fullpath, dict): + if 'bytecode' in dict: dict.pop('bytecode') + cachepath = self._cachename(fullpath) + if logger: logger.info("[tenjin.PickleCacheStorage] store cache (file=%s)" % repr(cachepath)) + _write_binary_file(cachepath, pickle.dumps(dict)) + + +class TextCacheStorage(FileCacheStorage): + + def _load(self, fullpath): + cachepath = self._cachename(fullpath) + if not os.path.isfile(cachepath): return None + if logger: logger.info("[tenjin.TextCacheStorage] load cache (file=%s)" % repr(cachepath)) + s = _read_binary_file(cachepath) + if python2: + header, script = s.split("\n\n", 1) + elif python3: + header, script = s.split("\n\n".encode('ascii'), 1) + header = header.decode('ascii') + timestamp = encoding = args = None + for line in header.split("\n"): + key, val = line.split(": ", 1) + if key == 'timestamp': timestamp = float(val) + elif key == 'encoding': encoding = val + elif key == 'args': args = val.split(', ') + if python2: + if encoding: script = script.decode(encoding) ## binary(=str) to unicode + elif python3: + script = script.decode(encoding or 'utf-8') ## binary to unicode(=str) + return {'args': args, 'script': script, 'timestamp': timestamp} + + def _store(self, fullpath, dict): + s = dict['script'] + if python2: + if dict.get('encoding') and isinstance(s, unicode): + s = s.encode(dict['encoding']) ## unicode to binary(=str) + sb = [] + sb.append("timestamp: %s\n" % dict['timestamp']) + if dict.get('encoding'): + sb.append("encoding: %s\n" % dict['encoding']) + if dict.get('args') is not None: + sb.append("args: %s\n" % ', '.join(dict['args'])) + sb.append("\n") + sb.append(s) + s = ''.join(sb) + if python3: + if isinstance(s, str): + s = s.encode(dict.get('encoding') or 'utf-8') ## unicode(=str) to binary + cachepath = self._cachename(fullpath) + if logger: logger.info("[tenjin.TextCacheStorage] store cache (file=%s)" % repr(cachepath)) + _write_binary_file(cachepath, s) + + def _save_data_of(self, template): + dict = FileCacheStorage._save_data_of(self, template) + dict['encoding'] = template.encoding + return dict + + +class GaeMemcacheCacheStorage(CacheStorage): + + lifetime = 0 # 0 means unlimited + + def __init__(self, lifetime=None, postfix='.cache'): + CacheStorage.__init__(self, postfix) + if lifetime is not None: self.lifetime = lifetime + global memcache + if memcache is None: from google.appengine.api import memcache + + def _load(self, fullpath): + key = self._cachename(fullpath) + if logger: logger.info("[tenjin.GaeMemcacheCacheStorage] load cache (key=%s)" % repr(key)) + return memcache.get(key) + + def _store(self, fullpath, dict): + if 'bytecode' in dict: dict.pop('bytecode') + key = self._cachename(fullpath) + if logger: logger.info("[tenjin.GaeMemcacheCacheStorage] store cache (key=%s)" % repr(key)) + ret = memcache.set(key, dict, self.lifetime) + if not ret: + if logger: logger.info("[tenjin.GaeMemcacheCacheStorage: failed to store cache (key=%s)" % repr(key)) + + def _delete(self, fullpath): + memcache.delete(self._cachename(fullpath)) + + + +## +## abstract class for data cache +## +class DataCache(object): + + def get(self, cache_key, *options): + raise NotImplementedError("%s.get(): not implemented yet." % self.__class__.__name__) + + def set(self, cache_key, data, *options): + raise NotImplementedError("%s.set(): not implemented yet." % self.__class__.__name__) + + def delete(self, cache_key, *options): + raise NotImplementedError("%s.del(): not implemented yet." % self.__class__.__name__) + + def has(self, cache_key, *options): + raise NotImplementedError("%s.has(): not implemented yet." % self.__class__.__name__) + + +## +## file base data cache +## +class FileBaseDataCache(DataCache): + + def __init__(self, root_path): + if not os.path.isdir(root_path): + raise ArgumentError("%s: directory not found." % root_path) + self.root_path = root_path + + _pat = re.compile(r'[^-\/\w]') + + def filepath(self, cache_key, _pat1=_pat): + return os.path.join(self.root_path, _pat1.sub('_', cache_key)) + + def get(self, cache_key, lifetime=0): + fpath = self.filepath(cache_key) + if not os.path.isfile(fpath): + return + if lifetime and os.path.getmtime(fpath) + lifetime <= time.time(): + return + return _read_binary_file(fpath) + + def set(self, cache_key, data, lifetime=0): + fpath = self.filepath(cache_key) + dirname = os.path.dirname(fpath) + if not os.path.isdir(dirname): + os.makedirs(dirname) + _write_binary_file(fpath, data) + + def delete(self, cache_key, lifetime=0): + fpath = self.filepath(cache_key) + if os.path.isfile(fpath): + os.unlink(fpath) + return True + return False + + def has(self, cache_key, lifetime=0): + fpath = self.filepath(cache_key) + if not os.path.isfile(fpath): return False + if linetime: return os.path.getmtime(fpath) + lifetime <= time.time() + return True + + + +## +## template engine class +## + +class Engine(object): + """Template Engine class. + See User's Guide, FAQ, and examples for details. + http://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html + http://www.kuwata-lab.com/tenjin/pytenjin-faq.html + http://www.kuwata-lab.com/tenjin/pytenjin-examples.html + """ + + ## default value of attributes + prefix = '' + postfix = '' + layout = None + templateclass = Template + path = None + cache = None + datacache = None + preprocess = False + timestamp_interval = 5 # seconds + _cache_storage_classes = { + 'marshal': MarshalCacheStorage, + 'pickle' : PickleCacheStorage, + 'text' : TextCacheStorage, + } + + def __init__(self, prefix=None, postfix=None, layout=None, path=None, cache=True, datacache=None, preprocess=None, templateclass=None, **kwargs): + """Initializer of Engine class. + + prefix:str (='') + Prefix string used to convert template short name to template filename. + postfix:str (='') + Postfix string used to convert template short name to template filename. + layout:str (=None) + Default layout template name. + path:list of str(=None) + List of directory names which contain template files. + cache:bool or 'text' (=True) + Cache converted python code into file. + If True, marshal-base cache files are created. + If 'text', text-base cache files are created. + If False, no cache files are created. + preprocess:bool(=False) + Activate preprocessing or not. + templateclass:class (=Template) + Template class which engine creates automatically. + kwargs:dict + Options for Template class constructor. + See document of Template.__init__() for details. + """ + if prefix: self.prefix = prefix + if postfix: self.postfix = postfix + if layout: self.layout = layout + if templateclass: self.templateclass = templateclass + if path is not None: self.path = path + if datacache: self.datacache = datacache + if preprocess is not None: self.preprocess = preprocess + self.kwargs = kwargs + self.encoding = kwargs.get('encoding') + self._filepaths = {} # template_name => relative path and absolute path + #self.cache = cache + self._set_cache_storage(cache) + + def _set_cache_storage(self, cache): + if cache is True: self.cache = MarshalCacheStorage() + elif cache is None: self.cache = MemoryCacheStorage() + elif cache is False: self.cache = None + elif isinstance(cache, CacheStorage): self.cache = cache + elif self._cache_storage_classes.get(cache): + self.cache = self._cache_storage_classes[cache]() + else: + raise ValueError("%s: invalid cache object." % repr(cache)) + + def to_filename(self, template_name): + """Convert template short name to filename. + ex. + >>> engine = tenjin.Engine(prefix='user_', postfix='.pyhtml') + >>> engine.to_filename('list') + 'list' + >>> engine.to_filename(':list') + 'user_list.pyhtml' + """ + if template_name[0] == ':' : + return self.prefix + template_name[1:] + self.postfix + return template_name + + def _relative_and_absolute_path(self, template_name): + pair = self._filepaths.get(template_name) + if pair: return pair + filename = self.to_filename(template_name) + filepath = self._find_file(filename) + if not filepath: + raise IOError('%s: filename not found (path=%s).' % (filename, repr(self.path))) + fullpath = os.path.abspath(filepath) + self._filepaths[template_name] = pair = (filepath, fullpath) + return pair + + def _find_file(self, filename): + if self.path: + for dirname in self.path: + filepath = os.path.join(dirname, filename) + if os.path.isfile(filepath): + return filepath + else: + if os.path.isfile(filename): + return filename + return None + + def _create_template(self, filepath, _context, _globals): + if filepath and self.preprocess: + s = self._preprocess(filepath, _context, _globals) + template = self.templateclass(None, **self.kwargs) + template.convert(s, filepath) + else: + template = self.templateclass(filepath, **self.kwargs) + return template + + def _preprocess(self, filepath, _context, _globals): + #if _context is None: _context = {} + #if _globals is None: _globals = sys._getframe(3).f_globals + if '_engine' not in _context: + self.hook_context(_context) + preprocessor = Preprocessor(filepath) + return preprocessor.render(_context, globals=_globals) + + def get_template(self, template_name, _context=None, _globals=None): + """Return template object. + If template object has not registered, template engine creates + and registers template object automatically. + """ + filename, fullpath = self._relative_and_absolute_path(template_name) + assert filename and fullpath + cache = self.cache + template = cache and cache.get(fullpath, self.templateclass) or None + mtime = None + now = time.time() + if template: + assert template.timestamp is not None + if now > getattr(template, '_last_checked_at', 0) + self.timestamp_interval: + mtime = os.path.getmtime(filename) + if template.timestamp != mtime: + #if cache: cache.delete(path) + template = None + if logger: logger.info("[tenjin.Engine] cache is old (filename=%s, template=%s)" % (repr(filename), repr(template))) + if not template: + if not mtime: mtime = os.path.getmtime(filename) + if self.preprocess: ## required for preprocess + if _context is None: _context = {} + if _globals is None: _globals = sys._getframe(1).f_globals + template = self._create_template(filename, _context, _globals) + template.timestamp = mtime + template._last_checked_at = now + if cache: + if not template.bytecode: template.compile() + cache.set(fullpath, template) + #else: + # template.compile() + return template + + def include(self, template_name, append_to_buf=True): + """Evaluate template using current local variables as context. + + template_name:str + Filename (ex. 'user_list.pyhtml') or short name (ex. ':list') of template. + append_to_buf:boolean (=True) + If True then append output into _buf and return None, + else return stirng output. + + ex. + + #{include('file.pyhtml', False)} + + """ + frame = sys._getframe(1) + locals = frame.f_locals + globals = frame.f_globals + assert '_context' in locals + context = locals['_context'] + # context and globals are passed to get_template() only for preprocessing. + template = self.get_template(template_name, context, globals) + if append_to_buf: _buf = locals['_buf'] + else: _buf = None + return template.render(context, globals, _buf=_buf) + + def render(self, template_name, context=None, context_block=None, globals=None, layout=True): + """Evaluate template with layout file and return result of evaluation. + + template_name:str + Filename (ex. 'user_list.pyhtml') or short name (ex. ':list') of template. + context:dict (=None) + Context object to evaluate. If None then new dict is used. + globals:dict (=None) + Global context to evaluate. If None then globals() is used. + layout:str or Bool(=True) + If True, the default layout name specified in constructor is used. + If False, no layout template is used. + If str, it is regarded as layout template name. + + If temlate object related with the 'template_name' argument is not exist, + engine generates a template object and register it automatically. + """ + if context is None: + context = {} + if globals is None: + globals = sys._getframe(1).f_globals + self.hook_context(context) + context['_context_block'] = context_block + while True: + # context and globals are passed to get_template() only for preprocessing + template = self.get_template(template_name, context, globals) + content = template.render(context, globals) + layout = context.pop('_layout', layout) + if layout is True or layout is None: + layout = self.layout + if not layout: + break + template_name = layout + layout = False + context['_content'] = content + context.pop('_content', None) + return content + + def hook_context(self, context): + context['_engine'] = self + #context['render'] = self.render + context['include'] = self.include + context['_datacache'] = self.datacache diff --git a/website/myapp.py b/website/myapp.py new file mode 100644 index 0000000..e710e41 --- /dev/null +++ b/website/myapp.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +### +### $Release: $ +### $Copyright$ +### $License$ +### + +from __future__ import with_statement + +from google.appengine.api import users +import apptenjin + + +## +## You can add your own functions available in templates +## +escape = apptenjin.GLOBAL['escape'] + +def js_link(label, js): + return '%s' % (escape(js), label) +apptenjin.GLOBAL['js_link'] = js_link + +def url_link(label, url): + if url: return '%s' % (escape(url), label) + else: return '%s' % label +apptenjin.GLOBAL['url_link'] = url_link + + +## +## You can add helper methods to handler class +## +class MyRequestHandler(apptenjin.DefaultRequestHandler): + """you can customize this class""" + + #def new_context(self): + # context = apptenjin.DefaultRequestHandler.new_context(self) + # context.site_title = 'Your Site Title' + # return context + + def current_lang(self): + lang = getattr(self, '_lang', None) + if not lang: + lang = self._current_lang() + self._lang = lang + return lang + + def _current_lang(self): + lang = self.request.method == 'GET' and self.request.GET.get('_lang') + if lang and lang.isalpha(): + self.response.headers['Set-Cookie'] = '_lang=%s; path=/' % lang + return lang + lang = self.request.cookies.get('_lang') + if lang: + return lang + langs = self.request.accept_language.best_matches() + if langs: + return langs[0] + return 'en' + + def current_user(self): + return users.get_current_user() + + def current_user_name(self): + user = users.get_current_user() + return (user.nickname if user else None) diff --git a/website/tmpl/404.html.pyt b/website/tmpl/404.html.pyt new file mode 100644 index 0000000..04fce8e --- /dev/null +++ b/website/tmpl/404.html.pyt @@ -0,0 +1,14 @@ + + + + + + + 404 Not Found + + + +

404 Not Found

+

${message}

+ + diff --git a/website/tmpl/_layout.html.pyt b/website/tmpl/_layout.html.pyt new file mode 100644 index 0000000..a76c5c3 --- /dev/null +++ b/website/tmpl/_layout.html.pyt @@ -0,0 +1,94 @@ + + + + + + + VersionSwitcher - ${self.context.get('page_title')} + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+ + +
+#{_content} +
 
+
+ + + + + +
 
+
+
+
+ + +
+ + + + + + + diff --git a/website/tmpl/_sidebar.html.pyt b/website/tmpl/_sidebar.html.pyt new file mode 100644 index 0000000..61f853c --- /dev/null +++ b/website/tmpl/_sidebar.html.pyt @@ -0,0 +1,57 @@ + +
  • + +
     
    +
  • + + +
  • +

    Aliquam tempus

    +

    Mauris vitae nisl nec metus placerat perdiet est. Phasellus dapibus semper consectetuer hendrerit.

    +
  • + + +
  • +

    Categories

    + +
  • + + +
  • +

    Blogroll

    + +
  • + + +
  • +

    Archives

    + +