// Settings variables
var DisableX          = true;              // If X-axis movement should be disabled when dragging
var YMin              = 130;               // Minimum Y value allowed, default
var YMax              = 50000;             // Maximum Y value allowed, default
var S_OriginalDragged = "OriginalDragged"; // The style of the original element when dragging
var S_HelperInvisible = "HelperInvisible"; // The style of the helper object when invisible
var S_HelperLayerId   = "DAD_HelperLayer"; // The id of the helper object

//// Nothing below this line should need edits  ---------

// Initialize the variables
var DragContainers  = [];    // The array holder of the containers holding draggable elements
var ActiveContHolder = null;  // Holds the active container hovered over
var PreviousContainer = null;  // Holds the container the element was in
var CurrentTarget   = null;  // The element being dragged
var PreviousTarget  = null;  // Holds the last element dragged
var HelperLayer     = null;  // Holds the copy of the dragged element and moves with mouse
var TargetParent    = null;  // The parent node of the element being dragged
var TargetSibling   = null;  // The next sibling of the element being dragged
var MouseDown       = false; // The state of the mouse down
var MouseState      = false; // The state of the mouse event
var NextNode        = null;  // Holds the (non-whitespace) node after the element being dragged
var DAD_Url         = "interface/default/SPT--DragAndDrop.php"; // Url to send AJAX request
var DAD_Function    = DAD_Function || null;  // User-defined function run on mouseup after element is dragged
var DAD_AJAXFunction = DAD_AJAXFunction || null; // User-defined AJAX function run on mouseup
var CustomDragContainers = null; // User-defined drag containers

function CreateDragContainer(Containers)
{
    // Add an empty array to the DragContainers array
    DragContainers[DragContainers.length] = [];
    var LastPicked = false;

    // Loop through each of the containers
    for (var i = 0; i < Containers.length; i++)
    {
        // Push the current container onto the new array in DragContainers
        DragContainers[DragContainers.length-1].push(Containers[i]);

        // Loop through the child nodes of the current container
        for (var j = 0; j < Containers[i].childNodes.length; j++)
        {
            // Skip white space nodes
            if (Containers[i].childNodes[j].nodeType !== 1 ||
            Trim(Containers[i].childNodes[j].innerHTML) === ""){continue;}

            // Set the drag property
            Containers[i].childNodes[j].setAttribute("DragGroup",
                DragContainers.length-1);
        }
    }
}

// Determines if an element or node is only whitespace
function ElementIsWhitespace(Element)
{
    if (typeof(Element) === undefined || Element.nodeType !== 1 || Trim(Element.innerHTML) === "")
    {
        return true;
    }
    return false;
}

$(document).ready(function(){
    // Append the actions to the various mouse movements
    document.onmousemove = OnMouseMove;
    document.onmousedown = OnMouseDown;
    document.onmouseup = OnMouseUp;

    window.onload = function()
    {
        // Get the containers of dragable elements
        if (CustomDragContainers) //** custom drag containers set
        {
            // Loop through each custom drag container
            for (var i = 0; i < CustomDragContainers.length; i++)
            {
                // Create the drag container
                CreateDragContainer(GetElementsByClass(CustomDragContainers[i]));
            }
        }
        else
        {
            CreateDragContainer(GetElementsByClass("DragContainer"));
        }

        // Create the helper object that will show the item while dragging
        HelperLayer = document.createElement("div");
        HelperLayer.id = S_HelperLayerId;
        HelperLayer.className = S_HelperInvisible;
        HelperLayer.style.cursor = "move";

        // Append the helper object to the body
        document.body.appendChild(HelperLayer);
    };
});

function OnMouseDown(Event)
{
    // Get the proper event and target of the even
    Event = Event || window.event;
    var Target = Event.target || Event.srcElement;

    // Set mousedown status to true
    MouseDown = true;

    // Determine if Target's mousedown function or drag group is set
    if (Target.onmousedown || Target.getAttribute("DragGroup")) //** above conditions true
    {
        return false;
    }
}

