﻿// README
//
// There are two steps to adding a property:
//
// 1. Create a member variable to store your property
// 2. Add the get_ and set_ accessors for your property
//
// Remember that both are case sensitive!

/// <reference name="MicrosoftAjax.debug.js" />
/// <reference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js" assembly="AjaxControlToolkit" />
/// <reference name="AjaxControlToolkit.Common.Common.js" assembly="AjaxControlToolkit" />
/// <reference name="Pacem.Web.Extensions.DragDrop.DragBehavior.js" assembly="Pacem.Web.Extensions" />
/// <reference name="Pacem.Web.Extensions.DragDrop.DropTargetBehavior.js" assembly="Pacem.Web.Extensions" />

Type.registerNamespace('Pacem.UI');

Pacem$UI$getDropSiblings = function(container, tagName) {
    /// <value type="Object" DomElement="false" mayBeNull="true">
    /// Returns the list of peers that the entering drag-content can attach to. Null if there's no entering drag-content.
    /// </value>
    var siblings = [];
    var sameTagName = container.getElementsByTagName(tagName);
    for (var j = 0; j < sameTagName.length; j++) {
        if (sameTagName[j].parentNode == container)
            siblings.push(sameTagName[j]);
    }
    return siblings;
}

Pacem.UI.DragDropBehavior = function(element) {
    Pacem.UI.DragDropBehavior.initializeBase(this, [element]);

    this._dragHandle = null;
    this._dropTargets = null;
    this._dropPlaceHolder = null;

    // private
    this._dragBehavior = null;
    this._hoverTarget = null;
    this._initialized = false;

    this._draggingHandler = null;
    this._dragStartHandler = null;
    this._dragFinishHandler = null;

    this._dragOffset = null;
    this._originalCoordinates = null; // JSON: {"parent": parent, "nextSibling": nextSiblling}
}
Pacem.UI.DragDropBehavior.prototype = {
    initialize: function() {
        Pacem.UI.DragDropBehavior.callBaseMethod(this, 'initialize');
        // TODO: Add your initalization code here

        var handle = this._dragHandle ? this._dragHandle : this.get_element();
        this._draggingHandler = Function.createDelegate(this, this._dragging);
        this._dragStartHandler = Function.createDelegate(this, this._dragStart);
        this._dragFinishHandler = Function.createDelegate(this, this._dragFinish);
        this._dragBehavior = $create(Pacem.UI.DragBehavior, null, { "dragStarted": this._dragStartHandler, "dragging": this._draggingHandler, "dragFinished": this._dragFinishHandler }, null, handle);

        if (!this._dropPlaceHolder) {
            this._dropPlaceHolder = this.get_element().cloneNode(true);
            this._dropPlaceHolder.id = this.get_element().id + "_placeHolder";
            this.get_element().ownerDocument.body.appendChild(this._dropPlaceHolder);
            $common.setElementOpacity($get(this._dropPlaceHolder.id), 0.25);
            /*this._dropPlaceHolder.innerHTML = ''; 
            this._dropPlaceHolder.style.backgroundColor = "#99ff99";
            this._dropPlaceHolder.style.borderWidth = "3px";
            this._dropPlaceHolder.style.borderStyle = "solid";
            this._dropPlaceHolder.style.borderColor = "#008800";*/
        }
        Sys.UI.DomElement.setVisibilityMode(this._dropPlaceHolder, Sys.UI.VisibilityMode.collapse);
        Sys.UI.DomElement.setVisible(this._dropPlaceHolder, false);
        //
        this._initialized = true;
    },
    dispose: function() {
        // TODO: Add your cleanup code here
        //
        Pacem.UI.DragDropBehavior.callBaseMethod(this, 'dispose');
    },
    /*
    == EVENTS ==
    */
    add_dropTargetEnter: function(handler) {
        this.get_events().addHandler('dropTargetEnter', handler);
    },
    remove_dropTargetEnter: function(handler) {
        this.get_events().removeHandler('dropTargetEnter', handler);
    },
    raiseDropTargetEnter: function(eventArgs) {
        var handler = this.get_events().getHandler('dropTargetEnter');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    add_dropTargetLeave: function(handler) {
        this.get_events().addHandler('dropTargetLeave', handler);
    },
    remove_dropTargetLeave: function(handler) {
        this.get_events().removeHandler('dropTargetLeave', handler);
    },
    raiseDropTargetLeave: function(eventArgs) {
        var handler = this.get_events().getHandler('dropTargetLeave');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    add_dragStarted: function(handler) {
        this.get_events().addHandler('dragStarted', handler);
    },
    remove_dragStarted: function(handler) {
        this.get_events().removeHandler('dragStarted', handler);
    },
    raiseDragStarted: function(eventArgs) {
        var handler = this.get_events().getHandler('dragStarted');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    add_dragging: function(handler) {
        this.get_events().addHandler('dragging', handler);
    },
    remove_dragging: function(handler) {
        this.get_events().removeHandler('dragging', handler);
    },
    raiseDragging: function(eventArgs) {
        var handler = this.get_events().getHandler('dragging');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    add_dragFinished: function(handler) {
        this.get_events().addHandler('dragFinished', handler);
    },
    remove_dragFinished: function(handler) {
        this.get_events().removeHandler('dragFinished', handler);
    },
    raiseDragFinished: function(eventArgs) {
        var handler = this.get_events().getHandler('dragFinished');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    add_drop: function(handler) {
        this.get_events().addHandler('drop', handler);
    },
    remove_drop: function(handler) {
        this.get_events().removeHandler('drop', handler);
    },
    raiseDrop: function(eventArgs) {
        var handler = this.get_events().getHandler('drop');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    _dragStart: function(sender, args) {
        var el = this.get_element();
        // register position to eventually tween return to original place.
        var nextSibling = el.nextSibling;
        if (nextSibling == this._dropPlaceHolder) nextSibling = nextSibling.nextSibling;
        this._originalCoordinates = { "parent": el.parentNode, "nextSibling": nextSibling };
        //
        var elBounds = Sys.UI.DomElement.getBounds(el);
        this._dragOffset = new Sys.UI.Point(sender._refPoint.x - elBounds.x, sender._refPoint.y - elBounds.y);
        //this._dropPlaceHolder.style.width = String.format("{0}px", elBounds.width);
        //this._dropPlaceHolder.style.height = String.format("{0}px", elBounds.height);
        el.parentNode.insertBefore(this._dropPlaceHolder, el.nextSibling);
        // change element position style
        with (el.style) {
            position = "absolute";
            zIndex = Number.MAX_VALUE;
        }
        if (el.parentNode != document.body)
            document.body.appendChild(el); // append object to the document body to get rid of Sys.UI.DomElement.setlocation method..
        Sys.UI.DomElement.setLocation(el, elBounds.x, elBounds.y);
        //
        Sys.UI.DomElement.setVisible(this._dropPlaceHolder, true);
        // raise drag started
        this.raiseDragStarted(Sys.EventArgs.Empty);
    },
    _dragFinish: function(sender, args) {
        var el = this.get_element();
        var loc = Sys.UI.DomElement.getLocation(this._dropPlaceHolder);
        Sys.UI.DomElement.setVisible(this._dropPlaceHolder, false);
        this._dropPlaceHolder.parentNode.insertBefore(el, this._dropPlaceHolder);
        Sys.UI.DomElement.setLocation(el, 0, 0);
        el.style.position = this._dropPlaceHolder.style.position;
        el.style.zIndex = this._dropPlaceHolder.style.zIndex;
        // raise drag finished
        this.raiseDragFinished(args);
        // raise drop
        if (this._hoverTarget != null) {
            this.raiseDrop(new Pacem.UI.TargetEventArgs(this._hoverTarget));
        }
        //
        this._dropPlaceHolder.parentNode.removeChild(this._dropPlaceHolder);
    },
    _intersects: function(point, bounds) {
        return point.x >= bounds.x && point.x <= bounds.x + bounds.width
                && point.y >= bounds.y && point.y <= bounds.y + bounds.height;
    },
    _dragging: function(sender, args) {
        var el = this.get_element();
        var targetX = args.get_position().x - this._dragOffset.x;
        var targetY = args.get_position().y - this._dragOffset.y;
        Sys.UI.DomElement.setLocation(el, targetX, targetY);
        //this.get_element().innerHTML = String.format("{0}-{1} = {3} vs {2}", args.get_position().x, this._dragOffset.x, Sys.UI.DomElement.getLocation(this.get_element()).x, targetX);
        // raise dragging
        this.raiseDragging(args);

        if (!this._dropTargets) return;
        var actualPos = args.get_position();
        for (var j = 0; j < this._dropTargets.length; j++) {
            var container = $get(this._dropTargets[j].ContainerID);
            var boundst = Sys.UI.DomElement.getBounds(container);
            // intersects the area?
            if (this._intersects(actualPos, boundst)) {
                if (container != this._hoverTarget) {
                    if (this._hoverTarget != null) {
                        // raise drag leave
                        this.raiseDropTargetLeave(new Pacem.UI.TargetEventArgs(this._hoverTarget));
                    }
                    this._hoverTarget = container;
                    // raise drag enter
                    this.raiseDropTargetEnter(new Pacem.UI.TargetEventArgs(this._hoverTarget));
                }
                // check siblings
                var siblings = Pacem$UI$getDropSiblings(container, el.tagName);

                var targetSibling = null;
                var before = false;
                var j = -1;
                for (var j = 0; j < siblings.length; j++) {
                    if (siblings[j] == el) continue;
                    var current = siblings[j];
                    var bounds = Sys.UI.DomElement.getBounds(current);
                    var intersects = this._intersects(actualPos, bounds);
                    if (intersects) {
                        if (siblings[j] == this._dropPlaceHolder) {
                            while (j < siblings.length) {
                                var nextSibling = siblings[++j];
                                if (nextSibling != el && nextSibling != this._dropPlaceHolder) {
                                    targetSibling = nextSibling;
                                    break;
                                }
                            }
                            break;
                        }
                        var ratio = bounds.width / bounds.height;
                        var deltaV = -actualPos.y + bounds.y + bounds.height * .5;
                        var deltaH = -actualPos.x + bounds.x + bounds.width * .5;
                        var afterV = actualPos.y > bounds.y + bounds.height * .5;
                        var afterH = actualPos.x < bounds.x + bounds.width * .5;
                        var useH = Math.abs(deltaH * ratio) > Math.abs(deltaV);
                        before = useH ? deltaH > 0 : deltaV > 0;
                        if (before) {
                            targetSibling = current;
                        } else {
                            while (j < siblings.length) {
                                var nextSibling = siblings[++j];
                                if (nextSibling != el && nextSibling != this._dropPlaceHolder) {
                                    targetSibling = nextSibling;
                                    break;
                                }
                            }
                        }
                        break;
                    }
                }
                if (targetSibling == null)
                    container.appendChild(this._dropPlaceHolder);
                else
                    container.insertBefore(this._dropPlaceHolder, targetSibling);
                //
                return;
            }
        }
        // still here?
        // then hover to nothing
        if (this._hoverTarget != null) {
            // raise drag leave
            this.raiseDropTargetLeave(new Pacem.UI.TargetEventArgs(this._hoverTarget));
            this._hoverTarget = null;
            var targetSibling = this._originalCoordinates["nextSibling"];
            if (targetSibling == null)
                this._originalCoordinates["parent"].appendChild(this._dropPlaceHolder);
            else
                this._originalCoordinates["parent"].insertBefore(this._dropPlaceHolder, targetSibling);
        }
    },

    get_dragHandle: function() {
        return this._dragHandle;
    },
    set_dragHandle: function(value) {
        this._dragHandle = value;
    },

    get_dropTargets: function() {
        return this._dropTargets;
    },
    set_dropTargets: function(value) {
        this._dropTargets = value;
    },

    get_dropPlaceHolder: function() {
        return this._dropPlaceHolder;
    },
    set_dropPlaceHolder: function(value) {
        this._dropPlaceHolder = value;
    }

}
Pacem.UI.DragDropBehavior.registerClass('Pacem.UI.DragDropBehavior', AjaxControlToolkit.BehaviorBase);


Pacem.UI.TargetEventArgs = function(element) {
    /// <summary>
    /// Event arguments used when a target DOM element gets triggered.
    /// </summary>
    /// <param name="element" DomElement="true" type="Object" mayBeNull="false">
    /// Target element.
    /// </param>
    Pacem.UI.TargetEventArgs.initializeBase(this);
    this._element = element;
}
Pacem.UI.TargetEventArgs.prototype = {
    get_element: function() {
        /// <value type="Object" DomElement="true" mayBeNull="false">
        /// DOM element.
        /// </value>
        return this._element;
    }
}
Pacem.UI.TargetEventArgs.registerClass('Pacem.UI.TargetEventArgs', Sys.EventArgs);

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();