Skip to content

Instantly share code, notes, and snippets.

@sebmarkbage
Created October 22, 2009 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebmarkbage/216161 to your computer and use it in GitHub Desktop.
Save sebmarkbage/216161 to your computer and use it in GitHub Desktop.
/*
@import Event.Drag.js
Script: Element.Draggable.js
Enables HTML 5 drag operation events in legacy browsers.
License:
MIT-style license.
Authors:
Sebastian Markbåge
*/
(function(window, document){
var startEvent = null;
var useNativeIE = false;
var simulateDragEvent = Element.dispatchDragEvent;
var dragPreStart = function(event){
if (startEvent) return;
startEvent = (Browser.Engine.gecko) ? new Event($extend({}, event.event)) : event;
event.preventDefault();
document.addEvents({ mousemove: dragCheck, mouseup: dragCancel, selectstart: stopEvent });
};
var dragCancel = function(){
this.removeEvents({ mousemove: dragCheck, mouseup: dragCancel });
startEvent = null;
};
var dragCheck = function(event){
if (Math.sqrt(Math.pow(event.page.x - startEvent.page.x, 2) + Math.pow(event.page.y - startEvent.page.y, 2)) <= 20) return;
this.removeEvents({ mousemove: dragCheck, mouseup: dragCancel });
event.stop();
if (useNativeIE && startEvent.target.dragDrop){
startEvent.target.dragDrop();
} else if (simulateDragEvent(startEvent.event, 'dragstart')){
attachListeners();
return;
}
this.removeEvent('selectstart', stopEvent);
startEvent = null;
};
// TODO: Cancelable using ESC key
var attachListeners = window.addEventListener ? function(){
var win = window;
win.addEventListener('mousemove', dragOver, true);
win.addEventListener('mouseover', dragEnter, true);
win.addEventListener('mouseout', dragLeave, true);
win.addEventListener('mouseup', dragEnd, true);
} : function(){
var body = document.body;
body.attachEvent('onmousemove', dragOver);
body.attachEvent('onmousemove', dragEnter);
body.attachEvent('onmousemove', dragLeave);
body.attachEvent('onmouseup', dragEnd);
body.setCapture();
};
var detachListeners = window.addEventListener ? function(){
var win = window;
win.removeEventListener('mousemove', dragOver, true);
win.removeEventListener('mouseover', dragEnter, true);
win.removeEventListener('mouseout', dragLeave, true);
win.removeEventListener('mouseup', dragEnd, true);
} : function(){
var body = document.body;
body.detachEvent('onmousemove', dragOver);
body.detachEvent('onmousemove', dragEnter);
body.detachEvent('onmousemove', dragLeave);
body.detachEvent('onmouseup', dragEnd);
document.releaseCapture();
};
var dragOver = function(event){
event = event || window.event;
stopEvent(event);
resetDropEffect('none');
var end = !simulateDragEvent(event, 'drag', startEvent.target);
resetDropEffect();
if (end || simulateDragEvent(event, 'dragover')) resetDropEffect('none');
if (end) dragEnd(event);
};
var dragEnter = function(event){
event = event || window.event;
stopEvent(event);
resetDropEffect();
if (simulateDragEvent(event, 'dragenter')) resetDropEffect('none');
};
var dragLeave = function(event){
event = event || window.event;
stopEvent(event);
simulateDragEvent(event, 'dragleave');
};
var dragEnd = function(event){
event = event || window.event;
stopEvent(event);
var dataTransfer = new DataTransfer();
if (dataTransfer.get && dataTransfer.get('dropAllowed'))
simulateDragEvent(event, 'drop');
simulateDragEvent(event, 'dragend', startEvent.target);
startEvent = null;
this.removeEvent('selectstart', stopEvent);
detachListeners();
};
var stopEvent = window.addEventListener ? function(event){
event.stopPropagation();
event.preventDefault();
} : function(event){
event = event || window.event;
event.cancelBubble = true;
event.returnValue = false;
};
var resetDropEffect = function(value){
var dataTransfer = new DataTransfer();
if (dataTransfer.set) dataTransfer.set('dropEffect', value);
};
var blockNativeStart = function(event){
if ((event.event || event).dataTransfer){ event.preventDefault(); }
};
/*if (Browser.Engine.gecko19) window.addEventListener('dragover', function(event){
simulateDragEvent(event, 'drag', startEvent.target);
}, true);*/
Element.Properties.draggable = /*Browser.Engine.webkit ? {
// TODO: Gesture option
// TODO: Too many bugs in WebKit on PC? Use fallback instead?
get: function(){
var drag = this.getStyle('-khtml-user-drag');
if (!drag) return this.draggable == undefined ? 'auto' : this.draggable;
return drag == 'auto' ? drag : (drag == 'element');
},
set: function(value){
this.setStyle('-khtml-user-drag', value == 'auto' ? 'auto' : (value && value != 'false' ? 'element' : 'none'));
this.setStyle('-webkit-user-select', 'none');
//if (value) this.addEvent('mousedown', stopEvent);
//else this.removeEvent('mousedown', stopEvent);
}
} :*/ {
get: function(){
//if (this.draggable != undefined) return this.draggable;
var events = this.retrieve('events');
return !events || !events['mousedown'] || !events['mousedown'].contains(dragPreStart);
},
set: function(value){
if (!useNativeIE && Browser.Engine.trident) this.addEvent('dragstart', blockNativeStart);
/*if (this.draggable != undefined) this.draggable = value;
else*/
if (value) this.addEvent('mousedown', dragPreStart);
else this.removeEvent('mousedown', dragPreStart);
}
};
})(window, document);
/*
@import Element.Measure.js
Script: Element.Snapshot.js
Extends Element with methods that clones an element with it's current relative stylesheets intact.
License:
MIT-style license.
Authors:
Sebastian Markbåge
*/
(function(window){
var isPercentage = /\%$/, isRelative = /\%|em|ex/, isDigits = /^\d+$/, hasUnit = /[a-z]{2}$/i,
isExcludedStyle = /cssText|^length$|parentRule|hasLayout/, isSizeConstraint = /(max|min)(Width|Height)/,
isZeroClip = /rect\((0(px)?\, ){3}0(px?)\)/,
relativeSides = ['textIndent', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop'];
var getCurrentStyle = window.getComputedStyle ? function(element){
return window.getComputedStyle(element, null);
} : function(element){
return element.currentStyle || element.style;
};
var getRelativeFontSize = function(element){
var z = getCurrentStyle(element).fontSize, u, i = 0;
if (hasUnit.test(z)) i = -2;
else if (isPercentage.test(z)) i = -1;
u = z.substr(z.length + i);
z = parseFloat(z.substr(0, z.length + i));
if (isRelative.test(u) && element.parentNode && element.parentNode != window.document.body){
var p = arguments.callee(element.parentNode);
z = (u == '%' ? z / 100 : (u == 'ex' ? z / 2 : z)) * p.size;
u = p.unit;
}
return { size: z, unit: u, value: z + u };
};
Element.implement({
cloneStyles: function(from){
var styles = getCurrentStyle(from);
for (var k in styles){
if (isDigits.test(k)) k = styles[k];
if (k == 'clip' && isZeroClip.test(styles[k]))
this.style['clip'] = 'auto';
else if (!isExcludedStyle.test(k) && styles[k] != '' && $type(styles[k]) != 'function'
&& (!isSizeConstraint.test(k) || styles[k] != '-1px'))
this.style[k] = styles[k];
}
return this;
},
snapshot: function(contents, keepid){
var clone = this.clone(contents, keepid);
if (!this.parentNode || this.parentNode.nodeType == 11) return clone;
var offsetParent = this;
while ((offsetParent = offsetParent.parentNode) && getCurrentStyle(offsetParent).display == 'inline');
var thisSize = this.getComputedSize(), parentWidth = document.id(offsetParent).getComputedSize().width;
var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
for (var i = ce.length; i--;) Element.cloneStyles(ce[i], te[i]);
var styles = getCurrentStyle(this);
relativeSides.each(function(style){
var value = styles[style];
if (isPercentage.test(value))
newElement.setStyle(style, parentWidth * parseFloat(value.substr(0, value.length - 1)) / 100);
});
return clone.setStyles({
'font-size': getRelativeFontSize(this).value,
'width': thisSize.width,
'height': thisSize.height
});
}
});
})(window);
/*
@import Event.DataTransfer.js
@import Element.Snapshot.js
Script: Event.DataTransfer.Feedback.js
Extends DataTransfer with methods to set image represenation of current drag operations.
License:
MIT-style license.
Authors:
Sebastian Markbåge
*/
(function(document){
var ghost, lastTarget;
var simulateDragEvent = Element.dispatchDragEvent;
var efpProp = Browser.Engine.webkit || Browser.Engine.presto ? 'page' : 'client',
offsetProp = Browser.Engine.trident4 ? 'page' : 'client',
positioning = Browser.Engine.trident4 ? 'absolute' : 'fixed',
emptyElement = new Element('div', { styles: { width: 1, height: 1, visibility: 'hidden' }});
var moveGhost = function(event){
if (!ghost.nativ)
ghost.element.setStyles({
left: event[offsetProp].x - ghost.offset.x,
top: event[offsetProp].y - ghost.offset.y
});
};
var endGhost = function(event){
ghost.nativ = false;
//moveGhost(lastDrag || event);
if (ghost.revert && event.dataTransfer.get('dropEffect') == 'none'){
ghost.revert.start(ghost.revertTarget).chain(cleanUpGhost);
} else {
cleanUpGhost();
}
};
var cleanUpGhost = function(){
if (!ghost) return;
if (ghost.effect) ghost.effect.cancel();
ghost.element.destroy();
document.body.removeEvents({ drag: moveGhost, dragend: endGhost });
ghost = undefined;
lastTarget = undefined;
};
var dragOverGhost = function(event){
event.stopPropagation();
var target = getTarget(event);
if (lastTarget != target){
if (simulateDragEvent(event.event, 'dragenter', target, lastTarget) === false) event.preventDefault();
if (lastTarget) simulateDragEvent(event.event, 'dragleave', lastTarget, target);
lastTarget = target;
}
else if (simulateDragEvent(event.event, 'dragover', target) === false) event.preventDefault();
};
var dropOnGhost = function(event){
event.stop();
simulateDragEvent(event, 'drop', getTarget(event));
};
var getTarget = document.elementFromPoint ? function(event){
var doc = event.target.ownerDocument, style = ghost.element.style;
var display = style.display;
style.display = 'none';
var target = doc.elementFromPoint(event[efpProp].x, event[efpProp].y);
style.display = display;
return target;
} : function(){
return document.body;
};
this.DataTransfer.implement({
setDragFeedback: function(element, options){
/*
options = {
opacity: 1,
class: '...',
styles: { ... },
revert: false,
container: false,
limit: false,
offset: { x: undefined, y: undefined },
wrapImage: true,
native: true
}
*/
element = document.id(element);
var dataTransfer = this.dataTransfer;
if (!element){
cleanUpGhost();
if (dataTransfer && dataTransfer.setDragImage)
dataTransfer.setDragImage(emptyElement, 0, 0);
return this;
}
if (!ghost)
document.body.addEvents({ drag: moveGhost, dragend: endGhost });
else
ghost.element.destroy();
ghost = {};
if (options.offset){
ghost.offset = options.offset;
} else if (this.event && element.getParent()){
var l = element.getPosition(), v = event.page, b = element.getDocument().body;
ghost.offset = { x: v.x - l.x - b.offsetLeft, y: v.y - l.y - b.offsetTop };
} else {
ghost.offset = { x: -1, y: -1 };
}
ghost.limit = document.id(options.container) ? options.container.getCoordinates() : options.limit;
//var size = ghost.element.getSize();
ghost.nativ = options['native'] !== false && !ghost.limit && (!Browser.Engine.webkit || Browser.Platform.mac) && !Browser.Engine.gecko && dataTransfer && dataTransfer.setDragImage;
if (element.getParent()) element = element.snapshot(true, false);
element.set({ styles: options.styles, 'class': options['class'], opacity: options.opacity });
if (ghost.nativ){
if (options.wrapImage !== false && element.get('tag') == 'img') element = new Element('div').adopt(element.setStyles({ margin: 0, left: 0, top: 0 }));
if (Browser.Engine.gecko) element.set('opacity', element.get('opacity') / 0.75);
}
if (options.revert && this.event && (!ghost.nativ || !Browser.Engine.webkit)){
ghost.revert = new Fx.Morph(element, $extend({duration: 250, link: 'cancel'}, options.revert));
ghost.revertTarget = {
left: this.event[offsetProp].x - ghost.offset.x,
top: this.event[offsetProp].y - ghost.offset.y
};
}
ghost.element = element.setStyles({
'position': positioning,
'z-index': 2000,
'left': -2000,
'top': 0,
'max-width': '100%',
'max-height': '100%',
'float': 'none',
'display': 'block',
'margin': 0
}).inject(document.body);
if (ghost.nativ){
dataTransfer.setDragImage(element, ghost.offset.x, ghost.offset.y);
} else {
if (dataTransfer && dataTransfer.setDragImage) dataTransfer.setDragImage(emptyElement, 0, 0);
element.addEvents({
dragenter: dragOverGhost,
dragleave: Event.stopPropagation,
dragover: dragOverGhost,
drop: dropOnGhost
});
}
return this;
},
setDragImage: function(image, x, y){
return this.setDragFeedback(image, { offset: { x: y, y: y }, wrapImage: false });
}
});
})(document);
/*
Script: Event.DataTransfer.js
Contains the dataTransfer object attached to events during drag & drop operations in HTML 5.
License:
MIT-style license.
Authors:
Sebastian Markbåge
*/
(function(document){
var currentDrag;
var getCurrentDrag = function(){
if (currentDrag) return currentDrag;
document.body.addEvent('dragend', cleanUpDrag);
return currentDrag = { data: {} };
};
var cleanUpDrag = function(){
currentDrag = false;
document.body.removeEvent('dragend', cleanUpDrag);
};
var limitedFormats = Browser.Engine.trident || (Browser.Engine.webkit && !Browser.Platform.mac);
var formatMap = limitedFormats ? { 'text/plain': 'Text', 'text/uri-list': 'URL' }
: (Browser.Engine.gecko ? { 'text/uri-list' : 'text/x-moz-url' } : {});
this.DataTransfer.implement({
clearData: function(format){
if ($type(format) == 'object'){
this.get('types').each(this.clearData, this);
return this;
}
if (this.dataTransfer) this.dataTransfer.clearData(formatMap[format] || format);
if (currentDrag) delete currentDrag.data[format];
return this;
},
hasData: function(format){
var dataTransfer = this.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.contains(formatMap[format] || format)) return true;
return currentDrag && currentDrag.data[format] != undefined;
},
setData: function(format, data){
if ($type(format) == 'object'){
for (var f in format) this.setData(f, format[f]);
return this;
}
var dataTransfer = this.dataTransfer;
if (dataTransfer && $type(data) == 'string'){
var map = formatMap[format];
if (map || !limitedFormats){
dataTransfer.setData(map || format, data);
if (currentDrag) delete currentDrag.data[format];
return this;
}
}
getCurrentDrag().data[format] = data;
},
getData: function(format){
if (!format){
var types = this.get('types');
return types.map(this.getData, this).associate(types);
}
if (currentDrag && currentDrag.data[format]) return currentDrag.data[format];
var dataTransfer = this.dataTransfer;
if (!dataTransfer) return undefined;
var map = formatMap[format];
return map || !limitedFormats ? dataTransfer.getData(map || format) : undefined;
},
get: function(name){
var dataTransfer = this.dataTransfer;
switch (name){
case 'data': return this.getData();
case 'types':
var types = dataTransfer && dataTransfer.types ? dataTransfer.types.map(Hash.keyOf, formatMap) :
(limitedFormats ? ['text/plain', 'text/uri-list'] : []);
if (currentDrag) for (var type in currentDrag.data) types.include(type);
return types;
case 'effectAllowed':
return dataTransfer ? dataTransfer[name] : (currentDrag ? currentDrag[name] : false) || 'uninitialized';
case 'dropEffect':
var current = dataTransfer || currentDrag;
if (current && current[name]) return current[name];
var allowed = this.get('effectAllowed');
if (allowed == 'none' || allowed == 'move') return allowed;
return allowed.substr(0, 4) == 'link' ? 'link' : 'copy';
case 'dropAllowed':
var allowed = this.get('effectAllowed');
return allowed == 'uninitialized' || allowed == 'all' || allowed.contains(this.get('dropEffect'));
default:
return currentDrag ? currentDrag[name] : undefined;
}
},
set: function(name, value){
switch (name){
case 'data': return this.setData(value);
case 'dropEffect':
// if (!(/^(none|copy|link|move)$/).test(type)) return this;
case 'effectAllowed':
// if (!(/^(none|copy|copyLink|copyMove|link|linkMove|move|all|uninitialized)$/).test(type)) return this;
if (this.dataTransfer){
this.dataTransfer[name] = value;
return this;
}
}
getCurrentDrag()[name] = value;
return this;
}
});
})(document);
/*
Script: Event.Drag.js
Extends the Event native to also cover drag and drop events.
License:
MIT-style license.
Authors:
Sebastian Markbåge
*/
// This logic should be moved to Core to avoid duplicate code and a cleaner model
(function(){
var originalConstructor = Event.prototype.constructor,
originalProperties = Event,
lastTarget;
this.Event = new Native({
legacy: Event,
name: 'Event',
initialize: function(event, win){
event = originalConstructor.apply(this, arguments);
win = win || window;
if (!event.type.match(/drag|drop/)) return event;
if (!event.relatedTarget && event.type.match(/enter|leave/)){
// TODO: Better relatedTarget solution for WebKit
var related = lastTarget || event.event.relatedTarget || event.event.fromElement || event.event.toElement;
if (!(function(){
while (related && related.nodeType == 3) related = related.parentNode;
return true;
}).create({ attempt: Browser.Engine.gecko })()) related = false;
event.relatedTarget = related;
lastTarget = event.target;
}
if (event.type == 'dragend') lastTarget = undefined;
if (!event.page){
var doc = win.document, e = event.event;
doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
event.page = { x: e.pageX || e.clientX + doc.scrollLeft, y: e.pageY || e.clientY + doc.scrollTop };
event.client = { x: (e.pageX) ? e.pageX - win.pageXOffset : e.clientX, y: (e.pageY) ? e.pageY - win.pageYOffset : e.clientY };
}
if (!event.dataTransfer) event.dataTransfer = new DataTransfer(event.event.dataTransfer, event);
return event;
}
});
for (var prop in originalProperties) Event[prop] = originalProperties[prop];
$extend(Element.NativeEvents, { dragstart: 2, drag: 2, dragend: 2, dragenter: 2, dragover: 2, dragleave: 2, drop: 2 });
var $check = function(event){
var related = event.relatedTarget;
if(related == undefined) return true;
if(related === false) return false;
return $type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related);
};
$extend(Element.Events, {
dragend: Browser.Engine.webkit ? {
condition: function(event){
// Delay dragend event on webkit until after the drop event to conform with HTML 5 specs.
// Important for correct clean up order. Cannot be done in drop event since that may occur in a different window.
if (event.$delayed) return true;
event.$delayed = true;
this.fireEvent.delay(40, this, ['dragend', event]);
return false;
}
} : {},
drag: Browser.Engine.gecko19 ? {
condition: function(event){
return event.page.x != 0 || event.page.y != 0 || event.client.x != 0 || event.client.y != 0;
}
} : {},
dragenterself: { base: 'dragenter', condition: $check },
dragleaveself: { base: 'dragleave', condition: $check }
});
if (!document.createEvent){
//var fireEvent = document.createElement('div').fireEvent;
Element.dispatchDragEvent = function(original, type, target, relatedTarget){
var event = document.createEventObject(original.event || original);
//event.type = type;
//if (type == 'dragenter') event.fromElement = relatedTarget || original.relatedTarget;
//if (type == 'dragleave') event.toElement = relatedTarget || original.relatedTarget;
var t = document.id(target || original.target || original.srcElement);
if (!t) return true;
return t._fireEvent('on' + type, event);
};
} else if (this.DragEvent && this.DragEvent.prototype){
Element.dispatchDragEvent = function(original, type, target, relatedTarget){
var event;
if ((type == 'drag' && Browser.Engine.gecko19) || !original.dataTransfer){
event = document.createEvent('MouseEvents');
event.initMouseEvent(type, true, true, original.view, original.detail,
original.screenX, original.screenY, original.clientX, original.clientY,
original.ctrlKey, original.altKey, original.shiftKey, original.metaKey,
original.button, relatedTarget || original.relatedTarget);
} else {
event = document.createEvent('DragEvents');
event.initDragEvent(type, true, true, original.view, original.detail,
original.screenX, original.screenY, original.clientX, original.clientY,
original.ctrlKey, original.altKey, original.shiftKey, original.metaKey,
original.button, relatedTarget || original.relatedTarget, original.dataTransfer);
}
return (target || original.target).dispatchEvent(event);
};
} else {
var unsafeEvents = Browser.Engine.gecko18 ? ['dragover', 'dragenter', 'dragleave'] : false, safeSuffix = '-safe';
//unsafeEvents = Browser.Engine.presto ? ['drop'] : false;
if (unsafeEvents) // Workaround for dispatching native XUL events
unsafeEvents.each(function(event){
Element.Events[event] = { base: event + safeSuffix };
Element.NativeEvents[event + safeSuffix] = 2;
});
Element.dispatchDragEvent = function(original, type, target, relatedTarget){
if (unsafeEvents && unsafeEvents.contains(type)) type += safeSuffix;
var event = document.createEvent('MouseEvents');
event.initMouseEvent(type, true, true, original.view, original.detail,
original.screenX, original.screenY, original.clientX, original.clientY,
original.ctrlKey, original.altKey, original.shiftKey, original.metaKey,
original.button, relatedTarget || original.relatedTarget);
return (target || original.target).dispatchEvent(event);
};
}
this.DataTransfer = new Native({
name: 'DataTransfer',
initialize: function(dataTransfer, event){
this.event = event;
if (dataTransfer) this.dataTransfer = dataTransfer.dataTransfer || dataTransfer;
}
});
})();
/*
@import Element.Draggable.js
@import Class.Binds.js
@import Fx.Elements.js
*/
var SortableList = new Class({
Implements: [Events, Options],
Binds: ['onDragStart', 'onDragEnd', 'onDragOver', 'onDragLeave', 'onDrop', 'cleanUp'],
options: {
/*
onStart: $empty,
onComplete: $empty,
*/
/* SortableList Options
placeholderBase: false,
placeholderStyle: false,*/
direction: 'right',
moveRegion: .3,
fx: { duration: 200 }
},
/*
fx: false,
placeholder: false,
*/
placeholders: [],
initialize: function(element, options){
this.element = element;
this.setOptions(options);
this.attach();
},
attach: function(){
this.element
.set('draggable', true)
.addEvents({
dragstart: this.onDragStart,
dragend: this.onDragEnd,
dragover: this.onDragOver,
dragleaveself: this.onDragLeave,
drop: this.onDrop
});
},
detach: function(){
this.element
.set('draggable', 'auto')
.removeEvents({
dragstart: this.onDragStart,
dragend: this.onDragEnd,
dragover: this.onDragOver,
dragleaveself: this.onDragLeave,
drop: this.onDrop
});
},
onDragStart: function(event){
var item = event.target;
if (item == this.element){
event.preventDefault();
return;
}
while (item && item.parentNode != this.element) item = item.parentNode;
if (!item) return;
this.fireEvent('start', [item, event.dataTransfer]);
this.getPlaceholder(item, false, true);
this.changePlaceholder();
this.currentDragDisplay = item.getStyle('display');
this.currentDrag = item.setStyle('display', 'none');
},
onDragEnd: function(event){
this.fireEvent('end', [this.currentDrag, event.dataTransfer]);
if (this.currentDrag) this.currentDrag.setStyle('display', this.currentDragDisplay);
this.placeholder = this.currentDrag = this.currentDragDisplay = null;
this.cleanUp();
},
onDragOver: function(event){
var item = event.target;
if (!item) return;
if (item == this.element){
if (!this.placeholder) this.changePlaceholder(this.getPlaceholder());
return;
}
var dropTarget = false;
do {
var tmp;
if (item.retrieve && (tmp = item.retrieve('events')) && (tmp = tmp['drop']) && tmp.keys.length > 0)
dropTarget = true;
} while (item.parentNode != this.element && (item = item.parentNode));
if (item.retrieve && item.retrieve('isPlaceholder')){
this.changePlaceholder(item);
return;
}
var cord = document.id(item).getCoordinates(),
m = this.options.moveRegion,
od = this.options.direction,
h = (od == 'right' || od == 'left'),
x = h ? event.page.x - cord.left : event.page.y - cord.top,
w = h ? cord.width : cord.height,
d = x > w / 2 ? x - w : x;
if (od == 'right' || od == 'down') d = -d;
// If not dropzone
if (!dropTarget || Math.abs(d) <= (m < 1 ? (w * m) : m)){
var ph = this.getPlaceholder(item, d > 0);
//if (this.placeholder == ph) ph = this.getPlaceholder(item, d <= 0);
this.changePlaceholder(ph);
}
},
onDragLeave: function(event){
this.changePlaceholder();
},
onDrop: function(event){
if (this.fx) this.fx.cancel();
this.fireEvent('drop', [this.placeholder, event.dataTransfer]);
event.preventDefault();
this.placeholder = null;
this.cleanUp();
},
getStyles: function(style){
if (style) return $type(style) == 'string' ? Fx.CSS.prototype.search(style) : style;
return {};
},
getPlaceholder: function(item, next, expanded){
var h = (this.options.direction == 'right' || this.options.direction == 'left');
var nph = item;
if (!nph){
nph = this.element.lastChild;
while (nph && (nph.nodeType != 1 || nph.getStyle('display') == 'none')) nph = nph.previousSibling;
item = nph;
next = true;
} else {
while ((nph = (next ? nph.nextSibling : nph.previousSibling)) && (nph.nodeType != 1 || nph.getStyle('display') == 'none'));
}
// If no placeholder is found create a new one
if (!nph || !nph.retrieve || !nph.retrieve('isPlaceholder')){
nph = new Element('li').store('isPlaceholder', true)
if (item)
nph.inject(item, next ? 'after' : 'before');
else
this.element.appendChild(nph);
this.placeholders.push(nph);
var size;
if (item){
size = item.getSize();
size = {
width: size.x + item.getStyle('margin-left').toInt() + item.getStyle('margin-right').toInt(),
height: size.y + item.getStyle('margin-top').toInt() + item.getStyle('margin-bottom').toInt()
};
} else {
size = this.element.getComputedSize();
size = {
width: size.width < 10 ? 10 : size.width,
height: size.height < 10 ? 10 : size.height
};
}
nph.store('targetSize', size);
nph.setStyles({ background: 'transparent', border: 0, padding: 0, margin: 0 }).setStyles(size);
if (expanded) nph.setStyles(nph.getStyles(this.options.placeholderStyle));
else nph.setStyles(h ? { width: 0 } : { height: 0 }).setStyles(nph.getStyles(this.options.placeholderBase));
}
return nph;
},
changePlaceholder: function(nph){
if (nph && this.placeholder == nph) return;
var sz = nph ? nph.retrieve('targetSize') : {},
h = (this.options.direction == 'right' || this.options.direction == 'left'),
prop = {},
hide = $extend(h ? { width: 0 } : { height: 0 }, this.getStyles(this.options.placeholderBase)),
show = $extend(h ? { width: sz.width } : { height: sz.height }, this.getStyles(this.options.placeholderStyle));
this.placeholders.each(function(ph, i){ prop[i] = nph == ph ? show : hide; });
this.placeholder = nph;
if (this.fx){
this.fx.cancel();
this.fx.elements = this.fx.subject = this.placeholders;
} else {
this.fx = new Fx.Elements(this.placeholders, this.options.fx);
}
/*if (Browser.Engine.trident){
this.fx.set(prop);
this.cleanUp();
} else {*/
this.fx.start(prop).chain(this.cleanUp);
//}
},
cleanUp: function(){
var currentPlaceholder = this.placeholder;
this.placeholders.each(function(ph){ if(ph != currentPlaceholder) ph.destroy(); });
this.placeholders = currentPlaceholder ? [currentPlaceholder] : [];
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment