dojo.provide("dojo.dnd.Selector"); dojo.require("dojo.dnd.common"); dojo.require("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 */ dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, { // summary: a Selector object, which knows how to select its children constructor: function(node, params){ // summary: a constructor of the Selector // node: Node: node or node's id to build the selector on // params: Object: a dict of parameters, recognized parameters are: // singular: Boolean // allows selection of only one element, if true // the rest of parameters are passed to the container // autoSync: Boolean // autosynchronizes the source with its list of DnD nodes, // false by default 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( dojo.connect(this.node, "onmousedown", this, "onMouseDown"), dojo.connect(this.node, "onmouseup", 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 dojo.NodeList(); var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } t.push(dojo.byId(i)); } return t; // Array }, selectNone: function(){ // summary: unselects all items return this._removeSelection()._removeAnchor(); // self }, selectAll: function(){ // summary: selects all items this.forInItems(function(data, id){ this._addItemClass(dojo.byId(id), "Selected"); this.selection[id] = 1; }, this); return this._removeAnchor(); // self }, deleteSelectedNodes: function(){ // summary: deletes all selected items var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } var n = dojo.byId(i); this.delItem(i); dojo._destroyElement(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 || dojo.global; var s = this.selection, e = dojo.dnd._empty; for(var i in s){ if(i in e){ continue; } f.call(o, this.getItem(i), i, this); } }, sync: function(){ // summary: synch up the node list with the data map dojo.dnd.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 = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } if(!this.getItem(i)){ t.push(i); } } dojo.forEach(t, function(i){ delete this.selection[i]; }, this); return this; // self }, insertNodes: function(addSelected, data, before, anchor){ // summary: inserts new data items (see Container's 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; }; dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor); this._normalizedCreator = oldCreator; return this; // self }, destroy: function(){ // summary: prepares the object to be garbage-collected dojo.dnd.Selector.superclass.destroy.call(this); this.selection = this.anchor = null; }, // markup methods markupFactory: function(params, node){ params._skipStartup = true; return new dojo.dnd.Selector(node, params); }, // 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 && !dojo.dnd.getCopyKeyState(e) && !e.shiftKey && (this.current.id in this.selection)){ this.simpleSelection = true; dojo.stopEvent(e); return; } if(!this.singular && e.shiftKey){ if(!dojo.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; for(; i < c.length; ++i){ var node = c[i]; if(node == this.anchor || node == this.current){ break; } } for(++i; i < c.length; ++i){ var 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(dojo.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(dojo.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; } } } } dojo.stopEvent(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 = dojo.connect(this.node, "onmousemove", this, "onMouseMove"); }, onOutEvent: function(){ // summary: this function is called once, when mouse is out of our container dojo.disconnect(this.onmousemoveEvent); delete this.onmousemoveEvent; }, _removeSelection: function(){ // summary: unselects all items var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } var node = dojo.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 } });