=this.text.length&&this.selectionEnd>=this.text.length)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorRightWithShift(e):this.moveCursorRightWithoutShift(e),this.initDelayedCursor()},moveCursorRightWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?this._moveRight(e,"selectionStart"):(this._selectionDirection="right",this._moveRight(e,"selectionEnd"),this.text.charAt(this.selectionEnd-1)==="\n"&&this.selectionEnd++,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorRightWithoutShift:function(e){this._selectionDirection="right",this.selectionStart===this.selectionEnd?(this._moveRight(e,"selectionStart"),this.selectionEnd=this.selectionStart):(this.selectionEnd+=this.getNumNewLinesInSelectedText(),this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length),this.selectionStart=this.selectionEnd)},removeChars:function(e){this.selectionStart===this.selectionEnd?this._removeCharsNearCursor(e):this._removeCharsFromTo(this.selectionStart,this.selectionEnd),this.selectionEnd=this.selectionStart,this._removeExtraneousStyles(),this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("text:changed")},_removeCharsNearCursor:function(e){if(this.selectionStart!==0)if(e.metaKey){var t=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(t,this.selectionStart),this.selectionStart=t}else if(e.altKey){var n=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(n,this.selectionStart),this.selectionStart=n}else{var r=this.text.slice(this.selectionStart-1,this.selectionStart)==="\n";this.removeStyleObject(r),this.selectionStart--,this.text=this.text.slice(0,this.selectionStart)+this.text.slice(this.selectionStart+1)}}}),fabric.util.object.extend(fabric.IText.prototype,{_setSVGTextLineText:function(e,t,n,r,i,s){this.styles[t]?this._setSVGTextLineChars(e,t,n,r,i,s):this.callSuper("_setSVGTextLineText",e,t,n,r,i)},_setSVGTextLineChars:function(e,t,n,r,i,s){var o=t===0||this.useNative?"y":"dy",u=e.split(""),a=0,f=this._getSVGLineLeftOffset(t),l=this._getSVGLineTopOffset(t),c=this._getHeightOfLine(this.ctx,t);for(var h=0,p=u.length;h'].join("")},_createTextCharSpan:function(e,t,n,r,i,s){var o=this.getSvgStyles.call(fabric.util.object.extend({visible:!0,fill:this.fill,stroke:this.stroke,type:"text"},t));return['',fabric.util.string.escapeXml(e),""].join("")}}),function(){function request(e,t,n){var r=URL.parse(e);r.port||(r.port=r.protocol.indexOf("https:")===0?443:80);var i=r.port===443?HTTPS:HTTP,s=i.request({hostname:r.hostname,port:r.port,path:r.path,method:"GET"},function(e){var r="";t&&e.setEncoding(t),e.on("end",function(){n(r)}),e.on("data",function(t){e.statusCode===200&&(r+=t)})});s.on("error",function(e){e.errno===process.ECONNREFUSED?fabric.log("ECONNREFUSED: connection refused to "+r.hostname+":"+r.port):fabric.log(e.message)}),s.end()}function request_fs(e,t){var n=require("fs");n.readFile(e,function(e,n){if(e)throw fabric.log(e),e;t(n)})}if(typeof document!="undefined"&&typeof window!="undefined")return;var DOMParser=(new require("xmldom")).DOMParser,URL=require("url"),HTTP=require("http"),HTTPS=require("https"),Canvas=require("canvas"),Image=require("canvas").Image;fabric.util.loadImage=function(e,t,n){var r=function(r){i.src=new Buffer(r,"binary"),i._src=e,t&&t.call(n,i)},i=new Image;e&&(e instanceof Buffer||e.indexOf("data")===0)?(i.src=i._src=e,t&&t.call(n,i)):e&&e.indexOf("http")!==0?request_fs(e,r):e?request(e,"binary",r):t&&t.call(n,e)},fabric.loadSVGFromURL=function(e,t,n){e=e.replace(/^\n\s*/,"").replace(/\?.*$/,"").trim(),e.indexOf("http")!==0?request_fs(e,function(e){fabric.loadSVGFromString(e,t,n)}):request(e,"",function(e){fabric.loadSVGFromString(e,t,n)})},fabric.loadSVGFromString=function(e,t,n){var r=(new DOMParser).parseFromString(e);fabric.parseSVGDocument(r.documentElement,function(e,n){t&&t(e,n)},n)},fabric.util.getScript=function(url,callback){request(url,"",function(body){eval(body),callback&&callback()})},fabric.Image.fromObject=function(e,t){fabric.util.loadImage(e.src,function(n){var r=new fabric.Image(n);r._initConfig(e),r._initFilters(e,function(e){r.filters=e||[],t&&t(r)})})},fabric.createCanvasForNode=function(e,t){var n=fabric.document.createElement("canvas"),r=new Canvas(e||600,t||600);n.style={},n.width=r.width,n.height=r.height;var i=fabric.Canvas||fabric.StaticCanvas,s=new i(n);return s.contextContainer=r.getContext("2d"),s.nodeCanvas=r,s.Font=Canvas.Font,s},fabric.StaticCanvas.prototype.createPNGStream=function(){return this.nodeCanvas.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(e){return this.nodeCanvas.createJPEGStream(e)};var origSetWidth=fabric.StaticCanvas.prototype.setWidth;fabric.StaticCanvas.prototype.setWidth=function(e){return origSetWidth.call(this,e),this.nodeCanvas.width=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setWidth=fabric.StaticCanvas.prototype.setWidth);var origSetHeight=fabric.StaticCanvas.prototype.setHeight;fabric.StaticCanvas.prototype.setHeight=function(e){return origSetHeight.call(this,e),this.nodeCanvas.height=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setHeight=fabric.StaticCanvas.prototype.setHeight)}();
\ No newline at end of file
diff --git a/dist/all.min.js.gz b/dist/all.min.js.gz
deleted file mode 100644
index a486530a..00000000
Binary files a/dist/all.min.js.gz and /dev/null differ
diff --git a/dist/all.require.js b/dist/fabric.js
similarity index 90%
rename from dist/all.require.js
rename to dist/fabric.js
index b6812cd8..03e32a6a 100644
--- a/dist/all.require.js
+++ b/dist/fabric.js
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL minifier=uglifyjs` */
-/*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */
+/*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: "1.4.0" };
+var fabric = fabric || { version: "1.4.6" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -37,6 +37,7 @@ fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
* @type array
*/
fabric.SHARED_ATTRIBUTES = [
+ "display",
"transform",
"fill", "fill-opacity", "fill-rule",
"opacity",
@@ -1276,7 +1277,7 @@ if (typeof exports != 'undefined') {
/*
json2.js
- 2011-10-19
+ 2014-02-04
Public Domain.
@@ -1435,8 +1436,7 @@ if (typeof exports != 'undefined') {
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
-var JSON;
-if (!JSON) {
+if (typeof JSON !== 'object') {
JSON = {};
}
@@ -1450,8 +1450,7 @@ if (!JSON) {
if (typeof Date.prototype.toJSON !== 'function') {
- /** @ignore */
- Date.prototype.toJSON = function (key) {
+ Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
@@ -1465,25 +1464,16 @@ if (!JSON) {
String.prototype.toJSON =
Number.prototype.toJSON =
- /** @ignore */
- Boolean.prototype.toJSON = function (key) {
+ Boolean.prototype.toJSON = function () {
return this.valueOf();
};
}
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ var cx,
+ escapable,
gap,
indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
+ meta,
rep;
@@ -1635,7 +1625,16 @@ if (!JSON) {
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
- /** @ignore */
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
@@ -1683,7 +1682,7 @@ if (!JSON) {
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
- /** @ignore */
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
@@ -1766,6 +1765,7 @@ if (!JSON) {
}
}());
+
/*
----------------------------------------------------
Event.js : 1.1.3 : 2013/07/17 : MIT License
@@ -1795,10 +1795,10 @@ if (typeof(eventjs) === "undefined") var eventjs = Event;
(function(root) { "use strict";
// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness).
-root.modifyEventListener = true;
+root.modifyEventListener = false;
// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness).
-root.modifySelectors = true;
+root.modifySelectors = false;
// Event maintenance.
root.add = function(target, type, listener, configure) {
@@ -3721,12 +3721,12 @@ fabric.Collection = {
/**
* Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
* Objects should be instances of (or inherit from) fabric.Object
- * @param [...] Zero or more fabric instances
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
*/
add: function () {
this._objects.push.apply(this._objects, arguments);
- for (var i = arguments.length; i--; ) {
+ for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
this.renderOnAddRemove && this.renderAll();
@@ -3740,6 +3740,7 @@ fabric.Collection = {
* @param {Number} index Index to insert object at
* @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
* @return {Self} thisArg
+ * @chainable
*/
insertAt: function (object, index, nonSplicing) {
var objects = this.getObjects();
@@ -3755,22 +3756,27 @@ fabric.Collection = {
},
/**
- * Removes an object from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * @param {Object} object Object to remove
+ * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
+ * @chainable
*/
- remove: function(object) {
+ remove: function() {
var objects = this.getObjects(),
- index = objects.indexOf(object);
+ index;
- // only call onObjectRemoved if an object was actually removed
- if (index !== -1) {
- objects.splice(index, 1);
- this._onObjectRemoved(object);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ index = objects.indexOf(arguments[i]);
+
+ // only call onObjectRemoved if an object was actually removed
+ if (index !== -1) {
+ objects.splice(index, 1);
+ this._onObjectRemoved(arguments[i]);
+ }
}
this.renderOnAddRemove && this.renderAll();
- return object;
+ return this;
},
/**
@@ -4020,7 +4026,9 @@ fabric.Collection = {
* @return {Object} Object for given namespace (default fabric)
*/
resolveNamespace: function(namespace) {
- if (!namespace) return fabric;
+ if (!namespace) {
+ return fabric;
+ }
var parts = namespace.split('.'),
len = parts.length,
@@ -4182,7 +4190,7 @@ fabric.Collection = {
drawDashedLine: function(ctx, x, y, x2, y2, da) {
var dx = x2 - x,
dy = y2 - y,
- len = sqrt(dx*dx + dy*dy),
+ len = sqrt(dx * dx + dy * dy),
rot = atan2(dy, dx),
dc = da.length,
di = 0,
@@ -4290,22 +4298,23 @@ fabric.Collection = {
var a = [
[matrixA[0], matrixA[2], matrixA[4]],
[matrixA[1], matrixA[3], matrixA[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var b = [
+ b = [
[matrixB[0], matrixB[2], matrixB[4]],
[matrixB[1], matrixB[3], matrixB[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var result = [];
- for (var r=0; r<3; r++) {
+ result = [];
+
+ for (var r = 0; r < 3; r++) {
result[r] = [];
- for (var c=0; c<3; c++) {
+ for (var c = 0; c < 3; c++) {
var sum = 0;
- for (var k=0; k<3; k++) {
- sum += a[r][k]*b[k][c];
+ for (var k = 0; k < 3; k++) {
+ sum += a[r][k] * b[k][c];
}
result[r][c] = sum;
@@ -4378,9 +4387,8 @@ fabric.Collection = {
}
}
- var _isTransparent = true;
- var imageData = ctx.getImageData(
- x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
+ var _isTransparent = true,
+ imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
// Split image data - for tolerance > 1, pixelDataSize = 4;
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
@@ -4414,37 +4422,43 @@ fabric.Collection = {
return arcToSegmentsCache[argsString];
}
- var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y);
+ var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y),
- var d = (coords.x1-coords.x0) * (coords.x1-coords.x0) +
- (coords.y1-coords.y0) * (coords.y1-coords.y0);
+ d = (coords.x1 - coords.x0) * (coords.x1 - coords.x0) +
+ (coords.y1 - coords.y0) * (coords.y1 - coords.y0),
- var sfactor_sq = 1 / d - 0.25;
- if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactorSq = 1 / d - 0.25;
- var sfactor = Math.sqrt(sfactor_sq);
- if (sweep === large) sfactor = -sfactor;
-
- var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1-coords.y0);
- var yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1-coords.x0);
-
- var th0 = Math.atan2(coords.y0-yc, coords.x0-xc);
- var th1 = Math.atan2(coords.y1-yc, coords.x1-xc);
-
- var th_arc = th1-th0;
- if (th_arc < 0 && sweep === 1) {
- th_arc += 2*Math.PI;
- }
- else if (th_arc > 0 && sweep === 0) {
- th_arc -= 2 * Math.PI;
+ if (sfactorSq < 0) {
+ sfactorSq = 0;
}
- var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
- var result = [];
- for (var i=0; i 0 && sweep === 0) {
+ thArc -= 2 * Math.PI;
+ }
+
+ var segments = Math.ceil(Math.abs(thArc / (Math.PI * 0.5 + 0.001))),
+ result = [];
+
+ for (var i = 0; i < segments; i++) {
+ var th2 = th0 + i * thArc / segments,
+ th3 = th0 + (i + 1) * thArc / segments;
+
+ result[i] = [xc, yc, th2, th3, rx, ry, coords.sinTh, coords.cosTh];
}
arcToSegmentsCache[argsString] = result;
@@ -4453,56 +4467,59 @@ fabric.Collection = {
function getXYCoords(rotateX, rx, ry, ox, oy, x, y) {
- var th = rotateX * (Math.PI/180);
- var sin_th = Math.sin(th);
- var cos_th = Math.cos(th);
+ var th = rotateX * (Math.PI / 180),
+ sinTh = Math.sin(th),
+ cosTh = Math.cos(th);
+
rx = Math.abs(rx);
ry = Math.abs(ry);
- var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
- var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
- var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
+
+ var px = cosTh * (ox - x) * 0.5 + sinTh * (oy - y) * 0.5,
+ py = cosTh * (oy - y) * 0.5 - sinTh * (ox - x) * 0.5,
+ pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
+
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
}
- var a00 = cos_th / rx;
- var a01 = sin_th / rx;
- var a10 = (-sin_th) / ry;
- var a11 = (cos_th) / ry;
+ var a00 = cosTh / rx,
+ a01 = sinTh / rx,
+ a10 = (-sinTh) / ry,
+ a11 = (cosTh) / ry;
return {
x0: a00 * ox + a01 * oy,
y0: a10 * ox + a11 * oy,
x1: a00 * x + a01 * y,
y1: a10 * x + a11 * y,
- sin_th: sin_th,
- cos_th: cos_th
+ sinTh: sinTh,
+ cosTh: cosTh
};
}
- function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
+ function segmentToBezier(cx, cy, th0, th1, rx, ry, sinTh, cosTh) {
argsString = _join.call(arguments);
+
if (segmentToBezierCache[argsString]) {
return segmentToBezierCache[argsString];
}
- var a00 = cos_th * rx;
- var a01 = -sin_th * ry;
- var a10 = sin_th * rx;
- var a11 = cos_th * ry;
+ var a00 = cosTh * rx,
+ a01 = -sinTh * ry,
+ a10 = sinTh * rx,
+ a11 = cosTh * ry,
+ thHalf = 0.5 * (th1 - th0),
+ t = (8 / 3) * Math.sin(thHalf * 0.5) *
+ Math.sin(thHalf * 0.5) / Math.sin(thHalf),
- var th_half = 0.5 * (th1 - th0);
- var t = (8/3) * Math.sin(th_half * 0.5) *
- Math.sin(th_half * 0.5) / Math.sin(th_half);
-
- var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
- var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
- var x3 = cx + Math.cos(th1);
- var y3 = cy + Math.sin(th1);
- var x2 = x3 + t * Math.sin(th1);
- var y2 = y3 - t * Math.cos(th1);
+ x1 = cx + Math.cos(th0) - t * Math.sin(th0),
+ y1 = cy + Math.sin(th0) + t * Math.cos(th0),
+ x3 = cx + Math.cos(th1),
+ y3 = cy + Math.sin(th1),
+ x2 = x3 + t * Math.sin(th1),
+ y2 = y3 - t * Math.cos(th1);
segmentToBezierCache[argsString] = [
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
@@ -4521,17 +4538,18 @@ fabric.Collection = {
* @param {Array} coords
*/
fabric.util.drawArc = function(ctx, x, y, coords) {
- var rx = coords[0];
- var ry = coords[1];
- var rot = coords[2];
- var large = coords[3];
- var sweep = coords[4];
- var ex = coords[5];
- var ey = coords[6];
- var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
- for (var i=0; iString#trim on MDN
+ */
+ String.prototype.trim = function () {
+ // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
+ return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ };
+ }
+ /* _ES5_COMPAT_END_ */
+
/**
- * Trims a string (removing whitespace from the beginning and the end)
- * @function external:String#trim
- * @see String#trim on MDN
+ * Camelizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to camelize
+ * @return {String} Camelized version of a string
*/
- String.prototype.trim = function () {
- // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
- return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ function camelize(string) {
+ return string.replace(/-+(.)?/g, function(match, character) {
+ return character ? character.toUpperCase() : '';
+ });
+ }
+
+ /**
+ * Capitalizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to capitalize
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
+ * and other letters stay untouched, if false first letter is capitalized
+ * and other letters are converted to lowercase.
+ * @return {String} Capitalized version of a string
+ */
+ function capitalize(string, firstLetterOnly) {
+ return string.charAt(0).toUpperCase() +
+ (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
+ }
+
+ /**
+ * Escapes XML in a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to escape
+ * @return {String} Escaped version of a string
+ */
+ function escapeXml(string) {
+ return string.replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>');
+ }
+
+ /**
+ * String utilities
+ * @namespace fabric.util.string
+ */
+ fabric.util.string = {
+ camelize: camelize,
+ capitalize: capitalize,
+ escapeXml: escapeXml
};
-}
-/* _ES5_COMPAT_END_ */
-
-/**
- * Camelizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to camelize
- * @return {String} Camelized version of a string
- */
-function camelize(string) {
- return string.replace(/-+(.)?/g, function(match, character) {
- return character ? character.toUpperCase() : '';
- });
-}
-
-/**
- * Capitalizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to capitalize
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
- * and other letters stay untouched, if false first letter is capitalized
- * and other letters are converted to lowercase.
- * @return {String} Capitalized version of a string
- */
-function capitalize(string, firstLetterOnly) {
- return string.charAt(0).toUpperCase() +
- (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
-}
-
-/**
- * Escapes XML in a string
- * @memberOf fabric.util.string
- * @param {String} string String to escape
- * @return {String} Escaped version of a string
- */
-function escapeXml(string) {
- return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(//g, '>');
-}
-
-/**
- * String utilities
- * @namespace fabric.util.string
- */
-fabric.util.string = {
- camelize: camelize,
- capitalize: capitalize,
- escapeXml: escapeXml
-};
}());
@@ -4907,16 +4925,16 @@ fabric.util.string = {
* @return {Function}
*/
Function.prototype.bind = function(thisArg) {
- var fn = this, args = slice.call(arguments, 1), bound;
+ var _this = this, args = slice.call(arguments, 1), bound;
if (args.length) {
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
};
}
else {
/** @ignore */
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, arguments);
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
};
}
Dummy.prototype = this.prototype;
@@ -4932,51 +4950,51 @@ fabric.util.string = {
(function() {
- var slice = Array.prototype.slice, emptyFunction = function() { };
+ var slice = Array.prototype.slice, emptyFunction = function() { },
- var IS_DONTENUM_BUGGY = (function(){
- for (var p in { toString: 1 }) {
- if (p === 'toString') return false;
- }
- return true;
- })();
+ IS_DONTENUM_BUGGY = (function(){
+ for (var p in { toString: 1 }) {
+ if (p === 'toString') return false;
+ }
+ return true;
+ })(),
- /** @ignore */
- var addMethods = function(klass, source, parent) {
- for (var property in source) {
+ /** @ignore */
+ addMethods = function(klass, source, parent) {
+ for (var property in source) {
- if (property in klass.prototype &&
- typeof klass.prototype[property] === 'function' &&
- (source[property] + '').indexOf('callSuper') > -1) {
+ if (property in klass.prototype &&
+ typeof klass.prototype[property] === 'function' &&
+ (source[property] + '').indexOf('callSuper') > -1) {
- klass.prototype[property] = (function(property) {
- return function() {
+ klass.prototype[property] = (function(property) {
+ return function() {
- var superclass = this.constructor.superclass;
- this.constructor.superclass = parent;
- var returnValue = source[property].apply(this, arguments);
- this.constructor.superclass = superclass;
+ var superclass = this.constructor.superclass;
+ this.constructor.superclass = parent;
+ var returnValue = source[property].apply(this, arguments);
+ this.constructor.superclass = superclass;
- if (property !== 'initialize') {
- return returnValue;
+ if (property !== 'initialize') {
+ return returnValue;
+ }
+ };
+ })(property);
+ }
+ else {
+ klass.prototype[property] = source[property];
+ }
+
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString !== Object.prototype.toString) {
+ klass.prototype.toString = source.toString;
}
- };
- })(property);
- }
- else {
- klass.prototype[property] = source[property];
- }
-
- if (IS_DONTENUM_BUGGY) {
- if (source.toString !== Object.prototype.toString) {
- klass.prototype.toString = source.toString;
+ if (source.valueOf !== Object.prototype.valueOf) {
+ klass.prototype.valueOf = source.valueOf;
+ }
+ }
}
- if (source.valueOf !== Object.prototype.valueOf) {
- klass.prototype.valueOf = source.valueOf;
- }
- }
- }
- };
+ };
function Subclass() { }
@@ -5043,15 +5061,16 @@ fabric.util.string = {
}
return true;
}
- var getUniqueId = (function () {
- var uid = 0;
- return function (element) {
- return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
- };
- })();
/** @ignore */
- var getElement, setElement;
+ var getElement,
+ setElement,
+ getUniqueId = (function () {
+ var uid = 0;
+ return function (element) {
+ return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
+ };
+ })();
(function () {
var elements = { };
@@ -5207,9 +5226,9 @@ fabric.util.string = {
event || (event = fabric.window.event);
var element = event.target ||
- (typeof event.srcElement !== unknown ? event.srcElement : null);
+ (typeof event.srcElement !== unknown ? event.srcElement : null),
- var scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
+ scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
return {
x: pointerX(event) + scroll.left,
@@ -5222,9 +5241,9 @@ fabric.util.string = {
// is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
// need to investigate later
return (typeof event.clientX !== unknown ? event.clientX : 0);
- };
+ },
- var pointerY = function(event) {
+ pointerY = function(event) {
return (typeof event.clientY !== unknown ? event.clientY : 0);
};
@@ -5339,21 +5358,21 @@ fabric.util.string = {
return typeof id === 'string' ? fabric.document.getElementById(id) : id;
}
- /**
- * Converts an array-like object (e.g. arguments or NodeList) to an array
- * @memberOf fabric.util
- * @param {Object} arrayLike
- * @return {Array}
- */
- var toArray = function(arrayLike) {
- return _slice.call(arrayLike, 0);
- };
+ var sliceCanConvertNodelists,
+ /**
+ * Converts an array-like object (e.g. arguments or NodeList) to an array
+ * @memberOf fabric.util
+ * @param {Object} arrayLike
+ * @return {Array}
+ */
+ toArray = function(arrayLike) {
+ return _slice.call(arrayLike, 0);
+ };
- var sliceCanConvertNodelists;
try {
sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
}
- catch(err) { }
+ catch (err) { }
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
@@ -5395,7 +5414,7 @@ fabric.util.string = {
* @param {String} className Class to add to an element
*/
function addClass(element, className) {
- if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
+ if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
element.className += (element.className ? ' ' : '') + className;
}
}
@@ -5418,7 +5437,14 @@ fabric.util.string = {
wrapper.appendChild(element);
return wrapper;
}
-
+
+ /**
+ * Returns element scroll offsets
+ * @memberOf fabric.util
+ * @param {HTMLElement} element Element to operate on
+ * @param {HTMLElement} upperCanvasEl Upper canvas element
+ * @return {Object} Object with left/top values
+ */
function getScrollLeftTop(element, upperCanvasEl) {
var firstFixedAncestor,
@@ -5469,19 +5495,19 @@ fabric.util.string = {
*/
function getElementOffset(element) {
var docElem,
- box = {left: 0, top: 0},
doc = element && element.ownerDocument,
- offset = {left: 0, top: 0},
+ box = { left: 0, top: 0 },
+ offset = { left: 0, top: 0 },
scrollLeftTop,
offsetAttributes = {
- 'borderLeftWidth': 'left',
- 'borderTopWidth': 'top',
- 'paddingLeft': 'left',
- 'paddingTop': 'top'
+ borderLeftWidth: 'left',
+ borderTopWidth: 'top',
+ paddingLeft: 'left',
+ paddingTop: 'top'
};
- if (!doc){
- return {left: 0, top: 0};
+ if (!doc) {
+ return { left: 0, top: 0 };
}
for (var attr in offsetAttributes) {
@@ -5489,7 +5515,7 @@ fabric.util.string = {
}
docElem = doc.documentElement;
- if ( typeof element.getBoundingClientRect !== "undefined" ) {
+ if ( typeof element.getBoundingClientRect !== 'undefined' ) {
box = element.getBoundingClientRect();
}
@@ -5508,33 +5534,33 @@ fabric.util.string = {
* @param {String} attr Style attribute to get for element
* @return {String} Style attribute value of the given element.
*/
- function getElementStyle(element, attr) {
- if (!element.style) {
- element.style = { };
- }
-
- if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
+ var getElementStyle;
+ if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
+ getElementStyle = function(element, attr) {
return fabric.document.defaultView.getComputedStyle(element, null)[attr];
- }
- else {
+ };
+ }
+ else {
+ getElementStyle = function(element, attr) {
var value = element.style[attr];
- if (!value && element.currentStyle) value = element.currentStyle[attr];
+ if (!value && element.currentStyle) {
+ value = element.currentStyle[attr];
+ }
return value;
- }
+ };
}
(function () {
- var style = fabric.document.documentElement.style;
-
- var selectProp = 'userSelect' in style
- ? 'userSelect'
- : 'MozUserSelect' in style
- ? 'MozUserSelect'
- : 'WebkitUserSelect' in style
- ? 'WebkitUserSelect'
- : 'KhtmlUserSelect' in style
- ? 'KhtmlUserSelect'
- : '';
+ var style = fabric.document.documentElement.style,
+ selectProp = 'userSelect' in style
+ ? 'userSelect'
+ : 'MozUserSelect' in style
+ ? 'MozUserSelect'
+ : 'WebkitUserSelect' in style
+ ? 'WebkitUserSelect'
+ : 'KhtmlUserSelect' in style
+ ? 'KhtmlUserSelect'
+ : '';
/**
* Makes element unselectable
@@ -5587,7 +5613,7 @@ fabric.util.string = {
* @param {Function} callback Callback to execute when script is finished loading
*/
function getScript(url, callback) {
- var headEl = fabric.document.getElementsByTagName("head")[0],
+ var headEl = fabric.document.getElementsByTagName('head')[0],
scriptEl = fabric.document.createElement('script'),
loading = true;
@@ -5631,9 +5657,9 @@ fabric.util.string = {
var makeXHR = (function() {
var factories = [
- function() { return new ActiveXObject("Microsoft.XMLHTTP"); },
- function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
- function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); },
+ function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
+ function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
+ function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
function() { return new XMLHttpRequest(); }
];
for (var i = factories.length; i--; ) {
@@ -5731,7 +5757,7 @@ if (typeof console !== 'undefined') {
* @param {Number} [options.endValue=100] Ending value
* @param {Number} [options.byValue=100] Value to modify the property by
* @param {Function} [options.easing] Easing function
- * @param {Number} [options.duration=500] Duration of change
+ * @param {Number} [options.duration=500] Duration of change (in ms)
*/
function animate(options) {
@@ -5743,7 +5769,7 @@ if (typeof console !== 'undefined') {
finish = start + duration, time,
onChange = options.onChange || function() { },
abort = options.abort || function() { return false; },
- easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t/d * (Math.PI/2)) + c + b;},
+ easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
startValue = 'startValue' in options ? options.startValue : 0,
endValue = 'endValue' in options ? options.endValue : 100,
byValue = options.byValue || endValue - startValue;
@@ -5783,9 +5809,9 @@ if (typeof console !== 'undefined') {
* @param {Function} callback Callback to invoke
* @param {DOMElement} element optional Element to associate with animation
*/
- var requestAnimFrame = function() {
+ function requestAnimFrame() {
return _requestAnimFrame.apply(fabric.window, arguments);
- };
+ }
fabric.util.animate = animate;
fabric.util.requestAnimFrame = requestAnimFrame;
@@ -5796,8 +5822,13 @@ if (typeof console !== 'undefined') {
(function() {
function normalize(a, c, p, s) {
- if (a < Math.abs(c)) { a=c; s=p/4; }
- else s = p/(2*Math.PI) * Math.asin (c/a);
+ if (a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
return { a: a, c: c, p: p, s: s };
}
@@ -5812,7 +5843,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutCubic(t, b, c, d) {
- return c*((t=t/d-1)*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t + 1) + b;
}
/**
@@ -5821,8 +5852,10 @@ if (typeof console !== 'undefined') {
*/
function easeInOutCubic(t, b, c, d) {
t /= d/2;
- if (t < 1) return c/2*t*t*t + b;
- return c/2*((t-=2)*t*t + 2) + b;
+ if (t < 1) {
+ return c / 2 * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t + 2) + b;
}
/**
@@ -5830,7 +5863,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInQuart(t, b, c, d) {
- return c*(t/=d)*t*t*t + b;
+ return c * (t /= d) * t * t * t + b;
}
/**
@@ -5838,7 +5871,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutQuart(t, b, c, d) {
- return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
/**
@@ -5846,9 +5879,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutQuart(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t + b;
- return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t + b;
+ }
+ return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
/**
@@ -5856,7 +5891,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInQuint(t, b, c, d) {
- return c*(t/=d)*t*t*t*t + b;
+ return c * (t /= d) * t * t * t * t + b;
}
/**
@@ -5864,7 +5899,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutQuint(t, b, c, d) {
- return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
/**
@@ -5872,9 +5907,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutQuint(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t*t + b;
- return c/2*((t-=2)*t*t*t*t + 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
/**
@@ -5882,7 +5919,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInSine(t, b, c, d) {
- return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
}
/**
@@ -5890,7 +5927,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutSine(t, b, c, d) {
- return c * Math.sin(t/d * (Math.PI/2)) + b;
+ return c * Math.sin(t / d * (Math.PI / 2)) + b;
}
/**
@@ -5898,7 +5935,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutSine(t, b, c, d) {
- return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
/**
@@ -5906,7 +5943,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInExpo(t, b, c, d) {
- return (t===0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
}
/**
@@ -5914,7 +5951,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutExpo(t, b, c, d) {
- return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
/**
@@ -5922,11 +5959,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutExpo(t, b, c, d) {
- if (t===0) return b;
- if (t===d) return b+c;
- t /= d/2;
- if (t < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ if (t === 0) {
+ return b;
+ }
+ if (t === d) {
+ return b + c;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ }
+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
/**
@@ -5934,7 +5977,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInCirc(t, b, c, d) {
- return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
}
/**
@@ -5942,7 +5985,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutCirc(t, b, c, d) {
- return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
}
/**
@@ -5950,9 +5993,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutCirc(t, b, c, d) {
- t /= d/2;
- if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
- return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ }
+ return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}
/**
@@ -5960,11 +6005,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
return -elastic(opts, t, d) + b;
}
@@ -5974,13 +6025,19 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
- return opts.a*Math.pow(2,-10*t) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p ) + opts.c + b;
+ return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
}
/**
@@ -5988,14 +6045,22 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
- t /= d/2;
- if (t===2) return b+c;
- if (!p) p=d*(0.3*1.5);
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d / 2;
+ if (t === 2) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * (0.3 * 1.5);
+ }
var opts = normalize(a, c, p, s);
- if (t < 1) return -0.5 * elastic(opts, t, d) + b;
- return opts.a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p )*0.5 + opts.c + b;
+ if (t < 1) {
+ return -0.5 * elastic(opts, t, d) + b;
+ }
+ return opts.a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
}
/**
@@ -6003,8 +6068,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*(t/=d)*t*((s+1)*t - s) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
/**
@@ -6012,8 +6079,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
}
/**
@@ -6021,10 +6090,14 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- t /= d/2;
- if (t < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
- return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ }
+ return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
}
/**
@@ -6032,7 +6105,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInBounce(t, b, c, d) {
- return c - easeOutBounce (d-t, 0, c, d) + b;
+ return c - easeOutBounce (d - t, 0, c, d) + b;
}
/**
@@ -6040,14 +6113,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutBounce(t, b, c, d) {
- if ((t/=d) < (1/2.75)) {
- return c*(7.5625*t*t) + b;
- } else if (t < (2/2.75)) {
- return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
- } else if (t < (2.5/2.75)) {
- return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
- } else {
- return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ if ((t /= d) < (1 / 2.75)) {
+ return c * (7.5625 * t * t) + b;
+ }
+ else if (t < (2/2.75)) {
+ return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
+ }
+ else if (t < (2.5/2.75)) {
+ return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
+ }
+ else {
+ return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
}
}
@@ -6056,8 +6132,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutBounce(t, b, c, d) {
- if (t < d/2) return easeInBounce (t*2, 0, c, d) * 0.5 + b;
- return easeOutBounce (t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
+ if (t < d / 2) {
+ return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
+ }
+ return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
}
/**
@@ -6072,7 +6150,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInQuad: function(t, b, c, d) {
- return c*(t/=d)*t + b;
+ return c * (t /= d) * t + b;
},
/**
@@ -6080,7 +6158,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeOutQuad: function(t, b, c, d) {
- return -c *(t/=d)*(t-2) + b;
+ return -c * (t /= d) * (t - 2) + b;
},
/**
@@ -6088,9 +6166,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInOutQuad: function(t, b, c, d) {
- t /= (d/2);
- if (t < 1) return c/2*t*t + b;
- return -c/2 * ((--t)*(t-2) - 1) + b;
+ t /= (d / 2);
+ if (t < 1) {
+ return c / 2 * t * t + b;
+ }
+ return -c / 2 * ((--t) * (t - 2) - 1) + b;
},
/**
@@ -6098,7 +6178,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInCubic: function(t, b, c, d) {
- return c*(t/=d)*t*t + b;
+ return c * (t /= d) * t * t + b;
},
easeOutCubic: easeOutCubic,
@@ -6134,7 +6214,7 @@ if (typeof console !== 'undefined') {
(function(global) {
- "use strict";
+ 'use strict';
/**
* @name fabric
@@ -6146,34 +6226,37 @@ if (typeof console !== 'undefined') {
capitalize = fabric.util.string.capitalize,
clone = fabric.util.object.clone,
toFixed = fabric.util.toFixed,
- multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
+ multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
- var attributesMap = {
- 'fill-opacity': 'fillOpacity',
- 'fill-rule': 'fillRule',
- 'font-family': 'fontFamily',
- 'font-size': 'fontSize',
- 'font-style': 'fontStyle',
- 'font-weight': 'fontWeight',
- 'cx': 'left',
- 'x': 'left',
- 'r': 'radius',
- 'stroke-dasharray': 'strokeDashArray',
- 'stroke-linecap': 'strokeLineCap',
- 'stroke-linejoin': 'strokeLineJoin',
- 'stroke-miterlimit':'strokeMiterLimit',
- 'stroke-opacity': 'strokeOpacity',
- 'stroke-width': 'strokeWidth',
- 'text-decoration': 'textDecoration',
- 'cy': 'top',
- 'y': 'top',
- 'transform': 'transformMatrix'
- };
+ attributesMap = {
+ cx: 'left',
+ x: 'left',
+ r: 'radius',
+ cy: 'top',
+ y: 'top',
+ display: 'visible',
+ visibility: 'visible',
+ transform: 'transformMatrix',
+ 'fill-opacity': 'fillOpacity',
+ 'fill-rule': 'fillRule',
+ 'font-family': 'fontFamily',
+ 'font-size': 'fontSize',
+ 'font-style': 'fontStyle',
+ 'font-weight': 'fontWeight',
+ 'stroke-dasharray': 'strokeDashArray',
+ 'stroke-linecap': 'strokeLineCap',
+ 'stroke-linejoin': 'strokeLineJoin',
+ 'stroke-miterlimit': 'strokeMiterLimit',
+ 'stroke-opacity': 'strokeOpacity',
+ 'stroke-width': 'strokeWidth',
+ 'text-decoration': 'textDecoration',
+ 'text-anchor': 'originX'
+ },
- var colorAttributes = {
- 'stroke': 'strokeOpacity',
- 'fill': 'fillOpacity'
- };
+ colorAttributes = {
+ stroke: 'strokeOpacity',
+ fill: 'fillOpacity'
+ };
function normalizeAttr(attr) {
// transform attribute names
@@ -6204,6 +6287,16 @@ if (typeof console !== 'undefined') {
value = fabric.parseTransformAttribute(value);
}
}
+ else if (attr === 'visible') {
+ value = (value === 'none' || value === 'hidden') ? false : true;
+ // display=none on parent element always takes precedence over child element
+ if (parentAttributes.visible === false) {
+ value = false;
+ }
+ }
+ else if (attr === 'originX' /* text-anchor */) {
+ value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
+ }
isArray = Object.prototype.toString.call(value) === '[object Array]';
@@ -6284,30 +6377,30 @@ if (typeof console !== 'undefined') {
],
// == begin transform regexp
- number = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)',
+ number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- comma_wsp = '(?:\\s+,?\\s*|,\\s*)',
+ commaWsp = '(?:\\s+,?\\s*|,\\s*)',
skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + ')' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + ')' +
+ commaWsp + '(' + number + '))?\\s*\\))',
scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
matrix = '(?:(matrix)\\s*\\(\\s*' +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
'(' + number + ')' +
'\\s*\\))',
@@ -6320,12 +6413,12 @@ if (typeof console !== 'undefined') {
skewY +
')',
- transforms = '(?:' + transform + '(?:' + comma_wsp + transform + ')*' + ')',
+ transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
- transform_list = '^\\s*(?:' + transforms + '?)\\s*$',
+ transformList = '^\\s*(?:' + transforms + '?)\\s*$',
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
- reTransformList = new RegExp(transform_list),
+ reTransformList = new RegExp(transformList),
// == end transform regexp
reTransform = new RegExp(transform, 'g');
@@ -6333,8 +6426,8 @@ if (typeof console !== 'undefined') {
return function(attributeValue) {
// start with identity matrix
- var matrix = iMatrix.concat();
- var matrices = [ ];
+ var matrix = iMatrix.concat(),
+ matrices = [ ];
// return if no argument was given or
// an argument does not match transform attribute regexp
@@ -6350,11 +6443,12 @@ if (typeof console !== 'undefined') {
operation = m[1],
args = m.slice(2).map(parseFloat);
- switch(operation) {
+ switch (operation) {
case 'translate':
translateMatrix(matrix, args);
break;
case 'rotate':
+ args[0] = fabric.util.degreesToRadians(args[0]);
rotateMatrix(matrix, args);
break;
case 'scale':
@@ -6393,19 +6487,19 @@ if (typeof console !== 'undefined') {
if (!match) return;
- var fontStyle = match[1];
- // Font variant is not used
- // var fontVariant = match[2];
- var fontWeight = match[3];
- var fontSize = match[4];
- var lineHeight = match[5];
- var fontFamily = match[6];
+ var fontStyle = match[1],
+ // font variant is not used
+ // fontVariant = match[2],
+ fontWeight = match[3],
+ fontSize = match[4],
+ lineHeight = match[5],
+ fontFamily = match[6];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
- oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
+ oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseFloat(fontSize);
@@ -6493,22 +6587,22 @@ if (typeof console !== 'undefined') {
*/
fabric.parseSVGDocument = (function() {
- var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/;
+ var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
- // \d doesn't quite cut it (as we need to match an actual float number)
+ // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+ // \d doesn't quite cut it (as we need to match an actual float number)
- // matches, e.g.: +14.56e-12, etc.
- var reNum = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)';
+ // matches, e.g.: +14.56e-12, etc.
+ reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- var reViewBoxAttrValue = new RegExp(
- '^' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*' +
- '$'
- );
+ reViewBoxAttrValue = new RegExp(
+ '^' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*' +
+ '$'
+ );
function hasAncestorWithNodeName(element, nodeName) {
while (element && (element = element.parentNode)) {
@@ -6525,10 +6619,10 @@ if (typeof console !== 'undefined') {
var startTime = new Date(),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
- if (descendants.length === 0) {
+ if (descendants.length === 0 && fabric.isLikelyNode) {
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
// https://github.com/ajaxorg/node-o3-xml/issues/21
- descendants = doc.selectNodes("//*[name(.)!='svg']");
+ descendants = doc.selectNodes('//*[name(.)!="svg"]');
var arr = [ ];
for (var i = 0, len = descendants.length; i < len; i++) {
arr[i] = descendants[i];
@@ -6541,30 +6635,43 @@ if (typeof console !== 'undefined') {
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
});
- if (!elements || (elements && !elements.length)) return;
+ if (!elements || (elements && !elements.length)) {
+ callback && callback([], {});
+ return;
+ }
var viewBoxAttr = doc.getAttribute('viewBox'),
- widthAttr = doc.getAttribute('width'),
- heightAttr = doc.getAttribute('height'),
+ widthAttr = parseFloat(doc.getAttribute('width')),
+ heightAttr = parseFloat(doc.getAttribute('height')),
width = null,
height = null,
+ viewBoxWidth,
+ viewBoxHeight,
minX,
minY;
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
- minX = parseInt(viewBoxAttr[1], 10);
- minY = parseInt(viewBoxAttr[2], 10);
- width = parseInt(viewBoxAttr[3], 10);
- height = parseInt(viewBoxAttr[4], 10);
+ minX = parseFloat(viewBoxAttr[1]);
+ minY = parseFloat(viewBoxAttr[2]);
+ viewBoxWidth = parseFloat(viewBoxAttr[3]);
+ viewBoxHeight = parseFloat(viewBoxAttr[4]);
}
- // values of width/height attributes overwrite those extracted from viewbox attribute
- width = widthAttr ? parseFloat(widthAttr) : width;
- height = heightAttr ? parseFloat(heightAttr) : height;
+ if (viewBoxWidth && widthAttr && viewBoxWidth !== widthAttr) {
+ width = viewBoxWidth;
+ height = viewBoxHeight;
+ }
+ else {
+ // values of width/height attributes overwrite those extracted from viewbox attribute
+ width = widthAttr ? widthAttr : viewBoxWidth;
+ height = heightAttr ? heightAttr : viewBoxHeight;
+ }
var options = {
width: width,
- height: height
+ height: height,
+ widthAttr: widthAttr,
+ heightAttr: heightAttr
};
fabric.gradientDefs = fabric.getGradientDefs(doc);
@@ -6748,7 +6855,7 @@ if (typeof console !== 'undefined') {
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
*/
parseElements: function(elements, callback, options, reviver) {
- fabric.ElementsParser.parse(elements, callback, options, reviver);
+ new fabric.ElementsParser(elements, callback, options, reviver).parse();
},
/**
@@ -6762,7 +6869,9 @@ if (typeof console !== 'undefined') {
var oStyle = { },
style = element.getAttribute('style');
- if (!style) return oStyle;
+ if (!style) {
+ return oStyle;
+ }
if (typeof style === 'string') {
parseStyleString(style, oStyle);
@@ -6798,21 +6907,27 @@ if (typeof console !== 'undefined') {
len = points.length;
for (; i < len; i++) {
var pair = points[i].split(',');
- parsedPoints.push({ x: parseFloat(pair[0]), y: parseFloat(pair[1]) });
+ parsedPoints.push({
+ x: parseFloat(pair[0]),
+ y: parseFloat(pair[1])
+ });
}
}
else {
i = 0;
len = points.length;
for (; i < len; i+=2) {
- parsedPoints.push({ x: parseFloat(points[i]), y: parseFloat(points[i+1]) });
+ parsedPoints.push({
+ x: parseFloat(points[i]),
+ y: parseFloat(points[i + 1])
+ });
}
}
// odd number of points is an error
- if (parsedPoints.length % 2 !== 0) {
+ // if (parsedPoints.length % 2 !== 0) {
// return null;
- }
+ // }
return parsedPoints;
},
@@ -6892,13 +7007,13 @@ if (typeof console !== 'undefined') {
function onComplete(r) {
var xml = r.responseXML;
- if (!xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
+ if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
xml = new ActiveXObject('Microsoft.XMLDOM');
xml.async = 'false';
//IE chokes on DOCTYPE
xml.loadXML(r.responseText.replace(//i,''));
}
- if (!xml.documentElement) return;
+ if (!xml || !xml.documentElement) return;
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
svgCache.set(url, {
@@ -6988,78 +7103,80 @@ if (typeof console !== 'undefined') {
})(typeof exports !== 'undefined' ? exports : this);
-fabric.ElementsParser = {
+fabric.ElementsParser = function(elements, callback, options, reviver) {
+ this.elements = elements;
+ this.callback = callback;
+ this.options = options;
+ this.reviver = reviver;
+};
- parse: function(elements, callback, options, reviver) {
+fabric.ElementsParser.prototype.parse = function() {
+ this.instances = new Array(this.elements.length);
+ this.numElements = this.elements.length;
- this.elements = elements;
- this.callback = callback;
- this.options = options;
- this.reviver = reviver;
+ this.createObjects();
+};
- this.instances = new Array(elements.length);
- this.numElements = elements.length;
+fabric.ElementsParser.prototype.createObjects = function() {
+ for (var i = 0, len = this.elements.length; i < len; i++) {
+ (function(_this, i) {
+ setTimeout(function() {
+ _this.createObject(_this.elements[i], i);
+ }, 0);
+ })(this, i);
+ }
+};
- this.createObjects();
- },
-
- createObjects: function() {
- for (var i = 0, len = this.elements.length; i < len; i++) {
- this.createObject(this.elements[i], i);
+fabric.ElementsParser.prototype.createObject = function(el, index) {
+ var klass = fabric[fabric.util.string.capitalize(el.tagName)];
+ if (klass && klass.fromElement) {
+ try {
+ this._createObject(klass, el, index);
}
- },
+ catch (err) {
+ fabric.log(err);
+ }
+ }
+ else {
+ this.checkIfDone();
+ }
+};
- createObject: function(el, index) {
- var klass = fabric[fabric.util.string.capitalize(el.tagName)];
- if (klass && klass.fromElement) {
- try {
- this._createObject(klass, el, index);
- }
- catch(err) {
- fabric.log(err);
- }
- }
- else {
- this.checkIfDone();
- }
- },
+fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
+ if (klass.async) {
+ klass.fromElement(el, this.createCallback(index, el), this.options);
+ }
+ else {
+ var obj = klass.fromElement(el, this.options);
+ this.reviver && this.reviver(el, obj);
+ this.instances.splice(index, 0, obj);
+ this.checkIfDone();
+ }
+};
- _createObject: function(klass, el, index) {
- if (klass.async) {
- klass.fromElement(el, this.createCallback(index, el), this.options);
- }
- else {
- var obj = klass.fromElement(el, this.options);
- this.reviver && this.reviver(el, obj);
- this.instances.splice(index, 0, obj);
- this.checkIfDone();
- }
- },
+fabric.ElementsParser.prototype.createCallback = function(index, el) {
+ var _this = this;
+ return function(obj) {
+ _this.reviver && _this.reviver(el, obj);
+ _this.instances.splice(index, 0, obj);
+ _this.checkIfDone();
+ };
+};
- createCallback: function(index, el) {
- var _this = this;
- return function(obj) {
- _this.reviver && _this.reviver(el, obj);
- _this.instances.splice(index, 0, obj);
- _this.checkIfDone();
- };
- },
-
- checkIfDone: function() {
- if (--this.numElements === 0) {
- this.instances = this.instances.filter(function(el) {
- return el != null;
- });
- fabric.resolveGradients(this.instances);
- this.callback(this.instances);
- }
+fabric.ElementsParser.prototype.checkIfDone = function() {
+ if (--this.numElements === 0) {
+ this.instances = this.instances.filter(function(el) {
+ return el != null;
+ });
+ fabric.resolveGradients(this.instances);
+ this.callback(this.instances);
}
};
(function(global) {
- "use strict";
+ 'use strict';
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
@@ -7309,7 +7426,7 @@ fabric.ElementsParser = {
* @return {String}
*/
toString: function () {
- return this.x + "," + this.y;
+ return this.x + ',' + this.y;
},
/**
@@ -7348,172 +7465,171 @@ fabric.ElementsParser = {
})(typeof exports !== 'undefined' ? exports : this);
-(function(global) {
-
- "use strict";
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Intersection) {
- fabric.warn('fabric.Intersection is already defined');
- return;
- }
-
- /**
- * Intersection class
- * @class fabric.Intersection
- * @memberOf fabric
- * @constructor
- */
- function Intersection(status) {
- this.status = status;
- this.points = [];
- }
-
- fabric.Intersection = Intersection;
-
- fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
- /**
- * Appends a point to intersection
- * @param {fabric.Point} point
- */
- appendPoint: function (point) {
- this.points.push(point);
- },
-
- /**
- * Appends points to intersection
- * @param {Array} points
- */
- appendPoints: function (points) {
- this.points = this.points.concat(points);
- }
- };
-
- /**
- * Checks if one line intersects another
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {fabric.Point} b1
- * @param {fabric.Point} b2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
- var result,
- ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
- ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
- u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
- if (u_b !== 0) {
- var ua = ua_t / u_b,
- ub = ub_t / u_b;
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- result = new Intersection("Intersection");
- result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
- }
- else {
- result = new Intersection();
- }
- }
- else {
- if (ua_t === 0 || ub_t === 0) {
- result = new Intersection("Coincident");
- }
- else {
- result = new Intersection("Parallel");
- }
- }
- return result;
- };
-
- /**
- * Checks if line intersects polygon
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {Array} points
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
- var result = new Intersection(),
- length = points.length;
-
- for (var i = 0; i < length; i++) {
- var b1 = points[i],
- b2 = points[(i+1) % length],
- inter = Intersection.intersectLineLine(a1, a2, b1, b2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects another polygon
- * @static
- * @param {Array} points1
- * @param {Array} points2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
- var result = new Intersection(),
- length = points1.length;
-
- for (var i = 0; i < length; i++) {
- var a1 = points1[i],
- a2 = points1[(i+1) % length],
- inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects rectangle
- * @static
- * @param {Array} points
- * @param {Number} r1
- * @param {Number} r2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
- var min = r1.min(r2),
- max = r1.max(r2),
- topRight = new fabric.Point(max.x, min.y),
- bottomLeft = new fabric.Point(min.x, max.y),
- inter1 = Intersection.intersectLinePolygon(min, topRight, points),
- inter2 = Intersection.intersectLinePolygon(topRight, max, points),
- inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
- inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
- result = new Intersection();
-
- result.appendPoints(inter1.points);
- result.appendPoints(inter2.points);
- result.appendPoints(inter3.points);
- result.appendPoints(inter4.points);
-
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
+(function(global) {
+
+ 'use strict';
+
+ /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
+ var fabric = global.fabric || (global.fabric = { });
+
+ if (fabric.Intersection) {
+ fabric.warn('fabric.Intersection is already defined');
+ return;
+ }
+
+ /**
+ * Intersection class
+ * @class fabric.Intersection
+ * @memberOf fabric
+ * @constructor
+ */
+ function Intersection(status) {
+ this.status = status;
+ this.points = [];
+ }
+
+ fabric.Intersection = Intersection;
+
+ fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
+
+ /**
+ * Appends a point to intersection
+ * @param {fabric.Point} point
+ */
+ appendPoint: function (point) {
+ this.points.push(point);
+ },
+
+ /**
+ * Appends points to intersection
+ * @param {Array} points
+ */
+ appendPoints: function (points) {
+ this.points = this.points.concat(points);
+ }
+ };
+
+ /**
+ * Checks if one line intersects another
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {fabric.Point} b1
+ * @param {fabric.Point} b2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
+ var result,
+ uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
+ ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
+ uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
+ if (uB !== 0) {
+ var ua = uaT / uB,
+ ub = ubT / uB;
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
+ result = new Intersection('Intersection');
+ result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
+ }
+ else {
+ result = new Intersection();
+ }
+ }
+ else {
+ if (uaT === 0 || ubT === 0) {
+ result = new Intersection('Coincident');
+ }
+ else {
+ result = new Intersection('Parallel');
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Checks if line intersects polygon
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {Array} points
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
+ var result = new Intersection(),
+ length = points.length;
+
+ for (var i = 0; i < length; i++) {
+ var b1 = points[i],
+ b2 = points[(i + 1) % length],
+ inter = Intersection.intersectLineLine(a1, a2, b1, b2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects another polygon
+ * @static
+ * @param {Array} points1
+ * @param {Array} points2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
+ var result = new Intersection(),
+ length = points1.length;
+
+ for (var i = 0; i < length; i++) {
+ var a1 = points1[i],
+ a2 = points1[(i + 1) % length],
+ inter = Intersection.intersectLinePolygon(a1, a2, points2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects rectangle
+ * @static
+ * @param {Array} points
+ * @param {Number} r1
+ * @param {Number} r2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
+ var min = r1.min(r2),
+ max = r1.max(r2),
+ topRight = new fabric.Point(max.x, min.y),
+ bottomLeft = new fabric.Point(min.x, max.y),
+ inter1 = Intersection.intersectLinePolygon(min, topRight, points),
+ inter2 = Intersection.intersectLinePolygon(topRight, max, points),
+ inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
+ inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
+ result = new Intersection();
+
+ result.appendPoints(inter1.points);
+ result.appendPoints(inter2.points);
+ result.appendPoints(inter3.points);
+ result.appendPoints(inter4.points);
+
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -7556,6 +7672,11 @@ fabric.ElementsParser = {
color = Color.colorNameMap[color];
}
+ if (color === 'transparent') {
+ this.setSource([255,255,255,0]);
+ return;
+ }
+
source = Color.sourceFromHex(color);
if (!source) {
@@ -7674,15 +7795,15 @@ fabric.ElementsParser = {
* @return {String} ex: FF5555
*/
toHex: function() {
- var source = this.getSource();
+ var source = this.getSource(), r, g, b;
- var r = source[0].toString(16);
+ r = source[0].toString(16);
r = (r.length === 1) ? ('0' + r) : r;
- var g = source[1].toString(16);
+ g = source[1].toString(16);
g = (g.length === 1) ? ('0' + g) : g;
- var b = source[2].toString(16);
+ b = source[2].toString(16);
b = (b.length === 1) ? ('0' + b) : b;
return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
@@ -7769,7 +7890,7 @@ fabric.ElementsParser = {
* @field
* @memberOf fabric.Color
*/
- fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
+ fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
/**
* Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
@@ -7795,23 +7916,23 @@ fabric.ElementsParser = {
* @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
*/
fabric.Color.colorNameMap = {
- 'aqua': '#00FFFF',
- 'black': '#000000',
- 'blue': '#0000FF',
- 'fuchsia': '#FF00FF',
- 'gray': '#808080',
- 'green': '#008000',
- 'lime': '#00FF00',
- 'maroon': '#800000',
- 'navy': '#000080',
- 'olive': '#808000',
- 'orange': '#FFA500',
- 'purple': '#800080',
- 'red': '#FF0000',
- 'silver': '#C0C0C0',
- 'teal': '#008080',
- 'white': '#FFFFFF',
- 'yellow': '#FFFF00'
+ aqua: '#00FFFF',
+ black: '#000000',
+ blue: '#0000FF',
+ fuchsia: '#FF00FF',
+ gray: '#808080',
+ green: '#008000',
+ lime: '#00FF00',
+ maroon: '#800000',
+ navy: '#000080',
+ olive: '#808000',
+ orange: '#FFA500',
+ purple: '#800080',
+ red: '#FF0000',
+ silver: '#C0C0C0',
+ teal: '#008080',
+ white: '#FFFFFF',
+ yellow: '#FFFF00'
};
/**
@@ -7822,11 +7943,21 @@ fabric.ElementsParser = {
* @return {Number}
*/
function hue2rgb(p, q, t){
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ if (t < 0) {
+ t += 1;
+ }
+ if (t > 1) {
+ t -= 1;
+ }
+ if (t < 1/6) {
+ return p + (q - p) * 6 * t;
+ }
+ if (t < 1/2) {
+ return q;
+ }
+ if (t < 2/3) {
+ return p + (q - p) * (2/3 - t) * 6;
+ }
return p;
}
@@ -7903,8 +8034,8 @@ fabric.ElementsParser = {
r = g = b = l;
}
else {
- var q = l <= 0.5 ? l * (s + 1) : l + s - l * s;
- var p = l * 2 - q;
+ var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
+ p = l * 2 - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
@@ -7994,7 +8125,7 @@ fabric.ElementsParser = {
if (style) {
var keyValuePairs = style.split(/\s*;\s*/);
- if (keyValuePairs[keyValuePairs.length-1] === '') {
+ if (keyValuePairs[keyValuePairs.length - 1] === '') {
keyValuePairs.pop();
}
@@ -8097,7 +8228,11 @@ fabric.ElementsParser = {
addColorStop: function(colorStop) {
for (var position in colorStop) {
var color = new fabric.Color(colorStop[position]);
- this.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ this.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this;
},
@@ -8461,10 +8596,10 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {String} SVG representation of a pattern
*/
toSVG: function(object) {
- var patternSource = typeof this.source === 'function' ? this.source() : this.source;
- var patternWidth = patternSource.width / object.getWidth();
- var patternHeight = patternSource.height / object.getHeight();
- var patternImgSrc = '';
+ var patternSource = typeof this.source === 'function' ? this.source() : this.source,
+ patternWidth = patternSource.width / object.getWidth(),
+ patternHeight = patternSource.height / object.getHeight(),
+ patternImgSrc = '';
if (patternSource.src) {
patternImgSrc = patternSource.src;
@@ -8493,11 +8628,23 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {CanvasPattern}
*/
toLive: function(ctx) {
- var source = typeof this.source === 'function' ? this.source() : this.source;
+ var source = typeof this.source === 'function'
+ ? this.source()
+ : this.source;
+
+ // if the image failed to load, return, and allow rest to continue loading
+ if (!source) {
+ return '';
+ }
+
// if an image
if (typeof source.src !== 'undefined') {
- if (!source.complete) return '';
- if (source.naturalWidth === 0 || source.naturalHeight === 0) return '';
+ if (!source.complete) {
+ return '';
+ }
+ if (source.naturalWidth === 0 || source.naturalHeight === 0) {
+ return '';
+ }
}
return ctx.createPattern(source, this.repeat);
}
@@ -8506,7 +8653,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -8587,9 +8734,8 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {Object} Shadow object with color, offsetX, offsetY and blur
*/
_parseShadow: function(shadow) {
- var shadowStr = shadow.trim();
-
- var offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
+ var shadowStr = shadow.trim(),
+ offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
return {
@@ -8679,7 +8825,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
(function () {
- "use strict";
+ 'use strict';
if (fabric.StaticCanvas) {
fabric.warn('fabric.StaticCanvas is already defined.');
@@ -8803,7 +8949,14 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @type Boolean
* @default
*/
- allowTouchScrolling: false,
+ allowTouchScrolling: false,
+
+ /**
+ * Indicates whether this canvas will use image smoothing, this is on by default in browsers
+ * @type Boolean
+ * @default
+ */
+ imageSmoothingEnabled: true,
/**
* The transformation (in the format of Canvas transform) which focuses the viewport
@@ -8830,6 +8983,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
this._createLowerCanvas(el);
this._initOptions(options);
+ this._setImageSmoothing();
if (options.overlayImage) {
this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
@@ -8989,6 +9143,20 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
},
+ /**
+ * @private
+ * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
+ */
+ _setImageSmoothing: function(){
+ var ctx = this.getContext();
+
+ ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.mozImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.msImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.oImageSmoothingEnabled = this.imageSmoothingEnabled;
+ },
+
/**
* @private
* @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
@@ -9076,11 +9244,14 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
this[prop] = options[prop];
}
- this.width = parseInt(this.lowerCanvasEl.width, 10) || 0;
- this.height = parseInt(this.lowerCanvasEl.height, 10) || 0;
+ this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
+ this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
if (!this.lowerCanvasEl.style) return;
+ this.lowerCanvasEl.width = this.width;
+ this.lowerCanvasEl.height = this.height;
+
this.lowerCanvasEl.style.width = this.width + 'px';
this.lowerCanvasEl.style.height = this.height + 'px';
},
@@ -9402,8 +9573,8 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
*/
renderAll: function (allOnTop) {
- var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'];
- var activeGroup = this.getActiveGroup();
+ var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
+ activeGroup = this.getActiveGroup();
if (this.contextTop && this.selection && !this._groupSelector) {
this.clearContext(this.contextTop);
@@ -9444,12 +9615,21 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @param {fabric.Group} activeGroup
*/
_renderObjects: function(ctx, activeGroup) {
- for (var i = 0, length = this._objects.length; i < length; ++i) {
- if (!activeGroup ||
- (activeGroup && this._objects[i] && !activeGroup.contains(this._objects[i]))) {
+ var i, length;
+
+ // fast path
+ if (!activeGroup) {
+ for (i = 0, length = this._objects.length; i < length; ++i) {
this._draw(ctx, this._objects[i]);
}
}
+ else {
+ for (i = 0, length = this._objects.length; i < length; ++i) {
+ if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
+ this._draw(ctx, this._objects[i]);
+ }
+ }
+ }
},
/**
@@ -9538,9 +9718,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
activeGroup.render(ctx);
}
- if (this.overlayImage) {
- ctx.drawImage(this.overlayImage, this.overlayImageLeft, this.overlayImageTop);
- }
+ this._renderOverlay(ctx);
this.fire('after:render');
@@ -9657,11 +9835,20 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
fabric.util.populateWithProperties(this, data, propertiesToInclude);
if (activeGroup) {
- this.setActiveGroup(new fabric.Group(activeGroup.getObjects()));
+ this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
+ originX: 'center',
+ originY: 'center'
+ }));
activeGroup.forEachObject(function(o) {
o.set('active', true);
});
+
+ if (this._currentTransform) {
+ this._currentTransform.target = this.getActiveGroup();
+ }
}
+
+
return data;
},
@@ -9798,7 +9985,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
'width="', (options.viewBox ? options.viewBox.width : this.width), '" ',
'height="', (options.viewBox ? options.viewBox.height : this.height), '" ',
(this.backgroundColor && !this.backgroundColor.toLive
- ? 'style="background-color: ' + this.backgroundColor +'" '
+ ? 'style="background-color: ' + this.backgroundColor + '" '
: null),
(options.viewBox
? 'viewBox="' +
@@ -9930,7 +10117,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
newIdx = idx;
// traverse down the stack looking for the nearest intersecting object
- for (var i=idx-1; i>=0; --i) {
+ for (var i = idx - 1; i >= 0; --i) {
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
object.isContainedWithinObject(this._objects[i]) ||
@@ -9960,7 +10147,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
var idx = this._objects.indexOf(object);
// if object is not on top of stack (last item in an array)
- if (idx !== this._objects.length-1) {
+ if (idx !== this._objects.length - 1) {
var newIdx = this._findNewUpperIndex(object, idx, intersecting);
removeFromArray(this._objects, object);
@@ -9993,7 +10180,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
}
}
else {
- newIdx = idx+1;
+ newIdx = idx + 1;
}
return newIdx;
@@ -10028,7 +10215,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {String} string representation of an instance
*/
toString: function () {
- return '#';
}
});
@@ -10319,27 +10506,27 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
ctx.beginPath();
- var p1 = this._points[0];
- var p2 = this._points[1];
+ var p1 = this._points[0],
+ p2 = this._points[1];
//if we only have 2 points in the path and they are the same
//it means that the user only clicked the canvas without moving the mouse
//then we should be drawing a dot. A path isn't drawn between two identical dots
//that's why we set them apart a bit
if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
- p1.x -= 0.5;
- p2.x += 0.5;
+ p1.x -= 0.5;
+ p2.x += 0.5;
}
ctx.moveTo(p1.x, p1.y);
for (var i = 1, len = this._points.length; i < len; i++) {
- // we pick the point between pi+1 & pi+2 as the
+ // we pick the point between pi + 1 & pi + 2 as the
// end point and p1 as our control point.
var midPoint = p1.midPointFrom(p2);
ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = this._points[i];
- p2 = this._points[i+1];
+ p2 = this._points[i + 1];
}
// Draw last line as a straight line while
// we wait for the next point to be able to calculate
@@ -10365,7 +10552,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @param {Array} points
* @return {Object} object with minx, miny, maxx, maxy
*/
- getPathBoundingBox: function(points) {
+ getPathBoundingBox: function(points) {
var xBounds = [],
yBounds = [],
p1 = points[0],
@@ -10381,19 +10568,19 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
yBounds.push(midPoint.y);
p1 = points[i];
- p2 = points[i+1];
+ p2 = points[i + 1];
startPoint = midPoint;
- } // end for
+ }
- xBounds.push(p1.x);
- yBounds.push(p1.y);
+ xBounds.push(p1.x);
+ yBounds.push(p1.y);
- return {
- minx: utilMin(xBounds),
- miny: utilMin(yBounds),
- maxx: utilMax(xBounds),
- maxy: utilMax(yBounds)
- };
+ return {
+ minx: utilMin(xBounds),
+ miny: utilMin(yBounds),
+ maxx: utilMax(xBounds),
+ maxy: utilMax(yBounds)
+ };
},
/**
@@ -10402,9 +10589,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @return {String} SVG path
*/
convertPointsToSVGPath: function(points, minX, maxX, minY) {
- var path = [];
- var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY);
- var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
+ var path = [],
+ p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
+ p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
for (var i = 1, len = points.length; i < len; i++) {
@@ -10414,8 +10601,8 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
// start point is p(i-1) value.
path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
- if ((i+1) < points.length) {
- p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY);
+ if ((i + 1) < points.length) {
+ p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
}
}
path.push('L ', p1.x, ' ', p1.y, ' ');
@@ -10454,7 +10641,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
ctx.closePath();
var pathData = this._getSVGPathData().join('');
- if (pathData === "M 0 0 Q 0 0 0 0 L 0 0") {
+ if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
@@ -10464,8 +10651,8 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
}
// set path origin coordinates based on our bounding box
- var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2;
- var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2;
+ var originLeft = this.box.minx + (this.box.maxx - this.box.minx) / 2,
+ originTop = this.box.miny + (this.box.maxy - this.box.miny) / 2;
this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
@@ -10518,8 +10705,8 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @param {Object} pointer
*/
drawDot: function(pointer) {
- var point = this.addPoint(pointer);
- var ctx = this.canvas.contextTop;
+ var point = this.addPoint(pointer),
+ ctx = this.canvas.contextTop;
var v = this.canvas.viewportTransform;
ctx.save();
@@ -10562,15 +10749,15 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
var circles = [ ];
for (var i = 0, len = this.points.length; i < len; i++) {
- var point = this.points[i];
- var circle = new fabric.Circle({
- radius: this.points[i].radius,
- left: point.x,
- top: point.y,
- originX: 'center',
- originY: 'center',
- fill: this.points[i].fill
- });
+ var point = this.points[i],
+ circle = new fabric.Circle({
+ radius: point.radius,
+ left: point.x,
+ top: point.y,
+ originX: 'center',
+ originY: 'center',
+ fill: point.fill
+ });
this.shadow && circle.setShadow(this.shadow);
@@ -10593,12 +10780,12 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @return {fabric.Point} Just added pointer point
*/
addPoint: function(pointer) {
- var pointerPoint = new fabric.Point(pointer.x, pointer.y);
+ var pointerPoint = new fabric.Point(pointer.x, pointer.y),
- var circleRadius = fabric.util.getRandomInt(
- Math.max(0, this.width - 20), this.width + 20) / 2;
+ circleRadius = fabric.util.getRandomInt(
+ Math.max(0, this.width - 20), this.width + 20) / 2,
- var circleColor = new fabric.Color(this.color)
+ circleColor = new fabric.Color(this.color)
.setAlpha(fabric.util.getRandomInt(0, 100) / 100)
.toRgba();
@@ -10903,6 +11090,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @fires mouse:down
* @fires mouse:move
* @fires mouse:up
+ * @fires mouse:over
+ * @fires mouse:out
*
*/
fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
@@ -11077,10 +11266,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var t = this._currentTransform;
t.target.set({
- 'scaleX': t.original.scaleX,
- 'scaleY': t.original.scaleY,
- 'left': t.original.left,
- 'top': t.original.top
+ scaleX: t.original.scaleX,
+ scaleY: t.original.scaleY,
+ left: t.original.left,
+ top: t.original.top
});
if (this._shouldCenterTransform(e, t.target)) {
@@ -11127,7 +11316,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
- return (target.containsPoint(xy) || target._findTargetCorner(e, this._offset));
+ return (target.containsPoint(xy) || target._findTargetCorner(pointer));
},
/**
@@ -11137,14 +11326,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var activeGroup = this.getActiveGroup(),
x = pointer.x,
y = pointer.y,
+ isObjectInGroup = (
+ activeGroup &&
+ object.type !== 'group' &&
+ activeGroup.contains(object)),
lt;
- var isObjectInGroup = (
- activeGroup &&
- object.type !== 'group' &&
- activeGroup.contains(object)
- );
-
if (isObjectInGroup) {
lt = new fabric.Point(activeGroup.left, activeGroup.top);
lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
@@ -11279,11 +11466,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_setupCurrentTransform: function (e, target) {
if (!target) return;
- var corner = target._findTargetCorner(e, this._offset),
- pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
- fabric.util.invertTransform(this.viewportTransform)
- ),
+ var pointer = this.getPointer(e),
+ corner = target._findTargetCorner(this.getPointer(e, true)),
action = this._getActionFromCorner(target, corner),
origin = this._getOriginFromCorner(target, corner);
@@ -11345,7 +11529,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_scaleObject: function (x, y, by) {
var t = this._currentTransform,
- offset = this._offset,
target = t.target,
lockScalingX = target.get('lockScalingX'),
lockScalingY = target.get('lockScalingY');
@@ -11353,8 +11536,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (lockScalingX && lockScalingY) return;
// Get the constraint point
- var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
- var localMouse = target.toLocalPoint(new fabric.Point(x - offset.left, y - offset.top), t.originX, t.originY);
+ var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
+ localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
this._setLocalMouse(localMouse, t);
@@ -11401,9 +11584,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_scaleObjectEqually: function(localMouse, target, transform) {
- var dist = localMouse.y + localMouse.x;
-
- var lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
+ var dist = localMouse.y + localMouse.x,
+ lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
(target.width + (target.strokeWidth)) * transform.original.scaleX;
// We use transform.scaleX/Y instead of target.scaleX/Y
@@ -11500,13 +11682,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_rotateObject: function (x, y) {
- var t = this._currentTransform,
- o = this._offset;
+ var t = this._currentTransform;
if (t.target.get('lockRotation')) return;
- var lastAngle = atan2(t.ey - t.top - o.top, t.ex - t.left - o.left),
- curAngle = atan2(y - t.top - o.top, x - t.left - o.left),
+ var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
+ curAngle = atan2(y - t.top, x - t.left),
angle = radiansToDegrees(curAngle - lastAngle + t.theta);
// normalize angle to positive value
@@ -11559,15 +11740,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// selection border
if (this.selectionDashArray.length > 1) {
- var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft);
- var py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
+ var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft),
+ py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, px, py, px+aleft, py, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py+atop-1, px+aleft, py+atop-1, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py, px, py+atop, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px+aleft-1, py, px+aleft-1, py+atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
ctx.closePath();
ctx.stroke();
@@ -11591,7 +11772,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.lastRenderedObjectWithControlsAboveOverlay &&
this.lastRenderedObjectWithControlsAboveOverlay.visible &&
this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
- this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e, this._offset));
+ this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
},
/**
@@ -11609,10 +11790,56 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// first check current group (if one exists)
var activeGroup = this.getActiveGroup();
if (activeGroup && !skipGroup && this.containsPoint(e, activeGroup)) {
+ console.log('AG', activeGroup);
return activeGroup;
}
- return this._searchPossibleTargets(e);
+ var target = this._searchPossibleTargets(e);
+ this._fireOverOutEvents(target);
+
+ return target;
+ },
+
+ /**
+ * @private
+ */
+ _fireOverOutEvents: function(target) {
+ if (target) {
+ if (this._hoveredTarget !== target) {
+ this.fire('mouse:over', { target: target });
+ target.fire('mouseover');
+ if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ }
+ this._hoveredTarget = target;
+ }
+ }
+ else if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ this._hoveredTarget = null;
+ }
+ },
+
+ /**
+ * @private
+ */
+ _checkTarget: function(e, obj, pointer) {
+ if (obj &&
+ obj.visible &&
+ obj.evented &&
+ this.containsPoint(e, obj)){
+ if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
+ var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
+ if (!isTransparent) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
},
/**
@@ -11621,33 +11848,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_searchPossibleTargets: function(e) {
// Cache all targets where their bounding box contains point.
- var possibleTargets = [],
- target,
- pointer = this.getPointer(e, true);
+ var target,
+ pointer = this.getPointer(e);
- for (var i = this._objects.length; i--; ) {
- if (this._objects[i] &&
- this._objects[i].visible &&
- this._objects[i].evented &&
- this.containsPoint(e, this._objects[i])) {
+ var i = this._objects.length;
- if (this.perPixelTargetFind || this._objects[i].perPixelTargetFind) {
- possibleTargets[possibleTargets.length] = this._objects[i];
- }
- else {
- target = this._objects[i];
- this.relatedTarget = target;
- break;
- }
- }
- }
-
- for (var j = 0, len = possibleTargets.length; j < len; j++) {
- pointer = this.getPointer(e, true);
- var isTransparent = this.isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
- if (!isTransparent) {
- target = possibleTargets[j];
- this.relatedTarget = target;
+ while (i--) {
+ if (this._checkTarget(e, this._objects[i], pointer)){
+ this.relatedTarget = this._objects[i];
+ target = this._objects[i];
break;
}
}
@@ -11664,7 +11873,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (!upperCanvasEl) {
upperCanvasEl = this.upperCanvasEl;
}
- var pointer = getPointer(e, upperCanvasEl);
+ var pointer = getPointer(e, upperCanvasEl),
+ bounds = upperCanvasEl.getBoundingClientRect(),
+ cssScale;
+
+ pointer.x = pointer.x - this._offset.left;
+ pointer.y = pointer.y - this._offset.top;
if (!ignoreZoom) {
pointer = fabric.util.transformPoint(
pointer,
@@ -11672,9 +11886,19 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
);
}
+ if (bounds.width === 0 || bounds.height === 0) {
+ // If bounds are not available (i.e. not visible), do not apply scale.
+ cssScale = { width: 1, height: 1 };
+ }
+ else {
+ cssScale = {
+ width: upperCanvasEl.width / bounds.width,
+ height: upperCanvasEl.height / bounds.height
+ };
+ }
return {
- x: pointer.x - this._offset.left,
- y: pointer.y - this._offset.top
+ x: pointer.x * cssScale.width,
+ y: pointer.y * cssScale.height
};
},
@@ -11985,32 +12209,36 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
(function(){
- var cursorMap = [
- 'n-resize',
- 'ne-resize',
- 'e-resize',
- 'se-resize',
- 's-resize',
- 'sw-resize',
- 'w-resize',
- 'nw-resize'
- ],
- cursorOffset = {
- 'mt': 0, // n
- 'tr': 1, // ne
- 'mr': 2, // e
- 'br': 3, // se
- 'mb': 4, // s
- 'bl': 5, // sw
- 'ml': 6, // w
- 'tl': 7 // nw
+ var cursorOffset = {
+ mt: 0, // n
+ tr: 1, // ne
+ mr: 2, // e
+ br: 3, // se
+ mb: 4, // s
+ bl: 5, // sw
+ ml: 6, // w
+ tl: 7 // nw
},
addListener = fabric.util.addListener,
- removeListener = fabric.util.removeListener,
- getPointer = fabric.util.getPointer;
+ removeListener = fabric.util.removeListener;
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
+ /**
+ * Map of cursor style values for each of the object controls
+ * @private
+ */
+ cursorMap: [
+ 'n-resize',
+ 'ne-resize',
+ 'e-resize',
+ 'se-resize',
+ 's-resize',
+ 'sw-resize',
+ 'w-resize',
+ 'nw-resize'
+ ],
+
/**
* Adds mouse listeners to canvas
* @private
@@ -12126,14 +12354,20 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_onMouseDown: function (e) {
this.__onMouseDown(e);
- addListener(fabric.document, 'mouseup', this._onMouseUp);
addListener(fabric.document, 'touchend', this._onMouseUp);
-
- addListener(fabric.document, 'mousemove', this._onMouseMove);
addListener(fabric.document, 'touchmove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchstart') {
+ // Unbind mousedown to prevent double triggers from touch devices
+ removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
+ }
+ else {
+ addListener(fabric.document, 'mouseup', this._onMouseUp);
+ addListener(fabric.document, 'mousemove', this._onMouseMove);
+ }
},
/**
@@ -12151,6 +12385,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchend') {
+ // Wait 400ms before rebinding mousedown to prevent double triggers
+ // from touch devices
+ var _this = this;
+ setTimeout(function() {
+ addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
+ }, 400);
+ }
},
/**
@@ -12249,8 +12492,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_finalizeCurrentTransform: function() {
- var transform = this._currentTransform;
- var target = transform.target;
+ var transform = this._currentTransform,
+ target = transform.target;
if (target._scaling) {
target._scaling = false;
@@ -12392,7 +12635,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.stateful && target.saveState();
// determine if it's a drag or rotate case
- if ((corner = target._findTargetCorner(e, this._offset))) {
+ if ((corner = target._findTargetCorner(this.getPointer(e)))) {
this.onBeforeScaleRotate(target);
}
@@ -12514,11 +12757,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {Event} e Event fired on mousemove
*/
_transformObject: function(e) {
-
var pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
+ fabric.util.getPointer(e, this.upperCanvasEl),
fabric.util.invertTransform(this.viewportTransform)
),
+ pointer = this.getPointer(e),
transform = this._currentTransform;
transform.reset = false,
@@ -12566,7 +12809,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fire: function(eventName, target, e) {
- this.fire('object:' + eventName, { target: target, e: e});
+ this.fire('object:' + eventName, { target: target, e: e });
target.fire(eventName, { e: e });
},
@@ -12623,11 +12866,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return false;
}
else {
- var activeGroup = this.getActiveGroup();
- // only show proper corner when group selection is not active
- var corner = target._findTargetCorner
+ var activeGroup = this.getActiveGroup(),
+ // only show proper corner when group selection is not active
+ corner = target._findTargetCorner
&& (!activeGroup || !activeGroup.contains(target))
- && target._findTargetCorner(e, this._offset);
+ && target._findTargetCorner(this.getPointer(e, true));
if (!corner) {
style.cursor = target.hoverCursor || this.hoverCursor;
@@ -12670,7 +12913,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// normalize n to be from 0 to 7
n %= 8;
- return cursorMap[n];
+ return this.cursorMap[n];
}
});
})();
@@ -12777,13 +13020,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_createGroup: function(target) {
- var objects = this.getObjects();
-
- var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target);
-
- var groupObjects = isActiveLower
- ? [ this._activeObject, target ]
- : [ target, this._activeObject ];
+ var objects = this.getObjects(),
+ isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
+ groupObjects = isActiveLower
+ ? [ this._activeObject, target ]
+ : [ target, this._activeObject ];
return new fabric.Group(groupObjects, {
originX: 'center',
@@ -12935,8 +13176,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.renderAll(true);
- var canvasEl = this.upperCanvasEl || this.lowerCanvasEl;
- var croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
+ var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
+ croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
if (format === 'jpg') {
@@ -12963,9 +13204,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
__getCroppedCanvas: function(canvasEl, cropping) {
var croppedCanvasEl,
- croppedCtx;
-
- var shouldCrop = 'left' in cropping ||
+ croppedCtx,
+ shouldCrop = 'left' in cropping ||
'top' in cropping ||
'width' in cropping ||
'height' in cropping;
@@ -12998,7 +13238,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
ctx = this.contextTop || this.contextContainer;
- this.setWidth(scaledWidth).setHeight(scaledHeight);
+ if (multiplier > 1) {
+ this.setWidth(scaledWidth).setHeight(scaledHeight);
+ }
ctx.scale(multiplier, multiplier);
if (cropping.left) {
@@ -13010,9 +13252,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (cropping.width) {
cropping.width *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.width = scaledWidth;
+ }
if (cropping.height) {
cropping.height *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.height = scaledHeight;
+ }
if (activeGroup) {
// not removing group due to complications with restoring it with correct state afterwords
@@ -13225,8 +13473,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_enlivenObjects: function (objects, callback, reviver) {
var _this = this;
- if (objects.length === 0) {
+ if (!objects || objects.length === 0) {
callback && callback();
+ return;
}
var renderOnAddRemove = this.renderOnAddRemove;
@@ -13324,7 +13573,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
__onTransformGesture: function(e, self) {
- if (this.isDrawingMode || e.touches.length !== 2 || 'gesture' !== self.gesture) {
+ if (this.isDrawingMode || !e.touches || e.touches.length !== 2 || 'gesture' !== self.gesture) {
return;
}
@@ -13335,7 +13584,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._scaleObjectBy(self.scale);
}
- this.fire('touch:gesture', {target: target, e: e, self: self});
+ this.fire('touch:gesture', { target: target, e: e, self: self });
},
/**
@@ -13345,7 +13594,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onDrag: function(e, self) {
- this.fire('touch:drag', {e: e, self: self});
+ this.fire('touch:drag', { e: e, self: self });
},
/**
@@ -13355,7 +13604,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onOrientationChange: function(e, self) {
- this.fire('touch:orientation', {e: e, self: self});
+ this.fire('touch:orientation', { e: e, self: self });
},
/**
@@ -13365,7 +13614,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onShake: function(e, self) {
- this.fire('touch:shake', {e: e, self: self});
+ this.fire('touch:shake', { e: e, self: self });
},
/**
@@ -13376,10 +13625,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_scaleObjectBy: function(s, by) {
var t = this._currentTransform,
- target = t.target;
-
- var lockScalingX = target.get('lockScalingX'),
- lockScalingY = target.get('lockScalingY');
+ target = t.target,
+ lockScalingX = target.get('lockScalingX'),
+ lockScalingY = target.get('lockScalingY');
if (lockScalingX && lockScalingY) return;
@@ -13417,7 +13665,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -14028,6 +14276,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Function that determines clipping of an object (context is passed as a first argument)
+ * Note that context origin is at the object's center point (not left/top corner)
* @type Function
*/
clipTo: null,
@@ -14169,34 +14418,34 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
toObject: function(propertiesToInclude) {
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var object = {
- type: this.type,
- originX: this.originX,
- originY: this.originY,
- left: toFixed(this.left, NUM_FRACTION_DIGITS),
- top: toFixed(this.top, NUM_FRACTION_DIGITS),
- width: toFixed(this.width, NUM_FRACTION_DIGITS),
- height: toFixed(this.height, NUM_FRACTION_DIGITS),
- fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
- stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
- strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
- strokeDashArray: this.strokeDashArray,
- strokeLineCap: this.strokeLineCap,
- strokeLineJoin: this.strokeLineJoin,
- strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
- scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
- scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
- angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
- flipX: this.flipX,
- flipY: this.flipY,
- opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
- shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
- visible: this.visible,
- clipTo: this.clipTo && String(this.clipTo),
- backgroundColor: this.backgroundColor
- };
+ object = {
+ type: this.type,
+ originX: this.originX,
+ originY: this.originY,
+ left: toFixed(this.left, NUM_FRACTION_DIGITS),
+ top: toFixed(this.top, NUM_FRACTION_DIGITS),
+ width: toFixed(this.width, NUM_FRACTION_DIGITS),
+ height: toFixed(this.height, NUM_FRACTION_DIGITS),
+ fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
+ stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
+ strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
+ strokeDashArray: this.strokeDashArray,
+ strokeLineCap: this.strokeLineCap,
+ strokeLineJoin: this.strokeLineJoin,
+ strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
+ scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
+ scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
+ angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
+ flipX: this.flipX,
+ flipY: this.flipY,
+ opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
+ shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
+ visible: this.visible,
+ clipTo: this.clipTo && String(this.clipTo),
+ backgroundColor: this.backgroundColor
+ };
if (!this.includeDefaultValues) {
object = this._removeDefaultValues(object);
@@ -14222,8 +14471,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Object} object
*/
_removeDefaultValues: function(object) {
- var prototype = fabric.util.getKlass(object.type).prototype;
- var stateProperties = prototype.stateProperties;
+ var prototype = fabric.util.getKlass(object.type).prototype,
+ stateProperties = prototype.stateProperties;
stateProperties.forEach(function(prop) {
if (object[prop] === prototype[prop]) {
@@ -14239,7 +14488,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {String}
*/
toString: function() {
- return "#";
+ return '#';
},
/**
@@ -14251,6 +14500,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this[property];
},
+ /**
+ * @private
+ */
+ _setObject: function(obj) {
+ for (var prop in obj) {
+ this._set(prop, obj[prop]);
+ }
+ },
+
/**
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
* @param {String|Object} key Property name or object (if object, iterate over the object properties)
@@ -14260,9 +14518,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
set: function(key, value) {
if (typeof key === 'object') {
- for (var prop in key) {
- this._set(prop, key[prop]);
- }
+ this._setObject(key);
}
else {
if (typeof value === 'function' && key !== 'clipTo') {
@@ -14332,6 +14588,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this;
},
+ /**
+ * Retrieves viewportTransform from Object's canvas if possible
+ * @method getViewportTransform
+ * @memberOf fabric.Object.prototype
+ * @return {Boolean} flipY value // TODO
+ */
+ getViewportTransform: function() {
+ if (this.canvas && this.canvas.viewportTransform)
+ return this.canvas.viewportTransform;
+ return [1, 0, 0, 1, 0, 0];
+ },
+
/**
* Renders an object on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -14343,6 +14611,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
ctx.save();
+ //setup fill rule for current object
+ this._setupFillRule(ctx);
+
this._transform(ctx, noTransform);
this._setStrokeStyles(ctx);
this._setFillStyles(ctx);
@@ -14358,6 +14629,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._render(ctx, noTransform);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
+ this._restoreFillRule(ctx);
+
ctx.restore();
this._renderControls(ctx, noTransform);
@@ -14365,7 +14638,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_transform: function(ctx, noTransform) {
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
@@ -14403,7 +14676,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Boolean} [noTransform] When true, context is not transformed
*/
_renderControls: function(ctx, noTransform) {
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
if (this.active && !noTransform) {
@@ -14444,6 +14717,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_removeShadow: function(ctx) {
+ if (!this.shadow) return;
+
ctx.shadowColor = '';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
},
@@ -14461,7 +14736,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
-this.width / 2 + this.fill.offsetX || 0,
-this.height / 2 + this.fill.offsetY || 0);
}
- ctx.fill();
+ if (this.fillRule === 'destination-over') {
+ ctx.fill('evenodd');
+ }
+ else {
+ ctx.fill();
+ }
if (this.fill.toLive) {
ctx.restore();
}
@@ -14660,7 +14940,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
setGradient: function(property, options) {
options || (options = { });
- var gradient = {colorStops: []};
+ var gradient = { colorStops: [] };
gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
gradient.coords = {
@@ -14677,7 +14957,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
for (var position in options.colorStops) {
var color = new fabric.Color(options.colorStops[position]);
- gradient.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ gradient.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this.set(property, fabric.Gradient.forObject(this, gradient));
@@ -14735,7 +15019,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Sets "color" of an instance (alias of `set('fill', …)`)
* @param {String} color Color value
- * @return {fabric.Text} thisArg
+ * @return {fabric.Object} thisArg
* @chainable
*/
setColor: function(color) {
@@ -14743,6 +15027,28 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this;
},
+ /**
+ * Sets "angle" of an instance
+ * @param {Number} angle Angle value
+ * @return {fabric.Object} thisArg
+ * @chainable
+ */
+ setAngle: function(angle) {
+ var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
+
+ if (shouldCenterOrigin) {
+ this._setOriginToCenter();
+ }
+
+ this.set('angle', angle);
+
+ if (shouldCenterOrigin) {
+ this._resetOrigin();
+ }
+
+ return this;
+ },
+
/**
* Centers object horizontally on canvas to which it was added last.
* You might need to call `setCoords` on an object after centering, to update controls area.
@@ -14782,7 +15088,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @chainable
*/
remove: function() {
- return this.canvas.remove(this);
+ this.canvas.remove(this);
+ return this;
},
/**
@@ -14798,6 +15105,28 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
x: pointer.x - objectLeftTop.x,
y: pointer.y - objectLeftTop.y
};
+ },
+
+ /**
+ * Sets canvas globalCompositeOperation for specific object
+ * custom composition operation for the particular object can be specifed using fillRule property
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _setupFillRule: function (ctx) {
+ if (this.fillRule) {
+ this._prevFillRule = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = this.fillRule;
+ }
+ },
+
+ /**
+ * Restores previously saved canvas globalCompositeOperation after obeject rendering
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _restoreFillRule: function (ctx) {
+ if (this.fillRule && this._prevFillRule) {
+ ctx.globalCompositeOperation = this._prevFillRule;
+ }
}
});
@@ -14851,17 +15180,17 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
cy = point.y,
strokeWidth = this.stroke ? this.strokeWidth : 0;
- if (originX === "left") {
+ if (originX === 'left') {
cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -14882,16 +15211,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
strokeWidth = this.stroke ? this.strokeWidth : 0;
// Get the point coordinates
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -14941,20 +15270,20 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
x, y;
if (originX && originY) {
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
else {
x = center.x;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
else {
@@ -14987,8 +15316,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {void}
*/
setPositionByOrigin: function(pos, originX, originY) {
- var center = this.translateToCenterPoint(pos, originX, originY);
- var position = this.translateToOriginPoint(center, this.originX, this.originY);
+ var center = this.translateToCenterPoint(pos, originX, originY),
+ position = this.translateToOriginPoint(center, this.originX, this.originY);
this.set('left', position.x);
this.set('top', position.y);
@@ -14998,13 +15327,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {String} to One of 'left', 'center', 'right'
*/
adjustPosition: function(to) {
- var angle = degreesToRadians(this.angle);
- var hypotHalf = this.getWidth() / 2;
- var xHalf = Math.cos(angle) * hypotHalf;
- var yHalf = Math.sin(angle) * hypotHalf;
- var hypotFull = this.getWidth();
- var xFull = Math.cos(angle) * hypotFull;
- var yFull = Math.sin(angle) * hypotFull;
+ var angle = degreesToRadians(this.angle),
+ hypotHalf = this.getWidth() / 2,
+ xHalf = Math.cos(angle) * hypotHalf,
+ yHalf = Math.sin(angle) * hypotHalf,
+ hypotFull = this.getWidth(),
+ xFull = Math.cos(angle) * hypotFull,
+ yFull = Math.sin(angle) * hypotFull;
if (this.originX === 'center' && to === 'left' ||
this.originX === 'right' && to === 'center') {
@@ -15033,6 +15362,45 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.originX = to;
},
+ /**
+ * @private
+ * Sets the origin/position of the object to it's center point
+ * @return {void}
+ */
+ _setOriginToCenter: function() {
+ this._originalOriginX = this.originX;
+ this._originalOriginY = this.originY;
+
+ var center = this.getCenterPoint();
+
+ this.originX = 'center';
+ this.originY = 'center';
+
+ this.left = center.x;
+ this.top = center.y;
+ },
+
+ /**
+ * @private
+ * Resets the origin/position of the object to it's original origin
+ * @return {void}
+ */
+ _resetOrigin: function() {
+ var originPoint = this.translateToOriginPoint(
+ this.getCenterPoint(),
+ this._originalOriginX,
+ this._originalOriginY);
+
+ this.originX = this._originalOriginX;
+ this.originY = this._originalOriginY;
+
+ this.left = originPoint.x;
+ this.top = originPoint.y;
+
+ this._originalOriginX = null;
+ this._originalOriginY = null;
+ },
+
/**
* @private
*/
@@ -15068,13 +15436,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
- br = new fabric.Point(oCoords.br.x, oCoords.br.y);
-
- var intersection = fabric.Intersection.intersectPolygonRectangle(
- [tl, tr, br, bl],
- pointTL,
- pointBR
- );
+ br = new fabric.Point(oCoords.br.x, oCoords.br.y),
+ intersection = fabric.Intersection.intersectPolygonRectangle(
+ [tl, tr, br, bl],
+ pointTL,
+ pointBR
+ );
return intersection.status === 'Intersection';
},
@@ -15094,12 +15461,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
};
}
var thisCoords = getCoords(this.oCoords),
- otherCoords = getCoords(other.oCoords);
-
- var intersection = fabric.Intersection.intersectPolygonPolygon(
- [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
- [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
- );
+ otherCoords = getCoords(other.oCoords),
+ intersection = fabric.Intersection.intersectPolygonPolygon(
+ [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
+ [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
+ );
return intersection.status === 'Intersection';
},
@@ -15127,10 +15493,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var boundingRect = this.getBoundingRect();
return (
- boundingRect.left > pointTL.x &&
- boundingRect.left + boundingRect.width < pointBR.x &&
- boundingRect.top > pointTL.y &&
- boundingRect.top + boundingRect.height < pointBR.y
+ boundingRect.left >= pointTL.x &&
+ boundingRect.left + boundingRect.width <= pointBR.x &&
+ boundingRect.top >= pointTL.y &&
+ boundingRect.top + boundingRect.height <= pointBR.y
);
},
@@ -15204,7 +15570,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
else {
b1 = 0;
b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
- a1 = point.y- b1 * point.x;
+ a1 = point.y - b1 * point.x;
a2 = iLine.o.y - b2 * iLine.o.x;
xi = - (a1 - a2) / (b1 - b2);
@@ -15247,15 +15613,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
getBoundingRect: function() {
this.oCoords || this.setCoords();
- var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x];
- var minX = fabric.util.array.min(xCoords);
- var maxX = fabric.util.array.max(xCoords);
- var width = Math.abs(minX - maxX);
+ var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
+ minX = fabric.util.array.min(xCoords),
+ maxX = fabric.util.array.max(xCoords),
+ width = Math.abs(minX - maxX),
- var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y];
- var minY = fabric.util.array.min(yCoords);
- var maxY = fabric.util.array.max(yCoords);
- var height = Math.abs(minY - maxY);
+ yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
+ minY = fabric.util.array.min(yCoords),
+ maxY = fabric.util.array.max(yCoords),
+ height = Math.abs(minY - maxY);
return {
left: minX,
@@ -15289,12 +15655,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_constrainScale: function(value) {
if (Math.abs(value) < this.minScaleLimit) {
- if (value < 0)
+ if (value < 0) {
return -this.minScaleLimit;
- else
+ }
+ else {
return this.minScaleLimit;
+ }
}
-
return value;
},
@@ -15355,7 +15722,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
theta = degreesToRadians(this.angle),
vpt;
// TODO: ideally we should never setCoords an object which lacks a canvas
- vpt = this.canvas ? this.canvas.viewportTransform : [1, 0, 0, 1, 0, 0];
+ vpt = this.getViewportTransform();
var f = function (p) {
return fabric.util.transformPoint(p, vpt);
@@ -15370,13 +15737,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
}
var _hypotenuse = Math.sqrt(
- Math.pow(this.currentWidth / 2, 2) +
- Math.pow(this.currentHeight / 2, 2));
+ Math.pow(this.currentWidth / 2, 2) +
+ Math.pow(this.currentHeight / 2, 2)),
- var _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0);
+ _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
- // offset added for rotate and scale actions
- var offsetX = Math.cos(_angle + theta) * _hypotenuse,
+ // offset added for rotate and scale actions
+ offsetX = Math.cos(_angle + theta) * _hypotenuse,
offsetY = Math.sin(_angle + theta) * _hypotenuse,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
@@ -15533,32 +15900,32 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
getSvgStyles: function() {
var fill = this.fill
- ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
- : 'none';
+ ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
+ : 'none',
- var stroke = this.stroke
- ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
- : 'none';
+ stroke = this.stroke
+ ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
+ : 'none',
- var strokeWidth = this.strokeWidth ? this.strokeWidth : '0';
- var strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '';
- var strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt';
- var strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter';
- var strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4';
- var opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1';
+ strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
+ strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
+ strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
+ strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
+ strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
+ opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
- var visibility = this.visible ? '' : " visibility: hidden;";
- var filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
+ visibility = this.visible ? '' : ' visibility: hidden;',
+ filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
return [
- "stroke: ", stroke, "; ",
- "stroke-width: ", strokeWidth, "; ",
- "stroke-dasharray: ", strokeDashArray, "; ",
- "stroke-linecap: ", strokeLineCap, "; ",
- "stroke-linejoin: ", strokeLineJoin, "; ",
- "stroke-miterlimit: ", strokeMiterLimit, "; ",
- "fill: ", fill, "; ",
- "opacity: ", opacity, ";",
+ 'stroke: ', stroke, '; ',
+ 'stroke-width: ', strokeWidth, '; ',
+ 'stroke-dasharray: ', strokeDashArray, '; ',
+ 'stroke-linecap: ', strokeLineCap, '; ',
+ 'stroke-linejoin: ', strokeLineJoin, '; ',
+ 'stroke-miterlimit: ', strokeMiterLimit, '; ',
+ 'fill: ', fill, '; ',
+ 'opacity: ', opacity, ';',
filter,
visibility
].join('');
@@ -15569,34 +15936,37 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransform: function() {
- var toFixed = fabric.util.toFixed;
- var angle = this.getAngle();
- var center = this.getCenterPoint();
+ var toFixed = fabric.util.toFixed,
+ angle = this.getAngle(),
+ center = this.getCenterPoint(),
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var translatePart = "translate(" +
+ translatePart = 'translate(' +
toFixed(center.x, NUM_FRACTION_DIGITS) +
- " " +
+ ' ' +
toFixed(center.y, NUM_FRACTION_DIGITS) +
- ")";
+ ')',
- var anglePart = angle !== 0
- ? (" rotate(" + toFixed(angle, NUM_FRACTION_DIGITS) + ")")
- : '';
+ anglePart = angle !== 0
+ ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
+ : '',
- var scalePart = (this.scaleX === 1 && this.scaleY === 1)
- ? '' :
- (" scale(" +
- toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
- " " +
- toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
- ")");
+ scalePart = (this.scaleX === 1 && this.scaleY === 1)
+ ? '' :
+ (' scale(' +
+ toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
+ ' ' +
+ toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
+ ')'),
- var flipXPart = this.flipX ? "matrix(-1 0 0 1 0 0) " : "";
- var flipYPart = this.flipY ? "matrix(1 0 0 -1 0 0)" : "";
+ flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
- return [ translatePart, anglePart, scalePart, flipXPart, flipYPart ].join('');
+ flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
+
+ return [
+ translatePart, anglePart, scalePart, flipXPart, flipYPart
+ ].join('');
},
/**
@@ -15669,8 +16039,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(){
- var getPointer = fabric.util.getPointer,
- degreesToRadians = fabric.util.degreesToRadians,
+ var degreesToRadians = fabric.util.degreesToRadians,
isVML = typeof G_vmlCanvasManager !== 'undefined';
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
@@ -15684,15 +16053,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* Determines which corner has been clicked
* @private
- * @param {Event} e Event object
- * @param {Object} offset Canvas offset
+ * @param {Object} pointer The pointer indicating the mouse position
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
*/
- _findTargetCorner: function(e, offset) {
+ _findTargetCorner: function(pointer) {
if (!this.hasControls || !this.active) return false;
- var pointer = this.canvas.getPointer(e, true),
- ex = pointer.x,
+ var ex = pointer.x,
ey = pointer.y,
xPoints,
lines;
@@ -15707,7 +16074,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
continue;
}
- if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
+ if (this.get('lockUniScaling') &&
+ (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
continue;
}
@@ -15727,7 +16095,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
// canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
// canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
- xPoints = this._findCrossPoints({x: ex, y: ey}, lines);
+ xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
if (xPoints !== 0 && xPoints % 2 === 1) {
this.__corner = i;
return i;
@@ -15947,7 +16315,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.lineWidth = 1 / this.borderScaleFactor;
- var vpt = this.canvas.viewportTransform,
+ var vpt = this.getViewportTransform(),
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, true),
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), vpt, true),
w = wh.x,
@@ -15999,7 +16367,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var size = this.cornerSize,
size2 = size / 2,
strokeWidth2 = ~~(this.strokeWidth / 2), // half strokeWidth rounded down
- wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.canvas.viewportTransform, true),
+ wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.getViewportTransform(), true),
width = wh.x,
height = wh.y,
left = -(width / 2),
@@ -16029,7 +16397,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
top - scaleOffset - strokeWidth2 - padding);
// bottom-left
- this._drawControl('tr', ctx, methodName,
+ this._drawControl('bl', ctx, methodName,
left - scaleOffset - strokeWidth2 - padding,
top + height + scaleOffsetSize + strokeWidth2 + padding);
@@ -16051,7 +16419,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
top + height + scaleOffsetSize + strokeWidth2 + padding);
// middle-right
- this._drawControl('mb', ctx, methodName,
+ this._drawControl('mr', ctx, methodName,
left + width + scaleOffsetSize + strokeWidth2 + padding,
top + height/2 - scaleOffset);
@@ -16140,15 +16508,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_getControlsVisibility: function() {
if (!this._controlsVisibility) {
this._controlsVisibility = {
- tl: true,
- tr: true,
- br: true,
- bl: true,
- ml: true,
- mt: true,
- mr: true,
- mb: true,
- mtr: true
+ tl: true,
+ tr: true,
+ br: true,
+ bl: true,
+ ml: true,
+ mt: true,
+ mr: true,
+ mb: true,
+ mtr: true
};
}
return this._controlsVisibility;
@@ -16301,7 +16669,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
for (prop in arguments[0]) {
propsToAnimate.push(prop);
}
- for (var i = 0, len = propsToAnimate.length; i'
- );
+ '"/>');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@@ -17400,7 +17888,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
toFixed = fabric.util.toFixed;
@@ -17425,6 +17913,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'polyline',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points (where each point is an object with x and y)
@@ -17522,7 +18017,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || p1;
+ p2 = this.points[i + 1] || p1;
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
},
@@ -17585,7 +18080,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -17613,6 +18108,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'polygon',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points
@@ -17723,7 +18225,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || this.points[0];
+ p2 = this.points[i + 1] || this.points[0];
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
ctx.closePath();
@@ -17786,26 +18288,25 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- var commandLengths = {
- m: 2,
- l: 2,
- h: 1,
- v: 1,
- c: 6,
- s: 4,
- q: 4,
- t: 2,
- a: 7
- };
-
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
min = fabric.util.array.min,
max = fabric.util.array.max,
extend = fabric.util.object.extend,
_toString = Object.prototype.toString,
- drawArc = fabric.util.drawArc;
+ drawArc = fabric.util.drawArc,
+ commandLengths = {
+ m: 2,
+ l: 2,
+ h: 1,
+ v: 1,
+ c: 6,
+ s: 4,
+ q: 4,
+ t: 2,
+ a: 7
+ };
if (fabric.Path) {
fabric.warn('fabric.Path is already defined');
@@ -17848,6 +18349,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'path',
+ /**
+ * Array of path points
+ * @type Array
+ * @default
+ */
+ path: null,
+
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
@@ -17911,7 +18419,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this.left = this.width / 2;
}
}
- this.pathOffset = this.pathOffset || this._calculatePathOffset(origLeft, origTop); //Save top-left coords as offset
+ this.pathOffset = this.pathOffset ||
+ // Save top-left coords as offset
+ this._calculatePathOffset(origLeft, origTop);
},
/**
@@ -18067,8 +18577,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
tempX = current[3];
tempY = current[4];
// calculate reflection of previous control points
- controlX = 2*x - controlX;
- controlY = 2*y - controlY;
+ controlX = 2 * x - controlX;
+ controlY = 2 * y - controlY;
ctx.bezierCurveTo(
controlX + l,
controlY + t,
@@ -18129,7 +18639,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
tempX = x + current[1];
tempY = y + current[2];
-
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
@@ -18230,7 +18739,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.save();
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -18271,7 +18780,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
toObject: function(propertiesToInclude) {
var o = extend(this.callSuper('toObject', propertiesToInclude), {
- path: this.path,
+ path: this.path.map(function(item) { return item.slice() }),
pathOffset: this.pathOffset
});
if (this.sourcePath) {
@@ -18343,7 +18852,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
coords = [ ],
currentPath,
parsed,
- re = /(-?\.\d+)|(-?\d+(\.\d+)?)/g,
+ re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
match,
coordsStr;
@@ -18399,14 +18908,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
maxX = max(aX),
maxY = max(aY),
deltaX = maxX - minX,
- deltaY = maxY - minY;
+ deltaY = maxY - minY,
- var o = {
- left: this.left + (minX + deltaX / 2),
- top: this.top + (minY + deltaY / 2),
- width: deltaX,
- height: deltaY
- };
+ o = {
+ left: this.left + (minX + deltaX / 2),
+ top: this.top + (minY + deltaY / 2),
+ width: deltaX,
+ height: deltaY
+ };
return o;
},
@@ -18427,13 +18936,18 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
isLowerCase = true;
}
- var xy = this._getXY(item, isLowerCase, previous);
+ var xy = this._getXY(item, isLowerCase, previous),
+ val;
- var val = parseInt(xy.x, 10);
- if (!isNaN(val)) aX.push(val);
+ val = parseInt(xy.x, 10);
+ if (!isNaN(val)) {
+ aX.push(val);
+ }
val = parseInt(xy.y, 10);
- if (!isNaN(val)) aY.push(val);
+ if (!isNaN(val)) {
+ aY.push(val);
+ }
},
_getXY: function(item, isLowerCase, previous) {
@@ -18445,13 +18959,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
? previous.x + getX(item)
: item[0] === 'V'
? previous.x
- : getX(item);
+ : getX(item),
- var y = isLowerCase
- ? previous.y + getY(item)
- : item[0] === 'H'
- ? previous.y
- : getY(item);
+ y = isLowerCase
+ ? previous.y + getY(item)
+ : item[0] === 'H'
+ ? previous.y
+ : getY(item);
return { x: x, y: y };
}
@@ -18467,9 +18981,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
fabric.Path.fromObject = function(object, callback) {
if (typeof object.path === 'string') {
fabric.loadSVGFromURL(object.path, function (elements) {
- var path = elements[0];
+ var path = elements[0],
+ pathUrl = object.path;
- var pathUrl = object.path;
delete object.path;
fabric.util.object.extend(path, object);
@@ -18520,7 +19034,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -18572,6 +19086,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this.setOptions(options);
+ if (options.widthAttr) {
+ this.scaleX = options.widthAttr / options.width;
+ }
+ if (options.heightAttr) {
+ this.scaleY = options.heightAttr / options.height;
+ }
+
+ this.setCoords();
+
if (options.sourcePath) {
this.setSourcePath(options.sourcePath);
}
@@ -18589,7 +19112,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -18663,13 +19186,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- var objects = this.getObjects();
- var markup = [
- ''
- ];
+ var objects = this.getObjects(),
+ markup = [
+ ''
+ ];
for (var i = 0, len = objects.length; i < len; i++) {
markup.push(objects[i].toSVG(reviver));
@@ -18694,9 +19217,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Boolean} true if all paths are of the same color (`fill`)
*/
isSameColor: function() {
- var firstPathFill = this.getObjects()[0].get('fill');
+ var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
return this.getObjects().every(function(path) {
- return path.get('fill') === firstPathFill;
+ return (path.get('fill') || '').toLowerCase() === firstPathFill;
});
},
@@ -18760,7 +19283,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global){
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -18992,7 +19515,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @private
*/
_renderObject: function(object, ctx) {
- var v = this.canvas.viewportTransform,
+ var v = this.getViewportTransform(),
sxy = fabric.util.transformPoint(
new fabric.Point(this.scaleX, this.scaleY),
v,
@@ -19169,11 +19692,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_setOpacityIfSame: function() {
var objects = this.getObjects(),
- firstValue = objects[0] ? objects[0].get('opacity') : 1;
-
- var isSameOpacity = objects.every(function(o) {
- return o.get('opacity') === firstValue;
- });
+ firstValue = objects[0] ? objects[0].get('opacity') : 1,
+ isSameOpacity = objects.every(function(o) {
+ return o.get('opacity') === firstValue;
+ });
if (isSameOpacity) {
this.opacity = firstValue;
@@ -19183,7 +19705,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* @private
*/
- _calcBounds: function() {
+ _calcBounds: function(onlyWidthHeight) {
var aX = [],
aY = [],
o;
@@ -19197,26 +19719,29 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
}
}
- this.set(this._getBounds(aX, aY));
+ this.set(this._getBounds(aX, aY, onlyWidthHeight));
},
/**
* @private
*/
- _getBounds: function(aX, aY) {
+ _getBounds: function(aX, aY, onlyWidthHeight) {
var ivt;
if (this.canvas) {
- ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
+ ivt = fabric.util.invertTransform(this.getViewportTransform());
}
var minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
- maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt);
+ maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
+ obj = {
+ width: (maxXY.x - minXY.x) || 0,
+ height: (maxXY.y - minXY.y) || 0
+ };
- return {
- width: (maxXY.x - minXY.x) || 0,
- height: (maxXY.y - minXY.y) || 0,
- left: (minXY.x + maxXY.x) / 2 || 0,
- top: (minXY.y + maxXY.y) / 2 || 0,
- };
+ if (!onlyWidthHeight) {
+ obj.left = (minXY.x + maxXY.x) / 2 || 0;
+ obj.top = (minXY.y + maxXY.y) / 2 || 0;
+ }
+ return obj;
},
/* _TO_SVG_START_ */
@@ -19299,7 +19824,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var extend = fabric.util.object.extend;
@@ -19421,17 +19946,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (!this.visible) return;
ctx.save();
- var m = this.transformMatrix;
- var v;
- v = this.canvas.viewportTransform;
-
- var isInPathGroup = this.group && this.group.type === 'path-group';
+ var m = this.transformMatrix,
+ v = this.getViewportTransform(),
+ isInPathGroup = this.group && this.group.type === 'path-group';
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
// this._resetWidthHeight();
if (isInPathGroup) {
- ctx.translate(-this.group.width/2 + this.width/2, -this.group.height/2 + this.height/2);
+ ctx.translate(-this.group.width/2, -this.group.height/2);
}
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -19439,6 +19962,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (!noTransform) {
this.transform(ctx);
}
+ if (isInPathGroup) {
+ ctx.translate(this.width/2, this.height/2);
+ }
ctx.save();
this._setShadow(ctx);
@@ -19482,10 +20008,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this._setStrokeStyles(ctx);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
ctx.closePath();
ctx.restore();
},
@@ -19552,7 +20078,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} Source of an image
*/
getSrc: function() {
- return this.getElement().src || this.getElement()._src;
+ if (this.getElement()) {
+ return this.getElement().src || this.getElement()._src;
+ }
},
/**
@@ -19581,6 +20109,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
applyFilters: function(callback) {
+ if (!this._originalElement) {
+ return;
+ }
+
if (this.filters.length === 0) {
this._element = this._originalElement;
callback && callback();
@@ -19630,13 +20162,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
- ctx.drawImage(
- this._element,
- -this.width / 2,
- -this.height / 2,
- this.width,
- this.height
- );
+ this._element && ctx.drawImage(
+ this._element,
+ -this.width / 2,
+ -this.height / 2,
+ this.width,
+ this.height
+ );
},
/**
@@ -19668,7 +20200,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
options || (options = { });
this.setOptions(options);
this._setWidthHeight(options);
- this._element.crossOrigin = this.crossOrigin;
+ if (this._element && this.crossOrigin) {
+ this._element.crossOrigin = this.crossOrigin;
+ }
},
/**
@@ -19694,11 +20228,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_setWidthHeight: function(options) {
this.width = 'width' in options
? options.width
- : (this.getElement().width || 0);
+ : (this.getElement()
+ ? this.getElement().width || 0
+ : 0);
this.height = 'height' in options
? options.height
- : (this.getElement().height || 0);
+ : (this.getElement()
+ ? this.getElement().height || 0
+ : 0);
},
/**
@@ -19716,7 +20254,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @type String
* @default
*/
- fabric.Image.CSS_CANVAS = "canvas-img";
+ fabric.Image.CSS_CANVAS = 'canvas-img';
/**
* Alias for getSrc
@@ -19805,9 +20343,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_getAngleValueForStraighten: function() {
var angle = this.getAngle() % 360;
if (angle > 0) {
- return Math.round((angle-1)/90) * 90;
+ return Math.round((angle - 1) / 90) * 90;
}
- return Math.round(angle/90) * 90;
+ return Math.round(angle / 90) * 90;
},
/**
@@ -19894,7 +20432,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
fabric.Image.filters = fabric.Image.filters || { };
-
/**
* Root filter class from which all filter classes inherit from
* @class fabric.Image.filters.BaseFilter
@@ -19930,7 +20467,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -19962,11 +20499,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* Constructor
* @memberOf fabric.Image.filters.Brightness.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.brightness=100] Value to brighten the image up (0..255)
+ * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
*/
initialize: function(options) {
options = options || { };
- this.brightness = options.brightness || 100;
+ this.brightness = options.brightness || 0;
},
/**
@@ -20014,7 +20551,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20080,9 +20617,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
options = options || { };
this.opaque = options.opaque;
- this.matrix = options.matrix || [ 0, 0, 0,
+ this.matrix = options.matrix || [
+ 0, 0, 0,
0, 1, 0,
- 0, 0, 0 ];
+ 0, 0, 0
+ ];
var canvasEl = fabric.util.createCanvasElement();
this.tmpCtx = canvasEl.getContext('2d');
@@ -20109,49 +20648,49 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
halfSide = Math.floor(side/2),
src = pixels.data,
sw = pixels.width,
- sh = pixels.height;
+ sh = pixels.height,
- // pad output by the convolution matrix
- var w = sw;
- var h = sh;
- var output = this._createImageData(w, h);
+ // pad output by the convolution matrix
+ w = sw,
+ h = sh,
+ output = this._createImageData(w, h),
- var dst = output.data;
+ dst = output.data,
- // go through the destination image pixels
- var alphaFac = this.opaque ? 1 : 0;
+ // go through the destination image pixels
+ alphaFac = this.opaque ? 1 : 0;
- for (var y=0; y sh || scx < 0 || scx > sw) continue;
- var srcOff = (scy*sw+scx)*4;
- var wt = weights[cy*side+cx];
+ var srcOff = (scy * sw + scx) * 4,
+ wt = weights[cy * side + cx];
r += src[srcOff] * wt;
- g += src[srcOff+1] * wt;
- b += src[srcOff+2] * wt;
- a += src[srcOff+3] * wt;
+ g += src[srcOff + 1] * wt;
+ b += src[srcOff + 2] * wt;
+ a += src[srcOff + 3] * wt;
}
}
dst[dstOff] = r;
- dst[dstOff+1] = g;
- dst[dstOff+2] = b;
- dst[dstOff+3] = a + alphaFac*(255-a);
+ dst[dstOff + 1] = g;
+ dst[dstOff + 2] = b;
+ dst[dstOff + 3] = a + alphaFac * (255 - a);
}
}
@@ -20177,7 +20716,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
*/
fabric.Image.filters.Convolute.fromObject = function(object) {
- return new fabric.Image.filters.Convolute(object);
+ return new fabric.Image.filters.Convolute(object);
};
})(typeof exports !== 'undefined' ? exports : this);
@@ -20185,7 +20724,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20268,7 +20807,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20331,7 +20870,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20390,7 +20929,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20495,7 +21034,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20527,11 +21066,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* Constructor
* @memberOf fabric.Image.filters.Noise.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.noise=100] Noise value
+ * @param {Number} [options.noise=0] Noise value
*/
initialize: function(options) {
options = options || { };
- this.noise = options.noise || 100;
+ this.noise = options.noise || 0;
},
/**
@@ -20582,7 +21121,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20639,9 +21178,9 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
index = (i * 4) * jLen + (j * 4);
r = data[index];
- g = data[index+1];
- b = data[index+2];
- a = data[index+3];
+ g = data[index + 1];
+ b = data[index + 2];
+ a = data[index + 3];
/*
blocksize: 4
@@ -20694,7 +21233,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20752,17 +21291,17 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
for (var i = 0, len = data.length; i < len; i += 4) {
r = data[i];
- g = data[i+1];
- b = data[i+2];
+ g = data[i + 1];
+ b = data[i + 2];
if (r > limit &&
g > limit &&
b > limit &&
- abs(r-g) < distance &&
- abs(r-b) < distance &&
- abs(g-b) < distance
+ abs(r - g) < distance &&
+ abs(r - b) < distance &&
+ abs(g - b) < distance
) {
- data[i+3] = 1;
+ data[i + 3] = 1;
}
}
@@ -20796,7 +21335,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20856,7 +21395,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20919,7 +21458,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -21033,7 +21572,101 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
+
+ var fabric = global.fabric || (global.fabric = { }),
+ extend = fabric.util.object.extend;
+
+ /**
+ * Multiply filter class
+ * Adapted from http://www.laurenscorijn.com/articles/colormath-basics
+ * @class fabric.Image.filters.Multiply
+ * @memberOf fabric.Image.filters
+ * @extends fabric.Image.filters.BaseFilter
+ * @example Multiply filter with hex color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: '#F0F'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ * @example Multiply filter with rgb color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: 'rgb(53, 21, 176)'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ */
+ fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
+
+ /**
+ * Filter type
+ * @param {String} type
+ * @default
+ */
+ type: 'Multiply',
+
+ /**
+ * Constructor
+ * @memberOf fabric.Image.filters.Multiply.prototype
+ * @param {Object} [options] Options object
+ * @param {String} [options.color=#000000] Color to multiply the image pixels with
+ */
+ initialize: function(options) {
+ options = options || { };
+
+ this.color = options.color || '#000000';
+ },
+
+ /**
+ * Applies filter to canvas element
+ * @param {Object} canvasEl Canvas element to apply filter to
+ */
+ applyTo: function(canvasEl) {
+ var context = canvasEl.getContext('2d'),
+ imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
+ data = imageData.data,
+ iLen = data.length, i,
+ source;
+
+ source = new fabric.Color(this.color).getSource();
+
+ for (i = 0; i < iLen; i+=4) {
+ data[i] *= source[0]/255;
+ data[i + 1] *= source[1]/255;
+ data[i + 2] *= source[2]/255;
+
+ }
+
+ context.putImageData(imageData, 0, 0);
+ },
+
+ /**
+ * Returns object representation of an instance
+ * @return {Object} Object representation of an instance
+ */
+ toObject: function() {
+ return extend(this.callSuper('toObject'), {
+ color: this.color
+ });
+ }
+ });
+
+ /**
+ * Returns filter instance from an object representation
+ * @static
+ * @param {Object} object Object to create an instance from
+ * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
+ */
+ fabric.Image.filters.Multiply.fromObject = function(object) {
+ return new fabric.Image.filters.Multiply(object);
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
+
+
+(function(global) {
+
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -21460,8 +22093,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
this._boundaries.push({
height: this.fontSize * this.lineHeight,
@@ -21544,18 +22177,18 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
return;
}
- var lineWidth = ctx.measureText(line).width;
- var totalWidth = this.width;
+ var lineWidth = ctx.measureText(line).width,
+ totalWidth = this.width;
if (totalWidth > lineWidth) {
// stretch the line
- var words = line.split(/\s+/);
- var wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width;
- var widthDiff = totalWidth - wordsWidth;
- var numSpaces = words.length - 1;
- var spaceWidth = widthDiff / numSpaces;
+ var words = line.split(/\s+/),
+ wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
+ widthDiff = totalWidth - wordsWidth,
+ numSpaces = words.length - 1,
+ spaceWidth = widthDiff / numSpaces,
+ leftOffset = 0;
- var leftOffset = 0;
for (var i = 0, len = words.length; i < len; i++) {
this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
leftOffset += ctx.measureText(words[i]).width + spaceWidth;
@@ -21697,8 +22330,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (textLines[i] !== '') {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
@@ -21747,15 +22380,15 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (!this.textDecoration) return;
// var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
- var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2;
- var _this = this;
+ var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
+ _this = this;
/** @ignore */
function renderLinesAtOffset(offset) {
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = _this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = _this._getLineLeftOffset(lineWidth);
+ var lineWidth = _this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = _this._getLineLeftOffset(lineWidth);
ctx.fillRect(
_this._getLeftOffset() + lineLeftOffset,
@@ -21799,8 +22432,13 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (!this.visible) return;
ctx.save();
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+
+ var m = this.transformMatrix;
+ if (m && (!this.group || this.group.type === 'path-group')) {
+ ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ }
this._render(ctx);
ctx.restore();
@@ -21986,7 +22624,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(i === 0 || this.useNative ? 'y' : 'dy'), '="',
toFixed(this.useNative
? ((lineHeight * i) - this.height / 2)
- : (lineHeight * lineTopOffsetMultiplier), 2) , '" ',
+ : (lineHeight * lineTopOffsetMultiplier), 2), '" ',
// doing this on elements since setting opacity
// on containing one doesn't work in Illustrator
this._getFillAttributes(this.fill), '>',
@@ -22081,7 +22719,14 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* @see: http://www.w3.org/TR/SVG/text.html#TextElement
*/
fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
- 'x y font-family font-style font-weight font-size text-decoration'.split(' '));
+ 'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
+
+ /**
+ * Default SVG font size
+ * @static
+ * @memberOf fabric.Text
+ */
+ fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
/**
* Returns fabric.Text instance from an SVG element (not yet implemented)
@@ -22099,6 +22744,20 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
+ if ('dx' in parsedAttributes) {
+ options.left += parsedAttributes.dx;
+ }
+ if ('dy' in parsedAttributes) {
+ options.top += parsedAttributes.dy;
+ }
+ if (!('fontSize' in options)) {
+ options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ }
+
+ if (!options.originX) {
+ options.originX = 'center';
+ }
+
var text = new fabric.Text(element.textContent, options);
/*
@@ -22221,9 +22880,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @extends fabric.Text
* @mixes fabric.Observable
*
- * @fires text:changed
- * @fires editing:entered
- * @fires editing:exited
+ * @fires changed ("text:changed" when observing canvas)
+ * @fires editing:entered ("text:editing:entered" when observing canvas)
+ * @fires editing:exited ("text:editing:exited" when observing canvas)
*
* @return {fabric.IText} thisArg
* @see {@link fabric.IText#initialize} for constructor definition
@@ -22246,6 +22905,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
* Copy text: ctrl/cmd + c
* Paste text: ctrl/cmd + v
* Cut text: ctrl/cmd + x
+ * Select entire text: ctrl/cmd + a
*
*
* Supported mouse/touch combination
@@ -22430,6 +23090,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} index Index to set selection start to
*/
setSelectionStart: function(index) {
+ if (this.selectionStart !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionStart = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index);
},
@@ -22439,6 +23102,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} index Index to set selection end to
*/
setSelectionEnd: function(index) {
+ if (this.selectionEnd !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionEnd = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index);
},
@@ -22502,9 +23168,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
},
/**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- */
+ * @private
+ * @param {CanvasRenderingContext2D} ctx Context to render on
+ */
_render: function(ctx) {
this.callSuper('_render', ctx);
this.ctx = ctx;
@@ -22538,8 +23204,8 @@ fabric.util.object.extend(fabric.Text.prototype, {
if (typeof selectionStart === 'undefined') {
selectionStart = this.selectionStart;
}
- var textBeforeCursor = this.text.slice(0, selectionStart);
- var linesBeforeCursor = textBeforeCursor.split(this._reNewline);
+ var textBeforeCursor = this.text.slice(0, selectionStart),
+ linesBeforeCursor = textBeforeCursor.split(this._reNewline);
return {
lineIndex: linesBeforeCursor.length - 1,
@@ -22547,6 +23213,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
};
},
+ /**
+ * Returns complete style of char at the current cursor
+ * @param {Number} lineIndex Line index
+ * @param {Number} charIndex Char index
+ * @return {Object} Character style
+ */
+ getCurrentCharStyle: function(lineIndex, charIndex) {
+ var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
+
+ return {
+ fontSize: style && style.fontSize || this.fontSize,
+ fill: style && style.fill || this.fill,
+ textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
+ textDecoration: style && style.textDecoration || this.textDecoration,
+ fontFamily: style && style.fontFamily || this.fontFamily,
+ stroke: style && style.stroke || this.stroke,
+ strokeWidth: style && style.strokeWidth || this.strokeWidth
+ };
+ },
+
/**
* Returns fontSize of char at the current cursor
* @param {Number} lineIndex Line index
@@ -22693,13 +23379,16 @@ fabric.util.object.extend(fabric.Text.prototype, {
var cursorLocation = this.get2DCursorLocation(),
lineIndex = cursorLocation.lineIndex,
charIndex = cursorLocation.charIndex,
- charHeight = this.getCurrentCharFontSize(lineIndex, charIndex);
+ charHeight = this.getCurrentCharFontSize(lineIndex, charIndex),
+ leftOffset = (lineIndex === 0 && charIndex === 0)
+ ? this._getCachedLineOffset(lineIndex, this.text.split(this._reNewline))
+ : boundaries.leftOffset;
ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex);
- ctx.globalAlpha = this._currentCursorOpacity;
+ ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
ctx.fillRect(
- boundaries.left + boundaries.leftOffset,
+ boundaries.left + leftOffset,
boundaries.top + boundaries.topOffset,
this.cursorWidth / this.scaleX,
charHeight);
@@ -22719,39 +23408,43 @@ fabric.util.object.extend(fabric.Text.prototype, {
ctx.fillStyle = this.selectionColor;
- var cursorLocation = this.get2DCursorLocation(),
- lineIndex = cursorLocation.lineIndex,
- charIndex = cursorLocation.charIndex,
- textLines = this.text.split(this._reNewline),
- origLineIndex = lineIndex;
+ var start = this.get2DCursorLocation(this.selectionStart),
+ end = this.get2DCursorLocation(this.selectionEnd),
+ startLine = start.lineIndex,
+ endLine = end.lineIndex,
+ textLines = this.text.split(this._reNewline);
- for (var i = this.selectionStart; i < this.selectionEnd; i++) {
+ for (var i = startLine; i <= endLine; i++) {
+ var lineOffset = this._getCachedLineOffset(i, textLines) || 0,
+ lineHeight = this._getCachedLineHeight(i),
+ boxWidth = 0;
- if (chars[i] === '\n') {
- boundaries.leftOffset = 0;
- boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex);
- lineIndex++;
- charIndex = 0;
- }
- else if (i !== this.text.length) {
-
- var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex),
- lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0;
-
- if (lineIndex === origLineIndex) {
- // only offset the line if we're rendering selection of 2nd, 3rd, etc. line
- lineOffset = 0;
+ if (i === startLine) {
+ for (var j = 0, len = textLines[i].length; j < len; j++) {
+ if (j >= start.charIndex && (i !== endLine || j < end.charIndex)) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
+ if (j < start.charIndex) {
+ lineOffset += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
}
-
- ctx.fillRect(
- boundaries.left + boundaries.leftOffset + lineOffset,
- boundaries.top + boundaries.topOffset,
- charWidth,
- this._getHeightOfLine(ctx, lineIndex));
-
- boundaries.leftOffset += charWidth;
- charIndex++;
}
+ else if (i > startLine && i < endLine) {
+ boxWidth += this._getCachedLineWidth(i, textLines) || 5;
+ }
+ else if (i === endLine) {
+ for (var j2 = 0, j2len = end.charIndex; j2 < j2len; j2++) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j2], i, j2);
+ }
+ }
+
+ ctx.fillRect(
+ boundaries.left + lineOffset,
+ boundaries.top + boundaries.topOffset,
+ boxWidth,
+ lineHeight);
+
+ boundaries.topOffset += lineHeight;
}
ctx.restore();
},
@@ -22781,14 +23474,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines),
lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines),
lineLeftOffset = this._getLineLeftOffset(lineWidth),
- chars = line.split('');
+ chars = line.split(''),
+ prevStyle,
+ charsToRender = '';
left += lineLeftOffset || 0;
ctx.save();
- for (var i = 0, len = chars.length; i < len; i++) {
- this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight);
+
+ for (var i = 0, len = chars.length; i <= len; i++) {
+ prevStyle = prevStyle || this.getCurrentCharStyle(lineIndex, i);
+ var thisStyle = this.getCurrentCharStyle(lineIndex, i + 1);
+
+ if (this._hasStyleChanged(prevStyle, thisStyle) || i === len) {
+ this._renderChar(method, ctx, lineIndex, i - 1, charsToRender, left, top, lineHeight);
+ charsToRender = '';
+ prevStyle = thisStyle;
+ }
+ charsToRender += chars[i];
}
+
ctx.restore();
},
@@ -22851,6 +23556,22 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
},
+ /**
+ * @private
+ * @param {Object} prevStyle
+ * @param {Object} thisStyle
+ */
+ _hasStyleChanged: function(prevStyle, thisStyle) {
+ return (prevStyle.fill !== thisStyle.fill ||
+ prevStyle.fontSize !== thisStyle.fontSize ||
+ prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
+ prevStyle.textDecoration !== thisStyle.textDecoration ||
+ prevStyle.fontFamily !== thisStyle.fontFamily ||
+ prevStyle.stroke !== thisStyle.stroke ||
+ prevStyle.strokeWidth !== thisStyle.strokeWidth
+ );
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -22858,10 +23579,10 @@ fabric.util.object.extend(fabric.Text.prototype, {
_renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight, charHeight) {
var textDecoration = styleDeclaration
- ? (styleDeclaration.textDecoration || this.textDecoration)
- : this.textDecoration;
+ ? (styleDeclaration.textDecoration || this.textDecoration)
+ : this.textDecoration,
- var fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
+ fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
if (!textDecoration) return;
@@ -23081,15 +23802,35 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
},
+ /**
+ * @private
+ * @param {Number} lineIndex
+ * @param {Number} charIndex
+ */
+ _getStyleDeclaration: function(lineIndex, charIndex) {
+ return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
+ ? clone(this.styles[lineIndex][charIndex])
+ : { };
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
- ctx.save();
- var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
- ctx.restore();
- return width;
+ var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
+ this._applyFontStyles(styleDeclaration);
+ var cacheProp = this._getCacheProp(_char, styleDeclaration);
+
+ if (this._charWidthsCache[cacheProp] && this.caching) {
+ return this._charWidthsCache[cacheProp];
+ }
+ else if (ctx) {
+ ctx.save();
+ var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
+ ctx.restore();
+ return width;
+ }
},
/**
@@ -23175,10 +23916,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
textLines = textLines || this.text.split(this._reNewline);
- var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0);
-
- var line = textLines[lineIndex];
- var chars = line.split('');
+ var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0),
+ line = textLines[lineIndex],
+ chars = line.split('');
for (var i = 1, len = chars.length; i < len; i++) {
var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i);
@@ -23211,6 +23951,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
return topOffset - (this.fontSize / this._fontSizeFraction);
},
+ /**
+ * @private
+ * This method is overwritten to account for different top offset
+ */
+ _renderTextBoxBackground: function(ctx) {
+ if (!this.backgroundColor) return;
+
+ ctx.save();
+ ctx.fillStyle = this.backgroundColor;
+
+ ctx.fillRect(
+ this._getLeftOffset(),
+ this._getTopOffset() + (this.fontSize / this._fontSizeFraction),
+ this.width,
+ this.height
+ );
+
+ ctx.restore();
+ },
+
/**
* Returns object representation of an instance
* @methd toObject
@@ -23235,6 +23995,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
return new fabric.IText(object.text, clone(object));
};
+ /**
+ * Contains all fabric.IText objects that have been created
+ * @static
+ * @memberof fabric.IText
+ * @type Array
+ */
fabric.IText.instances = [ ];
})();
@@ -23250,10 +24016,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* Initializes all the interactive behavior of IText
*/
initBehavior: function() {
- this.initKeyHandlers();
+ this.initAddedHandler();
this.initCursorSelectionHandlers();
this.initDoubleClickSimulation();
- this.initHiddenTextarea();
},
/**
@@ -23266,10 +24031,17 @@ fabric.util.object.extend(fabric.Text.prototype, {
setTimeout(function() {
_this.selected = true;
}, 100);
+ });
+ },
- if (!this._hasCanvasHandlers) {
+ /**
+ * Initializes "added" event handler
+ */
+ initAddedHandler: function() {
+ this.on('added', function() {
+ if (this.canvas && !this.canvas._hasITextHandlers) {
+ this.canvas._hasITextHandlers = true;
this._initCanvasHandlers();
- this._hasCanvasHandlers = true;
}
});
},
@@ -23278,25 +24050,18 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @private
*/
_initCanvasHandlers: function() {
- var _this = this;
-
- this.canvas.on('selection:cleared', function(options) {
-
- // do not exit editing if event fired
- // when clicking on an object again (in editing mode)
- if (options.e && _this.canvas.containsPoint(options.e, _this)) return;
-
- _this.exitEditing();
+ this.canvas.on('selection:cleared', function() {
+ fabric.IText.prototype.exitEditingOnOthers.call();
});
this.canvas.on('mouse:up', function() {
- this.getObjects('i-text').forEach(function(obj) {
+ fabric.IText.instances.forEach(function(obj) {
obj.__isMousedown = false;
});
});
- this.canvas.on('object:selected', function() {
- fabric.IText.prototype.exitEditingOnOthers.call(this);
+ this.canvas.on('object:selected', function(options) {
+ fabric.IText.prototype.exitEditingOnOthers.call(options.target);
});
},
@@ -23356,15 +24121,23 @@ fabric.util.object.extend(fabric.Text.prototype, {
/**
* Initializes delayed cursor
*/
- initDelayedCursor: function() {
- var _this = this;
+ initDelayedCursor: function(restart) {
+ var _this = this,
+ delay = restart ? 0 : this.cursorDelay;
+
+ if (restart) {
+ this._abortCursorAnimation = true;
+ clearTimeout(this._cursorTimeout1);
+ this._currentCursorOpacity = 1;
+ this.canvas && this.canvas.renderAll();
+ }
if (this._cursorTimeout2) {
clearTimeout(this._cursorTimeout2);
}
this._cursorTimeout2 = setTimeout(function() {
_this._abortCursorAnimation = false;
_this._tick();
- }, this.cursorDelay);
+ }, delay);
},
/**
@@ -23391,6 +24164,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
selectAll: function() {
this.selectionStart = 0;
this.selectionEnd = this.text.length;
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
},
/**
@@ -23482,8 +24256,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @return {Number} Number of newlines in selected text
*/
getNumNewLinesInSelectedText: function() {
- var selectedText = this.getSelectedText();
- var numNewLines = 0;
+ var selectedText = this.getSelectedText(),
+ numNewLines = 0;
+
for (var i = 0, chars = selectedText.split(''), len = chars.length; i < len; i++) {
if (chars[i] === '\n') {
numNewLines++;
@@ -23498,9 +24273,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} direction: 1 or -1
*/
searchWordBoundary: function(selectionStart, direction) {
- var index = selectionStart;
- var _char = this.text.charAt(index);
- var reNonWord = /[ \n\.,;!\?\-]/;
+ var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
+ _char = this.text.charAt(index),
+ reNonWord = /[ \n\.,;!\?\-]/;
while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
index += direction;
@@ -23517,11 +24292,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} selectionStart Index of a character
*/
selectWord: function(selectionStart) {
- var newSelectionStart = this.searchWordBoundary(selectionStart, -1); /* search backwards */
- var newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
+ var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
+ newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -23529,11 +24305,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} selectionStart Index of a character
*/
selectLine: function(selectionStart) {
- var newSelectionStart = this.findLineBoundaryLeft(selectionStart);
- var newSelectionEnd = this.findLineBoundaryRight(selectionStart);
+ var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
+ newSelectionEnd = this.findLineBoundaryRight(selectionStart);
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -23548,6 +24325,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.isEditing = true;
+ this.initHiddenTextarea();
this._updateTextarea();
this._saveEditingProps();
this._setEditingProps();
@@ -23556,14 +24334,17 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.canvas && this.canvas.renderAll();
this.fire('editing:entered');
+ this.canvas && this.canvas.fire('text:editing:entered', { target: this });
return this;
},
exitEditingOnOthers: function() {
fabric.IText.instances.forEach(function(obj) {
- if (obj === this) return;
- obj.exitEditing();
+ obj.selected = false;
+ if (obj.isEditing) {
+ obj.exitEditing();
+ }
}, this);
},
@@ -23591,7 +24372,6 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.hiddenTextarea.value = this.text;
this.hiddenTextarea.selectionStart = this.selectionStart;
- this.hiddenTextarea.focus();
},
/**
@@ -23639,13 +24419,15 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.selectable = true;
this.selectionEnd = this.selectionStart;
- this.hiddenTextarea && this.hiddenTextarea.blur();
+ this.hiddenTextarea && this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
+ this.hiddenTextarea = null;
this.abortCursorAnimation();
this._restoreEditingProps();
this._currentCursorOpacity = 0;
this.fire('editing:exited');
+ this.canvas && this.canvas.fire('text:editing:exited', { target: this });
return this;
},
@@ -23672,8 +24454,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
var prevIndex = this.get2DCursorLocation(i).charIndex;
i--;
- var index = this.get2DCursorLocation(i).charIndex;
- var isNewline = index > prevIndex;
+
+ var index = this.get2DCursorLocation(i).charIndex,
+ isNewline = index > prevIndex;
if (isNewline) {
this.removeStyleObject(isNewline, i + 1);
@@ -23702,10 +24485,10 @@ fabric.util.object.extend(fabric.Text.prototype, {
if (this.selectionStart === this.selectionEnd) {
this.insertStyleObjects(_chars, isEndOfLine, this.copiedStyles);
}
- else if (this.selectionEnd - this.selectionStart > 1) {
+ // else if (this.selectionEnd - this.selectionStart > 1) {
// TODO: replace styles properly
- console.log('replacing MORE than 1 char');
- }
+ // console.log('replacing MORE than 1 char');
+ // }
this.selectionStart += _chars.length;
this.selectionEnd = this.selectionStart;
@@ -23717,7 +24500,8 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -23864,7 +24648,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
var textLines = this.text.split(this._reNewline),
textOnPreviousLine = textLines[lineIndex - 1],
- newCharIndexOnPrevLine = textOnPreviousLine.length;
+ newCharIndexOnPrevLine = textOnPreviousLine
+ ? textOnPreviousLine.length
+ : 0;
if (!this.styles[lineIndex - 1]) {
this.styles[lineIndex - 1] = { };
@@ -23921,7 +24707,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// for triple click
this.__lastLastClickTime = +new Date();
- this.lastPointer = { };
+ this.__lastPointer = { };
this.on('mousedown', this.onMouseDown.bind(this));
},
@@ -23943,12 +24729,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.__lastLastClickTime = this.__lastClickTime;
this.__lastClickTime = this.__newClickTime;
this.__lastPointer = newPointer;
+ this.__lastIsEditing = this.isEditing;
},
isDoubleClick: function(newPointer) {
return this.__newClickTime - this.__lastClickTime < 500 &&
this.__lastPointer.x === newPointer.x &&
- this.__lastPointer.y === newPointer.y;
+ this.__lastPointer.y === newPointer.y && this.__lastIsEditing;
},
isTripleClick: function(newPointer) {
@@ -24005,9 +24792,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
}
- if (this.isEditing) {
+ if (this.selected) {
this.setCursorByClick(options.e);
+ }
+
+ if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
+ this.initDelayedCursor(true);
}
});
},
@@ -24048,12 +24839,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
initMouseupHandler: function() {
this.on('mouseup', function(options) {
this.__isMousedown = false;
-
if (this._isObjectMoved(options.e)) return;
if (this.selected) {
this.enterEditing();
+ this.initDelayedCursor(true);
}
+ this.selected = true;
});
},
@@ -24115,10 +24907,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
height += this._getHeightOfLine(this.ctx, i) * this.scaleY;
- var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfLine);
+ var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfLine);
- width = lineLeftOffset;
+ width = lineLeftOffset * this.scaleX;
if (this.flipX) {
// when oject is horizontally flipped we reverse chars
@@ -24141,6 +24933,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return this._getNewSelectionStartFromOffset(
mouseOffset, prevWidth, width, charIndex + i, jlen);
}
+
+ if (mouseOffset.y < height) {
+ return this._getNewSelectionStartFromOffset(
+ mouseOffset, prevWidth, width, charIndex + i, jlen, j);
+ }
}
// clicked somewhere after all chars, so set at the end
@@ -24152,7 +24949,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* @private
*/
- _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
+ _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen, j) {
var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
distanceBtwNextCharAndCursor = width - mouseOffset.x,
@@ -24168,6 +24965,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
newSelectionStart = this.text.length;
}
+ if (j === jlen) {
+ newSelectionStart--;
+ }
+
return newSelectionStart;
}
});
@@ -24175,14 +24976,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
- /**
- * Initializes key handlers
- */
- initKeyHandlers: function() {
- fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this));
- fabric.util.addListener(fabric.document, 'keypress', this.onKeyPress.bind(this));
- },
-
/**
* Initializes hidden textarea (needed to bring up keyboard in iOS)
*/
@@ -24193,6 +24986,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.hiddenTextarea.style.cssText = 'position: absolute; top: 0; left: -9999px';
fabric.document.body.appendChild(this.hiddenTextarea);
+
+ fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
+ fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
+
+ if (!this._clickHandlerInitialized && this.canvas) {
+ fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
+ this._clickHandlerInitialized = true;
+ }
},
/**
@@ -24218,6 +25019,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
88: 'cut'
},
+ onClick: function() {
+ // No need to trigger click event here, focus is enough to have the keyboard appear on Android
+ this.hiddenTextarea && this.hiddenTextarea.focus();
+ },
+
/**
* Handles keyup event
* @param {Event} e Event object
@@ -24324,8 +25130,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -24343,17 +25149,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex + 1;
- var widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfNextLine);
- var widthOfCharsOnNextLine = lineLeftOffset;
- var indexOnNextLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex + 1,
+ widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
+ widthOfCharsOnNextLine = lineLeftOffset,
+ indexOnNextLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
- var _char = textOnNextLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnNextLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnNextLine += widthOfChar;
@@ -24361,10 +25167,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnNextLine - widthOfChar;
- var rightEdge = widthOfCharsOnNextLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnNextLine - widthOfChar,
+ rightEdge = widthOfCharsOnNextLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
@@ -24452,13 +25258,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
textOnPreviousLine = (textBeforeCursor.match(/\n?(.*)\n.*$/) || {})[1] || '',
textLines = this.text.split(this._reNewline),
_char,
- lineLeftOffset;
-
- var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
- lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
-
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
+ widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -24476,17 +25279,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex - 1;
- var widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine);
- var widthOfCharsOnPreviousLine = lineLeftOffset;
- var indexOnPrevLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex - 1,
+ widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
+ widthOfCharsOnPreviousLine = lineLeftOffset,
+ indexOnPrevLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
- var _char = textOnPreviousLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnPreviousLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnPreviousLine += widthOfChar;
@@ -24494,10 +25297,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnPreviousLine - widthOfChar;
- var rightEdge = widthOfCharsOnPreviousLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
+ rightEdge = widthOfCharsOnPreviousLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
@@ -24747,7 +25550,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -24771,7 +25575,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.selectionStart = leftWordBoundary;
}
else {
- var isBeginningOfLine = this.text.slice(this.selectionStart-1, this.selectionStart) === '\n';
+ var isBeginningOfLine = this.text.slice(this.selectionStart - 1, this.selectionStart) === '\n';
this.removeStyleObject(isBeginningOfLine);
this.selectionStart--;
@@ -24905,7 +25709,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return;
}
- var DOMParser = new require('xmldom').DOMParser,
+ var DOMParser = require('xmldom').DOMParser,
URL = require('url'),
HTTP = require('http'),
HTTPS = require('https'),
@@ -24923,27 +25727,26 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
// assign request handler based on protocol
- var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP;
-
- var req = reqHandler.request({
- hostname: oURL.hostname,
- port: oURL.port,
- path: oURL.path,
- method: 'GET'
- }, function(response){
- var body = "";
- if (encoding) {
- response.setEncoding(encoding);
- }
- response.on('end', function () {
- callback(body);
- });
- response.on('data', function (chunk) {
- if (response.statusCode === 200) {
- body += chunk;
- }
- });
- });
+ var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP,
+ req = reqHandler.request({
+ hostname: oURL.hostname,
+ port: oURL.port,
+ path: oURL.path,
+ method: 'GET'
+ }, function(response) {
+ var body = '';
+ if (encoding) {
+ response.setEncoding(encoding);
+ }
+ response.on('end', function () {
+ callback(body);
+ });
+ response.on('data', function (chunk) {
+ if (response.statusCode === 200) {
+ body += chunk;
+ }
+ });
+ });
req.on('error', function(err) {
if (err.errno === process.ECONNREFUSED) {
@@ -24958,32 +25761,33 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
/** @private */
- function request_fs(path, callback){
+ function requestFs(path, callback){
var fs = require('fs');
fs.readFile(path, function (err, data) {
if (err) {
fabric.log(err);
throw err;
- } else {
+ }
+ else {
callback(data);
}
});
}
fabric.util.loadImage = function(url, callback, context) {
- var createImageAndCallBack = function(data){
+ function createImageAndCallBack(data) {
img.src = new Buffer(data, 'binary');
// preserving original url, which seems to be lost in node-canvas
img._src = url;
callback && callback.call(context, img);
- };
+ }
var img = new Image();
if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
img.src = img._src = url;
callback && callback.call(context, img);
}
else if (url && url.indexOf('http') !== 0) {
- request_fs(url, createImageAndCallBack);
+ requestFs(url, createImageAndCallBack);
}
else if (url) {
request(url, 'binary', createImageAndCallBack);
@@ -24996,8 +25800,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.loadSVGFromURL = function(url, callback, reviver) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
if (url.indexOf('http') !== 0) {
- request_fs(url, function(body) {
- fabric.loadSVGFromString(body, callback, reviver);
+ requestFs(url, function(body) {
+ fabric.loadSVGFromString(body.toString(), callback, reviver);
});
}
else {
@@ -25037,12 +25841,15 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* Only available when running fabric on node.js
* @param width Canvas width
* @param height Canvas height
+ * @param {Object} options to pass to FabricCanvas.
+ * @param {Object} options to pass to NodeCanvas.
* @return {Object} wrapped canvas instance
*/
- fabric.createCanvasForNode = function(width, height) {
+ fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
+ nodeCanvasOptions = nodeCanvasOptions || options;
var canvasEl = fabric.document.createElement('canvas'),
- nodeCanvas = new Canvas(width || 600, height || 600);
+ nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
// jsdom doesn't create style on canvas element, so here be temp. workaround
canvasEl.style = { };
@@ -25050,8 +25857,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
canvasEl.width = nodeCanvas.width;
canvasEl.height = nodeCanvas.height;
- var FabricCanvas = fabric.Canvas || fabric.StaticCanvas;
- var fabricCanvas = new FabricCanvas(canvasEl);
+ var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
+ fabricCanvas = new FabricCanvas(canvasEl, options);
+
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
fabricCanvas.nodeCanvas = nodeCanvas;
fabricCanvas.Font = Canvas.Font;
@@ -25090,16 +25898,3 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
})();
-
-/* Footer for requirejs AMD support */
-
-window.fabric = fabric;
-
-// make sure exports.fabric is always defined when used as 'global' later scopes
-var exports = exports || {};
-exports.fabric = fabric;
-
-if (typeof define === "function" && define.amd) {
- define([], function() { return fabric });
-}
-
diff --git a/dist/fabric.min.js b/dist/fabric.min.js
new file mode 100644
index 00000000..2d03ca19
--- /dev/null
+++ b/dist/fabric.min.js
@@ -0,0 +1,7 @@
+/* build: `node build.js modules=ALL exclude=gestures,cufon,json minifier=uglifyjs` *//*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */var fabric=fabric||{version:"1.4.6"};typeof exports!="undefined"&&(exports.fabric=fabric),typeof document!="undefined"&&typeof window!="undefined"?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom(""),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode=typeof Buffer!="undefined"&&typeof window=="undefined",fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width"],function(){function e(e,t){if(!this.__eventListeners[e])return;t?fabric.util.removeFromArray(this.__eventListeners[e],t):this.__eventListeners[e].length=0}function t(e,t){this.__eventListeners||(this.__eventListeners={});if(arguments.length===1)for(var n in e)this.on(n,e[n]);else this.__eventListeners[e]||(this.__eventListeners[e]=[]),this.__eventListeners[e].push(t);return this}function n(t,n){if(!this.__eventListeners)return;if(arguments.length===0)this.__eventListeners={};else if(arguments.length===1&&typeof arguments[0]=="object")for(var r in t)e.call(this,r,t[r]);else e.call(this,t,n);return this}function r(e,t){if(!this.__eventListeners)return;var n=this.__eventListeners[e];if(!n)return;for(var r=0,i=n.length;r-1},complexity:function(){return this.getObjects().reduce(function(e,t){return e+=t.complexity?t.complexity():0,e},0)}},function(e){var t=Math.sqrt,n=Math.atan2,r=Math.PI/180;fabric.util={removeFromArray:function(e,t){var n=e.indexOf(t);return n!==-1&&e.splice(n,1),e},getRandomInt:function(e,t){return Math.floor(Math.random()*(t-e+1))+e},degreesToRadians:function(e){return e*r},radiansToDegrees:function(e){return e/r},rotatePoint:function(e,t,n){var r=Math.sin(n),i=Math.cos(n);e.subtractEquals(t);var s=e.x*i-e.y*r,o=e.x*r+e.y*i;return(new fabric.Point(s,o)).addEquals(t)},toFixed:function(e,t){return parseFloat(Number(e).toFixed(t))},falseFunction:function(){return!1},getKlass:function(e,t){return e=fabric.util.string.camelize(e.charAt(0).toUpperCase()+e.slice(1)),fabric.util.resolveNamespace(t)[e]},resolveNamespace:function(t){if(!t)return fabric;var n=t.split("."),r=n.length,i=e||fabric.window;for(var s=0;s1?r=new fabric.PathGroup(e,t):r=e[0],typeof n!="undefined"&&r.setSourcePath(n),r},populateWithProperties:function(e,t,n){if(n&&Object.prototype.toString.call(n)==="[object Array]")for(var r=0,i=n.length;rr)r+=u[p++%h],r>l&&(r=l),e[d?"lineTo":"moveTo"](r,0),d=!d;e.restore()},createCanvasElement:function(e){return e||(e=fabric.document.createElement("canvas")),!e.getContext&&typeof G_vmlCanvasManager!="undefined"&&G_vmlCanvasManager.initElement(e),e},createImage:function(){return fabric.isLikelyNode?new(require("canvas").Image):fabric.document.createElement("img")},createAccessors:function(e){var t=e.prototype;for(var n=t.stateProperties.length;n--;){var r=t.stateProperties[n],i=r.charAt(0).toUpperCase()+r.slice(1),s="set"+i,o="get"+i;t[o]||(t[o]=function(e){return new Function('return this.get("'+e+'")')}(r)),t[s]||(t[s]=function(e){return new Function("value",'return this.set("'+e+'", value)')}(r))}},clipContext:function(e,t){t.save(),t.beginPath(),e.clipTo(t),t.clip()},multiplyTransformMatrices:function(e,t){var n=[[e[0],e[2],e[4]],[e[1],e[3],e[5]],[0,0,1]],r=[[t[0],t[2],t[4]],[t[1],t[3],t[5]],[0,0,1]],i=[];for(var s=0;s<3;s++){i[s]=[];for(var o=0;o<3;o++){var u=0;for(var a=0;a<3;a++)u+=n[s][a]*r[a][o];i[s][o]=u}}return[i[0][0],i[1][0],i[0][1],i[1][1],i[0][2],i[1][2]]},getFunctionBody:function(e){return(String(e).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},normalizePoints:function(e,t){var n=fabric.util.array.min(e,"x"),r=fabric.util.array.min(e,"y");n=n<0?n:0,r=n<0?r:0;for(var i=0,s=e.length;i0&&(t>r?t-=r:t=0,n>r?n-=r:n=0);var i=!0,s=e.getImageData(t,n,r*2||1,r*2||1);for(var o=3,u=s.data.length;o0&&f===0&&(E-=2*Math.PI);var S=Math.ceil(Math.abs(E/(Math.PI*.5+.001))),x=[];for(var T=0;T1&&(h=Math.sqrt(h),t*=h,n*=h);var p=f/t,d=a/t,v=-a/n,m=f/n;return{x0:p*r+d*i,y0:v*r+m*i,x1:p*s+d*o,y1:v*s+m*o,sinTh:a,cosTh:f}}function o(e,i,s,o,u,a,f,l){r=n.call(arguments);if(t[r])return t[r];var c=l*u,h=-f*a,p=f*u,d=l*a,v=.5*(o-s),m=8/3*Math.sin(v*.5)*Math.sin(v*.5)/Math.sin(v),g=e+Math.cos(s)-m*Math.sin(s),y=i+Math.sin(s)+m*Math.cos(s),b=e+Math.cos(o),w=i+Math.sin(o),E=b+m*Math.sin(o),S=w-m*Math.cos(o);return t[r]=[c*g+h*y,p*g+d*y,c*E+h*S,p*E+d*S,c*b+h*w,p*b+d*w],t[r]}var e={},t={},n=Array.prototype.join,r;fabric.util.drawArc=function(e,t,n,r){var s=r[0],u=r[1],a=r[2],f=r[3],l=r[4],c=r[5],h=r[6],p=i(c,h,s,u,f,l,a,t,n);for(var d=0;d=t})}function r(e,t){return i(e,t,function(e,t){return e>>0;if(n===0)return-1;var r=0;arguments.length>0&&(r=Number(arguments[1]),r!==r?r=0:r!==0&&r!==Number.POSITIVE_INFINITY&&r!==Number.NEGATIVE_INFINITY&&(r=(r>0||-1)*Math.floor(Math.abs(r))));if(r>=n)return-1;var i=r>=0?r:Math.max(n-Math.abs(r),0);for(;i>>0;n>>0;r>>0;n>>0;n>>0;i>>0,n=0,r;if(arguments.length>1)r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n/g,">")}String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^[\s\xA0]+/,"").replace(/[\s\xA0]+$/,"")}),fabric.util.string={camelize:e,capitalize:t,escapeXml:n}}(),function(){var e=Array.prototype.slice,t=Function.prototype.apply,n=function(){};Function.prototype.bind||(Function.prototype.bind=function(r){var i=this,s=e.call(arguments,1),o;return s.length?o=function(){return t.call(i,this instanceof n?this:r,s.concat(e.call(arguments)))}:o=function(){return t.call(i,this instanceof n?this:r,arguments)},n.prototype=this.prototype,o.prototype=new n,o})}(),function(){function i(){}function s(t){var n=this.constructor.superclass.prototype[t];return arguments.length>1?n.apply(this,e.call(arguments,1)):n.call(this)}function o(){function u(){this.initialize.apply(this,arguments)}var n=null,o=e.call(arguments,0);typeof o[0]=="function"&&(n=o.shift()),u.superclass=n,u.subclasses=[],n&&(i.prototype=n.prototype,u.prototype=new i,n.subclasses.push(u));for(var a=0,f=o.length;a-1?e.prototype[i]=function(e){return function(){var n=this.constructor.superclass;this.constructor.superclass=r;var i=t[e].apply(this,arguments);this.constructor.superclass=n;if(e!=="initialize")return i}}(i):e.prototype[i]=t[i],n&&(t.toString!==Object.prototype.toString&&(e.prototype.toString=t.toString),t.valueOf!==Object.prototype.valueOf&&(e.prototype.valueOf=t.valueOf))};fabric.util.createClass=o}(),function(){function t(e){var t=Array.prototype.slice.call(arguments,1),n,r,i=t.length;for(r=0;r-1?s(e,t.match(/opacity:\s*(\d?\.?\d*)/)[1]):e;for(var r in t)if(r==="opacity")s(e,t[r]);else{var i=r==="float"||r==="cssFloat"?typeof n.styleFloat=="undefined"?"cssFloat":"styleFloat":r;n[i]=t[r]}return e}var t=fabric.document.createElement("div"),n=typeof t.style.opacity=="string",r=typeof t.style.filter=="string",i=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,s=function(e){return e};n?s=function(e,t){return e.style.opacity=t,e}:r&&(s=function(e,t){var n=e.style;return e.currentStyle&&!e.currentStyle.hasLayout&&(n.zoom=1),i.test(n.filter)?(t=t>=.9999?"":"alpha(opacity="+t*100+")",n.filter=n.filter.replace(i,t)):n.filter+=" alpha(opacity="+t*100+")",e}),fabric.util.setStyle=e}(),function(){function t(e){return typeof e=="string"?fabric.document.getElementById(e):e}function s(e,t){var n=fabric.document.createElement(e);for(var r in t)r==="class"?n.className=t[r]:r==="for"?n.htmlFor=t[r]:n.setAttribute(r,t[r]);return n}function o(e,t){e&&(" "+e.className+" ").indexOf(" "+t+" ")===-1&&(e.className+=(e.className?" ":"")+t)}function u(e,t,n){return typeof t=="string"&&(t=s(t,n)),e.parentNode&&e.parentNode.replaceChild(t,e),t.appendChild(e),t}function a(e,t){var n,r,i=0,s=0,o=fabric.document.documentElement,u=fabric.document.body||{scrollLeft:0,scrollTop:0};r=e;while(e&&e.parentNode&&!n)e=e.parentNode,e!==fabric.document&&fabric.util.getElementStyle(e,"position")==="fixed"&&(n=e),e!==fabric.document&&r!==t&&fabric.util.getElementStyle(e,"position")==="absolute"?(i=0,s=0):e===fabric.document?(i=u.scrollLeft||o.scrollLeft||0,s=u.scrollTop||o.scrollTop||0):(i+=e.scrollLeft||0,s+=e.scrollTop||0);return{left:i,top:s}}function f(e){var t,n=e&&e.ownerDocument,r={left:0,top:0},i={left:0,top:0},s,o={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!n)return{left:0,top:0};for(var u in o)i[o[u]]+=parseInt(l(e,u),10)||0;return t=n.documentElement,typeof e.getBoundingClientRect!="undefined"&&(r=e.getBoundingClientRect()),s=fabric.util.getScrollLeftTop(e,null),{left:r.left+s.left-(t.clientLeft||0)+i.left,top:r.top+s.top-(t.clientTop||0)+i.top}}var e=Array.prototype.slice,n,r=function(t){return e.call(t,0)};try{n=r(fabric.document.childNodes)instanceof Array}catch(i){}n||(r=function(e){var t=new Array(e.length),n=e.length;while(n--)t[n]=e[n];return t});var l;fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?l=function(e,t){return fabric.document.defaultView.getComputedStyle(e,null)[t]}:l=function(e,t){var n=e.style[t];return!n&&e.currentStyle&&(n=e.currentStyle[t]),n},function(){function n(e){return typeof e.onselectstart!="undefined"&&(e.onselectstart=fabric.util.falseFunction),t?e.style[t]="none":typeof e.unselectable=="string"&&(e.unselectable="on"),e}function r(e){return typeof e.onselectstart!="undefined"&&(e.onselectstart=null),t?e.style[t]="":typeof e.unselectable=="string"&&(e.unselectable=""),e}var e=fabric.document.documentElement.style,t="userSelect"in e?"userSelect":"MozUserSelect"in e?"MozUserSelect":"WebkitUserSelect"in e?"WebkitUserSelect":"KhtmlUserSelect"in e?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=n,fabric.util.makeElementSelectable=r}(),function(){function e(e,t){var n=fabric.document.getElementsByTagName("head")[0],r=fabric.document.createElement("script"),i=!0;r.onload=r.onreadystatechange=function(e){if(i){if(typeof this.readyState=="string"&&this.readyState!=="loaded"&&this.readyState!=="complete")return;i=!1,t(e||fabric.window.event),r=r.onload=r.onreadystatechange=null}},r.src=e,n.appendChild(r)}fabric.util.getScript=e}(),fabric.util.getById=t,fabric.util.toArray=r,fabric.util.makeElement=s,fabric.util.addClass=o,fabric.util.wrapElement=u,fabric.util.getScrollLeftTop=a,fabric.util.getElementOffset=f,fabric.util.getElementStyle=l}(),function(){function e(e,t){return e+(/\?/.test(e)?"&":"?")+t}function n(){}function r(r,i){i||(i={});var s=i.method?i.method.toUpperCase():"GET",o=i.onComplete||function(){},u=t(),a;return u.onreadystatechange=function(){u.readyState===4&&(o(u),u.onreadystatechange=n)},s==="GET"&&(a=null,typeof i.parameters=="string"&&(r=e(r,i.parameters))),u.open(s,r,!0),(s==="POST"||s==="PUT")&&u.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),u.send(a),u}var t=function(){var e=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}];for(var t=e.length;t--;)try{var n=e[t]();if(n)return e[t]}catch(r){}}();fabric.util.request=r}(),fabric.log=function(){},fabric.warn=function(){},typeof console!="undefined"&&["log","warn"].forEach(function(e){typeof console[e]!="undefined"&&console[e].apply&&(fabric[e]=function(){return console[e].apply(console,arguments)})}),function(){function e(e){n(function(t){e||(e={});var r=t||+(new Date),i=e.duration||500,s=r+i,o,u=e.onChange||function(){},a=e.abort||function(){return!1},f=e.easing||function(e,t,n,r){return-n*Math.cos(e/r*(Math.PI/2))+n+t},l="startValue"in e?e.startValue:0,c="endValue"in e?e.endValue:100,h=e.byValue||c-l;e.onStart&&e.onStart(),function p(t){o=t||+(new Date);var c=o>s?i:o-r;if(a()){e.onComplete&&e.onComplete();return}u(f(c,l,h,i));if(o>s){e.onComplete&&e.onComplete();return}n(p)}(r)})}function n(){return t.apply(fabric.window,arguments)}var t=fabric.window.requestAnimationFrame||fabric.window.webkitRequestAnimationFrame||fabric.window.mozRequestAnimationFrame||fabric.window.oRequestAnimationFrame||fabric.window.msRequestAnimationFrame||function(e){fabric.window.setTimeout(e,1e3/60)};fabric.util.animate=e,fabric.util.requestAnimFrame=n}(),function(){function e(e,t,n,r){return e','')}var t=e.fabric||(e.fabric={}),n=t.util.object.extend,r=t.util.string.capitalize,i=t.util.object.clone,s=t.util.toFixed,o=t.util.multiplyTransformMatrices,u={cx:"left",x:"left",r:"radius",cy:"top",y:"top",display:"visible",visibility:"visible",transform:"transformMatrix","fill-opacity":"fillOpacity","fill-rule":"fillRule","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","stroke-dasharray":"strokeDashArray","stroke-linecap":"strokeLineCap","stroke-linejoin":"strokeLineJoin","stroke-miterlimit":"strokeMiterLimit","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","text-decoration":"textDecoration","text-anchor":"originX"},a={stroke:"strokeOpacity",fill:"fillOpacity"};t.parseTransformAttribute=function(){function e(e,t){var n=t[0];e[0]=Math.cos(n),e[1]=Math.sin(n),e[2]=-Math.sin(n),e[3]=Math.cos(n)}function n(e,t){var n=t[0],r=t.length===2?t[1]:t[0];e[0]=n,e[3]=r}function r(e,t){e[2]=t[0]}function i(e,t){e[1]=t[0]}function s(e,t){e[4]=t[0],t.length===2&&(e[5]=t[1])}var o=[1,0,0,1,0,0],u="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",a="(?:\\s+,?\\s*|,\\s*)",f="(?:(skewX)\\s*\\(\\s*("+u+")\\s*\\))",l="(?:(skewY)\\s*\\(\\s*("+u+")\\s*\\))",c="(?:(rotate)\\s*\\(\\s*("+u+")(?:"+a+"("+u+")"+a+"("+u+"))?\\s*\\))",h="(?:(scale)\\s*\\(\\s*("+u+")(?:"+a+"("+u+"))?\\s*\\))",p="(?:(translate)\\s*\\(\\s*("+u+")(?:"+a+"("+u+"))?\\s*\\))",d="(?:(matrix)\\s*\\(\\s*("+u+")"+a+"("+u+")"+a+"("+u+")"+a+"("+u+")"+a+"("+u+")"+a+"("+u+")"+"\\s*\\))",v="(?:"+d+"|"+p+"|"+h+"|"+c+"|"+f+"|"+l+")",m="(?:"+v+"(?:"+a+v+")*"+")",g="^\\s*(?:"+m+"?)\\s*$",y=new RegExp(g),b=new RegExp(v,"g");return function(u){var a=o.concat(),f=[];if(!u||u&&!y.test(u))return a;u.replace(b,function(u){var l=(new RegExp(v)).exec(u).filter(function(e){return e!==""&&e!=null}),c=l[1],h=l.slice(2).map(parseFloat);switch(c){case"translate":s(a,h);break;case"rotate":h[0]=t.util.degreesToRadians(h[0]),e(a,h);break;case"scale":n(a,h);break;case"skewX":r(a,h);break;case"skewY":i(a,h);break;case"matrix":a=h}f.push(a.concat()),a=o.concat()});var l=f[0];while(f.length>1)f.shift(),l=t.util.multiplyTransformMatrices(l,f[0]);return l}}(),t.parseSVGDocument=function(){function s(e,t){while(e&&(e=e.parentNode))if(t.test(e.nodeName))return!0;return!1}var e=/^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,n="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",r=new RegExp("^\\s*("+n+"+)\\s*,?"+"\\s*("+n+"+)\\s*,?"+"\\s*("+n+"+)\\s*,?"+"\\s*("+n+"+)\\s*"+"$");return function(n,o,u){if(!n)return;var a=new Date,f=t.util.toArray(n.getElementsByTagName("*"));if(f.length===0&&t.isLikelyNode){f=n.selectNodes('//*[name(.)!="svg"]');var l=[];for(var c=0,h=f.length;c-1;e=e.split(/\s+/);var n=[],r,i;if(t){r=0,i=e.length;for(;r/i,"")));if(!s||!s.documentElement)return;t.parseSVGDocument(s.documentElement,function(r,i){m.set(e,{objects:t.util.array.invoke(r,"toObject"),options:i}),n(r,i)},r)}e=e.replace(/^\n\s*/,"").trim(),m.has(e,function(r){r?m.get(e,function(e){var t=g(e);n(t.objects,t.options)}):new t.util.request(e,{method:"get",onComplete:i})})},loadSVGFromString:function(e,n,r){e=e.trim();var i;if(typeof DOMParser!="undefined"){var s=new DOMParser;s&&s.parseFromString&&(i=s.parseFromString(e,"text/xml"))}else t.window.ActiveXObject&&(i=new ActiveXObject("Microsoft.XMLDOM"),i.async="false",i.loadXML(e.replace(//i,"")));t.parseSVGDocument(i.documentElement,function(e,t){n(e,t)},r)},createSVGFontFacesMarkup:function(e){var t="";for(var n=0,r=e.length;n',"",""].join("")),t},createSVGRefElementsMarkup:function(e){var t=[];return y(t,e,"backgroundColor"),y(t,e,"overlayColor"),t.join("")}})}(typeof exports!="undefined"?exports:this),fabric.ElementsParser=function(e,t,n,r){this.elements=e,this.callback=t,this.options=n,this.reviver=r},fabric.ElementsParser.prototype.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},fabric.ElementsParser.prototype.createObjects=function(){for(var e=0,t=this.elements.length;ee.x&&this.y>e.y},gte:function(e){return this.x>=e.x&&this.y>=e.y},lerp:function(e,t){return new n(this.x+(e.x-this.x)*t,this.y+(e.y-this.y)*t)},distanceFrom:function(e){var t=this.x-e.x,n=this.y-e.y;return Math.sqrt(t*t+n*n)},midPointFrom:function(e){return new n(this.x+(e.x-this.x)/2,this.y+(e.y-this.y)/2)},min:function(e){return new n(Math.min(this.x,e.x),Math.min(this.y,e.y))},max:function(e){return new n(Math.max(this.x,e.x),Math.max(this.y,e.y))},toString:function(){return this.x+","+this.y},setXY:function(e,t){this.x=e,this.y=t},setFromPoint:function(e){this.x=e.x,this.y=e.y},swap:function(e){var t=this.x,n=this.y;this.x=e.x,this.y=e.y,e.x=t,e.y=n}}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function n(e){this.status=e,this.points=[]}var t=e.fabric||(e.fabric={});if(t.Intersection){t.warn("fabric.Intersection is already defined");return}t.Intersection=n,t.Intersection.prototype={appendPoint:function(e){this.points.push(e)},appendPoints:function(e){this.points=this.points.concat(e)}},t.Intersection.intersectLineLine=function(e,r,i,s){var o,u=(s.x-i.x)*(e.y-i.y)-(s.y-i.y)*(e.x-i.x),a=(r.x-e.x)*(e.y-i.y)-(r.y-e.y)*(e.x-i.x),f=(s.y-i.y)*(r.x-e.x)-(s.x-i.x)*(r.y-e.y);if(f!==0){var l=u/f,c=a/f;0<=l&&l<=1&&0<=c&&c<=1?(o=new n("Intersection"),o.points.push(new t.Point(e.x+l*(r.x-e.x),e.y+l*(r.y-e.y)))):o=new n}else u===0||a===0?o=new n("Coincident"):o=new n("Parallel");return o},t.Intersection.intersectLinePolygon=function(e,t,r){var i=new n,s=r.length;for(var o=0;o0&&(i.status="Intersection"),i},t.Intersection.intersectPolygonPolygon=function(e,t){var r=new n,i=e.length;for(var s=0;s0&&(r.status="Intersection"),r},t.Intersection.intersectPolygonRectangle=function(e,r,i){var s=r.min(i),o=r.max(i),u=new t.Point(o.x,s.y),a=new t.Point(s.x,o.y),f=n.intersectLinePolygon(s,u,e),l=n.intersectLinePolygon(u,o,e),c=n.intersectLinePolygon(o,a,e),h=n.intersectLinePolygon(a,s,e),p=new n;return p.appendPoints(f.points),p.appendPoints(l.points),p.appendPoints(c.points),p.appendPoints(h.points),p.points.length>0&&(p.status="Intersection"),p}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function n(e){e?this._tryParsingColor(e):this.setSource([0,0,0,1])}function r(e,t,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?e+(t-e)*6*n:n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e}var t=e.fabric||(e.fabric={});if(t.Color){t.warn("fabric.Color is already defined.");return}t.Color=n,t.Color.prototype={_tryParsingColor:function(e){var t;e in n.colorNameMap&&(e=n.colorNameMap[e]);if(e==="transparent"){this.setSource([255,255,255,0]);return}t=n.sourceFromHex(e),t||(t=n.sourceFromRgb(e)),t||(t=n.sourceFromHsl(e)),t&&this.setSource(t)},_rgbToHsl:function(e,n,r){e/=255,n/=255,r/=255;var i,s,o,u=t.util.array.max([e,n,r]),a=t.util.array.min([e,n,r]);o=(u+a)/2;if(u===a)i=s=0;else{var f=u-a;s=o>.5?f/(2-u-a):f/(u+a);switch(u){case e:i=(n-r)/f+(n']:this.type==="radial"&&(r=["']);for(var i=0;i');return r.push(this.type==="linear"?"":""),r.join("")},toLive:function(e){var t;if(!this.type)return;this.type==="linear"?t=e.createLinearGradient(this.coords.x1,this.coords.y1,this.coords.x2,this.coords.y2):this.type==="radial"&&(t=e.createRadialGradient(this.coords.x1,this.coords.y1,this.coords.r1,this.coords.x2,this.coords.y2,this.coords.r2));for(var n=0,r=this.colorStops.length;n'+''+""},toLive:function(e){var t=typeof this.source=="function"?this.source():this.source;if(!t)return"";if(typeof t.src!="undefined"){if(!t.complete)return"";if(t.naturalWidth===0||t.naturalHeight===0)return""}return e.createPattern(t,this.repeat)}}),function(e){"use strict";var t=e.fabric||(e.fabric={});if(t.Shadow){t.warn("fabric.Shadow is already defined.");return}t.Shadow=t.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(e){typeof e=="string"&&(e=this._parseShadow(e));for(var n in e)this[n]=e[n];this.id=t.Object.__uid++},_parseShadow:function(e){var n=e.trim(),r=t.Shadow.reOffsetsAndBlur.exec(n)||[],i=n.replace(t.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:i.trim(),offsetX:parseInt(r[1],10)||0,offsetY:parseInt(r[2],10)||0,blur:parseInt(r[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(e){var t="SourceAlpha";return e&&(e.fill===this.color||e.stroke===this.color)&&(t="SourceGraphic"),''+''+''+""+""+''+""+""},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY};var e={},n=t.Shadow.prototype;return this.color!==n.color&&(e.color=this.color),this.blur!==n.blur&&(e.blur=this.blur),this.offsetX!==n.offsetX&&(e.offsetX=this.offsetX),this.offsetY!==n.offsetY&&(e.offsetY=this.offsetY),e}}),t.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/}(typeof exports!="undefined"?exports:this),function(){"use strict";if(fabric.StaticCanvas){fabric.warn("fabric.StaticCanvas is already defined.");return}var e=fabric.util.object.extend,t=fabric.util.getElementOffset,n=fabric.util.removeFromArray,r=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(e,t){t||(t={}),this._initStatic(e,t),fabric.StaticCanvas.activeInstance=this},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!0,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,onBeforeScaleRotate:function(){},_initStatic:function(e,t){this._objects=[],this._createLowerCanvas(e),this._initOptions(t),this._setImageSmoothing(),t.overlayImage&&this.setOverlayImage(t.overlayImage,this.renderAll.bind(this)),t.backgroundImage&&this.setBackgroundImage(t.backgroundImage,this.renderAll.bind(this)),t.backgroundColor&&this.setBackgroundColor(t.backgroundColor,this.renderAll.bind(this)),t.overlayColor&&this.setOverlayColor(t.overlayColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=t(this.lowerCanvasEl),this},setOverlayImage:function(e,t,n){return this.__setBgOverlayImage("overlayImage",e,t,n)},setBackgroundImage:function(e,t,n){return this.__setBgOverlayImage("backgroundImage",e,t,n)},setOverlayColor:function(e,t){return this.__setBgOverlayColor("overlayColor",e,t)},setBackgroundColor:function(e,t){return this.__setBgOverlayColor("backgroundColor",e,t)},_setImageSmoothing:function(){var e=this.getContext();e.imageSmoothingEnabled=this.imageSmoothingEnabled,e.webkitImageSmoothingEnabled=this.imageSmoothingEnabled,e.mozImageSmoothingEnabled=this.imageSmoothingEnabled,e.msImageSmoothingEnabled=this.imageSmoothingEnabled,e.oImageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(e,t,n,r){return typeof t=="string"?fabric.util.loadImage(t,function(t){this[e]=new fabric.Image(t,r),n&&n()},this):(this[e]=t,n&&n()),this},__setBgOverlayColor:function(e,t,n){if(t.source){var r=this;fabric.util.loadImage(t.source,function(i){r[e]=new fabric.Pattern({source:i,repeat:t.repeat,offsetX:t.offsetX,offsetY:t.offsetY}),n&&n()})}else this[e]=t,n&&n();return this},_createCanvasElement:function(){var e=fabric.document.createElement("canvas");e.style||(e.style={});if(!e)throw r;return this._initCanvasElement(e),e},_initCanvasElement:function(e){fabric.util.createCanvasElement(e);if(typeof e.getContext=="undefined")throw r},_initOptions:function(e){for(var t in e)this[t]=e[t];this.width=this.width||parseInt(this.lowerCanvasEl.width,10)||0,this.height=this.height||parseInt(this.lowerCanvasEl.height,10)||0;if(!this.lowerCanvasEl.style)return;this.lowerCanvasEl.width=this.width,this.lowerCanvasEl.height=this.height,this.lowerCanvasEl.style.width=this.width+"px",this.lowerCanvasEl.style.height=this.height+"px"},_createLowerCanvas:function(e){this.lowerCanvasEl=fabric.util.getById(e)||this._createCanvasElement(),this._initCanvasElement(this.lowerCanvasEl),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(e){return this._setDimension("width",e)},setHeight:function(e){return this._setDimension("height",e)},setDimensions:function(e){for(var t in e)this._setDimension(t,e[t]);return this},_setDimension:function(e,t){return this.lowerCanvasEl[e]=t,this.lowerCanvasEl.style[e]=t+"px",this.upperCanvasEl&&(this.upperCanvasEl[e]=t,this.upperCanvasEl.style[e]=t+"px"),this.cacheCanvasEl&&(this.cacheCanvasEl[e]=t),this.wrapperEl&&(this.wrapperEl.style[e]=t+"px"),this[e]=t,this.calcOffset(),this.renderAll(),this},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(e,t){if(!t)return;if(this.controlsAboveOverlay){var n=t.hasBorders,r=t.hasControls;t.hasBorders=t.hasControls=!1,t.render(e),t.hasBorders=n,t.hasControls=r}else t.render(e)},_onObjectAdded:function(e){this.stateful&&e.setupState(),e.setCoords(),e.canvas=this,this.fire("object:added",{target:e}),e.fire("added")},_onObjectRemoved:function(e){this.getActiveObject()===e&&(this.fire("before:selection:cleared",{target:e}),this._discardActiveObject(),this.fire("selection:cleared")),this.fire("object:removed",{target:e}),e.fire("removed")},clearContext:function(e){return e.clearRect(0,0,this.width,this.height),this},getContext:function(){return this.contextContainer},clear:function(){return this._objects.length=0,this.discardActiveGroup&&this.discardActiveGroup(),this.discardActiveObject&&this.discardActiveObject(),this.clearContext(this.contextContainer),this.contextTop&&this.clearContext(this.contextTop),this.fire("canvas:cleared"),this.renderAll(),this},renderAll:function(e){var t=this[e===!0&&this.interactive?"contextTop":"contextContainer"],n=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),e||this.clearContext(t),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,t),this._renderBackground(t),this._renderObjects(t,n),this._renderActiveGroup(t,n),this.clipTo&&t.restore(),this._renderOverlay(t),this.controlsAboveOverlay&&this.interactive&&this.drawControls(t),this.fire("after:render"),this},_renderObjects:function(e,t){var n,r;if(!t)for(n=0,r=this._objects.length;n"),n.join("")},_setSVGPreamble:function(e,t){t.suppressPreamble||e.push('','\n')},_setSVGHeader:function(e,t){e.push(""),e?e(n.join("")):n.join("")},complexity:function(){return this.path.length},_parsePath:function(){var e=[],t=[],n,r,i=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,s,o;for(var a=0,f,l=this.path.length;ad)for(var v=1,m=f.length;v"];for(var r=0,i=t.length;r"),e?e(n.join("")):n.join("")},toString:function(){return"#"},isSameColor:function(){var e=(this.getObjects()[0].get("fill")||"").toLowerCase();return this.getObjects().every(function(t){return(t.get("fill")||"").toLowerCase()===e})},complexity:function(){return this.paths.reduce(function(e,t){return e+(t&&t.complexity?t.complexity():0)},0)},getObjects:function(){return this.paths}}),t.PathGroup.fromObject=function(e,n){typeof e.paths=="string"?t.loadSVGFromURL(e.paths,function(r){var i=e.paths;delete e.paths;var s=t.util.groupSVGElements(r,e,i);n(s)}):t.util.enlivenObjects(e.paths,function(r){delete e.paths,n(new t.PathGroup(r,e))})},t.PathGroup.async=!0}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend,r=t.util.array.min,i=t.util.array.max,s=t.util.array.invoke;if(t.Group)return;var o={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};t.Group=t.util.createClass(t.Object,t.Collection,{type:"group",initialize:function(e,t){t=t||{},this._objects=e||[];for(var r=this._objects.length;r--;)this._objects[r].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),t&&n(this,t),this._setOpacityIfSame(),this.setCoords(!0),this.saveCoords()},_updateObjectsCoords:function(){this.forEachObject(this._updateObjectCoords,this)},_updateObjectCoords:function(e){var t=e.getLeft(),n=e.getTop();e.set({originalLeft:t,originalTop:n,left:t-this.left,top:n-this.top}),e.setCoords(),e.__origHasControls=e.hasControls,e.hasControls=!1},toString:function(){return"#"},addWithUpdate:function(e){return this._restoreObjectsState(),this._objects.push(e),e.group=this,this.forEachObject(this._setObjectActive,this),this._calcBounds(),this._updateObjectsCoords(),this},_setObjectActive:function(e){e.set("active",!0),e.group=this},removeWithUpdate:function(e){return this._moveFlippedObject(e),this._restoreObjectsState(),this.forEachObject(this._setObjectActive,this),this.remove(e),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(e){e.group=this},_onObjectRemoved:function(e){delete e.group,e.set("active",!1)},delegatedProperties:{fill:!0,opacity:!0,fontFamily:!0,fontWeight:!0,fontSize:!0,fontStyle:!0,lineHeight:!0,textDecoration:!0,textAlign:!0,backgroundColor:!0},_set:function(e,t){if(e in this.delegatedProperties){var n=this._objects.length;this[e]=t;while(n--)this._objects[n].set(e,t)}else this[e]=t},toObject:function(e){return n(this.callSuper("toObject",e),{objects:s(this._objects,"toObject",e)})},render:function(e,n){if(!this.visible)return;e.save(),this.transform(e),this.clipTo&&t.util.clipContext(this,e);for(var r=0,i=this._objects.length;r'];for(var n=0,r=this._objects.length;n"),e?e(t.join("")):t.join("")},get:function(e){if(e in o){if(this[e])return this[e];for(var t=0,n=this._objects.length;t','");if(this.stroke||this.strokeDashArray){var n=this.fill;this.fill=null,t.push("'),this.fill=n}return t.push(""),e?e(t.join("")):t.join("")},getSrc:function(){if(this.getElement())return this.getElement().src||this.getElement()._src},toString:function(){return'#'},clone:function(e,t){this.constructor.fromObject(this.toObject(t),e)},applyFilters:function(e){if(!this._originalElement)return;if(this.filters.length===0){this._element=this._originalElement,e&&e();return}var t=this._originalElement,n=fabric.util.createCanvasElement(),r=fabric.util.createImage(),i=this;return n.width=t.width,n.height=t.height,n.getContext("2d").drawImage(t,0,0,t.width,t.height),this.filters.forEach(function(e){e&&e.applyTo(n)}),r.width=t.width,r.height=t.height,fabric.isLikelyNode?(r.src=n.toBuffer(undefined,fabric.Image.pngCompression),i._element=r,e&&e()):(r.onload=function(){i._element=r,e&&e(),r.onload=n=t=null},r.src=n.toDataURL("image/png")),this},_render:function(e){this._element&&e.drawImage(this._element,-this.width/2,-this.height/2,this.width,this.height)},_resetWidthHeight:function(){var e=this.getElement();this.set("width",e.width),this.set("height",e.height)},_initElement:function(e){this.setElement(fabric.util.getById(e)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(e){e||(e={}),this.setOptions(e),this._setWidthHeight(e),this._element&&this.crossOrigin&&(this._element.crossOrigin=this.crossOrigin)},_initFilters:function(e,t){e.filters&&e.filters.length?fabric.util.enlivenObjects(e.filters,function(e){t&&t(e)},"fabric.Image.filters"):t&&t()},_setWidthHeight:function(e){this.width="width"in e?e.width:this.getElement()?this.getElement().width||0:0,this.height="height"in e?e.height:this.getElement()?this.getElement().height||0:0},complexity:function(){return 1}}),fabric.Image.CSS_CANVAS="canvas-img",fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc,fabric.Image.fromObject=function(e,t){fabric.util.loadImage(e.src,function(n){fabric.Image.prototype._initFilters.call(e,e,function(r){e.filters=r||[];var i=new fabric.Image(n,e);t&&t(i)})},null,e.crossOrigin)},fabric.Image.fromURL=function(e,t,n){fabric.util.loadImage(e,function(e){t(new fabric.Image(e,n))},null,n&&n.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(e,n,r){var i=fabric.parseAttributes(e,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(i["xlink:href"],n,t(r?fabric.util.object.clone(r):{},i))},fabric.Image.async=!0,fabric.Image.pngCompression=1}(typeof exports!="undefined"?exports:this),fabric.util.object.extend(fabric.Object.prototype,{_getAngleValueForStraighten:function(){var e=this.getAngle()%360;return e>0?Math.round((e-1)/90)*90:Math.round(e/90)*90},straighten:function(){return this.setAngle(this._getAngleValueForStraighten()),this},fxStraighten:function(e){e=e||{};var t=function(){},n=e.onComplete||t,r=e.onChange||t,i=this;return fabric.util.animate({startValue:this.get("angle"),endValue:this._getAngleValueForStraighten(),duration:this.FX_DURATION,onChange:function(e){i.setAngle(e),r()},onComplete:function(){i.setCoords(),n()},onStart:function(){i.set("active",!1)}}),this}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{straightenObject:function(e){return e.straighten(),this.renderAll(),this},fxStraightenObject:function(e){return e.fxStraighten({onChange:this.renderAll.bind(this)}),this}}),fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",toObject:function(){return{type:this.type}},toJSON:function(){return this.toObject()}}),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Brightness=t.util.createClass(t.Image.filters.BaseFilter,{type:"Brightness",initialize:function(e){e=e||{},this.brightness=e.brightness||0},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.brightness;for(var s=0,o=r.length;sa||C<0||C>u)continue;var k=(N*u+C)*4,L=t[x*i+T];b+=o[k]*L,w+=o[k+1]*L,E+=o[k+2]*L,S+=o[k+3]*L}h[y]=b,h[y+1]=w,h[y+2]=E,h[y+3]=S+p*(255-S)}n.putImageData(c,0,0)},toObject:function(){return n(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),t.Image.filters.Convolute.fromObject=function(e){return new t.Image.filters.Convolute(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.GradientTransparency=t.util.createClass(t.Image.filters.BaseFilter,{type:"GradientTransparency",initialize:function(e){e=e||{},this.threshold=e.threshold||100},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.threshold,s=r.length;for(var o=0,u=r.length;o-1?e.channel:0},applyTo:function(e){if(!this.mask)return;var n=e.getContext("2d"),r=n.getImageData(0,0,e.width,e.height),i=r.data,s=this.mask.getElement(),o=t.util.createCanvasElement(),u=this.channel,a,f=r.width*r.height*4;o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0,s.width,s.height);var l=o.getContext("2d").getImageData(0,0,s.width,s.height),c=l.data;for(a=0;ao&&f>o&&l>o&&u(a-f)'},_render:function(e){var t=this.group&&this.group.type==="path-group";t&&!this.transformMatrix?e.translate(-this.group.width/2+this.left,-this.group.height/2+this.top):t&&this.transformMatrix&&e.translate(-this.group.width/2,-this.group.height/2),typeof Cufon=="undefined"||this.useNative===!0?this._renderViaNative(e):this._renderViaCufon(e)},_renderViaNative:function(e){var n=this.text.split(this._reNewline);this.transform(e,t.isLikelyNode),this._setTextStyles(e),this.width=this._getTextWidth(e,n),this.height=this._getTextHeight(e,n),this.clipTo&&t.util.clipContext(this,e),this._renderTextBackground(e,n),this._translateForTextAlign(e),this._renderText(e,n),this.textAlign!=="left"&&this.textAlign!=="justify"&&e.restore(),this._renderTextDecoration(e,n),this.clipTo&&e.restore(),this._setBoundaries(e,n),this._totalLineHeight=0},_renderText:function(e,t){e.save(),this._setShadow(e),this._renderTextFill(e,t),this._renderTextStroke(e,t),this._removeShadow(e),e.restore()},_translateForTextAlign:function(e){this.textAlign!=="left"&&this.textAlign!=="justify"&&(e.save(),e.translate(this.textAlign==="center"?this.width/2:this.width,0))},_setBoundaries:function(e,t){this._boundaries=[];for(var n=0,r=t.length;nn&&(n=s)}return n},_renderChars:function(e,t,n,r,i){t[e](n,r,i)},_renderTextLine:function(e,t,n,r,i,s){i-=this.fontSize/4;if(this.textAlign!=="justify"){this._renderChars(e,t,n,r,i,s);return}var o=t.measureText(n).width,u=this.width;if(u>o){var a=n.split(/\s+/),f=t.measureText(n.replace(/\s+/g,"")).width,l=u-f,c=a.length-1,h=l/c,p=0;for(var d=0,v=a.length;d-1&&i(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&i(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&i(this.fontSize*this.lineHeight-this.fontSize)},_getFontDeclaration:function(){return[t.isLikelyNode?this.fontWeight:this.fontStyle,t.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",t.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(e,t){if(!this.visible)return;e.save();var n=this.transformMatrix;n&&(!this.group||this.group.type==="path-group")&&e.transform(n[0],n[1],n[2],n[3],n[4],n[5]),this._render(e),!t&&this.active&&(this.drawBorders(e),this.drawControls(e)),e.restore()},toObject:function(e){var t=n(this.callSuper("toObject",e),{text:this.text,fontSize:this.fontSize,fontWeight:this.fontWeight,fontFamily:this.fontFamily,fontStyle:this.fontStyle,lineHeight:this.lineHeight,textDecoration:this.textDecoration,textAlign:this.textAlign,path:this.path,textBackgroundColor:this.textBackgroundColor,useNative:this.useNative});return this.includeDefaultValues||this._removeDefaultValues(t),t},toSVG:function(e){var t=[],n=this.text.split(this._reNewline),r=this._getSVGLeftTopOffsets(n),i=this._getSVGTextAndBg(r.lineTop,r.textLeft,n),s=this._getSVGShadows(r.lineTop,n);return r.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._wrapSVGTextAndBg(t,i,s,r),e?e(t.join("")):t.join("")},_getSVGLeftTopOffsets:function(e){var t=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,n=-(this.width/2),r=this.useNative?this.fontSize-1:this.height/2-e.length*this.fontSize-this._totalLineHeight;return{textLeft:n,textTop:r,lineTop:t}},_wrapSVGTextAndBg:function(e,t,n,r){e.push('',t.textBgRects.join(""),"',n.join(""),t.textSpans.join(""),"","")},_getSVGShadows:function(e,n){var r=[],s,o,u=1;if(!this.shadow||!this._boundaries)return r;for(s=0,o=n.length;s",t.util.string.escapeXml(n[s]),""),u=1}else u++;return r},_getSVGTextAndBg:function(e,t,n){var r=[],i=[],s=1;this._setSVGBg(i);for(var o=0,u=n.length;o",t.util.string.escapeXml(e),"")},_setSVGTextLineBg:function(e,t,n,r){e.push("')},_setSVGBg:function(e){this.backgroundColor&&this._boundaries&&e.push("')},_getFillAttributes:function(e){var n=e&&typeof e=="string"?new t.Color(e):"";return!n||!n.getSource()||n.getAlpha()===1?'fill="'+e+'"':'opacity="'+n.getAlpha()+'" fill="'+n.setAlpha(1).toRgb()+'"'},_set:function(e,t){e==="fontFamily"&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+t+"$3")),this.callSuper("_set",e,t),e in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),t.Text.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("x y dx dy font-family font-style font-weight font-size text-decoration text-anchor".split(" ")),t.Text.DEFAULT_SVG_FONT_SIZE=16,t.Text.fromElement=function(e,n){if(!e)return null;var r=t.parseAttributes(e,t.Text.ATTRIBUTE_NAMES);n=t.util.object.extend(n?t.util.object.clone(n):{},r),"dx"in r&&(n.left+=r.dx),"dy"in r&&(n.top+=r.dy),"fontSize"in n||(n.fontSize=t.Text.DEFAULT_SVG_FONT_SIZE),n.originX||(n.originX="center");var i=new t.Text(e.textContent,n);return i.set({left:i.getLeft()+i.getWidth()/2,top:i.getTop()-i.getHeight()/2}),i},t.Text.fromObject=function(e){return new t.Text(e.text,r(e))},t.util.createAccessors(t.Text)}(typeof exports!="undefined"?exports:this),function(){var e=fabric.util.object.clone;fabric.IText=fabric.util.createClass(fabric.Text,fabric.Observable,{type:"i-text",selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"#333",cursorDelay:1e3,cursorDuration:600,styles:null,caching:!0,_skipFillStrokeCheck:!0,_reSpace:/\s|\n/,_fontSizeFraction:4,_currentCursorOpacity:0,_selectionDirection:null,_abortCursorAnimation:!1,_charWidthsCache:{},initialize:function(e,t){this.styles=t?t.styles||{}:{},this.callSuper("initialize",e,t),this.initBehavior(),fabric.IText.instances.push(this),this.__lineWidths={},this.__lineHeights={},this.__lineOffsets={}},isEmptyStyles:function(){if(!this.styles)return!0;var e=this.styles;for(var t in e)for(var n in e[t])for(var r in e[t][n])return!1;return!0},setSelectionStart:function(e){this.selectionStart!==e&&this.canvas&&this.canvas.fire("text:selection:changed",{target:this}),this.selectionStart=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionStart=e)},setSelectionEnd:function(e){this.selectionEnd!==e&&this.canvas&&this.canvas.fire("text:selection:changed",{target:this}),this.selectionEnd=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionEnd=e)},getSelectionStyles:function(e,t){if(arguments.length===2){var n=[];for(var r=e;r=r.charIndex&&(a!==o||hs&&a-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,0,this.fontSize/20),u.indexOf("line-through")>-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,o/2,a/20),u.indexOf("overline")>-1&&this._renderCharDecorationAtOffset(e,n,r,i,s-this.fontSize/this._fontSizeFraction,this.fontSize/20)},_renderCharDecorationAtOffset:function(e,t,n,r,i,s){e.fillRect(t,n-i,r,s)},_renderTextLine:function(e,t,n,r,i,s){i+=this.fontSize/4,this.callSuper("_renderTextLine",e,t,n,r,i,s)},_renderTextDecoration:function(e,t){if(this.isEmptyStyles())return this.callSuper("_renderTextDecoration",e,t)},_renderTextLinesBackground:function(e,t){if(!this.textBackgroundColor&&!this.styles)return;e.save(),this.textBackgroundColor&&(e.fillStyle=this.textBackgroundColor);var n=0,r=this.fontSize/this._fontSizeFraction;for(var i=0,s=t.length;in&&(n=s)}return n},_getHeightOfLine:function(e,t,n){n=n||this.text.split(this._reNewline);var r=this._getHeightOfChar(e,n[t][0],t,0),i=n[t],s=i.split("");for(var o=1,u=s.length;or&&(r=a)}return r*this.lineHeight},_getTextHeight:function(e,t){var n=0;for(var r=0,i=t.length;r-1)t++,n--;return e-t},findWordBoundaryRight:function(e){var t=0,n=e;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n++;while(/\S/.test(this.text.charAt(n))&&n-1)t++,n--;return e-t},findLineBoundaryRight:function(e){var t=0,n=e;while(!/\n/.test(this.text.charAt(n))&&n0&&nr;s?this.removeStyleObject(s,n+1):this.removeStyleObject(this.get2DCursorLocation(n).charIndex===0,n)}this.text=this.text.slice(0,e)+this.text.slice(t)},insertChars:function(e){var t=this.text.slice(this.selectionStart,this.selectionStart+1)==="\n";this.text=this.text.slice(0,this.selectionStart)+e+this.text.slice(this.selectionEnd),this.selectionStart===this.selectionEnd&&this.insertStyleObjects(e,t,this.copiedStyles),this.selectionStart+=e.length,this.selectionEnd=this.selectionStart,this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("changed"),this.canvas&&this.canvas.fire("text:changed",{target:this})},insertNewlineStyleObject:function(t,n,r){this.shiftLineStyles(t,1),this.styles[t+1]||(this.styles[t+1]={});var i=this.styles[t][n-1],s={};if(r)s[0]=e(i),this.styles[t+1]=s;else{for(var o in this.styles[t])parseInt(o,10)>=n&&(s[parseInt(o,10)-n]=this.styles[t][o],delete this.styles[t][o]);this.styles[t+1]=s}},insertCharStyleObject:function(t,n,r){var i=this.styles[t],s=e(i);n===0&&!r&&(n=1);for(var o in s){var u=parseInt(o,10);u>=n&&(i[u+1]=s[u])}this.styles[t][n]=r||e(i[n-1])},insertStyleObjects:function(e,t,n){if(this.isEmptyStyles())return;var r=this.get2DCursorLocation(),i=r.lineIndex,s=r.charIndex;this.styles[i]||(this.styles[i]={}),e==="\n"?this.insertNewlineStyleObject(i,s,t):n?this._insertStyles(n):this.insertCharStyleObject(i,s)},_insertStyles:function(e){for(var t=0,n=e.length;tt&&(this.styles[s+n]=r[s])}},removeStyleObject:function(t,n){var r=this.get2DCursorLocation(n),i=r.lineIndex,s=r.charIndex;if(t){var o=this.text.split(this._reNewline),u=o[i-1],a=u?u.length:0;this.styles[i-1]||(this.styles[i-1]={});for(s in this.styles[i])this.styles[i-1][parseInt(s,10)+a]=this.styles[i][s];this.shiftLineStyles(i,-1)}else{var f=this.styles[i];if(f){var l=this.selectionStart===this.selectionEnd?-1:0;delete f[s+l]}var c=e(f);for(var h in c){var p=parseInt(h,10);p>=s&&p!==0&&(f[p-1]=c[p],delete f[p])}}},insertNewline:function(){this.insertChars("\n")}})}(),fabric.util.object.extend(fabric.IText.prototype,{initDoubleClickSimulation:function(){this.__lastClickTime=+(new Date),this.__lastLastClickTime=+(new Date),this.__lastPointer={},this.on("mousedown",this.onMouseDown.bind(this))},onMouseDown:function(e){this.__newClickTime=+(new Date);var t=this.canvas.getPointer(e.e);this.isTripleClick(t)?(this.fire("tripleclick",e),this._stopEvent(e.e)):this.isDoubleClick(t)&&(this.fire("dblclick",e),this._stopEvent(e.e)),this.__lastLastClickTime=this.__lastClickTime,this.__lastClickTime=this.__newClickTime,this.__lastPointer=t,this.__lastIsEditing=this.isEditing},isDoubleClick:function(e){return this.__newClickTime-this.__lastClickTime<500&&this.__lastPointer.x===e.x&&this.__lastPointer.y===e.y&&this.__lastIsEditing},isTripleClick:function(e){return this.__newClickTime-this.__lastClickTime<500&&this.__lastClickTime-this.__lastLastClickTime<500&&this.__lastPointer.x===e.x&&this.__lastPointer.y===e.y},_stopEvent:function(e){e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation()},initCursorSelectionHandlers:function(){this.initSelectedHandler(),this.initMousedownHandler(),this.initMousemoveHandler(),this.initMouseupHandler(),this.initClicks()},initClicks:function(){this.on("dblclick",function(e){this.selectWord(this.getSelectionStartFromPointer(e.e))}),this.on("tripleclick",function(e){this.selectLine
+(this.getSelectionStartFromPointer(e.e))})},initMousedownHandler:function(){this.on("mousedown",function(e){var t=this.canvas.getPointer(e.e);this.__mousedownX=t.x,this.__mousedownY=t.y,this.__isMousedown=!0,this.hiddenTextarea&&this.canvas&&this.canvas.wrapperEl.appendChild(this.hiddenTextarea),this.selected&&this.setCursorByClick(e.e),this.isEditing&&(this.__selectionStartOnMouseDown=this.selectionStart,this.initDelayedCursor(!0))})},initMousemoveHandler:function(){this.on("mousemove",function(e){if(!this.__isMousedown||!this.isEditing)return;var t=this.getSelectionStartFromPointer(e.e);t>=this.__selectionStartOnMouseDown?(this.setSelectionStart(this.__selectionStartOnMouseDown),this.setSelectionEnd(t)):(this.setSelectionStart(t),this.setSelectionEnd(this.__selectionStartOnMouseDown))})},_isObjectMoved:function(e){var t=this.canvas.getPointer(e);return this.__mousedownX!==t.x||this.__mousedownY!==t.y},initMouseupHandler:function(){this.on("mouseup",function(e){this.__isMousedown=!1;if(this._isObjectMoved(e.e))return;this.selected&&(this.enterEditing(),this.initDelayedCursor(!0)),this.selected=!0})},setCursorByClick:function(e){var t=this.getSelectionStartFromPointer(e);e.shiftKey?to?0:1,f=r+a;return this.flipX&&(f=i-f),f>this.text.length&&(f=this.text.length),s===i&&f--,f}}),fabric.util.object.extend(fabric.IText.prototype,{initHiddenTextarea:function(){this.hiddenTextarea=fabric.document.createElement("textarea"),this.hiddenTextarea.setAttribute("autocapitalize","off"),this.hiddenTextarea.style.cssText="position: absolute; top: 0; left: -9999px",fabric.document.body.appendChild(this.hiddenTextarea),fabric.util.addListener(this.hiddenTextarea,"keydown",this.onKeyDown.bind(this)),fabric.util.addListener(this.hiddenTextarea,"keypress",this.onKeyPress.bind(this)),!this._clickHandlerInitialized&&this.canvas&&(fabric.util.addListener(this.canvas.upperCanvasEl,"click",this.onClick.bind(this)),this._clickHandlerInitialized=!0)},_keysMap:{8:"removeChars",13:"insertNewline",37:"moveCursorLeft",38:"moveCursorUp",39:"moveCursorRight",40:"moveCursorDown",46:"forwardDelete"},_ctrlKeysMap:{65:"selectAll",67:"copy",86:"paste",88:"cut"},onClick:function(){this.hiddenTextarea&&this.hiddenTextarea.focus()},onKeyDown:function(e){if(!this.isEditing)return;if(e.keyCode in this._keysMap)this[this._keysMap[e.keyCode]](e);else{if(!(e.keyCode in this._ctrlKeysMap&&(e.ctrlKey||e.metaKey)))return;this[this._ctrlKeysMap[e.keyCode]](e)}e.preventDefault(),e.stopPropagation(),this.canvas&&this.canvas.renderAll()},forwardDelete:function(e){this.selectionStart===this.selectionEnd&&this.moveCursorRight(e),this.removeChars(e)},copy:function(){var e=this.getSelectedText();this.copiedText=e,this.copiedStyles=this.getSelectionStyles(this.selectionStart,this.selectionEnd)},paste:function(){this.copiedText&&this.insertChars(this.copiedText)},cut:function(e){this.copy(),this.removeChars(e)},onKeyPress:function(e){if(!this.isEditing||e.metaKey||e.ctrlKey||e.keyCode===8||e.keyCode===13)return;this.insertChars(String.fromCharCode(e.which)),e.preventDefault(),e.stopPropagation()},getDownCursorOffset:function(e,t){var n=t?this.selectionEnd:this.selectionStart,r=this.text.split(this._reNewline),i,s,o=this.text.slice(0,n),u=this.text.slice(n),a=o.slice(o.lastIndexOf("\n")+1),f=u.match(/(.*)\n?/)[1],l=(u.match(/.*\n(.*)\n?/)||{})[1]||"",c=this.get2DCursorLocation(n);if(c.lineIndex===r.length-1||e.metaKey)return this.text.length-n;var h=this._getWidthOfLine(this.ctx,c.lineIndex,r);s=this._getLineLeftOffset(h);var p=s,d=c.lineIndex;for(var v=0,m=a.length;vn){f=!0;var d=u-p,v=u,m=Math.abs(d-n),g=Math.abs(v-n);a=gthis.text.length&&(this.selectionStart=this.text.length),this.selectionEnd=this.selectionStart},moveCursorDownWithShift:function(e){if(this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd){this.selectionStart+=e,this._selectionDirection="left";return}this._selectionDirection="right",this.selectionEnd+=e,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length)},getUpCursorOffset:function(e,t){var n=t?this.selectionEnd:this.selectionStart,r=this.get2DCursorLocation(n);if(r.lineIndex===0||e.metaKey)return n;var i=this.text.slice(0,n),s=i.slice(i.lastIndexOf("\n")+1),o=(i.match(/\n?(.*)\n.*$/)||{})[1]||"",u=this.text.split(this._reNewline),a,f=this._getWidthOfLine(this.ctx,r.lineIndex,u),l=this._getLineLeftOffset(f),c=l,h=r.lineIndex;for(var p=0,d=s.length;pn){f=!0;var d=u-p,v=u,m=Math.abs(d-n),g=Math.abs(v-n);a=g=this.text.length&&this.selectionEnd>=this.text.length)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorRightWithShift(e):this.moveCursorRightWithoutShift(e),this.initDelayedCursor()},moveCursorRightWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?this._moveRight(e,"selectionStart"):(this._selectionDirection="right",this._moveRight(e,"selectionEnd"),this.text.charAt(this.selectionEnd-1)==="\n"&&this.selectionEnd++,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorRightWithoutShift:function(e){this._selectionDirection="right",this.selectionStart===this.selectionEnd?(this._moveRight(e,"selectionStart"),this.selectionEnd=this.selectionStart):(this.selectionEnd+=this.getNumNewLinesInSelectedText(),this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length),this.selectionStart=this.selectionEnd)},removeChars:function(e){this.selectionStart===this.selectionEnd?this._removeCharsNearCursor(e):this._removeCharsFromTo(this.selectionStart,this.selectionEnd),this.selectionEnd=this.selectionStart,this._removeExtraneousStyles(),this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("changed"),this.canvas&&this.canvas.fire("text:changed",{target:this})},_removeCharsNearCursor:function(e){if(this.selectionStart!==0)if(e.metaKey){var t=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(t,this.selectionStart),this.selectionStart=t}else if(e.altKey){var n=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(n,this.selectionStart),this.selectionStart=n}else{var r=this.text.slice(this.selectionStart-1,this.selectionStart)==="\n";this.removeStyleObject(r),this.selectionStart--,this.text=this.text.slice(0,this.selectionStart)+this.text.slice(this.selectionStart+1)}}}),fabric.util.object.extend(fabric.IText.prototype,{_setSVGTextLineText:function(e,t,n,r,i,s){this.styles[t]?this._setSVGTextLineChars(e,t,n,r,i,s):this.callSuper("_setSVGTextLineText",e,t,n,r,i)},_setSVGTextLineChars:function(e,t,n,r,i,s){var o=t===0||this.useNative?"y":"dy",u=e.split(""),a=0,f=this._getSVGLineLeftOffset(t),l=this._getSVGLineTopOffset(t),c=this._getHeightOfLine(this.ctx,t);for(var h=0,p=u.length;h'].join("")},_createTextCharSpan:function(e,t,n,r,i,s){var o=this.getSvgStyles.call(fabric.util.object.extend({visible:!0,fill:this.fill,stroke:this.stroke,type:"text"},t));return['',fabric.util.string.escapeXml(e),""].join("")}}),function(){function request(e,t,n){var r=URL.parse(e);r.port||(r.port=r.protocol.indexOf("https:")===0?443:80);var i=r.port===443?HTTPS:HTTP,s=i.request({hostname:r.hostname,port:r.port,path:r.path,method:"GET"},function(e){var r="";t&&e.setEncoding(t),e.on("end",function(){n(r)}),e.on("data",function(t){e.statusCode===200&&(r+=t)})});s.on("error",function(e){e.errno===process.ECONNREFUSED?fabric.log("ECONNREFUSED: connection refused to "+r.hostname+":"+r.port):fabric.log(e.message)}),s.end()}function requestFs(e,t){var n=require("fs");n.readFile(e,function(e,n){if(e)throw fabric.log(e),e;t(n)})}if(typeof document!="undefined"&&typeof window!="undefined")return;var DOMParser=require("xmldom").DOMParser,URL=require("url"),HTTP=require("http"),HTTPS=require("https"),Canvas=require("canvas"),Image=require("canvas").Image;fabric.util.loadImage=function(e,t,n){function r(r){i.src=new Buffer(r,"binary"),i._src=e,t&&t.call(n,i)}var i=new Image;e&&(e instanceof Buffer||e.indexOf("data")===0)?(i.src=i._src=e,t&&t.call(n,i)):e&&e.indexOf("http")!==0?requestFs(e,r):e?request(e,"binary",r):t&&t.call(n,e)},fabric.loadSVGFromURL=function(e,t,n){e=e.replace(/^\n\s*/,"").replace(/\?.*$/,"").trim(),e.indexOf("http")!==0?requestFs(e,function(e){fabric.loadSVGFromString(e.toString(),t,n)}):request(e,"",function(e){fabric.loadSVGFromString(e,t,n)})},fabric.loadSVGFromString=function(e,t,n){var r=(new DOMParser).parseFromString(e);fabric.parseSVGDocument(r.documentElement,function(e,n){t&&t(e,n)},n)},fabric.util.getScript=function(url,callback){request(url,"",function(body){eval(body),callback&&callback()})},fabric.Image.fromObject=function(e,t){fabric.util.loadImage(e.src,function(n){var r=new fabric.Image(n);r._initConfig(e),r._initFilters(e,function(e){r.filters=e||[],t&&t(r)})})},fabric.createCanvasForNode=function(e,t,n,r){r=r||n;var i=fabric.document.createElement("canvas"),s=new Canvas(e||600,t||600,r);i.style={},i.width=s.width,i.height=s.height;var o=fabric.Canvas||fabric.StaticCanvas,u=new o(i,n);return u.contextContainer=s.getContext("2d"),u.nodeCanvas=s,u.Font=Canvas.Font,u},fabric.StaticCanvas.prototype.createPNGStream=function(){return this.nodeCanvas.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(e){return this.nodeCanvas.createJPEGStream(e)};var origSetWidth=fabric.StaticCanvas.prototype.setWidth;fabric.StaticCanvas.prototype.setWidth=function(e){return origSetWidth.call(this,e),this.nodeCanvas.width=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setWidth=fabric.StaticCanvas.prototype.setWidth);var origSetHeight=fabric.StaticCanvas.prototype.setHeight;fabric.StaticCanvas.prototype.setHeight=function(e){return origSetHeight.call(this,e),this.nodeCanvas.height=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setHeight=fabric.StaticCanvas.prototype.setHeight)}();
\ No newline at end of file
diff --git a/dist/fabric.min.js.gz b/dist/fabric.min.js.gz
new file mode 100644
index 00000000..f65a6018
Binary files /dev/null and b/dist/fabric.min.js.gz differ
diff --git a/dist/all.js b/dist/fabric.require.js
similarity index 90%
rename from dist/all.js
rename to dist/fabric.require.js
index 962a3e37..320db6ac 100644
--- a/dist/all.js
+++ b/dist/fabric.require.js
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL minifier=uglifyjs` */
-/*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */
+/*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: "1.4.0" };
+var fabric = fabric || { version: "1.4.6" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -37,6 +37,7 @@ fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
* @type array
*/
fabric.SHARED_ATTRIBUTES = [
+ "display",
"transform",
"fill", "fill-opacity", "fill-rule",
"opacity",
@@ -1276,7 +1277,7 @@ if (typeof exports != 'undefined') {
/*
json2.js
- 2011-10-19
+ 2014-02-04
Public Domain.
@@ -1435,8 +1436,7 @@ if (typeof exports != 'undefined') {
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
-var JSON;
-if (!JSON) {
+if (typeof JSON !== 'object') {
JSON = {};
}
@@ -1450,8 +1450,7 @@ if (!JSON) {
if (typeof Date.prototype.toJSON !== 'function') {
- /** @ignore */
- Date.prototype.toJSON = function (key) {
+ Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
@@ -1465,25 +1464,16 @@ if (!JSON) {
String.prototype.toJSON =
Number.prototype.toJSON =
- /** @ignore */
- Boolean.prototype.toJSON = function (key) {
+ Boolean.prototype.toJSON = function () {
return this.valueOf();
};
}
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ var cx,
+ escapable,
gap,
indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
+ meta,
rep;
@@ -1635,7 +1625,16 @@ if (!JSON) {
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
- /** @ignore */
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
@@ -1683,7 +1682,7 @@ if (!JSON) {
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
- /** @ignore */
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
@@ -1766,6 +1765,7 @@ if (!JSON) {
}
}());
+
/*
----------------------------------------------------
Event.js : 1.1.3 : 2013/07/17 : MIT License
@@ -1795,10 +1795,10 @@ if (typeof(eventjs) === "undefined") var eventjs = Event;
(function(root) { "use strict";
// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness).
-root.modifyEventListener = true;
+root.modifyEventListener = false;
// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness).
-root.modifySelectors = true;
+root.modifySelectors = false;
// Event maintenance.
root.add = function(target, type, listener, configure) {
@@ -3721,12 +3721,12 @@ fabric.Collection = {
/**
* Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
* Objects should be instances of (or inherit from) fabric.Object
- * @param [...] Zero or more fabric instances
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
*/
add: function () {
this._objects.push.apply(this._objects, arguments);
- for (var i = arguments.length; i--; ) {
+ for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
this.renderOnAddRemove && this.renderAll();
@@ -3740,6 +3740,7 @@ fabric.Collection = {
* @param {Number} index Index to insert object at
* @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
* @return {Self} thisArg
+ * @chainable
*/
insertAt: function (object, index, nonSplicing) {
var objects = this.getObjects();
@@ -3755,22 +3756,27 @@ fabric.Collection = {
},
/**
- * Removes an object from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * @param {Object} object Object to remove
+ * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
+ * @chainable
*/
- remove: function(object) {
+ remove: function() {
var objects = this.getObjects(),
- index = objects.indexOf(object);
+ index;
- // only call onObjectRemoved if an object was actually removed
- if (index !== -1) {
- objects.splice(index, 1);
- this._onObjectRemoved(object);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ index = objects.indexOf(arguments[i]);
+
+ // only call onObjectRemoved if an object was actually removed
+ if (index !== -1) {
+ objects.splice(index, 1);
+ this._onObjectRemoved(arguments[i]);
+ }
}
this.renderOnAddRemove && this.renderAll();
- return object;
+ return this;
},
/**
@@ -4020,7 +4026,9 @@ fabric.Collection = {
* @return {Object} Object for given namespace (default fabric)
*/
resolveNamespace: function(namespace) {
- if (!namespace) return fabric;
+ if (!namespace) {
+ return fabric;
+ }
var parts = namespace.split('.'),
len = parts.length,
@@ -4182,7 +4190,7 @@ fabric.Collection = {
drawDashedLine: function(ctx, x, y, x2, y2, da) {
var dx = x2 - x,
dy = y2 - y,
- len = sqrt(dx*dx + dy*dy),
+ len = sqrt(dx * dx + dy * dy),
rot = atan2(dy, dx),
dc = da.length,
di = 0,
@@ -4290,22 +4298,23 @@ fabric.Collection = {
var a = [
[matrixA[0], matrixA[2], matrixA[4]],
[matrixA[1], matrixA[3], matrixA[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var b = [
+ b = [
[matrixB[0], matrixB[2], matrixB[4]],
[matrixB[1], matrixB[3], matrixB[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var result = [];
- for (var r=0; r<3; r++) {
+ result = [];
+
+ for (var r = 0; r < 3; r++) {
result[r] = [];
- for (var c=0; c<3; c++) {
+ for (var c = 0; c < 3; c++) {
var sum = 0;
- for (var k=0; k<3; k++) {
- sum += a[r][k]*b[k][c];
+ for (var k = 0; k < 3; k++) {
+ sum += a[r][k] * b[k][c];
}
result[r][c] = sum;
@@ -4378,9 +4387,8 @@ fabric.Collection = {
}
}
- var _isTransparent = true;
- var imageData = ctx.getImageData(
- x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
+ var _isTransparent = true,
+ imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
// Split image data - for tolerance > 1, pixelDataSize = 4;
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
@@ -4414,37 +4422,43 @@ fabric.Collection = {
return arcToSegmentsCache[argsString];
}
- var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y);
+ var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y),
- var d = (coords.x1-coords.x0) * (coords.x1-coords.x0) +
- (coords.y1-coords.y0) * (coords.y1-coords.y0);
+ d = (coords.x1 - coords.x0) * (coords.x1 - coords.x0) +
+ (coords.y1 - coords.y0) * (coords.y1 - coords.y0),
- var sfactor_sq = 1 / d - 0.25;
- if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactorSq = 1 / d - 0.25;
- var sfactor = Math.sqrt(sfactor_sq);
- if (sweep === large) sfactor = -sfactor;
-
- var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1-coords.y0);
- var yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1-coords.x0);
-
- var th0 = Math.atan2(coords.y0-yc, coords.x0-xc);
- var th1 = Math.atan2(coords.y1-yc, coords.x1-xc);
-
- var th_arc = th1-th0;
- if (th_arc < 0 && sweep === 1) {
- th_arc += 2*Math.PI;
- }
- else if (th_arc > 0 && sweep === 0) {
- th_arc -= 2 * Math.PI;
+ if (sfactorSq < 0) {
+ sfactorSq = 0;
}
- var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
- var result = [];
- for (var i=0; i 0 && sweep === 0) {
+ thArc -= 2 * Math.PI;
+ }
+
+ var segments = Math.ceil(Math.abs(thArc / (Math.PI * 0.5 + 0.001))),
+ result = [];
+
+ for (var i = 0; i < segments; i++) {
+ var th2 = th0 + i * thArc / segments,
+ th3 = th0 + (i + 1) * thArc / segments;
+
+ result[i] = [xc, yc, th2, th3, rx, ry, coords.sinTh, coords.cosTh];
}
arcToSegmentsCache[argsString] = result;
@@ -4453,56 +4467,59 @@ fabric.Collection = {
function getXYCoords(rotateX, rx, ry, ox, oy, x, y) {
- var th = rotateX * (Math.PI/180);
- var sin_th = Math.sin(th);
- var cos_th = Math.cos(th);
+ var th = rotateX * (Math.PI / 180),
+ sinTh = Math.sin(th),
+ cosTh = Math.cos(th);
+
rx = Math.abs(rx);
ry = Math.abs(ry);
- var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
- var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
- var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
+
+ var px = cosTh * (ox - x) * 0.5 + sinTh * (oy - y) * 0.5,
+ py = cosTh * (oy - y) * 0.5 - sinTh * (ox - x) * 0.5,
+ pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
+
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
}
- var a00 = cos_th / rx;
- var a01 = sin_th / rx;
- var a10 = (-sin_th) / ry;
- var a11 = (cos_th) / ry;
+ var a00 = cosTh / rx,
+ a01 = sinTh / rx,
+ a10 = (-sinTh) / ry,
+ a11 = (cosTh) / ry;
return {
x0: a00 * ox + a01 * oy,
y0: a10 * ox + a11 * oy,
x1: a00 * x + a01 * y,
y1: a10 * x + a11 * y,
- sin_th: sin_th,
- cos_th: cos_th
+ sinTh: sinTh,
+ cosTh: cosTh
};
}
- function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
+ function segmentToBezier(cx, cy, th0, th1, rx, ry, sinTh, cosTh) {
argsString = _join.call(arguments);
+
if (segmentToBezierCache[argsString]) {
return segmentToBezierCache[argsString];
}
- var a00 = cos_th * rx;
- var a01 = -sin_th * ry;
- var a10 = sin_th * rx;
- var a11 = cos_th * ry;
+ var a00 = cosTh * rx,
+ a01 = -sinTh * ry,
+ a10 = sinTh * rx,
+ a11 = cosTh * ry,
+ thHalf = 0.5 * (th1 - th0),
+ t = (8 / 3) * Math.sin(thHalf * 0.5) *
+ Math.sin(thHalf * 0.5) / Math.sin(thHalf),
- var th_half = 0.5 * (th1 - th0);
- var t = (8/3) * Math.sin(th_half * 0.5) *
- Math.sin(th_half * 0.5) / Math.sin(th_half);
-
- var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
- var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
- var x3 = cx + Math.cos(th1);
- var y3 = cy + Math.sin(th1);
- var x2 = x3 + t * Math.sin(th1);
- var y2 = y3 - t * Math.cos(th1);
+ x1 = cx + Math.cos(th0) - t * Math.sin(th0),
+ y1 = cy + Math.sin(th0) + t * Math.cos(th0),
+ x3 = cx + Math.cos(th1),
+ y3 = cy + Math.sin(th1),
+ x2 = x3 + t * Math.sin(th1),
+ y2 = y3 - t * Math.cos(th1);
segmentToBezierCache[argsString] = [
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
@@ -4521,17 +4538,18 @@ fabric.Collection = {
* @param {Array} coords
*/
fabric.util.drawArc = function(ctx, x, y, coords) {
- var rx = coords[0];
- var ry = coords[1];
- var rot = coords[2];
- var large = coords[3];
- var sweep = coords[4];
- var ex = coords[5];
- var ey = coords[6];
- var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
- for (var i=0; iString#trim on MDN
+ */
+ String.prototype.trim = function () {
+ // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
+ return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ };
+ }
+ /* _ES5_COMPAT_END_ */
+
/**
- * Trims a string (removing whitespace from the beginning and the end)
- * @function external:String#trim
- * @see String#trim on MDN
+ * Camelizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to camelize
+ * @return {String} Camelized version of a string
*/
- String.prototype.trim = function () {
- // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
- return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ function camelize(string) {
+ return string.replace(/-+(.)?/g, function(match, character) {
+ return character ? character.toUpperCase() : '';
+ });
+ }
+
+ /**
+ * Capitalizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to capitalize
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
+ * and other letters stay untouched, if false first letter is capitalized
+ * and other letters are converted to lowercase.
+ * @return {String} Capitalized version of a string
+ */
+ function capitalize(string, firstLetterOnly) {
+ return string.charAt(0).toUpperCase() +
+ (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
+ }
+
+ /**
+ * Escapes XML in a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to escape
+ * @return {String} Escaped version of a string
+ */
+ function escapeXml(string) {
+ return string.replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>');
+ }
+
+ /**
+ * String utilities
+ * @namespace fabric.util.string
+ */
+ fabric.util.string = {
+ camelize: camelize,
+ capitalize: capitalize,
+ escapeXml: escapeXml
};
-}
-/* _ES5_COMPAT_END_ */
-
-/**
- * Camelizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to camelize
- * @return {String} Camelized version of a string
- */
-function camelize(string) {
- return string.replace(/-+(.)?/g, function(match, character) {
- return character ? character.toUpperCase() : '';
- });
-}
-
-/**
- * Capitalizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to capitalize
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
- * and other letters stay untouched, if false first letter is capitalized
- * and other letters are converted to lowercase.
- * @return {String} Capitalized version of a string
- */
-function capitalize(string, firstLetterOnly) {
- return string.charAt(0).toUpperCase() +
- (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
-}
-
-/**
- * Escapes XML in a string
- * @memberOf fabric.util.string
- * @param {String} string String to escape
- * @return {String} Escaped version of a string
- */
-function escapeXml(string) {
- return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(//g, '>');
-}
-
-/**
- * String utilities
- * @namespace fabric.util.string
- */
-fabric.util.string = {
- camelize: camelize,
- capitalize: capitalize,
- escapeXml: escapeXml
-};
}());
@@ -4907,16 +4925,16 @@ fabric.util.string = {
* @return {Function}
*/
Function.prototype.bind = function(thisArg) {
- var fn = this, args = slice.call(arguments, 1), bound;
+ var _this = this, args = slice.call(arguments, 1), bound;
if (args.length) {
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
};
}
else {
/** @ignore */
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, arguments);
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
};
}
Dummy.prototype = this.prototype;
@@ -4932,51 +4950,51 @@ fabric.util.string = {
(function() {
- var slice = Array.prototype.slice, emptyFunction = function() { };
+ var slice = Array.prototype.slice, emptyFunction = function() { },
- var IS_DONTENUM_BUGGY = (function(){
- for (var p in { toString: 1 }) {
- if (p === 'toString') return false;
- }
- return true;
- })();
+ IS_DONTENUM_BUGGY = (function(){
+ for (var p in { toString: 1 }) {
+ if (p === 'toString') return false;
+ }
+ return true;
+ })(),
- /** @ignore */
- var addMethods = function(klass, source, parent) {
- for (var property in source) {
+ /** @ignore */
+ addMethods = function(klass, source, parent) {
+ for (var property in source) {
- if (property in klass.prototype &&
- typeof klass.prototype[property] === 'function' &&
- (source[property] + '').indexOf('callSuper') > -1) {
+ if (property in klass.prototype &&
+ typeof klass.prototype[property] === 'function' &&
+ (source[property] + '').indexOf('callSuper') > -1) {
- klass.prototype[property] = (function(property) {
- return function() {
+ klass.prototype[property] = (function(property) {
+ return function() {
- var superclass = this.constructor.superclass;
- this.constructor.superclass = parent;
- var returnValue = source[property].apply(this, arguments);
- this.constructor.superclass = superclass;
+ var superclass = this.constructor.superclass;
+ this.constructor.superclass = parent;
+ var returnValue = source[property].apply(this, arguments);
+ this.constructor.superclass = superclass;
- if (property !== 'initialize') {
- return returnValue;
+ if (property !== 'initialize') {
+ return returnValue;
+ }
+ };
+ })(property);
+ }
+ else {
+ klass.prototype[property] = source[property];
+ }
+
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString !== Object.prototype.toString) {
+ klass.prototype.toString = source.toString;
}
- };
- })(property);
- }
- else {
- klass.prototype[property] = source[property];
- }
-
- if (IS_DONTENUM_BUGGY) {
- if (source.toString !== Object.prototype.toString) {
- klass.prototype.toString = source.toString;
+ if (source.valueOf !== Object.prototype.valueOf) {
+ klass.prototype.valueOf = source.valueOf;
+ }
+ }
}
- if (source.valueOf !== Object.prototype.valueOf) {
- klass.prototype.valueOf = source.valueOf;
- }
- }
- }
- };
+ };
function Subclass() { }
@@ -5043,15 +5061,16 @@ fabric.util.string = {
}
return true;
}
- var getUniqueId = (function () {
- var uid = 0;
- return function (element) {
- return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
- };
- })();
/** @ignore */
- var getElement, setElement;
+ var getElement,
+ setElement,
+ getUniqueId = (function () {
+ var uid = 0;
+ return function (element) {
+ return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
+ };
+ })();
(function () {
var elements = { };
@@ -5207,9 +5226,9 @@ fabric.util.string = {
event || (event = fabric.window.event);
var element = event.target ||
- (typeof event.srcElement !== unknown ? event.srcElement : null);
+ (typeof event.srcElement !== unknown ? event.srcElement : null),
- var scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
+ scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
return {
x: pointerX(event) + scroll.left,
@@ -5222,9 +5241,9 @@ fabric.util.string = {
// is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
// need to investigate later
return (typeof event.clientX !== unknown ? event.clientX : 0);
- };
+ },
- var pointerY = function(event) {
+ pointerY = function(event) {
return (typeof event.clientY !== unknown ? event.clientY : 0);
};
@@ -5339,21 +5358,21 @@ fabric.util.string = {
return typeof id === 'string' ? fabric.document.getElementById(id) : id;
}
- /**
- * Converts an array-like object (e.g. arguments or NodeList) to an array
- * @memberOf fabric.util
- * @param {Object} arrayLike
- * @return {Array}
- */
- var toArray = function(arrayLike) {
- return _slice.call(arrayLike, 0);
- };
+ var sliceCanConvertNodelists,
+ /**
+ * Converts an array-like object (e.g. arguments or NodeList) to an array
+ * @memberOf fabric.util
+ * @param {Object} arrayLike
+ * @return {Array}
+ */
+ toArray = function(arrayLike) {
+ return _slice.call(arrayLike, 0);
+ };
- var sliceCanConvertNodelists;
try {
sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
}
- catch(err) { }
+ catch (err) { }
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
@@ -5395,7 +5414,7 @@ fabric.util.string = {
* @param {String} className Class to add to an element
*/
function addClass(element, className) {
- if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
+ if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
element.className += (element.className ? ' ' : '') + className;
}
}
@@ -5418,7 +5437,14 @@ fabric.util.string = {
wrapper.appendChild(element);
return wrapper;
}
-
+
+ /**
+ * Returns element scroll offsets
+ * @memberOf fabric.util
+ * @param {HTMLElement} element Element to operate on
+ * @param {HTMLElement} upperCanvasEl Upper canvas element
+ * @return {Object} Object with left/top values
+ */
function getScrollLeftTop(element, upperCanvasEl) {
var firstFixedAncestor,
@@ -5469,19 +5495,19 @@ fabric.util.string = {
*/
function getElementOffset(element) {
var docElem,
- box = {left: 0, top: 0},
doc = element && element.ownerDocument,
- offset = {left: 0, top: 0},
+ box = { left: 0, top: 0 },
+ offset = { left: 0, top: 0 },
scrollLeftTop,
offsetAttributes = {
- 'borderLeftWidth': 'left',
- 'borderTopWidth': 'top',
- 'paddingLeft': 'left',
- 'paddingTop': 'top'
+ borderLeftWidth: 'left',
+ borderTopWidth: 'top',
+ paddingLeft: 'left',
+ paddingTop: 'top'
};
- if (!doc){
- return {left: 0, top: 0};
+ if (!doc) {
+ return { left: 0, top: 0 };
}
for (var attr in offsetAttributes) {
@@ -5489,7 +5515,7 @@ fabric.util.string = {
}
docElem = doc.documentElement;
- if ( typeof element.getBoundingClientRect !== "undefined" ) {
+ if ( typeof element.getBoundingClientRect !== 'undefined' ) {
box = element.getBoundingClientRect();
}
@@ -5508,33 +5534,33 @@ fabric.util.string = {
* @param {String} attr Style attribute to get for element
* @return {String} Style attribute value of the given element.
*/
- function getElementStyle(element, attr) {
- if (!element.style) {
- element.style = { };
- }
-
- if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
+ var getElementStyle;
+ if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
+ getElementStyle = function(element, attr) {
return fabric.document.defaultView.getComputedStyle(element, null)[attr];
- }
- else {
+ };
+ }
+ else {
+ getElementStyle = function(element, attr) {
var value = element.style[attr];
- if (!value && element.currentStyle) value = element.currentStyle[attr];
+ if (!value && element.currentStyle) {
+ value = element.currentStyle[attr];
+ }
return value;
- }
+ };
}
(function () {
- var style = fabric.document.documentElement.style;
-
- var selectProp = 'userSelect' in style
- ? 'userSelect'
- : 'MozUserSelect' in style
- ? 'MozUserSelect'
- : 'WebkitUserSelect' in style
- ? 'WebkitUserSelect'
- : 'KhtmlUserSelect' in style
- ? 'KhtmlUserSelect'
- : '';
+ var style = fabric.document.documentElement.style,
+ selectProp = 'userSelect' in style
+ ? 'userSelect'
+ : 'MozUserSelect' in style
+ ? 'MozUserSelect'
+ : 'WebkitUserSelect' in style
+ ? 'WebkitUserSelect'
+ : 'KhtmlUserSelect' in style
+ ? 'KhtmlUserSelect'
+ : '';
/**
* Makes element unselectable
@@ -5587,7 +5613,7 @@ fabric.util.string = {
* @param {Function} callback Callback to execute when script is finished loading
*/
function getScript(url, callback) {
- var headEl = fabric.document.getElementsByTagName("head")[0],
+ var headEl = fabric.document.getElementsByTagName('head')[0],
scriptEl = fabric.document.createElement('script'),
loading = true;
@@ -5631,9 +5657,9 @@ fabric.util.string = {
var makeXHR = (function() {
var factories = [
- function() { return new ActiveXObject("Microsoft.XMLHTTP"); },
- function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
- function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); },
+ function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
+ function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
+ function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
function() { return new XMLHttpRequest(); }
];
for (var i = factories.length; i--; ) {
@@ -5731,7 +5757,7 @@ if (typeof console !== 'undefined') {
* @param {Number} [options.endValue=100] Ending value
* @param {Number} [options.byValue=100] Value to modify the property by
* @param {Function} [options.easing] Easing function
- * @param {Number} [options.duration=500] Duration of change
+ * @param {Number} [options.duration=500] Duration of change (in ms)
*/
function animate(options) {
@@ -5743,7 +5769,7 @@ if (typeof console !== 'undefined') {
finish = start + duration, time,
onChange = options.onChange || function() { },
abort = options.abort || function() { return false; },
- easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t/d * (Math.PI/2)) + c + b;},
+ easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
startValue = 'startValue' in options ? options.startValue : 0,
endValue = 'endValue' in options ? options.endValue : 100,
byValue = options.byValue || endValue - startValue;
@@ -5783,9 +5809,9 @@ if (typeof console !== 'undefined') {
* @param {Function} callback Callback to invoke
* @param {DOMElement} element optional Element to associate with animation
*/
- var requestAnimFrame = function() {
+ function requestAnimFrame() {
return _requestAnimFrame.apply(fabric.window, arguments);
- };
+ }
fabric.util.animate = animate;
fabric.util.requestAnimFrame = requestAnimFrame;
@@ -5796,8 +5822,13 @@ if (typeof console !== 'undefined') {
(function() {
function normalize(a, c, p, s) {
- if (a < Math.abs(c)) { a=c; s=p/4; }
- else s = p/(2*Math.PI) * Math.asin (c/a);
+ if (a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
return { a: a, c: c, p: p, s: s };
}
@@ -5812,7 +5843,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutCubic(t, b, c, d) {
- return c*((t=t/d-1)*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t + 1) + b;
}
/**
@@ -5821,8 +5852,10 @@ if (typeof console !== 'undefined') {
*/
function easeInOutCubic(t, b, c, d) {
t /= d/2;
- if (t < 1) return c/2*t*t*t + b;
- return c/2*((t-=2)*t*t + 2) + b;
+ if (t < 1) {
+ return c / 2 * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t + 2) + b;
}
/**
@@ -5830,7 +5863,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInQuart(t, b, c, d) {
- return c*(t/=d)*t*t*t + b;
+ return c * (t /= d) * t * t * t + b;
}
/**
@@ -5838,7 +5871,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutQuart(t, b, c, d) {
- return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
/**
@@ -5846,9 +5879,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutQuart(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t + b;
- return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t + b;
+ }
+ return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
/**
@@ -5856,7 +5891,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInQuint(t, b, c, d) {
- return c*(t/=d)*t*t*t*t + b;
+ return c * (t /= d) * t * t * t * t + b;
}
/**
@@ -5864,7 +5899,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutQuint(t, b, c, d) {
- return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
/**
@@ -5872,9 +5907,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutQuint(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t*t + b;
- return c/2*((t-=2)*t*t*t*t + 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
/**
@@ -5882,7 +5919,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInSine(t, b, c, d) {
- return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
}
/**
@@ -5890,7 +5927,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutSine(t, b, c, d) {
- return c * Math.sin(t/d * (Math.PI/2)) + b;
+ return c * Math.sin(t / d * (Math.PI / 2)) + b;
}
/**
@@ -5898,7 +5935,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutSine(t, b, c, d) {
- return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
/**
@@ -5906,7 +5943,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInExpo(t, b, c, d) {
- return (t===0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
}
/**
@@ -5914,7 +5951,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutExpo(t, b, c, d) {
- return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
/**
@@ -5922,11 +5959,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutExpo(t, b, c, d) {
- if (t===0) return b;
- if (t===d) return b+c;
- t /= d/2;
- if (t < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ if (t === 0) {
+ return b;
+ }
+ if (t === d) {
+ return b + c;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ }
+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
/**
@@ -5934,7 +5977,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInCirc(t, b, c, d) {
- return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
}
/**
@@ -5942,7 +5985,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutCirc(t, b, c, d) {
- return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
}
/**
@@ -5950,9 +5993,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutCirc(t, b, c, d) {
- t /= d/2;
- if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
- return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ }
+ return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}
/**
@@ -5960,11 +6005,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
return -elastic(opts, t, d) + b;
}
@@ -5974,13 +6025,19 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
- return opts.a*Math.pow(2,-10*t) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p ) + opts.c + b;
+ return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
}
/**
@@ -5988,14 +6045,22 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
- t /= d/2;
- if (t===2) return b+c;
- if (!p) p=d*(0.3*1.5);
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d / 2;
+ if (t === 2) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * (0.3 * 1.5);
+ }
var opts = normalize(a, c, p, s);
- if (t < 1) return -0.5 * elastic(opts, t, d) + b;
- return opts.a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p )*0.5 + opts.c + b;
+ if (t < 1) {
+ return -0.5 * elastic(opts, t, d) + b;
+ }
+ return opts.a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
}
/**
@@ -6003,8 +6068,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*(t/=d)*t*((s+1)*t - s) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
/**
@@ -6012,8 +6079,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
}
/**
@@ -6021,10 +6090,14 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- t /= d/2;
- if (t < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
- return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ }
+ return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
}
/**
@@ -6032,7 +6105,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInBounce(t, b, c, d) {
- return c - easeOutBounce (d-t, 0, c, d) + b;
+ return c - easeOutBounce (d - t, 0, c, d) + b;
}
/**
@@ -6040,14 +6113,17 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeOutBounce(t, b, c, d) {
- if ((t/=d) < (1/2.75)) {
- return c*(7.5625*t*t) + b;
- } else if (t < (2/2.75)) {
- return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
- } else if (t < (2.5/2.75)) {
- return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
- } else {
- return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ if ((t /= d) < (1 / 2.75)) {
+ return c * (7.5625 * t * t) + b;
+ }
+ else if (t < (2/2.75)) {
+ return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
+ }
+ else if (t < (2.5/2.75)) {
+ return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
+ }
+ else {
+ return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
}
}
@@ -6056,8 +6132,10 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
function easeInOutBounce(t, b, c, d) {
- if (t < d/2) return easeInBounce (t*2, 0, c, d) * 0.5 + b;
- return easeOutBounce (t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
+ if (t < d / 2) {
+ return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
+ }
+ return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
}
/**
@@ -6072,7 +6150,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInQuad: function(t, b, c, d) {
- return c*(t/=d)*t + b;
+ return c * (t /= d) * t + b;
},
/**
@@ -6080,7 +6158,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeOutQuad: function(t, b, c, d) {
- return -c *(t/=d)*(t-2) + b;
+ return -c * (t /= d) * (t - 2) + b;
},
/**
@@ -6088,9 +6166,11 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInOutQuad: function(t, b, c, d) {
- t /= (d/2);
- if (t < 1) return c/2*t*t + b;
- return -c/2 * ((--t)*(t-2) - 1) + b;
+ t /= (d / 2);
+ if (t < 1) {
+ return c / 2 * t * t + b;
+ }
+ return -c / 2 * ((--t) * (t - 2) - 1) + b;
},
/**
@@ -6098,7 +6178,7 @@ if (typeof console !== 'undefined') {
* @memberOf fabric.util.ease
*/
easeInCubic: function(t, b, c, d) {
- return c*(t/=d)*t*t + b;
+ return c * (t /= d) * t * t + b;
},
easeOutCubic: easeOutCubic,
@@ -6134,7 +6214,7 @@ if (typeof console !== 'undefined') {
(function(global) {
- "use strict";
+ 'use strict';
/**
* @name fabric
@@ -6146,34 +6226,37 @@ if (typeof console !== 'undefined') {
capitalize = fabric.util.string.capitalize,
clone = fabric.util.object.clone,
toFixed = fabric.util.toFixed,
- multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
+ multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
- var attributesMap = {
- 'fill-opacity': 'fillOpacity',
- 'fill-rule': 'fillRule',
- 'font-family': 'fontFamily',
- 'font-size': 'fontSize',
- 'font-style': 'fontStyle',
- 'font-weight': 'fontWeight',
- 'cx': 'left',
- 'x': 'left',
- 'r': 'radius',
- 'stroke-dasharray': 'strokeDashArray',
- 'stroke-linecap': 'strokeLineCap',
- 'stroke-linejoin': 'strokeLineJoin',
- 'stroke-miterlimit':'strokeMiterLimit',
- 'stroke-opacity': 'strokeOpacity',
- 'stroke-width': 'strokeWidth',
- 'text-decoration': 'textDecoration',
- 'cy': 'top',
- 'y': 'top',
- 'transform': 'transformMatrix'
- };
+ attributesMap = {
+ cx: 'left',
+ x: 'left',
+ r: 'radius',
+ cy: 'top',
+ y: 'top',
+ display: 'visible',
+ visibility: 'visible',
+ transform: 'transformMatrix',
+ 'fill-opacity': 'fillOpacity',
+ 'fill-rule': 'fillRule',
+ 'font-family': 'fontFamily',
+ 'font-size': 'fontSize',
+ 'font-style': 'fontStyle',
+ 'font-weight': 'fontWeight',
+ 'stroke-dasharray': 'strokeDashArray',
+ 'stroke-linecap': 'strokeLineCap',
+ 'stroke-linejoin': 'strokeLineJoin',
+ 'stroke-miterlimit': 'strokeMiterLimit',
+ 'stroke-opacity': 'strokeOpacity',
+ 'stroke-width': 'strokeWidth',
+ 'text-decoration': 'textDecoration',
+ 'text-anchor': 'originX'
+ },
- var colorAttributes = {
- 'stroke': 'strokeOpacity',
- 'fill': 'fillOpacity'
- };
+ colorAttributes = {
+ stroke: 'strokeOpacity',
+ fill: 'fillOpacity'
+ };
function normalizeAttr(attr) {
// transform attribute names
@@ -6204,6 +6287,16 @@ if (typeof console !== 'undefined') {
value = fabric.parseTransformAttribute(value);
}
}
+ else if (attr === 'visible') {
+ value = (value === 'none' || value === 'hidden') ? false : true;
+ // display=none on parent element always takes precedence over child element
+ if (parentAttributes.visible === false) {
+ value = false;
+ }
+ }
+ else if (attr === 'originX' /* text-anchor */) {
+ value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
+ }
isArray = Object.prototype.toString.call(value) === '[object Array]';
@@ -6284,30 +6377,30 @@ if (typeof console !== 'undefined') {
],
// == begin transform regexp
- number = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)',
+ number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- comma_wsp = '(?:\\s+,?\\s*|,\\s*)',
+ commaWsp = '(?:\\s+,?\\s*|,\\s*)',
skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + ')' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + ')' +
+ commaWsp + '(' + number + '))?\\s*\\))',
scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
matrix = '(?:(matrix)\\s*\\(\\s*' +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
'(' + number + ')' +
'\\s*\\))',
@@ -6320,12 +6413,12 @@ if (typeof console !== 'undefined') {
skewY +
')',
- transforms = '(?:' + transform + '(?:' + comma_wsp + transform + ')*' + ')',
+ transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
- transform_list = '^\\s*(?:' + transforms + '?)\\s*$',
+ transformList = '^\\s*(?:' + transforms + '?)\\s*$',
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
- reTransformList = new RegExp(transform_list),
+ reTransformList = new RegExp(transformList),
// == end transform regexp
reTransform = new RegExp(transform, 'g');
@@ -6333,8 +6426,8 @@ if (typeof console !== 'undefined') {
return function(attributeValue) {
// start with identity matrix
- var matrix = iMatrix.concat();
- var matrices = [ ];
+ var matrix = iMatrix.concat(),
+ matrices = [ ];
// return if no argument was given or
// an argument does not match transform attribute regexp
@@ -6350,11 +6443,12 @@ if (typeof console !== 'undefined') {
operation = m[1],
args = m.slice(2).map(parseFloat);
- switch(operation) {
+ switch (operation) {
case 'translate':
translateMatrix(matrix, args);
break;
case 'rotate':
+ args[0] = fabric.util.degreesToRadians(args[0]);
rotateMatrix(matrix, args);
break;
case 'scale':
@@ -6393,19 +6487,19 @@ if (typeof console !== 'undefined') {
if (!match) return;
- var fontStyle = match[1];
- // Font variant is not used
- // var fontVariant = match[2];
- var fontWeight = match[3];
- var fontSize = match[4];
- var lineHeight = match[5];
- var fontFamily = match[6];
+ var fontStyle = match[1],
+ // font variant is not used
+ // fontVariant = match[2],
+ fontWeight = match[3],
+ fontSize = match[4],
+ lineHeight = match[5],
+ fontFamily = match[6];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
- oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
+ oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseFloat(fontSize);
@@ -6493,22 +6587,22 @@ if (typeof console !== 'undefined') {
*/
fabric.parseSVGDocument = (function() {
- var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/;
+ var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
- // \d doesn't quite cut it (as we need to match an actual float number)
+ // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+ // \d doesn't quite cut it (as we need to match an actual float number)
- // matches, e.g.: +14.56e-12, etc.
- var reNum = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)';
+ // matches, e.g.: +14.56e-12, etc.
+ reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- var reViewBoxAttrValue = new RegExp(
- '^' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*' +
- '$'
- );
+ reViewBoxAttrValue = new RegExp(
+ '^' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*' +
+ '$'
+ );
function hasAncestorWithNodeName(element, nodeName) {
while (element && (element = element.parentNode)) {
@@ -6525,10 +6619,10 @@ if (typeof console !== 'undefined') {
var startTime = new Date(),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
- if (descendants.length === 0) {
+ if (descendants.length === 0 && fabric.isLikelyNode) {
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
// https://github.com/ajaxorg/node-o3-xml/issues/21
- descendants = doc.selectNodes("//*[name(.)!='svg']");
+ descendants = doc.selectNodes('//*[name(.)!="svg"]');
var arr = [ ];
for (var i = 0, len = descendants.length; i < len; i++) {
arr[i] = descendants[i];
@@ -6541,30 +6635,43 @@ if (typeof console !== 'undefined') {
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
});
- if (!elements || (elements && !elements.length)) return;
+ if (!elements || (elements && !elements.length)) {
+ callback && callback([], {});
+ return;
+ }
var viewBoxAttr = doc.getAttribute('viewBox'),
- widthAttr = doc.getAttribute('width'),
- heightAttr = doc.getAttribute('height'),
+ widthAttr = parseFloat(doc.getAttribute('width')),
+ heightAttr = parseFloat(doc.getAttribute('height')),
width = null,
height = null,
+ viewBoxWidth,
+ viewBoxHeight,
minX,
minY;
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
- minX = parseInt(viewBoxAttr[1], 10);
- minY = parseInt(viewBoxAttr[2], 10);
- width = parseInt(viewBoxAttr[3], 10);
- height = parseInt(viewBoxAttr[4], 10);
+ minX = parseFloat(viewBoxAttr[1]);
+ minY = parseFloat(viewBoxAttr[2]);
+ viewBoxWidth = parseFloat(viewBoxAttr[3]);
+ viewBoxHeight = parseFloat(viewBoxAttr[4]);
}
- // values of width/height attributes overwrite those extracted from viewbox attribute
- width = widthAttr ? parseFloat(widthAttr) : width;
- height = heightAttr ? parseFloat(heightAttr) : height;
+ if (viewBoxWidth && widthAttr && viewBoxWidth !== widthAttr) {
+ width = viewBoxWidth;
+ height = viewBoxHeight;
+ }
+ else {
+ // values of width/height attributes overwrite those extracted from viewbox attribute
+ width = widthAttr ? widthAttr : viewBoxWidth;
+ height = heightAttr ? heightAttr : viewBoxHeight;
+ }
var options = {
width: width,
- height: height
+ height: height,
+ widthAttr: widthAttr,
+ heightAttr: heightAttr
};
fabric.gradientDefs = fabric.getGradientDefs(doc);
@@ -6748,7 +6855,7 @@ if (typeof console !== 'undefined') {
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
*/
parseElements: function(elements, callback, options, reviver) {
- fabric.ElementsParser.parse(elements, callback, options, reviver);
+ new fabric.ElementsParser(elements, callback, options, reviver).parse();
},
/**
@@ -6762,7 +6869,9 @@ if (typeof console !== 'undefined') {
var oStyle = { },
style = element.getAttribute('style');
- if (!style) return oStyle;
+ if (!style) {
+ return oStyle;
+ }
if (typeof style === 'string') {
parseStyleString(style, oStyle);
@@ -6798,21 +6907,27 @@ if (typeof console !== 'undefined') {
len = points.length;
for (; i < len; i++) {
var pair = points[i].split(',');
- parsedPoints.push({ x: parseFloat(pair[0]), y: parseFloat(pair[1]) });
+ parsedPoints.push({
+ x: parseFloat(pair[0]),
+ y: parseFloat(pair[1])
+ });
}
}
else {
i = 0;
len = points.length;
for (; i < len; i+=2) {
- parsedPoints.push({ x: parseFloat(points[i]), y: parseFloat(points[i+1]) });
+ parsedPoints.push({
+ x: parseFloat(points[i]),
+ y: parseFloat(points[i + 1])
+ });
}
}
// odd number of points is an error
- if (parsedPoints.length % 2 !== 0) {
+ // if (parsedPoints.length % 2 !== 0) {
// return null;
- }
+ // }
return parsedPoints;
},
@@ -6892,13 +7007,13 @@ if (typeof console !== 'undefined') {
function onComplete(r) {
var xml = r.responseXML;
- if (!xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
+ if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
xml = new ActiveXObject('Microsoft.XMLDOM');
xml.async = 'false';
//IE chokes on DOCTYPE
xml.loadXML(r.responseText.replace(//i,''));
}
- if (!xml.documentElement) return;
+ if (!xml || !xml.documentElement) return;
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
svgCache.set(url, {
@@ -6988,78 +7103,80 @@ if (typeof console !== 'undefined') {
})(typeof exports !== 'undefined' ? exports : this);
-fabric.ElementsParser = {
+fabric.ElementsParser = function(elements, callback, options, reviver) {
+ this.elements = elements;
+ this.callback = callback;
+ this.options = options;
+ this.reviver = reviver;
+};
- parse: function(elements, callback, options, reviver) {
+fabric.ElementsParser.prototype.parse = function() {
+ this.instances = new Array(this.elements.length);
+ this.numElements = this.elements.length;
- this.elements = elements;
- this.callback = callback;
- this.options = options;
- this.reviver = reviver;
+ this.createObjects();
+};
- this.instances = new Array(elements.length);
- this.numElements = elements.length;
+fabric.ElementsParser.prototype.createObjects = function() {
+ for (var i = 0, len = this.elements.length; i < len; i++) {
+ (function(_this, i) {
+ setTimeout(function() {
+ _this.createObject(_this.elements[i], i);
+ }, 0);
+ })(this, i);
+ }
+};
- this.createObjects();
- },
-
- createObjects: function() {
- for (var i = 0, len = this.elements.length; i < len; i++) {
- this.createObject(this.elements[i], i);
+fabric.ElementsParser.prototype.createObject = function(el, index) {
+ var klass = fabric[fabric.util.string.capitalize(el.tagName)];
+ if (klass && klass.fromElement) {
+ try {
+ this._createObject(klass, el, index);
}
- },
+ catch (err) {
+ fabric.log(err);
+ }
+ }
+ else {
+ this.checkIfDone();
+ }
+};
- createObject: function(el, index) {
- var klass = fabric[fabric.util.string.capitalize(el.tagName)];
- if (klass && klass.fromElement) {
- try {
- this._createObject(klass, el, index);
- }
- catch(err) {
- fabric.log(err);
- }
- }
- else {
- this.checkIfDone();
- }
- },
+fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
+ if (klass.async) {
+ klass.fromElement(el, this.createCallback(index, el), this.options);
+ }
+ else {
+ var obj = klass.fromElement(el, this.options);
+ this.reviver && this.reviver(el, obj);
+ this.instances.splice(index, 0, obj);
+ this.checkIfDone();
+ }
+};
- _createObject: function(klass, el, index) {
- if (klass.async) {
- klass.fromElement(el, this.createCallback(index, el), this.options);
- }
- else {
- var obj = klass.fromElement(el, this.options);
- this.reviver && this.reviver(el, obj);
- this.instances.splice(index, 0, obj);
- this.checkIfDone();
- }
- },
+fabric.ElementsParser.prototype.createCallback = function(index, el) {
+ var _this = this;
+ return function(obj) {
+ _this.reviver && _this.reviver(el, obj);
+ _this.instances.splice(index, 0, obj);
+ _this.checkIfDone();
+ };
+};
- createCallback: function(index, el) {
- var _this = this;
- return function(obj) {
- _this.reviver && _this.reviver(el, obj);
- _this.instances.splice(index, 0, obj);
- _this.checkIfDone();
- };
- },
-
- checkIfDone: function() {
- if (--this.numElements === 0) {
- this.instances = this.instances.filter(function(el) {
- return el != null;
- });
- fabric.resolveGradients(this.instances);
- this.callback(this.instances);
- }
+fabric.ElementsParser.prototype.checkIfDone = function() {
+ if (--this.numElements === 0) {
+ this.instances = this.instances.filter(function(el) {
+ return el != null;
+ });
+ fabric.resolveGradients(this.instances);
+ this.callback(this.instances);
}
};
(function(global) {
- "use strict";
+ 'use strict';
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
@@ -7309,7 +7426,7 @@ fabric.ElementsParser = {
* @return {String}
*/
toString: function () {
- return this.x + "," + this.y;
+ return this.x + ',' + this.y;
},
/**
@@ -7348,172 +7465,171 @@ fabric.ElementsParser = {
})(typeof exports !== 'undefined' ? exports : this);
-(function(global) {
-
- "use strict";
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Intersection) {
- fabric.warn('fabric.Intersection is already defined');
- return;
- }
-
- /**
- * Intersection class
- * @class fabric.Intersection
- * @memberOf fabric
- * @constructor
- */
- function Intersection(status) {
- this.status = status;
- this.points = [];
- }
-
- fabric.Intersection = Intersection;
-
- fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
- /**
- * Appends a point to intersection
- * @param {fabric.Point} point
- */
- appendPoint: function (point) {
- this.points.push(point);
- },
-
- /**
- * Appends points to intersection
- * @param {Array} points
- */
- appendPoints: function (points) {
- this.points = this.points.concat(points);
- }
- };
-
- /**
- * Checks if one line intersects another
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {fabric.Point} b1
- * @param {fabric.Point} b2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
- var result,
- ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
- ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
- u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
- if (u_b !== 0) {
- var ua = ua_t / u_b,
- ub = ub_t / u_b;
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- result = new Intersection("Intersection");
- result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
- }
- else {
- result = new Intersection();
- }
- }
- else {
- if (ua_t === 0 || ub_t === 0) {
- result = new Intersection("Coincident");
- }
- else {
- result = new Intersection("Parallel");
- }
- }
- return result;
- };
-
- /**
- * Checks if line intersects polygon
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {Array} points
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
- var result = new Intersection(),
- length = points.length;
-
- for (var i = 0; i < length; i++) {
- var b1 = points[i],
- b2 = points[(i+1) % length],
- inter = Intersection.intersectLineLine(a1, a2, b1, b2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects another polygon
- * @static
- * @param {Array} points1
- * @param {Array} points2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
- var result = new Intersection(),
- length = points1.length;
-
- for (var i = 0; i < length; i++) {
- var a1 = points1[i],
- a2 = points1[(i+1) % length],
- inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects rectangle
- * @static
- * @param {Array} points
- * @param {Number} r1
- * @param {Number} r2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
- var min = r1.min(r2),
- max = r1.max(r2),
- topRight = new fabric.Point(max.x, min.y),
- bottomLeft = new fabric.Point(min.x, max.y),
- inter1 = Intersection.intersectLinePolygon(min, topRight, points),
- inter2 = Intersection.intersectLinePolygon(topRight, max, points),
- inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
- inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
- result = new Intersection();
-
- result.appendPoints(inter1.points);
- result.appendPoints(inter2.points);
- result.appendPoints(inter3.points);
- result.appendPoints(inter4.points);
-
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
+(function(global) {
+
+ 'use strict';
+
+ /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
+ var fabric = global.fabric || (global.fabric = { });
+
+ if (fabric.Intersection) {
+ fabric.warn('fabric.Intersection is already defined');
+ return;
+ }
+
+ /**
+ * Intersection class
+ * @class fabric.Intersection
+ * @memberOf fabric
+ * @constructor
+ */
+ function Intersection(status) {
+ this.status = status;
+ this.points = [];
+ }
+
+ fabric.Intersection = Intersection;
+
+ fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
+
+ /**
+ * Appends a point to intersection
+ * @param {fabric.Point} point
+ */
+ appendPoint: function (point) {
+ this.points.push(point);
+ },
+
+ /**
+ * Appends points to intersection
+ * @param {Array} points
+ */
+ appendPoints: function (points) {
+ this.points = this.points.concat(points);
+ }
+ };
+
+ /**
+ * Checks if one line intersects another
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {fabric.Point} b1
+ * @param {fabric.Point} b2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
+ var result,
+ uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
+ ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
+ uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
+ if (uB !== 0) {
+ var ua = uaT / uB,
+ ub = ubT / uB;
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
+ result = new Intersection('Intersection');
+ result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
+ }
+ else {
+ result = new Intersection();
+ }
+ }
+ else {
+ if (uaT === 0 || ubT === 0) {
+ result = new Intersection('Coincident');
+ }
+ else {
+ result = new Intersection('Parallel');
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Checks if line intersects polygon
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {Array} points
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
+ var result = new Intersection(),
+ length = points.length;
+
+ for (var i = 0; i < length; i++) {
+ var b1 = points[i],
+ b2 = points[(i + 1) % length],
+ inter = Intersection.intersectLineLine(a1, a2, b1, b2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects another polygon
+ * @static
+ * @param {Array} points1
+ * @param {Array} points2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
+ var result = new Intersection(),
+ length = points1.length;
+
+ for (var i = 0; i < length; i++) {
+ var a1 = points1[i],
+ a2 = points1[(i + 1) % length],
+ inter = Intersection.intersectLinePolygon(a1, a2, points2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects rectangle
+ * @static
+ * @param {Array} points
+ * @param {Number} r1
+ * @param {Number} r2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
+ var min = r1.min(r2),
+ max = r1.max(r2),
+ topRight = new fabric.Point(max.x, min.y),
+ bottomLeft = new fabric.Point(min.x, max.y),
+ inter1 = Intersection.intersectLinePolygon(min, topRight, points),
+ inter2 = Intersection.intersectLinePolygon(topRight, max, points),
+ inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
+ inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
+ result = new Intersection();
+
+ result.appendPoints(inter1.points);
+ result.appendPoints(inter2.points);
+ result.appendPoints(inter3.points);
+ result.appendPoints(inter4.points);
+
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -7556,6 +7672,11 @@ fabric.ElementsParser = {
color = Color.colorNameMap[color];
}
+ if (color === 'transparent') {
+ this.setSource([255,255,255,0]);
+ return;
+ }
+
source = Color.sourceFromHex(color);
if (!source) {
@@ -7674,15 +7795,15 @@ fabric.ElementsParser = {
* @return {String} ex: FF5555
*/
toHex: function() {
- var source = this.getSource();
+ var source = this.getSource(), r, g, b;
- var r = source[0].toString(16);
+ r = source[0].toString(16);
r = (r.length === 1) ? ('0' + r) : r;
- var g = source[1].toString(16);
+ g = source[1].toString(16);
g = (g.length === 1) ? ('0' + g) : g;
- var b = source[2].toString(16);
+ b = source[2].toString(16);
b = (b.length === 1) ? ('0' + b) : b;
return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
@@ -7769,7 +7890,7 @@ fabric.ElementsParser = {
* @field
* @memberOf fabric.Color
*/
- fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
+ fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
/**
* Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
@@ -7795,23 +7916,23 @@ fabric.ElementsParser = {
* @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
*/
fabric.Color.colorNameMap = {
- 'aqua': '#00FFFF',
- 'black': '#000000',
- 'blue': '#0000FF',
- 'fuchsia': '#FF00FF',
- 'gray': '#808080',
- 'green': '#008000',
- 'lime': '#00FF00',
- 'maroon': '#800000',
- 'navy': '#000080',
- 'olive': '#808000',
- 'orange': '#FFA500',
- 'purple': '#800080',
- 'red': '#FF0000',
- 'silver': '#C0C0C0',
- 'teal': '#008080',
- 'white': '#FFFFFF',
- 'yellow': '#FFFF00'
+ aqua: '#00FFFF',
+ black: '#000000',
+ blue: '#0000FF',
+ fuchsia: '#FF00FF',
+ gray: '#808080',
+ green: '#008000',
+ lime: '#00FF00',
+ maroon: '#800000',
+ navy: '#000080',
+ olive: '#808000',
+ orange: '#FFA500',
+ purple: '#800080',
+ red: '#FF0000',
+ silver: '#C0C0C0',
+ teal: '#008080',
+ white: '#FFFFFF',
+ yellow: '#FFFF00'
};
/**
@@ -7822,11 +7943,21 @@ fabric.ElementsParser = {
* @return {Number}
*/
function hue2rgb(p, q, t){
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ if (t < 0) {
+ t += 1;
+ }
+ if (t > 1) {
+ t -= 1;
+ }
+ if (t < 1/6) {
+ return p + (q - p) * 6 * t;
+ }
+ if (t < 1/2) {
+ return q;
+ }
+ if (t < 2/3) {
+ return p + (q - p) * (2/3 - t) * 6;
+ }
return p;
}
@@ -7903,8 +8034,8 @@ fabric.ElementsParser = {
r = g = b = l;
}
else {
- var q = l <= 0.5 ? l * (s + 1) : l + s - l * s;
- var p = l * 2 - q;
+ var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
+ p = l * 2 - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
@@ -7994,7 +8125,7 @@ fabric.ElementsParser = {
if (style) {
var keyValuePairs = style.split(/\s*;\s*/);
- if (keyValuePairs[keyValuePairs.length-1] === '') {
+ if (keyValuePairs[keyValuePairs.length - 1] === '') {
keyValuePairs.pop();
}
@@ -8097,7 +8228,11 @@ fabric.ElementsParser = {
addColorStop: function(colorStop) {
for (var position in colorStop) {
var color = new fabric.Color(colorStop[position]);
- this.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ this.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this;
},
@@ -8461,10 +8596,10 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {String} SVG representation of a pattern
*/
toSVG: function(object) {
- var patternSource = typeof this.source === 'function' ? this.source() : this.source;
- var patternWidth = patternSource.width / object.getWidth();
- var patternHeight = patternSource.height / object.getHeight();
- var patternImgSrc = '';
+ var patternSource = typeof this.source === 'function' ? this.source() : this.source,
+ patternWidth = patternSource.width / object.getWidth(),
+ patternHeight = patternSource.height / object.getHeight(),
+ patternImgSrc = '';
if (patternSource.src) {
patternImgSrc = patternSource.src;
@@ -8493,11 +8628,23 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {CanvasPattern}
*/
toLive: function(ctx) {
- var source = typeof this.source === 'function' ? this.source() : this.source;
+ var source = typeof this.source === 'function'
+ ? this.source()
+ : this.source;
+
+ // if the image failed to load, return, and allow rest to continue loading
+ if (!source) {
+ return '';
+ }
+
// if an image
if (typeof source.src !== 'undefined') {
- if (!source.complete) return '';
- if (source.naturalWidth === 0 || source.naturalHeight === 0) return '';
+ if (!source.complete) {
+ return '';
+ }
+ if (source.naturalWidth === 0 || source.naturalHeight === 0) {
+ return '';
+ }
}
return ctx.createPattern(source, this.repeat);
}
@@ -8506,7 +8653,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -8587,9 +8734,8 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {Object} Shadow object with color, offsetX, offsetY and blur
*/
_parseShadow: function(shadow) {
- var shadowStr = shadow.trim();
-
- var offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
+ var shadowStr = shadow.trim(),
+ offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
return {
@@ -8679,7 +8825,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
(function () {
- "use strict";
+ 'use strict';
if (fabric.StaticCanvas) {
fabric.warn('fabric.StaticCanvas is already defined.');
@@ -8803,7 +8949,14 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @type Boolean
* @default
*/
- allowTouchScrolling: false,
+ allowTouchScrolling: false,
+
+ /**
+ * Indicates whether this canvas will use image smoothing, this is on by default in browsers
+ * @type Boolean
+ * @default
+ */
+ imageSmoothingEnabled: true,
/**
* The transformation (in the format of Canvas transform) which focuses the viewport
@@ -8830,6 +8983,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
this._createLowerCanvas(el);
this._initOptions(options);
+ this._setImageSmoothing();
if (options.overlayImage) {
this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
@@ -8989,6 +9143,20 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
},
+ /**
+ * @private
+ * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
+ */
+ _setImageSmoothing: function(){
+ var ctx = this.getContext();
+
+ ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.mozImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.msImageSmoothingEnabled = this.imageSmoothingEnabled;
+ ctx.oImageSmoothingEnabled = this.imageSmoothingEnabled;
+ },
+
/**
* @private
* @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
@@ -9076,11 +9244,14 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
this[prop] = options[prop];
}
- this.width = parseInt(this.lowerCanvasEl.width, 10) || 0;
- this.height = parseInt(this.lowerCanvasEl.height, 10) || 0;
+ this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
+ this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
if (!this.lowerCanvasEl.style) return;
+ this.lowerCanvasEl.width = this.width;
+ this.lowerCanvasEl.height = this.height;
+
this.lowerCanvasEl.style.width = this.width + 'px';
this.lowerCanvasEl.style.height = this.height + 'px';
},
@@ -9402,8 +9573,8 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
*/
renderAll: function (allOnTop) {
- var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'];
- var activeGroup = this.getActiveGroup();
+ var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
+ activeGroup = this.getActiveGroup();
if (this.contextTop && this.selection && !this._groupSelector) {
this.clearContext(this.contextTop);
@@ -9444,12 +9615,21 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @param {fabric.Group} activeGroup
*/
_renderObjects: function(ctx, activeGroup) {
- for (var i = 0, length = this._objects.length; i < length; ++i) {
- if (!activeGroup ||
- (activeGroup && this._objects[i] && !activeGroup.contains(this._objects[i]))) {
+ var i, length;
+
+ // fast path
+ if (!activeGroup) {
+ for (i = 0, length = this._objects.length; i < length; ++i) {
this._draw(ctx, this._objects[i]);
}
}
+ else {
+ for (i = 0, length = this._objects.length; i < length; ++i) {
+ if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
+ this._draw(ctx, this._objects[i]);
+ }
+ }
+ }
},
/**
@@ -9538,9 +9718,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
activeGroup.render(ctx);
}
- if (this.overlayImage) {
- ctx.drawImage(this.overlayImage, this.overlayImageLeft, this.overlayImageTop);
- }
+ this._renderOverlay(ctx);
this.fire('after:render');
@@ -9657,11 +9835,20 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
fabric.util.populateWithProperties(this, data, propertiesToInclude);
if (activeGroup) {
- this.setActiveGroup(new fabric.Group(activeGroup.getObjects()));
+ this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
+ originX: 'center',
+ originY: 'center'
+ }));
activeGroup.forEachObject(function(o) {
o.set('active', true);
});
+
+ if (this._currentTransform) {
+ this._currentTransform.target = this.getActiveGroup();
+ }
}
+
+
return data;
},
@@ -9798,7 +9985,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
'width="', (options.viewBox ? options.viewBox.width : this.width), '" ',
'height="', (options.viewBox ? options.viewBox.height : this.height), '" ',
(this.backgroundColor && !this.backgroundColor.toLive
- ? 'style="background-color: ' + this.backgroundColor +'" '
+ ? 'style="background-color: ' + this.backgroundColor + '" '
: null),
(options.viewBox
? 'viewBox="' +
@@ -9930,7 +10117,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
newIdx = idx;
// traverse down the stack looking for the nearest intersecting object
- for (var i=idx-1; i>=0; --i) {
+ for (var i = idx - 1; i >= 0; --i) {
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
object.isContainedWithinObject(this._objects[i]) ||
@@ -9960,7 +10147,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
var idx = this._objects.indexOf(object);
// if object is not on top of stack (last item in an array)
- if (idx !== this._objects.length-1) {
+ if (idx !== this._objects.length - 1) {
var newIdx = this._findNewUpperIndex(object, idx, intersecting);
removeFromArray(this._objects, object);
@@ -9993,7 +10180,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
}
}
else {
- newIdx = idx+1;
+ newIdx = idx + 1;
}
return newIdx;
@@ -10028,7 +10215,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {String} string representation of an instance
*/
toString: function () {
- return '#';
}
});
@@ -10314,32 +10501,32 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
*/
_render: function() {
var ctx = this.canvas.contextTop;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
ctx.beginPath();
- var p1 = this._points[0];
- var p2 = this._points[1];
+ var p1 = this._points[0],
+ p2 = this._points[1];
//if we only have 2 points in the path and they are the same
//it means that the user only clicked the canvas without moving the mouse
//then we should be drawing a dot. A path isn't drawn between two identical dots
//that's why we set them apart a bit
if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
- p1.x -= 0.5;
- p2.x += 0.5;
+ p1.x -= 0.5;
+ p2.x += 0.5;
}
ctx.moveTo(p1.x, p1.y);
for (var i = 1, len = this._points.length; i < len; i++) {
- // we pick the point between pi+1 & pi+2 as the
+ // we pick the point between pi + 1 & pi + 2 as the
// end point and p1 as our control point.
var midPoint = p1.midPointFrom(p2);
ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = this._points[i];
- p2 = this._points[i+1];
+ p2 = this._points[i + 1];
}
// Draw last line as a straight line while
// we wait for the next point to be able to calculate
@@ -10365,7 +10552,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @param {Array} points
* @return {Object} object with minx, miny, maxx, maxy
*/
- getPathBoundingBox: function(points) {
+ getPathBoundingBox: function(points) {
var xBounds = [],
yBounds = [],
p1 = points[0],
@@ -10381,19 +10568,19 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
yBounds.push(midPoint.y);
p1 = points[i];
- p2 = points[i+1];
+ p2 = points[i + 1];
startPoint = midPoint;
- } // end for
+ }
- xBounds.push(p1.x);
- yBounds.push(p1.y);
+ xBounds.push(p1.x);
+ yBounds.push(p1.y);
- return {
- minx: utilMin(xBounds),
- miny: utilMin(yBounds),
- maxx: utilMax(xBounds),
- maxy: utilMax(yBounds)
- };
+ return {
+ minx: utilMin(xBounds),
+ miny: utilMin(yBounds),
+ maxx: utilMax(xBounds),
+ maxy: utilMax(yBounds)
+ };
},
/**
@@ -10402,9 +10589,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @return {String} SVG path
*/
convertPointsToSVGPath: function(points, minX, maxX, minY) {
- var path = [];
- var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY);
- var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
+ var path = [],
+ p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
+ p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
for (var i = 1, len = points.length; i < len; i++) {
@@ -10414,8 +10601,8 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
// start point is p(i-1) value.
path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
- if ((i+1) < points.length) {
- p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY);
+ if ((i + 1) < points.length) {
+ p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
}
}
path.push('L ', p1.x, ' ', p1.y, ' ');
@@ -10454,7 +10641,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
ctx.closePath();
var pathData = this._getSVGPathData().join('');
- if (pathData === "M 0 0 Q 0 0 0 0 L 0 0") {
+ if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
@@ -10464,8 +10651,8 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
}
// set path origin coordinates based on our bounding box
- var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2;
- var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2;
+ var originLeft = this.box.minx + (this.box.maxx - this.box.minx) / 2,
+ originTop = this.box.miny + (this.box.maxy - this.box.miny) / 2;
this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
@@ -10518,10 +10705,10 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @param {Object} pointer
*/
drawDot: function(pointer) {
- var point = this.addPoint(pointer);
- var ctx = this.canvas.contextTop;
+ var point = this.addPoint(pointer),
+ ctx = this.canvas.contextTop;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
@@ -10562,15 +10749,15 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
var circles = [ ];
for (var i = 0, len = this.points.length; i < len; i++) {
- var point = this.points[i];
- var circle = new fabric.Circle({
- radius: this.points[i].radius,
- left: point.x,
- top: point.y,
- originX: 'center',
- originY: 'center',
- fill: this.points[i].fill
- });
+ var point = this.points[i],
+ circle = new fabric.Circle({
+ radius: point.radius,
+ left: point.x,
+ top: point.y,
+ originX: 'center',
+ originY: 'center',
+ fill: point.fill
+ });
this.shadow && circle.setShadow(this.shadow);
@@ -10593,12 +10780,12 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @return {fabric.Point} Just added pointer point
*/
addPoint: function(pointer) {
- var pointerPoint = new fabric.Point(pointer.x, pointer.y);
+ var pointerPoint = new fabric.Point(pointer.x, pointer.y),
- var circleRadius = fabric.util.getRandomInt(
- Math.max(0, this.width - 20), this.width + 20) / 2;
+ circleRadius = fabric.util.getRandomInt(
+ Math.max(0, this.width - 20), this.width + 20) / 2,
- var circleColor = new fabric.Color(this.color)
+ circleColor = new fabric.Color(this.color)
.setAlpha(fabric.util.getRandomInt(0, 100) / 100)
.toRgba();
@@ -10763,7 +10950,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
var ctx = this.canvas.contextTop;
ctx.fillStyle = this.color;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
@@ -10903,6 +11090,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @fires mouse:down
* @fires mouse:move
* @fires mouse:up
+ * @fires mouse:over
+ * @fires mouse:out
*
*/
fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
@@ -11077,10 +11266,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var t = this._currentTransform;
t.target.set({
- 'scaleX': t.original.scaleX,
- 'scaleY': t.original.scaleY,
- 'left': t.original.left,
- 'top': t.original.top
+ scaleX: t.original.scaleX,
+ scaleY: t.original.scaleY,
+ left: t.original.left,
+ top: t.original.top
});
if (this._shouldCenterTransform(e, t.target)) {
@@ -11127,7 +11316,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
- return (target.containsPoint(xy) || target._findTargetCorner(e, this._offset));
+ return (target.containsPoint(xy) || target._findTargetCorner(pointer));
},
/**
@@ -11137,14 +11326,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var activeGroup = this.getActiveGroup(),
x = pointer.x,
y = pointer.y,
+ isObjectInGroup = (
+ activeGroup &&
+ object.type !== 'group' &&
+ activeGroup.contains(object)),
lt;
- var isObjectInGroup = (
- activeGroup &&
- object.type !== 'group' &&
- activeGroup.contains(object)
- );
-
if (isObjectInGroup) {
lt = new fabric.Point(activeGroup.left, activeGroup.top);
lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
@@ -11279,11 +11466,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_setupCurrentTransform: function (e, target) {
if (!target) return;
- var corner = target._findTargetCorner(e, this._offset),
- pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
- fabric.util.invertTransform(this.viewportTransform)
- ),
+ var pointer = this.getPointer(e),
+ corner = target._findTargetCorner(this.getPointer(e, true)),
action = this._getActionFromCorner(target, corner),
origin = this._getOriginFromCorner(target, corner);
@@ -11345,7 +11529,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_scaleObject: function (x, y, by) {
var t = this._currentTransform,
- offset = this._offset,
target = t.target,
lockScalingX = target.get('lockScalingX'),
lockScalingY = target.get('lockScalingY');
@@ -11353,8 +11536,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (lockScalingX && lockScalingY) return;
// Get the constraint point
- var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
- var localMouse = target.toLocalPoint(new fabric.Point(x - offset.left, y - offset.top), t.originX, t.originY);
+ var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
+ localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
this._setLocalMouse(localMouse, t);
@@ -11401,9 +11584,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_scaleObjectEqually: function(localMouse, target, transform) {
- var dist = localMouse.y + localMouse.x;
-
- var lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
+ var dist = localMouse.y + localMouse.x,
+ lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
(target.width + (target.strokeWidth)) * transform.original.scaleX;
// We use transform.scaleX/Y instead of target.scaleX/Y
@@ -11500,13 +11682,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_rotateObject: function (x, y) {
- var t = this._currentTransform,
- o = this._offset;
+ var t = this._currentTransform;
if (t.target.get('lockRotation')) return;
- var lastAngle = atan2(t.ey - t.top - o.top, t.ex - t.left - o.left),
- curAngle = atan2(y - t.top - o.top, x - t.left - o.left),
+ var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
+ curAngle = atan2(y - t.top, x - t.left),
angle = radiansToDegrees(curAngle - lastAngle + t.theta);
// normalize angle to positive value
@@ -11559,15 +11740,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// selection border
if (this.selectionDashArray.length > 1) {
- var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft);
- var py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
+ var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft),
+ py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, px, py, px+aleft, py, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py+atop-1, px+aleft, py+atop-1, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py, px, py+atop, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px+aleft-1, py, px+aleft-1, py+atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
ctx.closePath();
ctx.stroke();
@@ -11591,7 +11772,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.lastRenderedObjectWithControlsAboveOverlay &&
this.lastRenderedObjectWithControlsAboveOverlay.visible &&
this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
- this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e, this._offset));
+ this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
},
/**
@@ -11612,7 +11793,51 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return activeGroup;
}
- return this._searchPossibleTargets(e);
+ var target = this._searchPossibleTargets(e);
+ this._fireOverOutEvents(target);
+ return target;
+ },
+
+ /**
+ * @private
+ */
+ _fireOverOutEvents: function(target) {
+ if (target) {
+ if (this._hoveredTarget !== target) {
+ this.fire('mouse:over', { target: target });
+ target.fire('mouseover');
+ if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ }
+ this._hoveredTarget = target;
+ }
+ }
+ else if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ this._hoveredTarget = null;
+ }
+ },
+
+ /**
+ * @private
+ */
+ _checkTarget: function(e, obj, pointer) {
+ if (obj &&
+ obj.visible &&
+ obj.evented &&
+ this.containsPoint(e, obj)){
+ if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
+ var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
+ if (!isTransparent) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
},
/**
@@ -11621,33 +11846,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_searchPossibleTargets: function(e) {
// Cache all targets where their bounding box contains point.
- var possibleTargets = [],
- target,
+ var target,
pointer = this.getPointer(e, true);
- for (var i = this._objects.length; i--; ) {
- if (this._objects[i] &&
- this._objects[i].visible &&
- this._objects[i].evented &&
- this.containsPoint(e, this._objects[i])) {
+ var i = this._objects.length;
- if (this.perPixelTargetFind || this._objects[i].perPixelTargetFind) {
- possibleTargets[possibleTargets.length] = this._objects[i];
- }
- else {
- target = this._objects[i];
- this.relatedTarget = target;
- break;
- }
- }
- }
-
- for (var j = 0, len = possibleTargets.length; j < len; j++) {
- pointer = this.getPointer(e, true);
- var isTransparent = this.isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
- if (!isTransparent) {
- target = possibleTargets[j];
- this.relatedTarget = target;
+ while (i--) {
+ if (this._checkTarget(e, this._objects[i], pointer)){
+ this.relatedTarget = this._objects[i];
+ target = this._objects[i];
break;
}
}
@@ -11664,7 +11871,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (!upperCanvasEl) {
upperCanvasEl = this.upperCanvasEl;
}
- var pointer = getPointer(e, upperCanvasEl);
+ var pointer = getPointer(e, upperCanvasEl),
+ bounds = upperCanvasEl.getBoundingClientRect(),
+ cssScale;
+
+ pointer.x = pointer.x - this._offset.left;
+ pointer.y = pointer.y - this._offset.top;
if (!ignoreZoom) {
pointer = fabric.util.transformPoint(
pointer,
@@ -11672,9 +11884,19 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
);
}
+ if (bounds.width === 0 || bounds.height === 0) {
+ // If bounds are not available (i.e. not visible), do not apply scale.
+ cssScale = { width: 1, height: 1 };
+ }
+ else {
+ cssScale = {
+ width: upperCanvasEl.width / bounds.width,
+ height: upperCanvasEl.height / bounds.height
+ };
+ }
return {
- x: pointer.x - this._offset.left,
- y: pointer.y - this._offset.top
+ x: pointer.x * cssScale.width,
+ y: pointer.y * cssScale.height
};
},
@@ -11985,32 +12207,36 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
(function(){
- var cursorMap = [
- 'n-resize',
- 'ne-resize',
- 'e-resize',
- 'se-resize',
- 's-resize',
- 'sw-resize',
- 'w-resize',
- 'nw-resize'
- ],
- cursorOffset = {
- 'mt': 0, // n
- 'tr': 1, // ne
- 'mr': 2, // e
- 'br': 3, // se
- 'mb': 4, // s
- 'bl': 5, // sw
- 'ml': 6, // w
- 'tl': 7 // nw
+ var cursorOffset = {
+ mt: 0, // n
+ tr: 1, // ne
+ mr: 2, // e
+ br: 3, // se
+ mb: 4, // s
+ bl: 5, // sw
+ ml: 6, // w
+ tl: 7 // nw
},
addListener = fabric.util.addListener,
- removeListener = fabric.util.removeListener,
- getPointer = fabric.util.getPointer;
+ removeListener = fabric.util.removeListener;
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
+ /**
+ * Map of cursor style values for each of the object controls
+ * @private
+ */
+ cursorMap: [
+ 'n-resize',
+ 'ne-resize',
+ 'e-resize',
+ 'se-resize',
+ 's-resize',
+ 'sw-resize',
+ 'w-resize',
+ 'nw-resize'
+ ],
+
/**
* Adds mouse listeners to canvas
* @private
@@ -12126,14 +12352,20 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_onMouseDown: function (e) {
this.__onMouseDown(e);
- addListener(fabric.document, 'mouseup', this._onMouseUp);
addListener(fabric.document, 'touchend', this._onMouseUp);
-
- addListener(fabric.document, 'mousemove', this._onMouseMove);
addListener(fabric.document, 'touchmove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchstart') {
+ // Unbind mousedown to prevent double triggers from touch devices
+ removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
+ }
+ else {
+ addListener(fabric.document, 'mouseup', this._onMouseUp);
+ addListener(fabric.document, 'mousemove', this._onMouseMove);
+ }
},
/**
@@ -12151,6 +12383,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchend') {
+ // Wait 400ms before rebinding mousedown to prevent double triggers
+ // from touch devices
+ var _this = this;
+ setTimeout(function() {
+ addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
+ }, 400);
+ }
},
/**
@@ -12249,8 +12490,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_finalizeCurrentTransform: function() {
- var transform = this._currentTransform;
- var target = transform.target;
+ var transform = this._currentTransform,
+ target = transform.target;
if (target._scaling) {
target._scaling = false;
@@ -12392,7 +12633,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.stateful && target.saveState();
// determine if it's a drag or rotate case
- if ((corner = target._findTargetCorner(e, this._offset))) {
+ if ((corner = target._findTargetCorner(this.getPointer(e)))) {
this.onBeforeScaleRotate(target);
}
@@ -12514,11 +12755,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {Event} e Event fired on mousemove
*/
_transformObject: function(e) {
-
var pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
+ fabric.util.getPointer(e, this.upperCanvasEl),
fabric.util.invertTransform(this.viewportTransform)
),
+ pointer = this.getPointer(e),
transform = this._currentTransform;
transform.reset = false,
@@ -12566,7 +12807,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fire: function(eventName, target, e) {
- this.fire('object:' + eventName, { target: target, e: e});
+ this.fire('object:' + eventName, { target: target, e: e });
target.fire(eventName, { e: e });
},
@@ -12623,11 +12864,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return false;
}
else {
- var activeGroup = this.getActiveGroup();
- // only show proper corner when group selection is not active
- var corner = target._findTargetCorner
+ var activeGroup = this.getActiveGroup(),
+ // only show proper corner when group selection is not active
+ corner = target._findTargetCorner
&& (!activeGroup || !activeGroup.contains(target))
- && target._findTargetCorner(e, this._offset);
+ && target._findTargetCorner(this.getPointer(e, true));
if (!corner) {
style.cursor = target.hoverCursor || this.hoverCursor;
@@ -12670,7 +12911,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// normalize n to be from 0 to 7
n %= 8;
- return cursorMap[n];
+ return this.cursorMap[n];
}
});
})();
@@ -12777,13 +13018,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_createGroup: function(target) {
- var objects = this.getObjects();
-
- var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target);
-
- var groupObjects = isActiveLower
- ? [ this._activeObject, target ]
- : [ target, this._activeObject ];
+ var objects = this.getObjects(),
+ isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
+ groupObjects = isActiveLower
+ ? [ this._activeObject, target ]
+ : [ target, this._activeObject ];
return new fabric.Group(groupObjects, {
originX: 'center',
@@ -12935,8 +13174,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.renderAll(true);
- var canvasEl = this.upperCanvasEl || this.lowerCanvasEl;
- var croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
+ var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
+ croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
if (format === 'jpg') {
@@ -12963,9 +13202,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
__getCroppedCanvas: function(canvasEl, cropping) {
var croppedCanvasEl,
- croppedCtx;
-
- var shouldCrop = 'left' in cropping ||
+ croppedCtx,
+ shouldCrop = 'left' in cropping ||
'top' in cropping ||
'width' in cropping ||
'height' in cropping;
@@ -12998,7 +13236,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
ctx = this.contextTop || this.contextContainer;
- this.setWidth(scaledWidth).setHeight(scaledHeight);
+ if (multiplier > 1) {
+ this.setWidth(scaledWidth).setHeight(scaledHeight);
+ }
ctx.scale(multiplier, multiplier);
if (cropping.left) {
@@ -13010,9 +13250,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (cropping.width) {
cropping.width *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.width = scaledWidth;
+ }
if (cropping.height) {
cropping.height *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.height = scaledHeight;
+ }
if (activeGroup) {
// not removing group due to complications with restoring it with correct state afterwords
@@ -13225,8 +13471,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_enlivenObjects: function (objects, callback, reviver) {
var _this = this;
- if (objects.length === 0) {
+ if (!objects || objects.length === 0) {
callback && callback();
+ return;
}
var renderOnAddRemove = this.renderOnAddRemove;
@@ -13324,7 +13571,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
__onTransformGesture: function(e, self) {
- if (this.isDrawingMode || e.touches.length !== 2 || 'gesture' !== self.gesture) {
+ if (this.isDrawingMode || !e.touches || e.touches.length !== 2 || 'gesture' !== self.gesture) {
return;
}
@@ -13335,7 +13582,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._scaleObjectBy(self.scale);
}
- this.fire('touch:gesture', {target: target, e: e, self: self});
+ this.fire('touch:gesture', { target: target, e: e, self: self });
},
/**
@@ -13345,7 +13592,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onDrag: function(e, self) {
- this.fire('touch:drag', {e: e, self: self});
+ this.fire('touch:drag', { e: e, self: self });
},
/**
@@ -13355,7 +13602,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onOrientationChange: function(e, self) {
- this.fire('touch:orientation', {e: e, self: self});
+ this.fire('touch:orientation', { e: e, self: self });
},
/**
@@ -13365,7 +13612,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param self Event proxy object by Event.js
*/
__onShake: function(e, self) {
- this.fire('touch:shake', {e: e, self: self});
+ this.fire('touch:shake', { e: e, self: self });
},
/**
@@ -13376,10 +13623,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_scaleObjectBy: function(s, by) {
var t = this._currentTransform,
- target = t.target;
-
- var lockScalingX = target.get('lockScalingX'),
- lockScalingY = target.get('lockScalingY');
+ target = t.target,
+ lockScalingX = target.get('lockScalingX'),
+ lockScalingY = target.get('lockScalingY');
if (lockScalingX && lockScalingY) return;
@@ -13417,7 +13663,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -14028,6 +14274,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Function that determines clipping of an object (context is passed as a first argument)
+ * Note that context origin is at the object's center point (not left/top corner)
* @type Function
*/
clipTo: null,
@@ -14169,34 +14416,34 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
toObject: function(propertiesToInclude) {
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var object = {
- type: this.type,
- originX: this.originX,
- originY: this.originY,
- left: toFixed(this.left, NUM_FRACTION_DIGITS),
- top: toFixed(this.top, NUM_FRACTION_DIGITS),
- width: toFixed(this.width, NUM_FRACTION_DIGITS),
- height: toFixed(this.height, NUM_FRACTION_DIGITS),
- fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
- stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
- strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
- strokeDashArray: this.strokeDashArray,
- strokeLineCap: this.strokeLineCap,
- strokeLineJoin: this.strokeLineJoin,
- strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
- scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
- scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
- angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
- flipX: this.flipX,
- flipY: this.flipY,
- opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
- shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
- visible: this.visible,
- clipTo: this.clipTo && String(this.clipTo),
- backgroundColor: this.backgroundColor
- };
+ object = {
+ type: this.type,
+ originX: this.originX,
+ originY: this.originY,
+ left: toFixed(this.left, NUM_FRACTION_DIGITS),
+ top: toFixed(this.top, NUM_FRACTION_DIGITS),
+ width: toFixed(this.width, NUM_FRACTION_DIGITS),
+ height: toFixed(this.height, NUM_FRACTION_DIGITS),
+ fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
+ stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
+ strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
+ strokeDashArray: this.strokeDashArray,
+ strokeLineCap: this.strokeLineCap,
+ strokeLineJoin: this.strokeLineJoin,
+ strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
+ scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
+ scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
+ angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
+ flipX: this.flipX,
+ flipY: this.flipY,
+ opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
+ shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
+ visible: this.visible,
+ clipTo: this.clipTo && String(this.clipTo),
+ backgroundColor: this.backgroundColor
+ };
if (!this.includeDefaultValues) {
object = this._removeDefaultValues(object);
@@ -14222,8 +14469,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Object} object
*/
_removeDefaultValues: function(object) {
- var prototype = fabric.util.getKlass(object.type).prototype;
- var stateProperties = prototype.stateProperties;
+ var prototype = fabric.util.getKlass(object.type).prototype,
+ stateProperties = prototype.stateProperties;
stateProperties.forEach(function(prop) {
if (object[prop] === prototype[prop]) {
@@ -14239,7 +14486,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {String}
*/
toString: function() {
- return "#";
+ return '#';
},
/**
@@ -14251,6 +14498,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this[property];
},
+ /**
+ * @private
+ */
+ _setObject: function(obj) {
+ for (var prop in obj) {
+ this._set(prop, obj[prop]);
+ }
+ },
+
/**
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
* @param {String|Object} key Property name or object (if object, iterate over the object properties)
@@ -14260,9 +14516,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
set: function(key, value) {
if (typeof key === 'object') {
- for (var prop in key) {
- this._set(prop, key[prop]);
- }
+ this._setObject(key);
}
else {
if (typeof value === 'function' && key !== 'clipTo') {
@@ -14332,6 +14586,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this;
},
+ /**
+ * Retrieves viewportTransform from Object's canvas if possible
+ * @method getViewportTransform
+ * @memberOf fabric.Object.prototype
+ * @return {Boolean} flipY value // TODO
+ */
+ getViewportTransform: function() {
+ if (this.canvas && this.getViewportTransform())
+ return this.canvas.viewportTransform;
+ return [1, 0, 0, 1, 0, 0];
+ },
+
/**
* Renders an object on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -14343,6 +14609,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
ctx.save();
+ //setup fill rule for current object
+ this._setupFillRule(ctx);
+
this._transform(ctx, noTransform);
this._setStrokeStyles(ctx);
this._setFillStyles(ctx);
@@ -14358,6 +14627,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._render(ctx, noTransform);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
+ this._restoreFillRule(ctx);
+
ctx.restore();
this._renderControls(ctx, noTransform);
@@ -14365,7 +14636,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_transform: function(ctx, noTransform) {
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
@@ -14403,7 +14674,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Boolean} [noTransform] When true, context is not transformed
*/
_renderControls: function(ctx, noTransform) {
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
if (this.active && !noTransform) {
@@ -14444,6 +14715,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_removeShadow: function(ctx) {
+ if (!this.shadow) return;
+
ctx.shadowColor = '';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
},
@@ -14461,7 +14734,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
-this.width / 2 + this.fill.offsetX || 0,
-this.height / 2 + this.fill.offsetY || 0);
}
- ctx.fill();
+ if (this.fillRule === 'destination-over') {
+ ctx.fill('evenodd');
+ }
+ else {
+ ctx.fill();
+ }
if (this.fill.toLive) {
ctx.restore();
}
@@ -14660,7 +14938,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
setGradient: function(property, options) {
options || (options = { });
- var gradient = {colorStops: []};
+ var gradient = { colorStops: [] };
gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
gradient.coords = {
@@ -14677,7 +14955,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
for (var position in options.colorStops) {
var color = new fabric.Color(options.colorStops[position]);
- gradient.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ gradient.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this.set(property, fabric.Gradient.forObject(this, gradient));
@@ -14735,7 +15017,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Sets "color" of an instance (alias of `set('fill', …)`)
* @param {String} color Color value
- * @return {fabric.Text} thisArg
+ * @return {fabric.Object} thisArg
* @chainable
*/
setColor: function(color) {
@@ -14743,6 +15025,28 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return this;
},
+ /**
+ * Sets "angle" of an instance
+ * @param {Number} angle Angle value
+ * @return {fabric.Object} thisArg
+ * @chainable
+ */
+ setAngle: function(angle) {
+ var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
+
+ if (shouldCenterOrigin) {
+ this._setOriginToCenter();
+ }
+
+ this.set('angle', angle);
+
+ if (shouldCenterOrigin) {
+ this._resetOrigin();
+ }
+
+ return this;
+ },
+
/**
* Centers object horizontally on canvas to which it was added last.
* You might need to call `setCoords` on an object after centering, to update controls area.
@@ -14782,7 +15086,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @chainable
*/
remove: function() {
- return this.canvas.remove(this);
+ this.canvas.remove(this);
+ return this;
},
/**
@@ -14798,6 +15103,28 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
x: pointer.x - objectLeftTop.x,
y: pointer.y - objectLeftTop.y
};
+ },
+
+ /**
+ * Sets canvas globalCompositeOperation for specific object
+ * custom composition operation for the particular object can be specifed using fillRule property
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _setupFillRule: function (ctx) {
+ if (this.fillRule) {
+ this._prevFillRule = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = this.fillRule;
+ }
+ },
+
+ /**
+ * Restores previously saved canvas globalCompositeOperation after obeject rendering
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _restoreFillRule: function (ctx) {
+ if (this.fillRule && this._prevFillRule) {
+ ctx.globalCompositeOperation = this._prevFillRule;
+ }
}
});
@@ -14851,17 +15178,17 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
cy = point.y,
strokeWidth = this.stroke ? this.strokeWidth : 0;
- if (originX === "left") {
+ if (originX === 'left') {
cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -14882,16 +15209,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
strokeWidth = this.stroke ? this.strokeWidth : 0;
// Get the point coordinates
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -14941,20 +15268,20 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
x, y;
if (originX && originY) {
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
else {
x = center.x;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
else {
@@ -14987,8 +15314,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {void}
*/
setPositionByOrigin: function(pos, originX, originY) {
- var center = this.translateToCenterPoint(pos, originX, originY);
- var position = this.translateToOriginPoint(center, this.originX, this.originY);
+ var center = this.translateToCenterPoint(pos, originX, originY),
+ position = this.translateToOriginPoint(center, this.originX, this.originY);
this.set('left', position.x);
this.set('top', position.y);
@@ -14998,13 +15325,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {String} to One of 'left', 'center', 'right'
*/
adjustPosition: function(to) {
- var angle = degreesToRadians(this.angle);
- var hypotHalf = this.getWidth() / 2;
- var xHalf = Math.cos(angle) * hypotHalf;
- var yHalf = Math.sin(angle) * hypotHalf;
- var hypotFull = this.getWidth();
- var xFull = Math.cos(angle) * hypotFull;
- var yFull = Math.sin(angle) * hypotFull;
+ var angle = degreesToRadians(this.angle),
+ hypotHalf = this.getWidth() / 2,
+ xHalf = Math.cos(angle) * hypotHalf,
+ yHalf = Math.sin(angle) * hypotHalf,
+ hypotFull = this.getWidth(),
+ xFull = Math.cos(angle) * hypotFull,
+ yFull = Math.sin(angle) * hypotFull;
if (this.originX === 'center' && to === 'left' ||
this.originX === 'right' && to === 'center') {
@@ -15033,6 +15360,45 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.originX = to;
},
+ /**
+ * @private
+ * Sets the origin/position of the object to it's center point
+ * @return {void}
+ */
+ _setOriginToCenter: function() {
+ this._originalOriginX = this.originX;
+ this._originalOriginY = this.originY;
+
+ var center = this.getCenterPoint();
+
+ this.originX = 'center';
+ this.originY = 'center';
+
+ this.left = center.x;
+ this.top = center.y;
+ },
+
+ /**
+ * @private
+ * Resets the origin/position of the object to it's original origin
+ * @return {void}
+ */
+ _resetOrigin: function() {
+ var originPoint = this.translateToOriginPoint(
+ this.getCenterPoint(),
+ this._originalOriginX,
+ this._originalOriginY);
+
+ this.originX = this._originalOriginX;
+ this.originY = this._originalOriginY;
+
+ this.left = originPoint.x;
+ this.top = originPoint.y;
+
+ this._originalOriginX = null;
+ this._originalOriginY = null;
+ },
+
/**
* @private
*/
@@ -15068,13 +15434,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
- br = new fabric.Point(oCoords.br.x, oCoords.br.y);
-
- var intersection = fabric.Intersection.intersectPolygonRectangle(
- [tl, tr, br, bl],
- pointTL,
- pointBR
- );
+ br = new fabric.Point(oCoords.br.x, oCoords.br.y),
+ intersection = fabric.Intersection.intersectPolygonRectangle(
+ [tl, tr, br, bl],
+ pointTL,
+ pointBR
+ );
return intersection.status === 'Intersection';
},
@@ -15094,12 +15459,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
};
}
var thisCoords = getCoords(this.oCoords),
- otherCoords = getCoords(other.oCoords);
-
- var intersection = fabric.Intersection.intersectPolygonPolygon(
- [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
- [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
- );
+ otherCoords = getCoords(other.oCoords),
+ intersection = fabric.Intersection.intersectPolygonPolygon(
+ [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
+ [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
+ );
return intersection.status === 'Intersection';
},
@@ -15127,10 +15491,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var boundingRect = this.getBoundingRect();
return (
- boundingRect.left > pointTL.x &&
- boundingRect.left + boundingRect.width < pointBR.x &&
- boundingRect.top > pointTL.y &&
- boundingRect.top + boundingRect.height < pointBR.y
+ boundingRect.left >= pointTL.x &&
+ boundingRect.left + boundingRect.width <= pointBR.x &&
+ boundingRect.top >= pointTL.y &&
+ boundingRect.top + boundingRect.height <= pointBR.y
);
},
@@ -15204,7 +15568,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
else {
b1 = 0;
b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
- a1 = point.y- b1 * point.x;
+ a1 = point.y - b1 * point.x;
a2 = iLine.o.y - b2 * iLine.o.x;
xi = - (a1 - a2) / (b1 - b2);
@@ -15247,15 +15611,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
getBoundingRect: function() {
this.oCoords || this.setCoords();
- var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x];
- var minX = fabric.util.array.min(xCoords);
- var maxX = fabric.util.array.max(xCoords);
- var width = Math.abs(minX - maxX);
+ var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
+ minX = fabric.util.array.min(xCoords),
+ maxX = fabric.util.array.max(xCoords),
+ width = Math.abs(minX - maxX),
- var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y];
- var minY = fabric.util.array.min(yCoords);
- var maxY = fabric.util.array.max(yCoords);
- var height = Math.abs(minY - maxY);
+ yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
+ minY = fabric.util.array.min(yCoords),
+ maxY = fabric.util.array.max(yCoords),
+ height = Math.abs(minY - maxY);
return {
left: minX,
@@ -15289,12 +15653,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_constrainScale: function(value) {
if (Math.abs(value) < this.minScaleLimit) {
- if (value < 0)
+ if (value < 0) {
return -this.minScaleLimit;
- else
+ }
+ else {
return this.minScaleLimit;
+ }
}
-
return value;
},
@@ -15355,7 +15720,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
theta = degreesToRadians(this.angle),
vpt;
// TODO: ideally we should never setCoords an object which lacks a canvas
- vpt = this.canvas ? this.canvas.viewportTransform : [1, 0, 0, 1, 0, 0];
+ vpt = this.getViewportTransform();
var f = function (p) {
return fabric.util.transformPoint(p, vpt);
@@ -15370,13 +15735,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
}
var _hypotenuse = Math.sqrt(
- Math.pow(this.currentWidth / 2, 2) +
- Math.pow(this.currentHeight / 2, 2));
+ Math.pow(this.currentWidth / 2, 2) +
+ Math.pow(this.currentHeight / 2, 2)),
- var _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0);
+ _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
- // offset added for rotate and scale actions
- var offsetX = Math.cos(_angle + theta) * _hypotenuse,
+ // offset added for rotate and scale actions
+ offsetX = Math.cos(_angle + theta) * _hypotenuse,
offsetY = Math.sin(_angle + theta) * _hypotenuse,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
@@ -15533,32 +15898,32 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
getSvgStyles: function() {
var fill = this.fill
- ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
- : 'none';
+ ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
+ : 'none',
- var stroke = this.stroke
- ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
- : 'none';
+ stroke = this.stroke
+ ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
+ : 'none',
- var strokeWidth = this.strokeWidth ? this.strokeWidth : '0';
- var strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '';
- var strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt';
- var strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter';
- var strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4';
- var opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1';
+ strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
+ strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
+ strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
+ strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
+ strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
+ opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
- var visibility = this.visible ? '' : " visibility: hidden;";
- var filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
+ visibility = this.visible ? '' : ' visibility: hidden;',
+ filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
return [
- "stroke: ", stroke, "; ",
- "stroke-width: ", strokeWidth, "; ",
- "stroke-dasharray: ", strokeDashArray, "; ",
- "stroke-linecap: ", strokeLineCap, "; ",
- "stroke-linejoin: ", strokeLineJoin, "; ",
- "stroke-miterlimit: ", strokeMiterLimit, "; ",
- "fill: ", fill, "; ",
- "opacity: ", opacity, ";",
+ 'stroke: ', stroke, '; ',
+ 'stroke-width: ', strokeWidth, '; ',
+ 'stroke-dasharray: ', strokeDashArray, '; ',
+ 'stroke-linecap: ', strokeLineCap, '; ',
+ 'stroke-linejoin: ', strokeLineJoin, '; ',
+ 'stroke-miterlimit: ', strokeMiterLimit, '; ',
+ 'fill: ', fill, '; ',
+ 'opacity: ', opacity, ';',
filter,
visibility
].join('');
@@ -15569,34 +15934,37 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransform: function() {
- var toFixed = fabric.util.toFixed;
- var angle = this.getAngle();
- var center = this.getCenterPoint();
+ var toFixed = fabric.util.toFixed,
+ angle = this.getAngle(),
+ center = this.getCenterPoint(),
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var translatePart = "translate(" +
+ translatePart = 'translate(' +
toFixed(center.x, NUM_FRACTION_DIGITS) +
- " " +
+ ' ' +
toFixed(center.y, NUM_FRACTION_DIGITS) +
- ")";
+ ')',
- var anglePart = angle !== 0
- ? (" rotate(" + toFixed(angle, NUM_FRACTION_DIGITS) + ")")
- : '';
+ anglePart = angle !== 0
+ ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
+ : '',
- var scalePart = (this.scaleX === 1 && this.scaleY === 1)
- ? '' :
- (" scale(" +
- toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
- " " +
- toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
- ")");
+ scalePart = (this.scaleX === 1 && this.scaleY === 1)
+ ? '' :
+ (' scale(' +
+ toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
+ ' ' +
+ toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
+ ')'),
- var flipXPart = this.flipX ? "matrix(-1 0 0 1 0 0) " : "";
- var flipYPart = this.flipY ? "matrix(1 0 0 -1 0 0)" : "";
+ flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
- return [ translatePart, anglePart, scalePart, flipXPart, flipYPart ].join('');
+ flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
+
+ return [
+ translatePart, anglePart, scalePart, flipXPart, flipYPart
+ ].join('');
},
/**
@@ -15669,8 +16037,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(){
- var getPointer = fabric.util.getPointer,
- degreesToRadians = fabric.util.degreesToRadians,
+ var degreesToRadians = fabric.util.degreesToRadians,
isVML = typeof G_vmlCanvasManager !== 'undefined';
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
@@ -15684,15 +16051,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* Determines which corner has been clicked
* @private
- * @param {Event} e Event object
- * @param {Object} offset Canvas offset
+ * @param {Object} pointer The pointer indicating the mouse position
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
*/
- _findTargetCorner: function(e, offset) {
+ _findTargetCorner: function(pointer) {
if (!this.hasControls || !this.active) return false;
- var pointer = this.canvas.getPointer(e, true),
- ex = pointer.x,
+ var ex = pointer.x,
ey = pointer.y,
xPoints,
lines;
@@ -15707,7 +16072,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
continue;
}
- if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
+ if (this.get('lockUniScaling') &&
+ (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
continue;
}
@@ -15727,7 +16093,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
// canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
// canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
- xPoints = this._findCrossPoints({x: ex, y: ey}, lines);
+ xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
if (xPoints !== 0 && xPoints % 2 === 1) {
this.__corner = i;
return i;
@@ -15947,7 +16313,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.lineWidth = 1 / this.borderScaleFactor;
- var vpt = this.canvas.viewportTransform,
+ var vpt = this.getViewportTransform(),
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, true),
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), vpt, true),
w = wh.x,
@@ -15999,7 +16365,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var size = this.cornerSize,
size2 = size / 2,
strokeWidth2 = ~~(this.strokeWidth / 2), // half strokeWidth rounded down
- wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.canvas.viewportTransform, true),
+ wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.getViewportTransform(), true),
width = wh.x,
height = wh.y,
left = -(width / 2),
@@ -16029,7 +16395,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
top - scaleOffset - strokeWidth2 - padding);
// bottom-left
- this._drawControl('tr', ctx, methodName,
+ this._drawControl('bl', ctx, methodName,
left - scaleOffset - strokeWidth2 - padding,
top + height + scaleOffsetSize + strokeWidth2 + padding);
@@ -16051,7 +16417,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
top + height + scaleOffsetSize + strokeWidth2 + padding);
// middle-right
- this._drawControl('mb', ctx, methodName,
+ this._drawControl('mr', ctx, methodName,
left + width + scaleOffsetSize + strokeWidth2 + padding,
top + height/2 - scaleOffset);
@@ -16140,15 +16506,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_getControlsVisibility: function() {
if (!this._controlsVisibility) {
this._controlsVisibility = {
- tl: true,
- tr: true,
- br: true,
- bl: true,
- ml: true,
- mt: true,
- mr: true,
- mb: true,
- mtr: true
+ tl: true,
+ tr: true,
+ br: true,
+ bl: true,
+ ml: true,
+ mt: true,
+ mr: true,
+ mb: true,
+ mtr: true
};
}
return this._controlsVisibility;
@@ -16301,7 +16667,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
for (prop in arguments[0]) {
propsToAnimate.push(prop);
}
- for (var i = 0, len = propsToAnimate.length; i'
- );
+ '"/>');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@@ -17400,7 +17886,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
toFixed = fabric.util.toFixed;
@@ -17425,6 +17911,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'polyline',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points (where each point is an object with x and y)
@@ -17522,7 +18015,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || p1;
+ p2 = this.points[i + 1] || p1;
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
},
@@ -17585,7 +18078,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -17613,6 +18106,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'polygon',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points
@@ -17723,7 +18223,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || this.points[0];
+ p2 = this.points[i + 1] || this.points[0];
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
ctx.closePath();
@@ -17786,26 +18286,25 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- var commandLengths = {
- m: 2,
- l: 2,
- h: 1,
- v: 1,
- c: 6,
- s: 4,
- q: 4,
- t: 2,
- a: 7
- };
-
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
min = fabric.util.array.min,
max = fabric.util.array.max,
extend = fabric.util.object.extend,
_toString = Object.prototype.toString,
- drawArc = fabric.util.drawArc;
+ drawArc = fabric.util.drawArc,
+ commandLengths = {
+ m: 2,
+ l: 2,
+ h: 1,
+ v: 1,
+ c: 6,
+ s: 4,
+ q: 4,
+ t: 2,
+ a: 7
+ };
if (fabric.Path) {
fabric.warn('fabric.Path is already defined');
@@ -17848,6 +18347,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
type: 'path',
+ /**
+ * Array of path points
+ * @type Array
+ * @default
+ */
+ path: null,
+
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
@@ -17911,7 +18417,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this.left = this.width / 2;
}
}
- this.pathOffset = this.pathOffset || this._calculatePathOffset(origLeft, origTop); //Save top-left coords as offset
+ this.pathOffset = this.pathOffset ||
+ // Save top-left coords as offset
+ this._calculatePathOffset(origLeft, origTop);
},
/**
@@ -18067,8 +18575,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
tempX = current[3];
tempY = current[4];
// calculate reflection of previous control points
- controlX = 2*x - controlX;
- controlY = 2*y - controlY;
+ controlX = 2 * x - controlX;
+ controlY = 2 * y - controlY;
ctx.bezierCurveTo(
controlX + l,
controlY + t,
@@ -18129,7 +18637,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
tempX = x + current[1];
tempY = y + current[2];
-
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
@@ -18230,7 +18737,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.save();
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -18271,7 +18778,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
toObject: function(propertiesToInclude) {
var o = extend(this.callSuper('toObject', propertiesToInclude), {
- path: this.path,
+ path: this.path.map(function(item) { return item.slice() }),
pathOffset: this.pathOffset
});
if (this.sourcePath) {
@@ -18343,7 +18850,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
coords = [ ],
currentPath,
parsed,
- re = /(-?\.\d+)|(-?\d+(\.\d+)?)/g,
+ re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
match,
coordsStr;
@@ -18399,14 +18906,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
maxX = max(aX),
maxY = max(aY),
deltaX = maxX - minX,
- deltaY = maxY - minY;
+ deltaY = maxY - minY,
- var o = {
- left: this.left + (minX + deltaX / 2),
- top: this.top + (minY + deltaY / 2),
- width: deltaX,
- height: deltaY
- };
+ o = {
+ left: this.left + (minX + deltaX / 2),
+ top: this.top + (minY + deltaY / 2),
+ width: deltaX,
+ height: deltaY
+ };
return o;
},
@@ -18427,13 +18934,18 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
isLowerCase = true;
}
- var xy = this._getXY(item, isLowerCase, previous);
+ var xy = this._getXY(item, isLowerCase, previous),
+ val;
- var val = parseInt(xy.x, 10);
- if (!isNaN(val)) aX.push(val);
+ val = parseInt(xy.x, 10);
+ if (!isNaN(val)) {
+ aX.push(val);
+ }
val = parseInt(xy.y, 10);
- if (!isNaN(val)) aY.push(val);
+ if (!isNaN(val)) {
+ aY.push(val);
+ }
},
_getXY: function(item, isLowerCase, previous) {
@@ -18445,13 +18957,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
? previous.x + getX(item)
: item[0] === 'V'
? previous.x
- : getX(item);
+ : getX(item),
- var y = isLowerCase
- ? previous.y + getY(item)
- : item[0] === 'H'
- ? previous.y
- : getY(item);
+ y = isLowerCase
+ ? previous.y + getY(item)
+ : item[0] === 'H'
+ ? previous.y
+ : getY(item);
return { x: x, y: y };
}
@@ -18467,9 +18979,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
fabric.Path.fromObject = function(object, callback) {
if (typeof object.path === 'string') {
fabric.loadSVGFromURL(object.path, function (elements) {
- var path = elements[0];
+ var path = elements[0],
+ pathUrl = object.path;
- var pathUrl = object.path;
delete object.path;
fabric.util.object.extend(path, object);
@@ -18520,7 +19032,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -18572,6 +19084,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this.setOptions(options);
+ if (options.widthAttr) {
+ this.scaleX = options.widthAttr / options.width;
+ }
+ if (options.heightAttr) {
+ this.scaleY = options.heightAttr / options.height;
+ }
+
+ this.setCoords();
+
if (options.sourcePath) {
this.setSourcePath(options.sourcePath);
}
@@ -18589,7 +19110,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -18663,13 +19184,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- var objects = this.getObjects();
- var markup = [
- ''
- ];
+ var objects = this.getObjects(),
+ markup = [
+ ''
+ ];
for (var i = 0, len = objects.length; i < len; i++) {
markup.push(objects[i].toSVG(reviver));
@@ -18694,9 +19215,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Boolean} true if all paths are of the same color (`fill`)
*/
isSameColor: function() {
- var firstPathFill = this.getObjects()[0].get('fill');
+ var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
return this.getObjects().every(function(path) {
- return path.get('fill') === firstPathFill;
+ return (path.get('fill') || '').toLowerCase() === firstPathFill;
});
},
@@ -18760,7 +19281,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global){
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -18992,7 +19513,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @private
*/
_renderObject: function(object, ctx) {
- var v = this.canvas.viewportTransform,
+ var v = this.getViewportTransform(),
sxy = fabric.util.transformPoint(
new fabric.Point(this.scaleX, this.scaleY),
v,
@@ -19169,11 +19690,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_setOpacityIfSame: function() {
var objects = this.getObjects(),
- firstValue = objects[0] ? objects[0].get('opacity') : 1;
-
- var isSameOpacity = objects.every(function(o) {
- return o.get('opacity') === firstValue;
- });
+ firstValue = objects[0] ? objects[0].get('opacity') : 1,
+ isSameOpacity = objects.every(function(o) {
+ return o.get('opacity') === firstValue;
+ });
if (isSameOpacity) {
this.opacity = firstValue;
@@ -19183,7 +19703,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* @private
*/
- _calcBounds: function() {
+ _calcBounds: function(onlyWidthHeight) {
var aX = [],
aY = [],
o;
@@ -19197,26 +19717,29 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
}
}
- this.set(this._getBounds(aX, aY));
+ this.set(this._getBounds(aX, aY, onlyWidthHeight));
},
/**
* @private
*/
- _getBounds: function(aX, aY) {
+ _getBounds: function(aX, aY, onlyWidthHeight) {
var ivt;
if (this.canvas) {
- ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
+ ivt = fabric.util.invertTransform(this.getViewportTransform());
}
var minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
- maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt);
+ maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
+ obj = {
+ width: (maxXY.x - minXY.x) || 0,
+ height: (maxXY.y - minXY.y) || 0
+ };
- return {
- width: (maxXY.x - minXY.x) || 0,
- height: (maxXY.y - minXY.y) || 0,
- left: (minXY.x + maxXY.x) / 2 || 0,
- top: (minXY.y + maxXY.y) / 2 || 0,
- };
+ if (!onlyWidthHeight) {
+ obj.left = (minXY.x + maxXY.x) / 2 || 0;
+ obj.top = (minXY.y + maxXY.y) / 2 || 0;
+ }
+ return obj;
},
/* _TO_SVG_START_ */
@@ -19299,7 +19822,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
(function(global) {
- "use strict";
+ 'use strict';
var extend = fabric.util.object.extend;
@@ -19421,17 +19944,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (!this.visible) return;
ctx.save();
- var m = this.transformMatrix;
- var v;
- v = this.canvas.viewportTransform;
-
- var isInPathGroup = this.group && this.group.type === 'path-group';
+ var m = this.transformMatrix,
+ v = this.getViewportTransform(),
+ isInPathGroup = this.group && this.group.type === 'path-group';
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
// this._resetWidthHeight();
if (isInPathGroup) {
- ctx.translate(-this.group.width/2 + this.width/2, -this.group.height/2 + this.height/2);
+ ctx.translate(-this.group.width/2, -this.group.height/2);
}
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -19439,6 +19960,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (!noTransform) {
this.transform(ctx);
}
+ if (isInPathGroup) {
+ ctx.translate(this.width/2, this.height/2);
+ }
ctx.save();
this._setShadow(ctx);
@@ -19482,10 +20006,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this._setStrokeStyles(ctx);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
ctx.closePath();
ctx.restore();
},
@@ -19552,7 +20076,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} Source of an image
*/
getSrc: function() {
- return this.getElement().src || this.getElement()._src;
+ if (this.getElement()) {
+ return this.getElement().src || this.getElement()._src;
+ }
},
/**
@@ -19581,6 +20107,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
applyFilters: function(callback) {
+ if (!this._originalElement) {
+ return;
+ }
+
if (this.filters.length === 0) {
this._element = this._originalElement;
callback && callback();
@@ -19630,13 +20160,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
- ctx.drawImage(
- this._element,
- -this.width / 2,
- -this.height / 2,
- this.width,
- this.height
- );
+ this._element && ctx.drawImage(
+ this._element,
+ -this.width / 2,
+ -this.height / 2,
+ this.width,
+ this.height
+ );
},
/**
@@ -19668,7 +20198,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
options || (options = { });
this.setOptions(options);
this._setWidthHeight(options);
- this._element.crossOrigin = this.crossOrigin;
+ if (this._element && this.crossOrigin) {
+ this._element.crossOrigin = this.crossOrigin;
+ }
},
/**
@@ -19694,11 +20226,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_setWidthHeight: function(options) {
this.width = 'width' in options
? options.width
- : (this.getElement().width || 0);
+ : (this.getElement()
+ ? this.getElement().width || 0
+ : 0);
this.height = 'height' in options
? options.height
- : (this.getElement().height || 0);
+ : (this.getElement()
+ ? this.getElement().height || 0
+ : 0);
},
/**
@@ -19716,7 +20252,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @type String
* @default
*/
- fabric.Image.CSS_CANVAS = "canvas-img";
+ fabric.Image.CSS_CANVAS = 'canvas-img';
/**
* Alias for getSrc
@@ -19805,9 +20341,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_getAngleValueForStraighten: function() {
var angle = this.getAngle() % 360;
if (angle > 0) {
- return Math.round((angle-1)/90) * 90;
+ return Math.round((angle - 1) / 90) * 90;
}
- return Math.round(angle/90) * 90;
+ return Math.round(angle / 90) * 90;
},
/**
@@ -19894,7 +20430,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
fabric.Image.filters = fabric.Image.filters || { };
-
/**
* Root filter class from which all filter classes inherit from
* @class fabric.Image.filters.BaseFilter
@@ -19930,7 +20465,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -19962,11 +20497,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* Constructor
* @memberOf fabric.Image.filters.Brightness.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.brightness=100] Value to brighten the image up (0..255)
+ * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
*/
initialize: function(options) {
options = options || { };
- this.brightness = options.brightness || 100;
+ this.brightness = options.brightness || 0;
},
/**
@@ -20014,7 +20549,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20080,9 +20615,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
options = options || { };
this.opaque = options.opaque;
- this.matrix = options.matrix || [ 0, 0, 0,
+ this.matrix = options.matrix || [
+ 0, 0, 0,
0, 1, 0,
- 0, 0, 0 ];
+ 0, 0, 0
+ ];
var canvasEl = fabric.util.createCanvasElement();
this.tmpCtx = canvasEl.getContext('2d');
@@ -20109,49 +20646,49 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
halfSide = Math.floor(side/2),
src = pixels.data,
sw = pixels.width,
- sh = pixels.height;
+ sh = pixels.height,
- // pad output by the convolution matrix
- var w = sw;
- var h = sh;
- var output = this._createImageData(w, h);
+ // pad output by the convolution matrix
+ w = sw,
+ h = sh,
+ output = this._createImageData(w, h),
- var dst = output.data;
+ dst = output.data,
- // go through the destination image pixels
- var alphaFac = this.opaque ? 1 : 0;
+ // go through the destination image pixels
+ alphaFac = this.opaque ? 1 : 0;
- for (var y=0; y sh || scx < 0 || scx > sw) continue;
- var srcOff = (scy*sw+scx)*4;
- var wt = weights[cy*side+cx];
+ var srcOff = (scy * sw + scx) * 4,
+ wt = weights[cy * side + cx];
r += src[srcOff] * wt;
- g += src[srcOff+1] * wt;
- b += src[srcOff+2] * wt;
- a += src[srcOff+3] * wt;
+ g += src[srcOff + 1] * wt;
+ b += src[srcOff + 2] * wt;
+ a += src[srcOff + 3] * wt;
}
}
dst[dstOff] = r;
- dst[dstOff+1] = g;
- dst[dstOff+2] = b;
- dst[dstOff+3] = a + alphaFac*(255-a);
+ dst[dstOff + 1] = g;
+ dst[dstOff + 2] = b;
+ dst[dstOff + 3] = a + alphaFac * (255 - a);
}
}
@@ -20177,7 +20714,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
*/
fabric.Image.filters.Convolute.fromObject = function(object) {
- return new fabric.Image.filters.Convolute(object);
+ return new fabric.Image.filters.Convolute(object);
};
})(typeof exports !== 'undefined' ? exports : this);
@@ -20185,7 +20722,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20268,7 +20805,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20331,7 +20868,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20390,7 +20927,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20495,7 +21032,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20527,11 +21064,11 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* Constructor
* @memberOf fabric.Image.filters.Noise.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.noise=100] Noise value
+ * @param {Number} [options.noise=0] Noise value
*/
initialize: function(options) {
options = options || { };
- this.noise = options.noise || 100;
+ this.noise = options.noise || 0;
},
/**
@@ -20582,7 +21119,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20639,9 +21176,9 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
index = (i * 4) * jLen + (j * 4);
r = data[index];
- g = data[index+1];
- b = data[index+2];
- a = data[index+3];
+ g = data[index + 1];
+ b = data[index + 2];
+ a = data[index + 3];
/*
blocksize: 4
@@ -20694,7 +21231,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -20752,17 +21289,17 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
for (var i = 0, len = data.length; i < len; i += 4) {
r = data[i];
- g = data[i+1];
- b = data[i+2];
+ g = data[i + 1];
+ b = data[i + 2];
if (r > limit &&
g > limit &&
b > limit &&
- abs(r-g) < distance &&
- abs(r-b) < distance &&
- abs(g-b) < distance
+ abs(r - g) < distance &&
+ abs(r - b) < distance &&
+ abs(g - b) < distance
) {
- data[i+3] = 1;
+ data[i + 3] = 1;
}
}
@@ -20796,7 +21333,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20856,7 +21393,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -20919,7 +21456,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -21033,7 +21570,101 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(function(global) {
- "use strict";
+ 'use strict';
+
+ var fabric = global.fabric || (global.fabric = { }),
+ extend = fabric.util.object.extend;
+
+ /**
+ * Multiply filter class
+ * Adapted from http://www.laurenscorijn.com/articles/colormath-basics
+ * @class fabric.Image.filters.Multiply
+ * @memberOf fabric.Image.filters
+ * @extends fabric.Image.filters.BaseFilter
+ * @example Multiply filter with hex color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: '#F0F'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ * @example Multiply filter with rgb color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: 'rgb(53, 21, 176)'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ */
+ fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
+
+ /**
+ * Filter type
+ * @param {String} type
+ * @default
+ */
+ type: 'Multiply',
+
+ /**
+ * Constructor
+ * @memberOf fabric.Image.filters.Multiply.prototype
+ * @param {Object} [options] Options object
+ * @param {String} [options.color=#000000] Color to multiply the image pixels with
+ */
+ initialize: function(options) {
+ options = options || { };
+
+ this.color = options.color || '#000000';
+ },
+
+ /**
+ * Applies filter to canvas element
+ * @param {Object} canvasEl Canvas element to apply filter to
+ */
+ applyTo: function(canvasEl) {
+ var context = canvasEl.getContext('2d'),
+ imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
+ data = imageData.data,
+ iLen = data.length, i,
+ source;
+
+ source = new fabric.Color(this.color).getSource();
+
+ for (i = 0; i < iLen; i+=4) {
+ data[i] *= source[0]/255;
+ data[i + 1] *= source[1]/255;
+ data[i + 2] *= source[2]/255;
+
+ }
+
+ context.putImageData(imageData, 0, 0);
+ },
+
+ /**
+ * Returns object representation of an instance
+ * @return {Object} Object representation of an instance
+ */
+ toObject: function() {
+ return extend(this.callSuper('toObject'), {
+ color: this.color
+ });
+ }
+ });
+
+ /**
+ * Returns filter instance from an object representation
+ * @static
+ * @param {Object} object Object to create an instance from
+ * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
+ */
+ fabric.Image.filters.Multiply.fromObject = function(object) {
+ return new fabric.Image.filters.Multiply(object);
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
+
+
+(function(global) {
+
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -21460,8 +22091,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
this._boundaries.push({
height: this.fontSize * this.lineHeight,
@@ -21544,18 +22175,18 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
return;
}
- var lineWidth = ctx.measureText(line).width;
- var totalWidth = this.width;
+ var lineWidth = ctx.measureText(line).width,
+ totalWidth = this.width;
if (totalWidth > lineWidth) {
// stretch the line
- var words = line.split(/\s+/);
- var wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width;
- var widthDiff = totalWidth - wordsWidth;
- var numSpaces = words.length - 1;
- var spaceWidth = widthDiff / numSpaces;
+ var words = line.split(/\s+/),
+ wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
+ widthDiff = totalWidth - wordsWidth,
+ numSpaces = words.length - 1,
+ spaceWidth = widthDiff / numSpaces,
+ leftOffset = 0;
- var leftOffset = 0;
for (var i = 0, len = words.length; i < len; i++) {
this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
leftOffset += ctx.measureText(words[i]).width + spaceWidth;
@@ -21697,8 +22328,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (textLines[i] !== '') {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
@@ -21747,15 +22378,15 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (!this.textDecoration) return;
// var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
- var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2;
- var _this = this;
+ var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
+ _this = this;
/** @ignore */
function renderLinesAtOffset(offset) {
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = _this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = _this._getLineLeftOffset(lineWidth);
+ var lineWidth = _this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = _this._getLineLeftOffset(lineWidth);
ctx.fillRect(
_this._getLeftOffset() + lineLeftOffset,
@@ -21799,8 +22430,13 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
if (!this.visible) return;
ctx.save();
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+
+ var m = this.transformMatrix;
+ if (m && (!this.group || this.group.type === 'path-group')) {
+ ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ }
this._render(ctx);
ctx.restore();
@@ -21986,7 +22622,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
(i === 0 || this.useNative ? 'y' : 'dy'), '="',
toFixed(this.useNative
? ((lineHeight * i) - this.height / 2)
- : (lineHeight * lineTopOffsetMultiplier), 2) , '" ',
+ : (lineHeight * lineTopOffsetMultiplier), 2), '" ',
// doing this on elements since setting opacity
// on containing one doesn't work in Illustrator
this._getFillAttributes(this.fill), '>',
@@ -22081,7 +22717,14 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
* @see: http://www.w3.org/TR/SVG/text.html#TextElement
*/
fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
- 'x y font-family font-style font-weight font-size text-decoration'.split(' '));
+ 'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
+
+ /**
+ * Default SVG font size
+ * @static
+ * @memberOf fabric.Text
+ */
+ fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
/**
* Returns fabric.Text instance from an SVG element (not yet implemented)
@@ -22099,6 +22742,20 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
+ if ('dx' in parsedAttributes) {
+ options.left += parsedAttributes.dx;
+ }
+ if ('dy' in parsedAttributes) {
+ options.top += parsedAttributes.dy;
+ }
+ if (!('fontSize' in options)) {
+ options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ }
+
+ if (!options.originX) {
+ options.originX = 'center';
+ }
+
var text = new fabric.Text(element.textContent, options);
/*
@@ -22221,9 +22878,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @extends fabric.Text
* @mixes fabric.Observable
*
- * @fires text:changed
- * @fires editing:entered
- * @fires editing:exited
+ * @fires changed ("text:changed" when observing canvas)
+ * @fires editing:entered ("text:editing:entered" when observing canvas)
+ * @fires editing:exited ("text:editing:exited" when observing canvas)
*
* @return {fabric.IText} thisArg
* @see {@link fabric.IText#initialize} for constructor definition
@@ -22246,6 +22903,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
* Copy text: ctrl/cmd + c
* Paste text: ctrl/cmd + v
* Cut text: ctrl/cmd + x
+ * Select entire text: ctrl/cmd + a
*
*
* Supported mouse/touch combination
@@ -22430,6 +23088,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} index Index to set selection start to
*/
setSelectionStart: function(index) {
+ if (this.selectionStart !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionStart = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index);
},
@@ -22439,6 +23100,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} index Index to set selection end to
*/
setSelectionEnd: function(index) {
+ if (this.selectionEnd !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionEnd = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index);
},
@@ -22502,9 +23166,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
},
/**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- */
+ * @private
+ * @param {CanvasRenderingContext2D} ctx Context to render on
+ */
_render: function(ctx) {
this.callSuper('_render', ctx);
this.ctx = ctx;
@@ -22538,8 +23202,8 @@ fabric.util.object.extend(fabric.Text.prototype, {
if (typeof selectionStart === 'undefined') {
selectionStart = this.selectionStart;
}
- var textBeforeCursor = this.text.slice(0, selectionStart);
- var linesBeforeCursor = textBeforeCursor.split(this._reNewline);
+ var textBeforeCursor = this.text.slice(0, selectionStart),
+ linesBeforeCursor = textBeforeCursor.split(this._reNewline);
return {
lineIndex: linesBeforeCursor.length - 1,
@@ -22547,6 +23211,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
};
},
+ /**
+ * Returns complete style of char at the current cursor
+ * @param {Number} lineIndex Line index
+ * @param {Number} charIndex Char index
+ * @return {Object} Character style
+ */
+ getCurrentCharStyle: function(lineIndex, charIndex) {
+ var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
+
+ return {
+ fontSize: style && style.fontSize || this.fontSize,
+ fill: style && style.fill || this.fill,
+ textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
+ textDecoration: style && style.textDecoration || this.textDecoration,
+ fontFamily: style && style.fontFamily || this.fontFamily,
+ stroke: style && style.stroke || this.stroke,
+ strokeWidth: style && style.strokeWidth || this.strokeWidth
+ };
+ },
+
/**
* Returns fontSize of char at the current cursor
* @param {Number} lineIndex Line index
@@ -22693,13 +23377,16 @@ fabric.util.object.extend(fabric.Text.prototype, {
var cursorLocation = this.get2DCursorLocation(),
lineIndex = cursorLocation.lineIndex,
charIndex = cursorLocation.charIndex,
- charHeight = this.getCurrentCharFontSize(lineIndex, charIndex);
+ charHeight = this.getCurrentCharFontSize(lineIndex, charIndex),
+ leftOffset = (lineIndex === 0 && charIndex === 0)
+ ? this._getCachedLineOffset(lineIndex, this.text.split(this._reNewline))
+ : boundaries.leftOffset;
ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex);
- ctx.globalAlpha = this._currentCursorOpacity;
+ ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
ctx.fillRect(
- boundaries.left + boundaries.leftOffset,
+ boundaries.left + leftOffset,
boundaries.top + boundaries.topOffset,
this.cursorWidth / this.scaleX,
charHeight);
@@ -22719,39 +23406,43 @@ fabric.util.object.extend(fabric.Text.prototype, {
ctx.fillStyle = this.selectionColor;
- var cursorLocation = this.get2DCursorLocation(),
- lineIndex = cursorLocation.lineIndex,
- charIndex = cursorLocation.charIndex,
- textLines = this.text.split(this._reNewline),
- origLineIndex = lineIndex;
+ var start = this.get2DCursorLocation(this.selectionStart),
+ end = this.get2DCursorLocation(this.selectionEnd),
+ startLine = start.lineIndex,
+ endLine = end.lineIndex,
+ textLines = this.text.split(this._reNewline);
- for (var i = this.selectionStart; i < this.selectionEnd; i++) {
+ for (var i = startLine; i <= endLine; i++) {
+ var lineOffset = this._getCachedLineOffset(i, textLines) || 0,
+ lineHeight = this._getCachedLineHeight(i),
+ boxWidth = 0;
- if (chars[i] === '\n') {
- boundaries.leftOffset = 0;
- boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex);
- lineIndex++;
- charIndex = 0;
- }
- else if (i !== this.text.length) {
-
- var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex),
- lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0;
-
- if (lineIndex === origLineIndex) {
- // only offset the line if we're rendering selection of 2nd, 3rd, etc. line
- lineOffset = 0;
+ if (i === startLine) {
+ for (var j = 0, len = textLines[i].length; j < len; j++) {
+ if (j >= start.charIndex && (i !== endLine || j < end.charIndex)) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
+ if (j < start.charIndex) {
+ lineOffset += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
}
-
- ctx.fillRect(
- boundaries.left + boundaries.leftOffset + lineOffset,
- boundaries.top + boundaries.topOffset,
- charWidth,
- this._getHeightOfLine(ctx, lineIndex));
-
- boundaries.leftOffset += charWidth;
- charIndex++;
}
+ else if (i > startLine && i < endLine) {
+ boxWidth += this._getCachedLineWidth(i, textLines) || 5;
+ }
+ else if (i === endLine) {
+ for (var j2 = 0, j2len = end.charIndex; j2 < j2len; j2++) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j2], i, j2);
+ }
+ }
+
+ ctx.fillRect(
+ boundaries.left + lineOffset,
+ boundaries.top + boundaries.topOffset,
+ boxWidth,
+ lineHeight);
+
+ boundaries.topOffset += lineHeight;
}
ctx.restore();
},
@@ -22781,14 +23472,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines),
lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines),
lineLeftOffset = this._getLineLeftOffset(lineWidth),
- chars = line.split('');
+ chars = line.split(''),
+ prevStyle,
+ charsToRender = '';
left += lineLeftOffset || 0;
ctx.save();
- for (var i = 0, len = chars.length; i < len; i++) {
- this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight);
+
+ for (var i = 0, len = chars.length; i <= len; i++) {
+ prevStyle = prevStyle || this.getCurrentCharStyle(lineIndex, i);
+ var thisStyle = this.getCurrentCharStyle(lineIndex, i + 1);
+
+ if (this._hasStyleChanged(prevStyle, thisStyle) || i === len) {
+ this._renderChar(method, ctx, lineIndex, i - 1, charsToRender, left, top, lineHeight);
+ charsToRender = '';
+ prevStyle = thisStyle;
+ }
+ charsToRender += chars[i];
}
+
ctx.restore();
},
@@ -22851,6 +23554,22 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
},
+ /**
+ * @private
+ * @param {Object} prevStyle
+ * @param {Object} thisStyle
+ */
+ _hasStyleChanged: function(prevStyle, thisStyle) {
+ return (prevStyle.fill !== thisStyle.fill ||
+ prevStyle.fontSize !== thisStyle.fontSize ||
+ prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
+ prevStyle.textDecoration !== thisStyle.textDecoration ||
+ prevStyle.fontFamily !== thisStyle.fontFamily ||
+ prevStyle.stroke !== thisStyle.stroke ||
+ prevStyle.strokeWidth !== thisStyle.strokeWidth
+ );
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -22858,10 +23577,10 @@ fabric.util.object.extend(fabric.Text.prototype, {
_renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight, charHeight) {
var textDecoration = styleDeclaration
- ? (styleDeclaration.textDecoration || this.textDecoration)
- : this.textDecoration;
+ ? (styleDeclaration.textDecoration || this.textDecoration)
+ : this.textDecoration,
- var fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
+ fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
if (!textDecoration) return;
@@ -23081,15 +23800,35 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
},
+ /**
+ * @private
+ * @param {Number} lineIndex
+ * @param {Number} charIndex
+ */
+ _getStyleDeclaration: function(lineIndex, charIndex) {
+ return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
+ ? clone(this.styles[lineIndex][charIndex])
+ : { };
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
- ctx.save();
- var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
- ctx.restore();
- return width;
+ var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
+ this._applyFontStyles(styleDeclaration);
+ var cacheProp = this._getCacheProp(_char, styleDeclaration);
+
+ if (this._charWidthsCache[cacheProp] && this.caching) {
+ return this._charWidthsCache[cacheProp];
+ }
+ else if (ctx) {
+ ctx.save();
+ var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
+ ctx.restore();
+ return width;
+ }
},
/**
@@ -23175,10 +23914,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
textLines = textLines || this.text.split(this._reNewline);
- var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0);
-
- var line = textLines[lineIndex];
- var chars = line.split('');
+ var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0),
+ line = textLines[lineIndex],
+ chars = line.split('');
for (var i = 1, len = chars.length; i < len; i++) {
var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i);
@@ -23211,6 +23949,26 @@ fabric.util.object.extend(fabric.Text.prototype, {
return topOffset - (this.fontSize / this._fontSizeFraction);
},
+ /**
+ * @private
+ * This method is overwritten to account for different top offset
+ */
+ _renderTextBoxBackground: function(ctx) {
+ if (!this.backgroundColor) return;
+
+ ctx.save();
+ ctx.fillStyle = this.backgroundColor;
+
+ ctx.fillRect(
+ this._getLeftOffset(),
+ this._getTopOffset() + (this.fontSize / this._fontSizeFraction),
+ this.width,
+ this.height
+ );
+
+ ctx.restore();
+ },
+
/**
* Returns object representation of an instance
* @methd toObject
@@ -23235,6 +23993,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
return new fabric.IText(object.text, clone(object));
};
+ /**
+ * Contains all fabric.IText objects that have been created
+ * @static
+ * @memberof fabric.IText
+ * @type Array
+ */
fabric.IText.instances = [ ];
})();
@@ -23250,10 +24014,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* Initializes all the interactive behavior of IText
*/
initBehavior: function() {
- this.initKeyHandlers();
+ this.initAddedHandler();
this.initCursorSelectionHandlers();
this.initDoubleClickSimulation();
- this.initHiddenTextarea();
},
/**
@@ -23266,10 +24029,17 @@ fabric.util.object.extend(fabric.Text.prototype, {
setTimeout(function() {
_this.selected = true;
}, 100);
+ });
+ },
- if (!this._hasCanvasHandlers) {
+ /**
+ * Initializes "added" event handler
+ */
+ initAddedHandler: function() {
+ this.on('added', function() {
+ if (this.canvas && !this.canvas._hasITextHandlers) {
+ this.canvas._hasITextHandlers = true;
this._initCanvasHandlers();
- this._hasCanvasHandlers = true;
}
});
},
@@ -23278,25 +24048,18 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @private
*/
_initCanvasHandlers: function() {
- var _this = this;
-
- this.canvas.on('selection:cleared', function(options) {
-
- // do not exit editing if event fired
- // when clicking on an object again (in editing mode)
- if (options.e && _this.canvas.containsPoint(options.e, _this)) return;
-
- _this.exitEditing();
+ this.canvas.on('selection:cleared', function() {
+ fabric.IText.prototype.exitEditingOnOthers.call();
});
this.canvas.on('mouse:up', function() {
- this.getObjects('i-text').forEach(function(obj) {
+ fabric.IText.instances.forEach(function(obj) {
obj.__isMousedown = false;
});
});
- this.canvas.on('object:selected', function() {
- fabric.IText.prototype.exitEditingOnOthers.call(this);
+ this.canvas.on('object:selected', function(options) {
+ fabric.IText.prototype.exitEditingOnOthers.call(options.target);
});
},
@@ -23356,15 +24119,23 @@ fabric.util.object.extend(fabric.Text.prototype, {
/**
* Initializes delayed cursor
*/
- initDelayedCursor: function() {
- var _this = this;
+ initDelayedCursor: function(restart) {
+ var _this = this,
+ delay = restart ? 0 : this.cursorDelay;
+
+ if (restart) {
+ this._abortCursorAnimation = true;
+ clearTimeout(this._cursorTimeout1);
+ this._currentCursorOpacity = 1;
+ this.canvas && this.canvas.renderAll();
+ }
if (this._cursorTimeout2) {
clearTimeout(this._cursorTimeout2);
}
this._cursorTimeout2 = setTimeout(function() {
_this._abortCursorAnimation = false;
_this._tick();
- }, this.cursorDelay);
+ }, delay);
},
/**
@@ -23391,6 +24162,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
selectAll: function() {
this.selectionStart = 0;
this.selectionEnd = this.text.length;
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
},
/**
@@ -23482,8 +24254,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @return {Number} Number of newlines in selected text
*/
getNumNewLinesInSelectedText: function() {
- var selectedText = this.getSelectedText();
- var numNewLines = 0;
+ var selectedText = this.getSelectedText(),
+ numNewLines = 0;
+
for (var i = 0, chars = selectedText.split(''), len = chars.length; i < len; i++) {
if (chars[i] === '\n') {
numNewLines++;
@@ -23498,9 +24271,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} direction: 1 or -1
*/
searchWordBoundary: function(selectionStart, direction) {
- var index = selectionStart;
- var _char = this.text.charAt(index);
- var reNonWord = /[ \n\.,;!\?\-]/;
+ var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
+ _char = this.text.charAt(index),
+ reNonWord = /[ \n\.,;!\?\-]/;
while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
index += direction;
@@ -23517,11 +24290,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} selectionStart Index of a character
*/
selectWord: function(selectionStart) {
- var newSelectionStart = this.searchWordBoundary(selectionStart, -1); /* search backwards */
- var newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
+ var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
+ newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -23529,11 +24303,12 @@ fabric.util.object.extend(fabric.Text.prototype, {
* @param {Number} selectionStart Index of a character
*/
selectLine: function(selectionStart) {
- var newSelectionStart = this.findLineBoundaryLeft(selectionStart);
- var newSelectionEnd = this.findLineBoundaryRight(selectionStart);
+ var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
+ newSelectionEnd = this.findLineBoundaryRight(selectionStart);
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -23548,6 +24323,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.isEditing = true;
+ this.initHiddenTextarea();
this._updateTextarea();
this._saveEditingProps();
this._setEditingProps();
@@ -23556,14 +24332,17 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.canvas && this.canvas.renderAll();
this.fire('editing:entered');
+ this.canvas && this.canvas.fire('text:editing:entered', { target: this });
return this;
},
exitEditingOnOthers: function() {
fabric.IText.instances.forEach(function(obj) {
- if (obj === this) return;
- obj.exitEditing();
+ obj.selected = false;
+ if (obj.isEditing) {
+ obj.exitEditing();
+ }
}, this);
},
@@ -23591,7 +24370,6 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.hiddenTextarea.value = this.text;
this.hiddenTextarea.selectionStart = this.selectionStart;
- this.hiddenTextarea.focus();
},
/**
@@ -23639,13 +24417,15 @@ fabric.util.object.extend(fabric.Text.prototype, {
this.selectable = true;
this.selectionEnd = this.selectionStart;
- this.hiddenTextarea && this.hiddenTextarea.blur();
+ this.hiddenTextarea && this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
+ this.hiddenTextarea = null;
this.abortCursorAnimation();
this._restoreEditingProps();
this._currentCursorOpacity = 0;
this.fire('editing:exited');
+ this.canvas && this.canvas.fire('text:editing:exited', { target: this });
return this;
},
@@ -23672,8 +24452,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
var prevIndex = this.get2DCursorLocation(i).charIndex;
i--;
- var index = this.get2DCursorLocation(i).charIndex;
- var isNewline = index > prevIndex;
+
+ var index = this.get2DCursorLocation(i).charIndex,
+ isNewline = index > prevIndex;
if (isNewline) {
this.removeStyleObject(isNewline, i + 1);
@@ -23702,10 +24483,10 @@ fabric.util.object.extend(fabric.Text.prototype, {
if (this.selectionStart === this.selectionEnd) {
this.insertStyleObjects(_chars, isEndOfLine, this.copiedStyles);
}
- else if (this.selectionEnd - this.selectionStart > 1) {
+ // else if (this.selectionEnd - this.selectionStart > 1) {
// TODO: replace styles properly
- console.log('replacing MORE than 1 char');
- }
+ // console.log('replacing MORE than 1 char');
+ // }
this.selectionStart += _chars.length;
this.selectionEnd = this.selectionStart;
@@ -23717,7 +24498,8 @@ fabric.util.object.extend(fabric.Text.prototype, {
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -23864,7 +24646,9 @@ fabric.util.object.extend(fabric.Text.prototype, {
var textLines = this.text.split(this._reNewline),
textOnPreviousLine = textLines[lineIndex - 1],
- newCharIndexOnPrevLine = textOnPreviousLine.length;
+ newCharIndexOnPrevLine = textOnPreviousLine
+ ? textOnPreviousLine.length
+ : 0;
if (!this.styles[lineIndex - 1]) {
this.styles[lineIndex - 1] = { };
@@ -23921,7 +24705,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// for triple click
this.__lastLastClickTime = +new Date();
- this.lastPointer = { };
+ this.__lastPointer = { };
this.on('mousedown', this.onMouseDown.bind(this));
},
@@ -23943,12 +24727,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.__lastLastClickTime = this.__lastClickTime;
this.__lastClickTime = this.__newClickTime;
this.__lastPointer = newPointer;
+ this.__lastIsEditing = this.isEditing;
},
isDoubleClick: function(newPointer) {
return this.__newClickTime - this.__lastClickTime < 500 &&
this.__lastPointer.x === newPointer.x &&
- this.__lastPointer.y === newPointer.y;
+ this.__lastPointer.y === newPointer.y && this.__lastIsEditing;
},
isTripleClick: function(newPointer) {
@@ -24005,9 +24790,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
}
- if (this.isEditing) {
+ if (this.selected) {
this.setCursorByClick(options.e);
+ }
+
+ if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
+ this.initDelayedCursor(true);
}
});
},
@@ -24048,12 +24837,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
initMouseupHandler: function() {
this.on('mouseup', function(options) {
this.__isMousedown = false;
-
if (this._isObjectMoved(options.e)) return;
if (this.selected) {
this.enterEditing();
+ this.initDelayedCursor(true);
}
+ this.selected = true;
});
},
@@ -24115,10 +24905,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
height += this._getHeightOfLine(this.ctx, i) * this.scaleY;
- var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfLine);
+ var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfLine);
- width = lineLeftOffset;
+ width = lineLeftOffset * this.scaleX;
if (this.flipX) {
// when oject is horizontally flipped we reverse chars
@@ -24141,6 +24931,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return this._getNewSelectionStartFromOffset(
mouseOffset, prevWidth, width, charIndex + i, jlen);
}
+
+ if (mouseOffset.y < height) {
+ return this._getNewSelectionStartFromOffset(
+ mouseOffset, prevWidth, width, charIndex + i, jlen, j);
+ }
}
// clicked somewhere after all chars, so set at the end
@@ -24152,7 +24947,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* @private
*/
- _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
+ _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen, j) {
var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
distanceBtwNextCharAndCursor = width - mouseOffset.x,
@@ -24168,6 +24963,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
newSelectionStart = this.text.length;
}
+ if (j === jlen) {
+ newSelectionStart--;
+ }
+
return newSelectionStart;
}
});
@@ -24175,14 +24974,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
- /**
- * Initializes key handlers
- */
- initKeyHandlers: function() {
- fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this));
- fabric.util.addListener(fabric.document, 'keypress', this.onKeyPress.bind(this));
- },
-
/**
* Initializes hidden textarea (needed to bring up keyboard in iOS)
*/
@@ -24193,6 +24984,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.hiddenTextarea.style.cssText = 'position: absolute; top: 0; left: -9999px';
fabric.document.body.appendChild(this.hiddenTextarea);
+
+ fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
+ fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
+
+ if (!this._clickHandlerInitialized && this.canvas) {
+ fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
+ this._clickHandlerInitialized = true;
+ }
},
/**
@@ -24218,6 +25017,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
88: 'cut'
},
+ onClick: function() {
+ // No need to trigger click event here, focus is enough to have the keyboard appear on Android
+ this.hiddenTextarea && this.hiddenTextarea.focus();
+ },
+
/**
* Handles keyup event
* @param {Event} e Event object
@@ -24324,8 +25128,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -24343,17 +25147,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex + 1;
- var widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfNextLine);
- var widthOfCharsOnNextLine = lineLeftOffset;
- var indexOnNextLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex + 1,
+ widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
+ widthOfCharsOnNextLine = lineLeftOffset,
+ indexOnNextLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
- var _char = textOnNextLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnNextLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnNextLine += widthOfChar;
@@ -24361,10 +25165,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnNextLine - widthOfChar;
- var rightEdge = widthOfCharsOnNextLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnNextLine - widthOfChar,
+ rightEdge = widthOfCharsOnNextLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
@@ -24452,13 +25256,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
textOnPreviousLine = (textBeforeCursor.match(/\n?(.*)\n.*$/) || {})[1] || '',
textLines = this.text.split(this._reNewline),
_char,
- lineLeftOffset;
-
- var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
- lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
-
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
+ widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -24476,17 +25277,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex - 1;
- var widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine);
- var widthOfCharsOnPreviousLine = lineLeftOffset;
- var indexOnPrevLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex - 1,
+ widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
+ widthOfCharsOnPreviousLine = lineLeftOffset,
+ indexOnPrevLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
- var _char = textOnPreviousLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnPreviousLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnPreviousLine += widthOfChar;
@@ -24494,10 +25295,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnPreviousLine - widthOfChar;
- var rightEdge = widthOfCharsOnPreviousLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
+ rightEdge = widthOfCharsOnPreviousLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
@@ -24747,7 +25548,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -24771,7 +25573,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.selectionStart = leftWordBoundary;
}
else {
- var isBeginningOfLine = this.text.slice(this.selectionStart-1, this.selectionStart) === '\n';
+ var isBeginningOfLine = this.text.slice(this.selectionStart - 1, this.selectionStart) === '\n';
this.removeStyleObject(isBeginningOfLine);
this.selectionStart--;
@@ -24905,7 +25707,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return;
}
- var DOMParser = new require('xmldom').DOMParser,
+ var DOMParser = require('xmldom').DOMParser,
URL = require('url'),
HTTP = require('http'),
HTTPS = require('https'),
@@ -24923,27 +25725,26 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
// assign request handler based on protocol
- var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP;
-
- var req = reqHandler.request({
- hostname: oURL.hostname,
- port: oURL.port,
- path: oURL.path,
- method: 'GET'
- }, function(response){
- var body = "";
- if (encoding) {
- response.setEncoding(encoding);
- }
- response.on('end', function () {
- callback(body);
- });
- response.on('data', function (chunk) {
- if (response.statusCode === 200) {
- body += chunk;
- }
- });
- });
+ var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP,
+ req = reqHandler.request({
+ hostname: oURL.hostname,
+ port: oURL.port,
+ path: oURL.path,
+ method: 'GET'
+ }, function(response) {
+ var body = '';
+ if (encoding) {
+ response.setEncoding(encoding);
+ }
+ response.on('end', function () {
+ callback(body);
+ });
+ response.on('data', function (chunk) {
+ if (response.statusCode === 200) {
+ body += chunk;
+ }
+ });
+ });
req.on('error', function(err) {
if (err.errno === process.ECONNREFUSED) {
@@ -24958,32 +25759,33 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
/** @private */
- function request_fs(path, callback){
+ function requestFs(path, callback){
var fs = require('fs');
fs.readFile(path, function (err, data) {
if (err) {
fabric.log(err);
throw err;
- } else {
+ }
+ else {
callback(data);
}
});
}
fabric.util.loadImage = function(url, callback, context) {
- var createImageAndCallBack = function(data){
+ function createImageAndCallBack(data) {
img.src = new Buffer(data, 'binary');
// preserving original url, which seems to be lost in node-canvas
img._src = url;
callback && callback.call(context, img);
- };
+ }
var img = new Image();
if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
img.src = img._src = url;
callback && callback.call(context, img);
}
else if (url && url.indexOf('http') !== 0) {
- request_fs(url, createImageAndCallBack);
+ requestFs(url, createImageAndCallBack);
}
else if (url) {
request(url, 'binary', createImageAndCallBack);
@@ -24996,8 +25798,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.loadSVGFromURL = function(url, callback, reviver) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
if (url.indexOf('http') !== 0) {
- request_fs(url, function(body) {
- fabric.loadSVGFromString(body, callback, reviver);
+ requestFs(url, function(body) {
+ fabric.loadSVGFromString(body.toString(), callback, reviver);
});
}
else {
@@ -25037,12 +25839,15 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* Only available when running fabric on node.js
* @param width Canvas width
* @param height Canvas height
+ * @param {Object} options to pass to FabricCanvas.
+ * @param {Object} options to pass to NodeCanvas.
* @return {Object} wrapped canvas instance
*/
- fabric.createCanvasForNode = function(width, height) {
+ fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
+ nodeCanvasOptions = nodeCanvasOptions || options;
var canvasEl = fabric.document.createElement('canvas'),
- nodeCanvas = new Canvas(width || 600, height || 600);
+ nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
// jsdom doesn't create style on canvas element, so here be temp. workaround
canvasEl.style = { };
@@ -25050,8 +25855,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
canvasEl.width = nodeCanvas.width;
canvasEl.height = nodeCanvas.height;
- var FabricCanvas = fabric.Canvas || fabric.StaticCanvas;
- var fabricCanvas = new FabricCanvas(canvasEl);
+ var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
+ fabricCanvas = new FabricCanvas(canvasEl, options);
+
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
fabricCanvas.nodeCanvas = nodeCanvas;
fabricCanvas.Font = Canvas.Font;
@@ -25090,3 +25896,16 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
})();
+
+/* Footer for requirejs AMD support */
+
+window.fabric = fabric;
+
+// make sure exports.fabric is always defined when used as 'global' later scopes
+var exports = exports || {};
+exports.fabric = fabric;
+
+if (typeof define === 'function' && define.amd) {
+ define([], function() { return fabric });
+}
+
diff --git a/lib/aligning_guidelines.js b/lib/aligning_guidelines.js
index 289240b9..26282d5e 100644
--- a/lib/aligning_guidelines.js
+++ b/lib/aligning_guidelines.js
@@ -1,5 +1,5 @@
/**
- * Should objects by aligned by a bounding box?
+ * Should objects be aligned by a bounding box?
* [Bug] Scaled objects sometimes can not be aligned by edges
*
*/
diff --git a/lib/event.js b/lib/event.js
index e7790348..84b0f130 100644
--- a/lib/event.js
+++ b/lib/event.js
@@ -27,10 +27,10 @@ if (typeof(eventjs) === "undefined") var eventjs = Event;
(function(root) { "use strict";
// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness).
-root.modifyEventListener = true;
+root.modifyEventListener = false;
// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness).
-root.modifySelectors = true;
+root.modifySelectors = false;
// Event maintenance.
root.add = function(target, type, listener, configure) {
diff --git a/lib/excanvas-diff.patch b/lib/excanvas-diff.patch
index 2b7d64cc..37ad3c8b 100644
--- a/lib/excanvas-diff.patch
+++ b/lib/excanvas-diff.patch
@@ -6,7 +6,7 @@ Index: excanvas.js
o2.arcScaleX_ = o1.arcScaleX_;
o2.arcScaleY_ = o1.arcScaleY_;
o2.lineScale_ = o1.lineScale_;
-+ o2.rotation_ = o1.rotation_; // used for images
++ o2.rotation_ = o1.rotation_; // used for images
}
var colorData = {
@@ -14,7 +14,7 @@ Index: excanvas.js
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
-+ this.rotation_ = 0;
++ this.rotation_ = 0;
}
var contextPrototype = CanvasRenderingContext2D_.prototype;
@@ -26,10 +26,10 @@ Index: excanvas.js
+ contextPrototype.drawImage = function(image) {
var dx, dy, dw, dh, sx, sy, sw, sh;
-
-+
++
+ // to fix new Image() we check the existance of runtimeStyle
+ var rts = image.runtimeStyle.width;
-+
++
// to find the original width we overide the width and height
- var oldRuntimeWidth = image.runtimeStyle.width;
- var oldRuntimeHeight = image.runtimeStyle.height;
@@ -38,25 +38,25 @@ Index: excanvas.js
+ if(rts) {
+ var oldRuntimeWidth = image.runtimeStyle.width;
+ var oldRuntimeHeight = image.runtimeStyle.height;
-+
++
+ image.runtimeStyle.width = 'auto';
-+ image.runtimeStyle.height = 'auto';
++ image.runtimeStyle.height = 'auto';
+ }
// get the original size
var w = image.width;
var h = image.height;
-
-+
++
// and remove overides
- image.runtimeStyle.width = oldRuntimeWidth;
- image.runtimeStyle.height = oldRuntimeHeight;
-
+ if(rts) {
+ image.runtimeStyle.width = oldRuntimeWidth;
-+ image.runtimeStyle.height = oldRuntimeHeight;
++ image.runtimeStyle.height = oldRuntimeHeight;
+ }
-+
++
if (arguments.length == 3) {
dx = arguments[1];
dy = arguments[2];
@@ -64,9 +64,9 @@ Index: excanvas.js
var W = 10;
var H = 10;
-+
++
+ var scaleX = scaleY = 1;
-+
++
+ // FIX: divs give better quality then vml image and also fixes transparent PNG's
+ vmlStr.push(' ');
+ }
-+
-+
++
++
+ // Apply scales to width and height
+ vmlStr.push('
');
-+
-+ // Close the crop div if necessary
++
++ // Close the crop div if necessary
+ if (sx || sy) vmlStr.push('
');
-+
++
+ vmlStr.push('
');
-+
++
+ this.element_.insertAdjacentHTML('beforeEnd', vmlStr.join(''));
};
@@ -176,8 +176,8 @@ Index: excanvas.js
var c = mc(aRot);
var s = ms(aRot);
-+ this.rotation_ += aRot;
-+
++ this.rotation_ += aRot;
++
var m1 = [
[c, s, 0],
[-s, c, 0],
@@ -188,7 +188,7 @@ Index: excanvas.js
- this.textMeasureEl_.style.font = this.font;
+ // FIX: Apply current font style to textMeasureEl to get correct size
+ var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_),
-+ fontStyleString = buildStyle(fontStyle);
++ fontStyleString = buildStyle(fontStyle);
+ this.textMeasureEl_.style.font = fontStyleString;
+
// Don't use innerHTML or innerText because they allow markup/whitespace.
diff --git a/lib/excanvas.js b/lib/excanvas.js
index a6dceecb..b95043cf 100644
--- a/lib/excanvas.js
+++ b/lib/excanvas.js
@@ -253,7 +253,7 @@ if (!document.createElement('canvas').getContext) {
o2.arcScaleX_ = o1.arcScaleX_;
o2.arcScaleY_ = o1.arcScaleY_;
o2.lineScale_ = o1.lineScale_;
- o2.rotation_ = o1.rotation_; // used for images
+ o2.rotation_ = o1.rotation_; // used for images
}
var colorData = {
@@ -604,7 +604,7 @@ if (!document.createElement('canvas').getContext) {
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
- this.rotation_ = 0;
+ this.rotation_ = 0;
}
var contextPrototype = CanvasRenderingContext2D_.prototype;
@@ -771,29 +771,29 @@ if (!document.createElement('canvas').getContext) {
contextPrototype.drawImage = function(image) {
var dx, dy, dw, dh, sx, sy, sw, sh;
-
+
// to fix new Image() we check the existance of runtimeStyle
var rts = image.runtimeStyle.width;
-
+
// to find the original width we overide the width and height
if(rts) {
var oldRuntimeWidth = image.runtimeStyle.width;
var oldRuntimeHeight = image.runtimeStyle.height;
-
+
image.runtimeStyle.width = 'auto';
- image.runtimeStyle.height = 'auto';
+ image.runtimeStyle.height = 'auto';
}
// get the original size
var w = image.width;
var h = image.height;
-
+
// and remove overides
if(rts) {
image.runtimeStyle.width = oldRuntimeWidth;
- image.runtimeStyle.height = oldRuntimeHeight;
+ image.runtimeStyle.height = oldRuntimeHeight;
}
-
+
if (arguments.length == 3) {
dx = arguments[1];
dy = arguments[2];
@@ -830,9 +830,9 @@ if (!document.createElement('canvas').getContext) {
var W = 10;
var H = 10;
-
+
var scaleX = scaleY = 1;
-
+
// FIX: divs give better quality then vml image and also fixes transparent PNG's
vmlStr.push(' ');
}
-
-
+
+
// Apply scales to width and height
vmlStr.push('
');
-
- // Close the crop div if necessary
+
+ // Close the crop div if necessary
if (sx || sy) vmlStr.push('
');
-
+
vmlStr.push('
');
-
+
this.element_.insertAdjacentHTML('beforeEnd', vmlStr.join(''));
};
@@ -1198,8 +1198,8 @@ if (!document.createElement('canvas').getContext) {
var c = mc(aRot);
var s = ms(aRot);
- this.rotation_ += aRot;
-
+ this.rotation_ += aRot;
+
var m1 = [
[c, s, 0],
[-s, c, 0],
@@ -1355,7 +1355,7 @@ if (!document.createElement('canvas').getContext) {
this.textMeasureEl_.innerHTML = '';
// FIX: Apply current font style to textMeasureEl to get correct size
var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_),
- fontStyleString = buildStyle(fontStyle);
+ fontStyleString = buildStyle(fontStyle);
this.textMeasureEl_.style.font = fontStyleString;
// Don't use innerHTML or innerText because they allow markup/whitespace.
diff --git a/lib/json2.js b/lib/json2.js
index a43520a6..deb88ec9 100644
--- a/lib/json2.js
+++ b/lib/json2.js
@@ -1,6 +1,6 @@
/*
json2.js
- 2011-10-19
+ 2014-02-04
Public Domain.
@@ -159,8 +159,7 @@
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
-var JSON;
-if (!JSON) {
+if (typeof JSON !== 'object') {
JSON = {};
}
@@ -174,8 +173,7 @@ if (!JSON) {
if (typeof Date.prototype.toJSON !== 'function') {
- /** @ignore */
- Date.prototype.toJSON = function (key) {
+ Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
@@ -189,25 +187,16 @@ if (!JSON) {
String.prototype.toJSON =
Number.prototype.toJSON =
- /** @ignore */
- Boolean.prototype.toJSON = function (key) {
+ Boolean.prototype.toJSON = function () {
return this.valueOf();
};
}
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ var cx,
+ escapable,
gap,
indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
+ meta,
rep;
@@ -359,7 +348,16 @@ if (!JSON) {
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
- /** @ignore */
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
@@ -407,7 +405,7 @@ if (!JSON) {
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
- /** @ignore */
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
@@ -488,4 +486,4 @@ if (!JSON) {
throw new SyntaxError('JSON.parse');
};
}
-}());
\ No newline at end of file
+}());
diff --git a/lib/screenshot.png b/lib/screenshot.png
index 27c2b3b7..1135d4cd 100644
Binary files a/lib/screenshot.png and b/lib/screenshot.png differ
diff --git a/package.json b/package.json
index efb8d684..ab88eb81 100644
--- a/package.json
+++ b/package.json
@@ -1,30 +1,46 @@
{
"name": "fabric",
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
- "version": "1.4.0",
+ "homepage": "http://fabricjs.com/",
+ "version": "1.4.6",
"author": "Juriy Zaytsev ",
- "keywords": ["canvas", "graphic", "graphics", "SVG", "node-canvas", "parser", "HTML5", "object model"],
- "repository": "git://github.com/kangax/fabric.js",
- "licenses": [{
- "type": "MIT",
- "url": "http://github.com/kangax/fabric.js/raw/master/LICENSE"
- }],
+ "keywords": [
+ "canvas",
+ "graphic",
+ "graphics",
+ "SVG",
+ "node-canvas",
+ "parser",
+ "HTML5",
+ "object model"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/kangax/fabric.js"
+ },
+ "bugs": {
+ "url": "https://github.com/kangax/fabric.js/issues"
+ },
+ "license": "MIT",
"scripts": {
"build": "node build.js modules=ALL exclude=json,cufon,gestures",
"test": "node test.js && jshint src"
},
"dependencies": {
"canvas": "1.0.x",
- "jsdom": "0.7.x",
+ "jsdom": "0.10.x",
"xmldom": "0.1.x"
},
"devDependencies": {
- "qunit": "0.5.x",
- "jshint": "2.3.x",
- "uglify-js": "2.4.x",
"execSync": "0.0.x",
- "plato": "0.6.x"
+ "uglify-js": "2.4.x",
+ "jscs": "1.4.x",
+ "jshint": "2.5.x",
+ "qunit": "0.6.x",
+ "istanbul": "0.2.6"
},
- "engines": { "node": ">=0.4.0 && <1.0.0" },
- "main": "./dist/all.js"
+ "engines": {
+ "node": ">=0.4.0 && <1.0.0"
+ },
+ "main": "./dist/fabric.js"
}
diff --git a/src/amd/requirejs.js b/src/amd/requirejs.js
index 26a2696d..56259b84 100644
--- a/src/amd/requirejs.js
+++ b/src/amd/requirejs.js
@@ -6,6 +6,6 @@ window.fabric = fabric;
var exports = exports || {};
exports.fabric = fabric;
-if (typeof define === "function" && define.amd) {
+if (typeof define === 'function' && define.amd) {
define([], function() { return fabric });
}
diff --git a/src/brushes/circle_brush.class.js b/src/brushes/circle_brush.class.js
index 2302bb55..af24e237 100644
--- a/src/brushes/circle_brush.class.js
+++ b/src/brushes/circle_brush.class.js
@@ -25,8 +25,8 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @param {Object} pointer
*/
drawDot: function(pointer) {
- var point = this.addPoint(pointer);
- var ctx = this.canvas.contextTop;
+ var point = this.addPoint(pointer),
+ ctx = this.canvas.contextTop;
var v = this.canvas.viewportTransform;
ctx.save();
@@ -69,15 +69,15 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
var circles = [ ];
for (var i = 0, len = this.points.length; i < len; i++) {
- var point = this.points[i];
- var circle = new fabric.Circle({
- radius: this.points[i].radius,
- left: point.x,
- top: point.y,
- originX: 'center',
- originY: 'center',
- fill: this.points[i].fill
- });
+ var point = this.points[i],
+ circle = new fabric.Circle({
+ radius: point.radius,
+ left: point.x,
+ top: point.y,
+ originX: 'center',
+ originY: 'center',
+ fill: point.fill
+ });
this.shadow && circle.setShadow(this.shadow);
@@ -100,12 +100,12 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @return {fabric.Point} Just added pointer point
*/
addPoint: function(pointer) {
- var pointerPoint = new fabric.Point(pointer.x, pointer.y);
+ var pointerPoint = new fabric.Point(pointer.x, pointer.y),
- var circleRadius = fabric.util.getRandomInt(
- Math.max(0, this.width - 20), this.width + 20) / 2;
+ circleRadius = fabric.util.getRandomInt(
+ Math.max(0, this.width - 20), this.width + 20) / 2,
- var circleColor = new fabric.Color(this.color)
+ circleColor = new fabric.Color(this.color)
.setAlpha(fabric.util.getRandomInt(0, 100) / 100)
.toRgba();
diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js
index d6a2c947..5d6b00ec 100644
--- a/src/brushes/pencil_brush.class.js
+++ b/src/brushes/pencil_brush.class.js
@@ -109,27 +109,27 @@
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
ctx.beginPath();
- var p1 = this._points[0];
- var p2 = this._points[1];
+ var p1 = this._points[0],
+ p2 = this._points[1];
//if we only have 2 points in the path and they are the same
//it means that the user only clicked the canvas without moving the mouse
//then we should be drawing a dot. A path isn't drawn between two identical dots
//that's why we set them apart a bit
if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
- p1.x -= 0.5;
- p2.x += 0.5;
+ p1.x -= 0.5;
+ p2.x += 0.5;
}
ctx.moveTo(p1.x, p1.y);
for (var i = 1, len = this._points.length; i < len; i++) {
- // we pick the point between pi+1 & pi+2 as the
+ // we pick the point between pi + 1 & pi + 2 as the
// end point and p1 as our control point.
var midPoint = p1.midPointFrom(p2);
ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = this._points[i];
- p2 = this._points[i+1];
+ p2 = this._points[i + 1];
}
// Draw last line as a straight line while
// we wait for the next point to be able to calculate
@@ -155,7 +155,7 @@
* @param {Array} points
* @return {Object} object with minx, miny, maxx, maxy
*/
- getPathBoundingBox: function(points) {
+ getPathBoundingBox: function(points) {
var xBounds = [],
yBounds = [],
p1 = points[0],
@@ -171,19 +171,19 @@
yBounds.push(midPoint.y);
p1 = points[i];
- p2 = points[i+1];
+ p2 = points[i + 1];
startPoint = midPoint;
- } // end for
+ }
- xBounds.push(p1.x);
- yBounds.push(p1.y);
+ xBounds.push(p1.x);
+ yBounds.push(p1.y);
- return {
- minx: utilMin(xBounds),
- miny: utilMin(yBounds),
- maxx: utilMax(xBounds),
- maxy: utilMax(yBounds)
- };
+ return {
+ minx: utilMin(xBounds),
+ miny: utilMin(yBounds),
+ maxx: utilMax(xBounds),
+ maxy: utilMax(yBounds)
+ };
},
/**
@@ -192,9 +192,9 @@
* @return {String} SVG path
*/
convertPointsToSVGPath: function(points, minX, maxX, minY) {
- var path = [];
- var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY);
- var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
+ var path = [],
+ p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
+ p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
for (var i = 1, len = points.length; i < len; i++) {
@@ -204,8 +204,8 @@
// start point is p(i-1) value.
path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
- if ((i+1) < points.length) {
- p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY);
+ if ((i + 1) < points.length) {
+ p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
}
}
path.push('L ', p1.x, ' ', p1.y, ' ');
@@ -244,7 +244,7 @@
ctx.closePath();
var pathData = this._getSVGPathData().join('');
- if (pathData === "M 0 0 Q 0 0 0 0 L 0 0") {
+ if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
@@ -254,8 +254,8 @@
}
// set path origin coordinates based on our bounding box
- var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2;
- var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2;
+ var originLeft = this.box.minx + (this.box.maxx - this.box.minx) / 2,
+ originTop = this.box.miny + (this.box.maxy - this.box.miny) / 2;
this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
diff --git a/src/brushes/| b/src/brushes/|
deleted file mode 100644
index 2302bb55..00000000
--- a/src/brushes/|
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * CircleBrush class
- * @class fabric.CircleBrush
- */
-fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
-
- /**
- * Width of a brush
- * @type Number
- * @default
- */
- width: 10,
-
- /**
- * Constructor
- * @param {fabric.Canvas} canvas
- * @return {fabric.CircleBrush} Instance of a circle brush
- */
- initialize: function(canvas) {
- this.canvas = canvas;
- this.points = [ ];
- },
- /**
- * Invoked inside on mouse down and mouse move
- * @param {Object} pointer
- */
- drawDot: function(pointer) {
- var point = this.addPoint(pointer);
- var ctx = this.canvas.contextTop;
-
- var v = this.canvas.viewportTransform;
- ctx.save();
- ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-
- ctx.fillStyle = point.fill;
- ctx.beginPath();
- ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
- ctx.closePath();
- ctx.fill();
-
- ctx.restore();
- },
-
- /**
- * Invoked on mouse down
- */
- onMouseDown: function(pointer) {
- this.points.length = 0;
- this.canvas.clearContext(this.canvas.contextTop);
- this._setShadow();
- this.drawDot(pointer);
- },
-
- /**
- * Invoked on mouse move
- * @param {Object} pointer
- */
- onMouseMove: function(pointer) {
- this.drawDot(pointer);
- },
-
- /**
- * Invoked on mouse up
- */
- onMouseUp: function() {
- var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
- this.canvas.renderOnAddRemove = false;
-
- var circles = [ ];
-
- for (var i = 0, len = this.points.length; i < len; i++) {
- var point = this.points[i];
- var circle = new fabric.Circle({
- radius: this.points[i].radius,
- left: point.x,
- top: point.y,
- originX: 'center',
- originY: 'center',
- fill: this.points[i].fill
- });
-
- this.shadow && circle.setShadow(this.shadow);
-
- circles.push(circle);
- }
- var group = new fabric.Group(circles, { originX: 'center', originY: 'center' });
- group.canvas = this.canvas;
-
- this.canvas.add(group);
- this.canvas.fire('path:created', { path: group });
-
- this.canvas.clearContext(this.canvas.contextTop);
- this._resetShadow();
- this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
- this.canvas.renderAll();
- },
-
- /**
- * @param {Object} pointer
- * @return {fabric.Point} Just added pointer point
- */
- addPoint: function(pointer) {
- var pointerPoint = new fabric.Point(pointer.x, pointer.y);
-
- var circleRadius = fabric.util.getRandomInt(
- Math.max(0, this.width - 20), this.width + 20) / 2;
-
- var circleColor = new fabric.Color(this.color)
- .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
- .toRgba();
-
- pointerPoint.radius = circleRadius;
- pointerPoint.fill = circleColor;
-
- this.points.push(pointerPoint);
-
- return pointerPoint;
- }
-});
diff --git a/src/canvas.class.js b/src/canvas.class.js
index 36cad3f0..bd7326e2 100644
--- a/src/canvas.class.js
+++ b/src/canvas.class.js
@@ -29,6 +29,8 @@
* @fires mouse:down
* @fires mouse:move
* @fires mouse:up
+ * @fires mouse:over
+ * @fires mouse:out
*
*/
fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
@@ -203,10 +205,10 @@
var t = this._currentTransform;
t.target.set({
- 'scaleX': t.original.scaleX,
- 'scaleY': t.original.scaleY,
- 'left': t.original.left,
- 'top': t.original.top
+ scaleX: t.original.scaleX,
+ scaleY: t.original.scaleY,
+ left: t.original.left,
+ top: t.original.top
});
if (this._shouldCenterTransform(e, t.target)) {
@@ -253,7 +255,7 @@
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
- return (target.containsPoint(xy) || target._findTargetCorner(e, this._offset));
+ return (target.containsPoint(xy) || target._findTargetCorner(pointer));
},
/**
@@ -263,14 +265,12 @@
var activeGroup = this.getActiveGroup(),
x = pointer.x,
y = pointer.y,
+ isObjectInGroup = (
+ activeGroup &&
+ object.type !== 'group' &&
+ activeGroup.contains(object)),
lt;
- var isObjectInGroup = (
- activeGroup &&
- object.type !== 'group' &&
- activeGroup.contains(object)
- );
-
if (isObjectInGroup) {
lt = new fabric.Point(activeGroup.left, activeGroup.top);
lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
@@ -405,11 +405,8 @@
_setupCurrentTransform: function (e, target) {
if (!target) return;
- var corner = target._findTargetCorner(e, this._offset),
- pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
- fabric.util.invertTransform(this.viewportTransform)
- ),
+ var pointer = this.getPointer(e),
+ corner = target._findTargetCorner(this.getPointer(e, true)),
action = this._getActionFromCorner(target, corner),
origin = this._getOriginFromCorner(target, corner);
@@ -471,7 +468,6 @@
*/
_scaleObject: function (x, y, by) {
var t = this._currentTransform,
- offset = this._offset,
target = t.target,
lockScalingX = target.get('lockScalingX'),
lockScalingY = target.get('lockScalingY');
@@ -479,8 +475,8 @@
if (lockScalingX && lockScalingY) return;
// Get the constraint point
- var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
- var localMouse = target.toLocalPoint(new fabric.Point(x - offset.left, y - offset.top), t.originX, t.originY);
+ var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
+ localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
this._setLocalMouse(localMouse, t);
@@ -527,9 +523,8 @@
*/
_scaleObjectEqually: function(localMouse, target, transform) {
- var dist = localMouse.y + localMouse.x;
-
- var lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
+ var dist = localMouse.y + localMouse.x,
+ lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
(target.width + (target.strokeWidth)) * transform.original.scaleX;
// We use transform.scaleX/Y instead of target.scaleX/Y
@@ -626,13 +621,12 @@
*/
_rotateObject: function (x, y) {
- var t = this._currentTransform,
- o = this._offset;
+ var t = this._currentTransform;
if (t.target.get('lockRotation')) return;
- var lastAngle = atan2(t.ey - t.top - o.top, t.ex - t.left - o.left),
- curAngle = atan2(y - t.top - o.top, x - t.left - o.left),
+ var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
+ curAngle = atan2(y - t.top, x - t.left),
angle = radiansToDegrees(curAngle - lastAngle + t.theta);
// normalize angle to positive value
@@ -685,15 +679,15 @@
// selection border
if (this.selectionDashArray.length > 1) {
- var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft);
- var py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
+ var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft),
+ py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, px, py, px+aleft, py, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py+atop-1, px+aleft, py+atop-1, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px, py, px, py+atop, this.selectionDashArray);
- fabric.util.drawDashedLine(ctx, px+aleft-1, py, px+aleft-1, py+atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
+ fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
ctx.closePath();
ctx.stroke();
@@ -717,7 +711,7 @@
this.lastRenderedObjectWithControlsAboveOverlay &&
this.lastRenderedObjectWithControlsAboveOverlay.visible &&
this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
- this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e, this._offset));
+ this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
},
/**
@@ -735,10 +729,56 @@
// first check current group (if one exists)
var activeGroup = this.getActiveGroup();
if (activeGroup && !skipGroup && this.containsPoint(e, activeGroup)) {
+ console.log('AG', activeGroup);
return activeGroup;
}
- return this._searchPossibleTargets(e);
+ var target = this._searchPossibleTargets(e);
+ this._fireOverOutEvents(target);
+
+ return target;
+ },
+
+ /**
+ * @private
+ */
+ _fireOverOutEvents: function(target) {
+ if (target) {
+ if (this._hoveredTarget !== target) {
+ this.fire('mouse:over', { target: target });
+ target.fire('mouseover');
+ if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ }
+ this._hoveredTarget = target;
+ }
+ }
+ else if (this._hoveredTarget) {
+ this.fire('mouse:out', { target: this._hoveredTarget });
+ this._hoveredTarget.fire('mouseout');
+ this._hoveredTarget = null;
+ }
+ },
+
+ /**
+ * @private
+ */
+ _checkTarget: function(e, obj, pointer) {
+ if (obj &&
+ obj.visible &&
+ obj.evented &&
+ this.containsPoint(e, obj)){
+ if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
+ var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
+ if (!isTransparent) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
},
/**
@@ -747,33 +787,15 @@
_searchPossibleTargets: function(e) {
// Cache all targets where their bounding box contains point.
- var possibleTargets = [],
- target,
+ var target,
pointer = this.getPointer(e, true);
- for (var i = this._objects.length; i--; ) {
- if (this._objects[i] &&
- this._objects[i].visible &&
- this._objects[i].evented &&
- this.containsPoint(e, this._objects[i])) {
+ var i = this._objects.length;
- if (this.perPixelTargetFind || this._objects[i].perPixelTargetFind) {
- possibleTargets[possibleTargets.length] = this._objects[i];
- }
- else {
- target = this._objects[i];
- this.relatedTarget = target;
- break;
- }
- }
- }
-
- for (var j = 0, len = possibleTargets.length; j < len; j++) {
- pointer = this.getPointer(e, true);
- var isTransparent = this.isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
- if (!isTransparent) {
- target = possibleTargets[j];
- this.relatedTarget = target;
+ while (i--) {
+ if (this._checkTarget(e, this._objects[i], pointer)){
+ this.relatedTarget = this._objects[i];
+ target = this._objects[i];
break;
}
}
@@ -790,7 +812,12 @@
if (!upperCanvasEl) {
upperCanvasEl = this.upperCanvasEl;
}
- var pointer = getPointer(e, upperCanvasEl);
+ var pointer = getPointer(e, upperCanvasEl),
+ bounds = upperCanvasEl.getBoundingClientRect(),
+ cssScale;
+
+ pointer.x = pointer.x - this._offset.left;
+ pointer.y = pointer.y - this._offset.top;
if (!ignoreZoom) {
pointer = fabric.util.transformPoint(
pointer,
@@ -798,9 +825,19 @@
);
}
+ if (bounds.width === 0 || bounds.height === 0) {
+ // If bounds are not available (i.e. not visible), do not apply scale.
+ cssScale = { width: 1, height: 1 };
+ }
+ else {
+ cssScale = {
+ width: upperCanvasEl.width / bounds.width,
+ height: upperCanvasEl.height / bounds.height
+ };
+ }
return {
- x: pointer.x - this._offset.left,
- y: pointer.y - this._offset.top
+ x: pointer.x * cssScale.width,
+ y: pointer.y * cssScale.height
};
},
diff --git a/src/color.class.js b/src/color.class.js
index 5d7490db..35393c4d 100644
--- a/src/color.class.js
+++ b/src/color.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -43,6 +43,11 @@
color = Color.colorNameMap[color];
}
+ if (color === 'transparent') {
+ this.setSource([255,255,255,0]);
+ return;
+ }
+
source = Color.sourceFromHex(color);
if (!source) {
@@ -161,15 +166,15 @@
* @return {String} ex: FF5555
*/
toHex: function() {
- var source = this.getSource();
+ var source = this.getSource(), r, g, b;
- var r = source[0].toString(16);
+ r = source[0].toString(16);
r = (r.length === 1) ? ('0' + r) : r;
- var g = source[1].toString(16);
+ g = source[1].toString(16);
g = (g.length === 1) ? ('0' + g) : g;
- var b = source[2].toString(16);
+ b = source[2].toString(16);
b = (b.length === 1) ? ('0' + b) : b;
return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
@@ -256,7 +261,7 @@
* @field
* @memberOf fabric.Color
*/
- fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
+ fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
/**
* Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
@@ -282,23 +287,23 @@
* @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
*/
fabric.Color.colorNameMap = {
- 'aqua': '#00FFFF',
- 'black': '#000000',
- 'blue': '#0000FF',
- 'fuchsia': '#FF00FF',
- 'gray': '#808080',
- 'green': '#008000',
- 'lime': '#00FF00',
- 'maroon': '#800000',
- 'navy': '#000080',
- 'olive': '#808000',
- 'orange': '#FFA500',
- 'purple': '#800080',
- 'red': '#FF0000',
- 'silver': '#C0C0C0',
- 'teal': '#008080',
- 'white': '#FFFFFF',
- 'yellow': '#FFFF00'
+ aqua: '#00FFFF',
+ black: '#000000',
+ blue: '#0000FF',
+ fuchsia: '#FF00FF',
+ gray: '#808080',
+ green: '#008000',
+ lime: '#00FF00',
+ maroon: '#800000',
+ navy: '#000080',
+ olive: '#808000',
+ orange: '#FFA500',
+ purple: '#800080',
+ red: '#FF0000',
+ silver: '#C0C0C0',
+ teal: '#008080',
+ white: '#FFFFFF',
+ yellow: '#FFFF00'
};
/**
@@ -309,11 +314,21 @@
* @return {Number}
*/
function hue2rgb(p, q, t){
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ if (t < 0) {
+ t += 1;
+ }
+ if (t > 1) {
+ t -= 1;
+ }
+ if (t < 1/6) {
+ return p + (q - p) * 6 * t;
+ }
+ if (t < 1/2) {
+ return q;
+ }
+ if (t < 2/3) {
+ return p + (q - p) * (2/3 - t) * 6;
+ }
return p;
}
@@ -390,8 +405,8 @@
r = g = b = l;
}
else {
- var q = l <= 0.5 ? l * (s + 1) : l + s - l * s;
- var p = l * 2 - q;
+ var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
+ p = l * 2 - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
diff --git a/src/elements_parser.js b/src/elements_parser.js
index caf548ea..46f67580 100644
--- a/src/elements_parser.js
+++ b/src/elements_parser.js
@@ -1,67 +1,69 @@
-fabric.ElementsParser = {
+fabric.ElementsParser = function(elements, callback, options, reviver) {
+ this.elements = elements;
+ this.callback = callback;
+ this.options = options;
+ this.reviver = reviver;
+};
- parse: function(elements, callback, options, reviver) {
+fabric.ElementsParser.prototype.parse = function() {
+ this.instances = new Array(this.elements.length);
+ this.numElements = this.elements.length;
- this.elements = elements;
- this.callback = callback;
- this.options = options;
- this.reviver = reviver;
+ this.createObjects();
+};
- this.instances = new Array(elements.length);
- this.numElements = elements.length;
-
- this.createObjects();
- },
-
- createObjects: function() {
- for (var i = 0, len = this.elements.length; i < len; i++) {
- this.createObject(this.elements[i], i);
- }
- },
-
- createObject: function(el, index) {
- var klass = fabric[fabric.util.string.capitalize(el.tagName)];
- if (klass && klass.fromElement) {
- try {
- this._createObject(klass, el, index);
- }
- catch(err) {
- fabric.log(err);
- }
- }
- else {
- this.checkIfDone();
- }
- },
-
- _createObject: function(klass, el, index) {
- if (klass.async) {
- klass.fromElement(el, this.createCallback(index, el), this.options);
- }
- else {
- var obj = klass.fromElement(el, this.options);
- this.reviver && this.reviver(el, obj);
- this.instances.splice(index, 0, obj);
- this.checkIfDone();
- }
- },
-
- createCallback: function(index, el) {
- var _this = this;
- return function(obj) {
- _this.reviver && _this.reviver(el, obj);
- _this.instances.splice(index, 0, obj);
- _this.checkIfDone();
- };
- },
-
- checkIfDone: function() {
- if (--this.numElements === 0) {
- this.instances = this.instances.filter(function(el) {
- return el != null;
- });
- fabric.resolveGradients(this.instances);
- this.callback(this.instances);
- }
+fabric.ElementsParser.prototype.createObjects = function() {
+ for (var i = 0, len = this.elements.length; i < len; i++) {
+ (function(_this, i) {
+ setTimeout(function() {
+ _this.createObject(_this.elements[i], i);
+ }, 0);
+ })(this, i);
+ }
+};
+
+fabric.ElementsParser.prototype.createObject = function(el, index) {
+ var klass = fabric[fabric.util.string.capitalize(el.tagName)];
+ if (klass && klass.fromElement) {
+ try {
+ this._createObject(klass, el, index);
+ }
+ catch (err) {
+ fabric.log(err);
+ }
+ }
+ else {
+ this.checkIfDone();
+ }
+};
+
+fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
+ if (klass.async) {
+ klass.fromElement(el, this.createCallback(index, el), this.options);
+ }
+ else {
+ var obj = klass.fromElement(el, this.options);
+ this.reviver && this.reviver(el, obj);
+ this.instances.splice(index, 0, obj);
+ this.checkIfDone();
+ }
+};
+
+fabric.ElementsParser.prototype.createCallback = function(index, el) {
+ var _this = this;
+ return function(obj) {
+ _this.reviver && _this.reviver(el, obj);
+ _this.instances.splice(index, 0, obj);
+ _this.checkIfDone();
+ };
+};
+
+fabric.ElementsParser.prototype.checkIfDone = function() {
+ if (--this.numElements === 0) {
+ this.instances = this.instances.filter(function(el) {
+ return el != null;
+ });
+ fabric.resolveGradients(this.instances);
+ this.callback(this.instances);
}
};
diff --git a/src/filters/base_filter.class.js b/src/filters/base_filter.class.js
index 7ada290d..b5e1340c 100644
--- a/src/filters/base_filter.class.js
+++ b/src/filters/base_filter.class.js
@@ -6,7 +6,6 @@
*/
fabric.Image.filters = fabric.Image.filters || { };
-
/**
* Root filter class from which all filter classes inherit from
* @class fabric.Image.filters.BaseFilter
diff --git a/src/filters/brightness_filter.class.js b/src/filters/brightness_filter.class.js
index 4fe03d5d..93e2dcea 100644
--- a/src/filters/brightness_filter.class.js
+++ b/src/filters/brightness_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -32,11 +32,11 @@
* Constructor
* @memberOf fabric.Image.filters.Brightness.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.brightness=100] Value to brighten the image up (0..255)
+ * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
*/
initialize: function(options) {
options = options || { };
- this.brightness = options.brightness || 100;
+ this.brightness = options.brightness || 0;
},
/**
diff --git a/src/filters/convolute_filter.class.js b/src/filters/convolute_filter.class.js
index a63e7175..3aef4ab3 100644
--- a/src/filters/convolute_filter.class.js
+++ b/src/filters/convolute_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -66,9 +66,11 @@
options = options || { };
this.opaque = options.opaque;
- this.matrix = options.matrix || [ 0, 0, 0,
+ this.matrix = options.matrix || [
+ 0, 0, 0,
0, 1, 0,
- 0, 0, 0 ];
+ 0, 0, 0
+ ];
var canvasEl = fabric.util.createCanvasElement();
this.tmpCtx = canvasEl.getContext('2d');
@@ -95,49 +97,49 @@
halfSide = Math.floor(side/2),
src = pixels.data,
sw = pixels.width,
- sh = pixels.height;
+ sh = pixels.height,
- // pad output by the convolution matrix
- var w = sw;
- var h = sh;
- var output = this._createImageData(w, h);
+ // pad output by the convolution matrix
+ w = sw,
+ h = sh,
+ output = this._createImageData(w, h),
- var dst = output.data;
+ dst = output.data,
- // go through the destination image pixels
- var alphaFac = this.opaque ? 1 : 0;
+ // go through the destination image pixels
+ alphaFac = this.opaque ? 1 : 0;
- for (var y=0; y sh || scx < 0 || scx > sw) continue;
- var srcOff = (scy*sw+scx)*4;
- var wt = weights[cy*side+cx];
+ var srcOff = (scy * sw + scx) * 4,
+ wt = weights[cy * side + cx];
r += src[srcOff] * wt;
- g += src[srcOff+1] * wt;
- b += src[srcOff+2] * wt;
- a += src[srcOff+3] * wt;
+ g += src[srcOff + 1] * wt;
+ b += src[srcOff + 2] * wt;
+ a += src[srcOff + 3] * wt;
}
}
dst[dstOff] = r;
- dst[dstOff+1] = g;
- dst[dstOff+2] = b;
- dst[dstOff+3] = a + alphaFac*(255-a);
+ dst[dstOff + 1] = g;
+ dst[dstOff + 2] = b;
+ dst[dstOff + 3] = a + alphaFac * (255 - a);
}
}
@@ -163,7 +165,7 @@
* @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
*/
fabric.Image.filters.Convolute.fromObject = function(object) {
- return new fabric.Image.filters.Convolute(object);
+ return new fabric.Image.filters.Convolute(object);
};
})(typeof exports !== 'undefined' ? exports : this);
diff --git a/src/filters/gradienttransparency_filter.class.js b/src/filters/gradienttransparency_filter.class.js
index 9f7ea01f..fc83e322 100644
--- a/src/filters/gradienttransparency_filter.class.js
+++ b/src/filters/gradienttransparency_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
diff --git a/src/filters/grayscale_filter.class.js b/src/filters/grayscale_filter.class.js
index 34e61e76..1057da89 100644
--- a/src/filters/grayscale_filter.class.js
+++ b/src/filters/grayscale_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
diff --git a/src/filters/invert_filter.class.js b/src/filters/invert_filter.class.js
index 1478863b..6b472ef1 100644
--- a/src/filters/invert_filter.class.js
+++ b/src/filters/invert_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
diff --git a/src/filters/mask_filter.class.js b/src/filters/mask_filter.class.js
index f432cbed..7b7c457c 100644
--- a/src/filters/mask_filter.class.js
+++ b/src/filters/mask_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
diff --git a/src/filters/multiply_filter.class.js b/src/filters/multiply_filter.class.js
new file mode 100644
index 00000000..3361ff29
--- /dev/null
+++ b/src/filters/multiply_filter.class.js
@@ -0,0 +1,92 @@
+(function(global) {
+
+ 'use strict';
+
+ var fabric = global.fabric || (global.fabric = { }),
+ extend = fabric.util.object.extend;
+
+ /**
+ * Multiply filter class
+ * Adapted from http://www.laurenscorijn.com/articles/colormath-basics
+ * @class fabric.Image.filters.Multiply
+ * @memberOf fabric.Image.filters
+ * @extends fabric.Image.filters.BaseFilter
+ * @example Multiply filter with hex color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: '#F0F'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ * @example Multiply filter with rgb color
+ * var filter = new fabric.Image.filters.Multiply({
+ * color: 'rgb(53, 21, 176)'
+ * });
+ * object.filters.push(filter);
+ * object.applyFilters(canvas.renderAll.bind(canvas));
+ */
+ fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
+
+ /**
+ * Filter type
+ * @param {String} type
+ * @default
+ */
+ type: 'Multiply',
+
+ /**
+ * Constructor
+ * @memberOf fabric.Image.filters.Multiply.prototype
+ * @param {Object} [options] Options object
+ * @param {String} [options.color=#000000] Color to multiply the image pixels with
+ */
+ initialize: function(options) {
+ options = options || { };
+
+ this.color = options.color || '#000000';
+ },
+
+ /**
+ * Applies filter to canvas element
+ * @param {Object} canvasEl Canvas element to apply filter to
+ */
+ applyTo: function(canvasEl) {
+ var context = canvasEl.getContext('2d'),
+ imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
+ data = imageData.data,
+ iLen = data.length, i,
+ source;
+
+ source = new fabric.Color(this.color).getSource();
+
+ for (i = 0; i < iLen; i+=4) {
+ data[i] *= source[0]/255;
+ data[i + 1] *= source[1]/255;
+ data[i + 2] *= source[2]/255;
+
+ }
+
+ context.putImageData(imageData, 0, 0);
+ },
+
+ /**
+ * Returns object representation of an instance
+ * @return {Object} Object representation of an instance
+ */
+ toObject: function() {
+ return extend(this.callSuper('toObject'), {
+ color: this.color
+ });
+ }
+ });
+
+ /**
+ * Returns filter instance from an object representation
+ * @static
+ * @param {Object} object Object to create an instance from
+ * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
+ */
+ fabric.Image.filters.Multiply.fromObject = function(object) {
+ return new fabric.Image.filters.Multiply(object);
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
diff --git a/src/filters/noise_filter.class.js b/src/filters/noise_filter.class.js
index b6900554..3fd6e9b6 100644
--- a/src/filters/noise_filter.class.js
+++ b/src/filters/noise_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -32,11 +32,11 @@
* Constructor
* @memberOf fabric.Image.filters.Noise.prototype
* @param {Object} [options] Options object
- * @param {Number} [options.noise=100] Noise value
+ * @param {Number} [options.noise=0] Noise value
*/
initialize: function(options) {
options = options || { };
- this.noise = options.noise || 100;
+ this.noise = options.noise || 0;
},
/**
diff --git a/src/filters/pixelate_filter.class.js b/src/filters/pixelate_filter.class.js
index 1c31420b..df328902 100644
--- a/src/filters/pixelate_filter.class.js
+++ b/src/filters/pixelate_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -57,9 +57,9 @@
index = (i * 4) * jLen + (j * 4);
r = data[index];
- g = data[index+1];
- b = data[index+2];
- a = data[index+3];
+ g = data[index + 1];
+ b = data[index + 2];
+ a = data[index + 3];
/*
blocksize: 4
diff --git a/src/filters/removewhite_filter.class.js b/src/filters/removewhite_filter.class.js
index 1d33d994..44cd212c 100644
--- a/src/filters/removewhite_filter.class.js
+++ b/src/filters/removewhite_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -58,17 +58,17 @@
for (var i = 0, len = data.length; i < len; i += 4) {
r = data[i];
- g = data[i+1];
- b = data[i+2];
+ g = data[i + 1];
+ b = data[i + 2];
if (r > limit &&
g > limit &&
b > limit &&
- abs(r-g) < distance &&
- abs(r-b) < distance &&
- abs(g-b) < distance
+ abs(r - g) < distance &&
+ abs(r - b) < distance &&
+ abs(g - b) < distance
) {
- data[i+3] = 1;
+ data[i + 3] = 1;
}
}
diff --git a/src/filters/sepia2_filter.class.js b/src/filters/sepia2_filter.class.js
index c39ea090..5e639aba 100644
--- a/src/filters/sepia2_filter.class.js
+++ b/src/filters/sepia2_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
diff --git a/src/filters/sepia_filter.class.js b/src/filters/sepia_filter.class.js
index 663d5b3c..3751fab1 100644
--- a/src/filters/sepia_filter.class.js
+++ b/src/filters/sepia_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
diff --git a/src/filters/tint_filter.class.js b/src/filters/tint_filter.class.js
index 6a1c1093..ab9d7e94 100644
--- a/src/filters/tint_filter.class.js
+++ b/src/filters/tint_filter.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
diff --git a/src/gradient.class.js b/src/gradient.class.js
index 0f0210f3..6e58c16e 100644
--- a/src/gradient.class.js
+++ b/src/gradient.class.js
@@ -12,7 +12,7 @@
if (style) {
var keyValuePairs = style.split(/\s*;\s*/);
- if (keyValuePairs[keyValuePairs.length-1] === '') {
+ if (keyValuePairs[keyValuePairs.length - 1] === '') {
keyValuePairs.pop();
}
@@ -115,7 +115,11 @@
addColorStop: function(colorStop) {
for (var position in colorStop) {
var color = new fabric.Color(colorStop[position]);
- this.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ this.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this;
},
diff --git a/src/intersection.class.js b/src/intersection.class.js
index f76e5fb2..935fa659 100644
--- a/src/intersection.class.js
+++ b/src/intersection.class.js
@@ -1,161 +1,160 @@
-(function(global) {
-
- "use strict";
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Intersection) {
- fabric.warn('fabric.Intersection is already defined');
- return;
- }
-
- /**
- * Intersection class
- * @class fabric.Intersection
- * @memberOf fabric
- * @constructor
- */
- function Intersection(status) {
- this.status = status;
- this.points = [];
- }
-
- fabric.Intersection = Intersection;
-
- fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
- /**
- * Appends a point to intersection
- * @param {fabric.Point} point
- */
- appendPoint: function (point) {
- this.points.push(point);
- },
-
- /**
- * Appends points to intersection
- * @param {Array} points
- */
- appendPoints: function (points) {
- this.points = this.points.concat(points);
- }
- };
-
- /**
- * Checks if one line intersects another
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {fabric.Point} b1
- * @param {fabric.Point} b2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
- var result,
- ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
- ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
- u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
- if (u_b !== 0) {
- var ua = ua_t / u_b,
- ub = ub_t / u_b;
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- result = new Intersection("Intersection");
- result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
- }
- else {
- result = new Intersection();
- }
- }
- else {
- if (ua_t === 0 || ub_t === 0) {
- result = new Intersection("Coincident");
- }
- else {
- result = new Intersection("Parallel");
- }
- }
- return result;
- };
-
- /**
- * Checks if line intersects polygon
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {Array} points
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
- var result = new Intersection(),
- length = points.length;
-
- for (var i = 0; i < length; i++) {
- var b1 = points[i],
- b2 = points[(i+1) % length],
- inter = Intersection.intersectLineLine(a1, a2, b1, b2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects another polygon
- * @static
- * @param {Array} points1
- * @param {Array} points2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
- var result = new Intersection(),
- length = points1.length;
-
- for (var i = 0; i < length; i++) {
- var a1 = points1[i],
- a2 = points1[(i+1) % length],
- inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects rectangle
- * @static
- * @param {Array} points
- * @param {Number} r1
- * @param {Number} r2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
- var min = r1.min(r2),
- max = r1.max(r2),
- topRight = new fabric.Point(max.x, min.y),
- bottomLeft = new fabric.Point(min.x, max.y),
- inter1 = Intersection.intersectLinePolygon(min, topRight, points),
- inter2 = Intersection.intersectLinePolygon(topRight, max, points),
- inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
- inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
- result = new Intersection();
-
- result.appendPoints(inter1.points);
- result.appendPoints(inter2.points);
- result.appendPoints(inter3.points);
- result.appendPoints(inter4.points);
-
- if (result.points.length > 0) {
- result.status = "Intersection";
- }
- return result;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
+(function(global) {
+
+ 'use strict';
+
+ /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
+ var fabric = global.fabric || (global.fabric = { });
+
+ if (fabric.Intersection) {
+ fabric.warn('fabric.Intersection is already defined');
+ return;
+ }
+
+ /**
+ * Intersection class
+ * @class fabric.Intersection
+ * @memberOf fabric
+ * @constructor
+ */
+ function Intersection(status) {
+ this.status = status;
+ this.points = [];
+ }
+
+ fabric.Intersection = Intersection;
+
+ fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
+
+ /**
+ * Appends a point to intersection
+ * @param {fabric.Point} point
+ */
+ appendPoint: function (point) {
+ this.points.push(point);
+ },
+
+ /**
+ * Appends points to intersection
+ * @param {Array} points
+ */
+ appendPoints: function (points) {
+ this.points = this.points.concat(points);
+ }
+ };
+
+ /**
+ * Checks if one line intersects another
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {fabric.Point} b1
+ * @param {fabric.Point} b2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
+ var result,
+ uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
+ ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
+ uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
+ if (uB !== 0) {
+ var ua = uaT / uB,
+ ub = ubT / uB;
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
+ result = new Intersection('Intersection');
+ result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
+ }
+ else {
+ result = new Intersection();
+ }
+ }
+ else {
+ if (uaT === 0 || ubT === 0) {
+ result = new Intersection('Coincident');
+ }
+ else {
+ result = new Intersection('Parallel');
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Checks if line intersects polygon
+ * @static
+ * @param {fabric.Point} a1
+ * @param {fabric.Point} a2
+ * @param {Array} points
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
+ var result = new Intersection(),
+ length = points.length;
+
+ for (var i = 0; i < length; i++) {
+ var b1 = points[i],
+ b2 = points[(i + 1) % length],
+ inter = Intersection.intersectLineLine(a1, a2, b1, b2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects another polygon
+ * @static
+ * @param {Array} points1
+ * @param {Array} points2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
+ var result = new Intersection(),
+ length = points1.length;
+
+ for (var i = 0; i < length; i++) {
+ var a1 = points1[i],
+ a2 = points1[(i + 1) % length],
+ inter = Intersection.intersectLinePolygon(a1, a2, points2);
+
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+ /**
+ * Checks if polygon intersects rectangle
+ * @static
+ * @param {Array} points
+ * @param {Number} r1
+ * @param {Number} r2
+ * @return {fabric.Intersection}
+ */
+ fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
+ var min = r1.min(r2),
+ max = r1.max(r2),
+ topRight = new fabric.Point(max.x, min.y),
+ bottomLeft = new fabric.Point(min.x, max.y),
+ inter1 = Intersection.intersectLinePolygon(min, topRight, points),
+ inter2 = Intersection.intersectLinePolygon(topRight, max, points),
+ inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
+ inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
+ result = new Intersection();
+
+ result.appendPoints(inter1.points);
+ result.appendPoints(inter2.points);
+ result.appendPoints(inter3.points);
+ result.appendPoints(inter4.points);
+
+ if (result.points.length > 0) {
+ result.status = 'Intersection';
+ }
+ return result;
+ };
+
+})(typeof exports !== 'undefined' ? exports : this);
diff --git a/src/mixins/animation.mixin.js b/src/mixins/animation.mixin.js
index 456fa0b7..b937d49e 100644
--- a/src/mixins/animation.mixin.js
+++ b/src/mixins/animation.mixin.js
@@ -142,7 +142,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
for (prop in arguments[0]) {
propsToAnimate.push(prop);
}
- for (var i = 0, len = propsToAnimate.length; i 1) {
+ this.setWidth(scaledWidth).setHeight(scaledHeight);
+ }
ctx.scale(multiplier, multiplier);
if (cropping.left) {
@@ -134,9 +135,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (cropping.width) {
cropping.width *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.width = scaledWidth;
+ }
if (cropping.height) {
cropping.height *= multiplier;
}
+ else if (multiplier < 1) {
+ cropping.height = scaledHeight;
+ }
if (activeGroup) {
// not removing group due to complications with restoring it with correct state afterwords
diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js
index b97842d2..0d008997 100644
--- a/src/mixins/canvas_events.mixin.js
+++ b/src/mixins/canvas_events.mixin.js
@@ -1,31 +1,35 @@
(function(){
- var cursorMap = [
- 'n-resize',
- 'ne-resize',
- 'e-resize',
- 'se-resize',
- 's-resize',
- 'sw-resize',
- 'w-resize',
- 'nw-resize'
- ],
- cursorOffset = {
- 'mt': 0, // n
- 'tr': 1, // ne
- 'mr': 2, // e
- 'br': 3, // se
- 'mb': 4, // s
- 'bl': 5, // sw
- 'ml': 6, // w
- 'tl': 7 // nw
+ var cursorOffset = {
+ mt: 0, // n
+ tr: 1, // ne
+ mr: 2, // e
+ br: 3, // se
+ mb: 4, // s
+ bl: 5, // sw
+ ml: 6, // w
+ tl: 7 // nw
},
addListener = fabric.util.addListener,
- removeListener = fabric.util.removeListener,
- getPointer = fabric.util.getPointer;
+ removeListener = fabric.util.removeListener;
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
+ /**
+ * Map of cursor style values for each of the object controls
+ * @private
+ */
+ cursorMap: [
+ 'n-resize',
+ 'ne-resize',
+ 'e-resize',
+ 'se-resize',
+ 's-resize',
+ 'sw-resize',
+ 'w-resize',
+ 'nw-resize'
+ ],
+
/**
* Adds mouse listeners to canvas
* @private
@@ -141,14 +145,20 @@
_onMouseDown: function (e) {
this.__onMouseDown(e);
- addListener(fabric.document, 'mouseup', this._onMouseUp);
addListener(fabric.document, 'touchend', this._onMouseUp);
-
- addListener(fabric.document, 'mousemove', this._onMouseMove);
addListener(fabric.document, 'touchmove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchstart') {
+ // Unbind mousedown to prevent double triggers from touch devices
+ removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
+ }
+ else {
+ addListener(fabric.document, 'mouseup', this._onMouseUp);
+ addListener(fabric.document, 'mousemove', this._onMouseMove);
+ }
},
/**
@@ -166,6 +176,15 @@
addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
+
+ if (e.type === 'touchend') {
+ // Wait 400ms before rebinding mousedown to prevent double triggers
+ // from touch devices
+ var _this = this;
+ setTimeout(function() {
+ addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
+ }, 400);
+ }
},
/**
@@ -264,8 +283,8 @@
*/
_finalizeCurrentTransform: function() {
- var transform = this._currentTransform;
- var target = transform.target;
+ var transform = this._currentTransform,
+ target = transform.target;
if (target._scaling) {
target._scaling = false;
@@ -407,7 +426,7 @@
this.stateful && target.saveState();
// determine if it's a drag or rotate case
- if ((corner = target._findTargetCorner(e, this._offset))) {
+ if ((corner = target._findTargetCorner(this.getPointer(e)))) {
this.onBeforeScaleRotate(target);
}
@@ -529,11 +548,11 @@
* @param {Event} e Event fired on mousemove
*/
_transformObject: function(e) {
-
var pointer = fabric.util.transformPoint(
- getPointer(e, this.upperCanvasEl),
+ fabric.util.getPointer(e, this.upperCanvasEl),
fabric.util.invertTransform(this.viewportTransform)
),
+ pointer = this.getPointer(e),
transform = this._currentTransform;
transform.reset = false,
@@ -581,7 +600,7 @@
* @private
*/
_fire: function(eventName, target, e) {
- this.fire('object:' + eventName, { target: target, e: e});
+ this.fire('object:' + eventName, { target: target, e: e });
target.fire(eventName, { e: e });
},
@@ -638,11 +657,11 @@
return false;
}
else {
- var activeGroup = this.getActiveGroup();
- // only show proper corner when group selection is not active
- var corner = target._findTargetCorner
+ var activeGroup = this.getActiveGroup(),
+ // only show proper corner when group selection is not active
+ corner = target._findTargetCorner
&& (!activeGroup || !activeGroup.contains(target))
- && target._findTargetCorner(e, this._offset);
+ && target._findTargetCorner(this.getPointer(e, true));
if (!corner) {
style.cursor = target.hoverCursor || this.hoverCursor;
@@ -685,7 +704,7 @@
// normalize n to be from 0 to 7
n %= 8;
- return cursorMap[n];
+ return this.cursorMap[n];
}
});
})();
diff --git a/src/mixins/canvas_gestures.mixin.js b/src/mixins/canvas_gestures.mixin.js
index f615d293..fdb18853 100644
--- a/src/mixins/canvas_gestures.mixin.js
+++ b/src/mixins/canvas_gestures.mixin.js
@@ -14,7 +14,7 @@
*/
__onTransformGesture: function(e, self) {
- if (this.isDrawingMode || e.touches.length !== 2 || 'gesture' !== self.gesture) {
+ if (this.isDrawingMode || !e.touches || e.touches.length !== 2 || 'gesture' !== self.gesture) {
return;
}
@@ -25,7 +25,7 @@
this._scaleObjectBy(self.scale);
}
- this.fire('touch:gesture', {target: target, e: e, self: self});
+ this.fire('touch:gesture', { target: target, e: e, self: self });
},
/**
@@ -35,7 +35,7 @@
* @param self Event proxy object by Event.js
*/
__onDrag: function(e, self) {
- this.fire('touch:drag', {e: e, self: self});
+ this.fire('touch:drag', { e: e, self: self });
},
/**
@@ -45,7 +45,7 @@
* @param self Event proxy object by Event.js
*/
__onOrientationChange: function(e, self) {
- this.fire('touch:orientation', {e: e, self: self});
+ this.fire('touch:orientation', { e: e, self: self });
},
/**
@@ -55,7 +55,7 @@
* @param self Event proxy object by Event.js
*/
__onShake: function(e, self) {
- this.fire('touch:shake', {e: e, self: self});
+ this.fire('touch:shake', { e: e, self: self });
},
/**
@@ -66,10 +66,9 @@
*/
_scaleObjectBy: function(s, by) {
var t = this._currentTransform,
- target = t.target;
-
- var lockScalingX = target.get('lockScalingX'),
- lockScalingY = target.get('lockScalingY');
+ target = t.target,
+ lockScalingX = target.get('lockScalingX'),
+ lockScalingY = target.get('lockScalingY');
if (lockScalingX && lockScalingY) return;
diff --git a/src/mixins/canvas_grouping.mixin.js b/src/mixins/canvas_grouping.mixin.js
index 8fb945a2..b277ba94 100644
--- a/src/mixins/canvas_grouping.mixin.js
+++ b/src/mixins/canvas_grouping.mixin.js
@@ -99,13 +99,11 @@
*/
_createGroup: function(target) {
- var objects = this.getObjects();
-
- var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target);
-
- var groupObjects = isActiveLower
- ? [ this._activeObject, target ]
- : [ target, this._activeObject ];
+ var objects = this.getObjects(),
+ isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
+ groupObjects = isActiveLower
+ ? [ this._activeObject, target ]
+ : [ target, this._activeObject ];
return new fabric.Group(groupObjects, {
originX: 'center',
diff --git a/src/mixins/canvas_serialization.mixin.js b/src/mixins/canvas_serialization.mixin.js
index 86bb3ad8..907c87a5 100644
--- a/src/mixins/canvas_serialization.mixin.js
+++ b/src/mixins/canvas_serialization.mixin.js
@@ -129,8 +129,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_enlivenObjects: function (objects, callback, reviver) {
var _this = this;
- if (objects.length === 0) {
+ if (!objects || objects.length === 0) {
callback && callback();
+ return;
}
var renderOnAddRemove = this.renderOnAddRemove;
diff --git a/src/mixins/collection.mixin.js b/src/mixins/collection.mixin.js
index b387e7f9..bf4c686a 100644
--- a/src/mixins/collection.mixin.js
+++ b/src/mixins/collection.mixin.js
@@ -6,12 +6,12 @@ fabric.Collection = {
/**
* Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
* Objects should be instances of (or inherit from) fabric.Object
- * @param [...] Zero or more fabric instances
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
*/
add: function () {
this._objects.push.apply(this._objects, arguments);
- for (var i = arguments.length; i--; ) {
+ for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
this.renderOnAddRemove && this.renderAll();
@@ -25,6 +25,7 @@ fabric.Collection = {
* @param {Number} index Index to insert object at
* @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
* @return {Self} thisArg
+ * @chainable
*/
insertAt: function (object, index, nonSplicing) {
var objects = this.getObjects();
@@ -40,22 +41,27 @@ fabric.Collection = {
},
/**
- * Removes an object from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * @param {Object} object Object to remove
+ * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
+ * @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
+ * @chainable
*/
- remove: function(object) {
+ remove: function() {
var objects = this.getObjects(),
- index = objects.indexOf(object);
+ index;
- // only call onObjectRemoved if an object was actually removed
- if (index !== -1) {
- objects.splice(index, 1);
- this._onObjectRemoved(object);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ index = objects.indexOf(arguments[i]);
+
+ // only call onObjectRemoved if an object was actually removed
+ if (index !== -1) {
+ objects.splice(index, 1);
+ this._onObjectRemoved(arguments[i]);
+ }
}
this.renderOnAddRemove && this.renderAll();
- return object;
+ return this;
},
/**
diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js
index 6efe334f..32de02b5 100644
--- a/src/mixins/itext_behavior.mixin.js
+++ b/src/mixins/itext_behavior.mixin.js
@@ -8,10 +8,9 @@
* Initializes all the interactive behavior of IText
*/
initBehavior: function() {
- this.initKeyHandlers();
+ this.initAddedHandler();
this.initCursorSelectionHandlers();
this.initDoubleClickSimulation();
- this.initHiddenTextarea();
},
/**
@@ -24,10 +23,17 @@
setTimeout(function() {
_this.selected = true;
}, 100);
+ });
+ },
- if (!this._hasCanvasHandlers) {
+ /**
+ * Initializes "added" event handler
+ */
+ initAddedHandler: function() {
+ this.on('added', function() {
+ if (this.canvas && !this.canvas._hasITextHandlers) {
+ this.canvas._hasITextHandlers = true;
this._initCanvasHandlers();
- this._hasCanvasHandlers = true;
}
});
},
@@ -36,25 +42,18 @@
* @private
*/
_initCanvasHandlers: function() {
- var _this = this;
-
- this.canvas.on('selection:cleared', function(options) {
-
- // do not exit editing if event fired
- // when clicking on an object again (in editing mode)
- if (options.e && _this.canvas.containsPoint(options.e, _this)) return;
-
- _this.exitEditing();
+ this.canvas.on('selection:cleared', function() {
+ fabric.IText.prototype.exitEditingOnOthers.call();
});
this.canvas.on('mouse:up', function() {
- this.getObjects('i-text').forEach(function(obj) {
+ fabric.IText.instances.forEach(function(obj) {
obj.__isMousedown = false;
});
});
- this.canvas.on('object:selected', function() {
- fabric.IText.prototype.exitEditingOnOthers.call(this);
+ this.canvas.on('object:selected', function(options) {
+ fabric.IText.prototype.exitEditingOnOthers.call(options.target);
});
},
@@ -114,15 +113,23 @@
/**
* Initializes delayed cursor
*/
- initDelayedCursor: function() {
- var _this = this;
+ initDelayedCursor: function(restart) {
+ var _this = this,
+ delay = restart ? 0 : this.cursorDelay;
+
+ if (restart) {
+ this._abortCursorAnimation = true;
+ clearTimeout(this._cursorTimeout1);
+ this._currentCursorOpacity = 1;
+ this.canvas && this.canvas.renderAll();
+ }
if (this._cursorTimeout2) {
clearTimeout(this._cursorTimeout2);
}
this._cursorTimeout2 = setTimeout(function() {
_this._abortCursorAnimation = false;
_this._tick();
- }, this.cursorDelay);
+ }, delay);
},
/**
@@ -149,6 +156,7 @@
selectAll: function() {
this.selectionStart = 0;
this.selectionEnd = this.text.length;
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
},
/**
@@ -240,8 +248,9 @@
* @return {Number} Number of newlines in selected text
*/
getNumNewLinesInSelectedText: function() {
- var selectedText = this.getSelectedText();
- var numNewLines = 0;
+ var selectedText = this.getSelectedText(),
+ numNewLines = 0;
+
for (var i = 0, chars = selectedText.split(''), len = chars.length; i < len; i++) {
if (chars[i] === '\n') {
numNewLines++;
@@ -256,9 +265,9 @@
* @param {Number} direction: 1 or -1
*/
searchWordBoundary: function(selectionStart, direction) {
- var index = selectionStart;
- var _char = this.text.charAt(index);
- var reNonWord = /[ \n\.,;!\?\-]/;
+ var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
+ _char = this.text.charAt(index),
+ reNonWord = /[ \n\.,;!\?\-]/;
while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
index += direction;
@@ -275,11 +284,12 @@
* @param {Number} selectionStart Index of a character
*/
selectWord: function(selectionStart) {
- var newSelectionStart = this.searchWordBoundary(selectionStart, -1); /* search backwards */
- var newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
+ var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
+ newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -287,11 +297,12 @@
* @param {Number} selectionStart Index of a character
*/
selectLine: function(selectionStart) {
- var newSelectionStart = this.findLineBoundaryLeft(selectionStart);
- var newSelectionEnd = this.findLineBoundaryRight(selectionStart);
+ var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
+ newSelectionEnd = this.findLineBoundaryRight(selectionStart);
this.setSelectionStart(newSelectionStart);
this.setSelectionEnd(newSelectionEnd);
+ this.initDelayedCursor(true);
},
/**
@@ -306,6 +317,7 @@
this.isEditing = true;
+ this.initHiddenTextarea();
this._updateTextarea();
this._saveEditingProps();
this._setEditingProps();
@@ -314,14 +326,17 @@
this.canvas && this.canvas.renderAll();
this.fire('editing:entered');
+ this.canvas && this.canvas.fire('text:editing:entered', { target: this });
return this;
},
exitEditingOnOthers: function() {
fabric.IText.instances.forEach(function(obj) {
- if (obj === this) return;
- obj.exitEditing();
+ obj.selected = false;
+ if (obj.isEditing) {
+ obj.exitEditing();
+ }
}, this);
},
@@ -349,7 +364,6 @@
this.hiddenTextarea.value = this.text;
this.hiddenTextarea.selectionStart = this.selectionStart;
- this.hiddenTextarea.focus();
},
/**
@@ -397,13 +411,15 @@
this.selectable = true;
this.selectionEnd = this.selectionStart;
- this.hiddenTextarea && this.hiddenTextarea.blur();
+ this.hiddenTextarea && this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
+ this.hiddenTextarea = null;
this.abortCursorAnimation();
this._restoreEditingProps();
this._currentCursorOpacity = 0;
this.fire('editing:exited');
+ this.canvas && this.canvas.fire('text:editing:exited', { target: this });
return this;
},
@@ -430,8 +446,9 @@
var prevIndex = this.get2DCursorLocation(i).charIndex;
i--;
- var index = this.get2DCursorLocation(i).charIndex;
- var isNewline = index > prevIndex;
+
+ var index = this.get2DCursorLocation(i).charIndex,
+ isNewline = index > prevIndex;
if (isNewline) {
this.removeStyleObject(isNewline, i + 1);
@@ -460,10 +477,10 @@
if (this.selectionStart === this.selectionEnd) {
this.insertStyleObjects(_chars, isEndOfLine, this.copiedStyles);
}
- else if (this.selectionEnd - this.selectionStart > 1) {
+ // else if (this.selectionEnd - this.selectionStart > 1) {
// TODO: replace styles properly
- console.log('replacing MORE than 1 char');
- }
+ // console.log('replacing MORE than 1 char');
+ // }
this.selectionStart += _chars.length;
this.selectionEnd = this.selectionStart;
@@ -475,7 +492,8 @@
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -622,7 +640,9 @@
var textLines = this.text.split(this._reNewline),
textOnPreviousLine = textLines[lineIndex - 1],
- newCharIndexOnPrevLine = textOnPreviousLine.length;
+ newCharIndexOnPrevLine = textOnPreviousLine
+ ? textOnPreviousLine.length
+ : 0;
if (!this.styles[lineIndex - 1]) {
this.styles[lineIndex - 1] = { };
diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js
index 92aa0f67..adf2bec4 100644
--- a/src/mixins/itext_click_behavior.mixin.js
+++ b/src/mixins/itext_click_behavior.mixin.js
@@ -10,7 +10,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// for triple click
this.__lastLastClickTime = +new Date();
- this.lastPointer = { };
+ this.__lastPointer = { };
this.on('mousedown', this.onMouseDown.bind(this));
},
@@ -32,12 +32,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.__lastLastClickTime = this.__lastClickTime;
this.__lastClickTime = this.__newClickTime;
this.__lastPointer = newPointer;
+ this.__lastIsEditing = this.isEditing;
},
isDoubleClick: function(newPointer) {
return this.__newClickTime - this.__lastClickTime < 500 &&
this.__lastPointer.x === newPointer.x &&
- this.__lastPointer.y === newPointer.y;
+ this.__lastPointer.y === newPointer.y && this.__lastIsEditing;
},
isTripleClick: function(newPointer) {
@@ -94,9 +95,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
}
- if (this.isEditing) {
+ if (this.selected) {
this.setCursorByClick(options.e);
+ }
+
+ if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
+ this.initDelayedCursor(true);
}
});
},
@@ -137,12 +142,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
initMouseupHandler: function() {
this.on('mouseup', function(options) {
this.__isMousedown = false;
-
if (this._isObjectMoved(options.e)) return;
if (this.selected) {
this.enterEditing();
+ this.initDelayedCursor(true);
}
+ this.selected = true;
});
},
@@ -204,10 +210,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
height += this._getHeightOfLine(this.ctx, i) * this.scaleY;
- var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfLine);
+ var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfLine);
- width = lineLeftOffset;
+ width = lineLeftOffset * this.scaleX;
if (this.flipX) {
// when oject is horizontally flipped we reverse chars
@@ -230,6 +236,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return this._getNewSelectionStartFromOffset(
mouseOffset, prevWidth, width, charIndex + i, jlen);
}
+
+ if (mouseOffset.y < height) {
+ return this._getNewSelectionStartFromOffset(
+ mouseOffset, prevWidth, width, charIndex + i, jlen, j);
+ }
}
// clicked somewhere after all chars, so set at the end
@@ -241,7 +252,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* @private
*/
- _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
+ _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen, j) {
var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
distanceBtwNextCharAndCursor = width - mouseOffset.x,
@@ -257,6 +268,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
newSelectionStart = this.text.length;
}
+ if (j === jlen) {
+ newSelectionStart--;
+ }
+
return newSelectionStart;
}
});
diff --git a/src/mixins/itext_key_behavior.mixin.js b/src/mixins/itext_key_behavior.mixin.js
index 88945438..60b502e5 100644
--- a/src/mixins/itext_key_behavior.mixin.js
+++ b/src/mixins/itext_key_behavior.mixin.js
@@ -1,13 +1,5 @@
fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
- /**
- * Initializes key handlers
- */
- initKeyHandlers: function() {
- fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this));
- fabric.util.addListener(fabric.document, 'keypress', this.onKeyPress.bind(this));
- },
-
/**
* Initializes hidden textarea (needed to bring up keyboard in iOS)
*/
@@ -18,6 +10,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.hiddenTextarea.style.cssText = 'position: absolute; top: 0; left: -9999px';
fabric.document.body.appendChild(this.hiddenTextarea);
+
+ fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
+ fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
+
+ if (!this._clickHandlerInitialized && this.canvas) {
+ fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
+ this._clickHandlerInitialized = true;
+ }
},
/**
@@ -43,6 +43,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
88: 'cut'
},
+ onClick: function() {
+ // No need to trigger click event here, focus is enough to have the keyboard appear on Android
+ this.hiddenTextarea && this.hiddenTextarea.focus();
+ },
+
/**
* Handles keyup event
* @param {Event} e Event object
@@ -149,8 +154,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -168,17 +173,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex + 1;
- var widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfNextLine);
- var widthOfCharsOnNextLine = lineLeftOffset;
- var indexOnNextLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex + 1,
+ widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
+ widthOfCharsOnNextLine = lineLeftOffset,
+ indexOnNextLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
- var _char = textOnNextLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnNextLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnNextLine += widthOfChar;
@@ -186,10 +191,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnNextLine - widthOfChar;
- var rightEdge = widthOfCharsOnNextLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnNextLine - widthOfChar,
+ rightEdge = widthOfCharsOnNextLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
@@ -277,13 +282,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
textOnPreviousLine = (textBeforeCursor.match(/\n?(.*)\n.*$/) || {})[1] || '',
textLines = this.text.split(this._reNewline),
_char,
- lineLeftOffset;
-
- var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
- lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
-
- var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
- var lineIndex = cursorLocation.lineIndex;
+ widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
+ widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
+ lineIndex = cursorLocation.lineIndex;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
@@ -301,17 +303,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
- var lineIndex = cursorLocation.lineIndex - 1;
- var widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
- var lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine);
- var widthOfCharsOnPreviousLine = lineLeftOffset;
- var indexOnPrevLine = 0;
- var foundMatch;
+ var lineIndex = cursorLocation.lineIndex - 1,
+ widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
+ lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
+ widthOfCharsOnPreviousLine = lineLeftOffset,
+ indexOnPrevLine = 0,
+ foundMatch;
for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
- var _char = textOnPreviousLine[j];
- var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
+ var _char = textOnPreviousLine[j],
+ widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnPreviousLine += widthOfChar;
@@ -319,10 +321,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
foundMatch = true;
- var leftEdge = widthOfCharsOnPreviousLine - widthOfChar;
- var rightEdge = widthOfCharsOnPreviousLine;
- var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
- var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
+ var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
+ rightEdge = widthOfCharsOnPreviousLine,
+ offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
+ offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
@@ -572,7 +574,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
this.setCoords();
- this.fire('text:changed');
+ this.fire('changed');
+ this.canvas && this.canvas.fire('text:changed', { target: this });
},
/**
@@ -596,7 +599,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.selectionStart = leftWordBoundary;
}
else {
- var isBeginningOfLine = this.text.slice(this.selectionStart-1, this.selectionStart) === '\n';
+ var isBeginningOfLine = this.text.slice(this.selectionStart - 1, this.selectionStart) === '\n';
this.removeStyleObject(isBeginningOfLine);
this.selectionStart--;
diff --git a/src/mixins/object.svg_export.js b/src/mixins/object.svg_export.js
index 39cc07f2..43717121 100644
--- a/src/mixins/object.svg_export.js
+++ b/src/mixins/object.svg_export.js
@@ -8,32 +8,32 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
getSvgStyles: function() {
var fill = this.fill
- ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
- : 'none';
+ ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
+ : 'none',
- var stroke = this.stroke
- ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
- : 'none';
+ stroke = this.stroke
+ ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
+ : 'none',
- var strokeWidth = this.strokeWidth ? this.strokeWidth : '0';
- var strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '';
- var strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt';
- var strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter';
- var strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4';
- var opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1';
+ strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
+ strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
+ strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
+ strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
+ strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
+ opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
- var visibility = this.visible ? '' : " visibility: hidden;";
- var filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
+ visibility = this.visible ? '' : ' visibility: hidden;',
+ filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
return [
- "stroke: ", stroke, "; ",
- "stroke-width: ", strokeWidth, "; ",
- "stroke-dasharray: ", strokeDashArray, "; ",
- "stroke-linecap: ", strokeLineCap, "; ",
- "stroke-linejoin: ", strokeLineJoin, "; ",
- "stroke-miterlimit: ", strokeMiterLimit, "; ",
- "fill: ", fill, "; ",
- "opacity: ", opacity, ";",
+ 'stroke: ', stroke, '; ',
+ 'stroke-width: ', strokeWidth, '; ',
+ 'stroke-dasharray: ', strokeDashArray, '; ',
+ 'stroke-linecap: ', strokeLineCap, '; ',
+ 'stroke-linejoin: ', strokeLineJoin, '; ',
+ 'stroke-miterlimit: ', strokeMiterLimit, '; ',
+ 'fill: ', fill, '; ',
+ 'opacity: ', opacity, ';',
filter,
visibility
].join('');
@@ -44,34 +44,37 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransform: function() {
- var toFixed = fabric.util.toFixed;
- var angle = this.getAngle();
- var center = this.getCenterPoint();
+ var toFixed = fabric.util.toFixed,
+ angle = this.getAngle(),
+ center = this.getCenterPoint(),
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var translatePart = "translate(" +
+ translatePart = 'translate(' +
toFixed(center.x, NUM_FRACTION_DIGITS) +
- " " +
+ ' ' +
toFixed(center.y, NUM_FRACTION_DIGITS) +
- ")";
+ ')',
- var anglePart = angle !== 0
- ? (" rotate(" + toFixed(angle, NUM_FRACTION_DIGITS) + ")")
- : '';
+ anglePart = angle !== 0
+ ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
+ : '',
- var scalePart = (this.scaleX === 1 && this.scaleY === 1)
- ? '' :
- (" scale(" +
- toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
- " " +
- toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
- ")");
+ scalePart = (this.scaleX === 1 && this.scaleY === 1)
+ ? '' :
+ (' scale(' +
+ toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
+ ' ' +
+ toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
+ ')'),
- var flipXPart = this.flipX ? "matrix(-1 0 0 1 0 0) " : "";
- var flipYPart = this.flipY ? "matrix(1 0 0 -1 0 0)" : "";
+ flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
- return [ translatePart, anglePart, scalePart, flipXPart, flipYPart ].join('');
+ flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
+
+ return [
+ translatePart, anglePart, scalePart, flipXPart, flipYPart
+ ].join('');
},
/**
diff --git a/src/mixins/object_geometry.mixin.js b/src/mixins/object_geometry.mixin.js
index 03460f5f..d393b29c 100644
--- a/src/mixins/object_geometry.mixin.js
+++ b/src/mixins/object_geometry.mixin.js
@@ -22,13 +22,12 @@
tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
- br = new fabric.Point(oCoords.br.x, oCoords.br.y);
-
- var intersection = fabric.Intersection.intersectPolygonRectangle(
- [tl, tr, br, bl],
- pointTL,
- pointBR
- );
+ br = new fabric.Point(oCoords.br.x, oCoords.br.y),
+ intersection = fabric.Intersection.intersectPolygonRectangle(
+ [tl, tr, br, bl],
+ pointTL,
+ pointBR
+ );
return intersection.status === 'Intersection';
},
@@ -48,12 +47,11 @@
};
}
var thisCoords = getCoords(this.oCoords),
- otherCoords = getCoords(other.oCoords);
-
- var intersection = fabric.Intersection.intersectPolygonPolygon(
- [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
- [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
- );
+ otherCoords = getCoords(other.oCoords),
+ intersection = fabric.Intersection.intersectPolygonPolygon(
+ [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
+ [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
+ );
return intersection.status === 'Intersection';
},
@@ -81,10 +79,10 @@
var boundingRect = this.getBoundingRect();
return (
- boundingRect.left > pointTL.x &&
- boundingRect.left + boundingRect.width < pointBR.x &&
- boundingRect.top > pointTL.y &&
- boundingRect.top + boundingRect.height < pointBR.y
+ boundingRect.left >= pointTL.x &&
+ boundingRect.left + boundingRect.width <= pointBR.x &&
+ boundingRect.top >= pointTL.y &&
+ boundingRect.top + boundingRect.height <= pointBR.y
);
},
@@ -158,7 +156,7 @@
else {
b1 = 0;
b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
- a1 = point.y- b1 * point.x;
+ a1 = point.y - b1 * point.x;
a2 = iLine.o.y - b2 * iLine.o.x;
xi = - (a1 - a2) / (b1 - b2);
@@ -201,15 +199,15 @@
getBoundingRect: function() {
this.oCoords || this.setCoords();
- var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x];
- var minX = fabric.util.array.min(xCoords);
- var maxX = fabric.util.array.max(xCoords);
- var width = Math.abs(minX - maxX);
+ var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
+ minX = fabric.util.array.min(xCoords),
+ maxX = fabric.util.array.max(xCoords),
+ width = Math.abs(minX - maxX),
- var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y];
- var minY = fabric.util.array.min(yCoords);
- var maxY = fabric.util.array.max(yCoords);
- var height = Math.abs(minY - maxY);
+ yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
+ minY = fabric.util.array.min(yCoords),
+ maxY = fabric.util.array.max(yCoords),
+ height = Math.abs(minY - maxY);
return {
left: minX,
@@ -243,12 +241,13 @@
*/
_constrainScale: function(value) {
if (Math.abs(value) < this.minScaleLimit) {
- if (value < 0)
+ if (value < 0) {
return -this.minScaleLimit;
- else
+ }
+ else {
return this.minScaleLimit;
+ }
}
-
return value;
},
@@ -307,9 +306,7 @@
var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
padding = this.padding,
theta = degreesToRadians(this.angle),
- vpt;
- // TODO: ideally we should never setCoords an object which lacks a canvas
- vpt = this.canvas ? this.canvas.viewportTransform : [1, 0, 0, 1, 0, 0];
+ vpt = this.getViewportTransform();
var f = function (p) {
return fabric.util.transformPoint(p, vpt);
@@ -324,13 +321,13 @@
}
var _hypotenuse = Math.sqrt(
- Math.pow(this.currentWidth / 2, 2) +
- Math.pow(this.currentHeight / 2, 2));
+ Math.pow(this.currentWidth / 2, 2) +
+ Math.pow(this.currentHeight / 2, 2)),
- var _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0);
+ _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
- // offset added for rotate and scale actions
- var offsetX = Math.cos(_angle + theta) * _hypotenuse,
+ // offset added for rotate and scale actions
+ offsetX = Math.cos(_angle + theta) * _hypotenuse,
offsetY = Math.sin(_angle + theta) * _hypotenuse,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
diff --git a/src/mixins/object_interactivity.mixin.js b/src/mixins/object_interactivity.mixin.js
index 9ac2b341..de15029d 100644
--- a/src/mixins/object_interactivity.mixin.js
+++ b/src/mixins/object_interactivity.mixin.js
@@ -1,7 +1,6 @@
(function(){
- var getPointer = fabric.util.getPointer,
- degreesToRadians = fabric.util.degreesToRadians,
+ var degreesToRadians = fabric.util.degreesToRadians,
isVML = typeof G_vmlCanvasManager !== 'undefined';
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
@@ -15,15 +14,13 @@
/**
* Determines which corner has been clicked
* @private
- * @param {Event} e Event object
- * @param {Object} offset Canvas offset
+ * @param {Object} pointer The pointer indicating the mouse position
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
*/
- _findTargetCorner: function(e, offset) {
+ _findTargetCorner: function(pointer) {
if (!this.hasControls || !this.active) return false;
- var pointer = this.canvas.getPointer(e, true),
- ex = pointer.x,
+ var ex = pointer.x,
ey = pointer.y,
xPoints,
lines;
@@ -38,7 +35,8 @@
continue;
}
- if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
+ if (this.get('lockUniScaling') &&
+ (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
continue;
}
@@ -58,7 +56,7 @@
// canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
// canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
- xPoints = this._findCrossPoints({x: ex, y: ey}, lines);
+ xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
if (xPoints !== 0 && xPoints % 2 === 1) {
this.__corner = i;
return i;
@@ -278,7 +276,7 @@
ctx.lineWidth = 1 / this.borderScaleFactor;
- var vpt = this.canvas.viewportTransform,
+ var vpt = this.getViewportTransform(),
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, true),
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), vpt, true),
w = wh.x,
@@ -330,7 +328,7 @@
var size = this.cornerSize,
size2 = size / 2,
strokeWidth2 = ~~(this.strokeWidth / 2), // half strokeWidth rounded down
- wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.canvas.viewportTransform, true),
+ wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.getViewportTransform(), true),
width = wh.x,
height = wh.y,
left = -(width / 2),
@@ -360,7 +358,7 @@
top - scaleOffset - strokeWidth2 - padding);
// bottom-left
- this._drawControl('tr', ctx, methodName,
+ this._drawControl('bl', ctx, methodName,
left - scaleOffset - strokeWidth2 - padding,
top + height + scaleOffsetSize + strokeWidth2 + padding);
@@ -382,7 +380,7 @@
top + height + scaleOffsetSize + strokeWidth2 + padding);
// middle-right
- this._drawControl('mb', ctx, methodName,
+ this._drawControl('mr', ctx, methodName,
left + width + scaleOffsetSize + strokeWidth2 + padding,
top + height/2 - scaleOffset);
@@ -471,15 +469,15 @@
_getControlsVisibility: function() {
if (!this._controlsVisibility) {
this._controlsVisibility = {
- tl: true,
- tr: true,
- br: true,
- bl: true,
- ml: true,
- mt: true,
- mr: true,
- mb: true,
- mtr: true
+ tl: true,
+ tr: true,
+ br: true,
+ bl: true,
+ ml: true,
+ mt: true,
+ mr: true,
+ mb: true,
+ mtr: true
};
}
return this._controlsVisibility;
diff --git a/src/mixins/object_origin.mixin.js b/src/mixins/object_origin.mixin.js
index 102b170a..fbcce105 100644
--- a/src/mixins/object_origin.mixin.js
+++ b/src/mixins/object_origin.mixin.js
@@ -16,17 +16,17 @@
cy = point.y,
strokeWidth = this.stroke ? this.strokeWidth : 0;
- if (originX === "left") {
+ if (originX === 'left') {
cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -47,16 +47,16 @@
strokeWidth = this.stroke ? this.strokeWidth : 0;
// Get the point coordinates
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
@@ -106,20 +106,20 @@
x, y;
if (originX && originY) {
- if (originX === "left") {
+ if (originX === 'left') {
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
- else if (originX === "right") {
+ else if (originX === 'right') {
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
}
else {
x = center.x;
}
- if (originY === "top") {
+ if (originY === 'top') {
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
- else if (originY === "bottom") {
+ else if (originY === 'bottom') {
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
}
else {
@@ -152,8 +152,8 @@
* @return {void}
*/
setPositionByOrigin: function(pos, originX, originY) {
- var center = this.translateToCenterPoint(pos, originX, originY);
- var position = this.translateToOriginPoint(center, this.originX, this.originY);
+ var center = this.translateToCenterPoint(pos, originX, originY),
+ position = this.translateToOriginPoint(center, this.originX, this.originY);
this.set('left', position.x);
this.set('top', position.y);
@@ -163,13 +163,13 @@
* @param {String} to One of 'left', 'center', 'right'
*/
adjustPosition: function(to) {
- var angle = degreesToRadians(this.angle);
- var hypotHalf = this.getWidth() / 2;
- var xHalf = Math.cos(angle) * hypotHalf;
- var yHalf = Math.sin(angle) * hypotHalf;
- var hypotFull = this.getWidth();
- var xFull = Math.cos(angle) * hypotFull;
- var yFull = Math.sin(angle) * hypotFull;
+ var angle = degreesToRadians(this.angle),
+ hypotHalf = this.getWidth() / 2,
+ xHalf = Math.cos(angle) * hypotHalf,
+ yHalf = Math.sin(angle) * hypotHalf,
+ hypotFull = this.getWidth(),
+ xFull = Math.cos(angle) * hypotFull,
+ yFull = Math.sin(angle) * hypotFull;
if (this.originX === 'center' && to === 'left' ||
this.originX === 'right' && to === 'center') {
@@ -198,6 +198,45 @@
this.originX = to;
},
+ /**
+ * @private
+ * Sets the origin/position of the object to it's center point
+ * @return {void}
+ */
+ _setOriginToCenter: function() {
+ this._originalOriginX = this.originX;
+ this._originalOriginY = this.originY;
+
+ var center = this.getCenterPoint();
+
+ this.originX = 'center';
+ this.originY = 'center';
+
+ this.left = center.x;
+ this.top = center.y;
+ },
+
+ /**
+ * @private
+ * Resets the origin/position of the object to it's original origin
+ * @return {void}
+ */
+ _resetOrigin: function() {
+ var originPoint = this.translateToOriginPoint(
+ this.getCenterPoint(),
+ this._originalOriginX,
+ this._originalOriginY);
+
+ this.originX = this._originalOriginX;
+ this.originY = this._originalOriginY;
+
+ this.left = originPoint.x;
+ this.top = originPoint.y;
+
+ this._originalOriginX = null;
+ this._originalOriginY = null;
+ },
+
/**
* @private
*/
diff --git a/src/mixins/object_straightening.mixin.js b/src/mixins/object_straightening.mixin.js
index 1ec7ce23..0c3e30aa 100644
--- a/src/mixins/object_straightening.mixin.js
+++ b/src/mixins/object_straightening.mixin.js
@@ -7,9 +7,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
_getAngleValueForStraighten: function() {
var angle = this.getAngle() % 360;
if (angle > 0) {
- return Math.round((angle-1)/90) * 90;
+ return Math.round((angle - 1) / 90) * 90;
}
- return Math.round(angle/90) * 90;
+ return Math.round(angle / 90) * 90;
},
/**
diff --git a/src/node.js b/src/node.js
index 4bb44fab..5f344400 100644
--- a/src/node.js
+++ b/src/node.js
@@ -4,7 +4,7 @@
return;
}
- var DOMParser = new require('xmldom').DOMParser,
+ var DOMParser = require('xmldom').DOMParser,
URL = require('url'),
HTTP = require('http'),
HTTPS = require('https'),
@@ -22,27 +22,26 @@
}
// assign request handler based on protocol
- var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP;
-
- var req = reqHandler.request({
- hostname: oURL.hostname,
- port: oURL.port,
- path: oURL.path,
- method: 'GET'
- }, function(response){
- var body = "";
- if (encoding) {
- response.setEncoding(encoding);
- }
- response.on('end', function () {
- callback(body);
- });
- response.on('data', function (chunk) {
- if (response.statusCode === 200) {
- body += chunk;
- }
- });
- });
+ var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP,
+ req = reqHandler.request({
+ hostname: oURL.hostname,
+ port: oURL.port,
+ path: oURL.path,
+ method: 'GET'
+ }, function(response) {
+ var body = '';
+ if (encoding) {
+ response.setEncoding(encoding);
+ }
+ response.on('end', function () {
+ callback(body);
+ });
+ response.on('data', function (chunk) {
+ if (response.statusCode === 200) {
+ body += chunk;
+ }
+ });
+ });
req.on('error', function(err) {
if (err.errno === process.ECONNREFUSED) {
@@ -57,32 +56,33 @@
}
/** @private */
- function request_fs(path, callback){
+ function requestFs(path, callback){
var fs = require('fs');
fs.readFile(path, function (err, data) {
if (err) {
fabric.log(err);
throw err;
- } else {
+ }
+ else {
callback(data);
}
});
}
fabric.util.loadImage = function(url, callback, context) {
- var createImageAndCallBack = function(data){
+ function createImageAndCallBack(data) {
img.src = new Buffer(data, 'binary');
// preserving original url, which seems to be lost in node-canvas
img._src = url;
callback && callback.call(context, img);
- };
+ }
var img = new Image();
if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
img.src = img._src = url;
callback && callback.call(context, img);
}
else if (url && url.indexOf('http') !== 0) {
- request_fs(url, createImageAndCallBack);
+ requestFs(url, createImageAndCallBack);
}
else if (url) {
request(url, 'binary', createImageAndCallBack);
@@ -95,8 +95,8 @@
fabric.loadSVGFromURL = function(url, callback, reviver) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
if (url.indexOf('http') !== 0) {
- request_fs(url, function(body) {
- fabric.loadSVGFromString(body, callback, reviver);
+ requestFs(url, function(body) {
+ fabric.loadSVGFromString(body.toString(), callback, reviver);
});
}
else {
@@ -136,12 +136,15 @@
* Only available when running fabric on node.js
* @param width Canvas width
* @param height Canvas height
+ * @param {Object} options to pass to FabricCanvas.
+ * @param {Object} options to pass to NodeCanvas.
* @return {Object} wrapped canvas instance
*/
- fabric.createCanvasForNode = function(width, height) {
+ fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
+ nodeCanvasOptions = nodeCanvasOptions || options;
var canvasEl = fabric.document.createElement('canvas'),
- nodeCanvas = new Canvas(width || 600, height || 600);
+ nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
// jsdom doesn't create style on canvas element, so here be temp. workaround
canvasEl.style = { };
@@ -149,8 +152,9 @@
canvasEl.width = nodeCanvas.width;
canvasEl.height = nodeCanvas.height;
- var FabricCanvas = fabric.Canvas || fabric.StaticCanvas;
- var fabricCanvas = new FabricCanvas(canvasEl);
+ var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
+ fabricCanvas = new FabricCanvas(canvasEl, options);
+
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
fabricCanvas.nodeCanvas = nodeCanvas;
fabricCanvas.Font = Canvas.Font;
diff --git a/src/parser.js b/src/parser.js
index 7b5172b3..ff9ba93f 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
/**
* @name fabric
@@ -12,34 +12,37 @@
capitalize = fabric.util.string.capitalize,
clone = fabric.util.object.clone,
toFixed = fabric.util.toFixed,
- multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
+ multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
- var attributesMap = {
- 'fill-opacity': 'fillOpacity',
- 'fill-rule': 'fillRule',
- 'font-family': 'fontFamily',
- 'font-size': 'fontSize',
- 'font-style': 'fontStyle',
- 'font-weight': 'fontWeight',
- 'cx': 'left',
- 'x': 'left',
- 'r': 'radius',
- 'stroke-dasharray': 'strokeDashArray',
- 'stroke-linecap': 'strokeLineCap',
- 'stroke-linejoin': 'strokeLineJoin',
- 'stroke-miterlimit':'strokeMiterLimit',
- 'stroke-opacity': 'strokeOpacity',
- 'stroke-width': 'strokeWidth',
- 'text-decoration': 'textDecoration',
- 'cy': 'top',
- 'y': 'top',
- 'transform': 'transformMatrix'
- };
+ attributesMap = {
+ cx: 'left',
+ x: 'left',
+ r: 'radius',
+ cy: 'top',
+ y: 'top',
+ display: 'visible',
+ visibility: 'visible',
+ transform: 'transformMatrix',
+ 'fill-opacity': 'fillOpacity',
+ 'fill-rule': 'fillRule',
+ 'font-family': 'fontFamily',
+ 'font-size': 'fontSize',
+ 'font-style': 'fontStyle',
+ 'font-weight': 'fontWeight',
+ 'stroke-dasharray': 'strokeDashArray',
+ 'stroke-linecap': 'strokeLineCap',
+ 'stroke-linejoin': 'strokeLineJoin',
+ 'stroke-miterlimit': 'strokeMiterLimit',
+ 'stroke-opacity': 'strokeOpacity',
+ 'stroke-width': 'strokeWidth',
+ 'text-decoration': 'textDecoration',
+ 'text-anchor': 'originX'
+ },
- var colorAttributes = {
- 'stroke': 'strokeOpacity',
- 'fill': 'fillOpacity'
- };
+ colorAttributes = {
+ stroke: 'strokeOpacity',
+ fill: 'fillOpacity'
+ };
function normalizeAttr(attr) {
// transform attribute names
@@ -70,6 +73,16 @@
value = fabric.parseTransformAttribute(value);
}
}
+ else if (attr === 'visible') {
+ value = (value === 'none' || value === 'hidden') ? false : true;
+ // display=none on parent element always takes precedence over child element
+ if (parentAttributes.visible === false) {
+ value = false;
+ }
+ }
+ else if (attr === 'originX' /* text-anchor */) {
+ value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
+ }
isArray = Object.prototype.toString.call(value) === '[object Array]';
@@ -150,30 +163,30 @@
],
// == begin transform regexp
- number = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)',
+ number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- comma_wsp = '(?:\\s+,?\\s*|,\\s*)',
+ commaWsp = '(?:\\s+,?\\s*|,\\s*)',
skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + ')' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + ')' +
+ commaWsp + '(' + number + '))?\\s*\\))',
scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
- comma_wsp + '(' + number + '))?\\s*\\))',
+ commaWsp + '(' + number + '))?\\s*\\))',
matrix = '(?:(matrix)\\s*\\(\\s*' +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
- '(' + number + ')' + comma_wsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
+ '(' + number + ')' + commaWsp +
'(' + number + ')' +
'\\s*\\))',
@@ -186,12 +199,12 @@
skewY +
')',
- transforms = '(?:' + transform + '(?:' + comma_wsp + transform + ')*' + ')',
+ transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
- transform_list = '^\\s*(?:' + transforms + '?)\\s*$',
+ transformList = '^\\s*(?:' + transforms + '?)\\s*$',
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
- reTransformList = new RegExp(transform_list),
+ reTransformList = new RegExp(transformList),
// == end transform regexp
reTransform = new RegExp(transform, 'g');
@@ -199,8 +212,8 @@
return function(attributeValue) {
// start with identity matrix
- var matrix = iMatrix.concat();
- var matrices = [ ];
+ var matrix = iMatrix.concat(),
+ matrices = [ ];
// return if no argument was given or
// an argument does not match transform attribute regexp
@@ -216,11 +229,12 @@
operation = m[1],
args = m.slice(2).map(parseFloat);
- switch(operation) {
+ switch (operation) {
case 'translate':
translateMatrix(matrix, args);
break;
case 'rotate':
+ args[0] = fabric.util.degreesToRadians(args[0]);
rotateMatrix(matrix, args);
break;
case 'scale':
@@ -259,19 +273,19 @@
if (!match) return;
- var fontStyle = match[1];
- // Font variant is not used
- // var fontVariant = match[2];
- var fontWeight = match[3];
- var fontSize = match[4];
- var lineHeight = match[5];
- var fontFamily = match[6];
+ var fontStyle = match[1],
+ // font variant is not used
+ // fontVariant = match[2],
+ fontWeight = match[3],
+ fontSize = match[4],
+ lineHeight = match[5],
+ fontFamily = match[6];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
- oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
+ oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseFloat(fontSize);
@@ -359,22 +373,22 @@
*/
fabric.parseSVGDocument = (function() {
- var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/;
+ var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
- // \d doesn't quite cut it (as we need to match an actual float number)
+ // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+ // \d doesn't quite cut it (as we need to match an actual float number)
- // matches, e.g.: +14.56e-12, etc.
- var reNum = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)';
+ // matches, e.g.: +14.56e-12, etc.
+ reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
- var reViewBoxAttrValue = new RegExp(
- '^' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*' +
- '$'
- );
+ reViewBoxAttrValue = new RegExp(
+ '^' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*,?' +
+ '\\s*(' + reNum + '+)\\s*' +
+ '$'
+ );
function hasAncestorWithNodeName(element, nodeName) {
while (element && (element = element.parentNode)) {
@@ -391,10 +405,10 @@
var startTime = new Date(),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
- if (descendants.length === 0) {
+ if (descendants.length === 0 && fabric.isLikelyNode) {
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
// https://github.com/ajaxorg/node-o3-xml/issues/21
- descendants = doc.selectNodes("//*[name(.)!='svg']");
+ descendants = doc.selectNodes('//*[name(.)!="svg"]');
var arr = [ ];
for (var i = 0, len = descendants.length; i < len; i++) {
arr[i] = descendants[i];
@@ -407,30 +421,43 @@
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
});
- if (!elements || (elements && !elements.length)) return;
+ if (!elements || (elements && !elements.length)) {
+ callback && callback([], {});
+ return;
+ }
var viewBoxAttr = doc.getAttribute('viewBox'),
- widthAttr = doc.getAttribute('width'),
- heightAttr = doc.getAttribute('height'),
+ widthAttr = parseFloat(doc.getAttribute('width')),
+ heightAttr = parseFloat(doc.getAttribute('height')),
width = null,
height = null,
+ viewBoxWidth,
+ viewBoxHeight,
minX,
minY;
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
- minX = parseInt(viewBoxAttr[1], 10);
- minY = parseInt(viewBoxAttr[2], 10);
- width = parseInt(viewBoxAttr[3], 10);
- height = parseInt(viewBoxAttr[4], 10);
+ minX = parseFloat(viewBoxAttr[1]);
+ minY = parseFloat(viewBoxAttr[2]);
+ viewBoxWidth = parseFloat(viewBoxAttr[3]);
+ viewBoxHeight = parseFloat(viewBoxAttr[4]);
}
- // values of width/height attributes overwrite those extracted from viewbox attribute
- width = widthAttr ? parseFloat(widthAttr) : width;
- height = heightAttr ? parseFloat(heightAttr) : height;
+ if (viewBoxWidth && widthAttr && viewBoxWidth !== widthAttr) {
+ width = viewBoxWidth;
+ height = viewBoxHeight;
+ }
+ else {
+ // values of width/height attributes overwrite those extracted from viewbox attribute
+ width = widthAttr ? widthAttr : viewBoxWidth;
+ height = heightAttr ? heightAttr : viewBoxHeight;
+ }
var options = {
width: width,
- height: height
+ height: height,
+ widthAttr: widthAttr,
+ heightAttr: heightAttr
};
fabric.gradientDefs = fabric.getGradientDefs(doc);
@@ -614,7 +641,7 @@
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
*/
parseElements: function(elements, callback, options, reviver) {
- fabric.ElementsParser.parse(elements, callback, options, reviver);
+ new fabric.ElementsParser(elements, callback, options, reviver).parse();
},
/**
@@ -628,7 +655,9 @@
var oStyle = { },
style = element.getAttribute('style');
- if (!style) return oStyle;
+ if (!style) {
+ return oStyle;
+ }
if (typeof style === 'string') {
parseStyleString(style, oStyle);
@@ -664,21 +693,27 @@
len = points.length;
for (; i < len; i++) {
var pair = points[i].split(',');
- parsedPoints.push({ x: parseFloat(pair[0]), y: parseFloat(pair[1]) });
+ parsedPoints.push({
+ x: parseFloat(pair[0]),
+ y: parseFloat(pair[1])
+ });
}
}
else {
i = 0;
len = points.length;
for (; i < len; i+=2) {
- parsedPoints.push({ x: parseFloat(points[i]), y: parseFloat(points[i+1]) });
+ parsedPoints.push({
+ x: parseFloat(points[i]),
+ y: parseFloat(points[i + 1])
+ });
}
}
// odd number of points is an error
- if (parsedPoints.length % 2 !== 0) {
+ // if (parsedPoints.length % 2 !== 0) {
// return null;
- }
+ // }
return parsedPoints;
},
@@ -758,13 +793,13 @@
function onComplete(r) {
var xml = r.responseXML;
- if (!xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
+ if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
xml = new ActiveXObject('Microsoft.XMLDOM');
xml.async = 'false';
//IE chokes on DOCTYPE
xml.loadXML(r.responseText.replace(//i,''));
}
- if (!xml.documentElement) return;
+ if (!xml || !xml.documentElement) return;
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
svgCache.set(url, {
diff --git a/src/pattern.class.js b/src/pattern.class.js
index c89d371a..7d1cb04c 100644
--- a/src/pattern.class.js
+++ b/src/pattern.class.js
@@ -101,10 +101,10 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {String} SVG representation of a pattern
*/
toSVG: function(object) {
- var patternSource = typeof this.source === 'function' ? this.source() : this.source;
- var patternWidth = patternSource.width / object.getWidth();
- var patternHeight = patternSource.height / object.getHeight();
- var patternImgSrc = '';
+ var patternSource = typeof this.source === 'function' ? this.source() : this.source,
+ patternWidth = patternSource.width / object.getWidth(),
+ patternHeight = patternSource.height / object.getHeight(),
+ patternImgSrc = '';
if (patternSource.src) {
patternImgSrc = patternSource.src;
@@ -133,11 +133,23 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @return {CanvasPattern}
*/
toLive: function(ctx) {
- var source = typeof this.source === 'function' ? this.source() : this.source;
+ var source = typeof this.source === 'function'
+ ? this.source()
+ : this.source;
+
+ // if the image failed to load, return, and allow rest to continue loading
+ if (!source) {
+ return '';
+ }
+
// if an image
if (typeof source.src !== 'undefined') {
- if (!source.complete) return '';
- if (source.naturalWidth === 0 || source.naturalHeight === 0) return '';
+ if (!source.complete) {
+ return '';
+ }
+ if (source.naturalWidth === 0 || source.naturalHeight === 0) {
+ return '';
+ }
}
return ctx.createPattern(source, this.repeat);
}
diff --git a/src/point.class.js b/src/point.class.js
index 7ae1f776..727be608 100644
--- a/src/point.class.js
+++ b/src/point.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
@@ -250,7 +250,7 @@
* @return {String}
*/
toString: function () {
- return this.x + "," + this.y;
+ return this.x + ',' + this.y;
},
/**
diff --git a/src/shadow.class.js b/src/shadow.class.js
index 46b797b5..47ac4d5e 100644
--- a/src/shadow.class.js
+++ b/src/shadow.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -81,9 +81,8 @@
* @return {Object} Shadow object with color, offsetX, offsetY and blur
*/
_parseShadow: function(shadow) {
- var shadowStr = shadow.trim();
-
- var offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
+ var shadowStr = shadow.trim(),
+ offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
return {
diff --git a/src/shapes/circle.class.js b/src/shapes/circle.class.js
index b6390865..7abea87a 100644
--- a/src/shapes/circle.class.js
+++ b/src/shapes/circle.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
piBy2 = Math.PI * 2,
@@ -26,6 +26,13 @@
*/
type: 'circle',
+ /**
+ * Radius of this circle
+ * @type Number
+ * @default
+ */
+ radius: 0,
+
/**
* Constructor
* @param {Object} [options] Options object
@@ -99,7 +106,7 @@
ctx.closePath();
this._renderFill(ctx);
- this._renderStroke(ctx);
+ this.stroke && this._renderStroke(ctx);
},
/**
diff --git a/src/shapes/ellipse.class.js b/src/shapes/ellipse.class.js
index fd58efac..59c1c41f 100644
--- a/src/shapes/ellipse.class.js
+++ b/src/shapes/ellipse.class.js
@@ -1,6 +1,6 @@
(function(global){
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
piBy2 = Math.PI * 2,
@@ -116,10 +116,10 @@
}
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.rx, 0, piBy2, false);
+ ctx.restore();
this._renderFill(ctx);
this._renderStroke(ctx);
- ctx.restore();
},
/**
@@ -151,9 +151,9 @@
fabric.Ellipse.fromElement = function(element, options) {
options || (options = { });
- var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
- var cx = parsedAttributes.left;
- var cy = parsedAttributes.top;
+ var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES),
+ cx = parsedAttributes.left,
+ cy = parsedAttributes.top;
if ('left' in parsedAttributes) {
parsedAttributes.left -= (options.width / 2) || 0;
diff --git a/src/shapes/group.class.js b/src/shapes/group.class.js
index aa2e5786..6449911d 100644
--- a/src/shapes/group.class.js
+++ b/src/shapes/group.class.js
@@ -1,6 +1,6 @@
(function(global){
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -232,7 +232,7 @@
* @private
*/
_renderObject: function(object, ctx) {
- var v = this.canvas.viewportTransform,
+ var v = this.getViewportTransform(),
sxy = fabric.util.transformPoint(
new fabric.Point(this.scaleX, this.scaleY),
v,
@@ -409,11 +409,10 @@
*/
_setOpacityIfSame: function() {
var objects = this.getObjects(),
- firstValue = objects[0] ? objects[0].get('opacity') : 1;
-
- var isSameOpacity = objects.every(function(o) {
- return o.get('opacity') === firstValue;
- });
+ firstValue = objects[0] ? objects[0].get('opacity') : 1,
+ isSameOpacity = objects.every(function(o) {
+ return o.get('opacity') === firstValue;
+ });
if (isSameOpacity) {
this.opacity = firstValue;
@@ -423,7 +422,7 @@
/**
* @private
*/
- _calcBounds: function() {
+ _calcBounds: function(onlyWidthHeight) {
var aX = [],
aY = [],
o;
@@ -437,26 +436,29 @@
}
}
- this.set(this._getBounds(aX, aY));
+ this.set(this._getBounds(aX, aY, onlyWidthHeight));
},
/**
* @private
*/
- _getBounds: function(aX, aY) {
+ _getBounds: function(aX, aY, onlyWidthHeight) {
var ivt;
if (this.canvas) {
- ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
+ ivt = fabric.util.invertTransform(this.getViewportTransform());
}
var minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
- maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt);
+ maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
+ obj = {
+ width: (maxXY.x - minXY.x) || 0,
+ height: (maxXY.y - minXY.y) || 0
+ };
- return {
- width: (maxXY.x - minXY.x) || 0,
- height: (maxXY.y - minXY.y) || 0,
- left: (minXY.x + maxXY.x) / 2 || 0,
- top: (minXY.y + maxXY.y) / 2 || 0,
- };
+ if (!onlyWidthHeight) {
+ obj.left = (minXY.x + maxXY.x) / 2 || 0;
+ obj.top = (minXY.y + maxXY.y) / 2 || 0;
+ }
+ return obj;
},
/* _TO_SVG_START_ */
diff --git a/src/shapes/image.class.js b/src/shapes/image.class.js
index 65c41f1c..c485042a 100644
--- a/src/shapes/image.class.js
+++ b/src/shapes/image.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var extend = fabric.util.object.extend;
@@ -122,17 +122,15 @@
if (!this.visible) return;
ctx.save();
- var m = this.transformMatrix;
- var v;
- v = this.canvas.viewportTransform;
-
- var isInPathGroup = this.group && this.group.type === 'path-group';
+ var m = this.transformMatrix,
+ v = this.getViewportTransform(),
+ isInPathGroup = this.group && this.group.type === 'path-group';
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
// this._resetWidthHeight();
if (isInPathGroup) {
- ctx.translate(-this.group.width/2 + this.width/2, -this.group.height/2 + this.height/2);
+ ctx.translate(-this.group.width/2, -this.group.height/2);
}
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -140,6 +138,9 @@
if (!noTransform) {
this.transform(ctx);
}
+ if (isInPathGroup) {
+ ctx.translate(this.width/2, this.height/2);
+ }
ctx.save();
this._setShadow(ctx);
@@ -183,10 +184,10 @@
this._setStrokeStyles(ctx);
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
ctx.closePath();
ctx.restore();
},
@@ -253,7 +254,9 @@
* @return {String} Source of an image
*/
getSrc: function() {
- return this.getElement().src || this.getElement()._src;
+ if (this.getElement()) {
+ return this.getElement().src || this.getElement()._src;
+ }
},
/**
@@ -282,6 +285,10 @@
*/
applyFilters: function(callback) {
+ if (!this._originalElement) {
+ return;
+ }
+
if (this.filters.length === 0) {
this._element = this._originalElement;
callback && callback();
@@ -331,13 +338,13 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
- ctx.drawImage(
- this._element,
- -this.width / 2,
- -this.height / 2,
- this.width,
- this.height
- );
+ this._element && ctx.drawImage(
+ this._element,
+ -this.width / 2,
+ -this.height / 2,
+ this.width,
+ this.height
+ );
},
/**
@@ -369,7 +376,9 @@
options || (options = { });
this.setOptions(options);
this._setWidthHeight(options);
- this._element.crossOrigin = this.crossOrigin;
+ if (this._element && this.crossOrigin) {
+ this._element.crossOrigin = this.crossOrigin;
+ }
},
/**
@@ -395,11 +404,15 @@
_setWidthHeight: function(options) {
this.width = 'width' in options
? options.width
- : (this.getElement().width || 0);
+ : (this.getElement()
+ ? this.getElement().width || 0
+ : 0);
this.height = 'height' in options
? options.height
- : (this.getElement().height || 0);
+ : (this.getElement()
+ ? this.getElement().height || 0
+ : 0);
},
/**
@@ -417,7 +430,7 @@
* @type String
* @default
*/
- fabric.Image.CSS_CANVAS = "canvas-img";
+ fabric.Image.CSS_CANVAS = 'canvas-img';
/**
* Alias for getSrc
diff --git a/src/shapes/itext.class.js b/src/shapes/itext.class.js
index 4e0ca63a..ca66b3ec 100644
--- a/src/shapes/itext.class.js
+++ b/src/shapes/itext.class.js
@@ -8,9 +8,9 @@
* @extends fabric.Text
* @mixes fabric.Observable
*
- * @fires text:changed
- * @fires editing:entered
- * @fires editing:exited
+ * @fires changed ("text:changed" when observing canvas)
+ * @fires editing:entered ("text:editing:entered" when observing canvas)
+ * @fires editing:exited ("text:editing:exited" when observing canvas)
*
* @return {fabric.IText} thisArg
* @see {@link fabric.IText#initialize} for constructor definition
@@ -33,6 +33,7 @@
* Copy text: ctrl/cmd + c
* Paste text: ctrl/cmd + v
* Cut text: ctrl/cmd + x
+ * Select entire text: ctrl/cmd + a
*
*
* Supported mouse/touch combination
@@ -217,6 +218,9 @@
* @param {Number} index Index to set selection start to
*/
setSelectionStart: function(index) {
+ if (this.selectionStart !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionStart = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index);
},
@@ -226,6 +230,9 @@
* @param {Number} index Index to set selection end to
*/
setSelectionEnd: function(index) {
+ if (this.selectionEnd !== index) {
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ }
this.selectionEnd = index;
this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index);
},
@@ -289,9 +296,9 @@
},
/**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- */
+ * @private
+ * @param {CanvasRenderingContext2D} ctx Context to render on
+ */
_render: function(ctx) {
this.callSuper('_render', ctx);
this.ctx = ctx;
@@ -325,8 +332,8 @@
if (typeof selectionStart === 'undefined') {
selectionStart = this.selectionStart;
}
- var textBeforeCursor = this.text.slice(0, selectionStart);
- var linesBeforeCursor = textBeforeCursor.split(this._reNewline);
+ var textBeforeCursor = this.text.slice(0, selectionStart),
+ linesBeforeCursor = textBeforeCursor.split(this._reNewline);
return {
lineIndex: linesBeforeCursor.length - 1,
@@ -334,6 +341,26 @@
};
},
+ /**
+ * Returns complete style of char at the current cursor
+ * @param {Number} lineIndex Line index
+ * @param {Number} charIndex Char index
+ * @return {Object} Character style
+ */
+ getCurrentCharStyle: function(lineIndex, charIndex) {
+ var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
+
+ return {
+ fontSize: style && style.fontSize || this.fontSize,
+ fill: style && style.fill || this.fill,
+ textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
+ textDecoration: style && style.textDecoration || this.textDecoration,
+ fontFamily: style && style.fontFamily || this.fontFamily,
+ stroke: style && style.stroke || this.stroke,
+ strokeWidth: style && style.strokeWidth || this.strokeWidth
+ };
+ },
+
/**
* Returns fontSize of char at the current cursor
* @param {Number} lineIndex Line index
@@ -480,13 +507,16 @@
var cursorLocation = this.get2DCursorLocation(),
lineIndex = cursorLocation.lineIndex,
charIndex = cursorLocation.charIndex,
- charHeight = this.getCurrentCharFontSize(lineIndex, charIndex);
+ charHeight = this.getCurrentCharFontSize(lineIndex, charIndex),
+ leftOffset = (lineIndex === 0 && charIndex === 0)
+ ? this._getCachedLineOffset(lineIndex, this.text.split(this._reNewline))
+ : boundaries.leftOffset;
ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex);
- ctx.globalAlpha = this._currentCursorOpacity;
+ ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
ctx.fillRect(
- boundaries.left + boundaries.leftOffset,
+ boundaries.left + leftOffset,
boundaries.top + boundaries.topOffset,
this.cursorWidth / this.scaleX,
charHeight);
@@ -506,39 +536,43 @@
ctx.fillStyle = this.selectionColor;
- var cursorLocation = this.get2DCursorLocation(),
- lineIndex = cursorLocation.lineIndex,
- charIndex = cursorLocation.charIndex,
- textLines = this.text.split(this._reNewline),
- origLineIndex = lineIndex;
+ var start = this.get2DCursorLocation(this.selectionStart),
+ end = this.get2DCursorLocation(this.selectionEnd),
+ startLine = start.lineIndex,
+ endLine = end.lineIndex,
+ textLines = this.text.split(this._reNewline);
- for (var i = this.selectionStart; i < this.selectionEnd; i++) {
+ for (var i = startLine; i <= endLine; i++) {
+ var lineOffset = this._getCachedLineOffset(i, textLines) || 0,
+ lineHeight = this._getCachedLineHeight(i),
+ boxWidth = 0;
- if (chars[i] === '\n') {
- boundaries.leftOffset = 0;
- boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex);
- lineIndex++;
- charIndex = 0;
- }
- else if (i !== this.text.length) {
-
- var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex),
- lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0;
-
- if (lineIndex === origLineIndex) {
- // only offset the line if we're rendering selection of 2nd, 3rd, etc. line
- lineOffset = 0;
+ if (i === startLine) {
+ for (var j = 0, len = textLines[i].length; j < len; j++) {
+ if (j >= start.charIndex && (i !== endLine || j < end.charIndex)) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
+ if (j < start.charIndex) {
+ lineOffset += this._getWidthOfChar(ctx, textLines[i][j], i, j);
+ }
}
-
- ctx.fillRect(
- boundaries.left + boundaries.leftOffset + lineOffset,
- boundaries.top + boundaries.topOffset,
- charWidth,
- this._getHeightOfLine(ctx, lineIndex));
-
- boundaries.leftOffset += charWidth;
- charIndex++;
}
+ else if (i > startLine && i < endLine) {
+ boxWidth += this._getCachedLineWidth(i, textLines) || 5;
+ }
+ else if (i === endLine) {
+ for (var j2 = 0, j2len = end.charIndex; j2 < j2len; j2++) {
+ boxWidth += this._getWidthOfChar(ctx, textLines[i][j2], i, j2);
+ }
+ }
+
+ ctx.fillRect(
+ boundaries.left + lineOffset,
+ boundaries.top + boundaries.topOffset,
+ boxWidth,
+ lineHeight);
+
+ boundaries.topOffset += lineHeight;
}
ctx.restore();
},
@@ -568,14 +602,26 @@
lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines),
lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines),
lineLeftOffset = this._getLineLeftOffset(lineWidth),
- chars = line.split('');
+ chars = line.split(''),
+ prevStyle,
+ charsToRender = '';
left += lineLeftOffset || 0;
ctx.save();
- for (var i = 0, len = chars.length; i < len; i++) {
- this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight);
+
+ for (var i = 0, len = chars.length; i <= len; i++) {
+ prevStyle = prevStyle || this.getCurrentCharStyle(lineIndex, i);
+ var thisStyle = this.getCurrentCharStyle(lineIndex, i + 1);
+
+ if (this._hasStyleChanged(prevStyle, thisStyle) || i === len) {
+ this._renderChar(method, ctx, lineIndex, i - 1, charsToRender, left, top, lineHeight);
+ charsToRender = '';
+ prevStyle = thisStyle;
+ }
+ charsToRender += chars[i];
}
+
ctx.restore();
},
@@ -638,6 +684,22 @@
}
},
+ /**
+ * @private
+ * @param {Object} prevStyle
+ * @param {Object} thisStyle
+ */
+ _hasStyleChanged: function(prevStyle, thisStyle) {
+ return (prevStyle.fill !== thisStyle.fill ||
+ prevStyle.fontSize !== thisStyle.fontSize ||
+ prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
+ prevStyle.textDecoration !== thisStyle.textDecoration ||
+ prevStyle.fontFamily !== thisStyle.fontFamily ||
+ prevStyle.stroke !== thisStyle.stroke ||
+ prevStyle.strokeWidth !== thisStyle.strokeWidth
+ );
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -645,10 +707,10 @@
_renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight, charHeight) {
var textDecoration = styleDeclaration
- ? (styleDeclaration.textDecoration || this.textDecoration)
- : this.textDecoration;
+ ? (styleDeclaration.textDecoration || this.textDecoration)
+ : this.textDecoration,
- var fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
+ fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
if (!textDecoration) return;
@@ -868,15 +930,35 @@
}
},
+ /**
+ * @private
+ * @param {Number} lineIndex
+ * @param {Number} charIndex
+ */
+ _getStyleDeclaration: function(lineIndex, charIndex) {
+ return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
+ ? clone(this.styles[lineIndex][charIndex])
+ : { };
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
- ctx.save();
- var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
- ctx.restore();
- return width;
+ var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
+ this._applyFontStyles(styleDeclaration);
+ var cacheProp = this._getCacheProp(_char, styleDeclaration);
+
+ if (this._charWidthsCache[cacheProp] && this.caching) {
+ return this._charWidthsCache[cacheProp];
+ }
+ else if (ctx) {
+ ctx.save();
+ var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
+ ctx.restore();
+ return width;
+ }
},
/**
@@ -962,10 +1044,9 @@
textLines = textLines || this.text.split(this._reNewline);
- var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0);
-
- var line = textLines[lineIndex];
- var chars = line.split('');
+ var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0),
+ line = textLines[lineIndex],
+ chars = line.split('');
for (var i = 1, len = chars.length; i < len; i++) {
var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i);
@@ -998,6 +1079,26 @@
return topOffset - (this.fontSize / this._fontSizeFraction);
},
+ /**
+ * @private
+ * This method is overwritten to account for different top offset
+ */
+ _renderTextBoxBackground: function(ctx) {
+ if (!this.backgroundColor) return;
+
+ ctx.save();
+ ctx.fillStyle = this.backgroundColor;
+
+ ctx.fillRect(
+ this._getLeftOffset(),
+ this._getTopOffset() + (this.fontSize / this._fontSizeFraction),
+ this.width,
+ this.height
+ );
+
+ ctx.restore();
+ },
+
/**
* Returns object representation of an instance
* @methd toObject
@@ -1022,6 +1123,12 @@
return new fabric.IText(object.text, clone(object));
};
+ /**
+ * Contains all fabric.IText objects that have been created
+ * @static
+ * @memberof fabric.IText
+ * @type Array
+ */
fabric.IText.instances = [ ];
})();
diff --git a/src/shapes/line.class.js b/src/shapes/line.class.js
index 321ec458..52309ed9 100644
--- a/src/shapes/line.class.js
+++ b/src/shapes/line.class.js
@@ -1,10 +1,10 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
- coordProps = { 'x1': 1, 'x2': 1, 'y1': 1, 'y2': 1 },
+ coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
if (fabric.Line) {
@@ -27,6 +27,34 @@
*/
type: 'line',
+ /**
+ * x value or first line edge
+ * @type Number
+ * @default
+ */
+ x1: 0,
+
+ /**
+ * y value or first line edge
+ * @type Number
+ * @default
+ */
+ y1: 0,
+
+ /**
+ * x value or second line edge
+ * @type Number
+ * @default
+ */
+ x2: 0,
+
+ /**
+ * y value or second line edge
+ * @type Number
+ * @default
+ */
+ y2: 0,
+
/**
* Constructor
* @param {Array} [points] Array of points
@@ -57,11 +85,16 @@
_setWidthHeight: function(options) {
options || (options = { });
- this.set('width', Math.abs(this.x2 - this.x1) || 1);
- this.set('height', Math.abs(this.y2 - this.y1) || 1);
+ this.width = Math.abs(this.x2 - this.x1) || 1;
+ this.height = Math.abs(this.y2 - this.y1) || 1;
- this.set('left', 'left' in options ? options.left : (Math.min(this.x1, this.x2) + this.width / 2));
- this.set('top', 'top' in options ? options.top : (Math.min(this.y1, this.y2) + this.height / 2));
+ this.left = 'left' in options
+ ? options.left
+ : this._getLeftToOriginX();
+
+ this.top = 'top' in options
+ ? options.top
+ : this._getTopToOriginY();
},
/**
@@ -71,12 +104,48 @@
*/
_set: function(key, value) {
this[key] = value;
- if (key in coordProps) {
+ if (typeof coordProps[key] !== 'undefined') {
this._setWidthHeight();
}
return this;
},
+ /**
+ * @private
+ * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
+ */
+ _getLeftToOriginX: makeEdgeToOriginGetter(
+ { // property names
+ origin: 'originX',
+ axis1: 'x1',
+ axis2: 'x2',
+ dimension: 'width'
+ },
+ { // possible values of origin
+ nearest: 'left',
+ center: 'center',
+ farthest: 'right'
+ }
+ ),
+
+ /**
+ * @private
+ * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
+ */
+ _getTopToOriginY: makeEdgeToOriginGetter(
+ { // property names
+ origin: 'originY',
+ axis1: 'y1',
+ axis2: 'y2',
+ dimension: 'height'
+ },
+ { // possible values of origin
+ nearest: 'top',
+ center: 'center',
+ farthest: 'bottom'
+ }
+ ),
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -86,15 +155,23 @@
var isInPathGroup = this.group && this.group.type === 'path-group';
if (isInPathGroup && !this.transformMatrix) {
- ctx.translate(-this.group.width/2 + this.left, -this.group.height / 2 + this.top);
+ // Line coords are distances from left-top of canvas to origin of line.
+ //
+ // To render line in a path-group, we need to translate them to
+ // distances from center of path-group to center of line.
+ var cp = this.getCenterPoint();
+ ctx.translate(
+ -this.group.width/2 + cp.x,
+ -this.group.height / 2 + cp.y
+ );
}
if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
// move from center (of virtual box) to its left/top corner
// we can't assume x1, y1 is top left and x2, y2 is bottom right
- var xMult = this.x1 <= this.x2 ? -1 : 1;
- var yMult = this.y1 <= this.y2 ? -1 : 1;
+ var xMult = this.x1 <= this.x2 ? -1 : 1,
+ yMult = this.y1 <= this.y2 ? -1 : 1;
ctx.moveTo(
this.width === 1 ? 0 : (xMult * this.width / 2),
@@ -112,7 +189,7 @@
// (by copying fillStyle to strokeStyle, since line is stroked, not filled)
var origStrokeStyle = ctx.strokeStyle;
ctx.strokeStyle = this.stroke || ctx.fillStyle;
- this._renderStroke(ctx);
+ this.stroke && this._renderStroke(ctx);
ctx.strokeStyle = origStrokeStyle;
},
@@ -197,13 +274,13 @@
* @return {fabric.Line} instance of fabric.Line
*/
fabric.Line.fromElement = function(element, options) {
- var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES);
- var points = [
- parsedAttributes.x1 || 0,
- parsedAttributes.y1 || 0,
- parsedAttributes.x2 || 0,
- parsedAttributes.y2 || 0
- ];
+ var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
+ points = [
+ parsedAttributes.x1 || 0,
+ parsedAttributes.y1 || 0,
+ parsedAttributes.x2 || 0,
+ parsedAttributes.y2 || 0
+ ];
return new fabric.Line(points, extend(parsedAttributes, options));
};
/* _FROM_SVG_END_ */
@@ -220,4 +297,29 @@
return new fabric.Line(points, object);
};
+ /**
+ * Produces a function that calculates distance from canvas edge to Line origin.
+ */
+ function makeEdgeToOriginGetter(propertyNames, originValues) {
+ var origin = propertyNames.origin,
+ axis1 = propertyNames.axis1,
+ axis2 = propertyNames.axis2,
+ dimension = propertyNames.dimension,
+ nearest = originValues.nearest,
+ center = originValues.center,
+ farthest = originValues.farthest;
+
+ return function() {
+ switch (this.get(origin)) {
+ case nearest:
+ return Math.min(this.get(axis1), this.get(axis2));
+ case center:
+ return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
+ case farthest:
+ return Math.max(this.get(axis1), this.get(axis2));
+ }
+ };
+
+ }
+
})(typeof exports !== 'undefined' ? exports : this);
diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js
index 989f5e7c..407c4975 100644
--- a/src/shapes/object.class.js
+++ b/src/shapes/object.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -611,6 +611,7 @@
/**
* Function that determines clipping of an object (context is passed as a first argument)
+ * Note that context origin is at the object's center point (not left/top corner)
* @type Function
*/
clipTo: null,
@@ -752,34 +753,34 @@
*/
toObject: function(propertiesToInclude) {
- var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
- var object = {
- type: this.type,
- originX: this.originX,
- originY: this.originY,
- left: toFixed(this.left, NUM_FRACTION_DIGITS),
- top: toFixed(this.top, NUM_FRACTION_DIGITS),
- width: toFixed(this.width, NUM_FRACTION_DIGITS),
- height: toFixed(this.height, NUM_FRACTION_DIGITS),
- fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
- stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
- strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
- strokeDashArray: this.strokeDashArray,
- strokeLineCap: this.strokeLineCap,
- strokeLineJoin: this.strokeLineJoin,
- strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
- scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
- scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
- angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
- flipX: this.flipX,
- flipY: this.flipY,
- opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
- shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
- visible: this.visible,
- clipTo: this.clipTo && String(this.clipTo),
- backgroundColor: this.backgroundColor
- };
+ object = {
+ type: this.type,
+ originX: this.originX,
+ originY: this.originY,
+ left: toFixed(this.left, NUM_FRACTION_DIGITS),
+ top: toFixed(this.top, NUM_FRACTION_DIGITS),
+ width: toFixed(this.width, NUM_FRACTION_DIGITS),
+ height: toFixed(this.height, NUM_FRACTION_DIGITS),
+ fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
+ stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
+ strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
+ strokeDashArray: this.strokeDashArray,
+ strokeLineCap: this.strokeLineCap,
+ strokeLineJoin: this.strokeLineJoin,
+ strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
+ scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
+ scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
+ angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
+ flipX: this.flipX,
+ flipY: this.flipY,
+ opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
+ shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
+ visible: this.visible,
+ clipTo: this.clipTo && String(this.clipTo),
+ backgroundColor: this.backgroundColor
+ };
if (!this.includeDefaultValues) {
object = this._removeDefaultValues(object);
@@ -805,8 +806,8 @@
* @param {Object} object
*/
_removeDefaultValues: function(object) {
- var prototype = fabric.util.getKlass(object.type).prototype;
- var stateProperties = prototype.stateProperties;
+ var prototype = fabric.util.getKlass(object.type).prototype,
+ stateProperties = prototype.stateProperties;
stateProperties.forEach(function(prop) {
if (object[prop] === prototype[prop]) {
@@ -822,7 +823,7 @@
* @return {String}
*/
toString: function() {
- return "#";
+ return '#';
},
/**
@@ -834,6 +835,15 @@
return this[property];
},
+ /**
+ * @private
+ */
+ _setObject: function(obj) {
+ for (var prop in obj) {
+ this._set(prop, obj[prop]);
+ }
+ },
+
/**
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
* @param {String|Object} key Property name or object (if object, iterate over the object properties)
@@ -843,9 +853,7 @@
*/
set: function(key, value) {
if (typeof key === 'object') {
- for (var prop in key) {
- this._set(prop, key[prop]);
- }
+ this._setObject(key);
}
else {
if (typeof value === 'function' && key !== 'clipTo') {
@@ -915,6 +923,18 @@
return this;
},
+ /**
+ * Retrieves viewportTransform from Object's canvas if possible
+ * @method getViewportTransform
+ * @memberOf fabric.Object.prototype
+ * @return {Boolean} flipY value // TODO
+ */
+ getViewportTransform: function() {
+ if (this.canvas && this.canvas.viewportTransform)
+ return this.canvas.viewportTransform;
+ return [1, 0, 0, 1, 0, 0];
+ },
+
/**
* Renders an object on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -926,6 +946,9 @@
ctx.save();
+ //setup fill rule for current object
+ this._setupFillRule(ctx);
+
this._transform(ctx, noTransform);
this._setStrokeStyles(ctx);
this._setFillStyles(ctx);
@@ -941,6 +964,8 @@
this._render(ctx, noTransform);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
+ this._restoreFillRule(ctx);
+
ctx.restore();
this._renderControls(ctx, noTransform);
@@ -948,7 +973,7 @@
_transform: function(ctx, noTransform) {
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
@@ -986,7 +1011,7 @@
* @param {Boolean} [noTransform] When true, context is not transformed
*/
_renderControls: function(ctx, noTransform) {
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.save();
if (this.active && !noTransform) {
@@ -1027,6 +1052,8 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_removeShadow: function(ctx) {
+ if (!this.shadow) return;
+
ctx.shadowColor = '';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
},
@@ -1044,7 +1071,12 @@
-this.width / 2 + this.fill.offsetX || 0,
-this.height / 2 + this.fill.offsetY || 0);
}
- ctx.fill();
+ if (this.fillRule === 'destination-over') {
+ ctx.fill('evenodd');
+ }
+ else {
+ ctx.fill();
+ }
if (this.fill.toLive) {
ctx.restore();
}
@@ -1243,7 +1275,7 @@
setGradient: function(property, options) {
options || (options = { });
- var gradient = {colorStops: []};
+ var gradient = { colorStops: [] };
gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
gradient.coords = {
@@ -1260,7 +1292,11 @@
for (var position in options.colorStops) {
var color = new fabric.Color(options.colorStops[position]);
- gradient.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
+ gradient.colorStops.push({
+ offset: position,
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
}
return this.set(property, fabric.Gradient.forObject(this, gradient));
@@ -1318,7 +1354,7 @@
/**
* Sets "color" of an instance (alias of `set('fill', …)`)
* @param {String} color Color value
- * @return {fabric.Text} thisArg
+ * @return {fabric.Object} thisArg
* @chainable
*/
setColor: function(color) {
@@ -1326,6 +1362,28 @@
return this;
},
+ /**
+ * Sets "angle" of an instance
+ * @param {Number} angle Angle value
+ * @return {fabric.Object} thisArg
+ * @chainable
+ */
+ setAngle: function(angle) {
+ var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
+
+ if (shouldCenterOrigin) {
+ this._setOriginToCenter();
+ }
+
+ this.set('angle', angle);
+
+ if (shouldCenterOrigin) {
+ this._resetOrigin();
+ }
+
+ return this;
+ },
+
/**
* Centers object horizontally on canvas to which it was added last.
* You might need to call `setCoords` on an object after centering, to update controls area.
@@ -1365,7 +1423,8 @@
* @chainable
*/
remove: function() {
- return this.canvas.remove(this);
+ this.canvas.remove(this);
+ return this;
},
/**
@@ -1381,6 +1440,28 @@
x: pointer.x - objectLeftTop.x,
y: pointer.y - objectLeftTop.y
};
+ },
+
+ /**
+ * Sets canvas globalCompositeOperation for specific object
+ * custom composition operation for the particular object can be specifed using fillRule property
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _setupFillRule: function (ctx) {
+ if (this.fillRule) {
+ this._prevFillRule = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = this.fillRule;
+ }
+ },
+
+ /**
+ * Restores previously saved canvas globalCompositeOperation after obeject rendering
+ * @param {CanvasRenderingContext2D} ctx Rendering canvas context
+ */
+ _restoreFillRule: function (ctx) {
+ if (this.fillRule && this._prevFillRule) {
+ ctx.globalCompositeOperation = this._prevFillRule;
+ }
}
});
diff --git a/src/shapes/path.class.js b/src/shapes/path.class.js
index fc9b6207..1d6db96e 100644
--- a/src/shapes/path.class.js
+++ b/src/shapes/path.class.js
@@ -1,25 +1,24 @@
(function(global) {
- var commandLengths = {
- m: 2,
- l: 2,
- h: 1,
- v: 1,
- c: 6,
- s: 4,
- q: 4,
- t: 2,
- a: 7
- };
-
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
min = fabric.util.array.min,
max = fabric.util.array.max,
extend = fabric.util.object.extend,
_toString = Object.prototype.toString,
- drawArc = fabric.util.drawArc;
+ drawArc = fabric.util.drawArc,
+ commandLengths = {
+ m: 2,
+ l: 2,
+ h: 1,
+ v: 1,
+ c: 6,
+ s: 4,
+ q: 4,
+ t: 2,
+ a: 7
+ };
if (fabric.Path) {
fabric.warn('fabric.Path is already defined');
@@ -62,6 +61,13 @@
*/
type: 'path',
+ /**
+ * Array of path points
+ * @type Array
+ * @default
+ */
+ path: null,
+
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
@@ -125,7 +131,9 @@
this.left = this.width / 2;
}
}
- this.pathOffset = this.pathOffset || this._calculatePathOffset(origLeft, origTop); //Save top-left coords as offset
+ this.pathOffset = this.pathOffset ||
+ // Save top-left coords as offset
+ this._calculatePathOffset(origLeft, origTop);
},
/**
@@ -281,8 +289,8 @@
tempX = current[3];
tempY = current[4];
// calculate reflection of previous control points
- controlX = 2*x - controlX;
- controlY = 2*y - controlY;
+ controlX = 2 * x - controlX;
+ controlY = 2 * y - controlY;
ctx.bezierCurveTo(
controlX + l,
controlY + t,
@@ -343,7 +351,6 @@
tempX = x + current[1];
tempY = y + current[2];
-
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
@@ -444,7 +451,7 @@
ctx.save();
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -485,7 +492,7 @@
*/
toObject: function(propertiesToInclude) {
var o = extend(this.callSuper('toObject', propertiesToInclude), {
- path: this.path,
+ path: this.path.map(function(item) { return item.slice() }),
pathOffset: this.pathOffset
});
if (this.sourcePath) {
@@ -557,7 +564,7 @@
coords = [ ],
currentPath,
parsed,
- re = /(-?\.\d+)|(-?\d+(\.\d+)?)/g,
+ re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
match,
coordsStr;
@@ -613,14 +620,14 @@
maxX = max(aX),
maxY = max(aY),
deltaX = maxX - minX,
- deltaY = maxY - minY;
+ deltaY = maxY - minY,
- var o = {
- left: this.left + (minX + deltaX / 2),
- top: this.top + (minY + deltaY / 2),
- width: deltaX,
- height: deltaY
- };
+ o = {
+ left: this.left + (minX + deltaX / 2),
+ top: this.top + (minY + deltaY / 2),
+ width: deltaX,
+ height: deltaY
+ };
return o;
},
@@ -641,13 +648,18 @@
isLowerCase = true;
}
- var xy = this._getXY(item, isLowerCase, previous);
+ var xy = this._getXY(item, isLowerCase, previous),
+ val;
- var val = parseInt(xy.x, 10);
- if (!isNaN(val)) aX.push(val);
+ val = parseInt(xy.x, 10);
+ if (!isNaN(val)) {
+ aX.push(val);
+ }
val = parseInt(xy.y, 10);
- if (!isNaN(val)) aY.push(val);
+ if (!isNaN(val)) {
+ aY.push(val);
+ }
},
_getXY: function(item, isLowerCase, previous) {
@@ -659,13 +671,13 @@
? previous.x + getX(item)
: item[0] === 'V'
? previous.x
- : getX(item);
+ : getX(item),
- var y = isLowerCase
- ? previous.y + getY(item)
- : item[0] === 'H'
- ? previous.y
- : getY(item);
+ y = isLowerCase
+ ? previous.y + getY(item)
+ : item[0] === 'H'
+ ? previous.y
+ : getY(item);
return { x: x, y: y };
}
@@ -681,9 +693,9 @@
fabric.Path.fromObject = function(object, callback) {
if (typeof object.path === 'string') {
fabric.loadSVGFromURL(object.path, function (elements) {
- var path = elements[0];
+ var path = elements[0],
+ pathUrl = object.path;
- var pathUrl = object.path;
delete object.path;
fabric.util.object.extend(path, object);
diff --git a/src/shapes/path_group.class.js b/src/shapes/path_group.class.js
index a7ea505f..7c1acbab 100644
--- a/src/shapes/path_group.class.js
+++ b/src/shapes/path_group.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -52,6 +52,15 @@
this.setOptions(options);
+ if (options.widthAttr) {
+ this.scaleX = options.widthAttr / options.width;
+ }
+ if (options.heightAttr) {
+ this.scaleY = options.heightAttr / options.height;
+ }
+
+ this.setCoords();
+
if (options.sourcePath) {
this.setSourcePath(options.sourcePath);
}
@@ -69,7 +78,7 @@
var m = this.transformMatrix;
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (m) {
@@ -143,13 +152,13 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- var objects = this.getObjects();
- var markup = [
- ''
- ];
+ var objects = this.getObjects(),
+ markup = [
+ ''
+ ];
for (var i = 0, len = objects.length; i < len; i++) {
markup.push(objects[i].toSVG(reviver));
@@ -174,9 +183,9 @@
* @return {Boolean} true if all paths are of the same color (`fill`)
*/
isSameColor: function() {
- var firstPathFill = this.getObjects()[0].get('fill');
+ var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
return this.getObjects().every(function(path) {
- return path.get('fill') === firstPathFill;
+ return (path.get('fill') || '').toLowerCase() === firstPathFill;
});
},
diff --git a/src/shapes/polygon.class.js b/src/shapes/polygon.class.js
index c111d7bd..cf386e55 100644
--- a/src/shapes/polygon.class.js
+++ b/src/shapes/polygon.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -28,6 +28,13 @@
*/
type: 'polygon',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points
@@ -138,7 +145,7 @@
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || this.points[0];
+ p2 = this.points[i + 1] || this.points[0];
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
ctx.closePath();
diff --git a/src/shapes/polyline.class.js b/src/shapes/polyline.class.js
index b6f8f32a..c5a750d0 100644
--- a/src/shapes/polyline.class.js
+++ b/src/shapes/polyline.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
toFixed = fabric.util.toFixed;
@@ -25,6 +25,13 @@
*/
type: 'polyline',
+ /**
+ * Points array
+ * @type Array
+ * @default
+ */
+ points: null,
+
/**
* Constructor
* @param {Array} points Array of points (where each point is an object with x and y)
@@ -122,7 +129,7 @@
ctx.beginPath();
for (var i = 0, len = this.points.length; i < len; i++) {
p1 = this.points[i];
- p2 = this.points[i+1] || p1;
+ p2 = this.points[i + 1] || p1;
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
}
},
diff --git a/src/shapes/rect.class.js b/src/shapes/rect.class.js
index d882674b..184b90e0 100644
--- a/src/shapes/rect.class.js
+++ b/src/shapes/rect.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
@@ -101,13 +101,22 @@
* @param ctx {CanvasRenderingContext2D} context to render on
*/
_render: function(ctx) {
- var rx = this.rx || 0,
- ry = this.ry || 0,
- x = -this.width / 2,
- y = -this.height / 2,
+
+ // optimize 1x1 case (used in spray brush)
+ if (this.width === 1 && this.height === 1) {
+ ctx.fillRect(0, 0, 1, 1);
+ return;
+ }
+
+ var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
+ ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
w = this.width,
h = this.height,
- isInPathGroup = this.group && this.group.type === 'path-group';
+ x = -w / 2,
+ y = -h / 2,
+ isInPathGroup = this.group && this.group.type === 'path-group',
+ isRounded = rx !== 0 || ry !== 0,
+ k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
ctx.beginPath();
ctx.globalAlpha = isInPathGroup ? (ctx.globalAlpha * this.opacity) : this.opacity;
@@ -123,17 +132,20 @@
-this.group.height / 2 + this.height / 2 + this.y);
}
- var isRounded = rx !== 0 || ry !== 0;
+ ctx.moveTo(x + rx, y);
+
+ ctx.lineTo(x + w - rx, y);
+ isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
+
+ ctx.lineTo(x + w, y + h - ry);
+ isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
+
+ ctx.lineTo(x + rx, y + h);
+ isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
+
+ ctx.lineTo(x, y + ry);
+ isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
- ctx.moveTo(x+rx, y);
- ctx.lineTo(x+w-rx, y);
- isRounded && ctx.quadraticCurveTo(x+w, y, x+w, y+ry, x+w, y+ry);
- ctx.lineTo(x+w, y+h-ry);
- isRounded && ctx.quadraticCurveTo(x+w,y+h,x+w-rx,y+h,x+w-rx,y+h);
- ctx.lineTo(x+rx,y+h);
- isRounded && ctx.quadraticCurveTo(x,y+h,x,y+h-ry,x,y+h-ry);
- ctx.lineTo(x,y+ry);
- isRounded && ctx.quadraticCurveTo(x,y,x+rx,y,x+rx,y);
ctx.closePath();
this._renderFill(ctx);
@@ -145,16 +157,16 @@
* @param ctx {CanvasRenderingContext2D} context to render on
*/
_renderDashedStroke: function(ctx) {
- var x = -this.width/2,
- y = -this.height/2,
- w = this.width,
- h = this.height;
+ var x = -this.width / 2,
+ y = -this.height / 2,
+ w = this.width,
+ h = this.height;
ctx.beginPath();
- fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
- fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
+ fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
ctx.closePath();
},
@@ -208,8 +220,7 @@
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- '"/>'
- );
+ '"/>');
return reviver ? reviver(markup.join('')) : markup.join('');
},
diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js
index 7f193d3e..a942ad00 100644
--- a/src/shapes/text.class.js
+++ b/src/shapes/text.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
@@ -427,8 +427,8 @@
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
this._boundaries.push({
height: this.fontSize * this.lineHeight,
@@ -511,18 +511,18 @@
return;
}
- var lineWidth = ctx.measureText(line).width;
- var totalWidth = this.width;
+ var lineWidth = ctx.measureText(line).width,
+ totalWidth = this.width;
if (totalWidth > lineWidth) {
// stretch the line
- var words = line.split(/\s+/);
- var wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width;
- var widthDiff = totalWidth - wordsWidth;
- var numSpaces = words.length - 1;
- var spaceWidth = widthDiff / numSpaces;
+ var words = line.split(/\s+/),
+ wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
+ widthDiff = totalWidth - wordsWidth,
+ numSpaces = words.length - 1,
+ spaceWidth = widthDiff / numSpaces,
+ leftOffset = 0;
- var leftOffset = 0;
for (var i = 0, len = words.length; i < len; i++) {
this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
leftOffset += ctx.measureText(words[i]).width + spaceWidth;
@@ -664,8 +664,8 @@
if (textLines[i] !== '') {
- var lineWidth = this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = this._getLineLeftOffset(lineWidth);
+ var lineWidth = this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = this._getLineLeftOffset(lineWidth);
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
@@ -714,15 +714,15 @@
if (!this.textDecoration) return;
// var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
- var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2;
- var _this = this;
+ var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
+ _this = this;
/** @ignore */
function renderLinesAtOffset(offset) {
for (var i = 0, len = textLines.length; i < len; i++) {
- var lineWidth = _this._getLineWidth(ctx, textLines[i]);
- var lineLeftOffset = _this._getLineLeftOffset(lineWidth);
+ var lineWidth = _this._getLineWidth(ctx, textLines[i]),
+ lineLeftOffset = _this._getLineLeftOffset(lineWidth);
ctx.fillRect(
_this._getLeftOffset() + lineLeftOffset,
@@ -766,8 +766,13 @@
if (!this.visible) return;
ctx.save();
- var v = this.canvas.viewportTransform;
+ var v = this.getViewportTransform();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+
+ var m = this.transformMatrix;
+ if (m && (!this.group || this.group.type === 'path-group')) {
+ ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ }
this._render(ctx);
ctx.restore();
@@ -953,7 +958,7 @@
(i === 0 || this.useNative ? 'y' : 'dy'), '="',
toFixed(this.useNative
? ((lineHeight * i) - this.height / 2)
- : (lineHeight * lineTopOffsetMultiplier), 2) , '" ',
+ : (lineHeight * lineTopOffsetMultiplier), 2), '" ',
// doing this on elements since setting opacity
// on containing one doesn't work in Illustrator
this._getFillAttributes(this.fill), '>',
@@ -1048,7 +1053,14 @@
* @see: http://www.w3.org/TR/SVG/text.html#TextElement
*/
fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
- 'x y font-family font-style font-weight font-size text-decoration'.split(' '));
+ 'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
+
+ /**
+ * Default SVG font size
+ * @static
+ * @memberOf fabric.Text
+ */
+ fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
/**
* Returns fabric.Text instance from an SVG element (not yet implemented)
@@ -1066,6 +1078,20 @@
var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
+ if ('dx' in parsedAttributes) {
+ options.left += parsedAttributes.dx;
+ }
+ if ('dy' in parsedAttributes) {
+ options.top += parsedAttributes.dy;
+ }
+ if (!('fontSize' in options)) {
+ options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ }
+
+ if (!options.originX) {
+ options.originX = 'center';
+ }
+
var text = new fabric.Text(element.textContent, options);
/*
diff --git a/src/shapes/triangle.class.js b/src/shapes/triangle.class.js
index 36ed6636..12eaebb8 100644
--- a/src/shapes/triangle.class.js
+++ b/src/shapes/triangle.class.js
@@ -1,6 +1,6 @@
(function(global) {
- "use strict";
+ 'use strict';
var fabric = global.fabric || (global.fabric = { });
@@ -81,13 +81,13 @@
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup(),
widthBy2 = this.width / 2,
- heightBy2 = this.height / 2;
-
- var points = [
- -widthBy2 + " " + heightBy2,
- "0 " + -heightBy2,
- widthBy2 + " " + heightBy2
- ].join(",");
+ heightBy2 = this.height / 2,
+ points = [
+ -widthBy2 + ' ' + heightBy2,
+ '0 ' + -heightBy2,
+ widthBy2 + ' ' + heightBy2
+ ]
+ .join(',');
markup.push(
'=0; --i) {
+ for (var i = idx - 1; i >= 0; --i) {
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
object.isContainedWithinObject(this._objects[i]) ||
@@ -1281,7 +1322,7 @@
var idx = this._objects.indexOf(object);
// if object is not on top of stack (last item in an array)
- if (idx !== this._objects.length-1) {
+ if (idx !== this._objects.length - 1) {
var newIdx = this._findNewUpperIndex(object, idx, intersecting);
removeFromArray(this._objects, object);
@@ -1314,7 +1355,7 @@
}
}
else {
- newIdx = idx+1;
+ newIdx = idx + 1;
}
return newIdx;
@@ -1349,7 +1390,7 @@
* @return {String} string representation of an instance
*/
toString: function () {
- return '#';
}
});
diff --git a/src/util/anim_ease.js b/src/util/anim_ease.js
index bed3b7e9..1242d18a 100644
--- a/src/util/anim_ease.js
+++ b/src/util/anim_ease.js
@@ -1,8 +1,13 @@
(function() {
function normalize(a, c, p, s) {
- if (a < Math.abs(c)) { a=c; s=p/4; }
- else s = p/(2*Math.PI) * Math.asin (c/a);
+ if (a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
return { a: a, c: c, p: p, s: s };
}
@@ -17,7 +22,7 @@
* @memberOf fabric.util.ease
*/
function easeOutCubic(t, b, c, d) {
- return c*((t=t/d-1)*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t + 1) + b;
}
/**
@@ -26,8 +31,10 @@
*/
function easeInOutCubic(t, b, c, d) {
t /= d/2;
- if (t < 1) return c/2*t*t*t + b;
- return c/2*((t-=2)*t*t + 2) + b;
+ if (t < 1) {
+ return c / 2 * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t + 2) + b;
}
/**
@@ -35,7 +42,7 @@
* @memberOf fabric.util.ease
*/
function easeInQuart(t, b, c, d) {
- return c*(t/=d)*t*t*t + b;
+ return c * (t /= d) * t * t * t + b;
}
/**
@@ -43,7 +50,7 @@
* @memberOf fabric.util.ease
*/
function easeOutQuart(t, b, c, d) {
- return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
/**
@@ -51,9 +58,11 @@
* @memberOf fabric.util.ease
*/
function easeInOutQuart(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t + b;
- return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t + b;
+ }
+ return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
/**
@@ -61,7 +70,7 @@
* @memberOf fabric.util.ease
*/
function easeInQuint(t, b, c, d) {
- return c*(t/=d)*t*t*t*t + b;
+ return c * (t /= d) * t * t * t * t + b;
}
/**
@@ -69,7 +78,7 @@
* @memberOf fabric.util.ease
*/
function easeOutQuint(t, b, c, d) {
- return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
/**
@@ -77,9 +86,11 @@
* @memberOf fabric.util.ease
*/
function easeInOutQuint(t, b, c, d) {
- t /= d/2;
- if (t < 1) return c/2*t*t*t*t*t + b;
- return c/2*((t-=2)*t*t*t*t + 2) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
/**
@@ -87,7 +98,7 @@
* @memberOf fabric.util.ease
*/
function easeInSine(t, b, c, d) {
- return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
}
/**
@@ -95,7 +106,7 @@
* @memberOf fabric.util.ease
*/
function easeOutSine(t, b, c, d) {
- return c * Math.sin(t/d * (Math.PI/2)) + b;
+ return c * Math.sin(t / d * (Math.PI / 2)) + b;
}
/**
@@ -103,7 +114,7 @@
* @memberOf fabric.util.ease
*/
function easeInOutSine(t, b, c, d) {
- return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
/**
@@ -111,7 +122,7 @@
* @memberOf fabric.util.ease
*/
function easeInExpo(t, b, c, d) {
- return (t===0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
}
/**
@@ -119,7 +130,7 @@
* @memberOf fabric.util.ease
*/
function easeOutExpo(t, b, c, d) {
- return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
/**
@@ -127,11 +138,17 @@
* @memberOf fabric.util.ease
*/
function easeInOutExpo(t, b, c, d) {
- if (t===0) return b;
- if (t===d) return b+c;
- t /= d/2;
- if (t < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ if (t === 0) {
+ return b;
+ }
+ if (t === d) {
+ return b + c;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ }
+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
/**
@@ -139,7 +156,7 @@
* @memberOf fabric.util.ease
*/
function easeInCirc(t, b, c, d) {
- return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
}
/**
@@ -147,7 +164,7 @@
* @memberOf fabric.util.ease
*/
function easeOutCirc(t, b, c, d) {
- return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
}
/**
@@ -155,9 +172,11 @@
* @memberOf fabric.util.ease
*/
function easeInOutCirc(t, b, c, d) {
- t /= d/2;
- if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
- return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ t /= d / 2;
+ if (t < 1) {
+ return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ }
+ return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}
/**
@@ -165,11 +184,17 @@
* @memberOf fabric.util.ease
*/
function easeInElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
return -elastic(opts, t, d) + b;
}
@@ -179,13 +204,19 @@
* @memberOf fabric.util.ease
*/
function easeOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
t /= d;
- if (t===1) return b+c;
- if (!p) p=d*0.3;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * 0.3;
+ }
var opts = normalize(a, c, p, s);
- return opts.a*Math.pow(2,-10*t) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p ) + opts.c + b;
+ return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
}
/**
@@ -193,14 +224,22 @@
* @memberOf fabric.util.ease
*/
function easeInOutElastic(t, b, c, d) {
- var s=1.70158;var p=0;var a=c;
- if (t===0) return b;
- t /= d/2;
- if (t===2) return b+c;
- if (!p) p=d*(0.3*1.5);
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d / 2;
+ if (t === 2) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * (0.3 * 1.5);
+ }
var opts = normalize(a, c, p, s);
- if (t < 1) return -0.5 * elastic(opts, t, d) + b;
- return opts.a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p )*0.5 + opts.c + b;
+ if (t < 1) {
+ return -0.5 * elastic(opts, t, d) + b;
+ }
+ return opts.a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
}
/**
@@ -208,8 +247,10 @@
* @memberOf fabric.util.ease
*/
function easeInBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*(t/=d)*t*((s+1)*t - s) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
/**
@@ -217,8 +258,10 @@
* @memberOf fabric.util.ease
*/
function easeOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
}
/**
@@ -226,10 +269,14 @@
* @memberOf fabric.util.ease
*/
function easeInOutBack(t, b, c, d, s) {
- if (s === undefined) s = 1.70158;
- t /= d/2;
- if (t < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
- return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ }
+ return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
}
/**
@@ -237,7 +284,7 @@
* @memberOf fabric.util.ease
*/
function easeInBounce(t, b, c, d) {
- return c - easeOutBounce (d-t, 0, c, d) + b;
+ return c - easeOutBounce (d - t, 0, c, d) + b;
}
/**
@@ -245,14 +292,17 @@
* @memberOf fabric.util.ease
*/
function easeOutBounce(t, b, c, d) {
- if ((t/=d) < (1/2.75)) {
- return c*(7.5625*t*t) + b;
- } else if (t < (2/2.75)) {
- return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
- } else if (t < (2.5/2.75)) {
- return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
- } else {
- return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ if ((t /= d) < (1 / 2.75)) {
+ return c * (7.5625 * t * t) + b;
+ }
+ else if (t < (2/2.75)) {
+ return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
+ }
+ else if (t < (2.5/2.75)) {
+ return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
+ }
+ else {
+ return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
}
}
@@ -261,8 +311,10 @@
* @memberOf fabric.util.ease
*/
function easeInOutBounce(t, b, c, d) {
- if (t < d/2) return easeInBounce (t*2, 0, c, d) * 0.5 + b;
- return easeOutBounce (t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
+ if (t < d / 2) {
+ return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
+ }
+ return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
}
/**
@@ -277,7 +329,7 @@
* @memberOf fabric.util.ease
*/
easeInQuad: function(t, b, c, d) {
- return c*(t/=d)*t + b;
+ return c * (t /= d) * t + b;
},
/**
@@ -285,7 +337,7 @@
* @memberOf fabric.util.ease
*/
easeOutQuad: function(t, b, c, d) {
- return -c *(t/=d)*(t-2) + b;
+ return -c * (t /= d) * (t - 2) + b;
},
/**
@@ -293,9 +345,11 @@
* @memberOf fabric.util.ease
*/
easeInOutQuad: function(t, b, c, d) {
- t /= (d/2);
- if (t < 1) return c/2*t*t + b;
- return -c/2 * ((--t)*(t-2) - 1) + b;
+ t /= (d / 2);
+ if (t < 1) {
+ return c / 2 * t * t + b;
+ }
+ return -c / 2 * ((--t) * (t - 2) - 1) + b;
},
/**
@@ -303,7 +357,7 @@
* @memberOf fabric.util.ease
*/
easeInCubic: function(t, b, c, d) {
- return c*(t/=d)*t*t + b;
+ return c * (t /= d) * t * t + b;
},
easeOutCubic: easeOutCubic,
diff --git a/src/util/animate.js b/src/util/animate.js
index d1bbdbe7..f7c44f35 100644
--- a/src/util/animate.js
+++ b/src/util/animate.js
@@ -10,7 +10,7 @@
* @param {Number} [options.endValue=100] Ending value
* @param {Number} [options.byValue=100] Value to modify the property by
* @param {Function} [options.easing] Easing function
- * @param {Number} [options.duration=500] Duration of change
+ * @param {Number} [options.duration=500] Duration of change (in ms)
*/
function animate(options) {
@@ -22,7 +22,7 @@
finish = start + duration, time,
onChange = options.onChange || function() { },
abort = options.abort || function() { return false; },
- easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t/d * (Math.PI/2)) + c + b;},
+ easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
startValue = 'startValue' in options ? options.startValue : 0,
endValue = 'endValue' in options ? options.endValue : 100,
byValue = options.byValue || endValue - startValue;
@@ -62,9 +62,9 @@
* @param {Function} callback Callback to invoke
* @param {DOMElement} element optional Element to associate with animation
*/
- var requestAnimFrame = function() {
+ function requestAnimFrame() {
return _requestAnimFrame.apply(fabric.window, arguments);
- };
+ }
fabric.util.animate = animate;
fabric.util.requestAnimFrame = requestAnimFrame;
diff --git a/src/util/arc.js b/src/util/arc.js
index 36c567ec..550e344f 100644
--- a/src/util/arc.js
+++ b/src/util/arc.js
@@ -14,37 +14,43 @@
return arcToSegmentsCache[argsString];
}
- var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y);
+ var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y),
- var d = (coords.x1-coords.x0) * (coords.x1-coords.x0) +
- (coords.y1-coords.y0) * (coords.y1-coords.y0);
+ d = (coords.x1 - coords.x0) * (coords.x1 - coords.x0) +
+ (coords.y1 - coords.y0) * (coords.y1 - coords.y0),
- var sfactor_sq = 1 / d - 0.25;
- if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactorSq = 1 / d - 0.25;
- var sfactor = Math.sqrt(sfactor_sq);
- if (sweep === large) sfactor = -sfactor;
-
- var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1-coords.y0);
- var yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1-coords.x0);
-
- var th0 = Math.atan2(coords.y0-yc, coords.x0-xc);
- var th1 = Math.atan2(coords.y1-yc, coords.x1-xc);
-
- var th_arc = th1-th0;
- if (th_arc < 0 && sweep === 1) {
- th_arc += 2*Math.PI;
- }
- else if (th_arc > 0 && sweep === 0) {
- th_arc -= 2 * Math.PI;
+ if (sfactorSq < 0) {
+ sfactorSq = 0;
}
- var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
- var result = [];
- for (var i=0; i 0 && sweep === 0) {
+ thArc -= 2 * Math.PI;
+ }
+
+ var segments = Math.ceil(Math.abs(thArc / (Math.PI * 0.5 + 0.001))),
+ result = [];
+
+ for (var i = 0; i < segments; i++) {
+ var th2 = th0 + i * thArc / segments,
+ th3 = th0 + (i + 1) * thArc / segments;
+
+ result[i] = [xc, yc, th2, th3, rx, ry, coords.sinTh, coords.cosTh];
}
arcToSegmentsCache[argsString] = result;
@@ -53,56 +59,59 @@
function getXYCoords(rotateX, rx, ry, ox, oy, x, y) {
- var th = rotateX * (Math.PI/180);
- var sin_th = Math.sin(th);
- var cos_th = Math.cos(th);
+ var th = rotateX * (Math.PI / 180),
+ sinTh = Math.sin(th),
+ cosTh = Math.cos(th);
+
rx = Math.abs(rx);
ry = Math.abs(ry);
- var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
- var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
- var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
+
+ var px = cosTh * (ox - x) * 0.5 + sinTh * (oy - y) * 0.5,
+ py = cosTh * (oy - y) * 0.5 - sinTh * (ox - x) * 0.5,
+ pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
+
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
}
- var a00 = cos_th / rx;
- var a01 = sin_th / rx;
- var a10 = (-sin_th) / ry;
- var a11 = (cos_th) / ry;
+ var a00 = cosTh / rx,
+ a01 = sinTh / rx,
+ a10 = (-sinTh) / ry,
+ a11 = (cosTh) / ry;
return {
x0: a00 * ox + a01 * oy,
y0: a10 * ox + a11 * oy,
x1: a00 * x + a01 * y,
y1: a10 * x + a11 * y,
- sin_th: sin_th,
- cos_th: cos_th
+ sinTh: sinTh,
+ cosTh: cosTh
};
}
- function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
+ function segmentToBezier(cx, cy, th0, th1, rx, ry, sinTh, cosTh) {
argsString = _join.call(arguments);
+
if (segmentToBezierCache[argsString]) {
return segmentToBezierCache[argsString];
}
- var a00 = cos_th * rx;
- var a01 = -sin_th * ry;
- var a10 = sin_th * rx;
- var a11 = cos_th * ry;
+ var a00 = cosTh * rx,
+ a01 = -sinTh * ry,
+ a10 = sinTh * rx,
+ a11 = cosTh * ry,
+ thHalf = 0.5 * (th1 - th0),
+ t = (8 / 3) * Math.sin(thHalf * 0.5) *
+ Math.sin(thHalf * 0.5) / Math.sin(thHalf),
- var th_half = 0.5 * (th1 - th0);
- var t = (8/3) * Math.sin(th_half * 0.5) *
- Math.sin(th_half * 0.5) / Math.sin(th_half);
-
- var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
- var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
- var x3 = cx + Math.cos(th1);
- var y3 = cy + Math.sin(th1);
- var x2 = x3 + t * Math.sin(th1);
- var y2 = y3 - t * Math.cos(th1);
+ x1 = cx + Math.cos(th0) - t * Math.sin(th0),
+ y1 = cy + Math.sin(th0) + t * Math.cos(th0),
+ x3 = cx + Math.cos(th1),
+ y3 = cy + Math.sin(th1),
+ x2 = x3 + t * Math.sin(th1),
+ y2 = y3 - t * Math.cos(th1);
segmentToBezierCache[argsString] = [
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
@@ -121,17 +130,18 @@
* @param {Array} coords
*/
fabric.util.drawArc = function(ctx, x, y, coords) {
- var rx = coords[0];
- var ry = coords[1];
- var rot = coords[2];
- var large = coords[3];
- var sweep = coords[4];
- var ex = coords[5];
- var ey = coords[6];
- var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
- for (var i=0; i -1) {
+ if (property in klass.prototype &&
+ typeof klass.prototype[property] === 'function' &&
+ (source[property] + '').indexOf('callSuper') > -1) {
- klass.prototype[property] = (function(property) {
- return function() {
+ klass.prototype[property] = (function(property) {
+ return function() {
- var superclass = this.constructor.superclass;
- this.constructor.superclass = parent;
- var returnValue = source[property].apply(this, arguments);
- this.constructor.superclass = superclass;
+ var superclass = this.constructor.superclass;
+ this.constructor.superclass = parent;
+ var returnValue = source[property].apply(this, arguments);
+ this.constructor.superclass = superclass;
- if (property !== 'initialize') {
- return returnValue;
+ if (property !== 'initialize') {
+ return returnValue;
+ }
+ };
+ })(property);
+ }
+ else {
+ klass.prototype[property] = source[property];
+ }
+
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString !== Object.prototype.toString) {
+ klass.prototype.toString = source.toString;
}
- };
- })(property);
- }
- else {
- klass.prototype[property] = source[property];
- }
-
- if (IS_DONTENUM_BUGGY) {
- if (source.toString !== Object.prototype.toString) {
- klass.prototype.toString = source.toString;
+ if (source.valueOf !== Object.prototype.valueOf) {
+ klass.prototype.valueOf = source.valueOf;
+ }
+ }
}
- if (source.valueOf !== Object.prototype.valueOf) {
- klass.prototype.valueOf = source.valueOf;
- }
- }
- }
- };
+ };
function Subclass() { }
diff --git a/src/util/lang_function.js b/src/util/lang_function.js
index 10a99358..594f10a1 100644
--- a/src/util/lang_function.js
+++ b/src/util/lang_function.js
@@ -14,16 +14,16 @@
* @return {Function}
*/
Function.prototype.bind = function(thisArg) {
- var fn = this, args = slice.call(arguments, 1), bound;
+ var _this = this, args = slice.call(arguments, 1), bound;
if (args.length) {
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
};
}
else {
/** @ignore */
bound = function() {
- return apply.call(fn, this instanceof Dummy ? this : thisArg, arguments);
+ return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
};
}
Dummy.prototype = this.prototype;
diff --git a/src/util/lang_string.js b/src/util/lang_string.js
index cf4d2239..708e9b25 100644
--- a/src/util/lang_string.js
+++ b/src/util/lang_string.js
@@ -1,66 +1,66 @@
(function() {
-/* _ES5_COMPAT_START_ */
-if (!String.prototype.trim) {
+ /* _ES5_COMPAT_START_ */
+ if (!String.prototype.trim) {
+ /**
+ * Trims a string (removing whitespace from the beginning and the end)
+ * @function external:String#trim
+ * @see String#trim on MDN
+ */
+ String.prototype.trim = function () {
+ // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
+ return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ };
+ }
+ /* _ES5_COMPAT_END_ */
+
/**
- * Trims a string (removing whitespace from the beginning and the end)
- * @function external:String#trim
- * @see String#trim on MDN
+ * Camelizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to camelize
+ * @return {String} Camelized version of a string
*/
- String.prototype.trim = function () {
- // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
- return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
+ function camelize(string) {
+ return string.replace(/-+(.)?/g, function(match, character) {
+ return character ? character.toUpperCase() : '';
+ });
+ }
+
+ /**
+ * Capitalizes a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to capitalize
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
+ * and other letters stay untouched, if false first letter is capitalized
+ * and other letters are converted to lowercase.
+ * @return {String} Capitalized version of a string
+ */
+ function capitalize(string, firstLetterOnly) {
+ return string.charAt(0).toUpperCase() +
+ (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
+ }
+
+ /**
+ * Escapes XML in a string
+ * @memberOf fabric.util.string
+ * @param {String} string String to escape
+ * @return {String} Escaped version of a string
+ */
+ function escapeXml(string) {
+ return string.replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>');
+ }
+
+ /**
+ * String utilities
+ * @namespace fabric.util.string
+ */
+ fabric.util.string = {
+ camelize: camelize,
+ capitalize: capitalize,
+ escapeXml: escapeXml
};
-}
-/* _ES5_COMPAT_END_ */
-
-/**
- * Camelizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to camelize
- * @return {String} Camelized version of a string
- */
-function camelize(string) {
- return string.replace(/-+(.)?/g, function(match, character) {
- return character ? character.toUpperCase() : '';
- });
-}
-
-/**
- * Capitalizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to capitalize
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
- * and other letters stay untouched, if false first letter is capitalized
- * and other letters are converted to lowercase.
- * @return {String} Capitalized version of a string
- */
-function capitalize(string, firstLetterOnly) {
- return string.charAt(0).toUpperCase() +
- (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
-}
-
-/**
- * Escapes XML in a string
- * @memberOf fabric.util.string
- * @param {String} string String to escape
- * @return {String} Escaped version of a string
- */
-function escapeXml(string) {
- return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(//g, '>');
-}
-
-/**
- * String utilities
- * @namespace fabric.util.string
- */
-fabric.util.string = {
- camelize: camelize,
- capitalize: capitalize,
- escapeXml: escapeXml
-};
}());
diff --git a/src/util/misc.js b/src/util/misc.js
index b905c0fb..0bd8e2ca 100644
--- a/src/util/misc.js
+++ b/src/util/misc.js
@@ -162,7 +162,9 @@
* @return {Object} Object for given namespace (default fabric)
*/
resolveNamespace: function(namespace) {
- if (!namespace) return fabric;
+ if (!namespace) {
+ return fabric;
+ }
var parts = namespace.split('.'),
len = parts.length,
@@ -324,7 +326,7 @@
drawDashedLine: function(ctx, x, y, x2, y2, da) {
var dx = x2 - x,
dy = y2 - y,
- len = sqrt(dx*dx + dy*dy),
+ len = sqrt(dx * dx + dy * dy),
rot = atan2(dy, dx),
dc = da.length,
di = 0,
@@ -432,22 +434,23 @@
var a = [
[matrixA[0], matrixA[2], matrixA[4]],
[matrixA[1], matrixA[3], matrixA[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var b = [
+ b = [
[matrixB[0], matrixB[2], matrixB[4]],
[matrixB[1], matrixB[3], matrixB[5]],
- [0 , 0 , 1 ]
- ];
+ [0, 0, 1 ]
+ ],
- var result = [];
- for (var r=0; r<3; r++) {
+ result = [];
+
+ for (var r = 0; r < 3; r++) {
result[r] = [];
- for (var c=0; c<3; c++) {
+ for (var c = 0; c < 3; c++) {
var sum = 0;
- for (var k=0; k<3; k++) {
- sum += a[r][k]*b[k][c];
+ for (var k = 0; k < 3; k++) {
+ sum += a[r][k] * b[k][c];
}
result[r][c] = sum;
@@ -520,9 +523,8 @@
}
}
- var _isTransparent = true;
- var imageData = ctx.getImageData(
- x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
+ var _isTransparent = true,
+ imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
// Split image data - for tolerance > 1, pixelDataSize = 4;
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
diff --git a/test.js b/test.js
index cb9589b9..e221fde6 100644
--- a/test.js
+++ b/test.js
@@ -4,9 +4,11 @@ testrunner.options.log.summary = true;
testrunner.options.log.tests = false;
testrunner.options.log.assertions = false;
+testrunner.options.coverage = true;
+
testrunner.run({
deps: "./test/fixtures/test_script.js",
- code: "./dist/all.js",
+ code: "./dist/fabric.js",
tests: [
'./test/unit/rect.js',
'./test/unit/ellipse.js',
diff --git a/test/lib/event.simulate.js b/test/lib/event.simulate.js
index 290f873d..74194a8d 100644
--- a/test/lib/event.simulate.js
+++ b/test/lib/event.simulate.js
@@ -1,6 +1,6 @@
/**
* simulateEvent(@element, eventName[, options]) -> Element
- *
+ *
* - @element: element to fire event on
* - eventName: name of event to fire (only MouseEvents and HTMLEvents interfaces are supported)
* - options: optional object to fine-tune event properties - pointerX, pointerY, ctrlKey, etc.
@@ -29,44 +29,44 @@
bubbles: true,
cancelable: true
};
-
+
global.simulateEvent = function(element, eventName) {
-
+
var options = extendObject(extendObject({ }, defaultOptions), arguments[2] || { }),
- oEvent,
+ oEvent,
eventType;
-
+
element = typeof element == 'string' ? document.getElementById(element) : element;
-
+
for (var name in eventMatchers) {
if (eventMatchers[name].test(eventName)) {
- eventType = name;
- break;
+ eventType = name;
+ break;
}
}
-
+
if (!eventType) {
throw new SyntaxError('This event is not supported');
}
-
+
if (document.createEvent) {
try {
- // Opera doesn't support event types like "KeyboardEvent",
+ // Opera doesn't support event types like "KeyboardEvent",
// but allows to create event of type "HTMLEvents", then fire key event on it
oEvent = document.createEvent(eventType);
}
catch(err) {
oEvent = document.createEvent('HTMLEvents');
}
-
+
if (eventType == 'HTMLEvents') {
oEvent.initEvent(eventName, options.bubbles, options.cancelable);
}
else if (eventType === 'KeyboardEvent') {
// TODO (kangax): this needs to be tested
if (oEvent.initKeyEvent) {
- oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode,
+ oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode,
options.charCode);
}
else if (oEvent.initEvent) {
@@ -74,7 +74,7 @@
}
}
else {
- oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
+ oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
}
diff --git a/test/unit/canvas.js b/test/unit/canvas.js
index 4d370064..0b8c4d74 100644
--- a/test/unit/canvas.js
+++ b/test/unit/canvas.js
@@ -100,18 +100,25 @@
test('calcOffset', function() {
ok(typeof canvas.calcOffset == 'function', 'should respond to `calcOffset`');
- equal(canvas, canvas.calcOffset());
+ equal(canvas.calcOffset(), canvas, 'should be chainable');
});
test('add', function() {
- var rect = makeRect();
+ var rect1 = makeRect(),
+ rect2 = makeRect(),
+ rect3 = makeRect(),
+ rect4 = makeRect();
ok(typeof canvas.add == 'function');
- ok(canvas === canvas.add(rect), 'should be chainable');
- equal(canvas.item(0), rect);
+ equal(canvas.add(rect1), canvas, 'should be chainable');
+ strictEqual(canvas.item(0), rect1);
- canvas.add(makeRect(), makeRect(), makeRect());
+ canvas.add(rect2, rect3, rect4);
equal(canvas.getObjects().length, 4, 'should support multiple arguments');
+
+ strictEqual(canvas.item(1), rect2);
+ strictEqual(canvas.item(2), rect3);
+ strictEqual(canvas.item(3), rect4);
});
test('insertAt', function() {
@@ -124,10 +131,61 @@
var rect = makeRect();
canvas.insertAt(rect, 1);
- equal(canvas.item(1), rect);
+ strictEqual(canvas.item(1), rect);
canvas.insertAt(rect, 2);
- equal(canvas.item(2), rect);
- equal(canvas, canvas.insertAt(rect, 2), 'should be chainable');
+ strictEqual(canvas.item(2), rect);
+ equal(canvas.insertAt(rect, 2), canvas, 'should be chainable');
+ });
+
+ test('remove', function() {
+ var rect1 = makeRect(),
+ rect2 = makeRect(),
+ rect3 = makeRect(),
+ rect4 = makeRect();
+
+ canvas.add(rect1, rect2, rect3, rect4);
+
+ ok(typeof canvas.remove == 'function');
+ equal(canvas.remove(rect1), canvas, 'should be chainable');
+ strictEqual(canvas.item(0), rect2, 'should be second object');
+
+ canvas.remove(rect2, rect3);
+ strictEqual(canvas.item(0), rect4);
+
+ canvas.remove(rect4);
+ equal(canvas.isEmpty(), true, 'canvas should be empty');
+ });
+
+ test('before:selection:cleared', function() {
+ var isFired = false;
+ canvas.on('before:selection:cleared', function() { isFired = true });
+
+ canvas.add(new fabric.Rect());
+ canvas.remove(canvas.item(0));
+
+ equal(isFired, false, 'removing inactive object shouldnt fire "before:selection:cleared"');
+
+ canvas.add(new fabric.Rect());
+ canvas.setActiveObject(canvas.item(0));
+ canvas.remove(canvas.item(0));
+
+ equal(isFired, true, 'removing active object should fire "before:selection:cleared"');
+ });
+
+ test('selection:cleared', function() {
+ var isFired = false;
+ canvas.on('selection:cleared', function() { isFired = true });
+
+ canvas.add(new fabric.Rect());
+ canvas.remove(canvas.item(0));
+
+ equal(isFired, false, 'removing inactive object shouldnt fire "selection:cleared"');
+
+ canvas.add(new fabric.Rect());
+ canvas.setActiveObject(canvas.item(0));
+ canvas.remove(canvas.item(0));
+
+ equal(isFired, true, 'removing active object should fire "selection:cleared"');
});
test('getContext', function() {
@@ -136,13 +194,13 @@
test('clearContext', function() {
ok(typeof canvas.clearContext == 'function');
- equal(canvas, canvas.clearContext(canvas.getContext()), 'chainable');
+ equal(canvas.clearContext(canvas.getContext()), canvas, 'should be chainable');
});
test('clear', function() {
ok(typeof canvas.clear == 'function');
- equal(canvas, canvas.clear());
+ equal(canvas.clear(), canvas, 'should be chainable');
equal(canvas.getObjects().length, 0);
});
@@ -419,6 +477,25 @@
});
});
+ asyncTest('loadFromJSON without "objects" property', function() {
+ var c1 = new fabric.Canvas('c1', { backgroundColor: 'green', overlayColor: 'yellow' }),
+ c2 = new fabric.Canvas('c2', { backgroundColor: 'red', overlayColor: 'orange' });
+
+ var json = c1.toJSON();
+ var fired = false;
+
+ delete json.objects;
+
+ c2.loadFromJSON(json, function() {
+ fired = true;
+
+ ok(fired, 'Callback should be fired even if no "objects" property exists');
+ equal(c2.backgroundColor, 'green', 'Color should be set properly');
+ equal(c2.overlayColor, 'yellow', 'Color should be set properly');
+ start();
+ });
+ });
+
asyncTest('loadFromJSON with empty fabric.Group', function() {
var c1 = new fabric.Canvas('c1'),
c2 = new fabric.Canvas('c2'),
@@ -501,14 +578,6 @@
// }, 1000);
// });
- test('remove', function() {
- ok(typeof canvas.remove == 'function');
- var rect1 = makeRect(),
- rect2 = makeRect();
- canvas.add(rect1, rect2);
- equal(canvas.remove(rect1), rect1, 'should return removed object');
- equal(canvas.item(0), rect2, 'only second object should be left');
- });
test('sendToBack', function() {
ok(typeof canvas.sendToBack == 'function');
@@ -680,7 +749,7 @@
makeRect({ left: 20, top: 20 })
]);
- equal(canvas.setActiveGroup(group), canvas, 'chainable');
+ equal(canvas.setActiveGroup(group), canvas, 'should be chainable');
equal(canvas.getActiveGroup(), group);
});
@@ -704,7 +773,7 @@
ok(typeof canvas.discardActiveGroup == 'function');
var group = new fabric.Group([makeRect(), makeRect()]);
canvas.setActiveGroup(group);
- equal(canvas.discardActiveGroup(), canvas, 'chainable');
+ equal(canvas.discardActiveGroup(), canvas, 'should be chainable');
equal(canvas.getActiveGroup(), null, 'removing active group sets it to null');
});
@@ -867,14 +936,14 @@
ok(typeof canvas.getWidth == 'function');
equal(canvas.getWidth(), 600);
- equal(canvas.setWidth(444), canvas, 'chainable');
+ equal(canvas.setWidth(444), canvas, 'should be chainable');
equal(canvas.getWidth(), 444);
});
test('getSetHeight', function() {
ok(typeof canvas.getHeight == 'function');
equal(canvas.getHeight(), 600);
- equal(canvas.setHeight(765), canvas, 'chainable');
+ equal(canvas.setHeight(765), canvas, 'should be chainable');
equal(canvas.getHeight(), 765);
});
@@ -906,25 +975,6 @@
ok(canvas.containsPoint(eventStub, rect), 'on rect at (200, 200) should be within area (175, 175, 225, 225)');
});
- // asyncTest('resizeImageToFit', function() {
- // ok(typeof canvas._resizeImageToFit == 'function');
-
- // var imgEl = fabric.util.makeElement('img', { src: '../fixtures/very_large_image.jpg' }),
- // ORIGINAL_WIDTH = 3888,
- // ORIGINAL_HEIGHT = 2592;
-
- // setTimeout(function() {
- // equal(imgEl.width, ORIGINAL_WIDTH);
- // equal(imgEl.height, ORIGINAL_HEIGHT);
-
- // canvas._resizeImageToFit(imgEl);
-
- // ok(imgEl.width < ORIGINAL_WIDTH);
-
- // start();
- // }, 2000);
- // });
-
asyncTest('fxRemove', function() {
ok(typeof canvas.fxRemove == 'function');
@@ -973,22 +1023,6 @@
// }, 1000);
// });
- test('selection:cleared', function() {
- var isFired = false;
- canvas.on('selection:cleared', function() { isFired = true });
-
- canvas.add(new fabric.Rect());
- canvas.remove(canvas.item(0));
-
- equal(isFired, false, 'removing inactive object shouldnt fire "selection:cleared"');
-
- canvas.add(new fabric.Rect());
- canvas.setActiveObject(canvas.item(0));
- canvas.remove(canvas.item(0));
-
- equal(isFired, true, 'removing active object should fire "selection:cleared"');
- });
-
test('clipTo', function() {
canvas.clipTo = function(ctx) {
ctx.arc(0, 0, 10, 0, Math.PI * 2, false);
diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js
index 9ed22581..e8aa7aae 100644
--- a/test/unit/canvas_static.js
+++ b/test/unit/canvas_static.js
@@ -190,18 +190,25 @@
test('calcOffset', function() {
ok(typeof canvas.calcOffset == 'function', 'should respond to `calcOffset`');
- equal(canvas, canvas.calcOffset());
+ equal(canvas.calcOffset(), canvas, 'should be chainable');
});
test('add', function() {
- var rect = makeRect();
+ var rect1 = makeRect(),
+ rect2 = makeRect(),
+ rect3 = makeRect(),
+ rect4 = makeRect();
ok(typeof canvas.add == 'function');
- ok(canvas === canvas.add(rect), 'should be chainable');
- equal(canvas.item(0), rect);
+ equal(canvas.add(rect1), canvas, 'should be chainable');
+ strictEqual(canvas.item(0), rect1);
- canvas.add(makeRect(), makeRect(), makeRect());
+ canvas.add(rect2, rect3, rect4);
equal(canvas.getObjects().length, 4, 'should support multiple arguments');
+
+ strictEqual(canvas.item(1), rect2);
+ strictEqual(canvas.item(2), rect3);
+ strictEqual(canvas.item(3), rect4);
});
test('add renderOnAddRemove disabled', function() {
@@ -218,7 +225,7 @@
canvas.on('after:render', countRenderAll);
- ok(canvas === canvas.add(rect), 'should be chainable');
+ equal(canvas.add(rect), canvas, 'should be chainable');
equal(renderAllCount, 0);
equal(canvas.item(0), rect);
@@ -234,6 +241,32 @@
canvas.renderOnAddRemove = originalRenderOnAddition;
});
+ test('object:added', function() {
+ var objectsAdded = [];
+
+ canvas.on('object:added', function(e) {
+ objectsAdded.push(e.target);
+ });
+
+ var rect = new fabric.Rect({ width: 10, height: 20 });
+ canvas.add(rect);
+
+ deepEqual(objectsAdded[0], rect);
+
+ var circle1 = new fabric.Circle(),
+ circle2 = new fabric.Circle();
+
+ canvas.add(circle1, circle2);
+
+ strictEqual(objectsAdded[1], circle1);
+ strictEqual(objectsAdded[2], circle2);
+
+ var circle3 = new fabric.Circle();
+ canvas.insertAt(circle3, 2);
+
+ strictEqual(objectsAdded[3], circle3);
+ });
+
test('insertAt', function() {
var rect1 = makeRect(),
rect2 = makeRect();
@@ -244,10 +277,10 @@
var rect = makeRect();
canvas.insertAt(rect, 1);
- equal(canvas.item(1), rect);
+ strictEqual(canvas.item(1), rect);
canvas.insertAt(rect, 2);
- equal(canvas.item(2), rect);
- equal(canvas, canvas.insertAt(rect, 2), 'should be chainable');
+ strictEqual(canvas.item(2), rect);
+ equal(canvas.insertAt(rect, 2), canvas, 'should be chainable');
});
test('insertAt renderOnAddRemove disabled', function() {
@@ -273,7 +306,7 @@
canvas.insertAt(rect, 1);
equal(renderAllCount, 0);
- equal(canvas.item(1), rect);
+ strictEqual(canvas.item(1), rect);
canvas.insertAt(rect, 2);
equal(renderAllCount, 0);
@@ -284,15 +317,90 @@
canvas.renderOnAddRemove = originalRenderOnAddition;
});
+ test('remove', function() {
+ var rect1 = makeRect(),
+ rect2 = makeRect(),
+ rect3 = makeRect(),
+ rect4 = makeRect();
+
+ canvas.add(rect1, rect2, rect3, rect4);
+
+ ok(typeof canvas.remove == 'function');
+ equal(canvas.remove(rect1), canvas, 'should be chainable');
+ strictEqual(canvas.item(0), rect2, 'should be second object');
+
+ canvas.remove(rect2, rect3);
+ strictEqual(canvas.item(0), rect4);
+
+ canvas.remove(rect4);
+ equal(canvas.isEmpty(), true, 'canvas should be empty');
+ });
+
+ test('remove renderOnAddRemove disabled', function() {
+ var rect1 = makeRect(),
+ rect2 = makeRect(),
+ originalRenderOnAddition,
+ renderAllCount = 0;
+
+ function countRenderAll() {
+ renderAllCount++;
+ }
+
+ originalRenderOnAddition = canvas.renderOnAddRemove;
+ canvas.renderOnAddRemove = false;
+
+ canvas.on('after:render', countRenderAll);
+
+ canvas.add(rect1, rect2);
+ equal(renderAllCount, 0);
+
+ equal(canvas.remove(rect1), canvas, 'should be chainable');
+ equal(renderAllCount, 0);
+ strictEqual(canvas.item(0), rect2, 'only second object should be left');
+
+ canvas.renderAll();
+ equal(renderAllCount, 1);
+
+ canvas.off('after:render', countRenderAll);
+ canvas.renderOnAddRemove = originalRenderOnAddition;
+ });
+
+ test('object:removed', function() {
+ var objectsRemoved = [];
+
+ canvas.on('object:removed', function(e) {
+ objectsRemoved.push(e.target);
+ });
+
+ var rect = new fabric.Rect({ width: 10, height: 20 }),
+ circle1 = new fabric.Circle(),
+ circle2 = new fabric.Circle();
+
+ canvas.add(rect, circle1, circle2);
+
+ strictEqual(canvas.item(0), rect);
+ strictEqual(canvas.item(1), circle1);
+ strictEqual(canvas.item(2), circle2);
+
+ canvas.remove(rect);
+ strictEqual(objectsRemoved[0], rect);
+
+ canvas.remove(circle1, circle2);
+ strictEqual(objectsRemoved[1], circle1);
+ strictEqual(objectsRemoved[2], circle2);
+
+ equal(canvas.isEmpty(), true, 'canvas should be empty');
+ });
+
test('clearContext', function() {
ok(typeof canvas.clearContext == 'function');
- equal(canvas, canvas.clearContext(canvas.contextContainer), 'chainable');
+ equal(canvas.clearContext(canvas.contextContainer), canvas, 'should be chainable');
});
test('clear', function() {
ok(typeof canvas.clear == 'function');
- equal(canvas, canvas.clear());
+ equal(canvas.clear(), canvas, 'should be chainable');
equal(canvas.getObjects().length, 0);
});
@@ -665,44 +773,6 @@
});
});
- test('remove', function() {
- ok(typeof canvas.remove == 'function');
- var rect1 = makeRect(),
- rect2 = makeRect();
- canvas.add(rect1, rect2);
- equal(canvas.remove(rect1), rect1, 'should return removed object');
- equal(canvas.item(0), rect2, 'only second object should be left');
- });
-
- test('remove renderOnAddRemove disabled', function() {
- var rect1 = makeRect(),
- rect2 = makeRect(),
- originalRenderOnAddition,
- renderAllCount = 0;
-
- function countRenderAll() {
- renderAllCount++;
- }
-
- originalRenderOnAddition = canvas.renderOnAddRemove;
- canvas.renderOnAddRemove = false;
-
- canvas.on('after:render', countRenderAll);
-
- canvas.add(rect1, rect2);
- equal(renderAllCount, 0);
-
- equal(canvas.remove(rect1), rect1, 'should return removed object');
- equal(renderAllCount, 0);
- equal(canvas.item(0), rect2, 'only second object should be left');
-
- canvas.renderAll();
- equal(renderAllCount, 1);
-
- canvas.off('after:render', countRenderAll);
- canvas.renderOnAddRemove = originalRenderOnAddition;
- });
-
test('sendToBack', function() {
ok(typeof canvas.sendToBack == 'function');
@@ -940,36 +1010,17 @@
test('getSetWidth', function() {
ok(typeof canvas.getWidth == 'function');
equal(canvas.getWidth(), 600);
- equal(canvas.setWidth(444), canvas, 'chainable');
+ equal(canvas.setWidth(444), canvas, 'should be chainable');
equal(canvas.getWidth(), 444);
});
test('getSetHeight', function() {
ok(typeof canvas.getHeight == 'function');
equal(canvas.getHeight(), 600);
- equal(canvas.setHeight(765), canvas, 'chainable');
+ equal(canvas.setHeight(765), canvas, 'should be chainable');
equal(canvas.getHeight(), 765);
});
- // asyncTest('resizeImageToFit', function() {
- // ok(typeof canvas._resizeImageToFit == 'function');
-
- // var imgEl = fabric.util.makeElement('img', { src: '../fixtures/very_large_image.jpg' }),
- // ORIGINAL_WIDTH = 3888,
- // ORIGINAL_HEIGHT = 2592;
-
- // setTimeout(function() {
- // equal(imgEl.width, ORIGINAL_WIDTH);
- // equal(imgEl.height, ORIGINAL_HEIGHT);
-
- // canvas._resizeImageToFit(imgEl);
-
- // ok(imgEl.width < ORIGINAL_WIDTH);
-
- // start();
- // }, 2000);
- // });
-
asyncTest('fxRemove', function() {
ok(typeof canvas.fxRemove == 'function');
@@ -982,7 +1033,7 @@
}
ok(canvas.item(0) === rect);
- ok(canvas.fxRemove(rect, { onComplete: onComplete }) === canvas, 'should be chainable');
+ equal(canvas.fxRemove(rect, { onComplete: onComplete }), canvas, 'should be chainable');
setTimeout(function() {
equal(canvas.item(0), undefined);
@@ -1017,30 +1068,4 @@
// }, 1000);
// });
- test('object:added', function() {
-
- var objectsAdded = [];
- canvas.on('object:added', function(e) {
- objectsAdded.push(e.target);
- });
-
- var rect = new fabric.Rect({ width: 10, height: 20 });
- canvas.add(rect);
-
- deepEqual(objectsAdded[0], rect);
-
- var circle1 = new fabric.Circle(),
- circle2 = new fabric.Circle();
-
- canvas.add(circle1, circle2);
-
- deepEqual(objectsAdded[1], circle1);
- deepEqual(objectsAdded[2], circle2);
-
- var circle3 = new fabric.Circle();
- canvas.insertAt(circle3, 2);
-
- deepEqual(objectsAdded[3], circle3);
- });
-
})();
diff --git a/test/unit/color.js b/test/unit/color.js
index 9b25ba07..e669a94f 100644
--- a/test/unit/color.js
+++ b/test/unit/color.js
@@ -204,6 +204,17 @@
equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
});
+ test('fromRgba (percentage values with decimals)', function() {
+ var originalRgba = 'rgba( 100.00%, 100.00%, 100.00% , 0.5 )';
+ oColor = fabric.Color.fromRgba(originalRgba);
+ ok(oColor);
+ ok(oColor instanceof fabric.Color);
+ equal(oColor.toRgba(), 'rgba(255,255,255,0.5)');
+ equal(oColor.toHex(), 'FFFFFF');
+ equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
+ });
+
+
test('fromHsl', function() {
ok(typeof fabric.Color.fromHsl == 'function');
var originalHsl = 'hsl(262,80%,12%)';
@@ -313,4 +324,8 @@
oColor.overlayWith(new fabric.Color('rgb(0,0,0)'));
equal(oColor.toRgb(), 'rgb(128,128,128)');
});
+
+ test('transparent', function() {
+ deepEqual(new fabric.Color('transparent').getSource(), [255,255,255,0]);
+ });
})();
diff --git a/test/unit/group.js b/test/unit/group.js
index a102fc87..c6e93a21 100644
--- a/test/unit/group.js
+++ b/test/unit/group.js
@@ -52,17 +52,35 @@
ok(typeof group.getObjects == 'function');
ok(Object.prototype.toString.call(group.getObjects()) == '[object Array]', 'should be an array');
equal(group.getObjects().length, 2, 'should have 2 items');
- deepEqual([ rect1, rect2 ], group.getObjects(), 'should return deepEqual objects as those passed to constructor');
+ deepEqual(group.getObjects(), [ rect1, rect2 ], 'should return deepEqual objects as those passed to constructor');
+ });
+
+ test('getObjects with type', function() {
+ var rect = new fabric.Rect({ width: 10, height: 20 }),
+ circle = new fabric.Circle({ radius: 30 });
+
+ var group = new fabric.Group([ rect, circle ]);
+
+ equal(group.size(), 2, 'should have length=2 initially');
+
+ deepEqual(group.getObjects('rect'), [rect], 'should return rect only');
+ deepEqual(group.getObjects('circle'), [circle], 'should return circle only');
});
test('add', function() {
var group = makeGroupWith2Objects();
- var rect = new fabric.Rect();
+ var rect1 = new fabric.Rect(),
+ rect2 = new fabric.Rect(),
+ rect3 = new fabric.Rect();
ok(typeof group.add == 'function');
- equal(group.add(rect), group, 'should be chainable');
- equal(group.getObjects()[group.getObjects().length-1], rect, 'last object should be newly added one');
+ equal(group.add(rect1), group, 'should be chainable');
+ strictEqual(group.item(group.size()-1), rect1, 'last object should be newly added one');
equal(group.getObjects().length, 3, 'there should be 3 objects');
+
+ group.add(rect2, rect3);
+ strictEqual(group.item(group.size()-1), rect3, 'last object should be last added one');
+ equal(group.size(), 5, 'there should be 5 objects');
});
test('remove', function() {
@@ -72,8 +90,11 @@
group = new fabric.Group([ rect1, rect2, rect3 ]);
ok(typeof group.remove == 'function');
- equal(group.remove(rect2), rect2, 'should return removed object');
- deepEqual([rect1, rect3], group.getObjects(), 'should remove object properly');
+ equal(group.remove(rect2), group, 'should be chainable');
+ deepEqual(group.getObjects(), [rect1, rect3], 'should remove object properly');
+
+ group.remove(rect1, rect3);
+ equal(group.isEmpty(), true, 'group should be empty');
});
test('size', function() {
@@ -452,7 +473,7 @@ test('toObject without default values', function() {
equal(group.item(1), rect1);
group.insertAt(rect2, 2);
equal(group.item(2), rect2);
- equal(group, group.insertAt(rect1, 2), 'should be chainable');
+ equal(group.insertAt(rect1, 2), group, 'should be chainable');
});
// asyncTest('cloning group with image', function() {
diff --git a/test/unit/image.js b/test/unit/image.js
index bc412498..24134a58 100644
--- a/test/unit/image.js
+++ b/test/unit/image.js
@@ -161,14 +161,16 @@
equal(objRepr.crossOrigin, '', 'toObject should return proper crossOrigin value');
var elImage2 = _createImageElement();
+ elImage2.crossOrigin = 'anonymous';
image.setElement(elImage2);
- equal(elImage2.crossOrigin, '', 'setElement should set proper crossOrigin on an img element');
+ equal(elImage2.crossOrigin, 'anonymous', 'setElement should set proper crossOrigin on an img element');
// fromObject doesn't work on Node :/
if (fabric.isLikelyNode) {
start();
return;
}
+
fabric.Image.fromObject(objRepr, function(img) {
equal(img.crossOrigin, '');
start();
diff --git a/test/unit/image_filters.js b/test/unit/image_filters.js
index 019bfb86..9a87afc6 100644
--- a/test/unit/image_filters.js
+++ b/test/unit/image_filters.js
@@ -92,7 +92,7 @@
var filter = new fabric.Image.filters.Brightness();
equal(filter.type, 'Brightness');
- equal(filter.brightness, 100);
+ equal(filter.brightness, 0);
var filter2 = new fabric.Image.filters.Brightness({brightness: 30});
equal(filter2.brightness, 30);
@@ -108,6 +108,11 @@
ok(typeof filter.toObject == 'function');
var object = filter.toObject();
+ equal(JSON.stringify(object), '{"type":"Brightness","brightness":0}');
+
+ filter.brightness = 100;
+
+ object = filter.toObject();
equal(JSON.stringify(object), '{"type":"Brightness","brightness":100}');
});
@@ -116,6 +121,11 @@
ok(typeof filter.toJSON == 'function');
var json = filter.toJSON();
+ equal(JSON.stringify(json), '{"type":"Brightness","brightness":0}');
+
+ filter.brightness = 100;
+
+ json = filter.toJSON();
equal(JSON.stringify(json), '{"type":"Brightness","brightness":100}');
});
@@ -330,7 +340,7 @@
var filter = new fabric.Image.filters.Noise();
equal(filter.type, 'Noise');
- equal(filter.noise, 100);
+ equal(filter.noise, 0);
var filter2 = new fabric.Image.filters.Noise({noise: 200});
equal(filter2.noise, 200);
@@ -346,6 +356,11 @@
ok(typeof filter.toObject == 'function');
var object = filter.toObject();
+ equal(JSON.stringify(object), '{"type":"Noise","noise":0}');
+
+ filter.noise = 100;
+
+ object = filter.toObject();
equal(JSON.stringify(object), '{"type":"Noise","noise":100}');
});
@@ -354,6 +369,11 @@
ok(typeof filter.toJSON == 'function');
var json = filter.toJSON();
+ equal(JSON.stringify(json), '{"type":"Noise","noise":0}');
+
+ filter.noise = 100;
+
+ json = filter.toJSON();
equal(JSON.stringify(json), '{"type":"Noise","noise":100}');
});
diff --git a/test/unit/line.js b/test/unit/line.js
index 1c3102e5..1c0ced12 100644
--- a/test/unit/line.js
+++ b/test/unit/line.js
@@ -4,8 +4,8 @@
'type': 'line',
'originX': 'left',
'originY': 'top',
- 'left': 12,
- 'top': 13,
+ 'left': 11,
+ 'top': 12,
'width': 2,
'height': 2,
'fill': 'rgb(0,0,0)',
@@ -164,4 +164,430 @@
// equal(200, line.height);
// });
+ var lineCoordsCases = [
+ { description: 'default to 0 left and 0 top',
+ givenLineArgs: {},
+ expectedCoords: {
+ left: 0,
+ top: 0,
+ }
+ },
+ { description: 'origin defaults to left-top',
+ givenLineArgs: {
+ points: [0, 0, 11, 22],
+ },
+ expectedCoords: {
+ left: 0,
+ top: 0,
+ }
+ },
+ { description: 'equal smallest points when origin is left-top and line not offset',
+ givenLineArgs: {
+ points: [0, 0, 12.3, 34.5],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ },
+ },
+ expectedCoords: {
+ left: 0,
+ top: 0,
+ }
+ },
+ { description: 'include offsets for left-top origin',
+ givenLineArgs: {
+ points: [0+33, 0+44, 11+33, 22+44],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ },
+ },
+ expectedCoords: {
+ left: 33,
+ top: 44,
+ }
+ },
+ { description: 'equal half-dimensions when origin is center and line not offset',
+ givenLineArgs: {
+ points: [0, 0, 12.3, 34.5],
+ options: {
+ originX: 'center',
+ originY: 'center',
+ },
+ },
+ expectedCoords: {
+ left: 0.5 * 12.3,
+ top: 0.5 * 34.5,
+ }
+ },
+ { description: 'include offsets for center-center origin',
+ givenLineArgs: {
+ points: [0+9.87, 0-4.32, 12.3+9.87, 34.5-4.32],
+ options: {
+ originX: 'center',
+ originY: 'center',
+ },
+ },
+ expectedCoords: {
+ left: (0.5 * 12.3) + 9.87,
+ top: (0.5 * 34.5) - 4.32,
+ }
+ },
+ { description: 'equal full dimensions when origin is right-bottom and line not offset',
+ givenLineArgs: {
+ points: [0, 0, 55, 18],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ },
+ },
+ expectedCoords: {
+ left: 55,
+ top: 18,
+ }
+ },
+ { description: 'include offsets for right-bottom origin',
+ givenLineArgs: {
+ points: [0-3.14, 0-1.41, 55-3.14, 18-1.41],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ },
+ },
+ expectedCoords: {
+ left: 55 - 3.14,
+ top: 18 - 1.41,
+ }
+ },
+ { description: 'arent changed by rotation for left-top origin',
+ givenLineArgs: {
+ points: [1, 2, 30, 40],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ angle: 67,
+ }
+ },
+ expectedCoords: {
+ left: 1,
+ top: 2,
+ }
+ },
+ { description: 'arent changed by rotation for right-bottom origin',
+ givenLineArgs: {
+ points: [1, 2, 30, 40],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ angle: 67,
+ }
+ },
+ expectedCoords: {
+ left: 30,
+ top: 40,
+ }
+ },
+ { description: 'arent changed by scaling for left-top origin',
+ givenLineArgs: {
+ points: [1, 2, 30, 40],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ scale: 2.1,
+ }
+ },
+ expectedCoords: {
+ left: 1,
+ top: 2,
+ }
+ },
+ { description: 'arent changed by scaling for right-bottom origin',
+ givenLineArgs: {
+ points: [1, 2, 30, 40],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ scale: 1.2,
+ }
+ },
+ expectedCoords: {
+ left: 30,
+ top: 40,
+ }
+ },
+ { description: 'arent changed by strokeWidth for left-top origin',
+ givenLineArgs: {
+ points: [31, 41, 59, 26],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ stroke: 'black',
+ strokeWidth: '53'
+ }
+ },
+ expectedCoords: {
+ left: 31,
+ top: 26,
+ }
+ },
+ { description: 'arent changed by strokeWidth for center-center origin',
+ givenLineArgs: {
+ points: [0+31, 15+26, 28+31, 0+26],
+ options: {
+ originX: 'center',
+ originY: 'center',
+ stroke: 'black',
+ strokeWidth: '53'
+ }
+ },
+ expectedCoords: {
+ left: (0.5 * 28) + 31,
+ top: (0.5 * 15) + 26,
+ }
+ },
+ { description: 'arent changed by strokeWidth for right-bottom origin',
+ givenLineArgs: {
+ points: [1, 2, 30, 40],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ stroke: 'black',
+ strokeWidth: '53'
+ }
+ },
+ expectedCoords: {
+ left: 30,
+ top: 40,
+ }
+ },
+ { description: 'left and top options override points',
+ givenLineArgs: {
+ points: [12, 34, 56, 78],
+ options: {
+ left: 98,
+ top: 76,
+ }
+ },
+ expectedCoords: {
+ left: 98,
+ top: 76,
+ }
+ },
+ { description: '0 left and 0 top options override points',
+ givenLineArgs: {
+ points: [12, 34, 56, 78],
+ options: {
+ left: 0,
+ top: 0,
+ }
+ },
+ expectedCoords: {
+ left: 0,
+ top: 0,
+ }
+ },
+ { description: 'equal x2 and y2 for left-top origin when x1 and y1 are largest and line not offset',
+ givenLineArgs: {
+ points: [100, 200, 30, 40],
+ options: {
+ originX: 'left',
+ originY: 'top',
+ }
+ },
+ expectedCoords: {
+ left: 30,
+ top: 40,
+ }
+ },
+ { description: 'equal half-dimensions for center-center origin when x1 and y1 are largest and line not offset',
+ givenLineArgs: {
+ points: [100, 200, 0, 0],
+ options: {
+ originX: 'center',
+ originY: 'center',
+ }
+ },
+ expectedCoords: {
+ left: 0.5 * 100,
+ top: 0.5 * 200,
+ }
+ },
+ { description: 'equal x1 and y1 for right-bottom origin when x1 and y1 are largest and line not offset',
+ givenLineArgs: {
+ points: [100, 200, 0, 0],
+ options: {
+ originX: 'right',
+ originY: 'bottom',
+ }
+ },
+ expectedCoords: {
+ left: 100,
+ top: 200,
+ }
+ },
+ ];
+
+ lineCoordsCases.forEach(function (c_) {
+ test('stroke-less line coords ' + c_.description, function() {
+ var points = c_.givenLineArgs.points;
+ var options = c_.givenLineArgs.options;
+
+ var givenLine = new fabric.Line(
+ points,
+ options
+ );
+
+ equal(givenLine.left, c_.expectedCoords.left);
+ equal(givenLine.top, c_.expectedCoords.top);
+ });
+ });
+
+ var getLeftToOriginXCases = [
+ { description: 'is x1 for left origin and x1 lesser than x2',
+ givenOrigin: 'left',
+ givenPoints: [0, 0, 1, 0],
+ expectedLeft: 0,
+ },
+ { description: 'is x2 for left origin and x1 greater than x2',
+ givenOrigin: 'left',
+ givenPoints: [1, 0, 0, 0],
+ expectedLeft: 0,
+ },
+ { description: 'includes positive offset for left origin',
+ givenOrigin: 'left',
+ givenPoints: [0+20, 0, 1+20, 0],
+ expectedLeft: 0+20,
+ },
+ { description: 'includes negative offset for left origin',
+ givenOrigin: 'left',
+ givenPoints: [0-11, 0, 1-11, 0],
+ expectedLeft: 0-11,
+ },
+ { description: 'is half of x1 for center origin and x1 > x2',
+ givenOrigin: 'center',
+ givenPoints: [4, 0, 0, 0],
+ expectedLeft: 0.5 * 4,
+ },
+ { description: 'is half of x2 for center origin and x1 < x2',
+ givenOrigin: 'center',
+ givenPoints: [0, 0, 7, 0],
+ expectedLeft: 0.5 * 7,
+ },
+ { description: 'includes positive offset for center origin',
+ givenOrigin: 'center',
+ givenPoints: [0+39, 0, 7+39, 0],
+ expectedLeft: (0.5 * 7) + 39,
+ },
+ { description: 'includes negative offset for center origin',
+ givenOrigin: 'center',
+ givenPoints: [4-13, 0, 0-13, 0],
+ expectedLeft: (0.5 * 4) - 13,
+ },
+ { description: 'is x1 for right origin and x1 > x2',
+ givenOrigin: 'right',
+ givenPoints: [9, 0, 0, 0],
+ expectedLeft: 9,
+ },
+ { description: 'is x2 for right origin and x1 < x2',
+ givenOrigin: 'right',
+ givenPoints: [0, 0, 6, 0],
+ expectedLeft: 6,
+ },
+ { description: 'includes positive offset for right origin',
+ givenOrigin: 'right',
+ givenPoints: [0+47, 0, 6+47, 0],
+ expectedLeft: 6 + 47,
+ },
+ { description: 'includes negative offset for right origin',
+ givenOrigin: 'right',
+ givenPoints: [9-17, 0, 0-17, 0],
+ expectedLeft: 9 - 17,
+ },
+ ];
+
+ getLeftToOriginXCases.forEach(function (c_) {
+ test('Line.getLeftToOriginX() ' + c_.description, function () {
+ var line = new fabric.Line(
+ c_.givenPoints,
+ { originX: c_.givenOrigin }
+ );
+
+ equal(line._getLeftToOriginX(), c_.expectedLeft);
+ });
+ });
+
+ var getTopToOriginYCases = [
+ { description: 'is y1 for top origin and y1 lesser than y2',
+ givenOrigin: 'top',
+ givenPoints: [0, 0, 0, 1],
+ expectedTop: 0,
+ },
+ { description: 'is y2 for top origin and y1 greater than y2',
+ givenOrigin: 'top',
+ givenPoints: [0, 1, 0, 0],
+ expectedTop: 0,
+ },
+ { description: 'includes positive offset for top origin',
+ givenOrigin: 'top',
+ givenPoints: [0, 0+20, 0, 1+20],
+ expectedTop: 0+20,
+ },
+ { description: 'includes negative offset for top origin',
+ givenOrigin: 'top',
+ givenPoints: [0, 0-11, 0, 1-11],
+ expectedTop: 0-11,
+ },
+ { description: 'is half of y1 for center origin and y1 > y2',
+ givenOrigin: 'center',
+ givenPoints: [0, 4, 0, 0],
+ expectedTop: 0.5 * 4,
+ },
+ { description: 'is half of y2 for center origin and y1 < y2',
+ givenOrigin: 'center',
+ givenPoints: [0, 0, 0, 7],
+ expectedTop: 0.5 * 7,
+ },
+ { description: 'includes positive offset for center origin',
+ givenOrigin: 'center',
+ givenPoints: [0, 0+39, 0, 7+39],
+ expectedTop: (0.5 * 7) + 39,
+ },
+ { description: 'includes negative offset for center origin',
+ givenOrigin: 'center',
+ givenPoints: [0, 4-13, 0, 0-13],
+ expectedTop: (0.5 * 4) - 13,
+ },
+ { description: 'is y1 for bottom origin and y1 > y2',
+ givenOrigin: 'bottom',
+ givenPoints: [0, 9, 0, 0],
+ expectedTop: 9,
+ },
+ { description: 'is y2 for bottom origin and y1 < y2',
+ givenOrigin: 'bottom',
+ givenPoints: [0, 0, 0, 6],
+ expectedTop: 6,
+ },
+ { description: 'includes positive offset for bottom origin',
+ givenOrigin: 'bottom',
+ givenPoints: [0, 0+47, 0, 6+47],
+ expectedTop: 6 + 47,
+ },
+ { description: 'includes negative offset for bottom origin',
+ givenOrigin: 'bottom',
+ givenPoints: [0, 9-17, 0, 0-17],
+ expectedTop: 9 - 17,
+ },
+ ];
+
+ getTopToOriginYCases.forEach(function (c_) {
+ test('Line._getTopToOriginY() ' + c_.description, function () {
+ var line = new fabric.Line(
+ c_.givenPoints,
+ { originY: c_.givenOrigin }
+ );
+
+ equal(line._getTopToOriginY(), c_.expectedTop);
+ });
+ });
+
})();
diff --git a/test/unit/object.js b/test/unit/object.js
index 7930c9cc..b8f79e49 100644
--- a/test/unit/object.js
+++ b/test/unit/object.js
@@ -900,6 +900,18 @@ test('toDataURL & reference to canvas', function() {
equal(canvas.getObjects().length, 0);
});
+ test('object:removed', function() {
+ var object = new fabric.Object();
+ var removedEventFired = false;
+
+ canvas.add(object);
+
+ object.on('removed', function(){ removedEventFired = true; });
+ object.remove();
+
+ ok(removedEventFired);
+ });
+
test('center', function() {
var object = new fabric.Object();
diff --git a/test/unit/parser.js b/test/unit/parser.js
index 8b36a4b0..2550a91f 100644
--- a/test/unit/parser.js
+++ b/test/unit/parser.js
@@ -230,8 +230,9 @@
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual(parsedValue, [1,0,0,1,-10,-20]);
- var ANGLE = 90;
- element.setAttribute('transform', 'rotate(' + ANGLE + ')');
+ var ANGLE_DEG = 90;
+ var ANGLE = ANGLE_DEG * Math.PI / 180;
+ element.setAttribute('transform', 'rotate(' + ANGLE_DEG + ')');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual(parsedValue, [Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0]);
diff --git a/test/unit/path.js b/test/unit/path.js
index fe7f2d55..cb4d5ef4 100644
--- a/test/unit/path.js
+++ b/test/unit/path.js
@@ -89,6 +89,18 @@
});
});
+ asyncTest('path array not shared when cloned', function() {
+ makePathObject(function(originalPath) {
+ originalPath.clone(function(clonedPath) {
+
+ clonedPath.path[0][1] = 200;
+ equal(originalPath.path[0][1], 100);
+
+ start();
+ });
+ });
+ });
+
asyncTest('toDatalessObject', function() {
makePathObject(function(path) {
ok(typeof path.toDatalessObject == 'function');
@@ -148,9 +160,10 @@
transformMatrix: [2, 0, 0, 2, 0, 0]
}));
- var ANGLE = 90;
+ var ANGLE_DEG = 90;
+ var ANGLE = ANGLE_DEG * Math.PI / 180;
- elPath.setAttribute('transform', 'rotate(' + ANGLE + ')');
+ elPath.setAttribute('transform', 'rotate(' + ANGLE_DEG + ')');
fabric.Path.fromElement(elPath, function(path) {
deepEqual(
@@ -162,6 +175,22 @@
});
});
+ asyncTest('numbers with leading decimal point', function() {
+ ok(typeof fabric.Path.fromElement == 'function');
+ var elPath = fabric.document.createElement('path');
+
+ elPath.setAttribute('d', 'M 100 100 L 300 100 L 200 300 z');
+ elPath.setAttribute('transform', 'scale(.2)');
+
+ fabric.Path.fromElement(elPath, function(path) {
+ ok(path instanceof fabric.Path);
+
+ deepEqual(path.toObject().transformMatrix, [0.2, 0, 0, 0.2, 0, 0]);
+
+ start();
+ });
+ });
+
asyncTest('multiple sequences in path commands', function() {
var el = getPathElement('M100 100 l 200 200 300 300 400 -50 z');
fabric.Path.fromElement(el, function(obj) {
@@ -192,4 +221,17 @@
start();
});
});
+
+ asyncTest('compressed path commands with e^x', function() {
+ var el = getPathElement('M56.224e2 84.12E-2c-.047.132-.138.221-.322.215.046-.131.137-.221.322-.215m-.050 -20.100z');
+ fabric.Path.fromElement(el, function(obj) {
+
+ deepEqual(obj.path[0], ['M', 5622.4, 0.8412]);
+ deepEqual(obj.path[1], ['c', -0.047, 0.132, -0.138, 0.221, -0.322, 0.215]);
+ deepEqual(obj.path[2], ['c', 0.046, -0.131, 0.137, -0.221, 0.322, -0.215]);
+ deepEqual(obj.path[3], ['m', -0.05, -20.100]);
+ deepEqual(obj.path[4], ['z']);
+ start();
+ });
+ });
})();
diff --git a/test/unit/path_group.js b/test/unit/path_group.js
index 620aff1c..29e5e190 100644
--- a/test/unit/path_group.js
+++ b/test/unit/path_group.js
@@ -66,7 +66,7 @@
function getPathGroupObject(callback) {
getPathObjects(function(objects) {
callback(new fabric.PathGroup(objects));
- })
+ });
}
QUnit.module('fabric.PathGroup');
@@ -178,6 +178,12 @@
pathGroup.getObjects()[0].set('fill', 'black');
equal(pathGroup.isSameColor(), false);
+
+ // case
+ pathGroup.getObjects()[0].set('fill', '#ff5555');
+ pathGroup.getObjects()[1].set('fill', '#FF5555');
+ equal(pathGroup.isSameColor(), true);
+
start();
});
});
diff --git a/test/unit/text.js b/test/unit/text.js
index dbce4820..73b35dfb 100644
--- a/test/unit/text.js
+++ b/test/unit/text.js
@@ -151,8 +151,12 @@
// text.width = 20;
var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_TEXT_OBJECT), {
- left: 10,
- top: -26
+ left: 4,
+ top: -10.4,
+ width: 8,
+ height: 20.8,
+ fontSize: 16,
+ originX: 'center'
});
deepEqual(text.toObject(), expectedObject);
@@ -203,7 +207,8 @@
fontStyle: 'italic',
fontWeight: 'bold',
fontSize: 123,
- textDecoration: 'underline'
+ textDecoration: 'underline',
+ originX: 'center'
});
deepEqual(textWithAttrs.toObject(), expectedObject);
diff --git a/test/unit/util.js b/test/unit/util.js
index 0876017b..77c28fe1 100644
--- a/test/unit/util.js
+++ b/test/unit/util.js
@@ -476,19 +476,28 @@
asyncTest('fabric.util.groupSVGElements', function() {
ok(typeof fabric.util.groupSVGElements == 'function');
- var group1, group2;
+ var group1;
fabric.loadSVGFromString(SVG_WITH_1_ELEMENT, function(objects, options) {
group1 = fabric.util.groupSVGElements(objects, options);
});
+
+ setTimeout(function() {
+ ok(group1 instanceof fabric.Polygon);
+ start();
+ }, 2000);
+ });
+
+ asyncTest('fabric.util.groupSVGElements #2', function() {
+
+ var group2;
fabric.loadSVGFromString(SVG_WITH_2_ELEMENTS, function(objects, options) {
group2 = fabric.util.groupSVGElements(objects, options);
});
setTimeout(function() {
- ok(group1 instanceof fabric.Polygon);
ok(group2 instanceof fabric.PathGroup);
start();
- }, 1000);
+ }, 2000);
});
test('Array.prototype.indexOf', function() {