355 lines
14 KiB
JavaScript
355 lines
14 KiB
JavaScript
/**
|
|
* helper functions for sortable inlines (tabular and stacked)
|
|
*/
|
|
|
|
function reinitDateTimeFields(row) {
|
|
row.find(".vDateField").datepicker({
|
|
//appendText: '(mm/dd/yyyy)',
|
|
showOn: 'button',
|
|
buttonImageOnly: false,
|
|
buttonText: '',
|
|
dateFormat: grappelli.getFormat('date'),
|
|
});
|
|
row.find(".vTimeField").timepicker();
|
|
}
|
|
|
|
function updateSelectFilter(row) {
|
|
// If any SelectFilter widgets were added, instantiate a new instance.
|
|
if (typeof SelectFilter != "undefined"){
|
|
row.find(".selectfilter").each(function(index, value){
|
|
var namearr = value.name.split('-');
|
|
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% admin_media_prefix %}");
|
|
});
|
|
row.find(".selectfilterstacked").each(function(index, value){
|
|
var namearr = value.name.split('-');
|
|
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% admin_media_prefix %}");
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* reorder of inlines dom
|
|
* works pretty similar to updateFormIndex() of the inline() widget
|
|
* helper function for sortable
|
|
*/
|
|
function sortable_updateFormIndex(form, idx, prefix) {
|
|
var re = /-\d+-/g;
|
|
form.attr('id', prefix + idx);
|
|
form.find(':input,span,table,iframe,label,a,ul,p,img').each(function() {
|
|
var node = django.jQuery(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 + "-"));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* checks if inline form is filled
|
|
* helper function for sortable
|
|
*/
|
|
function is_form_filled(form) {
|
|
var input_tags = form.find("input"),
|
|
input_tag;
|
|
for (var i = 0; i < input_tags.length; i++) {
|
|
input_tag = django.jQuery(input_tags[i]);
|
|
if (input_tag.val()) {
|
|
if (input_tag.attr("type") == "checkbox" || input_tag.attr("type") == "radio") {
|
|
if (input_tag.attr("checked")) {
|
|
return true;
|
|
}
|
|
} else if (input_tag.attr("type") != "hidden"){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// updates label of inlines
|
|
// to keep them in sequence (#1,#2,#3,...)
|
|
function stacked_updateInlineLabel(row) {
|
|
row.parent().find("div.module").find("h3:first").each(function(i) {
|
|
var h3_node = django.jQuery(this);
|
|
h3_node.html(h3_node.html().replace(/(#\d+)/g, "#" + ++i));
|
|
});
|
|
}
|
|
|
|
// init tinymce for new inlines
|
|
function reinitTinyMCE(row) {
|
|
row.find("textarea.vLargeTextField").each(function() {
|
|
tinyMCE.execCommand('mceAddControl', false, this.id);
|
|
});
|
|
}
|
|
|
|
// need to remove tinymce form removed inline
|
|
function deleteTinyMCE(row) {
|
|
row.find("textarea.vLargeTextField").each(function() {
|
|
if (tinyMCE.getInstanceById(this.id)) {
|
|
tinyMCE.execCommand('mceRemoveControl', false, this.id);
|
|
}
|
|
});
|
|
}
|
|
|
|
function tabular_onAdded(row) {
|
|
reinitDateTimeFields(row);
|
|
updateSelectFilter(row);
|
|
}
|
|
|
|
function stacked_onAdded(row) {
|
|
reinitTinyMCE(row);
|
|
reinitDateTimeFields(row);
|
|
updateSelectFilter(row);
|
|
stacked_updateInlineLabel(row);
|
|
}
|
|
|
|
function stacked_onRemoved(row) {
|
|
stacked_updateInlineLabel(row);
|
|
deleteTinyMCE(row);
|
|
}
|
|
|
|
|
|
(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);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
};
|
|
})(django.jQuery);
|