text: dojo.provide("dojox.mobile._base");
  
  dojo.require("dijit._WidgetBase");
  dojo.isBB = (navigator.userAgent.indexOf("BlackBerry") != -1) && !dojo.isWebKit;
  
  // summary:
  //		Mobile Widgets
  // description:
  //		This module provides a number of widgets that can be used to build
  //		web-based applications for mobile devices such as iPhone or Android.
  //		These widgets work best with webkit-based browsers, such as Safari or
  //		Chrome, since webkit-specific CSS3 features are used.
  //		However, the widgets should work in a "graceful degradation" manner
  //		even on non-CSS3 browsers, such as IE or Firefox. In that case,
  //		fancy effects, such as animation, gradient color, or round corner
  //		rectangle, may not work, but you can still operate your application.
  //
  //		Furthermore, as a separate file, a compatibility module,
  //		dojox.mobile.compat, is available that simulates some of CSS3 features
  //		used in this module. If you use the compatibility module, fancy visual
  //		effects work better even on non-CSS3 browsers.
  //
  //		Note that use of dijit._Container, dijit._Contained, dijit._Templated,
  //		and dojo.query is intentionally avoided to reduce download code size.
  
  dojo.declare(
  	"dojox.mobile.View",
  	dijit._WidgetBase,
  {
  	// summary:
  	//		A widget that represents a view that occupies the full screen
  	// description:
  	//		View acts as a container for any HTML and/or widgets. An entire HTML page
  	//		can have multiple View widgets and the user can navigate through
  	//		the views back and forth without page transitions.
  
  	// selected: Boolean
  	//		If true, the view is displayed at startup time.
  	selected: false,
  
  	// keepScrollPos: Boolean
  	//		If true, the scroll position is kept between views.
  	keepScrollPos: true,
  
  	_started: false,
  
  	constructor: function(params, node){
  		if(node){
  			dojo.byId(node).style.visibility = "hidden";
  		}
  	},
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  		this.domNode.className = "mblView";
  		this.connect(this.domNode, "webkitAnimationEnd", "onAnimationEnd");
  		this.connect(this.domNode, "webkitAnimationStart", "onAnimationStart");
  		var id = location.href.match(/#(\w+)([^\w=]|$)/) ? RegExp.$1 : null;
  
  		this._visible = this.selected && !id || this.id == id;
  
  		if(this.selected){
  			dojox.mobile._defaultView = this;
  		}
  	},
  
  	startup: function(){
  		if(this._started){ return; }
  		var _this = this;
  		setTimeout(function(){
  			if(!_this._visible){
  				_this.domNode.style.display = "none";
  			}else{
  				dojox.mobile.currentView = _this;
  				_this.onStartView();
  			}
  			_this.domNode.style.visibility = "visible";
  		}, dojo.isIE?100:0); // give IE a little time to complete drawing
  		this._started = true;
  	},
  
  	onStartView: function(){
  		// Stub function to connect to from your application.
  		// Called only when this view is shown at startup time.
  	},
  
  	onBeforeTransitionIn: function(moveTo, dir, transition, context, method){
  		// Stub function to connect to from your application.
  	},
  
  	onAfterTransitionIn: function(moveTo, dir, transition, context, method){
  		// Stub function to connect to from your application.
  	},
  
  	onBeforeTransitionOut: function(moveTo, dir, transition, context, method){
  		// Stub function to connect to from your application.
  	},
  
  	onAfterTransitionOut: function(moveTo, dir, transition, context, method){
  		// Stub function to connect to from your application.
  	},
  
  	_saveState: function(moveTo, dir, transition, context, method){
  		this._context = context;
  		this._method = method;
  		if(transition == "none" || !dojo.isWebKit){
  			transition = null;
  		}
  		this._moveTo = moveTo;
  		this._dir = dir;
  		this._transition = transition;
  		this._arguments = [];
  		var i;
  		for(i = 0; i < arguments.length; i++){
  			this._arguments.push(arguments[i]);
  		}
  		this._args = [];
  		if(context || method){
  			for(i = 5; i < arguments.length; i++){
  				this._args.push(arguments[i]);
  			}
  		}
  	},
  
  	performTransition: function(/*String*/moveTo, /*Number*/dir, /*String*/transition,
  								/*Object|null*/context, /*String|Function*/method /*optional args*/){
  		// summary:
  		//		Function to perform the various types of view transitions, such as fade, slide, and flip.
  		// moveTo: String
  		//		The destination view id to transition the current view to.
  		//		If null, transitions to a blank view.
  		// dir: Number
  		//		The transition direction. If 1, transition forward. If -1, transition backward.
  		//		For example, the slide transition slides the view from right to left when dir == 1,
  		//		and from left to right when dir == -1.
  		// transision: String
  		//		The type of transition to perform. "slide", "fade", or "flip"
  		// context: Object
  		//		The object that the callback function will receive as "this".
  		// method: String|Function
  		//		A callback function that is called when the transition has been finished.
  		//		A function reference, or name of a function in context.
  		// tags:
  		//		public
  		// example:
  		//		Transitions to the blank view, and then opens another page.
  		//	|	performTransition(null, 1, "slide", null, function(){location.href = href;});
  		if(dojo.hash){
  			if(typeof(moveTo) == "string" && moveTo.charAt(0) == '#' && !dojox.mobile._params){
  				dojox.mobile._params = [];
  				for(var i = 0; i < arguments.length; i++){
  					dojox.mobile._params.push(arguments[i]);
  				}
  				dojo.hash(moveTo);
  				return;
  			}
  		}
  		this._saveState.apply(this, arguments);
  		var toNode;
  		if(moveTo){
  			if(typeof(moveTo) == "string"){
  				// removes a leading hash mark (#) and params if exists
  				// ex. "#bar&myParam=0003" -> "bar"
  				moveTo.match(/^#?([^&]+)/);
  				toNode = RegExp.$1;
  			}else{
  				toNode = moveTo;
  			}
  		}else{
  			if(!this._dummyNode){
  				this._dummyNode = dojo.doc.createElement("DIV");
  				dojo.body().appendChild(this._dummyNode);
  			}
  			toNode = this._dummyNode;
  		}
  		var fromNode = this.domNode;
  		toNode = this.toNode = dojo.byId(toNode);
  		if(!toNode){ alert("dojox.mobile.View#performTransition: destination view not found: "+toNode); }
  		toNode.style.visibility = "hidden";
  		toNode.style.display = "";
  		this.onBeforeTransitionOut.apply(this, arguments);
  		var toWidget = dijit.byNode(toNode);
  		if(toWidget){
  			// perform view transition keeping the scroll position
  			if(this.keepScrollPos && !dijit.getEnclosingWidget(this.domNode.parentNode)){
  				var scrollTop = dojo.body().scrollTop || dojo.doc.documentElement.scrollTop || dojo.global.pageYOffset || 0;
  				if(dir == 1){
  					toNode.style.top = "0px";
  					if(scrollTop > 1){
  						fromNode.style.top = -scrollTop + "px";
  						if(dojo.config["mblHideAddressBar"] !== false){
  							setTimeout(function(){ // iPhone needs setTimeout
  								dojo.global.scrollTo(0, 1);
  							}, 0);
  						}
  					}
  				}else{
  					if(scrollTop > 1 || toNode.offsetTop !== 0){
  						var toTop = -toNode.offsetTop;
  						toNode.style.top = "0px";
  						fromNode.style.top = toTop - scrollTop + "px";
  						if(dojo.config["mblHideAddressBar"] !== false && toTop > 0){
  							setTimeout(function(){ // iPhone needs setTimeout
  								dojo.global.scrollTo(0, toTop + 1);
  							}, 0);
  						}
  					}
  				}
  			}else{
  				toNode.style.top = "0px";
  			}
  			toWidget.onBeforeTransitionIn.apply(toWidget, arguments);
  		}
  		toNode.style.display = "none";
  		toNode.style.visibility = "visible";
  		this._doTransition(fromNode, toNode, transition, dir);
  	},
  
  	_doTransition: function(fromNode, toNode, transition, dir){
  		var rev = (dir == -1) ? " reverse" : "";
  		toNode.style.display = "";
  		if(!transition || transition == "none"){
  			this.domNode.style.display = "none";
  			this.invokeCallback();
  		}else{
  			dojo.addClass(fromNode, transition + " out" + rev);
  			dojo.addClass(toNode, transition + " in" + rev);
  		}
  	},
  
  	onAnimationStart: function(e){
  	},
  
  	onAnimationEnd: function(e){
  		var isOut = false;
  		if(dojo.hasClass(this.domNode, "out")){
  			isOut = true;
  			this.domNode.style.display = "none";
  			dojo.forEach([this._transition,"in","out","reverse"], function(s){
  				dojo.removeClass(this.domNode, s);
  			}, this);
  		}
  		if(e.animationName.indexOf("shrink") === 0){
  			var li = e.target;
  			li.style.display = "none";
  			dojo.removeClass(li, "mblCloseContent");
  		}
  		if(isOut){
  			this.invokeCallback();
  		}
  		// this.domNode may be destroyed as a result of invoking the callback,
  		// so check for that before accessing it.
  		this.domNode && (this.domNode.className = "mblView");
  	},
  
  	invokeCallback: function(){
  		this.onAfterTransitionOut.apply(this, this._arguments);
  		var toWidget = dijit.byNode(this.toNode);
  		if(toWidget){
  			toWidget.onAfterTransitionIn.apply(toWidget, this._arguments);
  		}
  
  		dojox.mobile.currentView = toWidget;
  
  		var c = this._context, m = this._method;
  		if(!c && !m){ return; }
  		if(!m){
  			m = c;
  			c = null;
  		}
  		c = c || dojo.global;
  		if(typeof(m) == "string"){
  			c[m].apply(c, this._args);
  		}else{
  			m.apply(c, this._args);
  		}
  	},
  
  	getShowingView: function(){
  		// summary:
  		//		Find the currently showing view from my sibling views.
  		// description:
  		//		Note that dojox.mobile.currentView is the last shown view.
  		//		If the page consists of a splitter, there are multiple showing views.
  		var nodes = this.domNode.parentNode.childNodes;
  		for(var i = 0; i < nodes.length; i++){
  			if(dojo.hasClass(nodes[i], "mblView") && dojo.style(nodes[i], "display") != "none"){
  				return dijit.byNode(nodes[i]);
  			}
  		}
  	},
  
  	show: function(){
  		// summary:
  		//		Shows this view without a transition animation.
  		var fs = this.getShowingView().domNode.style; // from-style
  		var ts = this.domNode.style; // to-style
  		fs.display = "none";
  		ts.display = "";
  		dojox.mobile.currentView = this;
  	},
  
  	addChild: function(widget){
  		this.containerNode.appendChild(widget.domNode);
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.Heading",
  	dijit._WidgetBase,
  {
  	back: "",
  	href: "",
  	moveTo: "",
  	transition: "slide",
  	label: "",
  	iconBase: "",
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("H1");
  		this.domNode.className = "mblHeading";
  		this._view = dijit.getEnclosingWidget(this.domNode.parentNode); // parentNode is null if created programmatically
  		if(this.label){
  			this.domNode.appendChild(document.createTextNode(this.label));
  		}else{
  			this.label = "";
  			dojo.forEach(this.domNode.childNodes, function(n){
  				if(n.nodeType == 3){ this.label += n.nodeValue; }
  			}, this);
  			this.label = dojo.trim(this.label);
  		}
  		if(this.back){
  			var btn = dojo.create("DIV", {className:"mblArrowButton"}, this.domNode, "first");
  			var head = dojo.create("DIV", {className:"mblArrowButtonHead"}, btn);
  			var body = dojo.create("DIV", {className:"mblArrowButtonBody mblArrowButtonText"}, btn);
  
  			this._body = body;
  			this._head = head;
  			this._btn = btn;
  			body.innerHTML = this.back;
  			this.connect(body, "onclick", "onClick");
  			var neck = dojo.create("DIV", {className:"mblArrowButtonNeck"}, btn);
  			btn.style.width = body.offsetWidth + head.offsetWidth + "px";
  			this.setLabel(this.label);
  		}
  	},
  
  	startup: function(){
  		if(this._btn){
  			this._btn.style.width = this._body.offsetWidth + this._head.offsetWidth + "px";
  		}
  	},
  
  	onClick: function(e){
  		var h1 = this.domNode;
  		dojo.addClass(h1, "mblArrowButtonSelected");
  		setTimeout(function(){
  			dojo.removeClass(h1, "mblArrowButtonSelected");
  		}, 1000);
  		this.goTo(this.moveTo, this.href);
  	},
  
  	setLabel: function(label){
  		if(label != this.label){
  			this.label = label;
  			this.domNode.firstChild.nodeValue = label;
  		}
  	},
  
  	goTo: function(moveTo, href){
  		if(!this._view){
  			this._view = dijit.byNode(this.domNode.parentNode);
  		}
  		if(!this._view){ return; }
  		if(href){
  			this._view.performTransition(null, -1, this.transition, this, function(){location.href = href;});
  		}else{
  			if(dojox.mobile.app && dojox.mobile.app.STAGE_CONTROLLER_ACTIVE){
  				// If in a full mobile app, then use its mechanisms to move back a scene
  				dojo.publish("/dojox/mobile/app/goback");
  			}
  			else{
  				this._view.performTransition(moveTo, -1, this.transition);
  			}
  
  		}
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.RoundRect",
  	dijit._WidgetBase,
  {
  	shadow: false,
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  		this.domNode.className = this.shadow ? "mblRoundRect mblShadow" : "mblRoundRect";
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.RoundRectCategory",
  	dijit._WidgetBase,
  {
  	label: "",
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("H2");
  		this.domNode.className = "mblRoundRectCategory";
  		if(this.label){
  			this.domNode.innerHTML = this.label;
  		}else{
  			this.label = this.domNode.innerHTML;
  		}
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.EdgeToEdgeCategory",
  	dojox.mobile.RoundRectCategory,
  {
  	buildRendering: function(){
  		this.inherited(arguments);
  		this.domNode.className = "mblEdgeToEdgeCategory";
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.RoundRectList",
  	dijit._WidgetBase,
  {
  	transition: "slide",
  	iconBase: "",
  	iconPos: "",
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("UL");
  		this.domNode.className = "mblRoundRectList";
  	},
  
  	addChild: function(widget){
  		this.containerNode.appendChild(widget.domNode);
  		widget.inheritParams();
  		widget.setIcon();
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.EdgeToEdgeList",
  	dojox.mobile.RoundRectList,
  {
  	stateful: false, // keep the selection state or not
  	buildRendering: function(){
  		this.inherited(arguments);
  		this.domNode.className = "mblEdgeToEdgeList";
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.AbstractItem",
  	dijit._WidgetBase,
  {
  	icon: "",
  	iconPos: "", // top,left,width,height (ex. "0,0,29,29")
  	href: "",
  	hrefTarget: "",
  	moveTo: "",
  	scene: "",
  	clickable: false,
  	url: "",
  	urlTarget: "", // node id under which a new view is created
  	transition: "",
  	transitionDir: 1,
  	callback: null,
  	sync: true,
  	label: "",
  	toggle: false,
  	_duration: 800, // duration of selection, milliseconds
  
  	inheritParams: function(){
  		var parent = this.getParentWidget();
  		if(parent){
  			if(!this.transition){ this.transition = parent.transition; }
  			if(!this.icon){ this.icon = parent.iconBase; }
  			if(!this.iconPos){ this.iconPos = parent.iconPos; }
  		}
  	},
  
  	findCurrentView: function(moveTo){
  		var w;
  		if(moveTo){
  			w = dijit.byId(moveTo);
  			if(w){ return w.getShowingView(); }
  		}
  		var n = this.domNode.parentNode;
  		while(true){
  			w = dijit.getEnclosingWidget(n);
  			if(!w){ return null; }
  			if(w.performTransition){ break; }
  			n = w.domNode.parentNode;
  		}
  		return w;
  	},
  
  	transitionTo: function(moveTo, href, url, scene){
  		var w = this.findCurrentView(moveTo); // the current view widget
  		if(!w || moveTo && w === dijit.byId(moveTo)){ return; }
  		if(href){
  			if(this.hrefTarget){
  				dojox.mobile.openWindow(this.href, this.hrefTarget);
  			}else{
  				w.performTransition(null, this.transitionDir, this.transition, this, function(){location.href = href;});
  			}
  			return;
  		} else if(scene){
  			dojo.publish("/dojox/mobile/app/pushScene", [scene]);
  			return;
  		}
  		if(url){
  			var id;
  			if(dojox.mobile._viewMap && dojox.mobile._viewMap[url]){
  				// external view has already been loaded
  				id = dojox.mobile._viewMap[url];
  			}else{
  				// get the specified external view and append it to the <body>
  				var text = this._text;
  				if(!text){
  					if(this.sync){
  						text = dojo.trim(dojo._getText(url));
  					}else{
  						dojo["require"]("dojo._base.xhr");
  						var prog = dojox.mobile.ProgressIndicator.getInstance();
  						dojo.body().appendChild(prog.domNode);
  						prog.start();
  						var xhr = dojo.xhrGet({
  							url: url,
  							handleAs: "text"
  						});
  						xhr.addCallback(dojo.hitch(this, function(response, ioArgs){
  							prog.stop();
  							if(response){
  								this._text = response;
  								this.transitionTo(moveTo, href, url, scene);
  							}
  						}));
  						xhr.addErrback(function(error){
  							prog.stop();
  							alert("Failed to load "+url+"\n"+(error.description||error));
  						});
  						return;
  					}
  				}
  				this._text = null;
  				id = this._parse(text);
  				if(!dojox.mobile._viewMap){
  					dojox.mobile._viewMap = [];
  				}
  				dojox.mobile._viewMap[url] = id;
  			}
  			moveTo = id;
  			w = this.findCurrentView(moveTo) || w; // the current view widget
  		}
  		w.performTransition(moveTo, this.transitionDir, this.transition, this.callback && this, this.callback);
  	},
  
  	_parse: function(text){
  		var container = dojo.create("DIV");
  		var view;
  		var id = this.urlTarget;
  		var target = dijit.byId(id) && dijit.byId(id).containerNode ||
  			dojo.byId(id) ||
  			dojox.mobile.currentView && dojox.mobile.currentView.domNode.parentNode ||
  			dojo.body();
  		if(text.charAt(0) == "<"){ // html markup
  			container.innerHTML = text;
  			view = container.firstChild; // <div dojoType="dojox.mobile.View">
  			if(!view && view.nodeType != 1){
  				alert("dojox.mobile.AbstractItem#transitionTo: invalid view content");
  				return;
  			}
  			view.setAttribute("_started", "true"); // to avoid startup() is called
  			view.style.visibility = "hidden";
  			target.appendChild(container);
  			(dojox.mobile.parser || dojo.parser).parse(container);
  			target.appendChild(target.removeChild(container).firstChild); // reparent
  		}else if(text.charAt(0) == "{"){ // json
  			target.appendChild(container);
  			this._ws = [];
  			view = this._instantiate(eval('('+text+')'), container);
  			for(var i = 0; i < this._ws.length; i++){
  				var w = this._ws[i];
  				w.startup && !w._started && (!w.getParent || !w.getParent()) && w.startup();
  			}
  			this._ws = null;
  		}
  		view.style.display = "none";
  		view.style.visibility = "visible";
  		var id = view.id;
  		return dojo.hash ? "#" + id : id;
  	},
  
  	_instantiate: function(/*Object*/obj, /*DomNode*/node, /*Widget*/parent){
  		var widget;
  		for(var key in obj){
  			if(key.charAt(0) == "@"){ continue; }
  			var cls = dojo.getObject(key);
  			if(!cls){ continue; }
  			var params = {};
  			var proto = cls.prototype;
  			var objs = dojo.isArray(obj[key]) ? obj[key] : [obj[key]];
  			for(var i = 0; i < objs.length; i++){
  				for(var prop in objs[i]){
  					if(prop.charAt(0) == "@"){
  						var val = objs[i][prop];
  						prop = prop.substring(1);
  						if(typeof proto[prop] == "string"){
  							params[prop] = val;
  						}else if(typeof proto[prop] == "number"){
  							params[prop] = val - 0;
  						}else if(typeof proto[prop] == "boolean"){
  							params[prop] = (val != "false");
  						}else if(typeof proto[prop] == "object"){
  							params[prop] = eval("(" + val + ")");
  						}
  					}
  				}
  				widget = new cls(params, node);
  				if(!node){ // not to call View's startup()
  					this._ws.push(widget);
  				}
  				if(parent && parent.addChild){
  					parent.addChild(widget);
  				}
  				this._instantiate(objs[i], null, widget);
  			}
  		}
  		return widget && widget.domNode;
  	},
  
  	createDomButton: function(/*DomNode*/refNode, /*DomNode?*/toNode){
  		var s = refNode.className;
  		if(s.match(/mblDomButton\w+_(\d+)/)){
  			var nDiv = RegExp.$1 - 0;
  			for(var i = 0, p = (toNode||refNode); i < nDiv; i++){
  				p = dojo.create("DIV", null, p);
  			}
  		}
  	},
  
  	select: function(/*Boolean?*/deselect){
  		// subclass must implement
  	},
  
  	defaultClickAction: function(){
  		if(this.toggle){
  			this.select(this.selected);
  		}else if(!this.selected){
  			this.select();
  			if(!this.selectOne){
  				var _this = this;
  				setTimeout(function(){
  					_this.select(true);
  				}, this._duration);
  			}
  			if(this.moveTo || this.href || this.url || this.scene){
  				this.transitionTo(this.moveTo, this.href, this.url, this.scene);
  			}
  		}
  	},
  
  	getParentWidget: function(){
  		var ref = this.srcNodeRef || this.domNode;
  		return ref && ref.parentNode ? dijit.getEnclosingWidget(ref.parentNode) : null;
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.ListItem",
  	dojox.mobile.AbstractItem,
  {
  	rightText: "",
  	btnClass: "",
  	anchorLabel: false,
  	noArrow: false,
  	selected: false,
  
  	buildRendering: function(){
  		this.inheritParams();
  		var a = this.anchorNode = dojo.create("A");
  		a.className = "mblListItemAnchor";
  		var box = dojo.create("DIV");
  		box.className = "mblListItemTextBox";
  		if(this.anchorLabel){
  			box.style.cursor = "pointer";
  		}
  		var r = this.srcNodeRef;
  		if(r){
  			for(var i = 0, len = r.childNodes.length; i < len; i++){
  				box.appendChild(r.removeChild(r.firstChild));
  			}
  		}
  		if(this.label){
  			box.appendChild(dojo.doc.createTextNode(this.label));
  		}
  		a.appendChild(box);
  		if(this.rightText){
  			this._setRightTextAttr(this.rightText);
  		}
  
  		if(this.moveTo || this.href || this.url || this.clickable){
  			var parent = this.getParentWidget();
  			if(!this.noArrow && !(parent && parent.stateful)){
  				var arrow = dojo.create("DIV");
  				arrow.className = "mblArrow";
  				a.appendChild(arrow);
  			}
  			this.connect(a, "onclick", "onClick");
  		}else if(this.btnClass){
  			var div = this.btnNode = dojo.create("DIV");
  			div.className = this.btnClass+" mblRightButton";
  			div.appendChild(dojo.create("DIV"));
  			div.appendChild(dojo.create("P"));
  
  			var dummyDiv = dojo.create("DIV");
  			dummyDiv.className = "mblRightButtonContainer";
  			dummyDiv.appendChild(div);
  			a.appendChild(dummyDiv);
  			dojo.addClass(a, "mblListItemAnchorHasRightButton");
  			setTimeout(function(){
  				dummyDiv.style.width = div.offsetWidth + "px";
  				dummyDiv.style.height = div.offsetHeight + "px";
  				if(dojo.isIE){
  					// IE seems to ignore the height of LI without this..
  					a.parentNode.style.height = a.parentNode.offsetHeight + "px";
  				}
  			}, 0);
  		}
  		if(this.anchorLabel){
  			box.style.display = "inline"; // to narrow the text region
  		}
  		var li = this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("LI");
  		li.className = "mblListItem" + (this.selected ? " mblItemSelected" : "");
  		li.appendChild(a);
  		this.setIcon();
  	},
  
  	setIcon: function(){
  		if(this.iconNode){ return; }
  		var a = this.anchorNode;
  		if(this.icon && this.icon != "none"){
  			var img = this.iconNode = dojo.create("IMG");
  			img.className = "mblListItemIcon";
  			img.src = this.icon;
  			this.domNode.insertBefore(img, a);
  			dojox.mobile.setupIcon(this.iconNode, this.iconPos);
  			dojo.removeClass(a, "mblListItemAnchorNoIcon");
  		}else{
  			dojo.addClass(a, "mblListItemAnchorNoIcon");
  		}
  	},
  
  	onClick: function(e){
  		var a = e.currentTarget;
  		var li = a.parentNode;
  		if(dojo.hasClass(li, "mblItemSelected")){ return; } // already selected
  		if(this.anchorLabel){
  			for(var p = e.target; p.tagName != "LI"; p = p.parentNode){
  				if(p.className == "mblListItemTextBox"){
  					dojo.addClass(p, "mblListItemTextBoxSelected");
  					setTimeout(function(){
  						dojo.removeClass(p, "mblListItemTextBoxSelected");
  					}, 1000);
  					this.onAnchorLabelClicked(e);
  					return;
  				}
  			}
  		}
  		if(this.getParentWidget().stateful){
  			for(var i = 0, c = li.parentNode.childNodes; i < c.length; i++){
  				dojo.removeClass(c[i], "mblItemSelected");
  			}
  		}else{
  			setTimeout(function(){
  				dojo.removeClass(li, "mblItemSelected");
  			}, 1000);
  		}
  		dojo.addClass(li, "mblItemSelected");
  		this.transitionTo(this.moveTo, this.href, this.url, this.scene);
  	},
  
  	onAnchorLabelClicked: function(e){
  	},
  
  	_setRightTextAttr: function(/*String*/text){
  		this.rightText = text;
  		if(!this._rightTextNode){
  			this._rightTextNode = dojo.create("DIV", {className:"mblRightText"}, this.anchorNode);
  		}
  		this._rightTextNode.innerHTML = text;
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.Switch",
  	dijit._WidgetBase,
  {
  	value: "on",
  	leftLabel: "ON",
  	rightLabel: "OFF",
  	_width: 53,
  
  	buildRendering: function(){
  		this.domNode = this.srcNodeRef || dojo.doc.createElement("DIV");
  		this.domNode.className = "mblSwitch";
  		this.domNode.innerHTML =
  			  '<div class="mblSwitchInner">'
  			+	'<div class="mblSwitchBg mblSwitchBgLeft">'
  			+		'<div class="mblSwitchText mblSwitchTextLeft">'+this.leftLabel+'</div>'
  			+	'</div>'
  			+	'<div class="mblSwitchBg mblSwitchBgRight">'
  			+		'<div class="mblSwitchText mblSwitchTextRight">'+this.rightLabel+'</div>'
  			+	'</div>'
  			+	'<div class="mblSwitchKnob"></div>'
  			+ '</div>';
  		var n = this.inner = this.domNode.firstChild;
  		this.left = n.childNodes[0];
  		this.right = n.childNodes[1];
  		this.knob = n.childNodes[2];
  
  		dojo.addClass(this.domNode, (this.value == "on") ? "mblSwitchOn" : "mblSwitchOff");
  		this[this.value == "off" ? "left" : "right"].style.display = "none";
  	},
  
  	postCreate: function(){
  		this.connect(this.knob, "onclick", "onClick");
  		this.connect(this.knob, "touchstart", "onTouchStart");
  		this.connect(this.knob, "mousedown", "onTouchStart");
  	},
  
  	_changeState: function(/*String*/state){
  		this.inner.style.left = "";
  		dojo.addClass(this.domNode, "mblSwitchAnimation");
  		dojo.removeClass(this.domNode, (state == "on") ? "mblSwitchOff" : "mblSwitchOn");
  		dojo.addClass(this.domNode, (state == "on") ? "mblSwitchOn" : "mblSwitchOff");
  
  		var _this = this;
  		setTimeout(function(){
  			_this[state == "off" ? "left" : "right"].style.display = "none";
  			dojo.removeClass(_this.domNode, "mblSwitchAnimation");
  		}, 300);
  	},
  
  	onClick: function(e){
  		if(this._moved){ return; }
  		this.value = (this.value == "on") ? "off" : "on";
  		this._changeState(this.value);
  		this.onStateChanged(this.value);
  	},
  
  	onTouchStart: function(e){
  		this._moved = false;
  		this.innerStartX = this.inner.offsetLeft;
  		if(e.targetTouches){
  			this.touchStartX = e.targetTouches[0].clientX;
  			this._conn1 = dojo.connect(this.inner, "touchmove", this, "onTouchMove");
  			this._conn2 = dojo.connect(this.inner, "touchend", this, "onTouchEnd");
  		}
  		this.left.style.display = "block";
  		this.right.style.display = "block";
  		dojo.stopEvent(e);
  	},
  
  	onTouchMove: function(e){
  		e.preventDefault();
  		var dx;
  		if(e.targetTouches){
  			if(e.targetTouches.length != 1){ return false; }
  			dx = e.targetTouches[0].clientX - this.touchStartX;
  		}else{
  			dx = e.clientX - this.touchStartX;
  		}
  		var pos = this.innerStartX + dx;
  		var d = 10;
  		if(pos <= -(this._width-d)){ pos = -this._width; }
  		if(pos >= -d){ pos = 0; }
  		this.inner.style.left = pos + "px";
  		this._moved = true;
  	},
  
  	onTouchEnd: function(e){
  		dojo.disconnect(this._conn1);
  		dojo.disconnect(this._conn2);
  		if(this.innerStartX == this.inner.offsetLeft){
  			if(dojo.isWebKit){
  				var ev = dojo.doc.createEvent("MouseEvents");
  				ev.initEvent("click", true, true);
  				this.knob.dispatchEvent(ev);
  			}
  			return;
  		}
  		var newState = (this.inner.offsetLeft < -(this._width/2)) ? "off" : "on";
  		this._changeState(newState);
  		if(newState != this.value){
  			this.value = newState;
  			this.onStateChanged(this.value);
  		}
  	},
  
  	onStateChanged: function(/*String*/newState){
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.Button",
  	dijit._WidgetBase,
  {
  	btnClass: "mblBlueButton",
  	duration: 1000, // duration of selection, milliseconds
  
  	label: null,
  
  	buildRendering: function(){
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("BUTTON");
  		this.domNode.className = "mblButton "+this.btnClass;
  
  		if(this.label){
  			this.domNode.innerHTML = this.label;
  		}
  
  		this.connect(this.domNode, "onclick", "onClick");
  	},
  
  	onClick: function(e){
  		var button = this.domNode;
  		var c = "mblButtonSelected "+this.btnClass+"Selected";
  		dojo.addClass(button, c);
  		setTimeout(function(){
  			dojo.removeClass(button, c);
  		}, this.duration);
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.ToolBarButton",
  	dojox.mobile.AbstractItem,
  {
  	selected: false,
  	_defaultColor: "mblColorDefault",
  	_selColor: "mblColorDefaultSel",
  
  	buildRendering: function(){
  		this.inheritParams();
  		this.domNode = this.containerNode = this.srcNodeRef || dojo.doc.createElement("div");
  		dojo.addClass(this.domNode, "mblToolbarButton mblArrowButtonText");
  		var color;
  		if(this.selected){
  			color = this._selColor;
  		}else if(this.domNode.className.indexOf("mblColor") == -1){
  			color = this._defaultColor;
  		}
  		dojo.addClass(this.domNode, color);
  
  		if(this.label){
  			this.domNode.innerHTML = this.label;
  		}else{
  			this.label = this.domNode.innerHTML;
  		}
  
  		if(this.icon && this.icon != "none"){
  			var img;
  			if(this.iconPos){
  				var iconDiv = dojo.create("DIV", null, this.domNode);
  				img = dojo.create("IMG", null, iconDiv);
  				img.style.position = "absolute";
  				var arr = this.iconPos.split(/[ ,]/);
  				dojo.style(iconDiv, {
  					position: "relative",
  					width: arr[2] + "px",
  					height: arr[3] + "px"
  				});
  			}else{
  				img = dojo.create("IMG", null, this.domNode);
  			}
  			img.src = this.icon;
  			dojox.mobile.setupIcon(img, this.iconPos);
  			this.iconNode = img;
  		}
  		this.createDomButton(this.domNode);
  		this.connect(this.domNode, "onclick", "onClick");
  	},
  
  	select: function(/*Boolean?*/deselect){
  		dojo.toggleClass(this.domNode, this._selColor, !deselect);
  		this.selected = !deselect;
  	},
  
  	onClick: function(e){
  		this.defaultClickAction();
  	}
  });
  
  dojo.declare(
  	"dojox.mobile.ProgressIndicator",
  	null,
  {
  	interval: 100, // milliseconds
  	colors: [
  		"#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0",
  		"#C0C0C0", "#C0C0C0", "#B8B9B8", "#AEAFAE",
  		"#A4A5A4", "#9A9A9A", "#8E8E8E", "#838383"
  	],
  
  	_bars: [],
  
  	constructor: function(){
  		this.domNode = dojo.create("DIV");
  		this.domNode.className = "mblProgContainer";
  		for(var i = 0; i < 12; i++){
  			var div = dojo.create("DIV");
  			div.className = "mblProg mblProg"+i;
  			this.domNode.appendChild(div);
  			this._bars.push(div);
  		}
  	},
  
  	start: function(){
  		var cntr = 0;
  		var _this = this;
  		this.timer = setInterval(function(){
  			cntr--;
  			cntr = cntr < 0 ? 11 : cntr;
  			var c = _this.colors;
  			for(var i = 0; i < 12; i++){
  				var idx = (cntr + i) % 12;
  				_this._bars[i].style.backgroundColor = c[idx];
  			}
  		}, this.interval);
  	},
  
  	stop: function(){
  		if(this.timer){
  			clearInterval(this.timer);
  		}
  		this.timer = null;
  		if(this.domNode.parentNode){
  			this.domNode.parentNode.removeChild(this.domNode);
  		}
  	}
  });
  dojox.mobile.ProgressIndicator._instance = null;
  dojox.mobile.ProgressIndicator.getInstance = function(){
  	if(!dojox.mobile.ProgressIndicator._instance){
  		dojox.mobile.ProgressIndicator._instance = new dojox.mobile.ProgressIndicator();
  	}
  	return dojox.mobile.ProgressIndicator._instance;
  };
  
  dojox.mobile.addClass = function(){
  	// summary:
  	//		Adds a theme class name to <body>.
  	// description:
  	//		Finds the currently applied theme name, such as 'iphone' or 'android'
  	//		from link elements, and adds it as a class name for the body element.
  	var elems = document.getElementsByTagName("link");
  	for(var i = 0, len = elems.length; i < len; i++){
  		if(elems[i].href.match(/dojox\/mobile\/themes\/(\w+)\//)){
  			dojox.mobile.theme = RegExp.$1;
  			dojo.addClass(dojo.body(), dojox.mobile.theme);
  			break;
  		}
  	}
  };
  
  dojox.mobile.setupIcon = function(/*DomNode*/iconNode, /*String*/iconPos){
  	if(iconNode && iconPos){
  		var arr = dojo.map(iconPos.split(/[ ,]/),
  								function(item){ return item - 0; });
  		var t = arr[0]; // top
  		var r = arr[1] + arr[2]; // right
  		var b = arr[0] + arr[3]; // bottom
  		var l = arr[1]; // left
  		iconNode.style.clip = "rect("+t+"px "+r+"px "+b+"px "+l+"px)";
  		iconNode.style.top = dojo.style(iconNode, "top") - t + "px";
  		iconNode.style.left = dojo.style(iconNode.parentNode, "paddingLeft") - l + "px";
  	}
  };
  
  dojox.mobile.hideAddressBar = function(){
  	dojo.body().style.minHeight = "1000px"; // to ensure enough height for scrollTo to work
  	setTimeout(function(){ scrollTo(0, 1); }, 100);
  	setTimeout(function(){ scrollTo(0, 1); }, 400);
  	setTimeout(function(){
  		scrollTo(0, 1);
  		// re-define the min-height with the actual height
  		dojo.body().style.minHeight = (dojo.global.innerHeight||dojo.doc.documentElement.clientHeight) + "px";
  	}, 1000);
  };
  
  dojox.mobile.openWindow = function(url, target){
  	dojo.global.open(url, target || "_blank");
  };
  
  dojo._loaders.unshift(function(){
  	// avoid use of dojo.query
  	/*
  	var list = dojo.query('[lazy=true] [dojoType]', null);
  	list.forEach(function(node, index, nodeList){
  		node.setAttribute("__dojoType", node.getAttribute("dojoType"));
  		node.removeAttribute("dojoType");
  	});
  	*/
  
  	var nodes = dojo.body().getElementsByTagName("*");
  	var i, len, s;
  	len = nodes.length;
  	for(i = 0; i < len; i++){
  		s = nodes[i].getAttribute("dojoType");
  		if(s){
  			if(nodes[i].parentNode.getAttribute("lazy") == "true"){
  				nodes[i].setAttribute("__dojoType", s);
  				nodes[i].removeAttribute("dojoType");
  			}
  		}
  	}
  });
  
  dojo.addOnLoad(function(){
  	dojox.mobile.addClass();
  	if(dojo.config["mblApplyPageStyles"] !== false){
  		dojo.addClass(dojo.doc.documentElement, "mobile");
  	}
  
  	//	You can disable hiding the address bar with the following djConfig.
  	//	var djConfig = { mblHideAddressBar: false };
  	if(dojo.config["mblHideAddressBar"] !== false){
  		dojox.mobile.hideAddressBar();
  		if(dojo.config["mblAlwaysHideAddressBar"] == true){
  			if(dojo.global.onorientationchange !== undefined){
  				dojo.connect(dojo.global, "onorientationchange", dojox.mobile.hideAddressBar);
  			}else{
  				dojo.connect(dojo.global, "onresize", dojox.mobile.hideAddressBar);
  			}
  		}
  	}
  
  	// avoid use of dojo.query
  	/*
  	var list = dojo.query('[__dojoType]', null);
  	list.forEach(function(node, index, nodeList){
  		node.setAttribute("dojoType", node.getAttribute("__dojoType"));
  		node.removeAttribute("__dojoType");
  	});
  	*/
  
  	var nodes = dojo.body().getElementsByTagName("*");
  	var i, len = nodes.length, s;
  	for(i = 0; i < len; i++){
  		s = nodes[i].getAttribute("__dojoType");
  		if(s){
  			nodes[i].setAttribute("dojoType", s);
  			nodes[i].removeAttribute("__dojoType");
  		}
  	}
  
  	if(dojo.hash){
  		// find widgets under root recursively
  		var findWidgets = function(root){
  			var arr;
  			arr = dijit.findWidgets(root);
  			var widgets = arr;
  			for(var i = 0; i < widgets.length; i++){
  				arr = arr.concat(findWidgets(widgets[i].containerNode));
  			}
  			return arr;
  		};
  		dojo.subscribe("/dojo/hashchange", null, function(value){
  			var view = dojox.mobile.currentView;
  			if(!view){ return; }
  			var params = dojox.mobile._params;
  			if(!params){ // browser back/forward button was pressed
  				var moveTo = value ? value : dojox.mobile._defaultView.id;
  				var widgets = findWidgets(view.domNode);
  				var dir = 1, transition = "slide";
  				for(i = 0; i < widgets.length; i++){
  					var w = widgets[i];
  					if("#"+moveTo == w.moveTo){
  						// found a widget that has the given moveTo
  						transition = w.transition;
  						dir = (w instanceof dojox.mobile.Heading) ? -1 : 1;
  						break;
  					}
  				}
  				params = [ moveTo, dir, transition ];
  			}
  			view.performTransition.apply(view, params);
  			dojox.mobile._params = null;
  		});
  	}
  
  	dojo.body().style.visibility = "visible";
  });
  
  dijit.getEnclosingWidget = function(node){
  	while(node && node.tagName !== "BODY"){
  		if(node.getAttribute && node.getAttribute("widgetId")){
  			return dijit.registry.byId(node.getAttribute("widgetId"));
  		}
  		node = node._parentNode || node.parentNode;
  	}
  	return null;
  };
  