# HG changeset patch # User David Eads # Date 1235757059 21600 # Node ID a5b2b9fa2a1aad90d1d4fa935e08c8b810b4eaca # Parent 99ba5941779cb38808aebf82376e73622d4bc241 Cross browser compatibility changes -- winnowing the scope of possible configurations. diff -r 99ba5941779c -r a5b2b9fa2a1a dnd.module --- a/dnd.module Fri Feb 27 00:49:22 2009 -0600 +++ b/dnd.module Fri Feb 27 11:50:59 2009 -0600 @@ -77,6 +77,7 @@ function dnd_process_textarea($element, $form_state) { if ($element['#dnd-enabled']) { + drupal_add_js(drupal_get_path('module', 'dnd') .'/js/jquery.url.packed.js', 'footer'); drupal_add_js(drupal_get_path('module', 'dnd') .'/js/dnd.js', 'footer'); drupal_add_js(drupal_get_path('module', 'dnd') .'/js/dnd-library.js', 'footer'); diff -r 99ba5941779c -r a5b2b9fa2a1a dnd_test/dnd_test.css --- a/dnd_test/dnd_test.css Fri Feb 27 00:49:22 2009 -0600 +++ b/dnd_test/dnd_test.css Fri Feb 27 11:50:59 2009 -0600 @@ -130,5 +130,5 @@ padding: 4px 0; } .dnd-library-wrapper .editor-item.dnd-child-inserted { - background-color: #ffd; + background-color: #ccc; } diff -r 99ba5941779c -r a5b2b9fa2a1a dnd_test/dnd_test.module --- a/dnd_test/dnd_test.module Fri Feb 27 00:49:22 2009 -0600 +++ b/dnd_test/dnd_test.module Fri Feb 27 11:50:59 2009 -0600 @@ -26,6 +26,10 @@ * This demonstrates how to attach Drag and Drop to a given textarea. */ function dnd_test_form_alter(&$form, &$form_state) { + + //global $_SERVER; + //dpm($_SERVER); + if ($form['#id'] == 'node-form' && $form['type']['#value'] == 'page') { drupal_add_css(drupal_get_path('module', 'dnd_test') .'/dnd_test.css'); $form['body_field']['body']['#dnd-enabled'] = TRUE; @@ -94,7 +98,7 @@ function dnd_editor_items($i) { $item = array(); foreach(array(t('S'), t('M'), t('L')) as $size) { - $item['item-'. $i .'-'. $size] = theme('dnd_editor_item', $i, $size); + $item[$i .'-'. $size] = theme('dnd_editor_item', $i, $size); } return $item; } @@ -103,6 +107,8 @@ * Completely contrived edit item theme function */ function template_preprocess_dnd_library_item(&$variables) { + global $_SERVER; + $i = $variables['i']; if ($i % 3 == 0) { @@ -115,16 +121,13 @@ $img = 1; } - $variables['image'] = theme('image', drupal_get_path('module', 'dnd_test') .'/img/item-'. $img .'-thumb.jpg'); + //$variables['image'] = theme('image', drupal_get_path('module', 'dnd_test') .'/img/item-'. $img .'-thumb.jpg'); + $variables['image'] = ''; $variables['title'] = t('Lorem Ipsum @count', array('@count' => $i)); $variables['date'] = 'Feb 18 2009'; $variables['author'] = 'David Eads'; foreach(array(t('S'), t('M'), t('L')) as $size) { - $attributes = array( - 'class' => 'drop size-'. $size, - 'id' => 'item-'. $i .'-'. $size, - ); - $sizes[] = theme('image', drupal_get_path('module', 'dnd_test') .'/img/icon/'. $size .'.png', $size, $size, $attributes); + $sizes[] = ''; } $variables['sizes'] = ''; } diff -r 99ba5941779c -r a5b2b9fa2a1a dnd_test/img/icon/L.png Binary file dnd_test/img/icon/L.png has changed diff -r 99ba5941779c -r a5b2b9fa2a1a dnd_test/img/icon/M.png Binary file dnd_test/img/icon/M.png has changed diff -r 99ba5941779c -r a5b2b9fa2a1a dnd_test/img/icon/S.png Binary file dnd_test/img/icon/S.png has changed diff -r 99ba5941779c -r a5b2b9fa2a1a js/dnd.js --- a/js/dnd.js Fri Feb 27 00:49:22 2009 -0600 +++ b/js/dnd.js Fri Feb 27 11:50:59 2009 -0600 @@ -1,44 +1,13 @@ /* jQuery Drag and Drop Library for Rich Editors * - * This library exists to provide a jQuery plugin that manages dragging - * and dropping of page assets into textareas and rich-text editors which use - * the iframe + designMode method. - * - * This plugin has a rather elaborate set of considerations based on common - * configurations of rich text editors and serious disparity in how browsers - * handle dragging and dropping assets into an iframe. - * - * The plugin scans an iframe with an embedded document with designMode enabled - * and tries to detect content that was injected by dragging and dropping - * within the browser. If it detects an injection of content, it attempts to - * conjure up an "editor representation" of the content based on the markup - * passed in. - * - * This works because links and images drop with their full HTML syntax in-tact - * across most browsers and platforms. This means we can set a timer on the - * iframe that scans for the insertion of the markup and replace it with - * another HTML snippet, as well as triggering actions inside the editor itself - * (this will perhaps be handled by a set of editor-specific plugins). - * - * Because of the mechanism of operation, it is expected that the iframe - * contain CSS which hides the markup dropped in to create the illusion of - * seamlessly dropping in the "editor representation" of the dropped item. - * - * It is expected that the implementer will be parsing the input text for - * the proper markup on the server side and making some decisions about how - * to parse and handle that markup on load and save. - * - * Of special interest is graceful degradation. There is no ideal graceful - * degradation path at this time. Every mainstream browser except IE 6 and - * IE 7 drop links and images into textareas with their href and src URIs, - * respectively, including querystrings. That means that the image or link - * url can be used for parsing, or a querystring included. But it won't work - * in Internet Explorer, and probably will require a server-side browser check - * in full blown implementations of the library system. + * A helper library which provides the ability to drag and drop images to Rich + * Text Editors (RTEs) that use the embedded iframe + DesignMode method, and + * provides a simple "clicky" interface for inserting the same markup directly + * into a textarea. * * Basic usage: * - * $('a.my-class').dnd({targets: $('#my-iframe')}); + * $('img.my-class').dnd({targets: $('#my-iframe')}); * * Options: * @@ -80,36 +49,55 @@ * postprocessDrop: * A callback that postprocesses the iframe. * + * interval: + * How often to check the iframe for a drop, in milliseconds. + * + * Usage notes: + * + * This is a very tricky problem and to achieve cross browser (as of writing, + * IE, Safari, and Firefox) support, severe limitations must be made on the + * nature of DOM elements that are dropped. + * + * Droppable elements must be tags. Internet Explorer will not accept + * anchors, and Safari strips attributes and additional markup from + * dropped anchors, images, and snippets. + * + * While the idSelector option allows you to parse the dropped element for + * any attribute that "comes along" with the element when it is dropped in a + * designmode-enabled iframe, the only safe attribute to scan is the 'src' + * attribute, and to avoid strange "relativization" of links, the src should + * always be expressed as an absolute url with a fully qualified domain name. + * + * Implementation notes and todos: + * + * Currently, there is no garbage collection instituted for the many many + * timers that are created, so memory usage could become as issue in scenarios + * where the user does a lot of paging or otherwise winds up invoking + * drag and drop on large numbers of elements. */ (function($) { $.fn.dnd = function(opt) { opt = $.extend({}, { dropWrapper: '

