32 containment.each( function(c) { options._containers.push($(c)) }); |
31 containment.each( function(c) { options._containers.push($(c)) }); |
33 } else { |
32 } else { |
34 options._containers.push($(containment)); |
33 options._containers.push($(containment)); |
35 } |
34 } |
36 } |
35 } |
37 |
36 |
38 if(options.accept) options.accept = [options.accept].flatten(); |
37 if(options.accept) options.accept = [options.accept].flatten(); |
39 |
38 |
40 Element.makePositioned(element); // fix IE |
39 Element.makePositioned(element); // fix IE |
41 options.element = element; |
40 options.element = element; |
42 |
41 |
43 this.drops.push(options); |
42 this.drops.push(options); |
44 }, |
43 }, |
45 |
44 |
46 findDeepestChild: function(drops) { |
45 findDeepestChild: function(drops) { |
47 deepest = drops[0]; |
46 deepest = drops[0]; |
48 |
47 |
49 for (i = 1; i < drops.length; ++i) |
48 for (i = 1; i < drops.length; ++i) |
50 if (Element.isParent(drops[i].element, deepest.element)) |
49 if (Element.isParent(drops[i].element, deepest.element)) |
51 deepest = drops[i]; |
50 deepest = drops[i]; |
52 |
51 |
53 return deepest; |
52 return deepest; |
54 }, |
53 }, |
55 |
54 |
56 isContained: function(element, drop) { |
55 isContained: function(element, drop) { |
57 var containmentNode; |
56 var containmentNode; |
58 if(drop.tree) { |
57 if(drop.tree) { |
59 containmentNode = element.treeNode; |
58 containmentNode = element.treeNode; |
60 } else { |
59 } else { |
61 containmentNode = element.parentNode; |
60 containmentNode = element.parentNode; |
62 } |
61 } |
63 return drop._containers.detect(function(c) { return containmentNode == c }); |
62 return drop._containers.detect(function(c) { return containmentNode == c }); |
64 }, |
63 }, |
65 |
64 |
66 isAffected: function(point, element, drop) { |
65 isAffected: function(point, element, drop) { |
67 return ( |
66 return ( |
68 (drop.element!=element) && |
67 (drop.element!=element) && |
69 ((!drop._containers) || |
68 ((!drop._containers) || |
70 this.isContained(element, drop)) && |
69 this.isContained(element, drop)) && |
71 ((!drop.accept) || |
70 ((!drop.accept) || |
72 (Element.classNames(element).detect( |
71 (Element.classNames(element).detect( |
73 function(v) { return drop.accept.include(v) } ) )) && |
72 function(v) { return drop.accept.include(v) } ) )) && |
74 Position.within(drop.element, point[0], point[1]) ); |
73 Position.within(drop.element, point[0], point[1]) ); |
75 }, |
74 }, |
76 |
75 |
77 deactivate: function(drop) { |
76 deactivate: function(drop) { |
87 }, |
86 }, |
88 |
87 |
89 show: function(point, element) { |
88 show: function(point, element) { |
90 if(!this.drops.length) return; |
89 if(!this.drops.length) return; |
91 var drop, affected = []; |
90 var drop, affected = []; |
92 |
91 |
93 this.drops.each( function(drop) { |
92 this.drops.each( function(drop) { |
94 if(Droppables.isAffected(point, element, drop)) |
93 if(Droppables.isAffected(point, element, drop)) |
95 affected.push(drop); |
94 affected.push(drop); |
96 }); |
95 }); |
97 |
96 |
98 if(affected.length>0) |
97 if(affected.length>0) |
99 drop = Droppables.findDeepestChild(affected); |
98 drop = Droppables.findDeepestChild(affected); |
100 |
99 |
101 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); |
100 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); |
102 if (drop) { |
101 if (drop) { |
103 Position.within(drop.element, point[0], point[1]); |
102 Position.within(drop.element, point[0], point[1]); |
104 if(drop.onHover) |
103 if(drop.onHover) |
105 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); |
104 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); |
106 |
105 |
107 if (drop != this.last_active) Droppables.activate(drop); |
106 if (drop != this.last_active) Droppables.activate(drop); |
108 } |
107 } |
109 }, |
108 }, |
110 |
109 |
111 fire: function(event, element) { |
110 fire: function(event, element) { |
112 if(!this.last_active) return; |
111 if(!this.last_active) return; |
113 Position.prepare(); |
112 Position.prepare(); |
114 |
113 |
115 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) |
114 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) |
116 if (this.last_active.onDrop) { |
115 if (this.last_active.onDrop) { |
117 this.last_active.onDrop(element, this.last_active.element, event); |
116 this.last_active.onDrop(element, this.last_active.element, event); |
118 return true; |
117 return true; |
119 } |
118 } |
120 }, |
119 }, |
121 |
120 |
122 reset: function() { |
121 reset: function() { |
123 if(this.last_active) |
122 if(this.last_active) |
124 this.deactivate(this.last_active); |
123 this.deactivate(this.last_active); |
125 } |
124 } |
126 } |
125 }; |
127 |
126 |
128 var Draggables = { |
127 var Draggables = { |
129 drags: [], |
128 drags: [], |
130 observers: [], |
129 observers: [], |
131 |
130 |
132 register: function(draggable) { |
131 register: function(draggable) { |
133 if(this.drags.length == 0) { |
132 if(this.drags.length == 0) { |
134 this.eventMouseUp = this.endDrag.bindAsEventListener(this); |
133 this.eventMouseUp = this.endDrag.bindAsEventListener(this); |
135 this.eventMouseMove = this.updateDrag.bindAsEventListener(this); |
134 this.eventMouseMove = this.updateDrag.bindAsEventListener(this); |
136 this.eventKeypress = this.keyPress.bindAsEventListener(this); |
135 this.eventKeypress = this.keyPress.bindAsEventListener(this); |
137 |
136 |
138 Event.observe(document, "mouseup", this.eventMouseUp); |
137 Event.observe(document, "mouseup", this.eventMouseUp); |
139 Event.observe(document, "mousemove", this.eventMouseMove); |
138 Event.observe(document, "mousemove", this.eventMouseMove); |
140 Event.observe(document, "keypress", this.eventKeypress); |
139 Event.observe(document, "keypress", this.eventKeypress); |
141 } |
140 } |
142 this.drags.push(draggable); |
141 this.drags.push(draggable); |
143 }, |
142 }, |
144 |
143 |
145 unregister: function(draggable) { |
144 unregister: function(draggable) { |
146 this.drags = this.drags.reject(function(d) { return d==draggable }); |
145 this.drags = this.drags.reject(function(d) { return d==draggable }); |
147 if(this.drags.length == 0) { |
146 if(this.drags.length == 0) { |
148 Event.stopObserving(document, "mouseup", this.eventMouseUp); |
147 Event.stopObserving(document, "mouseup", this.eventMouseUp); |
149 Event.stopObserving(document, "mousemove", this.eventMouseMove); |
148 Event.stopObserving(document, "mousemove", this.eventMouseMove); |
150 Event.stopObserving(document, "keypress", this.eventKeypress); |
149 Event.stopObserving(document, "keypress", this.eventKeypress); |
151 } |
150 } |
152 }, |
151 }, |
153 |
152 |
154 activate: function(draggable) { |
153 activate: function(draggable) { |
155 if(draggable.options.delay) { |
154 if(draggable.options.delay) { |
156 this._timeout = setTimeout(function() { |
155 this._timeout = setTimeout(function() { |
157 Draggables._timeout = null; |
156 Draggables._timeout = null; |
158 window.focus(); |
157 window.focus(); |
159 Draggables.activeDraggable = draggable; |
158 Draggables.activeDraggable = draggable; |
160 }.bind(this), draggable.options.delay); |
159 }.bind(this), draggable.options.delay); |
161 } else { |
160 } else { |
162 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari |
161 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari |
163 this.activeDraggable = draggable; |
162 this.activeDraggable = draggable; |
164 } |
163 } |
165 }, |
164 }, |
166 |
165 |
167 deactivate: function() { |
166 deactivate: function() { |
168 this.activeDraggable = null; |
167 this.activeDraggable = null; |
169 }, |
168 }, |
170 |
169 |
171 updateDrag: function(event) { |
170 updateDrag: function(event) { |
172 if(!this.activeDraggable) return; |
171 if(!this.activeDraggable) return; |
173 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
172 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
174 // Mozilla-based browsers fire successive mousemove events with |
173 // Mozilla-based browsers fire successive mousemove events with |
175 // the same coordinates, prevent needless redrawing (moz bug?) |
174 // the same coordinates, prevent needless redrawing (moz bug?) |
176 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; |
175 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; |
177 this._lastPointer = pointer; |
176 this._lastPointer = pointer; |
178 |
177 |
179 this.activeDraggable.updateDrag(event, pointer); |
178 this.activeDraggable.updateDrag(event, pointer); |
180 }, |
179 }, |
181 |
180 |
182 endDrag: function(event) { |
181 endDrag: function(event) { |
183 if(this._timeout) { |
182 if(this._timeout) { |
184 clearTimeout(this._timeout); |
183 clearTimeout(this._timeout); |
185 this._timeout = null; |
184 this._timeout = null; |
186 } |
185 } |
187 if(!this.activeDraggable) return; |
186 if(!this.activeDraggable) return; |
188 this._lastPointer = null; |
187 this._lastPointer = null; |
189 this.activeDraggable.endDrag(event); |
188 this.activeDraggable.endDrag(event); |
190 this.activeDraggable = null; |
189 this.activeDraggable = null; |
191 }, |
190 }, |
192 |
191 |
193 keyPress: function(event) { |
192 keyPress: function(event) { |
194 if(this.activeDraggable) |
193 if(this.activeDraggable) |
195 this.activeDraggable.keyPress(event); |
194 this.activeDraggable.keyPress(event); |
196 }, |
195 }, |
197 |
196 |
198 addObserver: function(observer) { |
197 addObserver: function(observer) { |
199 this.observers.push(observer); |
198 this.observers.push(observer); |
200 this._cacheObserverCallbacks(); |
199 this._cacheObserverCallbacks(); |
201 }, |
200 }, |
202 |
201 |
203 removeObserver: function(element) { // element instead of observer fixes mem leaks |
202 removeObserver: function(element) { // element instead of observer fixes mem leaks |
204 this.observers = this.observers.reject( function(o) { return o.element==element }); |
203 this.observers = this.observers.reject( function(o) { return o.element==element }); |
205 this._cacheObserverCallbacks(); |
204 this._cacheObserverCallbacks(); |
206 }, |
205 }, |
207 |
206 |
208 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' |
207 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' |
209 if(this[eventName+'Count'] > 0) |
208 if(this[eventName+'Count'] > 0) |
210 this.observers.each( function(o) { |
209 this.observers.each( function(o) { |
211 if(o[eventName]) o[eventName](eventName, draggable, event); |
210 if(o[eventName]) o[eventName](eventName, draggable, event); |
212 }); |
211 }); |
213 if(draggable.options[eventName]) draggable.options[eventName](draggable, event); |
212 if(draggable.options[eventName]) draggable.options[eventName](draggable, event); |
214 }, |
213 }, |
215 |
214 |
216 _cacheObserverCallbacks: function() { |
215 _cacheObserverCallbacks: function() { |
217 ['onStart','onEnd','onDrag'].each( function(eventName) { |
216 ['onStart','onEnd','onDrag'].each( function(eventName) { |
218 Draggables[eventName+'Count'] = Draggables.observers.select( |
217 Draggables[eventName+'Count'] = Draggables.observers.select( |
219 function(o) { return o[eventName]; } |
218 function(o) { return o[eventName]; } |
220 ).length; |
219 ).length; |
221 }); |
220 }); |
222 } |
221 } |
223 } |
222 }; |
224 |
223 |
225 /*--------------------------------------------------------------------------*/ |
224 /*--------------------------------------------------------------------------*/ |
226 |
225 |
227 var Draggable = Class.create({ |
226 var Draggable = Class.create({ |
228 initialize: function(element) { |
227 initialize: function(element) { |
234 queue: {scope:'_draggable', position:'end'} |
233 queue: {scope:'_draggable', position:'end'} |
235 }); |
234 }); |
236 }, |
235 }, |
237 endeffect: function(element) { |
236 endeffect: function(element) { |
238 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; |
237 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; |
239 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, |
238 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, |
240 queue: {scope:'_draggable', position:'end'}, |
239 queue: {scope:'_draggable', position:'end'}, |
241 afterFinish: function(){ |
240 afterFinish: function(){ |
242 Draggable._dragging[element] = false |
241 Draggable._dragging[element] = false |
243 } |
242 } |
244 }); |
243 }); |
245 }, |
244 }, |
246 zindex: 1000, |
245 zindex: 1000, |
247 revert: false, |
246 revert: false, |
248 quiet: false, |
247 quiet: false, |
249 scroll: false, |
248 scroll: false, |
250 scrollSensitivity: 20, |
249 scrollSensitivity: 20, |
251 scrollSpeed: 15, |
250 scrollSpeed: 15, |
252 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } |
251 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } |
253 delay: 0 |
252 delay: 0 |
254 }; |
253 }; |
255 |
254 |
256 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) |
255 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) |
257 Object.extend(defaults, { |
256 Object.extend(defaults, { |
258 starteffect: function(element) { |
257 starteffect: function(element) { |
259 element._opacity = Element.getOpacity(element); |
258 element._opacity = Element.getOpacity(element); |
260 Draggable._dragging[element] = true; |
259 Draggable._dragging[element] = true; |
261 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); |
260 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); |
262 } |
261 } |
263 }); |
262 }); |
264 |
263 |
265 var options = Object.extend(defaults, arguments[1] || { }); |
264 var options = Object.extend(defaults, arguments[1] || { }); |
266 |
265 |
267 this.element = $(element); |
266 this.element = $(element); |
268 |
267 |
269 if(options.handle && Object.isString(options.handle)) |
268 if(options.handle && Object.isString(options.handle)) |
270 this.handle = this.element.down('.'+options.handle, 0); |
269 this.handle = this.element.down('.'+options.handle, 0); |
271 |
270 |
272 if(!this.handle) this.handle = $(options.handle); |
271 if(!this.handle) this.handle = $(options.handle); |
273 if(!this.handle) this.handle = this.element; |
272 if(!this.handle) this.handle = this.element; |
274 |
273 |
275 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { |
274 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { |
276 options.scroll = $(options.scroll); |
275 options.scroll = $(options.scroll); |
277 this._isScrollChild = Element.childOf(this.element, options.scroll); |
276 this._isScrollChild = Element.childOf(this.element, options.scroll); |
278 } |
277 } |
279 |
278 |
280 Element.makePositioned(this.element); // fix IE |
279 Element.makePositioned(this.element); // fix IE |
281 |
280 |
282 this.options = options; |
281 this.options = options; |
283 this.dragging = false; |
282 this.dragging = false; |
284 |
283 |
285 this.eventMouseDown = this.initDrag.bindAsEventListener(this); |
284 this.eventMouseDown = this.initDrag.bindAsEventListener(this); |
286 Event.observe(this.handle, "mousedown", this.eventMouseDown); |
285 Event.observe(this.handle, "mousedown", this.eventMouseDown); |
287 |
286 |
288 Draggables.register(this); |
287 Draggables.register(this); |
289 }, |
288 }, |
290 |
289 |
291 destroy: function() { |
290 destroy: function() { |
292 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); |
291 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); |
293 Draggables.unregister(this); |
292 Draggables.unregister(this); |
294 }, |
293 }, |
295 |
294 |
296 currentDelta: function() { |
295 currentDelta: function() { |
297 return([ |
296 return([ |
298 parseInt(Element.getStyle(this.element,'left') || '0'), |
297 parseInt(Element.getStyle(this.element,'left') || '0'), |
299 parseInt(Element.getStyle(this.element,'top') || '0')]); |
298 parseInt(Element.getStyle(this.element,'top') || '0')]); |
300 }, |
299 }, |
301 |
300 |
302 initDrag: function(event) { |
301 initDrag: function(event) { |
303 if(!Object.isUndefined(Draggable._dragging[this.element]) && |
302 if(!Object.isUndefined(Draggable._dragging[this.element]) && |
304 Draggable._dragging[this.element]) return; |
303 Draggable._dragging[this.element]) return; |
305 if(Event.isLeftClick(event)) { |
304 if(Event.isLeftClick(event)) { |
306 // abort on form elements, fixes a Firefox issue |
305 // abort on form elements, fixes a Firefox issue |
307 var src = Event.element(event); |
306 var src = Event.element(event); |
308 if((tag_name = src.tagName.toUpperCase()) && ( |
307 if((tag_name = src.tagName.toUpperCase()) && ( |
309 tag_name=='INPUT' || |
308 tag_name=='INPUT' || |
310 tag_name=='SELECT' || |
309 tag_name=='SELECT' || |
311 tag_name=='OPTION' || |
310 tag_name=='OPTION' || |
312 tag_name=='BUTTON' || |
311 tag_name=='BUTTON' || |
313 tag_name=='TEXTAREA')) return; |
312 tag_name=='TEXTAREA')) return; |
314 |
313 |
315 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
314 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
316 var pos = Position.cumulativeOffset(this.element); |
315 var pos = this.element.cumulativeOffset(); |
317 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); |
316 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); |
318 |
317 |
319 Draggables.activate(this); |
318 Draggables.activate(this); |
320 Event.stop(event); |
319 Event.stop(event); |
321 } |
320 } |
322 }, |
321 }, |
323 |
322 |
324 startDrag: function(event) { |
323 startDrag: function(event) { |
325 this.dragging = true; |
324 this.dragging = true; |
326 if(!this.delta) |
325 if(!this.delta) |
327 this.delta = this.currentDelta(); |
326 this.delta = this.currentDelta(); |
328 |
327 |
329 if(this.options.zindex) { |
328 if(this.options.zindex) { |
330 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); |
329 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); |
331 this.element.style.zIndex = this.options.zindex; |
330 this.element.style.zIndex = this.options.zindex; |
332 } |
331 } |
333 |
332 |
334 if(this.options.ghosting) { |
333 if(this.options.ghosting) { |
335 this._clone = this.element.cloneNode(true); |
334 this._clone = this.element.cloneNode(true); |
336 this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); |
335 this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); |
337 if (!this.element._originallyAbsolute) |
336 if (!this._originallyAbsolute) |
338 Position.absolutize(this.element); |
337 Position.absolutize(this.element); |
339 this.element.parentNode.insertBefore(this._clone, this.element); |
338 this.element.parentNode.insertBefore(this._clone, this.element); |
340 } |
339 } |
341 |
340 |
342 if(this.options.scroll) { |
341 if(this.options.scroll) { |
343 if (this.options.scroll == window) { |
342 if (this.options.scroll == window) { |
344 var where = this._getWindowScroll(this.options.scroll); |
343 var where = this._getWindowScroll(this.options.scroll); |
345 this.originalScrollLeft = where.left; |
344 this.originalScrollLeft = where.left; |
346 this.originalScrollTop = where.top; |
345 this.originalScrollTop = where.top; |
347 } else { |
346 } else { |
348 this.originalScrollLeft = this.options.scroll.scrollLeft; |
347 this.originalScrollLeft = this.options.scroll.scrollLeft; |
349 this.originalScrollTop = this.options.scroll.scrollTop; |
348 this.originalScrollTop = this.options.scroll.scrollTop; |
350 } |
349 } |
351 } |
350 } |
352 |
351 |
353 Draggables.notify('onStart', this, event); |
352 Draggables.notify('onStart', this, event); |
354 |
353 |
355 if(this.options.starteffect) this.options.starteffect(this.element); |
354 if(this.options.starteffect) this.options.starteffect(this.element); |
356 }, |
355 }, |
357 |
356 |
358 updateDrag: function(event, pointer) { |
357 updateDrag: function(event, pointer) { |
359 if(!this.dragging) this.startDrag(event); |
358 if(!this.dragging) this.startDrag(event); |
360 |
359 |
361 if(!this.options.quiet){ |
360 if(!this.options.quiet){ |
362 Position.prepare(); |
361 Position.prepare(); |
363 Droppables.show(pointer, this.element); |
362 Droppables.show(pointer, this.element); |
364 } |
363 } |
365 |
364 |
366 Draggables.notify('onDrag', this, event); |
365 Draggables.notify('onDrag', this, event); |
367 |
366 |
368 this.draw(pointer); |
367 this.draw(pointer); |
369 if(this.options.change) this.options.change(this); |
368 if(this.options.change) this.options.change(this); |
370 |
369 |
371 if(this.options.scroll) { |
370 if(this.options.scroll) { |
372 this.stopScrolling(); |
371 this.stopScrolling(); |
373 |
372 |
374 var p; |
373 var p; |
375 if (this.options.scroll == window) { |
374 if (this.options.scroll == window) { |
376 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } |
375 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } |
377 } else { |
376 } else { |
378 p = Position.page(this.options.scroll); |
377 p = Position.page(this.options.scroll); |
386 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); |
385 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); |
387 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); |
386 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); |
388 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); |
387 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); |
389 this.startScrolling(speed); |
388 this.startScrolling(speed); |
390 } |
389 } |
391 |
390 |
392 // fix AppleWebKit rendering |
391 // fix AppleWebKit rendering |
393 if(Prototype.Browser.WebKit) window.scrollBy(0,0); |
392 if(Prototype.Browser.WebKit) window.scrollBy(0,0); |
394 |
393 |
395 Event.stop(event); |
394 Event.stop(event); |
396 }, |
395 }, |
397 |
396 |
398 finishDrag: function(event, success) { |
397 finishDrag: function(event, success) { |
399 this.dragging = false; |
398 this.dragging = false; |
400 |
399 |
401 if(this.options.quiet){ |
400 if(this.options.quiet){ |
402 Position.prepare(); |
401 Position.prepare(); |
403 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
402 var pointer = [Event.pointerX(event), Event.pointerY(event)]; |
404 Droppables.show(pointer, this.element); |
403 Droppables.show(pointer, this.element); |
405 } |
404 } |
406 |
405 |
407 if(this.options.ghosting) { |
406 if(this.options.ghosting) { |
408 if (!this.element._originallyAbsolute) |
407 if (!this._originallyAbsolute) |
409 Position.relativize(this.element); |
408 Position.relativize(this.element); |
410 delete this.element._originallyAbsolute; |
409 delete this._originallyAbsolute; |
411 Element.remove(this._clone); |
410 Element.remove(this._clone); |
412 this._clone = null; |
411 this._clone = null; |
413 } |
412 } |
414 |
413 |
415 var dropped = false; |
414 var dropped = false; |
416 if(success) { |
415 if(success) { |
417 dropped = Droppables.fire(event, this.element); |
416 dropped = Droppables.fire(event, this.element); |
418 if (!dropped) dropped = false; |
417 if (!dropped) dropped = false; |
419 } |
418 } |
420 if(dropped && this.options.onDropped) this.options.onDropped(this.element); |
419 if(dropped && this.options.onDropped) this.options.onDropped(this.element); |
421 Draggables.notify('onEnd', this, event); |
420 Draggables.notify('onEnd', this, event); |
422 |
421 |
423 var revert = this.options.revert; |
422 var revert = this.options.revert; |
424 if(revert && Object.isFunction(revert)) revert = revert(this.element); |
423 if(revert && Object.isFunction(revert)) revert = revert(this.element); |
425 |
424 |
426 var d = this.currentDelta(); |
425 var d = this.currentDelta(); |
427 if(revert && this.options.reverteffect) { |
426 if(revert && this.options.reverteffect) { |
428 if (dropped == 0 || revert != 'failure') |
427 if (dropped == 0 || revert != 'failure') |
429 this.options.reverteffect(this.element, |
428 this.options.reverteffect(this.element, |
430 d[1]-this.delta[1], d[0]-this.delta[0]); |
429 d[1]-this.delta[1], d[0]-this.delta[0]); |
433 } |
432 } |
434 |
433 |
435 if(this.options.zindex) |
434 if(this.options.zindex) |
436 this.element.style.zIndex = this.originalZ; |
435 this.element.style.zIndex = this.originalZ; |
437 |
436 |
438 if(this.options.endeffect) |
437 if(this.options.endeffect) |
439 this.options.endeffect(this.element); |
438 this.options.endeffect(this.element); |
440 |
439 |
441 Draggables.deactivate(this); |
440 Draggables.deactivate(this); |
442 Droppables.reset(); |
441 Droppables.reset(); |
443 }, |
442 }, |
444 |
443 |
445 keyPress: function(event) { |
444 keyPress: function(event) { |
446 if(event.keyCode!=Event.KEY_ESC) return; |
445 if(event.keyCode!=Event.KEY_ESC) return; |
447 this.finishDrag(event, false); |
446 this.finishDrag(event, false); |
448 Event.stop(event); |
447 Event.stop(event); |
449 }, |
448 }, |
450 |
449 |
451 endDrag: function(event) { |
450 endDrag: function(event) { |
452 if(!this.dragging) return; |
451 if(!this.dragging) return; |
453 this.stopScrolling(); |
452 this.stopScrolling(); |
454 this.finishDrag(event, true); |
453 this.finishDrag(event, true); |
455 Event.stop(event); |
454 Event.stop(event); |
456 }, |
455 }, |
457 |
456 |
458 draw: function(point) { |
457 draw: function(point) { |
459 var pos = Position.cumulativeOffset(this.element); |
458 var pos = this.element.cumulativeOffset(); |
460 if(this.options.ghosting) { |
459 if(this.options.ghosting) { |
461 var r = Position.realOffset(this.element); |
460 var r = Position.realOffset(this.element); |
462 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; |
461 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; |
463 } |
462 } |
464 |
463 |
465 var d = this.currentDelta(); |
464 var d = this.currentDelta(); |
466 pos[0] -= d[0]; pos[1] -= d[1]; |
465 pos[0] -= d[0]; pos[1] -= d[1]; |
467 |
466 |
468 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { |
467 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { |
469 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; |
468 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; |
470 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; |
469 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; |
471 } |
470 } |
472 |
471 |
473 var p = [0,1].map(function(i){ |
472 var p = [0,1].map(function(i){ |
474 return (point[i]-pos[i]-this.offset[i]) |
473 return (point[i]-pos[i]-this.offset[i]) |
475 }.bind(this)); |
474 }.bind(this)); |
476 |
475 |
477 if(this.options.snap) { |
476 if(this.options.snap) { |
478 if(Object.isFunction(this.options.snap)) { |
477 if(Object.isFunction(this.options.snap)) { |
479 p = this.options.snap(p[0],p[1],this); |
478 p = this.options.snap(p[0],p[1],this); |
480 } else { |
479 } else { |
481 if(Object.isArray(this.options.snap)) { |
480 if(Object.isArray(this.options.snap)) { |
482 p = p.map( function(v, i) { |
481 p = p.map( function(v, i) { |
483 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)) |
482 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); |
484 } else { |
483 } else { |
485 p = p.map( function(v) { |
484 p = p.map( function(v) { |
486 return (v/this.options.snap).round()*this.options.snap }.bind(this)) |
485 return (v/this.options.snap).round()*this.options.snap }.bind(this)); |
487 } |
486 } |
488 }} |
487 }} |
489 |
488 |
490 var style = this.element.style; |
489 var style = this.element.style; |
491 if((!this.options.constraint) || (this.options.constraint=='horizontal')) |
490 if((!this.options.constraint) || (this.options.constraint=='horizontal')) |
492 style.left = p[0] + "px"; |
491 style.left = p[0] + "px"; |
493 if((!this.options.constraint) || (this.options.constraint=='vertical')) |
492 if((!this.options.constraint) || (this.options.constraint=='vertical')) |
494 style.top = p[1] + "px"; |
493 style.top = p[1] + "px"; |
495 |
494 |
496 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering |
495 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering |
497 }, |
496 }, |
498 |
497 |
499 stopScrolling: function() { |
498 stopScrolling: function() { |
500 if(this.scrollInterval) { |
499 if(this.scrollInterval) { |
501 clearInterval(this.scrollInterval); |
500 clearInterval(this.scrollInterval); |
502 this.scrollInterval = null; |
501 this.scrollInterval = null; |
503 Draggables._lastScrollPointer = null; |
502 Draggables._lastScrollPointer = null; |
504 } |
503 } |
505 }, |
504 }, |
506 |
505 |
507 startScrolling: function(speed) { |
506 startScrolling: function(speed) { |
508 if(!(speed[0] || speed[1])) return; |
507 if(!(speed[0] || speed[1])) return; |
509 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; |
508 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; |
510 this.lastScrolled = new Date(); |
509 this.lastScrolled = new Date(); |
511 this.scrollInterval = setInterval(this.scroll.bind(this), 10); |
510 this.scrollInterval = setInterval(this.scroll.bind(this), 10); |
512 }, |
511 }, |
513 |
512 |
514 scroll: function() { |
513 scroll: function() { |
515 var current = new Date(); |
514 var current = new Date(); |
516 var delta = current - this.lastScrolled; |
515 var delta = current - this.lastScrolled; |
517 this.lastScrolled = current; |
516 this.lastScrolled = current; |
518 if(this.options.scroll == window) { |
517 if(this.options.scroll == window) { |
577 initialize: function(element, observer) { |
576 initialize: function(element, observer) { |
578 this.element = $(element); |
577 this.element = $(element); |
579 this.observer = observer; |
578 this.observer = observer; |
580 this.lastValue = Sortable.serialize(this.element); |
579 this.lastValue = Sortable.serialize(this.element); |
581 }, |
580 }, |
582 |
581 |
583 onStart: function() { |
582 onStart: function() { |
584 this.lastValue = Sortable.serialize(this.element); |
583 this.lastValue = Sortable.serialize(this.element); |
585 }, |
584 }, |
586 |
585 |
587 onEnd: function() { |
586 onEnd: function() { |
588 Sortable.unmark(); |
587 Sortable.unmark(); |
589 if(this.lastValue != Sortable.serialize(this.element)) |
588 if(this.lastValue != Sortable.serialize(this.element)) |
590 this.observer(this.element) |
589 this.observer(this.element) |
591 } |
590 } |
592 }); |
591 }); |
593 |
592 |
594 var Sortable = { |
593 var Sortable = { |
595 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, |
594 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, |
596 |
595 |
597 sortables: { }, |
596 sortables: { }, |
598 |
597 |
599 _findRootElement: function(element) { |
598 _findRootElement: function(element) { |
600 while (element.tagName.toUpperCase() != "BODY") { |
599 while (element.tagName.toUpperCase() != "BODY") { |
601 if(element.id && Sortable.sortables[element.id]) return element; |
600 if(element.id && Sortable.sortables[element.id]) return element; |
602 element = element.parentNode; |
601 element = element.parentNode; |
603 } |
602 } |
604 }, |
603 }, |
605 |
604 |
606 options: function(element) { |
605 options: function(element) { |
607 element = Sortable._findRootElement($(element)); |
606 element = Sortable._findRootElement($(element)); |
608 if(!element) return; |
607 if(!element) return; |
609 return Sortable.sortables[element.id]; |
608 return Sortable.sortables[element.id]; |
610 }, |
609 }, |
611 |
610 |
612 destroy: function(element){ |
611 destroy: function(element){ |
613 var s = Sortable.options(element); |
612 element = $(element); |
614 |
613 var s = Sortable.sortables[element.id]; |
|
614 |
615 if(s) { |
615 if(s) { |
616 Draggables.removeObserver(s.element); |
616 Draggables.removeObserver(s.element); |
617 s.droppables.each(function(d){ Droppables.remove(d) }); |
617 s.droppables.each(function(d){ Droppables.remove(d) }); |
618 s.draggables.invoke('destroy'); |
618 s.draggables.invoke('destroy'); |
619 |
619 |
620 delete Sortable.sortables[s.element.id]; |
620 delete Sortable.sortables[s.element.id]; |
621 } |
621 } |
622 }, |
622 }, |
623 |
623 |
624 create: function(element) { |
624 create: function(element) { |
625 element = $(element); |
625 element = $(element); |
626 var options = Object.extend({ |
626 var options = Object.extend({ |
627 element: element, |
627 element: element, |
628 tag: 'li', // assumes li children, override with tag: 'tagname' |
628 tag: 'li', // assumes li children, override with tag: 'tagname' |
629 dropOnEmpty: false, |
629 dropOnEmpty: false, |
630 tree: false, |
630 tree: false, |
631 treeTag: 'ul', |
631 treeTag: 'ul', |
758 Sortable.mark(dropon, 'before'); |
758 Sortable.mark(dropon, 'before'); |
759 if(dropon.previousSibling != element) { |
759 if(dropon.previousSibling != element) { |
760 var oldParentNode = element.parentNode; |
760 var oldParentNode = element.parentNode; |
761 element.style.visibility = "hidden"; // fix gecko rendering |
761 element.style.visibility = "hidden"; // fix gecko rendering |
762 dropon.parentNode.insertBefore(element, dropon); |
762 dropon.parentNode.insertBefore(element, dropon); |
763 if(dropon.parentNode!=oldParentNode) |
763 if(dropon.parentNode!=oldParentNode) |
764 Sortable.options(oldParentNode).onChange(element); |
764 Sortable.options(oldParentNode).onChange(element); |
765 Sortable.options(dropon.parentNode).onChange(element); |
765 Sortable.options(dropon.parentNode).onChange(element); |
766 } |
766 } |
767 } else { |
767 } else { |
768 Sortable.mark(dropon, 'after'); |
768 Sortable.mark(dropon, 'after'); |
769 var nextElement = dropon.nextSibling || null; |
769 var nextElement = dropon.nextSibling || null; |
770 if(nextElement != element) { |
770 if(nextElement != element) { |
771 var oldParentNode = element.parentNode; |
771 var oldParentNode = element.parentNode; |
772 element.style.visibility = "hidden"; // fix gecko rendering |
772 element.style.visibility = "hidden"; // fix gecko rendering |
773 dropon.parentNode.insertBefore(element, nextElement); |
773 dropon.parentNode.insertBefore(element, nextElement); |
774 if(dropon.parentNode!=oldParentNode) |
774 if(dropon.parentNode!=oldParentNode) |
775 Sortable.options(oldParentNode).onChange(element); |
775 Sortable.options(oldParentNode).onChange(element); |
776 Sortable.options(dropon.parentNode).onChange(element); |
776 Sortable.options(dropon.parentNode).onChange(element); |
777 } |
777 } |
778 } |
778 } |
779 }, |
779 }, |
780 |
780 |
781 onEmptyHover: function(element, dropon, overlap) { |
781 onEmptyHover: function(element, dropon, overlap) { |
782 var oldParentNode = element.parentNode; |
782 var oldParentNode = element.parentNode; |
783 var droponOptions = Sortable.options(dropon); |
783 var droponOptions = Sortable.options(dropon); |
784 |
784 |
785 if(!Element.isParent(dropon, element)) { |
785 if(!Element.isParent(dropon, element)) { |
786 var index; |
786 var index; |
787 |
787 |
788 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); |
788 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); |
789 var child = null; |
789 var child = null; |
790 |
790 |
791 if(children) { |
791 if(children) { |
792 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); |
792 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); |
793 |
793 |
794 for (index = 0; index < children.length; index += 1) { |
794 for (index = 0; index < children.length; index += 1) { |
795 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { |
795 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { |
796 offset -= Element.offsetSize (children[index], droponOptions.overlap); |
796 offset -= Element.offsetSize (children[index], droponOptions.overlap); |
797 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { |
797 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { |
798 child = index + 1 < children.length ? children[index + 1] : null; |
798 child = index + 1 < children.length ? children[index + 1] : null; |
816 }, |
816 }, |
817 |
817 |
818 mark: function(dropon, position) { |
818 mark: function(dropon, position) { |
819 // mark on ghosting only |
819 // mark on ghosting only |
820 var sortable = Sortable.options(dropon.parentNode); |
820 var sortable = Sortable.options(dropon.parentNode); |
821 if(sortable && !sortable.ghosting) return; |
821 if(sortable && !sortable.ghosting) return; |
822 |
822 |
823 if(!Sortable._marker) { |
823 if(!Sortable._marker) { |
824 Sortable._marker = |
824 Sortable._marker = |
825 ($('dropmarker') || Element.extend(document.createElement('DIV'))). |
825 ($('dropmarker') || Element.extend(document.createElement('DIV'))). |
826 hide().addClassName('dropmarker').setStyle({position:'absolute'}); |
826 hide().addClassName('dropmarker').setStyle({position:'absolute'}); |
827 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); |
827 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); |
828 } |
828 } |
829 var offsets = Position.cumulativeOffset(dropon); |
829 var offsets = dropon.cumulativeOffset(); |
830 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); |
830 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); |
831 |
831 |
832 if(position=='after') |
832 if(position=='after') |
833 if(sortable.overlap == 'horizontal') |
833 if(sortable.overlap == 'horizontal') |
834 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); |
834 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); |
835 else |
835 else |
836 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); |
836 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); |
837 |
837 |
838 Sortable._marker.show(); |
838 Sortable._marker.show(); |
839 }, |
839 }, |
840 |
840 |
841 _tree: function(element, options, parent) { |
841 _tree: function(element, options, parent) { |
842 var children = Sortable.findElements(element, options) || []; |
842 var children = Sortable.findElements(element, options) || []; |
843 |
843 |
844 for (var i = 0; i < children.length; ++i) { |
844 for (var i = 0; i < children.length; ++i) { |
845 var match = children[i].id.match(options.format); |
845 var match = children[i].id.match(options.format); |
846 |
846 |
847 if (!match) continue; |
847 if (!match) continue; |
848 |
848 |
849 var child = { |
849 var child = { |
850 id: encodeURIComponent(match ? match[1] : null), |
850 id: encodeURIComponent(match ? match[1] : null), |
851 element: element, |
851 element: element, |
852 parent: parent, |
852 parent: parent, |
853 children: [], |
853 children: [], |
854 position: parent.children.length, |
854 position: parent.children.length, |
855 container: $(children[i]).down(options.treeTag) |
855 container: $(children[i]).down(options.treeTag) |
856 } |
856 }; |
857 |
857 |
858 /* Get the element containing the children and recurse over it */ |
858 /* Get the element containing the children and recurse over it */ |
859 if (child.container) |
859 if (child.container) |
860 this._tree(child.container, options, child) |
860 this._tree(child.container, options, child); |
861 |
861 |
862 parent.children.push (child); |
862 parent.children.push (child); |
863 } |
863 } |
864 |
864 |
865 return parent; |
865 return parent; |
866 }, |
866 }, |
867 |
867 |
868 tree: function(element) { |
868 tree: function(element) { |
869 element = $(element); |
869 element = $(element); |
870 var sortableOptions = this.options(element); |
870 var sortableOptions = this.options(element); |
897 }, |
897 }, |
898 |
898 |
899 sequence: function(element) { |
899 sequence: function(element) { |
900 element = $(element); |
900 element = $(element); |
901 var options = Object.extend(this.options(element), arguments[1] || { }); |
901 var options = Object.extend(this.options(element), arguments[1] || { }); |
902 |
902 |
903 return $(this.findElements(element, options) || []).map( function(item) { |
903 return $(this.findElements(element, options) || []).map( function(item) { |
904 return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; |
904 return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; |
905 }); |
905 }); |
906 }, |
906 }, |
907 |
907 |
908 setSequence: function(element, new_sequence) { |
908 setSequence: function(element, new_sequence) { |
909 element = $(element); |
909 element = $(element); |
910 var options = Object.extend(this.options(element), arguments[2] || { }); |
910 var options = Object.extend(this.options(element), arguments[2] || { }); |
911 |
911 |
912 var nodeMap = { }; |
912 var nodeMap = { }; |
913 this.findElements(element, options).each( function(n) { |
913 this.findElements(element, options).each( function(n) { |
914 if (n.id.match(options.format)) |
914 if (n.id.match(options.format)) |
915 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; |
915 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; |
916 n.parentNode.removeChild(n); |
916 n.parentNode.removeChild(n); |
917 }); |
917 }); |
918 |
918 |
919 new_sequence.each(function(ident) { |
919 new_sequence.each(function(ident) { |
920 var n = nodeMap[ident]; |
920 var n = nodeMap[ident]; |
921 if (n) { |
921 if (n) { |
922 n[1].appendChild(n[0]); |
922 n[1].appendChild(n[0]); |
923 delete nodeMap[ident]; |
923 delete nodeMap[ident]; |
924 } |
924 } |
925 }); |
925 }); |
926 }, |
926 }, |
927 |
927 |
928 serialize: function(element) { |
928 serialize: function(element) { |
929 element = $(element); |
929 element = $(element); |
930 var options = Object.extend(Sortable.options(element), arguments[1] || { }); |
930 var options = Object.extend(Sortable.options(element), arguments[1] || { }); |
931 var name = encodeURIComponent( |
931 var name = encodeURIComponent( |
932 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); |
932 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); |
933 |
933 |
934 if (options.tree) { |
934 if (options.tree) { |
935 return Sortable.tree(element, arguments[1]).children.map( function (item) { |
935 return Sortable.tree(element, arguments[1]).children.map( function (item) { |
936 return [name + Sortable._constructIndex(item) + "[id]=" + |
936 return [name + Sortable._constructIndex(item) + "[id]=" + |
937 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); |
937 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); |
938 }).flatten().join('&'); |
938 }).flatten().join('&'); |
939 } else { |
939 } else { |
940 return Sortable.sequence(element, arguments[1]).map( function(item) { |
940 return Sortable.sequence(element, arguments[1]).map( function(item) { |
941 return name + "[]=" + encodeURIComponent(item); |
941 return name + "[]=" + encodeURIComponent(item); |
942 }).join('&'); |
942 }).join('&'); |
943 } |
943 } |
944 } |
944 } |
945 } |
945 }; |
946 |
946 |
947 // Returns true if child is contained within element |
947 // Returns true if child is contained within element |
948 Element.isParent = function(child, element) { |
948 Element.isParent = function(child, element) { |
949 if (!child.parentNode || child == element) return false; |
949 if (!child.parentNode || child == element) return false; |
950 if (child.parentNode == element) return true; |
950 if (child.parentNode == element) return true; |
951 return Element.isParent(child.parentNode, element); |
951 return Element.isParent(child.parentNode, element); |
952 } |
952 }; |
953 |
953 |
954 Element.findChildren = function(element, only, recursive, tagName) { |
954 Element.findChildren = function(element, only, recursive, tagName) { |
955 if(!element.hasChildNodes()) return null; |
955 if(!element.hasChildNodes()) return null; |
956 tagName = tagName.toUpperCase(); |
956 tagName = tagName.toUpperCase(); |
957 if(only) only = [only].flatten(); |
957 if(only) only = [only].flatten(); |
958 var elements = []; |
958 var elements = []; |
959 $A(element.childNodes).each( function(e) { |
959 $A(element.childNodes).each( function(e) { |