[#5942] Swap js library in label editor for a better one
Signed-off-by: Peter Hartmann <peterhartmann@apache.org>
diff --git a/Allura/allura/lib/widgets/form_fields.py b/Allura/allura/lib/widgets/form_fields.py
index cc845d1..24330c7 100644
--- a/Allura/allura/lib/widgets/form_fields.py
+++ b/Allura/allura/lib/widgets/form_fields.py
@@ -47,37 +47,12 @@
return ','.join(value)
def resources(self):
- yield ew.JSLink('js/jquery.tag.editor.js')
- yield ew.CSSScript('''
- input.label_edit{ float: left; }
- .tagEditor{
- margin: 4px 0;
- padding: 0;
- float: left;
- }
-
- .tagEditor li, .removable{
- display: inline;
- background-image: url('%s');
- background-color: #eef;
- background-position: right center;
- background-repeat: no-repeat;
- list-style-type: none;
- padding: 0 18px 0 6px;
- margin: 0 4px;
- cursor: pointer;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- }
-
- .tagEditor li:hover{
- background-color: #ebebeb;
- }''' % g.forge_static('images/minus_small.png'))
+ yield ew.JSLink('js/jquery.tagsinput.js')
+ yield ew.CSSLink('css/jquery.tagsinput.css')
yield onready('''
- $('input.label_edit').tagEditor({
- confirmRemoval: false,
- completeOnSeparator: true,
- completeOnBlur: true
+ $('input.label_edit').tagsInput({
+ 'height':'100%',
+ 'width':'100%'
});
''')
diff --git a/Allura/allura/lib/widgets/resources/css/jquery.tagsinput.css b/Allura/allura/lib/widgets/resources/css/jquery.tagsinput.css
new file mode 100644
index 0000000..32f4b4a
--- /dev/null
+++ b/Allura/allura/lib/widgets/resources/css/jquery.tagsinput.css
@@ -0,0 +1,8 @@
+div.tagsinput { padding:5px; width:300px; height:100px; overflow-y: auto;}
+div.tagsinput span.tag { border: 1px solid #0077AA; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #eeeeff; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;}
+div.tagsinput span.tag:hover { background-color: #ebebeb; }
+div.tagsinput span.tag a { font-weight: bold; text-decoration:none; font-size: 11px; }
+div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; }
+div.tagsinput div { display:block; float: left; }
+.tags_clear { clear: both; width: 100%; height: 0px; }
+.not_valid {background: #FBD8DB !important; color: #90111A !important;}
diff --git a/Allura/allura/lib/widgets/resources/js/jquery.tag.editor.js b/Allura/allura/lib/widgets/resources/js/jquery.tag.editor.js
deleted file mode 100755
index 1becb43..0000000
--- a/Allura/allura/lib/widgets/resources/js/jquery.tag.editor.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-@author: Karl-Johan Sjögren / http://blog.crazybeavers.se/
-@url: http://blog.crazybeavers.se/wp-content/demos/jquery.tag.editor/
-@license: Creative Commons License - ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
-@version: 1.3
-@changelog
- 1.3
- Any string already in the textbox when enabling the tag editor is now parsed as tags
- Added initialParse to stop the initial parsing
- Added confirmRemovalText as an option to better support different localizations
- Added the getTags method.
- Fixed completeOnBlur that wasn't working
- 1.2
- Fixed bug with completeOnSeparator for Firefox
- Fixed so that pressing return on an empty editor would submit the form
- 1.1
- Initial public release
- Added the completeOnSeparator and completeOnBlur options
-*/
-(function($) {
- $.fn.extend({
- tagEditor: function(options) {
- var defaults = {
- separator: ',',
- items: [],
- className: 'tagEditor',
- confirmRemoval: false,
- confirmRemovalText: 'Do you really want to remove the tag?',
- completeOnSeparator: false,
- completeOnBlur: false,
- initialParse: true
- }
-
- var options = $.extend(defaults, options);
- var listBase, textBase = this, hiddenText;
- var itemBase = [];
-
- this.getTags = function() {
- return itemBase.join(options.separator);
- };
-
- return this.each(function() {
- var textBase = $(this);
- var hiddenText = $(document.createElement('input'));
- hiddenText.attr('type', 'hidden');
- textBase.after(hiddenText);
-
- var listBase = $(document.createElement('ul'));
- listBase.attr('class', options.className);
- $(this).after(listBase);
- //
- // var plusIcon = $(document.createElement('span'));
- // plusIcon.attr('style', "display: inline-block");
- // plusIcon.attr('class', "ui-icon ui-icon-plus");
- // $(this).after(plusIcon);
-
- for (var i = 0; i < options.items.length; i++) {
- addTag(jQuery.trim(options.items[i]));
- }
-
- if (options.initialParse)
- parse();
-
- if (options.completeOnBlur)
- $(this).blur(parse);
-
- buildArray();
- $(this).keypress(handleKeys);
-
- var form = $(this).parents("form");
- form.submit(function() {
- parse();
- hiddenText.val(itemBase.join(options.separator));
- hiddenText.attr("id", textBase.attr("id"));
- hiddenText.attr("name", textBase.attr("name"));
- textBase.attr("id", textBase.attr("id") + '_old');
- textBase.attr("name", textBase.attr("name") + '_old');
- });
-
- function addTag(tag) {
- tag = jQuery.trim(tag);
- for (var i = 0; i < itemBase.length; i++) {
- if (itemBase[i].toLowerCase() == tag.toLowerCase())
- return;
- }
-
- var item = $(document.createElement('li'));
- item.text(tag);
- item.attr('title', 'Remove tag');
- item.click(function() {
- if (options.confirmRemoval)
- if (!confirm(options.confirmRemovalText))
- return;
-
- item.remove();
- parse();
- });
-
- listBase.append(item);
- }
-
- function buildArray() {
- itemBase = [];
- var items = $("li", listBase);
-
- for (var i = 0; i < items.length; i++) {
- itemBase.push(jQuery.trim($(items[i]).text()));
- }
- }
-
- function parse() {
- var items = textBase.val().split(options.separator);
-
- for (var i = 0; i < items.length; i++) {
- var trimmedItem = jQuery.trim(items[i]);
- if (trimmedItem.length > 0)
- addTag(trimmedItem);
- }
- textBase.val("");
- buildArray();
- }
-
- function handleKeys(ev) {
- var keyCode = (ev.which) ? ev.which : ev.keyCode;
-
- if (options.completeOnSeparator) {
- if (String.fromCharCode(keyCode) == options.separator) {
- parse();
- return false;
- }
- }
-
- switch (keyCode) {
- case 13:
- {
- if (jQuery.trim(textBase.val()) != '') {
- parse();
- return false;
- }
- return true;
- }
- }
- }
- });
- }
- });
-})(jQuery);
\ No newline at end of file
diff --git a/Allura/allura/lib/widgets/resources/js/jquery.tagsinput.js b/Allura/allura/lib/widgets/resources/js/jquery.tagsinput.js
new file mode 100644
index 0000000..b1b96c6
--- /dev/null
+++ b/Allura/allura/lib/widgets/resources/js/jquery.tagsinput.js
@@ -0,0 +1,357 @@
+/*
+
+ jQuery Tags Input Plugin 1.3.4
+
+ Copyright (c) 2011 XOXCO, Inc
+ Copyright (c) 2013 Peter Hartmann
+
+ "jq" function is copyright (c) 2013 jQuery Foundation and other contributors
+ http://learn.jquery.com/?page_id=297
+
+ Licensed under the MIT license:
+ http://www.opensource.org/licenses/mit-license.php
+
+*/
+
+function jq( id ) {
+ return "#" + id.replace( /(:|\.|\[|\])/g, "\\$1" );
+}
+
+(function($) {
+
+ var delimiter = new Array();
+ var tags_callbacks = new Array();
+ $.fn.doAutosize = function(o){
+ var minWidth = $(this).data('minwidth'),
+ maxWidth = $(this).data('maxwidth'),
+ val = '',
+ input = $(this),
+ testSubject = $(jq($(this).data('tester_id')));
+
+ if (val === (val = input.val())) {return;}
+
+ // Enter new content into testSubject
+ var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>');
+ testSubject.html(escaped);
+ // Calculate new width + whether to change
+ var testerWidth = testSubject.width(),
+ newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth,
+ currentWidth = input.width(),
+ isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
+ || (newWidth > minWidth && newWidth < maxWidth);
+
+ // Animate width
+ if (isValidWidthChange) {
+ input.width(newWidth);
+ }
+
+
+ };
+ $.fn.resetAutosize = function(options){
+ // alert(JSON.stringify(options));
+ var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(),
+ maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.tagsinput').width() - options.inputPadding),
+ val = '',
+ input = $(this),
+ testSubject = $('<tester/>').css({
+ position: 'absolute',
+ top: -9999,
+ left: -9999,
+ width: 'auto',
+ fontSize: input.css('fontSize'),
+ fontFamily: input.css('fontFamily'),
+ fontWeight: input.css('fontWeight'),
+ letterSpacing: input.css('letterSpacing'),
+ whiteSpace: 'nowrap'
+ }),
+ testerId = $(this).attr('id')+'_autosize_tester';
+ if(! $(jq(testerId)).length > 0){
+ testSubject.attr('id', testerId);
+ testSubject.appendTo('body');
+ }
+
+ input.data('minwidth', minWidth);
+ input.data('maxwidth', maxWidth);
+ input.data('tester_id', testerId);
+ input.css('width', minWidth);
+ };
+
+ $.fn.addTag = function(value,options) {
+ options = jQuery.extend({focus:false,callback:true},options);
+ this.each(function() {
+ var id = $(this).attr('id');
+
+ var tagslist = $(this).val().split(delimiter[id]);
+ if (tagslist[0] == '') {
+ tagslist = new Array();
+ }
+
+ value = jQuery.trim(value);
+
+ if (options.unique) {
+ var skipTag = $(this).tagExist(value);
+ if(skipTag == true) {
+ //Marks fake input as not_valid to let styling it
+ $(jq(id)+'_tag').addClass('not_valid');
+ }
+ } else {
+ var skipTag = false;
+ }
+
+ if (value !='' && skipTag != true) {
+ $('<span>').addClass('tag').append(
+ $('<span>').text(value).append(' '),
+ $('<a>', {
+ href : '#',
+ title : 'Removing tag',
+ text : 'x'
+ }).click(function () {
+ return $(jq(id)).removeTag(escape(value));
+ })
+ ).insertBefore(jq(id) + '_addTag');
+
+ tagslist.push(value);
+
+ $(jq(id)+'_tag').val('');
+ if (options.focus) {
+ $(jq(id)+'_tag').focus();
+ } else {
+ $(jq(id)+'_tag').blur();
+ }
+
+ $.fn.tagsInput.updateTagsField(this,tagslist);
+
+ if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) {
+ var f = tags_callbacks[id]['onAddTag'];
+ f.call(this, value);
+ }
+ if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
+ {
+ var i = tagslist.length;
+ var f = tags_callbacks[id]['onChange'];
+ f.call(this, $(this), tagslist[i-1]);
+ }
+ }
+
+ });
+
+ return false;
+ };
+
+ $.fn.removeTag = function(value) {
+ value = unescape(value);
+ this.each(function() {
+ var id = $(this).attr('id');
+
+ var old = $(this).val().split(delimiter[id]);
+
+ $(jq(id)+'_tagsinput .tag').remove();
+ str = '';
+ for (i=0; i< old.length; i++) {
+ if (old[i]!=value) {
+ str = str + delimiter[id] +old[i];
+ }
+ }
+
+ $.fn.tagsInput.importTags(this,str);
+
+ if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) {
+ var f = tags_callbacks[id]['onRemoveTag'];
+ f.call(this, value);
+ }
+ });
+
+ return false;
+ };
+
+ $.fn.tagExist = function(val) {
+ var id = $(this).attr('id');
+ var tagslist = $(this).val().split(delimiter[id]);
+ return (jQuery.inArray(val, tagslist) >= 0); //true when tag exists, false when not
+ };
+
+ // clear all existing tags and import new ones from a string
+ $.fn.importTags = function(str) {
+ id = $(this).attr('id');
+ $(jq(id)+'_tagsinput .tag').remove();
+ $.fn.tagsInput.importTags(this,str);
+ }
+
+ $.fn.tagsInput = function(options) {
+ var settings = jQuery.extend({
+ interactive:true,
+ defaultText:'add a tag',
+ minChars:0,
+ width:'300px',
+ height:'100px',
+ autocomplete: {selectFirst: false },
+ 'hide':true,
+ 'delimiter':',',
+ 'unique':true,
+ removeWithBackspace:true,
+ placeholderColor:'#666666',
+ autosize: true,
+ comfortZone: 20,
+ inputPadding: 6*2
+ },options);
+
+ this.each(function() {
+ if (settings.hide) {
+ $(this).hide();
+ }
+ var id = $(this).attr('id');
+ if (!id || delimiter[$(this).attr('id')]) {
+ id = $(this).attr('id', 'tags' + new Date().getTime()).attr('id');
+ }
+
+ var data = jQuery.extend({
+ pid:id,
+ real_input: jq(id),
+ holder: jq(id)+'_tagsinput',
+ input_wrapper: jq(id)+'_addTag',
+ fake_input: jq(id)+'_tag'
+ },settings);
+
+ delimiter[id] = data.delimiter;
+
+ if (settings.onAddTag || settings.onRemoveTag || settings.onChange) {
+ tags_callbacks[id] = new Array();
+ tags_callbacks[id]['onAddTag'] = settings.onAddTag;
+ tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag;
+ tags_callbacks[id]['onChange'] = settings.onChange;
+ }
+
+ var markup = '<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag">';
+
+ if (settings.interactive) {
+ markup = markup + '<input id="'+id+'_tag" value="" data-default="'+settings.defaultText+'" />';
+ }
+
+ markup = markup + '</div><div class="tags_clear"></div></div>';
+
+ $(markup).insertAfter(this);
+
+ $(data.holder).css('width',settings.width);
+ $(data.holder).css('min-height',settings.height);
+ $(data.holder).css('height','100%');
+
+ if ($(data.real_input).val()!='') {
+ $.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val());
+ }
+ if (settings.interactive) {
+ $(data.fake_input).val($(data.fake_input).attr('data-default'));
+ $(data.fake_input).css('color',settings.placeholderColor);
+ $(data.fake_input).resetAutosize(settings);
+
+ $(data.holder).bind('click',data,function(event) {
+ $(event.data.fake_input).focus();
+ });
+
+ $(data.fake_input).bind('focus',data,function(event) {
+ if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) {
+ $(event.data.fake_input).val('');
+ }
+ $(event.data.fake_input).css('color','#000000');
+ });
+
+ if (settings.autocomplete_url != undefined) {
+ autocomplete_options = {source: settings.autocomplete_url};
+ for (attrname in settings.autocomplete) {
+ autocomplete_options[attrname] = settings.autocomplete[attrname];
+ }
+
+ if (jQuery.Autocompleter !== undefined) {
+ $(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete);
+ $(data.fake_input).bind('result',data,function(event,data,formatted) {
+ if (data) {
+ $(jq(id)).addTag(data[0] + "",{focus:true,unique:(settings.unique)});
+ }
+ });
+ } else if (jQuery.ui.autocomplete !== undefined) {
+ $(data.fake_input).autocomplete(autocomplete_options);
+ $(data.fake_input).bind('autocompleteselect',data,function(event,ui) {
+ $(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)});
+ return false;
+ });
+ }
+
+
+ } else {
+ // if a user tabs out of the field, create a new tag
+ // this is only available if autocomplete is not used.
+ $(data.fake_input).bind('blur',data,function(event) {
+ var d = $(this).attr('data-default');
+ if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) {
+ if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
+ $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
+ } else {
+ $(event.data.fake_input).val($(event.data.fake_input).attr('data-default'));
+ $(event.data.fake_input).css('color',settings.placeholderColor);
+ }
+ return false;
+ });
+
+ }
+ // if user types a comma, create a new tag
+ $(data.fake_input).bind('keypress',data,function(event) {
+ if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13 ) {
+ event.preventDefault();
+ if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
+ $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
+ $(event.data.fake_input).resetAutosize(settings);
+ return false;
+ } else if (event.data.autosize) {
+ $(event.data.fake_input).doAutosize(settings);
+
+ }
+ });
+ //Delete last tag on backspace
+ data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event)
+ {
+ if(event.keyCode == 8 && $(this).val() == '')
+ {
+ event.preventDefault();
+ var last_tag = $(this).closest('.tagsinput').find('.tag:last').text();
+ var id = $(this).attr('id').replace(/_tag$/, '');
+ last_tag = last_tag.replace(/[\s]+x$/, '');
+ $(jq(id)).removeTag(escape(last_tag));
+ $(this).trigger('focus');
+ }
+ });
+ $(data.fake_input).blur();
+
+ //Removes the not_valid class when user changes the value of the fake input
+ if(data.unique) {
+ $(data.fake_input).keydown(function(event){
+ if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)) {
+ $(this).removeClass('not_valid');
+ }
+ });
+ }
+ } // if settings.interactive
+ });
+
+ return this;
+
+ };
+
+ $.fn.tagsInput.updateTagsField = function(obj,tagslist) {
+ var id = $(obj).attr('id');
+ $(obj).val(tagslist.join(delimiter[id]));
+ };
+
+ $.fn.tagsInput.importTags = function(obj,val) {
+ $(obj).val('');
+ var id = $(obj).attr('id');
+ var tags = val.split(delimiter[id]);
+ for (i=0; i<tags.length; i++) {
+ $(obj).addTag(tags[i],{focus:false,callback:false});
+ }
+ if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
+ {
+ var f = tags_callbacks[id]['onChange'];
+ f.call(obj, obj, tags[i]);
+ }
+ };
+
+})(jQuery);