comparison js/jquery.draganddrop.js @ 20:89fe0aca43d4

Refactored the entire interval system, and started on form behavior.
author David Eads <eads@chicagotech.org>
date Sun, 08 Mar 2009 20:29:57 -0500
parents 0d557e6e73f7
children 4f58fa0a9a6d
comparison
equal deleted inserted replaced
19:d83073a08b25 20:89fe0aca43d4
1 /* jQuery Drag and Drop Library for Rich Editors 1 /**
2 * jQuery Drag and Drop Library for Rich Editors
2 * 3 *
3 * A helper library which provides the ability to drag and drop images to Rich 4 * 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 * Text Editors (RTEs) that use the embedded iframe + DesignMode method. DnD
5 * also provides a simple "clicky" interface for inserting the same markup 6 * also provides a simple "clicky" interface for inserting the same markup
6 * directly into a textarea. 7 * directly into a textarea.
7 * 8 *
8 * Basic usage: 9 * Basic usage:
9 * 10 *
10 * $('img.my-class').dnd({targets: $('#my-iframe')}); 11 * $('img.my-draggable-class').dnd({targets: $('#my-rte-iframe')});
11 * 12 *
12 * Options: 13 * Options:
13 * 14 *
14 * targets (Required): 15 * targets (Required):
15 * A jQuery object corresponding to the proper iframe(s) and/or textarea(s) 16 * A jQuery object corresponding to the proper iframe(s) and/or textarea(s)
19 * A callback that parses out the unique ID of an image that is dropped in an 20 * 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 * iframe. While some browsers (such as Firefox and Internet Explorer) allow
21 * markup to be copied when dragging and dropping, Safari (and assumably 22 * 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 * 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 * to parse the element's src URL. Because querystrings seem to drop
24 * consistently across 25 * consistently across browsers, encoding the id in a query string is a good
26 * option.
25 * 27 *
26 * processIframeDrop: 28 * processIframeDrop:
27 * A callback that defines the mechanism for inserting and rendering the 29 * 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 30 * 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 31 * that implementers will listen for their RTE to load and then invoke DnD
30 * with a processIframeDrop appropriate to their editor. 32 * with a processIframeDrop appropriate to their editor. For performance
33 * reasons, this only runs once per invocation of DnD -- to compensate, we
34 * must pass in an idselector callback as an argument.
31 * 35 *
32 * processTextAreaDrop: 36 * processTextAreaClick:
33 * A callback that defines the mechanism for inserting and rendering the 37 * A callback that defines the mechanism for inserting and rendering the
34 * clicked item in a textarea. The default function just tries to insert 38 * clicked item in a textarea. The default function just tries to insert
35 * some markup at the caret location in the current textarea. 39 * some markup at the caret location in the current textarea.
36 * 40 *
41 * iframeTargetClass:
42 * A class to add to a draggable item if it can be dragged to an iframe.
43 *
44 * textareaTargetClass:
45 * A class to add to a draggable item if it can be dragged to a textarea.
46 *
47 * processClass:
48 * A class to add to draggable items processed by DnD.
49 *
37 * interval: 50 * interval:
38 * How often to check the iframe for a drop, in milliseconds. 51 * How often to check the iframe for a drop, in milliseconds.
39 *
40 *
41 * 52 *
42 * Usage notes: 53 * Usage notes:
43 * 54 *
44 * Due to cross browser flakiness, there are many many limitations on what is 55 * 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. 56 * possible in terms of dragging and dropping to DesignMode enabled iframes.
46 * 57 *
47 * To make this work, your "droppable" elements must be image tags. No ifs, ands, 58 * 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. 59 * or buts: image tags have the best cross browser behavior when dragging.
49 * 60 *
50 * When DnD is initialized, it begins timers which periodically check your 61 * When DnD is invoked, it begins a timer which periodically checks your
51 * editor iframe. If an image is dropped in, DnD takes the element and 62 * 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 63 * 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, 64 * an "editor representation." If an editor representation is found,
54 * typically the image is replaced with the representation snippet, but you are 65 * typically the image is replaced with the representation snippet, but you are
55 * free to do whatever you want. 66 * free to do whatever you want.
61 * to the proper representation ID. 72 * to the proper representation ID.
62 * 73 *
63 * If the target is not an iframe but a textarea, DnD provides a very minimal 74 * 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 75 * system for clicking what would otherwise be dragged to insert markup into
65 * the textarea. 76 * 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 * 77 *
70 * Because DnD spawns so many timers, they are stored in a $.data element 78 * Because DnD spawns so many timers, they are stored in a $.data element
71 * attached to the parent document body. Implementers should consider clearing 79 * attached to the parent document body. Implementers should consider clearing
72 * the timers when building systems such as dynamic searches or paging 80 * the timers when building systems such as dynamic searches or paging
73 * functionality to ensure memory usage and performance remains stable. 81 * functionality to ensure memory usage and performance remains stable.
88 if ($(element).is('img')) { 96 if ($(element).is('img')) {
89 return element.src; 97 return element.src;
90 } 98 }
91 return false; 99 return false;
92 }, 100 },
93 processIframeDrop: function(target, dragged, dropped, representation_id) { 101 processIframeDrop: function(drop, id_selector) {
94 // Keep a counter of how many times this element was used 102 var representation_id = opt.idSelector(drop);
95 var count = $.data(target, representation_id +'_count'); 103 $(drop).replaceWith(representation_id).wrap('<p class="dnd-dropped"></p>');
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 },
104 processTextAreaDrop: function(target, clicked, representation_id, e, data) { 105 processTextAreaClick: function(target, clicked, representation_id) {
105 var snippet = '<div><img src="'+ representation_id +'" /></div>'; 106 var snippet = '<div><img src="'+ representation_id +'" /></div>';
106 $(target).replaceSelection(snippet, true); 107 $(target).replaceSelection(snippet, true);
107 e.preventDefault(); 108 e.preventDefault();
108 } 109 },
110 processIframeClick: function (target, clicked, representation_id) { return true; },
111
112 iframeTargetClass: 'dnd-iframe-target',
113 textareaTargetClass: 'dnd-textarea-target',
114 processedClass: 'dnd-processed'
109 115
110 }, opt); 116 }, opt);
111 117
112 // Initialize plugin 118 // Initialize plugin
113 var targets = opt.processTargets(opt.targets); 119 var targets = opt.processTargets(opt.targets);
114 120
115 // Process! 121 // Watch iframes for changes
122 $(targets).filter('iframe').each(function() {
123 var target = this;
124 var t = setInterval(function() {
125 $('img:not(.dnd-dropped)', $(target).contents()).each(function() {
126 opt.processIframeDrop.call(target, this, opt.idSelector);
127 });
128 }, opt.interval);
129
130 // Track current active timers -- developers working with DnD
131 // can implement their own garbage collection for specific
132 // interactions, such as paging or live search.
133 var data = $(document).data('dnd_timers');
134 if (!data) { data = {}; }
135 data[target.id] = t;
136 $(document).data('dnd_timers', data);
137 });
138
139 // Process each draggable element
116 return this.each(function() { 140 return this.each(function() {
117 if ($(this).is('img')) { 141 if ($(this).is('img')) {
118 var element = this, $element = $(element); 142 var element = this, $element = $(element);
119 143
120 // If we don't have a proper id, bail
121 var representation_id = opt.idSelector(element); 144 var representation_id = opt.idSelector(element);
122 145
123 if (!representation_id) { 146 if (!representation_id) {
124 return this; 147 return this;
125 }; 148 };
126 149
127 // Add a special class 150 // Add a special class
128 $(element).addClass('dnd-processed'); 151 $(element).addClass(opt.processedClass);
129 152
130 // We need to differentiate behavior based on the targets... I guess. 153 // We need to differentiate behavior based on the targets
131 targets.each(function() { 154 targets.each(function() {
132 var target = this, $target = $(target); 155 var target = this, $target = $(target);
133 if ($target.is('iframe')) { 156 if ($target.is('iframe')) {
134 157 $(element).addClass(opt.iframeTargetClass);
135 // Indicate this element is draggy 158 $(element).click(function() {
136 $element.css('cursor', 'move'); 159 opt.processIframeClick.call(target, element, representation_id);
137 160 });
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 } else if ($target.is('textarea')) {
161 $element.css('cursor', 'pointer'); 162 $(element).addClass(opt.textareaTargetClass);
162 $(element).click(function(e, data) { 163 $(element).click(function() {
163 opt.processTextAreaDrop(target, element, representation_id, e, data); 164 opt.processTextAreaClick.call(target, element, representation_id);
164 }); 165 });
165 } 166 }
166 }); 167 });
167 } 168 }
168 }); 169 });