123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- /***
- Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
- Mochi-ized By Thomas Herve (_firstname_@nimail.org)
- See scriptaculous.js for full license.
- ***/
- if (typeof(dojo) != 'undefined') {
- dojo.provide('MochiKit.DragAndDrop');
- dojo.require('MochiKit.Base');
- dojo.require('MochiKit.DOM');
- dojo.require('MochiKit.Iter');
- }
- if (typeof(JSAN) != 'undefined') {
- JSAN.use("MochiKit.Base", []);
- JSAN.use("MochiKit.DOM", []);
- JSAN.use("MochiKit.Iter", []);
- }
- try {
- if (typeof(MochiKit.Base) == 'undefined' ||
- typeof(MochiKit.DOM) == 'undefined' ||
- typeof(MochiKit.Iter) == 'undefined') {
- throw "";
- }
- } catch (e) {
- throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM and MochiKit.Iter!";
- }
- if (typeof(MochiKit.Sortable) == 'undefined') {
- MochiKit.Sortable = {};
- }
- MochiKit.Sortable.NAME = 'MochiKit.Sortable';
- MochiKit.Sortable.VERSION = '1.4';
- MochiKit.Sortable.__repr__ = function () {
- return '[' + this.NAME + ' ' + this.VERSION + ']';
- };
- MochiKit.Sortable.toString = function () {
- return this.__repr__();
- };
- MochiKit.Sortable.EXPORT = [
- ];
- MochiKit.DragAndDrop.EXPORT_OK = [
- "Sortable"
- ];
- MochiKit.Sortable.Sortable = {
- /***
- Manage sortables. Mainly use the create function to add a sortable.
- ***/
- sortables: {},
- _findRootElement: function (element) {
- while (element.tagName.toUpperCase() != "BODY") {
- if (element.id && MochiKit.Sortable.Sortable.sortables[element.id]) {
- return element;
- }
- element = element.parentNode;
- }
- },
- /** @id MochiKit.Sortable.Sortable.options */
- options: function (element) {
- element = MochiKit.Sortable.Sortable._findRootElement(MochiKit.DOM.getElement(element));
- if (!element) {
- return;
- }
- return MochiKit.Sortable.Sortable.sortables[element.id];
- },
- /** @id MochiKit.Sortable.Sortable.destroy */
- destroy: function (element){
- var s = MochiKit.Sortable.Sortable.options(element);
- var b = MochiKit.Base;
- var d = MochiKit.DragAndDrop;
- if (s) {
- MochiKit.Signal.disconnect(s.startHandle);
- MochiKit.Signal.disconnect(s.endHandle);
- b.map(function (dr) {
- d.Droppables.remove(dr);
- }, s.droppables);
- b.map(function (dr) {
- dr.destroy();
- }, s.draggables);
- delete MochiKit.Sortable.Sortable.sortables[s.element.id];
- }
- },
- /** @id MochiKit.Sortable.Sortable.create */
- create: function (element, options) {
- element = MochiKit.DOM.getElement(element);
- var self = MochiKit.Sortable.Sortable;
-
- /** @id MochiKit.Sortable.Sortable.options */
- options = MochiKit.Base.update({
-
- /** @id MochiKit.Sortable.Sortable.element */
- element: element,
-
- /** @id MochiKit.Sortable.Sortable.tag */
- tag: 'li', // assumes li children, override with tag: 'tagname'
-
- /** @id MochiKit.Sortable.Sortable.dropOnEmpty */
- dropOnEmpty: false,
-
- /** @id MochiKit.Sortable.Sortable.tree */
- tree: false,
-
- /** @id MochiKit.Sortable.Sortable.treeTag */
- treeTag: 'ul',
-
- /** @id MochiKit.Sortable.Sortable.overlap */
- overlap: 'vertical', // one of 'vertical', 'horizontal'
-
- /** @id MochiKit.Sortable.Sortable.constraint */
- constraint: 'vertical', // one of 'vertical', 'horizontal', false
- // also takes array of elements (or ids); or false
-
- /** @id MochiKit.Sortable.Sortable.containment */
- containment: [element],
-
- /** @id MochiKit.Sortable.Sortable.handle */
- handle: false, // or a CSS class
-
- /** @id MochiKit.Sortable.Sortable.only */
- only: false,
-
- /** @id MochiKit.Sortable.Sortable.hoverclass */
- hoverclass: null,
-
- /** @id MochiKit.Sortable.Sortable.ghosting */
- ghosting: false,
-
- /** @id MochiKit.Sortable.Sortable.scroll */
- scroll: false,
-
- /** @id MochiKit.Sortable.Sortable.scrollSensitivity */
- scrollSensitivity: 20,
-
- /** @id MochiKit.Sortable.Sortable.scrollSpeed */
- scrollSpeed: 15,
-
- /** @id MochiKit.Sortable.Sortable.format */
- format: /^[^_]*_(.*)$/,
-
- /** @id MochiKit.Sortable.Sortable.onChange */
- onChange: MochiKit.Base.noop,
-
- /** @id MochiKit.Sortable.Sortable.onUpdate */
- onUpdate: MochiKit.Base.noop,
-
- /** @id MochiKit.Sortable.Sortable.accept */
- accept: null
- }, options);
- // clear any old sortable with same element
- self.destroy(element);
- // build options for the draggables
- var options_for_draggable = {
- revert: true,
- ghosting: options.ghosting,
- scroll: options.scroll,
- scrollSensitivity: options.scrollSensitivity,
- scrollSpeed: options.scrollSpeed,
- constraint: options.constraint,
- handle: options.handle
- };
- if (options.starteffect) {
- options_for_draggable.starteffect = options.starteffect;
- }
- if (options.reverteffect) {
- options_for_draggable.reverteffect = options.reverteffect;
- } else if (options.ghosting) {
- options_for_draggable.reverteffect = function (innerelement) {
- innerelement.style.top = 0;
- innerelement.style.left = 0;
- };
- }
- if (options.endeffect) {
- options_for_draggable.endeffect = options.endeffect;
- }
- if (options.zindex) {
- options_for_draggable.zindex = options.zindex;
- }
- // build options for the droppables
- var options_for_droppable = {
- overlap: options.overlap,
- containment: options.containment,
- hoverclass: options.hoverclass,
- onhover: self.onHover,
- tree: options.tree,
- accept: options.accept
- }
- var options_for_tree = {
- onhover: self.onEmptyHover,
- overlap: options.overlap,
- containment: options.containment,
- hoverclass: options.hoverclass,
- accept: options.accept
- }
- // fix for gecko engine
- MochiKit.DOM.removeEmptyTextNodes(element);
- options.draggables = [];
- options.droppables = [];
- // drop on empty handling
- if (options.dropOnEmpty || options.tree) {
- new MochiKit.DragAndDrop.Droppable(element, options_for_tree);
- options.droppables.push(element);
- }
- MochiKit.Base.map(function (e) {
- // handles are per-draggable
- var handle = options.handle ?
- MochiKit.DOM.getFirstElementByTagAndClassName(null,
- options.handle, e) : e;
- options.draggables.push(
- new MochiKit.DragAndDrop.Draggable(e,
- MochiKit.Base.update(options_for_draggable,
- {handle: handle})));
- new MochiKit.DragAndDrop.Droppable(e, options_for_droppable);
- if (options.tree) {
- e.treeNode = element;
- }
- options.droppables.push(e);
- }, (self.findElements(element, options) || []));
- if (options.tree) {
- MochiKit.Base.map(function (e) {
- new MochiKit.DragAndDrop.Droppable(e, options_for_tree);
- e.treeNode = element;
- options.droppables.push(e);
- }, (self.findTreeElements(element, options) || []));
- }
- // keep reference
- self.sortables[element.id] = options;
- options.lastValue = self.serialize(element);
- options.startHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'start',
- MochiKit.Base.partial(self.onStart, element));
- options.endHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'end',
- MochiKit.Base.partial(self.onEnd, element));
- },
- /** @id MochiKit.Sortable.Sortable.onStart */
- onStart: function (element, draggable) {
- var self = MochiKit.Sortable.Sortable;
- var options = self.options(element);
- options.lastValue = self.serialize(options.element);
- },
- /** @id MochiKit.Sortable.Sortable.onEnd */
- onEnd: function (element, draggable) {
- var self = MochiKit.Sortable.Sortable;
- self.unmark();
- var options = self.options(element);
- if (options.lastValue != self.serialize(options.element)) {
- options.onUpdate(options.element);
- }
- },
- // return all suitable-for-sortable elements in a guaranteed order
-
- /** @id MochiKit.Sortable.Sortable.findElements */
- findElements: function (element, options) {
- return MochiKit.Sortable.Sortable.findChildren(
- element, options.only, options.tree ? true : false, options.tag);
- },
- /** @id MochiKit.Sortable.Sortable.findTreeElements */
- findTreeElements: function (element, options) {
- return MochiKit.Sortable.Sortable.findChildren(
- element, options.only, options.tree ? true : false, options.treeTag);
- },
- /** @id MochiKit.Sortable.Sortable.findChildren */
- findChildren: function (element, only, recursive, tagName) {
- if (!element.hasChildNodes()) {
- return null;
- }
- tagName = tagName.toUpperCase();
- if (only) {
- only = MochiKit.Base.flattenArray([only]);
- }
- var elements = [];
- MochiKit.Base.map(function (e) {
- if (e.tagName &&
- e.tagName.toUpperCase() == tagName &&
- (!only ||
- MochiKit.Iter.some(only, function (c) {
- return MochiKit.DOM.hasElementClass(e, c);
- }))) {
- elements.push(e);
- }
- if (recursive) {
- var grandchildren = MochiKit.Sortable.Sortable.findChildren(e, only, recursive, tagName);
- if (grandchildren && grandchildren.length > 0) {
- elements = elements.concat(grandchildren);
- }
- }
- }, element.childNodes);
- return elements;
- },
- /** @id MochiKit.Sortable.Sortable.onHover */
- onHover: function (element, dropon, overlap) {
- if (MochiKit.DOM.isParent(dropon, element)) {
- return;
- }
- var self = MochiKit.Sortable.Sortable;
- if (overlap > .33 && overlap < .66 && self.options(dropon).tree) {
- return;
- } else if (overlap > 0.5) {
- self.mark(dropon, 'before');
- if (dropon.previousSibling != element) {
- var oldParentNode = element.parentNode;
- element.style.visibility = 'hidden'; // fix gecko rendering
- dropon.parentNode.insertBefore(element, dropon);
- if (dropon.parentNode != oldParentNode) {
- self.options(oldParentNode).onChange(element);
- }
- self.options(dropon.parentNode).onChange(element);
- }
- } else {
- self.mark(dropon, 'after');
- var nextElement = dropon.nextSibling || null;
- if (nextElement != element) {
- var oldParentNode = element.parentNode;
- element.style.visibility = 'hidden'; // fix gecko rendering
- dropon.parentNode.insertBefore(element, nextElement);
- if (dropon.parentNode != oldParentNode) {
- self.options(oldParentNode).onChange(element);
- }
- self.options(dropon.parentNode).onChange(element);
- }
- }
- },
- _offsetSize: function (element, type) {
- if (type == 'vertical' || type == 'height') {
- return element.offsetHeight;
- } else {
- return element.offsetWidth;
- }
- },
- /** @id MochiKit.Sortable.Sortable.onEmptyHover */
- onEmptyHover: function (element, dropon, overlap) {
- var oldParentNode = element.parentNode;
- var self = MochiKit.Sortable.Sortable;
- var droponOptions = self.options(dropon);
- if (!MochiKit.DOM.isParent(dropon, element)) {
- var index;
- var children = self.findElements(dropon, {tag: droponOptions.tag,
- only: droponOptions.only});
- var child = null;
- if (children) {
- var offset = self._offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
- for (index = 0; index < children.length; index += 1) {
- if (offset - self._offsetSize(children[index], droponOptions.overlap) >= 0) {
- offset -= self._offsetSize(children[index], droponOptions.overlap);
- } else if (offset - (self._offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
- child = index + 1 < children.length ? children[index + 1] : null;
- break;
- } else {
- child = children[index];
- break;
- }
- }
- }
- dropon.insertBefore(element, child);
- self.options(oldParentNode).onChange(element);
- droponOptions.onChange(element);
- }
- },
- /** @id MochiKit.Sortable.Sortable.unmark */
- unmark: function () {
- var m = MochiKit.Sortable.Sortable._marker;
- if (m) {
- MochiKit.Style.hideElement(m);
- }
- },
- /** @id MochiKit.Sortable.Sortable.mark */
- mark: function (dropon, position) {
- // mark on ghosting only
- var d = MochiKit.DOM;
- var self = MochiKit.Sortable.Sortable;
- var sortable = self.options(dropon.parentNode);
- if (sortable && !sortable.ghosting) {
- return;
- }
- if (!self._marker) {
- self._marker = d.getElement('dropmarker') ||
- document.createElement('DIV');
- MochiKit.Style.hideElement(self._marker);
- d.addElementClass(self._marker, 'dropmarker');
- self._marker.style.position = 'absolute';
- document.getElementsByTagName('body').item(0).appendChild(self._marker);
- }
- var offsets = MochiKit.Position.cumulativeOffset(dropon);
- self._marker.style.left = offsets.x + 'px';
- self._marker.style.top = offsets.y + 'px';
- if (position == 'after') {
- if (sortable.overlap == 'horizontal') {
- self._marker.style.left = (offsets.x + dropon.clientWidth) + 'px';
- } else {
- self._marker.style.top = (offsets.y + dropon.clientHeight) + 'px';
- }
- }
- MochiKit.Style.showElement(self._marker);
- },
- _tree: function (element, options, parent) {
- var self = MochiKit.Sortable.Sortable;
- var children = self.findElements(element, options) || [];
- for (var i = 0; i < children.length; ++i) {
- var match = children[i].id.match(options.format);
- if (!match) {
- continue;
- }
- var child = {
- id: encodeURIComponent(match ? match[1] : null),
- element: element,
- parent: parent,
- children: [],
- position: parent.children.length,
- container: self._findChildrenElement(children[i], options.treeTag.toUpperCase())
- }
- /* Get the element containing the children and recurse over it */
- if (child.container) {
- self._tree(child.container, options, child)
- }
- parent.children.push (child);
- }
- return parent;
- },
- /* Finds the first element of the given tag type within a parent element.
- Used for finding the first LI[ST] within a L[IST]I[TEM].*/
- _findChildrenElement: function (element, containerTag) {
- if (element && element.hasChildNodes) {
- containerTag = containerTag.toUpperCase();
- for (var i = 0; i < element.childNodes.length; ++i) {
- if (element.childNodes[i].tagName.toUpperCase() == containerTag) {
- return element.childNodes[i];
- }
- }
- }
- return null;
- },
- /** @id MochiKit.Sortable.Sortable.tree */
- tree: function (element, options) {
- element = MochiKit.DOM.getElement(element);
- var sortableOptions = MochiKit.Sortable.Sortable.options(element);
- options = MochiKit.Base.update({
- tag: sortableOptions.tag,
- treeTag: sortableOptions.treeTag,
- only: sortableOptions.only,
- name: element.id,
- format: sortableOptions.format
- }, options || {});
- var root = {
- id: null,
- parent: null,
- children: new Array,
- container: element,
- position: 0
- }
- return MochiKit.Sortable.Sortable._tree(element, options, root);
- },
- /**
- * Specifies the sequence for the Sortable.
- * @param {Node} element Element to use as the Sortable.
- * @param {Object} newSequence New sequence to use.
- * @param {Object} options Options to use fro the Sortable.
- */
- setSequence: function (element, newSequence, options) {
- var self = MochiKit.Sortable.Sortable;
- var b = MochiKit.Base;
- element = MochiKit.DOM.getElement(element);
- options = b.update(self.options(element), options || {});
- var nodeMap = {};
- b.map(function (n) {
- var m = n.id.match(options.format);
- if (m) {
- nodeMap[m[1]] = [n, n.parentNode];
- }
- n.parentNode.removeChild(n);
- }, self.findElements(element, options));
- b.map(function (ident) {
- var n = nodeMap[ident];
- if (n) {
- n[1].appendChild(n[0]);
- delete nodeMap[ident];
- }
- }, newSequence);
- },
- /* Construct a [i] index for a particular node */
- _constructIndex: function (node) {
- var index = '';
- do {
- if (node.id) {
- index = '[' + node.position + ']' + index;
- }
- } while ((node = node.parent) != null);
- return index;
- },
- /** @id MochiKit.Sortable.Sortable.sequence */
- sequence: function (element, options) {
- element = MochiKit.DOM.getElement(element);
- var self = MochiKit.Sortable.Sortable;
- var options = MochiKit.Base.update(self.options(element), options || {});
- return MochiKit.Base.map(function (item) {
- return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
- }, MochiKit.DOM.getElement(self.findElements(element, options) || []));
- },
- /**
- * Serializes the content of a Sortable. Useful to send this content through a XMLHTTPRequest.
- * These options override the Sortable options for the serialization only.
- * @param {Node} element Element to serialize.
- * @param {Object} options Serialization options.
- */
- serialize: function (element, options) {
- element = MochiKit.DOM.getElement(element);
- var self = MochiKit.Sortable.Sortable;
- options = MochiKit.Base.update(self.options(element), options || {});
- var name = encodeURIComponent(options.name || element.id);
- if (options.tree) {
- return MochiKit.Base.flattenArray(MochiKit.Base.map(function (item) {
- return [name + self._constructIndex(item) + "[id]=" +
- encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
- }, self.tree(element, options).children)).join('&');
- } else {
- return MochiKit.Base.map(function (item) {
- return name + "[]=" + encodeURIComponent(item);
- }, self.sequence(element, options)).join('&');
- }
- }
- };
|