text: define([
  	"dojo/_base/declare",
  	"dojo/_base/event",
  	"dojo/_base/lang",
  	"dojo/sniff",
  	"dojo/_base/window",
  	"dojo/dom-class",
  	"dojo/dom-construct",
  	"dojo/dom-geometry",
  	"dojo/dom-style",
  	"./_ItemBase",
  	"./Badge",
  	"./TransitionEvent",
  	"./iconUtils",
  	"./lazyLoadUtils",
  	"./viewRegistry",
  	"./_css3",
  	"dojo/has!dojo-bidi?dojox/mobile/bidi/IconItem"
  ], function(declare, event, lang, has, win, domClass, domConstruct, domGeometry, domStyle, ItemBase, Badge, TransitionEvent, iconUtils, lazyLoadUtils, viewRegistry, css3, BidiIconItem){
  
  	// module:
  	//		dojox/mobile/IconItem
  
  	var IconItem = declare(has("dojo-bidi") ? "dojox.mobile.NonBidiIconItem" : "dojox.mobile.IconItem", ItemBase, {
  		// summary:
  		//		An icon item widget.
  		// description:
  		//		IconItem represents an item that has an application component
  		//		and its icon image. You can tap the icon to open the
  		//		corresponding application component. You can also use the icon
  		//		to move to a different view by specifying either of the moveTo,
  		//		href or url parameters.
  
  		// lazy: String
  		//		If true, the content of the widget, which includes dojo markup,
  		//		is instantiated lazily. That is, only when the widget is opened
  		//		by the user, the required modules are loaded and the content
  		//		widgets are instantiated.
  		//		This option works both in the sync and async loader mode.
  		lazy: false,
  
  		// requires: String
  		//		Comma-separated required module names to be lazily loaded. This
  		//		property is effective only when lazy=true. All the modules
  		//		specified with data-dojo-type and their depending modules are
  		//		automatically loaded by the IconItem when it is opened.
  		//		However, if you need other extra modules to be loaded, use this parameter.
  		//		This option works both in the sync and async loader mode.
  		requires: "",
  
  		// timeout: String
  		//		Duration of highlight in seconds.
  		timeout: 10,
  
  		// content: String
  		//		An HTML fragment to embed as icon content.
  		content: "",
  
  		// badge: String
  		//		A text to show in a badge (ex. "55").
  		badge: "",
  
  		// badgeClass: String
  		//		A class name of a DOM button for a badge.
  		badgeClass: "mblDomButtonRedBadge",
  
  		// deletable: Boolean
  		//		If true, you can delete this IconItem by clicking on the delete
  		//		icon during edit mode.
  		//		If false, the delete icon is not displayed during edit mode so
  		//		that it cannot be deleted.
  		deletable: true,
  
  		// deleteIcon: String
  		//		A delete icon to display at the top-left corner of the item
  		//		during edit mode. The value can be either a path for an image
  		//		file or a class name of a DOM button.
  		deleteIcon: "",
  
  		// tag: String
  		//		A name of the HTML tag to create as domNode.
  		tag: "li",
  
  		/* internal properties */	
  		// Note these are overrides for similar properties defined in _ItemBase.
  		paramsToInherit: "transition,icon,deleteIcon,badgeClass,deleteIconTitle,deleteIconRole",
  		baseClass: "mblIconItem",
  		_selStartMethod: "touch",
  		_selEndMethod: "none",
  
  		destroy: function(){
  			if(this.badgeObj){
  				delete this.badgeObj;
  			}
  			this.inherited(arguments);
  		},
  
  		buildRendering: function(){
  			this.domNode = this.srcNodeRef || domConstruct.create(this.tag);
  
  			if(this.srcNodeRef){
  				// reparent
  				this._tmpNode = domConstruct.create("div");
  				for(var i = 0, len = this.srcNodeRef.childNodes.length; i < len; i++){
  					this._tmpNode.appendChild(this.srcNodeRef.firstChild);
  				}
  			}
  
  			this.iconDivNode = domConstruct.create("div", {className:"mblIconArea"}, this.domNode);
  			this.iconParentNode = domConstruct.create("div", {className:"mblIconAreaInner"}, this.iconDivNode);
  			this.labelNode = domConstruct.create("span", {className:"mblIconAreaTitle"}, this.iconDivNode);
  
  			this.inherited(arguments);
  		},
  
  		startup: function(){
  			if(this._started){ return; }
  
  			var p = this.getParent();
  			require([p.iconItemPaneClass], lang.hitch(this, function(module){
  				var w = this.paneWidget = new module(p.iconItemPaneProps);
  				this.containerNode = w.containerNode;
  				if(this._tmpNode){
  					// reparent
  					for(var i = 0, len = this._tmpNode.childNodes.length; i < len; i++){
  						w.containerNode.appendChild(this._tmpNode.firstChild);
  					}
  					this._tmpNode = null;
  				}
  				p.paneContainerWidget.addChild(w, this.getIndexInParent());
  				w.set("label", this.label);
  				this._clickCloseHandle = this.connect(w.closeIconNode, "onclick", "_closeIconClicked");
  				this._keydownCloseHandle = this.connect(w.closeIconNode, "onkeydown", "_closeIconClicked"); // for desktop browsers
  			}));
  
  			this.inherited(arguments);
  			if(!this._isOnLine){
  				this._isOnLine = true;
  				// retry applying the attribute for which the custom setter delays the actual 
  				// work until _isOnLine is true. 
  				this.set("icon", this._pendingIcon !== undefined ? this._pendingIcon : this.icon);
  				// Not needed anymore (this code executes only once per life cycle):
  				delete this._pendingIcon; 
  			}
  			if(!this.icon && p.defaultIcon){
  				this.set("icon", p.defaultIcon);
  			}
  
  			this._dragstartHandle = this.connect(this.domNode, "ondragstart", event.stop);
  			this.connect(this.domNode, "onkeydown", "_onClick"); // for desktop browsers
  		},
  
  		highlight: function(/*Number?*/timeout){
  			// summary:
  			//		Shakes the icon 10 seconds.
  			domClass.add(this.iconDivNode, "mblVibrate");
  			timeout = (timeout !== undefined) ? timeout : this.timeout;
  			if(timeout > 0){
  				var _this = this;
  				_this.defer(function(){
  					_this.unhighlight();
  				}, timeout*1000);
  			}
  		},
  
  		unhighlight: function(){
  			// summary:
  			//		Stops shaking the icon.
  			if(!has("ie") && has("trident") === 7){
  				// Workaround on IE11: if just removing the style, the icon continues to shake
  				domStyle.set(this.iconDivNode, "animation-name", "");
  			}
  			domClass.remove(this.iconDivNode, "mblVibrate");
  		},
  
  		isOpen: function(e){
  			// summary:
  			//		Returns true if the icon is open.
  			return this.paneWidget.isOpen();
  		},
  
  		_onClick: function(e){
  			// summary:
  			//		Internal handler for click events.
  			// tags:
  			//		private
  			if(this.getParent().isEditing || e && e.type === "keydown" && e.keyCode !== 13){ return; }
  			if(this.onClick(e) === false){ return; } // user's click action
  			this.defaultClickAction(e);
  		},
  
  		onClick: function(/*Event*/ /*===== e =====*/){
  			// summary:
  			//		User-defined function to handle clicks.
  			// tags:
  			//		callback
  		},
  
  		_onNewWindowOpened: function(e){
  			// Override from _ItemBase
  			this.set("selected", false);
  		},
  
  		_prepareForTransition: function(e, transOpts){
  			// Override from _ItemBase
  			if(transOpts){
  				this.defer(function(d){
  					this.set("selected", false);
  				}, 1500);
  				return true;
  			}else{
  				if(this.getParent().transition === "below" && this.isOpen()){
  					this.close();
  				}else{
  					this.open(e);
  				}
  				return false;
  			}
  		},
  
  		_closeIconClicked: function(e){
  			// summary:
  			//		Internal handler for click events.
  			// tags:
  			//		private
  			if(e){
  				if(e.type === "keydown" && e.keyCode !== 13){ return; }
  				if(this.closeIconClicked(e) === false){ return; } // user's click action
  				this.defer(function(d){ this._closeIconClicked(); });
  				return;
  			}
  			this.close();
  		},
  
  		closeIconClicked: function(/*Event*/ /*===== e =====*/){
  			// summary:
  			//		User-defined function to handle clicks for the close icon.
  			// tags:
  			//		callback
  		},
  
  		open: function(e){
  			// summary:
  			//		Opens the icon content, or makes a transition.
  			var parent = this.getParent(); // IconContainer
  			if(this.transition === "below"){
  				if(parent.single){
  					parent.closeAll();
  				}
  				this._open_1();
  			}else{
  				parent._opening = this;
  				if(parent.single){
  					this.paneWidget.closeHeaderNode.style.display = "none";
  					if(!this.isOpen()){
  						parent.closeAll();
  					}
  					parent.appView._heading.set("label", this.label);
  				}
  				this.moveTo = parent.id + "_mblApplView";
  				new TransitionEvent(this.domNode, this.getTransOpts(), e).dispatch();
  			}
  		},
  
  		_open_1: function(){
  			// summary:
  			//		Opens the icon content for the 'below' transition.
  			// tags:
  			//		private
  			this.paneWidget.show();
  			this.unhighlight();
  			if(this.lazy){
  				lazyLoadUtils.instantiateLazyWidgets(this.containerNode, this.requires);
  				this.lazy = false;
  			}
  			this.scrollIntoView(this.paneWidget.domNode);
  			this.onOpen();
  		},
  
  		scrollIntoView: function(/*DomNode*/node){
  			// summary:
  			//		Scrolls until the given node is in the view.
  			var s = viewRegistry.getEnclosingScrollable(node);
  			if(s){ // this node is placed inside scrollable
  				var dim = s.getDim();
  				if(dim.c.h >= dim.d.h){ // #16306: only if the content is larger than the display area 
  					s.scrollIntoView(node, true);
  				}
  			}else{
  				win.global.scrollBy(0, domGeometry.position(node, false).y);
  			}
  		},
  
  		close: function(/*Boolean?*/noAnimation){
  			// summary:
  			//		Closes the icon content.
  			if(!this.isOpen()){ return; }
  			this.set("selected", false);
  			if(has("css3-animations") && !noAnimation){
  				var contentNode = this.paneWidget.domNode;
  				if(this.getParent().transition == "below"){
  					domClass.add(contentNode, "mblCloseContent mblShrink");
  					var nodePos = domGeometry.position(contentNode, true);
  					var targetPos = domGeometry.position(this.domNode, true);
  					var origin = (targetPos.x + targetPos.w/2 - nodePos.x) + "px " + (targetPos.y + targetPos.h/2 - nodePos.y) + "px";
  					domStyle.set(contentNode, css3.add({}, { transformOrigin:origin }));
  				}else{
  					domClass.add(contentNode, "mblCloseContent mblShrink0");
  				}
  			}else{
  				this.paneWidget.hide();
  			}
  			this.onClose();
  		},
  
  		onOpen: function(){
  			// summary:
  			//		Stub method to allow the application to connect.
  		},
  
  		onClose: function(){
  			// summary:
  			//		Stub method to allow the application to connect.
  		},
  
  		_setLabelAttr: function(/*String*/text){
  			// tags:
  			//		private
  			this.label = text;
  			var s = this._cv ? this._cv(text) : text;
  			this.labelNode.innerHTML = s;
  			if(this.paneWidget){
  				this.paneWidget.set("label", text);
  			}
  		},
  
  		_getBadgeAttr: function(){
  			// tags:
  			//		private
  			return this.badgeObj ? this.badgeObj.getValue() : null;
  		},
  
  		_setBadgeAttr: function(/*String*/value){
  			// tags:
  			//		private
  			if(!this.badgeObj){
  				this.badgeObj = new Badge({fontSize:14, className:this.badgeClass});
  				domStyle.set(this.badgeObj.domNode, {
  					position: "absolute",
  					top: "-2px",
  					right: "2px"
  				});
  			}
  			this.badgeObj.setValue(value);
  			if(value){
  				this.iconDivNode.appendChild(this.badgeObj.domNode);
  			}else{
  				this.iconDivNode.removeChild(this.badgeObj.domNode);
  			}
  		},
  
  		_setDeleteIconAttr: function(icon){
  			// tags:
  			//		private
  			if(!this.getParent()){ return; } // icon may be invalid because inheritParams is not called yet
  
  			this._set("deleteIcon", icon);
  			icon = this.deletable ? icon : "";
  			this.deleteIconNode = iconUtils.setIcon(icon, this.deleteIconPos, this.deleteIconNode, 
  					this.deleteIconTitle || this.alt, this.iconDivNode);
  			if(this.deleteIconNode){
  				domClass.add(this.deleteIconNode, "mblIconItemDeleteIcon");
  				if(this.deleteIconRole){
  					this.deleteIconNode.setAttribute("role", this.deleteIconRole);
  				}
  			}
  		},
  
  		_setContentAttr: function(/*String|DomNode*/data){
  			// tags:
  			//		private
  			var root;
  			if(!this.paneWidget){
  				if(!this._tmpNode){
  					this._tmpNode = domConstruct.create("div");
  				}
  				root = this._tmpNode;
  			}else{
  				root = this.paneWidget.containerNode;
  			}
  
  			if(typeof data === "object"){
  				domConstruct.empty(root);
  				root.appendChild(data);
  			}else{
  				root.innerHTML = data;
  			}
  		},
  
  		_setSelectedAttr: function(/*Boolean*/selected){
  			// summary:
  			//		Makes this widget in the selected or unselected state.
  			// tags:
  			//		private
  			this.inherited(arguments);
  			this.iconNode && domStyle.set(this.iconNode, "opacity",
  						 selected ? this.getParent().pressedIconOpacity : 1);
  		}
  	});
  
  	return has("dojo-bidi") ? declare("dojox.mobile.IconItem", [IconItem, BidiIconItem]) : IconItem;
  });
  