imgareaselect-rails 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  module Imgareaselect
2
- VERSION = '0.9.9'
2
+ VERSION = '0.9.10'
3
3
  module Rails
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.2'
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2012 Michal Wojciechowski, http://odyniec.net/
1
+ Copyright (c) 2008-2013 Michal Wojciechowski, http://odyniec.net/
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  * imgAreaSelect jQuery plugin
3
- * version 0.9.9
3
+ * version 0.9.10
4
4
  *
5
- * Copyright (c) 2008-2012 Michal Wojciechowski (odyniec.net)
5
+ * Copyright (c) 2008-2013 Michal Wojciechowski (odyniec.net)
6
6
  *
7
7
  * Dual licensed under the MIT (MIT-LICENSE.txt)
8
8
  * and GPL (GPL-LICENSE.txt) licenses.
@@ -13,91 +13,207 @@
13
13
 
14
14
  (function($) {
15
15
 
16
+ /*
17
+ * Math functions will be used extensively, so it's convenient to make a few
18
+ * shortcuts
19
+ */
16
20
  var abs = Math.abs,
17
21
  max = Math.max,
18
22
  min = Math.min,
19
23
  round = Math.round;
20
24
 
25
+ /**
26
+ * Create a new HTML div element
27
+ *
28
+ * @return A jQuery object representing the new element
29
+ */
21
30
  function div() {
22
31
  return $('<div/>');
23
32
  }
24
33
 
34
+ /**
35
+ * imgAreaSelect initialization
36
+ *
37
+ * @param img
38
+ * A HTML image element to attach the plugin to
39
+ * @param options
40
+ * An options object
41
+ */
25
42
  $.imgAreaSelect = function (img, options) {
26
- var
27
-
43
+ var
44
+ /* jQuery object representing the image */
28
45
  $img = $(img),
29
-
46
+
47
+ /* Has the image finished loading? */
30
48
  imgLoaded,
31
-
49
+
50
+ /* Plugin elements */
51
+
52
+ /* Container box */
32
53
  $box = div(),
54
+ /* Selection area */
33
55
  $area = div(),
56
+ /* Border (four divs) */
34
57
  $border = div().add(div()).add(div()).add(div()),
58
+ /* Outer area (four divs) */
35
59
  $outer = div().add(div()).add(div()).add(div()),
60
+ /* Handles (empty by default, initialized in setOptions()) */
36
61
  $handles = $([]),
37
-
62
+
63
+ /*
64
+ * Additional element to work around a cursor problem in Opera
65
+ * (explained later)
66
+ */
38
67
  $areaOpera,
39
-
68
+
69
+ /* Image position (relative to viewport) */
40
70
  left, top,
41
-
71
+
72
+ /* Image offset (as returned by .offset()) */
42
73
  imgOfs = { left: 0, top: 0 },
43
-
74
+
75
+ /* Image dimensions (as returned by .width() and .height()) */
44
76
  imgWidth, imgHeight,
45
-
77
+
78
+ /*
79
+ * jQuery object representing the parent element that the plugin
80
+ * elements are appended to
81
+ */
46
82
  $parent,
47
-
83
+
84
+ /* Parent element offset (as returned by .offset()) */
48
85
  parOfs = { left: 0, top: 0 },
49
-
86
+
87
+ /* Base z-index for plugin elements */
50
88
  zIndex = 0,
51
-
89
+
90
+ /* Plugin elements position */
52
91
  position = 'absolute',
53
-
92
+
93
+ /* X/Y coordinates of the starting point for move/resize operations */
54
94
  startX, startY,
55
-
95
+
96
+ /* Horizontal and vertical scaling factors */
56
97
  scaleX, scaleY,
57
-
98
+
99
+ /* Current resize mode ("nw", "se", etc.) */
58
100
  resize,
59
-
101
+
102
+ /* Selection area constraints */
60
103
  minWidth, minHeight, maxWidth, maxHeight,
61
-
104
+
105
+ /* Aspect ratio to maintain (floating point number) */
62
106
  aspectRatio,
63
-
107
+
108
+ /* Are the plugin elements currently displayed? */
64
109
  shown,
65
-
110
+
111
+ /* Current selection (relative to parent element) */
66
112
  x1, y1, x2, y2,
67
-
113
+
114
+ /* Current selection (relative to scaled image) */
68
115
  selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
69
-
116
+
117
+ /* Document element */
70
118
  docElem = document.documentElement,
71
119
 
120
+ /* User agent */
121
+ ua = navigator.userAgent,
122
+
123
+ /* Various helper variables used throughout the code */
72
124
  $p, d, i, o, w, h, adjusted;
73
125
 
126
+ /*
127
+ * Translate selection coordinates (relative to scaled image) to viewport
128
+ * coordinates (relative to parent element)
129
+ */
130
+
131
+ /**
132
+ * Translate selection X to viewport X
133
+ *
134
+ * @param x
135
+ * Selection X
136
+ * @return Viewport X
137
+ */
74
138
  function viewX(x) {
75
139
  return x + imgOfs.left - parOfs.left;
76
140
  }
77
141
 
142
+ /**
143
+ * Translate selection Y to viewport Y
144
+ *
145
+ * @param y
146
+ * Selection Y
147
+ * @return Viewport Y
148
+ */
78
149
  function viewY(y) {
79
150
  return y + imgOfs.top - parOfs.top;
80
151
  }
81
152
 
153
+ /*
154
+ * Translate viewport coordinates to selection coordinates
155
+ */
156
+
157
+ /**
158
+ * Translate viewport X to selection X
159
+ *
160
+ * @param x
161
+ * Viewport X
162
+ * @return Selection X
163
+ */
82
164
  function selX(x) {
83
165
  return x - imgOfs.left + parOfs.left;
84
166
  }
85
167
 
168
+ /**
169
+ * Translate viewport Y to selection Y
170
+ *
171
+ * @param y
172
+ * Viewport Y
173
+ * @return Selection Y
174
+ */
86
175
  function selY(y) {
87
176
  return y - imgOfs.top + parOfs.top;
88
177
  }
89
-
178
+
179
+ /*
180
+ * Translate event coordinates (relative to document) to viewport
181
+ * coordinates
182
+ */
183
+
184
+ /**
185
+ * Get event X and translate it to viewport X
186
+ *
187
+ * @param event
188
+ * The event object
189
+ * @return Viewport X
190
+ */
90
191
  function evX(event) {
91
192
  return event.pageX - parOfs.left;
92
193
  }
93
194
 
195
+ /**
196
+ * Get event Y and translate it to viewport Y
197
+ *
198
+ * @param event
199
+ * The event object
200
+ * @return Viewport Y
201
+ */
94
202
  function evY(event) {
95
203
  return event.pageY - parOfs.top;
96
204
  }
97
205
 
206
+ /**
207
+ * Get the current selection
208
+ *
209
+ * @param noScale
210
+ * If set to <code>true</code>, scaling is not applied to the
211
+ * returned selection
212
+ * @return Selection object
213
+ */
98
214
  function getSelection(noScale) {
99
215
  var sx = noScale || scaleX, sy = noScale || scaleY;
100
-
216
+
101
217
  return { x1: round(selection.x1 * sx),
102
218
  y1: round(selection.y1 * sy),
103
219
  x2: round(selection.x2 * sx),
@@ -105,38 +221,71 @@ $.imgAreaSelect = function (img, options) {
105
221
  width: round(selection.x2 * sx) - round(selection.x1 * sx),
106
222
  height: round(selection.y2 * sy) - round(selection.y1 * sy) };
107
223
  }
108
-
224
+
225
+ /**
226
+ * Set the current selection
227
+ *
228
+ * @param x1
229
+ * X coordinate of the upper left corner of the selection area
230
+ * @param y1
231
+ * Y coordinate of the upper left corner of the selection area
232
+ * @param x2
233
+ * X coordinate of the lower right corner of the selection area
234
+ * @param y2
235
+ * Y coordinate of the lower right corner of the selection area
236
+ * @param noScale
237
+ * If set to <code>true</code>, scaling is not applied to the
238
+ * new selection
239
+ */
109
240
  function setSelection(x1, y1, x2, y2, noScale) {
110
241
  var sx = noScale || scaleX, sy = noScale || scaleY;
111
-
242
+
112
243
  selection = {
113
244
  x1: round(x1 / sx || 0),
114
245
  y1: round(y1 / sy || 0),
115
246
  x2: round(x2 / sx || 0),
116
247
  y2: round(y2 / sy || 0)
117
248
  };
118
-
249
+
119
250
  selection.width = selection.x2 - selection.x1;
120
251
  selection.height = selection.y2 - selection.y1;
121
252
  }
122
253
 
254
+ /**
255
+ * Recalculate image and parent offsets
256
+ */
123
257
  function adjust() {
258
+ /*
259
+ * Do not adjust if image has not yet loaded or if width is not a
260
+ * positive number. The latter might happen when imgAreaSelect is put
261
+ * on a parent element which is then hidden.
262
+ */
124
263
  if (!imgLoaded || !$img.width())
125
264
  return;
126
-
265
+
266
+ /*
267
+ * Get image offset. The .offset() method returns float values, so they
268
+ * need to be rounded.
269
+ */
127
270
  imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
128
-
271
+
272
+ /* Get image dimensions */
129
273
  imgWidth = $img.innerWidth();
130
274
  imgHeight = $img.innerHeight();
131
-
275
+
132
276
  imgOfs.top += ($img.outerHeight() - imgHeight) >> 1;
133
277
  imgOfs.left += ($img.outerWidth() - imgWidth) >> 1;
134
278
 
279
+ /* Set minimum and maximum selection area dimensions */
135
280
  minWidth = round(options.minWidth / scaleX) || 0;
136
281
  minHeight = round(options.minHeight / scaleY) || 0;
137
282
  maxWidth = round(min(options.maxWidth / scaleX || 1<<24, imgWidth));
138
283
  maxHeight = round(min(options.maxHeight / scaleY || 1<<24, imgHeight));
139
-
284
+
285
+ /*
286
+ * Workaround for jQuery 1.3.2 incorrect offset calculation, originally
287
+ * observed in Safari 3. Firefox 2 is also affected.
288
+ */
140
289
  if ($().jquery == '1.3.2' && position == 'fixed' &&
141
290
  !docElem['getBoundingClientRect'])
142
291
  {
@@ -144,32 +293,55 @@ $.imgAreaSelect = function (img, options) {
144
293
  imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
145
294
  }
146
295
 
296
+ /* Determine parent element offset */
147
297
  parOfs = /absolute|relative/.test($parent.css('position')) ?
148
298
  { left: round($parent.offset().left) - $parent.scrollLeft(),
149
299
  top: round($parent.offset().top) - $parent.scrollTop() } :
150
300
  position == 'fixed' ?
151
301
  { left: $(document).scrollLeft(), top: $(document).scrollTop() } :
152
302
  { left: 0, top: 0 };
153
-
303
+
154
304
  left = viewX(0);
155
305
  top = viewY(0);
156
-
306
+
307
+ /*
308
+ * Check if selection area is within image boundaries, adjust if
309
+ * necessary
310
+ */
157
311
  if (selection.x2 > imgWidth || selection.y2 > imgHeight)
158
312
  doResize();
159
313
  }
160
314
 
315
+ /**
316
+ * Update plugin elements
317
+ *
318
+ * @param resetKeyPress
319
+ * If set to <code>false</code>, this instance's keypress
320
+ * event handler is not activated
321
+ */
161
322
  function update(resetKeyPress) {
323
+ /* If plugin elements are hidden, do nothing */
162
324
  if (!shown) return;
163
325
 
326
+ /*
327
+ * Set the position and size of the container box and the selection area
328
+ * inside it
329
+ */
164
330
  $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
165
331
  .add($area).width(w = selection.width).height(h = selection.height);
166
332
 
333
+ /*
334
+ * Reset the position of selection area, borders, and handles (IE6/IE7
335
+ * position them incorrectly if we don't do this)
336
+ */
167
337
  $area.add($border).add($handles).css({ left: 0, top: 0 });
168
338
 
339
+ /* Set border dimensions */
169
340
  $border
170
341
  .width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
171
342
  .height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
172
343
 
344
+ /* Arrange the outer area elements */
173
345
  $($outer[0]).css({ left: left, top: top,
174
346
  width: selection.x1, height: imgHeight });
175
347
  $($outer[1]).css({ left: left + selection.x1, top: top,
@@ -178,10 +350,11 @@ $.imgAreaSelect = function (img, options) {
178
350
  width: imgWidth - selection.x2, height: imgHeight });
179
351
  $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
180
352
  width: w, height: imgHeight - selection.y2 });
181
-
353
+
182
354
  w -= $handles.outerWidth();
183
355
  h -= $handles.outerHeight();
184
-
356
+
357
+ /* Arrange handles */
185
358
  switch ($handles.length) {
186
359
  case 8:
187
360
  $($handles[4]).css({ left: w >> 1 });
@@ -194,37 +367,78 @@ $.imgAreaSelect = function (img, options) {
194
367
  }
195
368
 
196
369
  if (resetKeyPress !== false) {
370
+ /*
371
+ * Need to reset the document keypress event handler -- unbind the
372
+ * current handler
373
+ */
197
374
  if ($.imgAreaSelect.onKeyPress != docKeyPress)
198
375
  $(document).unbind($.imgAreaSelect.keyPress,
199
376
  $.imgAreaSelect.onKeyPress);
200
377
 
201
378
  if (options.keys)
379
+ /*
380
+ * Set the document keypress event handler to this instance's
381
+ * docKeyPress() function
382
+ */
202
383
  $(document)[$.imgAreaSelect.keyPress](
203
384
  $.imgAreaSelect.onKeyPress = docKeyPress);
204
385
  }
205
386
 
206
- if ($.browser.msie && $border.outerWidth() - $border.innerWidth() == 2) {
387
+ /*
388
+ * Internet Explorer displays 1px-wide dashed borders incorrectly by
389
+ * filling the spaces between dashes with white. Toggling the margin
390
+ * property between 0 and "auto" fixes this in IE6 and IE7 (IE8 is still
391
+ * broken). This workaround is not perfect, as it requires setTimeout()
392
+ * and thus causes the border to flicker a bit, but I haven't found a
393
+ * better solution.
394
+ *
395
+ * Note: This only happens with CSS borders, set with the borderWidth,
396
+ * borderOpacity, borderColor1, and borderColor2 options (which are now
397
+ * deprecated). Borders created with GIF background images are fine.
398
+ */
399
+ if (msie && $border.outerWidth() - $border.innerWidth() == 2) {
207
400
  $border.css('margin', 0);
208
401
  setTimeout(function () { $border.css('margin', 'auto'); }, 0);
209
402
  }
210
403
  }
211
-
404
+
405
+ /**
406
+ * Do the complete update sequence: recalculate offsets, update the
407
+ * elements, and set the correct values of x1, y1, x2, and y2.
408
+ *
409
+ * @param resetKeyPress
410
+ * If set to <code>false</code>, this instance's keypress
411
+ * event handler is not activated
412
+ */
212
413
  function doUpdate(resetKeyPress) {
213
414
  adjust();
214
415
  update(resetKeyPress);
215
416
  x1 = viewX(selection.x1); y1 = viewY(selection.y1);
216
417
  x2 = viewX(selection.x2); y2 = viewY(selection.y2);
217
418
  }
218
-
419
+
420
+ /**
421
+ * Hide or fade out an element (or multiple elements)
422
+ *
423
+ * @param $elem
424
+ * A jQuery object containing the element(s) to hide/fade out
425
+ * @param fn
426
+ * Callback function to be called when fadeOut() completes
427
+ */
219
428
  function hide($elem, fn) {
220
- options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
221
-
429
+ options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
222
430
  }
223
431
 
432
+ /**
433
+ * Selection area mousemove event handler
434
+ *
435
+ * @param event
436
+ * The event object
437
+ */
224
438
  function areaMouseMove(event) {
225
439
  var x = selX(evX(event)) - selection.x1,
226
440
  y = selY(evY(event)) - selection.y1;
227
-
441
+
228
442
  if (!adjusted) {
229
443
  adjust();
230
444
  adjusted = true;
@@ -232,9 +446,14 @@ $.imgAreaSelect = function (img, options) {
232
446
  $box.one('mouseout', function () { adjusted = false; });
233
447
  }
234
448
 
449
+ /* Clear the resize mode */
235
450
  resize = '';
236
451
 
237
452
  if (options.resizable) {
453
+ /*
454
+ * Check if the mouse pointer is over the resize margin area and set
455
+ * the resize mode accordingly
456
+ */
238
457
  if (y <= options.resizeMargin)
239
458
  resize = 'n';
240
459
  else if (y >= selection.height - options.resizeMargin)
@@ -251,28 +470,47 @@ $.imgAreaSelect = function (img, options) {
251
470
  $areaOpera.toggle();
252
471
  }
253
472
 
473
+ /**
474
+ * Document mouseup event handler
475
+ *
476
+ * @param event
477
+ * The event object
478
+ */
254
479
  function docMouseUp(event) {
480
+ /* Set back the default cursor */
255
481
  $('body').css('cursor', '');
482
+ /*
483
+ * If autoHide is enabled, or if the selection has zero width/height,
484
+ * hide the selection and the outer area
485
+ */
256
486
  if (options.autoHide || selection.width * selection.height == 0)
257
487
  hide($box.add($outer), function () { $(this).hide(); });
258
488
 
259
489
  $(document).unbind('mousemove', selectingMouseMove);
260
490
  $box.mousemove(areaMouseMove);
261
-
491
+
262
492
  options.onSelectEnd(img, getSelection());
263
493
  }
264
494
 
495
+ /**
496
+ * Selection area mousedown event handler
497
+ *
498
+ * @param event
499
+ * The event object
500
+ * @return false
501
+ */
265
502
  function areaMouseDown(event) {
266
503
  if (event.which != 1) return false;
267
504
 
268
505
  adjust();
269
506
 
270
507
  if (resize) {
508
+ /* Resize mode is in effect */
271
509
  $('body').css('cursor', resize + '-resize');
272
510
 
273
511
  x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
274
512
  y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
275
-
513
+
276
514
  $(document).mousemove(selectingMouseMove)
277
515
  .one('mouseup', docMouseUp);
278
516
  $box.unbind('mousemove', areaMouseMove);
@@ -297,12 +535,18 @@ $.imgAreaSelect = function (img, options) {
297
535
  return false;
298
536
  }
299
537
 
538
+ /**
539
+ * Adjust the x2/y2 coordinates to maintain aspect ratio (if defined)
540
+ *
541
+ * @param xFirst
542
+ * If set to <code>true</code>, calculate x2 first. Otherwise,
543
+ * calculate y2 first.
544
+ */
300
545
  function fixAspectRatio(xFirst) {
301
546
  if (aspectRatio)
302
547
  if (xFirst) {
303
548
  x2 = max(left, min(left + imgWidth,
304
- x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
305
-
549
+ x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
306
550
  y2 = round(max(top, min(top + imgHeight,
307
551
  y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
308
552
  x2 = round(x2);
@@ -316,11 +560,21 @@ $.imgAreaSelect = function (img, options) {
316
560
  }
317
561
  }
318
562
 
563
+ /**
564
+ * Resize the selection area respecting the minimum/maximum dimensions and
565
+ * aspect ratio
566
+ */
319
567
  function doResize() {
568
+ /*
569
+ * Make sure the top left corner of the selection area stays within
570
+ * image boundaries (it might not if the image source was dynamically
571
+ * changed).
572
+ */
320
573
  x1 = min(x1, left + imgWidth);
321
574
  y1 = min(y1, top + imgHeight);
322
-
575
+
323
576
  if (abs(x2 - x1) < minWidth) {
577
+ /* Selection width is smaller than minWidth */
324
578
  x2 = x1 - minWidth * (x2 < x1 || -1);
325
579
 
326
580
  if (x2 < left)
@@ -330,6 +584,7 @@ $.imgAreaSelect = function (img, options) {
330
584
  }
331
585
 
332
586
  if (abs(y2 - y1) < minHeight) {
587
+ /* Selection height is smaller than minHeight */
333
588
  y2 = y1 - minHeight * (y2 < y1 || -1);
334
589
 
335
590
  if (y2 < top)
@@ -340,15 +595,17 @@ $.imgAreaSelect = function (img, options) {
340
595
 
341
596
  x2 = max(left, min(x2, left + imgWidth));
342
597
  y2 = max(top, min(y2, top + imgHeight));
343
-
598
+
344
599
  fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
345
600
 
346
601
  if (abs(x2 - x1) > maxWidth) {
602
+ /* Selection width is greater than maxWidth */
347
603
  x2 = x1 - maxWidth * (x2 < x1 || -1);
348
604
  fixAspectRatio();
349
605
  }
350
606
 
351
607
  if (abs(y2 - y1) > maxHeight) {
608
+ /* Selection height is greater than maxHeight */
352
609
  y2 = y1 - maxHeight * (y2 < y1 || -1);
353
610
  fixAspectRatio(true);
354
611
  }
@@ -362,16 +619,30 @@ $.imgAreaSelect = function (img, options) {
362
619
  options.onSelectChange(img, getSelection());
363
620
  }
364
621
 
622
+ /**
623
+ * Mousemove event handler triggered when the user is selecting an area
624
+ *
625
+ * @param event
626
+ * The event object
627
+ * @return false
628
+ */
365
629
  function selectingMouseMove(event) {
366
630
  x2 = /w|e|^$/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
367
631
  y2 = /n|s|^$/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
368
632
 
369
633
  doResize();
370
634
 
371
- return false;
372
-
635
+ return false;
373
636
  }
374
637
 
638
+ /**
639
+ * Move the selection area
640
+ *
641
+ * @param newX1
642
+ * New viewport X1
643
+ * @param newY1
644
+ * New viewport Y1
645
+ */
375
646
  function doMove(newX1, newY1) {
376
647
  x2 = (x1 = newX1) + selection.width;
377
648
  y2 = (y1 = newY1) + selection.height;
@@ -384,29 +655,38 @@ $.imgAreaSelect = function (img, options) {
384
655
  options.onSelectChange(img, getSelection());
385
656
  }
386
657
 
658
+ /**
659
+ * Mousemove event handler triggered when the selection area is being moved
660
+ *
661
+ * @param event
662
+ * The event object
663
+ * @return false
664
+ */
387
665
  function movingMouseMove(event) {
388
666
  x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
389
667
  y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
390
668
 
391
669
  doMove(x1, y1);
392
670
 
393
- event.preventDefault();
394
-
671
+ event.preventDefault();
395
672
  return false;
396
673
  }
397
674
 
675
+ /**
676
+ * Start selection
677
+ */
398
678
  function startSelection() {
399
679
  $(document).unbind('mousemove', startSelection);
400
680
  adjust();
401
681
 
402
682
  x2 = x1;
403
- y2 = y1;
404
-
683
+ y2 = y1;
405
684
  doResize();
406
685
 
407
686
  resize = '';
408
687
 
409
688
  if (!$outer.is(':visible'))
689
+ /* Show the plugin elements */
410
690
  $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
411
691
 
412
692
  shown = true;
@@ -418,38 +698,59 @@ $.imgAreaSelect = function (img, options) {
418
698
  options.onSelectStart(img, getSelection());
419
699
  }
420
700
 
701
+ /**
702
+ * Cancel selection
703
+ */
421
704
  function cancelSelection() {
422
705
  $(document).unbind('mousemove', startSelection)
423
706
  .unbind('mouseup', cancelSelection);
424
707
  hide($box.add($outer));
425
-
708
+
426
709
  setSelection(selX(x1), selY(y1), selX(x1), selY(y1));
427
-
710
+
711
+ /* If this is an API call, callback functions should not be triggered */
428
712
  if (!(this instanceof $.imgAreaSelect)) {
429
713
  options.onSelectChange(img, getSelection());
430
714
  options.onSelectEnd(img, getSelection());
431
715
  }
432
716
  }
433
717
 
718
+ /**
719
+ * Image mousedown event handler
720
+ *
721
+ * @param event
722
+ * The event object
723
+ * @return false
724
+ */
434
725
  function imgMouseDown(event) {
726
+ /* Ignore the event if animation is in progress */
435
727
  if (event.which != 1 || $outer.is(':animated')) return false;
436
728
 
437
729
  adjust();
438
730
  startX = x1 = evX(event);
439
731
  startY = y1 = evY(event);
440
732
 
733
+ /* Selection will start when the mouse is moved */
441
734
  $(document).mousemove(startSelection).mouseup(cancelSelection);
442
735
 
443
736
  return false;
444
737
  }
445
-
738
+
739
+ /**
740
+ * Window resize event handler
741
+ */
446
742
  function windowResize() {
447
743
  doUpdate(false);
448
744
  }
449
745
 
746
+ /**
747
+ * Image load event handler. This is the final part of the initialization
748
+ * process.
749
+ */
450
750
  function imgLoad() {
451
751
  imgLoaded = true;
452
752
 
753
+ /* Set options */
453
754
  setOptions(options = $.extend({
454
755
  classPrefix: 'imgareaselect',
455
756
  movable: true,
@@ -463,7 +764,7 @@ $.imgAreaSelect = function (img, options) {
463
764
  }, options));
464
765
 
465
766
  $box.add($outer).css({ visibility: '' });
466
-
767
+
467
768
  if (options.show) {
468
769
  shown = true;
469
770
  adjust();
@@ -471,12 +772,24 @@ $.imgAreaSelect = function (img, options) {
471
772
  $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
472
773
  }
473
774
 
775
+ /*
776
+ * Call the onInit callback. The setTimeout() call is used to ensure
777
+ * that the plugin has been fully initialized and the object instance is
778
+ * available (so that it can be obtained in the callback).
779
+ */
474
780
  setTimeout(function () { options.onInit(img, getSelection()); }, 0);
475
781
  }
476
782
 
783
+ /**
784
+ * Document keypress event handler
785
+ *
786
+ * @param event
787
+ * The event object
788
+ * @return false
789
+ */
477
790
  var docKeyPress = function(event) {
478
791
  var k = options.keys, d, t, key = event.keyCode;
479
-
792
+
480
793
  d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
481
794
  !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
482
795
  !isNaN(k.shift) && event.shiftKey ? k.shift :
@@ -486,18 +799,24 @@ $.imgAreaSelect = function (img, options) {
486
799
  (k.ctrl == 'resize' && event.ctrlKey) ||
487
800
  (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
488
801
  {
802
+ /* Resize selection */
803
+
489
804
  switch (key) {
490
805
  case 37:
806
+ /* Left */
491
807
  d = -d;
492
808
  case 39:
809
+ /* Right */
493
810
  t = max(x1, x2);
494
811
  x1 = min(x1, x2);
495
812
  x2 = max(t + d, x1);
496
813
  fixAspectRatio();
497
814
  break;
498
815
  case 38:
816
+ /* Up */
499
817
  d = -d;
500
818
  case 40:
819
+ /* Down */
501
820
  t = max(y1, y2);
502
821
  y1 = min(y1, y2);
503
822
  y2 = max(t + d, y1);
@@ -510,20 +829,26 @@ $.imgAreaSelect = function (img, options) {
510
829
  doResize();
511
830
  }
512
831
  else {
832
+ /* Move selection */
833
+
513
834
  x1 = min(x1, x2);
514
835
  y1 = min(y1, y2);
515
836
 
516
837
  switch (key) {
517
838
  case 37:
839
+ /* Left */
518
840
  doMove(max(x1 - d, left), y1);
519
841
  break;
520
842
  case 38:
843
+ /* Up */
521
844
  doMove(x1, max(y1 - d, top));
522
845
  break;
523
846
  case 39:
847
+ /* Right */
524
848
  doMove(x1 + min(d, imgWidth - selX(x2)), y1);
525
849
  break;
526
850
  case 40:
851
+ /* Down */
527
852
  doMove(x1, y1 + min(d, imgHeight - selY(y2)));
528
853
  break;
529
854
  default:
@@ -534,21 +859,38 @@ $.imgAreaSelect = function (img, options) {
534
859
  return false;
535
860
  };
536
861
 
862
+ /**
863
+ * Apply style options to plugin element (or multiple elements)
864
+ *
865
+ * @param $elem
866
+ * A jQuery object representing the element(s) to style
867
+ * @param props
868
+ * An object that maps option names to corresponding CSS
869
+ * properties
870
+ */
537
871
  function styleOptions($elem, props) {
538
872
  for (var option in props)
539
873
  if (options[option] !== undefined)
540
874
  $elem.css(props[option], options[option]);
541
875
  }
542
876
 
877
+ /**
878
+ * Set plugin options
879
+ *
880
+ * @param newOptions
881
+ * The new options object
882
+ */
543
883
  function setOptions(newOptions) {
544
884
  if (newOptions.parent)
545
885
  ($parent = $(newOptions.parent)).append($box.add($outer));
546
-
886
+
887
+ /* Merge the new options with the existing ones */
547
888
  $.extend(options, newOptions);
548
889
 
549
890
  adjust();
550
891
 
551
892
  if (newOptions.handles != null) {
893
+ /* Recreate selection area handles */
552
894
  $handles.remove();
553
895
  $handles = $([]);
554
896
 
@@ -556,27 +898,43 @@ $.imgAreaSelect = function (img, options) {
556
898
 
557
899
  while (i--)
558
900
  $handles = $handles.add(div());
559
-
901
+
902
+ /* Add a class to handles and set the CSS properties */
560
903
  $handles.addClass(options.classPrefix + '-handle').css({
561
904
  position: 'absolute',
905
+ /*
906
+ * The font-size property needs to be set to zero, otherwise
907
+ * Internet Explorer makes the handles too large
908
+ */
562
909
  fontSize: 0,
563
910
  zIndex: zIndex + 1 || 1
564
911
  });
565
-
912
+
913
+ /*
914
+ * If handle width/height has not been set with CSS rules, set the
915
+ * default 5px
916
+ */
566
917
  if (!parseInt($handles.css('width')) >= 0)
567
918
  $handles.width(5).height(5);
568
-
919
+
920
+ /*
921
+ * If the borderWidth option is in use, add a solid border to
922
+ * handles
923
+ */
569
924
  if (o = options.borderWidth)
570
925
  $handles.css({ borderWidth: o, borderStyle: 'solid' });
571
926
 
927
+ /* Apply other style options */
572
928
  styleOptions($handles, { borderColor1: 'border-color',
573
929
  borderColor2: 'background-color',
574
930
  borderOpacity: 'opacity' });
575
931
  }
576
932
 
933
+ /* Calculate scale factors */
577
934
  scaleX = options.imageWidth / imgWidth || 1;
578
935
  scaleY = options.imageHeight / imgHeight || 1;
579
936
 
937
+ /* Set selection */
580
938
  if (newOptions.x1 != null) {
581
939
  setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
582
940
  newOptions.y2);
@@ -584,14 +942,17 @@ $.imgAreaSelect = function (img, options) {
584
942
  }
585
943
 
586
944
  if (newOptions.keys)
945
+ /* Enable keyboard support */
587
946
  options.keys = $.extend({ shift: 1, ctrl: 'resize' },
588
947
  newOptions.keys);
589
948
 
949
+ /* Add classes to plugin elements */
590
950
  $outer.addClass(options.classPrefix + '-outer');
591
951
  $area.addClass(options.classPrefix + '-selection');
592
952
  for (i = 0; i++ < 4;)
593
953
  $($border[i-1]).addClass(options.classPrefix + '-border' + i);
594
954
 
955
+ /* Apply style options */
595
956
  styleOptions($area, { selectionColor: 'background-color',
596
957
  selectionOpacity: 'opacity' });
597
958
  styleOptions($border, { borderOpacity: 'opacity',
@@ -603,15 +964,16 @@ $.imgAreaSelect = function (img, options) {
603
964
  if (o = options.borderColor2)
604
965
  $($border[1]).css({ borderStyle: 'dashed', borderColor: o });
605
966
 
606
- $box.append($area.add($border).add($areaOpera).add($handles));
967
+ /* Append all the selection area elements to the container box */
968
+ $box.append($area.add($border).add($areaOpera)).append($handles);
607
969
 
608
- if ($.browser.msie) {
970
+ if (msie) {
609
971
  if (o = ($outer.css('filter')||'').match(/opacity=(\d+)/))
610
972
  $outer.css('opacity', o[1]/100);
611
973
  if (o = ($border.css('filter')||'').match(/opacity=(\d+)/))
612
974
  $border.css('opacity', o[1]/100);
613
975
  }
614
-
976
+
615
977
  if (newOptions.hide)
616
978
  hide($box.add($outer));
617
979
  else if (newOptions.show && imgLoaded) {
@@ -620,102 +982,221 @@ $.imgAreaSelect = function (img, options) {
620
982
  doUpdate();
621
983
  }
622
984
 
985
+ /* Calculate the aspect ratio factor */
623
986
  aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
624
987
 
625
988
  $img.add($outer).unbind('mousedown', imgMouseDown);
626
-
989
+
627
990
  if (options.disable || options.enable === false) {
991
+ /* Disable the plugin */
628
992
  $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
629
993
  $(window).unbind('resize', windowResize);
630
994
  }
631
995
  else {
632
996
  if (options.enable || options.disable === false) {
997
+ /* Enable the plugin */
633
998
  if (options.resizable || options.movable)
634
999
  $box.mousemove(areaMouseMove).mousedown(areaMouseDown);
635
-
1000
+
636
1001
  $(window).resize(windowResize);
637
1002
  }
638
1003
 
639
1004
  if (!options.persistent)
640
1005
  $img.add($outer).mousedown(imgMouseDown);
641
1006
  }
642
-
1007
+
643
1008
  options.enable = options.disable = undefined;
644
1009
  }
645
-
1010
+
1011
+ /**
1012
+ * Remove plugin completely
1013
+ */
646
1014
  this.remove = function () {
1015
+ /*
1016
+ * Call setOptions with { disable: true } to unbind the event handlers
1017
+ */
647
1018
  setOptions({ disable: true });
648
1019
  $box.add($outer).remove();
649
1020
  };
650
-
1021
+
1022
+ /*
1023
+ * Public API
1024
+ */
1025
+
1026
+ /**
1027
+ * Get current options
1028
+ *
1029
+ * @return An object containing the set of options currently in use
1030
+ */
651
1031
  this.getOptions = function () { return options; };
652
-
1032
+
1033
+ /**
1034
+ * Set plugin options
1035
+ *
1036
+ * @param newOptions
1037
+ * The new options object
1038
+ */
653
1039
  this.setOptions = setOptions;
654
-
1040
+
1041
+ /**
1042
+ * Get the current selection
1043
+ *
1044
+ * @param noScale
1045
+ * If set to <code>true</code>, scaling is not applied to the
1046
+ * returned selection
1047
+ * @return Selection object
1048
+ */
655
1049
  this.getSelection = getSelection;
656
-
1050
+
1051
+ /**
1052
+ * Set the current selection
1053
+ *
1054
+ * @param x1
1055
+ * X coordinate of the upper left corner of the selection area
1056
+ * @param y1
1057
+ * Y coordinate of the upper left corner of the selection area
1058
+ * @param x2
1059
+ * X coordinate of the lower right corner of the selection area
1060
+ * @param y2
1061
+ * Y coordinate of the lower right corner of the selection area
1062
+ * @param noScale
1063
+ * If set to <code>true</code>, scaling is not applied to the
1064
+ * new selection
1065
+ */
657
1066
  this.setSelection = setSelection;
658
-
1067
+
1068
+ /**
1069
+ * Cancel selection
1070
+ */
659
1071
  this.cancelSelection = cancelSelection;
660
-
1072
+
1073
+ /**
1074
+ * Update plugin elements
1075
+ *
1076
+ * @param resetKeyPress
1077
+ * If set to <code>false</code>, this instance's keypress
1078
+ * event handler is not activated
1079
+ */
661
1080
  this.update = doUpdate;
662
1081
 
1082
+ /* Do the dreaded browser detection */
1083
+ var msie = (/msie ([\w.]+)/i.exec(ua)||[])[1],
1084
+ opera = /opera/i.test(ua),
1085
+ safari = /webkit/i.test(ua) && !/chrome/i.test(ua);
1086
+
1087
+ /*
1088
+ * Traverse the image's parent elements (up to <body>) and find the
1089
+ * highest z-index
1090
+ */
663
1091
  $p = $img;
664
1092
 
665
1093
  while ($p.length) {
666
1094
  zIndex = max(zIndex,
667
1095
  !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
1096
+ /* Also check if any of the ancestor elements has fixed position */
668
1097
  if ($p.css('position') == 'fixed')
669
1098
  position = 'fixed';
670
1099
 
671
1100
  $p = $p.parent(':not(body)');
672
1101
  }
673
-
1102
+
1103
+ /*
1104
+ * If z-index is given as an option, it overrides the one found by the
1105
+ * above loop
1106
+ */
674
1107
  zIndex = options.zIndex || zIndex;
675
1108
 
676
- if ($.browser.msie)
1109
+ if (msie)
677
1110
  $img.attr('unselectable', 'on');
678
1111
 
679
- $.imgAreaSelect.keyPress = $.browser.msie ||
680
- $.browser.safari ? 'keydown' : 'keypress';
681
-
682
- if ($.browser.opera)
1112
+ /*
1113
+ * In MSIE and WebKit, we need to use the keydown event instead of keypress
1114
+ */
1115
+ $.imgAreaSelect.keyPress = msie || safari ? 'keydown' : 'keypress';
1116
+
1117
+ /*
1118
+ * There is a bug affecting the CSS cursor property in Opera (observed in
1119
+ * versions up to 10.00) that prevents the cursor from being updated unless
1120
+ * the mouse leaves and enters the element again. To trigger the mouseover
1121
+ * event, we're adding an additional div to $box and we're going to toggle
1122
+ * it when mouse moves inside the selection area.
1123
+ */
1124
+ if (opera)
683
1125
  $areaOpera = div().css({ width: '100%', height: '100%',
684
1126
  position: 'absolute', zIndex: zIndex + 2 || 2 });
685
1127
 
1128
+ /*
1129
+ * We initially set visibility to "hidden" as a workaround for a weird
1130
+ * behaviour observed in Google Chrome 1.0.154.53 (on Windows XP). Normally
1131
+ * we would just set display to "none", but, for some reason, if we do so
1132
+ * then Chrome refuses to later display the element with .show() or
1133
+ * .fadeIn().
1134
+ */
686
1135
  $box.add($outer).css({ visibility: 'hidden', position: position,
687
1136
  overflow: 'hidden', zIndex: zIndex || '0' });
688
1137
  $box.css({ zIndex: zIndex + 2 || 2 });
689
1138
  $area.add($border).css({ position: 'absolute', fontSize: 0 });
690
-
1139
+
1140
+ /*
1141
+ * If the image has been fully loaded, or if it is not really an image (eg.
1142
+ * a div), call imgLoad() immediately; otherwise, bind it to be called once
1143
+ * on image load event.
1144
+ */
691
1145
  img.complete || img.readyState == 'complete' || !$img.is('img') ?
692
1146
  imgLoad() : $img.one('load', imgLoad);
693
1147
 
694
- if (!imgLoaded && $.browser.msie && $.browser.version >= 7)
1148
+ /*
1149
+ * MSIE 9.0 doesn't always fire the image load event -- resetting the src
1150
+ * attribute seems to trigger it. The check is for version 7 and above to
1151
+ * accommodate for MSIE 9 running in compatibility mode.
1152
+ */
1153
+ if (!imgLoaded && msie && msie >= 7)
695
1154
  img.src = img.src;
696
1155
  };
697
1156
 
1157
+ /**
1158
+ * Invoke imgAreaSelect on a jQuery object containing the image(s)
1159
+ *
1160
+ * @param options
1161
+ * Options object
1162
+ * @return The jQuery object or a reference to imgAreaSelect instance (if the
1163
+ * <code>instance</code> option was specified)
1164
+ */
698
1165
  $.fn.imgAreaSelect = function (options) {
699
1166
  options = options || {};
700
1167
 
701
1168
  this.each(function () {
1169
+ /* Is there already an imgAreaSelect instance bound to this element? */
702
1170
  if ($(this).data('imgAreaSelect')) {
1171
+ /* Yes there is -- is it supposed to be removed? */
703
1172
  if (options.remove) {
1173
+ /* Remove the plugin */
704
1174
  $(this).data('imgAreaSelect').remove();
705
1175
  $(this).removeData('imgAreaSelect');
706
1176
  }
707
1177
  else
1178
+ /* Reset options */
708
1179
  $(this).data('imgAreaSelect').setOptions(options);
709
1180
  }
710
1181
  else if (!options.remove) {
1182
+ /* No exising instance -- create a new one */
1183
+
1184
+ /*
1185
+ * If neither the "enable" nor the "disable" option is present, add
1186
+ * "enable" as the default
1187
+ */
711
1188
  if (options.enable === undefined && options.disable === undefined)
712
1189
  options.enable = true;
713
1190
 
714
1191
  $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
715
1192
  }
716
1193
  });
717
-
1194
+
718
1195
  if (options.instance)
1196
+ /*
1197
+ * Return the imgAreaSelect instance bound to the first element in the
1198
+ * set
1199
+ */
719
1200
  return $(this).data('imgAreaSelect');
720
1201
 
721
1202
  return this;
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imgareaselect-rails
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Aaron Lasseigne
@@ -15,10 +15,10 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-12-04 00:00:00 Z
18
+ date: 2013-02-04 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: rails
21
+ prerelease: false
22
22
  version_requirements: &id001 !ruby/object:Gem::Requirement
23
23
  none: false
24
24
  requirements:
@@ -29,10 +29,10 @@ dependencies:
29
29
  - 3
30
30
  - 1
31
31
  version: "3.1"
32
- type: :runtime
33
32
  requirement: *id001
34
- prerelease: false
35
- description: Places imgAreaSelect 0.9.9 in the Rails asset pipeline.
33
+ name: rails
34
+ type: :runtime
35
+ description: Places imgAreaSelect 0.9.10 in the Rails asset pipeline.
36
36
  email:
37
37
  - aaron@orgsync.com
38
38
  executables: []
@@ -91,9 +91,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  requirements: []
92
92
 
93
93
  rubyforge_project:
94
- rubygems_version: 1.8.24
94
+ rubygems_version: 1.8.15
95
95
  signing_key:
96
96
  specification_version: 3
97
- summary: Places imgAreaSelect 0.9.9 in the Rails asset pipeline.
97
+ summary: Places imgAreaSelect 0.9.10 in the Rails asset pipeline.
98
98
  test_files: []
99
99