scrivito_editors 0.66.0 → 0.70.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/imgly_sdk/ui/night/blur/blur.png +0 -0
  3. data/app/assets/images/imgly_sdk/ui/night/buttons/back.png +0 -0
  4. data/app/assets/images/imgly_sdk/ui/night/buttons/done-highlighted.png +0 -0
  5. data/app/assets/images/imgly_sdk/ui/night/buttons/done.png +0 -0
  6. data/app/assets/images/imgly_sdk/ui/night/cancel.png +0 -0
  7. data/app/assets/images/imgly_sdk/ui/night/close.png +0 -0
  8. data/app/assets/images/imgly_sdk/ui/night/crop/16-9.png +0 -0
  9. data/app/assets/images/imgly_sdk/ui/night/crop/4-3.png +0 -0
  10. data/app/assets/images/imgly_sdk/ui/night/crop/custom.png +0 -0
  11. data/app/assets/images/imgly_sdk/ui/night/crop/square.png +0 -0
  12. data/app/assets/images/imgly_sdk/ui/night/crosshair.png +0 -0
  13. data/app/assets/images/imgly_sdk/ui/night/filters/a15.png +0 -0
  14. data/app/assets/images/imgly_sdk/ui/night/filters/breeze.png +0 -0
  15. data/app/assets/images/imgly_sdk/ui/night/filters/bw.png +0 -0
  16. data/app/assets/images/imgly_sdk/ui/night/filters/bwhard.png +0 -0
  17. data/app/assets/images/imgly_sdk/ui/night/filters/celsius.png +0 -0
  18. data/app/assets/images/imgly_sdk/ui/night/filters/chest.png +0 -0
  19. data/app/assets/images/imgly_sdk/ui/night/filters/default.png +0 -0
  20. data/app/assets/images/imgly_sdk/ui/night/filters/fixie.png +0 -0
  21. data/app/assets/images/imgly_sdk/ui/night/filters/food.png +0 -0
  22. data/app/assets/images/imgly_sdk/ui/night/filters/fridge.png +0 -0
  23. data/app/assets/images/imgly_sdk/ui/night/filters/front.png +0 -0
  24. data/app/assets/images/imgly_sdk/ui/night/filters/glam.png +0 -0
  25. data/app/assets/images/imgly_sdk/ui/night/filters/gobblin.png +0 -0
  26. data/app/assets/images/imgly_sdk/ui/night/filters/identity.png +0 -0
  27. data/app/assets/images/imgly_sdk/ui/night/filters/k1.png +0 -0
  28. data/app/assets/images/imgly_sdk/ui/night/filters/k2.png +0 -0
  29. data/app/assets/images/imgly_sdk/ui/night/filters/k6.png +0 -0
  30. data/app/assets/images/imgly_sdk/ui/night/filters/kdynamic.png +0 -0
  31. data/app/assets/images/imgly_sdk/ui/night/filters/lenin.png +0 -0
  32. data/app/assets/images/imgly_sdk/ui/night/filters/lomo.png +0 -0
  33. data/app/assets/images/imgly_sdk/ui/night/filters/mellow.png +0 -0
  34. data/app/assets/images/imgly_sdk/ui/night/filters/morning.png +0 -0
  35. data/app/assets/images/imgly_sdk/ui/night/filters/orchid.png +0 -0
  36. data/app/assets/images/imgly_sdk/ui/night/filters/pola.png +0 -0
  37. data/app/assets/images/imgly_sdk/ui/night/filters/pola669.png +0 -0
  38. data/app/assets/images/imgly_sdk/ui/night/filters/quozi.png +0 -0
  39. data/app/assets/images/imgly_sdk/ui/night/filters/semired.png +0 -0
  40. data/app/assets/images/imgly_sdk/ui/night/filters/sunny.png +0 -0
  41. data/app/assets/images/imgly_sdk/ui/night/filters/texas.png +0 -0
  42. data/app/assets/images/imgly_sdk/ui/night/filters/x400.png +0 -0
  43. data/app/assets/images/imgly_sdk/ui/night/flip/horizontal.png +0 -0
  44. data/app/assets/images/imgly_sdk/ui/night/flip/vertical.png +0 -0
  45. data/app/assets/images/imgly_sdk/ui/night/loading.gif +0 -0
  46. data/app/assets/images/imgly_sdk/ui/night/operations/brightness.png +0 -0
  47. data/app/assets/images/imgly_sdk/ui/night/operations/contrast.png +0 -0
  48. data/app/assets/images/imgly_sdk/ui/night/operations/crop.png +0 -0
  49. data/app/assets/images/imgly_sdk/ui/night/operations/filters.png +0 -0
  50. data/app/assets/images/imgly_sdk/ui/night/operations/flip.png +0 -0
  51. data/app/assets/images/imgly_sdk/ui/night/operations/focus.png +0 -0
  52. data/app/assets/images/imgly_sdk/ui/night/operations/frames.png +0 -0
  53. data/app/assets/images/imgly_sdk/ui/night/operations/orientation.png +0 -0
  54. data/app/assets/images/imgly_sdk/ui/night/operations/radial-blur.png +0 -0
  55. data/app/assets/images/imgly_sdk/ui/night/operations/rotation.png +0 -0
  56. data/app/assets/images/imgly_sdk/ui/night/operations/saturation.png +0 -0
  57. data/app/assets/images/imgly_sdk/ui/night/operations/stickers.png +0 -0
  58. data/app/assets/images/imgly_sdk/ui/night/operations/text.png +0 -0
  59. data/app/assets/images/imgly_sdk/ui/night/operations/tilt-shift.png +0 -0
  60. data/app/assets/images/imgly_sdk/ui/night/rotation/left.png +0 -0
  61. data/app/assets/images/imgly_sdk/ui/night/rotation/right.png +0 -0
  62. data/app/assets/images/imgly_sdk/ui/night/slider/minus.png +0 -0
  63. data/app/assets/images/imgly_sdk/ui/night/slider/plus.png +0 -0
  64. data/app/assets/images/imgly_sdk/ui/night/top/aspect.png +0 -0
  65. data/app/assets/images/imgly_sdk/ui/night/top/new.png +0 -0
  66. data/app/assets/images/imgly_sdk/ui/night/top/original.png +0 -0
  67. data/app/assets/images/imgly_sdk/ui/night/top/undo.png +0 -0
  68. data/app/assets/images/imgly_sdk/ui/night/top/zoom-in.png +0 -0
  69. data/app/assets/images/imgly_sdk/ui/night/top/zoom-out.png +0 -0
  70. data/app/assets/images/imgly_sdk/ui/night/transparency.png +0 -0
  71. data/app/assets/images/imgly_sdk/ui/night/upload.png +0 -0
  72. data/app/assets/javascripts/canvas-to-blob.js +95 -0
  73. data/app/assets/javascripts/imglykit.js +19998 -0
  74. data/app/assets/javascripts/scrivito_editors/binary_editor.js.coffee +21 -18
  75. data/app/assets/javascripts/scrivito_editors/date_editor.js.coffee +15 -6
  76. data/app/assets/javascripts/scrivito_editors/helpers/file_dropzone.js.coffee +11 -5
  77. data/app/assets/javascripts/scrivito_editors/helpers/setup_binary_button_container.js.coffee +6 -0
  78. data/app/assets/javascripts/scrivito_editors/html_editor.js.coffee +1 -0
  79. data/app/assets/javascripts/scrivito_editors/{image_editor.js.coffee → image_drop_editor.js.coffee} +1 -1
  80. data/app/assets/javascripts/scrivito_editors/image_editor.js.coffee.erb +76 -0
  81. data/app/assets/javascripts/scrivito_editors/reload.js.coffee +12 -0
  82. data/app/assets/javascripts/scrivito_editors/slider_editor.js.coffee +1 -1
  83. data/app/assets/javascripts/scrivito_editors/string_editor.js.coffee +6 -3
  84. data/app/assets/javascripts/scrivito_editors/stringlist_editor.js.coffee +13 -0
  85. data/app/assets/javascripts/scrivito_editors_core.js +2 -0
  86. data/app/assets/stylesheets/imglykit-night-ui.css +1098 -0
  87. data/app/assets/stylesheets/scrivito-editors-imglykit-override.css +9 -0
  88. data/app/assets/stylesheets/scrivito_editors.css +2 -0
  89. data/app/assets/stylesheets/scrivito_editors/editors/button_container.css +29 -0
  90. data/app/assets/stylesheets/scrivito_editors/editors/{image_editor.css → image_drop_editor.css} +0 -0
  91. data/app/assets/stylesheets/scrivito_editors/icons.css.erb +1 -54
  92. data/app/assets/stylesheets/scrivito_editors/stringlist_editor.css +24 -0
  93. data/lib/scrivito_editors/engine.rb +20 -0
  94. data/vendor/assets/javascripts/jquery.caret.js +2 -0
  95. data/vendor/assets/javascripts/jquery.tag-editor.js +340 -0
  96. data/vendor/assets/stylesheets/jquery.tag-editor.css +49 -0
  97. metadata +91 -10
  98. data/Rakefile +0 -16
  99. data/app/assets/stylesheets/scrivito_editors/binary_editor.css +0 -14
