Mercurial > defr > drupal > scald > dnd
diff js/jquery.draganddrop.js @ 18:0d557e6e73f7
Added beautytips and some additional event handling code to the library.
author | David Eads <eads@chicagotech.org> |
---|---|
date | Fri, 06 Mar 2009 14:11:46 -0600 |
parents | js/dnd.js@1a77f87927dd |
children | 89fe0aca43d4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/jquery.draganddrop.js Fri Mar 06 14:11:46 2009 -0600 @@ -0,0 +1,170 @@ +/* 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 + * also provides a simple "clicky" interface for inserting the same markup + * directly into a textarea. + * + * Basic usage: + * + * $('img.my-class').dnd({targets: $('#my-iframe')}); + * + * Options: + * + * targets (Required): + * A jQuery object corresponding to the proper iframe(s) and/or textarea(s) + * that are allowed drop targets for the current set of elements. + * + * idSelector: + * A callback that parses out the unique ID of an image that is dropped in an + * iframe. While some browsers (such as Firefox and Internet Explorer) allow + * 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 + * + * 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. + * + * processTextAreaDrop: + * 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. + * + * 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 + * possible in terms of dragging and dropping to DesignMode enabled iframes. + * + * 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 + * 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, + * typically the image is replaced with the representation snippet, but you are + * free to do whatever you want. + * + * Because of browser limitations, the safest way to parse the element is to + * look at the img's src attribute and use some or all of the image URL. + * In my experience, the best way to deal with this is simply to parse out + * generate a query string in the src attribute server side that corresponds + * to the proper representation ID. + * + * If the target is not an iframe but a textarea, DnD provides a very minimal + * 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 + * functionality to ensure memory usage and performance remains stable. + */ + +(function($) { + $.fn.dnd = function(opt) { + opt = $.extend({}, { + interval: 250, + targets: $('iframe, textarea'), + processTargets: function(targets) { + return targets.each(function() { + $('head', $(this).contents()).append('<style type="text/css">img { display: none; } img.dnd-dropped {display: block; }</style>'); + return this; + }); + }, + idSelector: function(element) { + if ($(element).is('img')) { + return element.src; + } + 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>'); + }, + processTextAreaDrop: function(target, clicked, representation_id, e, data) { + var snippet = '<div><img src="'+ representation_id +'" /></div>'; + $(target).replaceSelection(snippet, true); + e.preventDefault(); + } + + }, opt); + + // Initialize plugin + var targets = opt.processTargets(opt.targets); + + // Process! + 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) { + return this; + }; + + // Add a special class + $(element).addClass('dnd-processed'); + + // We need to differentiate behavior based on the targets... I guess. + 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); + + } else if ($target.is('textarea')) { + $element.css('cursor', 'pointer'); + $(element).click(function(e, data) { + opt.processTextAreaDrop(target, element, representation_id, e, data); + }); + } + }); + } + }); + }; +})(jQuery);