summaryrefslogtreecommitdiff
path: root/app/lib/grappelli/media/js/inlines.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib/grappelli/media/js/inlines.js')
-rw-r--r--app/lib/grappelli/media/js/inlines.js354
1 files changed, 354 insertions, 0 deletions
diff --git a/app/lib/grappelli/media/js/inlines.js b/app/lib/grappelli/media/js/inlines.js
new file mode 100644
index 0000000..3725975
--- /dev/null
+++ b/app/lib/grappelli/media/js/inlines.js
@@ -0,0 +1,354 @@
+/**
+ * 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);