_base.js 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.mobile._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.mobile._base"] = true;
  8. dojo.provide("dojox.mobile._base");
  9. dojo.require("dijit._WidgetBase");
  10. dojo.isBB = (navigator.userAgent.indexOf("BlackBerry") != -1) && !dojo.isWebKit;
  11. // summary:
  12. // Mobile Widgets
  13. // description:
  14. // This module provides a number of widgets that can be used to build
  15. // web-based applications for mobile devices such as iPhone or Android.
  16. // These widgets work best with webkit-based browsers, such as Safari or
  17. // Chrome, since webkit-specific CSS3 features are used.
  18. // However, the widgets should work in a "graceful degradation" manner
  19. // even on non-CSS3 browsers, such as IE or Firefox. In that case,
  20. // fancy effects, such as animation, gradient color, or round corner
  21. // rectangle, may not work, but you can still operate your application.
  22. //
  23. // Furthermore, as a separate file, a compatibility module,
  24. // dojox.mobile.compat, is available that simulates some of CSS3 features
  25. // used in this module. If you use the compatibility module, fancy visual
  26. // effects work better even on non-CSS3 browsers.
  27. //
  28. // Note that use of dijit._Container, dijit._Contained, dijit._Templated,
  29. // and dojo.query is intentionally avoided to reduce download code size.
  30. dojo.declare(
  31. "dojox.mobile.View",
  32. dijit._WidgetBase,
  33. {
  34. // summary:
  35. // A widget that represents a view that occupies the full screen
  36. // description:
  37. // View acts as a container for any HTML and/or widgets. An entire HTML page
  38. // can have multiple View widgets and the user can navigate through
  39. // the views back and forth without page transitions.
  40. // selected: Boolean
  41. // If true, the view is displayed at startup time.
  42. selected: false,
  43. // keepScrollPos: Boolean
  44. // If true, the scroll position is kept between views.
  45. keepScrollPos: true,
  46. _started: false,
  47. constructor: function(params, node){
  48. if(node){
  49. dojo.byId(node).style.visibility = "hidden";
  50. }
  51. },
  52. buildRendering: function(){
  53. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  54. this.domNode.className = "mblView";
  55. this.connect(this.domNode, "webkitAnimationEnd", "onAnimationEnd");
  56. this.connect(this.domNode, "webkitAnimationStart", "onAnimationStart");
  57. var id = location.href.match(/#(\w+)([^\w=]|$)/) ? RegExp.$1 : null;
  58. this._visible = this.selected && !id || this.id == id;
  59. if(this.selected){
  60. dojox.mobile._defaultView = this;
  61. }
  62. },
  63. startup: function(){
  64. if(this._started){ return; }
  65. var _this = this;
  66. setTimeout(function(){
  67. if(!_this._visible){
  68. _this.domNode.style.display = "none";
  69. }else{
  70. dojox.mobile.currentView = _this;
  71. _this.onStartView();
  72. }
  73. _this.domNode.style.visibility = "visible";
  74. }, dojo.isIE?100:0); // give IE a little time to complete drawing
  75. this._started = true;
  76. },
  77. onStartView: function(){
  78. // Stub function to connect to from your application.
  79. // Called only when this view is shown at startup time.
  80. },
  81. onBeforeTransitionIn: function(moveTo, dir, transition, context, method){
  82. // Stub function to connect to from your application.
  83. },
  84. onAfterTransitionIn: function(moveTo, dir, transition, context, method){
  85. // Stub function to connect to from your application.
  86. },
  87. onBeforeTransitionOut: function(moveTo, dir, transition, context, method){
  88. // Stub function to connect to from your application.
  89. },
  90. onAfterTransitionOut: function(moveTo, dir, transition, context, method){
  91. // Stub function to connect to from your application.
  92. },
  93. _saveState: function(moveTo, dir, transition, context, method){
  94. this._context = context;
  95. this._method = method;
  96. if(transition == "none" || !dojo.isWebKit){
  97. transition = null;
  98. }
  99. this._moveTo = moveTo;
  100. this._dir = dir;
  101. this._transition = transition;
  102. this._arguments = [];
  103. var i;
  104. for(i = 0; i < arguments.length; i++){
  105. this._arguments.push(arguments[i]);
  106. }
  107. this._args = [];
  108. if(context || method){
  109. for(i = 5; i < arguments.length; i++){
  110. this._args.push(arguments[i]);
  111. }
  112. }
  113. },
  114. performTransition: function(/*String*/moveTo, /*Number*/dir, /*String*/transition,
  115. /*Object|null*/context, /*String|Function*/method /*optional args*/){
  116. // summary:
  117. // Function to perform the various types of view transitions, such as fade, slide, and flip.
  118. // moveTo: String
  119. // The destination view id to transition the current view to.
  120. // If null, transitions to a blank view.
  121. // dir: Number
  122. // The transition direction. If 1, transition forward. If -1, transition backward.
  123. // For example, the slide transition slides the view from right to left when dir == 1,
  124. // and from left to right when dir == -1.
  125. // transision: String
  126. // The type of transition to perform. "slide", "fade", or "flip"
  127. // context: Object
  128. // The object that the callback function will receive as "this".
  129. // method: String|Function
  130. // A callback function that is called when the transition has been finished.
  131. // A function reference, or name of a function in context.
  132. // tags:
  133. // public
  134. // example:
  135. // Transitions to the blank view, and then opens another page.
  136. // | performTransition(null, 1, "slide", null, function(){location.href = href;});
  137. if(dojo.hash){
  138. if(typeof(moveTo) == "string" && moveTo.charAt(0) == '#' && !dojox.mobile._params){
  139. dojox.mobile._params = [];
  140. for(var i = 0; i < arguments.length; i++){
  141. dojox.mobile._params.push(arguments[i]);
  142. }
  143. dojo.hash(moveTo);
  144. return;
  145. }
  146. }
  147. this._saveState.apply(this, arguments);
  148. var toNode;
  149. if(moveTo){
  150. if(typeof(moveTo) == "string"){
  151. // removes a leading hash mark (#) and params if exists
  152. // ex. "#bar&myParam=0003" -> "bar"
  153. moveTo.match(/^#?([^&?]+)/);
  154. toNode = RegExp.$1;
  155. }else{
  156. toNode = moveTo;
  157. }
  158. }else{
  159. if(!this._dummyNode){
  160. this._dummyNode = dojo.doc.createElement("DIV");
  161. dojo.body().appendChild(this._dummyNode);
  162. }
  163. toNode = this._dummyNode;
  164. }
  165. var fromNode = this.domNode;
  166. toNode = this.toNode = dojo.byId(toNode);
  167. if(!toNode){ alert("dojox.mobile.View#performTransition: destination view not found: "+toNode); }
  168. toNode.style.visibility = "hidden";
  169. toNode.style.display = "";
  170. this.onBeforeTransitionOut.apply(this, arguments);
  171. var toWidget = dijit.byNode(toNode);
  172. if(toWidget){
  173. // perform view transition keeping the scroll position
  174. if(this.keepScrollPos && !dijit.getEnclosingWidget(this.domNode.parentNode)){
  175. var scrollTop = dojo.body().scrollTop || dojo.doc.documentElement.scrollTop || dojo.global.pageYOffset || 0;
  176. if(dir == 1){
  177. toNode.style.top = "0px";
  178. if(scrollTop > 1){
  179. fromNode.style.top = -scrollTop + "px";
  180. if(dojo.config["mblHideAddressBar"] !== false){
  181. setTimeout(function(){ // iPhone needs setTimeout
  182. dojo.global.scrollTo(0, 1);
  183. }, 0);
  184. }
  185. }
  186. }else{
  187. if(scrollTop > 1 || toNode.offsetTop !== 0){
  188. var toTop = -toNode.offsetTop;
  189. toNode.style.top = "0px";
  190. fromNode.style.top = toTop - scrollTop + "px";
  191. if(dojo.config["mblHideAddressBar"] !== false && toTop > 0){
  192. setTimeout(function(){ // iPhone needs setTimeout
  193. dojo.global.scrollTo(0, toTop + 1);
  194. }, 0);
  195. }
  196. }
  197. }
  198. }else{
  199. toNode.style.top = "0px";
  200. }
  201. toWidget.onBeforeTransitionIn.apply(toWidget, arguments);
  202. }
  203. toNode.style.display = "none";
  204. toNode.style.visibility = "visible";
  205. this._doTransition(fromNode, toNode, transition, dir);
  206. },
  207. _doTransition: function(fromNode, toNode, transition, dir){
  208. var rev = (dir == -1) ? " reverse" : "";
  209. toNode.style.display = "";
  210. if(!transition || transition == "none"){
  211. this.domNode.style.display = "none";
  212. this.invokeCallback();
  213. }else{
  214. dojo.addClass(fromNode, transition + " out" + rev);
  215. dojo.addClass(toNode, transition + " in" + rev);
  216. }
  217. },
  218. onAnimationStart: function(e){
  219. },
  220. onAnimationEnd: function(e){
  221. var isOut = false;
  222. if(dojo.hasClass(this.domNode, "out")){
  223. isOut = true;
  224. this.domNode.style.display = "none";
  225. dojo.forEach([this._transition,"in","out","reverse"], function(s){
  226. dojo.removeClass(this.domNode, s);
  227. }, this);
  228. }
  229. if(e.animationName.indexOf("shrink") === 0){
  230. var li = e.target;
  231. li.style.display = "none";
  232. dojo.removeClass(li, "mblCloseContent");
  233. }
  234. if(isOut){
  235. this.invokeCallback();
  236. }
  237. // this.domNode may be destroyed as a result of invoking the callback,
  238. // so check for that before accessing it.
  239. this.domNode && (this.domNode.className = "mblView");
  240. },
  241. invokeCallback: function(){
  242. this.onAfterTransitionOut.apply(this, this._arguments);
  243. var toWidget = dijit.byNode(this.toNode);
  244. if(toWidget){
  245. toWidget.onAfterTransitionIn.apply(toWidget, this._arguments);
  246. }
  247. dojox.mobile.currentView = toWidget;
  248. var c = this._context, m = this._method;
  249. if(!c && !m){ return; }
  250. if(!m){
  251. m = c;
  252. c = null;
  253. }
  254. c = c || dojo.global;
  255. if(typeof(m) == "string"){
  256. c[m].apply(c, this._args);
  257. }else{
  258. m.apply(c, this._args);
  259. }
  260. },
  261. getShowingView: function(){
  262. // summary:
  263. // Find the currently showing view from my sibling views.
  264. // description:
  265. // Note that dojox.mobile.currentView is the last shown view.
  266. // If the page consists of a splitter, there are multiple showing views.
  267. var nodes = this.domNode.parentNode.childNodes;
  268. for(var i = 0; i < nodes.length; i++){
  269. if(dojo.hasClass(nodes[i], "mblView") && dojo.style(nodes[i], "display") != "none"){
  270. return dijit.byNode(nodes[i]);
  271. }
  272. }
  273. },
  274. show: function(){
  275. // summary:
  276. // Shows this view without a transition animation.
  277. var fs = this.getShowingView().domNode.style; // from-style
  278. var ts = this.domNode.style; // to-style
  279. fs.display = "none";
  280. ts.display = "";
  281. dojox.mobile.currentView = this;
  282. },
  283. addChild: function(widget){
  284. this.containerNode.appendChild(widget.domNode);
  285. }
  286. });
  287. dojo.declare(
  288. "dojox.mobile.Heading",
  289. dijit._WidgetBase,
  290. {
  291. back: "",
  292. href: "",
  293. moveTo: "",
  294. transition: "slide",
  295. label: "",
  296. iconBase: "",
  297. buildRendering: function(){
  298. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("H1");
  299. this.domNode.className = "mblHeading";
  300. this._view = dijit.getEnclosingWidget(this.domNode.parentNode); // parentNode is null if created programmatically
  301. if(this.label){
  302. this.domNode.appendChild(document.createTextNode(this.label));
  303. }else{
  304. this.label = "";
  305. dojo.forEach(this.domNode.childNodes, function(n){
  306. if(n.nodeType == 3){ this.label += n.nodeValue; }
  307. }, this);
  308. this.label = dojo.trim(this.label);
  309. }
  310. if(this.back){
  311. var btn = dojo.create("DIV", {className:"mblArrowButton"}, this.domNode, "first");
  312. var head = dojo.create("DIV", {className:"mblArrowButtonHead"}, btn);
  313. var body = dojo.create("DIV", {className:"mblArrowButtonBody mblArrowButtonText"}, btn);
  314. this._body = body;
  315. this._head = head;
  316. this._btn = btn;
  317. body.innerHTML = this.back;
  318. this.connect(body, "onclick", "onClick");
  319. var neck = dojo.create("DIV", {className:"mblArrowButtonNeck"}, btn);
  320. btn.style.width = body.offsetWidth + head.offsetWidth + "px";
  321. this.setLabel(this.label);
  322. }
  323. },
  324. startup: function(){
  325. if(this._btn){
  326. this._btn.style.width = this._body.offsetWidth + this._head.offsetWidth + "px";
  327. }
  328. },
  329. onClick: function(e){
  330. var h1 = this.domNode;
  331. dojo.addClass(h1, "mblArrowButtonSelected");
  332. setTimeout(function(){
  333. dojo.removeClass(h1, "mblArrowButtonSelected");
  334. }, 1000);
  335. this.goTo(this.moveTo, this.href);
  336. },
  337. setLabel: function(label){
  338. if(label != this.label){
  339. this.label = label;
  340. this.domNode.firstChild.nodeValue = label;
  341. }
  342. },
  343. goTo: function(moveTo, href){
  344. if(!this._view){
  345. this._view = dijit.byNode(this.domNode.parentNode);
  346. }
  347. if(!this._view){ return; }
  348. if(href){
  349. this._view.performTransition(null, -1, this.transition, this, function(){location.href = href;});
  350. }else{
  351. if(dojox.mobile.app && dojox.mobile.app.STAGE_CONTROLLER_ACTIVE){
  352. // If in a full mobile app, then use its mechanisms to move back a scene
  353. dojo.publish("/dojox/mobile/app/goback");
  354. }
  355. else{
  356. this._view.performTransition(moveTo, -1, this.transition);
  357. }
  358. }
  359. }
  360. });
  361. dojo.declare(
  362. "dojox.mobile.RoundRect",
  363. dijit._WidgetBase,
  364. {
  365. shadow: false,
  366. buildRendering: function(){
  367. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  368. this.domNode.className = this.shadow ? "mblRoundRect mblShadow" : "mblRoundRect";
  369. }
  370. });
  371. dojo.declare(
  372. "dojox.mobile.RoundRectCategory",
  373. dijit._WidgetBase,
  374. {
  375. label: "",
  376. buildRendering: function(){
  377. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("H2");
  378. this.domNode.className = "mblRoundRectCategory";
  379. if(this.label){
  380. this.domNode.innerHTML = this.label;
  381. }else{
  382. this.label = this.domNode.innerHTML;
  383. }
  384. }
  385. });
  386. dojo.declare(
  387. "dojox.mobile.EdgeToEdgeCategory",
  388. dojox.mobile.RoundRectCategory,
  389. {
  390. buildRendering: function(){
  391. this.inherited(arguments);
  392. this.domNode.className = "mblEdgeToEdgeCategory";
  393. }
  394. });
  395. dojo.declare(
  396. "dojox.mobile.RoundRectList",
  397. dijit._WidgetBase,
  398. {
  399. transition: "slide",
  400. iconBase: "",
  401. iconPos: "",
  402. buildRendering: function(){
  403. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("UL");
  404. this.domNode.className = "mblRoundRectList";
  405. },
  406. addChild: function(widget){
  407. this.containerNode.appendChild(widget.domNode);
  408. widget.inheritParams();
  409. widget.setIcon();
  410. }
  411. });
  412. dojo.declare(
  413. "dojox.mobile.EdgeToEdgeList",
  414. dojox.mobile.RoundRectList,
  415. {
  416. stateful: false, // keep the selection state or not
  417. buildRendering: function(){
  418. this.inherited(arguments);
  419. this.domNode.className = "mblEdgeToEdgeList";
  420. }
  421. });
  422. dojo.declare(
  423. "dojox.mobile.AbstractItem",
  424. dijit._WidgetBase,
  425. {
  426. icon: "",
  427. iconPos: "", // top,left,width,height (ex. "0,0,29,29")
  428. href: "",
  429. hrefTarget: "",
  430. moveTo: "",
  431. scene: "",
  432. clickable: false,
  433. url: "",
  434. urlTarget: "", // node id under which a new view is created
  435. transition: "",
  436. transitionDir: 1,
  437. callback: null,
  438. sync: true,
  439. label: "",
  440. toggle: false,
  441. _duration: 800, // duration of selection, milliseconds
  442. inheritParams: function(){
  443. var parent = this.getParentWidget();
  444. if(parent){
  445. if(!this.transition){ this.transition = parent.transition; }
  446. if(!this.icon){ this.icon = parent.iconBase; }
  447. if(!this.iconPos){ this.iconPos = parent.iconPos; }
  448. }
  449. },
  450. findCurrentView: function(moveTo){
  451. var w;
  452. if(moveTo){
  453. w = dijit.byId(moveTo);
  454. if(w){ return w.getShowingView(); }
  455. }
  456. var n = this.domNode.parentNode;
  457. while(true){
  458. w = dijit.getEnclosingWidget(n);
  459. if(!w){ return null; }
  460. if(w.performTransition){ break; }
  461. n = w.domNode.parentNode;
  462. }
  463. return w;
  464. },
  465. transitionTo: function(moveTo, href, url, scene){
  466. var w = this.findCurrentView(moveTo); // the current view widget
  467. if(!w || moveTo && w === dijit.byId(moveTo)){ return; }
  468. if(href){
  469. if(this.hrefTarget){
  470. dojox.mobile.openWindow(this.href, this.hrefTarget);
  471. }else{
  472. w.performTransition(null, this.transitionDir, this.transition, this, function(){location.href = href;});
  473. }
  474. return;
  475. } else if(scene){
  476. dojo.publish("/dojox/mobile/app/pushScene", [scene]);
  477. return;
  478. }
  479. if(url){
  480. var id;
  481. if(dojox.mobile._viewMap && dojox.mobile._viewMap[url]){
  482. // external view has already been loaded
  483. id = dojox.mobile._viewMap[url];
  484. }else{
  485. // get the specified external view and append it to the <body>
  486. var text = this._text;
  487. if(!text){
  488. if(this.sync){
  489. text = dojo.trim(dojo._getText(url));
  490. }else{
  491. dojo["require"]("dojo._base.xhr");
  492. var prog = dojox.mobile.ProgressIndicator.getInstance();
  493. dojo.body().appendChild(prog.domNode);
  494. prog.start();
  495. var xhr = dojo.xhrGet({
  496. url: url,
  497. handleAs: "text"
  498. });
  499. xhr.addCallback(dojo.hitch(this, function(response, ioArgs){
  500. prog.stop();
  501. if(response){
  502. this._text = response;
  503. this.transitionTo(moveTo, href, url, scene);
  504. }
  505. }));
  506. xhr.addErrback(function(error){
  507. prog.stop();
  508. alert("Failed to load "+url+"\n"+(error.description||error));
  509. });
  510. return;
  511. }
  512. }
  513. this._text = null;
  514. id = this._parse(text);
  515. if(!dojox.mobile._viewMap){
  516. dojox.mobile._viewMap = [];
  517. }
  518. dojox.mobile._viewMap[url] = id;
  519. }
  520. moveTo = id;
  521. w = this.findCurrentView(moveTo) || w; // the current view widget
  522. }
  523. w.performTransition(moveTo, this.transitionDir, this.transition, this.callback && this, this.callback);
  524. },
  525. _parse: function(text){
  526. var container = dojo.create("DIV");
  527. var view;
  528. var id = this.urlTarget;
  529. var target = dijit.byId(id) && dijit.byId(id).containerNode ||
  530. dojo.byId(id) ||
  531. dojox.mobile.currentView && dojox.mobile.currentView.domNode.parentNode ||
  532. dojo.body();
  533. if(text.charAt(0) == "<"){ // html markup
  534. container.innerHTML = text;
  535. view = container.firstChild; // <div dojoType="dojox.mobile.View">
  536. if(!view && view.nodeType != 1){
  537. alert("dojox.mobile.AbstractItem#transitionTo: invalid view content");
  538. return;
  539. }
  540. view.setAttribute("_started", "true"); // to avoid startup() is called
  541. view.style.visibility = "hidden";
  542. target.appendChild(container);
  543. (dojox.mobile.parser || dojo.parser).parse(container);
  544. target.appendChild(target.removeChild(container).firstChild); // reparent
  545. }else if(text.charAt(0) == "{"){ // json
  546. target.appendChild(container);
  547. this._ws = [];
  548. view = this._instantiate(eval('('+text+')'), container);
  549. for(var i = 0; i < this._ws.length; i++){
  550. var w = this._ws[i];
  551. w.startup && !w._started && (!w.getParent || !w.getParent()) && w.startup();
  552. }
  553. this._ws = null;
  554. }
  555. view.style.display = "none";
  556. view.style.visibility = "visible";
  557. var id = view.id;
  558. return dojo.hash ? "#" + id : id;
  559. },
  560. _instantiate: function(/*Object*/obj, /*DomNode*/node, /*Widget*/parent){
  561. var widget;
  562. for(var key in obj){
  563. if(key.charAt(0) == "@"){ continue; }
  564. var cls = dojo.getObject(key);
  565. if(!cls){ continue; }
  566. var params = {};
  567. var proto = cls.prototype;
  568. var objs = dojo.isArray(obj[key]) ? obj[key] : [obj[key]];
  569. for(var i = 0; i < objs.length; i++){
  570. for(var prop in objs[i]){
  571. if(prop.charAt(0) == "@"){
  572. var val = objs[i][prop];
  573. prop = prop.substring(1);
  574. if(typeof proto[prop] == "string"){
  575. params[prop] = val;
  576. }else if(typeof proto[prop] == "number"){
  577. params[prop] = val - 0;
  578. }else if(typeof proto[prop] == "boolean"){
  579. params[prop] = (val != "false");
  580. }else if(typeof proto[prop] == "object"){
  581. params[prop] = eval("(" + val + ")");
  582. }
  583. }
  584. }
  585. widget = new cls(params, node);
  586. if(!node){ // not to call View's startup()
  587. this._ws.push(widget);
  588. }
  589. if(parent && parent.addChild){
  590. parent.addChild(widget);
  591. }
  592. this._instantiate(objs[i], null, widget);
  593. }
  594. }
  595. return widget && widget.domNode;
  596. },
  597. createDomButton: function(/*DomNode*/refNode, /*DomNode?*/toNode){
  598. var s = refNode.className;
  599. if(s.match(/mblDomButton\w+_(\d+)/)){
  600. var nDiv = RegExp.$1 - 0;
  601. for(var i = 0, p = (toNode||refNode); i < nDiv; i++){
  602. p = dojo.create("DIV", null, p);
  603. }
  604. }
  605. },
  606. select: function(/*Boolean?*/deselect){
  607. // subclass must implement
  608. },
  609. defaultClickAction: function(){
  610. if(this.toggle){
  611. this.select(this.selected);
  612. }else if(!this.selected){
  613. this.select();
  614. if(!this.selectOne){
  615. var _this = this;
  616. setTimeout(function(){
  617. _this.select(true);
  618. }, this._duration);
  619. }
  620. if(this.moveTo || this.href || this.url || this.scene){
  621. this.transitionTo(this.moveTo, this.href, this.url, this.scene);
  622. }
  623. }
  624. },
  625. getParentWidget: function(){
  626. var ref = this.srcNodeRef || this.domNode;
  627. return ref && ref.parentNode ? dijit.getEnclosingWidget(ref.parentNode) : null;
  628. }
  629. });
  630. dojo.declare(
  631. "dojox.mobile.ListItem",
  632. dojox.mobile.AbstractItem,
  633. {
  634. rightText: "",
  635. btnClass: "",
  636. anchorLabel: false,
  637. noArrow: false,
  638. selected: false,
  639. buildRendering: function(){
  640. this.inheritParams();
  641. var a = this.anchorNode = dojo.create("A");
  642. a.className = "mblListItemAnchor";
  643. var box = dojo.create("DIV");
  644. box.className = "mblListItemTextBox";
  645. if(this.anchorLabel){
  646. box.style.cursor = "pointer";
  647. }
  648. var r = this.srcNodeRef;
  649. if(r){
  650. for(var i = 0, len = r.childNodes.length; i < len; i++){
  651. box.appendChild(r.removeChild(r.firstChild));
  652. }
  653. }
  654. if(this.label){
  655. box.appendChild(dojo.doc.createTextNode(this.label));
  656. }
  657. a.appendChild(box);
  658. if(this.rightText){
  659. this._setRightTextAttr(this.rightText);
  660. }
  661. if(this.moveTo || this.href || this.url || this.clickable){
  662. var parent = this.getParentWidget();
  663. if(!this.noArrow && !(parent && parent.stateful)){
  664. var arrow = dojo.create("DIV");
  665. arrow.className = "mblArrow";
  666. a.appendChild(arrow);
  667. }
  668. this.connect(a, "onclick", "onClick");
  669. }else if(this.btnClass){
  670. var div = this.btnNode = dojo.create("DIV");
  671. div.className = this.btnClass+" mblRightButton";
  672. div.appendChild(dojo.create("DIV"));
  673. div.appendChild(dojo.create("P"));
  674. var dummyDiv = dojo.create("DIV");
  675. dummyDiv.className = "mblRightButtonContainer";
  676. dummyDiv.appendChild(div);
  677. a.appendChild(dummyDiv);
  678. dojo.addClass(a, "mblListItemAnchorHasRightButton");
  679. setTimeout(function(){
  680. dummyDiv.style.width = div.offsetWidth + "px";
  681. dummyDiv.style.height = div.offsetHeight + "px";
  682. if(dojo.isIE){
  683. // IE seems to ignore the height of LI without this..
  684. a.parentNode.style.height = a.parentNode.offsetHeight + "px";
  685. }
  686. }, 0);
  687. }
  688. if(this.anchorLabel){
  689. box.style.display = "inline"; // to narrow the text region
  690. }
  691. var li = this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("LI");
  692. li.className = "mblListItem" + (this.selected ? " mblItemSelected" : "");
  693. li.appendChild(a);
  694. this.setIcon();
  695. },
  696. setIcon: function(){
  697. if(this.iconNode){ return; }
  698. var a = this.anchorNode;
  699. if(this.icon && this.icon != "none"){
  700. var img = this.iconNode = dojo.create("IMG");
  701. img.className = "mblListItemIcon";
  702. img.src = this.icon;
  703. this.domNode.insertBefore(img, a);
  704. dojox.mobile.setupIcon(this.iconNode, this.iconPos);
  705. dojo.removeClass(a, "mblListItemAnchorNoIcon");
  706. }else{
  707. dojo.addClass(a, "mblListItemAnchorNoIcon");
  708. }
  709. },
  710. onClick: function(e){
  711. var a = e.currentTarget;
  712. var li = a.parentNode;
  713. if(dojo.hasClass(li, "mblItemSelected")){ return; } // already selected
  714. if(this.anchorLabel){
  715. for(var p = e.target; p.tagName != "LI"; p = p.parentNode){
  716. if(p.className == "mblListItemTextBox"){
  717. dojo.addClass(p, "mblListItemTextBoxSelected");
  718. setTimeout(function(){
  719. dojo.removeClass(p, "mblListItemTextBoxSelected");
  720. }, 1000);
  721. this.onAnchorLabelClicked(e);
  722. return;
  723. }
  724. }
  725. }
  726. if(this.getParentWidget().stateful){
  727. for(var i = 0, c = li.parentNode.childNodes; i < c.length; i++){
  728. dojo.removeClass(c[i], "mblItemSelected");
  729. }
  730. }else{
  731. setTimeout(function(){
  732. dojo.removeClass(li, "mblItemSelected");
  733. }, 1000);
  734. }
  735. dojo.addClass(li, "mblItemSelected");
  736. this.transitionTo(this.moveTo, this.href, this.url, this.scene);
  737. },
  738. onAnchorLabelClicked: function(e){
  739. },
  740. _setRightTextAttr: function(/*String*/text){
  741. this.rightText = text;
  742. if(!this._rightTextNode){
  743. this._rightTextNode = dojo.create("DIV", {className:"mblRightText"}, this.anchorNode);
  744. }
  745. this._rightTextNode.innerHTML = text;
  746. }
  747. });
  748. dojo.declare(
  749. "dojox.mobile.Switch",
  750. dijit._WidgetBase,
  751. {
  752. value: "on",
  753. leftLabel: "ON",
  754. rightLabel: "OFF",
  755. _width: 53,
  756. buildRendering: function(){
  757. this.domNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  758. this.domNode.className = "mblSwitch";
  759. this.domNode.innerHTML =
  760. '<div class="mblSwitchInner">'
  761. + '<div class="mblSwitchBg mblSwitchBgLeft">'
  762. + '<div class="mblSwitchText mblSwitchTextLeft">'+this.leftLabel+'</div>'
  763. + '</div>'
  764. + '<div class="mblSwitchBg mblSwitchBgRight">'
  765. + '<div class="mblSwitchText mblSwitchTextRight">'+this.rightLabel+'</div>'
  766. + '</div>'
  767. + '<div class="mblSwitchKnob"></div>'
  768. + '</div>';
  769. var n = this.inner = this.domNode.firstChild;
  770. this.left = n.childNodes[0];
  771. this.right = n.childNodes[1];
  772. this.knob = n.childNodes[2];
  773. dojo.addClass(this.domNode, (this.value == "on") ? "mblSwitchOn" : "mblSwitchOff");
  774. this[this.value == "off" ? "left" : "right"].style.display = "none";
  775. },
  776. postCreate: function(){
  777. this.connect(this.knob, "onclick", "onClick");
  778. this.connect(this.knob, "touchstart", "onTouchStart");
  779. this.connect(this.knob, "mousedown", "onTouchStart");
  780. },
  781. _changeState: function(/*String*/state){
  782. this.inner.style.left = "";
  783. dojo.addClass(this.domNode, "mblSwitchAnimation");
  784. dojo.removeClass(this.domNode, (state == "on") ? "mblSwitchOff" : "mblSwitchOn");
  785. dojo.addClass(this.domNode, (state == "on") ? "mblSwitchOn" : "mblSwitchOff");
  786. var _this = this;
  787. setTimeout(function(){
  788. _this[state == "off" ? "left" : "right"].style.display = "none";
  789. dojo.removeClass(_this.domNode, "mblSwitchAnimation");
  790. }, 300);
  791. },
  792. onClick: function(e){
  793. if(this._moved){ return; }
  794. this.value = (this.value == "on") ? "off" : "on";
  795. this._changeState(this.value);
  796. this.onStateChanged(this.value);
  797. },
  798. onTouchStart: function(e){
  799. this._moved = false;
  800. this.innerStartX = this.inner.offsetLeft;
  801. if(e.targetTouches){
  802. this.touchStartX = e.targetTouches[0].clientX;
  803. this._conn1 = dojo.connect(this.inner, "touchmove", this, "onTouchMove");
  804. this._conn2 = dojo.connect(this.inner, "touchend", this, "onTouchEnd");
  805. }
  806. this.left.style.display = "block";
  807. this.right.style.display = "block";
  808. dojo.stopEvent(e);
  809. },
  810. onTouchMove: function(e){
  811. e.preventDefault();
  812. var dx;
  813. if(e.targetTouches){
  814. if(e.targetTouches.length != 1){ return false; }
  815. dx = e.targetTouches[0].clientX - this.touchStartX;
  816. }else{
  817. dx = e.clientX - this.touchStartX;
  818. }
  819. var pos = this.innerStartX + dx;
  820. var d = 10;
  821. if(pos <= -(this._width-d)){ pos = -this._width; }
  822. if(pos >= -d){ pos = 0; }
  823. this.inner.style.left = pos + "px";
  824. this._moved = true;
  825. },
  826. onTouchEnd: function(e){
  827. dojo.disconnect(this._conn1);
  828. dojo.disconnect(this._conn2);
  829. if(this.innerStartX == this.inner.offsetLeft){
  830. if(dojo.isWebKit){
  831. var ev = dojo.doc.createEvent("MouseEvents");
  832. ev.initEvent("click", true, true);
  833. this.knob.dispatchEvent(ev);
  834. }
  835. return;
  836. }
  837. var newState = (this.inner.offsetLeft < -(this._width/2)) ? "off" : "on";
  838. this._changeState(newState);
  839. if(newState != this.value){
  840. this.value = newState;
  841. this.onStateChanged(this.value);
  842. }
  843. },
  844. onStateChanged: function(/*String*/newState){
  845. }
  846. });
  847. dojo.declare(
  848. "dojox.mobile.Button",
  849. dijit._WidgetBase,
  850. {
  851. btnClass: "mblBlueButton",
  852. duration: 1000, // duration of selection, milliseconds
  853. label: null,
  854. buildRendering: function(){
  855. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("BUTTON");
  856. this.domNode.className = "mblButton "+this.btnClass;
  857. if(this.label){
  858. this.domNode.innerHTML = this.label;
  859. }
  860. this.connect(this.domNode, "onclick", "onClick");
  861. },
  862. onClick: function(e){
  863. var button = this.domNode;
  864. var c = "mblButtonSelected "+this.btnClass+"Selected";
  865. dojo.addClass(button, c);
  866. setTimeout(function(){
  867. dojo.removeClass(button, c);
  868. }, this.duration);
  869. }
  870. });
  871. dojo.declare(
  872. "dojox.mobile.ToolBarButton",
  873. dojox.mobile.AbstractItem,
  874. {
  875. selected: false,
  876. _defaultColor: "mblColorDefault",
  877. _selColor: "mblColorDefaultSel",
  878. buildRendering: function(){
  879. this.inheritParams();
  880. this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("div");
  881. dojo.addClass(this.domNode, "mblToolbarButton mblArrowButtonText");
  882. var color;
  883. if(this.selected){
  884. color = this._selColor;
  885. }else if(this.domNode.className.indexOf("mblColor") == -1){
  886. color = this._defaultColor;
  887. }
  888. dojo.addClass(this.domNode, color);
  889. if(this.label){
  890. this.domNode.innerHTML = this.label;
  891. }else{
  892. this.label = this.domNode.innerHTML;
  893. }
  894. if(this.icon && this.icon != "none"){
  895. var img;
  896. if(this.iconPos){
  897. var iconDiv = dojo.create("DIV", null, this.domNode);
  898. img = dojo.create("IMG", null, iconDiv);
  899. img.style.position = "absolute";
  900. var arr = this.iconPos.split(/[ ,]/);
  901. dojo.style(iconDiv, {
  902. position: "relative",
  903. width: arr[2] + "px",
  904. height: arr[3] + "px"
  905. });
  906. }else{
  907. img = dojo.create("IMG", null, this.domNode);
  908. }
  909. img.src = this.icon;
  910. dojox.mobile.setupIcon(img, this.iconPos);
  911. this.iconNode = img;
  912. }
  913. this.createDomButton(this.domNode);
  914. this.connect(this.domNode, "onclick", "onClick");
  915. },
  916. select: function(/*Boolean?*/deselect){
  917. dojo.toggleClass(this.domNode, this._selColor, !deselect);
  918. this.selected = !deselect;
  919. },
  920. onClick: function(e){
  921. this.defaultClickAction();
  922. }
  923. });
  924. dojo.declare(
  925. "dojox.mobile.ProgressIndicator",
  926. null,
  927. {
  928. interval: 100, // milliseconds
  929. colors: [
  930. "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0",
  931. "#C0C0C0", "#C0C0C0", "#B8B9B8", "#AEAFAE",
  932. "#A4A5A4", "#9A9A9A", "#8E8E8E", "#838383"
  933. ],
  934. _bars: [],
  935. constructor: function(){
  936. this.domNode = dojo.create("DIV");
  937. this.domNode.className = "mblProgContainer";
  938. for(var i = 0; i < 12; i++){
  939. var div = dojo.create("DIV");
  940. div.className = "mblProg mblProg"+i;
  941. this.domNode.appendChild(div);
  942. this._bars.push(div);
  943. }
  944. },
  945. start: function(){
  946. var cntr = 0;
  947. var _this = this;
  948. this.timer = setInterval(function(){
  949. cntr--;
  950. cntr = cntr < 0 ? 11 : cntr;
  951. var c = _this.colors;
  952. for(var i = 0; i < 12; i++){
  953. var idx = (cntr + i) % 12;
  954. _this._bars[i].style.backgroundColor = c[idx];
  955. }
  956. }, this.interval);
  957. },
  958. stop: function(){
  959. if(this.timer){
  960. clearInterval(this.timer);
  961. }
  962. this.timer = null;
  963. if(this.domNode.parentNode){
  964. this.domNode.parentNode.removeChild(this.domNode);
  965. }
  966. }
  967. });
  968. dojox.mobile.ProgressIndicator._instance = null;
  969. dojox.mobile.ProgressIndicator.getInstance = function(){
  970. if(!dojox.mobile.ProgressIndicator._instance){
  971. dojox.mobile.ProgressIndicator._instance = new dojox.mobile.ProgressIndicator();
  972. }
  973. return dojox.mobile.ProgressIndicator._instance;
  974. };
  975. dojox.mobile.addClass = function(){
  976. // summary:
  977. // Adds a theme class name to <body>.
  978. // description:
  979. // Finds the currently applied theme name, such as 'iphone' or 'android'
  980. // from link elements, and adds it as a class name for the body element.
  981. var elems = document.getElementsByTagName("link");
  982. for(var i = 0, len = elems.length; i < len; i++){
  983. if(elems[i].href.match(/dojox\/mobile\/themes\/(\w+)\//)){
  984. dojox.mobile.theme = RegExp.$1;
  985. dojo.addClass(dojo.body(), dojox.mobile.theme);
  986. break;
  987. }
  988. }
  989. };
  990. dojox.mobile.setupIcon = function(/*DomNode*/iconNode, /*String*/iconPos){
  991. if(iconNode && iconPos){
  992. var arr = dojo.map(iconPos.split(/[ ,]/),
  993. function(item){ return item - 0; });
  994. var t = arr[0]; // top
  995. var r = arr[1] + arr[2]; // right
  996. var b = arr[0] + arr[3]; // bottom
  997. var l = arr[1]; // left
  998. iconNode.style.clip = "rect("+t+"px "+r+"px "+b+"px "+l+"px)";
  999. iconNode.style.top = dojo.style(iconNode, "top") - t + "px";
  1000. iconNode.style.left = dojo.style(iconNode.parentNode, "paddingLeft") - l + "px";
  1001. }
  1002. };
  1003. dojox.mobile.hideAddressBar = function(){
  1004. dojo.body().style.minHeight = "1000px"; // to ensure enough height for scrollTo to work
  1005. setTimeout(function(){ scrollTo(0, 1); }, 100);
  1006. setTimeout(function(){ scrollTo(0, 1); }, 400);
  1007. setTimeout(function(){
  1008. scrollTo(0, 1);
  1009. // re-define the min-height with the actual height
  1010. dojo.body().style.minHeight = (dojo.global.innerHeight||dojo.doc.documentElement.clientHeight) + "px";
  1011. }, 1000);
  1012. };
  1013. dojox.mobile.openWindow = function(url, target){
  1014. dojo.global.open(url, target || "_blank");
  1015. };
  1016. dojo._loaders.unshift(function(){
  1017. // avoid use of dojo.query
  1018. /*
  1019. var list = dojo.query('[lazy=true] [dojoType]', null);
  1020. list.forEach(function(node, index, nodeList){
  1021. node.setAttribute("__dojoType", node.getAttribute("dojoType"));
  1022. node.removeAttribute("dojoType");
  1023. });
  1024. */
  1025. var nodes = dojo.body().getElementsByTagName("*");
  1026. var i, len, s;
  1027. len = nodes.length;
  1028. for(i = 0; i < len; i++){
  1029. s = nodes[i].getAttribute("dojoType");
  1030. if(s){
  1031. if(nodes[i].parentNode.getAttribute("lazy") == "true"){
  1032. nodes[i].setAttribute("__dojoType", s);
  1033. nodes[i].removeAttribute("dojoType");
  1034. }
  1035. }
  1036. }
  1037. });
  1038. dojo.addOnLoad(function(){
  1039. dojox.mobile.addClass();
  1040. if(dojo.config["mblApplyPageStyles"] !== false){
  1041. dojo.addClass(dojo.doc.documentElement, "mobile");
  1042. }
  1043. // You can disable hiding the address bar with the following djConfig.
  1044. // var djConfig = { mblHideAddressBar: false };
  1045. if(dojo.config["mblHideAddressBar"] !== false){
  1046. dojox.mobile.hideAddressBar();
  1047. if(dojo.config["mblAlwaysHideAddressBar"] == true){
  1048. if(dojo.global.onorientationchange !== undefined){
  1049. dojo.connect(dojo.global, "onorientationchange", dojox.mobile.hideAddressBar);
  1050. }else{
  1051. dojo.connect(dojo.global, "onresize", dojox.mobile.hideAddressBar);
  1052. }
  1053. }
  1054. }
  1055. // avoid use of dojo.query
  1056. /*
  1057. var list = dojo.query('[__dojoType]', null);
  1058. list.forEach(function(node, index, nodeList){
  1059. node.setAttribute("dojoType", node.getAttribute("__dojoType"));
  1060. node.removeAttribute("__dojoType");
  1061. });
  1062. */
  1063. var nodes = dojo.body().getElementsByTagName("*");
  1064. var i, len = nodes.length, s;
  1065. for(i = 0; i < len; i++){
  1066. s = nodes[i].getAttribute("__dojoType");
  1067. if(s){
  1068. nodes[i].setAttribute("dojoType", s);
  1069. nodes[i].removeAttribute("__dojoType");
  1070. }
  1071. }
  1072. if(dojo.hash){
  1073. // find widgets under root recursively
  1074. var findWidgets = function(root){
  1075. var arr;
  1076. arr = dijit.findWidgets(root);
  1077. var widgets = arr;
  1078. for(var i = 0; i < widgets.length; i++){
  1079. arr = arr.concat(findWidgets(widgets[i].containerNode));
  1080. }
  1081. return arr;
  1082. };
  1083. dojo.subscribe("/dojo/hashchange", null, function(value){
  1084. var view = dojox.mobile.currentView;
  1085. if(!view){ return; }
  1086. var params = dojox.mobile._params;
  1087. if(!params){ // browser back/forward button was pressed
  1088. var moveTo = value ? value : dojox.mobile._defaultView.id;
  1089. var widgets = findWidgets(view.domNode);
  1090. var dir = 1, transition = "slide";
  1091. for(i = 0; i < widgets.length; i++){
  1092. var w = widgets[i];
  1093. if("#"+moveTo == w.moveTo){
  1094. // found a widget that has the given moveTo
  1095. transition = w.transition;
  1096. dir = (w instanceof dojox.mobile.Heading) ? -1 : 1;
  1097. break;
  1098. }
  1099. }
  1100. params = [ moveTo, dir, transition ];
  1101. }
  1102. view.performTransition.apply(view, params);
  1103. dojox.mobile._params = null;
  1104. });
  1105. }
  1106. dojo.body().style.visibility = "visible";
  1107. });
  1108. dijit.getEnclosingWidget = function(node){
  1109. while(node && node.tagName !== "BODY"){
  1110. if(node.getAttribute && node.getAttribute("widgetId")){
  1111. return dijit.registry.byId(node.getAttribute("widgetId"));
  1112. }
  1113. node = node._parentNode || node.parentNode;
  1114. }
  1115. return null;
  1116. };
  1117. }