]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/tree/_dndSelector.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / tree / _dndSelector.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("dijit/tree/_dndSelector", [
2 "dojo/_base/array", // array.filter array.forEach array.map
3 "dojo/_base/connect", // connect.isCopyKey
4 "dojo/_base/declare", // declare
5 "dojo/_base/Deferred", // Deferred
6 "dojo/_base/kernel", // global
7 "dojo/_base/lang", // lang.hitch
8 "dojo/cookie", // cookie
9 "dojo/mouse", // mouse.isLeft
10 "dojo/on",
11 "dojo/touch",
12 "./_dndContainer"
13], function(array, connect, declare, Deferred, kernel, lang, cookie, mouse, on, touch, _dndContainer){
14
15 // module:
16 // dijit/tree/_dndSelector
17
18
19 return declare("dijit.tree._dndSelector", _dndContainer, {
20 // summary:
21 // This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
22 // It's based on `dojo/dnd/Selector`.
23 // tags:
24 // protected
25
26 /*=====
27 // selection: Object
28 // (id to DomNode) map for every TreeNode that's currently selected.
29 // The DOMNode is the TreeNode.rowNode.
30 selection: {},
31 =====*/
32
33 constructor: function(){
34 // summary:
35 // Initialization
36 // tags:
37 // private
38
39 this.selection={};
40 this.anchor = null;
41
42 if(!this.cookieName && this.tree.id){
43 this.cookieName = this.tree.id + "SaveSelectedCookie";
44 }
45
46 this.events.push(
47 on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
48 on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
49 on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
50 );
51 },
52
53 // singular: Boolean
54 // Allows selection of only one element, if true.
55 // Tree hasn't been tested in singular=true mode, unclear if it works.
56 singular: false,
57
58 // methods
59 getSelectedTreeNodes: function(){
60 // summary:
61 // Returns a list of selected node(s).
62 // Used by dndSource on the start of a drag.
63 // tags:
64 // protected
65 var nodes=[], sel = this.selection;
66 for(var i in sel){
67 nodes.push(sel[i]);
68 }
69 return nodes;
70 },
71
72 selectNone: function(){
73 // summary:
74 // Unselects all items
75 // tags:
76 // private
77
78 this.setSelection([]);
79 return this; // self
80 },
81
82 destroy: function(){
83 // summary:
84 // Prepares the object to be garbage-collected
85 this.inherited(arguments);
86 this.selection = this.anchor = null;
87 },
88 addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor){
89 // summary:
90 // add node to current selection
91 // node: Node
92 // node to add
93 // isAnchor: Boolean
94 // Whether the node should become anchor.
95
96 this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
97 if(isAnchor){ this.anchor = node; }
98 return node;
99 },
100 removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
101 // summary:
102 // remove node from current selection
103 // node: Node
104 // node to remove
105 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
106 return node;
107 },
108 isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
109 // summary:
110 // return true if node is currently selected
111 // node: Node
112 // the node to check whether it's in the current selection
113
114 return node.id && !!this.selection[node.id];
115 },
116 setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
117 // summary:
118 // set the list of selected nodes to be exactly newSelection. All changes to the
119 // selection should be passed through this function, which ensures that derived
120 // attributes are kept up to date. Anchor will be deleted if it has been removed
121 // from the selection, but no new anchor will be added by this function.
122 // newSelection: Node[]
123 // list of tree nodes to make selected
124 var oldSelection = this.getSelectedTreeNodes();
125 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
126 node.setSelected(false);
127 if(this.anchor == node){
128 delete this.anchor;
129 }
130 delete this.selection[node.id];
131 }));
132 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
133 node.setSelected(true);
134 this.selection[node.id] = node;
135 }));
136 this._updateSelectionProperties();
137 },
138 _setDifference: function(xs,ys){
139 // summary:
140 // Returns a copy of xs which lacks any objects
141 // occurring in ys. Checks for membership by
142 // modifying and then reading the object, so it will
143 // not properly handle sets of numbers or strings.
144
145 array.forEach(ys, function(y){ y.__exclude__ = true; });
146 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
147
148 // clean up after ourselves.
149 array.forEach(ys, function(y){ delete y['__exclude__'] });
150 return ret;
151 },
152 _updateSelectionProperties: function(){
153 // summary:
154 // Update the following tree properties from the current selection:
155 // path[s], selectedItem[s], selectedNode[s]
156
157 var selected = this.getSelectedTreeNodes();
158 var paths = [], nodes = [], selects = [];
159 array.forEach(selected, function(node){
160 var ary = node.getTreePath(), model = this.tree.model;
161 nodes.push(node);
162 paths.push(ary);
163 ary = array.map(ary, function(item){
164 return model.getIdentity(item);
165 }, this);
166 selects.push(ary.join("/"))
167 }, this);
168 var items = array.map(nodes,function(node){ return node.item; });
169 this.tree._set("paths", paths);
170 this.tree._set("path", paths[0] || []);
171 this.tree._set("selectedNodes", nodes);
172 this.tree._set("selectedNode", nodes[0] || null);
173 this.tree._set("selectedItems", items);
174 this.tree._set("selectedItem", items[0] || null);
175 if (this.tree.persist && selects.length > 0) {
176 cookie(this.cookieName, selects.join(","), {expires:365});
177 }
178 },
179 _getSavedPaths: function(){
180 // summary:
181 // Returns paths of nodes that were selected previously and saved in the cookie.
182
183 var tree = this.tree;
184 if(tree.persist && tree.dndController.cookieName){
185 var oreo, paths = [];
186 oreo = cookie(tree.dndController.cookieName);
187 if(oreo){
188 paths = array.map(oreo.split(","), function(path){
189 return path.split("/");
190 })
191 }
192 return paths;
193 }
194 },
195 // mouse events
196 onMouseDown: function(e){
197 // summary:
198 // Event processor for onmousedown/ontouchstart
199 // e: Event
200 // onmousedown/ontouchstart event
201 // tags:
202 // protected
203
204 // ignore click on expando node
205 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
206
207 if(mouse.isLeft(e)){
208 // Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
209 // for mobile because it will break things completely, see #15838.
210 e.preventDefault();
211 }else if(e.type != "touchstart"){
212 // Ignore right click
213 return;
214 }
215
216 var treeNode = this.current,
217 copy = connect.isCopyKey(e), id = treeNode.id;
218
219 // if shift key is not pressed, and the node is already in the selection,
220 // delay deselection until onmouseup so in the case of DND, deselection
221 // will be canceled by onmousemove.
222 if(!this.singular && !e.shiftKey && this.selection[id]){
223 this._doDeselect = true;
224 return;
225 }else{
226 this._doDeselect = false;
227 }
228 this.userSelect(treeNode, copy, e.shiftKey);
229 },
230
231 onMouseUp: function(e){
232 // summary:
233 // Event processor for onmouseup/ontouchend
234 // e: Event
235 // onmouseup/ontouchend event
236 // tags:
237 // protected
238
239 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
240 // a already selected item (to deselect the item), or click on a not-yet selected item
241 // (which should remove all current selection, and add the clicked item). This can not
242 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
243 // the deselection logic here, the user can drags an already selected item.
244 if(!this._doDeselect){ return; }
245 this._doDeselect = false;
246 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
247 },
248 onMouseMove: function(/*===== e =====*/){
249 // summary:
250 // event processor for onmousemove/ontouchmove
251 // e: Event
252 // onmousemove/ontouchmove event
253 this._doDeselect = false;
254 },
255
256 _compareNodes: function(n1, n2){
257 if(n1 === n2){
258 return 0;
259 }
260
261 if('sourceIndex' in document.documentElement){ //IE
262 //TODO: does not yet work if n1 and/or n2 is a text node
263 return n1.sourceIndex - n2.sourceIndex;
264 }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
265 return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
266 }else if(document.createRange){ //Webkit
267 var r1 = doc.createRange();
268 r1.setStartBefore(n1);
269
270 var r2 = doc.createRange();
271 r2.setStartBefore(n2);
272
273 return r1.compareBoundaryPoints(r1.END_TO_END, r2);
274 }else{
275 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
276 }
277 },
278
279 userSelect: function(node, multi, range){
280 // summary:
281 // Add or remove the given node from selection, responding
282 // to a user action such as a click or keypress.
283 // multi: Boolean
284 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
285 // range: Boolean
286 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
287 // tags:
288 // protected
289
290 if(this.singular){
291 if(this.anchor == node && multi){
292 this.selectNone();
293 }else{
294 this.setSelection([node]);
295 this.anchor = node;
296 }
297 }else{
298 if(range && this.anchor){
299 var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
300 begin, end, anchor = this.anchor;
301
302 if(cr < 0){ //current is after anchor
303 begin = anchor;
304 end = node;
305 }else{ //current is before anchor
306 begin = node;
307 end = anchor;
308 }
309 var nodes = [];
310 //add everything betweeen begin and end inclusively
311 while(begin != end){
312 nodes.push(begin);
313 begin = this.tree._getNextNode(begin);
314 }
315 nodes.push(end);
316
317 this.setSelection(nodes);
318 }else{
319 if( this.selection[ node.id ] && multi ){
320 this.removeTreeNode( node );
321 }else if(multi){
322 this.addTreeNode(node, true);
323 }else{
324 this.setSelection([node]);
325 this.anchor = node;
326 }
327 }
328 }
329 },
330
331 getItem: function(/*String*/ key){
332 // summary:
333 // Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
334 // Called by dojo/dnd/Source.checkAcceptance().
335 // tags:
336 // protected
337
338 var widget = this.selection[key];
339 return {
340 data: widget,
341 type: ["treeNode"]
342 }; // dojo/dnd/Container._Item
343 },
344
345 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
346 // summary:
347 // Iterates over selected items;
348 // see `dojo/dnd/Container.forInItems()` for details
349 o = o || kernel.global;
350 for(var id in this.selection){
351 // console.log("selected item id: " + id);
352 f.call(o, this.getItem(id), id, this);
353 }
354 }
355 });
356});