text: define([
  	"../_base/array", "../_base/declare", "../_base/event", "../_base/kernel", "../_base/lang",
  	"../dom", "../dom-construct", "../mouse", "../_base/NodeList", "../on", "../touch", "./common", "./Container"
  ], function(array, declare, event, kernel, lang, dom, domConstruct, mouse, NodeList, on, touch, dnd, Container) {
  
  // module:
  //		dojo/dnd/Selector
  // summary:
  //		TODOC
  
  /*=====
  Container = dojo.dnd.Container;
  =====*/
  
  /*
  	Container item states:
  		""			- an item is not selected
  		"Selected"	- an item is selected
  		"Anchor"	- an item is selected, and is an anchor for a "shift" selection
  */
  
  /*=====
  declare("dojo.dnd.__SelectorArgs", [dojo.dnd.__ContainerArgs], {
  	//	singular: Boolean
  	//		allows selection of only one element, if true
  	singular: false,
  
  	//	autoSync: Boolean
  	//		autosynchronizes the source with its list of DnD nodes,
  	autoSync: false
  });
  =====*/
  
  var Selector = declare("dojo.dnd.Selector", Container, {
  	// summary:
  	//		a Selector object, which knows how to select its children
  
  	/*=====
  	// selection: Set<String>
  	//		The set of id's that are currently selected, such that this.selection[id] == 1
  	//		if the node w/that id is selected.  Can iterate over selected node's id's like:
  	//	|		for(var id in this.selection)
  	selection: {},
  	=====*/
  
  	constructor: function(node, params){
  		// summary:
  		//		constructor of the Selector
  		// node: Node||String
  		//		node or node's id to build the selector on
  		// params: dojo.dnd.__SelectorArgs?
  		//		a dictionary of parameters
  		if(!params){ params = {}; }
  		this.singular = params.singular;
  		this.autoSync = params.autoSync;
  		// class-specific variables
  		this.selection = {};
  		this.anchor = null;
  		this.simpleSelection = false;
  		// set up events
  		this.events.push(
  			on(this.node, touch.press, lang.hitch(this, "onMouseDown")),
  			on(this.node, touch.release, lang.hitch(this, "onMouseUp"))
  		);
  	},
  
  	// object attributes (for markup)
  	singular: false,	// is singular property
  
  	// methods
  	getSelectedNodes: function(){
  		// summary:
  		//		returns a list (an array) of selected nodes
  		var t = new NodeList();
  		var e = dnd._empty;
  		for(var i in this.selection){
  			if(i in e){ continue; }
  			t.push(dom.byId(i));
  		}
  		return t;	// NodeList
  	},
  	selectNone: function(){
  		// summary:
  		//		unselects all items
  		return this._removeSelection()._removeAnchor();	// self
  	},
  	selectAll: function(){
  		// summary:
  		//		selects all items
  		this.forInItems(function(data, id){
  			this._addItemClass(dom.byId(id), "Selected");
  			this.selection[id] = 1;
  		}, this);
  		return this._removeAnchor();	// self
  	},
  	deleteSelectedNodes: function(){
  		// summary:
  		//		deletes all selected items
  		var e = dnd._empty;
  		for(var i in this.selection){
  			if(i in e){ continue; }
  			var n = dom.byId(i);
  			this.delItem(i);
  			domConstruct.destroy(n);
  		}
  		this.anchor = null;
  		this.selection = {};
  		return this;	// self
  	},
  	forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
  		// summary:
  		//		iterates over selected items;
  		//		see `dojo.dnd.Container.forInItems()` for details
  		o = o || kernel.global;
  		var s = this.selection, e = dnd._empty;
  		for(var i in s){
  			if(i in e){ continue; }
  			f.call(o, this.getItem(i), i, this);
  		}
  	},
  	sync: function(){
  		// summary:
  		//		sync up the node list with the data map
  
  		Selector.superclass.sync.call(this);
  
  		// fix the anchor
  		if(this.anchor){
  			if(!this.getItem(this.anchor.id)){
  				this.anchor = null;
  			}
  		}
  
  		// fix the selection
  		var t = [], e = dnd._empty;
  		for(var i in this.selection){
  			if(i in e){ continue; }
  			if(!this.getItem(i)){
  				t.push(i);
  			}
  		}
  		array.forEach(t, function(i){
  			delete this.selection[i];
  		}, this);
  
  		return this;	// self
  	},
  	insertNodes: function(addSelected, data, before, anchor){
  		// summary:
  		//		inserts new data items (see `dojo.dnd.Container.insertNodes()` method for details)
  		// addSelected: Boolean
  		//		all new nodes will be added to selected items, if true, no selection change otherwise
  		// data: Array
  		//		a list of data items, which should be processed by the creator function
  		// before: Boolean
  		//		insert before the anchor, if true, and after the anchor otherwise
  		// anchor: Node
  		//		the anchor node to be used as a point of insertion
  		var oldCreator = this._normalizedCreator;
  		this._normalizedCreator = function(item, hint){
  			var t = oldCreator.call(this, item, hint);
  			if(addSelected){
  				if(!this.anchor){
  					this.anchor = t.node;
  					this._removeItemClass(t.node, "Selected");
  					this._addItemClass(this.anchor, "Anchor");
  				}else if(this.anchor != t.node){
  					this._removeItemClass(t.node, "Anchor");
  					this._addItemClass(t.node, "Selected");
  				}
  				this.selection[t.node.id] = 1;
  			}else{
  				this._removeItemClass(t.node, "Selected");
  				this._removeItemClass(t.node, "Anchor");
  			}
  			return t;
  		};
  		Selector.superclass.insertNodes.call(this, data, before, anchor);
  		this._normalizedCreator = oldCreator;
  		return this;	// self
  	},
  	destroy: function(){
  		// summary:
  		//		prepares the object to be garbage-collected
  		Selector.superclass.destroy.call(this);
  		this.selection = this.anchor = null;
  	},
  
  	// mouse events
  	onMouseDown: function(e){
  		// summary:
  		//		event processor for onmousedown
  		// e: Event
  		//		mouse event
  		if(this.autoSync){ this.sync(); }
  		if(!this.current){ return; }
  		if(!this.singular && !dnd.getCopyKeyState(e) && !e.shiftKey && (this.current.id in this.selection)){
  			this.simpleSelection = true;
  			if(mouse.isLeft(e)){
  				// accept the left button and stop the event
  				// for IE we don't stop event when multiple buttons are pressed
  				event.stop(e);
  			}
  			return;
  		}
  		if(!this.singular && e.shiftKey){
  			if(!dnd.getCopyKeyState(e)){
  				this._removeSelection();
  			}
  			var c = this.getAllNodes();
  			if(c.length){
  				if(!this.anchor){
  					this.anchor = c[0];
  					this._addItemClass(this.anchor, "Anchor");
  				}
  				this.selection[this.anchor.id] = 1;
  				if(this.anchor != this.current){
  					var i = 0, node;
  					for(; i < c.length; ++i){
  						node = c[i];
  						if(node == this.anchor || node == this.current){ break; }
  					}
  					for(++i; i < c.length; ++i){
  						node = c[i];
  						if(node == this.anchor || node == this.current){ break; }
  						this._addItemClass(node, "Selected");
  						this.selection[node.id] = 1;
  					}
  					this._addItemClass(this.current, "Selected");
  					this.selection[this.current.id] = 1;
  				}
  			}
  		}else{
  			if(this.singular){
  				if(this.anchor == this.current){
  					if(dnd.getCopyKeyState(e)){
  						this.selectNone();
  					}
  				}else{
  					this.selectNone();
  					this.anchor = this.current;
  					this._addItemClass(this.anchor, "Anchor");
  					this.selection[this.current.id] = 1;
  				}
  			}else{
  				if(dnd.getCopyKeyState(e)){
  					if(this.anchor == this.current){
  						delete this.selection[this.anchor.id];
  						this._removeAnchor();
  					}else{
  						if(this.current.id in this.selection){
  							this._removeItemClass(this.current, "Selected");
  							delete this.selection[this.current.id];
  						}else{
  							if(this.anchor){
  								this._removeItemClass(this.anchor, "Anchor");
  								this._addItemClass(this.anchor, "Selected");
  							}
  							this.anchor = this.current;
  							this._addItemClass(this.current, "Anchor");
  							this.selection[this.current.id] = 1;
  						}
  					}
  				}else{
  					if(!(this.current.id in this.selection)){
  						this.selectNone();
  						this.anchor = this.current;
  						this._addItemClass(this.current, "Anchor");
  						this.selection[this.current.id] = 1;
  					}
  				}
  			}
  		}
  		event.stop(e);
  	},
  	onMouseUp: function(/*===== e =====*/){
  		// summary:
  		//		event processor for onmouseup
  		// e: Event
  		//		mouse event
  		if(!this.simpleSelection){ return; }
  		this.simpleSelection = false;
  		this.selectNone();
  		if(this.current){
  			this.anchor = this.current;
  			this._addItemClass(this.anchor, "Anchor");
  			this.selection[this.current.id] = 1;
  		}
  	},
  	onMouseMove: function(/*===== e =====*/){
  		// summary:
  		//		event processor for onmousemove
  		// e: Event
  		//		mouse event
  		this.simpleSelection = false;
  	},
  
  	// utilities
  	onOverEvent: function(){
  		// summary:
  		//		this function is called once, when mouse is over our container
  		this.onmousemoveEvent = on(this.node, touch.move, lang.hitch(this, "onMouseMove"));
  	},
  	onOutEvent: function(){
  		// summary:
  		//		this function is called once, when mouse is out of our container
  		if(this.onmousemoveEvent){
  			this.onmousemoveEvent.remove();
  			delete this.onmousemoveEvent;
  		}
  	},
  	_removeSelection: function(){
  		// summary:
  		//		unselects all items
  		var e = dnd._empty;
  		for(var i in this.selection){
  			if(i in e){ continue; }
  			var node = dom.byId(i);
  			if(node){ this._removeItemClass(node, "Selected"); }
  		}
  		this.selection = {};
  		return this;	// self
  	},
  	_removeAnchor: function(){
  		if(this.anchor){
  			this._removeItemClass(this.anchor, "Anchor");
  			this.anchor = null;
  		}
  		return this;	// self
  	}
  });
  
  return Selector;
  
  });
  