Mercurial > defr > drupal > scald > dnd
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); |