define("dojo/dnd/Selector", [ "../_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 /* 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 */ /*===== var __SelectorArgs = declare([Container.__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 // 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: __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. Stopping the event prevents text selection while // dragging. However, don't stop the event on mobile because that prevents a click event, // and also prevents scroll (see #15838). // 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; });