Source: js/draggable/draggable.js

/*
# ***** BEGIN LICENSE BLOCK *****
# Assets Library - The open source PHP/JavaScript/CSS library of Les Ateliers Pierrot
# Copyleft (c) 2013-2014 Pierre Cassat and contributors
# <www.ateliers-pierrot.fr> - <contact@ateliers-pierrot.fr>
# License GPL-3.0 <http://www.opensource.org/licenses/gpl-3.0.html>
# Sources <http://github.com/atelierspierrot/assets-library>
#
# Ce programme est un logiciel libre distribué sous licence GNU/GPL.
#
# ***** END LICENSE BLOCK ***** */

/**
 * Draggable tool
 * @param object elt The element to work on
 * @param object opts A set of options to overwrite the class defaults
 */
Draggable.version = "1.0.0";
function Draggable(elt, opts) {

    var defaults = {
            outbound: false,                // allow to put the element outside the window
            frame: 'page',                  // 'page' or 'window' to select the outbound scope
            element: 'div',
            className: 'draggable',
            activeClassName: 'dragged',
            useGhost: true,
            ghostClassName: 'ghost',
            handlerElement: 'div',
            handlerClassName: 'dragger',
            handlerTitle: 'Click to drag this element',
            handler: null
        }, _this;


    createGhost = function(element, _this) {
        if (typeof element!=='object') { return; }
        var ghost = element.cloneNode(true), itemPositions;
        itemPositions = getOffset(element);
        addClassName(ghost, _this.options.ghostClassName);
        ghost.style.width = parseInt(itemPositions.width, 10)+'px';
        ghost.style.height = parseInt(itemPositions.height, 10)+'px';
        ghost.style.left = parseInt(itemPositions.left, 10)+'px';
        ghost.style.top = parseInt(itemPositions.top, 10)+'px';
        ghost.innerHtml = '';
        return ghost;
    }

    _mouseDown = function(e, _this) {
        var event = e || window.event, itemPositions;
        if (!_this.isDragged) {
            if (_this.options.useGhost) {
                _this.ghost = createGhost(_this.element, _this);
                _this.element.parentNode.insertBefore(_this.ghost, _this.element);
            }
            document.documentElement.addEventListener('mousemove', dragElt=function(e){ _mouseDrag(e, _this); }, false);
            document.documentElement.addEventListener('mouseup', stopDragElt=function(e){ _mouseUp(e, _this); }, false);
        }
    }
    
    _mouseDrag = function(e, _this) {
        var event = e || window.event;
        if (!_this.isDragged) {
            itemPositions = getOffset(_this.element);
            _this.itemX = parseInt(itemPositions.left, 10);
            _this.itemY = parseInt(itemPositions.top, 10);
            _this.startX = (event.clientX || event.pageX);
            _this.startY = (event.clientY || event.pageY);
            _this.isDragged = true;

console.debug('positions: ', itemPositions);
console.debug({ startX:_this.startX, startY:_this.startY, itemX:_this.itemX, itemY:_this.itemY });

            addClassName(_this.element, _this.options.activeClassName);
        }
        else {
            var eventX, eventY, eventOffsetX, eventOffsetY;
            eventX = (event.clientX || event.pageX);
            eventY = (event.clientY || event.pageY);
            eventOffsetX = eventX - _this.startX;
            eventOffsetY = eventY - _this.startY;

console.debug({ eventX:eventX, eventY:eventY, eventOffsetX:eventOffsetX, eventOffsetY:eventOffsetY });
console.debug('doDrag ', { left:parseInt(_this.itemX + eventOffsetX), top:parseInt(_this.itemY + eventOffsetY) });

            var new_left = parseInt((_this.itemX + eventOffsetX), 10);
            var new_top = parseInt((_this.itemY + eventOffsetY), 10);
            if (!_this.options.outbound) {
                var frameSizes = {}, elementOffset = getOffset(_this.element);
                if (_this.options.frame==='page') {
                    try {
                        frameSizes = getDocumentSizes();
                    } catch(e) {}
                }
                else if (_this.options.frame==='window') {
                    try {
                        frameSizes = getWindowSizes();
                    } catch(e) {}
                }
console.debug(frameSizes);
                if (new_left<0) new_left = 0;
                if (new_left > (frameSizes.width-elementOffset.width)) {
                    new_left = frameSizes.width-elementOffset.width;
                }
                if (new_top<0) new_top = 0;
                if (new_top > frameSizes.height-elementOffset.height) {
                    new_top = frameSizes.height-elementOffset.height;
                }
            }
            _this.element.style.left = new_left+'px';
            _this.element.style.top = new_top+'px';
        }
    }
    
    _mouseUp = function(e, _this) {
        var event = e || window.event;
        if (_this.options.useGhost) {
            _this.element.parentNode.removeChild(_this.ghost);
        }
        removeClassName(_this.element, _this.options.activeClassName);
        document.documentElement.removeEventListener('mousemove', dragElt, false);
        document.documentElement.removeEventListener('mouseup', stopDragElt, false);
        _this.isDragged = false;
    }

    this.options    =extend(clone(defaults), opts);
    this.element    =elt;
    this.ghost      =null;
    this.startX     =0;
    this.startY     =0;
    this.itemX      =0;
    this.itemY      =0;
    this.isDragged  =false;

    addClassName(this.element, this.options.className);
    if (this.options.handler===null) {
        var handler = document.createElement( this.options.handlerElement );
        addClassName(handler, this.options.handlerClassName);
        this.element.appendChild(handler);
        this.options.handler = handler;
    }
    if (this.options.handlerTitle!==undefined && this.options.handlerTitle!==null && this.options.handlerTitle.length>0) {
        this.options.handler.setAttribute('title', this.options.handlerTitle);
    }
    _this = clone(this);
    this.options.handler.onmousedown = function(e){ _mouseDown(e, _this); return false; };
    this.options.handler.onselectstart = function(){ return false; };
    return this;
}

/**
 * Extend the Element prototype to allow "element._draggable(opts)"
 */
Element.prototype._draggable = function(opts) {
    Draggable(this, opts);
};

// Endfile