# HG changeset patch # User David Eads # Date 1236562197 18000 # Node ID 89fe0aca43d4ebad1e21d214c10e3761807d76f0 # Parent d83073a08b253d0f8d7b3d427a7ac4c066803140 Refactored the entire interval system, and started on form behavior. diff -r d83073a08b25 -r 89fe0aca43d4 dnd.module --- a/dnd.module Fri Mar 06 14:26:06 2009 -0600 +++ b/dnd.module Sun Mar 08 20:29:57 2009 -0500 @@ -76,10 +76,18 @@ */ function dnd_process_textarea($element, $form_state) { if ($element['#dnd-enabled']) { + + // BeautyTips drupal_add_js(drupal_get_path('module', 'dnd') .'/js/bt/other_libs/excanvas_0002/excanvas-compressed.js'); + drupal_add_js(drupal_get_path('module', 'dnd') .'/js/bt/other_libs/jquery.hoverIntent.minified.js'); + drupal_add_js(drupal_get_path('module', 'dnd') .'/js/bt/jquery.bt.js'); + + // Dependencies drupal_add_js(drupal_get_path('module', 'dnd') .'/js/jquery.url.packed.js'); drupal_add_js(drupal_get_path('module', 'dnd') .'/js/jquery.fieldselection.js'); - drupal_add_js(drupal_get_path('module', 'dnd') .'/js/bt/jquery.bt.js'); + drupal_add_js('misc/jquery.form.js'); + + // Drag and drop drupal_add_js(drupal_get_path('module', 'dnd') .'/js/jquery.draganddrop.js'); drupal_add_js(drupal_get_path('module', 'dnd') .'/js/dnd-library.js'); @@ -135,14 +143,14 @@ // Generate an array of editor representations to add if (is_array($json['editor_representations'])) { foreach ($json['editor_representations'] as $editor_id=>$editor_item) { - $editor_representations[$editor_id] = filter_xss_admin($editor_item); + $editor_representations[$editor_id] = $editor_item; } } // Generate an array of library previews to add if (is_array($json['library_previews'])) { foreach ($json['library_previews'] as $preview_id=>$preview_item) { - $library_previews[$preview_id] = filter_xss_admin($preview_item); + $library_previews[$preview_id] = $preview_item; } } @@ -153,7 +161,7 @@ ), 'setting'); $variables['library_id'] = $settings['library_id']; - $variables['header'] = filter_xss_admin($json['header']); - $variables['library'] = filter_xss_admin($json['library']); - $variables['footer'] = filter_xss_admin($json['footer']); + $variables['header'] = $json['header']; + $variables['library'] = $json['library']; + $variables['footer'] = $json['footer']; } diff -r d83073a08b25 -r 89fe0aca43d4 dnd_test/dnd_test.module --- a/dnd_test/dnd_test.module Fri Mar 06 14:26:06 2009 -0600 +++ b/dnd_test/dnd_test.module Sun Mar 08 20:29:57 2009 -0500 @@ -54,6 +54,10 @@ 'arguments' => array('i' => NULL), 'template' => 'dnd-library-preview', ), + 'dnd_library_header' => array( + 'arguments' => array('page' => NULL), + 'template' => 'dnd-library-header', + ), ); } @@ -64,7 +68,7 @@ $page = ($_GET['page']) ? $_GET['page'] : 1; $test_library = dnd_test_generate_library($page); return drupal_json(array( - 'header' => '

'. t('Test library: Page @page', array('@page' => $page)) .'

