Mercurial > defr > drupal > scald > dnd
view js/dnd-library.js @ 35:abc9d39cfbe9
Switch the trigger from mouseover to click.
MouseOver is problematic if people want to interact with the content of the
popup, for example to click the play button of an swf player.
author | Franck Deroche <franck@defr.org> |
---|---|
date | Fri, 18 Sep 2009 15:11:03 +0200 |
parents | ee520ba7d98b |
children | 7ed2ac0cec27 |
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). An exception is required * to make this work in IE. */ (function($) { // Custom selectors $.extend($.expr[":"], { 'dnd_empty' : function(a, i, m) { return !$(a).filter(function(i) { return !$(this).is('br'); }).length && !$.trim(a.textContent || a.innerText||$(a).text() || ""); } }); }) (jQuery); /** * Initialize and load drag and drop library and pass off rendering and * behavior attachment. */ 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)); // Set up some initial settings for BeautyTips var settings = Drupal.settings.dndEnabledLibraries[$editor.get(0).id] = $.extend({ 'btSettings' : { 'trigger': ['click'], 'width': 375, 'spikeLength': 7, 'spikeGirth': 9, 'corner-radius' : 3, 'strokeWidth': 1, 'fill': '#ffd', 'strokeStyle': '#555', 'closeWhenOthersOpen': true }, 'libraryHoverIntentSettings' : { 'interval': 500, 'timeout' : 0, 'over': function() { var $this = $(this); this.btOn(); // Remove the preview once dragging of any image has commenced $('img', $this).bind('drag', function(e) { $this.btOff(); }); $('img', $this).bind('click', function(e) { $this.btOff(); }); }, 'out': function() { this.btOff(); } } }, Drupal.settings.dndEnabledLibraries[$editor.get(0).id]); // 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); // Set up empty objects to keep track of things Drupal.settings.dndEditorRepresentations = {}; Drupal.settings.dndLibraryPreviews = {}; // Initialize the library $.getJSON(Drupal.settings.basePath + settings.url, function(data) { Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $editor); }); }); } Drupal.behaviors.dndLibrary.renderLibrary = function(data, editor) { $this = $(this); $this.html(data.library); var settings = Drupal.settings.dndEnabledLibraries[editor.get(0).id]; var params = Drupal.wysiwyg.instances[editor.get(0).id]; editor.trigger('wysiwygDetach', params); editor.trigger('wysiwygAttach', params); for (editor_id in data.editor_representations) { Drupal.settings.dndEditorRepresentations[editor_id] = data.editor_representations[editor_id]; } for (preview_id in data.library_previews) { Drupal.settings.dndLibraryPreviews[preview_id] = data.library_previews[preview_id]; } // Add preview behavior to editor items (thanks, BeautyTips!) $('.editor-item', $this).each(function () { $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], settings.btSettings); $(this).hoverIntent(settings.libraryHoverIntentSettings); }); // Preload images in editor representations var cached = $.data($(editor), 'dnd_preload') || {}; for (editor_id in Drupal.settings.dndEditorRepresentations) { if (!cached[editor_id]) { $representation = $(Drupal.settings.dndEditorRepresentations[editor_id].body); if ($representation.is('img') && $representation.get(0).src) { $representation.attr('src', $representation.get(0).src); } else { $('img', $representation).each(function() { $(this).attr('src', this.src); }); } } } $.data($(editor), 'dnd_preload', cached); $('.pager a', $this).click(function() { $.getJSON(this.href, function(data) { Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); }); return false; }); $('.view-filters input[type=submit]', $this).click(function() { $('.view-filters form', $this).ajaxSubmit({ 'url' : Drupal.settings.basePath + settings.url, 'dataType' : 'json', 'success' : function(data) { Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); } }); return false; }); } // Dynamically compose a callback based on the editor name Drupal.behaviors.dndLibrary.attach_library = function(e, data) { var settings = $.extend({idSelector: Drupal.behaviors.dndLibrary.idSelector}, Drupal.settings.dndEnabledLibraries[data.field]); var editor_fn = 'attach_' + data.editor; if ($.isFunction(window.Drupal.behaviors.dndLibrary[editor_fn])) { window.Drupal.behaviors.dndLibrary[editor_fn](data, settings); } } // Do garbage collection on detach Drupal.behaviors.dndLibrary.detach_library = function(e, data) {} // Basic textareas Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { settings = $.extend({ targets: $('#'+ data.field), processTextAreaClick: function(clicked, representation_id, e, data) { var target = this, $target = $(target); // Update element count Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); var snippet = '<p class="dnd-drop-wrapper">' + Drupal.settings.dndEditorRepresentations[representation_id].body + '</p>'; $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); $('#'+ data.field +'-wrapper').trigger('dnd_attach_library'); clearInterval(t); } }, 100); } } Drupal.behaviors.dndLibrary.idSelector = function(element) { if ($(element).is('img')) { return $.url.setUrl(element.src).param('dnd_id'); } return false; } // 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'), processTargets: function(targets) { return targets.each(function() { var target = this // Decrement counter on delete $(target).bind('dnd_delete', function(e, data) { Drupal.behaviors.dndLibrary.countElements(target, $(data.node).attr('dnd_id'), true); }); $('head', $(this).contents()).append('<style type="text/css">img { display: none; } img.dnd-dropped {display: block; }</style>'); return this; }); }, processIframeDrop: function(drop, id_selector) { var representation_id = id_selector.call(this, drop); var representation = Drupal.settings.dndEditorRepresentations[representation_id].body; var target = this, $target = $(target), $drop = $(drop), block; // Update element count Drupal.behaviors.dndLibrary.countElements(target, representation_id); // Search through block level parents $drop.parents().each(function() { var $this = $(this); if ($this.css('display') == 'block') { block = this; return false; } }); // Remove dropped item $drop.remove(); // Create an element to insert var insert = dom.create('p', {'class' : 'dnd-drop-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-drop-wrapper" id="dnd-inserted">' + representation + '</p>'); // The active target block should be empty if ($('#target-block:dnd_empty', $target.contents()).length > 0) { $('#target-block', $target.contents()).remove(); } 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 ) { $inserted.before('<p></p>'); } // 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-drop-wrapper')) { $(next).prepend('<span id="__caret">_</span>'); } else if (!$(next).hasClass('dnd-drop-wrapper')) { var after = dom.create('p', {}, '<span id="__caret">_</span>'); dom.insertAfter(after, 'dnd-inserted'); } // Force selection to reset the caret var c = dom.get('__caret'); if (c) { s.select(c); ed.execCommand('Delete', false, null); dom.remove(c); } // Unset id for next drop and add special dnd attribute for counting // purposes $inserted .removeAttr('id') .attr('dnd_id', representation_id); } }, settings); $(settings.drop_selector).dnd(settings); } // Keep a counter of times a representation ID has been used Drupal.behaviors.dndLibrary.countElements = function(target, representation_id, decrement) { var counter = $(target).data('dnd_representation_counter'); if (!counter) { counter = {} counter[representation_id] = 1; } else if (counter && !counter[representation_id]) { counter[representation_id] = 1; } else { counter[representation_id] = counter[representation_id] + ((decrement) ? -1 : 1); } $(target).data('dnd_representation_counter', counter); return counter[representation_id]; }