You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2860 lines
74 KiB
JavaScript
2860 lines
74 KiB
JavaScript
3 years ago
|
/*! Jcrop.js v2.0.4 - build: 20151117
|
||
|
* @copyright 2008-2015 Tapmodo Interactive LLC
|
||
|
* @license Free software under MIT License
|
||
|
* @website http://jcrop.org/
|
||
|
**/
|
||
|
(function($){
|
||
|
'use strict';
|
||
|
|
||
|
// Jcrop constructor
|
||
|
var Jcrop = function(element,opt){
|
||
|
var _ua = navigator.userAgent.toLowerCase();
|
||
|
|
||
|
this.opt = $.extend({},Jcrop.defaults,opt || {});
|
||
|
|
||
|
this.container = $(element);
|
||
|
|
||
|
this.opt.is_msie = /msie/.test(_ua);
|
||
|
this.opt.is_ie_lt9 = /msie [1-8]\./.test(_ua);
|
||
|
|
||
|
this.container.addClass(this.opt.css_container);
|
||
|
|
||
|
this.ui = {};
|
||
|
this.state = null;
|
||
|
this.ui.multi = [];
|
||
|
this.ui.selection = null;
|
||
|
this.filter = {};
|
||
|
|
||
|
this.init();
|
||
|
this.setOptions(opt);
|
||
|
this.applySizeConstraints();
|
||
|
this.container.trigger('cropinit',this);
|
||
|
|
||
|
// IE<9 doesn't work if mouse events are attached to window
|
||
|
if (this.opt.is_ie_lt9)
|
||
|
this.opt.dragEventTarget = document.body;
|
||
|
};
|
||
|
|
||
|
|
||
|
// Jcrop static functions
|
||
|
$.extend(Jcrop,{
|
||
|
component: { },
|
||
|
filter: { },
|
||
|
stage: { },
|
||
|
registerComponent: function(name,component){
|
||
|
Jcrop.component[name] = component;
|
||
|
},
|
||
|
registerFilter: function(name,filter){
|
||
|
Jcrop.filter[name] = filter;
|
||
|
},
|
||
|
registerStageType: function(name,stage){
|
||
|
Jcrop.stage[name] = stage;
|
||
|
},
|
||
|
// attach: function(element,opt){{{
|
||
|
attach: function(element,opt){
|
||
|
var obj = new $.Jcrop(element,opt);
|
||
|
return obj;
|
||
|
},
|
||
|
// }}}
|
||
|
// imgCopy: function(imgel){{{
|
||
|
imgCopy: function(imgel){
|
||
|
var img = new Image;
|
||
|
img.src = imgel.src;
|
||
|
return img;
|
||
|
},
|
||
|
// }}}
|
||
|
// imageClone: function(imgel){{{
|
||
|
imageClone: function(imgel){
|
||
|
return $.Jcrop.supportsCanvas?
|
||
|
Jcrop.canvasClone(imgel):
|
||
|
Jcrop.imgCopy(imgel);
|
||
|
},
|
||
|
// }}}
|
||
|
// canvasClone: function(imgel){{{
|
||
|
canvasClone: function(imgel){
|
||
|
var canvas = document.createElement('canvas'),
|
||
|
ctx = canvas.getContext('2d');
|
||
|
|
||
|
$(canvas).width(imgel.width).height(imgel.height),
|
||
|
canvas.width = imgel.naturalWidth;
|
||
|
canvas.height = imgel.naturalHeight;
|
||
|
ctx.drawImage(imgel,0,0,imgel.naturalWidth,imgel.naturalHeight);
|
||
|
return canvas;
|
||
|
},
|
||
|
// }}}
|
||
|
// propagate: function(plist,config,obj){{{
|
||
|
propagate: function(plist,config,obj){
|
||
|
for(var i=0,l=plist.length;i<l;i++)
|
||
|
if (config.hasOwnProperty(plist[i]))
|
||
|
obj[plist[i]] = config[plist[i]];
|
||
|
},
|
||
|
// }}}
|
||
|
// getLargestBox: function(ratio,w,h){{{
|
||
|
getLargestBox: function(ratio,w,h){
|
||
|
if ((w/h) > ratio)
|
||
|
return [ h * ratio, h ];
|
||
|
else return [ w, w / ratio ];
|
||
|
},
|
||
|
// }}}
|
||
|
// stageConstructor: function(el,options,callback){{{
|
||
|
stageConstructor: function(el,options,callback){
|
||
|
|
||
|
// Get a priority-ordered list of available stages
|
||
|
var stages = [];
|
||
|
$.each(Jcrop.stage,function(i,e){
|
||
|
stages.push(e);
|
||
|
});
|
||
|
stages.sort(function(a,b){ return a.priority - b.priority; });
|
||
|
|
||
|
// Find the first one that supports this element
|
||
|
for(var i=0,l=stages.length;i<l;i++){
|
||
|
if (stages[i].isSupported(el,options)){
|
||
|
stages[i].create(el,options,function(obj,opt){
|
||
|
if (typeof callback == 'function') callback(obj,opt);
|
||
|
});
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// supportsColorFade: function(){{{
|
||
|
supportsColorFade: function(){
|
||
|
return $.fx.step.hasOwnProperty('backgroundColor');
|
||
|
},
|
||
|
// }}}
|
||
|
// wrapFromXywh: function(xywh){{{
|
||
|
wrapFromXywh: function(xywh){
|
||
|
var b = { x: xywh[0], y: xywh[1], w: xywh[2], h: xywh[3] };
|
||
|
b.x2 = b.x + b.w;
|
||
|
b.y2 = b.y + b.h;
|
||
|
return b;
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
|
||
|
var AbstractStage = function(){
|
||
|
};
|
||
|
|
||
|
$.extend(AbstractStage,{
|
||
|
isSupported: function(el,o){
|
||
|
// @todo: should actually check if it's an HTML element
|
||
|
return true;
|
||
|
},
|
||
|
// A higher priority means less desirable
|
||
|
// AbstractStage is the last one we want to use
|
||
|
priority: 100,
|
||
|
create: function(el,options,callback){
|
||
|
var obj = new AbstractStage;
|
||
|
obj.element = el;
|
||
|
callback.call(this,obj,options);
|
||
|
},
|
||
|
prototype: {
|
||
|
attach: function(core){
|
||
|
this.init(core);
|
||
|
core.ui.stage = this;
|
||
|
},
|
||
|
triggerEvent: function(ev){
|
||
|
$(this.element).trigger(ev);
|
||
|
return this;
|
||
|
},
|
||
|
getElement: function(){
|
||
|
return this.element;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerStageType('Block',AbstractStage);
|
||
|
|
||
|
|
||
|
var ImageStage = function(){
|
||
|
};
|
||
|
|
||
|
ImageStage.prototype = new AbstractStage();
|
||
|
|
||
|
$.extend(ImageStage,{
|
||
|
isSupported: function(el,o){
|
||
|
if (el.tagName == 'IMG') return true;
|
||
|
},
|
||
|
priority: 90,
|
||
|
create: function(el,options,callback){
|
||
|
$.Jcrop.component.ImageLoader.attach(el,function(w,h){
|
||
|
var obj = new ImageStage;
|
||
|
obj.element = $(el).wrap('<div />').parent();
|
||
|
|
||
|
obj.element.width(w).height(h);
|
||
|
obj.imgsrc = el;
|
||
|
|
||
|
if (typeof callback == 'function')
|
||
|
callback.call(this,obj,options);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerStageType('Image',ImageStage);
|
||
|
|
||
|
|
||
|
var CanvasStage = function(){
|
||
|
this.angle = 0;
|
||
|
this.scale = 1;
|
||
|
this.scaleMin = 0.2;
|
||
|
this.scaleMax = 1.25;
|
||
|
this.offset = [0,0];
|
||
|
};
|
||
|
|
||
|
CanvasStage.prototype = new ImageStage();
|
||
|
|
||
|
$.extend(CanvasStage,{
|
||
|
isSupported: function(el,o){
|
||
|
if ($.Jcrop.supportsCanvas && (el.tagName == 'IMG')) return true;
|
||
|
},
|
||
|
priority: 60,
|
||
|
create: function(el,options,callback){
|
||
|
var $el = $(el);
|
||
|
var opt = $.extend({},options);
|
||
|
$.Jcrop.component.ImageLoader.attach(el,function(w,h){
|
||
|
var obj = new CanvasStage;
|
||
|
$el.hide();
|
||
|
obj.createCanvas(el,w,h);
|
||
|
$el.before(obj.element);
|
||
|
obj.imgsrc = el;
|
||
|
opt.imgsrc = el;
|
||
|
|
||
|
if (typeof callback == 'function'){
|
||
|
callback(obj,opt);
|
||
|
obj.redraw();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$.extend(CanvasStage.prototype,{
|
||
|
init: function(core){
|
||
|
this.core = core;
|
||
|
},
|
||
|
// setOffset: function(x,y) {{{
|
||
|
setOffset: function(x,y) {
|
||
|
this.offset = [x,y];
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// setAngle: function(v) {{{
|
||
|
setAngle: function(v) {
|
||
|
this.angle = v;
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// setScale: function(v) {{{
|
||
|
setScale: function(v) {
|
||
|
this.scale = this.boundScale(v);
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
boundScale: function(v){
|
||
|
if (v<this.scaleMin) v = this.scaleMin;
|
||
|
else if (v>this.scaleMax) v = this.scaleMax;
|
||
|
return v;
|
||
|
},
|
||
|
createCanvas: function(img,w,h){
|
||
|
this.width = w;
|
||
|
this.height = h;
|
||
|
this.canvas = document.createElement('canvas');
|
||
|
this.canvas.width = w;
|
||
|
this.canvas.height = h;
|
||
|
this.$canvas = $(this.canvas).width('100%').height('100%');
|
||
|
this.context = this.canvas.getContext('2d');
|
||
|
this.fillstyle = "rgb(0,0,0)";
|
||
|
this.element = this.$canvas.wrap('<div />').parent().width(w).height(h);
|
||
|
},
|
||
|
triggerEvent: function(ev){
|
||
|
this.$canvas.trigger(ev);
|
||
|
return this;
|
||
|
},
|
||
|
// clear: function() {{{
|
||
|
clear: function() {
|
||
|
this.context.fillStyle = this.fillstyle;
|
||
|
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// redraw: function() {{{
|
||
|
redraw: function() {
|
||
|
// Save the current context
|
||
|
this.context.save();
|
||
|
this.clear();
|
||
|
|
||
|
// Translate to the center point of our image
|
||
|
this.context.translate(parseInt(this.width * 0.5), parseInt(this.height * 0.5));
|
||
|
// Perform the rotation and scaling
|
||
|
this.context.translate(this.offset[0]/this.core.opt.xscale,this.offset[1]/this.core.opt.yscale);
|
||
|
this.context.rotate(this.angle * (Math.PI/180));
|
||
|
this.context.scale(this.scale,this.scale);
|
||
|
// Translate back to the top left of our image
|
||
|
this.context.translate(-parseInt(this.width * 0.5), -parseInt(this.height * 0.5));
|
||
|
// Finally we draw the image
|
||
|
this.context.drawImage(this.imgsrc,0,0,this.width,this.height);
|
||
|
|
||
|
// And restore the updated context
|
||
|
this.context.restore();
|
||
|
this.$canvas.trigger('cropredraw');
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// setFillStyle: function(v) {{{
|
||
|
setFillStyle: function(v) {
|
||
|
this.fillstyle = v;
|
||
|
return this;
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
|
||
|
Jcrop.registerStageType('Canvas',CanvasStage);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* BackoffFilter
|
||
|
* move out-of-bounds selection into allowed position at same size
|
||
|
*/
|
||
|
var BackoffFilter = function(){
|
||
|
this.minw = 40;
|
||
|
this.minh = 40;
|
||
|
this.maxw = 0;
|
||
|
this.maxh = 0;
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(BackoffFilter.prototype,{
|
||
|
tag: 'backoff',
|
||
|
priority: 22,
|
||
|
filter: function(b){
|
||
|
var r = this.bound;
|
||
|
|
||
|
if (b.x < r.minx) { b.x = r.minx; b.x2 = b.w + b.x; }
|
||
|
if (b.y < r.miny) { b.y = r.miny; b.y2 = b.h + b.y; }
|
||
|
if (b.x2 > r.maxx) { b.x2 = r.maxx; b.x = b.x2 - b.w; }
|
||
|
if (b.y2 > r.maxy) { b.y2 = r.maxy; b.y = b.y2 - b.h; }
|
||
|
|
||
|
return b;
|
||
|
},
|
||
|
refresh: function(sel){
|
||
|
this.elw = sel.core.container.width();
|
||
|
this.elh = sel.core.container.height();
|
||
|
this.bound = {
|
||
|
minx: 0 + sel.edge.w,
|
||
|
miny: 0 + sel.edge.n,
|
||
|
maxx: this.elw + sel.edge.e,
|
||
|
maxy: this.elh + sel.edge.s
|
||
|
};
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('backoff',BackoffFilter);
|
||
|
|
||
|
/**
|
||
|
* ConstrainFilter
|
||
|
* a filter to constrain crop selection to bounding element
|
||
|
*/
|
||
|
var ConstrainFilter = function(){
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(ConstrainFilter.prototype,{
|
||
|
tag: 'constrain',
|
||
|
priority: 5,
|
||
|
filter: function(b,ord){
|
||
|
if (ord == 'move') {
|
||
|
if (b.x < this.minx) { b.x = this.minx; b.x2 = b.w + b.x; }
|
||
|
if (b.y < this.miny) { b.y = this.miny; b.y2 = b.h + b.y; }
|
||
|
if (b.x2 > this.maxx) { b.x2 = this.maxx; b.x = b.x2 - b.w; }
|
||
|
if (b.y2 > this.maxy) { b.y2 = this.maxy; b.y = b.y2 - b.h; }
|
||
|
} else {
|
||
|
if (b.x < this.minx) { b.x = this.minx; }
|
||
|
if (b.y < this.miny) { b.y = this.miny; }
|
||
|
if (b.x2 > this.maxx) { b.x2 = this.maxx; }
|
||
|
if (b.y2 > this.maxy) { b.y2 = this.maxy; }
|
||
|
}
|
||
|
b.w = b.x2 - b.x;
|
||
|
b.h = b.y2 - b.y;
|
||
|
return b;
|
||
|
},
|
||
|
refresh: function(sel){
|
||
|
this.elw = sel.core.container.width();
|
||
|
this.elh = sel.core.container.height();
|
||
|
this.minx = 0 + sel.edge.w;
|
||
|
this.miny = 0 + sel.edge.n;
|
||
|
this.maxx = this.elw + sel.edge.e;
|
||
|
this.maxy = this.elh + sel.edge.s;
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('constrain',ConstrainFilter);
|
||
|
|
||
|
/**
|
||
|
* ExtentFilter
|
||
|
* a filter to implement minimum or maximum size
|
||
|
*/
|
||
|
var ExtentFilter = function(){
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(ExtentFilter.prototype,{
|
||
|
tag: 'extent',
|
||
|
priority: 12,
|
||
|
offsetFromCorner: function(corner,box,b){
|
||
|
var w = box[0], h = box[1];
|
||
|
switch(corner){
|
||
|
case 'bl': return [ b.x2 - w, b.y, w, h ];
|
||
|
case 'tl': return [ b.x2 - w , b.y2 - h, w, h ];
|
||
|
case 'br': return [ b.x, b.y, w, h ];
|
||
|
case 'tr': return [ b.x, b.y2 - h, w, h ];
|
||
|
}
|
||
|
},
|
||
|
getQuadrant: function(s){
|
||
|
var relx = s.opposite[0]-s.offsetx
|
||
|
var rely = s.opposite[1]-s.offsety;
|
||
|
|
||
|
if ((relx < 0) && (rely < 0)) return 'br';
|
||
|
else if ((relx >= 0) && (rely >= 0)) return 'tl';
|
||
|
else if ((relx < 0) && (rely >= 0)) return 'tr';
|
||
|
return 'bl';
|
||
|
},
|
||
|
filter: function(b,ord,sel){
|
||
|
|
||
|
if (ord == 'move') return b;
|
||
|
|
||
|
var w = b.w, h = b.h, st = sel.state, r = this.limits;
|
||
|
var quad = st? this.getQuadrant(st): 'br';
|
||
|
|
||
|
if (r.minw && (w < r.minw)) w = r.minw;
|
||
|
if (r.minh && (h < r.minh)) h = r.minh;
|
||
|
if (r.maxw && (w > r.maxw)) w = r.maxw;
|
||
|
if (r.maxh && (h > r.maxh)) h = r.maxh;
|
||
|
|
||
|
if ((w == b.w) && (h == b.h)) return b;
|
||
|
|
||
|
return Jcrop.wrapFromXywh(this.offsetFromCorner(quad,[w,h],b));
|
||
|
},
|
||
|
refresh: function(sel){
|
||
|
this.elw = sel.core.container.width();
|
||
|
this.elh = sel.core.container.height();
|
||
|
|
||
|
this.limits = {
|
||
|
minw: sel.minSize[0],
|
||
|
minh: sel.minSize[1],
|
||
|
maxw: sel.maxSize[0],
|
||
|
maxh: sel.maxSize[1]
|
||
|
};
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('extent',ExtentFilter);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* GridFilter
|
||
|
* a rudimentary grid effect
|
||
|
*/
|
||
|
var GridFilter = function(){
|
||
|
this.stepx = 1;
|
||
|
this.stepy = 1;
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(GridFilter.prototype,{
|
||
|
tag: 'grid',
|
||
|
priority: 19,
|
||
|
filter: function(b){
|
||
|
|
||
|
var n = {
|
||
|
x: Math.round(b.x / this.stepx) * this.stepx,
|
||
|
y: Math.round(b.y / this.stepy) * this.stepy,
|
||
|
x2: Math.round(b.x2 / this.stepx) * this.stepx,
|
||
|
y2: Math.round(b.y2 / this.stepy) * this.stepy
|
||
|
};
|
||
|
|
||
|
n.w = n.x2 - n.x;
|
||
|
n.h = n.y2 - n.y;
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('grid',GridFilter);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* RatioFilter
|
||
|
* implements aspectRatio locking
|
||
|
*/
|
||
|
var RatioFilter = function(){
|
||
|
this.ratio = 0;
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(RatioFilter.prototype,{
|
||
|
tag: 'ratio',
|
||
|
priority: 15,
|
||
|
offsetFromCorner: function(corner,box,b){
|
||
|
var w = box[0], h = box[1];
|
||
|
switch(corner){
|
||
|
case 'bl': return [ b.x2 - w, b.y, w, h ];
|
||
|
case 'tl': return [ b.x2 - w , b.y2 - h, w, h ];
|
||
|
case 'br': return [ b.x, b.y, w, h ];
|
||
|
case 'tr': return [ b.x, b.y2 - h, w, h ];
|
||
|
}
|
||
|
},
|
||
|
getBoundRatio: function(b,quad){
|
||
|
var box = Jcrop.getLargestBox(this.ratio,b.w,b.h);
|
||
|
return Jcrop.wrapFromXywh(this.offsetFromCorner(quad,box,b));
|
||
|
},
|
||
|
getQuadrant: function(s){
|
||
|
var relx = s.opposite[0]-s.offsetx
|
||
|
var rely = s.opposite[1]-s.offsety;
|
||
|
|
||
|
if ((relx < 0) && (rely < 0)) return 'br';
|
||
|
else if ((relx >= 0) && (rely >= 0)) return 'tl';
|
||
|
else if ((relx < 0) && (rely >= 0)) return 'tr';
|
||
|
return 'bl';
|
||
|
},
|
||
|
filter: function(b,ord,sel){
|
||
|
|
||
|
if (!this.ratio) return b;
|
||
|
|
||
|
var rt = b.w / b.h;
|
||
|
var st = sel.state;
|
||
|
|
||
|
var quad = st? this.getQuadrant(st): 'br';
|
||
|
ord = ord || 'se';
|
||
|
|
||
|
if (ord == 'move') return b;
|
||
|
|
||
|
switch(ord) {
|
||
|
case 'n':
|
||
|
b.x2 = this.elw;
|
||
|
b.w = b.x2 - b.x;
|
||
|
quad = 'tr';
|
||
|
break;
|
||
|
case 's':
|
||
|
b.x2 = this.elw;
|
||
|
b.w = b.x2 - b.x;
|
||
|
quad = 'br';
|
||
|
break;
|
||
|
case 'e':
|
||
|
b.y2 = this.elh;
|
||
|
b.h = b.y2 - b.y;
|
||
|
quad = 'br';
|
||
|
break;
|
||
|
case 'w':
|
||
|
b.y2 = this.elh;
|
||
|
b.h = b.y2 - b.y;
|
||
|
quad = 'bl';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return this.getBoundRatio(b,quad);
|
||
|
},
|
||
|
refresh: function(sel){
|
||
|
this.ratio = sel.aspectRatio;
|
||
|
this.elw = sel.core.container.width();
|
||
|
this.elh = sel.core.container.height();
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('ratio',RatioFilter);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* RoundFilter
|
||
|
* rounds coordinate values to integers
|
||
|
*/
|
||
|
var RoundFilter = function(){
|
||
|
this.core = null;
|
||
|
};
|
||
|
$.extend(RoundFilter.prototype,{
|
||
|
tag: 'round',
|
||
|
priority: 90,
|
||
|
filter: function(b){
|
||
|
|
||
|
var n = {
|
||
|
x: Math.round(b.x),
|
||
|
y: Math.round(b.y),
|
||
|
x2: Math.round(b.x2),
|
||
|
y2: Math.round(b.y2)
|
||
|
};
|
||
|
|
||
|
n.w = n.x2 - n.x;
|
||
|
n.h = n.y2 - n.y;
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('round',RoundFilter);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* ShadeFilter
|
||
|
* A filter that implements div-based shading on any element
|
||
|
*
|
||
|
* The shading you see is actually four semi-opaque divs
|
||
|
* positioned inside the container, around the selection
|
||
|
*/
|
||
|
var ShadeFilter = function(opacity,color){
|
||
|
this.color = color || 'black';
|
||
|
this.opacity = opacity || 0.5;
|
||
|
this.core = null;
|
||
|
this.shades = {};
|
||
|
};
|
||
|
$.extend(ShadeFilter.prototype,{
|
||
|
tag: 'shader',
|
||
|
fade: true,
|
||
|
fadeEasing: 'swing',
|
||
|
fadeSpeed: 320,
|
||
|
priority: 95,
|
||
|
init: function(){
|
||
|
var t = this;
|
||
|
|
||
|
if (!t.attached) {
|
||
|
t.visible = false;
|
||
|
|
||
|
t.container = $('<div />').addClass(t.core.opt.css_shades)
|
||
|
.prependTo(this.core.container).hide();
|
||
|
|
||
|
t.elh = this.core.container.height();
|
||
|
t.elw = this.core.container.width();
|
||
|
|
||
|
t.shades = {
|
||
|
top: t.createShade(),
|
||
|
right: t.createShade(),
|
||
|
left: t.createShade(),
|
||
|
bottom: t.createShade()
|
||
|
};
|
||
|
|
||
|
t.attached = true;
|
||
|
}
|
||
|
},
|
||
|
destroy: function(){
|
||
|
this.container.remove();
|
||
|
},
|
||
|
setColor: function(color,instant){
|
||
|
var t = this;
|
||
|
|
||
|
if (color == t.color) return t;
|
||
|
|
||
|
this.color = color;
|
||
|
var colorfade = Jcrop.supportsColorFade();
|
||
|
$.each(t.shades,function(u,i){
|
||
|
if (!t.fade || instant || !colorfade) i.css('backgroundColor',color);
|
||
|
else i.animate({backgroundColor:color},{queue:false,duration:t.fadeSpeed,easing:t.fadeEasing});
|
||
|
});
|
||
|
return t;
|
||
|
},
|
||
|
setOpacity: function(opacity,instant){
|
||
|
var t = this;
|
||
|
|
||
|
if (opacity == t.opacity) return t;
|
||
|
|
||
|
t.opacity = opacity;
|
||
|
$.each(t.shades,function(u,i){
|
||
|
if (!t.fade || instant) i.css({opacity:opacity});
|
||
|
else i.animate({opacity:opacity},{queue:false,duration:t.fadeSpeed,easing:t.fadeEasing});
|
||
|
});
|
||
|
return t;
|
||
|
},
|
||
|
createShade: function(){
|
||
|
return $('<div />').css({
|
||
|
position: 'absolute',
|
||
|
backgroundColor: this.color,
|
||
|
opacity: this.opacity
|
||
|
}).appendTo(this.container);
|
||
|
},
|
||
|
refresh: function(sel){
|
||
|
var m = this.core, s = this.shades;
|
||
|
|
||
|
this.setColor(sel.bgColor?sel.bgColor:this.core.opt.bgColor);
|
||
|
this.setOpacity(sel.bgOpacity?sel.bgOpacity:this.core.opt.bgOpacity);
|
||
|
|
||
|
this.elh = m.container.height();
|
||
|
this.elw = m.container.width();
|
||
|
s.right.css('height',this.elh+'px');
|
||
|
s.left.css('height',this.elh+'px');
|
||
|
},
|
||
|
filter: function(b,ord,sel){
|
||
|
|
||
|
if (!sel.active) return b;
|
||
|
|
||
|
var t = this,
|
||
|
s = t.shades;
|
||
|
|
||
|
s.top.css({
|
||
|
left: Math.round(b.x)+'px',
|
||
|
width: Math.round(b.w)+'px',
|
||
|
height: Math.round(b.y)+'px'
|
||
|
});
|
||
|
s.bottom.css({
|
||
|
top: Math.round(b.y2)+'px',
|
||
|
left: Math.round(b.x)+'px',
|
||
|
width: Math.round(b.w)+'px',
|
||
|
height: (t.elh-Math.round(b.y2))+'px'
|
||
|
});
|
||
|
s.right.css({
|
||
|
left: Math.round(b.x2)+'px',
|
||
|
width: (t.elw-Math.round(b.x2))+'px'
|
||
|
});
|
||
|
s.left.css({
|
||
|
width: Math.round(b.x)+'px'
|
||
|
});
|
||
|
|
||
|
if (!t.visible) {
|
||
|
t.container.show();
|
||
|
t.visible = true;
|
||
|
}
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerFilter('shader',ShadeFilter);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* CanvasAnimator
|
||
|
* manages smooth cropping animation
|
||
|
*
|
||
|
* This object is called internally to manage animation.
|
||
|
* An in-memory div is animated and a progress callback
|
||
|
* is used to update the selection coordinates of the
|
||
|
* visible selection in realtime.
|
||
|
*/
|
||
|
var CanvasAnimator = function(stage){
|
||
|
this.stage = stage;
|
||
|
this.core = stage.core;
|
||
|
this.cloneStagePosition();
|
||
|
};
|
||
|
|
||
|
CanvasAnimator.prototype = {
|
||
|
|
||
|
cloneStagePosition: function(){
|
||
|
var s = this.stage;
|
||
|
this.angle = s.angle;
|
||
|
this.scale = s.scale;
|
||
|
this.offset = s.offset;
|
||
|
},
|
||
|
|
||
|
getElement: function(){
|
||
|
var s = this.stage;
|
||
|
|
||
|
return $('<div />')
|
||
|
.css({
|
||
|
position: 'absolute',
|
||
|
top: s.offset[0]+'px',
|
||
|
left: s.offset[1]+'px',
|
||
|
width: s.angle+'px',
|
||
|
height: s.scale+'px'
|
||
|
});
|
||
|
},
|
||
|
|
||
|
animate: function(cb){
|
||
|
var t = this;
|
||
|
|
||
|
this.scale = this.stage.boundScale(this.scale);
|
||
|
t.stage.triggerEvent('croprotstart');
|
||
|
|
||
|
t.getElement().animate({
|
||
|
top: t.offset[0]+'px',
|
||
|
left: t.offset[1]+'px',
|
||
|
width: t.angle+'px',
|
||
|
height: t.scale+'px'
|
||
|
},{
|
||
|
easing: t.core.opt.animEasing,
|
||
|
duration: t.core.opt.animDuration,
|
||
|
complete: function(){
|
||
|
t.stage.triggerEvent('croprotend');
|
||
|
(typeof cb == 'function') && cb.call(this);
|
||
|
},
|
||
|
progress: function(anim){
|
||
|
var props = {}, i, tw = anim.tweens;
|
||
|
|
||
|
for(i=0;i<tw.length;i++){
|
||
|
props[tw[i].prop] = tw[i].now; }
|
||
|
|
||
|
t.stage.setAngle(props.width)
|
||
|
.setScale(props.height)
|
||
|
.setOffset(props.top,props.left)
|
||
|
.redraw();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
};
|
||
|
Jcrop.stage.Canvas.prototype.getAnimator = function(){
|
||
|
return new CanvasAnimator(this);
|
||
|
};
|
||
|
Jcrop.registerComponent('CanvasAnimator',CanvasAnimator);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* CropAnimator
|
||
|
* manages smooth cropping animation
|
||
|
*
|
||
|
* This object is called internally to manage animation.
|
||
|
* An in-memory div is animated and a progress callback
|
||
|
* is used to update the selection coordinates of the
|
||
|
* visible selection in realtime.
|
||
|
*/
|
||
|
// var CropAnimator = function(selection){{{
|
||
|
var CropAnimator = function(selection){
|
||
|
this.selection = selection;
|
||
|
this.core = selection.core;
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
CropAnimator.prototype = {
|
||
|
|
||
|
getElement: function(){
|
||
|
var b = this.selection.get();
|
||
|
|
||
|
return $('<div />')
|
||
|
.css({
|
||
|
position: 'absolute',
|
||
|
top: b.y+'px',
|
||
|
left: b.x+'px',
|
||
|
width: b.w+'px',
|
||
|
height: b.h+'px'
|
||
|
});
|
||
|
},
|
||
|
|
||
|
animate: function(x,y,w,h,cb){
|
||
|
var t = this;
|
||
|
|
||
|
t.selection.allowResize(false);
|
||
|
|
||
|
t.getElement().animate({
|
||
|
top: y+'px',
|
||
|
left: x+'px',
|
||
|
width: w+'px',
|
||
|
height: h+'px'
|
||
|
},{
|
||
|
easing: t.core.opt.animEasing,
|
||
|
duration: t.core.opt.animDuration,
|
||
|
complete: function(){
|
||
|
t.selection.allowResize(true);
|
||
|
cb && cb.call(this);
|
||
|
},
|
||
|
progress: function(anim){
|
||
|
var props = {}, i, tw = anim.tweens;
|
||
|
|
||
|
for(i=0;i<tw.length;i++){
|
||
|
props[tw[i].prop] = tw[i].now; }
|
||
|
|
||
|
var b = {
|
||
|
x: parseInt(props.left),
|
||
|
y: parseInt(props.top),
|
||
|
w: parseInt(props.width),
|
||
|
h: parseInt(props.height)
|
||
|
};
|
||
|
|
||
|
b.x2 = b.x + b.w;
|
||
|
b.y2 = b.y + b.h;
|
||
|
|
||
|
t.selection.updateRaw(b,'se');
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
};
|
||
|
Jcrop.registerComponent('Animator',CropAnimator);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* DragState
|
||
|
* an object that handles dragging events
|
||
|
*
|
||
|
* This object is used by the built-in selection object to
|
||
|
* track a dragging operation on a selection
|
||
|
*/
|
||
|
// var DragState = function(e,selection,ord){{{
|
||
|
var DragState = function(e,selection,ord){
|
||
|
var t = this;
|
||
|
|
||
|
t.x = e.pageX;
|
||
|
t.y = e.pageY;
|
||
|
|
||
|
t.selection = selection;
|
||
|
t.eventTarget = selection.core.opt.dragEventTarget;
|
||
|
t.orig = selection.get();
|
||
|
|
||
|
selection.callFilterFunction('refresh');
|
||
|
|
||
|
var p = selection.core.container.position();
|
||
|
t.elx = p.left;
|
||
|
t.ely = p.top;
|
||
|
|
||
|
t.offsetx = 0;
|
||
|
t.offsety = 0;
|
||
|
t.ord = ord;
|
||
|
t.opposite = t.getOppositeCornerOffset();
|
||
|
|
||
|
t.initEvents(e);
|
||
|
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
DragState.prototype = {
|
||
|
// getOppositeCornerOffset: function(){{{
|
||
|
// Calculate relative offset of locked corner
|
||
|
getOppositeCornerOffset: function(){
|
||
|
|
||
|
var o = this.orig;
|
||
|
var relx = this.x - this.elx - o.x;
|
||
|
var rely = this.y - this.ely - o.y;
|
||
|
|
||
|
switch(this.ord){
|
||
|
case 'nw':
|
||
|
case 'w':
|
||
|
return [ o.w - relx, o.h - rely ];
|
||
|
return [ o.x + o.w, o.y + o.h ];
|
||
|
|
||
|
case 'sw':
|
||
|
return [ o.w - relx, -rely ];
|
||
|
return [ o.x + o.w, o.y ];
|
||
|
|
||
|
case 'se':
|
||
|
case 's':
|
||
|
case 'e':
|
||
|
return [ -relx, -rely ];
|
||
|
return [ o.x, o.y ];
|
||
|
|
||
|
case 'ne':
|
||
|
case 'n':
|
||
|
return [ -relx, o.h - rely ];
|
||
|
return [ o.w, o.y + o.h ];
|
||
|
}
|
||
|
|
||
|
return [ null, null ];
|
||
|
},
|
||
|
// }}}
|
||
|
// initEvents: function(e){{{
|
||
|
initEvents: function(e){
|
||
|
$(this.eventTarget)
|
||
|
.on('mousemove.jcrop',this.createDragHandler())
|
||
|
.on('mouseup.jcrop',this.createStopHandler());
|
||
|
},
|
||
|
// }}}
|
||
|
// dragEvent: function(e){{{
|
||
|
dragEvent: function(e){
|
||
|
this.offsetx = e.pageX - this.x;
|
||
|
this.offsety = e.pageY - this.y;
|
||
|
this.selection.updateRaw(this.getBox(),this.ord);
|
||
|
},
|
||
|
// }}}
|
||
|
// endDragEvent: function(e){{{
|
||
|
endDragEvent: function(e){
|
||
|
var sel = this.selection;
|
||
|
sel.core.container.removeClass('jcrop-dragging');
|
||
|
sel.element.trigger('cropend',[sel,sel.core.unscale(sel.get())]);
|
||
|
sel.focus();
|
||
|
},
|
||
|
// }}}
|
||
|
// createStopHandler: function(){{{
|
||
|
createStopHandler: function(){
|
||
|
var t = this;
|
||
|
return function(e){
|
||
|
$(t.eventTarget).off('.jcrop');
|
||
|
t.endDragEvent(e);
|
||
|
return false;
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// createDragHandler: function(){{{
|
||
|
createDragHandler: function(){
|
||
|
var t = this;
|
||
|
return function(e){
|
||
|
t.dragEvent(e);
|
||
|
return false;
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
//update: function(x,y){{{
|
||
|
update: function(x,y){
|
||
|
var t = this;
|
||
|
t.offsetx = x - t.x;
|
||
|
t.offsety = y - t.y;
|
||
|
},
|
||
|
//}}}
|
||
|
//resultWrap: function(d){{{
|
||
|
resultWrap: function(d){
|
||
|
var b = {
|
||
|
x: Math.min(d[0],d[2]),
|
||
|
y: Math.min(d[1],d[3]),
|
||
|
x2: Math.max(d[0],d[2]),
|
||
|
y2: Math.max(d[1],d[3])
|
||
|
};
|
||
|
|
||
|
b.w = b.x2 - b.x;
|
||
|
b.h = b.y2 - b.y;
|
||
|
|
||
|
return b;
|
||
|
},
|
||
|
//}}}
|
||
|
//getBox: function(){{{
|
||
|
getBox: function(){
|
||
|
var t = this;
|
||
|
var o = t.orig;
|
||
|
var _c = { x2: o.x + o.w, y2: o.y + o.h };
|
||
|
switch(t.ord){
|
||
|
case 'n': return t.resultWrap([ o.x, t.offsety + o.y, _c.x2, _c.y2 ]);
|
||
|
case 's': return t.resultWrap([ o.x, o.y, _c.x2, t.offsety + _c.y2 ]);
|
||
|
case 'e': return t.resultWrap([ o.x, o.y, t.offsetx + _c.x2, _c.y2 ]);
|
||
|
case 'w': return t.resultWrap([ o.x + t.offsetx, o.y, _c.x2, _c.y2 ]);
|
||
|
case 'sw': return t.resultWrap([ t.offsetx + o.x, o.y, _c.x2, t.offsety + _c.y2 ]);
|
||
|
case 'se': return t.resultWrap([ o.x, o.y, t.offsetx + _c.x2, t.offsety + _c.y2 ]);
|
||
|
case 'ne': return t.resultWrap([ o.x, t.offsety + o.y, t.offsetx + _c.x2, _c.y2 ]);
|
||
|
case 'nw': return t.resultWrap([ t.offsetx + o.x, t.offsety + o.y, _c.x2, _c.y2 ]);
|
||
|
case 'move':
|
||
|
_c.nx = o.x + t.offsetx;
|
||
|
_c.ny = o.y + t.offsety;
|
||
|
return t.resultWrap([ _c.nx, _c.ny, _c.nx + o.w, _c.ny + o.h ]);
|
||
|
}
|
||
|
}
|
||
|
//}}}
|
||
|
};
|
||
|
Jcrop.registerComponent('DragState',DragState);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* EventManager
|
||
|
* provides internal event support
|
||
|
*/
|
||
|
var EventManager = function(core){
|
||
|
this.core = core;
|
||
|
};
|
||
|
EventManager.prototype = {
|
||
|
on: function(n,cb){ $(this).on(n,cb); },
|
||
|
off: function(n){ $(this).off(n); },
|
||
|
trigger: function(n){ $(this).trigger(n); }
|
||
|
};
|
||
|
Jcrop.registerComponent('EventManager',EventManager);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Image Loader
|
||
|
* Reliably pre-loads images
|
||
|
*/
|
||
|
// var ImageLoader = function(src,element,cb){{{
|
||
|
var ImageLoader = function(src,element,cb){
|
||
|
this.src = src;
|
||
|
if (!element) element = new Image;
|
||
|
this.element = element;
|
||
|
this.callback = cb;
|
||
|
this.load();
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
$.extend(ImageLoader,{
|
||
|
// attach: function(el,cb){{{
|
||
|
attach: function(el,cb){
|
||
|
return new ImageLoader(el.src,el,cb);
|
||
|
},
|
||
|
// }}}
|
||
|
// prototype: {{{
|
||
|
prototype: {
|
||
|
getDimensions: function(){
|
||
|
var el = this.element;
|
||
|
|
||
|
if (el.naturalWidth)
|
||
|
return [ el.naturalWidth, el. naturalHeight ];
|
||
|
|
||
|
if (el.width)
|
||
|
return [ el.width, el.height ];
|
||
|
|
||
|
return null;
|
||
|
},
|
||
|
fireCallback: function(){
|
||
|
this.element.onload = null;
|
||
|
if (typeof this.callback == 'function')
|
||
|
this.callback.apply(this,this.getDimensions());
|
||
|
},
|
||
|
isLoaded: function(){
|
||
|
return this.element.complete;
|
||
|
},
|
||
|
load: function(){
|
||
|
var t = this;
|
||
|
var el = t.element;
|
||
|
|
||
|
el.src = t.src;
|
||
|
|
||
|
if (t.isLoaded()) t.fireCallback();
|
||
|
else t.element.onload = function(e){
|
||
|
t.fireCallback();
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
Jcrop.registerComponent('ImageLoader',ImageLoader);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* JcropTouch
|
||
|
* Detects and enables mobile touch support
|
||
|
*/
|
||
|
// var JcropTouch = function(core){{{
|
||
|
var JcropTouch = function(core){
|
||
|
this.core = core;
|
||
|
this.init();
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
$.extend(JcropTouch,{
|
||
|
// support: function(){{{
|
||
|
support: function(){
|
||
|
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch)
|
||
|
return true;
|
||
|
},
|
||
|
// }}}
|
||
|
prototype: {
|
||
|
// init: function(){{{
|
||
|
init: function(){
|
||
|
var t = this,
|
||
|
p = $.Jcrop.component.DragState.prototype;
|
||
|
|
||
|
// A bit of an ugly hack to make sure we modify prototype
|
||
|
// only once, store a key on the prototype
|
||
|
if (!p.touch) {
|
||
|
t.initEvents();
|
||
|
t.shimDragState();
|
||
|
t.shimStageDrag();
|
||
|
p.touch = true;
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// shimDragState: function(){{{
|
||
|
shimDragState: function(){
|
||
|
var t = this;
|
||
|
$.Jcrop.component.DragState.prototype.initEvents = function(e){
|
||
|
|
||
|
// Attach subsequent drag event handlers based on initial
|
||
|
// event type - avoids collecting "pseudo-mouse" events
|
||
|
// generated by some mobile browsers in some circumstances
|
||
|
if (e.type.substr(0,5) == 'touch') {
|
||
|
|
||
|
$(this.eventTarget)
|
||
|
.on('touchmove.jcrop.jcrop-touch',t.dragWrap(this.createDragHandler()))
|
||
|
.on('touchend.jcrop.jcrop-touch',this.createStopHandler());
|
||
|
|
||
|
}
|
||
|
|
||
|
// For other events, use the mouse handlers that
|
||
|
// the default DragState.initEvents() method sets...
|
||
|
else {
|
||
|
|
||
|
$(this.eventTarget)
|
||
|
.on('mousemove.jcrop',this.createDragHandler())
|
||
|
.on('mouseup.jcrop',this.createStopHandler());
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// shimStageDrag: function(){{{
|
||
|
shimStageDrag: function(){
|
||
|
this.core.container
|
||
|
.addClass('jcrop-touch')
|
||
|
.on('touchstart.jcrop.jcrop-stage',this.dragWrap(this.core.ui.manager.startDragHandler()));
|
||
|
},
|
||
|
// }}}
|
||
|
// dragWrap: function(cb){{{
|
||
|
dragWrap: function(cb){
|
||
|
return function(e){
|
||
|
e.preventDefault();
|
||
|
e.stopPropagation();
|
||
|
if (e.type.substr(0,5) == 'touch') {
|
||
|
e.pageX = e.originalEvent.changedTouches[0].pageX;
|
||
|
e.pageY = e.originalEvent.changedTouches[0].pageY;
|
||
|
return cb(e);
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// initEvents: function(){{{
|
||
|
initEvents: function(){
|
||
|
var t = this, c = t.core;
|
||
|
|
||
|
c.container.on(
|
||
|
'touchstart.jcrop.jcrop-touch',
|
||
|
'.'+c.opt.css_drag,
|
||
|
t.dragWrap(c.startDrag())
|
||
|
);
|
||
|
}
|
||
|
// }}}
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerComponent('Touch',JcropTouch);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* KeyWatcher
|
||
|
* provides keyboard support
|
||
|
*/
|
||
|
// var KeyWatcher = function(core){{{
|
||
|
var KeyWatcher = function(core){
|
||
|
this.core = core;
|
||
|
this.init();
|
||
|
};
|
||
|
// }}}
|
||
|
$.extend(KeyWatcher,{
|
||
|
// defaults: {{{
|
||
|
defaults: {
|
||
|
eventName: 'keydown.jcrop',
|
||
|
passthru: [ 9 ],
|
||
|
debug: false
|
||
|
},
|
||
|
// }}}
|
||
|
prototype: {
|
||
|
// init: function(){{{
|
||
|
init: function(){
|
||
|
$.extend(this,KeyWatcher.defaults);
|
||
|
this.enable();
|
||
|
},
|
||
|
// }}}
|
||
|
// disable: function(){{{
|
||
|
disable: function(){
|
||
|
this.core.container.off(this.eventName);
|
||
|
},
|
||
|
// }}}
|
||
|
// enable: function(){{{
|
||
|
enable: function(){
|
||
|
var t = this, m = t.core;
|
||
|
m.container.on(t.eventName,function(e){
|
||
|
var nudge = e.shiftKey? 16: 2;
|
||
|
|
||
|
if ($.inArray(e.keyCode,t.passthru) >= 0)
|
||
|
return true;
|
||
|
|
||
|
switch(e.keyCode){
|
||
|
case 37: m.nudge(-nudge,0); break;
|
||
|
case 38: m.nudge(0,-nudge); break;
|
||
|
case 39: m.nudge(nudge,0); break;
|
||
|
case 40: m.nudge(0,nudge); break;
|
||
|
|
||
|
case 46:
|
||
|
case 8:
|
||
|
m.requestDelete();
|
||
|
return false;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if (t.debug) console.log('keycode: ' + e.keyCode);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!e.metaKey && !e.ctrlKey)
|
||
|
e.preventDefault();
|
||
|
});
|
||
|
}
|
||
|
// }}}
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerComponent('Keyboard',KeyWatcher);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Selection
|
||
|
* Built-in selection object
|
||
|
*/
|
||
|
var Selection = function(){};
|
||
|
|
||
|
$.extend(Selection,{
|
||
|
// defaults: {{{
|
||
|
defaults: {
|
||
|
minSize: [ 8, 8 ],
|
||
|
maxSize: [ 0, 0 ],
|
||
|
aspectRatio: 0,
|
||
|
edge: { n: 0, s: 0, e: 0, w: 0 },
|
||
|
bgColor: null,
|
||
|
bgOpacity: null,
|
||
|
last: null,
|
||
|
|
||
|
state: null,
|
||
|
active: true,
|
||
|
linked: true,
|
||
|
canDelete: true,
|
||
|
canDrag: true,
|
||
|
canResize: true,
|
||
|
canSelect: true
|
||
|
},
|
||
|
// }}}
|
||
|
prototype: {
|
||
|
// init: function(core){{{
|
||
|
init: function(core){
|
||
|
this.core = core;
|
||
|
this.startup();
|
||
|
this.linked = this.core.opt.linked;
|
||
|
this.attach();
|
||
|
this.setOptions(this.core.opt);
|
||
|
core.container.trigger('cropcreate',[this]);
|
||
|
},
|
||
|
// }}}
|
||
|
// attach: function(){{{
|
||
|
attach: function(){
|
||
|
// For extending init() sequence
|
||
|
},
|
||
|
// }}}
|
||
|
// startup: function(){{{
|
||
|
startup: function(){
|
||
|
var t = this, o = t.core.opt;
|
||
|
$.extend(t,Selection.defaults);
|
||
|
t.filter = t.core.getDefaultFilters();
|
||
|
|
||
|
t.element = $('<div />').addClass(o.css_selection).data({ selection: t });
|
||
|
t.frame = $('<button />').addClass(o.css_button).data('ord','move').attr('type','button');
|
||
|
t.element.append(t.frame).appendTo(t.core.container);
|
||
|
|
||
|
// IE background/draggable hack
|
||
|
if (t.core.opt.is_msie) t.frame.css({
|
||
|
opacity: 0,
|
||
|
backgroundColor: 'white'
|
||
|
});
|
||
|
|
||
|
t.insertElements();
|
||
|
|
||
|
// Bind focus and blur events for this selection
|
||
|
t.frame.on('focus.jcrop',function(e){
|
||
|
t.core.setSelection(t);
|
||
|
t.element.trigger('cropfocus',t);
|
||
|
t.element.addClass('jcrop-focus');
|
||
|
}).on('blur.jcrop',function(e){
|
||
|
t.element.removeClass('jcrop-focus');
|
||
|
t.element.trigger('cropblur',t);
|
||
|
});
|
||
|
},
|
||
|
// }}}
|
||
|
// propagate: [{{{
|
||
|
propagate: [
|
||
|
'canDelete', 'canDrag', 'canResize', 'canSelect',
|
||
|
'minSize', 'maxSize', 'aspectRatio', 'edge'
|
||
|
],
|
||
|
// }}}
|
||
|
// setOptions: function(opt){{{
|
||
|
setOptions: function(opt){
|
||
|
Jcrop.propagate(this.propagate,opt,this);
|
||
|
this.refresh();
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// refresh: function(){{{
|
||
|
refresh: function(){
|
||
|
this.allowResize();
|
||
|
this.allowDrag();
|
||
|
this.allowSelect();
|
||
|
this.callFilterFunction('refresh');
|
||
|
this.updateRaw(this.get(),'se');
|
||
|
},
|
||
|
// }}}
|
||
|
// callFilterFunction: function(f,args){{{
|
||
|
callFilterFunction: function(f,args){
|
||
|
for(var i=0;i<this.filter.length;i++)
|
||
|
if (this.filter[i][f]) this.filter[i][f](this);
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
//addFilter: function(filter){{{
|
||
|
addFilter: function(filter){
|
||
|
filter.core = this.core;
|
||
|
if (!this.hasFilter(filter)) {
|
||
|
this.filter.push(filter);
|
||
|
this.sortFilters();
|
||
|
if (filter.init) filter.init();
|
||
|
this.refresh();
|
||
|
}
|
||
|
},
|
||
|
//}}}
|
||
|
// hasFilter: function(filter){{{
|
||
|
hasFilter: function(filter){
|
||
|
var i, f = this.filter, n = [];
|
||
|
for(i=0;i<f.length;i++) if (f[i] === filter) return true;
|
||
|
},
|
||
|
// }}}
|
||
|
// sortFilters: function(){{{
|
||
|
sortFilters: function(){
|
||
|
this.filter.sort(
|
||
|
function(x,y){ return x.priority - y.priority; }
|
||
|
);
|
||
|
},
|
||
|
// }}}
|
||
|
//clearFilters: function(){{{
|
||
|
clearFilters: function(){
|
||
|
var i, f = this.filter;
|
||
|
|
||
|
for(var i=0;i<f.length;i++)
|
||
|
if (f[i].destroy) f[i].destroy();
|
||
|
|
||
|
this.filter = [];
|
||
|
},
|
||
|
//}}}
|
||
|
// removeFiltersByTag: function(tag){{{
|
||
|
removeFilter: function(tag){
|
||
|
var i, f = this.filter, n = [];
|
||
|
|
||
|
for(var i=0;i<f.length;i++)
|
||
|
if ((f[i].tag && (f[i].tag == tag)) || (tag === f[i])){
|
||
|
if (f[i].destroy) f[i].destroy();
|
||
|
}
|
||
|
else n.push(f[i]);
|
||
|
|
||
|
this.filter = n;
|
||
|
},
|
||
|
// }}}
|
||
|
// runFilters: function(b,ord){{{
|
||
|
runFilters: function(b,ord){
|
||
|
for(var i=0;i<this.filter.length;i++)
|
||
|
b = this.filter[i].filter(b,ord,this);
|
||
|
return b;
|
||
|
},
|
||
|
// }}}
|
||
|
//endDrag: function(){{{
|
||
|
endDrag: function(){
|
||
|
if (this.state) {
|
||
|
$(document.body).off('.jcrop');
|
||
|
this.focus();
|
||
|
this.state = null;
|
||
|
}
|
||
|
},
|
||
|
//}}}
|
||
|
// startDrag: function(e,ord){{{
|
||
|
startDrag: function(e,ord){
|
||
|
var t = this;
|
||
|
var m = t.core;
|
||
|
|
||
|
ord = ord || $(e.target).data('ord');
|
||
|
|
||
|
this.focus();
|
||
|
|
||
|
if ((ord == 'move') && t.element.hasClass(t.core.opt.css_nodrag))
|
||
|
return false;
|
||
|
|
||
|
this.state = new Jcrop.component.DragState(e,this,ord);
|
||
|
return false;
|
||
|
},
|
||
|
// }}}
|
||
|
// allowSelect: function(v){{{
|
||
|
allowSelect: function(v){
|
||
|
if (v === undefined) v = this.canSelect;
|
||
|
|
||
|
if (v && this.canSelect) this.frame.attr('disabled',false);
|
||
|
else this.frame.attr('disabled','disabled');
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// allowDrag: function(v){{{
|
||
|
allowDrag: function(v){
|
||
|
var t = this, o = t.core.opt;
|
||
|
if (v == undefined) v = t.canDrag;
|
||
|
|
||
|
if (v && t.canDrag) t.element.removeClass(o.css_nodrag);
|
||
|
else t.element.addClass(o.css_nodrag);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// allowResize: function(v){{{
|
||
|
allowResize: function(v){
|
||
|
var t = this, o = t.core.opt;
|
||
|
if (v == undefined) v = t.canResize;
|
||
|
|
||
|
if (v && t.canResize) t.element.removeClass(o.css_noresize);
|
||
|
else t.element.addClass(o.css_noresize);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// remove: function(){{{
|
||
|
remove: function(){
|
||
|
this.element.trigger('cropremove',this);
|
||
|
this.element.remove();
|
||
|
},
|
||
|
// }}}
|
||
|
// toBack: function(){{{
|
||
|
toBack: function(){
|
||
|
this.active = false;
|
||
|
this.element.removeClass('jcrop-current jcrop-focus');
|
||
|
},
|
||
|
// }}}
|
||
|
// toFront: function(){{{
|
||
|
toFront: function(){
|
||
|
this.active = true;
|
||
|
this.element.addClass('jcrop-current');
|
||
|
this.callFilterFunction('refresh');
|
||
|
this.refresh();
|
||
|
},
|
||
|
// }}}
|
||
|
// redraw: function(b){{{
|
||
|
redraw: function(b){
|
||
|
this.moveTo(b.x,b.y);
|
||
|
this.resize(b.w,b.h);
|
||
|
this.last = b;
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// update: function(b,ord){{{
|
||
|
update: function(b,ord){
|
||
|
return this.updateRaw(this.core.scale(b),ord);
|
||
|
},
|
||
|
// }}}
|
||
|
// update: function(b,ord){{{
|
||
|
updateRaw: function(b,ord){
|
||
|
b = this.runFilters(b,ord);
|
||
|
this.redraw(b);
|
||
|
this.element.trigger('cropmove',[this,this.core.unscale(b)]);
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// animateTo: function(box,cb){{{
|
||
|
animateTo: function(box,cb){
|
||
|
var ca = new Jcrop.component.Animator(this),
|
||
|
b = this.core.scale(Jcrop.wrapFromXywh(box));
|
||
|
|
||
|
ca.animate(b.x,b.y,b.w,b.h,cb);
|
||
|
},
|
||
|
// }}}
|
||
|
// center: function(instant){{{
|
||
|
center: function(instant){
|
||
|
var b = this.get(), m = this.core;
|
||
|
var elw = m.container.width(), elh = m.container.height();
|
||
|
var box = [ (elw-b.w)/2, (elh-b.h)/2, b.w, b.h ];
|
||
|
return this[instant?'setSelect':'animateTo'](box);
|
||
|
},
|
||
|
// }}}
|
||
|
//createElement: function(type,ord){{{
|
||
|
createElement: function(type,ord){
|
||
|
return $('<div />').addClass(type+' ord-'+ord).data('ord',ord);
|
||
|
},
|
||
|
//}}}
|
||
|
//moveTo: function(x,y){{{
|
||
|
moveTo: function(x,y){
|
||
|
this.element.css({top: y+'px', left: x+'px'});
|
||
|
},
|
||
|
//}}}
|
||
|
// blur: function(){{{
|
||
|
blur: function(){
|
||
|
this.element.blur();
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// focus: function(){{{
|
||
|
focus: function(){
|
||
|
this.core.setSelection(this);
|
||
|
this.frame.focus();
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
//resize: function(w,h){{{
|
||
|
resize: function(w,h){
|
||
|
this.element.css({width: w+'px', height: h+'px'});
|
||
|
},
|
||
|
//}}}
|
||
|
//get: function(){{{
|
||
|
get: function(){
|
||
|
var b = this.element,
|
||
|
o = b.position(),
|
||
|
w = b.width(),
|
||
|
h = b.height(),
|
||
|
rv = { x: o.left, y: o.top };
|
||
|
|
||
|
rv.x2 = rv.x + w;
|
||
|
rv.y2 = rv.y + h;
|
||
|
rv.w = w;
|
||
|
rv.h = h;
|
||
|
|
||
|
return rv;
|
||
|
},
|
||
|
//}}}
|
||
|
//insertElements: function(){{{
|
||
|
insertElements: function(){
|
||
|
var t = this, i,
|
||
|
m = t.core,
|
||
|
fr = t.element,
|
||
|
o = t.core.opt,
|
||
|
b = o.borders,
|
||
|
h = o.handles,
|
||
|
d = o.dragbars;
|
||
|
|
||
|
for(i=0; i<d.length; i++)
|
||
|
fr.append(t.createElement(o.css_dragbars,d[i]));
|
||
|
|
||
|
for(i=0; i<h.length; i++)
|
||
|
fr.append(t.createElement(o.css_handles,h[i]));
|
||
|
|
||
|
for(i=0; i<b.length; i++)
|
||
|
fr.append(t.createElement(o.css_borders,b[i]));
|
||
|
}
|
||
|
//}}}
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerComponent('Selection',Selection);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* StageDrag
|
||
|
* Facilitates dragging
|
||
|
*/
|
||
|
// var StageDrag = function(manager,opt){{{
|
||
|
var StageDrag = function(manager,opt){
|
||
|
$.extend(this,StageDrag.defaults,opt || {});
|
||
|
this.manager = manager;
|
||
|
this.core = manager.core;
|
||
|
};
|
||
|
// }}}
|
||
|
// StageDrag.defaults = {{{
|
||
|
StageDrag.defaults = {
|
||
|
offset: [ -8, -8 ],
|
||
|
active: true,
|
||
|
minsize: [ 20, 20 ]
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
$.extend(StageDrag.prototype,{
|
||
|
// start: function(e){{{
|
||
|
start: function(e){
|
||
|
var c = this.core;
|
||
|
|
||
|
// Do nothing if allowSelect is off
|
||
|
if (!c.opt.allowSelect) return;
|
||
|
|
||
|
// Also do nothing if we can't draw any more selections
|
||
|
if (c.opt.multi && c.opt.multiMax && (c.ui.multi.length >= c.opt.multiMax)) return false;
|
||
|
|
||
|
// calculate a few variables for this drag operation
|
||
|
var o = $(e.currentTarget).offset();
|
||
|
var origx = e.pageX - o.left + this.offset[0];
|
||
|
var origy = e.pageY - o.top + this.offset[1];
|
||
|
var m = c.ui.multi;
|
||
|
|
||
|
// Determine newly dragged crop behavior if multi disabled
|
||
|
if (!c.opt.multi) {
|
||
|
// For multiCleaanup true, remove all existing selections
|
||
|
if (c.opt.multiCleanup){
|
||
|
for(var i=0;i<m.length;i++) m[i].remove();
|
||
|
c.ui.multi = [];
|
||
|
}
|
||
|
// If not, only remove the currently active selection
|
||
|
else {
|
||
|
c.removeSelection(c.ui.selection);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c.container.addClass('jcrop-dragging');
|
||
|
|
||
|
// Create the new selection
|
||
|
var sel = c.newSelection()
|
||
|
// and position it
|
||
|
.updateRaw(Jcrop.wrapFromXywh([origx,origy,1,1]));
|
||
|
|
||
|
sel.element.trigger('cropstart',[sel,this.core.unscale(sel.get())]);
|
||
|
|
||
|
return sel.startDrag(e,'se');
|
||
|
},
|
||
|
// }}}
|
||
|
// end: function(x,y){{{
|
||
|
end: function(x,y){
|
||
|
this.drag(x,y);
|
||
|
var b = this.sel.get();
|
||
|
|
||
|
this.core.container.removeClass('jcrop-dragging');
|
||
|
|
||
|
if ((b.w < this.minsize[0]) || (b.h < this.minsize[1]))
|
||
|
this.core.requestDelete();
|
||
|
|
||
|
else this.sel.focus();
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
Jcrop.registerComponent('StageDrag',StageDrag);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* StageManager
|
||
|
* Provides basic stage-specific functionality
|
||
|
*/
|
||
|
// var StageManager = function(core){{{
|
||
|
var StageManager = function(core){
|
||
|
this.core = core;
|
||
|
this.ui = core.ui;
|
||
|
this.init();
|
||
|
};
|
||
|
// }}}
|
||
|
|
||
|
$.extend(StageManager.prototype,{
|
||
|
// init: function(){{{
|
||
|
init: function(){
|
||
|
this.setupEvents();
|
||
|
this.dragger = new StageDrag(this);
|
||
|
},
|
||
|
// }}}
|
||
|
// tellConfigUpdate: function(options){{{
|
||
|
tellConfigUpdate: function(options){
|
||
|
for(var i=0,m=this.ui.multi,l=m.length;i<l;i++)
|
||
|
if (m[i].setOptions && (m[i].linked || (this.core.opt.linkCurrent && m[i] == this.ui.selection)))
|
||
|
m[i].setOptions(options);
|
||
|
},
|
||
|
// }}}
|
||
|
// startDragHandler: function(){{{
|
||
|
startDragHandler: function(){
|
||
|
var t = this;
|
||
|
return function(e){
|
||
|
if (!e.button || t.core.opt.is_ie_lt9) return t.dragger.start(e);
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// removeEvents: function(){{{
|
||
|
removeEvents: function(){
|
||
|
this.core.event.off('.jcrop-stage');
|
||
|
this.core.container.off('.jcrop-stage');
|
||
|
},
|
||
|
// }}}
|
||
|
// shimLegacyHandlers: function(options){{{
|
||
|
// This method uses the legacyHandlers configuration object to
|
||
|
// gracefully wrap old-style Jcrop events with new ones
|
||
|
shimLegacyHandlers: function(options){
|
||
|
var _x = {}, core = this.core, tmp;
|
||
|
|
||
|
$.each(core.opt.legacyHandlers,function(k,i){
|
||
|
if (k in options) {
|
||
|
tmp = options[k];
|
||
|
core.container.off('.jcrop-'+k)
|
||
|
.on(i+'.jcrop.jcrop-'+k,function(e,s,c){
|
||
|
tmp.call(core,c);
|
||
|
});
|
||
|
delete options[k];
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
// }}}
|
||
|
// setupEvents: function(){{{
|
||
|
setupEvents: function(){
|
||
|
var t = this, c = t.core;
|
||
|
|
||
|
c.event.on('configupdate.jcrop-stage',function(e){
|
||
|
t.shimLegacyHandlers(c.opt);
|
||
|
t.tellConfigUpdate(c.opt)
|
||
|
c.container.trigger('cropconfig',[c,c.opt]);
|
||
|
});
|
||
|
|
||
|
this.core.container
|
||
|
.on('mousedown.jcrop.jcrop-stage',this.startDragHandler());
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
Jcrop.registerComponent('StageManager',StageManager);
|
||
|
|
||
|
|
||
|
var Thumbnailer = function(){
|
||
|
};
|
||
|
|
||
|
$.extend(Thumbnailer,{
|
||
|
defaults: {
|
||
|
// Set to a specific Selection object
|
||
|
// If this value is set, the preview will only track that Selection
|
||
|
selection: null,
|
||
|
|
||
|
fading: true,
|
||
|
fadeDelay: 1000,
|
||
|
fadeDuration: 1000,
|
||
|
autoHide: false,
|
||
|
width: 80,
|
||
|
height: 80,
|
||
|
_hiding: null
|
||
|
},
|
||
|
|
||
|
prototype: {
|
||
|
recopyCanvas: function(){
|
||
|
var s = this.core.ui.stage, cxt = s.context;
|
||
|
this.context.putImageData(cxt.getImageData(0,0,s.canvas.width,s.canvas.height),0,0);
|
||
|
},
|
||
|
init: function(core,options){
|
||
|
var t = this;
|
||
|
this.core = core;
|
||
|
$.extend(this,Thumbnailer.defaults,options);
|
||
|
t.initEvents();
|
||
|
t.refresh();
|
||
|
t.insertElements();
|
||
|
if (t.selection) {
|
||
|
t.renderSelection(t.selection);
|
||
|
t.selectionTarget = t.selection.element[0];
|
||
|
} else if (t.core.ui.selection) {
|
||
|
t.renderSelection(t.core.ui.selection);
|
||
|
}
|
||
|
|
||
|
if (t.core.ui.stage.canvas) {
|
||
|
t.context = t.preview[0].getContext('2d');
|
||
|
t.core.container.on('cropredraw',function(e){
|
||
|
t.recopyCanvas();
|
||
|
t.refresh();
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
updateImage: function(imgel){
|
||
|
this.preview.remove();
|
||
|
this.preview = $($.Jcrop.imageClone(imgel));
|
||
|
this.element.append(this.preview);
|
||
|
this.refresh();
|
||
|
return this;
|
||
|
},
|
||
|
insertElements: function(){
|
||
|
this.preview = $($.Jcrop.imageClone(this.core.ui.stage.imgsrc));
|
||
|
|
||
|
this.element = $('<div />').addClass('jcrop-thumb')
|
||
|
.width(this.width).height(this.height)
|
||
|
.append(this.preview)
|
||
|
.appendTo(this.core.container);
|
||
|
},
|
||
|
resize: function(w,h){
|
||
|
this.width = w;
|
||
|
this.height = h;
|
||
|
this.element.width(w).height(h);
|
||
|
this.renderCoords(this.last);
|
||
|
},
|
||
|
refresh: function(){
|
||
|
this.cw = (this.core.opt.xscale * this.core.container.width());
|
||
|
this.ch = (this.core.opt.yscale * this.core.container.height());
|
||
|
if (this.last) {
|
||
|
this.renderCoords(this.last);
|
||
|
}
|
||
|
},
|
||
|
renderCoords: function(c){
|
||
|
var rx = this.width / c.w;
|
||
|
var ry = this.height / c.h;
|
||
|
|
||
|
this.preview.css({
|
||
|
width: Math.round(rx * this.cw) + 'px',
|
||
|
height: Math.round(ry * this.ch) + 'px',
|
||
|
marginLeft: '-' + Math.round(rx * c.x) + 'px',
|
||
|
marginTop: '-' + Math.round(ry * c.y) + 'px'
|
||
|
});
|
||
|
|
||
|
this.last = c;
|
||
|
return this;
|
||
|
},
|
||
|
renderSelection: function(s){
|
||
|
return this.renderCoords(s.core.unscale(s.get()));
|
||
|
},
|
||
|
selectionStart: function(s){
|
||
|
this.renderSelection(s);
|
||
|
},
|
||
|
show: function(){
|
||
|
if (this._hiding) clearTimeout(this._hiding);
|
||
|
|
||
|
if (!this.fading) this.element.stop().css({ opacity: 1 });
|
||
|
else this.element.stop().animate({ opacity: 1 },{ duration: 80, queue: false });
|
||
|
},
|
||
|
hide: function(){
|
||
|
var t = this;
|
||
|
if (!t.fading) t.element.hide();
|
||
|
else t._hiding = setTimeout(function(){
|
||
|
t._hiding = null;
|
||
|
t.element.stop().animate({ opacity: 0 },{ duration: t.fadeDuration, queue: false });
|
||
|
},t.fadeDelay);
|
||
|
},
|
||
|
initEvents: function(){
|
||
|
var t = this;
|
||
|
t.core.container.on('croprotstart croprotend cropimage cropstart cropmove cropend',function(e,s,c){
|
||
|
if (t.selectionTarget && (t.selectionTarget !== e.target)) return false;
|
||
|
|
||
|
switch(e.type){
|
||
|
|
||
|
case 'cropimage':
|
||
|
t.updateImage(c);
|
||
|
break;
|
||
|
|
||
|
case 'cropstart':
|
||
|
t.selectionStart(s);
|
||
|
case 'croprotstart':
|
||
|
t.show();
|
||
|
break;
|
||
|
|
||
|
case 'cropend':
|
||
|
t.renderCoords(c);
|
||
|
case 'croprotend':
|
||
|
if (t.autoHide) t.hide();
|
||
|
break;
|
||
|
|
||
|
case 'cropmove':
|
||
|
t.renderCoords(c);
|
||
|
break;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
Jcrop.registerComponent('Thumbnailer',Thumbnailer);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* DialDrag component
|
||
|
* This is a little hacky, it was adapted from some previous/old code
|
||
|
* Plan to update this API in the future
|
||
|
*/
|
||
|
var DialDrag = function() { };
|
||
|
|
||
|
DialDrag.prototype = {
|
||
|
|
||
|
init: function(core,actuator,callback){
|
||
|
var that = this;
|
||
|
|
||
|
if (!actuator) actuator = core.container;
|
||
|
this.$btn = $(actuator);
|
||
|
this.$targ = $(actuator);
|
||
|
this.core = core;
|
||
|
|
||
|
this.$btn
|
||
|
.addClass('dialdrag')
|
||
|
.on('mousedown.dialdrag',this.mousedown())
|
||
|
.data('dialdrag',this);
|
||
|
|
||
|
if (!$.isFunction(callback)) callback = function(){ };
|
||
|
this.callback = callback;
|
||
|
this.ondone = callback;
|
||
|
},
|
||
|
|
||
|
remove: function(){
|
||
|
this.$btn
|
||
|
.removeClass('dialdrag')
|
||
|
.off('.dialdrag')
|
||
|
.data('dialdrag',null);
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
setTarget: function(obj){
|
||
|
this.$targ = $(obj);
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
getOffset: function(){
|
||
|
var targ = this.$targ, pos = targ.offset();
|
||
|
return [
|
||
|
pos.left + (targ.width()/2),
|
||
|
pos.top + (targ.height()/2)
|
||
|
];
|
||
|
},
|
||
|
|
||
|
relMouse: function(e){
|
||
|
var x = e.pageX - this.offset[0],
|
||
|
y = e.pageY - this.offset[1],
|
||
|
ang = Math.atan2(y,x) * (180 / Math.PI),
|
||
|
vec = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
|
||
|
return [ x, y, ang, vec ];
|
||
|
},
|
||
|
|
||
|
mousedown: function(){
|
||
|
var that = this;
|
||
|
|
||
|
function mouseUp(e){
|
||
|
$(window).off('.dialdrag');
|
||
|
that.ondone.call(that,that.relMouse(e));
|
||
|
that.core.container.trigger('croprotend');
|
||
|
}
|
||
|
|
||
|
function mouseMove(e){
|
||
|
that.callback.call(that,that.relMouse(e));
|
||
|
}
|
||
|
|
||
|
return function(e) {
|
||
|
that.offset = that.getOffset();
|
||
|
var rel = that.relMouse(e);
|
||
|
that.angleOffset = -that.core.ui.stage.angle+rel[2];
|
||
|
that.distOffset = rel[3];
|
||
|
that.dragOffset = [rel[0],rel[1]];
|
||
|
that.core.container.trigger('croprotstart');
|
||
|
|
||
|
$(window)
|
||
|
.on('mousemove.dialdrag',mouseMove)
|
||
|
.on('mouseup.dialdrag',mouseUp);
|
||
|
|
||
|
that.callback.call(that,that.relMouse(e));
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
};
|
||
|
Jcrop.registerComponent('DialDrag',DialDrag);
|
||
|
|
||
|
|
||
|
/////////////////////////////////
|
||
|
// DEFAULT SETTINGS
|
||
|
|
||
|
Jcrop.defaults = {
|
||
|
|
||
|
// Selection Behavior
|
||
|
edge: { n: 0, s: 0, e: 0, w: 0 },
|
||
|
setSelect: null,
|
||
|
linked: true,
|
||
|
linkCurrent: true,
|
||
|
canDelete: true,
|
||
|
canSelect: true,
|
||
|
canDrag: true,
|
||
|
canResize: true,
|
||
|
|
||
|
// Component constructors
|
||
|
eventManagerComponent: Jcrop.component.EventManager,
|
||
|
keyboardComponent: Jcrop.component.Keyboard,
|
||
|
dragstateComponent: Jcrop.component.DragState,
|
||
|
stagemanagerComponent: Jcrop.component.StageManager,
|
||
|
animatorComponent: Jcrop.component.Animator,
|
||
|
selectionComponent: Jcrop.component.Selection,
|
||
|
|
||
|
// This is a function that is called, which returns a stage object
|
||
|
stageConstructor: Jcrop.stageConstructor,
|
||
|
|
||
|
// Stage Behavior
|
||
|
allowSelect: true,
|
||
|
multi: false,
|
||
|
multiMax: false,
|
||
|
multiCleanup: true,
|
||
|
animation: true,
|
||
|
animEasing: 'swing',
|
||
|
animDuration: 400,
|
||
|
fading: true,
|
||
|
fadeDuration: 300,
|
||
|
fadeEasing: 'swing',
|
||
|
bgColor: 'black',
|
||
|
bgOpacity: .5,
|
||
|
|
||
|
// Startup options
|
||
|
applyFilters: [ 'constrain', 'extent', 'backoff', 'ratio', 'shader', 'round' ],
|
||
|
borders: [ 'e', 'w', 's', 'n' ],
|
||
|
handles: [ 'n', 's', 'e', 'w', 'sw', 'ne', 'nw', 'se' ],
|
||
|
dragbars: [ 'n', 'e', 'w', 's' ],
|
||
|
|
||
|
dragEventTarget: window,
|
||
|
|
||
|
xscale: 1,
|
||
|
yscale: 1,
|
||
|
|
||
|
boxWidth: null,
|
||
|
boxHeight: null,
|
||
|
|
||
|
// CSS Classes
|
||
|
// @todo: These need to be moved to top-level object keys
|
||
|
// for better customization. Currently if you try to extend one
|
||
|
// via an options object to Jcrop, it will wipe out all
|
||
|
// the others you don't specify. Be careful for now!
|
||
|
css_nodrag: 'jcrop-nodrag',
|
||
|
css_drag: 'jcrop-drag',
|
||
|
css_container: 'jcrop-active',
|
||
|
css_shades: 'jcrop-shades',
|
||
|
css_selection: 'jcrop-selection',
|
||
|
css_borders: 'jcrop-border',
|
||
|
css_handles: 'jcrop-handle jcrop-drag',
|
||
|
css_button: 'jcrop-box jcrop-drag',
|
||
|
css_noresize: 'jcrop-noresize',
|
||
|
css_dragbars: 'jcrop-dragbar jcrop-drag',
|
||
|
|
||
|
legacyHandlers: {
|
||
|
onChange: 'cropmove',
|
||
|
onSelect: 'cropend'
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
// Jcrop API methods
|
||
|
$.extend(Jcrop.prototype,{
|
||
|
//init: function(){{{
|
||
|
init: function(){
|
||
|
this.event = new this.opt.eventManagerComponent(this);
|
||
|
this.ui.keyboard = new this.opt.keyboardComponent(this);
|
||
|
this.ui.manager = new this.opt.stagemanagerComponent(this);
|
||
|
this.applyFilters();
|
||
|
|
||
|
if ($.Jcrop.supportsTouch)
|
||
|
new $.Jcrop.component.Touch(this);
|
||
|
|
||
|
this.initEvents();
|
||
|
},
|
||
|
//}}}
|
||
|
// applySizeConstraints: function(){{{
|
||
|
applySizeConstraints: function(){
|
||
|
var o = this.opt,
|
||
|
img = this.opt.imgsrc;
|
||
|
|
||
|
if (img){
|
||
|
|
||
|
var iw = img.naturalWidth || img.width,
|
||
|
ih = img.naturalHeight || img.height,
|
||
|
bw = o.boxWidth || iw,
|
||
|
bh = o.boxHeight || ih;
|
||
|
|
||
|
if (img && ((iw > bw) || (ih > bh))){
|
||
|
var bx = Jcrop.getLargestBox(iw/ih,bw,bh);
|
||
|
$(img).width(bx[0]).height(bx[1]);
|
||
|
this.resizeContainer(bx[0],bx[1]);
|
||
|
this.opt.xscale = iw / bx[0];
|
||
|
this.opt.yscale = ih / bx[1];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (this.opt.trueSize){
|
||
|
var dw = this.opt.trueSize[0];
|
||
|
var dh = this.opt.trueSize[1];
|
||
|
var cs = this.getContainerSize();
|
||
|
this.opt.xscale = dw / cs[0];
|
||
|
this.opt.yscale = dh / cs[1];
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
initComponent: function(name){
|
||
|
if (Jcrop.component[name]) {
|
||
|
var args = Array.prototype.slice.call(arguments);
|
||
|
var obj = new Jcrop.component[name];
|
||
|
args.shift();
|
||
|
args.unshift(this);
|
||
|
obj.init.apply(obj,args);
|
||
|
return obj;
|
||
|
}
|
||
|
},
|
||
|
// setOptions: function(opt){{{
|
||
|
setOptions: function(opt,proptype){
|
||
|
|
||
|
if (!$.isPlainObject(opt)) opt = {};
|
||
|
|
||
|
$.extend(this.opt,opt);
|
||
|
|
||
|
// Handle a setSelect value
|
||
|
if (this.opt.setSelect) {
|
||
|
|
||
|
// If there is no current selection
|
||
|
// passing setSelect will create one
|
||
|
if (!this.ui.multi.length)
|
||
|
this.newSelection();
|
||
|
|
||
|
// Use these values to update the current selection
|
||
|
this.setSelect(this.opt.setSelect);
|
||
|
|
||
|
// Set to null so it doesn't get called again
|
||
|
this.opt.setSelect = null;
|
||
|
}
|
||
|
|
||
|
this.event.trigger('configupdate');
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
//destroy: function(){{{
|
||
|
destroy: function(){
|
||
|
if (this.opt.imgsrc) {
|
||
|
this.container.before(this.opt.imgsrc);
|
||
|
this.container.remove();
|
||
|
$(this.opt.imgsrc).removeData('Jcrop').show();
|
||
|
} else {
|
||
|
// @todo: more elegant destroy() process for non-image containers
|
||
|
this.container.remove();
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// applyFilters: function(){{{
|
||
|
applyFilters: function(){
|
||
|
var obj;
|
||
|
for(var i=0,f=this.opt.applyFilters,l=f.length; i<l; i++){
|
||
|
if ($.Jcrop.filter[f[i]])
|
||
|
obj = new $.Jcrop.filter[f[i]];
|
||
|
obj.core = this;
|
||
|
if (obj.init) obj.init();
|
||
|
this.filter[f[i]] = obj;
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// getDefaultFilters: function(){{{
|
||
|
getDefaultFilters: function(){
|
||
|
var rv = [];
|
||
|
|
||
|
for(var i=0,f=this.opt.applyFilters,l=f.length; i<l; i++)
|
||
|
if(this.filter.hasOwnProperty(f[i]))
|
||
|
rv.push(this.filter[f[i]]);
|
||
|
|
||
|
rv.sort(function(x,y){ return x.priority - y.priority; });
|
||
|
return rv;
|
||
|
},
|
||
|
// }}}
|
||
|
// setSelection: function(sel){{{
|
||
|
setSelection: function(sel){
|
||
|
var m = this.ui.multi;
|
||
|
var n = [];
|
||
|
for(var i=0;i<m.length;i++) {
|
||
|
if (m[i] !== sel) n.push(m[i]);
|
||
|
m[i].toBack();
|
||
|
}
|
||
|
n.unshift(sel);
|
||
|
this.ui.multi = n;
|
||
|
this.ui.selection = sel;
|
||
|
sel.toFront();
|
||
|
return sel;
|
||
|
},
|
||
|
// }}}
|
||
|
// getSelection: function(raw){{{
|
||
|
getSelection: function(raw){
|
||
|
var b = this.ui.selection.get();
|
||
|
return b;
|
||
|
},
|
||
|
// }}}
|
||
|
// newSelection: function(){{{
|
||
|
newSelection: function(sel){
|
||
|
if (!sel)
|
||
|
sel = new this.opt.selectionComponent();
|
||
|
|
||
|
sel.init(this);
|
||
|
this.setSelection(sel);
|
||
|
|
||
|
return sel;
|
||
|
},
|
||
|
// }}}
|
||
|
// hasSelection: function(sel){{{
|
||
|
hasSelection: function(sel){
|
||
|
for(var i=0;i<this.ui.multi;i++)
|
||
|
if (sel === this.ui.multi[i]) return true;
|
||
|
},
|
||
|
// }}}
|
||
|
// removeSelection: function(sel){{{
|
||
|
removeSelection: function(sel){
|
||
|
var i, n = [], m = this.ui.multi;
|
||
|
for(var i=0;i<m.length;i++){
|
||
|
if (sel !== m[i])
|
||
|
n.push(m[i]);
|
||
|
else m[i].remove();
|
||
|
}
|
||
|
return this.ui.multi = n;
|
||
|
},
|
||
|
// }}}
|
||
|
//addFilter: function(filter){{{
|
||
|
addFilter: function(filter){
|
||
|
for(var i=0,m=this.ui.multi,l=m.length; i<l; i++)
|
||
|
m[i].addFilter(filter);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
//}}}
|
||
|
// removeFiltersByTag: function(tag){{{
|
||
|
removeFilter: function(filter){
|
||
|
for(var i=0,m=this.ui.multi,l=m.length; i<l; i++)
|
||
|
m[i].removeFilter(filter);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// blur: function(){{{
|
||
|
blur: function(){
|
||
|
this.ui.selection.blur();
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// focus: function(){{{
|
||
|
focus: function(){
|
||
|
this.ui.selection.focus();
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
//initEvents: function(){{{
|
||
|
initEvents: function(){
|
||
|
var t = this;
|
||
|
t.container.on('selectstart',function(e){ return false; })
|
||
|
.on('mousedown','.'+t.opt.css_drag,t.startDrag());
|
||
|
},
|
||
|
//}}}
|
||
|
// maxSelect: function(){{{
|
||
|
maxSelect: function(){
|
||
|
this.setSelect([0,0,this.elw,this.elh]);
|
||
|
},
|
||
|
// }}}
|
||
|
// nudge: function(x,y){{{
|
||
|
nudge: function(x,y){
|
||
|
var s = this.ui.selection, b = s.get();
|
||
|
|
||
|
b.x += x;
|
||
|
b.x2 += x;
|
||
|
b.y += y;
|
||
|
b.y2 += y;
|
||
|
|
||
|
if (b.x < 0) { b.x2 = b.w; b.x = 0; }
|
||
|
else if (b.x2 > this.elw) { b.x2 = this.elw; b.x = b.x2 - b.w; }
|
||
|
|
||
|
if (b.y < 0) { b.y2 = b.h; b.y = 0; }
|
||
|
else if (b.y2 > this.elh) { b.y2 = this.elh; b.y = b.y2 - b.h; }
|
||
|
|
||
|
s.element.trigger('cropstart',[s,this.unscale(b)]);
|
||
|
s.updateRaw(b,'move');
|
||
|
s.element.trigger('cropend',[s,this.unscale(b)]);
|
||
|
},
|
||
|
// }}}
|
||
|
// refresh: function(){{{
|
||
|
refresh: function(){
|
||
|
for(var i=0,s=this.ui.multi,l=s.length;i<l;i++)
|
||
|
s[i].refresh();
|
||
|
},
|
||
|
// }}}
|
||
|
// blurAll: function(){{{
|
||
|
blurAll: function(){
|
||
|
var m = this.ui.multi;
|
||
|
for(var i=0;i<m.length;i++) {
|
||
|
if (m[i] !== sel) n.push(m[i]);
|
||
|
m[i].toBack();
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// scale: function(b){{{
|
||
|
scale: function(b){
|
||
|
var xs = this.opt.xscale,
|
||
|
ys = this.opt.yscale;
|
||
|
|
||
|
return {
|
||
|
x: b.x / xs,
|
||
|
y: b.y / ys,
|
||
|
x2: b.x2 / xs,
|
||
|
y2: b.y2 / ys,
|
||
|
w: b.w / xs,
|
||
|
h: b.h / ys
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// unscale: function(b){{{
|
||
|
unscale: function(b){
|
||
|
var xs = this.opt.xscale,
|
||
|
ys = this.opt.yscale;
|
||
|
|
||
|
return {
|
||
|
x: b.x * xs,
|
||
|
y: b.y * ys,
|
||
|
x2: b.x2 * xs,
|
||
|
y2: b.y2 * ys,
|
||
|
w: b.w * xs,
|
||
|
h: b.h * ys
|
||
|
};
|
||
|
},
|
||
|
// }}}
|
||
|
// requestDelete: function(){{{
|
||
|
requestDelete: function(){
|
||
|
if ((this.ui.multi.length > 1) && (this.ui.selection.canDelete))
|
||
|
return this.deleteSelection();
|
||
|
},
|
||
|
// }}}
|
||
|
// deleteSelection: function(){{{
|
||
|
deleteSelection: function(){
|
||
|
if (this.ui.selection) {
|
||
|
this.removeSelection(this.ui.selection);
|
||
|
if (this.ui.multi.length) this.ui.multi[0].focus();
|
||
|
this.ui.selection.refresh();
|
||
|
}
|
||
|
},
|
||
|
// }}}
|
||
|
// animateTo: function(box){{{
|
||
|
animateTo: function(box){
|
||
|
if (this.ui.selection)
|
||
|
this.ui.selection.animateTo(box);
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
// setselect: function(box){{{
|
||
|
setSelect: function(box){
|
||
|
if (this.ui.selection)
|
||
|
this.ui.selection.update(Jcrop.wrapFromXywh(box));
|
||
|
return this;
|
||
|
},
|
||
|
// }}}
|
||
|
//startDrag: function(){{{
|
||
|
startDrag: function(){
|
||
|
var t = this;
|
||
|
return function(e){
|
||
|
var $targ = $(e.target);
|
||
|
var selection = $targ.closest('.'+t.opt.css_selection).data('selection');
|
||
|
var ord = $targ.data('ord');
|
||
|
t.container.trigger('cropstart',[selection,t.unscale(selection.get())]);
|
||
|
selection.startDrag(e,ord);
|
||
|
return false;
|
||
|
};
|
||
|
},
|
||
|
//}}}
|
||
|
// getContainerSize: function(){{{
|
||
|
getContainerSize: function(){
|
||
|
return [ this.container.width(), this.container.height() ];
|
||
|
},
|
||
|
// }}}
|
||
|
// resizeContainer: function(w,h){{{
|
||
|
resizeContainer: function(w,h){
|
||
|
this.container.width(w).height(h);
|
||
|
this.refresh();
|
||
|
},
|
||
|
// }}}
|
||
|
// setImage: function(src,cb){{{
|
||
|
setImage: function(src,cb){
|
||
|
var t = this, targ = t.opt.imgsrc;
|
||
|
|
||
|
if (!targ) return false;
|
||
|
|
||
|
new $.Jcrop.component.ImageLoader(src,null,function(w,h){
|
||
|
t.resizeContainer(w,h);
|
||
|
|
||
|
targ.src = src;
|
||
|
$(targ).width(w).height(h);
|
||
|
t.applySizeConstraints();
|
||
|
t.refresh();
|
||
|
t.container.trigger('cropimage',[t,targ]);
|
||
|
|
||
|
if (typeof cb == 'function')
|
||
|
cb.call(t,w,h);
|
||
|
});
|
||
|
},
|
||
|
// }}}
|
||
|
// update: function(b){{{
|
||
|
update: function(b){
|
||
|
if (this.ui.selection)
|
||
|
this.ui.selection.update(b);
|
||
|
}
|
||
|
// }}}
|
||
|
});
|
||
|
|
||
|
// Jcrop jQuery plugin function
|
||
|
$.fn.Jcrop = function(options,callback){
|
||
|
options = options || {};
|
||
|
|
||
|
var first = this.eq(0).data('Jcrop');
|
||
|
var args = Array.prototype.slice.call(arguments);
|
||
|
|
||
|
// Return API if requested
|
||
|
if (options == 'api') { return first; }
|
||
|
|
||
|
// Allow calling API methods (with arguments)
|
||
|
else if (first && (typeof options == 'string')) {
|
||
|
|
||
|
// Call method if it exists
|
||
|
if (first[options]) {
|
||
|
args.shift();
|
||
|
first[options].apply(first,args);
|
||
|
return first;
|
||
|
}
|
||
|
|
||
|
// Unknown input/method does not exist
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Otherwise, loop over selected elements
|
||
|
this.each(function(){
|
||
|
var t = this, $t = $(this);
|
||
|
var exists = $t.data('Jcrop');
|
||
|
var obj;
|
||
|
|
||
|
// If Jcrop already exists on this element only setOptions()
|
||
|
if (exists)
|
||
|
exists.setOptions(options);
|
||
|
|
||
|
else {
|
||
|
|
||
|
if (!options.stageConstructor)
|
||
|
options.stageConstructor = $.Jcrop.stageConstructor;
|
||
|
|
||
|
options.stageConstructor(this,options,function(stage,options){
|
||
|
var selection = options.setSelect;
|
||
|
if (selection) delete(options.setSelect);
|
||
|
|
||
|
var obj = $.Jcrop.attach(stage.element,options);
|
||
|
|
||
|
if (typeof stage.attach == 'function')
|
||
|
stage.attach(obj);
|
||
|
|
||
|
$t.data('Jcrop',obj);
|
||
|
|
||
|
if (selection) {
|
||
|
obj.newSelection();
|
||
|
obj.setSelect(selection);
|
||
|
}
|
||
|
|
||
|
if (typeof callback == 'function')
|
||
|
callback.call(obj);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/* Modernizr 2.7.1 (Custom Build) | MIT & BSD
|
||
|
* Build: http://modernizr.com/download/#-csstransforms-canvas-canvastext-draganddrop-inlinesvg-svg-svgclippaths-touch-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-url_data_uri
|
||
|
*/
|
||
|
;
|
||
|
|
||
|
var Modernizr = (function( window, document, undefined ) {
|
||
|
|
||
|
var version = '2.7.1',
|
||
|
|
||
|
Modernizr = {},
|
||
|
|
||
|
|
||
|
docElement = document.documentElement,
|
||
|
|
||
|
mod = 'modernizr',
|
||
|
modElem = document.createElement(mod),
|
||
|
mStyle = modElem.style,
|
||
|
|
||
|
inputElem ,
|
||
|
|
||
|
|
||
|
toString = {}.toString,
|
||
|
|
||
|
prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
|
||
|
|
||
|
|
||
|
|
||
|
omPrefixes = 'Webkit Moz O ms',
|
||
|
|
||
|
cssomPrefixes = omPrefixes.split(' '),
|
||
|
|
||
|
domPrefixes = omPrefixes.toLowerCase().split(' '),
|
||
|
|
||
|
ns = {'svg': 'http://www.w3.org/2000/svg'},
|
||
|
|
||
|
tests = {},
|
||
|
inputs = {},
|
||
|
attrs = {},
|
||
|
|
||
|
classes = [],
|
||
|
|
||
|
slice = classes.slice,
|
||
|
|
||
|
featureName,
|
||
|
|
||
|
|
||
|
injectElementWithStyles = function( rule, callback, nodes, testnames ) {
|
||
|
|
||
|
var style, ret, node, docOverflow,
|
||
|
div = document.createElement('div'),
|
||
|
body = document.body,
|
||
|
fakeBody = body || document.createElement('body');
|
||
|
|
||
|
if ( parseInt(nodes, 10) ) {
|
||
|
while ( nodes-- ) {
|
||
|
node = document.createElement('div');
|
||
|
node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
|
||
|
div.appendChild(node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
style = ['­','<style id="s', mod, '">', rule, '</style>'].join('');
|
||
|
div.id = mod;
|
||
|
(body ? div : fakeBody).innerHTML += style;
|
||
|
fakeBody.appendChild(div);
|
||
|
if ( !body ) {
|
||
|
fakeBody.style.background = '';
|
||
|
fakeBody.style.overflow = 'hidden';
|
||
|
docOverflow = docElement.style.overflow;
|
||
|
docElement.style.overflow = 'hidden';
|
||
|
docElement.appendChild(fakeBody);
|
||
|
}
|
||
|
|
||
|
ret = callback(div, rule);
|
||
|
if ( !body ) {
|
||
|
fakeBody.parentNode.removeChild(fakeBody);
|
||
|
docElement.style.overflow = docOverflow;
|
||
|
} else {
|
||
|
div.parentNode.removeChild(div);
|
||
|
}
|
||
|
|
||
|
return !!ret;
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
|
||
|
isEventSupported = (function() {
|
||
|
|
||
|
var TAGNAMES = {
|
||
|
'select': 'input', 'change': 'input',
|
||
|
'submit': 'form', 'reset': 'form',
|
||
|
'error': 'img', 'load': 'img', 'abort': 'img'
|
||
|
};
|
||
|
|
||
|
function isEventSupported( eventName, element ) {
|
||
|
|
||
|
element = element || document.createElement(TAGNAMES[eventName] || 'div');
|
||
|
eventName = 'on' + eventName;
|
||
|
|
||
|
var isSupported = eventName in element;
|
||
|
|
||
|
if ( !isSupported ) {
|
||
|
if ( !element.setAttribute ) {
|
||
|
element = document.createElement('div');
|
||
|
}
|
||
|
if ( element.setAttribute && element.removeAttribute ) {
|
||
|
element.setAttribute(eventName, '');
|
||
|
isSupported = is(element[eventName], 'function');
|
||
|
|
||
|
if ( !is(element[eventName], 'undefined') ) {
|
||
|
element[eventName] = undefined;
|
||
|
}
|
||
|
element.removeAttribute(eventName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
element = null;
|
||
|
return isSupported;
|
||
|
}
|
||
|
return isEventSupported;
|
||
|
})(),
|
||
|
|
||
|
|
||
|
_hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
|
||
|
|
||
|
if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
|
||
|
hasOwnProp = function (object, property) {
|
||
|
return _hasOwnProperty.call(object, property);
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
hasOwnProp = function (object, property) {
|
||
|
return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
|
||
|
};
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!Function.prototype.bind) {
|
||
|
Function.prototype.bind = function bind(that) {
|
||
|
|
||
|
var target = this;
|
||
|
|
||
|
if (typeof target != "function") {
|
||
|
throw new TypeError();
|
||
|
}
|
||
|
|
||
|
var args = slice.call(arguments, 1),
|
||
|
bound = function () {
|
||
|
|
||
|
if (this instanceof bound) {
|
||
|
|
||
|
var F = function(){};
|
||
|
F.prototype = target.prototype;
|
||
|
var self = new F();
|
||
|
|
||
|
var result = target.apply(
|
||
|
self,
|
||
|
args.concat(slice.call(arguments))
|
||
|
);
|
||
|
if (Object(result) === result) {
|
||
|
return result;
|
||
|
}
|
||
|
return self;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
return target.apply(
|
||
|
that,
|
||
|
args.concat(slice.call(arguments))
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
return bound;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function setCss( str ) {
|
||
|
mStyle.cssText = str;
|
||
|
}
|
||
|
|
||
|
function setCssAll( str1, str2 ) {
|
||
|
return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
|
||
|
}
|
||
|
|
||
|
function is( obj, type ) {
|
||
|
return typeof obj === type;
|
||
|
}
|
||
|
|
||
|
function contains( str, substr ) {
|
||
|
return !!~('' + str).indexOf(substr);
|
||
|
}
|
||
|
|
||
|
function testProps( props, prefixed ) {
|
||
|
for ( var i in props ) {
|
||
|
var prop = props[i];
|
||
|
if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
|
||
|
return prefixed == 'pfx' ? prop : true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function testDOMProps( props, obj, elem ) {
|
||
|
for ( var i in props ) {
|
||
|
var item = obj[props[i]];
|
||
|
if ( item !== undefined) {
|
||
|
|
||
|
if (elem === false) return props[i];
|
||
|
|
||
|
if (is(item, 'function')){
|
||
|
return item.bind(elem || obj);
|
||
|
}
|
||
|
|
||
|
return item;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function testPropsAll( prop, prefixed, elem ) {
|
||
|
|
||
|
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
|
||
|
props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
|
||
|
|
||
|
if(is(prefixed, "string") || is(prefixed, "undefined")) {
|
||
|
return testProps(props, prefixed);
|
||
|
|
||
|
} else {
|
||
|
props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
|
||
|
return testDOMProps(props, prefixed, elem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
tests['canvas'] = function() {
|
||
|
var elem = document.createElement('canvas');
|
||
|
return !!(elem.getContext && elem.getContext('2d'));
|
||
|
};
|
||
|
|
||
|
tests['canvastext'] = function() {
|
||
|
return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
|
||
|
};
|
||
|
tests['touch'] = function() {
|
||
|
var bool;
|
||
|
|
||
|
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
|
||
|
bool = true;
|
||
|
} else {
|
||
|
injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
|
||
|
bool = node.offsetTop === 9;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return bool;
|
||
|
};
|
||
|
|
||
|
tests['draganddrop'] = function() {
|
||
|
var div = document.createElement('div');
|
||
|
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
|
||
|
};
|
||
|
|
||
|
|
||
|
tests['csstransforms'] = function() {
|
||
|
return !!testPropsAll('transform');
|
||
|
};
|
||
|
|
||
|
|
||
|
tests['svg'] = function() {
|
||
|
return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
|
||
|
};
|
||
|
|
||
|
tests['inlinesvg'] = function() {
|
||
|
var div = document.createElement('div');
|
||
|
div.innerHTML = '<svg/>';
|
||
|
return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
tests['svgclippaths'] = function() {
|
||
|
return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
|
||
|
};
|
||
|
|
||
|
for ( var feature in tests ) {
|
||
|
if ( hasOwnProp(tests, feature) ) {
|
||
|
featureName = feature.toLowerCase();
|
||
|
Modernizr[featureName] = tests[feature]();
|
||
|
|
||
|
classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Modernizr.addTest = function ( feature, test ) {
|
||
|
if ( typeof feature == 'object' ) {
|
||
|
for ( var key in feature ) {
|
||
|
if ( hasOwnProp( feature, key ) ) {
|
||
|
Modernizr.addTest( key, feature[ key ] );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
feature = feature.toLowerCase();
|
||
|
|
||
|
if ( Modernizr[feature] !== undefined ) {
|
||
|
return Modernizr;
|
||
|
}
|
||
|
|
||
|
test = typeof test == 'function' ? test() : test;
|
||
|
|
||
|
if (typeof enableClasses !== "undefined" && enableClasses) {
|
||
|
docElement.className += ' ' + (test ? '' : 'no-') + feature;
|
||
|
}
|
||
|
Modernizr[feature] = test;
|
||
|
|
||
|
}
|
||
|
|
||
|
return Modernizr;
|
||
|
};
|
||
|
|
||
|
|
||
|
setCss('');
|
||
|
modElem = inputElem = null;
|
||
|
|
||
|
|
||
|
Modernizr._version = version;
|
||
|
|
||
|
Modernizr._prefixes = prefixes;
|
||
|
Modernizr._domPrefixes = domPrefixes;
|
||
|
Modernizr._cssomPrefixes = cssomPrefixes;
|
||
|
|
||
|
|
||
|
Modernizr.hasEvent = isEventSupported;
|
||
|
|
||
|
Modernizr.testProp = function(prop){
|
||
|
return testProps([prop]);
|
||
|
};
|
||
|
|
||
|
Modernizr.testAllProps = testPropsAll;
|
||
|
|
||
|
|
||
|
Modernizr.testStyles = injectElementWithStyles;
|
||
|
return Modernizr;
|
||
|
|
||
|
})(window, window.document);
|
||
|
// data uri test.
|
||
|
// https://github.com/Modernizr/Modernizr/issues/14
|
||
|
|
||
|
// This test is asynchronous. Watch out.
|
||
|
|
||
|
|
||
|
// in IE7 in HTTPS this can cause a Mixed Content security popup.
|
||
|
// github.com/Modernizr/Modernizr/issues/362
|
||
|
// To avoid that you can create a new iframe and inject this.. perhaps..
|
||
|
|
||
|
|
||
|
(function(){
|
||
|
|
||
|
var datauri = new Image();
|
||
|
|
||
|
|
||
|
datauri.onerror = function() {
|
||
|
Modernizr.addTest('datauri', function () { return false; });
|
||
|
};
|
||
|
datauri.onload = function() {
|
||
|
Modernizr.addTest('datauri', function () { return (datauri.width == 1 && datauri.height == 1); });
|
||
|
};
|
||
|
|
||
|
datauri.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
|
||
|
|
||
|
})();
|
||
|
;
|
||
|
|
||
|
// Attach to jQuery object
|
||
|
$.Jcrop = Jcrop;
|
||
|
|
||
|
$.Jcrop.supportsCanvas = Modernizr.canvas;
|
||
|
$.Jcrop.supportsCanvasText = Modernizr.canvastext;
|
||
|
$.Jcrop.supportsDragAndDrop = Modernizr.draganddrop;
|
||
|
$.Jcrop.supportsDataURI = Modernizr.datauri;
|
||
|
$.Jcrop.supportsSVG = Modernizr.svg;
|
||
|
$.Jcrop.supportsInlineSVG = Modernizr.inlinesvg;
|
||
|
$.Jcrop.supportsSVGClipPaths = Modernizr.svgclippaths;
|
||
|
$.Jcrop.supportsCSSTransforms = Modernizr.csstransforms;
|
||
|
$.Jcrop.supportsTouch = Modernizr.touch;
|
||
|
|
||
|
})(jQuery);
|