##// END OF EJS Templates
Removed "Wiki edits" option in the activity view if the project has no wiki....
Removed "Wiki edits" option in the activity view if the project has no wiki. git-svn-id: http://redmine.rubyforge.org/svn/trunk@517 e93f8b46-1217-0410-a6f0-8f06a7374b81

File last commit:

r238:2e72f2eca8f4
r514:d85f5518d903
Show More
dragdrop.js
942 lines | 29.8 KiB | application/javascript | JavascriptLexer
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
Jean-Philippe Lang
Initial commit...
r2 //
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 // script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
Jean-Philippe Lang
Initial commit...
r2
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(typeof Effect == 'undefined')
throw("dragdrop.js requires including script.aculo.us' effects.js library");
Jean-Philippe Lang
Initial commit...
r2
var Droppables = {
drops: [],
remove: function(element) {
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
},
add: function(element) {
element = $(element);
var options = Object.extend({
greedy: true,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 hoverclass: null,
tree: false
Jean-Philippe Lang
Initial commit...
r2 }, arguments[1] || {});
// cache containers
if(options.containment) {
options._containers = [];
var containment = options.containment;
if((typeof containment == 'object') &&
(containment.constructor == Array)) {
containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
}
}
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
options.element = element;
this.drops.push(options);
},
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
findDeepestChild: function(drops) {
deepest = drops[0];
for (i = 1; i < drops.length; ++i)
if (Element.isParent(drops[i].element, deepest.element))
deepest = drops[i];
return deepest;
},
Jean-Philippe Lang
Initial commit...
r2
isContained: function(element, drop) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 var containmentNode;
if(drop.tree) {
containmentNode = element.treeNode;
} else {
containmentNode = element.parentNode;
}
return drop._containers.detect(function(c) { return containmentNode == c });
Jean-Philippe Lang
Initial commit...
r2 },
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
deactivate: function(drop) {
if(drop.hoverclass)
Element.removeClassName(drop.element, drop.hoverclass);
this.last_active = null;
},
activate: function(drop) {
if(drop.hoverclass)
Element.addClassName(drop.element, drop.hoverclass);
this.last_active = drop;
},
show: function(point, element) {
if(!this.drops.length) return;
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 var affected = [];
Jean-Philippe Lang
Initial commit...
r2
if(this.last_active) this.deactivate(this.last_active);
this.drops.each( function(drop) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(Droppables.isAffected(point, element, drop))
affected.push(drop);
Jean-Philippe Lang
Initial commit...
r2 });
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
if(affected.length>0) {
drop = Droppables.findDeepestChild(affected);
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
Droppables.activate(drop);
}
Jean-Philippe Lang
Initial commit...
r2 },
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop)
this.last_active.onDrop(element, this.last_active.element, event);
},
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
}
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(draggable.options.delay) {
this._timeout = setTimeout(function() {
Draggables._timeout = null;
window.focus();
Draggables.activeDraggable = draggable;
}.bind(this), draggable.options.delay);
} else {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
}
Jean-Philippe Lang
Initial commit...
r2 },
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 deactivate: function() {
Jean-Philippe Lang
Initial commit...
r2 this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// Mozilla-based browsers fire successive mousemove events with
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(this._timeout) {
clearTimeout(this._timeout);
this._timeout = null;
}
Jean-Philippe Lang
Initial commit...
r2 if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 this.activeDraggable = null;
Jean-Philippe Lang
Initial commit...
r2 },
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
Jean-Philippe Lang
Initial commit...
r2 },
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
}
/*--------------------------------------------------------------------------*/
var Draggable = Class.create();
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Draggable._dragging = {};
Jean-Philippe Lang
Initial commit...
r2 Draggable.prototype = {
initialize: function(element) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 var defaults = {
Jean-Philippe Lang
Initial commit...
r2 handle: false,
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
queue: {scope:'_draggable', position:'end'}
});
Jean-Philippe Lang
Initial commit...
r2 },
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 endeffect: function(element) {
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
afterFinish: function(){
Draggable._dragging[element] = false
}
});
Jean-Philippe Lang
Initial commit...
r2 },
zindex: 1000,
revert: false,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
delay: 0
};
if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
Object.extend(defaults, {
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
Draggable._dragging[element] = true;
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
}
});
var options = Object.extend(defaults, arguments[1] || {});
Jean-Philippe Lang
Initial commit...
r2
this.element = $(element);
if(options.handle && (typeof options.handle == 'string'))
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 this.handle = this.element.down('.'+options.handle, 0);
Jean-Philippe Lang
Initial commit...
r2 if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
options.scroll = $(options.scroll);
this._isScrollChild = Element.childOf(this.element, options.scroll);
}
Jean-Philippe Lang
Initial commit...
r2
Element.makePositioned(this.element); // fix IE
this.delta = this.currentDelta();
this.options = options;
this.dragging = false;
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
Draggables.register(this);
},
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
Draggables.unregister(this);
},
currentDelta: function() {
return([
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 parseInt(Element.getStyle(this.element,'left') || '0'),
parseInt(Element.getStyle(this.element,'top') || '0')]);
Jean-Philippe Lang
Initial commit...
r2 },
initDrag: function(event) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(typeof Draggable._dragging[this.element] != 'undefined' &&
Draggable._dragging[this.element]) return;
Jean-Philippe Lang
Initial commit...
r2 if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if(src.tagName && (
src.tagName=='INPUT' ||
src.tagName=='SELECT' ||
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 src.tagName=='OPTION' ||
Jean-Philippe Lang
Initial commit...
r2 src.tagName=='BUTTON' ||
src.tagName=='TEXTAREA')) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function(event) {
this.dragging = true;
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(this.options.scroll) {
if (this.options.scroll == window) {
var where = this._getWindowScroll(this.options.scroll);
this.originalScrollLeft = where.left;
this.originalScrollTop = where.top;
} else {
this.originalScrollLeft = this.options.scroll.scrollLeft;
this.originalScrollTop = this.options.scroll.scrollTop;
}
}
Jean-Philippe Lang
Initial commit...
r2 Draggables.notify('onStart', this, event);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 if(this.options.starteffect) this.options.starteffect(this.element);
},
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
Position.prepare();
Droppables.show(pointer, this.element);
Draggables.notify('onDrag', this, event);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 this.draw(pointer);
if(this.options.change) this.options.change(this);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(this.options.scroll) {
this.stopScrolling();
var p;
if (this.options.scroll == window) {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
} else {
p = Position.page(this.options.scroll);
p[0] += this.options.scroll.scrollLeft + Position.deltaX;
p[1] += this.options.scroll.scrollTop + Position.deltaY;
p.push(p[0]+this.options.scroll.offsetWidth);
p.push(p[1]+this.options.scroll.offsetHeight);
}
var speed = [0,0];
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
this.startScrolling(speed);
}
Jean-Philippe Lang
Initial commit...
r2 // fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 Event.stop(event);
},
finishDrag: function(event, success) {
this.dragging = false;
if(this.options.ghosting) {
Position.relativize(this.element);
Element.remove(this._clone);
this._clone = null;
}
if(success) Droppables.fire(event, this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && typeof revert == 'function') revert = revert(this.element);
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
this.options.reverteffect(this.element,
d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.delta = d;
}
if(this.options.zindex)
this.element.style.zIndex = this.originalZ;
if(this.options.endeffect)
this.options.endeffect(this.element);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 Draggables.deactivate(this);
Droppables.reset();
},
keyPress: function(event) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(event.keyCode!=Event.KEY_ESC) return;
Jean-Philippe Lang
Initial commit...
r2 this.finishDrag(event, false);
Event.stop(event);
},
endDrag: function(event) {
if(!this.dragging) return;
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 this.stopScrolling();
Jean-Philippe Lang
Initial commit...
r2 this.finishDrag(event, true);
Event.stop(event);
},
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(this.options.ghosting) {
var r = Position.realOffset(this.element);
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
}
Jean-Philippe Lang
Initial commit...
r2 var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
var p = [0,1].map(function(i){
return (point[i]-pos[i]-this.offset[i])
}.bind(this));
Jean-Philippe Lang
Initial commit...
r2
if(this.options.snap) {
if(typeof this.options.snap == 'function') {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 p = this.options.snap(p[0],p[1],this);
Jean-Philippe Lang
Initial commit...
r2 } else {
if(this.options.snap instanceof Array) {
p = p.map( function(v, i) {
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
} else {
p = p.map( function(v) {
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
}
}}
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 },
stopScrolling: function() {
if(this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
Draggables._lastScrollPointer = null;
}
},
startScrolling: function(speed) {
if(!(speed[0] || speed[1])) return;
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
this.lastScrolled = new Date();
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
},
scroll: function() {
var current = new Date();
var delta = current - this.lastScrolled;
this.lastScrolled = current;
if(this.options.scroll == window) {
with (this._getWindowScroll(this.options.scroll)) {
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
var d = delta / 1000;
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
}
}
} else {
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
}
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
if (this._isScrollChild) {
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
if (Draggables._lastScrollPointer[0] < 0)
Draggables._lastScrollPointer[0] = 0;
if (Draggables._lastScrollPointer[1] < 0)
Draggables._lastScrollPointer[1] = 0;
this.draw(Draggables._lastScrollPointer);
}
if(this.options.change) this.options.change(this);
},
_getWindowScroll: function(w) {
var T, L, W, H;
with (w.document) {
if (w.document.documentElement && documentElement.scrollTop) {
T = documentElement.scrollTop;
L = documentElement.scrollLeft;
} else if (w.document.body) {
T = body.scrollTop;
L = body.scrollLeft;
}
if (w.innerWidth) {
W = w.innerWidth;
H = w.innerHeight;
} else if (w.document.documentElement && documentElement.clientWidth) {
W = documentElement.clientWidth;
H = documentElement.clientHeight;
} else {
W = body.offsetWidth;
H = body.offsetHeight
}
}
return { top: T, left: L, width: W, height: H };
Jean-Philippe Lang
Initial commit...
r2 }
}
/*--------------------------------------------------------------------------*/
var SortableObserver = Class.create();
SortableObserver.prototype = {
initialize: function(element, observer) {
this.element = $(element);
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
}
var Sortable = {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
Jean-Philippe Lang
Initial commit...
r2
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 sortables: {},
_findRootElement: function(element) {
while (element.tagName != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
},
options: function(element) {
element = Sortable._findRootElement($(element));
if(!element) return;
return Sortable.sortables[element.id];
Jean-Philippe Lang
Initial commit...
r2 },
destroy: function(element){
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 var s = Sortable.options(element);
if(s) {
Jean-Philippe Lang
Initial commit...
r2 Draggables.removeObserver(s.element);
s.droppables.each(function(d){ Droppables.remove(d) });
s.draggables.invoke('destroy');
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
delete Sortable.sortables[s.element.id];
}
Jean-Philippe Lang
Initial commit...
r2 },
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
Jean-Philippe Lang
Initial commit...
r2 create: function(element) {
element = $(element);
var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
dropOnEmpty: false,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 tree: false,
treeTag: 'ul',
Jean-Philippe Lang
Initial commit...
r2 overlap: 'vertical', // one of 'vertical', 'horizontal'
constraint: 'vertical', // one of 'vertical', 'horizontal', false
containment: element, // also takes array of elements (or id's); or false
handle: false, // or a CSS class
only: false,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 delay: 0,
Jean-Philippe Lang
Initial commit...
r2 hoverclass: null,
ghosting: false,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: this.SERIALIZE_RULE,
Jean-Philippe Lang
Initial commit...
r2 onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || {});
// clear any old sortable with same element
this.destroy(element);
// build options for the draggables
var options_for_draggable = {
revert: true,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
delay: options.delay,
Jean-Philippe Lang
Initial commit...
r2 ghosting: options.ghosting,
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(element) {
element.style.top = 0;
element.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,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 tree: options.tree,
Jean-Philippe Lang
Initial commit...
r2 hoverclass: options.hoverclass,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 onHover: Sortable.onHover
}
var options_for_tree = {
onHover: Sortable.onEmptyHover,
overlap: options.overlap,
containment: options.containment,
hoverclass: options.hoverclass
Jean-Philippe Lang
Initial commit...
r2 }
// fix for gecko engine
Element.cleanWhitespace(element);
options.draggables = [];
options.droppables = [];
// drop on empty handling
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(options.dropOnEmpty || options.tree) {
Droppables.add(element, options_for_tree);
Jean-Philippe Lang
Initial commit...
r2 options.droppables.push(element);
}
(this.findElements(element, options) || []).each( function(e) {
// handles are per-draggable
var handle = options.handle ?
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 $(e).down('.'+options.handle,0) : e;
Jean-Philippe Lang
Initial commit...
r2 options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(options.tree) e.treeNode = element;
Jean-Philippe Lang
Initial commit...
r2 options.droppables.push(e);
});
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
if(options.tree) {
(Sortable.findTreeElements(element, options) || []).each( function(e) {
Droppables.add(e, options_for_tree);
e.treeNode = element;
options.droppables.push(e);
});
}
Jean-Philippe Lang
Initial commit...
r2
// keep reference
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 this.sortables[element.id] = options;
Jean-Philippe Lang
Initial commit...
r2
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
},
// return all suitable-for-sortable elements in a guaranteed order
findElements: function(element, options) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 return Element.findChildren(
element, options.only, options.tree ? true : false, options.tag);
},
findTreeElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.treeTag);
Jean-Philippe Lang
Initial commit...
r2 },
onHover: function(element, dropon, overlap) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(Element.isParent(dropon, element)) return;
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
return;
} else if(overlap>0.5) {
Jean-Philippe Lang
Initial commit...
r2 Sortable.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)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
} else {
Sortable.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)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
}
},
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
onEmptyHover: function(element, dropon, overlap) {
var oldParentNode = element.parentNode;
var droponOptions = Sortable.options(dropon);
if(!Element.isParent(dropon, element)) {
var index;
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
var child = null;
if(children) {
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
for (index = 0; index < children.length; index += 1) {
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
offset -= Element.offsetSize (children[index], droponOptions.overlap);
} else if (offset - (Element.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);
Jean-Philippe Lang
Initial commit...
r2 Sortable.options(oldParentNode).onChange(element);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 droponOptions.onChange(element);
Jean-Philippe Lang
Initial commit...
r2 }
},
unmark: function() {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if(Sortable._marker) Sortable._marker.hide();
Jean-Philippe Lang
Initial commit...
r2 },
mark: function(dropon, position) {
// mark on ghosting only
var sortable = Sortable.options(dropon.parentNode);
if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Sortable._marker =
($('dropmarker') || Element.extend(document.createElement('DIV'))).
hide().addClassName('dropmarker').setStyle({position:'absolute'});
Jean-Philippe Lang
Initial commit...
r2 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
Jean-Philippe Lang
Initial commit...
r2
if(position=='after')
if(sortable.overlap == 'horizontal')
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
Jean-Philippe Lang
Initial commit...
r2 else
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
Jean-Philippe Lang
Initial commit...
r2
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 Sortable._marker.show();
Jean-Philippe Lang
Initial commit...
r2 },
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
_tree: function(element, options, parent) {
var children = Sortable.findElements(element, options) || [];
for (var i = 0; i < children.length; ++i) {
var match = children[i].id.match(options.format);
Jean-Philippe Lang
Initial commit...
r2
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 if (!match) continue;
var child = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
parent: parent,
children: [],
position: parent.children.length,
container: $(children[i]).down(options.treeTag)
}
/* Get the element containing the children and recurse over it */
if (child.container)
this._tree(child.container, options, child)
parent.children.push (child);
}
return parent;
},
tree: function(element) {
Jean-Philippe Lang
Initial commit...
r2 element = $(element);
var sortableOptions = this.options(element);
var options = Object.extend({
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 tag: sortableOptions.tag,
treeTag: sortableOptions.treeTag,
Jean-Philippe Lang
Initial commit...
r2 only: sortableOptions.only,
name: element.id,
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 format: sortableOptions.format
Jean-Philippe Lang
Initial commit...
r2 }, arguments[1] || {});
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238
var root = {
id: null,
parent: null,
children: [],
container: element,
position: 0
}
return Sortable._tree(element, options, root);
},
/* 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;
},
sequence: function(element) {
element = $(element);
var options = Object.extend(this.options(element), arguments[1] || {});
Jean-Philippe Lang
Initial commit...
r2 return $(this.findElements(element, options) || []).map( function(item) {
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
});
},
setSequence: function(element, new_sequence) {
element = $(element);
var options = Object.extend(this.options(element), arguments[2] || {});
var nodeMap = {};
this.findElements(element, options).each( function(n) {
if (n.id.match(options.format))
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
n.parentNode.removeChild(n);
});
new_sequence.each(function(ident) {
var n = nodeMap[ident];
if (n) {
n[1].appendChild(n[0]);
delete nodeMap[ident];
}
});
},
serialize: function(element) {
element = $(element);
var options = Object.extend(Sortable.options(element), arguments[1] || {});
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
if (options.tree) {
return Sortable.tree(element, arguments[1]).children.map( function (item) {
return [name + Sortable._constructIndex(item) + "[id]=" +
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
}).flatten().join('&');
} else {
return Sortable.sequence(element, arguments[1]).map( function(item) {
return name + "[]=" + encodeURIComponent(item);
}).join('&');
}
Jean-Philippe Lang
Initial commit...
r2 }
Jean-Philippe Lang
updated prototype (1.50) and script.aculo.us javascripts...
r238 }
// Returns true if child is contained within element
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element);
}
Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
var elements = [];
$A(element.childNodes).each( function(e) {
if(e.tagName && e.tagName.toUpperCase()==tagName &&
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
elements.push(e);
if(recursive) {
var grandchildren = Element.findChildren(e, only, recursive, tagName);
if(grandchildren) elements.push(grandchildren);
}
});
return (elements.length>0 ? elements.flatten() : []);
}
Element.offsetSize = function (element, type) {
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
}