Mercurial > defr > drupal > scald > dnd
view js/jquery.draganddrop.js @ 47:cbfe386cb51b
Add a function to refresh opened libraries.
The source URL of each libraries is now tracked, which allows for auto
refreshing the libraries based on various events. The obvious use
case is to refresh the library when an atom has been added to Scald,
for example via a Popups dialog.
author | Franck Deroche <defr@ows.fr> |
---|---|
date | Mon, 15 Feb 2010 14:08:04 +0000 |
parents | e71df38143d1 |
children | f817d2a5cc0a |
line wrap: on
line source
/** * 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-draggable-class').dnd({targets: $('#my-rte-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 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. For performance * reasons, this only runs once per invocation of DnD -- to compensate, we * must pass in an idselector callback as an argument. * * 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. * * processedClass: * A class to add to draggable items processed by DnD. * * droppedClass: * A class to add to images that are dropped (by default, this is used to * ensure that a successful drop manifests the editor representation only * once.) * * 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 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, * 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. * * 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(drop, id_selector) { var representation_id = opt.idSelector(drop); $(drop).replaceWith(representation_id).wrap('<p class="'+ opt.droppedClass +'"></p>'); }, 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', droppedClass: 'dnd-dropped' }, opt); // Initialize plugin var targets = opt.processTargets(opt.targets); // Watch iframes for changes $(targets).filter('iframe').each(function() { var target = this; var t = setInterval(function() { $('img:not(.'+ opt.droppedClass +')', $(target).contents()).each(function() { opt.processIframeDrop.call(target, this, opt.idSelector); var data = {'drop': this, 'representation_id': opt.idSelector(this)}; // Trigger event in container window $(target).trigger('dnd_drop', data); // Trigger event in iframe $(this).trigger('dnd_drop', data); }); }, 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); var representation_id = opt.idSelector(element); if (!representation_id) { return this; }; // Add a special class $(element).addClass(opt.processedClass); // Data for custom event var event_data = {'drop': element, 'representation_id': representation_id}; // We need to differentiate behavior based on the targets targets.each(function() { var target = this, $target = $(target); if ($target.is('iframe')) { $(element).addClass(opt.iframeTargetClass); $(element).click(function() { opt.processIframeClick.call(target, element, representation_id); $(target).trigger('dnd_drop', event_data); }); } else if ($target.is('textarea')) { $(element).addClass(opt.textareaTargetClass); $(element).click(function() { opt.processTextAreaClick.call(target, element, representation_id); $(target).trigger('dnd_drop', event_data); }); } }); } }); }; })(jQuery);