image_cropper 1.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2e153fbfc633756a6098169bdb4c5e8e7b7f94b8
4
+ data.tar.gz: c1ea8b304c8ed45b56c204ed4ae40005add49760
5
+ SHA512:
6
+ metadata.gz: a75b951de33ab1038173d78d0f6a258ffe0acb9442a00ab17787bc9f8153827b43ef46a3a593adfb9fac34c0737a5722b26e9055240a77c1289f71210118b06d
7
+ data.tar.gz: 7708f4b822e256822940d02495f2b57c41e4356ef4be3762c80b2a44c1bf872196f6b2336d38491bf60ae5cd9d8b0732f09137959fe82385c1a269319a00ea7b
@@ -0,0 +1,228 @@
1
+ # image_cropper
2
+
3
+ Combine upload images with a cropper
4
+
5
+
6
+ ## Features
7
+
8
+ * Previews image before upload
9
+ * Considers carrierwave cache (when form return with error)
10
+ * Responsive
11
+ * Real crop (generate 2 files, and in case of circle frame, effectively crop an circular image)
12
+
13
+ ## Dependencies
14
+
15
+ * Gem [jquery-rails](https://github.com/rails/jquery-rails)
16
+ * Gem [CarrierWave](https://github.com/carrierwaveuploader/carrierwave) to upload files
17
+ * Gem [RMagick](https://github.com/rmagick/rmagick) to crop images
18
+ * Gem [font-awesome-rails](https://github.com/bokmann/font-awesome-rails) to show pretty icons
19
+
20
+
21
+ ___
22
+
23
+
24
+ # Getting Started
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'image_cropper', git: 'git@gitlab.snappler.com:gems/image_cropper.git'
32
+ ```
33
+
34
+ And then execute:
35
+
36
+ $ bundle install
37
+
38
+
39
+ Finally run:
40
+
41
+ rails g image_cropper:install
42
+
43
+ This one generate the default null image **'app/assets/images/image_cropper/default.png'** used to show when the image window don't have one (later you can modify if you need)
44
+
45
+ ## Create/Update the Uploader
46
+
47
+ Start off by [generating an uploader](https://github.com/carrierwaveuploader/carrierwave#user-content-getting-started) or modify if you already get one:
48
+
49
+ Using "Image" as an example, you should have a file
50
+
51
+ app/uploaders/image_uploader.rb
52
+
53
+ Add these lines and leave it like this:
54
+
55
+ ```ruby
56
+ class ImageUploader < CarrierWave::Uploader::Base
57
+
58
+ #If you need to define another/s columns, just define in class
59
+ #this methods and puts in th array, but it's no necesary and take :file as default
60
+ #def self.mount_uploader_columns
61
+ # [:file]
62
+ #end
63
+
64
+ #Include the functionality of the gem
65
+ include ImageCropper::Uploader
66
+
67
+ storage :file
68
+
69
+ def store_dir
70
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
71
+ end
72
+ end
73
+ ```
74
+
75
+
76
+
77
+
78
+
79
+ ## Update Model
80
+
81
+ As an example I use a dummy model **User**:
82
+
83
+ $ rails g scaffold User name
84
+
85
+ Then integrate with [Carrierwave generating the column that it need](https://github.com/carrierwaveuploader/carrierwave#user-content-activerecord)
86
+ _(In the example of carrierwave use **avatar** as column, in this one I choose **file** as column because is more pretty :P)_
87
+
88
+
89
+
90
+ Now, add fields to the model you want to mount the uploader by executing:
91
+
92
+ $ rails g image_cropper:migrate User
93
+
94
+ This generate a file **XXXXXXXXXXXXXX_image_cropper_add_fields_to_users.rb**
95
+
96
+ ```ruby
97
+ class ImageCropperAddFieldsToUsers < ActiveRecord::Migration
98
+ def change
99
+ #Used to crop file
100
+ add_column :users, :coord_x, :float, default: 0
101
+ add_column :users, :coord_y, :float, default: 0
102
+ add_column :users, :coord_w, :float, default: 0
103
+ add_column :users, :coord_h, :float, default: 0
104
+ add_column :users, :coord_z, :float, default: 1
105
+ add_column :users, :frame, :string, default: 'square'
106
+ end
107
+ end
108
+
109
+ ```
110
+
111
+ Then execute:
112
+
113
+ ```ruby
114
+ rake db:migrate
115
+ ```
116
+
117
+
118
+ Open your model file and add these line:
119
+
120
+ ```ruby
121
+ class User < ActiveRecord::Base
122
+ include ImageCropper::Model
123
+ mount_uploader :file, ImageUploader
124
+ end
125
+ ```
126
+
127
+
128
+ ## Require Assets (Css/Js)
129
+
130
+ Include the following in your javascript manifest:
131
+
132
+ ```javascript
133
+ //= require jquery
134
+ //= require image_cropper
135
+ ```
136
+
137
+ And the following in your stylesheet manifest:
138
+
139
+ ```css
140
+ *= require font-awesome
141
+ *= require image_cropper
142
+ ```
143
+ (for the moment font-awesome is dependency and you need to require)
144
+
145
+
146
+ # Using
147
+
148
+ Now you're able to use it in forms:
149
+
150
+ ```erb
151
+ <div style='max-width: 400px;'>
152
+ <%= f.image_cropper_field :file %>
153
+ </div>
154
+ ```
155
+
156
+ If you need to save remote image, you need to set value in the hidden field and trigger 'change'
157
+ ```js
158
+ $('.image-cropper-file-url').val('https://www.gettyimages.es/gi-resources/images/Embed/new/embed2.jpg').trigger('change');
159
+ ```
160
+
161
+
162
+ remember put the property 'multipart' to form when you use image_cropper:
163
+
164
+ ```erb
165
+ <%= form_with(model: user, local: true, html: {multipart: true}) do |form| %>
166
+ ```
167
+
168
+ And add to strong parameters the fields
169
+
170
+ ```ruby
171
+ params.require(:user).permit(:name, :coord_x, :coord_y, :coord_w, :coord_h, :coord_z, :frame, :file, :file_cache, :remote_file_url)
172
+ ```
173
+ **(Is very important the orden of these params, always leave the :file_cache, and :file to the end)**
174
+
175
+
176
+
177
+ And in a show view:
178
+
179
+ ```erb
180
+ <div style='max-width: 400px;'>
181
+ <%= image_tag @user.file.thumb.url, class: 'image-cropper' %>
182
+ </div>
183
+ ```
184
+
185
+ You can send some params
186
+
187
+ * aspect_ratio
188
+ * frame
189
+ * operation_object
190
+ * destroyable
191
+ * destroy_class
192
+ * destroy_confirm
193
+
194
+ | Params | Type | Default | Options | Description |
195
+ | ---------------- |:-------------:|:-------------------------------:|:--------------------------------------:|-----------------------------------------------------------------------------:|
196
+ | aspect_ratio | _(string)_ | **'4:3'** | any combination of **'number:number'** | 4:4 are perfect square, 4:3 rectangle horizontal, 3:4 rectangle vertical |
197
+ | frame | _(string)_ | **'square'** | **'square'** or **'circle'** | frame of image, if you want a circle o square frame |
198
+ | operation_object | _(object)_ | **self** | any **object** | maybe the object where mount the uploader it's not the one you want to erase |
199
+ | destroyable | _(boolean)_ | **false** | **true** or **false** | If you want a button to remove object with nested_attributes |
200
+ | destroy_class | _(string)_ | **'.image-cropper-container'** | a class or id of containter | Is the container that disapear when you click remove |
201
+ | destroy_confirm | _(string)_ | **'Are you sure?'** | any message you want | message to display to confirm to remove the image |
202
+
203
+
204
+ ___
205
+
206
+
207
+ ## Features to the future
208
+ * Make test
209
+ * Problem with gif when crop area, not resize - **SOLVED**
210
+ * Problem with jpg, jpeg, circular crop - **NOT ALOW, JPG AND JPEG CAN'T CIRCULAR CROP**
211
+
212
+ ## Thanks!
213
+
214
+ RubyGems
215
+
216
+ Esta gema está publicada en [RubyGems](https://rubygems.org/gems/image_cropper) bajo el user juan.labattaglia@snappler.com
217
+
218
+
219
+ ## Actualización
220
+
221
+ Una vez que hay una nueva versión, modificar `lib/image_cropper/version.rb`
222
+
223
+ Y luego ejecutar:
224
+
225
+ ```shell
226
+ gem build image_cropper.gemspec
227
+ gem push image_cropper-[VERSION].gem
228
+ ```
@@ -0,0 +1,688 @@
1
+
2
+ (function ( $ ) {
3
+
4
+ $.fn.image_cropper = function( options ) {
5
+
6
+ var settings = $.extend({
7
+ }, options );
8
+
9
+ //-------------------------- Definitions
10
+ //Containers
11
+ var image_cropper_container = $(this);
12
+ var image_cropper_crop_container = image_cropper_container.find('.image-cropper-crop-container');
13
+
14
+ //Components
15
+ var image_cropper_crop = image_cropper_container.find('.image-cropper-crop');
16
+ var image_cropper_file = image_cropper_container.find('.image-cropper-file');
17
+ var image_cropper_file_cache = image_cropper_container.find('.image-cropper-file-cache');
18
+ var image_cropper_file_url = image_cropper_container.find('.image-cropper-file-url');
19
+
20
+ //Coords
21
+ var coord_x = image_cropper_container.find('.coord-x');
22
+ var coord_y = image_cropper_container.find('.coord-y');
23
+ var coord_z = image_cropper_container.find('.coord-z');
24
+ var coord_w = image_cropper_container.find('.coord-w');
25
+ var coord_h = image_cropper_container.find('.coord-h');
26
+
27
+ //Buttons
28
+ var image_cropper_file_trigger = image_cropper_container.find('.image-cropper-file-trigger');
29
+ var coord_zoom_in = image_cropper_container.find('.image-cropper-zoom-in');
30
+ var coord_zoom_out = image_cropper_container.find('.image-cropper-zoom-out');
31
+ var image_cropper_remove = image_cropper_container.find('.image-cropper-remove');
32
+
33
+ //Aux vars
34
+ var coord_x_val, coord_y_val, coord_z_val, aspect_ratio, aspect_ratio_arr, w_val, h_val, coord_w_val, coord_h_val;
35
+
36
+
37
+
38
+ var image_cropper_initialize_vars = function(){
39
+
40
+
41
+ //Set image_cropper active
42
+ image_cropper_container.addClass('active');
43
+
44
+ //Get X,Y,W,H,Z
45
+ coord_x_val = parseFloat(coord_x.val());
46
+ if (isNaN(coord_x_val)){
47
+ coord_x_val = 0;
48
+ }
49
+
50
+ coord_y_val = parseFloat(coord_y.val());
51
+ if (isNaN(coord_y_val)){
52
+ coord_y_val = 0;
53
+ }
54
+ coord_z_val = parseFloat(coord_z.val());
55
+ if (isNaN(coord_z_val)){
56
+ coord_z_val = 1;
57
+ }
58
+
59
+ aspect_ratio = image_cropper_container.data('aspect-ratio');
60
+
61
+ if (aspect_ratio === undefined){
62
+ aspect_ratio = '4:3';
63
+ }
64
+
65
+ aspect_ratio_arr = aspect_ratio.split(':');
66
+ w_val = parseFloat(aspect_ratio_arr[0]);
67
+ if (isNaN(w_val)){
68
+ w_val = 4;
69
+ }
70
+ h_val = parseFloat(aspect_ratio_arr[1]);
71
+ if (isNaN(h_val)){
72
+ h_val = 3;
73
+ }
74
+ coord_w_val = w_val * 160;
75
+ coord_h_val = h_val * 160;
76
+
77
+
78
+ //Define Crop
79
+ image_cropper_crop.guillotine({
80
+ width: coord_w_val,
81
+ height: coord_h_val,
82
+ init: { scale: coord_z_val, x: coord_x_val, y: coord_y_val },
83
+ onChange: function(data, action){
84
+ coord_x.val(data.x);
85
+ coord_y.val(data.y);
86
+ coord_w.val(data.w);
87
+ coord_h.val(data.h);
88
+ coord_z.val(data.scale);
89
+ }
90
+ });
91
+
92
+
93
+ if ((settings['enabled'] !== undefined) && (settings['enabled'] === false)){
94
+ image_cropper_crop.guillotine('disable');
95
+ image_cropper_crop_container.addClass('disable');
96
+ }
97
+
98
+ //Initialize
99
+ data = image_cropper_crop.guillotine('getData');
100
+ coord_x.val(data.x);
101
+ coord_y.val(data.y);
102
+ coord_w.val(data.w);
103
+ coord_h.val(data.h);
104
+ coord_z.val(data.scale);
105
+ }
106
+
107
+ var image_cropper_initialize_events = function(){
108
+
109
+
110
+ //remove image
111
+ image_cropper_remove.off('click');
112
+ image_cropper_remove.on('click', function() {
113
+ if (confirm($(this).data('destroy-confirm'))) {
114
+ $(this).prev('input[type=hidden]').val('1');
115
+
116
+ $(this).closest(settings['destroy_class']).fadeOut(function(){
117
+ $(this).hide();
118
+ });
119
+ }
120
+ });
121
+
122
+
123
+ //Upload image
124
+ image_cropper_file_trigger.off('click');
125
+ image_cropper_file_trigger.on('click', function() {
126
+ image_cropper_file.trigger('click');
127
+ });
128
+
129
+ //When change input file show img
130
+ image_cropper_file.off('change');
131
+ image_cropper_file.on('change', function() {
132
+ if (this.files && this.files[0]) {
133
+ //image_cropper_file_binary.val('');
134
+ var reader = new FileReader;
135
+ reader.onload = function(e) {
136
+ set_image(e.target.result);
137
+ image_cropper_file_url.val('');
138
+ image_cropper_file_cache.val('');
139
+ };
140
+ reader.readAsDataURL(this.files[0]);
141
+ }
142
+ });
143
+
144
+
145
+ //When change input url show img
146
+ image_cropper_file_url.off('change');
147
+ image_cropper_file_url.on('change', function() {
148
+ if ($(this).val() != '') {
149
+ set_image($(this).val());
150
+ image_cropper_file.val('');
151
+ image_cropper_file_cache.val('');
152
+ }
153
+ });
154
+
155
+
156
+ //Zoom in
157
+ coord_zoom_in.off('click');
158
+ coord_zoom_in.on('click', function() {
159
+ image_cropper_crop.guillotine('zoomIn');
160
+ });
161
+
162
+ //Zoom out
163
+ coord_zoom_out.off('click');
164
+ coord_zoom_out.on('click', function() {
165
+ image_cropper_crop.guillotine('zoomOut');
166
+ });
167
+
168
+ //Scrolling Zoom
169
+ image_cropper_crop.off('DOMMouseScroll mousewheel');
170
+ image_cropper_crop.on('DOMMouseScroll mousewheel', function (event) {
171
+ var guillotine = image_cropper_crop.guillotine('instance');
172
+ if(guillotine !== undefined){
173
+ if( event.originalEvent.detail > 0 || event.originalEvent.wheelDelta < 0 ) { //alternative options for wheelData: wheelDeltaX & wheelDeltaY
174
+ image_cropper_crop.guillotine('zoomOut');
175
+ } else {
176
+ image_cropper_crop.guillotine('zoomIn');
177
+ }
178
+ }
179
+ return false;
180
+ });
181
+
182
+
183
+ }
184
+
185
+
186
+ var set_image = function(data_uri){
187
+ var i;
188
+ image_cropper_crop.guillotine('remove');
189
+ image_cropper_crop.attr('src', data_uri);
190
+ coord_x.val('');
191
+ coord_y.val('');
192
+ coord_w.val('');
193
+ coord_h.val('');
194
+ coord_z.val('');
195
+ settings['enabled'] = true;
196
+ image_cropper_crop.guillotine('enable');
197
+ image_cropper_crop_container.removeClass('disable');
198
+
199
+ i = new Image();
200
+ i.src = data_uri;
201
+ i.onload = function() {
202
+ //alert(i.naturalWidth + ' x ' + i.naturalHeight);
203
+ image_cropper_initialize_vars();
204
+ };
205
+ }
206
+
207
+
208
+
209
+
210
+ //-------------------------- Initialize
211
+ //alert(image_cropper_container.hasClass('active'));
212
+ if(!image_cropper_container.hasClass('active')){
213
+ var i;
214
+ i = new Image();
215
+ i.src = image_cropper_crop.attr('src');
216
+ i.onload = function(e) {
217
+ image_cropper_initialize_events();
218
+ image_cropper_initialize_vars();
219
+ };
220
+ }
221
+
222
+
223
+ };
224
+
225
+ }( jQuery ));
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+ // Generated by CoffeeScript 1.8.0
252
+
253
+ /*
254
+ * jQuery Guillotine Plugin v1.3.1
255
+ * http://matiasgagliano.github.com/guillotine/
256
+ *
257
+ * Copyright 2014, Matías Gagliano.
258
+ * Dual licensed under the MIT or GPLv3 licenses.
259
+ * http://opensource.org/licenses/MIT
260
+ * http://opensource.org/licenses/GPL-3.0
261
+ *
262
+ */
263
+
264
+ (function() {
265
+ "use strict";
266
+ var $, Guillotine, canTransform, defaults, events, getPointerPosition, hardwareAccelerate, isTouch, pluginName, scope, touchRegExp, validEvent, whitelist,
267
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
268
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
269
+
270
+ $ = jQuery;
271
+
272
+ pluginName = 'guillotine';
273
+
274
+ scope = 'guillotine';
275
+
276
+ events = {
277
+ start: "touchstart." + scope + " mousedown." + scope,
278
+ move: "touchmove." + scope + " mousemove." + scope,
279
+ stop: "touchend." + scope + " mouseup." + scope
280
+ };
281
+
282
+ defaults = {
283
+ width: 400,
284
+ height: 300,
285
+ zoomStep: 0.1,
286
+ init: null,
287
+ eventOnChange: null,
288
+ onChange: null
289
+ };
290
+
291
+ touchRegExp = /touch/i;
292
+
293
+ isTouch = function(e) {
294
+ return touchRegExp.test(e.type);
295
+ };
296
+
297
+ validEvent = function(e) {
298
+ if (isTouch(e)) {
299
+ return e.originalEvent.changedTouches.length === 1;
300
+ } else {
301
+ return e.which === 1;
302
+ }
303
+ };
304
+
305
+ getPointerPosition = function(e) {
306
+ if (isTouch(e)) {
307
+ e = e.originalEvent.touches[0];
308
+ }
309
+ return {
310
+ x: e.pageX,
311
+ y: e.pageY
312
+ };
313
+ };
314
+
315
+ canTransform = function() {
316
+ var hasTransform, helper, prefix, prefixes, prop, test, tests, value, _i, _len;
317
+ hasTransform = false;
318
+ prefixes = ['webkit', 'Moz', 'O', 'ms', 'Khtml'];
319
+ tests = {
320
+ transform: 'transform'
321
+ };
322
+ for (_i = 0, _len = prefixes.length; _i < _len; _i++) {
323
+ prefix = prefixes[_i];
324
+ tests[prefix + 'Transform'] = '-' + prefix.toLowerCase() + '-transform';
325
+ }
326
+ helper = document.createElement('img');
327
+ document.body.insertBefore(helper, null);
328
+ for (test in tests) {
329
+ prop = tests[test];
330
+ if (helper.style[test] === void 0) {
331
+ continue;
332
+ }
333
+ helper.style[test] = 'rotate(90deg)';
334
+ value = window.getComputedStyle(helper).getPropertyValue(prop);
335
+ if ((value != null) && value.length && value !== 'none') {
336
+ hasTransform = true;
337
+ break;
338
+ }
339
+ }
340
+ document.body.removeChild(helper);
341
+ canTransform = hasTransform ? (function() {
342
+ return true;
343
+ }) : (function() {
344
+ return false;
345
+ });
346
+ return canTransform();
347
+ };
348
+
349
+ hardwareAccelerate = function(el) {
350
+ return $(el).css({
351
+ '-webkit-perspective': 1000,
352
+ 'perspective': 1000,
353
+ '-webkit-backface-visibility': 'hidden',
354
+ 'backface-visibility': 'hidden'
355
+ });
356
+ };
357
+
358
+ Guillotine = (function() {
359
+ function Guillotine(element, options) {
360
+ this._drag = __bind(this._drag, this);
361
+ this._unbind = __bind(this._unbind, this);
362
+ this._start = __bind(this._start, this);
363
+ this.op = $.extend(true, {}, defaults, options, $(element).data(pluginName));
364
+ this.enabled = true;
365
+ this.zoomInFactor = 1 + this.op.zoomStep;
366
+ this.zoomOutFactor = 1 / this.zoomInFactor;
367
+ this.glltRatio = this.op.height / this.op.width;
368
+ this.width = this.height = this.left = this.top = this.angle = 0;
369
+ this.data = {
370
+ scale: 1,
371
+ angle: 0,
372
+ x: 0,
373
+ y: 0,
374
+ w: this.op.width,
375
+ h: this.op.height
376
+ };
377
+ this._wrap(element);
378
+ if (this.op.init != null) {
379
+ this._init();
380
+ }
381
+ if (this.width < 1 || this.height < 1) {
382
+ this._fit() && this._center();
383
+ }
384
+ hardwareAccelerate(this.$el);
385
+ this.$el.on(events.start, this._start);
386
+ }
387
+
388
+ Guillotine.prototype._wrap = function(element) {
389
+ var canvas, el, guillotine, height, paddingTop, width;
390
+ el = $(element);
391
+ if (element.tagName === 'IMG') {
392
+ if (element.naturalWidth) {
393
+ width = element.naturalWidth;
394
+ height = element.naturalHeight;
395
+ } else {
396
+ el.addClass('guillotine-sample');
397
+ width = el.width();
398
+ height = el.height();
399
+ el.removeClass('guillotine-sample');
400
+ }
401
+ } else {
402
+ width = el.width();
403
+ height = el.height();
404
+ }
405
+ this.width = width / this.op.width;
406
+ this.height = height / this.op.height;
407
+ canvas = $('<div>').addClass('guillotine-canvas');
408
+ canvas.css({
409
+ width: this.width * 100 + '%',
410
+ height: this.height * 100 + '%',
411
+ top: 0,
412
+ left: 0
413
+ });
414
+ canvas = el.wrap(canvas).parent();
415
+ paddingTop = this.op.height / this.op.width * 100 + '%';
416
+ guillotine = $('<div>').addClass('guillotine-window');
417
+ guillotine.css({
418
+ width: '100%',
419
+ height: 'auto',
420
+ 'padding-top': paddingTop
421
+ });
422
+ guillotine = canvas.wrap(guillotine).parent();
423
+ this.$el = el;
424
+ this.el = el[0];
425
+ this.$canvas = canvas;
426
+ this.canvas = canvas[0];
427
+ this.$gllt = guillotine;
428
+ this.gllt = guillotine[0];
429
+ this.$document = $(element.ownerDocument);
430
+ return this.$body = $('body', this.$document);
431
+ };
432
+
433
+ Guillotine.prototype._unwrap = function() {
434
+ this.$el.removeAttr('style');
435
+ this.$el.insertBefore(this.gllt);
436
+ return this.$gllt.remove();
437
+ };
438
+
439
+ Guillotine.prototype._init = function() {
440
+ var angle, o, scale;
441
+ o = this.op.init;
442
+ if ((scale = parseFloat(o.scale))) {
443
+ this._zoom(scale);
444
+ }
445
+ if ((angle = parseInt(o.angle))) {
446
+ this._rotate(angle);
447
+ }
448
+ return this._offset(parseInt(o.x) / this.op.width || 0, parseInt(o.y) / this.op.height || 0);
449
+ };
450
+
451
+ Guillotine.prototype._start = function(e) {
452
+ if (!(this.enabled && validEvent(e))) {
453
+ return;
454
+ }
455
+ e.preventDefault();
456
+ e.stopImmediatePropagation();
457
+ this.p = getPointerPosition(e);
458
+ return this._bind();
459
+ };
460
+
461
+ Guillotine.prototype._bind = function() {
462
+ this.$body.addClass('guillotine-dragging');
463
+ this.$document.on(events.move, this._drag);
464
+ return this.$document.on(events.stop, this._unbind);
465
+ };
466
+
467
+ Guillotine.prototype._unbind = function(e) {
468
+ this.$body.removeClass('guillotine-dragging');
469
+ this.$document.off(events.move, this._drag);
470
+ this.$document.off(events.stop, this._unbind);
471
+ if (e != null) {
472
+ return this._trigger('drag');
473
+ }
474
+ };
475
+
476
+ Guillotine.prototype._trigger = function(action) {
477
+ if (this.op.eventOnChange != null) {
478
+ this.$el.trigger(this.op.eventOnChange, [this.data, action]);
479
+ }
480
+ if (typeof this.op.onChange === 'function') {
481
+ return this.op.onChange.call(this.el, this.data, action);
482
+ }
483
+ };
484
+
485
+ Guillotine.prototype._drag = function(e) {
486
+ var dx, dy, left, p, top;
487
+ e.preventDefault();
488
+ e.stopImmediatePropagation();
489
+ p = getPointerPosition(e);
490
+ dx = p.x - this.p.x;
491
+ dy = p.y - this.p.y;
492
+ this.p = p;
493
+ left = dx === 0 ? null : this.left - dx / this.gllt.clientWidth;
494
+ top = dy === 0 ? null : this.top - dy / this.gllt.clientHeight;
495
+ return this._offset(left, top);
496
+ };
497
+
498
+ Guillotine.prototype._offset = function(left, top) {
499
+ if (left || left === 0) {
500
+ if (left < 0) {
501
+ left = 0;
502
+ }
503
+ if (left > this.width - 1) {
504
+ left = this.width - 1;
505
+ }
506
+ this.canvas.style.left = (-left * 100).toFixed(2) + '%';
507
+ this.left = left;
508
+ this.data.x = Math.round(left * this.op.width);
509
+ }
510
+ if (top || top === 0) {
511
+ if (top < 0) {
512
+ top = 0;
513
+ }
514
+ if (top > this.height - 1) {
515
+ top = this.height - 1;
516
+ }
517
+ this.canvas.style.top = (-top * 100).toFixed(2) + '%';
518
+ this.top = top;
519
+ return this.data.y = Math.round(top * this.op.height);
520
+ }
521
+ };
522
+
523
+ Guillotine.prototype._zoom = function(factor) {
524
+ var h, left, top, w;
525
+ if (factor <= 0 || factor === 1) {
526
+ return;
527
+ }
528
+ w = this.width;
529
+ h = this.height;
530
+ if (w * factor > 1 && h * factor > 1) {
531
+ this.width *= factor;
532
+ this.height *= factor;
533
+ this.canvas.style.width = (this.width * 100).toFixed(2) + '%';
534
+ this.canvas.style.height = (this.height * 100).toFixed(2) + '%';
535
+ this.data.scale *= factor;
536
+ } else {
537
+ this._fit();
538
+ factor = this.width / w;
539
+ }
540
+ left = (this.left + 0.5) * factor - 0.5;
541
+ top = (this.top + 0.5) * factor - 0.5;
542
+ return this._offset(left, top);
543
+ };
544
+
545
+ Guillotine.prototype._fit = function() {
546
+ var prevWidth, relativeRatio;
547
+ prevWidth = this.width;
548
+ relativeRatio = this.height / this.width;
549
+ if (relativeRatio > 1) {
550
+ this.width = 1;
551
+ this.height = relativeRatio;
552
+ } else {
553
+ this.width = 1 / relativeRatio;
554
+ this.height = 1;
555
+ }
556
+ this.canvas.style.width = (this.width * 100).toFixed(2) + '%';
557
+ this.canvas.style.height = (this.height * 100).toFixed(2) + '%';
558
+ return this.data.scale *= this.width / prevWidth;
559
+ };
560
+
561
+ Guillotine.prototype._center = function() {
562
+ return this._offset((this.width - 1) / 2, (this.height - 1) / 2);
563
+ };
564
+
565
+ Guillotine.prototype._rotate = function(angle) {
566
+ var canvasRatio, glltRatio, h, w, _ref, _ref1, _ref2;
567
+ if (!canTransform()) {
568
+ return;
569
+ }
570
+ if (!(angle !== 0 && angle % 90 === 0)) {
571
+ return;
572
+ }
573
+ this.angle = (this.angle + angle) % 360;
574
+ if (this.angle < 0) {
575
+ this.angle = 360 + this.angle;
576
+ }
577
+ if (angle % 180 !== 0) {
578
+ glltRatio = this.op.height / this.op.width;
579
+ _ref = [this.height * glltRatio, this.width / glltRatio], this.width = _ref[0], this.height = _ref[1];
580
+ if (this.width >= 1 && this.height >= 1) {
581
+ this.canvas.style.width = this.width * 100 + '%';
582
+ this.canvas.style.height = this.height * 100 + '%';
583
+ } else {
584
+ this._fit();
585
+ }
586
+ }
587
+ _ref1 = [1, 1], w = _ref1[0], h = _ref1[1];
588
+ if (this.angle % 180 !== 0) {
589
+ canvasRatio = this.height / this.width * glltRatio;
590
+ _ref2 = [canvasRatio, 1 / canvasRatio], w = _ref2[0], h = _ref2[1];
591
+ }
592
+ this.el.style.width = w * 100 + '%';
593
+ this.el.style.height = h * 100 + '%';
594
+ this.el.style.left = (1 - w) / 2 * 100 + '%';
595
+ this.el.style.top = (1 - h) / 2 * 100 + '%';
596
+ this.$el.css({
597
+ transform: "rotate(" + this.angle + "deg)"
598
+ });
599
+ this._center();
600
+ return this.data.angle = this.angle;
601
+ };
602
+
603
+ Guillotine.prototype.rotateLeft = function() {
604
+ return this.enabled && (this._rotate(-90), this._trigger('rotateLeft'));
605
+ };
606
+
607
+ Guillotine.prototype.rotateRight = function() {
608
+ return this.enabled && (this._rotate(90), this._trigger('rotateRight'));
609
+ };
610
+
611
+ Guillotine.prototype.center = function() {
612
+ return this.enabled && (this._center(), this._trigger('center'));
613
+ };
614
+
615
+ Guillotine.prototype.fit = function() {
616
+ return this.enabled && (this._fit(), this._center(), this._trigger('fit'));
617
+ };
618
+
619
+ Guillotine.prototype.zoomIn = function() {
620
+ return this.enabled && (this._zoom(this.zoomInFactor), this._trigger('zoomIn'));
621
+ };
622
+
623
+ Guillotine.prototype.zoomOut = function() {
624
+ return this.enabled && (this._zoom(this.zoomOutFactor), this._trigger('zoomOut'));
625
+ };
626
+
627
+ Guillotine.prototype.getData = function() {
628
+ return this.data;
629
+ };
630
+
631
+ Guillotine.prototype.enable = function() {
632
+ return this.enabled = true;
633
+ };
634
+
635
+ Guillotine.prototype.disable = function() {
636
+ return this.enabled = false;
637
+ };
638
+
639
+ Guillotine.prototype.remove = function() {
640
+ this._unbind();
641
+ this._unwrap();
642
+ this.disable();
643
+ this.$el.off(events.start, this._start);
644
+ return this.$el.removeData(pluginName + 'Instance');
645
+ };
646
+
647
+ return Guillotine;
648
+
649
+ })();
650
+
651
+ whitelist = ['rotateLeft', 'rotateRight', 'center', 'fit', 'zoomIn', 'zoomOut', 'instance', 'getData', 'enable', 'disable', 'remove'];
652
+
653
+ $.fn[pluginName] = function(options) {
654
+ if (typeof options !== 'string') {
655
+ return this.each(function() {
656
+ var guillotine;
657
+ if (!$.data(this, pluginName + 'Instance')) {
658
+ guillotine = new Guillotine(this, options);
659
+ return $.data(this, pluginName + 'Instance', guillotine);
660
+ }
661
+ });
662
+ } else if (__indexOf.call(whitelist, options) >= 0) {
663
+ if (options === 'instance') {
664
+ return $.data(this[0], pluginName + 'Instance');
665
+ }
666
+ if (options === 'getData') {
667
+ return $.data(this[0], pluginName + 'Instance')[options]();
668
+ }
669
+ return this.each(function() {
670
+ var guillotine;
671
+ guillotine = $.data(this, pluginName + 'Instance');
672
+ if (guillotine) {
673
+ return guillotine[options]();
674
+ }
675
+ });
676
+ }
677
+ };
678
+
679
+ }).call(this);
680
+
681
+
682
+
683
+
684
+
685
+
686
+
687
+
688
+