@@ -0,0 +1,9 @@
1
+ /* override for scrivito */
2
+ .image_kit_start {height: 100%;}
3
+ .imglykit,
4
+ .imglykit-container {border-radius:0!important;}
5
+ .imglykit-header-row,
6
+ .imglykit-container .imglykit-header {display:none!important;}
7
+ .imglykit-container .imglykit-canvas-container.imglykit-header-padding {padding-top:40px;}
8
+ .imglykit-container .imglykit-top-controls.imglykit-header-padding {top:0;}
9
+ .imglykit-container .imglykit-controls img {display: inline;}
@@ -12,5 +12,7 @@
12
12
  *= require jquery-ui/slider
13
13
  *= require jquery-ui-timepicker-addon.min
14
14
  *= require redactor
15
+ *= require imglykit-night-ui
16
+ *= require scrivito-editors-imglykit-override
15
17
  *= require_tree .
16
18
  */
@@ -0,0 +1,29 @@
1
+
2
+ .scrivito-editor-image-button-container {
3
+ display:inline-block; position: relative;
4
+ max-width:100%;
5
+ }
6
+ .scrivito-editor-binary-button-container:not(.scrivito-editor-image-button-container) {
7
+ display:block; position: relative;
8
+ background: rgba(0,0,0,.1);
9
+ box-shadow: 0 0 0 2px rgba(0,0,0,.2);
10
+ min-height: 90px; padding:5px;
11
+ }
12
+ .scrivito-editor-image-button-container .editing-button,
13
+ .scrivito-editor-binary-button-container .editing-button {
14
+ padding: 5px 8px; margin: 0px;
15
+ position: absolute; right: 10px; top: 10px;
16
+ background: rgba(0,0,0,.7);
17
+ border:1px solid rgba(255, 255, 255, 0.3);
18
+ box-shadow: 0 0 3px 2px rgba(255, 255, 255, 0.2);
19
+ }
20
+ .scrivito-editor-image-button-container .editing-button:hover,
21
+ .scrivito-editor-binary-button-container .editing-button:hover {
22
+ background: rgba(88,88,88,.8);
23
+ }
24
+ .scrivito-editor-image-button-container .editing-button + .editing-button,
25
+ .scrivito-editor-binary-button-container .editing-button + .editing-button {
26
+ right: 50px;
27
+ }
28
+ .scrivito-editor-image-button-container .editing-button .editing-icon,
29
+ .scrivito-editor-binary-button-container .editing-button .editing-icon { color:#fff;}
@@ -25,60 +25,7 @@
25
25
  }