', - insertBefore: '', - insertAfter: '', + insertBefore: false, + insertAfter: false, processedClass: 'dnd-processed', - disableClick: true, + interval: 100, processTargets: function(targets) { return targets.each(function() { - $('head', $(this).contents()).append(''); + //$('head', $(this).contents()).append(''); + //@TODO use jQuery.rules() return this; }); }, // Must return a string idSelector: function(element) { - if (!element.id) { - // @TODO sanitize output here - if ($(element).is('a')) { - return element.href; - } - if ($(element).is('img')) { - return element.src; - } + if ($(element).is('img')) { + return $.url.setUrl(element.src).param('dnd_id'); } - return element.id; }, // @TODO: Target should be jQuery object @@ -131,6 +119,7 @@ // Back out markup to render in place after parent container preprocessDrop: function(target, drop) { + return drop; var old_parent = false; var element_id = ''; @@ -154,46 +143,58 @@ // Initialize plugin var targets = opt.processTargets(opt.targets); - if (opt.disableClick) { this.click(function() { return false; }); } // Process! return this.each(function() { + if ($(this).is('img')) { + var element = this; - var element = this; - var representation_id = opt.idSelector(element); + // If we don't have a proper id, bail + var representation_id = opt.idSelector(element); - if (!element.id) { - element.id = element.tagName.toLowerCase() + '-' + representation_id; + if (!representation_id) { + return this; + }; + + // Add some UI sugar and a special class + $(element) + .css('cursor', 'move') + .addClass(opt.processedClass); + + // We need to differentiate behavior based on the targets... I guess. + targets.each(function() { + if ($(this).is('iframe')) { + var target = this; + var selector = 'img[src='+ element.src +']'; + + // Watch the iframe for changes + var t = setInterval(function() { + var match = $(selector, $(target).contents()); + if (match.length > 0) { + var drop = opt.preprocessDrop(target, match); // Must return a jquery object + var representation = opt.renderRepresentation(target, drop, representation_id); + + if (representation) { + if (opt.dropWrapper) { + drop.wrap(opt.dropWrapper); + } + if (opt.insertBefore) { + drop.before(opt.insertBefore); + } + if (opt.insertAfter) { + drop.after(opt.insertAfter); + } + drop.replaceWith(representation); + opt.postprocessDrop(target, drop, element); + } + } + }, opt.interval); + // @TODO track the timer with $.data() so we can clear it? + } else if ($(this).is('textarea')) { + //console.log('@TODO handle textareas via.... regexp?'); + } + }); } - - // Add some UI sugar and a special class - $(element) - .css('cursor', 'move') - .addClass(opt.processedClass); - - // We need to differentiate behavior based on the targets... I guess. - targets.each(function() { - if ($(this).is('iframe')) { - var target = this; - - // Watch the iframe for changes - var t = setInterval(function() { - var match = $('#' + element.id, $(target).contents()); - if (match.length > 0) { - var drop = opt.preprocessDrop(target, match); // Must return a jquery object - drop - .before(opt.insertBefore) - .after(opt.insertAfter) - .wrap(opt.dropWrapper) - .replaceWith(opt.renderRepresentation(target, drop, representation_id)); - opt.postprocessDrop(target, drop, element); - } - }, 100); - // @TODO track the timer with $.data() so we can clear it? - } else if ($(this).is('textarea')) { - //console.log('@TODO handle textareas via.... regexp?'); - } - }); }); }; })(jQuery);