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 }