text: define("dojo/html", ["dojo", "dojo/parser"], function(dojo) {
  dojo.getObject("html", true, dojo);
  
  // the parser might be needed..
  (function(){ // private scope, sort of a namespace
  
  	// idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
  	var idCounter = 0,
  		d = dojo;
  	
  	dojo.html._secureForInnerHtml = function(/*String*/ cont){
  		// summary:
  		//		removes !DOCTYPE and title elements from the html string.
  		//
  		//		khtml is picky about dom faults, you can't attach a style or <title> node as child of body
  		//		must go into head, so we need to cut out those tags
  		//	cont:
  		//		An html string for insertion into the dom
  		//
  		return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
  	};
  
  /*====
  	dojo.html._emptyNode = function(node){
  		// summary:
  		//		removes all child nodes from the given node
  		//	node: DOMNode
  		//		the parent element
  	};
  =====*/
  	dojo.html._emptyNode = dojo.empty;
  
  	dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
  		// summary:
  		//		inserts the given content into the given node
  		//	node:
  		//		the parent element
  		//	content:
  		//		the content to be set on the parent element.
  		//		This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  		
  		// always empty
  		d.empty(node);
  
  		if(cont) {
  			if(typeof cont == "string") {
  				cont = d._toDom(cont, node.ownerDocument);
  			}
  			if(!cont.nodeType && d.isArrayLike(cont)) {
  				// handle as enumerable, but it may shrink as we enumerate it
  				for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
  					d.place( cont[i], node, "last");
  				}
  			} else {
  				// pass nodes, documentFragments and unknowns through to dojo.place
  				d.place(cont, node, "last");
  			}
  		}
  
  		// return DomNode
  		return node;
  	};
  
  	// we wrap up the content-setting operation in a object
  	dojo.declare("dojo.html._ContentSetter", null,
  		{
  			// node: DomNode|String
  			//		An node which will be the parent element that we set content into
  			node: "",
  
  			// content: String|DomNode|DomNode[]
  			//		The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
  			content: "",
  			
  			// id: String?
  			//		Usually only used internally, and auto-generated with each instance
  			id: "",
  
  			// cleanContent: Boolean
  			//		Should the content be treated as a full html document,
  			//		and the real content stripped of <html>, <body> wrapper before injection
  			cleanContent: false,
  			
  			// extractContent: Boolean
  			//		Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
  			extractContent: false,
  
  			// parseContent: Boolean
  			//		Should the node by passed to the parser after the new content is set
  			parseContent: false,
  
  			// parserScope: String
  			//		Flag passed to parser.  Root for attribute names to search for.   If scopeName is dojo,
  			//		will search for data-dojo-type (or dojoType).  For backwards compatibility
  			//		reasons defaults to dojo._scopeName (which is "dojo" except when
  			//		multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  			parserScope: dojo._scopeName,
  
  			// startup: Boolean
  			//		Start the child widgets after parsing them.   Only obeyed if parseContent is true.
  			startup: true,
  			
  			// lifecyle methods
  			constructor: function(/* Object */params, /* String|DomNode */node){
  				//	summary:
  				//		Provides a configurable, extensible object to wrap the setting on content on a node
  				//		call the set() method to actually set the content..
   
  				// the original params are mixed directly into the instance "this"
  				dojo.mixin(this, params || {});
  
  				// give precedence to params.node vs. the node argument
  				// and ensure its a node, not an id string
  				node = this.node = dojo.byId( this.node || node );
  	
  				if(!this.id){
  					this.id = [
  						"Setter",
  						(node) ? node.id || node.tagName : "",
  						idCounter++
  					].join("_");
  				}
  			},
  			set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
  				// summary:
  				//		front-end to the set-content sequence
  				//	cont:
  				//		An html string, node or enumerable list of nodes for insertion into the dom
  				//		If not provided, the object's content property will be used
  				if(undefined !== cont){
  					this.content = cont;
  				}
  				// in the re-use scenario, set needs to be able to mixin new configuration
  				if(params){
  					this._mixin(params);
  				}
  
  				this.onBegin();
  				this.setContent();
  				this.onEnd();
  
  				return this.node;
  			},
  			setContent: function(){
  				// summary:
  				//		sets the content on the node
  
  				var node = this.node;
  				if(!node) {
  				    // can't proceed
  					throw new Error(this.declaredClass + ": setContent given no node");
  				}
  				try{
  					node = dojo.html._setNodeContent(node, this.content);
  				}catch(e){
  					// check if a domfault occurs when we are appending this.errorMessage
  					// like for instance if domNode is a UL and we try append a DIV
  	
  					// FIXME: need to allow the user to provide a content error message string
  					var errMess = this.onContentError(e);
  					try{
  						node.innerHTML = errMess;
  					}catch(e){
  						console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
  					}
  				}
  				// always put back the node for the next method
  				this.node = node; // DomNode
  			},
  			
  			empty: function() {
  				// summary
  				//	cleanly empty out existing content
  
  				// destroy any widgets from a previous run
  				// NOTE: if you dont want this you'll need to empty
  				// the parseResults array property yourself to avoid bad things happenning
  				if(this.parseResults && this.parseResults.length) {
  					dojo.forEach(this.parseResults, function(w) {
  						if(w.destroy){
  							w.destroy();
  						}
  					});
  					delete this.parseResults;
  				}
  				// this is fast, but if you know its already empty or safe, you could
  				// override empty to skip this step
  				dojo.html._emptyNode(this.node);
  			},
  	
  			onBegin: function(){
  				// summary
  				//		Called after instantiation, but before set();
  				//		It allows modification of any of the object properties
  				//		- including the node and content provided - before the set operation actually takes place
  				//		This default implementation checks for cleanContent and extractContent flags to
  				//		optionally pre-process html string content
  				var cont = this.content;
  	
  				if(dojo.isString(cont)){
  					if(this.cleanContent){
  						cont = dojo.html._secureForInnerHtml(cont);
  					}
    
  					if(this.extractContent){
  						var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
  						if(match){ cont = match[1]; }
  					}
  				}
  
  				// clean out the node and any cruft associated with it - like widgets
  				this.empty();
  				
  				this.content = cont;
  				return this.node; /* DomNode */
  			},
  	
  			onEnd: function(){
  				// summary
  				//		Called after set(), when the new content has been pushed into the node
  				//		It provides an opportunity for post-processing before handing back the node to the caller
  				//		This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
  				if(this.parseContent){
  					// populates this.parseResults if you need those..
  					this._parse();
  				}
  				return this.node; /* DomNode */
  			},
  	
  			tearDown: function(){
  				// summary
  				//		manually reset the Setter instance if its being re-used for example for another set()
  				// description
  				//		tearDown() is not called automatically.
  				//		In normal use, the Setter instance properties are simply allowed to fall out of scope
  				//		but the tearDown method can be called to explicitly reset this instance.
  				delete this.parseResults;
  				delete this.node;
  				delete this.content;
  			},
    
  			onContentError: function(err){
  				return "Error occured setting content: " + err;
  			},
  			
  			_mixin: function(params){
  				// mix properties/methods into the instance
  				// TODO: the intention with tearDown is to put the Setter's state
  				// back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
  				// so we could do something here to move the original properties aside for later restoration
  				var empty = {}, key;
  				for(key in params){
  					if(key in empty){ continue; }
  					// TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
  					// .. but history shows we'll almost always guess wrong
  					this[key] = params[key];
  				}
  			},
  			_parse: function(){
  				// summary:
  				//		runs the dojo parser over the node contents, storing any results in this.parseResults
  				//		Any errors resulting from parsing are passed to _onError for handling
  
  				var rootNode = this.node;
  				try{
  					// store the results (widgets, whatever) for potential retrieval
  					var inherited = {};
  					dojo.forEach(["dir", "lang", "textDir"], function(name){
  						if(this[name]){
  							inherited[name] = this[name];
  						}
  					}, this);
  					this.parseResults = dojo.parser.parse({
  						rootNode: rootNode,
  						noStart: !this.startup,
  						inherited: inherited,
  						scope: this.parserScope
  					});
  				}catch(e){
  					this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
  				}
  			},
    
  			_onError: function(type, err, consoleText){
  				// summary:
  				//		shows user the string that is returned by on[type]Error
  				//		overide/implement on[type]Error and return your own string to customize
  				var errText = this['on' + type + 'Error'].call(this, err);
  				if(consoleText){
  					console.error(consoleText, err);
  				}else if(errText){ // a empty string won't change current content
  					dojo.html._setNodeContent(this.node, errText, true);
  				}
  			}
  	}); // end dojo.declare()
  
  	dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
  			// summary:
  			//		inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
  			//		may be a better choice for simple HTML insertion.
  			// description:
  			//		Unless you need to use the params capabilities of this method, you should use
  			//		dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
  			//		an HTML string into the DOM, but it only handles inserting an HTML string as DOM
  			//		elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
  			//		or the other capabilities as defined by the params object for this method.
  			//	node:
  			//		the parent element that will receive the content
  			//	cont:
  			//		the content to be set on the parent element.
  			//		This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  			//	params:
  			//		Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
  			//	example:
  			//		A safe string/node/nodelist content replacement/injection with hooks for extension
  			//		Example Usage:
  			//		dojo.html.set(node, "some string");
  			//		dojo.html.set(node, contentNode, {options});
  			//		dojo.html.set(node, myNode.childNodes, {options});
  		if(undefined == cont){
  			console.warn("dojo.html.set: no cont argument provided, using empty string");
  			cont = "";
  		}
  		if(!params){
  			// simple and fast
  			return dojo.html._setNodeContent(node, cont, true);
  		}else{
  			// more options but slower
  			// note the arguments are reversed in order, to match the convention for instantiation via the parser
  			var op = new dojo.html._ContentSetter(dojo.mixin(
  					params,
  					{ content: cont, node: node }
  			));
  			return op.set();
  		}
  	};
  })();
  
  return dojo.html;
  });
  