ballonizer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/examples/ballonizer_app/config.ru +59 -0
  3. data/examples/ballonizer_app/index.html +159 -0
  4. data/examples/ballonizer_js_module/index.html +196 -0
  5. data/lib/assets/javascripts/ballonizer.js +482 -0
  6. data/lib/assets/stylesheets/ballonizer.css +78 -0
  7. data/lib/ballonizer.rb +201 -36
  8. data/spec/ballonizer_spec.rb +153 -2
  9. data/spec/javascripts/ballonizer_spec.js +568 -0
  10. data/spec/javascripts/fixtures/ballonized-xkcd-with-anchor-in-image.html +163 -0
  11. data/spec/javascripts/fixtures/ballonized-xkcd-with-ballons.html +163 -0
  12. data/spec/javascripts/fixtures/ballonized-xkcd-without-ballons.html +163 -0
  13. data/spec/javascripts/fixtures/xkcd.css +191 -0
  14. data/spec/javascripts/helpers/jasmine-jquery.js +660 -0
  15. data/spec/javascripts/helpers/jquery.simulate-ext.js +32 -0
  16. data/spec/javascripts/helpers/jquery.simulate.drag-n-drop.js +583 -0
  17. data/spec/javascripts/helpers/jquery.simulate.js +328 -0
  18. data/spec/javascripts/support/jasmine.yml +99 -0
  19. data/vendor/assets/javascripts/jquery-2.0.1.js +8837 -0
  20. data/vendor/assets/javascripts/jquery-ui-1.10.3.custom.min.js +6 -0
  21. data/vendor/assets/javascripts/jquery.json-2.4.min.js +24 -0
  22. data/vendor/assets/stylesheets/ui-lightness/images/animated-overlay.gif +0 -0
  23. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  24. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  25. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  26. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  27. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  28. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  29. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  30. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  31. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  32. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  33. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  34. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  35. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  36. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  37. data/vendor/assets/stylesheets/ui-lightness/jquery-ui-1.10.3.custom.min.css +5 -0
  38. metadata +51 -3
