var fabric = fabric || {
version: "2.0.0-beta4"
};
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(decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E"), {
features: {
FetchExternalResources: [ "img" ]
}
});
fabric.window = fabric.document.defaultView;
}
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", "id" ];
fabric.DPI = 96;
fabric.reNum = "(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)";
fabric.fontPaths = {};
fabric.iMatrix = [ 1, 0, 0, 1, 0, 0 ];
fabric.canvasModule = "canvas";
fabric.perfLimitSizeTotal = 2097152;
fabric.maxCacheSideLimit = 4096;
fabric.minCacheSideLimit = 256;
fabric.charWidthsCache = {};
fabric.textureSize = 2048;
fabric.enableGLFiltering = true;
fabric.devicePixelRatio = fabric.window.devicePixelRatio || fabric.window.webkitDevicePixelRatio || fabric.window.mozDevicePixelRatio || 1;
fabric.initFilterBackend = function() {
if (fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize) && fabric.enableGLFiltering) {
console.log("max texture size: " + fabric.maxTextureSize);
return new fabric.WebglFilterBackend({
tileSize: fabric.textureSize
});
} else if (fabric.Canvas2dFilterBackend) {
return new fabric.Canvas2dFilterBackend();
}
};
(function() {
function _removeEventListener(eventName, handler) {
if (!this.__eventListeners[eventName]) {
return;
}
var eventListener = this.__eventListeners[eventName];
if (handler) {
eventListener[eventListener.indexOf(handler)] = false;
} else {
fabric.util.array.fill(eventListener, false);
}
}
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) {
for (eventName in this.__eventListeners) {
_removeEventListener.call(this, eventName);
}
} 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] && listenersForEvent[i].call(this, options || {});
}
this.__eventListeners[eventName] = listenersForEvent.filter(function(value) {
return value !== false;
});
return this;
}
fabric.Observable = {
observe: observe,
stopObserving: stopObserving,
fire: fire,
on: observe,
off: stopObserving,
trigger: fire
};
})();
fabric.Collection = {
_objects: [],
add: function() {
this._objects.push.apply(this._objects, arguments);
if (this._onObjectAdded) {
for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
}
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
insertAt: function(object, index, nonSplicing) {
var objects = this.getObjects();
if (nonSplicing) {
objects[index] = object;
} else {
objects.splice(index, 0, object);
}
this._onObjectAdded && this._onObjectAdded(object);
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
remove: function() {
var objects = this.getObjects(), index, somethingRemoved = false;
for (var i = 0, length = arguments.length; i < length; i++) {
index = objects.indexOf(arguments[i]);
if (index !== -1) {
somethingRemoved = true;
objects.splice(index, 1);
this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
}
}
this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
return this;
},
forEachObject: function(callback, context) {
var objects = this.getObjects();
for (var i = 0, len = objects.length; i < len; 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);
}
};
fabric.CommonMethods = {
_setOptions: function(options) {
for (var prop in options) {
this.set(prop, options[prop]);
}
},
_initGradient: function(filler, property) {
if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
this.set(property, new fabric.Gradient(filler));
}
},
_initPattern: function(filler, property, callback) {
if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
this.set(property, new fabric.Pattern(filler, callback));
} else {
callback && callback();
}
},
_initClipping: function(options) {
if (!options.clipTo || typeof options.clipTo !== "string") {
return;
}
var functionBody = fabric.util.getFunctionBody(options.clipTo);
if (typeof functionBody !== "undefined") {
this.clipTo = new Function("ctx", functionBody);
}
},
_setObject: function(obj) {
for (var prop in obj) {
this._set(prop, obj[prop]);
}
},
set: function(key, value) {
if (typeof key === "object") {
this._setObject(key);
} else {
if (typeof value === "function" && key !== "clipTo") {
this._set(key, value(this.get(key)));
} else {
this._set(key, value);
}
}
return this;
},
_set: function(key, value) {
this[key] = value;
},
toggle: function(property) {
var value = this.get(property);
if (typeof value === "boolean") {
this.set(property, !value);
}
return this;
},
get: function(property) {
return this[property];
}
};
(function(global) {
var sqrt = Math.sqrt, atan2 = Math.atan2, pow = Math.pow, abs = Math.abs, 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) {
point.subtractEquals(origin);
var v = fabric.util.rotateVector(point, radians);
return new fabric.Point(v.x, v.y).addEquals(origin);
},
rotateVector: function(vector, radians) {
var sin = Math.sin(radians), cos = Math.cos(radians), rx = vector.x * cos - vector.y * sin, ry = vector.x * sin + vector.y * cos;
return {
x: rx,
y: ry
};
},
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]);
},
makeBoundingBoxFromPoints: function(points) {
var xPoints = [ points[0].x, points[1].x, points[2].x, points[3].x ], minX = fabric.util.array.min(xPoints), maxX = fabric.util.array.max(xPoints), width = Math.abs(minX - maxX), yPoints = [ points[0].y, points[1].y, points[2].y, points[3].y ], minY = fabric.util.array.min(yPoints), maxY = fabric.util.array.max(yPoints), height = Math.abs(minY - maxY);
return {
left: minX,
top: minY,
width: width,
height: height
};
},
invertTransform: function(t) {
var a = 1 / (t[0] * t[3] - t[1] * t[2]), r = [ a * t[3], -a * t[1], -a * t[2], a * t[0] ], o = fabric.util.transformPoint({
x: t[4],
y: t[5]
}, r, true);
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, i, obj = global || fabric.window;
for (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 && crossOrigin) {
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);
klass.fromObject(o, function(obj, error) {
error || (enlivenedObjects[index] = obj);
reviver && reviver(o, obj, error);
onLoaded();
});
});
},
enlivenPatterns: function(patterns, callback) {
patterns = patterns || [];
function onLoaded() {
if (++numLoadedPatterns === numPatterns) {
callback && callback(enlivenedPatterns);
}
}
var enlivenedPatterns = [], numLoadedPatterns = 0, numPatterns = patterns.length;
if (!numPatterns) {
callback && callback(enlivenedPatterns);
return;
}
patterns.forEach(function(p, index) {
if (p && p.source) {
new fabric.Pattern(p, function(pattern) {
enlivenedPatterns[index] = pattern;
onLoaded();
});
} else {
enlivenedPatterns[index] = p;
onLoaded();
}
});
},
groupSVGElements: function(elements, options, path) {
var object;
if (elements.length === 1) {
return elements[0];
}
if (options) {
if (options.width && options.height) {
options.centerPoint = {
x: options.width / 2,
y: options.height / 2
};
} else {
delete options.width;
delete options.height;
}
}
object = new fabric.Group(elements, options);
if (typeof path !== "undefined") {
object.sourcePath = 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"));
return canvasEl;
},
createImage: function() {
return fabric.document.createElement("img");
},
clipContext: function(receiver, ctx) {
ctx.save();
ctx.beginPath();
receiver.clipTo(ctx);
ctx.clip();
},
multiplyTransformMatrices: function(a, b, is2x2) {
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], is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4], is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5] ];
},
qrDecompose: function(a) {
var angle = atan2(a[1], a[0]), denom = pow(a[0], 2) + pow(a[1], 2), scaleX = sqrt(denom), scaleY = (a[0] * a[3] - a[2] * a[1]) / scaleX, skewX = atan2(a[0] * a[2] + a[1] * a[3], denom);
return {
angle: angle / PiBy180,
scaleX: scaleX,
scaleY: scaleY,
skewX: skewX / PiBy180,
skewY: 0,
translateX: a[4],
translateY: a[5]
};
},
customTransformMatrix: function(scaleX, scaleY, skewX) {
var skewMatrixX = [ 1, 0, abs(Math.tan(skewX * PiBy180)), 1 ], scaleMatrix = [ abs(scaleX), 0, 0, abs(scaleY) ];
return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
},
resetObjectTransform: function(target) {
target.scaleX = 1;
target.scaleY = 1;
target.skewX = 0;
target.skewY = 0;
target.flipX = false;
target.flipY = false;
target.setAngle(0);
},
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, i, temp, imageData = ctx.getImageData(x, y, tolerance * 2 || 1, tolerance * 2 || 1), l = imageData.data.length;
for (i = 3; i < l; i += 4) {
temp = imageData.data[i];
_isTransparent = temp <= 0;
if (_isTransparent === false) {
break;
}
}
imageData = null;
return _isTransparent;
},
parsePreserveAspectRatioAttribute: function(attribute) {
var meetOrSlice = "meet", alignX = "Mid", alignY = "Mid", aspectRatioAttrs = attribute.split(" "), align;
if (aspectRatioAttrs && aspectRatioAttrs.length) {
meetOrSlice = aspectRatioAttrs.pop();
if (meetOrSlice !== "meet" && meetOrSlice !== "slice") {
align = meetOrSlice;
meetOrSlice = "meet";
} else if (aspectRatioAttrs.length) {
align = aspectRatioAttrs.pop();
}
}
alignX = align !== "none" ? align.slice(1, 4) : "none";
alignY = align !== "none" ? align.slice(5, 8) : "none";
return {
meetOrSlice: meetOrSlice,
alignX: alignX,
alignY: alignY
};
},
clearFabricFontCache: function(fontFamily) {
if (!fontFamily) {
fabric.charWidthsCache = {};
} else if (fabric.charWidthsCache[fontFamily]) {
delete fabric.charWidthsCache[fontFamily];
}
},
limitDimsByArea: function(ar, maximumArea) {
var roughWidth = Math.sqrt(maximumArea * ar), perfLimitSizeY = Math.floor(maximumArea / roughWidth);
return {
x: Math.floor(roughWidth),
y: perfLimitSizeY
};
},
capValue: function(min, value, max) {
return Math.max(min, Math.min(value, max));
},
findScaleToFit: function(source, destination) {
return Math.min(destination.width / source.width, destination.height / source.height);
},
findScaleToCover: function(source, destination) {
return Math.max(destination.width / source.width, destination.height / source.height);
}
};
})(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);
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]);
bounds.push({
x: bound[0].x + fx,
y: bound[0].y + fy
});
bounds.push({
x: bound[1].x + fx,
y: bound[1].y + fy
});
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;
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 fill(array, value) {
var k = array.length;
while (k--) {
array[k] = value;
}
return array;
}
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 = {
fill: fill,
invoke: invoke,
min: min,
max: max
};
})();
(function() {
function extend(destination, source, deep) {
if (deep) {
if (!fabric.isLikelyNode && source instanceof Element) {
destination = source;
} else if (source instanceof Array) {
destination = [];
for (var i = 0, len = source.length; i < len; i++) {
destination[i] = extend({}, source[i], deep);
}
} else if (source && typeof source === "object") {
for (var property in source) {
if (source.hasOwnProperty(property)) {
destination[property] = extend({}, source[property], deep);
}
}
} else {
destination = source;
}
} else {
for (var property in source) {
destination[property] = source[property];
}
}
return destination;
}
function clone(object, deep) {
return extend({}, object, deep);
}
fabric.util.object = {
extend: extend,
clone: clone
};
})();
(function() {
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, ">");
}
function graphemeSplit(textstring) {
var i = 0, graphemes = [];
for (var i = 0, chr; i < textstring.length; i++) {
if ((chr = getWholeChar(textstring, i)) === false) {
continue;
}
graphemes.push(chr);
}
return graphemes;
}
function getWholeChar(str, i) {
var code = str.charCodeAt(i);
if (isNaN(code)) {
return "";
}
if (code < 55296 || code > 57343) {
return str.charAt(i);
}
if (55296 <= code && code <= 56319) {
if (str.length <= i + 1) {
throw "High surrogate without following low surrogate";
}
var next = str.charCodeAt(i + 1);
if (56320 > next || next > 57343) {
throw "High surrogate without following low surrogate";
}
return str.charAt(i) + str.charAt(i + 1);
}
if (i === 0) {
throw "Low surrogate without preceding high surrogate";
}
var prev = str.charCodeAt(i - 1);
if (55296 > prev || prev > 56319) {
throw "Low surrogate without preceding high surrogate";
}
return false;
}
fabric.util.string = {
camelize: camelize,
capitalize: capitalize,
escapeXml: escapeXml,
graphemeSplit: graphemeSplit
};
})();
(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 parentMethod = null, _this = this;
while (_this.constructor.superclass) {
var superClassMethod = _this.constructor.superclass.prototype[methodName];
if (_this[methodName] !== superClassMethod) {
parentMethod = superClassMethod;
break;
}
_this = _this.constructor.superclass.prototype;
}
if (!parentMethod) {
return console.log("tried to callSuper " + methodName + ", method not found in prototype chain", this);
}
return arguments.length > 1 ? parentMethod.apply(this, slice.call(arguments, 1)) : parentMethod.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, options) {
element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
};
removeListener = function(element, eventName, handler, options) {
element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
};
} else if (shouldUseAttachEventDetachEvent) {
addListener = function(element, eventName, handler) {
if (!element) {
return;
}
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) {
if (!element) {
return;
}
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) {
if (!element) {
return;
}
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) {
if (!element) {
return;
}
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) {
event || (event = fabric.window.event);
var element = event.target || (typeof event.srcElement !== unknown ? event.srcElement : null), scroll = fabric.util.getScrollLeftTop(element);
return {
x: pointerX(event) + scroll.left,
y: pointerY(event) + scroll.top
};
}
var pointerX = function(event) {
return event.clientX;
}, pointerY = function(event) {
return event.clientY;
};
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) {
var left = 0, top = 0, docElement = fabric.document.documentElement, body = fabric.document.body || {
scrollLeft: 0,
scrollTop: 0
};
while (element && (element.parentNode || element.host)) {
element = element.parentNode || element.host;
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;
}
if (element.nodeType === 1 && fabric.util.getElementStyle(element, "position") === "fixed") {
break;
}
}
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 offset;
}
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 = getScrollLeftTop(element);
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 = options.body || options.parameters;
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 noop() {
return false;
}
function animate(options) {
requestAnimFrame(function(timestamp) {
options || (options = {});
var start = timestamp || +new Date(), duration = options.duration || 500, finish = start + duration, time, onChange = options.onChange || noop, abort = options.abort || noop, onComplete = options.onComplete || noop, 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) {
if (abort()) {
onComplete(endValue, 1, 1);
return;
}
time = ticktime || +new Date();
var currentTime = time > finish ? duration : time - start, timePerc = currentTime / duration, current = easing(currentTime, startValue, byValue, duration), valuePerc = Math.abs((current - startValue) / byValue);
onChange(current, valuePerc, timePerc);
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 calculateColor(begin, end, pos) {
var color = "rgba(" + parseInt(begin[0] + pos * (end[0] - begin[0]), 10) + "," + parseInt(begin[1] + pos * (end[1] - begin[1]), 10) + "," + parseInt(begin[2] + pos * (end[2] - begin[2]), 10);
color += "," + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
color += ")";
return color;
}
function animateColor(fromColor, toColor, duration, options) {
var startColor = new fabric.Color(fromColor).getSource(), endColor = new fabric.Color(toColor).getSource();
options = options || {};
fabric.util.animate(fabric.util.object.extend(options, {
duration: duration || 500,
startValue: startColor,
endValue: endColor,
byValue: endColor,
easing: function(currentTime, startValue, byValue, duration) {
var posValue = options.colorEasing ? options.colorEasing(currentTime, duration) : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
return calculateColor(startValue, byValue, posValue);
}
}));
}
fabric.util.animateColor = animateColor;
})();
(function() {
function normalize(a, c, p, s) {
if (a < Math.abs(c)) {
a = c;
s = p / 4;
} else {
if (c === 0 && a === 0) {
s = p / (2 * Math.PI) * Math.asin(1);
} 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, clone = fabric.util.object.clone, toFixed = fabric.util.toFixed, parseUnit = fabric.util.parseUnit, multiplyTransformMatrices = fabric.util.multiplyTransformMatrices, reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/i, reViewBoxTagNames = /^(symbol|image|marker|pattern|view|svg)$/i, reNotAllowedAncestors = /^(?:pattern|defs|symbol|metadata|clipPath|mask)$/i, reAllowedParents = /^(symbol|g|a|svg)$/i, 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",
opacity: "opacity"
}, 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") {
if (value === "none") {
value = null;
} else {
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 === "opacity") {
value = parseFloat(value);
if (parentAttributes && typeof parentAttributes.opacity !== "undefined") {
value *= parentAttributes.opacity;
}
} 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 (typeof attributes[colorAttributes[attr]] === "undefined" || attributes[attr] === "") {
continue;
}
if (typeof attributes[attr] === "undefined") {
if (!fabric.Object.prototype[attr]) {
continue;
}
attributes[attr] = fabric.Object.prototype[attr];
}
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;
}
function _getMultipleNodes(doc, nodeNames) {
var nodeName, nodeArray = [], nodeList;
for (var i = 0; i < nodeNames.length; i++) {
nodeName = nodeNames[i];
nodeList = doc.getElementsByTagName(nodeName);
nodeArray = nodeArray.concat(Array.prototype.slice.call(nodeList));
}
return nodeArray;
}
fabric.parseTransformAttribute = function() {
function rotateMatrix(matrix, args) {
var cos = Math.cos(args[0]), sin = Math.sin(args[0]), x = 0, y = 0;
if (args.length === 3) {
x = args[1];
y = args[2];
}
matrix[0] = cos;
matrix[1] = sin;
matrix[2] = -sin;
matrix[3] = cos;
matrix[4] = x - (cos * x - sin * y);
matrix[5] = y - (sin * x + cos * y);
}
function scaleMatrix(matrix, args) {
var multiplierX = args[0], multiplierY = args.length === 2 ? args[1] : args[0];
matrix[0] = multiplierX;
matrix[3] = multiplierY;
}
function skewMatrix(matrix, args, pos) {
matrix[pos] = 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;
}), 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":
skewMatrix(matrix, args, 2);
break;
case "skewY":
skewMatrix(matrix, args, 1);
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(/;\s*$/, "").split(";").forEach(function(chunk) {
var pair = chunk.split(":");
attr = pair[0].trim().toLowerCase();
value = 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 = prop.toLowerCase();
value = 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, nodelist = doc.getElementsByTagName("*");
for (i = 0; i < nodelist.length; i++) {
node = nodelist[i];
if (id === node.getAttribute("id")) {
return node;
}
}
}
function parseUseDirectives(doc) {
var nodelist = _getMultipleNodes(doc, [ "use", "svg: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, attr, j, attrs, l;
applyViewboxTransform(el2);
if (/^svg$/i.test(el2.nodeName)) {
var el3 = el2.ownerDocument.createElement("g");
for (j = 0, attrs = el2.attributes, l = attrs.length; j < l; j++) {
attr = attrs.item(j);
el3.setAttribute(attr.nodeName, attr.nodeValue);
}
while (el2.firstChild) {
el3.appendChild(el2.firstChild);
}
el2 = el3;
}
for (j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
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 applyViewboxTransform(element) {
var viewBoxAttr = element.getAttribute("viewBox"), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el, widthAttr = element.getAttribute("width"), heightAttr = element.getAttribute("height"), x = element.getAttribute("x") || 0, y = element.getAttribute("y") || 0, preserveAspectRatio = element.getAttribute("preserveAspectRatio") || "", missingViewBox = !viewBoxAttr || !reViewBoxTagNames.test(element.nodeName) || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue)), missingDimAttr = !widthAttr || !heightAttr || widthAttr === "100%" || heightAttr === "100%", toBeParsed = missingViewBox && missingDimAttr, parsedDim = {}, translateMatrix = "";
parsedDim.width = 0;
parsedDim.height = 0;
parsedDim.toBeParsed = toBeParsed;
if (toBeParsed) {
return parsedDim;
}
if (missingViewBox) {
parsedDim.width = parseUnit(widthAttr);
parsedDim.height = parseUnit(heightAttr);
return parsedDim;
}
minX = -parseFloat(viewBoxAttr[1]);
minY = -parseFloat(viewBoxAttr[2]);
viewBoxWidth = parseFloat(viewBoxAttr[3]);
viewBoxHeight = parseFloat(viewBoxAttr[4]);
if (!missingDimAttr) {
parsedDim.width = parseUnit(widthAttr);
parsedDim.height = parseUnit(heightAttr);
scaleX = parsedDim.width / viewBoxWidth;
scaleY = parsedDim.height / viewBoxHeight;
} else {
parsedDim.width = viewBoxWidth;
parsedDim.height = viewBoxHeight;
}
preserveAspectRatio = fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio);
if (preserveAspectRatio.alignX !== "none") {
scaleY = scaleX = scaleX > scaleY ? scaleY : scaleX;
}
if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) {
return parsedDim;
}
if (x || y) {
translateMatrix = " translate(" + parseUnit(x) + " " + parseUnit(y) + ") ";
}
matrix = translateMatrix + " matrix(" + scaleX + " 0" + " 0 " + scaleY + " " + minX * scaleX + " " + minY * scaleY + ") ";
if (element.nodeName === "svg") {
el = element.ownerDocument.createElement("g");
while (element.firstChild) {
el.appendChild(element.firstChild);
}
element.appendChild(el);
} else {
el = element;
matrix = el.getAttribute("transform") + matrix;
}
el.setAttribute("transform", matrix);
return parsedDim;
}
function hasAncestorWithNodeName(element, nodeName) {
while (element && (element = element.parentNode)) {
if (element.nodeName && nodeName.test(element.nodeName.replace("svg:", "")) && !element.getAttribute("instantiated_by_use")) {
return true;
}
}
return false;
}
fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) {
if (!doc) {
return;
}
parseUseDirectives(doc);
var svgUid = fabric.Object.__uid++, options = applyViewboxTransform(doc), descendants = fabric.util.toArray(doc.getElementsByTagName("*"));
options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
options.svgUid = svgUid;
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) {
applyViewboxTransform(el);
return reAllowedSVGTagNames.test(el.nodeName.replace("svg:", "")) && !hasAncestorWithNodeName(el, reNotAllowedAncestors);
});
if (!elements || elements && !elements.length) {
callback && callback([], {});
return;
}
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
fabric.parseElements(elements, function(instances, elements) {
if (callback) {
callback(instances, options, elements, descendants);
}
}, clone(options), reviver, parsingOptions);
};
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 tagArray = [ "linearGradient", "radialGradient", "svg:linearGradient", "svg:radialGradient" ], elList = _getMultipleNodes(doc, tagArray), el, j = 0, id, xlink, gradientDefs = {}, idsToXlinkMap = {};
j = elList.length;
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 && reAllowedParents.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) {
memo[attr] = value;
}
return memo;
}, {});
ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
var normalizedAttr, normalizedValue, normalizedStyle = {};
for (var attr in ownAttributes) {
normalizedAttr = normalizeAttr(attr);
normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize);
normalizedStyle[normalizedAttr] = normalizedValue;
}
if (normalizedStyle && normalizedStyle.font) {
fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle);
}
var mergedAttrs = extend(parentAttributes, normalizedStyle);
return reAllowedParents.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
},
parseElements: function(elements, callback, options, reviver, parsingOptions) {
new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).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 || styles[i].text;
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 = pair[0], value = pair[1];
ruleObj[property] = value;
}
rule = match[1];
rule.split(",").forEach(function(_rule) {
_rule = _rule.replace(/^svg/i, "").trim();
if (_rule === "") {
return;
}
if (allRules[_rule]) {
fabric.util.object.extend(allRules[_rule], ruleObj);
} else {
allRules[_rule] = fabric.util.object.clone(ruleObj);
}
});
});
}
return allRules;
},
loadSVGFromURL: function(url, callback, reviver, options) {
url = url.replace(/^\n\s*/, "").trim();
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) {
callback && callback(null);
}
fabric.parseSVGDocument(xml.documentElement, function(results, _options, elements, allElements) {
callback && callback(results, _options, elements, allElements);
}, reviver, options);
}
},
loadSVGFromString: function(string, callback, reviver, options) {
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, elements, allElements) {
callback(results, _options, elements, allElements);
}, reviver, options);
}
});
})(typeof exports !== "undefined" ? exports : this);
fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) {
this.elements = elements;
this.callback = callback;
this.options = options;
this.reviver = reviver;
this.svgUid = options && options.svgUid || 0;
this.parsingOptions = parsingOptions;
};
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(_obj, i) {
setTimeout(function() {
_obj.createObject(_obj.elements[i], i);
}, 0);
})(this, i);
}
};
fabric.ElementsParser.prototype.createObject = function(el, index) {
var klass = fabric[fabric.util.string.capitalize(el.tagName.replace("svg:", ""))];
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) {
klass.fromElement(el, this.createCallback(index, el), this.options);
};
fabric.ElementsParser.prototype.createCallback = function(index, el) {
var _this = this;
return function(obj) {
_this.resolveGradient(obj, "fill");
_this.resolveGradient(obj, "stroke");
obj._removeTransformMatrix();
if (obj instanceof fabric.Image) {
obj.parsePreserveAspectRatioAttribute(el);
}
_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, this.elements);
}
};
(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 = {
type: "point",
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) {
if (typeof t === "undefined") {
t = .5;
}
t = Math.max(Math.min(1, t), 0);
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 this.lerp(that);
},
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;
return this;
},
setX: function(x) {
this.x = x;
return this;
},
setY: function(y) {
this.y = y;
return this;
},
setFromPoint: function(that) {
this.x = that.x;
this.y = that.y;
return this;
},
swap: function(that) {
var x = this.x, y = this.y;
this.x = that.x;
this.y = that.y;
that.x = x;
that.y = y;
},
clone: function() {
return new Point(this.x, this.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 = {
constructor: Intersection,
appendPoint: function(point) {
this.points.push(point);
return this;
},
appendPoints: function(points) {
this.points = this.points.concat(points);
return this;
}
};
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.appendPoint(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, b1, b2, inter;
for (var i = 0; i < length; i++) {
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") {
source = [ 255, 255, 255, 0 ];
}
if (!source) {
source = Color.sourceFromHex(color);
}
if (!source) {
source = Color.sourceFromRgb(color);
}
if (!source) {
source = Color.sourceFromHsl(color);
}
if (!source) {
source = [ 0, 0, 0, 1 ];
}
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();
},
toHexa: function() {
var source = this.getSource(), a;
a = source[3] * 255;
a = a.toString(16);
a = a.length === 1 ? "0" + a : a;
return this.toHex() + a.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]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
fabric.Color.colorNameMap = {
aqua: "#00FFFF",
black: "#000000",
blue: "#0000FF",
fuchsia: "#FF00FF",
gray: "#808080",
grey: "#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 || value.length === 4, isRGBa = value.length === 8 || value.length === 4, 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), a = isRGBa ? isShortNotation ? value.charAt(3) + value.charAt(3) : value.substring(6, 8) : "FF";
return [ parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), parseFloat((parseInt(a, 16) / 255).toFixed(2)) ];
}
};
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") || 0, 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%"
};
}
var clone = fabric.util.object.clone;
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(colorStops) {
for (var position in colorStops) {
var color = new fabric.Color(colorStops[position]);
this.colorStops.push({
offset: parseFloat(position),
color: color.toRgb(),
opacity: color.getAlpha()
});
}
return this;
},
toObject: function(propertiesToInclude) {
var object = {
type: this.type,
coords: this.coords,
colorStops: this.colorStops,
offsetX: this.offsetX,
offsetY: this.offsetY,
gradientTransform: this.gradientTransform ? this.gradientTransform.concat() : this.gradientTransform
};
fabric.util.populateWithProperties(this, object, propertiesToInclude);
return object;
},
toSVG: function(object) {
var coords = clone(this.coords, true), markup, commonAttributes, colorStops = clone(this.colorStops, true), needsSwap = coords.r1 > coords.r2;
colorStops.sort(function(a, b) {
return a.offset - b.offset;
});
for (var 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;
}
}
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' ];
}
if (this.type === "radial") {
if (needsSwap) {
colorStops = colorStops.concat();
colorStops.reverse();
for (var i = 0; i < colorStops.length; i++) {
colorStops[i].offset = 1 - colorStops[i].offset;
}
}
var minRadius = Math.min(coords.r1, coords.r2);
if (minRadius > 0) {
var maxRadius = Math.max(coords.r1, coords.r2), percentageShift = minRadius / maxRadius;
for (var i = 0; i < colorStops.length; i++) {
colorStops[i].offset += percentageShift * (1 - colorStops[i].offset);
}
}
}
for (var i = 0; i < colorStops.length; i++) {
var colorStop = colorStops[i];
markup.push("\n');
}
markup.push(this.type === "linear" ? "\n" : "\n");
return markup.join("");
},
toLive: function(ctx) {
var gradient, coords = fabric.util.object.clone(this.coords);
if (!this.type) {
return;
}
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(offset, color);
}
return gradient;
}
});
fabric.util.object.extend(fabric.Gradient, {
fromElement: function(el, instance) {
var colorStopEls = el.getElementsByTagName("stop"), type, gradientUnits = el.getAttribute("gradientUnits") || "objectBoundingBox", gradientTransform = el.getAttribute("gradientTransform"), colorStops = [], coords, ellipseMatrix;
if (el.nodeName === "linearGradient" || el.nodeName === "LINEARGRADIENT") {
type = "linear";
} else {
type = "radial";
}
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) {
if (options[prop] === "Infinity") {
options[prop] = 1;
} else if (options[prop] === "-Infinity") {
options[prop] = 0;
}
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;
}
})();
(function() {
"use strict";
var toFixed = fabric.util.toFixed;
fabric.Pattern = fabric.util.createClass({
repeat: "repeat",
offsetX: 0,
offsetY: 0,
initialize: function(options, callback) {
options || (options = {});
this.id = fabric.Object.__uid++;
this.setOptions(options);
if (!options.source || options.source && typeof options.source !== "string") {
callback && callback(this);
return;
}
if (typeof fabric.util.getFunctionBody(options.source) !== "undefined") {
this.source = new Function(fabric.util.getFunctionBody(options.source));
callback && callback(this);
} else {
var _this = this;
this.source = fabric.util.createImage();
fabric.util.loadImage(options.source, function(img) {
_this.source = img;
callback && callback(_this);
});
}
},
toObject: function(propertiesToInclude) {
var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, source, object;
if (typeof this.source === "function") {
source = String(this.source);
} else if (typeof this.source.src === "string") {
source = this.source.src;
} else if (typeof this.source === "object" && this.source.toDataURL) {
source = this.source.toDataURL();
}
object = {
type: "pattern",
source: source,
repeat: this.repeat,
offsetX: toFixed(this.offsetX, NUM_FRACTION_DIGITS),
offsetY: toFixed(this.offsetY, NUM_FRACTION_DIGITS)
};
fabric.util.populateWithProperties(this, object, propertiesToInclude);
return object;
},
toSVG: function(object) {
var patternSource = typeof this.source === "function" ? this.source() : this.source, patternWidth = patternSource.width / object.width, patternHeight = patternSource.height / object.height, patternOffsetX = this.offsetX / object.width, patternOffsetY = this.offsetY / object.height, 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";
},
setOptions: function(options) {
for (var prop in options) {
this[prop] = options[prop];
}
},
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 fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, offset = fabric.util.rotateVector({
x: this.offsetX,
y: this.offsetY
}, fabric.util.degreesToRadians(-object.angle)), BLUR_BOX = 20;
if (object.width && object.height) {
fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
}
if (object.flipX) {
offset.x *= -1;
}
if (object.flipY) {
offset.y *= -1;
}
return '\n" + '\t\n' + '\t\n' + '\t\n' + '\t\n' + "\t\n" + "\t\t\n" + '\t\t\n' + "\t\n" + "\n";
},
toObject: function() {
if (this.includeDefaultValues) {
return {
color: this.color,
blur: this.blur,
offsetX: this.offsetX,
offsetY: this.offsetY,
affectStroke: this.affectStroke
};
}
var obj = {}, proto = fabric.Shadow.prototype;
[ "color", "blur", "offsetX", "offsetY", "affectStroke" ].forEach(function(prop) {
if (this[prop] !== proto[prop]) {
obj[prop] = this[prop];
}
}, this);
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, toFixed = fabric.util.toFixed, transformPoint = fabric.util.transformPoint, invertTransform = fabric.util.invertTransform, CANVAS_INIT_ERROR = new Error("Could not initialize `canvas` element");
fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, {
initialize: function(el, options) {
options || (options = {});
this.renderAndResetBound = this.renderAndReset.bind(this);
this.requestRenderAllBound = this.requestRenderAll.bind(this);
this._initStatic(el, options);
},
backgroundColor: "",
backgroundImage: null,
overlayColor: "",
overlayImage: null,
includeDefaultValues: true,
stateful: false,
renderOnAddRemove: true,
clipTo: null,
controlsAboveOverlay: false,
allowTouchScrolling: false,
imageSmoothingEnabled: true,
viewportTransform: fabric.iMatrix.concat(),
backgroundVpt: true,
overlayVpt: true,
onBeforeScaleRotate: function() {},
enableRetinaScaling: true,
vptCoords: {},
skipOffscreen: true,
_initStatic: function(el, options) {
var cb = this.requestRenderAllBound;
this._objects = [];
this._createLowerCanvas(el);
this._initOptions(options);
this._setImageSmoothing();
if (!this.interactive) {
this._initRetinaScaling();
}
if (options.overlayImage) {
this.setOverlayImage(options.overlayImage, cb);
}
if (options.backgroundImage) {
this.setBackgroundImage(options.backgroundImage, cb);
}
if (options.backgroundColor) {
this.setBackgroundColor(options.backgroundColor, cb);
}
if (options.overlayColor) {
this.setOverlayColor(options.overlayColor, cb);
}
this.calcOffset();
},
_isRetinaScaling: function() {
return fabric.devicePixelRatio !== 1 && this.enableRetinaScaling;
},
getRetinaScaling: function() {
return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
},
_initRetinaScaling: function() {
if (!this._isRetinaScaling()) {
return;
}
this.lowerCanvasEl.setAttribute("width", this.width * fabric.devicePixelRatio);
this.lowerCanvasEl.setAttribute("height", this.height * fabric.devicePixelRatio);
this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
},
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 = ctx.imageSmoothingEnabled || ctx.webkitImageSmoothingEnabled || ctx.mozImageSmoothingEnabled || ctx.msImageSmoothingEnabled || ctx.oImageSmoothingEnabled;
ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
},
__setBgOverlayImage: function(property, image, callback, options) {
if (typeof image === "string") {
fabric.util.loadImage(image, function(img) {
img && (this[property] = new fabric.Image(img, options));
callback && callback(img);
}, this, options && options.crossOrigin);
} else {
options && image.setOptions(options);
this[property] = image;
callback && callback(image);
}
return this;
},
__setBgOverlayColor: function(property, color, callback) {
this[property] = color;
this._initGradient(color, property);
this._initPattern(color, property, callback);
return this;
},
_createCanvasElement: function(canvasEl) {
var element = fabric.util.createCanvasElement(canvasEl);
if (!element.style) {
element.style = {};
}
if (!element) {
throw CANVAS_INIT_ERROR;
}
if (typeof element.getContext === "undefined") {
throw CANVAS_INIT_ERROR;
}
return element;
},
_initOptions: function(options) {
this._setOptions(options);
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(canvasEl);
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);
}
}
this._initRetinaScaling();
this._setImageSmoothing();
this.calcOffset();
if (!options.cssOnly) {
this.requestRenderAll();
}
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 this.viewportTransform[0];
},
setViewportTransform: function(vpt) {
var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true;
this.viewportTransform = vpt;
for (var i = 0, len = this._objects.length; i < len; i++) {
object = this._objects[i];
object.group || object.setCoords(ignoreVpt, skipAbsolute);
}
if (activeObject && activeObject.type === "activeSelection") {
activeObject.setCoords(ignoreVpt, skipAbsolute);
}
this.calcViewportBoundaries();
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
zoomToPoint: function(point, value) {
var before = point, vpt = this.viewportTransform.slice(0);
point = transformPoint(point, invertTransform(this.viewportTransform));
vpt[0] = value;
vpt[3] = value;
var after = transformPoint(point, vpt);
vpt[4] += before.x - after.x;
vpt[5] += before.y - after.y;
return this.setViewportTransform(vpt);
},
setZoom: function(value) {
this.zoomToPoint(new fabric.Point(0, 0), value);
return this;
},
absolutePan: function(point) {
var vpt = this.viewportTransform.slice(0);
vpt[4] = -point.x;
vpt[5] = -point.y;
return this.setViewportTransform(vpt);
},
relativePan: function(point) {
return this.absolutePan(new fabric.Point(-point.x - this.viewportTransform[4], -point.y - this.viewportTransform[5]));
},
getElement: function() {
return this.lowerCanvasEl;
},
_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) {
this.fire("object:removed", {
target: obj
});
obj.fire("removed");
delete obj.canvas;
},
clearContext: function(ctx) {
ctx.clearRect(0, 0, this.width, this.height);
return this;
},
getContext: function() {
return this.contextContainer;
},
clear: function() {
this._objects.length = 0;
this.backgroundImage = null;
this.overlayImage = null;
this.backgroundColor = "";
this.overlayColor = "";
if (this._hasITextHandlers) {
this.off("mouse:up", this._mouseUpITextHandler);
this._iTextInstances = null;
this._hasITextHandlers = false;
}
this.clearContext(this.contextContainer);
this.fire("canvas:cleared");
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
renderAll: function() {
var canvasToDrawOn = this.contextContainer;
this.renderCanvas(canvasToDrawOn, this._objects);
return this;
},
renderAndReset: function() {
this.renderAll();
this.isRendering = false;
},
requestRenderAll: function() {
if (!this.isRendering) {
this.isRendering = true;
fabric.util.requestAnimFrame(this.renderAndResetBound);
}
return this;
},
calcViewportBoundaries: function() {
var points = {}, width = this.width, height = this.height, iVpt = invertTransform(this.viewportTransform);
points.tl = transformPoint({
x: 0,
y: 0
}, iVpt);
points.br = transformPoint({
x: width,
y: height
}, iVpt);
points.tr = new fabric.Point(points.br.x, points.tl.y);
points.bl = new fabric.Point(points.tl.x, points.br.y);
this.vptCoords = points;
return points;
},
renderCanvas: function(ctx, objects) {
this.calcViewportBoundaries();
this.clearContext(ctx);
this.fire("before:render");
if (this.clipTo) {
fabric.util.clipContext(this, ctx);
}
this._renderBackground(ctx);
ctx.save();
ctx.transform.apply(ctx, this.viewportTransform);
this._renderObjects(ctx, objects);
ctx.restore();
if (!this.controlsAboveOverlay && this.interactive) {
this.drawControls(ctx);
}
if (this.clipTo) {
ctx.restore();
}
this._renderOverlay(ctx);
if (this.controlsAboveOverlay && this.interactive) {
this.drawControls(ctx);
}
this.fire("after:render");
},
_renderObjects: function(ctx, objects) {
for (var i = 0, length = objects.length; i < length; ++i) {
objects[i] && objects[i].render(ctx);
}
},
_renderBackgroundOrOverlay: function(ctx, property) {
var object = this[property + "Color"];
if (object) {
ctx.fillStyle = object.toLive ? object.toLive(ctx, this) : object;
ctx.fillRect(object.offsetX || 0, object.offsetY || 0, this.width, this.height);
}
object = this[property + "Image"];
if (object) {
if (this[property + "Vpt"]) {
ctx.save();
ctx.transform.apply(ctx, this.viewportTransform);
}
object.render(ctx);
this[property + "Vpt"] && ctx.restore();
}
},
_renderBackground: function(ctx) {
this._renderBackgroundOrOverlay(ctx, "background");
},
_renderOverlay: function(ctx) {
this._renderBackgroundOrOverlay(ctx, "overlay");
},
getCenter: function() {
return {
top: this.height / 2,
left: this.width / 2
};
},
centerObjectH: function(object) {
return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
},
centerObjectV: function(object) {
return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
},
centerObject: function(object) {
var center = this.getCenter();
return this._centerObject(object, new fabric.Point(center.left, center.top));
},
viewportCenterObject: function(object) {
var vpCenter = this.getVpCenter();
return this._centerObject(object, vpCenter);
},
viewportCenterObjectH: function(object) {
var vpCenter = this.getVpCenter();
this._centerObject(object, new fabric.Point(vpCenter.x, object.getCenterPoint().y));
return this;
},
viewportCenterObjectV: function(object) {
var vpCenter = this.getVpCenter();
return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, vpCenter.y));
},
getVpCenter: function() {
var center = this.getCenter(), iVpt = invertTransform(this.viewportTransform);
return transformPoint({
x: center.left,
y: center.top
}, iVpt);
},
_centerObject: function(object, center) {
object.setPositionByOrigin(center, "center", "center");
this.requestRenderAll();
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(methodName, propertiesToInclude));
fabric.util.populateWithProperties(this, data, propertiesToInclude);
return data;
},
_toObjects: function(methodName, propertiesToInclude) {
return this.getObjects().filter(function(object) {
return !object.excludeFromExport;
}).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 object = instance[methodName](propertiesToInclude);
if (!this.includeDefaultValues) {
instance.includeDefaultValues = originalValue;
}
return object;
},
__serializeBgOverlay: function(methodName, propertiesToInclude) {
var data = {}, bgImage = this.backgroundImage, overlay = this.overlayImage;
if (this.backgroundColor) {
data.background = this.backgroundColor.toObject ? this.backgroundColor.toObject(propertiesToInclude) : this.backgroundColor;
}
if (this.overlayColor) {
data.overlay = this.overlayColor.toObject ? this.overlayColor.toObject(propertiesToInclude) : this.overlayColor;
}
if (bgImage && !bgImage.excludeFromExport) {
data.backgroundImage = this._toObject(bgImage, methodName, propertiesToInclude);
}
if (overlay && !overlay.excludeFromExport) {
data.overlayImage = this._toObject(overlay, methodName, propertiesToInclude);
}
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", reviver);
this._setSVGObjects(markup, reviver);
this._setSVGBgOverlayColor(markup, "overlayColor");
this._setSVGBgOverlayImage(markup, "overlayImage", reviver);
markup.push("");
return markup.join("");
},
_setSVGPreamble: function(markup, options) {
if (options.suppressPreamble) {
return;
}
markup.push('\n', '\n');
},
_setSVGHeader: function(markup, options) {
var width = options.width || this.width, height = options.height || this.height, vpt, viewBox = 'viewBox="0 0 ' + this.width + " " + this.height + '" ', NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
if (options.viewBox) {
viewBox = 'viewBox="' + options.viewBox.x + " " + options.viewBox.y + " " + options.viewBox.width + " " + options.viewBox.height + '" ';
} else {
if (this.svgViewportTransformation) {
vpt = this.viewportTransform;
viewBox = 'viewBox="' + toFixed(-vpt[4] / vpt[0], NUM_FRACTION_DIGITS) + " " + toFixed(-vpt[5] / vpt[3], NUM_FRACTION_DIGITS) + " " + toFixed(this.width / vpt[0], NUM_FRACTION_DIGITS) + " " + toFixed(this.height / vpt[3], NUM_FRACTION_DIGITS) + '" ';
}
}
markup.push("