26
26
 
27
27
  /* kept for backwards compatibility reasons */
28
+ .editing-icon-edit:after { content: "\F026"; }
28
29
  .editing-icon-search:after { content: "\F043"; }
29
30
  .editing-icon-trash:after { content: "\F038"; }
30
31
  .editing-icon-plus:after { content: "\F044"; }
31
-
32
- /* deprecated */
33
- .editing-icon-ip:after,
34
- .editing-icon-text:after,
35
- .editing-icon-image:after,
36
- .editing-icon-headline:after,
37
- .editing-icon-video:after,
38
- .editing-icon-map:after,
39
- .editing-icon-table:after,
40
- .editing-icon-slider:after,
41
- .editing-icon-list:after,
42
- .editing-icon-downloads:after,
43
- .editing-icon-person:after,
44
- .editing-icon-presentation:after,
45
- .editing-icon-login:after,
46
- .editing-icon-teaser:after,
47
- .editing-icon-interactive:after,
48
- .editing-icon-hr:after,
49
- .editing-icon-1col:after,
50
- .editing-icon-2cols:after,
51
- .editing-icon-2colsl:after,
52
- .editing-icon-2colsr:after,
53
- .editing-icon-3cols:after,
54
- .editing-icon-workspaces:after,
55
- .editing-icon-tag:after,
56
- .editing-icon-link:after,
57
- .editing-icon-box:after,
58
- .editing-icon-puzzle:after,
59
- .editing-icon-dates:after,
60
- .editing-icon-newsletter:after,
61
- .editing-icon-special:after,
62
- .editing-icon-edit:after,
63
- .editing-icon-ok:after,
64
- .editing-icon-cancel:after,
65
- .editing-icon-eye:after,
66
- .editing-icon-ok-box:after,
67
- .editing-icon-group:after,
68
- .editing-icon-generic:after,
69
- .editing-icon-pdf:after,
70
- .editing-icon-chevron-up:after,
71
- .editing-icon-chevron-down:after,
72
- .editing-icon-chevron-left:after,
73
- .editing-icon-chevron-right:after,
74
- .editing-icon-refresh:after,
75
- .editing-icon-reorder:after,
76
- .editing-icon-th-small:after,
77
- .editing-icon-th-medium:after,
78
- .editing-icon-th:after,
79
- .editing-icon-th-large:after {
80
- content: "\F000";
81
- font-family: 'scrivito_content_browser_iconsregular';
82
- vertical-align: top;
83
- font-size: 110px;
84
- }
@@ -0,0 +1,24 @@
1
+ /*
2
+ *= require jquery.tag-editor
3
+ */
4
+
5
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-delete { padding-right: 10px; }
6
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-delete i {
7
+ -moz-osx-font-smoothing: grayscale;
8
+ -webkit-font-smoothing: antialiased;
9
+ color: #666;
10
+ font-family: "scrivito_customer_icons";
11
+ font-size: 10px;
12
+ font-style: normal;
13
+ font-weight: normal;
14
+ line-height: 9px!important;
15
+ text-decoration: none;
16
+ vertical-align: middle;
17
+ }
18
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-delete i { background: none; }
19
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-delete i:after { content: "\F03c"; }
20
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-tag.active+.tag-editor-delete,
21
+ [data-scrivito-field-type=stringlist]+.tag-editor .tag-editor-tag.active+.tag-editor-delete i:after {
22
+ content: "";
23
+ cursor: text;
24
+ }
@@ -9,5 +9,25 @@ module ScrivitoEditors
9
9
  ActionView::Base.send :include, CmsTagHelper
