scrivito_editors 1.1.1 → 1.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/scrivito_editors/binary_editor.js +132 -3
  3. data/app/assets/javascripts/scrivito_editors/enum_editor.js +42 -49
  4. data/app/assets/javascripts/scrivito_editors/linklist_editor.js +3 -2
  5. data/app/assets/javascripts/scrivito_editors/multienum_editor.js +39 -34
  6. data/app/assets/javascripts/scrivito_editors/permalink_editor.js +3 -0
  7. data/app/assets/javascripts/scrivito_editors/referencelist_editor.js +4 -2
  8. data/app/assets/javascripts/scrivito_editors/slider_editor.js +15 -5
  9. data/app/assets/javascripts/scrivito_editors/stringlist_editor.js +3 -0
  10. data/app/assets/javascripts/scrivito_editors.js +14 -2
  11. data/app/assets/stylesheets/scrivito_editors/{editors/image_drop_editor.css → image_drop_editor.css} +0 -0
  12. data/app/assets/stylesheets/{scrivito-editors-imglykit-override.css → scrivito_editors/imglykit_override.css} +0 -0
  13. data/app/assets/stylesheets/scrivito_editors/{editors/link_editor.css → link_editor.css} +0 -0
  14. data/app/assets/stylesheets/scrivito_editors/{editors/linklist_editor.css → linklist_editor.css} +2 -2
  15. data/app/assets/stylesheets/scrivito_editors/{editors/medium_editor.css → medium_editor.css} +0 -0
  16. data/app/assets/stylesheets/scrivito_editors/{editors/redactor_editor.css → redactor_editor.css} +0 -0
  17. data/app/assets/stylesheets/scrivito_editors/{editors/referencelist_editor.css → referencelist_editor.css} +2 -2
  18. data/app/assets/stylesheets/scrivito_editors/ui_container.css +105 -0
  19. data/app/assets/stylesheets/scrivito_editors.css +1 -1
  20. data/lib/scrivito_editors/engine.rb +1 -1
  21. data/{app → vendor}/assets/images/imgly_sdk/ui/night/blur/blur.png +0 -0
  22. data/{app → vendor}/assets/images/imgly_sdk/ui/night/buttons/back.png +0 -0
  23. data/{app → vendor}/assets/images/imgly_sdk/ui/night/buttons/done-highlighted.png +0 -0
  24. data/{app → vendor}/assets/images/imgly_sdk/ui/night/buttons/done.png +0 -0
  25. data/{app → vendor}/assets/images/imgly_sdk/ui/night/cancel.png +0 -0
  26. data/{app → vendor}/assets/images/imgly_sdk/ui/night/close.png +0 -0
  27. data/{app → vendor}/assets/images/imgly_sdk/ui/night/crop/16-9.png +0 -0
  28. data/{app → vendor}/assets/images/imgly_sdk/ui/night/crop/4-3.png +0 -0
  29. data/{app → vendor}/assets/images/imgly_sdk/ui/night/crop/custom.png +0 -0
  30. data/{app → vendor}/assets/images/imgly_sdk/ui/night/crop/square.png +0 -0
  31. data/{app → vendor}/assets/images/imgly_sdk/ui/night/crosshair.png +0 -0
  32. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/a15.png +0 -0
  33. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/breeze.png +0 -0
  34. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/bw.png +0 -0
  35. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/bwhard.png +0 -0
  36. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/celsius.png +0 -0
  37. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/chest.png +0 -0
  38. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/default.png +0 -0
  39. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/fixie.png +0 -0
  40. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/food.png +0 -0
  41. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/fridge.png +0 -0
  42. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/front.png +0 -0
  43. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/glam.png +0 -0
  44. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/gobblin.png +0 -0
  45. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/identity.png +0 -0
  46. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/k1.png +0 -0
  47. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/k2.png +0 -0
  48. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/k6.png +0 -0
  49. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/kdynamic.png +0 -0
  50. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/lenin.png +0 -0
  51. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/lomo.png +0 -0
  52. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/mellow.png +0 -0
  53. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/morning.png +0 -0
  54. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/orchid.png +0 -0
  55. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/pola.png +0 -0
  56. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/pola669.png +0 -0
  57. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/quozi.png +0 -0
  58. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/semired.png +0 -0
  59. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/sunny.png +0 -0
  60. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/texas.png +0 -0
  61. data/{app → vendor}/assets/images/imgly_sdk/ui/night/filters/x400.png +0 -0
  62. data/{app → vendor}/assets/images/imgly_sdk/ui/night/flip/horizontal.png +0 -0
  63. data/{app → vendor}/assets/images/imgly_sdk/ui/night/flip/vertical.png +0 -0
  64. data/{app → vendor}/assets/images/imgly_sdk/ui/night/loading.gif +0 -0
  65. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/brightness.png +0 -0
  66. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/contrast.png +0 -0
  67. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/crop.png +0 -0
  68. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/filters.png +0 -0
  69. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/flip.png +0 -0
  70. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/focus.png +0 -0
  71. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/frames.png +0 -0
  72. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/orientation.png +0 -0
  73. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/radial-blur.png +0 -0
  74. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/rotation.png +0 -0
  75. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/saturation.png +0 -0
  76. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/stickers.png +0 -0
  77. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/text.png +0 -0
  78. data/{app → vendor}/assets/images/imgly_sdk/ui/night/operations/tilt-shift.png +0 -0
  79. data/{app → vendor}/assets/images/imgly_sdk/ui/night/rotation/left.png +0 -0
  80. data/{app → vendor}/assets/images/imgly_sdk/ui/night/rotation/right.png +0 -0
  81. data/{app → vendor}/assets/images/imgly_sdk/ui/night/slider/minus.png +0 -0
  82. data/{app → vendor}/assets/images/imgly_sdk/ui/night/slider/plus.png +0 -0
  83. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/aspect.png +0 -0
  84. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/new.png +0 -0
  85. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/original.png +0 -0
  86. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/undo.png +0 -0
  87. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/zoom-in.png +0 -0
  88. data/{app → vendor}/assets/images/imgly_sdk/ui/night/top/zoom-out.png +0 -0
  89. data/{app → vendor}/assets/images/imgly_sdk/ui/night/transparency.png +0 -0
  90. data/{app → vendor}/assets/images/imgly_sdk/ui/night/upload.png +0 -0
  91. data/{app → vendor}/assets/javascripts/canvas-to-blob.js +0 -0
  92. data/{app → vendor}/assets/javascripts/imglykit.js +0 -0
  93. data/{app → vendor}/assets/javascripts/jquery-ui-timepicker-addon.min.js +0 -0
  94. data/vendor/assets/javascripts/jquery.tag-editor.js +68 -38
  95. data/vendor/assets/javascripts/scrivito_editors_sortable.js +1278 -0
  96. data/{app → vendor}/assets/stylesheets/imglykit-night-ui.css +0 -0
  97. data/{app → vendor}/assets/stylesheets/jquery-ui-timepicker-addon.min.css +0 -0
  98. data/vendor/assets/stylesheets/jquery.tag-editor.css +9 -13
  99. data/{app → vendor}/assets/stylesheets/redactor.css +0 -0
  100. metadata +91 -92
  101. data/app/assets/javascripts/scrivito_editors_addons.js +0 -5
  102. data/app/assets/javascripts/scrivito_editors_core.js +0 -8
  103. data/app/assets/stylesheets/scrivito_editors/editors/button_container.css +0 -33
