diff --git a/autonomie/__init__.py b/autonomie/__init__.py
index 419ce7be..12c576c4 100644
--- a/autonomie/__init__.py
+++ b/autonomie/__init__.py
@@ -42,9 +42,6 @@
from autonomie.models.config import get_config
from autonomie.utils.avatar import get_groups
from autonomie.utils.avatar import get_avatar
-from autonomie.utils.renderer import (
- customize_renderers,
-)
from autonomie.utils.session import get_session_factory
@@ -180,6 +177,9 @@ def base_configure(config, dbsession, **settings):
for module in AUTONOMIE_ADMIN_MODULES:
config.include(module)
+ from autonomie.utils.renderer import (
+ customize_renderers,
+ )
customize_renderers(config)
return config
diff --git a/autonomie/deform_templates/tasklines_sequence.pt b/autonomie/deform_templates/tasklines_sequence.pt
index 396c2955..5c5edae0 100644
--- a/autonomie/deform_templates/tasklines_sequence.pt
+++ b/autonomie/deform_templates/tasklines_sequence.pt
@@ -57,7 +57,7 @@
diff --git a/autonomie/resources.py b/autonomie/resources.py
index e7ae2bd1..031ebddc 100644
--- a/autonomie/resources.py
+++ b/autonomie/resources.py
@@ -44,8 +44,6 @@
lib_autonomie = Library("fanstatic", "static")
-# ui_dialog.depends.add(bootstrap_js)
-
def get_resource(filepath, minified=None, depends=None):
"""
@@ -59,6 +57,14 @@ def get_resource(filepath, minified=None, depends=None):
)
+# ui_dialog.depends.add(bootstrap_js)
+# To fix https://github.com/Pylons/deform/issues/276
+# https://github.com/Pylons/deform/issues/275 (only in the case of task forms
+# because we use custom templates)
+# We use a custom deform js
+custom_deform_js = get_resource("js/custom_deform.js")
+
+
def get_main_group():
"""
Return the main resource Group that will be used on all pages
diff --git a/autonomie/static/js/custom_deform.js b/autonomie/static/js/custom_deform.js
new file mode 100644
index 00000000..4f00051c
--- /dev/null
+++ b/autonomie/static/js/custom_deform.js
@@ -0,0 +1,196 @@
+/*
+ * Register a top-level callback to the deform.load() function
+ * this will be called when the DOM has finished loading. No need
+ * to include the call at the end of the page.
+ */
+
+$(document).ready(function(){
+ deform.load();
+});
+
+
+var deform_loaded = false;
+
+var deform = {
+ callbacks: [],
+
+ addCallback: function (oid, callback) {
+ deform.callbacks.push([oid, callback]);
+ },
+
+ clearCallbacks: function () {
+ deform.callbacks = [];
+ },
+
+ load: function() {
+ $(function() {
+ if (!deform_loaded) {
+ deform.processCallbacks();
+ deform.focusFirstInput();
+ deform_loaded = true;
+ }});
+ },
+
+
+ processCallbacks: function () {
+ $(deform.callbacks).each(function(num, item) {
+ var oid = item[0];
+ var callback = item[1];
+ callback(oid);
+ }
+ );
+ deform.clearCallbacks();
+ },
+
+ addSequenceItem: function (protonode, before) {
+ // - Clone the prototype node and add it before the "before" node.
+ // Also ensure any callbacks are run for the widget.
+
+ // In order to avoid breaking accessibility:
+ //
+ // - Find each tag within the prototype node with an id
+ // that has the string ``deformField(\d+)`` within it, and modify
+ // its id to have a random component.
+ // - For each label referencing an change id, change the label's
+ // for attribute to the new id.
+
+ var fieldmatch = /deformField(\d+)/;
+ var namematch = /(.+)?-[#]{3}/;
+ var jsvarmatch = /var field_oid = "deformField(\d+)";/;
+ var code = protonode.attr('prototype');
+ var html = decodeURIComponent(code);
+ var genid = deform.randomString(6);
+ html = html.replace(jsvarmatch, "var field_oid = \"deformField$1-" + genid + "\";");
+ var $htmlnode = $(html);
+ var $idnodes = $htmlnode.find('[id]');
+ var $namednodes = $htmlnode.find('[name]');
+ var idmap = {};
+
+ // replace ids containing ``deformField`` and associated label for=
+ // items which point at them
+
+ $idnodes.each(function(idx, node) {
+ var $node = $(node);
+ var oldid = $node.attr('id');
+ var newid = oldid.replace(fieldmatch, "deformField$1-" + genid);
+ $node.attr('id', newid);
+ idmap[oldid] = newid;
+ var labelselector = 'label[for=' + oldid + ']';
+ var $fornodes = $htmlnode.find(labelselector);
+ $fornodes.attr('for', newid);
+ });
+
+ // replace names a containing ```deformField`` like we do for ids
+
+ $namednodes.each(function(idx, node) {
+ var $node = $(node);
+ var oldname = $node.attr('name');
+ var newname = oldname.replace(fieldmatch, "deformField$1-" + genid);
+ $node.attr('name', newname);
+ });
+
+ $htmlnode.insertBefore(before);
+
+ $(deform.callbacks).each(function(num, item) {
+ var oid = item[0];
+ var callback = item[1];
+ var newid = idmap[oid];
+ if (newid) {
+ callback(newid);
+ }
+ });
+
+ deform.clearCallbacks();
+ var old_len = parseInt(before.attr('now_len')||'0', 10);
+ before.attr('now_len', old_len + 1);
+ // we added something to the dom, trigger a change event
+ var e = jQuery.Event("change");
+ $('#deform').trigger(e);
+ },
+
+ appendSequenceItem: function(node) {
+ var $oid_node = $(node).closest('.deformSeq');
+ var $proto_node = $oid_node.find('.deformProto').first();
+ var $before_node = $oid_node.find('.deformInsertBefore').last();
+ var min_len = parseInt($before_node.attr('min_len')||'0', 10);
+ var max_len = parseInt($before_node.attr('max_len')||'9999', 10);
+ var now_len = parseInt($before_node.attr('now_len')||'0', 10);
+ var orderable = parseInt($before_node.attr('orderable')||'0', 10);
+
+ if (now_len < max_len) {
+ deform.addSequenceItem($proto_node, $before_node);
+ deform.processSequenceButtons($oid_node, min_len, max_len,
+ now_len + 1, orderable);
+ }
+ return false;
+ },
+
+ removeSequenceItem: function(clicked) {
+ var $item_node = $(clicked).closest('.deformSeqItem');
+ var $oid_node = $item_node.closest('.deformSeq');
+ var $before_node = $oid_node.find('.deformInsertBefore').last();
+ var min_len = parseInt($before_node.attr('min_len')||'0', 10);
+ var max_len = parseInt($before_node.attr('max_len')||'9999', 10);
+ var now_len = parseInt($before_node.attr('now_len')||'0', 10);
+ var orderable = parseInt($before_node.attr('orderable')||'0', 10);
+ if (now_len > min_len) {
+ $before_node.attr('now_len', now_len - 1);
+ $item_node.remove();
+ deform.processSequenceButtons($oid_node, min_len, max_len,
+ now_len-1, orderable);
+ }
+ // we removed something from the dom, trigger a change event
+ var e = jQuery.Event("change");
+ $('#deform').trigger(e);
+ return false;
+ },
+
+ processSequenceButtons: function(oid_node, min_len, max_len, now_len,
+ orderable) {
+ orderable = !!orderable; // convert to bool
+ var has_multiple = now_len > 1;
+ var $ul = oid_node.children('.deformSeqContainer:not(.deformSeq .deformSeqContainer)');
+ var $lis = $ul.find('.deformSeqItem:not(.deformSeqContainer .deformSeqItem)');
+ var show_closebutton = now_len > min_len;
+ var show_addbutton = now_len < max_len;
+ $lis.find('.deformClosebutton').toggle(show_closebutton);
+ oid_node.find('.deformSeqAdd').toggle(show_addbutton);
+ $lis.find('.deformOrderbutton').toggle(orderable && has_multiple);
+ },
+
+ focusFirstInput: function (el) {
+ el = el || document.body;
+ var input = $(el).find(':input')
+ .filter('[id ^= deformField]')
+ .filter('[type != hidden]')
+ .first();
+ if (input) {
+ var raw = input.get(0);
+ if (raw) {
+ if (raw.type === 'text' || raw.type === 'file' ||
+ raw.type == 'password' || raw.type == 'text' ||
+ raw.type == 'textarea') {
+ if (!input.hasClass("hasDatepicker")) {
+ input.focus();
+ }
+ }
+ }
+ }
+ },
+
+ randomString: function (length) {
+ var chr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
+ chr = chr.split('');
+
+ if (! length) {
+ length = Math.floor(Math.random() * chr.length);
+ }
+
+ var str = '';
+ for (var i = 0; i < length; i++) {
+ str += chr[Math.floor(Math.random() * chr.length)];
+ }
+ return str;
+ }
+
+};
diff --git a/autonomie/utils/renderer.py b/autonomie/utils/renderer.py
index 2f99c15e..daf48a1f 100644
--- a/autonomie/utils/renderer.py
+++ b/autonomie/utils/renderer.py
@@ -39,7 +39,7 @@
from pyramid.threadlocal import get_current_request
-log = logging.getLogger(__name__)
+logger = logging.getLogger(__name__)
class CustomRenderer(ZPTRendererFactory):
@@ -128,6 +128,14 @@ def set_export_blacklist():
BLACKLISTED_KEYS = ('_acl', 'password', 'parent_id', 'parent', 'type_')
+def set_custom_deform_js():
+ from js import deform
+ from autonomie.resources import custom_deform_js
+ logger.debug(u"Overriding the default deform_js resource")
+ deform.deform_js = custom_deform_js
+ deform.resource_mapping['deform'] = [custom_deform_js]
+
+
def customize_renderers(config):
"""
Customize the different renderers
@@ -139,3 +147,4 @@ def customize_renderers(config):
# Exporters
set_export_formatters()
set_export_blacklist()
+ set_custom_deform_js()