Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /***
2 Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
3 Mochi-ized By Thomas Herve (_firstname_@nimail.org)
5 See scriptaculous.js for full license.
7 ***/
9 MochiKit.Base._deps('Sortable', ['Base', 'Iter', 'DOM', 'Position', 'DragAndDrop']);
11 MochiKit.Sortable.NAME = 'MochiKit.Sortable';
12 MochiKit.Sortable.VERSION = '1.4.2';
14 MochiKit.Sortable.__repr__ = function () {
15 return '[' + this.NAME + ' ' + this.VERSION + ']';
16 };
18 MochiKit.Sortable.toString = function () {
19 return this.__repr__();
20 };
22 MochiKit.Sortable.EXPORT = [
23 ];
25 MochiKit.Sortable.EXPORT_OK = [
26 ];
28 MochiKit.Base.update(MochiKit.Sortable, {
29 /***
31 Manage sortables. Mainly use the create function to add a sortable.
33 ***/
34 sortables: {},
36 _findRootElement: function (element) {
37 while (element.tagName.toUpperCase() != "BODY") {
38 if (element.id && MochiKit.Sortable.sortables[element.id]) {
39 return element;
40 }
41 element = element.parentNode;
42 }
43 },
45 _createElementId: function(element) {
46 if (element.id == null || element.id == "") {
47 var d = MochiKit.DOM;
48 var id;
49 var count = 1;
50 while (d.getElement(id = "sortable" + count) != null) {
51 count += 1;
52 }
53 d.setNodeAttribute(element, "id", id);
54 }
55 },
57 /** @id MochiKit.Sortable.options */
58 options: function (element) {
59 element = MochiKit.Sortable._findRootElement(MochiKit.DOM.getElement(element));
60 if (!element) {
61 return;
62 }
63 return MochiKit.Sortable.sortables[element.id];
64 },
66 /** @id MochiKit.Sortable.destroy */
67 destroy: function (element){
68 var s = MochiKit.Sortable.options(element);
69 var b = MochiKit.Base;
70 var d = MochiKit.DragAndDrop;
72 if (s) {
73 MochiKit.Signal.disconnect(s.startHandle);
74 MochiKit.Signal.disconnect(s.endHandle);
75 b.map(function (dr) {
76 d.Droppables.remove(dr);
77 }, s.droppables);
78 b.map(function (dr) {
79 dr.destroy();
80 }, s.draggables);
82 delete MochiKit.Sortable.sortables[s.element.id];
83 }
84 },
86 /** @id MochiKit.Sortable.create */
87 create: function (element, options) {
88 element = MochiKit.DOM.getElement(element);
89 var self = MochiKit.Sortable;
90 self._createElementId(element);
92 /** @id MochiKit.Sortable.options */
93 options = MochiKit.Base.update({
95 /** @id MochiKit.Sortable.element */
96 element: element,
98 /** @id MochiKit.Sortable.tag */
99 tag: 'li', // assumes li children, override with tag: 'tagname'
101 /** @id MochiKit.Sortable.dropOnEmpty */
102 dropOnEmpty: false,
104 /** @id MochiKit.Sortable.tree */
105 tree: false,
107 /** @id MochiKit.Sortable.treeTag */
108 treeTag: 'ul',
110 /** @id MochiKit.Sortable.overlap */
111 overlap: 'vertical', // one of 'vertical', 'horizontal'
113 /** @id MochiKit.Sortable.constraint */
114 constraint: 'vertical', // one of 'vertical', 'horizontal', false
115 // also takes array of elements (or ids); or false
117 /** @id MochiKit.Sortable.containment */
118 containment: [element],
120 /** @id MochiKit.Sortable.handle */
121 handle: false, // or a CSS class
123 /** @id MochiKit.Sortable.only */
124 only: false,
126 /** @id MochiKit.Sortable.hoverclass */
127 hoverclass: null,
129 /** @id MochiKit.Sortable.ghosting */
130 ghosting: false,
132 /** @id MochiKit.Sortable.scroll */
133 scroll: false,
135 /** @id MochiKit.Sortable.scrollSensitivity */
136 scrollSensitivity: 20,
138 /** @id MochiKit.Sortable.scrollSpeed */
139 scrollSpeed: 15,
141 /** @id MochiKit.Sortable.format */
142 format: /^[^_]*_(.*)$/,
144 /** @id MochiKit.Sortable.onChange */
145 onChange: MochiKit.Base.noop,
147 /** @id MochiKit.Sortable.onUpdate */
148 onUpdate: MochiKit.Base.noop,
150 /** @id MochiKit.Sortable.accept */
151 accept: null
152 }, options);
154 // clear any old sortable with same element
155 self.destroy(element);
157 // build options for the draggables
158 var options_for_draggable = {
159 revert: true,
160 ghosting: options.ghosting,
161 scroll: options.scroll,
162 scrollSensitivity: options.scrollSensitivity,
163 scrollSpeed: options.scrollSpeed,
164 constraint: options.constraint,
165 handle: options.handle
166 };
168 if (options.starteffect) {
169 options_for_draggable.starteffect = options.starteffect;
170 }
172 if (options.reverteffect) {
173 options_for_draggable.reverteffect = options.reverteffect;
174 } else if (options.ghosting) {
175 options_for_draggable.reverteffect = function (innerelement) {
176 innerelement.style.top = 0;
177 innerelement.style.left = 0;
178 };
179 }
181 if (options.endeffect) {
182 options_for_draggable.endeffect = options.endeffect;
183 }
185 if (options.zindex) {
186 options_for_draggable.zindex = options.zindex;
187 }
189 // build options for the droppables
190 var options_for_droppable = {
191 overlap: options.overlap,
192 containment: options.containment,
193 hoverclass: options.hoverclass,
194 onhover: self.onHover,
195 tree: options.tree,
196 accept: options.accept
197 }
199 var options_for_tree = {
200 onhover: self.onEmptyHover,
201 overlap: options.overlap,
202 containment: options.containment,
203 hoverclass: options.hoverclass,
204 accept: options.accept
205 }
207 // fix for gecko engine
208 MochiKit.DOM.removeEmptyTextNodes(element);
210 options.draggables = [];
211 options.droppables = [];
213 // drop on empty handling
214 if (options.dropOnEmpty || options.tree) {
215 new MochiKit.DragAndDrop.Droppable(element, options_for_tree);
216 options.droppables.push(element);
217 }
218 MochiKit.Base.map(function (e) {
219 // handles are per-draggable
220 var handle = options.handle ?
221 MochiKit.DOM.getFirstElementByTagAndClassName(null,
222 options.handle, e) : e;
223 options.draggables.push(
224 new MochiKit.DragAndDrop.Draggable(e,
225 MochiKit.Base.update(options_for_draggable,
226 {handle: handle})));
227 new MochiKit.DragAndDrop.Droppable(e, options_for_droppable);
228 if (options.tree) {
229 e.treeNode = element;
230 }
231 options.droppables.push(e);
232 }, (self.findElements(element, options) || []));
234 if (options.tree) {
235 MochiKit.Base.map(function (e) {
236 new MochiKit.DragAndDrop.Droppable(e, options_for_tree);
237 e.treeNode = element;
238 options.droppables.push(e);
239 }, (self.findTreeElements(element, options) || []));
240 }
242 // keep reference
243 self.sortables[element.id] = options;
245 options.lastValue = self.serialize(element);
246 options.startHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'start',
247 MochiKit.Base.partial(self.onStart, element));
248 options.endHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'end',
249 MochiKit.Base.partial(self.onEnd, element));
250 },
252 /** @id MochiKit.Sortable.onStart */
253 onStart: function (element, draggable) {
254 var self = MochiKit.Sortable;
255 var options = self.options(element);
256 options.lastValue = self.serialize(options.element);
257 },
259 /** @id MochiKit.Sortable.onEnd */
260 onEnd: function (element, draggable) {
261 var self = MochiKit.Sortable;
262 self.unmark();
263 var options = self.options(element);
264 if (options.lastValue != self.serialize(options.element)) {
265 options.onUpdate(options.element);
266 }
267 },
269 // return all suitable-for-sortable elements in a guaranteed order
271 /** @id MochiKit.Sortable.findElements */
272 findElements: function (element, options) {
273 return MochiKit.Sortable.findChildren(element, options.only, options.tree, options.tag);
274 },
276 /** @id MochiKit.Sortable.findTreeElements */
277 findTreeElements: function (element, options) {
278 return MochiKit.Sortable.findChildren(
279 element, options.only, options.tree ? true : false, options.treeTag);
280 },
282 /** @id MochiKit.Sortable.findChildren */
283 findChildren: function (element, only, recursive, tagName) {
284 if (!element.hasChildNodes()) {
285 return null;
286 }
287 tagName = tagName.toUpperCase();
288 if (only) {
289 only = MochiKit.Base.flattenArray([only]);
290 }
291 var elements = [];
292 MochiKit.Base.map(function (e) {
293 if (e.tagName &&
294 e.tagName.toUpperCase() == tagName &&
295 (!only ||
296 MochiKit.Iter.some(only, function (c) {
297 return MochiKit.DOM.hasElementClass(e, c);
298 }))) {
299 elements.push(e);
300 }
301 if (recursive) {
302 var grandchildren = MochiKit.Sortable.findChildren(e, only, recursive, tagName);
303 if (grandchildren && grandchildren.length > 0) {
304 elements = elements.concat(grandchildren);
305 }
306 }
307 }, element.childNodes);
308 return elements;
309 },
311 /** @id MochiKit.Sortable.onHover */
312 onHover: function (element, dropon, overlap) {
313 if (MochiKit.DOM.isChildNode(dropon, element)) {
314 return;
315 }
316 var self = MochiKit.Sortable;
318 if (overlap > .33 && overlap < .66 && self.options(dropon).tree) {
319 return;
320 } else if (overlap > 0.5) {
321 self.mark(dropon, 'before');
322 if (dropon.previousSibling != element) {
323 var oldParentNode = element.parentNode;
324 element.style.visibility = 'hidden'; // fix gecko rendering
325 dropon.parentNode.insertBefore(element, dropon);
326 if (dropon.parentNode != oldParentNode) {
327 self.options(oldParentNode).onChange(element);
328 }
329 self.options(dropon.parentNode).onChange(element);
330 }
331 } else {
332 self.mark(dropon, 'after');
333 var nextElement = dropon.nextSibling || null;
334 if (nextElement != element) {
335 var oldParentNode = element.parentNode;
336 element.style.visibility = 'hidden'; // fix gecko rendering
337 dropon.parentNode.insertBefore(element, nextElement);
338 if (dropon.parentNode != oldParentNode) {
339 self.options(oldParentNode).onChange(element);
340 }
341 self.options(dropon.parentNode).onChange(element);
342 }
343 }
344 },
346 _offsetSize: function (element, type) {
347 if (type == 'vertical' || type == 'height') {
348 return element.offsetHeight;
349 } else {
350 return element.offsetWidth;
351 }
352 },
354 /** @id MochiKit.Sortable.onEmptyHover */
355 onEmptyHover: function (element, dropon, overlap) {
356 var oldParentNode = element.parentNode;
357 var self = MochiKit.Sortable;
358 var droponOptions = self.options(dropon);
360 if (!MochiKit.DOM.isChildNode(dropon, element)) {
361 var index;
363 var children = self.findElements(dropon, {tag: droponOptions.tag,
364 only: droponOptions.only});
365 var child = null;
367 if (children) {
368 var offset = self._offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
370 for (index = 0; index < children.length; index += 1) {
371 if (offset - self._offsetSize(children[index], droponOptions.overlap) >= 0) {
372 offset -= self._offsetSize(children[index], droponOptions.overlap);
373 } else if (offset - (self._offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
374 child = index + 1 < children.length ? children[index + 1] : null;
375 break;
376 } else {
377 child = children[index];
378 break;
379 }
380 }
381 }
383 dropon.insertBefore(element, child);
385 self.options(oldParentNode).onChange(element);
386 droponOptions.onChange(element);
387 }
388 },
390 /** @id MochiKit.Sortable.unmark */
391 unmark: function () {
392 var m = MochiKit.Sortable._marker;
393 if (m) {
394 MochiKit.Style.hideElement(m);
395 }
396 },
398 /** @id MochiKit.Sortable.mark */
399 mark: function (dropon, position) {
400 // mark on ghosting only
401 var d = MochiKit.DOM;
402 var self = MochiKit.Sortable;
403 var sortable = self.options(dropon.parentNode);
404 if (sortable && !sortable.ghosting) {
405 return;
406 }
408 if (!self._marker) {
409 self._marker = d.getElement('dropmarker') ||
410 document.createElement('DIV');
411 MochiKit.Style.hideElement(self._marker);
412 d.addElementClass(self._marker, 'dropmarker');
413 self._marker.style.position = 'absolute';
414 document.getElementsByTagName('body').item(0).appendChild(self._marker);
415 }
416 var offsets = MochiKit.Position.cumulativeOffset(dropon);
417 self._marker.style.left = offsets.x + 'px';
418 self._marker.style.top = offsets.y + 'px';
420 if (position == 'after') {
421 if (sortable.overlap == 'horizontal') {
422 self._marker.style.left = (offsets.x + dropon.clientWidth) + 'px';
423 } else {
424 self._marker.style.top = (offsets.y + dropon.clientHeight) + 'px';
425 }
426 }
427 MochiKit.Style.showElement(self._marker);
428 },
430 _tree: function (element, options, parent) {
431 var self = MochiKit.Sortable;
432 var children = self.findElements(element, options) || [];
434 for (var i = 0; i < children.length; ++i) {
435 var match = children[i].id.match(options.format);
437 if (!match) {
438 continue;
439 }
441 var child = {
442 id: encodeURIComponent(match ? match[1] : null),
443 element: element,
444 parent: parent,
445 children: [],
446 position: parent.children.length,
447 container: self._findChildrenElement(children[i], options.treeTag.toUpperCase())
448 }
450 /* Get the element containing the children and recurse over it */
451 if (child.container) {
452 self._tree(child.container, options, child)
453 }
455 parent.children.push (child);
456 }
458 return parent;
459 },
461 /* Finds the first element of the given tag type within a parent element.
462 Used for finding the first LI[ST] within a L[IST]I[TEM].*/
463 _findChildrenElement: function (element, containerTag) {
464 if (element && element.hasChildNodes) {
465 containerTag = containerTag.toUpperCase();
466 for (var i = 0; i < element.childNodes.length; ++i) {
467 if (element.childNodes[i].tagName.toUpperCase() == containerTag) {
468 return element.childNodes[i];
469 }
470 }
471 }
472 return null;
473 },
475 /** @id MochiKit.Sortable.tree */
476 tree: function (element, options) {
477 element = MochiKit.DOM.getElement(element);
478 var sortableOptions = MochiKit.Sortable.options(element);
479 options = MochiKit.Base.update({
480 tag: sortableOptions.tag,
481 treeTag: sortableOptions.treeTag,
482 only: sortableOptions.only,
483 name: element.id,
484 format: sortableOptions.format
485 }, options || {});
487 var root = {
488 id: null,
489 parent: null,
490 children: new Array,
491 container: element,
492 position: 0
493 }
495 return MochiKit.Sortable._tree(element, options, root);
496 },
498 /**
499 * Specifies the sequence for the Sortable.
500 * @param {Node} element Element to use as the Sortable.
501 * @param {Object} newSequence New sequence to use.
502 * @param {Object} options Options to use fro the Sortable.
503 */
504 setSequence: function (element, newSequence, options) {
505 var self = MochiKit.Sortable;
506 var b = MochiKit.Base;
507 element = MochiKit.DOM.getElement(element);
508 options = b.update(self.options(element), options || {});
510 var nodeMap = {};
511 b.map(function (n) {
512 var m = n.id.match(options.format);
513 if (m) {
514 nodeMap[m[1]] = [n, n.parentNode];
515 }
516 n.parentNode.removeChild(n);
517 }, self.findElements(element, options));
519 b.map(function (ident) {
520 var n = nodeMap[ident];
521 if (n) {
522 n[1].appendChild(n[0]);
523 delete nodeMap[ident];
524 }
525 }, newSequence);
526 },
528 /* Construct a [i] index for a particular node */
529 _constructIndex: function (node) {
530 var index = '';
531 do {
532 if (node.id) {
533 index = '[' + node.position + ']' + index;
534 }
535 } while ((node = node.parent) != null);
536 return index;
537 },
539 /** @id MochiKit.Sortable.sequence */
540 sequence: function (element, options) {
541 element = MochiKit.DOM.getElement(element);
542 var self = MochiKit.Sortable;
543 var options = MochiKit.Base.update(self.options(element), options || {});
545 return MochiKit.Base.map(function (item) {
546 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
547 }, MochiKit.DOM.getElement(self.findElements(element, options) || []));
548 },
550 /**
551 * Serializes the content of a Sortable. Useful to send this content through a XMLHTTPRequest.
552 * These options override the Sortable options for the serialization only.
553 * @param {Node} element Element to serialize.
554 * @param {Object} options Serialization options.
555 */
556 serialize: function (element, options) {
557 element = MochiKit.DOM.getElement(element);
558 var self = MochiKit.Sortable;
559 options = MochiKit.Base.update(self.options(element), options || {});
560 var name = encodeURIComponent(options.name || element.id);
562 if (options.tree) {
563 return MochiKit.Base.flattenArray(MochiKit.Base.map(function (item) {
564 return [name + self._constructIndex(item) + "[id]=" +
565 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
566 }, self.tree(element, options).children)).join('&');
567 } else {
568 return MochiKit.Base.map(function (item) {
569 return name + "[]=" + encodeURIComponent(item);
570 }, self.sequence(element, options)).join('&');
571 }
572 }
573 });
575 // trunk compatibility
576 MochiKit.Sortable.Sortable = MochiKit.Sortable;
578 MochiKit.Sortable.__new__ = function () {
579 MochiKit.Base.nameFunctions(this);
581 this.EXPORT_TAGS = {
582 ":common": this.EXPORT,
583 ":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
584 };
585 };
587 MochiKit.Sortable.__new__();
589 MochiKit.Base._exportSymbols(this, MochiKit.Sortable);