/**
 * GRAPPELLI INLINES
 * jquery-plugin for inlines (stacked and tabular)
 */


(function($) {
    $.fn.grp_inline = function(options) {
        var defaults = {
            prefix: "form",                         // The form prefix for your django formset
            addText: "add another",                 // Text for the add link
            deleteText: "remove",                   // Text for the delete link
            addCssClass: "grp-add-handler",             // CSS class applied to the add link
            removeCssClass: "grp-remove-handler",       // CSS class applied to the remove link
            deleteCssClass: "grp-delete-handler",       // CSS class applied to the delete link
            emptyCssClass: "grp-empty-form",            // CSS class applied to the empty row
            formCssClass: "grp-dynamic-form",           // CSS class applied to each form in a formset
            predeleteCssClass: "grp-predelete",
            onBeforeInit: function(form) {},        // Function called before a form is initialized
            onBeforeAdded: function(inline) {},     // Function called before a form is added
            onBeforeRemoved: function(form) {},     // Function called before a form is removed
            onBeforeDeleted: function(form) {},     // Function called before a form is deleted
            onAfterInit: function(form) {},         // Function called after a form has been initialized
            onAfterAdded: function(form) {},        // Function called after a form has been added
            onAfterRemoved: function(inline) {},    // Function called after a form has been removed
            onAfterDeleted: function(form) {}       // Function called after a form has been deleted
        };
        options = $.extend(defaults, options);

        return this.each(function() {
            var inline = $(this); // the current inline node
            var totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS");
            // set autocomplete to off in order to prevent the browser from keeping the current value after reload
            totalForms.attr("autocomplete", "off");
            // init inline and add-buttons
            initInlineForms(inline, options);
            initAddButtons(inline, options);
            // button handlers
            addButtonHandler(inline.find("a." + options.addCssClass), options);
            removeButtonHandler(inline.find("a." + options.removeCssClass), options);
            deleteButtonHandler(inline.find("a." + options.deleteCssClass), options);
        });
    };

    getFormIndex = function(elem, options, regex) {
        var formIndex = elem.find("[id^='id_" + options.prefix + "']").attr('id');
        if (!formIndex) { return -1; }
        return parseInt(regex.exec(formIndex)[1], 10);
    };

    updateFormIndex = function(elem, options, replace_regex, replace_with) {
        elem.find(':input,span,table,iframe,label,a,ul,p,img,div').each(function() {
            var node = $(this),
                node_id = node.attr('id'),
                node_name = node.attr('name'),
                node_for = node.attr('for'),
                node_href = node.attr("href"),
                node_class = node.attr("class"),
                node_onclick = node.attr("onclick");
            if (node_id) { node.attr('id', node_id.replace(replace_regex, replace_with)); }
            if (node_name) { node.attr('name', node_name.replace(replace_regex, replace_with)); }
            if (node_for) { node.attr('for', node_for.replace(replace_regex, replace_with)); }
            if (node_href) { node.attr('href', node_href.replace(replace_regex, replace_with)); }
            if (node_class) { node.attr('class', node_class.replace(replace_regex, replace_with)); }
            if (node_onclick) { node.attr('onclick', node_onclick.replace(replace_regex, replace_with)); }
        });
        // update prepopulate ids for function initPrepopulatedFields
        elem.find('.prepopulated_field').each(function() {
            var dependency_ids = $(this).data('dependency_ids') || [],
                dependency_ids_updated = [];
            $.each(dependency_ids, function(i, id) {
                dependency_ids_updated.push(id.replace(replace_regex, replace_with));
            });
            $(this).data('dependency_ids', dependency_ids_updated);
        });
    };

    var initPrepopulatedFields = function(elem, options) {
        elem.find('.prepopulated_field').each(function() {
            var dependency_ids = $(this).data('dependency_ids') || [];
            $(this).prepopulate(dependency_ids, $(this).attr('maxlength'));
        });
    };

    initInlineForms = function(elem, options) {
        elem.find("div.grp-module").each(function() {
            var form = $(this);
            // callback
            options.onBeforeInit(form);
            // add options.formCssClass to all forms in the inline
            // except table/theader/add-item
            if (form.attr('id') !== "") {
                form.not("." + options.emptyCssClass).not(".grp-table").not(".grp-thead").not(".add-item").addClass(options.formCssClass);
            }
            // add options.predeleteCssClass to forms with the delete checkbox checked
            form.find("li.grp-delete-handler-container input").each(function() {
                if ($(this).is(":checked") && form.hasClass("has_original")) {
                    form.toggleClass(options.predeleteCssClass);
                }
            });
            // callback
            options.onAfterInit(form);
        });
    };

    initAddButtons = function(elem, options) {
        var totalForms = elem.find("#id_" + options.prefix + "-TOTAL_FORMS");
        var maxForms = elem.find("#id_" + options.prefix + "-MAX_NUM_FORMS");
        var addButtons = elem.find("a." + options.addCssClass);
        // hide add button in case we've hit the max, except we want to add infinitely
        if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
            hideAddButtons(elem, options);
        }
    };

    addButtonHandler = function(elem, options) {
        elem.on("click", function() {
            var inline = elem.parents(".grp-group"),
                totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS"),
                maxForms = inline.find("#id_" + options.prefix + "-MAX_NUM_FORMS"),
                addButtons = inline.find("a." + options.addCssClass),
                empty_template = inline.find("#" + options.prefix + "-empty");
            // callback
            options.onBeforeAdded(inline);
            // create new form
            var index = parseInt(totalForms.val(), 10),
                form = empty_template.clone(true);
            form.removeClass(options.emptyCssClass)
                .attr("id", empty_template.attr('id').replace("-empty", index));
            // update form index
            var re = /__prefix__/g;
            updateFormIndex(form, options, re, index);
            // after "__prefix__" strings has been substituted with the number
            // of the inline, we can add the form to DOM, not earlier.
            // This way we can support handlers that track live element
            // adding/removing, like those used in django-autocomplete-light
            form.insertBefore(empty_template)
                .addClass(options.formCssClass);
            // update total forms
            totalForms.val(index + 1);
            // hide add button in case we've hit the max, except we want to add infinitely
            if ((maxForms.val() !== 0) && (maxForms.val() !== "") && (maxForms.val() - totalForms.val()) <= 0) {
                hideAddButtons(inline, options);
            }
            // prepopulate fields
            initPrepopulatedFields(form, options);
            // callback
            options.onAfterAdded(form);
        });
    };

    removeButtonHandler = function(elem, options) {
        elem.on("click", function() {
            var inline = elem.parents(".grp-group"),
                form = $(this).parents("." + options.formCssClass).first(),
                totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS"),
                maxForms = inline.find("#id_" + options.prefix + "-MAX_NUM_FORMS"),
                re = /-(\d+)-/,
                removedFormIndex = getFormIndex(form, options, re);
            // callback
            options.onBeforeRemoved(form);
            // remove form
            form.remove();
            // update total forms
            totalForms.val(parseInt(totalForms.val(), 10) - 1);
            // show add button in case we've dropped below max
            if ((maxForms.val() !== 0) && (maxForms.val() - totalForms.val()) > 0) {
                showAddButtons(inline, options);
            }
            // update form index (only forms with a higher index than the removed form)
            inline.find("." + options.formCssClass).each(function() {
                var form = $(this),
                    formIndex = getFormIndex(form, options, re);
                if (formIndex > removedFormIndex) {
                    updateFormIndex(form, options, re, "-" + (formIndex - 1) + "-");
                }
            });
            // callback
            options.onAfterRemoved(inline);
        });
    };

    deleteButtonHandler = function(elem, options) {
        elem.on("click", function() {
            var deleteInput = $(this).prev(),
                form = $(this).parents("." + options.formCssClass).first();
            // callback
            options.onBeforeDeleted(form);
            // toggle options.predeleteCssClass and toggle checkbox
            if (form.hasClass("has_original")) {
                form.toggleClass(options.predeleteCssClass);
                if (deleteInput.prop("checked")) {
                    deleteInput.removeAttr("checked");
                } else {
                    deleteInput.prop("checked", true);
                }
            }
            // callback
            options.onAfterDeleted(form);
        });
    };

    hideAddButtons = function(elem, options) {
        var addButtons = elem.find("a." + options.addCssClass);
        addButtons.hide().parents('.grp-add-item').hide();
        // last row with stacked/tabular
        addButtons.closest('.grp-module.grp-transparent').hide();
    };

    showAddButtons = function(elem, options) {
        var addButtons = elem.find("a." + options.addCssClass);
        addButtons.show().parents('.grp-add-item').show();
        // last row with stacked/tabular
        addButtons.closest('.grp-module.grp-transparent').show();
    };

})(grp.jQuery);
