poodle-rb 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/poodle/application.js +1 -2
  3. data/app/assets/javascripts/poodle/cropper.js +1525 -0
  4. data/app/assets/javascripts/poodle/utilities.js +23 -7
  5. data/app/assets/stylesheets/poodle/poodle-theme.css +18 -1
  6. data/app/controllers/poodle/admin_controller.rb +0 -26
  7. data/app/controllers/poodle/images_controller.rb +65 -0
  8. data/app/helpers/poodle/image_helper.rb +171 -31
  9. data/app/helpers/poodle/render_helper.rb +31 -0
  10. data/{spec/dummy/app → app}/uploaders/image_uploader.rb +19 -22
  11. data/app/views/layouts/poodle/application/_footer.html.erb +1 -1
  12. data/app/views/layouts/poodle/application.html.erb +7 -3
  13. data/app/views/layouts/poodle/image_upload.html.erb +16 -0
  14. data/app/views/layouts/poodle/public/_footer.html.erb +2 -4
  15. data/app/views/layouts/poodle/public/_header.html.erb +1 -3
  16. data/app/views/layouts/poodle/public/public.html.erb +51 -0
  17. data/app/views/layouts/poodle/public.html.erb +8 -5
  18. data/lib/poodle/action_view/theme_helper.rb +0 -76
  19. data/lib/poodle/engine.rb +2 -0
  20. data/lib/poodle/version.rb +1 -1
  21. data/spec/dummy/app/controllers/profile_pictures_controller.rb +2 -0
  22. data/spec/dummy/app/models/image/base.rb +30 -0
  23. data/spec/dummy/app/models/image/profile_picture.rb +2 -0
  24. data/spec/dummy/app/models/user.rb +1 -1
  25. data/spec/dummy/app/views/profile_pictures/_crop_form.html.erb +44 -0
  26. data/spec/dummy/app/views/profile_pictures/_form.html.erb +41 -0
  27. data/spec/dummy/app/views/profile_pictures/_new.html.erb +9 -0
  28. data/spec/dummy/app/views/profile_pictures/_photos.html.erb +16 -0
  29. data/spec/dummy/app/views/profile_pictures/create.html.erb +10 -0
  30. data/spec/dummy/app/views/profile_pictures/crop.html.erb +5 -0
  31. data/spec/dummy/app/views/profile_pictures/edit.js.erb +7 -0
  32. data/spec/dummy/app/views/profile_pictures/new.js.erb +7 -0
  33. data/spec/dummy/app/views/profile_pictures/update.html.erb +10 -0
  34. data/spec/dummy/app/views/profile_pictures/update.js.erb +2 -0
  35. data/spec/dummy/config/initializers/carrier_wave.rb +2 -0
  36. data/spec/dummy/config/routes.rb +15 -1
  37. data/spec/dummy/db/migrate/20131108102728_create_images.rb +12 -0
  38. data/spec/dummy/db/schema.rb +5 -2
  39. data/spec/dummy/db/test.sqlite3 +0 -0
  40. data/{app/assets/javascripts/poodle/common.js → spec/dummy/log/development.log} +0 -0
  41. data/spec/dummy/log/test.log +11173 -0
  42. data/spec/dummy/spec/controllers/profile_pictures_controller_spec.rb +50 -0
  43. data/spec/dummy/spec/helpers/image_helper_spec.rb +129 -0
  44. data/spec/dummy/spec/helpers/theme_helper_spec.rb +14 -43
  45. data/spec/dummy/spec/support/factories/profile_pictures.rb +2 -2
  46. data/spec/dummy/spec/support/factories/users.rb +1 -1
  47. data/spec/dummy/{public/uploads/profile_picture/image → uploads/image/profile_picture}/1/large_test.jpg +0 -0
  48. data/spec/dummy/uploads/image/profile_picture/1/medium_test.jpg +0 -0
  49. data/spec/dummy/{public/uploads/profile_picture/image → uploads/image/profile_picture}/1/test.jpg +0 -0
  50. data/spec/dummy/{public/uploads/profile_picture/image → uploads/image/profile_picture}/1/thumb_test.jpg +0 -0
  51. data/spec/dummy/uploads/image/profile_picture/2/large_test.jpg +0 -0
  52. data/spec/dummy/uploads/image/profile_picture/2/medium_test.jpg +0 -0
  53. data/spec/dummy/uploads/image/profile_picture/2/test.jpg +0 -0
  54. data/spec/dummy/uploads/image/profile_picture/2/thumb_test.jpg +0 -0
  55. metadata +60 -18
  56. data/app/assets/javascripts/poodle/photo_upload.js +0 -104
  57. data/spec/dummy/app/models/profile_picture.rb +0 -8
  58. data/spec/dummy/db/migrate/20131108102728_create_profile_pictures.rb +0 -9
  59. data/spec/dummy/public/uploads/profile_picture/image/1/medium_test.jpg +0 -0
