var fabric = fabric || {
version: "1.5.0"
};
if (typeof exports !== "undefined") {
exports.fabric = fabric;
}
if (typeof document !== "undefined" && typeof window !== "undefined") {
fabric.document = document;
fabric.window = window;
window.fabric = fabric;
} else {
fabric.document = require("jsdom").jsdom("
");
if (fabric.document.createWindow) {
fabric.window = fabric.document.createWindow();
} else {
fabric.window = fabric.document.parentWindow;
}
}
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" ];
fabric.DPI = 96;
fabric.reNum = "(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)";
(function() {
function _removeEventListener(eventName, handler) {
if (!this.__eventListeners[eventName]) {
return;
}
if (handler) {
fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
} else {
this.__eventListeners[eventName].length = 0;
}
}
function observe(eventName, handler) {
if (!this.__eventListeners) {
this.__eventListeners = {};
}
if (arguments.length === 1) {
for (var prop in eventName) {
this.on(prop, eventName[prop]);
}
} else {
if (!this.__eventListeners[eventName]) {
this.__eventListeners[eventName] = [];
}
this.__eventListeners[eventName].push(handler);
}
return this;
}
function stopObserving(eventName, handler) {
if (!this.__eventListeners) {
return;
}
if (arguments.length === 0) {
this.__eventListeners = {};
} else if (arguments.length === 1 && typeof arguments[0] === "object") {
for (var prop in eventName) {
_removeEventListener.call(this, prop, eventName[prop]);
}
} else {
_removeEventListener.call(this, eventName, handler);
}
return this;
}
function fire(eventName, options) {
if (!this.__eventListeners) {
return;
}
var listenersForEvent = this.__eventListeners[eventName];
if (!listenersForEvent) {
return;
}
for (var i = 0, len = listenersForEvent.length; i < len; i++) {
listenersForEvent[i].call(this, options || {});
}
return this;
}
fabric.Observable = {
observe: observe,
stopObserving: stopObserving,
fire: fire,
on: observe,
off: stopObserving,
trigger: fire
};
})();
fabric.Collection = {
add: function() {
this._objects.push.apply(this._objects, arguments);
for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
this.renderOnAddRemove && this.renderAll();
return this;
},
insertAt: function(object, index, nonSplicing) {
var objects = this.getObjects();
if (nonSplicing) {
objects[index] = object;
} else {
objects.splice(index, 0, object);
}
this._onObjectAdded(object);
this.renderOnAddRemove && this.renderAll();
return this;
},
remove: function() {
var objects = this.getObjects(), index;
for (var i = 0, length = arguments.length; i < length; i++) {
index = objects.indexOf(arguments[i]);
if (index !== -1) {
objects.splice(index, 1);
this._onObjectRemoved(arguments[i]);
}
}
this.renderOnAddRemove && this.renderAll();
return this;
},
forEachObject: function(callback, context) {
var objects = this.getObjects(), i = objects.length;
while (i--) {
callback.call(context, objects[i], i, objects);
}
return this;
},
getObjects: function(type) {
if (typeof type === "undefined") {
return this._objects;
}
return this._objects.filter(function(o) {
return o.type === type;
});
},
item: function(index) {
return this.getObjects()[index];
},
isEmpty: function() {
return this.getObjects().length === 0;
},
size: function() {
return this.getObjects().length;
},
contains: function(object) {
return this.getObjects().indexOf(object) > -1;
},
complexity: function() {
return this.getObjects().reduce(function(memo, current) {
memo += current.complexity ? current.complexity() : 0;
return memo;
}, 0);
}
};
(function(global) {
var sqrt = Math.sqrt, atan2 = Math.atan2, PiBy180 = Math.PI / 180;
fabric.util = {
removeFromArray: function(array, value) {
var idx = array.indexOf(value);
if (idx !== -1) {
array.splice(idx, 1);
}
return array;
},
getRandomInt: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
degreesToRadians: function(degrees) {
return degrees * PiBy180;
},
radiansToDegrees: function(radians) {
return radians / PiBy180;
},
rotatePoint: function(point, origin, radians) {
var sin = Math.sin(radians), cos = Math.cos(radians);
point.subtractEquals(origin);
var rx = point.x * cos - point.y * sin, ry = point.x * sin + point.y * cos;
return new fabric.Point(rx, ry).addEquals(origin);
},
transformPoint: function(p, t, ignoreOffset) {
if (ignoreOffset) {
return new fabric.Point(t[0] * p.x + t[2] * p.y, t[1] * p.x + t[3] * p.y);
}
return new fabric.Point(t[0] * p.x + t[2] * p.y + t[4], t[1] * p.x + t[3] * p.y + t[5]);
},
invertTransform: function(t) {
var r = t.slice(), a = 1 / (t[0] * t[3] - t[1] * t[2]);
r = [ a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0 ];
var o = fabric.util.transformPoint({
x: t[4],
y: t[5]
}, r);
r[4] = -o.x;
r[5] = -o.y;
return r;
},
toFixed: function(number, fractionDigits) {
return parseFloat(Number(number).toFixed(fractionDigits));
},
parseUnit: function(value, fontSize) {
var unit = /\D{0,2}$/.exec(value), number = parseFloat(value);
if (!fontSize) {
fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
}
switch (unit[0]) {
case "mm":
return number * fabric.DPI / 25.4;
case "cm":
return number * fabric.DPI / 2.54;
case "in":
return number * fabric.DPI;
case "pt":
return number * fabric.DPI / 72;
case "pc":
return number * fabric.DPI / 72 * 12;
case "em":
return number * fontSize;
default:
return number;
}
},
falseFunction: function() {
return false;
},
getKlass: function(type, namespace) {
type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
return fabric.util.resolveNamespace(namespace)[type];
},
resolveNamespace: function(namespace) {
if (!namespace) {
return fabric;
}
var parts = namespace.split("."), len = parts.length, obj = global || fabric.window;
for (var i = 0; i < len; ++i) {
obj = obj[parts[i]];
}
return obj;
},
loadImage: function(url, callback, context, crossOrigin) {
if (!url) {
callback && callback.call(context, url);
return;
}
var img = fabric.util.createImage();
img.onload = function() {
callback && callback.call(context, img);
img = img.onload = img.onerror = null;
};
img.onerror = function() {
fabric.log("Error loading " + img.src);
callback && callback.call(context, null, true);
img = img.onload = img.onerror = null;
};
if (url.indexOf("data") !== 0 && typeof crossOrigin !== "undefined") {
img.crossOrigin = crossOrigin;
}
img.src = url;
},
enlivenObjects: function(objects, callback, namespace, reviver) {
objects = objects || [];
function onLoaded() {
if (++numLoadedObjects === numTotalObjects) {
callback && callback(enlivenedObjects);
}
}
var enlivenedObjects = [], numLoadedObjects = 0, numTotalObjects = objects.length;
if (!numTotalObjects) {
callback && callback(enlivenedObjects);
return;
}
objects.forEach(function(o, index) {
if (!o || !o.type) {
onLoaded();
return;
}
var klass = fabric.util.getKlass(o.type, namespace);
if (klass.async) {
klass.fromObject(o, function(obj, error) {
if (!error) {
enlivenedObjects[index] = obj;
reviver && reviver(o, enlivenedObjects[index]);
}
onLoaded();
});
} else {
enlivenedObjects[index] = klass.fromObject(o);
reviver && reviver(o, enlivenedObjects[index]);
onLoaded();
}
});
},
groupSVGElements: function(elements, options, path) {
var object;
object = new fabric.PathGroup(elements, options);
if (typeof path !== "undefined") {
object.setSourcePath(path);
}
return object;
},
populateWithProperties: function(source, destination, properties) {
if (properties && Object.prototype.toString.call(properties) === "[object Array]") {
for (var i = 0, len = properties.length; i < len; i++) {
if (properties[i] in source) {
destination[properties[i]] = source[properties[i]];
}
}
}
},
drawDashedLine: function(ctx, x, y, x2, y2, da) {
var dx = x2 - x, dy = y2 - y, len = sqrt(dx * dx + dy * dy), rot = atan2(dy, dx), dc = da.length, di = 0, draw = true;
ctx.save();
ctx.translate(x, y);
ctx.moveTo(0, 0);
ctx.rotate(rot);
x = 0;
while (len > x) {
x += da[di++ % dc];
if (x > len) {
x = len;
}
ctx[draw ? "lineTo" : "moveTo"](x, 0);
draw = !draw;
}
ctx.restore();
},
createCanvasElement: function(canvasEl) {
canvasEl || (canvasEl = fabric.document.createElement("canvas"));
if (!canvasEl.getContext && typeof G_vmlCanvasManager !== "undefined") {
G_vmlCanvasManager.initElement(canvasEl);
}
return canvasEl;
},
createImage: function() {
return fabric.isLikelyNode ? new (require("canvas").Image)() : fabric.document.createElement("img");
},
createAccessors: function(klass) {
var proto = klass.prototype;
for (var i = proto.stateProperties.length; i--; ) {
var propName = proto.stateProperties[i], capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1), setterName = "set" + capitalizedPropName, getterName = "get" + capitalizedPropName;
if (!proto[getterName]) {
proto[getterName] = function(property) {
return new Function('return this.get("' + property + '")');
}(propName);
}
if (!proto[setterName]) {
proto[setterName] = function(property) {
return new Function("value", 'return this.set("' + property + '", value)');
}(propName);
}
}
},
clipContext: function(receiver, ctx) {
ctx.save();
ctx.beginPath();
receiver.clipTo(ctx);
ctx.clip();
},
multiplyTransformMatrices: function(a, b) {
return [ a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5] ];
},
getFunctionBody: function(fn) {
return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
},
isTransparent: function(ctx, x, y, tolerance) {
if (tolerance > 0) {
if (x > tolerance) {
x -= tolerance;
} else {
x = 0;
}
if (y > tolerance) {
y -= tolerance;
} else {
y = 0;
}
}
var _isTransparent = true, imageData = ctx.getImageData(x, y, tolerance * 2 || 1, tolerance * 2 || 1);
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
var temp = imageData.data[i];
_isTransparent = temp <= 0;
if (_isTransparent === false) {
break;
}
}
imageData = null;
return _isTransparent;
}
};
})(typeof exports !== "undefined" ? exports : this);
(function() {
var arcToSegmentsCache = {}, segmentToBezierCache = {}, boundsOfCurveCache = {}, _join = Array.prototype.join;
function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
var argsString = _join.call(arguments);
if (arcToSegmentsCache[argsString]) {
return arcToSegmentsCache[argsString];
}
var PI = Math.PI, th = rotateX * PI / 180, sinTh = Math.sin(th), cosTh = Math.cos(th), fromX = 0, fromY = 0;
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = -cosTh * toX * .5 - sinTh * toY * .5, py = -cosTh * toY * .5 + sinTh * toX * .5, rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px, pl = rx2 * ry2 - rx2 * py2 - ry2 * px2, root = 0;
if (pl < 0) {
var s = Math.sqrt(1 - pl / (rx2 * ry2));
rx *= s;
ry *= s;
} else {
root = (large === sweep ? -1 : 1) * Math.sqrt(pl / (rx2 * py2 + ry2 * px2));
}
var cx = root * rx * py / ry, cy = -root * ry * px / rx, cx1 = cosTh * cx - sinTh * cy + toX * .5, cy1 = sinTh * cx + cosTh * cy + toY * .5, mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry), dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
if (sweep === 0 && dtheta > 0) {
dtheta -= 2 * PI;
} else if (sweep === 1 && dtheta < 0) {
dtheta += 2 * PI;
}
var segments = Math.ceil(Math.abs(dtheta / PI * 2)), result = [], mDelta = dtheta / segments, mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2), th3 = mTheta + mDelta;
for (var i = 0; i < segments; i++) {
result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
fromX = result[i][4];
fromY = result[i][5];
mTheta = th3;
th3 += mDelta;
}
arcToSegmentsCache[argsString] = result;
return result;
}
function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
var argsString2 = _join.call(arguments);
if (segmentToBezierCache[argsString2]) {
return segmentToBezierCache[argsString2];
}
var costh2 = Math.cos(th2), sinth2 = Math.sin(th2), costh3 = Math.cos(th3), sinth3 = Math.sin(th3), toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1, toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1, cp1X = fromX + mT * (-cosTh * rx * sinth2 - sinTh * ry * costh2), cp1Y = fromY + mT * (-sinTh * rx * sinth2 + cosTh * ry * costh2), cp2X = toX + mT * (cosTh * rx * sinth3 + sinTh * ry * costh3), cp2Y = toY + mT * (sinTh * rx * sinth3 - cosTh * ry * costh3);
segmentToBezierCache[argsString2] = [ cp1X, cp1Y, cp2X, cp2Y, toX, toY ];
return segmentToBezierCache[argsString2];
}
function calcVectorAngle(ux, uy, vx, vy) {
var ta = Math.atan2(uy, ux), tb = Math.atan2(vy, vx);
if (tb >= ta) {
return tb - ta;
} else {
return 2 * Math.PI - (ta - tb);
}
}
fabric.util.drawArc = function(ctx, fx, fy, coords) {
var rx = coords[0], ry = coords[1], rot = coords[2], large = coords[3], sweep = coords[4], tx = coords[5], ty = coords[6], segs = [ [], [], [], [] ], segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
for (var i = 0, len = segsNorm.length; i < len; i++) {
segs[i][0] = segsNorm[i][0] + fx;
segs[i][1] = segsNorm[i][1] + fy;
segs[i][2] = segsNorm[i][2] + fx;
segs[i][3] = segsNorm[i][3] + fy;
segs[i][4] = segsNorm[i][4] + fx;
segs[i][5] = segsNorm[i][5] + fy;
ctx.bezierCurveTo.apply(ctx, segs[i]);
}
};
fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
var fromX = 0, fromY = 0, bound = [], bounds = [], segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot), boundCopy = [ [], [] ];
for (var i = 0, len = segs.length; i < len; i++) {
bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
boundCopy[0].x = bound[0].x + fx;
boundCopy[0].y = bound[0].y + fy;
boundCopy[1].x = bound[1].x + fx;
boundCopy[1].y = bound[1].y + fy;
bounds.push(boundCopy[0]);
bounds.push(boundCopy[1]);
fromX = segs[i][4];
fromY = segs[i][5];
}
return bounds;
};
function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
var argsString = _join.call(arguments);
if (boundsOfCurveCache[argsString]) {
return boundsOfCurveCache[argsString];
}
var sqrt = Math.sqrt, min = Math.min, max = Math.max, abs = Math.abs, tvalues = [], bounds = [ [], [] ], a, b, c, t, t1, t2, b2ac, sqrtb2ac;
b = 6 * x0 - 12 * x1 + 6 * x2;
a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
c = 3 * x1 - 3 * x0;
for (var i = 0; i < 2; ++i) {
if (i > 0) {
b = 6 * y0 - 12 * y1 + 6 * y2;
a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
c = 3 * y1 - 3 * y0;
}
if (abs(a) < 1e-12) {
if (abs(b) < 1e-12) {
continue;
}
t = -c / b;
if (0 < t && t < 1) {
tvalues.push(t);
}
continue;
}
b2ac = b * b - 4 * c * a;
if (b2ac < 0) {
continue;
}
sqrtb2ac = sqrt(b2ac);
t1 = (-b + sqrtb2ac) / (2 * a);
if (0 < t1 && t1 < 1) {
tvalues.push(t1);
}
t2 = (-b - sqrtb2ac) / (2 * a);
if (0 < t2 && t2 < 1) {
tvalues.push(t2);
}
}
var x, y, j = tvalues.length, jlen = j, mt;
while (j--) {
t = tvalues[j];
mt = 1 - t;
x = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
bounds[0][j] = x;
y = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
bounds[1][j] = y;
}
bounds[0][jlen] = x0;
bounds[1][jlen] = y0;
bounds[0][jlen + 1] = x3;
bounds[1][jlen + 1] = y3;
var result = [ {
x: min.apply(null, bounds[0]),
y: min.apply(null, bounds[1])
}, {
x: max.apply(null, bounds[0]),
y: max.apply(null, bounds[1])
} ];
boundsOfCurveCache[argsString] = result;
return result;
}
fabric.util.getBoundsOfCurve = getBoundsOfCurve;
})();
(function() {
var slice = Array.prototype.slice;
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement) {
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this), len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n !== n) {
n = 0;
} else if (n !== 0 && n !== Number.POSITIVE_INFINITY && n !== Number.NEGATIVE_INFINITY) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (;k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
fn.call(context, this[i], i, this);
}
}
};
}
if (!Array.prototype.map) {
Array.prototype.map = function(fn, context) {
var result = [];
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
result[i] = fn.call(context, this[i], i, this);
}
}
return result;
};
}
if (!Array.prototype.every) {
Array.prototype.every = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && !fn.call(context, this[i], i, this)) {
return false;
}
}
return true;
};
}
if (!Array.prototype.some) {
Array.prototype.some = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && fn.call(context, this[i], i, this)) {
return true;
}
}
return false;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function(fn, context) {
var result = [], val;
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
val = this[i];
if (fn.call(context, val, i, this)) {
result.push(val);
}
}
}
return result;
};
}
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(fn) {
var len = this.length >>> 0, i = 0, rv;
if (arguments.length > 1) {
rv = arguments[1];
} else {
do {
if (i in this) {
rv = this[i++];
break;
}
if (++i >= len) {
throw new TypeError();
}
} while (true);
}
for (;i < len; i++) {
if (i in this) {
rv = fn.call(null, rv, this[i], i, this);
}
}
return rv;
};
}
function invoke(array, method) {
var args = slice.call(arguments, 2), result = [];
for (var i = 0, len = array.length; i < len; i++) {
result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
}
return result;
}
function max(array, byProperty) {
return find(array, byProperty, function(value1, value2) {
return value1 >= value2;
});
}
function min(array, byProperty) {
return find(array, byProperty, function(value1, value2) {
return value1 < value2;
});
}
function find(array, byProperty, condition) {
if (!array || array.length === 0) {
return;
}
var i = array.length - 1, result = byProperty ? array[i][byProperty] : array[i];
if (byProperty) {
while (i--) {
if (condition(array[i][byProperty], result)) {
result = array[i][byProperty];
}
}
} else {
while (i--) {
if (condition(array[i], result)) {
result = array[i];
}
}
}
return result;
}
fabric.util.array = {
invoke: invoke,
min: min,
max: max
};
})();
(function() {
function extend(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
function clone(object) {
return extend({}, object);
}
fabric.util.object = {
extend: extend,
clone: clone
};
})();
(function() {
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, "");
};
}
function camelize(string) {
return string.replace(/-+(.)?/g, function(match, character) {
return character ? character.toUpperCase() : "";
});
}
function capitalize(string, firstLetterOnly) {
return string.charAt(0).toUpperCase() + (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
}
function escapeXml(string) {
return string.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(//g, ">");
}
fabric.util.string = {
camelize: camelize,
capitalize: capitalize,
escapeXml: escapeXml
};
})();
(function() {
var slice = Array.prototype.slice, apply = Function.prototype.apply, Dummy = function() {};
if (!Function.prototype.bind) {
Function.prototype.bind = function(thisArg) {
var _this = this, args = slice.call(arguments, 1), bound;
if (args.length) {
bound = function() {
return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
};
} else {
bound = function() {
return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
};
}
Dummy.prototype = this.prototype;
bound.prototype = new Dummy();
return bound;
};
}
})();
(function() {
var slice = Array.prototype.slice, emptyFunction = function() {}, IS_DONTENUM_BUGGY = function() {
for (var p in {
toString: 1
}) {
if (p === "toString") {
return false;
}
}
return true;
}(), 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) {
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;
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;
}
if (source.valueOf !== Object.prototype.valueOf) {
klass.prototype.valueOf = source.valueOf;
}
}
}
};
function Subclass() {}
function callSuper(methodName) {
var fn = this.constructor.superclass.prototype[methodName];
return arguments.length > 1 ? fn.apply(this, slice.call(arguments, 1)) : fn.call(this);
}
function createClass() {
var parent = null, properties = slice.call(arguments, 0);
if (typeof properties[0] === "function") {
parent = properties.shift();
}
function klass() {
this.initialize.apply(this, arguments);
}
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
Subclass.prototype = parent.prototype;
klass.prototype = new Subclass();
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++) {
addMethods(klass, properties[i], parent);
}
if (!klass.prototype.initialize) {
klass.prototype.initialize = emptyFunction;
}
klass.prototype.constructor = klass;
klass.prototype.callSuper = callSuper;
return klass;
}
fabric.util.createClass = createClass;
})();
(function() {
var unknown = "unknown";
function areHostMethods(object) {
var methodNames = Array.prototype.slice.call(arguments, 1), t, i, len = methodNames.length;
for (i = 0; i < len; i++) {
t = typeof object[methodNames[i]];
if (!/^(?:function|object|unknown)$/.test(t)) {
return false;
}
}
return true;
}
var getElement, setElement, getUniqueId = function() {
var uid = 0;
return function(element) {
return element.__uniqueID || (element.__uniqueID = "uniqueID__" + uid++);
};
}();
(function() {
var elements = {};
getElement = function(uid) {
return elements[uid];
};
setElement = function(uid, element) {
elements[uid] = element;
};
})();
function createListener(uid, handler) {
return {
handler: handler,
wrappedHandler: createWrappedHandler(uid, handler)
};
}
function createWrappedHandler(uid, handler) {
return function(e) {
handler.call(getElement(uid), e || fabric.window.event);
};
}
function createDispatcher(uid, eventName) {
return function(e) {
if (handlers[uid] && handlers[uid][eventName]) {
var handlersForEvent = handlers[uid][eventName];
for (var i = 0, len = handlersForEvent.length; i < len; i++) {
handlersForEvent[i].call(this, e || fabric.window.event);
}
}
};
}
var shouldUseAddListenerRemoveListener = areHostMethods(fabric.document.documentElement, "addEventListener", "removeEventListener") && areHostMethods(fabric.window, "addEventListener", "removeEventListener"), shouldUseAttachEventDetachEvent = areHostMethods(fabric.document.documentElement, "attachEvent", "detachEvent") && areHostMethods(fabric.window, "attachEvent", "detachEvent"), listeners = {}, handlers = {}, addListener, removeListener;
if (shouldUseAddListenerRemoveListener) {
addListener = function(element, eventName, handler) {
element.addEventListener(eventName, handler, false);
};
removeListener = function(element, eventName, handler) {
element.removeEventListener(eventName, handler, false);
};
} else if (shouldUseAttachEventDetachEvent) {
addListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
setElement(uid, element);
if (!listeners[uid]) {
listeners[uid] = {};
}
if (!listeners[uid][eventName]) {
listeners[uid][eventName] = [];
}
var listener = createListener(uid, handler);
listeners[uid][eventName].push(listener);
element.attachEvent("on" + eventName, listener.wrappedHandler);
};
removeListener = function(element, eventName, handler) {
var uid = getUniqueId(element), listener;
if (listeners[uid] && listeners[uid][eventName]) {
for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
listener = listeners[uid][eventName][i];
if (listener && listener.handler === handler) {
element.detachEvent("on" + eventName, listener.wrappedHandler);
listeners[uid][eventName][i] = null;
}
}
}
};
} else {
addListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
if (!handlers[uid]) {
handlers[uid] = {};
}
if (!handlers[uid][eventName]) {
handlers[uid][eventName] = [];
var existingHandler = element["on" + eventName];
if (existingHandler) {
handlers[uid][eventName].push(existingHandler);
}
element["on" + eventName] = createDispatcher(uid, eventName);
}
handlers[uid][eventName].push(handler);
};
removeListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
if (handlers[uid] && handlers[uid][eventName]) {
var handlersForEvent = handlers[uid][eventName];
for (var i = 0, len = handlersForEvent.length; i < len; i++) {
if (handlersForEvent[i] === handler) {
handlersForEvent.splice(i, 1);
}
}
}
};
}
fabric.util.addListener = addListener;
fabric.util.removeListener = removeListener;
function getPointer(event, upperCanvasEl) {
event || (event = fabric.window.event);
var element = event.target || (typeof event.srcElement !== unknown ? event.srcElement : null), scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
return {
x: pointerX(event) + scroll.left,
y: pointerY(event) + scroll.top
};
}
var pointerX = function(event) {
return typeof event.clientX !== unknown ? event.clientX : 0;
}, pointerY = function(event) {
return typeof event.clientY !== unknown ? event.clientY : 0;
};
function _getPointer(event, pageProp, clientProp) {
var touchProp = event.type === "touchend" ? "changedTouches" : "touches";
return event[touchProp] && event[touchProp][0] ? event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]) || event[clientProp] : event[clientProp];
}
if (fabric.isTouchSupported) {
pointerX = function(event) {
return _getPointer(event, "pageX", "clientX");
};
pointerY = function(event) {
return _getPointer(event, "pageY", "clientY");
};
}
fabric.util.getPointer = getPointer;
fabric.util.object.extend(fabric.util, fabric.Observable);
})();
(function() {
function setStyle(element, styles) {
var elementStyle = element.style;
if (!elementStyle) {
return element;
}
if (typeof styles === "string") {
element.style.cssText += ";" + styles;
return styles.indexOf("opacity") > -1 ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
}
for (var property in styles) {
if (property === "opacity") {
setOpacity(element, styles[property]);
} else {
var normalizedProperty = property === "float" || property === "cssFloat" ? typeof elementStyle.styleFloat === "undefined" ? "cssFloat" : "styleFloat" : property;
elementStyle[normalizedProperty] = styles[property];
}
}
return element;
}
var parseEl = fabric.document.createElement("div"), supportsOpacity = typeof parseEl.style.opacity === "string", supportsFilters = typeof parseEl.style.filter === "string", reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/, setOpacity = function(element) {
return element;
};
if (supportsOpacity) {
setOpacity = function(element, value) {
element.style.opacity = value;
return element;
};
} else if (supportsFilters) {
setOpacity = function(element, value) {
var es = element.style;
if (element.currentStyle && !element.currentStyle.hasLayout) {
es.zoom = 1;
}
if (reOpacity.test(es.filter)) {
value = value >= .9999 ? "" : "alpha(opacity=" + value * 100 + ")";
es.filter = es.filter.replace(reOpacity, value);
} else {
es.filter += " alpha(opacity=" + value * 100 + ")";
}
return element;
};
}
fabric.util.setStyle = setStyle;
})();
(function() {
var _slice = Array.prototype.slice;
function getById(id) {
return typeof id === "string" ? fabric.document.getElementById(id) : id;
}
var sliceCanConvertNodelists, toArray = function(arrayLike) {
return _slice.call(arrayLike, 0);
};
try {
sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
} catch (err) {}
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
var arr = new Array(arrayLike.length), i = arrayLike.length;
while (i--) {
arr[i] = arrayLike[i];
}
return arr;
};
}
function makeElement(tagName, attributes) {
var el = fabric.document.createElement(tagName);
for (var prop in attributes) {
if (prop === "class") {
el.className = attributes[prop];
} else if (prop === "for") {
el.htmlFor = attributes[prop];
} else {
el.setAttribute(prop, attributes[prop]);
}
}
return el;
}
function addClass(element, className) {
if (element && (" " + element.className + " ").indexOf(" " + className + " ") === -1) {
element.className += (element.className ? " " : "") + className;
}
}
function wrapElement(element, wrapper, attributes) {
if (typeof wrapper === "string") {
wrapper = makeElement(wrapper, attributes);
}
if (element.parentNode) {
element.parentNode.replaceChild(wrapper, element);
}
wrapper.appendChild(element);
return wrapper;
}
function getScrollLeftTop(element, upperCanvasEl) {
var firstFixedAncestor, origElement, left = 0, top = 0, docElement = fabric.document.documentElement, body = fabric.document.body || {
scrollLeft: 0,
scrollTop: 0
};
origElement = element;
while (element && element.parentNode && !firstFixedAncestor) {
element = element.parentNode;
if (element.nodeType === 1 && fabric.util.getElementStyle(element, "position") === "fixed") {
firstFixedAncestor = element;
}
if (element.nodeType === 1 && origElement !== upperCanvasEl && fabric.util.getElementStyle(element, "position") === "absolute") {
left = 0;
top = 0;
} else if (element === fabric.document) {
left = body.scrollLeft || docElement.scrollLeft || 0;
top = body.scrollTop || docElement.scrollTop || 0;
} else {
left += element.scrollLeft || 0;
top += element.scrollTop || 0;
}
}
return {
left: left,
top: top
};
}
function getElementOffset(element) {
var docElem, doc = element && element.ownerDocument, box = {
left: 0,
top: 0
}, offset = {
left: 0,
top: 0
}, scrollLeftTop, offsetAttributes = {
borderLeftWidth: "left",
borderTopWidth: "top",
paddingLeft: "left",
paddingTop: "top"
};
if (!doc) {
return {
left: 0,
top: 0
};
}
for (var attr in offsetAttributes) {
offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
}
docElem = doc.documentElement;
if (typeof element.getBoundingClientRect !== "undefined") {
box = element.getBoundingClientRect();
}
scrollLeftTop = fabric.util.getScrollLeftTop(element, null);
return {
left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
};
}
var getElementStyle;
if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
getElementStyle = function(element, attr) {
var style = fabric.document.defaultView.getComputedStyle(element, null);
return style ? style[attr] : undefined;
};
} else {
getElementStyle = function(element, attr) {
var value = element.style[attr];
if (!value && element.currentStyle) {
value = element.currentStyle[attr];
}
return value;
};
}
(function() {
var style = fabric.document.documentElement.style, selectProp = "userSelect" in style ? "userSelect" : "MozUserSelect" in style ? "MozUserSelect" : "WebkitUserSelect" in style ? "WebkitUserSelect" : "KhtmlUserSelect" in style ? "KhtmlUserSelect" : "";
function makeElementUnselectable(element) {
if (typeof element.onselectstart !== "undefined") {
element.onselectstart = fabric.util.falseFunction;
}
if (selectProp) {
element.style[selectProp] = "none";
} else if (typeof element.unselectable === "string") {
element.unselectable = "on";
}
return element;
}
function makeElementSelectable(element) {
if (typeof element.onselectstart !== "undefined") {
element.onselectstart = null;
}
if (selectProp) {
element.style[selectProp] = "";
} else if (typeof element.unselectable === "string") {
element.unselectable = "";
}
return element;
}
fabric.util.makeElementUnselectable = makeElementUnselectable;
fabric.util.makeElementSelectable = makeElementSelectable;
})();
(function() {
function getScript(url, callback) {
var headEl = fabric.document.getElementsByTagName("head")[0], scriptEl = fabric.document.createElement("script"), loading = true;
scriptEl.onload = scriptEl.onreadystatechange = function(e) {
if (loading) {
if (typeof this.readyState === "string" && this.readyState !== "loaded" && this.readyState !== "complete") {
return;
}
loading = false;
callback(e || fabric.window.event);
scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
}
};
scriptEl.src = url;
headEl.appendChild(scriptEl);
}
fabric.util.getScript = getScript;
})();
fabric.util.getById = getById;
fabric.util.toArray = toArray;
fabric.util.makeElement = makeElement;
fabric.util.addClass = addClass;
fabric.util.wrapElement = wrapElement;
fabric.util.getScrollLeftTop = getScrollLeftTop;
fabric.util.getElementOffset = getElementOffset;
fabric.util.getElementStyle = getElementStyle;
})();
(function() {
function addParamToUrl(url, param) {
return url + (/\?/.test(url) ? "&" : "?") + param;
}
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 XMLHttpRequest();
} ];
for (var i = factories.length; i--; ) {
try {
var req = factories[i]();
if (req) {
return factories[i];
}
} catch (err) {}
}
}();
function emptyFn() {}
function request(url, options) {
options || (options = {});
var method = options.method ? options.method.toUpperCase() : "GET", onComplete = options.onComplete || function() {}, xhr = makeXHR(), body;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
onComplete(xhr);
xhr.onreadystatechange = emptyFn;
}
};
if (method === "GET") {
body = null;
if (typeof options.parameters === "string") {
url = addParamToUrl(url, options.parameters);
}
}
xhr.open(method, url, true);
if (method === "POST" || method === "PUT") {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
xhr.send(body);
return xhr;
}
fabric.util.request = request;
})();
fabric.log = function() {};
fabric.warn = function() {};
if (typeof console !== "undefined") {
[ "log", "warn" ].forEach(function(methodName) {
if (typeof console[methodName] !== "undefined" && typeof console[methodName].apply === "function") {
fabric[methodName] = function() {
return console[methodName].apply(console, arguments);
};
}
});
}
(function() {
function animate(options) {
requestAnimFrame(function(timestamp) {
options || (options = {});
var start = timestamp || +new Date(), duration = options.duration || 500, 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;
}, startValue = "startValue" in options ? options.startValue : 0, endValue = "endValue" in options ? options.endValue : 100, byValue = options.byValue || endValue - startValue;
options.onStart && options.onStart();
(function tick(ticktime) {
time = ticktime || +new Date();
var currentTime = time > finish ? duration : time - start;
if (abort()) {
options.onComplete && options.onComplete();
return;
}
onChange(easing(currentTime, startValue, byValue, duration));
if (time > finish) {
options.onComplete && options.onComplete();
return;
}
requestAnimFrame(tick);
})(start);
});
}
var _requestAnimFrame = fabric.window.requestAnimationFrame || fabric.window.webkitRequestAnimationFrame || fabric.window.mozRequestAnimationFrame || fabric.window.oRequestAnimationFrame || fabric.window.msRequestAnimationFrame || function(callback) {
fabric.window.setTimeout(callback, 1e3 / 60);
};
function requestAnimFrame() {
return _requestAnimFrame.apply(fabric.window, arguments);
}
fabric.util.animate = animate;
fabric.util.requestAnimFrame = requestAnimFrame;
})();
(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);
}
return {
a: a,
c: c,
p: p,
s: s
};
}
function elastic(opts, t, d) {
return opts.a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p);
}
function easeOutCubic(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
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;
}
function easeInQuart(t, b, c, d) {
return c * (t /= d) * t * t * t + b;
}
function easeOutQuart(t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
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;
}
function easeInQuint(t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
}
function easeOutQuint(t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
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;
}
function easeInSine(t, b, c, d) {
return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
}
function easeOutSine(t, b, c, d) {
return c * Math.sin(t / d * (Math.PI / 2)) + b;
}
function easeInOutSine(t, b, c, d) {
return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
function easeInExpo(t, b, c, d) {
return t === 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
}
function easeOutExpo(t, b, c, d) {
return t === d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
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;
}
function easeInCirc(t, b, c, d) {
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
}
function easeOutCirc(t, b, c, d) {
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
}
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;
}
function easeInElastic(t, b, c, d) {
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 * .3;
}
var opts = normalize(a, c, p, s);
return -elastic(opts, t, d) + b;
}
function easeOutElastic(t, b, c, d) {
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 * .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;
}
function easeInOutElastic(t, b, c, d) {
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 * (.3 * 1.5);
}
var opts = normalize(a, c, p, s);
if (t < 1) {
return -.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) * .5 + opts.c + b;
}
function easeInBack(t, b, c, d, s) {
if (s === undefined) {
s = 1.70158;
}
return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
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;
}
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;
}
function easeInBounce(t, b, c, d) {
return c - easeOutBounce(d - t, 0, c, d) + b;
}
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 + .75) + b;
} else if (t < 2.5 / 2.75) {
return c * (7.5625 * (t -= 2.25 / 2.75) * t + .9375) + b;
} else {
return c * (7.5625 * (t -= 2.625 / 2.75) * t + .984375) + b;
}
}
function easeInOutBounce(t, b, c, d) {
if (t < d / 2) {
return easeInBounce(t * 2, 0, c, d) * .5 + b;
}
return easeOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
}
fabric.util.ease = {
easeInQuad: function(t, b, c, d) {
return c * (t /= d) * t + b;
},
easeOutQuad: function(t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
},
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;
},
easeInCubic: function(t, b, c, d) {
return c * (t /= d) * t * t + b;
},
easeOutCubic: easeOutCubic,
easeInOutCubic: easeInOutCubic,
easeInQuart: easeInQuart,
easeOutQuart: easeOutQuart,
easeInOutQuart: easeInOutQuart,
easeInQuint: easeInQuint,
easeOutQuint: easeOutQuint,
easeInOutQuint: easeInOutQuint,
easeInSine: easeInSine,
easeOutSine: easeOutSine,
easeInOutSine: easeInOutSine,
easeInExpo: easeInExpo,
easeOutExpo: easeOutExpo,
easeInOutExpo: easeInOutExpo,
easeInCirc: easeInCirc,
easeOutCirc: easeOutCirc,
easeInOutCirc: easeInOutCirc,
easeInElastic: easeInElastic,
easeOutElastic: easeOutElastic,
easeInOutElastic: easeInOutElastic,
easeInBack: easeInBack,
easeOutBack: easeOutBack,
easeInOutBack: easeInOutBack,
easeInBounce: easeInBounce,
easeOutBounce: easeOutBounce,
easeInOutBounce: easeInOutBounce
};
})();
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend, capitalize = fabric.util.string.capitalize, clone = fabric.util.object.clone, toFixed = fabric.util.toFixed, parseUnit = fabric.util.parseUnit, multiplyTransformMatrices = fabric.util.multiplyTransformMatrices, 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"
}, colorAttributes = {
stroke: "strokeOpacity",
fill: "fillOpacity"
};
fabric.cssRules = {};
fabric.gradientDefs = {};
function normalizeAttr(attr) {
if (attr in attributesMap) {
return attributesMap[attr];
}
return attr;
}
function normalizeValue(attr, value, parentAttributes, fontSize) {
var isArray = Object.prototype.toString.call(value) === "[object Array]", parsed;
if ((attr === "fill" || attr === "stroke") && value === "none") {
value = "";
} else if (attr === "strokeDashArray") {
value = value.replace(/,/g, " ").split(/\s+/).map(function(n) {
return parseFloat(n);
});
} else if (attr === "transformMatrix") {
if (parentAttributes && parentAttributes.transformMatrix) {
value = multiplyTransformMatrices(parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
} else {
value = fabric.parseTransformAttribute(value);
}
} else if (attr === "visible") {
value = value === "none" || value === "hidden" ? false : true;
if (parentAttributes && parentAttributes.visible === false) {
value = false;
}
} else if (attr === "originX") {
value = value === "start" ? "left" : value === "end" ? "right" : "center";
} else {
parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
}
return !isArray && isNaN(parsed) ? value : parsed;
}
function _setStrokeFillOpacity(attributes) {
for (var attr in colorAttributes) {
if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === "undefined") {
continue;
}
if (attributes[attr].indexOf("url(") === 0) {
continue;
}
var color = new fabric.Color(attributes[attr]);
attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
}
return attributes;
}
fabric.parseTransformAttribute = function() {
function rotateMatrix(matrix, args) {
var angle = args[0];
matrix[0] = Math.cos(angle);
matrix[1] = Math.sin(angle);
matrix[2] = -Math.sin(angle);
matrix[3] = Math.cos(angle);
}
function scaleMatrix(matrix, args) {
var multiplierX = args[0], multiplierY = args.length === 2 ? args[1] : args[0];
matrix[0] = multiplierX;
matrix[3] = multiplierY;
}
function skewXMatrix(matrix, args) {
matrix[2] = Math.tan(fabric.util.degreesToRadians(args[0]));
}
function skewYMatrix(matrix, args) {
matrix[1] = Math.tan(fabric.util.degreesToRadians(args[0]));
}
function translateMatrix(matrix, args) {
matrix[4] = args[0];
if (args.length === 2) {
matrix[5] = args[1];
}
}
var iMatrix = [ 1, 0, 0, 1, 0, 0 ], number = fabric.reNum, commaWsp = "(?:\\s+,?\\s*|,\\s*)", skewX = "(?:(skewX)\\s*\\(\\s*(" + number + ")\\s*\\))", skewY = "(?:(skewY)\\s*\\(\\s*(" + number + ")\\s*\\))", rotate = "(?:(rotate)\\s*\\(\\s*(" + number + ")(?:" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + "))?\\s*\\))", scale = "(?:(scale)\\s*\\(\\s*(" + number + ")(?:" + commaWsp + "(" + number + "))?\\s*\\))", translate = "(?:(translate)\\s*\\(\\s*(" + number + ")(?:" + commaWsp + "(" + number + "))?\\s*\\))", matrix = "(?:(matrix)\\s*\\(\\s*" + "(" + number + ")" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + ")" + "\\s*\\))", transform = "(?:" + matrix + "|" + translate + "|" + scale + "|" + rotate + "|" + skewX + "|" + skewY + ")", transforms = "(?:" + transform + "(?:" + commaWsp + transform + ")*" + ")", transformList = "^\\s*(?:" + transforms + "?)\\s*$", reTransformList = new RegExp(transformList), reTransform = new RegExp(transform, "g");
return function(attributeValue) {
var matrix = iMatrix.concat(), matrices = [];
if (!attributeValue || attributeValue && !reTransformList.test(attributeValue)) {
return matrix;
}
attributeValue.replace(reTransform, function(match) {
var m = new RegExp(transform).exec(match).filter(function(match) {
return match !== "" && match != null;
}), operation = m[1], args = m.slice(2).map(parseFloat);
switch (operation) {
case "translate":
translateMatrix(matrix, args);
break;
case "rotate":
args[0] = fabric.util.degreesToRadians(args[0]);
rotateMatrix(matrix, args);
break;
case "scale":
scaleMatrix(matrix, args);
break;
case "skewX":
skewXMatrix(matrix, args);
break;
case "skewY":
skewYMatrix(matrix, args);
break;
case "matrix":
matrix = args;
break;
}
matrices.push(matrix.concat());
matrix = iMatrix.concat();
});
var combinedMatrix = matrices[0];
while (matrices.length > 1) {
matrices.shift();
combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
}
return combinedMatrix;
};
}();
function parseStyleString(style, oStyle) {
var attr, value;
style.replace(/;$/, "").split(";").forEach(function(chunk) {
var pair = chunk.split(":");
attr = normalizeAttr(pair[0].trim().toLowerCase());
value = normalizeValue(attr, pair[1].trim());
oStyle[attr] = value;
});
}
function parseStyleObject(style, oStyle) {
var attr, value;
for (var prop in style) {
if (typeof style[prop] === "undefined") {
continue;
}
attr = normalizeAttr(prop.toLowerCase());
value = normalizeValue(attr, style[prop]);
oStyle[attr] = value;
}
}
function getGlobalStylesForElement(element, svgUid) {
var styles = {};
for (var rule in fabric.cssRules[svgUid]) {
if (elementMatchesRule(element, rule.split(" "))) {
for (var property in fabric.cssRules[svgUid][rule]) {
styles[property] = fabric.cssRules[svgUid][rule][property];
}
}
}
return styles;
}
function elementMatchesRule(element, selectors) {
var firstMatching, parentMatching = true;
firstMatching = selectorMatches(element, selectors.pop());
if (firstMatching && selectors.length) {
parentMatching = doesSomeParentMatch(element, selectors);
}
return firstMatching && parentMatching && selectors.length === 0;
}
function doesSomeParentMatch(element, selectors) {
var selector, parentMatching = true;
while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
if (parentMatching) {
selector = selectors.pop();
}
element = element.parentNode;
parentMatching = selectorMatches(element, selector);
}
return selectors.length === 0;
}
function selectorMatches(element, selector) {
var nodeName = element.nodeName, classNames = element.getAttribute("class"), id = element.getAttribute("id"), matcher;
matcher = new RegExp("^" + nodeName, "i");
selector = selector.replace(matcher, "");
if (id && selector.length) {
matcher = new RegExp("#" + id + "(?![a-zA-Z\\-]+)", "i");
selector = selector.replace(matcher, "");
}
if (classNames && selector.length) {
classNames = classNames.split(" ");
for (var i = classNames.length; i--; ) {
matcher = new RegExp("\\." + classNames[i] + "(?![a-zA-Z\\-]+)", "i");
selector = selector.replace(matcher, "");
}
}
return selector.length === 0;
}
function elementById(doc, id) {
var el;
doc.getElementById && (el = doc.getElementById(id));
if (el) {
return el;
}
var node, i, idAttr, nodelist = doc.getElementsByTagName("*");
for (i = 0; i < nodelist.length; i++) {
node = nodelist[i];
if (idAttr === node.getAttribute("id")) {
return node;
}
}
}
function parseUseDirectives(doc) {
var nodelist = doc.getElementsByTagName("use"), i = 0;
while (nodelist.length && i < nodelist.length) {
var el = nodelist[i], xlink = el.getAttribute("xlink:href").substr(1), x = el.getAttribute("x") || 0, y = el.getAttribute("y") || 0, el2 = elementById(doc, xlink).cloneNode(true), currentTrans = (el2.getAttribute("transform") || "") + " translate(" + x + ", " + y + ")", parentNode, oldLength = nodelist.length;
for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
var attr = attrs.item(j);
if (attr.nodeName === "x" || attr.nodeName === "y" || attr.nodeName === "xlink:href") {
continue;
}
if (attr.nodeName === "transform") {
currentTrans = attr.nodeValue + " " + currentTrans;
} else {
el2.setAttribute(attr.nodeName, attr.nodeValue);
}
}
el2.setAttribute("transform", currentTrans);
el2.setAttribute("instantiated_by_use", "1");
el2.removeAttribute("id");
parentNode = el.parentNode;
parentNode.replaceChild(el2, el);
if (nodelist.length === oldLength) {
i++;
}
}
}
var reViewBoxAttrValue = new RegExp("^" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*" + "$");
function addVBTransform(element, widthAttr, heightAttr) {
var viewBoxAttr = element.getAttribute("viewBox"), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el;
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
minX = -parseFloat(viewBoxAttr[1]), minY = -parseFloat(viewBoxAttr[2]), viewBoxWidth = parseFloat(viewBoxAttr[3]),
viewBoxHeight = parseFloat(viewBoxAttr[4]);
} else {
return;
}
if (widthAttr && widthAttr !== viewBoxWidth) {
scaleX = widthAttr / viewBoxWidth;
}
if (heightAttr && heightAttr !== viewBoxHeight) {
scaleY = heightAttr / viewBoxHeight;
}
scaleY = scaleX = scaleX > scaleY ? scaleY : scaleX;
if (!(scaleX !== 1 || scaleY !== 1 || minX !== 0 || minY !== 0)) {
return;
}
matrix = " matrix(" + scaleX + " 0" + " 0 " + scaleY + " " + minX * scaleX + " " + minY * scaleY + ") ";
if (element.tagName === "svg") {
el = element.ownerDocument.createElement("g");
while (element.firstChild != null) {
el.appendChild(element.firstChild);
}
element.appendChild(el);
} else {
el = element;
matrix = el.getAttribute("transform") + matrix;
}
el.setAttribute("transform", matrix);
}
fabric.parseSVGDocument = function() {
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/, reViewBoxTagNames = /^(symbol|image|marker|pattern|view)$/;
function hasAncestorWithNodeName(element, nodeName) {
while (element && (element = element.parentNode)) {
if (nodeName.test(element.nodeName) && !element.getAttribute("instantiated_by_use")) {
return true;
}
}
return false;
}
return function(doc, callback, reviver) {
if (!doc) {
return;
}
parseUseDirectives(doc);
var startTime = new Date(), svgUid = fabric.Object.__uid++, widthAttr, heightAttr, toBeParsed = false;
if (doc.getAttribute("width") && doc.getAttribute("width") !== "100%") {
widthAttr = parseUnit(doc.getAttribute("width"));
}
if (doc.getAttribute("height") && doc.getAttribute("height") !== "100%") {
heightAttr = parseUnit(doc.getAttribute("height"));
}
if (!widthAttr || !heightAttr) {
var viewBoxAttr = doc.getAttribute("viewBox");
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
widthAttr = parseFloat(viewBoxAttr[3]), heightAttr = parseFloat(viewBoxAttr[4]);
} else {
toBeParsed = true;
}
}
addVBTransform(doc, widthAttr, heightAttr);
var descendants = fabric.util.toArray(doc.getElementsByTagName("*"));
if (descendants.length === 0 && fabric.isLikelyNode) {
descendants = doc.selectNodes('//*[name(.)!="svg"]');
var arr = [];
for (var i = 0, len = descendants.length; i < len; i++) {
arr[i] = descendants[i];
}
descendants = arr;
}
var elements = descendants.filter(function(el) {
reViewBoxTagNames.test(el.tagName) && addVBTransform(el, 0, 0);
return reAllowedSVGTagNames.test(el.tagName) && !hasAncestorWithNodeName(el, /^(?:pattern|defs|symbol|metadata)$/);
});
if (!elements || elements && !elements.length) {
callback && callback([], {});
return;
}
var options = {
width: widthAttr,
height: heightAttr,
svgUid: svgUid,
toBeParsed: toBeParsed
};
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
fabric.parseElements(elements, function(instances) {
fabric.documentParsingTime = new Date() - startTime;
if (callback) {
callback(instances, options);
}
}, clone(options), reviver);
};
}();
var svgCache = {
has: function(name, callback) {
callback(false);
},
get: function() {},
set: function() {}
};
function _enlivenCachedObject(cachedObject) {
var objects = cachedObject.objects, options = cachedObject.options;
objects = objects.map(function(o) {
return fabric[capitalize(o.type)].fromObject(o);
});
return {
objects: objects,
options: options
};
}
function _createSVGPattern(markup, canvas, property) {
if (canvas[property] && canvas[property].toSVG) {
markup.push('', '');
}
}
var reFontDeclaration = new RegExp("(normal|italic)?\\s*(normal|small-caps)?\\s*" + "(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(" + fabric.reNum + "(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|" + fabric.reNum + "))?\\s+(.*)");
extend(fabric, {
parseFontDeclaration: function(value, oStyle) {
var match = value.match(reFontDeclaration);
if (!match) {
return;
}
var fontStyle = match[1], fontWeight = match[3], fontSize = match[4], lineHeight = match[5], fontFamily = match[6];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseUnit(fontSize);
}
if (fontFamily) {
oStyle.fontFamily = fontFamily;
}
if (lineHeight) {
oStyle.lineHeight = lineHeight === "normal" ? 1 : lineHeight;
}
},
getGradientDefs: function(doc) {
var linearGradientEls = doc.getElementsByTagName("linearGradient"), radialGradientEls = doc.getElementsByTagName("radialGradient"), el, i, j = 0, id, xlink, elList = [], gradientDefs = {}, idsToXlinkMap = {};
elList.length = linearGradientEls.length + radialGradientEls.length;
i = linearGradientEls.length;
while (i--) {
elList[j++] = linearGradientEls[i];
}
i = radialGradientEls.length;
while (i--) {
elList[j++] = radialGradientEls[i];
}
while (j--) {
el = elList[j];
xlink = el.getAttribute("xlink:href");
id = el.getAttribute("id");
if (xlink) {
idsToXlinkMap[id] = xlink.substr(1);
}
gradientDefs[id] = el;
}
for (id in idsToXlinkMap) {
var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
el = gradientDefs[id];
while (el2.firstChild) {
el.appendChild(el2.firstChild);
}
}
return gradientDefs;
},
parseAttributes: function(element, attributes, svgUid) {
if (!element) {
return;
}
var value, parentAttributes = {}, fontSize;
if (typeof svgUid === "undefined") {
svgUid = element.getAttribute("svgUid");
}
if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
}
fontSize = parentAttributes && parentAttributes.fontSize || element.getAttribute("font-size") || fabric.Text.DEFAULT_SVG_FONT_SIZE;
var ownAttributes = attributes.reduce(function(memo, attr) {
value = element.getAttribute(attr);
if (value) {
attr = normalizeAttr(attr);
value = normalizeValue(attr, value, parentAttributes, fontSize);
memo[attr] = value;
}
return memo;
}, {});
ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
if (ownAttributes.font) {
fabric.parseFontDeclaration(ownAttributes.font, ownAttributes);
}
return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
},
parseElements: function(elements, callback, options, reviver) {
new fabric.ElementsParser(elements, callback, options, reviver).parse();
},
parseStyleAttribute: function(element) {
var oStyle = {}, style = element.getAttribute("style");
if (!style) {
return oStyle;
}
if (typeof style === "string") {
parseStyleString(style, oStyle);
} else {
parseStyleObject(style, oStyle);
}
return oStyle;
},
parsePointsAttribute: function(points) {
if (!points) {
return null;
}
points = points.replace(/,/g, " ").trim();
points = points.split(/\s+/);
var parsedPoints = [], i, len;
i = 0;
len = points.length;
for (;i < len; i += 2) {
parsedPoints.push({
x: parseFloat(points[i]),
y: parseFloat(points[i + 1])
});
}
return parsedPoints;
},
getCSSRules: function(doc) {
var styles = doc.getElementsByTagName("style"), allRules = {}, rules;
for (var i = 0, len = styles.length; i < len; i++) {
var styleContents = styles[i].textContent;
styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, "");
if (styleContents.trim() === "") {
continue;
}
rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
rules = rules.map(function(rule) {
return rule.trim();
});
rules.forEach(function(rule) {
var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/), ruleObj = {}, declaration = match[2].trim(), propertyValuePairs = declaration.replace(/;$/, "").split(/\s*;\s*/);
for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
var pair = propertyValuePairs[i].split(/\s*:\s*/), property = normalizeAttr(pair[0]), value = normalizeValue(property, pair[1], pair[0]);
ruleObj[property] = value;
}
rule = match[1];
rule.split(",").forEach(function(_rule) {
_rule = _rule.replace(/^svg/i, "").trim();
if (_rule === "") {
return;
}
allRules[_rule] = fabric.util.object.clone(ruleObj);
});
});
}
return allRules;
},
loadSVGFromURL: function(url, callback, reviver) {
url = url.replace(/^\n\s*/, "").trim();
svgCache.has(url, function(hasUrl) {
if (hasUrl) {
svgCache.get(url, function(value) {
var enlivedRecord = _enlivenCachedObject(value);
callback(enlivedRecord.objects, enlivedRecord.options);
});
} else {
new fabric.util.request(url, {
method: "get",
onComplete: onComplete
});
}
});
function onComplete(r) {
var xml = r.responseXML;
if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = "false";
xml.loadXML(r.responseText.replace(//i, ""));
}
if (!xml || !xml.documentElement) {
return;
}
fabric.parseSVGDocument(xml.documentElement, function(results, options) {
svgCache.set(url, {
objects: fabric.util.array.invoke(results, "toObject"),
options: options
});
callback(results, options);
}, reviver);
}
},
loadSVGFromString: function(string, callback, reviver) {
string = string.trim();
var doc;
if (typeof DOMParser !== "undefined") {
var parser = new DOMParser();
if (parser && parser.parseFromString) {
doc = parser.parseFromString(string, "text/xml");
}
} else if (fabric.window.ActiveXObject) {
doc = new ActiveXObject("Microsoft.XMLDOM");
doc.async = "false";
doc.loadXML(string.replace(//i, ""));
}
fabric.parseSVGDocument(doc.documentElement, function(results, options) {
callback(results, options);
}, reviver);
},
createSVGFontFacesMarkup: function(objects) {
var markup = "";
for (var i = 0, len = objects.length; i < len; i++) {
if (objects[i].type !== "text" || !objects[i].path) {
continue;
}
markup += [ "@font-face {", "font-family: ", objects[i].fontFamily, "; ", "src: url('", objects[i].path, "')", "}" ].join("");
}
if (markup) {
markup = [ '" ].join("");
}
return markup;
},
createSVGRefElementsMarkup: function(canvas) {
var markup = [];
_createSVGPattern(markup, canvas, "backgroundColor");
_createSVGPattern(markup, canvas, "overlayColor");
return markup.join("");
}
});
})(typeof exports !== "undefined" ? exports : this);
fabric.ElementsParser = function(elements, callback, options, reviver) {
this.elements = elements;
this.callback = callback;
this.options = options;
this.reviver = reviver;
this.svgUid = options && options.svgUid || 0;
};
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 i = 0, len = this.elements.length; i < len; i++) {
this.elements[i].setAttribute("svgUid", this.svgUid);
(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.resolveGradient(obj, "fill");
this.resolveGradient(obj, "stroke");
this.reviver && this.reviver(el, obj);
this.instances[index] = obj;
this.checkIfDone();
}
};
fabric.ElementsParser.prototype.createCallback = function(index, el) {
var _this = this;
return function(obj) {
_this.resolveGradient(obj, "fill");
_this.resolveGradient(obj, "stroke");
_this.reviver && _this.reviver(el, obj);
_this.instances[index] = obj;
_this.checkIfDone();
};
};
fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
var instanceFillValue = obj.get(property);
if (!/^url\(/.test(instanceFillValue)) {
return;
}
var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
if (fabric.gradientDefs[this.svgUid][gradientId]) {
obj.set(property, fabric.Gradient.fromElement(fabric.gradientDefs[this.svgUid][gradientId], obj));
}
};
fabric.ElementsParser.prototype.checkIfDone = function() {
if (--this.numElements === 0) {
this.instances = this.instances.filter(function(el) {
return el != null;
});
this.callback(this.instances);
}
};
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {});
if (fabric.Point) {
fabric.warn("fabric.Point is already defined");
return;
}
fabric.Point = Point;
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
constructor: Point,
add: function(that) {
return new Point(this.x + that.x, this.y + that.y);
},
addEquals: function(that) {
this.x += that.x;
this.y += that.y;
return this;
},
scalarAdd: function(scalar) {
return new Point(this.x + scalar, this.y + scalar);
},
scalarAddEquals: function(scalar) {
this.x += scalar;
this.y += scalar;
return this;
},
subtract: function(that) {
return new Point(this.x - that.x, this.y - that.y);
},
subtractEquals: function(that) {
this.x -= that.x;
this.y -= that.y;
return this;
},
scalarSubtract: function(scalar) {
return new Point(this.x - scalar, this.y - scalar);
},
scalarSubtractEquals: function(scalar) {
this.x -= scalar;
this.y -= scalar;
return this;
},
multiply: function(scalar) {
return new Point(this.x * scalar, this.y * scalar);
},
multiplyEquals: function(scalar) {
this.x *= scalar;
this.y *= scalar;
return this;
},
divide: function(scalar) {
return new Point(this.x / scalar, this.y / scalar);
},
divideEquals: function(scalar) {
this.x /= scalar;
this.y /= scalar;
return this;
},
eq: function(that) {
return this.x === that.x && this.y === that.y;
},
lt: function(that) {
return this.x < that.x && this.y < that.y;
},
lte: function(that) {
return this.x <= that.x && this.y <= that.y;
},
gt: function(that) {
return this.x > that.x && this.y > that.y;
},
gte: function(that) {
return this.x >= that.x && this.y >= that.y;
},
lerp: function(that, t) {
return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
},
distanceFrom: function(that) {
var dx = this.x - that.x, dy = this.y - that.y;
return Math.sqrt(dx * dx + dy * dy);
},
midPointFrom: function(that) {
return new Point(this.x + (that.x - this.x) / 2, this.y + (that.y - this.y) / 2);
},
min: function(that) {
return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
},
max: function(that) {
return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
},
toString: function() {
return this.x + "," + this.y;
},
setXY: function(x, y) {
this.x = x;
this.y = y;
},
setFromPoint: function(that) {
this.x = that.x;
this.y = that.y;
},
swap: function(that) {
var x = this.x, y = this.y;
this.x = that.x;
this.y = that.y;
that.x = x;
that.y = y;
}
};
})(typeof exports !== "undefined" ? exports : this);
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {});
if (fabric.Intersection) {
fabric.warn("fabric.Intersection is already defined");
return;
}
function Intersection(status) {
this.status = status;
this.points = [];
}
fabric.Intersection = Intersection;
fabric.Intersection.prototype = {
appendPoint: function(point) {
this.points.push(point);
},
appendPoints: function(points) {
this.points = this.points.concat(points);
}
};
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;
};
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;
};
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;
};
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";
var fabric = global.fabric || (global.fabric = {});
if (fabric.Color) {
fabric.warn("fabric.Color is already defined.");
return;
}
function Color(color) {
if (!color) {
this.setSource([ 0, 0, 0, 1 ]);
} else {
this._tryParsingColor(color);
}
}
fabric.Color = Color;
fabric.Color.prototype = {
_tryParsingColor: function(color) {
var source;
if (color in Color.colorNameMap) {
color = Color.colorNameMap[color];
}
if (color === "transparent") {
this.setSource([ 255, 255, 255, 0 ]);
return;
}
source = Color.sourceFromHex(color);
if (!source) {
source = Color.sourceFromRgb(color);
}
if (!source) {
source = Color.sourceFromHsl(color);
}
if (source) {
this.setSource(source);
}
},
_rgbToHsl: function(r, g, b) {
r /= 255, g /= 255, b /= 255;
var h, s, l, max = fabric.util.array.max([ r, g, b ]), min = fabric.util.array.min([ r, g, b ]);
l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
var d = max - min;
s = l > .5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [ Math.round(h * 360), Math.round(s * 100), Math.round(l * 100) ];
},
getSource: function() {
return this._source;
},
setSource: function(source) {
this._source = source;
},
toRgb: function() {
var source = this.getSource();
return "rgb(" + source[0] + "," + source[1] + "," + source[2] + ")";
},
toRgba: function() {
var source = this.getSource();
return "rgba(" + source[0] + "," + source[1] + "," + source[2] + "," + source[3] + ")";
},
toHsl: function() {
var source = this.getSource(), hsl = this._rgbToHsl(source[0], source[1], source[2]);
return "hsl(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%)";
},
toHsla: function() {
var source = this.getSource(), hsl = this._rgbToHsl(source[0], source[1], source[2]);
return "hsla(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%," + source[3] + ")";
},
toHex: function() {
var source = this.getSource(), r, g, b;
r = source[0].toString(16);
r = r.length === 1 ? "0" + r : r;
g = source[1].toString(16);
g = g.length === 1 ? "0" + g : g;
b = source[2].toString(16);
b = b.length === 1 ? "0" + b : b;
return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
},
getAlpha: function() {
return this.getSource()[3];
},
setAlpha: function(alpha) {
var source = this.getSource();
source[3] = alpha;
this.setSource(source);
return this;
},
toGrayscale: function() {
var source = this.getSource(), average = parseInt((source[0] * .3 + source[1] * .59 + source[2] * .11).toFixed(0), 10), currentAlpha = source[3];
this.setSource([ average, average, average, currentAlpha ]);
return this;
},
toBlackWhite: function(threshold) {
var source = this.getSource(), average = (source[0] * .3 + source[1] * .59 + source[2] * .11).toFixed(0), currentAlpha = source[3];
threshold = threshold || 127;
average = Number(average) < Number(threshold) ? 0 : 255;
this.setSource([ average, average, average, currentAlpha ]);
return this;
},
overlayWith: function(otherColor) {
if (!(otherColor instanceof Color)) {
otherColor = new Color(otherColor);
}
var result = [], alpha = this.getAlpha(), otherAlpha = .5, source = this.getSource(), otherSource = otherColor.getSource();
for (var i = 0; i < 3; i++) {
result.push(Math.round(source[i] * (1 - otherAlpha) + otherSource[i] * otherAlpha));
}
result[3] = alpha;
this.setSource(result);
return this;
}
};
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*)?\)$/;
fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
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"
};
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;
}
return p;
}
fabric.Color.fromRgb = function(color) {
return Color.fromSource(Color.sourceFromRgb(color));
};
fabric.Color.sourceFromRgb = function(color) {
var match = color.match(Color.reRGBa);
if (match) {
var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1), g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1), b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
return [ parseInt(r, 10), parseInt(g, 10), parseInt(b, 10), match[4] ? parseFloat(match[4]) : 1 ];
}
};
fabric.Color.fromRgba = Color.fromRgb;
fabric.Color.fromHsl = function(color) {
return Color.fromSource(Color.sourceFromHsl(color));
};
fabric.Color.sourceFromHsl = function(color) {
var match = color.match(Color.reHSLa);
if (!match) {
return;
}
var h = (parseFloat(match[1]) % 360 + 360) % 360 / 360, s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1), l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1), r, g, b;
if (s === 0) {
r = g = b = l;
} else {
var q = l <= .5 ? l * (s + 1) : l + s - l * s, p = l * 2 - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [ Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), match[4] ? parseFloat(match[4]) : 1 ];
};
fabric.Color.fromHsla = Color.fromHsl;
fabric.Color.fromHex = function(color) {
return Color.fromSource(Color.sourceFromHex(color));
};
fabric.Color.sourceFromHex = function(color) {
if (color.match(Color.reHex)) {
var value = color.slice(color.indexOf("#") + 1), isShortNotation = value.length === 3, r = isShortNotation ? value.charAt(0) + value.charAt(0) : value.substring(0, 2), g = isShortNotation ? value.charAt(1) + value.charAt(1) : value.substring(2, 4), b = isShortNotation ? value.charAt(2) + value.charAt(2) : value.substring(4, 6);
return [ parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1 ];
}
};
fabric.Color.fromSource = function(source) {
var oColor = new Color();
oColor.setSource(source);
return oColor;
};
})(typeof exports !== "undefined" ? exports : this);
(function() {
function getColorStop(el) {
var style = el.getAttribute("style"), offset = el.getAttribute("offset"), color, colorAlpha, opacity;
offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
offset = offset < 0 ? 0 : offset > 1 ? 1 : offset;
if (style) {
var keyValuePairs = style.split(/\s*;\s*/);
if (keyValuePairs[keyValuePairs.length - 1] === "") {
keyValuePairs.pop();
}
for (var i = keyValuePairs.length; i--; ) {
var split = keyValuePairs[i].split(/\s*:\s*/), key = split[0].trim(), value = split[1].trim();
if (key === "stop-color") {
color = value;
} else if (key === "stop-opacity") {
opacity = value;
}
}
}
if (!color) {
color = el.getAttribute("stop-color") || "rgb(0,0,0)";
}
if (!opacity) {
opacity = el.getAttribute("stop-opacity");
}
color = new fabric.Color(color);
colorAlpha = color.getAlpha();
opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
opacity *= colorAlpha;
return {
offset: offset,
color: color.toRgb(),
opacity: opacity
};
}
function getLinearCoords(el) {
return {
x1: el.getAttribute("x1") || 0,
y1: el.getAttribute("y1") || 0,
x2: el.getAttribute("x2") || "100%",
y2: el.getAttribute("y2") || 0
};
}
function getRadialCoords(el) {
return {
x1: el.getAttribute("fx") || el.getAttribute("cx") || "50%",
y1: el.getAttribute("fy") || el.getAttribute("cy") || "50%",
r1: 0,
x2: el.getAttribute("cx") || "50%",
y2: el.getAttribute("cy") || "50%",
r2: el.getAttribute("r") || "50%"
};
}
fabric.Gradient = fabric.util.createClass({
offsetX: 0,
offsetY: 0,
initialize: function(options) {
options || (options = {});
var coords = {};
this.id = fabric.Object.__uid++;
this.type = options.type || "linear";
coords = {
x1: options.coords.x1 || 0,
y1: options.coords.y1 || 0,
x2: options.coords.x2 || 0,
y2: options.coords.y2 || 0
};
if (this.type === "radial") {
coords.r1 = options.coords.r1 || 0;
coords.r2 = options.coords.r2 || 0;
}
this.coords = coords;
this.colorStops = options.colorStops.slice();
if (options.gradientTransform) {
this.gradientTransform = options.gradientTransform;
}
this.offsetX = options.offsetX || this.offsetX;
this.offsetY = options.offsetY || this.offsetY;
},
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()
});
}
return this;
},
toObject: function() {
return {
type: this.type,
coords: this.coords,
colorStops: this.colorStops,
offsetX: this.offsetX,
offsetY: this.offsetY
};
},
toSVG: function(object) {
var coords = fabric.util.object.clone(this.coords), markup, commonAttributes;
this.colorStops.sort(function(a, b) {
return a.offset - b.offset;
});
if (!(object.group && object.group.type === "path-group")) {
for (var prop in coords) {
if (prop === "x1" || prop === "x2" || prop === "r2") {
coords[prop] += this.offsetX - object.width / 2;
} else if (prop === "y1" || prop === "y2") {
coords[prop] += this.offsetY - object.height / 2;
}
}
}
commonAttributes = 'id="SVGID_' + this.id + '" gradientUnits="userSpaceOnUse"';
if (this.gradientTransform) {
commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(" ") + ')" ';
}
if (this.type === "linear") {
markup = [ "\n' ];
} else if (this.type === "radial") {
markup = [ "\n' ];
}
for (var i = 0; i < this.colorStops.length; i++) {
markup.push("\n');
}
markup.push(this.type === "linear" ? "\n" : "\n");
return markup.join("");
},
toLive: function(ctx, object) {
var gradient, prop, coords = fabric.util.object.clone(this.coords);
if (!this.type) {
return;
}
if (object.group && object.group.type === "path-group") {
for (prop in coords) {
if (prop === "x1" || prop === "x2") {
coords[prop] += -this.offsetX + object.width / 2;
} else if (prop === "y1" || prop === "y2") {
coords[prop] += -this.offsetY + object.height / 2;
}
}
}
if (this.type === "linear") {
gradient = ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2);
} else if (this.type === "radial") {
gradient = ctx.createRadialGradient(coords.x1, coords.y1, coords.r1, coords.x2, coords.y2, coords.r2);
}
for (var i = 0, len = this.colorStops.length; i < len; i++) {
var color = this.colorStops[i].color, opacity = this.colorStops[i].opacity, offset = this.colorStops[i].offset;
if (typeof opacity !== "undefined") {
color = new fabric.Color(color).setAlpha(opacity).toRgba();
}
gradient.addColorStop(parseFloat(offset), color);
}
return gradient;
}
});
fabric.util.object.extend(fabric.Gradient, {
fromElement: function(el, instance) {
var colorStopEls = el.getElementsByTagName("stop"), type = el.nodeName === "linearGradient" ? "linear" : "radial", gradientUnits = el.getAttribute("gradientUnits") || "objectBoundingBox", gradientTransform = el.getAttribute("gradientTransform"), colorStops = [], coords = {}, ellipseMatrix;
if (type === "linear") {
coords = getLinearCoords(el);
} else if (type === "radial") {
coords = getRadialCoords(el);
}
for (var i = colorStopEls.length; i--; ) {
colorStops.push(getColorStop(colorStopEls[i]));
}
ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
var gradient = new fabric.Gradient({
type: type,
coords: coords,
colorStops: colorStops,
offsetX: -instance.left,
offsetY: -instance.top
});
if (gradientTransform || ellipseMatrix !== "") {
gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || "") + ellipseMatrix);
}
return gradient;
},
forObject: function(obj, options) {
options || (options = {});
_convertPercentUnitsToValues(obj, options.coords, "userSpaceOnUse");
return new fabric.Gradient(options);
}
});
function _convertPercentUnitsToValues(object, options, gradientUnits) {
var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = "";
for (var prop in options) {
propValue = parseFloat(options[prop], 10);
if (typeof options[prop] === "string" && /^\d+%$/.test(options[prop])) {
multFactor = .01;
} else {
multFactor = 1;
}
if (prop === "x1" || prop === "x2" || prop === "r2") {
multFactor *= gradientUnits === "objectBoundingBox" ? object.width : 1;
addFactor = gradientUnits === "objectBoundingBox" ? object.left || 0 : 0;
} else if (prop === "y1" || prop === "y2") {
multFactor *= gradientUnits === "objectBoundingBox" ? object.height : 1;
addFactor = gradientUnits === "objectBoundingBox" ? object.top || 0 : 0;
}
options[prop] = propValue * multFactor + addFactor;
}
if (object.type === "ellipse" && options.r2 !== null && gradientUnits === "objectBoundingBox" && object.rx !== object.ry) {
var scaleFactor = object.ry / object.rx;
ellipseMatrix = " scale(1, " + scaleFactor + ")";
if (options.y1) {
options.y1 /= scaleFactor;
}
if (options.y2) {
options.y2 /= scaleFactor;
}
}
return ellipseMatrix;
}
})();
fabric.Pattern = fabric.util.createClass({
repeat: "repeat",
offsetX: 0,
offsetY: 0,
initialize: function(options) {
options || (options = {});
this.id = fabric.Object.__uid++;
if (options.source) {
if (typeof options.source === "string") {
if (typeof fabric.util.getFunctionBody(options.source) !== "undefined") {
this.source = new Function(fabric.util.getFunctionBody(options.source));
} else {
var _this = this;
this.source = fabric.util.createImage();
fabric.util.loadImage(options.source, function(img) {
_this.source = img;
});
}
} else {
this.source = options.source;
}
}
if (options.repeat) {
this.repeat = options.repeat;
}
if (options.offsetX) {
this.offsetX = options.offsetX;
}
if (options.offsetY) {
this.offsetY = options.offsetY;
}
},
toObject: function() {
var source;
if (typeof this.source === "function") {
source = String(this.source);
} else if (typeof this.source.src === "string") {
source = this.source.src;
}
return {
source: source,
repeat: this.repeat,
offsetX: this.offsetX,
offsetY: this.offsetY
};
},
toSVG: function(object) {
var patternSource = typeof this.source === "function" ? this.source() : this.source, patternWidth = patternSource.width / object.getWidth(), patternHeight = patternSource.height / object.getHeight(), patternOffsetX = this.offsetX / object.getWidth(), patternOffsetY = this.offsetY / object.getHeight(), patternImgSrc = "";
if (this.repeat === "repeat-x" || this.repeat === "no-repeat") {
patternHeight = 1;
}
if (this.repeat === "repeat-y" || this.repeat === "no-repeat") {
patternWidth = 1;
}
if (patternSource.src) {
patternImgSrc = patternSource.src;
} else if (patternSource.toDataURL) {
patternImgSrc = patternSource.toDataURL();
}
return '\n' + '\n' + "\n";
},
toLive: function(ctx) {
var source = typeof this.source === "function" ? this.source() : this.source;
if (!source) {
return "";
}
if (typeof source.src !== "undefined") {
if (!source.complete) {
return "";
}
if (source.naturalWidth === 0 || source.naturalHeight === 0) {
return "";
}
}
return ctx.createPattern(source, this.repeat);
}
});
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {}), toFixed = fabric.util.toFixed;
if (fabric.Shadow) {
fabric.warn("fabric.Shadow is already defined.");
return;
}
fabric.Shadow = fabric.util.createClass({
color: "rgb(0,0,0)",
blur: 0,
offsetX: 0,
offsetY: 0,
affectStroke: false,
includeDefaultValues: true,
initialize: function(options) {
if (typeof options === "string") {
options = this._parseShadow(options);
}
for (var prop in options) {
this[prop] = options[prop];
}
this.id = fabric.Object.__uid++;
},
_parseShadow: function(shadow) {
var shadowStr = shadow.trim(), offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [], color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, "") || "rgb(0,0,0)";
return {
color: color.trim(),
offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
blur: parseInt(offsetsAndBlur[3], 10) || 0
};
},
toString: function() {
return [ this.offsetX, this.offsetY, this.blur, this.color ].join("px ");
},
toSVG: function(object) {
var mode = "SourceAlpha", fBoxX = 40, fBoxY = 40;
if (object && (object.fill === this.color || object.stroke === this.color)) {
mode = "SourceGraphic";
}
if (object.width && object.height) {
fBoxX = toFixed(Math.abs(this.offsetX / object.getWidth()), 2) * 100 + 20;
fBoxY = toFixed(Math.abs(this.offsetY / object.getHeight()), 2) * 100 + 20;
}
return '\n" + ' \n' + ' \n' + ' \n' + " \n" + " \n" + ' \n' + " \n" + "\n";
},
toObject: function() {
if (this.includeDefaultValues) {
return {
color: this.color,
blur: this.blur,
offsetX: this.offsetX,
offsetY: this.offsetY
};
}
var obj = {}, proto = fabric.Shadow.prototype;
if (this.color !== proto.color) {
obj.color = this.color;
}
if (this.blur !== proto.blur) {
obj.blur = this.blur;
}
if (this.offsetX !== proto.offsetX) {
obj.offsetX = this.offsetX;
}
if (this.offsetY !== proto.offsetY) {
obj.offsetY = this.offsetY;
}
return obj;
}
});
fabric.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 extend = fabric.util.object.extend, getElementOffset = fabric.util.getElementOffset, removeFromArray = fabric.util.removeFromArray, CANVAS_INIT_ERROR = new Error("Could not initialize `canvas` element");
fabric.StaticCanvas = fabric.util.createClass({
initialize: function(el, options) {
options || (options = {});
this._initStatic(el, options);
fabric.StaticCanvas.activeInstance = this;
},
backgroundColor: "",
backgroundImage: null,
overlayColor: "",
overlayImage: null,
includeDefaultValues: true,
stateful: true,
renderOnAddRemove: true,
clipTo: null,
controlsAboveOverlay: false,
allowTouchScrolling: false,
imageSmoothingEnabled: true,
preserveObjectStacking: false,
viewportTransform: [ 1, 0, 0, 1, 0, 0 ],
onBeforeScaleRotate: function() {},
_initStatic: function(el, options) {
this._objects = [];
this._createLowerCanvas(el);
this._initOptions(options);
this._setImageSmoothing();
if (options.overlayImage) {
this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
}
if (options.backgroundImage) {
this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
}
if (options.backgroundColor) {
this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
}
if (options.overlayColor) {
this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
}
this.calcOffset();
},
calcOffset: function() {
this._offset = getElementOffset(this.lowerCanvasEl);
return this;
},
setOverlayImage: function(image, callback, options) {
return this.__setBgOverlayImage("overlayImage", image, callback, options);
},
setBackgroundImage: function(image, callback, options) {
return this.__setBgOverlayImage("backgroundImage", image, callback, options);
},
setOverlayColor: function(overlayColor, callback) {
return this.__setBgOverlayColor("overlayColor", overlayColor, callback);
},
setBackgroundColor: function(backgroundColor, callback) {
return this.__setBgOverlayColor("backgroundColor", backgroundColor, callback);
},
_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;
},
__setBgOverlayImage: function(property, image, callback, options) {
if (typeof image === "string") {
fabric.util.loadImage(image, function(img) {
this[property] = new fabric.Image(img, options);
callback && callback();
}, this, options && options.crossOrigin);
} else {
options && image.setOptions(options);
this[property] = image;
callback && callback();
}
return this;
},
__setBgOverlayColor: function(property, color, callback) {
if (color && color.source) {
var _this = this;
fabric.util.loadImage(color.source, function(img) {
_this[property] = new fabric.Pattern({
source: img,
repeat: color.repeat,
offsetX: color.offsetX,
offsetY: color.offsetY
});
callback && callback();
});
} else {
this[property] = color;
callback && callback();
}
return this;
},
_createCanvasElement: function() {
var element = fabric.document.createElement("canvas");
if (!element.style) {
element.style = {};
}
if (!element) {
throw CANVAS_INIT_ERROR;
}
this._initCanvasElement(element);
return element;
},
_initCanvasElement: function(element) {
fabric.util.createCanvasElement(element);
if (typeof element.getContext === "undefined") {
throw CANVAS_INIT_ERROR;
}
},
_initOptions: function(options) {
for (var prop in options) {
this[prop] = options[prop];
}
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";
this.viewportTransform = this.viewportTransform.slice();
},
_createLowerCanvas: function(canvasEl) {
this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
this._initCanvasElement(this.lowerCanvasEl);
fabric.util.addClass(this.lowerCanvasEl, "lower-canvas");
if (this.interactive) {
this._applyCanvasStyle(this.lowerCanvasEl);
}
this.contextContainer = this.lowerCanvasEl.getContext("2d");
},
getWidth: function() {
return this.width;
},
getHeight: function() {
return this.height;
},
setWidth: function(value, options) {
return this.setDimensions({
width: value
}, options);
},
setHeight: function(value, options) {
return this.setDimensions({
height: value
}, options);
},
setDimensions: function(dimensions, options) {
var cssValue;
options = options || {};
for (var prop in dimensions) {
cssValue = dimensions[prop];
if (!options.cssOnly) {
this._setBackstoreDimension(prop, dimensions[prop]);
cssValue += "px";
}
if (!options.backstoreOnly) {
this._setCssDimension(prop, cssValue);
}
}
if (!options.cssOnly) {
this.renderAll();
}
this.calcOffset();
return this;
},
_setBackstoreDimension: function(prop, value) {
this.lowerCanvasEl[prop] = value;
if (this.upperCanvasEl) {
this.upperCanvasEl[prop] = value;
}
if (this.cacheCanvasEl) {
this.cacheCanvasEl[prop] = value;
}
this[prop] = value;
return this;
},
_setCssDimension: function(prop, value) {
this.lowerCanvasEl.style[prop] = value;
if (this.upperCanvasEl) {
this.upperCanvasEl.style[prop] = value;
}
if (this.wrapperEl) {
this.wrapperEl.style[prop] = value;
}
return this;
},
getZoom: function() {
return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
},
setViewportTransform: function(vpt) {
var activeGroup = this.getActiveGroup();
this.viewportTransform = vpt;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
if (activeGroup) {
activeGroup.setCoords();
}
return this;
},
zoomToPoint: function(point, value) {
var before = point;
point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
this.viewportTransform[0] = value;
this.viewportTransform[3] = value;
var after = fabric.util.transformPoint(point, this.viewportTransform);
this.viewportTransform[4] += before.x - after.x;
this.viewportTransform[5] += before.y - after.y;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
return this;
},
setZoom: function(value) {
this.zoomToPoint(new fabric.Point(0, 0), value);
return this;
},
absolutePan: function(point) {
this.viewportTransform[4] = -point.x;
this.viewportTransform[5] = -point.y;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
return this;
},
relativePan: function(point) {
return this.absolutePan(new fabric.Point(-point.x - this.viewportTransform[4], -point.y - this.viewportTransform[5]));
},
getElement: function() {
return this.lowerCanvasEl;
},
getActiveObject: function() {
return null;
},
getActiveGroup: function() {
return null;
},
_draw: function(ctx, object) {
if (!object) {
return;
}
ctx.save();
var v = this.viewportTransform;
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
if (this._shouldRenderObject(object)) {
object.render(ctx);
}
ctx.restore();
if (!this.controlsAboveOverlay) {
object._renderControls(ctx);
}
},
_shouldRenderObject: function(object) {
if (!object) {
return false;
}
return object !== this.getActiveGroup() || !this.preserveObjectStacking;
},
_onObjectAdded: function(obj) {
this.stateful && obj.setupState();
obj._set("canvas", this);
obj.setCoords();
this.fire("object:added", {
target: obj
});
obj.fire("added");
},
_onObjectRemoved: function(obj) {
if (this.getActiveObject() === obj) {
this.fire("before:selection:cleared", {
target: obj
});
this._discardActiveObject();
this.fire("selection:cleared");
}
this.fire("object:removed", {
target: obj
});
obj.fire("removed");
},
clearContext: function(ctx) {
ctx.clearRect(0, 0, this.width, this.height);
return this;
},
getContext: function() {
return this.contextContainer;
},
clear: function() {
this._objects.length = 0;
if (this.discardActiveGroup) {
this.discardActiveGroup();
}
if (this.discardActiveObject) {
this.discardActiveObject();
}
this.clearContext(this.contextContainer);
if (this.contextTop) {
this.clearContext(this.contextTop);
}
this.fire("canvas:cleared");
this.renderAll();
return this;
},
renderAll: function(allOnTop) {
var canvasToDrawOn = this[allOnTop === true && this.interactive ? "contextTop" : "contextContainer"], activeGroup = this.getActiveGroup();
if (this.contextTop && this.selection && !this._groupSelector) {
this.clearContext(this.contextTop);
}
if (!allOnTop) {
this.clearContext(canvasToDrawOn);
}
this.fire("before:render");
if (this.clipTo) {
fabric.util.clipContext(this, canvasToDrawOn);
}
this._renderBackground(canvasToDrawOn);
this._renderObjects(canvasToDrawOn, activeGroup);
this._renderActiveGroup(canvasToDrawOn, activeGroup);
if (this.clipTo) {
canvasToDrawOn.restore();
}
this._renderOverlay(canvasToDrawOn);
if (this.controlsAboveOverlay && this.interactive) {
this.drawControls(canvasToDrawOn);
}
this.fire("after:render");
return this;
},
_renderObjects: function(ctx, activeGroup) {
var i, length;
if (!activeGroup || this.preserveObjectStacking) {
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]);
}
}
}
},
_renderActiveGroup: function(ctx, activeGroup) {
if (activeGroup) {
var sortedObjects = [];
this.forEachObject(function(object) {
if (activeGroup.contains(object)) {
sortedObjects.push(object);
}
});
activeGroup._set("_objects", sortedObjects.reverse());
this._draw(ctx, activeGroup);
}
},
_renderBackground: function(ctx) {
if (this.backgroundColor) {
ctx.fillStyle = this.backgroundColor.toLive ? this.backgroundColor.toLive(ctx) : this.backgroundColor;
ctx.fillRect(this.backgroundColor.offsetX || 0, this.backgroundColor.offsetY || 0, this.width, this.height);
}
if (this.backgroundImage) {
this._draw(ctx, this.backgroundImage);
}
},
_renderOverlay: function(ctx) {
if (this.overlayColor) {
ctx.fillStyle = this.overlayColor.toLive ? this.overlayColor.toLive(ctx) : this.overlayColor;
ctx.fillRect(this.overlayColor.offsetX || 0, this.overlayColor.offsetY || 0, this.width, this.height);
}
if (this.overlayImage) {
this._draw(ctx, this.overlayImage);
}
},
renderTop: function() {
var ctx = this.contextTop || this.contextContainer;
this.clearContext(ctx);
if (this.selection && this._groupSelector) {
this._drawSelection();
}
var activeGroup = this.getActiveGroup();
if (activeGroup) {
activeGroup.render(ctx);
}
this._renderOverlay(ctx);
this.fire("after:render");
return this;
},
getCenter: function() {
return {
top: this.getHeight() / 2,
left: this.getWidth() / 2
};
},
centerObjectH: function(object) {
this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
this.renderAll();
return this;
},
centerObjectV: function(object) {
this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
this.renderAll();
return this;
},
centerObject: function(object) {
var center = this.getCenter();
this._centerObject(object, new fabric.Point(center.left, center.top));
this.renderAll();
return this;
},
_centerObject: function(object, center) {
object.setPositionByOrigin(center, "center", "center");
return this;
},
toDatalessJSON: function(propertiesToInclude) {
return this.toDatalessObject(propertiesToInclude);
},
toObject: function(propertiesToInclude) {
return this._toObjectMethod("toObject", propertiesToInclude);
},
toDatalessObject: function(propertiesToInclude) {
return this._toObjectMethod("toDatalessObject", propertiesToInclude);
},
_toObjectMethod: function(methodName, propertiesToInclude) {
var data = {
objects: this._toObjects(methodName, propertiesToInclude)
};
extend(data, this.__serializeBgOverlay());
fabric.util.populateWithProperties(this, data, propertiesToInclude);
return data;
},
_toObjects: function(methodName, propertiesToInclude) {
return this.getObjects().map(function(instance) {
return this._toObject(instance, methodName, propertiesToInclude);
}, this);
},
_toObject: function(instance, methodName, propertiesToInclude) {
var originalValue;
if (!this.includeDefaultValues) {
originalValue = instance.includeDefaultValues;
instance.includeDefaultValues = false;
}
var originalProperties = this._realizeGroupTransformOnObject(instance), object = instance[methodName](propertiesToInclude);
if (!this.includeDefaultValues) {
instance.includeDefaultValues = originalValue;
}
this._unwindGroupTransformOnObject(instance, originalProperties);
return object;
},
_realizeGroupTransformOnObject: function(instance) {
var layoutProps = [ "angle", "flipX", "flipY", "height", "left", "scaleX", "scaleY", "top", "width" ];
if (instance.group && instance.group === this.getActiveGroup()) {
var originalValues = {};
layoutProps.forEach(function(prop) {
originalValues[prop] = instance[prop];
});
this.getActiveGroup().realizeTransform(instance);
return originalValues;
} else {
return null;
}
},
_unwindGroupTransformOnObject: function(instance, originalValues) {
if (originalValues) {
instance.set(originalValues);
}
},
__serializeBgOverlay: function() {
var data = {
background: this.backgroundColor && this.backgroundColor.toObject ? this.backgroundColor.toObject() : this.backgroundColor
};
if (this.overlayColor) {
data.overlay = this.overlayColor.toObject ? this.overlayColor.toObject() : this.overlayColor;
}
if (this.backgroundImage) {
data.backgroundImage = this.backgroundImage.toObject();
}
if (this.overlayImage) {
data.overlayImage = this.overlayImage.toObject();
}
return data;
},
svgViewportTransformation: true,
toSVG: function(options, reviver) {
options || (options = {});
var markup = [];
this._setSVGPreamble(markup, options);
this._setSVGHeader(markup, options);
this._setSVGBgOverlayColor(markup, "backgroundColor");
this._setSVGBgOverlayImage(markup, "backgroundImage");
this._setSVGObjects(markup, reviver);
this._setSVGBgOverlayColor(markup, "overlayColor");
this._setSVGBgOverlayImage(markup, "overlayImage");
markup.push("");
return markup.join("");
},
_setSVGPreamble: function(markup, options) {
if (!options.suppressPreamble) {
markup.push('', '\n');
}
},
_setSVGHeader: function(markup, options) {
var width, height, vpt;
if (options.viewBox) {
width = options.viewBox.width;
height = options.viewBox.height;
} else {
width = this.width;
height = this.height;
if (!this.svgViewportTransformation) {
vpt = this.viewportTransform;
width /= vpt[0];
height /= vpt[3];
}
}
markup.push("