You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
9.0 KiB
333 lines
9.0 KiB
define([ |
|
"../_base/array", "../_base/declare", "../_base/kernel", "../_base/lang", |
|
"../dom", "../dom-construct", "../mouse", "../_base/NodeList", "../on", "../touch", "./common", "./Container" |
|
], function(array, declare, 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<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: __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. |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
} |
|
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; |
|
} |
|
} |
|
} |
|
} |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
}, |
|
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; |
|
|
|
});
|
|
|