/**
 * Django admin inlines
 *
 * Based on jQuery Formset 1.1
 * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
 * @requires jQuery 1.2.6 or later
 *
 * Copyright (c) 2010, vonautomatisch
 *
 * Licensed under the New BSD License
 * See: http://www.opensource.org/licenses/bsd-license.php
 */
 
//  tabular:
//      <div id="name_set-group" class="group tabular ...">
//          <h2>
//          <ul class="tools">
//          – management_form
//          <div class="module table">
//              <div class="module thead">
//              <div id="name_set1" class="module tbody has_original">
//                  <div class="table-row dynamic-name_set">
//              ...
//              <div id="name_set-empty" class="module tbody empty-form">
//                  <div class="table-row dynamic-name_set">
//          <div class="module add-handler">

//  stacked:
//      <div id="name_set-group" class="group ...">
//          <h2>
//          <ul class="tools">
//          – management_form
//          <div class="items">
//              <div id="name_set1" class="module has_original">
//                  <div class="table-row dynamic-name_set">
//              ...
//              <div id="name_set-empty" class="module empty-form">
//                  <div class="table-row dynamic-name_set">
//          <div class="module add-handler">

/*
$(rows).inline({
            prefix: "bookmarkitem_set",
            deleteCssClass: "delete-handler",
            emptyCssClass: "empty-form",
            onAdded: (function(row) {
                reinitDateTimeShortCuts();
                updateSelectFilter();
            })
        });

*/

