image_cropper 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+