Mercurial > defr > drupal > scald > dnd
view js/dnd.js @ 8:b9cd179a30a8
Use user session for the drupal_http_request requesting the library.
By default, drupal_http_request runs in a sandbox environment, thus the
request doesn't have any idea about the current user. This in turn means that
the request on the library is performed as an anonymous user, who may not have
appropriate credentials to access the library.
author | Franck Deroche <franck@defr.org> |
---|---|
date | Wed, 01 Apr 2009 15:49:44 +0200 |
parents | c2eb995212bf |
children | 99ba5941779c |
line wrap: on
line source
/* 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. * * Basic usage: * * $('a.my-class').dnd({targets: $('#my-iframe')}); * * Options: * * targets * A jQuery object corresponding to the proper iframe(s) to enable dragging * and dropping for. [Required] * * dropWrapper * An html snippet to wrap the entire inserted content with in the editor * representation (i.e. '<p class="foo"></p>') * * insertBefore: * Markup to insert before drop (i.e. '<hr />') * * insertAfter: * Markup to insert after drop (i.e. '<div class="clearfix"></div>') * * processedClass: * The class to apply to links and images tagged as droppable. This class * should have a style rule in the editor that sets display to 'none' for * the best experience. * * idSelector: * A callback that parses a unique id out of a droppable element. B y default * this uses the id of the element, but one could parse out an ID based on * any part of the URL, interior markup, etc. * * renderRepresentation: * A callback that defines the mechanism for rendering a representation of * the content. The default is currently essential useless. * * preprocessDrop: * A callback that preprocesses the dropped snippet in the iframe, before * replacing it. By default this uses a little logic to walk up the DOM * tree to topmost parent of the place in the source where the item was * dropped, and add the dropped element after that parent instead of * inside it. * * postprocessDrop: * A callback that postprocesses the iframe. * */ (function($) { $.fn.dnd = function(opt) { opt = $.extend({}, { dropWrapper: '<p class="dnd-dropped"></p>', insertBefore: '', insertAfter: '', processedClass: 'dnd-processed', disableClick: true, processTargets: function(targets) { return targets.each(function() { $('head', $(this).contents()).append('<style type="text/css">.dnd-processed { display: none; }</style>'); 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; } } return element.id; }, // @TODO: Target should be jQuery object targets: $('iframe, textarea'), // Must return a string that DOES NOT share the id of any droppable item // living outside the iframe renderRepresentation: function(target, drop, 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); return '<span id="dnd-' + representation_id +'-'+ count +'">' + representation_id + '</span>'; }, // Back out markup to render in place after parent container preprocessDrop: function(target, drop) { var old_parent = false; var element_id = ''; var parents = drop.parents(); for (i=0; i < parents.length; i++) { if ($(parents[i]).is('body')) { element_id = $(drop).get(0).id; $(old_parent).after(drop.clone()); drop.remove(); } old_parent = parents[i]; } return $('#'+ element_id, $(target).contents()); }, postprocessDrop: function(target, drop, element) { $(element).addClass('dnd-inserted'); } }, opt); // Initialize plugin var targets = opt.processTargets(opt.targets); if (opt.disableClick) { this.click(function() { return false; }) } // Process! return this.each(function() { var element = this; var representation_id = opt.idSelector(element); if (!element.id) { element.id = element.tagName.toLowerCase() + '-' + representation_id; } // 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 t = setInterval(function() { var match = $('#' + element.id, $(target).contents()); if (match.length > 0) { 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);