﻿// This is only the script for how the draggable object should work
// Binding this to dom elements (single or groups) should be done
// separately.
// I have binded this to load with a particular setting to our show-modal
// which starts at line 151 in jquery.showModal.js


; (function($) { // secure $ jQuery alias


    // jquery method
    $.fn.drag = function(fn1, fn2, fn3) {
        if (fn2) this.bind('dragstart', fn1); // 2+ args
        if (fn3) this.bind('dragend', fn3); // 3 args
        return !fn1 ? this.trigger('drag') // 0 args
		: this.bind('drag', fn2 ? fn2 : fn1); // 1+ args
    };

    // local refs
    var $event = $.event, $special = $event.special,

    // special event configuration
drag = $special.drag = {
    not: ':input', // don't begin to drag on event.targets that match this selector
    distance: 0, // distance dragged before dragstart
    which: 1, // mouse button pressed to start drag sequence
    setup: function(data) {
        data = $.extend({
            distance: drag.distance,
            which: drag.which,
            not: drag.not
        }, data || {});
        data.distance = squared(data.distance); //  x² + y² = distance²
        $event.add(this, "mousedown", handler, data);
    },
    teardown: function() {
        $event.remove(this, "mousedown", handler);
        if (this === drag.dragging) drag.dragging = drag.proxy = null; // deactivate element
        selectable(this, true); // enable text selection
    }
};

    // handle drag-releatd DOM events
    function handler(event) {
        var elem = this, returned, data = event.data || {};
        // mousemove or mouseup
        if (data.elem) {
            // update event properties...
            elem = event.dragTarget = data.elem; // drag source element
            event.dragProxy = drag.proxy || elem; // proxy element or source
            event.cursorOffsetX = data.pageX - data.left; // mousedown offset
            event.cursorOffsetY = data.pageY - data.top; // mousedown offset
            event.offsetX = event.pageX - event.cursorOffsetX; // element offset
            event.offsetY = event.pageY - event.cursorOffsetY; // element offset
        }
        // mousedown, check some initial props to avoid the switch statement
        else if (drag.dragging || (data.which > 0 && event.which != data.which) ||
		$(event.target).is(data.not)) return;
        // handle various events
        switch (event.type) {
            // mousedown, left click, event.target is not restricted, init dragging 
            case 'mousedown':
                $.extend(data, $(elem).offset(), {
                    elem: elem, target: event.target,
                    pageX: event.pageX, pageY: event.pageY
                }); // store some initial attributes
                $event.add(document, "mousemove mouseup", handler, data);
                selectable(elem, false); // disable text selection
                return false; // prevents text selection in safari 
                // mousemove, check distance, start dragging
            case !drag.dragging && 'mousemove':
                if (squared(event.pageX - data.pageX)
				+ squared(event.pageY - data.pageY) //  x² + y² = distance²
				< data.distance) break; // distance tolerance not reached
                event.target = data.target; // force target from "mousedown" event (fix distance issue)
                returned = hijack(event, "dragstart", elem); // trigger "dragstart", return proxy element
                if (returned !== false) { // "dragstart" not rejected
                    drag.dragging = elem; // activate element
                    drag.proxy = event.dragProxy = $(returned || elem)[0]; // set proxy
                }
                // mousemove, dragging
            case 'mousemove':
                if (drag.dragging) {
                    returned = hijack(event, "drag", elem); // trigger "drag"		
                    if ($special.drop) { // manage drop events
                        $special.drop.allowed = (returned !== false); // prevent drop
                        $special.drop.handler(event); // "dropstart", "dropend"
                    }
                    if (returned !== false) break; // "drag" not rejected, stop		
                    event.type = "mouseup"; // helps "drop" handler behave
                }
                // mouseup, stop dragging
            case 'mouseup':
                $event.remove(document, "mousemove mouseup", handler); // remove page events
                if (drag.dragging) {
                    if ($special.drop) $special.drop.handler(event); // "drop"
                    hijack(event, "dragend", elem); // trigger "dragend"	
                }
                selectable(elem, true); // enable text selection
                drag.dragging = drag.proxy = data.elem = null; // deactivate element
                break;
        }
    };

    // set event type to custom value, and handle it
    function hijack(event, type, elem) {
        event.type = type; // force the event type
        var result = $event.handle.call(elem, event);
        return result === false ? false : result || event.result;
    };

    // return the value squared	
    function squared(value) { return Math.pow(value, 2); };

    // toggles text selection attributes	
    function selectable(elem, bool) {
        if (!elem) return; // maybe element was removed ? 
        elem.unselectable = bool ? "off" : "on"; // IE
        elem.onselectstart = function() { return bool; }; // IE
        if (document.selection && document.selection.empty) document.selection.empty(); // IE
        if (elem.style) elem.style.MozUserSelect = bool ? "" : "none"; // FF
    };

})(jQuery); // confine scope

