/*
    Drag and drop reordering of folder contents.

    Provides global ploneDnDReorder
*/


/*jslint nomen:false */

var ploneDnDReorder = {};

ploneDnDReorder.dragging = null;
ploneDnDReorder.table = null;
ploneDnDReorder.rows = null;
ploneDnDReorder.locked = false;

(function ($) {

    ploneDnDReorder.doDown = function (e) {
        var dragging = ploneDnDReorder.dragging,
            body;
        // Waiting for a server operation to complete or following an error
        if (ploneDnDReorder.locked) {
            return;
        }
        // already dragging, probably catching up a lost drag.
        if (dragging) {
            if ($(this).attr('id') !== dragging.attr('id')) {
                ploneDnDReorder.locked = true;
                dragging.removeClass('dragging').addClass('error');
            }
            return;
        }
        dragging = $(this).parents('.draggable:first');
        if (!dragging.length) {
            return;
        }
        ploneDnDReorder.rows.mousemove(ploneDnDReorder.doDrag);
        body = $('body');
        body.mouseup(ploneDnDReorder.doUp);
        body.mouseleave(ploneDnDReorder.doCancel);

        ploneDnDReorder.dragging = dragging;
        dragging.data('ploneDnDReorder.startPosition', ploneDnDReorder.getPos(dragging));
        dragging.addClass("dragging");
        $(this).parents('tr').addClass('dragindicator');
        // Find the original subset ids. This must be in the current order.
        dragging.data('ploneDnDReorder.subset_ids', $.map(
            ploneDnDReorder.table.find('tr.draggable'),
            function (elem) {
                return $(elem).attr('id').substr('folder-contents-item-'.length);
            }));

        return false;
    };

    ploneDnDReorder.getPos = function (node) {
        var pos = node.parent().children('.draggable').index(node[0]);
        return pos === -1 ? null : pos;
    };

    ploneDnDReorder.doDrag = function (e) {
        var dragging = ploneDnDReorder.dragging,
            target = this;

        if (!dragging) {
            return;
        }
        if (!target) {
            return;
        }

        if ($(target).attr('id') !== dragging.attr('id')) {
            ploneDnDReorder.swapElements($(target), dragging);
        }
        return false;
    };

    ploneDnDReorder.swapElements = function (child1, child2) {
        var parent = child1.parent(),
            items = parent.children('[id]'),
            t;

        // Only adjacent elements may be swapped.
        if (Math.abs(ploneDnDReorder.getPos(child1) - ploneDnDReorder.getPos(child2)) !== 1) {
            return;
        }

        items.removeClass('even').removeClass('odd');
        if (child1[0].swapNode) {
            // IE proprietary method
            child1[0].swapNode(child2[0]);
        } else {
            // swap the two elements, using a textnode as a position marker
            t = parent[0].insertBefore(document.createTextNode(''),
                child1[0]);
            child1.insertBefore(child2);
            child2.insertBefore(t);
            $(t).remove();
        }
        // odd and even are 0-based, so we want them the other way around
        parent.children('[id]:odd').addClass('even');
        parent.children('[id]:even').addClass('odd');
    };

    ploneDnDReorder.doUp = function (e) {
        var dragging = ploneDnDReorder.dragging,
            body = $('body');
        if (!dragging) {
            return;
        }

        ploneDnDReorder.updatePositionOnServer();
        dragging.removeData('ploneDnDReorder.startPosition');
        dragging.removeData('ploneDnDReorder.subset_ids');
        ploneDnDReorder.rows.unbind('mousemove', ploneDnDReorder.doDrag);
        body.unbind('mouseup', ploneDnDReorder.doUp);
        body.unbind('mouseleave', ploneDnDReorder.doCancel);

        $(this).parents('tr').removeClass('dragindicator');
        return false;
    };

    ploneDnDReorder.doCancel = function (e) {
        var dragging = ploneDnDReorder.dragging,
            body = $('body');
        if (!dragging) {
            return;
        }

        dragging.removeClass("dragging");
        // Need to remove the indicator as well, important in the
        // case when the mouse up occurs outside the grid.
        dragging.removeClass('dragindicator');

        if (ploneDnDReorder.getPos(dragging) - dragging.data('ploneDnDReorder.startPosition')) {
            // position has changed, error out
            ploneDnDReorder.locked = true;
            dragging.addClass("error");
        }
        ploneDnDReorder.rows.unbind('mousemove', ploneDnDReorder.doDrag);
        body.unbind('mouseup', ploneDnDReorder.doCancel);
        body.unbind('mouseleave', ploneDnDReorder.doCancel);
        ploneDnDReorder.dragging = null;

        return false;
    };

    ploneDnDReorder.updatePositionOnServer = function () {
        var dragging = ploneDnDReorder.dragging,
            delta,
            args,
            encoded;

        if (!dragging) {
            return;
        }

        delta = ploneDnDReorder.getPos(dragging) - dragging.data('ploneDnDReorder.startPosition');

        if (delta === 0) {
            // nothing changed
            // Do not just leave: need to cancel.
            // ... or, up to big surprise at next click.
            ploneDnDReorder.doCancel.call();
            return;
        }
        // Strip off id prefix
        args = {
            item_id: dragging.attr('id').substr('folder-contents-item-'.length),
            subset_ids: dragging.data('ploneDnDReorder.subset_ids')
        };
        args['delta:int'] = delta;
        // Convert jQuery's name[]=1&name[]=2 to Zope's name:list=1&name:list=2
        encoded = $.param(args).replace(/%5B%5D=/g, '%3Alist=');
        $.ajax({
            type: 'POST',
            url: 'folder_moveitem',
            data: encoded,
            complete: ploneDnDReorder.complete
        });
        ploneDnDReorder.locked = true;
    };

    ploneDnDReorder.complete = function (xhr, textStatus) {
        var dragging = ploneDnDReorder.dragging;
        dragging.removeClass("dragging");
        // Need to remove the indicator as well, important in the
        // case when the mouse up occurs outside the grid.
        dragging.removeClass('dragindicator');
        if (textStatus === "success" || textStatus === "notmodified") {
            ploneDnDReorder.locked = false;
        } else {
            dragging.addClass("error");
        }
        ploneDnDReorder.dragging = null;
    };

}(jQuery));


function initializeDnDReorder(table_selector) {
    var table = table_selector;
    ploneDnDReorder.table = jQuery(table);
    if (!ploneDnDReorder.table.length)
        return;
    ploneDnDReorder.rows = jQuery(table + " > tr," +
        table + " > tbody > tr");
    jQuery(table + " > tr > td.draggable," +
        table + " > tbody > tr > td.draggable")
        .not('.notDraggable')
        .mousedown(ploneDnDReorder.doDown)
        .mouseup(ploneDnDReorder.doUp)
        .addClass("draggingHook")
        .css("cursor", "ns-resize")
        .html('&#x28ff;');
}

$(document).ready(function () {
    initializeDnDReorder('#listing-table');
});
