fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1,286 lines (1,257 loc) • 529 kB
JavaScript
var fabric = fabric || {
version: "1.6.4"
};
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("<!DOCTYPE html><html><head></head><body></body></html>");
if (fabric.document.createWindow) {
fabric.window = fabric.document.createWindow();
} else {
fabric.window = fabric.document.parentWindow;
}
}
fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
fabric.isLikelyNode = typeof Buffer !== "undefined" && typeof window === "undefined";
fabric.SHARED_ATTRIBUTES = [ "display", "transform", "fill", "fill-opacity", "fill-rule", "opacity", "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "id" ];
fabric.DPI = 96;
fabric.reNum = "(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)";
fabric.fontPaths = {};
fabric.charWidthsCache = {};
fabric.devicePixelRatio = fabric.window.devicePixelRatio || fabric.window.webkitDevicePixelRatio || fabric.window.mozDevicePixelRatio || 1;
(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.renderAll();
return this;
},
insertAt: function(object, index, nonSplicing) {
var objects = this.getObjects();
if (nonSplicing) {
objects[index] = object;
} else {
objects.splice(index, 0, object);
}
this._onObjectAdded && this._onObjectAdded(object);
this.renderOnAddRemove && this.renderAll();
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.renderAll();
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);
}
};
(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, obj = global || fabric.window;
for (var i = 0; i < len; ++i) {
obj = obj[parts[i]];
}
return obj;
},
loadImage: function(url, callback, context, crossOrigin) {
if (!url) {
callback && callback.call(context, url);
return;
}
var img = fabric.util.createImage();
img.onload = function() {
callback && callback.call(context, img);
img = img.onload = img.onerror = null;
};
img.onerror = function() {
fabric.log("Error loading " + img.src);
callback && callback.call(context, null, true);
img = img.onload = img.onerror = null;
};
if (url.indexOf("data") !== 0 && 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);
if (klass.async) {
klass.fromObject(o, function(obj, error) {
if (!error) {
enlivenedObjects[index] = obj;
reviver && reviver(o, enlivenedObjects[index]);
}
onLoaded();
});
} else {
enlivenedObjects[index] = klass.fromObject(o);
reviver && reviver(o, enlivenedObjects[index]);
onLoaded();
}
});
},
groupSVGElements: function(elements, options, path) {
var object;
object = new fabric.PathGroup(elements, options);
if (typeof path !== "undefined") {
object.setSourcePath(path);
}
return object;
},
populateWithProperties: function(source, destination, properties) {
if (properties && Object.prototype.toString.call(properties) === "[object Array]") {
for (var i = 0, len = properties.length; i < len; i++) {
if (properties[i] in source) {
destination[properties[i]] = source[properties[i]];
}
}
}
},
drawDashedLine: function(ctx, x, y, x2, y2, da) {
var dx = x2 - x, dy = y2 - y, len = sqrt(dx * dx + dy * dy), rot = atan2(dy, dx), dc = da.length, di = 0, draw = true;
ctx.save();
ctx.translate(x, y);
ctx.moveTo(0, 0);
ctx.rotate(rot);
x = 0;
while (len > x) {
x += da[di++ % dc];
if (x > len) {
x = len;
}
ctx[draw ? "lineTo" : "moveTo"](x, 0);
draw = !draw;
}
ctx.restore();
},
createCanvasElement: function(canvasEl) {
canvasEl || (canvasEl = fabric.document.createElement("canvas"));
if (!canvasEl.getContext && typeof G_vmlCanvasManager !== "undefined") {
G_vmlCanvasManager.initElement(canvasEl);
}
return canvasEl;
},
createImage: function() {
return fabric.isLikelyNode ? new (require("canvas").Image)() : fabric.document.createElement("img");
},
createAccessors: function(klass) {
var proto = klass.prototype;
for (var i = proto.stateProperties.length; i--; ) {
var propName = proto.stateProperties[i], capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1), setterName = "set" + capitalizedPropName, getterName = "get" + capitalizedPropName;
if (!proto[getterName]) {
proto[getterName] = function(property) {
return new Function('return this.get("' + property + '")');
}(propName);
}
if (!proto[setterName]) {
proto[setterName] = function(property) {
return new Function("value", 'return this.set("' + property + '", value)');
}(propName);
}
}
},
clipContext: function(receiver, ctx) {
ctx.save();
ctx.beginPath();
receiver.clipTo(ctx);
ctx.clip();
},
multiplyTransformMatrices: function(a, b, 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, imageData = ctx.getImageData(x, y, tolerance * 2 || 1, tolerance * 2 || 1);
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
var temp = imageData.data[i];
_isTransparent = temp <= 0;
if (_isTransparent === false) {
break;
}
}
imageData = null;
return _isTransparent;
},
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];
}
}
};
})(typeof exports !== "undefined" ? exports : this);
(function() {
var arcToSegmentsCache = {}, segmentToBezierCache = {}, boundsOfCurveCache = {}, _join = Array.prototype.join;
function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
var argsString = _join.call(arguments);
if (arcToSegmentsCache[argsString]) {
return arcToSegmentsCache[argsString];
}
var PI = Math.PI, th = rotateX * PI / 180, sinTh = Math.sin(th), cosTh = Math.cos(th), fromX = 0, fromY = 0;
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = -cosTh * toX * .5 - sinTh * toY * .5, py = -cosTh * toY * .5 + sinTh * toX * .5, rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px, pl = rx2 * ry2 - rx2 * py2 - ry2 * px2, root = 0;
if (pl < 0) {
var s = Math.sqrt(1 - pl / (rx2 * ry2));
rx *= s;
ry *= s;
} else {
root = (large === sweep ? -1 : 1) * Math.sqrt(pl / (rx2 * py2 + ry2 * px2));
}
var cx = root * rx * py / ry, cy = -root * ry * px / rx, cx1 = cosTh * cx - sinTh * cy + toX * .5, cy1 = sinTh * cx + cosTh * cy + toY * .5, mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry), dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
if (sweep === 0 && dtheta > 0) {
dtheta -= 2 * PI;
} else if (sweep === 1 && dtheta < 0) {
dtheta += 2 * PI;
}
var segments = Math.ceil(Math.abs(dtheta / PI * 2)), result = [], mDelta = dtheta / segments, mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2), th3 = mTheta + mDelta;
for (var i = 0; i < segments; i++) {
result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
fromX = result[i][4];
fromY = result[i][5];
mTheta = th3;
th3 += mDelta;
}
arcToSegmentsCache[argsString] = result;
return result;
}
function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
var argsString2 = _join.call(arguments);
if (segmentToBezierCache[argsString2]) {
return segmentToBezierCache[argsString2];
}
var costh2 = Math.cos(th2), sinth2 = Math.sin(th2), costh3 = Math.cos(th3), sinth3 = Math.sin(th3), toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1, toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1, cp1X = fromX + mT * (-cosTh * rx * sinth2 - sinTh * ry * costh2), cp1Y = fromY + mT * (-sinTh * rx * sinth2 + cosTh * ry * costh2), cp2X = toX + mT * (cosTh * rx * sinth3 + sinTh * ry * costh3), cp2Y = toY + mT * (sinTh * rx * sinth3 - cosTh * ry * costh3);
segmentToBezierCache[argsString2] = [ cp1X, cp1Y, cp2X, cp2Y, toX, toY ];
return segmentToBezierCache[argsString2];
}
function calcVectorAngle(ux, uy, vx, vy) {
var ta = Math.atan2(uy, ux), tb = Math.atan2(vy, vx);
if (tb >= ta) {
return tb - ta;
} else {
return 2 * Math.PI - (ta - tb);
}
}
fabric.util.drawArc = function(ctx, fx, fy, coords) {
var rx = coords[0], ry = coords[1], rot = coords[2], large = coords[3], sweep = coords[4], tx = coords[5], ty = coords[6], segs = [ [], [], [], [] ], segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
for (var i = 0, len = segsNorm.length; i < len; i++) {
segs[i][0] = segsNorm[i][0] + fx;
segs[i][1] = segsNorm[i][1] + fy;
segs[i][2] = segsNorm[i][2] + fx;
segs[i][3] = segsNorm[i][3] + fy;
segs[i][4] = segsNorm[i][4] + fx;
segs[i][5] = segsNorm[i][5] + fy;
ctx.bezierCurveTo.apply(ctx, segs[i]);
}
};
fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
var fromX = 0, fromY = 0, bound = [], bounds = [], segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot), boundCopy = [ [], [] ];
for (var i = 0, len = segs.length; i < len; i++) {
bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
boundCopy[0].x = bound[0].x + fx;
boundCopy[0].y = bound[0].y + fy;
boundCopy[1].x = bound[1].x + fx;
boundCopy[1].y = bound[1].y + fy;
bounds.push(boundCopy[0]);
bounds.push(boundCopy[1]);
fromX = segs[i][4];
fromY = segs[i][5];
}
return bounds;
};
function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
var argsString = _join.call(arguments);
if (boundsOfCurveCache[argsString]) {
return boundsOfCurveCache[argsString];
}
var sqrt = Math.sqrt, min = Math.min, max = Math.max, abs = Math.abs, tvalues = [], bounds = [ [], [] ], a, b, c, t, t1, t2, b2ac, sqrtb2ac;
b = 6 * x0 - 12 * x1 + 6 * x2;
a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
c = 3 * x1 - 3 * x0;
for (var i = 0; i < 2; ++i) {
if (i > 0) {
b = 6 * y0 - 12 * y1 + 6 * y2;
a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
c = 3 * y1 - 3 * y0;
}
if (abs(a) < 1e-12) {
if (abs(b) < 1e-12) {
continue;
}
t = -c / b;
if (0 < t && t < 1) {
tvalues.push(t);
}
continue;
}
b2ac = b * b - 4 * c * a;
if (b2ac < 0) {
continue;
}
sqrtb2ac = sqrt(b2ac);
t1 = (-b + sqrtb2ac) / (2 * a);
if (0 < t1 && t1 < 1) {
tvalues.push(t1);
}
t2 = (-b - sqrtb2ac) / (2 * a);
if (0 < t2 && t2 < 1) {
tvalues.push(t2);
}
}
var x, y, j = tvalues.length, jlen = j, mt;
while (j--) {
t = tvalues[j];
mt = 1 - t;
x = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
bounds[0][j] = x;
y = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
bounds[1][j] = y;
}
bounds[0][jlen] = x0;
bounds[1][jlen] = y0;
bounds[0][jlen + 1] = x3;
bounds[1][jlen + 1] = y3;
var result = [ {
x: min.apply(null, bounds[0]),
y: min.apply(null, bounds[1])
}, {
x: max.apply(null, bounds[0]),
y: max.apply(null, bounds[1])
} ];
boundsOfCurveCache[argsString] = result;
return result;
}
fabric.util.getBoundsOfCurve = getBoundsOfCurve;
})();
(function() {
var slice = Array.prototype.slice;
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement) {
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this), len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n !== n) {
n = 0;
} else if (n !== 0 && n !== Number.POSITIVE_INFINITY && n !== Number.NEGATIVE_INFINITY) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (;k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
fn.call(context, this[i], i, this);
}
}
};
}
if (!Array.prototype.map) {
Array.prototype.map = function(fn, context) {
var result = [];
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
result[i] = fn.call(context, this[i], i, this);
}
}
return result;
};
}
if (!Array.prototype.every) {
Array.prototype.every = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && !fn.call(context, this[i], i, this)) {
return false;
}
}
return true;
};
}
if (!Array.prototype.some) {
Array.prototype.some = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && fn.call(context, this[i], i, this)) {
return true;
}
}
return false;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function(fn, context) {
var result = [], val;
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
val = this[i];
if (fn.call(context, val, i, this)) {
result.push(val);
}
}
}
return result;
};
}
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(fn) {
var len = this.length >>> 0, i = 0, rv;
if (arguments.length > 1) {
rv = arguments[1];
} else {
do {
if (i in this) {
rv = this[i++];
break;
}
if (++i >= len) {
throw new TypeError();
}
} while (true);
}
for (;i < len; i++) {
if (i in this) {
rv = fn.call(null, rv, this[i], i, this);
}
}
return rv;
};
}
function invoke(array, method) {
var args = slice.call(arguments, 2), result = [];
for (var i = 0, len = array.length; i < len; i++) {
result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
}
return result;
}
function max(array, byProperty) {
return find(array, byProperty, function(value1, value2) {
return value1 >= value2;
});
}
function min(array, byProperty) {
return find(array, byProperty, function(value1, value2) {
return value1 < value2;
});
}
function 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) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
function clone(object) {
return extend({}, object);
}
fabric.util.object = {
extend: extend,
clone: clone
};
})();
(function() {
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, "");
};
}
function camelize(string) {
return string.replace(/-+(.)?/g, function(match, character) {
return character ? character.toUpperCase() : "";
});
}
function capitalize(string, firstLetterOnly) {
return string.charAt(0).toUpperCase() + (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
}
function escapeXml(string) {
return string.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
}
fabric.util.string = {
camelize: camelize,
capitalize: capitalize,
escapeXml: escapeXml
};
})();
(function() {
var slice = Array.prototype.slice, apply = Function.prototype.apply, Dummy = function() {};
if (!Function.prototype.bind) {
Function.prototype.bind = function(thisArg) {
var _this = this, args = slice.call(arguments, 1), bound;
if (args.length) {
bound = function() {
return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
};
} else {
bound = function() {
return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
};
}
Dummy.prototype = this.prototype;
bound.prototype = new Dummy();
return bound;
};
}
})();
(function() {
var slice = Array.prototype.slice, emptyFunction = function() {}, IS_DONTENUM_BUGGY = function() {
for (var p in {
toString: 1
}) {
if (p === "toString") {
return false;
}
}
return true;
}(), addMethods = function(klass, source, parent) {
for (var property in source) {
if (property in klass.prototype && typeof klass.prototype[property] === "function" && (source[property] + "").indexOf("callSuper") > -1) {
klass.prototype[property] = function(property) {
return function() {
var superclass = this.constructor.superclass;
this.constructor.superclass = parent;
var returnValue = source[property].apply(this, arguments);
this.constructor.superclass = superclass;
if (property !== "initialize") {
return returnValue;
}
};
}(property);
} else {
klass.prototype[property] = source[property];
}
if (IS_DONTENUM_BUGGY) {
if (source.toString !== Object.prototype.toString) {
klass.prototype.toString = source.toString;
}
if (source.valueOf !== Object.prototype.valueOf) {
klass.prototype.valueOf = source.valueOf;
}
}
}
};
function Subclass() {}
function callSuper(methodName) {
var fn = this.constructor.superclass.prototype[methodName];
return arguments.length > 1 ? fn.apply(this, slice.call(arguments, 1)) : fn.call(this);
}
function createClass() {
var parent = null, properties = slice.call(arguments, 0);
if (typeof properties[0] === "function") {
parent = properties.shift();
}
function klass() {
this.initialize.apply(this, arguments);
}
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
Subclass.prototype = parent.prototype;
klass.prototype = new Subclass();
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++) {
addMethods(klass, properties[i], parent);
}
if (!klass.prototype.initialize) {
klass.prototype.initialize = emptyFunction;
}
klass.prototype.constructor = klass;
klass.prototype.callSuper = callSuper;
return klass;
}
fabric.util.createClass = createClass;
})();
(function() {
var unknown = "unknown";
function areHostMethods(object) {
var methodNames = Array.prototype.slice.call(arguments, 1), t, i, len = methodNames.length;
for (i = 0; i < len; i++) {
t = typeof object[methodNames[i]];
if (!/^(?:function|object|unknown)$/.test(t)) {
return false;
}
}
return true;
}
var getElement, setElement, getUniqueId = function() {
var uid = 0;
return function(element) {
return element.__uniqueID || (element.__uniqueID = "uniqueID__" + uid++);
};
}();
(function() {
var elements = {};
getElement = function(uid) {
return elements[uid];
};
setElement = function(uid, element) {
elements[uid] = element;
};
})();
function createListener(uid, handler) {
return {
handler: handler,
wrappedHandler: createWrappedHandler(uid, handler)
};
}
function createWrappedHandler(uid, handler) {
return function(e) {
handler.call(getElement(uid), e || fabric.window.event);
};
}
function createDispatcher(uid, eventName) {
return function(e) {
if (handlers[uid] && handlers[uid][eventName]) {
var handlersForEvent = handlers[uid][eventName];
for (var i = 0, len = handlersForEvent.length; i < len; i++) {
handlersForEvent[i].call(this, e || fabric.window.event);
}
}
};
}
var shouldUseAddListenerRemoveListener = areHostMethods(fabric.document.documentElement, "addEventListener", "removeEventListener") && areHostMethods(fabric.window, "addEventListener", "removeEventListener"), shouldUseAttachEventDetachEvent = areHostMethods(fabric.document.documentElement, "attachEvent", "detachEvent") && areHostMethods(fabric.window, "attachEvent", "detachEvent"), listeners = {}, handlers = {}, addListener, removeListener;
if (shouldUseAddListenerRemoveListener) {
addListener = function(element, eventName, handler) {
element.addEventListener(eventName, handler, false);
};
removeListener = function(element, eventName, handler) {
element.removeEventListener(eventName, handler, false);
};
} else if (shouldUseAttachEventDetachEvent) {
addListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
setElement(uid, element);
if (!listeners[uid]) {
listeners[uid] = {};
}
if (!listeners[uid][eventName]) {
listeners[uid][eventName] = [];
}
var listener = createListener(uid, handler);
listeners[uid][eventName].push(listener);
element.attachEvent("on" + eventName, listener.wrappedHandler);
};
removeListener = function(element, eventName, handler) {
var uid = getUniqueId(element), listener;
if (listeners[uid] && listeners[uid][eventName]) {
for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
listener = listeners[uid][eventName][i];
if (listener && listener.handler === handler) {
element.detachEvent("on" + eventName, listener.wrappedHandler);
listeners[uid][eventName][i] = null;
}
}
}
};
} else {
addListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
if (!handlers[uid]) {
handlers[uid] = {};
}
if (!handlers[uid][eventName]) {
handlers[uid][eventName] = [];
var existingHandler = element["on" + eventName];
if (existingHandler) {
handlers[uid][eventName].push(existingHandler);
}
element["on" + eventName] = createDispatcher(uid, eventName);
}
handlers[uid][eventName].push(handler);
};
removeListener = function(element, eventName, handler) {
var uid = getUniqueId(element);
if (handlers[uid] && handlers[uid][eventName]) {
var handlersForEvent = handlers[uid][eventName];
for (var i = 0, len = handlersForEvent.length; i < len; i++) {
if (handlersForEvent[i] === handler) {
handlersForEvent.splice(i, 1);
}
}
}
};
}
fabric.util.addListener = addListener;
fabric.util.removeListener = removeListener;
function getPointer(event) {
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 typeof event.clientX !== unknown ? event.clientX : 0;
}, pointerY = function(event) {
return typeof event.clientY !== unknown ? event.clientY : 0;
};
function _getPointer(event, pageProp, clientProp) {
var touchProp = event.type === "touchend" ? "changedTouches" : "touches";
return event[touchProp] && event[touchProp][0] ? event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]) || event[clientProp] : event[clientProp];
}
if (fabric.isTouchSupported) {
pointerX = function(event) {
return _getPointer(event, "pageX", "clientX");
};
pointerY = function(event) {
return _getPointer(event, "pageY", "clientY");
};
}
fabric.util.getPointer = getPointer;
fabric.util.object.extend(fabric.util, fabric.Observable);
})();
(function() {
function setStyle(element, styles) {
var elementStyle = element.style;
if (!elementStyle) {
return element;
}
if (typeof styles === "string") {
element.style.cssText += ";" + styles;
return styles.indexOf("opacity") > -1 ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
}
for (var property in styles) {
if (property === "opacity") {
setOpacity(element, styles[property]);
} else {
var normalizedProperty = property === "float" || property === "cssFloat" ? typeof elementStyle.styleFloat === "undefined" ? "cssFloat" : "styleFloat" : property;
elementStyle[normalizedProperty] = styles[property];
}
}
return element;
}
var parseEl = fabric.document.createElement("div"), supportsOpacity = typeof parseEl.style.opacity === "string", supportsFilters = typeof parseEl.style.filter === "string", reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/, setOpacity = function(element) {
return element;
};
if (supportsOpacity) {
setOpacity = function(element, value) {
element.style.opacity = value;
return element;
};
} else if (supportsFilters) {
setOpacity = function(element, value) {
var es = element.style;
if (element.currentStyle && !element.currentStyle.hasLayout) {
es.zoom = 1;
}
if (reOpacity.test(es.filter)) {
value = value >= .9999 ? "" : "alpha(opacity=" + value * 100 + ")";
es.filter = es.filter.replace(reOpacity, value);
} else {
es.filter += " alpha(opacity=" + value * 100 + ")";
}
return element;
};
}
fabric.util.setStyle = setStyle;
})();
(function() {
var _slice = Array.prototype.slice;
function getById(id) {
return typeof id === "string" ? fabric.document.getElementById(id) : id;
}
var sliceCanConvertNodelists, toArray = function(arrayLike) {
return _slice.call(arrayLike, 0);
};
try {
sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
} catch (err) {}
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
var arr = new Array(arrayLike.length), i = arrayLike.length;
while (i--) {
arr[i] = arrayLike[i];
}
return arr;
};
}
function makeElement(tagName, attributes) {
var el = fabric.document.createElement(tagName);
for (var prop in attributes) {
if (prop === "class") {
el.className = attributes[prop];
} else if (prop === "for") {
el.htmlFor = attributes[prop];
} else {
el.setAttribute(prop, attributes[prop]);
}
}
return el;
}
function addClass(element, className) {
if (element && (" " + element.className + " ").indexOf(" " + className + " ") === -1) {
element.className += (element.className ? " " : "") + className;
}
}
function wrapElement(element, wrapper, attributes) {
if (typeof wrapper === "string") {
wrapper = makeElement(wrapper, attributes);
}
if (element.parentNode) {
element.parentNode.replaceChild(wrapper, element);
}
wrapper.appendChild(element);
return wrapper;
}
function getScrollLeftTop(element) {
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("Micro