diff --git a/dist/html2canvas.js b/dist/html2canvas.js
deleted file mode 100644
index 43fc7a5..0000000
--- a/dist/html2canvas.js
+++ /dev/null
@@ -1,3519 +0,0 @@
-/*
- html2canvas 0.5.0-beta3
- Copyright (c) 2016 Niklas von Hertzen
-
- Released under License
-*/
-
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.html2canvas=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0x80 (not a basic code point)',
- 'invalid-input': 'Invalid input'
- },
-
- /** Convenience shortcuts */
- baseMinusTMin = base - tMin,
- floor = Math.floor,
- stringFromCharCode = String.fromCharCode,
-
- /** Temporary variable */
- key;
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * A generic error utility function.
- * @private
- * @param {String} type The error type.
- * @returns {Error} Throws a `RangeError` with the applicable error message.
- */
- function error(type) {
- throw RangeError(errors[type]);
- }
-
- /**
- * A generic `Array#map` utility function.
- * @private
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function that gets called for every array
- * item.
- * @returns {Array} A new array of values returned by the callback function.
- */
- function map(array, fn) {
- var length = array.length;
- while (length--) {
- array[length] = fn(array[length]);
- }
- return array;
- }
-
- /**
- * A simple `Array#map`-like wrapper to work with domain name strings.
- * @private
- * @param {String} domain The domain name.
- * @param {Function} callback The function that gets called for every
- * character.
- * @returns {Array} A new string of characters returned by the callback
- * function.
- */
- function mapDomain(string, fn) {
- return map(string.split(regexSeparators), fn).join('.');
- }
-
- /**
- * Creates an array containing the numeric code points of each Unicode
- * character in the string. While JavaScript uses UCS-2 internally,
- * this function will convert a pair of surrogate halves (each of which
- * UCS-2 exposes as separate characters) into a single code point,
- * matching UTF-16.
- * @see `punycode.ucs2.encode`
- * @see
- * @memberOf punycode.ucs2
- * @name decode
- * @param {String} string The Unicode input string (UCS-2).
- * @returns {Array} The new array of code points.
- */
- function ucs2decode(string) {
- var output = [],
- counter = 0,
- length = string.length,
- value,
- extra;
- while (counter < length) {
- value = string.charCodeAt(counter++);
- if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
- // high surrogate, and there is a next character
- extra = string.charCodeAt(counter++);
- if ((extra & 0xFC00) == 0xDC00) { // low surrogate
- output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
- } else {
- // unmatched surrogate; only append this code unit, in case the next
- // code unit is the high surrogate of a surrogate pair
- output.push(value);
- counter--;
- }
- } else {
- output.push(value);
- }
- }
- return output;
- }
-
- /**
- * Creates a string based on an array of numeric code points.
- * @see `punycode.ucs2.decode`
- * @memberOf punycode.ucs2
- * @name encode
- * @param {Array} codePoints The array of numeric code points.
- * @returns {String} The new Unicode string (UCS-2).
- */
- function ucs2encode(array) {
- return map(array, function(value) {
- var output = '';
- if (value > 0xFFFF) {
- value -= 0x10000;
- output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
- value = 0xDC00 | value & 0x3FF;
- }
- output += stringFromCharCode(value);
- return output;
- }).join('');
- }
-
- /**
- * Converts a basic code point into a digit/integer.
- * @see `digitToBasic()`
- * @private
- * @param {Number} codePoint The basic numeric code point value.
- * @returns {Number} The numeric value of a basic code point (for use in
- * representing integers) in the range `0` to `base - 1`, or `base` if
- * the code point does not represent a value.
- */
- function basicToDigit(codePoint) {
- if (codePoint - 48 < 10) {
- return codePoint - 22;
- }
- if (codePoint - 65 < 26) {
- return codePoint - 65;
- }
- if (codePoint - 97 < 26) {
- return codePoint - 97;
- }
- return base;
- }
-
- /**
- * Converts a digit/integer into a basic code point.
- * @see `basicToDigit()`
- * @private
- * @param {Number} digit The numeric value of a basic code point.
- * @returns {Number} The basic code point whose value (when used for
- * representing integers) is `digit`, which needs to be in the range
- * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
- * used; else, the lowercase form is used. The behavior is undefined
- * if `flag` is non-zero and `digit` has no uppercase form.
- */
- function digitToBasic(digit, flag) {
- // 0..25 map to ASCII a..z or A..Z
- // 26..35 map to ASCII 0..9
- return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
- }
-
- /**
- * Bias adaptation function as per section 3.4 of RFC 3492.
- * http://tools.ietf.org/html/rfc3492#section-3.4
- * @private
- */
- function adapt(delta, numPoints, firstTime) {
- var k = 0;
- delta = firstTime ? floor(delta / damp) : delta >> 1;
- delta += floor(delta / numPoints);
- for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
- delta = floor(delta / baseMinusTMin);
- }
- return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
- }
-
- /**
- * Converts a Punycode string of ASCII-only symbols to a string of Unicode
- * symbols.
- * @memberOf punycode
- * @param {String} input The Punycode string of ASCII-only symbols.
- * @returns {String} The resulting string of Unicode symbols.
- */
- function decode(input) {
- // Don't use UCS-2
- var output = [],
- inputLength = input.length,
- out,
- i = 0,
- n = initialN,
- bias = initialBias,
- basic,
- j,
- index,
- oldi,
- w,
- k,
- digit,
- t,
- /** Cached calculation results */
- baseMinusT;
-
- // Handle the basic code points: let `basic` be the number of input code
- // points before the last delimiter, or `0` if there is none, then copy
- // the first basic code points to the output.
-
- basic = input.lastIndexOf(delimiter);
- if (basic < 0) {
- basic = 0;
- }
-
- for (j = 0; j < basic; ++j) {
- // if it's not a basic code point
- if (input.charCodeAt(j) >= 0x80) {
- error('not-basic');
- }
- output.push(input.charCodeAt(j));
- }
-
- // Main decoding loop: start just after the last delimiter if any basic code
- // points were copied; start at the beginning otherwise.
-
- for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
-
- // `index` is the index of the next character to be consumed.
- // Decode a generalized variable-length integer into `delta`,
- // which gets added to `i`. The overflow checking is easier
- // if we increase `i` as we go, then subtract off its starting
- // value at the end to obtain `delta`.
- for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
-
- if (index >= inputLength) {
- error('invalid-input');
- }
-
- digit = basicToDigit(input.charCodeAt(index++));
-
- if (digit >= base || digit > floor((maxInt - i) / w)) {
- error('overflow');
- }
-
- i += digit * w;
- t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-
- if (digit < t) {
- break;
- }
-
- baseMinusT = base - t;
- if (w > floor(maxInt / baseMinusT)) {
- error('overflow');
- }
-
- w *= baseMinusT;
-
- }
-
- out = output.length + 1;
- bias = adapt(i - oldi, out, oldi == 0);
-
- // `i` was supposed to wrap around from `out` to `0`,
- // incrementing `n` each time, so we'll fix that now:
- if (floor(i / out) > maxInt - n) {
- error('overflow');
- }
-
- n += floor(i / out);
- i %= out;
-
- // Insert `n` at position `i` of the output
- output.splice(i++, 0, n);
-
- }
-
- return ucs2encode(output);
- }
-
- /**
- * Converts a string of Unicode symbols to a Punycode string of ASCII-only
- * symbols.
- * @memberOf punycode
- * @param {String} input The string of Unicode symbols.
- * @returns {String} The resulting Punycode string of ASCII-only symbols.
- */
- function encode(input) {
- var n,
- delta,
- handledCPCount,
- basicLength,
- bias,
- j,
- m,
- q,
- k,
- t,
- currentValue,
- output = [],
- /** `inputLength` will hold the number of code points in `input`. */
- inputLength,
- /** Cached calculation results */
- handledCPCountPlusOne,
- baseMinusT,
- qMinusT;
-
- // Convert the input in UCS-2 to Unicode
- input = ucs2decode(input);
-
- // Cache the length
- inputLength = input.length;
-
- // Initialize the state
- n = initialN;
- delta = 0;
- bias = initialBias;
-
- // Handle the basic code points
- for (j = 0; j < inputLength; ++j) {
- currentValue = input[j];
- if (currentValue < 0x80) {
- output.push(stringFromCharCode(currentValue));
- }
- }
-
- handledCPCount = basicLength = output.length;
-
- // `handledCPCount` is the number of code points that have been handled;
- // `basicLength` is the number of basic code points.
-
- // Finish the basic string - if it is not empty - with a delimiter
- if (basicLength) {
- output.push(delimiter);
- }
-
- // Main encoding loop:
- while (handledCPCount < inputLength) {
-
- // All non-basic code points < n have been handled already. Find the next
- // larger one:
- for (m = maxInt, j = 0; j < inputLength; ++j) {
- currentValue = input[j];
- if (currentValue >= n && currentValue < m) {
- m = currentValue;
- }
- }
-
- // Increase `delta` enough to advance the decoder's state to ,
- // but guard against overflow
- handledCPCountPlusOne = handledCPCount + 1;
- if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
- error('overflow');
- }
-
- delta += (m - n) * handledCPCountPlusOne;
- n = m;
-
- for (j = 0; j < inputLength; ++j) {
- currentValue = input[j];
-
- if (currentValue < n && ++delta > maxInt) {
- error('overflow');
- }
-
- if (currentValue == n) {
- // Represent delta as a generalized variable-length integer
- for (q = delta, k = base; /* no condition */; k += base) {
- t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
- if (q < t) {
- break;
- }
- qMinusT = q - t;
- baseMinusT = base - t;
- output.push(
- stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
- );
- q = floor(qMinusT / baseMinusT);
- }
-
- output.push(stringFromCharCode(digitToBasic(q, 0)));
- bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
- delta = 0;
- ++handledCPCount;
- }
- }
-
- ++delta;
- ++n;
-
- }
- return output.join('');
- }
-
- /**
- * Converts a Punycode string representing a domain name to Unicode. Only the
- * Punycoded parts of the domain name will be converted, i.e. it doesn't
- * matter if you call it on a string that has already been converted to
- * Unicode.
- * @memberOf punycode
- * @param {String} domain The Punycode domain name to convert to Unicode.
- * @returns {String} The Unicode representation of the given Punycode
- * string.
- */
- function toUnicode(domain) {
- return mapDomain(domain, function(string) {
- return regexPunycode.test(string)
- ? decode(string.slice(4).toLowerCase())
- : string;
- });
- }
-
- /**
- * Converts a Unicode string representing a domain name to Punycode. Only the
- * non-ASCII parts of the domain name will be converted, i.e. it doesn't
- * matter if you call it with a domain that's already in ASCII.
- * @memberOf punycode
- * @param {String} domain The domain name to convert, as a Unicode string.
- * @returns {String} The Punycode representation of the given domain name.
- */
- function toASCII(domain) {
- return mapDomain(domain, function(string) {
- return regexNonASCII.test(string)
- ? 'xn--' + encode(string)
- : string;
- });
- }
-
- /*--------------------------------------------------------------------------*/
-
- /** Define the public API */
- punycode = {
- /**
- * A string representing the current Punycode.js version number.
- * @memberOf punycode
- * @type String
- */
- 'version': '1.2.4',
- /**
- * An object of methods to convert from JavaScript's internal character
- * representation (UCS-2) to Unicode code points, and back.
- * @see
- * @memberOf punycode
- * @type Object
- */
- 'ucs2': {
- 'decode': ucs2decode,
- 'encode': ucs2encode
- },
- 'decode': decode,
- 'encode': encode,
- 'toASCII': toASCII,
- 'toUnicode': toUnicode
- };
-
- /** Expose `punycode` */
- // Some AMD build optimizers, like r.js, check for specific condition patterns
- // like the following:
- if (
- typeof define == 'function' &&
- typeof define.amd == 'object' &&
- define.amd
- ) {
- define('punycode', function() {
- return punycode;
- });
- } else if (freeExports && !freeExports.nodeType) {
- if (freeModule) { // in Node.js or RingoJS v0.8.0+
- freeModule.exports = punycode;
- } else { // in Narwhal or RingoJS v0.7.0-
- for (key in punycode) {
- punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
- }
- }
- } else { // in Rhino or a web browser
- root.punycode = punycode;
- }
-
-}(this));
-
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}],2:[function(_dereq_,module,exports){
-var log = _dereq_('./log');
-
-function restoreOwnerScroll(ownerDocument, x, y) {
- if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
- ownerDocument.defaultView.scrollTo(x, y);
- }
-}
-
-function cloneCanvasContents(canvas, clonedCanvas) {
- try {
- if (clonedCanvas) {
- clonedCanvas.width = canvas.width;
- clonedCanvas.height = canvas.height;
- clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0);
- }
- } catch(e) {
- log("Unable to copy canvas content from", canvas, e);
- }
-}
-
-function cloneNode(node, javascriptEnabled) {
- var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
-
- var child = node.firstChild;
- while(child) {
- if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
- clone.appendChild(cloneNode(child, javascriptEnabled));
- }
- child = child.nextSibling;
- }
-
- if (node.nodeType === 1) {
- clone._scrollTop = node.scrollTop;
- clone._scrollLeft = node.scrollLeft;
- if (node.nodeName === "CANVAS") {
- cloneCanvasContents(node, clone);
- } else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") {
- clone.value = node.value;
- }
- }
-
- return clone;
-}
-
-function initNode(node) {
- if (node.nodeType === 1) {
- node.scrollTop = node._scrollTop;
- node.scrollLeft = node._scrollLeft;
-
- var child = node.firstChild;
- while(child) {
- initNode(child);
- child = child.nextSibling;
- }
- }
-}
-
-module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) {
- var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled);
- var container = containerDocument.createElement("iframe");
-
- container.className = "html2canvas-container";
- container.style.visibility = "hidden";
- container.style.position = "fixed";
- container.style.left = "-10000px";
- container.style.top = "0px";
- container.style.border = "0";
- container.width = width;
- container.height = height;
- container.scrolling = "no"; // ios won't scroll without it
- containerDocument.body.appendChild(container);
-
- return new Promise(function(resolve) {
- var documentClone = container.contentWindow.document;
-
- /* Chrome doesn't detect relative background-images assigned in inline ');
-
- function getFontSizeInPixels(el, value) {
- return getSizeInPixels(el, /(?:em|ex|%)$/i.test(value) ? '1em' : value);
- }
-
- // Original by Dead Edwards.
- // Combined with getFontSizeInPixels it also works with relative units.
- function getSizeInPixels(el, value) {
- if (/px$/i.test(value)) return parseFloat(value);
- var style = el.style.left, runtimeStyle = el.runtimeStyle.left;
- el.runtimeStyle.left = el.currentStyle.left;
- el.style.left = value;
- var result = el.style.pixelLeft;
- el.style.left = style;
- el.runtimeStyle.left = runtimeStyle;
- return result;
- }
-
- return function(font, text, style, options, node, el, hasNext) {
- var redraw = (text === null);
-
- if (redraw) text = node.alt;
-
- // @todo word-spacing, text-decoration
-
- var viewBox = font.viewBox;
-
- var size = style.computedFontSize ||
- (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
-
- var letterSpacing = style.computedLSpacing;
-
- if (letterSpacing == undefined) {
- letterSpacing = style.get('letterSpacing');
- style.computedLSpacing = letterSpacing =
- (letterSpacing == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, letterSpacing));
- }
-
- var wrapper, canvas;
-
- if (redraw) {
- wrapper = node;
- canvas = node.firstChild;
- }
- else {
- wrapper = fabric.document.createElement('span');
- wrapper.className = 'cufon cufon-vml';
- wrapper.alt = text;
-
- canvas = fabric.document.createElement('span');
- canvas.className = 'cufon-vml-canvas';
- wrapper.appendChild(canvas);
-
- if (options.printable) {
- var print = fabric.document.createElement('span');
- print.className = 'cufon-alt';
- print.appendChild(fabric.document.createTextNode(text));
- wrapper.appendChild(print);
- }
-
- // ie6, for some reason, has trouble rendering the last VML element in the document.
- // we can work around this by injecting a dummy element where needed.
- // @todo find a better solution
- if (!hasNext) wrapper.appendChild(fabric.document.createElement('cvml:shape'));
- }
-
- var wStyle = wrapper.style;
- var cStyle = canvas.style;
-
- var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height);
- var roundingFactor = roundedHeight / height;
- var minX = viewBox.minX, minY = viewBox.minY;
-
- cStyle.height = roundedHeight;
- cStyle.top = Math.round(size.convert(minY - font.ascent));
- cStyle.left = Math.round(size.convert(minX));
-
- wStyle.height = size.convert(font.height) + 'px';
-
- var textDecoration = Cufon.getTextDecoration(options);
-
- var color = style.get('color');
-
- var chars = Cufon.CSS.textTransform(text, style).split('');
-
- var width = 0, offsetX = 0, advance = null;
-
- var glyph, shape, shadows = options.textShadow;
-
- // pre-calculate width
- for (var i = 0, k = 0, l = chars.length; i < l; ++i) {
- glyph = font.glyphs[chars[i]] || font.missingGlyph;
- if (glyph) width += advance = ~~(glyph.w || font.w) + letterSpacing;
- }
-
- if (advance === null) return null;
-
- var fullWidth = -minX + width + (viewBox.width - advance);
-
- var shapeWidth = size.convert(fullWidth * roundingFactor), roundedShapeWidth = Math.round(shapeWidth);
-
- var coordSize = fullWidth + ',' + viewBox.height, coordOrigin;
- var stretch = 'r' + coordSize + 'nsnf';
-
- for (i = 0; i < l; ++i) {
-
- glyph = font.glyphs[chars[i]] || font.missingGlyph;
- if (!glyph) continue;
-
- if (redraw) {
- // some glyphs may be missing so we can't use i
- shape = canvas.childNodes[k];
- if (shape.firstChild) shape.removeChild(shape.firstChild); // shadow
- }
- else {
- shape = fabric.document.createElement('cvml:shape');
- canvas.appendChild(shape);
- }
-
- shape.stroked = 'f';
- shape.coordsize = coordSize;
- shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
- shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
- shape.fillcolor = color;
-
- // it's important to not set top/left or IE8 will grind to a halt
- var sStyle = shape.style;
- sStyle.width = roundedShapeWidth;
- sStyle.height = roundedHeight;
-
- if (shadows) {
- // due to the limitations of the VML shadow element there
- // can only be two visible shadows. opacity is shared
- // for all shadows.
- var shadow1 = shadows[0], shadow2 = shadows[1];
- var color1 = Cufon.CSS.color(shadow1.color), color2;
- var shadow = fabric.document.createElement('cvml:shadow');
- shadow.on = 't';
- shadow.color = color1.color;
- shadow.offset = shadow1.offX + ',' + shadow1.offY;
- if (shadow2) {
- color2 = Cufon.CSS.color(shadow2.color);
- shadow.type = 'double';
- shadow.color2 = color2.color;
- shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
- }
- shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
- shape.appendChild(shadow);
- }
-
- offsetX += ~~(glyph.w || font.w) + letterSpacing;
-
- ++k;
-
- }
-
- wStyle.width = Math.max(Math.ceil(size.convert(width * roundingFactor)), 0);
-
- return wrapper;
-
- };
-
-})());
-
-Cufon.getTextDecoration = function(options) {
- return {
- underline: options.textDecoration === 'underline',
- overline: options.textDecoration === 'overline',
- 'line-through': options.textDecoration === 'line-through'
- };
-};
-
-if (typeof exports != 'undefined') {
- exports.Cufon = Cufon;
-}
-
-
-/*
- json2.js
- 2014-02-04
-
- Public Domain.
-
- NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
- See http://www.JSON.org/js.html
-
-
- This code should be minified before deployment.
- See http://javascript.crockford.com/jsmin.html
-
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
- NOT CONTROL.
-
-
- This file creates a global JSON object containing two methods: stringify
- and parse.
-
- JSON.stringify(value, replacer, space)
- value any JavaScript value, usually an object or array.
-
- replacer an optional parameter that determines how object
- values are stringified for objects. It can be a
- function or an array of strings.
-
- space an optional parameter that specifies the indentation
- of nested structures. If it is omitted, the text will
- be packed without extra whitespace. If it is a number,
- it will specify the number of spaces to indent at each
- level. If it is a string (such as '\t' or ' '),
- it contains the characters used to indent at each level.
-
- This method produces a JSON text from a JavaScript value.
-
- When an object value is found, if the object contains a toJSON
- method, its toJSON method will be called and the result will be
- stringified. A toJSON method does not serialize: it returns the
- value represented by the name/value pair that should be serialized,
- or undefined if nothing should be serialized. The toJSON method
- will be passed the key associated with the value, and this will be
- bound to the value
-
- For example, this would serialize Dates as ISO strings.
-
- Date.prototype.toJSON = function (key) {
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- return this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z';
- };
-
- You can provide an optional replacer method. It will be passed the
- key and value of each member, with this bound to the containing
- object. The value that is returned from your method will be
- serialized. If your method returns undefined, then the member will
- be excluded from the serialization.
-
- If the replacer parameter is an array of strings, then it will be
- used to select the members to be serialized. It filters the results
- such that only members with keys listed in the replacer array are
- stringified.
-
- Values that do not have JSON representations, such as undefined or
- functions, will not be serialized. Such values in objects will be
- dropped; in arrays they will be replaced with null. You can use
- a replacer function to replace those with JSON values.
- JSON.stringify(undefined) returns undefined.
-
- The optional space parameter produces a stringification of the
- value that is filled with line breaks and indentation to make it
- easier to read.
-
- If the space parameter is a non-empty string, then that string will
- be used for indentation. If the space parameter is a number, then
- the indentation will be that many spaces.
-
- Example:
-
- text = JSON.stringify(['e', {pluribus: 'unum'}]);
- // text is '["e",{"pluribus":"unum"}]'
-
-
- text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
- // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
- text = JSON.stringify([new Date()], function (key, value) {
- return this[key] instanceof Date ?
- 'Date(' + this[key] + ')' : value;
- });
- // text is '["Date(---current time---)"]'
-
-
- JSON.parse(text, reviver)
- This method parses a JSON text to produce an object or array.
- It can throw a SyntaxError exception.
-
- The optional reviver parameter is a function that can filter and
- transform the results. It receives each of the keys and values,
- and its return value is used instead of the original value.
- If it returns what it received, then the structure is not modified.
- If it returns undefined then the member is deleted.
-
- Example:
-
- // Parse the text. Values that look like ISO date strings will
- // be converted to Date objects.
-
- myData = JSON.parse(text, function (key, value) {
- var a;
- if (typeof value === 'string') {
- a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
- if (a) {
- return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
- +a[5], +a[6]));
- }
- }
- return value;
- });
-
- myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
- var d;
- if (typeof value === 'string' &&
- value.slice(0, 5) === 'Date(' &&
- value.slice(-1) === ')') {
- d = new Date(value.slice(5, -1));
- if (d) {
- return d;
- }
- }
- return value;
- });
-
-
- This is a reference implementation. You are free to copy, modify, or
- redistribute.
-*/
-
-/*jslint evil: true, regexp: true */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
- lastIndex, length, parse, prototype, push, replace, slice, stringify,
- test, toJSON, toString, valueOf
-*/
-
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (typeof JSON !== 'object') {
- JSON = {};
-}
-
-(function () {
- 'use strict';
-
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- if (typeof Date.prototype.toJSON !== 'function') {
-
- Date.prototype.toJSON = function () {
-
- return isFinite(this.valueOf())
- ? this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z'
- : null;
- };
-
- String.prototype.toJSON =
- Number.prototype.toJSON =
- Boolean.prototype.toJSON = function () {
- return this.valueOf();
- };
- }
-
- var cx,
- escapable,
- gap,
- indent,
- meta,
- rep;
-
-
- function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
- escapable.lastIndex = 0;
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string'
- ? c
- : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
- }
-
-
- function str(key, holder) {
-
-// Produce a string from holder[key].
-
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
- if (value && typeof value === 'object' &&
- typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
-
-// What happens next depends on the value's type.
-
- switch (typeof value) {
- case 'string':
- return quote(value);
-
- case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
- return isFinite(value) ? String(value) : 'null';
-
- case 'boolean':
- case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
- return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
- case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
- if (!value) {
- return 'null';
- }
-
-// Make an array to hold the partial results of stringifying this object value.
-
- gap += indent;
- partial = [];
-
-// Is the value an array?
-
- if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
- v = partial.length === 0
- ? '[]'
- : gap
- ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
- : '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- if (typeof rep[i] === 'string') {
- k = rep[i];
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
- v = partial.length === 0
- ? '{}'
- : gap
- ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
- : '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
- if (typeof JSON.stringify !== 'function') {
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- };
- JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
- var i;
- gap = '';
- indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
-
-// If the space parameter is a string, it will be used as the indent string.
-
- } else if (typeof space === 'string') {
- indent = space;
- }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
- }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
- return str('', {'': value});
- };
- }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
- if (typeof JSON.parse !== 'function') {
- cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
- JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
- var j;
-
- function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
- text = String(text);
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
- if (/^[\],:{}\s]*$/
- .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
- .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
- j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
- return typeof reviver === 'function'
- ? walk({'': j}, '')
- : j;
- }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
- throw new SyntaxError('JSON.parse');
- };
- }
-}());
-
-
-(function(){
-
- /**
- * @private
- * @param {String} eventName
- * @param {Function} handler
- */
- function _removeEventListener(eventName, handler) {
- if (!this.__eventListeners[eventName]) {
- return;
- }
-
- if (handler) {
- fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
- }
- else {
- this.__eventListeners[eventName].length = 0;
- }
- }
-
- /**
- * Observes specified event
- * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
- * @memberOf fabric.Observable
- * @alias on
- * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
- * @param {Function} handler Function that receives a notification when an event of the specified type occurs
- * @return {Self} thisArg
- * @chainable
- */
- function observe(eventName, handler) {
- if (!this.__eventListeners) {
- this.__eventListeners = { };
- }
- // one object with key/value pairs was passed
- 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;
- }
-
- /**
- * Stops event observing for a particular event handler. Calling this method
- * without arguments removes all handlers for all events
- * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
- * @memberOf fabric.Observable
- * @alias off
- * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
- * @param {Function} handler Function to be deleted from EventListeners
- * @return {Self} thisArg
- * @chainable
- */
- function stopObserving(eventName, handler) {
- if (!this.__eventListeners) {
- return;
- }
-
- // remove all key/value pairs (event name -> event handler)
- if (arguments.length === 0) {
- this.__eventListeners = { };
- }
- // one object with key/value pairs was passed
- 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;
- }
-
- /**
- * Fires event with an optional options object
- * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
- * @memberOf fabric.Observable
- * @alias trigger
- * @param {String} eventName Event name to fire
- * @param {Object} [options] Options object
- * @return {Self} thisArg
- * @chainable
- */
- 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++) {
- // avoiding try/catch for perf. reasons
- listenersForEvent[i].call(this, options || { });
- }
- return this;
- }
-
- /**
- * @namespace fabric.Observable
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#events}
- * @see {@link http://fabricjs.com/events/|Events demo}
- */
- fabric.Observable = {
- observe: observe,
- stopObserving: stopObserving,
- fire: fire,
-
- on: observe,
- off: stopObserving,
- trigger: fire
- };
-})();
-
-
-/**
- * @namespace fabric.Collection
- */
-fabric.Collection = {
-
- /**
- * Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * Objects should be instances of (or inherit from) fabric.Object
- * @param {...fabric.Object} object Zero or more fabric instances
- * @return {Self} thisArg
- */
- add: function () {
- this._objects.push.apply(this._objects, arguments);
- for (var i = 0, length = arguments.length; i < length; i++) {
- this._onObjectAdded(arguments[i]);
- }
- this.renderOnAddRemove && this.renderAll();
- return this;
- },
-
- /**
- * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
- * An object should be an instance of (or inherit from) fabric.Object
- * @param {Object} object Object to insert
- * @param {Number} index Index to insert object at
- * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
- * @return {Self} thisArg
- * @chainable
- */
- insertAt: function (object, index, nonSplicing) {
- var objects = this.getObjects();
- if (nonSplicing) {
- objects[index] = object;
- }
- else {
- objects.splice(index, 0, object);
- }
- this._onObjectAdded(object);
- this.renderOnAddRemove && this.renderAll();
- return this;
- },
-
- /**
- * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * @param {...fabric.Object} object Zero or more fabric instances
- * @return {Self} thisArg
- * @chainable
- */
- remove: function() {
- var objects = this.getObjects(),
- index;
-
- for (var i = 0, length = arguments.length; i < length; i++) {
- index = objects.indexOf(arguments[i]);
-
- // only call onObjectRemoved if an object was actually removed
- if (index !== -1) {
- objects.splice(index, 1);
- this._onObjectRemoved(arguments[i]);
- }
- }
-
- this.renderOnAddRemove && this.renderAll();
- return this;
- },
-
- /**
- * Executes given function for each object in this group
- * @param {Function} callback
- * Callback invoked with current object as first argument,
- * index - as second and an array of all objects - as third.
- * Iteration happens in reverse order (for performance reasons).
- * Callback is invoked in a context of Global Object (e.g. `window`)
- * when no `context` argument is given
- *
- * @param {Object} context Context (aka thisObject)
- * @return {Self} thisArg
- */
- forEachObject: function(callback, context) {
- var objects = this.getObjects(),
- i = objects.length;
- while (i--) {
- callback.call(context, objects[i], i, objects);
- }
- return this;
- },
-
- /**
- * Returns an array of children objects of this instance
- * Type parameter introduced in 1.3.10
- * @param {String} [type] When specified, only objects of this type are returned
- * @return {Array}
- */
- getObjects: function(type) {
- if (typeof type === 'undefined') {
- return this._objects;
- }
- return this._objects.filter(function(o) {
- return o.type === type;
- });
- },
-
- /**
- * Returns object at specified index
- * @param {Number} index
- * @return {Self} thisArg
- */
- item: function (index) {
- return this.getObjects()[index];
- },
-
- /**
- * Returns true if collection contains no objects
- * @return {Boolean} true if collection is empty
- */
- isEmpty: function () {
- return this.getObjects().length === 0;
- },
-
- /**
- * Returns a size of a collection (i.e: length of an array containing its objects)
- * @return {Number} Collection size
- */
- size: function() {
- return this.getObjects().length;
- },
-
- /**
- * Returns true if collection contains an object
- * @param {Object} object Object to check against
- * @return {Boolean} `true` if collection contains an object
- */
- contains: function(object) {
- return this.getObjects().indexOf(object) > -1;
- },
-
- /**
- * Returns number representation of a collection complexity
- * @return {Number} complexity
- */
- complexity: function () {
- return this.getObjects().reduce(function (memo, current) {
- memo += current.complexity ? current.complexity() : 0;
- return memo;
- }, 0);
- }
-};
-
-
-(function(global) {
-
- var sqrt = Math.sqrt,
- atan2 = Math.atan2,
- PiBy180 = Math.PI / 180;
-
- /**
- * @namespace fabric.util
- */
- fabric.util = {
-
- /**
- * Removes value from an array.
- * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
- * @static
- * @memberOf fabric.util
- * @param {Array} array
- * @param {Any} value
- * @return {Array} original array
- */
- removeFromArray: function(array, value) {
- var idx = array.indexOf(value);
- if (idx !== -1) {
- array.splice(idx, 1);
- }
- return array;
- },
-
- /**
- * Returns random number between 2 specified ones.
- * @static
- * @memberOf fabric.util
- * @param {Number} min lower limit
- * @param {Number} max upper limit
- * @return {Number} random value (between min and max)
- */
- getRandomInt: function(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- },
-
- /**
- * Transforms degrees to radians.
- * @static
- * @memberOf fabric.util
- * @param {Number} degrees value in degrees
- * @return {Number} value in radians
- */
- degreesToRadians: function(degrees) {
- return degrees * PiBy180;
- },
-
- /**
- * Transforms radians to degrees.
- * @static
- * @memberOf fabric.util
- * @param {Number} radians value in radians
- * @return {Number} value in degrees
- */
- radiansToDegrees: function(radians) {
- return radians / PiBy180;
- },
-
- /**
- * Rotates `point` around `origin` with `radians`
- * @static
- * @memberOf fabric.util
- * @param {fabric.Point} point The point to rotate
- * @param {fabric.Point} origin The origin of the rotation
- * @param {Number} radians The radians of the angle for the rotation
- * @return {fabric.Point} The new rotated point
- */
- rotatePoint: function(point, origin, radians) {
- var sin = Math.sin(radians),
- cos = Math.cos(radians);
-
- point.subtractEquals(origin);
-
- var rx = point.x * cos - point.y * sin,
- ry = point.x * sin + point.y * cos;
-
- return new fabric.Point(rx, ry).addEquals(origin);
- },
-
- /**
- * Apply transform t to point p
- * @static
- * @memberOf fabric.util
- * @param {fabric.Point} p The point to transform
- * @param {Array} t The transform
- * @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
- * @return {fabric.Point} The transformed point
- */
- transformPoint: function(p, t, ignoreOffset) {
- if (ignoreOffset) {
- return new fabric.Point(
- t[0] * p.x + t[1] * p.y,
- t[2] * p.x + t[3] * p.y
- );
- }
- return new fabric.Point(
- t[0] * p.x + t[1] * p.y + t[4],
- t[2] * p.x + t[3] * p.y + t[5]
- );
- },
-
- /**
- * Invert transformation t
- * @static
- * @memberOf fabric.util
- * @param {Array} t The transform
- * @return {Array} The inverted transform
- */
- invertTransform: function(t) {
- var r = t.slice(),
- a = 1 / (t[0] * t[3] - t[1] * t[2]);
- r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
- var o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r);
- r[4] = -o.x;
- r[5] = -o.y;
- return r;
- },
-
- /**
- * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
- * @static
- * @memberOf fabric.util
- * @param {Number|String} number number to operate on
- * @param {Number} fractionDigits number of fraction digits to "leave"
- * @return {Number}
- */
- toFixed: function(number, fractionDigits) {
- return parseFloat(Number(number).toFixed(fractionDigits));
- },
-
- /**
- * Converts from attribute value to pixel value if applicable.
- * Returns converted pixels or original value not converted.
- * @param {Number|String} value number to operate on
- * @return {Number|String}
- */
- parseUnit: function(value) {
- var unit = /\D{0,2}$/.exec(value),
- number = parseFloat(value);
-
- 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; // or * 4 / 3
-
- case 'pc':
- return number * fabric.DPI / 72 * 12; // or * 16
-
- default:
- return number;
- }
- },
-
- /**
- * Function which always returns `false`.
- * @static
- * @memberOf fabric.util
- * @return {Boolean}
- */
- falseFunction: function() {
- return false;
- },
-
- /**
- * Returns klass "Class" object of given namespace
- * @memberOf fabric.util
- * @param {String} type Type of object (eg. 'circle')
- * @param {String} namespace Namespace to get klass "Class" object from
- * @return {Object} klass "Class"
- */
- getKlass: function(type, namespace) {
- // capitalize first letter only
- type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
- return fabric.util.resolveNamespace(namespace)[type];
- },
-
- /**
- * Returns object of given namespace
- * @memberOf fabric.util
- * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
- * @return {Object} Object for given namespace (default fabric)
- */
- 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;
- },
-
- /**
- * Loads image element from given url and passes it to a callback
- * @memberOf fabric.util
- * @param {String} url URL representing an image
- * @param {Function} callback Callback; invoked with loaded image
- * @param {Any} [context] Context to invoke callback in
- * @param {Object} [crossOrigin] crossOrigin value to set image element to
- */
- loadImage: function(url, callback, context, crossOrigin) {
- if (!url) {
- callback && callback.call(context, url);
- return;
- }
-
- var img = fabric.util.createImage();
-
- /** @ignore */
- img.onload = function () {
- callback && callback.call(context, img);
- img = img.onload = img.onerror = null;
- };
-
- /** @ignore */
- img.onerror = function() {
- fabric.log('Error loading ' + img.src);
- callback && callback.call(context, null, true);
- img = img.onload = img.onerror = null;
- };
-
- // data-urls appear to be buggy with crossOrigin
- // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
- // see https://code.google.com/p/chromium/issues/detail?id=315152
- // https://bugzilla.mozilla.org/show_bug.cgi?id=935069
- if (url.indexOf('data') !== 0 && typeof crossOrigin !== 'undefined') {
- img.crossOrigin = crossOrigin;
- }
-
- img.src = url;
- },
-
- /**
- * Creates corresponding fabric instances from their object representations
- * @static
- * @memberOf fabric.util
- * @param {Array} objects Objects to enliven
- * @param {Function} callback Callback to invoke when all objects are created
- * @param {String} namespace Namespace to get klass "Class" object from
- * @param {Function} reviver Method for further parsing of object elements,
- * called after each fabric object created.
- */
- 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 sparse array
- 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();
- }
- });
- },
-
- /**
- * Groups SVG elements (usually those retrieved from SVG document)
- * @static
- * @memberOf fabric.util
- * @param {Array} elements SVG elements to group
- * @param {Object} [options] Options object
- * @return {fabric.Object|fabric.PathGroup}
- */
- groupSVGElements: function(elements, options, path) {
- var object;
-
- object = new fabric.PathGroup(elements, options);
-
- if (typeof path !== 'undefined') {
- object.setSourcePath(path);
- }
- return object;
- },
-
- /**
- * Populates an object with properties of another object
- * @static
- * @memberOf fabric.util
- * @param {Object} source Source object
- * @param {Object} destination Destination object
- * @return {Array} properties Propertie names to include
- */
- 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]];
- }
- }
- }
- },
-
- /**
- * Draws a dashed line between two points
- *
- * This method is used to draw dashed line around selection area.
- * See dotted stroke in canvas
- *
- * @param {CanvasRenderingContext2D} ctx context
- * @param {Number} x start x coordinate
- * @param {Number} y start y coordinate
- * @param {Number} x2 end x coordinate
- * @param {Number} y2 end y coordinate
- * @param {Array} da dash array pattern
- */
- 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();
- },
-
- /**
- * Creates canvas element and initializes it via excanvas if necessary
- * @static
- * @memberOf fabric.util
- * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
- * when not given, element is created implicitly
- * @return {CanvasElement} initialized canvas element
- */
- createCanvasElement: function(canvasEl) {
- canvasEl || (canvasEl = fabric.document.createElement('canvas'));
- //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
- if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
- G_vmlCanvasManager.initElement(canvasEl);
- }
- //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
- return canvasEl;
- },
-
- /**
- * Creates image element (works on client and node)
- * @static
- * @memberOf fabric.util
- * @return {HTMLImageElement} HTML image element
- */
- createImage: function() {
- return fabric.isLikelyNode
- ? new (_dereq_('canvas').Image)()
- : fabric.document.createElement('img');
- },
-
- /**
- * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
- * @static
- * @memberOf fabric.util
- * @param {Object} klass "Class" to create accessors for
- */
- 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;
-
- // using `new Function` for better introspection
- 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);
- }
- }
- },
-
- /**
- * @static
- * @memberOf fabric.util
- * @param {fabric.Object} receiver Object implementing `clipTo` method
- * @param {CanvasRenderingContext2D} ctx Context to clip
- */
- clipContext: function(receiver, ctx) {
- ctx.save();
- ctx.beginPath();
- receiver.clipTo(ctx);
- ctx.clip();
- },
-
- /**
- * Multiply matrix A by matrix B to nest transformations
- * @static
- * @memberOf fabric.util
- * @param {Array} matrixA First transformMatrix
- * @param {Array} matrixB Second transformMatrix
- * @return {Array} The product of the two transform matrices
- */
- multiplyTransformMatrices: function(matrixA, matrixB) {
- // Matrix multiply matrixA * matrixB
- var a = [
- [matrixA[0], matrixA[2], matrixA[4]],
- [matrixA[1], matrixA[3], matrixA[5]],
- [0, 0, 1 ]
- ],
-
- b = [
- [matrixB[0], matrixB[2], matrixB[4]],
- [matrixB[1], matrixB[3], matrixB[5]],
- [0, 0, 1 ]
- ],
-
- result = [];
-
- for (var r = 0; r < 3; r++) {
- result[r] = [];
- for (var c = 0; c < 3; c++) {
- var sum = 0;
- for (var k = 0; k < 3; k++) {
- sum += a[r][k] * b[k][c];
- }
-
- result[r][c] = sum;
- }
- }
-
- return [
- result[0][0],
- result[1][0],
- result[0][1],
- result[1][1],
- result[0][2],
- result[1][2]
- ];
- },
-
- /**
- * Returns string representation of function body
- * @param {Function} fn Function to get body of
- * @return {String} Function body
- */
- getFunctionBody: function(fn) {
- return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
- },
-
- /**
- * Returns true if context has transparent pixel
- * at specified location (taking tolerance into account)
- * @param {CanvasRenderingContext2D} ctx context
- * @param {Number} x x coordinate
- * @param {Number} y y coordinate
- * @param {Number} tolerance Tolerance
- */
- isTransparent: function(ctx, x, y, tolerance) {
-
- // If tolerance is > 0 adjust start coords to take into account.
- // If moves off Canvas fix to 0
- 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);
-
- // Split image data - for tolerance > 1, pixelDataSize = 4;
- for (var i = 3, l = imageData.data.length; i < l; i += 4) {
- var temp = imageData.data[i];
- _isTransparent = temp <= 0;
- if (_isTransparent === false) {
- break; // Stop if colour found
- }
- }
-
- imageData = null;
-
- return _isTransparent;
- }
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
- var arcToSegmentsCache = { },
- segmentToBezierCache = { },
- _join = Array.prototype.join;
-
- /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
- * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
- * http://mozilla.org/MPL/2.0/
- */
- 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 - sinTh * toY,
- py = -cosTh * toY + sinTh * toX,
- rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
- pl = 4 * rx2 * ry2 - rx2 * py2 - ry2 * px2,
- root = 0;
-
- if (pl < 0) {
- var s = Math.sqrt(1 - 0.25 * pl/(rx2 * ry2));
- rx *= s;
- ry *= s;
- }
- else {
- root = (large === sweep ? -0.5 : 0.5) *
- Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
- }
-
- var cx = root * rx * py / ry,
- cy = -root * ry * px / rx,
- cx1 = cosTh * cx - sinTh * cy + toX / 2,
- cy1 = sinTh * cx + cosTh * cy + toY / 2,
- 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;
- }
-
- // Convert into cubic bezier segments <= 90deg
- var segments = Math.ceil(Math.abs(dtheta / (PI * 0.5))),
- 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 += mDelta;
- 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];
- }
-
- /*
- * Private
- */
- 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);
- }
- }
-
- /**
- * Draws arc
- * @param {CanvasRenderingContext2D} ctx
- * @param {Number} fx
- * @param {Number} fy
- * @param {Array} coords
- */
- 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]);
- }
- };
-})();
-
-
-(function() {
-
- var slice = Array.prototype.slice;
-
-
-
- /**
- * Invokes method on all items in a given array
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} method Name of a method to invoke
- * @return {Array}
- */
- 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;
- }
-
- /**
- * Finds maximum value in array (not necessarily "first" one)
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} byProperty
- * @return {Any}
- */
- function max(array, byProperty) {
- return find(array, byProperty, function(value1, value2) {
- return value1 >= value2;
- });
- }
-
- /**
- * Finds minimum value in array (not necessarily "first" one)
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} byProperty
- * @return {Any}
- */
- function min(array, byProperty) {
- return find(array, byProperty, function(value1, value2) {
- return value1 < value2;
- });
- }
-
- /**
- * @private
- */
- 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;
- }
-
- /**
- * @namespace fabric.util.array
- */
- fabric.util.array = {
- invoke: invoke,
- min: min,
- max: max
- };
-
-})();
-
-
-(function(){
-
- /**
- * Copies all enumerable properties of one object to another
- * @memberOf fabric.util.object
- * @param {Object} destination Where to copy to
- * @param {Object} source Where to copy from
- * @return {Object}
- */
- function extend(destination, source) {
- // JScript DontEnum bug is not taken care of
- for (var property in source) {
- destination[property] = source[property];
- }
- return destination;
- }
-
- /**
- * Creates an empty object and copies all enumerable properties of another object to it
- * @memberOf fabric.util.object
- * @param {Object} object Object to clone
- * @return {Object}
- */
- function clone(object) {
- return extend({ }, object);
- }
-
- /** @namespace fabric.util.object */
- fabric.util.object = {
- extend: extend,
- clone: clone
- };
-
-})();
-
-
-(function() {
-
-
-
- /**
- * Camelizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to camelize
- * @return {String} Camelized version of a string
- */
- function camelize(string) {
- return string.replace(/-+(.)?/g, function(match, character) {
- return character ? character.toUpperCase() : '';
- });
- }
-
- /**
- * Capitalizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to capitalize
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
- * and other letters stay untouched, if false first letter is capitalized
- * and other letters are converted to lowercase.
- * @return {String} Capitalized version of a string
- */
- function capitalize(string, firstLetterOnly) {
- return string.charAt(0).toUpperCase() +
- (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
- }
-
- /**
- * Escapes XML in a string
- * @memberOf fabric.util.string
- * @param {String} string String to escape
- * @return {String} Escaped version of a string
- */
- function escapeXml(string) {
- return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(//g, '>');
- }
-
- /**
- * String utilities
- * @namespace fabric.util.string
- */
- fabric.util.string = {
- camelize: camelize,
- capitalize: capitalize,
- escapeXml: escapeXml
- };
-}());
-
-
-
-
-
-(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;
- })(),
-
- /** @ignore */
- addMethods = function(klass, source, parent) {
- for (var property in source) {
-
- if (property in klass.prototype &&
- typeof klass.prototype[property] === 'function' &&
- (source[property] + '').indexOf('callSuper') > -1) {
-
- 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);
- }
-
- /**
- * Helper for creation of "classes".
- * @memberOf fabric.util
- * @param {Function} [parent] optional "Class" to inherit from
- * @param {Object} [properties] Properties shared by all instances of this class
- * (be careful modifying objects defined here as this would affect all instances)
- */
- 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';
-
- /* EVENT HANDLING */
-
- 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;
- }
-
- /** @ignore */
- var getElement,
- setElement,
- getUniqueId = (function () {
- var uid = 0;
- return function (element) {
- return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
- };
- })();
-
- (function () {
- var elements = { };
- /** @ignore */
- getElement = function (uid) {
- return elements[uid];
- };
- /** @ignore */
- 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')),
-
- // IE branch
- listeners = { },
-
- // DOM L0 branch
- handlers = { },
-
- addListener, removeListener;
-
- if (shouldUseAddListenerRemoveListener) {
- /** @ignore */
- addListener = function (element, eventName, handler) {
- element.addEventListener(eventName, handler, false);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler) {
- element.removeEventListener(eventName, handler, false);
- };
- }
-
- else if (shouldUseAttachEventDetachEvent) {
- /** @ignore */
- 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);
- };
- /** @ignore */
- 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 {
- /** @ignore */
- 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);
- };
- /** @ignore */
- 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);
- }
- }
- }
- };
- }
-
- /**
- * Adds an event listener to an element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {String} eventName
- * @param {Function} handler
- */
- fabric.util.addListener = addListener;
-
- /**
- * Removes an event listener from an element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {String} eventName
- * @param {Function} handler
- */
- fabric.util.removeListener = removeListener;
-
- /**
- * Cross-browser wrapper for getting event's coordinates
- * @memberOf fabric.util
- * @param {Event} event Event object
- * @param {HTMLCanvasElement} upperCanvasEl <canvas> element on which object selection is drawn
- */
- function getPointer(event, upperCanvasEl) {
- event || (event = fabric.window.event);
-
- var element = event.target ||
- (typeof event.srcElement !== unknown ? event.srcElement : null),
-
- scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
-
- return {
- x: pointerX(event) + scroll.left,
- y: pointerY(event) + scroll.top
- };
- }
-
- var pointerX = function(event) {
- // looks like in IE (<9) clientX at certain point (apparently when mouseup fires on VML element)
- // is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
- // need to investigate later
- return (typeof event.clientX !== unknown ? event.clientX : 0);
- },
-
- 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 () {
-
- /**
- * Cross-browser wrapper for setting element's style
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {Object} styles
- * @return {HTMLElement} Element that was passed as a first argument
- */
- 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*([^\)]+)\)/,
-
- /** @ignore */
- setOpacity = function (element) { return element; };
-
- if (supportsOpacity) {
- /** @ignore */
- setOpacity = function(element, value) {
- element.style.opacity = value;
- return element;
- };
- }
- else if (supportsFilters) {
- /** @ignore */
- setOpacity = function(element, value) {
- var es = element.style;
- if (element.currentStyle && !element.currentStyle.hasLayout) {
- es.zoom = 1;
- }
- if (reOpacity.test(es.filter)) {
- value = value >= 0.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;
-
- /**
- * Takes id and returns an element with that id (if one exists in a document)
- * @memberOf fabric.util
- * @param {String|HTMLElement} id
- * @return {HTMLElement|null}
- */
- function getById(id) {
- return typeof id === 'string' ? fabric.document.getElementById(id) : id;
- }
-
- var sliceCanConvertNodelists,
- /**
- * Converts an array-like object (e.g. arguments or NodeList) to an array
- * @memberOf fabric.util
- * @param {Object} arrayLike
- * @return {Array}
- */
- toArray = function(arrayLike) {
- return _slice.call(arrayLike, 0);
- };
-
- 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;
- };
- }
-
- /**
- * Creates specified element with specified attributes
- * @memberOf fabric.util
- * @param {String} tagName Type of an element to create
- * @param {Object} [attributes] Attributes to set on an element
- * @return {HTMLElement} Newly created element
- */
- 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;
- }
-
- /**
- * Adds class to an element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to add class to
- * @param {String} className Class to add to an element
- */
- function addClass(element, className) {
- if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
- element.className += (element.className ? ' ' : '') + className;
- }
- }
-
- /**
- * Wraps element with another element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to wrap
- * @param {HTMLElement|String} wrapper Element to wrap with
- * @param {Object} [attributes] Attributes to set on a wrapper
- * @return {HTMLElement} wrapper
- */
- 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;
- }
-
- /**
- * Returns element scroll offsets
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to operate on
- * @param {HTMLElement} upperCanvasEl Upper canvas element
- * @return {Object} Object with left/top values
- */
- function getScrollLeftTop(element, upperCanvasEl) {
-
- var firstFixedAncestor,
- origElement,
- left = 0,
- top = 0,
- docElement = fabric.document.documentElement,
- body = fabric.document.body || {
- scrollLeft: 0, scrollTop: 0
- };
-
- origElement = element;
-
- while (element && element.parentNode && !firstFixedAncestor) {
-
- element = element.parentNode;
-
- if (element !== fabric.document &&
- fabric.util.getElementStyle(element, 'position') === 'fixed') {
- firstFixedAncestor = element;
- }
-
- if (element !== fabric.document &&
- origElement !== upperCanvasEl &&
- fabric.util.getElementStyle(element, 'position') === 'absolute') {
- left = 0;
- top = 0;
- }
- else if (element === fabric.document) {
- left = body.scrollLeft || docElement.scrollLeft || 0;
- top = body.scrollTop || docElement.scrollTop || 0;
- }
- else {
- left += element.scrollLeft || 0;
- top += element.scrollTop || 0;
- }
- }
-
- return { left: left, top: top };
- }
-
- /**
- * Returns offset for a given element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to get offset for
- * @return {Object} Object with "left" and "top" properties
- */
- function getElementOffset(element) {
- var docElem,
- doc = element && element.ownerDocument,
- box = { left: 0, top: 0 },
- offset = { left: 0, top: 0 },
- scrollLeftTop,
- offsetAttributes = {
- borderLeftWidth: 'left',
- borderTopWidth: 'top',
- paddingLeft: 'left',
- paddingTop: 'top'
- };
-
- if (!doc) {
- return { left: 0, top: 0 };
- }
-
- for (var attr in offsetAttributes) {
- offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
- }
-
- docElem = doc.documentElement;
- if ( typeof element.getBoundingClientRect !== 'undefined' ) {
- box = element.getBoundingClientRect();
- }
-
- scrollLeftTop = fabric.util.getScrollLeftTop(element, null);
-
- return {
- left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
- top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
- };
- }
-
- /**
- * Returns style attribute value of a given element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to get style attribute for
- * @param {String} attr Style attribute to get for element
- * @return {String} Style attribute value of the given element.
- */
- var getElementStyle;
- if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
- getElementStyle = function(element, attr) {
- return fabric.document.defaultView.getComputedStyle(element, null)[attr];
- };
- }
- 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'
- : '';
-
- /**
- * Makes element unselectable
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to make unselectable
- * @return {HTMLElement} Element that was passed in
- */
- 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;
- }
-
- /**
- * Makes element selectable
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to make selectable
- * @return {HTMLElement} Element that was passed in
- */
- 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() {
-
- /**
- * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
- * @memberOf fabric.util
- * @param {String} url URL of a script to load
- * @param {Function} callback Callback to execute when script is finished loading
- */
- function getScript(url, callback) {
- var headEl = fabric.document.getElementsByTagName('head')[0],
- scriptEl = fabric.document.createElement('script'),
- loading = true;
-
- /** @ignore */
- scriptEl.onload = /** @ignore */ 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);
- // causes issue in Opera
- // headEl.removeChild(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() { }
-
- /**
- * Cross-browser abstraction for sending XMLHttpRequest
- * @memberOf fabric.util
- * @param {String} url URL to send XMLHttpRequest to
- * @param {Object} [options] Options object
- * @param {String} [options.method="GET"]
- * @param {Function} options.onComplete Callback to invoke when request is completed
- * @return {XMLHttpRequest} request
- */
- function request(url, options) {
-
- options || (options = { });
-
- var method = options.method ? options.method.toUpperCase() : 'GET',
- onComplete = options.onComplete || function() { },
- xhr = makeXHR(),
- body;
-
- /** @ignore */
- 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;
-})();
-
-
-/**
- * Wrapper around `console.log` (when available)
- * @param {Any} [values] Values to log
- */
-fabric.log = function() { };
-
-/**
- * Wrapper around `console.warn` (when available)
- * @param {Any} [values] Values to log as a warning
- */
-fabric.warn = function() { };
-
-if (typeof console !== 'undefined') {
- ['log', 'warn'].forEach(function(methodName) {
- if (typeof console[methodName] !== 'undefined' && console[methodName].apply) {
- fabric[methodName] = function() {
- return console[methodName].apply(console, arguments);
- };
- }
- });
-}
-
-
-(function(global) {
-
- 'use strict';
-
- /**
- * @name fabric
- * @namespace
- */
-
- var fabric = global.fabric || (global.fabric = { }),
- extend = fabric.util.object.extend,
- capitalize = fabric.util.string.capitalize,
- clone = fabric.util.object.clone,
- toFixed = fabric.util.toFixed,
- parseUnit = fabric.util.parseUnit,
- multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
-
- attributesMap = {
- cx: 'left',
- x: 'left',
- r: 'radius',
- cy: 'top',
- y: 'top',
- display: 'visible',
- visibility: 'visible',
- transform: 'transformMatrix',
- 'fill-opacity': 'fillOpacity',
- 'fill-rule': 'fillRule',
- 'font-family': 'fontFamily',
- 'font-size': 'fontSize',
- 'font-style': 'fontStyle',
- 'font-weight': 'fontWeight',
- 'stroke-dasharray': 'strokeDashArray',
- 'stroke-linecap': 'strokeLineCap',
- 'stroke-linejoin': 'strokeLineJoin',
- 'stroke-miterlimit': 'strokeMiterLimit',
- 'stroke-opacity': 'strokeOpacity',
- 'stroke-width': 'strokeWidth',
- 'text-decoration': 'textDecoration',
- 'text-anchor': 'originX'
- },
-
- colorAttributes = {
- stroke: 'strokeOpacity',
- fill: 'fillOpacity'
- };
-
- function normalizeAttr(attr) {
- // transform attribute names
- if (attr in attributesMap) {
- return attributesMap[attr];
- }
- return attr;
- }
-
- function normalizeValue(attr, value, parentAttributes) {
- var isArray = Object.prototype.toString.call(value) === '[object Array]',
- parsed;
-
- if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
- value = '';
- }
- else if (attr === 'fillRule') {
- value = (value === 'evenodd') ? 'destination-over' : value;
- }
- else if (attr === 'strokeDashArray') {
- value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
- return parseInt(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;
- // display=none on parent element always takes precedence over child element
- if (parentAttributes && parentAttributes.visible === false) {
- value = false;
- }
- }
- else if (attr === 'originX' /* text-anchor */) {
- value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
- }
- else {
- parsed = isArray ? value.map(parseUnit) : parseUnit(value);
- }
-
- return (!isArray && isNaN(parsed) ? value : parsed);
- }
-
- /**
- * @private
- * @param {Object} attributes Array of attributes to parse
- */
- function _setStrokeFillOpacity(attributes) {
- for (var attr in colorAttributes) {
-
- if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') {
- continue;
- }
-
- if (attributes[attr].indexOf('url(') === 0) {
- continue;
- }
-
- var color = new fabric.Color(attributes[attr]);
- attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
- }
- return attributes;
- }
-
- /**
- * Parses "transform" attribute, returning an array of values
- * @static
- * @function
- * @memberOf fabric
- * @param {String} attributeValue String containing attribute value
- * @return {Array} Array of 6 elements representing transformation matrix
- */
- fabric.parseTransformAttribute = (function() {
- function rotateMatrix(matrix, args) {
- var angle = args[0];
-
- matrix[0] = Math.cos(angle);
- matrix[1] = Math.sin(angle);
- matrix[2] = -Math.sin(angle);
- matrix[3] = Math.cos(angle);
- }
-
- function scaleMatrix(matrix, args) {
- var multiplierX = args[0],
- multiplierY = (args.length === 2) ? args[1] : args[0];
-
- matrix[0] = multiplierX;
- matrix[3] = multiplierY;
- }
-
- function skewXMatrix(matrix, args) {
- matrix[2] = args[0];
- }
-
- function skewYMatrix(matrix, args) {
- matrix[1] = args[0];
- }
-
- function translateMatrix(matrix, args) {
- matrix[4] = args[0];
- if (args.length === 2) {
- matrix[5] = args[1];
- }
- }
-
- // identity matrix
- var iMatrix = [
- 1, // a
- 0, // b
- 0, // c
- 1, // d
- 0, // e
- 0 // f
- ],
-
- // == begin transform regexp
- number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
-
- 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*$',
-
- // http://www.w3.org/TR/SVG/coords.html#TransformAttribute
- reTransformList = new RegExp(transformList),
- // == end transform regexp
-
- reTransform = new RegExp(transform, 'g');
-
- return function(attributeValue) {
-
- // start with identity matrix
- var matrix = iMatrix.concat(),
- matrices = [ ];
-
- // return if no argument was given or
- // an argument does not match transform attribute regexp
- if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
- return matrix;
- }
-
- attributeValue.replace(reTransform, function(match) {
-
- var m = new RegExp(transform).exec(match).filter(function (match) {
- return (match !== '' && match != null);
- }),
- operation = m[1],
- args = m.slice(2).map(parseFloat);
-
- switch (operation) {
- case 'translate':
- translateMatrix(matrix, args);
- break;
- case 'rotate':
- args[0] = fabric.util.degreesToRadians(args[0]);
- rotateMatrix(matrix, args);
- break;
- case 'scale':
- scaleMatrix(matrix, args);
- break;
- case 'skewX':
- skewXMatrix(matrix, args);
- break;
- case 'skewY':
- skewYMatrix(matrix, args);
- break;
- case 'matrix':
- matrix = args;
- break;
- }
-
- // snapshot current matrix into matrices array
- matrices.push(matrix.concat());
- // reset
- matrix = iMatrix.concat();
- });
-
- var combinedMatrix = matrices[0];
- while (matrices.length > 1) {
- matrices.shift();
- combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
- }
- return combinedMatrix;
- };
- })();
-
- function parseFontDeclaration(value, oStyle) {
-
- // TODO: support non-px font size
- var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);
-
- if (!match) {
- return;
- }
-
- var fontStyle = match[1],
- // font variant is not used
- // fontVariant = match[2],
- fontWeight = match[3],
- fontSize = match[4],
- lineHeight = match[5],
- fontFamily = match[6];
-
- if (fontStyle) {
- oStyle.fontStyle = fontStyle;
- }
- if (fontWeight) {
- oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
- }
- if (fontSize) {
- oStyle.fontSize = parseFloat(fontSize);
- }
- if (fontFamily) {
- oStyle.fontFamily = fontFamily;
- }
- if (lineHeight) {
- oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
- }
- }
-
- /**
- * @private
- */
- function parseStyleString(style, oStyle) {
- var attr, value;
- style.replace(/;$/, '').split(';').forEach(function (chunk) {
- var pair = chunk.split(':');
-
- attr = normalizeAttr(pair[0].trim().toLowerCase());
- value = normalizeValue(attr, pair[1].trim());
-
- if (attr === 'font') {
- parseFontDeclaration(value, oStyle);
- }
- else {
- oStyle[attr] = value;
- }
- });
- }
-
- /**
- * @private
- */
- function parseStyleObject(style, oStyle) {
- var attr, value;
- for (var prop in style) {
- if (typeof style[prop] === 'undefined') {
- continue;
- }
-
- attr = normalizeAttr(prop.toLowerCase());
- value = normalizeValue(attr, style[prop]);
-
- if (attr === 'font') {
- parseFontDeclaration(value, oStyle);
- }
- else {
- oStyle[attr] = value;
- }
- }
- }
-
- /**
- * @private
- */
- function getGlobalStylesForElement(element) {
- var styles = { };
-
- for (var rule in fabric.cssRules) {
- if (elementMatchesRule(element, rule.split(' '))) {
- for (var property in fabric.cssRules[rule]) {
- styles[property] = fabric.cssRules[rule][property];
- }
- }
- }
- return styles;
- }
-
- /**
- * @private
- */
- function elementMatchesRule(element, selectors) {
- var firstMatching, parentMatching = true;
- //start from rightmost selector.
- 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;
- }
- /**
- * @private
- */
- function selectorMatches(element, selector) {
- var nodeName = element.nodeName,
- classNames = element.getAttribute('class'),
- id = element.getAttribute('id'), matcher;
- // i check if a selector matches slicing away part from it.
- // if i get empty string i should match
- 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;
- }
-
- /**
- * @private
- */
- function parseUseDirectives(doc) {
- var nodelist = doc.getElementsByTagName('use');
- while (nodelist.length) {
- var el = nodelist[0],
- xlink = el.getAttribute('xlink:href').substr(1),
- x = el.getAttribute('x') || 0,
- y = el.getAttribute('y') || 0,
- el2 = doc.getElementById(xlink).cloneNode(true),
- currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
- parentNode;
-
- for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
- var attr = attrs.item(j);
- if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
- continue;
- }
-
- if (attr.nodeName === 'transform') {
- currentTrans = currentTrans + ' ' + attr.nodeValue;
- }
- else {
- el2.setAttribute(attr.nodeName, attr.nodeValue);
- }
- }
-
- el2.setAttribute('transform', currentTrans);
- el2.removeAttribute('id');
- parentNode = el.parentNode;
- parentNode.replaceChild(el2, el);
- }
- }
-
- /**
- * Add a element that envelop all SCG elements and makes the viewbox transformMatrix descend on all elements
- */
- function addSvgTransform(doc, matrix) {
- matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
- if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
- return;
- }
- // default is to preserve aspect ratio
- // preserveAspectRatio attribute to be implemented
- var el = doc.ownerDocument.createElement('g');
- while (doc.firstChild != null) {
- el.appendChild(doc.firstChild);
- }
- el.setAttribute('transform','matrix(' + matrix[0] + ' ' + matrix[1] + ' ' + matrix[2] + ' ' + matrix[3] + ' ' + matrix[4] + ' ' + matrix[5] + ')');
- doc.appendChild(el);
- }
-
- /**
- * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @param {Function} callback Callback to call when parsing is finished; It's being passed an array of elements (parsed from a document).
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- */
- fabric.parseSVGDocument = (function() {
-
- var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
-
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
- // \d doesn't quite cut it (as we need to match an actual float number)
-
- // matches, e.g.: +14.56e-12, etc.
- reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
-
- reViewBoxAttrValue = new RegExp(
- '^' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*,?' +
- '\\s*(' + reNum + '+)\\s*' +
- '$'
- );
-
- function hasAncestorWithNodeName(element, nodeName) {
- while (element && (element = element.parentNode)) {
- if (nodeName.test(element.nodeName)) {
- return true;
- }
- }
- return false;
- }
-
- return function(doc, callback, reviver) {
- if (!doc) {
- return;
- }
- var startTime = new Date();
-
- parseUseDirectives(doc);
- /* http://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
- * as per spec, width and height attributes are to be considered
- * 100% if no value is specified.
- */
- var viewBoxAttr = doc.getAttribute('viewBox'),
- widthAttr = parseUnit(doc.getAttribute('width') || '100%'),
- heightAttr = parseUnit(doc.getAttribute('height') || '100%'),
- viewBoxWidth,
- viewBoxHeight;
-
- if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
- var minX = parseFloat(viewBoxAttr[1]),
- minY = parseFloat(viewBoxAttr[2]),
- scaleX = 1, scaleY = 1;
- viewBoxWidth = parseFloat(viewBoxAttr[3]);
- viewBoxHeight = parseFloat(viewBoxAttr[4]);
- if (widthAttr && widthAttr !== viewBoxWidth ) {
- scaleX = widthAttr / viewBoxWidth;
- }
- if (heightAttr && heightAttr !== viewBoxHeight) {
- scaleY = heightAttr / viewBoxHeight;
- }
- addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
- }
-
- var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
-
- if (descendants.length === 0 && fabric.isLikelyNode) {
- // we're likely in node, where "o3-xml" library fails to gEBTN("*")
- // https://github.com/ajaxorg/node-o3-xml/issues/21
- descendants = doc.selectNodes('//*[name(.)!="svg"]');
- var arr = [ ];
- for (var i = 0, len = descendants.length; i < len; i++) {
- arr[i] = descendants[i];
- }
- descendants = arr;
- }
-
- var elements = descendants.filter(function(el) {
- return reAllowedSVGTagNames.test(el.tagName) &&
- !hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
- });
-
- if (!elements || (elements && !elements.length)) {
- callback && callback([], {});
- return;
- }
-
- var options = {
- width: widthAttr ? widthAttr : viewBoxWidth,
- height: heightAttr ? heightAttr : viewBoxHeight,
- widthAttr: widthAttr,
- heightAttr: heightAttr
- };
-
- fabric.gradientDefs = fabric.getGradientDefs(doc);
- fabric.cssRules = fabric.getCSSRules(doc);
- // Precedence of rules: style > class > attribute
-
- fabric.parseElements(elements, function(instances) {
- fabric.documentParsingTime = new Date() - startTime;
- if (callback) {
- callback(instances, options);
- }
- }, clone(options), reviver);
- };
- })();
-
- /**
- * Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
- * @namespace
- */
- var svgCache = {
-
- /**
- * @param {String} name
- * @param {Function} callback
- */
- has: function (name, callback) {
- callback(false);
- },
-
- get: function () {
- /* NOOP */
- },
-
- set: function () {
- /* NOOP */
- }
- };
-
- /**
- * @private
- */
- function _enlivenCachedObject(cachedObject) {
-
- var objects = cachedObject.objects,
- options = cachedObject.options;
-
- objects = objects.map(function (o) {
- return fabric[capitalize(o.type)].fromObject(o);
- });
-
- return ({ objects: objects, options: options });
- }
-
- /**
- * @private
- */
- function _createSVGPattern(markup, canvas, property) {
- if (canvas[property] && canvas[property].toSVG) {
- markup.push(
- '',
- ''
- );
- }
- }
-
- extend(fabric, {
-
- /**
- * Parses an SVG document, returning all of the gradient declarations found in it
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
- */
- getGradientDefs: function(doc) {
- var linearGradientEls = doc.getElementsByTagName('linearGradient'),
- radialGradientEls = doc.getElementsByTagName('radialGradient'),
- el, i, j = 0, id, xlink, elList = [ ],
- gradientDefs = { }, idsToXlinkMap = { };
-
- elList.length = linearGradientEls.length + radialGradientEls.length;
- i = linearGradientEls.length;
- while (i--) {
- elList[j++] = linearGradientEls[i];
- }
- i = radialGradientEls.length;
- while (i--) {
- elList[j++] = radialGradientEls[i];
- }
-
- while (j--) {
- el = elList[j];
- xlink = el.getAttribute('xlink:href');
- id = el.getAttribute('id');
- if (xlink) {
- idsToXlinkMap[id] = xlink.substr(1);
- }
- gradientDefs[id] = el;
- }
-
- for (id in idsToXlinkMap) {
- var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
- el = gradientDefs[id];
- while (el2.firstChild) {
- el.appendChild(el2.firstChild);
- }
- }
- return gradientDefs;
- },
-
- /**
- * Returns an object of attributes' name/value, given element and an array of attribute names;
- * Parses parent "g" nodes recursively upwards.
- * @static
- * @memberOf fabric
- * @param {DOMElement} element Element to parse
- * @param {Array} attributes Array of attributes to parse
- * @return {Object} object containing parsed attributes' names/values
- */
- parseAttributes: function(element, attributes) {
-
- if (!element) {
- return;
- }
-
- var value,
- parentAttributes = { };
-
- // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
- if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
- parentAttributes = fabric.parseAttributes(element.parentNode, attributes);
- }
-
- var ownAttributes = attributes.reduce(function(memo, attr) {
- value = element.getAttribute(attr);
- if (value) {
- attr = normalizeAttr(attr);
- value = normalizeValue(attr, value, parentAttributes);
-
- memo[attr] = value;
- }
- return memo;
- }, { });
-
- // add values parsed from style, which take precedence over attributes
- // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
- ownAttributes = extend(ownAttributes,
- extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
-
- return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
- },
-
- /**
- * Transforms an array of svg elements to corresponding fabric.* instances
- * @static
- * @memberOf fabric
- * @param {Array} elements Array of elements to parse
- * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
- * @param {Object} [options] Options object
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- */
- parseElements: function(elements, callback, options, reviver) {
- new fabric.ElementsParser(elements, callback, options, reviver).parse();
- },
-
- /**
- * Parses "style" attribute, retuning an object with values
- * @static
- * @memberOf fabric
- * @param {SVGElement} element Element to parse
- * @return {Object} Objects with values parsed from style attribute of an element
- */
- 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;
- },
-
- /**
- * Parses "points" attribute, returning an array of values
- * @static
- * @memberOf fabric
- * @param {String} points points attribute string
- * @return {Array} array of points
- */
- parsePointsAttribute: function(points) {
-
- // points attribute is required and must not be empty
- if (!points) {
- return null;
- }
-
- // replace commas with whitespace and remove bookending whitespace
- 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])
- });
- }
-
- // odd number of points is an error
- // if (parsedPoints.length % 2 !== 0) {
- // return null;
- // }
-
- return parsedPoints;
- },
-
- /**
- * Returns CSS rules for a given SVG document
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @return {Object} CSS rules of this document
- */
- getCSSRules: function(doc) {
- var styles = doc.getElementsByTagName('style'),
- allRules = { }, rules;
-
- // very crude parsing of style contents
- for (var i = 0, len = styles.length; i < len; i++) {
- var styleContents = styles[0].textContent;
-
- // remove comments
- styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
-
- rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
- rules = rules.map(function(rule) { return rule.trim(); });
-
- rules.forEach(function(rule) {
-
- var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
- ruleObj = { }, declaration = match[2].trim(),
- propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
-
- for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
- var pair = propertyValuePairs[i].split(/\s*:\s*/),
- property = normalizeAttr(pair[0]),
- value = normalizeValue(property,pair[1],pair[0]);
- ruleObj[property] = value;
- }
- rule = match[1];
- rule.split(',').forEach(function(_rule) {
- allRules[_rule.trim()] = fabric.util.object.clone(ruleObj);
- });
- });
- }
- return allRules;
- },
-
- /**
- * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
- * @memberof fabric
- * @param {String} url
- * @param {Function} callback
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- */
- loadSVGFromURL: function(url, callback, reviver) {
-
- url = url.replace(/^\n\s*/, '').trim();
- svgCache.has(url, function (hasUrl) {
- if (hasUrl) {
- svgCache.get(url, function (value) {
- var enlivedRecord = _enlivenCachedObject(value);
- callback(enlivedRecord.objects, enlivedRecord.options);
- });
- }
- else {
- new fabric.util.request(url, {
- method: 'get',
- onComplete: onComplete
- });
- }
- });
-
- function onComplete(r) {
-
- var xml = r.responseXML;
- if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
- xml = new ActiveXObject('Microsoft.XMLDOM');
- xml.async = 'false';
- //IE chokes on DOCTYPE
- xml.loadXML(r.responseText.replace(//i,''));
- }
- if (!xml || !xml.documentElement) {
- return;
- }
-
- fabric.parseSVGDocument(xml.documentElement, function (results, options) {
- svgCache.set(url, {
- objects: fabric.util.array.invoke(results, 'toObject'),
- options: options
- });
- callback(results, options);
- }, reviver);
- }
- },
-
- /**
- * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
- * @memberof fabric
- * @param {String} string
- * @param {Function} callback
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- */
- loadSVGFromString: function(string, callback, reviver) {
- string = string.trim();
- var doc;
- if (typeof DOMParser !== 'undefined') {
- var parser = new DOMParser();
- if (parser && parser.parseFromString) {
- doc = parser.parseFromString(string, 'text/xml');
- }
- }
- else if (fabric.window.ActiveXObject) {
- doc = new ActiveXObject('Microsoft.XMLDOM');
- doc.async = 'false';
- //IE chokes on DOCTYPE
- doc.loadXML(string.replace(//i,''));
- }
-
- fabric.parseSVGDocument(doc.documentElement, function (results, options) {
- callback(results, options);
- }, reviver);
- },
-
- /**
- * Creates markup containing SVG font faces
- * @param {Array} objects Array of fabric objects
- * @return {String}
- */
- createSVGFontFacesMarkup: function(objects) {
- var markup = '';
-
- for (var i = 0, len = objects.length; i < len; i++) {
- if (objects[i].type !== 'text' || !objects[i].path) {
- continue;
- }
-
- markup += [
- //jscs:disable validateIndentation
- '@font-face {',
- 'font-family: ', objects[i].fontFamily, '; ',
- 'src: url(\'', objects[i].path, '\')',
- '}'
- //jscs:enable validateIndentation
- ].join('');
- }
-
- if (markup) {
- markup = [
- //jscs:disable validateIndentation
- ''
- //jscs:enable validateIndentation
- ].join('');
- }
-
- return markup;
- },
-
- /**
- * Creates markup containing SVG referenced elements like patterns, gradients etc.
- * @param {fabric.Canvas} canvas instance of fabric.Canvas
- * @return {String}
- */
- createSVGRefElementsMarkup: function(canvas) {
- var markup = [ ];
-
- _createSVGPattern(markup, canvas, 'backgroundColor');
- _createSVGPattern(markup, canvas, 'overlayColor');
-
- return markup.join('');
- }
- });
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-fabric.ElementsParser = function(elements, callback, options, reviver) {
- this.elements = elements;
- this.callback = callback;
- this.options = options;
- this.reviver = reviver;
-};
-
-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++) {
- (function(_this, i) {
- setTimeout(function() {
- _this.createObject(_this.elements[i], i);
- }, 0);
- })(this, i);
- }
-};
-
-fabric.ElementsParser.prototype.createObject = function(el, index) {
- var klass = fabric[fabric.util.string.capitalize(el.tagName)];
- if (klass && klass.fromElement) {
- try {
- this._createObject(klass, el, index);
- }
- catch (err) {
- fabric.log(err);
- }
- }
- else {
- this.checkIfDone();
- }
-};
-
-fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
- if (klass.async) {
- klass.fromElement(el, this.createCallback(index, el), this.options);
- }
- else {
- var obj = klass.fromElement(el, this.options);
- this.resolveGradient(obj, 'fill');
- this.resolveGradient(obj, 'stroke');
- this.reviver && this.reviver(el, obj);
- this.instances[index] = obj;
- this.checkIfDone();
- }
-};
-
-fabric.ElementsParser.prototype.createCallback = function(index, el) {
- var _this = this;
- return function(obj) {
- _this.resolveGradient(obj, 'fill');
- _this.resolveGradient(obj, 'stroke');
- _this.reviver && _this.reviver(el, obj);
- _this.instances[index] = obj;
- _this.checkIfDone();
- };
-};
-
-fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
-
- var instanceFillValue = obj.get(property);
- if (!(/^url\(/).test(instanceFillValue)) {
- return;
- }
- var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
- if (fabric.gradientDefs[gradientId]) {
- obj.set(property,
- fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], obj));
- }
-};
-
-fabric.ElementsParser.prototype.checkIfDone = function() {
- if (--this.numElements === 0) {
- this.instances = this.instances.filter(function(el) {
- return el != null;
- });
- this.callback(this.instances);
- }
-};
-
-
-(function(global) {
-
- 'use strict';
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Point) {
- fabric.warn('fabric.Point is already defined');
- return;
- }
-
- fabric.Point = Point;
-
- /**
- * Point class
- * @class fabric.Point
- * @memberOf fabric
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @return {fabric.Point} thisArg
- */
- function Point(x, y) {
- this.x = x;
- this.y = y;
- }
-
- Point.prototype = /** @lends fabric.Point.prototype */ {
-
- constructor: Point,
-
- /**
- * Adds another point to this one and returns another one
- * @param {fabric.Point} that
- * @return {fabric.Point} new Point instance with added values
- */
- add: function (that) {
- return new Point(this.x + that.x, this.y + that.y);
- },
-
- /**
- * Adds another point to this one
- * @param {fabric.Point} that
- * @return {fabric.Point} thisArg
- */
- addEquals: function (that) {
- this.x += that.x;
- this.y += that.y;
- return this;
- },
-
- /**
- * Adds value to this point and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point} new Point with added value
- */
- scalarAdd: function (scalar) {
- return new Point(this.x + scalar, this.y + scalar);
- },
-
- /**
- * Adds value to this point
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- */
- scalarAddEquals: function (scalar) {
- this.x += scalar;
- this.y += scalar;
- return this;
- },
-
- /**
- * Subtracts another point from this point and returns a new one
- * @param {fabric.Point} that
- * @return {fabric.Point} new Point object with subtracted values
- */
- subtract: function (that) {
- return new Point(this.x - that.x, this.y - that.y);
- },
-
- /**
- * Subtracts another point from this point
- * @param {fabric.Point} that
- * @return {fabric.Point} thisArg
- */
- subtractEquals: function (that) {
- this.x -= that.x;
- this.y -= that.y;
- return this;
- },
-
- /**
- * Subtracts value from this point and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- scalarSubtract: function (scalar) {
- return new Point(this.x - scalar, this.y - scalar);
- },
-
- /**
- * Subtracts value from this point
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- */
- scalarSubtractEquals: function (scalar) {
- this.x -= scalar;
- this.y -= scalar;
- return this;
- },
-
- /**
- * Miltiplies this point by a value and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- multiply: function (scalar) {
- return new Point(this.x * scalar, this.y * scalar);
- },
-
- /**
- * Miltiplies this point by a value
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- */
- multiplyEquals: function (scalar) {
- this.x *= scalar;
- this.y *= scalar;
- return this;
- },
-
- /**
- * Divides this point by a value and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- divide: function (scalar) {
- return new Point(this.x / scalar, this.y / scalar);
- },
-
- /**
- * Divides this point by a value
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- */
- divideEquals: function (scalar) {
- this.x /= scalar;
- this.y /= scalar;
- return this;
- },
-
- /**
- * Returns true if this point is equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- eq: function (that) {
- return (this.x === that.x && this.y === that.y);
- },
-
- /**
- * Returns true if this point is less than another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- lt: function (that) {
- return (this.x < that.x && this.y < that.y);
- },
-
- /**
- * Returns true if this point is less than or equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- lte: function (that) {
- return (this.x <= that.x && this.y <= that.y);
- },
-
- /**
-
- * Returns true if this point is greater another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- gt: function (that) {
- return (this.x > that.x && this.y > that.y);
- },
-
- /**
- * Returns true if this point is greater than or equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- gte: function (that) {
- return (this.x >= that.x && this.y >= that.y);
- },
-
- /**
- * Returns new point which is the result of linear interpolation with this one and another one
- * @param {fabric.Point} that
- * @param {Number} t
- * @return {fabric.Point}
- */
- lerp: function (that, t) {
- return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
- },
-
- /**
- * Returns distance from this point and another one
- * @param {fabric.Point} that
- * @return {Number}
- */
- distanceFrom: function (that) {
- var dx = this.x - that.x,
- dy = this.y - that.y;
- return Math.sqrt(dx * dx + dy * dy);
- },
-
- /**
- * Returns the point between this point and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- midPointFrom: function (that) {
- return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2);
- },
-
- /**
- * Returns a new point which is the min of this and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- min: function (that) {
- return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
- },
-
- /**
- * Returns a new point which is the max of this and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- max: function (that) {
- return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
- },
-
- /**
- * Returns string representation of this point
- * @return {String}
- */
- toString: function () {
- return this.x + ',' + this.y;
- },
-
- /**
- * Sets x/y of this point
- * @param {Number} x
- * @return {Number} y
- */
- setXY: function (x, y) {
- this.x = x;
- this.y = y;
- },
-
- /**
- * Sets x/y of this point from another point
- * @param {fabric.Point} that
- */
- setFromPoint: function (that) {
- this.x = that.x;
- this.y = that.y;
- },
-
- /**
- * Swaps x/y of this point and another point
- * @param {fabric.Point} that
- */
- swap: function (that) {
- var x = this.x,
- y = this.y;
- this.x = that.x;
- this.y = that.y;
- that.x = x;
- that.y = y;
- }
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
- 'use strict';
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Intersection) {
- fabric.warn('fabric.Intersection is already defined');
- return;
- }
-
- /**
- * Intersection class
- * @class fabric.Intersection
- * @memberOf fabric
- * @constructor
- */
- function Intersection(status) {
- this.status = status;
- this.points = [];
- }
-
- fabric.Intersection = Intersection;
-
- fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
- /**
- * Appends a point to intersection
- * @param {fabric.Point} point
- */
- appendPoint: function (point) {
- this.points.push(point);
- },
-
- /**
- * Appends points to intersection
- * @param {Array} points
- */
- appendPoints: function (points) {
- this.points = this.points.concat(points);
- }
- };
-
- /**
- * Checks if one line intersects another
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {fabric.Point} b1
- * @param {fabric.Point} b2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
- var result,
- uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
- ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
- uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
- if (uB !== 0) {
- var ua = uaT / uB,
- ub = ubT / uB;
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- result = new Intersection('Intersection');
- result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
- }
- else {
- result = new Intersection();
- }
- }
- else {
- if (uaT === 0 || ubT === 0) {
- result = new Intersection('Coincident');
- }
- else {
- result = new Intersection('Parallel');
- }
- }
- return result;
- };
-
- /**
- * Checks if line intersects polygon
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {Array} points
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
- var result = new Intersection(),
- length = points.length;
-
- for (var i = 0; i < length; i++) {
- var b1 = points[i],
- b2 = points[(i + 1) % length],
- inter = Intersection.intersectLineLine(a1, a2, b1, b2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects another polygon
- * @static
- * @param {Array} points1
- * @param {Array} points2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
- var result = new Intersection(),
- length = points1.length;
-
- for (var i = 0; i < length; i++) {
- var a1 = points1[i],
- a2 = points1[(i + 1) % length],
- inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects rectangle
- * @static
- * @param {Array} points
- * @param {Number} r1
- * @param {Number} r2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
- var min = r1.min(r2),
- max = r1.max(r2),
- topRight = new fabric.Point(max.x, min.y),
- bottomLeft = new fabric.Point(min.x, max.y),
- inter1 = Intersection.intersectLinePolygon(min, topRight, points),
- inter2 = Intersection.intersectLinePolygon(topRight, max, points),
- inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
- inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
- result = new Intersection();
-
- result.appendPoints(inter1.points);
- result.appendPoints(inter2.points);
- result.appendPoints(inter3.points);
- result.appendPoints(inter4.points);
-
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
- 'use strict';
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Color) {
- fabric.warn('fabric.Color is already defined.');
- return;
- }
-
- /**
- * Color class
- * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
- * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
- *
- * @class fabric.Color
- * @param {String} color optional in hex or rgb(a) format
- * @return {fabric.Color} thisArg
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
- */
- function Color(color) {
- if (!color) {
- this.setSource([0, 0, 0, 1]);
- }
- else {
- this._tryParsingColor(color);
- }
- }
-
- fabric.Color = Color;
-
- fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
-
- /**
- * @private
- * @param {String|Array} color Color value to parse
- */
- _tryParsingColor: function(color) {
- var source;
-
- if (color in Color.colorNameMap) {
- color = Color.colorNameMap[color];
- }
-
- if (color === 'transparent') {
- this.setSource([255,255,255,0]);
- return;
- }
-
- source = Color.sourceFromHex(color);
-
- if (!source) {
- source = Color.sourceFromRgb(color);
- }
- if (!source) {
- source = Color.sourceFromHsl(color);
- }
- if (source) {
- this.setSource(source);
- }
- },
-
- /**
- * Adapted from https://github.com/mjijackson
- * @private
- * @param {Number} r Red color value
- * @param {Number} g Green color value
- * @param {Number} b Blue color value
- * @return {Array} Hsl color
- */
- _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; // achromatic
- }
- else {
- var d = max - min;
- s = l > 0.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)
- ];
- },
-
- /**
- * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
- * @return {Array}
- */
- getSource: function() {
- return this._source;
- },
-
- /**
- * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
- * @param {Array} source
- */
- setSource: function(source) {
- this._source = source;
- },
-
- /**
- * Returns color represenation in RGB format
- * @return {String} ex: rgb(0-255,0-255,0-255)
- */
- toRgb: function() {
- var source = this.getSource();
- return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
- },
-
- /**
- * Returns color represenation in RGBA format
- * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
- */
- toRgba: function() {
- var source = this.getSource();
- return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
- },
-
- /**
- * Returns color represenation in HSL format
- * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
- */
- toHsl: function() {
- var source = this.getSource(),
- hsl = this._rgbToHsl(source[0], source[1], source[2]);
-
- return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
- },
-
- /**
- * Returns color represenation in HSLA format
- * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
- */
- 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] + ')';
- },
-
- /**
- * Returns color represenation in HEX format
- * @return {String} ex: FF5555
- */
- 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();
- },
-
- /**
- * Gets value of alpha channel for this color
- * @return {Number} 0-1
- */
- getAlpha: function() {
- return this.getSource()[3];
- },
-
- /**
- * Sets value of alpha channel for this color
- * @param {Number} alpha Alpha value 0-1
- * @return {fabric.Color} thisArg
- */
- setAlpha: function(alpha) {
- var source = this.getSource();
- source[3] = alpha;
- this.setSource(source);
- return this;
- },
-
- /**
- * Transforms color to its grayscale representation
- * @return {fabric.Color} thisArg
- */
- toGrayscale: function() {
- var source = this.getSource(),
- average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
- currentAlpha = source[3];
- this.setSource([average, average, average, currentAlpha]);
- return this;
- },
-
- /**
- * Transforms color to its black and white representation
- * @param {Number} threshold
- * @return {fabric.Color} thisArg
- */
- toBlackWhite: function(threshold) {
- var source = this.getSource(),
- average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
- currentAlpha = source[3];
-
- threshold = threshold || 127;
-
- average = (Number(average) < Number(threshold)) ? 0 : 255;
- this.setSource([average, average, average, currentAlpha]);
- return this;
- },
-
- /**
- * Overlays color with another color
- * @param {String|fabric.Color} otherColor
- * @return {fabric.Color} thisArg
- */
- overlayWith: function(otherColor) {
- if (!(otherColor instanceof Color)) {
- otherColor = new Color(otherColor);
- }
-
- var result = [],
- alpha = this.getAlpha(),
- otherAlpha = 0.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;
- }
- };
-
- /**
- * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
- * @static
- * @field
- * @memberOf fabric.Color
- */
- fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
-
- /**
- * Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
- * @static
- * @field
- * @memberOf fabric.Color
- */
- fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
-
- /**
- * Regex matching color in HEX format (ex: #FF5555, 010155, aff)
- * @static
- * @field
- * @memberOf fabric.Color
- */
- fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
-
- /**
- * Map of the 17 basic color names with HEX code
- * @static
- * @field
- * @memberOf fabric.Color
- * @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
- */
- fabric.Color.colorNameMap = {
- aqua: '#00FFFF',
- black: '#000000',
- blue: '#0000FF',
- fuchsia: '#FF00FF',
- gray: '#808080',
- green: '#008000',
- lime: '#00FF00',
- maroon: '#800000',
- navy: '#000080',
- olive: '#808000',
- orange: '#FFA500',
- purple: '#800080',
- red: '#FF0000',
- silver: '#C0C0C0',
- teal: '#008080',
- white: '#FFFFFF',
- yellow: '#FFFF00'
- };
-
- /**
- * @private
- * @param {Number} p
- * @param {Number} q
- * @param {Number} t
- * @return {Number}
- */
- function hue2rgb(p, q, t){
- if (t < 0) {
- t += 1;
- }
- if (t > 1) {
- t -= 1;
- }
- if (t < 1/6) {
- return p + (q - p) * 6 * t;
- }
- if (t < 1/2) {
- return q;
- }
- if (t < 2/3) {
- return p + (q - p) * (2/3 - t) * 6;
- }
- return p;
- }
-
- /**
- * Returns new color object, when given a color in RGB format
- * @memberOf fabric.Color
- * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
- * @return {fabric.Color}
- */
- fabric.Color.fromRgb = function(color) {
- return Color.fromSource(Color.sourceFromRgb(color));
- };
-
- /**
- * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
- * @memberOf fabric.Color
- * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
- * @return {Array} source
- */
- 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
- ];
- }
- };
-
- /**
- * Returns new color object, when given a color in RGBA format
- * @static
- * @function
- * @memberOf fabric.Color
- * @param {String} color
- * @return {fabric.Color}
- */
- fabric.Color.fromRgba = Color.fromRgb;
-
- /**
- * Returns new color object, when given a color in HSL format
- * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
- * @memberOf fabric.Color
- * @return {fabric.Color}
- */
- fabric.Color.fromHsl = function(color) {
- return Color.fromSource(Color.sourceFromHsl(color));
- };
-
- /**
- * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
- * Adapted from https://github.com/mjijackson
- * @memberOf fabric.Color
- * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
- * @return {Array} source
- * @see http://http://www.w3.org/TR/css3-color/#hsl-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 <= 0.5 ? l * (s + 1) : l + s - l * s,
- p = l * 2 - q;
-
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- 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
- ];
- };
-
- /**
- * Returns new color object, when given a color in HSLA format
- * @static
- * @function
- * @memberOf fabric.Color
- * @param {String} color
- * @return {fabric.Color}
- */
- fabric.Color.fromHsla = Color.fromHsl;
-
- /**
- * Returns new color object, when given a color in HEX format
- * @static
- * @memberOf fabric.Color
- * @param {String} color Color value ex: FF5555
- * @return {fabric.Color}
- */
- fabric.Color.fromHex = function(color) {
- return Color.fromSource(Color.sourceFromHex(color));
- };
-
- /**
- * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HEX format
- * @static
- * @memberOf fabric.Color
- * @param {String} color ex: FF5555
- * @return {Array} source
- */
- fabric.Color.sourceFromHex = function(color) {
- if (color.match(Color.reHex)) {
- var value = color.slice(color.indexOf('#') + 1),
- isShortNotation = (value.length === 3),
- r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
- g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
- b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6);
-
- return [
- parseInt(r, 16),
- parseInt(g, 16),
- parseInt(b, 16),
- 1
- ];
- }
- };
-
- /**
- * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
- * @static
- * @memberOf fabric.Color
- * @param {Array} source
- * @return {fabric.Color}
- */
- fabric.Color.fromSource = function(source) {
- var oColor = new Color();
- oColor.setSource(source);
- return oColor;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
- /* _FROM_SVG_START_ */
- function getColorStop(el) {
- var style = el.getAttribute('style'),
- offset = el.getAttribute('offset'),
- color, colorAlpha, opacity;
-
- // convert percents to absolute values
- 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%'
- };
- }
- /* _FROM_SVG_END_ */
-
- /**
- * Gradient class
- * @class fabric.Gradient
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
- * @see {@link fabric.Gradient#initialize} for constructor definition
- */
- fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
-
- /**
- * Horizontal offset for aligning gradients coming from SVG when outside pathgroups
- * @type Number
- * @default 0
- */
- offsetX: 0,
-
- /**
- * Vertical offset for aligning gradients coming from SVG when outside pathgroups
- * @type Number
- * @default 0
- */
- offsetY: 0,
-
- /**
- * Constructor
- * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
- * @return {fabric.Gradient} thisArg
- */
- 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;
- },
-
- /**
- * Adds another colorStop
- * @param {Object} colorStop Object with offset and color
- * @return {fabric.Gradient} thisArg
- */
- addColorStop: function(colorStop) {
- for (var position in colorStop) {
- var color = new fabric.Color(colorStop[position]);
- this.colorStops.push({
- offset: position,
- color: color.toRgb(),
- opacity: color.getAlpha()
- });
- }
- return this;
- },
-
- /**
- * Returns object representation of a gradient
- * @return {Object}
- */
- toObject: function() {
- return {
- type: this.type,
- coords: this.coords,
- colorStops: this.colorStops,
- offsetX: this.offsetX,
- offsetY: this.offsetY
- };
- },
-
- /* _TO_SVG_START_ */
- /**
- * Returns SVG representation of an gradient
- * @param {Object} object Object to create a gradient for
- * @param {Boolean} normalize Whether coords should be normalized
- * @return {String} SVG representation of an gradient (linear/radial)
- */
- toSVG: function(object) {
- var coords = fabric.util.object.clone(this.coords),
- markup, commonAttributes;
-
- // colorStops must be sorted ascending
- this.colorStops.sort(function(a, b) {
- return a.offset - b.offset;
- });
-
- if (!(object.group && object.group.type === 'path-group')) {
- for (var prop in coords) {
- if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
- coords[prop] += this.offsetX - object.width / 2;
- }
- else if (prop === 'y1' || prop === 'y2') {
- coords[prop] += this.offsetY - object.height / 2;
- }
- }
- }
-
- commonAttributes = 'id="SVGID_' + this.id +
- '" gradientUnits="userSpaceOnUse"';
- if (this.gradientTransform) {
- commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
- }
- if (this.type === 'linear') {
- markup = [
- //jscs:disable validateIndentation
- '\n'
- //jscs:enable validateIndentation
- ];
- }
- else if (this.type === 'radial') {
- markup = [
- //jscs:disable validateIndentation
- '\n'
- //jscs:enable validateIndentation
- ];
- }
-
- for (var i = 0; i < this.colorStops.length; i++) {
- markup.push(
- //jscs:disable validateIndentation
- '\n'
- //jscs:enable validateIndentation
- );
- }
-
- markup.push((this.type === 'linear' ? '\n' : '\n'));
-
- return markup.join('');
- },
- /* _TO_SVG_END_ */
-
- /**
- * Returns an instance of CanvasGradient
- * @param {CanvasRenderingContext2D} ctx Context to render on
- * @return {CanvasGradient}
- */
- toLive: function(ctx) {
- var gradient;
-
- if (!this.type) {
- return;
- }
-
- if (this.type === 'linear') {
- gradient = ctx.createLinearGradient(
- this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
- }
- else if (this.type === 'radial') {
- gradient = ctx.createRadialGradient(
- this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
- }
-
- for (var i = 0, len = this.colorStops.length; i < len; i++) {
- var color = this.colorStops[i].color,
- opacity = this.colorStops[i].opacity,
- offset = this.colorStops[i].offset;
-
- if (typeof opacity !== 'undefined') {
- color = new fabric.Color(color).setAlpha(opacity).toRgba();
- }
- gradient.addColorStop(parseFloat(offset), color);
- }
-
- return gradient;
- }
- });
-
- fabric.util.object.extend(fabric.Gradient, {
-
- /* _FROM_SVG_START_ */
- /**
- * Returns {@link fabric.Gradient} instance from an SVG element
- * @static
- * @memberof fabric.Gradient
- * @param {SVGGradientElement} el SVG gradient element
- * @param {fabric.Object} instance
- * @return {fabric.Gradient} Gradient instance
- * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
- * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
- */
- fromElement: function(el, instance) {
-
- /**
- * @example:
- *
- *
- *
- *
- *
- *
- * OR
- *
- *
- *
- *
- *
- *
- * OR
- *
- *
- *
- *
- *
- *
- *
- * OR
- *
- *
- *
- *
- *
- *
- *
- */
-
- var colorStopEls = el.getElementsByTagName('stop'),
- type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
- gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
- gradientTransform = el.getAttribute('gradientTransform'),
- colorStops = [],
- coords = { }, ellipseMatrix;
-
- if (type === 'linear') {
- coords = getLinearCoords(el);
- }
- else if (type === 'radial') {
- coords = getRadialCoords(el);
- }
-
- for (var i = colorStopEls.length; i--; ) {
- colorStops.push(getColorStop(colorStopEls[i]));
- }
-
- ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
-
- var gradient = new fabric.Gradient({
- type: type,
- coords: coords,
- colorStops: colorStops,
- offsetX: -instance.left,
- offsetY: -instance.top
- });
-
- if (gradientTransform || ellipseMatrix !== '') {
- gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix);
- }
- return gradient;
- },
- /* _FROM_SVG_END_ */
-
- /**
- * Returns {@link fabric.Gradient} instance from its object representation
- * @static
- * @memberof fabric.Gradient
- * @param {Object} obj
- * @param {Object} [options] Options object
- */
- forObject: function(obj, options) {
- options || (options = { });
- _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse');
- return new fabric.Gradient(options);
- }
- });
-
- /**
- * @private
- */
- function _convertPercentUnitsToValues(object, options, gradientUnits) {
- var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = '';
- for (var prop in options) {
- propValue = parseFloat(options[prop], 10);
- if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
- multFactor = 0.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;
- }
-})();
-
-
-/**
- * Pattern class
- * @class fabric.Pattern
- * @see {@link http://fabricjs.com/patterns/|Pattern demo}
- * @see {@link http://fabricjs.com/dynamic-patterns/|DynamicPattern demo}
- * @see {@link fabric.Pattern#initialize} for constructor definition
- */
-fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ {
-
- /**
- * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
- * @type String
- * @default
- */
- repeat: 'repeat',
-
- /**
- * Pattern horizontal offset from object's left/top corner
- * @type Number
- * @default
- */
- offsetX: 0,
-
- /**
- * Pattern vertical offset from object's left/top corner
- * @type Number
- * @default
- */
- offsetY: 0,
-
- /**
- * Constructor
- * @param {Object} [options] Options object
- * @return {fabric.Pattern} thisArg
- */
- initialize: function(options) {
- options || (options = { });
-
- this.id = fabric.Object.__uid++;
-
- if (options.source) {
- if (typeof options.source === 'string') {
- // function string
- if (typeof fabric.util.getFunctionBody(options.source) !== 'undefined') {
- this.source = new Function(fabric.util.getFunctionBody(options.source));
- }
- else {
- // img src string
- var _this = this;
- this.source = fabric.util.createImage();
- fabric.util.loadImage(options.source, function(img) {
- _this.source = img;
- });
- }
- }
- else {
- // img element
- this.source = options.source;
- }
- }
- if (options.repeat) {
- this.repeat = options.repeat;
- }
- if (options.offsetX) {
- this.offsetX = options.offsetX;
- }
- if (options.offsetY) {
- this.offsetY = options.offsetY;
- }
- },
-
- /**
- * Returns object representation of a pattern
- * @return {Object} Object representation of a pattern instance
- */
- toObject: function() {
-
- var source;
-
- // callback
- if (typeof this.source === 'function') {
- source = String(this.source);
- }
- //
element
- else if (typeof this.source.src === 'string') {
- source = this.source.src;
- }
-
- return {
- source: source,
- repeat: this.repeat,
- offsetX: this.offsetX,
- offsetY: this.offsetY
- };
- },
-
- /* _TO_SVG_START_ */
- /**
- * Returns SVG representation of a pattern
- * @param {fabric.Object} object
- * @return {String} SVG representation of a pattern
- */
- toSVG: function(object) {
- var patternSource = typeof this.source === 'function' ? this.source() : this.source,
- patternWidth = patternSource.width / object.getWidth(),
- patternHeight = patternSource.height / object.getHeight(),
- patternImgSrc = '';
-
- if (patternSource.src) {
- patternImgSrc = patternSource.src;
- }
- else if (patternSource.toDataURL) {
- patternImgSrc = patternSource.toDataURL();
- }
-
- return '' +
- '' +
- '';
- },
- /* _TO_SVG_END_ */
-
- /**
- * Returns an instance of CanvasPattern
- * @param {CanvasRenderingContext2D} ctx Context to create pattern
- * @return {CanvasPattern}
- */
- toLive: function(ctx) {
- var source = typeof this.source === 'function'
- ? this.source()
- : this.source;
-
- // if the image failed to load, return, and allow rest to continue loading
- if (!source) {
- return '';
- }
-
- // if an image
- if (typeof source.src !== 'undefined') {
- if (!source.complete) {
- return '';
- }
- if (source.naturalWidth === 0 || source.naturalHeight === 0) {
- return '';
- }
- }
- return ctx.createPattern(source, this.repeat);
- }
-});
-
-
-(function(global) {
-
- 'use strict';
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Shadow) {
- fabric.warn('fabric.Shadow is already defined.');
- return;
- }
-
- /**
- * Shadow class
- * @class fabric.Shadow
- * @see {@link http://fabricjs.com/shadows/|Shadow demo}
- * @see {@link fabric.Shadow#initialize} for constructor definition
- */
- fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
-
- /**
- * Shadow color
- * @type String
- * @default
- */
- color: 'rgb(0,0,0)',
-
- /**
- * Shadow blur
- * @type Number
- */
- blur: 0,
-
- /**
- * Shadow horizontal offset
- * @type Number
- * @default
- */
- offsetX: 0,
-
- /**
- * Shadow vertical offset
- * @type Number
- * @default
- */
- offsetY: 0,
-
- /**
- * Whether the shadow should affect stroke operations
- * @type Boolean
- * @default
- */
- affectStroke: false,
-
- /**
- * Indicates whether toObject should include default values
- * @type Boolean
- * @default
- */
- includeDefaultValues: true,
-
- /**
- * Constructor
- * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
- * @return {fabric.Shadow} thisArg
- */
- 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++;
- },
-
- /**
- * @private
- * @param {String} shadow Shadow value to parse
- * @return {Object} Shadow object with color, offsetX, offsetY and blur
- */
- _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
- };
- },
-
- /**
- * Returns a string representation of an instance
- * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
- * @return {String} Returns CSS3 text-shadow declaration
- */
- toString: function() {
- return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
- },
-
- /* _TO_SVG_START_ */
- /**
- * Returns SVG representation of a shadow
- * @param {fabric.Object} object
- * @return {String} SVG representation of a shadow
- */
- toSVG: function(object) {
- var mode = 'SourceAlpha';
-
- if (object && (object.fill === this.color || object.stroke === this.color)) {
- mode = 'SourceGraphic';
- }
-
- return (
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '');
- },
- /* _TO_SVG_END_ */
-
- /**
- * Returns object representation of a shadow
- * @return {Object} Object representation of a shadow instance
- */
- toObject: function() {
- if (this.includeDefaultValues) {
- return {
- color: this.color,
- blur: this.blur,
- offsetX: this.offsetX,
- offsetY: this.offsetY
- };
- }
- var obj = { }, proto = fabric.Shadow.prototype;
- if (this.color !== proto.color) {
- obj.color = this.color;
- }
- if (this.blur !== proto.blur) {
- obj.blur = this.blur;
- }
- if (this.offsetX !== proto.offsetX) {
- obj.offsetX = this.offsetX;
- }
- if (this.offsetY !== proto.offsetY) {
- obj.offsetY = this.offsetY;
- }
- return obj;
- }
- });
-
- /**
- * Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
- * @static
- * @field
- * @memberOf fabric.Shadow
- */
- 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;
- }
-
- // aliases for faster resolution
- var extend = fabric.util.object.extend,
- getElementOffset = fabric.util.getElementOffset,
- removeFromArray = fabric.util.removeFromArray,
-
- CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
-
- /**
- * Static canvas class
- * @class fabric.StaticCanvas
- * @mixes fabric.Collection
- * @mixes fabric.Observable
- * @see {@link http://fabricjs.com/static_canvas/|StaticCanvas demo}
- * @see {@link fabric.StaticCanvas#initialize} for constructor definition
- * @fires before:render
- * @fires after:render
- * @fires canvas:cleared
- * @fires object:added
- * @fires object:removed
- */
- fabric.StaticCanvas = fabric.util.createClass(/** @lends fabric.StaticCanvas.prototype */ {
-
- /**
- * Constructor
- * @param {HTMLElement | String} el <canvas> element to initialize instance on
- * @param {Object} [options] Options object
- * @return {Object} thisArg
- */
- initialize: function(el, options) {
- options || (options = { });
-
- this._initStatic(el, options);
- fabric.StaticCanvas.activeInstance = this;
- },
-
- /**
- * Background color of canvas instance.
- * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
- * @type {(String|fabric.Pattern)}
- * @default
- */
- backgroundColor: '',
-
- /**
- * Background image of canvas instance.
- * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
- * Backwards incompatibility note: The "backgroundImageOpacity"
- * and "backgroundImageStretch" properties are deprecated since 1.3.9.
- * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
- * @type fabric.Image
- * @default
- */
- backgroundImage: null,
-
- /**
- * Overlay color of canvas instance.
- * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
- * @since 1.3.9
- * @type {(String|fabric.Pattern)}
- * @default
- */
- overlayColor: '',
-
- /**
- * Overlay image of canvas instance.
- * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
- * Backwards incompatibility note: The "overlayImageLeft"
- * and "overlayImageTop" properties are deprecated since 1.3.9.
- * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
- * @type fabric.Image
- * @default
- */
- overlayImage: null,
-
- /**
- * Indicates whether toObject/toDatalessObject should include default values
- * @type Boolean
- * @default
- */
- includeDefaultValues: true,
-
- /**
- * Indicates whether objects' state should be saved
- * @type Boolean
- * @default
- */
- stateful: true,
-
- /**
- * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove} should also re-render canvas.
- * Disabling this option could give a great performance boost when adding/removing a lot of objects to/from canvas at once
- * (followed by a manual rendering after addition/deletion)
- * @type Boolean
- * @default
- */
- renderOnAddRemove: true,
-
- /**
- * Function that determines clipping of entire canvas area
- * Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
- * @type Function
- * @default
- */
- clipTo: null,
-
- /**
- * Indicates whether object controls (borders/controls) are rendered above overlay image
- * @type Boolean
- * @default
- */
- controlsAboveOverlay: false,
-
- /**
- * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
- * @type Boolean
- * @default
- */
- allowTouchScrolling: false,
-
- /**
- * Indicates whether this canvas will use image smoothing, this is on by default in browsers
- * @type Boolean
- * @default
- */
- imageSmoothingEnabled: true,
-
- /**
- * The transformation (in the format of Canvas transform) which focuses the viewport
- * @type Array
- * @default
- */
- viewportTransform: [1, 0, 0, 1, 0, 0],
-
- /**
- * Callback; invoked right before object is about to be scaled/rotated
- */
- onBeforeScaleRotate: function () {
- /* NOOP */
- },
-
- /**
- * @private
- * @param {HTMLElement | String} el <canvas> element to initialize instance on
- * @param {Object} [options] Options object
- */
- _initStatic: function(el, options) {
- this._objects = [];
-
- this._createLowerCanvas(el);
- this._initOptions(options);
- this._setImageSmoothing();
-
- if (options.overlayImage) {
- this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
- }
- if (options.backgroundImage) {
- this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
- }
- if (options.backgroundColor) {
- this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
- }
- if (options.overlayColor) {
- this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
- }
- this.calcOffset();
- },
-
- /**
- * Calculates canvas element offset relative to the document
- * This method is also attached as "resize" event handler of window
- * @return {fabric.Canvas} instance
- * @chainable
- */
- calcOffset: function () {
- this._offset = getElementOffset(this.lowerCanvasEl);
- return this;
- },
-
- /**
- * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas
- * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to
- * @param {Function} callback callback to invoke when image is loaded and set as an overlay
- * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.
- * @return {fabric.Canvas} thisArg
- * @chainable
- * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
- * @example Normal overlayImage with left/top = 0
- * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
- * // Needed to position overlayImage at 0/0
- * originX: 'left',
- * originY: 'top'
- * });
- * @example overlayImage with different properties
- * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
- * opacity: 0.5,
- * angle: 45,
- * left: 400,
- * top: 400,
- * originX: 'left',
- * originY: 'top'
- * });
- * @example Stretched overlayImage #1 - width/height correspond to canvas width/height
- * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
- * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
- * canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
- * });
- * @example Stretched overlayImage #2 - width/height correspond to canvas width/height
- * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
- * width: canvas.width,
- * height: canvas.height,
- * // Needed to position overlayImage at 0/0
- * originX: 'left',
- * originY: 'top'
- * });
- */
- setOverlayImage: function (image, callback, options) {
- return this.__setBgOverlayImage('overlayImage', image, callback, options);
- },
-
- /**
- * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas
- * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to
- * @param {Function} callback Callback to invoke when image is loaded and set as background
- * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.
- * @return {fabric.Canvas} thisArg
- * @chainable
- * @see {@link http://jsfiddle.net/fabricjs/YH9yD/|jsFiddle demo}
- * @example Normal backgroundImage with left/top = 0
- * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
- * // Needed to position backgroundImage at 0/0
- * originX: 'left',
- * originY: 'top'
- * });
- * @example backgroundImage with different properties
- * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
- * opacity: 0.5,
- * angle: 45,
- * left: 400,
- * top: 400,
- * originX: 'left',
- * originY: 'top'
- * });
- * @example Stretched backgroundImage #1 - width/height correspond to canvas width/height
- * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
- * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
- * canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
- * });
- * @example Stretched backgroundImage #2 - width/height correspond to canvas width/height
- * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
- * width: canvas.width,
- * height: canvas.height,
- * // Needed to position backgroundImage at 0/0
- * originX: 'left',
- * originY: 'top'
- * });
- */
- setBackgroundImage: function (image, callback, options) {
- return this.__setBgOverlayImage('backgroundImage', image, callback, options);
- },
-
- /**
- * Sets {@link fabric.StaticCanvas#overlayColor|background color} for this canvas
- * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set background color to
- * @param {Function} callback Callback to invoke when background color is set
- * @return {fabric.Canvas} thisArg
- * @chainable
- * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
- * @example Normal overlayColor - color value
- * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
- * @example fabric.Pattern used as overlayColor
- * canvas.setOverlayColor({
- * source: 'http://fabricjs.com/assets/escheresque_ste.png'
- * }, canvas.renderAll.bind(canvas));
- * @example fabric.Pattern used as overlayColor with repeat and offset
- * canvas.setOverlayColor({
- * source: 'http://fabricjs.com/assets/escheresque_ste.png',
- * repeat: 'repeat',
- * offsetX: 200,
- * offsetY: 100
- * }, canvas.renderAll.bind(canvas));
- */
- setOverlayColor: function(overlayColor, callback) {
- return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
- },
-
- /**
- * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
- * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
- * @param {Function} callback Callback to invoke when background color is set
- * @return {fabric.Canvas} thisArg
- * @chainable
- * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
- * @example Normal backgroundColor - color value
- * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
- * @example fabric.Pattern used as backgroundColor
- * canvas.setBackgroundColor({
- * source: 'http://fabricjs.com/assets/escheresque_ste.png'
- * }, canvas.renderAll.bind(canvas));
- * @example fabric.Pattern used as backgroundColor with repeat and offset
- * canvas.setBackgroundColor({
- * source: 'http://fabricjs.com/assets/escheresque_ste.png',
- * repeat: 'repeat',
- * offsetX: 200,
- * offsetY: 100
- * }, canvas.renderAll.bind(canvas));
- */
- setBackgroundColor: function(backgroundColor, callback) {
- return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
- },
-
- /**
- * @private
- * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
- */
- _setImageSmoothing: function(){
- var ctx = this.getContext();
-
- ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
- ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
- ctx.mozImageSmoothingEnabled = this.imageSmoothingEnabled;
- ctx.msImageSmoothingEnabled = this.imageSmoothingEnabled;
- ctx.oImageSmoothingEnabled = this.imageSmoothingEnabled;
- },
-
- /**
- * @private
- * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
- * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
- * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
- * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
- * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
- */
- __setBgOverlayImage: function(property, image, callback, options) {
- if (typeof image === 'string') {
- fabric.util.loadImage(image, function(img) {
- this[property] = new fabric.Image(img, options);
- callback && callback();
- }, this);
- }
- else {
- this[property] = image;
- callback && callback();
- }
-
- return this;
- },
-
- /**
- * @private
- * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
- * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
- * @param {(Object|String|null)} color Object with pattern information, color value or null
- * @param {Function} [callback] Callback is invoked when color is set
- */
- __setBgOverlayColor: function(property, color, callback) {
- if (color && color.source) {
- var _this = this;
- fabric.util.loadImage(color.source, function(img) {
- _this[property] = new fabric.Pattern({
- source: img,
- repeat: color.repeat,
- offsetX: color.offsetX,
- offsetY: color.offsetY
- });
- callback && callback();
- });
- }
- else {
- this[property] = color;
- callback && callback();
- }
-
- return this;
- },
-
- /**
- * @private
- */
- _createCanvasElement: function() {
- var element = fabric.document.createElement('canvas');
- if (!element.style) {
- element.style = { };
- }
- if (!element) {
- throw CANVAS_INIT_ERROR;
- }
- this._initCanvasElement(element);
- return element;
- },
-
- /**
- * @private
- * @param {HTMLElement} element
- */
- _initCanvasElement: function(element) {
- fabric.util.createCanvasElement(element);
-
- if (typeof element.getContext === 'undefined') {
- throw CANVAS_INIT_ERROR;
- }
- },
-
- /**
- * @private
- * @param {Object} [options] Options object
- */
- _initOptions: function (options) {
- for (var prop in options) {
- this[prop] = options[prop];
- }
-
- this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
- this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
-
- if (!this.lowerCanvasEl.style) {
- return;
- }
-
- this.lowerCanvasEl.width = this.width;
- this.lowerCanvasEl.height = this.height;
-
- this.lowerCanvasEl.style.width = this.width + 'px';
- this.lowerCanvasEl.style.height = this.height + 'px';
-
- this.viewportTransform = this.viewportTransform.slice();
- },
-
- /**
- * Creates a bottom canvas
- * @private
- * @param {HTMLElement} [canvasEl]
- */
- _createLowerCanvas: function (canvasEl) {
- this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
- this._initCanvasElement(this.lowerCanvasEl);
-
- fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
-
- if (this.interactive) {
- this._applyCanvasStyle(this.lowerCanvasEl);
- }
-
- this.contextContainer = this.lowerCanvasEl.getContext('2d');
- },
-
- /**
- * Returns canvas width (in px)
- * @return {Number}
- */
- getWidth: function () {
- return this.width;
- },
-
- /**
- * Returns canvas height (in px)
- * @return {Number}
- */
- getHeight: function () {
- return this.height;
- },
-
- /**
- * Sets width of this canvas instance
- * @param {Number|String} value Value to set width to
- * @param {Object} [options] Options object
- * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
- * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- setWidth: function (value, options) {
- return this.setDimensions({ width: value }, options);
- },
-
- /**
- * Sets height of this canvas instance
- * @param {Number|String} value Value to set height to
- * @param {Object} [options] Options object
- * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
- * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- setHeight: function (value, options) {
- return this.setDimensions({ height: value }, options);
- },
-
- /**
- * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
- * @param {Object} dimensions Object with width/height properties
- * @param {Number|String} [dimensions.width] Width of canvas element
- * @param {Number|String} [dimensions.height] Height of canvas element
- * @param {Object} [options] Options object
- * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
- * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- setDimensions: function (dimensions, options) {
- var cssValue;
-
- options = options || {};
-
- for (var prop in dimensions) {
- cssValue = dimensions[prop];
-
- if (!options.cssOnly) {
- this._setBackstoreDimension(prop, dimensions[prop]);
- cssValue += 'px';
- }
-
- if (!options.backstoreOnly) {
- this._setCssDimension(prop, cssValue);
- }
- }
-
- if (!options.cssOnly) {
- this.renderAll();
- }
-
- this.calcOffset();
-
- return this;
- },
-
- /**
- * Helper for setting width/height
- * @private
- * @param {String} prop property (width|height)
- * @param {Number} value value to set property to
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- _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;
- },
-
- /**
- * Helper for setting css width/height
- * @private
- * @param {String} prop property (width|height)
- * @param {String} value value to set property to
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- _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;
- },
-
- /**
- * Returns canvas zoom level
- * @return {Number}
- */
- getZoom: function () {
- return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
- },
-
- /**
- * Sets viewport transform of this canvas instance
- * @param {Array} vpt the transform in the form of context.transform
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- setViewportTransform: function (vpt) {
- this.viewportTransform = vpt;
- this.renderAll();
- for (var i = 0, len = this._objects.length; i < len; i++) {
- this._objects[i].setCoords();
- }
- return this;
- },
-
- /**
- * Sets zoom level of this canvas instance, zoom centered around point
- * @param {fabric.Point} point to zoom with respect to
- * @param {Number} value to set zoom to, less than 1 zooms out
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- zoomToPoint: function (point, value) {
- // TODO: just change the scale, preserve other transformations
- var before = point;
- point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
- this.viewportTransform[0] = value;
- this.viewportTransform[3] = value;
- var after = fabric.util.transformPoint(point, this.viewportTransform);
- this.viewportTransform[4] += before.x - after.x;
- this.viewportTransform[5] += before.y - after.y;
- this.renderAll();
- for (var i = 0, len = this._objects.length; i < len; i++) {
- this._objects[i].setCoords();
- }
- return this;
- },
-
- /**
- * Sets zoom level of this canvas instance
- * @param {Number} value to set zoom to, less than 1 zooms out
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- setZoom: function (value) {
- this.zoomToPoint(new fabric.Point(0, 0), value);
- return this;
- },
-
- /**
- * Pan viewport so as to place point at top left corner of canvas
- * @param {fabric.Point} point to move to
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- absolutePan: function (point) {
- this.viewportTransform[4] = -point.x;
- this.viewportTransform[5] = -point.y;
- this.renderAll();
- for (var i = 0, len = this._objects.length; i < len; i++) {
- this._objects[i].setCoords();
- }
- return this;
- },
-
- /**
- * Pans viewpoint relatively
- * @param {fabric.Point} point (position vector) to move by
- * @return {fabric.Canvas} instance
- * @chainable true
- */
- relativePan: function (point) {
- return this.absolutePan(new fabric.Point(
- -point.x - this.viewportTransform[4],
- -point.y - this.viewportTransform[5]
- ));
- },
-
- /**
- * Returns <canvas> element corresponding to this instance
- * @return {HTMLCanvasElement}
- */
- getElement: function () {
- return this.lowerCanvasEl;
- },
-
- /**
- * Returns currently selected object, if any
- * @return {fabric.Object}
- */
- getActiveObject: function() {
- return null;
- },
-
- /**
- * Returns currently selected group of object, if any
- * @return {fabric.Group}
- */
- getActiveGroup: function() {
- return null;
- },
-
- /**
- * Given a context, renders an object on that context
- * @param {CanvasRenderingContext2D} ctx Context to render object on
- * @param {fabric.Object} object Object to render
- * @private
- */
- _draw: function (ctx, object) {
- if (!object) {
- return;
- }
-
- ctx.save();
- var v = this.viewportTransform;
- ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
- object.render(ctx);
- ctx.restore();
- if (!this.controlsAboveOverlay) {
- object._renderControls(ctx);
- }
- },
-
- /**
- * @private
- * @param {fabric.Object} obj Object that was added
- */
- _onObjectAdded: function(obj) {
- this.stateful && obj.setupState();
- obj.canvas = this;
- obj.setCoords();
- this.fire('object:added', { target: obj });
- obj.fire('added');
- },
-
- /**
- * @private
- * @param {fabric.Object} obj Object that was removed
- */
- _onObjectRemoved: function(obj) {
- // removing active object should fire "selection:cleared" events
- if (this.getActiveObject() === obj) {
- this.fire('before:selection:cleared', { target: obj });
- this._discardActiveObject();
- this.fire('selection:cleared');
- }
-
- this.fire('object:removed', { target: obj });
- obj.fire('removed');
- },
-
- /**
- * Clears specified context of canvas element
- * @param {CanvasRenderingContext2D} ctx Context to clear
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- clearContext: function(ctx) {
- ctx.clearRect(0, 0, this.width, this.height);
- return this;
- },
-
- /**
- * Returns context of canvas where objects are drawn
- * @return {CanvasRenderingContext2D}
- */
- getContext: function () {
- return this.contextContainer;
- },
-
- /**
- * Clears all contexts (background, main, top) of an instance
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- clear: function () {
- this._objects.length = 0;
- if (this.discardActiveGroup) {
- this.discardActiveGroup();
- }
- if (this.discardActiveObject) {
- this.discardActiveObject();
- }
- this.clearContext(this.contextContainer);
- if (this.contextTop) {
- this.clearContext(this.contextTop);
- }
- this.fire('canvas:cleared');
- this.renderAll();
- return this;
- },
-
- /**
- * Renders both the top canvas and the secondary container canvas.
- * @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
- * @return {fabric.Canvas} instance
- * @chainable
- */
- renderAll: function (allOnTop) {
- var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
- activeGroup = this.getActiveGroup();
-
- if (this.contextTop && this.selection && !this._groupSelector) {
- this.clearContext(this.contextTop);
- }
-
- if (!allOnTop) {
- this.clearContext(canvasToDrawOn);
- }
-
- this.fire('before:render');
-
- if (this.clipTo) {
- fabric.util.clipContext(this, canvasToDrawOn);
- }
-
- this._renderBackground(canvasToDrawOn);
- this._renderObjects(canvasToDrawOn, activeGroup);
- this._renderActiveGroup(canvasToDrawOn, activeGroup);
-
- if (this.clipTo) {
- canvasToDrawOn.restore();
- }
-
- this._renderOverlay(canvasToDrawOn);
-
- if (this.controlsAboveOverlay && this.interactive) {
- this.drawControls(canvasToDrawOn);
- }
-
- this.fire('after:render');
-
- return this;
- },
-
- /**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- * @param {fabric.Group} activeGroup
- */
- _renderObjects: function(ctx, activeGroup) {
- var i, length;
-
- // fast path
- if (!activeGroup) {
- for (i = 0, length = this._objects.length; i < length; ++i) {
- this._draw(ctx, this._objects[i]);
- }
- }
- else {
- for (i = 0, length = this._objects.length; i < length; ++i) {
- if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
- this._draw(ctx, this._objects[i]);
- }
- }
- }
- },
-
- /**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- * @param {fabric.Group} activeGroup
- */
- _renderActiveGroup: function(ctx, activeGroup) {
-
- // delegate rendering to group selection (if one exists)
- if (activeGroup) {
-
- //Store objects in group preserving order, then replace
- var sortedObjects = [];
- this.forEachObject(function (object) {
- if (activeGroup.contains(object)) {
- sortedObjects.push(object);
- }
- });
- activeGroup._set('objects', sortedObjects);
- this._draw(ctx, activeGroup);
- }
- },
-
- /**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- */
- _renderBackground: function(ctx) {
- if (this.backgroundColor) {
- ctx.fillStyle = this.backgroundColor.toLive
- ? this.backgroundColor.toLive(ctx)
- : this.backgroundColor;
-
- ctx.fillRect(
- this.backgroundColor.offsetX || 0,
- this.backgroundColor.offsetY || 0,
- this.width,
- this.height);
- }
- if (this.backgroundImage) {
- this._draw(ctx, this.backgroundImage);
- }
- },
-
- /**
- * @private
- * @param {CanvasRenderingContext2D} ctx Context to render on
- */
- _renderOverlay: function(ctx) {
- if (this.overlayColor) {
- ctx.fillStyle = this.overlayColor.toLive
- ? this.overlayColor.toLive(ctx)
- : this.overlayColor;
-
- ctx.fillRect(
- this.overlayColor.offsetX || 0,
- this.overlayColor.offsetY || 0,
- this.width,
- this.height);
- }
- if (this.overlayImage) {
- this._draw(ctx, this.overlayImage);
- }
- },
-
- /**
- * Method to render only the top canvas.
- * Also used to render the group selection box.
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- renderTop: function () {
- var ctx = this.contextTop || this.contextContainer;
- this.clearContext(ctx);
-
- // we render the top context - last object
- if (this.selection && this._groupSelector) {
- this._drawSelection();
- }
-
- // delegate rendering to group selection if one exists
- // used for drawing selection borders/controls
- var activeGroup = this.getActiveGroup();
- if (activeGroup) {
- activeGroup.render(ctx);
- }
-
- this._renderOverlay(ctx);
-
- this.fire('after:render');
-
- return this;
- },
-
- /**
- * Returns coordinates of a center of canvas.
- * Returned value is an object with top and left properties
- * @return {Object} object with "top" and "left" number values
- */
- getCenter: function () {
- return {
- top: this.getHeight() / 2,
- left: this.getWidth() / 2
- };
- },
-
- /**
- * Centers object horizontally.
- * You might need to call `setCoords` on an object after centering, to update controls area.
- * @param {fabric.Object} object Object to center horizontally
- * @return {fabric.Canvas} thisArg
- */
- centerObjectH: function (object) {
- this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
- this.renderAll();
- return this;
- },
-
- /**
- * Centers object vertically.
- * You might need to call `setCoords` on an object after centering, to update controls area.
- * @param {fabric.Object} object Object to center vertically
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- centerObjectV: function (object) {
- this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
- this.renderAll();
- return this;
- },
-
- /**
- * Centers object vertically and horizontally.
- * You might need to call `setCoords` on an object after centering, to update controls area.
- * @param {fabric.Object} object Object to center vertically and horizontally
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- centerObject: function(object) {
- var center = this.getCenter();
-
- this._centerObject(object, new fabric.Point(center.left, center.top));
- this.renderAll();
- return this;
- },
-
- /**
- * @private
- * @param {fabric.Object} object Object to center
- * @param {fabric.Point} center Center point
- * @return {fabric.Canvas} thisArg
- * @chainable
- */
- _centerObject: function(object, center) {
- object.setPositionByOrigin(center, 'center', 'center');
- return this;
- },
-
- /**
- * Returs dataless JSON representation of canvas
- * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
- * @return {String} json string
- */
- toDatalessJSON: function (propertiesToInclude) {
- return this.toDatalessObject(propertiesToInclude);
- },
-
- /**
- * Returns object representation of canvas
- * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
- * @return {Object} object representation of an instance
- */
- toObject: function (propertiesToInclude) {
- return this._toObjectMethod('toObject', propertiesToInclude);
- },
-
- /**
- * Returns dataless object representation of canvas
- * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
- * @return {Object} object representation of an instance
- */
- toDatalessObject: function (propertiesToInclude) {
- return this._toObjectMethod('toDatalessObject', propertiesToInclude);
- },
-
- /**
- * @private
- */
- _toObjectMethod: function (methodName, propertiesToInclude) {
-
- var activeGroup = this.getActiveGroup();
- if (activeGroup) {
- this.discardActiveGroup();
- }
-
- var data = {
- objects: this._toObjects(methodName, propertiesToInclude)
- };
-
- extend(data, this.__serializeBgOverlay());
-
- fabric.util.populateWithProperties(this, data, propertiesToInclude);
-
- if (activeGroup) {
- this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
- originX: 'center',
- originY: 'center'
- }));
- activeGroup.forEachObject(function(o) {
- o.set('active', true);
- });
-
- if (this._currentTransform) {
- this._currentTransform.target = this.getActiveGroup();
- }
- }
-
- return data;
- },
-
- /**
- * @private
- */
- _toObjects: function(methodName, propertiesToInclude) {
- return this.getObjects().map(function(instance) {
- return this._toObject(instance, methodName, propertiesToInclude);
- }, this);
- },
-
- /**
- * @private
- */
- _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;
- },
-
- /**
- * @private
- */
- __serializeBgOverlay: function() {
- var data = {
- background: (this.backgroundColor && this.backgroundColor.toObject)
- ? this.backgroundColor.toObject()
- : this.backgroundColor
- };
-
- if (this.overlayColor) {
- data.overlay = this.overlayColor.toObject
- ? this.overlayColor.toObject()
- : this.overlayColor;
- }
- if (this.backgroundImage) {
- data.backgroundImage = this.backgroundImage.toObject();
- }
- if (this.overlayImage) {
- data.overlayImage = this.overlayImage.toObject();
- }
-
- return data;
- },
-
- /* _TO_SVG_START_ */
- /**
- * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
- * a zoomed canvas will then produce zoomed SVG output.
- * @type Boolean
- * @default
- */
- svgViewportTransformation: true,
-
- /**
- * Returns SVG representation of canvas
- * @function
- * @param {Object} [options] Options object for SVG output
- * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
- * @param {Object} [options.viewBox] SVG viewbox object
- * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
- * @param {Number} [options.viewBox.y] y-coordinate of viewbox
- * @param {Number} [options.viewBox.width] Width of viewbox
- * @param {Number} [options.viewBox.height] Height of viewbox
- * @param {String} [options.encoding=UTF-8] Encoding of SVG output
- * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
- * @return {String} SVG string
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
- * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
- * @example Normal SVG output
- * var svg = canvas.toSVG();
- * @example SVG output without preamble (without <?xml ../>)
- * var svg = canvas.toSVG({suppressPreamble: true});
- * @example SVG output with viewBox attribute
- * var svg = canvas.toSVG({
- * viewBox: {
- * x: 100,
- * y: 100,
- * width: 200,
- * height: 300
- * }
- * });
- * @example SVG output with different encoding (default: UTF-8)
- * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
- * @example Modify SVG output with reviver function
- * var svg = canvas.toSVG(null, function(svg) {
- * return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
- * });
- */
- toSVG: function(options, reviver) {
- options || (options = { });
-
- var markup = [];
-
- this._setSVGPreamble(markup, options);
- this._setSVGHeader(markup, options);
-
- this._setSVGBgOverlayColor(markup, 'backgroundColor');
- this._setSVGBgOverlayImage(markup, 'backgroundImage');
-
- this._setSVGObjects(markup, reviver);
-
- this._setSVGBgOverlayColor(markup, 'overlayColor');
- this._setSVGBgOverlayImage(markup, 'overlayImage');
-
- markup.push('');
-
- return markup.join('');
- },
-
- /**
- * @private
- */
- _setSVGPreamble: function(markup, options) {
- if (!options.suppressPreamble) {
- markup.push(
- '',
- '\n'
- );
- }
- },
-
- /**
- * @private
- */
- _setSVGHeader: function(markup, options) {
- var width, height, vpt;
-
- if (options.viewBox) {
- width = options.viewBox.width;
- height = options.viewBox.height;
- }
- else {
- width = this.width;
- height = this.height;
- if (!this.svgViewportTransformation) {
- vpt = this.viewportTransform;
- width /= vpt[0];
- height /= vpt[3];
- }
- }
-
- markup.push(
- '/g,">")}fabric.util.string={camelize:e,capitalize:n,escapeXml:f}}(),function(){function e(){}function n(e){var n=this.constructor.superclass.prototype[e];return arguments.length>1?n.apply(this,o.call(arguments,1)):n.call(this)}function f(){function f(){this.initialize.apply(this,arguments)}var i=null,l=o.call(arguments,0);"function"==typeof l[0]&&(i=l.shift()),f.superclass=i,f.subclasses=[],i&&(e.prototype=i.prototype,f.prototype=new e,i.subclasses.push(f));for(var s=0,u=l.length;u>s;s++)t(f,l[s],i);return f.prototype.initialize||(f.prototype.initialize=d),f.prototype.constructor=f,f.prototype.callSuper=n,f}var o=Array.prototype.slice,d=function(){},i=function(){for(var e in{toString:1})if("toString"===e)return!1;return!0}(),t=function(e,n,f){for(var o in n)e.prototype[o]=o in e.prototype&&"function"==typeof e.prototype[o]&&(n[o]+"").indexOf("callSuper")>-1?function(e){return function(){var o=this.constructor.superclass;this.constructor.superclass=f;var d=n[e].apply(this,arguments);return this.constructor.superclass=o,"initialize"!==e?d:void 0}}(o):n[o],i&&(n.toString!==Object.prototype.toString&&(e.prototype.toString=n.toString),n.valueOf!==Object.prototype.valueOf&&(e.prototype.valueOf=n.valueOf))};fabric.util.createClass=f}(),function(){function e(e){var n,f,o=Array.prototype.slice.call(arguments,1),d=o.length;for(f=0;d>f;f++)if(n=typeof e[o[f]],!/^(?:function|object|unknown)$/.test(n))return!1;return!0}function n(e,n){return{handler:n,wrappedHandler:f(e,n)}}function f(e,n){return function(f){n.call(t(e),f||fabric.window.event)}}function o(e,n){return function(f){if(r[e]&&r[e][n])for(var o=r[e][n],d=0,i=o.length;i>d;d++)o[d].call(this,f||fabric.window.event)}}function d(e,n){e||(e=fabric.window.event);var f=e.target||(typeof e.srcElement!==s?e.srcElement:null),o=fabric.util.getScrollLeftTop(f,n);return{x:v(e)+o.left,y:w(e)+o.top}}function i(e,n,f){var o="touchend"===e.type?"changedTouches":"touches";return e[o]&&e[o][0]?e[o][0][n]-(e[o][0][n]-e[o][0][f])||e[f]:e[f]}var t,l,s="unknown",u=function(){var e=0;return function(n){return n.__uniqueID||(n.__uniqueID="uniqueID__"+e++)}}();!function(){var e={};t=function(n){return e[n]},l=function(n,f){e[n]=f}}();var a,p,c=e(fabric.document.documentElement,"addEventListener","removeEventListener")&&e(fabric.window,"addEventListener","removeEventListener"),y=e(fabric.document.documentElement,"attachEvent","detachEvent")&&e(fabric.window,"attachEvent","detachEvent"),m={},r={};c?(a=function(e,n,f){e.addEventListener(n,f,!1)},p=function(e,n,f){e.removeEventListener(n,f,!1)}):y?(a=function(e,f,o){var d=u(e);l(d,e),m[d]||(m[d]={}),m[d][f]||(m[d][f]=[]);var i=n(d,o);m[d][f].push(i),e.attachEvent("on"+f,i.wrappedHandler)},p=function(e,n,f){var o,d=u(e);if(m[d]&&m[d][n])for(var i=0,t=m[d][n].length;t>i;i++)o=m[d][n][i],o&&o.handler===f&&(e.detachEvent("on"+n,o.wrappedHandler),m[d][n][i]=null)}):(a=function(e,n,f){var d=u(e);if(r[d]||(r[d]={}),!r[d][n]){r[d][n]=[];var i=e["on"+n];i&&r[d][n].push(i),e["on"+n]=o(d,n)}r[d][n].push(f)},p=function(e,n,f){var o=u(e);if(r[o]&&r[o][n])for(var d=r[o][n],i=0,t=d.length;t>i;i++)d[i]===f&&d.splice(i,1)}),fabric.util.addListener=a,fabric.util.removeListener=p;var v=function(e){return typeof e.clientX!==s?e.clientX:0},w=function(e){return typeof e.clientY!==s?e.clientY:0};fabric.isTouchSupported&&(v=function(e){return i(e,"pageX","clientX")},w=function(e){return i(e,"pageY","clientY")}),fabric.util.getPointer=d,fabric.util.object.extend(fabric.util,fabric.Observable)}(),function(){function e(e,n){var f=e.style;if(!f)return e;if("string"==typeof n)return e.style.cssText+=";"+n,n.indexOf("opacity")>-1?i(e,n.match(/opacity:\s*(\d?\.?\d*)/)[1]):e;for(var o in n)if("opacity"===o)i(e,n[o]);else{var d="float"===o||"cssFloat"===o?"undefined"==typeof f.styleFloat?"cssFloat":"styleFloat":o;f[d]=n[o]}return e}var n=fabric.document.createElement("div"),f="string"==typeof n.style.opacity,o="string"==typeof n.style.filter,d=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,i=function(e){return e};f?i=function(e,n){return e.style.opacity=n,e}:o&&(i=function(e,n){var f=e.style;return e.currentStyle&&!e.currentStyle.hasLayout&&(f.zoom=1),d.test(f.filter)?(n=n>=.9999?"":"alpha(opacity="+100*n+")",f.filter=f.filter.replace(d,n)):f.filter+=" alpha(opacity="+100*n+")",e}),fabric.util.setStyle=e}(),function(){function e(e){return"string"==typeof e?fabric.document.getElementById(e):e}function n(e,n){var f=fabric.document.createElement(e);for(var o in n)"class"===o?f.className=n[o]:"for"===o?f.htmlFor=n[o]:f.setAttribute(o,n[o]);return f}function f(e,n){e&&-1===(" "+e.className+" ").indexOf(" "+n+" ")&&(e.className+=(e.className?" ":"")+n)}function o(e,f,o){return"string"==typeof f&&(f=n(f,o)),e.parentNode&&e.parentNode.replaceChild(f,e),f.appendChild(e),f}function d(e,n){var f,o,d=0,i=0,t=fabric.document.documentElement,l=fabric.document.body||{scrollLeft:0,scrollTop:0};for(o=e;e&&e.parentNode&&!f;)e=e.parentNode,e!==fabric.document&&"fixed"===fabric.util.getElementStyle(e,"position")&&(f=e),e!==fabric.document&&o!==n&&"absolute"===fabric.util.getElementStyle(e,"position")?(d=0,i=0):e===fabric.document?(d=l.scrollLeft||t.scrollLeft||0,i=l.scrollTop||t.scrollTop||0):(d+=e.scrollLeft||0,i+=e.scrollTop||0);return{left:d,top:i}}function i(e){var n,f,o=e&&e.ownerDocument,d={left:0,top:0},i={left:0,top:0},t={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!o)return{left:0,top:0};for(var l in t)i[t[l]]+=parseInt(a(e,l),10)||0;return n=o.documentElement,"undefined"!=typeof e.getBoundingClientRect&&(d=e.getBoundingClientRect()),f=fabric.util.getScrollLeftTop(e,null),{left:d.left+f.left-(n.clientLeft||0)+i.left,top:d.top+f.top-(n.clientTop||0)+i.top}}var t,l=Array.prototype.slice,s=function(e){return l.call(e,0)};try{t=s(fabric.document.childNodes)instanceof Array}catch(u){}t||(s=function(e){for(var n=new Array(e.length),f=e.length;f--;)n[f]=e[f];return n});var a;a=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(e,n){return fabric.document.defaultView.getComputedStyle(e,null)[n]}:function(e,n){var f=e.style[n];return!f&&e.currentStyle&&(f=e.currentStyle[n]),f},function(){function e(e){return"undefined"!=typeof e.onselectstart&&(e.onselectstart=fabric.util.falseFunction),o?e.style[o]="none":"string"==typeof e.unselectable&&(e.unselectable="on"),e}function n(e){return"undefined"!=typeof e.onselectstart&&(e.onselectstart=null),o?e.style[o]="":"string"==typeof e.unselectable&&(e.unselectable=""),e}var f=fabric.document.documentElement.style,o="userSelect"in f?"userSelect":"MozUserSelect"in f?"MozUserSelect":"WebkitUserSelect"in f?"WebkitUserSelect":"KhtmlUserSelect"in f?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=e,fabric.util.makeElementSelectable=n}(),function(){function e(e,n){var f=fabric.document.getElementsByTagName("head")[0],o=fabric.document.createElement("script"),d=!0;o.onload=o.onreadystatechange=function(e){if(d){if("string"==typeof this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)return;d=!1,n(e||fabric.window.event),o=o.onload=o.onreadystatechange=null}},o.src=e,f.appendChild(o)}fabric.util.getScript=e}(),fabric.util.getById=e,fabric.util.toArray=s,fabric.util.makeElement=n,fabric.util.addClass=f,fabric.util.wrapElement=o,fabric.util.getScrollLeftTop=d,fabric.util.getElementOffset=i,fabric.util.getElementStyle=a}(),function(){function e(e,n){return e+(/\?/.test(e)?"&":"?")+n}function n(){}function f(f,d){d||(d={});var i,t=d.method?d.method.toUpperCase():"GET",l=d.onComplete||function(){},s=o();return s.onreadystatechange=function(){4===s.readyState&&(l(s),s.onreadystatechange=n)},"GET"===t&&(i=null,"string"==typeof d.parameters&&(f=e(f,d.parameters))),s.open(t,f,!0),("POST"===t||"PUT"===t)&&s.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),s.send(i),s}var o=function(){for(var e=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}],n=e.length;n--;)try{var f=e[n]();if(f)return e[n]}catch(o){}}();fabric.util.request=f}(),fabric.log=function(){},fabric.warn=function(){},"undefined"!=typeof console&&["log","warn"].forEach(function(e){"undefined"!=typeof console[e]&&console[e].apply&&(fabric[e]=function(){return console[e].apply(console,arguments)})}),function(e){"use strict";function n(e){return e in j?j[e]:e}function f(e,n,f){var o,d="[object Array]"===Object.prototype.toString.call(n);return"fill"!==e&&"stroke"!==e||"none"!==n?"fillRule"===e?n="evenodd"===n?"destination-over":n:"strokeDashArray"===e?n=n.replace(/,/g," ").split(/\s+/).map(function(e){return parseInt(e)}):"transformMatrix"===e?n=f&&f.transformMatrix?x(f.transformMatrix,r.parseTransformAttribute(n)):r.parseTransformAttribute(n):"visible"===e?(n="none"===n||"hidden"===n?!1:!0,f&&f.visible===!1&&(n=!1)):"originX"===e?n="start"===n?"left":"end"===n?"right":"center":o=d?n.map(h):h(n):n="",!d&&isNaN(o)?n:o}function o(e){for(var n in k)if(e[n]&&"undefined"!=typeof e[k[n]]&&0!==e[n].indexOf("url(")){var f=new r.Color(e[n]);e[n]=f.setAlpha(g(f.getAlpha()*e[k[n]],2)).toRgba()}return e}function d(e,n){var f=e.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);if(f){var o=f[1],d=f[3],i=f[4],t=f[5],l=f[6];o&&(n.fontStyle=o),d&&(n.fontWeight=isNaN(parseFloat(d))?d:parseFloat(d)),i&&(n.fontSize=parseFloat(i)),l&&(n.fontFamily=l),t&&(n.lineHeight="normal"===t?1:t)}}function i(e,o){var i,t;e.replace(/;$/,"").split(";").forEach(function(e){var l=e.split(":");i=n(l[0].trim().toLowerCase()),t=f(i,l[1].trim()),"font"===i?d(t,o):o[i]=t})}function t(e,o){var i,t;for(var l in e)"undefined"!=typeof e[l]&&(i=n(l.toLowerCase()),t=f(i,e[l]),"font"===i?d(t,o):o[i]=t)}function l(e){var n={};for(var f in r.cssRules)if(s(e,f.split(" ")))for(var o in r.cssRules[f])n[o]=r.cssRules[f][o];return n}function s(e,n){var f,o=!0;return f=a(e,n.pop()),f&&n.length&&(o=u(e,n)),f&&o&&0===n.length}function u(e,n){for(var f,o=!0;e.parentNode&&1===e.parentNode.nodeType&&n.length;)o&&(f=n.pop()),e=e.parentNode,o=a(e,f);return 0===n.length}function a(e,n){var f,o=e.nodeName,d=e.getAttribute("class"),i=e.getAttribute("id");if(f=new RegExp("^"+o,"i"),n=n.replace(f,""),i&&n.length&&(f=new RegExp("#"+i+"(?![a-zA-Z\\-]+)","i"),n=n.replace(f,"")),d&&n.length){d=d.split(" ");for(var t=d.length;t--;)f=new RegExp("\\."+d[t]+"(?![a-zA-Z\\-]+)","i"),n=n.replace(f,"")}return 0===n.length}function p(e){for(var n=e.getElementsByTagName("use");n.length;){for(var f,o=n[0],d=o.getAttribute("xlink:href").substr(1),i=o.getAttribute("x")||0,t=o.getAttribute("y")||0,l=e.getElementById(d).cloneNode(!0),s=(o.getAttribute("transform")||"")+" translate("+i+", "+t+")",u=0,a=o.attributes,p=a.length;p>u;u++){var c=a.item(u);"x"!==c.nodeName&&"y"!==c.nodeName&&"xlink:href"!==c.nodeName&&("transform"===c.nodeName?s=s+" "+c.nodeValue:l.setAttribute(c.nodeName,c.nodeValue))}l.setAttribute("transform",s),l.removeAttribute("id"),f=o.parentNode,f.replaceChild(l,o)}}function c(e,n){if(n[3]=n[0]=n[0]>n[3]?n[3]:n[0],1!==n[0]||1!==n[3]||0!==n[4]||0!==n[5]){for(var f=e.ownerDocument.createElement("g");null!=e.firstChild;)f.appendChild(e.firstChild);f.setAttribute("transform","matrix("+n[0]+" "+n[1]+" "+n[2]+" "+n[3]+" "+n[4]+" "+n[5]+")"),e.appendChild(f)}}function y(e){var n=e.objects,f=e.options;return n=n.map(function(e){return r[w(e.type)].fromObject(e)}),{objects:n,options:f}}function m(e,n,f){n[f]&&n[f].toSVG&&e.push('','')}var r=e.fabric||(e.fabric={}),v=r.util.object.extend,w=r.util.string.capitalize,b=r.util.object.clone,g=r.util.toFixed,h=r.util.parseUnit,x=r.util.multiplyTransformMatrices,j={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"},k={stroke:"strokeOpacity",fill:"fillOpacity"};r.parseTransformAttribute=function(){function e(e,n){var f=n[0];e[0]=Math.cos(f),e[1]=Math.sin(f),e[2]=-Math.sin(f),e[3]=Math.cos(f)}function n(e,n){var f=n[0],o=2===n.length?n[1]:n[0];e[0]=f,e[3]=o}function f(e,n){e[2]=n[0]}function o(e,n){e[1]=n[0]}function d(e,n){e[4]=n[0],2===n.length&&(e[5]=n[1])}var i=[1,0,0,1,0,0],t="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",l="(?:\\s+,?\\s*|,\\s*)",s="(?:(skewX)\\s*\\(\\s*("+t+")\\s*\\))",u="(?:(skewY)\\s*\\(\\s*("+t+")\\s*\\))",a="(?:(rotate)\\s*\\(\\s*("+t+")(?:"+l+"("+t+")"+l+"("+t+"))?\\s*\\))",p="(?:(scale)\\s*\\(\\s*("+t+")(?:"+l+"("+t+"))?\\s*\\))",c="(?:(translate)\\s*\\(\\s*("+t+")(?:"+l+"("+t+"))?\\s*\\))",y="(?:(matrix)\\s*\\(\\s*("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")\\s*\\))",m="(?:"+y+"|"+c+"|"+p+"|"+a+"|"+s+"|"+u+")",v="(?:"+m+"(?:"+l+m+")*)",w="^\\s*(?:"+v+"?)\\s*$",b=new RegExp(w),g=new RegExp(m,"g");return function(t){var l=i.concat(),s=[];if(!t||t&&!b.test(t))return l;t.replace(g,function(t){var u=new RegExp(m).exec(t).filter(function(e){return""!==e&&null!=e}),a=u[1],p=u.slice(2).map(parseFloat);switch(a){case"translate":d(l,p);break;case"rotate":p[0]=r.util.degreesToRadians(p[0]),e(l,p);break;case"scale":n(l,p);break;case"skewX":f(l,p);break;case"skewY":o(l,p);break;case"matrix":l=p}s.push(l.concat()),l=i.concat()});for(var u=s[0];s.length>1;)s.shift(),u=r.util.multiplyTransformMatrices(u,s[0]);return u}}(),r.parseSVGDocument=function(){function e(e,n){for(;e&&(e=e.parentNode);)if(n.test(e.nodeName))return!0;return!1}var n=/^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,f="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",o=new RegExp("^\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*$");return function(f,d,i){if(f){var t=new Date;p(f);var l,s,u=f.getAttribute("viewBox"),a=h(f.getAttribute("width")||"100%"),y=h(f.getAttribute("height")||"100%");if(u&&(u=u.match(o))){var m=parseFloat(u[1]),v=parseFloat(u[2]),w=1,g=1;l=parseFloat(u[3]),s=parseFloat(u[4]),a&&a!==l&&(w=a/l),y&&y!==s&&(g=y/s),c(f,[w,0,0,g,w*-m,g*-v])}var x=r.util.toArray(f.getElementsByTagName("*"));if(0===x.length&&r.isLikelyNode){x=f.selectNodes('//*[name(.)!="svg"]');for(var j=[],k=0,q=x.length;q>k;k++)j[k]=x[k];x=j}var z=x.filter(function(f){return n.test(f.tagName)&&!e(f,/^(?:pattern|defs)$/)});if(!z||z&&!z.length)return void(d&&d([],{}));var A={width:a?a:l,height:y?y:s,widthAttr:a,heightAttr:y};r.gradientDefs=r.getGradientDefs(f),r.cssRules=r.getCSSRules(f),r.parseElements(z,function(e){r.documentParsingTime=new Date-t,d&&d(e,A)},b(A),i)}}}();var q={has:function(e,n){n(!1)},get:function(){},set:function(){}};v(r,{getGradientDefs:function(e){var n,f,o,d,i=e.getElementsByTagName("linearGradient"),t=e.getElementsByTagName("radialGradient"),l=0,s=[],u={},a={};for(s.length=i.length+t.length,f=i.length;f--;)s[l++]=i[f];for(f=t.length;f--;)s[l++]=t[f];for(;l--;)n=s[l],d=n.getAttribute("xlink:href"),o=n.getAttribute("id"),d&&(a[o]=d.substr(1)),u[o]=n;for(o in a){var p=u[a[o]].cloneNode(!0);for(n=u[o];p.firstChild;)n.appendChild(p.firstChild)}return u},parseAttributes:function(e,d){if(e){var i,t={};e.parentNode&&/^symbol|[g|a]$/i.test(e.parentNode.nodeName)&&(t=r.parseAttributes(e.parentNode,d));var s=d.reduce(function(o,d){return i=e.getAttribute(d),i&&(d=n(d),i=f(d,i,t),o[d]=i),o},{});return s=v(s,v(l(e),r.parseStyleAttribute(e))),o(v(t,s))}},parseElements:function(e,n,f,o){new r.ElementsParser(e,n,f,o).parse()},parseStyleAttribute:function(e){var n={},f=e.getAttribute("style");return f?("string"==typeof f?i(f,n):t(f,n),n):n
-},parsePointsAttribute:function(e){if(!e)return null;e=e.replace(/,/g," ").trim(),e=e.split(/\s+/);var n,f,o=[];for(n=0,f=e.length;f>n;n+=2)o.push({x:parseFloat(e[n]),y:parseFloat(e[n+1])});return o},getCSSRules:function(e){for(var o,d=e.getElementsByTagName("style"),i={},t=0,l=d.length;l>t;t++){var s=d[0].textContent;s=s.replace(/\/\*[\s\S]*?\*\//g,""),o=s.match(/[^{]*\{[\s\S]*?\}/g),o=o.map(function(e){return e.trim()}),o.forEach(function(e){for(var o=e.match(/([\s\S]*?)\s*\{([^}]*)\}/),d={},t=o[2].trim(),l=t.replace(/;$/,"").split(/\s*;\s*/),s=0,u=l.length;u>s;s++){var a=l[s].split(/\s*:\s*/),p=n(a[0]),c=f(p,a[1],a[0]);d[p]=c}e=o[1],e.split(",").forEach(function(e){i[e.trim()]=r.util.object.clone(d)})})}return i},loadSVGFromURL:function(e,n,f){function o(o){var d=o.responseXML;d&&!d.documentElement&&r.window.ActiveXObject&&o.responseText&&(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(o.responseText.replace(//i,""))),d&&d.documentElement&&r.parseSVGDocument(d.documentElement,function(f,o){q.set(e,{objects:r.util.array.invoke(f,"toObject"),options:o}),n(f,o)},f)}e=e.replace(/^\n\s*/,"").trim(),q.has(e,function(f){f?q.get(e,function(e){var f=y(e);n(f.objects,f.options)}):new r.util.request(e,{method:"get",onComplete:o})})},loadSVGFromString:function(e,n,f){e=e.trim();var o;if("undefined"!=typeof DOMParser){var d=new DOMParser;d&&d.parseFromString&&(o=d.parseFromString(e,"text/xml"))}else r.window.ActiveXObject&&(o=new ActiveXObject("Microsoft.XMLDOM"),o.async="false",o.loadXML(e.replace(//i,"")));r.parseSVGDocument(o.documentElement,function(e,f){n(e,f)},f)},createSVGFontFacesMarkup:function(e){for(var n="",f=0,o=e.length;o>f;f++)"text"===e[f].type&&e[f].path&&(n+=["@font-face {","font-family: ",e[f].fontFamily,"; ","src: url('",e[f].path,"')","}"].join(""));return n&&(n=['"].join("")),n},createSVGRefElementsMarkup:function(e){var n=[];return m(n,e,"backgroundColor"),m(n,e,"overlayColor"),n.join("")}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(e,n,f,o){this.elements=e,this.callback=n,this.options=f,this.reviver=o},fabric.ElementsParser.prototype.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},fabric.ElementsParser.prototype.createObjects=function(){for(var e=0,n=this.elements.length;n>e;e++)!function(e,n){setTimeout(function(){e.createObject(e.elements[n],n)},0)}(this,e)},fabric.ElementsParser.prototype.createObject=function(e,n){var f=fabric[fabric.util.string.capitalize(e.tagName)];if(f&&f.fromElement)try{this._createObject(f,e,n)}catch(o){fabric.log(o)}else this.checkIfDone()},fabric.ElementsParser.prototype._createObject=function(e,n,f){if(e.async)e.fromElement(n,this.createCallback(f,n),this.options);else{var o=e.fromElement(n,this.options);this.resolveGradient(o,"fill"),this.resolveGradient(o,"stroke"),this.reviver&&this.reviver(n,o),this.instances[f]=o,this.checkIfDone()}},fabric.ElementsParser.prototype.createCallback=function(e,n){var f=this;return function(o){f.resolveGradient(o,"fill"),f.resolveGradient(o,"stroke"),f.reviver&&f.reviver(n,o),f.instances[e]=o,f.checkIfDone()}},fabric.ElementsParser.prototype.resolveGradient=function(e,n){var f=e.get(n);if(/^url\(/.test(f)){var o=f.slice(5,f.length-1);fabric.gradientDefs[o]&&e.set(n,fabric.Gradient.fromElement(fabric.gradientDefs[o],e))}},fabric.ElementsParser.prototype.checkIfDone=function(){0===--this.numElements&&(this.instances=this.instances.filter(function(e){return null!=e}),this.callback(this.instances))},function(e){"use strict";function n(e,n){this.x=e,this.y=n}var f=e.fabric||(e.fabric={});return f.Point?void f.warn("fabric.Point is already defined"):(f.Point=n,void(n.prototype={constructor:n,add:function(e){return new n(this.x+e.x,this.y+e.y)},addEquals:function(e){return this.x+=e.x,this.y+=e.y,this},scalarAdd:function(e){return new n(this.x+e,this.y+e)},scalarAddEquals:function(e){return this.x+=e,this.y+=e,this},subtract:function(e){return new n(this.x-e.x,this.y-e.y)},subtractEquals:function(e){return this.x-=e.x,this.y-=e.y,this},scalarSubtract:function(e){return new n(this.x-e,this.y-e)},scalarSubtractEquals:function(e){return this.x-=e,this.y-=e,this},multiply:function(e){return new n(this.x*e,this.y*e)},multiplyEquals:function(e){return this.x*=e,this.y*=e,this},divide:function(e){return new n(this.x/e,this.y/e)},divideEquals:function(e){return this.x/=e,this.y/=e,this},eq:function(e){return this.x===e.x&&this.y===e.y},lt:function(e){return this.xe.x&&this.y>e.y},gte:function(e){return this.x>=e.x&&this.y>=e.y},lerp:function(e,f){return new n(this.x+(e.x-this.x)*f,this.y+(e.y-this.y)*f)},distanceFrom:function(e){var n=this.x-e.x,f=this.y-e.y;return Math.sqrt(n*n+f*f)},midPointFrom:function(e){return new n(this.x+(e.x-this.x)/2,this.y+(e.y-this.y)/2)},min:function(e){return new n(Math.min(this.x,e.x),Math.min(this.y,e.y))},max:function(e){return new n(Math.max(this.x,e.x),Math.max(this.y,e.y))},toString:function(){return this.x+","+this.y},setXY:function(e,n){this.x=e,this.y=n},setFromPoint:function(e){this.x=e.x,this.y=e.y},swap:function(e){var n=this.x,f=this.y;this.x=e.x,this.y=e.y,e.x=n,e.y=f}}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){this.status=e,this.points=[]}var f=e.fabric||(e.fabric={});return f.Intersection?void f.warn("fabric.Intersection is already defined"):(f.Intersection=n,f.Intersection.prototype={appendPoint:function(e){this.points.push(e)},appendPoints:function(e){this.points=this.points.concat(e)}},f.Intersection.intersectLineLine=function(e,o,d,i){var t,l=(i.x-d.x)*(e.y-d.y)-(i.y-d.y)*(e.x-d.x),s=(o.x-e.x)*(e.y-d.y)-(o.y-e.y)*(e.x-d.x),u=(i.y-d.y)*(o.x-e.x)-(i.x-d.x)*(o.y-e.y);if(0!==u){var a=l/u,p=s/u;a>=0&&1>=a&&p>=0&&1>=p?(t=new n("Intersection"),t.points.push(new f.Point(e.x+a*(o.x-e.x),e.y+a*(o.y-e.y)))):t=new n}else t=new n(0===l||0===s?"Coincident":"Parallel");return t},f.Intersection.intersectLinePolygon=function(e,f,o){for(var d=new n,i=o.length,t=0;i>t;t++){var l=o[t],s=o[(t+1)%i],u=n.intersectLineLine(e,f,l,s);d.appendPoints(u.points)}return d.points.length>0&&(d.status="Intersection"),d},f.Intersection.intersectPolygonPolygon=function(e,f){for(var o=new n,d=e.length,i=0;d>i;i++){var t=e[i],l=e[(i+1)%d],s=n.intersectLinePolygon(t,l,f);o.appendPoints(s.points)}return o.points.length>0&&(o.status="Intersection"),o},void(f.Intersection.intersectPolygonRectangle=function(e,o,d){var i=o.min(d),t=o.max(d),l=new f.Point(t.x,i.y),s=new f.Point(i.x,t.y),u=n.intersectLinePolygon(i,l,e),a=n.intersectLinePolygon(l,t,e),p=n.intersectLinePolygon(t,s,e),c=n.intersectLinePolygon(s,i,e),y=new n;return y.appendPoints(u.points),y.appendPoints(a.points),y.appendPoints(p.points),y.appendPoints(c.points),y.points.length>0&&(y.status="Intersection"),y}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){e?this._tryParsingColor(e):this.setSource([0,0,0,1])}function f(e,n,f){return 0>f&&(f+=1),f>1&&(f-=1),1/6>f?e+6*(n-e)*f:.5>f?n:2/3>f?e+(n-e)*(2/3-f)*6:e}var o=e.fabric||(e.fabric={});return o.Color?void o.warn("fabric.Color is already defined."):(o.Color=n,o.Color.prototype={_tryParsingColor:function(e){var f;return e in n.colorNameMap&&(e=n.colorNameMap[e]),"transparent"===e?void this.setSource([255,255,255,0]):(f=n.sourceFromHex(e),f||(f=n.sourceFromRgb(e)),f||(f=n.sourceFromHsl(e)),void(f&&this.setSource(f)))},_rgbToHsl:function(e,n,f){e/=255,n/=255,f/=255;var d,i,t,l=o.util.array.max([e,n,f]),s=o.util.array.min([e,n,f]);if(t=(l+s)/2,l===s)d=i=0;else{var u=l-s;switch(i=t>.5?u/(2-l-s):u/(l+s),l){case e:d=(n-f)/u+(f>n?6:0);break;case n:d=(f-e)/u+2;break;case f:d=(e-n)/u+4}d/=6}return[Math.round(360*d),Math.round(100*i),Math.round(100*t)]},getSource:function(){return this._source},setSource:function(e){this._source=e},toRgb:function(){var e=this.getSource();return"rgb("+e[0]+","+e[1]+","+e[2]+")"},toRgba:function(){var e=this.getSource();return"rgba("+e[0]+","+e[1]+","+e[2]+","+e[3]+")"},toHsl:function(){var e=this.getSource(),n=this._rgbToHsl(e[0],e[1],e[2]);return"hsl("+n[0]+","+n[1]+"%,"+n[2]+"%)"},toHsla:function(){var e=this.getSource(),n=this._rgbToHsl(e[0],e[1],e[2]);return"hsla("+n[0]+","+n[1]+"%,"+n[2]+"%,"+e[3]+")"},toHex:function(){var e,n,f,o=this.getSource();return e=o[0].toString(16),e=1===e.length?"0"+e:e,n=o[1].toString(16),n=1===n.length?"0"+n:n,f=o[2].toString(16),f=1===f.length?"0"+f:f,e.toUpperCase()+n.toUpperCase()+f.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(e){var n=this.getSource();return n[3]=e,this.setSource(n),this},toGrayscale:function(){var e=this.getSource(),n=parseInt((.3*e[0]+.59*e[1]+.11*e[2]).toFixed(0),10),f=e[3];return this.setSource([n,n,n,f]),this},toBlackWhite:function(e){var n=this.getSource(),f=(.3*n[0]+.59*n[1]+.11*n[2]).toFixed(0),o=n[3];return e=e||127,f=Number(f)l;l++)f.push(Math.round(i[l]*(1-d)+t[l]*d));return f[3]=o,this.setSource(f),this}},o.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*)?\)$/,o.Color.reHSLa=/^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/,o.Color.reHex=/^#?([0-9a-f]{6}|[0-9a-f]{3})$/i,o.Color.colorNameMap={aqua:"#00FFFF",black:"#000000",blue:"#0000FF",fuchsia:"#FF00FF",gray:"#808080",green:"#008000",lime:"#00FF00",maroon:"#800000",navy:"#000080",olive:"#808000",orange:"#FFA500",purple:"#800080",red:"#FF0000",silver:"#C0C0C0",teal:"#008080",white:"#FFFFFF",yellow:"#FFFF00"},o.Color.fromRgb=function(e){return n.fromSource(n.sourceFromRgb(e))},o.Color.sourceFromRgb=function(e){var f=e.match(n.reRGBa);if(f){var o=parseInt(f[1],10)/(/%$/.test(f[1])?100:1)*(/%$/.test(f[1])?255:1),d=parseInt(f[2],10)/(/%$/.test(f[2])?100:1)*(/%$/.test(f[2])?255:1),i=parseInt(f[3],10)/(/%$/.test(f[3])?100:1)*(/%$/.test(f[3])?255:1);return[parseInt(o,10),parseInt(d,10),parseInt(i,10),f[4]?parseFloat(f[4]):1]}},o.Color.fromRgba=n.fromRgb,o.Color.fromHsl=function(e){return n.fromSource(n.sourceFromHsl(e))},o.Color.sourceFromHsl=function(e){var o=e.match(n.reHSLa);if(o){var d,i,t,l=(parseFloat(o[1])%360+360)%360/360,s=parseFloat(o[2])/(/%$/.test(o[2])?100:1),u=parseFloat(o[3])/(/%$/.test(o[3])?100:1);if(0===s)d=i=t=u;else{var a=.5>=u?u*(s+1):u+s-u*s,p=2*u-a;d=f(p,a,l+1/3),i=f(p,a,l),t=f(p,a,l-1/3)}return[Math.round(255*d),Math.round(255*i),Math.round(255*t),o[4]?parseFloat(o[4]):1]}},o.Color.fromHsla=n.fromHsl,o.Color.fromHex=function(e){return n.fromSource(n.sourceFromHex(e))},o.Color.sourceFromHex=function(e){if(e.match(n.reHex)){var f=e.slice(e.indexOf("#")+1),o=3===f.length,d=o?f.charAt(0)+f.charAt(0):f.substring(0,2),i=o?f.charAt(1)+f.charAt(1):f.substring(2,4),t=o?f.charAt(2)+f.charAt(2):f.substring(4,6);return[parseInt(d,16),parseInt(i,16),parseInt(t,16),1]}},void(o.Color.fromSource=function(e){var f=new n;return f.setSource(e),f}))}("undefined"!=typeof exports?exports:this),function(){function e(e){var n,f,o,d=e.getAttribute("style"),i=e.getAttribute("offset");if(i=parseFloat(i)/(/%$/.test(i)?100:1),i=0>i?0:i>1?1:i,d){var t=d.split(/\s*;\s*/);""===t[t.length-1]&&t.pop();for(var l=t.length;l--;){var s=t[l].split(/\s*:\s*/),u=s[0].trim(),a=s[1].trim();"stop-color"===u?n=a:"stop-opacity"===u&&(o=a)}}return n||(n=e.getAttribute("stop-color")||"rgb(0,0,0)"),o||(o=e.getAttribute("stop-opacity")),n=new fabric.Color(n),f=n.getAlpha(),o=isNaN(parseFloat(o))?1:parseFloat(o),o*=f,{offset:i,color:n.toRgb(),opacity:o}}function n(e){return{x1:e.getAttribute("x1")||0,y1:e.getAttribute("y1")||0,x2:e.getAttribute("x2")||"100%",y2:e.getAttribute("y2")||0}}function f(e){return{x1:e.getAttribute("fx")||e.getAttribute("cx")||"50%",y1:e.getAttribute("fy")||e.getAttribute("cy")||"50%",r1:0,x2:e.getAttribute("cx")||"50%",y2:e.getAttribute("cy")||"50%",r2:e.getAttribute("r")||"50%"}}function o(e,n,f){var o,d=0,i=1,t="";for(var l in n)o=parseFloat(n[l],10),i="string"==typeof n[l]&&/^\d+%$/.test(n[l])?.01:1,"x1"===l||"x2"===l||"r2"===l?(i*="objectBoundingBox"===f?e.width:1,d="objectBoundingBox"===f?e.left||0:0):("y1"===l||"y2"===l)&&(i*="objectBoundingBox"===f?e.height:1,d="objectBoundingBox"===f?e.top||0:0),n[l]=o*i+d;if("ellipse"===e.type&&null!==n.r2&&"objectBoundingBox"===f&&e.rx!==e.ry){var s=e.ry/e.rx;t=" scale(1, "+s+")",n.y1&&(n.y1/=s),n.y2&&(n.y2/=s)}return t}fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,initialize:function(e){e||(e={});var n={};this.id=fabric.Object.__uid++,this.type=e.type||"linear",n={x1:e.coords.x1||0,y1:e.coords.y1||0,x2:e.coords.x2||0,y2:e.coords.y2||0},"radial"===this.type&&(n.r1=e.coords.r1||0,n.r2=e.coords.r2||0),this.coords=n,this.colorStops=e.colorStops.slice(),e.gradientTransform&&(this.gradientTransform=e.gradientTransform),this.offsetX=e.offsetX||this.offsetX,this.offsetY=e.offsetY||this.offsetY},addColorStop:function(e){for(var n in e){var f=new fabric.Color(e[n]);this.colorStops.push({offset:n,color:f.toRgb(),opacity:f.getAlpha()})}return this},toObject:function(){return{type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(e){var n,f,o=fabric.util.object.clone(this.coords);if(this.colorStops.sort(function(e,n){return e.offset-n.offset}),!e.group||"path-group"!==e.group.type)for(var d in o)"x1"===d||"x2"===d||"r2"===d?o[d]+=this.offsetX-e.width/2:("y1"===d||"y2"===d)&&(o[d]+=this.offsetY-e.height/2);f='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(f+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?n=["\n']:"radial"===this.type&&(n=["\n']);for(var i=0;i\n');return n.push("linear"===this.type?"\n":"\n"),n.join("")},toLive:function(e){var n;if(this.type){"linear"===this.type?n=e.createLinearGradient(this.coords.x1,this.coords.y1,this.coords.x2,this.coords.y2):"radial"===this.type&&(n=e.createRadialGradient(this.coords.x1,this.coords.y1,this.coords.r1,this.coords.x2,this.coords.y2,this.coords.r2));for(var f=0,o=this.colorStops.length;o>f;f++){var d=this.colorStops[f].color,i=this.colorStops[f].opacity,t=this.colorStops[f].offset;"undefined"!=typeof i&&(d=new fabric.Color(d).setAlpha(i).toRgba()),n.addColorStop(parseFloat(t),d)}return n}}}),fabric.util.object.extend(fabric.Gradient,{fromElement:function(d,i){var t,l=d.getElementsByTagName("stop"),s="linearGradient"===d.nodeName?"linear":"radial",u=d.getAttribute("gradientUnits")||"objectBoundingBox",a=d.getAttribute("gradientTransform"),p=[],c={};"linear"===s?c=n(d):"radial"===s&&(c=f(d));for(var y=l.length;y--;)p.push(e(l[y]));t=o(i,c,u);var m=new fabric.Gradient({type:s,coords:c,colorStops:p,offsetX:-i.left,offsetY:-i.top});return(a||""!==t)&&(m.gradientTransform=fabric.parseTransformAttribute((a||"")+t)),m},forObject:function(e,n){return n||(n={}),o(e,n.coords,"userSpaceOnUse"),new fabric.Gradient(n)}})}(),fabric.Pattern=fabric.util.createClass({repeat:"repeat",offsetX:0,offsetY:0,initialize:function(e){if(e||(e={}),this.id=fabric.Object.__uid++,e.source)if("string"==typeof e.source)if("undefined"!=typeof fabric.util.getFunctionBody(e.source))this.source=new Function(fabric.util.getFunctionBody(e.source));else{var n=this;this.source=fabric.util.createImage(),fabric.util.loadImage(e.source,function(e){n.source=e})}else this.source=e.source;e.repeat&&(this.repeat=e.repeat),e.offsetX&&(this.offsetX=e.offsetX),e.offsetY&&(this.offsetY=e.offsetY)},toObject:function(){var e;return"function"==typeof this.source?e=String(this.source):"string"==typeof this.source.src&&(e=this.source.src),{source:e,repeat:this.repeat,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(e){var n="function"==typeof this.source?this.source():this.source,f=n.width/e.getWidth(),o=n.height/e.getHeight(),d="";return n.src?d=n.src:n.toDataURL&&(d=n.toDataURL()),''},toLive:function(e){var n="function"==typeof this.source?this.source():this.source;if(!n)return"";if("undefined"!=typeof n.src){if(!n.complete)return"";if(0===n.naturalWidth||0===n.naturalHeight)return""}return e.createPattern(n,this.repeat)}}),function(e){"use strict";var n=e.fabric||(e.fabric={});return n.Shadow?void n.warn("fabric.Shadow is already defined."):(n.Shadow=n.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(e){"string"==typeof e&&(e=this._parseShadow(e));for(var f in e)this[f]=e[f];this.id=n.Object.__uid++},_parseShadow:function(e){var f=e.trim(),o=n.Shadow.reOffsetsAndBlur.exec(f)||[],d=f.replace(n.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:d.trim(),offsetX:parseInt(o[1],10)||0,offsetY:parseInt(o[2],10)||0,blur:parseInt(o[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(e){var n="SourceAlpha";return!e||e.fill!==this.color&&e.stroke!==this.color||(n="SourceGraphic"),''},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY};var e={},f=n.Shadow.prototype;return this.color!==f.color&&(e.color=this.color),this.blur!==f.blur&&(e.blur=this.blur),this.offsetX!==f.offsetX&&(e.offsetX=this.offsetX),this.offsetY!==f.offsetY&&(e.offsetY=this.offsetY),e}}),void(n.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/))}("undefined"!=typeof exports?exports:this),function(){"use strict";if(fabric.StaticCanvas)return void fabric.warn("fabric.StaticCanvas is already defined.");var e=fabric.util.object.extend,n=fabric.util.getElementOffset,f=fabric.util.removeFromArray,o=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(e,n){n||(n={}),this._initStatic(e,n),fabric.StaticCanvas.activeInstance=this},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!0,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,viewportTransform:[1,0,0,1,0,0],onBeforeScaleRotate:function(){},_initStatic:function(e,n){this._objects=[],this._createLowerCanvas(e),this._initOptions(n),this._setImageSmoothing(),n.overlayImage&&this.setOverlayImage(n.overlayImage,this.renderAll.bind(this)),n.backgroundImage&&this.setBackgroundImage(n.backgroundImage,this.renderAll.bind(this)),n.backgroundColor&&this.setBackgroundColor(n.backgroundColor,this.renderAll.bind(this)),n.overlayColor&&this.setOverlayColor(n.overlayColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=n(this.lowerCanvasEl),this},setOverlayImage:function(e,n,f){return this.__setBgOverlayImage("overlayImage",e,n,f)},setBackgroundImage:function(e,n,f){return this.__setBgOverlayImage("backgroundImage",e,n,f)},setOverlayColor:function(e,n){return this.__setBgOverlayColor("overlayColor",e,n)},setBackgroundColor:function(e,n){return this.__setBgOverlayColor("backgroundColor",e,n)},_setImageSmoothing:function(){var e=this.getContext();e.imageSmoothingEnabled=this.imageSmoothingEnabled,e.webkitImageSmoothingEnabled=this.imageSmoothingEnabled,e.mozImageSmoothingEnabled=this.imageSmoothingEnabled,e.msImageSmoothingEnabled=this.imageSmoothingEnabled,e.oImageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(e,n,f,o){return"string"==typeof n?fabric.util.loadImage(n,function(n){this[e]=new fabric.Image(n,o),f&&f()},this):(this[e]=n,f&&f()),this},__setBgOverlayColor:function(e,n,f){if(n&&n.source){var o=this;fabric.util.loadImage(n.source,function(d){o[e]=new fabric.Pattern({source:d,repeat:n.repeat,offsetX:n.offsetX,offsetY:n.offsetY}),f&&f()})}else this[e]=n,f&&f();return this},_createCanvasElement:function(){var e=fabric.document.createElement("canvas");if(e.style||(e.style={}),!e)throw o;return this._initCanvasElement(e),e},_initCanvasElement:function(e){if(fabric.util.createCanvasElement(e),"undefined"==typeof e.getContext)throw o},_initOptions:function(e){for(var n in e)this[n]=e[n];this.width=this.width||parseInt(this.lowerCanvasEl.width,10)||0,this.height=this.height||parseInt(this.lowerCanvasEl.height,10)||0,this.lowerCanvasEl.style&&(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(e){this.lowerCanvasEl=fabric.util.getById(e)||this._createCanvasElement(),this._initCanvasElement(this.lowerCanvasEl),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(e,n){return this.setDimensions({width:e},n)},setHeight:function(e,n){return this.setDimensions({height:e},n)},setDimensions:function(e,n){var f;n=n||{};for(var o in e)f=e[o],n.cssOnly||(this._setBackstoreDimension(o,e[o]),f+="px"),n.backstoreOnly||this._setCssDimension(o,f);return n.cssOnly||this.renderAll(),this.calcOffset(),this},_setBackstoreDimension:function(e,n){return this.lowerCanvasEl[e]=n,this.upperCanvasEl&&(this.upperCanvasEl[e]=n),this.cacheCanvasEl&&(this.cacheCanvasEl[e]=n),this[e]=n,this},_setCssDimension:function(e,n){return this.lowerCanvasEl.style[e]=n,this.upperCanvasEl&&(this.upperCanvasEl.style[e]=n),this.wrapperEl&&(this.wrapperEl.style[e]=n),this},getZoom:function(){return Math.sqrt(this.viewportTransform[0]*this.viewportTransform[3])},setViewportTransform:function(e){this.viewportTransform=e,this.renderAll();for(var n=0,f=this._objects.length;f>n;n++)this._objects[n].setCoords();return this},zoomToPoint:function(e,n){var f=e;e=fabric.util.transformPoint(e,fabric.util.invertTransform(this.viewportTransform)),this.viewportTransform[0]=n,this.viewportTransform[3]=n;var o=fabric.util.transformPoint(e,this.viewportTransform);this.viewportTransform[4]+=f.x-o.x,this.viewportTransform[5]+=f.y-o.y,this.renderAll();for(var d=0,i=this._objects.length;i>d;d++)this._objects[d].setCoords();return this},setZoom:function(e){return this.zoomToPoint(new fabric.Point(0,0),e),this},absolutePan:function(e){this.viewportTransform[4]=-e.x,this.viewportTransform[5]=-e.y,this.renderAll();for(var n=0,f=this._objects.length;f>n;n++)this._objects[n].setCoords();return this},relativePan:function(e){return this.absolutePan(new fabric.Point(-e.x-this.viewportTransform[4],-e.y-this.viewportTransform[5]))},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(e,n){if(n){e.save();var f=this.viewportTransform;e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),n.render(e),e.restore(),this.controlsAboveOverlay||n._renderControls(e)}},_onObjectAdded:function(e){this.stateful&&e.setupState(),e.canvas=this,e.setCoords(),this.fire("object:added",{target:e}),e.fire("added")},_onObjectRemoved:function(e){this.getActiveObject()===e&&(this.fire("before:selection:cleared",{target:e}),this._discardActiveObject(),this.fire("selection:cleared")),this.fire("object:removed",{target:e}),e.fire("removed")},clearContext:function(e){return e.clearRect(0,0,this.width,this.height),this},getContext:function(){return this.contextContainer},clear:function(){return this._objects.length=0,this.discardActiveGroup&&this.discardActiveGroup(),this.discardActiveObject&&this.discardActiveObject(),this.clearContext(this.contextContainer),this.contextTop&&this.clearContext(this.contextTop),this.fire("canvas:cleared"),this.renderAll(),this},renderAll:function(e){var n=this[e===!0&&this.interactive?"contextTop":"contextContainer"],f=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),e||this.clearContext(n),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,n),this._renderBackground(n),this._renderObjects(n,f),this._renderActiveGroup(n,f),this.clipTo&&n.restore(),this._renderOverlay(n),this.controlsAboveOverlay&&this.interactive&&this.drawControls(n),this.fire("after:render"),this},_renderObjects:function(e,n){var f,o;if(n)for(f=0,o=this._objects.length;o>f;++f)this._objects[f]&&!n.contains(this._objects[f])&&this._draw(e,this._objects[f]);else for(f=0,o=this._objects.length;o>f;++f)this._draw(e,this._objects[f])},_renderActiveGroup:function(e,n){if(n){var f=[];this.forEachObject(function(e){n.contains(e)&&f.push(e)}),n._set("objects",f),this._draw(e,n)}},_renderBackground:function(e){this.backgroundColor&&(e.fillStyle=this.backgroundColor.toLive?this.backgroundColor.toLive(e):this.backgroundColor,e.fillRect(this.backgroundColor.offsetX||0,this.backgroundColor.offsetY||0,this.width,this.height)),this.backgroundImage&&this._draw(e,this.backgroundImage)},_renderOverlay:function(e){this.overlayColor&&(e.fillStyle=this.overlayColor.toLive?this.overlayColor.toLive(e):this.overlayColor,e.fillRect(this.overlayColor.offsetX||0,this.overlayColor.offsetY||0,this.width,this.height)),this.overlayImage&&this._draw(e,this.overlayImage)},renderTop:function(){var e=this.contextTop||this.contextContainer;this.clearContext(e),this.selection&&this._groupSelector&&this._drawSelection();var n=this.getActiveGroup();return n&&n.render(e),this._renderOverlay(e),this.fire("after:render"),this},getCenter:function(){return{top:this.getHeight()/2,left:this.getWidth()/2}},centerObjectH:function(e){return this._centerObject(e,new fabric.Point(this.getCenter().left,e.getCenterPoint().y)),this.renderAll(),this},centerObjectV:function(e){return this._centerObject(e,new fabric.Point(e.getCenterPoint().x,this.getCenter().top)),this.renderAll(),this},centerObject:function(e){var n=this.getCenter();return this._centerObject(e,new fabric.Point(n.left,n.top)),this.renderAll(),this},_centerObject:function(e,n){return e.setPositionByOrigin(n,"center","center"),this},toDatalessJSON:function(e){return this.toDatalessObject(e)},toObject:function(e){return this._toObjectMethod("toObject",e)},toDatalessObject:function(e){return this._toObjectMethod("toDatalessObject",e)},_toObjectMethod:function(n,f){var o=this.getActiveGroup();o&&this.discardActiveGroup();var d={objects:this._toObjects(n,f)};return e(d,this.__serializeBgOverlay()),fabric.util.populateWithProperties(this,d,f),o&&(this.setActiveGroup(new fabric.Group(o.getObjects(),{originX:"center",originY:"center"})),o.forEachObject(function(e){e.set("active",!0)}),this._currentTransform&&(this._currentTransform.target=this.getActiveGroup())),d},_toObjects:function(e,n){return this.getObjects().map(function(f){return this._toObject(f,e,n)},this)},_toObject:function(e,n,f){var o;this.includeDefaultValues||(o=e.includeDefaultValues,e.includeDefaultValues=!1);var d=e[n](f);return this.includeDefaultValues||(e.includeDefaultValues=o),d},__serializeBgOverlay:function(){var e={background:this.backgroundColor&&this.backgroundColor.toObject?this.backgroundColor.toObject():this.backgroundColor};return this.overlayColor&&(e.overlay=this.overlayColor.toObject?this.overlayColor.toObject():this.overlayColor),this.backgroundImage&&(e.backgroundImage=this.backgroundImage.toObject()),this.overlayImage&&(e.overlayImage=this.overlayImage.toObject()),e},svgViewportTransformation:!0,toSVG:function(e,n){e||(e={});var f=[];return this._setSVGPreamble(f,e),this._setSVGHeader(f,e),this._setSVGBgOverlayColor(f,"backgroundColor"),this._setSVGBgOverlayImage(f,"backgroundImage"),this._setSVGObjects(f,n),this._setSVGBgOverlayColor(f,"overlayColor"),this._setSVGBgOverlayImage(f,"overlayImage"),f.push(""),f.join("")},_setSVGPreamble:function(e,n){n.suppressPreamble||e.push('','\n')},_setSVGHeader:function(e,n){var f,o,d;n.viewBox?(f=n.viewBox.width,o=n.viewBox.height):(f=this.width,o=this.height,this.svgViewportTransformation||(d=this.viewportTransform,f/=d[0],o/=d[3])),e.push("