function OnMouseUp(Event)
{
    // Determine if CurrentTarget is set (working with draggable element)
    if (CurrentTarget) //** CurrentTarget set
    {
        // Enable text highlighting (fix IE bug)
        EnableTextHighlighting();

        // Remove HTML from HelperLayer and hide it
        HelperLayer.innerHTML = "";
        HelperLayer.style.display = "none";

        // Reset the class of CurrentTarget and remove the OrigClass attribute
        CurrentTarget.className = CurrentTarget.getAttribute("OrigClass");
        CurrentTarget.removeAttribute("OrigClass");

        // Get the resource id of the CurrentTarget
        var SourceId = CurrentTarget.getAttribute("ResourceId");

        // Initialize TargetId
        var TargetId = null;

        // Determine if NextNode is not null
        if (NextNode !== null) //** NextNode is not null
        {
            // Get the resource id of NextNode
            TargetId = NextNode.getAttribute("ResourceId");
        }
        else
        {
            TargetId = "LAST_ITEM";
        }

        // Initialize Type to null
        var Type = null;

        // Determine if a type is set in the CurrentTarget's attributes
        if (CurrentTarget.getAttribute("DragType")) //** type set
        {
            Type = CurrentTarget.getAttribute("DragType");
        }

        // Determine if a user-defined AJAX function is set
        if (DAD_AJAXFunction !== null) //** AJAX function set
        {
            DAD_AJAXFunction(SourceId, TargetId);
        }
        else //** AJAX function not set
        {
            // Send the AJAX request
            var Parameters = {Type:Type, TargetId:TargetId, SourceId:SourceId};
            SendRequest(DAD_Url, Parameters,
                        function(Response){
                            if (Response !== "OK")
                            {
                                alert("There was an error setting the order." +
                                      " Please try again later.");
                            }
                        });
        }
    }

    // Reset the variables
    CurrentTarget = null;
    MouseDown = false;
}

