/django/contrib/admin/media/js/inlines.js

https://code.google.com/p/mango-py/ · JavaScript · 136 lines · 97 code · 0 blank · 39 comment · 24 complexity · cf876c9232ad1ea579bb021c8d8a9b36 MD5 · raw file

  1. /**
  2. * Django admin inlines
  3. *
  4. * Based on jQuery Formset 1.1
  5. * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
  6. * @requires jQuery 1.2.6 or later
  7. *
  8. * Copyright (c) 2009, Stanislaus Madueke
  9. * All rights reserved.
  10. *
  11. * Spiced up with Code from Zain Memon's GSoC project 2009
  12. * and modified for Django by Jannis Leidel
  13. *
  14. * Licensed under the New BSD License
  15. * See: http://www.opensource.org/licenses/bsd-license.php
  16. */
  17. (function($) {
  18. $.fn.formset = function(opts) {
  19. var options = $.extend({}, $.fn.formset.defaults, opts);
  20. var updateElementIndex = function(el, prefix, ndx) {
  21. var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
  22. var replacement = prefix + "-" + ndx;
  23. if ($(el).attr("for")) {
  24. $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
  25. }
  26. if (el.id) {
  27. el.id = el.id.replace(id_regex, replacement);
  28. }
  29. if (el.name) {
  30. el.name = el.name.replace(id_regex, replacement);
  31. }
  32. };
  33. var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
  34. var nextIndex = parseInt(totalForms.val());
  35. var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
  36. // only show the add button if we are allowed to add more items,
  37. // note that max_num = None translates to a blank string.
  38. var showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0;
  39. $(this).each(function(i) {
  40. $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
  41. });
  42. if ($(this).length && showAddButton) {
  43. var addButton;
  44. if ($(this).attr("tagName") == "TR") {
  45. // If forms are laid out as table rows, insert the
  46. // "add" button in a new table row:
  47. var numCols = this.eq(0).children().length;
  48. $(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
  49. addButton = $(this).parent().find("tr:last a");
  50. } else {
  51. // Otherwise, insert it immediately after the last form:
  52. $(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
  53. addButton = $(this).filter(":last").next().find("a");
  54. }
  55. addButton.click(function() {
  56. var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
  57. var template = $("#" + options.prefix + "-empty");
  58. var row = template.clone(true);
  59. row.removeClass(options.emptyCssClass)
  60. .addClass(options.formCssClass)
  61. .attr("id", options.prefix + "-" + nextIndex);
  62. if (row.is("tr")) {
  63. // If the forms are laid out in table rows, insert
  64. // the remove button into the last table cell:
  65. row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
  66. } else if (row.is("ul") || row.is("ol")) {
  67. // If they're laid out as an ordered/unordered list,
  68. // insert an <li> after the last list item:
  69. row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
  70. } else {
  71. // Otherwise, just insert the remove button as the
  72. // last child element of the form's container:
  73. row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
  74. }
  75. row.find("*").each(function() {
  76. updateElementIndex(this, options.prefix, totalForms.val());
  77. });
  78. // Insert the new form when it has been fully edited
  79. row.insertBefore($(template));
  80. // Update number of total forms
  81. $(totalForms).val(parseInt(totalForms.val()) + 1);
  82. nextIndex += 1;
  83. // Hide add button in case we've hit the max, except we want to add infinitely
  84. if ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) {
  85. addButton.parent().hide();
  86. }
  87. // The delete button of each row triggers a bunch of other things
  88. row.find("a." + options.deleteCssClass).click(function() {
  89. // Remove the parent form containing this button:
  90. var row = $(this).parents("." + options.formCssClass);
  91. row.remove();
  92. nextIndex -= 1;
  93. // If a post-delete callback was provided, call it with the deleted form:
  94. if (options.removed) {
  95. options.removed(row);
  96. }
  97. // Update the TOTAL_FORMS form count.
  98. var forms = $("." + options.formCssClass);
  99. $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
  100. // Show add button again once we drop below max
  101. if ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) {
  102. addButton.parent().show();
  103. }
  104. // Also, update names and ids for all remaining form controls
  105. // so they remain in sequence:
  106. for (var i=0, formCount=forms.length; i<formCount; i++)
  107. {
  108. updateElementIndex($(forms).get(i), options.prefix, i);
  109. $(forms.get(i)).find("*").each(function() {
  110. updateElementIndex(this, options.prefix, i);
  111. });
  112. }
  113. return false;
  114. });
  115. // If a post-add callback was supplied, call it with the added form:
  116. if (options.added) {
  117. options.added(row);
  118. }
  119. return false;
  120. });
  121. }
  122. return this;
  123. }
  124. /* Setup plugin defaults */
  125. $.fn.formset.defaults = {
  126. prefix: "form", // The form prefix for your django formset
  127. addText: "add another", // Text for the add link
  128. deleteText: "remove", // Text for the delete link
  129. addCssClass: "add-row", // CSS class applied to the add link
  130. deleteCssClass: "delete-row", // CSS class applied to the delete link
  131. emptyCssClass: "empty-row", // CSS class applied to the empty row
  132. formCssClass: "dynamic-form", // CSS class applied to each form in a formset
  133. added: null, // Function called each time a new form is added
  134. removed: null // Function called each time a form is deleted
  135. }
  136. })(django.jQuery);