annotate js/dnd-library.js @ 16:bb68dc3ad56f

Major refactor to provide better TinyMCE support and less configuration options, added new jquery dependency, etc.
author David Eads <eads@chicagotech.org>
date Tue, 03 Mar 2009 16:57:39 -0600
parents 7a5f74482ee3
children 1a77f87927dd
rev   line source
eads@16 1 /**
eads@16 2 * Drag and Drop Library For Drupal
eads@16 3 *
eads@16 4 * This builds on the DnD jQuery plugin written to provide drag and drop media
eads@16 5 * handling to Rich Text Editors to consume, display, and attach behavior to
eads@16 6 * a "media library" provided via JSON and implemented for Drupal running
eads@16 7 * the Wysiwyg plugin.
eads@16 8 */
eads@16 9
eads@16 10 /**
eads@16 11 * Extend jQuery a bit
eads@16 12 *
eads@16 13 * We add a selector to look for "empty" elements (empty elements in TinyMCE
eads@16 14 * often have non-breaking spaces and <br /> tags).
eads@16 15 */
eads@16 16 (function($) {
eads@16 17 // Custom selectors
eads@16 18 $.extend($.expr[":"], {
eads@16 19 'empty' : function(a, i, m) {
eads@16 20 var text = $(a).html();
eads@16 21 text.replace(/\u00a0/g,''); // Remove &nbsp;
eads@16 22 $('br', $(text)).remove(); // Remove breaks
eads@16 23 return !$.trim(text);
eads@16 24 }
eads@16 25 });
eads@16 26 }) (jQuery);
eads@16 27
eads@2 28 Drupal.behaviors.dndLibrary = function(context) {
eads@16 29 $('.dnd-library-wrapper', context).each(function() {
eads@4 30 var $this = $(this);
eads@2 31
eads@4 32 // This is a bad hack to lop off '-dnd-library' from the id to get the editor name
eads@4 33 var editor = this.id.slice(0, -12);
eads@2 34
eads@16 35 // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg
eads@4 36 $('#' + editor).bind('wysiwygAttach', Drupal.behaviors.dndLibrary.attach_library);
eads@4 37 $('#' + editor).bind('wysiwygDetach', Drupal.behaviors.dndLibrary.detach_library);
eads@4 38
eads@4 39 // Ajax pager
eads@4 40 $('.pager a', $this).click(function(e, data) {
eads@2 41 $.getJSON(this.href, function(data) {
eads@4 42 $('.header', $this).html(data.header);
eads@4 43 $('.library', $this).html(data.library);
eads@4 44 //$('.footer', $this).html(data.footer);
eads@4 45 for (editor_id in data.editor_representations) {
eads@4 46 Drupal.settings.dndEditorRepresentations[editor_id] = data.editor_representations[editor_id];
eads@2 47 }
eads@4 48 var params = Drupal.wysiwyg.instances[editor];
eads@15 49 $('#' + editor).trigger('wysiwygDetach', params);
eads@4 50 $('#' + editor).trigger('wysiwygAttach', params);
eads@2 51 });
eads@2 52 return false;
eads@2 53 });
eads@4 54 });
eads@4 55 }
eads@2 56
eads@16 57 // Dynamically compose a callback based on the editor name
eads@4 58 Drupal.behaviors.dndLibrary.attach_library = function(e, data) {
eads@4 59 var editor_fn = 'attach_' + data.editor;
eads@4 60 if ($.isFunction(window.Drupal.behaviors.dndLibrary[editor_fn])) {
eads@16 61 window.Drupal.behaviors.dndLibrary[editor_fn](data, Drupal.settings.dndEnabledLibraries[data.field]);
eads@2 62 }
eads@2 63 }
eads@2 64
eads@16 65 // Do garbage collection on detach
eads@4 66 Drupal.behaviors.dndLibrary.detach_library = function(e, data) {
eads@15 67 for (t in $(document).data('dnd_timers')) {
eads@15 68 clearInterval(t);
eads@15 69 }
eads@15 70 $(document).removeData('dnd_timers');
eads@4 71 }
eads@4 72
eads@16 73 // Basic textareas
eads@16 74 Drupal.behaviors.dndLibrary.attach_none = function(data, settings) {
eads@16 75 settings = $.extend({
eads@16 76 targets: $('#'+ data.field),
eads@16 77 procressTextAreaDrop: function(target, clicked, representation_id, e, data) {
eads@16 78 var snippet = Drupal.settings.dndEditorRepresentations[representation_id];
eads@16 79 $(target).replaceSelection(snippet, true);
eads@16 80 }
eads@16 81 }, settings);
eads@16 82 $(settings.drop_selector).dnd(settings);
eads@16 83 }
eads@4 84
eads@16 85 // Attach TinyMCE
eads@2 86 Drupal.behaviors.dndLibrary.attach_tinymce = function(data, settings) {
eads@4 87 var tiny_instance = tinyMCE.getInstanceById(data.field);
eads@4 88
eads@4 89 // If the Tiny instance exists, attach directly, otherwise wait until Tiny
eads@4 90 // has registered a new instance.
eads@4 91 if (tiny_instance) {
eads@4 92 Drupal.behaviors.dndLibrary._attach_tinymce(data, settings, tiny_instance);
eads@4 93 } else {
eads@4 94 var t = setInterval(function() {
eads@4 95 var tiny_instance = tinyMCE.getInstanceById(data.field);
eads@4 96 if (tiny_instance) {
eads@4 97 Drupal.behaviors.dndLibrary._attach_tinymce(data, settings, tiny_instance);
eads@4 98 clearInterval(t);
eads@2 99 }
eads@4 100 }, 100);
eads@4 101 }
eads@4 102 }
eads@2 103
eads@16 104 // Really attach TinyMCE
eads@4 105 Drupal.behaviors.dndLibrary._attach_tinymce = function(data, settings, tiny_instance) {
eads@16 106 var ed = tiny_instance, dom = ed.dom, s = ed.selection;
eads@16 107
eads@4 108 settings = $.extend({
eads@4 109 targets: $('#'+ data.field +'-wrapper iframe'),
eads@16 110 idSelector: function(element) {
eads@16 111 if ($(element).is('img')) {
eads@16 112 return $.url.setUrl(element.src).param('dnd_id');
eads@16 113 }
eads@16 114 return false;
eads@16 115 },
eads@16 116 interval: 100,
eads@16 117 processIframeDrop: function(target, dragged, dropped, representation_id) {
eads@16 118 var representation = Drupal.settings.dndEditorRepresentations[representation_id];
eads@16 119 var $target = $(target), $dropped = $(dropped), $dragged = $(dragged), block;
eads@15 120
eads@16 121 // Search through block level parents
eads@16 122 $dropped.parents().each(function() {
eads@16 123 var $this = $(this);
eads@16 124 if ($this.css('display') == 'block') {
eads@16 125 block = this;
eads@16 126 return false;
eads@16 127 }
eads@16 128 });
eads@16 129
eads@16 130 // Remove dropped item
eads@16 131 $dropped.remove();
eads@16 132
eads@16 133 // Create an element to insert
eads@16 134 var insert = dom.create('p', {'class' : 'dnd-dropped-wrapper', 'id' : 'dnd-inserted'}, representation);
eads@16 135
eads@16 136 // The no-parent case
eads@16 137 if ($(block).is('body')) {
eads@16 138
eads@16 139 s.setNode(insert);
eads@16 140 }
eads@16 141 else {
eads@16 142 var old_id = block.id;
eads@16 143 block.id = 'target-block';
eads@16 144 $block = $('#target-block', $target.contents());
eads@16 145
eads@16 146 // @TODO is finding the parent broken in safari??
eads@16 147 $block.after('<p class="dnd-dropped-wrapper" id="dnd-inserted">' + representation + '</p>');
eads@16 148
eads@16 149 // The active target block should be empty
eads@16 150 if ($('#target-block:empty', $target.contents()).length > 0) {
eads@16 151 var c = dom.get('target-block');
eads@16 152 s.select(dom.get('target-block'));
eads@16 153 ed.execCommand('Delete', false, null);
eads@16 154 dom.remove(c);
eads@16 155 } else if (old_id) {
eads@16 156 block.id = old_id;
eads@16 157 } else {
eads@16 158 $block.removeAttr('id');
eads@16 159 }
eads@16 160 }
eads@16 161
eads@16 162 var $inserted = $('#dnd-inserted', $target.contents());
eads@16 163 var inserted = $inserted.get(0);
eads@16 164
eads@16 165 // Look behind in the DOM
eads@16 166 var previous = $inserted.prev().get(0);
eads@16 167
eads@16 168 // If the previous element is also an editor representation, we need to
eads@16 169 // put a dummy paragraph between the elements to prevent editor errors.
eads@16 170 if (previous && $(previous).hasClass('dnd-dropped-wrapper')) {
eads@16 171 $inserted.before('<p><span id="__spacer">_</span></p>');
eads@16 172 c = dom.get('__spacer');
eads@16 173 s.select(c);
eads@16 174 ed.execCommand('Delete', false, null);
eads@16 175 dom.remove(c);
eads@16 176 }
eads@16 177
eads@16 178 // Look ahead in the DOM
eads@16 179 var next = $inserted.next().get(0);
eads@16 180
eads@16 181 // If the next item exists and isn't an editor representation, drop the
eads@16 182 // caret at the beginning of the element, otherwise make a new paragraph
eads@16 183 // to advance the caret to.
eads@16 184 if (next && !$(next).hasClass('dnd-dropped-wrapper')) {
eads@16 185 $(next).prepend('<span id="__caret">_</span>');
eads@16 186 }
eads@16 187 else {
eads@16 188 var after = dom.create('p', {}, '<span id="__caret">_</span>');
eads@16 189 dom.insertAfter(after, 'dnd-inserted');
eads@16 190 }
eads@16 191
eads@16 192 // Clear the ID for the next drop
eads@16 193 $inserted.removeAttr('id');
eads@16 194
eads@16 195 // Force selection to reset the caret
eads@16 196 var c = dom.get('__caret');
eads@16 197 s.select(c);
eads@16 198 ed.execCommand('Delete', false, null);
eads@16 199 dom.remove(c);
eads@4 200
eads@4 201 // Add some classes to the library items
eads@16 202 $(dragged).addClass('dnd-inserted');
eads@16 203 drag_parents = $(dragged).parents('.editor-item');
eads@16 204 drag_parents.addClass('dnd-child-inserted');
eads@4 205 }
eads@4 206 }, settings);
eads@4 207
eads@4 208 $(settings.drop_selector).dnd(settings);
eads@2 209 }