# HG changeset patch # User Daniel Dourvaris # Date 2016-08-01 13:21:17 # Node ID a0b14eebcf7a1b5da04067b64c60afe2b736aeb8 # Parent 878882bdd6b79072cbda51e5cd8e0c85a2ce3607 deform: add deform.js to compiled scripts.js diff --git a/Gruntfile.js b/Gruntfile.js --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,6 +20,7 @@ module.exports = function(grunt) { '<%= dirs.js.src %>/moment.js', '<%= dirs.js.src %>/appenlight-client-0.4.1.min.js', '<%= dirs.js.src %>/i18n_utils.js', + '<%= dirs.js.src %>/deform.js', // Plugins '<%= dirs.js.src %>/plugins/jquery.pjax.js', diff --git a/rhodecode/public/js/src/deform.js b/rhodecode/public/js/src/deform.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/deform.js @@ -0,0 +1,194 @@ +/* + * 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 code = protonode.attr('prototype'); + var html = decodeURIComponent(code); + var $htmlnode = $(html); + var $idnodes = $htmlnode.find('[id]'); + var $namednodes = $htmlnode.find('[name]'); + var genid = deform.randomString(6); + 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('.deform-seq'); + var $proto_node = $oid_node.find('.deform-proto').first(); + var $before_node = $oid_node.find('.deform-insert-before').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('.deform-seq-item'); + var $oid_node = $item_node.closest('.deform-seq'); + var $before_node = $oid_node.find('.deform-insert-before').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.find('.deform-seq-container').not(oid_node.find('.deform-seq-container .deform-seq-container')); + var $lis = $ul.find('.deform-seq-item').not($ul.find('.deform-seq-container .deform-seq-item')); + var show_closebutton = now_len > min_len; + var show_addbutton = now_len < max_len; + $lis.find('.deform-close-button').not($lis.find('.deform-seq-container .deform-close-button')).toggle(show_closebutton); + oid_node.find('.deform-seq-add').not(oid_node.find('.deform-seq-container .deform-seq-add')).toggle(show_addbutton); + $lis.find('.deform-order-button').not($lis.find('.deform-seq-container .deform-order-button')).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/rhodecode/templates/base/root.html b/rhodecode/templates/base/root.html --- a/rhodecode/templates/base/root.html +++ b/rhodecode/templates/base/root.html @@ -83,7 +83,6 @@ c.template_context['visual']['default_re - ## avoide esaping the %N