(function($) {

$.fn.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: "add-handler",         // CSS class applied to the add link
        deleteCssClass: "delete-handler",   // CSS class applied to the delete link
        removeCssClass: "remove-handler",   // CSS class applied to the remove link
        emptyCssClass: "empty-form",         // CSS class applied to the empty row
        formCssClass: "dynamic-form",       // CSS class applied to each form in a formset
        predeleteCssClass: "predelete",
        onAdded: null,                        // Function called each time a new form is added
        onRemoved: null                       // Function called each time a form is deleted
    };
    
    options = $.extend(defaults, options);
    
    return this.each(function() {
        
        var inline = $(this), // the current inline node
            totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS"), // current forms total
            maxForms = inline.find("#id_" + options.prefix + "-MAX_NUM_FORMS"), // max forms in this inline (0 if no limit)
            addButtons = inline.find("a." + options.addCssClass),
            template = inline.find("#" + options.prefix + "-empty"), // the hidden node we copy to create an additional form
            template_id = template.attr('id'),
            template_ready = false,
            /*
            updateElementIndex = function(el, prefix, ndx) {
                var id_regex = new RegExp("(" + prefix + "-\\d+)");
                var replacement = prefix + "-" + ndx;
                if ($(el).attr("for")) {
                    $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
                }
                if (el.id) {
                    el.id = el.id.replace(id_regex, replacement);
                }
                if (el.name) {
                    el.name = el.name.replace(id_regex, replacement);
                }
            },
            */
            initAddButtons = function() {
                if (maxForms.val() == 0 || (maxForms.val() - totalForms.val()) > 0) {
                    addButtons.click(addButtonHandler);
                } else {
                    // hide add-buttons
                    hideAddBottons();
                }
            },
            
            initFormIndex = function(form, nextIndex) {
                var re = /__prefix__/g;
                form.find(':input,span,table,iframe,label,a,ul,p,img').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");
                    
                    if (node_id) node.attr('id', node_id.replace(re, nextIndex));
                    if (node_name) node.attr('name', node_name.replace(re, nextIndex));
                    if (node_for) node.attr('for', node_for.replace(re, nextIndex));
                    if (node_href) node.attr('href', node_href.replace(re, nextIndex));
                });
            },
            
            updateFormIndex = function(form, idx) {
                // need to trigger onRemove and 
                // onAdded (on bottom of function) to reinint rows
                if (options.onRemoved) {
                    options.onRemoved(form);
                }
                var re = /-\d+-/g;
                form.attr('id', options.prefix + idx);
                form.find(':input,span,table,iframe,label,a,ul,p,img').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");
                    
                    if (node_id) node.attr('id', node_id.replace(re, "-" + idx + "-"));
                    if (node_name) node.attr('name', node_name.replace(re, "-" + idx + "-"));
                    if (node_for) node.attr('for', node_for.replace(re, "-" + idx + "-"));
                    if (node_href) node.attr('href', node_href.replace(re, "-" + idx + "-"));
                });
                if (options.onAdded) {
                    options.onAdded(form);
                }
            },
            
            addButtonHandler = function() {
                // FIXME wrong place to do this
                // choices:
                // 1) create a new event (beforAdded) and try to do this form outside :(
                // 2) add the "editor_deselector" class to the templates textarea
                // ...
                if (!options.template_ready) {
                    if (typeof tinyMCE !== "undefined") {
                        template.find('textarea').each(function(e) {
                            if (tinyMCE.getInstanceById(this.id)) {
                                tinyMCE.execCommand('mceRemoveControl', false, this.id);
                            }
                        });
                    }
                    options.template_ready = true;
                }
                
                var nextIndex = parseInt(totalForms.val(), 10);
                
                // create new from (add it as last)
                var form = template.clone(true);
                
                form.removeClass(options.emptyCssClass)
                    .attr("id", template_id.replace("-empty", nextIndex))
                    .insertBefore(template)
                    .addClass(options.formCssClass);
                    
                initFormIndex(form, nextIndex);
                
                totalForms.val(nextIndex + 1);
                
                // Hide add button in case we've hit the max, except we want to add infinitely
                if ((maxForms.val() != 0) && (maxForms.val() - totalForms.val()) <= 0) {
                    // hide stuff
                    hideAddBottons();
                }
                
                // If a post-add callback was supplied, call it with the added form
                if (options.onAdded) {
                    options.onAdded(form);
                }
                return false;
            },
            
            hideAddBottons = function() {
                addButtons.hide().parents('div.add-item').hide();
            },
            
            showAddButtons = function() {
                addButtons.show().parents('div.add-item').show();
            },
            
            deleteHandler = function() {
                var deleteInput = $(this).prev(),
                    form = deleteInput.parents("." + options.formCssClass).first();
                if (form.hasClass("has_original")) { // toggle delete checkbox and delete css class
                    form.toggleClass(options.predeleteCssClass);
                    if (deleteInput.attr("checked")) {
                        deleteInput.removeAttr("checked");
                    } else {
                        deleteInput.attr("checked", 'checked');
                    }
                }
                return false;
            },
            
            removeHandler = function() {
                var deleteInput = $(this).prev(),
                    form = deleteInput.parents("." + options.formCssClass).first();
                // last one stays
                // else if (totalForms.val() == 1) 
                //    alert("letztes bleibt da!");
                //    return false;
                // remove form
                // Remove the parent form containing this button:
                form.remove();
                // If a post-delete callback was provided, call it with the deleted form:
                if (options.onRemoved) {
                    options.onRemoved(form);
                }
                // Update the TOTAL_FORMS form count.
                var forms = inline.find("." + options.formCssClass);
                totalForms.val(parseInt(totalForms.val(), 10) - 1);
                
                // Show add button again once we drop below max
                if ((maxForms.val() == 0) || (maxForms.val() >= forms.length)) {
                    showAddButtons();
                }
                // Also, update names and ids for all remaining form controls
                // so they remain in sequence:
                var startReplaceAt = form.attr("id");
                startReplaceAt = parseInt(startReplaceAt.replace(options.prefix, ""), 10);
                for (var i = startReplaceAt; i < forms.length; i++) {
                    updateFormIndex($(forms[i]), i);
                }
                return false;
            },
            
            initInlineForms = function() {
                var hasErrors = false;
                //inline.find("div.items div.module").each(function() {
                inline.find("div.module").each(function() {
                    var form = $(this);
                    // add the options.formCssClass to all forms in the inline
                    if (form.attr('id') !== "") {
                        form.not("." + options.emptyCssClass).addClass(options.formCssClass);
                    }
                    // open the form if it has errors
                    if (form.find("ul.errorlist").length > 0) {
                        form.removeClass('closed').addClass('open');
                        // to open the inline
                        hasErrors = true;
                    }
                });
                
                // open the inline if it has forms with errors in it
                if (hasErrors) {
                    inline.removeClass('closed').addClass('open');
                }
            };
        
        // set this to prevent the browser from keeping the current value after reload
        totalForms.attr("autocomplete", "off");
        
        initInlineForms();
        
        initAddButtons();
        
        // delete button
        // toggle the delete-checkbox and add the predelete-class to the row
        inline.find("a." + options.deleteCssClass).click(deleteHandler);
        inline.find("a." + options.removeCssClass).click(removeHandler);
        
        // add options.predeleteCssClass to forms with the delete checkbox checked
        inline.find("li.delete-handler-container input").each(function() {
            var deleteInput = $(this);
            if (deleteInput.attr("checked")) {
                var form = $(deleteInput.parents("." + options.formCssClass).first());
                if (form.hasClass("has_original")) {
                    form.toggleClass(options.predeleteCssClass);
                }
            }
        });
    });
};
})(jQuery);