@@ -0,0 +1,1525 @@
1
+ (function (factory) {
2
+ if (typeof define === "function" && define.amd) {
3
+ // AMD. Register as anonymous module.
4
+ define(["jquery"], factory);
5
+ } else if (typeof exports === "object") {
6
+ // Node / CommonJS
7
+ factory(require("jquery"));
8
+ } else {
9
+ // Browser globals.
10
+ factory(jQuery);
11
+ }
12
+ })(function ($) {
13
+
14
+ "use strict";
15
+
16
+ var $window = $(window),
17
+ $document = $(document),
18
+ location = window.location,
19
+
20
+ // Constants
21
+ TRUE = true,
22
+ FALSE = false,
23
+ NULL = null,
24
+ NAN = NaN,
25
+ INFINITY = Infinity,
26
+ STRING_UNDEFINED = "undefined",
27
+ STRING_DIRECTIVE = "directive",
28
+ CROPPER_NAMESPACE = ".cropper",
29
+
30
+ // RegExps
31
+ REGEXP_DIRECTIVES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/,
32
+ REGEXP_OPTIONS = /^(x|y|width|height)$/,
33
+ REGEXP_PROPERTIES = /^(naturalWidth|naturalHeight|width|height|aspectRatio|ratio|rotate)$/,
34
+
35
+ // Classes
36
+ CLASS_MODAL = "cropper-modal",
37
+ CLASS_HIDDEN = "cropper-hidden",
38
+ CLASS_INVISIBLE = "cropper-invisible",
39
+ CLASS_MOVE = "cropper-move",
40
+ CLASS_CROP = "cropper-crop",
41
+ CLASS_DISABLED = "cropper-disabled",
42
+
43
+ // Events
44
+ EVENT_MOUSE_DOWN = "mousedown touchstart",
45
+ EVENT_MOUSE_MOVE = "mousemove touchmove",
46
+ EVENT_MOUSE_UP = "mouseup mouseleave touchend touchleave touchcancel",
47
+ EVENT_WHEEL = "wheel mousewheel DOMMouseScroll",
48
+ EVENT_RESIZE = "resize" + CROPPER_NAMESPACE, // Bind to window with namespace
49
+ EVENT_DBLCLICK = "dblclick",
50
+ EVENT_BUILD = "build" + CROPPER_NAMESPACE,
51
+ EVENT_BUILT = "built" + CROPPER_NAMESPACE,
52
+ EVENT_DRAG_START = "dragstart" + CROPPER_NAMESPACE,
53
+ EVENT_DRAG_MOVE = "dragmove" + CROPPER_NAMESPACE,
54
+ EVENT_DRAG_END = "dragend" + CROPPER_NAMESPACE,
55
+
56
+ // Functions
57
+ isNumber = function (n) {
58
+ return typeof n === "number";
59
+ },
60
+
61
+ // Constructor
62
+ Cropper = function (element, options) {
63
+ this.element = element;
64
+ this.$element = $(element);
65
+ this.defaults = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) ? options : {});
66
+ this.$original = NULL;
67
+ this.ready = FALSE;
68
+ this.built = FALSE;
69
+ this.cropped = FALSE;
70
+ this.rotated = FALSE;
71
+ this.disabled = FALSE;
72
+ this.replaced = FALSE;
73
+ this.init();
74
+ },
75
+
76
+ // Others
77
+ round = Math.round,
78
+ sqrt = Math.sqrt,
79
+ min = Math.min,
80
+ max = Math.max,
81
+ abs = Math.abs,
82
+ sin = Math.sin,
83
+ cos = Math.cos,
84
+ num = parseFloat;
85
+
86
+ Cropper.prototype = {
87
+ constructor: Cropper,
88
+
89
+ support: {
90
+ canvas: $.isFunction($("<canvas>")[0].getContext)
91
+ },
92
+
93
+ init: function () {
94
+ var defaults = this.defaults;
95
+
96
+ $.each(defaults, function (i, n) {
97
+ switch (i) {
98
+ case "aspectRatio":
99
+ defaults[i] = abs(num(n)) || NAN; // 0 -> NaN
100
+ break;
101
+
102
+ case "autoCropArea":
103
+ defaults[i] = abs(num(n)) || 0.8; // 0 | NaN -> 0.8
104
+ break;
105
+
106
+ case "minWidth":
107
+ case "minHeight":
108
+ defaults[i] = abs(num(n)) || 0; // NaN -> 0
109
+ break;
110
+
111
+ case "maxWidth":
112
+ case "maxHeight":
113
+ defaults[i] = abs(num(n)) || INFINITY; // 0 | NaN -> Infinity
114
+ break;
115
+
116
+ // No default
117
+ }
118
+ });
119
+
120
+ // Set default image data
121
+ this.image = {
122
+ rotate: 0
123
+ };
124
+
125
+ this.load();
126
+ },
127
+
128
+ load: function () {
129
+ var _this = this,
130
+ $this = this.$element,
131
+ element = this.element,
132
+ image = this.image,
133
+ $clone,
134
+ url;
135
+
136
+ if ($this.is("img")) {
137
+ url = $this.prop("src");
138
+ } else if ($this.is("canvas") && this.support.canvas) {
139
+ url = element.toDataURL();
140
+ }
141
+
142
+ if (!url) {
143
+ return;
144
+ }
145
+
146
+ // Reset image rotate degree
147
+ if (this.replaced) {
148
+ this.replaced = FALSE;
149
+ image.rotate = 0;
150
+ }
151
+
152
+ this.$clone = ($clone = $("<img" + ((typeof $this.attr("crossOrigin") !== STRING_UNDEFINED || this.isCrossOriginURL(url)) ? " crossOrigin" : "") + ' src="' + url + '">'));
153
+
154
+ $clone.one("load", function () {
155
+ image.naturalWidth = this.naturalWidth || $clone.width();
156
+ image.naturalHeight = this.naturalHeight || $clone.height();
157
+ image.aspectRatio = image.naturalWidth / image.naturalHeight;
158
+
159
+ _this.url = url;
160
+ _this.ready = TRUE;
161
+ _this.build();
162
+ });
163
+
164
+ // Hide and prepend the clone iamge to the document body (Don't append to).
165
+ $clone.addClass(CLASS_INVISIBLE).prependTo("body");
166
+ },
167
+
168
+ isCrossOriginURL: function (url) {
169
+ var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
170
+
171
+ if ((parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port))) {
172
+ return TRUE;
173
+ }
174
+
175
+ return FALSE;
176
+ },
177
+
178
+ build: function () {
179
+ var $this = this.$element,
180
+ defaults = this.defaults,
181
+ buildEvent,
182
+ $cropper;
183
+
184
+ if (!this.ready) {
185
+ return;
186
+ }
187
+
188
+ if (this.built) {
189
+ this.unbuild();
190
+ }
191
+
192
+ $this.one(EVENT_BUILD, defaults.build); // Only trigger once
193
+ buildEvent = $.Event(EVENT_BUILD);
194
+ $this.trigger(buildEvent);
195
+
196
+ if (buildEvent.isDefaultPrevented()) {
197
+ return;
198
+ }
199
+
200
+ // Create cropper elements
201
+ this.$cropper = ($cropper = $(Cropper.TEMPLATE));
202
+
203
+ // Hide the original image
204
+ $this.addClass(CLASS_HIDDEN);
205
+
206
+ // Show and prepend the clone iamge to the cropper
207
+ this.$clone.removeClass(CLASS_INVISIBLE).prependTo($cropper);
208
+
209
+ // Save original image for rotation
210
+ if (!this.rotated) {
211
+ this.$original = this.$clone.clone();
212
+
213
+ // Append the image to document to avoid "NS_ERROR_NOT_AVAILABLE" error on Firefox when call the "drawImage" method.
214
+ this.$original.addClass(CLASS_INVISIBLE).prependTo(this.$cropper);
215
+
216
+ this.originalImage = $.extend({}, this.image);
217
+ }
218
+
219
+ this.$container = $this.parent();
220
+ this.$container.append($cropper);
221
+
222
+ this.$canvas = $cropper.find(".cropper-canvas");
223
+ this.$dragger = $cropper.find(".cropper-dragger");
224
+ this.$viewer = $cropper.find(".cropper-viewer");
225
+
226
+ defaults.autoCrop ? (this.cropped = TRUE) : this.$dragger.addClass(CLASS_HIDDEN);
227
+ defaults.dragCrop && this.setDragMode("crop");
228
+ defaults.modal && this.$canvas.addClass(CLASS_MODAL);
229
+ !defaults.dashed && this.$dragger.find(".cropper-dashed").addClass(CLASS_HIDDEN);
230
+ !defaults.movable && this.$dragger.find(".cropper-face").data(STRING_DIRECTIVE, "move");
231
+ !defaults.resizable && this.$dragger.find(".cropper-line, .cropper-point").addClass(CLASS_HIDDEN);
232
+
233
+ this.$scope = defaults.multiple ? this.$cropper : $document;
234
+
235
+ this.addListeners();
236
+ this.initPreview();
237
+
238
+ this.built = TRUE;
239
+ this.update();
240
+
241
+ $this.one(EVENT_BUILT, defaults.built); // Only trigger once
242
+ $this.trigger(EVENT_BUILT);
243
+ },
244
+
245
+ unbuild: function () {
246
+ if (!this.built) {
247
+ return;
248
+ }
249
+
250
+ this.built = FALSE;
251
+ this.removeListeners();
252
+
253
+ this.$preview.empty();
254
+ this.$preview = NULL;
255
+
256
+ this.$dragger = NULL;
257
+ this.$canvas = NULL;
258
+ this.$container = NULL;
259
+
260
+ this.$cropper.remove();
261
+ this.$cropper = NULL;
262
+ },
263
+
264
+ update: function (data) {
265
+ this.initContainer();
266
+ this.initCropper();
267
+ this.initImage();
268
+ this.initDragger();
269
+
270
+ if (data) {
271
+ this.setData(data, TRUE);
272
+ this.setDragMode("crop");
273
+ } else {
274
+ this.setData(this.defaults.data);
275
+ }
276
+ },
277
+
278
+ resize: function () {
279
+ clearTimeout(this.resizing);
280
+ this.resizing = setTimeout($.proxy(this.update, this, this.getData()), 200);
281
+ },
282
+
283
+ preview: function () {
284
+ var image = this.image,
285
+ dragger = this.dragger,
286
+ width = image.width,
287
+ height = image.height,
288
+ left = dragger.left - image.left,
289
+ top = dragger.top - image.top;
290
+
291
+ this.$viewer.find("img").css({
292
+ width: round(width),
293
+ height: round(height),
294
+ marginLeft: -round(left),
295
+ marginTop: -round(top)
296
+ });
297
+
298
+ this.$preview.each(function () {
299
+ var $this = $(this),
300
+ ratio = $this.width() / dragger.width;
301
+
302
+ $this.find("img").css({
303
+ width: round(width * ratio),
304
+ height: round(height * ratio),
305
+ marginLeft: -round(left * ratio),
306
+ marginTop: -round(top * ratio)
307
+ });
308
+ });
309
+ },
310
+
311
+ addListeners: function () {
312
+ var defaults = this.defaults;
313
+
314
+ this.$element.on(EVENT_DRAG_START, defaults.dragstart).on(EVENT_DRAG_MOVE, defaults.dragmove).on(EVENT_DRAG_END, defaults.dragend);
315
+ this.$cropper.on(EVENT_MOUSE_DOWN, (this._dragstart = $.proxy(this.dragstart, this))).on(EVENT_DBLCLICK, (this._dblclick = $.proxy(this.dblclick, this)));
316
+ defaults.zoomable && this.$cropper.on(EVENT_WHEEL, (this._wheel = $.proxy(this.wheel, this)));
317
+ this.$scope.on(EVENT_MOUSE_MOVE, (this._dragmove = $.proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = $.proxy(this.dragend, this)));
318
+
319
+ $window.on(EVENT_RESIZE, (this._resize = $.proxy(this.resize, this)));
320
+ },
321
+
322
+ removeListeners: function () {
323
+ var defaults = this.defaults;
324
+
325
+ this.$element.off(EVENT_DRAG_START, defaults.dragstart).off(EVENT_DRAG_MOVE, defaults.dragmove).off(EVENT_DRAG_END, defaults.dragend);
326
+ this.$cropper.off(EVENT_MOUSE_DOWN, this._dragstart).off(EVENT_DBLCLICK, this._dblclick);
327
+ defaults.zoomable && this.$cropper.off(EVENT_WHEEL, this._wheel);
328
+ this.$scope.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend);
329
+
330
+ $window.off(EVENT_RESIZE, this._resize);
331
+ },
332
+
333
+ initPreview: function () {
334
+ var img = '<img src="' + this.url + '">';
335
+
336
+ this.$preview = $(this.defaults.preview);
337
+ this.$viewer.html(img);
338
+ this.$preview.html(img).find("img").css("cssText", "min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;");
339
+ },
340
+
341
+ initContainer: function () {
342
+ var $container = this.$container;
343
+
344
+ this.container = {
345
+ width: max($container.width(), 300),
346
+ height: max($container.height(), 150)
347
+ };
348
+ },
349
+
350
+ initCropper: function () {
351
+ var container = this.container,
352
+ image = this.image,
353
+ cropper;
354
+
355
+ if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) {
356
+ cropper = {
357
+ width: container.width,
358
+ height: container.width / image.aspectRatio,
359
+ left: 0
360
+ };
361
+
362
+ cropper.top = (container.height - cropper.height) / 2;
363
+ } else {
364
+ cropper = {
365
+ width: container.height * image.aspectRatio,
366
+ height: container.height,
367
+ top: 0
368
+ };
369
+
370
+ cropper.left = (container.width - cropper.width) / 2;
371
+ }
372
+
373
+ this.$cropper.css({
374
+ width: round(cropper.width),
375
+ height: round(cropper.height),
376
+ left: round(cropper.left),
377
+ top: round(cropper.top)
378
+ });
379
+
380
+ this.cropper = cropper;
381
+ },
382
+
383
+ initImage: function () {
384
+ var image = this.image,
385
+ cropper = this.cropper,
386
+ defaultImage = {
387
+ _width: cropper.width,
388
+ _height: cropper.height,
389
+ width: cropper.width,
390
+ height: cropper.height,
391
+ left: 0,
392
+ top: 0,
393
+ ratio: cropper.width / image.naturalWidth
394
+ };
395
+
396
+ this.defaultImage = $.extend({}, image, defaultImage);
397
+
398
+ if (image._width !== cropper.width || image._height !== cropper.height) {
399
+ $.extend(image, defaultImage);
400
+ } else {
401
+ image = $.extend(defaultImage, image);
402
+ }
403
+
404
+ this.image = image;
405
+ this.renderImage();
406
+ },
407
+
408
+ renderImage: function (mode) {
409
+ var image = this.image;
410
+
411
+ if (mode === "zoom") {
412
+ image.left -= (image.width - image.oldWidth) / 2;
413
+ image.top -= (image.height - image.oldHeight) / 2;
414
+ }
415
+
416
+ image.left = min(max(image.left, image._width - image.width), 0);
417
+ image.top = min(max(image.top, image._height - image.height), 0);
418
+
419
+ this.$clone.css({
420
+ width: round(image.width),
421
+ height: round(image.height),
422
+ marginLeft: round(image.left),
423
+ marginTop: round(image.top)
424
+ });
425
+
426
+ if (mode) {
427
+ this.defaults.done(this.getData());
428
+ this.preview();
429
+ }
430
+ },
431
+
432
+ initDragger: function () {
433
+ var defaults = this.defaults,
434
+ cropper = this.cropper,
435
+ // If not set, use the original aspect ratio of the image.
436
+ aspectRatio = defaults.aspectRatio || this.image.aspectRatio,
437
+ ratio = this.image.ratio,
438
+ dragger;
439
+
440
+ if (((cropper.height * aspectRatio) - cropper.width) >= 0) {
441
+ dragger = {
442
+ height: cropper.width / aspectRatio,
443
+ width: cropper.width,
444
+ left: 0,
445
+ top: (cropper.height - (cropper.width / aspectRatio)) / 2,
446
+ maxWidth: cropper.width,
447
+ maxHeight: cropper.width / aspectRatio
448
+ };
449
+ } else {
450
+ dragger = {
451
+ height: cropper.height,
452
+ width: cropper.height * aspectRatio,
453
+ left: (cropper.width - (cropper.height * aspectRatio)) / 2,
454
+ top: 0,
455
+ maxWidth: cropper.height * aspectRatio,
456
+ maxHeight: cropper.height
457
+ };
458
+ }
459
+
460
+ dragger.minWidth = 0;
461
+ dragger.minHeight = 0;
462
+
463
+ if (defaults.aspectRatio) {
464
+ if (isFinite(defaults.maxWidth)) {
465
+ dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
466
+ dragger.maxHeight = dragger.maxWidth / aspectRatio;
467
+ } else if (isFinite(defaults.maxHeight)) {
468
+ dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
469
+ dragger.maxWidth = dragger.maxHeight * aspectRatio;
470
+ }
471
+
472
+ if (defaults.minWidth > 0) {
473
+ dragger.minWidth = max(0, defaults.minWidth * ratio);
474
+ dragger.minHeight = dragger.minWidth / aspectRatio;
475
+ } else if (defaults.minHeight > 0) {
476
+ dragger.minHeight = max(0, defaults.minHeight * ratio);
477
+ dragger.minWidth = dragger.minHeight * aspectRatio;
478
+ }
479
+ } else {
480
+ dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
481
+ dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
482
+ dragger.minWidth = max(0, defaults.minWidth * ratio);
483
+ dragger.minHeight = max(0, defaults.minHeight * ratio);
484
+ }
485
+
486
+ // minWidth can't be greater than maxWidth, and minHeight too.
487
+ dragger.minWidth = min(dragger.maxWidth, dragger.minWidth);
488
+ dragger.minHeight = min(dragger.maxHeight, dragger.minHeight);
489
+
490
+ // Center the dragger by default
491
+ dragger.height *= defaults.autoCropArea;
492
+ dragger.width *= defaults.autoCropArea;
493
+ dragger.left = (cropper.width - dragger.width) / 2;
494
+ dragger.top = (cropper.height - dragger.height) / 2;
495
+ dragger.oldLeft = dragger.left;
496
+ dragger.oldTop = dragger.top;
497
+
498
+ this.defaultDragger = dragger;
499
+ this.dragger = $.extend({}, dragger);
500
+ },
501
+
502
+ renderDragger: function () {
503
+ var dragger = this.dragger,
504
+ cropper = this.cropper;
505
+
506
+ if (dragger.width > dragger.maxWidth) {
507
+ dragger.width = dragger.maxWidth;
508
+ dragger.left = dragger.oldLeft;
509
+ } else if (dragger.width < dragger.minWidth) {
510
+ dragger.width = dragger.minWidth;
511
+ dragger.left = dragger.oldLeft;
512
+ }
513
+
514
+ if (dragger.height > dragger.maxHeight) {
515
+ dragger.height = dragger.maxHeight;
516
+ dragger.top = dragger.oldTop;
517
+ } else if (dragger.height < dragger.minHeight) {
518
+ dragger.height = dragger.minHeight;
519
+ dragger.top = dragger.oldTop;
520
+ }
521
+
522
+ dragger.left = min(max(dragger.left, 0), cropper.width - dragger.width);
523
+ dragger.top = min(max(dragger.top, 0), cropper.height - dragger.height);
524
+ dragger.oldLeft = dragger.left;
525
+ dragger.oldTop = dragger.top;
526
+
527
+ // Re-render the dragger
528
+ this.dragger = dragger;
529
+
530
+ if (!this.disabled) {
531
+ this.defaults.done(this.getData());
532
+ }
533
+
534
+ this.$dragger.css({
535
+ width: round(dragger.width),
536
+ height: round(dragger.height),
537
+ left: round(dragger.left),
538
+ top: round(dragger.top)
539
+ });
540
+
541
+ this.preview();
542
+ },
543
+
544
+ reset: function (deep) {
545
+ if (!this.cropped) {
546
+ return;
547
+ }
548
+
549
+ if (deep) {
550
+ this.defaults.data = {};
551
+ }
552
+
553
+ this.image = $.extend({}, this.defaultImage);
554
+ this.renderImage();
555
+ this.dragger = $.extend({}, this.defaultDragger);
556
+ this.setData(this.defaults.data);
557
+ },
558
+
559
+ clear: function () {
560
+ if (!this.cropped) {
561
+ return;
562
+ }
563
+
564
+ this.cropped = FALSE;
565
+
566
+ this.setData({
567
+ x: 0,
568
+ y: 0,
569
+ width: 0,
570
+ height: 0
571
+ });
572
+
573
+ this.$canvas.removeClass(CLASS_MODAL);
574
+ this.$dragger.addClass(CLASS_HIDDEN);
575
+ },
576
+
577
+ destroy: function () {
578
+ var $this = this.$element;
579
+
580
+ if (!this.ready) {
581
+ return;
582
+ }
583
+
584
+ this.unbuild();
585
+ $this.removeClass(CLASS_HIDDEN).removeData("cropper");
586
+
587
+ if (this.rotated) {
588
+ $this.attr("src", this.$original.attr("src"));
589
+ }
590
+ },
591
+
592
+ replace: function (url, /*INTERNAL*/ rotated) {
593
+ var _this = this,
594
+ $this = this.$element,
595
+ element = this.element,
596
+ context;
597
+
598
+ if (url && url !== this.url && url !== $this.attr("src")) {
599
+ if (!rotated) {
600
+ this.rotated = FALSE;
601
+ this.replaced = TRUE;
602
+ }
603
+
604
+ if ($this.is("img")) {
605
+ $this.attr("src", url);
606
+ this.load();
607
+ } else if ($this.is("canvas") && this.support.canvas) {
608
+ context = element.getContext("2d");
609
+
610
+ $('<img src="' + url + '">').one("load", function () {
611
+ element.width = this.width;
612
+ element.height = this.height;
613
+ context.clearRect(0, 0, element.width, element.height);
614
+ context.drawImage(this, 0, 0);
615
+ _this.load();
616
+ });
617
+ }
618
+ }
619
+ },
620
+
621
+ setData: function (data, /*INTERNAL*/ once) {
622
+ var cropper = this.cropper,
623
+ dragger = this.dragger,
624
+ image = this.image,
625
+ aspectRatio = this.defaults.aspectRatio;
626
+
627
+ if (!this.built || typeof data === STRING_UNDEFINED) {
628
+ return;
629
+ }
630
+
631
+ if (data === NULL || $.isEmptyObject(data)) {
632
+ dragger = $.extend({}, this.defaultDragger);
633
+ }
634
+
635
+ if ($.isPlainObject(data) && !$.isEmptyObject(data)) {
636
+
637
+ if (!once) {
638
+ this.defaults.data = data;
639
+ }
640
+
641
+ data = this.transformData(data);
642
+
643
+ if (isNumber(data.x) && data.x <= cropper.width - image.left) {
644
+ dragger.left = data.x + image.left;
645
+ }
646
+
647
+ if (isNumber(data.y) && data.y <= cropper.height - image.top) {
648
+ dragger.top = data.y + image.top;
649
+ }
650
+
651
+ if (aspectRatio) {
652
+ if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
653
+ dragger.width = data.width;
654
+ dragger.height = dragger.width / aspectRatio;
655
+ } else if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
656
+ dragger.height = data.height;
657
+ dragger.width = dragger.height * aspectRatio;
658
+ }
659
+ } else {
660
+ if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
661
+ dragger.width = data.width;
662
+ }
663
+
664
+ if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
665
+ dragger.height = data.height;
666
+ }
667
+ }
668
+ }
669
+
670
+ this.dragger = dragger;
671
+ this.renderDragger();
672
+ },
673
+
674
+ getData: function () {
675
+ var dragger = this.dragger,
676
+ image = this.image,
677
+ data = {};
678
+
679
+ if (this.built) {
680
+ data = {
681
+ x: dragger.left - image.left,
682
+ y: dragger.top - image.top,
683
+ width: dragger.width,
684
+ height: dragger.height
685
+ };
686
+
687
+ data = this.transformData(data, TRUE);
688
+ }
689
+
690
+ return data;
691
+ },
692
+
693
+ transformData: function (data, reverse) {
694
+ var ratio = this.image.ratio,
695
+ result = {};
696
+
697
+ $.each(data, function (i, n) {
698
+ n = num(n);
699
+
700
+ if (REGEXP_OPTIONS.test(i) && !isNaN(n)) {
701
+ // Not round when set data.
702
+ result[i] = reverse ? round(n / ratio) : n * ratio;
703
+ }
704
+ });
705
+
706
+ return result;
707
+ },
708
+
709
+ setAspectRatio: function (aspectRatio) {
710
+ var freeRatio = aspectRatio === "auto";
711
+
712
+ aspectRatio = num(aspectRatio);
713
+
714
+ if (freeRatio || (!isNaN(aspectRatio) && aspectRatio > 0)) {
715
+ this.defaults.aspectRatio = freeRatio ? NAN : aspectRatio;
716
+
717
+ if (this.built) {
718
+ this.initDragger();
719
+ this.renderDragger();
720
+ }
721
+ }
722
+ },
723
+
724
+ getImageData: function () {
725
+ var data = {};
726
+
727
+ if (this.ready) {
728
+ $.each(this.image, function (name, value) {
729
+ if (REGEXP_PROPERTIES.test(name)) {
730
+ data[name] = value;
731
+ }
732
+ });
733
+ }
734
+
735
+ return data;
736
+ },
737
+
738
+ getDataURL: function (options, type, quality) {
739
+ var canvas = $("<canvas>")[0],
740
+ data = this.getData(),
741
+ dataURL = "",
742
+ context;
743
+
744
+ if (!$.isPlainObject(options)) {
745
+ quality = type;
746
+ type = options;
747
+ options = {};
748
+ }
749
+
750
+ options = $.extend({
751
+ width: data.width,
752
+ height: data.height
753
+ }, options);
754
+
755
+ if (this.cropped && this.support.canvas) {
756
+ canvas.width = options.width;
757
+ canvas.height = options.height;
758
+ context = canvas.getContext("2d");
759
+
760
+ if (type === "image/jpeg") {
761
+ context.fillStyle = "#fff";
762
+ context.fillRect(0, 0, options.width, options.height);
763
+ }
764
+
765
+ context.drawImage(this.$clone[0], data.x, data.y, data.width, data.height, 0, 0, options.width, options.height);
766
+ dataURL = canvas.toDataURL(type, quality);
767
+ }
768
+
769
+ return dataURL;
770
+ },
771
+
772
+ setDragMode: function (mode) {
773
+ var $canvas = this.$canvas,
774
+ defaults = this.defaults,
775
+ cropable = FALSE,
776
+ movable = FALSE;
777
+
778
+ if (!this.built || this.disabled) {
779
+ return;
780
+ }
781
+
782
+ switch (mode) {
783
+ case "crop":
784
+ if (defaults.dragCrop) {
785
+ cropable = TRUE;
786
+ $canvas.data(STRING_DIRECTIVE, mode);
787
+ }
788
+
789
+ break;
790
+
791
+ case "move":
792
+ movable = TRUE;
793
+ $canvas.data(STRING_DIRECTIVE, mode);
794
+
795
+ break;
796
+
797
+ default:
798
+ $canvas.removeData(STRING_DIRECTIVE);
799
+ }
800
+
801
+ $canvas.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable);
802
+ },
803
+
804
+ enable: function () {
805
+ if (this.built) {
806
+ this.disabled = FALSE;
807
+ this.$cropper.removeClass(CLASS_DISABLED);
808
+ }
809
+ },
810
+
811
+ disable: function () {
812
+ if (this.built) {
813
+ this.disabled = TRUE;
814
+ this.$cropper.addClass(CLASS_DISABLED);
815
+ }
816
+ },
817
+
818
+ rotate: function (degree) {
819
+ var image = this.image;
820
+
821
+ degree = num(degree) || 0;
822
+
823
+ if (!this.built || degree === 0 || this.disabled || !this.defaults.rotatable || !this.support.canvas) {
824
+ return;
825
+ }
826
+
827
+ this.rotated = TRUE;
828
+ degree = (image.rotate = (image.rotate + degree) % 360);
829
+
830
+ // replace with "true" to prevent to override the original image
831
+ this.replace(this.getRotatedDataURL(degree), true);
832
+ },
833
+
834
+ getRotatedDataURL: function (degree) {
835
+ var canvas = $("<canvas>")[0],
836
+ context = canvas.getContext("2d"),
837
+ arc = degree * Math.PI / 180,
838
+ deg = abs(degree) % 180,
839
+ acuteAngle = deg > 90 ? (180 - deg) : deg,
840
+ acuteAngleArc = acuteAngle * Math.PI / 180,
841
+ originalImage = this.originalImage,
842
+ naturalWidth = originalImage.naturalWidth,
843
+ naturalHeight = originalImage.naturalHeight,
844
+ width = abs(naturalWidth * cos(acuteAngleArc) + naturalHeight * sin(acuteAngleArc)),
845
+ height = abs(naturalWidth * sin(acuteAngleArc) + naturalHeight * cos(acuteAngleArc));
846
+
847
+ canvas.width = width;
848
+ canvas.height = height;
849
+ context.save();
850
+ context.translate(width / 2, height / 2);
851
+ context.rotate(arc);
852
+ context.drawImage(this.$original[0], -naturalWidth / 2, -naturalHeight / 2, naturalWidth, naturalHeight);
853
+ context.restore();
854
+
855
+ return canvas.toDataURL();
856
+ },
857
+
858
+ zoom: function (delta) {
859
+ var image = this.image,
860
+ width,
861
+ height,
862
+ range;
863
+
864
+ delta = num(delta);
865
+
866
+ if (!this.built || !delta || this.disabled || !this.defaults.zoomable) {
867
+ return;
868
+ }
869
+
870
+ width = image.width * (1 + delta);
871
+ height = image.height * (1 + delta);
872
+ range = width / image._width;
873
+
874
+ if (range > 10) {
875
+ return;
876
+ }
877
+
878
+ if (range < 1) {
879
+ width = image._width;
880
+ height = image._height;
881
+ }
882
+
883
+ if (range <= 1) {
884
+ this.setDragMode("crop");
885
+ } else {
886
+ this.setDragMode("move");
887
+ }
888
+
889
+ image.oldWidth = image.width;
890
+ image.oldHeight = image.height;
891
+
892
+ image.width = width;
893
+ image.height = height;
894
+ image.ratio = image.width / image.naturalWidth;
895
+
896
+ this.renderImage("zoom");
897
+ },
898
+
899
+ dblclick: function () {
900
+ if (this.disabled) {
901
+ return;
902
+ }
903
+
904
+ if (this.$canvas.hasClass(CLASS_CROP)) {
905
+ this.setDragMode("move");
906
+ } else {
907
+ this.setDragMode("crop");
908
+ }
909
+ },
910
+
911
+ wheel: function (event) {
912
+ var e = event.originalEvent,
913
+ msDeltaY = 117.25, // IE
914
+ mozDelatY = 5, // Firefox
915
+ webkitDelatY = 166.66665649414062, // Chrome, Opera
916
+ zoomDelta = 0.1, // 10%
917
+ delta;
918
+
919
+ if (this.disabled) {
920
+ return;
921
+ }
922
+
923
+ event.preventDefault();
924
+
925
+ if (e.deltaY) {
926
+ delta = e.deltaY;
927
+ delta = delta % mozDelatY === 0 ? delta / mozDelatY : delta % msDeltaY === 0 ? delta / msDeltaY : delta / webkitDelatY;
928
+ } else {
929
+ delta = e.wheelDelta ? -e.wheelDelta / 120 : (e.detail ? e.detail / 3 : 0);
930
+ }
931
+
932
+ this.zoom(delta * zoomDelta);
933
+ },
934
+
935
+ dragstart: function (event) {
936
+ var touches = event.originalEvent.touches,
937
+ e = event,
938
+ directive,
939
+ dragStartEvent,
940
+ touchesLength;
941
+
942
+ if (this.disabled) {
943
+ return;
944
+ }
945
+
946
+ if (touches) {
947
+ touchesLength = touches.length;
948
+
949
+ if (touchesLength > 1) {
950
+ if (this.defaults.zoomable && touchesLength === 2) {
951
+ e = touches[1];
952
+ this.startX2 = e.pageX;
953
+ this.startY2 = e.pageY;
954
+ directive = "zoom";
955
+ } else {
956
+ return;
957
+ }
958
+ }
959
+
960
+ e = touches[0];
961
+ }
962
+
963
+ directive = directive || $(e.target).data(STRING_DIRECTIVE);
964
+
965
+ if (REGEXP_DIRECTIVES.test(directive)) {
966
+ event.preventDefault();
967
+
968
+ dragStartEvent = $.Event(EVENT_DRAG_START);
969
+ this.$element.trigger(dragStartEvent);
970
+
971
+ if (dragStartEvent.isDefaultPrevented()) {
972
+ return;
973
+ }
974
+
975
+ this.directive = directive;
976
+ this.cropping = FALSE;
977
+ this.startX = e.pageX;
978
+ this.startY = e.pageY;
979
+
980
+ if (directive === "crop") {
981
+ this.cropping = TRUE;
982
+ this.$canvas.addClass(CLASS_MODAL);
983
+ }
984
+ }
985
+ },
986
+
987
+ dragmove: function (event) {
988
+ var touches = event.originalEvent.touches,
989
+ e = event,
990
+ dragMoveEvent,
991
+ touchesLength;
992
+
993
+ if (this.disabled) {
994
+ return;
995
+ }
996
+
997
+ if (touches) {
998
+ touchesLength = touches.length;
999
+
1000
+ if (touchesLength > 1) {
1001
+ if (this.defaults.zoomable && touchesLength === 2) {
1002
+ e = touches[1];
1003
+ this.endX2 = e.pageX;
1004
+ this.endY2 = e.pageY;
1005
+ } else {
1006
+ return;
1007
+ }
1008
+ }
1009
+
1010
+ e = touches[0];
1011
+ }
1012
+
1013
+ if (this.directive) {
1014
+ event.preventDefault();
1015
+
1016
+ dragMoveEvent = $.Event(EVENT_DRAG_MOVE);
1017
+ this.$element.trigger(dragMoveEvent);
1018
+
1019
+ if (dragMoveEvent.isDefaultPrevented()) {
1020
+ return;
1021
+ }
1022
+
1023
+ this.endX = e.pageX;
1024
+ this.endY = e.pageY;
1025
+
1026
+ this.dragging();
1027
+ }
1028
+ },
1029
+
1030
+ dragend: function (event) {
1031
+ var dragEndEvent;
1032
+
1033
+ if (this.disabled) {
1034
+ return;
1035
+ }
1036
+
1037
+ if (this.directive) {
1038
+ event.preventDefault();
1039
+
1040
+ dragEndEvent = $.Event(EVENT_DRAG_END);
1041
+ this.$element.trigger(dragEndEvent);
1042
+
1043
+ if (dragEndEvent.isDefaultPrevented()) {
1044
+ return;
1045
+ }
1046
+
1047
+ if (this.cropping) {
1048
+ this.cropping = FALSE;
1049
+ this.$canvas.toggleClass(CLASS_MODAL, this.cropped && this.defaults.modal);
1050
+ }
1051
+
1052
+ this.directive = "";
1053
+ }
1054
+ },
1055
+
1056
+ dragging: function () {
1057
+ var directive = this.directive,
1058
+ image = this.image,
1059
+ cropper = this.cropper,
1060
+ maxWidth = cropper.width,
1061
+ maxHeight = cropper.height,
1062
+ dragger = this.dragger,
1063
+ width = dragger.width,
1064
+ height = dragger.height,
1065
+ left = dragger.left,
1066
+ top = dragger.top,
1067
+ right = left + width,
1068
+ bottom = top + height,
1069
+ renderable = TRUE,
1070
+ defaults = this.defaults,
1071
+ aspectRatio = defaults.aspectRatio,
1072
+ range = {
1073
+ x: this.endX - this.startX,
1074
+ y: this.endY - this.startY
1075
+ },
1076
+ offset;
1077
+
1078
+ if (aspectRatio) {
1079
+ range.X = range.y * aspectRatio;
1080
+ range.Y = range.x / aspectRatio;
1081
+ }
1082
+
1083
+ switch (directive) {
1084
+ // Move dragger
1085
+ case "all":
1086
+ left += range.x;
1087
+ top += range.y;
1088
+
1089
+ break;
1090
+
1091
+ // Resize dragger
1092
+ case "e":
1093
+ if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
1094
+ renderable = FALSE;
1095
+ break;
1096
+ }
1097
+
1098
+ width += range.x;
1099
+
1100
+ if (aspectRatio) {
1101
+ height = width / aspectRatio;
1102
+ top -= range.Y / 2;
1103
+ }
1104
+
1105
+ if (width < 0) {
1106
+ directive = "w";
1107
+ width = 0;
1108
+ }
1109
+
1110
+ break;
1111
+
1112
+ case "n":
1113
+ if (range.y <= 0 && (top <= 0 || aspectRatio && (left <= 0 || right >= maxWidth))) {
1114
+ renderable = FALSE;
1115
+ break;
1116
+ }
1117
+
1118
+ height -= range.y;
1119
+ top += range.y;
1120
+
1121
+ if (aspectRatio) {
1122
+ width = height * aspectRatio;
1123
+ left += range.X / 2;
1124
+ }
1125
+
1126
+ if (height < 0) {
1127
+ directive = "s";
1128
+ height = 0;
1129
+ }
1130
+
1131
+ break;
1132
+
1133
+ case "w":
1134
+ if (range.x <= 0 && (left <= 0 || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
1135
+ renderable = FALSE;
1136
+ break;
1137
+ }
1138
+
1139
+ width -= range.x;
1140
+ left += range.x;
1141
+
1142
+ if (aspectRatio) {
1143
+ height = width / aspectRatio;
1144
+ top += range.Y / 2;
1145
+ }
1146
+
1147
+ if (width < 0) {
1148
+ directive = "e";
1149
+ width = 0;
1150
+ }
1151
+
1152
+ break;
1153
+
1154
+ case "s":
1155
+ if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= 0 || right >= maxWidth))) {
1156
+ renderable = FALSE;
1157
+ break;
1158
+ }
1159
+
1160
+ height += range.y;
1161
+
1162
+ if (aspectRatio) {
1163
+ width = height * aspectRatio;
1164
+ left -= range.X / 2;
1165
+ }
1166
+
1167
+ if (height < 0) {
1168
+ directive = "n";
1169
+ height = 0;
1170
+ }
1171
+
1172
+ break;
1173
+
1174
+ case "ne":
1175
+ if (aspectRatio) {
1176
+ if (range.y <= 0 && (top <= 0 || right >= maxWidth)) {
1177
+ renderable = FALSE;
1178
+ break;
1179
+ }
1180
+
1181
+ height -= range.y;
1182
+ top += range.y;
1183
+ width = height * aspectRatio;
1184
+ } else {
1185
+ if (range.x >= 0) {
1186
+ if (right < maxWidth) {
1187
+ width += range.x;
1188
+ } else if (range.y <= 0 && top <= 0) {
1189
+ renderable = FALSE;
1190
+ }
1191
+ } else {
1192
+ width += range.x;
1193
+ }
1194
+
1195
+ if (range.y <= 0) {
1196
+ if (top > 0) {
1197
+ height -= range.y;
1198
+ top += range.y;
1199
+ }
1200
+ } else {
1201
+ height -= range.y;
1202
+ top += range.y;
1203
+ }
1204
+ }
1205
+
1206
+ if (height < 0) {
1207
+ directive = "sw";
1208
+ height = 0;
1209
+ width = 0;
1210
+ }
1211
+
1212
+ break;
1213
+
1214
+ case "nw":
1215
+ if (aspectRatio) {
1216
+ if (range.y <= 0 && (top <= 0 || left <= 0)) {
1217
+ renderable = FALSE;
1218
+ break;
1219
+ }
1220
+
1221
+ height -= range.y;
1222
+ top += range.y;
1223
+ width = height * aspectRatio;
1224
+ left += range.X;
1225
+ } else {
1226
+ if (range.x <= 0) {
1227
+ if (left > 0) {
1228
+ width -= range.x;
1229
+ left += range.x;
1230
+ } else if (range.y <= 0 && top <= 0) {
1231
+ renderable = FALSE;
1232
+ }
1233
+ } else {
1234
+ width -= range.x;
1235
+ left += range.x;
1236
+ }
1237
+
1238
+ if (range.y <= 0) {
1239
+ if (top > 0) {
1240
+ height -= range.y;
1241
+ top += range.y;
1242
+ }
1243
+ } else {
1244
+ height -= range.y;
1245
+ top += range.y;
1246
+ }
1247
+ }
1248
+
1249
+ if (height < 0) {
1250
+ directive = "se";
1251
+ height = 0;
1252
+ width = 0;
1253
+ }
1254
+
1255
+ break;
1256
+
1257
+ case "sw":
1258
+ if (aspectRatio) {
1259
+ if (range.x <= 0 && (left <= 0 || bottom >= maxHeight)) {
1260
+ renderable = FALSE;
1261
+ break;
1262
+ }
1263
+
1264
+ width -= range.x;
1265
+ left += range.x;
1266
+ height = width / aspectRatio;
1267
+ } else {
1268
+ if (range.x <= 0) {
1269
+ if (left > 0) {
1270
+ width -= range.x;
1271
+ left += range.x;
1272
+ } else if (range.y >= 0 && bottom >= maxHeight) {
1273
+ renderable = FALSE;
1274
+ }
1275
+ } else {
1276
+ width -= range.x;
1277
+ left += range.x;
1278
+ }
1279
+
1280
+ if (range.y >= 0) {
1281
+ if (bottom < maxHeight) {
1282
+ height += range.y;
1283
+ }
1284
+ } else {
1285
+ height += range.y;
1286
+ }
1287
+ }
1288
+
1289
+ if (width < 0) {
1290
+ directive = "ne";
1291
+ height = 0;
1292
+ width = 0;
1293
+ }
1294
+
1295
+ break;
1296
+
1297
+ case "se":
1298
+ if (aspectRatio) {
1299
+ if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
1300
+ renderable = FALSE;
1301
+ break;
1302
+ }
1303
+
1304
+ width += range.x;
1305
+ height = width / aspectRatio;
1306
+ } else {
1307
+ if (range.x >= 0) {
1308
+ if (right < maxWidth) {
1309
+ width += range.x;
1310
+ } else if (range.y >= 0 && bottom >= maxHeight) {
1311
+ renderable = FALSE;
1312
+ }
1313
+ } else {
1314
+ width += range.x;
1315
+ }
1316
+
1317
+ if (range.y >= 0) {
1318
+ if (bottom < maxHeight) {
1319
+ height += range.y;
1320
+ }
1321
+ } else {
1322
+ height += range.y;
1323
+ }
1324
+ }
1325
+
1326
+ if (width < 0) {
1327
+ directive = "nw";
1328
+ height = 0;
1329
+ width = 0;
1330
+ }
1331
+
1332
+ break;
1333
+
1334
+ // Move image
1335
+ case "move":
1336
+ image.left += range.x;
1337
+ image.top += range.y;
1338
+ this.renderImage("move");
1339
+ renderable = FALSE;
1340
+
1341
+ break;
1342
+
1343
+ // Scale image
1344
+ case "zoom":
1345
+ if (defaults.zoomable) {
1346
+ this.zoom(function (x, y, x1, y1, x2, y2) {
1347
+ return (sqrt(x2 * x2 + y2 * y2) - sqrt(x1 * x1 + y1 * y1)) / sqrt(x * x + y * y);
1348
+ }(
1349
+ image.width,
1350
+ image.height,
1351
+ abs(this.startX - this.startX2),
1352
+ abs(this.startY - this.startY2),
1353
+ abs(this.endX - this.endX2),
1354
+ abs(this.endY - this.endY2)
1355
+ ));
1356
+
1357
+ this.endX2 = this.startX2;
1358
+ this.endY2 = this.startY2;
1359
+ }
1360
+
1361
+ break;
1362
+
1363
+ // Crop image
1364
+ case "crop":
1365
+ if (range.x && range.y) {
1366
+ offset = this.$cropper.offset();
1367
+ left = this.startX - offset.left;
1368
+ top = this.startY - offset.top;
1369
+ width = dragger.minWidth;
1370
+ height = dragger.minHeight;
1371
+
1372
+ if (range.x > 0) {
1373
+ if (range.y > 0) {
1374
+ directive = "se";
1375
+ } else {
1376
+ directive = "ne";
1377
+ top -= height;
1378
+ }
1379
+ } else {
1380
+ if (range.y > 0) {
1381
+ directive = "sw";
1382
+ left -= width;
1383
+ } else {
1384
+ directive = "nw";
1385
+ left -= width;
1386
+ top -= height;
1387
+ }
1388
+ }
1389
+
1390
+ // Show the dragger if is hidden
1391
+ if (!this.cropped) {
1392
+ this.cropped = TRUE;
1393
+ this.$dragger.removeClass(CLASS_HIDDEN);
1394
+ }
1395
+ }
1396
+
1397
+ break;
1398
+
1399
+ // No default
1400
+ }
1401
+
1402
+ if (renderable) {
1403
+ dragger.width = width;
1404
+ dragger.height = height;
1405
+ dragger.left = left;
1406
+ dragger.top = top;
1407
+ this.directive = directive;
1408
+
1409
+ this.renderDragger();
1410
+ }
1411
+
1412
+ // Override
1413
+ this.startX = this.endX;
1414
+ this.startY = this.endY;
1415
+ }
1416
+ };
1417
+
1418
+ // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin)
1419
+ Cropper.TEMPLATE = (function (source, words) {
1420
+ words = words.split(",");
1421
+ return source.replace(/\d+/g, function (i) {
1422
+ return words[i];
1423
+ });
1424
+ })('<0 6="5-container"><0 6="5-canvas"></0><0 6="5-dragger"><1 6="5-viewer"></1><1 6="5-8 8-h"></1><1 6="5-8 8-v"></1><1 6="5-face" 3-2="all"></1><1 6="5-7 7-e" 3-2="e"></1><1 6="5-7 7-n" 3-2="n"></1><1 6="5-7 7-w" 3-2="w"></1><1 6="5-7 7-s" 3-2="s"></1><1 6="5-4 4-e" 3-2="e"></1><1 6="5-4 4-n" 3-2="n"></1><1 6="5-4 4-w" 3-2="w"></1><1 6="5-4 4-s" 3-2="s"></1><1 6="5-4 4-ne" 3-2="ne"></1><1 6="5-4 4-nw" 3-2="nw"></1><1 6="5-4 4-sw" 3-2="sw"></1><1 6="5-4 4-se" 3-2="se"></1></0></0>', "div,span,directive,data,point,cropper,class,line,dashed");
1425
+
1426
+ /* Template source:
1427
+ <div class="cropper-container">
1428
+ <div class="cropper-canvas"></div>
1429
+ <div class="cropper-dragger">
1430
+ <span class="cropper-viewer"></span>
1431
+ <span class="cropper-dashed dashed-h"></span>
1432
+ <span class="cropper-dashed dashed-v"></span>
1433
+ <span class="cropper-face" data-directive="all"></span>
1434
+ <span class="cropper-line line-e" data-directive="e"></span>
1435
+ <span class="cropper-line line-n" data-directive="n"></span>
1436
+ <span class="cropper-line line-w" data-directive="w"></span>
1437
+ <span class="cropper-line line-s" data-directive="s"></span>
1438
+ <span class="cropper-point point-e" data-directive="e"></span>
1439
+ <span class="cropper-point point-n" data-directive="n"></span>
1440
+ <span class="cropper-point point-w" data-directive="w"></span>
1441
+ <span class="cropper-point point-s" data-directive="s"></span>
1442
+ <span class="cropper-point point-ne" data-directive="ne"></span>
1443
+ <span class="cropper-point point-nw" data-directive="nw"></span>
1444
+ <span class="cropper-point point-sw" data-directive="sw"></span>
1445
+ <span class="cropper-point point-se" data-directive="se"></span>
1446
+ </div>
1447
+ </div>
1448
+ */
1449
+
1450
+ Cropper.DEFAULTS = {
1451
+ // Basic
1452
+ aspectRatio: "auto",
1453
+ autoCropArea: 0.8, // 80%
1454
+ data: {
1455
+ // x: 0,
1456
+ // y: 0,
1457
+ // width: 300,
1458
+ // height: 150
1459
+ },
1460
+ done: $.noop,
1461
+ preview: "",
1462
+
1463
+ // Toggles
1464
+ multiple: FALSE,
1465
+ autoCrop: TRUE,
1466
+ dragCrop: TRUE,
1467
+ dashed: TRUE,
1468
+ modal: TRUE,
1469
+ movable: TRUE,
1470
+ resizable: TRUE,
1471
+ zoomable: TRUE,
1472
+ rotatable: TRUE,
1473
+
1474
+ // Dimensions
1475
+ minWidth: 0,
1476
+ minHeight: 0,
1477
+ maxWidth: INFINITY,
1478
+ maxHeight: INFINITY,
1479
+
1480
+ // Events
1481
+ build: NULL,
1482
+ built: NULL,
1483
+ dragstart: NULL,
1484
+ dragmove: NULL,
1485
+ dragend: NULL
1486
+ };
1487
+
1488
+ Cropper.setDefaults = function (options) {
1489
+ $.extend(Cropper.DEFAULTS, options);
1490
+ };
1491
+
1492
+ // Save the other cropper
1493
+ Cropper.other = $.fn.cropper;
1494
+
1495
+ // Register as jQuery plugin
1496
+ $.fn.cropper = function (options) {
1497
+ var args = [].slice.call(arguments, 1),
1498
+ result;
1499
+
1500
+ this.each(function () {
1501
+ var $this = $(this),
1502
+ data = $this.data("cropper"),
1503
+ fn;
1504
+
1505
+ if (!data) {
1506
+ $this.data("cropper", (data = new Cropper(this, options)));
1507
+ }
1508
+
1509
+ if (typeof options === "string" && $.isFunction((fn = data[options]))) {
1510
+ result = fn.apply(data, args);
1511
+ }
1512
+ });
1513
+
1514
+ return (typeof result !== STRING_UNDEFINED ? result : this);
1515
+ };
1516
+
1517
+ $.fn.cropper.Constructor = Cropper;
1518
+ $.fn.cropper.setDefaults = Cropper.setDefaults;
1519
+
1520
+ // No conflict
1521
+ $.fn.cropper.noConflict = function () {
1522
+ $.fn.cropper = Cropper.other;
1523
+ return this;
1524
+ };
1525
+ });