mirror of https://github.com/django/django.git
Moved the admin inline JS to new JS files for cleanliness.
This commit is contained in:
parent
6e2bb344e4
commit
4754f122dd
1
AUTHORS
1
AUTHORS
|
@ -506,6 +506,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Johan C. Stöver <johan@nilling.nl>
|
Johan C. Stöver <johan@nilling.nl>
|
||||||
Nowell Strite <http://nowell.strite.org/>
|
Nowell Strite <http://nowell.strite.org/>
|
||||||
Thomas Stromberg <tstromberg@google.com>
|
Thomas Stromberg <tstromberg@google.com>
|
||||||
|
Travis Swicegood <travis@domain51.com>
|
||||||
Pascal Varet
|
Pascal Varet
|
||||||
SuperJared
|
SuperJared
|
||||||
Radek Švarz <http://www.svarz.cz/translate/>
|
Radek Švarz <http://www.svarz.cz/translate/>
|
||||||
|
|
|
@ -1456,8 +1456,10 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
return request.user.has_perm(
|
return request.user.has_perm(
|
||||||
self.opts.app_label + '.' + self.opts.get_delete_permission())
|
self.opts.app_label + '.' + self.opts.get_delete_permission())
|
||||||
|
|
||||||
|
|
||||||
class StackedInline(InlineModelAdmin):
|
class StackedInline(InlineModelAdmin):
|
||||||
template = 'admin/edit_inline/stacked.html'
|
template = 'admin/edit_inline/stacked.html'
|
||||||
|
|
||||||
|
|
||||||
class TabularInline(InlineModelAdmin):
|
class TabularInline(InlineModelAdmin):
|
||||||
template = 'admin/edit_inline/tabular.html'
|
template = 'admin/edit_inline/tabular.html'
|
||||||
|
|
|
@ -9,128 +9,264 @@
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Spiced up with Code from Zain Memon's GSoC project 2009
|
* Spiced up with Code from Zain Memon's GSoC project 2009
|
||||||
* and modified for Django by Jannis Leidel
|
* and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
|
||||||
*
|
*
|
||||||
* Licensed under the New BSD License
|
* Licensed under the New BSD License
|
||||||
* See: http://www.opensource.org/licenses/bsd-license.php
|
* See: http://www.opensource.org/licenses/bsd-license.php
|
||||||
*/
|
*/
|
||||||
(function($) {
|
(function($) {
|
||||||
$.fn.formset = function(opts) {
|
$.fn.formset = function(opts) {
|
||||||
var options = $.extend({}, $.fn.formset.defaults, opts);
|
var options = $.extend({}, $.fn.formset.defaults, opts);
|
||||||
var updateElementIndex = function(el, prefix, ndx) {
|
var $this = $(this);
|
||||||
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
|
var $parent = $this.parent();
|
||||||
var replacement = prefix + "-" + ndx;
|
var updateElementIndex = function(el, prefix, ndx) {
|
||||||
if ($(el).attr("for")) {
|
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
|
||||||
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
var replacement = prefix + "-" + ndx;
|
||||||
}
|
if ($(el).attr("for")) {
|
||||||
if (el.id) {
|
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
||||||
el.id = el.id.replace(id_regex, replacement);
|
}
|
||||||
}
|
if (el.id) {
|
||||||
if (el.name) {
|
el.id = el.id.replace(id_regex, replacement);
|
||||||
el.name = el.name.replace(id_regex, replacement);
|
}
|
||||||
}
|
if (el.name) {
|
||||||
};
|
el.name = el.name.replace(id_regex, replacement);
|
||||||
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
|
}
|
||||||
var nextIndex = parseInt(totalForms.val(), 10);
|
};
|
||||||
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
|
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
|
||||||
// only show the add button if we are allowed to add more items,
|
var nextIndex = parseInt(totalForms.val(), 10);
|
||||||
|
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
|
||||||
|
// only show the add button if we are allowed to add more items,
|
||||||
// note that max_num = None translates to a blank string.
|
// note that max_num = None translates to a blank string.
|
||||||
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
|
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
|
||||||
$(this).each(function(i) {
|
$this.each(function(i) {
|
||||||
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
|
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
|
||||||
});
|
});
|
||||||
if ($(this).length && showAddButton) {
|
if ($this.length && showAddButton) {
|
||||||
var addButton;
|
var addButton;
|
||||||
if ($(this).attr("tagName") == "TR") {
|
if ($this.attr("tagName") == "TR") {
|
||||||
// If forms are laid out as table rows, insert the
|
// If forms are laid out as table rows, insert the
|
||||||
// "add" button in a new table row:
|
// "add" button in a new table row:
|
||||||
var numCols = this.eq(-1).children().length;
|
var numCols = this.eq(-1).children().length;
|
||||||
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
|
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
|
||||||
addButton = $(this).parent().find("tr:last a");
|
addButton = $parent.find("tr:last a");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, insert it immediately after the last form:
|
// Otherwise, insert it immediately after the last form:
|
||||||
$(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
|
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
|
||||||
addButton = $(this).filter(":last").next().find("a");
|
addButton = $this.filter(":last").next().find("a");
|
||||||
}
|
}
|
||||||
addButton.click(function(e) {
|
addButton.click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
|
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
|
||||||
var template = $("#" + options.prefix + "-empty");
|
var template = $("#" + options.prefix + "-empty");
|
||||||
var row = template.clone(true);
|
var row = template.clone(true);
|
||||||
row.removeClass(options.emptyCssClass)
|
row.removeClass(options.emptyCssClass)
|
||||||
.addClass(options.formCssClass)
|
.addClass(options.formCssClass)
|
||||||
.attr("id", options.prefix + "-" + nextIndex);
|
.attr("id", options.prefix + "-" + nextIndex);
|
||||||
if (row.is("tr")) {
|
if (row.is("tr")) {
|
||||||
// If the forms are laid out in table rows, insert
|
// If the forms are laid out in table rows, insert
|
||||||
// the remove button into the last table cell:
|
// the remove button into the last table cell:
|
||||||
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
|
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
|
||||||
} else if (row.is("ul") || row.is("ol")) {
|
} else if (row.is("ul") || row.is("ol")) {
|
||||||
// If they're laid out as an ordered/unordered list,
|
// If they're laid out as an ordered/unordered list,
|
||||||
// insert an <li> after the last list item:
|
// insert an <li> after the last list item:
|
||||||
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
|
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just insert the remove button as the
|
// Otherwise, just insert the remove button as the
|
||||||
// last child element of the form's container:
|
// last child element of the form's container:
|
||||||
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
|
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
|
||||||
}
|
}
|
||||||
row.find("*").each(function() {
|
row.find("*").each(function() {
|
||||||
updateElementIndex(this, options.prefix, totalForms.val());
|
updateElementIndex(this, options.prefix, totalForms.val());
|
||||||
});
|
});
|
||||||
// Insert the new form when it has been fully edited
|
// Insert the new form when it has been fully edited
|
||||||
row.insertBefore($(template));
|
row.insertBefore($(template));
|
||||||
// Update number of total forms
|
// Update number of total forms
|
||||||
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
|
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
|
||||||
nextIndex += 1;
|
nextIndex += 1;
|
||||||
// Hide add button in case we've hit the max, except we want to add infinitely
|
// Hide add button in case we've hit the max, except we want to add infinitely
|
||||||
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
|
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
|
||||||
addButton.parent().hide();
|
addButton.parent().hide();
|
||||||
}
|
}
|
||||||
// The delete button of each row triggers a bunch of other things
|
// The delete button of each row triggers a bunch of other things
|
||||||
row.find("a." + options.deleteCssClass).click(function(e) {
|
row.find("a." + options.deleteCssClass).click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Remove the parent form containing this button:
|
// Remove the parent form containing this button:
|
||||||
var row = $(this).parents("." + options.formCssClass);
|
var row = $(this).parents("." + options.formCssClass);
|
||||||
row.remove();
|
row.remove();
|
||||||
nextIndex -= 1;
|
nextIndex -= 1;
|
||||||
// If a post-delete callback was provided, call it with the deleted form:
|
// If a post-delete callback was provided, call it with the deleted form:
|
||||||
if (options.removed) {
|
if (options.removed) {
|
||||||
options.removed(row);
|
options.removed(row);
|
||||||
}
|
}
|
||||||
// Update the TOTAL_FORMS form count.
|
// Update the TOTAL_FORMS form count.
|
||||||
var forms = $("." + options.formCssClass);
|
var forms = $("." + options.formCssClass);
|
||||||
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
|
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
|
||||||
// Show add button again once we drop below max
|
// Show add button again once we drop below max
|
||||||
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
|
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
|
||||||
addButton.parent().show();
|
addButton.parent().show();
|
||||||
}
|
}
|
||||||
// Also, update names and ids for all remaining form controls
|
// Also, update names and ids for all remaining form controls
|
||||||
// so they remain in sequence:
|
// so they remain in sequence:
|
||||||
for (var i=0, formCount=forms.length; i<formCount; i++)
|
for (var i=0, formCount=forms.length; i<formCount; i++)
|
||||||
{
|
{
|
||||||
updateElementIndex($(forms).get(i), options.prefix, i);
|
updateElementIndex($(forms).get(i), options.prefix, i);
|
||||||
$(forms.get(i)).find("*").each(function() {
|
$(forms.get(i)).find("*").each(function() {
|
||||||
updateElementIndex(this, options.prefix, i);
|
updateElementIndex(this, options.prefix, i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// If a post-add callback was supplied, call it with the added form:
|
// If a post-add callback was supplied, call it with the added form:
|
||||||
if (options.added) {
|
if (options.added) {
|
||||||
options.added(row);
|
options.added(row);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
/* Setup plugin defaults */
|
|
||||||
$.fn.formset.defaults = {
|
/* Setup plugin defaults */
|
||||||
prefix: "form", // The form prefix for your django formset
|
$.fn.formset.defaults = {
|
||||||
addText: "add another", // Text for the add link
|
prefix: "form", // The form prefix for your django formset
|
||||||
deleteText: "remove", // Text for the delete link
|
addText: "add another", // Text for the add link
|
||||||
addCssClass: "add-row", // CSS class applied to the add link
|
deleteText: "remove", // Text for the delete link
|
||||||
deleteCssClass: "delete-row", // CSS class applied to the delete link
|
addCssClass: "add-row", // CSS class applied to the add link
|
||||||
emptyCssClass: "empty-row", // CSS class applied to the empty row
|
deleteCssClass: "delete-row", // CSS class applied to the delete link
|
||||||
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
|
emptyCssClass: "empty-row", // CSS class applied to the empty row
|
||||||
added: null, // Function called each time a new form is added
|
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
|
||||||
removed: null // Function called each time a form is deleted
|
added: null, // Function called each time a new form is added
|
||||||
};
|
removed: null // Function called each time a form is deleted
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Tabular inlines ---------------------------------------------------------
|
||||||
|
$.fn.tabularFormset = function(options) {
|
||||||
|
var $rows = $(this);
|
||||||
|
var alternatingRows = function(row) {
|
||||||
|
$($rows.selector).not(".add-row").removeClass("row1 row2")
|
||||||
|
.filter(":even").addClass("row1").end()
|
||||||
|
.filter(":odd").addClass("row2");
|
||||||
|
};
|
||||||
|
|
||||||
|
var reinitDateTimeShortCuts = function() {
|
||||||
|
// Reinitialize the calendar and clock widgets by force
|
||||||
|
if (typeof DateTimeShortcuts != "undefined") {
|
||||||
|
$(".datetimeshortcuts").remove();
|
||||||
|
DateTimeShortcuts.init();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateSelectFilter = function() {
|
||||||
|
// If any SelectFilter widgets are a part of the new form,
|
||||||
|
// instantiate a new SelectFilter instance for it.
|
||||||
|
if (typeof SelectFilter != 'undefined'){
|
||||||
|
$('.selectfilter').each(function(index, value){
|
||||||
|
var namearr = value.name.split('-');
|
||||||
|
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix );
|
||||||
|
});
|
||||||
|
$('.selectfilterstacked').each(function(index, value){
|
||||||
|
var namearr = value.name.split('-');
|
||||||
|
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var initPrepopulatedFields = function(row) {
|
||||||
|
row.find('.prepopulated_field').each(function() {
|
||||||
|
var field = $(this),
|
||||||
|
input = field.find('input, select, textarea'),
|
||||||
|
dependency_list = input.data('dependency_list') || [],
|
||||||
|
dependencies = [];
|
||||||
|
$.each(dependency_list, function(i, field_name) {
|
||||||
|
dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
|
||||||
|
});
|
||||||
|
if (dependencies.length) {
|
||||||
|
input.prepopulate(dependencies, input.attr('maxlength'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$rows.formset({
|
||||||
|
prefix: options.prefix,
|
||||||
|
addText: options.addText,
|
||||||
|
formCssClass: "dynamic-" + options.prefix,
|
||||||
|
deleteCssClass: "inline-deletelink",
|
||||||
|
deleteText: options.deleteText,
|
||||||
|
emptyCssClass: "empty-form",
|
||||||
|
removed: alternatingRows,
|
||||||
|
added: function(row) {
|
||||||
|
initPrepopulatedFields(row);
|
||||||
|
reinitDateTimeShortCuts();
|
||||||
|
updateSelectFilter();
|
||||||
|
alternatingRows(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stacked inlines ---------------------------------------------------------
|
||||||
|
$.fn.stackedFormset = function(options) {
|
||||||
|
var $rows = $(this);
|
||||||
|
var updateInlineLabel = function(row) {
|
||||||
|
$($rows.selector).find(".inline_label").each(function(i) {
|
||||||
|
var count = i + 1;
|
||||||
|
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var reinitDateTimeShortCuts = function() {
|
||||||
|
// Reinitialize the calendar and clock widgets by force, yuck.
|
||||||
|
if (typeof DateTimeShortcuts != "undefined") {
|
||||||
|
$(".datetimeshortcuts").remove();
|
||||||
|
DateTimeShortcuts.init();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateSelectFilter = function() {
|
||||||
|
// If any SelectFilter widgets were added, instantiate a new instance.
|
||||||
|
if (typeof SelectFilter != "undefined"){
|
||||||
|
$(".selectfilter").each(function(index, value){
|
||||||
|
var namearr = value.name.split('-');
|
||||||
|
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix);
|
||||||
|
});
|
||||||
|
$(".selectfilterstacked").each(function(index, value){
|
||||||
|
var namearr = value.name.split('-');
|
||||||
|
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var initPrepopulatedFields = function(row) {
|
||||||
|
row.find('.prepopulated_field').each(function() {
|
||||||
|
var field = $(this),
|
||||||
|
input = field.find('input, select, textarea'),
|
||||||
|
dependency_list = input.data('dependency_list') || [],
|
||||||
|
dependencies = [];
|
||||||
|
$.each(dependency_list, function(i, field_name) {
|
||||||
|
dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
|
||||||
|
});
|
||||||
|
if (dependencies.length) {
|
||||||
|
input.prepopulate(dependencies, input.attr('maxlength'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$rows.formset({
|
||||||
|
prefix: options.prefix,
|
||||||
|
addText: options.addText,
|
||||||
|
formCssClass: "dynamic-" + options.prefix,
|
||||||
|
deleteCssClass: "inline-deletelink",
|
||||||
|
deleteText: options.deleteText,
|
||||||
|
emptyCssClass: "empty-form",
|
||||||
|
removed: updateInlineLabel,
|
||||||
|
added: (function(row) {
|
||||||
|
initPrepopulatedFields(row);
|
||||||
|
reinitDateTimeShortCuts();
|
||||||
|
updateSelectFilter();
|
||||||
|
updateInlineLabel(row);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
};
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
(function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,e){var d=RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+e;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(d,f));if(c.id)c.id=c.id.replace(d,f);if(c.name)c.name=c.name.replace(d,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()===""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
|
(function(b){b.fn.formset=function(d){var a=b.extend({},b.fn.formset.defaults,d),c=b(this),d=c.parent(),i=function(a,e,g){var d=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+g;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(d,e));a.id&&(a.id=a.id.replace(d,e));a.name&&(a.name=a.name.replace(d,e))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),g=parseInt(f.val(),10),e=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),f=""===e.val()||0<e.val()-f.val();c.each(function(){b(this).not("."+
|
||||||
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(-1).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(c){c.preventDefault();
|
a.emptyCssClass).addClass(a.formCssClass)});if(c.length&&f){var h;"TR"==c.attr("tagName")?(c=this.eq(-1).children().length,d.append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=d.find("tr:last a")):(c.filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=c.filter(":last").next().find("a"));h.click(function(d){d.preventDefault();var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),d=b("#"+a.prefix+
|
||||||
var f=b("#id_"+a.prefix+"-TOTAL_FORMS");c=b("#"+a.prefix+"-empty");var e=c.clone(true);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(e.is("tr"))e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
|
"-empty"),c=d.clone(true);c.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);c.is("tr")?c.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):c.is("ul")||c.is("ol")?c.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):c.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></span>");c.find("*").each(function(){i(this,
|
||||||
a.deleteText+"</a></span>");e.find("*").each(function(){k(this,a.prefix,f.val())});e.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);l+=1;h.val()!==""&&h.val()-f.val()<=0&&j.parent().hide();e.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();l-=1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);if(h.val()===""||h.val()-d.length>0)j.parent().show();for(var i=0,m=d.length;i<m;i++){k(b(d).get(i),
|
a.prefix,f.val())});c.insertBefore(b(d));b(f).val(parseInt(f.val(),10)+1);g=g+1;e.val()!==""&&e.val()-f.val()<=0&&h.parent().hide();c.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();g=g-1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(e.val()===""||e.val()-d.length>0)&&h.parent().show();for(var c=0,f=d.length;c<f;c++){i(b(d).get(c),a.prefix,c);b(d.get(c)).find("*").each(function(){i(this,
|
||||||
a.prefix,i);b(d.get(i)).find("*").each(function(){k(this,a.prefix,i)})}});a.added&&a.added(e)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
|
a.prefix,c)})}});a.added&&a.added(c)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};b.fn.tabularFormset=function(d){var a=b(this),c=function(){b(a.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")};a.formset({prefix:d.prefix,addText:d.addText,formCssClass:"dynamic-"+
|
||||||
|
d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());"undefined"!=
|
||||||
|
typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a};b.fn.stackedFormset=function(d){var a=b(this),c=function(){b(a.selector).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,"#"+a))})};a.formset({prefix:d.prefix,
|
||||||
|
addText:d.addText,formCssClass:"dynamic-"+d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".form-row .field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),
|
||||||
|
DateTimeShortcuts.init());"undefined"!=typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a}})(django.jQuery);
|
||||||
|
|
|
@ -20,63 +20,11 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function() {
|
$("#{{ inline_admin_formset.formset.prefix }}-group .inline-related").stackedFormset({
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
|
prefix: '{{ inline_admin_formset.formset.prefix }}',
|
||||||
var updateInlineLabel = function(row) {
|
adminStaticPrefix: '{% static "admin/" %}',
|
||||||
$(rows).find(".inline_label").each(function(i) {
|
deleteText: "{% trans "Remove" %}",
|
||||||
var count = i + 1;
|
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}"
|
||||||
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
|
});
|
||||||
});
|
|
||||||
};
|
|
||||||
var reinitDateTimeShortCuts = function() {
|
|
||||||
// Reinitialize the calendar and clock widgets by force, yuck.
|
|
||||||
if (typeof DateTimeShortcuts != "undefined") {
|
|
||||||
$(".datetimeshortcuts").remove();
|
|
||||||
DateTimeShortcuts.init();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var updateSelectFilter = function() {
|
|
||||||
// If any SelectFilter widgets were added, instantiate a new instance.
|
|
||||||
if (typeof SelectFilter != "undefined"){
|
|
||||||
$(".selectfilter").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
|
|
||||||
});
|
|
||||||
$(".selectfilterstacked").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var initPrepopulatedFields = function(row) {
|
|
||||||
row.find('.prepopulated_field').each(function() {
|
|
||||||
var field = $(this);
|
|
||||||
var input = field.find('input, select, textarea');
|
|
||||||
var dependency_list = input.data('dependency_list') || [];
|
|
||||||
var dependencies = [];
|
|
||||||
$.each(dependency_list, function(i, field_name) {
|
|
||||||
dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
|
|
||||||
});
|
|
||||||
if (dependencies.length) {
|
|
||||||
input.prepopulate(dependencies, input.attr('maxlength'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$(rows).formset({
|
|
||||||
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
|
|
||||||
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
deleteCssClass: "inline-deletelink",
|
|
||||||
deleteText: "{% trans "Remove" %}",
|
|
||||||
emptyCssClass: "empty-form",
|
|
||||||
removed: updateInlineLabel,
|
|
||||||
added: (function(row) {
|
|
||||||
initPrepopulatedFields(row);
|
|
||||||
reinitDateTimeShortCuts();
|
|
||||||
updateSelectFilter();
|
|
||||||
updateInlineLabel(row);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -67,64 +67,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function($) {
|
$("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
|
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
||||||
var alternatingRows = function(row) {
|
adminStaticPrefix: '{% static "admin/" %}',
|
||||||
$(rows).not(".add-row").removeClass("row1 row2")
|
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
|
||||||
.filter(":even").addClass("row1").end()
|
deleteText: "{% trans 'Remove' %}"
|
||||||
.filter(rows + ":odd").addClass("row2");
|
});
|
||||||
}
|
|
||||||
var reinitDateTimeShortCuts = function() {
|
|
||||||
// Reinitialize the calendar and clock widgets by force
|
|
||||||
if (typeof DateTimeShortcuts != "undefined") {
|
|
||||||
$(".datetimeshortcuts").remove();
|
|
||||||
DateTimeShortcuts.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var updateSelectFilter = function() {
|
|
||||||
// If any SelectFilter widgets are a part of the new form,
|
|
||||||
// instantiate a new SelectFilter instance for it.
|
|
||||||
if (typeof SelectFilter != "undefined"){
|
|
||||||
$(".selectfilter").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
|
|
||||||
});
|
|
||||||
$(".selectfilterstacked").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var initPrepopulatedFields = function(row) {
|
|
||||||
row.find('.prepopulated_field').each(function() {
|
|
||||||
var field = $(this);
|
|
||||||
var input = field.find('input, select, textarea');
|
|
||||||
var dependency_list = input.data('dependency_list') || [];
|
|
||||||
var dependencies = [];
|
|
||||||
$.each(dependency_list, function(i, field_name) {
|
|
||||||
dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
|
|
||||||
});
|
|
||||||
if (dependencies.length) {
|
|
||||||
input.prepopulate(dependencies, input.attr('maxlength'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$(rows).formset({
|
|
||||||
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
|
|
||||||
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
deleteCssClass: "inline-deletelink",
|
|
||||||
deleteText: "{% trans "Remove" %}",
|
|
||||||
emptyCssClass: "empty-form",
|
|
||||||
removed: alternatingRows,
|
|
||||||
added: (function(row) {
|
|
||||||
initPrepopulatedFields(row);
|
|
||||||
reinitDateTimeShortCuts();
|
|
||||||
updateSelectFilter();
|
|
||||||
alternatingRows(row);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,10 +8,11 @@ from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
# local test models
|
# local test models
|
||||||
from .admin import InnerInline
|
from .admin import InnerInline, TitleInline, site
|
||||||
from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
|
from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
|
||||||
OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile,
|
OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile,
|
||||||
ProfileCollection, ParentModelWithCustomPk, ChildModel1, ChildModel2)
|
ProfileCollection, ParentModelWithCustomPk, ChildModel1, ChildModel2,
|
||||||
|
Title)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
@ -408,6 +409,47 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
|
||||||
fixtures = ['admin-views-users.xml']
|
fixtures = ['admin-views-users.xml']
|
||||||
urls = "regressiontests.admin_inlines.urls"
|
urls = "regressiontests.admin_inlines.urls"
|
||||||
|
|
||||||
|
def test_add_stackeds(self):
|
||||||
|
"""
|
||||||
|
Ensure that the "Add another XXX" link correctly adds items to the
|
||||||
|
stacked formset.
|
||||||
|
"""
|
||||||
|
self.admin_login(username='super', password='secret')
|
||||||
|
self.selenium.get('%s%s' % (self.live_server_url,
|
||||||
|
'/admin/admin_inlines/holder4/add/'))
|
||||||
|
|
||||||
|
inline_id = '#inner4stacked_set-group'
|
||||||
|
rows_length = lambda: len(self.selenium.find_elements_by_css_selector(
|
||||||
|
'%s .dynamic-inner4stacked_set' % inline_id))
|
||||||
|
self.assertEqual(rows_length(), 3)
|
||||||
|
|
||||||
|
add_button = self.selenium.find_element_by_link_text(
|
||||||
|
'Add another Inner4 Stacked')
|
||||||
|
add_button.click()
|
||||||
|
|
||||||
|
self.assertEqual(rows_length(), 4)
|
||||||
|
|
||||||
|
def test_delete_stackeds(self):
|
||||||
|
self.admin_login(username='super', password='secret')
|
||||||
|
self.selenium.get('%s%s' % (self.live_server_url,
|
||||||
|
'/admin/admin_inlines/holder4/add/'))
|
||||||
|
|
||||||
|
inline_id = '#inner4stacked_set-group'
|
||||||
|
rows_length = lambda: len(self.selenium.find_elements_by_css_selector(
|
||||||
|
'%s .dynamic-inner4stacked_set' % inline_id))
|
||||||
|
self.assertEqual(rows_length(), 3)
|
||||||
|
|
||||||
|
add_button = self.selenium.find_element_by_link_text(
|
||||||
|
'Add another Inner4 Stacked')
|
||||||
|
add_button.click()
|
||||||
|
add_button.click()
|
||||||
|
|
||||||
|
self.assertEqual(rows_length(), 5, msg="sanity check")
|
||||||
|
for delete_link in self.selenium.find_elements_by_css_selector(
|
||||||
|
'%s .inline-deletelink' % inline_id):
|
||||||
|
delete_link.click()
|
||||||
|
self.assertEqual(rows_length(), 3)
|
||||||
|
|
||||||
def test_add_inlines(self):
|
def test_add_inlines(self):
|
||||||
"""
|
"""
|
||||||
Ensure that the "Add another XXX" link correctly adds items to the
|
Ensure that the "Add another XXX" link correctly adds items to the
|
||||||
|
@ -516,6 +558,21 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
|
||||||
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
|
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
|
||||||
'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
|
'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
|
||||||
|
|
||||||
|
def test_alternating_rows(self):
|
||||||
|
self.admin_login(username='super', password='secret')
|
||||||
|
self.selenium.get('%s%s' % (self.live_server_url,
|
||||||
|
'/admin/admin_inlines/profilecollection/add/'))
|
||||||
|
|
||||||
|
# Add a few inlines
|
||||||
|
self.selenium.find_element_by_link_text('Add another Profile').click()
|
||||||
|
self.selenium.find_element_by_link_text('Add another Profile').click()
|
||||||
|
|
||||||
|
row_selector = 'form#profilecollection_form tr.dynamic-profile_set'
|
||||||
|
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
|
||||||
|
"%s.row1" % row_selector)), 2, msg="Expect two row1 styled rows")
|
||||||
|
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
|
||||||
|
"%s.row2" % row_selector)), 1, msg="Expect one row2 styled row")
|
||||||
|
|
||||||
|
|
||||||
class SeleniumChromeTests(SeleniumFirefoxTests):
|
class SeleniumChromeTests(SeleniumFirefoxTests):
|
||||||
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
|
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
|
||||||
|
|
Loading…
Reference in New Issue