10
10
  ActionView::Base.send :include, ScrivitoTagHelper
11
11
  end
12
+
13
+ class << self
14
+ def imgly_assets
15
+ imgly_assets_paths.inject({}) do |asset_map, file|
16
+ asset_map[file] = yield(file)
17
+ asset_map
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def imgly_assets_paths
24
+ images_path = ScrivitoEditors::Engine.root + 'app/assets/images/'
25
+ absolute_paths = Dir[images_path + 'imgly_sdk/**/*'].select { |f| File.file?(f) }
26
+
27
+ absolute_paths.map do |absolute_path|
28
+ Pathname.new(absolute_path).relative_path_from(images_path).to_s
29
+ end
30
+ end
31
+ end
12
32
  end
13
33
  end
@@ -0,0 +1,2 @@
1
+ // http://code.accursoft.com/caret - 1.3.3
2
+ !function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery);
@@ -0,0 +1,340 @@
1
+ /*
2
+ jQuery tagEditor v1.0.14
3
+ Copyright (c) 2014 Simon Steinberger / Pixabay
4
+ GitHub: https://github.com/Pixabay/jQuery-tagEditor
5
+ License: http://www.opensource.org/licenses/mit-license.php
6
+ */
7
+
8
+ (function($){
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)};
11
+
12
+ // plugin with val as parameter for public methods
13
+ $.fn.tagEditor = function(options, val, blur){
14
+
15
+ // build options dictionary with default values
16
+ var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this;
17
+
18
+ // store regex and default delimiter in options for later use
19
+ o.dregex = new RegExp('['+o.delimiter.replace('-', '\-')+']', 'g');
20
+
21
+ // public methods
22
+ if (typeof options == 'string') {
23
+
24
+ // depending on selector, response may contain tag lists of multiple editor instances
25
+ var response = [];
26
+ selector.each(function(){
27
+ // the editor is the next sibling to the hidden, original field
28
+ var el = $(this), o = el.data('options'), ed = el.next('.tag-editor');
29
+ if (options == 'getTags')
30
+ response.push({field: el[0], editor: ed, tags: ed.data('tags')});
31
+ else if (options == 'addTag') {
32
+ // insert new tag
33
+ $('<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
+ .html('<input type="text" maxlength="'+o.maxLength+'">').addClass('active').find('input').val(val).blur();
35
+ if (!blur) ed.click();
36
+ else $('.placeholder', ed).remove();
37
+ } else if (options == 'removeTag') {
38
+ // 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();
40
+ if (!blur) ed.click();
41
+ } else if (options == 'destroy') {
42
+ el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove();
43
+ }
44
+ });
45
+ return options == 'getTags' ? response : this;
46
+ }
47
+
48
+ // delete selected tags on backspace, delete, ctrl+x
49
+ function delete_selected_tags(e){
50
+ if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) {
51
+ try {
52
+ var sel = getSelection(), el = $(sel.getRangeAt(0).commonAncestorContainer);
53
+ } catch(e){ el = 0; }
54
+ if (el && el.hasClass('tag-editor')) {
55
+ var tags = [], splits = sel.toString().split(el.prev().data('options').dregex);
56
+ for (i=0; i<splits.length; i++){ var tag = $.trim(splits[i]); if (tag) tags.push(tag); }
57
+ $('.tag-editor-tag', el).each(function(){
58
+ if (~$.inArray($(this).html(), tags)) $(this).closest('li').find('.tag-editor-delete').click();
59
+ });
60
+ }
61
+ }
62
+ }
63
+ if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', delete_selected_tags);
64
+
65
+ return selector.each(function(){
66
+ var el = $(this), tag_list = []; // cache current tags
67
+
68
+ // create editor (ed) instance
69
+ var ed = $('<ul '+(o.clickDelete ? 'oncontextmenu="return false;" ' : '')+'class="tag-editor"></ul>').insertAfter(el);
70
+ el.addClass('tag-editor-hidden-src') // hide original field
71
+ .data('options', o) // set data on hidden field
72
+ .on('focus.tag-editor', function(){ ed.click(); }); // simulate tabindex
73
+
74
+ // add dummy item for min-height on empty editor
75
+ ed.append('<li style="width:1px">&nbsp;</li>');
76
+
77
+ // markup for new tag
78
+ var new_tag = '<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>';
79
+
80
+ // helper: update global data
81
+ function set_placeholder(){
82
+ if (o.placeholder && !tag_list.length && !$('.deleted, .placeholder, input', ed).length)
83
+ ed.append('<li class="placeholder"><div>'+o.placeholder+'</div></li>');
84
+ }
85
+
86
+ // helper: update global data
87
+ function update_globals(init){
88
+ var old_tags = tag_list.toString();
89
+ tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function(i, e) {
90
+ var val = $.trim($(this).hasClass('active') ? $(this).find('input').val() : $(e).text());
91
+ if (val) return val;
92
+ }).get();
93
+ ed.data('tags', tag_list);
94
+ el.val(tag_list.join(o.delimiter[0]));
95
+ // change callback except for plugin init
96
+ if (!init) if (old_tags != tag_list.toString()) o.onChange(el, ed, tag_list);
97
+ set_placeholder();
98
+ }
99
+
100
+ ed.click(function(e, closest_tag){
101
+ var d, dist = 99999, loc;
102
+
103
+ // do not create tag when user selects tags by text selection
104
+ if (window.getSelection && getSelection() != '') return;
105
+
106
+ blur_result = true
107
+ $('input:focus', ed).blur();
108
+ if (!blur_result) return false;
109
+ blur_result = true
110
+
111
+ // always remove placeholder on click
112
+ $('.placeholder', ed).remove();
113
+ if (closest_tag && closest_tag.length)
114
+ loc = 'before';
115
+ else {
116
+ // calculate tag closest to click position
117
+ $('.tag-editor-tag', ed).each(function(){
118
+ var tag = $(this), to = tag.offset(), tag_x = to.left, tag_y = to.top;
119
+ if (e.pageY >= tag_y && e.pageY <= tag_y+tag.height()) {
120
+ if (e.pageX < tag_x) loc = 'before', d = tag_x - e.pageX;
121
+ else loc = 'after', d = e.pageX - tag_x - tag.width();
122
+ if (d < dist) dist = d, closest_tag = tag;
123
+ }
124
+ });
125
+ }
126
+
127
+ if (loc == 'before') {
128
+ $(new_tag).insertBefore(closest_tag.closest('li')).find('.tag-editor-tag').click();
129
+ } else if (loc == 'after')
130
+ $(new_tag).insertAfter(closest_tag.closest('li')).find('.tag-editor-tag').click();
131
+ else // empty editor
132
+ $(new_tag).appendTo(ed).find('.tag-editor-tag').click();
133
+ return false;
134
+ });
135
+
136
+ ed.on('click', '.tag-editor-delete', function(e){
137
+ // delete icon is hidden when input is visible; place cursor near invisible delete icon on click
138
+ if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; }
139
+
140
+ 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(); });
143
+ update_globals();
144
+ return false;
145
+ });
146
+
147
+ // delete on right mouse click or ctrl+click
148
+ if (o.clickDelete)
149
+ ed.on('mousedown', '.tag-editor-tag', function(e){
150
+ if (e.ctrlKey || e.which > 1) {
151
+ 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(); });
154
+ update_globals();
155
+ return false;
156
+ }
157
+ });
158
+
159
+ ed.on('click', '.tag-editor-tag', function(e){
160
+ // delete on right click or ctrl+click -> exit
161
+ if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false;
162
+
163
+ if (!$(this).hasClass('active')) {
164
+ var tag = $(this).html();
165
+ // guess cursor position in text input
166
+ 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');
168
+ input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos);
169
+ if (o.autocomplete) {
170
+ var aco = $.extend({}, o.autocomplete);
171
+ // extend user provided autocomplete select method
172
+ var ac_select = 'select' in aco ? o.autocomplete.select : '';
173
+ aco.select = function(e, ui){ if (ac_select) ac_select(e, ui); setTimeout(function(){
174
+ ed.trigger('click', [$('.active', ed).find('input').closest('li').next('li').find('.tag-editor-tag')]);
175
+ }, 20); };
176
+ input.autocomplete(aco);
177
+ }
178
+ }
179
+ return false;
180
+ });
181
+
182
+ // helper: split into multiple tags, e.g. after paste
183
+ 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++) {
187
+ 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
+ }
197
+ }
198
+ input.attr('maxlength', o.maxLength).removeData('old_tag').val('').focus();
199
+ update_globals();
200
+ }
201
+
202
+ ed.on('blur', 'input', function(e){
203
+ var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0]));
204
+ if (!tag) {
205
+ if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) {
206
+ input.val(old_tag).focus();
207
+ blur_result = false;
208
+ update_globals();
209
+ return;
210
+ }
211
+ try { input.closest('li').remove(); } catch(e){}
212
+ if (old_tag) update_globals();
213
+ }
214
+ else if (tag.indexOf(o.delimiter[0])>=0) { split_cleanup(input); return; }
215
+ else if (tag != old_tag) {
216
+ if (o.forceLowercase) tag = tag.toLowerCase();
217
+ tag = o.beforeTagSave(el, ed, tag_list, old_tag, tag) || tag;
218
+ // remove duplicates
219
+ $('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).html() == tag) $(this).closest('li').remove(); });
220
+ }
221
+ input.parent().html(tag).removeClass('active');
222
+ if (tag != old_tag) update_globals();
223
+ set_placeholder();
224
+ });
225
+
226
+ var pasted_content;
227
+ ed.on('paste', 'input', function(e){
228
+ $(this).removeAttr('maxlength');
229
+ pasted_content = $(this);
230
+ setTimeout(function(){ split_cleanup(pasted_content); }, 30);
231
+ });
232
+
233
+ // keypress delimiter
234
+ var inp;
235
+ ed.on('keypress', 'input', function(e){
236
+ if (o.delimiter.indexOf(String.fromCharCode(e.which))>=0) {
237
+ inp = $(this);
238
+ setTimeout(function(){ split_cleanup(inp); }, 20);
239
+ }
240
+ });
241
+
242
+ ed.on('keydown', 'input', function(e){
243
+ var $t = $(this);
244
+
245
+ // left/up key + backspace key on empty field
246
+ if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) {
247
+ var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
248
+ 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();
250
+ return false;
251
+ }
252
+ // right/down key
253
+ else if ((e.which == 39 || !o.autocomplete && e.which == 40) && ($t.caret() == $t.val().length)) {
254
+ var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
255
+ if (next_tag.length) next_tag.click().find('input').caret(0);
256
+ else if ($t.val()) ed.click();
257
+ return false;
258
+ }
259
+ // tab key
260
+ else if (e.which == 9) {
261
+ // shift+tab
262
+ if (e.shiftKey) {
263
+ var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
264
+ 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();
266
+ // allow tabbing to previous element
267
+ else {
268
+ el.attr('disabled', 'disabled');
269
+ setTimeout(function(){ el.removeAttr('disabled'); }, 30);
270
+ return;
271
+ }
272
+ return false;
273
+ // tab
274
+ } else {
275
+ var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
276
+ if (next_tag.length) next_tag.click().find('input').caret(0);
277
+ else if ($t.val()) ed.click();
278
+ else return; // allow tabbing to next element
279
+ return false;
280
+ }
281
+ }
282
+ // del key
283
+ else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret() == $t.val().length))) {
284
+ var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
285
+ if (next_tag.length) next_tag.click().find('input').caret(0);
286
+ else if ($t.val()) ed.click();
287
+ return false;
288
+ }
289
+ // enter key
290
+ else if (e.which == 13) {
291
+ ed.trigger('click', [$t.closest('li').next('li').find('.tag-editor-tag')]);
292
+ return false;
293
+ }
294
+ // pos1
295
+ else if (e.which == 36 && !$t.caret()) ed.find('.tag-editor-tag').first().click();
296
+ // end
297
+ else if (e.which == 35 && $t.caret() == $t.val().length) ed.find('.tag-editor-tag').last().click();
298
+ // esc
299
+ else if (e.which == 27) {
300
+ $t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur();
301
+ return false;
302
+ }
303
+ });
304
+
305
+ // create initial tags
306
+ var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex);
307
+ for (i=0; i<tags.length; i++) {
308
+ var tag = $.trim(tags[i].replace(/ +/, ' '));
309
+ if (tag) {
310
+ if (o.forceLowercase) tag = tag.toLowerCase();
311
+ 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>');
313
+ }
314
+ }
315
+ update_globals(true); // true -> no onChange callback
316
+
317
+ // init sortable
318
+ if (o.sortable && $.fn.sortable) ed.sortable({
319
+ distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone',
320
+ update: function(){ update_globals(); }
321
+ });
322
+ });
323
+ };
324
+
325
+ $.fn.tagEditor.defaults = {
326
+ initialTags: [],
327
+ maxLength: 50,
328
+ delimiter: ',;',
329
+ placeholder: '',
330
+ forceLowercase: true,
331
+ clickDelete: false,
332
+ sortable: true, // jQuery UI sortable
333
+ autocomplete: null, // options dict for jQuery UI autocomplete
334
+
335
+ // callbacks
336
+ onChange: function(){},
337
+ beforeTagSave: function(){},
338
+ beforeTagDelete: function(){}
339
+ };
340
+ }(jQuery));