imgareaselect-rails 1.0.1 → 1.0.2

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.
@@ -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