@@ -0,0 +1,583 @@
1
+ /*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
2
+ /*jslint white: true vars: true browser: true todo: true */
3
+ /*global jQuery:true $:true */
4
+
5
+ /* jQuery Simulate Drag-n-Drop Plugin 1.1.5
6
+ * http://github.com/j-ulrich/jquery-simulate-ext
7
+ *
8
+ * Copyright (c) 2013 Jochen Ulrich
9
+ * Licensed under the MIT license (MIT-LICENSE.txt).
10
+ */
11
+
12
+ ;(function($, undefined) {
13
+ "use strict";
14
+
15
+ /* Overwrite the $.fn.simulate function to reduce the jQuery set to the first element for the
16
+ * drag-n-drop interactions.
17
+ */
18
+ $.fn.simulate = function( type, options ) {
19
+ switch (type) {
20
+ case "drag":
21
+ case "drop":
22
+ case "drag-n-drop":
23
+ var ele = this.first();
24
+ new $.simulate( ele[0], type, options);
25
+ return ele;
26
+ default:
27
+ return this.each(function() {
28
+ new $.simulate( this, type, options );
29
+ });
30
+ }
31
+ };
32
+
33
+ var now = Date.now || function() { return new Date().getTime(); };
34
+
35
+ var rdocument = /\[object (?:HTML)?Document\]/;
36
+ /**
37
+ * Tests whether an object is an (HTML) document object.
38
+ * @param {DOM Element} elem - the object/element to be tested
39
+ * @returns {Boolean} <code>true</code> if <i>elem</i> is an (HTML) document object.
40
+ * @private
41
+ * @author julrich
42
+ * @since 1.1
43
+ */
44
+ function isDocument( elem ) {
45
+ return rdocument.test(Object.prototype.toString.call($(elem)[0]));
46
+ }
47
+
48
+ /**
49
+ * Selects the first match from an array.
50
+ * @param {Array} array - Array of objects to be be tested
51
+ * @param {Function} check - Callback function that accepts one argument (which will be one element
52
+ * from the <i>array</i>) and returns a boolean.
53
+ * @returns {Boolean|null} the first element in <i>array</i> for which <i>check</i> returns <code>true</code>.
54
+ * If none of the elements in <i>array</i> passes <i>check</i>, <code>null</code> is returned.
55
+ * @private
56
+ * @author julrich
57
+ * @since 1.1
58
+ */
59
+ function selectFirstMatch(array, check) {
60
+ var i;
61
+ if ($.isFunction(check)) {
62
+ for (i=0; i < array.length; i+=1) {
63
+ if (check(array[i])) {
64
+ return array[i];
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ else {
70
+ for (i=0; i < array.length; i+=1) {
71
+ if (array[i]) {
72
+ return array[i];
73
+ }
74
+ }
75
+ return null;
76
+ }
77
+ }
78
+
79
+ // Based on the findCenter function from jquery.simulate.js
80
+ /**
81
+ * Calculates the position of the center of an DOM element.
82
+ * @param {DOM Element} elem - the element whose center should be calculated.
83
+ * @returns {Object} an object with the properties <code>x</code> and <code>y</code>
84
+ * representing the position of the center of <i>elem</i> in page relative coordinates
85
+ * (i.e. independent of any scrolling).
86
+ * @private
87
+ * @author julrich
88
+ * @since 1.0
89
+ */
90
+ function findCenter( elem ) {
91
+ var offset;
92
+ elem = $( elem );
93
+ if ( isDocument(elem[0]) ) {
94
+ offset = {left: 0, top: 0};
95
+ }
96
+ else {
97
+ offset = elem.offset();
98
+ }
99
+
100
+ return {
101
+ x: offset.left + elem.outerWidth() / 2,
102
+ y: offset.top + elem.outerHeight() / 2
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Converts page relative coordinates into client relative coordinates.
108
+ * @param {Numeric|Object} x - Either the x coordinate of the page relative coordinates or
109
+ * an object with the properties <code>pageX</code> and <code>pageY</code> representing page
110
+ * relative coordinates.
111
+ * @param {Numeric} [y] - If <i>x</i> is numeric (i.e. the x coordinate of page relative coordinates),
112
+ * then this is the y coordinate. If <i>x</i> is an object, this parameter is skipped.
113
+ * @param {DOM Document} [docRel] - Optional DOM document object used to calculate the client relative
114
+ * coordinates. The page relative coordinates are interpreted as being relative to that document and
115
+ * the scroll position of that document is used to calculate the client relative coordinates.
116
+ * By default, <code>document</code> is used.
117
+ * @returns {Object} an object representing the client relative coordinates corresponding to the
118
+ * given page relative coordinates. The object either provides the properties <code>x</code> and
119
+ * <code>y</code> when <i>x</i> and <i>y</i> were given as arguments, or <code>clientX</code>
120
+ * and <code>clientY</code> when the parameter <i>x</i> was given as an object (see above).
121
+ * @private
122
+ * @author julrich
123
+ * @since 1.0
124
+ */
125
+ function pageToClientPos(x, y, docRel) {
126
+ var jDocument;
127
+ if ( isDocument(y) ) {
128
+ jDocument = $(y);
129
+ } else {
130
+ jDocument = $(docRel || document);
131
+ }
132
+
133
+ if (typeof x === "number" && typeof y === "number") {
134
+ return {
135
+ x: x - jDocument.scrollLeft(),
136
+ y: y - jDocument.scrollTop()
137
+ };
138
+ }
139
+ else if (typeof x === "object" && x.pageX && x.pageY) {
140
+ return {
141
+ clientX: x.pageX - jDocument.scrollLeft(),
142
+ clientY: x.pageY - jDocument.scrollTop()
143
+ };
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Browser-independent implementation of <code>document.elementFromPoint()</code>.
149
+ *
150
+ * When run for the first time on a scrolled page, this function performs a check on how
151
+ * <code>document.elementFromPoint()</code> is implemented in the current browser. It stores
152
+ * the results in two static variables so that the check can be skipped for successive calls.
153
+ *
154
+ * @param {Numeric|Object} x - Either the x coordinate of client relative coordinates or an object
155
+ * with the properties <code>x</code> and <code>y</code> representing client relative coordinates.
156
+ * @param {Numeric} [y] - If <i>x</i> is numeric (i.e. the x coordinate of client relative coordinates),
157
+ * this is the y coordinate. If <i>x</i> is an object, this parameter is skipped.
158
+ * @param {DOM Document} [docRel] - Optional DOM document object
159
+ * @returns {DOM Element|Null}
160
+ * @private
161
+ * @author Nicolas Zeh (Basic idea), julrich
162
+ * @see http://www.zehnet.de/2010/11/19/document-elementfrompoint-a-jquery-solution/
163
+ * @since 1.0
164
+ */
165
+ function elementAtPosition(x, y, docRel) {
166
+ var doc;
167
+ if ( isDocument(y) ) {
168
+ doc = y;
169
+ } else {
170
+ doc = docRel || document;
171
+ }
172
+
173
+ if(!doc.elementFromPoint) {
174
+ return null;
175
+ }
176
+
177
+ var clientX = x, clientY = y;
178
+ if (typeof x === "object" && (x.clientX || x.clientY)) {
179
+ clientX = x.clientX || 0 ;
180
+ clientY = x.clientY || 0;
181
+ }
182
+
183
+ if(elementAtPosition.prototype.check)
184
+ {
185
+ var sl, ele;
186
+ if ((sl = $(doc).scrollTop()) >0)
187
+ {
188
+ ele = doc.elementFromPoint(0, sl + $(window).height() -1);
189
+ if ( ele !== null && ele.tagName.toUpperCase() === 'HTML' ) { ele = null; }
190
+ elementAtPosition.prototype.nativeUsesRelative = ( ele === null );
191
+ }
192
+ else if((sl = $(doc).scrollLeft()) >0)
193
+ {
194
+ ele = doc.elementFromPoint(sl + $(window).width() -1, 0);
195
+ if ( ele !== null && ele.tagName.toUpperCase() === 'HTML' ) { ele = null; }
196
+ elementAtPosition.prototype.nativeUsesRelative = ( ele === null );
197
+ }
198
+ elementAtPosition.prototype.check = (sl<=0); // Check was not meaningful because we were at scroll position 0
199
+ }
200
+
201
+ if(!elementAtPosition.prototype.nativeUsesRelative)
202
+ {
203
+ clientX += $(doc).scrollLeft();
204
+ clientY += $(doc).scrollTop();
205
+ }
206
+
207
+ return doc.elementFromPoint(clientX,clientY);
208
+ }
209
+ // Default values for the check variables
210
+ elementAtPosition.prototype.check = true;
211
+ elementAtPosition.prototype.nativeUsesRelative = true;
212
+
213
+ /**
214
+ * Informs the rest of the world that the drag is finished.
215
+ * @param {DOM Element} ele - The element which was dragged.
216
+ * @param {Object} [options] - The drag options.
217
+ * @fires simulate-drag
218
+ * @private
219
+ * @author julrich
220
+ * @since 1.0
221
+ */
222
+ function dragFinished(ele, options) {
223
+ var opts = options || {};
224
+ $(ele).trigger({type: "simulate-drag"});
225
+ if ($.isFunction(opts.callback)) {
226
+ opts.callback.apply(ele);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Generates a series of <code>mousemove</code> events for a drag.
232
+ * @param {Object} self - The simulate object.
233
+ * @param {DOM Element} ele - The element which is dragged.
234
+ * @param {Object} start - The start coordinates of the drag, represented using the properties
235
+ * <code>x</code> and <code>y</code>.
236
+ * @param {Object} drag - The distance to be dragged, represented using the properties <code>dx</code>
237
+ * and <code>dy</code>.
238
+ * @param {Object} options - The drag options. Must have the property <code>interpolation</code>
239
+ * containing the interpolation options (<code>stepWidth</code>, <code>stepCount</code>, etc.).
240
+ * @requires eventTarget
241
+ * @requires now()
242
+ * @private
243
+ * @author julrich
244
+ * @since 1.0
245
+ */
246
+ function interpolatedEvents(self, ele, start, drag, options) {
247
+ var targetDoc = selectFirstMatch([ele, ele.ownerDocument], isDocument) || document,
248
+ interpolOptions = options.interpolation,
249
+ dragDistance = Math.sqrt(Math.pow(drag.dx,2) + Math.pow(drag.dy,2)), // sqrt(a^2 + b^2)
250
+ stepWidth, stepCount, stepVector;
251
+
252
+ if (interpolOptions.stepWidth) {
253
+ stepWidth = parseInt(interpolOptions.stepWidth, 10);
254
+ stepCount = Math.floor(dragDistance / stepWidth)-1;
255
+ var stepScale = stepWidth / dragDistance;
256
+ stepVector = {x: drag.dx*stepScale, y: drag.dy*stepScale };
257
+ }
258
+ else {
259
+ stepCount = parseInt(interpolOptions.stepCount, 10);
260
+ stepWidth = dragDistance / (stepCount+1);
261
+ stepVector = {x: drag.dx/(stepCount+1), y: drag.dy/(stepCount+1)};
262
+ }
263
+
264
+
265
+ var coords = $.extend({},start);
266
+
267
+ /**
268
+ * Calculates the effective coordinates for one <code>mousemove</code> event and fires the event.
269
+ * @requires eventTarget
270
+ * @requires targetDoc
271
+ * @requires coords
272
+ * @requires stepVector
273
+ * @requires interpolOptions
274
+ * @fires mousemove
275
+ * @inner
276
+ * @author julrich
277
+ * @since 1.0
278
+ */
279
+ function interpolationStep() {
280
+ coords.x += stepVector.x;
281
+ coords.y += stepVector.y;
282
+ var effectiveCoords = {pageX: coords.x, pageY: coords.y};
283
+ if (interpolOptions.shaky && (interpolOptions.shaky === true || !isNaN(parseInt(interpolOptions.shaky,10)) )) {
284
+ var amplitude = (interpolOptions.shaky === true)? 1 : parseInt(interpolOptions.shaky,10);
285
+ effectiveCoords.pageX += Math.floor(Math.random()*(2*amplitude+1)-amplitude);
286
+ effectiveCoords.pageY += Math.floor(Math.random()*(2*amplitude+1)-amplitude);
287
+ }
288
+ var clientCoord = pageToClientPos(effectiveCoords, targetDoc),
289
+ eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
290
+ self.simulateEvent( eventTarget, "mousemove", {pageX: Math.round(effectiveCoords.pageX), pageY: Math.round(effectiveCoords.pageY)});
291
+ }
292
+
293
+
294
+ var lastTime;
295
+
296
+ /**
297
+ * Performs one interpolation step (i.e. cares about firing the event) and then sleeps for
298
+ * <code>stepDelay</code> milliseconds.
299
+ * @requires lastTime
300
+ * @requires stepDelay
301
+ * @requires step
302
+ * @requires ele
303
+ * @requires eventTarget
304
+ * @reuiqre targetDoc
305
+ * @requires start
306
+ * @requires drag
307
+ * @requires now()
308
+ * @inner
309
+ * @author julrich
310
+ * @since 1.0
311
+ */
312
+ function stepAndSleep() {
313
+ var timeElapsed = now() - lastTime; // Work-around for Firefox & IE "bug": setTimeout can fire before the timeout
314
+ if (timeElapsed >= stepDelay) {
315
+ if (step < stepCount) {
316
+ interpolationStep();
317
+ step += 1;
318
+ lastTime = now();
319
+ setTimeout(stepAndSleep, stepDelay);
320
+ }
321
+ else {
322
+ var pageCoord = {pageX: Math.round(start.x+drag.dx), pageY: Math.round(start.y+drag.dy)},
323
+ clientCoord = pageToClientPos(pageCoord, targetDoc),
324
+ eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
325
+ self.simulateEvent( eventTarget, "mousemove", pageCoord);
326
+ dragFinished(ele, options);
327
+ }
328
+ }
329
+ else {
330
+ setTimeout(stepAndSleep, stepDelay - timeElapsed);
331
+ }
332
+
333
+ }
334
+
335
+ if ( (!interpolOptions.stepDelay && !interpolOptions.duration) || ((interpolOptions.stepDelay <= 0) && (interpolOptions.duration <= 0)) ) {
336
+ // Trigger as fast as possible
337
+ for (var i=0; i < stepCount; i+=1) {
338
+ interpolationStep();
339
+ }
340
+ var pageCoord = {pageX: Math.round(start.x+drag.dx), pageY: Math.round(start.y+drag.dy)},
341
+ clientCoord = pageToClientPos(pageCoord, targetDoc),
342
+ eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
343
+ self.simulateEvent( eventTarget, "mousemove", pageCoord);
344
+ dragFinished(ele, options);
345
+ }
346
+ else {
347
+ var stepDelay = parseInt(interpolOptions.stepDelay,10) || Math.ceil(parseInt(interpolOptions.duration,10) / (stepCount+1));
348
+ var step = 0;
349
+
350
+ lastTime = now();
351
+ setTimeout(stepAndSleep, stepDelay);
352
+ }
353
+
354
+ }
355
+
356
+ /**
357
+ * @returns {Object|undefined} an object containing information about the currently active drag
358
+ * or <code>undefined</code> when there is no active drag.
359
+ * The returned object contains the following properties:
360
+ * <ul>
361
+ * <li><code>dragElement</code>: the dragged element</li>
362
+ * <li><code>dragStart</code>: object with the properties <code>x</code> and <code>y</code>
363
+ * representing the page relative start coordinates of the drag</li>
364
+ * <li><code>dragDistance</code>: object with the properties <code>x</code> and <code>y</code>
365
+ * representing the distance of the drag in x and y direction</li>
366
+ * </ul>
367
+ * @public
368
+ * @author julrich
369
+ * @since 1.0
370
+ */
371
+ $.simulate.activeDrag = function() {
372
+ if (!$.simulate._activeDrag) {
373
+ return undefined;
374
+ }
375
+ return $.extend(true,{},$.simulate._activeDrag);
376
+ };
377
+
378
+ $.extend( $.simulate.prototype,
379
+
380
+ /**
381
+ * @lends $.simulate.prototype
382
+ */
383
+ {
384
+
385
+
386
+ /**
387
+ * Simulates a drag.
388
+ *
389
+ * @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
390
+ * @public
391
+ * @author julrich
392
+ * @since 1.0
393
+ */
394
+ simulateDrag: function() {
395
+ var self = this,
396
+ ele = self.target,
397
+ options = $.extend({
398
+ dx: 0,
399
+ dy: 0,
400
+ dragTarget: undefined,
401
+ clickToDrag: false,
402
+ interpolation: {
403
+ stepWidth: 0,
404
+ stepCount: 0,
405
+ stepDelay: 0,
406
+ duration: 0,
407
+ shaky: false
408
+ },
409
+ callback: undefined
410
+ }, this.options);
411
+
412
+ var start,
413
+ continueDrag = ($.simulate._activeDrag && $.simulate._activeDrag.dragElement === ele);
414
+
415
+ if (continueDrag) {
416
+ start = $.simulate._activeDrag.dragStart;
417
+ }
418
+ else {
419
+ start = findCenter( ele );
420
+ }
421
+
422
+ var x = Math.round( start.x ),
423
+ y = Math.round( start.y ),
424
+ coord = { pageX: x, pageY: y },
425
+ dx,
426
+ dy;
427
+
428
+ if (options.dragTarget) {
429
+ var end = findCenter(options.dragTarget);
430
+ dx = Math.round(end.x - start.x);
431
+ dy = Math.round(end.y - start.y);
432
+ }
433
+ else {
434
+ dx = options.dx || 0;
435
+ dy = options.dy || 0;
436
+ }
437
+
438
+ if (continueDrag) {
439
+ // We just continue to move the dragged element
440
+ $.simulate._activeDrag.dragDistance.x += dx;
441
+ $.simulate._activeDrag.dragDistance.y += dy;
442
+ coord = { pageX: Math.round(x + $.simulate._activeDrag.dragDistance.x) , pageY: Math.round(y + $.simulate._activeDrag.dragDistance.y) };
443
+ }
444
+ else {
445
+ if ($.simulate._activeDrag) {
446
+ // Drop before starting a new drag
447
+ $($.simulate._activeDrag.dragElement).simulate( "drop" );
448
+ }
449
+
450
+ // We start a new drag
451
+ self.simulateEvent( ele, "mousedown", coord );
452
+ if (options.clickToDrag === true) {
453
+ self.simulateEvent( ele, "mouseup", coord );
454
+ self.simulateEvent( ele, "click", coord );
455
+ }
456
+ $(ele).add(ele.ownerDocument).one('mouseup', function() {
457
+ $.simulate._activeDrag = undefined;
458
+ });
459
+
460
+ $.extend($.simulate, {
461
+ _activeDrag: {
462
+ dragElement: ele,
463
+ dragStart: { x: x, y: y },
464
+ dragDistance: { x: dx, y: dy }
465
+ }
466
+ });
467
+ coord = { pageX: Math.round(x + dx), pageY: Math.round(y + dy) };
468
+ }
469
+
470
+ if (dx !== 0 || dy !== 0) {
471
+
472
+ if ( options.interpolation && (options.interpolation.stepCount || options.interpolation.stepWidth) ) {
473
+ interpolatedEvents(self, ele, {x: x, y: y}, {dx: dx, dy: dy}, options);
474
+ }
475
+ else {
476
+ var targetDoc = selectFirstMatch([ele, ele.ownerDocument], isDocument) || document,
477
+ clientCoord = pageToClientPos(coord, targetDoc),
478
+ eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
479
+
480
+ self.simulateEvent( eventTarget, "mousemove", coord );
481
+ dragFinished(ele, options);
482
+ }
483
+ }
484
+ else {
485
+ dragFinished(ele, options);
486
+ }
487
+ },
488
+
489
+ /**
490
+ * Simulates a drop.
491
+ *
492
+ * @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
493
+ * @public
494
+ * @author julrich
495
+ * @since 1.0
496
+ */
497
+ simulateDrop: function() {
498
+ var self = this,
499
+ ele = this.target,
500
+ activeDrag = $.simulate._activeDrag,
501
+ options = $.extend({
502
+ clickToDrop: false,
503
+ callback: undefined
504
+ }, self.options),
505
+ moveBeforeDrop = true,
506
+ center = findCenter( ele ),
507
+ x = Math.round( center.x ),
508
+ y = Math.round( center.y ),
509
+ coord = { pageX: x, pageY: y },
510
+ targetDoc = ( (activeDrag)? selectFirstMatch([activeDrag.dragElement, activeDrag.dragElement.ownerDocument], isDocument) : selectFirstMatch([ele, ele.ownerDocument], isDocument) ) || document,
511
+ clientCoord = pageToClientPos(coord, targetDoc),
512
+ eventTarget = elementAtPosition(clientCoord, targetDoc);
513
+
514
+ if (activeDrag && (activeDrag.dragElement === ele || isDocument(ele))) {
515
+ // We already moved the mouse during the drag so we just simulate the drop on the end position
516
+ x = Math.round(activeDrag.dragStart.x + activeDrag.dragDistance.x);
517
+ y = Math.round(activeDrag.dragStart.y + activeDrag.dragDistance.y);
518
+ coord = { pageX: x, pageY: y };
519
+ clientCoord = pageToClientPos(coord, targetDoc);
520
+ eventTarget = elementAtPosition(clientCoord, targetDoc);
521
+ moveBeforeDrop = false;
522
+ }
523
+
524
+ if (!eventTarget) {
525
+ eventTarget = (activeDrag)? activeDrag.dragElement : ele;
526
+ }
527
+
528
+ if (moveBeforeDrop === true) {
529
+ // Else we assume the drop should happen on target, so we move there
530
+ self.simulateEvent( eventTarget, "mousemove", coord );
531
+ }
532
+
533
+ if (options.clickToDrop) {
534
+ self.simulateEvent( eventTarget, "mousedown", coord );
535
+ }
536
+ this.simulateEvent( eventTarget, "mouseup", coord );
537
+ if (options.clickToDrop) {
538
+ self.simulateEvent( eventTarget, "click", coord );
539
+ }
540
+
541
+ $.simulate._activeDrag = undefined;
542
+ $(eventTarget).trigger({type: "simulate-drop"});
543
+ if ($.isFunction(options.callback)) {
544
+ options.callback.apply(eventTarget);
545
+ }
546
+ },
547
+
548
+ /**
549
+ * Simulates a drag followed by drop.
550
+ *
551
+ * @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
552
+ * @public
553
+ * @author julrich
554
+ * @since 1.0
555
+ */
556
+ simulateDragNDrop: function() {
557
+ var self = this,
558
+ ele = this.target,
559
+ options = $.extend({
560
+ dragTarget: undefined,
561
+ dropTarget: undefined
562
+ }, self.options),
563
+ // If there is a dragTarget or dx/dy, then we drag there and simulate an independent drop on dropTarget or ele
564
+ dropEle = ((options.dragTarget || options.dx || options.dy)? options.dropTarget : ele) || ele;
565
+ /*
566
+ dx = (options.dropTarget)? 0 : (options.dx || 0),
567
+ dy = (options.dropTarget)? 0 : (options.dy || 0),
568
+ dragDistance = { dx: dx, dy: dy };
569
+
570
+ $.extend(options, dragDistance);
571
+ */
572
+ $(ele).simulate( "drag", $.extend({},options,{
573
+ // If there is no dragTarget, no dx and no dy, we drag onto the dropTarget directly
574
+ dragTarget: options.dragTarget || ((options.dx || options.dy)?undefined:options.dropTarget),
575
+ callback: function() {
576
+ $(dropEle).simulate( "drop", options );
577
+ }
578
+ }));
579
+
580
+ }
581
+ });
582
+
583
+ }(jQuery));