ballonizer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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));