testing/mochitest/tests/MochiKit-1.4.2/MochiKit/Sortable.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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);

mercurial