UNPKG

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
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, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); } 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