text: define([
  	"dojo/_base/declare", // declare
  	"dojo/dom-construct", // domConstruct.create
  	"dojo/dom-style", // domStyle.getComputedStyle
  	"dojo/_base/kernel", // kernel.deprecated
  	"dojo/_base/lang", // lang.hitch
  	"dojo/on",
  	"dojo/sniff", // has("ie") has("mozilla")
  	"./_FormValueWidget",
  	"./_TextBoxMixin",
  	"dojo/text!./templates/TextBox.html",
  	"../main"	// to export dijit._setSelectionRange, remove in 2.0
  ], function(declare, domConstruct, domStyle, kernel, lang, on, has,
  			_FormValueWidget, _TextBoxMixin, template, dijit){
  
  	// module:
  	//		dijit/form/TextBox
  
  	var TextBox = declare("dijit.form.TextBox" + (has("dojo-bidi") ? "_NoBidi" : ""), [_FormValueWidget, _TextBoxMixin], {
  		// summary:
  		//		A base class for textbox form inputs
  
  		templateString: template,
  		_singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
  
  		_buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
  
  		baseClass: "dijitTextBox",
  
  		postMixInProperties: function(){
  			var type = this.type.toLowerCase();
  			if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
  				this.templateString = this._singleNodeTemplate;
  			}
  			this.inherited(arguments);
  		},
  
  		postCreate: function(){
  			this.inherited(arguments);
  
  			if(has("ie") < 9){
  				// IE INPUT tag fontFamily has to be set directly using STYLE
  				// the defer gives IE a chance to render the TextBox and to deal with font inheritance
  				this.defer(function(){
  					try{
  						var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
  						if(s){
  							var ff = s.fontFamily;
  							if(ff){
  								var inputs = this.domNode.getElementsByTagName("INPUT");
  								if(inputs){
  									for(var i=0; i < inputs.length; i++){
  										inputs[i].style.fontFamily = ff;
  									}
  								}
  							}
  						}
  					}catch(e){/*when used in a Dialog, and this is called before the dialog is
  					 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
  				});
  			}
  		},
  
  		_setPlaceHolderAttr: function(v){
  			this._set("placeHolder", v);
  			if(!this._phspan){
  				this._attachPoints.push('_phspan');
  				// dijitInputField class gives placeHolder same padding as the input field
  				// parent node already has dijitInputField class but it doesn't affect this <span>
  				// since it's position: absolute.
  				this._phspan = domConstruct.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
  				this.own(
  					on(this._phspan, "mousedown", function(evt){ evt.preventDefault(); }),
  					on(this._phspan, "touchend, pointerup, MSPointerUp", lang.hitch(this, function(){
  						// If the user clicks placeholder rather than the <input>, need programmatic focus.  Normally this
  						// is done in _FormWidgetMixin._onFocus() but after [30663] it's done on a delay, which is ineffective.
  						this.focus();
  					}))
  				);
  			}
  			this._phspan.innerHTML="";
  			this._phspan.appendChild(this._phspan.ownerDocument.createTextNode(v));
  			this._updatePlaceHolder();
  		},
  
  		_onInput: function(/*Event*/ evt){
  			// summary:
  			//		Called AFTER the input event has happened
  			//		See if the placeHolder text should be removed or added while editing.
  			this.inherited(arguments);
  			this._updatePlaceHolder();
  		},
  
  		_updatePlaceHolder: function(){
  			if(this._phspan){
  				this._phspan.style.display = (this.placeHolder && !this.textbox.value) ? "" : "none";
  			}
  		},
  
  		_setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  			this.inherited(arguments);
  			this._updatePlaceHolder();
  		},
  
  		getDisplayedValue: function(){
  			// summary:
  			//		Deprecated.  Use get('displayedValue') instead.
  			// tags:
  			//		deprecated
  			kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
  			return this.get('displayedValue');
  		},
  
  		setDisplayedValue: function(/*String*/ value){
  			// summary:
  			//		Deprecated.  Use set('displayedValue', ...) instead.
  			// tags:
  			//		deprecated
  			kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
  			this.set('displayedValue', value);
  		},
  
  		_onBlur: function(e){
  			if(this.disabled){ return; }
  			this.inherited(arguments);
  			this._updatePlaceHolder();
  
  			if(has("mozilla")){
  				if(this.selectOnClick){
  					// clear selection so that the next mouse click doesn't reselect
  					this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
  				}
  			}
  		},
  
  		_onFocus: function(/*String*/ by){
  			if(this.disabled || this.readOnly){ return; }
  			this.inherited(arguments);
  			this._updatePlaceHolder();
  		}
  	});
  
  	if(has("ie") < 9){
  		TextBox.prototype._isTextSelected = function(){
  			var range = this.ownerDocument.selection.createRange();
  			var parent = range.parentElement();
  			return parent == this.textbox && range.text.length > 0;
  		};
  
  		// Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
  		dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
  			if(element.createTextRange){
  				var r = element.createTextRange();
  				r.collapse(true);
  				r.moveStart("character", -99999); // move to 0
  				r.moveStart("character", start); // delta from 0 is the correct position
  				r.moveEnd("character", stop-start);
  				r.select();
  			}
  		}
  	}
  
  	if(has("dojo-bidi")){
  		TextBox = declare("dijit.form.TextBox", TextBox, {
  			_setPlaceHolderAttr: function(v){
  				this.inherited(arguments);
  				this.applyTextDir(this._phspan);
  			}
  		});
  	}
  
  	return TextBox;
  });
  