', + 'header' => theme('dnd_library_header', $page), 'library' => $test_library['library'], 'editor_representations' => $test_library['editor_representations'], 'library_previews' => $test_library['library_previews'], @@ -150,7 +154,6 @@ $variables['sizes'] = ''; } - function template_preprocess_dnd_editor_item(&$variables) { list($i, $size) = array($variables['i'], $variables['size']); @@ -165,3 +168,5 @@ } $variables['image'] = theme('image', drupal_get_path('module', 'dnd_test') .'/img/item-'. $img .'-'. $size .'.jpg', 'foo', 'foo', array('class' => 'dnd-dropped')); } + +function template_preprocess_dnd_library_header(&$variables) {} diff -r d83073a08b25 -r 89fe0aca43d4 js/bt/.jquery.bt.js.swp Binary file js/bt/.jquery.bt.js.swp has changed diff -r d83073a08b25 -r 89fe0aca43d4 js/dnd-library.js --- a/js/dnd-library.js Fri Mar 06 14:26:06 2009 -0600 +++ b/js/dnd-library.js Sun Mar 08 20:29:57 2009 -0500 @@ -32,36 +32,41 @@ // This is a bad hack to lop off '-dnd-library' from the id to get the editor name var $editor = $('#' + this.id.slice(0, -12)); - // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg $editor.bind('wysiwygAttach', Drupal.behaviors.dndLibrary.attach_library); $editor.bind('wysiwygDetach', Drupal.behaviors.dndLibrary.detach_library); + // Add preview behavior to editor items (thanks, BeautyTips!) $('.editor-item', context).each(function () { $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], { - 'trigger' : 'none', - 'width' : 300, - 'spikeLength' : 7, - 'spikeGirth' : 9, + 'trigger': 'none', + 'width': 300, + 'spikeLength': 7, + 'spikeGirth': 9, 'corner-radius' : 3, - 'strokeWidth' : 1, - 'fill' : '#eee', + 'strokeWidth': 1, + 'fill': '#ffd', 'strokeStyle': '#555' }); - $(this).hover(function() { - var $this = $(this); - this.btOn(); - // Remove the preview once dragging of any image has commenced - $('img', $this).bind('drag', function(e) { - $this.btOff(); - }); - }, function() { - this.btOff(); + $(this).hoverIntent({ + 'interval': 500, + 'timeout' : 0, + 'over': function() { + var $this = $(this); + this.btOn(); + // Remove the preview once dragging of any image has commenced + $('img', $this).bind('drag', function(e) { + $this.btOff(); + }); + $('img', $this).bind('click', function(e) { + $this.btOff(); + }); + }, + 'out': function() { this.btOff(); } }); }); - - // Ajax pager + // Simple AJAX pager $('.pager a', $this).click(function() { $.getJSON(this.href, function(data) { Drupal.behaviors.dndLibrary.refreshLibrary.call($this.get(0), data, $editor); @@ -70,6 +75,18 @@ return false; }); + $('.view-filters form', $this).submit(function() { + $(this).ajaxSubmit({ + // @TODO add URL from settings and target more specifically... + 'dataType': 'json', + 'success': function(responsetext, statustext) { + drupal.behaviors.dndlibrary.responsetext.call($this.get(0), responsetext, $editor); + drupal.behaviors.dndlibrary(); + } + }); + return false; + }); + // Preload images in editor representations var cached = $.data($editor, 'dnd_preload') || {}; for (editor_id in Drupal.settings.dndEditorRepresentations) { @@ -132,7 +149,7 @@ Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { settings = $.extend({ targets: $('#'+ data.field), - processTextAreaDrop: function(target, clicked, representation_id, e, data) { + processTextAreaClick: function(target, clicked, representation_id, e, data) { var snippet = '

' + Drupal.settings.dndEditorRepresentations[representation_id] + '

'; $(target).replaceSelection(snippet, true); } @@ -172,13 +189,13 @@ settings = $.extend({ targets: $('#'+ data.field +'-wrapper iframe'), - interval: 100, - processIframeDrop: function(target, dragged, dropped, representation_id) { + processIframeDrop: function(drop, id_selector) { + var representation_id = id_selector.call(this, drop); var representation = Drupal.settings.dndEditorRepresentations[representation_id]; - var $target = $(target), $dropped = $(dropped), $dragged = $(dragged), block; + var target = this, $target = $(target), $drop = $(drop), block; // Search through block level parents - $dropped.parents().each(function() { + $drop.parents().each(function() { var $this = $(this); if ($this.css('display') == 'block') { block = this; @@ -187,7 +204,7 @@ }); // Remove dropped item - $dropped.remove(); + $drop.remove(); // Create an element to insert var insert = dom.create('p', {'class' : 'dnd-dropped-wrapper', 'id' : 'dnd-inserted'}, representation); @@ -236,7 +253,7 @@ // If the next item exists and isn't an editor representation, drop the // caret at the beginning of the element, otherwise make a new paragraph // to advance the caret to. - if (next && !$(next).hasClass('dnd-dropped-wrapper')) { + if (next && !$(next).hasClass('dnd-droped-wrapper')) { $(next).prepend('_'); } else { @@ -252,11 +269,6 @@ s.select(c); ed.execCommand('Delete', false, null); dom.remove(c); - - // Add some classes to the library items - $(dragged).addClass('dnd-inserted'); - drag_parents = $(dragged).parents('.editor-item'); - drag_parents.addClass('dnd-child-inserted'); } }, settings); diff -r d83073a08b25 -r 89fe0aca43d4 js/jquery.draganddrop.js --- 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('

' + representation_id + '

'); + processIframeDrop: function(drop, id_selector) { + var representation_id = opt.idSelector(drop); + $(drop).replaceWith(representation_id).wrap('

'); }, - processTextAreaDrop: function(target, clicked, representation_id, e, data) { + processTextAreaClick: function(target, clicked, representation_id) { var snippet = '
'; $(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); }); } });