diff js/jquery.draganddrop.js @ 20:89fe0aca43d4

Refactored the entire interval system, and started on form behavior.
author David Eads <eads@chicagotech.org>
date Sun, 08 Mar 2009 20:29:57 -0500
parents 0d557e6e73f7
children 4f58fa0a9a6d
line wrap: on
line diff
--- a/js/jquery.draganddrop.js	Fri Mar 06 14:26:06 2009 -0600
+++ b/js/jquery.draganddrop.js	Sun Mar 08 20:29:57 2009 -0500
@@ -1,4 +1,5 @@
-/* jQuery Drag and Drop Library for Rich Editors
+/**
+ * jQuery Drag and Drop Library for Rich Editors
  *
  * A helper library which provides the ability to drag and drop images to Rich 
  * Text Editors (RTEs) that use the embedded iframe + DesignMode method. DnD
@@ -7,7 +8,7 @@
  *
  * Basic usage:
  *
- * $('img.my-class').dnd({targets: $('#my-iframe')});
+ * $('img.my-draggable-class').dnd({targets: $('#my-rte-iframe')});
  *
  * Options:
  *
@@ -21,24 +22,34 @@
  *  markup to be copied when dragging and dropping, Safari (and assumably
  *  other webkit based browsers) don't.  The upshot is that the safest bet is
  *  to parse the element's src URL.  Because querystrings seem to drop 
- *  consistently across 
+ *  consistently across browsers, encoding the id in a query string is a good
+ *  option.
  *
  * processIframeDrop:
  *   A callback that defines the mechanism for inserting and rendering the 
  *   dropped item in an iframe.  The typical usage pattern I expect to see is
  *   that implementers will listen for their RTE to load and then invoke DnD
- *   with a processIframeDrop appropriate to their editor.
+ *   with a processIframeDrop appropriate to their editor.  For performance
+ *   reasons, this only runs once per invocation of DnD -- to compensate, we 
+ *   must pass in an idselector callback as an argument. 
  *
- * processTextAreaDrop:
+ * processTextAreaClick:
  *   A callback that defines the mechanism for inserting and rendering the
  *   clicked item in a textarea.  The default function just tries to insert
  *   some markup at the caret location in the current textarea.
  *
+ * iframeTargetClass:
+ *   A class to add to a draggable item if it can be dragged to an iframe.
+ *
+ * textareaTargetClass:
+ *   A class to add to a draggable item if it can be dragged to a textarea.
+ *
+ * processClass:
+ *   A class to add to draggable items processed by DnD.
+ *
  * interval:
  *   How often to check the iframe for a drop, in milliseconds.
  *
- * 
- *
  * Usage notes:
  *
  * Due to cross browser flakiness, there are many many limitations on what is
@@ -47,7 +58,7 @@
  * To make this work, your "droppable" elements must be image tags.  No ifs, ands, 
  * or buts:  image tags have the best cross browser behavior when dragging.
  *
- * When DnD is initialized, it begins timers which periodically check your 
+ * When DnD is invoked, it begins a timer which periodically checks your 
  * editor iframe.  If an image is dropped in, DnD takes the element and 
  * attempts to parse it for a unique ID which you can use to do a lookup for 
  * an "editor representation."  If an editor representation is found, 
@@ -64,9 +75,6 @@
  * system for clicking what would otherwise be dragged to insert markup into 
  * the textarea.
  *
- * DnD is purely a utility library:  to make it work for any particular editor,
- * CMS, etc:  It is very unlikely it will provide much benefit out of the box.
- *
  * Because DnD spawns so many timers, they are stored in a $.data element 
  * attached to the parent document body.  Implementers should consider clearing
  * the timers when building systems such as dynamic searches or paging 
@@ -90,34 +98,49 @@
         }
         return false;
       }, 
-      processIframeDrop: function(target, dragged, dropped, representation_id) {
-        // Keep a counter of how many times this element was used
-        var count = $.data(target, representation_id +'_count');
-        if (!count) { 
-          count = 1; 
-        } else { 
-          count++;
-        }
-        $.data(target, representation_id +'_count', count);
-        $(dropped).replaceWith('<p id="dnd-' + representation_id +'-'+ count +'">' + representation_id + '</p>');
+      processIframeDrop: function(drop, id_selector) {
+        var representation_id = opt.idSelector(drop);
+        $(drop).replaceWith(representation_id).wrap('<p class="dnd-dropped"></p>');
       },
-      processTextAreaDrop: function(target, clicked, representation_id, e, data) {
+      processTextAreaClick: function(target, clicked, representation_id) {
         var snippet = '<div><img src="'+ representation_id +'" /></div>';
         $(target).replaceSelection(snippet, true);
         e.preventDefault();
-      }
+      },
+      processIframeClick: function (target, clicked, representation_id) { return true; },
+
+      iframeTargetClass: 'dnd-iframe-target',
+      textareaTargetClass: 'dnd-textarea-target',
+      processedClass: 'dnd-processed'
 
     }, opt);
 
     // Initialize plugin
     var targets = opt.processTargets(opt.targets);
 
-    // Process!
+    // Watch iframes for changes
+    $(targets).filter('iframe').each(function() {
+      var target = this; 
+      var t = setInterval(function() {              
+        $('img:not(.dnd-dropped)', $(target).contents()).each(function() {
+          opt.processIframeDrop.call(target, this, opt.idSelector);
+        });
+      }, opt.interval);
+
+      // Track current active timers -- developers working with DnD
+      // can implement their own garbage collection for specific 
+      // interactions, such as paging or live search.
+      var data = $(document).data('dnd_timers');
+      if (!data) { data = {}; }
+      data[target.id] = t;
+      $(document).data('dnd_timers', data);
+    });
+
+    // Process each draggable element
     return this.each(function() {
       if ($(this).is('img')) {
         var element = this, $element = $(element);
 
-        // If we don't have a proper id, bail
         var representation_id = opt.idSelector(element);
 
         if (!representation_id) {
@@ -125,42 +148,20 @@
         };
 
         // Add a special class
-        $(element).addClass('dnd-processed');
+        $(element).addClass(opt.processedClass);
 
-        // We need to differentiate behavior based on the targets... I guess.
+        // We need to differentiate behavior based on the targets
         targets.each(function() {
           var target = this, $target = $(target);
           if ($target.is('iframe')) {
-
-            // Indicate this element is draggy
-            $element.css('cursor', 'move');
-
-            // Watch the iframe for changes
-            var t = setInterval(function() {              
-              $('img:not(.dnd-dropped)', $(target).contents()).each(function() {
-                var dropped = this;
-                if (opt.idSelector(dropped) == representation_id) {
-                  opt.processIframeDrop(target, element, dropped, representation_id);
-                }
-              });
-            }, opt.interval);
-
-            // Track current active timers -- developers working with DnD
-            // can implement their own garbage collection for specific 
-            // interactions, such as paging or live search.
-            var data = $(document).data('dnd_timers');
-            if (data) {
-              data[data.length] = t;
-            } else {
-              data = new Array();
-              data[0] = t;
-            }
-            $(document).data('dnd_timers', data);
-
+            $(element).addClass(opt.iframeTargetClass);
+            $(element).click(function() {
+              opt.processIframeClick.call(target, element, representation_id);
+            });
           } else if ($target.is('textarea')) {
-            $element.css('cursor', 'pointer');
-            $(element).click(function(e, data) {
-              opt.processTextAreaDrop(target, element, representation_id, e, data);
+            $(element).addClass(opt.textareaTargetClass);
+            $(element).click(function() {
+              opt.processTextAreaClick.call(target, element, representation_id);
             });
           }
         });