diff --git a/.jshintrc b/.jshintrc index 813ab4c..77ec1af 100644 --- a/.jshintrc +++ b/.jshintrc @@ -15,5 +15,5 @@ "globals": { "jQuery": true }, - "predef": ["-Promise", "define"] + "predef": ["Promise", "define"] } diff --git a/dist/html2canvas.js b/dist/html2canvas.js deleted file mode 100644 index 1b8a9d5..0000000 --- a/dist/html2canvas.js +++ /dev/null @@ -1,4531 +0,0 @@ -/* - html2canvas 0.5.0-alpha2 - Copyright (c) 2015 Niklas von Hertzen - - Released under MIT 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 1) { - throw new Error('Second argument not supported'); - } - if (typeof o !== 'object') { - throw new TypeError('Argument must be an object'); - } - $$utils$$F.prototype = o; - return new $$utils$$F(); - }); - - var $$asap$$len = 0; - - var $$asap$$default = function asap(callback, arg) { - $$asap$$queue[$$asap$$len] = callback; - $$asap$$queue[$$asap$$len + 1] = arg; - $$asap$$len += 2; - if ($$asap$$len === 2) { - // If len is 1, that means that we need to schedule an async flush. - // If additional callbacks are queued before the queue is flushed, they - // will be processed by this flush that we are scheduling. - $$asap$$scheduleFlush(); - } - }; - - var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {}; - var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver; - - // test for web worker but not in IE10 - var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && - typeof importScripts !== 'undefined' && - typeof MessageChannel !== 'undefined'; - - // node - function $$asap$$useNextTick() { - return function() { - process.nextTick($$asap$$flush); - }; - } - - function $$asap$$useMutationObserver() { - var iterations = 0; - var observer = new $$asap$$BrowserMutationObserver($$asap$$flush); - var node = document.createTextNode(''); - observer.observe(node, { characterData: true }); - - return function() { - node.data = (iterations = ++iterations % 2); - }; - } - - // web worker - function $$asap$$useMessageChannel() { - var channel = new MessageChannel(); - channel.port1.onmessage = $$asap$$flush; - return function () { - channel.port2.postMessage(0); - }; - } - - function $$asap$$useSetTimeout() { - return function() { - setTimeout($$asap$$flush, 1); - }; - } - - var $$asap$$queue = new Array(1000); - - function $$asap$$flush() { - for (var i = 0; i < $$asap$$len; i+=2) { - var callback = $$asap$$queue[i]; - var arg = $$asap$$queue[i+1]; - - callback(arg); - - $$asap$$queue[i] = undefined; - $$asap$$queue[i+1] = undefined; - } - - $$asap$$len = 0; - } - - var $$asap$$scheduleFlush; - - // Decide what async method to use to triggering processing of queued callbacks: - if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { - $$asap$$scheduleFlush = $$asap$$useNextTick(); - } else if ($$asap$$BrowserMutationObserver) { - $$asap$$scheduleFlush = $$asap$$useMutationObserver(); - } else if ($$asap$$isWorker) { - $$asap$$scheduleFlush = $$asap$$useMessageChannel(); - } else { - $$asap$$scheduleFlush = $$asap$$useSetTimeout(); - } - - function $$$internal$$noop() {} - var $$$internal$$PENDING = void 0; - var $$$internal$$FULFILLED = 1; - var $$$internal$$REJECTED = 2; - var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject(); - - function $$$internal$$selfFullfillment() { - return new TypeError("You cannot resolve a promise with itself"); - } - - function $$$internal$$cannotReturnOwn() { - return new TypeError('A promises callback cannot return that same promise.') - } - - function $$$internal$$getThen(promise) { - try { - return promise.then; - } catch(error) { - $$$internal$$GET_THEN_ERROR.error = error; - return $$$internal$$GET_THEN_ERROR; - } - } - - function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { - try { - then.call(value, fulfillmentHandler, rejectionHandler); - } catch(e) { - return e; - } - } - - function $$$internal$$handleForeignThenable(promise, thenable, then) { - $$asap$$default(function(promise) { - var sealed = false; - var error = $$$internal$$tryThen(then, thenable, function(value) { - if (sealed) { return; } - sealed = true; - if (thenable !== value) { - $$$internal$$resolve(promise, value); - } else { - $$$internal$$fulfill(promise, value); - } - }, function(reason) { - if (sealed) { return; } - sealed = true; - - $$$internal$$reject(promise, reason); - }, 'Settle: ' + (promise._label || ' unknown promise')); - - if (!sealed && error) { - sealed = true; - $$$internal$$reject(promise, error); - } - }, promise); - } - - function $$$internal$$handleOwnThenable(promise, thenable) { - if (thenable._state === $$$internal$$FULFILLED) { - $$$internal$$fulfill(promise, thenable._result); - } else if (promise._state === $$$internal$$REJECTED) { - $$$internal$$reject(promise, thenable._result); - } else { - $$$internal$$subscribe(thenable, undefined, function(value) { - $$$internal$$resolve(promise, value); - }, function(reason) { - $$$internal$$reject(promise, reason); - }); - } - } - - function $$$internal$$handleMaybeThenable(promise, maybeThenable) { - if (maybeThenable.constructor === promise.constructor) { - $$$internal$$handleOwnThenable(promise, maybeThenable); - } else { - var then = $$$internal$$getThen(maybeThenable); - - if (then === $$$internal$$GET_THEN_ERROR) { - $$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error); - } else if (then === undefined) { - $$$internal$$fulfill(promise, maybeThenable); - } else if ($$utils$$isFunction(then)) { - $$$internal$$handleForeignThenable(promise, maybeThenable, then); - } else { - $$$internal$$fulfill(promise, maybeThenable); - } - } - } - - function $$$internal$$resolve(promise, value) { - if (promise === value) { - $$$internal$$reject(promise, $$$internal$$selfFullfillment()); - } else if ($$utils$$objectOrFunction(value)) { - $$$internal$$handleMaybeThenable(promise, value); - } else { - $$$internal$$fulfill(promise, value); - } - } - - function $$$internal$$publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._result); - } - - $$$internal$$publish(promise); - } - - function $$$internal$$fulfill(promise, value) { - if (promise._state !== $$$internal$$PENDING) { return; } - - promise._result = value; - promise._state = $$$internal$$FULFILLED; - - if (promise._subscribers.length === 0) { - } else { - $$asap$$default($$$internal$$publish, promise); - } - } - - function $$$internal$$reject(promise, reason) { - if (promise._state !== $$$internal$$PENDING) { return; } - promise._state = $$$internal$$REJECTED; - promise._result = reason; - - $$asap$$default($$$internal$$publishRejection, promise); - } - - function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; - - parent._onerror = null; - - subscribers[length] = child; - subscribers[length + $$$internal$$FULFILLED] = onFulfillment; - subscribers[length + $$$internal$$REJECTED] = onRejection; - - if (length === 0 && parent._state) { - $$asap$$default($$$internal$$publish, parent); - } - } - - function $$$internal$$publish(promise) { - var subscribers = promise._subscribers; - var settled = promise._state; - - if (subscribers.length === 0) { return; } - - var child, callback, detail = promise._result; - - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; - - if (child) { - $$$internal$$invokeCallback(settled, child, callback, detail); - } else { - callback(detail); - } - } - - promise._subscribers.length = 0; - } - - function $$$internal$$ErrorObject() { - this.error = null; - } - - var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject(); - - function $$$internal$$tryCatch(callback, detail) { - try { - return callback(detail); - } catch(e) { - $$$internal$$TRY_CATCH_ERROR.error = e; - return $$$internal$$TRY_CATCH_ERROR; - } - } - - function $$$internal$$invokeCallback(settled, promise, callback, detail) { - var hasCallback = $$utils$$isFunction(callback), - value, error, succeeded, failed; - - if (hasCallback) { - value = $$$internal$$tryCatch(callback, detail); - - if (value === $$$internal$$TRY_CATCH_ERROR) { - failed = true; - error = value.error; - value = null; - } else { - succeeded = true; - } - - if (promise === value) { - $$$internal$$reject(promise, $$$internal$$cannotReturnOwn()); - return; - } - - } else { - value = detail; - succeeded = true; - } - - if (promise._state !== $$$internal$$PENDING) { - // noop - } else if (hasCallback && succeeded) { - $$$internal$$resolve(promise, value); - } else if (failed) { - $$$internal$$reject(promise, error); - } else if (settled === $$$internal$$FULFILLED) { - $$$internal$$fulfill(promise, value); - } else if (settled === $$$internal$$REJECTED) { - $$$internal$$reject(promise, value); - } - } - - function $$$internal$$initializePromise(promise, resolver) { - try { - resolver(function resolvePromise(value){ - $$$internal$$resolve(promise, value); - }, function rejectPromise(reason) { - $$$internal$$reject(promise, reason); - }); - } catch(e) { - $$$internal$$reject(promise, e); - } - } - - function $$$enumerator$$makeSettledResult(state, position, value) { - if (state === $$$internal$$FULFILLED) { - return { - state: 'fulfilled', - value: value - }; - } else { - return { - state: 'rejected', - reason: value - }; - } - } - - function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) { - this._instanceConstructor = Constructor; - this.promise = new Constructor($$$internal$$noop, label); - this._abortOnReject = abortOnReject; - - if (this._validateInput(input)) { - this._input = input; - this.length = input.length; - this._remaining = input.length; - - this._init(); - - if (this.length === 0) { - $$$internal$$fulfill(this.promise, this._result); - } else { - this.length = this.length || 0; - this._enumerate(); - if (this._remaining === 0) { - $$$internal$$fulfill(this.promise, this._result); - } - } - } else { - $$$internal$$reject(this.promise, this._validationError()); - } - } - - $$$enumerator$$Enumerator.prototype._validateInput = function(input) { - return $$utils$$isArray(input); - }; - - $$$enumerator$$Enumerator.prototype._validationError = function() { - return new Error('Array Methods must be provided an Array'); - }; - - $$$enumerator$$Enumerator.prototype._init = function() { - this._result = new Array(this.length); - }; - - var $$$enumerator$$default = $$$enumerator$$Enumerator; - - $$$enumerator$$Enumerator.prototype._enumerate = function() { - var length = this.length; - var promise = this.promise; - var input = this._input; - - for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { - this._eachEntry(input[i], i); - } - }; - - $$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { - var c = this._instanceConstructor; - if ($$utils$$isMaybeThenable(entry)) { - if (entry.constructor === c && entry._state !== $$$internal$$PENDING) { - entry._onerror = null; - this._settledAt(entry._state, i, entry._result); - } else { - this._willSettleAt(c.resolve(entry), i); - } - } else { - this._remaining--; - this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry); - } - }; - - $$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { - var promise = this.promise; - - if (promise._state === $$$internal$$PENDING) { - this._remaining--; - - if (this._abortOnReject && state === $$$internal$$REJECTED) { - $$$internal$$reject(promise, value); - } else { - this._result[i] = this._makeResult(state, i, value); - } - } - - if (this._remaining === 0) { - $$$internal$$fulfill(promise, this._result); - } - }; - - $$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) { - return value; - }; - - $$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { - var enumerator = this; - - $$$internal$$subscribe(promise, undefined, function(value) { - enumerator._settledAt($$$internal$$FULFILLED, i, value); - }, function(reason) { - enumerator._settledAt($$$internal$$REJECTED, i, reason); - }); - }; - - var $$promise$all$$default = function all(entries, label) { - return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise; - }; - - var $$promise$race$$default = function race(entries, label) { - /*jshint validthis:true */ - var Constructor = this; - - var promise = new Constructor($$$internal$$noop, label); - - if (!$$utils$$isArray(entries)) { - $$$internal$$reject(promise, new TypeError('You must pass an array to race.')); - return promise; - } - - var length = entries.length; - - function onFulfillment(value) { - $$$internal$$resolve(promise, value); - } - - function onRejection(reason) { - $$$internal$$reject(promise, reason); - } - - for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { - $$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); - } - - return promise; - }; - - var $$promise$resolve$$default = function resolve(object, label) { - /*jshint validthis:true */ - var Constructor = this; - - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } - - var promise = new Constructor($$$internal$$noop, label); - $$$internal$$resolve(promise, object); - return promise; - }; - - var $$promise$reject$$default = function reject(reason, label) { - /*jshint validthis:true */ - var Constructor = this; - var promise = new Constructor($$$internal$$noop, label); - $$$internal$$reject(promise, reason); - return promise; - }; - - var $$es6$promise$promise$$counter = 0; - - function $$es6$promise$promise$$needsResolver() { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); - } - - function $$es6$promise$promise$$needsNew() { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); - } - - var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise; - - /** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise’s eventual value or the reason - why the promise cannot be fulfilled. - - Terminology - ----------- - - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. - - A promise can be in one of three states: pending, fulfilled, or rejected. - - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. - - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. - - - Basic Usage: - ------------ - - ```js - var promise = new Promise(function(resolve, reject) { - // on success - resolve(value); - - // on failure - reject(reason); - }); - - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Advanced Usage: - --------------- - - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. - - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - var xhr = new XMLHttpRequest(); - - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); - - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); - } - } - }; - }); - } - - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Unlike callbacks, promises are great composable primitives. - - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON - - return values; - }); - ``` - - @class Promise - @param {function} resolver - Useful for tooling. - @constructor - */ - function $$es6$promise$promise$$Promise(resolver) { - this._id = $$es6$promise$promise$$counter++; - this._state = undefined; - this._result = undefined; - this._subscribers = []; - - if ($$$internal$$noop !== resolver) { - if (!$$utils$$isFunction(resolver)) { - $$es6$promise$promise$$needsResolver(); - } - - if (!(this instanceof $$es6$promise$promise$$Promise)) { - $$es6$promise$promise$$needsNew(); - } - - $$$internal$$initializePromise(this, resolver); - } - } - - $$es6$promise$promise$$Promise.all = $$promise$all$$default; - $$es6$promise$promise$$Promise.race = $$promise$race$$default; - $$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default; - $$es6$promise$promise$$Promise.reject = $$promise$reject$$default; - - $$es6$promise$promise$$Promise.prototype = { - constructor: $$es6$promise$promise$$Promise, - - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. - - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` - - Chaining - -------- - - The return value of `then` is itself a promise. This second, 'downstream' - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. - - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return 'default name'; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `'default name'` - }); - - findUser().then(function (user) { - throw new Error('Found user, but still unhappy'); - }, function (reason) { - throw new Error('`findUser` rejected and we're unhappy'); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. - // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - - ```js - findUser().then(function (user) { - throw new PedagogicalException('Upstream error'); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` - - Assimilation - ------------ - - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` - - If the assimliated promise rejects, then the downstream promise will also reject. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` - - Simple Example - -------------- - - Synchronous Example - - ```javascript - var result; - - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` - - Promise Example; - - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` - - Advanced Example - -------------- - - Synchronous Example - - ```javascript - var author, books; - - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - - function foundBooks(books) { - - } - - function failure(reason) { - - } - - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); - ``` - - Promise Example; - - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` - - @method then - @param {Function} onFulfilled - @param {Function} onRejected - Useful for tooling. - @return {Promise} - */ - then: function(onFulfillment, onRejection) { - var parent = this; - var state = parent._state; - - if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) { - return this; - } - - var child = new this.constructor($$$internal$$noop); - var result = parent._result; - - if (state) { - var callback = arguments[state - 1]; - $$asap$$default(function(){ - $$$internal$$invokeCallback(state, child, callback, result); - }); - } else { - $$$internal$$subscribe(parent, child, onFulfillment, onRejection); - } - - return child; - }, - - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. - - ```js - function findAuthor(){ - throw new Error('couldn't find that author'); - } - - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } - - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` - - @method catch - @param {Function} onRejection - Useful for tooling. - @return {Promise} - */ - 'catch': function(onRejection) { - return this.then(null, onRejection); - } - }; - - var $$es6$promise$polyfill$$default = function polyfill() { - var local; - - if (typeof global !== 'undefined') { - local = global; - } else if (typeof window !== 'undefined' && window.document) { - local = window; - } else { - local = self; - } - - var es6PromiseSupport = - "Promise" in local && - // Some of these methods are missing from - // Firefox/Chrome experimental implementations - "resolve" in local.Promise && - "reject" in local.Promise && - "all" in local.Promise && - "race" in local.Promise && - // Older version of the spec had a resolver object - // as the arg rather than a function - (function() { - var resolve; - new local.Promise(function(r) { resolve = r; }); - return $$utils$$isFunction(resolve); - }()); - - if (!es6PromiseSupport) { - local.Promise = $$es6$promise$promise$$default; - } - }; - - var es6$promise$umd$$ES6Promise = { - 'Promise': $$es6$promise$promise$$default, - 'polyfill': $$es6$promise$polyfill$$default - }; - - /* global define:true module:true window: true */ - if (typeof define === 'function' && define['amd']) { - define(function() { return es6$promise$umd$$ES6Promise; }); - } else if (typeof module !== 'undefined' && module['exports']) { - module['exports'] = es6$promise$umd$$ES6Promise; - } else if (typeof this !== 'undefined') { - this['ES6Promise'] = es6$promise$umd$$ES6Promise; - } -}).call(this); -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":2}],2:[function(require,module,exports){ -// shim for using process in browser - -var process = module.exports = {}; -var queue = []; -var draining = false; - -function drainQueue() { - if (draining) { - return; - } - draining = true; - var currentQueue; - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - var i = -1; - while (++i < len) { - currentQueue[i](); - } - len = queue.length; - } - draining = false; -} -process.nextTick = function (fun) { - queue.push(fun); - if (!draining) { - setTimeout(drainQueue, 0); - } -}; - -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -// TODO(shtylman) -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],3:[function(require,module,exports){ -(function (global){ -/*! http://mths.be/punycode v1.2.4 by @mathias */ -;(function(root) { - - /** Detect free variables */ - var freeExports = typeof exports == 'object' && exports; - var freeModule = typeof module == 'object' && module && - module.exports == freeExports && module; - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - root = freeGlobal; - } - - /** - * The `punycode` object. - * @name punycode - * @type Object - */ - var punycode, - - /** Highest positive signed 32-bit float value */ - maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 - - /** Bootstring parameters */ - base = 36, - tMin = 1, - tMax = 26, - skew = 38, - damp = 700, - initialBias = 72, - initialN = 128, // 0x80 - delimiter = '-', // '\x2D' - - /** Regular expressions */ - regexPunycode = /^xn--/, - regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars - regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators - - /** Error messages */ - errors = { - 'overflow': 'Overflow: input needs wider integers to process', - 'not-basic': 'Illegal input >= 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 : {}) -},{}],4:[function(require,module,exports){ -var log = require('./log'); -var Promise = require('./promise'); - -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 (require('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( - '', - 'Created with Fabric.js ', fabric.version, '', - '', - fabric.createSVGFontFacesMarkup(this.getObjects()), - fabric.createSVGRefElementsMarkup(this), - '' - ); - }, - - /** - * @private - */ - _setSVGObjects: function(markup, reviver) { - var activeGroup = this.getActiveGroup(); - if (activeGroup) { - this.discardActiveGroup(); - } - for (var i = 0, objects = this.getObjects(), len = objects.length; i < len; i++) { - markup.push(objects[i].toSVG(reviver)); - } - if (activeGroup) { - this.setActiveGroup(new fabric.Group(activeGroup.getObjects())); - activeGroup.forEachObject(function(o) { - o.set('active', true); - }); - } - }, - - /** - * @private - */ - _setSVGBgOverlayImage: function(markup, property) { - if (this[property] && this[property].toSVG) { - markup.push(this[property].toSVG()); - } - }, - - /** - * @private - */ - _setSVGBgOverlayColor: function(markup, property) { - if (this[property] && this[property].source) { - markup.push( - '' - ); - } - else if (this[property] && property === 'overlayColor') { - markup.push( - '' - ); - } - }, - /* _TO_SVG_END_ */ - - /** - * Moves an object to the bottom of the stack of drawn objects - * @param {fabric.Object} object Object to send to back - * @return {fabric.Canvas} thisArg - * @chainable - */ - sendToBack: function (object) { - removeFromArray(this._objects, object); - this._objects.unshift(object); - return this.renderAll && this.renderAll(); - }, - - /** - * Moves an object to the top of the stack of drawn objects - * @param {fabric.Object} object Object to send - * @return {fabric.Canvas} thisArg - * @chainable - */ - bringToFront: function (object) { - removeFromArray(this._objects, object); - this._objects.push(object); - return this.renderAll && this.renderAll(); - }, - - /** - * Moves an object down in stack of drawn objects - * @param {fabric.Object} object Object to send - * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object - * @return {fabric.Canvas} thisArg - * @chainable - */ - sendBackwards: function (object, intersecting) { - var idx = this._objects.indexOf(object); - - // if object is not on the bottom of stack - if (idx !== 0) { - var newIdx = this._findNewLowerIndex(object, idx, intersecting); - - removeFromArray(this._objects, object); - this._objects.splice(newIdx, 0, object); - this.renderAll && this.renderAll(); - } - return this; - }, - - /** - * @private - */ - _findNewLowerIndex: function(object, idx, intersecting) { - var newIdx; - - if (intersecting) { - newIdx = idx; - - // traverse down the stack looking for the nearest intersecting object - for (var i = idx - 1; i >= 0; --i) { - - var isIntersecting = object.intersectsWithObject(this._objects[i]) || - object.isContainedWithinObject(this._objects[i]) || - this._objects[i].isContainedWithinObject(object); - - if (isIntersecting) { - newIdx = i; - break; - } - } - } - else { - newIdx = idx - 1; - } - - return newIdx; - }, - - /** - * Moves an object up in stack of drawn objects - * @param {fabric.Object} object Object to send - * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object - * @return {fabric.Canvas} thisArg - * @chainable - */ - bringForward: function (object, intersecting) { - var idx = this._objects.indexOf(object); - - // if object is not on top of stack (last item in an array) - if (idx !== this._objects.length - 1) { - var newIdx = this._findNewUpperIndex(object, idx, intersecting); - - removeFromArray(this._objects, object); - this._objects.splice(newIdx, 0, object); - this.renderAll && this.renderAll(); - } - return this; - }, - - /** - * @private - */ - _findNewUpperIndex: function(object, idx, intersecting) { - var newIdx; - - if (intersecting) { - newIdx = idx; - - // traverse up the stack looking for the nearest intersecting object - for (var i = idx + 1; i < this._objects.length; ++i) { - - var isIntersecting = object.intersectsWithObject(this._objects[i]) || - object.isContainedWithinObject(this._objects[i]) || - this._objects[i].isContainedWithinObject(object); - - if (isIntersecting) { - newIdx = i; - break; - } - } - } - else { - newIdx = idx + 1; - } - - return newIdx; - }, - - /** - * Moves an object to specified level in stack of drawn objects - * @param {fabric.Object} object Object to send - * @param {Number} index Position to move to - * @return {fabric.Canvas} thisArg - * @chainable - */ - moveTo: function (object, index) { - removeFromArray(this._objects, object); - this._objects.splice(index, 0, object); - return this.renderAll && this.renderAll(); - }, - - /** - * Clears a canvas element and removes all event listeners - * @return {fabric.Canvas} thisArg - * @chainable - */ - dispose: function () { - this.clear(); - this.interactive && this.removeListeners(); - return this; - }, - - /** - * Returns a string representation of an instance - * @return {String} string representation of an instance - */ - toString: function () { - return '#'; - } - }); - - extend(fabric.StaticCanvas.prototype, fabric.Observable); - extend(fabric.StaticCanvas.prototype, fabric.Collection); - extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter); - - extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ { - - /** - * @static - * @type String - * @default - */ - EMPTY_JSON: '{"objects": [], "background": "white"}', - - /** - * Provides a way to check support of some of the canvas methods - * (either those of HTMLCanvasElement itself, or rendering context) - * - * @param {String} methodName Method to check support for; - * Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash" - * @return {Boolean | null} `true` if method is supported (or at least exists), - * `null` if canvas element or context can not be initialized - */ - supports: function (methodName) { - var el = fabric.util.createCanvasElement(); - - if (!el || !el.getContext) { - return null; - } - - var ctx = el.getContext('2d'); - if (!ctx) { - return null; - } - - switch (methodName) { - - case 'getImageData': - return typeof ctx.getImageData !== 'undefined'; - - case 'setLineDash': - return typeof ctx.setLineDash !== 'undefined'; - - case 'toDataURL': - return typeof el.toDataURL !== 'undefined'; - - case 'toDataURLWithQuality': - try { - el.toDataURL('image/jpeg', 0); - return true; - } - catch (e) { } - return false; - - default: - return null; - } - } - }); - - /** - * Returns JSON representation of canvas - * @function - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {String} JSON string - * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization} - * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo} - * @example JSON without additional properties - * var json = canvas.toJSON(); - * @example JSON with additional properties included - * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']); - * @example JSON without default values - * canvas.includeDefaultValues = false; - * var json = canvas.toJSON(); - */ - fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject; - -})(); - - -/** - * BaseBrush class - * @class fabric.BaseBrush - * @see {@link http://fabricjs.com/freedrawing/|Freedrawing demo} - */ -fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ { - - /** - * Color of a brush - * @type String - * @default - */ - color: 'rgb(0, 0, 0)', - - /** - * Width of a brush - * @type Number - * @default - */ - width: 1, - - /** - * Shadow object representing shadow of this shape. - * Backwards incompatibility note: This property replaces "shadowColor" (String), "shadowOffsetX" (Number), - * "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12 - * @type fabric.Shadow - * @default - */ - shadow: null, - - /** - * Line endings style of a brush (one of "butt", "round", "square") - * @type String - * @default - */ - strokeLineCap: 'round', - - /** - * Corner style of a brush (one of "bevil", "round", "miter") - * @type String - * @default - */ - strokeLineJoin: 'round', - - /** - * Sets shadow of an object - * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)") - * @return {fabric.Object} thisArg - * @chainable - */ - setShadow: function(options) { - this.shadow = new fabric.Shadow(options); - return this; - }, - - /** - * Sets brush styles - * @private - */ - _setBrushStyles: function() { - var ctx = this.canvas.contextTop; - - ctx.strokeStyle = this.color; - ctx.lineWidth = this.width; - ctx.lineCap = this.strokeLineCap; - ctx.lineJoin = this.strokeLineJoin; - }, - - /** - * Sets brush shadow styles - * @private - */ - _setShadow: function() { - if (!this.shadow) { - return; - } - - var ctx = this.canvas.contextTop; - - ctx.shadowColor = this.shadow.color; - ctx.shadowBlur = this.shadow.blur; - ctx.shadowOffsetX = this.shadow.offsetX; - ctx.shadowOffsetY = this.shadow.offsetY; - }, - - /** - * Removes brush shadow styles - * @private - */ - _resetShadow: function() { - var ctx = this.canvas.contextTop; - - ctx.shadowColor = ''; - ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0; - } -}); - - -(function() { - - var utilMin = fabric.util.array.min, - utilMax = fabric.util.array.max; - - /** - * PencilBrush class - * @class fabric.PencilBrush - * @extends fabric.BaseBrush - */ - fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ { - - /** - * Constructor - * @param {fabric.Canvas} canvas - * @return {fabric.PencilBrush} Instance of a pencil brush - */ - initialize: function(canvas) { - this.canvas = canvas; - this._points = [ ]; - }, - - /** - * Inovoked on mouse down - * @param {Object} pointer - */ - onMouseDown: function(pointer) { - this._prepareForDrawing(pointer); - // capture coordinates immediately - // this allows to draw dots (when movement never occurs) - this._captureDrawingPath(pointer); - this._render(); - }, - - /** - * Inovoked on mouse move - * @param {Object} pointer - */ - onMouseMove: function(pointer) { - this._captureDrawingPath(pointer); - // redraw curve - // clear top canvas - this.canvas.clearContext(this.canvas.contextTop); - this._render(); - }, - - /** - * Invoked on mouse up - */ - onMouseUp: function() { - this._finalizeAndAddPath(); - }, - - /** - * @private - * @param {Object} pointer Actual mouse position related to the canvas. - */ - _prepareForDrawing: function(pointer) { - - var p = new fabric.Point(pointer.x, pointer.y); - - this._reset(); - this._addPoint(p); - - this.canvas.contextTop.moveTo(p.x, p.y); - }, - - /** - * @private - * @param {fabric.Point} point Point to be added to points array - */ - _addPoint: function(point) { - this._points.push(point); - }, - - /** - * Clear points array and set contextTop canvas style. - * @private - */ - _reset: function() { - this._points.length = 0; - - this._setBrushStyles(); - this._setShadow(); - }, - - /** - * @private - * @param {Object} pointer Actual mouse position related to the canvas. - */ - _captureDrawingPath: function(pointer) { - var pointerPoint = new fabric.Point(pointer.x, pointer.y); - this._addPoint(pointerPoint); - }, - - /** - * Draw a smooth path on the topCanvas using quadraticCurveTo - * @private - */ - _render: function() { - var ctx = this.canvas.contextTop, - v = this.canvas.viewportTransform, - p1 = this._points[0], - p2 = this._points[1]; - - ctx.save(); - ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]); - ctx.beginPath(); - - //if we only have 2 points in the path and they are the same - //it means that the user only clicked the canvas without moving the mouse - //then we should be drawing a dot. A path isn't drawn between two identical dots - //that's why we set them apart a bit - if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { - p1.x -= 0.5; - p2.x += 0.5; - } - ctx.moveTo(p1.x, p1.y); - - for (var i = 1, len = this._points.length; i < len; i++) { - // we pick the point between pi + 1 & pi + 2 as the - // end point and p1 as our control point. - var midPoint = p1.midPointFrom(p2); - ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); - - p1 = this._points[i]; - p2 = this._points[i + 1]; - } - // Draw last line as a straight line while - // we wait for the next point to be able to calculate - // the bezier control point - ctx.lineTo(p1.x, p1.y); - ctx.stroke(); - ctx.restore(); - }, - - /** - * Return an SVG path based on our captured points and their bounding box - * @private - */ - _getSVGPathData: function() { - this.box = this.getPathBoundingBox(this._points); - return this.convertPointsToSVGPath( - this._points, this.box.minX, this.box.minY); - }, - - /** - * Returns bounding box of a path based on given points - * @param {Array} points Array of points - * @return {Object} Object with minX, minY, maxX, maxY - */ - getPathBoundingBox: function(points) { - var xBounds = [], - yBounds = [], - p1 = points[0], - p2 = points[1], - startPoint = p1; - - for (var i = 1, len = points.length; i < len; i++) { - var midPoint = p1.midPointFrom(p2); - // with startPoint, p1 as control point, midpoint as end point - xBounds.push(startPoint.x); - xBounds.push(midPoint.x); - yBounds.push(startPoint.y); - yBounds.push(midPoint.y); - - p1 = points[i]; - p2 = points[i + 1]; - startPoint = midPoint; - } - - xBounds.push(p1.x); - yBounds.push(p1.y); - - return { - minX: utilMin(xBounds), - minY: utilMin(yBounds), - maxX: utilMax(xBounds), - maxY: utilMax(yBounds) - }; - }, - - /** - * Converts points to SVG path - * @param {Array} points Array of points - * @param {Number} minX - * @param {Number} minY - * @return {String} SVG path - */ - convertPointsToSVGPath: function(points, minX, minY) { - var path = [], - p1 = new fabric.Point(points[0].x - minX, points[0].y - minY), - p2 = new fabric.Point(points[1].x - minX, points[1].y - minY); - - path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' '); - for (var i = 1, len = points.length; i < len; i++) { - var midPoint = p1.midPointFrom(p2); - // p1 is our bezier control point - // midpoint is our endpoint - // start point is p(i-1) value. - path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' '); - p1 = new fabric.Point(points[i].x - minX, points[i].y - minY); - if ((i + 1) < points.length) { - p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY); - } - } - path.push('L ', p1.x, ' ', p1.y, ' '); - return path; - }, - - /** - * Creates fabric.Path object to add on canvas - * @param {String} pathData Path data - * @return {fabric.Path} Path to add on canvas - */ - createPath: function(pathData) { - var path = new fabric.Path(pathData); - path.fill = null; - path.stroke = this.color; - path.strokeWidth = this.width; - path.strokeLineCap = this.strokeLineCap; - path.strokeLineJoin = this.strokeLineJoin; - - if (this.shadow) { - this.shadow.affectStroke = true; - path.setShadow(this.shadow); - } - - return path; - }, - - /** - * On mouseup after drawing the path on contextTop canvas - * we use the points captured to create an new fabric path object - * and add it to the fabric canvas. - */ - _finalizeAndAddPath: function() { - var ctx = this.canvas.contextTop; - ctx.closePath(); - - var pathData = this._getSVGPathData().join(''); - if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') { - // do not create 0 width/height paths, as they are - // rendered inconsistently across browsers - // Firefox 4, for example, renders a dot, - // whereas Chrome 10 renders nothing - this.canvas.renderAll(); - return; - } - - // set path origin coordinates based on our bounding box - var originLeft = this.box.minX + (this.box.maxX - this.box.minX) / 2, - originTop = this.box.minY + (this.box.maxY - this.box.minY) / 2; - - this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false); - - var path = this.createPath(pathData); - path.set({ - left: originLeft, - top: originTop, - originX: 'center', - originY: 'center' - }); - - this.canvas.add(path); - path.setCoords(); - - this.canvas.clearContext(this.canvas.contextTop); - this._resetShadow(); - this.canvas.renderAll(); - - // fire event 'path' created - this.canvas.fire('path:created', { path: path }); - } - }); -})(); - - -/** - * CircleBrush class - * @class fabric.CircleBrush - */ -fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ { - - /** - * Width of a brush - * @type Number - * @default - */ - width: 10, - - /** - * Constructor - * @param {fabric.Canvas} canvas - * @return {fabric.CircleBrush} Instance of a circle brush - */ - initialize: function(canvas) { - this.canvas = canvas; - this.points = [ ]; - }, - /** - * Invoked inside on mouse down and mouse move - * @param {Object} pointer - */ - drawDot: function(pointer) { - var point = this.addPoint(pointer), - ctx = this.canvas.contextTop, - v = this.canvas.viewportTransform; - ctx.save(); - ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]); - - ctx.fillStyle = point.fill; - ctx.beginPath(); - ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false); - ctx.closePath(); - ctx.fill(); - - ctx.restore(); - }, - - /** - * Invoked on mouse down - */ - onMouseDown: function(pointer) { - this.points.length = 0; - this.canvas.clearContext(this.canvas.contextTop); - this._setShadow(); - this.drawDot(pointer); - }, - - /** - * Invoked on mouse move - * @param {Object} pointer - */ - onMouseMove: function(pointer) { - this.drawDot(pointer); - }, - - /** - * Invoked on mouse up - */ - onMouseUp: function() { - var originalRenderOnAddRemove = this.canvas.renderOnAddRemove; - this.canvas.renderOnAddRemove = false; - - var circles = [ ]; - - for (var i = 0, len = this.points.length; i < len; i++) { - var point = this.points[i], - circle = new fabric.Circle({ - radius: point.radius, - left: point.x, - top: point.y, - originX: 'center', - originY: 'center', - fill: point.fill - }); - - this.shadow && circle.setShadow(this.shadow); - - circles.push(circle); - } - var group = new fabric.Group(circles, { originX: 'center', originY: 'center' }); - group.canvas = this.canvas; - - this.canvas.add(group); - this.canvas.fire('path:created', { path: group }); - - this.canvas.clearContext(this.canvas.contextTop); - this._resetShadow(); - this.canvas.renderOnAddRemove = originalRenderOnAddRemove; - this.canvas.renderAll(); - }, - - /** - * @param {Object} pointer - * @return {fabric.Point} Just added pointer point - */ - addPoint: function(pointer) { - var pointerPoint = new fabric.Point(pointer.x, pointer.y), - - circleRadius = fabric.util.getRandomInt( - Math.max(0, this.width - 20), this.width + 20) / 2, - - circleColor = new fabric.Color(this.color) - .setAlpha(fabric.util.getRandomInt(0, 100) / 100) - .toRgba(); - - pointerPoint.radius = circleRadius; - pointerPoint.fill = circleColor; - - this.points.push(pointerPoint); - - return pointerPoint; - } -}); - - -/** - * SprayBrush class - * @class fabric.SprayBrush - */ -fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ { - - /** - * Width of a spray - * @type Number - * @default - */ - width: 10, - - /** - * Density of a spray (number of dots per chunk) - * @type Number - * @default - */ - density: 20, - - /** - * Width of spray dots - * @type Number - * @default - */ - dotWidth: 1, - - /** - * Width variance of spray dots - * @type Number - * @default - */ - dotWidthVariance: 1, - - /** - * Whether opacity of a dot should be random - * @type Boolean - * @default - */ - randomOpacity: false, - - /** - * Whether overlapping dots (rectangles) should be removed (for performance reasons) - * @type Boolean - * @default - */ - optimizeOverlapping: true, - - /** - * Constructor - * @param {fabric.Canvas} canvas - * @return {fabric.SprayBrush} Instance of a spray brush - */ - initialize: function(canvas) { - this.canvas = canvas; - this.sprayChunks = [ ]; - }, - - /** - * Invoked on mouse down - * @param {Object} pointer - */ - onMouseDown: function(pointer) { - this.sprayChunks.length = 0; - this.canvas.clearContext(this.canvas.contextTop); - this._setShadow(); - - this.addSprayChunk(pointer); - this.render(); - }, - - /** - * Invoked on mouse move - * @param {Object} pointer - */ - onMouseMove: function(pointer) { - this.addSprayChunk(pointer); - this.render(); - }, - - /** - * Invoked on mouse up - */ - onMouseUp: function() { - var originalRenderOnAddRemove = this.canvas.renderOnAddRemove; - this.canvas.renderOnAddRemove = false; - - var rects = [ ]; - - for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) { - var sprayChunk = this.sprayChunks[i]; - - for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) { - - var rect = new fabric.Rect({ - width: sprayChunk[j].width, - height: sprayChunk[j].width, - left: sprayChunk[j].x + 1, - top: sprayChunk[j].y + 1, - originX: 'center', - originY: 'center', - fill: this.color - }); - - this.shadow && rect.setShadow(this.shadow); - rects.push(rect); - } - } - - if (this.optimizeOverlapping) { - rects = this._getOptimizedRects(rects); - } - - var group = new fabric.Group(rects, { originX: 'center', originY: 'center' }); - group.canvas = this.canvas; - - this.canvas.add(group); - this.canvas.fire('path:created', { path: group }); - - this.canvas.clearContext(this.canvas.contextTop); - this._resetShadow(); - this.canvas.renderOnAddRemove = originalRenderOnAddRemove; - this.canvas.renderAll(); - }, - - /** - * @private - * @param {Array} rects - */ - _getOptimizedRects: function(rects) { - - // avoid creating duplicate rects at the same coordinates - var uniqueRects = { }, key; - - for (var i = 0, len = rects.length; i < len; i++) { - key = rects[i].left + '' + rects[i].top; - if (!uniqueRects[key]) { - uniqueRects[key] = rects[i]; - } - } - var uniqueRectsArray = [ ]; - for (key in uniqueRects) { - uniqueRectsArray.push(uniqueRects[key]); - } - - return uniqueRectsArray; - }, - - /** - * Renders brush - */ - render: function() { - var ctx = this.canvas.contextTop; - ctx.fillStyle = this.color; - - var v = this.canvas.viewportTransform; - ctx.save(); - ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]); - - for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) { - var point = this.sprayChunkPoints[i]; - if (typeof point.opacity !== 'undefined') { - ctx.globalAlpha = point.opacity; - } - ctx.fillRect(point.x, point.y, point.width, point.width); - } - ctx.restore(); - }, - - /** - * @param {Object} pointer - */ - addSprayChunk: function(pointer) { - this.sprayChunkPoints = [ ]; - - var x, y, width, radius = this.width / 2; - - for (var i = 0; i < this.density; i++) { - - x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius); - y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius); - - if (this.dotWidthVariance) { - width = fabric.util.getRandomInt( - // bottom clamp width to 1 - Math.max(1, this.dotWidth - this.dotWidthVariance), - this.dotWidth + this.dotWidthVariance); - } - else { - width = this.dotWidth; - } - - var point = new fabric.Point(x, y); - point.width = width; - - if (this.randomOpacity) { - point.opacity = fabric.util.getRandomInt(0, 100) / 100; - } - - this.sprayChunkPoints.push(point); - } - - this.sprayChunks.push(this.sprayChunkPoints); - } -}); - - -/** - * PatternBrush class - * @class fabric.PatternBrush - * @extends fabric.BaseBrush - */ -fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fabric.PatternBrush.prototype */ { - - getPatternSrc: function() { - - var dotWidth = 20, - dotDistance = 5, - patternCanvas = fabric.document.createElement('canvas'), - patternCtx = patternCanvas.getContext('2d'); - - patternCanvas.width = patternCanvas.height = dotWidth + dotDistance; - - patternCtx.fillStyle = this.color; - patternCtx.beginPath(); - patternCtx.arc(dotWidth / 2, dotWidth / 2, dotWidth / 2, 0, Math.PI * 2, false); - patternCtx.closePath(); - patternCtx.fill(); - - return patternCanvas; - }, - - getPatternSrcFunction: function() { - return String(this.getPatternSrc).replace('this.color', '"' + this.color + '"'); - }, - - /** - * Creates "pattern" instance property - */ - getPattern: function() { - return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), 'repeat'); - }, - - /** - * Sets brush styles - */ - _setBrushStyles: function() { - this.callSuper('_setBrushStyles'); - this.canvas.contextTop.strokeStyle = this.getPattern(); - }, - - /** - * Creates path - */ - createPath: function(pathData) { - var path = this.callSuper('createPath', pathData); - path.stroke = new fabric.Pattern({ - source: this.source || this.getPatternSrcFunction() - }); - return path; - } -}); - - -fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ { - - /** - * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately - * @param {Object} [options] Options object - * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png" - * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg. - * @param {Number} [options.multiplier=1] Multiplier to scale by - * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14 - * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14 - * @param {Number} [options.width] Cropping width. Introduced in v1.2.14 - * @param {Number} [options.height] Cropping height. Introduced in v1.2.14 - * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format - * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo} - * @example Generate jpeg dataURL with lower quality - * var dataURL = canvas.toDataURL({ - * format: 'jpeg', - * quality: 0.8 - * }); - * @example Generate cropped png dataURL (clipping of canvas) - * var dataURL = canvas.toDataURL({ - * format: 'png', - * left: 100, - * top: 100, - * width: 200, - * height: 200 - * }); - * @example Generate double scaled png dataURL - * var dataURL = canvas.toDataURL({ - * format: 'png', - * multiplier: 2 - * }); - */ - toDataURL: function (options) { - options || (options = { }); - - var format = options.format || 'png', - quality = options.quality || 1, - multiplier = options.multiplier || 1, - cropping = { - left: options.left, - top: options.top, - width: options.width, - height: options.height - }; - - if (multiplier !== 1) { - return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier); - } - else { - return this.__toDataURL(format, quality, cropping); - } - }, - - /** - * @private - */ - __toDataURL: function(format, quality, cropping) { - - this.renderAll(true); - - var canvasEl = this.upperCanvasEl || this.lowerCanvasEl, - croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping); - - // to avoid common confusion https://github.com/kangax/fabric.js/issues/806 - if (format === 'jpg') { - format = 'jpeg'; - } - - var data = (fabric.StaticCanvas.supports('toDataURLWithQuality')) - ? (croppedCanvasEl || canvasEl).toDataURL('image/' + format, quality) - : (croppedCanvasEl || canvasEl).toDataURL('image/' + format); - - this.contextTop && this.clearContext(this.contextTop); - this.renderAll(); - - if (croppedCanvasEl) { - croppedCanvasEl = null; - } - - return data; - }, - - /** - * @private - */ - __getCroppedCanvas: function(canvasEl, cropping) { - - var croppedCanvasEl, - croppedCtx, - shouldCrop = 'left' in cropping || - 'top' in cropping || - 'width' in cropping || - 'height' in cropping; - - if (shouldCrop) { - - croppedCanvasEl = fabric.util.createCanvasElement(); - croppedCtx = croppedCanvasEl.getContext('2d'); - - croppedCanvasEl.width = cropping.width || this.width; - croppedCanvasEl.height = cropping.height || this.height; - - croppedCtx.drawImage(canvasEl, -cropping.left || 0, -cropping.top || 0); - } - - return croppedCanvasEl; - }, - - /** - * @private - */ - __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) { - - var origWidth = this.getWidth(), - origHeight = this.getHeight(), - scaledWidth = origWidth * multiplier, - scaledHeight = origHeight * multiplier, - activeObject = this.getActiveObject(), - activeGroup = this.getActiveGroup(), - - ctx = this.contextTop || this.contextContainer; - - if (multiplier > 1) { - this.setWidth(scaledWidth).setHeight(scaledHeight); - } - ctx.scale(multiplier, multiplier); - - if (cropping.left) { - cropping.left *= multiplier; - } - if (cropping.top) { - cropping.top *= multiplier; - } - if (cropping.width) { - cropping.width *= multiplier; - } - else if (multiplier < 1) { - cropping.width = scaledWidth; - } - if (cropping.height) { - cropping.height *= multiplier; - } - else if (multiplier < 1) { - cropping.height = scaledHeight; - } - - if (activeGroup) { - // not removing group due to complications with restoring it with correct state afterwords - this._tempRemoveBordersControlsFromGroup(activeGroup); - } - else if (activeObject && this.deactivateAll) { - this.deactivateAll(); - } - - this.renderAll(true); - - var data = this.__toDataURL(format, quality, cropping); - - // restoring width, height for `renderAll` to draw - // background properly (while context is scaled) - this.width = origWidth; - this.height = origHeight; - - ctx.scale(1 / multiplier, 1 / multiplier); - this.setWidth(origWidth).setHeight(origHeight); - - if (activeGroup) { - this._restoreBordersControlsOnGroup(activeGroup); - } - else if (activeObject && this.setActiveObject) { - this.setActiveObject(activeObject); - } - - this.contextTop && this.clearContext(this.contextTop); - this.renderAll(); - - return data; - }, - - /** - * Exports canvas element to a dataurl image (allowing to change image size via multiplier). - * @deprecated since 1.0.13 - * @param {String} format (png|jpeg) - * @param {Number} multiplier - * @param {Number} quality (0..1) - * @return {String} - */ - toDataURLWithMultiplier: function (format, multiplier, quality) { - return this.toDataURL({ - format: format, - multiplier: multiplier, - quality: quality - }); - }, - - /** - * @private - */ - _tempRemoveBordersControlsFromGroup: function(group) { - group.origHasControls = group.hasControls; - group.origBorderColor = group.borderColor; - - group.hasControls = true; - group.borderColor = 'rgba(0,0,0,0)'; - - group.forEachObject(function(o) { - o.origBorderColor = o.borderColor; - o.borderColor = 'rgba(0,0,0,0)'; - }); - }, - - /** - * @private - */ - _restoreBordersControlsOnGroup: function(group) { - group.hideControls = group.origHideControls; - group.borderColor = group.origBorderColor; - - group.forEachObject(function(o) { - o.borderColor = o.origBorderColor; - delete o.origBorderColor; - }); - } -}); - - -fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ { - - /** - * Populates canvas with data from the specified dataless JSON. - * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON} - * @deprecated since 1.2.2 - * @param {String|Object} json JSON string or object - * @param {Function} callback Callback, invoked when json is parsed - * and corresponding objects (e.g: {@link fabric.Image}) - * are initialized - * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created. - * @return {fabric.Canvas} instance - * @chainable - * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization} - */ - loadFromDatalessJSON: function (json, callback, reviver) { - return this.loadFromJSON(json, callback, reviver); - }, - - /** - * Populates canvas with data from the specified JSON. - * JSON format must conform to the one of {@link fabric.Canvas#toJSON} - * @param {String|Object} json JSON string or object - * @param {Function} callback Callback, invoked when json is parsed - * and corresponding objects (e.g: {@link fabric.Image}) - * are initialized - * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created. - * @return {fabric.Canvas} instance - * @chainable - * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization} - * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo} - * @example loadFromJSON - * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas)); - * @example loadFromJSON with reviver - * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) { - * // `o` = json object - * // `object` = fabric.Object instance - * // ... do some stuff ... - * }); - */ - loadFromJSON: function (json, callback, reviver) { - if (!json) { - return; - } - - // serialize if it wasn't already - var serialized = (typeof json === 'string') - ? JSON.parse(json) - : json; - - this.clear(); - - var _this = this; - this._enlivenObjects(serialized.objects, function () { - _this._setBgOverlay(serialized, callback); - }, reviver); - - return this; - }, - - /** - * @private - * @param {Object} serialized Object with background and overlay information - * @param {Function} callback Invoked after all background and overlay images/patterns loaded - */ - _setBgOverlay: function(serialized, callback) { - var _this = this, - loaded = { - backgroundColor: false, - overlayColor: false, - backgroundImage: false, - overlayImage: false - }; - - if (!serialized.backgroundImage && !serialized.overlayImage && !serialized.background && !serialized.overlay) { - callback && callback(); - return; - } - - var cbIfLoaded = function () { - if (loaded.backgroundImage && loaded.overlayImage && loaded.backgroundColor && loaded.overlayColor) { - _this.renderAll(); - callback && callback(); - } - }; - - this.__setBgOverlay('backgroundImage', serialized.backgroundImage, loaded, cbIfLoaded); - this.__setBgOverlay('overlayImage', serialized.overlayImage, loaded, cbIfLoaded); - this.__setBgOverlay('backgroundColor', serialized.background, loaded, cbIfLoaded); - this.__setBgOverlay('overlayColor', serialized.overlay, loaded, cbIfLoaded); - - cbIfLoaded(); - }, - - /** - * @private - * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor) - * @param {(Object|String)} value Value to set - * @param {Object} loaded Set loaded property to true if property is set - * @param {Object} callback Callback function to invoke after property is set - */ - __setBgOverlay: function(property, value, loaded, callback) { - var _this = this; - - if (!value) { - loaded[property] = true; - return; - } - - if (property === 'backgroundImage' || property === 'overlayImage') { - fabric.Image.fromObject(value, function(img) { - _this[property] = img; - loaded[property] = true; - callback && callback(); - }); - } - else { - this['set' + fabric.util.string.capitalize(property, true)](value, function() { - loaded[property] = true; - callback && callback(); - }); - } - }, - - /** - * @private - * @param {Array} objects - * @param {Function} callback - * @param {Function} [reviver] - */ - _enlivenObjects: function (objects, callback, reviver) { - var _this = this; - - if (!objects || objects.length === 0) { - callback && callback(); - return; - } - - var renderOnAddRemove = this.renderOnAddRemove; - this.renderOnAddRemove = false; - - fabric.util.enlivenObjects(objects, function(enlivenedObjects) { - enlivenedObjects.forEach(function(obj, index) { - _this.insertAt(obj, index, true); - }); - - _this.renderOnAddRemove = renderOnAddRemove; - callback && callback(); - }, null, reviver); - }, - - /** - * @private - * @param {String} format - * @param {Function} callback - */ - _toDataURL: function (format, callback) { - this.clone(function (clone) { - callback(clone.toDataURL(format)); - }); - }, - - /** - * @private - * @param {String} format - * @param {Number} multiplier - * @param {Function} callback - */ - _toDataURLWithMultiplier: function (format, multiplier, callback) { - this.clone(function (clone) { - callback(clone.toDataURLWithMultiplier(format, multiplier)); - }); - }, - - /** - * Clones canvas instance - * @param {Object} [callback] Receives cloned instance as a first argument - * @param {Array} [properties] Array of properties to include in the cloned canvas and children - */ - clone: function (callback, properties) { - var data = JSON.stringify(this.toJSON(properties)); - this.cloneWithoutData(function(clone) { - clone.loadFromJSON(data, function() { - callback && callback(clone); - }); - }); - }, - - /** - * Clones canvas instance without cloning existing data. - * This essentially copies canvas dimensions, clipping properties, etc. - * but leaves data empty (so that you can populate it with your own) - * @param {Object} [callback] Receives cloned instance as a first argument - */ - cloneWithoutData: function(callback) { - var el = fabric.document.createElement('canvas'); - - el.width = this.getWidth(); - el.height = this.getHeight(); - - var clone = new fabric.Canvas(el); - clone.clipTo = this.clipTo; - if (this.backgroundImage) { - clone.setBackgroundImage(this.backgroundImage.src, function() { - clone.renderAll(); - callback && callback(clone); - }); - clone.backgroundImageOpacity = this.backgroundImageOpacity; - clone.backgroundImageStretch = this.backgroundImageStretch; - } - else { - callback && callback(clone); - } - } -}); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - toFixed = fabric.util.toFixed, - capitalize = fabric.util.string.capitalize, - degreesToRadians = fabric.util.degreesToRadians, - supportsLineDash = fabric.StaticCanvas.supports('setLineDash'); - - if (fabric.Object) { - return; - } - - /** - * Root object class from which all 2d shape classes inherit from - * @class fabric.Object - * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#objects} - * @see {@link fabric.Object#initialize} for constructor definition - * - * @fires added - * @fires removed - * - * @fires selected - * @fires modified - * @fires rotating - * @fires scaling - * @fires moving - * - * @fires mousedown - * @fires mouseup - */ - fabric.Object = fabric.util.createClass(/** @lends fabric.Object.prototype */ { - - /** - * Retrieves object's {@link fabric.Object#clipTo|clipping function} - * @method getClipTo - * @memberOf fabric.Object.prototype - * @return {Function} - */ - - /** - * Sets object's {@link fabric.Object#clipTo|clipping function} - * @method setClipTo - * @memberOf fabric.Object.prototype - * @param {Function} clipTo Clipping function - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#transformMatrix|transformMatrix} - * @method getTransformMatrix - * @memberOf fabric.Object.prototype - * @return {Array} transformMatrix - */ - - /** - * Sets object's {@link fabric.Object#transformMatrix|transformMatrix} - * @method setTransformMatrix - * @memberOf fabric.Object.prototype - * @param {Array} transformMatrix - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#visible|visible} state - * @method getVisible - * @memberOf fabric.Object.prototype - * @return {Boolean} True if visible - */ - - /** - * Sets object's {@link fabric.Object#visible|visible} state - * @method setVisible - * @memberOf fabric.Object.prototype - * @param {Boolean} value visible value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#shadow|shadow} - * @method getShadow - * @memberOf fabric.Object.prototype - * @return {Object} Shadow instance - */ - - /** - * Retrieves object's {@link fabric.Object#stroke|stroke} - * @method getStroke - * @memberOf fabric.Object.prototype - * @return {String} stroke value - */ - - /** - * Sets object's {@link fabric.Object#stroke|stroke} - * @method setStroke - * @memberOf fabric.Object.prototype - * @param {String} value stroke value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#strokeWidth|strokeWidth} - * @method getStrokeWidth - * @memberOf fabric.Object.prototype - * @return {Number} strokeWidth value - */ - - /** - * Sets object's {@link fabric.Object#strokeWidth|strokeWidth} - * @method setStrokeWidth - * @memberOf fabric.Object.prototype - * @param {Number} value strokeWidth value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#originX|originX} - * @method getOriginX - * @memberOf fabric.Object.prototype - * @return {String} originX value - */ - - /** - * Sets object's {@link fabric.Object#originX|originX} - * @method setOriginX - * @memberOf fabric.Object.prototype - * @param {String} value originX value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#originY|originY} - * @method getOriginY - * @memberOf fabric.Object.prototype - * @return {String} originY value - */ - - /** - * Sets object's {@link fabric.Object#originY|originY} - * @method setOriginY - * @memberOf fabric.Object.prototype - * @param {String} value originY value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#fill|fill} - * @method getFill - * @memberOf fabric.Object.prototype - * @return {String} Fill value - */ - - /** - * Sets object's {@link fabric.Object#fill|fill} - * @method setFill - * @memberOf fabric.Object.prototype - * @param {String} value Fill value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#opacity|opacity} - * @method getOpacity - * @memberOf fabric.Object.prototype - * @return {Number} Opacity value (0-1) - */ - - /** - * Sets object's {@link fabric.Object#opacity|opacity} - * @method setOpacity - * @memberOf fabric.Object.prototype - * @param {Number} value Opacity value (0-1) - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#angle|angle} (in degrees) - * @method getAngle - * @memberOf fabric.Object.prototype - * @return {Number} - */ - - /** - * Sets object's {@link fabric.Object#angle|angle} - * @method setAngle - * @memberOf fabric.Object.prototype - * @param {Number} value Angle value (in degrees) - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#top|top position} - * @method getTop - * @memberOf fabric.Object.prototype - * @return {Number} Top value (in pixels) - */ - - /** - * Sets object's {@link fabric.Object#top|top position} - * @method setTop - * @memberOf fabric.Object.prototype - * @param {Number} value Top value (in pixels) - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#left|left position} - * @method getLeft - * @memberOf fabric.Object.prototype - * @return {Number} Left value (in pixels) - */ - - /** - * Sets object's {@link fabric.Object#left|left position} - * @method setLeft - * @memberOf fabric.Object.prototype - * @param {Number} value Left value (in pixels) - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#scaleX|scaleX} value - * @method getScaleX - * @memberOf fabric.Object.prototype - * @return {Number} scaleX value - */ - - /** - * Sets object's {@link fabric.Object#scaleX|scaleX} value - * @method setScaleX - * @memberOf fabric.Object.prototype - * @param {Number} value scaleX value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#scaleY|scaleY} value - * @method getScaleY - * @memberOf fabric.Object.prototype - * @return {Number} scaleY value - */ - - /** - * Sets object's {@link fabric.Object#scaleY|scaleY} value - * @method setScaleY - * @memberOf fabric.Object.prototype - * @param {Number} value scaleY value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#flipX|flipX} value - * @method getFlipX - * @memberOf fabric.Object.prototype - * @return {Boolean} flipX value - */ - - /** - * Sets object's {@link fabric.Object#flipX|flipX} value - * @method setFlipX - * @memberOf fabric.Object.prototype - * @param {Boolean} value flipX value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Retrieves object's {@link fabric.Object#flipY|flipY} value - * @method getFlipY - * @memberOf fabric.Object.prototype - * @return {Boolean} flipY value - */ - - /** - * Sets object's {@link fabric.Object#flipY|flipY} value - * @method setFlipY - * @memberOf fabric.Object.prototype - * @param {Boolean} value flipY value - * @return {fabric.Object} thisArg - * @chainable - */ - - /** - * Type of an object (rect, circle, path, etc.) - * @type String - * @default - */ - type: 'object', - - /** - * Horizontal origin of transformation of an object (one of "left", "right", "center") - * @type String - * @default - */ - originX: 'left', - - /** - * Vertical origin of transformation of an object (one of "top", "bottom", "center") - * @type String - * @default - */ - originY: 'top', - - /** - * Top position of an object. Note that by default it's relative to object center. You can change this by setting originY={top/center/bottom} - * @type Number - * @default - */ - top: 0, - - /** - * Left position of an object. Note that by default it's relative to object center. You can change this by setting originX={left/center/right} - * @type Number - * @default - */ - left: 0, - - /** - * Object width - * @type Number - * @default - */ - width: 0, - - /** - * Object height - * @type Number - * @default - */ - height: 0, - - /** - * Object scale factor (horizontal) - * @type Number - * @default - */ - scaleX: 1, - - /** - * Object scale factor (vertical) - * @type Number - * @default - */ - scaleY: 1, - - /** - * When true, an object is rendered as flipped horizontally - * @type Boolean - * @default - */ - flipX: false, - - /** - * When true, an object is rendered as flipped vertically - * @type Boolean - * @default - */ - flipY: false, - - /** - * Opacity of an object - * @type Number - * @default - */ - opacity: 1, - - /** - * Angle of rotation of an object (in degrees) - * @type Number - * @default - */ - angle: 0, - - /** - * Size of object's controlling corners (in pixels) - * @type Number - * @default - */ - cornerSize: 12, - - /** - * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill) - * @type Boolean - * @default - */ - transparentCorners: true, - - /** - * Default cursor value used when hovering over this object on canvas - * @type String - * @default - */ - hoverCursor: null, - - /** - * Padding between object and its controlling borders (in pixels) - * @type Number - * @default - */ - padding: 0, - - /** - * Color of controlling borders of an object (when it's active) - * @type String - * @default - */ - borderColor: 'rgba(102,153,255,0.75)', - - /** - * Color of controlling corners of an object (when it's active) - * @type String - * @default - */ - cornerColor: 'rgba(102,153,255,0.5)', - - /** - * When true, this object will use center point as the origin of transformation - * when being scaled via the controls. - * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). - * @since 1.3.4 - * @type Boolean - * @default - */ - centeredScaling: false, - - /** - * When true, this object will use center point as the origin of transformation - * when being rotated via the controls. - * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). - * @since 1.3.4 - * @type Boolean - * @default - */ - centeredRotation: true, - - /** - * Color of object's fill - * @type String - * @default - */ - fill: 'rgb(0,0,0)', - - /** - * Fill rule used to fill an object - * @type String - * @default - */ - fillRule: 'source-over', - - /** - * Background color of an object. Only works with text objects at the moment. - * @type String - * @default - */ - backgroundColor: '', - - /** - * When defined, an object is rendered via stroke and this property specifies its color - * @type String - * @default - */ - stroke: null, - - /** - * Width of a stroke used to render this object - * @type Number - * @default - */ - strokeWidth: 1, - - /** - * Array specifying dash pattern of an object's stroke (stroke must be defined) - * @type Array - */ - strokeDashArray: null, - - /** - * Line endings style of an object's stroke (one of "butt", "round", "square") - * @type String - * @default - */ - strokeLineCap: 'butt', - - /** - * Corner style of an object's stroke (one of "bevil", "round", "miter") - * @type String - * @default - */ - strokeLineJoin: 'miter', - - /** - * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke - * @type Number - * @default - */ - strokeMiterLimit: 10, - - /** - * Shadow object representing shadow of this shape - * @type fabric.Shadow - * @default - */ - shadow: null, - - /** - * Opacity of object's controlling borders when object is active and moving - * @type Number - * @default - */ - borderOpacityWhenMoving: 0.4, - - /** - * Scale factor of object's controlling borders - * @type Number - * @default - */ - borderScaleFactor: 1, - - /** - * Transform matrix (similar to SVG's transform matrix) - * @type Array - */ - transformMatrix: null, - - /** - * Minimum allowed scale value of an object - * @type Number - * @default - */ - minScaleLimit: 0.01, - - /** - * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection). - * But events still fire on it. - * @type Boolean - * @default - */ - selectable: true, - - /** - * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4 - * @type Boolean - * @default - */ - evented: true, - - /** - * When set to `false`, an object is not rendered on canvas - * @type Boolean - * @default - */ - visible: true, - - /** - * When set to `false`, object's controls are not displayed and can not be used to manipulate object - * @type Boolean - * @default - */ - hasControls: true, - - /** - * When set to `false`, object's controlling borders are not rendered - * @type Boolean - * @default - */ - hasBorders: true, - - /** - * When set to `false`, object's controlling rotating point will not be visible or selectable - * @type Boolean - * @default - */ - hasRotatingPoint: true, - - /** - * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`) - * @type Number - * @default - */ - rotatingPointOffset: 40, - - /** - * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box - * @type Boolean - * @default - */ - perPixelTargetFind: false, - - /** - * When `false`, default object's values are not included in its serialization - * @type Boolean - * @default - */ - includeDefaultValues: true, - - /** - * Function that determines clipping of an object (context is passed as a first argument) - * Note that context origin is at the object's center point (not left/top corner) - * @type Function - */ - clipTo: null, - - /** - * When `true`, object horizontal movement is locked - * @type Boolean - * @default - */ - lockMovementX: false, - - /** - * When `true`, object vertical movement is locked - * @type Boolean - * @default - */ - lockMovementY: false, - - /** - * When `true`, object rotation is locked - * @type Boolean - * @default - */ - lockRotation: false, - - /** - * When `true`, object horizontal scaling is locked - * @type Boolean - * @default - */ - lockScalingX: false, - - /** - * When `true`, object vertical scaling is locked - * @type Boolean - * @default - */ - lockScalingY: false, - - /** - * When `true`, object non-uniform scaling is locked - * @type Boolean - * @default - */ - lockUniScaling: false, - - /** - * When `true`, object cannot be flipped by scaling into negative values - * @type Boolean - * @default - */ - - lockScalingFlip: false, - /** - * List of properties to consider when checking if state - * of an object is changed (fabric.Object#hasStateChanged) - * as well as for history (undo/redo) purposes - * @type Array - */ - stateProperties: ( - 'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' + - 'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' + - 'angle opacity fill fillRule shadow clipTo visible backgroundColor' - ).split(' '), - - /** - * Constructor - * @param {Object} [options] Options object - */ - initialize: function(options) { - if (options) { - this.setOptions(options); - } - }, - - /** - * @private - * @param {Object} [options] Options object - */ - _initGradient: function(options) { - if (options.fill && options.fill.colorStops && !(options.fill instanceof fabric.Gradient)) { - this.set('fill', new fabric.Gradient(options.fill)); - } - }, - - /** - * @private - * @param {Object} [options] Options object - */ - _initPattern: function(options) { - if (options.fill && options.fill.source && !(options.fill instanceof fabric.Pattern)) { - this.set('fill', new fabric.Pattern(options.fill)); - } - if (options.stroke && options.stroke.source && !(options.stroke instanceof fabric.Pattern)) { - this.set('stroke', new fabric.Pattern(options.stroke)); - } - }, - - /** - * @private - * @param {Object} [options] Options object - */ - _initClipping: function(options) { - if (!options.clipTo || typeof options.clipTo !== 'string') { - return; - } - - var functionBody = fabric.util.getFunctionBody(options.clipTo); - if (typeof functionBody !== 'undefined') { - this.clipTo = new Function('ctx', functionBody); - } - }, - - /** - * Sets object's properties from options - * @param {Object} [options] Options object - */ - setOptions: function(options) { - for (var prop in options) { - this.set(prop, options[prop]); - } - this._initGradient(options); - this._initPattern(options); - this._initClipping(options); - }, - - /** - * Transforms context when rendering an object - * @param {CanvasRenderingContext2D} ctx Context - * @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node - */ - transform: function(ctx, fromLeft) { - if (this.group) { - this.group.transform(ctx, fromLeft); - } - ctx.globalAlpha = this.opacity; - - var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint(); - ctx.translate(center.x, center.y); - ctx.rotate(degreesToRadians(this.angle)); - ctx.scale( - this.scaleX * (this.flipX ? -1 : 1), - this.scaleY * (this.flipY ? -1 : 1) - ); - }, - - /** - * Returns an object representation of an instance - * @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) { - var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, - - object = { - type: this.type, - originX: this.originX, - originY: this.originY, - left: toFixed(this.left, NUM_FRACTION_DIGITS), - top: toFixed(this.top, NUM_FRACTION_DIGITS), - width: toFixed(this.width, NUM_FRACTION_DIGITS), - height: toFixed(this.height, NUM_FRACTION_DIGITS), - fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill, - stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke, - strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS), - strokeDashArray: this.strokeDashArray, - strokeLineCap: this.strokeLineCap, - strokeLineJoin: this.strokeLineJoin, - strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS), - scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS), - scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS), - angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS), - flipX: this.flipX, - flipY: this.flipY, - opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS), - shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow, - visible: this.visible, - clipTo: this.clipTo && String(this.clipTo), - backgroundColor: this.backgroundColor - }; - - if (!this.includeDefaultValues) { - object = this._removeDefaultValues(object); - } - - fabric.util.populateWithProperties(this, object, propertiesToInclude); - - return object; - }, - - /** - * Returns (dataless) object representation of an instance - * @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) { - // will be overwritten by subclasses - return this.toObject(propertiesToInclude); - }, - - /** - * @private - * @param {Object} object - */ - _removeDefaultValues: function(object) { - var prototype = fabric.util.getKlass(object.type).prototype, - stateProperties = prototype.stateProperties; - - stateProperties.forEach(function(prop) { - if (object[prop] === prototype[prop]) { - delete object[prop]; - } - }); - - return object; - }, - - /** - * Returns a string representation of an instance - * @return {String} - */ - toString: function() { - return '#'; - }, - - /** - * Basic getter - * @param {String} property Property name - * @return {Any} value of a property - */ - get: function(property) { - return this[property]; - }, - - /** - * @private - */ - _setObject: function(obj) { - for (var prop in obj) { - this._set(prop, obj[prop]); - } - }, - - /** - * Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`. - * @param {String|Object} key Property name or object (if object, iterate over the object properties) - * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one) - * @return {fabric.Object} thisArg - * @chainable - */ - set: function(key, value) { - if (typeof key === 'object') { - this._setObject(key); - } - else { - if (typeof value === 'function' && key !== 'clipTo') { - this._set(key, value(this.get(key))); - } - else { - this._set(key, value); - } - } - return this; - }, - - /** - * @private - * @param {String} key - * @param {Any} value - * @return {fabric.Object} thisArg - */ - _set: function(key, value) { - var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY'); - - if (shouldConstrainValue) { - value = this._constrainScale(value); - } - if (key === 'scaleX' && value < 0) { - this.flipX = !this.flipX; - value *= -1; - } - else if (key === 'scaleY' && value < 0) { - this.flipY = !this.flipY; - value *= -1; - } - else if (key === 'width' || key === 'height') { - this.minScaleLimit = toFixed(Math.min(0.1, 1/Math.max(this.width, this.height)), 2); - } - else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) { - value = new fabric.Shadow(value); - } - - this[key] = value; - - return this; - }, - - /** - * Toggles specified property from `true` to `false` or from `false` to `true` - * @param {String} property Property to toggle - * @return {fabric.Object} thisArg - * @chainable - */ - toggle: function(property) { - var value = this.get(property); - if (typeof value === 'boolean') { - this.set(property, !value); - } - return this; - }, - - /** - * Sets sourcePath of an object - * @param {String} value Value to set sourcePath to - * @return {fabric.Object} thisArg - * @chainable - */ - setSourcePath: function(value) { - this.sourcePath = value; - return this; - }, - - /** - * Retrieves viewportTransform from Object's canvas if possible - * @method getViewportTransform - * @memberOf fabric.Object.prototype - * @return {Boolean} flipY value // TODO - */ - getViewportTransform: function() { - if (this.canvas && this.canvas.viewportTransform) { - return this.canvas.viewportTransform; - } - return [1, 0, 0, 1, 0, 0]; - }, - - /** - * Renders an object on a specified context - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - render: function(ctx, noTransform) { - // do not render if width/height are zeros or object is not visible - if (this.width === 0 || this.height === 0 || !this.visible) { - return; - } - - ctx.save(); - - //setup fill rule for current object - this._setupFillRule(ctx); - - this._transform(ctx, noTransform); - this._setStrokeStyles(ctx); - this._setFillStyles(ctx); - - if (this.group && this.group.type === 'path-group') { - ctx.translate(-this.group.width/2, -this.group.height/2); - var m = this.transformMatrix; - if (m) { - ctx.transform.apply(ctx, m); - } - } - ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity; - this._setShadow(ctx); - this.clipTo && fabric.util.clipContext(this, ctx); - this._render(ctx, noTransform); - this.clipTo && ctx.restore(); - this._removeShadow(ctx); - this._restoreFillRule(ctx); - - ctx.restore(); - }, - - _transform: function(ctx, noTransform) { - var m = this.transformMatrix; - - if (m && !this.group) { - ctx.setTransform.apply(ctx, m); - } - if (!noTransform) { - this.transform(ctx); - } - }, - - _setStrokeStyles: function(ctx) { - if (this.stroke) { - ctx.lineWidth = this.strokeWidth; - ctx.lineCap = this.strokeLineCap; - ctx.lineJoin = this.strokeLineJoin; - ctx.miterLimit = this.strokeMiterLimit; - ctx.strokeStyle = this.stroke.toLive - ? this.stroke.toLive(ctx) - : this.stroke; - } - }, - - _setFillStyles: function(ctx) { - if (this.fill) { - ctx.fillStyle = this.fill.toLive - ? this.fill.toLive(ctx) - : this.fill; - } - }, - - /** - * Renders controls and borders for the object - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - _renderControls: function(ctx, noTransform) { - var vpt = this.getViewportTransform(); - - ctx.save(); - if (this.active && !noTransform) { - var center; - if (this.group) { - center = fabric.util.transformPoint(this.group.getCenterPoint(), vpt); - ctx.translate(center.x, center.y); - ctx.rotate(degreesToRadians(this.group.angle)); - } - center = fabric.util.transformPoint(this.getCenterPoint(), vpt, null != this.group); - if (this.group) { - center.x *= this.group.scaleX; - center.y *= this.group.scaleY; - } - ctx.translate(center.x, center.y); - ctx.rotate(degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _setShadow: function(ctx) { - if (!this.shadow) { - return; - } - - ctx.shadowColor = this.shadow.color; - ctx.shadowBlur = this.shadow.blur; - ctx.shadowOffsetX = this.shadow.offsetX; - ctx.shadowOffsetY = this.shadow.offsetY; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _removeShadow: function(ctx) { - if (!this.shadow) { - return; - } - - ctx.shadowColor = ''; - ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderFill: function(ctx) { - if (!this.fill) { - return; - } - - ctx.save(); - if (this.fill.toLive) { - ctx.translate( - -this.width / 2 + this.fill.offsetX || 0, - -this.height / 2 + this.fill.offsetY || 0); - } - if (this.fill.gradientTransform) { - var g = this.fill.gradientTransform; - ctx.transform.apply(ctx, g); - } - if (this.fillRule === 'destination-over') { - ctx.fill('evenodd'); - } - else { - ctx.fill(); - } - ctx.restore(); - if (this.shadow && !this.shadow.affectStroke) { - this._removeShadow(ctx); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderStroke: function(ctx) { - if (!this.stroke || this.strokeWidth === 0) { - return; - } - - ctx.save(); - if (this.strokeDashArray) { - // Spec requires the concatenation of two copies the dash list when the number of elements is odd - if (1 & this.strokeDashArray.length) { - this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray); - } - - if (supportsLineDash) { - ctx.setLineDash(this.strokeDashArray); - this._stroke && this._stroke(ctx); - } - else { - this._renderDashedStroke && this._renderDashedStroke(ctx); - } - ctx.stroke(); - } - else { - if (this.stroke.gradientTransform) { - var g = this.stroke.gradientTransform; - ctx.transform.apply(ctx, g); - } - this._stroke ? this._stroke(ctx) : ctx.stroke(); - } - this._removeShadow(ctx); - ctx.restore(); - }, - - /** - * Clones an instance - * @param {Function} callback Callback is invoked with a clone as a first argument - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {fabric.Object} clone of an instance - */ - clone: function(callback, propertiesToInclude) { - if (this.constructor.fromObject) { - return this.constructor.fromObject(this.toObject(propertiesToInclude), callback); - } - return new fabric.Object(this.toObject(propertiesToInclude)); - }, - - /** - * Creates an instance of fabric.Image out of an object - * @param {Function} callback callback, invoked with an instance as a first argument - * @return {fabric.Object} thisArg - */ - cloneAsImage: function(callback) { - var dataUrl = this.toDataURL(); - fabric.util.loadImage(dataUrl, function(img) { - if (callback) { - callback(new fabric.Image(img)); - } - }); - return this; - }, - - /** - * Converts an object into a data-url-like string - * @param {Object} options Options object - * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png" - * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg. - * @param {Number} [options.multiplier=1] Multiplier to scale by - * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14 - * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14 - * @param {Number} [options.width] Cropping width. Introduced in v1.2.14 - * @param {Number} [options.height] Cropping height. Introduced in v1.2.14 - * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format - */ - toDataURL: function(options) { - options || (options = { }); - - var el = fabric.util.createCanvasElement(), - boundingRect = this.getBoundingRect(); - - el.width = boundingRect.width; - el.height = boundingRect.height; - - fabric.util.wrapElement(el, 'div'); - var canvas = new fabric.Canvas(el); - - // to avoid common confusion https://github.com/kangax/fabric.js/issues/806 - if (options.format === 'jpg') { - options.format = 'jpeg'; - } - - if (options.format === 'jpeg') { - canvas.backgroundColor = '#fff'; - } - - var origParams = { - active: this.get('active'), - left: this.getLeft(), - top: this.getTop() - }; - - this.set('active', false); - this.setPositionByOrigin(new fabric.Point(el.width / 2, el.height / 2), 'center', 'center'); - - var originalCanvas = this.canvas; - canvas.add(this); - var data = canvas.toDataURL(options); - - this.set(origParams).setCoords(); - this.canvas = originalCanvas; - - canvas.dispose(); - canvas = null; - - return data; - }, - - /** - * Returns true if specified type is identical to the type of an instance - * @param {String} type Type to check against - * @return {Boolean} - */ - isType: function(type) { - return this.type === type; - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return 0; - }, - - /** - * Returns a JSON representation of an instance - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} JSON - */ - toJSON: function(propertiesToInclude) { - // delegate, not alias - return this.toObject(propertiesToInclude); - }, - - /** - * Sets gradient (fill or stroke) of an object - * Backwards incompatibility note: This method was named "setGradientFill" until v1.1.0 - * @param {String} property Property name 'stroke' or 'fill' - * @param {Object} [options] Options object - * @param {String} [options.type] Type of gradient 'radial' or 'linear' - * @param {Number} [options.x1=0] x-coordinate of start point - * @param {Number} [options.y1=0] y-coordinate of start point - * @param {Number} [options.x2=0] x-coordinate of end point - * @param {Number} [options.y2=0] y-coordinate of end point - * @param {Number} [options.r1=0] Radius of start point (only for radial gradients) - * @param {Number} [options.r2=0] Radius of end point (only for radial gradients) - * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'} - * @return {fabric.Object} thisArg - * @chainable - * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo} - * @example Set linear gradient - * object.setGradient('fill', { - * type: 'linear', - * x1: -object.width / 2, - * y1: 0, - * x2: object.width / 2, - * y2: 0, - * colorStops: { - * 0: 'red', - * 0.5: '#005555', - * 1: 'rgba(0,0,255,0.5)' - * } - * }); - * canvas.renderAll(); - * @example Set radial gradient - * object.setGradient('fill', { - * type: 'radial', - * x1: 0, - * y1: 0, - * x2: 0, - * y2: 0, - * r1: object.width / 2, - * r2: 10, - * colorStops: { - * 0: 'red', - * 0.5: '#005555', - * 1: 'rgba(0,0,255,0.5)' - * } - * }); - * canvas.renderAll(); - */ - setGradient: function(property, options) { - options || (options = { }); - - var gradient = { colorStops: [] }; - - gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear'); - gradient.coords = { - x1: options.x1, - y1: options.y1, - x2: options.x2, - y2: options.y2 - }; - - if (options.r1 || options.r2) { - gradient.coords.r1 = options.r1; - gradient.coords.r2 = options.r2; - } - - for (var position in options.colorStops) { - var color = new fabric.Color(options.colorStops[position]); - gradient.colorStops.push({ - offset: position, - color: color.toRgb(), - opacity: color.getAlpha() - }); - } - - return this.set(property, fabric.Gradient.forObject(this, gradient)); - }, - - /** - * Sets pattern fill of an object - * @param {Object} options Options object - * @param {(String|HTMLImageElement)} options.source Pattern source - * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat) - * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner - * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner - * @return {fabric.Object} thisArg - * @chainable - * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo} - * @example Set pattern - * fabric.util.loadImage('http://fabricjs.com/assets/escheresque_ste.png', function(img) { - * object.setPatternFill({ - * source: img, - * repeat: 'repeat' - * }); - * canvas.renderAll(); - * }); - */ - setPatternFill: function(options) { - return this.set('fill', new fabric.Pattern(options)); - }, - - /** - * Sets {@link fabric.Object#shadow|shadow} of an object - * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)") - * @param {String} [options.color=rgb(0,0,0)] Shadow color - * @param {Number} [options.blur=0] Shadow blur - * @param {Number} [options.offsetX=0] Shadow horizontal offset - * @param {Number} [options.offsetY=0] Shadow vertical offset - * @return {fabric.Object} thisArg - * @chainable - * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo} - * @example Set shadow with string notation - * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)'); - * canvas.renderAll(); - * @example Set shadow with object notation - * object.setShadow({ - * color: 'red', - * blur: 10, - * offsetX: 20, - * offsetY: 20 - * }); - * canvas.renderAll(); - */ - setShadow: function(options) { - return this.set('shadow', options ? new fabric.Shadow(options) : null); - }, - - /** - * Sets "color" of an instance (alias of `set('fill', …)`) - * @param {String} color Color value - * @return {fabric.Object} thisArg - * @chainable - */ - setColor: function(color) { - this.set('fill', color); - return this; - }, - - /** - * Sets "angle" of an instance - * @param {Number} angle Angle value - * @return {fabric.Object} thisArg - * @chainable - */ - setAngle: function(angle) { - var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation; - - if (shouldCenterOrigin) { - this._setOriginToCenter(); - } - - this.set('angle', angle); - - if (shouldCenterOrigin) { - this._resetOrigin(); - } - - return this; - }, - - /** - * Centers object horizontally on canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - * @return {fabric.Object} thisArg - * @chainable - */ - centerH: function () { - this.canvas.centerObjectH(this); - return this; - }, - - /** - * Centers object vertically on canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - * @return {fabric.Object} thisArg - * @chainable - */ - centerV: function () { - this.canvas.centerObjectV(this); - return this; - }, - - /** - * Centers object vertically and horizontally on canvas to which is was added last - * You might need to call `setCoords` on an object after centering, to update controls area. - * @return {fabric.Object} thisArg - * @chainable - */ - center: function () { - this.canvas.centerObject(this); - return this; - }, - - /** - * Removes object from canvas to which it was added last - * @return {fabric.Object} thisArg - * @chainable - */ - remove: function() { - this.canvas.remove(this); - return this; - }, - - /** - * Returns coordinates of a pointer relative to an object - * @param {Event} e Event to operate upon - * @param {Object} [pointer] Pointer to operate upon (instead of event) - * @return {Object} Coordinates of a pointer (x, y) - */ - getLocalPointer: function(e, pointer) { - pointer = pointer || this.canvas.getPointer(e); - var objectLeftTop = this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top'); - return { - x: pointer.x - objectLeftTop.x, - y: pointer.y - objectLeftTop.y - }; - }, - - /** - * Sets canvas globalCompositeOperation for specific object - * custom composition operation for the particular object can be specifed using fillRule property - * @param {CanvasRenderingContext2D} ctx Rendering canvas context - */ - _setupFillRule: function (ctx) { - if (this.fillRule) { - this._prevFillRule = ctx.globalCompositeOperation; - ctx.globalCompositeOperation = this.fillRule; - } - }, - - /** - * Restores previously saved canvas globalCompositeOperation after obeject rendering - * @param {CanvasRenderingContext2D} ctx Rendering canvas context - */ - _restoreFillRule: function (ctx) { - if (this.fillRule && this._prevFillRule) { - ctx.globalCompositeOperation = this._prevFillRule; - } - } - }); - - fabric.util.createAccessors(fabric.Object); - - /** - * Alias for {@link fabric.Object.prototype.setAngle} - * @alias rotate -> setAngle - * @memberof fabric.Object - */ - fabric.Object.prototype.rotate = fabric.Object.prototype.setAngle; - - extend(fabric.Object.prototype, fabric.Observable); - - /** - * Defines the number of fraction digits to use when serializing object values. - * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc. - * @static - * @memberof fabric.Object - * @constant - * @type Number - */ - fabric.Object.NUM_FRACTION_DIGITS = 2; - - /** - * Unique id used internally when creating SVG elements - * @static - * @memberof fabric.Object - * @type Number - */ - fabric.Object.__uid = 0; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function() { - - var degreesToRadians = fabric.util.degreesToRadians; - - fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { - - /** - * Translates the coordinates from origin to center coordinates (based on the object's dimensions) - * @param {fabric.Point} point The point which corresponds to the originX and originY params - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' - * @return {fabric.Point} - */ - translateToCenterPoint: function(point, originX, originY) { - var cx = point.x, - cy = point.y, - strokeWidth = this.stroke ? this.strokeWidth : 0; - - if (originX === 'left') { - cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - else if (originX === 'right') { - cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - - if (originY === 'top') { - cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - else if (originY === 'bottom') { - cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - - // Apply the reverse rotation to the point (it's already scaled properly) - return fabric.util.rotatePoint(new fabric.Point(cx, cy), point, degreesToRadians(this.angle)); - }, - - /** - * Translates the coordinates from center to origin coordinates (based on the object's dimensions) - * @param {fabric.Point} center The point which corresponds to center of the object - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' - * @return {fabric.Point} - */ - translateToOriginPoint: function(center, originX, originY) { - var x = center.x, - y = center.y, - strokeWidth = this.stroke ? this.strokeWidth : 0; - - // Get the point coordinates - if (originX === 'left') { - x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - else if (originX === 'right') { - x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - if (originY === 'top') { - y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - else if (originY === 'bottom') { - y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - - // Apply the rotation to the point (it's already scaled properly) - return fabric.util.rotatePoint(new fabric.Point(x, y), center, degreesToRadians(this.angle)); - }, - - /** - * Returns the real center coordinates of the object - * @return {fabric.Point} - */ - getCenterPoint: function() { - var leftTop = new fabric.Point(this.left, this.top); - return this.translateToCenterPoint(leftTop, this.originX, this.originY); - }, - - /** - * Returns the coordinates of the object based on center coordinates - * @param {fabric.Point} point The point which corresponds to the originX and originY params - * @return {fabric.Point} - */ - // getOriginPoint: function(center) { - // return this.translateToOriginPoint(center, this.originX, this.originY); - // }, - - /** - * Returns the coordinates of the object as if it has a different origin - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' - * @return {fabric.Point} - */ - getPointByOrigin: function(originX, originY) { - var center = this.getCenterPoint(); - return this.translateToOriginPoint(center, originX, originY); - }, - - /** - * Returns the point in local coordinates - * @param {fabric.Point} point The point relative to the global coordinate system - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' - * @return {fabric.Point} - */ - toLocalPoint: function(point, originX, originY) { - var center = this.getCenterPoint(), - strokeWidth = this.stroke ? this.strokeWidth : 0, - x, y; - - if (originX && originY) { - if (originX === 'left') { - x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - else if (originX === 'right') { - x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2; - } - else { - x = center.x; - } - - if (originY === 'top') { - y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - else if (originY === 'bottom') { - y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2; - } - else { - y = center.y; - } - } - else { - x = this.left; - y = this.top; - } - - return fabric.util.rotatePoint(new fabric.Point(point.x, point.y), center, -degreesToRadians(this.angle)) - .subtractEquals(new fabric.Point(x, y)); - }, - - /** - * Returns the point in global coordinates - * @param {fabric.Point} The point relative to the local coordinate system - * @return {fabric.Point} - */ - // toGlobalPoint: function(point) { - // return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top)); - // }, - - /** - * Sets the position of the object taking into consideration the object's origin - * @param {fabric.Point} pos The new position of the object - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' - * @return {void} - */ - setPositionByOrigin: function(pos, originX, originY) { - var center = this.translateToCenterPoint(pos, originX, originY), - position = this.translateToOriginPoint(center, this.originX, this.originY); - - this.set('left', position.x); - this.set('top', position.y); - }, - - /** - * @param {String} to One of 'left', 'center', 'right' - */ - adjustPosition: function(to) { - var angle = degreesToRadians(this.angle), - hypotHalf = this.getWidth() / 2, - xHalf = Math.cos(angle) * hypotHalf, - yHalf = Math.sin(angle) * hypotHalf, - hypotFull = this.getWidth(), - xFull = Math.cos(angle) * hypotFull, - yFull = Math.sin(angle) * hypotFull; - - if (this.originX === 'center' && to === 'left' || - this.originX === 'right' && to === 'center') { - // move half left - this.left -= xHalf; - this.top -= yHalf; - } - else if (this.originX === 'left' && to === 'center' || - this.originX === 'center' && to === 'right') { - // move half right - this.left += xHalf; - this.top += yHalf; - } - else if (this.originX === 'left' && to === 'right') { - // move full right - this.left += xFull; - this.top += yFull; - } - else if (this.originX === 'right' && to === 'left') { - // move full left - this.left -= xFull; - this.top -= yFull; - } - - this.setCoords(); - this.originX = to; - }, - - /** - * Sets the origin/position of the object to it's center point - * @private - * @return {void} - */ - _setOriginToCenter: function() { - this._originalOriginX = this.originX; - this._originalOriginY = this.originY; - - var center = this.getCenterPoint(); - - this.originX = 'center'; - this.originY = 'center'; - - this.left = center.x; - this.top = center.y; - }, - - /** - * Resets the origin/position of the object to it's original origin - * @private - * @return {void} - */ - _resetOrigin: function() { - var originPoint = this.translateToOriginPoint( - this.getCenterPoint(), - this._originalOriginX, - this._originalOriginY); - - this.originX = this._originalOriginX; - this.originY = this._originalOriginY; - - this.left = originPoint.x; - this.top = originPoint.y; - - this._originalOriginX = null; - this._originalOriginY = null; - }, - - /** - * @private - */ - _getLeftTopCoords: function() { - return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'center'); - } - }); - -})(); - - -(function() { - - var degreesToRadians = fabric.util.degreesToRadians; - - fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { - - /** - * Object containing coordinates of object's controls - * @type Object - * @default - */ - oCoords: null, - - /** - * Checks if object intersects with an area formed by 2 points - * @param {Object} pointTL top-left point of area - * @param {Object} pointBR bottom-right point of area - * @return {Boolean} true if object intersects with an area formed by 2 points - */ - intersectsWithRect: function(pointTL, pointBR) { - var oCoords = this.oCoords, - tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y), - tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y), - bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y), - br = new fabric.Point(oCoords.br.x, oCoords.br.y), - intersection = fabric.Intersection.intersectPolygonRectangle( - [tl, tr, br, bl], - pointTL, - pointBR - ); - return intersection.status === 'Intersection'; - }, - - /** - * Checks if object intersects with another object - * @param {Object} other Object to test - * @return {Boolean} true if object intersects with another object - */ - intersectsWithObject: function(other) { - // extracts coords - function getCoords(oCoords) { - return { - tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y), - tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y), - bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y), - br: new fabric.Point(oCoords.br.x, oCoords.br.y) - }; - } - var thisCoords = getCoords(this.oCoords), - otherCoords = getCoords(other.oCoords), - intersection = fabric.Intersection.intersectPolygonPolygon( - [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl], - [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl] - ); - - return intersection.status === 'Intersection'; - }, - - /** - * Checks if object is fully contained within area of another object - * @param {Object} other Object to test - * @return {Boolean} true if object is fully contained within area of another object - */ - isContainedWithinObject: function(other) { - var boundingRect = other.getBoundingRect(), - point1 = new fabric.Point(boundingRect.left, boundingRect.top), - point2 = new fabric.Point(boundingRect.left + boundingRect.width, boundingRect.top + boundingRect.height); - - return this.isContainedWithinRect(point1, point2); - }, - - /** - * Checks if object is fully contained within area formed by 2 points - * @param {Object} pointTL top-left point of area - * @param {Object} pointBR bottom-right point of area - * @return {Boolean} true if object is fully contained within area formed by 2 points - */ - isContainedWithinRect: function(pointTL, pointBR) { - var boundingRect = this.getBoundingRect(); - - return ( - boundingRect.left >= pointTL.x && - boundingRect.left + boundingRect.width <= pointBR.x && - boundingRect.top >= pointTL.y && - boundingRect.top + boundingRect.height <= pointBR.y - ); - }, - - /** - * Checks if point is inside the object - * @param {fabric.Point} point Point to check against - * @return {Boolean} true if point is inside the object - */ - containsPoint: function(point) { - var lines = this._getImageLines(this.oCoords), - xPoints = this._findCrossPoints(point, lines); - - // if xPoints is odd then point is inside the object - return (xPoints !== 0 && xPoints % 2 === 1); - }, - - /** - * Method that returns an object with the object edges in it, given the coordinates of the corners - * @private - * @param {Object} oCoords Coordinates of the object corners - */ - _getImageLines: function(oCoords) { - return { - topline: { - o: oCoords.tl, - d: oCoords.tr - }, - rightline: { - o: oCoords.tr, - d: oCoords.br - }, - bottomline: { - o: oCoords.br, - d: oCoords.bl - }, - leftline: { - o: oCoords.bl, - d: oCoords.tl - } - }; - }, - - /** - * Helper method to determine how many cross points are between the 4 object edges - * and the horizontal line determined by a point on canvas - * @private - * @param {fabric.Point} point Point to check - * @param {Object} oCoords Coordinates of the object being evaluated - */ - _findCrossPoints: function(point, oCoords) { - var b1, b2, a1, a2, xi, yi, - xcount = 0, - iLine; - - for (var lineKey in oCoords) { - iLine = oCoords[lineKey]; - // optimisation 1: line below point. no cross - if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) { - continue; - } - // optimisation 2: line above point. no cross - if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) { - continue; - } - // optimisation 3: vertical line case - if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) { - xi = iLine.o.x; - yi = point.y; - } - // calculate the intersection point - else { - b1 = 0; - b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x); - a1 = point.y - b1 * point.x; - a2 = iLine.o.y - b2 * iLine.o.x; - - xi = - (a1 - a2) / (b1 - b2); - yi = a1 + b1 * xi; - } - // dont count xi < point.x cases - if (xi >= point.x) { - xcount += 1; - } - // optimisation 4: specific for square images - if (xcount === 2) { - break; - } - } - return xcount; - }, - - /** - * Returns width of an object's bounding rectangle - * @deprecated since 1.0.4 - * @return {Number} width value - */ - getBoundingRectWidth: function() { - return this.getBoundingRect().width; - }, - - /** - * Returns height of an object's bounding rectangle - * @deprecated since 1.0.4 - * @return {Number} height value - */ - getBoundingRectHeight: function() { - return this.getBoundingRect().height; - }, - - /** - * Returns coordinates of object's bounding rectangle (left, top, width, height) - * @return {Object} Object with left, top, width, height properties - */ - getBoundingRect: function() { - this.oCoords || this.setCoords(); - - var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x], - minX = fabric.util.array.min(xCoords), - maxX = fabric.util.array.max(xCoords), - width = Math.abs(minX - maxX), - - yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y], - minY = fabric.util.array.min(yCoords), - maxY = fabric.util.array.max(yCoords), - height = Math.abs(minY - maxY); - - return { - left: minX, - top: minY, - width: width, - height: height - }; - }, - - /** - * Returns width of an object - * @return {Number} width value - */ - getWidth: function() { - return this.width * this.scaleX; - }, - - /** - * Returns height of an object - * @return {Number} height value - */ - getHeight: function() { - return this.height * this.scaleY; - }, - - /** - * Makes sure the scale is valid and modifies it if necessary - * @private - * @param {Number} value - * @return {Number} - */ - _constrainScale: function(value) { - if (Math.abs(value) < this.minScaleLimit) { - if (value < 0) { - return -this.minScaleLimit; - } - else { - return this.minScaleLimit; - } - } - return value; - }, - - /** - * Scales an object (equally by x and y) - * @param {Number} value Scale factor - * @return {fabric.Object} thisArg - * @chainable - */ - scale: function(value) { - value = this._constrainScale(value); - - if (value < 0) { - this.flipX = !this.flipX; - this.flipY = !this.flipY; - value *= -1; - } - - this.scaleX = value; - this.scaleY = value; - this.setCoords(); - return this; - }, - - /** - * Scales an object to a given width, with respect to bounding box (scaling by x/y equally) - * @param {Number} value New width value - * @return {fabric.Object} thisArg - * @chainable - */ - scaleToWidth: function(value) { - // adjust to bounding rect factor so that rotated shapes would fit as well - var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth(); - return this.scale(value / this.width / boundingRectFactor); - }, - - /** - * Scales an object to a given height, with respect to bounding box (scaling by x/y equally) - * @param {Number} value New height value - * @return {fabric.Object} thisArg - * @chainable - */ - scaleToHeight: function(value) { - // adjust to bounding rect factor so that rotated shapes would fit as well - var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight(); - return this.scale(value / this.height / boundingRectFactor); - }, - - /** - * Sets corner position coordinates based on current angle, width and height - * @return {fabric.Object} thisArg - * @chainable - */ - setCoords: function() { - var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0, - theta = degreesToRadians(this.angle), - vpt = this.getViewportTransform(), - f = function (p) { - return fabric.util.transformPoint(p, vpt); - }, - w = this.width, - h = this.height, - capped = this.strokeLineCap === 'round' || this.strokeLineCap === 'square', - vLine = this.type === 'line' && this.width === 1, - hLine = this.type === 'line' && this.height === 1, - strokeW = (capped && hLine) || this.type !== 'line', - strokeH = (capped && vLine) || this.type !== 'line'; - - if (vLine) { - w = strokeWidth; - } - else if (hLine) { - h = strokeWidth; - } - if (strokeW) { - w += strokeWidth; - } - if (strokeH) { - h += strokeWidth; - } - this.currentWidth = w * this.scaleX; - this.currentHeight = h * this.scaleY; - - // If width is negative, make postive. Fixes path selection issue - if (this.currentWidth < 0) { - this.currentWidth = Math.abs(this.currentWidth); - } - - var _hypotenuse = Math.sqrt( - Math.pow(this.currentWidth / 2, 2) + - Math.pow(this.currentHeight / 2, 2)), - - _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0), - - // offset added for rotate and scale actions - offsetX = Math.cos(_angle + theta) * _hypotenuse, - offsetY = Math.sin(_angle + theta) * _hypotenuse, - sinTh = Math.sin(theta), - cosTh = Math.cos(theta), - coords = this.getCenterPoint(), - wh = new fabric.Point(this.currentWidth, this.currentHeight), - _tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY), - _tr = new fabric.Point(_tl.x + (wh.x * cosTh), _tl.y + (wh.x * sinTh)), - _bl = new fabric.Point(_tl.x - (wh.y * sinTh), _tl.y + (wh.y * cosTh)), - _mt = new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh)), - tl = f(_tl), - tr = f(_tr), - br = f(new fabric.Point(_tr.x - (wh.y * sinTh), _tr.y + (wh.y * cosTh))), - bl = f(_bl), - ml = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh))), - mt = f(_mt), - mr = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh))), - mb = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh))), - mtr = f(new fabric.Point(_mt.x, _mt.y)), - - // padding - padX = Math.cos(_angle + theta) * this.padding * Math.sqrt(2), - padY = Math.sin(_angle + theta) * this.padding * Math.sqrt(2); - - tl = tl.add(new fabric.Point(-padX, -padY)); - tr = tr.add(new fabric.Point(padY, -padX)); - br = br.add(new fabric.Point(padX, padY)); - bl = bl.add(new fabric.Point(-padY, padX)); - ml = ml.add(new fabric.Point((-padX - padY) / 2, (-padY + padX) / 2)); - mt = mt.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2)); - mr = mr.add(new fabric.Point((padY + padX) / 2, (padY - padX) / 2)); - mb = mb.add(new fabric.Point((padX - padY) / 2, (padX + padY) / 2)); - mtr = mtr.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2)); - - // debugging - - // setTimeout(function() { - // canvas.contextTop.fillStyle = 'green'; - // canvas.contextTop.fillRect(mb.x, mb.y, 3, 3); - // canvas.contextTop.fillRect(bl.x, bl.y, 3, 3); - // canvas.contextTop.fillRect(br.x, br.y, 3, 3); - // canvas.contextTop.fillRect(tl.x, tl.y, 3, 3); - // canvas.contextTop.fillRect(tr.x, tr.y, 3, 3); - // canvas.contextTop.fillRect(ml.x, ml.y, 3, 3); - // canvas.contextTop.fillRect(mr.x, mr.y, 3, 3); - // canvas.contextTop.fillRect(mt.x, mt.y, 3, 3); - // }, 50); - - this.oCoords = { - // corners - tl: tl, tr: tr, br: br, bl: bl, - // middle - ml: ml, mt: mt, mr: mr, mb: mb, - // rotating point - mtr: mtr - }; - - // set coordinates of the draggable boxes in the corners used to scale/rotate the image - this._setCornerCoords && this._setCornerCoords(); - - return this; - } - }); -})(); - - -fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { - - /** - * Moves an object to the bottom of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - sendToBack: function() { - if (this.group) { - fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); - } - else { - this.canvas.sendToBack(this); - } - return this; - }, - - /** - * Moves an object to the top of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - bringToFront: function() { - if (this.group) { - fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); - } - else { - this.canvas.bringToFront(this); - } - return this; - }, - - /** - * Moves an object down in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - sendBackwards: function(intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting); - } - else { - this.canvas.sendBackwards(this, intersecting); - } - return this; - }, - - /** - * Moves an object up in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - bringForward: function(intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting); - } - else { - this.canvas.bringForward(this, intersecting); - } - return this; - }, - - /** - * Moves an object to specified level in stack of drawn objects - * @param {Number} index New position of object - * @return {fabric.Object} thisArg - * @chainable - */ - moveTo: function(index) { - if (this.group) { - fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); - } - else { - this.canvas.moveTo(this, index); - } - return this; - } -}); - - -/* _TO_SVG_START_ */ -fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { - - /** - * Returns styles-string for svg-export - * @return {String} - */ - getSvgStyles: function() { - - var fill = this.fill - ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill) - : 'none', - fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule), - stroke = this.stroke - ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke) - : 'none', - - strokeWidth = this.strokeWidth ? this.strokeWidth : '0', - strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '', - strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt', - strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter', - strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4', - opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1', - - visibility = this.visible ? '' : ' visibility: hidden;', - filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : ''; - - return [ - 'stroke: ', stroke, '; ', - 'stroke-width: ', strokeWidth, '; ', - 'stroke-dasharray: ', strokeDashArray, '; ', - 'stroke-linecap: ', strokeLineCap, '; ', - 'stroke-linejoin: ', strokeLineJoin, '; ', - 'stroke-miterlimit: ', strokeMiterLimit, '; ', - 'fill: ', fill, '; ', - 'fill-rule: ', fillRule, '; ', - 'opacity: ', opacity, ';', - filter, - visibility - ].join(''); - }, - - /** - * Returns transform-string for svg-export - * @return {String} - */ - getSvgTransform: function() { - if (this.group) { - return ''; - } - var toFixed = fabric.util.toFixed, - angle = this.getAngle(), - vpt = !this.canvas || this.canvas.svgViewportTransformation ? this.getViewportTransform() : [1, 0, 0, 1, 0, 0], - center = fabric.util.transformPoint(this.getCenterPoint(), vpt), - - NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, - - translatePart = this.type === 'path-group' ? '' : 'translate(' + - toFixed(center.x, NUM_FRACTION_DIGITS) + - ' ' + - toFixed(center.y, NUM_FRACTION_DIGITS) + - ')', - - anglePart = angle !== 0 - ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')') - : '', - - scalePart = (this.scaleX === 1 && this.scaleY === 1 && vpt[0] === 1 && vpt[3] === 1) - ? '' : - (' scale(' + - toFixed(this.scaleX * vpt[0], NUM_FRACTION_DIGITS) + - ' ' + - toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) + - ')'), - - addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0, - - flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '', - - addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0, - - flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : ''; - - return [ - translatePart, anglePart, scalePart, flipXPart, flipYPart - ].join(''); - }, - - /** - * Returns transform-string for svg-export from the transform matrix of single elements - * @return {String} - */ - getSvgTransformMatrix: function() { - return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : ''; - }, - - /** - * @private - */ - _createBaseSVGMarkup: function() { - var markup = [ ]; - - if (this.fill && this.fill.toLive) { - markup.push(this.fill.toSVG(this, false)); - } - if (this.stroke && this.stroke.toLive) { - markup.push(this.stroke.toSVG(this, false)); - } - if (this.shadow) { - markup.push(this.shadow.toSVG(this)); - } - return markup; - } -}); -/* _TO_SVG_END_ */ - - -/* - Depends on `stateProperties` -*/ -fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { - - /** - * Returns true if object state (one of its state properties) was changed - * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called - */ - hasStateChanged: function() { - return this.stateProperties.some(function(prop) { - return this.get(prop) !== this.originalState[prop]; - }, this); - }, - - /** - * Saves state of an object - * @param {Object} [options] Object with additional `stateProperties` array to include when saving state - * @return {fabric.Object} thisArg - */ - saveState: function(options) { - this.stateProperties.forEach(function(prop) { - this.originalState[prop] = this.get(prop); - }, this); - - if (options && options.stateProperties) { - options.stateProperties.forEach(function(prop) { - this.originalState[prop] = this.get(prop); - }, this); - } - - return this; - }, - - /** - * Setups state of an object - * @return {fabric.Object} thisArg - */ - setupState: function() { - this.originalState = { }; - this.saveState(); - - return this; - } -}); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 }, - supportsLineDash = fabric.StaticCanvas.supports('setLineDash'); - - if (fabric.Line) { - fabric.warn('fabric.Line is already defined'); - return; - } - - /** - * Line class - * @class fabric.Line - * @extends fabric.Object - * @see {@link fabric.Line#initialize} for constructor definition - */ - fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'line', - - /** - * x value or first line edge - * @type Number - * @default - */ - x1: 0, - - /** - * y value or first line edge - * @type Number - * @default - */ - y1: 0, - - /** - * x value or second line edge - * @type Number - * @default - */ - x2: 0, - - /** - * y value or second line edge - * @type Number - * @default - */ - y2: 0, - - /** - * Constructor - * @param {Array} [points] Array of points - * @param {Object} [options] Options object - * @return {fabric.Line} thisArg - */ - initialize: function(points, options) { - options = options || { }; - - if (!points) { - points = [0, 0, 0, 0]; - } - - this.callSuper('initialize', options); - - this.set('x1', points[0]); - this.set('y1', points[1]); - this.set('x2', points[2]); - this.set('y2', points[3]); - - this._setWidthHeight(options); - }, - - /** - * @private - * @param {Object} [options] Options - */ - _setWidthHeight: function(options) { - options || (options = { }); - - this.width = Math.abs(this.x2 - this.x1) || 1; - this.height = Math.abs(this.y2 - this.y1) || 1; - - this.left = 'left' in options - ? options.left - : this._getLeftToOriginX(); - - this.top = 'top' in options - ? options.top - : this._getTopToOriginY(); - }, - - /** - * @private - * @param {String} key - * @param {Any} value - */ - _set: function(key, value) { - this[key] = value; - if (typeof coordProps[key] !== 'undefined') { - this._setWidthHeight(); - } - return this; - }, - - /** - * @private - * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line. - */ - _getLeftToOriginX: makeEdgeToOriginGetter( - { // property names - origin: 'originX', - axis1: 'x1', - axis2: 'x2', - dimension: 'width' - }, - { // possible values of origin - nearest: 'left', - center: 'center', - farthest: 'right' - } - ), - - /** - * @private - * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line. - */ - _getTopToOriginY: makeEdgeToOriginGetter( - { // property names - origin: 'originY', - axis1: 'y1', - axis2: 'y2', - dimension: 'height' - }, - { // possible values of origin - nearest: 'top', - center: 'center', - farthest: 'bottom' - } - ), - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx, noTransform) { - ctx.beginPath(); - - if (noTransform) { - // Line coords are distances from left-top of canvas to origin of line. - // - // To render line in a path-group, we need to translate them to - // distances from center of path-group to center of line. - var cp = this.getCenterPoint(); - ctx.translate( - cp.x, - cp.y - ); - } - - if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) { - - // move from center (of virtual box) to its left/top corner - // we can't assume x1, y1 is top left and x2, y2 is bottom right - var xMult = this.x1 <= this.x2 ? -1 : 1, - yMult = this.y1 <= this.y2 ? -1 : 1; - - ctx.moveTo( - this.width === 1 ? 0 : (xMult * this.width / 2), - this.height === 1 ? 0 : (yMult * this.height / 2)); - - ctx.lineTo( - this.width === 1 ? 0 : (xMult * -1 * this.width / 2), - this.height === 1 ? 0 : (yMult * -1 * this.height / 2)); - } - - ctx.lineWidth = this.strokeWidth; - - // TODO: test this - // make sure setting "fill" changes color of a line - // (by copying fillStyle to strokeStyle, since line is stroked, not filled) - var origStrokeStyle = ctx.strokeStyle; - ctx.strokeStyle = this.stroke || ctx.fillStyle; - this.stroke && this._renderStroke(ctx); - ctx.strokeStyle = origStrokeStyle; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var - xMult = this.x1 <= this.x2 ? -1 : 1, - yMult = this.y1 <= this.y2 ? -1 : 1, - x = this.width === 1 ? 0 : xMult * this.width / 2, - y = this.height === 1 ? 0 : yMult * this.height / 2; - - ctx.beginPath(); - fabric.util.drawDashedLine(ctx, x, y, -x, -y, this.strokeDashArray); - ctx.closePath(); - }, - - /** - * Returns object representation of an instance - * @methd toObject - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - x1: this.get('x1'), - y1: this.get('y1'), - x2: this.get('x2'), - y2: this.get('y2') - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = this._createBaseSVGMarkup(), addTranslate = ''; - if (!this.group) { - var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1), - y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1); - addTranslate = 'translate(' + x + ', ' + y + ') '; - } - markup.push( - '\n' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns complexity of an instance - * @return {Number} complexity - */ - complexity: function() { - return 1; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement}) - * @static - * @memberOf fabric.Line - * @see http://www.w3.org/TR/SVG/shapes.html#LineElement - */ - fabric.Line.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' ')); - - /** - * Returns fabric.Line instance from an SVG element - * @static - * @memberOf fabric.Line - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Line} instance of fabric.Line - */ - fabric.Line.fromElement = function(element, options) { - var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES), - points = [ - parsedAttributes.x1 || 0, - parsedAttributes.y1 || 0, - parsedAttributes.x2 || 0, - parsedAttributes.y2 || 0 - ]; - return new fabric.Line(points, extend(parsedAttributes, options)); - }; - /* _FROM_SVG_END_ */ - - /** - * Returns fabric.Line instance from an object representation - * @static - * @memberOf fabric.Line - * @param {Object} object Object to create an instance from - * @return {fabric.Line} instance of fabric.Line - */ - fabric.Line.fromObject = function(object) { - var points = [object.x1, object.y1, object.x2, object.y2]; - return new fabric.Line(points, object); - }; - - /** - * Produces a function that calculates distance from canvas edge to Line origin. - */ - function makeEdgeToOriginGetter(propertyNames, originValues) { - var origin = propertyNames.origin, - axis1 = propertyNames.axis1, - axis2 = propertyNames.axis2, - dimension = propertyNames.dimension, - nearest = originValues.nearest, - center = originValues.center, - farthest = originValues.farthest; - - return function() { - switch (this.get(origin)) { - case nearest: - return Math.min(this.get(axis1), this.get(axis2)); - case center: - return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension)); - case farthest: - return Math.max(this.get(axis1), this.get(axis2)); - } - }; - - } - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - piBy2 = Math.PI * 2, - extend = fabric.util.object.extend; - - if (fabric.Circle) { - fabric.warn('fabric.Circle is already defined.'); - return; - } - - /** - * Circle class - * @class fabric.Circle - * @extends fabric.Object - * @see {@link fabric.Circle#initialize} for constructor definition - */ - fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'circle', - - /** - * Radius of this circle - * @type Number - * @default - */ - radius: 0, - - /** - * Constructor - * @param {Object} [options] Options object - * @return {fabric.Circle} thisArg - */ - initialize: function(options) { - options = options || { }; - - this.callSuper('initialize', options); - this.set('radius', options.radius || 0); - }, - - /** - * @private - * @param {String} key - * @param {Any} value - * @return {fabric.Circle} thisArg - */ - _set: function(key, value) { - this.callSuper('_set', key, value); - - if (key === 'radius') { - this.setRadius(value); - } - - return this; - }, - - /** - * Returns object representation of an instance - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - radius: this.get('radius') - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = this._createBaseSVGMarkup(), x = 0, y = 0; - if (this.group) { - x = this.left + this.radius; - y = this.top + this.radius; - } - markup.push( - '\n' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * @private - * @param {CanvasRenderingContext2D} ctx context to render on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - _render: function(ctx, noTransform) { - ctx.beginPath(); - ctx.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false); - this._renderFill(ctx); - this._renderStroke(ctx); - }, - - /** - * Returns horizontal radius of an object (according to how an object is scaled) - * @return {Number} - */ - getRadiusX: function() { - return this.get('radius') * this.get('scaleX'); - }, - - /** - * Returns vertical radius of an object (according to how an object is scaled) - * @return {Number} - */ - getRadiusY: function() { - return this.get('radius') * this.get('scaleY'); - }, - - /** - * Sets radius of an object (and updates width accordingly) - * @return {Number} - */ - setRadius: function(value) { - this.radius = value; - this.set('width', value * 2).set('height', value * 2); - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return 1; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement}) - * @static - * @memberOf fabric.Circle - * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement - */ - fabric.Circle.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' ')); - - /** - * Returns {@link fabric.Circle} instance from an SVG element - * @static - * @memberOf fabric.Circle - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @throws {Error} If value of `r` attribute is missing or invalid - * @return {fabric.Circle} Instance of fabric.Circle - */ - fabric.Circle.fromElement = function(element, options) { - options || (options = { }); - - var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES); - - if (!isValidRadius(parsedAttributes)) { - throw new Error('value of `r` attribute is required and can not be negative'); - } - - parsedAttributes.left = parsedAttributes.left || 0; - parsedAttributes.top = parsedAttributes.top || 0; - - var obj = new fabric.Circle(extend(parsedAttributes, options)); - - obj.left -= obj.radius; - obj.top -= obj.radius; - return obj; - }; - - /** - * @private - */ - function isValidRadius(attributes) { - return (('radius' in attributes) && (attributes.radius > 0)); - } - /* _FROM_SVG_END_ */ - - /** - * Returns {@link fabric.Circle} instance from an object representation - * @static - * @memberOf fabric.Circle - * @param {Object} object Object to create an instance from - * @return {Object} Instance of fabric.Circle - */ - fabric.Circle.fromObject = function(object) { - return new fabric.Circle(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }); - - if (fabric.Triangle) { - fabric.warn('fabric.Triangle is already defined'); - return; - } - - /** - * Triangle class - * @class fabric.Triangle - * @extends fabric.Object - * @return {fabric.Triangle} thisArg - * @see {@link fabric.Triangle#initialize} for constructor definition - */ - fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'triangle', - - /** - * Constructor - * @param {Object} [options] Options object - * @return {Object} thisArg - */ - initialize: function(options) { - options = options || { }; - - this.callSuper('initialize', options); - - this.set('width', options.width || 100) - .set('height', options.height || 100); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - var widthBy2 = this.width / 2, - heightBy2 = this.height / 2; - - ctx.beginPath(); - ctx.moveTo(-widthBy2, heightBy2); - ctx.lineTo(0, -heightBy2); - ctx.lineTo(widthBy2, heightBy2); - ctx.closePath(); - - this._renderFill(ctx); - this._renderStroke(ctx); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var widthBy2 = this.width / 2, - heightBy2 = this.height / 2; - - ctx.beginPath(); - fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray); - ctx.closePath(); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = this._createBaseSVGMarkup(), - widthBy2 = this.width / 2, - heightBy2 = this.height / 2, - points = [ - -widthBy2 + ' ' + heightBy2, - '0 ' + -heightBy2, - widthBy2 + ' ' + heightBy2 - ] - .join(','); - - markup.push( - '' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return 1; - } - }); - - /** - * Returns fabric.Triangle instance from an object representation - * @static - * @memberOf fabric.Triangle - * @param {Object} object Object to create an instance from - * @return {Object} instance of Canvas.Triangle - */ - fabric.Triangle.fromObject = function(object) { - return new fabric.Triangle(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global){ - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - piBy2 = Math.PI * 2, - extend = fabric.util.object.extend; - - if (fabric.Ellipse) { - fabric.warn('fabric.Ellipse is already defined.'); - return; - } - - /** - * Ellipse class - * @class fabric.Ellipse - * @extends fabric.Object - * @return {fabric.Ellipse} thisArg - * @see {@link fabric.Ellipse#initialize} for constructor definition - */ - fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'ellipse', - - /** - * Horizontal radius - * @type Number - * @default - */ - rx: 0, - - /** - * Vertical radius - * @type Number - * @default - */ - ry: 0, - - /** - * Constructor - * @param {Object} [options] Options object - * @return {fabric.Ellipse} thisArg - */ - initialize: function(options) { - options = options || { }; - - this.callSuper('initialize', options); - - this.set('rx', options.rx || 0); - this.set('ry', options.ry || 0); - - this.set('width', this.get('rx') * 2); - this.set('height', this.get('ry') * 2); - }, - - /** - * Returns object representation of an instance - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - rx: this.get('rx'), - ry: this.get('ry') - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = this._createBaseSVGMarkup(), x = 0, y = 0; - if (this.group) { - x = this.left + this.rx; - y = this.top + this.ry; - } - markup.push( - '\n' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * @private - * @param {CanvasRenderingContext2D} ctx context to render on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - _render: function(ctx, noTransform) { - ctx.beginPath(); - ctx.save(); - ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0); - ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false); - ctx.restore(); - this._renderFill(ctx); - this._renderStroke(ctx); - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity - */ - complexity: function() { - return 1; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement}) - * @static - * @memberOf fabric.Ellipse - * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement - */ - fabric.Ellipse.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' ')); - - /** - * Returns {@link fabric.Ellipse} instance from an SVG element - * @static - * @memberOf fabric.Ellipse - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Ellipse} - */ - fabric.Ellipse.fromElement = function(element, options) { - options || (options = { }); - - var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES); - - parsedAttributes.left = parsedAttributes.left || 0; - parsedAttributes.top = parsedAttributes.top || 0; - - var ellipse = new fabric.Ellipse(extend(parsedAttributes, options)); - - ellipse.top -= ellipse.ry; - ellipse.left -= ellipse.rx; - return ellipse; - }; - /* _FROM_SVG_END_ */ - - /** - * Returns {@link fabric.Ellipse} instance from an object representation - * @static - * @memberOf fabric.Ellipse - * @param {Object} object Object to create an instance from - * @return {fabric.Ellipse} - */ - fabric.Ellipse.fromObject = function(object) { - return new fabric.Ellipse(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - if (fabric.Rect) { - console.warn('fabric.Rect is already defined'); - return; - } - - var stateProperties = fabric.Object.prototype.stateProperties.concat(); - stateProperties.push('rx', 'ry', 'x', 'y'); - - /** - * Rectangle class - * @class fabric.Rect - * @extends fabric.Object - * @return {fabric.Rect} thisArg - * @see {@link fabric.Rect#initialize} for constructor definition - */ - fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ { - - /** - * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged}) - * as well as for history (undo/redo) purposes - * @type Array - */ - stateProperties: stateProperties, - - /** - * Type of an object - * @type String - * @default - */ - type: 'rect', - - /** - * Horizontal border radius - * @type Number - * @default - */ - rx: 0, - - /** - * Vertical border radius - * @type Number - * @default - */ - ry: 0, - - /** - * Used to specify dash pattern for stroke on this object - * @type Array - */ - strokeDashArray: null, - - /** - * Constructor - * @param {Object} [options] Options object - * @return {Object} thisArg - */ - initialize: function(options) { - options = options || { }; - - this.callSuper('initialize', options); - this._initRxRy(); - - }, - - /** - * Initializes rx/ry attributes - * @private - */ - _initRxRy: function() { - if (this.rx && !this.ry) { - this.ry = this.rx; - } - else if (this.ry && !this.rx) { - this.rx = this.ry; - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx, noTransform) { - - // optimize 1x1 case (used in spray brush) - if (this.width === 1 && this.height === 1) { - ctx.fillRect(0, 0, 1, 1); - return; - } - - var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0, - ry = this.ry ? Math.min(this.ry, this.height / 2) : 0, - w = this.width, - h = this.height, - x = noTransform ? this.left : -this.width / 2, - y = noTransform ? this.top : -this.height / 2, - isRounded = rx !== 0 || ry !== 0, - k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */; - - ctx.beginPath(); - - ctx.moveTo(x + rx, y); - - ctx.lineTo(x + w - rx, y); - isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry); - - ctx.lineTo(x + w, y + h - ry); - isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h); - - ctx.lineTo(x + rx, y + h); - isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry); - - ctx.lineTo(x, y + ry); - isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y); - - ctx.closePath(); - - this._renderFill(ctx); - this._renderStroke(ctx); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var x = -this.width / 2, - y = -this.height / 2, - w = this.width, - h = this.height; - - ctx.beginPath(); - fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray); - ctx.closePath(); - }, - - /** - * Returns object representation of an instance - * @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) { - var object = extend(this.callSuper('toObject', propertiesToInclude), { - rx: this.get('rx') || 0, - ry: this.get('ry') || 0 - }); - if (!this.includeDefaultValues) { - this._removeDefaultValues(object); - } - return object; - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top; - if (!this.group) { - x = -this.width / 2; - y = -this.height / 2; - } - markup.push( - '\n'); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns complexity of an instance - * @return {Number} complexity - */ - complexity: function() { - return 1; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`) - * @static - * @memberOf fabric.Rect - * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement - */ - fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' ')); - - /** - * Returns {@link fabric.Rect} instance from an SVG element - * @static - * @memberOf fabric.Rect - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Rect} Instance of fabric.Rect - */ - fabric.Rect.fromElement = function(element, options) { - if (!element) { - return null; - } - options = options || { }; - - var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES); - - parsedAttributes.left = parsedAttributes.left || 0; - parsedAttributes.top = parsedAttributes.top || 0; - - return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes)); - }; - /* _FROM_SVG_END_ */ - - /** - * Returns {@link fabric.Rect} instance from an object representation - * @static - * @memberOf fabric.Rect - * @param {Object} object Object to create an instance from - * @return {Object} instance of fabric.Rect - */ - fabric.Rect.fromObject = function(object) { - return new fabric.Rect(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - toFixed = fabric.util.toFixed; - - if (fabric.Polyline) { - fabric.warn('fabric.Polyline is already defined'); - return; - } - - /** - * Polyline class - * @class fabric.Polyline - * @extends fabric.Object - * @see {@link fabric.Polyline#initialize} for constructor definition - */ - fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'polyline', - - /** - * Points array - * @type Array - * @default - */ - points: null, - - /** - * Constructor - * @param {Array} points Array of points (where each point is an object with x and y) - * @param {Object} [options] Options object - * @param {Boolean} [skipOffset] Whether points offsetting should be skipped - * @return {fabric.Polyline} thisArg - * @example - * var poly = new fabric.Polyline([ - * { x: 10, y: 10 }, - * { x: 50, y: 30 }, - * { x: 40, y: 70 }, - * { x: 60, y: 50 }, - * { x: 100, y: 150 }, - * { x: 40, y: 100 } - * ], { - * stroke: 'red', - * left: 100, - * top: 100 - * }); - */ - initialize: function(points, options) { - options = options || { }; - this.set('points', points); - this.callSuper('initialize', options); - this._calcDimensions(); - }, - - /** - * @private - */ - _calcDimensions: function() { - return fabric.Polygon.prototype._calcDimensions.call(this); - }, - - /** - * @private - */ - _applyPointOffset: function() { - return fabric.Polygon.prototype._applyPointOffset.call(this); - }, - - /** - * Returns object representation of an instance - * @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 fabric.Polygon.prototype.toObject.call(this, propertiesToInclude); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var points = [], - markup = this._createBaseSVGMarkup(); - - for (var i = 0, len = this.points.length; i < len; i++) { - points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' '); - } - - markup.push( - '\n' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - var point; - ctx.beginPath(); - - if (this._applyPointOffset) { - if (!(this.group && this.group.type === 'path-group')) { - this._applyPointOffset(); - } - this._applyPointOffset = null; - } - - ctx.moveTo(this.points[0].x, this.points[0].y); - for (var i = 0, len = this.points.length; i < len; i++) { - point = this.points[i]; - ctx.lineTo(point.x, point.y); - } - - this._renderFill(ctx); - this._renderStroke(ctx); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var p1, p2; - - ctx.beginPath(); - for (var i = 0, len = this.points.length; i < len; i++) { - p1 = this.points[i]; - p2 = this.points[i + 1] || p1; - fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray); - } - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return this.get('points').length; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement}) - * @static - * @memberOf fabric.Polyline - * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement - */ - fabric.Polyline.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(); - - /** - * Returns fabric.Polyline instance from an SVG element - * @static - * @memberOf fabric.Polyline - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Polyline} Instance of fabric.Polyline - */ - fabric.Polyline.fromElement = function(element, options) { - if (!element) { - return null; - } - options || (options = { }); - - var points = fabric.parsePointsAttribute(element.getAttribute('points')), - parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES); - - if (points === null) { - return null; - } - - return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options)); - }; - /* _FROM_SVG_END_ */ - - /** - * Returns fabric.Polyline instance from an object representation - * @static - * @memberOf fabric.Polyline - * @param {Object} object Object to create an instance from - * @return {fabric.Polyline} Instance of fabric.Polyline - */ - fabric.Polyline.fromObject = function(object) { - var points = object.points; - return new fabric.Polyline(points, object, true); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - min = fabric.util.array.min, - max = fabric.util.array.max, - toFixed = fabric.util.toFixed; - - if (fabric.Polygon) { - fabric.warn('fabric.Polygon is already defined'); - return; - } - - /** - * Polygon class - * @class fabric.Polygon - * @extends fabric.Object - * @see {@link fabric.Polygon#initialize} for constructor definition - */ - fabric.Polygon = fabric.util.createClass(fabric.Object, /** @lends fabric.Polygon.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'polygon', - - /** - * Points array - * @type Array - * @default - */ - points: null, - - /** - * Constructor - * @param {Array} points Array of points - * @param {Object} [options] Options object - * @return {fabric.Polygon} thisArg - */ - initialize: function(points, options) { - options = options || { }; - this.points = points; - this.callSuper('initialize', options); - this._calcDimensions(); - }, - - /** - * @private - */ - _calcDimensions: function() { - - var points = this.points, - minX = min(points, 'x'), - minY = min(points, 'y'), - maxX = max(points, 'x'), - maxY = max(points, 'y'); - - this.width = (maxX - minX) || 1; - this.height = (maxY - minY) || 1; - - this.left = minX, - this.top = minY; - }, - - /** - * @private - */ - _applyPointOffset: function() { - // change points to offset polygon into a bounding box - // executed one time - this.points.forEach(function(p) { - p.x -= (this.left + this.width / 2); - p.y -= (this.top + this.height / 2); - }, this); - }, - - /** - * Returns object representation of an instance - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - points: this.points.concat() - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var points = [], - markup = this._createBaseSVGMarkup(); - - for (var i = 0, len = this.points.length; i < len; i++) { - points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' '); - } - - markup.push( - '\n' - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - var point; - ctx.beginPath(); - - if (this._applyPointOffset) { - if (!(this.group && this.group.type === 'path-group')) { - this._applyPointOffset(); - } - this._applyPointOffset = null; - } - - ctx.moveTo(this.points[0].x, this.points[0].y); - for (var i = 0, len = this.points.length; i < len; i++) { - point = this.points[i]; - ctx.lineTo(point.x, point.y); - } - this._renderFill(ctx); - if (this.stroke || this.strokeDashArray) { - ctx.closePath(); - this._renderStroke(ctx); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var p1, p2; - - ctx.beginPath(); - for (var i = 0, len = this.points.length; i < len; i++) { - p1 = this.points[i]; - p2 = this.points[i + 1] || this.points[0]; - fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray); - } - ctx.closePath(); - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return this.points.length; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`) - * @static - * @memberOf fabric.Polygon - * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement - */ - fabric.Polygon.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(); - - /** - * Returns {@link fabric.Polygon} instance from an SVG element - * @static - * @memberOf fabric.Polygon - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Polygon} Instance of fabric.Polygon - */ - fabric.Polygon.fromElement = function(element, options) { - if (!element) { - return null; - } - - options || (options = { }); - - var points = fabric.parsePointsAttribute(element.getAttribute('points')), - parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES); - - if (points === null) { - return null; - } - - return new fabric.Polygon(points, extend(parsedAttributes, options)); - }; - /* _FROM_SVG_END_ */ - - /** - * Returns fabric.Polygon instance from an object representation - * @static - * @memberOf fabric.Polygon - * @param {Object} object Object to create an instance from - * @return {fabric.Polygon} Instance of fabric.Polygon - */ - fabric.Polygon.fromObject = function(object) { - return new fabric.Polygon(object.points, object, true); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - min = fabric.util.array.min, - max = fabric.util.array.max, - extend = fabric.util.object.extend, - _toString = Object.prototype.toString, - drawArc = fabric.util.drawArc, - commandLengths = { - m: 2, - l: 2, - h: 1, - v: 1, - c: 6, - s: 4, - q: 4, - t: 2, - a: 7 - }, - repeatedCommands = { - m: 'l', - M: 'L' - }; - - if (fabric.Path) { - fabric.warn('fabric.Path is already defined'); - return; - } - - /** - * @private - */ - function getX(item) { - if (item[0] === 'H') { - return item[1]; - } - return item[item.length - 2]; - } - - /** - * @private - */ - function getY(item) { - if (item[0] === 'V') { - return item[1]; - } - return item[item.length - 1]; - } - - /** - * Path class - * @class fabric.Path - * @extends fabric.Object - * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup} - * @see {@link fabric.Path#initialize} for constructor definition - */ - fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'path', - - /** - * Array of path points - * @type Array - * @default - */ - path: null, - - /** - * Constructor - * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens) - * @param {Object} [options] Options object - * @return {fabric.Path} thisArg - */ - initialize: function(path, options) { - options = options || { }; - - this.setOptions(options); - - if (!path) { - throw new Error('`path` argument is required'); - } - - var fromArray = _toString.call(path) === '[object Array]'; - - this.path = fromArray - ? path - // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values) - : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi); - - if (!this.path) { - return; - } - - if (!fromArray) { - this.path = this._parsePath(); - } - this._initializePath(options); - - if (options.sourcePath) { - this.setSourcePath(options.sourcePath); - } - }, - - /** - * @private - * @param {Object} [options] Options object - */ - _initializePath: function (options) { - var isWidthSet = 'width' in options && options.width != null, - isHeightSet = 'height' in options && options.width != null, - isLeftSet = 'left' in options, - isTopSet = 'top' in options, - origLeft = isLeftSet ? this.left : 0, - origTop = isTopSet ? this.top : 0; - - if (!isWidthSet || !isHeightSet) { - extend(this, this._parseDimensions()); - if (isWidthSet) { - this.width = options.width; - } - if (isHeightSet) { - this.height = options.height; - } - } - else { //Set center location relative to given height/width if not specified - if (!isTopSet) { - this.top = this.height / 2; - } - if (!isLeftSet) { - this.left = this.width / 2; - } - } - this.pathOffset = this.pathOffset || - // Save top-left coords as offset - this._calculatePathOffset(origLeft, origTop); - }, - - /** - * @private - * @param {Number} origLeft Original left position - * @param {Number} origTop Original top position - */ - _calculatePathOffset: function (origLeft, origTop) { - return { - x: this.left - origLeft - (this.width / 2), - y: this.top - origTop - (this.height / 2) - }; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx context to render path on - */ - _render: function(ctx, noTransform) { - var current, // current instruction - previous = null, - subpathStartX = 0, - subpathStartY = 0, - x = 0, // current x - y = 0, // current y - controlX = 0, // current control point x - controlY = 0, // current control point y - tempX, - tempY, - tempControlX, - tempControlY, - l = -((this.width / 2) + this.pathOffset.x), - t = -((this.height / 2) + this.pathOffset.y); - - if (noTransform) { - l += this.width / 2; - t += this.height / 2; - } - - for (var i = 0, len = this.path.length; i < len; ++i) { - - current = this.path[i]; - - switch (current[0]) { // first letter - - case 'l': // lineto, relative - x += current[1]; - y += current[2]; - ctx.lineTo(x + l, y + t); - break; - - case 'L': // lineto, absolute - x = current[1]; - y = current[2]; - ctx.lineTo(x + l, y + t); - break; - - case 'h': // horizontal lineto, relative - x += current[1]; - ctx.lineTo(x + l, y + t); - break; - - case 'H': // horizontal lineto, absolute - x = current[1]; - ctx.lineTo(x + l, y + t); - break; - - case 'v': // vertical lineto, relative - y += current[1]; - ctx.lineTo(x + l, y + t); - break; - - case 'V': // verical lineto, absolute - y = current[1]; - ctx.lineTo(x + l, y + t); - break; - - case 'm': // moveTo, relative - x += current[1]; - y += current[2]; - subpathStartX = x; - subpathStartY = y; - ctx.moveTo(x + l, y + t); - break; - - case 'M': // moveTo, absolute - x = current[1]; - y = current[2]; - subpathStartX = x; - subpathStartY = y; - ctx.moveTo(x + l, y + t); - break; - - case 'c': // bezierCurveTo, relative - tempX = x + current[5]; - tempY = y + current[6]; - controlX = x + current[3]; - controlY = y + current[4]; - ctx.bezierCurveTo( - x + current[1] + l, // x1 - y + current[2] + t, // y1 - controlX + l, // x2 - controlY + t, // y2 - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - break; - - case 'C': // bezierCurveTo, absolute - x = current[5]; - y = current[6]; - controlX = current[3]; - controlY = current[4]; - ctx.bezierCurveTo( - current[1] + l, - current[2] + t, - controlX + l, - controlY + t, - x + l, - y + t - ); - break; - - case 's': // shorthand cubic bezierCurveTo, relative - - // transform to absolute x,y - tempX = x + current[3]; - tempY = y + current[4]; - - // calculate reflection of previous control points - controlX = controlX ? (2 * x - controlX) : x; - controlY = controlY ? (2 * y - controlY) : y; - - ctx.bezierCurveTo( - controlX + l, - controlY + t, - x + current[1] + l, - y + current[2] + t, - tempX + l, - tempY + t - ); - // set control point to 2nd one of this command - // "... the first control point is assumed to be - // the reflection of the second control point on - // the previous command relative to the current point." - controlX = x + current[1]; - controlY = y + current[2]; - - x = tempX; - y = tempY; - break; - - case 'S': // shorthand cubic bezierCurveTo, absolute - tempX = current[3]; - tempY = current[4]; - // calculate reflection of previous control points - controlX = 2 * x - controlX; - controlY = 2 * y - controlY; - ctx.bezierCurveTo( - controlX + l, - controlY + t, - current[1] + l, - current[2] + t, - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - - // set control point to 2nd one of this command - // "... the first control point is assumed to be - // the reflection of the second control point on - // the previous command relative to the current point." - controlX = current[1]; - controlY = current[2]; - - break; - - case 'q': // quadraticCurveTo, relative - // transform to absolute x,y - tempX = x + current[3]; - tempY = y + current[4]; - - controlX = x + current[1]; - controlY = y + current[2]; - - ctx.quadraticCurveTo( - controlX + l, - controlY + t, - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - break; - - case 'Q': // quadraticCurveTo, absolute - tempX = current[3]; - tempY = current[4]; - - ctx.quadraticCurveTo( - current[1] + l, - current[2] + t, - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - controlX = current[1]; - controlY = current[2]; - break; - - case 't': // shorthand quadraticCurveTo, relative - - // transform to absolute x,y - tempX = x + current[1]; - tempY = y + current[2]; - - if (previous[0].match(/[QqTt]/) === null) { - // If there is no previous command or if the previous command was not a Q, q, T or t, - // assume the control point is coincident with the current point - controlX = x; - controlY = y; - } - else if (previous[0] === 't') { - // calculate reflection of previous control points for t - controlX = 2 * x - tempControlX; - controlY = 2 * y - tempControlY; - } - else if (previous[0] === 'q') { - // calculate reflection of previous control points for q - controlX = 2 * x - controlX; - controlY = 2 * y - controlY; - } - - tempControlX = controlX; - tempControlY = controlY; - - ctx.quadraticCurveTo( - controlX + l, - controlY + t, - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - controlX = x + current[1]; - controlY = y + current[2]; - break; - - case 'T': - tempX = current[1]; - tempY = current[2]; - - // calculate reflection of previous control points - controlX = 2 * x - controlX; - controlY = 2 * y - controlY; - ctx.quadraticCurveTo( - controlX + l, - controlY + t, - tempX + l, - tempY + t - ); - x = tempX; - y = tempY; - break; - - case 'a': - // TODO: optimize this - drawArc(ctx, x + l, y + t, [ - current[1], - current[2], - current[3], - current[4], - current[5], - current[6] + x + l, - current[7] + y + t - ]); - x += current[6]; - y += current[7]; - break; - - case 'A': - // TODO: optimize this - drawArc(ctx, x + l, y + t, [ - current[1], - current[2], - current[3], - current[4], - current[5], - current[6] + l, - current[7] + t - ]); - x = current[6]; - y = current[7]; - break; - - case 'z': - case 'Z': - x = subpathStartX; - y = subpathStartY; - ctx.closePath(); - break; - } - previous = current; - } - }, - - /** - * Renders path on a specified context - * @param {CanvasRenderingContext2D} ctx context to render path on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - render: function(ctx, noTransform) { - // do not render if object is not visible - if (!this.visible) { - return; - } - - ctx.save(); - if (noTransform) { - ctx.translate(-this.width/2, -this.height/2); - } - var m = this.transformMatrix; - - if (m) { - ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]); - } - if (!noTransform) { - this.transform(ctx); - } - this._setStrokeStyles(ctx); - this._setFillStyles(ctx); - this._setShadow(ctx); - this.clipTo && fabric.util.clipContext(this, ctx); - ctx.beginPath(); - ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity; - this._render(ctx, noTransform); - this._renderFill(ctx); - this._renderStroke(ctx); - this.clipTo && ctx.restore(); - this._removeShadow(ctx); - ctx.restore(); - }, - - /** - * Returns string representation of an instance - * @return {String} string representation of an instance - */ - toString: function() { - return '#'; - }, - - /** - * Returns object representation of an instance - * @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) { - var o = extend(this.callSuper('toObject', propertiesToInclude), { - path: this.path.map(function(item) { return item.slice() }), - pathOffset: this.pathOffset - }); - if (this.sourcePath) { - o.sourcePath = this.sourcePath; - } - if (this.transformMatrix) { - o.transformMatrix = this.transformMatrix; - } - return o; - }, - - /** - * Returns dataless object representation of an instance - * @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) { - var o = this.toObject(propertiesToInclude); - if (this.sourcePath) { - o.path = this.sourcePath; - } - delete o.sourcePath; - return o; - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var chunks = [], - markup = this._createBaseSVGMarkup(); - - for (var i = 0, len = this.path.length; i < len; i++) { - chunks.push(this.path[i].join(' ')); - } - var path = chunks.join(' '); - - markup.push( - //jscs:disable validateIndentation - '\n' - //jscs:enable validateIndentation - ); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns number representation of an instance complexity - * @return {Number} complexity of this instance - */ - complexity: function() { - return this.path.length; - }, - - /** - * @private - */ - _parsePath: function() { - var result = [ ], - coords = [ ], - currentPath, - parsed, - re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig, - match, - coordsStr; - - for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) { - currentPath = this.path[i]; - - coordsStr = currentPath.slice(1).trim(); - coords.length = 0; - - while ((match = re.exec(coordsStr))) { - coords.push(match[0]); - } - - coordsParsed = [ currentPath.charAt(0) ]; - - for (var j = 0, jlen = coords.length; j < jlen; j++) { - parsed = parseFloat(coords[j]); - if (!isNaN(parsed)) { - coordsParsed.push(parsed); - } - } - - var command = coordsParsed[0], - commandLength = commandLengths[command.toLowerCase()], - repeatedCommand = repeatedCommands[command] || command; - - if (coordsParsed.length - 1 > commandLength) { - for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) { - result.push([ command ].concat(coordsParsed.slice(k, k + commandLength))); - command = repeatedCommand; - } - } - else { - result.push(coordsParsed); - } - } - - return result; - }, - - /** - * @private - */ - _parseDimensions: function() { - var aX = [], - aY = [], - previous = { }; - - this.path.forEach(function(item, i) { - this._getCoordsFromCommand(item, i, aX, aY, previous); - }, this); - - var minX = min(aX), - minY = min(aY), - maxX = max(aX), - maxY = max(aY), - deltaX = maxX - minX, - deltaY = maxY - minY, - - o = { - left: this.left + (minX + deltaX / 2), - top: this.top + (minY + deltaY / 2), - width: deltaX, - height: deltaY - }; - - return o; - }, - - _getCoordsFromCommand: function(item, i, aX, aY, previous) { - var isLowerCase = false; - - if (item[0] !== 'H') { - previous.x = (i === 0) ? getX(item) : getX(this.path[i - 1]); - } - if (item[0] !== 'V') { - previous.y = (i === 0) ? getY(item) : getY(this.path[i - 1]); - } - - // lowercased letter denotes relative position; - // transform to absolute - if (item[0] === item[0].toLowerCase()) { - isLowerCase = true; - } - - var xy = this._getXY(item, isLowerCase, previous), - val; - - val = parseInt(xy.x, 10); - if (!isNaN(val)) { - aX.push(val); - } - - val = parseInt(xy.y, 10); - if (!isNaN(val)) { - aY.push(val); - } - }, - - _getXY: function(item, isLowerCase, previous) { - - // last 2 items in an array of coordinates are the actualy x/y (except H/V), collect them - // TODO (kangax): support relative h/v commands - - var x = isLowerCase - ? previous.x + getX(item) - : item[0] === 'V' - ? previous.x - : getX(item), - - y = isLowerCase - ? previous.y + getY(item) - : item[0] === 'H' - ? previous.y - : getY(item); - - return { x: x, y: y }; - } - }); - - /** - * Creates an instance of fabric.Path from an object - * @static - * @memberOf fabric.Path - * @param {Object} object - * @param {Function} callback Callback to invoke when an fabric.Path instance is created - */ - fabric.Path.fromObject = function(object, callback) { - if (typeof object.path === 'string') { - fabric.loadSVGFromURL(object.path, function (elements) { - var path = elements[0], - pathUrl = object.path; - - delete object.path; - - fabric.util.object.extend(path, object); - path.setSourcePath(pathUrl); - - callback(path); - }); - } - else { - callback(new fabric.Path(object.path, object)); - } - }; - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`) - * @static - * @memberOf fabric.Path - * @see http://www.w3.org/TR/SVG/paths.html#PathElement - */ - fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']); - - /** - * Creates an instance of fabric.Path from an SVG element - * @static - * @memberOf fabric.Path - * @param {SVGElement} element to parse - * @param {Function} callback Callback to invoke when an fabric.Path instance is created - * @param {Object} [options] Options object - */ - fabric.Path.fromElement = function(element, callback, options) { - var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES); - callback && callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options))); - }; - /* _FROM_SVG_END_ */ - - /** - * Indicates that instances of this type are async - * @static - * @memberOf fabric.Path - * @type Boolean - * @default - */ - fabric.Path.async = true; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - invoke = fabric.util.array.invoke, - parentToObject = fabric.Object.prototype.toObject; - - if (fabric.PathGroup) { - fabric.warn('fabric.PathGroup is already defined'); - return; - } - - /** - * Path group class - * @class fabric.PathGroup - * @extends fabric.Path - * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup} - * @see {@link fabric.PathGroup#initialize} for constructor definition - */ - fabric.PathGroup = fabric.util.createClass(fabric.Path, /** @lends fabric.PathGroup.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'path-group', - - /** - * Fill value - * @type String - * @default - */ - fill: '', - - /** - * Constructor - * @param {Array} paths - * @param {Object} [options] Options object - * @return {fabric.PathGroup} thisArg - */ - initialize: function(paths, options) { - - options = options || { }; - this.paths = paths || [ ]; - - for (var i = this.paths.length; i--; ) { - this.paths[i].group = this; - } - - this.setOptions(options); - - if (options.widthAttr) { - this.scaleX = options.widthAttr / options.width; - } - if (options.heightAttr) { - this.scaleY = options.heightAttr / options.height; - } - - this.setCoords(); - - if (options.sourcePath) { - this.setSourcePath(options.sourcePath); - } - }, - - /** - * Renders this group on a specified context - * @param {CanvasRenderingContext2D} ctx Context to render this instance on - */ - render: function(ctx) { - // do not render if object is not visible - if (!this.visible) { - return; - } - - ctx.save(); - - var m = this.transformMatrix; - - if (m) { - ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]); - } - this.transform(ctx); - - this._setShadow(ctx); - this.clipTo && fabric.util.clipContext(this, ctx); - for (var i = 0, l = this.paths.length; i < l; ++i) { - this.paths[i].render(ctx, true); - } - this.clipTo && ctx.restore(); - this._removeShadow(ctx); - ctx.restore(); - }, - - /** - * Sets certain property to a certain value - * @param {String} prop - * @param {Any} value - * @return {fabric.PathGroup} thisArg - */ - _set: function(prop, value) { - - if (prop === 'fill' && value && this.isSameColor()) { - var i = this.paths.length; - while (i--) { - this.paths[i]._set(prop, value); - } - } - - return this.callSuper('_set', prop, value); - }, - - /** - * Returns object representation of this path group - * @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) { - var o = extend(parentToObject.call(this, propertiesToInclude), { - paths: invoke(this.getObjects(), 'toObject', propertiesToInclude) - }); - if (this.sourcePath) { - o.sourcePath = this.sourcePath; - } - return o; - }, - - /** - * Returns dataless object representation of this path group - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} dataless object representation of an instance - */ - toDatalessObject: function(propertiesToInclude) { - var o = this.toObject(propertiesToInclude); - if (this.sourcePath) { - o.paths = this.sourcePath; - } - return o; - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var objects = this.getObjects(), - translatePart = 'translate(' + this.left + ' ' + this.top + ')', - markup = [ - //jscs:disable validateIndentation - '\n' - //jscs:enable validateIndentation - ]; - - for (var i = 0, len = objects.length; i < len; i++) { - markup.push(objects[i].toSVG(reviver)); - } - markup.push('\n'); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns a string representation of this path group - * @return {String} string representation of an object - */ - toString: function() { - return '#'; - }, - - /** - * Returns true if all paths in this group are of same color - * @return {Boolean} true if all paths are of the same color (`fill`) - */ - isSameColor: function() { - var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase(); - return this.getObjects().every(function(path) { - return (path.get('fill') || '').toLowerCase() === firstPathFill; - }); - }, - - /** - * Returns number representation of object's complexity - * @return {Number} complexity - */ - complexity: function() { - return this.paths.reduce(function(total, path) { - return total + ((path && path.complexity) ? path.complexity() : 0); - }, 0); - }, - - /** - * Returns all paths in this path group - * @return {Array} array of path objects included in this path group - */ - getObjects: function() { - return this.paths; - } - }); - - /** - * Creates fabric.PathGroup instance from an object representation - * @static - * @memberOf fabric.PathGroup - * @param {Object} object Object to create an instance from - * @param {Function} callback Callback to invoke when an fabric.PathGroup instance is created - */ - fabric.PathGroup.fromObject = function(object, callback) { - if (typeof object.paths === 'string') { - fabric.loadSVGFromURL(object.paths, function (elements) { - - var pathUrl = object.paths; - delete object.paths; - - var pathGroup = fabric.util.groupSVGElements(elements, object, pathUrl); - - callback(pathGroup); - }); - } - else { - fabric.util.enlivenObjects(object.paths, function(enlivenedObjects) { - delete object.paths; - callback(new fabric.PathGroup(enlivenedObjects, object)); - }); - } - }; - - /** - * Indicates that instances of this type are async - * @static - * @memberOf fabric.PathGroup - * @type Boolean - * @default - */ - fabric.PathGroup.async = true; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global){ - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - min = fabric.util.array.min, - max = fabric.util.array.max, - invoke = fabric.util.array.invoke; - - if (fabric.Group) { - return; - } - - // lock-related properties, for use in fabric.Group#get - // to enable locking behavior on group - // when one of its objects has lock-related properties set - var _lockProperties = { - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - lockUniScaling: true - }; - - /** - * Group class - * @class fabric.Group - * @extends fabric.Object - * @mixes fabric.Collection - * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#groups} - * @see {@link fabric.Group#initialize} for constructor definition - */ - fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'group', - - /** - * Constructor - * @param {Object} objects Group objects - * @param {Object} [options] Options object - * @return {Object} thisArg - */ - initialize: function(objects, options) { - options = options || { }; - - this._objects = objects || []; - for (var i = this._objects.length; i--; ) { - this._objects[i].group = this; - } - - this.originalState = { }; - this.callSuper('initialize'); - - this._calcBounds(); - this._updateObjectsCoords(); - - if (options) { - extend(this, options); - } - this._setOpacityIfSame(); - - this.setCoords(); - this.saveCoords(); - }, - - /** - * @private - */ - _updateObjectsCoords: function() { - this.forEachObject(this._updateObjectCoords, this); - }, - - /** - * @private - */ - _updateObjectCoords: function(object) { - var objectLeft = object.getLeft(), - objectTop = object.getTop(); - - object.set({ - originalLeft: objectLeft, - originalTop: objectTop, - left: objectLeft - this.left, - top: objectTop - this.top - }); - - object.setCoords(); - - // do not display corners of objects enclosed in a group - object.__origHasControls = object.hasControls; - object.hasControls = false; - }, - - /** - * Returns string represenation of a group - * @return {String} - */ - toString: function() { - return '#'; - }, - - /** - * Adds an object to a group; Then recalculates group's dimension, position. - * @param {Object} object - * @return {fabric.Group} thisArg - * @chainable - */ - addWithUpdate: function(object) { - this._restoreObjectsState(); - if (object) { - this._objects.push(object); - object.group = this; - } - // since _restoreObjectsState set objects inactive - this.forEachObject(this._setObjectActive, this); - this._calcBounds(); - this._updateObjectsCoords(); - return this; - }, - - /** - * @private - */ - _setObjectActive: function(object) { - object.set('active', true); - object.group = this; - }, - - /** - * Removes an object from a group; Then recalculates group's dimension, position. - * @param {Object} object - * @return {fabric.Group} thisArg - * @chainable - */ - removeWithUpdate: function(object) { - this._moveFlippedObject(object); - this._restoreObjectsState(); - - // since _restoreObjectsState set objects inactive - this.forEachObject(this._setObjectActive, this); - - this.remove(object); - this._calcBounds(); - this._updateObjectsCoords(); - - return this; - }, - - /** - * @private - */ - _onObjectAdded: function(object) { - object.group = this; - }, - - /** - * @private - */ - _onObjectRemoved: function(object) { - delete object.group; - object.set('active', false); - }, - - /** - * Properties that are delegated to group objects when reading/writing - * @param {Object} delegatedProperties - */ - delegatedProperties: { - fill: true, - opacity: true, - fontFamily: true, - fontWeight: true, - fontSize: true, - fontStyle: true, - lineHeight: true, - textDecoration: true, - textAlign: true, - backgroundColor: true - }, - - /** - * @private - */ - _set: function(key, value) { - if (key in this.delegatedProperties) { - var i = this._objects.length; - this[key] = value; - while (i--) { - this._objects[i].set(key, value); - } - } - else { - this[key] = value; - } - }, - - /** - * Returns object representation of an instance - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - objects: invoke(this._objects, 'toObject', propertiesToInclude) - }); - }, - - /** - * Renders instance on a given context - * @param {CanvasRenderingContext2D} ctx context to render instance on - */ - render: function(ctx) { - // do not render if object is not visible - if (!this.visible) { - return; - } - - ctx.save(); - this.clipTo && fabric.util.clipContext(this, ctx); - - // the array is now sorted in order of highest first, so start from end - for (var i = 0, len = this._objects.length; i < len; i++) { - this._renderObject(this._objects[i], ctx); - } - - this.clipTo && ctx.restore(); - - ctx.restore(); - }, - - /** - * Renders controls and borders for the object - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Boolean} [noTransform] When true, context is not transformed - */ - _renderControls: function(ctx, noTransform) { - this.callSuper('_renderControls', ctx, noTransform); - for (var i = 0, len = this._objects.length; i < len; i++) { - this._objects[i]._renderControls(ctx); - } - }, - - /** - * @private - */ - _renderObject: function(object, ctx) { - var originalHasRotatingPoint = object.hasRotatingPoint; - - // do not render if object is not visible - if (!object.visible) { - return; - } - - object.hasRotatingPoint = false; - - object.render(ctx); - - object.hasRotatingPoint = originalHasRotatingPoint; - }, - - /** - * Retores original state of each of group objects (original state is that which was before group was created). - * @private - * @return {fabric.Group} thisArg - * @chainable - */ - _restoreObjectsState: function() { - this._objects.forEach(this._restoreObjectState, this); - return this; - }, - - /** - * Moves a flipped object to the position where it's displayed - * @private - * @param {fabric.Object} object - * @return {fabric.Group} thisArg - */ - _moveFlippedObject: function(object) { - var oldOriginX = object.get('originX'), - oldOriginY = object.get('originY'), - center = object.getCenterPoint(); - - object.set({ - originX: 'center', - originY: 'center', - left: center.x, - top: center.y - }); - - this._toggleFlipping(object); - - var newOrigin = object.getPointByOrigin(oldOriginX, oldOriginY); - - object.set({ - originX: oldOriginX, - originY: oldOriginY, - left: newOrigin.x, - top: newOrigin.y - }); - - return this; - }, - - /** - * @private - */ - _toggleFlipping: function(object) { - if (this.flipX) { - object.toggle('flipX'); - object.set('left', -object.get('left')); - object.setAngle(-object.getAngle()); - } - if (this.flipY) { - object.toggle('flipY'); - object.set('top', -object.get('top')); - object.setAngle(-object.getAngle()); - } - }, - - /** - * Restores original state of a specified object in group - * @private - * @param {fabric.Object} object - * @return {fabric.Group} thisArg - */ - _restoreObjectState: function(object) { - this._setObjectPosition(object); - - object.setCoords(); - object.hasControls = object.__origHasControls; - delete object.__origHasControls; - object.set('active', false); - object.setCoords(); - delete object.group; - - return this; - }, - - /** - * @private - */ - _setObjectPosition: function(object) { - var groupLeft = this.getLeft(), - groupTop = this.getTop(), - rotated = this._getRotatedLeftTop(object); - - object.set({ - angle: object.getAngle() + this.getAngle(), - left: groupLeft + rotated.left, - top: groupTop + rotated.top, - scaleX: object.get('scaleX') * this.get('scaleX'), - scaleY: object.get('scaleY') * this.get('scaleY') - }); - }, - - /** - * @private - */ - _getRotatedLeftTop: function(object) { - var groupAngle = this.getAngle() * (Math.PI / 180); - return { - left: (-Math.sin(groupAngle) * object.getTop() * this.get('scaleY') + - Math.cos(groupAngle) * object.getLeft() * this.get('scaleX')), - - top: (Math.cos(groupAngle) * object.getTop() * this.get('scaleY') + - Math.sin(groupAngle) * object.getLeft() * this.get('scaleX')) - }; - }, - - /** - * Destroys a group (restoring state of its objects) - * @return {fabric.Group} thisArg - * @chainable - */ - destroy: function() { - this._objects.forEach(this._moveFlippedObject, this); - return this._restoreObjectsState(); - }, - - /** - * Saves coordinates of this instance (to be used together with `hasMoved`) - * @saveCoords - * @return {fabric.Group} thisArg - * @chainable - */ - saveCoords: function() { - this._originalLeft = this.get('left'); - this._originalTop = this.get('top'); - return this; - }, - - /** - * Checks whether this group was moved (since `saveCoords` was called last) - * @return {Boolean} true if an object was moved (since fabric.Group#saveCoords was called) - */ - hasMoved: function() { - return this._originalLeft !== this.get('left') || - this._originalTop !== this.get('top'); - }, - - /** - * Sets coordinates of all group objects - * @return {fabric.Group} thisArg - * @chainable - */ - setObjectsCoords: function() { - this.forEachObject(function(object) { - object.setCoords(); - }); - return this; - }, - - /** - * @private - */ - _setOpacityIfSame: function() { - var objects = this.getObjects(), - firstValue = objects[0] ? objects[0].get('opacity') : 1, - isSameOpacity = objects.every(function(o) { - return o.get('opacity') === firstValue; - }); - - if (isSameOpacity) { - this.opacity = firstValue; - } - }, - - /** - * @private - */ - _calcBounds: function(onlyWidthHeight) { - var aX = [], - aY = [], - o; - - for (var i = 0, len = this._objects.length; i < len; ++i) { - o = this._objects[i]; - o.setCoords(); - for (var prop in o.oCoords) { - aX.push(o.oCoords[prop].x); - aY.push(o.oCoords[prop].y); - } - } - - this.set(this._getBounds(aX, aY, onlyWidthHeight)); - }, - - /** - * @private - */ - _getBounds: function(aX, aY, onlyWidthHeight) { - var ivt = fabric.util.invertTransform(this.getViewportTransform()), - minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt), - maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt), - obj = { - width: (maxXY.x - minXY.x) || 0, - height: (maxXY.y - minXY.y) || 0 - }; - - if (!onlyWidthHeight) { - obj.left = (minXY.x + maxXY.x) / 2 || 0; - obj.top = (minXY.y + maxXY.y) / 2 || 0; - } - return obj; - }, - - /* _TO_SVG_START_ */ - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = [ - //jscs:disable validateIndentation - '\n' - //jscs:enable validateIndentation - ]; - - for (var i = 0, len = this._objects.length; i < len; i++) { - markup.push(this._objects[i].toSVG(reviver)); - } - - markup.push('\n'); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns requested property - * @param {String} prop Property to get - * @return {Any} - */ - get: function(prop) { - if (prop in _lockProperties) { - if (this[prop]) { - return this[prop]; - } - else { - for (var i = 0, len = this._objects.length; i < len; i++) { - if (this._objects[i][prop]) { - return true; - } - } - return false; - } - } - else { - if (prop in this.delegatedProperties) { - return this._objects[0] && this._objects[0].get(prop); - } - return this[prop]; - } - } - }); - - /** - * Returns {@link fabric.Group} instance from an object representation - * @static - * @memberOf fabric.Group - * @param {Object} object Object to create a group from - * @param {Function} [callback] Callback to invoke when an group instance is created - * @return {fabric.Group} An instance of fabric.Group - */ - fabric.Group.fromObject = function(object, callback) { - fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) { - delete object.objects; - callback && callback(new fabric.Group(enlivenedObjects, object)); - }); - }; - - /** - * Indicates that instances of this type are async - * @static - * @memberOf fabric.Group - * @type Boolean - * @default - */ - fabric.Group.async = true; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var extend = fabric.util.object.extend; - - if (!global.fabric) { - global.fabric = { }; - } - - if (global.fabric.Image) { - fabric.warn('fabric.Image is already defined.'); - return; - } - - /** - * Image class - * @class fabric.Image - * @extends fabric.Object - * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#images} - * @see {@link fabric.Image#initialize} for constructor definition - */ - fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ { - - /** - * Type of an object - * @type String - * @default - */ - type: 'image', - - /** - * crossOrigin value (one of "", "anonymous", "allow-credentials") - * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes - * @type String - * @default - */ - crossOrigin: '', - - /** - * Constructor - * @param {HTMLImageElement | String} element Image element - * @param {Object} [options] Options object - * @return {fabric.Image} thisArg - */ - initialize: function(element, options) { - options || (options = { }); - - this.filters = [ ]; - - this.callSuper('initialize', options); - - this._initElement(element, options); - this._initConfig(options); - - if (options.filters) { - this.filters = options.filters; - this.applyFilters(); - } - }, - - /** - * Returns image element which this instance if based on - * @return {HTMLImageElement} Image element - */ - getElement: function() { - return this._element; - }, - - /** - * Sets image element for this instance to a specified one. - * If filters defined they are applied to new image. - * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area. - * @param {HTMLImageElement} element - * @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated - * @return {fabric.Image} thisArg - * @chainable - */ - setElement: function(element, callback) { - this._element = element; - this._originalElement = element; - this._initConfig(); - - if (this.filters.length !== 0) { - this.applyFilters(callback); - } - - return this; - }, - - /** - * Sets crossOrigin value (on an instance and corresponding image element) - * @return {fabric.Image} thisArg - * @chainable - */ - setCrossOrigin: function(value) { - this.crossOrigin = value; - this._element.crossOrigin = value; - - return this; - }, - - /** - * Returns original size of an image - * @return {Object} Object with "width" and "height" properties - */ - getOriginalSize: function() { - var element = this.getElement(); - return { - width: element.width, - height: element.height - }; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _stroke: function(ctx) { - ctx.save(); - this._setStrokeStyles(ctx); - ctx.beginPath(); - ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height); - ctx.closePath(); - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderDashedStroke: function(ctx) { - var x = -this.width / 2, - y = -this.height / 2, - w = this.width, - h = this.height; - - ctx.save(); - this._setStrokeStyles(ctx); - - ctx.beginPath(); - fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray); - fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray); - ctx.closePath(); - ctx.restore(); - }, - - /** - * Returns object representation of an instance - * @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 extend(this.callSuper('toObject', propertiesToInclude), { - src: this._originalElement.src || this._originalElement._src, - filters: this.filters.map(function(filterObj) { - return filterObj && filterObj.toObject(); - }), - crossOrigin: this.crossOrigin - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = [], x = -this.width / 2, y = -this.height / 2; - if (this.group) { - x = this.left; - y = this.top; - } - markup.push( - '\n', - '\n' - ); - - if (this.stroke || this.strokeDashArray) { - var origFill = this.fill; - this.fill = null; - markup.push( - '\n' - ); - this.fill = origFill; - } - - markup.push('\n'); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - /* _TO_SVG_END_ */ - - /** - * Returns source of an image - * @return {String} Source of an image - */ - getSrc: function() { - if (this.getElement()) { - return this.getElement().src || this.getElement()._src; - } - }, - - /** - * Returns string representation of an instance - * @return {String} String representation of an instance - */ - toString: function() { - return '#'; - }, - - /** - * Returns a clone of an instance - * @param {Function} callback Callback is invoked with a clone as a first argument - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - */ - clone: function(callback, propertiesToInclude) { - this.constructor.fromObject(this.toObject(propertiesToInclude), callback); - }, - - /** - * Applies filters assigned to this image (from "filters" array) - * @mthod applyFilters - * @param {Function} callback Callback is invoked when all filters have been applied and new image is generated - * @return {fabric.Image} thisArg - * @chainable - */ - applyFilters: function(callback) { - - if (!this._originalElement) { - return; - } - - if (this.filters.length === 0) { - this._element = this._originalElement; - callback && callback(); - return; - } - - var imgEl = this._originalElement, - canvasEl = fabric.util.createCanvasElement(), - replacement = fabric.util.createImage(), - _this = this; - - canvasEl.width = imgEl.width; - canvasEl.height = imgEl.height; - - canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height); - - this.filters.forEach(function(filter) { - filter && filter.applyTo(canvasEl); - }); - - /** @ignore */ - - replacement.width = imgEl.width; - replacement.height = imgEl.height; - - if (fabric.isLikelyNode) { - replacement.src = canvasEl.toBuffer(undefined, fabric.Image.pngCompression); - - // onload doesn't fire in some node versions, so we invoke callback manually - _this._element = replacement; - callback && callback(); - } - else { - replacement.onload = function() { - _this._element = replacement; - callback && callback(); - replacement.onload = canvasEl = imgEl = null; - }; - replacement.src = canvasEl.toDataURL('image/png'); - } - - return this; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx, noTransform) { - this._element && - ctx.drawImage( - this._element, - noTransform ? this.left : -this.width/2, - noTransform ? this.top : -this.height/2, - this.width, - this.height - ); - this._renderStroke(ctx); - }, - - /** - * @private - */ - _resetWidthHeight: function() { - var element = this.getElement(); - - this.set('width', element.width); - this.set('height', element.height); - }, - - /** - * The Image class's initialization method. This method is automatically - * called by the constructor. - * @private - * @param {HTMLImageElement|String} element The element representing the image - */ - _initElement: function(element) { - this.setElement(fabric.util.getById(element)); - fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS); - }, - - /** - * @private - * @param {Object} [options] Options object - */ - _initConfig: function(options) { - options || (options = { }); - this.setOptions(options); - this._setWidthHeight(options); - if (this._element && this.crossOrigin) { - this._element.crossOrigin = this.crossOrigin; - } - }, - - /** - * @private - * @param {Object} object Object with filters property - * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created - */ - _initFilters: function(object, callback) { - if (object.filters && object.filters.length) { - fabric.util.enlivenObjects(object.filters, function(enlivenedObjects) { - callback && callback(enlivenedObjects); - }, 'fabric.Image.filters'); - } - else { - callback && callback(); - } - }, - - /** - * @private - * @param {Object} [options] Object with width/height properties - */ - _setWidthHeight: function(options) { - this.width = 'width' in options - ? options.width - : (this.getElement() - ? this.getElement().width || 0 - : 0); - - this.height = 'height' in options - ? options.height - : (this.getElement() - ? this.getElement().height || 0 - : 0); - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity of this instance - */ - complexity: function() { - return 1; - } - }); - - /** - * Default CSS class name for canvas - * @static - * @type String - * @default - */ - fabric.Image.CSS_CANVAS = 'canvas-img'; - - /** - * Alias for getSrc - * @static - */ - fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc; - - /** - * Creates an instance of fabric.Image from its object representation - * @static - * @param {Object} object Object to create an instance from - * @param {Function} [callback] Callback to invoke when an image instance is created - */ - fabric.Image.fromObject = function(object, callback) { - fabric.util.loadImage(object.src, function(img) { - fabric.Image.prototype._initFilters.call(object, object, function(filters) { - object.filters = filters || [ ]; - var instance = new fabric.Image(img, object); - callback && callback(instance); - }); - }, null, object.crossOrigin); - }; - - /** - * Creates an instance of fabric.Image from an URL string - * @static - * @param {String} url URL to create an image from - * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument) - * @param {Object} [imgOptions] Options object - */ - fabric.Image.fromURL = function(url, callback, imgOptions) { - fabric.util.loadImage(url, function(img) { - callback(new fabric.Image(img, imgOptions)); - }, null, imgOptions && imgOptions.crossOrigin); - }; - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement}) - * @static - * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement} - */ - fabric.Image.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y width height xlink:href'.split(' ')); - - /** - * Returns {@link fabric.Image} instance from an SVG element - * @static - * @param {SVGElement} element Element to parse - * @param {Function} callback Callback to execute when fabric.Image object is created - * @param {Object} [options] Options object - * @return {fabric.Image} Instance of fabric.Image - */ - fabric.Image.fromElement = function(element, callback, options) { - var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES); - - fabric.Image.fromURL(parsedAttributes['xlink:href'], callback, - extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes)); - }; - /* _FROM_SVG_END_ */ - - /** - * Indicates that instances of this type are async - * @static - * @type Boolean - * @default - */ - fabric.Image.async = true; - - /** - * Indicates compression level used when generating PNG under Node (in applyFilters). Any of 0-9 - * @static - * @type Number - * @default - */ - fabric.Image.pngCompression = 1; - -})(typeof exports !== 'undefined' ? exports : this); - - -/** - * @namespace fabric.Image.filters - * @memberOf fabric.Image - * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#image_filters} - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - */ -fabric.Image.filters = fabric.Image.filters || { }; - -/** - * Root filter class from which all filter classes inherit from - * @class fabric.Image.filters.BaseFilter - * @memberOf fabric.Image.filters - */ -fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'BaseFilter', - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return { type: this.type }; - }, - - /** - * Returns a JSON representation of an instance - * @return {Object} JSON - */ - toJSON: function() { - // delegate, not alias - return this.toObject(); - } -}); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Brightness filter class - * @class fabric.Image.filters.Brightness - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Brightness({ - * brightness: 200 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Brightness = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Brightness.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Brightness', - - /** - * Constructor - * @memberOf fabric.Image.filters.Brightness.prototype - * @param {Object} [options] Options object - * @param {Number} [options.brightness=0] Value to brighten the image up (0..255) - */ - initialize: function(options) { - options = options || { }; - this.brightness = options.brightness || 0; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - brightness = this.brightness; - - for (var i = 0, len = data.length; i < len; i += 4) { - data[i] += brightness; - data[i + 1] += brightness; - data[i + 2] += brightness; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - brightness: this.brightness - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness - */ - fabric.Image.filters.Brightness.fromObject = function(object) { - return new fabric.Image.filters.Brightness(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Adapted from html5rocks article - * @class fabric.Image.filters.Convolute - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example Sharpen filter - * var filter = new fabric.Image.filters.Convolute({ - * matrix: [ 0, -1, 0, - * -1, 5, -1, - * 0, -1, 0 ] - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - * @example Blur filter - * var filter = new fabric.Image.filters.Convolute({ - * matrix: [ 1/9, 1/9, 1/9, - * 1/9, 1/9, 1/9, - * 1/9, 1/9, 1/9 ] - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - * @example Emboss filter - * var filter = new fabric.Image.filters.Convolute({ - * matrix: [ 1, 1, 1, - * 1, 0.7, -1, - * -1, -1, -1 ] - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - * @example Emboss filter with opaqueness - * var filter = new fabric.Image.filters.Convolute({ - * opaque: true, - * matrix: [ 1, 1, 1, - * 1, 0.7, -1, - * -1, -1, -1 ] - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Convolute = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Convolute', - - /** - * Constructor - * @memberOf fabric.Image.filters.Convolute.prototype - * @param {Object} [options] Options object - * @param {Boolean} [options.opaque=false] Opaque value (true/false) - * @param {Array} [options.matrix] Filter matrix - */ - initialize: function(options) { - options = options || { }; - - this.opaque = options.opaque; - this.matrix = options.matrix || [ - 0, 0, 0, - 0, 1, 0, - 0, 0, 0 - ]; - - var canvasEl = fabric.util.createCanvasElement(); - this.tmpCtx = canvasEl.getContext('2d'); - }, - - /** - * @private - */ - _createImageData: function(w, h) { - return this.tmpCtx.createImageData(w, h); - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - - var weights = this.matrix, - context = canvasEl.getContext('2d'), - pixels = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - - side = Math.round(Math.sqrt(weights.length)), - halfSide = Math.floor(side/2), - src = pixels.data, - sw = pixels.width, - sh = pixels.height, - - // pad output by the convolution matrix - w = sw, - h = sh, - output = this._createImageData(w, h), - - dst = output.data, - - // go through the destination image pixels - alphaFac = this.opaque ? 1 : 0; - - for (var y = 0; y < h; y++) { - for (var x = 0; x < w; x++) { - var sy = y, - sx = x, - dstOff = (y * w + x) * 4, - // calculate the weighed sum of the source image pixels that - // fall under the convolution matrix - r = 0, g = 0, b = 0, a = 0; - - for (var cy = 0; cy < side; cy++) { - for (var cx = 0; cx < side; cx++) { - - var scy = sy + cy - halfSide, - scx = sx + cx - halfSide; - - /* jshint maxdepth:5 */ - if (scy < 0 || scy > sh || scx < 0 || scx > sw) { - continue; - } - - var srcOff = (scy * sw + scx) * 4, - wt = weights[cy * side + cx]; - - r += src[srcOff] * wt; - g += src[srcOff + 1] * wt; - b += src[srcOff + 2] * wt; - a += src[srcOff + 3] * wt; - } - } - dst[dstOff] = r; - dst[dstOff + 1] = g; - dst[dstOff + 2] = b; - dst[dstOff + 3] = a + alphaFac * (255 - a); - } - } - - context.putImageData(output, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - opaque: this.opaque, - matrix: this.matrix - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute - */ - fabric.Image.filters.Convolute.fromObject = function(object) { - return new fabric.Image.filters.Convolute(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * GradientTransparency filter class - * @class fabric.Image.filters.GradientTransparency - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.GradientTransparency#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.GradientTransparency({ - * threshold: 200 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.GradientTransparency = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.GradientTransparency.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'GradientTransparency', - - /** - * Constructor - * @memberOf fabric.Image.filters.GradientTransparency.prototype - * @param {Object} [options] Options object - * @param {Number} [options.threshold=100] Threshold value - */ - initialize: function(options) { - options = options || { }; - this.threshold = options.threshold || 100; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - threshold = this.threshold, - total = data.length; - - for (var i = 0, len = data.length; i < len; i += 4) { - data[i + 3] = threshold + 255 * (total - i) / total; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - threshold: this.threshold - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.GradientTransparency} Instance of fabric.Image.filters.GradientTransparency - */ - fabric.Image.filters.GradientTransparency.fromObject = function(object) { - return new fabric.Image.filters.GradientTransparency(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }); - - /** - * Grayscale image filter class - * @class fabric.Image.filters.Grayscale - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Grayscale(); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Grayscale = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Grayscale.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Grayscale', - - /** - * Applies filter to canvas element - * @memberOf fabric.Image.filters.Grayscale.prototype - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - len = imageData.width * imageData.height * 4, - index = 0, - average; - - while (index < len) { - average = (data[index] + data[index + 1] + data[index + 2]) / 3; - data[index] = average; - data[index + 1] = average; - data[index + 2] = average; - index += 4; - } - - context.putImageData(imageData, 0, 0); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale - */ - fabric.Image.filters.Grayscale.fromObject = function() { - return new fabric.Image.filters.Grayscale(); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }); - - /** - * Invert filter class - * @class fabric.Image.filters.Invert - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Invert(); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Invert = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Invert.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Invert', - - /** - * Applies filter to canvas element - * @memberOf fabric.Image.filters.Invert.prototype - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = data.length, i; - - for (i = 0; i < iLen; i+=4) { - data[i] = 255 - data[i]; - data[i + 1] = 255 - data[i + 1]; - data[i + 2] = 255 - data[i + 2]; - } - - context.putImageData(imageData, 0, 0); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert - */ - fabric.Image.filters.Invert.fromObject = function() { - return new fabric.Image.filters.Invert(); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Mask filter class - * See http://resources.aleph-1.com/mask/ - * @class fabric.Image.filters.Mask - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Mask#initialize} for constructor definition - */ - fabric.Image.filters.Mask = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Mask.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Mask', - - /** - * Constructor - * @memberOf fabric.Image.filters.Mask.prototype - * @param {Object} [options] Options object - * @param {fabric.Image} [options.mask] Mask image object - * @param {Number} [options.channel=0] Rgb channel (0, 1, 2 or 3) - */ - initialize: function(options) { - options = options || { }; - - this.mask = options.mask; - this.channel = [ 0, 1, 2, 3 ].indexOf(options.channel) > -1 ? options.channel : 0; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - if (!this.mask) { - return; - } - - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - maskEl = this.mask.getElement(), - maskCanvasEl = fabric.util.createCanvasElement(), - channel = this.channel, - i, - iLen = imageData.width * imageData.height * 4; - - maskCanvasEl.width = maskEl.width; - maskCanvasEl.height = maskEl.height; - - maskCanvasEl.getContext('2d').drawImage(maskEl, 0, 0, maskEl.width, maskEl.height); - - var maskImageData = maskCanvasEl.getContext('2d').getImageData(0, 0, maskEl.width, maskEl.height), - maskData = maskImageData.data; - - for (i = 0; i < iLen; i += 4) { - data[i + 3] = maskData[i + channel]; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - mask: this.mask.toObject(), - channel: this.channel - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @param {Function} [callback] Callback to invoke when a mask filter instance is created - */ - fabric.Image.filters.Mask.fromObject = function(object, callback) { - fabric.util.loadImage(object.mask.src, function(img) { - object.mask = new fabric.Image(img, object.mask); - callback && callback(new fabric.Image.filters.Mask(object)); - }); - }; - - /** - * Indicates that instances of this type are async - * @static - * @type Boolean - * @default - */ - fabric.Image.filters.Mask.async = true; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Noise filter class - * @class fabric.Image.filters.Noise - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Noise({ - * noise: 700 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Noise = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Noise.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Noise', - - /** - * Constructor - * @memberOf fabric.Image.filters.Noise.prototype - * @param {Object} [options] Options object - * @param {Number} [options.noise=0] Noise value - */ - initialize: function(options) { - options = options || { }; - this.noise = options.noise || 0; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - noise = this.noise, rand; - - for (var i = 0, len = data.length; i < len; i += 4) { - - rand = (0.5 - Math.random()) * noise; - - data[i] += rand; - data[i + 1] += rand; - data[i + 2] += rand; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - noise: this.noise - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise - */ - fabric.Image.filters.Noise.fromObject = function(object) { - return new fabric.Image.filters.Noise(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Pixelate filter class - * @class fabric.Image.filters.Pixelate - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Pixelate({ - * blocksize: 8 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Pixelate = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Pixelate.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Pixelate', - - /** - * Constructor - * @memberOf fabric.Image.filters.Pixelate.prototype - * @param {Object} [options] Options object - * @param {Number} [options.blocksize=4] Blocksize for pixelate - */ - initialize: function(options) { - options = options || { }; - this.blocksize = options.blocksize || 4; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = imageData.height, - jLen = imageData.width, - index, i, j, r, g, b, a; - - for (i = 0; i < iLen; i += this.blocksize) { - for (j = 0; j < jLen; j += this.blocksize) { - - index = (i * 4) * jLen + (j * 4); - - r = data[index]; - g = data[index + 1]; - b = data[index + 2]; - a = data[index + 3]; - - /* - blocksize: 4 - - [1,x,x,x,1] - [x,x,x,x,1] - [x,x,x,x,1] - [x,x,x,x,1] - [1,1,1,1,1] - */ - - for (var _i = i, _ilen = i + this.blocksize; _i < _ilen; _i++) { - for (var _j = j, _jlen = j + this.blocksize; _j < _jlen; _j++) { - index = (_i * 4) * jLen + (_j * 4); - data[index] = r; - data[index + 1] = g; - data[index + 2] = b; - data[index + 3] = a; - } - } - } - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - blocksize: this.blocksize - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate - */ - fabric.Image.filters.Pixelate.fromObject = function(object) { - return new fabric.Image.filters.Pixelate(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Remove white filter class - * @class fabric.Image.filters.RemoveWhite - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.RemoveWhite#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.RemoveWhite({ - * threshold: 40, - * distance: 140 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.RemoveWhite = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.RemoveWhite.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'RemoveWhite', - - /** - * Constructor - * @memberOf fabric.Image.filters.RemoveWhite.prototype - * @param {Object} [options] Options object - * @param {Number} [options.threshold=30] Threshold value - * @param {Number} [options.distance=20] Distance value - */ - initialize: function(options) { - options = options || { }; - this.threshold = options.threshold || 30; - this.distance = options.distance || 20; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - threshold = this.threshold, - distance = this.distance, - limit = 255 - threshold, - abs = Math.abs, - r, g, b; - - for (var i = 0, len = data.length; i < len; i += 4) { - r = data[i]; - g = data[i + 1]; - b = data[i + 2]; - - if (r > limit && - g > limit && - b > limit && - abs(r - g) < distance && - abs(r - b) < distance && - abs(g - b) < distance - ) { - data[i + 3] = 1; - } - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - threshold: this.threshold, - distance: this.distance - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.RemoveWhite} Instance of fabric.Image.filters.RemoveWhite - */ - fabric.Image.filters.RemoveWhite.fromObject = function(object) { - return new fabric.Image.filters.RemoveWhite(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }); - - /** - * Sepia filter class - * @class fabric.Image.filters.Sepia - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Sepia(); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Sepia = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Sepia', - - /** - * Applies filter to canvas element - * @memberOf fabric.Image.filters.Sepia.prototype - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = data.length, i, avg; - - for (i = 0; i < iLen; i+=4) { - avg = 0.3 * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2]; - data[i] = avg + 100; - data[i + 1] = avg + 50; - data[i + 2] = avg + 255; - } - - context.putImageData(imageData, 0, 0); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @return {fabric.Image.filters.Sepia} Instance of fabric.Image.filters.Sepia - */ - fabric.Image.filters.Sepia.fromObject = function() { - return new fabric.Image.filters.Sepia(); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }); - - /** - * Sepia2 filter class - * @class fabric.Image.filters.Sepia2 - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example - * var filter = new fabric.Image.filters.Sepia2(); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Sepia2 = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia2.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Sepia2', - - /** - * Applies filter to canvas element - * @memberOf fabric.Image.filters.Sepia.prototype - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = data.length, i, r, g, b; - - for (i = 0; i < iLen; i+=4) { - r = data[i]; - g = data[i + 1]; - b = data[i + 2]; - - data[i] = (r * 0.393 + g * 0.769 + b * 0.189 ) / 1.351; - data[i + 1] = (r * 0.349 + g * 0.686 + b * 0.168 ) / 1.203; - data[i + 2] = (r * 0.272 + g * 0.534 + b * 0.131 ) / 2.140; - } - - context.putImageData(imageData, 0, 0); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @return {fabric.Image.filters.Sepia2} Instance of fabric.Image.filters.Sepia2 - */ - fabric.Image.filters.Sepia2.fromObject = function() { - return new fabric.Image.filters.Sepia2(); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Tint filter class - * Adapted from https://github.com/mezzoblue/PaintbrushJS - * @class fabric.Image.filters.Tint - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @see {@link fabric.Image.filters.Tint#initialize} for constructor definition - * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo} - * @example Tint filter with hex color and opacity - * var filter = new fabric.Image.filters.Tint({ - * color: '#3513B0', - * opacity: 0.5 - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - * @example Tint filter with rgba color - * var filter = new fabric.Image.filters.Tint({ - * color: 'rgba(53, 21, 176, 0.5)' - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Tint = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Tint.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Tint', - - /** - * Constructor - * @memberOf fabric.Image.filters.Tint.prototype - * @param {Object} [options] Options object - * @param {String} [options.color=#000000] Color to tint the image with - * @param {Number} [options.opacity] Opacity value that controls the tint effect's transparency (0..1) - */ - initialize: function(options) { - options = options || { }; - - this.color = options.color || '#000000'; - this.opacity = typeof options.opacity !== 'undefined' - ? options.opacity - : new fabric.Color(this.color).getAlpha(); - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = data.length, i, - tintR, tintG, tintB, - r, g, b, alpha1, - source; - - source = new fabric.Color(this.color).getSource(); - - tintR = source[0] * this.opacity; - tintG = source[1] * this.opacity; - tintB = source[2] * this.opacity; - - alpha1 = 1 - this.opacity; - - for (i = 0; i < iLen; i+=4) { - r = data[i]; - g = data[i + 1]; - b = data[i + 2]; - - // alpha compositing - data[i] = tintR + r * alpha1; - data[i + 1] = tintG + g * alpha1; - data[i + 2] = tintB + b * alpha1; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - color: this.color, - opacity: this.opacity - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Tint} Instance of fabric.Image.filters.Tint - */ - fabric.Image.filters.Tint.fromObject = function(object) { - return new fabric.Image.filters.Tint(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend; - - /** - * Multiply filter class - * Adapted from http://www.laurenscorijn.com/articles/colormath-basics - * @class fabric.Image.filters.Multiply - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @example Multiply filter with hex color - * var filter = new fabric.Image.filters.Multiply({ - * color: '#F0F' - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - * @example Multiply filter with rgb color - * var filter = new fabric.Image.filters.Multiply({ - * color: 'rgb(53, 21, 176)' - * }); - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ { - - /** - * Filter type - * @param {String} type - * @default - */ - type: 'Multiply', - - /** - * Constructor - * @memberOf fabric.Image.filters.Multiply.prototype - * @param {Object} [options] Options object - * @param {String} [options.color=#000000] Color to multiply the image pixels with - */ - initialize: function(options) { - options = options || { }; - - this.color = options.color || '#000000'; - }, - - /** - * Applies filter to canvas element - * @param {Object} canvasEl Canvas element to apply filter to - */ - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - iLen = data.length, i, - source; - - source = new fabric.Color(this.color).getSource(); - - for (i = 0; i < iLen; i+=4) { - data[i] *= source[0] / 255; - data[i + 1] *= source[1] / 255; - data[i + 2] *= source[2] / 255; - } - - context.putImageData(imageData, 0, 0); - }, - - /** - * Returns object representation of an instance - * @return {Object} Object representation of an instance - */ - toObject: function() { - return extend(this.callSuper('toObject'), { - color: this.color - }); - } - }); - - /** - * Returns filter instance from an object representation - * @static - * @param {Object} object Object to create an instance from - * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply - */ - fabric.Image.filters.Multiply.fromObject = function(object) { - return new fabric.Image.filters.Multiply(object); - }; - -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global){ - 'use strict'; - - var fabric = global.fabric; - - /** - * Color Blend filter class - * @class fabric.Image.filter.Blend - * @memberOf fabric.Image.filters - * @extends fabric.Image.filters.BaseFilter - * @example - * var filter = new fabric.Image.filters.Blend({ - * color: '#000', - * mode: 'multiply' - * }); - * - * var filter = new fabric.Image.filters.Blend({ - * image: fabricImageObject, - * mode: 'multiply', - * alpha: 0.5 - * }); - - * object.filters.push(filter); - * object.applyFilters(canvas.renderAll.bind(canvas)); - */ - fabric.Image.filters.Blend = fabric.util.createClass({ - type: 'Blend', - - initialize: function(options){ - options = options || {}; - this.color = options.color || '#000'; - this.image = options.image || false; - this.mode = options.mode || 'multiply'; - this.alpha = options.alpha || 1; - }, - - applyTo: function(canvasEl) { - var context = canvasEl.getContext('2d'), - imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), - data = imageData.data, - tr, tg, tb, - r, g, b, - source, - isImage = false; - - if (this.image) { - // Blend images - isImage = true; - - var _el = fabric.util.createCanvasElement(); - _el.width = this.image.width; - _el.height = this.image.height; - - var tmpCanvas = new fabric.StaticCanvas(_el); - tmpCanvas.add(this.image); - var context2 = tmpCanvas.getContext('2d'); - source = context2.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height).data; - } - else { - // Blend color - source = new fabric.Color(this.color).getSource(); - - tr = source[0] * this.alpha; - tg = source[1] * this.alpha; - tb = source[2] * this.alpha; - } - - for (var i = 0, len = data.length; i < len; i += 4) { - - r = data[i]; - g = data[i + 1]; - b = data[i + 2]; - - if (isImage) { - tr = source[i] * this.alpha; - tg = source[i + 1] * this.alpha; - tb = source[i + 2] * this.alpha; - } - - switch (this.mode) { - case 'multiply': - data[i] = r * tr / 255; - data[i + 1] = g * tg / 255; - data[i + 2] = b * tb / 255; - break; - case 'screen': - data[i] = 1 - (1 - r) * (1 - tr); - data[i + 1] = 1 - (1 - g) * (1 - tg); - data[i + 2] = 1 - (1 - b) * (1 - tb); - break; - case 'add': - data[i] = Math.min(255, r + tr); - data[i + 1] = Math.min(255, g + tg); - data[i + 2] = Math.min(255, b + tb); - break; - case 'diff': - case 'difference': - data[i] = Math.abs(r - tr); - data[i + 1] = Math.abs(g - tg); - data[i + 2] = Math.abs(b - tb); - break; - case 'subtract': - var _r = r - tr, - _g = g - tg, - _b = b - tb; - - data[i] = (_r < 0) ? 0 : _r; - data[i + 1] = (_g < 0) ? 0 : _g; - data[i + 2] = (_b < 0) ? 0 : _b; - break; - case 'darken': - data[i] = Math.min(r, tr); - data[i + 1] = Math.min(g, tg); - data[i + 2] = Math.min(b, tb); - break; - case 'lighten': - data[i] = Math.max(r, tr); - data[i + 1] = Math.max(g, tg); - data[i + 2] = Math.max(b, tb); - break; - } - } - - context.putImageData(imageData, 0, 0); - } - }); - - fabric.Image.filters.Blend.fromObject = function(object) { - return new fabric.Image.filters.Blend(object); - }; -})(typeof exports !== 'undefined' ? exports : this); - - -(function(global) { - - 'use strict'; - - var fabric = global.fabric || (global.fabric = { }), - extend = fabric.util.object.extend, - clone = fabric.util.object.clone, - toFixed = fabric.util.toFixed, - supportsLineDash = fabric.StaticCanvas.supports('setLineDash'); - - if (fabric.Text) { - fabric.warn('fabric.Text is already defined'); - return; - } - - var stateProperties = fabric.Object.prototype.stateProperties.concat(); - stateProperties.push( - 'fontFamily', - 'fontWeight', - 'fontSize', - 'text', - 'textDecoration', - 'textAlign', - 'fontStyle', - 'lineHeight', - 'textBackgroundColor', - 'useNative', - 'path' - ); - - /** - * Text class - * @class fabric.Text - * @extends fabric.Object - * @return {fabric.Text} thisArg - * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#text} - * @see {@link fabric.Text#initialize} for constructor definition - */ - fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ { - - /** - * Properties which when set cause object to change dimensions - * @type Object - * @private - */ - _dimensionAffectingProps: { - fontSize: true, - fontWeight: true, - fontFamily: true, - textDecoration: true, - fontStyle: true, - lineHeight: true, - stroke: true, - strokeWidth: true, - text: true - }, - - /** - * @private - */ - _reNewline: /\r?\n/, - - /** - * Retrieves object's fontSize - * @method getFontSize - * @memberOf fabric.Text.prototype - * @return {String} Font size (in pixels) - */ - - /** - * Sets object's fontSize - * @method setFontSize - * @memberOf fabric.Text.prototype - * @param {Number} fontSize Font size (in pixels) - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's fontWeight - * @method getFontWeight - * @memberOf fabric.Text.prototype - * @return {(String|Number)} Font weight - */ - - /** - * Sets object's fontWeight - * @method setFontWeight - * @memberOf fabric.Text.prototype - * @param {(Number|String)} fontWeight Font weight - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's fontFamily - * @method getFontFamily - * @memberOf fabric.Text.prototype - * @return {String} Font family - */ - - /** - * Sets object's fontFamily - * @method setFontFamily - * @memberOf fabric.Text.prototype - * @param {String} fontFamily Font family - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's text - * @method getText - * @memberOf fabric.Text.prototype - * @return {String} text - */ - - /** - * Sets object's text - * @method setText - * @memberOf fabric.Text.prototype - * @param {String} text Text - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's textDecoration - * @method getTextDecoration - * @memberOf fabric.Text.prototype - * @return {String} Text decoration - */ - - /** - * Sets object's textDecoration - * @method setTextDecoration - * @memberOf fabric.Text.prototype - * @param {String} textDecoration Text decoration - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's fontStyle - * @method getFontStyle - * @memberOf fabric.Text.prototype - * @return {String} Font style - */ - - /** - * Sets object's fontStyle - * @method setFontStyle - * @memberOf fabric.Text.prototype - * @param {String} fontStyle Font style - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's lineHeight - * @method getLineHeight - * @memberOf fabric.Text.prototype - * @return {Number} Line height - */ - - /** - * Sets object's lineHeight - * @method setLineHeight - * @memberOf fabric.Text.prototype - * @param {Number} lineHeight Line height - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's textAlign - * @method getTextAlign - * @memberOf fabric.Text.prototype - * @return {String} Text alignment - */ - - /** - * Sets object's textAlign - * @method setTextAlign - * @memberOf fabric.Text.prototype - * @param {String} textAlign Text alignment - * @return {fabric.Text} - * @chainable - */ - - /** - * Retrieves object's textBackgroundColor - * @method getTextBackgroundColor - * @memberOf fabric.Text.prototype - * @return {String} Text background color - */ - - /** - * Sets object's textBackgroundColor - * @method setTextBackgroundColor - * @memberOf fabric.Text.prototype - * @param {String} textBackgroundColor Text background color - * @return {fabric.Text} - * @chainable - */ - - /** - * Type of an object - * @type String - * @default - */ - type: 'text', - - /** - * Font size (in pixels) - * @type Number - * @default - */ - fontSize: 40, - - /** - * Font weight (e.g. bold, normal, 400, 600, 800) - * @type {(Number|String)} - * @default - */ - fontWeight: 'normal', - - /** - * Font family - * @type String - * @default - */ - fontFamily: 'Times New Roman', - - /** - * Text decoration Possible values: "", "underline", "overline" or "line-through". - * @type String - * @default - */ - textDecoration: '', - - /** - * Text alignment. Possible values: "left", "center", or "right". - * @type String - * @default - */ - textAlign: 'left', - - /** - * Font style . Possible values: "", "normal", "italic" or "oblique". - * @type String - * @default - */ - fontStyle: '', - - /** - * Line height - * @type Number - * @default - */ - lineHeight: 1.3, - - /** - * Background color of text lines - * @type String - * @default - */ - textBackgroundColor: '', - - /** - * URL of a font file, when using Cufon - * @type String | null - * @default - */ - path: null, - - /** - * Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used) - * @type Boolean - * @default - */ - useNative: true, - - /** - * List of properties to consider when checking if - * state of an object is changed ({@link fabric.Object#hasStateChanged}) - * as well as for history (undo/redo) purposes - * @type Array - */ - stateProperties: stateProperties, - - /** - * When defined, an object is rendered via stroke and this property specifies its color. - * Backwards incompatibility note: This property was named "strokeStyle" until v1.1.6 - * @type String - * @default - */ - stroke: null, - - /** - * Shadow object representing shadow of this shape. - * Backwards incompatibility note: This property was named "textShadow" (String) until v1.2.11 - * @type fabric.Shadow - * @default - */ - shadow: null, - - /** - * Constructor - * @param {String} text Text string - * @param {Object} [options] Options object - * @return {fabric.Text} thisArg - */ - initialize: function(text, options) { - options = options || { }; - - this.text = text; - this.__skipDimension = true; - this.setOptions(options); - this.__skipDimension = false; - this._initDimensions(); - }, - - /** - * Renders text object on offscreen canvas, so that it would get dimensions - * @private - */ - _initDimensions: function() { - if (this.__skipDimension) { - return; - } - var canvasEl = fabric.util.createCanvasElement(); - this._render(canvasEl.getContext('2d')); - }, - - /** - * Returns string representation of an instance - * @return {String} String representation of text object - */ - toString: function() { - return '#'; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - - if (typeof Cufon === 'undefined' || this.useNative === true) { - this._renderViaNative(ctx); - } - else { - this._renderViaCufon(ctx); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderViaNative: function(ctx) { - var textLines = this.text.split(this._reNewline); - - this._setTextStyles(ctx); - - this.width = this._getTextWidth(ctx, textLines); - this.height = this._getTextHeight(ctx, textLines); - - this.clipTo && fabric.util.clipContext(this, ctx); - - this._renderTextBackground(ctx, textLines); - this._translateForTextAlign(ctx); - this._renderText(ctx, textLines); - - if (this.textAlign !== 'left' && this.textAlign !== 'justify') { - ctx.restore(); - } - - this._renderTextDecoration(ctx, textLines); - this.clipTo && ctx.restore(); - - this._setBoundaries(ctx, textLines); - this._totalLineHeight = 0; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderText: function(ctx, textLines) { - ctx.save(); - this._setShadow(ctx); - this._setupFillRule(ctx); - this._renderTextFill(ctx, textLines); - this._renderTextStroke(ctx, textLines); - this._restoreFillRule(ctx); - this._removeShadow(ctx); - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _translateForTextAlign: function(ctx) { - if (this.textAlign !== 'left' && this.textAlign !== 'justify') { - ctx.save(); - ctx.translate(this.textAlign === 'center' ? (this.width / 2) : this.width, 0); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _setBoundaries: function(ctx, textLines) { - this._boundaries = [ ]; - - for (var i = 0, len = textLines.length; i < len; i++) { - - var lineWidth = this._getLineWidth(ctx, textLines[i]), - lineLeftOffset = this._getLineLeftOffset(lineWidth); - - this._boundaries.push({ - height: this.fontSize * this.lineHeight, - width: lineWidth, - left: lineLeftOffset - }); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _setTextStyles: function(ctx) { - this._setFillStyles(ctx); - this._setStrokeStyles(ctx); - ctx.textBaseline = 'alphabetic'; - if (!this.skipTextAlign) { - ctx.textAlign = this.textAlign; - } - ctx.font = this._getFontDeclaration(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - * @return {Number} Height of fabric.Text object - */ - _getTextHeight: function(ctx, textLines) { - return this.fontSize * textLines.length * this.lineHeight; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - * @return {Number} Maximum width of fabric.Text object - */ - _getTextWidth: function(ctx, textLines) { - var maxWidth = ctx.measureText(textLines[0] || '|').width; - - for (var i = 1, len = textLines.length; i < len; i++) { - var currentLineWidth = ctx.measureText(textLines[i]).width; - if (currentLineWidth > maxWidth) { - maxWidth = currentLineWidth; - } - } - return maxWidth; - }, - - /** - * @private - * @param {String} method Method name ("fillText" or "strokeText") - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} chars Chars to render - * @param {Number} left Left position of text - * @param {Number} top Top position of text - */ - _renderChars: function(method, ctx, chars, left, top) { - ctx[method](chars, left, top); - }, - - /** - * @private - * @param {String} method Method name ("fillText" or "strokeText") - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line Text to render - * @param {Number} left Left position of text - * @param {Number} top Top position of text - * @param {Number} lineIndex Index of a line in a text - */ - _renderTextLine: function(method, ctx, line, left, top, lineIndex) { - // lift the line by quarter of fontSize - top -= this.fontSize / 4; - - // short-circuit - if (this.textAlign !== 'justify') { - this._renderChars(method, ctx, line, left, top, lineIndex); - return; - } - - var lineWidth = ctx.measureText(line).width, - totalWidth = this.width; - - if (totalWidth > lineWidth) { - // stretch the line - var words = line.split(/\s+/), - wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width, - widthDiff = totalWidth - wordsWidth, - numSpaces = words.length - 1, - spaceWidth = widthDiff / numSpaces, - leftOffset = 0; - - for (var i = 0, len = words.length; i < len; i++) { - this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex); - leftOffset += ctx.measureText(words[i]).width + spaceWidth; - } - } - else { - this._renderChars(method, ctx, line, left, top, lineIndex); - } - }, - - /** - * @private - * @return {Number} Left offset - */ - _getLeftOffset: function() { - if (fabric.isLikelyNode) { - return 0; - } - return -this.width / 2; - }, - - /** - * @private - * @return {Number} Top offset - */ - _getTopOffset: function() { - return -this.height / 2; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextFill: function(ctx, textLines) { - if (!this.fill && !this._skipFillStrokeCheck) { - return; - } - - this._boundaries = [ ]; - var lineHeights = 0; - - for (var i = 0, len = textLines.length; i < len; i++) { - var heightOfLine = this._getHeightOfLine(ctx, i, textLines); - lineHeights += heightOfLine; - - this._renderTextLine( - 'fillText', - ctx, - textLines[i], - this._getLeftOffset(), - this._getTopOffset() + lineHeights, - i - ); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextStroke: function(ctx, textLines) { - if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) { - return; - } - - var lineHeights = 0; - - ctx.save(); - if (this.strokeDashArray) { - // Spec requires the concatenation of two copies the dash list when the number of elements is odd - if (1 & this.strokeDashArray.length) { - this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray); - } - supportsLineDash && ctx.setLineDash(this.strokeDashArray); - } - - ctx.beginPath(); - for (var i = 0, len = textLines.length; i < len; i++) { - var heightOfLine = this._getHeightOfLine(ctx, i, textLines); - lineHeights += heightOfLine; - - this._renderTextLine( - 'strokeText', - ctx, - textLines[i], - this._getLeftOffset(), - this._getTopOffset() + lineHeights, - i - ); - } - ctx.closePath(); - ctx.restore(); - }, - - _getHeightOfLine: function() { - return this.fontSize * this.lineHeight; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextBackground: function(ctx, textLines) { - this._renderTextBoxBackground(ctx); - this._renderTextLinesBackground(ctx, textLines); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderTextBoxBackground: function(ctx) { - if (!this.backgroundColor) { - return; - } - - ctx.save(); - ctx.fillStyle = this.backgroundColor; - - ctx.fillRect( - this._getLeftOffset(), - this._getTopOffset(), - this.width, - this.height - ); - - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextLinesBackground: function(ctx, textLines) { - if (!this.textBackgroundColor) { - return; - } - - ctx.save(); - ctx.fillStyle = this.textBackgroundColor; - - for (var i = 0, len = textLines.length; i < len; i++) { - - if (textLines[i] !== '') { - - var lineWidth = this._getLineWidth(ctx, textLines[i]), - lineLeftOffset = this._getLineLeftOffset(lineWidth); - - ctx.fillRect( - this._getLeftOffset() + lineLeftOffset, - this._getTopOffset() + (i * this.fontSize * this.lineHeight), - lineWidth, - this.fontSize * this.lineHeight - ); - } - } - ctx.restore(); - }, - - /** - * @private - * @param {Number} lineWidth Width of text line - * @return {Number} Line left offset - */ - _getLineLeftOffset: function(lineWidth) { - if (this.textAlign === 'center') { - return (this.width - lineWidth) / 2; - } - if (this.textAlign === 'right') { - return this.width - lineWidth; - } - return 0; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line Text line - * @return {Number} Line width - */ - _getLineWidth: function(ctx, line) { - return this.textAlign === 'justify' - ? this.width - : ctx.measureText(line).width; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextDecoration: function(ctx, textLines) { - if (!this.textDecoration) { - return; - } - - // var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2; - var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2, - _this = this; - - /** @ignore */ - function renderLinesAtOffset(offset) { - for (var i = 0, len = textLines.length; i < len; i++) { - - var lineWidth = _this._getLineWidth(ctx, textLines[i]), - lineLeftOffset = _this._getLineLeftOffset(lineWidth); - - ctx.fillRect( - _this._getLeftOffset() + lineLeftOffset, - ~~((offset + (i * _this._getHeightOfLine(ctx, i, textLines))) - halfOfVerticalBox), - lineWidth, - 1); - } - } - - if (this.textDecoration.indexOf('underline') > -1) { - renderLinesAtOffset(this.fontSize * this.lineHeight); - } - if (this.textDecoration.indexOf('line-through') > -1) { - renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize / 2); - } - if (this.textDecoration.indexOf('overline') > -1) { - renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize); - } - }, - - /** - * @private - */ - _getFontDeclaration: function() { - return [ - // node-canvas needs "weight style", while browsers need "style weight" - (fabric.isLikelyNode ? this.fontWeight : this.fontStyle), - (fabric.isLikelyNode ? this.fontStyle : this.fontWeight), - this.fontSize + 'px', - (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily) - ].join(' '); - }, - - /** - * Renders text instance on a specified context - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - render: function(ctx, noTransform) { - // do not render if object is not visible - if (!this.visible) { - return; - } - - ctx.save(); - this._transform(ctx, noTransform); - - var m = this.transformMatrix, - isInPathGroup = this.group && this.group.type === 'path-group'; - - if (isInPathGroup) { - ctx.translate(-this.group.width/2, -this.group.height/2); - } - if (m) { - ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]); - } - if (isInPathGroup) { - ctx.translate(this.left, this.top); - } - this._render(ctx); - ctx.restore(); - }, - - /** - * Returns object representation of an instance - * @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) { - var object = extend(this.callSuper('toObject', propertiesToInclude), { - text: this.text, - fontSize: this.fontSize, - fontWeight: this.fontWeight, - fontFamily: this.fontFamily, - fontStyle: this.fontStyle, - lineHeight: this.lineHeight, - textDecoration: this.textDecoration, - textAlign: this.textAlign, - path: this.path, - textBackgroundColor: this.textBackgroundColor, - useNative: this.useNative - }); - if (!this.includeDefaultValues) { - this._removeDefaultValues(object); - } - return object; - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - var markup = [ ], - textLines = this.text.split(this._reNewline), - offsets = this._getSVGLeftTopOffsets(textLines), - textAndBg = this._getSVGTextAndBg(offsets.lineTop, offsets.textLeft, textLines), - shadowSpans = this._getSVGShadows(offsets.lineTop, textLines); - - // move top offset by an ascent - offsets.textTop += (this._fontAscent ? ((this._fontAscent / 5) * this.lineHeight) : 0); - - this._wrapSVGTextAndBg(markup, textAndBg, shadowSpans, offsets); - - return reviver ? reviver(markup.join('')) : markup.join(''); - }, - - /** - * @private - */ - _getSVGLeftTopOffsets: function(textLines) { - var lineTop = this.useNative - ? this.fontSize * this.lineHeight - : (-this._fontAscent - ((this._fontAscent / 5) * this.lineHeight)), - - textLeft = -(this.width/2), - textTop = this.useNative - ? this.fontSize - 1 - : (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight; - - return { - textLeft: textLeft + (this.group && this.group.type === 'path-group' ? this.left : 0), - textTop: textTop + (this.group && this.group.type === 'path-group' ? this.top : 0), - lineTop: lineTop - }; - }, - - /** - * @private - */ - _wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) { - markup.push( - '\n', - textAndBg.textBgRects.join(''), - '', - shadowSpans.join(''), - textAndBg.textSpans.join(''), - '\n', - '\n' - ); - }, - - /** - * @private - * @param {Number} lineHeight - * @param {Array} textLines Array of all text lines - * @return {Array} - */ - _getSVGShadows: function(lineHeight, textLines) { - var shadowSpans = [], - i, len, - lineTopOffsetMultiplier = 1; - - if (!this.shadow || !this._boundaries) { - return shadowSpans; - } - - for (i = 0, len = textLines.length; i < len; i++) { - if (textLines[i] !== '') { - var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0; - shadowSpans.push( - '', - fabric.util.string.escapeXml(textLines[i]), - ''); - lineTopOffsetMultiplier = 1; - } - else { - // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier - // prevents empty tspans - lineTopOffsetMultiplier++; - } - } - - return shadowSpans; - }, - - /** - * @private - * @param {Number} lineHeight - * @param {Number} textLeftOffset Text left offset - * @param {Array} textLines Array of all text lines - * @return {Object} - */ - _getSVGTextAndBg: function(lineHeight, textLeftOffset, textLines) { - var textSpans = [ ], - textBgRects = [ ], - lineTopOffsetMultiplier = 1; - - // bounding-box background - this._setSVGBg(textBgRects); - - // text and text-background - for (var i = 0, len = textLines.length; i < len; i++) { - if (textLines[i] !== '') { - this._setSVGTextLineText(textLines[i], i, textSpans, lineHeight, lineTopOffsetMultiplier, textBgRects); - lineTopOffsetMultiplier = 1; - } - else { - // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier - // prevents empty tspans - lineTopOffsetMultiplier++; - } - - if (!this.textBackgroundColor || !this._boundaries) { - continue; - } - - this._setSVGTextLineBg(textBgRects, i, textLeftOffset, lineHeight); - } - - return { - textSpans: textSpans, - textBgRects: textBgRects - }; - }, - - _setSVGTextLineText: function(textLine, i, textSpans, lineHeight, lineTopOffsetMultiplier) { - var lineLeftOffset = (this._boundaries && this._boundaries[i]) - ? toFixed(this._boundaries[i].left, 2) - : 0; - - textSpans.push( - ' elements since setting opacity - // on containing one doesn't work in Illustrator - this._getFillAttributes(this.fill), '>', - fabric.util.string.escapeXml(textLine), - '' - ); - }, - - _setSVGTextLineBg: function(textBgRects, i, textLeftOffset, lineHeight) { - textBgRects.push( - '\n'); - }, - - _setSVGBg: function(textBgRects) { - if (this.backgroundColor && this._boundaries) { - textBgRects.push( - ''); - } - }, - - /** - * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values - * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1 - * - * @private - * @param {Any} value - * @return {String} - */ - _getFillAttributes: function(value) { - var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : ''; - if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) { - return 'fill="' + value + '"'; - } - return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"'; - }, - /* _TO_SVG_END_ */ - - /** - * Sets specified property to a specified value - * @param {String} key - * @param {Any} value - * @return {fabric.Text} thisArg - * @chainable - */ - _set: function(key, value) { - if (key === 'fontFamily' && this.path) { - this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3'); - } - this.callSuper('_set', key, value); - - if (key in this._dimensionAffectingProps) { - this._initDimensions(); - this.setCoords(); - } - }, - - /** - * Returns complexity of an instance - * @return {Number} complexity - */ - complexity: function() { - return 1; - } - }); - - /* _FROM_SVG_START_ */ - /** - * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement}) - * @static - * @memberOf fabric.Text - * @see: http://www.w3.org/TR/SVG/text.html#TextElement - */ - fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat( - 'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' ')); - - /** - * Default SVG font size - * @static - * @memberOf fabric.Text - */ - fabric.Text.DEFAULT_SVG_FONT_SIZE = 16; - - /** - * Returns fabric.Text instance from an SVG element (not yet implemented) - * @static - * @memberOf fabric.Text - * @param {SVGElement} element Element to parse - * @param {Object} [options] Options object - * @return {fabric.Text} Instance of fabric.Text - */ - fabric.Text.fromElement = function(element, options) { - if (!element) { - return null; - } - - var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES); - options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes); - - if ('dx' in parsedAttributes) { - options.left += parsedAttributes.dx; - } - if ('dy' in parsedAttributes) { - options.top += parsedAttributes.dy; - } - if (!('fontSize' in options)) { - options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE; - } - - if (!options.originX) { - options.originX = 'left'; - } - - var text = new fabric.Text(element.textContent, options), - /* - Adjust positioning: - x/y attributes in SVG correspond to the bottom-left corner of text bounding box - top/left properties in Fabric correspond to center point of text bounding box - */ - offX = 0; - - if (text.originX === 'left') { - offX = text.getWidth() / 2; - } - if (text.originX === 'right') { - offX = -text.getWidth() / 2; - } - text.set({ - left: text.getLeft() + offX, - top: text.getTop() - text.getHeight() / 2 - }); - - return text; - }; - /* _FROM_SVG_END_ */ - - /** - * Returns fabric.Text instance from an object representation - * @static - * @memberOf fabric.Text - * @param {Object} object Object to create an instance from - * @return {fabric.Text} Instance of fabric.Text - */ - fabric.Text.fromObject = function(object) { - return new fabric.Text(object.text, clone(object)); - }; - - fabric.util.createAccessors(fabric.Text); - -})(typeof exports !== 'undefined' ? exports : this); - - -}).call(this,require("buffer").Buffer) -},{"buffer":2,"canvas":1,"jsdom":1}]},{},[6])(6) -}); \ No newline at end of file diff --git a/dist/html2canvas.svg.min.js b/dist/html2canvas.svg.min.js deleted file mode 100644 index 5fb1c1c..0000000 --- a/dist/html2canvas.svg.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - html2canvas 0.5.0-alpha2 - Copyright (c) 2015 Niklas von Hertzen - - Released under MIT License -*/ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),(n.html2canvas||(n.html2canvas={})).svg=e()}}(function(){var define,module,exports;return function e(n,f,o){function d(t,l){if(!f[t]){if(!n[t]){var s="function"==typeof require&&require;if(!l&&s)return s(t,!0);if(i)return i(t,!0);var u=new Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}var a=f[t]={exports:{}};n[t][0].call(a.exports,function(e){var f=n[t][1][e];return d(f?f:e)},a,a.exports,e,n,f,o)}return f[t].exports}for(var i="function"==typeof require&&require,t=0;t0?e>>>0:0;else if("string"===i)d=o.byteLength(e,n);else{if("object"!==i||null===e)throw new TypeError("must start with number, buffer, array or string");"Buffer"===e.type&&K(e.data)&&(e=e.data),d=+e.length>0?Math.floor(+e.length):0}if(d>L)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+L.toString(16)+" bytes");var t;o.TYPED_ARRAY_SUPPORT?t=o._augment(new Uint8Array(d)):(t=this,t.length=d,t._isBuffer=!0);var l;if(o.TYPED_ARRAY_SUPPORT&&"number"==typeof e.byteLength)t._set(e);else if(A(e))if(o.isBuffer(e))for(l=0;d>l;l++)t[l]=e.readUInt8(l);else for(l=0;d>l;l++)t[l]=(e[l]%256+256)%256;else if("string"===i)t.write(e,0,n);else if("number"===i&&!o.TYPED_ARRAY_SUPPORT&&!f)for(l=0;d>l;l++)t[l]=0;return d>0&&d<=o.poolSize&&(t.parent=M),t}function d(e,n,f){if(!(this instanceof d))return new d(e,n,f);var i=new o(e,n,f);return delete i.parent,i}function i(e,n,f,o){f=Number(f)||0;var d=e.length-f;o?(o=Number(o),o>d&&(o=d)):o=d;var i=n.length;if(i%2!==0)throw new Error("Invalid hex string");o>i/2&&(o=i/2);for(var t=0;o>t;t++){var l=parseInt(n.substr(2*t,2),16);if(isNaN(l))throw new Error("Invalid hex string");e[f+t]=l}return t}function t(e,n,f,o){var d=G(C(n,e.length-f),e,f,o);return d}function l(e,n,f,o){var d=G(D(n),e,f,o);return d}function s(e,n,f,o){return l(e,n,f,o)}function u(e,n,f,o){var d=G(F(n),e,f,o);return d}function a(e,n,f,o){var d=G(E(n,e.length-f),e,f,o,2);return d}function p(e,n,f){return I.fromByteArray(0===n&&f===e.length?e:e.slice(n,f))}function c(e,n,f){var o="",d="";f=Math.min(e.length,f);for(var i=n;f>i;i++)e[i]<=127?(o+=H(d)+String.fromCharCode(e[i]),d=""):d+="%"+e[i].toString(16);return o+H(d)}function y(e,n,f){var o="";f=Math.min(e.length,f);for(var d=n;f>d;d++)o+=String.fromCharCode(127&e[d]);return o}function m(e,n,f){var o="";f=Math.min(e.length,f);for(var d=n;f>d;d++)o+=String.fromCharCode(e[d]);return o}function r(e,n,f){var o=e.length;(!n||0>n)&&(n=0),(!f||0>f||f>o)&&(f=o);for(var d="",i=n;f>i;i++)d+=B(e[i]);return d}function v(e,n,f){for(var o=e.slice(n,f),d="",i=0;ie)throw new RangeError("offset is not uint");if(e+n>f)throw new RangeError("Trying to access beyond buffer length")}function b(e,n,f,d,i,t){if(!o.isBuffer(e))throw new TypeError("buffer must be a Buffer instance");if(n>i||t>n)throw new RangeError("value is out of bounds");if(f+d>e.length)throw new RangeError("index out of range")}function g(e,n,f,o){0>n&&(n=65535+n+1);for(var d=0,i=Math.min(e.length-f,2);i>d;d++)e[f+d]=(n&255<<8*(o?d:1-d))>>>8*(o?d:1-d)}function h(e,n,f,o){0>n&&(n=4294967295+n+1);for(var d=0,i=Math.min(e.length-f,4);i>d;d++)e[f+d]=n>>>8*(o?d:3-d)&255}function x(e,n,f,o,d,i){if(n>d||i>n)throw new RangeError("value is out of bounds");if(f+o>e.length)throw new RangeError("index out of range");if(0>f)throw new RangeError("index out of range")}function j(e,n,f,o,d){return d||x(e,n,f,4,3.4028234663852886e38,-3.4028234663852886e38),J.write(e,n,f,o,23,4),f+4}function k(e,n,f,o,d){return d||x(e,n,f,8,1.7976931348623157e308,-1.7976931348623157e308),J.write(e,n,f,o,52,8),f+8}function q(e){if(e=z(e).replace(O,""),e.length<2)return"";for(;e.length%4!==0;)e+="=";return e}function z(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function A(e){return K(e)||o.isBuffer(e)||e&&"object"==typeof e&&"number"==typeof e.length}function B(e){return 16>e?"0"+e.toString(16):e.toString(16)}function C(e,n){var f,o=e.length,d=null;n=n||1/0;for(var i=[],t=0;o>t;t++){if(f=e.charCodeAt(t),f>55295&&57344>f){if(!d){if(f>56319){(n-=3)>-1&&i.push(239,191,189);continue}if(t+1===o){(n-=3)>-1&&i.push(239,191,189);continue}d=f;continue}if(56320>f){(n-=3)>-1&&i.push(239,191,189),d=f;continue}f=d-55296<<10|f-56320|65536,d=null}else d&&((n-=3)>-1&&i.push(239,191,189),d=null);if(128>f){if((n-=1)<0)break;i.push(f)}else if(2048>f){if((n-=2)<0)break;i.push(f>>6|192,63&f|128)}else if(65536>f){if((n-=3)<0)break;i.push(f>>12|224,f>>6&63|128,63&f|128)}else{if(!(2097152>f))throw new Error("Invalid code point");if((n-=4)<0)break;i.push(f>>18|240,f>>12&63|128,f>>6&63|128,63&f|128)}}return i}function D(e){for(var n=[],f=0;f>8,d=f%256,i.push(d),i.push(o);return i}function F(e){return I.toByteArray(q(e))}function G(e,n,f,o,d){d&&(o-=o%d);for(var i=0;o>i&&!(i+f>=n.length||i>=e.length);i++)n[i+f]=e[i];return i}function H(e){try{return decodeURIComponent(e)}catch(n){return String.fromCharCode(65533)}}var I=e("base64-js"),J=e("ieee754"),K=e("is-array");f.Buffer=o,f.SlowBuffer=d,f.INSPECT_MAX_BYTES=50,o.poolSize=8192;var L=1073741823,M={};o.TYPED_ARRAY_SUPPORT=function(){try{var e=new ArrayBuffer(0),n=new Uint8Array(e);return n.foo=function(){return 42},42===n.foo()&&"function"==typeof n.subarray&&0===new Uint8Array(1).subarray(1,1).byteLength}catch(f){return!1}}(),o.isBuffer=function(e){return!(null==e||!e._isBuffer)},o.compare=function(e,n){if(!o.isBuffer(e)||!o.isBuffer(n))throw new TypeError("Arguments must be Buffers");for(var f=e.length,d=n.length,i=0,t=Math.min(f,d);t>i&&e[i]===n[i];i++);return i!==t&&(f=e[i],d=n[i]),d>f?-1:f>d?1:0},o.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},o.concat=function(e,n){if(!K(e))throw new TypeError("Usage: Buffer.concat(list[, length])");if(0===e.length)return new o(0);if(1===e.length)return e[0];var f;if(void 0===n)for(n=0,f=0;f>>1;break;case"utf8":case"utf-8":f=C(e).length;break;case"base64":f=F(e).length;break;default:f=e.length}return f},o.prototype.length=void 0,o.prototype.parent=void 0,o.prototype.toString=function(e,n,f){var o=!1;if(n>>>=0,f=void 0===f||1/0===f?this.length:f>>>0,e||(e="utf8"),0>n&&(n=0),f>this.length&&(f=this.length),n>=f)return"";for(;;)switch(e){case"hex":return r(this,n,f);case"utf8":case"utf-8":return c(this,n,f);case"ascii":return y(this,n,f);case"binary":return m(this,n,f);case"base64":return p(this,n,f);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return v(this,n,f);default:if(o)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),o=!0}},o.prototype.equals=function(e){if(!o.isBuffer(e))throw new TypeError("Argument must be a Buffer");return 0===o.compare(this,e)},o.prototype.inspect=function(){var e="",n=f.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},o.prototype.compare=function(e){if(!o.isBuffer(e))throw new TypeError("Argument must be a Buffer");return o.compare(this,e)},o.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},o.prototype.set=function(e,n){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,n)},o.prototype.write=function(e,n,f,o){if(isFinite(n))isFinite(f)||(o=f,f=void 0);else{var d=o;o=n,n=f,f=d}if(n=Number(n)||0,0>f||0>n||n>this.length)throw new RangeError("attempt to write outside buffer bounds");var p=this.length-n;f?(f=Number(f),f>p&&(f=p)):f=p,o=String(o||"utf8").toLowerCase();var c;switch(o){case"hex":c=i(this,e,n,f);break;case"utf8":case"utf-8":c=t(this,e,n,f);break;case"ascii":c=l(this,e,n,f);break;case"binary":c=s(this,e,n,f);break;case"base64":c=u(this,e,n,f);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":c=a(this,e,n,f);break;default:throw new TypeError("Unknown encoding: "+o)}return c},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},o.prototype.slice=function(e,n){var f=this.length;e=~~e,n=void 0===n?f:~~n,0>e?(e+=f,0>e&&(e=0)):e>f&&(e=f),0>n?(n+=f,0>n&&(n=0)):n>f&&(n=f),e>n&&(n=e);var d;if(o.TYPED_ARRAY_SUPPORT)d=o._augment(this.subarray(e,n));else{var i=n-e;d=new o(i,void 0,!0);for(var t=0;i>t;t++)d[t]=this[t+e]}return d.length&&(d.parent=this.parent||this),d},o.prototype.readUIntLE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e],d=1,i=0;++i>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e+--n],d=1;n>0&&(d*=256);)o+=this[e+--n]*d;return o},o.prototype.readUInt8=function(e,n){return n||w(e,1,this.length),this[e]},o.prototype.readUInt16LE=function(e,n){return n||w(e,2,this.length),this[e]|this[e+1]<<8},o.prototype.readUInt16BE=function(e,n){return n||w(e,2,this.length),this[e]<<8|this[e+1]},o.prototype.readUInt32LE=function(e,n){return n||w(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},o.prototype.readUInt32BE=function(e,n){return n||w(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},o.prototype.readIntLE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e],d=1,i=0;++i=d&&(o-=Math.pow(2,8*n)),o},o.prototype.readIntBE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=n,d=1,i=this[e+--o];o>0&&(d*=256);)i+=this[e+--o]*d;return d*=128,i>=d&&(i-=Math.pow(2,8*n)),i},o.prototype.readInt8=function(e,n){return n||w(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},o.prototype.readInt16LE=function(e,n){n||w(e,2,this.length);var f=this[e]|this[e+1]<<8;return 32768&f?4294901760|f:f},o.prototype.readInt16BE=function(e,n){n||w(e,2,this.length);var f=this[e+1]|this[e]<<8;return 32768&f?4294901760|f:f},o.prototype.readInt32LE=function(e,n){return n||w(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},o.prototype.readInt32BE=function(e,n){return n||w(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},o.prototype.readFloatLE=function(e,n){return n||w(e,4,this.length),J.read(this,e,!0,23,4)},o.prototype.readFloatBE=function(e,n){return n||w(e,4,this.length),J.read(this,e,!1,23,4)},o.prototype.readDoubleLE=function(e,n){return n||w(e,8,this.length),J.read(this,e,!0,52,8)},o.prototype.readDoubleBE=function(e,n){return n||w(e,8,this.length),J.read(this,e,!1,52,8)},o.prototype.writeUIntLE=function(e,n,f,o){e=+e,n>>>=0,f>>>=0,o||b(this,e,n,f,Math.pow(2,8*f),0);var d=1,i=0;for(this[n]=255&e;++i>>0&255;return n+f},o.prototype.writeUIntBE=function(e,n,f,o){e=+e,n>>>=0,f>>>=0,o||b(this,e,n,f,Math.pow(2,8*f),0);var d=f-1,i=1;for(this[n+d]=255&e;--d>=0&&(i*=256);)this[n+d]=e/i>>>0&255;return n+f},o.prototype.writeUInt8=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,1,255,0),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[n]=e,n+1},o.prototype.writeUInt16LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8):g(this,e,n,!0),n+2},o.prototype.writeUInt16BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>8,this[n+1]=e):g(this,e,n,!1),n+2},o.prototype.writeUInt32LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[n+3]=e>>>24,this[n+2]=e>>>16,this[n+1]=e>>>8,this[n]=e):h(this,e,n,!0),n+4},o.prototype.writeUInt32BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>24,this[n+1]=e>>>16,this[n+2]=e>>>8,this[n+3]=e):h(this,e,n,!1),n+4},o.prototype.writeIntLE=function(e,n,f,o){e=+e,n>>>=0,o||b(this,e,n,f,Math.pow(2,8*f-1)-1,-Math.pow(2,8*f-1));var d=0,i=1,t=0>e?1:0;for(this[n]=255&e;++d>0)-t&255;return n+f},o.prototype.writeIntBE=function(e,n,f,o){e=+e,n>>>=0,o||b(this,e,n,f,Math.pow(2,8*f-1)-1,-Math.pow(2,8*f-1));var d=f-1,i=1,t=0>e?1:0;for(this[n+d]=255&e;--d>=0&&(i*=256);)this[n+d]=(e/i>>0)-t&255;return n+f},o.prototype.writeInt8=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,1,127,-128),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),0>e&&(e=255+e+1),this[n]=e,n+1},o.prototype.writeInt16LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8):g(this,e,n,!0),n+2},o.prototype.writeInt16BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>8,this[n+1]=e):g(this,e,n,!1),n+2},o.prototype.writeInt32LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,2147483647,-2147483648),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8,this[n+2]=e>>>16,this[n+3]=e>>>24):h(this,e,n,!0),n+4},o.prototype.writeInt32BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,2147483647,-2147483648),0>e&&(e=4294967295+e+1),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>24,this[n+1]=e>>>16,this[n+2]=e>>>8,this[n+3]=e):h(this,e,n,!1),n+4},o.prototype.writeFloatLE=function(e,n,f){return j(this,e,n,!0,f)},o.prototype.writeFloatBE=function(e,n,f){return j(this,e,n,!1,f)},o.prototype.writeDoubleLE=function(e,n,f){return k(this,e,n,!0,f)},o.prototype.writeDoubleBE=function(e,n,f){return k(this,e,n,!1,f)},o.prototype.copy=function(e,n,f,d){var i=this;if(f||(f=0),d||0===d||(d=this.length),n>=e.length&&(n=e.length),n||(n=0),d>0&&f>d&&(d=f),d===f)return 0;if(0===e.length||0===i.length)return 0;if(0>n)throw new RangeError("targetStart out of bounds");if(0>f||f>=i.length)throw new RangeError("sourceStart out of bounds");if(0>d)throw new RangeError("sourceEnd out of bounds");d>this.length&&(d=this.length),e.length-nt||!o.TYPED_ARRAY_SUPPORT)for(var l=0;t>l;l++)e[l+n]=this[l+f];else e._set(this.subarray(f,f+t),n);return t},o.prototype.fill=function(e,n,f){if(e||(e=0),n||(n=0),f||(f=this.length),n>f)throw new RangeError("end < start");if(f!==n&&0!==this.length){if(0>n||n>=this.length)throw new RangeError("start out of bounds");if(0>f||f>this.length)throw new RangeError("end out of bounds");var o;if("number"==typeof e)for(o=n;f>o;o++)this[o]=e;else{var d=C(e.toString()),i=d.length;for(o=n;f>o;o++)this[o]=d[o%i]}return this}},o.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(o.TYPED_ARRAY_SUPPORT)return new o(this).buffer;for(var e=new Uint8Array(this.length),n=0,f=e.length;f>n;n+=1)e[n]=this[n];return e.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser")};var N=o.prototype;o._augment=function(e){return e.constructor=o,e._isBuffer=!0,e._get=e.get,e._set=e.set,e.get=N.get,e.set=N.set,e.write=N.write,e.toString=N.toString,e.toLocaleString=N.toString,e.toJSON=N.toJSON,e.equals=N.equals,e.compare=N.compare,e.copy=N.copy,e.slice=N.slice,e.readUIntLE=N.readUIntLE,e.readUIntBE=N.readUIntBE,e.readUInt8=N.readUInt8,e.readUInt16LE=N.readUInt16LE,e.readUInt16BE=N.readUInt16BE,e.readUInt32LE=N.readUInt32LE,e.readUInt32BE=N.readUInt32BE,e.readIntLE=N.readIntLE,e.readIntBE=N.readIntBE,e.readInt8=N.readInt8,e.readInt16LE=N.readInt16LE,e.readInt16BE=N.readInt16BE,e.readInt32LE=N.readInt32LE,e.readInt32BE=N.readInt32BE,e.readFloatLE=N.readFloatLE,e.readFloatBE=N.readFloatBE,e.readDoubleLE=N.readDoubleLE,e.readDoubleBE=N.readDoubleBE,e.writeUInt8=N.writeUInt8,e.writeUIntLE=N.writeUIntLE,e.writeUIntBE=N.writeUIntBE,e.writeUInt16LE=N.writeUInt16LE,e.writeUInt16BE=N.writeUInt16BE,e.writeUInt32LE=N.writeUInt32LE,e.writeUInt32BE=N.writeUInt32BE,e.writeIntLE=N.writeIntLE,e.writeIntBE=N.writeIntBE,e.writeInt8=N.writeInt8,e.writeInt16LE=N.writeInt16LE,e.writeInt16BE=N.writeInt16BE,e.writeInt32LE=N.writeInt32LE,e.writeInt32BE=N.writeInt32BE,e.writeFloatLE=N.writeFloatLE,e.writeFloatBE=N.writeFloatBE,e.writeDoubleLE=N.writeDoubleLE,e.writeDoubleBE=N.writeDoubleBE,e.fill=N.fill,e.inspect=N.inspect,e.toArrayBuffer=N.toArrayBuffer,e};var O=/[^+\/0-9A-z\-]/g},{"base64-js":3,ieee754:4,"is-array":5}],3:[function(e,n,f){var o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function n(e){var n=e.charCodeAt(0);return n===t||n===p?62:n===l||n===c?63:s>n?-1:s+10>n?n-s+26+26:a+26>n?n-a:u+26>n?n-u+26:void 0}function f(e){function f(e){u[p++]=e}var o,d,t,l,s,u;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var a=e.length;s="="===e.charAt(a-2)?2:"="===e.charAt(a-1)?1:0,u=new i(3*e.length/4-s),t=s>0?e.length-4:e.length;var p=0;for(o=0,d=0;t>o;o+=4,d+=3)l=n(e.charAt(o))<<18|n(e.charAt(o+1))<<12|n(e.charAt(o+2))<<6|n(e.charAt(o+3)),f((16711680&l)>>16),f((65280&l)>>8),f(255&l);return 2===s?(l=n(e.charAt(o))<<2|n(e.charAt(o+1))>>4,f(255&l)):1===s&&(l=n(e.charAt(o))<<10|n(e.charAt(o+1))<<4|n(e.charAt(o+2))>>2,f(l>>8&255),f(255&l)),u}function d(e){function n(e){return o.charAt(e)}function f(e){return n(e>>18&63)+n(e>>12&63)+n(e>>6&63)+n(63&e)}var d,i,t,l=e.length%3,s="";for(d=0,t=e.length-l;t>d;d+=3)i=(e[d]<<16)+(e[d+1]<<8)+e[d+2],s+=f(i);switch(l){case 1:i=e[e.length-1],s+=n(i>>2),s+=n(i<<4&63),s+="==";break;case 2:i=(e[e.length-2]<<8)+e[e.length-1],s+=n(i>>10),s+=n(i>>4&63),s+=n(i<<2&63),s+="="}return s}var i="undefined"!=typeof Uint8Array?Uint8Array:Array,t="+".charCodeAt(0),l="/".charCodeAt(0),s="0".charCodeAt(0),u="a".charCodeAt(0),a="A".charCodeAt(0),p="-".charCodeAt(0),c="_".charCodeAt(0);e.toByteArray=f,e.fromByteArray=d}("undefined"==typeof f?this.base64js={}:f)},{}],4:[function(e,n,f){f.read=function(e,n,f,o,d){var i,t,l=8*d-o-1,s=(1<>1,a=-7,p=f?d-1:0,c=f?-1:1,y=e[n+p];for(p+=c,i=y&(1<<-a)-1,y>>=-a,a+=l;a>0;i=256*i+e[n+p],p+=c,a-=8);for(t=i&(1<<-a)-1,i>>=-a,a+=o;a>0;t=256*t+e[n+p],p+=c,a-=8);if(0===i)i=1-u;else{if(i===s)return t?0/0:1/0*(y?-1:1);t+=Math.pow(2,o),i-=u}return(y?-1:1)*t*Math.pow(2,i-o)},f.write=function(e,n,f,o,d,i){var t,l,s,u=8*i-d-1,a=(1<>1,c=23===d?Math.pow(2,-24)-Math.pow(2,-77):0,y=o?0:i-1,m=o?1:-1,r=0>n||0===n&&0>1/n?1:0;for(n=Math.abs(n),isNaN(n)||1/0===n?(l=isNaN(n)?1:0,t=a):(t=Math.floor(Math.log(n)/Math.LN2),n*(s=Math.pow(2,-t))<1&&(t--,s*=2),n+=t+p>=1?c/s:c*Math.pow(2,1-p),n*s>=2&&(t++,s/=2),t+p>=a?(l=0,t=a):t+p>=1?(l=(n*s-1)*Math.pow(2,d),t+=p):(l=n*Math.pow(2,p-1)*Math.pow(2,d),t=0));d>=8;e[f+y]=255&l,y+=m,l/=256,d-=8);for(t=t<0;e[f+y]=255&t,y+=m,t/=256,u-=8);e[f+y-m]|=128*r}},{}],5:[function(e,n){var f=Array.isArray,o=Object.prototype.toString;n.exports=f||function(e){return!!e&&"[object Array]"==o.call(e)}},{}],6:[function(require,module,exports){(function(Buffer){var fabric=fabric||{version:"1.4.11"};"undefined"!=typeof exports&&(exports.fabric=fabric),"undefined"!=typeof document&&"undefined"!=typeof window?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom(""),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode="undefined"!=typeof Buffer&&"undefined"==typeof window,fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width"],fabric.DPI=96;var Cufon=function(){function e(e){var n=this.face=e.face;this.glyphs=e.glyphs,this.w=e.w,this.baseSize=parseInt(n["units-per-em"],10),this.family=n["font-family"].toLowerCase(),this.weight=n["font-weight"],this.style=n["font-style"]||"normal",this.viewBox=function(){var e=n.bbox.split(/\s+/),f={minX:parseInt(e[0],10),minY:parseInt(e[1],10),maxX:parseInt(e[2],10),maxY:parseInt(e[3],10)};return f.width=f.maxX-f.minX,f.height=f.maxY-f.minY,f.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},f}(),this.ascent=-parseInt(n.ascent,10),this.descent=-parseInt(n.descent,10),this.height=-this.ascent+this.descent}function n(){var e={},n={oblique:"italic",italic:"oblique"};this.add=function(n){(e[n.style]||(e[n.style]={}))[n.weight]=n},this.get=function(f,o){var d=e[f]||e[n[f]]||e.normal||e.italic||e.oblique;if(!d)return null;if(o={normal:400,bold:700}[o]||parseInt(o,10),d[o])return d[o];var i,t,l={1:1,99:0}[o%100],s=[];void 0===l&&(l=o>400),500==o&&(o=400);for(var u in d)u=parseInt(u,10),(!i||i>u)&&(i=u),(!t||u>t)&&(t=u),s.push(u);return i>o&&(o=i),o>t&&(o=t),s.sort(function(e,n){return(l?e>o&&n>o?n>e:e>n:o>e&&o>n?e>n:n>e)?-1:1}),d[s[0]]}}function f(){function e(e,n){return e.contains?e.contains(n):16&e.compareDocumentPosition(n)}function n(n){var f=n.relatedTarget;f&&!e(this,f)&&o(this)}function f(){o(this)}function o(e){setTimeout(function(){y.replace(e,w.get(e).options,!0)},10)}this.attach=function(e){void 0===e.onmouseenter?(i(e,"mouseover",n),i(e,"mouseout",n)):(i(e,"mouseenter",f),i(e,"mouseleave",f))}}function o(){function e(e){return e.cufid||(e.cufid=++f)}var n={},f=0;this.get=function(f){var o=e(f);return n[o]||(n[o]={})}}function d(e){var n={},f={};this.get=function(f){return void 0!=n[f]?n[f]:e[f]},this.getSize=function(e,n){return f[e]||(f[e]=new r.Size(this.get(e),n))},this.extend=function(e){for(var f in e)n[f]=e[f];return this}}function i(e,n,f){e.addEventListener?e.addEventListener(n,f,!1):e.attachEvent&&e.attachEvent("on"+n,function(){return f.call(e,fabric.window.event)})}function t(e,n){var f=w.get(e);return f.options?e:(n.hover&&n.hoverables[e.nodeName.toLowerCase()]&&b.attach(e),f.options=n,e)}function l(e){var n={};return function(f){return n.hasOwnProperty(f)||(n[f]=e.apply(null,arguments)),n[f]}}function s(e,n){n||(n=r.getStyle(e));for(var f,o=r.quotedList(n.get("fontFamily").toLowerCase()),d=0,i=o.length;i>d;++d)if(f=o[d],x[f])return x[f].get(n.get("fontStyle"),n.get("fontWeight"));return null}function u(e){return fabric.document.getElementsByTagName(e)}function a(){for(var e,n={},f=0,o=arguments.length;o>f;++f)for(e in arguments[f])n[e]=arguments[f][e];return n}function p(e,n,f,o,d,i){var t=o.separate;if("none"==t)return h[o.engine].apply(null,arguments);var l,s=fabric.document.createDocumentFragment(),u=n.split(k[t]),a="words"==t;a&&v&&(/^\s/.test(n)&&u.unshift(""),/\s$/.test(n)&&u.push(""));for(var p=0,c=u.length;c>p;++p)l=h[o.engine](e,a?r.textAlign(u[p],f,p,c):u[p],f,o,d,i,c-1>p),l&&s.appendChild(l);return s}function c(e,n){for(var f,o,d,i,l=t(e,n).firstChild;l;l=d){if(d=l.nextSibling,i=!1,1==l.nodeType){if(!l.firstChild)continue;if(!/cufon/.test(l.className)){arguments.callee(l,n);continue}i=!0}if(o||(o=r.getStyle(e).extend(n)),f||(f=s(e,o)),f)if(i)h[n.engine](f,null,o,n,l,e);else{var u=l.data;if("undefined"!=typeof G_vmlCanvasManager&&(u=u.replace(/\r/g,"\n")),""!==u){var a=p(f,u,o,n,l,e);a?l.parentNode.replaceChild(a,l):l.parentNode.removeChild(l)}}}}var y=function(){return y.replace.apply(null,arguments)},m=y.DOM={ready:function(){var e=!1,n={loaded:1,complete:1},f=[],o=function(){if(!e){e=!0;for(var n;n=f.shift();n());}};return fabric.document.addEventListener&&(fabric.document.addEventListener("DOMContentLoaded",o,!1),fabric.window.addEventListener("pageshow",o,!1)),!fabric.window.opera&&fabric.document.readyState&&function(){n[fabric.document.readyState]?o():setTimeout(arguments.callee,10)}(),fabric.document.readyState&&fabric.document.createStyleSheet&&function(){try{fabric.document.body.doScroll("left"),o()}catch(e){setTimeout(arguments.callee,1)}}(),i(fabric.window,"load",o),function(n){arguments.length?e?n():f.push(n):o()}}()},r=y.CSS={Size:function(e,n){this.value=parseFloat(e),this.unit=String(e).match(/[a-z%]*$/)[0]||"px",this.convert=function(e){return e/n*this.value},this.convertFrom=function(e){return e/this.value*n},this.toString=function(){return this.value+this.unit}},getStyle:function(e){return new d(e.style)},quotedList:l(function(e){for(var n,f=[],o=/\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g;n=o.exec(e);)f.push(n[3]||n[1]);return f}),ready:function(){var e=!1,n=[],f=function(){e=!0;for(var f;f=n.shift();f());},o=Object.prototype.propertyIsEnumerable?u("style"):{length:0},d=u("link");return m.ready(function(){for(var e,n=0,i=0,t=d.length;e=d[i],t>i;++i)e.disabled||"stylesheet"!=e.rel.toLowerCase()||++n;fabric.document.styleSheets.length>=o.length+n?f():setTimeout(arguments.callee,10)}),function(f){e?f():n.push(f)}}(),supports:function(e,n){var f=fabric.document.createElement("span").style;return void 0===f[e]?!1:(f[e]=n,f[e]===n)},textAlign:function(e,n,f,o){return"right"==n.get("textAlign")?f>0&&(e=" "+e):o-1>f&&(e+=" "),e},textDecoration:function(e,n){n||(n=this.getStyle(e));for(var f={underline:null,overline:null,"line-through":null},o=e;o.parentNode&&1==o.parentNode.nodeType;){var d=!0;for(var i in f)f[i]||(-1!=n.get("textDecoration").indexOf(i)&&(f[i]=n.get("color")),d=!1);if(d)break;n=this.getStyle(o=o.parentNode)}return f},textShadow:l(function(e){if("none"==e)return null;for(var n,f=[],o={},d=0,i=/(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/gi;n=i.exec(e);)","==n[0]?(f.push(o),o={},d=0):n[1]?o.color=n[1]:o[["offX","offY","blur"][d++]]=n[2];return f.push(o),f}),color:l(function(e){var n={};return n.color=e.replace(/^rgba\((.*?),\s*([\d.]+)\)/,function(e,f,o){return n.opacity=parseFloat(o),"rgb("+f+")"}),n}),textTransform:function(e,n){return e[{uppercase:"toUpperCase",lowercase:"toLowerCase"}[n.get("textTransform")]||"toString"]()}},v=0==" ".split(/\s+/).length,w=new o,b=new f,g=[],h={},x={},j={engine:null,hover:!1,hoverables:{a:!0},printable:!0,selector:fabric.window.Sizzle||fabric.window.jQuery&&function(e){return jQuery(e)}||fabric.window.dojo&&dojo.query||fabric.window.$$&&function(e){return $$(e)}||fabric.window.$&&function(e){return $(e)}||fabric.document.querySelectorAll&&function(e){return fabric.document.querySelectorAll(e)}||u,separate:"words",textShadow:"none"},k={words:/\s+/,characters:""};return y.now=function(){return m.ready(),y},y.refresh=function(){for(var e=g.splice(0,g.length),n=0,f=e.length;f>n;++n)y.replace.apply(null,e[n]);return y},y.registerEngine=function(e,n){return n?(h[e]=n,y.set("engine",e)):y},y.registerFont=function(f){var o=new e(f),d=o.family;return x[d]||(x[d]=new n),x[d].add(o),y.set("fontFamily",'"'+d+'"')},y.replace=function(e,n,f){return n=a(j,n),n.engine?("string"==typeof n.textShadow&&n.textShadow&&(n.textShadow=r.textShadow(n.textShadow)),f||g.push(arguments),(e.nodeType||"string"==typeof e)&&(e=[e]),r.ready(function(){for(var f=0,o=e.length;o>f;++f){var d=e[f];"string"==typeof d?y.replace(n.selector(d),n,!0):c(d,n)}}),y):y},y.replaceElement=function(e,n){return n=a(j,n),"string"==typeof n.textShadow&&n.textShadow&&(n.textShadow=r.textShadow(n.textShadow)),c(e,n)},y.engines=h,y.fonts=x,y.getOptions=function(){return a(j)},y.set=function(e,n){return j[e]=n,y},y}();Cufon.registerEngine("canvas",function(){function e(e,n){var f,o=0,d=0,i=[],t=/([mrvxe])([^a-z]*)/g;e:for(var l=0;f=t.exec(e);++l){var s=f[2].split(",");switch(f[1]){case"v":i[l]={m:"bezierCurveTo",a:[o+~~s[0],d+~~s[1],o+~~s[2],d+~~s[3],o+=~~s[4],d+=~~s[5]]};break;case"r":i[l]={m:"lineTo",a:[o+=~~s[0],d+=~~s[1]]};break;case"m":i[l]={m:"moveTo",a:[o=~~s[0],d=~~s[1]]};break;case"x":i[l]={m:"closePath",a:[]};break;case"e":break e}n[i[l].m].apply(n,i[l].a)}return i}function n(e,n){for(var f=0,o=e.length;o>f;++f){var d=e[f];n[d.m].apply(n,d.a)}}var f=Cufon.CSS.supports("display","inline-block"),o=!f&&("BackCompat"==fabric.document.compatMode||/frameset|transitional/i.test(fabric.document.doctype.publicId)),d=fabric.document.createElement("style");d.type="text/css";var i=fabric.document.createTextNode(".cufon-canvas{text-indent:0}@media screen,projection{.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle"+(o?"":";font-size:1px;line-height:1px")+"}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}"+(f?".cufon-canvas canvas{position:relative}":".cufon-canvas canvas{position:absolute}")+"}@media print{.cufon-canvas{padding:0 !important}.cufon-canvas canvas{display:none}.cufon-canvas .cufon-alt{display:inline}}");try{d.appendChild(i)}catch(t){d.setAttribute("type","text/css"),d.styleSheet.cssText=i.data}return fabric.document.getElementsByTagName("head")[0].appendChild(d),function(o,d,i,t,l){function s(){T.save();var e=0,n=0,f=[{left:0}];t.backgroundColor&&(T.save(),T.fillStyle=t.backgroundColor,T.translate(0,o.ascent),T.fillRect(0,0,A+10,(-o.ascent+o.descent)*D),T.restore()),"right"===t.textAlign?(T.translate(G[n],0),f[0].left=G[n]*U):"center"===t.textAlign&&(T.translate(G[n]/2,0),f[0].left=G[n]/2*U);for(var d=0,i=z.length;i>d;++d)if("\n"!==z[d]){var l=o.glyphs[z[d]]||o.missingGlyph;if(l){var s=Number(l.w||o.w)+y;t.textBackgroundColor&&(T.save(),T.fillStyle=t.textBackgroundColor,T.translate(0,o.ascent),T.fillRect(0,0,s+10,-o.ascent+o.descent),T.restore()),T.translate(s,0),e+=s,d==i-1&&(f[f.length-1].width=e*U,f[f.length-1].height=(-o.ascent+o.descent)*U)}}else{n++;var u=-o.ascent-o.ascent/5*t.lineHeight,a=f[f.length-1],p={left:0};a.width=e*U,a.height=(-o.ascent+o.descent)*U,"right"===t.textAlign?(T.translate(-A,u),T.translate(G[n],0),p.left=G[n]*U):"center"===t.textAlign?(T.translate(-e-G[n-1]/2,u),T.translate(G[n]/2,0),p.left=G[n]/2*U):T.translate(-e,u),f.push(p),e=0}T.restore(),Cufon.textOptions.boundaries=f}function u(f){T.fillStyle=f||Cufon.textOptions.color||i.get("color");var d=0,l=0;"right"===t.textAlign?T.translate(G[l],0):"center"===t.textAlign&&T.translate(G[l]/2,0);for(var s=0,u=z.length;u>s;++s)if("\n"!==z[s]){var a=o.glyphs[z[s]]||o.missingGlyph;if(a){var p=Number(a.w||o.w)+y;W&&(T.save(),T.strokeStyle=T.fillStyle,T.lineWidth+=T.lineWidth,T.beginPath(),W.underline&&(T.moveTo(0,-o.face["underline-position"]+.5),T.lineTo(p,-o.face["underline-position"]+.5)),W.overline&&(T.moveTo(0,o.ascent+.5),T.lineTo(p,o.ascent+.5)),W["line-through"]&&(T.moveTo(0,-o.descent+.5),T.lineTo(p,-o.descent+.5)),T.stroke(),T.restore()),X&&(T.save(),T.transform(1,0,-.25,1,0,0)),T.beginPath(),a.d&&(a.code?n(a.code,T):a.code=e("m"+a.d,T)),T.fill(),t.strokeStyle&&(T.closePath(),T.save(),T.lineWidth=t.strokeWidth,T.strokeStyle=t.strokeStyle,T.stroke(),T.restore()),X&&T.restore(),T.translate(p,0),d+=p}}else{l++;var c=-o.ascent-o.ascent/5*t.lineHeight;"right"===t.textAlign?(T.translate(-A,c),T.translate(G[l],0)):"center"===t.textAlign?(T.translate(-d-G[l-1]/2,c),T.translate(G[l]/2,0)):T.translate(-d,c),d=0}}var a=null===d,p=o.viewBox,c=i.getSize("fontSize",o.baseSize),y=i.get("letterSpacing");y="normal"==y?0:c.convertFrom(parseInt(y,10));var m=0,r=0,v=0,w=0,b=t.textShadow,g=[];if(Cufon.textOptions.shadowOffsets=[],Cufon.textOptions.shadows=null,b){Cufon.textOptions.shadows=b;for(var h=0,x=b.length;x>h;++h){var j=b[h],k=c.convertFrom(parseFloat(j.offX)),q=c.convertFrom(parseFloat(j.offY));g[h]=[k,q]}}for(var z=Cufon.CSS.textTransform(a?l.alt:d,i).split(""),A=0,B=null,C=0,D=1,E=[],h=0,x=z.length;x>h;++h)if("\n"!==z[h]){var F=o.glyphs[z[h]]||o.missingGlyph;F&&(A+=B=Number(F.w||o.w)+y)}else D++,A>C&&(C=A),E.push(A),A=0;E.push(A),A=Math.max(C,A);for(var G=[],h=E.length;h--;)G[h]=A-E[h];if(null===B)return null;r+=p.width-B,w+=p.minX;var H,I;if(a)H=l,I=l.firstChild;else if(H=fabric.document.createElement("span"),H.className="cufon cufon-canvas",H.alt=d,I=fabric.document.createElement("canvas"),H.appendChild(I),t.printable){var J=fabric.document.createElement("span"); -J.className="cufon-alt",J.appendChild(fabric.document.createTextNode(d)),H.appendChild(J)}var K=H.style,L=I.style||{},M=c.convert(p.height-m+v),N=Math.ceil(M),O=N/M;I.width=Math.ceil(c.convert(A+r-w)*O),I.height=N,m+=p.minY,L.top=Math.round(c.convert(m-o.ascent))+"px",L.left=Math.round(c.convert(w))+"px";var P=Math.ceil(c.convert(A*O)),Q=P+"px",R=c.convert(o.height),S=(t.lineHeight-1)*c.convert(-o.ascent/5)*(D-1);Cufon.textOptions.width=P,Cufon.textOptions.height=R*D+S,Cufon.textOptions.lines=D,Cufon.textOptions.totalLineHeight=S,f?(K.width=Q,K.height=R+"px"):(K.paddingLeft=Q,K.paddingBottom=R-1+"px");var T=Cufon.textOptions.context||I.getContext("2d"),U=N/p.height;Cufon.textOptions.fontAscent=o.ascent*U,Cufon.textOptions.boundaries=null;for(var V=Cufon.textOptions.shadowOffsets,h=g.length;h--;)V[h]=[g[h][0]*U,g[h][1]*U];T.save(),T.scale(U,U),T.translate(-w-1/U*I.width/2+(Cufon.fonts[o.family].offsetLeft||0),-m-Cufon.textOptions.height/U/2+(Cufon.fonts[o.family].offsetTop||0)),T.lineWidth=o.face["underline-thickness"],T.save();var W=Cufon.getTextDecoration(t),X="italic"===t.fontStyle;if(T.save(),s(),b)for(var h=0,x=b.length;x>h;++h){var j=b[h];T.save(),T.translate.apply(T,g[h]),u(j.color),T.restore()}return u(),T.restore(),T.restore(),T.restore(),H}}()),Cufon.registerEngine("vml",function(){function e(e,f){return n(e,/(?:em|ex|%)$/i.test(f)?"1em":f)}function n(e,n){if(/px$/i.test(n))return parseFloat(n);var f=e.style.left,o=e.runtimeStyle.left;e.runtimeStyle.left=e.currentStyle.left,e.style.left=n;var d=e.style.pixelLeft;return e.style.left=f,e.runtimeStyle.left=o,d}if(fabric.document.namespaces){var f=fabric.document.createElement("canvas");if(!(f&&f.getContext&&f.getContext.apply)){null==fabric.document.namespaces.cvml&&fabric.document.namespaces.add("cvml","urn:schemas-microsoft-com:vml");var o=fabric.document.createElement("cvml:shape");if(o.style.behavior="url(#default#VML)",o.coordsize)return o=null,fabric.document.write(''),function(f,o,d,i,t,l,s){var u=null===o;u&&(o=t.alt);var a=f.viewBox,p=d.computedFontSize||(d.computedFontSize=new Cufon.CSS.Size(e(l,d.get("fontSize"))+"px",f.baseSize)),c=d.computedLSpacing;void 0==c&&(c=d.get("letterSpacing"),d.computedLSpacing=c="normal"==c?0:~~p.convertFrom(n(l,c)));var y,m;if(u)y=t,m=t.firstChild;else{if(y=fabric.document.createElement("span"),y.className="cufon cufon-vml",y.alt=o,m=fabric.document.createElement("span"),m.className="cufon-vml-canvas",y.appendChild(m),i.printable){var r=fabric.document.createElement("span");r.className="cufon-alt",r.appendChild(fabric.document.createTextNode(o)),y.appendChild(r)}s||y.appendChild(fabric.document.createElement("cvml:shape"))}var v=y.style,w=m.style,b=p.convert(a.height),g=Math.ceil(b),h=g/b,x=a.minX,j=a.minY;w.height=g,w.top=Math.round(p.convert(j-f.ascent)),w.left=Math.round(p.convert(x)),v.height=p.convert(f.height)+"px";for(var k,q,z=(Cufon.getTextDecoration(i),d.get("color")),A=Cufon.CSS.textTransform(o,d).split(""),B=0,C=0,D=null,E=i.textShadow,F=0,G=0,H=A.length;H>F;++F)k=f.glyphs[A[F]]||f.missingGlyph,k&&(B+=D=~~(k.w||f.w)+c);if(null===D)return null;var I,J=-x+B+(a.width-D),K=p.convert(J*h),L=Math.round(K),M=J+","+a.height,N="r"+M+"nsnf";for(F=0;H>F;++F)if(k=f.glyphs[A[F]]||f.missingGlyph){u?(q=m.childNodes[G],q.firstChild&&q.removeChild(q.firstChild)):(q=fabric.document.createElement("cvml:shape"),m.appendChild(q)),q.stroked="f",q.coordsize=M,q.coordorigin=I=x-C+","+j,q.path=(k.d?"m"+k.d+"xe":"")+"m"+I+N,q.fillcolor=z;var O=q.style;if(O.width=L,O.height=g,E){var P,Q=E[0],R=E[1],S=Cufon.CSS.color(Q.color),T=fabric.document.createElement("cvml:shadow");T.on="t",T.color=S.color,T.offset=Q.offX+","+Q.offY,R&&(P=Cufon.CSS.color(R.color),T.type="double",T.color2=P.color,T.offset2=R.offX+","+R.offY),T.opacity=S.opacity||P&&P.opacity||1,q.appendChild(T)}C+=~~(k.w||f.w)+c,++G}return v.width=Math.max(Math.ceil(p.convert(B*h)),0),y}}}}()),Cufon.getTextDecoration=function(e){return{underline:"underline"===e.textDecoration,overline:"overline"===e.textDecoration,"line-through":"line-through"===e.textDecoration}},"undefined"!=typeof exports&&(exports.Cufon=Cufon),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(e){return 10>e?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var n=meta[e];return"string"==typeof n?n:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,n){var f,o,d,i,t,l=gap,s=n[e];switch(s&&"object"==typeof s&&"function"==typeof s.toJSON&&(s=s.toJSON(e)),"function"==typeof rep&&(s=rep.call(n,e,s)),typeof s){case"string":return quote(s);case"number":return isFinite(s)?String(s):"null";case"boolean":case"null":return String(s);case"object":if(!s)return"null";if(gap+=indent,t=[],"[object Array]"===Object.prototype.toString.apply(s)){for(i=s.length,f=0;i>f;f+=1)t[f]=str(f,s)||"null";return d=0===t.length?"[]":gap?"[\n"+gap+t.join(",\n"+gap)+"\n"+l+"]":"["+t.join(",")+"]",gap=l,d}if(rep&&"object"==typeof rep)for(i=rep.length,f=0;i>f;f+=1)"string"==typeof rep[f]&&(o=rep[f],d=str(o,s),d&&t.push(quote(o)+(gap?": ":":")+d));else for(o in s)Object.prototype.hasOwnProperty.call(s,o)&&(d=str(o,s),d&&t.push(quote(o)+(gap?": ":":")+d));return d=0===t.length?"{}":gap?"{\n"+gap+t.join(",\n"+gap)+"\n"+l+"}":"{"+t.join(",")+"}",gap=l,d}}"function"!=typeof Date.prototype.toJSON&&(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"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(e,n,f){var o;if(gap="",indent="","number"==typeof f)for(o=0;f>o;o+=1)indent+=" ";else"string"==typeof f&&(indent=f);if(rep=n,n&&"function"!=typeof n&&("object"!=typeof n||"number"!=typeof n.length))throw new Error("JSON.stringify");return str("",{"":e})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){function walk(e,n){var f,o,d=e[n];if(d&&"object"==typeof d)for(f in d)Object.prototype.hasOwnProperty.call(d,f)&&(o=walk(d,f),void 0!==o?d[f]=o:delete d[f]);return reviver.call(e,n,d)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\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,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),function(){function e(e,n){this.__eventListeners[e]&&(n?fabric.util.removeFromArray(this.__eventListeners[e],n):this.__eventListeners[e].length=0)}function n(e,n){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var f in e)this.on(f,e[f]);else this.__eventListeners[e]||(this.__eventListeners[e]=[]),this.__eventListeners[e].push(n);return this}function f(n,f){if(this.__eventListeners){if(0===arguments.length)this.__eventListeners={};else if(1===arguments.length&&"object"==typeof arguments[0])for(var o in n)e.call(this,o,n[o]);else e.call(this,n,f);return this}}function o(e,n){if(this.__eventListeners){var f=this.__eventListeners[e];if(f){for(var o=0,d=f.length;d>o;o++)f[o].call(this,n||{});return this}}}fabric.Observable={observe:n,stopObserving:f,fire:o,on:n,off:f,trigger:o}}(),fabric.Collection={add:function(){this._objects.push.apply(this._objects,arguments);for(var e=0,n=arguments.length;n>e;e++)this._onObjectAdded(arguments[e]);return this.renderOnAddRemove&&this.renderAll(),this},insertAt:function(e,n,f){var o=this.getObjects();return f?o[n]=e:o.splice(n,0,e),this._onObjectAdded(e),this.renderOnAddRemove&&this.renderAll(),this},remove:function(){for(var e,n=this.getObjects(),f=0,o=arguments.length;o>f;f++)e=n.indexOf(arguments[f]),-1!==e&&(n.splice(e,1),this._onObjectRemoved(arguments[f]));return this.renderOnAddRemove&&this.renderAll(),this},forEachObject:function(e,n){for(var f=this.getObjects(),o=f.length;o--;)e.call(n,f[o],o,f);return this},getObjects:function(e){return"undefined"==typeof e?this._objects:this._objects.filter(function(n){return n.type===e})},item:function(e){return this.getObjects()[e]},isEmpty:function(){return 0===this.getObjects().length},size:function(){return this.getObjects().length},contains:function(e){return this.getObjects().indexOf(e)>-1},complexity:function(){return this.getObjects().reduce(function(e,n){return e+=n.complexity?n.complexity():0},0)}},function(e){var n=Math.sqrt,f=Math.atan2,o=Math.PI/180;fabric.util={removeFromArray:function(e,n){var f=e.indexOf(n);return-1!==f&&e.splice(f,1),e},getRandomInt:function(e,n){return Math.floor(Math.random()*(n-e+1))+e},degreesToRadians:function(e){return e*o},radiansToDegrees:function(e){return e/o},rotatePoint:function(e,n,f){var o=Math.sin(f),d=Math.cos(f);e.subtractEquals(n);var i=e.x*d-e.y*o,t=e.x*o+e.y*d;return new fabric.Point(i,t).addEquals(n)},transformPoint:function(e,n,f){return f?new fabric.Point(n[0]*e.x+n[1]*e.y,n[2]*e.x+n[3]*e.y):new fabric.Point(n[0]*e.x+n[1]*e.y+n[4],n[2]*e.x+n[3]*e.y+n[5])},invertTransform:function(e){var n=e.slice(),f=1/(e[0]*e[3]-e[1]*e[2]);n=[f*e[3],-f*e[1],-f*e[2],f*e[0],0,0];var o=fabric.util.transformPoint({x:e[4],y:e[5]},n);return n[4]=-o.x,n[5]=-o.y,n},toFixed:function(e,n){return parseFloat(Number(e).toFixed(n))},parseUnit:function(e){var n=/\D{0,2}$/.exec(e),f=parseFloat(e);switch(n[0]){case"mm":return f*fabric.DPI/25.4;case"cm":return f*fabric.DPI/2.54;case"in":return f*fabric.DPI;case"pt":return f*fabric.DPI/72;case"pc":return f*fabric.DPI/72*12;default:return f}},falseFunction:function(){return!1},getKlass:function(e,n){return e=fabric.util.string.camelize(e.charAt(0).toUpperCase()+e.slice(1)),fabric.util.resolveNamespace(n)[e]},resolveNamespace:function(n){if(!n)return fabric;for(var f=n.split("."),o=f.length,d=e||fabric.window,i=0;o>i;++i)d=d[f[i]];return d},loadImage:function(e,n,f,o){if(!e)return void(n&&n.call(f,e));var d=fabric.util.createImage();d.onload=function(){n&&n.call(f,d),d=d.onload=d.onerror=null},d.onerror=function(){fabric.log("Error loading "+d.src),n&&n.call(f,null,!0),d=d.onload=d.onerror=null},0!==e.indexOf("data")&&"undefined"!=typeof o&&(d.crossOrigin=o),d.src=e},enlivenObjects:function(e,n,f,o){function d(){++t===l&&n&&n(i)}e=e||[];var i=[],t=0,l=e.length;return l?void e.forEach(function(e,n){if(!e||!e.type)return void d();var t=fabric.util.getKlass(e.type,f);t.async?t.fromObject(e,function(f,t){t||(i[n]=f,o&&o(e,i[n])),d()}):(i[n]=t.fromObject(e),o&&o(e,i[n]),d())}):void(n&&n(i))},groupSVGElements:function(e,n,f){var o;return o=new fabric.PathGroup(e,n),"undefined"!=typeof f&&o.setSourcePath(f),o},populateWithProperties:function(e,n,f){if(f&&"[object Array]"===Object.prototype.toString.call(f))for(var o=0,d=f.length;d>o;o++)f[o]in e&&(n[f[o]]=e[f[o]])},drawDashedLine:function(e,o,d,i,t,l){var s=i-o,u=t-d,a=n(s*s+u*u),p=f(u,s),c=l.length,y=0,m=!0;for(e.save(),e.translate(o,d),e.moveTo(0,0),e.rotate(p),o=0;a>o;)o+=l[y++%c],o>a&&(o=a),e[m?"lineTo":"moveTo"](o,0),m=!m;e.restore()},createCanvasElement:function(e){return e||(e=fabric.document.createElement("canvas")),e.getContext||"undefined"==typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(e),e},createImage:function(){return fabric.isLikelyNode?new(require("canvas").Image):fabric.document.createElement("img")},createAccessors:function(e){for(var n=e.prototype,f=n.stateProperties.length;f--;){var o=n.stateProperties[f],d=o.charAt(0).toUpperCase()+o.slice(1),i="set"+d,t="get"+d;n[t]||(n[t]=function(e){return new Function('return this.get("'+e+'")')}(o)),n[i]||(n[i]=function(e){return new Function("value",'return this.set("'+e+'", value)')}(o))}},clipContext:function(e,n){n.save(),n.beginPath(),e.clipTo(n),n.clip()},multiplyTransformMatrices:function(e,n){for(var f=[[e[0],e[2],e[4]],[e[1],e[3],e[5]],[0,0,1]],o=[[n[0],n[2],n[4]],[n[1],n[3],n[5]],[0,0,1]],d=[],i=0;3>i;i++){d[i]=[];for(var t=0;3>t;t++){for(var l=0,s=0;3>s;s++)l+=f[i][s]*o[s][t];d[i][t]=l}}return[d[0][0],d[1][0],d[0][1],d[1][1],d[0][2],d[1][2]]},getFunctionBody:function(e){return(String(e).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},isTransparent:function(e,n,f,o){o>0&&(n>o?n-=o:n=0,f>o?f-=o:f=0);for(var d=!0,i=e.getImageData(n,f,2*o||1,2*o||1),t=3,l=i.data.length;l>t;t+=4){var s=i.data[t];if(d=0>=s,d===!1)break}return i=null,d}}}("undefined"!=typeof exports?exports:this),function(){function e(e,d,t,l,s,u,a){var p=i.call(arguments);if(o[p])return o[p];var c=Math.PI,y=a*(c/180),m=Math.sin(y),r=Math.cos(y),v=0,w=0;t=Math.abs(t),l=Math.abs(l);var b=-r*e-m*d,g=-r*d+m*e,h=t*t,x=l*l,j=g*g,k=b*b,q=4*h*x-h*j-x*k,z=0;if(0>q){var A=Math.sqrt(1-.25*q/(h*x));t*=A,l*=A}else z=(s===u?-.5:.5)*Math.sqrt(q/(h*j+x*k));var B=z*t*g/l,C=-z*l*b/t,D=r*B-m*C+e/2,E=m*B+r*C+d/2,F=f(1,0,(b-B)/t,(g-C)/l),G=f((b-B)/t,(g-C)/l,(-b-B)/t,(-g-C)/l);0===u&&G>0?G-=2*c:1===u&&0>G&&(G+=2*c);for(var H=Math.ceil(Math.abs(G/(.5*c))),I=[],J=G/H,K=8/3*Math.sin(J/4)*Math.sin(J/4)/Math.sin(J/2),L=F+J,M=0;H>M;M++)I[M]=n(F,L,r,m,t,l,D,E,K,v,w),v=I[M][4],w=I[M][5],F+=J,L+=J;return o[p]=I,I}function n(e,n,f,o,t,l,s,u,a,p,c){var y=i.call(arguments);if(d[y])return d[y];var m=Math.cos(e),r=Math.sin(e),v=Math.cos(n),w=Math.sin(n),b=f*t*v-o*l*w+s,g=o*t*v+f*l*w+u,h=p+a*(-f*t*r-o*l*m),x=c+a*(-o*t*r+f*l*m),j=b+a*(f*t*w+o*l*v),k=g+a*(o*t*w-f*l*v);return d[y]=[h,x,j,k,b,g],d[y]}function f(e,n,f,o){var d=Math.atan2(n,e),i=Math.atan2(o,f);return i>=d?i-d:2*Math.PI-(d-i)}var o={},d={},i=Array.prototype.join;fabric.util.drawArc=function(n,f,o,d){for(var i=d[0],t=d[1],l=d[2],s=d[3],u=d[4],a=d[5],p=d[6],c=[[],[],[],[]],y=e(a-f,p-o,i,t,s,u,l),m=0,r=y.length;r>m;m++)c[m][0]=y[m][0]+f,c[m][1]=y[m][1]+o,c[m][2]=y[m][2]+f,c[m][3]=y[m][3]+o,c[m][4]=y[m][4]+f,c[m][5]=y[m][5]+o,n.bezierCurveTo.apply(n,c[m])}}(),function(){function e(e,n){for(var f=d.call(arguments,2),o=[],i=0,t=e.length;t>i;i++)o[i]=f.length?e[i][n].apply(e[i],f):e[i][n].call(e[i]);return o}function n(e,n){return o(e,n,function(e,n){return e>=n})}function f(e,n){return o(e,n,function(e,n){return n>e})}function o(e,n,f){if(e&&0!==e.length){var o=e.length-1,d=n?e[o][n]:e[o];if(n)for(;o--;)f(e[o][n],d)&&(d=e[o][n]);else for(;o--;)f(e[o],d)&&(d=e[o]);return d}}var d=Array.prototype.slice;fabric.util.array={invoke:e,min:f,max:n}}(),function(){function e(e,n){for(var f in n)e[f]=n[f];return e}function n(n){return e({},n)}fabric.util.object={extend:e,clone:n}}(),function(){function e(e){return e.replace(/-+(.)?/g,function(e,n){return n?n.toUpperCase():""})}function n(e,n){return e.charAt(0).toUpperCase()+(n?e.slice(1):e.slice(1).toLowerCase())}function f(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//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("',"Created with Fabric.js ",fabric.version,"","",fabric.createSVGFontFacesMarkup(this.getObjects()),fabric.createSVGRefElementsMarkup(this),"")},_setSVGObjects:function(e,n){var f=this.getActiveGroup();f&&this.discardActiveGroup();for(var o=0,d=this.getObjects(),i=d.length;i>o;o++)e.push(d[o].toSVG(n));f&&(this.setActiveGroup(new fabric.Group(f.getObjects())),f.forEachObject(function(e){e.set("active",!0)}))},_setSVGBgOverlayImage:function(e,n){this[n]&&this[n].toSVG&&e.push(this[n].toSVG())},_setSVGBgOverlayColor:function(e,n){this[n]&&this[n].source?e.push('"):this[n]&&"overlayColor"===n&&e.push('")},sendToBack:function(e){return f(this._objects,e),this._objects.unshift(e),this.renderAll&&this.renderAll()},bringToFront:function(e){return f(this._objects,e),this._objects.push(e),this.renderAll&&this.renderAll()},sendBackwards:function(e,n){var o=this._objects.indexOf(e);if(0!==o){var d=this._findNewLowerIndex(e,o,n);f(this._objects,e),this._objects.splice(d,0,e),this.renderAll&&this.renderAll()}return this},_findNewLowerIndex:function(e,n,f){var o;if(f){o=n;for(var d=n-1;d>=0;--d){var i=e.intersectsWithObject(this._objects[d])||e.isContainedWithinObject(this._objects[d])||this._objects[d].isContainedWithinObject(e);if(i){o=d;break}}}else o=n-1;return o},bringForward:function(e,n){var o=this._objects.indexOf(e);if(o!==this._objects.length-1){var d=this._findNewUpperIndex(e,o,n);f(this._objects,e),this._objects.splice(d,0,e),this.renderAll&&this.renderAll()}return this},_findNewUpperIndex:function(e,n,f){var o;if(f){o=n;for(var d=n+1;d"}}),e(fabric.StaticCanvas.prototype,fabric.Observable),e(fabric.StaticCanvas.prototype,fabric.Collection),e(fabric.StaticCanvas.prototype,fabric.DataURLExporter),e(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(e){var n=fabric.util.createCanvasElement();if(!n||!n.getContext)return null;var f=n.getContext("2d");if(!f)return null;switch(e){case"getImageData":return"undefined"!=typeof f.getImageData;case"setLineDash":return"undefined"!=typeof f.setLineDash;case"toDataURL":return"undefined"!=typeof n.toDataURL;case"toDataURLWithQuality":try{return n.toDataURL("image/jpeg",0),!0}catch(o){}return!1;default:return null}}}),fabric.StaticCanvas.prototype.toJSON=fabric.StaticCanvas.prototype.toObject}(),fabric.BaseBrush=fabric.util.createClass({color:"rgb(0, 0, 0)",width:1,shadow:null,strokeLineCap:"round",strokeLineJoin:"round",setShadow:function(e){return this.shadow=new fabric.Shadow(e),this},_setBrushStyles:function(){var e=this.canvas.contextTop;e.strokeStyle=this.color,e.lineWidth=this.width,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin},_setShadow:function(){if(this.shadow){var e=this.canvas.contextTop;e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY}},_resetShadow:function(){var e=this.canvas.contextTop;e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0}}),function(){var e=fabric.util.array.min,n=fabric.util.array.max;fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{initialize:function(e){this.canvas=e,this._points=[]},onMouseDown:function(e){this._prepareForDrawing(e),this._captureDrawingPath(e),this._render()},onMouseMove:function(e){this._captureDrawingPath(e),this.canvas.clearContext(this.canvas.contextTop),this._render()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(e){var n=new fabric.Point(e.x,e.y);this._reset(),this._addPoint(n),this.canvas.contextTop.moveTo(n.x,n.y)},_addPoint:function(e){this._points.push(e)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(e){var n=new fabric.Point(e.x,e.y);this._addPoint(n)},_render:function(){var e=this.canvas.contextTop,n=this.canvas.viewportTransform,f=this._points[0],o=this._points[1];e.save(),e.transform(n[0],n[1],n[2],n[3],n[4],n[5]),e.beginPath(),2===this._points.length&&f.x===o.x&&f.y===o.y&&(f.x-=.5,o.x+=.5),e.moveTo(f.x,f.y);for(var d=1,i=this._points.length;i>d;d++){var t=f.midPointFrom(o);e.quadraticCurveTo(f.x,f.y,t.x,t.y),f=this._points[d],o=this._points[d+1]}e.lineTo(f.x,f.y),e.stroke(),e.restore()},_getSVGPathData:function(){return this.box=this.getPathBoundingBox(this._points),this.convertPointsToSVGPath(this._points,this.box.minX,this.box.minY)},getPathBoundingBox:function(f){for(var o=[],d=[],i=f[0],t=f[1],l=i,s=1,u=f.length;u>s;s++){var a=i.midPointFrom(t);o.push(l.x),o.push(a.x),d.push(l.y),d.push(a.y),i=f[s],t=f[s+1],l=a}return o.push(i.x),d.push(i.y),{minX:e(o),minY:e(d),maxX:n(o),maxY:n(d)}},convertPointsToSVGPath:function(e,n,f){var o=[],d=new fabric.Point(e[0].x-n,e[0].y-f),i=new fabric.Point(e[1].x-n,e[1].y-f);o.push("M ",e[0].x-n," ",e[0].y-f," ");for(var t=1,l=e.length;l>t;t++){var s=d.midPointFrom(i);o.push("Q ",d.x," ",d.y," ",s.x," ",s.y," "),d=new fabric.Point(e[t].x-n,e[t].y-f),t+1f;f++){var d=this.points[f],i=new fabric.Circle({radius:d.radius,left:d.x,top:d.y,originX:"center",originY:"center",fill:d.fill});this.shadow&&i.setShadow(this.shadow),n.push(i)}var t=new fabric.Group(n,{originX:"center",originY:"center"});t.canvas=this.canvas,this.canvas.add(t),this.canvas.fire("path:created",{path:t}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=e,this.canvas.renderAll()},addPoint:function(e){var n=new fabric.Point(e.x,e.y),f=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,o=new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return n.radius=f,n.fill=o,this.points.push(n),n}}),fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(e){this.canvas=e,this.sprayChunks=[]},onMouseDown:function(e){this.sprayChunks.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.addSprayChunk(e),this.render()},onMouseMove:function(e){this.addSprayChunk(e),this.render()},onMouseUp:function(){var e=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var n=[],f=0,o=this.sprayChunks.length;o>f;f++)for(var d=this.sprayChunks[f],i=0,t=d.length;t>i;i++){var l=new fabric.Rect({width:d[i].width,height:d[i].width,left:d[i].x+1,top:d[i].y+1,originX:"center",originY:"center",fill:this.color});this.shadow&&l.setShadow(this.shadow),n.push(l)}this.optimizeOverlapping&&(n=this._getOptimizedRects(n));var s=new fabric.Group(n,{originX:"center",originY:"center"});s.canvas=this.canvas,this.canvas.add(s),this.canvas.fire("path:created",{path:s}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=e,this.canvas.renderAll()},_getOptimizedRects:function(e){for(var n,f={},o=0,d=e.length;d>o;o++)n=e[o].left+""+e[o].top,f[n]||(f[n]=e[o]);var i=[];for(n in f)i.push(f[n]);return i},render:function(){var e=this.canvas.contextTop;e.fillStyle=this.color;var n=this.canvas.viewportTransform;e.save(),e.transform(n[0],n[1],n[2],n[3],n[4],n[5]);for(var f=0,o=this.sprayChunkPoints.length;o>f;f++){var d=this.sprayChunkPoints[f];"undefined"!=typeof d.opacity&&(e.globalAlpha=d.opacity),e.fillRect(d.x,d.y,d.width,d.width)}e.restore()},addSprayChunk:function(e){this.sprayChunkPoints=[];for(var n,f,o,d=this.width/2,i=0;i1&&this.setWidth(t).setHeight(l),a.scale(o,o),f.left&&(f.left*=o),f.top&&(f.top*=o),f.width?f.width*=o:1>o&&(f.width=t),f.height?f.height*=o:1>o&&(f.height=l),u?this._tempRemoveBordersControlsFromGroup(u):s&&this.deactivateAll&&this.deactivateAll(),this.renderAll(!0);var p=this.__toDataURL(e,n,f);return this.width=d,this.height=i,a.scale(1/o,1/o),this.setWidth(d).setHeight(i),u?this._restoreBordersControlsOnGroup(u):s&&this.setActiveObject&&this.setActiveObject(s),this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),p},toDataURLWithMultiplier:function(e,n,f){return this.toDataURL({format:e,multiplier:n,quality:f})},_tempRemoveBordersControlsFromGroup:function(e){e.origHasControls=e.hasControls,e.origBorderColor=e.borderColor,e.hasControls=!0,e.borderColor="rgba(0,0,0,0)",e.forEachObject(function(e){e.origBorderColor=e.borderColor,e.borderColor="rgba(0,0,0,0)"})},_restoreBordersControlsOnGroup:function(e){e.hideControls=e.origHideControls,e.borderColor=e.origBorderColor,e.forEachObject(function(e){e.borderColor=e.origBorderColor,delete e.origBorderColor})}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{loadFromDatalessJSON:function(e,n,f){return this.loadFromJSON(e,n,f)},loadFromJSON:function(e,n,f){if(e){var o="string"==typeof e?JSON.parse(e):e;this.clear();var d=this;return this._enlivenObjects(o.objects,function(){d._setBgOverlay(o,n)},f),this}},_setBgOverlay:function(e,n){var f=this,o={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!(e.backgroundImage||e.overlayImage||e.background||e.overlay))return void(n&&n());var d=function(){o.backgroundImage&&o.overlayImage&&o.backgroundColor&&o.overlayColor&&(f.renderAll(),n&&n())};this.__setBgOverlay("backgroundImage",e.backgroundImage,o,d),this.__setBgOverlay("overlayImage",e.overlayImage,o,d),this.__setBgOverlay("backgroundColor",e.background,o,d),this.__setBgOverlay("overlayColor",e.overlay,o,d),d()},__setBgOverlay:function(e,n,f,o){var d=this;return n?void("backgroundImage"===e||"overlayImage"===e?fabric.Image.fromObject(n,function(n){d[e]=n,f[e]=!0,o&&o()}):this["set"+fabric.util.string.capitalize(e,!0)](n,function(){f[e]=!0,o&&o()})):void(f[e]=!0)},_enlivenObjects:function(e,n,f){var o=this;if(!e||0===e.length)return void(n&&n());var d=this.renderOnAddRemove;this.renderOnAddRemove=!1,fabric.util.enlivenObjects(e,function(e){e.forEach(function(e,n){o.insertAt(e,n,!0)}),o.renderOnAddRemove=d,n&&n()},null,f)},_toDataURL:function(e,n){this.clone(function(f){n(f.toDataURL(e))})},_toDataURLWithMultiplier:function(e,n,f){this.clone(function(o){f(o.toDataURLWithMultiplier(e,n))})},clone:function(e,n){var f=JSON.stringify(this.toJSON(n));this.cloneWithoutData(function(n){n.loadFromJSON(f,function(){e&&e(n)})})},cloneWithoutData:function(e){var n=fabric.document.createElement("canvas");n.width=this.getWidth(),n.height=this.getHeight();var f=new fabric.Canvas(n);f.clipTo=this.clipTo,this.backgroundImage?(f.setBackgroundImage(this.backgroundImage.src,function(){f.renderAll(),e&&e(f)}),f.backgroundImageOpacity=this.backgroundImageOpacity,f.backgroundImageStretch=this.backgroundImageStretch):e&&e(f)}}),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.toFixed,d=n.util.string.capitalize,i=n.util.degreesToRadians,t=n.StaticCanvas.supports("setLineDash");n.Object||(n.Object=n.util.createClass({type:"object",originX:"left",originY:"top",top:0,left:0,width:0,height:0,scaleX:1,scaleY:1,flipX:!1,flipY:!1,opacity:1,angle:0,cornerSize:12,transparentCorners:!0,hoverCursor:null,padding:0,borderColor:"rgba(102,153,255,0.75)",cornerColor:"rgba(102,153,255,0.5)",centeredScaling:!1,centeredRotation:!0,fill:"rgb(0,0,0)",fillRule:"source-over",backgroundColor:"",stroke:null,strokeWidth:1,strokeDashArray:null,strokeLineCap:"butt",strokeLineJoin:"miter",strokeMiterLimit:10,shadow:null,borderOpacityWhenMoving:.4,borderScaleFactor:1,transformMatrix:null,minScaleLimit:.01,selectable:!0,evented:!0,visible:!0,hasControls:!0,hasBorders:!0,hasRotatingPoint:!0,rotatingPointOffset:40,perPixelTargetFind:!1,includeDefaultValues:!0,clipTo:null,lockMovementX:!1,lockMovementY:!1,lockRotation:!1,lockScalingX:!1,lockScalingY:!1,lockUniScaling:!1,lockScalingFlip:!1,stateProperties:"top left width height scaleX scaleY flipX flipY originX originY transformMatrix stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit angle opacity fill fillRule shadow clipTo visible backgroundColor".split(" "),initialize:function(e){e&&this.setOptions(e)},_initGradient:function(e){!e.fill||!e.fill.colorStops||e.fill instanceof n.Gradient||this.set("fill",new n.Gradient(e.fill))},_initPattern:function(e){!e.fill||!e.fill.source||e.fill instanceof n.Pattern||this.set("fill",new n.Pattern(e.fill)),!e.stroke||!e.stroke.source||e.stroke instanceof n.Pattern||this.set("stroke",new n.Pattern(e.stroke))},_initClipping:function(e){if(e.clipTo&&"string"==typeof e.clipTo){var f=n.util.getFunctionBody(e.clipTo);"undefined"!=typeof f&&(this.clipTo=new Function("ctx",f))}},setOptions:function(e){for(var n in e)this.set(n,e[n]);this._initGradient(e),this._initPattern(e),this._initClipping(e)},transform:function(e,n){this.group&&this.group.transform(e,n),e.globalAlpha=this.opacity;var f=n?this._getLeftTopCoords():this.getCenterPoint();e.translate(f.x,f.y),e.rotate(i(this.angle)),e.scale(this.scaleX*(this.flipX?-1:1),this.scaleY*(this.flipY?-1:1))},toObject:function(e){var f=n.Object.NUM_FRACTION_DIGITS,d={type:this.type,originX:this.originX,originY:this.originY,left:o(this.left,f),top:o(this.top,f),width:o(this.width,f),height:o(this.height,f),fill:this.fill&&this.fill.toObject?this.fill.toObject():this.fill,stroke:this.stroke&&this.stroke.toObject?this.stroke.toObject():this.stroke,strokeWidth:o(this.strokeWidth,f),strokeDashArray:this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeLineJoin:this.strokeLineJoin,strokeMiterLimit:o(this.strokeMiterLimit,f),scaleX:o(this.scaleX,f),scaleY:o(this.scaleY,f),angle:o(this.getAngle(),f),flipX:this.flipX,flipY:this.flipY,opacity:o(this.opacity,f),shadow:this.shadow&&this.shadow.toObject?this.shadow.toObject():this.shadow,visible:this.visible,clipTo:this.clipTo&&String(this.clipTo),backgroundColor:this.backgroundColor};return this.includeDefaultValues||(d=this._removeDefaultValues(d)),n.util.populateWithProperties(this,d,e),d},toDatalessObject:function(e){return this.toObject(e)},_removeDefaultValues:function(e){var f=n.util.getKlass(e.type).prototype,o=f.stateProperties;return o.forEach(function(n){e[n]===f[n]&&delete e[n]}),e},toString:function(){return"#"},get:function(e){return this[e]},_setObject:function(e){for(var n in e)this._set(n,e[n])},set:function(e,n){return"object"==typeof e?this._setObject(e):"function"==typeof n&&"clipTo"!==e?this._set(e,n(this.get(e))):this._set(e,n),this},_set:function(e,f){var d="scaleX"===e||"scaleY"===e;return d&&(f=this._constrainScale(f)),"scaleX"===e&&0>f?(this.flipX=!this.flipX,f*=-1):"scaleY"===e&&0>f?(this.flipY=!this.flipY,f*=-1):"width"===e||"height"===e?this.minScaleLimit=o(Math.min(.1,1/Math.max(this.width,this.height)),2):"shadow"!==e||!f||f instanceof n.Shadow||(f=new n.Shadow(f)),this[e]=f,this},toggle:function(e){var n=this.get(e);return"boolean"==typeof n&&this.set(e,!n),this},setSourcePath:function(e){return this.sourcePath=e,this},getViewportTransform:function(){return this.canvas&&this.canvas.viewportTransform?this.canvas.viewportTransform:[1,0,0,1,0,0]},render:function(e,f){if(0!==this.width&&0!==this.height&&this.visible){if(e.save(),this._setupFillRule(e),this._transform(e,f),this._setStrokeStyles(e),this._setFillStyles(e),this.group&&"path-group"===this.group.type){e.translate(-this.group.width/2,-this.group.height/2);var o=this.transformMatrix;o&&e.transform.apply(e,o)}e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,this._setShadow(e),this.clipTo&&n.util.clipContext(this,e),this._render(e,f),this.clipTo&&e.restore(),this._removeShadow(e),this._restoreFillRule(e),e.restore()}},_transform:function(e,n){var f=this.transformMatrix;f&&!this.group&&e.setTransform.apply(e,f),n||this.transform(e)},_setStrokeStyles:function(e){this.stroke&&(e.lineWidth=this.strokeWidth,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin,e.miterLimit=this.strokeMiterLimit,e.strokeStyle=this.stroke.toLive?this.stroke.toLive(e):this.stroke)},_setFillStyles:function(e){this.fill&&(e.fillStyle=this.fill.toLive?this.fill.toLive(e):this.fill)},_renderControls:function(e,f){var o=this.getViewportTransform();if(e.save(),this.active&&!f){var d;this.group&&(d=n.util.transformPoint(this.group.getCenterPoint(),o),e.translate(d.x,d.y),e.rotate(i(this.group.angle))),d=n.util.transformPoint(this.getCenterPoint(),o,null!=this.group),this.group&&(d.x*=this.group.scaleX,d.y*=this.group.scaleY),e.translate(d.x,d.y),e.rotate(i(this.angle)),this.drawBorders(e),this.drawControls(e)}e.restore()},_setShadow:function(e){this.shadow&&(e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY)},_removeShadow:function(e){this.shadow&&(e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0)},_renderFill:function(e){if(this.fill){if(e.save(),this.fill.toLive&&e.translate(-this.width/2+this.fill.offsetX||0,-this.height/2+this.fill.offsetY||0),this.fill.gradientTransform){var n=this.fill.gradientTransform;e.transform.apply(e,n)}"destination-over"===this.fillRule?e.fill("evenodd"):e.fill(),e.restore(),this.shadow&&!this.shadow.affectStroke&&this._removeShadow(e)}},_renderStroke:function(e){if(this.stroke&&0!==this.strokeWidth){if(e.save(),this.strokeDashArray)1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),t?(e.setLineDash(this.strokeDashArray),this._stroke&&this._stroke(e)):this._renderDashedStroke&&this._renderDashedStroke(e),e.stroke();else{if(this.stroke.gradientTransform){var n=this.stroke.gradientTransform;e.transform.apply(e,n)}this._stroke?this._stroke(e):e.stroke()}this._removeShadow(e),e.restore()}},clone:function(e,f){return this.constructor.fromObject?this.constructor.fromObject(this.toObject(f),e):new n.Object(this.toObject(f))},cloneAsImage:function(e){var f=this.toDataURL();return n.util.loadImage(f,function(f){e&&e(new n.Image(f))}),this},toDataURL:function(e){e||(e={});var f=n.util.createCanvasElement(),o=this.getBoundingRect();f.width=o.width,f.height=o.height,n.util.wrapElement(f,"div");var d=new n.Canvas(f);"jpg"===e.format&&(e.format="jpeg"),"jpeg"===e.format&&(d.backgroundColor="#fff");var i={active:this.get("active"),left:this.getLeft(),top:this.getTop()};this.set("active",!1),this.setPositionByOrigin(new n.Point(f.width/2,f.height/2),"center","center");var t=this.canvas;d.add(this);var l=d.toDataURL(e);return this.set(i).setCoords(),this.canvas=t,d.dispose(),d=null,l},isType:function(e){return this.type===e},complexity:function(){return 0},toJSON:function(e){return this.toObject(e)},setGradient:function(e,f){f||(f={});var o={colorStops:[]};o.type=f.type||(f.r1||f.r2?"radial":"linear"),o.coords={x1:f.x1,y1:f.y1,x2:f.x2,y2:f.y2},(f.r1||f.r2)&&(o.coords.r1=f.r1,o.coords.r2=f.r2);for(var d in f.colorStops){var i=new n.Color(f.colorStops[d]);o.colorStops.push({offset:d,color:i.toRgb(),opacity:i.getAlpha()})}return this.set(e,n.Gradient.forObject(this,o))},setPatternFill:function(e){return this.set("fill",new n.Pattern(e))},setShadow:function(e){return this.set("shadow",e?new n.Shadow(e):null)},setColor:function(e){return this.set("fill",e),this},setAngle:function(e){var n=("center"!==this.originX||"center"!==this.originY)&&this.centeredRotation;return n&&this._setOriginToCenter(),this.set("angle",e),n&&this._resetOrigin(),this},centerH:function(){return this.canvas.centerObjectH(this),this},centerV:function(){return this.canvas.centerObjectV(this),this},center:function(){return this.canvas.centerObject(this),this},remove:function(){return this.canvas.remove(this),this},getLocalPointer:function(e,n){n=n||this.canvas.getPointer(e);var f=this.translateToOriginPoint(this.getCenterPoint(),"left","top");return{x:n.x-f.x,y:n.y-f.y}},_setupFillRule:function(e){this.fillRule&&(this._prevFillRule=e.globalCompositeOperation,e.globalCompositeOperation=this.fillRule)},_restoreFillRule:function(e){this.fillRule&&this._prevFillRule&&(e.globalCompositeOperation=this._prevFillRule)}}),n.util.createAccessors(n.Object),n.Object.prototype.rotate=n.Object.prototype.setAngle,f(n.Object.prototype,n.Observable),n.Object.NUM_FRACTION_DIGITS=2,n.Object.__uid=0)}("undefined"!=typeof exports?exports:this),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{translateToCenterPoint:function(n,f,o){var d=n.x,i=n.y,t=this.stroke?this.strokeWidth:0;return"left"===f?d=n.x+(this.getWidth()+t*this.scaleX)/2:"right"===f&&(d=n.x-(this.getWidth()+t*this.scaleX)/2),"top"===o?i=n.y+(this.getHeight()+t*this.scaleY)/2:"bottom"===o&&(i=n.y-(this.getHeight()+t*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(d,i),n,e(this.angle))},translateToOriginPoint:function(n,f,o){var d=n.x,i=n.y,t=this.stroke?this.strokeWidth:0;return"left"===f?d=n.x-(this.getWidth()+t*this.scaleX)/2:"right"===f&&(d=n.x+(this.getWidth()+t*this.scaleX)/2),"top"===o?i=n.y-(this.getHeight()+t*this.scaleY)/2:"bottom"===o&&(i=n.y+(this.getHeight()+t*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(d,i),n,e(this.angle))},getCenterPoint:function(){var e=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(e,this.originX,this.originY)},getPointByOrigin:function(e,n){var f=this.getCenterPoint();return this.translateToOriginPoint(f,e,n)},toLocalPoint:function(n,f,o){var d,i,t=this.getCenterPoint(),l=this.stroke?this.strokeWidth:0;return f&&o?(d="left"===f?t.x-(this.getWidth()+l*this.scaleX)/2:"right"===f?t.x+(this.getWidth()+l*this.scaleX)/2:t.x,i="top"===o?t.y-(this.getHeight()+l*this.scaleY)/2:"bottom"===o?t.y+(this.getHeight()+l*this.scaleY)/2:t.y):(d=this.left,i=this.top),fabric.util.rotatePoint(new fabric.Point(n.x,n.y),t,-e(this.angle)).subtractEquals(new fabric.Point(d,i))},setPositionByOrigin:function(e,n,f){var o=this.translateToCenterPoint(e,n,f),d=this.translateToOriginPoint(o,this.originX,this.originY);this.set("left",d.x),this.set("top",d.y)},adjustPosition:function(n){var f=e(this.angle),o=this.getWidth()/2,d=Math.cos(f)*o,i=Math.sin(f)*o,t=this.getWidth(),l=Math.cos(f)*t,s=Math.sin(f)*t;"center"===this.originX&&"left"===n||"right"===this.originX&&"center"===n?(this.left-=d,this.top-=i):"left"===this.originX&&"center"===n||"center"===this.originX&&"right"===n?(this.left+=d,this.top+=i):"left"===this.originX&&"right"===n?(this.left+=l,this.top+=s):"right"===this.originX&&"left"===n&&(this.left-=l,this.top-=s),this.setCoords(),this.originX=n},_setOriginToCenter:function(){this._originalOriginX=this.originX,this._originalOriginY=this.originY;var e=this.getCenterPoint();this.originX="center",this.originY="center",this.left=e.x,this.top=e.y},_resetOrigin:function(){var e=this.translateToOriginPoint(this.getCenterPoint(),this._originalOriginX,this._originalOriginY);this.originX=this._originalOriginX,this.originY=this._originalOriginY,this.left=e.x,this.top=e.y,this._originalOriginX=null,this._originalOriginY=null},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","center")}})}(),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{oCoords:null,intersectsWithRect:function(e,n){var f=this.oCoords,o=new fabric.Point(f.tl.x,f.tl.y),d=new fabric.Point(f.tr.x,f.tr.y),i=new fabric.Point(f.bl.x,f.bl.y),t=new fabric.Point(f.br.x,f.br.y),l=fabric.Intersection.intersectPolygonRectangle([o,d,t,i],e,n);return"Intersection"===l.status},intersectsWithObject:function(e){function n(e){return{tl:new fabric.Point(e.tl.x,e.tl.y),tr:new fabric.Point(e.tr.x,e.tr.y),bl:new fabric.Point(e.bl.x,e.bl.y),br:new fabric.Point(e.br.x,e.br.y)}}var f=n(this.oCoords),o=n(e.oCoords),d=fabric.Intersection.intersectPolygonPolygon([f.tl,f.tr,f.br,f.bl],[o.tl,o.tr,o.br,o.bl]);return"Intersection"===d.status},isContainedWithinObject:function(e){var n=e.getBoundingRect(),f=new fabric.Point(n.left,n.top),o=new fabric.Point(n.left+n.width,n.top+n.height);return this.isContainedWithinRect(f,o)},isContainedWithinRect:function(e,n){var f=this.getBoundingRect();return f.left>=e.x&&f.left+f.width<=n.x&&f.top>=e.y&&f.top+f.height<=n.y},containsPoint:function(e){var n=this._getImageLines(this.oCoords),f=this._findCrossPoints(e,n);return 0!==f&&f%2===1},_getImageLines:function(e){return{topline:{o:e.tl,d:e.tr},rightline:{o:e.tr,d:e.br},bottomline:{o:e.br,d:e.bl},leftline:{o:e.bl,d:e.tl}}},_findCrossPoints:function(e,n){var f,o,d,i,t,l,s,u=0;for(var a in n)if(s=n[a],!(s.o.y=e.y&&s.d.y>=e.y||(s.o.x===s.d.x&&s.o.x>=e.x?(t=s.o.x,l=e.y):(f=0,o=(s.d.y-s.o.y)/(s.d.x-s.o.x),d=e.y-f*e.x,i=s.o.y-o*s.o.x,t=-(d-i)/(f-o),l=d+f*t),t>=e.x&&(u+=1),2!==u)))break;return u},getBoundingRectWidth:function(){return this.getBoundingRect().width},getBoundingRectHeight:function(){return this.getBoundingRect().height},getBoundingRect:function(){this.oCoords||this.setCoords();var e=[this.oCoords.tl.x,this.oCoords.tr.x,this.oCoords.br.x,this.oCoords.bl.x],n=fabric.util.array.min(e),f=fabric.util.array.max(e),o=Math.abs(n-f),d=[this.oCoords.tl.y,this.oCoords.tr.y,this.oCoords.br.y,this.oCoords.bl.y],i=fabric.util.array.min(d),t=fabric.util.array.max(d),l=Math.abs(i-t);return{left:n,top:i,width:o,height:l}},getWidth:function(){return this.width*this.scaleX},getHeight:function(){return this.height*this.scaleY},_constrainScale:function(e){return Math.abs(e)e?-this.minScaleLimit:this.minScaleLimit:e},scale:function(e){return e=this._constrainScale(e),0>e&&(this.flipX=!this.flipX,this.flipY=!this.flipY,e*=-1),this.scaleX=e,this.scaleY=e,this.setCoords(),this},scaleToWidth:function(e){var n=this.getBoundingRectWidth()/this.getWidth();return this.scale(e/this.width/n)},scaleToHeight:function(e){var n=this.getBoundingRectHeight()/this.getHeight();return this.scale(e/this.height/n)},setCoords:function(){var n=this.strokeWidth>1?this.strokeWidth:0,f=e(this.angle),o=this.getViewportTransform(),d=function(e){return fabric.util.transformPoint(e,o)},i=this.width,t=this.height,l="round"===this.strokeLineCap||"square"===this.strokeLineCap,s="line"===this.type&&1===this.width,u="line"===this.type&&1===this.height,a=l&&u||"line"!==this.type,p=l&&s||"line"!==this.type;s?i=n:u&&(t=n),a&&(i+=n),p&&(t+=n),this.currentWidth=i*this.scaleX,this.currentHeight=t*this.scaleY,this.currentWidth<0&&(this.currentWidth=Math.abs(this.currentWidth));var c=Math.sqrt(Math.pow(this.currentWidth/2,2)+Math.pow(this.currentHeight/2,2)),y=Math.atan(isFinite(this.currentHeight/this.currentWidth)?this.currentHeight/this.currentWidth:0),m=Math.cos(y+f)*c,r=Math.sin(y+f)*c,v=Math.sin(f),w=Math.cos(f),b=this.getCenterPoint(),g=new fabric.Point(this.currentWidth,this.currentHeight),h=new fabric.Point(b.x-m,b.y-r),x=new fabric.Point(h.x+g.x*w,h.y+g.x*v),j=new fabric.Point(h.x-g.y*v,h.y+g.y*w),k=new fabric.Point(h.x+g.x/2*w,h.y+g.x/2*v),q=d(h),z=d(x),A=d(new fabric.Point(x.x-g.y*v,x.y+g.y*w)),B=d(j),C=d(new fabric.Point(h.x-g.y/2*v,h.y+g.y/2*w)),D=d(k),E=d(new fabric.Point(x.x-g.y/2*v,x.y+g.y/2*w)),F=d(new fabric.Point(j.x+g.x/2*w,j.y+g.x/2*v)),G=d(new fabric.Point(k.x,k.y)),H=Math.cos(y+f)*this.padding*Math.sqrt(2),I=Math.sin(y+f)*this.padding*Math.sqrt(2);return q=q.add(new fabric.Point(-H,-I)),z=z.add(new fabric.Point(I,-H)),A=A.add(new fabric.Point(H,I)),B=B.add(new fabric.Point(-I,H)),C=C.add(new fabric.Point((-H-I)/2,(-I+H)/2)),D=D.add(new fabric.Point((I-H)/2,-(I+H)/2)),E=E.add(new fabric.Point((I+H)/2,(I-H)/2)),F=F.add(new fabric.Point((H-I)/2,(H+I)/2)),G=G.add(new fabric.Point((I-H)/2,-(I+H)/2)),this.oCoords={tl:q,tr:z,br:A,bl:B,ml:C,mt:D,mr:E,mb:F,mtr:G},this._setCornerCoords&&this._setCornerCoords(),this}})}(),fabric.util.object.extend(fabric.Object.prototype,{sendToBack:function(){return this.group?fabric.StaticCanvas.prototype.sendToBack.call(this.group,this):this.canvas.sendToBack(this),this},bringToFront:function(){return this.group?fabric.StaticCanvas.prototype.bringToFront.call(this.group,this):this.canvas.bringToFront(this),this},sendBackwards:function(e){return this.group?fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,e):this.canvas.sendBackwards(this,e),this},bringForward:function(e){return this.group?fabric.StaticCanvas.prototype.bringForward.call(this.group,this,e):this.canvas.bringForward(this,e),this},moveTo:function(e){return this.group?fabric.StaticCanvas.prototype.moveTo.call(this.group,this,e):this.canvas.moveTo(this,e),this}}),fabric.util.object.extend(fabric.Object.prototype,{getSvgStyles:function(){var e=this.fill?this.fill.toLive?"url(#SVGID_"+this.fill.id+")":this.fill:"none",n="destination-over"===this.fillRule?"evenodd":this.fillRule,f=this.stroke?this.stroke.toLive?"url(#SVGID_"+this.stroke.id+")":this.stroke:"none",o=this.strokeWidth?this.strokeWidth:"0",d=this.strokeDashArray?this.strokeDashArray.join(" "):"",i=this.strokeLineCap?this.strokeLineCap:"butt",t=this.strokeLineJoin?this.strokeLineJoin:"miter",l=this.strokeMiterLimit?this.strokeMiterLimit:"4",s="undefined"!=typeof this.opacity?this.opacity:"1",u=this.visible?"":" visibility: hidden;",a=this.shadow&&"text"!==this.type?"filter: url(#SVGID_"+this.shadow.id+");":"";return["stroke: ",f,"; ","stroke-width: ",o,"; ","stroke-dasharray: ",d,"; ","stroke-linecap: ",i,"; ","stroke-linejoin: ",t,"; ","stroke-miterlimit: ",l,"; ","fill: ",e,"; ","fill-rule: ",n,"; ","opacity: ",s,";",a,u].join("")},getSvgTransform:function(){if(this.group)return"";var e=fabric.util.toFixed,n=this.getAngle(),f=!this.canvas||this.canvas.svgViewportTransformation?this.getViewportTransform():[1,0,0,1,0,0],o=fabric.util.transformPoint(this.getCenterPoint(),f),d=fabric.Object.NUM_FRACTION_DIGITS,i="path-group"===this.type?"":"translate("+e(o.x,d)+" "+e(o.y,d)+")",t=0!==n?" rotate("+e(n,d)+")":"",l=1===this.scaleX&&1===this.scaleY&&1===f[0]&&1===f[3]?"":" scale("+e(this.scaleX*f[0],d)+" "+e(this.scaleY*f[3],d)+")",s="path-group"===this.type?this.width*f[0]:0,u=this.flipX?" matrix(-1 0 0 1 "+s+" 0) ":"",a="path-group"===this.type?this.height*f[3]:0,p=this.flipY?" matrix(1 0 0 -1 0 "+a+")":""; -return[i,t,l,u,p].join("")},getSvgTransformMatrix:function(){return this.transformMatrix?" matrix("+this.transformMatrix.join(" ")+")":""},_createBaseSVGMarkup:function(){var e=[];return this.fill&&this.fill.toLive&&e.push(this.fill.toSVG(this,!1)),this.stroke&&this.stroke.toLive&&e.push(this.stroke.toSVG(this,!1)),this.shadow&&e.push(this.shadow.toSVG(this)),e}}),fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(){return this.stateProperties.some(function(e){return this.get(e)!==this.originalState[e]},this)},saveState:function(e){return this.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),e&&e.stateProperties&&e.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),this},setupState:function(){return this.originalState={},this.saveState(),this}}),function(e){"use strict";function n(e,n){var f=e.origin,o=e.axis1,d=e.axis2,i=e.dimension,t=n.nearest,l=n.center,s=n.farthest;return function(){switch(this.get(f)){case t:return Math.min(this.get(o),this.get(d));case l:return Math.min(this.get(o),this.get(d))+.5*this.get(i);case s:return Math.max(this.get(o),this.get(d))}}}var f=e.fabric||(e.fabric={}),o=f.util.object.extend,d={x1:1,x2:1,y1:1,y2:1},i=f.StaticCanvas.supports("setLineDash");return f.Line?void f.warn("fabric.Line is already defined"):(f.Line=f.util.createClass(f.Object,{type:"line",x1:0,y1:0,x2:0,y2:0,initialize:function(e,n){n=n||{},e||(e=[0,0,0,0]),this.callSuper("initialize",n),this.set("x1",e[0]),this.set("y1",e[1]),this.set("x2",e[2]),this.set("y2",e[3]),this._setWidthHeight(n)},_setWidthHeight:function(e){e||(e={}),this.width=Math.abs(this.x2-this.x1)||1,this.height=Math.abs(this.y2-this.y1)||1,this.left="left"in e?e.left:this._getLeftToOriginX(),this.top="top"in e?e.top:this._getTopToOriginY()},_set:function(e,n){return this[e]=n,"undefined"!=typeof d[e]&&this._setWidthHeight(),this},_getLeftToOriginX:n({origin:"originX",axis1:"x1",axis2:"x2",dimension:"width"},{nearest:"left",center:"center",farthest:"right"}),_getTopToOriginY:n({origin:"originY",axis1:"y1",axis2:"y2",dimension:"height"},{nearest:"top",center:"center",farthest:"bottom"}),_render:function(e,n){if(e.beginPath(),n){var f=this.getCenterPoint();e.translate(f.x,f.y)}if(!this.strokeDashArray||this.strokeDashArray&&i){var o=this.x1<=this.x2?-1:1,d=this.y1<=this.y2?-1:1;e.moveTo(1===this.width?0:o*this.width/2,1===this.height?0:d*this.height/2),e.lineTo(1===this.width?0:-1*o*this.width/2,1===this.height?0:-1*d*this.height/2)}e.lineWidth=this.strokeWidth;var t=e.strokeStyle;e.strokeStyle=this.stroke||e.fillStyle,this.stroke&&this._renderStroke(e),e.strokeStyle=t},_renderDashedStroke:function(e){var n=this.x1<=this.x2?-1:1,o=this.y1<=this.y2?-1:1,d=1===this.width?0:n*this.width/2,i=1===this.height?0:o*this.height/2;e.beginPath(),f.util.drawDashedLine(e,d,i,-d,-i,this.strokeDashArray),e.closePath()},toObject:function(e){return o(this.callSuper("toObject",e),{x1:this.get("x1"),y1:this.get("y1"),x2:this.get("x2"),y2:this.get("y2")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f="";if(!this.group){var o=-this.width/2-(this.x1>this.x2?this.x2:this.x1),d=-this.height/2-(this.y1>this.y2?this.y2:this.y1);f="translate("+o+", "+d+") "}return n.push("\n'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),f.Line.ATTRIBUTE_NAMES=f.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),f.Line.fromElement=function(e,n){var d=f.parseAttributes(e,f.Line.ATTRIBUTE_NAMES),i=[d.x1||0,d.y1||0,d.x2||0,d.y2||0];return new f.Line(i,o(d,n))},void(f.Line.fromObject=function(e){var n=[e.x1,e.y1,e.x2,e.y2];return new f.Line(n,e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){return"radius"in e&&e.radius>0}var f=e.fabric||(e.fabric={}),o=2*Math.PI,d=f.util.object.extend;return f.Circle?void f.warn("fabric.Circle is already defined."):(f.Circle=f.util.createClass(f.Object,{type:"circle",radius:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("radius",e.radius||0)},_set:function(e,n){return this.callSuper("_set",e,n),"radius"===e&&this.setRadius(n),this},toObject:function(e){return d(this.callSuper("toObject",e),{radius:this.get("radius")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=0,o=0;return this.group&&(f=this.left+this.radius,o=this.top+this.radius),n.push("\n'),e?e(n.join("")):n.join("")},_render:function(e,n){e.beginPath(),e.arc(n?this.left+this.radius:0,n?this.top+this.radius:0,this.radius,0,o,!1),this._renderFill(e),this._renderStroke(e)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(e){this.radius=e,this.set("width",2*e).set("height",2*e)},complexity:function(){return 1}}),f.Circle.ATTRIBUTE_NAMES=f.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),f.Circle.fromElement=function(e,o){o||(o={});var i=f.parseAttributes(e,f.Circle.ATTRIBUTE_NAMES);if(!n(i))throw new Error("value of `r` attribute is required and can not be negative");i.left=i.left||0,i.top=i.top||0;var t=new f.Circle(d(i,o));return t.left-=t.radius,t.top-=t.radius,t},void(f.Circle.fromObject=function(e){return new f.Circle(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});return n.Triangle?void n.warn("fabric.Triangle is already defined"):(n.Triangle=n.util.createClass(n.Object,{type:"triangle",initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("width",e.width||100).set("height",e.height||100)},_render:function(e){var n=this.width/2,f=this.height/2;e.beginPath(),e.moveTo(-n,f),e.lineTo(0,-f),e.lineTo(n,f),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f=this.width/2,o=this.height/2;e.beginPath(),n.util.drawDashedLine(e,-f,o,0,-o,this.strokeDashArray),n.util.drawDashedLine(e,0,-o,f,o,this.strokeDashArray),n.util.drawDashedLine(e,f,o,-f,o,this.strokeDashArray),e.closePath()},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=this.width/2,o=this.height/2,d=[-f+" "+o,"0 "+-o,f+" "+o].join(",");return n.push("'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),void(n.Triangle.fromObject=function(e){return new n.Triangle(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=2*Math.PI,o=n.util.object.extend;return n.Ellipse?void n.warn("fabric.Ellipse is already defined."):(n.Ellipse=n.util.createClass(n.Object,{type:"ellipse",rx:0,ry:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("rx",e.rx||0),this.set("ry",e.ry||0),this.set("width",2*this.get("rx")),this.set("height",2*this.get("ry"))},toObject:function(e){return o(this.callSuper("toObject",e),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=0,o=0;return this.group&&(f=this.left+this.rx,o=this.top+this.ry),n.push("\n'),e?e(n.join("")):n.join("")},_render:function(e,n){e.beginPath(),e.save(),e.transform(1,0,0,this.ry/this.rx,0,0),e.arc(n?this.left+this.rx:0,n?(this.top+this.ry)*this.rx/this.ry:0,this.rx,0,f,!1),e.restore(),this._renderFill(e),this._renderStroke(e)},complexity:function(){return 1}}),n.Ellipse.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),n.Ellipse.fromElement=function(e,f){f||(f={});var d=n.parseAttributes(e,n.Ellipse.ATTRIBUTE_NAMES);d.left=d.left||0,d.top=d.top||0;var i=new n.Ellipse(o(d,f));return i.top-=i.ry,i.left-=i.rx,i},void(n.Ellipse.fromObject=function(e){return new n.Ellipse(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;if(n.Rect)return void console.warn("fabric.Rect is already defined");var o=n.Object.prototype.stateProperties.concat();o.push("rx","ry","x","y"),n.Rect=n.util.createClass(n.Object,{stateProperties:o,type:"rect",rx:0,ry:0,strokeDashArray:null,initialize:function(e){e=e||{},this.callSuper("initialize",e),this._initRxRy()},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(e,n){if(1===this.width&&1===this.height)return void e.fillRect(0,0,1,1);var f=this.rx?Math.min(this.rx,this.width/2):0,o=this.ry?Math.min(this.ry,this.height/2):0,d=this.width,i=this.height,t=n?this.left:-this.width/2,l=n?this.top:-this.height/2,s=0!==f||0!==o,u=.4477152502;e.beginPath(),e.moveTo(t+f,l),e.lineTo(t+d-f,l),s&&e.bezierCurveTo(t+d-u*f,l,t+d,l+u*o,t+d,l+o),e.lineTo(t+d,l+i-o),s&&e.bezierCurveTo(t+d,l+i-u*o,t+d-u*f,l+i,t+d-f,l+i),e.lineTo(t+f,l+i),s&&e.bezierCurveTo(t+u*f,l+i,t,l+i-u*o,t,l+i-o),e.lineTo(t,l+o),s&&e.bezierCurveTo(t,l+u*o,t+u*f,l,t+f,l),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f=-this.width/2,o=-this.height/2,d=this.width,i=this.height;e.beginPath(),n.util.drawDashedLine(e,f,o,f+d,o,this.strokeDashArray),n.util.drawDashedLine(e,f+d,o,f+d,o+i,this.strokeDashArray),n.util.drawDashedLine(e,f+d,o+i,f,o+i,this.strokeDashArray),n.util.drawDashedLine(e,f,o+i,f,o,this.strokeDashArray),e.closePath()},toObject:function(e){var n=f(this.callSuper("toObject",e),{rx:this.get("rx")||0,ry:this.get("ry")||0});return this.includeDefaultValues||this._removeDefaultValues(n),n},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=this.left,o=this.top;return this.group||(f=-this.width/2,o=-this.height/2),n.push("\n'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),n.Rect.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),n.Rect.fromElement=function(e,o){if(!e)return null;o=o||{};var d=n.parseAttributes(e,n.Rect.ATTRIBUTE_NAMES);return d.left=d.left||0,d.top=d.top||0,new n.Rect(f(o?n.util.object.clone(o):{},d))},n.Rect.fromObject=function(e){return new n.Rect(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.toFixed;return n.Polyline?void n.warn("fabric.Polyline is already defined"):(n.Polyline=n.util.createClass(n.Object,{type:"polyline",points:null,initialize:function(e,n){n=n||{},this.set("points",e),this.callSuper("initialize",n),this._calcDimensions()},_calcDimensions:function(){return n.Polygon.prototype._calcDimensions.call(this)},_applyPointOffset:function(){return n.Polygon.prototype._applyPointOffset.call(this)},toObject:function(e){return n.Polygon.prototype.toObject.call(this,e)},toSVG:function(e){for(var n=[],o=this._createBaseSVGMarkup(),d=0,i=this.points.length;i>d;d++)n.push(f(this.points[d].x,2),",",f(this.points[d].y,2)," ");return o.push("\n'),e?e(o.join("")):o.join("")},_render:function(e){var n;e.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),e.moveTo(this.points[0].x,this.points[0].y);for(var f=0,o=this.points.length;o>f;f++)n=this.points[f],e.lineTo(n.x,n.y);this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f,o;e.beginPath();for(var d=0,i=this.points.length;i>d;d++)f=this.points[d],o=this.points[d+1]||f,n.util.drawDashedLine(e,f.x,f.y,o.x,o.y,this.strokeDashArray)},complexity:function(){return this.get("points").length}}),n.Polyline.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat(),n.Polyline.fromElement=function(e,f){if(!e)return null;f||(f={});var o=n.parsePointsAttribute(e.getAttribute("points")),d=n.parseAttributes(e,n.Polyline.ATTRIBUTE_NAMES);return null===o?null:new n.Polyline(o,n.util.object.extend(d,f))},void(n.Polyline.fromObject=function(e){var f=e.points;return new n.Polyline(f,e,!0)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.min,d=n.util.array.max,i=n.util.toFixed;return n.Polygon?void n.warn("fabric.Polygon is already defined"):(n.Polygon=n.util.createClass(n.Object,{type:"polygon",points:null,initialize:function(e,n){n=n||{},this.points=e,this.callSuper("initialize",n),this._calcDimensions()},_calcDimensions:function(){var e=this.points,n=o(e,"x"),f=o(e,"y"),i=d(e,"x"),t=d(e,"y");this.width=i-n||1,this.height=t-f||1,this.left=n,this.top=f},_applyPointOffset:function(){this.points.forEach(function(e){e.x-=this.left+this.width/2,e.y-=this.top+this.height/2},this)},toObject:function(e){return f(this.callSuper("toObject",e),{points:this.points.concat()})},toSVG:function(e){for(var n=[],f=this._createBaseSVGMarkup(),o=0,d=this.points.length;d>o;o++)n.push(i(this.points[o].x,2),",",i(this.points[o].y,2)," ");return f.push("\n'),e?e(f.join("")):f.join("")},_render:function(e){var n;e.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),e.moveTo(this.points[0].x,this.points[0].y);for(var f=0,o=this.points.length;o>f;f++)n=this.points[f],e.lineTo(n.x,n.y);this._renderFill(e),(this.stroke||this.strokeDashArray)&&(e.closePath(),this._renderStroke(e))},_renderDashedStroke:function(e){var f,o;e.beginPath();for(var d=0,i=this.points.length;i>d;d++)f=this.points[d],o=this.points[d+1]||this.points[0],n.util.drawDashedLine(e,f.x,f.y,o.x,o.y,this.strokeDashArray);e.closePath()},complexity:function(){return this.points.length}}),n.Polygon.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat(),n.Polygon.fromElement=function(e,o){if(!e)return null;o||(o={});var d=n.parsePointsAttribute(e.getAttribute("points")),i=n.parseAttributes(e,n.Polygon.ATTRIBUTE_NAMES);return null===d?null:new n.Polygon(d,f(i,o))},void(n.Polygon.fromObject=function(e){return new n.Polygon(e.points,e,!0)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){return"H"===e[0]?e[1]:e[e.length-2]}function f(e){return"V"===e[0]?e[1]:e[e.length-1]}var o=e.fabric||(e.fabric={}),d=o.util.array.min,i=o.util.array.max,t=o.util.object.extend,l=Object.prototype.toString,s=o.util.drawArc,u={m:2,l:2,h:1,v:1,c:6,s:4,q:4,t:2,a:7},a={m:"l",M:"L"};return o.Path?void o.warn("fabric.Path is already defined"):(o.Path=o.util.createClass(o.Object,{type:"path",path:null,initialize:function(e,n){if(n=n||{},this.setOptions(n),!e)throw new Error("`path` argument is required");var f="[object Array]"===l.call(e);this.path=f?e:e.match&&e.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi),this.path&&(f||(this.path=this._parsePath()),this._initializePath(n),n.sourcePath&&this.setSourcePath(n.sourcePath))},_initializePath:function(e){var n="width"in e&&null!=e.width,f="height"in e&&null!=e.width,o="left"in e,d="top"in e,i=o?this.left:0,l=d?this.top:0;n&&f?(d||(this.top=this.height/2),o||(this.left=this.width/2)):(t(this,this._parseDimensions()),n&&(this.width=e.width),f&&(this.height=e.height)),this.pathOffset=this.pathOffset||this._calculatePathOffset(i,l)},_calculatePathOffset:function(e,n){return{x:this.left-e-this.width/2,y:this.top-n-this.height/2}},_render:function(e,n){var f,o,d,i,t,l=null,u=0,a=0,p=0,c=0,y=0,m=0,r=-(this.width/2+this.pathOffset.x),v=-(this.height/2+this.pathOffset.y);n&&(r+=this.width/2,v+=this.height/2);for(var w=0,b=this.path.length;b>w;++w){switch(f=this.path[w],f[0]){case"l":p+=f[1],c+=f[2],e.lineTo(p+r,c+v);break;case"L":p=f[1],c=f[2],e.lineTo(p+r,c+v);break;case"h":p+=f[1],e.lineTo(p+r,c+v);break;case"H":p=f[1],e.lineTo(p+r,c+v);break;case"v":c+=f[1],e.lineTo(p+r,c+v);break;case"V":c=f[1],e.lineTo(p+r,c+v);break;case"m":p+=f[1],c+=f[2],u=p,a=c,e.moveTo(p+r,c+v);break;case"M":p=f[1],c=f[2],u=p,a=c,e.moveTo(p+r,c+v);break;case"c":o=p+f[5],d=c+f[6],y=p+f[3],m=c+f[4],e.bezierCurveTo(p+f[1]+r,c+f[2]+v,y+r,m+v,o+r,d+v),p=o,c=d;break;case"C":p=f[5],c=f[6],y=f[3],m=f[4],e.bezierCurveTo(f[1]+r,f[2]+v,y+r,m+v,p+r,c+v);break;case"s":o=p+f[3],d=c+f[4],y=y?2*p-y:p,m=m?2*c-m:c,e.bezierCurveTo(y+r,m+v,p+f[1]+r,c+f[2]+v,o+r,d+v),y=p+f[1],m=c+f[2],p=o,c=d;break;case"S":o=f[3],d=f[4],y=2*p-y,m=2*c-m,e.bezierCurveTo(y+r,m+v,f[1]+r,f[2]+v,o+r,d+v),p=o,c=d,y=f[1],m=f[2];break;case"q":o=p+f[3],d=c+f[4],y=p+f[1],m=c+f[2],e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d;break;case"Q":o=f[3],d=f[4],e.quadraticCurveTo(f[1]+r,f[2]+v,o+r,d+v),p=o,c=d,y=f[1],m=f[2];break;case"t":o=p+f[1],d=c+f[2],null===l[0].match(/[QqTt]/)?(y=p,m=c):"t"===l[0]?(y=2*p-i,m=2*c-t):"q"===l[0]&&(y=2*p-y,m=2*c-m),i=y,t=m,e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d,y=p+f[1],m=c+f[2];break;case"T":o=f[1],d=f[2],y=2*p-y,m=2*c-m,e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d;break;case"a":s(e,p+r,c+v,[f[1],f[2],f[3],f[4],f[5],f[6]+p+r,f[7]+c+v]),p+=f[6],c+=f[7];break;case"A":s(e,p+r,c+v,[f[1],f[2],f[3],f[4],f[5],f[6]+r,f[7]+v]),p=f[6],c=f[7];break;case"z":case"Z":p=u,c=a,e.closePath()}l=f}},render:function(e,n){if(this.visible){e.save(),n&&e.translate(-this.width/2,-this.height/2);var f=this.transformMatrix;f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),n||this.transform(e),this._setStrokeStyles(e),this._setFillStyles(e),this._setShadow(e),this.clipTo&&o.util.clipContext(this,e),e.beginPath(),e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,this._render(e,n),this._renderFill(e),this._renderStroke(e),this.clipTo&&e.restore(),this._removeShadow(e),e.restore()}},toString:function(){return"#"},toObject:function(e){var n=t(this.callSuper("toObject",e),{path:this.path.map(function(e){return e.slice()}),pathOffset:this.pathOffset});return this.sourcePath&&(n.sourcePath=this.sourcePath),this.transformMatrix&&(n.transformMatrix=this.transformMatrix),n},toDatalessObject:function(e){var n=this.toObject(e);return this.sourcePath&&(n.path=this.sourcePath),delete n.sourcePath,n},toSVG:function(e){for(var n=[],f=this._createBaseSVGMarkup(),o=0,d=this.path.length;d>o;o++)n.push(this.path[o].join(" "));var i=n.join(" ");return f.push("\n"),e?e(f.join("")):f.join("")},complexity:function(){return this.path.length},_parsePath:function(){for(var e,n,f,o,d,i=[],t=[],l=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi,s=0,p=this.path.length;p>s;s++){for(e=this.path[s],o=e.slice(1).trim(),t.length=0;f=l.exec(o);)t.push(f[0]);d=[e.charAt(0)];for(var c=0,y=t.length;y>c;c++)n=parseFloat(t[c]),isNaN(n)||d.push(n);var m=d[0],r=u[m.toLowerCase()],v=a[m]||m;if(d.length-1>r)for(var w=1,b=d.length;b>w;w+=r)i.push([m].concat(d.slice(w,w+r))),m=v;else i.push(d)}return i},_parseDimensions:function(){var e=[],n=[],f={};this.path.forEach(function(o,d){this._getCoordsFromCommand(o,d,e,n,f)},this);var o=d(e),t=d(n),l=i(e),s=i(n),u=l-o,a=s-t,p={left:this.left+(o+u/2),top:this.top+(t+a/2),width:u,height:a};return p},_getCoordsFromCommand:function(e,o,d,i,t){var l=!1;"H"!==e[0]&&(t.x=n(0===o?e:this.path[o-1])),"V"!==e[0]&&(t.y=f(0===o?e:this.path[o-1])),e[0]===e[0].toLowerCase()&&(l=!0);var s,u=this._getXY(e,l,t);s=parseInt(u.x,10),isNaN(s)||d.push(s),s=parseInt(u.y,10),isNaN(s)||i.push(s)},_getXY:function(e,o,d){var i=o?d.x+n(e):"V"===e[0]?d.x:n(e),t=o?d.y+f(e):"H"===e[0]?d.y:f(e);return{x:i,y:t}}}),o.Path.fromObject=function(e,n){"string"==typeof e.path?o.loadSVGFromURL(e.path,function(f){var d=f[0],i=e.path;delete e.path,o.util.object.extend(d,e),d.setSourcePath(i),n(d)}):n(new o.Path(e.path,e))},o.Path.ATTRIBUTE_NAMES=o.SHARED_ATTRIBUTES.concat(["d"]),o.Path.fromElement=function(e,n,f){var d=o.parseAttributes(e,o.Path.ATTRIBUTE_NAMES);n&&n(new o.Path(d.d,t(d,f)))},void(o.Path.async=!0))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.invoke,d=n.Object.prototype.toObject;return n.PathGroup?void n.warn("fabric.PathGroup is already defined"):(n.PathGroup=n.util.createClass(n.Path,{type:"path-group",fill:"",initialize:function(e,n){n=n||{},this.paths=e||[];for(var f=this.paths.length;f--;)this.paths[f].group=this;this.setOptions(n),n.widthAttr&&(this.scaleX=n.widthAttr/n.width),n.heightAttr&&(this.scaleY=n.heightAttr/n.height),this.setCoords(),n.sourcePath&&this.setSourcePath(n.sourcePath)},render:function(e){if(this.visible){e.save();var f=this.transformMatrix;f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),this.transform(e),this._setShadow(e),this.clipTo&&n.util.clipContext(this,e);for(var o=0,d=this.paths.length;d>o;++o)this.paths[o].render(e,!0);this.clipTo&&e.restore(),this._removeShadow(e),e.restore()}},_set:function(e,n){if("fill"===e&&n&&this.isSameColor())for(var f=this.paths.length;f--;)this.paths[f]._set(e,n);return this.callSuper("_set",e,n)},toObject:function(e){var n=f(d.call(this,e),{paths:o(this.getObjects(),"toObject",e)});return this.sourcePath&&(n.sourcePath=this.sourcePath),n},toDatalessObject:function(e){var n=this.toObject(e);return this.sourcePath&&(n.paths=this.sourcePath),n},toSVG:function(e){for(var n=this.getObjects(),f="translate("+this.left+" "+this.top+")",o=["\n"],d=0,i=n.length;i>d;d++)o.push(n[d].toSVG(e));return o.push("\n"),e?e(o.join("")):o.join("")},toString:function(){return"#"},isSameColor:function(){var e=(this.getObjects()[0].get("fill")||"").toLowerCase();return this.getObjects().every(function(n){return(n.get("fill")||"").toLowerCase()===e})},complexity:function(){return this.paths.reduce(function(e,n){return e+(n&&n.complexity?n.complexity():0)},0)},getObjects:function(){return this.paths}}),n.PathGroup.fromObject=function(e,f){"string"==typeof e.paths?n.loadSVGFromURL(e.paths,function(o){var d=e.paths;delete e.paths;var i=n.util.groupSVGElements(o,e,d);f(i)}):n.util.enlivenObjects(e.paths,function(o){delete e.paths,f(new n.PathGroup(o,e))})},void(n.PathGroup.async=!0))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.min,d=n.util.array.max,i=n.util.array.invoke;if(!n.Group){var t={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};n.Group=n.util.createClass(n.Object,n.Collection,{type:"group",initialize:function(e,n){n=n||{},this._objects=e||[];for(var o=this._objects.length;o--;)this._objects[o].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),n&&f(this,n),this._setOpacityIfSame(),this.setCoords(),this.saveCoords()},_updateObjectsCoords:function(){this.forEachObject(this._updateObjectCoords,this)},_updateObjectCoords:function(e){var n=e.getLeft(),f=e.getTop();e.set({originalLeft:n,originalTop:f,left:n-this.left,top:f-this.top}),e.setCoords(),e.__origHasControls=e.hasControls,e.hasControls=!1},toString:function(){return"#"},addWithUpdate:function(e){return this._restoreObjectsState(),e&&(this._objects.push(e),e.group=this),this.forEachObject(this._setObjectActive,this),this._calcBounds(),this._updateObjectsCoords(),this},_setObjectActive:function(e){e.set("active",!0),e.group=this},removeWithUpdate:function(e){return this._moveFlippedObject(e),this._restoreObjectsState(),this.forEachObject(this._setObjectActive,this),this.remove(e),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(e){e.group=this},_onObjectRemoved:function(e){delete e.group,e.set("active",!1)},delegatedProperties:{fill:!0,opacity:!0,fontFamily:!0,fontWeight:!0,fontSize:!0,fontStyle:!0,lineHeight:!0,textDecoration:!0,textAlign:!0,backgroundColor:!0},_set:function(e,n){if(e in this.delegatedProperties){var f=this._objects.length;for(this[e]=n;f--;)this._objects[f].set(e,n)}else this[e]=n},toObject:function(e){return f(this.callSuper("toObject",e),{objects:i(this._objects,"toObject",e)})},render:function(e){if(this.visible){e.save(),this.clipTo&&n.util.clipContext(this,e);for(var f=0,o=this._objects.length;o>f;f++)this._renderObject(this._objects[f],e);this.clipTo&&e.restore(),e.restore()}},_renderControls:function(e,n){this.callSuper("_renderControls",e,n);for(var f=0,o=this._objects.length;o>f;f++)this._objects[f]._renderControls(e)},_renderObject:function(e,n){var f=e.hasRotatingPoint;e.visible&&(e.hasRotatingPoint=!1,e.render(n),e.hasRotatingPoint=f)},_restoreObjectsState:function(){return this._objects.forEach(this._restoreObjectState,this),this},_moveFlippedObject:function(e){var n=e.get("originX"),f=e.get("originY"),o=e.getCenterPoint();e.set({originX:"center",originY:"center",left:o.x,top:o.y}),this._toggleFlipping(e);var d=e.getPointByOrigin(n,f);return e.set({originX:n,originY:f,left:d.x,top:d.y}),this},_toggleFlipping:function(e){this.flipX&&(e.toggle("flipX"),e.set("left",-e.get("left")),e.setAngle(-e.getAngle())),this.flipY&&(e.toggle("flipY"),e.set("top",-e.get("top")),e.setAngle(-e.getAngle()))},_restoreObjectState:function(e){return this._setObjectPosition(e),e.setCoords(),e.hasControls=e.__origHasControls,delete e.__origHasControls,e.set("active",!1),e.setCoords(),delete e.group,this},_setObjectPosition:function(e){var n=this.getLeft(),f=this.getTop(),o=this._getRotatedLeftTop(e);e.set({angle:e.getAngle()+this.getAngle(),left:n+o.left,top:f+o.top,scaleX:e.get("scaleX")*this.get("scaleX"),scaleY:e.get("scaleY")*this.get("scaleY")})},_getRotatedLeftTop:function(e){var n=this.getAngle()*(Math.PI/180);return{left:-Math.sin(n)*e.getTop()*this.get("scaleY")+Math.cos(n)*e.getLeft()*this.get("scaleX"),top:Math.cos(n)*e.getTop()*this.get("scaleY")+Math.sin(n)*e.getLeft()*this.get("scaleX")}},destroy:function(){return this._objects.forEach(this._moveFlippedObject,this),this._restoreObjectsState()},saveCoords:function(){return this._originalLeft=this.get("left"),this._originalTop=this.get("top"),this},hasMoved:function(){return this._originalLeft!==this.get("left")||this._originalTop!==this.get("top")},setObjectsCoords:function(){return this.forEachObject(function(e){e.setCoords()}),this},_setOpacityIfSame:function(){var e=this.getObjects(),n=e[0]?e[0].get("opacity"):1,f=e.every(function(e){return e.get("opacity")===n});f&&(this.opacity=n)},_calcBounds:function(e){for(var n,f=[],o=[],d=0,i=this._objects.length;i>d;++d){n=this._objects[d],n.setCoords();for(var t in n.oCoords)f.push(n.oCoords[t].x),o.push(n.oCoords[t].y)}this.set(this._getBounds(f,o,e))},_getBounds:function(e,f,i){var t=n.util.invertTransform(this.getViewportTransform()),l=n.util.transformPoint(new n.Point(o(e),o(f)),t),s=n.util.transformPoint(new n.Point(d(e),d(f)),t),u={width:s.x-l.x||0,height:s.y-l.y||0};return i||(u.left=(l.x+s.x)/2||0,u.top=(l.y+s.y)/2||0),u},toSVG:function(e){for(var n=["\n'],f=0,o=this._objects.length;o>f;f++)n.push(this._objects[f].toSVG(e));return n.push("\n"),e?e(n.join("")):n.join("")},get:function(e){if(e in t){if(this[e])return this[e];for(var n=0,f=this._objects.length;f>n;n++)if(this._objects[n][e])return!0;return!1}return e in this.delegatedProperties?this._objects[0]&&this._objects[0].get(e):this[e]}}),n.Group.fromObject=function(e,f){n.util.enlivenObjects(e.objects,function(o){delete e.objects,f&&f(new n.Group(o,e))})},n.Group.async=!0}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=fabric.util.object.extend;return e.fabric||(e.fabric={}),e.fabric.Image?void fabric.warn("fabric.Image is already defined."):(fabric.Image=fabric.util.createClass(fabric.Object,{type:"image",crossOrigin:"",initialize:function(e,n){n||(n={}),this.filters=[],this.callSuper("initialize",n),this._initElement(e,n),this._initConfig(n),n.filters&&(this.filters=n.filters,this.applyFilters())},getElement:function(){return this._element},setElement:function(e,n){return this._element=e,this._originalElement=e,this._initConfig(),0!==this.filters.length&&this.applyFilters(n),this},setCrossOrigin:function(e){return this.crossOrigin=e,this._element.crossOrigin=e,this},getOriginalSize:function(){var e=this.getElement();return{width:e.width,height:e.height}},_stroke:function(e){e.save(),this._setStrokeStyles(e),e.beginPath(),e.strokeRect(-this.width/2,-this.height/2,this.width,this.height),e.closePath(),e.restore()},_renderDashedStroke:function(e){var n=-this.width/2,f=-this.height/2,o=this.width,d=this.height;e.save(),this._setStrokeStyles(e),e.beginPath(),fabric.util.drawDashedLine(e,n,f,n+o,f,this.strokeDashArray),fabric.util.drawDashedLine(e,n+o,f,n+o,f+d,this.strokeDashArray),fabric.util.drawDashedLine(e,n+o,f+d,n,f+d,this.strokeDashArray),fabric.util.drawDashedLine(e,n,f+d,n,f,this.strokeDashArray),e.closePath(),e.restore()},toObject:function(e){return n(this.callSuper("toObject",e),{src:this._originalElement.src||this._originalElement._src,filters:this.filters.map(function(e){return e&&e.toObject()}),crossOrigin:this.crossOrigin})},toSVG:function(e){var n=[],f=-this.width/2,o=-this.height/2;if(this.group&&(f=this.left,o=this.top),n.push('\n','\n"),this.stroke||this.strokeDashArray){var d=this.fill;this.fill=null,n.push("\n'),this.fill=d}return n.push("\n"),e?e(n.join("")):n.join("")},getSrc:function(){return this.getElement()?this.getElement().src||this.getElement()._src:void 0},toString:function(){return'#'},clone:function(e,n){this.constructor.fromObject(this.toObject(n),e)},applyFilters:function(e){if(this._originalElement){if(0===this.filters.length)return this._element=this._originalElement,void(e&&e());var n=this._originalElement,f=fabric.util.createCanvasElement(),o=fabric.util.createImage(),d=this;return f.width=n.width,f.height=n.height,f.getContext("2d").drawImage(n,0,0,n.width,n.height),this.filters.forEach(function(e){e&&e.applyTo(f)}),o.width=n.width,o.height=n.height,fabric.isLikelyNode?(o.src=f.toBuffer(void 0,fabric.Image.pngCompression),d._element=o,e&&e()):(o.onload=function(){d._element=o,e&&e(),o.onload=f=n=null},o.src=f.toDataURL("image/png")),this}},_render:function(e,n){this._element&&e.drawImage(this._element,n?this.left:-this.width/2,n?this.top:-this.height/2,this.width,this.height),this._renderStroke(e)},_resetWidthHeight:function(){var e=this.getElement();this.set("width",e.width),this.set("height",e.height)},_initElement:function(e){this.setElement(fabric.util.getById(e)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(e){e||(e={}),this.setOptions(e),this._setWidthHeight(e),this._element&&this.crossOrigin&&(this._element.crossOrigin=this.crossOrigin)},_initFilters:function(e,n){e.filters&&e.filters.length?fabric.util.enlivenObjects(e.filters,function(e){n&&n(e)},"fabric.Image.filters"):n&&n()},_setWidthHeight:function(e){this.width="width"in e?e.width:this.getElement()?this.getElement().width||0:0,this.height="height"in e?e.height:this.getElement()?this.getElement().height||0:0 -},complexity:function(){return 1}}),fabric.Image.CSS_CANVAS="canvas-img",fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc,fabric.Image.fromObject=function(e,n){fabric.util.loadImage(e.src,function(f){fabric.Image.prototype._initFilters.call(e,e,function(o){e.filters=o||[];var d=new fabric.Image(f,e);n&&n(d)})},null,e.crossOrigin)},fabric.Image.fromURL=function(e,n,f){fabric.util.loadImage(e,function(e){n(new fabric.Image(e,f))},null,f&&f.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(e,f,o){var d=fabric.parseAttributes(e,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(d["xlink:href"],f,n(o?fabric.util.object.clone(o):{},d))},fabric.Image.async=!0,void(fabric.Image.pngCompression=1))}("undefined"!=typeof exports?exports:this),fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",toObject:function(){return{type:this.type}},toJSON:function(){return this.toObject()}}),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Brightness=n.util.createClass(n.Image.filters.BaseFilter,{type:"Brightness",initialize:function(e){e=e||{},this.brightness=e.brightness||0},applyTo:function(e){for(var n=e.getContext("2d"),f=n.getImageData(0,0,e.width,e.height),o=f.data,d=this.brightness,i=0,t=o.length;t>i;i+=4)o[i]+=d,o[i+1]+=d,o[i+2]+=d;n.putImageData(f,0,0)},toObject:function(){return f(this.callSuper("toObject"),{brightness:this.brightness})}}),n.Image.filters.Brightness.fromObject=function(e){return new n.Image.filters.Brightness(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Convolute=n.util.createClass(n.Image.filters.BaseFilter,{type:"Convolute",initialize:function(e){e=e||{},this.opaque=e.opaque,this.matrix=e.matrix||[0,0,0,0,1,0,0,0,0];var f=n.util.createCanvasElement();this.tmpCtx=f.getContext("2d")},_createImageData:function(e,n){return this.tmpCtx.createImageData(e,n)},applyTo:function(e){for(var n=this.matrix,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=Math.round(Math.sqrt(n.length)),i=Math.floor(d/2),t=o.data,l=o.width,s=o.height,u=l,a=s,p=this._createImageData(u,a),c=p.data,y=this.opaque?1:0,m=0;a>m;m++)for(var r=0;u>r;r++){for(var v=m,w=r,b=4*(m*u+r),g=0,h=0,x=0,j=0,k=0;d>k;k++)for(var q=0;d>q;q++){var z=v+k-i,A=w+q-i;if(!(0>z||z>s||0>A||A>l)){var B=4*(z*l+A),C=n[k*d+q];g+=t[B]*C,h+=t[B+1]*C,x+=t[B+2]*C,j+=t[B+3]*C}}c[b]=g,c[b+1]=h,c[b+2]=x,c[b+3]=j+y*(255-j)}f.putImageData(p,0,0)},toObject:function(){return f(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),n.Image.filters.Convolute.fromObject=function(e){return new n.Image.filters.Convolute(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.GradientTransparency=n.util.createClass(n.Image.filters.BaseFilter,{type:"GradientTransparency",initialize:function(e){e=e||{},this.threshold=e.threshold||100},applyTo:function(e){for(var n=e.getContext("2d"),f=n.getImageData(0,0,e.width,e.height),o=f.data,d=this.threshold,i=o.length,t=0,l=o.length;l>t;t+=4)o[t+3]=d+255*(i-t)/i;n.putImageData(f,0,0)},toObject:function(){return f(this.callSuper("toObject"),{threshold:this.threshold})}}),n.Image.filters.GradientTransparency.fromObject=function(e){return new n.Image.filters.GradientTransparency(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Grayscale=n.util.createClass(n.Image.filters.BaseFilter,{type:"Grayscale",applyTo:function(e){for(var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=o.width*o.height*4,t=0;i>t;)n=(d[t]+d[t+1]+d[t+2])/3,d[t]=n,d[t+1]=n,d[t+2]=n,t+=4;f.putImageData(o,0,0)}}),n.Image.filters.Grayscale.fromObject=function(){return new n.Image.filters.Grayscale}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Invert=n.util.createClass(n.Image.filters.BaseFilter,{type:"Invert",applyTo:function(e){var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=d.length;for(n=0;i>n;n+=4)d[n]=255-d[n],d[n+1]=255-d[n+1],d[n+2]=255-d[n+2];f.putImageData(o,0,0)}}),n.Image.filters.Invert.fromObject=function(){return new n.Image.filters.Invert}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Mask=n.util.createClass(n.Image.filters.BaseFilter,{type:"Mask",initialize:function(e){e=e||{},this.mask=e.mask,this.channel=[0,1,2,3].indexOf(e.channel)>-1?e.channel:0},applyTo:function(e){if(this.mask){var f,o=e.getContext("2d"),d=o.getImageData(0,0,e.width,e.height),i=d.data,t=this.mask.getElement(),l=n.util.createCanvasElement(),s=this.channel,u=d.width*d.height*4;l.width=t.width,l.height=t.height,l.getContext("2d").drawImage(t,0,0,t.width,t.height);var a=l.getContext("2d").getImageData(0,0,t.width,t.height),p=a.data;for(f=0;u>f;f+=4)i[f+3]=p[f+s];o.putImageData(d,0,0)}},toObject:function(){return f(this.callSuper("toObject"),{mask:this.mask.toObject(),channel:this.channel})}}),n.Image.filters.Mask.fromObject=function(e,f){n.util.loadImage(e.mask.src,function(o){e.mask=new n.Image(o,e.mask),f&&f(new n.Image.filters.Mask(e))})},n.Image.filters.Mask.async=!0}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Noise=n.util.createClass(n.Image.filters.BaseFilter,{type:"Noise",initialize:function(e){e=e||{},this.noise=e.noise||0},applyTo:function(e){for(var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=this.noise,t=0,l=d.length;l>t;t+=4)n=(.5-Math.random())*i,d[t]+=n,d[t+1]+=n,d[t+2]+=n;f.putImageData(o,0,0)},toObject:function(){return f(this.callSuper("toObject"),{noise:this.noise})}}),n.Image.filters.Noise.fromObject=function(e){return new n.Image.filters.Noise(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Pixelate=n.util.createClass(n.Image.filters.BaseFilter,{type:"Pixelate",initialize:function(e){e=e||{},this.blocksize=e.blocksize||4},applyTo:function(e){var n,f,o,d,i,t,l,s=e.getContext("2d"),u=s.getImageData(0,0,e.width,e.height),a=u.data,p=u.height,c=u.width;for(f=0;p>f;f+=this.blocksize)for(o=0;c>o;o+=this.blocksize){n=4*f*c+4*o,d=a[n],i=a[n+1],t=a[n+2],l=a[n+3];for(var y=f,m=f+this.blocksize;m>y;y++)for(var r=o,v=o+this.blocksize;v>r;r++)n=4*y*c+4*r,a[n]=d,a[n+1]=i,a[n+2]=t,a[n+3]=l}s.putImageData(u,0,0)},toObject:function(){return f(this.callSuper("toObject"),{blocksize:this.blocksize})}}),n.Image.filters.Pixelate.fromObject=function(e){return new n.Image.filters.Pixelate(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.RemoveWhite=n.util.createClass(n.Image.filters.BaseFilter,{type:"RemoveWhite",initialize:function(e){e=e||{},this.threshold=e.threshold||30,this.distance=e.distance||20},applyTo:function(e){for(var n,f,o,d=e.getContext("2d"),i=d.getImageData(0,0,e.width,e.height),t=i.data,l=this.threshold,s=this.distance,u=255-l,a=Math.abs,p=0,c=t.length;c>p;p+=4)n=t[p],f=t[p+1],o=t[p+2],n>u&&f>u&&o>u&&a(n-f)n;n+=4)f=.3*i[n]+.59*i[n+1]+.11*i[n+2],i[n]=f+100,i[n+1]=f+50,i[n+2]=f+255;o.putImageData(d,0,0)}}),n.Image.filters.Sepia.fromObject=function(){return new n.Image.filters.Sepia}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Sepia2=n.util.createClass(n.Image.filters.BaseFilter,{type:"Sepia2",applyTo:function(e){var n,f,o,d,i=e.getContext("2d"),t=i.getImageData(0,0,e.width,e.height),l=t.data,s=l.length;for(n=0;s>n;n+=4)f=l[n],o=l[n+1],d=l[n+2],l[n]=(.393*f+.769*o+.189*d)/1.351,l[n+1]=(.349*f+.686*o+.168*d)/1.203,l[n+2]=(.272*f+.534*o+.131*d)/2.14;i.putImageData(t,0,0)}}),n.Image.filters.Sepia2.fromObject=function(){return new n.Image.filters.Sepia2}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Tint=n.util.createClass(n.Image.filters.BaseFilter,{type:"Tint",initialize:function(e){e=e||{},this.color=e.color||"#000000",this.opacity="undefined"!=typeof e.opacity?e.opacity:new n.Color(this.color).getAlpha()},applyTo:function(e){var f,o,d,i,t,l,s,u,a,p=e.getContext("2d"),c=p.getImageData(0,0,e.width,e.height),y=c.data,m=y.length;for(a=new n.Color(this.color).getSource(),o=a[0]*this.opacity,d=a[1]*this.opacity,i=a[2]*this.opacity,u=1-this.opacity,f=0;m>f;f+=4)t=y[f],l=y[f+1],s=y[f+2],y[f]=o+t*u,y[f+1]=d+l*u,y[f+2]=i+s*u;p.putImageData(c,0,0)},toObject:function(){return f(this.callSuper("toObject"),{color:this.color,opacity:this.opacity})}}),n.Image.filters.Tint.fromObject=function(e){return new n.Image.filters.Tint(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Multiply=n.util.createClass(n.Image.filters.BaseFilter,{type:"Multiply",initialize:function(e){e=e||{},this.color=e.color||"#000000"},applyTo:function(e){var f,o,d=e.getContext("2d"),i=d.getImageData(0,0,e.width,e.height),t=i.data,l=t.length;for(o=new n.Color(this.color).getSource(),f=0;l>f;f+=4)t[f]*=o[0]/255,t[f+1]*=o[1]/255,t[f+2]*=o[2]/255;d.putImageData(i,0,0)},toObject:function(){return f(this.callSuper("toObject"),{color:this.color})}}),n.Image.filters.Multiply.fromObject=function(e){return new n.Image.filters.Multiply(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric;n.Image.filters.Blend=n.util.createClass({type:"Blend",initialize:function(e){e=e||{},this.color=e.color||"#000",this.image=e.image||!1,this.mode=e.mode||"multiply",this.alpha=e.alpha||1},applyTo:function(e){var f,o,d,i,t,l,s,u=e.getContext("2d"),a=u.getImageData(0,0,e.width,e.height),p=a.data,c=!1;if(this.image){c=!0;var y=n.util.createCanvasElement();y.width=this.image.width,y.height=this.image.height;var m=new n.StaticCanvas(y);m.add(this.image);var r=m.getContext("2d");s=r.getImageData(0,0,m.width,m.height).data}else s=new n.Color(this.color).getSource(),f=s[0]*this.alpha,o=s[1]*this.alpha,d=s[2]*this.alpha;for(var v=0,w=p.length;w>v;v+=4)switch(i=p[v],t=p[v+1],l=p[v+2],c&&(f=s[v]*this.alpha,o=s[v+1]*this.alpha,d=s[v+2]*this.alpha),this.mode){case"multiply":p[v]=i*f/255,p[v+1]=t*o/255,p[v+2]=l*d/255;break;case"screen":p[v]=1-(1-i)*(1-f),p[v+1]=1-(1-t)*(1-o),p[v+2]=1-(1-l)*(1-d);break;case"add":p[v]=Math.min(255,i+f),p[v+1]=Math.min(255,t+o),p[v+2]=Math.min(255,l+d);break;case"diff":case"difference":p[v]=Math.abs(i-f),p[v+1]=Math.abs(t-o),p[v+2]=Math.abs(l-d);break;case"subtract":var b=i-f,g=t-o,h=l-d;p[v]=0>b?0:b,p[v+1]=0>g?0:g,p[v+2]=0>h?0:h;break;case"darken":p[v]=Math.min(i,f),p[v+1]=Math.min(t,o),p[v+2]=Math.min(l,d);break;case"lighten":p[v]=Math.max(i,f),p[v+1]=Math.max(t,o),p[v+2]=Math.max(l,d)}u.putImageData(a,0,0)}}),n.Image.filters.Blend.fromObject=function(e){return new n.Image.filters.Blend(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.object.clone,d=n.util.toFixed,i=n.StaticCanvas.supports("setLineDash");if(n.Text)return void n.warn("fabric.Text is already defined");var t=n.Object.prototype.stateProperties.concat();t.push("fontFamily","fontWeight","fontSize","text","textDecoration","textAlign","fontStyle","lineHeight","textBackgroundColor","useNative","path"),n.Text=n.util.createClass(n.Object,{_dimensionAffectingProps:{fontSize:!0,fontWeight:!0,fontFamily:!0,textDecoration:!0,fontStyle:!0,lineHeight:!0,stroke:!0,strokeWidth:!0,text:!0},_reNewline:/\r?\n/,type:"text",fontSize:40,fontWeight:"normal",fontFamily:"Times New Roman",textDecoration:"",textAlign:"left",fontStyle:"",lineHeight:1.3,textBackgroundColor:"",path:null,useNative:!0,stateProperties:t,stroke:null,shadow:null,initialize:function(e,n){n=n||{},this.text=e,this.__skipDimension=!0,this.setOptions(n),this.__skipDimension=!1,this._initDimensions()},_initDimensions:function(){if(!this.__skipDimension){var e=n.util.createCanvasElement();this._render(e.getContext("2d"))}},toString:function(){return"#'},_render:function(e){"undefined"==typeof Cufon||this.useNative===!0?this._renderViaNative(e):this._renderViaCufon(e)},_renderViaNative:function(e){var f=this.text.split(this._reNewline);this._setTextStyles(e),this.width=this._getTextWidth(e,f),this.height=this._getTextHeight(e,f),this.clipTo&&n.util.clipContext(this,e),this._renderTextBackground(e,f),this._translateForTextAlign(e),this._renderText(e,f),"left"!==this.textAlign&&"justify"!==this.textAlign&&e.restore(),this._renderTextDecoration(e,f),this.clipTo&&e.restore(),this._setBoundaries(e,f),this._totalLineHeight=0},_renderText:function(e,n){e.save(),this._setShadow(e),this._setupFillRule(e),this._renderTextFill(e,n),this._renderTextStroke(e,n),this._restoreFillRule(e),this._removeShadow(e),e.restore()},_translateForTextAlign:function(e){"left"!==this.textAlign&&"justify"!==this.textAlign&&(e.save(),e.translate("center"===this.textAlign?this.width/2:this.width,0))},_setBoundaries:function(e,n){this._boundaries=[];for(var f=0,o=n.length;o>f;f++){var d=this._getLineWidth(e,n[f]),i=this._getLineLeftOffset(d);this._boundaries.push({height:this.fontSize*this.lineHeight,width:d,left:i})}},_setTextStyles:function(e){this._setFillStyles(e),this._setStrokeStyles(e),e.textBaseline="alphabetic",this.skipTextAlign||(e.textAlign=this.textAlign),e.font=this._getFontDeclaration()},_getTextHeight:function(e,n){return this.fontSize*n.length*this.lineHeight},_getTextWidth:function(e,n){for(var f=e.measureText(n[0]||"|").width,o=1,d=n.length;d>o;o++){var i=e.measureText(n[o]).width;i>f&&(f=i)}return f},_renderChars:function(e,n,f,o,d){n[e](f,o,d)},_renderTextLine:function(e,n,f,o,d,i){if(d-=this.fontSize/4,"justify"!==this.textAlign)return void this._renderChars(e,n,f,o,d,i);var t=n.measureText(f).width,l=this.width;if(l>t)for(var s=f.split(/\s+/),u=n.measureText(f.replace(/\s+/g,"")).width,a=l-u,p=s.length-1,c=a/p,y=0,m=0,r=s.length;r>m;m++)this._renderChars(e,n,s[m],o+y,d,i),y+=n.measureText(s[m]).width+c;else this._renderChars(e,n,f,o,d,i)},_getLeftOffset:function(){return n.isLikelyNode?0:-this.width/2},_getTopOffset:function(){return-this.height/2},_renderTextFill:function(e,n){if(this.fill||this._skipFillStrokeCheck){this._boundaries=[];for(var f=0,o=0,d=n.length;d>o;o++){var i=this._getHeightOfLine(e,o,n);f+=i,this._renderTextLine("fillText",e,n[o],this._getLeftOffset(),this._getTopOffset()+f,o)}}},_renderTextStroke:function(e,n){if(this.stroke&&0!==this.strokeWidth||this._skipFillStrokeCheck){var f=0;e.save(),this.strokeDashArray&&(1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),i&&e.setLineDash(this.strokeDashArray)),e.beginPath();for(var o=0,d=n.length;d>o;o++){var t=this._getHeightOfLine(e,o,n);f+=t,this._renderTextLine("strokeText",e,n[o],this._getLeftOffset(),this._getTopOffset()+f,o)}e.closePath(),e.restore()}},_getHeightOfLine:function(){return this.fontSize*this.lineHeight},_renderTextBackground:function(e,n){this._renderTextBoxBackground(e),this._renderTextLinesBackground(e,n)},_renderTextBoxBackground:function(e){this.backgroundColor&&(e.save(),e.fillStyle=this.backgroundColor,e.fillRect(this._getLeftOffset(),this._getTopOffset(),this.width,this.height),e.restore())},_renderTextLinesBackground:function(e,n){if(this.textBackgroundColor){e.save(),e.fillStyle=this.textBackgroundColor;for(var f=0,o=n.length;o>f;f++)if(""!==n[f]){var d=this._getLineWidth(e,n[f]),i=this._getLineLeftOffset(d);e.fillRect(this._getLeftOffset()+i,this._getTopOffset()+f*this.fontSize*this.lineHeight,d,this.fontSize*this.lineHeight)}e.restore()}},_getLineLeftOffset:function(e){return"center"===this.textAlign?(this.width-e)/2:"right"===this.textAlign?this.width-e:0},_getLineWidth:function(e,n){return"justify"===this.textAlign?this.width:e.measureText(n).width},_renderTextDecoration:function(e,n){function f(f){for(var i=0,t=n.length;t>i;i++){var l=d._getLineWidth(e,n[i]),s=d._getLineLeftOffset(l);e.fillRect(d._getLeftOffset()+s,~~(f+i*d._getHeightOfLine(e,i,n)-o),l,1)}}if(this.textDecoration){var o=this._getTextHeight(e,n)/2,d=this;this.textDecoration.indexOf("underline")>-1&&f(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&f(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&f(this.fontSize*this.lineHeight-this.fontSize)}},_getFontDeclaration:function(){return[n.isLikelyNode?this.fontWeight:this.fontStyle,n.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",n.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(e,n){if(this.visible){e.save(),this._transform(e,n);var f=this.transformMatrix,o=this.group&&"path-group"===this.group.type;o&&e.translate(-this.group.width/2,-this.group.height/2),f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),o&&e.translate(this.left,this.top),this._render(e),e.restore()}},toObject:function(e){var n=f(this.callSuper("toObject",e),{text:this.text,fontSize:this.fontSize,fontWeight:this.fontWeight,fontFamily:this.fontFamily,fontStyle:this.fontStyle,lineHeight:this.lineHeight,textDecoration:this.textDecoration,textAlign:this.textAlign,path:this.path,textBackgroundColor:this.textBackgroundColor,useNative:this.useNative});return this.includeDefaultValues||this._removeDefaultValues(n),n},toSVG:function(e){var n=[],f=this.text.split(this._reNewline),o=this._getSVGLeftTopOffsets(f),d=this._getSVGTextAndBg(o.lineTop,o.textLeft,f),i=this._getSVGShadows(o.lineTop,f);return o.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._wrapSVGTextAndBg(n,d,i,o),e?e(n.join("")):n.join("")},_getSVGLeftTopOffsets:function(e){var n=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,f=-(this.width/2),o=this.useNative?this.fontSize-1:this.height/2-e.length*this.fontSize-this._totalLineHeight;return{textLeft:f+(this.group&&"path-group"===this.group.type?this.left:0),textTop:o+(this.group&&"path-group"===this.group.type?this.top:0),lineTop:n}},_wrapSVGTextAndBg:function(e,n,f,o){e.push('\n',n.textBgRects.join(""),"',f.join(""),n.textSpans.join(""),"\n","\n")},_getSVGShadows:function(e,f){var o,i,t=[],l=1;if(!this.shadow||!this._boundaries)return t;for(o=0,i=f.length;i>o;o++)if(""!==f[o]){var s=this._boundaries&&this._boundaries[o]?this._boundaries[o].left:0;t.push('",n.util.string.escapeXml(f[o]),""),l=1}else l++;return t},_getSVGTextAndBg:function(e,n,f){var o=[],d=[],i=1;this._setSVGBg(d);for(var t=0,l=f.length;l>t;t++)""!==f[t]?(this._setSVGTextLineText(f[t],t,o,e,i,d),i=1):i++,this.textBackgroundColor&&this._boundaries&&this._setSVGTextLineBg(d,t,n,e);return{textSpans:o,textBgRects:d}},_setSVGTextLineText:function(e,f,o,i,t){var l=this._boundaries&&this._boundaries[f]?d(this._boundaries[f].left,2):0;o.push('",n.util.string.escapeXml(e),"")},_setSVGTextLineBg:function(e,n,f,o){e.push("\n')},_setSVGBg:function(e){this.backgroundColor&&this._boundaries&&e.push("')},_getFillAttributes:function(e){var f=e&&"string"==typeof e?new n.Color(e):"";return f&&f.getSource()&&1!==f.getAlpha()?'opacity="'+f.getAlpha()+'" fill="'+f.setAlpha(1).toRgb()+'"':'fill="'+e+'"'},_set:function(e,n){"fontFamily"===e&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+n+"$3")),this.callSuper("_set",e,n),e in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),n.Text.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("x y dx dy font-family font-style font-weight font-size text-decoration text-anchor".split(" ")),n.Text.DEFAULT_SVG_FONT_SIZE=16,n.Text.fromElement=function(e,f){if(!e)return null;var o=n.parseAttributes(e,n.Text.ATTRIBUTE_NAMES);f=n.util.object.extend(f?n.util.object.clone(f):{},o),"dx"in o&&(f.left+=o.dx),"dy"in o&&(f.top+=o.dy),"fontSize"in f||(f.fontSize=n.Text.DEFAULT_SVG_FONT_SIZE),f.originX||(f.originX="left");var d=new n.Text(e.textContent,f),i=0;return"left"===d.originX&&(i=d.getWidth()/2),"right"===d.originX&&(i=-d.getWidth()/2),d.set({left:d.getLeft()+i,top:d.getTop()-d.getHeight()/2}),d},n.Text.fromObject=function(e){return new n.Text(e.text,o(e))},n.util.createAccessors(n.Text)}("undefined"!=typeof exports?exports:this)}).call(this,require("buffer").Buffer)},{buffer:2,canvas:1,jsdom:1}]},{},[6])(6)}); \ No newline at end of file diff --git a/package.json b/package.json index e424d55..7fde346 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,6 @@ "engines": { "node": ">=0.10.0" }, - "dependencies": { - "es6-promise": "^2.0.1" - }, "repository": { "type": "git", "url": "git@github.com:niklasvh/html2canvas.git" diff --git a/readme.md b/readme.md index a1709ba..972637f 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ The script is still in a **very experimental state**, so I don't recommend using ###Browser compatibility### -The script should work fine on the following browsers: +The library should work fine on the following browsers (with `Promise` polyfill): * Firefox 3.5+ * Google Chrome @@ -32,6 +32,10 @@ As each CSS property needs to be manually built to be supported, there are a num ### Usage ### +The html2canvas library utilizes `Promise`s and expects them to be available in the global context. If you wish to +support [older browsers](http://caniuse.com/#search=promise) that do not natively support `Promise`s, please include a polyfill such as +[es6-promise](https://github.com/jakearchibald/es6-promise) before including `html2canvas`. + **Note!** These instructions are for using the current dev version of 0.5, for the latest release version (0.4.1), checkout the [old readme](https://github.com/niklasvh/html2canvas/blob/v0.4/readme.md). To render an `element` with html2canvas, simply call: diff --git a/src/clone.js b/src/clone.js index 826d45b..bafaa09 100644 --- a/src/clone.js +++ b/src/clone.js @@ -1,5 +1,4 @@ var log = require('./log'); -var Promise = require('./promise'); function restoreOwnerScroll(ownerDocument, x, y) { if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) { diff --git a/src/core.js b/src/core.js index b2b81e9..ce6a2de 100644 --- a/src/core.js +++ b/src/core.js @@ -1,4 +1,3 @@ -var Promise = require('./promise'); var Support = require('./support'); var CanvasRenderer = require('./renderers/canvas'); var ImageLoader = require('./imageloader'); @@ -51,7 +50,6 @@ function html2canvas(nodeList, options) { }); } -html2canvas.Promise = Promise; html2canvas.CanvasRenderer = CanvasRenderer; html2canvas.NodeContainer = NodeContainer; html2canvas.log = log; diff --git a/src/dummyimagecontainer.js b/src/dummyimagecontainer.js index 5cb2700..4ee7401 100644 --- a/src/dummyimagecontainer.js +++ b/src/dummyimagecontainer.js @@ -1,4 +1,3 @@ -var Promise = require('./promise'); var log = require('./log'); var smallImage = require('./utils').smallImage; diff --git a/src/framecontainer.js b/src/framecontainer.js index 6f6efe8..475b9fa 100644 --- a/src/framecontainer.js +++ b/src/framecontainer.js @@ -1,5 +1,4 @@ var utils = require('./utils'); -var Promise = require('./promise'); var getBounds = utils.getBounds; var loadUrlDocument = require('./proxy').loadUrlDocument; diff --git a/src/gradientcontainer.js b/src/gradientcontainer.js index 4b91354..8520f34 100644 --- a/src/gradientcontainer.js +++ b/src/gradientcontainer.js @@ -1,5 +1,3 @@ -var Promise = require('./promise'); - function GradientContainer(imageData) { this.src = imageData.value; this.colorStops = []; diff --git a/src/imagecontainer.js b/src/imagecontainer.js index 4f46dd2..8710b56 100644 --- a/src/imagecontainer.js +++ b/src/imagecontainer.js @@ -1,5 +1,3 @@ -var Promise = require('./promise'); - function ImageContainer(src, cors) { this.src = src; this.image = new Image(); diff --git a/src/imageloader.js b/src/imageloader.js index 176db56..3a504e1 100644 --- a/src/imageloader.js +++ b/src/imageloader.js @@ -1,4 +1,3 @@ -var Promise = require('./promise'); var log = require('./log'); var ImageContainer = require('./imagecontainer'); var DummyImageContainer = require('./dummyimagecontainer'); diff --git a/src/nodeparser.js b/src/nodeparser.js index aa5c25c..683f9bd 100644 --- a/src/nodeparser.js +++ b/src/nodeparser.js @@ -5,7 +5,6 @@ var TextContainer = require('./textcontainer'); var PseudoElementContainer = require('./pseudoelementcontainer'); var FontMetrics = require('./fontmetrics'); var Color = require('./color'); -var Promise = require('./promise'); var StackingContext = require('./stackingcontext'); var utils = require('./utils'); var bind = utils.bind; diff --git a/src/promise.js b/src/promise.js deleted file mode 100644 index 1d7217a..0000000 --- a/src/promise.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('es6-promise').Promise; diff --git a/src/proxy.js b/src/proxy.js index 51bb7b0..8a41ac6 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -1,4 +1,3 @@ -var Promise = require('./promise'); var XHR = require('./xhr'); var utils = require('./utils'); var log = require('./log'); diff --git a/src/proxyimagecontainer.js b/src/proxyimagecontainer.js index ea1b4c7..1cac032 100644 --- a/src/proxyimagecontainer.js +++ b/src/proxyimagecontainer.js @@ -1,5 +1,4 @@ var ProxyURL = require('./proxy').ProxyURL; -var Promise = require('./promise'); function ProxyImageContainer(src, proxy) { var link = document.createElement("a"); diff --git a/src/svgcontainer.js b/src/svgcontainer.js index 0a3ed68..67280cd 100644 --- a/src/svgcontainer.js +++ b/src/svgcontainer.js @@ -1,4 +1,3 @@ -var Promise = require('./promise'); var XHR = require('./xhr'); var decode64 = require('./utils').decode64; diff --git a/src/svgnodecontainer.js b/src/svgnodecontainer.js index 2001662..9d5f1c3 100644 --- a/src/svgnodecontainer.js +++ b/src/svgnodecontainer.js @@ -1,5 +1,4 @@ var SVGContainer = require('./svgcontainer'); -var Promise = require('./promise'); function SVGNodeContainer(node, _native) { this.src = node; diff --git a/src/xhr.js b/src/xhr.js index 534f849..e7ae375 100644 --- a/src/xhr.js +++ b/src/xhr.js @@ -1,5 +1,3 @@ -var Promise = require('./promise'); - function XHR(url) { return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); diff --git a/tests/mocha/background.html b/tests/mocha/background.html index 7ecb5d9..626a557 100644 --- a/tests/mocha/background.html +++ b/tests/mocha/background.html @@ -3,6 +3,7 @@ Mocha Tests + diff --git a/tests/mocha/cropping.html b/tests/mocha/cropping.html index fbb1444..2a6d4fc 100644 --- a/tests/mocha/cropping.html +++ b/tests/mocha/cropping.html @@ -3,6 +3,7 @@ Mocha Tests + diff --git a/tests/mocha/form-rendering.html b/tests/mocha/form-rendering.html index 1f2dff1..363b836 100644 --- a/tests/mocha/form-rendering.html +++ b/tests/mocha/form-rendering.html @@ -3,6 +3,7 @@ Mocha Tests + diff --git a/tests/mocha/ie9-clonenode-bug.html b/tests/mocha/ie9-clonenode-bug.html index 78b246c..7949018 100644 --- a/tests/mocha/ie9-clonenode-bug.html +++ b/tests/mocha/ie9-clonenode-bug.html @@ -4,6 +4,7 @@ Proxy tests + diff --git a/tests/mocha/multiple-renders.html b/tests/mocha/multiple-renders.html index 8307638..ff30a22 100644 --- a/tests/mocha/multiple-renders.html +++ b/tests/mocha/multiple-renders.html @@ -3,6 +3,7 @@ Mocha Tests + @@ -25,7 +26,6 @@
@@ -23,7 +24,6 @@
diff --git a/tests/test.js b/tests/test.js index 61d3888..2312b32 100644 --- a/tests/test.js +++ b/tests/test.js @@ -4,10 +4,7 @@ var h2cSelector, h2cOptions; document.write(''); } - var sources = ['log', 'punycode/punycode', 'core', 'nodecontainer', 'pseudoelementcontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'dummyimagecontainer', 'proxyimagecontainer', 'gradientcontainer', - 'lineargradientcontainer', 'webkitgradientcontainer', 'svgcontainer', 'svgnodecontainer', 'imageloader', 'nodeparser', 'font', 'fontmetrics', 'renderer', 'promise', 'xhr', 'framecontainer', 'proxy', 'color', 'renderers/canvas']; - - ['/tests/assets/jquery-1.6.2', '/dist/html2canvas'].forEach(appendScript); + ['/node_modules/bluebird/js/browser/bluebird', '/tests/assets/jquery-1.6.2', '/dist/html2canvas'].forEach(appendScript); if (typeof(noFabric) === "undefined") { appendScript('/dist/html2canvas.svg');