File without changes
@@ -1,5 +1,5 @@
1
1
  /*
2
- jQuery tagEditor v1.0.14
2
+ jQuery tagEditor v1.0.20
3
3
  Copyright (c) 2014 Simon Steinberger / Pixabay
4
4
  GitHub: https://github.com/Pixabay/jQuery-tagEditor
5
5
  License: http://www.opensource.org/licenses/mit-license.php
@@ -7,11 +7,16 @@
7
7
 
8
8
  (function($){
9
9
  // auto grow input (stackoverflow.com/questions/931207)
10
- $.fn.tagEditorInput=function(){var t=" ",e=$(this),n=parseInt(e.css("fontSize")),i=$("<span/>").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.html(t.replace(/&/g,"&amp;").replace(/\s/g,"&nbsp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"));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)};
10
+ $.fn.tagEditorInput=function(){var t=" ",e=$(this),n=parseInt(e.css("fontSize")),i=$("<span/>").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)};
11
11
 
12
12
  // plugin with val as parameter for public methods
13
13
  $.fn.tagEditor = function(options, val, blur){
14
14
 
15
+ // helper
16
+ function escape(tag) {
17
+ return tag.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
18
+ }
19
+
15
20
  // build options dictionary with default values
16
21
  var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this;
17
22
 
@@ -20,7 +25,6 @@
20
25
 
21
26
  // public methods
22
27
  if (typeof options == 'string') {
23
-
24
28
  // depending on selector, response may contain tag lists of multiple editor instances
25
29
  var response = [];
26
30
  selector.each(function(){
@@ -29,6 +33,7 @@
29
33
  if (options == 'getTags')
30
34
  response.push({field: el[0], editor: ed, tags: ed.data('tags')});
31
35
  else if (options == 'addTag') {
36
+ if (o.maxTags && ed.data('tags').length >= o.maxTags) return false;
32
37
  // insert new tag
33
38
  $('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(ed).find('.tag-editor-tag')
34
39
  .html('<input type="text" maxlength="'+o.maxLength+'">').addClass('active').find('input').val(val).blur();
@@ -36,7 +41,7 @@
36
41
  else $('.placeholder', ed).remove();
37
42
  } else if (options == 'removeTag') {
38
43
  // trigger delete on matching tag, then click editor to create a new tag
39
- $('.tag-editor-tag', ed).filter(function(){return $(this).html()==val;}).closest('li').find('.tag-editor-delete').click();
44
+ $('.tag-editor-tag', ed).filter(function(){return $(this).text()==val;}).closest('li').find('.tag-editor-delete').click();
40
45
  if (!blur) ed.click();
41
46
  } else if (options == 'destroy') {
42
47
  el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove();
@@ -46,21 +51,21 @@
46
51
  }
47
52
 
48
53
  // delete selected tags on backspace, delete, ctrl+x
49
- function delete_selected_tags(e){
54
+ if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e){
50
55
  if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) {
51
56
  try {
52
- var sel = getSelection(), el = $(sel.getRangeAt(0).commonAncestorContainer);
57
+ var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0;
53
58
  } catch(e){ el = 0; }
54
- if (el && el.hasClass('tag-editor')) {
59
+ if (sel.rangeCount > 0 && el && el.length) {
55
60
  var tags = [], splits = sel.toString().split(el.prev().data('options').dregex);
56
61
  for (i=0; i<splits.length; i++){ var tag = $.trim(splits[i]); if (tag) tags.push(tag); }
57
62
  $('.tag-editor-tag', el).each(function(){
58
- if (~$.inArray($(this).html(), tags)) $(this).closest('li').find('.tag-editor-delete').click();
63
+ if (~$.inArray($(this).text(), tags)) $(this).closest('li').find('.tag-editor-delete').click();
59
64
  });
65
+ return false;
60
66
  }
61
67
  }
62
- }
63
- if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', delete_selected_tags);
68
+ });
64
69
 
65
70
  return selector.each(function(){
66
71
  var el = $(this), tag_list = []; // cache current tags
@@ -103,6 +108,8 @@
103
108
  // do not create tag when user selects tags by text selection
104
109
  if (window.getSelection && getSelection() != '') return;
105
110
 
111
+ if (o.maxTags && ed.data('tags').length >= o.maxTags) { ed.find('input').blur(); return false; }
112
+
106
113
  blur_result = true
107
114
  $('input:focus', ed).blur();
108
115
  if (!blur_result) return false;
@@ -138,8 +145,8 @@
138
145
  if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; }
139
146
 
140
147
  var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
141
- if (o.beforeTagDelete(el, ed, tag_list, tag.html()) === false) return false;
142
- tag.addClass('deleted').animate({width: 0}, 175, function(){ li.remove(); set_placeholder(); });
148
+ if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false;
149
+ tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); });
143
150
  update_globals();
144
151
  return false;
145
152
  });
@@ -149,8 +156,8 @@
149
156
  ed.on('mousedown', '.tag-editor-tag', function(e){
150
157
  if (e.ctrlKey || e.which > 1) {
151
158
  var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
152
- if (o.beforeTagDelete(el, ed, tag_list, tag.html()) === false) return false;
153
- tag.addClass('deleted').animate({width: 0}, 175, function(){ li.remove(); set_placeholder(); });
159
+ if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false;
160
+ tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); });
154
161
  update_globals();
155
162
  return false;
156
163
  }
@@ -161,10 +168,10 @@
161
168
  if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false;
162
169
 
163
170
  if (!$(this).hasClass('active')) {
164
- var tag = $(this).html();
171
+ var tag = $(this).text();
165
172
  // guess cursor position in text input
166
173
  var left_percent = Math.abs(($(this).offset().left - e.pageX)/$(this).width()), caret_pos = parseInt(tag.length*left_percent),
167
- input = $(this).html('<input type="text" maxlength="'+o.maxLength+'" value="'+tag+'">').addClass('active').find('input');
174
+ input = $(this).html('<input type="text" maxlength="'+o.maxLength+'" value="'+escape(tag)+'">').addClass('active').find('input');
168
175
  input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos);
169
176
  if (o.autocomplete) {
170
177
  var aco = $.extend({}, o.autocomplete);
@@ -181,25 +188,28 @@
181
188
 
182
189
  // helper: split into multiple tags, e.g. after paste
183
190
  function split_cleanup(input){
184
- var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), old_tag = input.data('old_tag');
185
- var old_tags = tag_list.slice(0); // copy tag_list
186
- for (i=0; i<sub_tags.length; i++) {
191
+ var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex),
192
+ old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list
193
+ for (var i=0; i<sub_tags.length; i++) {
187
194
  tag = $.trim(sub_tags[i]).slice(0, o.maxLength);
188
- if (tag) {
189
- if (o.forceLowercase) tag = tag.toLowerCase();
190
- tag = o.beforeTagSave(el, ed, old_tags, old_tag, tag) || tag;
191
- // remove duplicates
192
- if (~$.inArray(tag, old_tags))
193
- $('.tag-editor-tag', ed).each(function(){ if ($(this).html() == tag) $(this).closest('li').remove(); });
194
- old_tags.push(tag);
195
- li.before('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+tag+'</div><div class="tag-editor-delete"><i></i></div></li>');
196
- }
195
+ if (o.forceLowercase) tag = tag.toLowerCase();
196
+ cb_val = o.beforeTagSave(el, ed, old_tags, old_tag, tag);
197
+ tag = cb_val || tag;
198
+ if (cb_val === false || !tag) continue;
199
+ // remove duplicates
200
+ if (o.removeDuplicates && ~$.inArray(tag, old_tags))
201
+ $('.tag-editor-tag', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); });
202
+ old_tags.push(tag);
203
+ li.before('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+escape(tag)+'</div><div class="tag-editor-delete"><i></i></div></li>');
204
+ if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; }
197
205
  }
198
- input.attr('maxlength', o.maxLength).removeData('old_tag').val('').focus();
206
+ input.attr('maxlength', o.maxLength).removeData('old_tag').val('')
207
+ if (exceeded) input.blur(); else input.focus();
199
208
  update_globals();
200
209
  }
201
210
 
202
211
  ed.on('blur', 'input', function(e){
212
+ e.stopPropagation();
203
213
  var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0]));
204
214
  if (!tag) {
205
215
  if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) {
@@ -214,11 +224,23 @@
214
224
  else if (tag.indexOf(o.delimiter[0])>=0) { split_cleanup(input); return; }
215
225
  else if (tag != old_tag) {
216
226
  if (o.forceLowercase) tag = tag.toLowerCase();
217
- tag = o.beforeTagSave(el, ed, tag_list, old_tag, tag) || tag;
227
+ cb_val = o.beforeTagSave(el, ed, tag_list, old_tag, tag);
228
+ tag = cb_val || tag;
229
+ if (cb_val === false) {
230
+ if (old_tag) {
231
+ input.val(old_tag).focus();
232
+ blur_result = false;
233
+ update_globals();
234
+ return;
235
+ }
236
+ try { input.closest('li').remove(); } catch(e){}
237
+ if (old_tag) update_globals();
238
+ }
218
239
  // remove duplicates
219
- $('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).html() == tag) $(this).closest('li').remove(); });
240
+ else if (o.removeDuplicates)
241
+ $('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); });
220
242
  }
221
- input.parent().html(tag).removeClass('active');
243
+ input.parent().html(escape(tag)).removeClass('active');
222
244
  if (tag != old_tag) update_globals();
223
245
  set_placeholder();
224
246
  });
@@ -246,7 +268,7 @@
246
268
  if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) {
247
269
  var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
248
270
  if (prev_tag.length) prev_tag.click().find('input').caret(-1);
249
- else if ($t.val()) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
271
+ else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
250
272
  return false;
251
273
  }
252
274
  // right/down key
@@ -262,13 +284,13 @@
262
284
  if (e.shiftKey) {
263
285
  var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
264
286
  if (prev_tag.length) prev_tag.click().find('input').caret(0);
265
- else if ($t.val()) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
287
+ else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
266
288
  // allow tabbing to previous element
267
289
  else {
268
290
  el.attr('disabled', 'disabled');
269
291
  setTimeout(function(){ el.removeAttr('disabled'); }, 30);
270
- return;
271
- }
292
+ return;
293
+ }
272
294
  return false;
273
295
  // tab
274
296
  } else {
@@ -289,6 +311,10 @@
289
311
  // enter key
290
312
  else if (e.which == 13) {
291
313
  ed.trigger('click', [$t.closest('li').next('li').find('.tag-editor-tag')]);
314
+
315
+ // trigger blur if maxTags limit is reached
316
+ if (o.maxTags && ed.data('tags').length >= o.maxTags) ed.find('input').blur();
317
+
292
318
  return false;
293
319
  }
294
320
  // pos1
@@ -304,12 +330,13 @@
304
330
 
305
331
  // create initial tags
306
332
  var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex);
307
- for (i=0; i<tags.length; i++) {
333
+ for (var i=0; i<tags.length; i++) {
334
+ if (o.maxTags && i >= o.maxTags) break;
308
335
  var tag = $.trim(tags[i].replace(/ +/, ' '));
309
336
  if (tag) {
310
337
  if (o.forceLowercase) tag = tag.toLowerCase();
311
338
  tag_list.push(tag);
312
- ed.append('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+tag+'</div><div class="tag-editor-delete"><i></i></div></li>');
339
+ ed.append('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+escape(tag)+'</div><div class="tag-editor-delete"><i></i></div></li>');
313
340
  }
314
341
  }
315
342
  update_globals(true); // true -> no onChange callback
@@ -324,11 +351,14 @@
324
351
 
325
352
  $.fn.tagEditor.defaults = {
326
353
  initialTags: [],
354
+ maxTags: 0,
327
355
  maxLength: 50,
328
356
  delimiter: ',;',
329
357
  placeholder: '',
330
358
  forceLowercase: true,
359
+ removeDuplicates: true,
331
360
  clickDelete: false,
361
+ animateDelete: 175,
332
362
  sortable: true, // jQuery UI sortable
333
363
  autocomplete: null, // options dict for jQuery UI autocomplete
334
364