-
+ F728E059183ADF05BC868D9FD6ADB3C5C5329586BEA8A6D0C39AC2749EDD6D9CD6668FA70AB90C895911858A965A032ACCC8E777EFB3A810BC366AEDE09DCAD4
mp-wp/wp-includes/js/scriptaculous/dragdrop.js
(0 . 0)(1 . 974)
108781 // script.aculo.us dragdrop.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
108782
108783 // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
108784 // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
108785 //
108786 // script.aculo.us is freely distributable under the terms of an MIT-style license.
108787 // For details, see the script.aculo.us web site: http://script.aculo.us/
108788
108789 if(Object.isUndefined(Effect))
108790 throw("dragdrop.js requires including script.aculo.us' effects.js library");
108791
108792 var Droppables = {
108793 drops: [],
108794
108795 remove: function(element) {
108796 this.drops = this.drops.reject(function(d) { return d.element==$(element) });
108797 },
108798
108799 add: function(element) {
108800 element = $(element);
108801 var options = Object.extend({
108802 greedy: true,
108803 hoverclass: null,
108804 tree: false
108805 }, arguments[1] || { });
108806
108807 // cache containers
108808 if(options.containment) {
108809 options._containers = [];
108810 var containment = options.containment;
108811 if(Object.isArray(containment)) {
108812 containment.each( function(c) { options._containers.push($(c)) });
108813 } else {
108814 options._containers.push($(containment));
108815 }
108816 }
108817
108818 if(options.accept) options.accept = [options.accept].flatten();
108819
108820 Element.makePositioned(element); // fix IE
108821 options.element = element;
108822
108823 this.drops.push(options);
108824 },
108825
108826 findDeepestChild: function(drops) {
108827 deepest = drops[0];
108828
108829 for (i = 1; i < drops.length; ++i)
108830 if (Element.isParent(drops[i].element, deepest.element))
108831 deepest = drops[i];
108832
108833 return deepest;
108834 },
108835
108836 isContained: function(element, drop) {
108837 var containmentNode;
108838 if(drop.tree) {
108839 containmentNode = element.treeNode;
108840 } else {
108841 containmentNode = element.parentNode;
108842 }
108843 return drop._containers.detect(function(c) { return containmentNode == c });
108844 },
108845
108846 isAffected: function(point, element, drop) {
108847 return (
108848 (drop.element!=element) &&
108849 ((!drop._containers) ||
108850 this.isContained(element, drop)) &&
108851 ((!drop.accept) ||
108852 (Element.classNames(element).detect(
108853 function(v) { return drop.accept.include(v) } ) )) &&
108854 Position.within(drop.element, point[0], point[1]) );
108855 },
108856
108857 deactivate: function(drop) {
108858 if(drop.hoverclass)
108859 Element.removeClassName(drop.element, drop.hoverclass);
108860 this.last_active = null;
108861 },
108862
108863 activate: function(drop) {
108864 if(drop.hoverclass)
108865 Element.addClassName(drop.element, drop.hoverclass);
108866 this.last_active = drop;
108867 },
108868
108869 show: function(point, element) {
108870 if(!this.drops.length) return;
108871 var drop, affected = [];
108872
108873 this.drops.each( function(drop) {
108874 if(Droppables.isAffected(point, element, drop))
108875 affected.push(drop);
108876 });
108877
108878 if(affected.length>0)
108879 drop = Droppables.findDeepestChild(affected);
108880
108881 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
108882 if (drop) {
108883 Position.within(drop.element, point[0], point[1]);
108884 if(drop.onHover)
108885 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
108886
108887 if (drop != this.last_active) Droppables.activate(drop);
108888 }
108889 },
108890
108891 fire: function(event, element) {
108892 if(!this.last_active) return;
108893 Position.prepare();
108894
108895 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
108896 if (this.last_active.onDrop) {
108897 this.last_active.onDrop(element, this.last_active.element, event);
108898 return true;
108899 }
108900 },
108901
108902 reset: function() {
108903 if(this.last_active)
108904 this.deactivate(this.last_active);
108905 }
108906 }
108907
108908 var Draggables = {
108909 drags: [],
108910 observers: [],
108911
108912 register: function(draggable) {
108913 if(this.drags.length == 0) {
108914 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
108915 this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
108916 this.eventKeypress = this.keyPress.bindAsEventListener(this);
108917
108918 Event.observe(document, "mouseup", this.eventMouseUp);
108919 Event.observe(document, "mousemove", this.eventMouseMove);
108920 Event.observe(document, "keypress", this.eventKeypress);
108921 }
108922 this.drags.push(draggable);
108923 },
108924
108925 unregister: function(draggable) {
108926 this.drags = this.drags.reject(function(d) { return d==draggable });
108927 if(this.drags.length == 0) {
108928 Event.stopObserving(document, "mouseup", this.eventMouseUp);
108929 Event.stopObserving(document, "mousemove", this.eventMouseMove);
108930 Event.stopObserving(document, "keypress", this.eventKeypress);
108931 }
108932 },
108933
108934 activate: function(draggable) {
108935 if(draggable.options.delay) {
108936 this._timeout = setTimeout(function() {
108937 Draggables._timeout = null;
108938 window.focus();
108939 Draggables.activeDraggable = draggable;
108940 }.bind(this), draggable.options.delay);
108941 } else {
108942 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
108943 this.activeDraggable = draggable;
108944 }
108945 },
108946
108947 deactivate: function() {
108948 this.activeDraggable = null;
108949 },
108950
108951 updateDrag: function(event) {
108952 if(!this.activeDraggable) return;
108953 var pointer = [Event.pointerX(event), Event.pointerY(event)];
108954 // Mozilla-based browsers fire successive mousemove events with
108955 // the same coordinates, prevent needless redrawing (moz bug?)
108956 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
108957 this._lastPointer = pointer;
108958
108959 this.activeDraggable.updateDrag(event, pointer);
108960 },
108961
108962 endDrag: function(event) {
108963 if(this._timeout) {
108964 clearTimeout(this._timeout);
108965 this._timeout = null;
108966 }
108967 if(!this.activeDraggable) return;
108968 this._lastPointer = null;
108969 this.activeDraggable.endDrag(event);
108970 this.activeDraggable = null;
108971 },
108972
108973 keyPress: function(event) {
108974 if(this.activeDraggable)
108975 this.activeDraggable.keyPress(event);
108976 },
108977
108978 addObserver: function(observer) {
108979 this.observers.push(observer);
108980 this._cacheObserverCallbacks();
108981 },
108982
108983 removeObserver: function(element) { // element instead of observer fixes mem leaks
108984 this.observers = this.observers.reject( function(o) { return o.element==element });
108985 this._cacheObserverCallbacks();
108986 },
108987
108988 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
108989 if(this[eventName+'Count'] > 0)
108990 this.observers.each( function(o) {
108991 if(o[eventName]) o[eventName](eventName, draggable, event);
108992 });
108993 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
108994 },
108995
108996 _cacheObserverCallbacks: function() {
108997 ['onStart','onEnd','onDrag'].each( function(eventName) {
108998 Draggables[eventName+'Count'] = Draggables.observers.select(
108999 function(o) { return o[eventName]; }
109000 ).length;
109001 });
109002 }
109003 }
109004
109005 /*--------------------------------------------------------------------------*/
109006
109007 var Draggable = Class.create({
109008 initialize: function(element) {
109009 var defaults = {
109010 handle: false,
109011 reverteffect: function(element, top_offset, left_offset) {
109012 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
109013 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
109014 queue: {scope:'_draggable', position:'end'}
109015 });
109016 },
109017 endeffect: function(element) {
109018 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
109019 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
109020 queue: {scope:'_draggable', position:'end'},
109021 afterFinish: function(){
109022 Draggable._dragging[element] = false
109023 }
109024 });
109025 },
109026 zindex: 1000,
109027 revert: false,
109028 quiet: false,
109029 scroll: false,
109030 scrollSensitivity: 20,
109031 scrollSpeed: 15,
109032 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
109033 delay: 0
109034 };
109035
109036 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
109037 Object.extend(defaults, {
109038 starteffect: function(element) {
109039 element._opacity = Element.getOpacity(element);
109040 Draggable._dragging[element] = true;
109041 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
109042 }
109043 });
109044
109045 var options = Object.extend(defaults, arguments[1] || { });
109046
109047 this.element = $(element);
109048
109049 if(options.handle && Object.isString(options.handle))
109050 this.handle = this.element.down('.'+options.handle, 0);
109051
109052 if(!this.handle) this.handle = $(options.handle);
109053 if(!this.handle) this.handle = this.element;
109054
109055 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
109056 options.scroll = $(options.scroll);
109057 this._isScrollChild = Element.childOf(this.element, options.scroll);
109058 }
109059
109060 Element.makePositioned(this.element); // fix IE
109061
109062 this.options = options;
109063 this.dragging = false;
109064
109065 this.eventMouseDown = this.initDrag.bindAsEventListener(this);
109066 Event.observe(this.handle, "mousedown", this.eventMouseDown);
109067
109068 Draggables.register(this);
109069 },
109070
109071 destroy: function() {
109072 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
109073 Draggables.unregister(this);
109074 },
109075
109076 currentDelta: function() {
109077 return([
109078 parseInt(Element.getStyle(this.element,'left') || '0'),
109079 parseInt(Element.getStyle(this.element,'top') || '0')]);
109080 },
109081
109082 initDrag: function(event) {
109083 if(!Object.isUndefined(Draggable._dragging[this.element]) &&
109084 Draggable._dragging[this.element]) return;
109085 if(Event.isLeftClick(event)) {
109086 // abort on form elements, fixes a Firefox issue
109087 var src = Event.element(event);
109088 if((tag_name = src.tagName.toUpperCase()) && (
109089 tag_name=='INPUT' ||
109090 tag_name=='SELECT' ||
109091 tag_name=='OPTION' ||
109092 tag_name=='BUTTON' ||
109093 tag_name=='TEXTAREA')) return;
109094
109095 var pointer = [Event.pointerX(event), Event.pointerY(event)];
109096 var pos = Position.cumulativeOffset(this.element);
109097 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
109098
109099 Draggables.activate(this);
109100 Event.stop(event);
109101 }
109102 },
109103
109104 startDrag: function(event) {
109105 this.dragging = true;
109106 if(!this.delta)
109107 this.delta = this.currentDelta();
109108
109109 if(this.options.zindex) {
109110 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
109111 this.element.style.zIndex = this.options.zindex;
109112 }
109113
109114 if(this.options.ghosting) {
109115 this._clone = this.element.cloneNode(true);
109116 this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
109117 if (!this.element._originallyAbsolute)
109118 Position.absolutize(this.element);
109119 this.element.parentNode.insertBefore(this._clone, this.element);
109120 }
109121
109122 if(this.options.scroll) {
109123 if (this.options.scroll == window) {
109124 var where = this._getWindowScroll(this.options.scroll);
109125 this.originalScrollLeft = where.left;
109126 this.originalScrollTop = where.top;
109127 } else {
109128 this.originalScrollLeft = this.options.scroll.scrollLeft;
109129 this.originalScrollTop = this.options.scroll.scrollTop;
109130 }
109131 }
109132
109133 Draggables.notify('onStart', this, event);
109134
109135 if(this.options.starteffect) this.options.starteffect(this.element);
109136 },
109137
109138 updateDrag: function(event, pointer) {
109139 if(!this.dragging) this.startDrag(event);
109140
109141 if(!this.options.quiet){
109142 Position.prepare();
109143 Droppables.show(pointer, this.element);
109144 }
109145
109146 Draggables.notify('onDrag', this, event);
109147
109148 this.draw(pointer);
109149 if(this.options.change) this.options.change(this);
109150
109151 if(this.options.scroll) {
109152 this.stopScrolling();
109153
109154 var p;
109155 if (this.options.scroll == window) {
109156 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
109157 } else {
109158 p = Position.page(this.options.scroll);
109159 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
109160 p[1] += this.options.scroll.scrollTop + Position.deltaY;
109161 p.push(p[0]+this.options.scroll.offsetWidth);
109162 p.push(p[1]+this.options.scroll.offsetHeight);
109163 }
109164 var speed = [0,0];
109165 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
109166 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
109167 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
109168 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
109169 this.startScrolling(speed);
109170 }
109171
109172 // fix AppleWebKit rendering
109173 if(Prototype.Browser.WebKit) window.scrollBy(0,0);
109174
109175 Event.stop(event);
109176 },
109177
109178 finishDrag: function(event, success) {
109179 this.dragging = false;
109180
109181 if(this.options.quiet){
109182 Position.prepare();
109183 var pointer = [Event.pointerX(event), Event.pointerY(event)];
109184 Droppables.show(pointer, this.element);
109185 }
109186
109187 if(this.options.ghosting) {
109188 if (!this.element._originallyAbsolute)
109189 Position.relativize(this.element);
109190 delete this.element._originallyAbsolute;
109191 Element.remove(this._clone);
109192 this._clone = null;
109193 }
109194
109195 var dropped = false;
109196 if(success) {
109197 dropped = Droppables.fire(event, this.element);
109198 if (!dropped) dropped = false;
109199 }
109200 if(dropped && this.options.onDropped) this.options.onDropped(this.element);
109201 Draggables.notify('onEnd', this, event);
109202
109203 var revert = this.options.revert;
109204 if(revert && Object.isFunction(revert)) revert = revert(this.element);
109205
109206 var d = this.currentDelta();
109207 if(revert && this.options.reverteffect) {
109208 if (dropped == 0 || revert != 'failure')
109209 this.options.reverteffect(this.element,
109210 d[1]-this.delta[1], d[0]-this.delta[0]);
109211 } else {
109212 this.delta = d;
109213 }
109214
109215 if(this.options.zindex)
109216 this.element.style.zIndex = this.originalZ;
109217
109218 if(this.options.endeffect)
109219 this.options.endeffect(this.element);
109220
109221 Draggables.deactivate(this);
109222 Droppables.reset();
109223 },
109224
109225 keyPress: function(event) {
109226 if(event.keyCode!=Event.KEY_ESC) return;
109227 this.finishDrag(event, false);
109228 Event.stop(event);
109229 },
109230
109231 endDrag: function(event) {
109232 if(!this.dragging) return;
109233 this.stopScrolling();
109234 this.finishDrag(event, true);
109235 Event.stop(event);
109236 },
109237
109238 draw: function(point) {
109239 var pos = Position.cumulativeOffset(this.element);
109240 if(this.options.ghosting) {
109241 var r = Position.realOffset(this.element);
109242 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
109243 }
109244
109245 var d = this.currentDelta();
109246 pos[0] -= d[0]; pos[1] -= d[1];
109247
109248 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
109249 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
109250 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
109251 }
109252
109253 var p = [0,1].map(function(i){
109254 return (point[i]-pos[i]-this.offset[i])
109255 }.bind(this));
109256
109257 if(this.options.snap) {
109258 if(Object.isFunction(this.options.snap)) {
109259 p = this.options.snap(p[0],p[1],this);
109260 } else {
109261 if(Object.isArray(this.options.snap)) {
109262 p = p.map( function(v, i) {
109263 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
109264 } else {
109265 p = p.map( function(v) {
109266 return (v/this.options.snap).round()*this.options.snap }.bind(this))
109267 }
109268 }}
109269
109270 var style = this.element.style;
109271 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
109272 style.left = p[0] + "px";
109273 if((!this.options.constraint) || (this.options.constraint=='vertical'))
109274 style.top = p[1] + "px";
109275
109276 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
109277 },
109278
109279 stopScrolling: function() {
109280 if(this.scrollInterval) {
109281 clearInterval(this.scrollInterval);
109282 this.scrollInterval = null;
109283 Draggables._lastScrollPointer = null;
109284 }
109285 },
109286
109287 startScrolling: function(speed) {
109288 if(!(speed[0] || speed[1])) return;
109289 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
109290 this.lastScrolled = new Date();
109291 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
109292 },
109293
109294 scroll: function() {
109295 var current = new Date();
109296 var delta = current - this.lastScrolled;
109297 this.lastScrolled = current;
109298 if(this.options.scroll == window) {
109299 with (this._getWindowScroll(this.options.scroll)) {
109300 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
109301 var d = delta / 1000;
109302 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
109303 }
109304 }
109305 } else {
109306 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
109307 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
109308 }
109309
109310 Position.prepare();
109311 Droppables.show(Draggables._lastPointer, this.element);
109312 Draggables.notify('onDrag', this);
109313 if (this._isScrollChild) {
109314 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
109315 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
109316 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
109317 if (Draggables._lastScrollPointer[0] < 0)
109318 Draggables._lastScrollPointer[0] = 0;
109319 if (Draggables._lastScrollPointer[1] < 0)
109320 Draggables._lastScrollPointer[1] = 0;
109321 this.draw(Draggables._lastScrollPointer);
109322 }
109323
109324 if(this.options.change) this.options.change(this);
109325 },
109326
109327 _getWindowScroll: function(w) {
109328 var T, L, W, H;
109329 with (w.document) {
109330 if (w.document.documentElement && documentElement.scrollTop) {
109331 T = documentElement.scrollTop;
109332 L = documentElement.scrollLeft;
109333 } else if (w.document.body) {
109334 T = body.scrollTop;
109335 L = body.scrollLeft;
109336 }
109337 if (w.innerWidth) {
109338 W = w.innerWidth;
109339 H = w.innerHeight;
109340 } else if (w.document.documentElement && documentElement.clientWidth) {
109341 W = documentElement.clientWidth;
109342 H = documentElement.clientHeight;
109343 } else {
109344 W = body.offsetWidth;
109345 H = body.offsetHeight
109346 }
109347 }
109348 return { top: T, left: L, width: W, height: H };
109349 }
109350 });
109351
109352 Draggable._dragging = { };
109353
109354 /*--------------------------------------------------------------------------*/
109355
109356 var SortableObserver = Class.create({
109357 initialize: function(element, observer) {
109358 this.element = $(element);
109359 this.observer = observer;
109360 this.lastValue = Sortable.serialize(this.element);
109361 },
109362
109363 onStart: function() {
109364 this.lastValue = Sortable.serialize(this.element);
109365 },
109366
109367 onEnd: function() {
109368 Sortable.unmark();
109369 if(this.lastValue != Sortable.serialize(this.element))
109370 this.observer(this.element)
109371 }
109372 });
109373
109374 var Sortable = {
109375 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
109376
109377 sortables: { },
109378
109379 _findRootElement: function(element) {
109380 while (element.tagName.toUpperCase() != "BODY") {
109381 if(element.id && Sortable.sortables[element.id]) return element;
109382 element = element.parentNode;
109383 }
109384 },
109385
109386 options: function(element) {
109387 element = Sortable._findRootElement($(element));
109388 if(!element) return;
109389 return Sortable.sortables[element.id];
109390 },
109391
109392 destroy: function(element){
109393 var s = Sortable.options(element);
109394
109395 if(s) {
109396 Draggables.removeObserver(s.element);
109397 s.droppables.each(function(d){ Droppables.remove(d) });
109398 s.draggables.invoke('destroy');
109399
109400 delete Sortable.sortables[s.element.id];
109401 }
109402 },
109403
109404 create: function(element) {
109405 element = $(element);
109406 var options = Object.extend({
109407 element: element,
109408 tag: 'li', // assumes li children, override with tag: 'tagname'
109409 dropOnEmpty: false,
109410 tree: false,
109411 treeTag: 'ul',
109412 overlap: 'vertical', // one of 'vertical', 'horizontal'
109413 constraint: 'vertical', // one of 'vertical', 'horizontal', false
109414 containment: element, // also takes array of elements (or id's); or false
109415 handle: false, // or a CSS class
109416 only: false,
109417 delay: 0,
109418 hoverclass: null,
109419 ghosting: false,
109420 quiet: false,
109421 scroll: false,
109422 scrollSensitivity: 20,
109423 scrollSpeed: 15,
109424 format: this.SERIALIZE_RULE,
109425
109426 // these take arrays of elements or ids and can be
109427 // used for better initialization performance
109428 elements: false,
109429 handles: false,
109430
109431 onChange: Prototype.emptyFunction,
109432 onUpdate: Prototype.emptyFunction
109433 }, arguments[1] || { });
109434
109435 // clear any old sortable with same element
109436 this.destroy(element);
109437
109438 // build options for the draggables
109439 var options_for_draggable = {
109440 revert: true,
109441 quiet: options.quiet,
109442 scroll: options.scroll,
109443 scrollSpeed: options.scrollSpeed,
109444 scrollSensitivity: options.scrollSensitivity,
109445 delay: options.delay,
109446 ghosting: options.ghosting,
109447 constraint: options.constraint,
109448 handle: options.handle };
109449
109450 if(options.starteffect)
109451 options_for_draggable.starteffect = options.starteffect;
109452
109453 if(options.reverteffect)
109454 options_for_draggable.reverteffect = options.reverteffect;
109455 else
109456 if(options.ghosting) options_for_draggable.reverteffect = function(element) {
109457 element.style.top = 0;
109458 element.style.left = 0;
109459 };
109460
109461 if(options.endeffect)
109462 options_for_draggable.endeffect = options.endeffect;
109463
109464 if(options.zindex)
109465 options_for_draggable.zindex = options.zindex;
109466
109467 // build options for the droppables
109468 var options_for_droppable = {
109469 overlap: options.overlap,
109470 containment: options.containment,
109471 tree: options.tree,
109472 hoverclass: options.hoverclass,
109473 onHover: Sortable.onHover
109474 }
109475
109476 var options_for_tree = {
109477 onHover: Sortable.onEmptyHover,
109478 overlap: options.overlap,
109479 containment: options.containment,
109480 hoverclass: options.hoverclass
109481 }
109482
109483 // fix for gecko engine
109484 Element.cleanWhitespace(element);
109485
109486 options.draggables = [];
109487 options.droppables = [];
109488
109489 // drop on empty handling
109490 if(options.dropOnEmpty || options.tree) {
109491 Droppables.add(element, options_for_tree);
109492 options.droppables.push(element);
109493 }
109494
109495 (options.elements || this.findElements(element, options) || []).each( function(e,i) {
109496 var handle = options.handles ? $(options.handles[i]) :
109497 (options.handle ? $(e).select('.' + options.handle)[0] : e);
109498 options.draggables.push(
109499 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
109500 Droppables.add(e, options_for_droppable);
109501 if(options.tree) e.treeNode = element;
109502 options.droppables.push(e);
109503 });
109504
109505 if(options.tree) {
109506 (Sortable.findTreeElements(element, options) || []).each( function(e) {
109507 Droppables.add(e, options_for_tree);
109508 e.treeNode = element;
109509 options.droppables.push(e);
109510 });
109511 }
109512
109513 // keep reference
109514 this.sortables[element.id] = options;
109515
109516 // for onupdate
109517 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
109518
109519 },
109520
109521 // return all suitable-for-sortable elements in a guaranteed order
109522 findElements: function(element, options) {
109523 return Element.findChildren(
109524 element, options.only, options.tree ? true : false, options.tag);
109525 },
109526
109527 findTreeElements: function(element, options) {
109528 return Element.findChildren(
109529 element, options.only, options.tree ? true : false, options.treeTag);
109530 },
109531
109532 onHover: function(element, dropon, overlap) {
109533 if(Element.isParent(dropon, element)) return;
109534
109535 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
109536 return;
109537 } else if(overlap>0.5) {
109538 Sortable.mark(dropon, 'before');
109539 if(dropon.previousSibling != element) {
109540 var oldParentNode = element.parentNode;
109541 element.style.visibility = "hidden"; // fix gecko rendering
109542 dropon.parentNode.insertBefore(element, dropon);
109543 if(dropon.parentNode!=oldParentNode)
109544 Sortable.options(oldParentNode).onChange(element);
109545 Sortable.options(dropon.parentNode).onChange(element);
109546 }
109547 } else {
109548 Sortable.mark(dropon, 'after');
109549 var nextElement = dropon.nextSibling || null;
109550 if(nextElement != element) {
109551 var oldParentNode = element.parentNode;
109552 element.style.visibility = "hidden"; // fix gecko rendering
109553 dropon.parentNode.insertBefore(element, nextElement);
109554 if(dropon.parentNode!=oldParentNode)
109555 Sortable.options(oldParentNode).onChange(element);
109556 Sortable.options(dropon.parentNode).onChange(element);
109557 }
109558 }
109559 },
109560
109561 onEmptyHover: function(element, dropon, overlap) {
109562 var oldParentNode = element.parentNode;
109563 var droponOptions = Sortable.options(dropon);
109564
109565 if(!Element.isParent(dropon, element)) {
109566 var index;
109567
109568 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
109569 var child = null;
109570
109571 if(children) {
109572 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
109573
109574 for (index = 0; index < children.length; index += 1) {
109575 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
109576 offset -= Element.offsetSize (children[index], droponOptions.overlap);
109577 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
109578 child = index + 1 < children.length ? children[index + 1] : null;
109579 break;
109580 } else {
109581 child = children[index];
109582 break;
109583 }
109584 }
109585 }
109586
109587 dropon.insertBefore(element, child);
109588
109589 Sortable.options(oldParentNode).onChange(element);
109590 droponOptions.onChange(element);
109591 }
109592 },
109593
109594 unmark: function() {
109595 if(Sortable._marker) Sortable._marker.hide();
109596 },
109597
109598 mark: function(dropon, position) {
109599 // mark on ghosting only
109600 var sortable = Sortable.options(dropon.parentNode);
109601 if(sortable && !sortable.ghosting) return;
109602
109603 if(!Sortable._marker) {
109604 Sortable._marker =
109605 ($('dropmarker') || Element.extend(document.createElement('DIV'))).
109606 hide().addClassName('dropmarker').setStyle({position:'absolute'});
109607 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
109608 }
109609 var offsets = Position.cumulativeOffset(dropon);
109610 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
109611
109612 if(position=='after')
109613 if(sortable.overlap == 'horizontal')
109614 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
109615 else
109616 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
109617
109618 Sortable._marker.show();
109619 },
109620
109621 _tree: function(element, options, parent) {
109622 var children = Sortable.findElements(element, options) || [];
109623
109624 for (var i = 0; i < children.length; ++i) {
109625 var match = children[i].id.match(options.format);
109626
109627 if (!match) continue;
109628
109629 var child = {
109630 id: encodeURIComponent(match ? match[1] : null),
109631 element: element,
109632 parent: parent,
109633 children: [],
109634 position: parent.children.length,
109635 container: $(children[i]).down(options.treeTag)
109636 }
109637
109638 /* Get the element containing the children and recurse over it */
109639 if (child.container)
109640 this._tree(child.container, options, child)
109641
109642 parent.children.push (child);
109643 }
109644
109645 return parent;
109646 },
109647
109648 tree: function(element) {
109649 element = $(element);
109650 var sortableOptions = this.options(element);
109651 var options = Object.extend({
109652 tag: sortableOptions.tag,
109653 treeTag: sortableOptions.treeTag,
109654 only: sortableOptions.only,
109655 name: element.id,
109656 format: sortableOptions.format
109657 }, arguments[1] || { });
109658
109659 var root = {
109660 id: null,
109661 parent: null,
109662 children: [],
109663 container: element,
109664 position: 0
109665 }
109666
109667 return Sortable._tree(element, options, root);
109668 },
109669
109670 /* Construct a [i] index for a particular node */
109671 _constructIndex: function(node) {
109672 var index = '';
109673 do {
109674 if (node.id) index = '[' + node.position + ']' + index;
109675 } while ((node = node.parent) != null);
109676 return index;
109677 },
109678
109679 sequence: function(element) {
109680 element = $(element);
109681 var options = Object.extend(this.options(element), arguments[1] || { });
109682
109683 return $(this.findElements(element, options) || []).map( function(item) {
109684 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
109685 });
109686 },
109687
109688 setSequence: function(element, new_sequence) {
109689 element = $(element);
109690 var options = Object.extend(this.options(element), arguments[2] || { });
109691
109692 var nodeMap = { };
109693 this.findElements(element, options).each( function(n) {
109694 if (n.id.match(options.format))
109695 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
109696 n.parentNode.removeChild(n);
109697 });
109698
109699 new_sequence.each(function(ident) {
109700 var n = nodeMap[ident];
109701 if (n) {
109702 n[1].appendChild(n[0]);
109703 delete nodeMap[ident];
109704 }
109705 });
109706 },
109707
109708 serialize: function(element) {
109709 element = $(element);
109710 var options = Object.extend(Sortable.options(element), arguments[1] || { });
109711 var name = encodeURIComponent(
109712 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
109713
109714 if (options.tree) {
109715 return Sortable.tree(element, arguments[1]).children.map( function (item) {
109716 return [name + Sortable._constructIndex(item) + "[id]=" +
109717 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
109718 }).flatten().join('&');
109719 } else {
109720 return Sortable.sequence(element, arguments[1]).map( function(item) {
109721 return name + "[]=" + encodeURIComponent(item);
109722 }).join('&');
109723 }
109724 }
109725 }
109726
109727 // Returns true if child is contained within element
109728 Element.isParent = function(child, element) {
109729 if (!child.parentNode || child == element) return false;
109730 if (child.parentNode == element) return true;
109731 return Element.isParent(child.parentNode, element);
109732 }
109733
109734 Element.findChildren = function(element, only, recursive, tagName) {
109735 if(!element.hasChildNodes()) return null;
109736 tagName = tagName.toUpperCase();
109737 if(only) only = [only].flatten();
109738 var elements = [];
109739 $A(element.childNodes).each( function(e) {
109740 if(e.tagName && e.tagName.toUpperCase()==tagName &&
109741 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
109742 elements.push(e);
109743 if(recursive) {
109744 var grandchildren = Element.findChildren(e, only, recursive, tagName);
109745 if(grandchildren) elements.push(grandchildren);
109746 }
109747 });
109748
109749 return (elements.length>0 ? elements.flatten() : []);
109750 }
109751
109752 Element.offsetSize = function (element, type) {
109753 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
109754 }