0
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
0
+// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
0
// See scriptaculous.js for full license.
0
var options = Object.extend({
0
}, arguments[1] || {});
0
this.drops.push(options);
0
+ findDeepestChild: function(drops) {
0
+ for (i = 1; i < drops.length; ++i)
0
+ if (Element.isParent(drops[i].element, deepest.element))
0
isContained: function(element, drop) {
0
- var parentNode = element.parentNode;
0
- return drop._containers.detect(function(c) { return parentNode == c });
0
+ containmentNode = element.treeNode;
0
+ containmentNode = element.parentNode;
0
+ return drop._containers.detect(function(c) { return containmentNode == c });
0
isAffected: function(point, element, drop) {
0
(drop.element!=element) &&
0
show: function(point, element) {
0
if(!this.drops.length) return;
0
if(this.last_active) this.deactivate(this.last_active);
0
this.drops.each( function(drop) {
0
- if(Droppables.isAffected(point, element, drop)) {
0
- drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
0
- Droppables.activate(drop);
0
+ if(Droppables.isAffected(point, element, drop))
0
+ if(affected.length>0) {
0
+ drop = Droppables.findDeepestChild(affected);
0
+ Position.within(drop.element, point[0], point[1]);
0
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
0
+ Droppables.activate(drop);
0
fire: function(event, element) {
0
this.element = $(element);
0
- if(options.handle && (typeof options.handle == 'string'))
0
- this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
0
+ if(options.handle && (typeof options.handle == 'string')) {
0
+ var h = Element.childrenWithClassName(this.element, options.handle, true);
0
+ if(h.length>0) this.handle = h[0];
0
if(!this.handle) this.handle = $(options.handle);
0
if(!this.handle) this.handle = this.element;
0
- if(options.scroll) options.scroll = $(options.scroll);
0
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
0
+ options.scroll = $(options.scroll);
0
Element.makePositioned(this.element); // fix IE
0
if(this.options.scroll) {
0
- this.originalScrollLeft = this.options.scroll.scrollLeft;
0
- this.originalScrollTop = this.options.scroll.scrollTop;
0
+ if (this.options.scroll == window) {
0
+ var where = this._getWindowScroll(this.options.scroll);
0
+ this.originalScrollLeft = where.left;
0
+ this.originalScrollTop = where.top;
0
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
0
+ this.originalScrollTop = this.options.scroll.scrollTop;
0
Draggables.notify('onStart', this, event);
0
if(this.options.change) this.options.change(this);
0
if(this.options.scroll) {
0
- //if(this.scrollInterval) this.scroll();
0
- var p = Position.page(this.options.scroll);
0
- p[0] += this.options.scroll.scrollLeft;
0
- p[1] += this.options.scroll.scrollTop;
0
- p.push(p[0]+this.options.scroll.offsetWidth);
0
- p.push(p[1]+this.options.scroll.offsetHeight);
0
+ if (this.options.scroll == window) {
0
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
0
+ p = Position.page(this.options.scroll);
0
+ p[0] += this.options.scroll.scrollLeft;
0
+ p[1] += this.options.scroll.scrollTop;
0
+ p.push(p[0]+this.options.scroll.offsetWidth);
0
+ p.push(p[1]+this.options.scroll.offsetHeight);
0
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
0
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
0
var d = this.currentDelta();
0
pos[0] -= d[0]; pos[1] -= d[1];
0
- if(this.options.scroll
) {
0
+ if(this.options.scroll
&& (this.options.scroll != window)) {
0
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
0
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
0
if(this.scrollInterval) {
0
clearInterval(this.scrollInterval);
0
this.scrollInterval = null;
0
+ Draggables._lastScrollPointer = null;
0
var current = new Date();
0
var delta = current - this.lastScrolled;
0
this.lastScrolled = current;
0
- this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
0
- this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
0
+ if(this.options.scroll == window) {
0
+ with (this._getWindowScroll(this.options.scroll)) {
0
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
0
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
0
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
0
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
0
Droppables.show(Draggables._lastPointer, this.element);
0
Draggables.notify('onDrag', this);
0
- this.draw(Draggables._lastPointer);
0
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
0
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
0
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
0
+ if (Draggables._lastScrollPointer[0] < 0)
0
+ Draggables._lastScrollPointer[0] = 0;
0
+ if (Draggables._lastScrollPointer[1] < 0)
0
+ Draggables._lastScrollPointer[1] = 0;
0
+ this.draw(Draggables._lastScrollPointer);
0
if(this.options.change) this.options.change(this);
0
+ _getWindowScroll: function(w) {
0
+ if (w.document.documentElement && documentElement.scrollTop) {
0
+ T = documentElement.scrollTop;
0
+ L = documentElement.scrollLeft;
0
+ } else if (w.document.body) {
0
+ } else if (w.document.documentElement && documentElement.clientWidth) {
0
+ W = documentElement.clientWidth;
0
+ H = documentElement.clientHeight;
0
+ return { top: T, left: L, width: W, height: H };
0
- sortables:
new Array(),
0
- options: function(element){
0
- return this.sortables.detect(function(s) { return s.element == element });
0
+ _findRootElement: function(element) {
0
+ while (element.tagName != "BODY") {
0
+ if(element.id && Sortable.sortables[element.id]) return element;
0
+ element = element.parentNode;
0
+ options: function(element) {
0
+ element = Sortable._findRootElement($(element));
0
+ return Sortable.sortables[element.id];
0
destroy: function(element){
0
- this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
0
+ var s = Sortable.options(element);
0
Draggables.removeObserver(s.element);
0
s.droppables.each(function(d){ Droppables.remove(d) });
0
s.draggables.invoke('destroy');
0
- this.sortables = this.sortables.reject(function(s) { return s.element == element });
0
+ delete Sortable.sortables[s.element.id];
0
create: function(element) {
0
var options = Object.extend({
0
tag: 'li', // assumes li children, override with tag: 'tagname'
0
- tree: false, // fixme: unimplemented
0
overlap: 'vertical', // one of 'vertical', 'horizontal'
0
constraint: 'vertical', // one of 'vertical', 'horizontal', false
0
containment: element, // also takes array of elements (or id's); or false
0
+ scrollSensitivity: 20,
0
format: /^[^_]*_(.*)$/,
0
onChange: Prototype.emptyFunction,
0
onUpdate: Prototype.emptyFunction
0
var options_for_draggable = {
0
scroll: options.scroll,
0
+ scrollSpeed: options.scrollSpeed,
0
+ scrollSensitivity: options.scrollSensitivity,
0
ghosting: options.ghosting,
0
constraint: options.constraint,
0
handle: options.handle };
0
var options_for_droppable = {
0
overlap: options.overlap,
0
containment: options.containment,
0
hoverclass: options.hoverclass,
0
- onHover: Sortable.onHover,
0
- greedy: !options.dropOnEmpty
0
+ onHover: Sortable.onHover
0
+ //greedy: !options.dropOnEmpty
0
+ var options_for_tree = {
0
+ onHover: Sortable.onEmptyHover,
0
+ overlap: options.overlap,
0
+ containment: options.containment,
0
+ hoverclass: options.hoverclass
0
// fix for gecko engine
0
Element.cleanWhitespace(element);
0
options.draggables = [];
0
options.droppables = [];
0
// drop on empty handling
0
- if(options.dropOnEmpty) {
0
- Droppables.add(element,
0
- {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
0
+ if(options.dropOnEmpty || options.tree) {
0
+ Droppables.add(element, options_for_tree);
0
options.droppables.push(element);
0
options.draggables.push(
0
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
0
Droppables.add(e, options_for_droppable);
0
+ if(options.tree) e.treeNode = element;
0
options.droppables.push(e);
0
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
0
+ Droppables.add(e, options_for_tree);
0
+ options.droppables.push(e);
0
- this.sortables
.push(options);
0
+ this.sortables
[element.id] = options;
0
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
0
// return all suitable-for-sortable elements in a guaranteed order
0
findElements: function(element, options) {
0
- if(!element.hasChildNodes()) return null;
0
- $A(element.childNodes).each( function(e) {
0
- if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
0
- (!options.only || (Element.hasClassName(e, options.only))))
0
- var grandchildren = this.findElements(e, options);
0
- if(grandchildren) elements.push(grandchildren);
0
- return (elements.length>0 ? elements.flatten() : null);
0
+ return Element.findChildren(
0
+ element, options.only, options.tree ? true : false, options.tag);
0
+ findTreeElements: function(element, options) {
0
+ return Element.findChildren(
0
+ element, options.only, options.tree ? true : false, options.treeTag);
0
onHover: function(element, dropon, overlap) {
0
+ if(Element.isParent(dropon, element)) return;
0
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
0
+ } else if(overlap>0.5) {
0
Sortable.mark(dropon, 'before');
0
if(dropon.previousSibling != element) {
0
var oldParentNode = element.parentNode;
0
- onEmptyHover: function(element, dropon) {
0
- if(element.parentNode!=dropon) {
0
- var oldParentNode = element.parentNode;
0
- dropon.appendChild(element);
0
+ onEmptyHover: function(element, dropon, overlap) {
0
+ var oldParentNode = element.parentNode;
0
+ var droponOptions = Sortable.options(dropon);
0
+ if(!Element.isParent(dropon, element)) {
0
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
0
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
0
+ for (index = 0; index < children.length; index += 1) {
0
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
0
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
0
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
0
+ child = index + 1 < children.length ? children[index + 1] : null;
0
+ child = children[index];
0
+ dropon.insertBefore(element, child);
0
Sortable.options(oldParentNode).onChange(element);
0
-
Sortable.options(dropon).onChange(element);
0
+
droponOptions.onChange(element);
0
Element.show(Sortable._marker);
0
+ _tree: function(element, options, parent) {
0
+ var children = Sortable.findElements(element, options) || [];
0
+ for (var i = 0; i < children.length; ++i) {
0
+ var match = children[i].id.match(options.format);
0
+ id: encodeURIComponent(match ? match[1] : null),
0
+ position: parent.children.length,
0
+ container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
0
+ /* Get the element containing the children and recurse over it */
0
+ this._tree(child.container, options, child)
0
+ parent.children.push (child);
0
+ /* Finds the first element of the given tag type within a parent element.
0
+ Used for finding the first LI[ST] within a L[IST]I[TEM].*/
0
+ _findChildrenElement: function (element, containerTag) {
0
+ if (element && element.hasChildNodes)
0
+ for (var i = 0; i < element.childNodes.length; ++i)
0
+ if (element.childNodes[i].tagName == containerTag)
0
+ return element.childNodes[i];
0
+ tree: function(element) {
0
+ var sortableOptions = this.options(element);
0
+ var options = Object.extend({
0
+ tag: sortableOptions.tag,
0
+ treeTag: sortableOptions.treeTag,
0
+ only: sortableOptions.only,
0
+ format: sortableOptions.format
0
+ }, arguments[1] || {});
0
+ return Sortable._tree (element, options, root);
0
+ /* Construct a [i] index for a particular node */
0
+ _constructIndex: function(node) {
0
+ if (node.id) index = '[' + node.position + ']' + index;
0
+ } while ((node = node.parent) != null);
0
sequence: function(element) {
0
var options = Object.extend(this.options(element), arguments[1] || {});
0
new_sequence.each(function(ident) {
0
- var n = nodeMap[ident];
0
- n[1].appendChild(n[0]);
0
- delete nodeMap[ident];
0
+ var n = nodeMap[ident];
0
+ n[1].appendChild(n[0]);
0
+ delete nodeMap[ident];
0
serialize: function(element) {
0
+ var options = Object.extend(Sortable.options(element), arguments[1] || {});
0
var name = encodeURIComponent(
0
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
0
- return Sortable.sequence(element, arguments[1]).map( function(item) {
0
- return name + "[]=" + encodeURIComponent(item);
0
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
0
+ return [name + Sortable._constructIndex(item) + "=" +
0
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
0
+ }).flatten().join('&');
0
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
0
+ return name + "[]=" + encodeURIComponent(item);
0
+/* Returns true if child is contained within element */
0
+Element.isParent = function(child, element) {
0
+ if (!child.parentNode || child == element) return false;
0
+ if (child.parentNode == element) return true;
0
+ return Element.isParent(child.parentNode, element);
0
+Element.findChildren = function(element, only, recursive, tagName) {
0
+ if(!element.hasChildNodes()) return null;
0
+ tagName = tagName.toUpperCase();
0
+ if(only) only = [only].flatten();
0
+ $A(element.childNodes).each( function(e) {
0
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
0
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
0
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
0
+ if(grandchildren) elements.push(grandchildren);
0
+ return (elements.length>0 ? elements.flatten() : []);
0
+Element.offsetSize = function (element, type) {
0
+ if (type == 'vertical' || type == 'height')
0
+ return element.offsetHeight;
0
+ return element.offsetWidth;