Jcrop 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0847b44ae8762d6bff6616a7cd351bc8b6d0349c
4
+ data.tar.gz: 9207c5a9cd50b44abe9ab8f22add618e23eaa209
5
+ SHA512:
6
+ metadata.gz: e8f9c74e594ec08b78c3d553695d9cd067621f17d6bdeb5f9eebef6824986b15633f58fc6f9e26df49f84b36a1067a2b58b7523e837fea85f3d8be4704a1d8a0
7
+ data.tar.gz: 3d226a9726164b99404dfad12074f3d447d954fb7b43c3911fe05ceafe6c6dff3602ba58a9dc25bd0c63c27637b7b87eb433976542173c1eddfae623de99dd0e
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in Jcrop.gemspec
4
+ gemspec
data/Jcrop.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'Jcrop/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "Jcrop"
8
+ spec.version = Jcrop::VERSION
9
+ spec.authors = ["Pouya Gharib Pour"]
10
+ spec.email = ["p.gharibpour@gmail.com"]
11
+ spec.summary = %q{Jcrop library.}
12
+ spec.description = %q{A gem for adding Jcrop library to your Rails project.}
13
+ spec.homepage = "https://github.com/psparabara/Jcrop"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Pouya Gharib Pour
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Jcrop
2
+
3
+ A gem for adding [Jcrop(2.0.4)](https://github.com/tapmodo/Jcrop) library to your Rails project.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'Jcrop'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install Jcrop
20
+
21
+ Then add this line to your **application.js** file:
22
+
23
+ ```javascript
24
+ //= require Jcrop
25
+ ```
26
+
27
+ And this line to your **application.css** file:
28
+
29
+ ```scss
30
+ /*
31
+ *= require Jcrop
32
+ */
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it ( https://github.com/psparabara/Jcrop/fork )
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
Binary file
@@ -0,0 +1,2859 @@
1
+ /*! Jcrop.js v2.0.4 - build: 20151117
2
+ * @copyright 2008-2015 Tapmodo Interactive LLC
3
+ * @license Free software under MIT License
4
+ * @website http://jcrop.org/
5
+ **/
6
+ (function($){
7
+ 'use strict';
8
+
9
+ // Jcrop constructor
10
+ var Jcrop = function(element,opt){
11
+ var _ua = navigator.userAgent.toLowerCase();
12
+
13
+ this.opt = $.extend({},Jcrop.defaults,opt || {});
14
+
15
+ this.container = $(element);
16
+
17
+ this.opt.is_msie = /msie/.test(_ua);
18
+ this.opt.is_ie_lt9 = /msie [1-8]\./.test(_ua);
19
+
20
+ this.container.addClass(this.opt.css_container);
21
+
22
+ this.ui = {};
23
+ this.state = null;
24
+ this.ui.multi = [];
25
+ this.ui.selection = null;
26
+ this.filter = {};
27
+
28
+ this.init();
29
+ this.setOptions(opt);
30
+ this.applySizeConstraints();
31
+ this.container.trigger('cropinit',this);
32
+
33
+ // IE<9 doesn't work if mouse events are attached to window
34
+ if (this.opt.is_ie_lt9)
35
+ this.opt.dragEventTarget = document.body;
36
+ };
37
+
38
+
39
+ // Jcrop static functions
40
+ $.extend(Jcrop,{
41
+ component: { },
42
+ filter: { },
43
+ stage: { },
44
+ registerComponent: function(name,component){
45
+ Jcrop.component[name] = component;
46
+ },
47
+ registerFilter: function(name,filter){
48
+ Jcrop.filter[name] = filter;
49
+ },
50
+ registerStageType: function(name,stage){
51
+ Jcrop.stage[name] = stage;
52
+ },
53
+ // attach: function(element,opt){{{
54
+ attach: function(element,opt){
55
+ var obj = new $.Jcrop(element,opt);
56
+ return obj;
57
+ },
58
+ // }}}
59
+ // imgCopy: function(imgel){{{
60
+ imgCopy: function(imgel){
61
+ var img = new Image;
62
+ img.src = imgel.src;
63
+ return img;
64
+ },
65
+ // }}}
66
+ // imageClone: function(imgel){{{
67
+ imageClone: function(imgel){
68
+ return $.Jcrop.supportsCanvas?
69
+ Jcrop.canvasClone(imgel):
70
+ Jcrop.imgCopy(imgel);
71
+ },
72
+ // }}}
73
+ // canvasClone: function(imgel){{{
74
+ canvasClone: function(imgel){
75
+ var canvas = document.createElement('canvas'),
76
+ ctx = canvas.getContext('2d');
77
+
78
+ $(canvas).width(imgel.width).height(imgel.height),
79
+ canvas.width = imgel.naturalWidth;
80
+ canvas.height = imgel.naturalHeight;
81
+ ctx.drawImage(imgel,0,0,imgel.naturalWidth,imgel.naturalHeight);
82
+ return canvas;
83
+ },
84
+ // }}}
85
+ // propagate: function(plist,config,obj){{{
86
+ propagate: function(plist,config,obj){
87
+ for(var i=0,l=plist.length;i<l;i++)
88
+ if (config.hasOwnProperty(plist[i]))
89
+ obj[plist[i]] = config[plist[i]];
90
+ },
91
+ // }}}
92
+ // getLargestBox: function(ratio,w,h){{{
93
+ getLargestBox: function(ratio,w,h){
94
+ if ((w/h) > ratio)
95
+ return [ h * ratio, h ];
96
+ else return [ w, w / ratio ];
97
+ },
98
+ // }}}
99
+ // stageConstructor: function(el,options,callback){{{
100
+ stageConstructor: function(el,options,callback){
101
+
102
+ // Get a priority-ordered list of available stages
103
+ var stages = [];
104
+ $.each(Jcrop.stage,function(i,e){
105
+ stages.push(e);
106
+ });
107
+ stages.sort(function(a,b){ return a.priority - b.priority; });
108
+
109
+ // Find the first one that supports this element
110
+ for(var i=0,l=stages.length;i<l;i++){
111
+ if (stages[i].isSupported(el,options)){
112
+ stages[i].create(el,options,function(obj,opt){
113
+ if (typeof callback == 'function') callback(obj,opt);
114
+ });
115
+ break;
116
+ }
117
+ }
118
+ },
119
+ // }}}
120
+ // supportsColorFade: function(){{{
121
+ supportsColorFade: function(){
122
+ return $.fx.step.hasOwnProperty('backgroundColor');
123
+ },
124
+ // }}}
125
+ // wrapFromXywh: function(xywh){{{
126
+ wrapFromXywh: function(xywh){
127
+ var b = { x: xywh[0], y: xywh[1], w: xywh[2], h: xywh[3] };
128
+ b.x2 = b.x + b.w;
129
+ b.y2 = b.y + b.h;
130
+ return b;
131
+ }
132
+ // }}}
133
+ });
134
+
135
+ var AbstractStage = function(){
136
+ };
137
+
138
+ $.extend(AbstractStage,{
139
+ isSupported: function(el,o){
140
+ // @todo: should actually check if it's an HTML element
141
+ return true;
142
+ },
143
+ // A higher priority means less desirable
144
+ // AbstractStage is the last one we want to use
145
+ priority: 100,
146
+ create: function(el,options,callback){
147
+ var obj = new AbstractStage;
148
+ obj.element = el;
149
+ callback.call(this,obj,options);
150
+ },
151
+ prototype: {
152
+ attach: function(core){
153
+ this.init(core);
154
+ core.ui.stage = this;
155
+ },
156
+ triggerEvent: function(ev){
157
+ $(this.element).trigger(ev);
158
+ return this;
159
+ },
160
+ getElement: function(){
161
+ return this.element;
162
+ }
163
+ }
164
+ });
165
+ Jcrop.registerStageType('Block',AbstractStage);
166
+
167
+
168
+ var ImageStage = function(){
169
+ };
170
+
171
+ ImageStage.prototype = new AbstractStage();
172
+
173
+ $.extend(ImageStage,{
174
+ isSupported: function(el,o){
175
+ if (el.tagName == 'IMG') return true;
176
+ },
177
+ priority: 90,
178
+ create: function(el,options,callback){
179
+ $.Jcrop.component.ImageLoader.attach(el,function(w,h){
180
+ var obj = new ImageStage;
181
+ obj.element = $(el).wrap('<div />').parent();
182
+
183
+ obj.element.width(w).height(h);
184
+ obj.imgsrc = el;
185
+
186
+ if (typeof callback == 'function')
187
+ callback.call(this,obj,options);
188
+ });
189
+ }
190
+ });
191
+ Jcrop.registerStageType('Image',ImageStage);
192
+
193
+
194
+ var CanvasStage = function(){
195
+ this.angle = 0;
196
+ this.scale = 1;
197
+ this.scaleMin = 0.2;
198
+ this.scaleMax = 1.25;
199
+ this.offset = [0,0];
200
+ };
201
+
202
+ CanvasStage.prototype = new ImageStage();
203
+
204
+ $.extend(CanvasStage,{
205
+ isSupported: function(el,o){
206
+ if ($.Jcrop.supportsCanvas && (el.tagName == 'IMG')) return true;
207
+ },
208
+ priority: 60,
209
+ create: function(el,options,callback){
210
+ var $el = $(el);
211
+ var opt = $.extend({},options);
212
+ $.Jcrop.component.ImageLoader.attach(el,function(w,h){
213
+ var obj = new CanvasStage;
214
+ $el.hide();
215
+ obj.createCanvas(el,w,h);
216
+ $el.before(obj.element);
217
+ obj.imgsrc = el;
218
+ opt.imgsrc = el;
219
+
220
+ if (typeof callback == 'function'){
221
+ callback(obj,opt);
222
+ obj.redraw();
223
+ }
224
+ });
225
+ }
226
+ });
227
+
228
+ $.extend(CanvasStage.prototype,{
229
+ init: function(core){
230
+ this.core = core;
231
+ },
232
+ // setOffset: function(x,y) {{{
233
+ setOffset: function(x,y) {
234
+ this.offset = [x,y];
235
+ return this;
236
+ },
237
+ // }}}
238
+ // setAngle: function(v) {{{
239
+ setAngle: function(v) {
240
+ this.angle = v;
241
+ return this;
242
+ },
243
+ // }}}
244
+ // setScale: function(v) {{{
245
+ setScale: function(v) {
246
+ this.scale = this.boundScale(v);
247
+ return this;
248
+ },
249
+ // }}}
250
+ boundScale: function(v){
251
+ if (v<this.scaleMin) v = this.scaleMin;
252
+ else if (v>this.scaleMax) v = this.scaleMax;
253
+ return v;
254
+ },
255
+ createCanvas: function(img,w,h){
256
+ this.width = w;
257
+ this.height = h;
258
+ this.canvas = document.createElement('canvas');
259
+ this.canvas.width = w;
260
+ this.canvas.height = h;
261
+ this.$canvas = $(this.canvas).width('100%').height('100%');
262
+ this.context = this.canvas.getContext('2d');
263
+ this.fillstyle = "rgb(0,0,0)";
264
+ this.element = this.$canvas.wrap('<div />').parent().width(w).height(h);
265
+ },
266
+ triggerEvent: function(ev){
267
+ this.$canvas.trigger(ev);
268
+ return this;
269
+ },
270
+ // clear: function() {{{
271
+ clear: function() {
272
+ this.context.fillStyle = this.fillstyle;
273
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
274
+ return this;
275
+ },
276
+ // }}}
277
+ // redraw: function() {{{
278
+ redraw: function() {
279
+ // Save the current context
280
+ this.context.save();
281
+ this.clear();
282
+
283
+ // Translate to the center point of our image
284
+ this.context.translate(parseInt(this.width * 0.5), parseInt(this.height * 0.5));
285
+ // Perform the rotation and scaling
286
+ this.context.translate(this.offset[0]/this.core.opt.xscale,this.offset[1]/this.core.opt.yscale);
287
+ this.context.rotate(this.angle * (Math.PI/180));
288
+ this.context.scale(this.scale,this.scale);
289
+ // Translate back to the top left of our image
290
+ this.context.translate(-parseInt(this.width * 0.5), -parseInt(this.height * 0.5));
291
+ // Finally we draw the image
292
+ this.context.drawImage(this.imgsrc,0,0,this.width,this.height);
293
+
294
+ // And restore the updated context
295
+ this.context.restore();
296
+ this.$canvas.trigger('cropredraw');
297
+ return this;
298
+ },
299
+ // }}}
300
+ // setFillStyle: function(v) {{{
301
+ setFillStyle: function(v) {
302
+ this.fillstyle = v;
303
+ return this;
304
+ }
305
+ // }}}
306
+ });
307
+
308
+ Jcrop.registerStageType('Canvas',CanvasStage);
309
+
310
+
311
+ /**
312
+ * BackoffFilter
313
+ * move out-of-bounds selection into allowed position at same size
314
+ */
315
+ var BackoffFilter = function(){
316
+ this.minw = 40;
317
+ this.minh = 40;
318
+ this.maxw = 0;
319
+ this.maxh = 0;
320
+ this.core = null;
321
+ };
322
+ $.extend(BackoffFilter.prototype,{
323
+ tag: 'backoff',
324
+ priority: 22,
325
+ filter: function(b){
326
+ var r = this.bound;
327
+
328
+ if (b.x < r.minx) { b.x = r.minx; b.x2 = b.w + b.x; }
329
+ if (b.y < r.miny) { b.y = r.miny; b.y2 = b.h + b.y; }
330
+ if (b.x2 > r.maxx) { b.x2 = r.maxx; b.x = b.x2 - b.w; }
331
+ if (b.y2 > r.maxy) { b.y2 = r.maxy; b.y = b.y2 - b.h; }
332
+
333
+ return b;
334
+ },
335
+ refresh: function(sel){
336
+ this.elw = sel.core.container.width();
337
+ this.elh = sel.core.container.height();
338
+ this.bound = {
339
+ minx: 0 + sel.edge.w,
340
+ miny: 0 + sel.edge.n,
341
+ maxx: this.elw + sel.edge.e,
342
+ maxy: this.elh + sel.edge.s
343
+ };
344
+ }
345
+ });
346
+ Jcrop.registerFilter('backoff',BackoffFilter);
347
+
348
+ /**
349
+ * ConstrainFilter
350
+ * a filter to constrain crop selection to bounding element
351
+ */
352
+ var ConstrainFilter = function(){
353
+ this.core = null;
354
+ };
355
+ $.extend(ConstrainFilter.prototype,{
356
+ tag: 'constrain',
357
+ priority: 5,
358
+ filter: function(b,ord){
359
+ if (ord == 'move') {
360
+ if (b.x < this.minx) { b.x = this.minx; b.x2 = b.w + b.x; }
361
+ if (b.y < this.miny) { b.y = this.miny; b.y2 = b.h + b.y; }
362
+ if (b.x2 > this.maxx) { b.x2 = this.maxx; b.x = b.x2 - b.w; }
363
+ if (b.y2 > this.maxy) { b.y2 = this.maxy; b.y = b.y2 - b.h; }
364
+ } else {
365
+ if (b.x < this.minx) { b.x = this.minx; }
366
+ if (b.y < this.miny) { b.y = this.miny; }
367
+ if (b.x2 > this.maxx) { b.x2 = this.maxx; }
368
+ if (b.y2 > this.maxy) { b.y2 = this.maxy; }
369
+ }
370
+ b.w = b.x2 - b.x;
371
+ b.h = b.y2 - b.y;
372
+ return b;
373
+ },
374
+ refresh: function(sel){
375
+ this.elw = sel.core.container.width();
376
+ this.elh = sel.core.container.height();
377
+ this.minx = 0 + sel.edge.w;
378
+ this.miny = 0 + sel.edge.n;
379
+ this.maxx = this.elw + sel.edge.e;
380
+ this.maxy = this.elh + sel.edge.s;
381
+ }
382
+ });
383
+ Jcrop.registerFilter('constrain',ConstrainFilter);
384
+
385
+ /**
386
+ * ExtentFilter
387
+ * a filter to implement minimum or maximum size
388
+ */
389
+ var ExtentFilter = function(){
390
+ this.core = null;
391
+ };
392
+ $.extend(ExtentFilter.prototype,{
393
+ tag: 'extent',
394
+ priority: 12,
395
+ offsetFromCorner: function(corner,box,b){
396
+ var w = box[0], h = box[1];
397
+ switch(corner){
398
+ case 'bl': return [ b.x2 - w, b.y, w, h ];
399
+ case 'tl': return [ b.x2 - w , b.y2 - h, w, h ];
400
+ case 'br': return [ b.x, b.y, w, h ];
401
+ case 'tr': return [ b.x, b.y2 - h, w, h ];
402
+ }
403
+ },
404
+ getQuadrant: function(s){
405
+ var relx = s.opposite[0]-s.offsetx
406
+ var rely = s.opposite[1]-s.offsety;
407
+
408
+ if ((relx < 0) && (rely < 0)) return 'br';
409
+ else if ((relx >= 0) && (rely >= 0)) return 'tl';
410
+ else if ((relx < 0) && (rely >= 0)) return 'tr';
411
+ return 'bl';
412
+ },
413
+ filter: function(b,ord,sel){
414
+
415
+ if (ord == 'move') return b;
416
+
417
+ var w = b.w, h = b.h, st = sel.state, r = this.limits;
418
+ var quad = st? this.getQuadrant(st): 'br';
419
+
420
+ if (r.minw && (w < r.minw)) w = r.minw;
421
+ if (r.minh && (h < r.minh)) h = r.minh;
422
+ if (r.maxw && (w > r.maxw)) w = r.maxw;
423
+ if (r.maxh && (h > r.maxh)) h = r.maxh;
424
+
425
+ if ((w == b.w) && (h == b.h)) return b;
426
+
427
+ return Jcrop.wrapFromXywh(this.offsetFromCorner(quad,[w,h],b));
428
+ },
429
+ refresh: function(sel){
430
+ this.elw = sel.core.container.width();
431
+ this.elh = sel.core.container.height();
432
+
433
+ this.limits = {
434
+ minw: sel.minSize[0],
435
+ minh: sel.minSize[1],
436
+ maxw: sel.maxSize[0],
437
+ maxh: sel.maxSize[1]
438
+ };
439
+ }
440
+ });
441
+ Jcrop.registerFilter('extent',ExtentFilter);
442
+
443
+
444
+ /**
445
+ * GridFilter
446
+ * a rudimentary grid effect
447
+ */
448
+ var GridFilter = function(){
449
+ this.stepx = 1;
450
+ this.stepy = 1;
451
+ this.core = null;
452
+ };
453
+ $.extend(GridFilter.prototype,{
454
+ tag: 'grid',
455
+ priority: 19,
456
+ filter: function(b){
457
+
458
+ var n = {
459
+ x: Math.round(b.x / this.stepx) * this.stepx,
460
+ y: Math.round(b.y / this.stepy) * this.stepy,
461
+ x2: Math.round(b.x2 / this.stepx) * this.stepx,
462
+ y2: Math.round(b.y2 / this.stepy) * this.stepy
463
+ };
464
+
465
+ n.w = n.x2 - n.x;
466
+ n.h = n.y2 - n.y;
467
+
468
+ return n;
469
+ }
470
+ });
471
+ Jcrop.registerFilter('grid',GridFilter);
472
+
473
+
474
+ /**
475
+ * RatioFilter
476
+ * implements aspectRatio locking
477
+ */
478
+ var RatioFilter = function(){
479
+ this.ratio = 0;
480
+ this.core = null;
481
+ };
482
+ $.extend(RatioFilter.prototype,{
483
+ tag: 'ratio',
484
+ priority: 15,
485
+ offsetFromCorner: function(corner,box,b){
486
+ var w = box[0], h = box[1];
487
+ switch(corner){
488
+ case 'bl': return [ b.x2 - w, b.y, w, h ];
489
+ case 'tl': return [ b.x2 - w , b.y2 - h, w, h ];
490
+ case 'br': return [ b.x, b.y, w, h ];
491
+ case 'tr': return [ b.x, b.y2 - h, w, h ];
492
+ }
493
+ },
494
+ getBoundRatio: function(b,quad){
495
+ var box = Jcrop.getLargestBox(this.ratio,b.w,b.h);
496
+ return Jcrop.wrapFromXywh(this.offsetFromCorner(quad,box,b));
497
+ },
498
+ getQuadrant: function(s){
499
+ var relx = s.opposite[0]-s.offsetx
500
+ var rely = s.opposite[1]-s.offsety;
501
+
502
+ if ((relx < 0) && (rely < 0)) return 'br';
503
+ else if ((relx >= 0) && (rely >= 0)) return 'tl';
504
+ else if ((relx < 0) && (rely >= 0)) return 'tr';
505
+ return 'bl';
506
+ },
507
+ filter: function(b,ord,sel){
508
+
509
+ if (!this.ratio) return b;
510
+
511
+ var rt = b.w / b.h;
512
+ var st = sel.state;
513
+
514
+ var quad = st? this.getQuadrant(st): 'br';
515
+ ord = ord || 'se';
516
+
517
+ if (ord == 'move') return b;
518
+
519
+ switch(ord) {
520
+ case 'n':
521
+ b.x2 = this.elw;
522
+ b.w = b.x2 - b.x;
523
+ quad = 'tr';
524
+ break;
525
+ case 's':
526
+ b.x2 = this.elw;
527
+ b.w = b.x2 - b.x;
528
+ quad = 'br';
529
+ break;
530
+ case 'e':
531
+ b.y2 = this.elh;
532
+ b.h = b.y2 - b.y;
533
+ quad = 'br';
534
+ break;
535
+ case 'w':
536
+ b.y2 = this.elh;
537
+ b.h = b.y2 - b.y;
538
+ quad = 'bl';
539
+ break;
540
+ }
541
+
542
+ return this.getBoundRatio(b,quad);
543
+ },
544
+ refresh: function(sel){
545
+ this.ratio = sel.aspectRatio;
546
+ this.elw = sel.core.container.width();
547
+ this.elh = sel.core.container.height();
548
+ }
549
+ });
550
+ Jcrop.registerFilter('ratio',RatioFilter);
551
+
552
+
553
+ /**
554
+ * RoundFilter
555
+ * rounds coordinate values to integers
556
+ */
557
+ var RoundFilter = function(){
558
+ this.core = null;
559
+ };
560
+ $.extend(RoundFilter.prototype,{
561
+ tag: 'round',
562
+ priority: 90,
563
+ filter: function(b){
564
+
565
+ var n = {
566
+ x: Math.round(b.x),
567
+ y: Math.round(b.y),
568
+ x2: Math.round(b.x2),
569
+ y2: Math.round(b.y2)
570
+ };
571
+
572
+ n.w = n.x2 - n.x;
573
+ n.h = n.y2 - n.y;
574
+
575
+ return n;
576
+ }
577
+ });
578
+ Jcrop.registerFilter('round',RoundFilter);
579
+
580
+
581
+ /**
582
+ * ShadeFilter
583
+ * A filter that implements div-based shading on any element
584
+ *
585
+ * The shading you see is actually four semi-opaque divs
586
+ * positioned inside the container, around the selection
587
+ */
588
+ var ShadeFilter = function(opacity,color){
589
+ this.color = color || 'black';
590
+ this.opacity = opacity || 0.5;
591
+ this.core = null;
592
+ this.shades = {};
593
+ };
594
+ $.extend(ShadeFilter.prototype,{
595
+ tag: 'shader',
596
+ fade: true,
597
+ fadeEasing: 'swing',
598
+ fadeSpeed: 320,
599
+ priority: 95,
600
+ init: function(){
601
+ var t = this;
602
+
603
+ if (!t.attached) {
604
+ t.visible = false;
605
+
606
+ t.container = $('<div />').addClass(t.core.opt.css_shades)
607
+ .prependTo(this.core.container).hide();
608
+
609
+ t.elh = this.core.container.height();
610
+ t.elw = this.core.container.width();
611
+
612
+ t.shades = {
613
+ top: t.createShade(),
614
+ right: t.createShade(),
615
+ left: t.createShade(),
616
+ bottom: t.createShade()
617
+ };
618
+
619
+ t.attached = true;
620
+ }
621
+ },
622
+ destroy: function(){
623
+ this.container.remove();
624
+ },
625
+ setColor: function(color,instant){
626
+ var t = this;
627
+
628
+ if (color == t.color) return t;
629
+
630
+ this.color = color;
631
+ var colorfade = Jcrop.supportsColorFade();
632
+ $.each(t.shades,function(u,i){
633
+ if (!t.fade || instant || !colorfade) i.css('backgroundColor',color);
634
+ else i.animate({backgroundColor:color},{queue:false,duration:t.fadeSpeed,easing:t.fadeEasing});
635
+ });
636
+ return t;
637
+ },
638
+ setOpacity: function(opacity,instant){
639
+ var t = this;
640
+
641
+ if (opacity == t.opacity) return t;
642
+
643
+ t.opacity = opacity;
644
+ $.each(t.shades,function(u,i){
645
+ if (!t.fade || instant) i.css({opacity:opacity});
646
+ else i.animate({opacity:opacity},{queue:false,duration:t.fadeSpeed,easing:t.fadeEasing});
647
+ });
648
+ return t;
649
+ },
650
+ createShade: function(){
651
+ return $('<div />').css({
652
+ position: 'absolute',
653
+ backgroundColor: this.color,
654
+ opacity: this.opacity
655
+ }).appendTo(this.container);
656
+ },
657
+ refresh: function(sel){
658
+ var m = this.core, s = this.shades;
659
+
660
+ this.setColor(sel.bgColor?sel.bgColor:this.core.opt.bgColor);
661
+ this.setOpacity(sel.bgOpacity?sel.bgOpacity:this.core.opt.bgOpacity);
662
+
663
+ this.elh = m.container.height();
664
+ this.elw = m.container.width();
665
+ s.right.css('height',this.elh+'px');
666
+ s.left.css('height',this.elh+'px');
667
+ },
668
+ filter: function(b,ord,sel){
669
+
670
+ if (!sel.active) return b;
671
+
672
+ var t = this,
673
+ s = t.shades;
674
+
675
+ s.top.css({
676
+ left: Math.round(b.x)+'px',
677
+ width: Math.round(b.w)+'px',
678
+ height: Math.round(b.y)+'px'
679
+ });
680
+ s.bottom.css({
681
+ top: Math.round(b.y2)+'px',
682
+ left: Math.round(b.x)+'px',
683
+ width: Math.round(b.w)+'px',
684
+ height: (t.elh-Math.round(b.y2))+'px'
685
+ });
686
+ s.right.css({
687
+ left: Math.round(b.x2)+'px',
688
+ width: (t.elw-Math.round(b.x2))+'px'
689
+ });
690
+ s.left.css({
691
+ width: Math.round(b.x)+'px'
692
+ });
693
+
694
+ if (!t.visible) {
695
+ t.container.show();
696
+ t.visible = true;
697
+ }
698
+
699
+ return b;
700
+ }
701
+ });
702
+ Jcrop.registerFilter('shader',ShadeFilter);
703
+
704
+
705
+ /**
706
+ * CanvasAnimator
707
+ * manages smooth cropping animation
708
+ *
709
+ * This object is called internally to manage animation.
710
+ * An in-memory div is animated and a progress callback
711
+ * is used to update the selection coordinates of the
712
+ * visible selection in realtime.
713
+ */
714
+ var CanvasAnimator = function(stage){
715
+ this.stage = stage;
716
+ this.core = stage.core;
717
+ this.cloneStagePosition();
718
+ };
719
+
720
+ CanvasAnimator.prototype = {
721
+
722
+ cloneStagePosition: function(){
723
+ var s = this.stage;
724
+ this.angle = s.angle;
725
+ this.scale = s.scale;
726
+ this.offset = s.offset;
727
+ },
728
+
729
+ getElement: function(){
730
+ var s = this.stage;
731
+
732
+ return $('<div />')
733
+ .css({
734
+ position: 'absolute',
735
+ top: s.offset[0]+'px',
736
+ left: s.offset[1]+'px',
737
+ width: s.angle+'px',
738
+ height: s.scale+'px'
739
+ });
740
+ },
741
+
742
+ animate: function(cb){
743
+ var t = this;
744
+
745
+ this.scale = this.stage.boundScale(this.scale);
746
+ t.stage.triggerEvent('croprotstart');
747
+
748
+ t.getElement().animate({
749
+ top: t.offset[0]+'px',
750
+ left: t.offset[1]+'px',
751
+ width: t.angle+'px',
752
+ height: t.scale+'px'
753
+ },{
754
+ easing: t.core.opt.animEasing,
755
+ duration: t.core.opt.animDuration,
756
+ complete: function(){
757
+ t.stage.triggerEvent('croprotend');
758
+ (typeof cb == 'function') && cb.call(this);
759
+ },
760
+ progress: function(anim){
761
+ var props = {}, i, tw = anim.tweens;
762
+
763
+ for(i=0;i<tw.length;i++){
764
+ props[tw[i].prop] = tw[i].now; }
765
+
766
+ t.stage.setAngle(props.width)
767
+ .setScale(props.height)
768
+ .setOffset(props.top,props.left)
769
+ .redraw();
770
+ }
771
+ });
772
+ }
773
+
774
+ };
775
+ Jcrop.stage.Canvas.prototype.getAnimator = function(){
776
+ return new CanvasAnimator(this);
777
+ };
778
+ Jcrop.registerComponent('CanvasAnimator',CanvasAnimator);
779
+
780
+
781
+ /**
782
+ * CropAnimator
783
+ * manages smooth cropping animation
784
+ *
785
+ * This object is called internally to manage animation.
786
+ * An in-memory div is animated and a progress callback
787
+ * is used to update the selection coordinates of the
788
+ * visible selection in realtime.
789
+ */
790
+ // var CropAnimator = function(selection){{{
791
+ var CropAnimator = function(selection){
792
+ this.selection = selection;
793
+ this.core = selection.core;
794
+ };
795
+ // }}}
796
+
797
+ CropAnimator.prototype = {
798
+
799
+ getElement: function(){
800
+ var b = this.selection.get();
801
+
802
+ return $('<div />')
803
+ .css({
804
+ position: 'absolute',
805
+ top: b.y+'px',
806
+ left: b.x+'px',
807
+ width: b.w+'px',
808
+ height: b.h+'px'
809
+ });
810
+ },
811
+
812
+ animate: function(x,y,w,h,cb){
813
+ var t = this;
814
+
815
+ t.selection.allowResize(false);
816
+
817
+ t.getElement().animate({
818
+ top: y+'px',
819
+ left: x+'px',
820
+ width: w+'px',
821
+ height: h+'px'
822
+ },{
823
+ easing: t.core.opt.animEasing,
824
+ duration: t.core.opt.animDuration,
825
+ complete: function(){
826
+ t.selection.allowResize(true);
827
+ cb && cb.call(this);
828
+ },
829
+ progress: function(anim){
830
+ var props = {}, i, tw = anim.tweens;
831
+
832
+ for(i=0;i<tw.length;i++){
833
+ props[tw[i].prop] = tw[i].now; }
834
+
835
+ var b = {
836
+ x: parseInt(props.left),
837
+ y: parseInt(props.top),
838
+ w: parseInt(props.width),
839
+ h: parseInt(props.height)
840
+ };
841
+
842
+ b.x2 = b.x + b.w;
843
+ b.y2 = b.y + b.h;
844
+
845
+ t.selection.updateRaw(b,'se');
846
+ }
847
+ });
848
+ }
849
+
850
+ };
851
+ Jcrop.registerComponent('Animator',CropAnimator);
852
+
853
+
854
+ /**
855
+ * DragState
856
+ * an object that handles dragging events
857
+ *
858
+ * This object is used by the built-in selection object to
859
+ * track a dragging operation on a selection
860
+ */
861
+ // var DragState = function(e,selection,ord){{{
862
+ var DragState = function(e,selection,ord){
863
+ var t = this;
864
+
865
+ t.x = e.pageX;
866
+ t.y = e.pageY;
867
+
868
+ t.selection = selection;
869
+ t.eventTarget = selection.core.opt.dragEventTarget;
870
+ t.orig = selection.get();
871
+
872
+ selection.callFilterFunction('refresh');
873
+
874
+ var p = selection.core.container.position();
875
+ t.elx = p.left;
876
+ t.ely = p.top;
877
+
878
+ t.offsetx = 0;
879
+ t.offsety = 0;
880
+ t.ord = ord;
881
+ t.opposite = t.getOppositeCornerOffset();
882
+
883
+ t.initEvents(e);
884
+
885
+ };
886
+ // }}}
887
+
888
+ DragState.prototype = {
889
+ // getOppositeCornerOffset: function(){{{
890
+ // Calculate relative offset of locked corner
891
+ getOppositeCornerOffset: function(){
892
+
893
+ var o = this.orig;
894
+ var relx = this.x - this.elx - o.x;
895
+ var rely = this.y - this.ely - o.y;
896
+
897
+ switch(this.ord){
898
+ case 'nw':
899
+ case 'w':
900
+ return [ o.w - relx, o.h - rely ];
901
+ return [ o.x + o.w, o.y + o.h ];
902
+
903
+ case 'sw':
904
+ return [ o.w - relx, -rely ];
905
+ return [ o.x + o.w, o.y ];
906
+
907
+ case 'se':
908
+ case 's':
909
+ case 'e':
910
+ return [ -relx, -rely ];
911
+ return [ o.x, o.y ];
912
+
913
+ case 'ne':
914
+ case 'n':
915
+ return [ -relx, o.h - rely ];
916
+ return [ o.w, o.y + o.h ];
917
+ }
918
+
919
+ return [ null, null ];
920
+ },
921
+ // }}}
922
+ // initEvents: function(e){{{
923
+ initEvents: function(e){
924
+ $(this.eventTarget)
925
+ .on('mousemove.jcrop',this.createDragHandler())
926
+ .on('mouseup.jcrop',this.createStopHandler());
927
+ },
928
+ // }}}
929
+ // dragEvent: function(e){{{
930
+ dragEvent: function(e){
931
+ this.offsetx = e.pageX - this.x;
932
+ this.offsety = e.pageY - this.y;
933
+ this.selection.updateRaw(this.getBox(),this.ord);
934
+ },
935
+ // }}}
936
+ // endDragEvent: function(e){{{
937
+ endDragEvent: function(e){
938
+ var sel = this.selection;
939
+ sel.core.container.removeClass('jcrop-dragging');
940
+ sel.element.trigger('cropend',[sel,sel.core.unscale(sel.get())]);
941
+ sel.focus();
942
+ },
943
+ // }}}
944
+ // createStopHandler: function(){{{
945
+ createStopHandler: function(){
946
+ var t = this;
947
+ return function(e){
948
+ $(t.eventTarget).off('.jcrop');
949
+ t.endDragEvent(e);
950
+ return false;
951
+ };
952
+ },
953
+ // }}}
954
+ // createDragHandler: function(){{{
955
+ createDragHandler: function(){
956
+ var t = this;
957
+ return function(e){
958
+ t.dragEvent(e);
959
+ return false;
960
+ };
961
+ },
962
+ // }}}
963
+ //update: function(x,y){{{
964
+ update: function(x,y){
965
+ var t = this;
966
+ t.offsetx = x - t.x;
967
+ t.offsety = y - t.y;
968
+ },
969
+ //}}}
970
+ //resultWrap: function(d){{{
971
+ resultWrap: function(d){
972
+ var b = {
973
+ x: Math.min(d[0],d[2]),
974
+ y: Math.min(d[1],d[3]),
975
+ x2: Math.max(d[0],d[2]),
976
+ y2: Math.max(d[1],d[3])
977
+ };
978
+
979
+ b.w = b.x2 - b.x;
980
+ b.h = b.y2 - b.y;
981
+
982
+ return b;
983
+ },
984
+ //}}}
985
+ //getBox: function(){{{
986
+ getBox: function(){
987
+ var t = this;
988
+ var o = t.orig;
989
+ var _c = { x2: o.x + o.w, y2: o.y + o.h };
990
+ switch(t.ord){
991
+ case 'n': return t.resultWrap([ o.x, t.offsety + o.y, _c.x2, _c.y2 ]);
992
+ case 's': return t.resultWrap([ o.x, o.y, _c.x2, t.offsety + _c.y2 ]);
993
+ case 'e': return t.resultWrap([ o.x, o.y, t.offsetx + _c.x2, _c.y2 ]);
994
+ case 'w': return t.resultWrap([ o.x + t.offsetx, o.y, _c.x2, _c.y2 ]);
995
+ case 'sw': return t.resultWrap([ t.offsetx + o.x, o.y, _c.x2, t.offsety + _c.y2 ]);
996
+ case 'se': return t.resultWrap([ o.x, o.y, t.offsetx + _c.x2, t.offsety + _c.y2 ]);
997
+ case 'ne': return t.resultWrap([ o.x, t.offsety + o.y, t.offsetx + _c.x2, _c.y2 ]);
998
+ case 'nw': return t.resultWrap([ t.offsetx + o.x, t.offsety + o.y, _c.x2, _c.y2 ]);
999
+ case 'move':
1000
+ _c.nx = o.x + t.offsetx;
1001
+ _c.ny = o.y + t.offsety;
1002
+ return t.resultWrap([ _c.nx, _c.ny, _c.nx + o.w, _c.ny + o.h ]);
1003
+ }
1004
+ }
1005
+ //}}}
1006
+ };
1007
+ Jcrop.registerComponent('DragState',DragState);
1008
+
1009
+
1010
+ /**
1011
+ * EventManager
1012
+ * provides internal event support
1013
+ */
1014
+ var EventManager = function(core){
1015
+ this.core = core;
1016
+ };
1017
+ EventManager.prototype = {
1018
+ on: function(n,cb){ $(this).on(n,cb); },
1019
+ off: function(n){ $(this).off(n); },
1020
+ trigger: function(n){ $(this).trigger(n); }
1021
+ };
1022
+ Jcrop.registerComponent('EventManager',EventManager);
1023
+
1024
+
1025
+ /**
1026
+ * Image Loader
1027
+ * Reliably pre-loads images
1028
+ */
1029
+ // var ImageLoader = function(src,element,cb){{{
1030
+ var ImageLoader = function(src,element,cb){
1031
+ this.src = src;
1032
+ if (!element) element = new Image;
1033
+ this.element = element;
1034
+ this.callback = cb;
1035
+ this.load();
1036
+ };
1037
+ // }}}
1038
+
1039
+ $.extend(ImageLoader,{
1040
+ // attach: function(el,cb){{{
1041
+ attach: function(el,cb){
1042
+ return new ImageLoader(el.src,el,cb);
1043
+ },
1044
+ // }}}
1045
+ // prototype: {{{
1046
+ prototype: {
1047
+ getDimensions: function(){
1048
+ var el = this.element;
1049
+
1050
+ if (el.naturalWidth)
1051
+ return [ el.naturalWidth, el. naturalHeight ];
1052
+
1053
+ if (el.width)
1054
+ return [ el.width, el.height ];
1055
+
1056
+ return null;
1057
+ },
1058
+ fireCallback: function(){
1059
+ this.element.onload = null;
1060
+ if (typeof this.callback == 'function')
1061
+ this.callback.apply(this,this.getDimensions());
1062
+ },
1063
+ isLoaded: function(){
1064
+ return this.element.complete;
1065
+ },
1066
+ load: function(){
1067
+ var t = this;
1068
+ var el = t.element;
1069
+
1070
+ el.src = t.src;
1071
+
1072
+ if (t.isLoaded()) t.fireCallback();
1073
+ else t.element.onload = function(e){
1074
+ t.fireCallback();
1075
+ };
1076
+ }
1077
+ }
1078
+ // }}}
1079
+ });
1080
+ Jcrop.registerComponent('ImageLoader',ImageLoader);
1081
+
1082
+
1083
+ /**
1084
+ * JcropTouch
1085
+ * Detects and enables mobile touch support
1086
+ */
1087
+ // var JcropTouch = function(core){{{
1088
+ var JcropTouch = function(core){
1089
+ this.core = core;
1090
+ this.init();
1091
+ };
1092
+ // }}}
1093
+
1094
+ $.extend(JcropTouch,{
1095
+ // support: function(){{{
1096
+ support: function(){
1097
+ if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch)
1098
+ return true;
1099
+ },
1100
+ // }}}
1101
+ prototype: {
1102
+ // init: function(){{{
1103
+ init: function(){
1104
+ var t = this,
1105
+ p = $.Jcrop.component.DragState.prototype;
1106
+
1107
+ // A bit of an ugly hack to make sure we modify prototype
1108
+ // only once, store a key on the prototype
1109
+ if (!p.touch) {
1110
+ t.initEvents();
1111
+ t.shimDragState();
1112
+ t.shimStageDrag();
1113
+ p.touch = true;
1114
+ }
1115
+ },
1116
+ // }}}
1117
+ // shimDragState: function(){{{
1118
+ shimDragState: function(){
1119
+ var t = this;
1120
+ $.Jcrop.component.DragState.prototype.initEvents = function(e){
1121
+
1122
+ // Attach subsequent drag event handlers based on initial
1123
+ // event type - avoids collecting "pseudo-mouse" events
1124
+ // generated by some mobile browsers in some circumstances
1125
+ if (e.type.substr(0,5) == 'touch') {
1126
+
1127
+ $(this.eventTarget)
1128
+ .on('touchmove.jcrop.jcrop-touch',t.dragWrap(this.createDragHandler()))
1129
+ .on('touchend.jcrop.jcrop-touch',this.createStopHandler());
1130
+
1131
+ }
1132
+
1133
+ // For other events, use the mouse handlers that
1134
+ // the default DragState.initEvents() method sets...
1135
+ else {
1136
+
1137
+ $(this.eventTarget)
1138
+ .on('mousemove.jcrop',this.createDragHandler())
1139
+ .on('mouseup.jcrop',this.createStopHandler());
1140
+
1141
+ }
1142
+
1143
+ };
1144
+ },
1145
+ // }}}
1146
+ // shimStageDrag: function(){{{
1147
+ shimStageDrag: function(){
1148
+ this.core.container
1149
+ .addClass('jcrop-touch')
1150
+ .on('touchstart.jcrop.jcrop-stage',this.dragWrap(this.core.ui.manager.startDragHandler()));
1151
+ },
1152
+ // }}}
1153
+ // dragWrap: function(cb){{{
1154
+ dragWrap: function(cb){
1155
+ return function(e){
1156
+ e.preventDefault();
1157
+ e.stopPropagation();
1158
+ if (e.type.substr(0,5) == 'touch') {
1159
+ e.pageX = e.originalEvent.changedTouches[0].pageX;
1160
+ e.pageY = e.originalEvent.changedTouches[0].pageY;
1161
+ return cb(e);
1162
+ }
1163
+ return false;
1164
+ };
1165
+ },
1166
+ // }}}
1167
+ // initEvents: function(){{{
1168
+ initEvents: function(){
1169
+ var t = this, c = t.core;
1170
+
1171
+ c.container.on(
1172
+ 'touchstart.jcrop.jcrop-touch',
1173
+ '.'+c.opt.css_drag,
1174
+ t.dragWrap(c.startDrag())
1175
+ );
1176
+ }
1177
+ // }}}
1178
+ }
1179
+ });
1180
+ Jcrop.registerComponent('Touch',JcropTouch);
1181
+
1182
+
1183
+ /**
1184
+ * KeyWatcher
1185
+ * provides keyboard support
1186
+ */
1187
+ // var KeyWatcher = function(core){{{
1188
+ var KeyWatcher = function(core){
1189
+ this.core = core;
1190
+ this.init();
1191
+ };
1192
+ // }}}
1193
+ $.extend(KeyWatcher,{
1194
+ // defaults: {{{
1195
+ defaults: {
1196
+ eventName: 'keydown.jcrop',
1197
+ passthru: [ 9 ],
1198
+ debug: false
1199
+ },
1200
+ // }}}
1201
+ prototype: {
1202
+ // init: function(){{{
1203
+ init: function(){
1204
+ $.extend(this,KeyWatcher.defaults);
1205
+ this.enable();
1206
+ },
1207
+ // }}}
1208
+ // disable: function(){{{
1209
+ disable: function(){
1210
+ this.core.container.off(this.eventName);
1211
+ },
1212
+ // }}}
1213
+ // enable: function(){{{
1214
+ enable: function(){
1215
+ var t = this, m = t.core;
1216
+ m.container.on(t.eventName,function(e){
1217
+ var nudge = e.shiftKey? 16: 2;
1218
+
1219
+ if ($.inArray(e.keyCode,t.passthru) >= 0)
1220
+ return true;
1221
+
1222
+ switch(e.keyCode){
1223
+ case 37: m.nudge(-nudge,0); break;
1224
+ case 38: m.nudge(0,-nudge); break;
1225
+ case 39: m.nudge(nudge,0); break;
1226
+ case 40: m.nudge(0,nudge); break;
1227
+
1228
+ case 46:
1229
+ case 8:
1230
+ m.requestDelete();
1231
+ return false;
1232
+ break;
1233
+
1234
+ default:
1235
+ if (t.debug) console.log('keycode: ' + e.keyCode);
1236
+ break;
1237
+ }
1238
+
1239
+ if (!e.metaKey && !e.ctrlKey)
1240
+ e.preventDefault();
1241
+ });
1242
+ }
1243
+ // }}}
1244
+ }
1245
+ });
1246
+ Jcrop.registerComponent('Keyboard',KeyWatcher);
1247
+
1248
+
1249
+ /**
1250
+ * Selection
1251
+ * Built-in selection object
1252
+ */
1253
+ var Selection = function(){};
1254
+
1255
+ $.extend(Selection,{
1256
+ // defaults: {{{
1257
+ defaults: {
1258
+ minSize: [ 8, 8 ],
1259
+ maxSize: [ 0, 0 ],
1260
+ aspectRatio: 0,
1261
+ edge: { n: 0, s: 0, e: 0, w: 0 },
1262
+ bgColor: null,
1263
+ bgOpacity: null,
1264
+ last: null,
1265
+
1266
+ state: null,
1267
+ active: true,
1268
+ linked: true,
1269
+ canDelete: true,
1270
+ canDrag: true,
1271
+ canResize: true,
1272
+ canSelect: true
1273
+ },
1274
+ // }}}
1275
+ prototype: {
1276
+ // init: function(core){{{
1277
+ init: function(core){
1278
+ this.core = core;
1279
+ this.startup();
1280
+ this.linked = this.core.opt.linked;
1281
+ this.attach();
1282
+ this.setOptions(this.core.opt);
1283
+ core.container.trigger('cropcreate',[this]);
1284
+ },
1285
+ // }}}
1286
+ // attach: function(){{{
1287
+ attach: function(){
1288
+ // For extending init() sequence
1289
+ },
1290
+ // }}}
1291
+ // startup: function(){{{
1292
+ startup: function(){
1293
+ var t = this, o = t.core.opt;
1294
+ $.extend(t,Selection.defaults);
1295
+ t.filter = t.core.getDefaultFilters();
1296
+
1297
+ t.element = $('<div />').addClass(o.css_selection).data({ selection: t });
1298
+ t.frame = $('<button />').addClass(o.css_button).data('ord','move').attr('type','button');
1299
+ t.element.append(t.frame).appendTo(t.core.container);
1300
+
1301
+ // IE background/draggable hack
1302
+ if (t.core.opt.is_msie) t.frame.css({
1303
+ opacity: 0,
1304
+ backgroundColor: 'white'
1305
+ });
1306
+
1307
+ t.insertElements();
1308
+
1309
+ // Bind focus and blur events for this selection
1310
+ t.frame.on('focus.jcrop',function(e){
1311
+ t.core.setSelection(t);
1312
+ t.element.trigger('cropfocus',t);
1313
+ t.element.addClass('jcrop-focus');
1314
+ }).on('blur.jcrop',function(e){
1315
+ t.element.removeClass('jcrop-focus');
1316
+ t.element.trigger('cropblur',t);
1317
+ });
1318
+ },
1319
+ // }}}
1320
+ // propagate: [{{{
1321
+ propagate: [
1322
+ 'canDelete', 'canDrag', 'canResize', 'canSelect',
1323
+ 'minSize', 'maxSize', 'aspectRatio', 'edge'
1324
+ ],
1325
+ // }}}
1326
+ // setOptions: function(opt){{{
1327
+ setOptions: function(opt){
1328
+ Jcrop.propagate(this.propagate,opt,this);
1329
+ this.refresh();
1330
+ return this;
1331
+ },
1332
+ // }}}
1333
+ // refresh: function(){{{
1334
+ refresh: function(){
1335
+ this.allowResize();
1336
+ this.allowDrag();
1337
+ this.allowSelect();
1338
+ this.callFilterFunction('refresh');
1339
+ this.updateRaw(this.get(),'se');
1340
+ },
1341
+ // }}}
1342
+ // callFilterFunction: function(f,args){{{
1343
+ callFilterFunction: function(f,args){
1344
+ for(var i=0;i<this.filter.length;i++)
1345
+ if (this.filter[i][f]) this.filter[i][f](this);
1346
+ return this;
1347
+ },
1348
+ // }}}
1349
+ //addFilter: function(filter){{{
1350
+ addFilter: function(filter){
1351
+ filter.core = this.core;
1352
+ if (!this.hasFilter(filter)) {
1353
+ this.filter.push(filter);
1354
+ this.sortFilters();
1355
+ if (filter.init) filter.init();
1356
+ this.refresh();
1357
+ }
1358
+ },
1359
+ //}}}
1360
+ // hasFilter: function(filter){{{
1361
+ hasFilter: function(filter){
1362
+ var i, f = this.filter, n = [];
1363
+ for(i=0;i<f.length;i++) if (f[i] === filter) return true;
1364
+ },
1365
+ // }}}
1366
+ // sortFilters: function(){{{
1367
+ sortFilters: function(){
1368
+ this.filter.sort(
1369
+ function(x,y){ return x.priority - y.priority; }
1370
+ );
1371
+ },
1372
+ // }}}
1373
+ //clearFilters: function(){{{
1374
+ clearFilters: function(){
1375
+ var i, f = this.filter;
1376
+
1377
+ for(var i=0;i<f.length;i++)
1378
+ if (f[i].destroy) f[i].destroy();
1379
+
1380
+ this.filter = [];
1381
+ },
1382
+ //}}}
1383
+ // removeFiltersByTag: function(tag){{{
1384
+ removeFilter: function(tag){
1385
+ var i, f = this.filter, n = [];
1386
+
1387
+ for(var i=0;i<f.length;i++)
1388
+ if ((f[i].tag && (f[i].tag == tag)) || (tag === f[i])){
1389
+ if (f[i].destroy) f[i].destroy();
1390
+ }
1391
+ else n.push(f[i]);
1392
+
1393
+ this.filter = n;
1394
+ },
1395
+ // }}}
1396
+ // runFilters: function(b,ord){{{
1397
+ runFilters: function(b,ord){
1398
+ for(var i=0;i<this.filter.length;i++)
1399
+ b = this.filter[i].filter(b,ord,this);
1400
+ return b;
1401
+ },
1402
+ // }}}
1403
+ //endDrag: function(){{{
1404
+ endDrag: function(){
1405
+ if (this.state) {
1406
+ $(document.body).off('.jcrop');
1407
+ this.focus();
1408
+ this.state = null;
1409
+ }
1410
+ },
1411
+ //}}}
1412
+ // startDrag: function(e,ord){{{
1413
+ startDrag: function(e,ord){
1414
+ var t = this;
1415
+ var m = t.core;
1416
+
1417
+ ord = ord || $(e.target).data('ord');
1418
+
1419
+ this.focus();
1420
+
1421
+ if ((ord == 'move') && t.element.hasClass(t.core.opt.css_nodrag))
1422
+ return false;
1423
+
1424
+ this.state = new Jcrop.component.DragState(e,this,ord);
1425
+ return false;
1426
+ },
1427
+ // }}}
1428
+ // allowSelect: function(v){{{
1429
+ allowSelect: function(v){
1430
+ if (v === undefined) v = this.canSelect;
1431
+
1432
+ if (v && this.canSelect) this.frame.attr('disabled',false);
1433
+ else this.frame.attr('disabled','disabled');
1434
+
1435
+ return this;
1436
+ },
1437
+ // }}}
1438
+ // allowDrag: function(v){{{
1439
+ allowDrag: function(v){
1440
+ var t = this, o = t.core.opt;
1441
+ if (v == undefined) v = t.canDrag;
1442
+
1443
+ if (v && t.canDrag) t.element.removeClass(o.css_nodrag);
1444
+ else t.element.addClass(o.css_nodrag);
1445
+
1446
+ return this;
1447
+ },
1448
+ // }}}
1449
+ // allowResize: function(v){{{
1450
+ allowResize: function(v){
1451
+ var t = this, o = t.core.opt;
1452
+ if (v == undefined) v = t.canResize;
1453
+
1454
+ if (v && t.canResize) t.element.removeClass(o.css_noresize);
1455
+ else t.element.addClass(o.css_noresize);
1456
+
1457
+ return this;
1458
+ },
1459
+ // }}}
1460
+ // remove: function(){{{
1461
+ remove: function(){
1462
+ this.element.trigger('cropremove',this);
1463
+ this.element.remove();
1464
+ },
1465
+ // }}}
1466
+ // toBack: function(){{{
1467
+ toBack: function(){
1468
+ this.active = false;
1469
+ this.element.removeClass('jcrop-current jcrop-focus');
1470
+ },
1471
+ // }}}
1472
+ // toFront: function(){{{
1473
+ toFront: function(){
1474
+ this.active = true;
1475
+ this.element.addClass('jcrop-current');
1476
+ this.callFilterFunction('refresh');
1477
+ this.refresh();
1478
+ },
1479
+ // }}}
1480
+ // redraw: function(b){{{
1481
+ redraw: function(b){
1482
+ this.moveTo(b.x,b.y);
1483
+ this.resize(b.w,b.h);
1484
+ this.last = b;
1485
+ return this;
1486
+ },
1487
+ // }}}
1488
+ // update: function(b,ord){{{
1489
+ update: function(b,ord){
1490
+ return this.updateRaw(this.core.scale(b),ord);
1491
+ },
1492
+ // }}}
1493
+ // update: function(b,ord){{{
1494
+ updateRaw: function(b,ord){
1495
+ b = this.runFilters(b,ord);
1496
+ this.redraw(b);
1497
+ this.element.trigger('cropmove',[this,this.core.unscale(b)]);
1498
+ return this;
1499
+ },
1500
+ // }}}
1501
+ // animateTo: function(box,cb){{{
1502
+ animateTo: function(box,cb){
1503
+ var ca = new Jcrop.component.Animator(this),
1504
+ b = this.core.scale(Jcrop.wrapFromXywh(box));
1505
+
1506
+ ca.animate(b.x,b.y,b.w,b.h,cb);
1507
+ },
1508
+ // }}}
1509
+ // center: function(instant){{{
1510
+ center: function(instant){
1511
+ var b = this.get(), m = this.core;
1512
+ var elw = m.container.width(), elh = m.container.height();
1513
+ var box = [ (elw-b.w)/2, (elh-b.h)/2, b.w, b.h ];
1514
+ return this[instant?'setSelect':'animateTo'](box);
1515
+ },
1516
+ // }}}
1517
+ //createElement: function(type,ord){{{
1518
+ createElement: function(type,ord){
1519
+ return $('<div />').addClass(type+' ord-'+ord).data('ord',ord);
1520
+ },
1521
+ //}}}
1522
+ //moveTo: function(x,y){{{
1523
+ moveTo: function(x,y){
1524
+ this.element.css({top: y+'px', left: x+'px'});
1525
+ },
1526
+ //}}}
1527
+ // blur: function(){{{
1528
+ blur: function(){
1529
+ this.element.blur();
1530
+ return this;
1531
+ },
1532
+ // }}}
1533
+ // focus: function(){{{
1534
+ focus: function(){
1535
+ this.core.setSelection(this);
1536
+ this.frame.focus();
1537
+ return this;
1538
+ },
1539
+ // }}}
1540
+ //resize: function(w,h){{{
1541
+ resize: function(w,h){
1542
+ this.element.css({width: w+'px', height: h+'px'});
1543
+ },
1544
+ //}}}
1545
+ //get: function(){{{
1546
+ get: function(){
1547
+ var b = this.element,
1548
+ o = b.position(),
1549
+ w = b.width(),
1550
+ h = b.height(),
1551
+ rv = { x: o.left, y: o.top };
1552
+
1553
+ rv.x2 = rv.x + w;
1554
+ rv.y2 = rv.y + h;
1555
+ rv.w = w;
1556
+ rv.h = h;
1557
+
1558
+ return rv;
1559
+ },
1560
+ //}}}
1561
+ //insertElements: function(){{{
1562
+ insertElements: function(){
1563
+ var t = this, i,
1564
+ m = t.core,
1565
+ fr = t.element,
1566
+ o = t.core.opt,
1567
+ b = o.borders,
1568
+ h = o.handles,
1569
+ d = o.dragbars;
1570
+
1571
+ for(i=0; i<d.length; i++)
1572
+ fr.append(t.createElement(o.css_dragbars,d[i]));
1573
+
1574
+ for(i=0; i<h.length; i++)
1575
+ fr.append(t.createElement(o.css_handles,h[i]));
1576
+
1577
+ for(i=0; i<b.length; i++)
1578
+ fr.append(t.createElement(o.css_borders,b[i]));
1579
+ }
1580
+ //}}}
1581
+ }
1582
+ });
1583
+ Jcrop.registerComponent('Selection',Selection);
1584
+
1585
+
1586
+ /**
1587
+ * StageDrag
1588
+ * Facilitates dragging
1589
+ */
1590
+ // var StageDrag = function(manager,opt){{{
1591
+ var StageDrag = function(manager,opt){
1592
+ $.extend(this,StageDrag.defaults,opt || {});
1593
+ this.manager = manager;
1594
+ this.core = manager.core;
1595
+ };
1596
+ // }}}
1597
+ // StageDrag.defaults = {{{
1598
+ StageDrag.defaults = {
1599
+ offset: [ -8, -8 ],
1600
+ active: true,
1601
+ minsize: [ 20, 20 ]
1602
+ };
1603
+ // }}}
1604
+
1605
+ $.extend(StageDrag.prototype,{
1606
+ // start: function(e){{{
1607
+ start: function(e){
1608
+ var c = this.core;
1609
+
1610
+ // Do nothing if allowSelect is off
1611
+ if (!c.opt.allowSelect) return;
1612
+
1613
+ // Also do nothing if we can't draw any more selections
1614
+ if (c.opt.multi && c.opt.multiMax && (c.ui.multi.length >= c.opt.multiMax)) return false;
1615
+
1616
+ // calculate a few variables for this drag operation
1617
+ var o = $(e.currentTarget).offset();
1618
+ var origx = e.pageX - o.left + this.offset[0];
1619
+ var origy = e.pageY - o.top + this.offset[1];
1620
+ var m = c.ui.multi;
1621
+
1622
+ // Determine newly dragged crop behavior if multi disabled
1623
+ if (!c.opt.multi) {
1624
+ // For multiCleaanup true, remove all existing selections
1625
+ if (c.opt.multiCleanup){
1626
+ for(var i=0;i<m.length;i++) m[i].remove();
1627
+ c.ui.multi = [];
1628
+ }
1629
+ // If not, only remove the currently active selection
1630
+ else {
1631
+ c.removeSelection(c.ui.selection);
1632
+ }
1633
+ }
1634
+
1635
+ c.container.addClass('jcrop-dragging');
1636
+
1637
+ // Create the new selection
1638
+ var sel = c.newSelection()
1639
+ // and position it
1640
+ .updateRaw(Jcrop.wrapFromXywh([origx,origy,1,1]));
1641
+
1642
+ sel.element.trigger('cropstart',[sel,this.core.unscale(sel.get())]);
1643
+
1644
+ return sel.startDrag(e,'se');
1645
+ },
1646
+ // }}}
1647
+ // end: function(x,y){{{
1648
+ end: function(x,y){
1649
+ this.drag(x,y);
1650
+ var b = this.sel.get();
1651
+
1652
+ this.core.container.removeClass('jcrop-dragging');
1653
+
1654
+ if ((b.w < this.minsize[0]) || (b.h < this.minsize[1]))
1655
+ this.core.requestDelete();
1656
+
1657
+ else this.sel.focus();
1658
+ }
1659
+ // }}}
1660
+ });
1661
+ Jcrop.registerComponent('StageDrag',StageDrag);
1662
+
1663
+
1664
+ /**
1665
+ * StageManager
1666
+ * Provides basic stage-specific functionality
1667
+ */
1668
+ // var StageManager = function(core){{{
1669
+ var StageManager = function(core){
1670
+ this.core = core;
1671
+ this.ui = core.ui;
1672
+ this.init();
1673
+ };
1674
+ // }}}
1675
+
1676
+ $.extend(StageManager.prototype,{
1677
+ // init: function(){{{
1678
+ init: function(){
1679
+ this.setupEvents();
1680
+ this.dragger = new StageDrag(this);
1681
+ },
1682
+ // }}}
1683
+ // tellConfigUpdate: function(options){{{
1684
+ tellConfigUpdate: function(options){
1685
+ for(var i=0,m=this.ui.multi,l=m.length;i<l;i++)
1686
+ if (m[i].setOptions && (m[i].linked || (this.core.opt.linkCurrent && m[i] == this.ui.selection)))
1687
+ m[i].setOptions(options);
1688
+ },
1689
+ // }}}
1690
+ // startDragHandler: function(){{{
1691
+ startDragHandler: function(){
1692
+ var t = this;
1693
+ return function(e){
1694
+ if (!e.button || t.core.opt.is_ie_lt9) return t.dragger.start(e);
1695
+ };
1696
+ },
1697
+ // }}}
1698
+ // removeEvents: function(){{{
1699
+ removeEvents: function(){
1700
+ this.core.event.off('.jcrop-stage');
1701
+ this.core.container.off('.jcrop-stage');
1702
+ },
1703
+ // }}}
1704
+ // shimLegacyHandlers: function(options){{{
1705
+ // This method uses the legacyHandlers configuration object to
1706
+ // gracefully wrap old-style Jcrop events with new ones
1707
+ shimLegacyHandlers: function(options){
1708
+ var _x = {}, core = this.core, tmp;
1709
+
1710
+ $.each(core.opt.legacyHandlers,function(k,i){
1711
+ if (k in options) {
1712
+ tmp = options[k];
1713
+ core.container.off('.jcrop-'+k)
1714
+ .on(i+'.jcrop.jcrop-'+k,function(e,s,c){
1715
+ tmp.call(core,c);
1716
+ });
1717
+ delete options[k];
1718
+ }
1719
+ });
1720
+ },
1721
+ // }}}
1722
+ // setupEvents: function(){{{
1723
+ setupEvents: function(){
1724
+ var t = this, c = t.core;
1725
+
1726
+ c.event.on('configupdate.jcrop-stage',function(e){
1727
+ t.shimLegacyHandlers(c.opt);
1728
+ t.tellConfigUpdate(c.opt)
1729
+ c.container.trigger('cropconfig',[c,c.opt]);
1730
+ });
1731
+
1732
+ this.core.container
1733
+ .on('mousedown.jcrop.jcrop-stage',this.startDragHandler());
1734
+ }
1735
+ // }}}
1736
+ });
1737
+ Jcrop.registerComponent('StageManager',StageManager);
1738
+
1739
+
1740
+ var Thumbnailer = function(){
1741
+ };
1742
+
1743
+ $.extend(Thumbnailer,{
1744
+ defaults: {
1745
+ // Set to a specific Selection object
1746
+ // If this value is set, the preview will only track that Selection
1747
+ selection: null,
1748
+
1749
+ fading: true,
1750
+ fadeDelay: 1000,
1751
+ fadeDuration: 1000,
1752
+ autoHide: false,
1753
+ width: 80,
1754
+ height: 80,
1755
+ _hiding: null
1756
+ },
1757
+
1758
+ prototype: {
1759
+ recopyCanvas: function(){
1760
+ var s = this.core.ui.stage, cxt = s.context;
1761
+ this.context.putImageData(cxt.getImageData(0,0,s.canvas.width,s.canvas.height),0,0);
1762
+ },
1763
+ init: function(core,options){
1764
+ var t = this;
1765
+ this.core = core;
1766
+ $.extend(this,Thumbnailer.defaults,options);
1767
+ t.initEvents();
1768
+ t.refresh();
1769
+ t.insertElements();
1770
+ if (t.selection) {
1771
+ t.renderSelection(t.selection);
1772
+ t.selectionTarget = t.selection.element[0];
1773
+ } else if (t.core.ui.selection) {
1774
+ t.renderSelection(t.core.ui.selection);
1775
+ }
1776
+
1777
+ if (t.core.ui.stage.canvas) {
1778
+ t.context = t.preview[0].getContext('2d');
1779
+ t.core.container.on('cropredraw',function(e){
1780
+ t.recopyCanvas();
1781
+ t.refresh();
1782
+ });
1783
+ }
1784
+ },
1785
+ updateImage: function(imgel){
1786
+ this.preview.remove();
1787
+ this.preview = $($.Jcrop.imageClone(imgel));
1788
+ this.element.append(this.preview);
1789
+ this.refresh();
1790
+ return this;
1791
+ },
1792
+ insertElements: function(){
1793
+ this.preview = $($.Jcrop.imageClone(this.core.ui.stage.imgsrc));
1794
+
1795
+ this.element = $('<div />').addClass('jcrop-thumb')
1796
+ .width(this.width).height(this.height)
1797
+ .append(this.preview)
1798
+ .appendTo(this.core.container);
1799
+ },
1800
+ resize: function(w,h){
1801
+ this.width = w;
1802
+ this.height = h;
1803
+ this.element.width(w).height(h);
1804
+ this.renderCoords(this.last);
1805
+ },
1806
+ refresh: function(){
1807
+ this.cw = (this.core.opt.xscale * this.core.container.width());
1808
+ this.ch = (this.core.opt.yscale * this.core.container.height());
1809
+ if (this.last) {
1810
+ this.renderCoords(this.last);
1811
+ }
1812
+ },
1813
+ renderCoords: function(c){
1814
+ var rx = this.width / c.w;
1815
+ var ry = this.height / c.h;
1816
+
1817
+ this.preview.css({
1818
+ width: Math.round(rx * this.cw) + 'px',
1819
+ height: Math.round(ry * this.ch) + 'px',
1820
+ marginLeft: '-' + Math.round(rx * c.x) + 'px',
1821
+ marginTop: '-' + Math.round(ry * c.y) + 'px'
1822
+ });
1823
+
1824
+ this.last = c;
1825
+ return this;
1826
+ },
1827
+ renderSelection: function(s){
1828
+ return this.renderCoords(s.core.unscale(s.get()));
1829
+ },
1830
+ selectionStart: function(s){
1831
+ this.renderSelection(s);
1832
+ },
1833
+ show: function(){
1834
+ if (this._hiding) clearTimeout(this._hiding);
1835
+
1836
+ if (!this.fading) this.element.stop().css({ opacity: 1 });
1837
+ else this.element.stop().animate({ opacity: 1 },{ duration: 80, queue: false });
1838
+ },
1839
+ hide: function(){
1840
+ var t = this;
1841
+ if (!t.fading) t.element.hide();
1842
+ else t._hiding = setTimeout(function(){
1843
+ t._hiding = null;
1844
+ t.element.stop().animate({ opacity: 0 },{ duration: t.fadeDuration, queue: false });
1845
+ },t.fadeDelay);
1846
+ },
1847
+ initEvents: function(){
1848
+ var t = this;
1849
+ t.core.container.on('croprotstart croprotend cropimage cropstart cropmove cropend',function(e,s,c){
1850
+ if (t.selectionTarget && (t.selectionTarget !== e.target)) return false;
1851
+
1852
+ switch(e.type){
1853
+
1854
+ case 'cropimage':
1855
+ t.updateImage(c);
1856
+ break;
1857
+
1858
+ case 'cropstart':
1859
+ t.selectionStart(s);
1860
+ case 'croprotstart':
1861
+ t.show();
1862
+ break;
1863
+
1864
+ case 'cropend':
1865
+ t.renderCoords(c);
1866
+ case 'croprotend':
1867
+ if (t.autoHide) t.hide();
1868
+ break;
1869
+
1870
+ case 'cropmove':
1871
+ t.renderCoords(c);
1872
+ break;
1873
+ }
1874
+ });
1875
+ }
1876
+ }
1877
+ });
1878
+ Jcrop.registerComponent('Thumbnailer',Thumbnailer);
1879
+
1880
+
1881
+ /**
1882
+ * DialDrag component
1883
+ * This is a little hacky, it was adapted from some previous/old code
1884
+ * Plan to update this API in the future
1885
+ */
1886
+ var DialDrag = function() { };
1887
+
1888
+ DialDrag.prototype = {
1889
+
1890
+ init: function(core,actuator,callback){
1891
+ var that = this;
1892
+
1893
+ if (!actuator) actuator = core.container;
1894
+ this.$btn = $(actuator);
1895
+ this.$targ = $(actuator);
1896
+ this.core = core;
1897
+
1898
+ this.$btn
1899
+ .addClass('dialdrag')
1900
+ .on('mousedown.dialdrag',this.mousedown())
1901
+ .data('dialdrag',this);
1902
+
1903
+ if (!$.isFunction(callback)) callback = function(){ };
1904
+ this.callback = callback;
1905
+ this.ondone = callback;
1906
+ },
1907
+
1908
+ remove: function(){
1909
+ this.$btn
1910
+ .removeClass('dialdrag')
1911
+ .off('.dialdrag')
1912
+ .data('dialdrag',null);
1913
+ return this;
1914
+ },
1915
+
1916
+ setTarget: function(obj){
1917
+ this.$targ = $(obj);
1918
+ return this;
1919
+ },
1920
+
1921
+ getOffset: function(){
1922
+ var targ = this.$targ, pos = targ.offset();
1923
+ return [
1924
+ pos.left + (targ.width()/2),
1925
+ pos.top + (targ.height()/2)
1926
+ ];
1927
+ },
1928
+
1929
+ relMouse: function(e){
1930
+ var x = e.pageX - this.offset[0],
1931
+ y = e.pageY - this.offset[1],
1932
+ ang = Math.atan2(y,x) * (180 / Math.PI),
1933
+ vec = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
1934
+ return [ x, y, ang, vec ];
1935
+ },
1936
+
1937
+ mousedown: function(){
1938
+ var that = this;
1939
+
1940
+ function mouseUp(e){
1941
+ $(window).off('.dialdrag');
1942
+ that.ondone.call(that,that.relMouse(e));
1943
+ that.core.container.trigger('croprotend');
1944
+ }
1945
+
1946
+ function mouseMove(e){
1947
+ that.callback.call(that,that.relMouse(e));
1948
+ }
1949
+
1950
+ return function(e) {
1951
+ that.offset = that.getOffset();
1952
+ var rel = that.relMouse(e);
1953
+ that.angleOffset = -that.core.ui.stage.angle+rel[2];
1954
+ that.distOffset = rel[3];
1955
+ that.dragOffset = [rel[0],rel[1]];
1956
+ that.core.container.trigger('croprotstart');
1957
+
1958
+ $(window)
1959
+ .on('mousemove.dialdrag',mouseMove)
1960
+ .on('mouseup.dialdrag',mouseUp);
1961
+
1962
+ that.callback.call(that,that.relMouse(e));
1963
+
1964
+ return false;
1965
+ };
1966
+ }
1967
+
1968
+ };
1969
+ Jcrop.registerComponent('DialDrag',DialDrag);
1970
+
1971
+
1972
+ /////////////////////////////////
1973
+ // DEFAULT SETTINGS
1974
+
1975
+ Jcrop.defaults = {
1976
+
1977
+ // Selection Behavior
1978
+ edge: { n: 0, s: 0, e: 0, w: 0 },
1979
+ setSelect: null,
1980
+ linked: true,
1981
+ linkCurrent: true,
1982
+ canDelete: true,
1983
+ canSelect: true,
1984
+ canDrag: true,
1985
+ canResize: true,
1986
+
1987
+ // Component constructors
1988
+ eventManagerComponent: Jcrop.component.EventManager,
1989
+ keyboardComponent: Jcrop.component.Keyboard,
1990
+ dragstateComponent: Jcrop.component.DragState,
1991
+ stagemanagerComponent: Jcrop.component.StageManager,
1992
+ animatorComponent: Jcrop.component.Animator,
1993
+ selectionComponent: Jcrop.component.Selection,
1994
+
1995
+ // This is a function that is called, which returns a stage object
1996
+ stageConstructor: Jcrop.stageConstructor,
1997
+
1998
+ // Stage Behavior
1999
+ allowSelect: true,
2000
+ multi: false,
2001
+ multiMax: false,
2002
+ multiCleanup: true,
2003
+ animation: true,
2004
+ animEasing: 'swing',
2005
+ animDuration: 400,
2006
+ fading: true,
2007
+ fadeDuration: 300,
2008
+ fadeEasing: 'swing',
2009
+ bgColor: 'black',
2010
+ bgOpacity: .5,
2011
+
2012
+ // Startup options
2013
+ applyFilters: [ 'constrain', 'extent', 'backoff', 'ratio', 'shader', 'round' ],
2014
+ borders: [ 'e', 'w', 's', 'n' ],
2015
+ handles: [ 'n', 's', 'e', 'w', 'sw', 'ne', 'nw', 'se' ],
2016
+ dragbars: [ 'n', 'e', 'w', 's' ],
2017
+
2018
+ dragEventTarget: window,
2019
+
2020
+ xscale: 1,
2021
+ yscale: 1,
2022
+
2023
+ boxWidth: null,
2024
+ boxHeight: null,
2025
+
2026
+ // CSS Classes
2027
+ // @todo: These need to be moved to top-level object keys
2028
+ // for better customization. Currently if you try to extend one
2029
+ // via an options object to Jcrop, it will wipe out all
2030
+ // the others you don't specify. Be careful for now!
2031
+ css_nodrag: 'jcrop-nodrag',
2032
+ css_drag: 'jcrop-drag',
2033
+ css_container: 'jcrop-active',
2034
+ css_shades: 'jcrop-shades',
2035
+ css_selection: 'jcrop-selection',
2036
+ css_borders: 'jcrop-border',
2037
+ css_handles: 'jcrop-handle jcrop-drag',
2038
+ css_button: 'jcrop-box jcrop-drag',
2039
+ css_noresize: 'jcrop-noresize',
2040
+ css_dragbars: 'jcrop-dragbar jcrop-drag',
2041
+
2042
+ legacyHandlers: {
2043
+ onChange: 'cropmove',
2044
+ onSelect: 'cropend'
2045
+ }
2046
+
2047
+ };
2048
+
2049
+
2050
+ // Jcrop API methods
2051
+ $.extend(Jcrop.prototype,{
2052
+ //init: function(){{{
2053
+ init: function(){
2054
+ this.event = new this.opt.eventManagerComponent(this);
2055
+ this.ui.keyboard = new this.opt.keyboardComponent(this);
2056
+ this.ui.manager = new this.opt.stagemanagerComponent(this);
2057
+ this.applyFilters();
2058
+
2059
+ if ($.Jcrop.supportsTouch)
2060
+ new $.Jcrop.component.Touch(this);
2061
+
2062
+ this.initEvents();
2063
+ },
2064
+ //}}}
2065
+ // applySizeConstraints: function(){{{
2066
+ applySizeConstraints: function(){
2067
+ var o = this.opt,
2068
+ img = this.opt.imgsrc;
2069
+
2070
+ if (img){
2071
+
2072
+ var iw = img.naturalWidth || img.width,
2073
+ ih = img.naturalHeight || img.height,
2074
+ bw = o.boxWidth || iw,
2075
+ bh = o.boxHeight || ih;
2076
+
2077
+ if (img && ((iw > bw) || (ih > bh))){
2078
+ var bx = Jcrop.getLargestBox(iw/ih,bw,bh);
2079
+ $(img).width(bx[0]).height(bx[1]);
2080
+ this.resizeContainer(bx[0],bx[1]);
2081
+ this.opt.xscale = iw / bx[0];
2082
+ this.opt.yscale = ih / bx[1];
2083
+ }
2084
+
2085
+ }
2086
+
2087
+ if (this.opt.trueSize){
2088
+ var dw = this.opt.trueSize[0];
2089
+ var dh = this.opt.trueSize[1];
2090
+ var cs = this.getContainerSize();
2091
+ this.opt.xscale = dw / cs[0];
2092
+ this.opt.yscale = dh / cs[1];
2093
+ }
2094
+ },
2095
+ // }}}
2096
+ initComponent: function(name){
2097
+ if (Jcrop.component[name]) {
2098
+ var args = Array.prototype.slice.call(arguments);
2099
+ var obj = new Jcrop.component[name];
2100
+ args.shift();
2101
+ args.unshift(this);
2102
+ obj.init.apply(obj,args);
2103
+ return obj;
2104
+ }
2105
+ },
2106
+ // setOptions: function(opt){{{
2107
+ setOptions: function(opt,proptype){
2108
+
2109
+ if (!$.isPlainObject(opt)) opt = {};
2110
+
2111
+ $.extend(this.opt,opt);
2112
+
2113
+ // Handle a setSelect value
2114
+ if (this.opt.setSelect) {
2115
+
2116
+ // If there is no current selection
2117
+ // passing setSelect will create one
2118
+ if (!this.ui.multi.length)
2119
+ this.newSelection();
2120
+
2121
+ // Use these values to update the current selection
2122
+ this.setSelect(this.opt.setSelect);
2123
+
2124
+ // Set to null so it doesn't get called again
2125
+ this.opt.setSelect = null;
2126
+ }
2127
+
2128
+ this.event.trigger('configupdate');
2129
+ return this;
2130
+ },
2131
+ // }}}
2132
+ //destroy: function(){{{
2133
+ destroy: function(){
2134
+ if (this.opt.imgsrc) {
2135
+ this.container.before(this.opt.imgsrc);
2136
+ this.container.remove();
2137
+ $(this.opt.imgsrc).removeData('Jcrop').show();
2138
+ } else {
2139
+ // @todo: more elegant destroy() process for non-image containers
2140
+ this.container.remove();
2141
+ }
2142
+ },
2143
+ // }}}
2144
+ // applyFilters: function(){{{
2145
+ applyFilters: function(){
2146
+ var obj;
2147
+ for(var i=0,f=this.opt.applyFilters,l=f.length; i<l; i++){
2148
+ if ($.Jcrop.filter[f[i]])
2149
+ obj = new $.Jcrop.filter[f[i]];
2150
+ obj.core = this;
2151
+ if (obj.init) obj.init();
2152
+ this.filter[f[i]] = obj;
2153
+ }
2154
+ },
2155
+ // }}}
2156
+ // getDefaultFilters: function(){{{
2157
+ getDefaultFilters: function(){
2158
+ var rv = [];
2159
+
2160
+ for(var i=0,f=this.opt.applyFilters,l=f.length; i<l; i++)
2161
+ if(this.filter.hasOwnProperty(f[i]))
2162
+ rv.push(this.filter[f[i]]);
2163
+
2164
+ rv.sort(function(x,y){ return x.priority - y.priority; });
2165
+ return rv;
2166
+ },
2167
+ // }}}
2168
+ // setSelection: function(sel){{{
2169
+ setSelection: function(sel){
2170
+ var m = this.ui.multi;
2171
+ var n = [];
2172
+ for(var i=0;i<m.length;i++) {
2173
+ if (m[i] !== sel) n.push(m[i]);
2174
+ m[i].toBack();
2175
+ }
2176
+ n.unshift(sel);
2177
+ this.ui.multi = n;
2178
+ this.ui.selection = sel;
2179
+ sel.toFront();
2180
+ return sel;
2181
+ },
2182
+ // }}}
2183
+ // getSelection: function(raw){{{
2184
+ getSelection: function(raw){
2185
+ var b = this.ui.selection.get();
2186
+ return b;
2187
+ },
2188
+ // }}}
2189
+ // newSelection: function(){{{
2190
+ newSelection: function(sel){
2191
+ if (!sel)
2192
+ sel = new this.opt.selectionComponent();
2193
+
2194
+ sel.init(this);
2195
+ this.setSelection(sel);
2196
+
2197
+ return sel;
2198
+ },
2199
+ // }}}
2200
+ // hasSelection: function(sel){{{
2201
+ hasSelection: function(sel){
2202
+ for(var i=0;i<this.ui.multi;i++)
2203
+ if (sel === this.ui.multi[i]) return true;
2204
+ },
2205
+ // }}}
2206
+ // removeSelection: function(sel){{{
2207
+ removeSelection: function(sel){
2208
+ var i, n = [], m = this.ui.multi;
2209
+ for(var i=0;i<m.length;i++){
2210
+ if (sel !== m[i])
2211
+ n.push(m[i]);
2212
+ else m[i].remove();
2213
+ }
2214
+ return this.ui.multi = n;
2215
+ },
2216
+ // }}}
2217
+ //addFilter: function(filter){{{
2218
+ addFilter: function(filter){
2219
+ for(var i=0,m=this.ui.multi,l=m.length; i<l; i++)
2220
+ m[i].addFilter(filter);
2221
+
2222
+ return this;
2223
+ },
2224
+ //}}}
2225
+ // removeFiltersByTag: function(tag){{{
2226
+ removeFilter: function(filter){
2227
+ for(var i=0,m=this.ui.multi,l=m.length; i<l; i++)
2228
+ m[i].removeFilter(filter);
2229
+
2230
+ return this;
2231
+ },
2232
+ // }}}
2233
+ // blur: function(){{{
2234
+ blur: function(){
2235
+ this.ui.selection.blur();
2236
+ return this;
2237
+ },
2238
+ // }}}
2239
+ // focus: function(){{{
2240
+ focus: function(){
2241
+ this.ui.selection.focus();
2242
+ return this;
2243
+ },
2244
+ // }}}
2245
+ //initEvents: function(){{{
2246
+ initEvents: function(){
2247
+ var t = this;
2248
+ t.container.on('selectstart',function(e){ return false; })
2249
+ .on('mousedown','.'+t.opt.css_drag,t.startDrag());
2250
+ },
2251
+ //}}}
2252
+ // maxSelect: function(){{{
2253
+ maxSelect: function(){
2254
+ this.setSelect([0,0,this.elw,this.elh]);
2255
+ },
2256
+ // }}}
2257
+ // nudge: function(x,y){{{
2258
+ nudge: function(x,y){
2259
+ var s = this.ui.selection, b = s.get();
2260
+
2261
+ b.x += x;
2262
+ b.x2 += x;
2263
+ b.y += y;
2264
+ b.y2 += y;
2265
+
2266
+ if (b.x < 0) { b.x2 = b.w; b.x = 0; }
2267
+ else if (b.x2 > this.elw) { b.x2 = this.elw; b.x = b.x2 - b.w; }
2268
+
2269
+ if (b.y < 0) { b.y2 = b.h; b.y = 0; }
2270
+ else if (b.y2 > this.elh) { b.y2 = this.elh; b.y = b.y2 - b.h; }
2271
+
2272
+ s.element.trigger('cropstart',[s,this.unscale(b)]);
2273
+ s.updateRaw(b,'move');
2274
+ s.element.trigger('cropend',[s,this.unscale(b)]);
2275
+ },
2276
+ // }}}
2277
+ // refresh: function(){{{
2278
+ refresh: function(){
2279
+ for(var i=0,s=this.ui.multi,l=s.length;i<l;i++)
2280
+ s[i].refresh();
2281
+ },
2282
+ // }}}
2283
+ // blurAll: function(){{{
2284
+ blurAll: function(){
2285
+ var m = this.ui.multi;
2286
+ for(var i=0;i<m.length;i++) {
2287
+ if (m[i] !== sel) n.push(m[i]);
2288
+ m[i].toBack();
2289
+ }
2290
+ },
2291
+ // }}}
2292
+ // scale: function(b){{{
2293
+ scale: function(b){
2294
+ var xs = this.opt.xscale,
2295
+ ys = this.opt.yscale;
2296
+
2297
+ return {
2298
+ x: b.x / xs,
2299
+ y: b.y / ys,
2300
+ x2: b.x2 / xs,
2301
+ y2: b.y2 / ys,
2302
+ w: b.w / xs,
2303
+ h: b.h / ys
2304
+ };
2305
+ },
2306
+ // }}}
2307
+ // unscale: function(b){{{
2308
+ unscale: function(b){
2309
+ var xs = this.opt.xscale,
2310
+ ys = this.opt.yscale;
2311
+
2312
+ return {
2313
+ x: b.x * xs,
2314
+ y: b.y * ys,
2315
+ x2: b.x2 * xs,
2316
+ y2: b.y2 * ys,
2317
+ w: b.w * xs,
2318
+ h: b.h * ys
2319
+ };
2320
+ },
2321
+ // }}}
2322
+ // requestDelete: function(){{{
2323
+ requestDelete: function(){
2324
+ if ((this.ui.multi.length > 1) && (this.ui.selection.canDelete))
2325
+ return this.deleteSelection();
2326
+ },
2327
+ // }}}
2328
+ // deleteSelection: function(){{{
2329
+ deleteSelection: function(){
2330
+ if (this.ui.selection) {
2331
+ this.removeSelection(this.ui.selection);
2332
+ if (this.ui.multi.length) this.ui.multi[0].focus();
2333
+ this.ui.selection.refresh();
2334
+ }
2335
+ },
2336
+ // }}}
2337
+ // animateTo: function(box){{{
2338
+ animateTo: function(box){
2339
+ if (this.ui.selection)
2340
+ this.ui.selection.animateTo(box);
2341
+ return this;
2342
+ },
2343
+ // }}}
2344
+ // setselect: function(box){{{
2345
+ setSelect: function(box){
2346
+ if (this.ui.selection)
2347
+ this.ui.selection.update(Jcrop.wrapFromXywh(box));
2348
+ return this;
2349
+ },
2350
+ // }}}
2351
+ //startDrag: function(){{{
2352
+ startDrag: function(){
2353
+ var t = this;
2354
+ return function(e){
2355
+ var $targ = $(e.target);
2356
+ var selection = $targ.closest('.'+t.opt.css_selection).data('selection');
2357
+ var ord = $targ.data('ord');
2358
+ t.container.trigger('cropstart',[selection,t.unscale(selection.get())]);
2359
+ selection.startDrag(e,ord);
2360
+ return false;
2361
+ };
2362
+ },
2363
+ //}}}
2364
+ // getContainerSize: function(){{{
2365
+ getContainerSize: function(){
2366
+ return [ this.container.width(), this.container.height() ];
2367
+ },
2368
+ // }}}
2369
+ // resizeContainer: function(w,h){{{
2370
+ resizeContainer: function(w,h){
2371
+ this.container.width(w).height(h);
2372
+ this.refresh();
2373
+ },
2374
+ // }}}
2375
+ // setImage: function(src,cb){{{
2376
+ setImage: function(src,cb){
2377
+ var t = this, targ = t.opt.imgsrc;
2378
+
2379
+ if (!targ) return false;
2380
+
2381
+ new $.Jcrop.component.ImageLoader(src,null,function(w,h){
2382
+ t.resizeContainer(w,h);
2383
+
2384
+ targ.src = src;
2385
+ $(targ).width(w).height(h);
2386
+ t.applySizeConstraints();
2387
+ t.refresh();
2388
+ t.container.trigger('cropimage',[t,targ]);
2389
+
2390
+ if (typeof cb == 'function')
2391
+ cb.call(t,w,h);
2392
+ });
2393
+ },
2394
+ // }}}
2395
+ // update: function(b){{{
2396
+ update: function(b){
2397
+ if (this.ui.selection)
2398
+ this.ui.selection.update(b);
2399
+ }
2400
+ // }}}
2401
+ });
2402
+
2403
+ // Jcrop jQuery plugin function
2404
+ $.fn.Jcrop = function(options,callback){
2405
+ options = options || {};
2406
+
2407
+ var first = this.eq(0).data('Jcrop');
2408
+ var args = Array.prototype.slice.call(arguments);
2409
+
2410
+ // Return API if requested
2411
+ if (options == 'api') { return first; }
2412
+
2413
+ // Allow calling API methods (with arguments)
2414
+ else if (first && (typeof options == 'string')) {
2415
+
2416
+ // Call method if it exists
2417
+ if (first[options]) {
2418
+ args.shift();
2419
+ first[options].apply(first,args);
2420
+ return first;
2421
+ }
2422
+
2423
+ // Unknown input/method does not exist
2424
+ return false;
2425
+ }
2426
+
2427
+ // Otherwise, loop over selected elements
2428
+ this.each(function(){
2429
+ var t = this, $t = $(this);
2430
+ var exists = $t.data('Jcrop');
2431
+ var obj;
2432
+
2433
+ // If Jcrop already exists on this element only setOptions()
2434
+ if (exists)
2435
+ exists.setOptions(options);
2436
+
2437
+ else {
2438
+
2439
+ if (!options.stageConstructor)
2440
+ options.stageConstructor = $.Jcrop.stageConstructor;
2441
+
2442
+ options.stageConstructor(this,options,function(stage,options){
2443
+ var selection = options.setSelect;
2444
+ if (selection) delete(options.setSelect);
2445
+
2446
+ var obj = $.Jcrop.attach(stage.element,options);
2447
+
2448
+ if (typeof stage.attach == 'function')
2449
+ stage.attach(obj);
2450
+
2451
+ $t.data('Jcrop',obj);
2452
+
2453
+ if (selection) {
2454
+ obj.newSelection();
2455
+ obj.setSelect(selection);
2456
+ }
2457
+
2458
+ if (typeof callback == 'function')
2459
+ callback.call(obj);
2460
+ });
2461
+ }
2462
+
2463
+ return this;
2464
+ });
2465
+ };
2466
+
2467
+ /* Modernizr 2.7.1 (Custom Build) | MIT & BSD
2468
+ * Build: http://modernizr.com/download/#-csstransforms-canvas-canvastext-draganddrop-inlinesvg-svg-svgclippaths-touch-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-url_data_uri
2469
+ */
2470
+ ;
2471
+
2472
+ var Modernizr = (function( window, document, undefined ) {
2473
+
2474
+ var version = '2.7.1',
2475
+
2476
+ Modernizr = {},
2477
+
2478
+
2479
+ docElement = document.documentElement,
2480
+
2481
+ mod = 'modernizr',
2482
+ modElem = document.createElement(mod),
2483
+ mStyle = modElem.style,
2484
+
2485
+ inputElem ,
2486
+
2487
+
2488
+ toString = {}.toString,
2489
+
2490
+ prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
2491
+
2492
+
2493
+
2494
+ omPrefixes = 'Webkit Moz O ms',
2495
+
2496
+ cssomPrefixes = omPrefixes.split(' '),
2497
+
2498
+ domPrefixes = omPrefixes.toLowerCase().split(' '),
2499
+
2500
+ ns = {'svg': 'http://www.w3.org/2000/svg'},
2501
+
2502
+ tests = {},
2503
+ inputs = {},
2504
+ attrs = {},
2505
+
2506
+ classes = [],
2507
+
2508
+ slice = classes.slice,
2509
+
2510
+ featureName,
2511
+
2512
+
2513
+ injectElementWithStyles = function( rule, callback, nodes, testnames ) {
2514
+
2515
+ var style, ret, node, docOverflow,
2516
+ div = document.createElement('div'),
2517
+ body = document.body,
2518
+ fakeBody = body || document.createElement('body');
2519
+
2520
+ if ( parseInt(nodes, 10) ) {
2521
+ while ( nodes-- ) {
2522
+ node = document.createElement('div');
2523
+ node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
2524
+ div.appendChild(node);
2525
+ }
2526
+ }
2527
+
2528
+ style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
2529
+ div.id = mod;
2530
+ (body ? div : fakeBody).innerHTML += style;
2531
+ fakeBody.appendChild(div);
2532
+ if ( !body ) {
2533
+ fakeBody.style.background = '';
2534
+ fakeBody.style.overflow = 'hidden';
2535
+ docOverflow = docElement.style.overflow;
2536
+ docElement.style.overflow = 'hidden';
2537
+ docElement.appendChild(fakeBody);
2538
+ }
2539
+
2540
+ ret = callback(div, rule);
2541
+ if ( !body ) {
2542
+ fakeBody.parentNode.removeChild(fakeBody);
2543
+ docElement.style.overflow = docOverflow;
2544
+ } else {
2545
+ div.parentNode.removeChild(div);
2546
+ }
2547
+
2548
+ return !!ret;
2549
+
2550
+ },
2551
+
2552
+
2553
+
2554
+ isEventSupported = (function() {
2555
+
2556
+ var TAGNAMES = {
2557
+ 'select': 'input', 'change': 'input',
2558
+ 'submit': 'form', 'reset': 'form',
2559
+ 'error': 'img', 'load': 'img', 'abort': 'img'
2560
+ };
2561
+
2562
+ function isEventSupported( eventName, element ) {
2563
+
2564
+ element = element || document.createElement(TAGNAMES[eventName] || 'div');
2565
+ eventName = 'on' + eventName;
2566
+
2567
+ var isSupported = eventName in element;
2568
+
2569
+ if ( !isSupported ) {
2570
+ if ( !element.setAttribute ) {
2571
+ element = document.createElement('div');
2572
+ }
2573
+ if ( element.setAttribute && element.removeAttribute ) {
2574
+ element.setAttribute(eventName, '');
2575
+ isSupported = is(element[eventName], 'function');
2576
+
2577
+ if ( !is(element[eventName], 'undefined') ) {
2578
+ element[eventName] = undefined;
2579
+ }
2580
+ element.removeAttribute(eventName);
2581
+ }
2582
+ }
2583
+
2584
+ element = null;
2585
+ return isSupported;
2586
+ }
2587
+ return isEventSupported;
2588
+ })(),
2589
+
2590
+
2591
+ _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
2592
+
2593
+ if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
2594
+ hasOwnProp = function (object, property) {
2595
+ return _hasOwnProperty.call(object, property);
2596
+ };
2597
+ }
2598
+ else {
2599
+ hasOwnProp = function (object, property) {
2600
+ return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
2601
+ };
2602
+ }
2603
+
2604
+
2605
+ if (!Function.prototype.bind) {
2606
+ Function.prototype.bind = function bind(that) {
2607
+
2608
+ var target = this;
2609
+
2610
+ if (typeof target != "function") {
2611
+ throw new TypeError();
2612
+ }
2613
+
2614
+ var args = slice.call(arguments, 1),
2615
+ bound = function () {
2616
+
2617
+ if (this instanceof bound) {
2618
+
2619
+ var F = function(){};
2620
+ F.prototype = target.prototype;
2621
+ var self = new F();
2622
+
2623
+ var result = target.apply(
2624
+ self,
2625
+ args.concat(slice.call(arguments))
2626
+ );
2627
+ if (Object(result) === result) {
2628
+ return result;
2629
+ }
2630
+ return self;
2631
+
2632
+ } else {
2633
+
2634
+ return target.apply(
2635
+ that,
2636
+ args.concat(slice.call(arguments))
2637
+ );
2638
+
2639
+ }
2640
+
2641
+ };
2642
+
2643
+ return bound;
2644
+ };
2645
+ }
2646
+
2647
+ function setCss( str ) {
2648
+ mStyle.cssText = str;
2649
+ }
2650
+
2651
+ function setCssAll( str1, str2 ) {
2652
+ return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
2653
+ }
2654
+
2655
+ function is( obj, type ) {
2656
+ return typeof obj === type;
2657
+ }
2658
+
2659
+ function contains( str, substr ) {
2660
+ return !!~('' + str).indexOf(substr);
2661
+ }
2662
+
2663
+ function testProps( props, prefixed ) {
2664
+ for ( var i in props ) {
2665
+ var prop = props[i];
2666
+ if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
2667
+ return prefixed == 'pfx' ? prop : true;
2668
+ }
2669
+ }
2670
+ return false;
2671
+ }
2672
+
2673
+ function testDOMProps( props, obj, elem ) {
2674
+ for ( var i in props ) {
2675
+ var item = obj[props[i]];
2676
+ if ( item !== undefined) {
2677
+
2678
+ if (elem === false) return props[i];
2679
+
2680
+ if (is(item, 'function')){
2681
+ return item.bind(elem || obj);
2682
+ }
2683
+
2684
+ return item;
2685
+ }
2686
+ }
2687
+ return false;
2688
+ }
2689
+
2690
+ function testPropsAll( prop, prefixed, elem ) {
2691
+
2692
+ var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
2693
+ props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
2694
+
2695
+ if(is(prefixed, "string") || is(prefixed, "undefined")) {
2696
+ return testProps(props, prefixed);
2697
+
2698
+ } else {
2699
+ props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
2700
+ return testDOMProps(props, prefixed, elem);
2701
+ }
2702
+ }
2703
+
2704
+
2705
+
2706
+ tests['canvas'] = function() {
2707
+ var elem = document.createElement('canvas');
2708
+ return !!(elem.getContext && elem.getContext('2d'));
2709
+ };
2710
+
2711
+ tests['canvastext'] = function() {
2712
+ return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
2713
+ };
2714
+ tests['touch'] = function() {
2715
+ var bool;
2716
+
2717
+ if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
2718
+ bool = true;
2719
+ } else {
2720
+ injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
2721
+ bool = node.offsetTop === 9;
2722
+ });
2723
+ }
2724
+
2725
+ return bool;
2726
+ };
2727
+
2728
+ tests['draganddrop'] = function() {
2729
+ var div = document.createElement('div');
2730
+ return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
2731
+ };
2732
+
2733
+
2734
+ tests['csstransforms'] = function() {
2735
+ return !!testPropsAll('transform');
2736
+ };
2737
+
2738
+
2739
+ tests['svg'] = function() {
2740
+ return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
2741
+ };
2742
+
2743
+ tests['inlinesvg'] = function() {
2744
+ var div = document.createElement('div');
2745
+ div.innerHTML = '<svg/>';
2746
+ return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
2747
+ };
2748
+
2749
+
2750
+
2751
+ tests['svgclippaths'] = function() {
2752
+ return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
2753
+ };
2754
+
2755
+ for ( var feature in tests ) {
2756
+ if ( hasOwnProp(tests, feature) ) {
2757
+ featureName = feature.toLowerCase();
2758
+ Modernizr[featureName] = tests[feature]();
2759
+
2760
+ classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
2761
+ }
2762
+ }
2763
+
2764
+
2765
+
2766
+ Modernizr.addTest = function ( feature, test ) {
2767
+ if ( typeof feature == 'object' ) {
2768
+ for ( var key in feature ) {
2769
+ if ( hasOwnProp( feature, key ) ) {
2770
+ Modernizr.addTest( key, feature[ key ] );
2771
+ }
2772
+ }
2773
+ } else {
2774
+
2775
+ feature = feature.toLowerCase();
2776
+
2777
+ if ( Modernizr[feature] !== undefined ) {
2778
+ return Modernizr;
2779
+ }
2780
+
2781
+ test = typeof test == 'function' ? test() : test;
2782
+
2783
+ if (typeof enableClasses !== "undefined" && enableClasses) {
2784
+ docElement.className += ' ' + (test ? '' : 'no-') + feature;
2785
+ }
2786
+ Modernizr[feature] = test;
2787
+
2788
+ }
2789
+
2790
+ return Modernizr;
2791
+ };
2792
+
2793
+
2794
+ setCss('');
2795
+ modElem = inputElem = null;
2796
+
2797
+
2798
+ Modernizr._version = version;
2799
+
2800
+ Modernizr._prefixes = prefixes;
2801
+ Modernizr._domPrefixes = domPrefixes;
2802
+ Modernizr._cssomPrefixes = cssomPrefixes;
2803
+
2804
+
2805
+ Modernizr.hasEvent = isEventSupported;
2806
+
2807
+ Modernizr.testProp = function(prop){
2808
+ return testProps([prop]);
2809
+ };
2810
+
2811
+ Modernizr.testAllProps = testPropsAll;
2812
+
2813
+
2814
+ Modernizr.testStyles = injectElementWithStyles;
2815
+ return Modernizr;
2816
+
2817
+ })(window, window.document);
2818
+ // data uri test.
2819
+ // https://github.com/Modernizr/Modernizr/issues/14
2820
+
2821
+ // This test is asynchronous. Watch out.
2822
+
2823
+
2824
+ // in IE7 in HTTPS this can cause a Mixed Content security popup.
2825
+ // github.com/Modernizr/Modernizr/issues/362
2826
+ // To avoid that you can create a new iframe and inject this.. perhaps..
2827
+
2828
+
2829
+ (function(){
2830
+
2831
+ var datauri = new Image();
2832
+
2833
+
2834
+ datauri.onerror = function() {
2835
+ Modernizr.addTest('datauri', function () { return false; });
2836
+ };
2837
+ datauri.onload = function() {
2838
+ Modernizr.addTest('datauri', function () { return (datauri.width == 1 && datauri.height == 1); });
2839
+ };
2840
+
2841
+ datauri.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
2842
+
2843
+ })();
2844
+ ;
2845
+
2846
+ // Attach to jQuery object
2847
+ $.Jcrop = Jcrop;
2848
+
2849
+ $.Jcrop.supportsCanvas = Modernizr.canvas;
2850
+ $.Jcrop.supportsCanvasText = Modernizr.canvastext;
2851
+ $.Jcrop.supportsDragAndDrop = Modernizr.draganddrop;
2852
+ $.Jcrop.supportsDataURI = Modernizr.datauri;
2853
+ $.Jcrop.supportsSVG = Modernizr.svg;
2854
+ $.Jcrop.supportsInlineSVG = Modernizr.inlinesvg;
2855
+ $.Jcrop.supportsSVGClipPaths = Modernizr.svgclippaths;
2856
+ $.Jcrop.supportsCSSTransforms = Modernizr.csstransforms;
2857
+ $.Jcrop.supportsTouch = Modernizr.touch;
2858
+
2859
+ })(jQuery);