Mercurial > defr > drupal > scald > dnd
comparison js/dnd-library.js @ 31:767ebf925654
Added forcecontainer tinymce plugin, lots and lots and lots of refactorizing.
author | David Eads <eads@chicagotech.org> |
---|---|
date | Thu, 19 Mar 2009 15:58:36 -0500 |
parents | 2d49adbd8992 |
children | ee520ba7d98b |
comparison
equal
deleted
inserted
replaced
30:2d49adbd8992 | 31:767ebf925654 |
---|---|
15 * to make this work in IE. | 15 * to make this work in IE. |
16 */ | 16 */ |
17 (function($) { | 17 (function($) { |
18 // Custom selectors | 18 // Custom selectors |
19 $.extend($.expr[":"], { | 19 $.extend($.expr[":"], { |
20 'empty' : function(a, i, m) { | 20 'dnd_empty' : function(a, i, m) { |
21 return !$(a).filter(function(i) { | 21 return !$(a).filter(function(i) { |
22 return !$(this).is('br'); | 22 return !$(this).is('br'); |
23 }).length && !$.trim(a.textContent || a.innerText||$(a).text() || ""); | 23 }).length && !$.trim(a.textContent || a.innerText||$(a).text() || ""); |
24 } | 24 } |
25 }); | 25 }); |
36 // This is a bad hack to lop off '-dnd-library' from the id to get the editor name | 36 // This is a bad hack to lop off '-dnd-library' from the id to get the editor name |
37 var $editor = $('#' + this.id.slice(0, -12)); | 37 var $editor = $('#' + this.id.slice(0, -12)); |
38 | 38 |
39 // Set up some initial settings for BeautyTips | 39 // Set up some initial settings for BeautyTips |
40 var settings = Drupal.settings.dndEnabledLibraries[$editor.get(0).id] = $.extend({ | 40 var settings = Drupal.settings.dndEnabledLibraries[$editor.get(0).id] = $.extend({ |
41 'btSettings' : { | |
41 'trigger': 'none', | 42 'trigger': 'none', |
42 'width': 375, | 43 'width': 375, |
43 'spikeLength': 7, | 44 'spikeLength': 7, |
44 'spikeGirth': 9, | 45 'spikeGirth': 9, |
45 'corner-radius' : 3, | 46 'corner-radius' : 3, |
46 'strokeWidth': 1, | 47 'strokeWidth': 1, |
47 'fill': '#ffd', | 48 'fill': '#ffd', |
48 'strokeStyle': '#555' | 49 'strokeStyle': '#555' |
50 }, | |
51 'libraryHoverIntentSettings' : { | |
52 'interval': 500, | |
53 'timeout' : 0, | |
54 'over': function() { | |
55 var $this = $(this); | |
56 this.btOn(); | |
57 // Remove the preview once dragging of any image has commenced | |
58 $('img', $this).bind('drag', function(e) { | |
59 $this.btOff(); | |
60 }); | |
61 $('img', $this).bind('click', function(e) { | |
62 $this.btOff(); | |
63 }); | |
64 }, | |
65 'out': function() { this.btOff(); } | |
66 } | |
49 }, Drupal.settings.dndEnabledLibraries[$editor.get(0).id]); | 67 }, Drupal.settings.dndEnabledLibraries[$editor.get(0).id]); |
50 | 68 |
51 // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg | 69 // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg |
52 $editor.bind('wysiwygAttach', Drupal.behaviors.dndLibrary.attach_library); | 70 $editor.bind('wysiwygAttach', Drupal.behaviors.dndLibrary.attach_library); |
53 $editor.bind('wysiwygDetach', Drupal.behaviors.dndLibrary.detach_library); | 71 $editor.bind('wysiwygDetach', Drupal.behaviors.dndLibrary.detach_library); |
54 | 72 |
55 // Set up empty objects to keep track of things | 73 // Set up empty objects to keep track of things |
56 Drupal.settings.dndEditorRepresentations = {}; | 74 Drupal.settings.dndEditorRepresentations = {}; |
57 Drupal.settings.dndLibraryPreviews = {}; | 75 Drupal.settings.dndLibraryPreviews = {}; |
58 | 76 |
59 // Populate | 77 // Initialize the library |
60 $.getJSON(Drupal.settings.basePath + settings.url, function(data) { | 78 $.getJSON(Drupal.settings.basePath + settings.url, function(data) { |
61 Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $editor); | 79 Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $editor); |
62 }); | 80 }); |
63 | 81 |
64 }); | 82 }); |
82 Drupal.settings.dndLibraryPreviews[preview_id] = data.library_previews[preview_id]; | 100 Drupal.settings.dndLibraryPreviews[preview_id] = data.library_previews[preview_id]; |
83 } | 101 } |
84 | 102 |
85 // Add preview behavior to editor items (thanks, BeautyTips!) | 103 // Add preview behavior to editor items (thanks, BeautyTips!) |
86 $('.editor-item', $this).each(function () { | 104 $('.editor-item', $this).each(function () { |
87 $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], settings.bt_settings); | 105 $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], settings.btSettings); |
88 var hover_opts = $.extend({ | 106 $(this).hoverIntent(settings.libraryHoverIntentSettings); |
89 'interval': 500, | |
90 'timeout' : 0, | |
91 'over': function() { | |
92 var $this = $(this); | |
93 this.btOn(); | |
94 // Remove the preview once dragging of any image has commenced | |
95 $('img', $this).bind('drag', function(e) { | |
96 $this.btOff(); | |
97 }); | |
98 $('img', $this).bind('click', function(e) { | |
99 $this.btOff(); | |
100 }); | |
101 }, | |
102 'out': function() { this.btOff(); } | |
103 }, settings.libraryHoverIntentOpts); | |
104 $(this).hoverIntent(hover_opts); | |
105 }); | 107 }); |
106 | 108 |
107 // Preload images in editor representations | 109 // Preload images in editor representations |
108 var cached = $.data($(editor), 'dnd_preload') || {}; | 110 var cached = $.data($(editor), 'dnd_preload') || {}; |
109 /*for (editor_id in Drupal.settings.dndEditorRepresentations) { | 111 for (editor_id in Drupal.settings.dndEditorRepresentations) { |
110 if (!cached[editor_id]) { | 112 if (!cached[editor_id]) { |
111 $representation = $(Drupal.settings.dndEditorRepresentations[editor_id].body); | 113 $representation = $(Drupal.settings.dndEditorRepresentations[editor_id].body); |
112 if ($representation.is('img') && $representation.get(0).src) { | 114 if ($representation.is('img') && $representation.get(0).src) { |
113 $representation.attr('src', $representation.get(0).src); | 115 $representation.attr('src', $representation.get(0).src); |
114 } else { | 116 } else { |
115 $('img', $representation).each(function() { | 117 $('img', $representation).each(function() { |
116 this.attr('src', this.src); | 118 $(this).attr('src', this.src); |
117 }); | 119 }); |
118 } | 120 } |
119 } | 121 } |
120 } | 122 } |
121 $.data($(editor), 'dnd_preload', cached);*/ | 123 $.data($(editor), 'dnd_preload', cached); |
122 | 124 |
123 } | 125 $('.pager a', $this).click(function() { |
124 | 126 $.getJSON(this.href, function(data) { |
127 Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); | |
128 }); | |
129 return false; | |
130 }); | |
131 $('.view-filters input[type=submit]', $this).click(function() { | |
132 $(this).ajaxSubmit({ | |
133 'url' : Drupal.settings.basePath + settings.url, | |
134 'dataType' : 'json', | |
135 'success' : function(data) { | |
136 Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); | |
137 } | |
138 }); | |
139 return false; | |
140 }); | |
141 } | |
125 | 142 |
126 // Dynamically compose a callback based on the editor name | 143 // Dynamically compose a callback based on the editor name |
127 Drupal.behaviors.dndLibrary.attach_library = function(e, data) { | 144 Drupal.behaviors.dndLibrary.attach_library = function(e, data) { |
128 var settings = $.extend({idSelector: Drupal.behaviors.dndLibrary.idSelector}, Drupal.settings.dndEnabledLibraries[data.field]); | 145 var settings = $.extend({idSelector: Drupal.behaviors.dndLibrary.idSelector}, Drupal.settings.dndEnabledLibraries[data.field]); |
129 var editor_fn = 'attach_' + data.editor; | 146 var editor_fn = 'attach_' + data.editor; |
131 window.Drupal.behaviors.dndLibrary[editor_fn](data, settings); | 148 window.Drupal.behaviors.dndLibrary[editor_fn](data, settings); |
132 } | 149 } |
133 } | 150 } |
134 | 151 |
135 // Do garbage collection on detach | 152 // Do garbage collection on detach |
136 Drupal.behaviors.dndLibrary.detach_library = function(e, data) { | 153 Drupal.behaviors.dndLibrary.detach_library = function(e, data) {} |
137 for (t in $(document).data('dnd_timers')) { | |
138 clearInterval(t); | |
139 } | |
140 $(document).removeData('dnd_timers'); | |
141 } | |
142 | 154 |
143 // Basic textareas | 155 // Basic textareas |
144 Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { | 156 Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { |
145 settings = $.extend({ | 157 settings = $.extend({ |
146 targets: $('#'+ data.field), | 158 targets: $('#'+ data.field), |
148 var target = this, $target = $(target); | 160 var target = this, $target = $(target); |
149 | 161 |
150 // Update element count | 162 // Update element count |
151 Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); | 163 Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); |
152 | 164 |
153 var snippet = '<p class="dnd-dropped-wrapper">' + Drupal.settings.dndEditorRepresentations[representation_id].body + '</p>'; | 165 var snippet = '<p class="dnd-drop-wrapper">' + Drupal.settings.dndEditorRepresentations[representation_id].body + '</p>'; |
154 $target.replaceSelection(snippet, true); | 166 $target.replaceSelection(snippet, true); |
155 } | 167 } |
156 }, settings); | 168 }, settings); |
157 $(settings.drop_selector).dnd(settings); | 169 $(settings.drop_selector).dnd(settings); |
158 } | 170 } |
188 Drupal.behaviors.dndLibrary._attach_tinymce = function(data, settings, tiny_instance) { | 200 Drupal.behaviors.dndLibrary._attach_tinymce = function(data, settings, tiny_instance) { |
189 var ed = tiny_instance, dom = ed.dom, s = ed.selection; | 201 var ed = tiny_instance, dom = ed.dom, s = ed.selection; |
190 | 202 |
191 settings = $.extend({ | 203 settings = $.extend({ |
192 targets: $('#'+ data.field +'-wrapper iframe'), | 204 targets: $('#'+ data.field +'-wrapper iframe'), |
205 processTargets: function(targets) { | |
206 return targets.each(function() { | |
207 var target = this | |
208 // Decrement counter on delete | |
209 $(target).bind('dnd_delete', function(e, data) { | |
210 Drupal.behaviors.dndLibrary.countElements(target, $(data.node).attr('dnd_id'), true); | |
211 }); | |
212 $('head', $(this).contents()).append('<style type="text/css">img { display: none; } img.dnd-dropped {display: block; }</style>'); | |
213 return this; | |
214 }); | |
215 }, | |
193 processIframeDrop: function(drop, id_selector) { | 216 processIframeDrop: function(drop, id_selector) { |
194 var representation_id = id_selector.call(this, drop); | 217 var representation_id = id_selector.call(this, drop); |
195 var representation = Drupal.settings.dndEditorRepresentations[representation_id].body; | 218 var representation = Drupal.settings.dndEditorRepresentations[representation_id].body; |
196 var target = this, $target = $(target), $drop = $(drop), block; | 219 var target = this, $target = $(target), $drop = $(drop), block; |
197 | 220 |
198 // Update element count | 221 // Update element count |
199 //Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); | 222 Drupal.behaviors.dndLibrary.countElements(target, representation_id); |
200 | 223 |
201 // Search through block level parents | 224 // Search through block level parents |
202 $drop.parents().each(function() { | 225 $drop.parents().each(function() { |
203 var $this = $(this); | 226 var $this = $(this); |
204 if ($this.css('display') == 'block') { | 227 if ($this.css('display') == 'block') { |
209 | 232 |
210 // Remove dropped item | 233 // Remove dropped item |
211 $drop.remove(); | 234 $drop.remove(); |
212 | 235 |
213 // Create an element to insert | 236 // Create an element to insert |
214 var insert = dom.create('p', {'class' : 'dnd-dropped-wrapper', 'id' : 'dnd-inserted'}, representation); | 237 var insert = dom.create('p', {'class' : 'dnd-drop-wrapper', 'id' : 'dnd-inserted'}, representation); |
215 | 238 |
216 // The no-parent case | 239 // The no-parent case |
217 if ($(block).is('body')) { | 240 if ($(block).is('body')) { |
218 s.setNode(insert); | 241 s.setNode(insert); |
219 } | 242 } |
221 var old_id = block.id; | 244 var old_id = block.id; |
222 block.id = 'target-block'; | 245 block.id = 'target-block'; |
223 $block = $('#target-block', $target.contents()); | 246 $block = $('#target-block', $target.contents()); |
224 | 247 |
225 // @TODO is finding the parent broken in safari?? | 248 // @TODO is finding the parent broken in safari?? |
226 $block.after('<p class="dnd-dropped-wrapper" id="dnd-inserted">' + representation + '</p>'); | 249 $block.after('<p class="dnd-drop-wrapper" id="dnd-inserted">' + representation + '</p>'); |
227 | 250 |
228 // The active target block should be empty | 251 // The active target block should be empty |
229 if ($('#target-block:empty', $target.contents()).length > 0) { | 252 if ($('#target-block:dnd_empty', $target.contents()).length > 0) { |
230 $('#target-block', $target.contents()).remove(); | 253 $('#target-block', $target.contents()).remove(); |
231 } else if (old_id) { | 254 } else if (old_id) { |
232 block.id = old_id; | 255 block.id = old_id; |
233 } else { | 256 } else { |
234 $block.removeAttr('id'); | 257 $block.removeAttr('id'); |
235 } | 258 } |
236 } | 259 } |
237 | 260 |
238 var $inserted = $('#dnd-inserted', $target.contents()); | 261 var $inserted = $('#dnd-inserted', $target.contents()); |
239 var inserted = $inserted.get(0); | 262 var inserted = $inserted.get(0); |
240 | 263 |
241 // Look behind in the DOM | 264 // Look behind in the DOM |
242 var previous = $inserted.prev().get(0); | 265 var previous = $inserted.prev().get(0); |
243 | 266 |
244 // If the previous element is also an editor representation, we need to | 267 // If the previous element is also an editor representation, we need to |
245 // put a dummy paragraph between the elements to prevent editor errors. | 268 // put a dummy paragraph between the elements to prevent editor errors. |
246 if (previous && $(previous).hasClass('dnd-dropped-wrapper')) { | 269 if (previous ) { |
247 $inserted.before('<p><span id="__spacer">_</span></p>'); | 270 $inserted.before('<p></p>'); |
248 c = dom.get('__spacer'); | 271 } |
272 | |
273 // Look ahead in the DOM | |
274 var next = $inserted.next().get(0); | |
275 | |
276 // If the next item exists and isn't an editor representation, drop the | |
277 // caret at the beginning of the element, otherwise make a new paragraph | |
278 // to advance the caret to. | |
279 if (next && !$(next).hasClass('dnd-drop-wrapper')) { | |
280 $(next).prepend('<span id="__caret">_</span>'); | |
281 } | |
282 else if (!$(next).hasClass('dnd-drop-wrapper')) { | |
283 var after = dom.create('p', {}, '<span id="__caret">_</span>'); | |
284 dom.insertAfter(after, 'dnd-inserted'); | |
285 } | |
286 | |
287 // Force selection to reset the caret | |
288 var c = dom.get('__caret'); | |
289 if (c) { | |
249 s.select(c); | 290 s.select(c); |
250 ed.execCommand('Delete', false, null); | 291 ed.execCommand('Delete', false, null); |
251 dom.remove(c); | 292 dom.remove(c); |
252 } | 293 } |
253 | 294 |
254 // Look ahead in the DOM | 295 // Unset id for next drop and add special dnd attribute for counting |
255 var next = $inserted.next().get(0); | 296 // purposes |
256 | 297 $inserted |
257 // If the next item exists and isn't an editor representation, drop the | 298 .removeAttr('id') |
258 // caret at the beginning of the element, otherwise make a new paragraph | 299 .attr('dnd_id', representation_id); |
259 // to advance the caret to. | 300 |
260 if (next && !$(next).hasClass('dnd-dropped-wrapper')) { | |
261 $(next).prepend('<span id="__caret">_</span>'); | |
262 } | |
263 else { | |
264 var after = dom.create('p', {}, '<span id="__caret">_</span>'); | |
265 dom.insertAfter(after, 'dnd-inserted'); | |
266 } | |
267 | |
268 // Clear the ID for the next drop | |
269 $inserted.removeAttr('id'); | |
270 | |
271 // Force selection to reset the caret | |
272 var c = dom.get('__caret'); | |
273 s.select(c); | |
274 ed.execCommand('Delete', false, null); | |
275 dom.remove(c); | |
276 } | 301 } |
277 }, settings); | 302 }, settings); |
278 | 303 |
279 $(settings.drop_selector).dnd(settings); | 304 $(settings.drop_selector).dnd(settings); |
280 } | 305 } |
281 | 306 |
282 | 307 |
283 // Keep a counter of times a representation ID has been used | 308 // Keep a counter of times a representation ID has been used |
284 Drupal.behaviors.dndLibrary.countElements = function(representation_id) { | 309 Drupal.behaviors.dndLibrary.countElements = function(target, representation_id, decrement) { |
285 // We need to track element usage betwen all editors, so we look for the | 310 var counter = $(target).data('dnd_representation_counter'); |
286 // parent form item | |
287 $target = $(this).parents('.form-item'); | |
288 var counter = $target.data('representation_counter'); | |
289 if (!counter) { | 311 if (!counter) { |
290 counter = {} | 312 counter = {} |
291 counter[representation_id] = 1; | 313 counter[representation_id] = 1; |
292 } else if (counter && !counter[representation_id]) { | 314 } else if (counter && !counter[representation_id]) { |
293 counter[representation_id] = 1; | 315 counter[representation_id] = 1; |
294 } else { | 316 } else { |
295 counter[representation_id] = counter[representation_id] + 1; | 317 counter[representation_id] = counter[representation_id] + ((decrement) ? -1 : 1); |
296 } | 318 } |
297 $target.data('representation_counter', counter); | 319 $(target).data('dnd_representation_counter', counter); |
298 } | 320 return counter[representation_id]; |
321 } |