From 9e8ffd1f74b7c3c7de0511694510f54cce85f1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 24 Feb 2016 16:27:35 +0000 Subject: [PATCH 01/48] When using jQuery-UI autocomplete, honour _renderItem extension point --- jquery.tag-editor.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index 048f538..3f1026b 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -181,6 +181,9 @@ ed.trigger('click', [$('.active', ed).find('input').closest('li').next('li').find('.tag-editor-tag')]); }, 20); }; input.autocomplete(aco); + if (aco._renderItem) { + input.autocomplete('instance')._renderItem = aco._renderItem; + } } } return false; From f93aeb5dfe7d1af262adf039a7ee7af2caaf3b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 24 Feb 2016 16:56:43 +0000 Subject: [PATCH 02/48] Allow \n to be used as a delimiter --- jquery.tag-editor.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index 3f1026b..f0953dd 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -190,8 +190,8 @@ }); // helper: split into multiple tags, e.g. after paste - function split_cleanup(input){ - var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), + function split_cleanup(input, text){ + var li = input.closest('li'), sub_tags = text.replace(/ +/, ' ').split(o.dregex), old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list for (var i=0; i Date: Fri, 6 May 2016 12:12:10 +0100 Subject: [PATCH 03/48] Delete support for removeDuplicates Using the public method addTag with a duplicate causes two empty tags to be inserted before the duplicate at the end. Support is removed until it works perfectly. --- jquery.tag-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index f0953dd..e8744b1 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -200,8 +200,8 @@ tag = cb_val || tag; if (cb_val === false || !tag) continue; // remove duplicates - if (o.removeDuplicates && ~$.inArray(tag, old_tags)) - $('.tag-editor-tag', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); + // if (o.removeDuplicates && ~$.inArray(tag, old_tags)) + // $('.tag-editor-tag', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); old_tags.push(tag); li.before('
  •  '+o.delimiter[0]+'
    '+escape(tag)+'
  • '); if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; } @@ -361,7 +361,7 @@ delimiter: ',;', placeholder: '', forceLowercase: true, - removeDuplicates: true, + // removeDuplicates: true, clickDelete: false, animateDelete: 175, sortable: true, // jQuery UI sortable From 11d4a966ce606e2db33718fb9b110f29e2ba6486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 11 Apr 2016 13:07:41 +0100 Subject: [PATCH 04/48] Add .gitignore to repository --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ca22b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +# Created by .ignore support plugin (hsz.mobi) From e451455d1aa550f8c166c65944b7da19bc70673a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 11 Apr 2016 13:08:53 +0100 Subject: [PATCH 05/48] Delete minified JS (out-of-sync with main) --- jquery.tag-editor.min.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 jquery.tag-editor.min.js diff --git a/jquery.tag-editor.min.js b/jquery.tag-editor.min.js deleted file mode 100644 index 1d083d2..0000000 --- a/jquery.tag-editor.min.js +++ /dev/null @@ -1,3 +0,0 @@ -// jQuery tagEditor v1.0.20 -// https://github.com/Pixabay/jQuery-tagEditor -!function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('
  •  '+l.delimiter[0]+'
  • ').appendTo(n).find(".tag-editor-tag").html('').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="BODY"==document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i
    '+o.placeholder+"
    ")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u
     '+o.delimiter[0]+'
    '+l(v)+'
    '),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("
      ').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('
    •  
    • ');var d='
    •  '+o.delimiter[0]+'
    • ';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageXa&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('
    •  '+o.delimiter[0]+'
      '+l(v)+'
    • '))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery); \ No newline at end of file From ae3123b43087b9ce28b17fc41b60d82947c74315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 18 Apr 2016 13:12:54 +0100 Subject: [PATCH 06/48] =?UTF-8?q?Support=20for=20cut=20is=20Windows-specif?= =?UTF-8?q?ic=20(ctrlKey):=20remove=20it=20as=20I=20don=E2=80=99t=20see=20?= =?UTF-8?q?a=20portable=20way=20to=20support=20it=20for=20OS=20X=20as=20we?= =?UTF-8?q?ll=20(metaKey);=20besides=20it=E2=80=99s=20ugly=20as=20it=20sho?= =?UTF-8?q?ws=20the=20hidden=20separator=20char?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jquery.tag-editor.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index e8744b1..66ab2cd 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -27,7 +27,7 @@ if (typeof options == 'string') { // depending on selector, response may contain tag lists of multiple editor instances var response = []; - selector.each(function(){ + selector.each(function() { // the editor is the next sibling to the hidden, original field var el = $(this), o = el.data('options'), ed = el.next('.tag-editor'); if (options == 'getTags') @@ -50,9 +50,9 @@ return options == 'getTags' ? response : this; } - // delete selected tags on backspace, delete, ctrl+x - if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e){ - if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) { + // delete selected tags on backspace, delete + if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e) { + if (e.which == 8 || e.which == 46) { try { var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; } catch(e){ el = 0; } @@ -67,7 +67,7 @@ } }); - return selector.each(function(){ + return selector.each(function() { var el = $(this), tag_list = []; // cache current tags // create editor (ed) instance @@ -153,7 +153,7 @@ // delete on right mouse click or ctrl+click if (o.clickDelete) - ed.on('mousedown', '.tag-editor-tag', function(e){ + ed.on('mousedown', '.tag-editor-tag', function(e) { if (e.ctrlKey || e.which > 1) { var li = $(this).closest('li'), tag = li.find('.tag-editor-tag'); if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false; @@ -163,7 +163,7 @@ } }); - ed.on('click', '.tag-editor-tag', function(e){ + ed.on('click', '.tag-editor-tag', function(e) { // delete on right click or ctrl+click -> exit if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false; @@ -190,7 +190,7 @@ }); // helper: split into multiple tags, e.g. after paste - function split_cleanup(input, text){ + function split_cleanup(input, text) { var li = input.closest('li'), sub_tags = text.replace(/ +/, ' ').split(o.dregex), old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list for (var i=0; i Date: Fri, 6 May 2016 16:53:53 +0100 Subject: [PATCH 07/48] Delete support for removeDuplicates Using the public method addTag with a duplicate causes two empty tags to be inserted before the duplicate at the end. Support is removed until it works perfectly. (cherry picked from commit 2bc18ea) --- jquery.tag-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index 3f1026b..e9f71b7 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -200,8 +200,8 @@ tag = cb_val || tag; if (cb_val === false || !tag) continue; // remove duplicates - if (o.removeDuplicates && ~$.inArray(tag, old_tags)) - $('.tag-editor-tag', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); + // if (o.removeDuplicates && ~$.inArray(tag, old_tags)) + // $('.tag-editor-tag', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); old_tags.push(tag); li.before('
    •  '+o.delimiter[0]+'
      '+escape(tag)+'
    • '); if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; } @@ -359,7 +359,7 @@ delimiter: ',;', placeholder: '', forceLowercase: true, - removeDuplicates: true, + // removeDuplicates: true, clickDelete: false, animateDelete: 175, sortable: true, // jQuery UI sortable From b49646df46c3f6a2f7df002c3d9c5b20f7f00f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 11 Apr 2016 13:07:41 +0100 Subject: [PATCH 08/48] Add .gitignore to repository (cherry picked from commit 11d4a96) --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ca22b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +# Created by .ignore support plugin (hsz.mobi) From 9cb02eee06fc7b76e1867be770256a612d8e3338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Fri, 6 May 2016 16:58:01 +0100 Subject: [PATCH 09/48] Delete minified JS (out-of-sync with main) (cherry picked from commit e451455) --- jquery.tag-editor.min.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 jquery.tag-editor.min.js diff --git a/jquery.tag-editor.min.js b/jquery.tag-editor.min.js deleted file mode 100644 index 1d083d2..0000000 --- a/jquery.tag-editor.min.js +++ /dev/null @@ -1,3 +0,0 @@ -// jQuery tagEditor v1.0.20 -// https://github.com/Pixabay/jQuery-tagEditor -!function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('
    •  '+l.delimiter[0]+'
    • ').appendTo(n).find(".tag-editor-tag").html('').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="BODY"==document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i
      '+o.placeholder+"
      ")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u
       '+o.delimiter[0]+'
      '+l(v)+'
      '),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("
        ').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('
      •  
      • ');var d='
      •  '+o.delimiter[0]+'
      • ';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageXa&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('
      •  '+o.delimiter[0]+'
        '+l(v)+'
      • '))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery); \ No newline at end of file From d9860153e88edd8534f83828f4b656b465431c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Fri, 6 May 2016 17:00:44 +0100 Subject: [PATCH 10/48] =?UTF-8?q?Support=20for=20cut=20is=20Windows-specif?= =?UTF-8?q?ic=20(ctrlKey):=20remove=20it=20as=20I=20don=E2=80=99t=20see=20?= =?UTF-8?q?a=20portable=20way=20to=20support=20it=20for=20OS=20X=20as=20we?= =?UTF-8?q?ll=20(metaKey);=20besides=20it=E2=80=99s=20ugly=20as=20it=20sho?= =?UTF-8?q?ws=20the=20hidden=20separator=20char=20(cherry=20picked=20from?= =?UTF-8?q?=20commit=20ae3123b)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jquery.tag-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index e9f71b7..0b10dda 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -50,9 +50,9 @@ return options == 'getTags' ? response : this; } - // delete selected tags on backspace, delete, ctrl+x - if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e){ - if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) { + // delete selected tags on backspace, delete + if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e) { + if (e.which == 8 || e.which == 46) { try { var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; } catch(e){ el = 0; } From 5098d5daef551ca391d648ecc1a77dc359da7766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Fri, 6 May 2016 18:11:23 +0100 Subject: [PATCH 11/48] =?UTF-8?q?Format=20code:=20terse=20code=20doesn?= =?UTF-8?q?=E2=80=99t=20make=20for=20very=20readable=20code...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jquery.tag-editor.js | 512 +++++++++++++++++++++++++++++++------------ 1 file changed, 368 insertions(+), 144 deletions(-) diff --git a/jquery.tag-editor.js b/jquery.tag-editor.js index 0b10dda..f7fe197 100644 --- a/jquery.tag-editor.js +++ b/jquery.tag-editor.js @@ -1,131 +1,221 @@ -/* - jQuery tagEditor v1.0.20 - Copyright (c) 2014 Simon Steinberger / Pixabay - GitHub: https://github.com/Pixabay/jQuery-tagEditor - License: http://www.opensource.org/licenses/mit-license.php -*/ +"use strict"; (function($){ // auto grow input (stackoverflow.com/questions/931207) - $.fn.tagEditorInput=function(){var t=" ",e=$(this),n=parseInt(e.css("fontSize")),i=$("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),s=function(){if(t!==(t=e.val())){i.text(t);var s=i.width()+n;20>s&&(s=20),s!=e.width()&&e.width(s)}};return i.insertAfter(e),e.bind("keyup keydown focus",s)}; + $.fn.tagEditorInput = function() { + var t = " ", + e = $(this), + n = parseInt(e.css("fontSize")), + i = $("").css({position: "absolute", top: -9999, left: -9999, width: "auto", fontSize: e.css("fontSize"), fontFamily: e.css("fontFamily"), fontWeight: e.css("fontWeight"), letterSpacing: e.css("letterSpacing"), whiteSpace: "nowrap"}), + s = function() { + if (t !== (t = e.val())) { + i.text(t); + var s = i.width()+n; + 20 > s && (s=20), + s != e.width() && e.width(s) + } + }; + return i.insertAfter(e), + e.bind("keyup keydown focus",s) + }; // plugin with val as parameter for public methods - $.fn.tagEditor = function(options, val, blur){ + $.fn.tagEditor = function(options, val, blur) { // helper function escape(tag) { - return tag.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + return tag.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); } // build options dictionary with default values - var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this; + var blur_result, + o = $.extend({}, $.fn.tagEditor.defaults, options), + selector = this; // store regex and default delimiter in options for later use - o.dregex = new RegExp('['+o.delimiter.replace('-', '\-')+']', 'g'); + o.dregex = new RegExp('[' + o.delimiter.replace('-', '\-') + ']', 'g'); // public methods if (typeof options == 'string') { // depending on selector, response may contain tag lists of multiple editor instances var response = []; - selector.each(function(){ + selector.each(function() { // the editor is the next sibling to the hidden, original field - var el = $(this), o = el.data('options'), ed = el.next('.tag-editor'); - if (options == 'getTags') + var el = $(this), + o = el.data('options'), + ed = el.next('.tag-editor'); + + if (options == 'getTags') { response.push({field: el[0], editor: ed, tags: ed.data('tags')}); + } else if (options == 'addTag') { - if (o.maxTags && ed.data('tags').length >= o.maxTags) return false; + if (o.maxTags && ed.data('tags').length >= o.maxTags) { + return false; + } + // insert new tag - $('
      •  '+o.delimiter[0]+'
      • ').appendTo(ed).find('.tag-editor-tag') - .html('').addClass('active').find('input').val(val).blur(); - if (!blur) ed.click(); - else $('.placeholder', ed).remove(); - } else if (options == 'removeTag') { + $('
      • ' + + '
         ' + o.delimiter[0] + '
        ' + + '
        ' + + '
        ' + + '
      • ') + .appendTo(ed).find('.tag-editor-tag') + .html('').addClass('active').find('input').val(val).blur(); + + if (!blur) { + ed.click(); + } + else { + $('.placeholder', ed).remove(); + } + } + else if (options == 'removeTag') { // trigger delete on matching tag, then click editor to create a new tag - $('.tag-editor-tag', ed).filter(function(){return $(this).text()==val;}).closest('li').find('.tag-editor-delete').click(); - if (!blur) ed.click(); - } else if (options == 'destroy') { + $('.tag-editor-tag', ed).filter(function() { + return $(this).text()==val; + }).closest('li').find('.tag-editor-delete').click(); + + if (!blur){ + ed.click(); + } + } + else if (options == 'destroy') { el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove(); } }); + return options == 'getTags' ? response : this; } // delete selected tags on backspace, delete - if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e) { - if (e.which == 8 || e.which == 46) { - try { - var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; - } catch(e){ el = 0; } - if (sel.rangeCount > 0 && el && el.length) { - var tags = [], splits = sel.toString().split(el.prev().data('options').dregex); - for (i=0; i 0 && el && el.length) { + var tags = [], + splits = sel.toString().split(el.prev().data('options').dregex); + + for (var i = 0 ; i < splits.length ; i++) { + var tag = $.trim(splits[i]); + if (tag) { + tags.push(tag); + } + } + $('.tag-editor-tag', el).each(function() { + if ($.inArray($(this).text(), tags) >= 0) { + $(this).closest('li').find('.tag-editor-delete').click(); + } + }); + + return false; + } } - } - }); + }); + } - return selector.each(function(){ - var el = $(this), tag_list = []; // cache current tags + // Create a tagEditor for each of the matched elements (usually an or a

        - getTags - addTag 'example' - removeTag 'example' + getTags + addTag 'example' + removeTag 'example' Remove all tags - destroy - + destroy +

         // actions on button clicks
         
         // getTags
        -alert( $('#demo3').tagEditor('getTags')[0].tags );
        +alert( $('#demo3').jsonTagEditor('getTags')[0].tags );
         
         // addTag
        -$('#demo3').tagEditor('addTag', 'example');
        +$('#demo3').jsonTagEditor('addTag', 'example');
         
         // removeTag
        -$('#demo3').tagEditor('removeTag', 'example');
        +$('#demo3').jsonTagEditor('removeTag', 'example');
         
         // Remove all tags
         function() {
        -    var tags = $('#demo3').tagEditor('getTags')[0].tags;
        -    for (i = 0; i < tags.length; i++) { $('#demo3').tagEditor('removeTag', tags[i]); }
        +    var tags = $('#demo3').jsonTagEditor('getTags')[0].tags;
        +    for (i = 0; i < tags.length; i++) { $('#demo3').jsonTagEditor('removeTag', tags[i]); }
         }
         // working shortcut for removing all tags
         // $('#demo3').next('.tag-editor').find('.tag-editor-delete').click();
         
         // destroy
        -$('#demo3').tagEditor('destroy');
        +$('#demo3').jsonTagEditor('destroy');
         
         // re-init editor
        -$('#demo3').tagEditor({ placeholder: 'Enter tags ...' });
        +$('#demo3').jsonTagEditor({ placeholder: 'Enter tags ...' });

        Callbacks

        -$('#demo4').tagEditor({
        +$('#demo4').jsonTagEditor({
             initialTags: ['Hello', 'World'],
             placeholder: 'Enter tags ...',
             onChange: function(field, editor, tags) {
        @@ -280,7 +279,7 @@ 

        Custom style and clickDelete

        Use right mouse click or Ctrl+left click to delete tags.

        -$('#demo5').tagEditor({
        +$('#demo5').jsonTagEditor({
             clickDelete: true,
             initialTags: [ ... ],
             placeholder: 'Enter tags ...'
        @@ -307,7 +306,7 @@ 

        Custom CSS classes for tags

        Using the onChange callback for adding custom CSS classes to specific tags.

        -$('#demo6').tagEditor({
        +$('#demo6').jsonTagEditor({
             initialTags: ['custom', 'class', 'red', 'green', 'demo'],
             onChange: tag_classes
         });
        @@ -322,7 +321,7 @@ 

        Custom CSS classes for tags

        } // first assign tag classes after initializing tagEditor; onChange is not called on init -tag_classes(null, $('#demo6').tagEditor('getTags')[0].editor);
        +tag_classes(null, $('#demo6').jsonTagEditor('getTags')[0].editor);

        In the onChange callback we iterate over all tags and assign custom CSS classes where appropriate. @@ -405,26 +404,26 @@

        Custom CSS classes for tags

        } $(function() { - $('#hero-demo').tagEditor({ + $('#hero-demo').jsonTagEditor({ placeholder: 'Enter tags ...', autocomplete: { source: googleSuggest, minLength: 3, delay: 250, html: true, position: { collision: 'flip' } } }); - $('#demo1').tagEditor({ initialTags: ['Hello', 'World', 'Example', 'Tags'], delimiter: ', ', placeholder: 'Enter tags ...' }).css('display', 'block').attr('readonly', true); + $('#demo1').jsonTagEditor({ initialTags: ['Hello', 'World', 'Example', 'Tags'], delimiter: ', ', placeholder: 'Enter tags ...' }).css('display', 'block').attr('readonly', true); - $('#demo2').tagEditor({ + $('#demo2').jsonTagEditor({ autocomplete: { delay: 0, position: { collision: 'flip' }, source: ['ActionScript', 'AppleScript', 'Asp', 'BASIC', 'C', 'C++', 'CSS', 'Clojure', 'COBOL', 'ColdFusion', 'Erlang', 'Fortran', 'Groovy', 'Haskell', 'HTML', 'Java', 'JavaScript', 'Lisp', 'Perl', 'PHP', 'Python', 'Ruby', 'Scala', 'Scheme'] }, forceLowercase: false, placeholder: 'Programming languages ...' }); - $('#demo3').tagEditor({ initialTags: ['Hello', 'World'], placeholder: 'Enter tags ...' }); + $('#demo3').jsonTagEditor({ initialTags: ['Hello', 'World'], placeholder: 'Enter tags ...' }); $('#remove_all_tags').click(function() { - var tags = $('#demo3').tagEditor('getTags')[0].tags; - for (i=0;i'+(tags.length ? tags.join(', ') : '----')+'
        '); }, @@ -437,7 +436,7 @@

        Custom CSS classes for tags

        } }); - $('#demo5').tagEditor({ clickDelete: true, initialTags: ['custom style', 'dark tags', 'delete on click', 'no delete icon', 'hello', 'world'], placeholder: 'Enter tags ...' }); + $('#demo5').jsonTagEditor({ clickDelete: true, initialTags: ['custom style', 'dark tags', 'delete on click', 'no delete icon', 'hello', 'world'], placeholder: 'Enter tags ...' }); function tag_classes(field, editor, tags) { $('li', editor).each(function(){ @@ -447,8 +446,8 @@

        Custom CSS classes for tags

        else li.removeClass('red-tag green-tag'); }); } - $('#demo6').tagEditor({ initialTags: ['custom', 'class', 'red', 'green', 'demo'], onChange: tag_classes }); - tag_classes(null, $('#demo6').tagEditor('getTags')[0].editor); // or editor == $('#demo6').next() + $('#demo6').jsonTagEditor({ initialTags: ['custom', 'class', 'red', 'green', 'demo'], onChange: tag_classes }); + tag_classes(null, $('#demo6').jsonTagEditor('getTags')[0].editor); // or editor == $('#demo6').next() }); if (~window.location.href.indexOf('http')) { diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index 92cfee3..9b7eb52 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -2,7 +2,7 @@ (function($){ // plugin with val as parameter for public methods - $.fn.tagEditor = function(options, val, blur) { + $.fn.jsonTagEditor = function(options, val, blur) { // helper function escape(tag) { @@ -25,13 +25,17 @@ return String(tag).trim().length > 0 ? {tagValue: String(tag).trim()} : false; } + function ellipsify(str, maxLength) { + return str.length > maxLength ? str.substring(0, maxLength - 1) + "…" : str; + } + function deepEquals(arr1, arr2) { return $(arr1).not(arr2).length === 0 && $(arr2).not(arr1).length === 0; } // build options dictionary with default values var blur_result, - o = $.extend({}, $.fn.tagEditor.defaults, options), + o = $.extend({}, $.fn.jsonTagEditor.defaults, options), selector = this; // store regex and default delimiter in options for later use @@ -67,7 +71,7 @@ } } - // The will be replaced with its value in L412 if it has no delimiters or in L341 if it does + // The will be removed and its label value placed inside the tag-editor-tag
        after calling blur() $('
      • ') .append('
         ' + o.delimiter[0] + '
        ') .append('
        ') @@ -361,10 +365,12 @@ continue; } - var escapedTag = escape(tag); - var $tagEditorTag = $('
        ' + escapedTag + '
        '); + var $tagEditorTag = + $('
        o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + + '
        '); Object.assign($tagEditorTag.get(0).dataset, input.get(0).dataset); - $tagEditorTag.get(0).dataset.tagValue = escapedTag; + $tagEditorTag.get(0).dataset.tagValue = escape(tag); old_tags.push(tag); li.before( @@ -430,12 +436,14 @@ // Replace with its escaped value. E.g.: //
        -->
        tag < text
        - var escapedTag = escape(tag), - $tagEditorTag = input.parent(); + var $tagEditorTag = input.parent(); Object.assign($tagEditorTag.get(0).dataset, input.get(0).dataset); - $tagEditorTag.get(0).dataset.tagValue = escapedTag; - $tagEditorTag.html(escape(tag)).removeClass('active'); + $tagEditorTag.get(0).dataset.tagValue = escape(tag); + if (tag.length > o.maxTagLength) { + $tagEditorTag.attr('title', escape(tag)); + } + $tagEditorTag.html(escape(ellipsify(tag, o.maxTagLength))).removeClass('active'); if (tag != old_tag) { update_globals(); @@ -588,7 +596,8 @@ ed.append( '
      • ' + '
         ' + o.delimiter[0] + '
        ' + - '
        ' + escape(tag) + '
        ' + + '
        o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + '
        ' + '
        ' + '
      • '); } @@ -606,10 +615,11 @@ }); }; - $.fn.tagEditor.defaults = { + $.fn.jsonTagEditor.defaults = { initialTags: [], maxTags: 0, maxLength: 50, + maxTagLength: 100, delimiter: ',;', placeholder: '', forceLowercase: true, From 7297d35667936d1b684a24d723c186c90419cccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 11 May 2016 17:54:13 +0100 Subject: [PATCH 24/48] Add documentation --- readme.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8990f89..3256425 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,31 @@ +jQuery JSON Tag Editor +====================== + +jQuery JSON Tag Editor is a fork of [jQuery-tagEditor](https://goodies.pixabay.com/jquery/tag-editor/demo.html), originally developed by Simon Steinberger for +[Pixabay.com](https://pixabay.com) and licensed under the [MIT license](https://opensource.org/licenses/MIT). This fork maintains the same license. + +*jQuery JSON Tag Editor* adds a separation between the visual representation of a tag and the tag itself. Tags are plain JavaScript objects with a `tagValue` +that is its visual representation in the tag editor. The `tagValue` property plus any others that the tag object may have are all stored as +[data attributes](https://developer.mozilla.org/en/docs/Web/Guide/HTML/Using_data_attributes) in each `<div class="tag-editor-tag">` element. + +So, using the `addTag` public method, the following: +``` +$('#id').jsonTagEditor('addTag', {tagValue: 'Car', tagType: 'string', category: 'vehicle'} +``` + +Is turned into the following markup: +``` +
        Car
        +``` + +If only a string is provided as an argument then an object with only the `tagValue` property is created. This is the case with entering tags manually into the editor. + +What follows is the original contents of jQuery-tagEditor’s README.md file: + jQuery-tagEditor ================ -A powerful and lightweight tag editor plugin for jQuery. +A powerful and lightweight tag editor plugin for jQuery based on jQuery-tagEditor by Pixabay Compatible with jQuery 1.7.0+ in Firefox, Safari, Chrome, Opera, Internet Explorer 8+. IE7 technically works, but no care has gone into CSS/layout bugs. Released under the MIT License: http://www.opensource.org/licenses/mit-license.php From 4d3aa05df8037e19f8a178add3b5f073148b7491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer=20Fuentes?= Date: Wed, 11 May 2016 18:12:30 +0100 Subject: [PATCH 25/48] Rename readme.md to README.md --- readme.md => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename readme.md => README.md (100%) diff --git a/readme.md b/README.md similarity index 100% rename from readme.md rename to README.md From 23a043a6d191db0c0333a67206e16b4015574750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 16 May 2016 17:24:18 +0100 Subject: [PATCH 26/48] Separate tags in #hero-demo using tabs instead of commas --- demo.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo.html b/demo.html index 31ed97f..2d38ee2 100644 --- a/demo.html +++ b/demo.html @@ -82,7 +82,8 @@

        A powerful and lightweight tag editor plugin for jQuery based on
        - +

        From 967103aea28096155b3f7036f8a424b97dd1f230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 16 May 2016 17:25:06 +0100 Subject: [PATCH 27/48] Rewrite some documentation and add info about hard-coded delimiters --- README.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3256425..562bb2e 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,41 @@ jQuery JSON Tag Editor ====================== -jQuery JSON Tag Editor is a fork of [jQuery-tagEditor](https://goodies.pixabay.com/jquery/tag-editor/demo.html), originally developed by Simon Steinberger for +*jQuery JSON Tag Editor* is a fork of [jQuery-tagEditor](https://goodies.pixabay.com/jquery/tag-editor/demo.html), originally developed by Simon Steinberger for [Pixabay.com](https://pixabay.com) and licensed under the [MIT license](https://opensource.org/licenses/MIT). This fork maintains the same license. *jQuery JSON Tag Editor* adds a separation between the visual representation of a tag and the tag itself. Tags are plain JavaScript objects with a `tagValue` -that is its visual representation in the tag editor. The `tagValue` property plus any others that the tag object may have are all stored as -[data attributes](https://developer.mozilla.org/en/docs/Web/Guide/HTML/Using_data_attributes) in each `<div class="tag-editor-tag">` element. +that is its visual representation in the tag editor. `tagValue` plus any other properties that the tag object may have are all stored as +[data attributes](https://developer.mozilla.org/en/docs/Web/Guide/HTML/Using_data_attributes) in each `
        ` element. -So, using the `addTag` public method, the following: +So, using the `addTag` public method, this: ``` -$('#id').jsonTagEditor('addTag', {tagValue: 'Car', tagType: 'string', category: 'vehicle'} +$('#id').jsonTagEditor('addTag', '{"tagValue": "Car", "tagType": "string", "category": "vehicle"}') ``` -Is turned into the following markup: +Produces the markup below: ```
        Car
        ``` -If only a string is provided as an argument then an object with only the `tagValue` property is created. This is the case with entering tags manually into the editor. - -What follows is the original contents of jQuery-tagEditor’s README.md file: +If a string is provided as an argument then an object with only the `tagValue` property is created. This is the case when tags are manually typed into the editor. + +Features added: +--------------- +* Tags are complex objects: what you see in the editor is the property `tagValue`, but you can add arbitrary data to your tags +* Tag object properties are stored as data attributes of their corresponding `
        ` element +* A new option `maxTagLength` can trim and ellipsify tags while keeping the original value in `data-tag-value` + +Features removed: +----------------- +* Auto-grow width of `` editors +* Support for cut: the original feature was Window-specific but buggy as tags weren’t put in the clipboard +* `removeDuplicates`: as tags aren’t strings it was the simplest way to deal with this issue +* The `delimiter` is hard-coded to `\t` and `\n` + +--- + +What follows are the original contents of jQuery-tagEditor’s README.md file: jQuery-tagEditor ================ From 9ba0bc1f3a0c458a9597dbc49e6f9108bd0712d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 16 May 2016 17:26:19 +0100 Subject: [PATCH 28/48] Delimiter hard-coded to \t and \n; paste now works with multi-line work; input is parsed as JSON only (no objects are accepted anymore) --- jquery.json-tag-editor.js | 143 ++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 67 deletions(-) diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index 9b7eb52..85680fb 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -4,6 +4,8 @@ // plugin with val as parameter for public methods $.fn.jsonTagEditor = function(options, val, blur) { + var delimiter = '\t\n'; + // helper function escape(tag) { return tag.replace(/&/g, '&') @@ -15,7 +17,7 @@ function validate(tag) { try { - var parsedTag = typeof tag === 'string' ? JSON.parse(tag) : tag; + var parsedTag = JSON.parse(tag); if (parsedTag.hasOwnProperty('tagValue') && typeof parsedTag.tagValue === 'string' && parsedTag.tagValue.trim().length > 0) { return parsedTag; @@ -39,7 +41,7 @@ selector = this; // store regex and default delimiter in options for later use - o.dregex = new RegExp('[' + o.delimiter.replace('-', '\-') + ']', 'g'); + o.dregex = new RegExp('[' + delimiter.replace('-', '\-') + ']', 'g'); // public methods if (typeof options === 'string') { @@ -59,34 +61,23 @@ return false; } - var validTag = validate(val); - if (validTag) { - var $input = $(''), - tagKeys = Object.keys(validTag); - - // Populate with the tag data - for (var i = 0 ; i < tagKeys.length ; i++) { - if (tagKeys[i] !== 'tagValue') { - $input[0].dataset[tagKeys[i]] = validTag[tagKeys[i]]; - } - } - - // The will be removed and its label value placed inside the tag-editor-tag
        after calling blur() - $('
      • ') - .append('
         ' + o.delimiter[0] + '
        ') - .append('
        ') - .append('
        ') - .appendTo(ed).find('.tag-editor-tag') - .append($input).addClass('active').find('input').val(validTag.tagValue).blur(); + // The will be removed and its label value placed inside the tag-editor-tag
        after calling blur() + $('
      • ') + .append('
         ' + delimiter[0] + '
        ') + .append('
        ') + .append('
        ') + .appendTo(ed).find('.tag-editor-tag') + .append('') + .addClass('active').find('input').val(val).blur(); - if (!blur) { - ed.click(); - } - else { - $('.placeholder', ed).remove(); - } + if (!blur) { + ed.click(); + } + else { + $('.placeholder', ed).remove(); } + } else if (options === 'removeTag') { // Trigger delete on matching tag, then click editor to create a new tag @@ -158,7 +149,7 @@ // Markup for new tag var new_tag = '
      • ' + - '
         ' + o.delimiter[0] + '
        ' + + '
         ' + delimiter[0] + '
        ' + '
        ' + '
        ' + '
      • '; @@ -189,7 +180,7 @@ }).get(); ed.data('tags', tagList); - el.val(tagList.reduce(function(previous, current) {return previous + o.delimiter[0] + current.tagValue}, '')); + el.val(tagList.reduce(function(previous, current) {return previous + delimiter[0] + current.tagValue}, '')); // Change callback except for plugin init if (!init) { @@ -344,9 +335,9 @@ }); // helper: split into multiple tags, e.g. after paste - function split_cleanup(input) { + function split_cleanup(input, text) { var li = input.closest('li'), - sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), + sub_tags = text ? text.replace(/ +/, ' ').split(o.dregex) : input.val().replace(/ +/, ' ').split(o.dregex), old_tag = input.data('old_tag'), old_tags = tagList.slice(0), exceeded = false, @@ -365,24 +356,35 @@ continue; } - var $tagEditorTag = - $('
        o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + - '
        '); - Object.assign($tagEditorTag.get(0).dataset, input.get(0).dataset); - $tagEditorTag.get(0).dataset.tagValue = escape(tag); - - old_tags.push(tag); - li.before( - $('
      • ') - .append('
         ' + o.delimiter[0] + '
        ') - .append($tagEditorTag) - .append('
        ') - ); - - if (o.maxTags && old_tags.length >= o.maxTags) { - exceeded = true; - break; + var tagObject = validate(tag); + + if (tagObject) { + var $tagEditorTag = + $('
        o.maxTagLength ? ' title="' + escape(tagObject.tagValue) + '"' : '') + '>' + escape(ellipsify(tagObject.tagValue, o.maxTagLength)) + + '
        '); + + var tagProperties = Object.keys(tagObject); + for (var j = 0 ; j < tagProperties.length ; j++) { + $tagEditorTag.get(0).dataset[tagProperties[j]] = tagObject[tagProperties[j]]; + } + + + // Object.assign($tagEditorTag.get(0).dataset, input.get(0).dataset); + // $tagEditorTag.get(0).dataset.tagValue = escape(tag); + + old_tags.push(tagObject); + li.before( + $('
      • ') + .append('
         ' + delimiter[0] + '
        ') + .append($tagEditorTag) + .append('
        ') + ); + + if (o.maxTags && old_tags.length >= o.maxTags) { + exceeded = true; + break; + } } } @@ -396,7 +398,7 @@ var input = $(this), old_tag = input.data('old_tag'), - tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0])); + tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, delimiter[0])); if (!tag) { if (old_tag && o.beforeTagDelete(el, ed, tagList, old_tag) === false) { @@ -408,7 +410,7 @@ try { input.closest('li').remove(); } catch(e){} if (old_tag) update_globals(); } - else if (tag.indexOf(o.delimiter[0]) >= 0) { + else if (tag.indexOf(delimiter[0]) >= 0) { split_cleanup(input); return; } @@ -436,14 +438,22 @@ // Replace with its escaped value. E.g.: //
        -->
        tag < text
        - var $tagEditorTag = input.parent(); + var $tagEditorTag = input.parent(), + tagObject = validate(tag); + + if (tagObject) { + var tagProperties = Object.keys(tagObject); + for (var i = 0 ; i < tagProperties.length ; i++) { + $tagEditorTag.get(0).dataset[tagProperties[i]] = tagObject[tagProperties[i]]; + } - Object.assign($tagEditorTag.get(0).dataset, input.get(0).dataset); - $tagEditorTag.get(0).dataset.tagValue = escape(tag); - if (tag.length > o.maxTagLength) { - $tagEditorTag.attr('title', escape(tag)); + if (tagObject.tagValue.length > o.maxTagLength) { + $tagEditorTag.attr('title', escape(tagObject.tagValue)); + } else { + $tagEditorTag.removeAttr('title'); + } + $tagEditorTag.html(escape(ellipsify(tagObject.tagValue, o.maxTagLength))).removeClass('active'); } - $tagEditorTag.html(escape(ellipsify(tag, o.maxTagLength))).removeClass('active'); if (tag != old_tag) { update_globals(); @@ -452,20 +462,20 @@ set_placeholder(); }); - var pasted_content; ed.on('paste', 'input', function(e) { + var pastedContent, inputContent; $(this).removeAttr('maxlength'); - pasted_content = $(this); + pastedContent = (e.originalEvent || e).clipboardData.getData('text/plain'); + inputContent = $(this); setTimeout(function() { - split_cleanup(pasted_content); + split_cleanup(inputContent, pastedContent); }, 30); }); // keypress delimiter - var inp; ed.on('keypress', 'input', function(e) { - if (o.delimiter.indexOf(String.fromCharCode(e.which)) >= 0) { - inp = $(this); + if (delimiter.indexOf(String.fromCharCode(e.which)) >= 0) { + var inp = $(this); setTimeout(function() { split_cleanup(inp); }, 20); @@ -595,7 +605,7 @@ tagList.push(tag); ed.append( '
      • ' + - '
         ' + o.delimiter[0] + '
        ' + + '
         ' + delimiter[0] + '
        ' + '
        o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + '
        ' + '
        ' + @@ -619,10 +629,9 @@ initialTags: [], maxTags: 0, maxLength: 50, - maxTagLength: 100, - delimiter: ',;', + maxTagLength: 20, placeholder: '', - forceLowercase: true, + forceLowercase: false, clickDelete: false, animateDelete: 175, sortable: true, // jQuery UI sortable From eea846422803089a4e4ca806bc2936eb79b0cbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Tue, 17 May 2016 12:40:10 +0100 Subject: [PATCH 29/48] =?UTF-8?q?Clean=20up=20code;=20prefix=20=E2=80=9Cta?= =?UTF-8?q?g-editor=E2=80=9D=20classes=20with=20=E2=80=9Cjson-=E2=80=9D=20?= =?UTF-8?q?to=20avoid=20clashes=20with=20the=20regular=20tag=20editor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jquery.json-tag-editor.js | 254 +++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 124 deletions(-) diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index 85680fb..e9b25a0 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -1,12 +1,9 @@ "use strict"; (function($){ - // plugin with val as parameter for public methods - $.fn.jsonTagEditor = function(options, val, blur) { - var delimiter = '\t\n'; + $.fn.jsonTagEditor = function(options, val, blur) { - // helper function escape(tag) { return tag.replace(/&/g, '&') .replace(/= o.maxTags) { - return false; - } + //switch (options) {} - // The will be removed and its label value placed inside the tag-editor-tag
        after calling blur() - $('
      • ') - .append('
         ' + delimiter[0] + '
        ') - .append('
        ') - .append('
        ') - .appendTo(ed).find('.tag-editor-tag') - .append('') - .addClass('active').find('input').val(val).blur(); + switch (options) { + case 'getTags': + response.push({field: el[0], editor: ed, tags: ed.data('tags')}); + break; + case 'addTag': + if (o.maxTags && ed.data('tags').length >= o.maxTags) { + return false; + } - if (!blur) { - ed.click(); - } - else { - $('.placeholder', ed).remove(); - } - - } - else if (options === 'removeTag') { - // Trigger delete on matching tag, then click editor to create a new tag - $('.tag-editor-tag', ed).filter(function() { - return $(this).get(0).dataset.tagValue === val; - }).closest('li').find('.tag-editor-delete').click(); + // The tag is placed in an which will be removed, and its value placed inside the json-tag-editor-tag
        , after calling blur() + $('
      • ') + .append('
         ' + o.delimiter[0] + '
        ') + .append('
        ') + .append('
        ') + .appendTo(ed).find('.json-tag-editor-tag') + .append('') + .addClass('active').find('input').val(val).blur(); - if (!blur){ - ed.click(); - } - } - else if (options === 'destroy') { - el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove(); + if (!blur) { + ed.click(); + } else { + // setPlaceHolder() isn’t declared in this context, so .placeholder is removed explicitly + $('.placeholder', ed).remove(); + } + break; + + case 'removeTag': + // Trigger delete on matching tag, then click editor to create a new tag + $('.json-tag-editor-tag', ed).filter(function() { + return $(this).get(0).dataset.tagValue === val; + }).closest('li').find('.json-tag-editor-delete').click(); + + if (!blur){ + ed.click(); + } + break; + + case 'destroy': + el.removeClass('json-tag-editor-hidden-src').removeData('options').off('focus.json-tag-editor').next('.json-tag-editor').remove(); + break; + + default: + return this; } }); return options === 'getTags' ? response : this; } + // End of public methods - // delete selected tags on backspace, delete + + // Delete selected tags on backspace, delete if (window.getSelection) { - $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e) { + $(document).off('keydown.json-tag-editor').on('keydown.json-tag-editor', function(e) { if (e.which === 8 || e.which === 46) { try { var sel = getSelection(), - el = document.activeElement.tagName === 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; + el = document.activeElement.tagName === 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.json-tag-editor') : 0; } catch(e) { el = 0; @@ -119,9 +129,9 @@ tags.push(tag); } } - $('.tag-editor-tag', el).each(function() { + $('.json-tag-editor-tag', el).each(function() { if ($.inArray($(this).text(), tags) >= 0) { - $(this).closest('li').find('.tag-editor-delete').click(); + $(this).closest('li').find('.json-tag-editor-delete').click(); } }); @@ -137,11 +147,11 @@ tagList = []; // Cache current tags // Create editor (ed) instance: el -> $ + example tags sortable autocomplete edit in place tab/cursor navigation duplicate check callbacks copy-paste placeholder public methods graceful degradation
        @@ -102,7 +102,6 @@

        Overview and Features

      • Optional jQuery UI autocomplete
      • Copy-paste or delete multiple selected tags
      • Duplicate tags check
      • -
      • Custom delimiter/s
      • Placeholder
      • Custom style for faulty tags
      • Public methods for reading, adding and removing tags + destroy function
      • @@ -141,15 +140,6 @@

        Settings

        initialTags[]Initial tags as an array of strings. maxTagsnullMaximum number of allowed tags. maxLength50maxlength attribute of the tag input field. - - delimiter',;' - -

        - Required string of delimiters - characters for separating tags. - The first character is used as default delimiter in the (hidden) original field. -

        - - placeholder''Placeholder text for empty tag editor. forceLowercasetrueLowercase all tags. removeDuplicatestrueAutomatically remove duplicate tags. @@ -181,7 +171,6 @@

        Basic settings

         $('#demo1').jsonTagEditor({
             initialTags: ['Hello', 'World', 'Example', 'Tags'],
        -    delimiter: ', ', /* space and comma */
             placeholder: 'Enter tags ...'
         });
        @@ -410,7 +399,7 @@

        Custom CSS classes for tags

        autocomplete: { source: googleSuggest, minLength: 3, delay: 250, html: true, position: { collision: 'flip' } } }); - $('#demo1').jsonTagEditor({ initialTags: ['Hello', 'World', 'Example', 'Tags'], delimiter: ', ', placeholder: 'Enter tags ...' }).css('display', 'block').attr('readonly', true); + $('#demo1').jsonTagEditor({ initialTags: ['Hello', 'World', 'Example', 'Tags'], placeholder: 'Enter tags ...' }).css('display', 'block').attr('readonly', true); $('#demo2').jsonTagEditor({ autocomplete: { delay: 0, position: { collision: 'flip' }, source: ['ActionScript', 'AppleScript', 'Asp', 'BASIC', 'C', 'C++', 'CSS', 'Clojure', 'COBOL', 'ColdFusion', 'Erlang', 'Fortran', 'Groovy', 'Haskell', 'HTML', 'Java', 'JavaScript', 'Lisp', 'Perl', 'PHP', 'Python', 'Ruby', 'Scala', 'Scheme'] }, From 71ccceb2f8819742904e91a24916f88aa0884c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 18 May 2016 12:34:02 +0100 Subject: [PATCH 34/48] =?UTF-8?q?When=20editing=20an=20ellipsified=20tag?= =?UTF-8?q?=20don=E2=80=99t=20add=20an=20empty=20tag,=20as=20the=20new=20t?= =?UTF-8?q?ag=20isn=E2=80=99t=20there=20yet=20and=20we=20can=E2=80=99t=20r?= =?UTF-8?q?etrieve=20the=20data=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jquery.json-tag-editor.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index bef94c2..cad51b4 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -128,7 +128,7 @@ }); return false; } - + } }); } @@ -196,7 +196,7 @@ } // ed -> $
          - // closest_tag is only used when autocomplete is wired to the tag editor, L325: ed.trigger('click', ... + // closestTag is only used when autocomplete is wired to the tag editor, below, at ed.trigger('click', ... ed.click(function(e, closestTag) { var d, dist = 99999, @@ -307,10 +307,12 @@ } if (!$(this).hasClass('active')) { - var tagValue = $(this).text(); + // If this is an existing tag get the data- attribute (e.g. expand an ellipsified tag value), otherwise get the text (e.g. it’s being edited) + var tagValue = $(this).get(0).dataset.tagValue ? $(this).get(0).dataset.tagValue : $(this).text(), + tagDisplay = $(this).text(); // Guess cursor position in text input var leftPercent = Math.abs(($(this).offset().left - e.pageX) / $(this).width()), - caretPos = parseInt(tagValue.length * leftPercent), + caretPos = parseInt(tagDisplay.length * leftPercent), input = $(this).html('').addClass('active').find('input'); input.data('old_tag', tagValue).focus().caret(caretPos); @@ -485,11 +487,13 @@ }); ed.on('keydown', 'input', function(e) { - var $this = $(this); + var $this = $(this), + previousTag, + nextTag; // left/up key + backspace key on empty field if ((e.which === 37 || !o.autocomplete && e.which === 38) && !$this.caret() || e.which === 8 && !$this.val()) { - var previousTag = $this.closest('li').prev('li').find('.json-tag-editor-tag'); + previousTag = $this.closest('li').prev('li').find('.json-tag-editor-tag'); if (previousTag.length) { previousTag.click().find('input').caret(-1); @@ -502,7 +506,7 @@ } // right/down key else if ((e.which === 39 || !o.autocomplete && e.which === 40) && ($this.caret() === $this.val().length)) { - var nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); + nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); if (nextTag.length) { nextTag.click().find('input').caret(0); @@ -517,7 +521,7 @@ else if (e.which === 9) { // shift+tab if (e.shiftKey) { - var previousTag = $this.closest('li').prev('li').find('.json-tag-editor-tag'); + previousTag = $this.closest('li').prev('li').find('.json-tag-editor-tag'); if (previousTag.length) { previousTag.click().find('input').caret(0); @@ -537,7 +541,7 @@ // tab } else { - var nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); + nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); if (nextTag.length) { nextTag.click().find('input').caret(0); @@ -553,7 +557,7 @@ } // del key else if (e.which === 46 && (!$.trim($this.val()) || ($this.caret() === $this.val().length))) { - var nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); + nextTag = $this.closest('li').next('li').find('.json-tag-editor-tag'); if (nextTag.length) { nextTag.click().find('input').caret(0); From 88d5193124f53e9efa43b13ca2328e45bf8f99d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 18 May 2016 12:42:28 +0100 Subject: [PATCH 35/48] Multi-line paste feature --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 562bb2e..a082deb 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,15 @@ If a string is provided as an argument then an object with only the `tagValue` p Features added: --------------- * Tags are complex objects: what you see in the editor is the property `tagValue`, but you can add arbitrary data to your tags -* Tag object properties are stored as data attributes of their corresponding `
          ` element +* Tag object properties are stored as data attributes in their corresponding `
          ` element * A new option `maxTagLength` can trim and ellipsify tags while keeping the original value in `data-tag-value` +* Multiple tags when pasting a multi-line text snippet, one per line Features removed: ----------------- * Auto-grow width of `` editors * Support for cut: the original feature was Window-specific but buggy as tags weren’t put in the clipboard -* `removeDuplicates`: as tags aren’t strings it was the simplest way to deal with this issue +* `removeDuplicates`: because tags aren’t strings it was the simplest way to deal with this issue * The `delimiter` is hard-coded to `\t` and `\n` --- From 18d11c8faf503a960cf93dbd19579a3187b9ea0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Wed, 18 May 2016 12:46:58 +0100 Subject: [PATCH 36/48] Add minified version --- jquery.json-tag-editor.min.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 jquery.json-tag-editor.min.js diff --git a/jquery.json-tag-editor.min.js b/jquery.json-tag-editor.min.js new file mode 100644 index 0000000..5473e82 --- /dev/null +++ b/jquery.json-tag-editor.min.js @@ -0,0 +1 @@ +"use strict";(function($){$.fn.jsonTagEditor=function(options,val,blur){function escape(tag){return tag.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function validate(tag){try{var parsedTag=JSON.parse(tag);if(parsedTag.hasOwnProperty("tagValue")&&typeof parsedTag.tagValue==="string"&&parsedTag.tagValue.trim().length>0){return parsedTag}}catch(e){}return String(tag).trim().length>0?{tagValue:String(tag).trim()}:false}function ellipsify(str,maxLength){return str.length>maxLength?str.substring(0,maxLength-1)+"…":str}function deepEquals(arr1,arr2){return $(arr1).not(arr2).length===0&&$(arr2).not(arr1).length===0}var blurResult,o=$.extend({},$.fn.jsonTagEditor.defaults,options),selector=this;o.delimiter=" \n";o.dregex=new RegExp("["+o.delimiter+"]","g");if(typeof options==="string"){var response=[];selector.each(function(){var el=$(this),o=el.data("options"),ed=el.next(".json-tag-editor");switch(options){case"getTags":response.push({field:el[0],editor:ed,tags:ed.data("tags")});break;case"addTag":if(o.maxTags&&ed.data("tags").length>=o.maxTags){return false}$("
        • ").append('
           '+o.delimiter[0]+"
          ").append('
          ').append('
          ').appendTo(ed).find(".json-tag-editor-tag").append('').addClass("active").find("input").val(val).blur();if(!blur){ed.click()}else{$(".placeholder",ed).remove()}break;case"removeTag":$(".json-tag-editor-tag",ed).filter(function(){return $(this).get(0).dataset.tagValue===val}).closest("li").find(".json-tag-editor-delete").click();if(!blur){ed.click()}break;case"destroy":el.removeClass("json-tag-editor-hidden-src").removeData("options").off("focus.json-tag-editor").next(".json-tag-editor").remove();break;default:return this}});return options==="getTags"?response:this}if(window.getSelection){$(document).off("keydown.json-tag-editor").on("keydown.json-tag-editor",function(e){if(e.which===8||e.which===46){try{var sel=getSelection(),el=document.activeElement.tagName==="BODY"?$(sel.getRangeAt(0).startContainer.parentNode).closest(".json-tag-editor"):0}catch(e){el=0}if(sel.rangeCount>0&&el&&el.length){$(".json-tag-editor-tag",el).each(function(){if(sel.containsNode($(this).get(0))){$(this).closest("li").find(".json-tag-editor-delete").click()}});return false}}})}return selector.each(function(){var el=$(this),tagList=[];var ed=$("
            ').insertAfter(el);el.addClass("json-tag-editor-hidden-src").data("options",o).on("focus.json-tag-editor",function(){ed.click()});ed.append('
          •  
          • ');var newTag="
          • "+'
             '+o.delimiter[0]+"
            "+'
            '+'
            '+"
          • ";function setPlaceholder(){if(o.placeholder&&!tagList.length&&!$(".deleted, .placeholder, input",ed).length){ed.append('
          • '+o.placeholder+"
          • ")}}function updateGlobals(init){var oldTags=tagList;tagList=$(".json-tag-editor-tag:not(.deleted)",ed).map(function(i,e){var tag={};if($(this).hasClass("active")){Object.assign(tag,$(this).find("input").get(0).dataset);tag.tagValue=$(this).find("input").val()}else{Object.assign(tag,$(e).get(0).dataset)}if(tag.tagValue){return tag}}).get();ed.data("tags",tagList);el.val(tagList.reduce(function(previous,current){return previous+o.delimiter[0]+current.tagValue},""));if(!init){if(!deepEquals(oldTags,tagList)){o.onChange(el,ed,tagList)}}setPlaceholder()}ed.click(function(e,closestTag){var d,dist=99999,loc;if(window.getSelection&&getSelection().toString()!==""){return}if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur();return false}blurResult=true;$("input:focus",ed).blur();if(!blurResult){return false}blurResult=true;$(".placeholder",ed).remove();if(closestTag&&closestTag.length){loc="before"}else{$(".json-tag-editor-tag",ed).each(function(){var tag=$(this),to=tag.offset(),tagX=to.left,tagY=to.top;if(e.pageY>=tagY&&e.pageY<=tagY+tag.height()){if(e.pageX1){var li=$(this).closest("li"),tag=li.find(".json-tag-editor-tag");if(o.beforeTagDelete(el,ed,tagList,tag.text())===false){return false}tag.addClass("deleted").animate({width:0},o.animateDelete,function(){li.remove();setPlaceholder()});updateGlobals();return false}})}ed.on("click",".json-tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1)){return false}if(!$(this).hasClass("active")){var tagValue=$(this).get(0).dataset.tagValue?$(this).get(0).dataset.tagValue:$(this).text(),tagDisplay=$(this).text();var leftPercent=Math.abs(($(this).offset().left-e.pageX)/$(this).width()),caretPos=parseInt(tagDisplay.length*leftPercent),input=$(this).html('').addClass("active").find("input");input.data("old_tag",tagValue).focus().caret(caretPos);if(o.autocomplete){var aco=$.extend({},o.autocomplete);var acSelect="select"in aco?o.autocomplete.select:"";aco.select=function(e,ui){if(acSelect){acSelect(e,ui)}setTimeout(function(){ed.trigger("click",[$(".active",ed).find("input").closest("li").next("li").find(".json-tag-editor-tag")])},20)};input.autocomplete(aco);if(aco._renderItem){input.autocomplete("instance")._renderItem=aco._renderItem}}}return false});function splitCleanup(input,text){var li=input.closest("li"),subTags=text?text.replace(/ +/," ").split(o.dregex):input.val().replace(/ +/," ").split(o.dregex),oldTag=input.data("old_tag"),oldTags=tagList.slice(0),exceeded=false,cbVal;for(var i=0;io.maxTagLength?' title="'+escape(tagObject.tagValue)+'"':"")+">"+escape(ellipsify(tagObject.tagValue,o.maxTagLength))+"
            ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
             '+o.delimiter[0]+"
            ").append($tagEditorTag).append('
            '));if(o.maxTags&&oldTags.length>=o.maxTags){exceeded=true;break}}}input.closest("li").remove();updateGlobals()}ed.on("blur","input",function(e){e.stopPropagation();var input=$(this),oldTag=input.data("old_tag"),tag=$.trim(input.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(!tag){if(oldTag&&o.beforeTagDelete(el,ed,tagList,oldTag)===false){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}else if(tag.indexOf(o.delimiter[0])>=0){splitCleanup(input);return}else if(tag!=oldTag){if(o.forceLowercase){tag=tag.toLowerCase()}var cbVal=o.beforeTagSave(el,ed,tagList,oldTag,tag);tag=cbVal||tag;if(cbVal===false){if(oldTag){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}}var $tagEditorTag=input.parent(),tagObject=validate(tag);if(tagObject){var tagProperties=Object.keys(tagObject);for(var i=0;io.maxTagLength){$tagEditorTag.attr("title",escape(tagObject.tagValue))}else{$tagEditorTag.removeAttr("title")}$tagEditorTag.html(escape(ellipsify(tagObject.tagValue,o.maxTagLength))).removeClass("active")}if(tag!=oldTag){updateGlobals()}setPlaceholder()});ed.on("paste","input",function(e){var pastedContent,inputContent;$(this).removeAttr("maxlength");pastedContent=(e.originalEvent||e).clipboardData.getData("text/plain");inputContent=$(this);setTimeout(function(){splitCleanup(inputContent,pastedContent)},30)});ed.on("keypress","input",function(e){if(o.delimiter.indexOf(String.fromCharCode(e.which))>=0){var inp=$(this);setTimeout(function(){splitCleanup(inp)},20)}});ed.on("keydown","input",function(e){var $this=$(this),previousTag,nextTag;if((e.which===37||!o.autocomplete&&e.which===38)&&!$this.caret()||e.which===8&&!$this.val()){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(-1)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}return false}else if((e.which===39||!o.autocomplete&&e.which===40)&&$this.caret()===$this.val().length){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===9){if(e.shiftKey){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(0)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}else{el.attr("disabled","disabled");setTimeout(function(){el.removeAttr("disabled")},30);return}return false}else{nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}else{return}return false}}else if(e.which===46&&(!$.trim($this.val())||$this.caret()===$this.val().length)){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===13){ed.trigger("click",[$this.closest("li").next("li").find(".json-tag-editor-tag")]);if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur()}return false}else if(e.which===36&&!$this.caret()){ed.find(".json-tag-editor-tag").first().click()}else if(e.which===35&&$this.caret()===$this.val().length){ed.find(".json-tag-editor-tag").last().click()}else if(e.which===27){$this.val($this.data("old_tag")?$this.data("old_tag"):"").blur();return false}});var tags=o.initialTags.length?o.initialTags:el.val().split(o.dregex);for(var i=0;i=o.maxTags){break}var tag=$.trim(tags[i].replace(/ +/," "));if(tag){if(o.forceLowercase){tag=tag.toLowerCase()}tagList.push(tag);ed.append("
          • "+'
             '+o.delimiter[0]+"
            "+'
            o.maxTagLength?' title="'+escape(tag)+'"':"")+">"+escape(ellipsify(tag,o.maxTagLength))+"
            "+'
            '+"
          • ")}}updateGlobals(true);if(o.sortable&&$.fn.sortable){ed.sortable({distance:5,cancel:".json-tag-editor-spacer, input",helper:"clone",update:function(){updateGlobals()}})}})};$.fn.jsonTagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,maxTagLength:20,placeholder:"",forceLowercase:false,clickDelete:false,animateDelete:175,sortable:true,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}})(jQuery); \ No newline at end of file From b0c70abbe0ac49e542605026b30fd7fa01e6ab56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 11 Jul 2016 12:35:50 +0100 Subject: [PATCH 37/48] Rename tag-value and tagValue to value (less verbose and less redundant) --- jquery.json-tag-editor.js | 28 ++++++++++++++-------------- jquery.json-tag-editor.min.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index cad51b4..b9348b7 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -16,12 +16,12 @@ try { var parsedTag = JSON.parse(tag); - if (parsedTag.hasOwnProperty('tagValue') && typeof parsedTag.tagValue === 'string' && parsedTag.tagValue.trim().length > 0) { + if (parsedTag.hasOwnProperty('value') && typeof parsedTag.value === 'string' && parsedTag.value.trim().length > 0) { return parsedTag; } } catch (e) {} - return String(tag).trim().length > 0 ? {tagValue: String(tag).trim()} : false; + return String(tag).trim().length > 0 ? {value: String(tag).trim()} : false; } function ellipsify(str, maxLength) { @@ -86,7 +86,7 @@ case 'removeTag': // Trigger delete on matching tag, then click editor to create a new tag $('.json-tag-editor-tag', ed).filter(function() { - return $(this).get(0).dataset.tagValue === val; + return $(this).get(0).dataset.value === val; }).closest('li').find('.json-tag-editor-delete').click(); if (!blur){ @@ -171,19 +171,19 @@ var tag = {}; if ($(this).hasClass('active')) { Object.assign(tag, $(this).find('input').get(0).dataset); - tag.tagValue = $(this).find('input').val(); + tag.value = $(this).find('input').val(); } else { Object.assign(tag, $(e).get(0).dataset) } - if (tag.tagValue) { + if (tag.value) { return tag; } }).get(); ed.data('tags', tagList); - el.val(tagList.reduce(function(previous, current) {return previous + o.delimiter[0] + current.tagValue}, '')); + el.val(tagList.reduce(function(previous, current) {return previous + o.delimiter[0] + current.value}, '')); // Change callback except for plugin init if (!init) { @@ -308,14 +308,14 @@ if (!$(this).hasClass('active')) { // If this is an existing tag get the data- attribute (e.g. expand an ellipsified tag value), otherwise get the text (e.g. it’s being edited) - var tagValue = $(this).get(0).dataset.tagValue ? $(this).get(0).dataset.tagValue : $(this).text(), + var value = $(this).get(0).dataset.value ? $(this).get(0).dataset.value : $(this).text(), tagDisplay = $(this).text(); // Guess cursor position in text input var leftPercent = Math.abs(($(this).offset().left - e.pageX) / $(this).width()), caretPos = parseInt(tagDisplay.length * leftPercent), - input = $(this).html('').addClass('active').find('input'); + input = $(this).html('').addClass('active').find('input'); - input.data('old_tag', tagValue).focus().caret(caretPos); + input.data('old_tag', value).focus().caret(caretPos); if (o.autocomplete) { var aco = $.extend({}, o.autocomplete); @@ -367,7 +367,7 @@ if (tagObject) { var $tagEditorTag = $('
            o.maxTagLength ? ' title="' + escape(tagObject.tagValue) + '"' : '') + '>' + escape(ellipsify(tagObject.tagValue, o.maxTagLength)) + + (tag.length > o.maxTagLength ? ' title="' + escape(tagObject.value) + '"' : '') + '>' + escape(ellipsify(tagObject.value, o.maxTagLength)) + '
            '); var tagProperties = Object.keys(tagObject); @@ -450,13 +450,13 @@ $tagEditorTag.get(0).dataset[tagProperties[i]] = tagObject[tagProperties[i]]; } - if (tagObject.tagValue.length > o.maxTagLength) { - $tagEditorTag.attr('title', escape(tagObject.tagValue)); + if (tagObject.value.length > o.maxTagLength) { + $tagEditorTag.attr('title', escape(tagObject.value)); } else { $tagEditorTag.removeAttr('title'); } - $tagEditorTag.html(escape(ellipsify(tagObject.tagValue, o.maxTagLength))).removeClass('active'); + $tagEditorTag.html(escape(ellipsify(tagObject.value, o.maxTagLength))).removeClass('active'); } if (tag != oldTag) { @@ -613,7 +613,7 @@ ed.append( '
          • ' + '
             ' + o.delimiter[0] + '
            ' + - '
            o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + '
            ' + '
            ' + '
          • '); diff --git a/jquery.json-tag-editor.min.js b/jquery.json-tag-editor.min.js index 5473e82..7558653 100644 --- a/jquery.json-tag-editor.min.js +++ b/jquery.json-tag-editor.min.js @@ -1 +1 @@ -"use strict";(function($){$.fn.jsonTagEditor=function(options,val,blur){function escape(tag){return tag.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function validate(tag){try{var parsedTag=JSON.parse(tag);if(parsedTag.hasOwnProperty("tagValue")&&typeof parsedTag.tagValue==="string"&&parsedTag.tagValue.trim().length>0){return parsedTag}}catch(e){}return String(tag).trim().length>0?{tagValue:String(tag).trim()}:false}function ellipsify(str,maxLength){return str.length>maxLength?str.substring(0,maxLength-1)+"…":str}function deepEquals(arr1,arr2){return $(arr1).not(arr2).length===0&&$(arr2).not(arr1).length===0}var blurResult,o=$.extend({},$.fn.jsonTagEditor.defaults,options),selector=this;o.delimiter=" \n";o.dregex=new RegExp("["+o.delimiter+"]","g");if(typeof options==="string"){var response=[];selector.each(function(){var el=$(this),o=el.data("options"),ed=el.next(".json-tag-editor");switch(options){case"getTags":response.push({field:el[0],editor:ed,tags:ed.data("tags")});break;case"addTag":if(o.maxTags&&ed.data("tags").length>=o.maxTags){return false}$("
          • ").append('
             '+o.delimiter[0]+"
            ").append('
            ').append('
            ').appendTo(ed).find(".json-tag-editor-tag").append('').addClass("active").find("input").val(val).blur();if(!blur){ed.click()}else{$(".placeholder",ed).remove()}break;case"removeTag":$(".json-tag-editor-tag",ed).filter(function(){return $(this).get(0).dataset.tagValue===val}).closest("li").find(".json-tag-editor-delete").click();if(!blur){ed.click()}break;case"destroy":el.removeClass("json-tag-editor-hidden-src").removeData("options").off("focus.json-tag-editor").next(".json-tag-editor").remove();break;default:return this}});return options==="getTags"?response:this}if(window.getSelection){$(document).off("keydown.json-tag-editor").on("keydown.json-tag-editor",function(e){if(e.which===8||e.which===46){try{var sel=getSelection(),el=document.activeElement.tagName==="BODY"?$(sel.getRangeAt(0).startContainer.parentNode).closest(".json-tag-editor"):0}catch(e){el=0}if(sel.rangeCount>0&&el&&el.length){$(".json-tag-editor-tag",el).each(function(){if(sel.containsNode($(this).get(0))){$(this).closest("li").find(".json-tag-editor-delete").click()}});return false}}})}return selector.each(function(){var el=$(this),tagList=[];var ed=$("
              ').insertAfter(el);el.addClass("json-tag-editor-hidden-src").data("options",o).on("focus.json-tag-editor",function(){ed.click()});ed.append('
            •  
            • ');var newTag="
            • "+'
               '+o.delimiter[0]+"
              "+'
              '+'
              '+"
            • ";function setPlaceholder(){if(o.placeholder&&!tagList.length&&!$(".deleted, .placeholder, input",ed).length){ed.append('
            • '+o.placeholder+"
            • ")}}function updateGlobals(init){var oldTags=tagList;tagList=$(".json-tag-editor-tag:not(.deleted)",ed).map(function(i,e){var tag={};if($(this).hasClass("active")){Object.assign(tag,$(this).find("input").get(0).dataset);tag.tagValue=$(this).find("input").val()}else{Object.assign(tag,$(e).get(0).dataset)}if(tag.tagValue){return tag}}).get();ed.data("tags",tagList);el.val(tagList.reduce(function(previous,current){return previous+o.delimiter[0]+current.tagValue},""));if(!init){if(!deepEquals(oldTags,tagList)){o.onChange(el,ed,tagList)}}setPlaceholder()}ed.click(function(e,closestTag){var d,dist=99999,loc;if(window.getSelection&&getSelection().toString()!==""){return}if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur();return false}blurResult=true;$("input:focus",ed).blur();if(!blurResult){return false}blurResult=true;$(".placeholder",ed).remove();if(closestTag&&closestTag.length){loc="before"}else{$(".json-tag-editor-tag",ed).each(function(){var tag=$(this),to=tag.offset(),tagX=to.left,tagY=to.top;if(e.pageY>=tagY&&e.pageY<=tagY+tag.height()){if(e.pageX1){var li=$(this).closest("li"),tag=li.find(".json-tag-editor-tag");if(o.beforeTagDelete(el,ed,tagList,tag.text())===false){return false}tag.addClass("deleted").animate({width:0},o.animateDelete,function(){li.remove();setPlaceholder()});updateGlobals();return false}})}ed.on("click",".json-tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1)){return false}if(!$(this).hasClass("active")){var tagValue=$(this).get(0).dataset.tagValue?$(this).get(0).dataset.tagValue:$(this).text(),tagDisplay=$(this).text();var leftPercent=Math.abs(($(this).offset().left-e.pageX)/$(this).width()),caretPos=parseInt(tagDisplay.length*leftPercent),input=$(this).html('').addClass("active").find("input");input.data("old_tag",tagValue).focus().caret(caretPos);if(o.autocomplete){var aco=$.extend({},o.autocomplete);var acSelect="select"in aco?o.autocomplete.select:"";aco.select=function(e,ui){if(acSelect){acSelect(e,ui)}setTimeout(function(){ed.trigger("click",[$(".active",ed).find("input").closest("li").next("li").find(".json-tag-editor-tag")])},20)};input.autocomplete(aco);if(aco._renderItem){input.autocomplete("instance")._renderItem=aco._renderItem}}}return false});function splitCleanup(input,text){var li=input.closest("li"),subTags=text?text.replace(/ +/," ").split(o.dregex):input.val().replace(/ +/," ").split(o.dregex),oldTag=input.data("old_tag"),oldTags=tagList.slice(0),exceeded=false,cbVal;for(var i=0;io.maxTagLength?' title="'+escape(tagObject.tagValue)+'"':"")+">"+escape(ellipsify(tagObject.tagValue,o.maxTagLength))+"
              ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
               '+o.delimiter[0]+"
              ").append($tagEditorTag).append('
              '));if(o.maxTags&&oldTags.length>=o.maxTags){exceeded=true;break}}}input.closest("li").remove();updateGlobals()}ed.on("blur","input",function(e){e.stopPropagation();var input=$(this),oldTag=input.data("old_tag"),tag=$.trim(input.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(!tag){if(oldTag&&o.beforeTagDelete(el,ed,tagList,oldTag)===false){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}else if(tag.indexOf(o.delimiter[0])>=0){splitCleanup(input);return}else if(tag!=oldTag){if(o.forceLowercase){tag=tag.toLowerCase()}var cbVal=o.beforeTagSave(el,ed,tagList,oldTag,tag);tag=cbVal||tag;if(cbVal===false){if(oldTag){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}}var $tagEditorTag=input.parent(),tagObject=validate(tag);if(tagObject){var tagProperties=Object.keys(tagObject);for(var i=0;io.maxTagLength){$tagEditorTag.attr("title",escape(tagObject.tagValue))}else{$tagEditorTag.removeAttr("title")}$tagEditorTag.html(escape(ellipsify(tagObject.tagValue,o.maxTagLength))).removeClass("active")}if(tag!=oldTag){updateGlobals()}setPlaceholder()});ed.on("paste","input",function(e){var pastedContent,inputContent;$(this).removeAttr("maxlength");pastedContent=(e.originalEvent||e).clipboardData.getData("text/plain");inputContent=$(this);setTimeout(function(){splitCleanup(inputContent,pastedContent)},30)});ed.on("keypress","input",function(e){if(o.delimiter.indexOf(String.fromCharCode(e.which))>=0){var inp=$(this);setTimeout(function(){splitCleanup(inp)},20)}});ed.on("keydown","input",function(e){var $this=$(this),previousTag,nextTag;if((e.which===37||!o.autocomplete&&e.which===38)&&!$this.caret()||e.which===8&&!$this.val()){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(-1)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}return false}else if((e.which===39||!o.autocomplete&&e.which===40)&&$this.caret()===$this.val().length){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===9){if(e.shiftKey){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(0)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}else{el.attr("disabled","disabled");setTimeout(function(){el.removeAttr("disabled")},30);return}return false}else{nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}else{return}return false}}else if(e.which===46&&(!$.trim($this.val())||$this.caret()===$this.val().length)){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===13){ed.trigger("click",[$this.closest("li").next("li").find(".json-tag-editor-tag")]);if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur()}return false}else if(e.which===36&&!$this.caret()){ed.find(".json-tag-editor-tag").first().click()}else if(e.which===35&&$this.caret()===$this.val().length){ed.find(".json-tag-editor-tag").last().click()}else if(e.which===27){$this.val($this.data("old_tag")?$this.data("old_tag"):"").blur();return false}});var tags=o.initialTags.length?o.initialTags:el.val().split(o.dregex);for(var i=0;i=o.maxTags){break}var tag=$.trim(tags[i].replace(/ +/," "));if(tag){if(o.forceLowercase){tag=tag.toLowerCase()}tagList.push(tag);ed.append("
            • "+'
               '+o.delimiter[0]+"
              "+'
              o.maxTagLength?' title="'+escape(tag)+'"':"")+">"+escape(ellipsify(tag,o.maxTagLength))+"
              "+'
              '+"
            • ")}}updateGlobals(true);if(o.sortable&&$.fn.sortable){ed.sortable({distance:5,cancel:".json-tag-editor-spacer, input",helper:"clone",update:function(){updateGlobals()}})}})};$.fn.jsonTagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,maxTagLength:20,placeholder:"",forceLowercase:false,clickDelete:false,animateDelete:175,sortable:true,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}})(jQuery); \ No newline at end of file +"use strict";(function($){$.fn.jsonTagEditor=function(options,val,blur){function escape(tag){return tag.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function validate(tag){try{var parsedTag=JSON.parse(tag);if(parsedTag.hasOwnProperty("value")&&typeof parsedTag.value==="string"&&parsedTag.value.trim().length>0){return parsedTag}}catch(e){}return String(tag).trim().length>0?{value:String(tag).trim()}:false}function ellipsify(str,maxLength){return str.length>maxLength?str.substring(0,maxLength-1)+"…":str}function deepEquals(arr1,arr2){return $(arr1).not(arr2).length===0&&$(arr2).not(arr1).length===0}var blurResult,o=$.extend({},$.fn.jsonTagEditor.defaults,options),selector=this;o.delimiter=" \n";o.dregex=new RegExp("["+o.delimiter+"]","g");if(typeof options==="string"){var response=[];selector.each(function(){var el=$(this),o=el.data("options"),ed=el.next(".json-tag-editor");switch(options){case"getTags":response.push({field:el[0],editor:ed,tags:ed.data("tags")});break;case"addTag":if(o.maxTags&&ed.data("tags").length>=o.maxTags){return false}$("
            • ").append('
               '+o.delimiter[0]+"
              ").append('
              ').append('
              ').appendTo(ed).find(".json-tag-editor-tag").append('').addClass("active").find("input").val(val).blur();if(!blur){ed.click()}else{$(".placeholder",ed).remove()}break;case"removeTag":$(".json-tag-editor-tag",ed).filter(function(){return $(this).get(0).dataset.value===val}).closest("li").find(".json-tag-editor-delete").click();if(!blur){ed.click()}break;case"destroy":el.removeClass("json-tag-editor-hidden-src").removeData("options").off("focus.json-tag-editor").next(".json-tag-editor").remove();break;default:return this}});return options==="getTags"?response:this}if(window.getSelection){$(document).off("keydown.json-tag-editor").on("keydown.json-tag-editor",function(e){if(e.which===8||e.which===46){try{var sel=getSelection(),el=document.activeElement.tagName==="BODY"?$(sel.getRangeAt(0).startContainer.parentNode).closest(".json-tag-editor"):0}catch(e){el=0}if(sel.rangeCount>0&&el&&el.length){$(".json-tag-editor-tag",el).each(function(){if(sel.containsNode($(this).get(0))){$(this).closest("li").find(".json-tag-editor-delete").click()}});return false}}})}return selector.each(function(){var el=$(this),tagList=[];var ed=$("
                ').insertAfter(el);el.addClass("json-tag-editor-hidden-src").data("options",o).on("focus.json-tag-editor",function(){ed.click()});ed.append('
              •  
              • ');var newTag="
              • "+'
                 '+o.delimiter[0]+"
                "+'
                '+'
                '+"
              • ";function setPlaceholder(){if(o.placeholder&&!tagList.length&&!$(".deleted, .placeholder, input",ed).length){ed.append('
              • '+o.placeholder+"
              • ")}}function updateGlobals(init){var oldTags=tagList;tagList=$(".json-tag-editor-tag:not(.deleted)",ed).map(function(i,e){var tag={};if($(this).hasClass("active")){Object.assign(tag,$(this).find("input").get(0).dataset);tag.value=$(this).find("input").val()}else{Object.assign(tag,$(e).get(0).dataset)}if(tag.value){return tag}}).get();ed.data("tags",tagList);el.val(tagList.reduce(function(previous,current){return previous+o.delimiter[0]+current.value},""));if(!init){if(!deepEquals(oldTags,tagList)){o.onChange(el,ed,tagList)}}setPlaceholder()}ed.click(function(e,closestTag){var d,dist=99999,loc;if(window.getSelection&&getSelection().toString()!==""){return}if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur();return false}blurResult=true;$("input:focus",ed).blur();if(!blurResult){return false}blurResult=true;$(".placeholder",ed).remove();if(closestTag&&closestTag.length){loc="before"}else{$(".json-tag-editor-tag",ed).each(function(){var tag=$(this),to=tag.offset(),tagX=to.left,tagY=to.top;if(e.pageY>=tagY&&e.pageY<=tagY+tag.height()){if(e.pageX1){var li=$(this).closest("li"),tag=li.find(".json-tag-editor-tag");if(o.beforeTagDelete(el,ed,tagList,tag.text())===false){return false}tag.addClass("deleted").animate({width:0},o.animateDelete,function(){li.remove();setPlaceholder()});updateGlobals();return false}})}ed.on("click",".json-tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1)){return false}if(!$(this).hasClass("active")){var value=$(this).get(0).dataset.value?$(this).get(0).dataset.value:$(this).text(),tagDisplay=$(this).text();var leftPercent=Math.abs(($(this).offset().left-e.pageX)/$(this).width()),caretPos=parseInt(tagDisplay.length*leftPercent),input=$(this).html('').addClass("active").find("input");input.data("old_tag",value).focus().caret(caretPos);if(o.autocomplete){var aco=$.extend({},o.autocomplete);var acSelect="select"in aco?o.autocomplete.select:"";aco.select=function(e,ui){if(acSelect){acSelect(e,ui)}setTimeout(function(){ed.trigger("click",[$(".active",ed).find("input").closest("li").next("li").find(".json-tag-editor-tag")])},20)};input.autocomplete(aco);if(aco._renderItem){input.autocomplete("instance")._renderItem=aco._renderItem}}}return false});function splitCleanup(input,text){var li=input.closest("li"),subTags=text?text.replace(/ +/," ").split(o.dregex):input.val().replace(/ +/," ").split(o.dregex),oldTag=input.data("old_tag"),oldTags=tagList.slice(0),exceeded=false,cbVal;for(var i=0;io.maxTagLength?' title="'+escape(tagObject.value)+'"':"")+">"+escape(ellipsify(tagObject.value,o.maxTagLength))+"
              ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
               '+o.delimiter[0]+"
              ").append($tagEditorTag).append('
              '));if(o.maxTags&&oldTags.length>=o.maxTags){exceeded=true;break}}}input.closest("li").remove();updateGlobals()}ed.on("blur","input",function(e){e.stopPropagation();var input=$(this),oldTag=input.data("old_tag"),tag=$.trim(input.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(!tag){if(oldTag&&o.beforeTagDelete(el,ed,tagList,oldTag)===false){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}else if(tag.indexOf(o.delimiter[0])>=0){splitCleanup(input);return}else if(tag!=oldTag){if(o.forceLowercase){tag=tag.toLowerCase()}var cbVal=o.beforeTagSave(el,ed,tagList,oldTag,tag);tag=cbVal||tag;if(cbVal===false){if(oldTag){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}}var $tagEditorTag=input.parent(),tagObject=validate(tag);if(tagObject){var tagProperties=Object.keys(tagObject);for(var i=0;io.maxTagLength){$tagEditorTag.attr("title",escape(tagObject.value))}else{$tagEditorTag.removeAttr("title")}$tagEditorTag.html(escape(ellipsify(tagObject.value,o.maxTagLength))).removeClass("active")}if(tag!=oldTag){updateGlobals()}setPlaceholder()});ed.on("paste","input",function(e){var pastedContent,inputContent;$(this).removeAttr("maxlength");pastedContent=(e.originalEvent||e).clipboardData.getData("text/plain");inputContent=$(this);setTimeout(function(){splitCleanup(inputContent,pastedContent)},30)});ed.on("keypress","input",function(e){if(o.delimiter.indexOf(String.fromCharCode(e.which))>=0){var inp=$(this);setTimeout(function(){splitCleanup(inp)},20)}});ed.on("keydown","input",function(e){var $this=$(this),previousTag,nextTag;if((e.which===37||!o.autocomplete&&e.which===38)&&!$this.caret()||e.which===8&&!$this.val()){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(-1)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}return false}else if((e.which===39||!o.autocomplete&&e.which===40)&&$this.caret()===$this.val().length){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===9){if(e.shiftKey){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(0)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}else{el.attr("disabled","disabled");setTimeout(function(){el.removeAttr("disabled")},30);return}return false}else{nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}else{return}return false}}else if(e.which===46&&(!$.trim($this.val())||$this.caret()===$this.val().length)){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===13){ed.trigger("click",[$this.closest("li").next("li").find(".json-tag-editor-tag")]);if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur()}return false}else if(e.which===36&&!$this.caret()){ed.find(".json-tag-editor-tag").first().click()}else if(e.which===35&&$this.caret()===$this.val().length){ed.find(".json-tag-editor-tag").last().click()}else if(e.which===27){$this.val($this.data("old_tag")?$this.data("old_tag"):"").blur();return false}});var tags=o.initialTags.length?o.initialTags:el.val().split(o.dregex);for(var i=0;i=o.maxTags){break}var tag=$.trim(tags[i].replace(/ +/," "));if(tag){if(o.forceLowercase){tag=tag.toLowerCase()}tagList.push(tag);ed.append("
            • "+'
               '+o.delimiter[0]+"
              "+'
              o.maxTagLength?' title="'+escape(tag)+'"':"")+">"+escape(ellipsify(tag,o.maxTagLength))+"
              "+'
              '+"
            • ")}}updateGlobals(true);if(o.sortable&&$.fn.sortable){ed.sortable({distance:5,cancel:".json-tag-editor-spacer, input",helper:"clone",update:function(){updateGlobals()}})}})};$.fn.jsonTagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,maxTagLength:20,placeholder:"",forceLowercase:false,clickDelete:false,animateDelete:175,sortable:true,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}})(jQuery); From 0b72c3f123a073965562438274ff0befa62387bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Sat, 23 Jul 2016 00:09:34 +0100 Subject: [PATCH 38/48] Parse and validate initial tags as array of strings, array of objects or JSON array --- jquery.json-tag-editor.js | 60 ++++++++++++++++++++++++++--------- jquery.json-tag-editor.min.js | 2 +- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/jquery.json-tag-editor.js b/jquery.json-tag-editor.js index b9348b7..e916047 100644 --- a/jquery.json-tag-editor.js +++ b/jquery.json-tag-editor.js @@ -12,11 +12,30 @@ .replace(/'/g, '''); } + function validateTagArray(json) { + try { + return JSON.parse(json).map(function(tagArrayElement) { + return validate(JSON.stringify(tagArrayElement)); + }).filter(function(element) { + return element; + }); + } catch (e) { + return json.split(o.dregex).map(function(tagArrayElement) { + return tagArrayElement.trim().length > 0 ? {value: tagArrayElement.trim()} : false; + }).filter(function(element) { + return element; + }); + } + } + + function validateParsedTag(tagObject) { + return (tagObject.hasOwnProperty('value') && typeof tagObject.value === 'string' && tagObject.value.trim().length > 0); + } + function validate(tag) { try { var parsedTag = JSON.parse(tag); - - if (parsedTag.hasOwnProperty('value') && typeof parsedTag.value === 'string' && parsedTag.value.trim().length > 0) { + if (validateParsedTag(parsedTag)) { return parsedTag; } } catch (e) {} @@ -350,7 +369,7 @@ cbVal; // copy tagList for (var i = 0 ; i < subTags.length ; i++) { - tag = $.trim(subTags[i]).slice(0, o.maxLength); + var tag = $.trim(subTags[i]).slice(0, o.maxLength); if (o.forceLowercase) { tag = tag.toLowerCase(); } @@ -595,28 +614,39 @@ }); // Create initial tags - var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex); + var tags = o.initialTags.length ? + o.initialTags.map(function(element) { + return validateParsedTag(element) ? element : validate(element); + }) : + validateTagArray(el.val()); for (var i = 0 ; i < tags.length ; i++) { if (o.maxTags && i >= o.maxTags) { break; } - var tag = $.trim(tags[i].replace(/ +/, ' ')); - - if (tag) { + var tagObject = tags[i]; + if (tagObject) { if (o.forceLowercase) { - tag = tag.toLowerCase(); + tagObject.value = tagObject.value.toLowerCase(); } - tagList.push(tag); + tagList.push(tagObject); + var $tagEditorTag = + $('
              o.maxTagLength ? ' title="' + escape(tagObject.value) + '"' : '') + '>' + escape(ellipsify(tagObject.value, o.maxTagLength)) + + '
              '); + + var tagProperties = Object.keys(tagObject); + for (var j = 0 ; j < tagProperties.length ; j++) { + $tagEditorTag.get(0).dataset[tagProperties[j]] = tagObject[tagProperties[j]]; + } ed.append( - '
            • ' + - '
               ' + o.delimiter[0] + '
              ' + - '
              o.maxTagLength ? ' title="' + escape(tag) + '"' : '') + '>' + escape(ellipsify(tag, o.maxTagLength)) + '
              ' + - '
              ' + - '
            • '); + $('
            • ') + .append('
               ' + o.delimiter[0] + '
              ') + .append($tagEditorTag) + .append('
              ') + ); } } diff --git a/jquery.json-tag-editor.min.js b/jquery.json-tag-editor.min.js index 7558653..c1340c5 100644 --- a/jquery.json-tag-editor.min.js +++ b/jquery.json-tag-editor.min.js @@ -1 +1 @@ -"use strict";(function($){$.fn.jsonTagEditor=function(options,val,blur){function escape(tag){return tag.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function validate(tag){try{var parsedTag=JSON.parse(tag);if(parsedTag.hasOwnProperty("value")&&typeof parsedTag.value==="string"&&parsedTag.value.trim().length>0){return parsedTag}}catch(e){}return String(tag).trim().length>0?{value:String(tag).trim()}:false}function ellipsify(str,maxLength){return str.length>maxLength?str.substring(0,maxLength-1)+"…":str}function deepEquals(arr1,arr2){return $(arr1).not(arr2).length===0&&$(arr2).not(arr1).length===0}var blurResult,o=$.extend({},$.fn.jsonTagEditor.defaults,options),selector=this;o.delimiter=" \n";o.dregex=new RegExp("["+o.delimiter+"]","g");if(typeof options==="string"){var response=[];selector.each(function(){var el=$(this),o=el.data("options"),ed=el.next(".json-tag-editor");switch(options){case"getTags":response.push({field:el[0],editor:ed,tags:ed.data("tags")});break;case"addTag":if(o.maxTags&&ed.data("tags").length>=o.maxTags){return false}$("
            • ").append('
               '+o.delimiter[0]+"
              ").append('
              ').append('
              ').appendTo(ed).find(".json-tag-editor-tag").append('').addClass("active").find("input").val(val).blur();if(!blur){ed.click()}else{$(".placeholder",ed).remove()}break;case"removeTag":$(".json-tag-editor-tag",ed).filter(function(){return $(this).get(0).dataset.value===val}).closest("li").find(".json-tag-editor-delete").click();if(!blur){ed.click()}break;case"destroy":el.removeClass("json-tag-editor-hidden-src").removeData("options").off("focus.json-tag-editor").next(".json-tag-editor").remove();break;default:return this}});return options==="getTags"?response:this}if(window.getSelection){$(document).off("keydown.json-tag-editor").on("keydown.json-tag-editor",function(e){if(e.which===8||e.which===46){try{var sel=getSelection(),el=document.activeElement.tagName==="BODY"?$(sel.getRangeAt(0).startContainer.parentNode).closest(".json-tag-editor"):0}catch(e){el=0}if(sel.rangeCount>0&&el&&el.length){$(".json-tag-editor-tag",el).each(function(){if(sel.containsNode($(this).get(0))){$(this).closest("li").find(".json-tag-editor-delete").click()}});return false}}})}return selector.each(function(){var el=$(this),tagList=[];var ed=$("
                ').insertAfter(el);el.addClass("json-tag-editor-hidden-src").data("options",o).on("focus.json-tag-editor",function(){ed.click()});ed.append('
              •  
              • ');var newTag="
              • "+'
                 '+o.delimiter[0]+"
                "+'
                '+'
                '+"
              • ";function setPlaceholder(){if(o.placeholder&&!tagList.length&&!$(".deleted, .placeholder, input",ed).length){ed.append('
              • '+o.placeholder+"
              • ")}}function updateGlobals(init){var oldTags=tagList;tagList=$(".json-tag-editor-tag:not(.deleted)",ed).map(function(i,e){var tag={};if($(this).hasClass("active")){Object.assign(tag,$(this).find("input").get(0).dataset);tag.value=$(this).find("input").val()}else{Object.assign(tag,$(e).get(0).dataset)}if(tag.value){return tag}}).get();ed.data("tags",tagList);el.val(tagList.reduce(function(previous,current){return previous+o.delimiter[0]+current.value},""));if(!init){if(!deepEquals(oldTags,tagList)){o.onChange(el,ed,tagList)}}setPlaceholder()}ed.click(function(e,closestTag){var d,dist=99999,loc;if(window.getSelection&&getSelection().toString()!==""){return}if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur();return false}blurResult=true;$("input:focus",ed).blur();if(!blurResult){return false}blurResult=true;$(".placeholder",ed).remove();if(closestTag&&closestTag.length){loc="before"}else{$(".json-tag-editor-tag",ed).each(function(){var tag=$(this),to=tag.offset(),tagX=to.left,tagY=to.top;if(e.pageY>=tagY&&e.pageY<=tagY+tag.height()){if(e.pageX1){var li=$(this).closest("li"),tag=li.find(".json-tag-editor-tag");if(o.beforeTagDelete(el,ed,tagList,tag.text())===false){return false}tag.addClass("deleted").animate({width:0},o.animateDelete,function(){li.remove();setPlaceholder()});updateGlobals();return false}})}ed.on("click",".json-tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1)){return false}if(!$(this).hasClass("active")){var value=$(this).get(0).dataset.value?$(this).get(0).dataset.value:$(this).text(),tagDisplay=$(this).text();var leftPercent=Math.abs(($(this).offset().left-e.pageX)/$(this).width()),caretPos=parseInt(tagDisplay.length*leftPercent),input=$(this).html('').addClass("active").find("input");input.data("old_tag",value).focus().caret(caretPos);if(o.autocomplete){var aco=$.extend({},o.autocomplete);var acSelect="select"in aco?o.autocomplete.select:"";aco.select=function(e,ui){if(acSelect){acSelect(e,ui)}setTimeout(function(){ed.trigger("click",[$(".active",ed).find("input").closest("li").next("li").find(".json-tag-editor-tag")])},20)};input.autocomplete(aco);if(aco._renderItem){input.autocomplete("instance")._renderItem=aco._renderItem}}}return false});function splitCleanup(input,text){var li=input.closest("li"),subTags=text?text.replace(/ +/," ").split(o.dregex):input.val().replace(/ +/," ").split(o.dregex),oldTag=input.data("old_tag"),oldTags=tagList.slice(0),exceeded=false,cbVal;for(var i=0;io.maxTagLength?' title="'+escape(tagObject.value)+'"':"")+">"+escape(ellipsify(tagObject.value,o.maxTagLength))+"
                ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
                 '+o.delimiter[0]+"
                ").append($tagEditorTag).append('
                '));if(o.maxTags&&oldTags.length>=o.maxTags){exceeded=true;break}}}input.closest("li").remove();updateGlobals()}ed.on("blur","input",function(e){e.stopPropagation();var input=$(this),oldTag=input.data("old_tag"),tag=$.trim(input.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(!tag){if(oldTag&&o.beforeTagDelete(el,ed,tagList,oldTag)===false){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}else if(tag.indexOf(o.delimiter[0])>=0){splitCleanup(input);return}else if(tag!=oldTag){if(o.forceLowercase){tag=tag.toLowerCase()}var cbVal=o.beforeTagSave(el,ed,tagList,oldTag,tag);tag=cbVal||tag;if(cbVal===false){if(oldTag){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}}var $tagEditorTag=input.parent(),tagObject=validate(tag);if(tagObject){var tagProperties=Object.keys(tagObject);for(var i=0;io.maxTagLength){$tagEditorTag.attr("title",escape(tagObject.value))}else{$tagEditorTag.removeAttr("title")}$tagEditorTag.html(escape(ellipsify(tagObject.value,o.maxTagLength))).removeClass("active")}if(tag!=oldTag){updateGlobals()}setPlaceholder()});ed.on("paste","input",function(e){var pastedContent,inputContent;$(this).removeAttr("maxlength");pastedContent=(e.originalEvent||e).clipboardData.getData("text/plain");inputContent=$(this);setTimeout(function(){splitCleanup(inputContent,pastedContent)},30)});ed.on("keypress","input",function(e){if(o.delimiter.indexOf(String.fromCharCode(e.which))>=0){var inp=$(this);setTimeout(function(){splitCleanup(inp)},20)}});ed.on("keydown","input",function(e){var $this=$(this),previousTag,nextTag;if((e.which===37||!o.autocomplete&&e.which===38)&&!$this.caret()||e.which===8&&!$this.val()){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(-1)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}return false}else if((e.which===39||!o.autocomplete&&e.which===40)&&$this.caret()===$this.val().length){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===9){if(e.shiftKey){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(0)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}else{el.attr("disabled","disabled");setTimeout(function(){el.removeAttr("disabled")},30);return}return false}else{nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}else{return}return false}}else if(e.which===46&&(!$.trim($this.val())||$this.caret()===$this.val().length)){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===13){ed.trigger("click",[$this.closest("li").next("li").find(".json-tag-editor-tag")]);if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur()}return false}else if(e.which===36&&!$this.caret()){ed.find(".json-tag-editor-tag").first().click()}else if(e.which===35&&$this.caret()===$this.val().length){ed.find(".json-tag-editor-tag").last().click()}else if(e.which===27){$this.val($this.data("old_tag")?$this.data("old_tag"):"").blur();return false}});var tags=o.initialTags.length?o.initialTags:el.val().split(o.dregex);for(var i=0;i=o.maxTags){break}var tag=$.trim(tags[i].replace(/ +/," "));if(tag){if(o.forceLowercase){tag=tag.toLowerCase()}tagList.push(tag);ed.append("
              • "+'
                 '+o.delimiter[0]+"
                "+'
                o.maxTagLength?' title="'+escape(tag)+'"':"")+">"+escape(ellipsify(tag,o.maxTagLength))+"
                "+'
                '+"
              • ")}}updateGlobals(true);if(o.sortable&&$.fn.sortable){ed.sortable({distance:5,cancel:".json-tag-editor-spacer, input",helper:"clone",update:function(){updateGlobals()}})}})};$.fn.jsonTagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,maxTagLength:20,placeholder:"",forceLowercase:false,clickDelete:false,animateDelete:175,sortable:true,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}})(jQuery); +"use strict";(function($){$.fn.jsonTagEditor=function(options,val,blur){function escape(tag){return tag.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function validateTagArray(json){try{return JSON.parse(json).map(function(tagArrayElement){return validate(JSON.stringify(tagArrayElement))}).filter(function(element){return element})}catch(e){return json.split(o.dregex).map(function(tagArrayElement){return tagArrayElement.trim().length>0?{value:tagArrayElement.trim()}:false}).filter(function(element){return element})}}function validateParsedTag(tagObject){return tagObject.hasOwnProperty("value")&&typeof tagObject.value==="string"&&tagObject.value.trim().length>0}function validate(tag){try{var parsedTag=JSON.parse(tag);if(validateParsedTag(parsedTag)){return parsedTag}}catch(e){}return String(tag).trim().length>0?{value:String(tag).trim()}:false}function ellipsify(str,maxLength){return str.length>maxLength?str.substring(0,maxLength-1)+"…":str}function deepEquals(arr1,arr2){return $(arr1).not(arr2).length===0&&$(arr2).not(arr1).length===0}var blurResult,o=$.extend({},$.fn.jsonTagEditor.defaults,options),selector=this;o.delimiter=" \n";o.dregex=new RegExp("["+o.delimiter+"]","g");if(typeof options==="string"){var response=[];selector.each(function(){var el=$(this),o=el.data("options"),ed=el.next(".json-tag-editor");switch(options){case"getTags":response.push({field:el[0],editor:ed,tags:ed.data("tags")});break;case"addTag":if(o.maxTags&&ed.data("tags").length>=o.maxTags){return false}$("
              • ").append('
                 '+o.delimiter[0]+"
                ").append('
                ').append('
                ').appendTo(ed).find(".json-tag-editor-tag").append('').addClass("active").find("input").val(val).blur();if(!blur){ed.click()}else{$(".placeholder",ed).remove()}break;case"removeTag":$(".json-tag-editor-tag",ed).filter(function(){return $(this).get(0).dataset.value===val}).closest("li").find(".json-tag-editor-delete").click();if(!blur){ed.click()}break;case"destroy":el.removeClass("json-tag-editor-hidden-src").removeData("options").off("focus.json-tag-editor").next(".json-tag-editor").remove();break;default:return this}});return options==="getTags"?response:this}if(window.getSelection){$(document).off("keydown.json-tag-editor").on("keydown.json-tag-editor",function(e){if(e.which===8||e.which===46){try{var sel=getSelection(),el=document.activeElement.tagName==="BODY"?$(sel.getRangeAt(0).startContainer.parentNode).closest(".json-tag-editor"):0}catch(e){el=0}if(sel.rangeCount>0&&el&&el.length){$(".json-tag-editor-tag",el).each(function(){if(sel.containsNode($(this).get(0))){$(this).closest("li").find(".json-tag-editor-delete").click()}});return false}}})}return selector.each(function(){var el=$(this),tagList=[];var ed=$("
                  ').insertAfter(el);el.addClass("json-tag-editor-hidden-src").data("options",o).on("focus.json-tag-editor",function(){ed.click()});ed.append('
                •  
                • ');var newTag="
                • "+'
                   '+o.delimiter[0]+"
                  "+'
                  '+'
                  '+"
                • ";function setPlaceholder(){if(o.placeholder&&!tagList.length&&!$(".deleted, .placeholder, input",ed).length){ed.append('
                • '+o.placeholder+"
                • ")}}function updateGlobals(init){var oldTags=tagList;tagList=$(".json-tag-editor-tag:not(.deleted)",ed).map(function(i,e){var tag={};if($(this).hasClass("active")){Object.assign(tag,$(this).find("input").get(0).dataset);tag.value=$(this).find("input").val()}else{Object.assign(tag,$(e).get(0).dataset)}if(tag.value){return tag}}).get();ed.data("tags",tagList);el.val(tagList.reduce(function(previous,current){return previous+o.delimiter[0]+current.value},""));if(!init){if(!deepEquals(oldTags,tagList)){o.onChange(el,ed,tagList)}}setPlaceholder()}ed.click(function(e,closestTag){var d,dist=99999,loc;if(window.getSelection&&getSelection().toString()!==""){return}if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur();return false}blurResult=true;$("input:focus",ed).blur();if(!blurResult){return false}blurResult=true;$(".placeholder",ed).remove();if(closestTag&&closestTag.length){loc="before"}else{$(".json-tag-editor-tag",ed).each(function(){var tag=$(this),to=tag.offset(),tagX=to.left,tagY=to.top;if(e.pageY>=tagY&&e.pageY<=tagY+tag.height()){if(e.pageX1){var li=$(this).closest("li"),tag=li.find(".json-tag-editor-tag");if(o.beforeTagDelete(el,ed,tagList,tag.text())===false){return false}tag.addClass("deleted").animate({width:0},o.animateDelete,function(){li.remove();setPlaceholder()});updateGlobals();return false}})}ed.on("click",".json-tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1)){return false}if(!$(this).hasClass("active")){var value=$(this).get(0).dataset.value?$(this).get(0).dataset.value:$(this).text(),tagDisplay=$(this).text();var leftPercent=Math.abs(($(this).offset().left-e.pageX)/$(this).width()),caretPos=parseInt(tagDisplay.length*leftPercent),input=$(this).html('').addClass("active").find("input");input.data("old_tag",value).focus().caret(caretPos);if(o.autocomplete){var aco=$.extend({},o.autocomplete);var acSelect="select"in aco?o.autocomplete.select:"";aco.select=function(e,ui){if(acSelect){acSelect(e,ui)}setTimeout(function(){ed.trigger("click",[$(".active",ed).find("input").closest("li").next("li").find(".json-tag-editor-tag")])},20)};input.autocomplete(aco);if(aco._renderItem){input.autocomplete("instance")._renderItem=aco._renderItem}}}return false});function splitCleanup(input,text){var li=input.closest("li"),subTags=text?text.replace(/ +/," ").split(o.dregex):input.val().replace(/ +/," ").split(o.dregex),oldTag=input.data("old_tag"),oldTags=tagList.slice(0),exceeded=false,cbVal;for(var i=0;io.maxTagLength?' title="'+escape(tagObject.value)+'"':"")+">"+escape(ellipsify(tagObject.value,o.maxTagLength))+"
                  ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
                   '+o.delimiter[0]+"
                  ").append($tagEditorTag).append('
                  '));if(o.maxTags&&oldTags.length>=o.maxTags){exceeded=true;break}}}input.closest("li").remove();updateGlobals()}ed.on("blur","input",function(e){e.stopPropagation();var input=$(this),oldTag=input.data("old_tag"),tag=$.trim(input.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(!tag){if(oldTag&&o.beforeTagDelete(el,ed,tagList,oldTag)===false){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}else if(tag.indexOf(o.delimiter[0])>=0){splitCleanup(input);return}else if(tag!=oldTag){if(o.forceLowercase){tag=tag.toLowerCase()}var cbVal=o.beforeTagSave(el,ed,tagList,oldTag,tag);tag=cbVal||tag;if(cbVal===false){if(oldTag){input.val(oldTag).focus();blurResult=false;updateGlobals();return}try{input.closest("li").remove()}catch(e){}if(oldTag){updateGlobals()}}}var $tagEditorTag=input.parent(),tagObject=validate(tag);if(tagObject){var tagProperties=Object.keys(tagObject);for(var i=0;io.maxTagLength){$tagEditorTag.attr("title",escape(tagObject.value))}else{$tagEditorTag.removeAttr("title")}$tagEditorTag.html(escape(ellipsify(tagObject.value,o.maxTagLength))).removeClass("active")}if(tag!=oldTag){updateGlobals()}setPlaceholder()});ed.on("paste","input",function(e){var pastedContent,inputContent;$(this).removeAttr("maxlength");pastedContent=(e.originalEvent||e).clipboardData.getData("text/plain");inputContent=$(this);setTimeout(function(){splitCleanup(inputContent,pastedContent)},30)});ed.on("keypress","input",function(e){if(o.delimiter.indexOf(String.fromCharCode(e.which))>=0){var inp=$(this);setTimeout(function(){splitCleanup(inp)},20)}});ed.on("keydown","input",function(e){var $this=$(this),previousTag,nextTag;if((e.which===37||!o.autocomplete&&e.which===38)&&!$this.caret()||e.which===8&&!$this.val()){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(-1)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}return false}else if((e.which===39||!o.autocomplete&&e.which===40)&&$this.caret()===$this.val().length){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===9){if(e.shiftKey){previousTag=$this.closest("li").prev("li").find(".json-tag-editor-tag");if(previousTag.length){previousTag.click().find("input").caret(0)}else if($this.val()&&!(o.maxTags&&ed.data("tags").length>=o.maxTags)){$(newTag).insertBefore($this.closest("li")).find(".json-tag-editor-tag").click()}else{el.attr("disabled","disabled");setTimeout(function(){el.removeAttr("disabled")},30);return}return false}else{nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}else{return}return false}}else if(e.which===46&&(!$.trim($this.val())||$this.caret()===$this.val().length)){nextTag=$this.closest("li").next("li").find(".json-tag-editor-tag");if(nextTag.length){nextTag.click().find("input").caret(0)}else if($this.val()){ed.click()}return false}else if(e.which===13){ed.trigger("click",[$this.closest("li").next("li").find(".json-tag-editor-tag")]);if(o.maxTags&&ed.data("tags").length>=o.maxTags){ed.find("input").blur()}return false}else if(e.which===36&&!$this.caret()){ed.find(".json-tag-editor-tag").first().click()}else if(e.which===35&&$this.caret()===$this.val().length){ed.find(".json-tag-editor-tag").last().click()}else if(e.which===27){$this.val($this.data("old_tag")?$this.data("old_tag"):"").blur();return false}});var tags=o.initialTags.length?o.initialTags.map(function(element){return validateParsedTag(element)?element:validate(element)}):validateTagArray(el.val());for(var i=0;i=o.maxTags){break}var tagObject=tags[i];if(tagObject){if(o.forceLowercase){tagObject.value=tagObject.value.toLowerCase()}tagList.push(tagObject);var $tagEditorTag=$('
                  o.maxTagLength?' title="'+escape(tagObject.value)+'"':"")+">"+escape(ellipsify(tagObject.value,o.maxTagLength))+"
                  ");var tagProperties=Object.keys(tagObject);for(var j=0;j").append('
                   '+o.delimiter[0]+"
                  ").append($tagEditorTag).append('
                  '))}}updateGlobals(true);if(o.sortable&&$.fn.sortable){ed.sortable({distance:5,cancel:".json-tag-editor-spacer, input",helper:"clone",update:function(){updateGlobals()}})}})};$.fn.jsonTagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,maxTagLength:20,placeholder:"",forceLowercase:false,clickDelete:false,animateDelete:175,sortable:true,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}})(jQuery); \ No newline at end of file From dbae97c464fbbc0169e9c8ad2eda456632c3ad1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Sat, 23 Jul 2016 00:10:24 +0100 Subject: [PATCH 39/48] Provide examples to test parsing and validation of initial tags as array of strings, array of objects or JSON array --- demo.html | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/demo.html b/demo.html index 6083bb8..7ae3d97 100644 --- a/demo.html +++ b/demo.html @@ -86,6 +86,14 @@

                  A powerful and lightweight tag editor plugin for jQuery based on +
                  + +
                  +

                  +

                  Overview and Features

                  @@ -170,7 +178,7 @@

                  DemosBasic settings

                   $('#demo1').jsonTagEditor({
                  -    initialTags: ['Hello', 'World', 'Example', 'Tags'],
                  +    initialTags: [{"value: "Hello"}, {"value": "World"}, {"value": "Example"}, {"value": "Tags"}],
                       placeholder: 'Enter tags ...'
                   });
                  @@ -202,7 +210,7 @@

                  Autocomplete

                  Public methods

                   $('#demo3').jsonTagEditor({
                  -    initialTags: ['Hello', 'World'],
                  +    initialTags: [{"value":"Hello", {"value":"World"}],
                       placeholder: 'Enter tags ...'
                   });
                  @@ -271,7 +279,7 @@

                  Custom style and clickDelete

                   $('#demo5').jsonTagEditor({
                       clickDelete: true,
                  -    initialTags: [ ... ],
                  +    initialTags: [ ],
                       placeholder: 'Enter tags ...'
                   });
                  @@ -364,7 +372,7 @@

                  Custom CSS classes for tags

                  integrity="sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=" crossorigin="anonymous"> - + - + From 04633dd8b4930d919284810c725a1439f070ac42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Mu=C3=B1oz-Pomer?= Date: Mon, 25 Jul 2016 11:38:02 +0100 Subject: [PATCH 44/48] Last touches on the demo HTML --- README.md | 23 +++++++++++++---------- demo.html | 14 ++++++-------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a082deb..e9654a5 100644 --- a/README.md +++ b/README.md @@ -2,37 +2,40 @@ jQuery JSON Tag Editor ====================== *jQuery JSON Tag Editor* is a fork of [jQuery-tagEditor](https://goodies.pixabay.com/jquery/tag-editor/demo.html), originally developed by Simon Steinberger for -[Pixabay.com](https://pixabay.com) and licensed under the [MIT license](https://opensource.org/licenses/MIT). This fork maintains the same license. +[Pixabay.com](https://pixabay.com) and licensed under the [MIT license](https://opensource.org/licenses/MIT). This fork maintains the same license and adds the +[Apache License, 2.0](http://www.apache.org/licenses/LICENSE-2.0). -*jQuery JSON Tag Editor* adds a separation between the visual representation of a tag and the tag itself. Tags are plain JavaScript objects with a `tagValue` -that is its visual representation in the tag editor. `tagValue` plus any other properties that the tag object may have are all stored as +*jQuery JSON Tag Editor* adds a separation between the visual representation of a tag and the tag itself. Tags are plain JavaScript objects with a `value` attribute +that is its visual representation in the tag editor. `value` plus any other properties that the tag object may have are all stored as [data attributes](https://developer.mozilla.org/en/docs/Web/Guide/HTML/Using_data_attributes) in each `
                  ` element. So, using the `addTag` public method, this: ``` -$('#id').jsonTagEditor('addTag', '{"tagValue": "Car", "tagType": "string", "category": "vehicle"}') +$('#id').jsonTagEditor('addTag', '{"value": "Car", "tagType": "string", "category": "vehicle"}') ``` Produces the markup below: ``` -
                  Car
                  +
                  Car
                  ``` -If a string is provided as an argument then an object with only the `tagValue` property is created. This is the case when tags are manually typed into the editor. +If a string is provided as an argument then an object with only the `value` property is created. This is the case when tags are manually typed into the editor. Features added: --------------- -* Tags are complex objects: what you see in the editor is the property `tagValue`, but you can add arbitrary data to your tags +* Tags are complex objects: what you see in the editor is the property `value`, but you can add arbitrary data to your tags * Tag object properties are stored as data attributes in their corresponding `
                  ` element -* A new option `maxTagLength` can trim and ellipsify tags while keeping the original value in `data-tag-value` -* Multiple tags when pasting a multi-line text snippet, one per line +* A new option `maxTagLength` can ellipsify tags while keeping the original value in `data-value` +* Multiple tags when pasting a multi-line text snippet, one per line, or split by tabs Features removed: ----------------- * Auto-grow width of `` editors * Support for cut: the original feature was Window-specific but buggy as tags weren’t put in the clipboard * `removeDuplicates`: because tags aren’t strings it was the simplest way to deal with this issue -* The `delimiter` is hard-coded to `\t` and `\n` +* The `delimiter` is hard-coded to `\t` and `\n` + +__[Visit the demo page for the full documentation](https://github.io/alfonsomunozpomer/jsontageditor/demo.html).__ --- diff --git a/demo.html b/demo.html index 8182172..113a802 100644 --- a/demo.html +++ b/demo.html @@ -2,7 +2,7 @@ jQuery tagEditor Plugin - + @@ -369,17 +369,15 @@

                  Custom CSS classes for tags

                  -

                  Please report any bugs and issues at the GitHub repositiory.

                  -

                  This software is released as Open Source under the MIT License by Simon Steinberger / Pixabay.com.

                  +

                  Please report any bugs and issues at the GitHub repositiory.

                  +

                  This software is released as Open Source under the Apache License, 2.0 by Alfonso Muñoz-Pomer Fuentes. +

                  Fork of an original project by Simon Steinberger / Pixabay.com.

                  - About Us - Blog - More Goodies - © Pixabay.com / Simon Steinberger / Hans Braxmeier + jQuery tagEditor is © Pixabay.com / Simon Steinberger / Hans Braxmeier
                  @@ -391,7 +389,7 @@

                  Custom CSS classes for tags

                  integrity="sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=" crossorigin="anonymous"> - +