/** * jQuery Formset 1.5-pre * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) * @requires jQuery 1.2.6 or later * * Copyright (c) 2009, Stanislaus Madueke * All rights reserved. * * Licensed under the New BSD License * See: http://www.opensource.org/licenses/bsd-license.php */ ;(function($) { $.fn.formset = function(opts) { var options = $.extend({}, $.fn.formset.defaults, opts), flatExtraClasses = options.extraClasses.join(' '), totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'), maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'), minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'), childElementSelector = 'input,select,textarea,label,div', $$ = $(this), applyExtraClasses = function(row, ndx) { if (options.extraClasses) { row.removeClass(flatExtraClasses); row.addClass(options.extraClasses[ndx % options.extraClasses.length]); } }, updateElementIndex = function(elem, prefix, ndx) { var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'), replacement = prefix + '-' + ndx + '-'; if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement)); if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement)); if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement)); }, hasChildElements = function(row) { return row.find(childElementSelector).length > 0; }, showAddButton = function() { return maxForms.length == 0 || // For Django versions pre 1.2 (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0)); }, /** * Indicates whether delete link(s) can be displayed - when total forms > min forms */ showDeleteLinks = function() { return minForms.length == 0 || // For Django versions pre 1.7 (minForms.val() == '' || (totalForms.val() - minForms.val() > 0)); }, insertDeleteLink = function(row) { var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'), addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.'); var delButtonHTML = '' + options.deleteText +''; if (options.deleteContainerClass) { // If we have a specific container for the remove button, // place it as the last child of that container: row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML); } else if (row.is('TR')) { // If the forms are laid out in table rows, insert // the remove button into the last table cell: row.children('td:last').append(delButtonHTML); } else if (row.is('UL') || row.is('OL')) { // If they're laid out as an ordered/unordered list, // insert an
  • after the last list item: row.append('
  • ' + delButtonHTML + '
  • '); } else { // Otherwise, just insert the remove button as the // last child element of the form's container: row.append(delButtonHTML); } // Check if we're under the minimum number of forms - not to display delete link at rendering if (!showDeleteLinks()){ row.find('a.' + delCssSelector).hide(); } row.find('a.' + delCssSelector).click(function() { var row = $(this).parents('.' + options.formCssClass), del = row.find('input:hidden[id $= "-DELETE"]'), buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'), forms; if (del.length) { // We're dealing with an inline formset. // Rather than remove this form from the DOM, we'll mark it as deleted // and hide it, then let Django handle the deleting: del.val('on'); row.hide(); forms = $('.' + options.formCssClass).not(':hidden'); } else { row.remove(); // Update the TOTAL_FORMS count: forms = $('.' + options.formCssClass).not('.formset-custom-template'); totalForms.val(forms.length); } for (var i=0, formCount=forms.length; i