From ff1ffc407d6239ddb49bcab8d890c0e25493f6cc Mon Sep 17 00:00:00 2001 From: Wanja Date: Fri, 6 Apr 2012 01:54:53 +0200 Subject: [PATCH] * Got rid of jo framework * Using jqMobi as a javascript framework * Using jqMobi-Ui as ui framework * Implemented basic minimax AI * Implemented minimalistic player pairing server for node.js --- AndroidManifest.xml | 50 +- assets/www/css/icons.css | 78 + assets/www/css/icons.svg | 68 + assets/www/css/icons.ttf | Bin 0 -> 10028 bytes assets/www/css/images/Off-On_Slider.png | Bin 0 -> 1560 bytes assets/www/css/images/ajax-loader.png | Bin 0 -> 503 bytes assets/www/css/images/back.png | Bin 0 -> 2327 bytes assets/www/css/images/background.jpg | Bin 0 -> 3709 bytes assets/www/css/images/badge.png | Bin 0 -> 4380 bytes assets/www/css/images/carbon/ajax-loader.png | Bin 0 -> 503 bytes assets/www/css/images/carbon/bg-carbon.jpg | Bin 0 -> 990 bytes assets/www/css/images/carbon/close.png | Bin 0 -> 1492 bytes assets/www/css/images/carbon/collapse.png | Bin 0 -> 766 bytes .../www/css/images/carbon/custom_inputs.png | Bin 0 -> 6432 bytes assets/www/css/images/carbon/expand.png | Bin 0 -> 759 bytes assets/www/css/images/carbon/nav_icons.png | Bin 0 -> 3131 bytes assets/www/css/images/carbon/splash.png | Bin 0 -> 11191 bytes assets/www/css/images/carbon/vert_divider.png | Bin 0 -> 182 bytes assets/www/css/images/close.png | Bin 0 -> 1492 bytes assets/www/css/images/collapse.png | Bin 0 -> 766 bytes assets/www/css/images/custom_inputs.png | Bin 0 -> 6432 bytes assets/www/css/images/eco/ajax-loader.png | Bin 0 -> 697 bytes assets/www/css/images/eco/close.png | Bin 0 -> 1492 bytes assets/www/css/images/eco/collapse.png | Bin 0 -> 766 bytes assets/www/css/images/eco/custom_inputs.png | Bin 0 -> 7170 bytes assets/www/css/images/eco/expand.png | Bin 0 -> 759 bytes assets/www/css/images/eco/nav_icons.png | Bin 0 -> 3578 bytes assets/www/css/images/eco/splash.png | Bin 0 -> 12980 bytes assets/www/css/images/expand.png | Bin 0 -> 759 bytes assets/www/css/images/frosty/ajax-loader.png | Bin 0 -> 708 bytes assets/www/css/images/frosty/close.png | Bin 0 -> 1492 bytes assets/www/css/images/frosty/collapse.png | Bin 0 -> 766 bytes .../www/css/images/frosty/custom_inputs.png | Bin 0 -> 7294 bytes assets/www/css/images/frosty/expand.png | Bin 0 -> 759 bytes assets/www/css/images/frosty/nav_icons.png | Bin 0 -> 3345 bytes assets/www/css/images/frosty/splash.png | Bin 0 -> 12298 bytes assets/www/css/images/horz_border.png | Bin 0 -> 266 bytes assets/www/css/images/listGroup.png | Bin 0 -> 2867 bytes assets/www/css/images/magnifier.png | Bin 0 -> 844 bytes assets/www/css/images/nav_buttons.png | Bin 0 -> 15343 bytes assets/www/css/images/nav_icons.png | Bin 0 -> 3131 bytes assets/www/css/images/slate/ajax-loader.png | Bin 0 -> 503 bytes assets/www/css/images/slate/close.png | Bin 0 -> 1492 bytes assets/www/css/images/slate/collapse.png | Bin 0 -> 766 bytes assets/www/css/images/slate/custom_inputs.png | Bin 0 -> 6432 bytes assets/www/css/images/slate/expand.png | Bin 0 -> 759 bytes assets/www/css/images/slate/nav_icons.png | Bin 0 -> 3131 bytes assets/www/css/images/slate/splash.png | Bin 0 -> 11191 bytes assets/www/css/images/slate/stripe_bg.png | Bin 0 -> 173 bytes assets/www/css/images/slate/vert_divider.png | Bin 0 -> 182 bytes assets/www/css/images/splash.png | Bin 0 -> 11191 bytes assets/www/css/images/stripe_bg.png | Bin 0 -> 173 bytes assets/www/css/images/vert_divider.png | Bin 0 -> 182 bytes assets/www/css/images/volcano/ajax-loader.png | Bin 0 -> 503 bytes assets/www/css/images/volcano/close.png | Bin 0 -> 1492 bytes assets/www/css/images/volcano/collapse.png | Bin 0 -> 766 bytes .../www/css/images/volcano/custom_inputs.png | Bin 0 -> 6432 bytes assets/www/css/images/volcano/expand.png | Bin 0 -> 759 bytes assets/www/css/images/volcano/nav_icons.png | Bin 0 -> 3131 bytes assets/www/css/images/volcano/splash.png | Bin 0 -> 11191 bytes assets/www/css/jo/back-icon.png | Bin 176 -> 0 bytes assets/www/css/jo/back-line.png | Bin 203 -> 0 bytes assets/www/css/jo/back-mini.png | Bin 176 -> 0 bytes assets/www/css/jo/bright-shiny.png | Bin 486 -> 0 bytes assets/www/css/jo/browser.css | 47 - assets/www/css/jo/dark_matte.png | Bin 365 -> 0 bytes assets/www/css/jo/expando.png | Bin 601 -> 0 bytes assets/www/css/jo/full-matte.png | Bin 772 -> 0 bytes assets/www/css/jo/jo.css | 1031 --- assets/www/css/jo/lite-matte.png | Bin 567 -> 0 bytes assets/www/css/jo/sample.html | 53 - assets/www/css/jo/shade-top.png | Bin 382 -> 0 bytes assets/www/css/jo/shade.png | Bin 270 -> 0 bytes assets/www/css/jo/shiny-button.png | Bin 762 -> 0 bytes assets/www/css/jo/shiny.png | Bin 480 -> 0 bytes assets/www/css/jo/subtle-matte-full.png | Bin 784 -> 0 bytes assets/www/css/jo/subtle-shiny-button.png | Bin 779 -> 0 bytes assets/www/css/jo/subtle-shiny.png | Bin 478 -> 0 bytes assets/www/css/jo/webos.css | 34 - assets/www/css/jq.ui.css | 1017 +++ assets/www/css/ui.css | 75 +- assets/www/index.html | 86 +- assets/www/js/jo.js | 6146 ----------------- assets/www/js/jo_min.js | 379 - assets/www/js/jq.mobi.js | 1895 +++++ assets/www/js/jq.mobi.min.js | 31 + assets/www/js/jq.ui.js | 1894 +++++ assets/www/js/jq.ui.min.js | 127 + assets/www/js/net.js | 7 +- assets/www/js/tafl.js | 490 +- assets/www/js/touch.js | 92 + assets/www/js/ui.js | 415 +- server/tafl_server.js | 26 +- 93 files changed, 5964 insertions(+), 8077 deletions(-) create mode 100644 assets/www/css/icons.css create mode 100644 assets/www/css/icons.svg create mode 100644 assets/www/css/icons.ttf create mode 100644 assets/www/css/images/Off-On_Slider.png create mode 100644 assets/www/css/images/ajax-loader.png create mode 100644 assets/www/css/images/back.png create mode 100644 assets/www/css/images/background.jpg create mode 100644 assets/www/css/images/badge.png create mode 100644 assets/www/css/images/carbon/ajax-loader.png create mode 100644 assets/www/css/images/carbon/bg-carbon.jpg create mode 100644 assets/www/css/images/carbon/close.png create mode 100644 assets/www/css/images/carbon/collapse.png create mode 100644 assets/www/css/images/carbon/custom_inputs.png create mode 100644 assets/www/css/images/carbon/expand.png create mode 100644 assets/www/css/images/carbon/nav_icons.png create mode 100644 assets/www/css/images/carbon/splash.png create mode 100644 assets/www/css/images/carbon/vert_divider.png create mode 100644 assets/www/css/images/close.png create mode 100644 assets/www/css/images/collapse.png create mode 100644 assets/www/css/images/custom_inputs.png create mode 100644 assets/www/css/images/eco/ajax-loader.png create mode 100644 assets/www/css/images/eco/close.png create mode 100644 assets/www/css/images/eco/collapse.png create mode 100644 assets/www/css/images/eco/custom_inputs.png create mode 100644 assets/www/css/images/eco/expand.png create mode 100644 assets/www/css/images/eco/nav_icons.png create mode 100644 assets/www/css/images/eco/splash.png create mode 100644 assets/www/css/images/expand.png create mode 100644 assets/www/css/images/frosty/ajax-loader.png create mode 100644 assets/www/css/images/frosty/close.png create mode 100644 assets/www/css/images/frosty/collapse.png create mode 100644 assets/www/css/images/frosty/custom_inputs.png create mode 100644 assets/www/css/images/frosty/expand.png create mode 100644 assets/www/css/images/frosty/nav_icons.png create mode 100644 assets/www/css/images/frosty/splash.png create mode 100644 assets/www/css/images/horz_border.png create mode 100644 assets/www/css/images/listGroup.png create mode 100644 assets/www/css/images/magnifier.png create mode 100644 assets/www/css/images/nav_buttons.png create mode 100644 assets/www/css/images/nav_icons.png create mode 100644 assets/www/css/images/slate/ajax-loader.png create mode 100644 assets/www/css/images/slate/close.png create mode 100644 assets/www/css/images/slate/collapse.png create mode 100644 assets/www/css/images/slate/custom_inputs.png create mode 100644 assets/www/css/images/slate/expand.png create mode 100644 assets/www/css/images/slate/nav_icons.png create mode 100644 assets/www/css/images/slate/splash.png create mode 100644 assets/www/css/images/slate/stripe_bg.png create mode 100644 assets/www/css/images/slate/vert_divider.png create mode 100644 assets/www/css/images/splash.png create mode 100644 assets/www/css/images/stripe_bg.png create mode 100644 assets/www/css/images/vert_divider.png create mode 100644 assets/www/css/images/volcano/ajax-loader.png create mode 100644 assets/www/css/images/volcano/close.png create mode 100644 assets/www/css/images/volcano/collapse.png create mode 100644 assets/www/css/images/volcano/custom_inputs.png create mode 100644 assets/www/css/images/volcano/expand.png create mode 100644 assets/www/css/images/volcano/nav_icons.png create mode 100644 assets/www/css/images/volcano/splash.png delete mode 100644 assets/www/css/jo/back-icon.png delete mode 100644 assets/www/css/jo/back-line.png delete mode 100644 assets/www/css/jo/back-mini.png delete mode 100644 assets/www/css/jo/bright-shiny.png delete mode 100644 assets/www/css/jo/browser.css delete mode 100644 assets/www/css/jo/dark_matte.png delete mode 100644 assets/www/css/jo/expando.png delete mode 100644 assets/www/css/jo/full-matte.png delete mode 100644 assets/www/css/jo/jo.css delete mode 100644 assets/www/css/jo/lite-matte.png delete mode 100644 assets/www/css/jo/sample.html delete mode 100644 assets/www/css/jo/shade-top.png delete mode 100644 assets/www/css/jo/shade.png delete mode 100644 assets/www/css/jo/shiny-button.png delete mode 100644 assets/www/css/jo/shiny.png delete mode 100644 assets/www/css/jo/subtle-matte-full.png delete mode 100644 assets/www/css/jo/subtle-shiny-button.png delete mode 100644 assets/www/css/jo/subtle-shiny.png delete mode 100644 assets/www/css/jo/webos.css create mode 100644 assets/www/css/jq.ui.css delete mode 100644 assets/www/js/jo.js delete mode 100644 assets/www/js/jo_min.js create mode 100644 assets/www/js/jq.mobi.js create mode 100644 assets/www/js/jq.mobi.min.js create mode 100644 assets/www/js/jq.ui.js create mode 100644 assets/www/js/jq.ui.min.js create mode 100644 assets/www/js/touch.js diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 482c8d0..4411546 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4,31 +4,32 @@ android:versionCode="1" android:versionName="1.0" > - + + - - - - - - - - - - - - - - - - + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" + android:resizeable="true" + android:anyDensity="true" /> + + + + + + + + + + + + + + + + + + android:configChanges="orientation|keyboardHidden" + android:screenOrientation="portrait" > diff --git a/assets/www/css/icons.css b/assets/www/css/icons.css new file mode 100644 index 0000000..2377597 --- /dev/null +++ b/assets/www/css/icons.css @@ -0,0 +1,78 @@ +@font-face { + font-family: 'IcoMoon'; + src: + url('icons.ttf') format('truetype'), + url('icons.svg#IcoMoon') format('svg'); + font-weight: normal; + font-style: normal; + +} +.icon:before { +color:inherit;font-weight:normal !important;font-family:'IcoMoon'; +padding:5px;float:left; +font-size: 28px; +text-align: center; +vertical-align:middle; +line-height:auto; +} +.icon.mini:before{font-size:20px !important;height:16px !important;width:16px !important;line-height:16px !important;} +.icon.big:before{font-size:40px !important;height:32px !important;width:32px !important;line-height:32px !important;} + + +.icon.home:before{content:'!';} +.icon.pencil:before{content:'\22';} +.icon.picture:before{content:'#';} +.icon.camera:before{content:'$';} +.icon.headset:before{content:'%';} +.icon.paper:before{content:'&';} +.icon.stack:before{content:'\27';} +.icon.folder:before{content:'(';} +.icon.tag:before{content:')';} +.icon.basket:before{content:'*';} +.icon.phone:before{content:'+';} +.icon.mail:before{content:',';} +.icon.location:before{content:'-';} +.icon.clock:before{content:'.';} +.icon.calendar:before{content:'/';} +.icon.message:before{content:'0';} +.icon.chat:before{content:'1';} +.icon.user:before{content:'2';} +.icon.loading:before{content:'3';} +.icon.refresh:before{content:'4';} +.icon.magnifier:before{content:'5';} +.icon.key:before{content:'6';} +.icon.settings:before{content:'7';} +.icon.graph:before{content:'8';} +.icon.trash:before{content:'9';} +.icon.pin:before{content:':';} +.icon.target:before{content:';';} +.icon.download:before{content:'<';} +.icon.upload:before{content:'=';} +.icon.star:before{content:'>';} +.icon.heart:before{content:'?';} +.icon.warning:before{content:'@';} +.icon.add:before{content:'A';} +.icon.remove:before{content:'B';} +.icon.question:before{content:'C';} +.icon.info:before{content:'D';} +.icon.error:before{content:'E';} +.icon.check:before{content:'F';} +.icon.minimize:before{content:'G';} +.icon.close:before{content:'H';} +.icon.up:before{content:'I';} +.icon.down:before{content:'J';} +.icon.left:before{content:'K';} +.icon.right:before{content:'L';} +.icon.tools:before{content:'M';} +.icon.html5:before{content:'N';} +.icon.css:before{content:'O';} +.icon.js:before{content:'P';} +.icon.cloud:before{content:'Q';} +.icon.tv:before{content:'R';} +.icon.wifi:before{content:'S';} +.icon.new:before{content:'T';} +.icon.mic:before{content:'U';} +.icon.database:before{content:'V';} +.icon.busy:before{content:'W';} +.icon.bug:before{content:'X';} +.icon.lamp:before{content:'Y';} diff --git a/assets/www/css/icons.svg b/assets/www/css/icons.svg new file mode 100644 index 0000000..35b1133 --- /dev/null +++ b/assets/www/css/icons.svg @@ -0,0 +1,68 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/www/css/icons.ttf b/assets/www/css/icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..42c6b79bc8841d3efd53723737c1e02dc0c14b1a GIT binary patch literal 10028 zcmd6NdAuCOeP>s7_jLChJ<~nY({s=C+;3*y%)B|Dq<2UHJ#?T03G|+X(1DOSBq0Kb zLkXBjio#)HdCi)`jtm&GaR?B}Hjd5ndif;5n>g(9as0{3;E%817|5Dz;w4#j{hIw& z&paU%^GWu#us@PrlD_r2wdjOD)wNVZu-6HKf96R=yK53?!E2S3}?|hIgbAX zo(tFRx#gz!-$%<_@lBNFYY*J{G5v3%AIBpvn)~DH_U*ame|`Pz-{9GQ$FFxC2;z6> zOK2;Pa{ju5x8C8d3HPD=pHRN%z@fc+-g^5-tI_^Rl#d+TbH`2G103EbpmCWS_Z-}} z;d{4Sk5~T`v=cWSy5&~>hx{@0e<_|n#<8*D`kDR0Kkf2Qyvuob#uxYPUt9acVhVma zI#1_OmO09(xbU7s*-qyN&lg)7JjPvcC==Wn&ganBo5Z0l=jslH(0Ou_6FHgQ@3i%a zK}vqajS;SO+DGl^J#TWboP&t~$tY zA*2uC*BX{pq+6YQl}m!753cj`1L_Ix1otF995=!(!DsP0h0Gj&58#LOb$*E<-xs6A zxJzJG%1B+#?64Wx#%2$f#nShG^1(O>WNdNJY&Oo#yi*-F_K+7umn180kJqRA1DYNT z8DTRLjl~nmR61j2bNNECR352RYoql>bF9_wbbI~rB}{>g*k^;(S(fH*;Sm z0dkzYOcQj9?xugiclho6BmA4fZNgK+55$1@3GpvoVb@mIPo$7^xpcSmr?MJIyn)$jE0T7y0GH87$e)TW!tK zN^LD?5#`NqeTzP}@G*-*MA&N1Vn^+Rq+xF;Bv%k~Vwcr!8)PThxyJrG`|s9JU)5e$ zqx9xW}h>mNTD3XO=1e!~hTj5kV;175LR>pd0;bMAn;j?t# z^__galPB$wXtJ>K>J?V3STDxZfJ#I=O@3UpZ+kZ%jTH*9=#Ymh=Hw(7#10(gCLJCO zE#%5s=u+$SmXW@dCb?2hsuHbjajg^$sWe-sohhP&!%m%MRT@eWdQy+Y^o7rAv6%Ll zL8Qc|znb`l@l8VLHTEB@Ufztyo685unqD&>3gw$f3?om+qq@CTi^nzcf*zfltIo~Y zYsvlRYO|N@w~QRCK6ooPSP$SB*2B-Gxs4nb?1dTE4N@nQq|@tLDUu{1m!G(tQJDRZ zOG*&~cP&NCUU$H@5i+MpU#k;LZlB$!D8}~{MOBrw+vjtWlkA>m<*#S~UZ`XhO>);a z-f+ha8|!XKQ?eC-4`}^e_nf?ESD!S9&uuR#J|fV7`yF?H4ika+6vDfGq+VW|^+bJ* z)dAuYJ#uzr0_~u=iIJ@A5$#_DRyTZ6Pj+p2&Bu53`@234=?Bu$W5ClOq@;q?!643C zowfyuk&L$4DVB2GA{Nbs$6=6rUAo_`Mm5{5MWY(oq*`wO?Lk7Ga>@1|xn$Wz{>dfN zq9>!;6Ivvq(Hpdg&mFm=%)5lActu?JjHoCM3=0gu>8HR$j;nE9#u4Onk&8)^%NCs* zlNQELlC;^az)D^0Ez_YRM_UoaXC+D~Y)Q9WK^5<_+bS;k%)*NMx1YGiv%BZB=D zIfLI0>P6*sOr#nSy%yEUm-TpDA4R^VM@H?nTgg?yC{nV+-T-lS_V^me#VwqhgXKU> zJ|tPWF5hWSleTG@^hE&K!pCr{#($=fS(&^=?7fgt^1EbgTLTk3Xk(yx1xOc1G=-YI zPN|rcT%mA_&f6R9cgg85=(^U_v}_ZEckPYDLr#Ak1V+sQT+mJgd^`nhHOh@Svo^`C z;8t;K(4vv!Ek4(6_t~b#WI-%3i8;wCWm#sCD4Al;0FGgKKO&{``R2kqjnZdtm!p~< z#r#lmJ!xK#0kM)6m2bDdIDzXhyWMs!NB(@9{rhBY+oK*Y>fufD+rLd?tOZSVKe})e z2_5Wz_=fTDOn=iaV$W4Eo@v-N*gK|5Bz)_9315_lBv&|1LMC<^zG0rhV;Yhx;Wq$F zlRO*t$WOcTMyTvhZn$DgHrFf1Wg7CSY0vG7EXjgMh*FLfroG-L?kLCs@k*Y`Zj)Pv-RkQwL&QqYJf75bu8AYy@hgL{ABV($I-$<9e)EnG}2 za>D+q{Z(?=PwfNbp`Sj#z5nd9gn##4CO5EcXc_w?%XTV5$|N6h87eQiq#~Q2PH~b4 zY=&FCPBH5Wk9rd+6=t_> zn=NeGur>_>FDjL_y#-WjGlLUrgA3zeIFZBZV27rt0iezSsTr(&2&##RH5&+8zuPvj zHc);8#96Z%Kn6VX9NE9AUf;C;$bJS;WQ%;am<*a3O&xQI3Y|xd)#tyl@Efe|4eXU0 z@FuB`8Cp1F24d1nFYyAKSJ-@xof&oPw~M$-xOKqoE!-@3CAW*)%U#QO25eZ6{$&i0 z9UvK|0^qDu!Zu-0<1#`QmZ2ht8jG~M*eioy=_B%Vw-#Oa{fKt8SNHwQr+e*hWy2a$ zos49|NPL=oE1ipK3;#!pkbn0%=X&k|=lbTLM6XU6Yt|U4x#*2e-}MHvU>N8yH}4y8Y5{bxgsYUvO2bWL$$wqwS$KP z{jPH*XvwYv){8jJ=B&WxIpnGmqXdl)8E^F_$=oP|TU~V60I+^tAe9kexD6MXq_pbUr_QMKiy0yz1Mc zPi(ldb$-@k`m5G-BJY>8t!0zr!*r?W<8y-G`e#3lwhE}YHX32C6t`?z9d88M zOQ**3D&AN+IbKi)zOfr~at1s>#-U?i<*B43;7qQLlkm>>o*` z(hqB+0WCAC!4q)g-G%;N;MQ{6(Z5S#O0k5*g?lbr0{HifB?&XsFE$7SbDH$}Oj(-4 zgmGrVkuasjBu!uxNWzEg>tnJ$;zXglC=y4Dn1s*8Nlt4Ab9|e}gDS}ybpM3C zbE0E>GpZlgqu(^_U4>5J^wOoD>l8Xa{ETv3q2YzMX?QNjfG#&@Z*jV1UE@c^oGhb% z9vpD=tLR^J#t4g3YLGtPH(}?9!BU|lnY4TK>K#{^fAY|oPg}R!|NEu8$ZY&@oGv|T z?%1){e)FL(efmG#_7ZX3wIg;oh9(_(8t8IAP;F5o3_Pyj6yJy82wTF)b)h2KMlQvh z-S#>4Fwhb|=cYTYYmYQiIrpm1fk=IHLBE*rn5jl@K74pz+;Q}paj%zj zi)O)Rt}IzgYcbnCzIDe|@*myHE758uc*`wDz8O!TF07kkHyB7*(drWWnWN7>>&(es z@UiUzJ}?W65h#;o%M1}EP3X> z)`R2^Pc1zB@WLtAd+*sVKYg9>@%F|&k~#?zzgg1R1Dr$u_3bE%~();HNJ)k2|~h7PW;Td_1*+|sApBO~LP%F3m^ zMCQ_FYkfA^U9zH-8D|DZu{gp+)Y%6k*a!cf^T20<7(+9#2tuDgiArX>&%=VN6BxbW zHa(-q3JYfnu^=f1JNj17q_+~wkaWk a_wG8Dlna};hKiD@}u~;_SK_uuydm@f9 zqR|C=ik($oHoZp9OZCm_L_pY=_uK95I|L@M$gk6KjyTO{gr z>yj#Y+yk@yysiIddDalwrUw%pP4kGN*B$aiRKKNPE((gI`g{_?crLHU2SvH2=a;-* zpX8Ign&0s^So~!ktDWGMUoc)1Cb}7Jvr*@=LB}${s9{`62D3|;PY9dSnSrbe(TfYl z_bCHn4#O&$dPGzNQ4-vu>r0}~>*hrl?-E6~Ah~7n$HQgV+3)XU;g=PmSUeak_KNlr z=Fd~g%aZpE!K4CDsU#?#Csa}9@r1B&1~?w9y9E0YsI9^I*80%UOglkh`zHDOS6;Dy z@X9OXs=MymaQEGVFP$IIvai^MyYAk2H@qqCi2M!+W z`O0J6_Gn<`sTFIcAD^YaHu;+m{_b}FXs6#9^~I{ckiCzwV{lf>zP_6FTC_7RUqM1|f9uW%n)V(|i3u>|`P z(L1(VTz=72r@+Ym6rHAn4Z?u3UqZDyOsamV7lwpMCNw46J<}wOxA3Z8?3IdW%?$VH zGXbi))v{{;y-ydFfE0p4*g+YVK<` z?8>Z+CB0fwv*IB^QUrmodA%7)mi&?$PG#;=+=}uSisF$TLKlik10>@OiteyiG`(tI zRU&F29O(85yk>+IL64vtuo#U<0Ywb@5}Gd=y(qJ5!!?ROmfVy}Xqk9Wko{;s?e$i9 zL2$EfvPWce^KHqauvilhubQKu#(oswYiHr8$d#}Q8< zB>wjLjX$r|?wm4BbPKEJ!|}<3+-V5ZuZmH&OTQ{c$-&n*954jvF+bEKn8%_=m?fLbb`jCgV14C$reVi&zKffYW zdi(ts#7gZ?q;$VWkp!6+cpwZ%7jyNuUC;= zA}Xtoe z3Aiou2xk%MQ=@f{!n-KiwsfsL8dQ`l<#_@8q(eTxd^3tfK#4l7Gu&zkKv)YS*r{yLP3H z9{ty+WB0*>=?xnJd(cMc_J@V_{wvx}u+AKF7jiP7vl(_v)uUuiabr zD)tctvP4cO-n%2Z9?>-Cp1K_uc?pLoPdkzREA{9PJ#WA5`QbWN+x}bUordP~YMR$K z8yKX2l(?P+o1FOPAU^<4 z$;MPnh8T--EEQ7(mrGFOt2akm+W5+GtrcrMexlN{4jes@ZH|2Y#(mq@C0or?^QW4v z#HHJR>i0^T9*|kUOb+Op&F#6&AI{bGD>|T4|f!7~=@b%Y6tF>zFi<>up@%1fR zz!2qd9!*ogVS`0s3y?aX6hd7PH$an0`;IjaV--Af>-;4AVVLyr^CuC5HA_iNOO{N? zo(8e}gIIk!Kli=a{B%85iIn?O{gKE|g4tT^jp%4L7|f1F--y++!PC9+CF40^U!}57 z$cSW<}`!U6wiW5&iUCQt=ETN zw-|vOSdb6iwtsfH78o9V1+*2jH#iho9+G4kG&;|=*BskJ$e42&nf)b?5!ckO$|t#@jg2Mfngc^LhZhh^#`Y(#O|4(}#k%QYrBa+;M}6z3iq)!Zd!erb zUbk1YZx+4Js3Q?dA|oow7Zpc~-+_?~5}_a!MvFu(eR6K@lcktdv0@)&NgxsoMzlgG zRIvX_O9W`3mP9FFrQ@F4LIo{Z11X^$@Wj&=j;YS~AAG=&8H~``ZNm_TVd7I_$3P_v z2sTVE%MLmrz%0xmb^G013GxkFhwVa+qu0tFk37PHzt7KAYqjIA zJ?}H6J3XE|CDZr(YsYJ~Dq=Lp-0r3fo!9gz$L3~h_F6U`f&CwYjyY?#1mn3JG)Nv> zCSGD5I~D;^ZG`>j|aDT-?fJi>_KM6sduZB-i#XixD1fBj@~lRa+wR-8_-%@ z9}(e}^K0V76G%LB=*C-T4&8k1zD%p$%q-0e`$ewRA8n1c@Xr`9^AWslMXxt=x8Pr2 zjOPqmWc_nn_uYKU^@ncEjMY*2sCO9-pMR0_3r>ClcY<01ELb+zGU7RI%)b}bvkIR! zfOw(~ofra+hk-j0XqOllhp(H&KQ+?e$AXoeIG literal 0 HcmV?d00001 diff --git a/assets/www/css/images/Off-On_Slider.png b/assets/www/css/images/Off-On_Slider.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7f3ba4be40742eaefdee76b354ab2e92bac775 GIT binary patch literal 1560 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 x!0Q067=CL44d3% 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/carbon/nav_icons.png b/assets/www/css/images/carbon/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b40938faa9b585e9bb6ae7fa7181e788a647eb GIT binary patch literal 3131 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/carbon/splash.png b/assets/www/css/images/carbon/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..b67de93281f21b6e984b199c52b7ceaef64a8b21 GIT binary patch literal 11191 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/carbon/vert_divider.png b/assets/www/css/images/carbon/vert_divider.png new file mode 100644 index 0000000000000000000000000000000000000000..6fead76eec5cc2e68f7205d127a68d7aaccc062c GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-Y0V1m%Ufcyz%*9TgAsieWw;%dH0CG7CJR*x3 z7`R@8Fk@3UZxm2ati&~<#JMOnu_QA;Paz~TH@?T^vI=W+ta3KE7bM3Fwp-hE;E+ RB?W*|44$rjF6*2UngGZWF<$@x literal 0 HcmV?d00001 diff --git a/assets/www/css/images/close.png b/assets/www/css/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef9355c1c709004a6c75e88af91563bc60cc62a GIT binary patch literal 1492 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/eco/close.png b/assets/www/css/images/eco/close.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef9355c1c709004a6c75e88af91563bc60cc62a GIT binary patch literal 1492 zcmV;_1uOcAP)M^_;l zDHKJ-KmgGMUlG*Appi#}#Hio{hyfpIt1H$;% zXc^7eGKT<-3lPz;YFrd)T)eMw(f;gn&(onphpD5ZgVO0VNhwK6N$GT&IyyS&*sF)+0J!ce1+b~Kv~=lv?{3K!7Z)o4lvFB(!NEZc4GqDvED#Ym z=kWP_V2q(R9!~~;5AIyI?)6nSuDkanDG<9}% zwzajjtsCvQBek}+_OlZwsxF7aVG0I=L&0G1!`z<7^|7XO{`{pIH*S!OF_uUqP8~gV z{H=SZ&m2EmvbgTMh3md@uKWBYB_->V$>f$qB9YQ{T{uqmCBrc0=OUk`anXMApQzQ~e-jHyg#zocm2}RU;_UzU2@`{B-gtD@VkL&8{vXMxH znwy({CL%!NV*RS8pCQvUvx!6^bK$~;S2Qj@Ubg%Rx_b30*|tr=V6fi>bB&9tI}0a} zVHlLnW~sBYGnGsxA0;Au^X+%hSS&`(%`Hj8Fdl=(#SaG${zSHIQ%_INFD}FzjYcoK zC!ts@MyF4o?$fw9oR>dt7#xqsDHe-SJRbktwObnsg(wz_QEzYWP7i3 z<%rt=nZm-t!ltGs0{~Q2MPXrK-hl%@E@^5E!|(Sakw`#F3C{Uf`T6`!$y|pU-zC5D4UTb#?VI#=sauptu;it|OI7K}rd)*ZXBb zLBXp4$Td56RaMNJS5g!Tg?hQ}yLAM7&b)bZ z;X3%!1E9XIuMeuKf-yG7{pkJ4lP5jg+R_BS-w(^Oat0sO)YP8$csyW?Vd~VW1>uI^ z87bvB*KWb%ix-24z!+KQFM~)8KwoQ>py+jH#?cQ&kx zM5AdUqPn{JOP_vPw&P|K(Y=b}E1r0gdU|?Ix+XP5RCFDjh)`W!U3UI_M~aB3p`pR~ zdiU-Yjf=G!7Xgin+guH}YkM^=u9TIR)4;%h<${}us5Hm>$1~U3+WKaD`?(GxBGWV} z96m#pm3!%h)obXkNmJ;qNmJ;B)oZA-axZmv_hjgAsTm0#74GWnix)3`pGaK3L^qc% uBHEU_ijk@i5&lEw0(c0(Ec}l@fWH7JLfoI6qpte^0000Kjs{jB17IZ~ebVG7wVRUJ4ZXi@?ZDjy4GA}SO zFEKHbNRVX!0010xMObuGZ)S9NVRB^vL1b@YWgtdra%FdKa%*!SLsK5dm4sw$%6hd(j+6vtz7@R~z%#$zqg}rW99WAX7Q-(#y{wiL^FGxk6B7W>fQ{aua4Tce?U77+``$K&x2YinyO^?E%#EPjxj zH^_`-S$003XL)&fVSj)B746->Jb;MUKsJ-fVB0nTlarG#E0xNFO9O~V1Sr_HZDzCC zzxMI*@oOToQLEL?oJSx4U?mcXL<@xirfHsXOifMQIXF02Ct$1k07PU2_*5#DzQ9d17?B%=)DG|ln;FDB(s3Ni%HWo4fqulKsuG5++iPB50U^l9_G}55v7!L zKDLXn&*(7H(t)yw#2Sr;l}@LhZ*Olm2zb!}qrg+wb-T8aK>+Vu*L@NWhp(qnsTYpp z5DJ9?mELN#FbsqF`S~BfU7*n$BLKU*yRQ?8#M@@G={t_&h)7T>Rfb`-Ow+st>;ZE? wP00EE)1^@s6TOrHG00004b3#c}2nYxW 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 Eg8p@{ng9R* literal 0 HcmV?d00001 diff --git a/assets/www/css/images/eco/expand.png b/assets/www/css/images/eco/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..9be80ba94cc3cc592cd822846780d9facf928454 GIT binary patch literal 759 zcmVKjs{jB17IZ~ebVG7wVRUJ4ZXi@?ZDjy4GA}SO zFEKHbNRVX!0010xMObuGZ)S9NVRB^vL1b@YWgtdra%FdKa%*!SLsK-Vlg{2Gc%V=CNBWfz!QIobTk?@wANoN%X$fX3>h4Nm6esr zv9U3d$t1v8|GEL7wTAA7*@Fc4poWKs55GJ9#YnFqpp;T3kfD2`jyQNpt%=C`FGr;U z*&}|1h;)XAhNjZ#^mHs1qtp45Ln);vCMKSGzw3Vg6A1vc*7K=U>S`j9hz9uF1j;7` zhk%#Ou(5BMF3E(R-Z2}E)h<@?A=PGvO~B*r0KSn>$V--VIsi8dcB^A z$KxY~LScSjD7IGIChjcwcHa=CB72=KnI;nLF5)b8%?XZbsnMC6Qr9;lu%cM-S- poM*7HDZnP(o83BM@ZY~GegZtEPCIkdT3G-9002ovPDHLkV1f(=N8JDb literal 0 HcmV?d00001 diff --git a/assets/www/css/images/eco/nav_icons.png b/assets/www/css/images/eco/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..0935c65e26496003561a986e57c70415bb5e528d GIT binary patch literal 3578 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 MrGrI-SwQB005koe>;M1& literal 0 HcmV?d00001 diff --git a/assets/www/css/images/eco/splash.png b/assets/www/css/images/eco/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..a899054eabc79ecb98fe6920aa0eefdc23c2b99d GIT binary patch literal 12980 zcmX9_19%-x+di>vHEff{Y8u-~qsBHGHz&5)*tXf&wynmtt$*L|&viCuv)As-?94OI zjhTch$Vs3e5+Z^?AQUM{Q6&%v0uMO1g@*-x56lOQ0bkG#!cxlcz>f#KaR~4?f}Ny> z0|gG4wsas9`T5Pheuo1D zA_GZ@3Msp!pJljuV41uR3N){|}%AHry z(3v~0Y&kC_a&l~6_+~PHS1OLJh8Kt-q!0j$maY9&4T+ukwj=ia9iDA`SI#sRcI@SD z`;KBZ<=}~b&?Czz^Wf$(LVSl=T{xBs%uRCZ#)k|G<*AdSAXuWDTUZ!mjZH2L4MX?` z_U`U3J)qw0?pL{Xb8K97lW5VbFB$wmD}91=j#5dsY8iQgG#_H{;6n2un0szT#m8U2SP9#5~M9Q|RT zJ%r^iWbng}Ow@Fi^X`Y$kD!?n=bBJf(GYYm!Ls?>%1Tt6y{}8nZ%mR>Sx1*02>rO! zwJ$Gjvh+GM#m>7S7SeM9^Xjy9FEm#28IxsYaS8nh&@IZX+#3xx8Q(YIX*tF zK7Jo|xoAgA=f(|#4<&}dO5E&PPtsmLn5_<>l8R4E++J*q>>p`4X!j≫OHMu+SC2 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 z zPoE3~4C8t<=w_D_KbkAqnoeU~;df-(ltNnFY`(d*zVOM}dU^4$$Tc*#bynN)GSk?V zY<|HE$|36nF?HTzZvQL*XXrE>GPYgMlsP#b6rNbxKSo2ee+lm~5H7@S#$t>486U>) z05`K|lrgaA_K3Va(ko>5A3FFZxE~8QLv_00mL~8~i=&VPb>3$6J z3U-0>{G(JeA~#u(b#zMdlxs}*sqUq<@s~DM_?KLL;ZHl7b;`ncn4o`chjK#&2%KKm z@Ur{lpv3cCKqU}_Sz}i!FIxQvV^3RyF)cjfnqly8fcflXzM zI)@2}>vh9y|JE<0O`?^Au*3V+>+D!Mmg_P6Mf_=)O8Vk+5sMtnrW3vkQX%$k<-zH5 zq?u#9(3z_23><4|ml8UmWd_FZ{wck7J>($j#=9hs`=Won#VP^TUe_jQz$|7V!ds{PxMw jiMXxacj{EB4>Wksq7w@CbbR1HOh8g%a-x;O`hNch0*4)s literal 0 HcmV?d00001 diff --git a/assets/www/css/images/expand.png b/assets/www/css/images/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..9be80ba94cc3cc592cd822846780d9facf928454 GIT binary patch literal 759 zcmVKjs{jB17IZ~ebVG7wVRUJ4ZXi@?ZDjy4GA}SO zFEKHbNRVX!0010xMObuGZ)S9NVRB^vL1b@YWgtdra%FdKa%*!SLsK-Vlg{2Gc%V=CNBWfz!QIobTk?@wANoN%X$fX3>h4Nm6esr zv9U3d$t1v8|GEL7wTAA7*@Fc4poWKs55GJ9#YnFqpp;T3kfD2`jyQNpt%=C`FGr;U z*&}|1h;)XAhNjZ#^mHs1qtp45Ln);vCMKSGzw3Vg6A1vc*7K=U>S`j9hz9uF1j;7` zhk%#Ou(5BMF3E(R-Z2}E)h<@?A=PGvO~B*r0KSn>$V--VIsi8dcB^A z$KxY~LScSjD7IGIChjcwcHa=CB72=KnI;nLF5)b8%?XZbsnMC6Qr9;lu%cM-S- poM*7HDZnP(o83BM@ZY~GegZtEPCIkdT3G-9002ovPDHLkV1f(=N8JDb literal 0 HcmV?d00001 diff --git a/assets/www/css/images/frosty/ajax-loader.png b/assets/www/css/images/frosty/ajax-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..4a51d1330eeb352fc1c105f9f59350c0b41d31a1 GIT binary patch literal 708 zcmV;#0z3VQP)500004b3#c}2nYxW zd;deRHZ6cA2WmBDlr~mr^HcyLsRr<-wAo(_L$)07-x4Quldi>dOaX~D2_Re@ z=9uWFad5%73iC?TY^-0^qtXV`IJ{)TV75g9>97)%(>Qe0r%~D*=ssLWrHz}$q2&z= zn`m}B%oc<;w9`2J&D|Z-I5Yt6Bh63J#WNVPhx_UXmLkRWN{uVMX5cZ#zO?3vvLxy47bN&-Wi*S z?BcN#g^{-MR;%L0tvU;f)p8Ifw2ibnx$LUFg(&wlv33r#^-2^ev}pmNw29TGFHs01 z`T0FC6s+OuxUFF*m!)AyJy?OH!nqj;9oVb{#$lu+w@b|^7bKLFHm-#ZJa#OA`cj~h zaZ=LbY1@A<28#8MCDR2Ab@V`dP_4A7Qcx5V{y3670WkwlM^_;l zDHKJ-KmgGMUlG*Appi#}#Hio{hyfpIt1H$;% zXc^7eGKT<-3lPz;YFrd)T)eMw(f;gn&(onphpD5ZgVO0VNhwK6N$GT&IyyS&*sF)+0J!ce1+b~Kv~=lv?{3K!7Z)o4lvFB(!NEZc4GqDvED#Ym z=kWP_V2q(R9!~~;5AIyI?)6nSuDkanDG<9}% zwzajjtsCvQBek}+_OlZwsxF7aVG0I=L&0G1!`z<7^|7XO{`{pIH*S!OF_uUqP8~gV z{H=SZ&m2EmvbgTMh3md@uKWBYB_->V$>f$qB9YQ{T{uqmCBrc0=OUk`anXMApQzQ~e-jHyg#zocm2}RU;_UzU2@`{B-gtD@VkL&8{vXMxH znwy({CL%!NV*RS8pCQvUvx!6^bK$~;S2Qj@Ubg%Rx_b30*|tr=V6fi>bB&9tI}0a} zVHlLnW~sBYGnGsxA0;Au^X+%hSS&`(%`Hj8Fdl=(#SaG${zSHIQ%_INFD}FzjYcoK zC!ts@MyF4o?$fw9oR>dt7#xqsDHe-SJRbktwObnsg(wz_QEzYWP7i3 z<%rt=nZm-t!ltGs0{~Q2MPXrK-hl%@E@^5E!|(Sakw`#F3C{Uf`T6`!$y|pU-zC5D4UTb#?VI#=sauptu;it|OI7K}rd)*ZXBb zLBXp4$Td56RaMNJS5g!Tg?hQ}yLAM7&b)bZ z;X3%!1E9XIuMeuKf-yG7{pkJ4lP5jg+R_BS-w(^Oat0sO)YP8$csyW?Vd~VW1>uI^ z87bvB*KWb%ix-24z!+KQFM~)8KwoQ>py+jH#?cQ&kx zM5AdUqPn{JOP_vPw&P|K(Y=b}E1r0gdU|?Ix+XP5RCFDjh)`W!U3UI_M~aB3p`pR~ zdiU-Yjf=G!7Xgin+guH}YkM^=u9TIR)4;%h<${}us5Hm>$1~U3+WKaD`?(GxBGWV} z96m#pm3!%h)obXkNmJ;qNmJ;B)oZA-axZmv_hjgAsTm0#74GWnix)3`pGaK3L^qc% uBHEU_ijk@i5&lEw0(c0(Ec}l@fWH7JLfoI6qpte^0000Kjs{jB17IZ~ebVG7wVRUJ4ZXi@?ZDjy4GA}SO zFEKHbNRVX!0010xMObuGZ)S9NVRB^vL1b@YWgtdra%FdKa%*!SLsK5dm4sw$%6hd(j+6vtz7@R~z%#$zqg}rW99WAX7Q-(#y{wiL^FGxk6B7W>fQ{aua4Tce?U77+``$K&x2YinyO^?E%#EPjxj zH^_`-S$003XL)&fVSj)B746->Jb;MUKsJ-fVB0nTlarG#E0xNFO9O~V1Sr_HZDzCC zzxMI*@oOToQLEL?oJSx4U?mcXL<@xirfHsXOifMQIXF02Ct$1k07PU2_*5#DzQ9d17?B%=)DG|ln;FDB(s3Ni%HWo4fqulKsuG5++iPB50U^l9_G}55v7!L zKDLXn&*(7H(t)yw#2Sr;l}@LhZ*Olm2zb!}qrg+wb-T8aK>+Vu*L@NWhp(qnsTYpp z5DJ9?mELN#FbsqF`S~BfU7*n$BLKU*yRQ?8#M@@G={t_&h)7T>Rfb`-Ow+st>;ZE? w3qiSYpwQO@0I#PLuY}6mW@wOjQr_) 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/frosty/expand.png b/assets/www/css/images/frosty/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..9be80ba94cc3cc592cd822846780d9facf928454 GIT binary patch literal 759 zcmVKjs{jB17IZ~ebVG7wVRUJ4ZXi@?ZDjy4GA}SO zFEKHbNRVX!0010xMObuGZ)S9NVRB^vL1b@YWgtdra%FdKa%*!SLsK-Vlg{2Gc%V=CNBWfz!QIobTk?@wANoN%X$fX3>h4Nm6esr zv9U3d$t1v8|GEL7wTAA7*@Fc4poWKs55GJ9#YnFqpp;T3kfD2`jyQNpt%=C`FGr;U z*&}|1h;)XAhNjZ#^mHs1qtp45Ln);vCMKSGzw3Vg6A1vc*7K=U>S`j9hz9uF1j;7` zhk%#Ou(5BMF3E(R-Z2}E)h<@?A=PGvO~B*r0KSn>$V--VIsi8dcB^A z$KxY~LScSjD7IGIChjcwcHa=CB72=KnI;nLF5)b8%?XZbsnMC6Qr9;lu%cM-S- poM*7HDZnP(o83BM@ZY~GegZtEPCIkdT3G-9002ovPDHLkV1f(=N8JDb literal 0 HcmV?d00001 diff --git a/assets/www/css/images/frosty/nav_icons.png b/assets/www/css/images/frosty/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..8a62db10915f23d9acf00371e2293da6f1bd4a85 GIT binary patch literal 3345 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 KrlrO%k^cjA)lsAX literal 0 HcmV?d00001 diff --git a/assets/www/css/images/frosty/splash.png b/assets/www/css/images/frosty/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..6b8ee965db97484520d6bde44a28a6570a392e9f GIT binary patch literal 12298 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 F69D0`Vd4M) literal 0 HcmV?d00001 diff --git a/assets/www/css/images/listGroup.png b/assets/www/css/images/listGroup.png new file mode 100644 index 0000000000000000000000000000000000000000..221553ae9a0d0daf255a807245a1ec3372d51521 GIT binary patch literal 2867 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 WrQ~00sV5!)0000;8OIZwd~~ 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 ThXZUO0)b>C6vZn<4FmrNK>;r8 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/nav_icons.png b/assets/www/css/images/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b40938faa9b585e9bb6ae7fa7181e788a647eb GIT binary patch literal 3131 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/slate/ajax-loader.png b/assets/www/css/images/slate/ajax-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..811a2cdd1b492c47faf85c1206ad6606521eb6e4 GIT binary patch literal 503 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/slate/nav_icons.png b/assets/www/css/images/slate/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b40938faa9b585e9bb6ae7fa7181e788a647eb GIT binary patch literal 3131 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/slate/splash.png b/assets/www/css/images/slate/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..b67de93281f21b6e984b199c52b7ceaef64a8b21 GIT binary patch literal 11191 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/slate/stripe_bg.png b/assets/www/css/images/slate/stripe_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..65f649de6b1cee0e99fa51053d90a2e0bbc9d508 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|p6gzo_Z~#FKM@k2f&spFRS^RMGhhG! literal 0 HcmV?d00001 diff --git a/assets/www/css/images/slate/vert_divider.png b/assets/www/css/images/slate/vert_divider.png new file mode 100644 index 0000000000000000000000000000000000000000..6fead76eec5cc2e68f7205d127a68d7aaccc062c GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-Y0V1m%Ufcyz%*9TgAsieWw;%dH0CG7CJR*x3 z7`R@8Fk@3UZxm2ati&~<#JMOnu_QA;Paz~TH@?T^vI=W+ta3KE7bM3Fwp-hE;E+ RB?W*|44$rjF6*2UngGZWF<$@x literal 0 HcmV?d00001 diff --git a/assets/www/css/images/splash.png b/assets/www/css/images/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..b67de93281f21b6e984b199c52b7ceaef64a8b21 GIT binary patch literal 11191 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/stripe_bg.png b/assets/www/css/images/stripe_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..65f649de6b1cee0e99fa51053d90a2e0bbc9d508 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|p6gzo_Z~#FKM@k2f&spFRS^RMGhhG! literal 0 HcmV?d00001 diff --git a/assets/www/css/images/vert_divider.png b/assets/www/css/images/vert_divider.png new file mode 100644 index 0000000000000000000000000000000000000000..6fead76eec5cc2e68f7205d127a68d7aaccc062c GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-Y0V1m%Ufcyz%*9TgAsieWw;%dH0CG7CJR*x3 z7`R@8Fk@3UZxm2ati&~<#JMOnu_QA;Paz~TH@?T^vI=W+ta3KE7bM3Fwp-hE;E+ RB?W*|44$rjF6*2UngGZWF<$@x literal 0 HcmV?d00001 diff --git a/assets/www/css/images/volcano/ajax-loader.png b/assets/www/css/images/volcano/ajax-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..811a2cdd1b492c47faf85c1206ad6606521eb6e4 GIT binary patch literal 503 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 literal 0 HcmV?d00001 diff --git a/assets/www/css/images/volcano/nav_icons.png b/assets/www/css/images/volcano/nav_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b40938faa9b585e9bb6ae7fa7181e788a647eb GIT binary patch literal 3131 zcmbVPc{tSH_kWK)8Bw;V5uvg#Lrr!~mO?}^V+k=?8YBBYq%xDGP%^f5p~i&9zD<#B z5E(TFGkv0|h|Jh&`p)O~`|tbb_dd^go#)=?obx)*bI-l!Uc8I59avmZ8~^~YgFXB_ zUvKcmLQI5@B7PH3_(~|s#=%{TKa#})as2tN2zx{n0PNoTuYks;Q+N1_2cl1VM!R89 z(Q*EfL4b82CLrjLZAfHL940a>>d+}S<7?!vCVV@&f9+012Kh&aV8Rc%hoFN1eFHsx zsGfmg*R<9mKV*Re+}b_9^j8`7>OS`V-p)G(rC-%v9?bR{gQAhbo=S2!#<5Dxp2sfG zT8Vea`@(A826r!_({5G2ra+Mwy55j)R(T=Fx-~AQ7l~(0WOh~LtIyxEFS+ef9s1_O z$yYlrl@v;M=g0`}wGk{g{X_@%%iwk%ay z7k8R!Yino!tTXROjXI-Jox^-KZ{W0nM1U%+FVRJz3KriUkXGIL>)}du3?&m}sIMG= zh}P-w*w$%@F5>-Mvk5OAXHf4FSR{i=Js>6&&mJzbAjVX=yb$b?1j_6c=z?jW6@4QV zwP<;ws|zqP+D^78x5V;}5skH^sw(Lk^L^W5x7&f=KVOn`i%sKnfv&42mzf4<^W&2)I@O=SfBr|EbN1pwxNU8tfCk^IN zXVlu9n}@|~#;=c#8Xt?!8f5icM>84_cbnXGPS~1;)zjP7@JGrwo{`C`m$>W^Wlb$D zEs#rL_R+M;o`7WmD?YP(CzZok~h9&8b8GH5}fSp z?6{N+QLHW8TFtiIhq}`uH;ECEhM`To?7X}fJRPrw1A=}w0dEQmq|a`{DhQveawprB z(h~UffPI>0T9%#;DmOt-A^`6A#EXkVG#YL6RHkGtbLnBkw7jl2i$bxsNZEptE_GT6 zv2=-H{VrbU3m0hE)|QqAeXb&Eri)ra*xaVJWfMC4sr5z8+QeVlD1+Ol(xi`GA!+T(+Rt0F#?-exYbd$Q#JTFobpy$dw_6E#0UY$YVX zj2iW$%#D~Pqib(jFmgZdPbD&-3xi5dB^xG3pEl0y_8uG1DR zf~gI1oJi9Q24hdKkK3)H^0P_)ze^_r58k%Sa)#?Z7^KaNj)LmxrBN33^z6mOMe!FO z>DKU?DcNefu&|d={2bn|{2C2j*_+1ZAE;?R{VCx`R6g$U^D%?`Ra4ACmEOaUk(eIR zNXHm5?a=Of3p&8UwPx>Ea=H()KuzO6e?{ha_$JKV`B6;rZPzs73LX#kNZjPiJ$77` z1cS|=$2gtIRG>tK7Q`GixxOziX{6k|WRk(Se+HJjwiesuT@$N1`D)TS@=bmk^s_(RQMTk=ZJF@X??a=}27oWn4m1dQiak;#rYIR<`>F4!$)$t`!9F8_ zg7{r(fJ;~-<4;*x*%Y1L&#x#ZllkyR24OVTIrAOm>9!v7&G+I7a$qG<7=LA+mMzz_ zqM4`m@Eb%u!ExwZe5eE%JfygPKmK`G4qSC%HLZE(*XPun{} zYqb$|voBpwC7D5Hnxoh!j2@nhIWzqG2nZOAf!rEv8@`uFBnVaCFf5im^Gvq*;fjQT zPvKl}!_89DxZLfnt)}K?m=H@JVCc9E=FBWe@^cPvEQwqAOp(h{Pv-4x8W<W^ zCD!dpW3MqVOJ|s=zb47JiI@tSH|NKThYh(}R{r_DF1GbH_%}A(g48lk135K9M9*iMH+36AkQ%e|vC+A3Iel9byvg=mYvhW@$qV_!(!m-3`V3TJMx z7riI*U8Y8vdyxCW37OLh-saANo<8pGP|>b>fCpwpnn5}XdG|dv6qS@}v443qxO;WO zhkhwBAPdt5j?T<}%K}n`S<=A83`zvn)phg? zU@7_=0$|M`cTfY3p)}3vnws$8tPSmBIy&ucLpz5LA9e+V+b=SRX$lnC>$r-cQW*E? z%*>3PsHo`DOl#bdt?B?&AY~T|1C)#YKAOgm5IL2hJ-B51^m2-?w|Dl$!~~{_WRdjq z=g)z0>e6z&a}@zweW=jsB5rGI>q1ION(F}#33R>BQSz^+m#aQmr5@g zqcVzdN#_v=nEH{paT@K9br?9&O2J#@stKcMPnn~}zF7Wd=i+juM-5er%ePKS@5fi- zacg=$Eh{5&Q`dSGGVGqZLZQ&;m&6o9`+S-6TI=|IN1Rn^r|J&bb*r}wyz*)s{_y$p z=asj+^`h5SSHb&6g8&uawC&U7u-Mji4&h39Rd7g%wSz-{QA4e#wzr!bw?^YF#wH*z zaciL8Y>(ycrlpSU*i-5mmK&^$aAS`1=^GT;w|@HFn3Wbahu8G0ujv|V1cbhVS%@Ja z^$G4o%=hNVxq*nFprFNoz&Pa@>g`gQx(p?i1R$?W9<2Rt+poV8~VaqM5xgX115tYf;m5Uyp* zbqXtp*j<^XWOaD&{IJu#nLwpR@x*ZyFyP&6RTzZjtaU$ycevj(Y#OWYj)u*@YHErv z(H>L*?gqhvO-iuUWR08qtpfJHKmiy)f%K_nQDXoOXuS*!W%l59R3Xg#Z8m literal 0 HcmV?d00001 diff --git a/assets/www/css/images/volcano/splash.png b/assets/www/css/images/volcano/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..b67de93281f21b6e984b199c52b7ceaef64a8b21 GIT binary patch literal 11191 zcmaKSWmp`|*6rX78r&tg4i?~xjLxM|icZURmJA?!a1a}f#gA?FR&UfDP z-5>Yd{xQ?l&#qn7)w_1BwW?w@)Z{VINYMZQ0H&gXj1~X@M+CcfLq&%Dj?71Vh5eCu z$r^ZRyV-jASbBf~k~VHuU}{AdOFOU@*wV(=eH<(T03c91=ookzsHzBAySacY|Js0{ zE)ZC406;_>3bC|y0(((gf$bbzMQP4Dduga0Y(#1Fc~v=8A<|%b2L(S5u(qF?j4RvvL^%R1N()`P>5bXZ%V-6bXf0=kWiPHQ}rwmjzsHNRJ zz|_1TPIhZfK2B;rE)W;b8-9L%HfnB8E=~?kZVoPPb}oJ)9&RB{PU?SOG_ck@Y;1+J zWaR$c7OW&nWAEh!5#r$R@$muq@POPr>^Qgt1qJ{5;O1tBS+INhx_Vhc*j7n%QBmjl)rj=!Jv|N6}TJc7l}-`oEL zEv)dLum`)sV%-B4)WT+sDgXczyP}Mw4s`iA5Hmn$zU@p=;f~Le1}&`B9l&*cnO zhQp3$E~)WHLE}p7fsNy!Ei&$zu0x-rn8-oS2xlldR)n6giIb)kpEghP73r%(YD6-+ zff)Y6sP`CDGq1vS>v@kwmjngPJty;d>{42SdVeeb`kLLcVB~-DV&-%b_(Rdr!2xF| zEiH{sMiEQkaud?OWiyGgh1#dduxO(_T1X?HB(PRPjP)J~Jk)ednE3M*d5}QYZm^=& z1)U<6N$I3Aj!Aq%ngBVLotatwrbh>2MN4{x1t*h^zJ7s07xaP_qTb+=!$;2@+D!zP z4ggYMT1fDDVJXch_B9=QcdjJ-)L^1>UTXf?dAsiKwpAn@{i|yIm849-SYBp3mSz_c zu|w#HQ5tG*fq3w<68mi%_G9B=C?EFRKyn-#jXvBvFa*m;?D@`GHtOi;$T)-D_@nl9 zzF&VNH(p)wLY?uaAADMmjs3$yn^LnR>_|vRnq1GPoo9NUC`2jNRqr!Mlr{o>e=DJw zJ2%>PC*EBmV(eMtYd1hP!73Upj>aMvIM?rZWH`c@LL-;(+nH=_ZQZ~LIfUTLYkXIB zEtn$0?g^xGu-6Xw&6dUUw%)KsZsDw<`({~q_d%1ReK&N02GFzr;QpRc^mA%JqUxWq zy1~YQ-u|IH)w~5)PTvUz*OIdG^70C3n6g05SIdrxa)Gc3dXuKmveeY?m6%>fKU{Kk z=!%I<29zh|BAXx6sZ84;Tfs}4R;l%&`@7uurd@sX_CB#~O!V~Nv{$l}=u$LYKfvM2 z6%`t_uC+uaf7%5n874H}6qS>C+LDV*Ps**2r!sP!NnGB!d*-J14e#7OVxogrn0?}q znVlPCQQBHt5j{OTAZ`O14U~$XWZR^4?@# zCoSpJ@Qsld-sh9&Y#zH)EJXNNtc)UTvAa!Rcydf;#U73GM$eOejef&=$?v_J!33_t z)3r|Qx$W-7d%_%p>gsBvi#_od!!xK%!|))1Nm995o{-N9F~15qn7t z;SDQovJayE-j=?;_evDv?D#f>(3C`OUhm^Yvi>qZF|_yenSV}VF4_ymh{P^`|K8c+ zbCOJe`m%Qv(^_CV9Zg>ye1B=HuXp8(SV$z59yBStlHY86T>=~Q7uuVkq+F!vq{^9!QmIl}m4<2Emm7UKEGh#F*JIuo_Z!%Cd%7l^ zE^SZ8U(@a`Z`+!WUe5qo9w%w($wUJxRyt~vGY#ACk&LMlbI}oH=Q%L<#LU~-*r3wk z193iqOg>;|PSM*!DhPHht3H2zIEIZncLJ1<2xQVOPERlE`eWv2TUvTLki4eh0)WTg zB=3k4rjsJX&#zDI-~4BY5?-%9M&UT+%P+cM3KcI2<fs2NeaA($v(qmx3Ic zCcDYBF+OB)CJEn&>)yjhKz|z7HWq|qh&Uo(9sB>l(m_1|oJeNzxh#Kj>%RVxV>5eU zjHshgE^=t7wN#m@$nr{{KYWOm4aF~YsGjHuphES2`mVn7bRgiUCgo#!n?V}n;5C1~ ztVc1+G=jV+lYq&+T3u;5`F%i7#=yXU_}0_8ncrE7F6FU=GY@HXRn>i0Yik;cxG(OX z&CNM_{=52>oFtJ@wjuF`SPBF`1t zf5m7$r+bgdNVuDWx6h&~gw@iI^k}5KW>n`Pa^8oa>M z3fq&bs;WBZrN3jIH~iq0xU?xX@R_%Zk&xb~3+itF=kDTDHW1X*=%PB@q6)~g=?{~^ z{g}1k9&#x>uQr>Ikia97FL1Wpc0?T;(YF4?4ww2Uo{JZ*!G=A-VMSx;Ha}=@|D)D0 zFoVPVG~0Rhr!}aL(p{%84C+b^^VKLI3Xe-3KC)MiDQ(eVIq7epnfD5g;^V4U3xM( z%pjXU=%s9RRq@0bzEHwtKz(ZiAFdRoLY6u__grF~WvoZvbz7dTcf$q)hsNp5x2&uz zkHlagA08WW|NMBiG@I#nMER(C9IUcFLSSGJ(((Lo#18~MXT&Ab%1`18oTw{%O_kxV zdAFEa*5I6WFSgjJhtE8E9n8`va9J(n?)FPxSC`@>p(t76<0wz32s;YM#>%~9VIcI2LILB} z#_dLMaOwpPFLp*>{$$(bbCr%=U0wAs zeEZXyWYoU?ZN1yye4BXt8gH2hQemCF03UWeK*Q}RC=ePipTt}w=(603f?M{BSuE+J z>)K}{%3Ht9P!t9u-VLdY{6V+#H-`tnOvj|w65rpye)@NhKyYoJ<*iSu(CT=T2d-V^ z-jNHUJKxa1;|*1*Yq3nvRfgLBY?aa}$6x+dApY`O#vSbB=%{zGsDUE4JTY*eA#&`4 zg%>XmtR!MHIIr)|rt;)y`tl|^reY0CRL-5{RKIiu?qMDHS zaJ=@rZ={V3k#Z)NAR$qymbBg7>Dvzvc`);z9hJK4w|*j&W-i$uC*24j?w)9|@TADK z;>qj02ol5%ezdmC-HBCs9D;OhYTg!d&f$xT^9DH~w8LIVgB1C+{eL zCdD&dsn%g^<%l463z%nlb|C{yoKd0r4=9yLkD{-Tev2$0c?lAM*5B&%6h*Qw#XF3? zqaahI#4lG#&O+h3Ctw8fRCysPeblvxmh<}!$VKEKFu`@uQrwLlRN5Sv7|_9Ub%zY& zVVZnZJCFFf?}P4NYc!Y^AuU@Iq8#&mv+2@5B{2fK2nK_K7A5=X`z-xC9eX`2nvr1Q6gk3rFGBhc21gabtabeanqM=ewKA$5%pT0Qhn+IOEpc8?}8Tmj0jYAX-VKVMTrk&nTu{%4rux7q8 zx@`1xua1d+etYx-Z(!s=DLnOi>H-%@)^tS!6Tte8pn+UTE9*4d$btEDLi9>OM#c{$ zjpwI==c|eai+($;t9xtCdU&=Xk_j77l*~Hm`QPo^&BXUyFdrBYkYJ(JjVlFUDgxaC$HyZB3knK)$sU-D&$|7u59bL6 z^7~?NXen!8kVdqhhRuwuE3l6mbMt*JsK+hZzKUCglCq96WnSkOUWsHwhHe^M_| zL?B>y3;(;MS?IC(lO2Y*OmvMxwwXL^LiWg|{VB zqPHd)7O8X;;z0p;P>vF-q_msSjxz*V`L!+pVJt+hrn+dYWVCw#FWddeqRp5fS{C_|vnUV@Hk&QPb-|w)BBMOr&qa*m` zdC>jFpbO~A2)@8LU^Dfwe7HJzF)}pl=o?AN3R6~Sl<1L-YpAWQbrQSZO`UfiQ{Nq- zmB&G_h;>q^KsOodj8-km&CO->IbFTyK}CRecgjj+3Ma=STben-FmS;oJPYf@-++5` zZ!CZm$Pp$`{m`aR?5i+;cV~9Z1+`@vm=jm#PTzHGu zMZ5csnBrBO%`0x_T^(&+91pZ-*wVoY>4U-0x#8_+M4dP!PrMa4#HkM-K6GL(w;5nL z)w&)m$)Mj5N8^Lqb66(*LMoZ;)y;-h&cog*Hrza^L zKFQfR`^6&DcRzArJgv506B=!mYcf09n)1$3D>z9e#S1q1lPOom*(Q2 zoBMlJw$wBfN(-B&-5yL)VXq6Ic62{VKXjPReTm#AwM~klW-n2wMn&_diN%P#4D-zW z>6T3VZux=CC(qXg- z>Z}cGBykfL0S$Puw7QdO(M5G_WmDj$aicLP+BndcA>5Ye<*V z{B4{e$7sA3g=hj%IN1qi);W@G(c>uT^hHpfD3Shsz&8$q0QiR*=cvmPRQt zDkmBz_eb)K@Bnj}2gOT&_J^aj&f9(<9s{aX2+qn0Y@Qyrw6u(d<_=&7orlMfxpir3 zAY@%$UUIez6u#y^C`AoTY3zr$L4MA#k`{lycpu4H-%j)A2?n;%*MlBUh|W6CdX@;A zMTy3?%A-bcFVU4A8ekN9iZ`_==;%{fvI6bVp$JxSDj|Mz_m0_Yjy z2QSolIhIT-h~fBo>0S6E15EOz#Im{C9ZzlhEAy<`+_>`~)S%i{>}@>?Zo0Pu=Ve}- z)CE)b>dmB)olk#GtxygV%QmS=8WcX)JJFvWbfaE)Eq@ziR4L$wxroO+Oh`htmx zDOgGLCS;~Wp5tY|y6Cdp!#?>{(h@H8(a=il)T+(nG2przB~9$)5oQ%=!D?|IG9WugLfY2oH~Vew{+dq&-ve{zMpnKpvt< zw>npx$@hRS<3DCVB4GJ)B|nxc&HJZte}pps^4oc_}=f=V$Wr zNvp&6Uws{Zas^wr@0gdEu1)Ywd1U0HG?u__J@@4$>IGu-`&zV#RN`XSUsxMT$Ml+E z{9x{}FhB3|@N^c; zk<_1sqI8V1!7uWBx6@6(9Dn}aZ)|L=(BoT(2G5^t+>o&(7&k2MD|Ge5x|4`%!M{W2 z1bm1t!kFAf#Q)W&*^si2f6F>c96gA`!pd61#>4~-Pu$7=NYfnne80yuhS3`Oc-Gr{ ze>uT1+Czz$+W#R>*yow(@D)l^iYoPcM-K^eBnR{Unb{V*DKXCJhk)BPpD59HFxC}} zbz+x#!^MVyZDGFw=aQzUs|!vf=8|GQ#y*nU?T=t&Kg7nCOId{+bLE3)3lBn#y6SE^5S4 ze%qmCfNc!}qy#XEARHIQqZ5LhcuLIe0V7A$&*g3#J>xC5-#+I(iE^u)K9-Mp0>d=h zIBzklhd&W#iv9k|LzI0as?-(I6&xzyjl2Si2pZ>Kg-Gx6crCKS1PJq%fX!O^UsTt9 zhl+nlf>B=obi5Gcbd1Ig7U*p3we+-9p_KCM#f8XCyDaqtK9boxLqp_+Nbn+@jdl10 zd}v9j{37i8HZSIA-jAUWp9hOwO-o15IZFk&kqDxliyXp-An^z0OIcVp48jeQe=xU# zVVSF+&W{+4ktl3{i4vuv)U4pStxWSE!-`U(tJbosD!F5~`ksfwqShA~JUeJEa~nqn z%2W%tot@oA_*S9y*>b9O|PjN3-xaJ9p;1!sOy1{YTqQOob7Z2>Ee*qO(; z=kg2dB(Kwth?miJwW~+SIC9p3xk-n|PvI*%)al!&+l{mA++3CZVhs)7ep0me@Fkjq zg?01`te4z&ler+6J|TZ_Yrz>fbP=en{awTMJS|o#L17%209&EIF9*q~HyQ+-Ci1WE zHw;l-ay(#0DFr3>tQkF~-4u{g@r3 z;%wRiFYE(3fQ`gi_|B@wt{~7G`;nFyzr7hpZ1aITn?O3$@FdMi9Pzft*h}qVzS!!y zAn4&BP|2_VQSm_AoZdX)tx^%t~aD5R!7R3 znShI^JUl$=!$YHhGZ=gw2(C|ibjtN}$w4OR#Z)qekKQM<7&owxE?8^^Plz&&fiq1r zqJRn#Uu=r5MLQgU16KAmi1(4%_T=q*#;xZb=bQcM)c!cd57R}`JP)^L8yFQ;${ZGZ z+#x#zzUXG*HEc8caio>hm-r6#Qq(mXY=wvh4e%0=e??-Nsn^qO)M(jVG=*pr-Z1vT z55;vhb`eq7FR5w8aOD;3(;!rLdF;NMYZc4wL6e0Y}F6EMZYnxSO~xk6PTGO*1y>^F-np&56ey z-*Wh|i8`%d37o7y$KJyBVNV0~>tLf&X`H>Yb63oGt86@BV7JY9%9MVC*_!P~_%v55 zo&`?j=hz@yj9wKMO=e!Frax9v*G;38%IQ00Jw4Aaug(}6ofhh2Iuk$)47dl*U(a!< zFYJV$f#S#ap~rSHp`)?B^m|=Oe?(#FD|K;0Vv$eAH5-5RG^I+r!qqAf4KJNhJ%3eQ z9B+CgyH^3eFjuR&=y|CN1=?o36pD`gT=qdV=9-p%v@aAv+-9Y!w$>yPA^w^U&LBuW zCE=q$x#Ty~L`7G-~o7tn=o=0xOP1>P-Rw9Sg>uG<}X z>r+u~eNykQU%yh119`Z)k;VR;51!CSIa7(UwPSxVW8w4{;&Wb1^8BvKe?rM=A;Ey8 z981?gO^@&;3J?2~jAr6d%F=R60@V=2ji>iFy>(>~r?|hXtl6kYXwkVFvxns}+>l&P|V>3^OfaD(SGWLNpTM(Vl#|K0x(hL0Q z3Jvmws7m`QvmSWs9F&opR$`a&guc%kTe{S%D-uFm@h}0lJXNwz=W2!VGd8K_Gpx)P zGA*d$ABollB_6D?yYWwW?a}0xK&DcEt$4xlmeS-gn)>U9Nl@Zo2f?-{%b#?mF_Q5% z3G?H8UMD@iuS{WKlLN*J#zsbH{eVZ#K)s={+)`>|;vv8>A)Gyi%|mxqnE3fcWt?hz z`flN8iIu_3DIBqI8lnx3F}EnZ-$YHo4@0)cVqO>9!=O#~GA`u{f&D-;bFJOV{s^zU*Yv6cnIgu^lSO7+lT_-*0_0nv!BVOg?>1VEFHaT)T?BuaaL;>VCO z#+d9f7ALEj)}Ge|%I8PoK|r~>C-0TwSfNOmO0Rtru&y*yy?RAKmF7Y${qyI~;heFU z1V&WR(}wo3_d4s9Oz0-130>v3omlbo-bdQ4iiL$+VIWJGIsrpjeI99a-CRHFi@6P zw@-m>IiS<+mmtS+(af_Rpmf8rR-}wqbj7SVA(8~sAl5(sZ`f0{ zAw4b!yh7djqTifk^2rO3Q-Pc?3(5H}HBEy7J7@J_h8*046c>WO1Tk;P^8g~_oe;$u zC}}{d1R+7hHS@>KMrjj^{a9ykz^LiW7-`L@eDW^c??tnnFp~P^PxSKJ>iPCD^UOIn z;vn~6iOym|k?K^3Y26$p^Yh?Rsk}-da!epsznKbVEc4sG3|9M=;0UoBN^3`Lf_F*s zQtJkXMtInA4?_~hCeZKwcG160mUSlw9KAWZ&ZYP|R~legCOi1$al^e=DZ`|RD*Z^O z#+^dYWB2P)iyaR=OL7xnr|0^I@!FLkW)xJ1QJda9Qb^zuXpq4XiYJ%9 z994x?j#QZC$mE^Zj>HVTWj0d2=GdCea2$mJpBE0-RpU0t`OCSvIT9}kOH(t>Aw`2X zu_ngFEu{MKvpX1fqR)iFYkR*>9%1yadNhHg1qb?M^kuQ65>#$PqEU6Qm zdWy}Y9b%SHGqCl2tgMwSva%0n%@uUM4L8AuiaWn)Fy@*~svVU(y$8z!G$i}cm$W9{p$dah z_h5p)IvEe-+y+a}G^jWgv(cwfSQLIIuuM|`G3#_k;Ky^t9m5t|%yj#dOOgycMsh1gVx{l11eT3j_GcRs zqmvtde(@7Eo#I?Z`3>Ml&)`d{WEMBIY(mEI5#++ZJi++oJEZ`9xd;}#aCG`~M-#{T zGAjHhnx}*~`I1K%w_m4_;%bmV)qSPH2_+L$RaM)9F&^roVSu)wj5soGtU zqyt@)*`s}oRns1+(S$7_aRDac*Rk%HH3P@>eAK%?XE}leVwKE2F6yP7RVvgAdV*() zWjXlx_@P}AujP5Ma-w?a&MJ8>Q!FCGTghMXgR;cBDRzv)vMyb11+C_N9;=ui> zsK`0)TsC-nWBotakM zQ?e|3#W}^5BlLm_2annHpAV}SV7p;STH4y$Y5?R!f7DbP!f1~fz1x&pj0&mkL&)S` zTugUqxDN8R`yj{#4M{6<~%~4vJRM z2>}yDxOV$HEeDZR!t7*cqEDohF>THU72(7LgXgLoG6 zQyg{o{P9T^1&cfjEij*bffAa$gn~^O;4&10;}f2UO}{<`0g%)l>be4_WK9I){m`vs zlUVeQ-IjIS5$3w2PxFmx+!Zgv0yaZr1}DeHi1T3=uiLsvLg3L>fIz_jarfeRaW$x1 zgG4PwyjFuzlVUQqK?ZJp`KuD3^zGa=vQH3v$QLqU?_;QfNrT>(^QA|Lgg|0tBP~Ce zv?1!qZC{1P_}Y$+4*tF;l=BX!)@X!b10dRscRG4tWej3Km<3&GJ;v8E0KbH&{%SxHmui-4eLJ{5io7 zUzs=P{hE89ajRcmHft59TqWCz*yKlp`FkM!UfY5hnc!_p!ILuvb;(gb3Kc#~+L!Kr zAG8^iCqYRy5%W6gL||vBs+;=$OkbNh$EjO7e3p3MY>Bqdh+4P^UTn17YECmWG<4vH z=~c1`H5b2y&$_hH5qhXxu#uHH+I?6UuCkin?QP!Gq}5BTQ}rcuA+27C1E{p&3bz7R+OLVFN`&{!dN-&4KRbn$I^ z3f!(ZfXf%)bwnei5-HY$y3owZAiB>&ko?kaFnh{R0xmChz{BP+g!x(C?Q*cFCG93&wfK6QJy9oOiUoL?2fC9_a?`sQ`hLSqK;%VfI-Ii$OH~637YUo2B5dy?eZpkBj6-0bsNZphVcCzOe zOvk;7*uV}yW7%Cs&~S**VEdh6?0NvVbF{3%lm;KnJk{>;Ag|0Oi#|JFQ<^awN0MoJ z$gi~FR5wD8`o&-G+@~J4G4xEP9m&en`oki<*uFY`TvrDVfA`$VYRZ z(d*alHnS#>InI>cX+5zfqC(T-Gql{9SAQl}OGoK(7}ZwBl+>%>sd=k1!`) z8;||b43u;4rXC7Hsm(dqro0N|si@@dy4`FoUBQC3Z+Udkfu{{T4aJ23zN literal 0 HcmV?d00001 diff --git a/assets/www/css/jo/back-icon.png b/assets/www/css/jo/back-icon.png deleted file mode 100644 index b2b50eb95ca4b313c2d44b663418b2c36c14c389..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^EI`b`!3HGn8OO1L1d5$JLpXq-h9ji|$mcBZh%5%G zzYfBTP8zc-fP#`Gt`Q}{`DrEPiAAXl<>lpinR(g8$%zH2dih1^v)|cB0Tt P8W}uY{an^LB{Ts5pyf5_ diff --git a/assets/www/css/jo/back-line.png b/assets/www/css/jo/back-line.png deleted file mode 100644 index 032bc2bc53c1e0dee4e198b4d71e977cfc3e4b1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^96&6htlmn&OatyVwKWGs!*TFABa^(PkVht2he*sgX3s2&Q>Sa@ox r6GNJ^n|I$i8CKJ-s>$zuWIbR^lpinR(g8$%zH2dih1^v)|cB0Tt P8W}uY{an^LB{Ts5pyf5_ diff --git a/assets/www/css/jo/bright-shiny.png b/assets/www/css/jo/bright-shiny.png deleted file mode 100644 index a0b4c164fa119f776bdbda589464070e5e229353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^bmSQK*5Dp-y;YjHK@;M7UB8wRq zT-8CC(JQ-U7f?{L#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDFz0{WKS2z zkcv5PZ#QxtHV|MrSRU~I>rzSgl};SfW^(bIJ~>hG{Jr1SDQjom+%Df#eC^DiJbRYs z5e0J&6!yKa;O^V}z@qW7%*8@68TEoW%2wx^9<%iQzEj6=ZWBm?rLWkx3#^2t?=v4r zF;oXjpRk1=M1`=$x%NJwIFbNR<~v9!s!-$OYiIU=9HC=zZuu!BYY!B@I|y+=cF-Kw z * { - position: relative; - top: auto; -} -.flick { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; -} -.flickback { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; -} -.flickfast { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; -} - -jocard { - padding-bottom: 0; -} - -jocontainer { - height: inherit; -} - -joscroller { - display: block; - height: 100%; -} - diff --git a/assets/www/css/jo/dark_matte.png b/assets/www/css/jo/dark_matte.png deleted file mode 100644 index 5964903fe101018d14fb78be2b373c7a628d4d88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^6+j%o!3HER&ED7vq*#ibJVQ8upoSx*1IXtr@Q5sC zU~p9jVMed)mR&$W$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSUU|AW zhE&XXd!v!_uz>)}!O4ODO?Pu8N2FH zV3v-B-vbMEE3lZb#W`u2^n-0cP9>IZM6Z4!6-V#^5)Me9S;!GkCiCxvXd4>qe3smyrTo)LsIJMIXrBZvFmDB%!wlBUzB7!l4jq3gn000j|l9f^u zA;g?9Hbcb6wryts0F1FS5xuo-d$!h|>bic9)x?-0G{*3zX_kn%BqIK$Afh9pQc79o zdG3A;ptYWI&Nl!s4U&G2i*s&QRn_I&0E@-qiHO#rUH6J3Iz)Uq9*?hmIlV=wwVuY9 zXIb{#_l<}+5v{e>y{G2|Fvf7s`6g!PNs_EW5S;UkF~+Oq1!$UP2?Opt1OWikt7aHL zDK$YvZ}Lwd5OJxLngjua5c45LR}{q|27-vV5Mmw#V2sUT%>e+?G~LHS__R9!i1;`J zU9MUx1hqOBYMD%tK^FD1~4GcPg zSo1#Z4uG}xgowd5dW%?dM0D2LQxKr8>vtk5V*z5!0RV`o^bIQBzd%Y^0>GDje26&! zT&|j(@B-v{?woVGn5zhxBcgN8?fRyp(Fc;zN3!5cSXI?UO1TAqeaw|ybM{ioZO9xT naw@*XXk+<3gcyqV(DCY@`?%m>NA@ z978JRyuEdh^RR({>%r21|Fug_o|wKxfu&#a#N7{-*+QS*?A`BtZPv{DZ`&WtF*W}E z&AFhj>9L5+@&^{o$1N`wa`hP(%n_Caa#@dSTAVvj=vOkQ=`oMYa~B}j?;w&qRC#a# zLLR0ZYMi9SIcta>prXddb6x<=1d4U`f%S;Uq(87ww*s2U)>pg;;smIjY#>K80&NAV zU2`WX^{quGX*v;5Y-|q_ZDZW#KhT2rkT^jLFqna$FnBHtibrIPXfc5t(lCup zkFQBsp50M+4`eym%ZPB-kAy@xasYn^*?<}+&#NJc;5$?I)~@M|e*gXhQ!In0tDnm{ Hr-UW|EUZLK diff --git a/assets/www/css/jo/jo.css b/assets/www/css/jo/jo.css deleted file mode 100644 index f30fd71..0000000 --- a/assets/www/css/jo/jo.css +++ /dev/null @@ -1,1031 +0,0 @@ -body { - margin: 0; - padding: 0; - background: #666; - word-wrap: break-word; - overflow: hidden; - font: normal 15px "Apres", "Trebuchet MS"; -} -jobutton, -joview, -jolist, -jolistitem, -jomenu, -jomenuitem, -joexpando, -joexpandotitle, -jogroup, -jocard, -jostack, -jotitle, -jocaption, -jolabel, -jodivider, -joinput, -input, -textarea, -jotextarea, -jooption, -jooptionitem, -jonavbar, -jocontainer, -jotoggle { - display: block; - margin: 0; - padding: 0; - -webkit-user-select: none; - -moz-user-select: none; - -o-user-select: none; - user-select: none; - /* background: transparent; */ - -} -/* -em, i, b, span, u, a, button, input { - display: inline; -} -*/ -.noflex { - -webkit-box-flex: 0; - -moz-box-flex: 0; - -o-box-flex: 0; - box-flex: 0; -} -.flexible { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; -} -.flex { - display: block; - display: -webkit-box; - display: -moz-box; - display: -o-box; - display: box; - margin: 0; - -moz-box-align: stretch; - -o-box-align: stretch; - box-align: stretch; -} -.listitem { - border-top: 1px solid rgba(0, 0, 0, 0.4); - margin: 0; - padding: 10px; - cursor: pointer; -} -.widgety { - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; -} -.shiny { - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - border: 1px solid rgba(0, 0, 0, 0.8); - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; -} -.matte { - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - background-image: url(subtle-matte-full.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 100%; - -webkit-background-size: 100% 100%; - -moz-background-size: 100% 100%; - /* - background-image: -webkit-gradient(linear, left top, left 80%, from(rgba(255, 255, 255, .3)), to(rgba(0, 0, 0, 0))); -*/ - -} -.stretch-full { - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} -.widget { - margin: 0px 10px 10px 10px; -} -*.selected, *.focus { - -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); - -moz-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); - -o-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); -} -.selected { - background-color: #e98021; - background-image: url(shade-top.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; -} -.focus { - background-color: #fff; -} -jooptionitem:last-child { - -webkit-border-radius: 0; - -webkit-border-top-right-radius: 5px; - -webkit-border-bottom-right-radius: 5px; - -moz-border-radius: 0 5px 5px 0; - border-radius: 0 5px 5px 0; - border-right-width: 1px; -} -jobutton { - display: block; - text-align: center; - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - border: 1px solid rgba(0, 0, 0, 0.8); - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} -jobutton .focus { - background-color: #e98021; - background-image: url(shade-top.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; -} -jooption { - display: block; - display: -webkit-box; - display: -moz-box; - display: -o-box; - display: box; - margin: 0; - -moz-box-align: stretch; - -o-box-align: stretch; - box-align: stretch; - margin: 0px 10px 10px 10px; -} -jooptionitem { - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - border: 1px solid rgba(0, 0, 0, 0.8); - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; - text-align: center; - border: 1px solid rgba(0, 0, 0, 0.8); - border-right-width: 0; - margin: 0; -} -jooptionitem:first-child { - -webkit-border-radius: 0; - -webkit-border-top-left-radius: 5px; - -webkit-border-bottom-left-radius: 5px; - -moz-border-radius: 5px 0 0 5px; - border-radius: 5px 0 0 5px; -} -jooptionitem:last-child { - -webkit-border-radius: 0; - -webkit-border-top-right-radius: 5px; - -webkit-border-bottom-right-radius: 5px; - -moz-border-radius: 0 5px 5px 0; - border-radius: 0 5px 5px 0; - border-right-width: 1px; -} -jobutton, -input, -jolabel, -textarea, -joexpando { - margin: 0px 10px 10px 10px; -} -jotitle { - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - background-image: url(subtle-matte-full.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 100%; - -webkit-background-size: 100% 100%; - -moz-background-size: 100% 100%; - /* - background-image: -webkit-gradient(linear, left top, left 80%, from(rgba(255, 255, 255, .3)), to(rgba(0, 0, 0, 0))); -*/ - - background-image: none; - text-align: center; - color: rgba(255, 255, 255, 0.8); - padding: 10px; - background-color: #313539; - font-size: 18px; - margin: 0; - border-top: none; - border-left: none; - border-right: none; -} -*:focus { - outline: none; -} -jolistitem, jomenuitem { - border-top: 1px solid rgba(0, 0, 0, 0.4); - margin: 0; - padding: 10px; - cursor: pointer; -} -jobutton.focus { - background-color: #e98021; - background-image: none; -} -joselect.focus { - background-color: transparent; -} -jolist { - margin: 0; -} -joselectlist, joexpandocontent { - display: block; - border: 1px solid rgba(0, 0, 0, 0.6); - border-top: none; - -webkit-border-radius: 0; - -webkit-border-bottom-right-radius: 10px; - -webkit-border-bottom-left-radius: 10px; - -moz-border-radius: 0 0 10px 10px; - border-radius: 0 0 10px 10px; - background-color: rgba(255, 255, 255, 0.6); -} -joexpando { - display: block; - padding-bottom: 0px; - margin-bottom: 0; -} -joexpando.open { - margin-bottom: 10px; -} -joexpandocontent { - padding-top: 10px; -} -joselectlist > *:last-child { - border-bottom: none; - -webkit-border-radius: 0; - -webkit-border-bottom-right-radius: 10px; - -webkit-border-bottom-left-radius: 10px; - -moz-border-radius: 0 0 10px 10px; - border-radius: 0 0 10px 10px; -} -joselectlist > *:last-child.select { - border-bottom: none; - -webkit-border-radius: 0; - -webkit-border-bottom-right-radius: 10px; - -webkit-border-bottom-left-radius: 10px; - -moz-border-radius: 0 0 10px 10px; - border-radius: 0 0 10px 10px; -} -joselectlist > *.first-child { - border-top: none; -} -jocard > jolist, jocard > jomenu { - margin: 0; -} -jobutton, -jotitle, -jooptionitem, -joexpandotitle, -jotoggle { - text-shadow: 0 0 5px rgba(0, 0, 0, 0.6); -} -joinput.password, input.password { - -webkit-text-security: disc; - -moz-text-security: disc; - -o-text-security: disc; - text-security: disc; -} -joinput, -jotextarea, -input, -textarea { - cursor: text; - display: block; - border: 1px solid rgba(0, 0, 0, 0.6); - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - margin: 0 10px 10px 10px; - padding: 9px; - background: rgba(255, 255, 255, 0.6); - white-space: nowrap; - overflow: hidden; - outline: none; - font-family: "Apres", "Verdana"; - font-size: 17px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0); -} -jolist, jomenu { - font-size: 18px; -} -jotextarea, textarea { - white-space: normal; -} -joinput.disabled, -jotextarea.disabled, -input.disabled, -textarea.disabled { - color: #666; - cursor: text; -} -joexpandotitle joicon { - position: absolute; - border: none; - display: block; - height: 32px; - width: 32px; - right: 4px; - top: 10%; - background: url(expando.png) no-repeat; - -webkit-transform-origin: 16px 16px 0; - -webkit-transform: rotatez(0); - -webkit-transition: -webkit-transform 0.2s ease-out; - -moz-transform-origin: 16px 16px 0; - -moz-transform: rotate(0); - -moz-transition: -moz-transform 0.2s ease-out; - -o-transform-origin: 16px 16px 0; - -o-transform: rotate(0); - -o-transition: -o-transform 0.2s ease-out; - -ms-transform-origin: 16px 16px 0; - -ms-transform: rotate(0); - -ms-transition: -ms-transform 0.2s ease-out; -} -joexpando.open > joexpandotitle joicon { - -webkit-transform: rotatez(90deg); - -moz-transform: rotate(90deg); - -o-transform: rotate(90deg); - -ms-transform: rotate(90deg); -} -joexpando > *:last-child { - height: 0; - overflow: hidden; - opacity: 0; - margin-top: 0; - margin-bottom: 0; - -webkit-transform-style: preserve- 3 d; - -webkit-transform: rotatex(-45deg); - -webkit-transform-origin: 0 0 0; - -webkit-transition: -webkit-transform 0.2s ease-out, opacity 0.2s ease-out, height 0.2s ease-out, overflow 0.2s ease-out; - -moz-transform-style: preserve- 3 d; - -moz-transform: rotatex(-45deg); - -moz-transform-origin: 0 0 0; - -moz-transition: -moz-transform 0.2s ease-out, opacity 0.2s ease-out, height 0.2s ease-out, overflow 0.2s ease-out; - -o-transform-style: preserve- 3 d; - -o-transform: rotatex(-45deg); - -o-transform-origin: 0 0 0; - -o-transform: scaley(1); - -o-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-out, height 0.2s ease-out, overflow 0.2s ease-out; - -ms-transform-style: preserve- 3 d; - -ms-transform: rotatex(-45deg); - -ms-transform-origin: 0 0 0; - -ms-transform: scaley(1); - -ms-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-out, height 0.2s ease-out, overflow 0.2s ease-out; -} -joexpando.open > *:last-child { - height: 100%; - overflow: visible; - opacity: 1; - -webkit-transform: rotatex(0); - -moz-transform: rotatex(0); - -o-transform: rotatex(0); - -ms-transform: rotatex(0); -} -joexpando > *:first-child { - -webkit-transform: none; - opacity: 1; - overflow: visible; - height: auto; -} -joexpandotitle { - color: #fff; - position: relative; - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - border: 1px solid rgba(0, 0, 0, 0.8); - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - padding: 10px; - cursor: pointer; - text-align: left; -} -joexpando.open > *:first-child { - background-color: #e98021; - -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.6); - -moz-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.6); - -o-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.6); - -webkit-border-radius: 0; - -webkit-border-top-right-radius: 10px; - -webkit-border-top-left-radius: 10px; - -moz-border-radius: 10px 10px 0 0; - border-radius: 10px 10px 0 0; -} -jolabel, label { - margin-bottom: 5px; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.6); -} -jocaption { - margin: 10px; -} -johtml { - display: block; - margin: 0 10px 10px 10px; - padding: 0; -} -jodivider { - border-top: 1px solid rgba(0, 0, 0, 0.5); - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - margin: 0 0 10px 0; - text-align: center; - height: 0; - display: block; -} -joflexrow > jotoggle { - -webkit-box-flex: 0; - -moz-box-flex: 0; - -o-box-flex: 0; - box-flex: 0; -} -jotoggle { - cursor: pointer; - display: block; - margin: 10px 10px 0 10px; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - border: 1px solid rgba(0, 0, 0, 0.6); - background-color: inherit; - background-image: url(shade-top.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); - -moz-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); - -o-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.6); - position: relative; - overflow: hidden; - width: 90px; -} -jotoggle > * { - margin: 0px 10px 10px 10px; - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - border: 1px solid rgba(0, 0, 0, 0.8); - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - border-color: #000; - margin: 0; - text-align: center; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; - padding: 10px 0; - width: 60px; - -webkit-box-shadow: 2px 0 2px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 2px 0 2px rgba(0, 0, 0, 0.2); - box-shadow: 2px 0 2px rgba(0, 0, 0, 0.2); - -webkit-transition: -webkit-transform 0.1s ease, background-color 0.1s ease; - -moz-transition: -moz-transform 0.1s ease, background-color 0.1s ease; - -o-transition: -o-transform 0.1s ease, background-color 0.1s ease; - -ms-transition: -ms-transform 0.1s ease, background-color 0.1s ease; -} -jotoggle.on { - background-color: inherit; - background-image: url(shade-top.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; -} -jotoggle.on > * { - -webkit-box-shadow: -2px 0 2px rgba(0, 0, 0, 0.2); - background-color: #e98021; - -webkit-transform: translatex(28px); - -moz-transform: translatex(26px); - -o-transform: translatex(26px); - -ms-transform: translatex(26px); -} -jotable { - display: block; - margin: 0 10px; -} -tr { - margin: 0; -} -th { - text-align: left; - padding: 5px; -} -td { - padding: 5px; -} -jolabel.left { - margin-top: 10px; - padding: 9px 0; -} -.disabled { - opacity: .2; -} -jotoolbar { - padding: 10px; -} -joflexrow { - display: block; - display: -webkit-box; - display: -moz-box; - display: -o-box; - display: box; - margin: 0; - -moz-box-align: stretch; - -o-box-align: stretch; - box-align: stretch; - width: 100%; -} -joflexrow > * { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; - margin-right: 0; - position: relative; -} -joflexrow > *:last-child { - margin-right: 10px; -} -joflexcol { - display: block; - display: -webkit-box; - display: -moz-box; - display: -o-box; - display: box; - margin: 0; - -moz-box-align: stretch; - -o-box-align: stretch; - box-align: stretch; - height: 100%; - width: 100%; - -webkit-box-orient: vertical; - -webkit-box-align: stretch; - -moz-box-orient: vertical; - -moz-box-align: stretch; - -o-box-orient: vertical; - -o-box-align: stretch; - box-orient: vertical; - box-align: stretch; - margin: 0; -} -joflexcol > * { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; -} -jotitle + joflexcol, jotitle + joflexrow { - margin-top: 10px; -} -jostack { - height: 100%; - -webkit-perspective: 400px; - -moz-perspective: 400px; - -o-perspective: 400px; - -ms-perspective: 400px; -} -jostack { - -webkit-transition: opacity 0.3s ease-out; - -moz-transition: opacity 0.3s ease-out; - -o-transition: opacity 0.3s ease-out; - -ms-transition: opacity 0.3s ease-out; -} -jostack > * { - -webkit-transform-origin: 50% 100% 0; - -webkit-transform: rotatez(0) translatez(0); - -webkit-transition: -webkit-transform 0.3s ease-out, z-index 0.3s ease-out, height 0s ease, overflow 0s ease; - -moz-transform-origin: 50% 100% 0; - -moz-transform: rotatez(0) translatez(0); - -moz-transition: -moz-transform 0.3s ease-out, z-index 0.3s ease-out, height 0s ease, overflow 0s ease; - -o-transform-origin: 50% 100% 0; - -o-transform: rotatez(0); - -o-transition: -o-transform 0.3s ease-out; - -ms-transform-origin: 50% 100% 0; - -ms-transform: rotatez(0); - -ms-transition: -ms-transform 0.3s ease-out; -} -jostack > .next { - z-index: -1; - -webkit-transform: rotatez(45deg) translatey(-10%) translatex(100%); - -moz-transform: rotatez(45deg); - -o-transform: rotatez(45deg); - -ms-transform: rotatez(45deg); - height: 100%; - overflow: hidden; -} -jostack > .prev { - z-index: 1; - -webkit-transform: rotatez(-45deg) translatey(10%) translatex(-100%); - -moz-transform: rotatez(-45deg); - -o-transform: rotatez(-45deg); - -ms-transform: rotatez(-45deg); - height: 100%; - overflow: hidden; -} -* { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -jobutton { - background-repeat: repeat-x; -} -jocard { - width: 100%; - min-height: 100%; - display: -webkit-box; - display: -moz-box; - display: -o-box; - display: box; - padding: 0; - margin: 0; - -webkit-box-orient: vertical; - -webkit-box-align: stretch; - -moz-box-orient: vertical; - -moz-box-align: stretch; - -o-box-orient: vertical; - -o-box-align: stretch; - box-orient: vertical; - box-align: stretch; - background: #a6a8b9; -} -jocard > * { - /* display: block; */ - -} -jocard > *:last-child { - margin-bottom: 10px; -} -joscroller { - position: absolute; - display: block; - height: 100%; - width: 100%; - overflow: hidden; - padding: 0; - margin: 0; - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translatey(0); - -o-transform: translate(0, 0); - -ms-transform: translate(0, 0); -} -joscroller > * { - position: absolute; - top: 0; - -webkit-animation-fill-mode: forwards; - -webkit-transition: -webkit-transform 0s ease; - -moz-animation-fill-mode: forwards; - -moz-transition: -moz-transform 0s ease; - -o-animation-fill-mode: forwards; - -o-transition: -o-transform 0s ease; - -ms-animation-fill-mode: forwards; - -ms-transition: -ms-transform 0s ease; -} -.flick { - -webkit-transition: -webkit-transform 1.8s cubic-bezier(0, 0.2, 0, 1); - -moz-transition: -moz-transform 1.8s cubic-bezier(0, 0.2, 0, 1); - -o-transition: -o-transform 1.8s cubic-bezier(0, 0.2, 0, 1); - -ms-transition: -ms-transform 1.8s cubic-bezier(0, 0.2, 0, 1); -} -.flickback { - -webkit-transition: -webkit-transform 0.2s cubic-bezier(0, 0, 0.4, 1); - -moz-transition: -moz-transform 0.2s cubic-bezier(0, 0, 0.4, 1); - -o-transition: -o-transform 0.2s cubic-bezier(0, 0, 0.4, 1); - -ms-transition: -o-transform 0.2s cubic-bezier(0, 0, 0.4, 1); -} -.flickfast { - -webkit-transition: -webkit-transform 0.5s cubic-bezier(0, 0.2, 0, 1); - -moz-transition: -moz-transform 0.5s cubic-bezier(0, 0.2, 0, 1); - -o-transition: -o-transform 0.5s cubic-bezier(0, 0.2, 0, 1); -} -jocontainer { - margin: 0; - display: block; - position: relative; -} -jogroup { - margin: 10px; - padding: 10px 0; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - background: #c8cadb; -} -jogroup > *.last-child { - margin-bottom: 0; -} -jofooter { - display: block; - display: -webkit-box; - display: -moz-box; - display: box; - width: 100%; - -webkit-box-flex: 1; - -webkit-box-orient: vertical; - -webkit-box-align: stretch; - -webkit-box-pack: end; - -moz-box-flex: 1; - -moz-box-orient: vertical; - -moz-box-align: stretch; - -moz-box-pack: end; - box-flex: 1; - box-orient: vertical; - box-align: stretch; - box-pack: end; - margin: 0; -} -jofooter > * { - -webkit-box-align: end; - -moz-box-align: end; - -o-box-align: end; - box-align: end; -} -joshim { - opacity: 0; - overflow: hidden; - position: absolute; - z-index: 100; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: none; - background: rgba(0, 0, 0, 0.6); - opacity: 0; - -webkit-transition: opacity .2s ease; - -moz-transition: opacity .2s ease; - -o-transition: opacity .2s ease; - -ms-transition: opacity .2s ease; -} -joshim.show { - display: block; - opacity: 1; -} -jotoolbar { - border-top: 1px solid rgba(0, 0, 0, 0.8); - padding: 10px 0; - color: rgba(255, 255, 255, 0.9); - background-color: #7e82a1; - font-weight: normal; - cursor: pointer; - outline: none; - outline-color: transparent; - padding: 10px 0; - background-image: url(subtle-matte-full.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 100%; - -webkit-background-size: 100% 100%; - -moz-background-size: 100% 100%; - /* - background-image: -webkit-gradient(linear, left top, left 80%, from(rgba(255, 255, 255, .3)), to(rgba(0, 0, 0, 0))); -*/ - - background-color: rgba(0, 0, 0, 0.8); - z-index: 1; - position: absolute; - bottom: 0; - left: 0; - right: 0; - text-align: center; -} -jotoolbar jobutton { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -jopopup { - -webkit-box-flex: 0; - -moz-box-flex: 0; - -o-box-flex: 0; - box-flex: 0; - font-size: 15px; - display: block; - overflow: hidden; - margin: 0 auto; - border: 2px solid #fff; - -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, 0.6); - -moz-box-shadow: 0 0 20px rgba(0, 0, 0, 0.6); - -o-box-shadow: 0 0 20px rgba(0, 0, 0, 0.6); - -webkit-border-radius: 20px; - -moz-border-radius: 20px; - border-radius: 20px; - background-color: #34c; - color: #fff; - background-image: url(aluminum/shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 20px; - -webkit-transition: -webkit-transform 0.4s ease-in, opacity 0.4s ease-in; - -moz-transition: -moz-transform 0.4s ease-in, opacity 0.4s ease-in; - -o-transition: -o-transform 0.4s ease-in, opacity 0.4s ease-in; - -ms-transition: -m-transform 0.4s ease-in, opacity 0.4s ease-in; - -webkit-transform: scale(0.5); - -moz-transform: scale(0.5); - -o-transform: scale(0.5); - -ms-transform: scale(0.5); - max-width: 280px; - margin: 0 auto; - opacity: 0; -} -jopopup > jolist > *.select:first-childjopopup > jomenu > *.select:first-child { - border-top: none; - -webkit-border-radius: 0; - -webkit-border-top-right-radius: 20px; - -webkit-border-top-left-radius: 20px; - -moz-border-radius: 20px 20px 0 0; - border-radius: 20px 20px 0 0; -} -jopopup > jolist > *:last-child.selectjopopup > jomenu > *:first-child.select { - border-bottom: none; - -webkit-border-radius: 0; - -webkit-border-bottom-right-radius: 20px; - -webkit-border-bottom-left-radius: 20px; - -moz-border-radius: 0 0 20px 20px; - border-radius: 0 0 20px 20px; -} -jopopup.show { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -o-transform: scale(1); - -ms-transform: scale(1); - opacity: 1; -} -jopopup > joscroller { - width: 100%; -} -jonavbar { - background-image: url(shiny.png); - background-repeat: repeat-x; - background-position: top left; - background-size: 100% 50%; - -webkit-background-size: 100% 50%; - -moz-background-size: 100% 50%; - display: block; - position: relative; - margin: 0; - padding: 0; - color: #fff; - border-top: none; - border-right: none; - border-left: none; - text-align: center; - -webkit-box-flex: 0; - -moz-box-flex: 0; - -o-box-flex: 0; - box-flex: 0; - background-color: #333; - cursor: default; -} -jonavbar > joview { - display: block; - text-align: center; - padding: 10px 0; -} -jonavbar > joflexrow { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} -jobackbutton { - background: transparent url(back-mini.png) 10px center no-repeat; - border: 1px solid rgba(0, 0, 0, 0.3); - border-bottom-color: rgba(255, 255, 255, 0.3); - padding: 5px 10px 5px 20px; - margin: 5px 10px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - -webkit-box-shadow: inset 0 0 1px rgba(255, 255, 255, 0.6); - -moz-box-shadow: inset 0 0 1px rgba(255, 255, 255, 0.6); - -o-box-shadow: inset 0 0 1px rgba(255, 255, 255, 0.6); - display: none; - color: #ddd; -} -jobackbutton.focus, jobackbutton.selected { - background-color: rgba(0, 0, 0, 0.5); -} -jobackbutton.active { - display: block; -} -jonavbar > joflexrow > * { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; -} -jonavbar > joflexrow > jobackbutton { - -webkit-box-flex: 0; - -moz-box-flex: 0; - -o-box-flex: 0; - box-flex: 0; - max-width: 4em; -} -jopopup > jotitle { - color: #fff; - -webkit-border-radius: 0; - -webkit-border-top-right-radius: 20px; - -webkit-border-top-left-radius: 20px; - -moz-border-radius: 20px 20px 0 0; - border-radius: 20px 20px 0 0; - background: transparent; -} -jopopup > jobutton { - background-color: rgba(255, 255, 255, 0.4); -} -joflexcol { - height: 100%; - width: 100%; -} -html { - -webkit-text-size-adjust: none; -} -body { - -webkit-backface-visibility: hidden; -} diff --git a/assets/www/css/jo/lite-matte.png b/assets/www/css/jo/lite-matte.png deleted file mode 100644 index b44edd44748561f30164ed74fa152a0c332c45bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^6+pazgAGU?=Ug!XNU;<%q`B`}t>^gb6bB&*Wm7zn#7Pdem##P4i~{^s{fOoi{Ubx%@#p zuD;-cIZcmwWS+Ym*R(iypwO=b$Q6-U{=kCyxaGw{u0G>}Il{6)E-R8eRJq?lBzdTE zsB!5JEcSzBNiiL092>~GMxb+m`k9YQf*gcZXt%$8!(I_Ws<4>#`DPB-yV%YD?+WxJ z*jGSzH$I;80_ - - - - - - - joTest - - - - - - - - - Hello, Jo - This is a sample static HTML page with Jo UI markup. It is designed - to make it easier to design stylesheets which work with Jo. - - - Menu sample - - - Menu Item 1 - Menu Item 2 - Menu Item 3 - - - - Dialog Sample - - Username - Jo - Password - 12345 - Realm - Disabled Input - - - - Go, Jo - Cancel - - - - - diff --git a/assets/www/css/jo/shade-top.png b/assets/www/css/jo/shade-top.png deleted file mode 100644 index 875f54ff40969e021f26fd4f1298592203f0e2de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^6+j%o!3HER&ED7vq*#ibJVQ8upoSx*1IXtr@Q5sC zU~p9jVMed)mR&$W$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSetWt& zhE&XXd!v(+*^q(R@##zLzvfeya3x2iX&LBj+F70RyJyY!ZTH_g7xWz{e4l&&cKZVv z?ml4)zXukLk69p$`v(ifWYi1h94KTu&Iw`cYksVQtY*jax6B38SdT{*%(0IIs(#Mf zxA}nu$e?qgvgsiGK!WM`&P6b7d!S-qgSD;BH321n1jxp7(nyNI5E67IH<9oN+=E*k|v-AN4_U6eqY}T=O$N!_oLR?ly(yEr+qAXP8FD1G)j8!4coZci7- zkcv5PuQhTuI0!HwTyektY?$I9qu_&wh0bT+Hr;fN#ZTDcUC!?Rbu9NcKCozf%+gof z{8-1r?}5eq7Z%)o!XTEp#knS+7?5Cr2-rs!ipiuO1QO~6a}E?T9shZ<%lh`b{qhIP l!CD(1-+6$dnWe9qab_38ov&vt?*pC1;OXk;vd$@?2>_i}VJH9q diff --git a/assets/www/css/jo/shiny-button.png b/assets/www/css/jo/shiny-button.png deleted file mode 100644 index bde7fc0ef0f8eb753b1638087bf20fc1b247a488..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 762 zcmeAS@N?(olHy`uVBq!ia0vp^6+rxfgAGWYZeNfNq*#ibJVQ8upoSx*1IXtr@Q5sC zU~p9jVMed)mR&$W$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@`?%n94m} z978JRyuERdlgUxQ^|Ko|wPTRL#zNwg{PuL>w z?3sOk1?D6lEMz*)DWhI6=KwO}=W3&KO^-q9{2o{|f*I%DKd|6N7C^|vPc0NfGop_D zcqE#cP+R`3>Jhd$r)>o_{~T}MW{?$o7CqL1Sat_)kGTa{(dRCR5|+N=W}J$EW)e1u zC`DlFcBJRmo{rlt->?}a5a50uw2XH@>pP;93uYZC%)ax`1R7!30}>dx&>&~&`>d)B z=0aly=)L$&kFK5BlW))RJOUDtI}gHQIj>+2D=^V~XLdc?kXR{OWC={C44$rjF6*2U FngDwuKcoNv diff --git a/assets/www/css/jo/shiny.png b/assets/www/css/jo/shiny.png deleted file mode 100644 index 2add8895d2516d25e5f562c8d7a0d55fb966c904..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^bmSQK*5Dp-y;YjHK@;M7UB8wRq zT-8CC(JQ-U7f?{L#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDFz0{SWg$n zkcv5PZ#8zdItZ{H4D7kLAa-$t*sN6?b|(2TeY>wky`NAz{~zODb|8n1O-q`r{ zm;MC##>aDBSaA0VTlhV&xc>mmI@j5^`GEyo0>)+O`^?+-Ieu!Pn2dVC9AVk?gN01T zEiVGO&-Ei=OrXqx!hHuZ)u2f{->>yp$KsqhlC?kwG(NW3^oXUec+q1Ph>QByz}$vQ z3g$Y5FTg5wKnAN@oofP`1T+rJZ3MDEpXd^{0O|qz7ooE8aZNMGJxwSo!J)v?_nXmC XKII7i15YPlY%qAb`njxgN@xNA_Cv#6 diff --git a/assets/www/css/jo/subtle-matte-full.png b/assets/www/css/jo/subtle-matte-full.png deleted file mode 100644 index ad8e1dcd4eed158bede2011e178fdcbc29e62026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 784 zcmeAS@N?(olHy`uVBq!ia0vp^6+rxfgAGWYZeNfNq*#ibJVQ8upoSx*1IXtr@Q5sC zU~p9jVMed)mR&$W$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@`?%n0h^3 z978JRyuIz%rEDnRdQfNa?%&tKa%)%>D_jn|k(RR$y=!p(-f!!awKH#Suje=?XpwjJ z%=>Tr2Q9^9)C=Ya%cdVJWIAqnu@KBXPPd-P$ohKs83o3XrLH~U^>nz^PCrE44SDZT$aAiY{!4f z|10z>nR5W5Pgxsml`zO!`AC@Kn2!HE(FJuEnh8Lw-NWXv0__C)>Oi60rbl2WHv--J zd7>!PIY6oYHDF%>2^Nqy(e%JP*97z-%v(Uef_#nQH?RlczJwUd4e}~70d{zyLZJ zVH?aMu;p;u(EJ36$)?BGI_9Y5%`pc#1|Di)*Vmtf1UJb0K)vA5tOj`o8qAgcyqV(DCY@`?%m^wUN z978JRyuIbPO4(3=^a8n?He}hSol4#*uUtpj>WkqAOYm~m&{>39$7HwK;gU>7TkTCA6PU#wz*g+ zCX;@!km-0OR4Gf}XBUtvgi?emgi;h$tj9TJo_B%_1e?m;Ck(Q#@i7Y#j6chF@3{V! z|A7wBbq5Ob3g$?Ij1smu$Jx?^lnB7#gKFFz{$qSYAO6i=`;{;9{FybE`48GL9p{u$FPL+n z5X2~-U&rvRD(#u|Ux7D|U5;B`Ec7dx!+N}u9m*7zeGU_aXn^a)WIS)KpR@nX5r`SW zvgrp4?;Qjg2Q~xAPN>eGs@mt89_v^D36{R%MMx|JSI5HdfrYvim;ftAuwZ(@2B4XO q5QDN1+R)5{S&pz0!2(&nL;m!!ka;`Li)aAjg2B_(&t;ucLK6V_WW!Ye diff --git a/assets/www/css/jo/webos.css b/assets/www/css/jo/webos.css deleted file mode 100644 index 2941173..0000000 --- a/assets/www/css/jo/webos.css +++ /dev/null @@ -1,34 +0,0 @@ -joscroller > * { - -webkit-user-drag: element; -} - -jotoolbar joflexrow jobutton.back { - display: none; -} -jotoolbar { - padding-top: 10px; - padding-bottom: 5px; -} - -jopopup { - max-width: inherit; - margin: 0; - -webkit-border-radius: 20px 20px 0 0; - -webkit-transform: translateY(100%); - border-bottom: none; -} - -jopopup.show { - -webkit-transform: translateY(0); -} - -joshim>joflexcol>*:first-child { - -webkit-box-flex: 1; -} -joshim>joflexcol>*:last-child { - display: none; -} - -jobackbutton.active { - display: none; -} diff --git a/assets/www/css/jq.ui.css b/assets/www/css/jq.ui.css new file mode 100644 index 0000000..50c98d6 --- /dev/null +++ b/assets/www/css/jq.ui.css @@ -0,0 +1,1017 @@ +/********************************************************** + GENERAL UI ELEMENTS +**********************************************************/ + +* { + -webkit-user-select:none; /* Prevent copy paste for all elements except text fields */ + -webkit-tap-highlight-color:rgba(255, 255, 255, 0); /* set highlight color for user interaction */ +} + +input,textarea { -webkit-user-select:text; } /* allow users to select text that appears in input fields */ + +* { + margin:0; + padding:0; +} /* Remove default padding and margins for all elements */ + +img { border:none; } /* Remove default borders for images */ + +body { + overflow-x:hidden; + -webkit-text-size-adjust:none; + font-family:Arial, Helvetica, sans-serif; + color:#fff; + font-size:14px; + background:rgba(19,29,38,1); /* primary background color */ + + + display: -webkit-box; + /* We want to layout our first container vertically */ + -webkit-box-orient: vertical; + /* we want our child elements to stretch to fit the container */ + -webkit-box-align:stretch; + +} /* General styles that apply to elements not contained within other classes and styles */ + +p { + color:#fff; + font-family:Arial, Helvetica, sans-serif; + font-size:14px; + line-height:18px; + background:#131d26; + padding:16px; + -webkit-box-shadow:rgba(0,0,0,0.5) 0px 0px 24px inset; box-shadow:rgba(0,0,0,0.5) 0px 0px 24px inset; /* Applies an inner shadow to the text area */ +} /* Paragraph class used for text areas. */ + + +#jQui_modal{ + -webkit-transform:translate3d(100%,0,0); + -webkit-backface-visibility: hidden; + z-index:9999 !important; + width:100%; + height:100%; + display:none; + position:absolute; top:0px; left:-100%; + overflow:hidden; + background:rgba(29,29,28,1) !important; + -webkit-perspective: 1000; +} + +#modalContainer { +width:100%; +} + +.horzRule { + position:relative; + display:block; + background-image:-webkit-gradient(linear, left top, right top, from(rgba(61,61,61,1)), to(rgba(61,61,61,1)), color-stop(.3,#999), color-stop(.7,#999)); + width:100%; height:1px; +} /* Horizontal line. */ + +.jqmScrollbar { background:white !important; } /* Sets the color of the scrollbar */ + +#jQUi { + position:absolute; + width:100%; + top:0px; + bottom:0px; +} + +#jQUi > #splashscreen { + position:absolute; + top:0px;bottom:0px; + width:100%; + left:0px; + min-height:100%; + background:rgba(29,29,28,1) !important; + color:white !important; + font-size:30px; + text-align:center; + z-index:9999; + display:block; + margin-left: auto !important; margin-right: auto !important; + padding-top:80px !important; +} + + + +/********************************************************** + header +**********************************************************/ + +#jQUi > #header { + display:block; + z-index:250; + -webkit-box-sizing:border-box; box-sizing:border-box; + height:48px; + left:0px;right:0px; + background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(16,25,33,1)), to(rgba(14,23,32,1)), color-stop(.5, rgba(27,36,46,1)), color-stop(.5, rgba(14,23,32,1))); + -webkit-box-shadow:0px 0px 24px rgba(0,0,0,0.8); box-shadow:0px 0px 24px rgba(0,0,0,0.8); +} /* This is masthead bar that appears at the top of the UI */ + +#header > h1 { + position:absolute; + overflow:hidden; + width:100%; + z-index:-11; + left:-10px; + text-align:center; + height:48px; + font-family:'Eurostile-Black', Eurostile, Helvetica, Arial, sans-serif; + font-weight:normal; + font-size:24px; + line-height:48px; + text-shadow:rgba(0,0,0,0.8) 0px -1px 0px 1px; + text-align:center; + text-overflow:ellipsis; + white-space:nowrap; + color:#fff; +} /* This is text that appears in the header at the top of the screen */ + + + + +/********************************************************** + BUTTON STYLES +**********************************************************/ +#backButton { + display:block; + position:absolute; + left:0px; + top:0px; + max-width:50px; + text-overflow:ellipsis; +} /* Sets up positioning of the back button which appears in the header */ + +#backButton > div { + max-width:50px; + overflow:hidden; + text-overflow:ellipsis; +} /* sets up sizing and handling of excess text for the type in the back button */ + +.button { + color:#333; + cursor:pointer; + display:inline-block; + font-weight:bold; + font-size:14px; + line-height:36px; + position:relative; + text-decoration:none; + text-align:center; + text-shadow:#fff 0 1px 0; + height:36px; min-width:40px; + padding:0px 8px; + margin-top:6px; margin-left:6px; + text-overflow:ellipsis; + -webkit-border-radius:6px; border-radius:6px; + background-image:-webkit-gradient(linear, left top, left bottom, from(#fff), to(#999), color-stop(.5,#fff), color-stop(.5,#ccc)); background-image:linear-gradient(left top, left bottom, from(#fff), to(#999), color-stop(.5,#fff), color-stop(.5,#ccc)); +} /* Styling for CSS-generated buttons, including the back button. These buttons can bue used anywhere in the UI. */ + +.button.pressed { + color:#fff; + text-shadow:#2e4987 0 1px 0; + background-color:#5884d6; + border-color:#7488b8; + background-image:-webkit-gradient(linear,left top,left bottom, color-stop(0.01, #eee), color-stop(0.05, #709de7), color-stop(0.70, #5884d6), color-stop(1, #4470ca)); +} /* behavior of button for touch interaction */ + +.modalbutton { + position:absolute; + top:0px; + right:5px; + height:32px; + width:58px; + line-height:32px; + z-index:9999; +} + + + + +/********************************************************** + CONTENT AREA +**********************************************************/ +#content{ + z-index:180; + display:block; + position:absolute; + top:48px; + bottom:62px; + left:0px;right:0px; +} /* Accounts for positioning of the content area, which is everything below the header and above the navbar. */ + +.panel { + -webkit-backface-visibility: hidden; + z-index:180; + width:100%; + height:100%; + display:none; + position:absolute; top:0px; left:-100%; + overflow:hidden; + background:rgba(19,29,38,1); + + -webkit-perspective: 1000; + margin-top:10px; + margin-bottom:10px; +} /* This class is applied to the divs that contain the various "views" or pages of the app. */ +.panel > * { + -webkit-backface-visibility: hidden; + -webkit-perspective: 1000; +} + + + +/********************************************************** + TOOL BAR +**********************************************************/ +/* Tool bar appears locked to the bottom of the screen. It is the primary navigation. can contain text or graphical navigation */ +#navbar { + position:absolute; + bottom:0px; + z-index:1000; + text-align:center; + left:0px;right:0px; + height:62px; padding:0px 1px; + background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(16,25,33,1)), to(rgba(14,23,32,1)), color-stop(.5, rgba(27,36,46,1)), color-stop(.5, rgba(14,23,32,1))); + -webkit-box-shadow:0px 0px 24px rgba(0,0,0,0.8); box-shadow:0px 0px 24px rgba(0,0,0,0.8); +} + +#navbar a { + height:60px; + overflow:hidden; + display:inline-block; + font-family:Helvetica, Arial, sans-serif; + font-weight:bold; + font-size:12px; + text-decoration:none; + color:#a7a7a7; + text-align:center; + text-shadow:rgba(10,12,20,.9) 0px -1px 1px; + margin: 0px auto; + width:62px; + line-height:100px; + position:relative; +} + +#navbar a:active, #navbar a:focus, #navbar a:active:focus, #navbar .selected{ + display:inline-block; + font-family:Helvetica, Arial, sans-serif; + font-weight:bold; + color:#fff !important; + background-image:-webkit-gradient(linear, left top, left bottom, from(#000), to(#000), color-stop(.5,#000), color-stop(.5,rgba(15,22,29,1))); +} + + +/* CUSTOM NAVBAR */ +#navbar a:not(:last-of-type):after { + display: block; + content: ""; + width: 1px; + height: 60px; + background: url('images/vert_divider.png') top right repeat-y; + float:right; +} + +#navbar .icon:before { +line-height:62px; +text-align:center; +position:absolute; +top:-10px; +left:10px; +font-size:48px; +} + +.jq-badge { + display:-webkit-box; + -webkit-box-direction:reverse; + position:absolute; + font-size:10px; + border:2px solid white; + color:white; + background:red; + line-height:12px; + border-radius:10px; + overflow: hidden; + text-overflow: ellipsis; + min-width:20px; + max-width:90%; + height:20px; + margin:2px; + padding: 1px 4px 1px 4px; + top:2px; + right:2px; + text-align:center; + z-index:100; +} + +.jq-badge.br { + bottom:2px; + right:2px; + top:auto; + left:auto; +} + +.jq-badge.bl { + -webkit-box-direction:normal !important; + bottom:2px; + left:2px; + top:auto; + right:auto; +} + +.jq-badge.tl { + -webkit-box-direction:normal !important; + top:2px; + left:2px; + right:auto; + bottom:auto; +} + +.closebutton { + background:url('images/close.png'); + height:23px !important; + width:23px !important; +} + +/* Custom footers - always hidden */ +#jQUi footer { + display:none; +} +/* side menu - always hidden */ +#jQUi nav { + display:none; +} + + +#jQUi > #menu { + z-index:200; + width:200px; + bottom:0px; + height:100%; + display:none; + position:absolute; top:0px; left:-200px; + background:rgba(29,29,28,1); + -webkit-transform:translate3d(0,0,0); + -webkit-transition: all 300ms !important; +} +#menu_scroller { + padding-bottom:10px; +} +#menu > * { + width:90%; + margin-left:auto; + margin-right:auto; +} +.jqMenuHeader +{ + position:relative; + text-align:center; + margin-top:2px; + font-size:24px; + margin-left:auto;margin-right:auto; + font-weight:bold; + margin-bottom:3px; + z-index:1; +} + +.jqMenuClose{ + position:absolute; + top:5px; + right:10px; + z-index:2; +} + +#menu .title { + font-size:16px; + border-bottom:2px solid #333; + margin-bottom:3px; + font-weight:bold; +} + +#menu ul { + margin:0px; + padding:0px; + margin-bottom:5px; +} + +#menu ul > li { + padding-left:10px; + display:block; + width:100%; height:28px; + + list-style:none; + background:inherit; +} /* A plain, non-interactive list item--best suited to a heading. */ + +#menu ul > li > a { + display:block; + width:100%; height:28px; + font-family:'Eurostile-Bold', Eurostile, Helvetica, Arial, sans-serif; + font-weight:normal; + font-size:14px; + border-top:1px solid black; + border-left:1px solid black; + border-right:1px solid black; + line-height:28px; + text-decoration:none; + border-bottom:none; + color:#fff; + background:#333; +} + +#menu ul > li > a:after { + content:""; +} + +#menu ul > li:last-child > a{ + border-bottom:1px solid black; +} + + + +/* End side menu css */ + +.splashscreen { + background:rgba(29,29,28,1) !important; + padding-left:40px; + padding-top:30px !important; + min-height:100%; +} + +/* A touchable, interactive list item. */ +/********************************************************** + UL > LI +**********************************************************/ +/* The unordered list/list item classes are the basis of the secondary navigation used in JQ.Mobi: the stacked, listed menu system. */ + +ul { + margin:0px; + padding:0px; +} + +ul > li { + + display:block; + width:100%; height:48px; + list-style:none; + background:#39424b; +} /* A plain, non-interactive list item--best suited to a heading. */ + +ul > li > a { + display:block; + width:100%; height:48px; + font-family:'Eurostile-Bold', Eurostile, Helvetica, Arial, sans-serif; + font-weight:bold; + font-size:18px; + line-height:48px; + text-decoration:none; + border-bottom:1px solid black; + color:#fff; + padding-left:8px; + padding-right:-80px; +} /* A touchable, interactive list item. */ + +ul > li > a:after { + content:">"; + position:absolute; + right:15px; +} + + ul > li > a[selected], ul > li > a:active { + color:#fff !important; + +} /* A selected and active states for interactive list items. */ + +ul > li.group { + position:relative; + top:-1px; + margin-bottom:-2px; + border-top:1px solid #7d7d7d; + border-bottom:1px solid #999999; + padding:1px 10px; + background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0.09, rgb(125,125,125)), color-stop(0.49, rgb(153,153,153)), color-stop(0.81, rgb(153,153,153))); + font-size:17px; + font-weight:bold; + text-shadow:rgba(0, 0, 0, 0.4) 0px 1px 0px; + color:#fff; +} + +ul > li.group:first-child { + top:0; +} + +h2 { + display:block; + width:100%; height:34px; + font-family:'Eurostile-Bold', Eurostile, Helvetica, Arial, sans-serif; + font-weight:bold; + font-size:18px; + line-height:34px; + padding: 0px 8px 0px 8px; + color:#fff; + text-shadow:rgba(0,0,0,.9) 0px -1px 1px; + background:#39424b; +} /* Header class used for non-navigable header bars (h1 is reserved for the header) */ + +.collapsed:after{content:url("images/collapse.png");position:absolute;top:5px;right:5px;} +.expanded:after{content:url("images/expand.png");position:absolute;top:5px;right:5px;} + + +/********************************************************** + UI +**********************************************************/ +.ui-icon { + background: #666; + background: rgba(0,0,0,.4); + background-repeat: no-repeat; + border-radius: 9px; +} + +.ui-loader { display: none; position: absolute; opacity: .85; z-index: 100; left: 50%; width: 200px; margin-left: -100px; margin-top: -35px; padding: 10px 30px; background: #666;background:rgba(0,0,0,.4);border-radius:9px;} +.ui-loader h1 { font-size: 15px; text-align: center; } +.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; } + +.spin { + -webkit-transform: rotate(360deg); + -webkit-animation-name: spin; + -webkit-animation-duration: 1s; + -webkit-animation-iteration-count: infinite; +} +@-webkit-keyframes spin { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} +} + +.ui-icon-loading { + background-image: url(images/ajax-loader.png); + width: 40px; + height: 40px; + border-radius: 20px; + background-size: 35px 35px; +} + +.ui-corner-all { + border-radius:.6em; +} +#jQui_mask {position:absolute;top:45%;} + + +/********************************************************** + FORM ELEMENTS +**********************************************************/ + +* { -webkit-box-sizing:border-box; box-sizing:border-box; } + +fieldset h4 { + text-align:left; + font-family:'Eurostile-Bold', Eurostile, Helvetica, Arial, sans-serif; + color:#a8b4bb; + font-weight:bold; + font-size:18px; + line-height:21px; + margin-bottom:-8px; margin-top:8px; + text-shadow:1px 1px 3px rgba(0, 0, 0, 0.9); +} /* Style for subhead for a group of labels/inputs within a fieldset. */ + + +fieldset { + margin:12px 8px; + padding:12px 18px 12px 18px; + -webkit-border-radius:5px; + border-radius:5px; + border:1px solid #595d61; + outline:0; + width:95%; + background:url('images/stripe_bg.png') repeat; +} /* Styles the border line, background image and spacing for fieldsets. */ + +fieldset p { + display:block; + position:relative; + overflow:hidden; + padding:0px; + padding-bottom:8px; + padding-top:8px; + font-family:Arial, Helvetica, sans-serif; + font-size:14px; + line-height:10px; + background:transparent; + height:37px !important; + -webkit-box-shadow:none; +} /* The paragraph within a fieldset is used as a wrapper to help manage the replacement of native input elements. */ + +legend { + margin:0; + font-family:'Eurostile-Bold', Eurostile, Helvetica, Arial, sans-serif; + font-size:18px; + font-weight:bold; + overflow:hidden; + color:#ccc; + text-transform:uppercase; + text-shadow:1px 1px 3px rgba(0, 0, 0, 0.9); +} /* This is the name that appears at the top left of the fieldset. */ + +textarea.jq-ui-forms { + display:block; + margin-top:4px; margin-bottom:18px; + padding:8px; + border:solid 1px #999; + outline:0; + color:#fff; + background:#595D61; + -webkit-border-radius:6px; border-radius:6px; + -webkit-box-shadow:inset 2px 2px 6px rgba(0,0,0,.5); box-shadow:inset 2px 2px 6px rgba(0,0,0,.5); + width:85%; + min-height:150px; + line-height:120%; +} /* These properties determine the look of textarea inputs. */ + + +input.jq-ui-forms { + + display:inline-block; + width:65%; + margin-top:4px; margin-bottom:12px; + padding:8px; + border:solid 1px #999; + outline:0; + color:#fff; + background:#595D61; + -webkit-border-radius:6px; border-radius:6px; + -webkit-box-shadow:inset 2px 2px 6px rgba(0,0,0,.5); box-shadow:inset 2px 2px 6px rgba(0,0,0,.5); +} /* These properties combine to create the look of the input fields. */ + + + +input.jq-ui-forms:hover, textarea.jq-ui-forms:hover, input.jq-ui-forms:focus, textarea.jq-ui-forms:focus { border-color:#ccc; } /* Changes the border color of the input field while active. */ + + +input.jq-ui-forms[type=checkbox] ,input.jq-ui-forms[type=radio] { + position: absolute; + left: 0; + -webkit-appearance: none; + opacity:0; + margin-bottom:30px; + width:0px; +} /* Blocks rendering of the native radio controls. */ + +input.jq-ui-forms[type=checkbox] + label,input.jq-ui-forms[type=radio] + label { + + float:left; + font-size:15px; + font-weight:bold; + line-height:26px; /* changing this value will change the vertical relationship to the radios & checkboxes. */ + margin-left:10px; + padding-left:30px; + background:url('./images/custom_inputs.png') 0 0px no-repeat; + background-repeat:none; + height:25px; + +} /* Styling for the labels. */ + + +/* Following block of paragraph classes sets up the usage of the custom radio and checkbox graphics. */ + + +input.jq-ui-forms[type=radio] + label { background-position:0 -225px; } +input.jq-ui-forms[type=checkbox] + label { background-position:0 -25px; } + +/* Styles for "checked." */ +input.jq-ui-forms[type=radio]:checked + label { background-position:0 -300px; } +input.jq-ui-forms[type=radio]:hover:checked + label, +input.jq-ui-forms[type=radio]:focus:checked + label, +input.jq-ui-forms[type=radio]:checked + label:hover, +input.jq-ui-forms[type=radio]:focus:checked + label { background-position:0 -300px; } + +input.jq-ui-forms[type=checkbox]:checked + label { background-position:0 -100px; } +input.jq-ui-forms[type=checkbox]:hover:checked + label, +input.jq-ui-forms[type=checkbox]:focus:checked + label, +input.jq-ui-forms[type=checkbox]:checked + label:hover, +input.jq-ui-forms[type=checkbox]:focus:checked + label { background-position:0 -100px; } + + +/* Styles for "hover/focus." */ +input.jq-ui-forms[type=checkbox]:hover + label, +input.jq-ui-forms[type=checkbox]:focus + label, +input.jq-ui-forms[type=checkbox] + label:hover { background-position:0 -0x; } + +input.jq-ui-forms[type=radio]:hover + label, +input.jq-ui-forms[type=radio]:focus + label, +input.jq-ui-forms[type=radio] + label:hover { background-position:0 -200px; } + +/* Styles for "active." */ +input.jq-ui-forms[type=checkbox]:active + label, +input.jq-ui-forms[type=checkbox] + label:hover:active { background-position:0 -0px; } + +input.jq-ui-forms[type=radio]:active + label, +input.jq-ui-forms[type=radio] + label:hover:active { background-position:0 -200px; } + + +input.jq-ui-forms[type=checkbox]:active:checked + label, +input.jq-ui-forms[type=checkbox]:checked + label:hover:active { background-position:0 -100px; } + +input.jq-ui-forms[type=radio]:active:checked + label, +input.jq-ui-forms[type=radio]:checked + label:hover:active { background-position:0 -300px; } + +/* Styles for "disabled." */ +input.jq-ui-forms[type=checkbox]:disabled + label, +input.jq-ui-forms[type=checkbox]:hover:disabled + label, +input.jq-ui-forms[type=checkbox]:focus:disabled + label, +input.jq-ui-forms[type=checkbox]:disabled + label:hover, +input.jq-ui-forms[type=checkbox]:disabled + label:hover:active { background-position:0 -175px; opacity:.5 !important; } + +input.jq-ui-forms[type=radio]:disabled + label, +input.jq-ui-forms[type=radio]:hover:disabled + label, +input.jq-ui-forms[type=radio]:focus:disabled + label, +input.jq-ui-forms[type=radio]:disabled + label:hover, +input.jq-ui-forms[type=radio]:disabled + label:hover:active { background-position:0 -250px; opacity:.5 !important; } + +input.jq-ui-forms[type=checkbox]:disabled:checked + label, +input.jq-ui-forms[type=checkbox]:hover:disabled:checked + label, +input.jq-ui-forms[type=checkbox]:focus:disabled:checked + label, +input.jq-ui-forms[type=checkbox]:disabled:checked + label:hover, +input.jq-ui-forms[type=checkbox]:disabled:checked + label:hover:active { background-position:0 -200px; opacity:.5 !important; } + +input.jq-ui-forms[type=radio]:disabled:checked + label, +input.jq-ui-forms[type=radio]:hover:disabled:checked + label, +input.jq-ui-forms[type=radio]:focus:disabled:checked + label, +input.jq-ui-forms[type=radio]:disabled:checked + label:hover, +input.jq-ui-forms[type=radio]:disabled:checked + label:hover:active { background-position:0 -375px; opacity:.5 !important; } + + + + + /******** slider controls */ +input.jq-ui-slider { + position:absolute; + left:0; + opacity:0; + -webkit-appearance: none; +} /* Blocks rendering of the native radio controls. */ + + +input.jq-ui-slider + label { + + float:left; + font-size:15px; + font-weight:normal; + line-height:24px; /* changing this value will change the vertical relationship to the radios & checkboxes. */ + margin-left:10px; + padding-left:57px; + color:#fff; + background:url('images/Off-On_Slider.png') 0 0px no-repeat; + height:24px; + width:57px; + display:inline-block; + -webkit-transition: all 0.3s ease-in-out; + +} +input.jq-ui-forms[type=radio]:active:checked + label, +input.jq-ui-forms[type=checkbox]:active:checked + label, +input.jq-ui-forms[type=radio]:active + label, +input.jq-ui-forms[type=checkbox]:active + label, +input.jq-ui-slider[type=checkbox]:checked + label , +input.jq-ui-slider[type=radio]:checked + label +{ background-position:-28px 0px; } + +input.jq-ui-slider[type=radio]:disabled:checked + label, +input.jq-ui-slider[type=radio]:focus:disabled:checked + label, +input.jq-ui-slider[type=checkbox]:disabled:checked + label, +input.jq-ui-slider[type=checkbox]:focus:disabled:checked + label +{ background-position:-28px 0px; opacity:.7 !important;} + +/* Styles for "disabled." */ +input.jq-ui-slider[type=checkbox]:disabled + label, +input.jq-ui-slider[type=checkbox]:focus:disabled + label, +input.jq-ui-slider[type=radio]:disabled + label, +input.jq-ui-slider[type=radio]:focus:disabled + label +{ background-position:0 0; opacity:.7 !important; } + + + +select[disabled]~div { + opacity:.7; +} + + +.hasMenu{ +-webkit-transition: all 300ms; + position:absolute !important; + left:0px !important; +} +.hasMenu.on { + position:absolute !important; + left:200px !important; + +} +#menu.on { +left:0px !important; +} + +#badgetablet{ + display:none !important; +} + +@media handheld, only screen and (min-width: 768px){ + .hasMenu, .hasMenu.on { + position:absolute !important; + left:256px !important; + -webkit-transition: all 0ms !important; + } + #badgephone{ + display:none !important; + } + #badgetablet{ + display:inline-block !important; + } + .jqMenuClose { + display:none !important; + } + + nav~#menu { + z-index:200 !important; + width:256px !important; + bottom:0px !important; + height:100% !important; + display:block !important; + position:absolute !important; top:0px !important; left:0px !important; + background:rgba(29,29,28,1) !important; + -webkit-transform:translate3d(0,0,0) !important; + } + +} + +/** Below are styles for plugins */ +#jq_actionsheet { + position:absolute; + left:0px; + right:0px; + padding-left:10px; + padding-right:10px; + padding-top:10px; + margin:auto; + background:black; + float:left; + z-index:1000; + border-top:#fff 1px solid; + background:rgba(71,71,71,.95); + background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.4)), to(rgba(255,255,255,0)), color-stop(.08,rgba(255,255,255,.1)), color-stop(.08,rgba(255,255,255,0))); + background-image:-webkit-linear-gradient(top, rgba(255,255,255,.4) 0%, rgba(255,255,255,.1) 8%, rgba(255,255,255,0) 8%); + box-shadow:0px -1px 2px rgba(0,0,0,.4); +} +#jq_actionsheet a { + text-decoration:none; + -webkit-transition:all 0.4s ease; + text-shadow:0px -1px rgba(0,0,0,.8); + padding:0px .25em; + border:1px solid rgba(0,0,0,.8); + text-overflow:ellipsis; + -webkit-border-radius:.75em; border-radius:.75em; + background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.4)), to(rgba(255,255,255,0)), color-stop(.5,rgba(255,255,255,.1)), color-stop(.5,rgba(255,255,255,0))); + background-image:-webkit-linear-gradient(top, rgba(255,255,255,.5) 0%, rgba(255,255,255,.2) 50%, rgba(255,255,255,0) 50%); + -webkit-box-shadow:0px 1px 1px rgba(255,255,255,0.7); box-shadow:0px 1px 1px rgba(255,255,255,0.7); + display:block; + color:white; + text-align:center; + line-height:36px; + font-size:20px; + font-weight:bold; + margin-bottom:10px; + background-color:rgba(130,130,130,1); +} + +#jq_actionsheet a.selected { + background-color:rgba(150,150,150,1); +} + +#jq_actionsheet a.cancel { + background-color:rgba(43,43,43,1); +} + +#jq_actionsheet a.cancel.selected { + background-color:rgba(73,73,73,1); +} +#jq_actionsheet a.red { + color:white; + background-color:rgba(204,0,0,1); +} + +#jq_actionsheet a.red.selected { + background-color:rgba(255,0,0,1); +} + +BODY>DIV#mask{ + display:block; + width:100%; + height:100%; + background:#000; + z-index: 999999; + position:absolute; + top:0; + left:0; +} + +.jqPopup { + display: block; + width: 300px; + border: solid 1px #72767b; + -webkit-box-shadow: 0px 4px 6px #555, 0 0 20px rgba(255,255,255,0.5); + -webkit-border-radius: 10px; + padding: 10px; + opacity: 1; + -webkit-transform: scale(1); + -webkit-transition: all 0.20s ease-in-out; + position: absolute; + z-index: 1000000; + margin-left: auto!important; + margin-right: auto!important; + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(70,70,70,0.8)), to(rgba(0,20,20,0.8))); +} +.jqPopup >* { +color:white; +} + +.jqPopup.hidden { + opacity: 0; + -webkit-transform: scale(0); + top: 50%; + left: 50%; + margin: 0px auto; +} + +.jqPopup>HEADER{ + font-weight:bold; + font-size:20px; + margin:0; + padding:0; +} + +.jqPopup>DIV{ + font-size:12px; + margin:8px; +} + +.jqPopup>FOOTER{ + width:100%; + text-align:center; + display:block !important; +} + +.jqPopup>FOOTER>A#cancel{ + float:left; +} + +.jqPopup>FOOTER>A#action{ + float:right; + margin-right:4px; +} + +.jqPopup>FOOTER>A.center{ + float:none!important; + width:80%; + margin:8px; +}/** This can be your default scrollbar class. You must use !important to override the default inline styles */ +.scrollBar { + position:absolute !important; + width:5px !important; + height:20px !important; + border-radius:2px !important; + border:1px soldid black !important; + background:black !important; + opacity:0 !important; +}/* + * Since the styles are built in, you have to override values with !important + */ + +/* Row of selected item */ +.jqmobiSelectRowFound { +} + +/* Button (radio) for the found item in the list */ +.jqmobiSelectRowButtonFound{ +} + + +/* Row for items in the list */ +.jqmobiSelectRow{ + +} + +/* button for the items in the list */ +.jqmobiSelectRowButton{ + +} + +/* class for the item text displayed */ +.jqmobiSelectRowText{ +} + +/* Header for select box */ +#jqmobiSelectBoxHeader{ +} + +/* div that holds the options listed*/ +#jqmobiSelectBoxFix { + +} \ No newline at end of file diff --git a/assets/www/css/ui.css b/assets/www/css/ui.css index 09d072b..47fa224 100644 --- a/assets/www/css/ui.css +++ b/assets/www/css/ui.css @@ -1,52 +1,75 @@ -/* Tafl board */ -jotaflboard { - display: block; - margin: 6px; +#board { + display: table; + + width: 100%; + height: 100%; + + border: 1px solid white; + + margin-left: auto; + margin-right: auto; } -jocard { - margin: auto; - - text-align: center; +#board .row { + display: table-row; } -jotaflboard { - border-collapse: collapse; +#board .square { + display: table-cell; - margin-left: 6px; - margin-right: 6px; + background-repeat: no-repeat; + background-position: center center; + background-size: 100%; } -jotaflboard td { - padding: 0px; - width: 27px; - height: 27px; +#game_container { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; - border: 1px solid grey; + padding: 5px; + padding-top: 0px; + padding-bottom: 15px; - background-repeat: no-repeat !important; - background-position: center center !important; - background-size: 100% 100% !important; + overflow: hidden; } -jotaflboard td.W { +#game_panel > div { + height: 100%; +} + +.white { background-image: url(white.png); } -jotaflboard td.B { +.black { background-image: url(black.png); } -jotaflboard td.K { +.king { background-image: url(king.png); } -jotaflboard td.throne { +.throne { background-color: #ccc; } -jotaflboard td.selected { - background-color: yellow; +.active { + background-color: #dd6; } + +@media only screen and (orientation:landscape) { + #header, #menu { + display: none; + } + + #navbar { + display: block; + } +} + + diff --git a/assets/www/index.html b/assets/www/index.html index 8175fae..de4ef16 100644 --- a/assets/www/index.html +++ b/assets/www/index.html @@ -1,24 +1,66 @@ - + + - - PhoneGap - - - - - - - - - - - - - - - - - - - + + Androtafl + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/www/js/jo.js b/assets/www/js/jo.js deleted file mode 100644 index b52122c..0000000 --- a/assets/www/js/jo.js +++ /dev/null @@ -1,6146 +0,0 @@ -/** - joLog - ===== - - Wrapper for `console.log()` (or whatever device-specific logging you have). Also could - be extended to send log information to a RESTful service as well, handy for devices - which don't have decent logging abilities. - - Use - --- - - It's an all-in-one utility that's smart enough to ferret out whatever you throw at it - and display it in the console. - - joLog("x=", x, "listdata=", listdata); - - Basically, fill it up with strings, variables, objects, arrays and the function will - produce a string version of each argument (where appropriate; browser debuggers tend to - display objects nicely) in the same console line. Simple, effective, easy to use. - -*/ - -joLog = function() { - var strings = []; - - for (var i = 0; i < arguments.length; i++) { - // TODO: stringify for objects and arrays - strings.push(arguments[i]); - } - - // spit out our line - console.log(strings.join(" ")); -} -/** - - - - - - jo - == - - Singleton which the framework uses to store global infomation. It also is - responsible for initializing the rest of the framework, detecting your environment, - and notifying your application when jo is ready to use. - - Methods - ------- - - - `load()` - - This method should be called after your DOM is loaded and before your app uses - jo. Typically, you can call this function from your document's `onLoad` method, - but it is recommended you use more device-specific "ready" notification if - they are available. - - - `getPlatform()` - - Returns the platform you're running in as a string. Usually this is not needed, - but can be useful. - - - `getVersion()` - - Returns the version of jo you loaded in the form of a string (e.g. `0.1.1`). - - - `matchPlatform(string)` - - Feed in a string list of desired platforms (e.g. `"mozilla chrome ipad"`), - and returns true if the identified platform is in the test list. - - Events - ------ - - - `loadEvent` - - `unloadEvent` - - These events are fired after jo loads or unloads, and can be used in your - application to perform initialization or cleanup tasks. - - Function - ======== - - jo extends the Function object to add a few goodies which augment JavaScript - in a farily non-intrusive way. - - Methods - ------- - - - `extend(superclass, prototype)` - - Gives you an easy way to extend a class using JavaScript's natural prototypal - inheritance. See Class Patterns for more information. - - - `bind(context)` - - Returns a private function wrapper which automagically resolves context - for `this` when your method is called. - - HTMLElement - =========== - - This is a standard DOM element for JavaScript. Most of the jo views, continers - and controls deal with these so your application doesn't need to. - - Methods - ------- - - Not a complete list by any means, but the useful ones for our - purposes are: - - - `appendChild(node)` - - `insertChild(before, node)` - - `removeChild(node)` - - Properties - ---------- - - jo uses these properties quite a bit: - - - `innerHTML` - - `className` - - `style` - -*/ - -// syntactic sugar to make it easier to extend a class -Function.prototype.extend = function(superclass, proto) { - // create our new subclass - this.prototype = new superclass(); - - // optional subclass methods and properties - if (proto) { - for (var i in proto) - this.prototype[i] = proto[i]; - } -}; - -// add bind() method if we don't have it already -if (typeof Function.prototype.bind === 'undefined') { - Function.prototype.bind = function(context) { - var self = this; - - function callbind() { - return self.apply(context, arguments); - } - - return callbind; - }; -} - -// hacky kludge for hacky browsers -if (typeof HTMLElement === 'undefined') - HTMLElement = Object; - -// no console.log? sad... -if (typeof console === 'undefined' || typeof console.log !== 'function') - console = {log: function(msg) { }}; - -// just a place to hang our hat -jo = { - platform: "webkit", - version: "0.4.1", - - useragent: [ - 'ipad', - 'iphone', - 'webos', - 'bada', - 'android', - 'opera', - 'chrome', - 'safari', - 'mozilla', - 'gecko', - 'explorer' - ], - - debug: false, - setDebug: function(state) { - this.debug = state; - }, - - flag: { - stopback: false - }, - - load: function(call, context) { - joDOM.enable(); - - this.loadEvent = new joSubject(this); - this.unloadEvent = new joSubject(this); - - // capture these events, prevent default for applications - document.body.onMouseDown = function(e) { e.preventDefault(); }; - document.body.onDragStart = function(e) { e.preventDefault(); }; - - // quick test to see which environment we're in - if (typeof navigator == 'object' && navigator.userAgent) { - var agent = navigator.userAgent.toLowerCase(); - for (var i = 0; i < this.useragent.length; i++) { - if (agent.indexOf(this.useragent[i]) >= 0) { - this.platform = this.useragent[i]; - break; - } - } - } - - if (joEvent) { - // detect if we're on a touch or mouse based browser - var o = document.createElement('div'); - var test = ("ontouchstart" in o); - if (!test) { - o.setAttribute("ontouchstart", 'return;'); - test = (typeof o.ontouchstart === 'function'); - } - joEvent.touchy = test; - o = null; - } - - if (joGesture) - joGesture.load(); - - var s = joScroller.prototype; - - // setup transition css hooks for the scroller - if (typeof document.body.style.webkitTransition !== "undefined") { - // webkit, leave everything alone - } - else if (typeof document.body.style.MozTransition !== "undefined") { - // mozilla with transitions - s.transitionEnd = "transitionend"; - s.setPosition = function(x, y, node) { - node.style.MozTransform = "translate(" + x + "px," + y + "px)"; - }; - } - else if (typeof document.body.style.msTransform !== "undefined") { - // IE9 with transitions - s.transitionEnd = "transitionend"; - s.setPosition = function(x, y, node) { - node.style.msTransform = "translate(" + x + "px," + y + "px)"; - }; - } - else if (typeof document.body.style.OTransition !== "undefined") { - // opera with transitions - s.transitionEnd = "otransitionend"; - s.setPosition = function(x, y, node) { - node.style.OTransform = "translate(" + x + "px," + y + "px)"; - }; - } - else { - // no transitions, disable flick scrolling - s.velocity = 0; - s.bump = 0; - s.transitionEnd = "transitionend"; - s.setPosition = function(x, y, node) { - if (this.vertical) - node.style.top = y + "px"; - - if (this.horizontal) - node.style.left = x + "px"; - }; - } - - joLog("Jo", this.version, "loaded for", this.platform, "environment"); - - this.loadEvent.fire(); - }, - - tagMap: {}, - tagMapLoaded: false, - - // make a map of node.tagName -> joView class constructor - initTagMap: function() { - // we only do this once per session - if (this.tagMapLoaded) - return; - - var key = this.tagMap; - - // defaults - key.JOVIEW = joView; - key.BODY = joScreen; - - // run through all our children of joView - // and add to our joCollect.view object - for (var p in window) { - var o = window[p]; - if (typeof o === 'function' - && o.prototype - && typeof o.prototype.tagName !== 'undefined' - && o.prototype instanceof joView) { - var tag = o.prototype.tagName.toUpperCase(); - - if (o.prototype.type) { - // handle tags with multiple types - if (!key[tag]) - key[tag] = {}; - - key[tag][o.prototype.type] = o; - } - else { - key[tag] = o; - } - } - } - }, - - getPlatform: function() { - return this.platform; - }, - - matchPlatform: function(test) { - return (test.indexOf(this.platform) >= 0); - }, - - getVersion: function() { - return this.version; - }, - - getLanguage: function() { - return this.language; - } -}; - -/** - joDOM - ====== - - Singleton with utility methods for manipulating DOM elements. - - Methods - ------- - - - `get(id)` - - Returns an HTMLElement which has the given id or if the - id is not a string returns the value of id. - - - `create(type, style)` - - Type is a valid HTML tag type. Style is the same as `setStyle()` - method. Returns an HTMLElement. - - // simple - var x = joDOM.create("div", "mycssclass"); - - // more interesting - var x = joDOM.create("div", { - id: "name", - className: "selected", - background: "#fff", - color: "#000" - }); - - - `setStyle(tag, style)` - - Style can be an object literal with - style information (including "id" or "className") or a string. If - it's a string, it will simply use the style string as the className - for the new element. - - Note that the preferred and most cross-platform method for working - with the DOM is to use `className` and possibly `id` and put your - actual style information in your CSS file. That said, sometimes it's - easier to just set the background color in the code. Up to you. - - - `getParentWithin(node, ancestor)` - - Returns an HTMLElement which is - the first child of the ancestor which is a parent of a given node. - - - `addCSSClass(HTMLElement, classname)` - - Adds a CSS class to an element unless it is already there. - - - `removeCSSClass(HTMLElement, classname)` - - Removes a CSS class from an element if it exists. - - - `toggleCSSClass(HTMLElement, classname)` - - Auto add or remove a class from an element. - - - `pageOffsetLeft(HTMLElement)` and `pageOffsetHeight(HTMLElement)` - - Returns the "true" left and top, in pixels, of a given element relative - to the page. - - - `applyCSS(css, stylenode)` - - Applies a `css` string to the app. Useful for quick changes, like backgrounds - and other goodies. Basically creates an inline `'; - - document.body.appendChild(css); - - return css; - }, - - removeCSS: function(node) { - document.body.removeChild(node); - }, - - loadCSS: function(filename, oldnode) { - // you can just replace the source for a given - // link if one is passed in - if (oldnode) - var css = oldnode; - else - var css = joDOM.create('link'); - - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = filename + (jo.debug ? ("?" + joTime.timestamp()) : ""); - - if (!oldnode) - document.body.appendChild(css); - - return css; - }, - - pageOffsetLeft: function(node) { - var l = 0; - - while (typeof node !== 'undefined' && node && node.parentNode !== window) { - if (node.offsetLeft) - l += node.offsetLeft; - - node = node.parentNode; - } - - return l; - }, - - pageOffsetTop: function(node) { - var t = 0; - - while (typeof node !== 'undefined' && node && node.parentNode !== window) { - t += node.offsetTop; - node = node.parentNode; - } - - return t; - } -}; - -joCSSRule = function(data) { - this.setData(data); -}; -joCSSRule.prototype = { - container: null, - - setData: function(data) { - this.data = data || ""; - this.enable(); - }, - - clear: function() { - this.setData(); - }, - - disable: function() { - joDOM.removeCSS(this.container); - }, - - enable: function() { - this.container = joDOM.applyCSS(this.data, this.container); - } -}; -/** - joEvent - ======== - - Singleton with DOM event model utility methods. Ideally, application-level - code shouldn't have to use this, but library code does. - - Methods - ------- - - `on(HTMLElement, event, Function, context, data)` - - Set a DOM event listener for an HTMLElement which calls a given Function - with an optional context for `this` and optional static data. Returns a - reference to the handler function, which is required if you need to `remove()` - later. - - - `capture(HTMLElement, event, function, context, data)` - - This is the same os `on()`, but captures the event at the node before its - children. If in doubt, use `on()` instead. - - - `remove(HTMLElement, event, handler)` - - Removes a previously declared DOM event. Note that `handler` is the return - value of the `on()` and `capture()` methods. - - - `stop(event)` - - Stop event propogation. - - - `preventDefault(event)` - - Prevent default action for this event. - - - `block(event)` - - Useful for preventing dragging the window around in some browsers, also highlighting - text in a desktop browser. - - - `getTarget(event)` - - Returns the HTMLElement which a DOM event relates to. - -*/ - -joEvent = { - eventMap: { - "mousedown": "touchstart", - "mousemove": "touchmove", - "mouseup": "touchend", - "mouseout": "touchcancel" - }, - touchy: false, - - getTarget: function(e) { - if (!e) - var e = window.event; - - return e.target ? e.target : e.srcElement; - }, - - capture: function(element, event, call, context, data) { - return this.on(element, event, call, context, data, true); - }, - - on: function(element, event, call, context, data, capture) { - if (!call || !element) - return false; - - if (this.touchy) { - if (this.eventMap[event]) - event = this.eventMap[event]; - } - - var element = joDOM.get(element); - var call = call; - var data = data || ""; - - function wrappercall(e) { - // support touchy platforms, - // might reverse this to turn non-touch into touch - if (e.touches && e.touches.length == 1) { - var touches = e.touches[0]; - e.pageX = touches.pageX; - e.pageY = touches.pageY; - e.screenX = touches.screenX; - e.screenY = touches.screenY; - e.clientX = touches.clientX; - e.clientY = touches.clientY; - } - - if (context) - call.call(context, e, data); - else - call(e, data); - }; - - // annoying kludge for Mozilla - wrappercall.capture = capture || false; - - if (!window.addEventListener) - element.attachEvent("on" + event, wrappercall); - else - element.addEventListener(event, wrappercall, capture || false); - - return wrappercall; - }, - - remove: function(element, event, call, capture) { - if (this.touchy) { - if (this.eventMap[event]) { - event = this.eventMap[event]; - } - } - - if (typeof element.removeEventListener !== 'undefined') - element.removeEventListener(event, call, capture || false); - }, - - stop: function(e) { - if (e.stopPropagation) - e.stopPropagation(); - else - e.cancelBubble = true; - }, - - preventDefault: function(e) { - e.preventDefault(); - }, - - block: function(e) { - if (window.event) - var e = window.event; - - if (typeof e.target == 'undefined') - e.target = e.srcElement; - - switch (e.target.nodeName.toLowerCase()) { - case 'input': - case 'textarea': - return true; - break; - default: - return false; - } - } -}; -/** - joSubject - ========== - - Class for custom events using the Observer Pattern. This is designed to be used - inside a subject to create events which observers can subscribe to. Unlike - the classic observer pattern, a subject can fire more than one event when called, - and each observer gets data from the subject. This is very similar to YUI 2.x - event model. - - You can also "lock" the notification chain by using the `capture()` method, which - tells the event to only notify the most recent subscriber (observer) which requested - to capture the event exclusively. - - Methods - ------- - - - `subscribe(Function, context, data)` - - Both `context` and `data` are optional. Also, you may use the `Function.bind(this)` - approach instead of passing in the `context` as a separate argument. - All subscribers will be notified when the event is fired. - - - `unsubscribe(Function, context)` - - Does what you'd think. The `context` is only required if you used one when - you set up a subscriber. - - - `capture(Function, context, data)` - - Only the last subscriber to capture this event will be notified until it is - released. Note that you can stack `capture()` calls to produce a modal event - heiarchy. Used in conjunction with the `resume()` method, you can build an - event chain where each observer can fire the next based on some decision making. - - - `release(Function, context)` - - Removes the most recent subscription called with `capture()`, freeing up the next - subscribers in the list to be notified the next time the event is fired. - - - `fire(data)` - - Calls subscriber methods for all observers, and passes in: `data` from the subject, - a reference to the `subject` and any static `data` which was passed in the - `subscribe()` call. - - - `resume(data)` - - If you used `capture()` to subscribe to this event, you can continue notifying - other subscribers in the chain with this method. The `data` parameter, as in - `fire()`, is optional. - - Use - --- - - ### In the subject (or "publisher") object - - // inside the Subject, we setup an event observers can subscribe to - this.changeEvent = new joSubject(this); - - // to fire the event inside the Subject - this.changeEvent.fire(somedata); - - ### In the observer (or "subscriber") object - - // simple case, using Function.bind() - somesubject.changeEvent.subscribe(this.mymethod.bind()); - - // explicit context (this) - somesubject.changeEvent.subscribe(this.mymethod, this); - - // optional data which gets passed with the event fires - somesubject.changeEvent.subscribe(this.mymethod, this, "hello"); - - This is a very flexible way to handle messages between objects. Each subject - may have multiple events which any number of observer objects can subscribe - to. - -*/ -joSubject = function(subject) { - this.subscriptions = []; - this.subject = subject; -}; -joSubject.prototype = { - last: -1, - - subscribe: function(call, observer, data) { - if (!call) - return false; - - var o = { "call": call }; - - if (observer) - o.observer = observer; - - if (data) - o.data = data; - - this.subscriptions.push(o); - - return this.subject; - }, - - unsubscribe: function(call, observer) { - if (!call) - return false; - - for (var i = 0, l = this.subscriptions.length; i < l; i++) { - var sub = this.subscriptions[i]; - if (sub.call === call && (typeof sub.observer === 'undefined' || sub.observer === observer)) { - this.subscriptions.splice(i, 1); - break; - } - } - - return this.subject; - }, - - resume: function(data) { - if (this.last != -1) - this.fire(data, true); - - return this.subject; - }, - - fire: function(data, resume) { - if (typeof data === 'undefined') - data = ""; - - var i = (resume) ? (this.last || 0) : 0; - - // reset our call stack - this.last = -1; - - for (var l = this.subscriptions.length; i < l; i++) { - var sub = this.subscriptions[i]; - var subjectdata = (typeof sub.data !== 'undefined') ? sub.data : null; - - if (sub.observer) - sub.call.call(sub.observer, data, this.subject, subjectdata); - else - sub.call(data, this.subject, subjectdata); - - // if this subscriber wants to capture events, - // stop calling other subscribers - if (sub.capture) { - this.last = i + 1; - break; - } - } - - return this.subject; - }, - - capture: function(call, observer, data) { - if (!call) - return false; - - var o = { "call": call, capture: true }; - - if (observer) - o.observer = observer; - - if (data) - o.data = data; - - this.subscriptions.unshift(o); - - return this.subject; - }, - - release: function(call, observer) { - return this.unsubscribe(call, observer); - } -}; -/** - joTime - ====== - - Time utility functions. More will be added, but only as needed by the - framework. There are entire libraries dedicated to extensive datetime - manipulation, and Jo doesn't pretend to be one of them. - - Methods - ------- - - - `timestamp()` - - Returns a current timestamp in milliseconds from 01/01/1970 from - the system clock. - - Constants - --------- - - - `SEC`, `MIN`, `HOUR`, `DAY` - - Convenience global constants which make it easier to manipulate - timestamps. - - Use - --- - - var twoHoursLater = joTime.timestamp() + (HOUR * 2); - -*/ - -var SEC = 1000; -var MIN = 60 * SEC; -var HOUR = 60 * MIN; -var DAY = 24 * HOUR; - -joTime = { - timestamp: function() { - var now = new Date(); - return now / 1; - } -}; -/** - joDefer - ======= - - Utility function which calls a given method within a given context after `n` - milliseconds with optional static data. - - Use - ----- - - joDefer(Function, context, delay, data); - - Note that delay defaults to 100ms if not specified, and `data` is optional. - - joYield - ======= - - Deprecated, use joDefer instead. - -*/ -function joDefer(call, context, delay, data) { - if (!delay) - var delay = 100; - - if (!context) - var context = this; - - var timer = window.setTimeout(function() { - call.call(context, data); - }, delay); - - return timer; -}; -joYield = joDefer;/** - joCache - ======= - - A singleton which makes it easy to setup deferred object creation and cached - results. This is a performance menchanism initially designed for UI views, but - could be extended to handle data requests and other object types. - - Methods - ------- - - - `set(key, call, context)` - - Defines a factory (`call`) for building an object keyed from the `key` string. - The `context` argument is optional, but provides a reference for `this`. - - - `get(key)` - - Returns an object based on the `key` string. If an object has not been created - which corresponds to the `key`, joCache will call the constructor defined to - create it and store the reference for future calls to `get()`. - - Use - --- - - Defining a view for on-demand use: - - joCache.set("home", function() { - return new joCard([ - new joTitle("Home"), - new joMenu([ - "Top Stories", - "Latest News", - "Old News", - "No News" - ]) - ]); - }); - - Displaying a view later: - - mystack.push(joCache.get("home")); - - // the first call to get() will instantiate - // the view, subsequent calls will return the - // view that was created the first time - - // you can pass parameters into your view factory - var x = joCache.get("home", "My Title"); - - // note that if you want to use joCache to cache - // views which differ based on parameters passed in, - // you probably want your own caching mechanism instead. - -*/ - -joCache = { - cache: {}, - - set: function(key, call, context) { - if (call) - this.cache[key] = { "call": call, "context": context || this }; - }, - - get: function(key) { - var cache = this.cache[key] || null; - if (cache) { - if (!cache.view) - cache.view = cache.call.apply(cache.context, arguments); - - return cache.view; - } - else { - return new joView("View not found: " + key); - } - } -}; - -/** - joChain - ======== - - Class which strings asyncronous calls together. - - > In serious need of rework; doesn't meet original goal of sequencing - > these calls. This class might also become deprecated. - - Methods - ------- - - - `add(Function, context, data)` - - `start()` - - `stop()` - - `next()` - -*/ - -joChain = function() { - this.queue = []; - this.active = false; - - this.addEvent = new joSubject("add", this); - this.startEvent = new joSubject("start", this); - this.stopEvent = new joSubject("stop", this); - this.nextEvent = new joSubject("next", this); - - this.stop(); - - this.delay = 100; -}; -joChain.prototype = { - add: function(call, context, data) { - if (!context) - var context = this; - - if (!data) - var data = ""; - - this.queue.push({ - "call":call, - "context": context, - "data": data - }); - - if (this.active && !this.timer) - this.next(); - }, - - start: function() { - this.active = true; - - this.startEvent.fire(); - - this.next(); - }, - - stop: function() { - this.active = false; - - if (this.timer != null) - window.clearTimeout(this.timer); - - this.timer = null; - - this.stopEvent.fire(); - }, - - next: function() { - var nextcall = this.queue.shift(); - - if (!nextcall) { - this.timer = null; - return; - } - - this.nextEvent.fire(nextcall); - - nextcall.call.call(nextcall.context, nextcall.data); - - if (this.queue.length) - this.timer = joEvent.yield(this.next, this, this.delay); - else - this.timer = null; - } -}; -/** - joClipboard - =========== - - Singleton which abstracts the system clipboard. Note that this is a platform - dependant interface. By default, the class will simply store the contents in - a special joPreference named "joClipboardData" to provide clipboard capabilities - within your app. - - > Even if you think you're just going to use the default behavior, it is - > recommended that you never manipulate the "joClipboardData" preference directly. - - Methods - ------- - - - `get()` - - `set(String)` - - Low level methods which use just strings. At this time, you will need to - stringify your own data when setting, and extract your data when getting. - - - `cut(joControl)` - - `copy(joControl)` - - `paste(joControl)` - - High level methods which work with any joControl or subclass. If a control - supports selections, `cut()` will automatically remove the selection after - copying its contents. Otherwise, `cut()` will work the same as `copy()`. - - > Note: this is not working yet, steer clear (or contribute some working code!) - -*/ -joClipboard = { - data: "", - - get: function() { - return joPreference.get("joClipboardData") || this.data; - }, - - set: function(clip) { - // don't feed it junk; stringify it first - // TODO: detect non-strings and stringify them - this.data = clip; - joPreference.set("joClipboardData"); - } -}; -/* - not used at this time -*/ - -/** - joDataSource - ============= - - Wraps data acquisition in an event-driven class. Objects can - subscribe to the `changeEvent` to update their own data. - - This base class can be used as-is as a data dispatcher, but is - designed to be extended to handle asynchronous file or SQL queries. - - Methods - ------- - - `set()` - - `get()` - - `clear()` - - `setQuery(...)` - - `getQuery()` - - `load()` - - `refresh()` - - Events - ------ - - - `changeEvent` - - `errorEvent` - - > Under construction, use with care. - -*/ -joDataSource = function(data) { - this.changeEvent = new joSubject(this); - this.errorEvent = new joSubject(this); - - if (typeof data !== "undefined") - this.setData(data); - else - this.data = ""; -}; -joDataSource.prototype = { - autoSave: true, - data: null, - - setQuery: function(query) { - this.query = query; - }, - - setAutoSave: function(state) { - this.autoSave = state; - return this; - }, - - setData: function(data) { - var last = this.data; - this.data = data; - - if (data !== last) - this.changeEvent.fire(data); - }, - - getData: function() { - return this.data; - }, - - getDataCount: function() { - return this.getData().length; - }, - - getPageCount: function() { - if (this.pageSize) - return Math.floor(this.getData().length / this.pageSize) + 1; - else - return 1; - }, - - getPage: function(index) { - var start = index * this.pageSize; - var end = start + this.pageSize; - - if (end > this.getData().length) - end = this.getData().length; - - if (start < 0) - start = 0; - - return this.data.slice(start, end); - }, - - refresh: function() { - // needs to make a new query object - }, - - setPageSize: function(length) { - this.pageSize = length; - }, - - getPageSze: function() { - return this.pageSize; - }, - - load: function(data) { - this.data = data; - this.changeEvent.fire(data); - }, - - error: function(msg) { - this.errorEvent.fire(msg); - } -}; -/** - joRecord - ======== - - An event-driven wrapper for an object and its properties. Useful as a - data interface for forms and other collections of UI controls. - - Extends - ------- - - - joDataSource - - Methods - ------- - - - `link(property)` - - Returns a reference to a joProperty object which can be used with UI - controls (children of joControl) to automatically save or load data - based on user interaction. - - - `save()` - - Saves the object's data. The base class does not itself save the data; - you will need to make your own action for the save method, or have - something which subscribes to the `saveEvent`. - - - `load()` - - Loads the object's data, and fires off notifications to any UI controls - which are linked to this joRecord object. Same as the `save()` method, - you will have to make this function do some actual file loading if that's - what you want it to do. - - - `getProperty(property)` - - `setProperty(property, value)` - - Get or set a given property. Used in conjunction with `setAutoSave()`, - `setProprty()` will also trigger a call to the `save()` method. - - - `getDelegate(property)` - - Returns a reference to the joProperty object which fires off events - for data changes for that property. If none exists, one is created. - This method is used by the `link()` method, and can be overriden if - you extend this class to provide some other flavor of a joDataSource - to manage events for your properties. - - Use - --- - - // setup a joRecord - var r = new joRecord({ - user: "Jo", - password: "1234", - active: true - }); - - // bind it to some fields - var x = new joGroup([ - new joLabel("User"), - new joInput(r.link("user")), - new joLabel("Password"), - new joPasswordInput(r.link("password")), - new joFlexBox([ - new joLabel("Active"), - new joToggle(r.link("active")) - ]) - ]); - - And if you want the data to be persistent, or interact with some - cloud service, you'll need to do something like this: - - // make something happen to load the data - r.load = function() { - // some AJAX or SQL call here - }; - - // make something happen to save the data - r.save = function() { - // some AJAX or SQL call here - }; - - You could also make your own subclass of joRecord with your own save - and load methods using `extend()` like this: - - var preferences = function() { - // call to the superclass constructor - joRecord.apply(this, arguments); - }; - preferences.extend(joRecord, { - save: function() { - // do an AJAX or SQL call here - }, - - load: function() { - // do an AJAX or SQL call here - } - } - - See Class Patterns for more details on this method of "subclassing" - in JavaScript. - -*/ -joRecord = function(data) { - joDataSource.call(this, data); - this.delegate = {}; -}; -joRecord.extend(joDataSource, { - link: function(p) { - return this.getDelegate(p); - }, - - getDelegate: function(p) { - if (!this.delegate[p]) - this.delegate[p] = new joProperty(this, p); - - return this.delegate[p]; - }, - - getProperty: function(p) { - return this.data[p]; - }, - - setProperty: function(p, data) { - if (this.data[p] === data) - return; - - this.data[p] = data; - this.changeEvent.fire(this); - - if (this.autoSave) - this.save(); - - return this; - }, - - load: function() { - console.log("TODO: extend the load() method"); - return this; - }, - - save: function() { - console.log("TODO: extend the save() method"); - return this; - } -}); - -/** - joProperty - ========== - - Used by joRecord to provide an event-driven binding to properties. - This class is instantiated by joRecord and not of much use on its own. - - Extends - ------- - - - joDataSource - - Use - --- - - See joRecord for examples. -*/ -joProperty = function(datasource, p) { - joDataSource.call(this); - - this.changeEvent = new joSubject(this); - datasource.changeEvent.subscribe(this.onSourceChange, this); - - this.datasource = datasource; - this.p = p; -}; -joProperty.extend(joDataSource, { - setData: function(data) { - if (this.datasource) - this.datasource.setProperty(this.p, data); - - return this; - }, - - getData: function() { - if (!this.datasource) - return null; - - return this.datasource.getProperty(this.p); - }, - - onSourceChange: function() { - this.changeEvent.fire(this.getData()); - } -}); -/** - - - - - - joDatabase - =========== - - Wrapper class for WebKit SQLite database. - - Methods - ------- - - - `open(datafile, size)` - - `datafile` is a filename, `size` is an optional parameter for initial - allocation size for the database. - - - `close()` - - - `now()` - - *Deprecated* convenience method which returns a SQLite-formatted date - string for use in queries. Should be replaced with a utility function - in joTime. -*/ -joDatabase = function(datafile, size) { - this.openEvent = new joEvent.Subject(this); - this.closeEvent = new joEvent.Subject(this); - this.errorEvent = new joEvent.Subject(this); - - this.datafile = datafile; - this.size = size || 256000; - this.db = null; -}; -joDatabase.prototype = { - open: function() { - this.db = openDatabase(this.datafile, "1.0", this.datafile, this.size); - - if (this.db) { - this.openEvent.fire(); - } - else { - joLog("DataBase Error", this.db); - this.errorEvent.fire(); - } - }, - - close: function() { - this.db.close(); - this.closeEvent.fire(); - }, - - now: function(offset) { - var date = new Date(); - - if (offset) - date.setDate(date.valueOf() + (offset * 1000 * 60 * 60 * 24)); - - return date.format("yyyy-mm-dd"); - } -}; -/** - joSQLDataSource - ================ - - SQL flavor of joDataSource which uses "HTML5" SQL found in webkit. - - Methods - ------- - - - `setDatabase(joDatabase)` - - `setQuery(query)` - - `setParameters(arguments)` - - `execute(query, arguments)` - - Events - ------ - - - `changeEvent` - - Fired when data is loaded after an `execute()` or when data is cleared. - - - `errorEvent` - - Fired when some sort of SQL error happens. - - Extends - ------- - - - joDataSource -*/ -joSQLDataSource = function(db, query, args) { - this.db = db; - this.query = (typeof query == 'undefined') ? "" : query; - this.args = (typeof args == 'undefined') ? [] : args; - - this.changeEvent = new joEvent.subject(this); - this.errorEvent = new joEvent.subject(this); -}; -joSQLDataSource.prototype = { - setDatabase: function(db) { - this.db = db; - }, - - setQuery: function(query) { - this.query = query; - }, - - setData: function(data) { - this.data = data; - this.changeEvent.fire(); - }, - - clear: function() { - this.data = []; - this.changeEvent.fire(); - }, - - setParameters: function(args) { - this.args = args; - }, - - execute: function(query, args) { - this.setQuery(query || ""); - this.setParameters(args); - - if (this.query) - this.refresh(); - }, - - refresh: function() { - if (!this.db) { - this.errorEvent.fire(); -// joLog("query error: no db!"); - return; - } - - var self = this; - - if (arguments.length) { - var args = []; - for (var i = 0; i < arguments.length; i++) - args.push(arguments[i]); - } - else { - var args = this.args; - } - - var query = this.query; - - function success(t, result) { - self.data = []; - - for (var i = 0, l = result.rows.length; i < l; i++) { - var row = result.rows.item(i); - - self.data.push(row); - } - - self.changeEvent.fire(self.data); - } - - function error() { - joLog('SQL error', query, "argument count", args.length); - self.errorEvent.fire(); - } - - this.db.db.transaction(function(t) { - t.executeSql(query, args, success, error); - }); - } -}; -/** - joFileSource - ============ - - A special joDataSource which loads and handles a file. This class - wraps joFile. - - Extends - ------- - - - `joDataSource` - -*/ -joFileSource = function(url, timeout) { - this.changeEvent = new joSubject(this); - this.errorEvent = new joSubject(this); - - if (timeout) - this.setTimeout(timeout); - - if (url) - this.setQuery(url); -}; -joFileSource.extend(joDataSource, { - baseurl: '', - query: '', - - load: function() { - var get = this.baseurl + this.query; - - joFile(get, this.callBack, this); - }, - - callBack: function(data, error) { - if (error) - this.errorEvent.fire(error); - else - this.setData(data); - } -}); - -/** - joFile - ====== - - A utility method which uses XMLHttpRequest to load a text-like file - from either a remote server or a local file. - - > Note that some browsers and mobile devices will *not* allow you to - > load from just any URL, and some will restrict use with local files - > especially (I'm looking at you, FireFox). - > - > If your aim is to load JavaScript-like data (also, JSON), you may want - > to look at joScript instead, which uses script tags to accomplish the job. - - Calling - ------- - - joFile(url, call, context, timeout) - - Where - ----- - - - `url` is a well-formed URL, or, in most cases, a relative url to a local - file - - - `call` is a function to call when the operation completes - - - `context` is an optional scope for the function to call (i.e. value of `this`). - You can also ignore this parameter (or pass in `null` and use `Function.bind(this)` - instead. - - - `timeout` is an optional parameter which tells joFile to wait, in seconds, - for a response before throwing an error. - - Use - --- - - // simple call with a global callback - var x = joFile("about.html", App.loadAbout); - - // an inline function - var y = joFile("http://joapp.com/index.html", function(data, error) { - if (error) { - console.log("error loading file"); - return; - } - - console.log(data); - }); -*/ -joFile = function(url, call, context, timeout) { - var req = new XMLHttpRequest(); - - if (!req) - return onerror(); - - // 30 second default on requests - if (!timeout) - var timeout = 60 * SEC; - - var timer = (timeout > 0) ? setTimeout(onerror, timeout) : null; - - req.open('GET', url, true); - req.onreadystatechange = onchange; - req.onError = onerror; - req.send(null); - - function onchange(e) { - if (timer) - timer = clearTimeout(timer); - - if (req.readyState == 4) - handler(req.responseText, 0); - } - - function onerror() { - handler(null, true); - } - - function handler(data, error) { - if (call) { - if (context) - call.call(context, data, error); - else - call(error, data, error); - } - } -} - -/** - joScript - ======== - - Script tag loader function which can be used to dynamically load script - files or make RESTful calls to many JSON services (provided they have some - sort of callback ability). This is a low-level utility function. - - > Need a URL with some examples of this. - - Calling - ------- - - `joScript(url, callback, context, errorcallback, errorcontext)` - - - url - - callback is a function (supports bind, in which case context is optional) - - context (usually `this`, and is optional) - - Returns - ------- - - Calls your handler method and passes a truthy value if there was an error. - - Use - --- - - joScript("myscript.js", function(error, url) { - if (error) - console.log("script " + url + " didn't load."); - }, this); - -*/ -function joScript(url, call, context) { - var node = joDOM.create('script'); - - if (!node) - return; - - node.onload = onload; - node.onerror = onerror; - node.src = url; - document.body.appendChild(node); - - function onerror() { - handler(true); - } - - function onload() { - handler(false); - } - - function handler(error) { - if (call) { - if (context) - call.call(context, error, url); - else - call(error, url); - } - - document.body.removeChild(node); - node = null; - } -} - -/** - joPreference - ============ - - A class used for storing and retrieving preferences in your application. - - *The interface for this is changing.* joPreference will become a specialized - application-level extension of joRecord in the near future. Until then, you - should use joRecord to achieve this use-case. - - Extends - ------- - - - joRecord - -*/ - -// placeholder for now -joPreference = joRecord; -/** - joYQL - ===== - - A joDataSource geared for YQL RESTful JSON calls. YQL is like SQL, but for cloud - services. Pretty amazing stuff: - - > The Yahoo! Query Language is an expressive SQL-like language that lets you query, - > filter, and join data across Web services. With YQL, apps run faster with fewer lines of - > code and a smaller network footprint. - > - > Yahoo! and other websites across the Internet make much of their structured data - > available to developers, primarily through Web services. To access and query these - > services, developers traditionally endure the pain of locating the right URLs and - > documentation to access and query each Web service. - > - > With YQL, developers can access and shape data across the Internet through one - > simple language, eliminating the need to learn how to call different APIs. - - [Yahoo! Query Language Home](http://developer.yahoo.com/yql/) - - Use - --- - - A simple one-shot use would look like: - - // setup our data source - var yql = new joYQL("select * from rss where url='http://davebalmer.wordpress.com'"); - - // subscribe to load events - yql.loadEvent.subscribe(function(data) { - joLog("received data!"); - }); - - // kick off our call - yql.exec(); - - A more robust example with parameters in the query could look something - like this: - - // quick/dirty augmentation of the setQuery method - var yql = new joYQL(); - yql.setQuery = function(feed, limit) { - this.query = "select * from rss where url='" - + feed + "' limit " + limit - + " | sort(field=pubDate)"; - }; - - // we can hook up a list to display the results - var list = new joList(yql).attach(document.body); - list.formatItem = function(data, index) { - var html = new joListItem(data.title + " (" + data.pubDate + ")", index); - }; - - // later, we make our call with our parameters - yql.exec("http://davebalmer.wordpress.com", 10); - - Methods - ------- - - `setQuery()` - - Designed to be augmented, see the example above. - - - `exec()` - - Extends - ------- - - - joDataSource - -*/ - -joYQL = function(query) { - joDataSource.call(this); - - this.setQuery(query); -}; -joYQL.extend(joDataSource, { - baseurl: 'http://query.yahooapis.com/v1/public/yql?', - format: 'json', - query: '', - - exec: function() { - var get = this.baseurl + "q=" + encodeURIComponent(this.query) - + "&format=" + this.format + "&callback=" + joDepot(this.load, this); - - joScript(get, this.callBack, this); - }, - - load: function(data) { - var results = data.query && data.query.results && data.query.results.item; - - if (!results) - this.errorEvent.fire(data); - else { - this.data = results; - this.changeEvent.fire(results); - } - }, - - callBack: function(error) { - if (error) - this.errorEvent.fire(); - } -}); - - -/* - Used by joYQL for RESTful calls, may be abstracted into - a restful superclass, but that will be dependant on a - callback paramter as well. -*/ -joDepotCall = []; -joDepot = function(call, context) { - joDepotCall.push(handler); - - function handler(data) { - if (context) - call.call(context, data); - else - call(data); - }; - - return "joDepotCall[" + (joDepotCall.length - 1) + "]"; -}; -/** - joInterface - =========== - - *EXPERIMENTAL* - - > This utility method is experimental! Be very careful with it. *NOTE* that - > for now, this class requires you to remove whitespace in your HTML. If you - > don't know a good approach offhand to do that, then this thing probably isn't - > ready for you yet. - - This class parses the DOM tree for a given element and attempts to - attach appropriate joView subclasses to all the relevant HTML nodes. - Returns an object with references to all elements with the `id` - attribute set. This method helps turn HTML into HTML + JavaScript. - - Use - --- - - // an HTML element by its ID - var x = new joInterface("someid"); - - // a known HTML element - var y = new joInterface(someHTMLElement); - - // the entire document body (careful, see below) - var z = new joInterface(); - - Returns - ------- - - A new object with a property for each element ID found. For example: - - - - Login - - Username - - Password - - - Login - - - Parsed with this JavaScript: - - // walk the DOM, find nodes, create controls for each - var x = new joInterface("login"); - - Produces these properties: - - - `x.login` is a reference to a `new joCard` - - `x.username` is a reference to a `new joInput` - - `x.password` is a reference to a `new joPassword` - - `x.loginbutton` is a reference to a `new joButton` - - This in essence flattens your UI to a single set of properties you can - use to access the controls that were created from your DOM structure. - - In addition, any unrecognized tags which have an `id` attribute set will - also be loaded into the properties. - - Parsing complex trees - --------------------- - - Yes, you can make a joInterface that encapsulates your entire UI with HTML. - This is not recommended for larger or more complex applications, some - reasons being: - - - Rendering speed: if you're defining multiple views within a `` - (or another subclass of joContainer), your users will see a flicker and - longer load time while the window renders your static tags and the extra - views for the stack are removed from view. - - - Double rendering: again with `` tags, you're going to see a separate - render when the first view is redrawn (has to). - - - Load time: especially if you're doing a mobile app, this could be a biggie. - You are almost always going to be better off building the app controls with - JavaScript (especially in conjunction with joCache, which only creates DOM - nodes for a given view structure on demand). - - If you really want to use HTML as your primary means of defining your UI, you're - better off putting your major UI components inside of a `
` (or other tag) - with `display: none` set in its CSS property. Like this: - - -
- - About this app - - This is my app, it is cool. - - Done - - - ... etc ... - -
- - Then in your JavaScript: - - // pull in all our card views from HTML - var cards = new joInterface("cards"); - - Definitely use this class judiciously or you'll end up doing a lot of recatoring - as your application grows. - - Flattening UI widget references - ------------------------------- - - This is both good and bad, depending on your coding style and complexity of - your app. Because all the tags with an ID attribute (regardless of where they - are in your tag tree) get a single corresponding property reference, things - could get very messy in larger apps. Again, be smart. - -*/ -joInterface = function(parent) { - // initialize our tag lookup object - jo.initTagMap(); - - // surprise! we're only using our prototype once and - // just returning references to the nodes with ID attributes - return this.get(parent); -}; -joInterface.prototype = { - get: function(parent) { - parent = joDOM.get(parent); - - if (!parent) - parent = document.body; - - var ui = {}; - - // pure evil -- seriously - var setContainer = joView.setContainer; - var draw = joView.draw; - - parse(parent); - - // evil purged - joView.setContainer = setContainer; - joView.draw = draw; - - function parse(node) { - if (!node) - return; - - var args = ""; - - // handle all the leaves first - if (node.childNodes && node.firstChild) { - // spin through child nodes, build our list - var kids = node.childNodes; - args = []; - - for (var i = 0, l = kids.length; i < l; i++) { - var p = parse(kids[i]); - - if (p) - args.push(p); - } - } - - // make this control - return newview(node, args); - } - - // create appropriate joView widget from the tag type, - // otherwise return the node itself - function newview(node, args) { - var tag = node.tagName; - var view = node; - -// console.log(tag, node.nodeType); - - if (jo.tagMap[tag]) { - if (args instanceof Array && args.length) { - if (args.length == 1) - args = args[0]; - } - - if (args instanceof Text) - args = node.nodeData; - - if (!args) - args = node.value || node.checked || node.innerText || node.innerHTML; - -// console.log(args); - - joView.setContainer = function() { - this.container = node; - - return this; - }; - - if (typeof jo.tagMap[tag] === "function") { - var o = jo.tagMap[tag]; - } - else { - var t = node.type || node.getAttribute("type"); - var o = jo.tagMap[tag][t]; - } - - if (typeof o === "function") - var view = new o(args); - else - joLog("joInterface can't process ", tag, "'type' attribute?"); - } - - // keep track of named controls - if (node.id) - ui[node.id] = view; - - return view; - } - - // send back our object with named controls as properties -// console.log(ui); - return ui; - } -}; -/** - joCollect - ========= - - *DEPRECATED* use joInterface instead. This function is planned - to die when jo goes beta. - -*/ -joCollect = { - get: function(parent) { - // this is what happens when you announced something not - // quite fully baked - return new joInterface(parent); - } -}; -/** - joView - ======= - - Base class for all other views, containers, controls and other visual doo-dads. - - Use - ----- - - var x = new joView(data); - - Where `data` is either a text or HTML string, an HTMLElement, or any joView object - or subclass. - - Methods - ------- - - - `setData(data)` - - `getData()` - - `createContainer(type, classname)` - - `setContainer(HTMLElement)` - - `getContainer()` - - `clear()` - - `refresh()` - - - `attach(HTMLElement or joView)` - - `detach(HTMLElement or joView)` - - Convenience methods which allow you to append a view or DOM node to the - current view (or detach it). - -*/ -joView = function(data) { - this.changeEvent = new joSubject(this); - - this.setContainer(); - - if (data) - this.setData(data); -}; -joView.prototype = { - tagName: "joview", - busyNode: null, - container: null, - data: null, - - getContainer: function() { - return this.container; - }, - - setContainer: function(container) { - this.container = joDOM.get(container); - - if (!this.container) - this.container = this.createContainer(); - - this.setEvents(); - - return this; - }, - - createContainer: function() { - return joDOM.create(this); - }, - - clear: function() { - this.data = ""; - - if (this.container) - this.container.innerHTML = ""; - - this.changeEvent.fire(); - }, - - setData: function(data) { - this.data = data; - this.refresh(); - - return this; - }, - - getData: function() { - return this.data; - }, - - refresh: function() { - if (!this.container || typeof this.data == "undefined") - return 0; - - this.container.innerHTML = ""; - this.draw(); - - this.changeEvent.fire(this.data); - }, - - draw: function() { - this.container.innerHTML = this.data; - }, - - setStyle: function(style) { - joDOM.setStyle(this.container, style); - - return this; - }, - - attach: function(parent) { - if (!this.container) - return this; - - var node = joDOM.get(parent) || document.body; - node.appendChild(this.container); - - return this; - }, - - detach: function(parent) { - if (!this.container) - return this; - - var node = joDOM.get(parent) || document.body; - - if (this.container && this.container.parentNode === node) - node.removeChild(this.container); - - return this; - }, - - setEvents: function() {} -}; -/** - joContainer - ============ - - A view which is designed to contain other views and controls. Subclass to provide - different layout types. A container can be used to intantiate an entire tree of - controls at once, and is a very powerful UI component in jo. - - Use - --- - - // plain container - var x = new joContainer(); - - // HTML or plain text - var y = new joContainer("Some HTML"); - - // HTMLElement - var w = new joContainer(joDOM.get("mydiv")); - - // nested inline structure with text, HTML, joViews or HTMLElements - var z = new joContainer([ - new joTitle("Hello"), - new joList([ - "Red", - "Green", - "Blue" - ]), - new joFieldset([ - "Name", new joInput(joPreference.bind("name")), - "Phone", new joInput(joPreference.bind("phone")) - ]), - new joButton("Done") - ]); - - // set an optional title string, used with joNavbar - z.setTitle("About"); - - Extends - ------- - - - joView - - Events - ------ - - - `changeEvent` - - Methods - ------- - - - `setData(data)` - - The constructor calls this method if you provide `data` when you instantiate - (see example above) - - - `push(data)` - - Same support as `setData()`, but places the new content at the end of the - existing content. - - - `setTitle(string)` - - `getTitle(string)` - - Titles are optional, but used with joStack & joStackScroller to update a - joNavbar control automagically. - -*/ -joContainer = function(data) { - joView.apply(this, arguments); -}; -joContainer.extend(joView, { - tagName: "jocontainer", - title: null, - - getContent: function() { - return this.container.childNodes; - }, - - setTitle: function(title) { - this.title = title; - return this; - }, - - setData: function(data) { - this.data = data; - this.refresh(); - return this; - }, - - activate: function() {}, - - deactivate: function() {}, - - push: function(data) { - if (typeof data === 'object') { - if (data instanceof Array) { - // we have a list of stuff - for (var i = 0; i < data.length; i++) - this.push(data[i]); - } - else if (data instanceof joView && data.container !== this.container) { - // ok, we have a single widget here - this.container.appendChild(data.container); - } - else if (data instanceof HTMLElement) { - // DOM element attached directly - this.container.appendChild(data); - } - } - else { - // shoving html directly in does work - var o = document.createElement("div"); - o.innerHTML = data; - this.container.appendChild(o); - } - }, - - getTitle: function() { - return this.title; - }, - - refresh: function() { - if (this.container) - this.container.innerHTML = ""; - - this.draw(); - this.changeEvent.fire(); - }, - - draw: function() { - this.push(this.data); - } -}); -/** - joControl - ========= - - Interactive, data-driven control class which may be bound to a joDataSource, - can receive focus events, and can fire off important events which other objects - can listen for and react to. - - Extends - ------- - - - joView - - Events - ------ - - - `changeEvent` - - `selectEvent` - - Methods - ------- - - - `setValue(value)` - - Many controls have a *value* in addition to their *data*. This is - particularly useful for `joList`, `joMenu`, `joOption` and other controls - which has a list of possibilities (the data) and a current seletion from those - (the value). - - - `enable()` - - `disable()` - - Enable or disable the control, pretty much does what you'd expect. - - - `focus()` - - `blur()` - - Manually control focus for this control. - - - `setDataSource(joDataSource)` - - Tells this control to bind its data to any `joDataSource` or subclass. - - - `setValueSource(joDataSource)` - - Tells this control to bind its *value* to any `joDataSource` type. - - - `setReadOnly(state)` - - Certain controls can have their interaction turned off. State is either `true` - or `false`. - - See Also - -------- - - - joRecord and joProperty are specialized joDataSource classes which - make it simple to bind control values to a data structure. - -*/ -joControl = function(data, value) { - this.selectEvent = new joSubject(this); - this.enabled = true; - this.value = null; - - if (typeof value !== "undefined" && value != null) { - if (value instanceof joDataSource) - this.setValueSource(value); - else - this.value = value; - } - - if (data instanceof joDataSource) { - // we want to bind directly to some data - joView.call(this); - this.setDataSource(data); - } - else { - joView.apply(this, arguments); - } -}; -joControl.extend(joView, { - tagName: "jocontrol", - - setEvents: function() { - // not sure what we want to do here, want to use - // gesture system, but that's not defined - joEvent.on(this.container, "click", this.onMouseDown, this); - joEvent.on(this.container, "blur", this.onBlur, this); - joEvent.on(this.container, "focus", this.onFocus, this); - }, - - onMouseDown: function(e) { - this.select(e); - }, - - select: function(e) { - if (e) - joEvent.stop(e); - - this.selectEvent.fire(this.data); - }, - - enable: function() { - joDOM.removeCSSClass(this.container, 'disabled'); - this.container.contentEditable = true; - this.enabled = true; - - return this; - }, - - disable: function() { - joDOM.addCSSClass(this.container, 'disabled'); - this.container.contentEditable = false; - this.enabled = false; - - return this; - }, - - setReadOnly: function(value) { - if (typeof value === 'undefined' || value) - this.container.setAttribute('readonly', '1'); - else - this.container.removeAttribute('readonly'); - - return this; - }, - - onFocus: function(e) { - joEvent.stop(e); - - if (this.enabled) - joFocus.set(this); - }, - - onBlur: function(e) { - this.data = (this.container.value) ? this.container.value : this.container.innerHTML; - joEvent.stop(e); - - if (this.enabled) { - this.blur(); - - this.changeEvent.fire(this.data); - } - }, - - focus: function(e) { - if (!this.enabled) - return; - - joDOM.addCSSClass(this.container, 'focus'); - - if (!e) - this.container.focus(); - - return this; - }, - - setValue: function(value) { - this.value = value; - this.changeEvent.fire(value); - - return this; - }, - - getValue: function() { - return this.value; - }, - - blur: function() { - joDOM.removeCSSClass(this.container, 'focus'); - - return this; - }, - - setDataSource: function(source) { - this.dataSource = source; - source.changeEvent.subscribe(this.setData, this); - this.setData(source.getData() || null); - this.changeEvent.subscribe(source.setData, source); - - return this; - }, - - setValueSource: function(source) { - this.valueSource = source; - source.changeEvent.subscribe(this.setValue, this); - this.setValue(source.getData() || null); - this.selectEvent.subscribe(source.setData, source); - - return this; - } -}); -/** - joButton - ======== - - Button control. - - // simple invocation - var x = new joButton("Done"); - - // optionally pass in a CSS classname to style the button - var y = new joButton("Cancel", "cancelbutton"); - - // like other controls, you can pass in a joDataSource - // which could be useful, so why not - var z = new joButton(joPreference.bind("processname")); - - Extends - ------- - - - joControl - - Methods - ------- - - - enable() - - disable() - -*/ - -joButton = function(data, classname) { - // call super - joControl.apply(this, arguments); - this.enabled = true; - - if (classname) - this.container.className = classname; -}; -joButton.extend(joControl, { - tagName: "jobutton", - - createContainer: function() { - var o = joDOM.create(this.tagName); - - if (o) - o.setAttribute("tabindex", "1"); - - return o; - }, - - enable: function() { - this.container.setAttribute("tabindex", "1"); - return joControl.prototype.enable.call(this); - }, - - disable: function() { - // this doesn't seem to work in safari doh - this.container.removeAttribute("tabindex"); - return joControl.prototype.disable.call(this); - } -}); -/** - - - - - - joBusy - ====== - - The idea here is to make a generic "spinner" control which you - can overlay on other controls. It's still in flux, don't use it - just yet. - - Extends - ------- - - - joView - - Methods - ------- - - - `setMessage(status)` - - You can update the status message in this busy box so users - have a better idea why the busy box is showing. -*/ - -joBusy = function(data) { - joContainer.apply(this, arguments); -}; -joBusy.extend(joContainer, { - tagName: "jobusy", - - draw: function() { - this.container.innerHTML = ""; - for (var i = 0; i < 9; i++) - this.container.appendChild(joDom.create("jobusyblock")); - }, - - setMessage: function(msg) { - this.message = msg || ""; - }, - - setEvents: function() { - return this; - } -}); -/** - joList - ======= - - A widget class which expects an array of any data type and renders the - array as a list. The list control handles DOM interactions with only a - single touch event to determine which item was selected. - - Extends - ------- - - - joControl - - Events - ------ - - - `selectEvent` - - Fired when an item is selected from the list. The data in the call is the - index of the item selected. - - - `changeEvent` - - Fired when the data is changed for the list. - - Methods - ------- - - - `formatItem(data, index)` - - When subclassing or augmenting, this is the method responsible for - rendering a list item's data. - - - `compareItems(a, b)` - - For sorting purposes, this method is called and should be overriden - to support custom data types. - - // general logic and approriate return values - if (a > b) - return 1; - else if (a == b) - return 0; - else - return -1 - - - `setIndex(index)` - - `getIndex()` - - *DEPRECATED* USe `setValue()` and `getValue()` instead, see joControl. - - - `refresh()` - - - `setDefault(message)` - - Will present this message (HTML string) when the list is empty. - Normally the list is empty; this is a convenience for "zero state" - UI requirements. - - - `getNodeData(index)` - - - `getLength()` - - - `next()` - - - `prev()` - - - `setAutoSort(boolean)` - -*/ -joList = function() { - // these are being deprecated in the BETA - // for now, we'll keep references to the new stuff - this.setIndex = this.setValue; - this.getIndex = this.getValue; - - joControl.apply(this, arguments); -}; -joList.extend(joControl, { - tagName: "jolist", - defaultMessage: "", - lastNode: null, - value: null, - autoSort: false, - - setDefault: function(msg) { - this.defaultMessage = msg; - - if (typeof this.data === 'undefined' || !this.data || !this.data.length) { - if (typeof msg === 'object') { - this.innerHTML = ""; - if (msg instanceof joView) - this.container.appendChild(msg.container); - else if (msg instanceof HTMLElement) - this.container.appendChild(msg); - } - else { - this.innerHTML = msg; - } - } - - return this; - }, - - draw: function() { - var html = ""; - var length = 0; - - if (typeof this.data === 'undefined' || !this.data || !this.data.length) { - if (this.defaultMessage) - this.container.innerHTML = this.defaultMessage; - - return; - } - - for (var i = 0, l = this.data.length; i < l; i++) { - var element = this.formatItem(this.data[i], i, length); - - if (element == null) - continue; - - if (typeof element === "string") - html += element; - else - this.container.appendChild((element instanceof joView) ? element.container : element); - - ++length; - } - - // support setting the contents with innerHTML in one go, - // or getting back HTMLElements ready to append to the contents - if (html.length) - this.container.innerHTML = html; - - // refresh our current selection - if (this.value >= 0) - this.setValue(this.value, true); - - return; - }, - - deselect: function() { - if (typeof this.container == 'undefined' - || !this.container['childNodes']) - return; - - var node = this.getNode(this.value); - if (node) { - if (this.lastNode) { - joDOM.removeCSSClass(this.lastNode, "selected"); - this.value = null; - } - } - - return this; - }, - - setValue: function(index, silent) { - this.value = index; - - if (index == null) - return; - - if (typeof this.container === 'undefined' - || !this.container - || !this.container.firstChild) { - return this; - } - - var node = this.getNode(this.value); - if (node) { - if (this.lastNode) - joDOM.removeCSSClass(this.lastNode, "selected"); - - joDOM.addCSSClass(node, "selected"); - this.lastNode = node; - } - - if (index >= 0 && !silent) { - this.fireSelect(index); - this.changeEvent.fire(index); - } - - return this; - }, - - getNode: function(index) { - return this.container.childNodes[index]; - }, - - fireSelect: function(index) { - this.selectEvent.fire(index); - }, - - getValue: function() { - return this.value; - }, - - onMouseDown: function(e) { - joEvent.stop(e); - - var node = joEvent.getTarget(e); - var index = -1; - - while (index == -1 && node !== this.container) { - index = node.getAttribute("index") || -1; - node = node.parentNode; - } - - if (index >= 0) - this.setValue(index); - }, - - refresh: function() { -// this.value = null; -// this.lastNode = null; - - if (this.autoSort) - this.sort(); - - joControl.prototype.refresh.apply(this); - }, - - getNodeData: function(index) { - if (this.data && this.data.length && index >= 0 && index < this.data.length) - return this.data[index]; - else - return null; - }, - - getLength: function() { - return this.length || this.data.length || 0; - }, - - sort: function() { - this.data.sort(this.compareItems); - }, - - getNodeIndex: function(element) { - var index = element.getAttribute('index'); - if (typeof index !== "undefined" && index != null) - return parseInt(index) - else - return -1; - }, - - formatItem: function(itemData, index) { - var element = document.createElement('jolistitem'); - element.innerHTML = itemData; - element.setAttribute("index", index); - - return element; - }, - - compareItems: function(a, b) { - if (a > b) - return 1; - else if (a == b) - return 0; - else - return -1; - }, - - setAutoSort: function(state) { - this.autoSort = state; - return this; - }, - - next: function() { - if (this.getValue() < this.getLength() - 1) - this.setValue(this.value + 1); - }, - - prev: function() { - if (this.getValue() > 0) - this.setValue(this.value - 1); - } -}); -/** - - - - - - joBusy - ====== - - The idea here is to make a generic "spinner" control which you - can overlay on other controls. It's still in flux, don't use it - just yet. - - Extends - ------- - - - joView - - Methods - ------- - - - `setMessage(status)` - - You can update the status message in this busy box so users - have a better idea why the busy box is showing. -*/ - -joBusy = function(data) { - joContainer.apply(this, arguments); -}; -joBusy.extend(joContainer, { - tagName: "jobusy", - - draw: function() { - this.container.innerHTML = ""; - for (var i = 0; i < 9; i++) - this.container.appendChild(joDom.create("jobusyblock")); - }, - - setMessage: function(msg) { - this.message = msg || ""; - }, - - setEvents: function() { - return this; - } -}); -/** - joCaption - ========= - - Basically, a paragraph of text. - - Extends - ------- - - - joControl - -*/ -joCaption = function(data) { - joControl.apply(this, arguments); -}; -joCaption.extend(joControl, { - tagName: "jocaption" -}); - -/** - joCard - ====== - - Special container for card views, more of an application-level view. - - Extends - ------- - - - joContainer - - Methods - ------- - - - `activate()` - - `deactivate()` - - These methods are called automatically by various joView objects, for - now joStack is the only one which does. Basically, allows you to add - application-level handlers to initialize or cleanup a joCard. - -*/ -joCard = function(data) { - joContainer.apply(this, arguments); -}; -joCard.extend(joContainer, { - tagName: "jocard" -}); - -/** - joStack - ======== - - A UI container which keeps an array of views which can be pushed and popped. - The DOM elements for a given view are removed from the DOM tree when popped - so we keep the render tree clean. - - Extends - ------- - - - joView - - Methods - ------- - - - `push(joView | HTMLElement)` - - Pushes a new joView (or HTMLELement) onto the stack. - - - `pop()` - - Pulls the current view off the stack and goes back to the previous view. - - - `home()` - - Return to the first view, pop everything else off the stack. - - - `show()` - - `hide()` - - Controls the visibility of the entire stack. - - - `forward()` - - `back()` - - Much like your browser forward and back buttons, only for the stack. - - - `setLocked(boolean)` - - The `setLocked()` method tells the stack to keep the first view pushed onto the - stack set; that is, `pop()` won't remove it. Most apps will probably use this, - so setting it as a default for now. - - Events - ------ - - - `showEvent` - - `hideEvent` - - `homeEvent` - - `pushEvent` - - `popEvent` - - Notes - ----- - - Should set classNames to new/old views to allow for CSS transitions to be set - (swiping in/out, cross fading, etc). Currently, it does none of this. - - Also, some weirdness with the new `forward()` and `back()` methods in conjuction - with `push()` -- need to work on that, or just have your app rigged to `pop()` - on back to keep the nesting simple. - -*/ -joStack = function(data) { - this.visible = false; - - this.data = []; - - joContainer.apply(this, arguments); - - // yes, nice to have one control, but we need an array - if (this.data && !(this.data instanceof Array)) - this.data = [ this.data ]; - else if (this.data.length > 1) - this.data = [ this.data[0] ]; - - // we need to clear inlined stuff out for this to work - if (this.container && this.container.firstChild) - this.container.innerHTML = ""; - - // default to keep first card on the stack; won't pop() off - this.setLocked(true); - - this.pushEvent = new joSubject(this); - this.popEvent = new joSubject(this); - this.homeEvent = new joSubject(this); - this.showEvent = new joSubject(this); - this.hideEvent = new joSubject(this); - this.backEvent = new joSubject(this); - this.forwardEvent = new joSubject(this); - - this.index = 0; - this.lastIndex = 0; - this.lastNode = null; -}; -joStack.extend(joContainer, { - tagName: "jostack", - type: "fixed", - eventset: false, - - setEvents: function() { - // do not setup DOM events for the stack - }, - - onClick: function(e) { - joEvent.stop(e); - }, - - forward: function() { - if (this.index < this.data.length - 1) { - this.index++; - this.draw(); - this.forwardEvent.fire(); - } - }, - - back: function() { - if (this.index > 0) { - this.index--; - this.draw(); - this.backEvent.fire(); - } - }, - - draw: function() { - if (!this.container) - this.createContainer(); - - if (!this.data || !this.data.length) - return; - - // short term hack for webos - // not happy with it but works for now - jo.flag.stopback = this.index ? true : false; - - var container = this.container; - var oldchild = this.lastNode; - var newnode = getnode(this.data[this.index]); - var newchild = this.getChildStyleContainer(newnode); - - function getnode(o) { - return (o instanceof joView) ? o.container : o; - } - - if (!newchild) - return; - - if (this.index > this.lastIndex) { - var oldclass = "prev"; - var newclass = "next"; - joDOM.addCSSClass(newchild, newclass); - } - else if (this.index < this.lastIndex) { - var oldclass = "next"; - var newclass = "prev"; - joDOM.addCSSClass(newchild, newclass); - } - else { -// this.getContentContainer().innerHTML = ""; - } - - this.appendChild(newnode); - - var self = this; - var transitionevent = null; - - joDefer(animate, this, 1); - - function animate() { - // FIXME: AHHH must have some sort of transition for this to work, - // need to check computed style for transition to make this - // better - if (typeof window.onwebkittransitionend !== 'undefined') - transitionevent = joEvent.on(newchild, "webkitTransitionEnd", cleanup, self); - else - joDefer(cleanup, this, 200); - - if (newclass && newchild) - joDOM.removeCSSClass(newchild, newclass); - - if (oldclass && oldchild) - joDOM.addCSSClass(oldchild, oldclass); - } - - function cleanup() { - if (oldchild) { - self.removeChild(oldchild); - joDOM.removeCSSClass(oldchild, "next"); - joDOM.removeCSSClass(oldchild, "prev"); - } - - if (newchild) { - if (transitionevent) - joEvent.remove(newchild, "webkitTransitionEnd", transitionevent); - - joDOM.removeCSSClass(newchild, "next"); - joDOM.removeCSSClass(newchild, "prev"); - } - } - - if (typeof this.data[this.index].activate !== "undefined") - this.data[this.index].activate.call(this.data[this.index]); - - this.lastIndex = this.index; - this.lastNode = newchild; - }, - - appendChild: function(child) { - this.container.appendChild(child); - }, - - getChildStyleContainer: function(child) { - return child; - }, - - getChild: function() { - return this.container.firstChild; - }, - - getContentContainer: function() { - return this.container; - }, - - removeChild: function(child) { - if (child && child.parentNode === this.container) - this.container.removeChild(child); - }, - - isVisible: function() { - return this.visible; - }, - - push: function(o) { -// if (!this.data || !this.data.length || o !== this.data[this.data.length - 1]) -// return; - - // don't push the same view we already have - if (this.data && this.data.length && this.data[this.data.length - 1] === o) - return; - - this.data.push(o); - this.index = this.data.length - 1; - this.draw(); - this.pushEvent.fire(o); - }, - - // lock the stack so the first pushed view stays put - setLocked: function(state) { - this.locked = (state) ? 1 : 0; - }, - - pop: function() { - if (this.data.length > this.locked) { - var o = this.data.pop(); - this.index = this.data.length - 1; - - this.draw(); - - if (typeof o.deactivate === "function") - o.deactivate.call(o); - - if (!this.data.length) - this.hide(); - } - - if (this.data.length > 0) - this.popEvent.fire(); - }, - - home: function() { - if (this.data && this.data.length && this.data.length > 1) { - var o = this.data[0]; - var c = this.data[this.index]; - - if (o === c) - return; - - this.data = [o]; - this.lastIndex = 1; - this.index = 0; -// this.lastNode = null; - this.draw(); - - this.popEvent.fire(); - this.homeEvent.fire(); - } - }, - - showHome: function() { - this.home(); - - if (!this.visible) { - this.visible = true; - joDOM.addCSSClass(this.container, "show"); - this.showEvent.fire(); - } - }, - - getTitle: function() { - var c = this.data[this.index]; - if (typeof c.getTitle === 'function') - return c.getTitle(); - else - return false; - }, - - show: function() { - if (!this.visible) { - this.visible = true; - joDOM.addCSSClass(this.container, "show"); - - joDefer(this.showEvent.fire, this.showEvent, 500); - } - }, - - hide: function() { - if (this.visible) { - this.visible = false; - joDOM.removeCSSClass(this.container, "show"); - - joDefer(this.hideEvent.fire, this.hideEvent, 500); - } - } -}); -/** - joScroller - ========== - - A scroller container. Ultimately, mobile webkit implementations - should properly support scrolling elements that have the CSS - `overflow` property set to `scroll` or `auto`. Why don't they, - anyway? Until some sanity is adopted, we need to handle this scrolling - issue ourselves. joScroller expects a single child to manage - scrolling for. - - Use - --- - - // make a scroller and set its child later - var x = new joScroller(); - x.setData(myCard); - - // or define things inline, not always a good idea - var y = new joScroller(new joList(mydata)); - - // you can dump a big hunk of HTML in there, too - // since jo wraps strings in a container element, this works - var z = new joScroller('Some giant HTML as a string'); - - Extends - ------- - - - joContainer - - Methods - ------- - - - `scrollBy(position)` - - `scrollTo(position or joView or HTMLElement)` - - Scrolls to the position or the view or element. If you - specify an element or view, make sure that element is a - child node, or you'll get interesting results. - - - `setScroll(horizontal, vertical)` - - Tells this scroller to allow scrolling the vertical, horizontal, both or none. - - // free scroller - z.setScroll(true, true); - - // horizontal - z.setScroll(true, false); - - // no scrolling - z.setScroll(false, false); - -*/ - -joScroller = function(data) { - this.points = []; - this.eventset = false; - - this.horizontal = 0; - this.vertical = 1; - this.inMotion = false; - this.moved = false; - this.mousemove = null; - this.mouseup = null; - this.bump = 0; - - // Call Super - joContainer.apply(this, arguments); -}; -joScroller.extend(joContainer, { - tagName: "joscroller", - velocity: 1.6, - transitionEnd: "webkitTransitionEnd", - - setEvents: function() { - joEvent.capture(this.container, "click", this.onClick, this); - joEvent.on(this.container, "mousedown", this.onDown, this); - }, - - onFlick: function(e) { - // placeholder - }, - - onClick: function(e) { - if (this.moved) { - this.moved = false; - joEvent.stop(e); - joEvent.preventDefault(e); - } - }, - - onDown: function(e) { - joEvent.stop(e); - - this.reset(); - - var node = this.container.firstChild; - - joDOM.removeCSSClass(node, "flick"); - joDOM.removeCSSClass(node, "flickback"); - joDOM.removeCSSClass(node, "flickfast"); - - this.start = this.getMouse(e); - this.points.unshift(this.start); - this.inMotion = true; - - if (!this.mousemove) { - this.mousemove = joEvent.capture(document.body, "mousemove", this.onMove, this); - this.mouseup = joEvent.capture(document.body, "mouseup", this.onUp, this); - } - }, - - reset: function() { - this.points = []; - this.moved = false; - this.inMotion = false; - }, - - onMove: function(e) { - if (!this.inMotion) - return; - - joEvent.stop(e); - e.preventDefault(); - - var point = this.getMouse(e); - - var y = point.y - this.points[0].y; - var x = point.x - this.points[0].x; - -// if (y == 0) -// return; - - this.points.unshift(point); - - if (this.points.length > 7) - this.points.pop(); - - // cleanup points if the user drags slowly to avoid unwanted flicks - var self = this; - this.timer = window.setTimeout(function() { - if (self.inMotion && self.points.length > 1) - self.points.pop(); - }, 100); - - this.scrollBy(x, y, true); - - if (!this.moved && this.points.length > 3) - this.moved = true; - }, - - onUp: function (e) { - if (!this.inMotion) - return; - - joEvent.remove(document.body, "mousemove", this.mousemove, true); - joEvent.remove(document.body, "mouseup", this.mouseup, true); - - this.mousemove = null; - this.inMotion = false; - - joEvent.stop(e); - joEvent.preventDefault(e); - - var end = this.getMouse(e); - var node = this.container.firstChild; - - var top = this.getTop(); - var left = this.getLeft(); - - var dy = 0; - var dx = 0; - - for (var i = 0; i < this.points.length - 1; i++) { - dy += (this.points[i].y - this.points[i + 1].y); - dx += (this.points[i].x - this.points[i + 1].x); - } - - var max = 0 - node.offsetHeight + this.container.offsetHeight; - var maxx = 0 - node.offsetWidth + this.container.offsetWidth; - - // if the velocity is "high" then it was a flick - if ((Math.abs(dy) * this.vertical > 4 || Math.abs(dx) * this.horizontal > 4)) { - var flick = dy * (this.velocity * (node.offsetHeight / this.container.offsetHeight)); - var flickx = dx * (this.velocity * (node.offsetWidth / this.container.offsetWidth)); - - // we want to move quickly if we're going to land past - // the top or bottom - if ((flick + top < max || flick + top > 0) - || (flickx + left < maxx || flickx + left > 0)) { - joDOM.addCSSClass(node, "flickfast"); - } - else { - joDOM.addCSSClass(node, "flick"); - } - - this.scrollBy(flickx, flick, false); - - joDefer(this.snapBack, this, 3000); - } - else { - joDefer(this.snapBack, this, 10); - } - - }, - - getMouse: function(e) { - return { - x: (this.horizontal) ? e.screenX : 0, - y: (this.vertical) ? e.screenY : 0 - }; - }, - - scrollBy: function(x, y, test) { - var node = this.container.firstChild; - - var top = this.getTop(); - var left = this.getLeft(); - - var dy = Math.floor(top + y); - var dx = Math.floor(left + x); - - if (this.vertical && (node.offsetHeight <= this.container.offsetHeight)) - return; - - var max = 0 - node.offsetHeight + this.container.offsetHeight; - var maxx = 0 - node.offsetWidth + this.container.offsetWidth; - - var ody = dy; - var odx = dx; - - if (this.bump) { - if (dy > this.bump) - dy = this.bump; - else if (dy < max - this.bump) - dy = max - this.bump; - - if (dx > this.bump) - dx = this.bump; - else if (dy < maxx - this.bump) - dx = maxx - this.bump; - } - - if (!this.eventset) - this.eventset = joEvent.capture(node, this.transitionEnd, this.snapBack, this); - - if (top != dx || left != dy) - this.moveTo(dx, dy); - }, - - scrollTo: function(y, instant) { - var node = this.container.firstChild; - - if (!node) - return; - - if (typeof y == 'object') { - if (y instanceof HTMLElement) - var e = y; - else if (y instanceof joView) - var e = y.container; - - var t = 0 - e.offsetTop; - var h = e.offsetHeight + 80; - - var y = top; - - var top = this.getTop(); - var bottom = top - this.container.offsetHeight; - - if (t - h < bottom) - y = (t - h) + this.container.offsetHeight; - - if (y < t) - y = t; - } - - if (y < 0 - node.offsetHeight) - y = 0 - node.offsetHeight; - else if (y > 0) - y = 0; - - if (!instant) { - joDOM.addCSSClass(node, 'flick'); - } - else { - joDOM.removeCSSClass(node, 'flick'); - joDOM.removeCSSClass(node, 'flickback'); - } - - this.moveTo(0, y); - }, - - // called after a flick transition to snap the view - // back into our container if necessary. - snapBack: function() { - var node = this.container.firstChild; - var top = this.getTop(); - var left = this.getLeft(); - - var dy = top; - var dx = left; - - var max = 0 - node.offsetHeight + this.container.offsetHeight; - var maxx = 0 - node.offsetWidth + this.container.offsetWidth; - - if (this.eventset) - joEvent.remove(node, this.transitionEnd, this.eventset); - - this.eventset = null; - - joDOM.removeCSSClass(node, 'flick'); - - if (dy > 0) - dy = 0; - else if (dy < max) - dy = max; - - if (dx > 0) - dx = 0; - else if (dx < maxx) - dx = maxx; - - if (dx != left || dy != top) { - joDOM.addCSSClass(node, 'flickback'); - this.moveTo(dx, dy); - } - }, - - setScroll: function(x, y) { - this.horizontal = x ? 1 : 0; - this.vertical = y ? 1 : 0; - return this; - }, - - moveTo: function(x, y) { - var node = this.container.firstChild; - - if (!node) - return; - - this.setPosition(x * this.horizontal, y * this.vertical, node); - - node.jotop = y; - node.joleft = x; - }, - - setPosition: function(x, y, node) { - node.style.webkitTransform = "translate3d(" + x + "px, " + y + "px, 0)"; - }, - - getTop: function() { - return this.container.firstChild.jotop || 0; - }, - - getLeft: function() { - return this.container.firstChild.joleft || 0; - }, - - setData: function(data) { - joContainer.prototype.setData.apply(this, arguments); - } -}); -/** - joDivider - ========= - - Simple visual divider. - - Extends - ------- - - - joView - -*/ -joDivider = function(data) { - joView.apply(this, arguments); -}; -joDivider.extend(joView, { - tagName: "jodivider" -}); - -/** - joExpando - ========= - - A compound UI element which allows the user to hide/show its contents. - The first object passed in becomes the trigger control for the container, - and the second becomes the container which expands and contracts. This - action is controlled in the CSS by the presence of the "open" class. - - Use - --- - - This is a typical pattern: - - // normal look & feel - var x = new joExpando([ - new joExpandoTitle("Options"), - new joExpandoContent([ - new joLabel("Label"), - new joInput("sample field") - ]) - ]); - - Note that joExpando doesn't care what sort of controls you tell it - to use. In this example, we have a joButton that hides and shows a - DOM element: - - // you can use other things though - var y = new joExpando([ - new joButton("More..."), - joDOM.get("someelementid") - ]); - - Extends - ------- - - - joContainer - - Methods - ------- - - - `open()` - - `close()` - - `toggle()` - - Events - ------ - - - `openEvent` - - `closeEvent` - -*/ -joExpando = function(data) { - this.openEvent = new joSubject(this); - this.closeEvent = new joSubject(this); - - joContainer.apply(this, arguments); -}; -joExpando.extend(joContainer, { - tagName: "joexpando", - - draw: function() { - if (!this.data) - return; - - joContainer.prototype.draw.apply(this, arguments); - this.setToggleEvent(); - }, - - setEvents: function() { - }, - - setToggleEvent: function() { - joEvent.on(this.container.childNodes[0], "click", this.toggle, this); - }, - - toggle: function() { - if (this.container.className.indexOf("open") >= 0) - this.close(); - else - this.open(); - }, - - open: function() { - joDOM.addCSSClass(this.container, "open"); - this.openEvent.fire(); - }, - - close: function() { - joDOM.removeCSSClass(this.container, "open"); - this.closeEvent.fire(); - } -}); - - -/** - joExpandoContent - ================ - - New widget to contain expando contents. This is normally used with - joExpando, but not required. - - Extends - ------- - - joContainer -*/ -joExpandoContent = function() { - joContainer.apply(this, arguments); -}; -joExpandoContent.extend(joContainer, { - tagName: "joexpandocontent" -}); - - -/** - - joExpandoTitle - ============== - - Common UI element to trigger a joExpando. Contains a stylable - arrow image which indicates open/closed state. - - Extends - ------- - - - joControl - - Use - --- - - See joExpando use. - -*/ -joExpandoTitle = function(data) { - joControl.apply(this, arguments); -}; -joExpandoTitle.extend(joControl, { - tagName: "joexpandotitle", - - setData: function() { - joView.prototype.setData.apply(this, arguments); - this.draw(); - }, - - draw: function() { - this.container.innerHTML = this.data + ""; - } -}); -/** - joFlexrow - ========= - - Uses the flexible box model in CSS to stretch elements evenly across a row. - - Use - --- - - // a simple row of things - var x = new joFlexrow([ - new joButton("OK"), - new joButton("Cancel") - ]); - - // making a control stretch - var y = new joFlexrow(new joInput("Bob")); - - Extends - ------- - - - joContainer - -*/ -joFlexrow = function(data) { - joContainer.apply(this, arguments); -}; -joFlexrow.extend(joContainer, { - tagName: "joflexrow" -}); - -/** - joFlexcol - ========= - - Uses the flexible box model in CSS to stretch elements evenly across a column. - - Use - --- - - // fill up a vertical space with things - var x = new joFlexcol([ - new joNavbar(), - new joStackScroller() - ]); - - Extends - ------- - - - joContainer - -*/ -joFlexcol = function(data) { - joContainer.apply(this, arguments); -}; -joFlexcol.extend(joContainer, { - tagName: "joflexcol" -}); -/** - joFocus - ======= - - Singleton which manages global input and event focus among joControl objects. - - Methods - ------- - - - `set(joControl)` - - Unsets focus on the last control, and sets focus on the control passed in. - - - `clear()` - - Unsets focus on the last control. - - - `refresh()` - - Sets focus back to the last control that was focused. - -*/ - -joFocus = { - last: null, - - set: function(control) { - if (this.last && this.last !== control) - this.last.blur(); - - if (control && control instanceof joControl) { - control.focus(); - this.last = control; - } - }, - - get: function(control) { - return this.last; - }, - - refresh: function() { -// joLog("joFocus.refresh()"); - if (this.last) - this.last.focus(); - }, - - clear: function() { - this.set(); - } -}; - -/** - joFooter - ====== - - Attempt to make a filler object which pushed subsequent joView objects - further down in the container if possible (to attach its contents to - the bottom of a card, for eaxmple). - - > This behavior requires a working box model to attach properly to the bottom - > of your container view. - - Extends - ------- - - - joContainer - -*/ -joFooter = function(data) { - joContainer.apply(this, arguments); -}; -joFooter.extend(joContainer, { - tagName: "jofooter" -}); -/** - joGesture - ========= - - Experimental global gesture handler (keyboard, dpad, back, home, flick?). - This needs a lot more fleshing out, so it's not (quite) ready for general - consumption. - - Events - ------ - - - `upEvent` - - `downEvent` - - `leftEvent` - - `rightEvent` - - `backEvent` - - `forwardEvent` - - `homeEvent` - - `closeEvent` - - `activateEvent` - - `deactivateEvent` - - > Note that the events setup here are for the browser - > or webOS. The `setEvents` method most likely needs to change - > based on which OS you're running, although looking more deeply - > into PhoneGap event layer. - -*/ -joGesture = { - load: function() { - this.upEvent = new joSubject(this); - this.downEvent = new joSubject(this); - this.leftEvent = new joSubject(this); - this.rightEvent = new joSubject(this); - this.forwardEvent = new joSubject(this); - this.backEvent = new joSubject(this); - this.homeEvent = new joSubject(this); - this.closeEvent = new joSubject(this); - this.activateEvent = new joSubject(this); - this.deactivateEvent = new joSubject(this); - this.resizeEvent = new joSubject(this); - - this.setEvents(); - }, - - // by default, set for browser - setEvents: function() { - joEvent.on(document.body, "keydown", this.onKeyDown, this); - joEvent.on(document.body, "keyup", this.onKeyUp, this); - - joEvent.on(document.body, "unload", this.closeEvent, this); - joEvent.on(window, "activate", this.activateEvent, this); - joEvent.on(window, "deactivate", this.deactivateEvent, this); - - joEvent.on(window, "resize", this.resize, this); - }, - - resize: function() { - this.resizeEvent.fire(window); - }, - - onKeyUp: function(e) { - if (!e) - var e = window.event; - - if (e.keyCode == 18) { - this.altkey = false; - - return; - } - - if (e.keyCode == 27) { - if (jo.flag.stopback) { - joEvent.stop(e); - joEvent.preventDefault(e); - } - - this.backEvent.fire("back"); - return; - } - - if (!this.altkey) - return; - - joEvent.stop(e); - - switch (e.keyCode) { - case 37: - this.leftEvent.fire("left"); - break; - case 38: - this.upEvent.fire("up"); - break; - case 39: - this.rightEvent.fire("right"); - break; - case 40: - this.downEvent.fire("down"); - break; - case 27: - this.backEvent.fire("back"); - break; - case 13: - this.forwardEvent.fire("forward"); - break; - } - }, - - onKeyDown: function(e) { - if (!e) - var e = window.event; - - if (e.keyCode == 27) { - joEvent.stop(e); - joEvent.preventDefault(e); - } - else if (e.keyCode == 13 && joFocus.get() instanceof joInput) { - joEvent.stop(e); - } - else if (e.keyCode == 18) { - this.altkey = true; - } - - return; - } -}; -/** - joGroup - ======= - - Group of controls, purely visual. - - Extends - ------- - - - joContainer - -*/ -joGroup = function(data) { - joContainer.apply(this, arguments); -}; -joGroup.extend(joContainer, { - tagName: "jogroup" -}); -/** - joHTML - ====== - - A simple HTML content control. One interesting feature is it intercepts all - `` tag interactions and fires off a `selectEvent` with the contents of - the tag's `href` property. - - This is a relatively lightweight approach to displaying arbitrary HTML - data inside your app, but it is _not_ recommended you allow external - JavaScript inside the HTML chunk in question. - - Also keep in mind that your app document already _has_ ``, `` and - `` tags. When you use the `setData()` method on this view, _make sure - you don't use any of these tags_ to avoid weird issues. - - > In a future version, it is feasible to load in stylesheets references in - > the HTML document's `` section. For now, that entire can of worms - > will be avoided, and it's left up to you, the developer, to load in any - > required CSS files using `joDOM.loadCSS()`. - - Extends - ------- - - - joControl - - Use - --- - - // simple html string - var x = new joHTML("

Hello World!

Sup?

"); - - // use a joDataSource like a file loader - var y = new joHTML(new joFileSource("sample.html")); - -*/ -joHTML = function(data) { - joControl.apply(this, arguments); -}; -joHTML.extend(joControl, { - tagName: "johtml", - - setEvents: function() { - // limited events, no focus for example - joEvent.on(this.container, "click", this.onClick, this); - }, - - // special sauce -- we want to trap any a href click events - // and return them in our select event -- don't need to be - // refreshing our entire page, after all - onClick: function(e) { - joEvent.stop(e); - joEvent.preventDefault(e); - - // figure out what was clicked, look for an href - var container = this.container; - var hrefnode = findhref(joEvent.getTarget(e)); - - if (hrefnode) { - // whoa we have an
tag clicked - this.selectEvent.fire(hrefnode.href); - } - - function findhref(node) { - if (!node) - return null; - - if (node.href) - return node; - - if (typeof node.parentNode !== "undefined" && node.parentNode !== container) - return findhref(node.parentNode); - else - return null; - } - } -}); - -/** - joInput - ======= - - Single-line text input control. When you instantiate or use `setData()`, you can - either pass in an initial value or a reference to a joDataSource object which it, - like other joControl instances, will bind to. - - Use - --- - - // simple value, simple field - var x = new joInput(a); - - // set up a simple joRecord instance with some default data - var pref = new joRecord({ - username: "Bob", - password: "password" - }); - - // attach the value to a data structure property - var y = new joInput(pref.link("username")); - - Extends - ------- - - - joControl - - Methods - ------- - - - `focus()` - - `blur()` - - You can manually set focus or call the `blur()` method (which also - triggers a data save). - - - `setData()` - - Pass in either some arbitrary value for the control, or a reference to - a joDataSource if you want to automatically bind to a storage system - (e.g. joPreference). - -*/ -joInput = function(data) { - joControl.apply(this, arguments); -}; -joInput.extend(joControl, { - tagName: "input", - type: "text", - - setData: function(data) { - if (data !== this.data) { - this.data = data; - - if (typeof this.container.value !== "undefined") - this.container.value = data; - else - this.container.innerHTML = data; - - this.changeEvent.fire(this.data); - } - }, - - getData: function() { - if (typeof this.container.value !== "undefined") - return this.container.value; - else - return this.container.innerHTML; - }, - - enable: function() { - this.container.setAttribute("tabindex", "1"); - joControl.prototype.enable.call(this); - }, - - disable: function() { - this.container.removeAttribute("tabindex"); - joControl.prototype.disable.call(this); - }, - - createContainer: function() { - var o = joDOM.create(this); - - if (!o) - return; - - o.setAttribute("type", "text"); - o.setAttribute("tabindex", "1"); - o.contentEditable = this.enabled; - - return o; - }, - - setEvents: function() { - joControl.prototype.setEvents.call(this); - joEvent.on(this.container, "keydown", this.onKeyDown, this); - }, - - onKeyDown: function(e) { - if (e.keyCode == 13) { - e.preventDefault(); - joEvent.stop(e); - } - return false; - }, - - draw: function() { - if (this.container.value) - this.value = this.data; - else - this.innerHTML = this.value; - }, - - onMouseDown: function(e) { - joEvent.stop(e); - this.focus(); - }, - - storeData: function() { - this.data = this.getData(); - if (this.dataSource) - this.dataSource.set(this.value); - } -}); - -/** - joLabel - ======= - - Label view, purely a visual presentation. Usually placed in front - of input fields and other controls. - - Extends - ------- - - - joView - -*/ -joLabel = function(data) { - joControl.apply(this, arguments); -}; -joLabel.extend(joControl, { - tagName: "jolabel" -}); - -/** - joMenu - ====== - - Simple menu class with optional icons. - - Extends - ------- - - - joList - - Methods - ------- - - - `setData(menudata)` - - See the example below for the format of the menu data. - - Use - --- - - // simple inline menu; you can always setup the menu items (or change - // them) but using the `setData()` method, same as any joView - var menu = new joMenu([ - { title: "About" }, - { title: "Frequently Asked Questions", id: "faq" }, - { title: "Visit our website", id: "visit", icon: "images/web" } - ]); - - // simple inline function event handler - menu.selectEvent.subscribe(function(id) { - switch (id) { - case "0": - // the "About" line; if no id, the index of the menu item is used - stack.push(aboutCard); - break; - case "faq": - stack.push(faqCard); - break; - case "visit": - stack.push(visitCard); - break; - } - }); - - Advanced Use - ------------ - - This could actually be called "more consistent and simple" use. If your menus - are static, you could always setup an id-based dispatch delegate which pushes - the appropriate card based on the menu `id` selected. - - You could use the `id` in conjunction with view keys you create with joCache. - The handler would then be something like: - - menu.selectEvent.subscribe(function(id) { - mystack.push(joCache.get(id)); - }); - -*/ -joMenu = function() { - joList.apply(this, arguments); -}; -joMenu.extend(joList, { - tagName: "jomenu", - itemTagName: "jomenuitem", - value: null, - - fireSelect: function(index) { - if (typeof this.data[index].id !== "undefined" && this.data[index].id) - this.selectEvent.fire(this.data[index].id); - else - this.selectEvent.fire(index); - }, - - formatItem: function(item, index) { - var o = joDOM.create(this.itemTagName); - - // TODO: not thrilled with this system of finding the - // selected item. It's flexible but annoying to code to. - o.setAttribute("index", index); - - // quick/dirty - if (typeof item === "object") { - o.innerHTML = item.title; - if (item.icon) { - o.style.backgroundImage = "url(" + item.icon + ")"; - joDOM.addCSSClass(o, "icon"); - } - } - else { - o.innerHTML = item; - } - - return o; - } -}); -/** - joOption - ======== - - This controls lets the user select one of a few options. Basically, this - is a menu with a horizontal layout (depending on your CSS). - - Use - --- - - // simple set of options - var x = new joOption([ - "Red", - "Blue", - "Green" - ]); - - // set the current value - x.setValue(2); - - // or, associate the value with a joRecord property - var pref = new joRecord(); - - var y = new joOption([ - "Orange", - "Banana", - "Grape", - "Lime" - ], pref.link("fruit")); - - // you can even associate the list with a datasource - var fruits = new joDataSource( ... some query stuff ...); - var z = new joOption(fruits, pref.link("fruit")); - - - Extends - ------- - - - joMenu - -*/ -joOption = function() { - joMenu.apply(this, arguments); -}; -joOption.extend(joMenu, { - tagName: "jooption", - itemTagName: "jooptionitem" -}); -/** - joPasswordInput - =============== - - Secret data input field (e.g. displays `******` instead of `secret`). - - Extends - ------- - - - joInput - - > Note that this requires CSS3 which is known not to be currently supported - > in Opera or Internet Explorer. - -*/ -joPasswordInput = function(data) { - joInput.apply(this, arguments); -}; -joPasswordInput.extend(joInput, { - className: "password", - type: "password" -}); -/** - joPopup - ======= - - A simple popup control. Pass in the UI contents as you would - any other subclass of joContainer (e.g. joCard). - - Methods - ------- - - - `show()` - - `hide()` - - These do what you'd expect. - - Extends - ------- - - - joContainer - - Events - ------ - - - `showEvent` - - `hideEvent` - - -*/ - -joPopup = function() { - this.showEvent = new joSubject(this); - this.hideEvent = new joSubject(this); - - joContainer.apply(this, arguments); -}; -joPopup.extend(joContainer, { - tagName: "jopopup", - - setEvents: function() { - joEvent.on(this.container, "mousedown", this.onClick, this); - }, - - onClick: function(e) { - joEvent.stop(e); - }, - - hide: function() { - joEvent.on(this.container, "webkitTransitionEnd", this.onHide, this); - this.container.className = 'hide'; - }, - - onHide: function() { - this.hideEvent.fire(); - }, - - show: function() { - this.container.className = 'show'; - this.showEvent.fire(); - } -}); -/** - joScreen - ======== - - Abstraction layer for the device screen. Uses document.body as its - DOM element and allows other controls to be nested within (usually - a joStack or other high-level containers or controls). - - Methods - ------- - - - `alert(title, message, buttons)` - - Simple alert box. The `buttons` parameter is optional; a simple - "OK" button is added if nothing is specified. - - - `showPopup(joView)` - - `hidePopup(joView)` - - These methods allow you to do a completely custom modal joPopup. - Pass in either a joView, an array of them, or and HTMLElement - or a string, the same as you would when you create a joCard or - other child of joContainer. - - Extends - ------- - - - joContainer - - Use - --- - - var x = new joScreen([ - new joNav(), - new joStack(), - new joToolbar() - ]); - - // show a simple alert dialog - x.alert("Hello", "This is an alert"); - - // a more complex alert - x.alert("Hola", "Do you like this alert?", [ - { label: "Yes", action: yesFunction, context: this }, - { label: "No", action: noFunction, context: this } - ]); - - // a completely custom popup - x.showPopup(myView); - - Events - ------ - - - `resizeEvent` - - `menuEvent` - - `activateEvent` - - `deactivateEvent` - - `backEvent` - - `forwardEvent` - -*/ - -joScreen = function() { - this.resizeEvent = new joSubject(this); - this.menuEvent = new joSubject(this); - this.activateEvent = new joSubject(this); - this.deactivateEvent = new joSubject(this); - this.backEvent = new joSubject(this); - this.forwardEvent = new joSubject(this); - - joContainer.apply(this, arguments); -}; -joScreen.extend(joContainer, { - tagName: "screen", - - setupEvents: function() { - joEvent.on(window, "resize", this.resizeEvent.fire, this); - joEvent.on(window, "appmenushow", this.menuEvent.fire, this); - joEvent.on(window, "activate", this.activateEvent.fire, this); - joEvent.on(window, "deactivate", this.deactivateEvent.fire, this); - joEvent.on(window, "back", this.backEvent.fire, this); - }, - - createContainer: function() { - return document.body; - }, - - // show a popup made from your own UI controls - showPopup: function(data) { - // take a view, a DOM element or some HTML and - // make it pop up in the screen. - if (!this.popup) { - this.shim = new joShim( - new joFlexcol([ - ' ', - this.popup = new joPopup(data), - ' ' - ]) - ); - } - else { - this.popup.setData(data); - } -// this.shim.showEvent.subscribe(this.popup.show, this); - this.shim.show(); - this.popup.show(); - }, - - hidePopup: function() { - if (this.shim) - this.shim.hide(); - }, - - // shortcut to a simple alert dialog, not the most efficient - // way to do this, but for now, it serves its purpose and - // the API is clean enough. - alert: function(title, msg, options, context) { - var buttons = []; - var callback; - - var context = (typeof context === 'object') ? context : null; - - if (typeof options === 'object') { - if (options instanceof Array) { - // we have several options - for (var i = 0; i < options.length; i++) - addbutton(options[i]); - } - else { - addbutton(options); - } - } - else if (typeof options === 'string') { - addbutton({ label: options }); - } - else { - if (typeof options === 'function') - callback = options; - - addbutton(); - } - - var view = [ - new joTitle(title), - new joHTML(msg), - buttons - ]; - this.showPopup(view); - - var self = this; - - function addbutton(options) { - if (!options) - var options = { label: 'OK' }; - - var button = new joButton(options.label); - button.selectEvent.subscribe( - function() { - if (options.action) - options.action.call(options.context); - - defaultaction(); - }, options.context || self - ); - - buttons.push(button); - } - - function defaultaction() { - self.hidePopup(); - if (callback) { - if (context) - callback.call(context); - else - callback(); - } - } - } -}); - -/** - joShim - ====== - - A simple screen dimmer. Used mostly for popups and other - modal use cases. - - Methods - ------- - - `show()` - - `hide()` - - These do what you'd expect. - - Extends - ------- - - joView - - Events - ------ - - - `showEvent` - - `hideEvent` - -*/ - -joShim = function() { - this.showEvent = new joSubject(this); - this.hideEvent = new joSubject(this); - this.selectEvent = new joSubject(this); - - joContainer.apply(this, arguments); -}; -joShim.extend(joContainer, { - tagName: "joshim", - - setEvents: function() { - joEvent.on(this.container, "mousedown", this.onMouseDown, this); - }, - - onMouseDown: function(e) { - joEvent.stop(e); - this.hide(); -// this.selectEvent.fire(); - }, - - hide: function() { - this.container.className = ''; - joEvent.on(this.container, "webkitTransitionEnd", this.onHide, this); - }, - - show: function() { - this.attach(); - - this.container.className = 'show'; - joEvent.on(this.container, "webkitTransitionEnd", this.onShow, this); - - // default parent to the document body - if (!this.lastParent) - this.lastParent = document.body; - }, - - onShow: function() { - this.showEvent.fire(); - }, - - onHide: function() { - this.detach(); - this.hideEvent.fire(); - } -}); -/** - joSound - ======== - - Play preloaded sound effects using the HTML5 `Audio` object. This module could - be wildly different for various platforms. Be warned. - - Methods - ------- - - - `play()` - - `pause()` - - `rewind()` - - `stop()` - - Basic sound controls. - - - `setLoop(n)` - - Tell the joSound to automatically loop `n` times. Set to `-1` to loop - continuously until `pause()`. - - - `setVolume(level)` - - Level is a decimal value from `0` to `1`. So, half volume would be `0.5`. - - Events - ------ - - - `endedEvent` - - `errorEvent` - -*/ -joSound = function(filename, repeat) { - this.endedEvent = new joSubject(this); - this.errorEvent = new joSubject(this); - - if (typeof Audio == 'undefined') - return; - - this.filename = filename; - this.audio = new Audio(); - this.audio.autoplay = false; - - if (!this.audio) - return; - - joDefer(function() { - this.audio.src = filename; - this.audio.load(); - }, this, 5); - - this.setRepeatCount(repeat); - - joEvent.on(this.audio, "ended", this.onEnded, this); - -// this.pause(); -}; -joSound.prototype = { - play: function() { - if (!this.audio || this.audio.volume == 0) - return; - - this.audio.play(); - - return this; - }, - - onEnded: function(e) { - this.endedEvent.fire(this.repeat); - - if (++this.repeat < this.repeatCount) - this.play(); - else - this.repeat = 0; - }, - - setRepeatCount: function(repeat) { - this.repeatCount = repeat; - this.repeat = 0; - - return this; - }, - - pause: function() { - if (!this.audio) - return; - - this.audio.pause(); - - return this; - }, - - rewind: function() { - if (!this.audio) - return; - - try { - this.audio.currentTime = 0.0; - } - catch (e) { - joLog("joSound: can't rewind..."); - } - - this.repeat = 0; - - return this; - }, - - stop: function() { - this.pause(); - this.rewind(); - - this.repeat = 0; - - return this; - }, - - setVolume: function(vol) { - if (!this.audio || vol < 0 || vol > 1) - return; - - this.audio.volume = vol; - - return this; - } -}; -/** - joStackScroller - =============== - - What happens when you mix joStack and joScroller? You get this - class. Use exactly as you would joStack, only it automatically - puts a scroller in the stack as needed. At some point, this - might get folded into joStack, but for now it's a special class. - - It also handles the `scrollTo()` and `scrollBy()` methods from - joScroller. - - Extends - ------- - - joStack - - joScroller -*/ - -joStackScroller = function(data) { - this.scrollers = [ - new joScroller(), - new joScroller() - ]; - this.scroller = this.scrollers[0]; - - joStack.apply(this, arguments); - - this.scroller.attach(this.container); -}; -joStackScroller.extend(joStack, { - type: "scroll", - scrollerindex: 1, - scroller: null, - scrollers: [], - - switchScroller: function() { - this.scrollerindex = this.scrollerindex ? 0 : 1; - this.scroller = this.scrollers[this.scrollerindex]; - }, - - getLastScroller: function() { - return this.scrollers[this.scrollerindex ? 0 : 1]; - }, - - scrollTo: function(something) { - this.scroller.scrollTo(something); - }, - - scrollBy: function(y) { - this.scroller.scrollBy(y); - }, - - getChildStyleContainer: function() { - return this.scroller.container; - }, - - getContentContainer: function() { - return this.scroller.container; - }, - - appendChild: function(child) { - var scroller = this.scroller; - scroller.setData(child); - this.container.appendChild(scroller.container); - }, - - getChild: function() { - return this.scroller.container || null; - }, - - forward: function() { - if (this.index < this.data.length - 1) - this.switchScroller(); - - joStack.prototype.forward.call(this); - }, - - back: function() { - if (this.index > 0) - this.switchScroller(); - - joStack.prototype.forward.call(this); - }, - - home: function() { - if (this.data && this.data.length && this.data.length > 1) { - this.switchScroller(); - joStack.prototype.home.call(this); - } - }, - - push: function(o) { - // don't push the same view we already have - if (this.data && this.data.length && this.data[this.data.length - 1] === o) - return; - - this.switchScroller(); - - joDOM.removeCSSClass(o, 'flick'); - joDOM.removeCSSClass(o, 'flickback'); - - this.scroller.setData(o); - this.scroller.scrollTo(0, true); - - joStack.prototype.push.call(this, o); - }, - - pop: function() { - if (this.data.length > this.locked) - this.switchScroller(); - - joStack.prototype.pop.call(this); - } -}); - -/** - joTabBar - ========= - - Tab bar widget. - - Extends - ------- - - - joList - - Model - ----- - - Data is expected to be an array of `{ type: "", label: ""}` objects, - in the display order for the bar. - -*/ -joTabBar = function() { - joList.apply(this, arguments); -}; -joTabBar.extend(joList, { - tagName: "jotabbar", - - formatItem: function(data, index) { - var o = document.createElement("jotab"); - - if (data.label) - o.innerHTML = data.label; - - if (data.type) - o.className = data.type; - - o.setAttribute("index", index); - - return o; - } -}); -/** - joTable - ======= - - Table control, purely visual representation of tabular data (usually - an array of arrays). - - Use - --- - - // simple table with inline data - var x = new joTable([ - ["Nickname", "Phone", "Email"], - ["Bob", "555-1234", "bob@bobco.not"], - ["Jo", "555-3456", "jo@joco.not"], - ["Jane", "555-6789", "jane@janeco.not"] - ]); - - // we can respond to touch events in the table - x.selectEvent.subscribe(function(index, table) { - // get the current row and column - joLog("Table cell clicked:", table.getRow(), table.getCol()); - - // you can also get at the cell HTML element as well - joDOM.setStyle(table.getNode(), { fontWeight: "bold" }); - }); - - Extends - ------- - - - joList - - Methods - ------- - - - `setCell(row, column)` - - Sets the active cell for the table, also makes it editiable and sets focus. - - - `getRow()`, `getCol()` - - Return the current row or column -*/ - -joTable = function(data) { - joList.apply(this, arguments); -}; -joTable.extend(joList, { - tagName: "jotable", - - // default row formatter - formatItem: function(row, index) { - var tr = document.createElement("tr"); - - for (var i = 0, l = row.length; i < l; i++) { - var o = document.createElement(index ? "td" : "th"); - o.innerHTML = row[i]; - - // this is a little brittle, but plays nicely with joList's select event - o.setAttribute("index", index * l + i); - tr.appendChild(o); - } - - return tr; - }, - - // override joList's getNode - getNode: function(index) { - var row = this.getRow(index); - var col = this.getCol(index); - - return this.container.childNodes[row].childNodes[col]; - }, - - getRow: function(index) { - if (typeof index == "undefined") - var index = this.getIndex(); - - var rowsize = this.data[0].length; - return Math.floor(index / rowsize); - }, - - getCol: function(index) { - if (typeof index == "undefined") - var index = this.getIndex(); - - var rowsize = this.data[0].length; - return index % rowsize; - } -}); - -/** - joTextarea - ========== - - Multi-line text input control. When you instantiate or use `setData()`, you can - either pass in an initial value or a reference to a joDataSource object which it, - like other joControl instances, will bind to. - - Basically, this is just a multi-line version of joInput. - - Use - --- - - // simple multi-line field - var sample = "This is some sample text to edit."; - var x = new joTextarea(sample); - - // setting the style inline using chaining - var f = new joTextarea(sample).setStyle({ - minHeight: "100px", - maxHeight: "300px" - }); - - // adding a simple change event handler using chaining - var h = new joTextarea(sample).changeEvent.subscribe(function(data) { - joLog("text area changed:", data); - }); - - // attach the value to a preference - var y = new joTextarea(joPreference.bind("username")); - - // attach input control to a custom joDataSource - var username = new joDataSource("bob"); - var z = new joTextarea(username); - - Extends - ------- - - - joInput - -*/ -joTextarea = function(data) { - joInput.apply(this, arguments); -}; -joTextarea.extend(joInput, { - tagName: "textarea", - - onKeyDown: function(e) { - // here we want the enter key to work, overriding joInput's behavior - return false; - } -}); - -/** - joTitle - ======= - - Title view, purely a visual presentation. - - Extends - ------- - - - joContainer - -*/ -joTitle = function(data) { - joView.apply(this, arguments); -}; -joTitle.extend(joView, { - tagName: "jotitle" -}); - -/** - joToolbar - ========= - - Locks UI controls to the bottom of whatever you put this container into. - - Extends - ------- - - - joContainer - -*/ -joToolbar = function(data) { - joContainer.apply(this, arguments); -}; -joToolbar.extend(joContainer, { - tagName: "jotoolbar" -}); -joForm = function() { - joContainer.apply(this, arguments); -}; -joForm.extend(joContainer, { - tagName: "form" -});/** - joDialog - ======== - - This is a higher level container that wraps a joPopup with a joShim. -*/ -joDialog = function(data) { - joShim.call(this, new joFlexcol([ - '', - new joPopup( - new joScroller(data) - ).setStyle("show"), - '' - ])); -}; -joDialog.extend(joShim, { -}); -/** - joSelectList - ============ - - A selection list of options used by joSelect. - - Extends - ------- - - - joList -*/ - -joSelectList = function() { - joList.apply(this, arguments); -}; -joSelectList.extend(joList, { - tagName: "joselectlist" -}); -/** - joNavbar - ======== - - Floating navigation control. Usually tied to a joStack or joStackScroller. - Will handle display of a "back" button (controllable in CSS) and show the - title string of the current view in a stack (if it exists). - - Use - --- - - // make a stack - var stack = new joStackScroller(); - - // new navbar - var x = new joNavbar(); - - // link to a stack - x.setStack(stack); - - Methods - ------- - - - `back()` - - Signals the associated stack to move back in its stack (i.e. calls - the stack's `pop()` method). - - - `setStack(joStack or joStackScroller)` - - Links this control to a stack. - -*/ - -joNavbar = function(title) { - if (title) - this.firstTitle = title; - - var ui = [ - this.titlebar = new joView(title || ' ').setStyle('title'), - new joFlexrow([this.back = new joBackButton('Back').selectEvent.subscribe(this.back, this), ""]) - ]; - - joContainer.call(this, ui); -}; -joNavbar.extend(joContainer, { - tagName: "jonavbar", - stack: null, - - back: function() { - if (this.stack) - this.stack.pop(); - }, - - setStack: function(stack) { - if (this.stack) { - this.stack.pushEvent.unsubscribe(this.update, this); - this.stack.popEvent.unsubscribe(this.update, this); - } - - if (!stack) { - this.stack = null; - return; - } - - this.stack = stack; - - stack.pushEvent.subscribe(this.update, this); - stack.popEvent.subscribe(this.update, this); - - this.refresh(); - }, - - update: function() { - if (!this.stack) - return; - - joDOM.removeCSSClass(this.back, 'selected'); - joDOM.removeCSSClass(this.back, 'focus'); - -// console.log('update ' + this.stack.data.length); - - if (this.stack.data.length > 1) - joDOM.addCSSClass(this.back, 'active'); - else - joDOM.removeCSSClass(this.back, 'active'); - - var title = this.stack.getTitle(); - - if (typeof title === 'string') - this.titlebar.setData(title); - else - this.titlebar.setData(this.firstTitle); - }, - - setTitle: function(title) { - this.titlebar.setData(title); - this.firstTitle = title; - return this; - } -}); - - -/** - joBackButton - ============ - - A "back" button, which can be made to be shown only in appropriate - platforms (e.g. iOS, Safari, Chrome) through CSS styling. - - See joNavbar for more information. - - Extends - ------- - - - joButton - -*/ -joBackButton = function() { - joButton.apply(this, arguments); -}; -joBackButton.extend(joButton, { - tagName: "jobackbutton" -}); -/** - joSelect - ======== - - Multi-select control which presents a set of options for the user - to choose from. - - Methods - ------- - - - `setValue(value)` - - Set the current value, based on the index for the option list. - - - `getValue()` - - Returns the index of the current selected item. - - Extends - ------- - - - joExpando - - Consumes - -------- - - - joSelectTitle - - joSelectList - - Properties - ---------- - - - `field` - - Reference to the value field for this control. - - - `list` - - Reference to the joSelectList for this control. - - Use - --- - - // pass in an array of options - var x = new joSelect([ "Apples", "Oranges", "Grapes" ]); - - // pass in a current value - var y = new joSelect([ "Apples", "Oranges", "Grapes" ], 2); - - // respond to the change event - y.changeEvent = function(value, list) { - console.log("Fruit: " + list.getNodeValue(value)); - }); - -*/ -joSelect = function(data, value) { - var v = value; - if (value instanceof joDataSource) - v = value.getData(); - - var ui = [ - this.field = new joSelectTitle(v), - this.list = new joSelectList(data, value) - ]; - - this.field.setList(this.list); - - this.changeEvent = this.list.changeEvent; - this.selectEvent = this.list.selectEvent; - - joExpando.call(this, ui); - this.container.setAttribute("tabindex", 1); - - this.field.setData(this.list.value); - - this.list.selectEvent.subscribe(this.setValue, this); -}; -joSelect.extend(joExpando, { - setValue: function(value, list) { - if (list) { - this.field.setData(value); - this.close(); - } - else { - this.field.setData(value); - } - }, - - getValue: function() { - return this.list.getValue(); - }, - - setEvents: function() { - joControl.prototype.setEvents.call(this); - }, - - onBlur: function(e) { - joEvent.stop(e); - joDOM.removeCSSClass(this, "focus"); - this.close(); - } -}); - -/** - joSelectTitle - ============= - - joSelect flavor of joExpandoTitle. - - Extends - ------- - - - joExpandoTitle -*/ -joSelectTitle = function() { - joExpandoTitle.apply(this, arguments); -}; -joSelectTitle.extend(joExpandoTitle, { - list: null, - - setList: function(list) { - this.list = list; - }, - - setData: function(value) { - if (this.list) - joExpandoTitle.prototype.setData.call(this, this.list.getNodeData(value) || "Select..."); - else - joExpandoTitle.prototype.setData.call(this, value); - } -}); -/** - joToggle - ======== - - Boolean widget (on or off). - - Methods - ------- - - - `setLabels(Array)` - - You can change the labels for this control, which default to "Off" and "On". - - Extends - ------- - - - joControl - - Use - --- - - // simple - var x = new joToggle(); - - // with value - var y = new joToggle(true); - - // with custom labels - var z = new joToggle().setLabels(["No", "Yes"]); - - See Data Driven Controls for more examples. - -*/ -joToggle = function(data) { - joControl.call(this, data); -}; -joToggle.extend(joControl, { - tagName: "jotoggle", - button: null, - labels: ["Off", "On"], - - setLabels: function(labels) { - if (labels instanceof Array) - this.labels = labels; - else if (arguments.length == 2) - this.labels = arguments; - - this.draw(); - - return this; - }, - - select: function(e) { - if (e) - joEvent.stop(e); - - this.setData((this.data) ? false : true); - }, - - onBlur: function(e) { - joEvent.stop(e); - this.blur(); - }, - - draw: function() { - if (!this.container) - return; - - if (!this.container.firstChild) { - this.button = joDOM.create("div"); - this.container.appendChild(this.button); - } - - this.button.innerHTML = this.labels[(this.data) ? 1 : 0]; - - if (this.data) - joDOM.addCSSClass(this.container, "on"); - else - joDOM.removeCSSClass(this.container, "on"); - } -}); -/** - joSlider - ======== - - Slider control, horizontal presentation (may be extended later to allow for - vertical and x/y). - - Extends - ------- - - - joControl - - Methods - ------- - - - `setRange(min, max, snap)` - - Where `min`/`max` is a number, either integer or decimal, doesn't matter. If `max` - and `min` are integers, then `snap` defaults to `1`, otherwise it is set to `0` (no - snap, which allows free movement). - - The optional `snap` value adjusts the granularuty of choices. Set to `0` for - free-floating, or any other positive number. Any `snap` that is less than `0` - or greater than the total range of possible values will be ignored. - - Use - --- - - // basic slider, will allow any decimal value - // between 0 and 1, defaults to 0 - var x = new joSlider(); - - // custom range and default value set - var y = new joSlider(0).setRange(-10, 10); - - // percent slider, with 5% snap - var z = new joSlider(0).setRange(0, 100, 5); - - // responding to change events - var r = new joSlider().changEvent.subscribe(function(value) { - console.log(value); - }, this); - -*/ - -joSlider = function(value) { - this.min = 0; - this.max = 1; - this.snap = 0; - this.range = 1; - this.thumb = null; - this.horizontal = 1; - this.vertical = 0; - this.moved = false; - this.jump = true; - - joControl.call(this, null, value); -}; -joSlider.extend(joControl, { - tagName: "joslider", - - setRange: function(min, max, snap) { - if (min >= max) { - joLog("WARNING: joSlider.setRange, min must be less than max."); - return this; - } - - this.min = min || 0; - this.max = max || 1; - - if (min < 0 && max >= 0) - this.range = Math.abs(min) + max; - else if (min < 0 && max <= 0) - this.range = min - max; - else - this.range = max - min; - - if (typeof snap !== 'undefined') - this.snap = (snap >= 0 && snap <= this.range) ? snap : 0; - else - this.snap = 0; - - this.setValue(this.value); - - return this; - }, - - setValue: function(value, silent) { - var v = this.adjustValue(value); - - if (v != this.value) { - joControl.prototype.setValue.call(this, v); - if (!silent) - this.draw(); - } - - return this; - }, - - adjustValue: function(v) { - var value = v; - - if (this.snap) - value = Math.floor(value / this.snap) * this.snap; - - if (value < this.min) - value = this.min; - else if (value > this.max) - value = this.max; - - return value; - }, - - createContainer: function() { - var o = joDOM.create(this.tagName); - - if (o) { - o.setAttribute("tabindex", "1"); - - var t = joDOM.create("josliderthumb"); - o.appendChild(t); - this.thumb = t; - } - - return o; - }, - - onDown: function(e) { - joEvent.stop(e); - - this.reset(); - - var node = this.container.firstChild; - - this.inMotion = true; - this.moved = false; - - if (!this.mousemove) { - this.mousemove = joEvent.on(document.body, "mousemove", this.onMove, this); - this.mouseup = joEvent.capture(document.body, "mouseup", this.onUp, this); - } - }, - - reset: function() { - this.moved = false; - this.inMotion = false; - this.firstX = -1; - this.firstY = -1; - }, - - onMove: function(e) { - if (!this.inMotion) - return; - - joEvent.stop(e); - e.preventDefault(); - - var point = this.getMouse(e); - - var y = point.y; - var x = point.x; - - if (this.firstX == -1) { - this.firstX = x; - this.firstY = y; - - this.ox = this.thumb.offsetLeft; - this.oy = this.thumb.offsetTop; - } - - var x = (x - this.firstX) + this.ox; - var y = (y - this.firstY) + this.oy; - - if (x > 4 || y > 4) - this.moved = true; - - var t = this.thumb.offsetWidth; - var w = this.container.offsetWidth - t; - - if (x < 0) - x = 0; - else if (x > w) - x = w; - - if (!this.snap) - this.moveTo(x); - - this.setValue((x / w) * this.range + this.min, !this.snap); - }, - - moveTo: function(x) { - this.thumb.style.left = x + "px"; - }, - - initValue: function(value) { - var t = this.container.firstChild.offsetWidth; - var w = this.container.offsetWidth - t; - - var x = Math.floor((this.value / this.range) * w); - - this.moveTo(x); - - return this; - }, - - onUp: function (e) { - if (!this.inMotion) - return; - - joEvent.remove(document.body, "mousemove", this.mousemove); - joEvent.remove(document.body, "mouseup", this.mouseup); - this.mousemove = null; - - joEvent.stop(e); - joEvent.preventDefault(e); - - joDefer(function() { - this.reset(); - }, this); - }, - - setEvents: function() { - joEvent.on(this.container, "click", this.onClick, this); - joEvent.on(this.thumb, "mousedown", this.onDown, this); - - // we have to adjust if the window changes size - joGesture.resizeEvent.subscribe(this.draw, this); - - console.log('setevents'); - }, - - onClick: function(e) { - if (this.inMotion || this.moved) - return; - - joEvent.stop(e); - joEvent.preventDefault(e); - - var point = this.getMouse(e); - - var l = joDOM.pageOffsetLeft(this.container); -// console.log(l); - - var x = Math.floor((point.x - l) - this.thumb.offsetWidth * 1.5); - -// console.log(x); - - var t = this.thumb.offsetWidth; - - x = x - t; - - var w = this.container.offsetWidth - t; - - if ((x < t && this.snap) || x < 0) - x = 0; - else if (x > w) - x = w; - -// this.moveTo(x); - - this.setValue((x / w) * this.range + this.min); - }, - - getMouse: function(e) { - return { - x: (this.horizontal) ? e.screenX : 0, - y: (this.vertical) ? e.screenY : 0 - }; - }, - - draw: function() { - if (!this.container) - this.setContainer(); - - this.initValue(this.value); - } -}); - diff --git a/assets/www/js/jo_min.js b/assets/www/js/jo_min.js deleted file mode 100644 index d689a98..0000000 --- a/assets/www/js/jo_min.js +++ /dev/null @@ -1,379 +0,0 @@ - -joLog=function(){var strings=[];for(var i=0;i=0){this.platform=this.useragent[i];break;}}} -if(joEvent){var o=document.createElement('div');var test=("ontouchstart"in o);if(!test){o.setAttribute("ontouchstart",'return;');test=(typeof o.ontouchstart==='function');} -joEvent.touchy=test;o=null;} -if(joGesture) -joGesture.load();var s=joScroller.prototype;if(typeof document.body.style.webkitTransition!=="undefined"){} -else if(typeof document.body.style.MozTransition!=="undefined"){s.transitionEnd="transitionend";s.setPosition=function(x,y,node){node.style.MozTransform="translate("+x+"px,"+y+"px)";};} -else if(typeof document.body.style.msTransform!=="undefined"){s.transitionEnd="transitionend";s.setPosition=function(x,y,node){node.style.msTransform="translate("+x+"px,"+y+"px)";};} -else if(typeof document.body.style.OTransition!=="undefined"){s.transitionEnd="otransitionend";s.setPosition=function(x,y,node){node.style.OTransform="translate("+x+"px,"+y+"px)";};} -else{s.velocity=0;s.bump=0;s.transitionEnd="transitionend";s.setPosition=function(x,y,node){if(this.vertical) -node.style.top=y+"px";if(this.horizontal) -node.style.left=x+"px";};} -joLog("Jo",this.version,"loaded for",this.platform,"environment");this.loadEvent.fire();},tagMap:{},tagMapLoaded:false,initTagMap:function(){if(this.tagMapLoaded) -return;var key=this.tagMap;key.JOVIEW=joView;key.BODY=joScreen;for(var p in window){var o=window[p];if(typeof o==='function'&&o.prototype&&typeof o.prototype.tagName!=='undefined'&&o.prototype instanceof joView){var tag=o.prototype.tagName.toUpperCase();if(o.prototype.type){if(!key[tag]) -key[tag]={};key[tag][o.prototype.type]=o;} -else{key[tag]=o;}}}},getPlatform:function(){return this.platform;},matchPlatform:function(test){return(test.indexOf(this.platform)>=0);},getVersion:function(){return this.version;},getLanguage:function(){return this.language;}};joDOM={enabled:false,get:function(id){if(typeof id==="string"){return document.getElementById(id);} -else if(typeof id==='object'){if(id instanceof joView) -return id.container;else -return id;}},remove:function(node){if(node.parentNode){node.parentNode.removeChild(node);}},enable:function(){this.enabled=true;},getParentWithin:function(node,ancestor){while(node.parentNode!==window&&node.parentNode!==ancestor){node=node.parentNode;} -return node;},addCSSClass:function(node,classname){var node=joDOM.get(node);if(typeof node.className!=="undefined"){var n=node.className.split(/\s+/);for(var i=0,l=n.length;i'+style+'';document.body.appendChild(css);return css;},removeCSS:function(node){document.body.removeChild(node);},loadCSS:function(filename,oldnode){if(oldnode) -var css=oldnode;else -var css=joDOM.create('link');css.rel='stylesheet';css.type='text/css';css.href=filename+(jo.debug?("?"+joTime.timestamp()):"");if(!oldnode) -document.body.appendChild(css);return css;},pageOffsetLeft:function(node){var l=0;while(typeof node!=='undefined'&&node&&node.parentNode!==window){if(node.offsetLeft) -l+=node.offsetLeft;node=node.parentNode;} -return l;},pageOffsetTop:function(node){var t=0;while(typeof node!=='undefined'&&node&&node.parentNode!==window){t+=node.offsetTop;node=node.parentNode;} -return t;}};joCSSRule=function(data){this.setData(data);};joCSSRule.prototype={container:null,setData:function(data){this.data=data||"";this.enable();},clear:function(){this.setData();},disable:function(){joDOM.removeCSS(this.container);},enable:function(){this.container=joDOM.applyCSS(this.data,this.container);}};joEvent={eventMap:{"mousedown":"touchstart","mousemove":"touchmove","mouseup":"touchend","mouseout":"touchcancel"},touchy:false,getTarget:function(e){if(!e) -var e=window.event;return e.target?e.target:e.srcElement;},capture:function(element,event,call,context,data){return this.on(element,event,call,context,data,true);},on:function(element,event,call,context,data,capture){if(!call||!element) -return false;if(this.touchy){if(this.eventMap[event]) -event=this.eventMap[event];} -var element=joDOM.get(element);var call=call;var data=data||"";function wrappercall(e){if(e.touches&&e.touches.length==1){var touches=e.touches[0];e.pageX=touches.pageX;e.pageY=touches.pageY;e.screenX=touches.screenX;e.screenY=touches.screenY;e.clientX=touches.clientX;e.clientY=touches.clientY;} -if(context) -call.call(context,e,data);else -call(e,data);};wrappercall.capture=capture||false;if(!window.addEventListener) -element.attachEvent("on"+event,wrappercall);else -element.addEventListener(event,wrappercall,capture||false);return wrappercall;},remove:function(element,event,call,capture){if(this.touchy){if(this.eventMap[event]){event=this.eventMap[event];}} -if(typeof element.removeEventListener!=='undefined') -element.removeEventListener(event,call,capture||false);},stop:function(e){if(e.stopPropagation) -e.stopPropagation();else -e.cancelBubble=true;},preventDefault:function(e){e.preventDefault();},block:function(e){if(window.event) -var e=window.event;if(typeof e.target=='undefined') -e.target=e.srcElement;switch(e.target.nodeName.toLowerCase()){case'input':case'textarea':return true;break;default:return false;}}};joSubject=function(subject){this.subscriptions=[];this.subject=subject;};joSubject.prototype={last:-1,subscribe:function(call,observer,data){if(!call) -return false;var o={"call":call};if(observer) -o.observer=observer;if(data) -o.data=data;this.subscriptions.push(o);return this.subject;},unsubscribe:function(call,observer){if(!call) -return false;for(var i=0,l=this.subscriptions.length;ithis.getData().length) -end=this.getData().length;if(start<0) -start=0;return this.data.slice(start,end);},refresh:function(){},setPageSize:function(length){this.pageSize=length;},getPageSze:function(){return this.pageSize;},load:function(data){this.data=data;this.changeEvent.fire(data);},error:function(msg){this.errorEvent.fire(msg);}};joRecord=function(data){joDataSource.call(this,data);this.delegate={};};joRecord.extend(joDataSource,{link:function(p){return this.getDelegate(p);},getDelegate:function(p){if(!this.delegate[p]) -this.delegate[p]=new joProperty(this,p);return this.delegate[p];},getProperty:function(p){return this.data[p];},setProperty:function(p,data){if(this.data[p]===data) -return;this.data[p]=data;this.changeEvent.fire(this);if(this.autoSave) -this.save();return this;},load:function(){console.log("TODO: extend the load() method");return this;},save:function(){console.log("TODO: extend the save() method");return this;}});joProperty=function(datasource,p){joDataSource.call(this);this.changeEvent=new joSubject(this);datasource.changeEvent.subscribe(this.onSourceChange,this);this.datasource=datasource;this.p=p;};joProperty.extend(joDataSource,{setData:function(data){if(this.datasource) -this.datasource.setProperty(this.p,data);return this;},getData:function(){if(!this.datasource) -return null;return this.datasource.getProperty(this.p);},onSourceChange:function(){this.changeEvent.fire(this.getData());}});joDatabase=function(datafile,size){this.openEvent=new joEvent.Subject(this);this.closeEvent=new joEvent.Subject(this);this.errorEvent=new joEvent.Subject(this);this.datafile=datafile;this.size=size||256000;this.db=null;};joDatabase.prototype={open:function(){this.db=openDatabase(this.datafile,"1.0",this.datafile,this.size);if(this.db){this.openEvent.fire();} -else{joLog("DataBase Error",this.db);this.errorEvent.fire();}},close:function(){this.db.close();this.closeEvent.fire();},now:function(offset){var date=new Date();if(offset) -date.setDate(date.valueOf()+(offset*1000*60*60*24));return date.format("yyyy-mm-dd");}};joSQLDataSource=function(db,query,args){this.db=db;this.query=(typeof query=='undefined')?"":query;this.args=(typeof args=='undefined')?[]:args;this.changeEvent=new joEvent.subject(this);this.errorEvent=new joEvent.subject(this);};joSQLDataSource.prototype={setDatabase:function(db){this.db=db;},setQuery:function(query){this.query=query;},setData:function(data){this.data=data;this.changeEvent.fire();},clear:function(){this.data=[];this.changeEvent.fire();},setParameters:function(args){this.args=args;},execute:function(query,args){this.setQuery(query||"");this.setParameters(args);if(this.query) -this.refresh();},refresh:function(){if(!this.db){this.errorEvent.fire();return;} -var self=this;if(arguments.length){var args=[];for(var i=0;i0)?setTimeout(onerror,timeout):null;req.open('GET',url,true);req.onreadystatechange=onchange;req.onError=onerror;req.send(null);function onchange(e){if(timer) -timer=clearTimeout(timer);if(req.readyState==4) -handler(req.responseText,0);} -function onerror(){handler(null,true);} -function handler(data,error){if(call){if(context) -call.call(context,data,error);else -call(error,data,error);}}} -function joScript(url,call,context){var node=joDOM.create('script');if(!node) -return;node.onload=onload;node.onerror=onerror;node.src=url;document.body.appendChild(node);function onerror(){handler(true);} -function onload(){handler(false);} -function handler(error){if(call){if(context) -call.call(context,error,url);else -call(error,url);} -document.body.removeChild(node);node=null;}} -joPreference=joRecord;joYQL=function(query){joDataSource.call(this);this.setQuery(query);};joYQL.extend(joDataSource,{baseurl:'http://query.yahooapis.com/v1/public/yql?',format:'json',query:'',exec:function(){var get=this.baseurl+"q="+encodeURIComponent(this.query) -+"&format="+this.format+"&callback="+joDepot(this.load,this);joScript(get,this.callBack,this);},load:function(data){var results=data.query&&data.query.results&&data.query.results.item;if(!results) -this.errorEvent.fire(data);else{this.data=results;this.changeEvent.fire(results);}},callBack:function(error){if(error) -this.errorEvent.fire();}});joDepotCall=[];joDepot=function(call,context){joDepotCall.push(handler);function handler(data){if(context) -call.call(context,data);else -call(data);};return"joDepotCall["+(joDepotCall.length-1)+"]";};joInterface=function(parent){jo.initTagMap();return this.get(parent);};joInterface.prototype={get:function(parent){parent=joDOM.get(parent);if(!parent) -parent=document.body;var ui={};var setContainer=joView.setContainer;var draw=joView.draw;parse(parent);joView.setContainer=setContainer;joView.draw=draw;function parse(node){if(!node) -return;var args="";if(node.childNodes&&node.firstChild){var kids=node.childNodes;args=[];for(var i=0,l=kids.length;i=0) -this.setValue(this.value,true);return;},deselect:function(){if(typeof this.container=='undefined'||!this.container['childNodes']) -return;var node=this.getNode(this.value);if(node){if(this.lastNode){joDOM.removeCSSClass(this.lastNode,"selected");this.value=null;}} -return this;},setValue:function(index,silent){this.value=index;if(index==null) -return;if(typeof this.container==='undefined'||!this.container||!this.container.firstChild){return this;} -var node=this.getNode(this.value);if(node){if(this.lastNode) -joDOM.removeCSSClass(this.lastNode,"selected");joDOM.addCSSClass(node,"selected");this.lastNode=node;} -if(index>=0&&!silent){this.fireSelect(index);this.changeEvent.fire(index);} -return this;},getNode:function(index){return this.container.childNodes[index];},fireSelect:function(index){this.selectEvent.fire(index);},getValue:function(){return this.value;},onMouseDown:function(e){joEvent.stop(e);var node=joEvent.getTarget(e);var index=-1;while(index==-1&&node!==this.container){index=node.getAttribute("index")||-1;node=node.parentNode;} -if(index>=0) -this.setValue(index);},refresh:function(){if(this.autoSort) -this.sort();joControl.prototype.refresh.apply(this);},getNodeData:function(index){if(this.data&&this.data.length&&index>=0&&indexb) -return 1;else if(a==b) -return 0;else -return-1;},setAutoSort:function(state){this.autoSort=state;return this;},next:function(){if(this.getValue()0) -this.setValue(this.value-1);}});joBusy=function(data){joContainer.apply(this,arguments);};joBusy.extend(joContainer,{tagName:"jobusy",draw:function(){this.container.innerHTML="";for(var i=0;i<9;i++) -this.container.appendChild(joDom.create("jobusyblock"));},setMessage:function(msg){this.message=msg||"";},setEvents:function(){return this;}});joCaption=function(data){joControl.apply(this,arguments);};joCaption.extend(joControl,{tagName:"jocaption"});joCard=function(data){joContainer.apply(this,arguments);};joCard.extend(joContainer,{tagName:"jocard"});joStack=function(data){this.visible=false;this.data=[];joContainer.apply(this,arguments);if(this.data&&!(this.data instanceof Array)) -this.data=[this.data];else if(this.data.length>1) -this.data=[this.data[0]];if(this.container&&this.container.firstChild) -this.container.innerHTML="";this.setLocked(true);this.pushEvent=new joSubject(this);this.popEvent=new joSubject(this);this.homeEvent=new joSubject(this);this.showEvent=new joSubject(this);this.hideEvent=new joSubject(this);this.backEvent=new joSubject(this);this.forwardEvent=new joSubject(this);this.index=0;this.lastIndex=0;this.lastNode=null;};joStack.extend(joContainer,{tagName:"jostack",type:"fixed",eventset:false,setEvents:function(){},onClick:function(e){joEvent.stop(e);},forward:function(){if(this.index0){this.index--;this.draw();this.backEvent.fire();}},draw:function(){if(!this.container) -this.createContainer();if(!this.data||!this.data.length) -return;jo.flag.stopback=this.index?true:false;var container=this.container;var oldchild=this.lastNode;var newnode=getnode(this.data[this.index]);var newchild=this.getChildStyleContainer(newnode);function getnode(o){return(o instanceof joView)?o.container:o;} -if(!newchild) -return;if(this.index>this.lastIndex){var oldclass="prev";var newclass="next";joDOM.addCSSClass(newchild,newclass);} -else if(this.indexthis.locked){var o=this.data.pop();this.index=this.data.length-1;this.draw();if(typeof o.deactivate==="function") -o.deactivate.call(o);if(!this.data.length) -this.hide();} -if(this.data.length>0) -this.popEvent.fire();},home:function(){if(this.data&&this.data.length&&this.data.length>1){var o=this.data[0];var c=this.data[this.index];if(o===c) -return;this.data=[o];this.lastIndex=1;this.index=0;this.draw();this.popEvent.fire();this.homeEvent.fire();}},showHome:function(){this.home();if(!this.visible){this.visible=true;joDOM.addCSSClass(this.container,"show");this.showEvent.fire();}},getTitle:function(){var c=this.data[this.index];if(typeof c.getTitle==='function') -return c.getTitle();else -return false;},show:function(){if(!this.visible){this.visible=true;joDOM.addCSSClass(this.container,"show");joDefer(this.showEvent.fire,this.showEvent,500);}},hide:function(){if(this.visible){this.visible=false;joDOM.removeCSSClass(this.container,"show");joDefer(this.hideEvent.fire,this.hideEvent,500);}}});joScroller=function(data){this.points=[];this.eventset=false;this.horizontal=0;this.vertical=1;this.inMotion=false;this.moved=false;this.mousemove=null;this.mouseup=null;this.bump=0;joContainer.apply(this,arguments);};joScroller.extend(joContainer,{tagName:"joscroller",velocity:1.6,transitionEnd:"webkitTransitionEnd",setEvents:function(){joEvent.capture(this.container,"click",this.onClick,this);joEvent.on(this.container,"mousedown",this.onDown,this);},onFlick:function(e){},onClick:function(e){if(this.moved){this.moved=false;joEvent.stop(e);joEvent.preventDefault(e);}},onDown:function(e){joEvent.stop(e);this.reset();var node=this.container.firstChild;joDOM.removeCSSClass(node,"flick");joDOM.removeCSSClass(node,"flickback");joDOM.removeCSSClass(node,"flickfast");this.start=this.getMouse(e);this.points.unshift(this.start);this.inMotion=true;if(!this.mousemove){this.mousemove=joEvent.capture(document.body,"mousemove",this.onMove,this);this.mouseup=joEvent.capture(document.body,"mouseup",this.onUp,this);}},reset:function(){this.points=[];this.moved=false;this.inMotion=false;},onMove:function(e){if(!this.inMotion) -return;joEvent.stop(e);e.preventDefault();var point=this.getMouse(e);var y=point.y-this.points[0].y;var x=point.x-this.points[0].x;this.points.unshift(point);if(this.points.length>7) -this.points.pop();var self=this;this.timer=window.setTimeout(function(){if(self.inMotion&&self.points.length>1) -self.points.pop();},100);this.scrollBy(x,y,true);if(!this.moved&&this.points.length>3) -this.moved=true;},onUp:function(e){if(!this.inMotion) -return;joEvent.remove(document.body,"mousemove",this.mousemove,true);joEvent.remove(document.body,"mouseup",this.mouseup,true);this.mousemove=null;this.inMotion=false;joEvent.stop(e);joEvent.preventDefault(e);var end=this.getMouse(e);var node=this.container.firstChild;var top=this.getTop();var left=this.getLeft();var dy=0;var dx=0;for(var i=0;i4||Math.abs(dx)*this.horizontal>4)){var flick=dy*(this.velocity*(node.offsetHeight/this.container.offsetHeight));var flickx=dx*(this.velocity*(node.offsetWidth/this.container.offsetWidth));if((flick+top0)||(flickx+left0)){joDOM.addCSSClass(node,"flickfast");} -else{joDOM.addCSSClass(node,"flick");} -this.scrollBy(flickx,flick,false);joDefer(this.snapBack,this,3000);} -else{joDefer(this.snapBack,this,10);}},getMouse:function(e){return{x:(this.horizontal)?e.screenX:0,y:(this.vertical)?e.screenY:0};},scrollBy:function(x,y,test){var node=this.container.firstChild;var top=this.getTop();var left=this.getLeft();var dy=Math.floor(top+y);var dx=Math.floor(left+x);if(this.vertical&&(node.offsetHeight<=this.container.offsetHeight)) -return;var max=0-node.offsetHeight+this.container.offsetHeight;var maxx=0-node.offsetWidth+this.container.offsetWidth;var ody=dy;var odx=dx;if(this.bump){if(dy>this.bump) -dy=this.bump;else if(dythis.bump) -dx=this.bump;else if(dy0) -y=0;if(!instant){joDOM.addCSSClass(node,'flick');} -else{joDOM.removeCSSClass(node,'flick');joDOM.removeCSSClass(node,'flickback');} -this.moveTo(0,y);},snapBack:function(){var node=this.container.firstChild;var top=this.getTop();var left=this.getLeft();var dy=top;var dx=left;var max=0-node.offsetHeight+this.container.offsetHeight;var maxx=0-node.offsetWidth+this.container.offsetWidth;if(this.eventset) -joEvent.remove(node,this.transitionEnd,this.eventset);this.eventset=null;joDOM.removeCSSClass(node,'flick');if(dy>0) -dy=0;else if(dy0) -dx=0;else if(dx=0) -this.close();else -this.open();},open:function(){joDOM.addCSSClass(this.container,"open");this.openEvent.fire();},close:function(){joDOM.removeCSSClass(this.container,"open");this.closeEvent.fire();}});joExpandoContent=function(){joContainer.apply(this,arguments);};joExpandoContent.extend(joContainer,{tagName:"joexpandocontent"});joExpandoTitle=function(data){joControl.apply(this,arguments);};joExpandoTitle.extend(joControl,{tagName:"joexpandotitle",setData:function(){joView.prototype.setData.apply(this,arguments);this.draw();},draw:function(){this.container.innerHTML=this.data+"";}});joFlexrow=function(data){joContainer.apply(this,arguments);};joFlexrow.extend(joContainer,{tagName:"joflexrow"});joFlexcol=function(data){joContainer.apply(this,arguments);};joFlexcol.extend(joContainer,{tagName:"joflexcol"});joFocus={last:null,set:function(control){if(this.last&&this.last!==control) -this.last.blur();if(control&&control instanceof joControl){control.focus();this.last=control;}},get:function(control){return this.last;},refresh:function(){if(this.last) -this.last.focus();},clear:function(){this.set();}};joFooter=function(data){joContainer.apply(this,arguments);};joFooter.extend(joContainer,{tagName:"jofooter"});joGesture={load:function(){this.upEvent=new joSubject(this);this.downEvent=new joSubject(this);this.leftEvent=new joSubject(this);this.rightEvent=new joSubject(this);this.forwardEvent=new joSubject(this);this.backEvent=new joSubject(this);this.homeEvent=new joSubject(this);this.closeEvent=new joSubject(this);this.activateEvent=new joSubject(this);this.deactivateEvent=new joSubject(this);this.resizeEvent=new joSubject(this);this.setEvents();},setEvents:function(){joEvent.on(document.body,"keydown",this.onKeyDown,this);joEvent.on(document.body,"keyup",this.onKeyUp,this);joEvent.on(document.body,"unload",this.closeEvent,this);joEvent.on(window,"activate",this.activateEvent,this);joEvent.on(window,"deactivate",this.deactivateEvent,this);joEvent.on(window,"resize",this.resize,this);},resize:function(){this.resizeEvent.fire(window);},onKeyUp:function(e){if(!e) -var e=window.event;if(e.keyCode==18){this.altkey=false;return;} -if(e.keyCode==27){if(jo.flag.stopback){joEvent.stop(e);joEvent.preventDefault(e);} -this.backEvent.fire("back");return;} -if(!this.altkey) -return;joEvent.stop(e);switch(e.keyCode){case 37:this.leftEvent.fire("left");break;case 38:this.upEvent.fire("up");break;case 39:this.rightEvent.fire("right");break;case 40:this.downEvent.fire("down");break;case 27:this.backEvent.fire("back");break;case 13:this.forwardEvent.fire("forward");break;}},onKeyDown:function(e){if(!e) -var e=window.event;if(e.keyCode==27){joEvent.stop(e);joEvent.preventDefault(e);} -else if(e.keyCode==13&&joFocus.get()instanceof joInput){joEvent.stop(e);} -else if(e.keyCode==18){this.altkey=true;} -return;}};joGroup=function(data){joContainer.apply(this,arguments);};joGroup.extend(joContainer,{tagName:"jogroup"});joHTML=function(data){joControl.apply(this,arguments);};joHTML.extend(joControl,{tagName:"johtml",setEvents:function(){joEvent.on(this.container,"click",this.onClick,this);},onClick:function(e){joEvent.stop(e);joEvent.preventDefault(e);var container=this.container;var hrefnode=findhref(joEvent.getTarget(e));if(hrefnode){this.selectEvent.fire(hrefnode.href);} -function findhref(node){if(!node) -return null;if(node.href) -return node;if(typeof node.parentNode!=="undefined"&&node.parentNode!==container) -return findhref(node.parentNode);else -return null;}}});joInput=function(data){joControl.apply(this,arguments);};joInput.extend(joControl,{tagName:"input",type:"text",setData:function(data){if(data!==this.data){this.data=data;if(typeof this.container.value!=="undefined") -this.container.value=data;else -this.container.innerHTML=data;this.changeEvent.fire(this.data);}},getData:function(){if(typeof this.container.value!=="undefined") -return this.container.value;else -return this.container.innerHTML;},enable:function(){this.container.setAttribute("tabindex","1");joControl.prototype.enable.call(this);},disable:function(){this.container.removeAttribute("tabindex");joControl.prototype.disable.call(this);},createContainer:function(){var o=joDOM.create(this);if(!o) -return;o.setAttribute("type","text");o.setAttribute("tabindex","1");o.contentEditable=this.enabled;return o;},setEvents:function(){joControl.prototype.setEvents.call(this);joEvent.on(this.container,"keydown",this.onKeyDown,this);},onKeyDown:function(e){if(e.keyCode==13){e.preventDefault();joEvent.stop(e);} -return false;},draw:function(){if(this.container.value) -this.value=this.data;else -this.innerHTML=this.value;},onMouseDown:function(e){joEvent.stop(e);this.focus();},storeData:function(){this.data=this.getData();if(this.dataSource) -this.dataSource.set(this.value);}});joLabel=function(data){joControl.apply(this,arguments);};joLabel.extend(joControl,{tagName:"jolabel"});joMenu=function(){joList.apply(this,arguments);};joMenu.extend(joList,{tagName:"jomenu",itemTagName:"jomenuitem",value:null,fireSelect:function(index){if(typeof this.data[index].id!=="undefined"&&this.data[index].id) -this.selectEvent.fire(this.data[index].id);else -this.selectEvent.fire(index);},formatItem:function(item,index){var o=joDOM.create(this.itemTagName);o.setAttribute("index",index);if(typeof item==="object"){o.innerHTML=item.title;if(item.icon){o.style.backgroundImage="url("+item.icon+")";joDOM.addCSSClass(o,"icon");}} -else{o.innerHTML=item;} -return o;}});joOption=function(){joMenu.apply(this,arguments);};joOption.extend(joMenu,{tagName:"jooption",itemTagName:"jooptionitem"});joPasswordInput=function(data){joInput.apply(this,arguments);};joPasswordInput.extend(joInput,{className:"password",type:"password"});joPopup=function(){this.showEvent=new joSubject(this);this.hideEvent=new joSubject(this);joContainer.apply(this,arguments);};joPopup.extend(joContainer,{tagName:"jopopup",setEvents:function(){joEvent.on(this.container,"mousedown",this.onClick,this);},onClick:function(e){joEvent.stop(e);},hide:function(){joEvent.on(this.container,"webkitTransitionEnd",this.onHide,this);this.container.className='hide';},onHide:function(){this.hideEvent.fire();},show:function(){this.container.className='show';this.showEvent.fire();}});joScreen=function(){this.resizeEvent=new joSubject(this);this.menuEvent=new joSubject(this);this.activateEvent=new joSubject(this);this.deactivateEvent=new joSubject(this);this.backEvent=new joSubject(this);this.forwardEvent=new joSubject(this);joContainer.apply(this,arguments);};joScreen.extend(joContainer,{tagName:"screen",setupEvents:function(){joEvent.on(window,"resize",this.resizeEvent.fire,this);joEvent.on(window,"appmenushow",this.menuEvent.fire,this);joEvent.on(window,"activate",this.activateEvent.fire,this);joEvent.on(window,"deactivate",this.deactivateEvent.fire,this);joEvent.on(window,"back",this.backEvent.fire,this);},createContainer:function(){return document.body;},showPopup:function(data){if(!this.popup){this.shim=new joShim(new joFlexcol([' ',this.popup=new joPopup(data),' ']));} -else{this.popup.setData(data);} -this.shim.show();this.popup.show();},hidePopup:function(){if(this.shim) -this.shim.hide();},alert:function(title,msg,options,context){var buttons=[];var callback;var context=(typeof context==='object')?context:null;if(typeof options==='object'){if(options instanceof Array){for(var i=0;i1) -return;this.audio.volume=vol;return this;}};joStackScroller=function(data){this.scrollers=[new joScroller(),new joScroller()];this.scroller=this.scrollers[0];joStack.apply(this,arguments);this.scroller.attach(this.container);};joStackScroller.extend(joStack,{type:"scroll",scrollerindex:1,scroller:null,scrollers:[],switchScroller:function(){this.scrollerindex=this.scrollerindex?0:1;this.scroller=this.scrollers[this.scrollerindex];},getLastScroller:function(){return this.scrollers[this.scrollerindex?0:1];},scrollTo:function(something){this.scroller.scrollTo(something);},scrollBy:function(y){this.scroller.scrollBy(y);},getChildStyleContainer:function(){return this.scroller.container;},getContentContainer:function(){return this.scroller.container;},appendChild:function(child){var scroller=this.scroller;scroller.setData(child);this.container.appendChild(scroller.container);},getChild:function(){return this.scroller.container||null;},forward:function(){if(this.index0) -this.switchScroller();joStack.prototype.forward.call(this);},home:function(){if(this.data&&this.data.length&&this.data.length>1){this.switchScroller();joStack.prototype.home.call(this);}},push:function(o){if(this.data&&this.data.length&&this.data[this.data.length-1]===o) -return;this.switchScroller();joDOM.removeCSSClass(o,'flick');joDOM.removeCSSClass(o,'flickback');this.scroller.setData(o);this.scroller.scrollTo(0,true);joStack.prototype.push.call(this,o);},pop:function(){if(this.data.length>this.locked) -this.switchScroller();joStack.prototype.pop.call(this);}});joTabBar=function(){joList.apply(this,arguments);};joTabBar.extend(joList,{tagName:"jotabbar",formatItem:function(data,index){var o=document.createElement("jotab");if(data.label) -o.innerHTML=data.label;if(data.type) -o.className=data.type;o.setAttribute("index",index);return o;}});joTable=function(data){joList.apply(this,arguments);};joTable.extend(joList,{tagName:"jotable",formatItem:function(row,index){var tr=document.createElement("tr");for(var i=0,l=row.length;i1) -joDOM.addCSSClass(this.back,'active');else -joDOM.removeCSSClass(this.back,'active');var title=this.stack.getTitle();if(typeof title==='string') -this.titlebar.setData(title);else -this.titlebar.setData(this.firstTitle);},setTitle:function(title){this.titlebar.setData(title);this.firstTitle=title;return this;}});joBackButton=function(){joButton.apply(this,arguments);};joBackButton.extend(joButton,{tagName:"jobackbutton"});joSelect=function(data,value){var v=value;if(value instanceof joDataSource) -v=value.getData();var ui=[this.field=new joSelectTitle(v),this.list=new joSelectList(data,value)];this.field.setList(this.list);this.changeEvent=this.list.changeEvent;this.selectEvent=this.list.selectEvent;joExpando.call(this,ui);this.container.setAttribute("tabindex",1);this.field.setData(this.list.value);this.list.selectEvent.subscribe(this.setValue,this);};joSelect.extend(joExpando,{setValue:function(value,list){if(list){this.field.setData(value);this.close();} -else{this.field.setData(value);}},getValue:function(){return this.list.getValue();},setEvents:function(){joControl.prototype.setEvents.call(this);},onBlur:function(e){joEvent.stop(e);joDOM.removeCSSClass(this,"focus");this.close();}});joSelectTitle=function(){joExpandoTitle.apply(this,arguments);};joSelectTitle.extend(joExpandoTitle,{list:null,setList:function(list){this.list=list;},setData:function(value){if(this.list) -joExpandoTitle.prototype.setData.call(this,this.list.getNodeData(value)||"Select...");else -joExpandoTitle.prototype.setData.call(this,value);}});joToggle=function(data){joControl.call(this,data);};joToggle.extend(joControl,{tagName:"jotoggle",button:null,labels:["Off","On"],setLabels:function(labels){if(labels instanceof Array) -this.labels=labels;else if(arguments.length==2) -this.labels=arguments;this.draw();return this;},select:function(e){if(e) -joEvent.stop(e);this.setData((this.data)?false:true);},onBlur:function(e){joEvent.stop(e);this.blur();},draw:function(){if(!this.container) -return;if(!this.container.firstChild){this.button=joDOM.create("div");this.container.appendChild(this.button);} -this.button.innerHTML=this.labels[(this.data)?1:0];if(this.data) -joDOM.addCSSClass(this.container,"on");else -joDOM.removeCSSClass(this.container,"on");}});joSlider=function(value){this.min=0;this.max=1;this.snap=0;this.range=1;this.thumb=null;this.horizontal=1;this.vertical=0;this.moved=false;this.jump=true;joControl.call(this,null,value);};joSlider.extend(joControl,{tagName:"joslider",setRange:function(min,max,snap){if(min>=max){joLog("WARNING: joSlider.setRange, min must be less than max.");return this;} -this.min=min||0;this.max=max||1;if(min<0&&max>=0) -this.range=Math.abs(min)+max;else if(min<0&&max<=0) -this.range=min-max;else -this.range=max-min;if(typeof snap!=='undefined') -this.snap=(snap>=0&&snap<=this.range)?snap:0;else -this.snap=0;this.setValue(this.value);return this;},setValue:function(value,silent){var v=this.adjustValue(value);if(v!=this.value){joControl.prototype.setValue.call(this,v);if(!silent) -this.draw();} -return this;},adjustValue:function(v){var value=v;if(this.snap) -value=Math.floor(value/this.snap)*this.snap;if(valuethis.max) -value=this.max;return value;},createContainer:function(){var o=joDOM.create(this.tagName);if(o){o.setAttribute("tabindex","1");var t=joDOM.create("josliderthumb");o.appendChild(t);this.thumb=t;} -return o;},onDown:function(e){joEvent.stop(e);this.reset();var node=this.container.firstChild;this.inMotion=true;this.moved=false;if(!this.mousemove){this.mousemove=joEvent.on(document.body,"mousemove",this.onMove,this);this.mouseup=joEvent.capture(document.body,"mouseup",this.onUp,this);}},reset:function(){this.moved=false;this.inMotion=false;this.firstX=-1;this.firstY=-1;},onMove:function(e){if(!this.inMotion) -return;joEvent.stop(e);e.preventDefault();var point=this.getMouse(e);var y=point.y;var x=point.x;if(this.firstX==-1){this.firstX=x;this.firstY=y;this.ox=this.thumb.offsetLeft;this.oy=this.thumb.offsetTop;} -var x=(x-this.firstX)+this.ox;var y=(y-this.firstY)+this.oy;if(x>4||y>4) -this.moved=true;var t=this.thumb.offsetWidth;var w=this.container.offsetWidth-t;if(x<0) -x=0;else if(x>w) -x=w;if(!this.snap) -this.moveTo(x);this.setValue((x/w)*this.range+this.min,!this.snap);},moveTo:function(x){this.thumb.style.left=x+"px";},initValue:function(value){var t=this.container.firstChild.offsetWidth;var w=this.container.offsetWidth-t;var x=Math.floor((this.value/this.range)*w);this.moveTo(x);return this;},onUp:function(e){if(!this.inMotion) -return;joEvent.remove(document.body,"mousemove",this.mousemove);joEvent.remove(document.body,"mouseup",this.mouseup);this.mousemove=null;joEvent.stop(e);joEvent.preventDefault(e);joDefer(function(){this.reset();},this);},setEvents:function(){joEvent.on(this.container,"click",this.onClick,this);joEvent.on(this.thumb,"mousedown",this.onDown,this);joGesture.resizeEvent.subscribe(this.draw,this);console.log('setevents');},onClick:function(e){if(this.inMotion||this.moved) -return;joEvent.stop(e);joEvent.preventDefault(e);var point=this.getMouse(e);var l=joDOM.pageOffsetLeft(this.container);var x=Math.floor((point.x-l)-this.thumb.offsetWidth*1.5);var t=this.thumb.offsetWidth;x=x-t;var w=this.container.offsetWidth-t;if((xw) -x=w;this.setValue((x/w)*this.range+this.min);},getMouse:function(e){return{x:(this.horizontal)?e.screenX:0,y:(this.vertical)?e.screenY:0};},draw:function(){if(!this.container) -this.setContainer();this.initValue(this.value);}}); \ No newline at end of file diff --git a/assets/www/js/jq.mobi.js b/assets/www/js/jq.mobi.js new file mode 100644 index 0000000..29dd54d --- /dev/null +++ b/assets/www/js/jq.mobi.js @@ -0,0 +1,1895 @@ +/** +* jqMobi is a query selector class for HTML5 mobile apps on a WebkitBrowser. +* Since most mobile devices (Android, iOS, webOS) use a WebKit browser, you only need to target one browser. +* We are able to increase the speed greatly by removing support for legacy desktop browsers and taking advantage of browser features, like native JSON parsing and querySelectorAll + + +* MIT License +* @author AppMobi +* @api private +*/ +if (!window.jq || typeof (jq) !== "function") { + /** + * This is our master jq object that everything is built upon. + * $ is a pointer to this object + * @title jqMobi + * @api private + */ + var jq = (function(window) { + "use strict"; + var undefined, document = window.document, + emptyArray = [], + slice = emptyArray.slice, + classCache = [], + eventHandlers = [], + _eventID = 1, + jsonPHandlers = [], + _jsonPID = 1, + fragementRE=/^\s*<(\w+)[^>]*>/, + _attrCache={}; + + + /** + * Internal function to test if a class name fits in a regular expression + * @param {String} name to search against + * @return {Boolean} + * @api private + */ + function classRE(name) { + return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')); + } + + /** + * Internal function that returns a array of unique elements + * @param {Array} array to compare against + * @return {Array} array of unique elements + * @api private + */ + function unique(arr) { + for (var i = 0; i < arr.length; i++) { + if (arr.indexOf(arr[i]) != i) { + arr.splice(i, 1); + i--; + } + } + return arr; + } + + /** + * Given a set of nodes, it returns them as an array. Used to find + * siblings of an element + * @param {Nodelist} Node list to search + * @param {Object} [element] to find siblings off of + * @return {Array} array of sibblings + * @api private + */ + function siblings(nodes, element) { + var elems = []; + if (nodes == undefined) + return elems; + + for (; nodes; nodes = nodes.nextSibling) { + if (nodes.nodeType == 1 && nodes !== element) { + elems.push(nodes); + } + } + return elems; + } + + /** + * This is the internal jqMobi object that gets extended and added on to it + * This is also the start of our query selector engine + * @param {String|Element|Object|Array} selector + * @param {String|Element|Object} [context] + */ + var $jqm = function(toSelect, what) { + this.length = 0; + if (!toSelect) { + return this; + } else if (toSelect instanceof $jqm && what == undefined) { + return toSelect; + } else if ($.isFunction(toSelect)) { + return $(document).ready(toSelect); + } else if ($.isArray(toSelect) && toSelect.length != undefined) { //Passing in an array or object + for (var i = 0; i < toSelect.length; i++) + this[this.length++] = toSelect[i]; + return this; + } else if ($.isObject(toSelect) && $.isObject(what)) { //var tmp=$("span"); $("p").find(tmp); + if (toSelect.length == undefined) { + if (toSelect.parentNode == what) + this[this.length++] = toSelect; + } else { + for (var i = 0; i < toSelect.length; i++) + if (toSelect[i].parentNode == what) + this[this.length++] = toSelect[i]; + } + return this; + } else if ($.isObject(toSelect) && what == undefined) { //Single object + this[this.length++] = toSelect; + return this; + } else if (what !== undefined) { + if (what instanceof $jqm) { + return what.find(toSelect); + } + + } else { + what = document; + } + + var dom = this.selector(toSelect, what); + if (!dom) { + return this; + } + //reverse the query selector all storage + else if ($.isArray(dom)) { + for (var j = 0; j < dom.length; j++) { + this[this.length++] = dom[j]; + } + } else { + this[this.length++] = dom; + return this; + } + return this; + }; + + /** + * This calls the $jqm function + * @param {String|Element|Object|Array} selector + * @param {String|Element|Object} [context] + */ + var $ = function(selector, what) { + return new $jqm(selector, what); + }; + + /** + * this is the query selector library for elements + * @param {String} selector + * @param {String|Element|Object} [context] + * @api private + */ + + function _selector(selector, what) { + var dom; + try { + selector=selector.trim(); + if (selector[0] === "#" && selector.indexOf(" ") === -1 && selector.indexOf(">") === -1) { + if (what == document) + dom = what.getElementById(selector.replace("#", "")); + else + dom = [].slice.call(what.querySelectorAll(selector)); + } else if (selector[0] === "<" && selector[selector.length - 1] === ">") //html + { + var tmp = document.createElement("div"); + tmp.innerHTML = selector.trim(); + dom = [].slice.call(tmp.childNodes); + } else { + dom = [].slice.call(what.querySelectorAll(selector)); + } + } catch (e) { + } + return dom; + } + + /** + * Map takes in elements and executes a callback function on each and returns a collection + ``` + $.map([1,2],function(ind){return ind+1}); + ``` + + * @param {Array|Object} elements + * @param {Function} callback + * @return {Object} jqMobi object with elements in it + * @title $.map(elements,callback) + */ + $.map = function(elements, callback) { + var value, values = [], + i, key; + if ($.isArray(elements)) + for (i = 0; i < elements.length; i++) { + value = callback(elements[i], i); + if (value !== undefined) + values.push(value); + } + else if ($.isObject(elements)) + for (key in elements) { + if (!elements.hasOwnProperty(key)) + continue; + value = callback(elements[key], key); + if (value !== undefined) + values.push(value); + } + return $([values]); + }; + + /** + * Iterates through elements and executes a callback. Returns if false + ``` + $.each([1,2],function(ind){console.log(ind);}); + ``` + + * @param {Array|Object} elements + * @param {Function} callback + * @return {Array} elements + * @title $.each(elements,callback) + */ + $.each = function(elements, callback) { + var i, key; + if ($.isArray(elements)) + for (i = 0; i < elements.length; i++) { + if (callback(i, elements[i]) === false) + return elements; + } + else if ($.isObject(elements)) + for (key in elements) { + if (!elements.hasOwnProperty(key)) + continue; + if (callback(key, elements[key]) === false) + return elements; + } + return elements; + }; + + /** + * Extends an object with additional arguments + ``` + $.extend({foo:'bar'}); + $.extend(element,{foo:'bar'}); + ``` + + * @param {Object} [target] element + * @param any number of additional arguments + * @return {Object} [target] + * @title $.extend(target,{params}) + */ + $.extend = function(target) { + if (target == undefined) + target = this; + if (arguments.length === 1) { + for (var key in target) + this[key] = target[key]; + return this; + } else { + slice.call(arguments, 1).forEach(function(source) { + for (var key in source) + target[key] = source[key]; + }); + } + return target; + }; + + /** + * Checks to see if the parameter is an array + ``` + var arr=[]; + $.isArray(arr); + ``` + + * @param {Object} element + * @return {Boolean} + * @example $.isArray([1]); + * @title $.isArray(param) + */ + $.isArray = function(obj) { + return obj instanceof Array && obj['push'] != undefined; //ios 3.1.3 doesn't have Array.isArray + }; + + /** + * Checks to see if the parameter is a function + ``` + var func=function(){}; + $.isFunction(func); + ``` + + * @param {Object} element + * @return {Boolean} + * @title $.isFunction(param) + */ + $.isFunction = function(obj) { + return typeof obj === "function"; + }; + /** + * Checks to see if the parameter is a object + ``` + var foo={bar:'bar'}; + $.isObject(foo); + ``` + + * @param {Object} element + * @return {Boolean} + * @title $.isObject(param) + */ + $.isObject = function(obj) { + return typeof obj === "object"; + } + + + /** + * Prototype for jqm object. Also extens $.fn + */ + $.fn = $jqm.prototype = { + constructor: $jqm, + forEach: emptyArray.forEach, + reduce: emptyArray.reduce, + push: emptyArray.push, + indexOf: emptyArray.indexOf, + concat: emptyArray.concat, + selector: _selector, + oldElement: undefined, + slice: emptyArray.slice, + /** + * This is a utility function for .end() + * @param {Object} params + * @return {Object} a jqMobi with params.oldElement set to this + * @api private + */ + setupOld: function(params) { + if (params == undefined) + return $(); + params.oldElement = this; + return params; + + }, + /** + * This is a wrapper to $.map on the selected elements + ``` + $().map(function(){this.value+=ind}); + ``` + + * @param {Function} callback + * @return {Object} a jqMobi object + * @title $().map(function) + */ + map: function(fn) { + return $.map(this, function(el, i) { + return fn.call(el, i, el); + }); + }, + /** + * Iterates through all elements and applys a callback function + ``` + $().each(function(){console.log(this.value)}); + ``` + + * @param {Function} callback + * @return {Object} a jqMobi object + * @title $().each(function) + */ + each: function(callback) { + this.forEach(function(el, idx) { + callback.call(el, idx, el); + }); + return this; + }, + /** + * This is executed when DOMContentLoaded happens, or after if you've registered for it. + ``` + $(document).ready(function(){console.log('I'm ready');}); + ``` + + * @param {Function} callback + * @return {Object} a jqMobi object + * @title $().ready(function) + */ + + ready: function(callback) { + if (document.readyState === "complete" || document.readyState === "loaded") + callback(); + document.addEventListener("DOMContentLoaded", callback, false); + return this; + }, + /** + * Searches through the collection and reduces them to elements that match the selector + ``` + $("#foo").find('.bar'); + $("#foo").find($('.bar')); + $("#foo").find($('.bar').get()); + ``` + + * @param {String|Object|Array} selector + * @return {Object} a jqMobi object filtered + * @title $().find(selector) + + */ + find: function(sel) { + if (this.length === 0) + return undefined; + var elems = []; + var tmpElems; + for (var i = 0; i < this.length; i++) { + tmpElems = ($(sel, this[i])); + + for (var j = 0; j < tmpElems.length; j++) { + elems.push(tmpElems[j]); + } + } + return $(unique(elems)); + }, + /** + * Gets or sets the innerHTML for the collection. + * If used as a get, the first elements innerHTML is returned + ``` + $("#foo").html(); //gets the first elements html + $("#foo").html('new html');//sets the html + ``` + + * @param {String} html to set + * @return {Object} a jqMobi object + * @title $().html([html]) + */ + html: function(html) { + if (this.length === 0) + return undefined; + if (html === undefined) + return this[0].innerHTML; + for (var i = 0; i < this.length; i++) { + this[i].innerHTML = html; + } + return this; + }, + /** + * Gets or sets the innerText for the collection. + * If used as a get, the first elements innerText is returned + ``` + $("#foo").text(); //gets the first elements text; + $("#foo").text('new text'); //sets the text + ``` + + * @param {String} text to set + * @return {Object} a jqMobi object + * @title $().text([text]) + */ + text: function(text) { + if (this.length === 0) + return undefined; + if (text === undefined) + return this[0].textContent; + for (var i = 0; i < this.length; i++) { + this[i].textContent = text; + } + return this; + }, + /** + * Gets or sets a css property for the collection + * If used as a get, the first elements css property is returned + ``` + $().css("background"); // Gets the first elements background + $().css("background","red") //Sets the elements background to red + ``` + + * @param {String} attribute to get + * @param {String} value to set as + * @return {Object} a jqMobi object + * @title $().css(attribute,[value]) + */ + css: function(attribute, value, obj) { + var toAct = obj != undefined ? obj : this[0]; + if (this.length === 0) + return undefined; + if (value == undefined && typeof (attribute) === "string") { + var styles = window.getComputedStyle(toAct); + return toAct.style[attribute] ? toAct.style[attribute]: window.getComputedStyle(toAct)[attribute] ; + } + for (var i = 0; i < this.length; i++) { + if ($.isObject(attribute)) { + for (var j in attribute) { + this[i].style[j] = attribute[j]; + } + } else { + this[i].style[attribute] = value; + } + } + return this; + }, + /** + * Sets the innerHTML of all elements to an empty string + ``` + $().empty(); + ``` + + * @return {Object} a jqMobi object + * @title $().empty() + */ + empty: function() { + for (var i = 0; i < this.length; i++) { + this[i].innerHTML = ''; + } + return this; + }, + /** + * Sets the elements display property to "none". + * This will also store the old property into an attribute for hide + ``` + $().hide(); + ``` + + * @return {Object} a jqMobi object + * @title $().hide() + */ + hide: function() { + if (this.length === 0) + return this; + for (var i = 0; i < this.length; i++) { + if (this.css("display", null, this[i]) != "none") { + this[i].setAttribute("jqmOldStyle", this.css("display", null, this[i])); + this[i].style.display = "none"; + } + } + return this; + }, + /** + * Shows all the elements by setting the css display property + * We look to see if we were retaining an old style (like table-cell) and restore that, otherwise we set it to block + ``` + $().show(); + ``` + + * @return {Object} a jqMobi object + * @title $().show() + */ + show: function() { + if (this.length === 0) + return this; + for (var i = 0; i < this.length; i++) { + if (this.css("display", null, this[i]) == "none") { + this[i].style.display = this[i].getAttribute("jqmOldStyle") ? this[i].getAttribute("jqmOldStyle") : 'block'; + this[i].removeAttribute("jqmOldStyle"); + } + } + return this; + }, + /** + * Toggle the visibility of a div + ``` + $().toggle(); + $().toggle(true); //force showing + ``` + + * @param {Boolean} [show] -force the hiding or showing of the element + * @return {Object} a jqMobi object + * @title $().toggle([show]) + */ + toggle: function(show) { + var show2 = show === true ? true : false; + for (var i = 0; i < this.length; i++) { + if (window.getComputedStyle(this[i])['display'] !== "none" || (show !== undefined && show2 === false)) { + this[i].setAttribute("jqmOldStyle", this[i].style.display) + this[i].style.display = "none"; + } else { + this[i].style.display = this[i].getAttribute("jqmOldStyle") != undefined ? this[i].getAttribute("jqmOldStyle") : 'block'; + this[i].removeAttribute("jqmOldStyle"); + } + } + return this; + }, + /** + * Gets or sets an elements value + * If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined + ``` + $().value; //Gets the first elements value; + $().value="bar"; //Sets all elements value to bar + ``` + + * @param {String} [value] to set + * @return {String|Object} A string as a getter, jqMobi object as a setter + * @title $().val([value]) + */ + val: function(value) { + if (this.length === 0) + return undefined; + if (value == undefined) + return this[0].value; + for (var i = 0; i < this.length; i++) { + this[i].value = value; + } + return this; + }, + /** + * Gets or sets an attribute on an element + * If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined + ``` + $().attr("foo"); //Gets the first elements 'foo' attribute + $().attr("foo","bar");//Sets the elements 'foo' attribute to 'bar' + $().attr("foo",{bar:'bar'}) //Adds the object to an internal cache + ``` + + * @param {String|Object} attribute to act upon. If it's an object (hashmap), it will set the attributes based off the kvp. + * @param {String|Array|Object|function} [value] to set + * @return {String|Object|Array|Function} If used as a getter, return the attribute value. If a setter, return a jqMobi object + * @title $().attr(attribute,[value]) + */ + attr: function(attr, value) { + if (this.length === 0) + return undefined; + if (value === undefined && !$.isObject(attr)) { + + var val = (this[0].jqmCacheId&&_attrCache[this[0].jqmCacheId][attr])?(this[0].jqmCacheId&&_attrCache[this[0].jqmCacheId][attr]):this[0].getAttribute(attr); + return val; + } + for (var i = 0; i < this.length; i++) { + if ($.isObject(attr)) { + for (var key in attr) { + $(this[i]).attr(key,attr[key]); + } + } + else if($.isArray(value)||$.isObject(value)||$.isFunction(value)) + { + + if(!this[i].jqmCacheId) + this[i].jqmCacheId=$.uuid(); + + if(!_attrCache[this[i].jqmCacheId]) + _attrCache[this[i].jqmCacheId]={} + _attrCache[this[i].jqmCacheId][attr]=value; + } + else if (value == null && value !== undefined) + { + this[i].removeAttribute(attr); + if(this[i].jqmCacheId&&_attrCache[this[i].jqmCacheId][attr]) + delete _attrCache[this[i].jqmCacheId][attr]; + } + else{ + this[i].setAttribute(attr, value); + } + } + return this; + }, + /** + * Removes an attribute on the elements + ``` + $().removeAttr("foo"); + ``` + + * @param {String} attributes that can be space delimited + * @return {Object} jqMobi object + * @title $().removeAttr(attribute) + */ + removeAttr: function(attr) { + var that = this; + for (var i = 0; i < this.length; i++) { + attr.split(/\s+/g).forEach(function(param) { + that[i].removeAttribute(param); + if(that[i].jqmCacheId&&_attrCache[that[i].jqmCacheId][attr]) + delete _attrCache[that[i].jqmCacheId][attr]; + }); + } + return this; + }, + /** + * Removes elements based off a selector + ``` + $().remove(".foo");//Remove off a string selector + var element=$("#foo").get(); + $().remove(element); //Remove by an element + $().remove($(".foo")); //Remove by a collection + ``` + + * @param {String|Object|Array} selector to filter against + * @return {Object} jqMobi object + * @title $().remove(selector) + */ + remove: function(selector) { + var elems = $(this).filter(selector); + if (elems == undefined) + return this; + for (var i = 0; i < elems.length; i++) { + elems[i].parentNode.removeChild(elems[i]); + } + return this; + }, + /** + * Adds a css class to elements. + ``` + $().addClass("selected"); + ``` + + * @param {String} classes that are space delimited + * @return {Object} jqMobi object + * @title $().addClass(name) + */ + addClass: function(name) { + for (var i = 0; i < this.length; i++) { + var cls = this[i].className; + var classList = []; + var that = this; + name.split(/\s+/g).forEach(function(cname) { + if (!that.hasClass(cname, that[i])) + classList.push(cname); + }); + + this[i].className += (cls ? " " : "") + classList.join(" "); + this[i].className = this[i].className.trim(); + } + return this; + }, + /** + * Removes a css class from elements. + ``` + $().removeClass("foo"); //single class + $().removeClass("foo selected");//remove multiple classess + ``` + + * @param {String} classes that are space delimited + * @return {Object} jqMobi object + * @title $().removeClass(name) + */ + removeClass: function(name) { + for (var i = 0; i < this.length; i++) { + if (name == undefined) { + this[i].className = ''; + return this; + } + var classList = this[i].className; + name.split(/\s+/g).forEach(function(cname) { + classList = classList.replace(classRE(cname), " "); + }); + if (classList.length > 0) + this[i].className = classList.trim(); + else + this[i].className = ""; + } + return this; + }, + /** + * Checks to see if an element has a class. + ``` + $().hasClass('foo'); + $().hasClass('foo',element); + ``` + + * @param {String} class name to check against + * @param {Object} [element] to check against + * @return {Boolean} + * @title $().hasClass(name,[element]) + */ + hasClass: function(name, element) { + if (this.length === 0) + return false; + if (!element) + element = this[0]; + return classRE(name).test(element.className); + }, + /** + * Appends to the elements + * We boil everything down to a jqMobi object and then loop through that. + * If it's HTML, we create a dom element so we do not break event bindings. + * if it's a script tag, we evaluate it. + ``` + $().append("
"); //Creates the object from the string and appends it + $().append($("#foo")); //Append an object; + ``` + + * @param {String|Object} Element/string to add + * @param {Boolean} [insert] insert or append + * @return {Object} jqMobi object + * @title $().append(element,[insert]) + */ + append: function(element, insert) { + if (element && element.length != undefined && element.length === 0) + return this; + if ($.isArray(element) || $.isObject(element)) + element = $(element); + var i; + + for (i = 0; i < this.length; i++) { + if (element.length && typeof element != "string") { + element = $(element); + for (var j = 0; j < element.length; j++) + insert != undefined ? this[i].insertBefore(element[j], this[i].firstChild) : this[i].appendChild(element[j]); + } else { + var obj =fragementRE.test(element)?$(element):undefined; + if (obj == undefined || obj.length == 0) { + obj = document.createTextNode(element); + } + if (obj.nodeName != undefined && obj.nodeName.toLowerCase() == "script" && (!obj.type || obj.type.toLowerCase() === 'text/javascript')) { + window.eval(obj.innerHTML); + } else if(obj instanceof $jqm) { + for(var k=0;k
");//Creates the object from the string and appends it + $().prepend($("#foo")); //Prepends an object + ``` + + * @param {String|Object} Element/string to add + * @return {Object} jqMobi object + * @title $().prepend(element) + */ + prepend: function(element) { + return this.append(element, 1); + }, + /** + * Inserts collection before the target (adjacent) + ``` + $().insertBefore(jq("#target")); + ``` + + * @param {String|Object} Target + * @title $().insertBefore(target); + */ + insertBefore: function(target, after) { + if (this.length == 0) + return this; + target = $(target).get(0); + if (!target || target.length == 0) + return this; + for (var i = 0; i < this.length; i++) + { + after ? target.parentNode.insertBefore(this[i], target.nextSibling) : target.parentNode.insertBefore(this[i], target); + } + return this; + }, + /** + * Inserts collection after the target (adjacent) + ``` + $().insertAfter(jq("#target")); + ``` + * @param {String|Object} target + * @title $().insertAfter(target); + */ + insertAfter: function(target) { + this.insertBefore(target, true); + }, + /** + * Returns the raw DOM element. + ``` + $().get(); //returns the first element + $().get(2);// returns the third element + ``` + + * @param {Int} [index] + * @return {Object} raw DOM element + * @title $().get([index]) + */ + get: function(index) { + index = index == undefined ? 0 : index; + if (index < 0) + index += this.length; + return (this[index]) ? this[index] : undefined; + }, + /** + * Returns the offset of the element, including traversing up the tree + ``` + $().offset(); + ``` + + * @return {Object} with left, top, width and height properties + * @title $().offset() + */ + offset: function() { + if (this.length === 0) + return undefined; + var obj = this[0].getBoundingClientRect(); + return { + left: obj.left + window.pageXOffset, + top: obj.top + window.pageYOffset, + width: parseInt(this[0].style.width), + height: parseInt(this[0].style.height) + }; + }, + /** + * Returns the parent nodes of the elements based off the selector + ``` + $("#foo").parent('.bar'); + $("#foo").parent($('.bar')); + $("#foo").parent($('.bar').get()); + ``` + + * @param {String|Array|Object} [selector] + * @return {Object} jqMobi object with unique parents + * @title $().parent(selector) + */ + parent: function(selector) { + if (this.length == 0) + return undefined; + var elems = []; + for (var i = 0; i < this.length; i++) { + if (this[i].parentNode) + elems.push(this[i].parentNode); + } + return this.setupOld($(unique(elems)).filter(selector)); + }, + /** + * Returns the child nodes of the elements based off the selector + ``` + $("#foo").children('.bar'); //Selector + $("#foo").children($('.bar')); //Objects + $("#foo").children($('.bar').get()); //Single element + ``` + + * @param {String|Array|Object} [selector] + * @return {Object} jqMobi object with unique children + * @title $().children(selector) + */ + children: function(selector) { + + if (this.length == 0) + return undefined; + var elems = []; + for (var i = 0; i < this.length; i++) { + elems = elems.concat(siblings(this[i].firstChild)); + } + return this.setupOld($((elems)).filter(selector)); + + }, + /** + * Returns the siblings of the element based off the selector + ``` + $("#foo").siblings('.bar'); //Selector + $("#foo").siblings($('.bar')); //Objects + $("#foo").siblings($('.bar').get()); //Single element + ``` + + * @param {String|Array|Object} [selector] + * @return {Object} jqMobi object with unique siblings + * @title $().siblings(selector) + */ + siblings: function(selector) { + if (this.length == 0) + return undefined; + var elems = []; + for (var i = 0; i < this.length; i++) { + if (this[i].parentNode) + elems = elems.concat(siblings(this[i].parentNode.firstChild, this[i])); + } + return this.setupOld($(elems).filter(selector)); + }, + /** + * Returns the closest element based off the selector and optional context + ``` + $("#foo").closest('.bar'); //Selector + $("#foo").closest($('.bar')); //Objects + $("#foo").closest($('.bar').get()); //Single element + ``` + + * @param {String|Array|Object} selector + * @param {Object} [context] + * @return {Object} Returns a jqMobi object with the closest element based off the selector + * @title $().closest(selector,[context]); + */ + closest: function(selector, context) { + if (this.length == 0) + return undefined; + var elems = [], + cur = this[0]; + + var start = $(selector, context); + if (start.length == 0) + return $(); + while (cur && start.indexOf(cur) == -1) { + cur = cur !== context && cur !== document && cur.parentNode; + } + return $(cur); + + }, + /** + * Filters elements based off the selector + ``` + $("#foo").filter('.bar'); //Selector + $("#foo").filter($('.bar')); //Objects + $("#foo").filter($('.bar').get()); //Single element + ``` + + * @param {String|Array|Object} selector + * @return {Object} Returns a jqMobi object after the filter was run + * @title $().filter(selector); + */ + filter: function(selector) { + if (this.length == 0) + return undefined; + + if (selector == undefined) + return this; + var elems = []; + for (var i = 0; i < this.length; i++) { + var val = this[i]; + if (val.parentNode && $(selector, val.parentNode).indexOf(val) >= 0) + elems.push(val); + } + return this.setupOld($(unique(elems))); + }, + /** + * Basically the reverse of filter. Return all elements that do NOT match the selector + ``` + $("#foo").not('.bar'); //Selector + $("#foo").not($('.bar')); //Objects + $("#foo").not($('.bar').get()); //Single element + ``` + + * @param {String|Array|Object} selector + * @return {Object} Returns a jqMobi object after the filter was run + * @title $().not(selector); + */ + not: function(selector) { + if (this.length == 0) + return undefined; + var elems = []; + for (var i = 0; i < this.length; i++) { + var val = this[i]; + if (val.parentNode && $(selector, val.parentNode).indexOf(val) == -1) + elems.push(val); + } + return this.setupOld($(unique(elems))); + }, + /** + * Gets or set data-* attribute parameters on elements + * When used as a getter, it's only the first element + ``` + $().data("foo"); //Gets the data-foo attribute for the first element + $().data("foo","bar"); //Sets the data-foo attribute for all elements + $().data("foo",{bar:'bar'});//object as the data + ``` + + * @param {String} key + * @param {String|Array|Object} value + * @return {String|Object} returns the value or jqMobi object + * @title $().data(key,[value]); + */ + data: function(key, value) { + return this.attr('data-' + key, value); + }, + /** + * Rolls back the jqMobi elements when filters were applied + * This can be used after .not(), .filter(), .children(), .parent() + ``` + $().filter(".panel").end(); //This will return the collection BEFORE filter is applied + ``` + + * @return {Object} returns the previous jqMobi object before filter was applied + * @title $().end(); + */ + end: function() { + return this.oldElement != undefined ? this.oldElement : $(); + }, + /** + * Clones the nodes in the collection. + ``` + $().clone();// Deep clone of all elements + $().clone(false); //Shallow clone + ``` + + * @param {Boolean} [deep] - do a deep copy or not + * @return {Object} jqMobi object of cloned nodes + * @title $().clone(); + */ + clone: function(deep) { + deep = deep === false ? false : true; + if (this.length == 0) + return undefined; + var elems = []; + for (var i = 0; i < this.length; i++) { + elems.push(this[i].cloneNode(deep)); + } + + return $(elems); + }, + /** + * Returns the number of elements in the collection + ``` + $().size(); + ``` + + * @return {Int} + * @title $().size(); + */ + size: function() { + return this.length; + }, + /** + * Serailizes a form into a query string + ``` + $().serialize(grouping); + ``` + * @param {String} [grouping] - optional grouping to the fields -e.g users[name] + * @return {String} + * @title $().serialize(grouping) + */ + serialize: function(grouping) { + if (this.length == 0) + return ""; + var params = {}; + for (var i = 0; i < this.length; i++) + { + this.slice.call(this[i].elements).forEach(function(elem) { + var type = elem.getAttribute("type"); + if (elem.nodeName.toLowerCase() != "fieldset" && !elem.disabled && type != "submit" + && type != "reset" && type != "button" && ((type != "radio" && type != "checkbox") || elem.checked)) + params[elem.getAttribute("name")] = elem.value; + }); + } + return $.param(params,grouping); + } + }; + + + /* AJAX functions */ + + function empty() { + } + var ajaxSettings = { + type: 'GET', + beforeSend: empty, + success: empty, + error: empty, + complete: empty, + context: undefined, + timeout: 0, + crossDomain:false + }; + /** + * Execute a jsonP call, allowing cross domain scripting + * options.url - URL to call + * options.success - Success function to call + * options.error - Error function to call + ``` + $.jsonP({url:'mysite.php?callback=?&foo=bar',success:function(){},error:function(){}}); + ``` + + * @param {Object} options + * @title $.jsonP(options) + */ + $.jsonP = function(options) { + var callbackName = 'jsonp_callback' + (++_jsonPID); + var abortTimeout = "", + context; + var script = document.createElement("script"); + var abort = function() { + $(script).remove(); + if (window[callbackName]) + window[callbackName] = empty; + }; + window[callbackName] = function(data) { + clearTimeout(abortTimeout); + $(script).remove(); + delete window[callbackName]; + options.success.call(context, data); + }; + script.src = options.url.replace(/=\?/, '=' + callbackName); + if(options.error) + { + script.onerror=function(){ + clearTimeout(abortTimeout); + options.error.call(context, "", 'error'); + } + } + $('head').append(script); + if (options.timeout > 0) + abortTimeout = setTimeout(function() { + options.error.call(context, "", 'timeout'); + }, options.timeout); + return {}; + }; + + /** + * Execute an Ajax call with the given options + * options.type - Type of request + * options.beforeSend - function to execute before sending the request + * options.success - success callback + * options.error - error callback + * options.complete - complete callback - callled with a success or error + * options.timeout - timeout to wait for the request + * options.url - URL to make request against + * options.contentType - HTTP Request Content Type + * options.headers - Object of headers to set + * options.dataType - Data type of request + * options.data - data to pass into request. $.param is called on objects + ``` + var opts={ + type:"GET", + success:function(data){}, + url:"mypage.php", + data:{bar:'bar'}, + } + $.ajax(opts); + ``` + + * @param {Object} options + * @title $.ajax(options) + */ + $.ajax = function(opts) { + var xhr; + try { + xhr = new window.XMLHttpRequest(); + var settings = opts || {}; + for (var key in ajaxSettings) { + if (!settings[key]) + settings[key] = ajaxSettings[key]; + } + + if (!settings.url) + settings.url = window.location; + if (!settings.contentType) + settings.contentType = "application/x-www-form-urlencoded"; + if (!settings.headers) + settings.headers = {}; + + if (!settings.dataType) + settings.dataType = "text/html"; + else { + switch (settings.dataType) { + case "script": + settings.dataType = 'text/javascript, application/javascript'; + break; + case "json": + settings.dataType = 'application/json'; + break; + case "xml": + settings.dataType = 'application/xml, text/xml'; + break; + case "html": + settings.dataType = 'text/html'; + break; + case "text": + settings.dataType = 'text/plain'; + break; + default: + settings.dataType = "text/html"; + break; + case "jsonp": + return $.jsonP(opts); + break; + } + } + if ($.isObject(settings.data)) + settings.data = $.param(settings.data); + if (settings.type.toLowerCase() === "get" && settings.data) { + if (settings.url.indexOf("?") === -1) + settings.url += "?" + settings.data; + else + settings.url += "&" + settings.data; + } + + if (/=\?/.test(settings.url)) { + return $.jsonP(settings); + } + + if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && + RegExp.$2 != window.location.host; + + if(!settings.crossDomain) + settings.headers = $.extend({'X-Requested-With': 'XMLHttpRequest'}, settings.headers); + var abortTimeout; + var context = settings.context; + var protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol; + xhr.onreadystatechange = function() { + var mime = settings.dataType; + if (xhr.readyState === 4) { + clearTimeout(abortTimeout); + var result, error = false; + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0&&protocol=='file:') { + if (mime === 'application/json' && !(/^\s*$/.test(xhr.responseText))) { + try { + result = JSON.parse(xhr.responseText); + } catch (e) { + error = e; + } + } else + result = xhr.responseText; + //If we're looking at a local file, we assume that no response sent back means there was an error + if(xhr.status===0&&result.length===0) + error=true; + if (error) + settings.error.call(context, xhr, 'parsererror', error); + else { + settings.success.call(context, result, 'success', xhr); + } + } else { + error = true; + settings.error.call(context, xhr, 'error'); + } + settings.complete.call(context, xhr, error ? 'error' : 'success'); + } + }; + xhr.open(settings.type, settings.url, true); + + if (settings.contentType) + settings.headers['Content-Type'] = settings.contentType; + for (var name in settings.headers) + xhr.setRequestHeader(name, settings.headers[name]); + if (settings.beforeSend.call(context, xhr, settings) === false) { + xhr.abort(); + return false; + } + + if (settings.timeout > 0) + abortTimeout = setTimeout(function() { + xhr.onreadystatechange = empty; + xhr.abort(); + settings.error.call(context, xhr, 'timeout'); + }, settings.timeout); + xhr.send(settings.data); + } catch (e) { + console.log(e); + } + return xhr; + }; + + + /** + * Shorthand call to an Ajax GET request + ``` + $.get("mypage.php?foo=bar",function(data){}); + ``` + + * @param {String} url to hit + * @param {Function} success + * @title $.get(url,success) + */ + $.get = function(url, success) { + return this.ajax({ + url: url, + success: success + }); + }; + /** + * Shorthand call to an Ajax POST request + ``` + $.post("mypage.php",{bar:'bar'},function(data){}); + ``` + + * @param {String} url to hit + * @param {Object} [data] to pass in + * @param {Function} success + * @param {String} [dataType] + * @title $.post(url,[data],success,[dataType]) + */ + $.post = function(url, data, success, dataType) { + if (typeof (data) === "function") { + success = data; + data = {}; + } + if (dataType === undefined) + dataType = "html"; + return this.ajax({ + url: url, + type: "POST", + data: data, + dataType: dataType, + success: success + }); + }; + /** + * Shorthand call to an Ajax request that expects a JSON response + ``` + $.getJSON("mypage.php",{bar:'bar'},function(data){}); + ``` + + * @param {String} url to hit + * @param {Object} [data] + * @param {Function} [success] + * @title $.getJSON(url,data,success) + */ + $.getJSON = function(url, data, success) { + if (typeof (data) === "function") { + success = data; + data = {}; + } + return this.ajax({ + url: url, + data: data, + success: success, + dataType: "json" + }); + }; + + /** + * Converts an object into a key/value par with an optional prefix. Used for converting objects to a query string + ``` + var obj={ + foo:'foo', + bar:'bar' + } + var kvp=$.param(obj,'data'); + ``` + + * @param {Object} object + * @param {String} [prefix] + * @return {String} Key/value pair representation + * @title $.param(object,[prefix]; + */ + $.param = function(obj, prefix) { + var str = []; + if (obj instanceof $jqm) { + obj.each(function() { + var k = prefix ? prefix + "[]" : this.id, + v = this.value; + str.push((k) + "=" + encodeURIComponent(v)); + }); + } else { + for (var p in obj) { + var k = prefix ? prefix + "[" + p + "]" : p, + v = obj[p]; + str.push($.isObject(v) ? $.param(v, k) : (k) + "=" + encodeURIComponent(v)); + } + } + return str.join("&"); + }; + /** + * Used for backwards compatibility. Uses native JSON.parse function + ``` + var obj=$.parseJSON("{\"bar\":\"bar\"}"); + ``` + + * @params {String} string + * @return {Object} + * @title $.parseJSON(string) + */ + $.parseJSON = function(string) { + return JSON.parse(string); + }; + /** + * Helper function to convert XML into the DOM node representation + ``` + var xmlDoc=$.parseXML("bar"); + ``` + + * @param {String} string + * @return {Object} DOM nodes + * @title $.parseXML(string) + */ + $.parseXML = function(string) { + return (new DOMParser).parseFromString(string, "text/xml"); + }; + /** + * Helper function to parse the user agent. Sets the following + * .os.webkit + * .os.android + * .os.ipad + * .os.iphone + * .os.webos + * .os.touchpad + * .os.blackberry + * .os.opera + * .os.fennec + * @api private + */ + function detectUA($, userAgent) { + $.os = {}; + $.os.webkit = userAgent.match(/WebKit\/([\d.]+)/) ? true : false; + $.os.android = userAgent.match(/(Android)\s+([\d.]+)/) || userAgent.match(/Silk-Accelerated/) ? true : false; + $.os.ipad = userAgent.match(/(iPad).*OS\s([\d_]+)/) ? true : false; + $.os.iphone = !$.os.ipad && userAgent.match(/(iPhone\sOS)\s([\d_]+)/) ? true : false; + $.os.webos = userAgent.match(/(webOS|hpwOS)[\s\/]([\d.]+)/) ? true : false; + $.os.touchpad = $.os.webos && userAgent.match(/TouchPad/) ? true : false; + $.os.ios = $.os.ipad || $.os.iphone; + $.os.blackberry = userAgent.match(/BlackBerry/) || userAgent.match(/PlayBook/) ? true : false; + $.os.opera = userAgent.match(/Opera Mobi/) ? true : false; + $.os.fennec = userAgent.match(/fennec/i) ? true : false; + $.os.desktop = !($.os.ios || $.os.android || $.os.blackberry || $.os.opera || $.os.fennec); + } + detectUA($, navigator.userAgent); + $.__detectUA = detectUA; //needed for unit tests + if (typeof String.prototype.trim !== 'function') { + + /** + * Helper function for iOS 3.1.3 + */ + String.prototype.trim = function() { + this.replace(/(\r\n|\n|\r)/gm, "").replace(/^\s+|\s+$/, ''); + return this + }; + } + + /** + * Utility function to create a psuedo GUID + ``` + var id= $.uuid(); + ``` + * @title $.uuid + */ + $.uuid = function () { + var S4 = function () { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + } + return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); + }; + /** + Zepto.js events + @api private + */ + + //The following is modified from Zepto.js / events.js + //We've removed depricated jQuery events like .live and allow anonymous functions to be removed + var $$ = $.qsa, + handlers = {}, + _jqmid = 1, + specialEvents = {}; + /** + * Gets or sets the expando property on a javascript element + * Also increments the internal counter for elements; + * @param {Object} element + * @return {Int} jqmid + * @api private + */ + function jqmid(element) { + return element._jqmid || (element._jqmid = _jqmid++); + } + /** + * Searches through a local array that keeps track of event handlers for proxying. + * Since we listen for multiple events, we match up the event, function and selector. + * This is used to find, execute, remove proxied event functions + * @param {Object} element + * @param {String} [event] + * @param {Function} [function] + * @param {String|Object|Array} [selector] + * @return {Function|null} handler function or false if not found + * @api private + */ + function findHandlers(element, event, fn, selector) { + event = parse(event); + if (event.ns) + var matcher = matcherFor(event.ns); + return (handlers[jqmid(element)] || []).filter(function(handler) { + return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || handler.fn == fn || (typeof handler.fn === 'function' && typeof fn === 'function' && "" + handler.fn === "" + fn)) && (!selector || handler.sel == selector); + }); + } + /** + * Splits an event name by "." to look for namespaces (e.g touch.click) + * @param {String} event + * @return {Object} an object with the event name and namespace + * @api private + */ + function parse(event) { + var parts = ('' + event).split('.'); + return { + e: parts[0], + ns: parts.slice(1).sort().join(' ') + }; + } + /** + * Regular expression checker for event namespace checking + * @param {String} namespace + * @return {Regex} regular expression + * @api private + */ + function matcherFor(ns) { + return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)'); + } + + /** + * Utility function that will loop through events that can be a hash or space delimited and executes the function + * @param {String|Object} events + * @param {Function} fn + * @param {Iterator} [iterator] + * @api private + */ + function eachEvent(events, fn, iterator) { + if ($.isObject(events)) + $.each(events, iterator); + else + events.split(/\s/).forEach(function(type) { + iterator(type, fn) + }); + } + + /** + * Helper function for adding an event and creating the proxy handler function. + * All event handlers call this to wire event listeners up. We create proxy handlers so they can be removed then. + * This is needed for delegate/on + * @param {Object} element + * @param {String|Object} events + * @param {Function} function that will be executed when event triggers + * @param {String|Array|Object} [selector] + * @param {Boolean} [getDelegate] + * @api private + */ + function add(element, events, fn, selector, getDelegate) { + var id = jqmid(element), + set = (handlers[id] || (handlers[id] = [])); + + eachEvent(events, fn, function(event, fn) { + var delegate = getDelegate && getDelegate(fn, event), + callback = delegate || fn; + var proxyfn = function(event) { + var result = callback.apply(element, [event].concat(event.data)); + if (result === false) + event.preventDefault(); + return result; + }; + var handler = $.extend(parse(event), { + fn: fn, + proxy: proxyfn, + sel: selector, + del: delegate, + i: set.length + }); + set.push(handler); + element.addEventListener(handler.e, proxyfn, false); + }); + } + + /** + * Helper function to remove event listeners. We look through each event and then the proxy handler array to see if it exists + * If found, we remove the listener and the entry from the proxy array. If no function is specified, we remove all listeners that match + * @param {Object} element + * @param {String|Object} events + * @param {Function} [fn] + * @param {String|Array|Object} [selector] + * @api private + */ + function remove(element, events, fn, selector) { + + var id = jqmid(element); + eachEvent(events || '', fn, function(event, fn) { + findHandlers(element, event, fn, selector).forEach(function(handler) { + delete handlers[id][handler.i]; + element.removeEventListener(handler.e, handler.proxy, false); + }); + }); + } + + $.event = { + add: add, + remove: remove + } + + /** + * Binds an event to each element in the collection and executes the callback + ``` + $().bind('click',function(){console.log('I clicked '+this.id);}); + ``` + + * @param {String|Object} event + * @param {Function} callback + * @return {Object} jqMobi object + * @title $().bind(event,callback) + */ + $.fn.bind = function(event, callback) { + for (var i = 0; i < this.length; i++) { + add(this[i], event, callback); + } + return this; + }; + /** + * Unbinds an event to each element in the collection. If a callback is passed in, we remove just that one, otherwise we remove all callbacks for those events + ``` + $().unbind('click'); //Unbinds all click events + $().unbind('click',myFunc); //Unbinds myFunc + ``` + + * @param {String|Object} event + * @param {Function} [callback] + * @return {Object} jqMobi object + * @title $().unbind(event,[callback]); + */ + $.fn.unbind = function(event, callback) { + for (var i = 0; i < this.length; i++) { + remove(this[i], event, callback); + } + return this; + }; + + /** + * Binds an event to each element in the collection that will only execute once. When it executes, we remove the event listener then right away so it no longer happens + ``` + $().one('click',function(){console.log('I was clicked once');}); + ``` + + * @param {String|Object} event + * @param {Function} [callback] + * @return jqMobi object + * @title $().one(event,callback); + */ + $.fn.one = function(event, callback) { + return this.each(function(i, element) { + add(this, event, callback, null, function(fn, type) { + return function() { + var result = fn.apply(element, arguments); + remove(element, type, fn); + return result; + } + }); + }); + }; + + /** + * internal variables + * @api private + */ + + var returnTrue = function() { + return true + }, + returnFalse = function() { + return false + }, + eventMethods = { + preventDefault: 'isDefaultPrevented', + stopImmediatePropagation: 'isImmediatePropagationStopped', + stopPropagation: 'isPropagationStopped' + }; + /** + * Creates a proxy function for event handlers + * @param {String} event + * @return {Function} proxy + * @api private + */ + function createProxy(event) { + var proxy = $.extend({ + originalEvent: event + }, event); + $.each(eventMethods, function(name, predicate) { + proxy[name] = function() { + this[predicate] = returnTrue; + return event[name].apply(event, arguments); + }; + proxy[predicate] = returnFalse; + }) + return proxy; + } + + /** + * Delegate an event based off the selector. The event will be registered at the parent level, but executes on the selector. + ``` + $("#div").delegate("p",'click',callback); + ``` + + * @param {String|Array|Object} selector + * @param {String|Object} event + * @param {Function} callback + * @return {Object} jqMobi object + * @title $().delegate(selector,event,callback) + */ + $.fn.delegate = function(selector, event, callback) { + for (var i = 0; i < this.length; i++) { + var element = this[i]; + add(element, event, callback, selector, function(fn) { + return function(e) { + var evt, match = $(e.target).closest(selector, element).get(0); + if (match) { + evt = $.extend(createProxy(e), { + currentTarget: match, + liveFired: element + }); + return fn.apply(match, [evt].concat([].slice.call(arguments, 1))); + } + } + }); + } + return this; + }; + + /** + * Unbinds events that were registered through delegate. It acts upon the selector and event. If a callback is specified, it will remove that one, otherwise it removes all of them. + ``` + $("#div").undelegate("p",'click',callback);//Undelegates callback for the click event + $("#div").undelegate("p",'click');//Undelegates all click events + ``` + + * @param {String|Array|Object} selector + * @param {String|Object} event + * @param {Function} callback + * @return {Object} jqMobi object + * @title $().undelegate(selector,event,[callback]); + */ + $.fn.undelegate = function(selector, event, callback) { + for (var i = 0; i < this.length; i++) { + remove(this[i], event, callback, selector); + } + return this; + } + + /** + * Similar to delegate, but the function parameter order is easier to understand. + * If selector is undefined or a function, we just call .bind, otherwise we use .delegate + ``` + $("#div").on("click","p",callback); + ``` + + * @param {String|Array|Object} selector + * @param {String|Object} event + * @param {Function} callback + * @return {Object} jqMobi object + * @title $().on(event,selector,callback); + */ + $.fn.on = function(event, selector, callback) { + return selector === undefined || $.isFunction(selector) ? this.bind(event, selector) : this.delegate(selector, event, callback); + }; + /** + * Removes event listeners for .on() + * If selector is undefined or a function, we call unbind, otherwise it's undelegate + ``` + $().off("click","p",callback); //Remove callback function for click events + $().off("click","p") //Remove all click events + ``` + + * @param {String|Object} event + * @param {String|Array|Object} selector + * @param {Sunction} callback + * @return {Object} jqMobi object + * @title $().off(event,selector,[callback]) + */ + $.fn.off = function(event, selector, callback) { + return selector === undefined || $.isFunction(selector) ? this.unbind(event, selector) : this.undelegate(selector, event, callback); + }; + + /** + This triggers an event to be dispatched. Usefull for emulating events, etc. + ``` + $().trigger("click",{foo:'bar'});//Trigger the click event and pass in data + ``` + + * @param {String|Object} event + * @param {Object} [data] + * @return {Object} jqMobi object + * @title $().trigger(event,data); + */ + $.fn.trigger = function(event, data) { + if (typeof event == 'string') + event = $.Event(event); + event.data = data; + for (var i = 0; i < this.length; i++) { + this[i].dispatchEvent(event) + } + return this; + }; + + /** + * Creates a custom event to be used internally. + * @param {String} type + * @param {Object} [properties] + * @return {event} a custom event that can then be dispatched + * @title $.Event(type,props); + */ + + $.Event = function(type, props) { + var event = document.createEvent(specialEvents[type] || 'Events'), + bubbles = true; + if (props) + for (var name in props) + (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]); + event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null); + return event; + }; + + /** + * Creates a proxy function so you can change the 'this' context in the function + ``` + var newObj={foo:bar} + $("#main").bind("click",$.proxy(function(evt){console.log(this)},newObj); + ``` + + * @param {Function} Callback + * @param {Object} Context + * @title $.proxy(callback,context); + */ + + $.proxy=function(fnc,ctx){ + var tmp=function(evt){ + + return fnc.call(ctx,evt); + } + return tmp; + } + + /** + * End of APIS + * @api private + */ + + //End zepto/events.js + return $; + + })(window); + '$' in window || (window.$ = jq); + //Helper function used in jq.mobi.plugins. + if (!window.numOnly) { + window.numOnly = function numOnly(val) { + if (isNaN(parseFloat(val))) + val = val.replace(/[^0-9.-]/, ""); + return parseFloat(val); + } + } +} \ No newline at end of file diff --git a/assets/www/js/jq.mobi.min.js b/assets/www/js/jq.mobi.min.js new file mode 100644 index 0000000..29f9c4c --- /dev/null +++ b/assets/www/js/jq.mobi.min.js @@ -0,0 +1,31 @@ +if(!window.jq||typeof jq!=="function"){var jq=function(g){function u(a){return a in s?s[a]:s[a]=RegExp("(^|\\s)"+a+"(\\s|$)")}function o(a){for(var c=0;c]*>/,j={},m=function(a,c){this.length=0;if(a)if(a instanceof m&&c==f)return a;else if(d.isFunction(a))return d(i).ready(a);else if(d.isArray(a)&&a.length!=f){for(var b=0;b")===-1)b=c==i?c.getElementById(a.replace("#","")):[].slice.call(c.querySelectorAll(a));else if(a[0]==="<"&&a[a.length-1]===">"){var e=i.createElement("div");e.innerHTML=a.trim();b=[].slice.call(e.childNodes)}else b=[].slice.call(c.querySelectorAll(a))}catch(d){}return b},oldElement:f,slice:k.slice,setupOld:function(a){if(a==f)return d(); +a.oldElement=this;return a},map:function(a){return d.map(this,function(c,b){return a.call(c,b,c)})},each:function(a){this.forEach(function(c,b){a.call(c,b,c)});return this},ready:function(a){(i.readyState==="complete"||i.readyState==="loaded")&&a();i.addEventListener("DOMContentLoaded",a,!1);return this},find:function(a){if(this.length===0)return f;for(var c=[],b,e=0;e0?b.trim():""}return this},hasClass:function(a, +c){if(this.length===0)return!1;c||(c=this[0]);return u(a).test(c.className)},append:function(a,c){if(a&&a.length!=f&&a.length===0)return this;if(d.isArray(a)||d.isObject(a))a=d(a);var b;for(b=0;b=0&&c.push(e)}return this.setupOld(d(o(c)))},not:function(a){if(this.length==0)return f;for(var c=[],b=0;b0&&(b=setTimeout(function(){a.error.call(void 0,"","timeout")}, +a.timeout));return{}};d.ajax=function(a){var c;try{c=new g.XMLHttpRequest;var b=a||{},e;for(e in z)b[e]||(b[e]=z[e]);if(!b.url)b.url=g.location;if(!b.contentType)b.contentType="application/x-www-form-urlencoded";if(!b.headers)b.headers={};if(b.dataType)switch(b.dataType){case "script":b.dataType="text/javascript, application/javascript";break;case "json":b.dataType="application/json";break;case "xml":b.dataType="application/xml, text/xml";break;case "html":b.dataType="text/html";break;case "text":b.dataType= +"text/plain";break;default:b.dataType="text/html";break;case "jsonp":return d.jsonP(a)}else b.dataType="text/html";if(d.isObject(b.data))b.data=d.param(b.data);b.type.toLowerCase()==="get"&&b.data&&(b.url+=b.url.indexOf("?")===-1?"?"+b.data:"&"+b.data);if(/=\?/.test(b.url))return d.jsonP(b);if(!b.crossDomain)b.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(b.url)&&RegExp.$2!=g.location.host;if(!b.crossDomain)b.headers=d.extend({"X-Requested-With":"XMLHttpRequest"},b.headers);var f,l=b.context,i=/^([\w-]+:)\/\//.test(b.url)? +RegExp.$1:g.location.protocol;c.onreadystatechange=function(){var a=b.dataType;if(c.readyState===4){clearTimeout(f);var d,e=!1;if(c.status>=200&&c.status<300||c.status===0&&i=="file:"){if(a==="application/json"&&!/^\s*$/.test(c.responseText))try{d=JSON.parse(c.responseText)}catch(g){e=g}else d=c.responseText;c.status===0&&d.length===0&&(e=!0);e?b.error.call(l,c,"parsererror",e):b.success.call(l,d,"success",c)}else e=!0,b.error.call(l,c,"error");b.complete.call(l,c,e?"error":"success")}};c.open(b.type, +b.url,!0);if(b.contentType)b.headers["Content-Type"]=b.contentType;for(var j in b.headers)c.setRequestHeader(j,b.headers[j]);if(b.beforeSend.call(l,c,b)===!1)return c.abort(),!1;b.timeout>0&&(f=setTimeout(function(){c.onreadystatechange=n;c.abort();b.error.call(l,c,"timeout")},b.timeout));c.send(b.data)}catch(k){console.log(k)}return c};d.get=function(a,c){return this.ajax({url:a,success:c})};d.post=function(a,c,b,e){typeof c==="function"&&(b=c,c={});e===f&&(e="html");return this.ajax({url:a,type:"POST", +data:c,dataType:e,success:b})};d.getJSON=function(a,c,b){typeof c==="function"&&(b=c,c={});return this.ajax({url:a,data:c,success:b,dataType:"json"})};d.param=function(a,c){var b=[];if(a instanceof m)a.each(function(){b.push((c?c+"[]":this.id)+"="+encodeURIComponent(this.value))});else for(var e in a){var f=c?c+"["+e+"]":e,g=a[e];b.push(d.isObject(g)?d.param(g,f):f+"="+encodeURIComponent(g))}return b.join("&")};d.parseJSON=function(a){return JSON.parse(a)};d.parseXML=function(a){return(new DOMParser).parseFromString(a, +"text/xml")};w(d,navigator.userAgent);d.__detectUA=w;if(typeof String.prototype.trim!=="function")String.prototype.trim=function(){this.replace(/(\r\n|\n|\r)/gm,"").replace(/^\s+|\s+$/,"");return this};d.uuid=function(){var a=function(){return((1+Math.random())*65536|0).toString(16).substring(1)};return a()+a()+"-"+a()+"-"+a()+"-"+a()+"-"+a()+a()+a()};var p={},t=1,I={};d.event={add:q,remove:r};d.fn.bind=function(a,c){for(var b=0;bSettings Logout") + $.ui.actionsheet("[{ + text: 'back', + cssClasses: 'red', + handler: function () { $.ui.goBack(); ; } + }, { + text: 'show alert 5', + cssClasses: 'blue', + handler: function () { alert("hi"); } + }, { + text: 'show alert 6', + cssClasses: '', + handler: function () { alert("goodbye"); } + }]"); + ``` + * @param {String,Array} links + * @title $.ui.actionsheet() + */ + actionsheet: function(opts) { + return jq("#jQUi").actionsheet(opts); + }, + /** + * This is a wrapper to jq.popup.js plugin. If you pass in a text string, it acts like an alert box and just gives a message + ``` + $.ui.popup(opts); + $.ui.popup( { + title:"Alert! Alert!", + message:"This is a test of the emergency alert system!! Don't PANIC!", + cancelText:"Cancel me", + cancelCallback: function(){console.log("cancelled");}, + doneText:"I'm done!", + doneCallback: function(){console.log("Done for!");}, + cancelOnly:false + }); + $.ui.popup('Hi there'); + ``` + * @param {Object|String} options + * @title $.ui.popup(opts) + */ + popup: function(opts) { + return $("#jQUi").popup(opts); + }, + + /** + *This will throw up a mask and block the UI + ``` + $.ui.blockUI(.9) + ```` + * @param {Float} opacity + * @title $.ui.blockUI(opacity) + */ + blockUI: function(opacity) { + $.blockUI(opacity); + }, + /** + *This will remove the UI mask + ``` + $.ui.unblockUI() + ```` + * @title $.ui.unblockUI() + */ + unblockUI: function() { + $.unblockUI(); + }, + /** + * Will remove the bottom nav bar menu from your application + ``` + $.ui.removeFooterMenu(); + ``` + * @title $.ui.removeFooterMenu + */ + removeFooterMenu: function() { + jq("#navbar").hide(); + jq("#content").css("bottom", "0px"); + this.showNavMenu = false; + }, + /** + * Boolean if you want to show the bottom nav menu. + ``` + $.ui.showNavMenu = false; + ``` + * @title $.ui.showNavMenu + */ + showNavMenu: true, + /** + * Boolean if you want to auto launch jqUi + ``` + $.ui.autoLaunch = false; // + * @title $.ui.autoLaunch + */ + autoLaunch: true, + /** + * Boolean if you want to show the back button + ``` + $.ui.showBackButton = false; // + * @title $.ui.showBackButton + */ + showBackbutton: true, + /** + * @api private + */ + backButtonText: "", + /** + * Boolean if you want to reset the scroller position when navigating panels. Default is true + ``` + $.ui.resetScrollers=false; //Do not reset the scrollers when switching panels + ``` + * @title $.ui.resetScrollers + */ + resetScrollers: true, + /** + * function to fire when jqUi is ready and completed launch + ``` + $.ui.ready(function(){console.log('jqUi is ready');}); + ``` + * @param {Function} function to execute + * @title $.ui.ready + */ + ready: function(param) { + if(this.launchCompleted) + param(); + else + document.addEventListener("jq.ui.ready",param,false); + }, + /** + * Override the back button class name + ``` + $.ui.setBackButtonStyle('newClass'); + ``` + * @param {String} new class name + * @title $.ui.setBackButtonStyle(class) + */ + setBackButtonStyle: function(className) { + $am("backButton").className = className; + }, + /** + * Initiate a back transition + ``` + $.ui.goBack() + ``` + + * @title $.ui.goBack() + */ + goBack: function() { + if (this.history.length > 0) { + var tmpEl = this.history.pop(); + this.loadContent(tmpEl.target + "", 0, 1, tmpEl.transition); + this.transitionType = tmpEl.transition; + } + }, + /** + * Clear the history queue + ``` + $.ui.clearHistory() + ``` + + * @title $.ui.clearHistory() + */ + clearHistory: function() { + this.history = []; + this.backButton.style.visibility = "hidden"; + }, + /** + * Update a badge on the selected target. Position can be + bl = bottom left + tl = top left + br = bottom right + tr = top right (default) + ``` + $.ui.updateBadge('#mydiv','3','bl'); + ``` + * @param {String} target + * @param {String} Value + * @param {String} [position] + * @title $.ui.updateBadge(target,value,[position]) + */ + updateBadge: function(target, value, position) { + if (position === undefined) + position = ""; + + if (target[0] != "#") + target = "#" + target; + var badge = jq(target).find("span.jq-badge"); + if (badge.length == 0) { + if (jq(target).css("position") != "absolute") + jq(target).css("position", "relative"); + badge = jq(target).append("" + value + ""); + } else + badge.html(value); + + }, + /** + * Removes a badge from the selected target. + ``` + $.ui.removeBadge('#mydiv'); + ``` + * @param {String} target + * @title $.ui.removeBadge(target) + */ + removeBadge: function(target) { + jq(target).find("span.jq-badge").remove(); + }, + /** + * Toggles the bottom nav nav menu. Force is a boolean to force show or hide. + ``` + $.ui.toggleNavMenu();//toggle it + $.ui.toggleNavMenu(true); //force show it + ``` + * @param {Boolean} [force] + * @title $.ui.toggleNavMenu([force]) + */ + toggleNavMenu: function(force) { + if (!jq.ui.showNavMenu) + return; + if (jq("#navbar").css("display") != "none" && ((force !== undefined && force !== true) || force === undefined)) { + jq("#content").css("bottom", "0px"); + jq("#navbar").hide(); + } else if (force === undefined || (force !== undefined && force === true)) { + jq("#navbar").show(); + jq("#content").css("bottom", jq("#navbar").css("height")); + + } + }, + /** + * Toggles the top header menu. + ``` + $.ui.toggleHeaderMenu();//toggle it + ``` + * @param {Boolean} [force] + * @title $.ui.toggleHeaderMenu([force]) + */ + toggleHeaderMenu: function(force) { + + if (jq("#header").css("display") != "none" && ((force !== undefined && force !== true) || force === undefined)) { + jq("#content").css("top", "0px"); + jq("#header").hide(); + } else if (force === undefined || (force !== undefined && force === true)) { + jq("#header").show(); + jq("#content").css("top", jq("#header").css("height")); + + } + }, + /** + * Toggles the side menu. Force is a boolean to force show or hide. + ``` + $.ui.toggleSideMenu();//toggle it + ``` + * @param {Boolean} [force] + * @title $.ui.toggleSideMenu([force]) + */ + toggleSideMenu: function(force) { + var that = this; + if (!jq("#content").hasClass("hasMenu")) + return; + if (jq("#menu").css("display") != "block" && ((force !== undefined && force !== false) || force === undefined)) { + this.scrollingDivs["menu_scroller"].initEvents(); + jq("#menu").show(); + window.setTimeout(function() { + jq("#menu").addClass("on"); + jq("#header").addClass("on"); + jq("#navbar").addClass("on"); + jq("#content").addClass("on"); + }, 1); //needs to run after + + } else if (force === undefined || (force !== undefined && force === false)) { + this.scrollingDivs["menu_scroller"].removeEvents(); + + jq("#header").removeClass("on"); + jq("#menu").removeClass("on"); + jq("#navbar").removeClass("on"); + jq("#content").removeClass("on"); + setTimeout(function() { + jq("#menu").hide(); + }, 300); //lame I know + } + }, + /** + * Re-wire events on the bottom navbar after content has been updateded + ``` + $.ui.updateNavbar();//toggle it + ``` + * @title $.ui.updateNavbar([force]) + * @api private + */ + updateNavbar: function() { + var that = this; + var links = jq(this.navbar).find("a"); + for (var i = 0; i < links.length; i++) { + links[i].setAttribute("data-ignore-pressed", "true"); + links[i].setAttribute("resetHistory", "true"); + links[i].oldclick = links[i].onclick; + + links[i].onclick = function(e) { + if (that.doingTransition) + return; + jq("#navbar a").removeClass("selected"); + jq(this).addClass("selected"); + if (this.oldclick) + this.oldclick(e); + } + + } + links = null; + }, + /** + * Updates the elements in the navbar + ``` + $.ui.updateNavbarElements(elements); + ``` + * @param {String|Object} Elements + * @title $.ui.updateNavbarElements(Elements) + */ + updateNavbarElements: function(elems) { + var nb = jq("#navbar"); + if (elems === undefined || elems == null) + return; + if (typeof (elems) == "string") + return nb.html(elems), null; + nb.html(""); + for (var i = 0; i < elems.length; i++) { + var node = elems[i].cloneNode(true); + if (elems[i].oldhash) { + node.href = elems[i].oldhref; + node.onclick = elems[i].oldonclick; + } + nb.append(node); + } + this.updateNavbar(); + }, + /** + * Updates the elements in the side menu + ``` + $.ui.updateSideMenu(elements); + ``` + * @param {String|Object} Elements + * @title $.ui.updateSideMenu(Elements) + */ + updateSideMenu: function(elems) { + var that = this; + + var nb = jq("#menu_scroller"); + + if (elems === undefined || elems == null) + return; + if (typeof (elems) == "string") + return nb.html(elems), null; + nb.html(''); + var close = document.createElement("a"); + close.className = "closebutton jqMenuClose"; + close.href = "javascript:;" + close.onclick = function() { + that.toggleSideMenu(); + }; + nb.append(close); + var tmp = document.createElement("div"); + tmp.className = "jqMenuHeader"; + tmp.innerHTML = "Menu"; + nb.append(tmp); + for (var i = 0; i < elems.length; i++) { + var node = elems[i].cloneNode(true); + if (elems[i].oldhash) { + node.href = elems[i].oldhref; + node.onclick = elems[i].oldonclick; + } + nb.append(node); + } + //Move the scroller to the top and hide it + this.scrollingDivs['menu_scroller'].hideScrollbars(); + this.scrollingDivs['menu_scroller'].scrollTo({ + x: 0, + y: 0 + }, "0"); + }, + /** + * Set the title of the current panel + ``` + $.ui.setTitle("new title"); + ``` + + * @param {String} value + * @title $.ui.setTitle(value) + */ + setTitle: function(val) { + this.titleBar.innerHTML = val; + }, + /** + * Override the text for the back button + ``` + $.ui.setBackButtonText("GO..."); + ``` + + * @param {String} value + * @title $.ui.setBackButtonText(value) + */ + setBackButtonText: function(text) { + if (this.backButtonText.length > 0) + this.backButton.innerHTML = this.backButtonText; + else + this.backButton.innerHTML = text; + }, + /** + * Show the loading mask + ``` + $.ui.showMask() + $.ui.showMask(;Doing work') + ``` + + * @param {String} [text] + * @title $.ui.showMask(text); + */ + showMask: function(text) { + if (!text) + text = "Loading Content"; + jq("#jQui_mask>h1").html(text); + $am("jQui_mask").style.display = "block"; + }, + /** + * Hide the loading mask + * @title $.ui.hideMask(); + */ + hideMask: function() { + $am("jQui_mask").style.display = "none"; + }, + /** + * Load a content panel in a modal window. We set the innerHTML so event binding will not work. + ``` + $.ui.showModal("#myDiv"); + ``` + * @param {String|Object} panel to show + * @title $.ui.showModal(); + */ + showModal: function(id) { + var that = this; + try { + if ($am(id)) { + //jq("#modalContainer").html('
'+$am(id).childNodes[0].innerHTML+''); + jq("#modalContainer").html($am(id).childNodes[0].innerHTML+''); + jq('#modalContainer').append(""); + this.modalWindow.style.display = "block"; + + button = null; + content = null; + this.scrollingDivs['modal_container'].initEvents(); + this.scrollToTop('modal'); + } + } catch (e) { + console.log("Error with modal - " + e, this.modalWindow); + } + }, + /** + * Hide the modal window and remove the content + ``` + $.ui.hideModal("#myDiv"); + ``` + * @title $.ui.hideModal(); + */ + hideModal: function() { + $am("modalContainer").innerHTML = ""; + $am("jQui_modal").style.display = "none"; + + this.scrollingDivs['modal_container'].removeEvents(); + }, + + /** + * Update the HTML in a content panel + ``` + $.ui.updateContentDiv("#myDiv","This is the new content"); + ``` + * @param {String,Object} panel + * @param {String} html to update with + * @title $.ui.updateContentDiv(id,content); + */ + updateContentDiv: function(id, content) { + var el = $am(id); + if (!el) + return; + if (el.getAttribute("scrolling") && el.getAttribute("scrolling").toLowerCase() == "no") + el.innerHTML = content; + else + el.childNodes[0].innerHTML = content; + }, + /** + * Dynamically create a new panel on the fly. It wires events, creates the scroller, applies Android fixes, etc. + ``` + $.ui.addContentDiv("myDiv","This is the new content","Title"); + ``` + * @param {String|Object} Element to add + * @param {String} Content + * @param {String} title + * @title $.ui.addContentDiv(id,content,title); + */ + addContentDiv: function(el, content, title, refresh, refreshFunc) { + var myEl = $am(el); + if (!myEl) { + var newDiv = document.createElement("div"); + newDiv.id = el; + newDiv.title = title; + newDiv.innerHTML = content; + } else { + newDiv = myEl; + } + newDiv.className = "panel"; + var that = this; + + myEl = null; + that.addDivAndScroll(newDiv, refresh, refreshFunc); + newDiv = null; + return; + }, + /** + * Takes a div and sets up scrolling for it.. + ``` + $.ui.addDivAndScroll(object); + ``` + * @param {Object} Element + * @title $.ui.addDivAndScroll(element); + * @api private + */ + addDivAndScroll: function(tmp, refreshPull, refreshFunc) { + var addScroller = true; + if (tmp.getAttribute("scrolling") && tmp.getAttribute("scrolling").toLowerCase() == "no") + addScroller = false; + if (!addScroller) { + this.content.appendChild(tmp); + tmp = null; + return; + } + //WE need to clone the div so we keep events + var myDiv = tmp.cloneNode(false); + + + tmp.title = null; + tmp.id = null; + tmp.removeAttribute("footer"); + tmp.removeAttribute("nav"); + jq(tmp).removeClass("panel"); + tmp.style.width = "100%"; + //tmp.style.height = "inherit"; + + myDiv.appendChild(tmp); + + this.content.appendChild(myDiv); + + this.selectBox.getOldSelects(myDiv.id); + this.passwordBox.getOldPasswords(myDiv.id); + + if (addScroller) { + this.scrollingDivs[myDiv.id] = (jq(tmp).scroller({ + scrollBars: true, + verticalScroll: true, + horizontalScroll: false, + vScrollCSS: "jqmScrollbar", + refresh: false + })); + this.scrollingDivs[myDiv.id].removeEvents(); + } + + + myDiv = null; + tmp = null; + }, + + /** + * This has been depricated, as it was a design flaw on my end. People were updating segments of the panel and not the whole part. It's counter intuitive to have to call + * $.ui.updateAnchors(object,reset) after each change. + * @title $.ui.updateAnchors(element,resetHistory); + * @api private + */ + updateAnchors: function(domEl, reset) { + return; + }, + /** + * Scrolls a panel to the top + ``` + $.ui.scrollToTop(id); + ``` + * @param {String} id without hash + * @title $.ui.scrollToTop(id); + */ + scrollToTop: function(id) { + if (this.scrollingDivs[id]) { + this.scrollingDivs[id].scrollTo({ + x: 0, + y: 0 + }, 0); + } + }, + /** + * Scrolls all panels to the top and fixes position when orientation changes + ``` + $.ui.updateOrientation(event); + ``` + * @param {Event} event + * @title $.ui.updateOrientation(event); + * @api private + */ + updateOrientation: function(event) { + for (var j in this.scrollingDivs) { + if (typeof (this.scrollingDivs[j]) !== "function") + this.scrollToTop(j); + } + this.css3animate(this.activeDiv, { + x: "100%", + time: "0ms" + }); + }, + + /** + * This is used when a transition fires to do helper events. We check to see if we need to change the nav menus, footer, and fire + * the load/onload functions for panels + ``` + $.ui.parsePanelFunctions(currentDiv,oldDiv); + ``` + * @param {Object} current div + * @param {Object} old div + * @title $.ui.parsePanelFunctions(currentDiv,oldDiv); + * @api private + */ + parsePanelFunctions: function(what, oldDiv) { + //check for custom footer + var that = this; + var hasFooter = what.getAttribute("data-footer"); + + window.setTimeout(function() { + if (hasFooter && hasFooter.toLowerCase() == "none") { + that.toggleNavMenu(false); + } else { + that.toggleNavMenu(true); + } + if (hasFooter && that.customFooter != hasFooter) { + that.customFooter = hasFooter; + that.updateNavbarElements(jq("#" + hasFooter).children()); + } else if (hasFooter != that.customFooter) { + if (that.customFooter) + that.updateNavbarElements(that.defaultFooter); + that.customFooter = false; + } + if (what.getAttribute("data-tab")) { //Allow the dev to force the footer menu + jq("#navbar a").removeClass("selected"); + jq("#" + what.getAttribute("data-tab")).addClass("selected"); + } + }, 10); + var hasMenu = what.getAttribute("data-nav"); + if (hasMenu && this.customMenu != hasMenu) { + this.customMenu = hasMenu; + this.updateSideMenu(jq("#" + hasMenu).children()); + } else if (hasMenu != this.customMenu) { + if (this.customMenu) + this.updateSideMenu(this.defaultMenu); + this.customMenu = false; + } + + + var fnc = what.getAttribute("data-load"); + if (typeof fnc == "string" && window[fnc]) { + window[fnc](what); + } + if (oldDiv) { + fnc = oldDiv.getAttribute("data-unload"); + if (typeof fnc == "string" && window[fnc]) { + window[fnc](oldDiv); + } + } + if (this.menu.style.display == "block") + this.toggleSideMenu(); //Close on phones to prevent orientation change bug. + + }, + /** + * Helper function that parses a contents html for any script tags and either adds them or executes the code + * @api private + */ + parseScriptTags: function(div) { + if (!div) + return; + var scripts = div.getElementsByTagName("script"); + div = null; + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].src.length > 0 && !that.remoteJSPages[scripts[i].src]) { + var doc = document.createElement("script"); + doc.type = scripts[i].type; + doc.src = scripts[i].src; + document.getElementsByTagName('head')[0].appendChild(doc); + that.remoteJSPages[scripts[i].src] = 1; + doc = null; + } else { + window.eval(scripts[i].innerHTML); + } + } + }, + /** + * This is called to initiate a transition or load content via ajax. + * We can pass in a hash+id or URL and then we parse the panel for additional functions + ``` + $.ui.loadContent("#main",false,false,"up"); + ``` + * @param {String} target + * @param {Boolean} newtab (resets history) + * @param {Boolean} go back (initiate the back click) + * @param {String} transition + * @title $.ui.loadContent(target,newTab,goBack,transition); + * @api public + */ + loadContent: function(target, newTab, back, transition, anchor) { + + if (this.doingTransition) + return; + + try { + what = null; + var that = this; + that.hideMask(); + var loadAjax = true; + if (target.indexOf("#") == -1) { + var urlHash = "url" + crc32(target); //Ajax urls + if ($am(urlHash)) { + + //ajax div already exists. Let's see if we should be refreshing it. + loadAjax = false; + if (anchor.getAttribute("data-refresh-ajax") === 'true' || (anchor.refresh && anchor.refresh === true)) { + loadAjax = true; + } else + target = "#" + urlHash; + } + } + + if (target.indexOf("#") == -1 && anchor && loadAjax) { + + // XML Request + if (this.activeDiv.id == "jQui_ajax" && target == this.ajaxUrl) + return; + if (target.indexOf("http") == -1) + target = AppMobi.webRoot + target; + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + this.doingTransition = false; + + var doReturn = false; + + //Here we check to see if we are retaining the div, if so update it + if ($am(urlHash) !== undefined) { + that.updateContentDiv(urlHash, xmlhttp.responseText); + $am(urlHash).title = anchor.title ? anchor.title : target; + } else if (anchor.getAttribute("data-persist-ajax")) { + + var refresh = (anchor.getAttribute("data-pull-scroller") === 'true') ? true : false; + refreshFunction = refresh ? + function() { + anchor.refresh = true; + that.loadContent(target, newTab, back, transition, anchor); + anchor.refresh = false; + } : null + that.addContentDiv(urlHash, xmlhttp.responseText, refresh, refreshFunction); + $am(urlHash).title = anchor.title ? anchor.title : target; + } else { + that.updateContentDiv("jQui_ajax", xmlhttp.responseText); + $am("jQui_ajax").title = anchor.title ? anchor.title : target; + that.loadContent("#jQui_ajax", newTab, back); + doReturn = true; + } + //Let's load the content now. + //We need to check for any script tags and handle them + var div = document.createElement("div"); + div.innerHTML = xmlhttp.responseText; + that.parseScriptTags(div); + if (doReturn) + return; + + return that.loadContent("#" + urlHash); + + } + }; + ajaxUrl = target; + var newtarget = this.useAjaxCacheBuster?target + (target.split('?')[1] ? '&' : '?') + "cache=" + Math.random() * 10000000000000000:target; + xmlhttp.open("GET", newtarget, true); + xmlhttp.send(); + // show Ajax Mask + this.showMask(); + return; + } else { + // load a div + what = target.replace("#", ""); + + var slashIndex = what.indexOf('/'); + var hashLink = ""; + if (slashIndex != -1) { + // Ignore everything after the slash for loading + hashLink = what.substr(slashIndex); + what = what.substr(0, slashIndex); + } + + what = $am(what); + + if (!what) + throw ("Target: " + target + " was not found"); + if (what == this.activeDiv && !back) + return; + + if (what.getAttribute("data-modal") == "true" || what.getAttribute("modal") == "true") { + return this.showModal(what.id); + } + what.style.display = "block"; + + + + this.transitionType = transition; + var oldDiv = this.activeDiv; + var currWhat = what; + + if (oldDiv == currWhat) //prevent it from going to itself + return; + + if (newTab) { + this.history = []; + this.history.push({ + target: "#" + this.firstDiv.id, + transition: transition + }); + } else if (!back) { + this.history.push({ + target: previousTarget, + transition: transition + }); + } + try { + window.history.pushState(what.id, what.id, startPath + '#' + what.id + hashLink); + $(window).trigger("hashchange", {newUrl: startPath + '#' + what.id + hashLink,oldURL: startPath + previousTarget}); + } + catch (e) { + } + + previousTarget = '#' + what.id + hashLink; + + if (this.resetScrollers && this.scrollingDivs[what.id]) { + this.scrollingDivs[what.id].scrollTo({ + x: 0, + y: 0 + }); + } + this.doingTransition = true; + if (this.availableTransitions[transition]) + this.availableTransitions[transition].call(this, oldDiv, currWhat, back); + else + this.availableTransitions['default'].call(this, oldDiv, currWhat, back); + + if (back) { + if (this.history.length > 0) { + var val = this.history[this.history.length - 1]; + + var el = $am(val.target.replace("#", "")); + this.setBackButtonText(el.title) + } + } else if (this.activeDiv.title) + this.setBackButtonText(this.activeDiv.title) + else + this.setBackButtonText("Back"); + if (what.title) { + this.titleBar.innerHTML = what.title; + } + if (newTab) { + this.setBackButtonText(this.firstDiv.title) + } + + if (this.history.length == 0) { + this.backButton.style.visibility = "hidden"; + this.history = []; + } else if (this.showBackbutton) + this.backButton.style.visibility = "visible"; + this.activeDiv = what; + if (this.scrollingDivs[this.activeDiv.id]) { + this.scrollingDivs[this.activeDiv.id].initEvents(); + } + if (this.scrollingDivs[oldDiv.id]) { + this.scrollingDivs[oldDiv.id].removeEvents(); + } + //Let's check if it has a function to run to update the data + this.parsePanelFunctions(what, oldDiv); + window.scrollTo(1,1); + + } + } catch (e) { + console.log("Error with loading content " + e + " - " + target); + } + }, + + /** + * This is callled when you want to launch jqUi. If autoLaunch is set to true, it gets called on DOMContentLoaded. + * If autoLaunch is set to false, you can manually invoke it. + ``` + $.ui.autoLaunch=false; + $.ui.launch(); + ``` + * @title $.ui.launch(); + */ + launch: function() { + + if (this.hasLaunched == false || this.launchCompleted) { + this.hasLaunched = true; + return; + } + this.isAppMobi = (window.AppMobi && typeof (AppMobi) == "object" && AppMobi.app !== undefined) ? true : false; + var that = this; + this.viewportContainer = jq("#jQUi"); + this.navbar = $am("navbar"); + this.content = $am("content"); + this.header = $am("header"); + this.menu = $am("menu"); + if (this.viewportContainer.length == 0) { + var container = document.createElement("div"); + container.id = "jQUi"; + var body = document.body; + while (body.firstChild) { + container.appendChild(body.firstChild); + } + jq(document.body).prepend(container); + this.viewportContainer = jq("#jQUi"); + } + if (!this.navbar) { + this.navbar = document.createElement("div"); + this.navbar.id = "navbar"; + this.navbar.style.cssText = "display:none"; + this.viewportContainer.append(this.navbar); + } + if (!this.header) { + this.header = document.createElement("div"); + this.header.id = "header"; + this.viewportContainer.prepend(this.header); + } + if (!this.menu) { + this.menu = document.createElement("div"); + this.menu.id = "menu"; + this.menu.style.overflow = "hidden"; + this.menu.innerHTML = ""; + this.viewportContainer.append(this.menu); + this.scrollingDivs["menu_scroller"] = jq("#menu_scroller").scroller({ + scrollBars: true, + verticalScroll: true, + vScrollCSS: "jqmScrollbar" + }); + } + + + if (!this.content) { + this.content = document.createElement("div"); + this.content.id = "content"; + this.viewportContainer.append(this.content); + } + this.header.innerHTML = '

' + header.innerHTML; + this.backButton = $am("backButton"); + this.backButton.className = "button"; + + this.backButton.onclick = function() { + that.goBack(); + }; + this.backButton.style.visibility = "hidden"; + this.titleBar = $am("pageTitle"); + this.addContentDiv("jQui_ajax", ""); + var maskDiv = document.createElement("div"); + maskDiv.id = "jQui_mask"; + maskDiv.className = "ui-loader"; + maskDiv.innerHTML = "

Loading Content

"; + maskDiv.style.zIndex = 20000; + maskDiv.style.display = "none"; + document.body.appendChild(maskDiv); + var modalDiv = document.createElement("div"); + modalDiv.id = "jQui_modal"; + + this.viewportContainer.append(modalDiv); + modalDiv.appendChild(jq("
").get()); + this.scrollingDivs['modal_container'] = jq("#modalContainer").scroller({ + scrollBars: true, + vertical: true, + vScrollCSS: "jqmScrollbar" + }); + + this.modalWindow = modalDiv; + this.updateNavbar(); + var defer = {}; + var contentDivs = this.viewportContainer.get().querySelectorAll(".panel"); + for (var i = 0; i < contentDivs.length; i++) { + var el = contentDivs[i]; + var tmp = el; + var id; + if (el.parentNode && el.parentNode.id != "content") { + el.parentNode.removeChild(el); + var id = el.id; + this.addDivAndScroll(tmp); + if (tmp.getAttribute("selected")) + this.firstDiv = $am(id); + } else if (!el.parsedContent) { + el.parsedContent = 1; + el.parentNode.removeChild(el); + var id = el.id; + this.addDivAndScroll(tmp); + if (tmp.getAttribute("selected")) + this.firstDiv = $am(id); + } + if (el.getAttribute("data-defer")) + defer[id] = el.getAttribute("data-defer"); + el = null; + } + contentDivs = null; + for (var j in defer) { + (function(j) { + jq.ajax({url:AppMobi.webRoot + defer[j], success:function(data) { + if (data.length == 0) + return; + $.ui.updateContentDiv(j, data); + that.parseScriptTags(jq(j).get()); + },error:function(msg){console.log("Error with deferred load "+defer[j])}}); + })(j); + } + if (this.firstDiv) { + + var that = this; + // Fix a bug in iOS where translate3d makes the content blurry + this.activeDiv = this.firstDiv; + if (this.scrollingDivs[this.activeDiv.id]) { + this.scrollingDivs[this.activeDiv.id].initEvents(); + } + window.setTimeout(function() { + //activeDiv = firstDiv; + that.firstDiv.style.display = "block"; + that.css3animate(that.firstDiv, { + x: "100%", + time: "0ms" + }); + if (that.activeDiv.title) + that.titleBar.innerHTML = that.activeDiv.title; + that.parsePanelFunctions(that.activeDiv); + //Load the default hash + if (defaultHash.length > 0&&that.loadDefaultHash) + { + that.loadContent(defaultHash); + } + modalDiv = null; + maskDiv = null; + that.launchCompleted = true; + jq(document).trigger("jq.ui.ready"); + jq("#splashscreen").remove(); + that.defaultFooter = jq("#navbar").children(); + var firstMenu = jq("nav").get(); + that.defaultMenu = jq(firstMenu).children(); + + that.updateSideMenu(that.defaultMenu); + }, 100); + } + + }, + + /** + * Initiate a sliding transition. This is a sample to show how transitions are implemented. These are registered in $.ui.availableTransitions and take in three parameters. + * @param {Object} previous panel + * @param {Object} current panel + * @param {Boolean} go back + * @title $.ui.slideTransition(previousPanel,currentPanel,goBack); + */ + slideTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + + if (back) { + that.css3animate(oldDiv, { + x: "200%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + that.css3animate(currDiv, { + x: "0%", + time: "1ms", + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms" + }); + } + }); + } else { + that.css3animate(oldDiv, { + x: "0%", + time: "200ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + that.css3animate(currDiv, { + x: "200%", + time: "1ms", + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms" + }); + } + }); + } + }, + /** + * This is here to keep the parser from continuing on + * @api private + */ + slideUpTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this; + if (back) { + that.css3animate(currDiv, { + x: "100%", + y: "0%", + time: "1ms" + }); + that.css3animate(oldDiv, { + y: "100%", + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + } + }); + } else { + oldDiv.style.zIndex = 1; + currDiv.style.zIndex = 2; + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + that.css3animate(currDiv, { + y: "100%", + x: "100%", + time: "1ms", + callback: function() { + that.css3animate(currDiv, { + y: "0%", + x: "100%", + time: "200ms" + }); + } + }); + } + }, + slideDownTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + if (back) { + that.css3animate(currDiv, { + x: "100%", + y: "0%", + time: "1ms" + }); + that.css3animate(oldDiv, { + y: "-100%", + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + + } + }); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + } + }); + } else { + oldDiv.style.zIndex = 1; + currDiv.style.zIndex = 2; + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + that.css3animate(currDiv, { + y: "-100%", + x: "100%", + time: "1ms", + callback: function() { + that.css3animate(currDiv, { + y: "0%", + x: "100%", + time: "200ms" + }); + } + }); + } + }, + flipTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + if (back) { + that.css3animate(currDiv, { + x: "200%", + time: "1ms", + scale: .8, + rotateY: "180deg", + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms" + }); + } + }); + that.css3animate(oldDiv, { + x: "200%", + time: "200ms", + scale: .8, + rotateY: "180deg", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + time: "1ms", + opacity: 1, + callback: function() { + that.finishTransition(oldDiv); + } + }); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + } + }); + } else { + oldDiv.style.zIndex = 1; + currDiv.style.zIndex = 2; + that.css3animate(oldDiv, { + x: "200%", + time: "200ms", + scale: '.8', + rotateY: "180deg", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + that.css3animate(currDiv, { + x: "200%", + time: "1ms", + scale: .8, + rotateY: "180deg", + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms" + }); + } + }); + } + }, + fadeTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + if (back) { + that.css3animate(currDiv, { + x: "100%", + time: "1ms" + }); + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + opacity: .1, + callback: function() { + that.css3animate(oldDiv, { + x: 0, + time: "1ms", + opacity: 1, + callback: function() { + that.finishTransition(oldDiv); + } + + }); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + } + }); + } else { + oldDiv.style.zIndex = 1; + currDiv.style.zIndex = 2; + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + currDiv.style.opacity = 0; + that.css3animate(currDiv, { + x: "100%", + time: "1ms", + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms", + opacity: 1 + }); + } + }); + } + }, + popTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + if (back) { + that.css3animate(currDiv, { + x: "100%", + time: "1ms" + }); + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + opacity: .1, + scale: .2, + origin: "50% 50%", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + } + }); + } else { + oldDiv.style.zIndex = 1; + currDiv.style.zIndex = 2; + that.css3animate(oldDiv, { + x: "100%", + time: "200ms", + callback: function() { + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms", + callback: function() { + that.finishTransition(oldDiv); + } + }); + } + }); + that.css3animate(currDiv, { + x: "100%", + y: "0%", + time: "1ms", + scale: .2, + origin: "50% 50%", + opacity: .1, + callback: function() { + that.css3animate(currDiv, { + x: "100%", + time: "200ms", + scale: 1, + opacity: 1, + origin: "0% 0%" + }); + } + }); + } + }, + noTransition: function(oldDiv, currDiv, back) { + oldDiv.style.display = "block"; + currDiv.style.display = "block"; + var that = this + if (back) { + that.css3animate(currDiv, { + x: "100%", + time: "1ms" + }); + that.css3animate(oldDiv, { + x: 0, + time: "1ms" + }); + + } else { + + that.css3animate(oldDiv, { + x: 0, + y: 0, + time: "1ms" + }); + that.css3animate(currDiv, { + x: "100%", + time: "1ms" + }); + } + that.finishTransition(oldDiv); + currDiv.style.zIndex = 2; + oldDiv.style.zIndex = 1; + }, + + /** + * This must be called at the end of every transition to hide the old div and reset the doingTransition variable + * + * @param {Object} Div that transitioned out + * @title $.ui.finishTransition(oldDiv) + */ + finishTransition: function(oldDiv) { + oldDiv.style.display = 'none'; + this.doingTransition = false; + + } + /** + * END + * @api private + */ + }; + + function $am(el) { + el = el.indexOf("#") == -1 ? "#" + el : el; + return jq(el).get(0); + } + + var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; /* Number */ + var crc32 = function( /* String */str, /* Number */crc) { + if (crc == undefined) + crc = 0; + var n = 0; //a number between 0 and 255 + var x = 0; //an hex number + crc = crc ^ (-1); + for (var i = 0, iTop = str.length; i < iTop; i++) { + n = (crc ^ str.charCodeAt(i)) & 0xFF; + x = "0x" + table.substr(n * 9, 8); + crc = (crc >>> 8) ^ x; + } + return crc ^ (-1); + }; + + + $.ui = new ui; +})(jq); + +//The following functions are utilitiy functions for jqUi. They are not apart of the base class, but help with locking the page scroll, +//input box issues, remove the address bar on iOS and android, etc +(function() { + var jQUi; + + //Check to see if any