Mercurial > defr > drupal > scald > dnd
view 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 |
line wrap: on
line source
/** * Drag and Drop Library For Drupal * * This builds on the DnD jQuery plugin written to provide drag and drop media * handling to Rich Text Editors to consume, display, and attach behavior to * a "media library" provided via JSON and implemented for Drupal running * the Wysiwyg plugin. */ /** * Extend jQuery a bit * * We add a selector to look for "empty" elements (empty elements in TinyMCE * often have non-breaking spaces and <br /> tags). */ (function($) { // Custom selectors $.extend($.expr[":"], { 'empty' : function(a, i, m) { var text = $(a).html(); text.replace(/\u00a0/g,''); // Remove $('br', $(text)).remove(); // Remove breaks return !$.trim(text); } }); }) (jQuery); Drupal.behaviors.dndLibrary = function(context) { $('.dnd-library-wrapper', context).each(function() { var $this = $(this); // This is a bad hack to lop off '-dnd-library' from the id to get the editor name var editor = this.id.slice(0, -12); // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg $('#' + editor).bind('wysiwygAttach', Drupal.behaviors.dndLibrary.attach_library); $('#' + editor).bind('wysiwygDetach', Drupal.behaviors.dndLibrary.detach_library); // Ajax pager $('.pager a', $this).click(function(e, data) { $.getJSON(this.href, function(data) { $('.header', $this).html(data.header); $('.library', $this).html(data.library); //$('.footer', $this).html(data.footer); for (editor_id in data.editor_representations) { Drupal.settings.dndEditorRepresentations[editor_id] = data.editor_representations[editor_id]; } var params = Drupal.wysiwyg.instances[editor]; $('#' + editor).trigger('wysiwygDetach', params); $('#' + editor).trigger('wysiwygAttach', params); }); return false; }); }); } // Dynamically compose a callback based on the editor name Drupal.behaviors.dndLibrary.attach_library = function(e, data) { var editor_fn = 'attach_' + data.editor; if ($.isFunction(window.Drupal.behaviors.dndLibrary[editor_fn])) { window.Drupal.behaviors.dndLibrary[editor_fn](data, Drupal.settings.dndEnabledLibraries[data.field]); } } // Do garbage collection on detach Drupal.behaviors.dndLibrary.detach_library = function(e, data) { for (t in $(document).data('dnd_timers')) { clearInterval(t); } $(document).removeData('dnd_timers'); } // Basic textareas Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { settings = $.extend({ targets: $('#'+ data.field), procressTextAreaDrop: function(target, clicked, representation_id, e, data) { var snippet = Drupal.settings.dndEditorRepresentations[representation_id]; $(target).replaceSelection(snippet, true); } }, settings); $(settings.drop_selector).dnd(settings); } // Attach TinyMCE Drupal.behaviors.dndLibrary.attach_tinymce = function(data, settings) { var tiny_instance = tinyMCE.getInstanceById(data.field); // If the Tiny instance exists, attach directly, otherwise wait until Tiny // has registered a new instance. if (tiny_instance) { Drupal.behaviors.dndLibrary._attach_tinymce(data, settings, tiny_instance); } else { var t = setInterval(function() { var tiny_instance = tinyMCE.getInstanceById(data.field); if (tiny_instance) { Drupal.behaviors.dndLibrary._attach_tinymce(data, settings, tiny_instance); clearInterval(t); } }, 100); } } // Really attach TinyMCE Drupal.behaviors.dndLibrary._attach_tinymce = function(data, settings, tiny_instance) { var ed = tiny_instance, dom = ed.dom, s = ed.selection; settings = $.extend({ targets: $('#'+ data.field +'-wrapper iframe'), idSelector: function(element) { if ($(element).is('img')) { return $.url.setUrl(element.src).param('dnd_id'); } return false; }, interval: 100, processIframeDrop: function(target, dragged, dropped, representation_id) { var representation = Drupal.settings.dndEditorRepresentations[representation_id]; var $target = $(target), $dropped = $(dropped), $dragged = $(dragged), block; // Search through block level parents $dropped.parents().each(function() { var $this = $(this); if ($this.css('display') == 'block') { block = this; return false; } }); // Remove dropped item $dropped.remove(); // Create an element to insert var insert = dom.create('p', {'class' : 'dnd-dropped-wrapper', 'id' : 'dnd-inserted'}, representation); // The no-parent case if ($(block).is('body')) { s.setNode(insert); } else { var old_id = block.id; block.id = 'target-block'; $block = $('#target-block', $target.contents()); // @TODO is finding the parent broken in safari?? $block.after('<p class="dnd-dropped-wrapper" id="dnd-inserted">' + representation + '</p>'); // The active target block should be empty if ($('#target-block:empty', $target.contents()).length > 0) { var c = dom.get('target-block'); s.select(dom.get('target-block')); ed.execCommand('Delete', false, null); dom.remove(c); } else if (old_id) { block.id = old_id; } else { $block.removeAttr('id'); } } var $inserted = $('#dnd-inserted', $target.contents()); var inserted = $inserted.get(0); // Look behind in the DOM var previous = $inserted.prev().get(0); // If the previous element is also an editor representation, we need to // put a dummy paragraph between the elements to prevent editor errors. if (previous && $(previous).hasClass('dnd-dropped-wrapper')) { $inserted.before('<p><span id="__spacer">_</span></p>'); c = dom.get('__spacer'); s.select(c); ed.execCommand('Delete', false, null); dom.remove(c); } // Look ahead in the DOM var next = $inserted.next().get(0); // If the next item exists and isn't an editor representation, drop the // caret at the beginning of the element, otherwise make a new paragraph // to advance the caret to. if (next && !$(next).hasClass('dnd-dropped-wrapper')) { $(next).prepend('<span id="__caret">_</span>'); } else { var after = dom.create('p', {}, '<span id="__caret">_</span>'); dom.insertAfter(after, 'dnd-inserted'); } // Clear the ID for the next drop $inserted.removeAttr('id'); // Force selection to reset the caret var c = dom.get('__caret'); s.select(c); ed.execCommand('Delete', false, null); dom.remove(c); // Add some classes to the library items $(dragged).addClass('dnd-inserted'); drag_parents = $(dragged).parents('.editor-item'); drag_parents.addClass('dnd-child-inserted'); } }, settings); $(settings.drop_selector).dnd(settings); }