comparison 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
comparison
equal deleted inserted replaced
17:1a77f87927dd 18:0d557e6e73f7
1 /* jQuery Drag and Drop Library for Rich Editors
2 *
3 * A helper library which provides the ability to drag and drop images to Rich
4 * Text Editors (RTEs) that use the embedded iframe + DesignMode method. DnD
5 * also provides a simple "clicky" interface for inserting the same markup
6 * directly into a textarea.
7 *
8 * Basic usage:
9 *
10 * $('img.my-class').dnd({targets: $('#my-iframe')});
11 *
12 * Options:
13 *
14 * targets (Required):
15 * A jQuery object corresponding to the proper iframe(s) and/or textarea(s)
16 * that are allowed drop targets for the current set of elements.
17 *
18 * idSelector:
19 * A callback that parses out the unique ID of an image that is dropped in an
20 * iframe. While some browsers (such as Firefox and Internet Explorer) allow
21 * markup to be copied when dragging and dropping, Safari (and assumably
22 * other webkit based browsers) don't. The upshot is that the safest bet is
23 * to parse the element's src URL. Because querystrings seem to drop
24 * consistently across
25 *
26 * processIframeDrop:
27 * A callback that defines the mechanism for inserting and rendering the
28 * dropped item in an iframe. The typical usage pattern I expect to see is
29 * that implementers will listen for their RTE to load and then invoke DnD
30 * with a processIframeDrop appropriate to their editor.
31 *
32 * processTextAreaDrop:
33 * A callback that defines the mechanism for inserting and rendering the
34 * clicked item in a textarea. The default function just tries to insert
35 * some markup at the caret location in the current textarea.
36 *
37 * interval:
38 * How often to check the iframe for a drop, in milliseconds.
39 *
40 *
41 *
42 * Usage notes:
43 *
44 * Due to cross browser flakiness, there are many many limitations on what is
45 * possible in terms of dragging and dropping to DesignMode enabled iframes.
46 *
47 * To make this work, your "droppable" elements must be image tags. No ifs, ands,
48 * or buts: image tags have the best cross browser behavior when dragging.
49 *
50 * When DnD is initialized, it begins timers which periodically check your
51 * editor iframe. If an image is dropped in, DnD takes the element and
52 * attempts to parse it for a unique ID which you can use to do a lookup for
53 * an "editor representation." If an editor representation is found,
54 * typically the image is replaced with the representation snippet, but you are
55 * free to do whatever you want.
56 *
57 * Because of browser limitations, the safest way to parse the element is to
58 * look at the img's src attribute and use some or all of the image URL.
59 * In my experience, the best way to deal with this is simply to parse out
60 * generate a query string in the src attribute server side that corresponds
61 * to the proper representation ID.
62 *
63 * If the target is not an iframe but a textarea, DnD provides a very minimal
64 * system for clicking what would otherwise be dragged to insert markup into
65 * the textarea.
66 *
67 * DnD is purely a utility library: to make it work for any particular editor,
68 * CMS, etc: It is very unlikely it will provide much benefit out of the box.
69 *
70 * Because DnD spawns so many timers, they are stored in a $.data element
71 * attached to the parent document body. Implementers should consider clearing
72 * the timers when building systems such as dynamic searches or paging
73 * functionality to ensure memory usage and performance remains stable.
74 */
75
76 (function($) {
77 $.fn.dnd = function(opt) {
78 opt = $.extend({}, {
79 interval: 250,
80 targets: $('iframe, textarea'),
81 processTargets: function(targets) {
82 return targets.each(function() {
83 $('head', $(this).contents()).append('<style type="text/css">img { display: none; } img.dnd-dropped {display: block; }</style>');
84 return this;
85 });
86 },
87 idSelector: function(element) {
88 if ($(element).is('img')) {
89 return element.src;
90 }
91 return false;
92 },
93 processIframeDrop: function(target, dragged, dropped, representation_id) {
94 // Keep a counter of how many times this element was used
95 var count = $.data(target, representation_id +'_count');
96 if (!count) {
97 count = 1;
98 } else {
99 count++;
100 }
101 $.data(target, representation_id +'_count', count);
102 $(dropped).replaceWith('<p id="dnd-' + representation_id +'-'+ count +'">' + representation_id + '</p>');
103 },
104 processTextAreaDrop: function(target, clicked, representation_id, e, data) {
105 var snippet = '<div><img src="'+ representation_id +'" /></div>';
106 $(target).replaceSelection(snippet, true);
107 e.preventDefault();
108 }
109
110 }, opt);
111
112 // Initialize plugin
113 var targets = opt.processTargets(opt.targets);
114
115 // Process!
116 return this.each(function() {
117 if ($(this).is('img')) {
118 var element = this, $element = $(element);
119
120 // If we don't have a proper id, bail
121 var representation_id = opt.idSelector(element);
122
123 if (!representation_id) {
124 return this;
125 };
126
127 // Add a special class
128 $(element).addClass('dnd-processed');
129
130 // We need to differentiate behavior based on the targets... I guess.
131 targets.each(function() {
132 var target = this, $target = $(target);
133 if ($target.is('iframe')) {
134
135 // Indicate this element is draggy
136 $element.css('cursor', 'move');
137
138 // Watch the iframe for changes
139 var t = setInterval(function() {
140 $('img:not(.dnd-dropped)', $(target).contents()).each(function() {
141 var dropped = this;
142 if (opt.idSelector(dropped) == representation_id) {
143 opt.processIframeDrop(target, element, dropped, representation_id);
144 }
145 });
146 }, opt.interval);
147
148 // Track current active timers -- developers working with DnD
149 // can implement their own garbage collection for specific
150 // interactions, such as paging or live search.
151 var data = $(document).data('dnd_timers');
152 if (data) {
153 data[data.length] = t;
154 } else {
155 data = new Array();
156 data[0] = t;
157 }
158 $(document).data('dnd_timers', data);
159
160 } else if ($target.is('textarea')) {
161 $element.css('cursor', 'pointer');
162 $(element).click(function(e, data) {
163 opt.processTextAreaDrop(target, element, representation_id, e, data);
164 });
165 }
166 });
167 }
168 });
169 };
170 })(jQuery);