function OnMouseMove(Event)
{
    // Get the event and target
    Event = Event || window.event;
    var Target = Event.target || Event.srcElement;

    // Get the current position of the mouse
    var MousePosition = GetMousePosition(Event);

    // Determine if the drag group is set (dragable object)
    if (Target.getAttribute("DragGroup") !== null) //** moved over draggable element
    {
        // If the user is starting to drag the element
        if (MouseDown && !MouseState)
        {
            // Set the target into CurrentTarget
            CurrentTarget = Target;

            // Save the original container
            OriginalContainer = CurrentTarget.parentNode;

            // Get CurrentTarget's parent and first sibling
            TargetParent = CurrentTarget.parentNode;
            TargetSibling = GetNextSibling(CurrentTarget);

            // Get the size and position of the element, and the mouse offset
            var Size = GetElementSize(CurrentTarget);
            var Position = GetElementPosition(CurrentTarget);
            MouseOffset = GetMouseOffset(CurrentTarget, Event);
            OriginalWidth = Size.Width;
            OriginalLeft  = Position.Left;

            // Remove anything in the drag helper
            HelperLayer.innerHTML = "";

            // Make a copy of CurrentTarget and put it in HelperLayer
            HelperLayer.appendChild(CurrentTarget.cloneNode(true));

            // Resize helper layer
            HelperLayer.style.width = Size.Width + "px";

            // Reposition the helper layer
            HelperLayer.style.top  = (MousePosition.Y - MouseOffset.Y) + "px";

            // Determine if the X-Axis is disabled
            if (DisableX === true) //** is disabled
            {
                // Set left position of drag layer to left position of element being dragged
                HelperLayer.style.left = OriginalLeft + "px";
            }
            else //** not disabled
            {
                // Set left position of drag layer to mouse position minus mouse offset
                HelperLayer.style.left = (MousePosition.X - MouseOffset.X) + "px";
            }

            // Disable dragging from the helper layer
            HelperLayer.firstChild.removeAttribute("DragGroup");

            // Display the helper layer
            HelperLayer.style.display = "block";

            // Fixes position issue if text align isn't set
            if (!HelperLayer.style.textAlign)
            {
                HelperLayer.style.textAlign = "left";
            }

            // Record the size of the element being dragged and apply the dragging class
            CurrentTarget.setAttribute("StartWidth", parseInt(CurrentTarget.offsetWidth));
            CurrentTarget.setAttribute("StartHeight", parseInt(CurrentTarget.offsetHeight));
            CurrentTarget.setAttribute("OrigClass", CurrentTarget.className);
            CurrentTarget.className = S_OriginalDragged;

            // Get the array named for the drag group in the containers array
            var ContainerContents = DragContainers[Target.getAttribute("DragGroup")];

            // Loop through each possible drop container in the drag array
            for (var i = 0; i < ContainerContents.length; i++)
            {
                with (ContainerContents[i])
                {
                    // Get the position of the container
                    var Position = GetElementPosition(ContainerContents[i]);

                    // Get the size and save it and the poistion to the container (!important!)
                    setAttribute("StartWidth", parseInt(offsetWidth));
                    setAttribute("StartHeight", parseInt(offsetHeight));
                    setAttribute("StartLeft", Position.Left);
                    setAttribute("StartTop", Position.Top);
                }

                // Loop through each child element of each container
                for (var j = 0; j < ContainerContents[i].childNodes.length; j++)
                {
                    with(ContainerContents[i].childNodes[j])
                    {
                        // Determine if the child node is not whitespace
                        if (ElementIsWhitespace(ContainerContents[i].childNodes[j]) === false) //** not whitespace
                        {
                            // Ge the position of the current child node
                            var Position = GetElementPosition(ContainerContents[i].childNodes[j]);

                            // Get the size save it and the position to the node (!important!)
                            setAttribute("StartWidth", parseInt(offsetWidth));
                            setAttribute("StartHeight", parseInt(offsetHeight));
                            setAttribute("StartLeft", Position.Left);
                            setAttribute("StartTop", Position.Top);
                        }
                    }
                }
            }
        }
    }

    // If something is being dragged
    if (CurrentTarget)
    {
        // Disable text highlighting (IE bug)
        DisableTextHighlighting();

        // Determine if the mouse position minus offset is <= the min Y value
        if (MousePosition.Y - MouseOffset.Y <= YMin) //** is <= min Y value
        {
            // Set top position of helper layer to min Y value
            HelperLayer.style.top = YMin + "px";
        }
        else if (MousePosition.Y - MouseOffset.Y >= YMax) //** else if position minus offset is >= max Y value
        {
            // Set top position of helper layer to max Y value
            HelperLayer.style.top = YMax + "px";
        }
        else //** within Y value range
        {
            // Set top position of helper layer to mouse position minus offset
            HelperLayer.style.top = (MousePosition.Y - MouseOffset.Y) + "px";
        }

        // Store the previous container and initialize ActiveContainer to null
        PreviousContainer = CurrentTarget.parentNode;
        ActiveContainer = null;

        // Get the center of the element being dragged
        var XPosition = MousePosition.X - MouseOffset.X + (parseInt(CurrentTarget.getAttribute("StartWidth")) / 2);
        var YPosition = MousePosition.Y - MouseOffset.Y + (parseInt(CurrentTarget.getAttribute("StartHeight")) / 2);

        // Get the array named for the drag group in the containers array
        var ContainerContents = DragContainers[CurrentTarget.getAttribute("DragGroup")];

        // Check each drop container to determine if the target object is hovering over the container
        for (var i = 0; i < ContainerContents.length; i++)
        {
            with (ContainerContents[i])
            {
                // If the target is in the container save it into the ActiveContent variable
                if ((parseInt(getAttribute("StartLeft")) < XPosition) && (parseInt(getAttribute("StartTop")) < YPosition) &&
                   ((parseInt(getAttribute("StartLeft")) + parseInt(getAttribute("StartWidth"))) > XPosition) &&
                   ((parseInt(getAttribute("StartTop")) + parseInt(getAttribute("StartHeight")) + 15) > YPosition))
                {
                    ActiveContainer = ContainerContents[i];
                    ActiveContHolder = ContainerContents[i];
                    break;
                }
            }
        }

        // If the target is in a container
        if (ActiveContainer)
        {
            // Initialize NextNode to null
            NextNode = null;

            // Loop through each child node of the current container
            for (var i = ActiveContainer.childNodes.length - 1; i >= 0; i--)
            {
                with (ActiveContainer.childNodes[i])
                {
                    // Determine if the child node is not whitespace
                    if (nodeType == 1) //** not whitespace
                    {
                        // Determine if the current child node is not equal to
                        // CurrentTarget or it's next sibling and if the upper right and
                        // lower left corners of the current child node are further right
                        // and further down, respectively
                        if (CurrentTarget != ActiveContainer.childNodes[i] && ((parseInt(getAttribute("StartLeft")) +
                           parseInt(getAttribute("StartWidth"))) > XPosition) && ((parseInt(getAttribute("StartTop")) +
                           parseInt(getAttribute("StartHeight"))) > YPosition))
                        {
                            // Set NextNode equal to the next child node
                            NextNode = ActiveContainer.childNodes[i];
                        }
                    }
                }
            }

            // Determine if NextNode is not null
            if (NextNode) //** not null
            {
                if (NextNode != CurrentTarget.nextSibling)
                {
                    ActiveContainer.insertBefore(CurrentTarget, NextNode);
                }
            }
            else //** is null
            {
                if (CurrentTarget.nextSibling || CurrentTarget.parentNode !== ActiveContainer)
                {
                    ActiveContainer.appendChild(CurrentTarget);
                }
            }

            // Set the new position attributes of ActiveContainer
            var ContainerPos = GetElementPosition(ActiveContainer);
            ActiveContainer.setAttribute("StartWidth", parseInt(ActiveContainer.offsetWidth));
            ActiveContainer.setAttribute("StartHeight", parseInt(ActiveContainer.offsetHeight));
            ActiveContainer.setAttribute("StartLeft", ContainerPos.Left);
            ActiveContainer.setAttribute("StartTop", ContainerPos.Top);

            // Determine if a user-defined function is set
            if (DAD_Function !== null) //** function set
            {
                DAD_Function();
            }
        }
    }

    // Set the current mouse state
    MouseState = MouseDown;
    // Set the mousemove target
    PreviousTarget = Target;
}

