1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/tests/MochiKit-1.4.2/MochiKit/Sortable.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,589 @@ 1.4 +/*** 1.5 +Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 1.6 + Mochi-ized By Thomas Herve (_firstname_@nimail.org) 1.7 + 1.8 +See scriptaculous.js for full license. 1.9 + 1.10 +***/ 1.11 + 1.12 +MochiKit.Base._deps('Sortable', ['Base', 'Iter', 'DOM', 'Position', 'DragAndDrop']); 1.13 + 1.14 +MochiKit.Sortable.NAME = 'MochiKit.Sortable'; 1.15 +MochiKit.Sortable.VERSION = '1.4.2'; 1.16 + 1.17 +MochiKit.Sortable.__repr__ = function () { 1.18 + return '[' + this.NAME + ' ' + this.VERSION + ']'; 1.19 +}; 1.20 + 1.21 +MochiKit.Sortable.toString = function () { 1.22 + return this.__repr__(); 1.23 +}; 1.24 + 1.25 +MochiKit.Sortable.EXPORT = [ 1.26 +]; 1.27 + 1.28 +MochiKit.Sortable.EXPORT_OK = [ 1.29 +]; 1.30 + 1.31 +MochiKit.Base.update(MochiKit.Sortable, { 1.32 + /*** 1.33 + 1.34 + Manage sortables. Mainly use the create function to add a sortable. 1.35 + 1.36 + ***/ 1.37 + sortables: {}, 1.38 + 1.39 + _findRootElement: function (element) { 1.40 + while (element.tagName.toUpperCase() != "BODY") { 1.41 + if (element.id && MochiKit.Sortable.sortables[element.id]) { 1.42 + return element; 1.43 + } 1.44 + element = element.parentNode; 1.45 + } 1.46 + }, 1.47 + 1.48 + _createElementId: function(element) { 1.49 + if (element.id == null || element.id == "") { 1.50 + var d = MochiKit.DOM; 1.51 + var id; 1.52 + var count = 1; 1.53 + while (d.getElement(id = "sortable" + count) != null) { 1.54 + count += 1; 1.55 + } 1.56 + d.setNodeAttribute(element, "id", id); 1.57 + } 1.58 + }, 1.59 + 1.60 + /** @id MochiKit.Sortable.options */ 1.61 + options: function (element) { 1.62 + element = MochiKit.Sortable._findRootElement(MochiKit.DOM.getElement(element)); 1.63 + if (!element) { 1.64 + return; 1.65 + } 1.66 + return MochiKit.Sortable.sortables[element.id]; 1.67 + }, 1.68 + 1.69 + /** @id MochiKit.Sortable.destroy */ 1.70 + destroy: function (element){ 1.71 + var s = MochiKit.Sortable.options(element); 1.72 + var b = MochiKit.Base; 1.73 + var d = MochiKit.DragAndDrop; 1.74 + 1.75 + if (s) { 1.76 + MochiKit.Signal.disconnect(s.startHandle); 1.77 + MochiKit.Signal.disconnect(s.endHandle); 1.78 + b.map(function (dr) { 1.79 + d.Droppables.remove(dr); 1.80 + }, s.droppables); 1.81 + b.map(function (dr) { 1.82 + dr.destroy(); 1.83 + }, s.draggables); 1.84 + 1.85 + delete MochiKit.Sortable.sortables[s.element.id]; 1.86 + } 1.87 + }, 1.88 + 1.89 + /** @id MochiKit.Sortable.create */ 1.90 + create: function (element, options) { 1.91 + element = MochiKit.DOM.getElement(element); 1.92 + var self = MochiKit.Sortable; 1.93 + self._createElementId(element); 1.94 + 1.95 + /** @id MochiKit.Sortable.options */ 1.96 + options = MochiKit.Base.update({ 1.97 + 1.98 + /** @id MochiKit.Sortable.element */ 1.99 + element: element, 1.100 + 1.101 + /** @id MochiKit.Sortable.tag */ 1.102 + tag: 'li', // assumes li children, override with tag: 'tagname' 1.103 + 1.104 + /** @id MochiKit.Sortable.dropOnEmpty */ 1.105 + dropOnEmpty: false, 1.106 + 1.107 + /** @id MochiKit.Sortable.tree */ 1.108 + tree: false, 1.109 + 1.110 + /** @id MochiKit.Sortable.treeTag */ 1.111 + treeTag: 'ul', 1.112 + 1.113 + /** @id MochiKit.Sortable.overlap */ 1.114 + overlap: 'vertical', // one of 'vertical', 'horizontal' 1.115 + 1.116 + /** @id MochiKit.Sortable.constraint */ 1.117 + constraint: 'vertical', // one of 'vertical', 'horizontal', false 1.118 + // also takes array of elements (or ids); or false 1.119 + 1.120 + /** @id MochiKit.Sortable.containment */ 1.121 + containment: [element], 1.122 + 1.123 + /** @id MochiKit.Sortable.handle */ 1.124 + handle: false, // or a CSS class 1.125 + 1.126 + /** @id MochiKit.Sortable.only */ 1.127 + only: false, 1.128 + 1.129 + /** @id MochiKit.Sortable.hoverclass */ 1.130 + hoverclass: null, 1.131 + 1.132 + /** @id MochiKit.Sortable.ghosting */ 1.133 + ghosting: false, 1.134 + 1.135 + /** @id MochiKit.Sortable.scroll */ 1.136 + scroll: false, 1.137 + 1.138 + /** @id MochiKit.Sortable.scrollSensitivity */ 1.139 + scrollSensitivity: 20, 1.140 + 1.141 + /** @id MochiKit.Sortable.scrollSpeed */ 1.142 + scrollSpeed: 15, 1.143 + 1.144 + /** @id MochiKit.Sortable.format */ 1.145 + format: /^[^_]*_(.*)$/, 1.146 + 1.147 + /** @id MochiKit.Sortable.onChange */ 1.148 + onChange: MochiKit.Base.noop, 1.149 + 1.150 + /** @id MochiKit.Sortable.onUpdate */ 1.151 + onUpdate: MochiKit.Base.noop, 1.152 + 1.153 + /** @id MochiKit.Sortable.accept */ 1.154 + accept: null 1.155 + }, options); 1.156 + 1.157 + // clear any old sortable with same element 1.158 + self.destroy(element); 1.159 + 1.160 + // build options for the draggables 1.161 + var options_for_draggable = { 1.162 + revert: true, 1.163 + ghosting: options.ghosting, 1.164 + scroll: options.scroll, 1.165 + scrollSensitivity: options.scrollSensitivity, 1.166 + scrollSpeed: options.scrollSpeed, 1.167 + constraint: options.constraint, 1.168 + handle: options.handle 1.169 + }; 1.170 + 1.171 + if (options.starteffect) { 1.172 + options_for_draggable.starteffect = options.starteffect; 1.173 + } 1.174 + 1.175 + if (options.reverteffect) { 1.176 + options_for_draggable.reverteffect = options.reverteffect; 1.177 + } else if (options.ghosting) { 1.178 + options_for_draggable.reverteffect = function (innerelement) { 1.179 + innerelement.style.top = 0; 1.180 + innerelement.style.left = 0; 1.181 + }; 1.182 + } 1.183 + 1.184 + if (options.endeffect) { 1.185 + options_for_draggable.endeffect = options.endeffect; 1.186 + } 1.187 + 1.188 + if (options.zindex) { 1.189 + options_for_draggable.zindex = options.zindex; 1.190 + } 1.191 + 1.192 + // build options for the droppables 1.193 + var options_for_droppable = { 1.194 + overlap: options.overlap, 1.195 + containment: options.containment, 1.196 + hoverclass: options.hoverclass, 1.197 + onhover: self.onHover, 1.198 + tree: options.tree, 1.199 + accept: options.accept 1.200 + } 1.201 + 1.202 + var options_for_tree = { 1.203 + onhover: self.onEmptyHover, 1.204 + overlap: options.overlap, 1.205 + containment: options.containment, 1.206 + hoverclass: options.hoverclass, 1.207 + accept: options.accept 1.208 + } 1.209 + 1.210 + // fix for gecko engine 1.211 + MochiKit.DOM.removeEmptyTextNodes(element); 1.212 + 1.213 + options.draggables = []; 1.214 + options.droppables = []; 1.215 + 1.216 + // drop on empty handling 1.217 + if (options.dropOnEmpty || options.tree) { 1.218 + new MochiKit.DragAndDrop.Droppable(element, options_for_tree); 1.219 + options.droppables.push(element); 1.220 + } 1.221 + MochiKit.Base.map(function (e) { 1.222 + // handles are per-draggable 1.223 + var handle = options.handle ? 1.224 + MochiKit.DOM.getFirstElementByTagAndClassName(null, 1.225 + options.handle, e) : e; 1.226 + options.draggables.push( 1.227 + new MochiKit.DragAndDrop.Draggable(e, 1.228 + MochiKit.Base.update(options_for_draggable, 1.229 + {handle: handle}))); 1.230 + new MochiKit.DragAndDrop.Droppable(e, options_for_droppable); 1.231 + if (options.tree) { 1.232 + e.treeNode = element; 1.233 + } 1.234 + options.droppables.push(e); 1.235 + }, (self.findElements(element, options) || [])); 1.236 + 1.237 + if (options.tree) { 1.238 + MochiKit.Base.map(function (e) { 1.239 + new MochiKit.DragAndDrop.Droppable(e, options_for_tree); 1.240 + e.treeNode = element; 1.241 + options.droppables.push(e); 1.242 + }, (self.findTreeElements(element, options) || [])); 1.243 + } 1.244 + 1.245 + // keep reference 1.246 + self.sortables[element.id] = options; 1.247 + 1.248 + options.lastValue = self.serialize(element); 1.249 + options.startHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'start', 1.250 + MochiKit.Base.partial(self.onStart, element)); 1.251 + options.endHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'end', 1.252 + MochiKit.Base.partial(self.onEnd, element)); 1.253 + }, 1.254 + 1.255 + /** @id MochiKit.Sortable.onStart */ 1.256 + onStart: function (element, draggable) { 1.257 + var self = MochiKit.Sortable; 1.258 + var options = self.options(element); 1.259 + options.lastValue = self.serialize(options.element); 1.260 + }, 1.261 + 1.262 + /** @id MochiKit.Sortable.onEnd */ 1.263 + onEnd: function (element, draggable) { 1.264 + var self = MochiKit.Sortable; 1.265 + self.unmark(); 1.266 + var options = self.options(element); 1.267 + if (options.lastValue != self.serialize(options.element)) { 1.268 + options.onUpdate(options.element); 1.269 + } 1.270 + }, 1.271 + 1.272 + // return all suitable-for-sortable elements in a guaranteed order 1.273 + 1.274 + /** @id MochiKit.Sortable.findElements */ 1.275 + findElements: function (element, options) { 1.276 + return MochiKit.Sortable.findChildren(element, options.only, options.tree, options.tag); 1.277 + }, 1.278 + 1.279 + /** @id MochiKit.Sortable.findTreeElements */ 1.280 + findTreeElements: function (element, options) { 1.281 + return MochiKit.Sortable.findChildren( 1.282 + element, options.only, options.tree ? true : false, options.treeTag); 1.283 + }, 1.284 + 1.285 + /** @id MochiKit.Sortable.findChildren */ 1.286 + findChildren: function (element, only, recursive, tagName) { 1.287 + if (!element.hasChildNodes()) { 1.288 + return null; 1.289 + } 1.290 + tagName = tagName.toUpperCase(); 1.291 + if (only) { 1.292 + only = MochiKit.Base.flattenArray([only]); 1.293 + } 1.294 + var elements = []; 1.295 + MochiKit.Base.map(function (e) { 1.296 + if (e.tagName && 1.297 + e.tagName.toUpperCase() == tagName && 1.298 + (!only || 1.299 + MochiKit.Iter.some(only, function (c) { 1.300 + return MochiKit.DOM.hasElementClass(e, c); 1.301 + }))) { 1.302 + elements.push(e); 1.303 + } 1.304 + if (recursive) { 1.305 + var grandchildren = MochiKit.Sortable.findChildren(e, only, recursive, tagName); 1.306 + if (grandchildren && grandchildren.length > 0) { 1.307 + elements = elements.concat(grandchildren); 1.308 + } 1.309 + } 1.310 + }, element.childNodes); 1.311 + return elements; 1.312 + }, 1.313 + 1.314 + /** @id MochiKit.Sortable.onHover */ 1.315 + onHover: function (element, dropon, overlap) { 1.316 + if (MochiKit.DOM.isChildNode(dropon, element)) { 1.317 + return; 1.318 + } 1.319 + var self = MochiKit.Sortable; 1.320 + 1.321 + if (overlap > .33 && overlap < .66 && self.options(dropon).tree) { 1.322 + return; 1.323 + } else if (overlap > 0.5) { 1.324 + self.mark(dropon, 'before'); 1.325 + if (dropon.previousSibling != element) { 1.326 + var oldParentNode = element.parentNode; 1.327 + element.style.visibility = 'hidden'; // fix gecko rendering 1.328 + dropon.parentNode.insertBefore(element, dropon); 1.329 + if (dropon.parentNode != oldParentNode) { 1.330 + self.options(oldParentNode).onChange(element); 1.331 + } 1.332 + self.options(dropon.parentNode).onChange(element); 1.333 + } 1.334 + } else { 1.335 + self.mark(dropon, 'after'); 1.336 + var nextElement = dropon.nextSibling || null; 1.337 + if (nextElement != element) { 1.338 + var oldParentNode = element.parentNode; 1.339 + element.style.visibility = 'hidden'; // fix gecko rendering 1.340 + dropon.parentNode.insertBefore(element, nextElement); 1.341 + if (dropon.parentNode != oldParentNode) { 1.342 + self.options(oldParentNode).onChange(element); 1.343 + } 1.344 + self.options(dropon.parentNode).onChange(element); 1.345 + } 1.346 + } 1.347 + }, 1.348 + 1.349 + _offsetSize: function (element, type) { 1.350 + if (type == 'vertical' || type == 'height') { 1.351 + return element.offsetHeight; 1.352 + } else { 1.353 + return element.offsetWidth; 1.354 + } 1.355 + }, 1.356 + 1.357 + /** @id MochiKit.Sortable.onEmptyHover */ 1.358 + onEmptyHover: function (element, dropon, overlap) { 1.359 + var oldParentNode = element.parentNode; 1.360 + var self = MochiKit.Sortable; 1.361 + var droponOptions = self.options(dropon); 1.362 + 1.363 + if (!MochiKit.DOM.isChildNode(dropon, element)) { 1.364 + var index; 1.365 + 1.366 + var children = self.findElements(dropon, {tag: droponOptions.tag, 1.367 + only: droponOptions.only}); 1.368 + var child = null; 1.369 + 1.370 + if (children) { 1.371 + var offset = self._offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); 1.372 + 1.373 + for (index = 0; index < children.length; index += 1) { 1.374 + if (offset - self._offsetSize(children[index], droponOptions.overlap) >= 0) { 1.375 + offset -= self._offsetSize(children[index], droponOptions.overlap); 1.376 + } else if (offset - (self._offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { 1.377 + child = index + 1 < children.length ? children[index + 1] : null; 1.378 + break; 1.379 + } else { 1.380 + child = children[index]; 1.381 + break; 1.382 + } 1.383 + } 1.384 + } 1.385 + 1.386 + dropon.insertBefore(element, child); 1.387 + 1.388 + self.options(oldParentNode).onChange(element); 1.389 + droponOptions.onChange(element); 1.390 + } 1.391 + }, 1.392 + 1.393 + /** @id MochiKit.Sortable.unmark */ 1.394 + unmark: function () { 1.395 + var m = MochiKit.Sortable._marker; 1.396 + if (m) { 1.397 + MochiKit.Style.hideElement(m); 1.398 + } 1.399 + }, 1.400 + 1.401 + /** @id MochiKit.Sortable.mark */ 1.402 + mark: function (dropon, position) { 1.403 + // mark on ghosting only 1.404 + var d = MochiKit.DOM; 1.405 + var self = MochiKit.Sortable; 1.406 + var sortable = self.options(dropon.parentNode); 1.407 + if (sortable && !sortable.ghosting) { 1.408 + return; 1.409 + } 1.410 + 1.411 + if (!self._marker) { 1.412 + self._marker = d.getElement('dropmarker') || 1.413 + document.createElement('DIV'); 1.414 + MochiKit.Style.hideElement(self._marker); 1.415 + d.addElementClass(self._marker, 'dropmarker'); 1.416 + self._marker.style.position = 'absolute'; 1.417 + document.getElementsByTagName('body').item(0).appendChild(self._marker); 1.418 + } 1.419 + var offsets = MochiKit.Position.cumulativeOffset(dropon); 1.420 + self._marker.style.left = offsets.x + 'px'; 1.421 + self._marker.style.top = offsets.y + 'px'; 1.422 + 1.423 + if (position == 'after') { 1.424 + if (sortable.overlap == 'horizontal') { 1.425 + self._marker.style.left = (offsets.x + dropon.clientWidth) + 'px'; 1.426 + } else { 1.427 + self._marker.style.top = (offsets.y + dropon.clientHeight) + 'px'; 1.428 + } 1.429 + } 1.430 + MochiKit.Style.showElement(self._marker); 1.431 + }, 1.432 + 1.433 + _tree: function (element, options, parent) { 1.434 + var self = MochiKit.Sortable; 1.435 + var children = self.findElements(element, options) || []; 1.436 + 1.437 + for (var i = 0; i < children.length; ++i) { 1.438 + var match = children[i].id.match(options.format); 1.439 + 1.440 + if (!match) { 1.441 + continue; 1.442 + } 1.443 + 1.444 + var child = { 1.445 + id: encodeURIComponent(match ? match[1] : null), 1.446 + element: element, 1.447 + parent: parent, 1.448 + children: [], 1.449 + position: parent.children.length, 1.450 + container: self._findChildrenElement(children[i], options.treeTag.toUpperCase()) 1.451 + } 1.452 + 1.453 + /* Get the element containing the children and recurse over it */ 1.454 + if (child.container) { 1.455 + self._tree(child.container, options, child) 1.456 + } 1.457 + 1.458 + parent.children.push (child); 1.459 + } 1.460 + 1.461 + return parent; 1.462 + }, 1.463 + 1.464 + /* Finds the first element of the given tag type within a parent element. 1.465 + Used for finding the first LI[ST] within a L[IST]I[TEM].*/ 1.466 + _findChildrenElement: function (element, containerTag) { 1.467 + if (element && element.hasChildNodes) { 1.468 + containerTag = containerTag.toUpperCase(); 1.469 + for (var i = 0; i < element.childNodes.length; ++i) { 1.470 + if (element.childNodes[i].tagName.toUpperCase() == containerTag) { 1.471 + return element.childNodes[i]; 1.472 + } 1.473 + } 1.474 + } 1.475 + return null; 1.476 + }, 1.477 + 1.478 + /** @id MochiKit.Sortable.tree */ 1.479 + tree: function (element, options) { 1.480 + element = MochiKit.DOM.getElement(element); 1.481 + var sortableOptions = MochiKit.Sortable.options(element); 1.482 + options = MochiKit.Base.update({ 1.483 + tag: sortableOptions.tag, 1.484 + treeTag: sortableOptions.treeTag, 1.485 + only: sortableOptions.only, 1.486 + name: element.id, 1.487 + format: sortableOptions.format 1.488 + }, options || {}); 1.489 + 1.490 + var root = { 1.491 + id: null, 1.492 + parent: null, 1.493 + children: new Array, 1.494 + container: element, 1.495 + position: 0 1.496 + } 1.497 + 1.498 + return MochiKit.Sortable._tree(element, options, root); 1.499 + }, 1.500 + 1.501 + /** 1.502 + * Specifies the sequence for the Sortable. 1.503 + * @param {Node} element Element to use as the Sortable. 1.504 + * @param {Object} newSequence New sequence to use. 1.505 + * @param {Object} options Options to use fro the Sortable. 1.506 + */ 1.507 + setSequence: function (element, newSequence, options) { 1.508 + var self = MochiKit.Sortable; 1.509 + var b = MochiKit.Base; 1.510 + element = MochiKit.DOM.getElement(element); 1.511 + options = b.update(self.options(element), options || {}); 1.512 + 1.513 + var nodeMap = {}; 1.514 + b.map(function (n) { 1.515 + var m = n.id.match(options.format); 1.516 + if (m) { 1.517 + nodeMap[m[1]] = [n, n.parentNode]; 1.518 + } 1.519 + n.parentNode.removeChild(n); 1.520 + }, self.findElements(element, options)); 1.521 + 1.522 + b.map(function (ident) { 1.523 + var n = nodeMap[ident]; 1.524 + if (n) { 1.525 + n[1].appendChild(n[0]); 1.526 + delete nodeMap[ident]; 1.527 + } 1.528 + }, newSequence); 1.529 + }, 1.530 + 1.531 + /* Construct a [i] index for a particular node */ 1.532 + _constructIndex: function (node) { 1.533 + var index = ''; 1.534 + do { 1.535 + if (node.id) { 1.536 + index = '[' + node.position + ']' + index; 1.537 + } 1.538 + } while ((node = node.parent) != null); 1.539 + return index; 1.540 + }, 1.541 + 1.542 + /** @id MochiKit.Sortable.sequence */ 1.543 + sequence: function (element, options) { 1.544 + element = MochiKit.DOM.getElement(element); 1.545 + var self = MochiKit.Sortable; 1.546 + var options = MochiKit.Base.update(self.options(element), options || {}); 1.547 + 1.548 + return MochiKit.Base.map(function (item) { 1.549 + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; 1.550 + }, MochiKit.DOM.getElement(self.findElements(element, options) || [])); 1.551 + }, 1.552 + 1.553 + /** 1.554 + * Serializes the content of a Sortable. Useful to send this content through a XMLHTTPRequest. 1.555 + * These options override the Sortable options for the serialization only. 1.556 + * @param {Node} element Element to serialize. 1.557 + * @param {Object} options Serialization options. 1.558 + */ 1.559 + serialize: function (element, options) { 1.560 + element = MochiKit.DOM.getElement(element); 1.561 + var self = MochiKit.Sortable; 1.562 + options = MochiKit.Base.update(self.options(element), options || {}); 1.563 + var name = encodeURIComponent(options.name || element.id); 1.564 + 1.565 + if (options.tree) { 1.566 + return MochiKit.Base.flattenArray(MochiKit.Base.map(function (item) { 1.567 + return [name + self._constructIndex(item) + "[id]=" + 1.568 + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); 1.569 + }, self.tree(element, options).children)).join('&'); 1.570 + } else { 1.571 + return MochiKit.Base.map(function (item) { 1.572 + return name + "[]=" + encodeURIComponent(item); 1.573 + }, self.sequence(element, options)).join('&'); 1.574 + } 1.575 + } 1.576 +}); 1.577 + 1.578 +// trunk compatibility 1.579 +MochiKit.Sortable.Sortable = MochiKit.Sortable; 1.580 + 1.581 +MochiKit.Sortable.__new__ = function () { 1.582 + MochiKit.Base.nameFunctions(this); 1.583 + 1.584 + this.EXPORT_TAGS = { 1.585 + ":common": this.EXPORT, 1.586 + ":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK) 1.587 + }; 1.588 +}; 1.589 + 1.590 +MochiKit.Sortable.__new__(); 1.591 + 1.592 +MochiKit.Base._exportSymbols(this, MochiKit.Sortable);