/
qrcodeStyle.js
29 lines (29 loc) · 891 KB
/
qrcodeStyle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.QRCode=t():e.QRCode=t()}(window,function(){return d={"./js/fabric.js":
/*!**********************!*\
!*** ./js/fabric.js ***!
\**********************/
/*! no static exports found */function(module,exports,__webpack_require__){eval("/* WEBPACK VAR INJECTION */(function(Buffer) {function _typeof(obj){\"@babel/helpers - typeof\";if(typeof Symbol===\"function\"&&typeof Symbol.iterator===\"symbol\"){_typeof=function _typeof(obj){return typeof obj;};}else{_typeof=function _typeof(obj){return obj&&typeof Symbol===\"function\"&&obj.constructor===Symbol&&obj!==Symbol.prototype?\"symbol\":typeof obj;};}return _typeof(obj);}/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */var fabric=fabric||{version:'3.6.1'};if(true){exports.fabric=fabric;}/* _AMD_START_ */else {}/* _AMD_END_ */if(typeof document!=='undefined'&&typeof window!=='undefined'){if(document instanceof(typeof HTMLDocument!=='undefined'?HTMLDocument:Document)){fabric.document=document;}else{fabric.document=document.implementation.createHTMLDocument('');}fabric.window=window;}else{}// assume we're running under node.js when document/window are not present\n/* var jsdom = require('jsdom');\n var virtualWindow = new jsdom.JSDOM(\n decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),\n {\n features: {\n FetchExternalResources: ['img']\n },\n resources: 'usable'\n }).window;\n fabric.document = virtualWindow.document;\n fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;\n fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;\n fabric.window = virtualWindow;\n DOMParser = fabric.window.DOMParser; */ /**\n * True when in environment that supports touch events\n * @type boolean\n */fabric.isTouchSupported='ontouchstart'in fabric.window||'ontouchstart'in fabric.document||fabric.window&&fabric.window.navigator&&fabric.window.navigator.maxTouchPoints>0;/**\n * True when in environment that's probably Node.js\n * @type boolean\n */fabric.isLikelyNode=typeof Buffer!=='undefined'&&typeof window==='undefined';/* _FROM_SVG_START_ */ /**\n * Attributes parsed from all SVG elements\n * @type array\n */fabric.SHARED_ATTRIBUTES=['display','transform','fill','fill-opacity','fill-rule','opacity','stroke','stroke-dasharray','stroke-linecap','stroke-dashoffset','stroke-linejoin','stroke-miterlimit','stroke-opacity','stroke-width','id','paint-order','vector-effect','instantiated_by_use','clip-path'];/* _FROM_SVG_END_ */ /**\n * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.\n */fabric.DPI=96;fabric.reNum='(?:[-+]?(?:\\\\d+|\\\\d*\\\\.\\\\d+)(?:[eE][-+]?\\\\d+)?)';fabric.rePathCommand=/([-+]?((\\d+\\.\\d+)|((\\d+)|(\\.\\d+)))(?:[eE][-+]?\\d+)?)/ig;fabric.reNonWord=/[ \\n\\.,;!\\?\\-]/;fabric.fontPaths={};fabric.iMatrix=[1,0,0,1,0,0];fabric.svgNS='http://www.w3.org/2000/svg';/**\n * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.\n * @since 1.7.14\n * @type Number\n * @default\n */fabric.perfLimitSizeTotal=2097152;/**\n * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000\n * @since 1.7.14\n * @type Number\n * @default\n */fabric.maxCacheSideLimit=4096;/**\n * Lowest pixel limit for cache canvases, set at 256PX\n * @since 1.7.14\n * @type Number\n * @default\n */fabric.minCacheSideLimit=256;/**\n * Cache Object for widths of chars in text rendering.\n */fabric.charWidthsCache={};/**\n * if webgl is enabled and available, textureSize will determine the size\n * of the canvas backend\n * @since 2.0.0\n * @type Number\n * @default\n */fabric.textureSize=2048;/**\n * When 'true', style information is not retained when copy/pasting text, making\n * pasted text use destination style.\n * Defaults to 'false'.\n * @type Boolean\n * @default\n */fabric.disableStyleCopyPaste=false;/**\n * Enable webgl for filtering picture is available\n * A filtering backend will be initialized, this will both take memory and\n * time since a default 2048x2048 canvas will be created for the gl context\n * @since 2.0.0\n * @type Boolean\n * @default\n */fabric.enableGLFiltering=true;/**\n * Device Pixel Ratio\n * @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html\n */fabric.devicePixelRatio=fabric.window.devicePixelRatio||fabric.window.webkitDevicePixelRatio||fabric.window.mozDevicePixelRatio||1;/**\n * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value,\n * which is unitless and not rendered equally across browsers.\n *\n * Values that work quite well (as of October 2017) are:\n * - Chrome: 1.5\n * - Edge: 1.75\n * - Firefox: 0.9\n * - Safari: 0.95\n *\n * @since 2.0.0\n * @type Number\n * @default 1\n */fabric.browserShadowBlurConstant=1;/**\n * This object contains the result of arc to beizer conversion for faster retrieving if the same arc needs to be converted again.\n * It was an internal variable, is accessible since version 2.3.4\n */fabric.arcToSegmentsCache={};/**\n * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it.\n * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing\n * you do not get any speed benefit and you get a big object in memory.\n * The object was a private variable before, while now is appended to the lib so that you have access to it and you\n * can eventually clear it.\n * It was an internal variable, is accessible since version 2.3.4\n */fabric.boundsOfCurveCache={};/**\n * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better\n * @default true\n */fabric.cachesBoundsOfCurve=true;/**\n * Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on\n * Chrome + old hardware. if your users are experiencing empty images after filtering you may try to force this to true\n * this has to be set before instantiating the filtering backend ( before filtering the first image )\n * @type Boolean\n * @default false\n */fabric.forceGLPutImageData=false;fabric.initFilterBackend=function(){if(fabric.enableGLFiltering&&fabric.isWebglSupported&&fabric.isWebglSupported(fabric.textureSize)){console.log('max texture size: '+fabric.maxTextureSize);return new fabric.WebglFilterBackend({tileSize:fabric.textureSize});}else if(fabric.Canvas2dFilterBackend){return new fabric.Canvas2dFilterBackend();}};if(typeof document!=='undefined'&&typeof window!=='undefined'){// ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)\nwindow.fabric=fabric;}(function(){/**\n * @private\n * @param {String} eventName\n * @param {Function} handler\n */function _removeEventListener(eventName,handler){if(!this.__eventListeners[eventName]){return;}var eventListener=this.__eventListeners[eventName];if(handler){eventListener[eventListener.indexOf(handler)]=false;}else{fabric.util.array.fill(eventListener,false);}}/**\n * Observes specified event\n * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)\n * @memberOf fabric.Observable\n * @alias on\n * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})\n * @param {Function} handler Function that receives a notification when an event of the specified type occurs\n * @return {Self} thisArg\n * @chainable\n */function observe(eventName,handler){if(!this.__eventListeners){this.__eventListeners={};}// one object with key/value pairs was passed\nif(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;}/**\n * Stops event observing for a particular event handler. Calling this method\n * without arguments removes all handlers for all events\n * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)\n * @memberOf fabric.Observable\n * @alias off\n * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})\n * @param {Function} handler Function to be deleted from EventListeners\n * @return {Self} thisArg\n * @chainable\n */function stopObserving(eventName,handler){if(!this.__eventListeners){return this;}// remove all key/value pairs (event name -> event handler)\nif(arguments.length===0){for(eventName in this.__eventListeners){_removeEventListener.call(this,eventName);}}// one object with key/value pairs was passed\nelse 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;}/**\n * Fires event with an optional options object\n * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)\n * @memberOf fabric.Observable\n * @alias trigger\n * @param {String} eventName Event name to fire\n * @param {Object} [options] Options object\n * @return {Self} thisArg\n * @chainable\n */function fire(eventName,options){if(!this.__eventListeners){return this;}var listenersForEvent=this.__eventListeners[eventName];if(!listenersForEvent){return this;}for(var i=0,len=listenersForEvent.length;i<len;i++){listenersForEvent[i]&&listenersForEvent[i].call(this,options||{});}this.__eventListeners[eventName]=listenersForEvent.filter(function(value){return value!==false;});return this;}/**\n * @namespace fabric.Observable\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#events}\n * @see {@link http://fabricjs.com/events|Events demo}\n */fabric.Observable={observe:observe,stopObserving:stopObserving,fire:fire,on:observe,off:stopObserving,trigger:fire};})();/**\n * @namespace fabric.Collection\n */fabric.Collection={_objects:[],/**\n * Adds objects to collection, Canvas or Group, then renders canvas\n * (if `renderOnAddRemove` is not `false`).\n * in case of Group no changes to bounding box are made.\n * Objects should be instances of (or inherit from) fabric.Object\n * Use of this function is highly discouraged for groups.\n * you can add a bunch of objects with the add method but then you NEED\n * to run a addWithUpdate call for the Group class or position/bbox will be wrong.\n * @param {...fabric.Object} object Zero or more fabric instances\n * @return {Self} thisArg\n * @chainable\n */add:function add(){this._objects.push.apply(this._objects,arguments);if(this._onObjectAdded){for(var i=0,length=arguments.length;i<length;i++){this._onObjectAdded(arguments[i]);}}this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)\n * An object should be an instance of (or inherit from) fabric.Object\n * Use of this function is highly discouraged for groups.\n * you can add a bunch of objects with the insertAt method but then you NEED\n * to run a addWithUpdate call for the Group class or position/bbox will be wrong.\n * @param {Object} object Object to insert\n * @param {Number} index Index to insert object at\n * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs\n * @return {Self} thisArg\n * @chainable\n */insertAt:function insertAt(object,index,nonSplicing){var objects=this._objects;if(nonSplicing){objects[index]=object;}else{objects.splice(index,0,object);}this._onObjectAdded&&this._onObjectAdded(object);this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)\n * @param {...fabric.Object} object Zero or more fabric instances\n * @return {Self} thisArg\n * @chainable\n */remove:function remove(){var objects=this._objects,index,somethingRemoved=false;for(var i=0,length=arguments.length;i<length;i++){index=objects.indexOf(arguments[i]);// only call onObjectRemoved if an object was actually removed\nif(index!==-1){somethingRemoved=true;objects.splice(index,1);this._onObjectRemoved&&this._onObjectRemoved(arguments[i]);}}this.renderOnAddRemove&&somethingRemoved&&this.requestRenderAll();return this;},/**\n * Executes given function for each object in this group\n * @param {Function} callback\n * Callback invoked with current object as first argument,\n * index - as second and an array of all objects - as third.\n * Callback is invoked in a context of Global Object (e.g. `window`)\n * when no `context` argument is given\n *\n * @param {Object} context Context (aka thisObject)\n * @return {Self} thisArg\n * @chainable\n */forEachObject:function forEachObject(callback,context){var objects=this.getObjects();for(var i=0,len=objects.length;i<len;i++){callback.call(context,objects[i],i,objects);}return this;},/**\n * Returns an array of children objects of this instance\n * Type parameter introduced in 1.3.10\n * since 2.3.5 this method return always a COPY of the array;\n * @param {String} [type] When specified, only objects of this type are returned\n * @return {Array}\n */getObjects:function getObjects(type){if(typeof type==='undefined'){return this._objects.concat();}return this._objects.filter(function(o){return o.type===type;});},/**\n * Returns object at specified index\n * @param {Number} index\n * @return {Self} thisArg\n */item:function item(index){return this._objects[index];},/**\n * Returns true if collection contains no objects\n * @return {Boolean} true if collection is empty\n */isEmpty:function isEmpty(){return this._objects.length===0;},/**\n * Returns a size of a collection (i.e: length of an array containing its objects)\n * @return {Number} Collection size\n */size:function size(){return this._objects.length;},/**\n * Returns true if collection contains an object\n * @param {Object} object Object to check against\n * @return {Boolean} `true` if collection contains an object\n */contains:function contains(object){return this._objects.indexOf(object)>-1;},/**\n * Returns number representation of a collection complexity\n * @return {Number} complexity\n */complexity:function complexity(){return this._objects.reduce(function(memo,current){memo+=current.complexity?current.complexity():0;return memo;},0);}};/**\n * @namespace fabric.CommonMethods\n */fabric.CommonMethods={/**\n * Sets object's properties from options\n * @param {Object} [options] Options object\n */_setOptions:function _setOptions(options){for(var prop in options){this.set(prop,options[prop]);}},/**\n * @private\n * @param {Object} [filler] Options object\n * @param {String} [property] property to set the Gradient to\n */_initGradient:function _initGradient(filler,property){if(filler&&filler.colorStops&&!(filler instanceof fabric.Gradient)){this.set(property,new fabric.Gradient(filler));}},/**\n * @private\n * @param {Object} [filler] Options object\n * @param {String} [property] property to set the Pattern to\n * @param {Function} [callback] callback to invoke after pattern load\n */_initPattern:function _initPattern(filler,property,callback){if(filler&&filler.source&&!(filler instanceof fabric.Pattern)){this.set(property,new fabric.Pattern(filler,callback));}else{callback&&callback();}},/**\n * @private\n * @param {Object} [options] Options object\n */_initClipping:function _initClipping(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);}},/**\n * @private\n */_setObject:function _setObject(obj){for(var prop in obj){this._set(prop,obj[prop]);}},/**\n * 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()`.\n * @param {String|Object} key Property name or object (if object, iterate over the object properties)\n * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)\n * @return {fabric.Object} thisArg\n * @chainable\n */set:function set(key,value){if(_typeof(key)==='object'){this._setObject(key);}else{if(typeof value==='function'&&key!=='clipTo'){this._set(key,value(this.get(key)));}else{this._set(key,value);}}return this;},_set:function _set(key,value){this[key]=value;},/**\n * Toggles specified property from `true` to `false` or from `false` to `true`\n * @param {String} property Property to toggle\n * @return {fabric.Object} thisArg\n * @chainable\n */toggle:function toggle(property){var value=this.get(property);if(typeof value==='boolean'){this.set(property,!value);}return this;},/**\n * Basic getter\n * @param {String} property Property name\n * @return {*} value of a property\n */get:function get(property){return this[property];}};(function(global){var sqrt=Math.sqrt,atan2=Math.atan2,pow=Math.pow,PiBy180=Math.PI/180,PiBy2=Math.PI/2;/**\n * @namespace fabric.util\n */fabric.util={/**\n * Calculate the cos of an angle, avoiding returning floats for known results\n * @static\n * @memberOf fabric.util\n * @param {Number} angle the angle in radians or in degree\n * @return {Number}\n */cos:function cos(angle){if(angle===0){return 1;}if(angle<0){// cos(a) = cos(-a)\nangle=-angle;}var angleSlice=angle/PiBy2;switch(angleSlice){case 1:case 3:return 0;case 2:return-1;}return Math.cos(angle);},/**\n * Calculate the sin of an angle, avoiding returning floats for known results\n * @static\n * @memberOf fabric.util\n * @param {Number} angle the angle in radians or in degree\n * @return {Number}\n */sin:function sin(angle){if(angle===0){return 0;}var angleSlice=angle/PiBy2,sign=1;if(angle<0){// sin(-a) = -sin(a)\nsign=-1;}switch(angleSlice){case 1:return sign;case 2:return 0;case 3:return-sign;}return Math.sin(angle);},/**\n * Removes value from an array.\n * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`\n * @static\n * @memberOf fabric.util\n * @param {Array} array\n * @param {*} value\n * @return {Array} original array\n */removeFromArray:function removeFromArray(array,value){var idx=array.indexOf(value);if(idx!==-1){array.splice(idx,1);}return array;},/**\n * Returns random number between 2 specified ones.\n * @static\n * @memberOf fabric.util\n * @param {Number} min lower limit\n * @param {Number} max upper limit\n * @return {Number} random value (between min and max)\n */getRandomInt:function getRandomInt(min,max){return Math.floor(Math.random()*(max-min+1))+min;},/**\n * Transforms degrees to radians.\n * @static\n * @memberOf fabric.util\n * @param {Number} degrees value in degrees\n * @return {Number} value in radians\n */degreesToRadians:function degreesToRadians(degrees){return degrees*PiBy180;},/**\n * Transforms radians to degrees.\n * @static\n * @memberOf fabric.util\n * @param {Number} radians value in radians\n * @return {Number} value in degrees\n */radiansToDegrees:function radiansToDegrees(radians){return radians/PiBy180;},/**\n * Rotates `point` around `origin` with `radians`\n * @static\n * @memberOf fabric.util\n * @param {fabric.Point} point The point to rotate\n * @param {fabric.Point} origin The origin of the rotation\n * @param {Number} radians The radians of the angle for the rotation\n * @return {fabric.Point} The new rotated point\n */rotatePoint:function rotatePoint(point,origin,radians){point.subtractEquals(origin);var v=fabric.util.rotateVector(point,radians);return new fabric.Point(v.x,v.y).addEquals(origin);},/**\n * Rotates `vector` with `radians`\n * @static\n * @memberOf fabric.util\n * @param {Object} vector The vector to rotate (x and y)\n * @param {Number} radians The radians of the angle for the rotation\n * @return {Object} The new rotated point\n */rotateVector:function rotateVector(vector,radians){var sin=fabric.util.sin(radians),cos=fabric.util.cos(radians),rx=vector.x*cos-vector.y*sin,ry=vector.x*sin+vector.y*cos;return{x:rx,y:ry};},/**\n * Apply transform t to point p\n * @static\n * @memberOf fabric.util\n * @param {fabric.Point} p The point to transform\n * @param {Array} t The transform\n * @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied\n * @return {fabric.Point} The transformed point\n */transformPoint:function transformPoint(p,t,ignoreOffset){if(ignoreOffset){return new fabric.Point(t[0]*p.x+t[2]*p.y,t[1]*p.x+t[3]*p.y);}return new fabric.Point(t[0]*p.x+t[2]*p.y+t[4],t[1]*p.x+t[3]*p.y+t[5]);},/**\n * Returns coordinates of points's bounding rectangle (left, top, width, height)\n * @param {Array} points 4 points array\n * @param {Array} [transform] an array of 6 numbers representing a 2x3 transform matrix\n * @return {Object} Object with left, top, width, height properties\n */makeBoundingBoxFromPoints:function makeBoundingBoxFromPoints(points,transform){if(transform){for(var i=0;i<points.length;i++){points[i]=fabric.util.transformPoint(points[i],transform);}}var xPoints=[points[0].x,points[1].x,points[2].x,points[3].x],minX=fabric.util.array.min(xPoints),maxX=fabric.util.array.max(xPoints),width=maxX-minX,yPoints=[points[0].y,points[1].y,points[2].y,points[3].y],minY=fabric.util.array.min(yPoints),maxY=fabric.util.array.max(yPoints),height=maxY-minY;return{left:minX,top:minY,width:width,height:height};},/**\n * Invert transformation t\n * @static\n * @memberOf fabric.util\n * @param {Array} t The transform\n * @return {Array} The inverted transform\n */invertTransform:function invertTransform(t){var a=1/(t[0]*t[3]-t[1]*t[2]),r=[a*t[3],-a*t[1],-a*t[2],a*t[0]],o=fabric.util.transformPoint({x:t[4],y:t[5]},r,true);r[4]=-o.x;r[5]=-o.y;return r;},/**\n * A wrapper around Number#toFixed, which contrary to native method returns number, not string.\n * @static\n * @memberOf fabric.util\n * @param {Number|String} number number to operate on\n * @param {Number} fractionDigits number of fraction digits to \"leave\"\n * @return {Number}\n */toFixed:function toFixed(number,fractionDigits){return parseFloat(Number(number).toFixed(fractionDigits));},/**\n * Converts from attribute value to pixel value if applicable.\n * Returns converted pixels or original value not converted.\n * @param {Number|String} value number to operate on\n * @param {Number} fontSize\n * @return {Number|String}\n */parseUnit:function parseUnit(value,fontSize){var unit=/\\D{0,2}$/.exec(value),number=parseFloat(value);if(!fontSize){fontSize=fabric.Text.DEFAULT_SVG_FONT_SIZE;}switch(unit[0]){case'mm':return number*fabric.DPI/25.4;case'cm':return number*fabric.DPI/2.54;case'in':return number*fabric.DPI;case'pt':return number*fabric.DPI/72;// or * 4 / 3\ncase'pc':return number*fabric.DPI/72*12;// or * 16\ncase'em':return number*fontSize;default:return number;}},/**\n * Function which always returns `false`.\n * @static\n * @memberOf fabric.util\n * @return {Boolean}\n */falseFunction:function falseFunction(){return false;},/**\n * Returns klass \"Class\" object of given namespace\n * @memberOf fabric.util\n * @param {String} type Type of object (eg. 'circle')\n * @param {String} namespace Namespace to get klass \"Class\" object from\n * @return {Object} klass \"Class\"\n */getKlass:function getKlass(type,namespace){// capitalize first letter only\ntype=fabric.util.string.camelize(type.charAt(0).toUpperCase()+type.slice(1));return fabric.util.resolveNamespace(namespace)[type];},/**\n * Returns array of attributes for given svg that fabric parses\n * @memberOf fabric.util\n * @param {String} type Type of svg element (eg. 'circle')\n * @return {Array} string names of supported attributes\n */getSvgAttributes:function getSvgAttributes(type){var attributes=['instantiated_by_use','style','id','class'];switch(type){case'linearGradient':attributes=attributes.concat(['x1','y1','x2','y2','gradientUnits','gradientTransform']);break;case'radialGradient':attributes=attributes.concat(['gradientUnits','gradientTransform','cx','cy','r','fx','fy','fr']);break;case'stop':attributes=attributes.concat(['offset','stop-color','stop-opacity']);break;}return attributes;},/**\n * Returns object of given namespace\n * @memberOf fabric.util\n * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'\n * @return {Object} Object for given namespace (default fabric)\n */resolveNamespace:function resolveNamespace(namespace){if(!namespace){return fabric;}var parts=namespace.split('.'),len=parts.length,i,obj=global||fabric.window;for(i=0;i<len;++i){obj=obj[parts[i]];}return obj;},/**\n * Loads image element from given url and passes it to a callback\n * @memberOf fabric.util\n * @param {String} url URL representing an image\n * @param {Function} callback Callback; invoked with loaded image\n * @param {*} [context] Context to invoke callback in\n * @param {Object} [crossOrigin] crossOrigin value to set image element to\n */loadImage:function loadImage(url,callback,context,crossOrigin){if(!url){callback&&callback.call(context,url);return;}var img=fabric.util.createImage();/** @ignore */var onLoadCallback=function onLoadCallback(){callback&&callback.call(context,img);img=img.onload=img.onerror=null;};img.onload=onLoadCallback;/** @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\n// https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767\n// see https://code.google.com/p/chromium/issues/detail?id=315152\n// https://bugzilla.mozilla.org/show_bug.cgi?id=935069\nif(url.indexOf('data')!==0&&crossOrigin){img.crossOrigin=crossOrigin;}// IE10 / IE11-Fix: SVG contents from data: URI\n// will only be available if the IMG is present\n// in the DOM (and visible)\nif(url.substring(0,14)==='data:image/svg'){img.onload=null;fabric.util.loadImageInDom(img,onLoadCallback);}img.src=url;},/**\n * Attaches SVG image with data: URL to the dom\n * @memberOf fabric.util\n * @param {Object} img Image object with data:image/svg src\n * @param {Function} callback Callback; invoked with loaded image\n * @return {Object} DOM element (div containing the SVG image)\n */loadImageInDom:function loadImageInDom(img,onLoadCallback){var div=fabric.document.createElement('div');div.style.width=div.style.height='1px';div.style.left=div.style.top='-100%';div.style.position='absolute';div.appendChild(img);fabric.document.querySelector('body').appendChild(div);/**\n * Wrap in function to:\n * 1. Call existing callback\n * 2. Cleanup DOM\n */img.onload=function(){onLoadCallback();div.parentNode.removeChild(div);div=null;};},/**\n * Creates corresponding fabric instances from their object representations\n * @static\n * @memberOf fabric.util\n * @param {Array} objects Objects to enliven\n * @param {Function} callback Callback to invoke when all objects are created\n * @param {String} namespace Namespace to get klass \"Class\" object from\n * @param {Function} reviver Method for further parsing of object elements,\n * called after each fabric object created.\n */enlivenObjects:function enlivenObjects(objects,callback,namespace,reviver){objects=objects||[];var enlivenedObjects=[],numLoadedObjects=0,numTotalObjects=objects.length;function onLoaded(){if(++numLoadedObjects===numTotalObjects){callback&&callback(enlivenedObjects.filter(function(obj){// filter out undefined objects (objects that gave error)\nreturn obj;}));}}if(!numTotalObjects){callback&&callback(enlivenedObjects);return;}objects.forEach(function(o,index){// if sparse array\nif(!o||!o.type){onLoaded();return;}var klass=fabric.util.getKlass(o.type,namespace);klass.fromObject(o,function(obj,error){error||(enlivenedObjects[index]=obj);reviver&&reviver(o,obj,error);onLoaded();});});},/**\n * Create and wait for loading of patterns\n * @static\n * @memberOf fabric.util\n * @param {Array} patterns Objects to enliven\n * @param {Function} callback Callback to invoke when all objects are created\n * called after each fabric object created.\n */enlivenPatterns:function enlivenPatterns(patterns,callback){patterns=patterns||[];function onLoaded(){if(++numLoadedPatterns===numPatterns){callback&&callback(enlivenedPatterns);}}var enlivenedPatterns=[],numLoadedPatterns=0,numPatterns=patterns.length;if(!numPatterns){callback&&callback(enlivenedPatterns);return;}patterns.forEach(function(p,index){if(p&&p.source){new fabric.Pattern(p,function(pattern){enlivenedPatterns[index]=pattern;onLoaded();});}else{enlivenedPatterns[index]=p;onLoaded();}});},/**\n * Groups SVG elements (usually those retrieved from SVG document)\n * @static\n * @memberOf fabric.util\n * @param {Array} elements SVG elements to group\n * @param {Object} [options] Options object\n * @param {String} path Value to set sourcePath to\n * @return {fabric.Object|fabric.Group}\n */groupSVGElements:function groupSVGElements(elements,options,path){var object;if(elements&&elements.length===1){return elements[0];}if(options){if(options.width&&options.height){options.centerPoint={x:options.width/2,y:options.height/2};}else{delete options.width;delete options.height;}}object=new fabric.Group(elements,options);if(typeof path!=='undefined'){object.sourcePath=path;}return object;},/**\n * Populates an object with properties of another object\n * @static\n * @memberOf fabric.util\n * @param {Object} source Source object\n * @param {Object} destination Destination object\n * @return {Array} properties Properties names to include\n */populateWithProperties:function populateWithProperties(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]];}}}},/**\n * Draws a dashed line between two points\n *\n * This method is used to draw dashed line around selection area.\n * See <a href=\"http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas\">dotted stroke in canvas</a>\n *\n * @param {CanvasRenderingContext2D} ctx context\n * @param {Number} x start x coordinate\n * @param {Number} y start y coordinate\n * @param {Number} x2 end x coordinate\n * @param {Number} y2 end y coordinate\n * @param {Array} da dash array pattern\n */drawDashedLine:function drawDashedLine(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();},/**\n * Creates canvas element\n * @static\n * @memberOf fabric.util\n * @return {CanvasElement} initialized canvas element\n */createCanvasElement:function createCanvasElement(){return fabric.document.createElement('canvas');},/**\n * Creates a canvas element that is a copy of another and is also painted\n * @param {CanvasElement} canvas to copy size and content of\n * @static\n * @memberOf fabric.util\n * @return {CanvasElement} initialized canvas element\n */copyCanvasElement:function copyCanvasElement(canvas){var newCanvas=fabric.util.createCanvasElement();newCanvas.width=canvas.width;newCanvas.height=canvas.height;newCanvas.getContext('2d').drawImage(canvas,0,0);return newCanvas;},/**\n * since 2.6.0 moved from canvas instance to utility.\n * @param {CanvasElement} canvasEl to copy size and content of\n * @param {String} format 'jpeg' or 'png', in some browsers 'webp' is ok too\n * @param {Number} quality <= 1 and > 0\n * @static\n * @memberOf fabric.util\n * @return {String} data url\n */toDataURL:function toDataURL(canvasEl,format,quality){return canvasEl.toDataURL('image/'+format,quality);},/**\n * Creates image element (works on client and node)\n * @static\n * @memberOf fabric.util\n * @return {HTMLImageElement} HTML image element\n */createImage:function createImage(){return fabric.document.createElement('img');},/**\n * @static\n * @memberOf fabric.util\n * @deprecated since 2.0.0\n * @param {fabric.Object} receiver Object implementing `clipTo` method\n * @param {CanvasRenderingContext2D} ctx Context to clip\n */clipContext:function clipContext(receiver,ctx){ctx.save();ctx.beginPath();receiver.clipTo(ctx);ctx.clip();},/**\n * Multiply matrix A by matrix B to nest transformations\n * @static\n * @memberOf fabric.util\n * @param {Array} a First transformMatrix\n * @param {Array} b Second transformMatrix\n * @param {Boolean} is2x2 flag to multiply matrices as 2x2 matrices\n * @return {Array} The product of the two transform matrices\n */multiplyTransformMatrices:function multiplyTransformMatrices(a,b,is2x2){// Matrix multiply a * b\nreturn[a[0]*b[0]+a[2]*b[1],a[1]*b[0]+a[3]*b[1],a[0]*b[2]+a[2]*b[3],a[1]*b[2]+a[3]*b[3],is2x2?0:a[0]*b[4]+a[2]*b[5]+a[4],is2x2?0:a[1]*b[4]+a[3]*b[5]+a[5]];},/**\n * Decomposes standard 2x3 matrix into transform components\n * @static\n * @memberOf fabric.util\n * @param {Array} a transformMatrix\n * @return {Object} Components of transform\n */qrDecompose:function qrDecompose(a){var angle=atan2(a[1],a[0]),denom=pow(a[0],2)+pow(a[1],2),scaleX=sqrt(denom),scaleY=(a[0]*a[3]-a[2]*a[1])/scaleX,skewX=atan2(a[0]*a[2]+a[1]*a[3],denom);return{angle:angle/PiBy180,scaleX:scaleX,scaleY:scaleY,skewX:skewX/PiBy180,skewY:0,translateX:a[4],translateY:a[5]};},/**\n * Returns a transform matrix starting from an object of the same kind of\n * the one returned from qrDecompose, useful also if you want to calculate some\n * transformations from an object that is not enlived yet\n * @static\n * @memberOf fabric.util\n * @param {Object} options\n * @param {Number} [options.angle] angle in degrees\n * @return {Number[]} transform matrix\n */calcRotateMatrix:function calcRotateMatrix(options){if(!options.angle){return fabric.iMatrix.concat();}var theta=fabric.util.degreesToRadians(options.angle),cos=fabric.util.cos(theta),sin=fabric.util.sin(theta);return[cos,sin,-sin,cos,0,0];},/**\n * Returns a transform matrix starting from an object of the same kind of\n * the one returned from qrDecompose, useful also if you want to calculate some\n * transformations from an object that is not enlived yet.\n * is called DimensionsTransformMatrix because those properties are the one that influence\n * the size of the resulting box of the object.\n * @static\n * @memberOf fabric.util\n * @param {Object} options\n * @param {Number} [options.scaleX]\n * @param {Number} [options.scaleY]\n * @param {Boolean} [options.flipX]\n * @param {Boolean} [options.flipY]\n * @param {Number} [options.skewX]\n * @param {Number} [options.skewX]\n * @return {Number[]} transform matrix\n */calcDimensionsMatrix:function calcDimensionsMatrix(options){var scaleX=typeof options.scaleX==='undefined'?1:options.scaleX,scaleY=typeof options.scaleY==='undefined'?1:options.scaleY,scaleMatrix=[options.flipX?-scaleX:scaleX,0,0,options.flipY?-scaleY:scaleY,0,0],multiply=fabric.util.multiplyTransformMatrices,degreesToRadians=fabric.util.degreesToRadians;if(options.skewX){scaleMatrix=multiply(scaleMatrix,[1,0,Math.tan(degreesToRadians(options.skewX)),1],true);}if(options.skewY){scaleMatrix=multiply(scaleMatrix,[1,Math.tan(degreesToRadians(options.skewY)),0,1],true);}return scaleMatrix;},/**\n * Returns a transform matrix starting from an object of the same kind of\n * the one returned from qrDecompose, useful also if you want to calculate some\n * transformations from an object that is not enlived yet\n * @static\n * @memberOf fabric.util\n * @param {Object} options\n * @param {Number} [options.angle]\n * @param {Number} [options.scaleX]\n * @param {Number} [options.scaleY]\n * @param {Boolean} [options.flipX]\n * @param {Boolean} [options.flipY]\n * @param {Number} [options.skewX]\n * @param {Number} [options.skewX]\n * @param {Number} [options.translateX]\n * @param {Number} [options.translateY]\n * @return {Number[]} transform matrix\n */composeMatrix:function composeMatrix(options){var matrix=[1,0,0,1,options.translateX||0,options.translateY||0],multiply=fabric.util.multiplyTransformMatrices;if(options.angle){matrix=multiply(matrix,fabric.util.calcRotateMatrix(options));}if(options.scaleX||options.scaleY||options.skewX||options.skewY||options.flipX||options.flipY){matrix=multiply(matrix,fabric.util.calcDimensionsMatrix(options));}return matrix;},/**\n * Returns a transform matrix that has the same effect of scaleX, scaleY and skewX.\n * Is deprecated for composeMatrix. Please do not use it.\n * @static\n * @deprecated since 3.4.0\n * @memberOf fabric.util\n * @param {Number} scaleX\n * @param {Number} scaleY\n * @param {Number} skewX\n * @return {Number[]} transform matrix\n */customTransformMatrix:function customTransformMatrix(scaleX,scaleY,skewX){return fabric.util.composeMatrix({scaleX:scaleX,scaleY:scaleY,skewX:skewX});},/**\n * reset an object transform state to neutral. Top and left are not accounted for\n * @static\n * @memberOf fabric.util\n * @param {fabric.Object} target object to transform\n */resetObjectTransform:function resetObjectTransform(target){target.scaleX=1;target.scaleY=1;target.skewX=0;target.skewY=0;target.flipX=false;target.flipY=false;target.rotate(0);},/**\n * Extract Object transform values\n * @static\n * @memberOf fabric.util\n * @param {fabric.Object} target object to read from\n * @return {Object} Components of transform\n */saveObjectTransform:function saveObjectTransform(target){return{scaleX:target.scaleX,scaleY:target.scaleY,skewX:target.skewX,skewY:target.skewY,angle:target.angle,left:target.left,flipX:target.flipX,flipY:target.flipY,top:target.top};},/**\n * Returns string representation of function body\n * @param {Function} fn Function to get body of\n * @return {String} Function body\n */getFunctionBody:function getFunctionBody(fn){return(String(fn).match(/function[^{]*\\{([\\s\\S]*)\\}/)||{})[1];},/**\n * Returns true if context has transparent pixel\n * at specified location (taking tolerance into account)\n * @param {CanvasRenderingContext2D} ctx context\n * @param {Number} x x coordinate\n * @param {Number} y y coordinate\n * @param {Number} tolerance Tolerance\n */isTransparent:function isTransparent(ctx,x,y,tolerance){// If tolerance is > 0 adjust start coords to take into account.\n// If moves off Canvas fix to 0\nif(tolerance>0){if(x>tolerance){x-=tolerance;}else{x=0;}if(y>tolerance){y-=tolerance;}else{y=0;}}var _isTransparent=true,i,temp,imageData=ctx.getImageData(x,y,tolerance*2||1,tolerance*2||1),l=imageData.data.length;// Split image data - for tolerance > 1, pixelDataSize = 4;\nfor(i=3;i<l;i+=4){temp=imageData.data[i];_isTransparent=temp<=0;if(_isTransparent===false){break;// Stop if colour found\n}}imageData=null;return _isTransparent;},/**\n * Parse preserveAspectRatio attribute from element\n * @param {string} attribute to be parsed\n * @return {Object} an object containing align and meetOrSlice attribute\n */parsePreserveAspectRatioAttribute:function parsePreserveAspectRatioAttribute(attribute){var meetOrSlice='meet',alignX='Mid',alignY='Mid',aspectRatioAttrs=attribute.split(' '),align;if(aspectRatioAttrs&&aspectRatioAttrs.length){meetOrSlice=aspectRatioAttrs.pop();if(meetOrSlice!=='meet'&&meetOrSlice!=='slice'){align=meetOrSlice;meetOrSlice='meet';}else if(aspectRatioAttrs.length){align=aspectRatioAttrs.pop();}}//divide align in alignX and alignY\nalignX=align!=='none'?align.slice(1,4):'none';alignY=align!=='none'?align.slice(5,8):'none';return{meetOrSlice:meetOrSlice,alignX:alignX,alignY:alignY};},/**\n * Clear char widths cache for the given font family or all the cache if no\n * fontFamily is specified.\n * Use it if you know you are loading fonts in a lazy way and you are not waiting\n * for custom fonts to load properly when adding text objects to the canvas.\n * If a text object is added when its own font is not loaded yet, you will get wrong\n * measurement and so wrong bounding boxes.\n * After the font cache is cleared, either change the textObject text content or call\n * initDimensions() to trigger a recalculation\n * @memberOf fabric.util\n * @param {String} [fontFamily] font family to clear\n */clearFabricFontCache:function clearFabricFontCache(fontFamily){fontFamily=(fontFamily||'').toLowerCase();if(!fontFamily){fabric.charWidthsCache={};}else if(fabric.charWidthsCache[fontFamily]){delete fabric.charWidthsCache[fontFamily];}},/**\n * Given current aspect ratio, determines the max width and height that can\n * respect the total allowed area for the cache.\n * @memberOf fabric.util\n * @param {Number} ar aspect ratio\n * @param {Number} maximumArea Maximum area you want to achieve\n * @return {Object.x} Limited dimensions by X\n * @return {Object.y} Limited dimensions by Y\n */limitDimsByArea:function limitDimsByArea(ar,maximumArea){var roughWidth=Math.sqrt(maximumArea*ar),perfLimitSizeY=Math.floor(maximumArea/roughWidth);return{x:Math.floor(roughWidth),y:perfLimitSizeY};},capValue:function capValue(min,value,max){return Math.max(min,Math.min(value,max));},findScaleToFit:function findScaleToFit(source,destination){return Math.min(destination.width/source.width,destination.height/source.height);},findScaleToCover:function findScaleToCover(source,destination){return Math.max(destination.width/source.width,destination.height/source.height);},/**\n * given an array of 6 number returns something like `\"matrix(...numbers)\"`\n * @memberOf fabric.util\n * @param {Array} trasnform an array with 6 numbers\n * @return {String} transform matrix for svg\n * @return {Object.y} Limited dimensions by Y\n */matrixToSVG:function matrixToSVG(transform){return'matrix('+transform.map(function(value){return fabric.util.toFixed(value,fabric.Object.NUM_FRACTION_DIGITS);}).join(' ')+')';}};})( true?exports:undefined);(function(){var _join=Array.prototype.join;/* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp\n * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here\n * http://mozilla.org/MPL/2.0/\n */function arcToSegments(toX,toY,rx,ry,large,sweep,rotateX){var argsString=_join.call(arguments);if(fabric.arcToSegmentsCache[argsString]){return fabric.arcToSegmentsCache[argsString];}var PI=Math.PI,th=rotateX*PI/180,sinTh=fabric.util.sin(th),cosTh=fabric.util.cos(th),fromX=0,fromY=0;rx=Math.abs(rx);ry=Math.abs(ry);var px=-cosTh*toX*0.5-sinTh*toY*0.5,py=-cosTh*toY*0.5+sinTh*toX*0.5,rx2=rx*rx,ry2=ry*ry,py2=py*py,px2=px*px,pl=rx2*ry2-rx2*py2-ry2*px2,root=0;if(pl<0){var s=Math.sqrt(1-pl/(rx2*ry2));rx*=s;ry*=s;}else{root=(large===sweep?-1.0:1.0)*Math.sqrt(pl/(rx2*py2+ry2*px2));}var cx=root*rx*py/ry,cy=-root*ry*px/rx,cx1=cosTh*cx-sinTh*cy+toX*0.5,cy1=sinTh*cx+cosTh*cy+toY*0.5,mTheta=calcVectorAngle(1,0,(px-cx)/rx,(py-cy)/ry),dtheta=calcVectorAngle((px-cx)/rx,(py-cy)/ry,(-px-cx)/rx,(-py-cy)/ry);if(sweep===0&&dtheta>0){dtheta-=2*PI;}else if(sweep===1&&dtheta<0){dtheta+=2*PI;}// Convert into cubic bezier segments <= 90deg\nvar segments=Math.ceil(Math.abs(dtheta/PI*2)),result=[],mDelta=dtheta/segments,mT=8/3*Math.sin(mDelta/4)*Math.sin(mDelta/4)/Math.sin(mDelta/2),th3=mTheta+mDelta;for(var i=0;i<segments;i++){result[i]=segmentToBezier(mTheta,th3,cosTh,sinTh,rx,ry,cx1,cy1,mT,fromX,fromY);fromX=result[i][4];fromY=result[i][5];mTheta=th3;th3+=mDelta;}fabric.arcToSegmentsCache[argsString]=result;return result;}function segmentToBezier(th2,th3,cosTh,sinTh,rx,ry,cx1,cy1,mT,fromX,fromY){var costh2=fabric.util.cos(th2),sinth2=fabric.util.sin(th2),costh3=fabric.util.cos(th3),sinth3=fabric.util.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);return[cp1X,cp1Y,cp2X,cp2Y,toX,toY];}/*\n * Private\n */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);}}/**\n * Draws arc\n * @param {CanvasRenderingContext2D} ctx\n * @param {Number} fx\n * @param {Number} fy\n * @param {Array} coords\n */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]);}};/**\n * Calculate bounding box of a elliptic-arc\n * @param {Number} fx start point of arc\n * @param {Number} fy\n * @param {Number} rx horizontal radius\n * @param {Number} ry vertical radius\n * @param {Number} rot angle of horizontal axe\n * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points\n * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction\n * @param {Number} tx end point of arc\n * @param {Number} ty\n */fabric.util.getBoundsOfArc=function(fx,fy,rx,ry,rot,large,sweep,tx,ty){var fromX=0,fromY=0,bound,bounds=[],segs=arcToSegments(tx-fx,ty-fy,rx,ry,large,sweep,rot);for(var i=0,len=segs.length;i<len;i++){bound=getBoundsOfCurve(fromX,fromY,segs[i][0],segs[i][1],segs[i][2],segs[i][3],segs[i][4],segs[i][5]);bounds.push({x:bound[0].x+fx,y:bound[0].y+fy});bounds.push({x:bound[1].x+fx,y:bound[1].y+fy});fromX=segs[i][4];fromY=segs[i][5];}return bounds;};/**\n * Calculate bounding box of a beziercurve\n * @param {Number} x0 starting point\n * @param {Number} y0\n * @param {Number} x1 first control point\n * @param {Number} y1\n * @param {Number} x2 secondo control point\n * @param {Number} y2\n * @param {Number} x3 end of beizer\n * @param {Number} y3\n */ // taken from http://jsbin.com/ivomiq/56/edit no credits available for that.\nfunction getBoundsOfCurve(x0,y0,x1,y1,x2,y2,x3,y3){var argsString;if(fabric.cachesBoundsOfCurve){argsString=_join.call(arguments);if(fabric.boundsOfCurveCache[argsString]){return fabric.boundsOfCurveCache[argsString];}}var sqrt=Math.sqrt,min=Math.min,max=Math.max,abs=Math.abs,tvalues=[],bounds=[[],[]],a,b,c,t,t1,t2,b2ac,sqrtb2ac;b=6*x0-12*x1+6*x2;a=-3*x0+9*x1-9*x2+3*x3;c=3*x1-3*x0;for(var i=0;i<2;++i){if(i>0){b=6*y0-12*y1+6*y2;a=-3*y0+9*y1-9*y2+3*y3;c=3*y1-3*y0;}if(abs(a)<1e-12){if(abs(b)<1e-12){continue;}t=-c/b;if(0<t&&t<1){tvalues.push(t);}continue;}b2ac=b*b-4*c*a;if(b2ac<0){continue;}sqrtb2ac=sqrt(b2ac);t1=(-b+sqrtb2ac)/(2*a);if(0<t1&&t1<1){tvalues.push(t1);}t2=(-b-sqrtb2ac)/(2*a);if(0<t2&&t2<1){tvalues.push(t2);}}var x,y,j=tvalues.length,jlen=j,mt;while(j--){t=tvalues[j];mt=1-t;x=mt*mt*mt*x0+3*mt*mt*t*x1+3*mt*t*t*x2+t*t*t*x3;bounds[0][j]=x;y=mt*mt*mt*y0+3*mt*mt*t*y1+3*mt*t*t*y2+t*t*t*y3;bounds[1][j]=y;}bounds[0][jlen]=x0;bounds[1][jlen]=y0;bounds[0][jlen+1]=x3;bounds[1][jlen+1]=y3;var result=[{x:min.apply(null,bounds[0]),y:min.apply(null,bounds[1])},{x:max.apply(null,bounds[0]),y:max.apply(null,bounds[1])}];if(fabric.cachesBoundsOfCurve){fabric.boundsOfCurveCache[argsString]=result;}return result;}fabric.util.getBoundsOfCurve=getBoundsOfCurve;})();(function(){var slice=Array.prototype.slice;/**\n * Invokes method on all items in a given array\n * @memberOf fabric.util.array\n * @param {Array} array Array to iterate over\n * @param {String} method Name of a method to invoke\n * @return {Array}\n */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;}/**\n * Finds maximum value in array (not necessarily \"first\" one)\n * @memberOf fabric.util.array\n * @param {Array} array Array to iterate over\n * @param {String} byProperty\n * @return {*}\n */function max(array,byProperty){return find(array,byProperty,function(value1,value2){return value1>=value2;});}/**\n * Finds minimum value in array (not necessarily \"first\" one)\n * @memberOf fabric.util.array\n * @param {Array} array Array to iterate over\n * @param {String} byProperty\n * @return {*}\n */function min(array,byProperty){return find(array,byProperty,function(value1,value2){return value1<value2;});}/**\n * @private\n */function fill(array,value){var k=array.length;while(k--){array[k]=value;}return array;}/**\n * @private\n */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;}/**\n * @namespace fabric.util.array\n */fabric.util.array={fill:fill,invoke:invoke,min:min,max:max};})();(function(){/**\n * Copies all enumerable properties of one js object to another\n * this does not and cannot compete with generic utils.\n * Does not clone or extend fabric.Object subclasses.\n * This is mostly for internal use and has extra handling for fabricJS objects\n * it skips the canvas property in deep cloning.\n * @memberOf fabric.util.object\n * @param {Object} destination Where to copy to\n * @param {Object} source Where to copy from\n * @return {Object}\n */function extend(destination,source,deep){// JScript DontEnum bug is not taken care of\n// the deep clone is for internal use, is not meant to avoid\n// javascript traps or cloning html element or self referenced objects.\nif(deep){if(!fabric.isLikelyNode&&source instanceof Element){// avoid cloning deep images, canvases,\ndestination=source;}else if(source instanceof Array){destination=[];for(var i=0,len=source.length;i<len;i++){destination[i]=extend({},source[i],deep);}}else if(source&&_typeof(source)==='object'){for(var property in source){if(property==='canvas'){destination[property]=extend({},source[property]);}else if(source.hasOwnProperty(property)){destination[property]=extend({},source[property],deep);}}}else{// this sounds odd for an extend but is ok for recursive use\ndestination=source;}}else{for(var property in source){destination[property]=source[property];}}return destination;}/**\n * Creates an empty object and copies all enumerable properties of another object to it\n * @memberOf fabric.util.object\n * TODO: this function return an empty object if you try to clone null\n * @param {Object} object Object to clone\n * @return {Object}\n */function clone(object,deep){return extend({},object,deep);}/** @namespace fabric.util.object */fabric.util.object={extend:extend,clone:clone};fabric.util.object.extend(fabric.util,fabric.Observable);})();(function(){/**\n * Camelizes a string\n * @memberOf fabric.util.string\n * @param {String} string String to camelize\n * @return {String} Camelized version of a string\n */function camelize(string){return string.replace(/-+(.)?/g,function(match,character){return character?character.toUpperCase():'';});}/**\n * Capitalizes a string\n * @memberOf fabric.util.string\n * @param {String} string String to capitalize\n * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized\n * and other letters stay untouched, if false first letter is capitalized\n * and other letters are converted to lowercase.\n * @return {String} Capitalized version of a string\n */function capitalize(string,firstLetterOnly){return string.charAt(0).toUpperCase()+(firstLetterOnly?string.slice(1):string.slice(1).toLowerCase());}/**\n * Escapes XML in a string\n * @memberOf fabric.util.string\n * @param {String} string String to escape\n * @return {String} Escaped version of a string\n */function escapeXml(string){return string.replace(/&/g,'&').replace(/\"/g,'"').replace(/'/g,''').replace(/</g,'<').replace(/>/g,'>');}/**\n * Divide a string in the user perceived single units\n * @memberOf fabric.util.string\n * @param {String} textstring String to escape\n * @return {Array} array containing the graphemes\n */function graphemeSplit(textstring){var i=0,chr,graphemes=[];for(i=0,chr;i<textstring.length;i++){if((chr=getWholeChar(textstring,i))===false){continue;}graphemes.push(chr);}return graphemes;}// taken from mdn in the charAt doc page.\nfunction getWholeChar(str,i){var code=str.charCodeAt(i);if(isNaN(code)){return'';// Position not found\n}if(code<0xD800||code>0xDFFF){return str.charAt(i);}// High surrogate (could change last hex to 0xDB7F to treat high private\n// surrogates as single characters)\nif(0xD800<=code&&code<=0xDBFF){if(str.length<=i+1){throw'High surrogate without following low surrogate';}var next=str.charCodeAt(i+1);if(0xDC00>next||next>0xDFFF){throw'High surrogate without following low surrogate';}return str.charAt(i)+str.charAt(i+1);}// Low surrogate (0xDC00 <= code && code <= 0xDFFF)\nif(i===0){throw'Low surrogate without preceding high surrogate';}var prev=str.charCodeAt(i-1);// (could change last hex to 0xDB7F to treat high private\n// surrogates as single characters)\nif(0xD800>prev||prev>0xDBFF){throw'Low surrogate without preceding high surrogate';}// We can pass over low surrogates now as the second component\n// in a pair which we have already processed\nreturn false;}/**\n * String utilities\n * @namespace fabric.util.string\n */fabric.util.string={camelize:camelize,capitalize:capitalize,escapeXml:escapeXml,graphemeSplit:graphemeSplit};})();(function(){var slice=Array.prototype.slice,emptyFunction=function emptyFunction(){},IS_DONTENUM_BUGGY=function(){for(var p in{toString:1}){if(p==='toString'){return false;}}return true;}(),/** @ignore */addMethods=function addMethods(klass,source,parent){for(var property in source){if(property in klass.prototype&&typeof klass.prototype[property]==='function'&&(source[property]+'').indexOf('callSuper')>-1){klass.prototype[property]=function(property){return function(){var superclass=this.constructor.superclass;this.constructor.superclass=parent;var returnValue=source[property].apply(this,arguments);this.constructor.superclass=superclass;if(property!=='initialize'){return returnValue;}};}(property);}else{klass.prototype[property]=source[property];}if(IS_DONTENUM_BUGGY){if(source.toString!==Object.prototype.toString){klass.prototype.toString=source.toString;}if(source.valueOf!==Object.prototype.valueOf){klass.prototype.valueOf=source.valueOf;}}}};function Subclass(){}function callSuper(methodName){var parentMethod=null,_this=this;// climb prototype chain to find method not equal to callee's method\nwhile(_this.constructor.superclass){var superClassMethod=_this.constructor.superclass.prototype[methodName];if(_this[methodName]!==superClassMethod){parentMethod=superClassMethod;break;}// eslint-disable-next-line\n_this=_this.constructor.superclass.prototype;}if(!parentMethod){return console.log('tried to callSuper '+methodName+', method not found in prototype chain',this);}return arguments.length>1?parentMethod.apply(this,slice.call(arguments,1)):parentMethod.call(this);}/**\n * Helper for creation of \"classes\".\n * @memberOf fabric.util\n * @param {Function} [parent] optional \"Class\" to inherit from\n * @param {Object} [properties] Properties shared by all instances of this class\n * (be careful modifying objects defined here as this would affect all instances)\n */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(){// since ie10 or ie9 can use addEventListener but they do not support options, i need to check\nvar couldUseAttachEvent=!!fabric.document.createElement('div').attachEvent;/**\n * Adds an event listener to an element\n * @function\n * @memberOf fabric.util\n * @param {HTMLElement} element\n * @param {String} eventName\n * @param {Function} handler\n */fabric.util.addListener=function(element,eventName,handler,options){element&&element.addEventListener(eventName,handler,couldUseAttachEvent?false:options);};/**\n * Removes an event listener from an element\n * @function\n * @memberOf fabric.util\n * @param {HTMLElement} element\n * @param {String} eventName\n * @param {Function} handler\n */fabric.util.removeListener=function(element,eventName,handler,options){element&&element.removeEventListener(eventName,handler,couldUseAttachEvent?false:options);};function getTouchInfo(event){var touchProp=event.changedTouches;if(touchProp&&touchProp[0]){return touchProp[0];}return event;}fabric.util.getPointer=function(event){var element=event.target,scroll=fabric.util.getScrollLeftTop(element),_evt=getTouchInfo(event);return{x:_evt.clientX+scroll.left,y:_evt.clientY+scroll.top};};})();(function(){/**\n * Cross-browser wrapper for setting element's style\n * @memberOf fabric.util\n * @param {HTMLElement} element\n * @param {Object} styles\n * @return {HTMLElement} Element that was passed as a first argument\n */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 setOpacity(element){return element;};if(supportsOpacity){/** @ignore */setOpacity=function setOpacity(element,value){element.style.opacity=value;return element;};}else if(supportsFilters){/** @ignore */setOpacity=function setOpacity(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;/**\n * Takes id and returns an element with that id (if one exists in a document)\n * @memberOf fabric.util\n * @param {String|HTMLElement} id\n * @return {HTMLElement|null}\n */function getById(id){return typeof id==='string'?fabric.document.getElementById(id):id;}var sliceCanConvertNodelists,/**\n * Converts an array-like object (e.g. arguments or NodeList) to an array\n * @memberOf fabric.util\n * @param {Object} arrayLike\n * @return {Array}\n */toArray=function toArray(arrayLike){return _slice.call(arrayLike,0);};try{sliceCanConvertNodelists=toArray(fabric.document.childNodes)instanceof Array;}catch(err){}if(!sliceCanConvertNodelists){toArray=function toArray(arrayLike){var arr=new Array(arrayLike.length),i=arrayLike.length;while(i--){arr[i]=arrayLike[i];}return arr;};}/**\n * Creates specified element with specified attributes\n * @memberOf fabric.util\n * @param {String} tagName Type of an element to create\n * @param {Object} [attributes] Attributes to set on an element\n * @return {HTMLElement} Newly created element\n */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;}/**\n * Adds class to an element\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to add class to\n * @param {String} className Class to add to an element\n */function addClass(element,className){if(element&&(' '+element.className+' ').indexOf(' '+className+' ')===-1){element.className+=(element.className?' ':'')+className;}}/**\n * Wraps element with another element\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to wrap\n * @param {HTMLElement|String} wrapper Element to wrap with\n * @param {Object} [attributes] Attributes to set on a wrapper\n * @return {HTMLElement} wrapper\n */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;}/**\n * Returns element scroll offsets\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to operate on\n * @return {Object} Object with left/top values\n */function getScrollLeftTop(element){var left=0,top=0,docElement=fabric.document.documentElement,body=fabric.document.body||{scrollLeft:0,scrollTop:0};// While loop checks (and then sets element to) .parentNode OR .host\n// to account for ShadowDOM. We still want to traverse up out of ShadowDOM,\n// but the .parentNode of a root ShadowDOM node will always be null, instead\n// it should be accessed through .host. See http://stackoverflow.com/a/24765528/4383938\nwhile(element&&(element.parentNode||element.host)){// Set element to element parent, or 'host' in case of ShadowDOM\nelement=element.parentNode||element.host;if(element===fabric.document){left=body.scrollLeft||docElement.scrollLeft||0;top=body.scrollTop||docElement.scrollTop||0;}else{left+=element.scrollLeft||0;top+=element.scrollTop||0;}if(element.nodeType===1&&element.style.position==='fixed'){break;}}return{left:left,top:top};}/**\n * Returns offset for a given element\n * @function\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to get offset for\n * @return {Object} Object with \"left\" and \"top\" properties\n */function getElementOffset(element){var docElem,doc=element&&element.ownerDocument,box={left:0,top:0},offset={left:0,top:0},scrollLeftTop,offsetAttributes={borderLeftWidth:'left',borderTopWidth:'top',paddingLeft:'left',paddingTop:'top'};if(!doc){return offset;}for(var attr in offsetAttributes){offset[offsetAttributes[attr]]+=parseInt(getElementStyle(element,attr),10)||0;}docElem=doc.documentElement;if(typeof element.getBoundingClientRect!=='undefined'){box=element.getBoundingClientRect();}scrollLeftTop=getScrollLeftTop(element);return{left:box.left+scrollLeftTop.left-(docElem.clientLeft||0)+offset.left,top:box.top+scrollLeftTop.top-(docElem.clientTop||0)+offset.top};}/**\n * Returns style attribute value of a given element\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to get style attribute for\n * @param {String} attr Style attribute to get for element\n * @return {String} Style attribute value of the given element.\n */var getElementStyle;if(fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle){getElementStyle=function getElementStyle(element,attr){var style=fabric.document.defaultView.getComputedStyle(element,null);return style?style[attr]:undefined;};}else{getElementStyle=function getElementStyle(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':'';/**\n * Makes element unselectable\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to make unselectable\n * @return {HTMLElement} Element that was passed in\n */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;}/**\n * Makes element selectable\n * @memberOf fabric.util\n * @param {HTMLElement} element Element to make selectable\n * @return {HTMLElement} Element that was passed in\n */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(){/**\n * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading\n * @memberOf fabric.util\n * @param {String} url URL of a script to load\n * @param {Function} callback Callback to execute when script is finished loading\n */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\n// headEl.removeChild(scriptEl);\n}fabric.util.getScript=getScript;})();function getNodeCanvas(element){var impl=fabric.jsdomImplForWrapper(element);return impl._canvas||impl._image;};function cleanUpJsdomNode(element){if(!fabric.isLikelyNode){return;}var impl=fabric.jsdomImplForWrapper(element);if(impl){impl._image=null;impl._canvas=null;// unsure if necessary\nimpl._currentSrc=null;impl._attributes=null;impl._classList=null;}}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;fabric.util.getNodeCanvas=getNodeCanvas;fabric.util.cleanUpJsdomNode=cleanUpJsdomNode;})();(function(){function addParamToUrl(url,param){return url+(/\\?/.test(url)?'&':'?')+param;}function emptyFn(){}/**\n * Cross-browser abstraction for sending XMLHttpRequest\n * @memberOf fabric.util\n * @param {String} url URL to send XMLHttpRequest to\n * @param {Object} [options] Options object\n * @param {String} [options.method=\"GET\"]\n * @param {String} [options.parameters] parameters to append to url in GET or in body\n * @param {String} [options.body] body to send with POST or PUT request\n * @param {Function} options.onComplete Callback to invoke when request is completed\n * @return {XMLHttpRequest} request\n */function request(url,options){options||(options={});var method=options.method?options.method.toUpperCase():'GET',onComplete=options.onComplete||function(){},xhr=new fabric.window.XMLHttpRequest(),body=options.body||options.parameters;/** @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;})();/**\n * Wrapper around `console.log` (when available)\n * @param {*} [values] Values to log\n */fabric.log=console.log;/**\n * Wrapper around `console.warn` (when available)\n * @param {*} [values] Values to log as a warning\n */fabric.warn=console.warn;(function(){function noop(){return false;}function defaultEasing(t,b,c,d){return-c*Math.cos(t/d*(Math.PI/2))+c+b;}/**\n * Changes value from one to another within certain period of time, invoking callbacks as value is being changed.\n * @memberOf fabric.util\n * @param {Object} [options] Animation options\n * @param {Function} [options.onChange] Callback; invoked on every value change\n * @param {Function} [options.onComplete] Callback; invoked when value change is completed\n * @param {Number} [options.startValue=0] Starting value\n * @param {Number} [options.endValue=100] Ending value\n * @param {Number} [options.byValue=100] Value to modify the property by\n * @param {Function} [options.easing] Easing function\n * @param {Number} [options.duration=500] Duration of change (in ms)\n * @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.\n */function animate(options){requestAnimFrame(function(timestamp){options||(options={});var start=timestamp||+new Date(),duration=options.duration||500,finish=start+duration,time,onChange=options.onChange||noop,abort=options.abort||noop,onComplete=options.onComplete||noop,easing=options.easing||defaultEasing,startValue='startValue'in options?options.startValue:0,endValue='endValue'in options?options.endValue:100,byValue=options.byValue||endValue-startValue;options.onStart&&options.onStart();(function tick(ticktime){// TODO: move abort call after calculation\n// and pass (current,valuePerc, timePerc) as arguments\ntime=ticktime||+new Date();var currentTime=time>finish?duration:time-start,timePerc=currentTime/duration,current=easing(currentTime,startValue,byValue,duration),valuePerc=Math.abs((current-startValue)/byValue);if(abort()){onComplete(endValue,1,1);return;}if(time>finish){onChange(endValue,1,1);onComplete(endValue,1,1);return;}else{onChange(current,valuePerc,timePerc);requestAnimFrame(tick);}})(start);});}var _requestAnimFrame=fabric.window.requestAnimationFrame||fabric.window.webkitRequestAnimationFrame||fabric.window.mozRequestAnimationFrame||fabric.window.oRequestAnimationFrame||fabric.window.msRequestAnimationFrame||function(callback){return fabric.window.setTimeout(callback,1000/60);};var _cancelAnimFrame=fabric.window.cancelAnimationFrame||fabric.window.clearTimeout;/**\n * requestAnimationFrame polyfill based on http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n * In order to get a precise start time, `requestAnimFrame` should be called as an entry into the method\n * @memberOf fabric.util\n * @param {Function} callback Callback to invoke\n * @param {DOMElement} element optional Element to associate with animation\n */function requestAnimFrame(){return _requestAnimFrame.apply(fabric.window,arguments);}function cancelAnimFrame(){return _cancelAnimFrame.apply(fabric.window,arguments);}fabric.util.animate=animate;fabric.util.requestAnimFrame=requestAnimFrame;fabric.util.cancelAnimFrame=cancelAnimFrame;})();(function(){// Calculate an in-between color. Returns a \"rgba()\" string.\n// Credit: Edwin Martin <edwin@bitstorm.org>\n// http://www.bitstorm.org/jquery/color-animation/jquery.animate-colors.js\nfunction calculateColor(begin,end,pos){var color='rgba('+parseInt(begin[0]+pos*(end[0]-begin[0]),10)+','+parseInt(begin[1]+pos*(end[1]-begin[1]),10)+','+parseInt(begin[2]+pos*(end[2]-begin[2]),10);color+=','+(begin&&end?parseFloat(begin[3]+pos*(end[3]-begin[3])):1);color+=')';return color;}/**\n * Changes the color from one to another within certain period of time, invoking callbacks as value is being changed.\n * @memberOf fabric.util\n * @param {String} fromColor The starting color in hex or rgb(a) format.\n * @param {String} toColor The starting color in hex or rgb(a) format.\n * @param {Number} [duration] Duration of change (in ms).\n * @param {Object} [options] Animation options\n * @param {Function} [options.onChange] Callback; invoked on every value change\n * @param {Function} [options.onComplete] Callback; invoked when value change is completed\n * @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.\n * @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.\n */function animateColor(fromColor,toColor,duration,options){var startColor=new fabric.Color(fromColor).getSource(),endColor=new fabric.Color(toColor).getSource();options=options||{};fabric.util.animate(fabric.util.object.extend(options,{duration:duration||500,startValue:startColor,endValue:endColor,byValue:endColor,easing:function easing(currentTime,startValue,byValue,duration){var posValue=options.colorEasing?options.colorEasing(currentTime,duration):1-Math.cos(currentTime/duration*(Math.PI/2));return calculateColor(startValue,byValue,posValue);}}));}fabric.util.animateColor=animateColor;})();(function(){function normalize(a,c,p,s){if(a<Math.abs(c)){a=c;s=p/4;}else{//handle the 0/0 case:\nif(c===0&&a===0){s=p/(2*Math.PI)*Math.asin(1);}else{s=p/(2*Math.PI)*Math.asin(c/a);}}return{a:a,c:c,p:p,s:s};}function elastic(opts,t,d){return opts.a*Math.pow(2,10*(t-=1))*Math.sin((t*d-opts.s)*(2*Math.PI)/opts.p);}/**\n * Cubic easing out\n * @memberOf fabric.util.ease\n */function easeOutCubic(t,b,c,d){return c*((t=t/d-1)*t*t+1)+b;}/**\n * Cubic easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutCubic(t,b,c,d){t/=d/2;if(t<1){return c/2*t*t*t+b;}return c/2*((t-=2)*t*t+2)+b;}/**\n * Quartic easing in\n * @memberOf fabric.util.ease\n */function easeInQuart(t,b,c,d){return c*(t/=d)*t*t*t+b;}/**\n * Quartic easing out\n * @memberOf fabric.util.ease\n */function easeOutQuart(t,b,c,d){return-c*((t=t/d-1)*t*t*t-1)+b;}/**\n * Quartic easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutQuart(t,b,c,d){t/=d/2;if(t<1){return c/2*t*t*t*t+b;}return-c/2*((t-=2)*t*t*t-2)+b;}/**\n * Quintic easing in\n * @memberOf fabric.util.ease\n */function easeInQuint(t,b,c,d){return c*(t/=d)*t*t*t*t+b;}/**\n * Quintic easing out\n * @memberOf fabric.util.ease\n */function easeOutQuint(t,b,c,d){return c*((t=t/d-1)*t*t*t*t+1)+b;}/**\n * Quintic easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutQuint(t,b,c,d){t/=d/2;if(t<1){return c/2*t*t*t*t*t+b;}return c/2*((t-=2)*t*t*t*t+2)+b;}/**\n * Sinusoidal easing in\n * @memberOf fabric.util.ease\n */function easeInSine(t,b,c,d){return-c*Math.cos(t/d*(Math.PI/2))+c+b;}/**\n * Sinusoidal easing out\n * @memberOf fabric.util.ease\n */function easeOutSine(t,b,c,d){return c*Math.sin(t/d*(Math.PI/2))+b;}/**\n * Sinusoidal easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutSine(t,b,c,d){return-c/2*(Math.cos(Math.PI*t/d)-1)+b;}/**\n * Exponential easing in\n * @memberOf fabric.util.ease\n */function easeInExpo(t,b,c,d){return t===0?b:c*Math.pow(2,10*(t/d-1))+b;}/**\n * Exponential easing out\n * @memberOf fabric.util.ease\n */function easeOutExpo(t,b,c,d){return t===d?b+c:c*(-Math.pow(2,-10*t/d)+1)+b;}/**\n * Exponential easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutExpo(t,b,c,d){if(t===0){return b;}if(t===d){return b+c;}t/=d/2;if(t<1){return c/2*Math.pow(2,10*(t-1))+b;}return c/2*(-Math.pow(2,-10*--t)+2)+b;}/**\n * Circular easing in\n * @memberOf fabric.util.ease\n */function easeInCirc(t,b,c,d){return-c*(Math.sqrt(1-(t/=d)*t)-1)+b;}/**\n * Circular easing out\n * @memberOf fabric.util.ease\n */function easeOutCirc(t,b,c,d){return c*Math.sqrt(1-(t=t/d-1)*t)+b;}/**\n * Circular easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutCirc(t,b,c,d){t/=d/2;if(t<1){return-c/2*(Math.sqrt(1-t*t)-1)+b;}return c/2*(Math.sqrt(1-(t-=2)*t)+1)+b;}/**\n * Elastic easing in\n * @memberOf fabric.util.ease\n */function easeInElastic(t,b,c,d){var s=1.70158,p=0,a=c;if(t===0){return b;}t/=d;if(t===1){return b+c;}if(!p){p=d*0.3;}var opts=normalize(a,c,p,s);return-elastic(opts,t,d)+b;}/**\n * Elastic easing out\n * @memberOf fabric.util.ease\n */function easeOutElastic(t,b,c,d){var s=1.70158,p=0,a=c;if(t===0){return b;}t/=d;if(t===1){return b+c;}if(!p){p=d*0.3;}var opts=normalize(a,c,p,s);return opts.a*Math.pow(2,-10*t)*Math.sin((t*d-opts.s)*(2*Math.PI)/opts.p)+opts.c+b;}/**\n * Elastic easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutElastic(t,b,c,d){var s=1.70158,p=0,a=c;if(t===0){return b;}t/=d/2;if(t===2){return b+c;}if(!p){p=d*(0.3*1.5);}var opts=normalize(a,c,p,s);if(t<1){return-0.5*elastic(opts,t,d)+b;}return opts.a*Math.pow(2,-10*(t-=1))*Math.sin((t*d-opts.s)*(2*Math.PI)/opts.p)*0.5+opts.c+b;}/**\n * Backwards easing in\n * @memberOf fabric.util.ease\n */function easeInBack(t,b,c,d,s){if(s===undefined){s=1.70158;}return c*(t/=d)*t*((s+1)*t-s)+b;}/**\n * Backwards easing out\n * @memberOf fabric.util.ease\n */function easeOutBack(t,b,c,d,s){if(s===undefined){s=1.70158;}return c*((t=t/d-1)*t*((s+1)*t+s)+1)+b;}/**\n * Backwards easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutBack(t,b,c,d,s){if(s===undefined){s=1.70158;}t/=d/2;if(t<1){return c/2*(t*t*(((s*=1.525)+1)*t-s))+b;}return c/2*((t-=2)*t*(((s*=1.525)+1)*t+s)+2)+b;}/**\n * Bouncing easing in\n * @memberOf fabric.util.ease\n */function easeInBounce(t,b,c,d){return c-easeOutBounce(d-t,0,c,d)+b;}/**\n * Bouncing easing out\n * @memberOf fabric.util.ease\n */function easeOutBounce(t,b,c,d){if((t/=d)<1/2.75){return c*(7.5625*t*t)+b;}else if(t<2/2.75){return c*(7.5625*(t-=1.5/2.75)*t+0.75)+b;}else if(t<2.5/2.75){return c*(7.5625*(t-=2.25/2.75)*t+0.9375)+b;}else{return c*(7.5625*(t-=2.625/2.75)*t+0.984375)+b;}}/**\n * Bouncing easing in and out\n * @memberOf fabric.util.ease\n */function easeInOutBounce(t,b,c,d){if(t<d/2){return easeInBounce(t*2,0,c,d)*0.5+b;}return easeOutBounce(t*2-d,0,c,d)*0.5+c*0.5+b;}/**\n * Easing functions\n * See <a href=\"http://gizma.com/easing/\">Easing Equations by Robert Penner</a>\n * @namespace fabric.util.ease\n */fabric.util.ease={/**\n * Quadratic easing in\n * @memberOf fabric.util.ease\n */easeInQuad:function easeInQuad(t,b,c,d){return c*(t/=d)*t+b;},/**\n * Quadratic easing out\n * @memberOf fabric.util.ease\n */easeOutQuad:function easeOutQuad(t,b,c,d){return-c*(t/=d)*(t-2)+b;},/**\n * Quadratic easing in and out\n * @memberOf fabric.util.ease\n */easeInOutQuad:function easeInOutQuad(t,b,c,d){t/=d/2;if(t<1){return c/2*t*t+b;}return-c/2*(--t*(t-2)-1)+b;},/**\n * Cubic easing in\n * @memberOf fabric.util.ease\n */easeInCubic:function easeInCubic(t,b,c,d){return c*(t/=d)*t*t+b;},easeOutCubic:easeOutCubic,easeInOutCubic:easeInOutCubic,easeInQuart:easeInQuart,easeOutQuart:easeOutQuart,easeInOutQuart:easeInOutQuart,easeInQuint:easeInQuint,easeOutQuint:easeOutQuint,easeInOutQuint:easeInOutQuint,easeInSine:easeInSine,easeOutSine:easeOutSine,easeInOutSine:easeInOutSine,easeInExpo:easeInExpo,easeOutExpo:easeOutExpo,easeInOutExpo:easeInOutExpo,easeInCirc:easeInCirc,easeOutCirc:easeOutCirc,easeInOutCirc:easeInOutCirc,easeInElastic:easeInElastic,easeOutElastic:easeOutElastic,easeInOutElastic:easeInOutElastic,easeInBack:easeInBack,easeOutBack:easeOutBack,easeInOutBack:easeInOutBack,easeInBounce:easeInBounce,easeOutBounce:easeOutBounce,easeInOutBounce:easeInOutBounce};})();(function(global){'use strict';/**\n * @name fabric\n * @namespace\n */var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend,clone=fabric.util.object.clone,toFixed=fabric.util.toFixed,parseUnit=fabric.util.parseUnit,multiplyTransformMatrices=fabric.util.multiplyTransformMatrices,svgValidTagNames=['path','circle','polygon','polyline','ellipse','rect','line','image','text'],svgViewBoxElements=['symbol','image','marker','pattern','view','svg'],svgInvalidAncestors=['pattern','defs','symbol','metadata','clipPath','mask','desc'],svgValidParents=['symbol','g','a','svg','clipPath','defs'],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','letter-spacing':'charSpacing','paint-order':'paintFirst','stroke-dasharray':'strokeDashArray','stroke-dashoffset':'strokeDashOffset','stroke-linecap':'strokeLineCap','stroke-linejoin':'strokeLineJoin','stroke-miterlimit':'strokeMiterLimit','stroke-opacity':'strokeOpacity','stroke-width':'strokeWidth','text-decoration':'textDecoration','text-anchor':'textAnchor',opacity:'opacity','clip-path':'clipPath','clip-rule':'clipRule','vector-effect':'strokeUniform'},colorAttributes={stroke:'strokeOpacity',fill:'fillOpacity'},fSize='font-size',cPath='clip-path';fabric.svgValidTagNamesRegEx=getSvgRegex(svgValidTagNames);fabric.svgViewBoxElementsRegEx=getSvgRegex(svgViewBoxElements);fabric.svgInvalidAncestorsRegEx=getSvgRegex(svgInvalidAncestors);fabric.svgValidParentsRegEx=getSvgRegex(svgValidParents);fabric.cssRules={};fabric.gradientDefs={};fabric.clipPaths={};function normalizeAttr(attr){// transform attribute names\nif(attr in attributesMap){return attributesMap[attr];}return attr;}function normalizeValue(attr,value,parentAttributes,fontSize){var isArray=Object.prototype.toString.call(value)==='[object Array]',parsed;if((attr==='fill'||attr==='stroke')&&value==='none'){value='';}else if(attr==='vector-effect'){value=value==='non-scaling-stroke';}else if(attr==='strokeDashArray'){if(value==='none'){value=null;}else{value=value.replace(/,/g,' ').split(/\\s+/).map(parseFloat);}}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';// display=none on parent element always takes precedence over child element\nif(parentAttributes&&parentAttributes.visible===false){value=false;}}else if(attr==='opacity'){value=parseFloat(value);if(parentAttributes&&typeof parentAttributes.opacity!=='undefined'){value*=parentAttributes.opacity;}}else if(attr==='textAnchor'/* text-anchor */){value=value==='start'?'left':value==='end'?'right':'center';}else if(attr==='charSpacing'){// parseUnit returns px and we convert it to em\nparsed=parseUnit(value,fontSize)/fontSize*1000;}else if(attr==='paintFirst'){var fillIndex=value.indexOf('fill');var strokeIndex=value.indexOf('stroke');var value='fill';if(fillIndex>-1&&strokeIndex>-1&&strokeIndex<fillIndex){value='stroke';}else if(fillIndex===-1&&strokeIndex>-1){value='stroke';}}else if(attr==='href'||attr==='xlink:href'){return value;}else{parsed=isArray?value.map(parseUnit):parseUnit(value,fontSize);}return!isArray&&isNaN(parsed)?value:parsed;}/**\n * @private\n */function getSvgRegex(arr){return new RegExp('^('+arr.join('|')+')\\\\b','i');}/**\n * @private\n * @param {Object} attributes Array of attributes to parse\n */function _setStrokeFillOpacity(attributes){for(var attr in colorAttributes){if(typeof attributes[colorAttributes[attr]]==='undefined'||attributes[attr]===''){continue;}if(typeof attributes[attr]==='undefined'){if(!fabric.Object.prototype[attr]){continue;}attributes[attr]=fabric.Object.prototype[attr];}if(attributes[attr].indexOf('url(')===0){continue;}var color=new fabric.Color(attributes[attr]);attributes[attr]=color.setAlpha(toFixed(color.getAlpha()*attributes[colorAttributes[attr]],2)).toRgba();}return attributes;}/**\n * @private\n */function _getMultipleNodes(doc,nodeNames){var nodeName,nodeArray=[],nodeList,i,len;for(i=0,len=nodeNames.length;i<len;i++){nodeName=nodeNames[i];nodeList=doc.getElementsByTagName(nodeName);nodeArray=nodeArray.concat(Array.prototype.slice.call(nodeList));}return nodeArray;}/**\n * Parses \"transform\" attribute, returning an array of values\n * @static\n * @function\n * @memberOf fabric\n * @param {String} attributeValue String containing attribute value\n * @return {Array} Array of 6 elements representing transformation matrix\n */fabric.parseTransformAttribute=function(){function rotateMatrix(matrix,args){var cos=fabric.util.cos(args[0]),sin=fabric.util.sin(args[0]),x=0,y=0;if(args.length===3){x=args[1];y=args[2];}matrix[0]=cos;matrix[1]=sin;matrix[2]=-sin;matrix[3]=cos;matrix[4]=x-(cos*x-sin*y);matrix[5]=y-(sin*x+cos*y);}function scaleMatrix(matrix,args){var multiplierX=args[0],multiplierY=args.length===2?args[1]:args[0];matrix[0]=multiplierX;matrix[3]=multiplierY;}function skewMatrix(matrix,args,pos){matrix[pos]=Math.tan(fabric.util.degreesToRadians(args[0]));}function translateMatrix(matrix,args){matrix[4]=args[0];if(args.length===2){matrix[5]=args[1];}}// identity matrix\nvar iMatrix=fabric.iMatrix,// == begin transform regexp\nnumber=fabric.reNum,commaWsp='(?:\\\\s+,?\\\\s*|,\\\\s*)',skewX='(?:(skewX)\\\\s*\\\\(\\\\s*('+number+')\\\\s*\\\\))',skewY='(?:(skewY)\\\\s*\\\\(\\\\s*('+number+')\\\\s*\\\\))',rotate='(?:(rotate)\\\\s*\\\\(\\\\s*('+number+')(?:'+commaWsp+'('+number+')'+commaWsp+'('+number+'))?\\\\s*\\\\))',scale='(?:(scale)\\\\s*\\\\(\\\\s*('+number+')(?:'+commaWsp+'('+number+'))?\\\\s*\\\\))',translate='(?:(translate)\\\\s*\\\\(\\\\s*('+number+')(?:'+commaWsp+'('+number+'))?\\\\s*\\\\))',matrix='(?:(matrix)\\\\s*\\\\(\\\\s*'+'('+number+')'+commaWsp+'('+number+')'+commaWsp+'('+number+')'+commaWsp+'('+number+')'+commaWsp+'('+number+')'+commaWsp+'('+number+')'+'\\\\s*\\\\))',transform='(?:'+matrix+'|'+translate+'|'+scale+'|'+rotate+'|'+skewX+'|'+skewY+')',transforms='(?:'+transform+'(?:'+commaWsp+'*'+transform+')*'+')',transformList='^\\\\s*(?:'+transforms+'?)\\\\s*$',// http://www.w3.org/TR/SVG/coords.html#TransformAttribute\nreTransformList=new RegExp(transformList),// == end transform regexp\nreTransform=new RegExp(transform,'g');return function(attributeValue){// start with identity matrix\nvar matrix=iMatrix.concat(),matrices=[];// return if no argument was given or\n// an argument does not match transform attribute regexp\nif(!attributeValue||attributeValue&&!reTransformList.test(attributeValue)){return matrix;}attributeValue.replace(reTransform,function(match){var m=new RegExp(transform).exec(match).filter(function(match){// match !== '' && match != null\nreturn!!match;}),operation=m[1],args=m.slice(2).map(parseFloat);switch(operation){case'translate':translateMatrix(matrix,args);break;case'rotate':args[0]=fabric.util.degreesToRadians(args[0]);rotateMatrix(matrix,args);break;case'scale':scaleMatrix(matrix,args);break;case'skewX':skewMatrix(matrix,args,2);break;case'skewY':skewMatrix(matrix,args,1);break;case'matrix':matrix=args;break;}// snapshot current matrix into matrices array\nmatrices.push(matrix.concat());// reset\nmatrix=iMatrix.concat();});var combinedMatrix=matrices[0];while(matrices.length>1){matrices.shift();combinedMatrix=fabric.util.multiplyTransformMatrices(combinedMatrix,matrices[0]);}return combinedMatrix;};}();/**\n * @private\n */function parseStyleString(style,oStyle){var attr,value;style.replace(/;\\s*$/,'').split(';').forEach(function(chunk){var pair=chunk.split(':');attr=pair[0].trim().toLowerCase();value=pair[1].trim();oStyle[attr]=value;});}/**\n * @private\n */function parseStyleObject(style,oStyle){var attr,value;for(var prop in style){if(typeof style[prop]==='undefined'){continue;}attr=prop.toLowerCase();value=style[prop];oStyle[attr]=value;}}/**\n * @private\n */function getGlobalStylesForElement(element,svgUid){var styles={};for(var rule in fabric.cssRules[svgUid]){if(elementMatchesRule(element,rule.split(' '))){for(var property in fabric.cssRules[svgUid][rule]){styles[property]=fabric.cssRules[svgUid][rule][property];}}}return styles;}/**\n * @private\n */function elementMatchesRule(element,selectors){var firstMatching,parentMatching=true;//start from rightmost selector.\nfirstMatching=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;}/**\n * @private\n */function selectorMatches(element,selector){var nodeName=element.nodeName,classNames=element.getAttribute('class'),id=element.getAttribute('id'),matcher,i;// i check if a selector matches slicing away part from it.\n// if i get empty string i should match\nmatcher=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(i=classNames.length;i--;){matcher=new RegExp('\\\\.'+classNames[i]+'(?![a-zA-Z\\\\-]+)','i');selector=selector.replace(matcher,'');}}return selector.length===0;}/**\n * @private\n * to support IE8 missing getElementById on SVGdocument and on node xmlDOM\n */function elementById(doc,id){var el;doc.getElementById&&(el=doc.getElementById(id));if(el){return el;}var node,i,len,nodelist=doc.getElementsByTagName('*');for(i=0,len=nodelist.length;i<len;i++){node=nodelist[i];if(id===node.getAttribute('id')){return node;}}}/**\n * @private\n */function parseUseDirectives(doc){var nodelist=_getMultipleNodes(doc,['use','svg:use']),i=0;while(nodelist.length&&i<nodelist.length){var el=nodelist[i],xlink=(el.getAttribute('xlink:href')||el.getAttribute('href')).substr(1),x=el.getAttribute('x')||0,y=el.getAttribute('y')||0,el2=elementById(doc,xlink).cloneNode(true),currentTrans=(el2.getAttribute('transform')||'')+' translate('+x+', '+y+')',parentNode,oldLength=nodelist.length,attr,j,attrs,len,namespace=fabric.svgNS;applyViewboxTransform(el2);if(/^svg$/i.test(el2.nodeName)){var el3=el2.ownerDocument.createElementNS(namespace,'g');for(j=0,attrs=el2.attributes,len=attrs.length;j<len;j++){attr=attrs.item(j);el3.setAttributeNS(namespace,attr.nodeName,attr.nodeValue);}// el2.firstChild != null\nwhile(el2.firstChild){el3.appendChild(el2.firstChild);}el2=el3;}for(j=0,attrs=el.attributes,len=attrs.length;j<len;j++){attr=attrs.item(j);if(attr.nodeName==='x'||attr.nodeName==='y'||attr.nodeName==='xlink:href'||attr.nodeName==='href'){continue;}if(attr.nodeName==='transform'){currentTrans=attr.nodeValue+' '+currentTrans;}else{el2.setAttribute(attr.nodeName,attr.nodeValue);}}el2.setAttribute('transform',currentTrans);el2.setAttribute('instantiated_by_use','1');el2.removeAttribute('id');parentNode=el.parentNode;parentNode.replaceChild(el2,el);// some browsers do not shorten nodelist after replaceChild (IE8)\nif(nodelist.length===oldLength){i++;}}}// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute\n// matches, e.g.: +14.56e-12, etc.\nvar reViewBoxAttrValue=new RegExp('^'+'\\\\s*('+fabric.reNum+'+)\\\\s*,?'+'\\\\s*('+fabric.reNum+'+)\\\\s*,?'+'\\\\s*('+fabric.reNum+'+)\\\\s*,?'+'\\\\s*('+fabric.reNum+'+)\\\\s*'+'$');/**\n * Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements\n */function applyViewboxTransform(element){var viewBoxAttr=element.getAttribute('viewBox'),scaleX=1,scaleY=1,minX=0,minY=0,viewBoxWidth,viewBoxHeight,matrix,el,widthAttr=element.getAttribute('width'),heightAttr=element.getAttribute('height'),x=element.getAttribute('x')||0,y=element.getAttribute('y')||0,preserveAspectRatio=element.getAttribute('preserveAspectRatio')||'',missingViewBox=!viewBoxAttr||!fabric.svgViewBoxElementsRegEx.test(element.nodeName)||!(viewBoxAttr=viewBoxAttr.match(reViewBoxAttrValue)),missingDimAttr=!widthAttr||!heightAttr||widthAttr==='100%'||heightAttr==='100%',toBeParsed=missingViewBox&&missingDimAttr,parsedDim={},translateMatrix='',widthDiff=0,heightDiff=0;parsedDim.width=0;parsedDim.height=0;parsedDim.toBeParsed=toBeParsed;if(toBeParsed){return parsedDim;}if(missingViewBox){parsedDim.width=parseUnit(widthAttr);parsedDim.height=parseUnit(heightAttr);return parsedDim;}minX=-parseFloat(viewBoxAttr[1]);minY=-parseFloat(viewBoxAttr[2]);viewBoxWidth=parseFloat(viewBoxAttr[3]);viewBoxHeight=parseFloat(viewBoxAttr[4]);parsedDim.minX=minX;parsedDim.minY=minY;parsedDim.viewBoxWidth=viewBoxWidth;parsedDim.viewBoxHeight=viewBoxHeight;if(!missingDimAttr){parsedDim.width=parseUnit(widthAttr);parsedDim.height=parseUnit(heightAttr);scaleX=parsedDim.width/viewBoxWidth;scaleY=parsedDim.height/viewBoxHeight;}else{parsedDim.width=viewBoxWidth;parsedDim.height=viewBoxHeight;}// default is to preserve aspect ratio\npreserveAspectRatio=fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio);if(preserveAspectRatio.alignX!=='none'){//translate all container for the effect of Mid, Min, Max\nif(preserveAspectRatio.meetOrSlice==='meet'){scaleY=scaleX=scaleX>scaleY?scaleY:scaleX;// calculate additional translation to move the viewbox\n}if(preserveAspectRatio.meetOrSlice==='slice'){scaleY=scaleX=scaleX>scaleY?scaleX:scaleY;// calculate additional translation to move the viewbox\n}widthDiff=parsedDim.width-viewBoxWidth*scaleX;heightDiff=parsedDim.height-viewBoxHeight*scaleX;if(preserveAspectRatio.alignX==='Mid'){widthDiff/=2;}if(preserveAspectRatio.alignY==='Mid'){heightDiff/=2;}if(preserveAspectRatio.alignX==='Min'){widthDiff=0;}if(preserveAspectRatio.alignY==='Min'){heightDiff=0;}}if(scaleX===1&&scaleY===1&&minX===0&&minY===0&&x===0&&y===0){return parsedDim;}if(x||y){translateMatrix=' translate('+parseUnit(x)+' '+parseUnit(y)+') ';}matrix=translateMatrix+' matrix('+scaleX+' 0'+' 0 '+scaleY+' '+(minX*scaleX+widthDiff)+' '+(minY*scaleY+heightDiff)+') ';parsedDim.viewboxTransform=fabric.parseTransformAttribute(matrix);if(element.nodeName==='svg'){el=element.ownerDocument.createElementNS(fabric.svgNS,'g');// element.firstChild != null\nwhile(element.firstChild){el.appendChild(element.firstChild);}element.appendChild(el);}else{el=element;matrix=el.getAttribute('transform')+matrix;}el.setAttribute('transform',matrix);return parsedDim;}function hasAncestorWithNodeName(element,nodeName){while(element&&(element=element.parentNode)){if(element.nodeName&&nodeName.test(element.nodeName.replace('svg:',''))&&!element.getAttribute('instantiated_by_use')){return true;}}return false;}/**\n * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback\n * @static\n * @function\n * @memberOf fabric\n * @param {SVGDocument} doc SVG document to parse\n * @param {Function} callback Callback to call when parsing is finished;\n * It's being passed an array of elements (parsed from a document).\n * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.\n * @param {Object} [parsingOptions] options for parsing document\n * @param {String} [parsingOptions.crossOrigin] crossOrigin settings\n */fabric.parseSVGDocument=function(doc,callback,reviver,parsingOptions){if(!doc){return;}parseUseDirectives(doc);var svgUid=fabric.Object.__uid++,i,len,options=applyViewboxTransform(doc),descendants=fabric.util.toArray(doc.getElementsByTagName('*'));options.crossOrigin=parsingOptions&&parsingOptions.crossOrigin;options.svgUid=svgUid;if(descendants.length===0&&fabric.isLikelyNode){// we're likely in node, where \"o3-xml\" library fails to gEBTN(\"*\")\n// https://github.com/ajaxorg/node-o3-xml/issues/21\ndescendants=doc.selectNodes('//*[name(.)!=\"svg\"]');var arr=[];for(i=0,len=descendants.length;i<len;i++){arr[i]=descendants[i];}descendants=arr;}var elements=descendants.filter(function(el){applyViewboxTransform(el);return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:',''))&&!hasAncestorWithNodeName(el,fabric.svgInvalidAncestorsRegEx);// http://www.w3.org/TR/SVG/struct.html#DefsElement\n});if(!elements||elements&&!elements.length){callback&&callback([],{});return;}var clipPaths={};descendants.filter(function(el){return el.nodeName.replace('svg:','')==='clipPath';}).forEach(function(el){var id=el.getAttribute('id');clipPaths[id]=fabric.util.toArray(el.getElementsByTagName('*')).filter(function(el){return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:',''));});});fabric.gradientDefs[svgUid]=fabric.getGradientDefs(doc);fabric.cssRules[svgUid]=fabric.getCSSRules(doc);fabric.clipPaths[svgUid]=clipPaths;// Precedence of rules: style > class > attribute\nfabric.parseElements(elements,function(instances,elements){if(callback){callback(instances,options,elements,descendants);delete fabric.gradientDefs[svgUid];delete fabric.cssRules[svgUid];delete fabric.clipPaths[svgUid];}},clone(options),reviver,parsingOptions);};function recursivelyParseGradientsXlink(doc,gradient){var gradientsAttrs=['gradientTransform','x1','x2','y1','y2','gradientUnits','cx','cy','r','fx','fy'],xlinkAttr='xlink:href',xLink=gradient.getAttribute(xlinkAttr).substr(1),referencedGradient=elementById(doc,xLink);if(referencedGradient&&referencedGradient.getAttribute(xlinkAttr)){recursivelyParseGradientsXlink(doc,referencedGradient);}gradientsAttrs.forEach(function(attr){if(referencedGradient&&!gradient.hasAttribute(attr)&&referencedGradient.hasAttribute(attr)){gradient.setAttribute(attr,referencedGradient.getAttribute(attr));}});if(!gradient.children.length){var referenceClone=referencedGradient.cloneNode(true);while(referenceClone.firstChild){gradient.appendChild(referenceClone.firstChild);}}gradient.removeAttribute(xlinkAttr);}var reFontDeclaration=new RegExp('(normal|italic)?\\\\s*(normal|small-caps)?\\\\s*'+'(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\\\s*('+fabric.reNum+'(?:px|cm|mm|em|pt|pc|in)*)(?:\\\\/(normal|'+fabric.reNum+'))?\\\\s+(.*)');extend(fabric,{/**\n * Parses a short font declaration, building adding its properties to a style object\n * @static\n * @function\n * @memberOf fabric\n * @param {String} value font declaration\n * @param {Object} oStyle definition\n */parseFontDeclaration:function parseFontDeclaration(value,oStyle){var match=value.match(reFontDeclaration);if(!match){return;}var fontStyle=match[1],// font variant is not used\n// fontVariant = match[2],\nfontWeight=match[3],fontSize=match[4],lineHeight=match[5],fontFamily=match[6];if(fontStyle){oStyle.fontStyle=fontStyle;}if(fontWeight){oStyle.fontWeight=isNaN(parseFloat(fontWeight))?fontWeight:parseFloat(fontWeight);}if(fontSize){oStyle.fontSize=parseUnit(fontSize);}if(fontFamily){oStyle.fontFamily=fontFamily;}if(lineHeight){oStyle.lineHeight=lineHeight==='normal'?1:lineHeight;}},/**\n * Parses an SVG document, returning all of the gradient declarations found in it\n * @static\n * @function\n * @memberOf fabric\n * @param {SVGDocument} doc SVG document to parse\n * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element\n */getGradientDefs:function getGradientDefs(doc){var tagArray=['linearGradient','radialGradient','svg:linearGradient','svg:radialGradient'],elList=_getMultipleNodes(doc,tagArray),el,j=0,gradientDefs={};j=elList.length;while(j--){el=elList[j];if(el.getAttribute('xlink:href')){recursivelyParseGradientsXlink(doc,el);}gradientDefs[el.getAttribute('id')]=el;}return gradientDefs;},/**\n * Returns an object of attributes' name/value, given element and an array of attribute names;\n * Parses parent \"g\" nodes recursively upwards.\n * @static\n * @memberOf fabric\n * @param {DOMElement} element Element to parse\n * @param {Array} attributes Array of attributes to parse\n * @return {Object} object containing parsed attributes' names/values\n */parseAttributes:function parseAttributes(element,attributes,svgUid){if(!element){return;}var value,parentAttributes={},fontSize,parentFontSize;if(typeof svgUid==='undefined'){svgUid=element.getAttribute('svgUid');}// if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards\nif(element.parentNode&&fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)){parentAttributes=fabric.parseAttributes(element.parentNode,attributes,svgUid);}var ownAttributes=attributes.reduce(function(memo,attr){value=element.getAttribute(attr);if(value){// eslint-disable-line\nmemo[attr]=value;}return memo;},{});// add values parsed from style, which take precedence over attributes\n// (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)\nvar cssAttrs=extend(getGlobalStylesForElement(element,svgUid),fabric.parseStyleAttribute(element));ownAttributes=extend(ownAttributes,cssAttrs);if(cssAttrs[cPath]){element.setAttribute(cPath,cssAttrs[cPath]);}fontSize=parentFontSize=parentAttributes.fontSize||fabric.Text.DEFAULT_SVG_FONT_SIZE;if(ownAttributes[fSize]){// looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers.\nownAttributes[fSize]=fontSize=parseUnit(ownAttributes[fSize],parentFontSize);}var normalizedAttr,normalizedValue,normalizedStyle={};for(var attr in ownAttributes){normalizedAttr=normalizeAttr(attr);normalizedValue=normalizeValue(normalizedAttr,ownAttributes[attr],parentAttributes,fontSize);normalizedStyle[normalizedAttr]=normalizedValue;}if(normalizedStyle&&normalizedStyle.font){fabric.parseFontDeclaration(normalizedStyle.font,normalizedStyle);}var mergedAttrs=extend(parentAttributes,normalizedStyle);return fabric.svgValidParentsRegEx.test(element.nodeName)?mergedAttrs:_setStrokeFillOpacity(mergedAttrs);},/**\n * Transforms an array of svg elements to corresponding fabric.* instances\n * @static\n * @memberOf fabric\n * @param {Array} elements Array of elements to parse\n * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)\n * @param {Object} [options] Options object\n * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.\n */parseElements:function parseElements(elements,callback,options,reviver,parsingOptions){new fabric.ElementsParser(elements,callback,options,reviver,parsingOptions).parse();},/**\n * Parses \"style\" attribute, retuning an object with values\n * @static\n * @memberOf fabric\n * @param {SVGElement} element Element to parse\n * @return {Object} Objects with values parsed from style attribute of an element\n */parseStyleAttribute:function parseStyleAttribute(element){var oStyle={},style=element.getAttribute('style');if(!style){return oStyle;}if(typeof style==='string'){parseStyleString(style,oStyle);}else{parseStyleObject(style,oStyle);}return oStyle;},/**\n * Parses \"points\" attribute, returning an array of values\n * @static\n * @memberOf fabric\n * @param {String} points points attribute string\n * @return {Array} array of points\n */parsePointsAttribute:function parsePointsAttribute(points){// points attribute is required and must not be empty\nif(!points){return null;}// replace commas with whitespace and remove bookending whitespace\npoints=points.replace(/,/g,' ').trim();points=points.split(/\\s+/);var parsedPoints=[],i,len;for(i=0,len=points.length;i<len;i+=2){parsedPoints.push({x:parseFloat(points[i]),y:parseFloat(points[i+1])});}// odd number of points is an error\n// if (parsedPoints.length % 2 !== 0) {\n// return null;\n// }\nreturn parsedPoints;},/**\n * Returns CSS rules for a given SVG document\n * @static\n * @function\n * @memberOf fabric\n * @param {SVGDocument} doc SVG document to parse\n * @return {Object} CSS rules of this document\n */getCSSRules:function getCSSRules(doc){var styles=doc.getElementsByTagName('style'),i,len,allRules={},rules;// very crude parsing of style contents\nfor(i=0,len=styles.length;i<len;i++){// IE9 doesn't support textContent, but provides text instead.\nvar styleContents=styles[i].textContent||styles[i].text;// remove comments\nif(!styleContents)continue;styleContents=styleContents.replace(/\\/\\*[\\s\\S]*?\\*\\//g,'');if(styleContents.trim()===''){continue;}rules=styleContents.match(/[^{]*\\{[\\s\\S]*?\\}/g);rules=rules.map(function(rule){return rule.trim();});// eslint-disable-next-line no-loop-func\nrules.forEach(function(rule){var match=rule.match(/([\\s\\S]*?)\\s*\\{([^}]*)\\}/),ruleObj={},declaration=match[2].trim(),propertyValuePairs=declaration.replace(/;$/,'').split(/\\s*;\\s*/);for(i=0,len=propertyValuePairs.length;i<len;i++){var pair=propertyValuePairs[i].split(/\\s*:\\s*/),property=pair[0],value=pair[1];ruleObj[property]=value;}rule=match[1];rule.split(',').forEach(function(_rule){_rule=_rule.replace(/^svg/i,'').trim();if(_rule===''){return;}if(allRules[_rule]){fabric.util.object.extend(allRules[_rule],ruleObj);}else{allRules[_rule]=fabric.util.object.clone(ruleObj);}});});}return allRules;},/**\n * Takes url corresponding to an SVG document, and parses it into a set of fabric objects.\n * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)\n * @memberOf fabric\n * @param {String} url\n * @param {Function} callback\n * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.\n * @param {Object} [options] Object containing options for parsing\n * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources\n */loadSVGFromURL:function loadSVGFromURL(url,callback,reviver,options){url=url.replace(/^\\n\\s*/,'').trim();new fabric.util.request(url,{method:'get',onComplete:onComplete});function onComplete(r){var xml=r.responseXML;if(xml&&!xml.documentElement&&fabric.window.ActiveXObject&&r.responseText){xml=new ActiveXObject('Microsoft.XMLDOM');xml.async='false';//IE chokes on DOCTYPE\nxml.loadXML(r.responseText.replace(/<!DOCTYPE[\\s\\S]*?(\\[[\\s\\S]*\\])*?>/i,''));}if(!xml||!xml.documentElement){callback&&callback(null);return false;}fabric.parseSVGDocument(xml.documentElement,function(results,_options,elements,allElements){callback&&callback(results,_options,elements,allElements);},reviver,options);}},/**\n * Takes string corresponding to an SVG document, and parses it into a set of fabric objects\n * @memberOf fabric\n * @param {String} string\n * @param {Function} callback\n * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.\n * @param {Object} [options] Object containing options for parsing\n * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources\n */loadSVGFromString:function loadSVGFromString(string,callback,reviver,options){string=string.trim();var doc;if(typeof fabric.window.DOMParser!=='undefined'){var parser=new fabric.window.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\ndoc.loadXML(string.replace(/<!DOCTYPE[\\s\\S]*?(\\[[\\s\\S]*\\])*?>/i,''));}fabric.parseSVGDocument(doc.documentElement,function(results,_options,elements,allElements){callback(results,_options,elements,allElements);},reviver,options);}});})( true?exports:undefined);fabric.ElementsParser=function(elements,callback,options,reviver,parsingOptions,doc){this.elements=elements;this.callback=callback;this.options=options;this.reviver=reviver;this.svgUid=options&&options.svgUid||0;this.parsingOptions=parsingOptions;this.regexUrl=/^url\\(['\"]?#([^'\"]+)['\"]?\\)/g;this.doc=doc;};(function(proto){proto.parse=function(){this.instances=new Array(this.elements.length);this.numElements=this.elements.length;this.createObjects();};proto.createObjects=function(){var _this=this;this.elements.forEach(function(element,i){element.setAttribute('svgUid',_this.svgUid);_this.createObject(element,i);});};proto.findTag=function(el){return fabric[fabric.util.string.capitalize(el.tagName.replace('svg:',''))];};proto.createObject=function(el,index){var klass=this.findTag(el);if(klass&&klass.fromElement){try{klass.fromElement(el,this.createCallback(index,el),this.options);}catch(err){fabric.log(err);}}else{this.checkIfDone();}};proto.createCallback=function(index,el){var _this=this;return function(obj){var _options;_this.resolveGradient(obj,el,'fill');_this.resolveGradient(obj,el,'stroke');if(obj instanceof fabric.Image&&obj._originalElement){_options=obj.parsePreserveAspectRatioAttribute(el);}obj._removeTransformMatrix(_options);_this.resolveClipPath(obj,el);_this.reviver&&_this.reviver(el,obj);_this.instances[index]=obj;_this.checkIfDone();};};proto.extractPropertyDefinition=function(obj,property,storage){var value=obj[property],regex=this.regexUrl;if(!regex.test(value)){return;}regex.lastIndex=0;var id=regex.exec(value)[1];regex.lastIndex=0;return fabric[storage][this.svgUid][id];};proto.resolveGradient=function(obj,el,property){var gradientDef=this.extractPropertyDefinition(obj,property,'gradientDefs');if(gradientDef){var opacityAttr=el.getAttribute(property+'-opacity');var gradient=fabric.Gradient.fromElement(gradientDef,obj,opacityAttr,this.options);obj.set(property,gradient);}};proto.createClipPathCallback=function(obj,container){return function(_newObj){_newObj._removeTransformMatrix();_newObj.fillRule=_newObj.clipRule;container.push(_newObj);};};proto.resolveClipPath=function(obj,usingElement){var clipPath=this.extractPropertyDefinition(obj,'clipPath','clipPaths'),element,klass,objTransformInv,container,gTransform,options;if(clipPath){container=[];objTransformInv=fabric.util.invertTransform(obj.calcTransformMatrix());// move the clipPath tag as sibling to the real element that is using it\nvar clipPathTag=clipPath[0].parentNode;var clipPathOwner=usingElement;while(clipPathOwner.parentNode&&clipPathOwner.getAttribute('clip-path')!==obj.clipPath){clipPathOwner=clipPathOwner.parentNode;}clipPathOwner.parentNode.appendChild(clipPathTag);for(var i=0;i<clipPath.length;i++){element=clipPath[i];klass=this.findTag(element);klass.fromElement(element,this.createClipPathCallback(obj,container),this.options);}if(container.length===1){clipPath=container[0];}else{clipPath=new fabric.Group(container);}gTransform=fabric.util.multiplyTransformMatrices(objTransformInv,clipPath.calcTransformMatrix());if(clipPath.clipPath){this.resolveClipPath(clipPath,clipPathOwner);}var options=fabric.util.qrDecompose(gTransform);clipPath.flipX=false;clipPath.flipY=false;clipPath.set('scaleX',options.scaleX);clipPath.set('scaleY',options.scaleY);clipPath.angle=options.angle;clipPath.skewX=options.skewX;clipPath.skewY=0;clipPath.setPositionByOrigin({x:options.translateX,y:options.translateY},'center','center');obj.clipPath=clipPath;}};proto.checkIfDone=function(){if(--this.numElements===0){this.instances=this.instances.filter(function(el){// eslint-disable-next-line no-eq-null, eqeqeq\nreturn el!=null;});this.callback(this.instances,this.elements);}};})(fabric.ElementsParser.prototype);(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;/**\n * Point class\n * @class fabric.Point\n * @memberOf fabric\n * @constructor\n * @param {Number} x\n * @param {Number} y\n * @return {fabric.Point} thisArg\n */function Point(x,y){this.x=x;this.y=y;}Point.prototype=/** @lends fabric.Point.prototype */{type:'point',constructor:Point,/**\n * Adds another point to this one and returns another one\n * @param {fabric.Point} that\n * @return {fabric.Point} new Point instance with added values\n */add:function add(that){return new Point(this.x+that.x,this.y+that.y);},/**\n * Adds another point to this one\n * @param {fabric.Point} that\n * @return {fabric.Point} thisArg\n * @chainable\n */addEquals:function addEquals(that){this.x+=that.x;this.y+=that.y;return this;},/**\n * Adds value to this point and returns a new one\n * @param {Number} scalar\n * @return {fabric.Point} new Point with added value\n */scalarAdd:function scalarAdd(scalar){return new Point(this.x+scalar,this.y+scalar);},/**\n * Adds value to this point\n * @param {Number} scalar\n * @return {fabric.Point} thisArg\n * @chainable\n */scalarAddEquals:function scalarAddEquals(scalar){this.x+=scalar;this.y+=scalar;return this;},/**\n * Subtracts another point from this point and returns a new one\n * @param {fabric.Point} that\n * @return {fabric.Point} new Point object with subtracted values\n */subtract:function subtract(that){return new Point(this.x-that.x,this.y-that.y);},/**\n * Subtracts another point from this point\n * @param {fabric.Point} that\n * @return {fabric.Point} thisArg\n * @chainable\n */subtractEquals:function subtractEquals(that){this.x-=that.x;this.y-=that.y;return this;},/**\n * Subtracts value from this point and returns a new one\n * @param {Number} scalar\n * @return {fabric.Point}\n */scalarSubtract:function scalarSubtract(scalar){return new Point(this.x-scalar,this.y-scalar);},/**\n * Subtracts value from this point\n * @param {Number} scalar\n * @return {fabric.Point} thisArg\n * @chainable\n */scalarSubtractEquals:function scalarSubtractEquals(scalar){this.x-=scalar;this.y-=scalar;return this;},/**\n * Multiplies this point by a value and returns a new one\n * TODO: rename in scalarMultiply in 2.0\n * @param {Number} scalar\n * @return {fabric.Point}\n */multiply:function multiply(scalar){return new Point(this.x*scalar,this.y*scalar);},/**\n * Multiplies this point by a value\n * TODO: rename in scalarMultiplyEquals in 2.0\n * @param {Number} scalar\n * @return {fabric.Point} thisArg\n * @chainable\n */multiplyEquals:function multiplyEquals(scalar){this.x*=scalar;this.y*=scalar;return this;},/**\n * Divides this point by a value and returns a new one\n * TODO: rename in scalarDivide in 2.0\n * @param {Number} scalar\n * @return {fabric.Point}\n */divide:function divide(scalar){return new Point(this.x/scalar,this.y/scalar);},/**\n * Divides this point by a value\n * TODO: rename in scalarDivideEquals in 2.0\n * @param {Number} scalar\n * @return {fabric.Point} thisArg\n * @chainable\n */divideEquals:function divideEquals(scalar){this.x/=scalar;this.y/=scalar;return this;},/**\n * Returns true if this point is equal to another one\n * @param {fabric.Point} that\n * @return {Boolean}\n */eq:function eq(that){return this.x===that.x&&this.y===that.y;},/**\n * Returns true if this point is less than another one\n * @param {fabric.Point} that\n * @return {Boolean}\n */lt:function lt(that){return this.x<that.x&&this.y<that.y;},/**\n * Returns true if this point is less than or equal to another one\n * @param {fabric.Point} that\n * @return {Boolean}\n */lte:function lte(that){return this.x<=that.x&&this.y<=that.y;},/**\n\n * Returns true if this point is greater another one\n * @param {fabric.Point} that\n * @return {Boolean}\n */gt:function gt(that){return this.x>that.x&&this.y>that.y;},/**\n * Returns true if this point is greater than or equal to another one\n * @param {fabric.Point} that\n * @return {Boolean}\n */gte:function gte(that){return this.x>=that.x&&this.y>=that.y;},/**\n * Returns new point which is the result of linear interpolation with this one and another one\n * @param {fabric.Point} that\n * @param {Number} t , position of interpolation, between 0 and 1 default 0.5\n * @return {fabric.Point}\n */lerp:function lerp(that,t){if(typeof t==='undefined'){t=0.5;}t=Math.max(Math.min(1,t),0);return new Point(this.x+(that.x-this.x)*t,this.y+(that.y-this.y)*t);},/**\n * Returns distance from this point and another one\n * @param {fabric.Point} that\n * @return {Number}\n */distanceFrom:function distanceFrom(that){var dx=this.x-that.x,dy=this.y-that.y;return Math.sqrt(dx*dx+dy*dy);},/**\n * Returns the point between this point and another one\n * @param {fabric.Point} that\n * @return {fabric.Point}\n */midPointFrom:function midPointFrom(that){return this.lerp(that);},/**\n * Returns a new point which is the min of this and another one\n * @param {fabric.Point} that\n * @return {fabric.Point}\n */min:function min(that){return new Point(Math.min(this.x,that.x),Math.min(this.y,that.y));},/**\n * Returns a new point which is the max of this and another one\n * @param {fabric.Point} that\n * @return {fabric.Point}\n */max:function max(that){return new Point(Math.max(this.x,that.x),Math.max(this.y,that.y));},/**\n * Returns string representation of this point\n * @return {String}\n */toString:function toString(){return this.x+','+this.y;},/**\n * Sets x/y of this point\n * @param {Number} x\n * @param {Number} y\n * @chainable\n */setXY:function setXY(x,y){this.x=x;this.y=y;return this;},/**\n * Sets x of this point\n * @param {Number} x\n * @chainable\n */setX:function setX(x){this.x=x;return this;},/**\n * Sets y of this point\n * @param {Number} y\n * @chainable\n */setY:function setY(y){this.y=y;return this;},/**\n * Sets x/y of this point from another point\n * @param {fabric.Point} that\n * @chainable\n */setFromPoint:function setFromPoint(that){this.x=that.x;this.y=that.y;return this;},/**\n * Swaps x/y of this point and another point\n * @param {fabric.Point} that\n */swap:function swap(that){var x=this.x,y=this.y;this.x=that.x;this.y=that.y;that.x=x;that.y=y;},/**\n * return a cloned instance of the point\n * @return {fabric.Point}\n */clone:function clone(){return new Point(this.x,this.y);}};})( true?exports:undefined);(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;}/**\n * Intersection class\n * @class fabric.Intersection\n * @memberOf fabric\n * @constructor\n */function Intersection(status){this.status=status;this.points=[];}fabric.Intersection=Intersection;fabric.Intersection.prototype=/** @lends fabric.Intersection.prototype */{constructor:Intersection,/**\n * Appends a point to intersection\n * @param {fabric.Point} point\n * @return {fabric.Intersection} thisArg\n * @chainable\n */appendPoint:function appendPoint(point){this.points.push(point);return this;},/**\n * Appends points to intersection\n * @param {Array} points\n * @return {fabric.Intersection} thisArg\n * @chainable\n */appendPoints:function appendPoints(points){this.points=this.points.concat(points);return this;}};/**\n * Checks if one line intersects another\n * TODO: rename in intersectSegmentSegment\n * @static\n * @param {fabric.Point} a1\n * @param {fabric.Point} a2\n * @param {fabric.Point} b1\n * @param {fabric.Point} b2\n * @return {fabric.Intersection}\n */fabric.Intersection.intersectLineLine=function(a1,a2,b1,b2){var result,uaT=(b2.x-b1.x)*(a1.y-b1.y)-(b2.y-b1.y)*(a1.x-b1.x),ubT=(a2.x-a1.x)*(a1.y-b1.y)-(a2.y-a1.y)*(a1.x-b1.x),uB=(b2.y-b1.y)*(a2.x-a1.x)-(b2.x-b1.x)*(a2.y-a1.y);if(uB!==0){var ua=uaT/uB,ub=ubT/uB;if(0<=ua&&ua<=1&&0<=ub&&ub<=1){result=new Intersection('Intersection');result.appendPoint(new fabric.Point(a1.x+ua*(a2.x-a1.x),a1.y+ua*(a2.y-a1.y)));}else{result=new Intersection();}}else{if(uaT===0||ubT===0){result=new Intersection('Coincident');}else{result=new Intersection('Parallel');}}return result;};/**\n * Checks if line intersects polygon\n * TODO: rename in intersectSegmentPolygon\n * fix detection of coincident\n * @static\n * @param {fabric.Point} a1\n * @param {fabric.Point} a2\n * @param {Array} points\n * @return {fabric.Intersection}\n */fabric.Intersection.intersectLinePolygon=function(a1,a2,points){var result=new Intersection(),length=points.length,b1,b2,inter,i;for(i=0;i<length;i++){b1=points[i];b2=points[(i+1)%length];inter=Intersection.intersectLineLine(a1,a2,b1,b2);result.appendPoints(inter.points);}if(result.points.length>0){result.status='Intersection';}return result;};/**\n * Checks if polygon intersects another polygon\n * @static\n * @param {Array} points1\n * @param {Array} points2\n * @return {fabric.Intersection}\n */fabric.Intersection.intersectPolygonPolygon=function(points1,points2){var result=new Intersection(),length=points1.length,i;for(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;};/**\n * Checks if polygon intersects rectangle\n * @static\n * @param {Array} points\n * @param {fabric.Point} r1\n * @param {fabric.Point} r2\n * @return {fabric.Intersection}\n */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;};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={});if(fabric.Color){fabric.warn('fabric.Color is already defined.');return;}/**\n * Color class\n * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;\n * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.\n *\n * @class fabric.Color\n * @param {String} color optional in hex or rgb(a) or hsl format or from known color list\n * @return {fabric.Color} thisArg\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}\n */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 */{/**\n * @private\n * @param {String|Array} color Color value to parse\n */_tryParsingColor:function _tryParsingColor(color){var source;if(color in Color.colorNameMap){color=Color.colorNameMap[color];}if(color==='transparent'){source=[255,255,255,0];}if(!source){source=Color.sourceFromHex(color);}if(!source){source=Color.sourceFromRgb(color);}if(!source){source=Color.sourceFromHsl(color);}if(!source){//if color is not recognize let's make black as canvas does\nsource=[0,0,0,1];}if(source){this.setSource(source);}},/**\n * Adapted from <a href=\"https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html\">https://github.com/mjijackson</a>\n * @private\n * @param {Number} r Red color value\n * @param {Number} g Green color value\n * @param {Number} b Blue color value\n * @return {Array} Hsl color\n */_rgbToHsl:function _rgbToHsl(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\n}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)];},/**\n * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])\n * @return {Array}\n */getSource:function getSource(){return this._source;},/**\n * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])\n * @param {Array} source\n */setSource:function setSource(source){this._source=source;},/**\n * Returns color representation in RGB format\n * @return {String} ex: rgb(0-255,0-255,0-255)\n */toRgb:function toRgb(){var source=this.getSource();return'rgb('+source[0]+','+source[1]+','+source[2]+')';},/**\n * Returns color representation in RGBA format\n * @return {String} ex: rgba(0-255,0-255,0-255,0-1)\n */toRgba:function toRgba(){var source=this.getSource();return'rgba('+source[0]+','+source[1]+','+source[2]+','+source[3]+')';},/**\n * Returns color representation in HSL format\n * @return {String} ex: hsl(0-360,0%-100%,0%-100%)\n */toHsl:function toHsl(){var source=this.getSource(),hsl=this._rgbToHsl(source[0],source[1],source[2]);return'hsl('+hsl[0]+','+hsl[1]+'%,'+hsl[2]+'%)';},/**\n * Returns color representation in HSLA format\n * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)\n */toHsla:function toHsla(){var source=this.getSource(),hsl=this._rgbToHsl(source[0],source[1],source[2]);return'hsla('+hsl[0]+','+hsl[1]+'%,'+hsl[2]+'%,'+source[3]+')';},/**\n * Returns color representation in HEX format\n * @return {String} ex: FF5555\n */toHex:function toHex(){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();},/**\n * Returns color representation in HEXA format\n * @return {String} ex: FF5555CC\n */toHexa:function toHexa(){var source=this.getSource(),a;a=Math.round(source[3]*255);a=a.toString(16);a=a.length===1?'0'+a:a;return this.toHex()+a.toUpperCase();},/**\n * Gets value of alpha channel for this color\n * @return {Number} 0-1\n */getAlpha:function getAlpha(){return this.getSource()[3];},/**\n * Sets value of alpha channel for this color\n * @param {Number} alpha Alpha value 0-1\n * @return {fabric.Color} thisArg\n */setAlpha:function setAlpha(alpha){var source=this.getSource();source[3]=alpha;this.setSource(source);return this;},/**\n * Transforms color to its grayscale representation\n * @return {fabric.Color} thisArg\n */toGrayscale:function toGrayscale(){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;},/**\n * Transforms color to its black and white representation\n * @param {Number} threshold\n * @return {fabric.Color} thisArg\n */toBlackWhite:function toBlackWhite(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;},/**\n * Overlays color with another color\n * @param {String|fabric.Color} otherColor\n * @return {fabric.Color} thisArg\n */overlayWith:function overlayWith(otherColor){if(!(otherColor instanceof Color)){otherColor=new Color(otherColor);}var result=[],alpha=this.getAlpha(),otherAlpha=0.5,source=this.getSource(),otherSource=otherColor.getSource(),i;for(i=0;i<3;i++){result.push(Math.round(source[i]*(1-otherAlpha)+otherSource[i]*otherAlpha));}result[3]=alpha;this.setSource(result);return this;}};/**\n * 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))\n * @static\n * @field\n * @memberOf fabric.Color\n */ // eslint-disable-next-line max-len\nfabric.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*)?\\)$/i;/**\n * 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 ))\n * @static\n * @field\n * @memberOf fabric.Color\n */fabric.Color.reHSLa=/^hsla?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3}\\%)\\s*,\\s*(\\d{1,3}\\%)\\s*(?:\\s*,\\s*(\\d+(?:\\.\\d+)?)\\s*)?\\)$/i;/**\n * Regex matching color in HEX format (ex: #FF5544CC, #FF5555, 010155, aff)\n * @static\n * @field\n * @memberOf fabric.Color\n */fabric.Color.reHex=/^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;/**\n * Map of the 148 color names with HEX code\n * @static\n * @field\n * @memberOf fabric.Color\n * @see: https://www.w3.org/TR/css3-color/#svg-color\n */fabric.Color.colorNameMap={aliceblue:'#F0F8FF',antiquewhite:'#FAEBD7',aqua:'#00FFFF',aquamarine:'#7FFFD4',azure:'#F0FFFF',beige:'#F5F5DC',bisque:'#FFE4C4',black:'#000000',blanchedalmond:'#FFEBCD',blue:'#0000FF',blueviolet:'#8A2BE2',brown:'#A52A2A',burlywood:'#DEB887',cadetblue:'#5F9EA0',chartreuse:'#7FFF00',chocolate:'#D2691E',coral:'#FF7F50',cornflowerblue:'#6495ED',cornsilk:'#FFF8DC',crimson:'#DC143C',cyan:'#00FFFF',darkblue:'#00008B',darkcyan:'#008B8B',darkgoldenrod:'#B8860B',darkgray:'#A9A9A9',darkgrey:'#A9A9A9',darkgreen:'#006400',darkkhaki:'#BDB76B',darkmagenta:'#8B008B',darkolivegreen:'#556B2F',darkorange:'#FF8C00',darkorchid:'#9932CC',darkred:'#8B0000',darksalmon:'#E9967A',darkseagreen:'#8FBC8F',darkslateblue:'#483D8B',darkslategray:'#2F4F4F',darkslategrey:'#2F4F4F',darkturquoise:'#00CED1',darkviolet:'#9400D3',deeppink:'#FF1493',deepskyblue:'#00BFFF',dimgray:'#696969',dimgrey:'#696969',dodgerblue:'#1E90FF',firebrick:'#B22222',floralwhite:'#FFFAF0',forestgreen:'#228B22',fuchsia:'#FF00FF',gainsboro:'#DCDCDC',ghostwhite:'#F8F8FF',gold:'#FFD700',goldenrod:'#DAA520',gray:'#808080',grey:'#808080',green:'#008000',greenyellow:'#ADFF2F',honeydew:'#F0FFF0',hotpink:'#FF69B4',indianred:'#CD5C5C',indigo:'#4B0082',ivory:'#FFFFF0',khaki:'#F0E68C',lavender:'#E6E6FA',lavenderblush:'#FFF0F5',lawngreen:'#7CFC00',lemonchiffon:'#FFFACD',lightblue:'#ADD8E6',lightcoral:'#F08080',lightcyan:'#E0FFFF',lightgoldenrodyellow:'#FAFAD2',lightgray:'#D3D3D3',lightgrey:'#D3D3D3',lightgreen:'#90EE90',lightpink:'#FFB6C1',lightsalmon:'#FFA07A',lightseagreen:'#20B2AA',lightskyblue:'#87CEFA',lightslategray:'#778899',lightslategrey:'#778899',lightsteelblue:'#B0C4DE',lightyellow:'#FFFFE0',lime:'#00FF00',limegreen:'#32CD32',linen:'#FAF0E6',magenta:'#FF00FF',maroon:'#800000',mediumaquamarine:'#66CDAA',mediumblue:'#0000CD',mediumorchid:'#BA55D3',mediumpurple:'#9370DB',mediumseagreen:'#3CB371',mediumslateblue:'#7B68EE',mediumspringgreen:'#00FA9A',mediumturquoise:'#48D1CC',mediumvioletred:'#C71585',midnightblue:'#191970',mintcream:'#F5FFFA',mistyrose:'#FFE4E1',moccasin:'#FFE4B5',navajowhite:'#FFDEAD',navy:'#000080',oldlace:'#FDF5E6',olive:'#808000',olivedrab:'#6B8E23',orange:'#FFA500',orangered:'#FF4500',orchid:'#DA70D6',palegoldenrod:'#EEE8AA',palegreen:'#98FB98',paleturquoise:'#AFEEEE',palevioletred:'#DB7093',papayawhip:'#FFEFD5',peachpuff:'#FFDAB9',peru:'#CD853F',pink:'#FFC0CB',plum:'#DDA0DD',powderblue:'#B0E0E6',purple:'#800080',rebeccapurple:'#663399',red:'#FF0000',rosybrown:'#BC8F8F',royalblue:'#4169E1',saddlebrown:'#8B4513',salmon:'#FA8072',sandybrown:'#F4A460',seagreen:'#2E8B57',seashell:'#FFF5EE',sienna:'#A0522D',silver:'#C0C0C0',skyblue:'#87CEEB',slateblue:'#6A5ACD',slategray:'#708090',slategrey:'#708090',snow:'#FFFAFA',springgreen:'#00FF7F',steelblue:'#4682B4',tan:'#D2B48C',teal:'#008080',thistle:'#D8BFD8',tomato:'#FF6347',turquoise:'#40E0D0',violet:'#EE82EE',wheat:'#F5DEB3',white:'#FFFFFF',whitesmoke:'#F5F5F5',yellow:'#FFFF00',yellowgreen:'#9ACD32'};/**\n * @private\n * @param {Number} p\n * @param {Number} q\n * @param {Number} t\n * @return {Number}\n */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;}/**\n * Returns new color object, when given a color in RGB format\n * @memberOf fabric.Color\n * @param {String} color Color value ex: rgb(0-255,0-255,0-255)\n * @return {fabric.Color}\n */fabric.Color.fromRgb=function(color){return Color.fromSource(Color.sourceFromRgb(color));};/**\n * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format\n * @memberOf fabric.Color\n * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)\n * @return {Array} source\n */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];}};/**\n * Returns new color object, when given a color in RGBA format\n * @static\n * @function\n * @memberOf fabric.Color\n * @param {String} color\n * @return {fabric.Color}\n */fabric.Color.fromRgba=Color.fromRgb;/**\n * Returns new color object, when given a color in HSL format\n * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)\n * @memberOf fabric.Color\n * @return {fabric.Color}\n */fabric.Color.fromHsl=function(color){return Color.fromSource(Color.sourceFromHsl(color));};/**\n * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.\n * Adapted from <a href=\"https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html\">https://github.com/mjijackson</a>\n * @memberOf fabric.Color\n * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)\n * @return {Array} source\n * @see http://http://www.w3.org/TR/css3-color/#hsl-color\n */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];};/**\n * Returns new color object, when given a color in HSLA format\n * @static\n * @function\n * @memberOf fabric.Color\n * @param {String} color\n * @return {fabric.Color}\n */fabric.Color.fromHsla=Color.fromHsl;/**\n * Returns new color object, when given a color in HEX format\n * @static\n * @memberOf fabric.Color\n * @param {String} color Color value ex: FF5555\n * @return {fabric.Color}\n */fabric.Color.fromHex=function(color){return Color.fromSource(Color.sourceFromHex(color));};/**\n * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HEX format\n * @static\n * @memberOf fabric.Color\n * @param {String} color ex: FF5555 or FF5544CC (RGBa)\n * @return {Array} source\n */fabric.Color.sourceFromHex=function(color){if(color.match(Color.reHex)){var value=color.slice(color.indexOf('#')+1),isShortNotation=value.length===3||value.length===4,isRGBa=value.length===8||value.length===4,r=isShortNotation?value.charAt(0)+value.charAt(0):value.substring(0,2),g=isShortNotation?value.charAt(1)+value.charAt(1):value.substring(2,4),b=isShortNotation?value.charAt(2)+value.charAt(2):value.substring(4,6),a=isRGBa?isShortNotation?value.charAt(3)+value.charAt(3):value.substring(6,8):'FF';return[parseInt(r,16),parseInt(g,16),parseInt(b,16),parseFloat((parseInt(a,16)/255).toFixed(2))];}};/**\n * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])\n * @static\n * @memberOf fabric.Color\n * @param {Array} source\n * @return {fabric.Color}\n */fabric.Color.fromSource=function(source){var oColor=new Color();oColor.setSource(source);return oColor;};})( true?exports:undefined);(function(){/* _FROM_SVG_START_ */function getColorStop(el,multiplier){var style=el.getAttribute('style'),offset=el.getAttribute('offset')||0,color,colorAlpha,opacity,i;// convert percents to absolute values\noffset=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(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*multiplier;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_ */var clone=fabric.util.object.clone;/**\n * Gradient class\n * @class fabric.Gradient\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#gradients}\n * @see {@link fabric.Gradient#initialize} for constructor definition\n */fabric.Gradient=fabric.util.createClass(/** @lends fabric.Gradient.prototype */{/**\n * Horizontal offset for aligning gradients coming from SVG when outside pathgroups\n * @type Number\n * @default 0\n */offsetX:0,/**\n * Vertical offset for aligning gradients coming from SVG when outside pathgroups\n * @type Number\n * @default 0\n */offsetY:0,/**\n * A transform matrix to apply to the gradient before painting.\n * Imported from svg gradients, is not applied with the current transform in the center.\n * Before this transform is applied, the origin point is at the top left corner of the object\n * plus the addition of offsetY and offsetX.\n * @type Number[]\n * @default null\n */gradientTransform:null,/**\n * coordinates units for coords.\n * If `pixels`, the number of coords are in the same unit of width / height.\n * If set as `percentage` the coords are still a number, but 1 means 100% of width\n * for the X and 100% of the height for the y. It can be bigger than 1 and negative.\n * allowed values pixels or percentage.\n * @type String\n * @default 'pixels'\n */gradientUnits:'pixels',/**\n * Gradient type linear or radial\n * @type String\n * @default 'pixels'\n */type:'linear',/**\n * Constructor\n * @param {Object} options Options object with type, coords, gradientUnits and colorStops\n * @param {Object} [options.type] gradient type linear or radial\n * @param {Object} [options.gradientUnits] gradient units\n * @param {Object} [options.offsetX] SVG import compatibility\n * @param {Object} [options.offsetY] SVG import compatibility\n * @param {Object[]} options.colorStops contains the colorstops.\n * @param {Object} options.coords contains the coords of the gradient\n * @param {Number} [options.coords.x1] X coordiante of the first point for linear or of the focal point for radial\n * @param {Number} [options.coords.y1] Y coordiante of the first point for linear or of the focal point for radial\n * @param {Number} [options.coords.x2] X coordiante of the second point for linear or of the center point for radial\n * @param {Number} [options.coords.y2] Y coordiante of the second point for linear or of the center point for radial\n * @param {Number} [options.coords.r1] only for radial gradient, radius of the inner circle\n * @param {Number} [options.coords.r2] only for radial gradient, radius of the external circle\n * @return {fabric.Gradient} thisArg\n */initialize:function initialize(options){options||(options={});options.coords||(options.coords={});var coords,_this=this;// sets everything, then coords and colorstops get sets again\nObject.keys(options).forEach(function(option){_this[option]=options[option];});if(this.id){this.id+='_'+fabric.Object.__uid++;}else{this.id=fabric.Object.__uid++;}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();},/**\n * Adds another colorStop\n * @param {Object} colorStop Object with offset and color\n * @return {fabric.Gradient} thisArg\n */addColorStop:function addColorStop(colorStops){for(var position in colorStops){var color=new fabric.Color(colorStops[position]);this.colorStops.push({offset:parseFloat(position),color:color.toRgb(),opacity:color.getAlpha()});}return this;},/**\n * Returns object representation of a gradient\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object}\n */toObject:function toObject(propertiesToInclude){var object={type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY,gradientUnits:this.gradientUnits,gradientTransform:this.gradientTransform?this.gradientTransform.concat():this.gradientTransform};fabric.util.populateWithProperties(this,object,propertiesToInclude);return object;},/* _TO_SVG_START_ */ /**\n * Returns SVG representation of an gradient\n * @param {Object} object Object to create a gradient for\n * @return {String} SVG representation of an gradient (linear/radial)\n */toSVG:function toSVG(object,options){var coords=clone(this.coords,true),i,len,options=options||{},markup,commonAttributes,colorStops=clone(this.colorStops,true),needsSwap=coords.r1>coords.r2,transform=this.gradientTransform?this.gradientTransform.concat():fabric.iMatrix.concat(),offsetX=-this.offsetX,offsetY=-this.offsetY,withViewport=!!options.additionalTransform,gradientUnits=this.gradientUnits==='pixels'?'userSpaceOnUse':'objectBoundingBox';// colorStops must be sorted ascending\ncolorStops.sort(function(a,b){return a.offset-b.offset;});if(gradientUnits==='objectBoundingBox'){offsetX/=object.width;offsetY/=object.height;}else{offsetX+=object.width/2;offsetY+=object.height/2;}if(object.type==='path'){offsetX-=object.pathOffset.x;offsetY-=object.pathOffset.y;}transform[4]-=offsetX;transform[5]-=offsetY;commonAttributes='id=\"SVGID_'+this.id+'\" gradientUnits=\"'+gradientUnits+'\"';commonAttributes+=' gradientTransform=\"'+(withViewport?options.additionalTransform+' ':'')+fabric.util.matrixToSVG(transform)+'\" ';if(this.type==='linear'){markup=['<linearGradient ',commonAttributes,' x1=\"',coords.x1,'\" y1=\"',coords.y1,'\" x2=\"',coords.x2,'\" y2=\"',coords.y2,'\">\\n'];}else if(this.type==='radial'){// svg radial gradient has just 1 radius. the biggest.\nmarkup=['<radialGradient ',commonAttributes,' cx=\"',needsSwap?coords.x1:coords.x2,'\" cy=\"',needsSwap?coords.y1:coords.y2,'\" r=\"',needsSwap?coords.r1:coords.r2,'\" fx=\"',needsSwap?coords.x2:coords.x1,'\" fy=\"',needsSwap?coords.y2:coords.y1,'\">\\n'];}if(this.type==='radial'){if(needsSwap){// svg goes from internal to external radius. if radius are inverted, swap color stops.\ncolorStops=colorStops.concat();colorStops.reverse();for(i=0,len=colorStops.length;i<len;i++){colorStops[i].offset=1-colorStops[i].offset;}}var minRadius=Math.min(coords.r1,coords.r2);if(minRadius>0){// i have to shift all colorStops and add new one in 0.\nvar maxRadius=Math.max(coords.r1,coords.r2),percentageShift=minRadius/maxRadius;for(i=0,len=colorStops.length;i<len;i++){colorStops[i].offset+=percentageShift*(1-colorStops[i].offset);}}}for(i=0,len=colorStops.length;i<len;i++){var colorStop=colorStops[i];markup.push('<stop ','offset=\"',colorStop.offset*100+'%','\" style=\"stop-color:',colorStop.color,typeof colorStop.opacity!=='undefined'?';stop-opacity: '+colorStop.opacity:';','\"/>\\n');}markup.push(this.type==='linear'?'</linearGradient>\\n':'</radialGradient>\\n');return markup.join('');},/* _TO_SVG_END_ */ /**\n * Returns an instance of CanvasGradient\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {fabric.Object} object the fabric.Object for which the gradient is\n * @return {CanvasGradient}\n */toLive:function toLive(ctx,object){var gradient,coords=fabric.util.object.clone(this.coords),i,len,x1=coords.x1,y1=coords.y1,x2=coords.x2,y2=coords.y2,stops=this.colorStops;if(!this.type){return;}if(object instanceof fabric.Text&&this.gradientUnits==='percentage'){x1*=object.width;y1*=object.height;x2*=object.width;y2*=object.height;}if(this.type==='linear'){gradient=ctx.createLinearGradient(x1,y1,x2,y2);}else if(this.type==='radial'){gradient=ctx.createRadialGradient(x1,y1,coords.r1,x2,y2,coords.r2);}for(i=0,len=stops.length;i<len;i++){var color=stops[i].color,opacity=stops[i].opacity,offset=stops[i].offset;if(typeof opacity!=='undefined'){color=new fabric.Color(color).setAlpha(opacity).toRgba();}gradient.addColorStop(offset,color);}return gradient;}});fabric.util.object.extend(fabric.Gradient,{/* _FROM_SVG_START_ */ /**\n * Returns {@link fabric.Gradient} instance from an SVG element\n * @static\n * @memberOf fabric.Gradient\n * @param {SVGGradientElement} el SVG gradient element\n * @param {fabric.Object} instance\n * @param {String} opacityAttr A fill-opacity or stroke-opacity attribute to multiply to each stop's opacity.\n * @param {Object} svgOptions an object containing the size of the SVG in order to parse correctly graidents\n * that uses gradientUnits as 'userSpaceOnUse' and percentages.\n * @param {Object.number} viewBoxWidth width part of the viewBox attribute on svg\n * @param {Object.number} viewBoxHeight height part of the viewBox attribute on svg\n * @param {Object.number} width width part of the svg tag if viewBox is not specified\n * @param {Object.number} height height part of the svg tag if viewBox is not specified\n * @return {fabric.Gradient} Gradient instance\n * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement\n * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement\n */fromElement:function fromElement(el,instance,opacityAttr,svgOptions){/**\n * @example:\n *\n * <linearGradient id=\"linearGrad1\">\n * <stop offset=\"0%\" stop-color=\"white\"/>\n * <stop offset=\"100%\" stop-color=\"black\"/>\n * </linearGradient>\n *\n * OR\n *\n * <linearGradient id=\"linearGrad2\">\n * <stop offset=\"0\" style=\"stop-color:rgb(255,255,255)\"/>\n * <stop offset=\"1\" style=\"stop-color:rgb(0,0,0)\"/>\n * </linearGradient>\n *\n * OR\n *\n * <radialGradient id=\"radialGrad1\">\n * <stop offset=\"0%\" stop-color=\"white\" stop-opacity=\"1\" />\n * <stop offset=\"50%\" stop-color=\"black\" stop-opacity=\"0.5\" />\n * <stop offset=\"100%\" stop-color=\"white\" stop-opacity=\"1\" />\n * </radialGradient>\n *\n * OR\n *\n * <radialGradient id=\"radialGrad2\">\n * <stop offset=\"0\" stop-color=\"rgb(255,255,255)\" />\n * <stop offset=\"0.5\" stop-color=\"rgb(0,0,0)\" />\n * <stop offset=\"1\" stop-color=\"rgb(255,255,255)\" />\n * </radialGradient>\n *\n */var multiplier=parseFloat(opacityAttr)/(/%$/.test(opacityAttr)?100:1);multiplier=multiplier<0?0:multiplier>1?1:multiplier;if(isNaN(multiplier)){multiplier=1;}var colorStopEls=el.getElementsByTagName('stop'),type,gradientUnits=el.getAttribute('gradientUnits')==='userSpaceOnUse'?'pixels':'percentage',gradientTransform=el.getAttribute('gradientTransform')||'',colorStops=[],coords,i,offsetX=0,offsetY=0,transformMatrix;if(el.nodeName==='linearGradient'||el.nodeName==='LINEARGRADIENT'){type='linear';coords=getLinearCoords(el);}else{type='radial';coords=getRadialCoords(el);}for(i=colorStopEls.length;i--;){colorStops.push(getColorStop(colorStopEls[i],multiplier));}transformMatrix=fabric.parseTransformAttribute(gradientTransform);__convertPercentUnitsToValues(instance,coords,svgOptions,gradientUnits);if(gradientUnits==='pixels'){offsetX=-instance.left;offsetY=-instance.top;}var gradient=new fabric.Gradient({id:el.getAttribute('id'),type:type,coords:coords,colorStops:colorStops,gradientUnits:gradientUnits,gradientTransform:transformMatrix,offsetX:offsetX,offsetY:offsetY});return gradient;},/* _FROM_SVG_END_ */ /**\n * Returns {@link fabric.Gradient} instance from its object representation\n * this function is uniquely used by Object.setGradient and is deprecated with it.\n * @static\n * @deprecated since 3.4.0\n * @memberOf fabric.Gradient\n * @param {Object} obj\n * @param {Object} [options] Options object\n */forObject:function forObject(obj,options){options||(options={});__convertPercentUnitsToValues(obj,options.coords,options.gradientUnits,{// those values are to avoid errors. this function is uniquely used by\nviewBoxWidth:100,viewBoxHeight:100});return new fabric.Gradient(options);}});/**\n * @private\n */function __convertPercentUnitsToValues(instance,options,svgOptions,gradientUnits){var propValue,finalValue;Object.keys(options).forEach(function(prop){propValue=options[prop];if(propValue==='Infinity'){finalValue=1;}else if(propValue==='-Infinity'){finalValue=0;}else{finalValue=parseFloat(options[prop],10);if(typeof propValue==='string'&&/^(\\d+\\.\\d+)%|(\\d+)%$/.test(propValue)){finalValue*=0.01;if(gradientUnits==='pixels'){// then we need to fix those percentages here in svg parsing\nif(prop==='x1'||prop==='x2'||prop==='r2'){finalValue*=svgOptions.viewBoxWidth||svgOptions.width;}if(prop==='y1'||prop==='y2'){finalValue*=svgOptions.viewBoxHeight||svgOptions.height;}}}}options[prop]=finalValue;});}})();(function(){'use strict';var toFixed=fabric.util.toFixed;/**\n * Pattern class\n * @class fabric.Pattern\n * @see {@link http://fabricjs.com/patterns|Pattern demo}\n * @see {@link http://fabricjs.com/dynamic-patterns|DynamicPattern demo}\n * @see {@link fabric.Pattern#initialize} for constructor definition\n */fabric.Pattern=fabric.util.createClass(/** @lends fabric.Pattern.prototype */{/**\n * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)\n * @type String\n * @default\n */repeat:'repeat',/**\n * Pattern horizontal offset from object's left/top corner\n * @type Number\n * @default\n */offsetX:0,/**\n * Pattern vertical offset from object's left/top corner\n * @type Number\n * @default\n */offsetY:0,/**\n * crossOrigin value (one of \"\", \"anonymous\", \"use-credentials\")\n * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes\n * @type String\n * @default\n */crossOrigin:'',/**\n * transform matrix to change the pattern, imported from svgs.\n * @type Array\n * @default\n */patternTransform:null,/**\n * Constructor\n * @param {Object} [options] Options object\n * @param {Function} [callback] function to invoke after callback init.\n * @return {fabric.Pattern} thisArg\n */initialize:function initialize(options,callback){options||(options={});this.id=fabric.Object.__uid++;this.setOptions(options);if(!options.source||options.source&&typeof options.source!=='string'){callback&&callback(this);return;}// function string\nif(typeof fabric.util.getFunctionBody(options.source)!=='undefined'){this.source=new Function(fabric.util.getFunctionBody(options.source));callback&&callback(this);}else{// img src string\nvar _this=this;this.source=fabric.util.createImage();fabric.util.loadImage(options.source,function(img){_this.source=img;callback&&callback(_this);},null,this.crossOrigin);}},/**\n * Returns object representation of a pattern\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of a pattern instance\n */toObject:function toObject(propertiesToInclude){var NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS,source,object;// callback\nif(typeof this.source==='function'){source=String(this.source);}// <img> element\nelse if(typeof this.source.src==='string'){source=this.source.src;}// <canvas> element\nelse if(_typeof(this.source)==='object'&&this.source.toDataURL){source=this.source.toDataURL();}object={type:'pattern',source:source,repeat:this.repeat,crossOrigin:this.crossOrigin,offsetX:toFixed(this.offsetX,NUM_FRACTION_DIGITS),offsetY:toFixed(this.offsetY,NUM_FRACTION_DIGITS),patternTransform:this.patternTransform?this.patternTransform.concat():null};fabric.util.populateWithProperties(this,object,propertiesToInclude);return object;},/* _TO_SVG_START_ */ /**\n * Returns SVG representation of a pattern\n * @param {fabric.Object} object\n * @return {String} SVG representation of a pattern\n */toSVG:function toSVG(object){var patternSource=typeof this.source==='function'?this.source():this.source,patternWidth=patternSource.width/object.width,patternHeight=patternSource.height/object.height,patternOffsetX=this.offsetX/object.width,patternOffsetY=this.offsetY/object.height,patternImgSrc='';if(this.repeat==='repeat-x'||this.repeat==='no-repeat'){patternHeight=1;if(patternOffsetY){patternHeight+=Math.abs(patternOffsetY);}}if(this.repeat==='repeat-y'||this.repeat==='no-repeat'){patternWidth=1;if(patternOffsetX){patternWidth+=Math.abs(patternOffsetX);}}if(patternSource.src){patternImgSrc=patternSource.src;}else if(patternSource.toDataURL){patternImgSrc=patternSource.toDataURL();}return'<pattern id=\"SVGID_'+this.id+'\" x=\"'+patternOffsetX+'\" y=\"'+patternOffsetY+'\" width=\"'+patternWidth+'\" height=\"'+patternHeight+'\">\\n'+'<image x=\"0\" y=\"0\"'+' width=\"'+patternSource.width+'\" height=\"'+patternSource.height+'\" xlink:href=\"'+patternImgSrc+'\"></image>\\n'+'</pattern>\\n';},/* _TO_SVG_END_ */setOptions:function setOptions(options){for(var prop in options){this[prop]=options[prop];}},/**\n * Returns an instance of CanvasPattern\n * @param {CanvasRenderingContext2D} ctx Context to create pattern\n * @return {CanvasPattern}\n */toLive:function toLive(ctx){var source=typeof this.source==='function'?this.source():this.source;// if the image failed to load, return, and allow rest to continue loading\nif(!source){return'';}// if an image\nif(typeof source.src!=='undefined'){if(!source.complete){return'';}if(source.naturalWidth===0||source.naturalHeight===0){return'';}}return ctx.createPattern(source,this.repeat);}});})();(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),toFixed=fabric.util.toFixed;if(fabric.Shadow){fabric.warn('fabric.Shadow is already defined.');return;}/**\n * Shadow class\n * @class fabric.Shadow\n * @see {@link http://fabricjs.com/shadows|Shadow demo}\n * @see {@link fabric.Shadow#initialize} for constructor definition\n */fabric.Shadow=fabric.util.createClass(/** @lends fabric.Shadow.prototype */{/**\n * Shadow color\n * @type String\n * @default\n */color:'rgb(0,0,0)',/**\n * Shadow blur\n * @type Number\n */blur:0,/**\n * Shadow horizontal offset\n * @type Number\n * @default\n */offsetX:0,/**\n * Shadow vertical offset\n * @type Number\n * @default\n */offsetY:0,/**\n * Whether the shadow should affect stroke operations\n * @type Boolean\n * @default\n */affectStroke:false,/**\n * Indicates whether toObject should include default values\n * @type Boolean\n * @default\n */includeDefaultValues:true,/**\n * When `false`, the shadow will scale with the object.\n * When `true`, the shadow's offsetX, offsetY, and blur will not be affected by the object's scale.\n * default to false\n * @type Boolean\n * @default\n */nonScaling:false,/**\n * Constructor\n * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetY properties or string (e.g. \"rgba(0,0,0,0.2) 2px 2px 10px\")\n * @return {fabric.Shadow} thisArg\n */initialize:function initialize(options){if(typeof options==='string'){options=this._parseShadow(options);}for(var prop in options){this[prop]=options[prop];}this.id=fabric.Object.__uid++;},/**\n * @private\n * @param {String} shadow Shadow value to parse\n * @return {Object} Shadow object with color, offsetX, offsetY and blur\n */_parseShadow:function _parseShadow(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};},/**\n * Returns a string representation of an instance\n * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow\n * @return {String} Returns CSS3 text-shadow declaration\n */toString:function toString(){return[this.offsetX,this.offsetY,this.blur,this.color].join('px ');},/* _TO_SVG_START_ */ /**\n * Returns SVG representation of a shadow\n * @param {fabric.Object} object\n * @return {String} SVG representation of a shadow\n */toSVG:function toSVG(object){var fBoxX=40,fBoxY=40,NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS,offset=fabric.util.rotateVector({x:this.offsetX,y:this.offsetY},fabric.util.degreesToRadians(-object.angle)),BLUR_BOX=20,color=new fabric.Color(this.color);if(object.width&&object.height){//http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion\n// we add some extra space to filter box to contain the blur ( 20 )\nfBoxX=toFixed((Math.abs(offset.x)+this.blur)/object.width,NUM_FRACTION_DIGITS)*100+BLUR_BOX;fBoxY=toFixed((Math.abs(offset.y)+this.blur)/object.height,NUM_FRACTION_DIGITS)*100+BLUR_BOX;}if(object.flipX){offset.x*=-1;}if(object.flipY){offset.y*=-1;}return'<filter id=\"SVGID_'+this.id+'\" y=\"-'+fBoxY+'%\" height=\"'+(100+2*fBoxY)+'%\" '+'x=\"-'+fBoxX+'%\" width=\"'+(100+2*fBoxX)+'%\" '+'>\\n'+'\\t<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"'+toFixed(this.blur?this.blur/2:0,NUM_FRACTION_DIGITS)+'\"></feGaussianBlur>\\n'+'\\t<feOffset dx=\"'+toFixed(offset.x,NUM_FRACTION_DIGITS)+'\" dy=\"'+toFixed(offset.y,NUM_FRACTION_DIGITS)+'\" result=\"oBlur\" ></feOffset>\\n'+'\\t<feFlood flood-color=\"'+color.toRgb()+'\" flood-opacity=\"'+color.getAlpha()+'\"/>\\n'+'\\t<feComposite in2=\"oBlur\" operator=\"in\" />\\n'+'\\t<feMerge>\\n'+'\\t\\t<feMergeNode></feMergeNode>\\n'+'\\t\\t<feMergeNode in=\"SourceGraphic\"></feMergeNode>\\n'+'\\t</feMerge>\\n'+'</filter>\\n';},/* _TO_SVG_END_ */ /**\n * Returns object representation of a shadow\n * @return {Object} Object representation of a shadow instance\n */toObject:function toObject(){if(this.includeDefaultValues){return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY,affectStroke:this.affectStroke,nonScaling:this.nonScaling};}var obj={},proto=fabric.Shadow.prototype;['color','blur','offsetX','offsetY','affectStroke','nonScaling'].forEach(function(prop){if(this[prop]!==proto[prop]){obj[prop]=this[prop];}},this);return obj;}});/**\n * Regex matching shadow offsetX, offsetY and blur (ex: \"2px 2px 10px rgba(0,0,0,0.2)\", \"rgb(0,255,0) 2px 2px\")\n * @static\n * @field\n * @memberOf fabric.Shadow\n */ // eslint-disable-next-line max-len\nfabric.Shadow.reOffsetsAndBlur=/(?:\\s|^)(-?\\d+(?:px)?(?:\\s?|$))?(-?\\d+(?:px)?(?:\\s?|$))?(\\d+(?:px)?)?(?:\\s?|$)(?:$|\\s)/;})( true?exports:undefined);(function(){'use strict';if(fabric.StaticCanvas){fabric.warn('fabric.StaticCanvas is already defined.');return;}// aliases for faster resolution\nvar extend=fabric.util.object.extend,getElementOffset=fabric.util.getElementOffset,removeFromArray=fabric.util.removeFromArray,toFixed=fabric.util.toFixed,transformPoint=fabric.util.transformPoint,invertTransform=fabric.util.invertTransform,getNodeCanvas=fabric.util.getNodeCanvas,createCanvasElement=fabric.util.createCanvasElement,CANVAS_INIT_ERROR=new Error('Could not initialize `canvas` element');/**\n * Static canvas class\n * @class fabric.StaticCanvas\n * @mixes fabric.Collection\n * @mixes fabric.Observable\n * @see {@link http://fabricjs.com/static_canvas|StaticCanvas demo}\n * @see {@link fabric.StaticCanvas#initialize} for constructor definition\n * @fires before:render\n * @fires after:render\n * @fires canvas:cleared\n * @fires object:added\n * @fires object:removed\n */fabric.StaticCanvas=fabric.util.createClass(fabric.CommonMethods,/** @lends fabric.StaticCanvas.prototype */{/**\n * Constructor\n * @param {HTMLElement | String} el <canvas> element to initialize instance on\n * @param {Object} [options] Options object\n * @return {Object} thisArg\n */initialize:function initialize(el,options){options||(options={});this.renderAndResetBound=this.renderAndReset.bind(this);this.requestRenderAllBound=this.requestRenderAll.bind(this);this._initStatic(el,options);},/**\n * Background color of canvas instance.\n * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.\n * @type {(String|fabric.Pattern)}\n * @default\n */backgroundColor:'',/**\n * Background image of canvas instance.\n * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.\n * <b>Backwards incompatibility note:</b> The \"backgroundImageOpacity\"\n * and \"backgroundImageStretch\" properties are deprecated since 1.3.9.\n * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.\n * since 2.4.0 image caching is active, please when putting an image as background, add to the\n * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom\n * vale. As an alternative you can disable image objectCaching\n * @type fabric.Image\n * @default\n */backgroundImage:null,/**\n * Overlay color of canvas instance.\n * Should be set via {@link fabric.StaticCanvas#setOverlayColor}\n * @since 1.3.9\n * @type {(String|fabric.Pattern)}\n * @default\n */overlayColor:'',/**\n * Overlay image of canvas instance.\n * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.\n * <b>Backwards incompatibility note:</b> The \"overlayImageLeft\"\n * and \"overlayImageTop\" properties are deprecated since 1.3.9.\n * Use {@link fabric.Image#left} and {@link fabric.Image#top}.\n * since 2.4.0 image caching is active, please when putting an image as overlay, add to the\n * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom\n * vale. As an alternative you can disable image objectCaching\n * @type fabric.Image\n * @default\n */overlayImage:null,/**\n * Indicates whether toObject/toDatalessObject should include default values\n * if set to false, takes precedence over the object value.\n * @type Boolean\n * @default\n */includeDefaultValues:true,/**\n * Indicates whether objects' state should be saved\n * @type Boolean\n * @default\n */stateful:false,/**\n * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove},\n * {@link fabric.StaticCanvas.moveTo}, {@link fabric.StaticCanvas.clear} and many more, should also re-render canvas.\n * Disabling this option will not give a performance boost when adding/removing a lot of objects to/from canvas at once\n * since the renders are quequed and executed one per frame.\n * Disabling is suggested anyway and managing the renders of the app manually is not a big effort ( canvas.requestRenderAll() )\n * Left default to true to do not break documentation and old app, fiddles.\n * @type Boolean\n * @default\n */renderOnAddRemove:true,/**\n * Function that determines clipping of entire canvas area\n * Being passed context as first argument.\n * If you are using code minification, ctx argument can be minified/manglied you should use\n * as a workaround `var ctx = arguments[0];` in the function;\n * See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}\n * @deprecated since 2.0.0\n * @type Function\n * @default\n */clipTo:null,/**\n * Indicates whether object controls (borders/controls) are rendered above overlay image\n * @type Boolean\n * @default\n */controlsAboveOverlay:false,/**\n * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas\n * @type Boolean\n * @default\n */allowTouchScrolling:false,/**\n * Indicates whether this canvas will use image smoothing, this is on by default in browsers\n * @type Boolean\n * @default\n */imageSmoothingEnabled:true,/**\n * The transformation (in the format of Canvas transform) which focuses the viewport\n * @type Array\n * @default\n */viewportTransform:fabric.iMatrix.concat(),/**\n * if set to false background image is not affected by viewport transform\n * @since 1.6.3\n * @type Boolean\n * @default\n */backgroundVpt:true,/**\n * if set to false overlya image is not affected by viewport transform\n * @since 1.6.3\n * @type Boolean\n * @default\n */overlayVpt:true,/**\n * Callback; invoked right before object is about to be scaled/rotated\n * @deprecated since 2.3.0\n * Use before:transform event\n */onBeforeScaleRotate:function onBeforeScaleRotate(){/* NOOP */},/**\n * When true, canvas is scaled by devicePixelRatio for better rendering on retina screens\n * @type Boolean\n * @default\n */enableRetinaScaling:true,/**\n * Describe canvas element extension over design\n * properties are tl,tr,bl,br.\n * if canvas is not zoomed/panned those points are the four corner of canvas\n * if canvas is viewportTransformed you those points indicate the extension\n * of canvas element in plain untrasformed coordinates\n * The coordinates get updated with @method calcViewportBoundaries.\n * @memberOf fabric.StaticCanvas.prototype\n */vptCoords:{},/**\n * Based on vptCoords and object.aCoords, skip rendering of objects that\n * are not included in current viewport.\n * May greatly help in applications with crowded canvas and use of zoom/pan\n * If One of the corner of the bounding box of the object is on the canvas\n * the objects get rendered.\n * @memberOf fabric.StaticCanvas.prototype\n * @type Boolean\n * @default\n */skipOffscreen:true,/**\n * a fabricObject that, without stroke define a clipping area with their shape. filled in black\n * the clipPath object gets used when the canvas has rendered, and the context is placed in the\n * top left corner of the canvas.\n * clipPath will clip away controls, if you do not want this to happen use controlsAboveOverlay = true\n * @type fabric.Object\n */clipPath:undefined,/**\n * @private\n * @param {HTMLElement | String} el <canvas> element to initialize instance on\n * @param {Object} [options] Options object\n */_initStatic:function _initStatic(el,options){var cb=this.requestRenderAllBound;this._objects=[];this._createLowerCanvas(el);this._initOptions(options);this._setImageSmoothing();// only initialize retina scaling once\nif(!this.interactive){this._initRetinaScaling();}if(options.overlayImage){this.setOverlayImage(options.overlayImage,cb);}if(options.backgroundImage){this.setBackgroundImage(options.backgroundImage,cb);}if(options.backgroundColor){this.setBackgroundColor(options.backgroundColor,cb);}if(options.overlayColor){this.setOverlayColor(options.overlayColor,cb);}this.calcOffset();},/**\n * @private\n */_isRetinaScaling:function _isRetinaScaling(){return fabric.devicePixelRatio!==1&&this.enableRetinaScaling;},/**\n * @private\n * @return {Number} retinaScaling if applied, otherwise 1;\n */getRetinaScaling:function getRetinaScaling(){return this._isRetinaScaling()?fabric.devicePixelRatio:1;},/**\n * @private\n */_initRetinaScaling:function _initRetinaScaling(){if(!this._isRetinaScaling()){return;}var scaleRatio=fabric.devicePixelRatio;this.__initRetinaScaling(scaleRatio,this.lowerCanvasEl,this.contextContainer);if(this.upperCanvasEl){this.__initRetinaScaling(scaleRatio,this.upperCanvasEl,this.contextTop);}},__initRetinaScaling:function __initRetinaScaling(scaleRatio,canvas,context){canvas.setAttribute('width',this.width*scaleRatio);canvas.setAttribute('height',this.height*scaleRatio);context.scale(scaleRatio,scaleRatio);},/**\n * Calculates canvas element offset relative to the document\n * This method is also attached as \"resize\" event handler of window\n * @return {fabric.Canvas} instance\n * @chainable\n */calcOffset:function calcOffset(){this._offset=getElementOffset(this.lowerCanvasEl);return this;},/**\n * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas\n * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to\n * @param {Function} callback callback to invoke when image is loaded and set as an overlay\n * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.\n * @return {fabric.Canvas} thisArg\n * @chainable\n * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}\n * @example <caption>Normal overlayImage with left/top = 0</caption>\n * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {\n * // Needed to position overlayImage at 0/0\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>overlayImage with different properties</caption>\n * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {\n * opacity: 0.5,\n * angle: 45,\n * left: 400,\n * top: 400,\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>\n * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {\n * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});\n * canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));\n * });\n * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>\n * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {\n * width: canvas.width,\n * height: canvas.height,\n * // Needed to position overlayImage at 0/0\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>overlayImage loaded from cross-origin</caption>\n * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {\n * opacity: 0.5,\n * angle: 45,\n * left: 400,\n * top: 400,\n * originX: 'left',\n * originY: 'top',\n * crossOrigin: 'anonymous'\n * });\n */setOverlayImage:function setOverlayImage(image,callback,options){return this.__setBgOverlayImage('overlayImage',image,callback,options);},/**\n * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas\n * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to\n * @param {Function} callback Callback to invoke when image is loaded and set as background\n * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.\n * @return {fabric.Canvas} thisArg\n * @chainable\n * @see {@link http://jsfiddle.net/djnr8o7a/28/|jsFiddle demo}\n * @example <caption>Normal backgroundImage with left/top = 0</caption>\n * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {\n * // Needed to position backgroundImage at 0/0\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>backgroundImage with different properties</caption>\n * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {\n * opacity: 0.5,\n * angle: 45,\n * left: 400,\n * top: 400,\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>\n * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {\n * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});\n * canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));\n * });\n * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>\n * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {\n * width: canvas.width,\n * height: canvas.height,\n * // Needed to position backgroundImage at 0/0\n * originX: 'left',\n * originY: 'top'\n * });\n * @example <caption>backgroundImage loaded from cross-origin</caption>\n * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {\n * opacity: 0.5,\n * angle: 45,\n * left: 400,\n * top: 400,\n * originX: 'left',\n * originY: 'top',\n * crossOrigin: 'anonymous'\n * });\n */ // TODO: fix stretched examples\nsetBackgroundImage:function setBackgroundImage(image,callback,options){return this.__setBgOverlayImage('backgroundImage',image,callback,options);},/**\n * Sets {@link fabric.StaticCanvas#overlayColor|foreground color} for this canvas\n * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set foreground color to\n * @param {Function} callback Callback to invoke when foreground color is set\n * @return {fabric.Canvas} thisArg\n * @chainable\n * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}\n * @example <caption>Normal overlayColor - color value</caption>\n * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));\n * @example <caption>fabric.Pattern used as overlayColor</caption>\n * canvas.setOverlayColor({\n * source: 'http://fabricjs.com/assets/escheresque_ste.png'\n * }, canvas.renderAll.bind(canvas));\n * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>\n * canvas.setOverlayColor({\n * source: 'http://fabricjs.com/assets/escheresque_ste.png',\n * repeat: 'repeat',\n * offsetX: 200,\n * offsetY: 100\n * }, canvas.renderAll.bind(canvas));\n */setOverlayColor:function setOverlayColor(overlayColor,callback){return this.__setBgOverlayColor('overlayColor',overlayColor,callback);},/**\n * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas\n * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to\n * @param {Function} callback Callback to invoke when background color is set\n * @return {fabric.Canvas} thisArg\n * @chainable\n * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}\n * @example <caption>Normal backgroundColor - color value</caption>\n * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));\n * @example <caption>fabric.Pattern used as backgroundColor</caption>\n * canvas.setBackgroundColor({\n * source: 'http://fabricjs.com/assets/escheresque_ste.png'\n * }, canvas.renderAll.bind(canvas));\n * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>\n * canvas.setBackgroundColor({\n * source: 'http://fabricjs.com/assets/escheresque_ste.png',\n * repeat: 'repeat',\n * offsetX: 200,\n * offsetY: 100\n * }, canvas.renderAll.bind(canvas));\n */setBackgroundColor:function setBackgroundColor(backgroundColor,callback){return this.__setBgOverlayColor('backgroundColor',backgroundColor,callback);},/**\n * @private\n * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}\n */_setImageSmoothing:function _setImageSmoothing(){var ctx=this.getContext();ctx.imageSmoothingEnabled=ctx.imageSmoothingEnabled||ctx.webkitImageSmoothingEnabled||ctx.mozImageSmoothingEnabled||ctx.msImageSmoothingEnabled||ctx.oImageSmoothingEnabled;ctx.imageSmoothingEnabled=this.imageSmoothingEnabled;},/**\n * @private\n * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}\n * or {@link fabric.StaticCanvas#overlayImage|overlayImage})\n * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to\n * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay\n * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.\n */__setBgOverlayImage:function __setBgOverlayImage(property,image,callback,options){if(typeof image==='string'){fabric.util.loadImage(image,function(img){if(img){var instance=new fabric.Image(img,options);this[property]=instance;instance.canvas=this;}callback&&callback(img);},this,options&&options.crossOrigin);}else{options&&image.setOptions(options);this[property]=image;image&&(image.canvas=this);callback&&callback(image);}return this;},/**\n * @private\n * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}\n * or {@link fabric.StaticCanvas#overlayColor|overlayColor})\n * @param {(Object|String|null)} color Object with pattern information, color value or null\n * @param {Function} [callback] Callback is invoked when color is set\n */__setBgOverlayColor:function __setBgOverlayColor(property,color,callback){this[property]=color;this._initGradient(color,property);this._initPattern(color,property,callback);return this;},/**\n * @private\n */_createCanvasElement:function _createCanvasElement(){var element=createCanvasElement();if(!element){throw CANVAS_INIT_ERROR;}if(!element.style){element.style={};}if(typeof element.getContext==='undefined'){throw CANVAS_INIT_ERROR;}return element;},/**\n * @private\n * @param {Object} [options] Options object\n */_initOptions:function _initOptions(options){var lowerCanvasEl=this.lowerCanvasEl;this._setOptions(options);this.width=this.width||parseInt(lowerCanvasEl.width,10)||0;this.height=this.height||parseInt(lowerCanvasEl.height,10)||0;if(!this.lowerCanvasEl.style){return;}lowerCanvasEl.width=this.width;lowerCanvasEl.height=this.height;lowerCanvasEl.style.width=this.width+'px';lowerCanvasEl.style.height=this.height+'px';this.viewportTransform=this.viewportTransform.slice();},/**\n * Creates a bottom canvas\n * @private\n * @param {HTMLElement} [canvasEl]\n */_createLowerCanvas:function _createLowerCanvas(canvasEl){// canvasEl === 'HTMLCanvasElement' does not work on jsdom/node\nif(canvasEl&&canvasEl.getContext){this.lowerCanvasEl=canvasEl;}else{this.lowerCanvasEl=fabric.util.getById(canvasEl)||this._createCanvasElement();}fabric.util.addClass(this.lowerCanvasEl,'lower-canvas');if(this.interactive){this._applyCanvasStyle(this.lowerCanvasEl);}this.contextContainer=this.lowerCanvasEl.getContext('2d');},/**\n * Returns canvas width (in px)\n * @return {Number}\n */getWidth:function getWidth(){return this.width;},/**\n * Returns canvas height (in px)\n * @return {Number}\n */getHeight:function getHeight(){return this.height;},/**\n * Sets width of this canvas instance\n * @param {Number|String} value Value to set width to\n * @param {Object} [options] Options object\n * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions\n * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions\n * @return {fabric.Canvas} instance\n * @chainable true\n */setWidth:function setWidth(value,options){return this.setDimensions({width:value},options);},/**\n * Sets height of this canvas instance\n * @param {Number|String} value Value to set height to\n * @param {Object} [options] Options object\n * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions\n * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions\n * @return {fabric.Canvas} instance\n * @chainable true\n */setHeight:function setHeight(value,options){return this.setDimensions({height:value},options);},/**\n * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)\n * @param {Object} dimensions Object with width/height properties\n * @param {Number|String} [dimensions.width] Width of canvas element\n * @param {Number|String} [dimensions.height] Height of canvas element\n * @param {Object} [options] Options object\n * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions\n * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions\n * @return {fabric.Canvas} thisArg\n * @chainable\n */setDimensions:function setDimensions(dimensions,options){var cssValue;options=options||{};for(var prop in dimensions){cssValue=dimensions[prop];if(!options.cssOnly){this._setBackstoreDimension(prop,dimensions[prop]);cssValue+='px';this.hasLostContext=true;}if(!options.backstoreOnly){this._setCssDimension(prop,cssValue);}}if(this._isCurrentlyDrawing){this.freeDrawingBrush&&this.freeDrawingBrush._setBrushStyles();}this._initRetinaScaling();this._setImageSmoothing();this.calcOffset();if(!options.cssOnly){this.requestRenderAll();}return this;},/**\n * Helper for setting width/height\n * @private\n * @param {String} prop property (width|height)\n * @param {Number} value value to set property to\n * @return {fabric.Canvas} instance\n * @chainable true\n */_setBackstoreDimension:function _setBackstoreDimension(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;},/**\n * Helper for setting css width/height\n * @private\n * @param {String} prop property (width|height)\n * @param {String} value value to set property to\n * @return {fabric.Canvas} instance\n * @chainable true\n */_setCssDimension:function _setCssDimension(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;},/**\n * Returns canvas zoom level\n * @return {Number}\n */getZoom:function getZoom(){return this.viewportTransform[0];},/**\n * Sets viewport transform of this canvas instance\n * @param {Array} vpt the transform in the form of context.transform\n * @return {fabric.Canvas} instance\n * @chainable true\n */setViewportTransform:function setViewportTransform(vpt){var activeObject=this._activeObject,object,ignoreVpt=false,skipAbsolute=true,i,len;this.viewportTransform=vpt;for(i=0,len=this._objects.length;i<len;i++){object=this._objects[i];object.group||object.setCoords(ignoreVpt,skipAbsolute);}if(activeObject&&activeObject.type==='activeSelection'){activeObject.setCoords(ignoreVpt,skipAbsolute);}this.calcViewportBoundaries();this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Sets zoom level of this canvas instance, zoom centered around point\n * @param {fabric.Point} point to zoom with respect to\n * @param {Number} value to set zoom to, less than 1 zooms out\n * @return {fabric.Canvas} instance\n * @chainable true\n */zoomToPoint:function zoomToPoint(point,value){// TODO: just change the scale, preserve other transformations\nvar before=point,vpt=this.viewportTransform.slice(0);point=transformPoint(point,invertTransform(this.viewportTransform));vpt[0]=value;vpt[3]=value;var after=transformPoint(point,vpt);vpt[4]+=before.x-after.x;vpt[5]+=before.y-after.y;return this.setViewportTransform(vpt);},/**\n * Sets zoom level of this canvas instance\n * @param {Number} value to set zoom to, less than 1 zooms out\n * @return {fabric.Canvas} instance\n * @chainable true\n */setZoom:function setZoom(value){this.zoomToPoint(new fabric.Point(0,0),value);return this;},/**\n * Pan viewport so as to place point at top left corner of canvas\n * @param {fabric.Point} point to move to\n * @return {fabric.Canvas} instance\n * @chainable true\n */absolutePan:function absolutePan(point){var vpt=this.viewportTransform.slice(0);vpt[4]=-point.x;vpt[5]=-point.y;return this.setViewportTransform(vpt);},/**\n * Pans viewpoint relatively\n * @param {fabric.Point} point (position vector) to move by\n * @return {fabric.Canvas} instance\n * @chainable true\n */relativePan:function relativePan(point){return this.absolutePan(new fabric.Point(-point.x-this.viewportTransform[4],-point.y-this.viewportTransform[5]));},/**\n * Returns <canvas> element corresponding to this instance\n * @return {HTMLCanvasElement}\n */getElement:function getElement(){return this.lowerCanvasEl;},/**\n * @private\n * @param {fabric.Object} obj Object that was added\n */_onObjectAdded:function _onObjectAdded(obj){this.stateful&&obj.setupState();obj._set('canvas',this);obj.setCoords();this.fire('object:added',{target:obj});obj.fire('added');},/**\n * @private\n * @param {fabric.Object} obj Object that was removed\n */_onObjectRemoved:function _onObjectRemoved(obj){this.fire('object:removed',{target:obj});obj.fire('removed');delete obj.canvas;},/**\n * Clears specified context of canvas element\n * @param {CanvasRenderingContext2D} ctx Context to clear\n * @return {fabric.Canvas} thisArg\n * @chainable\n */clearContext:function clearContext(ctx){ctx.clearRect(0,0,this.width,this.height);return this;},/**\n * Returns context of canvas where objects are drawn\n * @return {CanvasRenderingContext2D}\n */getContext:function getContext(){return this.contextContainer;},/**\n * Clears all contexts (background, main, top) of an instance\n * @return {fabric.Canvas} thisArg\n * @chainable\n */clear:function clear(){this._objects.length=0;this.backgroundImage=null;this.overlayImage=null;this.backgroundColor='';this.overlayColor='';if(this._hasITextHandlers){this.off('mouse:up',this._mouseUpITextHandler);this._iTextInstances=null;this._hasITextHandlers=false;}this.clearContext(this.contextContainer);this.fire('canvas:cleared');this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Renders the canvas\n * @return {fabric.Canvas} instance\n * @chainable\n */renderAll:function renderAll(){var canvasToDrawOn=this.contextContainer;this.renderCanvas(canvasToDrawOn,this._objects);return this;},/**\n * Function created to be instance bound at initialization\n * used in requestAnimationFrame rendering\n * Let the fabricJS call it. If you call it manually you could have more\n * animationFrame stacking on to of each other\n * for an imperative rendering, use canvas.renderAll\n * @private\n * @return {fabric.Canvas} instance\n * @chainable\n */renderAndReset:function renderAndReset(){this.isRendering=0;this.renderAll();},/**\n * Append a renderAll request to next animation frame.\n * unless one is already in progress, in that case nothing is done\n * a boolean flag will avoid appending more.\n * @return {fabric.Canvas} instance\n * @chainable\n */requestRenderAll:function requestRenderAll(){if(!this.isRendering){this.isRendering=fabric.util.requestAnimFrame(this.renderAndResetBound);}return this;},/**\n * Calculate the position of the 4 corner of canvas with current viewportTransform.\n * helps to determinate when an object is in the current rendering viewport using\n * object absolute coordinates ( aCoords )\n * @return {Object} points.tl\n * @chainable\n */calcViewportBoundaries:function calcViewportBoundaries(){var points={},width=this.width,height=this.height,iVpt=invertTransform(this.viewportTransform);points.tl=transformPoint({x:0,y:0},iVpt);points.br=transformPoint({x:width,y:height},iVpt);points.tr=new fabric.Point(points.br.x,points.tl.y);points.bl=new fabric.Point(points.tl.x,points.br.y);this.vptCoords=points;return points;},cancelRequestedRender:function cancelRequestedRender(){if(this.isRendering){fabric.util.cancelAnimFrame(this.isRendering);this.isRendering=0;}},/**\n * Renders background, objects, overlay and controls.\n * @param {CanvasRenderingContext2D} ctx\n * @param {Array} objects to render\n * @return {fabric.Canvas} instance\n * @chainable\n */renderCanvas:function renderCanvas(ctx,objects){var v=this.viewportTransform,path=this.clipPath;this.cancelRequestedRender();this.calcViewportBoundaries();this.clearContext(ctx);this.fire('before:render',{ctx:ctx});if(this.clipTo){fabric.util.clipContext(this,ctx);}this._renderBackground(ctx);ctx.save();//apply viewport transform once for all rendering process\nctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);this._renderObjects(ctx,objects);ctx.restore();if(!this.controlsAboveOverlay&&this.interactive){this.drawControls(ctx);}if(this.clipTo){ctx.restore();}if(path){path.canvas=this;// needed to setup a couple of variables\npath.shouldCache();path._transformDone=true;path.renderCache({forClipping:true});this.drawClipPathOnCanvas(ctx);}this._renderOverlay(ctx);if(this.controlsAboveOverlay&&this.interactive){this.drawControls(ctx);}this.fire('after:render',{ctx:ctx});},/**\n * Paint the cached clipPath on the lowerCanvasEl\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */drawClipPathOnCanvas:function drawClipPathOnCanvas(ctx){var v=this.viewportTransform,path=this.clipPath;ctx.save();ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);// DEBUG: uncomment this line, comment the following\n// ctx.globalAlpha = 0.4;\nctx.globalCompositeOperation='destination-in';path.transform(ctx);ctx.scale(1/path.zoomX,1/path.zoomY);ctx.drawImage(path._cacheCanvas,-path.cacheTranslationX,-path.cacheTranslationY);ctx.restore();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Array} objects to render\n */_renderObjects:function _renderObjects(ctx,objects){var i,len;for(i=0,len=objects.length;i<len;++i){objects[i]&&objects[i].render(ctx);}},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {string} property 'background' or 'overlay'\n */_renderBackgroundOrOverlay:function _renderBackgroundOrOverlay(ctx,property){var fill=this[property+'Color'],object=this[property+'Image'],v=this.viewportTransform,needsVpt=this[property+'Vpt'];if(!fill&&!object){return;}if(fill){ctx.save();ctx.beginPath();ctx.moveTo(0,0);ctx.lineTo(this.width,0);ctx.lineTo(this.width,this.height);ctx.lineTo(0,this.height);ctx.closePath();ctx.fillStyle=fill.toLive?fill.toLive(ctx,this):fill;if(needsVpt){ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);}ctx.transform(1,0,0,1,fill.offsetX||0,fill.offsetY||0);var m=fill.gradientTransform||fill.patternTransform;m&&ctx.transform(m[0],m[1],m[2],m[3],m[4],m[5]);ctx.fill();ctx.restore();}if(object){ctx.save();if(needsVpt){ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);}object.render(ctx);ctx.restore();}},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderBackground:function _renderBackground(ctx){this._renderBackgroundOrOverlay(ctx,'background');},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderOverlay:function _renderOverlay(ctx){this._renderBackgroundOrOverlay(ctx,'overlay');},/**\n * Returns coordinates of a center of canvas.\n * Returned value is an object with top and left properties\n * @return {Object} object with \"top\" and \"left\" number values\n */getCenter:function getCenter(){return{top:this.height/2,left:this.width/2};},/**\n * Centers object horizontally in the canvas\n * @param {fabric.Object} object Object to center horizontally\n * @return {fabric.Canvas} thisArg\n */centerObjectH:function centerObjectH(object){return this._centerObject(object,new fabric.Point(this.getCenter().left,object.getCenterPoint().y));},/**\n * Centers object vertically in the canvas\n * @param {fabric.Object} object Object to center vertically\n * @return {fabric.Canvas} thisArg\n * @chainable\n */centerObjectV:function centerObjectV(object){return this._centerObject(object,new fabric.Point(object.getCenterPoint().x,this.getCenter().top));},/**\n * Centers object vertically and horizontally in the canvas\n * @param {fabric.Object} object Object to center vertically and horizontally\n * @return {fabric.Canvas} thisArg\n * @chainable\n */centerObject:function centerObject(object){var center=this.getCenter();return this._centerObject(object,new fabric.Point(center.left,center.top));},/**\n * Centers object vertically and horizontally in the viewport\n * @param {fabric.Object} object Object to center vertically and horizontally\n * @return {fabric.Canvas} thisArg\n * @chainable\n */viewportCenterObject:function viewportCenterObject(object){var vpCenter=this.getVpCenter();return this._centerObject(object,vpCenter);},/**\n * Centers object horizontally in the viewport, object.top is unchanged\n * @param {fabric.Object} object Object to center vertically and horizontally\n * @return {fabric.Canvas} thisArg\n * @chainable\n */viewportCenterObjectH:function viewportCenterObjectH(object){var vpCenter=this.getVpCenter();this._centerObject(object,new fabric.Point(vpCenter.x,object.getCenterPoint().y));return this;},/**\n * Centers object Vertically in the viewport, object.top is unchanged\n * @param {fabric.Object} object Object to center vertically and horizontally\n * @return {fabric.Canvas} thisArg\n * @chainable\n */viewportCenterObjectV:function viewportCenterObjectV(object){var vpCenter=this.getVpCenter();return this._centerObject(object,new fabric.Point(object.getCenterPoint().x,vpCenter.y));},/**\n * Calculate the point in canvas that correspond to the center of actual viewport.\n * @return {fabric.Point} vpCenter, viewport center\n * @chainable\n */getVpCenter:function getVpCenter(){var center=this.getCenter(),iVpt=invertTransform(this.viewportTransform);return transformPoint({x:center.left,y:center.top},iVpt);},/**\n * @private\n * @param {fabric.Object} object Object to center\n * @param {fabric.Point} center Center point\n * @return {fabric.Canvas} thisArg\n * @chainable\n */_centerObject:function _centerObject(object,center){object.setPositionByOrigin(center,'center','center');object.setCoords();this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Returs dataless JSON representation of canvas\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {String} json string\n */toDatalessJSON:function toDatalessJSON(propertiesToInclude){return this.toDatalessObject(propertiesToInclude);},/**\n * Returns object representation of canvas\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return this._toObjectMethod('toObject',propertiesToInclude);},/**\n * Returns dataless object representation of canvas\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toDatalessObject:function toDatalessObject(propertiesToInclude){return this._toObjectMethod('toDatalessObject',propertiesToInclude);},/**\n * @private\n */_toObjectMethod:function _toObjectMethod(methodName,propertiesToInclude){var clipPath=this.clipPath,data={version:fabric.version,objects:this._toObjects(methodName,propertiesToInclude)};if(clipPath){data.clipPath=this._toObject(this.clipPath,methodName,propertiesToInclude);}extend(data,this.__serializeBgOverlay(methodName,propertiesToInclude));fabric.util.populateWithProperties(this,data,propertiesToInclude);return data;},/**\n * @private\n */_toObjects:function _toObjects(methodName,propertiesToInclude){return this._objects.filter(function(object){return!object.excludeFromExport;}).map(function(instance){return this._toObject(instance,methodName,propertiesToInclude);},this);},/**\n * @private\n */_toObject:function _toObject(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;},/**\n * @private\n */__serializeBgOverlay:function __serializeBgOverlay(methodName,propertiesToInclude){var data={},bgImage=this.backgroundImage,overlay=this.overlayImage;if(this.backgroundColor){data.background=this.backgroundColor.toObject?this.backgroundColor.toObject(propertiesToInclude):this.backgroundColor;}if(this.overlayColor){data.overlay=this.overlayColor.toObject?this.overlayColor.toObject(propertiesToInclude):this.overlayColor;}if(bgImage&&!bgImage.excludeFromExport){data.backgroundImage=this._toObject(bgImage,methodName,propertiesToInclude);}if(overlay&&!overlay.excludeFromExport){data.overlayImage=this._toObject(overlay,methodName,propertiesToInclude);}return data;},/* _TO_SVG_START_ */ /**\n * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,\n * a zoomed canvas will then produce zoomed SVG output.\n * @type Boolean\n * @default\n */svgViewportTransformation:true,/**\n * Returns SVG representation of canvas\n * @function\n * @param {Object} [options] Options object for SVG output\n * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included\n * @param {Object} [options.viewBox] SVG viewbox object\n * @param {Number} [options.viewBox.x] x-cooridnate of viewbox\n * @param {Number} [options.viewBox.y] y-coordinate of viewbox\n * @param {Number} [options.viewBox.width] Width of viewbox\n * @param {Number} [options.viewBox.height] Height of viewbox\n * @param {String} [options.encoding=UTF-8] Encoding of SVG output\n * @param {String} [options.width] desired width of svg with or without units\n * @param {String} [options.height] desired height of svg with or without units\n * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.\n * @return {String} SVG string\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}\n * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}\n * @example <caption>Normal SVG output</caption>\n * var svg = canvas.toSVG();\n * @example <caption>SVG output without preamble (without <?xml ../>)</caption>\n * var svg = canvas.toSVG({suppressPreamble: true});\n * @example <caption>SVG output with viewBox attribute</caption>\n * var svg = canvas.toSVG({\n * viewBox: {\n * x: 100,\n * y: 100,\n * width: 200,\n * height: 300\n * }\n * });\n * @example <caption>SVG output with different encoding (default: UTF-8)</caption>\n * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});\n * @example <caption>Modify SVG output with reviver function</caption>\n * var svg = canvas.toSVG(null, function(svg) {\n * return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');\n * });\n */toSVG:function toSVG(options,reviver){options||(options={});options.reviver=reviver;var markup=[];this._setSVGPreamble(markup,options);this._setSVGHeader(markup,options);if(this.clipPath){markup.push('<g clip-path=\"url(#'+this.clipPath.clipPathId+')\" >\\n');}this._setSVGBgOverlayColor(markup,'background');this._setSVGBgOverlayImage(markup,'backgroundImage',reviver);this._setSVGObjects(markup,reviver);if(this.clipPath){markup.push('</g>\\n');}this._setSVGBgOverlayColor(markup,'overlay');this._setSVGBgOverlayImage(markup,'overlayImage',reviver);markup.push('</svg>');return markup.join('');},/**\n * @private\n */_setSVGPreamble:function _setSVGPreamble(markup,options){if(options.suppressPreamble){return;}markup.push('<?xml version=\"1.0\" encoding=\"',options.encoding||'UTF-8','\" standalone=\"no\" ?>\\n','<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ','\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\\n');},/**\n * @private\n */_setSVGHeader:function _setSVGHeader(markup,options){var width=options.width||this.width,height=options.height||this.height,vpt,viewBox='viewBox=\"0 0 '+this.width+' '+this.height+'\" ',NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS;if(options.viewBox){viewBox='viewBox=\"'+options.viewBox.x+' '+options.viewBox.y+' '+options.viewBox.width+' '+options.viewBox.height+'\" ';}else{if(this.svgViewportTransformation){vpt=this.viewportTransform;viewBox='viewBox=\"'+toFixed(-vpt[4]/vpt[0],NUM_FRACTION_DIGITS)+' '+toFixed(-vpt[5]/vpt[3],NUM_FRACTION_DIGITS)+' '+toFixed(this.width/vpt[0],NUM_FRACTION_DIGITS)+' '+toFixed(this.height/vpt[3],NUM_FRACTION_DIGITS)+'\" ';}}markup.push('<svg ','xmlns=\"http://www.w3.org/2000/svg\" ','xmlns:xlink=\"http://www.w3.org/1999/xlink\" ','version=\"1.1\" ','width=\"',width,'\" ','height=\"',height,'\" ',viewBox,'xml:space=\"preserve\">\\n','<desc>Created with Fabric.js ',fabric.version,'</desc>\\n','<defs>\\n',this.createSVGFontFacesMarkup(),this.createSVGRefElementsMarkup(),this.createSVGClipPathMarkup(options),'</defs>\\n');},createSVGClipPathMarkup:function createSVGClipPathMarkup(options){var clipPath=this.clipPath;if(clipPath){clipPath.clipPathId='CLIPPATH_'+fabric.Object.__uid++;return'<clipPath id=\"'+clipPath.clipPathId+'\" >\\n'+this.clipPath.toClipPathSVG(options.reviver)+'</clipPath>\\n';}return'';},/**\n * Creates markup containing SVG referenced elements like patterns, gradients etc.\n * @return {String}\n */createSVGRefElementsMarkup:function createSVGRefElementsMarkup(){var _this=this,markup=['background','overlay'].map(function(prop){var fill=_this[prop+'Color'];if(fill&&fill.toLive){var shouldTransform=_this[prop+'Vpt'],vpt=_this.viewportTransform,object={width:_this.width/(shouldTransform?vpt[0]:1),height:_this.height/(shouldTransform?vpt[3]:1)};return fill.toSVG(object,{additionalTransform:shouldTransform?fabric.util.matrixToSVG(vpt):''});}});return markup.join('');},/**\n * Creates markup containing SVG font faces,\n * font URLs for font faces must be collected by developers\n * and are not extracted from the DOM by fabricjs\n * @param {Array} objects Array of fabric objects\n * @return {String}\n */createSVGFontFacesMarkup:function createSVGFontFacesMarkup(){var markup='',fontList={},obj,fontFamily,style,row,rowIndex,_char,charIndex,i,len,fontPaths=fabric.fontPaths,objects=this._objects;for(i=0,len=objects.length;i<len;i++){obj=objects[i];fontFamily=obj.fontFamily;if(obj.type.indexOf('text')===-1||fontList[fontFamily]||!fontPaths[fontFamily]){continue;}fontList[fontFamily]=true;if(!obj.styles){continue;}style=obj.styles;for(rowIndex in style){row=style[rowIndex];for(charIndex in row){_char=row[charIndex];fontFamily=_char.fontFamily;if(!fontList[fontFamily]&&fontPaths[fontFamily]){fontList[fontFamily]=true;}}}}for(var j in fontList){markup+=['\\t\\t@font-face {\\n','\\t\\t\\tfont-family: \\'',j,'\\';\\n','\\t\\t\\tsrc: url(\\'',fontPaths[j],'\\');\\n','\\t\\t}\\n'].join('');}if(markup){markup=['\\t<style type=\"text/css\">','<![CDATA[\\n',markup,']]>','</style>\\n'].join('');}return markup;},/**\n * @private\n */_setSVGObjects:function _setSVGObjects(markup,reviver){var instance,i,len,objects=this._objects;for(i=0,len=objects.length;i<len;i++){instance=objects[i];if(instance.excludeFromExport){continue;}this._setSVGObject(markup,instance,reviver);}},/**\n * @private\n */_setSVGObject:function _setSVGObject(markup,instance,reviver){markup.push(instance.toSVG(reviver));},/**\n * @private\n */_setSVGBgOverlayImage:function _setSVGBgOverlayImage(markup,property,reviver){if(this[property]&&!this[property].excludeFromExport&&this[property].toSVG){markup.push(this[property].toSVG(reviver));}},/**\n * @private\n */_setSVGBgOverlayColor:function _setSVGBgOverlayColor(markup,property){var filler=this[property+'Color'],vpt=this.viewportTransform,finalWidth=this.width,finalHeight=this.height;if(!filler){return;}if(filler.toLive){var repeat=filler.repeat,iVpt=fabric.util.invertTransform(vpt),shouldInvert=this[property+'Vpt'],additionalTransform=shouldInvert?fabric.util.matrixToSVG(iVpt):'';markup.push('<rect transform=\"'+additionalTransform+' translate(',finalWidth/2,',',finalHeight/2,')\"',' x=\"',filler.offsetX-finalWidth/2,'\" y=\"',filler.offsetY-finalHeight/2,'\" ','width=\"',repeat==='repeat-y'||repeat==='no-repeat'?filler.source.width:finalWidth,'\" height=\"',repeat==='repeat-x'||repeat==='no-repeat'?filler.source.height:finalHeight,'\" fill=\"url(#SVGID_'+filler.id+')\"','></rect>\\n');}else{markup.push('<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" ','fill=\"',filler,'\"','></rect>\\n');}},/* _TO_SVG_END_ */ /**\n * Moves an object or the objects of a multiple selection\n * to the bottom of the stack of drawn objects\n * @param {fabric.Object} object Object to send to back\n * @return {fabric.Canvas} thisArg\n * @chainable\n */sendToBack:function sendToBack(object){if(!object){return this;}var activeSelection=this._activeObject,i,obj,objs;if(object===activeSelection&&object.type==='activeSelection'){objs=activeSelection._objects;for(i=objs.length;i--;){obj=objs[i];removeFromArray(this._objects,obj);this._objects.unshift(obj);}}else{removeFromArray(this._objects,object);this._objects.unshift(object);}this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Moves an object or the objects of a multiple selection\n * to the top of the stack of drawn objects\n * @param {fabric.Object} object Object to send\n * @return {fabric.Canvas} thisArg\n * @chainable\n */bringToFront:function bringToFront(object){if(!object){return this;}var activeSelection=this._activeObject,i,obj,objs;if(object===activeSelection&&object.type==='activeSelection'){objs=activeSelection._objects;for(i=0;i<objs.length;i++){obj=objs[i];removeFromArray(this._objects,obj);this._objects.push(obj);}}else{removeFromArray(this._objects,object);this._objects.push(object);}this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * Moves an object or a selection down in stack of drawn objects\n * An optional paramter, intersecting allowes to move the object in behind\n * the first intersecting object. Where intersection is calculated with\n * bounding box. If no intersection is found, there will not be change in the\n * stack.\n * @param {fabric.Object} object Object to send\n * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object\n * @return {fabric.Canvas} thisArg\n * @chainable\n */sendBackwards:function sendBackwards(object,intersecting){if(!object){return this;}var activeSelection=this._activeObject,i,obj,idx,newIdx,objs,objsMoved=0;if(object===activeSelection&&object.type==='activeSelection'){objs=activeSelection._objects;for(i=0;i<objs.length;i++){obj=objs[i];idx=this._objects.indexOf(obj);if(idx>0+objsMoved){newIdx=idx-1;removeFromArray(this._objects,obj);this._objects.splice(newIdx,0,obj);}objsMoved++;}}else{idx=this._objects.indexOf(object);if(idx!==0){// if object is not on the bottom of stack\nnewIdx=this._findNewLowerIndex(object,idx,intersecting);removeFromArray(this._objects,object);this._objects.splice(newIdx,0,object);}}this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * @private\n */_findNewLowerIndex:function _findNewLowerIndex(object,idx,intersecting){var newIdx,i;if(intersecting){newIdx=idx;// traverse down the stack looking for the nearest intersecting object\nfor(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;},/**\n * Moves an object or a selection up in stack of drawn objects\n * An optional paramter, intersecting allowes to move the object in front\n * of the first intersecting object. Where intersection is calculated with\n * bounding box. If no intersection is found, there will not be change in the\n * stack.\n * @param {fabric.Object} object Object to send\n * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object\n * @return {fabric.Canvas} thisArg\n * @chainable\n */bringForward:function bringForward(object,intersecting){if(!object){return this;}var activeSelection=this._activeObject,i,obj,idx,newIdx,objs,objsMoved=0;if(object===activeSelection&&object.type==='activeSelection'){objs=activeSelection._objects;for(i=objs.length;i--;){obj=objs[i];idx=this._objects.indexOf(obj);if(idx<this._objects.length-1-objsMoved){newIdx=idx+1;removeFromArray(this._objects,obj);this._objects.splice(newIdx,0,obj);}objsMoved++;}}else{idx=this._objects.indexOf(object);if(idx!==this._objects.length-1){// if object is not on top of stack (last item in an array)\nnewIdx=this._findNewUpperIndex(object,idx,intersecting);removeFromArray(this._objects,object);this._objects.splice(newIdx,0,object);}}this.renderOnAddRemove&&this.requestRenderAll();return this;},/**\n * @private\n */_findNewUpperIndex:function _findNewUpperIndex(object,idx,intersecting){var newIdx,i,len;if(intersecting){newIdx=idx;// traverse up the stack looking for the nearest intersecting object\nfor(i=idx+1,len=this._objects.length;i<len;++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;},/**\n * Moves an object to specified level in stack of drawn objects\n * @param {fabric.Object} object Object to send\n * @param {Number} index Position to move to\n * @return {fabric.Canvas} thisArg\n * @chainable\n */moveTo:function moveTo(object,index){removeFromArray(this._objects,object);this._objects.splice(index,0,object);return this.renderOnAddRemove&&this.requestRenderAll();},/**\n * Clears a canvas element and dispose objects\n * @return {fabric.Canvas} thisArg\n * @chainable\n */dispose:function dispose(){// cancel eventually ongoing renders\nif(this.isRendering){fabric.util.cancelAnimFrame(this.isRendering);this.isRendering=0;}this.forEachObject(function(object){object.dispose&&object.dispose();});this._objects=[];if(this.backgroundImage&&this.backgroundImage.dispose){this.backgroundImage.dispose();}this.backgroundImage=null;if(this.overlayImage&&this.overlayImage.dispose){this.overlayImage.dispose();}this.overlayImage=null;this._iTextInstances=null;this.contextContainer=null;fabric.util.cleanUpJsdomNode(this.lowerCanvasEl);this.lowerCanvasEl=undefined;return this;},/**\n * Returns a string representation of an instance\n * @return {String} string representation of an instance\n */toString:function toString(){return'#<fabric.Canvas ('+this.complexity()+'): '+'{ objects: '+this._objects.length+' }>';}});extend(fabric.StaticCanvas.prototype,fabric.Observable);extend(fabric.StaticCanvas.prototype,fabric.Collection);extend(fabric.StaticCanvas.prototype,fabric.DataURLExporter);extend(fabric.StaticCanvas,/** @lends fabric.StaticCanvas */{/**\n * @static\n * @type String\n * @default\n */EMPTY_JSON:'{\"objects\": [], \"background\": \"white\"}',/**\n * Provides a way to check support of some of the canvas methods\n * (either those of HTMLCanvasElement itself, or rendering context)\n *\n * @param {String} methodName Method to check support for;\n * Could be one of \"setLineDash\"\n * @return {Boolean | null} `true` if method is supported (or at least exists),\n * `null` if canvas element or context can not be initialized\n */supports:function supports(methodName){var el=createCanvasElement();if(!el||!el.getContext){return null;}var ctx=el.getContext('2d');if(!ctx){return null;}switch(methodName){case'setLineDash':return typeof ctx.setLineDash!=='undefined';default:return null;}}});/**\n * Returns JSON representation of canvas\n * @function\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {String} JSON string\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}\n * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}\n * @example <caption>JSON without additional properties</caption>\n * var json = canvas.toJSON();\n * @example <caption>JSON with additional properties included</caption>\n * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);\n * @example <caption>JSON without default values</caption>\n * canvas.includeDefaultValues = false;\n * var json = canvas.toJSON();\n */fabric.StaticCanvas.prototype.toJSON=fabric.StaticCanvas.prototype.toObject;if(fabric.isLikelyNode){fabric.StaticCanvas.prototype.createPNGStream=function(){var impl=getNodeCanvas(this.lowerCanvasEl);return impl&&impl.createPNGStream();};fabric.StaticCanvas.prototype.createJPEGStream=function(opts){var impl=getNodeCanvas(this.lowerCanvasEl);return impl&&impl.createJPEGStream(opts);};}})();/**\n * BaseBrush class\n * @class fabric.BaseBrush\n * @see {@link http://fabricjs.com/freedrawing|Freedrawing demo}\n */fabric.BaseBrush=fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */{/**\n * Color of a brush\n * @type String\n * @default\n */color:'rgb(0, 0, 0)',/**\n * Width of a brush, has to be a Number, no string literals\n * @type Number\n * @default\n */width:1,/**\n * Shadow object representing shadow of this shape.\n * <b>Backwards incompatibility note:</b> This property replaces \"shadowColor\" (String), \"shadowOffsetX\" (Number),\n * \"shadowOffsetY\" (Number) and \"shadowBlur\" (Number) since v1.2.12\n * @type fabric.Shadow\n * @default\n */shadow:null,/**\n * Line endings style of a brush (one of \"butt\", \"round\", \"square\")\n * @type String\n * @default\n */strokeLineCap:'round',/**\n * Corner style of a brush (one of \"bevel\", \"round\", \"miter\")\n * @type String\n * @default\n */strokeLineJoin:'round',/**\n * Maximum miter length (used for strokeLineJoin = \"miter\") of a brush's\n * @type Number\n * @default\n */strokeMiterLimit:10,/**\n * Stroke Dash Array.\n * @type Array\n * @default\n */strokeDashArray:null,/**\n * Sets shadow of an object\n * @param {Object|String} [options] Options object or string (e.g. \"2px 2px 10px rgba(0,0,0,0.2)\")\n * @return {fabric.Object} thisArg\n * @chainable\n */setShadow:function setShadow(options){this.shadow=new fabric.Shadow(options);return this;},/**\n * Sets brush styles\n * @private\n */_setBrushStyles:function _setBrushStyles(){var ctx=this.canvas.contextTop;ctx.strokeStyle=this.color;ctx.lineWidth=this.width;ctx.lineCap=this.strokeLineCap;ctx.miterLimit=this.strokeMiterLimit;ctx.lineJoin=this.strokeLineJoin;if(fabric.StaticCanvas.supports('setLineDash')){ctx.setLineDash(this.strokeDashArray||[]);}},/**\n * Sets the transformation on given context\n * @param {RenderingContext2d} ctx context to render on\n * @private\n */_saveAndTransform:function _saveAndTransform(ctx){var v=this.canvas.viewportTransform;ctx.save();ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);},/**\n * Sets brush shadow styles\n * @private\n */_setShadow:function _setShadow(){if(!this.shadow){return;}var canvas=this.canvas,shadow=this.shadow,ctx=canvas.contextTop,zoom=canvas.getZoom();if(canvas&&canvas._isRetinaScaling()){zoom*=fabric.devicePixelRatio;}ctx.shadowColor=shadow.color;ctx.shadowBlur=shadow.blur*zoom;ctx.shadowOffsetX=shadow.offsetX*zoom;ctx.shadowOffsetY=shadow.offsetY*zoom;},needsFullRender:function needsFullRender(){var color=new fabric.Color(this.color);return color.getAlpha()<1||!!this.shadow;},/**\n * Removes brush shadow styles\n * @private\n */_resetShadow:function _resetShadow(){var ctx=this.canvas.contextTop;ctx.shadowColor='';ctx.shadowBlur=ctx.shadowOffsetX=ctx.shadowOffsetY=0;}});(function(){/**\n * PencilBrush class\n * @class fabric.PencilBrush\n * @extends fabric.BaseBrush\n */fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,/** @lends fabric.PencilBrush.prototype */{/**\n * Discard points that are less than `decimate` pixel distant from each other\n * @type Number\n * @default 0.4\n */decimate:0.4,/**\n * Constructor\n * @param {fabric.Canvas} canvas\n * @return {fabric.PencilBrush} Instance of a pencil brush\n */initialize:function initialize(canvas){this.canvas=canvas;this._points=[];},/**\n * Invoked inside on mouse down and mouse move\n * @param {Object} pointer\n */_drawSegment:function _drawSegment(ctx,p1,p2){var midPoint=p1.midPointFrom(p2);ctx.quadraticCurveTo(p1.x,p1.y,midPoint.x,midPoint.y);return midPoint;},/**\n * Inovoked on mouse down\n * @param {Object} pointer\n */onMouseDown:function onMouseDown(pointer,options){if(!this.canvas._isMainEvent(options.e)){return;}this._prepareForDrawing(pointer);// capture coordinates immediately\n// this allows to draw dots (when movement never occurs)\nthis._captureDrawingPath(pointer);this._render();},/**\n * Inovoked on mouse move\n * @param {Object} pointer\n */onMouseMove:function onMouseMove(pointer,options){if(!this.canvas._isMainEvent(options.e)){return;}if(this._captureDrawingPath(pointer)&&this._points.length>1){if(this.needsFullRender()){// redraw curve\n// clear top canvas\nthis.canvas.clearContext(this.canvas.contextTop);this._render();}else{var points=this._points,length=points.length,ctx=this.canvas.contextTop;// draw the curve update\nthis._saveAndTransform(ctx);if(this.oldEnd){ctx.beginPath();ctx.moveTo(this.oldEnd.x,this.oldEnd.y);}this.oldEnd=this._drawSegment(ctx,points[length-2],points[length-1],true);ctx.stroke();ctx.restore();}}},/**\n * Invoked on mouse up\n */onMouseUp:function onMouseUp(options){if(!this.canvas._isMainEvent(options.e)){return true;}this.oldEnd=undefined;this._finalizeAndAddPath();return false;},/**\n * @private\n * @param {Object} pointer Actual mouse position related to the canvas.\n */_prepareForDrawing:function _prepareForDrawing(pointer){var p=new fabric.Point(pointer.x,pointer.y);this._reset();this._addPoint(p);this.canvas.contextTop.moveTo(p.x,p.y);},/**\n * @private\n * @param {fabric.Point} point Point to be added to points array\n */_addPoint:function _addPoint(point){if(this._points.length>1&&point.eq(this._points[this._points.length-1])){return false;}this._points.push(point);return true;},/**\n * Clear points array and set contextTop canvas style.\n * @private\n */_reset:function _reset(){this._points=[];this._setBrushStyles();this._setShadow();},/**\n * @private\n * @param {Object} pointer Actual mouse position related to the canvas.\n */_captureDrawingPath:function _captureDrawingPath(pointer){var pointerPoint=new fabric.Point(pointer.x,pointer.y);return this._addPoint(pointerPoint);},/**\n * Draw a smooth path on the topCanvas using quadraticCurveTo\n * @private\n */_render:function _render(){var ctx=this.canvas.contextTop,i,len,p1=this._points[0],p2=this._points[1];this._saveAndTransform(ctx);ctx.beginPath();//if we only have 2 points in the path and they are the same\n//it means that the user only clicked the canvas without moving the mouse\n//then we should be drawing a dot. A path isn't drawn between two identical dots\n//that's why we set them apart a bit\nif(this._points.length===2&&p1.x===p2.x&&p1.y===p2.y){var width=this.width/1000;p1=new fabric.Point(p1.x,p1.y);p2=new fabric.Point(p2.x,p2.y);p1.x-=width;p2.x+=width;}ctx.moveTo(p1.x,p1.y);for(i=1,len=this._points.length;i<len;i++){// we pick the point between pi + 1 & pi + 2 as the\n// end point and p1 as our control point.\nthis._drawSegment(ctx,p1,p2);p1=this._points[i];p2=this._points[i+1];}// Draw last line as a straight line while\n// we wait for the next point to be able to calculate\n// the bezier control point\nctx.lineTo(p1.x,p1.y);ctx.stroke();ctx.restore();},/**\n * Converts points to SVG path\n * @param {Array} points Array of points\n * @return {String} SVG path\n */convertPointsToSVGPath:function convertPointsToSVGPath(points){var path=[],i,width=this.width/1000,p1=new fabric.Point(points[0].x,points[0].y),p2=new fabric.Point(points[1].x,points[1].y),len=points.length,multSignX=1,multSignY=0,manyPoints=len>2;if(manyPoints){multSignX=points[2].x<p2.x?-1:points[2].x===p2.x?0:1;multSignY=points[2].y<p2.y?-1:points[2].y===p2.y?0:1;}path.push('M ',p1.x-multSignX*width,' ',p1.y-multSignY*width,' ');for(i=1;i<len;i++){if(!p1.eq(p2)){var midPoint=p1.midPointFrom(p2);// p1 is our bezier control point\n// midpoint is our endpoint\n// start point is p(i-1) value.\npath.push('Q ',p1.x,' ',p1.y,' ',midPoint.x,' ',midPoint.y,' ');}p1=points[i];if(i+1<points.length){p2=points[i+1];}}if(manyPoints){multSignX=p1.x>points[i-2].x?1:p1.x===points[i-2].x?0:-1;multSignY=p1.y>points[i-2].y?1:p1.y===points[i-2].y?0:-1;}path.push('L ',p1.x+multSignX*width,' ',p1.y+multSignY*width);return path;},/**\n * Creates fabric.Path object to add on canvas\n * @param {String} pathData Path data\n * @return {fabric.Path} Path to add on canvas\n */createPath:function createPath(pathData){var path=new fabric.Path(pathData,{fill:null,stroke:this.color,strokeWidth:this.width,strokeLineCap:this.strokeLineCap,strokeMiterLimit:this.strokeMiterLimit,strokeLineJoin:this.strokeLineJoin,strokeDashArray:this.strokeDashArray});if(this.shadow){this.shadow.affectStroke=true;path.setShadow(this.shadow);}return path;},/**\n * Decimate poins array with the decimate value\n */decimatePoints:function decimatePoints(points,distance){if(points.length<=2){return points;}var zoom=this.canvas.getZoom(),adjustedDistance=Math.pow(distance/zoom,2),i,l=points.length-1,lastPoint=points[0],newPoints=[lastPoint],cDistance;for(i=1;i<l;i++){cDistance=Math.pow(lastPoint.x-points[i].x,2)+Math.pow(lastPoint.y-points[i].y,2);if(cDistance>=adjustedDistance){lastPoint=points[i];newPoints.push(lastPoint);}}if(newPoints.length===1){newPoints.push(new fabric.Point(newPoints[0].x,newPoints[0].y));}return newPoints;},/**\n * On mouseup after drawing the path on contextTop canvas\n * we use the points captured to create an new fabric path object\n * and add it to the fabric canvas.\n */_finalizeAndAddPath:function _finalizeAndAddPath(){var ctx=this.canvas.contextTop;ctx.closePath();if(this.decimate){this._points=this.decimatePoints(this._points,this.decimate);}var pathData=this.convertPointsToSVGPath(this._points).join('');if(pathData==='M 0 0 Q 0 0 0 0 L 0 0'){// do not create 0 width/height paths, as they are\n// rendered inconsistently across browsers\n// Firefox 4, for example, renders a dot,\n// whereas Chrome 10 renders nothing\nthis.canvas.requestRenderAll();return;}var path=this.createPath(pathData);this.canvas.clearContext(this.canvas.contextTop);this.canvas.add(path);this.canvas.requestRenderAll();path.setCoords();this._resetShadow();// fire event 'path' created\nthis.canvas.fire('path:created',{path:path});}});})();/**\n * CircleBrush class\n * @class fabric.CircleBrush\n */fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,/** @lends fabric.CircleBrush.prototype */{/**\n * Width of a brush\n * @type Number\n * @default\n */width:10,/**\n * Constructor\n * @param {fabric.Canvas} canvas\n * @return {fabric.CircleBrush} Instance of a circle brush\n */initialize:function initialize(canvas){this.canvas=canvas;this.points=[];},/**\n * Invoked inside on mouse down and mouse move\n * @param {Object} pointer\n */drawDot:function drawDot(pointer){var point=this.addPoint(pointer),ctx=this.canvas.contextTop;this._saveAndTransform(ctx);this.dot(ctx,point);ctx.restore();},dot:function dot(ctx,point){ctx.fillStyle=point.fill;ctx.beginPath();ctx.arc(point.x,point.y,point.radius,0,Math.PI*2,false);ctx.closePath();ctx.fill();},/**\n * Invoked on mouse down\n */onMouseDown:function onMouseDown(pointer){this.points.length=0;this.canvas.clearContext(this.canvas.contextTop);this._setShadow();this.drawDot(pointer);},/**\n * Render the full state of the brush\n * @private\n */_render:function _render(){var ctx=this.canvas.contextTop,i,len,points=this.points;this._saveAndTransform(ctx);for(i=0,len=points.length;i<len;i++){this.dot(ctx,points[i]);}ctx.restore();},/**\n * Invoked on mouse move\n * @param {Object} pointer\n */onMouseMove:function onMouseMove(pointer){if(this.needsFullRender()){this.canvas.clearContext(this.canvas.contextTop);this.addPoint(pointer);this._render();}else{this.drawDot(pointer);}},/**\n * Invoked on mouse up\n */onMouseUp:function onMouseUp(){var originalRenderOnAddRemove=this.canvas.renderOnAddRemove,i,len;this.canvas.renderOnAddRemove=false;var circles=[];for(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);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.requestRenderAll();},/**\n * @param {Object} pointer\n * @return {fabric.Point} Just added pointer point\n */addPoint:function addPoint(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;}});/**\n * SprayBrush class\n * @class fabric.SprayBrush\n */fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,/** @lends fabric.SprayBrush.prototype */{/**\n * Width of a spray\n * @type Number\n * @default\n */width:10,/**\n * Density of a spray (number of dots per chunk)\n * @type Number\n * @default\n */density:20,/**\n * Width of spray dots\n * @type Number\n * @default\n */dotWidth:1,/**\n * Width variance of spray dots\n * @type Number\n * @default\n */dotWidthVariance:1,/**\n * Whether opacity of a dot should be random\n * @type Boolean\n * @default\n */randomOpacity:false,/**\n * Whether overlapping dots (rectangles) should be removed (for performance reasons)\n * @type Boolean\n * @default\n */optimizeOverlapping:true,/**\n * Constructor\n * @param {fabric.Canvas} canvas\n * @return {fabric.SprayBrush} Instance of a spray brush\n */initialize:function initialize(canvas){this.canvas=canvas;this.sprayChunks=[];},/**\n * Invoked on mouse down\n * @param {Object} pointer\n */onMouseDown:function onMouseDown(pointer){this.sprayChunks.length=0;this.canvas.clearContext(this.canvas.contextTop);this._setShadow();this.addSprayChunk(pointer);this.render(this.sprayChunkPoints);},/**\n * Invoked on mouse move\n * @param {Object} pointer\n */onMouseMove:function onMouseMove(pointer){this.addSprayChunk(pointer);this.render(this.sprayChunkPoints);},/**\n * Invoked on mouse up\n */onMouseUp:function onMouseUp(){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});rects.push(rect);}}if(this.optimizeOverlapping){rects=this._getOptimizedRects(rects);}var group=new fabric.Group(rects);this.shadow&&group.setShadow(this.shadow);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.requestRenderAll();},/**\n * @private\n * @param {Array} rects\n */_getOptimizedRects:function _getOptimizedRects(rects){// avoid creating duplicate rects at the same coordinates\nvar uniqueRects={},key,i,len;for(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;},/**\n * Render new chunk of spray brush\n */render:function render(sprayChunk){var ctx=this.canvas.contextTop,i,len;ctx.fillStyle=this.color;this._saveAndTransform(ctx);for(i=0,len=sprayChunk.length;i<len;i++){var point=sprayChunk[i];if(typeof point.opacity!=='undefined'){ctx.globalAlpha=point.opacity;}ctx.fillRect(point.x,point.y,point.width,point.width);}ctx.restore();},/**\n * Render all spray chunks\n */_render:function _render(){var ctx=this.canvas.contextTop,i,ilen;ctx.fillStyle=this.color;this._saveAndTransform(ctx);for(i=0,ilen=this.sprayChunks.length;i<ilen;i++){this.render(this.sprayChunks[i]);}ctx.restore();},/**\n * @param {Object} pointer\n */addSprayChunk:function addSprayChunk(pointer){this.sprayChunkPoints=[];var x,y,width,radius=this.width/2,i;for(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\nMath.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);}});/**\n * PatternBrush class\n * @class fabric.PatternBrush\n * @extends fabric.BaseBrush\n */fabric.PatternBrush=fabric.util.createClass(fabric.PencilBrush,/** @lends fabric.PatternBrush.prototype */{getPatternSrc:function getPatternSrc(){var dotWidth=20,dotDistance=5,patternCanvas=fabric.util.createCanvasElement(),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 getPatternSrcFunction(){return String(this.getPatternSrc).replace('this.color','\"'+this.color+'\"');},/**\n * Creates \"pattern\" instance property\n */getPattern:function getPattern(){return this.canvas.contextTop.createPattern(this.source||this.getPatternSrc(),'repeat');},/**\n * Sets brush styles\n */_setBrushStyles:function _setBrushStyles(){this.callSuper('_setBrushStyles');this.canvas.contextTop.strokeStyle=this.getPattern();},/**\n * Creates path\n */createPath:function createPath(pathData){var path=this.callSuper('createPath',pathData),topLeft=path._getLeftTopCoords().scalarAdd(path.strokeWidth/2);path.stroke=new fabric.Pattern({source:this.source||this.getPatternSrcFunction(),offsetX:-topLeft.x,offsetY:-topLeft.y});return path;}});(function(){var _getPointer=fabric.util.getPointer,degreesToRadians=fabric.util.degreesToRadians,radiansToDegrees=fabric.util.radiansToDegrees,atan2=Math.atan2,abs=Math.abs,supportLineDash=fabric.StaticCanvas.supports('setLineDash'),STROKE_OFFSET=0.5;/**\n * Canvas class\n * @class fabric.Canvas\n * @extends fabric.StaticCanvas\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#canvas}\n * @see {@link fabric.Canvas#initialize} for constructor definition\n *\n * @fires object:modified\n * @fires object:rotated\n * @fires object:scaled\n * @fires object:moved\n * @fires object:skewed\n * @fires object:rotating\n * @fires object:scaling\n * @fires object:moving\n * @fires object:skewing\n * @fires object:selected this event is deprecated. use selection:created\n *\n * @fires before:transform\n * @fires before:selection:cleared\n * @fires selection:cleared\n * @fires selection:updated\n * @fires selection:created\n *\n * @fires path:created\n * @fires mouse:down\n * @fires mouse:move\n * @fires mouse:up\n * @fires mouse:down:before\n * @fires mouse:move:before\n * @fires mouse:up:before\n * @fires mouse:over\n * @fires mouse:out\n * @fires mouse:dblclick\n *\n * @fires dragover\n * @fires dragenter\n * @fires dragleave\n * @fires drop\n *\n */fabric.Canvas=fabric.util.createClass(fabric.StaticCanvas,/** @lends fabric.Canvas.prototype */{/**\n * Constructor\n * @param {HTMLElement | String} el <canvas> element to initialize instance on\n * @param {Object} [options] Options object\n * @return {Object} thisArg\n */initialize:function initialize(el,options){options||(options={});this.renderAndResetBound=this.renderAndReset.bind(this);this.requestRenderAllBound=this.requestRenderAll.bind(this);this._initStatic(el,options);this._initInteractive();this._createCacheCanvas();},/**\n * When true, objects can be transformed by one side (unproportionally)\n * @type Boolean\n * @default\n */uniScaleTransform:false,/**\n * Indicates which key enable unproportional scaling\n * values: 'altKey', 'shiftKey', 'ctrlKey'.\n * If `null` or 'none' or any other string that is not a modifier key\n * feature is disabled feature disabled.\n * @since 1.6.2\n * @type String\n * @default\n */uniScaleKey:'shiftKey',/**\n * When true, objects use center point as the origin of scale transformation.\n * <b>Backwards incompatibility note:</b> This property replaces \"centerTransform\" (Boolean).\n * @since 1.3.4\n * @type Boolean\n * @default\n */centeredScaling:false,/**\n * When true, objects use center point as the origin of rotate transformation.\n * <b>Backwards incompatibility note:</b> This property replaces \"centerTransform\" (Boolean).\n * @since 1.3.4\n * @type Boolean\n * @default\n */centeredRotation:false,/**\n * Indicates which key enable centered Transform\n * values: 'altKey', 'shiftKey', 'ctrlKey'.\n * If `null` or 'none' or any other string that is not a modifier key\n * feature is disabled feature disabled.\n * @since 1.6.2\n * @type String\n * @default\n */centeredKey:'altKey',/**\n * Indicates which key enable alternate action on corner\n * values: 'altKey', 'shiftKey', 'ctrlKey'.\n * If `null` or 'none' or any other string that is not a modifier key\n * feature is disabled feature disabled.\n * @since 1.6.2\n * @type String\n * @default\n */altActionKey:'shiftKey',/**\n * Indicates that canvas is interactive. This property should not be changed.\n * @type Boolean\n * @default\n */interactive:true,/**\n * Indicates whether group selection should be enabled\n * @type Boolean\n * @default\n */selection:true,/**\n * Indicates which key or keys enable multiple click selection\n * Pass value as a string or array of strings\n * values: 'altKey', 'shiftKey', 'ctrlKey'.\n * If `null` or empty or containing any other string that is not a modifier key\n * feature is disabled.\n * @since 1.6.2\n * @type String|Array\n * @default\n */selectionKey:'shiftKey',/**\n * Indicates which key enable alternative selection\n * in case of target overlapping with active object\n * values: 'altKey', 'shiftKey', 'ctrlKey'.\n * For a series of reason that come from the general expectations on how\n * things should work, this feature works only for preserveObjectStacking true.\n * If `null` or 'none' or any other string that is not a modifier key\n * feature is disabled.\n * @since 1.6.5\n * @type null|String\n * @default\n */altSelectionKey:null,/**\n * Color of selection\n * @type String\n * @default\n */selectionColor:'rgba(100, 100, 255, 0.3)',// blue\n/**\n * Default dash array pattern\n * If not empty the selection border is dashed\n * @type Array\n */selectionDashArray:[],/**\n * Color of the border of selection (usually slightly darker than color of selection itself)\n * @type String\n * @default\n */selectionBorderColor:'rgba(255, 255, 255, 0.3)',/**\n * Width of a line used in object/group selection\n * @type Number\n * @default\n */selectionLineWidth:1,/**\n * Select only shapes that are fully contained in the dragged selection rectangle.\n * @type Boolean\n * @default\n */selectionFullyContained:false,/**\n * Default cursor value used when hovering over an object on canvas\n * @type String\n * @default\n */hoverCursor:'move',/**\n * Default cursor value used when moving an object on canvas\n * @type String\n * @default\n */moveCursor:'move',/**\n * Default cursor value used for the entire canvas\n * @type String\n * @default\n */defaultCursor:'default',/**\n * Cursor value used during free drawing\n * @type String\n * @default\n */freeDrawingCursor:'crosshair',/**\n * Cursor value used for rotation point\n * @type String\n * @default\n */rotationCursor:'crosshair',/**\n * Cursor value used for disabled elements ( corners with disabled action )\n * @type String\n * @since 2.0.0\n * @default\n */notAllowedCursor:'not-allowed',/**\n * Default element class that's given to wrapper (div) element of canvas\n * @type String\n * @default\n */containerClass:'canvas-container',/**\n * When true, object detection happens on per-pixel basis rather than on per-bounding-box\n * @type Boolean\n * @default\n */perPixelTargetFind:false,/**\n * Number of pixels around target pixel to tolerate (consider active) during object detection\n * @type Number\n * @default\n */targetFindTolerance:0,/**\n * When true, target detection is skipped when hovering over canvas. This can be used to improve performance.\n * @type Boolean\n * @default\n */skipTargetFind:false,/**\n * When true, mouse events on canvas (mousedown/mousemove/mouseup) result in free drawing.\n * After mousedown, mousemove creates a shape,\n * and then mouseup finalizes it and adds an instance of `fabric.Path` onto canvas.\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-4#free_drawing}\n * @type Boolean\n * @default\n */isDrawingMode:false,/**\n * Indicates whether objects should remain in current stack position when selected.\n * When false objects are brought to top and rendered as part of the selection group\n * @type Boolean\n * @default\n */preserveObjectStacking:false,/**\n * Indicates the angle that an object will lock to while rotating.\n * @type Number\n * @since 1.6.7\n * @default\n */snapAngle:0,/**\n * Indicates the distance from the snapAngle the rotation will lock to the snapAngle.\n * When `null`, the snapThreshold will default to the snapAngle.\n * @type null|Number\n * @since 1.6.7\n * @default\n */snapThreshold:null,/**\n * Indicates if the right click on canvas can output the context menu or not\n * @type Boolean\n * @since 1.6.5\n * @default\n */stopContextMenu:false,/**\n * Indicates if the canvas can fire right click events\n * @type Boolean\n * @since 1.6.5\n * @default\n */fireRightClick:false,/**\n * Indicates if the canvas can fire middle click events\n * @type Boolean\n * @since 1.7.8\n * @default\n */fireMiddleClick:false,/**\n * Keep track of the subTargets for Mouse Events\n * @type fabric.Object[]\n */targets:[],/**\n * Keep track of the hovered target\n * @type fabric.Object\n * @private\n */_hoveredTarget:null,/**\n * hold the list of nested targets hovered\n * @type fabric.Object[]\n * @private\n */_hoveredTargets:[],/**\n * @private\n */_initInteractive:function _initInteractive(){this._currentTransform=null;this._groupSelector=null;this._initWrapperElement();this._createUpperCanvas();this._initEventListeners();this._initRetinaScaling();this.freeDrawingBrush=fabric.PencilBrush&&new fabric.PencilBrush(this);this.calcOffset();},/**\n * Divides objects in two groups, one to render immediately\n * and one to render as activeGroup.\n * @return {Array} objects to render immediately and pushes the other in the activeGroup.\n */_chooseObjectsToRender:function _chooseObjectsToRender(){var activeObjects=this.getActiveObjects(),object,objsToRender,activeGroupObjects;if(activeObjects.length>0&&!this.preserveObjectStacking){objsToRender=[];activeGroupObjects=[];for(var i=0,length=this._objects.length;i<length;i++){object=this._objects[i];if(activeObjects.indexOf(object)===-1){objsToRender.push(object);}else{activeGroupObjects.push(object);}}if(activeObjects.length>1){this._activeObject._objects=activeGroupObjects;}objsToRender.push.apply(objsToRender,activeGroupObjects);}else{objsToRender=this._objects;}return objsToRender;},/**\n * Renders both the top canvas and the secondary container canvas.\n * @return {fabric.Canvas} instance\n * @chainable\n */renderAll:function renderAll(){if(this.contextTopDirty&&!this._groupSelector&&!this.isDrawingMode){this.clearContext(this.contextTop);this.contextTopDirty=false;}if(this.hasLostContext){this.renderTopLayer(this.contextTop);}var canvasToDrawOn=this.contextContainer;this.renderCanvas(canvasToDrawOn,this._chooseObjectsToRender());return this;},renderTopLayer:function renderTopLayer(ctx){ctx.save();if(this.isDrawingMode&&this._isCurrentlyDrawing){this.freeDrawingBrush&&this.freeDrawingBrush._render();this.contextTopDirty=true;}// we render the top context - last object\nif(this.selection&&this._groupSelector){this._drawSelection(ctx);this.contextTopDirty=true;}ctx.restore();},/**\n * Method to render only the top canvas.\n * Also used to render the group selection box.\n * @return {fabric.Canvas} thisArg\n * @chainable\n */renderTop:function renderTop(){var ctx=this.contextTop;this.clearContext(ctx);this.renderTopLayer(ctx);this.fire('after:render');return this;},/**\n * Resets the current transform to its original values and chooses the type of resizing based on the event\n * @private\n */_resetCurrentTransform:function _resetCurrentTransform(){var t=this._currentTransform;t.target.set({scaleX:t.original.scaleX,scaleY:t.original.scaleY,skewX:t.original.skewX,skewY:t.original.skewY,left:t.original.left,top:t.original.top});if(this._shouldCenterTransform(t.target)){if(t.originX!=='center'){if(t.originX==='right'){t.mouseXSign=-1;}else{t.mouseXSign=1;}}if(t.originY!=='center'){if(t.originY==='bottom'){t.mouseYSign=-1;}else{t.mouseYSign=1;}}t.originX='center';t.originY='center';}else{t.originX=t.original.originX;t.originY=t.original.originY;}},/**\n * Checks if point is contained within an area of given object\n * @param {Event} e Event object\n * @param {fabric.Object} target Object to test against\n * @param {Object} [point] x,y object of point coordinates we want to check.\n * @return {Boolean} true if point is contained within an area of given object\n */containsPoint:function containsPoint(e,target,point){var ignoreZoom=true,pointer=point||this.getPointer(e,ignoreZoom),xy;if(target.group&&target.group===this._activeObject&&target.group.type==='activeSelection'){xy=this._normalizePointer(target.group,pointer);}else{xy={x:pointer.x,y:pointer.y};}// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html\n// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html\nreturn target.containsPoint(xy)||target._findTargetCorner(pointer);},/**\n * @private\n */_normalizePointer:function _normalizePointer(object,pointer){var m=object.calcTransformMatrix(),invertedM=fabric.util.invertTransform(m),vptPointer=this.restorePointerVpt(pointer);return fabric.util.transformPoint(vptPointer,invertedM);},/**\n * Returns true if object is transparent at a certain location\n * @param {fabric.Object} target Object to check\n * @param {Number} x Left coordinate\n * @param {Number} y Top coordinate\n * @return {Boolean}\n */isTargetTransparent:function isTargetTransparent(target,x,y){// in case the target is the activeObject, we cannot execute this optimization\n// because we need to draw controls too.\nif(target.shouldCache()&&target._cacheCanvas&&target!==this._activeObject){var normalizedPointer=this._normalizePointer(target,{x:x,y:y}),targetRelativeX=Math.max(target.cacheTranslationX+normalizedPointer.x*target.zoomX,0),targetRelativeY=Math.max(target.cacheTranslationY+normalizedPointer.y*target.zoomY,0);var isTransparent=fabric.util.isTransparent(target._cacheContext,Math.round(targetRelativeX),Math.round(targetRelativeY),this.targetFindTolerance);return isTransparent;}var ctx=this.contextCache,originalColor=target.selectionBackgroundColor,v=this.viewportTransform;target.selectionBackgroundColor='';this.clearContext(ctx);ctx.save();ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);target.render(ctx);ctx.restore();target===this._activeObject&&target._renderControls(ctx,{hasBorders:false,transparentCorners:false},{hasBorders:false});target.selectionBackgroundColor=originalColor;var isTransparent=fabric.util.isTransparent(ctx,x,y,this.targetFindTolerance);return isTransparent;},/**\n * takes an event and determins if selection key has been pressed\n * @private\n * @param {Event} e Event object\n */_isSelectionKeyPressed:function _isSelectionKeyPressed(e){var selectionKeyPressed=false;if(Object.prototype.toString.call(this.selectionKey)==='[object Array]'){selectionKeyPressed=!!this.selectionKey.find(function(key){return e[key]===true;});}else{selectionKeyPressed=e[this.selectionKey];}return selectionKeyPressed;},/**\n * @private\n * @param {Event} e Event object\n * @param {fabric.Object} target\n */_shouldClearSelection:function _shouldClearSelection(e,target){var activeObjects=this.getActiveObjects(),activeObject=this._activeObject;return!target||target&&activeObject&&activeObjects.length>1&&activeObjects.indexOf(target)===-1&&activeObject!==target&&!this._isSelectionKeyPressed(e)||target&&!target.evented||target&&!target.selectable&&activeObject&&activeObject!==target;},/**\n * centeredScaling from object can't override centeredScaling from canvas.\n * this should be fixed, since object setting should take precedence over canvas.\n * @private\n * @param {fabric.Object} target\n */_shouldCenterTransform:function _shouldCenterTransform(target){if(!target){return;}var t=this._currentTransform,centerTransform;if(t.action==='scale'||t.action==='scaleX'||t.action==='scaleY'){centerTransform=this.centeredScaling||target.centeredScaling;}else if(t.action==='rotate'){centerTransform=this.centeredRotation||target.centeredRotation;}return centerTransform?!t.altKey:t.altKey;},/**\n * @private\n */_getOriginFromCorner:function _getOriginFromCorner(target,corner){var origin={x:target.originX,y:target.originY};if(corner==='ml'||corner==='tl'||corner==='bl'){origin.x='right';}else if(corner==='mr'||corner==='tr'||corner==='br'){origin.x='left';}if(corner==='tl'||corner==='mt'||corner==='tr'){origin.y='bottom';}else if(corner==='bl'||corner==='mb'||corner==='br'){origin.y='top';}return origin;},/**\n * @private\n * @param {Boolean} alreadySelected true if target is already selected\n * @param {String} corner a string representing the corner ml, mr, tl ...\n * @param {Event} e Event object\n * @param {fabric.Object} [target] inserted back to help overriding. Unused\n */_getActionFromCorner:function _getActionFromCorner(alreadySelected,corner,e/* target */){if(!corner||!alreadySelected){return'drag';}switch(corner){case'mtr':return'rotate';case'ml':case'mr':return e[this.altActionKey]?'skewY':'scaleX';case'mt':case'mb':return e[this.altActionKey]?'skewX':'scaleY';default:return'scale';}},/**\n * @private\n * @param {Event} e Event object\n * @param {fabric.Object} target\n */_setupCurrentTransform:function _setupCurrentTransform(e,target,alreadySelected){if(!target){return;}var pointer=this.getPointer(e),corner=target._findTargetCorner(this.getPointer(e,true)),action=this._getActionFromCorner(alreadySelected,corner,e,target),origin=this._getOriginFromCorner(target,corner);this._currentTransform={target:target,action:action,corner:corner,scaleX:target.scaleX,scaleY:target.scaleY,skewX:target.skewX,skewY:target.skewY,// used by transation\noffsetX:pointer.x-target.left,offsetY:pointer.y-target.top,originX:origin.x,originY:origin.y,ex:pointer.x,ey:pointer.y,lastX:pointer.x,lastY:pointer.y,// unsure they are usefull anymore.\n// left: target.left,\n// top: target.top,\ntheta:degreesToRadians(target.angle),// end of unsure\nwidth:target.width*target.scaleX,mouseXSign:1,mouseYSign:1,shiftKey:e.shiftKey,altKey:e[this.centeredKey],original:fabric.util.saveObjectTransform(target)};this._currentTransform.original.originX=origin.x;this._currentTransform.original.originY=origin.y;this._resetCurrentTransform();this._beforeTransform(e);},/**\n * Translates object by \"setting\" its left/top\n * @private\n * @param {Number} x pointer's x coordinate\n * @param {Number} y pointer's y coordinate\n * @return {Boolean} true if the translation occurred\n */_translateObject:function _translateObject(x,y){var transform=this._currentTransform,target=transform.target,newLeft=x-transform.offsetX,newTop=y-transform.offsetY,moveX=!target.get('lockMovementX')&&target.left!==newLeft,moveY=!target.get('lockMovementY')&&target.top!==newTop;moveX&&target.set('left',newLeft);moveY&&target.set('top',newTop);return moveX||moveY;},/**\n * Check if we are increasing a positive skew or lower it,\n * checking mouse direction and pressed corner.\n * @private\n */_changeSkewTransformOrigin:function _changeSkewTransformOrigin(mouseMove,t,by){var property='originX',origins={0:'center'},skew=t.target.skewX,originA='left',originB='right',corner=t.corner==='mt'||t.corner==='ml'?1:-1,flipSign=1;mouseMove=mouseMove>0?1:-1;if(by==='y'){skew=t.target.skewY;originA='top';originB='bottom';property='originY';}origins[-1]=originA;origins[1]=originB;t.target.flipX&&(flipSign*=-1);t.target.flipY&&(flipSign*=-1);if(skew===0){t.skewSign=-corner*mouseMove*flipSign;t[property]=origins[-mouseMove];}else{skew=skew>0?1:-1;t.skewSign=skew;t[property]=origins[skew*corner*flipSign];}},/**\n * Skew object by mouse events\n * @private\n * @param {Number} x pointer's x coordinate\n * @param {Number} y pointer's y coordinate\n * @param {String} by Either 'x' or 'y'\n * @return {Boolean} true if the skewing occurred\n */_skewObject:function _skewObject(x,y,by){var t=this._currentTransform,target=t.target,skewed=false,lockSkewingX=target.get('lockSkewingX'),lockSkewingY=target.get('lockSkewingY');if(lockSkewingX&&by==='x'||lockSkewingY&&by==='y'){return false;}// Get the constraint point\nvar center=target.getCenterPoint(),actualMouseByCenter=target.toLocalPoint(new fabric.Point(x,y),'center','center')[by],lastMouseByCenter=target.toLocalPoint(new fabric.Point(t.lastX,t.lastY),'center','center')[by],actualMouseByOrigin,constraintPosition,dim=target._getTransformedDimensions();this._changeSkewTransformOrigin(actualMouseByCenter-lastMouseByCenter,t,by);actualMouseByOrigin=target.toLocalPoint(new fabric.Point(x,y),t.originX,t.originY)[by];constraintPosition=target.translateToOriginPoint(center,t.originX,t.originY);// Actually skew the object\nskewed=this._setObjectSkew(actualMouseByOrigin,t,by,dim);t.lastX=x;t.lastY=y;// Make sure the constraints apply\ntarget.setPositionByOrigin(constraintPosition,t.originX,t.originY);return skewed;},/**\n * Set object skew\n * @private\n * @return {Boolean} true if the skewing occurred\n */_setObjectSkew:function _setObjectSkew(localMouse,transform,by,_dim){var target=transform.target,newValue,skewed=false,skewSign=transform.skewSign,newDim,dimNoSkew,otherBy,_otherBy,_by,newDimMouse,skewX,skewY;if(by==='x'){otherBy='y';_otherBy='Y';_by='X';skewX=0;skewY=target.skewY;}else{otherBy='x';_otherBy='X';_by='Y';skewX=target.skewX;skewY=0;}dimNoSkew=target._getTransformedDimensions(skewX,skewY);newDimMouse=2*Math.abs(localMouse)-dimNoSkew[by];if(newDimMouse<=2){newValue=0;}else{newValue=skewSign*Math.atan(newDimMouse/target['scale'+_by]/(dimNoSkew[otherBy]/target['scale'+_otherBy]));newValue=fabric.util.radiansToDegrees(newValue);}skewed=target['skew'+_by]!==newValue;target.set('skew'+_by,newValue);if(target['skew'+_otherBy]!==0){newDim=target._getTransformedDimensions();newValue=_dim[otherBy]/newDim[otherBy]*target['scale'+_otherBy];target.set('scale'+_otherBy,newValue);}return skewed;},/**\n * Scales object by invoking its scaleX/scaleY methods\n * @private\n * @param {Number} x pointer's x coordinate\n * @param {Number} y pointer's y coordinate\n * @param {String} by Either 'x' or 'y' - specifies dimension constraint by which to scale an object.\n * When not provided, an object is scaled by both dimensions equally\n * @return {Boolean} true if the scaling occurred\n */_scaleObject:function _scaleObject(x,y,by){var t=this._currentTransform,target=t.target,lockScalingX=target.lockScalingX,lockScalingY=target.lockScalingY,lockScalingFlip=target.lockScalingFlip;if(lockScalingX&&lockScalingY){return false;}// Get the constraint point\nvar constraintPosition=target.translateToOriginPoint(target.getCenterPoint(),t.originX,t.originY),localMouse=target.toLocalPoint(new fabric.Point(x,y),t.originX,t.originY),dim=target._getTransformedDimensions(),scaled=false;this._setLocalMouse(localMouse,t);// Actually scale the object\nscaled=this._setObjectScale(localMouse,t,lockScalingX,lockScalingY,by,lockScalingFlip,dim);// Make sure the constraints apply\ntarget.setPositionByOrigin(constraintPosition,t.originX,t.originY);return scaled;},/**\n * @private\n * @return {Boolean} true if the scaling occurred\n */_setObjectScale:function _setObjectScale(localMouse,transform,lockScalingX,lockScalingY,by,lockScalingFlip,_dim){var target=transform.target,forbidScalingX=false,forbidScalingY=false,scaled=false,scaleX=localMouse.x*target.scaleX/_dim.x,scaleY=localMouse.y*target.scaleY/_dim.y,changeX=target.scaleX!==scaleX,changeY=target.scaleY!==scaleY;transform.newScaleX=scaleX;transform.newScaleY=scaleY;if(fabric.Textbox&&by==='x'&&target instanceof fabric.Textbox){var w=target.width*(localMouse.x/_dim.x);if(w>=target.getMinWidth()){scaled=w!==target.width;target.set('width',w);return scaled;}return false;}if(lockScalingFlip&&scaleX<=0&&scaleX<target.scaleX){forbidScalingX=true;localMouse.x=0;}if(lockScalingFlip&&scaleY<=0&&scaleY<target.scaleY){forbidScalingY=true;localMouse.y=0;}if(by==='equally'&&!lockScalingX&&!lockScalingY){scaled=this._scaleObjectEqually(localMouse,target,transform,_dim);}else if(!by){forbidScalingX||lockScalingX||target.set('scaleX',scaleX)&&(scaled=scaled||changeX);forbidScalingY||lockScalingY||target.set('scaleY',scaleY)&&(scaled=scaled||changeY);}else if(by==='x'&&!target.get('lockUniScaling')){forbidScalingX||lockScalingX||target.set('scaleX',scaleX)&&(scaled=changeX);}else if(by==='y'&&!target.get('lockUniScaling')){forbidScalingY||lockScalingY||target.set('scaleY',scaleY)&&(scaled=changeY);}forbidScalingX||forbidScalingY||this._flipObject(transform,by);return scaled;},/**\n * @private\n * @return {Boolean} true if the scaling occurred\n */_scaleObjectEqually:function _scaleObjectEqually(localMouse,target,transform,_dim){var dist=localMouse.y+localMouse.x,lastDist=_dim.y*transform.original.scaleY/target.scaleY+_dim.x*transform.original.scaleX/target.scaleX,scaled,signX=localMouse.x<0?-1:1,signY=localMouse.y<0?-1:1,newScaleX,newScaleY;// We use transform.scaleX/Y instead of target.scaleX/Y\n// because the object may have a min scale and we'll loose the proportions\nnewScaleX=signX*Math.abs(transform.original.scaleX*dist/lastDist);newScaleY=signY*Math.abs(transform.original.scaleY*dist/lastDist);scaled=newScaleX!==target.scaleX||newScaleY!==target.scaleY;target.set('scaleX',newScaleX);target.set('scaleY',newScaleY);return scaled;},/**\n * @private\n */_flipObject:function _flipObject(transform,by){if(transform.newScaleX<0&&by!=='y'){if(transform.originX==='left'){transform.originX='right';}else if(transform.originX==='right'){transform.originX='left';}}if(transform.newScaleY<0&&by!=='x'){if(transform.originY==='top'){transform.originY='bottom';}else if(transform.originY==='bottom'){transform.originY='top';}}},/**\n * @private\n */_setLocalMouse:function _setLocalMouse(localMouse,t){var target=t.target,zoom=this.getZoom(),padding=target.padding/zoom;if(t.originX==='right'){localMouse.x*=-1;}else if(t.originX==='center'){localMouse.x*=t.mouseXSign*2;if(localMouse.x<0){t.mouseXSign=-t.mouseXSign;}}if(t.originY==='bottom'){localMouse.y*=-1;}else if(t.originY==='center'){localMouse.y*=t.mouseYSign*2;if(localMouse.y<0){t.mouseYSign=-t.mouseYSign;}}// adjust the mouse coordinates when dealing with padding\nif(abs(localMouse.x)>padding){if(localMouse.x<0){localMouse.x+=padding;}else{localMouse.x-=padding;}}else{// mouse is within the padding, set to 0\nlocalMouse.x=0;}if(abs(localMouse.y)>padding){if(localMouse.y<0){localMouse.y+=padding;}else{localMouse.y-=padding;}}else{localMouse.y=0;}},/**\n * Rotates object by invoking its rotate method\n * @private\n * @param {Number} x pointer's x coordinate\n * @param {Number} y pointer's y coordinate\n * @return {Boolean} true if the rotation occurred\n */_rotateObject:function _rotateObject(x,y){var t=this._currentTransform,target=t.target,constraintPosition,constraintPosition=target.translateToOriginPoint(target.getCenterPoint(),t.originX,t.originY);if(target.lockRotation){return false;}var lastAngle=atan2(t.ey-constraintPosition.y,t.ex-constraintPosition.x),curAngle=atan2(y-constraintPosition.y,x-constraintPosition.x),angle=radiansToDegrees(curAngle-lastAngle+t.theta),hasRotated=true;if(target.snapAngle>0){var snapAngle=target.snapAngle,snapThreshold=target.snapThreshold||snapAngle,rightAngleLocked=Math.ceil(angle/snapAngle)*snapAngle,leftAngleLocked=Math.floor(angle/snapAngle)*snapAngle;if(Math.abs(angle-leftAngleLocked)<snapThreshold){angle=leftAngleLocked;}else if(Math.abs(angle-rightAngleLocked)<snapThreshold){angle=rightAngleLocked;}}// normalize angle to positive value\nif(angle<0){angle=360+angle;}angle%=360;if(target.angle===angle){hasRotated=false;}else{// rotation only happen here\ntarget.angle=angle;// Make sure the constraints apply\ntarget.setPositionByOrigin(constraintPosition,t.originX,t.originY);}return hasRotated;},/**\n * Set the cursor type of the canvas element\n * @param {String} value Cursor type of the canvas element.\n * @see http://www.w3.org/TR/css3-ui/#cursor\n */setCursor:function setCursor(value){this.upperCanvasEl.style.cursor=value;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx to draw the selection on\n */_drawSelection:function _drawSelection(ctx){var groupSelector=this._groupSelector,left=groupSelector.left,top=groupSelector.top,aleft=abs(left),atop=abs(top);if(this.selectionColor){ctx.fillStyle=this.selectionColor;ctx.fillRect(groupSelector.ex-(left>0?0:-left),groupSelector.ey-(top>0?0:-top),aleft,atop);}if(!this.selectionLineWidth||!this.selectionBorderColor){return;}ctx.lineWidth=this.selectionLineWidth;ctx.strokeStyle=this.selectionBorderColor;// selection border\nif(this.selectionDashArray.length>1&&!supportLineDash){var px=groupSelector.ex+STROKE_OFFSET-(left>0?0:aleft),py=groupSelector.ey+STROKE_OFFSET-(top>0?0:atop);ctx.beginPath();fabric.util.drawDashedLine(ctx,px,py,px+aleft,py,this.selectionDashArray);fabric.util.drawDashedLine(ctx,px,py+atop-1,px+aleft,py+atop-1,this.selectionDashArray);fabric.util.drawDashedLine(ctx,px,py,px,py+atop,this.selectionDashArray);fabric.util.drawDashedLine(ctx,px+aleft-1,py,px+aleft-1,py+atop,this.selectionDashArray);ctx.closePath();ctx.stroke();}else{fabric.Object.prototype._setLineDash.call(this,ctx,this.selectionDashArray);ctx.strokeRect(groupSelector.ex+STROKE_OFFSET-(left>0?0:aleft),groupSelector.ey+STROKE_OFFSET-(top>0?0:atop),aleft,atop);}},/**\n * Method that determines what object we are clicking on\n * the skipGroup parameter is for internal use, is needed for shift+click action\n * 11/09/2018 TODO: would be cool if findTarget could discern between being a full target\n * or the outside part of the corner.\n * @param {Event} e mouse event\n * @param {Boolean} skipGroup when true, activeGroup is skipped and only objects are traversed through\n * @return {fabric.Object} the target found\n */findTarget:function findTarget(e,skipGroup){if(this.skipTargetFind){return;}var ignoreZoom=true,pointer=this.getPointer(e,ignoreZoom),activeObject=this._activeObject,aObjects=this.getActiveObjects(),activeTarget,activeTargetSubs;// first check current group (if one exists)\n// active group does not check sub targets like normal groups.\n// if active group just exits.\nthis.targets=[];if(aObjects.length>1&&!skipGroup&&activeObject===this._searchPossibleTargets([activeObject],pointer)){return activeObject;}// if we hit the corner of an activeObject, let's return that.\nif(aObjects.length===1&&activeObject._findTargetCorner(pointer)){return activeObject;}if(aObjects.length===1&&activeObject===this._searchPossibleTargets([activeObject],pointer)){if(!this.preserveObjectStacking){return activeObject;}else{activeTarget=activeObject;activeTargetSubs=this.targets;this.targets=[];}}var target=this._searchPossibleTargets(this._objects,pointer);if(e[this.altSelectionKey]&&target&&activeTarget&&target!==activeTarget){target=activeTarget;this.targets=activeTargetSubs;}return target;},/**\n * Checks point is inside the object.\n * @param {Object} [pointer] x,y object of point coordinates we want to check.\n * @param {fabric.Object} obj Object to test against\n * @param {Object} [globalPointer] x,y object of point coordinates relative to canvas used to search per pixel target.\n * @return {Boolean} true if point is contained within an area of given object\n * @private\n */_checkTarget:function _checkTarget(pointer,obj,globalPointer){if(obj&&obj.visible&&obj.evented&&this.containsPoint(null,obj,pointer)){if((this.perPixelTargetFind||obj.perPixelTargetFind)&&!obj.isEditing){var isTransparent=this.isTargetTransparent(obj,globalPointer.x,globalPointer.y);if(!isTransparent){return true;}}else{return true;}}},/**\n * Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted\n * @param {Array} [objects] objects array to look into\n * @param {Object} [pointer] x,y object of point coordinates we want to check.\n * @return {fabric.Object} object that contains pointer\n * @private\n */_searchPossibleTargets:function _searchPossibleTargets(objects,pointer){// Cache all targets where their bounding box contains point.\nvar target,i=objects.length,subTarget;// Do not check for currently grouped objects, since we check the parent group itself.\n// until we call this function specifically to search inside the activeGroup\nwhile(i--){var objToCheck=objects[i];var pointerToUse=objToCheck.group&&objToCheck.group.type!=='activeSelection'?this._normalizePointer(objToCheck.group,pointer):pointer;if(this._checkTarget(pointerToUse,objToCheck,pointer)){target=objects[i];if(target.subTargetCheck&&target instanceof fabric.Group){subTarget=this._searchPossibleTargets(target._objects,pointer);subTarget&&this.targets.push(subTarget);}break;}}return target;},/**\n * Returns pointer coordinates without the effect of the viewport\n * @param {Object} pointer with \"x\" and \"y\" number values\n * @return {Object} object with \"x\" and \"y\" number values\n */restorePointerVpt:function restorePointerVpt(pointer){return fabric.util.transformPoint(pointer,fabric.util.invertTransform(this.viewportTransform));},/**\n * Returns pointer coordinates relative to canvas.\n * Can return coordinates with or without viewportTransform.\n * ignoreZoom false gives back coordinates that represent\n * the point clicked on canvas element.\n * ignoreZoom true gives back coordinates after being processed\n * by the viewportTransform ( sort of coordinates of what is displayed\n * on the canvas where you are clicking.\n * ignoreZoom true = HTMLElement coordinates relative to top,left\n * ignoreZoom false, default = fabric space coordinates, the same used for shape position\n * To interact with your shapes top and left you want to use ignoreZoom true\n * most of the time, while ignoreZoom false will give you coordinates\n * compatible with the object.oCoords system.\n * of the time.\n * @param {Event} e\n * @param {Boolean} ignoreZoom\n * @return {Object} object with \"x\" and \"y\" number values\n */getPointer:function getPointer(e,ignoreZoom){// return cached values if we are in the event processing chain\nif(this._absolutePointer&&!ignoreZoom){return this._absolutePointer;}if(this._pointer&&ignoreZoom){return this._pointer;}var pointer=_getPointer(e),upperCanvasEl=this.upperCanvasEl,bounds=upperCanvasEl.getBoundingClientRect(),boundsWidth=bounds.width||0,boundsHeight=bounds.height||0,cssScale;if(!boundsWidth||!boundsHeight){if('top'in bounds&&'bottom'in bounds){boundsHeight=Math.abs(bounds.top-bounds.bottom);}if('right'in bounds&&'left'in bounds){boundsWidth=Math.abs(bounds.right-bounds.left);}}this.calcOffset();pointer.x=pointer.x-this._offset.left;pointer.y=pointer.y-this._offset.top;if(!ignoreZoom){pointer=this.restorePointerVpt(pointer);}var retinaScaling=this.getRetinaScaling();if(retinaScaling!==1){pointer.x/=retinaScaling;pointer.y/=retinaScaling;}if(boundsWidth===0||boundsHeight===0){// If bounds are not available (i.e. not visible), do not apply scale.\ncssScale={width:1,height:1};}else{cssScale={width:upperCanvasEl.width/boundsWidth,height:upperCanvasEl.height/boundsHeight};}return{x:pointer.x*cssScale.width,y:pointer.y*cssScale.height};},/**\n * @private\n * @throws {CANVAS_INIT_ERROR} If canvas can not be initialized\n */_createUpperCanvas:function _createUpperCanvas(){var lowerCanvasClass=this.lowerCanvasEl.className.replace(/\\s*lower-canvas\\s*/,''),lowerCanvasEl=this.lowerCanvasEl,upperCanvasEl=this.upperCanvasEl;// there is no need to create a new upperCanvas element if we have already one.\nif(upperCanvasEl){upperCanvasEl.className='';}else{upperCanvasEl=this._createCanvasElement();this.upperCanvasEl=upperCanvasEl;}fabric.util.addClass(upperCanvasEl,'upper-canvas '+lowerCanvasClass);this.wrapperEl.appendChild(upperCanvasEl);this._copyCanvasStyle(lowerCanvasEl,upperCanvasEl);this._applyCanvasStyle(upperCanvasEl);this.contextTop=upperCanvasEl.getContext('2d');},/**\n * @private\n */_createCacheCanvas:function _createCacheCanvas(){this.cacheCanvasEl=this._createCanvasElement();this.cacheCanvasEl.setAttribute('width',this.width);this.cacheCanvasEl.setAttribute('height',this.height);this.contextCache=this.cacheCanvasEl.getContext('2d');},/**\n * @private\n */_initWrapperElement:function _initWrapperElement(){this.wrapperEl=fabric.util.wrapElement(this.lowerCanvasEl,'div',{'class':this.containerClass});fabric.util.setStyle(this.wrapperEl,{width:this.width+'px',height:this.height+'px',position:'relative'});fabric.util.makeElementUnselectable(this.wrapperEl);},/**\n * @private\n * @param {HTMLElement} element canvas element to apply styles on\n */_applyCanvasStyle:function _applyCanvasStyle(element){var width=this.width||element.width,height=this.height||element.height;fabric.util.setStyle(element,{position:'absolute',width:width+'px',height:height+'px',left:0,top:0,'touch-action':this.allowTouchScrolling?'manipulation':'none','-ms-touch-action':this.allowTouchScrolling?'manipulation':'none'});element.width=width;element.height=height;fabric.util.makeElementUnselectable(element);},/**\n * Copy the entire inline style from one element (fromEl) to another (toEl)\n * @private\n * @param {Element} fromEl Element style is copied from\n * @param {Element} toEl Element copied style is applied to\n */_copyCanvasStyle:function _copyCanvasStyle(fromEl,toEl){toEl.style.cssText=fromEl.style.cssText;},/**\n * Returns context of canvas where object selection is drawn\n * @return {CanvasRenderingContext2D}\n */getSelectionContext:function getSelectionContext(){return this.contextTop;},/**\n * Returns <canvas> element on which object selection is drawn\n * @return {HTMLCanvasElement}\n */getSelectionElement:function getSelectionElement(){return this.upperCanvasEl;},/**\n * Returns currently active object\n * @return {fabric.Object} active object\n */getActiveObject:function getActiveObject(){return this._activeObject;},/**\n * Returns an array with the current selected objects\n * @return {fabric.Object} active object\n */getActiveObjects:function getActiveObjects(){var active=this._activeObject;if(active){if(active.type==='activeSelection'&&active._objects){return active._objects.slice(0);}else{return[active];}}return[];},/**\n * @private\n * @param {fabric.Object} obj Object that was removed\n */_onObjectRemoved:function _onObjectRemoved(obj){// removing active object should fire \"selection:cleared\" events\nif(obj===this._activeObject){this.fire('before:selection:cleared',{target:obj});this._discardActiveObject();this.fire('selection:cleared',{target:obj});obj.fire('deselected');}if(obj===this._hoveredTarget){this._hoveredTarget=null;this._hoveredTargets=[];}this.callSuper('_onObjectRemoved',obj);},/**\n * @private\n * Compares the old activeObject with the current one and fires correct events\n * @param {fabric.Object} obj old activeObject\n */_fireSelectionEvents:function _fireSelectionEvents(oldObjects,e){var somethingChanged=false,objects=this.getActiveObjects(),added=[],removed=[],opt={e:e};oldObjects.forEach(function(oldObject){if(objects.indexOf(oldObject)===-1){somethingChanged=true;oldObject.fire('deselected',opt);removed.push(oldObject);}});objects.forEach(function(object){if(oldObjects.indexOf(object)===-1){somethingChanged=true;object.fire('selected',opt);added.push(object);}});if(oldObjects.length>0&&objects.length>0){opt.selected=added;opt.deselected=removed;// added for backward compatibility\nopt.updated=added[0]||removed[0];opt.target=this._activeObject;somethingChanged&&this.fire('selection:updated',opt);}else if(objects.length>0){// deprecated event\nif(objects.length===1){opt.target=added[0];this.fire('object:selected',opt);}opt.selected=added;// added for backward compatibility\nopt.target=this._activeObject;this.fire('selection:created',opt);}else if(oldObjects.length>0){opt.deselected=removed;this.fire('selection:cleared',opt);}},/**\n * Sets given object as the only active object on canvas\n * @param {fabric.Object} object Object to set as an active one\n * @param {Event} [e] Event (passed along when firing \"object:selected\")\n * @return {fabric.Canvas} thisArg\n * @chainable\n */setActiveObject:function setActiveObject(object,e){var currentActives=this.getActiveObjects();this._setActiveObject(object,e);this._fireSelectionEvents(currentActives,e);return this;},/**\n * @private\n * @param {Object} object to set as active\n * @param {Event} [e] Event (passed along when firing \"object:selected\")\n * @return {Boolean} true if the selection happened\n */_setActiveObject:function _setActiveObject(object,e){if(this._activeObject===object){return false;}if(!this._discardActiveObject(e,object)){return false;}if(object.onSelect({e:e})){return false;}this._activeObject=object;return true;},/**\n * @private\n */_discardActiveObject:function _discardActiveObject(e,object){var obj=this._activeObject;if(obj){// onDeselect return TRUE to cancel selection;\nif(obj.onDeselect({e:e,object:object})){return false;}this._activeObject=null;}return true;},/**\n * Discards currently active object and fire events. If the function is called by fabric\n * as a consequence of a mouse event, the event is passed as a parameter and\n * sent to the fire function for the custom events. When used as a method the\n * e param does not have any application.\n * @param {event} e\n * @return {fabric.Canvas} thisArg\n * @chainable\n */discardActiveObject:function discardActiveObject(e){var currentActives=this.getActiveObjects(),activeObject=this.getActiveObject();if(currentActives.length){this.fire('before:selection:cleared',{target:activeObject,e:e});}this._discardActiveObject(e);this._fireSelectionEvents(currentActives,e);return this;},/**\n * Clears a canvas element and removes all event listeners\n * @return {fabric.Canvas} thisArg\n * @chainable\n */dispose:function dispose(){var wrapper=this.wrapperEl;this.removeListeners();wrapper.removeChild(this.upperCanvasEl);wrapper.removeChild(this.lowerCanvasEl);this.contextCache=null;this.contextTop=null;['upperCanvasEl','cacheCanvasEl'].forEach(function(element){fabric.util.cleanUpJsdomNode(this[element]);this[element]=undefined;}.bind(this));if(wrapper.parentNode){wrapper.parentNode.replaceChild(this.lowerCanvasEl,this.wrapperEl);}delete this.wrapperEl;fabric.StaticCanvas.prototype.dispose.call(this);return this;},/**\n * Clears all contexts (background, main, top) of an instance\n * @return {fabric.Canvas} thisArg\n * @chainable\n */clear:function clear(){// this.discardActiveGroup();\nthis.discardActiveObject();this.clearContext(this.contextTop);return this.callSuper('clear');},/**\n * Draws objects' controls (borders/controls)\n * @param {CanvasRenderingContext2D} ctx Context to render controls on\n */drawControls:function drawControls(ctx){var activeObject=this._activeObject;if(activeObject){activeObject._renderControls(ctx);}},/**\n * @private\n */_toObject:function _toObject(instance,methodName,propertiesToInclude){//If the object is part of the current selection group, it should\n//be transformed appropriately\n//i.e. it should be serialised as it would appear if the selection group\n//were to be destroyed.\nvar originalProperties=this._realizeGroupTransformOnObject(instance),object=this.callSuper('_toObject',instance,methodName,propertiesToInclude);//Undo the damage we did by changing all of its properties\nthis._unwindGroupTransformOnObject(instance,originalProperties);return object;},/**\n * Realises an object's group transformation on it\n * @private\n * @param {fabric.Object} [instance] the object to transform (gets mutated)\n * @returns the original values of instance which were changed\n */_realizeGroupTransformOnObject:function _realizeGroupTransformOnObject(instance){if(instance.group&&instance.group.type==='activeSelection'&&this._activeObject===instance.group){var layoutProps=['angle','flipX','flipY','left','scaleX','scaleY','skewX','skewY','top'];//Copy all the positionally relevant properties across now\nvar originalValues={};layoutProps.forEach(function(prop){originalValues[prop]=instance[prop];});this._activeObject.realizeTransform(instance);return originalValues;}else{return null;}},/**\n * Restores the changed properties of instance\n * @private\n * @param {fabric.Object} [instance] the object to un-transform (gets mutated)\n * @param {Object} [originalValues] the original values of instance, as returned by _realizeGroupTransformOnObject\n */_unwindGroupTransformOnObject:function _unwindGroupTransformOnObject(instance,originalValues){if(originalValues){instance.set(originalValues);}},/**\n * @private\n */_setSVGObject:function _setSVGObject(markup,instance,reviver){//If the object is in a selection group, simulate what would happen to that\n//object when the group is deselected\nvar originalProperties=this._realizeGroupTransformOnObject(instance);this.callSuper('_setSVGObject',markup,instance,reviver);this._unwindGroupTransformOnObject(instance,originalProperties);},setViewportTransform:function setViewportTransform(vpt){if(this.renderOnAddRemove&&this._activeObject&&this._activeObject.isEditing){this._activeObject.clearContextTop();}fabric.StaticCanvas.prototype.setViewportTransform.call(this,vpt);}});// copying static properties manually to work around Opera's bug,\n// where \"prototype\" property is enumerable and overrides existing prototype\nfor(var prop in fabric.StaticCanvas){if(prop!=='prototype'){fabric.Canvas[prop]=fabric.StaticCanvas[prop];}}})();(function(){var cursorOffset={mt:0,// n\ntr:1,// ne\nmr:2,// e\nbr:3,// se\nmb:4,// s\nbl:5,// sw\nml:6,// w\ntl:7// nw\n},addListener=fabric.util.addListener,removeListener=fabric.util.removeListener,RIGHT_CLICK=3,MIDDLE_CLICK=2,LEFT_CLICK=1,addEventOptions={passive:false};function checkClick(e,value){return e.button&&e.button===value-1;}fabric.util.object.extend(fabric.Canvas.prototype,/** @lends fabric.Canvas.prototype */{/**\n * Map of cursor style values for each of the object controls\n * @private\n */cursorMap:['n-resize','ne-resize','e-resize','se-resize','s-resize','sw-resize','w-resize','nw-resize'],/**\n * Contains the id of the touch event that owns the fabric transform\n * @type Number\n * @private\n */mainTouchId:null,/**\n * Adds mouse listeners to canvas\n * @private\n */_initEventListeners:function _initEventListeners(){// in case we initialized the class twice. This should not happen normally\n// but in some kind of applications where the canvas element may be changed\n// this is a workaround to having double listeners.\nthis.removeListeners();this._bindEvents();this.addOrRemove(addListener,'add');},/**\n * return an event prefix pointer or mouse.\n * @private\n */_getEventPrefix:function _getEventPrefix(){return this.enablePointerEvents?'pointer':'mouse';},addOrRemove:function addOrRemove(functor,eventjsFunctor){var canvasElement=this.upperCanvasEl,eventTypePrefix=this._getEventPrefix();functor(fabric.window,'resize',this._onResize);functor(canvasElement,eventTypePrefix+'down',this._onMouseDown);functor(canvasElement,eventTypePrefix+'move',this._onMouseMove,addEventOptions);functor(canvasElement,eventTypePrefix+'out',this._onMouseOut);functor(canvasElement,eventTypePrefix+'enter',this._onMouseEnter);functor(canvasElement,'wheel',this._onMouseWheel);functor(canvasElement,'contextmenu',this._onContextMenu);functor(canvasElement,'dblclick',this._onDoubleClick);functor(canvasElement,'dragover',this._onDragOver);functor(canvasElement,'dragenter',this._onDragEnter);functor(canvasElement,'dragleave',this._onDragLeave);functor(canvasElement,'drop',this._onDrop);if(!this.enablePointerEvents){functor(canvasElement,'touchstart',this._onTouchStart,addEventOptions);}if(typeof eventjs!=='undefined'&&eventjsFunctor in eventjs){eventjs[eventjsFunctor](canvasElement,'gesture',this._onGesture);eventjs[eventjsFunctor](canvasElement,'drag',this._onDrag);eventjs[eventjsFunctor](canvasElement,'orientation',this._onOrientationChange);eventjs[eventjsFunctor](canvasElement,'shake',this._onShake);eventjs[eventjsFunctor](canvasElement,'longpress',this._onLongPress);}},/**\n * Removes all event listeners\n */removeListeners:function removeListeners(){this.addOrRemove(removeListener,'remove');// if you dispose on a mouseDown, before mouse up, you need to clean document to...\nvar eventTypePrefix=this._getEventPrefix();removeListener(fabric.document,eventTypePrefix+'up',this._onMouseUp);removeListener(fabric.document,'touchend',this._onTouchEnd,addEventOptions);removeListener(fabric.document,eventTypePrefix+'move',this._onMouseMove,addEventOptions);removeListener(fabric.document,'touchmove',this._onMouseMove,addEventOptions);},/**\n * @private\n */_bindEvents:function _bindEvents(){if(this.eventsBound){// for any reason we pass here twice we do not want to bind events twice.\nreturn;}this._onMouseDown=this._onMouseDown.bind(this);this._onTouchStart=this._onTouchStart.bind(this);this._onMouseMove=this._onMouseMove.bind(this);this._onMouseUp=this._onMouseUp.bind(this);this._onTouchEnd=this._onTouchEnd.bind(this);this._onResize=this._onResize.bind(this);this._onGesture=this._onGesture.bind(this);this._onDrag=this._onDrag.bind(this);this._onShake=this._onShake.bind(this);this._onLongPress=this._onLongPress.bind(this);this._onOrientationChange=this._onOrientationChange.bind(this);this._onMouseWheel=this._onMouseWheel.bind(this);this._onMouseOut=this._onMouseOut.bind(this);this._onMouseEnter=this._onMouseEnter.bind(this);this._onContextMenu=this._onContextMenu.bind(this);this._onDoubleClick=this._onDoubleClick.bind(this);this._onDragOver=this._onDragOver.bind(this);this._onDragEnter=this._simpleEventHandler.bind(this,'dragenter');this._onDragLeave=this._simpleEventHandler.bind(this,'dragleave');this._onDrop=this._simpleEventHandler.bind(this,'drop');this.eventsBound=true;},/**\n * @private\n * @param {Event} [e] Event object fired on Event.js gesture\n * @param {Event} [self] Inner Event object\n */_onGesture:function _onGesture(e,self){this.__onTransformGesture&&this.__onTransformGesture(e,self);},/**\n * @private\n * @param {Event} [e] Event object fired on Event.js drag\n * @param {Event} [self] Inner Event object\n */_onDrag:function _onDrag(e,self){this.__onDrag&&this.__onDrag(e,self);},/**\n * @private\n * @param {Event} [e] Event object fired on wheel event\n */_onMouseWheel:function _onMouseWheel(e){this.__onMouseWheel(e);},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onMouseOut:function _onMouseOut(e){var target=this._hoveredTarget;this.fire('mouse:out',{target:target,e:e});this._hoveredTarget=null;target&&target.fire('mouseout',{e:e});var _this=this;this._hoveredTargets.forEach(function(_target){_this.fire('mouse:out',{target:target,e:e});_target&&target.fire('mouseout',{e:e});});this._hoveredTargets=[];if(this._iTextInstances){this._iTextInstances.forEach(function(obj){if(obj.isEditing){obj.hiddenTextarea.focus();}});}},/**\n * @private\n * @param {Event} e Event object fired on mouseenter\n */_onMouseEnter:function _onMouseEnter(e){// This find target and consequent 'mouse:over' is used to\n// clear old instances on hovered target.\n// calling findTarget has the side effect of killing target.__corner.\n// as a short term fix we are not firing this if we are currently transforming.\n// as a long term fix we need to separate the action of finding a target with the\n// side effects we added to it.\nif(!this.currentTransform&&!this.findTarget(e)){this.fire('mouse:over',{target:null,e:e});this._hoveredTarget=null;this._hoveredTargets=[];}},/**\n * @private\n * @param {Event} [e] Event object fired on Event.js orientation change\n * @param {Event} [self] Inner Event object\n */_onOrientationChange:function _onOrientationChange(e,self){this.__onOrientationChange&&this.__onOrientationChange(e,self);},/**\n * @private\n * @param {Event} [e] Event object fired on Event.js shake\n * @param {Event} [self] Inner Event object\n */_onShake:function _onShake(e,self){this.__onShake&&this.__onShake(e,self);},/**\n * @private\n * @param {Event} [e] Event object fired on Event.js shake\n * @param {Event} [self] Inner Event object\n */_onLongPress:function _onLongPress(e,self){this.__onLongPress&&this.__onLongPress(e,self);},/**\n * prevent default to allow drop event to be fired\n * @private\n * @param {Event} [e] Event object fired on Event.js shake\n */_onDragOver:function _onDragOver(e){e.preventDefault();var target=this._simpleEventHandler('dragover',e);this._fireEnterLeaveEvents(target,e);},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onContextMenu:function _onContextMenu(e){if(this.stopContextMenu){e.stopPropagation();e.preventDefault();}return false;},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onDoubleClick:function _onDoubleClick(e){this._cacheTransformEventData(e);this._handleEvent(e,'dblclick');this._resetTransformEventData(e);},/**\n * Return a the id of an event.\n * returns either the pointerId or the identifier or 0 for the mouse event\n * @private\n * @param {Event} evt Event object\n */getPointerId:function getPointerId(evt){var changedTouches=evt.changedTouches;if(changedTouches){return changedTouches[0]&&changedTouches[0].identifier;}if(this.enablePointerEvents){return evt.pointerId;}return-1;},/**\n * Determines if an event has the id of the event that is considered main\n * @private\n * @param {evt} event Event object\n */_isMainEvent:function _isMainEvent(evt){if(evt.isPrimary===true){return true;}if(evt.isPrimary===false){return false;}if(evt.type==='touchend'&&evt.touches.length===0){return true;}if(evt.changedTouches){return evt.changedTouches[0].identifier===this.mainTouchId;}return true;},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onTouchStart:function _onTouchStart(e){e.preventDefault();if(this.mainTouchId===null){this.mainTouchId=this.getPointerId(e);}this.__onMouseDown(e);this._resetTransformEventData();var canvasElement=this.upperCanvasEl,eventTypePrefix=this._getEventPrefix();addListener(fabric.document,'touchend',this._onTouchEnd,addEventOptions);addListener(fabric.document,'touchmove',this._onMouseMove,addEventOptions);// Unbind mousedown to prevent double triggers from touch devices\nremoveListener(canvasElement,eventTypePrefix+'down',this._onMouseDown);},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onMouseDown:function _onMouseDown(e){this.__onMouseDown(e);this._resetTransformEventData();var canvasElement=this.upperCanvasEl,eventTypePrefix=this._getEventPrefix();removeListener(canvasElement,eventTypePrefix+'move',this._onMouseMove,addEventOptions);addListener(fabric.document,eventTypePrefix+'up',this._onMouseUp);addListener(fabric.document,eventTypePrefix+'move',this._onMouseMove,addEventOptions);},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onTouchEnd:function _onTouchEnd(e){if(e.touches.length>0){// if there are still touches stop here\nreturn;}this.__onMouseUp(e);this._resetTransformEventData();this.mainTouchId=null;var eventTypePrefix=this._getEventPrefix();removeListener(fabric.document,'touchend',this._onTouchEnd,addEventOptions);removeListener(fabric.document,'touchmove',this._onMouseMove,addEventOptions);var _this=this;if(this._willAddMouseDown){clearTimeout(this._willAddMouseDown);}this._willAddMouseDown=setTimeout(function(){// Wait 400ms before rebinding mousedown to prevent double triggers\n// from touch devices\naddListener(_this.upperCanvasEl,eventTypePrefix+'down',_this._onMouseDown);_this._willAddMouseDown=0;},400);},/**\n * @private\n * @param {Event} e Event object fired on mouseup\n */_onMouseUp:function _onMouseUp(e){this.__onMouseUp(e);this._resetTransformEventData();var canvasElement=this.upperCanvasEl,eventTypePrefix=this._getEventPrefix();if(this._isMainEvent(e)){removeListener(fabric.document,eventTypePrefix+'up',this._onMouseUp);removeListener(fabric.document,eventTypePrefix+'move',this._onMouseMove,addEventOptions);addListener(canvasElement,eventTypePrefix+'move',this._onMouseMove,addEventOptions);}},/**\n * @private\n * @param {Event} e Event object fired on mousemove\n */_onMouseMove:function _onMouseMove(e){!this.allowTouchScrolling&&e.preventDefault&&e.preventDefault();this.__onMouseMove(e);},/**\n * @private\n */_onResize:function _onResize(){this.calcOffset();},/**\n * Decides whether the canvas should be redrawn in mouseup and mousedown events.\n * @private\n * @param {Object} target\n */_shouldRender:function _shouldRender(target){var activeObject=this._activeObject;if(!!activeObject!==!!target||activeObject&&target&&activeObject!==target){// this covers: switch of target, from target to no target, selection of target\n// multiSelection with key and mouse\nreturn true;}else if(activeObject&&activeObject.isEditing){// if we mouse up/down over a editing textbox a cursor change,\n// there is no need to re render\nreturn false;}return false;},/**\n * Method that defines the actions when mouse is released on canvas.\n * The method resets the currentTransform parameters, store the image corner\n * position in the image object and render the canvas on top.\n * @private\n * @param {Event} e Event object fired on mouseup\n */__onMouseUp:function __onMouseUp(e){var target,transform=this._currentTransform,groupSelector=this._groupSelector,shouldRender=false,isClick=!groupSelector||groupSelector.left===0&&groupSelector.top===0;this._cacheTransformEventData(e);target=this._target;this._handleEvent(e,'up:before');// if right/middle click just fire events and return\n// target undefined will make the _handleEvent search the target\nif(checkClick(e,RIGHT_CLICK)){if(this.fireRightClick){this._handleEvent(e,'up',RIGHT_CLICK,isClick);}return;}if(checkClick(e,MIDDLE_CLICK)){if(this.fireMiddleClick){this._handleEvent(e,'up',MIDDLE_CLICK,isClick);}this._resetTransformEventData();return;}if(this.isDrawingMode&&this._isCurrentlyDrawing){this._onMouseUpInDrawingMode(e);return;}if(!this._isMainEvent(e)){return;}if(transform){this._finalizeCurrentTransform(e);shouldRender=transform.actionPerformed;}if(!isClick){this._maybeGroupObjects(e);shouldRender||(shouldRender=this._shouldRender(target));}if(target){target.isMoving=false;}this._setCursorFromEvent(e,target);this._handleEvent(e,'up',LEFT_CLICK,isClick);this._groupSelector=null;this._currentTransform=null;// reset the target information about which corner is selected\ntarget&&(target.__corner=0);if(shouldRender){this.requestRenderAll();}else if(!isClick){this.renderTop();}},/**\n * @private\n * Handle event firing for target and subtargets\n * @param {Event} e event from mouse\n * @param {String} eventType event to fire (up, down or move)\n * @return {Fabric.Object} target return the the target found, for internal reasons.\n */_simpleEventHandler:function _simpleEventHandler(eventType,e){var target=this.findTarget(e),targets=this.targets,options={e:e,target:target,subTargets:targets};this.fire(eventType,options);target&&target.fire(eventType,options);if(!targets){return target;}for(var i=0;i<targets.length;i++){targets[i].fire(eventType,options);}return target;},/**\n * @private\n * Handle event firing for target and subtargets\n * @param {Event} e event from mouse\n * @param {String} eventType event to fire (up, down or move)\n * @param {fabric.Object} targetObj receiving event\n * @param {Number} [button] button used in the event 1 = left, 2 = middle, 3 = right\n * @param {Boolean} isClick for left button only, indicates that the mouse up happened without move.\n */_handleEvent:function _handleEvent(e,eventType,button,isClick){var target=this._target,targets=this.targets||[],options={e:e,target:target,subTargets:targets,button:button||LEFT_CLICK,isClick:isClick||false,pointer:this._pointer,absolutePointer:this._absolutePointer,transform:this._currentTransform};this.fire('mouse:'+eventType,options);target&&target.fire('mouse'+eventType,options);for(var i=0;i<targets.length;i++){targets[i].fire('mouse'+eventType,options);}},/**\n * @private\n * @param {Event} e send the mouse event that generate the finalize down, so it can be used in the event\n */_finalizeCurrentTransform:function _finalizeCurrentTransform(e){var transform=this._currentTransform,target=transform.target,eventName,options={e:e,target:target,transform:transform};if(target._scaling){target._scaling=false;}target.setCoords();if(transform.actionPerformed||this.stateful&&target.hasStateChanged()){if(transform.actionPerformed){eventName=this._addEventOptions(options,transform);this._fire(eventName,options);}this._fire('modified',options);}},/**\n * Mutate option object in order to add by property and give back the event name.\n * @private\n * @param {Object} options to mutate\n * @param {Object} transform to inspect action from\n */_addEventOptions:function _addEventOptions(options,transform){// we can probably add more details at low cost\n// scale change, rotation changes, translation changes\nvar eventName,by;switch(transform.action){case'scaleX':eventName='scaled';by='x';break;case'scaleY':eventName='scaled';by='y';break;case'skewX':eventName='skewed';by='x';break;case'skewY':eventName='skewed';by='y';break;case'scale':eventName='scaled';by='equally';break;case'rotate':eventName='rotated';break;case'drag':eventName='moved';break;}options.by=by;return eventName;},/**\n * @private\n * @param {Event} e Event object fired on mousedown\n */_onMouseDownInDrawingMode:function _onMouseDownInDrawingMode(e){this._isCurrentlyDrawing=true;if(this.getActiveObject()){this.discardActiveObject(e).requestRenderAll();}if(this.clipTo){fabric.util.clipContext(this,this.contextTop);}var pointer=this.getPointer(e);this.freeDrawingBrush.onMouseDown(pointer,{e:e,pointer:pointer});this._handleEvent(e,'down');},/**\n * @private\n * @param {Event} e Event object fired on mousemove\n */_onMouseMoveInDrawingMode:function _onMouseMoveInDrawingMode(e){if(this._isCurrentlyDrawing){var pointer=this.getPointer(e);this.freeDrawingBrush.onMouseMove(pointer,{e:e,pointer:pointer});}this.setCursor(this.freeDrawingCursor);this._handleEvent(e,'move');},/**\n * @private\n * @param {Event} e Event object fired on mouseup\n */_onMouseUpInDrawingMode:function _onMouseUpInDrawingMode(e){if(this.clipTo){this.contextTop.restore();}var pointer=this.getPointer(e);this._isCurrentlyDrawing=this.freeDrawingBrush.onMouseUp({e:e,pointer:pointer});this._handleEvent(e,'up');},/**\n * Method that defines the actions when mouse is clicked on canvas.\n * The method inits the currentTransform parameters and renders all the\n * canvas so the current image can be placed on the top canvas and the rest\n * in on the container one.\n * @private\n * @param {Event} e Event object fired on mousedown\n */__onMouseDown:function __onMouseDown(e){this._cacheTransformEventData(e);this._handleEvent(e,'down:before');var target=this._target;// if right click just fire events\nif(checkClick(e,RIGHT_CLICK)){if(this.fireRightClick){this._handleEvent(e,'down',RIGHT_CLICK);}return;}if(checkClick(e,MIDDLE_CLICK)){if(this.fireMiddleClick){this._handleEvent(e,'down',MIDDLE_CLICK);}return;}if(this.isDrawingMode){this._onMouseDownInDrawingMode(e);return;}if(!this._isMainEvent(e)){return;}// ignore if some object is being transformed at this moment\nif(this._currentTransform){return;}var pointer=this._pointer;// save pointer for check in __onMouseUp event\nthis._previousPointer=pointer;var shouldRender=this._shouldRender(target),shouldGroup=this._shouldGroup(e,target);if(this._shouldClearSelection(e,target)){this.discardActiveObject(e);}else if(shouldGroup){this._handleGrouping(e,target);target=this._activeObject;}if(this.selection&&(!target||!target.selectable&&!target.isEditing&&target!==this._activeObject)){this._groupSelector={ex:pointer.x,ey:pointer.y,top:0,left:0};}if(target){var alreadySelected=target===this._activeObject;if(target.selectable){this.setActiveObject(target,e);}if(target===this._activeObject&&(target.__corner||!shouldGroup)){this._setupCurrentTransform(e,target,alreadySelected);}}this._handleEvent(e,'down');// we must renderAll so that we update the visuals\n(shouldRender||shouldGroup)&&this.requestRenderAll();},/**\n * reset cache form common information needed during event processing\n * @private\n */_resetTransformEventData:function _resetTransformEventData(){this._target=null;this._pointer=null;this._absolutePointer=null;},/**\n * Cache common information needed during event processing\n * @private\n * @param {Event} e Event object fired on event\n */_cacheTransformEventData:function _cacheTransformEventData(e){// reset in order to avoid stale caching\nthis._resetTransformEventData();this._pointer=this.getPointer(e,true);this._absolutePointer=this.restorePointerVpt(this._pointer);this._target=this._currentTransform?this._currentTransform.target:this.findTarget(e)||null;},/**\n * @private\n */_beforeTransform:function _beforeTransform(e){var t=this._currentTransform;this.stateful&&t.target.saveState();this.fire('before:transform',{e:e,transform:t});// determine if it's a drag or rotate case\nif(t.corner){this.onBeforeScaleRotate(t.target);}},/**\n * Method that defines the actions when mouse is hovering the canvas.\n * The currentTransform parameter will define whether the user is rotating/scaling/translating\n * an image or neither of them (only hovering). A group selection is also possible and would cancel\n * all any other type of action.\n * In case of an image transformation only the top canvas will be rendered.\n * @private\n * @param {Event} e Event object fired on mousemove\n */__onMouseMove:function __onMouseMove(e){this._handleEvent(e,'move:before');this._cacheTransformEventData(e);var target,pointer;if(this.isDrawingMode){this._onMouseMoveInDrawingMode(e);return;}if(!this._isMainEvent(e)){return;}var groupSelector=this._groupSelector;// We initially clicked in an empty area, so we draw a box for multiple selection\nif(groupSelector){pointer=this._pointer;groupSelector.left=pointer.x-groupSelector.ex;groupSelector.top=pointer.y-groupSelector.ey;this.renderTop();}else if(!this._currentTransform){target=this.findTarget(e)||null;this._setCursorFromEvent(e,target);this._fireOverOutEvents(target,e);}else{this._transformObject(e);}this._handleEvent(e,'move');this._resetTransformEventData();},/**\n * Manage the mouseout, mouseover events for the fabric object on the canvas\n * @param {Fabric.Object} target the target where the target from the mousemove event\n * @param {Event} e Event object fired on mousemove\n * @private\n */_fireOverOutEvents:function _fireOverOutEvents(target,e){var _hoveredTarget=this._hoveredTarget,_hoveredTargets=this._hoveredTargets,targets=this.targets,length=Math.max(_hoveredTargets.length,targets.length);this.fireSyntheticInOutEvents(target,e,{oldTarget:_hoveredTarget,evtOut:'mouseout',canvasEvtOut:'mouse:out',evtIn:'mouseover',canvasEvtIn:'mouse:over'});for(var i=0;i<length;i++){this.fireSyntheticInOutEvents(targets[i],e,{oldTarget:_hoveredTargets[i],evtOut:'mouseout',evtIn:'mouseover'});}this._hoveredTarget=target;this._hoveredTargets=this.targets.concat();},/**\n * Manage the dragEnter, dragLeave events for the fabric objects on the canvas\n * @param {Fabric.Object} target the target where the target from the onDrag event\n * @param {Event} e Event object fired on ondrag\n * @private\n */_fireEnterLeaveEvents:function _fireEnterLeaveEvents(target,e){var _draggedoverTarget=this._draggedoverTarget,_hoveredTargets=this._hoveredTargets,targets=this.targets,length=Math.max(_hoveredTargets.length,targets.length);this.fireSyntheticInOutEvents(target,e,{oldTarget:_draggedoverTarget,evtOut:'dragleave',evtIn:'dragenter'});for(var i=0;i<length;i++){this.fireSyntheticInOutEvents(targets[i],e,{oldTarget:_hoveredTargets[i],evtOut:'dragleave',evtIn:'dragenter'});}this._draggedoverTarget=target;},/**\n * Manage the synthetic in/out events for the fabric objects on the canvas\n * @param {Fabric.Object} target the target where the target from the supported events\n * @param {Event} e Event object fired\n * @param {Object} config configuration for the function to work\n * @param {String} config.targetName property on the canvas where the old target is stored\n * @param {String} [config.canvasEvtOut] name of the event to fire at canvas level for out\n * @param {String} config.evtOut name of the event to fire for out\n * @param {String} [config.canvasEvtIn] name of the event to fire at canvas level for in\n * @param {String} config.evtIn name of the event to fire for in\n * @private\n */fireSyntheticInOutEvents:function fireSyntheticInOutEvents(target,e,config){var inOpt,outOpt,oldTarget=config.oldTarget,outFires,inFires,targetChanged=oldTarget!==target,canvasEvtIn=config.canvasEvtIn,canvasEvtOut=config.canvasEvtOut;if(targetChanged){inOpt={e:e,target:target,previousTarget:oldTarget};outOpt={e:e,target:oldTarget,nextTarget:target};}inFires=target&&targetChanged;outFires=oldTarget&&targetChanged;if(outFires){canvasEvtOut&&this.fire(canvasEvtOut,outOpt);oldTarget.fire(config.evtOut,outOpt);}if(inFires){canvasEvtIn&&this.fire(canvasEvtIn,inOpt);target.fire(config.evtIn,inOpt);}},/**\n * Method that defines actions when an Event Mouse Wheel\n * @param {Event} e Event object fired on mouseup\n */__onMouseWheel:function __onMouseWheel(e){this._cacheTransformEventData(e);this._handleEvent(e,'wheel');this._resetTransformEventData();},/**\n * @private\n * @param {Event} e Event fired on mousemove\n */_transformObject:function _transformObject(e){var pointer=this.getPointer(e),transform=this._currentTransform;transform.reset=false;transform.target.isMoving=true;transform.shiftKey=e.shiftKey;transform.altKey=e[this.centeredKey];this._beforeScaleTransform(e,transform);this._performTransformAction(e,transform,pointer);transform.actionPerformed&&this.requestRenderAll();},/**\n * @private\n */_performTransformAction:function _performTransformAction(e,transform,pointer){var x=pointer.x,y=pointer.y,action=transform.action,actionPerformed=false,options={target:transform.target,e:e,transform:transform,pointer:pointer};if(action==='rotate'){(actionPerformed=this._rotateObject(x,y))&&this._fire('rotating',options);}else if(action==='scale'){(actionPerformed=this._onScale(e,transform,x,y))&&this._fire('scaling',options);}else if(action==='scaleX'){(actionPerformed=this._scaleObject(x,y,'x'))&&this._fire('scaling',options);}else if(action==='scaleY'){(actionPerformed=this._scaleObject(x,y,'y'))&&this._fire('scaling',options);}else if(action==='skewX'){(actionPerformed=this._skewObject(x,y,'x'))&&this._fire('skewing',options);}else if(action==='skewY'){(actionPerformed=this._skewObject(x,y,'y'))&&this._fire('skewing',options);}else{actionPerformed=this._translateObject(x,y);if(actionPerformed){this._fire('moving',options);this.setCursor(options.target.moveCursor||this.moveCursor);}}transform.actionPerformed=transform.actionPerformed||actionPerformed;},/**\n * @private\n */_fire:function _fire(eventName,options){this.fire('object:'+eventName,options);options.target.fire(eventName,options);},/**\n * @private\n */_beforeScaleTransform:function _beforeScaleTransform(e,transform){if(transform.action==='scale'||transform.action==='scaleX'||transform.action==='scaleY'){var centerTransform=this._shouldCenterTransform(transform.target);// Switch from a normal resize to center-based\nif(centerTransform&&(transform.originX!=='center'||transform.originY!=='center')||// Switch from center-based resize to normal one\n!centerTransform&&transform.originX==='center'&&transform.originY==='center'){this._resetCurrentTransform();transform.reset=true;}}},/**\n * @private\n * @param {Event} e Event object\n * @param {Object} transform current transform\n * @param {Number} x mouse position x from origin\n * @param {Number} y mouse position y from origin\n * @return {Boolean} true if the scaling occurred\n */_onScale:function _onScale(e,transform,x,y){if(this._isUniscalePossible(e,transform.target)){transform.currentAction='scale';return this._scaleObject(x,y);}else{// Switch from a normal resize to proportional\nif(!transform.reset&&transform.currentAction==='scale'){this._resetCurrentTransform();}transform.currentAction='scaleEqually';return this._scaleObject(x,y,'equally');}},/**\n * @private\n * @param {Event} e Event object\n * @param {fabric.Object} target current target\n * @return {Boolean} true if unproportional scaling is possible\n */_isUniscalePossible:function _isUniscalePossible(e,target){return(e[this.uniScaleKey]||this.uniScaleTransform)&&!target.get('lockUniScaling');},/**\n * Sets the cursor depending on where the canvas is being hovered.\n * Note: very buggy in Opera\n * @param {Event} e Event object\n * @param {Object} target Object that the mouse is hovering, if so.\n */_setCursorFromEvent:function _setCursorFromEvent(e,target){if(!target){this.setCursor(this.defaultCursor);return false;}var hoverCursor=target.hoverCursor||this.hoverCursor,activeSelection=this._activeObject&&this._activeObject.type==='activeSelection'?this._activeObject:null,// only show proper corner when group selection is not active\ncorner=(!activeSelection||!activeSelection.contains(target))&&target._findTargetCorner(this.getPointer(e,true));if(!corner){if(target.subTargetCheck){// hoverCursor should come from top-most subTarget,\n// so we walk the array backwards\nthis.targets.concat().reverse().map(function(_target){hoverCursor=_target.hoverCursor||hoverCursor;});}this.setCursor(hoverCursor);}else{this.setCursor(this.getCornerCursor(corner,target,e));}},/**\n * @private\n */getCornerCursor:function getCornerCursor(corner,target,e){if(this.actionIsDisabled(corner,target,e)){return this.notAllowedCursor;}else if(corner in cursorOffset){return this._getRotatedCornerCursor(corner,target,e);}else if(corner==='mtr'&&target.hasRotatingPoint){return this.rotationCursor;}else{return this.defaultCursor;}},actionIsDisabled:function actionIsDisabled(corner,target,e){if(corner==='mt'||corner==='mb'){return e[this.altActionKey]?target.lockSkewingX:target.lockScalingY;}else if(corner==='ml'||corner==='mr'){return e[this.altActionKey]?target.lockSkewingY:target.lockScalingX;}else if(corner==='mtr'){return target.lockRotation;}else{return this._isUniscalePossible(e,target)?target.lockScalingX&&target.lockScalingY:target.lockScalingX||target.lockScalingY;}},/**\n * @private\n */_getRotatedCornerCursor:function _getRotatedCornerCursor(corner,target,e){var n=Math.round(target.angle%360/45);if(n<0){n+=8;// full circle ahead\n}n+=cursorOffset[corner];if(e[this.altActionKey]&&cursorOffset[corner]%2===0){//if we are holding shift and we are on a mx corner...\nn+=2;}// normalize n to be from 0 to 7\nn%=8;return this.cursorMap[n];}});})();(function(){var min=Math.min,max=Math.max;fabric.util.object.extend(fabric.Canvas.prototype,/** @lends fabric.Canvas.prototype */{/**\n * @private\n * @param {Event} e Event object\n * @param {fabric.Object} target\n * @return {Boolean}\n */_shouldGroup:function _shouldGroup(e,target){var activeObject=this._activeObject;return activeObject&&this._isSelectionKeyPressed(e)&&target&&target.selectable&&this.selection&&(activeObject!==target||activeObject.type==='activeSelection')&&!target.onSelect({e:e});},/**\n * @private\n * @param {Event} e Event object\n * @param {fabric.Object} target\n */_handleGrouping:function _handleGrouping(e,target){var activeObject=this._activeObject;// avoid multi select when shift click on a corner\nif(activeObject.__corner){return;}if(target===activeObject){// if it's a group, find target again, using activeGroup objects\ntarget=this.findTarget(e,true);// if even object is not found or we are on activeObjectCorner, bail out\nif(!target||!target.selectable){return;}}if(activeObject&&activeObject.type==='activeSelection'){this._updateActiveSelection(target,e);}else{this._createActiveSelection(target,e);}},/**\n * @private\n */_updateActiveSelection:function _updateActiveSelection(target,e){var activeSelection=this._activeObject,currentActiveObjects=activeSelection._objects.slice(0);if(activeSelection.contains(target)){activeSelection.removeWithUpdate(target);this._hoveredTarget=target;this._hoveredTargets=this.targets.concat();if(activeSelection.size()===1){// activate last remaining object\nthis._setActiveObject(activeSelection.item(0),e);}}else{activeSelection.addWithUpdate(target);this._hoveredTarget=activeSelection;this._hoveredTargets=this.targets.concat();}this._fireSelectionEvents(currentActiveObjects,e);},/**\n * @private\n */_createActiveSelection:function _createActiveSelection(target,e){var currentActives=this.getActiveObjects(),group=this._createGroup(target);this._hoveredTarget=group;// ISSUE 4115: should we consider subTargets here?\n// this._hoveredTargets = [];\n// this._hoveredTargets = this.targets.concat();\nthis._setActiveObject(group,e);this._fireSelectionEvents(currentActives,e);},/**\n * @private\n * @param {Object} target\n */_createGroup:function _createGroup(target){var objects=this._objects,isActiveLower=objects.indexOf(this._activeObject)<objects.indexOf(target),groupObjects=isActiveLower?[this._activeObject,target]:[target,this._activeObject];this._activeObject.isEditing&&this._activeObject.exitEditing();return new fabric.ActiveSelection(groupObjects,{canvas:this});},/**\n * @private\n * @param {Event} e mouse event\n */_groupSelectedObjects:function _groupSelectedObjects(e){var group=this._collectObjects(e),aGroup;// do not create group for 1 element only\nif(group.length===1){this.setActiveObject(group[0],e);}else if(group.length>1){aGroup=new fabric.ActiveSelection(group.reverse(),{canvas:this});this.setActiveObject(aGroup,e);}},/**\n * @private\n */_collectObjects:function _collectObjects(e){var group=[],currentObject,x1=this._groupSelector.ex,y1=this._groupSelector.ey,x2=x1+this._groupSelector.left,y2=y1+this._groupSelector.top,selectionX1Y1=new fabric.Point(min(x1,x2),min(y1,y2)),selectionX2Y2=new fabric.Point(max(x1,x2),max(y1,y2)),allowIntersect=!this.selectionFullyContained,isClick=x1===x2&&y1===y2;// we iterate reverse order to collect top first in case of click.\nfor(var i=this._objects.length;i--;){currentObject=this._objects[i];if(!currentObject||!currentObject.selectable||!currentObject.visible){continue;}if(allowIntersect&¤tObject.intersectsWithRect(selectionX1Y1,selectionX2Y2)||currentObject.isContainedWithinRect(selectionX1Y1,selectionX2Y2)||allowIntersect&¤tObject.containsPoint(selectionX1Y1)||allowIntersect&¤tObject.containsPoint(selectionX2Y2)){group.push(currentObject);// only add one object if it's a click\nif(isClick){break;}}}if(group.length>1){group=group.filter(function(object){return!object.onSelect({e:e});});}return group;},/**\n * @private\n */_maybeGroupObjects:function _maybeGroupObjects(e){if(this.selection&&this._groupSelector){this._groupSelectedObjects(e);}this.setCursor(this.defaultCursor);// clear selection and current transformation\nthis._groupSelector=null;}});})();(function(){fabric.util.object.extend(fabric.StaticCanvas.prototype,/** @lends fabric.StaticCanvas.prototype */{/**\n * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately\n * @param {Object} [options] Options object\n * @param {String} [options.format=png] The format of the output image. Either \"jpeg\" or \"png\"\n * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.\n * @param {Number} [options.multiplier=1] Multiplier to scale by, to have consistent\n * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14\n * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14\n * @param {Number} [options.width] Cropping width. Introduced in v1.2.14\n * @param {Number} [options.height] Cropping height. Introduced in v1.2.14\n * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 2.0.0\n * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format\n * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}\n * @example <caption>Generate jpeg dataURL with lower quality</caption>\n * var dataURL = canvas.toDataURL({\n * format: 'jpeg',\n * quality: 0.8\n * });\n * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>\n * var dataURL = canvas.toDataURL({\n * format: 'png',\n * left: 100,\n * top: 100,\n * width: 200,\n * height: 200\n * });\n * @example <caption>Generate double scaled png dataURL</caption>\n * var dataURL = canvas.toDataURL({\n * format: 'png',\n * multiplier: 2\n * });\n */toDataURL:function toDataURL(options){options||(options={});var format=options.format||'png',quality=options.quality||1,multiplier=(options.multiplier||1)*(options.enableRetinaScaling?this.getRetinaScaling():1),canvasEl=this.toCanvasElement(multiplier,options);return fabric.util.toDataURL(canvasEl,format,quality);},/**\n * Create a new HTMLCanvas element painted with the current canvas content.\n * No need to resize the actual one or repaint it.\n * Will transfer object ownership to a new canvas, paint it, and set everything back.\n * This is an intermediary step used to get to a dataUrl but also it is useful to\n * create quick image copies of a canvas without passing for the dataUrl string\n * @param {Number} [multiplier] a zoom factor.\n * @param {Object} [cropping] Cropping informations\n * @param {Number} [cropping.left] Cropping left offset.\n * @param {Number} [cropping.top] Cropping top offset.\n * @param {Number} [cropping.width] Cropping width.\n * @param {Number} [cropping.height] Cropping height.\n */toCanvasElement:function toCanvasElement(multiplier,cropping){multiplier=multiplier||1;cropping=cropping||{};var scaledWidth=(cropping.width||this.width)*multiplier,scaledHeight=(cropping.height||this.height)*multiplier,zoom=this.getZoom(),originalWidth=this.width,originalHeight=this.height,newZoom=zoom*multiplier,vp=this.viewportTransform,translateX=(vp[4]-(cropping.left||0))*multiplier,translateY=(vp[5]-(cropping.top||0))*multiplier,originalInteractive=this.interactive,newVp=[newZoom,0,0,newZoom,translateX,translateY],originalRetina=this.enableRetinaScaling,canvasEl=fabric.util.createCanvasElement(),originalContextTop=this.contextTop;canvasEl.width=scaledWidth;canvasEl.height=scaledHeight;this.contextTop=null;this.enableRetinaScaling=false;this.interactive=false;this.viewportTransform=newVp;this.width=scaledWidth;this.height=scaledHeight;this.calcViewportBoundaries();this.renderCanvas(canvasEl.getContext('2d'),this._objects);this.viewportTransform=vp;this.width=originalWidth;this.height=originalHeight;this.calcViewportBoundaries();this.interactive=originalInteractive;this.enableRetinaScaling=originalRetina;this.contextTop=originalContextTop;return canvasEl;}});})();fabric.util.object.extend(fabric.StaticCanvas.prototype,/** @lends fabric.StaticCanvas.prototype */{/**\n * Populates canvas with data from the specified dataless JSON.\n * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON}\n * @deprecated since 1.2.2\n * @param {String|Object} json JSON string or object\n * @param {Function} callback Callback, invoked when json is parsed\n * and corresponding objects (e.g: {@link fabric.Image})\n * are initialized\n * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.\n * @return {fabric.Canvas} instance\n * @chainable\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#deserialization}\n */loadFromDatalessJSON:function loadFromDatalessJSON(json,callback,reviver){return this.loadFromJSON(json,callback,reviver);},/**\n * Populates canvas with data from the specified JSON.\n * JSON format must conform to the one of {@link fabric.Canvas#toJSON}\n * @param {String|Object} json JSON string or object\n * @param {Function} callback Callback, invoked when json is parsed\n * and corresponding objects (e.g: {@link fabric.Image})\n * are initialized\n * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.\n * @return {fabric.Canvas} instance\n * @chainable\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#deserialization}\n * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo}\n * @example <caption>loadFromJSON</caption>\n * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));\n * @example <caption>loadFromJSON with reviver</caption>\n * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) {\n * // `o` = json object\n * // `object` = fabric.Object instance\n * // ... do some stuff ...\n * });\n */loadFromJSON:function loadFromJSON(json,callback,reviver){if(!json){return;}// serialize if it wasn't already\nvar serialized=typeof json==='string'?JSON.parse(json):fabric.util.object.clone(json);var _this=this,clipPath=serialized.clipPath,renderOnAddRemove=this.renderOnAddRemove;this.renderOnAddRemove=false;delete serialized.clipPath;this._enlivenObjects(serialized.objects,function(enlivenedObjects){_this.clear();_this._setBgOverlay(serialized,function(){if(clipPath){_this._enlivenObjects([clipPath],function(enlivenedCanvasClip){_this.clipPath=enlivenedCanvasClip[0];_this.__setupCanvas.call(_this,serialized,enlivenedObjects,renderOnAddRemove,callback);});}else{_this.__setupCanvas.call(_this,serialized,enlivenedObjects,renderOnAddRemove,callback);}});},reviver);return this;},/**\n * @private\n * @param {Object} serialized Object with background and overlay information\n * @param {Array} restored canvas objects\n * @param {Function} cached renderOnAddRemove callback\n * @param {Function} callback Invoked after all background and overlay images/patterns loaded\n */__setupCanvas:function __setupCanvas(serialized,enlivenedObjects,renderOnAddRemove,callback){var _this=this;enlivenedObjects.forEach(function(obj,index){// we splice the array just in case some custom classes restored from JSON\n// will add more object to canvas at canvas init.\n_this.insertAt(obj,index);});this.renderOnAddRemove=renderOnAddRemove;// remove parts i cannot set as options\ndelete serialized.objects;delete serialized.backgroundImage;delete serialized.overlayImage;delete serialized.background;delete serialized.overlay;// this._initOptions does too many things to just\n// call it. Normally loading an Object from JSON\n// create the Object instance. Here the Canvas is\n// already an instance and we are just loading things over it\nthis._setOptions(serialized);this.renderAll();callback&&callback();},/**\n * @private\n * @param {Object} serialized Object with background and overlay information\n * @param {Function} callback Invoked after all background and overlay images/patterns loaded\n */_setBgOverlay:function _setBgOverlay(serialized,callback){var loaded={backgroundColor:false,overlayColor:false,backgroundImage:false,overlayImage:false};if(!serialized.backgroundImage&&!serialized.overlayImage&&!serialized.background&&!serialized.overlay){callback&&callback();return;}var cbIfLoaded=function cbIfLoaded(){if(loaded.backgroundImage&&loaded.overlayImage&&loaded.backgroundColor&&loaded.overlayColor){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);},/**\n * @private\n * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor)\n * @param {(Object|String)} value Value to set\n * @param {Object} loaded Set loaded property to true if property is set\n * @param {Object} callback Callback function to invoke after property is set\n */__setBgOverlay:function __setBgOverlay(property,value,loaded,callback){var _this=this;if(!value){loaded[property]=true;callback&&callback();return;}if(property==='backgroundImage'||property==='overlayImage'){fabric.util.enlivenObjects([value],function(enlivedObject){_this[property]=enlivedObject[0];loaded[property]=true;callback&&callback();});}else{this['set'+fabric.util.string.capitalize(property,true)](value,function(){loaded[property]=true;callback&&callback();});}},/**\n * @private\n * @param {Array} objects\n * @param {Function} callback\n * @param {Function} [reviver]\n */_enlivenObjects:function _enlivenObjects(objects,callback,reviver){if(!objects||objects.length===0){callback&&callback([]);return;}fabric.util.enlivenObjects(objects,function(enlivenedObjects){callback&&callback(enlivenedObjects);},null,reviver);},/**\n * @private\n * @param {String} format\n * @param {Function} callback\n */_toDataURL:function _toDataURL(format,callback){this.clone(function(clone){callback(clone.toDataURL(format));});},/**\n * @private\n * @param {String} format\n * @param {Number} multiplier\n * @param {Function} callback\n */_toDataURLWithMultiplier:function _toDataURLWithMultiplier(format,multiplier,callback){this.clone(function(clone){callback(clone.toDataURLWithMultiplier(format,multiplier));});},/**\n * Clones canvas instance\n * @param {Object} [callback] Receives cloned instance as a first argument\n * @param {Array} [properties] Array of properties to include in the cloned canvas and children\n */clone:function clone(callback,properties){var data=JSON.stringify(this.toJSON(properties));this.cloneWithoutData(function(clone){clone.loadFromJSON(data,function(){callback&&callback(clone);});});},/**\n * Clones canvas instance without cloning existing data.\n * This essentially copies canvas dimensions, clipping properties, etc.\n * but leaves data empty (so that you can populate it with your own)\n * @param {Object} [callback] Receives cloned instance as a first argument\n */cloneWithoutData:function cloneWithoutData(callback){var el=fabric.util.createCanvasElement();el.width=this.width;el.height=this.height;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,clone=fabric.util.object.clone,toFixed=fabric.util.toFixed,capitalize=fabric.util.string.capitalize,degreesToRadians=fabric.util.degreesToRadians,supportsLineDash=fabric.StaticCanvas.supports('setLineDash'),objectCaching=!fabric.isLikelyNode,ALIASING_LIMIT=2;if(fabric.Object){return;}/**\n * Root object class from which all 2d shape classes inherit from\n * @class fabric.Object\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#objects}\n * @see {@link fabric.Object#initialize} for constructor definition\n *\n * @fires added\n * @fires removed\n *\n * @fires selected\n * @fires deselected\n * @fires modified\n * @fires modified\n * @fires moved\n * @fires scaled\n * @fires rotated\n * @fires skewed\n *\n * @fires rotating\n * @fires scaling\n * @fires moving\n * @fires skewing\n *\n * @fires mousedown\n * @fires mouseup\n * @fires mouseover\n * @fires mouseout\n * @fires mousewheel\n * @fires mousedblclick\n *\n * @fires dragover\n * @fires dragenter\n * @fires dragleave\n * @fires drop\n */fabric.Object=fabric.util.createClass(fabric.CommonMethods,/** @lends fabric.Object.prototype */{/**\n * Type of an object (rect, circle, path, etc.).\n * Note that this property is meant to be read-only and not meant to be modified.\n * If you modify, certain parts of Fabric (such as JSON loading) won't work correctly.\n * @type String\n * @default\n */type:'object',/**\n * Horizontal origin of transformation of an object (one of \"left\", \"right\", \"center\")\n * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups\n * @type String\n * @default\n */originX:'left',/**\n * Vertical origin of transformation of an object (one of \"top\", \"bottom\", \"center\")\n * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups\n * @type String\n * @default\n */originY:'top',/**\n * Top position of an object. Note that by default it's relative to object top. You can change this by setting originY={top/center/bottom}\n * @type Number\n * @default\n */top:0,/**\n * Left position of an object. Note that by default it's relative to object left. You can change this by setting originX={left/center/right}\n * @type Number\n * @default\n */left:0,/**\n * Object width\n * @type Number\n * @default\n */width:0,/**\n * Object height\n * @type Number\n * @default\n */height:0,/**\n * Object scale factor (horizontal)\n * @type Number\n * @default\n */scaleX:1,/**\n * Object scale factor (vertical)\n * @type Number\n * @default\n */scaleY:1,/**\n * When true, an object is rendered as flipped horizontally\n * @type Boolean\n * @default\n */flipX:false,/**\n * When true, an object is rendered as flipped vertically\n * @type Boolean\n * @default\n */flipY:false,/**\n * Opacity of an object\n * @type Number\n * @default\n */opacity:1,/**\n * Angle of rotation of an object (in degrees)\n * @type Number\n * @default\n */angle:0,/**\n * Angle of skew on x axes of an object (in degrees)\n * @type Number\n * @default\n */skewX:0,/**\n * Angle of skew on y axes of an object (in degrees)\n * @type Number\n * @default\n */skewY:0,/**\n * Size of object's controlling corners (in pixels)\n * @type Number\n * @default\n */cornerSize:13,/**\n * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)\n * @type Boolean\n * @default\n */transparentCorners:true,/**\n * Default cursor value used when hovering over this object on canvas\n * @type String\n * @default\n */hoverCursor:null,/**\n * Default cursor value used when moving this object on canvas\n * @type String\n * @default\n */moveCursor:null,/**\n * Padding between object and its controlling borders (in pixels)\n * @type Number\n * @default\n */padding:0,/**\n * Color of controlling borders of an object (when it's active)\n * @type String\n * @default\n */borderColor:'rgba(102,153,255,0.75)',/**\n * Array specifying dash pattern of an object's borders (hasBorder must be true)\n * @since 1.6.2\n * @type Array\n */borderDashArray:null,/**\n * Color of controlling corners of an object (when it's active)\n * @type String\n * @default\n */cornerColor:'rgba(102,153,255,0.5)',/**\n * Color of controlling corners of an object (when it's active and transparentCorners false)\n * @since 1.6.2\n * @type String\n * @default\n */cornerStrokeColor:null,/**\n * Specify style of control, 'rect' or 'circle'\n * @since 1.6.2\n * @type String\n */cornerStyle:'rect',/**\n * Array specifying dash pattern of an object's control (hasBorder must be true)\n * @since 1.6.2\n * @type Array\n */cornerDashArray:null,/**\n * When true, this object will use center point as the origin of transformation\n * when being scaled via the controls.\n * <b>Backwards incompatibility note:</b> This property replaces \"centerTransform\" (Boolean).\n * @since 1.3.4\n * @type Boolean\n * @default\n */centeredScaling:false,/**\n * When true, this object will use center point as the origin of transformation\n * when being rotated via the controls.\n * <b>Backwards incompatibility note:</b> This property replaces \"centerTransform\" (Boolean).\n * @since 1.3.4\n * @type Boolean\n * @default\n */centeredRotation:true,/**\n * Color of object's fill\n * takes css colors https://www.w3.org/TR/css-color-3/\n * @type String\n * @default\n */fill:'rgb(0,0,0)',/**\n * Fill rule used to fill an object\n * accepted values are nonzero, evenodd\n * <b>Backwards incompatibility note:</b> This property was used for setting globalCompositeOperation until v1.4.12 (use `fabric.Object#globalCompositeOperation` instead)\n * @type String\n * @default\n */fillRule:'nonzero',/**\n * Composite rule used for canvas globalCompositeOperation\n * @type String\n * @default\n */globalCompositeOperation:'source-over',/**\n * Background color of an object.\n * takes css colors https://www.w3.org/TR/css-color-3/\n * @type String\n * @default\n */backgroundColor:'',/**\n * Selection Background color of an object. colored layer behind the object when it is active.\n * does not mix good with globalCompositeOperation methods.\n * @type String\n * @default\n */selectionBackgroundColor:'',/**\n * When defined, an object is rendered via stroke and this property specifies its color\n * takes css colors https://www.w3.org/TR/css-color-3/\n * @type String\n * @default\n */stroke:null,/**\n * Width of a stroke used to render this object\n * @type Number\n * @default\n */strokeWidth:1,/**\n * Array specifying dash pattern of an object's stroke (stroke must be defined)\n * @type Array\n */strokeDashArray:null,/**\n * Line offset of an object's stroke\n * @type Number\n * @default\n */strokeDashOffset:0,/**\n * Line endings style of an object's stroke (one of \"butt\", \"round\", \"square\")\n * @type String\n * @default\n */strokeLineCap:'butt',/**\n * Corner style of an object's stroke (one of \"bevil\", \"round\", \"miter\")\n * @type String\n * @default\n */strokeLineJoin:'miter',/**\n * Maximum miter length (used for strokeLineJoin = \"miter\") of an object's stroke\n * @type Number\n * @default\n */strokeMiterLimit:4,/**\n * Shadow object representing shadow of this shape\n * @type fabric.Shadow\n * @default\n */shadow:null,/**\n * Opacity of object's controlling borders when object is active and moving\n * @type Number\n * @default\n */borderOpacityWhenMoving:0.4,/**\n * Scale factor of object's controlling borders\n * @type Number\n * @default\n */borderScaleFactor:1,/**\n * Transform matrix (similar to SVG's transform matrix)\n * This property has been depreacted. Since caching and and qrDecompose this\n * property can be handled with the standard top,left,scaleX,scaleY,angle and skewX.\n * A documentation example on how to parse and merge a transformMatrix will be provided before\n * completely removing it in fabric 4.0\n * If you are starting a project now, DO NOT use it.\n * @deprecated since 3.2.0\n * @type Array\n */transformMatrix:null,/**\n * Minimum allowed scale value of an object\n * @type Number\n * @default\n */minScaleLimit:0,/**\n * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).\n * But events still fire on it.\n * @type Boolean\n * @default\n */selectable:true,/**\n * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4\n * @type Boolean\n * @default\n */evented:true,/**\n * When set to `false`, an object is not rendered on canvas\n * @type Boolean\n * @default\n */visible:true,/**\n * When set to `false`, object's controls are not displayed and can not be used to manipulate object\n * @type Boolean\n * @default\n */hasControls:true,/**\n * When set to `false`, object's controlling borders are not rendered\n * @type Boolean\n * @default\n */hasBorders:true,/**\n * When set to `false`, object's controlling rotating point will not be visible or selectable\n * @type Boolean\n * @default\n */hasRotatingPoint:true,/**\n * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)\n * @type Number\n * @default\n */rotatingPointOffset:40,/**\n * When set to `true`, objects are \"found\" on canvas on per-pixel basis rather than according to bounding box\n * @type Boolean\n * @default\n */perPixelTargetFind:false,/**\n * When `false`, default object's values are not included in its serialization\n * @type Boolean\n * @default\n */includeDefaultValues:true,/**\n * Function that determines clipping of an object (context is passed as a first argument).\n * If you are using code minification, ctx argument can be minified/manglied you should use\n * as a workaround `var ctx = arguments[0];` in the function;\n * Note that context origin is at the object's center point (not left/top corner)\n * @deprecated since 2.0.0\n * @type Function\n */clipTo:null,/**\n * When `true`, object horizontal movement is locked\n * @type Boolean\n * @default\n */lockMovementX:false,/**\n * When `true`, object vertical movement is locked\n * @type Boolean\n * @default\n */lockMovementY:false,/**\n * When `true`, object rotation is locked\n * @type Boolean\n * @default\n */lockRotation:false,/**\n * When `true`, object horizontal scaling is locked\n * @type Boolean\n * @default\n */lockScalingX:false,/**\n * When `true`, object vertical scaling is locked\n * @type Boolean\n * @default\n */lockScalingY:false,/**\n * When `true`, object non-uniform scaling is locked\n * @type Boolean\n * @default\n */lockUniScaling:false,/**\n * When `true`, object horizontal skewing is locked\n * @type Boolean\n * @default\n */lockSkewingX:false,/**\n * When `true`, object vertical skewing is locked\n * @type Boolean\n * @default\n */lockSkewingY:false,/**\n * When `true`, object cannot be flipped by scaling into negative values\n * @type Boolean\n * @default\n */lockScalingFlip:false,/**\n * When `true`, object is not exported in OBJECT/JSON\n * @since 1.6.3\n * @type Boolean\n * @default\n */excludeFromExport:false,/**\n * When `true`, object is cached on an additional canvas.\n * When `false`, object is not cached unless necessary ( clipPath )\n * default to true\n * @since 1.7.0\n * @type Boolean\n * @default true\n */objectCaching:objectCaching,/**\n * When `true`, object properties are checked for cache invalidation. In some particular\n * situation you may want this to be disabled ( spray brush, very big, groups)\n * or if your application does not allow you to modify properties for groups child you want\n * to disable it for groups.\n * default to false\n * since 1.7.0\n * @type Boolean\n * @default false\n */statefullCache:false,/**\n * When `true`, cache does not get updated during scaling. The picture will get blocky if scaled\n * too much and will be redrawn with correct details at the end of scaling.\n * this setting is performance and application dependant.\n * default to true\n * since 1.7.0\n * @type Boolean\n * @default true\n */noScaleCache:true,/**\n * When `false`, the stoke width will scale with the object.\n * When `true`, the stroke will always match the exact pixel size entered for stroke width.\n * default to false\n * @since 2.6.0\n * @type Boolean\n * @default false\n * @type Boolean\n * @default false\n */strokeUniform:false,/**\n * When set to `true`, object's cache will be rerendered next render call.\n * since 1.7.0\n * @type Boolean\n * @default true\n */dirty:true,/**\n * keeps the value of the last hovered corner during mouse move.\n * 0 is no corner, or 'mt', 'ml', 'mtr' etc..\n * It should be private, but there is no harm in using it as\n * a read-only property.\n * @type number|string|any\n * @default 0\n */__corner:0,/**\n * Determines if the fill or the stroke is drawn first (one of \"fill\" or \"stroke\")\n * @type String\n * @default\n */paintFirst:'fill',/**\n * List of properties to consider when checking if state\n * of an object is changed (fabric.Object#hasStateChanged)\n * as well as for history (undo/redo) purposes\n * @type Array\n */stateProperties:('top left width height scaleX scaleY flipX flipY originX originY transformMatrix '+'stroke strokeWidth strokeDashArray strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit '+'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor '+'skewX skewY fillRule paintFirst clipPath strokeUniform').split(' '),/**\n * List of properties to consider when checking if cache needs refresh\n * Those properties are checked by statefullCache ON ( or lazy mode if we want ) or from single\n * calls to Object.set(key, value). If the key is in this list, the object is marked as dirty\n * and refreshed at the next render\n * @type Array\n */cacheProperties:('fill stroke strokeWidth strokeDashArray width height paintFirst strokeUniform'+' strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath').split(' '),/**\n * a fabricObject that, without stroke define a clipping area with their shape. filled in black\n * the clipPath object gets used when the object has rendered, and the context is placed in the center\n * of the object cacheCanvas.\n * If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center'\n * @type fabric.Object\n */clipPath:undefined,/**\n * Meaningful ONLY when the object is used as clipPath.\n * if true, the clipPath will make the object clip to the outside of the clipPath\n * since 2.4.0\n * @type boolean\n * @default false\n */inverted:false,/**\n * Meaningful ONLY when the object is used as clipPath.\n * if true, the clipPath will have its top and left relative to canvas, and will\n * not be influenced by the object transform. This will make the clipPath relative\n * to the canvas, but clipping just a particular object.\n * WARNING this is beta, this feature may change or be renamed.\n * since 2.4.0\n * @type boolean\n * @default false\n */absolutePositioned:false,/**\n * Constructor\n * @param {Object} [options] Options object\n */initialize:function initialize(options){if(options){this.setOptions(options);}},/**\n * Create a the canvas used to keep the cached copy of the object\n * @private\n */_createCacheCanvas:function _createCacheCanvas(){this._cacheProperties={};this._cacheCanvas=fabric.util.createCanvasElement();this._cacheContext=this._cacheCanvas.getContext('2d');this._updateCacheCanvas();// if canvas gets created, is empty, so dirty.\nthis.dirty=true;},/**\n * Limit the cache dimensions so that X * Y do not cross fabric.perfLimitSizeTotal\n * and each side do not cross fabric.cacheSideLimit\n * those numbers are configurable so that you can get as much detail as you want\n * making bargain with performances.\n * @param {Object} dims\n * @param {Object} dims.width width of canvas\n * @param {Object} dims.height height of canvas\n * @param {Object} dims.zoomX zoomX zoom value to unscale the canvas before drawing cache\n * @param {Object} dims.zoomY zoomY zoom value to unscale the canvas before drawing cache\n * @return {Object}.width width of canvas\n * @return {Object}.height height of canvas\n * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache\n * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache\n */_limitCacheSize:function _limitCacheSize(dims){var perfLimitSizeTotal=fabric.perfLimitSizeTotal,width=dims.width,height=dims.height,max=fabric.maxCacheSideLimit,min=fabric.minCacheSideLimit;if(width<=max&&height<=max&&width*height<=perfLimitSizeTotal){if(width<min){dims.width=min;}if(height<min){dims.height=min;}return dims;}var ar=width/height,limitedDims=fabric.util.limitDimsByArea(ar,perfLimitSizeTotal),capValue=fabric.util.capValue,x=capValue(min,limitedDims.x,max),y=capValue(min,limitedDims.y,max);if(width>x){dims.zoomX/=width/x;dims.width=x;dims.capped=true;}if(height>y){dims.zoomY/=height/y;dims.height=y;dims.capped=true;}return dims;},/**\n * Return the dimension and the zoom level needed to create a cache canvas\n * big enough to host the object to be cached.\n * @private\n * @return {Object}.x width of object to be cached\n * @return {Object}.y height of object to be cached\n * @return {Object}.width width of canvas\n * @return {Object}.height height of canvas\n * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache\n * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache\n */_getCacheCanvasDimensions:function _getCacheCanvasDimensions(){var objectScale=this.getTotalObjectScaling(),// caculate dimensions without skewing\ndim=this._getTransformedDimensions(0,0),neededX=dim.x*objectScale.scaleX/this.scaleX,neededY=dim.y*objectScale.scaleY/this.scaleY;return{// for sure this ALIASING_LIMIT is slightly creating problem\n// in situation in which the cache canvas gets an upper limit\n// also objectScale contains already scaleX and scaleY\nwidth:neededX+ALIASING_LIMIT,height:neededY+ALIASING_LIMIT,zoomX:objectScale.scaleX,zoomY:objectScale.scaleY,x:neededX,y:neededY};},/**\n * Update width and height of the canvas for cache\n * returns true or false if canvas needed resize.\n * @private\n * @return {Boolean} true if the canvas has been resized\n */_updateCacheCanvas:function _updateCacheCanvas(){var targetCanvas=this.canvas;if(this.noScaleCache&&targetCanvas&&targetCanvas._currentTransform){var target=targetCanvas._currentTransform.target,action=targetCanvas._currentTransform.action;if(this===target&&action.slice&&action.slice(0,5)==='scale'){return false;}}var canvas=this._cacheCanvas,dims=this._limitCacheSize(this._getCacheCanvasDimensions()),minCacheSize=fabric.minCacheSideLimit,width=dims.width,height=dims.height,drawingWidth,drawingHeight,zoomX=dims.zoomX,zoomY=dims.zoomY,dimensionsChanged=width!==this.cacheWidth||height!==this.cacheHeight,zoomChanged=this.zoomX!==zoomX||this.zoomY!==zoomY,shouldRedraw=dimensionsChanged||zoomChanged,additionalWidth=0,additionalHeight=0,shouldResizeCanvas=false;if(dimensionsChanged){var canvasWidth=this._cacheCanvas.width,canvasHeight=this._cacheCanvas.height,sizeGrowing=width>canvasWidth||height>canvasHeight,sizeShrinking=(width<canvasWidth*0.9||height<canvasHeight*0.9)&&canvasWidth>minCacheSize&&canvasHeight>minCacheSize;shouldResizeCanvas=sizeGrowing||sizeShrinking;if(sizeGrowing&&!dims.capped&&(width>minCacheSize||height>minCacheSize)){additionalWidth=width*0.1;additionalHeight=height*0.1;}}if(shouldRedraw){if(shouldResizeCanvas){canvas.width=Math.ceil(width+additionalWidth);canvas.height=Math.ceil(height+additionalHeight);}else{this._cacheContext.setTransform(1,0,0,1,0,0);this._cacheContext.clearRect(0,0,canvas.width,canvas.height);}drawingWidth=dims.x/2;drawingHeight=dims.y/2;this.cacheTranslationX=Math.round(canvas.width/2-drawingWidth)+drawingWidth;this.cacheTranslationY=Math.round(canvas.height/2-drawingHeight)+drawingHeight;this.cacheWidth=width;this.cacheHeight=height;this._cacheContext.translate(this.cacheTranslationX,this.cacheTranslationY);this._cacheContext.scale(zoomX,zoomY);this.zoomX=zoomX;this.zoomY=zoomY;return true;}return false;},/**\n * Sets object's properties from options\n * @param {Object} [options] Options object\n */setOptions:function setOptions(options){this._setOptions(options);this._initGradient(options.fill,'fill');this._initGradient(options.stroke,'stroke');this._initClipping(options);this._initPattern(options.fill,'fill');this._initPattern(options.stroke,'stroke');},/**\n * Transforms context when rendering an object\n * @param {CanvasRenderingContext2D} ctx Context\n */transform:function transform(ctx){var m;if(this.group&&!this.group._transformDone){m=this.calcTransformMatrix();}else{m=this.calcOwnMatrix();}ctx.transform(m[0],m[1],m[2],m[3],m[4],m[5]);},/**\n * Returns an object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */toObject:function toObject(propertiesToInclude){var NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS,object={type:this.type,version:fabric.version,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?this.strokeDashArray.concat():this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeDashOffset:this.strokeDashOffset,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.angle,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,fillRule:this.fillRule,paintFirst:this.paintFirst,globalCompositeOperation:this.globalCompositeOperation,transformMatrix:this.transformMatrix?this.transformMatrix.concat():null,skewX:toFixed(this.skewX,NUM_FRACTION_DIGITS),skewY:toFixed(this.skewY,NUM_FRACTION_DIGITS)};if(this.clipPath){object.clipPath=this.clipPath.toObject(propertiesToInclude);object.clipPath.inverted=this.clipPath.inverted;object.clipPath.absolutePositioned=this.clipPath.absolutePositioned;}fabric.util.populateWithProperties(this,object,propertiesToInclude);if(!this.includeDefaultValues){object=this._removeDefaultValues(object);}return object;},/**\n * Returns (dataless) object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */toDatalessObject:function toDatalessObject(propertiesToInclude){// will be overwritten by subclasses\nreturn this.toObject(propertiesToInclude);},/**\n * @private\n * @param {Object} object\n */_removeDefaultValues:function _removeDefaultValues(object){var prototype=fabric.util.getKlass(object.type).prototype,stateProperties=prototype.stateProperties;stateProperties.forEach(function(prop){if(prop==='left'||prop==='top'){return;}if(object[prop]===prototype[prop]){delete object[prop];}var isArray=Object.prototype.toString.call(object[prop])==='[object Array]'&&Object.prototype.toString.call(prototype[prop])==='[object Array]';// basically a check for [] === []\nif(isArray&&object[prop].length===0&&prototype[prop].length===0){delete object[prop];}});return object;},/**\n * Returns a string representation of an instance\n * @return {String}\n */toString:function toString(){return'#<fabric.'+capitalize(this.type)+'>';},/**\n * Return the object scale factor counting also the group scaling\n * @return {Object} object with scaleX and scaleY properties\n */getObjectScaling:function getObjectScaling(){var scaleX=this.scaleX,scaleY=this.scaleY;if(this.group){var scaling=this.group.getObjectScaling();scaleX*=scaling.scaleX;scaleY*=scaling.scaleY;}return{scaleX:scaleX,scaleY:scaleY};},/**\n * Return the object scale factor counting also the group scaling, zoom and retina\n * @return {Object} object with scaleX and scaleY properties\n */getTotalObjectScaling:function getTotalObjectScaling(){var scale=this.getObjectScaling(),scaleX=scale.scaleX,scaleY=scale.scaleY;if(this.canvas){var zoom=this.canvas.getZoom();var retina=this.canvas.getRetinaScaling();scaleX*=zoom*retina;scaleY*=zoom*retina;}return{scaleX:scaleX,scaleY:scaleY};},/**\n * Return the object opacity counting also the group property\n * @return {Number}\n */getObjectOpacity:function getObjectOpacity(){var opacity=this.opacity;if(this.group){opacity*=this.group.getObjectOpacity();}return opacity;},/**\n * @private\n * @param {String} key\n * @param {*} value\n * @return {fabric.Object} thisArg\n */_set:function _set(key,value){var shouldConstrainValue=key==='scaleX'||key==='scaleY',isChanged=this[key]!==value,groupNeedsUpdate=false;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==='shadow'&&value&&!(value instanceof fabric.Shadow)){value=new fabric.Shadow(value);}else if(key==='dirty'&&this.group){this.group.set('dirty',value);}this[key]=value;if(isChanged){groupNeedsUpdate=this.group&&this.group.isOnACache();if(this.cacheProperties.indexOf(key)>-1){this.dirty=true;groupNeedsUpdate&&this.group.set('dirty',true);}else if(groupNeedsUpdate&&this.stateProperties.indexOf(key)>-1){this.group.set('dirty',true);}}return this;},/**\n * This callback function is called by the parent group of an object every\n * time a non-delegated property changes on the group. It is passed the key\n * and value as parameters. Not adding in this function's signature to avoid\n * Travis build error about unused variables.\n */setOnGroup:function setOnGroup(){// implemented by sub-classes, as needed.\n},/**\n * Retrieves viewportTransform from Object's canvas if possible\n * @method getViewportTransform\n * @memberOf fabric.Object.prototype\n * @return {Array}\n */getViewportTransform:function getViewportTransform(){if(this.canvas&&this.canvas.viewportTransform){return this.canvas.viewportTransform;}return fabric.iMatrix.concat();},/*\n * @private\n * return if the object would be visible in rendering\n * @memberOf fabric.Object.prototype\n * @return {Boolean}\n */isNotVisible:function isNotVisible(){return this.opacity===0||this.width===0&&this.height===0&&this.strokeWidth===0||!this.visible;},/**\n * Renders an object on a specified context\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */render:function render(ctx){// do not render if width/height are zeros or object is not visible\nif(this.isNotVisible()){return;}if(this.canvas&&this.canvas.skipOffscreen&&!this.group&&!this.isOnScreen()){return;}ctx.save();this._setupCompositeOperation(ctx);this.drawSelectionBackground(ctx);this.transform(ctx);this._setOpacity(ctx);this._setShadow(ctx,this);if(this.transformMatrix){ctx.transform.apply(ctx,this.transformMatrix);}this.clipTo&&fabric.util.clipContext(this,ctx);if(this.shouldCache()){this.renderCache();this.drawCacheOnCanvas(ctx);}else{this._removeCacheCanvas();this.dirty=false;this.drawObject(ctx);if(this.objectCaching&&this.statefullCache){this.saveState({propertySet:'cacheProperties'});}}this.clipTo&&ctx.restore();ctx.restore();},renderCache:function renderCache(options){options=options||{};if(!this._cacheCanvas){this._createCacheCanvas();}if(this.isCacheDirty()){this.statefullCache&&this.saveState({propertySet:'cacheProperties'});this.drawObject(this._cacheContext,options.forClipping);this.dirty=false;}},/**\n * Remove cacheCanvas and its dimensions from the objects\n */_removeCacheCanvas:function _removeCacheCanvas(){this._cacheCanvas=null;this.cacheWidth=0;this.cacheHeight=0;},/**\n * return true if the object will draw a stroke\n * Does not consider text styles. This is just a shortcut used at rendering time\n * We want it to be an aproximation and be fast.\n * wrote to avoid extra caching, it has to return true when stroke happens,\n * can guess when it will not happen at 100% chance, does not matter if it misses\n * some use case where the stroke is invisible.\n * @since 3.0.0\n * @returns Boolean\n */hasStroke:function hasStroke(){return this.stroke&&this.stroke!=='transparent'&&this.strokeWidth!==0;},/**\n * return true if the object will draw a fill\n * Does not consider text styles. This is just a shortcut used at rendering time\n * We want it to be an aproximation and be fast.\n * wrote to avoid extra caching, it has to return true when fill happens,\n * can guess when it will not happen at 100% chance, does not matter if it misses\n * some use case where the fill is invisible.\n * @since 3.0.0\n * @returns Boolean\n */hasFill:function hasFill(){return this.fill&&this.fill!=='transparent';},/**\n * When set to `true`, force the object to have its own cache, even if it is inside a group\n * it may be needed when your object behave in a particular way on the cache and always needs\n * its own isolated canvas to render correctly.\n * Created to be overridden\n * since 1.7.12\n * @returns Boolean\n */needsItsOwnCache:function needsItsOwnCache(){if(this.paintFirst==='stroke'&&this.hasFill()&&this.hasStroke()&&_typeof(this.shadow)==='object'){return true;}if(this.clipPath){return true;}return false;},/**\n * Decide if the object should cache or not. Create its own cache level\n * objectCaching is a global flag, wins over everything\n * needsItsOwnCache should be used when the object drawing method requires\n * a cache step. None of the fabric classes requires it.\n * Generally you do not cache objects in groups because the group outside is cached.\n * Read as: cache if is needed, or if the feature is enabled but we are not already caching.\n * @return {Boolean}\n */shouldCache:function shouldCache(){this.ownCaching=this.needsItsOwnCache()||this.objectCaching&&(!this.group||!this.group.isOnACache());return this.ownCaching;},/**\n * Check if this object or a child object will cast a shadow\n * used by Group.shouldCache to know if child has a shadow recursively\n * @return {Boolean}\n */willDrawShadow:function willDrawShadow(){return!!this.shadow&&(this.shadow.offsetX!==0||this.shadow.offsetY!==0);},/**\n * Execute the drawing operation for an object clipPath\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */drawClipPathOnCache:function drawClipPathOnCache(ctx){var path=this.clipPath;ctx.save();// DEBUG: uncomment this line, comment the following\n// ctx.globalAlpha = 0.4\nif(path.inverted){ctx.globalCompositeOperation='destination-out';}else{ctx.globalCompositeOperation='destination-in';}//ctx.scale(1 / 2, 1 / 2);\nif(path.absolutePositioned){var m=fabric.util.invertTransform(this.calcTransformMatrix());ctx.transform(m[0],m[1],m[2],m[3],m[4],m[5]);}path.transform(ctx);ctx.scale(1/path.zoomX,1/path.zoomY);ctx.drawImage(path._cacheCanvas,-path.cacheTranslationX,-path.cacheTranslationY);ctx.restore();},/**\n * Execute the drawing operation for an object on a specified context\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */drawObject:function drawObject(ctx,forClipping){var originalFill=this.fill,originalStroke=this.stroke;if(forClipping){this.fill='black';this.stroke='';this._setClippingProperties(ctx);}else{this._renderBackground(ctx);this._setStrokeStyles(ctx,this);this._setFillStyles(ctx,this);}this._render(ctx);this._drawClipPath(ctx);this.fill=originalFill;this.stroke=originalStroke;},_drawClipPath:function _drawClipPath(ctx){var path=this.clipPath;if(!path){return;}// needed to setup a couple of variables\n// path canvas gets overridden with this one.\n// TODO find a better solution?\npath.canvas=this.canvas;path.shouldCache();path._transformDone=true;path.renderCache({forClipping:true});this.drawClipPathOnCache(ctx);},/**\n * Paint the cached copy of the object on the target context.\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */drawCacheOnCanvas:function drawCacheOnCanvas(ctx){ctx.scale(1/this.zoomX,1/this.zoomY);ctx.drawImage(this._cacheCanvas,-this.cacheTranslationX,-this.cacheTranslationY);},/**\n * Check if cache is dirty\n * @param {Boolean} skipCanvas skip canvas checks because this object is painted\n * on parent canvas.\n */isCacheDirty:function isCacheDirty(skipCanvas){if(this.isNotVisible()){return false;}if(this._cacheCanvas&&!skipCanvas&&this._updateCacheCanvas()){// in this case the context is already cleared.\nreturn true;}else{if(this.dirty||this.clipPath&&this.clipPath.absolutePositioned||this.statefullCache&&this.hasStateChanged('cacheProperties')){if(this._cacheCanvas&&!skipCanvas){var width=this.cacheWidth/this.zoomX;var height=this.cacheHeight/this.zoomY;this._cacheContext.clearRect(-width/2,-height/2,width,height);}return true;}}return false;},/**\n * Draws a background for the object big as its untransformed dimensions\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderBackground:function _renderBackground(ctx){if(!this.backgroundColor){return;}var dim=this._getNonTransformedDimensions();ctx.fillStyle=this.backgroundColor;ctx.fillRect(-dim.x/2,-dim.y/2,dim.x,dim.y);// if there is background color no other shadows\n// should be casted\nthis._removeShadow(ctx);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_setOpacity:function _setOpacity(ctx){if(this.group&&!this.group._transformDone){ctx.globalAlpha=this.getObjectOpacity();}else{ctx.globalAlpha*=this.opacity;}},_setStrokeStyles:function _setStrokeStyles(ctx,decl){if(decl.stroke){ctx.lineWidth=decl.strokeWidth;ctx.lineCap=decl.strokeLineCap;ctx.lineDashOffset=decl.strokeDashOffset;ctx.lineJoin=decl.strokeLineJoin;ctx.miterLimit=decl.strokeMiterLimit;ctx.strokeStyle=decl.stroke.toLive?decl.stroke.toLive(ctx,this):decl.stroke;}},_setFillStyles:function _setFillStyles(ctx,decl){if(decl.fill){ctx.fillStyle=decl.fill.toLive?decl.fill.toLive(ctx,this):decl.fill;}},_setClippingProperties:function _setClippingProperties(ctx){ctx.globalAlpha=1;ctx.strokeStyle='transparent';ctx.fillStyle='#000000';},/**\n * @private\n * Sets line dash\n * @param {CanvasRenderingContext2D} ctx Context to set the dash line on\n * @param {Array} dashArray array representing dashes\n * @param {Function} alternative function to call if browser does not support lineDash\n */_setLineDash:function _setLineDash(ctx,dashArray,alternative){if(!dashArray||dashArray.length===0){return;}// Spec requires the concatenation of two copies the dash list when the number of elements is odd\nif(1&dashArray.length){dashArray.push.apply(dashArray,dashArray);}if(supportsLineDash){ctx.setLineDash(dashArray);}else{alternative&&alternative(ctx);}},/**\n * Renders controls and borders for the object\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Object} [styleOverride] properties to override the object style\n */_renderControls:function _renderControls(ctx,styleOverride){var vpt=this.getViewportTransform(),matrix=this.calcTransformMatrix(),options,drawBorders,drawControls;styleOverride=styleOverride||{};drawBorders=typeof styleOverride.hasBorders!=='undefined'?styleOverride.hasBorders:this.hasBorders;drawControls=typeof styleOverride.hasControls!=='undefined'?styleOverride.hasControls:this.hasControls;matrix=fabric.util.multiplyTransformMatrices(vpt,matrix);options=fabric.util.qrDecompose(matrix);ctx.save();ctx.translate(options.translateX,options.translateY);ctx.lineWidth=1*this.borderScaleFactor;if(!this.group){ctx.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1;}if(styleOverride.forActiveSelection){ctx.rotate(degreesToRadians(options.angle));drawBorders&&this.drawBordersInGroup(ctx,options,styleOverride);}else{ctx.rotate(degreesToRadians(this.angle));drawBorders&&this.drawBorders(ctx,styleOverride);}drawControls&&this.drawControls(ctx,styleOverride);ctx.restore();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_setShadow:function _setShadow(ctx){if(!this.shadow){return;}var shadow=this.shadow,canvas=this.canvas,scaling,multX=canvas&&canvas.viewportTransform[0]||1,multY=canvas&&canvas.viewportTransform[3]||1;if(shadow.nonScaling){scaling={scaleX:1,scaleY:1};}else{scaling=this.getObjectScaling();}if(canvas&&canvas._isRetinaScaling()){multX*=fabric.devicePixelRatio;multY*=fabric.devicePixelRatio;}ctx.shadowColor=shadow.color;ctx.shadowBlur=shadow.blur*fabric.browserShadowBlurConstant*(multX+multY)*(scaling.scaleX+scaling.scaleY)/4;ctx.shadowOffsetX=shadow.offsetX*multX*scaling.scaleX;ctx.shadowOffsetY=shadow.offsetY*multY*scaling.scaleY;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_removeShadow:function _removeShadow(ctx){if(!this.shadow){return;}ctx.shadowColor='';ctx.shadowBlur=ctx.shadowOffsetX=ctx.shadowOffsetY=0;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Object} filler fabric.Pattern or fabric.Gradient\n * @return {Object} offset.offsetX offset for text rendering\n * @return {Object} offset.offsetY offset for text rendering\n */_applyPatternGradientTransform:function _applyPatternGradientTransform(ctx,filler){if(!filler||!filler.toLive){return{offsetX:0,offsetY:0};}var t=filler.gradientTransform||filler.patternTransform;var offsetX=-this.width/2+filler.offsetX||0,offsetY=-this.height/2+filler.offsetY||0;if(filler.gradientUnits==='percentage'){ctx.transform(this.width,0,0,this.height,offsetX,offsetY);}else{ctx.transform(1,0,0,1,offsetX,offsetY);}if(t){ctx.transform(t[0],t[1],t[2],t[3],t[4],t[5]);}return{offsetX:offsetX,offsetY:offsetY};},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderPaintInOrder:function _renderPaintInOrder(ctx){if(this.paintFirst==='stroke'){this._renderStroke(ctx);this._renderFill(ctx);}else{this._renderFill(ctx);this._renderStroke(ctx);}},/**\n * @private\n * function that actually render something on the context.\n * empty here to allow Obects to work on tests to benchmark fabric functionalites\n * not related to rendering\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render()/* ctx */{},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderFill:function _renderFill(ctx){if(!this.fill){return;}ctx.save();this._applyPatternGradientTransform(ctx,this.fill);if(this.fillRule==='evenodd'){ctx.fill('evenodd');}else{ctx.fill();}ctx.restore();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderStroke:function _renderStroke(ctx){if(!this.stroke||this.strokeWidth===0){return;}if(this.shadow&&!this.shadow.affectStroke){this._removeShadow(ctx);}ctx.save();if(this.strokeUniform){ctx.scale(1/this.scaleX,1/this.scaleY);}this._setLineDash(ctx,this.strokeDashArray,this._renderDashedStroke);if(this.stroke.toLive&&this.stroke.gradientUnits==='percentage'){// need to transform gradient in a pattern.\n// this is a slow process. If you are hitting this codepath, and the object\n// is not using caching, you should consider switching it on.\n// we need a canvas as big as the current object caching canvas.\nthis._applyPatternForTransformedGradient(ctx,this.stroke);}else{this._applyPatternGradientTransform(ctx,this.stroke);}ctx.stroke();ctx.restore();},/**\n * This function try to patch the missing gradientTransform on canvas gradients.\n * transforming a context to transform the gradient, is going to transform the stroke too.\n * we want to transform the gradient but not the stroke operation, so we create\n * a transformed gradient on a pattern and then we use the pattern instead of the gradient.\n * this method has drwabacks: is slow, is in low resolution, needs a patch for when the size\n * is limited.\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {fabric.Gradient} filler a fabric gradient instance\n */_applyPatternForTransformedGradient:function _applyPatternForTransformedGradient(ctx,filler){var dims=this._limitCacheSize(this._getCacheCanvasDimensions()),pCanvas=fabric.util.createCanvasElement(),pCtx,retinaScaling=this.canvas.getRetinaScaling(),width=dims.x/this.scaleX/retinaScaling,height=dims.y/this.scaleY/retinaScaling;pCanvas.width=width;pCanvas.height=height;pCtx=pCanvas.getContext('2d');pCtx.beginPath();pCtx.moveTo(0,0);pCtx.lineTo(width,0);pCtx.lineTo(width,height);pCtx.lineTo(0,height);pCtx.closePath();pCtx.translate(width/2,height/2);pCtx.scale(dims.zoomX/this.scaleX/retinaScaling,dims.zoomY/this.scaleY/retinaScaling);this._applyPatternGradientTransform(pCtx,filler);pCtx.fillStyle=filler.toLive(ctx);pCtx.fill();ctx.translate(-this.width/2-this.strokeWidth/2,-this.height/2-this.strokeWidth/2);ctx.scale(retinaScaling*this.scaleX/dims.zoomX,retinaScaling*this.scaleY/dims.zoomY);ctx.strokeStyle=pCtx.createPattern(pCanvas,'no-repeat');},/**\n * This function is an helper for svg import. it returns the center of the object in the svg\n * untransformed coordinates\n * @private\n * @return {Object} center point from element coordinates\n */_findCenterFromElement:function _findCenterFromElement(){return{x:this.left+this.width/2,y:this.top+this.height/2};},/**\n * This function is an helper for svg import. it decompose the transformMatrix\n * and assign properties to object.\n * untransformed coordinates\n * @private\n * @chainable\n */_assignTransformMatrixProps:function _assignTransformMatrixProps(){if(this.transformMatrix){var options=fabric.util.qrDecompose(this.transformMatrix);this.flipX=false;this.flipY=false;this.set('scaleX',options.scaleX);this.set('scaleY',options.scaleY);this.angle=options.angle;this.skewX=options.skewX;this.skewY=0;}},/**\n * This function is an helper for svg import. it removes the transform matrix\n * and set to object properties that fabricjs can handle\n * @private\n * @param {Object} preserveAspectRatioOptions\n * @return {thisArg}\n */_removeTransformMatrix:function _removeTransformMatrix(preserveAspectRatioOptions){var center=this._findCenterFromElement();if(this.transformMatrix){this._assignTransformMatrixProps();center=fabric.util.transformPoint(center,this.transformMatrix);}this.transformMatrix=null;if(preserveAspectRatioOptions){this.scaleX*=preserveAspectRatioOptions.scaleX;this.scaleY*=preserveAspectRatioOptions.scaleY;this.cropX=preserveAspectRatioOptions.cropX;this.cropY=preserveAspectRatioOptions.cropY;center.x+=preserveAspectRatioOptions.offsetLeft;center.y+=preserveAspectRatioOptions.offsetTop;this.width=preserveAspectRatioOptions.width;this.height=preserveAspectRatioOptions.height;}this.setPositionByOrigin(center,'center','center');},/**\n * Clones an instance, using a callback method will work for every object.\n * @param {Function} callback Callback is invoked with a clone as a first argument\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n */clone:function clone(callback,propertiesToInclude){var objectForm=this.toObject(propertiesToInclude);if(this.constructor.fromObject){this.constructor.fromObject(objectForm,callback);}else{fabric.Object._fromObject('Object',objectForm,callback);}},/**\n * Creates an instance of fabric.Image out of an object\n * could make use of both toDataUrl or toCanvasElement.\n * @param {Function} callback callback, invoked with an instance as a first argument\n * @param {Object} [options] for clone as image, passed to toDataURL\n * @param {String} [options.format=png] The format of the output image. Either \"jpeg\" or \"png\"\n * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.\n * @param {Number} [options.multiplier=1] Multiplier to scale by\n * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14\n * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14\n * @param {Number} [options.width] Cropping width. Introduced in v1.2.14\n * @param {Number} [options.height] Cropping height. Introduced in v1.2.14\n * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4\n * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4\n * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2\n * @return {fabric.Object} thisArg\n */cloneAsImage:function cloneAsImage(callback,options){var canvasEl=this.toCanvasElement(options);if(callback){callback(new fabric.Image(canvasEl));}return this;},/**\n * Converts an object into a HTMLCanvas element\n * @param {Object} options Options object\n * @param {Number} [options.multiplier=1] Multiplier to scale by\n * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14\n * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14\n * @param {Number} [options.width] Cropping width. Introduced in v1.2.14\n * @param {Number} [options.height] Cropping height. Introduced in v1.2.14\n * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4\n * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4\n * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2\n * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format\n */toCanvasElement:function toCanvasElement(options){options||(options={});var utils=fabric.util,origParams=utils.saveObjectTransform(this),originalGroup=this.group,originalShadow=this.shadow,abs=Math.abs,multiplier=(options.multiplier||1)*(options.enableRetinaScaling?fabric.devicePixelRatio:1);delete this.group;if(options.withoutTransform){utils.resetObjectTransform(this);}if(options.withoutShadow){this.shadow=null;}var el=fabric.util.createCanvasElement(),// skip canvas zoom and calculate with setCoords now.\nboundingRect=this.getBoundingRect(true,true),shadow=this.shadow,scaling,shadowOffset={x:0,y:0},shadowBlur;if(shadow){shadowBlur=shadow.blur;if(shadow.nonScaling){scaling={scaleX:1,scaleY:1};}else{scaling=this.getObjectScaling();}// consider non scaling shadow.\nshadowOffset.x=2*Math.round(abs(shadow.offsetX)+shadowBlur)*abs(scaling.scaleX);shadowOffset.y=2*Math.round(abs(shadow.offsetY)+shadowBlur)*abs(scaling.scaleY);}el.width=boundingRect.width+shadowOffset.x;el.height=boundingRect.height+shadowOffset.y;el.width+=el.width%2?2-el.width%2:0;el.height+=el.height%2?2-el.height%2:0;var canvas=new fabric.StaticCanvas(el,{enableRetinaScaling:false,renderOnAddRemove:false,skipOffscreen:false});if(options.format==='jpeg'){canvas.backgroundColor='#fff';}this.setPositionByOrigin(new fabric.Point(canvas.width/2,canvas.height/2),'center','center');var originalCanvas=this.canvas;canvas.add(this);var canvasEl=canvas.toCanvasElement(multiplier||1,options);this.shadow=originalShadow;this.canvas=originalCanvas;if(originalGroup){this.group=originalGroup;}this.set(origParams).setCoords();// canvas.dispose will call image.dispose that will nullify the elements\n// since this canvas is a simple element for the process, we remove references\n// to objects in this way in order to avoid object trashing.\ncanvas._objects=[];canvas.dispose();canvas=null;return canvasEl;},/**\n * Converts an object into a data-url-like string\n * @param {Object} options Options object\n * @param {String} [options.format=png] The format of the output image. Either \"jpeg\" or \"png\"\n * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.\n * @param {Number} [options.multiplier=1] Multiplier to scale by\n * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14\n * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14\n * @param {Number} [options.width] Cropping width. Introduced in v1.2.14\n * @param {Number} [options.height] Cropping height. Introduced in v1.2.14\n * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4\n * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4\n * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2\n * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format\n */toDataURL:function toDataURL(options){options||(options={});return fabric.util.toDataURL(this.toCanvasElement(options),options.format||'png',options.quality||1);},/**\n * Returns true if specified type is identical to the type of an instance\n * @param {String} type Type to check against\n * @return {Boolean}\n */isType:function isType(type){return this.type===type;},/**\n * Returns complexity of an instance\n * @return {Number} complexity of this instance (is 1 unless subclassed)\n */complexity:function complexity(){return 1;},/**\n * Returns a JSON representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} JSON\n */toJSON:function toJSON(propertiesToInclude){// delegate, not alias\nreturn this.toObject(propertiesToInclude);},/**\n * Sets gradient (fill or stroke) of an object\n * percentages for x1,x2,y1,y2,r1,r2 together with gradientUnits 'pixels', are not supported.\n * <b>Backwards incompatibility note:</b> This method was named \"setGradientFill\" until v1.1.0\n * @param {String} property Property name 'stroke' or 'fill'\n * @param {Object} [options] Options object\n * @param {String} [options.type] Type of gradient 'radial' or 'linear'\n * @param {Number} [options.x1=0] x-coordinate of start point\n * @param {Number} [options.y1=0] y-coordinate of start point\n * @param {Number} [options.x2=0] x-coordinate of end point\n * @param {Number} [options.y2=0] y-coordinate of end point\n * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)\n * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)\n * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}\n * @param {Object} [options.gradientTransform] transformMatrix for gradient\n * @return {fabric.Object} thisArg\n * @chainable\n * @deprecated since 3.4.0\n * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}\n * @example <caption>Set linear gradient</caption>\n * object.setGradient('fill', {\n * type: 'linear',\n * x1: -object.width / 2,\n * y1: 0,\n * x2: object.width / 2,\n * y2: 0,\n * colorStops: {\n * 0: 'red',\n * 0.5: '#005555',\n * 1: 'rgba(0,0,255,0.5)'\n * }\n * });\n * canvas.renderAll();\n * @example <caption>Set radial gradient</caption>\n * object.setGradient('fill', {\n * type: 'radial',\n * x1: 0,\n * y1: 0,\n * x2: 0,\n * y2: 0,\n * r1: object.width / 2,\n * r2: 10,\n * colorStops: {\n * 0: 'red',\n * 0.5: '#005555',\n * 1: 'rgba(0,0,255,0.5)'\n * }\n * });\n * canvas.renderAll();\n */setGradient:function setGradient(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};gradient.gradientUnits=options.gradientUnits||'pixels';if(options.r1||options.r2){gradient.coords.r1=options.r1;gradient.coords.r2=options.r2;}gradient.gradientTransform=options.gradientTransform;fabric.Gradient.prototype.addColorStop.call(gradient,options.colorStops);return this.set(property,fabric.Gradient.forObject(this,gradient));},/**\n * Sets pattern fill of an object\n * @param {Object} options Options object\n * @param {(String|HTMLImageElement)} options.source Pattern source\n * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)\n * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner\n * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner\n * @param {Function} [callback] Callback to invoke when image set as a pattern\n * @return {fabric.Object} thisArg\n * @chainable\n * @deprecated since 3.5.0\n * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}\n * @example <caption>Set pattern</caption>\n * object.setPatternFill({\n * source: 'http://fabricjs.com/assets/escheresque_ste.png',\n * repeat: 'repeat'\n * },canvas.renderAll.bind(canvas));\n */setPatternFill:function setPatternFill(options,callback){return this.set('fill',new fabric.Pattern(options,callback));},/**\n * Sets {@link fabric.Object#shadow|shadow} of an object\n * @param {Object|String} [options] Options object or string (e.g. \"2px 2px 10px rgba(0,0,0,0.2)\")\n * @param {String} [options.color=rgb(0,0,0)] Shadow color\n * @param {Number} [options.blur=0] Shadow blur\n * @param {Number} [options.offsetX=0] Shadow horizontal offset\n * @param {Number} [options.offsetY=0] Shadow vertical offset\n * @return {fabric.Object} thisArg\n * @chainable\n * @deprecated since 3.5.0\n * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}\n * @example <caption>Set shadow with string notation</caption>\n * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');\n * canvas.renderAll();\n * @example <caption>Set shadow with object notation</caption>\n * object.setShadow({\n * color: 'red',\n * blur: 10,\n * offsetX: 20,\n * offsetY: 20\n * });\n * canvas.renderAll();\n */setShadow:function setShadow(options){return this.set('shadow',options?new fabric.Shadow(options):null);},/**\n * Sets \"color\" of an instance (alias of `set('fill', …)`)\n * @param {String} color Color value\n * @return {fabric.Object} thisArg\n * @deprecated since 3.5.0\n * @chainable\n */setColor:function setColor(color){this.set('fill',color);return this;},/**\n * Sets \"angle\" of an instance with centered rotation\n * @param {Number} angle Angle value (in degrees)\n * @return {fabric.Object} thisArg\n * @chainable\n */rotate:function rotate(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;},/**\n * Centers object horizontally on canvas to which it was added last.\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */centerH:function centerH(){this.canvas&&this.canvas.centerObjectH(this);return this;},/**\n * Centers object horizontally on current viewport of canvas to which it was added last.\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */viewportCenterH:function viewportCenterH(){this.canvas&&this.canvas.viewportCenterObjectH(this);return this;},/**\n * Centers object vertically on canvas to which it was added last.\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */centerV:function centerV(){this.canvas&&this.canvas.centerObjectV(this);return this;},/**\n * Centers object vertically on current viewport of canvas to which it was added last.\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */viewportCenterV:function viewportCenterV(){this.canvas&&this.canvas.viewportCenterObjectV(this);return this;},/**\n * Centers object vertically and horizontally on canvas to which is was added last\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */center:function center(){this.canvas&&this.canvas.centerObject(this);return this;},/**\n * Centers object on current viewport of canvas to which it was added last.\n * You might need to call `setCoords` on an object after centering, to update controls area.\n * @return {fabric.Object} thisArg\n * @chainable\n */viewportCenter:function viewportCenter(){this.canvas&&this.canvas.viewportCenterObject(this);return this;},/**\n * Returns coordinates of a pointer relative to an object\n * @param {Event} e Event to operate upon\n * @param {Object} [pointer] Pointer to operate upon (instead of event)\n * @return {Object} Coordinates of a pointer (x, y)\n */getLocalPointer:function getLocalPointer(e,pointer){pointer=pointer||this.canvas.getPointer(e);var pClicked=new fabric.Point(pointer.x,pointer.y),objectLeftTop=this._getLeftTopCoords();if(this.angle){pClicked=fabric.util.rotatePoint(pClicked,objectLeftTop,degreesToRadians(-this.angle));}return{x:pClicked.x-objectLeftTop.x,y:pClicked.y-objectLeftTop.y};},/**\n * Sets canvas globalCompositeOperation for specific object\n * custom composition operation for the particular object can be specified using globalCompositeOperation property\n * @param {CanvasRenderingContext2D} ctx Rendering canvas context\n */_setupCompositeOperation:function _setupCompositeOperation(ctx){if(this.globalCompositeOperation){ctx.globalCompositeOperation=this.globalCompositeOperation;}}});fabric.util.createAccessors&&fabric.util.createAccessors(fabric.Object);extend(fabric.Object.prototype,fabric.Observable);/**\n * Defines the number of fraction digits to use when serializing object values.\n * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.\n * @static\n * @memberOf fabric.Object\n * @constant\n * @type Number\n */fabric.Object.NUM_FRACTION_DIGITS=2;fabric.Object._fromObject=function(className,object,callback,extraParam){var klass=fabric[className];object=clone(object,true);fabric.util.enlivenPatterns([object.fill,object.stroke],function(patterns){if(typeof patterns[0]!=='undefined'){object.fill=patterns[0];}if(typeof patterns[1]!=='undefined'){object.stroke=patterns[1];}fabric.util.enlivenObjects([object.clipPath],function(enlivedProps){object.clipPath=enlivedProps[0];var instance=extraParam?new klass(object[extraParam],object):new klass(object);callback&&callback(instance);});});};/**\n * Unique id used internally when creating SVG elements\n * @static\n * @memberOf fabric.Object\n * @type Number\n */fabric.Object.__uid=0;})( true?exports:undefined);(function(){var degreesToRadians=fabric.util.degreesToRadians,originXOffset={left:-0.5,center:0,right:0.5},originYOffset={top:-0.5,center:0,bottom:0.5};fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Translates the coordinates from a set of origin to another (based on the object's dimensions)\n * @param {fabric.Point} point The point which corresponds to the originX and originY params\n * @param {String} fromOriginX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} fromOriginY Vertical origin: 'top', 'center' or 'bottom'\n * @param {String} toOriginX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} toOriginY Vertical origin: 'top', 'center' or 'bottom'\n * @return {fabric.Point}\n */translateToGivenOrigin:function translateToGivenOrigin(point,fromOriginX,fromOriginY,toOriginX,toOriginY){var x=point.x,y=point.y,offsetX,offsetY,dim;if(typeof fromOriginX==='string'){fromOriginX=originXOffset[fromOriginX];}else{fromOriginX-=0.5;}if(typeof toOriginX==='string'){toOriginX=originXOffset[toOriginX];}else{toOriginX-=0.5;}offsetX=toOriginX-fromOriginX;if(typeof fromOriginY==='string'){fromOriginY=originYOffset[fromOriginY];}else{fromOriginY-=0.5;}if(typeof toOriginY==='string'){toOriginY=originYOffset[toOriginY];}else{toOriginY-=0.5;}offsetY=toOriginY-fromOriginY;if(offsetX||offsetY){dim=this._getTransformedDimensions();x=point.x+offsetX*dim.x;y=point.y+offsetY*dim.y;}return new fabric.Point(x,y);},/**\n * Translates the coordinates from origin to center coordinates (based on the object's dimensions)\n * @param {fabric.Point} point The point which corresponds to the originX and originY params\n * @param {String} originX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'\n * @return {fabric.Point}\n */translateToCenterPoint:function translateToCenterPoint(point,originX,originY){var p=this.translateToGivenOrigin(point,originX,originY,'center','center');if(this.angle){return fabric.util.rotatePoint(p,point,degreesToRadians(this.angle));}return p;},/**\n * Translates the coordinates from center to origin coordinates (based on the object's dimensions)\n * @param {fabric.Point} center The point which corresponds to center of the object\n * @param {String} originX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'\n * @return {fabric.Point}\n */translateToOriginPoint:function translateToOriginPoint(center,originX,originY){var p=this.translateToGivenOrigin(center,'center','center',originX,originY);if(this.angle){return fabric.util.rotatePoint(p,center,degreesToRadians(this.angle));}return p;},/**\n * Returns the real center coordinates of the object\n * @return {fabric.Point}\n */getCenterPoint:function getCenterPoint(){var leftTop=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(leftTop,this.originX,this.originY);},/**\n * Returns the coordinates of the object based on center coordinates\n * @param {fabric.Point} point The point which corresponds to the originX and originY params\n * @return {fabric.Point}\n */ // getOriginPoint: function(center) {\n// return this.translateToOriginPoint(center, this.originX, this.originY);\n// },\n/**\n * Returns the coordinates of the object as if it has a different origin\n * @param {String} originX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'\n * @return {fabric.Point}\n */getPointByOrigin:function getPointByOrigin(originX,originY){var center=this.getCenterPoint();return this.translateToOriginPoint(center,originX,originY);},/**\n * Returns the point in local coordinates\n * @param {fabric.Point} point The point relative to the global coordinate system\n * @param {String} originX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'\n * @return {fabric.Point}\n */toLocalPoint:function toLocalPoint(point,originX,originY){var center=this.getCenterPoint(),p,p2;if(typeof originX!=='undefined'&&typeof originY!=='undefined'){p=this.translateToGivenOrigin(center,'center','center',originX,originY);}else{p=new fabric.Point(this.left,this.top);}p2=new fabric.Point(point.x,point.y);if(this.angle){p2=fabric.util.rotatePoint(p2,center,-degreesToRadians(this.angle));}return p2.subtractEquals(p);},/**\n * Returns the point in global coordinates\n * @param {fabric.Point} The point relative to the local coordinate system\n * @return {fabric.Point}\n */ // toGlobalPoint: function(point) {\n// return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));\n// },\n/**\n * Sets the position of the object taking into consideration the object's origin\n * @param {fabric.Point} pos The new position of the object\n * @param {String} originX Horizontal origin: 'left', 'center' or 'right'\n * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'\n * @return {void}\n */setPositionByOrigin:function setPositionByOrigin(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);},/**\n * @param {String} to One of 'left', 'center', 'right'\n */adjustPosition:function adjustPosition(to){var angle=degreesToRadians(this.angle),hypotFull=this.getScaledWidth(),xFull=fabric.util.cos(angle)*hypotFull,yFull=fabric.util.sin(angle)*hypotFull,offsetFrom,offsetTo;//TODO: this function does not consider mixed situation like top, center.\nif(typeof this.originX==='string'){offsetFrom=originXOffset[this.originX];}else{offsetFrom=this.originX-0.5;}if(typeof to==='string'){offsetTo=originXOffset[to];}else{offsetTo=to-0.5;}this.left+=xFull*(offsetTo-offsetFrom);this.top+=yFull*(offsetTo-offsetFrom);this.setCoords();this.originX=to;},/**\n * Sets the origin/position of the object to it's center point\n * @private\n * @return {void}\n */_setOriginToCenter:function _setOriginToCenter(){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;},/**\n * Resets the origin/position of the object to it's original origin\n * @private\n * @return {void}\n */_resetOrigin:function _resetOrigin(){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;},/**\n * @private\n */_getLeftTopCoords:function _getLeftTopCoords(){return this.translateToOriginPoint(this.getCenterPoint(),'left','top');}});})();(function(){function _getCoords(coords){return[new fabric.Point(coords.tl.x,coords.tl.y),new fabric.Point(coords.tr.x,coords.tr.y),new fabric.Point(coords.br.x,coords.br.y),new fabric.Point(coords.bl.x,coords.bl.y)];}var degreesToRadians=fabric.util.degreesToRadians,multiplyMatrices=fabric.util.multiplyTransformMatrices,transformPoint=fabric.util.transformPoint;fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Describe object's corner position in canvas element coordinates.\n * properties are tl,mt,tr,ml,mr,bl,mb,br,mtr for the main controls.\n * each property is an object with x, y and corner.\n * The `corner` property contains in a similar manner the 4 points of the\n * interactive area of the corner.\n * The coordinates depends from this properties: width, height, scaleX, scaleY\n * skewX, skewY, angle, strokeWidth, viewportTransform, top, left, padding.\n * The coordinates get updated with @method setCoords.\n * You can calculate them without updating with @method calcCoords;\n * @memberOf fabric.Object.prototype\n */oCoords:null,/**\n * Describe object's corner position in canvas object absolute coordinates\n * properties are tl,tr,bl,br and describe the four main corner.\n * each property is an object with x, y, instance of Fabric.Point.\n * The coordinates depends from this properties: width, height, scaleX, scaleY\n * skewX, skewY, angle, strokeWidth, top, left.\n * Those coordinates are useful to understand where an object is. They get updated\n * with oCoords but they do not need to be updated when zoom or panning change.\n * The coordinates get updated with @method setCoords.\n * You can calculate them without updating with @method calcCoords(true);\n * @memberOf fabric.Object.prototype\n */aCoords:null,/**\n * storage for object transform matrix\n */ownMatrixCache:null,/**\n * storage for object full transform matrix\n */matrixCache:null,/**\n * return correct set of coordinates for intersection\n */getCoords:function getCoords(absolute,calculate){if(!this.oCoords){this.setCoords();}var coords=absolute?this.aCoords:this.oCoords;return _getCoords(calculate?this.calcCoords(absolute):coords);},/**\n * Checks if object intersects with an area formed by 2 points\n * @param {Object} pointTL top-left point of area\n * @param {Object} pointBR bottom-right point of area\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if object intersects with an area formed by 2 points\n */intersectsWithRect:function intersectsWithRect(pointTL,pointBR,absolute,calculate){var coords=this.getCoords(absolute,calculate),intersection=fabric.Intersection.intersectPolygonRectangle(coords,pointTL,pointBR);return intersection.status==='Intersection';},/**\n * Checks if object intersects with another object\n * @param {Object} other Object to test\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if object intersects with another object\n */intersectsWithObject:function intersectsWithObject(other,absolute,calculate){var intersection=fabric.Intersection.intersectPolygonPolygon(this.getCoords(absolute,calculate),other.getCoords(absolute,calculate));return intersection.status==='Intersection'||other.isContainedWithinObject(this,absolute,calculate)||this.isContainedWithinObject(other,absolute,calculate);},/**\n * Checks if object is fully contained within area of another object\n * @param {Object} other Object to test\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if object is fully contained within area of another object\n */isContainedWithinObject:function isContainedWithinObject(other,absolute,calculate){var points=this.getCoords(absolute,calculate),i=0,lines=other._getImageLines(calculate?other.calcCoords(absolute):absolute?other.aCoords:other.oCoords);for(;i<4;i++){if(!other.containsPoint(points[i],lines)){return false;}}return true;},/**\n * Checks if object is fully contained within area formed by 2 points\n * @param {Object} pointTL top-left point of area\n * @param {Object} pointBR bottom-right point of area\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if object is fully contained within area formed by 2 points\n */isContainedWithinRect:function isContainedWithinRect(pointTL,pointBR,absolute,calculate){var boundingRect=this.getBoundingRect(absolute,calculate);return boundingRect.left>=pointTL.x&&boundingRect.left+boundingRect.width<=pointBR.x&&boundingRect.top>=pointTL.y&&boundingRect.top+boundingRect.height<=pointBR.y;},/**\n * Checks if point is inside the object\n * @param {fabric.Point} point Point to check against\n * @param {Object} [lines] object returned from @method _getImageLines\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if point is inside the object\n */containsPoint:function containsPoint(point,lines,absolute,calculate){var lines=lines||this._getImageLines(calculate?this.calcCoords(absolute):absolute?this.aCoords:this.oCoords),xPoints=this._findCrossPoints(point,lines);// if xPoints is odd then point is inside the object\nreturn xPoints!==0&&xPoints%2===1;},/**\n * Checks if object is contained within the canvas with current viewportTransform\n * the check is done stopping at first point that appears on screen\n * @param {Boolean} [calculate] use coordinates of current position instead of .aCoords\n * @return {Boolean} true if object is fully or partially contained within canvas\n */isOnScreen:function isOnScreen(calculate){if(!this.canvas){return false;}var pointTL=this.canvas.vptCoords.tl,pointBR=this.canvas.vptCoords.br;var points=this.getCoords(true,calculate),point;for(var i=0;i<4;i++){point=points[i];if(point.x<=pointBR.x&&point.x>=pointTL.x&&point.y<=pointBR.y&&point.y>=pointTL.y){return true;}}// no points on screen, check intersection with absolute coordinates\nif(this.intersectsWithRect(pointTL,pointBR,true,calculate)){return true;}return this._containsCenterOfCanvas(pointTL,pointBR,calculate);},/**\n * Checks if the object contains the midpoint between canvas extremities\n * Does not make sense outside the context of isOnScreen and isPartiallyOnScreen\n * @private\n * @param {Fabric.Point} pointTL Top Left point\n * @param {Fabric.Point} pointBR Top Right point\n * @param {Boolean} calculate use coordinates of current position instead of .oCoords\n * @return {Boolean} true if the object contains the point\n */_containsCenterOfCanvas:function _containsCenterOfCanvas(pointTL,pointBR,calculate){// worst case scenario the object is so big that contains the screen\nvar centerPoint={x:(pointTL.x+pointBR.x)/2,y:(pointTL.y+pointBR.y)/2};if(this.containsPoint(centerPoint,null,true,calculate)){return true;}return false;},/**\n * Checks if object is partially contained within the canvas with current viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords\n * @return {Boolean} true if object is partially contained within canvas\n */isPartiallyOnScreen:function isPartiallyOnScreen(calculate){if(!this.canvas){return false;}var pointTL=this.canvas.vptCoords.tl,pointBR=this.canvas.vptCoords.br;if(this.intersectsWithRect(pointTL,pointBR,true,calculate)){return true;}return this._containsCenterOfCanvas(pointTL,pointBR,calculate);},/**\n * Method that returns an object with the object edges in it, given the coordinates of the corners\n * @private\n * @param {Object} oCoords Coordinates of the object corners\n */_getImageLines:function _getImageLines(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}};},/**\n * Helper method to determine how many cross points are between the 4 object edges\n * and the horizontal line determined by a point on canvas\n * @private\n * @param {fabric.Point} point Point to check\n * @param {Object} lines Coordinates of the object being evaluated\n */ // remove yi, not used but left code here just in case.\n_findCrossPoints:function _findCrossPoints(point,lines){var b1,b2,a1,a2,xi,// yi,\nxcount=0,iLine;for(var lineKey in lines){iLine=lines[lineKey];// optimisation 1: line below point. no cross\nif(iLine.o.y<point.y&&iLine.d.y<point.y){continue;}// optimisation 2: line above point. no cross\nif(iLine.o.y>=point.y&&iLine.d.y>=point.y){continue;}// optimisation 3: vertical line case\nif(iLine.o.x===iLine.d.x&&iLine.o.x>=point.x){xi=iLine.o.x;// yi = point.y;\n}// calculate the intersection point\nelse{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;\n}// dont count xi < point.x cases\nif(xi>=point.x){xcount+=1;}// optimisation 4: specific for square images\nif(xcount===2){break;}}return xcount;},/**\n * Returns coordinates of object's bounding rectangle (left, top, width, height)\n * the box is intended as aligned to axis of canvas.\n * @param {Boolean} [absolute] use coordinates without viewportTransform\n * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords / .aCoords\n * @return {Object} Object with left, top, width, height properties\n */getBoundingRect:function getBoundingRect(absolute,calculate){var coords=this.getCoords(absolute,calculate);return fabric.util.makeBoundingBoxFromPoints(coords);},/**\n * Returns width of an object's bounding box counting transformations\n * before 2.0 it was named getWidth();\n * @return {Number} width value\n */getScaledWidth:function getScaledWidth(){return this._getTransformedDimensions().x;},/**\n * Returns height of an object bounding box counting transformations\n * before 2.0 it was named getHeight();\n * @return {Number} height value\n */getScaledHeight:function getScaledHeight(){return this._getTransformedDimensions().y;},/**\n * Makes sure the scale is valid and modifies it if necessary\n * @private\n * @param {Number} value\n * @return {Number}\n */_constrainScale:function _constrainScale(value){if(Math.abs(value)<this.minScaleLimit){if(value<0){return-this.minScaleLimit;}else{return this.minScaleLimit;}}else if(value===0){return 0.0001;}return value;},/**\n * Scales an object (equally by x and y)\n * @param {Number} value Scale factor\n * @return {fabric.Object} thisArg\n * @chainable\n */scale:function scale(value){this._set('scaleX',value);this._set('scaleY',value);return this.setCoords();},/**\n * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)\n * @param {Number} value New width value\n * @param {Boolean} absolute ignore viewport\n * @return {fabric.Object} thisArg\n * @chainable\n */scaleToWidth:function scaleToWidth(value,absolute){// adjust to bounding rect factor so that rotated shapes would fit as well\nvar boundingRectFactor=this.getBoundingRect(absolute).width/this.getScaledWidth();return this.scale(value/this.width/boundingRectFactor);},/**\n * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)\n * @param {Number} value New height value\n * @param {Boolean} absolute ignore viewport\n * @return {fabric.Object} thisArg\n * @chainable\n */scaleToHeight:function scaleToHeight(value,absolute){// adjust to bounding rect factor so that rotated shapes would fit as well\nvar boundingRectFactor=this.getBoundingRect(absolute).height/this.getScaledHeight();return this.scale(value/this.height/boundingRectFactor);},/**\n * Calculates and returns the .coords of an object.\n * @return {Object} Object with tl, tr, br, bl ....\n * @chainable\n */calcCoords:function calcCoords(absolute){var rotateMatrix=this._calcRotateMatrix(),translateMatrix=this._calcTranslateMatrix(),startMatrix=multiplyMatrices(translateMatrix,rotateMatrix),vpt=this.getViewportTransform(),finalMatrix=absolute?startMatrix:multiplyMatrices(vpt,startMatrix),dim=this._getTransformedDimensions(),w=dim.x/2,h=dim.y/2,tl=transformPoint({x:-w,y:-h},finalMatrix),tr=transformPoint({x:w,y:-h},finalMatrix),bl=transformPoint({x:-w,y:h},finalMatrix),br=transformPoint({x:w,y:h},finalMatrix);if(!absolute){var padding=this.padding,angle=degreesToRadians(this.angle),cos=fabric.util.cos(angle),sin=fabric.util.sin(angle),cosP=cos*padding,sinP=sin*padding,cosPSinP=cosP+sinP,cosPMinusSinP=cosP-sinP;if(padding){tl.x-=cosPMinusSinP;tl.y-=cosPSinP;tr.x+=cosPSinP;tr.y-=cosPMinusSinP;bl.x-=cosPSinP;bl.y+=cosPMinusSinP;br.x+=cosPMinusSinP;br.y+=cosPSinP;}var ml=new fabric.Point((tl.x+bl.x)/2,(tl.y+bl.y)/2),mt=new fabric.Point((tr.x+tl.x)/2,(tr.y+tl.y)/2),mr=new fabric.Point((br.x+tr.x)/2,(br.y+tr.y)/2),mb=new fabric.Point((br.x+bl.x)/2,(br.y+bl.y)/2),mtr=new fabric.Point(mt.x+sin*this.rotatingPointOffset,mt.y-cos*this.rotatingPointOffset);}// if (!absolute) {\n// var canvas = this.canvas;\n// setTimeout(function() {\n// canvas.contextTop.clearRect(0, 0, 700, 700);\n// canvas.contextTop.fillStyle = 'green';\n// canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);\n// canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);\n// canvas.contextTop.fillRect(br.x, br.y, 3, 3);\n// canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);\n// canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);\n// canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);\n// canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);\n// canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);\n// canvas.contextTop.fillRect(mtr.x, mtr.y, 3, 3);\n// }, 50);\n// }\nvar coords={// corners\ntl:tl,tr:tr,br:br,bl:bl};if(!absolute){// middle\ncoords.ml=ml;coords.mt=mt;coords.mr=mr;coords.mb=mb;// rotating point\ncoords.mtr=mtr;}return coords;},/**\n * Sets corner position coordinates based on current angle, width and height.\n * See {@link https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords|When-to-call-setCoords}\n * @param {Boolean} [ignoreZoom] set oCoords with or without the viewport transform.\n * @param {Boolean} [skipAbsolute] skip calculation of aCoords, useful in setViewportTransform\n * @return {fabric.Object} thisArg\n * @chainable\n */setCoords:function setCoords(ignoreZoom,skipAbsolute){this.oCoords=this.calcCoords(ignoreZoom);if(!skipAbsolute){this.aCoords=this.calcCoords(true);}// set coordinates of the draggable boxes in the corners used to scale/rotate the image\nignoreZoom||this._setCornerCoords&&this._setCornerCoords();return this;},/**\n * calculate rotation matrix of an object\n * @return {Array} rotation matrix for the object\n */_calcRotateMatrix:function _calcRotateMatrix(){return fabric.util.calcRotateMatrix(this);},/**\n * calculate the translation matrix for an object transform\n * @return {Array} rotation matrix for the object\n */_calcTranslateMatrix:function _calcTranslateMatrix(){var center=this.getCenterPoint();return[1,0,0,1,center.x,center.y];},transformMatrixKey:function transformMatrixKey(skipGroup){var sep='_',prefix='';if(!skipGroup&&this.group){prefix=this.group.transformMatrixKey(skipGroup)+sep;};return prefix+this.top+sep+this.left+sep+this.scaleX+sep+this.scaleY+sep+this.skewX+sep+this.skewY+sep+this.angle+sep+this.originX+sep+this.originY+sep+this.width+sep+this.height+sep+this.strokeWidth+this.flipX+this.flipY;},/**\n * calculate transform matrix that represents the current transformations from the\n * object's properties.\n * @param {Boolean} [skipGroup] return transform matrix for object not counting parent transformations\n * @return {Array} transform matrix for the object\n */calcTransformMatrix:function calcTransformMatrix(skipGroup){if(skipGroup){return this.calcOwnMatrix();}var key=this.transformMatrixKey(),cache=this.matrixCache||(this.matrixCache={});if(cache.key===key){return cache.value;}var matrix=this.calcOwnMatrix();if(this.group){matrix=multiplyMatrices(this.group.calcTransformMatrix(),matrix);}cache.key=key;cache.value=matrix;return matrix;},/**\n * calculate transform matrix that represents the current transformations from the\n * object's properties, this matrix does not include the group transformation\n * @return {Array} transform matrix for the object\n */calcOwnMatrix:function calcOwnMatrix(){var key=this.transformMatrixKey(true),cache=this.ownMatrixCache||(this.ownMatrixCache={});if(cache.key===key){return cache.value;}var tMatrix=this._calcTranslateMatrix();this.translateX=tMatrix[4];this.translateY=tMatrix[5];cache.key=key;cache.value=fabric.util.composeMatrix(this);return cache.value;},/*\n * Calculate object dimensions from its properties\n * @private\n * @deprecated since 3.4.0, please use fabric.util._calcDimensionsTransformMatrix\n * not including or including flipX, flipY to emulate the flipping boolean\n * @return {Object} .x width dimension\n * @return {Object} .y height dimension\n */_calcDimensionsTransformMatrix:function _calcDimensionsTransformMatrix(skewX,skewY,flipping){return fabric.util.calcDimensionsMatrix({skewX:skewX,skewY:skewY,scaleX:this.scaleX*(flipping&&this.flipX?-1:1),scaleY:this.scaleY*(flipping&&this.flipY?-1:1)});},/*\n * Calculate object dimensions from its properties\n * @private\n * @return {Object} .x width dimension\n * @return {Object} .y height dimension\n */_getNonTransformedDimensions:function _getNonTransformedDimensions(){var strokeWidth=this.strokeWidth,w=this.width+strokeWidth,h=this.height+strokeWidth;return{x:w,y:h};},/*\n * Calculate object bounding box dimensions from its properties scale, skew.\n * The skewX and skewY parameters are used in the skewing logic path and\n * do not provide something useful to common use cases.\n * @param {Number} [skewX], a value to override current skewX\n * @param {Number} [skewY], a value to override current skewY\n * @private\n * @return {Object} .x width dimension\n * @return {Object} .y height dimension\n */_getTransformedDimensions:function _getTransformedDimensions(skewX,skewY){if(typeof skewX==='undefined'){skewX=this.skewX;}if(typeof skewY==='undefined'){skewY=this.skewY;}var dimensions=this._getNonTransformedDimensions(),dimX,dimY,noSkew=skewX===0&&skewY===0;if(this.strokeUniform){dimX=this.width;dimY=this.height;}else{dimX=dimensions.x;dimY=dimensions.y;}if(noSkew){return this._finalizeDimensions(dimX*this.scaleX,dimY*this.scaleY);}else{dimX/=2;dimY/=2;}var points=[{x:-dimX,y:-dimY},{x:dimX,y:-dimY},{x:-dimX,y:dimY},{x:dimX,y:dimY}],transformMatrix=fabric.util.calcDimensionsMatrix({scaleX:this.scaleX,scaleY:this.scaleY,skewX:skewX,skewY:skewY}),bbox=fabric.util.makeBoundingBoxFromPoints(points,transformMatrix);return this._finalizeDimensions(bbox.width,bbox.height);},/*\n * Calculate object bounding box dimensions from its properties scale, skew.\n * @param Number width width of the bbox\n * @param Number height height of the bbox\n * @private\n * @return {Object} .x finalized width dimension\n * @return {Object} .y finalized height dimension\n */_finalizeDimensions:function _finalizeDimensions(width,height){return this.strokeUniform?{x:width+this.strokeWidth,y:height+this.strokeWidth}:{x:width,y:height};},/*\n * Calculate object dimensions for controls, including padding and canvas zoom.\n * private\n */_calculateCurrentDimensions:function _calculateCurrentDimensions(){var vpt=this.getViewportTransform(),dim=this._getTransformedDimensions(),p=fabric.util.transformPoint(dim,vpt,true);return p.scalarAdd(2*this.padding);}});})();fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Moves an object to the bottom of the stack of drawn objects\n * @return {fabric.Object} thisArg\n * @chainable\n */sendToBack:function sendToBack(){if(this.group){fabric.StaticCanvas.prototype.sendToBack.call(this.group,this);}else{this.canvas.sendToBack(this);}return this;},/**\n * Moves an object to the top of the stack of drawn objects\n * @return {fabric.Object} thisArg\n * @chainable\n */bringToFront:function bringToFront(){if(this.group){fabric.StaticCanvas.prototype.bringToFront.call(this.group,this);}else{this.canvas.bringToFront(this);}return this;},/**\n * Moves an object down in stack of drawn objects\n * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object\n * @return {fabric.Object} thisArg\n * @chainable\n */sendBackwards:function sendBackwards(intersecting){if(this.group){fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,intersecting);}else{this.canvas.sendBackwards(this,intersecting);}return this;},/**\n * Moves an object up in stack of drawn objects\n * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object\n * @return {fabric.Object} thisArg\n * @chainable\n */bringForward:function bringForward(intersecting){if(this.group){fabric.StaticCanvas.prototype.bringForward.call(this.group,this,intersecting);}else{this.canvas.bringForward(this,intersecting);}return this;},/**\n * Moves an object to specified level in stack of drawn objects\n * @param {Number} index New position of object\n * @return {fabric.Object} thisArg\n * @chainable\n */moveTo:function moveTo(index){if(this.group&&this.group.type!=='activeSelection'){fabric.StaticCanvas.prototype.moveTo.call(this.group,this,index);}else{this.canvas.moveTo(this,index);}return this;}});/* _TO_SVG_START_ */(function(){function getSvgColorString(prop,value){if(!value){return prop+': none; ';}else if(value.toLive){return prop+': url(#SVGID_'+value.id+'); ';}else{var color=new fabric.Color(value),str=prop+': '+color.toRgb()+'; ',opacity=color.getAlpha();if(opacity!==1){//change the color in rgb + opacity\nstr+=prop+'-opacity: '+opacity.toString()+'; ';}return str;}}var toFixed=fabric.util.toFixed;fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Returns styles-string for svg-export\n * @param {Boolean} skipShadow a boolean to skip shadow filter output\n * @return {String}\n */getSvgStyles:function getSvgStyles(skipShadow){var fillRule=this.fillRule?this.fillRule:'nonzero',strokeWidth=this.strokeWidth?this.strokeWidth:'0',strokeDashArray=this.strokeDashArray?this.strokeDashArray.join(' '):'none',strokeDashOffset=this.strokeDashOffset?this.strokeDashOffset:'0',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=skipShadow?'':this.getSvgFilter(),fill=getSvgColorString('fill',this.fill),stroke=getSvgColorString('stroke',this.stroke);return[stroke,'stroke-width: ',strokeWidth,'; ','stroke-dasharray: ',strokeDashArray,'; ','stroke-linecap: ',strokeLineCap,'; ','stroke-dashoffset: ',strokeDashOffset,'; ','stroke-linejoin: ',strokeLineJoin,'; ','stroke-miterlimit: ',strokeMiterLimit,'; ',fill,'fill-rule: ',fillRule,'; ','opacity: ',opacity,';',filter,visibility].join('');},/**\n * Returns styles-string for svg-export\n * @param {Object} style the object from which to retrieve style properties\n * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the style.\n * @return {String}\n */getSvgSpanStyles:function getSvgSpanStyles(style,useWhiteSpace){var term='; ';var fontFamily=style.fontFamily?'font-family: '+(style.fontFamily.indexOf('\\'')===-1&&style.fontFamily.indexOf('\"')===-1?'\\''+style.fontFamily+'\\'':style.fontFamily)+term:'';var strokeWidth=style.strokeWidth?'stroke-width: '+style.strokeWidth+term:'',fontFamily=fontFamily,fontSize=style.fontSize?'font-size: '+style.fontSize+'px'+term:'',fontStyle=style.fontStyle?'font-style: '+style.fontStyle+term:'',fontWeight=style.fontWeight?'font-weight: '+style.fontWeight+term:'',fill=style.fill?getSvgColorString('fill',style.fill):'',stroke=style.stroke?getSvgColorString('stroke',style.stroke):'',textDecoration=this.getSvgTextDecoration(style),deltaY=style.deltaY?'baseline-shift: '+-style.deltaY+'; ':'';if(textDecoration){textDecoration='text-decoration: '+textDecoration+term;}return[stroke,strokeWidth,fontFamily,fontSize,fontStyle,fontWeight,textDecoration,fill,deltaY,useWhiteSpace?'white-space: pre; ':''].join('');},/**\n * Returns text-decoration property for svg-export\n * @param {Object} style the object from which to retrieve style properties\n * @return {String}\n */getSvgTextDecoration:function getSvgTextDecoration(style){if('overline'in style||'underline'in style||'linethrough'in style){return(style.overline?'overline ':'')+(style.underline?'underline ':'')+(style.linethrough?'line-through ':'');}return'';},/**\n * Returns filter for svg shadow\n * @return {String}\n */getSvgFilter:function getSvgFilter(){return this.shadow?'filter: url(#SVGID_'+this.shadow.id+');':'';},/**\n * Returns id attribute for svg output\n * @return {String}\n */getSvgCommons:function getSvgCommons(){return[this.id?'id=\"'+this.id+'\" ':'',this.clipPath?'clip-path=\"url(#'+this.clipPath.clipPathId+')\" ':''].join('');},/**\n * Returns transform-string for svg-export\n * @param {Boolean} use the full transform or the single object one.\n * @return {String}\n */getSvgTransform:function getSvgTransform(full,additionalTransform){var transform=full?this.calcTransformMatrix():this.calcOwnMatrix(),svgTransform='transform=\"'+fabric.util.matrixToSVG(transform);return svgTransform+(additionalTransform||'')+this.getSvgTransformMatrix()+'\" ';},/**\n * Returns transform-string for svg-export from the transform matrix of single elements\n * @return {String}\n */getSvgTransformMatrix:function getSvgTransformMatrix(){return this.transformMatrix?' '+fabric.util.matrixToSVG(this.transformMatrix):'';},_setSVGBg:function _setSVGBg(textBgRects){if(this.backgroundColor){var NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS;textBgRects.push('\\t\\t<rect ',this._getFillAttributes(this.backgroundColor),' x=\"',toFixed(-this.width/2,NUM_FRACTION_DIGITS),'\" y=\"',toFixed(-this.height/2,NUM_FRACTION_DIGITS),'\" width=\"',toFixed(this.width,NUM_FRACTION_DIGITS),'\" height=\"',toFixed(this.height,NUM_FRACTION_DIGITS),'\"></rect>\\n');}},/**\n * Returns svg representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toSVG:function toSVG(reviver){return this._createBaseSVGMarkup(this._toSVG(reviver),{reviver:reviver});},/**\n * Returns svg clipPath representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toClipPathSVG:function toClipPathSVG(reviver){return'\\t'+this._createBaseClipPathSVGMarkup(this._toSVG(reviver),{reviver:reviver});},/**\n * @private\n */_createBaseClipPathSVGMarkup:function _createBaseClipPathSVGMarkup(objectMarkup,options){options=options||{};var reviver=options.reviver,additionalTransform=options.additionalTransform||'',commonPieces=[this.getSvgTransform(true,additionalTransform),this.getSvgCommons()].join(''),// insert commons in the markup, style and svgCommons\nindex=objectMarkup.indexOf('COMMON_PARTS');objectMarkup[index]=commonPieces;return reviver?reviver(objectMarkup.join('')):objectMarkup.join('');},/**\n * @private\n */_createBaseSVGMarkup:function _createBaseSVGMarkup(objectMarkup,options){options=options||{};var noStyle=options.noStyle,reviver=options.reviver,styleInfo=noStyle?'':'style=\"'+this.getSvgStyles()+'\" ',shadowInfo=options.withShadow?'style=\"'+this.getSvgFilter()+'\" ':'',clipPath=this.clipPath,vectorEffect=this.strokeUniform?'vector-effect=\"non-scaling-stroke\" ':'',absoluteClipPath=clipPath&&clipPath.absolutePositioned,stroke=this.stroke,fill=this.fill,shadow=this.shadow,commonPieces,markup=[],clipPathMarkup,// insert commons in the markup, style and svgCommons\nindex=objectMarkup.indexOf('COMMON_PARTS'),additionalTransform=options.additionalTransform;if(clipPath){clipPath.clipPathId='CLIPPATH_'+fabric.Object.__uid++;clipPathMarkup='<clipPath id=\"'+clipPath.clipPathId+'\" >\\n'+clipPath.toClipPathSVG(reviver)+'</clipPath>\\n';}if(absoluteClipPath){markup.push('<g ',shadowInfo,this.getSvgCommons(),' >\\n');}markup.push('<g ',this.getSvgTransform(false),!absoluteClipPath?shadowInfo+this.getSvgCommons():'',' >\\n');commonPieces=[styleInfo,vectorEffect,noStyle?'':this.addPaintOrder(),' ',additionalTransform?'transform=\"'+additionalTransform+'\" ':''].join('');objectMarkup[index]=commonPieces;if(fill&&fill.toLive){markup.push(fill.toSVG(this));}if(stroke&&stroke.toLive){markup.push(stroke.toSVG(this));}if(shadow){markup.push(shadow.toSVG(this));}if(clipPath){markup.push(clipPathMarkup);}markup.push(objectMarkup.join(''));markup.push('</g>\\n');absoluteClipPath&&markup.push('</g>\\n');return reviver?reviver(markup.join('')):markup.join('');},addPaintOrder:function addPaintOrder(){return this.paintFirst!=='fill'?' paint-order=\"'+this.paintFirst+'\" ':'';}});})();/* _TO_SVG_END_ */(function(){var extend=fabric.util.object.extend,originalSet='stateProperties';/*\n Depends on `stateProperties`\n */function saveProps(origin,destination,props){var tmpObj={},deep=true;props.forEach(function(prop){tmpObj[prop]=origin[prop];});extend(origin[destination],tmpObj,deep);}function _isEqual(origValue,currentValue,firstPass){if(origValue===currentValue){// if the objects are identical, return\nreturn true;}else if(Array.isArray(origValue)){if(!Array.isArray(currentValue)||origValue.length!==currentValue.length){return false;}for(var i=0,len=origValue.length;i<len;i++){if(!_isEqual(origValue[i],currentValue[i])){return false;}}return true;}else if(origValue&&_typeof(origValue)==='object'){var keys=Object.keys(origValue),key;if(!currentValue||_typeof(currentValue)!=='object'||!firstPass&&keys.length!==Object.keys(currentValue).length){return false;}for(var i=0,len=keys.length;i<len;i++){key=keys[i];// since clipPath is in the statefull cache list and the clipPath objects\n// would be iterated as an object, this would lead to possible infinite recursion\nif(key==='canvas'){continue;}if(!_isEqual(origValue[key],currentValue[key])){return false;}}return true;}}fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Returns true if object state (one of its state properties) was changed\n * @param {String} [propertySet] optional name for the set of property we want to save\n * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called\n */hasStateChanged:function hasStateChanged(propertySet){propertySet=propertySet||originalSet;var dashedPropertySet='_'+propertySet;if(Object.keys(this[dashedPropertySet]).length<this[propertySet].length){return true;}return!_isEqual(this[dashedPropertySet],this,true);},/**\n * Saves state of an object\n * @param {Object} [options] Object with additional `stateProperties` array to include when saving state\n * @return {fabric.Object} thisArg\n */saveState:function saveState(options){var propertySet=options&&options.propertySet||originalSet,destination='_'+propertySet;if(!this[destination]){return this.setupState(options);}saveProps(this,destination,this[propertySet]);if(options&&options.stateProperties){saveProps(this,destination,options.stateProperties);}return this;},/**\n * Setups state of an object\n * @param {Object} [options] Object with additional `stateProperties` array to include when saving state\n * @return {fabric.Object} thisArg\n */setupState:function setupState(options){options=options||{};var propertySet=options.propertySet||originalSet;options.propertySet=propertySet;this['_'+propertySet]={};this.saveState(options);return this;}});})();(function(){var degreesToRadians=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * The object interactivity controls.\n * @private\n */_controlsVisibility:null,/**\n * Determines which corner has been clicked\n * @private\n * @param {Object} pointer The pointer indicating the mouse position\n * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found\n */_findTargetCorner:function _findTargetCorner(pointer){// objects in group, anykind, are not self modificable,\n// must not return an hovered corner.\nif(!this.hasControls||this.group||!this.canvas||this.canvas._activeObject!==this){return false;}var ex=pointer.x,ey=pointer.y,xPoints,lines;this.__corner=0;for(var i in this.oCoords){if(!this.isControlVisible(i)){continue;}if(i==='mtr'&&!this.hasRotatingPoint){continue;}if(this.get('lockUniScaling')&&(i==='mt'||i==='mr'||i==='mb'||i==='ml')){continue;}lines=this._getImageLines(this.oCoords[i].corner);// debugging\n// canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);\n// canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);\n// canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);\n// canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);\n// canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);\n// canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);\n// canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);\n// canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);\nxPoints=this._findCrossPoints({x:ex,y:ey},lines);if(xPoints!==0&&xPoints%2===1){this.__corner=i;return i;}}return false;},/**\n * Sets the coordinates of the draggable boxes in the corners of\n * the image used to scale/rotate it.\n * @private\n */_setCornerCoords:function _setCornerCoords(){var coords=this.oCoords,newTheta=degreesToRadians(45-this.angle),/* Math.sqrt(2 * Math.pow(this.cornerSize, 2)) / 2, */ /* 0.707106 stands for sqrt(2)/2 */cornerHypotenuse=this.cornerSize*0.707106,cosHalfOffset=cornerHypotenuse*fabric.util.cos(newTheta),sinHalfOffset=cornerHypotenuse*fabric.util.sin(newTheta),x,y;for(var point in coords){x=coords[point].x;y=coords[point].y;coords[point].corner={tl:{x:x-sinHalfOffset,y:y-cosHalfOffset},tr:{x:x+cosHalfOffset,y:y-sinHalfOffset},bl:{x:x-cosHalfOffset,y:y+sinHalfOffset},br:{x:x+sinHalfOffset,y:y+cosHalfOffset}};}},/**\n * Draws a colored layer behind the object, inside its selection borders.\n * Requires public options: padding, selectionBackgroundColor\n * this function is called when the context is transformed\n * has checks to be skipped when the object is on a staticCanvas\n * @param {CanvasRenderingContext2D} ctx Context to draw on\n * @return {fabric.Object} thisArg\n * @chainable\n */drawSelectionBackground:function drawSelectionBackground(ctx){if(!this.selectionBackgroundColor||this.canvas&&!this.canvas.interactive||this.canvas&&this.canvas._activeObject!==this){return this;}ctx.save();var center=this.getCenterPoint(),wh=this._calculateCurrentDimensions(),vpt=this.canvas.viewportTransform;ctx.translate(center.x,center.y);ctx.scale(1/vpt[0],1/vpt[3]);ctx.rotate(degreesToRadians(this.angle));ctx.fillStyle=this.selectionBackgroundColor;ctx.fillRect(-wh.x/2,-wh.y/2,wh.x,wh.y);ctx.restore();return this;},/**\n * Draws borders of an object's bounding box.\n * Requires public properties: width, height\n * Requires public options: padding, borderColor\n * @param {CanvasRenderingContext2D} ctx Context to draw on\n * @param {Object} styleOverride object to override the object style\n * @return {fabric.Object} thisArg\n * @chainable\n */drawBorders:function drawBorders(ctx,styleOverride){styleOverride=styleOverride||{};var wh=this._calculateCurrentDimensions(),strokeWidth=1/this.borderScaleFactor,width=wh.x+strokeWidth,height=wh.y+strokeWidth,drawRotatingPoint=typeof styleOverride.hasRotatingPoint!=='undefined'?styleOverride.hasRotatingPoint:this.hasRotatingPoint,hasControls=typeof styleOverride.hasControls!=='undefined'?styleOverride.hasControls:this.hasControls,rotatingPointOffset=typeof styleOverride.rotatingPointOffset!=='undefined'?styleOverride.rotatingPointOffset:this.rotatingPointOffset;ctx.save();ctx.strokeStyle=styleOverride.borderColor||this.borderColor;this._setLineDash(ctx,styleOverride.borderDashArray||this.borderDashArray,null);ctx.strokeRect(-width/2,-height/2,width,height);if(drawRotatingPoint&&this.isControlVisible('mtr')&&hasControls){var rotateHeight=-height/2;ctx.beginPath();ctx.moveTo(0,rotateHeight);ctx.lineTo(0,rotateHeight-rotatingPointOffset);ctx.stroke();}ctx.restore();return this;},/**\n * Draws borders of an object's bounding box when it is inside a group.\n * Requires public properties: width, height\n * Requires public options: padding, borderColor\n * @param {CanvasRenderingContext2D} ctx Context to draw on\n * @param {object} options object representing current object parameters\n * @param {Object} styleOverride object to override the object style\n * @return {fabric.Object} thisArg\n * @chainable\n */drawBordersInGroup:function drawBordersInGroup(ctx,options,styleOverride){styleOverride=styleOverride||{};var p=this._getNonTransformedDimensions(),matrix=fabric.util.composeMatrix({scaleX:options.scaleX,scaleY:options.scaleY,skewX:options.skewX}),wh=fabric.util.transformPoint(p,matrix),strokeWidth=1/this.borderScaleFactor,width=wh.x+strokeWidth,height=wh.y+strokeWidth;ctx.save();this._setLineDash(ctx,styleOverride.borderDashArray||this.borderDashArray,null);ctx.strokeStyle=styleOverride.borderColor||this.borderColor;ctx.strokeRect(-width/2,-height/2,width,height);ctx.restore();return this;},/**\n * Draws corners of an object's bounding box.\n * Requires public properties: width, height\n * Requires public options: cornerSize, padding\n * @param {CanvasRenderingContext2D} ctx Context to draw on\n * @param {Object} styleOverride object to override the object style\n * @return {fabric.Object} thisArg\n * @chainable\n */drawControls:function drawControls(ctx,styleOverride){styleOverride=styleOverride||{};var wh=this._calculateCurrentDimensions(),width=wh.x,height=wh.y,scaleOffset=styleOverride.cornerSize||this.cornerSize,left=-(width+scaleOffset)/2,top=-(height+scaleOffset)/2,transparentCorners=typeof styleOverride.transparentCorners!=='undefined'?styleOverride.transparentCorners:this.transparentCorners,hasRotatingPoint=typeof styleOverride.hasRotatingPoint!=='undefined'?styleOverride.hasRotatingPoint:this.hasRotatingPoint,methodName=transparentCorners?'stroke':'fill';ctx.save();ctx.strokeStyle=ctx.fillStyle=styleOverride.cornerColor||this.cornerColor;if(!this.transparentCorners){ctx.strokeStyle=styleOverride.cornerStrokeColor||this.cornerStrokeColor;}this._setLineDash(ctx,styleOverride.cornerDashArray||this.cornerDashArray,null);// top-left\nthis._drawControl('tl',ctx,methodName,left,top,styleOverride);// top-right\nthis._drawControl('tr',ctx,methodName,left+width,top,styleOverride);// bottom-left\nthis._drawControl('bl',ctx,methodName,left,top+height,styleOverride);// bottom-right\nthis._drawControl('br',ctx,methodName,left+width,top+height,styleOverride);if(!this.get('lockUniScaling')){// middle-top\nthis._drawControl('mt',ctx,methodName,left+width/2,top,styleOverride);// middle-bottom\nthis._drawControl('mb',ctx,methodName,left+width/2,top+height,styleOverride);// middle-right\nthis._drawControl('mr',ctx,methodName,left+width,top+height/2,styleOverride);// middle-left\nthis._drawControl('ml',ctx,methodName,left,top+height/2,styleOverride);}// middle-top-rotate\nif(hasRotatingPoint){this._drawControl('mtr',ctx,methodName,left+width/2,top-this.rotatingPointOffset,styleOverride);}ctx.restore();return this;},/**\n * @private\n */_drawControl:function _drawControl(control,ctx,methodName,left,top,styleOverride){styleOverride=styleOverride||{};if(!this.isControlVisible(control)){return;}var size=this.cornerSize,stroke=!this.transparentCorners&&this.cornerStrokeColor;switch(styleOverride.cornerStyle||this.cornerStyle){case'circle':ctx.beginPath();ctx.arc(left+size/2,top+size/2,size/2,0,2*Math.PI,false);ctx[methodName]();if(stroke){ctx.stroke();}break;default:this.transparentCorners||ctx.clearRect(left,top,size,size);ctx[methodName+'Rect'](left,top,size,size);if(stroke){ctx.strokeRect(left,top,size,size);}}},/**\n * Returns true if the specified control is visible, false otherwise.\n * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.\n * @returns {Boolean} true if the specified control is visible, false otherwise\n */isControlVisible:function isControlVisible(controlName){return this._getControlsVisibility()[controlName];},/**\n * Sets the visibility of the specified control.\n * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.\n * @param {Boolean} visible true to set the specified control visible, false otherwise\n * @return {fabric.Object} thisArg\n * @chainable\n */setControlVisible:function setControlVisible(controlName,visible){this._getControlsVisibility()[controlName]=visible;return this;},/**\n * Sets the visibility state of object controls.\n * @param {Object} [options] Options object\n * @param {Boolean} [options.bl] true to enable the bottom-left control, false to disable it\n * @param {Boolean} [options.br] true to enable the bottom-right control, false to disable it\n * @param {Boolean} [options.mb] true to enable the middle-bottom control, false to disable it\n * @param {Boolean} [options.ml] true to enable the middle-left control, false to disable it\n * @param {Boolean} [options.mr] true to enable the middle-right control, false to disable it\n * @param {Boolean} [options.mt] true to enable the middle-top control, false to disable it\n * @param {Boolean} [options.tl] true to enable the top-left control, false to disable it\n * @param {Boolean} [options.tr] true to enable the top-right control, false to disable it\n * @param {Boolean} [options.mtr] true to enable the middle-top-rotate control, false to disable it\n * @return {fabric.Object} thisArg\n * @chainable\n */setControlsVisibility:function setControlsVisibility(options){options||(options={});for(var p in options){this.setControlVisible(p,options[p]);}return this;},/**\n * Returns the instance of the control visibility set for this object.\n * @private\n * @returns {Object}\n */_getControlsVisibility:function _getControlsVisibility(){if(!this._controlsVisibility){this._controlsVisibility={tl:true,tr:true,br:true,bl:true,ml:true,mt:true,mr:true,mb:true,mtr:true};}return this._controlsVisibility;},/**\n * This callback function is called every time _discardActiveObject or _setActiveObject\n * try to to deselect this object. If the function returns true, the process is cancelled\n * @param {Object} [options] options sent from the upper functions\n * @param {Event} [options.e] event if the process is generated by an event\n */onDeselect:function onDeselect(){// implemented by sub-classes, as needed.\n},/**\n * This callback function is called every time _discardActiveObject or _setActiveObject\n * try to to select this object. If the function returns true, the process is cancelled\n * @param {Object} [options] options sent from the upper functions\n * @param {Event} [options.e] event if the process is generated by an event\n */onSelect:function onSelect(){// implemented by sub-classes, as needed.\n}});})();fabric.util.object.extend(fabric.StaticCanvas.prototype,/** @lends fabric.StaticCanvas.prototype */{/**\n * Animation duration (in ms) for fx* methods\n * @type Number\n * @default\n */FX_DURATION:500,/**\n * Centers object horizontally with animation.\n * @param {fabric.Object} object Object to center\n * @param {Object} [callbacks] Callbacks object with optional \"onComplete\" and/or \"onChange\" properties\n * @param {Function} [callbacks.onComplete] Invoked on completion\n * @param {Function} [callbacks.onChange] Invoked on every step of animation\n * @return {fabric.Canvas} thisArg\n * @chainable\n */fxCenterObjectH:function fxCenterObjectH(object,callbacks){callbacks=callbacks||{};var empty=function empty(){},_onComplete=callbacks.onComplete||empty,_onChange=callbacks.onChange||empty,_this=this;fabric.util.animate({startValue:object.left,endValue:this.getCenter().left,duration:this.FX_DURATION,onChange:function onChange(value){object.set('left',value);_this.requestRenderAll();_onChange();},onComplete:function onComplete(){object.setCoords();_onComplete();}});return this;},/**\n * Centers object vertically with animation.\n * @param {fabric.Object} object Object to center\n * @param {Object} [callbacks] Callbacks object with optional \"onComplete\" and/or \"onChange\" properties\n * @param {Function} [callbacks.onComplete] Invoked on completion\n * @param {Function} [callbacks.onChange] Invoked on every step of animation\n * @return {fabric.Canvas} thisArg\n * @chainable\n */fxCenterObjectV:function fxCenterObjectV(object,callbacks){callbacks=callbacks||{};var empty=function empty(){},_onComplete2=callbacks.onComplete||empty,_onChange2=callbacks.onChange||empty,_this=this;fabric.util.animate({startValue:object.top,endValue:this.getCenter().top,duration:this.FX_DURATION,onChange:function onChange(value){object.set('top',value);_this.requestRenderAll();_onChange2();},onComplete:function onComplete(){object.setCoords();_onComplete2();}});return this;},/**\n * Same as `fabric.Canvas#remove` but animated\n * @param {fabric.Object} object Object to remove\n * @param {Object} [callbacks] Callbacks object with optional \"onComplete\" and/or \"onChange\" properties\n * @param {Function} [callbacks.onComplete] Invoked on completion\n * @param {Function} [callbacks.onChange] Invoked on every step of animation\n * @return {fabric.Canvas} thisArg\n * @chainable\n */fxRemove:function fxRemove(object,callbacks){callbacks=callbacks||{};var empty=function empty(){},_onComplete3=callbacks.onComplete||empty,_onChange3=callbacks.onChange||empty,_this=this;fabric.util.animate({startValue:object.opacity,endValue:0,duration:this.FX_DURATION,onChange:function onChange(value){object.set('opacity',value);_this.requestRenderAll();_onChange3();},onComplete:function onComplete(){_this.remove(object);_onComplete3();}});return this;}});fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * Animates object's properties\n * @param {String|Object} property Property to animate (if string) or properties to animate (if object)\n * @param {Number|Object} value Value to animate property to (if string was given first) or options object\n * @return {fabric.Object} thisArg\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation}\n * @chainable\n *\n * As object — multiple properties\n *\n * object.animate({ left: ..., top: ... });\n * object.animate({ left: ..., top: ... }, { duration: ... });\n *\n * As string — one property\n *\n * object.animate('left', ...);\n * object.animate('left', { duration: ... });\n *\n */animate:function animate(){if(arguments[0]&&_typeof(arguments[0])==='object'){var propsToAnimate=[],prop,skipCallbacks;for(prop in arguments[0]){propsToAnimate.push(prop);}for(var i=0,len=propsToAnimate.length;i<len;i++){prop=propsToAnimate[i];skipCallbacks=i!==len-1;this._animate(prop,arguments[0][prop],arguments[1],skipCallbacks);}}else{this._animate.apply(this,arguments);}return this;},/**\n * @private\n * @param {String} property Property to animate\n * @param {String} to Value to animate to\n * @param {Object} [options] Options object\n * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked\n */_animate:function _animate(property,to,options,skipCallbacks){var _this=this,propPair;to=to.toString();if(!options){options={};}else{options=fabric.util.object.clone(options);}if(~property.indexOf('.')){propPair=property.split('.');}var currentValue=propPair?this.get(propPair[0])[propPair[1]]:this.get(property);if(!('from'in options)){options.from=currentValue;}if(~to.indexOf('=')){to=currentValue+parseFloat(to.replace('=',''));}else{to=parseFloat(to);}fabric.util.animate({startValue:options.from,endValue:to,byValue:options.by,easing:options.easing,duration:options.duration,abort:options.abort&&function(){return options.abort.call(_this);},onChange:function onChange(value,valueProgress,timeProgress){if(propPair){_this[propPair[0]][propPair[1]]=value;}else{_this.set(property,value);}if(skipCallbacks){return;}options.onChange&&options.onChange(value,valueProgress,timeProgress);},onComplete:function onComplete(value,valueProgress,timeProgress){if(skipCallbacks){return;}_this.setCoords();options.onComplete&&options.onComplete(value,valueProgress,timeProgress);}});}});(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend,clone=fabric.util.object.clone,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;}/**\n * Line class\n * @class fabric.Line\n * @extends fabric.Object\n * @see {@link fabric.Line#initialize} for constructor definition\n */fabric.Line=fabric.util.createClass(fabric.Object,/** @lends fabric.Line.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'line',/**\n * x value or first line edge\n * @type Number\n * @default\n */x1:0,/**\n * y value or first line edge\n * @type Number\n * @default\n */y1:0,/**\n * x value or second line edge\n * @type Number\n * @default\n */x2:0,/**\n * y value or second line edge\n * @type Number\n * @default\n */y2:0,cacheProperties:fabric.Object.prototype.cacheProperties.concat('x1','x2','y1','y2'),/**\n * Constructor\n * @param {Array} [points] Array of points\n * @param {Object} [options] Options object\n * @return {fabric.Line} thisArg\n */initialize:function initialize(points,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);},/**\n * @private\n * @param {Object} [options] Options\n */_setWidthHeight:function _setWidthHeight(options){options||(options={});this.width=Math.abs(this.x2-this.x1);this.height=Math.abs(this.y2-this.y1);this.left='left'in options?options.left:this._getLeftToOriginX();this.top='top'in options?options.top:this._getTopToOriginY();},/**\n * @private\n * @param {String} key\n * @param {*} value\n */_set:function _set(key,value){this.callSuper('_set',key,value);if(typeof coordProps[key]!=='undefined'){this._setWidthHeight();}return this;},/**\n * @private\n * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.\n */_getLeftToOriginX:makeEdgeToOriginGetter({// property names\norigin:'originX',axis1:'x1',axis2:'x2',dimension:'width'},{// possible values of origin\nnearest:'left',center:'center',farthest:'right'}),/**\n * @private\n * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.\n */_getTopToOriginY:makeEdgeToOriginGetter({// property names\norigin:'originY',axis1:'y1',axis2:'y2',dimension:'height'},{// possible values of origin\nnearest:'top',center:'center',farthest:'bottom'}),/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){ctx.beginPath();if(!this.strokeDashArray||this.strokeDashArray&&supportsLineDash){// move from center (of virtual box) to its left/top corner\n// we can't assume x1, y1 is top left and x2, y2 is bottom right\nvar p=this.calcLinePoints();ctx.moveTo(p.x1,p.y1);ctx.lineTo(p.x2,p.y2);}ctx.lineWidth=this.strokeWidth;// TODO: test this\n// make sure setting \"fill\" changes color of a line\n// (by copying fillStyle to strokeStyle, since line is stroked, not filled)\nvar origStrokeStyle=ctx.strokeStyle;ctx.strokeStyle=this.stroke||ctx.fillStyle;this.stroke&&this._renderStroke(ctx);ctx.strokeStyle=origStrokeStyle;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(ctx){var p=this.calcLinePoints();ctx.beginPath();fabric.util.drawDashedLine(ctx,p.x1,p.y1,p.x2,p.y2,this.strokeDashArray);ctx.closePath();},/**\n * This function is an helper for svg import. it returns the center of the object in the svg\n * untransformed coordinates\n * @private\n * @return {Object} center point from element coordinates\n */_findCenterFromElement:function _findCenterFromElement(){return{x:(this.x1+this.x2)/2,y:(this.y1+this.y2)/2};},/**\n * Returns object representation of an instance\n * @methd toObject\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return extend(this.callSuper('toObject',propertiesToInclude),this.calcLinePoints());},/*\n * Calculate object dimensions from its properties\n * @private\n */_getNonTransformedDimensions:function _getNonTransformedDimensions(){var dim=this.callSuper('_getNonTransformedDimensions');if(this.strokeLineCap==='butt'){if(this.width===0){dim.y-=this.strokeWidth;}if(this.height===0){dim.x-=this.strokeWidth;}}return dim;},/**\n * Recalculates line points given width and height\n * @private\n */calcLinePoints:function calcLinePoints(){var xMult=this.x1<=this.x2?-1:1,yMult=this.y1<=this.y2?-1:1,x1=xMult*this.width*0.5,y1=yMult*this.height*0.5,x2=xMult*this.width*-0.5,y2=yMult*this.height*-0.5;return{x1:x1,x2:x2,y1:y1,y2:y2};},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var p=this.calcLinePoints();return['<line ','COMMON_PARTS','x1=\"',p.x1,'\" y1=\"',p.y1,'\" x2=\"',p.x2,'\" y2=\"',p.y2,'\" />\\n'];}/* _TO_SVG_END_ */});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement})\n * @static\n * @memberOf fabric.Line\n * @see http://www.w3.org/TR/SVG/shapes.html#LineElement\n */fabric.Line.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' '));/**\n * Returns fabric.Line instance from an SVG element\n * @static\n * @memberOf fabric.Line\n * @param {SVGElement} element Element to parse\n * @param {Object} [options] Options object\n * @param {Function} [callback] callback function invoked after parsing\n */fabric.Line.fromElement=function(element,callback,options){options=options||{};var parsedAttributes=fabric.parseAttributes(element,fabric.Line.ATTRIBUTE_NAMES),points=[parsedAttributes.x1||0,parsedAttributes.y1||0,parsedAttributes.x2||0,parsedAttributes.y2||0];callback(new fabric.Line(points,extend(parsedAttributes,options)));};/* _FROM_SVG_END_ */ /**\n * Returns fabric.Line instance from an object representation\n * @static\n * @memberOf fabric.Line\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] invoked with new instance as first argument\n */fabric.Line.fromObject=function(object,callback){function _callback(instance){delete instance.points;callback&&callback(instance);};var options=clone(object,true);options.points=[object.x1,object.y1,object.x2,object.y2];fabric.Object._fromObject('Line',options,_callback,'points');};/**\n * Produces a function that calculates distance from canvas edge to Line origin.\n */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));}};}})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),pi=Math.PI;if(fabric.Circle){fabric.warn('fabric.Circle is already defined.');return;}/**\n * Circle class\n * @class fabric.Circle\n * @extends fabric.Object\n * @see {@link fabric.Circle#initialize} for constructor definition\n */fabric.Circle=fabric.util.createClass(fabric.Object,/** @lends fabric.Circle.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'circle',/**\n * Radius of this circle\n * @type Number\n * @default\n */radius:0,/**\n * Start angle of the circle, moving clockwise\n * deprectated type, this should be in degree, this was an oversight.\n * probably will change to degrees in next major version\n * @type Number\n * @default 0\n */startAngle:0,/**\n * End angle of the circle\n * deprectated type, this should be in degree, this was an oversight.\n * probably will change to degrees in next major version\n * @type Number\n * @default 2Pi\n */endAngle:pi*2,cacheProperties:fabric.Object.prototype.cacheProperties.concat('radius','startAngle','endAngle'),/**\n * @private\n * @param {String} key\n * @param {*} value\n * @return {fabric.Circle} thisArg\n */_set:function _set(key,value){this.callSuper('_set',key,value);if(key==='radius'){this.setRadius(value);}return this;},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return this.callSuper('toObject',['radius','startAngle','endAngle'].concat(propertiesToInclude));},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var svgString,x=0,y=0,angle=(this.endAngle-this.startAngle)%(2*pi);if(angle===0){svgString=['<circle ','COMMON_PARTS','cx=\"'+x+'\" cy=\"'+y+'\" ','r=\"',this.radius,'\" />\\n'];}else{var startX=fabric.util.cos(this.startAngle)*this.radius,startY=fabric.util.sin(this.startAngle)*this.radius,endX=fabric.util.cos(this.endAngle)*this.radius,endY=fabric.util.sin(this.endAngle)*this.radius,largeFlag=angle>pi?'1':'0';svgString=['<path d=\"M '+startX+' '+startY,' A '+this.radius+' '+this.radius,' 0 ',+largeFlag+' 1',' '+endX+' '+endY,'\" ','COMMON_PARTS',' />\\n'];}return svgString;},/* _TO_SVG_END_ */ /**\n * @private\n * @param {CanvasRenderingContext2D} ctx context to render on\n */_render:function _render(ctx){ctx.beginPath();ctx.arc(0,0,this.radius,this.startAngle,this.endAngle,false);this._renderPaintInOrder(ctx);},/**\n * Returns horizontal radius of an object (according to how an object is scaled)\n * @return {Number}\n */getRadiusX:function getRadiusX(){return this.get('radius')*this.get('scaleX');},/**\n * Returns vertical radius of an object (according to how an object is scaled)\n * @return {Number}\n */getRadiusY:function getRadiusY(){return this.get('radius')*this.get('scaleY');},/**\n * Sets radius of an object (and updates width accordingly)\n * @return {fabric.Circle} thisArg\n */setRadius:function setRadius(value){this.radius=value;return this.set('width',value*2).set('height',value*2);}});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})\n * @static\n * @memberOf fabric.Circle\n * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement\n */fabric.Circle.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' '));/**\n * Returns {@link fabric.Circle} instance from an SVG element\n * @static\n * @memberOf fabric.Circle\n * @param {SVGElement} element Element to parse\n * @param {Function} [callback] Options callback invoked after parsing is finished\n * @param {Object} [options] Options object\n * @throws {Error} If value of `r` attribute is missing or invalid\n */fabric.Circle.fromElement=function(element,callback){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.radius;parsedAttributes.top=(parsedAttributes.top||0)-parsedAttributes.radius;callback(new fabric.Circle(parsedAttributes));};/**\n * @private\n */function isValidRadius(attributes){return'radius'in attributes&&attributes.radius>=0;}/* _FROM_SVG_END_ */ /**\n * Returns {@link fabric.Circle} instance from an object representation\n * @static\n * @memberOf fabric.Circle\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] invoked with new instance as first argument\n * @return {Object} Instance of fabric.Circle\n */fabric.Circle.fromObject=function(object,callback){return fabric.Object._fromObject('Circle',object,callback);};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={});if(fabric.Triangle){fabric.warn('fabric.Triangle is already defined');return;}/**\n * Triangle class\n * @class fabric.Triangle\n * @extends fabric.Object\n * @return {fabric.Triangle} thisArg\n * @see {@link fabric.Triangle#initialize} for constructor definition\n */fabric.Triangle=fabric.util.createClass(fabric.Object,/** @lends fabric.Triangle.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'triangle',/**\n * Width is set to 100 to compensate the old initialize code that was setting it to 100\n * @type Number\n * @default\n */width:100,/**\n * Height is set to 100 to compensate the old initialize code that was setting it to 100\n * @type Number\n * @default\n */height:100,/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(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._renderPaintInOrder(ctx);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(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_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var widthBy2=this.width/2,heightBy2=this.height/2,points=[-widthBy2+' '+heightBy2,'0 '+-heightBy2,widthBy2+' '+heightBy2].join(',');return['<polygon ','COMMON_PARTS','points=\"',points,'\" />'];}/* _TO_SVG_END_ */});/**\n * Returns {@link fabric.Triangle} instance from an object representation\n * @static\n * @memberOf fabric.Triangle\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] invoked with new instance as first argument\n */fabric.Triangle.fromObject=function(object,callback){return fabric.Object._fromObject('Triangle',object,callback);};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),piBy2=Math.PI*2;if(fabric.Ellipse){fabric.warn('fabric.Ellipse is already defined.');return;}/**\n * Ellipse class\n * @class fabric.Ellipse\n * @extends fabric.Object\n * @return {fabric.Ellipse} thisArg\n * @see {@link fabric.Ellipse#initialize} for constructor definition\n */fabric.Ellipse=fabric.util.createClass(fabric.Object,/** @lends fabric.Ellipse.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'ellipse',/**\n * Horizontal radius\n * @type Number\n * @default\n */rx:0,/**\n * Vertical radius\n * @type Number\n * @default\n */ry:0,cacheProperties:fabric.Object.prototype.cacheProperties.concat('rx','ry'),/**\n * Constructor\n * @param {Object} [options] Options object\n * @return {fabric.Ellipse} thisArg\n */initialize:function initialize(options){this.callSuper('initialize',options);this.set('rx',options&&options.rx||0);this.set('ry',options&&options.ry||0);},/**\n * @private\n * @param {String} key\n * @param {*} value\n * @return {fabric.Ellipse} thisArg\n */_set:function _set(key,value){this.callSuper('_set',key,value);switch(key){case'rx':this.rx=value;this.set('width',value*2);break;case'ry':this.ry=value;this.set('height',value*2);break;}return this;},/**\n * Returns horizontal radius of an object (according to how an object is scaled)\n * @return {Number}\n */getRx:function getRx(){return this.get('rx')*this.get('scaleX');},/**\n * Returns Vertical radius of an object (according to how an object is scaled)\n * @return {Number}\n */getRy:function getRy(){return this.get('ry')*this.get('scaleY');},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return this.callSuper('toObject',['rx','ry'].concat(propertiesToInclude));},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){return['<ellipse ','COMMON_PARTS','cx=\"0\" cy=\"0\" ','rx=\"',this.rx,'\" ry=\"',this.ry,'\" />\\n'];},/* _TO_SVG_END_ */ /**\n * @private\n * @param {CanvasRenderingContext2D} ctx context to render on\n */_render:function _render(ctx){ctx.beginPath();ctx.save();ctx.transform(1,0,0,this.ry/this.rx,0,0);ctx.arc(0,0,this.rx,0,piBy2,false);ctx.restore();this._renderPaintInOrder(ctx);}});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement})\n * @static\n * @memberOf fabric.Ellipse\n * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement\n */fabric.Ellipse.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' '));/**\n * Returns {@link fabric.Ellipse} instance from an SVG element\n * @static\n * @memberOf fabric.Ellipse\n * @param {SVGElement} element Element to parse\n * @param {Function} [callback] Options callback invoked after parsing is finished\n * @return {fabric.Ellipse}\n */fabric.Ellipse.fromElement=function(element,callback){var parsedAttributes=fabric.parseAttributes(element,fabric.Ellipse.ATTRIBUTE_NAMES);parsedAttributes.left=(parsedAttributes.left||0)-parsedAttributes.rx;parsedAttributes.top=(parsedAttributes.top||0)-parsedAttributes.ry;callback(new fabric.Ellipse(parsedAttributes));};/* _FROM_SVG_END_ */ /**\n * Returns {@link fabric.Ellipse} instance from an object representation\n * @static\n * @memberOf fabric.Ellipse\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] invoked with new instance as first argument\n * @return {fabric.Ellipse}\n */fabric.Ellipse.fromObject=function(object,callback){return fabric.Object._fromObject('Ellipse',object,callback);};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend;if(fabric.Rect){fabric.warn('fabric.Rect is already defined');return;}/**\n * Rectangle class\n * @class fabric.Rect\n * @extends fabric.Object\n * @return {fabric.Rect} thisArg\n * @see {@link fabric.Rect#initialize} for constructor definition\n */fabric.Rect=fabric.util.createClass(fabric.Object,/** @lends fabric.Rect.prototype */{/**\n * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})\n * as well as for history (undo/redo) purposes\n * @type Array\n */stateProperties:fabric.Object.prototype.stateProperties.concat('rx','ry'),/**\n * Type of an object\n * @type String\n * @default\n */type:'rect',/**\n * Horizontal border radius\n * @type Number\n * @default\n */rx:0,/**\n * Vertical border radius\n * @type Number\n * @default\n */ry:0,cacheProperties:fabric.Object.prototype.cacheProperties.concat('rx','ry'),/**\n * Constructor\n * @param {Object} [options] Options object\n * @return {Object} thisArg\n */initialize:function initialize(options){this.callSuper('initialize',options);this._initRxRy();},/**\n * Initializes rx/ry attributes\n * @private\n */_initRxRy:function _initRxRy(){if(this.rx&&!this.ry){this.ry=this.rx;}else if(this.ry&&!this.rx){this.rx=this.ry;}},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){// 1x1 case (used in spray brush) optimization was removed because\n// with caching and higher zoom level this makes more damage than help\nvar 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=-this.width/2,y=-this.height/2,isRounded=rx!==0||ry!==0,/* \"magic number\" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */k=1-0.5522847498;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._renderPaintInOrder(ctx);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(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();},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return this.callSuper('toObject',['rx','ry'].concat(propertiesToInclude));},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var x=-this.width/2,y=-this.height/2;return['<rect ','COMMON_PARTS','x=\"',x,'\" y=\"',y,'\" rx=\"',this.rx,'\" ry=\"',this.ry,'\" width=\"',this.width,'\" height=\"',this.height,'\" />\\n'];}/* _TO_SVG_END_ */});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)\n * @static\n * @memberOf fabric.Rect\n * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement\n */fabric.Rect.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));/**\n * Returns {@link fabric.Rect} instance from an SVG element\n * @static\n * @memberOf fabric.Rect\n * @param {SVGElement} element Element to parse\n * @param {Function} callback callback function invoked after parsing\n * @param {Object} [options] Options object\n */fabric.Rect.fromElement=function(element,callback,options){if(!element){return callback(null);}options=options||{};var parsedAttributes=fabric.parseAttributes(element,fabric.Rect.ATTRIBUTE_NAMES);parsedAttributes.left=parsedAttributes.left||0;parsedAttributes.top=parsedAttributes.top||0;parsedAttributes.height=parsedAttributes.height||0;parsedAttributes.width=parsedAttributes.width||0;var rect=new fabric.Rect(extend(options?fabric.util.object.clone(options):{},parsedAttributes));rect.visible=rect.visible&&rect.width>0&&rect.height>0;callback(rect);};/* _FROM_SVG_END_ */ /**\n * Returns {@link fabric.Rect} instance from an object representation\n * @static\n * @memberOf fabric.Rect\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] Callback to invoke when an fabric.Rect instance is created\n */fabric.Rect.fromObject=function(object,callback){return fabric.Object._fromObject('Rect',object,callback);};})( true?exports:undefined);(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.Polyline){fabric.warn('fabric.Polyline is already defined');return;}/**\n * Polyline class\n * @class fabric.Polyline\n * @extends fabric.Object\n * @see {@link fabric.Polyline#initialize} for constructor definition\n */fabric.Polyline=fabric.util.createClass(fabric.Object,/** @lends fabric.Polyline.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'polyline',/**\n * Points array\n * @type Array\n * @default\n */points:null,cacheProperties:fabric.Object.prototype.cacheProperties.concat('points'),/**\n * Constructor\n * @param {Array} points Array of points (where each point is an object with x and y)\n * @param {Object} [options] Options object\n * @return {fabric.Polyline} thisArg\n * @example\n * var poly = new fabric.Polyline([\n * { x: 10, y: 10 },\n * { x: 50, y: 30 },\n * { x: 40, y: 70 },\n * { x: 60, y: 50 },\n * { x: 100, y: 150 },\n * { x: 40, y: 100 }\n * ], {\n * stroke: 'red',\n * left: 100,\n * top: 100\n * });\n */initialize:function initialize(points,options){options=options||{};this.points=points||[];this.callSuper('initialize',options);this._setPositionDimensions(options);},_setPositionDimensions:function _setPositionDimensions(options){var calcDim=this._calcDimensions(options),correctLeftTop;this.width=calcDim.width;this.height=calcDim.height;if(!options.fromSVG){correctLeftTop=this.translateToGivenOrigin({x:calcDim.left-this.strokeWidth/2,y:calcDim.top-this.strokeWidth/2},'left','top',this.originX,this.originY);}if(typeof options.left==='undefined'){this.left=options.fromSVG?calcDim.left:correctLeftTop.x;}if(typeof options.top==='undefined'){this.top=options.fromSVG?calcDim.top:correctLeftTop.y;}this.pathOffset={x:calcDim.left+this.width/2,y:calcDim.top+this.height/2};},/**\n * Calculate the polygon min and max point from points array,\n * returning an object with left, top, widht, height to measure the\n * polygon size\n * @return {Object} object.left X coordinate of the polygon leftmost point\n * @return {Object} object.top Y coordinate of the polygon topmost point\n * @return {Object} object.width distance between X coordinates of the polygon leftmost and rightmost point\n * @return {Object} object.height distance between Y coordinates of the polygon topmost and bottommost point\n * @private\n */_calcDimensions:function _calcDimensions(){var points=this.points,minX=min(points,'x')||0,minY=min(points,'y')||0,maxX=max(points,'x')||0,maxY=max(points,'y')||0,width=maxX-minX,height=maxY-minY;return{left:minX,top:minY,width:width,height:height};},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */toObject:function toObject(propertiesToInclude){return extend(this.callSuper('toObject',propertiesToInclude),{points:this.points.concat()});},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var points=[],diffX=this.pathOffset.x,diffY=this.pathOffset.y,NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS;for(var i=0,len=this.points.length;i<len;i++){points.push(toFixed(this.points[i].x-diffX,NUM_FRACTION_DIGITS),',',toFixed(this.points[i].y-diffY,NUM_FRACTION_DIGITS),' ');}return['<'+this.type+' ','COMMON_PARTS','points=\"',points.join(''),'\" />\\n'];},/* _TO_SVG_END_ */ /**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */commonRender:function commonRender(ctx){var point,len=this.points.length,x=this.pathOffset.x,y=this.pathOffset.y;if(!len||isNaN(this.points[len-1].y)){// do not draw if no points or odd points\n// NaN comes from parseFloat of a empty string in parser\nreturn false;}ctx.beginPath();ctx.moveTo(this.points[0].x-x,this.points[0].y-y);for(var i=0;i<len;i++){point=this.points[i];ctx.lineTo(point.x-x,point.y-y);}return true;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){if(!this.commonRender(ctx)){return;}this._renderPaintInOrder(ctx);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(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);}},/**\n * Returns complexity of an instance\n * @return {Number} complexity of this instance\n */complexity:function complexity(){return this.get('points').length;}});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement})\n * @static\n * @memberOf fabric.Polyline\n * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement\n */fabric.Polyline.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat();/**\n * Returns fabric.Polyline instance from an SVG element\n * @static\n * @memberOf fabric.Polyline\n * @param {SVGElement} element Element to parser\n * @param {Function} callback callback function invoked after parsing\n * @param {Object} [options] Options object\n */fabric.Polyline.fromElementGenerator=function(_class){return function(element,callback,options){if(!element){return callback(null);}options||(options={});var points=fabric.parsePointsAttribute(element.getAttribute('points')),parsedAttributes=fabric.parseAttributes(element,fabric[_class].ATTRIBUTE_NAMES);parsedAttributes.fromSVG=true;callback(new fabric[_class](points,extend(parsedAttributes,options)));};};fabric.Polyline.fromElement=fabric.Polyline.fromElementGenerator('Polyline');/* _FROM_SVG_END_ */ /**\n * Returns fabric.Polyline instance from an object representation\n * @static\n * @memberOf fabric.Polyline\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created\n */fabric.Polyline.fromObject=function(object,callback){return fabric.Object._fromObject('Polyline',object,callback,'points');};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={});if(fabric.Polygon){fabric.warn('fabric.Polygon is already defined');return;}/**\n * Polygon class\n * @class fabric.Polygon\n * @extends fabric.Polyline\n * @see {@link fabric.Polygon#initialize} for constructor definition\n */fabric.Polygon=fabric.util.createClass(fabric.Polyline,/** @lends fabric.Polygon.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'polygon',/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){if(!this.commonRender(ctx)){return;}ctx.closePath();this._renderPaintInOrder(ctx);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(ctx){this.callSuper('_renderDashedStroke',ctx);ctx.closePath();}});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`)\n * @static\n * @memberOf fabric.Polygon\n * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement\n */fabric.Polygon.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat();/**\n * Returns {@link fabric.Polygon} instance from an SVG element\n * @static\n * @memberOf fabric.Polygon\n * @param {SVGElement} element Element to parse\n * @param {Function} callback callback function invoked after parsing\n * @param {Object} [options] Options object\n */fabric.Polygon.fromElement=fabric.Polyline.fromElementGenerator('Polygon');/* _FROM_SVG_END_ */ /**\n * Returns fabric.Polygon instance from an object representation\n * @static\n * @memberOf fabric.Polygon\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created\n */fabric.Polygon.fromObject=function(object,callback){return fabric.Object._fromObject('Polygon',object,callback,'points');};})( true?exports:undefined);(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,toFixed=fabric.util.toFixed,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;}/**\n * Path class\n * @class fabric.Path\n * @extends fabric.Object\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#path_and_pathgroup}\n * @see {@link fabric.Path#initialize} for constructor definition\n */fabric.Path=fabric.util.createClass(fabric.Object,/** @lends fabric.Path.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'path',/**\n * Array of path points\n * @type Array\n * @default\n */path:null,cacheProperties:fabric.Object.prototype.cacheProperties.concat('path','fillRule'),stateProperties:fabric.Object.prototype.stateProperties.concat('path'),/**\n * Constructor\n * @param {Array|String} path Path data (sequence of coordinates and corresponding \"command\" tokens)\n * @param {Object} [options] Options object\n * @return {fabric.Path} thisArg\n */initialize:function initialize(path,options){options=options||{};this.callSuper('initialize',options);if(!path){path=[];}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)\n:path.match&&path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);if(!this.path){return;}if(!fromArray){this.path=this._parsePath();}fabric.Polyline.prototype._setPositionDimensions.call(this,options);},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx context to render path on\n */_renderPathCommands:function _renderPathCommands(ctx){var current,// current instruction\nprevious=null,subpathStartX=0,subpathStartY=0,x=0,// current x\ny=0,// current y\ncontrolX=0,// current control point x\ncontrolY=0,// current control point y\ntempX,tempY,l=-this.pathOffset.x,t=-this.pathOffset.y;ctx.beginPath();for(var i=0,len=this.path.length;i<len;++i){current=this.path[i];switch(current[0]){// first letter\ncase'l':// lineto, relative\nx+=current[1];y+=current[2];ctx.lineTo(x+l,y+t);break;case'L':// lineto, absolute\nx=current[1];y=current[2];ctx.lineTo(x+l,y+t);break;case'h':// horizontal lineto, relative\nx+=current[1];ctx.lineTo(x+l,y+t);break;case'H':// horizontal lineto, absolute\nx=current[1];ctx.lineTo(x+l,y+t);break;case'v':// vertical lineto, relative\ny+=current[1];ctx.lineTo(x+l,y+t);break;case'V':// verical lineto, absolute\ny=current[1];ctx.lineTo(x+l,y+t);break;case'm':// moveTo, relative\nx+=current[1];y+=current[2];subpathStartX=x;subpathStartY=y;ctx.moveTo(x+l,y+t);break;case'M':// moveTo, absolute\nx=current[1];y=current[2];subpathStartX=x;subpathStartY=y;ctx.moveTo(x+l,y+t);break;case'c':// bezierCurveTo, relative\ntempX=x+current[5];tempY=y+current[6];controlX=x+current[3];controlY=y+current[4];ctx.bezierCurveTo(x+current[1]+l,// x1\ny+current[2]+t,// y1\ncontrolX+l,// x2\ncontrolY+t,// y2\ntempX+l,tempY+t);x=tempX;y=tempY;break;case'C':// bezierCurveTo, absolute\nx=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\n// transform to absolute x,y\ntempX=x+current[3];tempY=y+current[4];if(previous[0].match(/[CcSs]/)===null){// If there is no previous command or if the previous command was not a C, c, S, or s,\n// the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control points\ncontrolX=2*x-controlX;controlY=2*y-controlY;}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\n// \"... the first control point is assumed to be\n// the reflection of the second control point on\n// the previous command relative to the current point.\"\ncontrolX=x+current[1];controlY=y+current[2];x=tempX;y=tempY;break;case'S':// shorthand cubic bezierCurveTo, absolute\ntempX=current[3];tempY=current[4];if(previous[0].match(/[CcSs]/)===null){// If there is no previous command or if the previous command was not a C, c, S, or s,\n// the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control points\ncontrolX=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\n// \"... the first control point is assumed to be\n// the reflection of the second control point on\n// the previous command relative to the current point.\"\ncontrolX=current[1];controlY=current[2];break;case'q':// quadraticCurveTo, relative\n// transform to absolute x,y\ntempX=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\ntempX=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\n// transform to absolute x,y\ntempX=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,\n// assume the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control point\ncontrolX=2*x-controlX;controlY=2*y-controlY;}ctx.quadraticCurveTo(controlX+l,controlY+t,tempX+l,tempY+t);x=tempX;y=tempY;break;case'T':tempX=current[1];tempY=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,\n// assume the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control point\ncontrolX=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\ndrawArc(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\ndrawArc(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;}},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx context to render path on\n */_render:function _render(ctx){this._renderPathCommands(ctx);this._renderPaintInOrder(ctx);},/**\n * Returns string representation of an instance\n * @return {String} string representation of an instance\n */toString:function toString(){return'#<fabric.Path ('+this.complexity()+'): { \"top\": '+this.top+', \"left\": '+this.left+' }>';},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return extend(this.callSuper('toObject',propertiesToInclude),{path:this.path.map(function(item){return item.slice();})});},/**\n * Returns dataless object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toDatalessObject:function toDatalessObject(propertiesToInclude){var o=this.toObject(['sourcePath'].concat(propertiesToInclude));if(o.sourcePath){delete o.path;}return o;},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var path=this.path.map(function(path){return path.join(' ');}).join(' ');return['<path ','COMMON_PARTS','d=\"',path,'\" stroke-linecap=\"round\" ','/>\\n'];},_getOffsetTransform:function _getOffsetTransform(){var digits=fabric.Object.NUM_FRACTION_DIGITS;return' translate('+toFixed(-this.pathOffset.x,digits)+', '+toFixed(-this.pathOffset.y,digits)+')';},/**\n * Returns svg clipPath representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toClipPathSVG:function toClipPathSVG(reviver){var additionalTransform=this._getOffsetTransform();return'\\t'+this._createBaseClipPathSVGMarkup(this._toSVG(),{reviver:reviver,additionalTransform:additionalTransform});},/**\n * Returns svg representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toSVG:function toSVG(reviver){var additionalTransform=this._getOffsetTransform();return this._createBaseSVGMarkup(this._toSVG(),{reviver:reviver,additionalTransform:additionalTransform});},/* _TO_SVG_END_ */ /**\n * Returns number representation of an instance complexity\n * @return {Number} complexity of this instance\n */complexity:function complexity(){return this.path.length;},/**\n * @private\n */_parsePath:function _parsePath(){var result=[],coords=[],currentPath,parsed,re=fabric.rePathCommand,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;},/**\n * @private\n */_calcDimensions:function _calcDimensions(){var aX=[],aY=[],current,// current instruction\nprevious=null,subpathStartX=0,subpathStartY=0,x=0,// current x\ny=0,// current y\ncontrolX=0,// current control point x\ncontrolY=0,// current control point y\ntempX,tempY,bounds;for(var i=0,len=this.path.length;i<len;++i){current=this.path[i];switch(current[0]){// first letter\ncase'l':// lineto, relative\nx+=current[1];y+=current[2];bounds=[];break;case'L':// lineto, absolute\nx=current[1];y=current[2];bounds=[];break;case'h':// horizontal lineto, relative\nx+=current[1];bounds=[];break;case'H':// horizontal lineto, absolute\nx=current[1];bounds=[];break;case'v':// vertical lineto, relative\ny+=current[1];bounds=[];break;case'V':// verical lineto, absolute\ny=current[1];bounds=[];break;case'm':// moveTo, relative\nx+=current[1];y+=current[2];subpathStartX=x;subpathStartY=y;bounds=[];break;case'M':// moveTo, absolute\nx=current[1];y=current[2];subpathStartX=x;subpathStartY=y;bounds=[];break;case'c':// bezierCurveTo, relative\ntempX=x+current[5];tempY=y+current[6];controlX=x+current[3];controlY=y+current[4];bounds=fabric.util.getBoundsOfCurve(x,y,x+current[1],// x1\ny+current[2],// y1\ncontrolX,// x2\ncontrolY,// y2\ntempX,tempY);x=tempX;y=tempY;break;case'C':// bezierCurveTo, absolute\ncontrolX=current[3];controlY=current[4];bounds=fabric.util.getBoundsOfCurve(x,y,current[1],current[2],controlX,controlY,current[5],current[6]);x=current[5];y=current[6];break;case's':// shorthand cubic bezierCurveTo, relative\n// transform to absolute x,y\ntempX=x+current[3];tempY=y+current[4];if(previous[0].match(/[CcSs]/)===null){// If there is no previous command or if the previous command was not a C, c, S, or s,\n// the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control points\ncontrolX=2*x-controlX;controlY=2*y-controlY;}bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,x+current[1],y+current[2],tempX,tempY);// set control point to 2nd one of this command\n// \"... the first control point is assumed to be\n// the reflection of the second control point on\n// the previous command relative to the current point.\"\ncontrolX=x+current[1];controlY=y+current[2];x=tempX;y=tempY;break;case'S':// shorthand cubic bezierCurveTo, absolute\ntempX=current[3];tempY=current[4];if(previous[0].match(/[CcSs]/)===null){// If there is no previous command or if the previous command was not a C, c, S, or s,\n// the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control points\ncontrolX=2*x-controlX;controlY=2*y-controlY;}bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,current[1],current[2],tempX,tempY);x=tempX;y=tempY;// set control point to 2nd one of this command\n// \"... the first control point is assumed to be\n// the reflection of the second control point on\n// the previous command relative to the current point.\"\ncontrolX=current[1];controlY=current[2];break;case'q':// quadraticCurveTo, relative\n// transform to absolute x,y\ntempX=x+current[3];tempY=y+current[4];controlX=x+current[1];controlY=y+current[2];bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,controlX,controlY,tempX,tempY);x=tempX;y=tempY;break;case'Q':// quadraticCurveTo, absolute\ncontrolX=current[1];controlY=current[2];bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,controlX,controlY,current[3],current[4]);x=current[3];y=current[4];break;case't':// shorthand quadraticCurveTo, relative\n// transform to absolute x,y\ntempX=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,\n// assume the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control point\ncontrolX=2*x-controlX;controlY=2*y-controlY;}bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,controlX,controlY,tempX,tempY);x=tempX;y=tempY;break;case'T':tempX=current[1];tempY=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,\n// assume the control point is coincident with the current point\ncontrolX=x;controlY=y;}else{// calculate reflection of previous control point\ncontrolX=2*x-controlX;controlY=2*y-controlY;}bounds=fabric.util.getBoundsOfCurve(x,y,controlX,controlY,controlX,controlY,tempX,tempY);x=tempX;y=tempY;break;case'a':// TODO: optimize this\nbounds=fabric.util.getBoundsOfArc(x,y,current[1],current[2],current[3],current[4],current[5],current[6]+x,current[7]+y);x+=current[6];y+=current[7];break;case'A':// TODO: optimize this\nbounds=fabric.util.getBoundsOfArc(x,y,current[1],current[2],current[3],current[4],current[5],current[6],current[7]);x=current[6];y=current[7];break;case'z':case'Z':x=subpathStartX;y=subpathStartY;break;}previous=current;bounds.forEach(function(point){aX.push(point.x);aY.push(point.y);});aX.push(x);aY.push(y);}var minX=min(aX)||0,minY=min(aY)||0,maxX=max(aX)||0,maxY=max(aY)||0,deltaX=maxX-minX,deltaY=maxY-minY;return{left:minX,top:minY,width:deltaX,height:deltaY};}});/**\n * Creates an instance of fabric.Path from an object\n * @static\n * @memberOf fabric.Path\n * @param {Object} object\n * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created\n */fabric.Path.fromObject=function(object,callback){if(typeof object.sourcePath==='string'){var pathUrl=object.sourcePath;fabric.loadSVGFromURL(pathUrl,function(elements){var path=elements[0];path.setOptions(object);callback&&callback(path);});}else{fabric.Object._fromObject('Path',object,callback,'path');}};/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)\n * @static\n * @memberOf fabric.Path\n * @see http://www.w3.org/TR/SVG/paths.html#PathElement\n */fabric.Path.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat(['d']);/**\n * Creates an instance of fabric.Path from an SVG <path> element\n * @static\n * @memberOf fabric.Path\n * @param {SVGElement} element to parse\n * @param {Function} callback Callback to invoke when an fabric.Path instance is created\n * @param {Object} [options] Options object\n * @param {Function} [callback] Options callback invoked after parsing is finished\n */fabric.Path.fromElement=function(element,callback,options){var parsedAttributes=fabric.parseAttributes(element,fabric.Path.ATTRIBUTE_NAMES);parsedAttributes.fromSVG=true;callback(new fabric.Path(parsedAttributes.d,extend(parsedAttributes,options)));};/* _FROM_SVG_END_ */})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),min=fabric.util.array.min,max=fabric.util.array.max;if(fabric.Group){return;}/**\n * Group class\n * @class fabric.Group\n * @extends fabric.Object\n * @mixes fabric.Collection\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}\n * @see {@link fabric.Group#initialize} for constructor definition\n */fabric.Group=fabric.util.createClass(fabric.Object,fabric.Collection,/** @lends fabric.Group.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'group',/**\n * Width of stroke\n * @type Number\n * @default\n */strokeWidth:0,/**\n * Indicates if click, mouseover, mouseout events & hoverCursor should also check for subtargets\n * @type Boolean\n * @default\n */subTargetCheck:false,/**\n * Groups are container, do not render anything on theyr own, ence no cache properties\n * @type Array\n * @default\n */cacheProperties:[],/**\n * setOnGroup is a method used for TextBox that is no more used since 2.0.0 The behavior is still\n * available setting this boolean to true.\n * @type Boolean\n * @since 2.0.0\n * @default\n */useSetOnGroup:false,/**\n * Constructor\n * @param {Object} objects Group objects\n * @param {Object} [options] Options object\n * @param {Boolean} [isAlreadyGrouped] if true, objects have been grouped already.\n * @return {Object} thisArg\n */initialize:function initialize(objects,options,isAlreadyGrouped){options=options||{};this._objects=[];// if objects enclosed in a group have been grouped already,\n// we cannot change properties of objects.\n// Thus we need to set options to group without objects,\nisAlreadyGrouped&&this.callSuper('initialize',options);this._objects=objects||[];for(var i=this._objects.length;i--;){this._objects[i].group=this;}if(!isAlreadyGrouped){var center=options&&options.centerPoint;// we want to set origins before calculating the bounding box.\n// so that the topleft can be set with that in mind.\n// if specific top and left are passed, are overwritten later\n// with the callSuper('initialize', options)\nif(options.originX!==undefined){this.originX=options.originX;}if(options.originY!==undefined){this.originY=options.originY;}// if coming from svg i do not want to calc bounds.\n// i assume width and height are passed along options\ncenter||this._calcBounds();this._updateObjectsCoords(center);delete options.centerPoint;this.callSuper('initialize',options);}else{this._updateObjectsACoords();}this.setCoords();},/**\n * @private\n * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change\n */_updateObjectsACoords:function _updateObjectsACoords(){var ignoreZoom=true,skipAbsolute=true;for(var i=this._objects.length;i--;){this._objects[i].setCoords(ignoreZoom,skipAbsolute);}},/**\n * @private\n * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change\n */_updateObjectsCoords:function _updateObjectsCoords(center){var center=center||this.getCenterPoint();for(var i=this._objects.length;i--;){this._updateObjectCoords(this._objects[i],center);}},/**\n * @private\n * @param {Object} object\n * @param {fabric.Point} center, current center of group.\n */_updateObjectCoords:function _updateObjectCoords(object,center){var objectLeft=object.left,objectTop=object.top,ignoreZoom=true,skipAbsolute=true;object.set({left:objectLeft-center.x,top:objectTop-center.y});object.group=this;object.setCoords(ignoreZoom,skipAbsolute);},/**\n * Returns string represenation of a group\n * @return {String}\n */toString:function toString(){return'#<fabric.Group: ('+this.complexity()+')>';},/**\n * Adds an object to a group; Then recalculates group's dimension, position.\n * @param {Object} object\n * @return {fabric.Group} thisArg\n * @chainable\n */addWithUpdate:function addWithUpdate(object){this._restoreObjectsState();fabric.util.resetObjectTransform(this);if(object){this._objects.push(object);object.group=this;object._set('canvas',this.canvas);}this._calcBounds();this._updateObjectsCoords();this.setCoords();this.dirty=true;return this;},/**\n * Removes an object from a group; Then recalculates group's dimension, position.\n * @param {Object} object\n * @return {fabric.Group} thisArg\n * @chainable\n */removeWithUpdate:function removeWithUpdate(object){this._restoreObjectsState();fabric.util.resetObjectTransform(this);this.remove(object);this._calcBounds();this._updateObjectsCoords();this.setCoords();this.dirty=true;return this;},/**\n * @private\n */_onObjectAdded:function _onObjectAdded(object){this.dirty=true;object.group=this;object._set('canvas',this.canvas);},/**\n * @private\n */_onObjectRemoved:function _onObjectRemoved(object){this.dirty=true;delete object.group;},/**\n * @private\n */_set:function _set(key,value){var i=this._objects.length;if(this.useSetOnGroup){while(i--){this._objects[i].setOnGroup(key,value);}}if(key==='canvas'){while(i--){this._objects[i]._set(key,value);}}fabric.Object.prototype._set.call(this,key,value);},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){var _includeDefaultValues=this.includeDefaultValues;var objsToObject=this._objects.map(function(obj){var originalDefaults=obj.includeDefaultValues;obj.includeDefaultValues=_includeDefaultValues;var _obj=obj.toObject(propertiesToInclude);obj.includeDefaultValues=originalDefaults;return _obj;});var obj=fabric.Object.prototype.toObject.call(this,propertiesToInclude);obj.objects=objsToObject;return obj;},/**\n * Returns object representation of an instance, in dataless mode.\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toDatalessObject:function toDatalessObject(propertiesToInclude){var objsToObject,sourcePath=this.sourcePath;if(sourcePath){objsToObject=sourcePath;}else{var _includeDefaultValues=this.includeDefaultValues;objsToObject=this._objects.map(function(obj){var originalDefaults=obj.includeDefaultValues;obj.includeDefaultValues=_includeDefaultValues;var _obj=obj.toDatalessObject(propertiesToInclude);obj.includeDefaultValues=originalDefaults;return _obj;});}var obj=fabric.Object.prototype.toDatalessObject.call(this,propertiesToInclude);obj.objects=objsToObject;return obj;},/**\n * Renders instance on a given context\n * @param {CanvasRenderingContext2D} ctx context to render instance on\n */render:function render(ctx){this._transformDone=true;this.callSuper('render',ctx);this._transformDone=false;},/**\n * Decide if the object should cache or not. Create its own cache level\n * needsItsOwnCache should be used when the object drawing method requires\n * a cache step. None of the fabric classes requires it.\n * Generally you do not cache objects in groups because the group is already cached.\n * @return {Boolean}\n */shouldCache:function shouldCache(){var ownCache=fabric.Object.prototype.shouldCache.call(this);if(ownCache){for(var i=0,len=this._objects.length;i<len;i++){if(this._objects[i].willDrawShadow()){this.ownCaching=false;return false;}}}return ownCache;},/**\n * Check if this object or a child object will cast a shadow\n * @return {Boolean}\n */willDrawShadow:function willDrawShadow(){if(this.shadow){return fabric.Object.prototype.willDrawShadow.call(this);}for(var i=0,len=this._objects.length;i<len;i++){if(this._objects[i].willDrawShadow()){return true;}}return false;},/**\n * Check if this group or its parent group are caching, recursively up\n * @return {Boolean}\n */isOnACache:function isOnACache(){return this.ownCaching||this.group&&this.group.isOnACache();},/**\n * Execute the drawing operation for an object on a specified context\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */drawObject:function drawObject(ctx){for(var i=0,len=this._objects.length;i<len;i++){this._objects[i].render(ctx);}this._drawClipPath(ctx);},/**\n * Check if cache is dirty\n */isCacheDirty:function isCacheDirty(skipCanvas){if(this.callSuper('isCacheDirty',skipCanvas)){return true;}if(!this.statefullCache){return false;}for(var i=0,len=this._objects.length;i<len;i++){if(this._objects[i].isCacheDirty(true)){if(this._cacheCanvas){// if this group has not a cache canvas there is nothing to clean\nvar x=this.cacheWidth/this.zoomX,y=this.cacheHeight/this.zoomY;this._cacheContext.clearRect(-x/2,-y/2,x,y);}return true;}}return false;},/**\n * Retores original state of each of group objects (original state is that which was before group was created).\n * @private\n * @return {fabric.Group} thisArg\n * @chainable\n */_restoreObjectsState:function _restoreObjectsState(){this._objects.forEach(this._restoreObjectState,this);return this;},/**\n * Realises the transform from this group onto the supplied object\n * i.e. it tells you what would happen if the supplied object was in\n * the group, and then the group was destroyed. It mutates the supplied\n * object.\n * @param {fabric.Object} object\n * @return {fabric.Object} transformedObject\n */realizeTransform:function realizeTransform(object){var matrix=object.calcTransformMatrix(),options=fabric.util.qrDecompose(matrix),center=new fabric.Point(options.translateX,options.translateY);object.flipX=false;object.flipY=false;object.set('scaleX',options.scaleX);object.set('scaleY',options.scaleY);object.skewX=options.skewX;object.skewY=options.skewY;object.angle=options.angle;object.setPositionByOrigin(center,'center','center');return object;},/**\n * Restores original state of a specified object in group\n * @private\n * @param {fabric.Object} object\n * @return {fabric.Group} thisArg\n */_restoreObjectState:function _restoreObjectState(object){this.realizeTransform(object);object.setCoords();delete object.group;return this;},/**\n * Destroys a group (restoring state of its objects)\n * @return {fabric.Group} thisArg\n * @chainable\n */destroy:function destroy(){// when group is destroyed objects needs to get a repaint to be eventually\n// displayed on canvas.\nthis._objects.forEach(function(object){object.set('dirty',true);});return this._restoreObjectsState();},/**\n * make a group an active selection, remove the group from canvas\n * the group has to be on canvas for this to work.\n * @return {fabric.ActiveSelection} thisArg\n * @chainable\n */toActiveSelection:function toActiveSelection(){if(!this.canvas){return;}var objects=this._objects,canvas=this.canvas;this._objects=[];var options=this.toObject();delete options.objects;var activeSelection=new fabric.ActiveSelection([]);activeSelection.set(options);activeSelection.type='activeSelection';canvas.remove(this);objects.forEach(function(object){object.group=activeSelection;object.dirty=true;canvas.add(object);});activeSelection.canvas=canvas;activeSelection._objects=objects;canvas._activeObject=activeSelection;activeSelection.setCoords();return activeSelection;},/**\n * Destroys a group (restoring state of its objects)\n * @return {fabric.Group} thisArg\n * @chainable\n */ungroupOnCanvas:function ungroupOnCanvas(){return this._restoreObjectsState();},/**\n * Sets coordinates of all objects inside group\n * @return {fabric.Group} thisArg\n * @chainable\n */setObjectsCoords:function setObjectsCoords(){var ignoreZoom=true,skipAbsolute=true;this.forEachObject(function(object){object.setCoords(ignoreZoom,skipAbsolute);});return this;},/**\n * @private\n */_calcBounds:function _calcBounds(onlyWidthHeight){var aX=[],aY=[],o,prop,props=['tr','br','bl','tl'],i=0,iLen=this._objects.length,j,jLen=props.length,ignoreZoom=true;for(;i<iLen;++i){o=this._objects[i];o.setCoords(ignoreZoom);for(j=0;j<jLen;j++){prop=props[j];aX.push(o.oCoords[prop].x);aY.push(o.oCoords[prop].y);}}this._getBounds(aX,aY,onlyWidthHeight);},/**\n * @private\n */_getBounds:function _getBounds(aX,aY,onlyWidthHeight){var minXY=new fabric.Point(min(aX),min(aY)),maxXY=new fabric.Point(max(aX),max(aY)),top=minXY.y||0,left=minXY.x||0,width=maxXY.x-minXY.x||0,height=maxXY.y-minXY.y||0;this.width=width;this.height=height;if(!onlyWidthHeight){// the bounding box always finds the topleft most corner.\n// whatever is the group origin, we set up here the left/top position.\nthis.setPositionByOrigin({x:left,y:top},'left','top');}},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */_toSVG:function _toSVG(reviver){var svgString=['<g ','COMMON_PARTS',' >\\n'];for(var i=0,len=this._objects.length;i<len;i++){svgString.push('\\t\\t',this._objects[i].toSVG(reviver));}svgString.push('</g>\\n');return svgString;},/**\n * Returns styles-string for svg-export, specific version for group\n * @return {String}\n */getSvgStyles:function getSvgStyles(){var opacity=typeof this.opacity!=='undefined'&&this.opacity!==1?'opacity: '+this.opacity+';':'',visibility=this.visible?'':' visibility: hidden;';return[opacity,this.getSvgFilter(),visibility].join('');},/**\n * Returns svg clipPath representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toClipPathSVG:function toClipPathSVG(reviver){var svgString=[];for(var i=0,len=this._objects.length;i<len;i++){svgString.push('\\t',this._objects[i].toClipPathSVG(reviver));}return this._createBaseClipPathSVGMarkup(svgString,{reviver:reviver});}/* _TO_SVG_END_ */});/**\n * Returns {@link fabric.Group} instance from an object representation\n * @static\n * @memberOf fabric.Group\n * @param {Object} object Object to create a group from\n * @param {Function} [callback] Callback to invoke when an group instance is created\n */fabric.Group.fromObject=function(object,callback){var objects=object.objects,options=fabric.util.object.clone(object,true);delete options.objects;if(typeof objects==='string'){// it has to be an url or something went wrong.\nfabric.loadSVGFromURL(objects,function(elements){var group=fabric.util.groupSVGElements(elements,object,objects);group.set(options);callback&&callback(group);});return;}fabric.util.enlivenObjects(objects,function(enlivenedObjects){fabric.util.enlivenObjects([object.clipPath],function(enlivedClipPath){var options=fabric.util.object.clone(object,true);options.clipPath=enlivedClipPath[0];delete options.objects;callback&&callback(new fabric.Group(enlivenedObjects,options,true));});});};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={});if(fabric.ActiveSelection){return;}/**\n * Group class\n * @class fabric.ActiveSelection\n * @extends fabric.Group\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}\n * @see {@link fabric.ActiveSelection#initialize} for constructor definition\n */fabric.ActiveSelection=fabric.util.createClass(fabric.Group,/** @lends fabric.ActiveSelection.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'activeSelection',/**\n * Constructor\n * @param {Object} objects ActiveSelection objects\n * @param {Object} [options] Options object\n * @return {Object} thisArg\n */initialize:function initialize(objects,options){options=options||{};this._objects=objects||[];for(var i=this._objects.length;i--;){this._objects[i].group=this;}if(options.originX){this.originX=options.originX;}if(options.originY){this.originY=options.originY;}this._calcBounds();this._updateObjectsCoords();fabric.Object.prototype.initialize.call(this,options);this.setCoords();},/**\n * Change te activeSelection to a normal group,\n * High level function that automatically adds it to canvas as\n * active object. no events fired.\n * @since 2.0.0\n * @return {fabric.Group}\n */toGroup:function toGroup(){var objects=this._objects.concat();this._objects=[];var options=fabric.Object.prototype.toObject.call(this);var newGroup=new fabric.Group([]);delete options.type;newGroup.set(options);objects.forEach(function(object){object.canvas.remove(object);object.group=newGroup;});newGroup._objects=objects;if(!this.canvas){return newGroup;}var canvas=this.canvas;canvas.add(newGroup);canvas._activeObject=newGroup;newGroup.setCoords();return newGroup;},/**\n * If returns true, deselection is cancelled.\n * @since 2.0.0\n * @return {Boolean} [cancel]\n */onDeselect:function onDeselect(){this.destroy();return false;},/**\n * Returns string representation of a group\n * @return {String}\n */toString:function toString(){return'#<fabric.ActiveSelection: ('+this.complexity()+')>';},/**\n * Decide if the object should cache or not. Create its own cache level\n * objectCaching is a global flag, wins over everything\n * needsItsOwnCache should be used when the object drawing method requires\n * a cache step. None of the fabric classes requires it.\n * Generally you do not cache objects in groups because the group outside is cached.\n * @return {Boolean}\n */shouldCache:function shouldCache(){return false;},/**\n * Check if this group or its parent group are caching, recursively up\n * @return {Boolean}\n */isOnACache:function isOnACache(){return false;},/**\n * Renders controls and borders for the object\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Object} [styleOverride] properties to override the object style\n * @param {Object} [childrenOverride] properties to override the children overrides\n */_renderControls:function _renderControls(ctx,styleOverride,childrenOverride){ctx.save();ctx.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1;this.callSuper('_renderControls',ctx,styleOverride);childrenOverride=childrenOverride||{};if(typeof childrenOverride.hasControls==='undefined'){childrenOverride.hasControls=false;}if(typeof childrenOverride.hasRotatingPoint==='undefined'){childrenOverride.hasRotatingPoint=false;}childrenOverride.forActiveSelection=true;for(var i=0,len=this._objects.length;i<len;i++){this._objects[i]._renderControls(ctx,childrenOverride);}ctx.restore();}});/**\n * Returns {@link fabric.ActiveSelection} instance from an object representation\n * @static\n * @memberOf fabric.ActiveSelection\n * @param {Object} object Object to create a group from\n * @param {Function} [callback] Callback to invoke when an ActiveSelection instance is created\n */fabric.ActiveSelection.fromObject=function(object,callback){fabric.util.enlivenObjects(object.objects,function(enlivenedObjects){delete object.objects;callback&&callback(new fabric.ActiveSelection(enlivenedObjects,object,true));});};})( true?exports:undefined);(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;}/**\n * Image class\n * @class fabric.Image\n * @extends fabric.Object\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#images}\n * @see {@link fabric.Image#initialize} for constructor definition\n */fabric.Image=fabric.util.createClass(fabric.Object,/** @lends fabric.Image.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'image',/**\n * crossOrigin value (one of \"\", \"anonymous\", \"use-credentials\")\n * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes\n * @type String\n * @default\n */crossOrigin:'',/**\n * Width of a stroke.\n * For image quality a stroke multiple of 2 gives better results.\n * @type Number\n * @default\n */strokeWidth:0,/**\n * When calling {@link fabric.Image.getSrc}, return value from element src with `element.getAttribute('src')`.\n * This allows for relative urls as image src.\n * @since 2.7.0\n * @type Boolean\n * @default\n */srcFromAttribute:false,/**\n * private\n * contains last value of scaleX to detect\n * if the Image got resized after the last Render\n * @type Number\n */_lastScaleX:1,/**\n * private\n * contains last value of scaleY to detect\n * if the Image got resized after the last Render\n * @type Number\n */_lastScaleY:1,/**\n * private\n * contains last value of scaling applied by the apply filter chain\n * @type Number\n */_filterScalingX:1,/**\n * private\n * contains last value of scaling applied by the apply filter chain\n * @type Number\n */_filterScalingY:1,/**\n * minimum scale factor under which any resizeFilter is triggered to resize the image\n * 0 will disable the automatic resize. 1 will trigger automatically always.\n * number bigger than 1 are not implemented yet.\n * @type Number\n */minimumScaleTrigger:0.5,/**\n * List of properties to consider when checking if\n * state of an object is changed ({@link fabric.Object#hasStateChanged})\n * as well as for history (undo/redo) purposes\n * @type Array\n */stateProperties:fabric.Object.prototype.stateProperties.concat('cropX','cropY'),/**\n * key used to retrieve the texture representing this image\n * @since 2.0.0\n * @type String\n * @default\n */cacheKey:'',/**\n * Image crop in pixels from original image size.\n * @since 2.0.0\n * @type Number\n * @default\n */cropX:0,/**\n * Image crop in pixels from original image size.\n * @since 2.0.0\n * @type Number\n * @default\n */cropY:0,/**\n * Constructor\n * @param {HTMLImageElement | String} element Image element\n * @param {Object} [options] Options object\n * @param {function} [callback] callback function to call after eventual filters applied.\n * @return {fabric.Image} thisArg\n */initialize:function initialize(element,options){options||(options={});this.filters=[];this.cacheKey='texture'+fabric.Object.__uid++;this.callSuper('initialize',options);this._initElement(element,options);},/**\n * Returns image element which this instance if based on\n * @return {HTMLImageElement} Image element\n */getElement:function getElement(){return this._element||{};},/**\n * Sets image element for this instance to a specified one.\n * If filters defined they are applied to new image.\n * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.\n * @param {HTMLImageElement} element\n * @param {Object} [options] Options object\n * @return {fabric.Image} thisArg\n * @chainable\n */setElement:function setElement(element,options){this.removeTexture(this.cacheKey);this.removeTexture(this.cacheKey+'_filtered');this._element=element;this._originalElement=element;this._initConfig(options);if(this.filters.length!==0){this.applyFilters();}// resizeFilters work on the already filtered copy.\n// we need to apply resizeFilters AFTER normal filters.\n// applyResizeFilters is run more often than normal fiters\n// and is triggered by user interactions rather than dev code\nif(this.resizeFilter){this.applyResizeFilters();}return this;},/**\n * Delete a single texture if in webgl mode\n */removeTexture:function removeTexture(key){var backend=fabric.filterBackend;if(backend&&backend.evictCachesForKey){backend.evictCachesForKey(key);}},/**\n * Delete textures, reference to elements and eventually JSDOM cleanup\n */dispose:function dispose(){this.removeTexture(this.cacheKey);this.removeTexture(this.cacheKey+'_filtered');this._cacheContext=undefined;['_originalElement','_element','_filteredEl','_cacheCanvas'].forEach(function(element){fabric.util.cleanUpJsdomNode(this[element]);this[element]=undefined;}.bind(this));},/**\n * Sets crossOrigin value (on an instance and corresponding image element)\n * @return {fabric.Image} thisArg\n * @chainable\n */setCrossOrigin:function setCrossOrigin(value){this.crossOrigin=value;this._element.crossOrigin=value;return this;},/**\n * Returns original size of an image\n * @return {Object} Object with \"width\" and \"height\" properties\n */getOriginalSize:function getOriginalSize(){var element=this.getElement();return{width:element.naturalWidth||element.width,height:element.naturalHeight||element.height};},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_stroke:function _stroke(ctx){if(!this.stroke||this.strokeWidth===0){return;}var w=this.width/2,h=this.height/2;ctx.beginPath();ctx.moveTo(-w,-h);ctx.lineTo(w,-h);ctx.lineTo(w,h);ctx.lineTo(-w,h);ctx.lineTo(-w,-h);ctx.closePath();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderDashedStroke:function _renderDashedStroke(ctx){var x=-this.width/2,y=-this.height/2,w=this.width,h=this.height;ctx.save();this._setStrokeStyles(ctx,this);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();},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */toObject:function toObject(propertiesToInclude){var filters=[];this.filters.forEach(function(filterObj){if(filterObj){filters.push(filterObj.toObject());}});var object=extend(this.callSuper('toObject',['crossOrigin','cropX','cropY'].concat(propertiesToInclude)),{src:this.getSrc(),filters:filters});if(this.resizeFilter){object.resizeFilter=this.resizeFilter.toObject();}return object;},/**\n * Returns true if an image has crop applied, inspecting values of cropX,cropY,width,hight.\n * @return {Boolean}\n */hasCrop:function hasCrop(){return this.cropX||this.cropY||this.width<this._element.width||this.height<this._element.height;},/* _TO_SVG_START_ */ /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */_toSVG:function _toSVG(){var svgString=[],imageMarkup=[],strokeSvg,x=-this.width/2,y=-this.height/2,clipPath='';if(this.hasCrop()){var clipPathId=fabric.Object.__uid++;svgString.push('<clipPath id=\"imageCrop_'+clipPathId+'\">\\n','\\t<rect x=\"'+x+'\" y=\"'+y+'\" width=\"'+this.width+'\" height=\"'+this.height+'\" />\\n','</clipPath>\\n');clipPath=' clip-path=\"url(#imageCrop_'+clipPathId+')\" ';}imageMarkup.push('\\t<image ','COMMON_PARTS','xlink:href=\"',this.getSvgSrc(true),'\" x=\"',x-this.cropX,'\" y=\"',y-this.cropY,// we're essentially moving origin of transformation from top/left corner to the center of the shape\n// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left\n// so that object's center aligns with container's left/top\n'\" width=\"',this._element.width||this._element.naturalWidth,'\" height=\"',this._element.height||this._element.height,'\"',clipPath,'></image>\\n');if(this.stroke||this.strokeDashArray){var origFill=this.fill;this.fill=null;strokeSvg=['\\t<rect ','x=\"',x,'\" y=\"',y,'\" width=\"',this.width,'\" height=\"',this.height,'\" style=\"',this.getSvgStyles(),'\"/>\\n'];this.fill=origFill;}if(this.paintFirst!=='fill'){svgString=svgString.concat(strokeSvg,imageMarkup);}else{svgString=svgString.concat(imageMarkup,strokeSvg);}return svgString;},/* _TO_SVG_END_ */ /**\n * Returns source of an image\n * @param {Boolean} filtered indicates if the src is needed for svg\n * @return {String} Source of an image\n */getSrc:function getSrc(filtered){var element=filtered?this._element:this._originalElement;if(element){if(element.toDataURL){return element.toDataURL();}if(this.srcFromAttribute){return element.getAttribute('src');}else{return element.src;}}else{return this.src||'';}},/**\n * Sets source of an image\n * @param {String} src Source string (URL)\n * @param {Function} [callback] Callback is invoked when image has been loaded (and all filters have been applied)\n * @param {Object} [options] Options object\n * @return {fabric.Image} thisArg\n * @chainable\n */setSrc:function setSrc(src,callback,options){fabric.util.loadImage(src,function(img){this.setElement(img,options);this._setWidthHeight();callback&&callback(this);},this,options&&options.crossOrigin);return this;},/**\n * Returns string representation of an instance\n * @return {String} String representation of an instance\n */toString:function toString(){return'#<fabric.Image: { src: \"'+this.getSrc()+'\" }>';},applyResizeFilters:function applyResizeFilters(){var filter=this.resizeFilter,minimumScale=this.minimumScaleTrigger,objectScale=this.getTotalObjectScaling(),scaleX=objectScale.scaleX,scaleY=objectScale.scaleY,elementToFilter=this._filteredEl||this._originalElement;if(this.group){this.set('dirty',true);}if(!filter||scaleX>minimumScale&&scaleY>minimumScale){this._element=elementToFilter;this._filterScalingX=1;this._filterScalingY=1;this._lastScaleX=scaleX;this._lastScaleY=scaleY;return;}if(!fabric.filterBackend){fabric.filterBackend=fabric.initFilterBackend();}var canvasEl=fabric.util.createCanvasElement(),cacheKey=this._filteredEl?this.cacheKey+'_filtered':this.cacheKey,sourceWidth=elementToFilter.width,sourceHeight=elementToFilter.height;canvasEl.width=sourceWidth;canvasEl.height=sourceHeight;this._element=canvasEl;this._lastScaleX=filter.scaleX=scaleX;this._lastScaleY=filter.scaleY=scaleY;fabric.filterBackend.applyFilters([filter],elementToFilter,sourceWidth,sourceHeight,this._element,cacheKey);this._filterScalingX=canvasEl.width/this._originalElement.width;this._filterScalingY=canvasEl.height/this._originalElement.height;},/**\n * Applies filters assigned to this image (from \"filters\" array) or from filter param\n * @method applyFilters\n * @param {Array} filters to be applied\n * @param {Boolean} forResizing specify if the filter operation is a resize operation\n * @return {thisArg} return the fabric.Image object\n * @chainable\n */applyFilters:function applyFilters(filters){filters=filters||this.filters||[];filters=filters.filter(function(filter){return filter&&!filter.isNeutralState();});this.set('dirty',true);// needs to clear out or WEBGL will not resize correctly\nthis.removeTexture(this.cacheKey+'_filtered');if(filters.length===0){this._element=this._originalElement;this._filteredEl=null;this._filterScalingX=1;this._filterScalingY=1;return this;}var imgElement=this._originalElement,sourceWidth=imgElement.naturalWidth||imgElement.width,sourceHeight=imgElement.naturalHeight||imgElement.height;if(this._element===this._originalElement){// if the element is the same we need to create a new element\nvar canvasEl=fabric.util.createCanvasElement();canvasEl.width=sourceWidth;canvasEl.height=sourceHeight;this._element=canvasEl;this._filteredEl=canvasEl;}else{// clear the existing element to get new filter data\n// also dereference the eventual resized _element\nthis._element=this._filteredEl;this._filteredEl.getContext('2d').clearRect(0,0,sourceWidth,sourceHeight);// we also need to resize again at next renderAll, so remove saved _lastScaleX/Y\nthis._lastScaleX=1;this._lastScaleY=1;}if(!fabric.filterBackend){fabric.filterBackend=fabric.initFilterBackend();}fabric.filterBackend.applyFilters(filters,this._originalElement,sourceWidth,sourceHeight,this._element,this.cacheKey);if(this._originalElement.width!==this._element.width||this._originalElement.height!==this._element.height){this._filterScalingX=this._element.width/this._originalElement.width;this._filterScalingY=this._element.height/this._originalElement.height;}return this;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){if(this.isMoving!==true&&this.resizeFilter&&this._needsResize()){this.applyResizeFilters();}this._stroke(ctx);this._renderPaintInOrder(ctx);},/**\n * Decide if the object should cache or not. Create its own cache level\n * needsItsOwnCache should be used when the object drawing method requires\n * a cache step. None of the fabric classes requires it.\n * Generally you do not cache objects in groups because the group outside is cached.\n * This is the special image version where we would like to avoid caching where possible.\n * Essentially images do not benefit from caching. They may require caching, and in that\n * case we do it. Also caching an image usually ends in a loss of details.\n * A full performance audit should be done.\n * @return {Boolean}\n */shouldCache:function shouldCache(){return this.needsItsOwnCache();},_renderFill:function _renderFill(ctx){var elementToDraw=this._element,w=this.width,h=this.height,sW=Math.min(elementToDraw.naturalWidth||elementToDraw.width,w*this._filterScalingX),sH=Math.min(elementToDraw.naturalHeight||elementToDraw.height,h*this._filterScalingY),x=-w/2,y=-h/2,sX=Math.max(0,this.cropX*this._filterScalingX),sY=Math.max(0,this.cropY*this._filterScalingY);elementToDraw&&ctx.drawImage(elementToDraw,sX,sY,sW,sH,x,y,w,h);},/**\n * needed to check if image needs resize\n * @private\n */_needsResize:function _needsResize(){var scale=this.getTotalObjectScaling();return scale.scaleX!==this._lastScaleX||scale.scaleY!==this._lastScaleY;},/**\n * @private\n */_resetWidthHeight:function _resetWidthHeight(){this.set(this.getOriginalSize());},/**\n * The Image class's initialization method. This method is automatically\n * called by the constructor.\n * @private\n * @param {HTMLImageElement|String} element The element representing the image\n * @param {Object} [options] Options object\n */_initElement:function _initElement(element,options){this.setElement(fabric.util.getById(element),options);fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS);},/**\n * @private\n * @param {Object} [options] Options object\n */_initConfig:function _initConfig(options){options||(options={});this.setOptions(options);this._setWidthHeight(options);if(this._element&&this.crossOrigin){this._element.crossOrigin=this.crossOrigin;}},/**\n * @private\n * @param {Array} filters to be initialized\n * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created\n */_initFilters:function _initFilters(filters,callback){if(filters&&filters.length){fabric.util.enlivenObjects(filters,function(enlivenedObjects){callback&&callback(enlivenedObjects);},'fabric.Image.filters');}else{callback&&callback();}},/**\n * @private\n * Set the width and the height of the image object, using the element or the\n * options.\n * @param {Object} [options] Object with width/height properties\n */_setWidthHeight:function _setWidthHeight(options){options||(options={});var el=this.getElement();this.width=options.width||el.naturalWidth||el.width||0;this.height=options.height||el.naturalHeight||el.height||0;},/**\n * Calculate offset for center and scale factor for the image in order to respect\n * the preserveAspectRatio attribute\n * @private\n * @return {Object}\n */parsePreserveAspectRatioAttribute:function parsePreserveAspectRatioAttribute(){var pAR=fabric.util.parsePreserveAspectRatioAttribute(this.preserveAspectRatio||''),rWidth=this._element.width,rHeight=this._element.height,scaleX=1,scaleY=1,offsetLeft=0,offsetTop=0,cropX=0,cropY=0,offset,pWidth=this.width,pHeight=this.height,parsedAttributes={width:pWidth,height:pHeight};if(pAR&&(pAR.alignX!=='none'||pAR.alignY!=='none')){if(pAR.meetOrSlice==='meet'){scaleX=scaleY=fabric.util.findScaleToFit(this._element,parsedAttributes);offset=(pWidth-rWidth*scaleX)/2;if(pAR.alignX==='Min'){offsetLeft=-offset;}if(pAR.alignX==='Max'){offsetLeft=offset;}offset=(pHeight-rHeight*scaleY)/2;if(pAR.alignY==='Min'){offsetTop=-offset;}if(pAR.alignY==='Max'){offsetTop=offset;}}if(pAR.meetOrSlice==='slice'){scaleX=scaleY=fabric.util.findScaleToCover(this._element,parsedAttributes);offset=rWidth-pWidth/scaleX;if(pAR.alignX==='Mid'){cropX=offset/2;}if(pAR.alignX==='Max'){cropX=offset;}offset=rHeight-pHeight/scaleY;if(pAR.alignY==='Mid'){cropY=offset/2;}if(pAR.alignY==='Max'){cropY=offset;}rWidth=pWidth/scaleX;rHeight=pHeight/scaleY;}}else{scaleX=pWidth/rWidth;scaleY=pHeight/rHeight;}return{width:rWidth,height:rHeight,scaleX:scaleX,scaleY:scaleY,offsetLeft:offsetLeft,offsetTop:offsetTop,cropX:cropX,cropY:cropY};}});/**\n * Default CSS class name for canvas\n * @static\n * @type String\n * @default\n */fabric.Image.CSS_CANVAS='canvas-img';/**\n * Alias for getSrc\n * @static\n */fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc;/**\n * Creates an instance of fabric.Image from its object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} callback Callback to invoke when an image instance is created\n */fabric.Image.fromObject=function(_object,callback){var object=fabric.util.object.clone(_object);fabric.util.loadImage(object.src,function(img,error){if(error){callback&&callback(null,error);return;}fabric.Image.prototype._initFilters.call(object,object.filters,function(filters){object.filters=filters||[];fabric.Image.prototype._initFilters.call(object,[object.resizeFilter],function(resizeFilters){object.resizeFilter=resizeFilters[0];fabric.util.enlivenObjects([object.clipPath],function(enlivedProps){object.clipPath=enlivedProps[0];var image=new fabric.Image(img,object);callback(image);});});});},null,object.crossOrigin);};/**\n * Creates an instance of fabric.Image from an URL string\n * @static\n * @param {String} url URL to create an image from\n * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)\n * @param {Object} [imgOptions] Options object\n */fabric.Image.fromURL=function(url,callback,imgOptions){fabric.util.loadImage(url,function(img){callback&&callback(new fabric.Image(img,imgOptions));},null,imgOptions&&imgOptions.crossOrigin);};/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})\n * @static\n * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}\n */fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href crossOrigin'.split(' '));/**\n * Returns {@link fabric.Image} instance from an SVG element\n * @static\n * @param {SVGElement} element Element to parse\n * @param {Object} [options] Options object\n * @param {Function} callback Callback to execute when fabric.Image object is created\n * @return {fabric.Image} Instance of fabric.Image\n */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_ */})( true?exports:undefined);fabric.util.object.extend(fabric.Object.prototype,/** @lends fabric.Object.prototype */{/**\n * @private\n * @return {Number} angle value\n */_getAngleValueForStraighten:function _getAngleValueForStraighten(){var angle=this.angle%360;if(angle>0){return Math.round((angle-1)/90)*90;}return Math.round(angle/90)*90;},/**\n * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer)\n * @return {fabric.Object} thisArg\n * @chainable\n */straighten:function straighten(){this.rotate(this._getAngleValueForStraighten());return this;},/**\n * Same as {@link fabric.Object.prototype.straighten} but with animation\n * @param {Object} callbacks Object with callback functions\n * @param {Function} [callbacks.onComplete] Invoked on completion\n * @param {Function} [callbacks.onChange] Invoked on every step of animation\n * @return {fabric.Object} thisArg\n * @chainable\n */fxStraighten:function fxStraighten(callbacks){callbacks=callbacks||{};var empty=function empty(){},_onComplete4=callbacks.onComplete||empty,_onChange4=callbacks.onChange||empty,_this=this;fabric.util.animate({startValue:this.get('angle'),endValue:this._getAngleValueForStraighten(),duration:this.FX_DURATION,onChange:function onChange(value){_this.rotate(value);_onChange4();},onComplete:function onComplete(){_this.setCoords();_onComplete4();}});return this;}});fabric.util.object.extend(fabric.StaticCanvas.prototype,/** @lends fabric.StaticCanvas.prototype */{/**\n * Straightens object, then rerenders canvas\n * @param {fabric.Object} object Object to straighten\n * @return {fabric.Canvas} thisArg\n * @chainable\n */straightenObject:function straightenObject(object){object.straighten();this.requestRenderAll();return this;},/**\n * Same as {@link fabric.Canvas.prototype.straightenObject}, but animated\n * @param {fabric.Object} object Object to straighten\n * @return {fabric.Canvas} thisArg\n * @chainable\n */fxStraightenObject:function fxStraightenObject(object){object.fxStraighten({onChange:this.requestRenderAllBound});return this;}});(function(){'use strict';/**\n * Tests if webgl supports certain precision\n * @param {WebGL} Canvas WebGL context to test on\n * @param {String} Precision to test can be any of following: 'lowp', 'mediump', 'highp'\n * @returns {Boolean} Whether the user's browser WebGL supports given precision.\n */function testPrecision(gl,precision){var fragmentSource='precision '+precision+' float;\\nvoid main(){}';var fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);gl.shaderSource(fragmentShader,fragmentSource);gl.compileShader(fragmentShader);if(!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)){return false;}return true;}/**\n * Indicate whether this filtering backend is supported by the user's browser.\n * @param {Number} tileSize check if the tileSize is supported\n * @returns {Boolean} Whether the user's browser supports WebGL.\n */fabric.isWebglSupported=function(tileSize){if(fabric.isLikelyNode){return false;}tileSize=tileSize||fabric.WebglFilterBackend.prototype.tileSize;var canvas=document.createElement('canvas');var gl=canvas.getContext('webgl')||canvas.getContext('experimental-webgl');var isSupported=false;// eslint-disable-next-line\nif(gl){fabric.maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE);isSupported=fabric.maxTextureSize>=tileSize;var precisions=['highp','mediump','lowp'];for(var i=0;i<3;i++){if(testPrecision(gl,precisions[i])){fabric.webGlPrecision=precisions[i];break;};}}this.isSupported=isSupported;return isSupported;};fabric.WebglFilterBackend=WebglFilterBackend;/**\n * WebGL filter backend.\n */function WebglFilterBackend(options){if(options&&options.tileSize){this.tileSize=options.tileSize;}this.setupGLContext(this.tileSize,this.tileSize);this.captureGPUInfo();};WebglFilterBackend.prototype=/** @lends fabric.WebglFilterBackend.prototype */{tileSize:2048,/**\n * Experimental. This object is a sort of repository of help layers used to avoid\n * of recreating them during frequent filtering. If you are previewing a filter with\n * a slider you problably do not want to create help layers every filter step.\n * in this object there will be appended some canvases, created once, resized sometimes\n * cleared never. Clearing is left to the developer.\n **/resources:{},/**\n * Setup a WebGL context suitable for filtering, and bind any needed event handlers.\n */setupGLContext:function setupGLContext(width,height){this.dispose();this.createWebGLCanvas(width,height);// eslint-disable-next-line\nthis.aPosition=new Float32Array([0,0,0,1,1,0,1,1]);this.chooseFastestCopyGLTo2DMethod(width,height);},/**\n * Pick a method to copy data from GL context to 2d canvas. In some browsers using\n * putImageData is faster than drawImage for that specific operation.\n */chooseFastestCopyGLTo2DMethod:function chooseFastestCopyGLTo2DMethod(width,height){var canMeasurePerf=typeof window.performance!=='undefined',canUseImageData;try{new ImageData(1,1);canUseImageData=true;}catch(e){canUseImageData=false;}// eslint-disable-next-line no-undef\nvar canUseArrayBuffer=typeof ArrayBuffer!=='undefined';// eslint-disable-next-line no-undef\nvar canUseUint8Clamped=typeof Uint8ClampedArray!=='undefined';if(!(canMeasurePerf&&canUseImageData&&canUseArrayBuffer&&canUseUint8Clamped)){return;}var targetCanvas=fabric.util.createCanvasElement();// eslint-disable-next-line no-undef\nvar imageBuffer=new ArrayBuffer(width*height*4);if(fabric.forceGLPutImageData){this.imageBuffer=imageBuffer;this.copyGLTo2D=copyGLTo2DPutImageData;return;}var testContext={imageBuffer:imageBuffer,destinationWidth:width,destinationHeight:height,targetCanvas:targetCanvas};var startTime,drawImageTime,putImageDataTime;targetCanvas.width=width;targetCanvas.height=height;startTime=window.performance.now();copyGLTo2DDrawImage.call(testContext,this.gl,testContext);drawImageTime=window.performance.now()-startTime;startTime=window.performance.now();copyGLTo2DPutImageData.call(testContext,this.gl,testContext);putImageDataTime=window.performance.now()-startTime;if(drawImageTime>putImageDataTime){this.imageBuffer=imageBuffer;this.copyGLTo2D=copyGLTo2DPutImageData;}else{this.copyGLTo2D=copyGLTo2DDrawImage;}},/**\n * Create a canvas element and associated WebGL context and attaches them as\n * class properties to the GLFilterBackend class.\n */createWebGLCanvas:function createWebGLCanvas(width,height){var canvas=fabric.util.createCanvasElement();canvas.width=width;canvas.height=height;var glOptions={alpha:true,premultipliedAlpha:false,depth:false,stencil:false,antialias:false},gl=canvas.getContext('webgl',glOptions);if(!gl){gl=canvas.getContext('experimental-webgl',glOptions);}if(!gl){return;}gl.clearColor(0,0,0,0);// this canvas can fire webglcontextlost and webglcontextrestored\nthis.canvas=canvas;this.gl=gl;},/**\n * Attempts to apply the requested filters to the source provided, drawing the filtered output\n * to the provided target canvas.\n *\n * @param {Array} filters The filters to apply.\n * @param {HTMLImageElement|HTMLCanvasElement} source The source to be filtered.\n * @param {Number} width The width of the source input.\n * @param {Number} height The height of the source input.\n * @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.\n * @param {String|undefined} cacheKey A key used to cache resources related to the source. If\n * omitted, caching will be skipped.\n */applyFilters:function applyFilters(filters,source,width,height,targetCanvas,cacheKey){var gl=this.gl;var cachedTexture;if(cacheKey){cachedTexture=this.getCachedTexture(cacheKey,source);}var pipelineState={originalWidth:source.width||source.originalWidth,originalHeight:source.height||source.originalHeight,sourceWidth:width,sourceHeight:height,destinationWidth:width,destinationHeight:height,context:gl,sourceTexture:this.createTexture(gl,width,height,!cachedTexture&&source),targetTexture:this.createTexture(gl,width,height),originalTexture:cachedTexture||this.createTexture(gl,width,height,!cachedTexture&&source),passes:filters.length,webgl:true,aPosition:this.aPosition,programCache:this.programCache,pass:0,filterBackend:this,targetCanvas:targetCanvas};var tempFbo=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,tempFbo);filters.forEach(function(filter){filter&&filter.applyTo(pipelineState);});resizeCanvasIfNeeded(pipelineState);this.copyGLTo2D(gl,pipelineState);gl.bindTexture(gl.TEXTURE_2D,null);gl.deleteTexture(pipelineState.sourceTexture);gl.deleteTexture(pipelineState.targetTexture);gl.deleteFramebuffer(tempFbo);targetCanvas.getContext('2d').setTransform(1,0,0,1,0,0);return pipelineState;},/**\n * Detach event listeners, remove references, and clean up caches.\n */dispose:function dispose(){if(this.canvas){this.canvas=null;this.gl=null;}this.clearWebGLCaches();},/**\n * Wipe out WebGL-related caches.\n */clearWebGLCaches:function clearWebGLCaches(){this.programCache={};this.textureCache={};},/**\n * Create a WebGL texture object.\n *\n * Accepts specific dimensions to initialize the textuer to or a source image.\n *\n * @param {WebGLRenderingContext} gl The GL context to use for creating the texture.\n * @param {Number} width The width to initialize the texture at.\n * @param {Number} height The height to initialize the texture.\n * @param {HTMLImageElement|HTMLCanvasElement} textureImageSource A source for the texture data.\n * @returns {WebGLTexture}\n */createTexture:function createTexture(gl,width,height,textureImageSource){var texture=gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,texture);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);if(textureImageSource){gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,textureImageSource);}else{gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,width,height,0,gl.RGBA,gl.UNSIGNED_BYTE,null);}return texture;},/**\n * Can be optionally used to get a texture from the cache array\n *\n * If an existing texture is not found, a new texture is created and cached.\n *\n * @param {String} uniqueId A cache key to use to find an existing texture.\n * @param {HTMLImageElement|HTMLCanvasElement} textureImageSource A source to use to create the\n * texture cache entry if one does not already exist.\n */getCachedTexture:function getCachedTexture(uniqueId,textureImageSource){if(this.textureCache[uniqueId]){return this.textureCache[uniqueId];}else{var texture=this.createTexture(this.gl,textureImageSource.width,textureImageSource.height,textureImageSource);this.textureCache[uniqueId]=texture;return texture;}},/**\n * Clear out cached resources related to a source image that has been\n * filtered previously.\n *\n * @param {String} cacheKey The cache key provided when the source image was filtered.\n */evictCachesForKey:function evictCachesForKey(cacheKey){if(this.textureCache[cacheKey]){this.gl.deleteTexture(this.textureCache[cacheKey]);delete this.textureCache[cacheKey];}},copyGLTo2D:copyGLTo2DDrawImage,/**\n * Attempt to extract GPU information strings from a WebGL context.\n *\n * Useful information when debugging or blacklisting specific GPUs.\n *\n * @returns {Object} A GPU info object with renderer and vendor strings.\n */captureGPUInfo:function captureGPUInfo(){if(this.gpuInfo){return this.gpuInfo;}var gl=this.gl,gpuInfo={renderer:'',vendor:''};if(!gl){return gpuInfo;}var ext=gl.getExtension('WEBGL_debug_renderer_info');if(ext){var renderer=gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);var vendor=gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);if(renderer){gpuInfo.renderer=renderer.toLowerCase();}if(vendor){gpuInfo.vendor=vendor.toLowerCase();}}this.gpuInfo=gpuInfo;return gpuInfo;}};})();function resizeCanvasIfNeeded(pipelineState){var targetCanvas=pipelineState.targetCanvas,width=targetCanvas.width,height=targetCanvas.height,dWidth=pipelineState.destinationWidth,dHeight=pipelineState.destinationHeight;if(width!==dWidth||height!==dHeight){targetCanvas.width=dWidth;targetCanvas.height=dHeight;}}/**\n * Copy an input WebGL canvas on to an output 2D canvas.\n *\n * The WebGL canvas is assumed to be upside down, with the top-left pixel of the\n * desired output image appearing in the bottom-left corner of the WebGL canvas.\n *\n * @param {WebGLRenderingContext} sourceContext The WebGL context to copy from.\n * @param {HTMLCanvasElement} targetCanvas The 2D target canvas to copy on to.\n * @param {Object} pipelineState The 2D target canvas to copy on to.\n */function copyGLTo2DDrawImage(gl,pipelineState){var glCanvas=gl.canvas,targetCanvas=pipelineState.targetCanvas,ctx=targetCanvas.getContext('2d');ctx.translate(0,targetCanvas.height);// move it down again\nctx.scale(1,-1);// vertical flip\n// where is my image on the big glcanvas?\nvar sourceY=glCanvas.height-targetCanvas.height;ctx.drawImage(glCanvas,0,sourceY,targetCanvas.width,targetCanvas.height,0,0,targetCanvas.width,targetCanvas.height);}/**\n * Copy an input WebGL canvas on to an output 2D canvas using 2d canvas' putImageData\n * API. Measurably faster than using ctx.drawImage in Firefox (version 54 on OSX Sierra).\n *\n * @param {WebGLRenderingContext} sourceContext The WebGL context to copy from.\n * @param {HTMLCanvasElement} targetCanvas The 2D target canvas to copy on to.\n * @param {Object} pipelineState The 2D target canvas to copy on to.\n */function copyGLTo2DPutImageData(gl,pipelineState){var targetCanvas=pipelineState.targetCanvas,ctx=targetCanvas.getContext('2d'),dWidth=pipelineState.destinationWidth,dHeight=pipelineState.destinationHeight,numBytes=dWidth*dHeight*4;// eslint-disable-next-line no-undef\nvar u8=new Uint8Array(this.imageBuffer,0,numBytes);// eslint-disable-next-line no-undef\nvar u8Clamped=new Uint8ClampedArray(this.imageBuffer,0,numBytes);gl.readPixels(0,0,dWidth,dHeight,gl.RGBA,gl.UNSIGNED_BYTE,u8);var imgData=new ImageData(u8Clamped,dWidth,dHeight);ctx.putImageData(imgData,0,0);}(function(){'use strict';var noop=function noop(){};fabric.Canvas2dFilterBackend=Canvas2dFilterBackend;/**\n * Canvas 2D filter backend.\n */function Canvas2dFilterBackend(){};Canvas2dFilterBackend.prototype=/** @lends fabric.Canvas2dFilterBackend.prototype */{evictCachesForKey:noop,dispose:noop,clearWebGLCaches:noop,/**\n * Experimental. This object is a sort of repository of help layers used to avoid\n * of recreating them during frequent filtering. If you are previewing a filter with\n * a slider you probably do not want to create help layers every filter step.\n * in this object there will be appended some canvases, created once, resized sometimes\n * cleared never. Clearing is left to the developer.\n **/resources:{},/**\n * Apply a set of filters against a source image and draw the filtered output\n * to the provided destination canvas.\n *\n * @param {EnhancedFilter} filters The filter to apply.\n * @param {HTMLImageElement|HTMLCanvasElement} sourceElement The source to be filtered.\n * @param {Number} sourceWidth The width of the source input.\n * @param {Number} sourceHeight The height of the source input.\n * @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.\n */applyFilters:function applyFilters(filters,sourceElement,sourceWidth,sourceHeight,targetCanvas){var ctx=targetCanvas.getContext('2d');ctx.drawImage(sourceElement,0,0,sourceWidth,sourceHeight);var imageData=ctx.getImageData(0,0,sourceWidth,sourceHeight);var originalImageData=ctx.getImageData(0,0,sourceWidth,sourceHeight);var pipelineState={sourceWidth:sourceWidth,sourceHeight:sourceHeight,imageData:imageData,originalEl:sourceElement,originalImageData:originalImageData,canvasEl:targetCanvas,ctx:ctx,filterBackend:this};filters.forEach(function(filter){filter.applyTo(pipelineState);});if(pipelineState.imageData.width!==sourceWidth||pipelineState.imageData.height!==sourceHeight){targetCanvas.width=pipelineState.imageData.width;targetCanvas.height=pipelineState.imageData.height;}ctx.putImageData(pipelineState.imageData,0,0);return pipelineState;}};})();/**\n * @namespace fabric.Image.filters\n * @memberOf fabric.Image\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#image_filters}\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n */fabric.Image=fabric.Image||{};fabric.Image.filters=fabric.Image.filters||{};/**\n * Root filter class from which all filter classes inherit from\n * @class fabric.Image.filters.BaseFilter\n * @memberOf fabric.Image.filters\n */fabric.Image.filters.BaseFilter=fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'BaseFilter',/**\n * Array of attributes to send with buffers. do not modify\n * @private\n */vertexSource:'attribute vec2 aPosition;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vTexCoord = aPosition;\\n'+'gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\\n'+'}',fragmentSource:'precision highp float;\\n'+'varying vec2 vTexCoord;\\n'+'uniform sampler2D uTexture;\\n'+'void main() {\\n'+'gl_FragColor = texture2D(uTexture, vTexCoord);\\n'+'}',/**\n * Constructor\n * @param {Object} [options] Options object\n */initialize:function initialize(options){if(options){this.setOptions(options);}},/**\n * Sets filter's properties from options\n * @param {Object} [options] Options object\n */setOptions:function setOptions(options){for(var prop in options){this[prop]=options[prop];}},/**\n * Compile this filter's shader program.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context to use for shader compilation.\n * @param {String} fragmentSource fragmentShader source for compilation\n * @param {String} vertexSource vertexShader source for compilation\n */createProgram:function createProgram(gl,fragmentSource,vertexSource){fragmentSource=fragmentSource||this.fragmentSource;vertexSource=vertexSource||this.vertexSource;if(fabric.webGlPrecision!=='highp'){fragmentSource=fragmentSource.replace(/precision highp float/g,'precision '+fabric.webGlPrecision+' float');}var vertexShader=gl.createShader(gl.VERTEX_SHADER);gl.shaderSource(vertexShader,vertexSource);gl.compileShader(vertexShader);if(!gl.getShaderParameter(vertexShader,gl.COMPILE_STATUS)){throw new Error(// eslint-disable-next-line prefer-template\n'Vertex shader compile error for '+this.type+': '+gl.getShaderInfoLog(vertexShader));}var fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);gl.shaderSource(fragmentShader,fragmentSource);gl.compileShader(fragmentShader);if(!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)){throw new Error(// eslint-disable-next-line prefer-template\n'Fragment shader compile error for '+this.type+': '+gl.getShaderInfoLog(fragmentShader));}var program=gl.createProgram();gl.attachShader(program,vertexShader);gl.attachShader(program,fragmentShader);gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS)){throw new Error(// eslint-disable-next-line prefer-template\n'Shader link error for \"${this.type}\" '+gl.getProgramInfoLog(program));}var attributeLocations=this.getAttributeLocations(gl,program);var uniformLocations=this.getUniformLocations(gl,program)||{};uniformLocations.uStepW=gl.getUniformLocation(program,'uStepW');uniformLocations.uStepH=gl.getUniformLocation(program,'uStepH');return{program:program,attributeLocations:attributeLocations,uniformLocations:uniformLocations};},/**\n * Return a map of attribute names to WebGLAttributeLocation objects.\n *\n * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.\n * @param {WebGLShaderProgram} program The shader program from which to take attribute locations.\n * @returns {Object} A map of attribute names to attribute locations.\n */getAttributeLocations:function getAttributeLocations(gl,program){return{aPosition:gl.getAttribLocation(program,'aPosition')};},/**\n * Return a map of uniform names to WebGLUniformLocation objects.\n *\n * Intended to be overridden by subclasses.\n *\n * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.\n * @param {WebGLShaderProgram} program The shader program from which to take uniform locations.\n * @returns {Object} A map of uniform names to uniform locations.\n */getUniformLocations:function getUniformLocations()/* gl, program */{// in case i do not need any special uniform i need to return an empty object\nreturn{};},/**\n * Send attribute data from this filter to its shader program on the GPU.\n *\n * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.\n * @param {Object} attributeLocations A map of shader attribute names to their locations.\n */sendAttributeData:function sendAttributeData(gl,attributeLocations,aPositionData){var attributeLocation=attributeLocations.aPosition;var buffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buffer);gl.enableVertexAttribArray(attributeLocation);gl.vertexAttribPointer(attributeLocation,2,gl.FLOAT,false,0,0);gl.bufferData(gl.ARRAY_BUFFER,aPositionData,gl.STATIC_DRAW);},_setupFrameBuffer:function _setupFrameBuffer(options){var gl=options.context,width,height;if(options.passes>1){width=options.destinationWidth;height=options.destinationHeight;if(options.sourceWidth!==width||options.sourceHeight!==height){gl.deleteTexture(options.targetTexture);options.targetTexture=options.filterBackend.createTexture(gl,width,height);}gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,options.targetTexture,0);}else{// draw last filter on canvas and not to framebuffer.\ngl.bindFramebuffer(gl.FRAMEBUFFER,null);gl.finish();}},_swapTextures:function _swapTextures(options){options.passes--;options.pass++;var temp=options.targetTexture;options.targetTexture=options.sourceTexture;options.sourceTexture=temp;},/**\n * Generic isNeutral implementation for one parameter based filters.\n * Used only in image applyFilters to discard filters that will not have an effect\n * on the image\n * Other filters may need their own verison ( ColorMatrix, HueRotation, gamma, ComposedFilter )\n * @param {Object} options\n **/isNeutralState:function isNeutralState()/* options */{var main=this.mainParameter,_class=fabric.Image.filters[this.type].prototype;if(main){if(Array.isArray(_class[main])){for(var i=_class[main].length;i--;){if(this[main][i]!==_class[main][i]){return false;}}return true;}else{return _class[main]===this[main];}}else{return false;}},/**\n * Apply this filter to the input image data provided.\n *\n * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.\n *\n * @param {Object} options\n * @param {Number} options.passes The number of filters remaining to be executed\n * @param {Boolean} options.webgl Whether to use webgl to render the filter.\n * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.\n * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */applyTo:function applyTo(options){if(options.webgl){this._setupFrameBuffer(options);this.applyToWebGL(options);this._swapTextures(options);}else{this.applyTo2d(options);}},/**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){if(!options.programCache.hasOwnProperty(this.type)){options.programCache[this.type]=this.createProgram(options.context);}return options.programCache[this.type];},/**\n * Apply this filter using webgl.\n *\n * @param {Object} options\n * @param {Number} options.passes The number of filters remaining to be executed\n * @param {Boolean} options.webgl Whether to use webgl to render the filter.\n * @param {WebGLTexture} options.originalTexture The texture of the original input image.\n * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.\n * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */applyToWebGL:function applyToWebGL(options){var gl=options.context;var shader=this.retrieveShader(options);if(options.pass===0&&options.originalTexture){gl.bindTexture(gl.TEXTURE_2D,options.originalTexture);}else{gl.bindTexture(gl.TEXTURE_2D,options.sourceTexture);}gl.useProgram(shader.program);this.sendAttributeData(gl,shader.attributeLocations,options.aPosition);gl.uniform1f(shader.uniformLocations.uStepW,1/options.sourceWidth);gl.uniform1f(shader.uniformLocations.uStepH,1/options.sourceHeight);this.sendUniformData(gl,shader.uniformLocations);gl.viewport(0,0,options.destinationWidth,options.destinationHeight);gl.drawArrays(gl.TRIANGLE_STRIP,0,4);},bindAdditionalTexture:function bindAdditionalTexture(gl,texture,textureUnit){gl.activeTexture(textureUnit);gl.bindTexture(gl.TEXTURE_2D,texture);// reset active texture to 0 as usual\ngl.activeTexture(gl.TEXTURE0);},unbindAdditionalTexture:function unbindAdditionalTexture(gl,textureUnit){gl.activeTexture(textureUnit);gl.bindTexture(gl.TEXTURE_2D,null);gl.activeTexture(gl.TEXTURE0);},getMainParameter:function getMainParameter(){return this[this.mainParameter];},setMainParameter:function setMainParameter(value){this[this.mainParameter]=value;},/**\n * Send uniform data from this filter to its shader program on the GPU.\n *\n * Intended to be overridden by subclasses.\n *\n * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.\n * @param {Object} uniformLocations A map of shader uniform names to their locations.\n */sendUniformData:function sendUniformData()/* gl, uniformLocations */{// Intentionally left blank. Override me in subclasses.\n},/**\n * If needed by a 2d filter, this functions can create an helper canvas to be used\n * remember that options.targetCanvas is available for use till end of chain.\n */createHelpLayer:function createHelpLayer(options){if(!options.helpLayer){var helpLayer=document.createElement('canvas');helpLayer.width=options.sourceWidth;helpLayer.height=options.sourceHeight;options.helpLayer=helpLayer;}},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){var object={type:this.type},mainP=this.mainParameter;if(mainP){object[mainP]=this[mainP];}return object;},/**\n * Returns a JSON representation of an instance\n * @return {Object} JSON\n */toJSON:function toJSON(){// delegate, not alias\nreturn this.toObject();}});fabric.Image.filters.BaseFilter.fromObject=function(object,callback){var filter=new fabric.Image.filters[object.type](object);callback&&callback(filter);return filter;};(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Color Matrix filter class\n * @class fabric.Image.filters.ColorMatrix\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.ColorMatrix#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @see {@Link http://www.webwasp.co.uk/tutorials/219/Color_Matrix_Filter.php}\n * @see {@Link http://phoboslab.org/log/2013/11/fast-image-filters-with-webgl}\n * @example <caption>Kodachrome filter</caption>\n * var filter = new fabric.Image.filters.ColorMatrix({\n * matrix: [\n 1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,\n -0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,\n -0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,\n 0, 0, 0, 1, 0\n ]\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.ColorMatrix=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.ColorMatrix.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'ColorMatrix',fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'varying vec2 vTexCoord;\\n'+'uniform mat4 uColorMatrix;\\n'+'uniform vec4 uConstants;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'color *= uColorMatrix;\\n'+'color += uConstants;\\n'+'gl_FragColor = color;\\n'+'}',/**\n * Colormatrix for pixels.\n * array of 20 floats. Numbers in positions 4, 9, 14, 19 loose meaning\n * outside the -1, 1 range.\n * 0.0039215686 is the part of 1 that get translated to 1 in 2d\n * @param {Array} matrix array of 20 numbers.\n * @default\n */matrix:[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],mainParameter:'matrix',/**\n * Lock the colormatrix on the color part, skipping alpha, manly for non webgl scenario\n * to save some calculation\n */colorsOnly:true,/**\n * Constructor\n * @param {Object} [options] Options object\n */initialize:function initialize(options){this.callSuper('initialize',options);// create a new array instead mutating the prototype with push\nthis.matrix=this.matrix.slice(0);},/**\n * Apply the ColorMatrix operation to a Uint8Array representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8Array to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,iLen=data.length,m=this.matrix,r,g,b,a,i,colorsOnly=this.colorsOnly;for(i=0;i<iLen;i+=4){r=data[i];g=data[i+1];b=data[i+2];if(colorsOnly){data[i]=r*m[0]+g*m[1]+b*m[2]+m[4]*255;data[i+1]=r*m[5]+g*m[6]+b*m[7]+m[9]*255;data[i+2]=r*m[10]+g*m[11]+b*m[12]+m[14]*255;}else{a=data[i+3];data[i]=r*m[0]+g*m[1]+b*m[2]+a*m[3]+m[4]*255;data[i+1]=r*m[5]+g*m[6]+b*m[7]+a*m[8]+m[9]*255;data[i+2]=r*m[10]+g*m[11]+b*m[12]+a*m[13]+m[14]*255;data[i+3]=r*m[15]+g*m[16]+b*m[17]+a*m[18]+m[19]*255;}}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uColorMatrix:gl.getUniformLocation(program,'uColorMatrix'),uConstants:gl.getUniformLocation(program,'uConstants')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){var m=this.matrix,matrix=[m[0],m[1],m[2],m[3],m[5],m[6],m[7],m[8],m[10],m[11],m[12],m[13],m[15],m[16],m[17],m[18]],constants=[m[4],m[9],m[14],m[19]];gl.uniformMatrix4fv(uniformLocations.uColorMatrix,false,matrix);gl.uniform4fv(uniformLocations.uConstants,constants);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] function to invoke after filter creation\n * @return {fabric.Image.filters.ColorMatrix} Instance of fabric.Image.filters.ColorMatrix\n */fabric.Image.filters.ColorMatrix.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Brightness filter class\n * @class fabric.Image.filters.Brightness\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Brightness({\n * brightness: 0.05\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Brightness=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Brightness.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Brightness',/**\n * Fragment source for the brightness program\n */fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uBrightness;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'color.rgb += uBrightness;\\n'+'gl_FragColor = color;\\n'+'}',/**\n * Brightness value, from -1 to 1.\n * translated to -255 to 255 for 2d\n * 0.0039215686 is the part of 1 that get translated to 1 in 2d\n * @param {Number} brightness\n * @default\n */brightness:0,/**\n * Describe the property that is the filter parameter\n * @param {String} m\n * @default\n */mainParameter:'brightness',/**\n * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){if(this.brightness===0){return;}var imageData=options.imageData,data=imageData.data,i,len=data.length,brightness=Math.round(this.brightness*255);for(i=0;i<len;i+=4){data[i]=data[i]+brightness;data[i+1]=data[i+1]+brightness;data[i+2]=data[i+2]+brightness;}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uBrightness:gl.getUniformLocation(program,'uBrightness')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1f(uniformLocations.uBrightness,this.brightness);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness\n */fabric.Image.filters.Brightness.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Adapted from <a href=\"http://www.html5rocks.com/en/tutorials/canvas/imagefilters/\">html5rocks article</a>\n * @class fabric.Image.filters.Convolute\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example <caption>Sharpen filter</caption>\n * var filter = new fabric.Image.filters.Convolute({\n * matrix: [ 0, -1, 0,\n * -1, 5, -1,\n * 0, -1, 0 ]\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n * @example <caption>Blur filter</caption>\n * var filter = new fabric.Image.filters.Convolute({\n * matrix: [ 1/9, 1/9, 1/9,\n * 1/9, 1/9, 1/9,\n * 1/9, 1/9, 1/9 ]\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n * @example <caption>Emboss filter</caption>\n * var filter = new fabric.Image.filters.Convolute({\n * matrix: [ 1, 1, 1,\n * 1, 0.7, -1,\n * -1, -1, -1 ]\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n * @example <caption>Emboss filter with opaqueness</caption>\n * var filter = new fabric.Image.filters.Convolute({\n * opaque: true,\n * matrix: [ 1, 1, 1,\n * 1, 0.7, -1,\n * -1, -1, -1 ]\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.Convolute=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Convolute.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Convolute',/*\n * Opaque value (true/false)\n */opaque:false,/*\n * matrix for the filter, max 9x9\n */matrix:[0,0,0,0,1,0,0,0,0],/**\n * Fragment source for the brightness program\n */fragmentSource:{Convolute_3_1:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[9];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 0);\\n'+'for (float h = 0.0; h < 3.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 3.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 1), uStepH * (h - 1));\\n'+'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 3.0 + w)];\\n'+'}\\n'+'}\\n'+'gl_FragColor = color;\\n'+'}',Convolute_3_0:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[9];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 1);\\n'+'for (float h = 0.0; h < 3.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 3.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 1.0), uStepH * (h - 1.0));\\n'+'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 3.0 + w)];\\n'+'}\\n'+'}\\n'+'float alpha = texture2D(uTexture, vTexCoord).a;\\n'+'gl_FragColor = color;\\n'+'gl_FragColor.a = alpha;\\n'+'}',Convolute_5_1:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[25];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 0);\\n'+'for (float h = 0.0; h < 5.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 5.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\\n'+'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 5.0 + w)];\\n'+'}\\n'+'}\\n'+'gl_FragColor = color;\\n'+'}',Convolute_5_0:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[25];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 1);\\n'+'for (float h = 0.0; h < 5.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 5.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\\n'+'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 5.0 + w)];\\n'+'}\\n'+'}\\n'+'float alpha = texture2D(uTexture, vTexCoord).a;\\n'+'gl_FragColor = color;\\n'+'gl_FragColor.a = alpha;\\n'+'}',Convolute_7_1:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[49];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 0);\\n'+'for (float h = 0.0; h < 7.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 7.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\\n'+'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 7.0 + w)];\\n'+'}\\n'+'}\\n'+'gl_FragColor = color;\\n'+'}',Convolute_7_0:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[49];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 1);\\n'+'for (float h = 0.0; h < 7.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 7.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\\n'+'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 7.0 + w)];\\n'+'}\\n'+'}\\n'+'float alpha = texture2D(uTexture, vTexCoord).a;\\n'+'gl_FragColor = color;\\n'+'gl_FragColor.a = alpha;\\n'+'}',Convolute_9_1:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[81];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 0);\\n'+'for (float h = 0.0; h < 9.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 9.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\\n'+'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 9.0 + w)];\\n'+'}\\n'+'}\\n'+'gl_FragColor = color;\\n'+'}',Convolute_9_0:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uMatrix[81];\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = vec4(0, 0, 0, 1);\\n'+'for (float h = 0.0; h < 9.0; h+=1.0) {\\n'+'for (float w = 0.0; w < 9.0; w+=1.0) {\\n'+'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\\n'+'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 9.0 + w)];\\n'+'}\\n'+'}\\n'+'float alpha = texture2D(uTexture, vTexCoord).a;\\n'+'gl_FragColor = color;\\n'+'gl_FragColor.a = alpha;\\n'+'}'},/**\n * Constructor\n * @memberOf fabric.Image.filters.Convolute.prototype\n * @param {Object} [options] Options object\n * @param {Boolean} [options.opaque=false] Opaque value (true/false)\n * @param {Array} [options.matrix] Filter matrix\n */ /**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){var size=Math.sqrt(this.matrix.length);var cacheKey=this.type+'_'+size+'_'+(this.opaque?1:0);var shaderSource=this.fragmentSource[cacheKey];if(!options.programCache.hasOwnProperty(cacheKey)){options.programCache[cacheKey]=this.createProgram(options.context,shaderSource);}return options.programCache[cacheKey];},/**\n * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,weights=this.matrix,side=Math.round(Math.sqrt(weights.length)),halfSide=Math.floor(side/2),sw=imageData.width,sh=imageData.height,output=options.ctx.createImageData(sw,sh),dst=output.data,// go through the destination image pixels\nalphaFac=this.opaque?1:0,r,g,b,a,dstOff,scx,scy,srcOff,wt,x,y,cx,cy;for(y=0;y<sh;y++){for(x=0;x<sw;x++){dstOff=(y*sw+x)*4;// calculate the weighed sum of the source image pixels that\n// fall under the convolution matrix\nr=0;g=0;b=0;a=0;for(cy=0;cy<side;cy++){for(cx=0;cx<side;cx++){scy=y+cy-halfSide;scx=x+cx-halfSide;// eslint-disable-next-line max-depth\nif(scy<0||scy>=sh||scx<0||scx>=sw){continue;}srcOff=(scy*sw+scx)*4;wt=weights[cy*side+cx];r+=data[srcOff]*wt;g+=data[srcOff+1]*wt;b+=data[srcOff+2]*wt;// eslint-disable-next-line max-depth\nif(!alphaFac){a+=data[srcOff+3]*wt;}}}dst[dstOff]=r;dst[dstOff+1]=g;dst[dstOff+2]=b;if(!alphaFac){dst[dstOff+3]=a;}else{dst[dstOff+3]=data[dstOff+3];}}}options.imageData=output;},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uMatrix:gl.getUniformLocation(program,'uMatrix'),uOpaque:gl.getUniformLocation(program,'uOpaque'),uHalfSize:gl.getUniformLocation(program,'uHalfSize'),uSize:gl.getUniformLocation(program,'uSize')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1fv(uniformLocations.uMatrix,this.matrix);},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return extend(this.callSuper('toObject'),{opaque:this.opaque,matrix:this.matrix});}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute\n */fabric.Image.filters.Convolute.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Grayscale image filter class\n * @class fabric.Image.filters.Grayscale\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Grayscale();\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Grayscale=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Grayscale.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Grayscale',fragmentSource:{average:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'float average = (color.r + color.b + color.g) / 3.0;\\n'+'gl_FragColor = vec4(average, average, average, color.a);\\n'+'}',lightness:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform int uMode;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 col = texture2D(uTexture, vTexCoord);\\n'+'float average = (max(max(col.r, col.g),col.b) + min(min(col.r, col.g),col.b)) / 2.0;\\n'+'gl_FragColor = vec4(average, average, average, col.a);\\n'+'}',luminosity:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform int uMode;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 col = texture2D(uTexture, vTexCoord);\\n'+'float average = 0.21 * col.r + 0.72 * col.g + 0.07 * col.b;\\n'+'gl_FragColor = vec4(average, average, average, col.a);\\n'+'}'},/**\n * Grayscale mode, between 'average', 'lightness', 'luminosity'\n * @param {String} type\n * @default\n */mode:'average',mainParameter:'mode',/**\n * Apply the Grayscale operation to a Uint8Array representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8Array to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,i,len=data.length,value,mode=this.mode;for(i=0;i<len;i+=4){if(mode==='average'){value=(data[i]+data[i+1]+data[i+2])/3;}else if(mode==='lightness'){value=(Math.min(data[i],data[i+1],data[i+2])+Math.max(data[i],data[i+1],data[i+2]))/2;}else if(mode==='luminosity'){value=0.21*data[i]+0.72*data[i+1]+0.07*data[i+2];}data[i]=value;data[i+1]=value;data[i+2]=value;}},/**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){var cacheKey=this.type+'_'+this.mode;if(!options.programCache.hasOwnProperty(cacheKey)){var shaderSource=this.fragmentSource[this.mode];options.programCache[cacheKey]=this.createProgram(options.context,shaderSource);}return options.programCache[cacheKey];},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uMode:gl.getUniformLocation(program,'uMode')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){// default average mode.\nvar mode=1;gl.uniform1i(uniformLocations.uMode,mode);},/**\n * Grayscale filter isNeutralState implementation\n * The filter is never neutral\n * on the image\n **/isNeutralState:function isNeutralState(){return false;}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale\n */fabric.Image.filters.Grayscale.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Invert filter class\n * @class fabric.Image.filters.Invert\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Invert();\n * object.filters.push(filter);\n * object.applyFilters(canvas.renderAll.bind(canvas));\n */filters.Invert=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Invert.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Invert',fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform int uInvert;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'if (uInvert == 1) {\\n'+'gl_FragColor = vec4(1.0 - color.r,1.0 -color.g,1.0 -color.b,color.a);\\n'+'} else {\\n'+'gl_FragColor = color;\\n'+'}\\n'+'}',/**\n * Filter invert. if false, does nothing\n * @param {Boolean} invert\n * @default\n */invert:true,mainParameter:'invert',/**\n * Apply the Invert operation to a Uint8Array representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8Array to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,i,len=data.length;for(i=0;i<len;i+=4){data[i]=255-data[i];data[i+1]=255-data[i+1];data[i+2]=255-data[i+2];}},/**\n * Invert filter isNeutralState implementation\n * Used only in image applyFilters to discard filters that will not have an effect\n * on the image\n * @param {Object} options\n **/isNeutralState:function isNeutralState(){return!this.invert;},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uInvert:gl.getUniformLocation(program,'uInvert')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1i(uniformLocations.uInvert,this.invert);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert\n */fabric.Image.filters.Invert.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Noise filter class\n * @class fabric.Image.filters.Noise\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Noise({\n * noise: 700\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.Noise=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Noise.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Noise',/**\n * Fragment source for the noise program\n */fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uStepH;\\n'+'uniform float uNoise;\\n'+'uniform float uSeed;\\n'+'varying vec2 vTexCoord;\\n'+'float rand(vec2 co, float seed, float vScale) {\\n'+'return fract(sin(dot(co.xy * vScale ,vec2(12.9898 , 78.233))) * 43758.5453 * (seed + 0.01) / 2.0);\\n'+'}\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'color.rgb += (0.5 - rand(vTexCoord, uSeed, 0.1 / uStepH)) * uNoise;\\n'+'gl_FragColor = color;\\n'+'}',/**\n * Describe the property that is the filter parameter\n * @param {String} m\n * @default\n */mainParameter:'noise',/**\n * Noise value, from\n * @param {Number} noise\n * @default\n */noise:0,/**\n * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){if(this.noise===0){return;}var imageData=options.imageData,data=imageData.data,i,len=data.length,noise=this.noise,rand;for(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;}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uNoise:gl.getUniformLocation(program,'uNoise'),uSeed:gl.getUniformLocation(program,'uSeed')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1f(uniformLocations.uNoise,this.noise/255);gl.uniform1f(uniformLocations.uSeed,Math.random());},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return extend(this.callSuper('toObject'),{noise:this.noise});}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise\n */fabric.Image.filters.Noise.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Pixelate filter class\n * @class fabric.Image.filters.Pixelate\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Pixelate({\n * blocksize: 8\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Pixelate=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Pixelate.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Pixelate',blocksize:4,mainParameter:'blocksize',/**\n * Fragment source for the Pixelate program\n */fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uBlocksize;\\n'+'uniform float uStepW;\\n'+'uniform float uStepH;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'float blockW = uBlocksize * uStepW;\\n'+'float blockH = uBlocksize * uStepW;\\n'+'int posX = int(vTexCoord.x / blockW);\\n'+'int posY = int(vTexCoord.y / blockH);\\n'+'float fposX = float(posX);\\n'+'float fposY = float(posY);\\n'+'vec2 squareCoords = vec2(fposX * blockW, fposY * blockH);\\n'+'vec4 color = texture2D(uTexture, squareCoords);\\n'+'gl_FragColor = color;\\n'+'}',/**\n * Apply the Pixelate operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,iLen=imageData.height,jLen=imageData.width,index,i,j,r,g,b,a,_i,_j,_iLen,_jLen;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];_iLen=Math.min(i+this.blocksize,iLen);_jLen=Math.min(j+this.blocksize,jLen);for(_i=i;_i<_iLen;_i++){for(_j=j;_j<_jLen;_j++){index=_i*4*jLen+_j*4;data[index]=r;data[index+1]=g;data[index+2]=b;data[index+3]=a;}}}}},/**\n * Indicate when the filter is not gonna apply changes to the image\n **/isNeutralState:function isNeutralState(){return this.blocksize===1;},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uBlocksize:gl.getUniformLocation(program,'uBlocksize'),uStepW:gl.getUniformLocation(program,'uStepW'),uStepH:gl.getUniformLocation(program,'uStepH')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1f(uniformLocations.uBlocksize,this.blocksize);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate\n */fabric.Image.filters.Pixelate.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),extend=fabric.util.object.extend,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Remove white filter class\n * @class fabric.Image.filters.RemoveColor\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.RemoveColor#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.RemoveColor({\n * threshold: 0.2,\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.RemoveColor=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.RemoveColor.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'RemoveColor',/**\n * Color to remove, in any format understood by fabric.Color.\n * @param {String} type\n * @default\n */color:'#FFFFFF',/**\n * Fragment source for the brightness program\n */fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform vec4 uLow;\\n'+'uniform vec4 uHigh;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'gl_FragColor = texture2D(uTexture, vTexCoord);\\n'+'if(all(greaterThan(gl_FragColor.rgb,uLow.rgb)) && all(greaterThan(uHigh.rgb,gl_FragColor.rgb))) {\\n'+'gl_FragColor.a = 0.0;\\n'+'}\\n'+'}',/**\n * distance to actual color, as value up or down from each r,g,b\n * between 0 and 1\n **/distance:0.02,/**\n * For color to remove inside distance, use alpha channel for a smoother deletion\n * NOT IMPLEMENTED YET\n **/useAlpha:false,/**\n * Constructor\n * @memberOf fabric.Image.filters.RemoveWhite.prototype\n * @param {Object} [options] Options object\n * @param {Number} [options.color=#RRGGBB] Threshold value\n * @param {Number} [options.distance=10] Distance value\n */ /**\n * Applies filter to canvas element\n * @param {Object} canvasEl Canvas element to apply filter to\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,i,distance=this.distance*255,r,g,b,source=new fabric.Color(this.color).getSource(),lowC=[source[0]-distance,source[1]-distance,source[2]-distance],highC=[source[0]+distance,source[1]+distance,source[2]+distance];for(i=0;i<data.length;i+=4){r=data[i];g=data[i+1];b=data[i+2];if(r>lowC[0]&&g>lowC[1]&&b>lowC[2]&&r<highC[0]&&g<highC[1]&&b<highC[2]){data[i+3]=0;}}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uLow:gl.getUniformLocation(program,'uLow'),uHigh:gl.getUniformLocation(program,'uHigh')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){var source=new fabric.Color(this.color).getSource(),distance=parseFloat(this.distance),lowC=[0+source[0]/255-distance,0+source[1]/255-distance,0+source[2]/255-distance,1],highC=[source[0]/255+distance,source[1]/255+distance,source[2]/255+distance,1];gl.uniform4fv(uniformLocations.uLow,lowC);gl.uniform4fv(uniformLocations.uHigh,highC);},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return extend(this.callSuper('toObject'),{color:this.color,distance:this.distance});}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.RemoveColor} Instance of fabric.Image.filters.RemoveWhite\n */fabric.Image.filters.RemoveColor.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;var matrices={Brownie:[0.59970,0.34553,-0.27082,0,0.186,-0.03770,0.86095,0.15059,0,-0.1449,0.24113,-0.07441,0.44972,0,-0.02965,0,0,0,1,0],Vintage:[0.62793,0.32021,-0.03965,0,0.03784,0.02578,0.64411,0.03259,0,0.02926,0.04660,-0.08512,0.52416,0,0.02023,0,0,0,1,0],Kodachrome:[1.12855,-0.39673,-0.03992,0,0.24991,-0.16404,1.08352,-0.05498,0,0.09698,-0.16786,-0.56034,1.60148,0,0.13972,0,0,0,1,0],Technicolor:[1.91252,-0.85453,-0.09155,0,0.04624,-0.30878,1.76589,-0.10601,0,-0.27589,-0.23110,-0.75018,1.84759,0,0.12137,0,0,0,1,0],Polaroid:[1.438,-0.062,-0.062,0,0,-0.122,1.378,-0.122,0,0,-0.016,-0.016,1.483,0,0,0,0,0,1,0],Sepia:[0.393,0.769,0.189,0,0,0.349,0.686,0.168,0,0,0.272,0.534,0.131,0,0,0,0,0,1,0],BlackWhite:[1.5,1.5,1.5,0,-1,1.5,1.5,1.5,0,-1,1.5,1.5,1.5,0,-1,0,0,0,1,0]};for(var key in matrices){filters[key]=createClass(filters.ColorMatrix,/** @lends fabric.Image.filters.Sepia.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:key,/**\n * Colormatrix for the effect\n * array of 20 floats. Numbers in positions 4, 9, 14, 19 loose meaning\n * outside the -1, 1 range.\n * @param {Array} matrix array of 20 numbers.\n * @default\n */matrix:matrices[key],/**\n * Lock the matrix export for this kind of static, parameter less filters.\n */mainParameter:false,/**\n * Lock the colormatrix on the color part, skipping alpha\n */colorsOnly:true});fabric.Image.filters[key].fromObject=fabric.Image.filters.BaseFilter.fromObject;}})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Color Blend filter class\n * @class fabric.Image.filter.BlendColor\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @example\n * var filter = new fabric.Image.filters.BlendColor({\n * color: '#000',\n * mode: 'multiply'\n * });\n *\n * var filter = new fabric.Image.filters.BlendImage({\n * image: fabricImageObject,\n * mode: 'multiply',\n * alpha: 0.5\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.BlendColor=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Blend.prototype */{type:'BlendColor',/**\n * Color to make the blend operation with. default to a reddish color since black or white\n * gives always strong result.\n **/color:'#F95C63',/**\n * Blend mode for the filter: one of multiply, add, diff, screen, subtract,\n * darken, lighten, overlay, exclusion, tint.\n **/mode:'multiply',/**\n * alpha value. represent the strength of the blend color operation.\n **/alpha:1,/**\n * Fragment source for the Multiply program\n */fragmentSource:{multiply:'gl_FragColor.rgb *= uColor.rgb;\\n',screen:'gl_FragColor.rgb = 1.0 - (1.0 - gl_FragColor.rgb) * (1.0 - uColor.rgb);\\n',add:'gl_FragColor.rgb += uColor.rgb;\\n',diff:'gl_FragColor.rgb = abs(gl_FragColor.rgb - uColor.rgb);\\n',subtract:'gl_FragColor.rgb -= uColor.rgb;\\n',lighten:'gl_FragColor.rgb = max(gl_FragColor.rgb, uColor.rgb);\\n',darken:'gl_FragColor.rgb = min(gl_FragColor.rgb, uColor.rgb);\\n',exclusion:'gl_FragColor.rgb += uColor.rgb - 2.0 * (uColor.rgb * gl_FragColor.rgb);\\n',overlay:'if (uColor.r < 0.5) {\\n'+'gl_FragColor.r *= 2.0 * uColor.r;\\n'+'} else {\\n'+'gl_FragColor.r = 1.0 - 2.0 * (1.0 - gl_FragColor.r) * (1.0 - uColor.r);\\n'+'}\\n'+'if (uColor.g < 0.5) {\\n'+'gl_FragColor.g *= 2.0 * uColor.g;\\n'+'} else {\\n'+'gl_FragColor.g = 1.0 - 2.0 * (1.0 - gl_FragColor.g) * (1.0 - uColor.g);\\n'+'}\\n'+'if (uColor.b < 0.5) {\\n'+'gl_FragColor.b *= 2.0 * uColor.b;\\n'+'} else {\\n'+'gl_FragColor.b = 1.0 - 2.0 * (1.0 - gl_FragColor.b) * (1.0 - uColor.b);\\n'+'}\\n',tint:'gl_FragColor.rgb *= (1.0 - uColor.a);\\n'+'gl_FragColor.rgb += uColor.rgb;\\n'},/**\n * build the fragment source for the filters, joining the common part with\n * the specific one.\n * @param {String} mode the mode of the filter, a key of this.fragmentSource\n * @return {String} the source to be compiled\n * @private\n */buildSource:function buildSource(mode){return'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform vec4 uColor;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'gl_FragColor = color;\\n'+'if (color.a > 0.0) {\\n'+this.fragmentSource[mode]+'}\\n'+'}';},/**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){var cacheKey=this.type+'_'+this.mode,shaderSource;if(!options.programCache.hasOwnProperty(cacheKey)){shaderSource=this.buildSource(this.mode);options.programCache[cacheKey]=this.createProgram(options.context,shaderSource);}return options.programCache[cacheKey];},/**\n * Apply the Blend operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,iLen=data.length,tr,tg,tb,r,g,b,source,alpha1=1-this.alpha;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;i<iLen;i+=4){r=data[i];g=data[i+1];b=data[i+2];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]=255-(255-r)*(255-tr)/255;data[i+1]=255-(255-g)*(255-tg)/255;data[i+2]=255-(255-b)*(255-tb)/255;break;case'add':data[i]=r+tr;data[i+1]=g+tg;data[i+2]=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':data[i]=r-tr;data[i+1]=g-tg;data[i+2]=b-tb;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;case'overlay':data[i]=tr<128?2*r*tr/255:255-2*(255-r)*(255-tr)/255;data[i+1]=tg<128?2*g*tg/255:255-2*(255-g)*(255-tg)/255;data[i+2]=tb<128?2*b*tb/255:255-2*(255-b)*(255-tb)/255;break;case'exclusion':data[i]=tr+r-2*tr*r/255;data[i+1]=tg+g-2*tg*g/255;data[i+2]=tb+b-2*tb*b/255;break;case'tint':data[i]=tr+r*alpha1;data[i+1]=tg+g*alpha1;data[i+2]=tb+b*alpha1;}}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uColor:gl.getUniformLocation(program,'uColor')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){var source=new fabric.Color(this.color).getSource();source[0]=this.alpha*source[0]/255;source[1]=this.alpha*source[1]/255;source[2]=this.alpha*source[2]/255;source[3]=this.alpha;gl.uniform4fv(uniformLocations.uColor,source);},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return{type:this.type,color:this.color,mode:this.mode,alpha:this.alpha};}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.BlendColor} Instance of fabric.Image.filters.BlendColor\n */fabric.Image.filters.BlendColor.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Image Blend filter class\n * @class fabric.Image.filter.BlendImage\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @example\n * var filter = new fabric.Image.filters.BlendColor({\n * color: '#000',\n * mode: 'multiply'\n * });\n *\n * var filter = new fabric.Image.filters.BlendImage({\n * image: fabricImageObject,\n * mode: 'multiply',\n * alpha: 0.5\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.BlendImage=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.BlendImage.prototype */{type:'BlendImage',/**\n * Color to make the blend operation with. default to a reddish color since black or white\n * gives always strong result.\n **/image:null,/**\n * Blend mode for the filter: one of multiply, add, diff, screen, subtract,\n * darken, lighten, overlay, exclusion, tint.\n **/mode:'multiply',/**\n * alpha value. represent the strength of the blend image operation.\n * not implemented.\n **/alpha:1,vertexSource:'attribute vec2 aPosition;\\n'+'varying vec2 vTexCoord;\\n'+'varying vec2 vTexCoord2;\\n'+'uniform mat3 uTransformMatrix;\\n'+'void main() {\\n'+'vTexCoord = aPosition;\\n'+'vTexCoord2 = (uTransformMatrix * vec3(aPosition, 1.0)).xy;\\n'+'gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\\n'+'}',/**\n * Fragment source for the Multiply program\n */fragmentSource:{multiply:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform sampler2D uImage;\\n'+'uniform vec4 uColor;\\n'+'varying vec2 vTexCoord;\\n'+'varying vec2 vTexCoord2;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'vec4 color2 = texture2D(uImage, vTexCoord2);\\n'+'color.rgba *= color2.rgba;\\n'+'gl_FragColor = color;\\n'+'}',mask:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform sampler2D uImage;\\n'+'uniform vec4 uColor;\\n'+'varying vec2 vTexCoord;\\n'+'varying vec2 vTexCoord2;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'vec4 color2 = texture2D(uImage, vTexCoord2);\\n'+'color.a = color2.a;\\n'+'gl_FragColor = color;\\n'+'}'},/**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){var cacheKey=this.type+'_'+this.mode;var shaderSource=this.fragmentSource[this.mode];if(!options.programCache.hasOwnProperty(cacheKey)){options.programCache[cacheKey]=this.createProgram(options.context,shaderSource);}return options.programCache[cacheKey];},applyToWebGL:function applyToWebGL(options){// load texture to blend.\nvar gl=options.context,texture=this.createTexture(options.filterBackend,this.image);this.bindAdditionalTexture(gl,texture,gl.TEXTURE1);this.callSuper('applyToWebGL',options);this.unbindAdditionalTexture(gl,gl.TEXTURE1);},createTexture:function createTexture(backend,image){return backend.getCachedTexture(image.cacheKey,image._element);},/**\n * Calculate a transformMatrix to adapt the image to blend over\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */calculateMatrix:function calculateMatrix(){var image=this.image,width=image._element.width,height=image._element.height;return[1/image.scaleX,0,0,0,1/image.scaleY,0,-image.left/width,-image.top/height,1];},/**\n * Apply the Blend operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,resources=options.filterBackend.resources,data=imageData.data,iLen=data.length,width=imageData.width,height=imageData.height,tr,tg,tb,ta,r,g,b,a,canvas1,context,image=this.image,blendData;if(!resources.blendImage){resources.blendImage=fabric.util.createCanvasElement();}canvas1=resources.blendImage;context=canvas1.getContext('2d');if(canvas1.width!==width||canvas1.height!==height){canvas1.width=width;canvas1.height=height;}else{context.clearRect(0,0,width,height);}context.setTransform(image.scaleX,0,0,image.scaleY,image.left,image.top);context.drawImage(image._element,0,0,width,height);blendData=context.getImageData(0,0,width,height).data;for(var i=0;i<iLen;i+=4){r=data[i];g=data[i+1];b=data[i+2];a=data[i+3];tr=blendData[i];tg=blendData[i+1];tb=blendData[i+2];ta=blendData[i+3];switch(this.mode){case'multiply':data[i]=r*tr/255;data[i+1]=g*tg/255;data[i+2]=b*tb/255;data[i+3]=a*ta/255;break;case'mask':data[i+3]=ta;break;}}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uTransformMatrix:gl.getUniformLocation(program,'uTransformMatrix'),uImage:gl.getUniformLocation(program,'uImage')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){var matrix=this.calculateMatrix();gl.uniform1i(uniformLocations.uImage,1);// texture unit 1.\ngl.uniformMatrix3fv(uniformLocations.uTransformMatrix,false,matrix);},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return{type:this.type,image:this.image&&this.image.toObject(),mode:this.mode,alpha:this.alpha};}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} callback to be invoked after filter creation\n * @return {fabric.Image.filters.BlendImage} Instance of fabric.Image.filters.BlendImage\n */fabric.Image.filters.BlendImage.fromObject=function(object,callback){fabric.Image.fromObject(object.image,function(image){var options=fabric.util.object.clone(object);options.image=image;callback(new fabric.Image.filters.BlendImage(options));});};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),pow=Math.pow,floor=Math.floor,sqrt=Math.sqrt,abs=Math.abs,round=Math.round,sin=Math.sin,ceil=Math.ceil,filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Resize image filter class\n * @class fabric.Image.filters.Resize\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Resize();\n * object.filters.push(filter);\n * object.applyFilters(canvas.renderAll.bind(canvas));\n */filters.Resize=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Resize.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Resize',/**\n * Resize type\n * for webgl resizeType is just lanczos, for canvas2d can be:\n * bilinear, hermite, sliceHack, lanczos.\n * @param {String} resizeType\n * @default\n */resizeType:'hermite',/**\n * Scale factor for resizing, x axis\n * @param {Number} scaleX\n * @default\n */scaleX:1,/**\n * Scale factor for resizing, y axis\n * @param {Number} scaleY\n * @default\n */scaleY:1,/**\n * LanczosLobes parameter for lanczos filter, valid for resizeType lanczos\n * @param {Number} lanczosLobes\n * @default\n */lanczosLobes:3,/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uDelta:gl.getUniformLocation(program,'uDelta'),uTaps:gl.getUniformLocation(program,'uTaps')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform2fv(uniformLocations.uDelta,this.horizontal?[1/this.width,0]:[0,1/this.height]);gl.uniform1fv(uniformLocations.uTaps,this.taps);},/**\n * Retrieves the cached shader.\n * @param {Object} options\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */retrieveShader:function retrieveShader(options){var filterWindow=this.getFilterWindow(),cacheKey=this.type+'_'+filterWindow;if(!options.programCache.hasOwnProperty(cacheKey)){var fragmentShader=this.generateShader(filterWindow);options.programCache[cacheKey]=this.createProgram(options.context,fragmentShader);}return options.programCache[cacheKey];},getFilterWindow:function getFilterWindow(){var scale=this.tempScale;return Math.ceil(this.lanczosLobes/scale);},getTaps:function getTaps(){var lobeFunction=this.lanczosCreate(this.lanczosLobes),scale=this.tempScale,filterWindow=this.getFilterWindow(),taps=new Array(filterWindow);for(var i=1;i<=filterWindow;i++){taps[i-1]=lobeFunction(i*scale);}return taps;},/**\n * Generate vertex and shader sources from the necessary steps numbers\n * @param {Number} filterWindow\n */generateShader:function generateShader(filterWindow){var offsets=new Array(filterWindow),fragmentShader=this.fragmentSourceTOP,filterWindow;for(var i=1;i<=filterWindow;i++){offsets[i-1]=i+'.0 * uDelta';}fragmentShader+='uniform float uTaps['+filterWindow+'];\\n';fragmentShader+='void main() {\\n';fragmentShader+=' vec4 color = texture2D(uTexture, vTexCoord);\\n';fragmentShader+=' float sum = 1.0;\\n';offsets.forEach(function(offset,i){fragmentShader+=' color += texture2D(uTexture, vTexCoord + '+offset+') * uTaps['+i+'];\\n';fragmentShader+=' color += texture2D(uTexture, vTexCoord - '+offset+') * uTaps['+i+'];\\n';fragmentShader+=' sum += 2.0 * uTaps['+i+'];\\n';});fragmentShader+=' gl_FragColor = color / sum;\\n';fragmentShader+='}';return fragmentShader;},fragmentSourceTOP:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform vec2 uDelta;\\n'+'varying vec2 vTexCoord;\\n',/**\n * Apply the resize filter to the image\n * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.\n *\n * @param {Object} options\n * @param {Number} options.passes The number of filters remaining to be executed\n * @param {Boolean} options.webgl Whether to use webgl to render the filter.\n * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.\n * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */applyTo:function applyTo(options){if(options.webgl){options.passes++;this.width=options.sourceWidth;this.horizontal=true;this.dW=Math.round(this.width*this.scaleX);this.dH=options.sourceHeight;this.tempScale=this.dW/this.width;this.taps=this.getTaps();options.destinationWidth=this.dW;this._setupFrameBuffer(options);this.applyToWebGL(options);this._swapTextures(options);options.sourceWidth=options.destinationWidth;this.height=options.sourceHeight;this.horizontal=false;this.dH=Math.round(this.height*this.scaleY);this.tempScale=this.dH/this.height;this.taps=this.getTaps();options.destinationHeight=this.dH;this._setupFrameBuffer(options);this.applyToWebGL(options);this._swapTextures(options);options.sourceHeight=options.destinationHeight;}else{this.applyTo2d(options);}},isNeutralState:function isNeutralState(){return this.scaleX===1&&this.scaleY===1;},lanczosCreate:function lanczosCreate(lobes){return function(x){if(x>=lobes||x<=-lobes){return 0.0;}if(x<1.19209290E-07&&x>-1.19209290E-07){return 1.0;}x*=Math.PI;var xx=x/lobes;return sin(x)/x*sin(xx)/xx;};},/**\n * Applies filter to canvas element\n * @memberOf fabric.Image.filters.Resize.prototype\n * @param {Object} canvasEl Canvas element to apply filter to\n * @param {Number} scaleX\n * @param {Number} scaleY\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,scaleX=this.scaleX,scaleY=this.scaleY;this.rcpScaleX=1/scaleX;this.rcpScaleY=1/scaleY;var oW=imageData.width,oH=imageData.height,dW=round(oW*scaleX),dH=round(oH*scaleY),newData;if(this.resizeType==='sliceHack'){newData=this.sliceByTwo(options,oW,oH,dW,dH);}else if(this.resizeType==='hermite'){newData=this.hermiteFastResize(options,oW,oH,dW,dH);}else if(this.resizeType==='bilinear'){newData=this.bilinearFiltering(options,oW,oH,dW,dH);}else if(this.resizeType==='lanczos'){newData=this.lanczosResize(options,oW,oH,dW,dH);}options.imageData=newData;},/**\n * Filter sliceByTwo\n * @param {Object} canvasEl Canvas element to apply filter to\n * @param {Number} oW Original Width\n * @param {Number} oH Original Height\n * @param {Number} dW Destination Width\n * @param {Number} dH Destination Height\n * @returns {ImageData}\n */sliceByTwo:function sliceByTwo(options,oW,oH,dW,dH){var imageData=options.imageData,mult=0.5,doneW=false,doneH=false,stepW=oW*mult,stepH=oH*mult,resources=fabric.filterBackend.resources,tmpCanvas,ctx,sX=0,sY=0,dX=oW,dY=0;if(!resources.sliceByTwo){resources.sliceByTwo=document.createElement('canvas');}tmpCanvas=resources.sliceByTwo;if(tmpCanvas.width<oW*1.5||tmpCanvas.height<oH){tmpCanvas.width=oW*1.5;tmpCanvas.height=oH;}ctx=tmpCanvas.getContext('2d');ctx.clearRect(0,0,oW*1.5,oH);ctx.putImageData(imageData,0,0);dW=floor(dW);dH=floor(dH);while(!doneW||!doneH){oW=stepW;oH=stepH;if(dW<floor(stepW*mult)){stepW=floor(stepW*mult);}else{stepW=dW;doneW=true;}if(dH<floor(stepH*mult)){stepH=floor(stepH*mult);}else{stepH=dH;doneH=true;}ctx.drawImage(tmpCanvas,sX,sY,oW,oH,dX,dY,stepW,stepH);sX=dX;sY=dY;dY+=stepH;}return ctx.getImageData(sX,sY,dW,dH);},/**\n * Filter lanczosResize\n * @param {Object} canvasEl Canvas element to apply filter to\n * @param {Number} oW Original Width\n * @param {Number} oH Original Height\n * @param {Number} dW Destination Width\n * @param {Number} dH Destination Height\n * @returns {ImageData}\n */lanczosResize:function lanczosResize(options,oW,oH,dW,dH){function process(u){var v,i,weight,idx,a,red,green,blue,alpha,fX,fY;center.x=(u+0.5)*ratioX;icenter.x=floor(center.x);for(v=0;v<dH;v++){center.y=(v+0.5)*ratioY;icenter.y=floor(center.y);a=0;red=0;green=0;blue=0;alpha=0;for(i=icenter.x-range2X;i<=icenter.x+range2X;i++){if(i<0||i>=oW){continue;}fX=floor(1000*abs(i-center.x));if(!cacheLanc[fX]){cacheLanc[fX]={};}for(var j=icenter.y-range2Y;j<=icenter.y+range2Y;j++){if(j<0||j>=oH){continue;}fY=floor(1000*abs(j-center.y));if(!cacheLanc[fX][fY]){cacheLanc[fX][fY]=lanczos(sqrt(pow(fX*rcpRatioX,2)+pow(fY*rcpRatioY,2))/1000);}weight=cacheLanc[fX][fY];if(weight>0){idx=(j*oW+i)*4;a+=weight;red+=weight*srcData[idx];green+=weight*srcData[idx+1];blue+=weight*srcData[idx+2];alpha+=weight*srcData[idx+3];}}}idx=(v*dW+u)*4;destData[idx]=red/a;destData[idx+1]=green/a;destData[idx+2]=blue/a;destData[idx+3]=alpha/a;}if(++u<dW){return process(u);}else{return destImg;}}var srcData=options.imageData.data,destImg=options.ctx.createImageData(dW,dH),destData=destImg.data,lanczos=this.lanczosCreate(this.lanczosLobes),ratioX=this.rcpScaleX,ratioY=this.rcpScaleY,rcpRatioX=2/this.rcpScaleX,rcpRatioY=2/this.rcpScaleY,range2X=ceil(ratioX*this.lanczosLobes/2),range2Y=ceil(ratioY*this.lanczosLobes/2),cacheLanc={},center={},icenter={};return process(0);},/**\n * bilinearFiltering\n * @param {Object} canvasEl Canvas element to apply filter to\n * @param {Number} oW Original Width\n * @param {Number} oH Original Height\n * @param {Number} dW Destination Width\n * @param {Number} dH Destination Height\n * @returns {ImageData}\n */bilinearFiltering:function bilinearFiltering(options,oW,oH,dW,dH){var a,b,c,d,x,y,i,j,xDiff,yDiff,chnl,color,offset=0,origPix,ratioX=this.rcpScaleX,ratioY=this.rcpScaleY,w4=4*(oW-1),img=options.imageData,pixels=img.data,destImage=options.ctx.createImageData(dW,dH),destPixels=destImage.data;for(i=0;i<dH;i++){for(j=0;j<dW;j++){x=floor(ratioX*j);y=floor(ratioY*i);xDiff=ratioX*j-x;yDiff=ratioY*i-y;origPix=4*(y*oW+x);for(chnl=0;chnl<4;chnl++){a=pixels[origPix+chnl];b=pixels[origPix+4+chnl];c=pixels[origPix+w4+chnl];d=pixels[origPix+w4+4+chnl];color=a*(1-xDiff)*(1-yDiff)+b*xDiff*(1-yDiff)+c*yDiff*(1-xDiff)+d*xDiff*yDiff;destPixels[offset++]=color;}}}return destImage;},/**\n * hermiteFastResize\n * @param {Object} canvasEl Canvas element to apply filter to\n * @param {Number} oW Original Width\n * @param {Number} oH Original Height\n * @param {Number} dW Destination Width\n * @param {Number} dH Destination Height\n * @returns {ImageData}\n */hermiteFastResize:function hermiteFastResize(options,oW,oH,dW,dH){var ratioW=this.rcpScaleX,ratioH=this.rcpScaleY,ratioWHalf=ceil(ratioW/2),ratioHHalf=ceil(ratioH/2),img=options.imageData,data=img.data,img2=options.ctx.createImageData(dW,dH),data2=img2.data;for(var j=0;j<dH;j++){for(var i=0;i<dW;i++){var x2=(i+j*dW)*4,weight=0,weights=0,weightsAlpha=0,gxR=0,gxG=0,gxB=0,gxA=0,centerY=(j+0.5)*ratioH;for(var yy=floor(j*ratioH);yy<(j+1)*ratioH;yy++){var dy=abs(centerY-(yy+0.5))/ratioHHalf,centerX=(i+0.5)*ratioW,w0=dy*dy;for(var xx=floor(i*ratioW);xx<(i+1)*ratioW;xx++){var dx=abs(centerX-(xx+0.5))/ratioWHalf,w=sqrt(w0+dx*dx);/* eslint-disable max-depth */if(w>1&&w<-1){continue;}//hermite filter\nweight=2*w*w*w-3*w*w+1;if(weight>0){dx=4*(xx+yy*oW);//alpha\ngxA+=weight*data[dx+3];weightsAlpha+=weight;//colors\nif(data[dx+3]<255){weight=weight*data[dx+3]/250;}gxR+=weight*data[dx];gxG+=weight*data[dx+1];gxB+=weight*data[dx+2];weights+=weight;}/* eslint-enable max-depth */}}data2[x2]=gxR/weights;data2[x2+1]=gxG/weights;data2[x2+2]=gxB/weights;data2[x2+3]=gxA/weightsAlpha;}}return img2;},/**\n * Returns object representation of an instance\n * @return {Object} Object representation of an instance\n */toObject:function toObject(){return{type:this.type,scaleX:this.scaleX,scaleY:this.scaleY,resizeType:this.resizeType,lanczosLobes:this.lanczosLobes};}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Resize} Instance of fabric.Image.filters.Resize\n */fabric.Image.filters.Resize.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Contrast filter class\n * @class fabric.Image.filters.Contrast\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Contrast#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Contrast({\n * contrast: 40\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Contrast=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Contrast.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Contrast',fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uContrast;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'float contrastF = 1.015 * (uContrast + 1.0) / (1.0 * (1.015 - uContrast));\\n'+'color.rgb = contrastF * (color.rgb - 0.5) + 0.5;\\n'+'gl_FragColor = color;\\n'+'}',contrast:0,mainParameter:'contrast',/**\n * Constructor\n * @memberOf fabric.Image.filters.Contrast.prototype\n * @param {Object} [options] Options object\n * @param {Number} [options.contrast=0] Value to contrast the image up (-1...1)\n */ /**\n * Apply the Contrast operation to a Uint8Array representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8Array to be filtered.\n */applyTo2d:function applyTo2d(options){if(this.contrast===0){return;}var imageData=options.imageData,i,len,data=imageData.data,len=data.length,contrast=Math.floor(this.contrast*255),contrastF=259*(contrast+255)/(255*(259-contrast));for(i=0;i<len;i+=4){data[i]=contrastF*(data[i]-128)+128;data[i+1]=contrastF*(data[i+1]-128)+128;data[i+2]=contrastF*(data[i+2]-128)+128;}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uContrast:gl.getUniformLocation(program,'uContrast')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1f(uniformLocations.uContrast,this.contrast);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Contrast} Instance of fabric.Image.filters.Contrast\n */fabric.Image.filters.Contrast.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Saturate filter class\n * @class fabric.Image.filters.Saturation\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Saturation#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Saturation({\n * saturation: 100\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Saturation=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Saturation.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Saturation',fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform float uSaturation;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'float rgMax = max(color.r, color.g);\\n'+'float rgbMax = max(rgMax, color.b);\\n'+'color.r += rgbMax != color.r ? (rgbMax - color.r) * uSaturation : 0.00;\\n'+'color.g += rgbMax != color.g ? (rgbMax - color.g) * uSaturation : 0.00;\\n'+'color.b += rgbMax != color.b ? (rgbMax - color.b) * uSaturation : 0.00;\\n'+'gl_FragColor = color;\\n'+'}',saturation:0,mainParameter:'saturation',/**\n * Constructor\n * @memberOf fabric.Image.filters.Saturate.prototype\n * @param {Object} [options] Options object\n * @param {Number} [options.saturate=0] Value to saturate the image (-1...1)\n */ /**\n * Apply the Saturation operation to a Uint8ClampedArray representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.\n */applyTo2d:function applyTo2d(options){if(this.saturation===0){return;}var imageData=options.imageData,data=imageData.data,len=data.length,adjust=-this.saturation,i,max;for(i=0;i<len;i+=4){max=Math.max(data[i],data[i+1],data[i+2]);data[i]+=max!==data[i]?(max-data[i])*adjust:0;data[i+1]+=max!==data[i+1]?(max-data[i+1])*adjust:0;data[i+2]+=max!==data[i+2]?(max-data[i+2])*adjust:0;}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uSaturation:gl.getUniformLocation(program,'uSaturation')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform1f(uniformLocations.uSaturation,-this.saturation);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Saturation} Instance of fabric.Image.filters.Saturate\n */fabric.Image.filters.Saturation.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Blur filter class\n * @class fabric.Image.filters.Blur\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Blur#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Blur({\n * blur: 0.5\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */filters.Blur=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Blur.prototype */{type:'Blur',/*\n'gl_FragColor = vec4(0.0);',\n'gl_FragColor += texture2D(texture, vTexCoord + -7 * uDelta)*0.0044299121055113265;',\n'gl_FragColor += texture2D(texture, vTexCoord + -6 * uDelta)*0.00895781211794;',\n'gl_FragColor += texture2D(texture, vTexCoord + -5 * uDelta)*0.0215963866053;',\n'gl_FragColor += texture2D(texture, vTexCoord + -4 * uDelta)*0.0443683338718;',\n'gl_FragColor += texture2D(texture, vTexCoord + -3 * uDelta)*0.0776744219933;',\n'gl_FragColor += texture2D(texture, vTexCoord + -2 * uDelta)*0.115876621105;',\n'gl_FragColor += texture2D(texture, vTexCoord + -1 * uDelta)*0.147308056121;',\n'gl_FragColor += texture2D(texture, vTexCoord )*0.159576912161;',\n'gl_FragColor += texture2D(texture, vTexCoord + 1 * uDelta)*0.147308056121;',\n'gl_FragColor += texture2D(texture, vTexCoord + 2 * uDelta)*0.115876621105;',\n'gl_FragColor += texture2D(texture, vTexCoord + 3 * uDelta)*0.0776744219933;',\n'gl_FragColor += texture2D(texture, vTexCoord + 4 * uDelta)*0.0443683338718;',\n'gl_FragColor += texture2D(texture, vTexCoord + 5 * uDelta)*0.0215963866053;',\n'gl_FragColor += texture2D(texture, vTexCoord + 6 * uDelta)*0.00895781211794;',\n'gl_FragColor += texture2D(texture, vTexCoord + 7 * uDelta)*0.0044299121055113265;',\n*/ /* eslint-disable max-len */fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform vec2 uDelta;\\n'+'varying vec2 vTexCoord;\\n'+'const float nSamples = 15.0;\\n'+'vec3 v3offset = vec3(12.9898, 78.233, 151.7182);\\n'+'float random(vec3 scale) {\\n'+/* use the fragment position for a different seed per-pixel */'return fract(sin(dot(gl_FragCoord.xyz, scale)) * 43758.5453);\\n'+'}\\n'+'void main() {\\n'+'vec4 color = vec4(0.0);\\n'+'float total = 0.0;\\n'+'float offset = random(v3offset);\\n'+'for (float t = -nSamples; t <= nSamples; t++) {\\n'+'float percent = (t + offset - 0.5) / nSamples;\\n'+'float weight = 1.0 - abs(percent);\\n'+'color += texture2D(uTexture, vTexCoord + uDelta * percent) * weight;\\n'+'total += weight;\\n'+'}\\n'+'gl_FragColor = color / total;\\n'+'}',/* eslint-enable max-len */ /**\n * blur value, in percentage of image dimensions.\n * specific to keep the image blur constant at different resolutions\n * range bewteen 0 and 1.\n */blur:0,mainParameter:'blur',applyTo:function applyTo(options){if(options.webgl){// this aspectRatio is used to give the same blur to vertical and horizontal\nthis.aspectRatio=options.sourceWidth/options.sourceHeight;options.passes++;this._setupFrameBuffer(options);this.horizontal=true;this.applyToWebGL(options);this._swapTextures(options);this._setupFrameBuffer(options);this.horizontal=false;this.applyToWebGL(options);this._swapTextures(options);}else{this.applyTo2d(options);}},applyTo2d:function applyTo2d(options){// paint canvasEl with current image data.\n//options.ctx.putImageData(options.imageData, 0, 0);\noptions.imageData=this.simpleBlur(options);},simpleBlur:function simpleBlur(options){var resources=options.filterBackend.resources,canvas1,canvas2,width=options.imageData.width,height=options.imageData.height;if(!resources.blurLayer1){resources.blurLayer1=fabric.util.createCanvasElement();resources.blurLayer2=fabric.util.createCanvasElement();}canvas1=resources.blurLayer1;canvas2=resources.blurLayer2;if(canvas1.width!==width||canvas1.height!==height){canvas2.width=canvas1.width=width;canvas2.height=canvas1.height=height;}var ctx1=canvas1.getContext('2d'),ctx2=canvas2.getContext('2d'),nSamples=15,random,percent,j,i,blur=this.blur*0.06*0.5;// load first canvas\nctx1.putImageData(options.imageData,0,0);ctx2.clearRect(0,0,width,height);for(i=-nSamples;i<=nSamples;i++){random=(Math.random()-0.5)/4;percent=i/nSamples;j=blur*percent*width+random;ctx2.globalAlpha=1-Math.abs(percent);ctx2.drawImage(canvas1,j,random);ctx1.drawImage(canvas2,0,0);ctx2.globalAlpha=1;ctx2.clearRect(0,0,canvas2.width,canvas2.height);}for(i=-nSamples;i<=nSamples;i++){random=(Math.random()-0.5)/4;percent=i/nSamples;j=blur*percent*height+random;ctx2.globalAlpha=1-Math.abs(percent);ctx2.drawImage(canvas1,random,j);ctx1.drawImage(canvas2,0,0);ctx2.globalAlpha=1;ctx2.clearRect(0,0,canvas2.width,canvas2.height);}options.ctx.drawImage(canvas1,0,0);var newImageData=options.ctx.getImageData(0,0,canvas1.width,canvas1.height);ctx1.globalAlpha=1;ctx1.clearRect(0,0,canvas1.width,canvas1.height);return newImageData;},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{delta:gl.getUniformLocation(program,'uDelta')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){var delta=this.chooseRightDelta();gl.uniform2fv(uniformLocations.delta,delta);},/**\n * choose right value of image percentage to blur with\n * @returns {Array} a numeric array with delta values\n */chooseRightDelta:function chooseRightDelta(){var blurScale=1,delta=[0,0],blur;if(this.horizontal){if(this.aspectRatio>1){// image is wide, i want to shrink radius horizontal\nblurScale=1/this.aspectRatio;}}else{if(this.aspectRatio<1){// image is tall, i want to shrink radius vertical\nblurScale=this.aspectRatio;}}blur=blurScale*this.blur*0.12;if(this.horizontal){delta[0]=blur;}else{delta[1]=blur;}return delta;}});/**\n * Deserialize a JSON definition of a BlurFilter into a concrete instance.\n */filters.Blur.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * Gamma filter class\n * @class fabric.Image.filters.Gamma\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.Gamma#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.Gamma({\n * brightness: 200\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.Gamma=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Gamma.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'Gamma',fragmentSource:'precision highp float;\\n'+'uniform sampler2D uTexture;\\n'+'uniform vec3 uGamma;\\n'+'varying vec2 vTexCoord;\\n'+'void main() {\\n'+'vec4 color = texture2D(uTexture, vTexCoord);\\n'+'vec3 correction = (1.0 / uGamma);\\n'+'color.r = pow(color.r, correction.r);\\n'+'color.g = pow(color.g, correction.g);\\n'+'color.b = pow(color.b, correction.b);\\n'+'gl_FragColor = color;\\n'+'gl_FragColor.rgb *= color.a;\\n'+'}',/**\n * Gamma array value, from 0.01 to 2.2.\n * @param {Array} gamma\n * @default\n */gamma:[1,1,1],/**\n * Describe the property that is the filter parameter\n * @param {String} m\n * @default\n */mainParameter:'gamma',/**\n * Constructor\n * @param {Object} [options] Options object\n */initialize:function initialize(options){this.gamma=[1,1,1];filters.BaseFilter.prototype.initialize.call(this,options);},/**\n * Apply the Gamma operation to a Uint8Array representing the pixels of an image.\n *\n * @param {Object} options\n * @param {ImageData} options.imageData The Uint8Array to be filtered.\n */applyTo2d:function applyTo2d(options){var imageData=options.imageData,data=imageData.data,gamma=this.gamma,len=data.length,rInv=1/gamma[0],gInv=1/gamma[1],bInv=1/gamma[2],i;if(!this.rVals){// eslint-disable-next-line\nthis.rVals=new Uint8Array(256);// eslint-disable-next-line\nthis.gVals=new Uint8Array(256);// eslint-disable-next-line\nthis.bVals=new Uint8Array(256);}// This is an optimization - pre-compute a look-up table for each color channel\n// instead of performing these pow calls for each pixel in the image.\nfor(i=0,len=256;i<len;i++){this.rVals[i]=Math.pow(i/255,rInv)*255;this.gVals[i]=Math.pow(i/255,gInv)*255;this.bVals[i]=Math.pow(i/255,bInv)*255;}for(i=0,len=data.length;i<len;i+=4){data[i]=this.rVals[data[i]];data[i+1]=this.gVals[data[i+1]];data[i+2]=this.bVals[data[i+2]];}},/**\n * Return WebGL uniform locations for this filter's shader.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {WebGLShaderProgram} program This filter's compiled shader program.\n */getUniformLocations:function getUniformLocations(gl,program){return{uGamma:gl.getUniformLocation(program,'uGamma')};},/**\n * Send data from this filter to its shader program's uniforms.\n *\n * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n */sendUniformData:function sendUniformData(gl,uniformLocations){gl.uniform3fv(uniformLocations.uGamma,this.gamma);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.Gamma} Instance of fabric.Image.filters.Gamma\n */fabric.Image.filters.Gamma.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * A container class that knows how to apply a sequence of filters to an input image.\n */filters.Composed=createClass(filters.BaseFilter,/** @lends fabric.Image.filters.Composed.prototype */{type:'Composed',/**\n * A non sparse array of filters to apply\n */subFilters:[],/**\n * Constructor\n * @param {Object} [options] Options object\n */initialize:function initialize(options){this.callSuper('initialize',options);// create a new array instead mutating the prototype with push\nthis.subFilters=this.subFilters.slice(0);},/**\n * Apply this container's filters to the input image provided.\n *\n * @param {Object} options\n * @param {Number} options.passes The number of filters remaining to be applied.\n */applyTo:function applyTo(options){options.passes+=this.subFilters.length-1;this.subFilters.forEach(function(filter){filter.applyTo(options);});},/**\n * Serialize this filter into JSON.\n *\n * @returns {Object} A JSON representation of this filter.\n */toObject:function toObject(){return fabric.util.object.extend(this.callSuper('toObject'),{subFilters:this.subFilters.map(function(filter){return filter.toObject();})});},isNeutralState:function isNeutralState(){return!this.subFilters.some(function(filter){return!filter.isNeutralState();});}});/**\n * Deserialize a JSON definition of a ComposedFilter into a concrete instance.\n */fabric.Image.filters.Composed.fromObject=function(object,callback){var filters=object.subFilters||[],subFilters=filters.map(function(filter){return new fabric.Image.filters[filter.type](filter);}),instance=new fabric.Image.filters.Composed({subFilters:subFilters});callback&&callback(instance);return instance;};})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),filters=fabric.Image.filters,createClass=fabric.util.createClass;/**\n * HueRotation filter class\n * @class fabric.Image.filters.HueRotation\n * @memberOf fabric.Image.filters\n * @extends fabric.Image.filters.BaseFilter\n * @see {@link fabric.Image.filters.HueRotation#initialize} for constructor definition\n * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}\n * @example\n * var filter = new fabric.Image.filters.HueRotation({\n * rotation: -0.5\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n */filters.HueRotation=createClass(filters.ColorMatrix,/** @lends fabric.Image.filters.HueRotation.prototype */{/**\n * Filter type\n * @param {String} type\n * @default\n */type:'HueRotation',/**\n * HueRotation value, from -1 to 1.\n * the unit is radians\n * @param {Number} myParameter\n * @default\n */rotation:0,/**\n * Describe the property that is the filter parameter\n * @param {String} m\n * @default\n */mainParameter:'rotation',calculateMatrix:function calculateMatrix(){var rad=this.rotation*Math.PI,cos=fabric.util.cos(rad),sin=fabric.util.sin(rad),aThird=1/3,aThirdSqtSin=Math.sqrt(aThird)*sin,OneMinusCos=1-cos;this.matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];this.matrix[0]=cos+OneMinusCos/3;this.matrix[1]=aThird*OneMinusCos-aThirdSqtSin;this.matrix[2]=aThird*OneMinusCos+aThirdSqtSin;this.matrix[5]=aThird*OneMinusCos+aThirdSqtSin;this.matrix[6]=cos+aThird*OneMinusCos;this.matrix[7]=aThird*OneMinusCos-aThirdSqtSin;this.matrix[10]=aThird*OneMinusCos-aThirdSqtSin;this.matrix[11]=aThird*OneMinusCos+aThirdSqtSin;this.matrix[12]=cos+aThird*OneMinusCos;},/**\n * HueRotation isNeutralState implementation\n * Used only in image applyFilters to discard filters that will not have an effect\n * on the image\n * @param {Object} options\n **/isNeutralState:function isNeutralState(options){this.calculateMatrix();return filters.BaseFilter.prototype.isNeutralState.call(this,options);},/**\n * Apply this filter to the input image data provided.\n *\n * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.\n *\n * @param {Object} options\n * @param {Number} options.passes The number of filters remaining to be executed\n * @param {Boolean} options.webgl Whether to use webgl to render the filter.\n * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.\n * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.\n * @param {WebGLRenderingContext} options.context The GL context used for rendering.\n * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.\n */applyTo:function applyTo(options){this.calculateMatrix();filters.BaseFilter.prototype.applyTo.call(this,options);}});/**\n * Returns filter instance from an object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] to be invoked after filter creation\n * @return {fabric.Image.filters.HueRotation} Instance of fabric.Image.filters.HueRotation\n */fabric.Image.filters.HueRotation.fromObject=fabric.Image.filters.BaseFilter.fromObject;})( true?exports:undefined);(function(global){'use strict';var fabric=global.fabric||(global.fabric={}),clone=fabric.util.object.clone;if(fabric.Text){fabric.warn('fabric.Text is already defined');return;}/**\n * Text class\n * @class fabric.Text\n * @extends fabric.Object\n * @return {fabric.Text} thisArg\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#text}\n * @see {@link fabric.Text#initialize} for constructor definition\n */fabric.Text=fabric.util.createClass(fabric.Object,/** @lends fabric.Text.prototype */{/**\n * Properties which when set cause object to change dimensions\n * @type Array\n * @private\n */_dimensionAffectingProps:['fontSize','fontWeight','fontFamily','fontStyle','lineHeight','text','charSpacing','textAlign','styles'],/**\n * @private\n */_reNewline:/\\r?\\n/,/**\n * Use this regular expression to filter for whitespaces that is not a new line.\n * Mostly used when text is 'justify' aligned.\n * @private\n */_reSpacesAndTabs:/[ \\t\\r]/g,/**\n * Use this regular expression to filter for whitespace that is not a new line.\n * Mostly used when text is 'justify' aligned.\n * @private\n */_reSpaceAndTab:/[ \\t\\r]/,/**\n * Use this regular expression to filter consecutive groups of non spaces.\n * Mostly used when text is 'justify' aligned.\n * @private\n */_reWords:/\\S+/g,/**\n * Type of an object\n * @type String\n * @default\n */type:'text',/**\n * Font size (in pixels)\n * @type Number\n * @default\n */fontSize:40,/**\n * Font weight (e.g. bold, normal, 400, 600, 800)\n * @type {(Number|String)}\n * @default\n */fontWeight:'normal',/**\n * Font family\n * @type String\n * @default\n */fontFamily:'Times New Roman',/**\n * Text decoration underline.\n * @type Boolean\n * @default\n */underline:false,/**\n * Text decoration overline.\n * @type Boolean\n * @default\n */overline:false,/**\n * Text decoration linethrough.\n * @type Boolean\n * @default\n */linethrough:false,/**\n * Text alignment. Possible values: \"left\", \"center\", \"right\", \"justify\",\n * \"justify-left\", \"justify-center\" or \"justify-right\".\n * @type String\n * @default\n */textAlign:'left',/**\n * Font style . Possible values: \"\", \"normal\", \"italic\" or \"oblique\".\n * @type String\n * @default\n */fontStyle:'normal',/**\n * Line height\n * @type Number\n * @default\n */lineHeight:1.16,/**\n * Superscript schema object (minimum overlap)\n * @type {Object}\n * @default\n */superscript:{size:0.60,// fontSize factor\nbaseline:-0.35// baseline-shift factor (upwards)\n},/**\n * Subscript schema object (minimum overlap)\n * @type {Object}\n * @default\n */subscript:{size:0.60,// fontSize factor\nbaseline:0.11// baseline-shift factor (downwards)\n},/**\n * Background color of text lines\n * @type String\n * @default\n */textBackgroundColor:'',/**\n * List of properties to consider when checking if\n * state of an object is changed ({@link fabric.Object#hasStateChanged})\n * as well as for history (undo/redo) purposes\n * @type Array\n */stateProperties:fabric.Object.prototype.stateProperties.concat('fontFamily','fontWeight','fontSize','text','underline','overline','linethrough','textAlign','fontStyle','lineHeight','textBackgroundColor','charSpacing','styles'),/**\n * List of properties to consider when checking if cache needs refresh\n * @type Array\n */cacheProperties:fabric.Object.prototype.cacheProperties.concat('fontFamily','fontWeight','fontSize','text','underline','overline','linethrough','textAlign','fontStyle','lineHeight','textBackgroundColor','charSpacing','styles'),/**\n * When defined, an object is rendered via stroke and this property specifies its color.\n * <b>Backwards incompatibility note:</b> This property was named \"strokeStyle\" until v1.1.6\n * @type String\n * @default\n */stroke:null,/**\n * Shadow object representing shadow of this shape.\n * <b>Backwards incompatibility note:</b> This property was named \"textShadow\" (String) until v1.2.11\n * @type fabric.Shadow\n * @default\n */shadow:null,/**\n * @private\n */_fontSizeFraction:0.222,/**\n * @private\n */offsets:{underline:0.10,linethrough:-0.315,overline:-0.88},/**\n * Text Line proportion to font Size (in pixels)\n * @type Number\n * @default\n */_fontSizeMult:1.13,/**\n * additional space between characters\n * expressed in thousands of em unit\n * @type Number\n * @default\n */charSpacing:0,/**\n * Object containing character styles - top-level properties -> line numbers,\n * 2nd-level properties - charater numbers\n * @type Object\n * @default\n */styles:null,/**\n * Reference to a context to measure text char or couple of chars\n * the cacheContext of the canvas will be used or a freshly created one if the object is not on canvas\n * once created it will be referenced on fabric._measuringContext to avoide creating a canvas for every\n * text object created.\n * @type {CanvasRenderingContext2D}\n * @default\n */_measuringContext:null,/**\n * Baseline shift, stlyes only, keep at 0 for the main text object\n * @type {Number}\n * @default\n */deltaY:0,/**\n * Array of properties that define a style unit (of 'styles').\n * @type {Array}\n * @default\n */_styleProperties:['stroke','strokeWidth','fill','fontFamily','fontSize','fontWeight','fontStyle','underline','overline','linethrough','deltaY','textBackgroundColor'],/**\n * contains characters bounding boxes\n */__charBounds:[],/**\n * use this size when measuring text. To avoid IE11 rounding errors\n * @type {Number}\n * @default\n * @readonly\n * @private\n */CACHE_FONT_SIZE:400,/**\n * contains the min text width to avoid getting 0\n * @type {Number}\n * @default\n */MIN_TEXT_WIDTH:2,/**\n * Constructor\n * @param {String} text Text string\n * @param {Object} [options] Options object\n * @return {fabric.Text} thisArg\n */initialize:function initialize(text,options){this.styles=options?options.styles||{}:{};this.text=text;this.__skipDimension=true;this.callSuper('initialize',options);this.__skipDimension=false;this.initDimensions();this.setCoords();this.setupState({propertySet:'_dimensionAffectingProps'});},/**\n * Return a contex for measurement of text string.\n * if created it gets stored for reuse\n * @param {String} text Text string\n * @param {Object} [options] Options object\n * @return {fabric.Text} thisArg\n */getMeasuringContext:function getMeasuringContext(){// if we did not return we have to measure something.\nif(!fabric._measuringContext){fabric._measuringContext=this.canvas&&this.canvas.contextCache||fabric.util.createCanvasElement().getContext('2d');}return fabric._measuringContext;},/**\n * @private\n * Divides text into lines of text and lines of graphemes.\n */_splitText:function _splitText(){var newLines=this._splitTextIntoLines(this.text);this.textLines=newLines.lines;this._textLines=newLines.graphemeLines;this._unwrappedTextLines=newLines._unwrappedLines;this._text=newLines.graphemeText;return newLines;},/**\n * Initialize or update text dimensions.\n * Updates this.width and this.height with the proper values.\n * Does not return dimensions.\n */initDimensions:function initDimensions(){if(this.__skipDimension){return;}this._splitText();this._clearCache();this.width=this.calcTextWidth()||this.cursorWidth||this.MIN_TEXT_WIDTH;if(this.textAlign.indexOf('justify')!==-1){// once text is measured we need to make space fatter to make justified text.\nthis.enlargeSpaces();}this.height=this.calcTextHeight();this.saveState({propertySet:'_dimensionAffectingProps'});},/**\n * Enlarge space boxes and shift the others\n */enlargeSpaces:function enlargeSpaces(){var diffSpace,currentLineWidth,numberOfSpaces,accumulatedSpace,line,charBound,spaces;for(var i=0,len=this._textLines.length;i<len;i++){if(this.textAlign!=='justify'&&(i===len-1||this.isEndOfWrapping(i))){continue;}accumulatedSpace=0;line=this._textLines[i];currentLineWidth=this.getLineWidth(i);if(currentLineWidth<this.width&&(spaces=this.textLines[i].match(this._reSpacesAndTabs))){numberOfSpaces=spaces.length;diffSpace=(this.width-currentLineWidth)/numberOfSpaces;for(var j=0,jlen=line.length;j<=jlen;j++){charBound=this.__charBounds[i][j];if(this._reSpaceAndTab.test(line[j])){charBound.width+=diffSpace;charBound.kernedWidth+=diffSpace;charBound.left+=accumulatedSpace;accumulatedSpace+=diffSpace;}else{charBound.left+=accumulatedSpace;}}}}},/**\n * Detect if the text line is ended with an hard break\n * text and itext do not have wrapping, return false\n * @return {Boolean}\n */isEndOfWrapping:function isEndOfWrapping(lineIndex){return lineIndex===this._textLines.length-1;},/**\n * Detect if a line has a linebreak and so we need to account for it when moving\n * and counting style.\n * It return always for text and Itext.\n * @return Number\n */missingNewlineOffset:function missingNewlineOffset(){return 1;},/**\n * Returns string representation of an instance\n * @return {String} String representation of text object\n */toString:function toString(){return'#<fabric.Text ('+this.complexity()+'): { \"text\": \"'+this.text+'\", \"fontFamily\": \"'+this.fontFamily+'\" }>';},/**\n * Return the dimension and the zoom level needed to create a cache canvas\n * big enough to host the object to be cached.\n * @private\n * @param {Object} dim.x width of object to be cached\n * @param {Object} dim.y height of object to be cached\n * @return {Object}.width width of canvas\n * @return {Object}.height height of canvas\n * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache\n * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache\n */_getCacheCanvasDimensions:function _getCacheCanvasDimensions(){var dims=this.callSuper('_getCacheCanvasDimensions');var fontSize=this.fontSize;dims.width+=fontSize*dims.zoomX;dims.height+=fontSize*dims.zoomY;return dims;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){this._setTextStyles(ctx);this._renderTextLinesBackground(ctx);this._renderTextDecoration(ctx,'underline');this._renderText(ctx);this._renderTextDecoration(ctx,'overline');this._renderTextDecoration(ctx,'linethrough');},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderText:function _renderText(ctx){if(this.paintFirst==='stroke'){this._renderTextStroke(ctx);this._renderTextFill(ctx);}else{this._renderTextFill(ctx);this._renderTextStroke(ctx);}},/**\n * Set the font parameter of the context with the object properties or with charStyle\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Object} [charStyle] object with font style properties\n * @param {String} [charStyle.fontFamily] Font Family\n * @param {Number} [charStyle.fontSize] Font size in pixels. ( without px suffix )\n * @param {String} [charStyle.fontWeight] Font weight\n * @param {String} [charStyle.fontStyle] Font style (italic|normal)\n */_setTextStyles:function _setTextStyles(ctx,charStyle,forMeasuring){ctx.textBaseline='alphabetic';ctx.font=this._getFontDeclaration(charStyle,forMeasuring);},/**\n * calculate and return the text Width measuring each line.\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @return {Number} Maximum width of fabric.Text object\n */calcTextWidth:function calcTextWidth(){var maxWidth=this.getLineWidth(0);for(var i=1,len=this._textLines.length;i<len;i++){var currentLineWidth=this.getLineWidth(i);if(currentLineWidth>maxWidth){maxWidth=currentLineWidth;}}return maxWidth;},/**\n * @private\n * @param {String} method Method name (\"fillText\" or \"strokeText\")\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {String} line Text to render\n * @param {Number} left Left position of text\n * @param {Number} top Top position of text\n * @param {Number} lineIndex Index of a line in a text\n */_renderTextLine:function _renderTextLine(method,ctx,line,left,top,lineIndex){this._renderChars(method,ctx,line,left,top,lineIndex);},/**\n * Renders the text background for lines, taking care of style\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderTextLinesBackground:function _renderTextLinesBackground(ctx){if(!this.textBackgroundColor&&!this.styleHas('textBackgroundColor')){return;}var lineTopOffset=0,heightOfLine,lineLeftOffset,originalFill=ctx.fillStyle,line,lastColor,leftOffset=this._getLeftOffset(),topOffset=this._getTopOffset(),boxStart=0,boxWidth=0,charBox,currentColor;for(var i=0,len=this._textLines.length;i<len;i++){heightOfLine=this.getHeightOfLine(i);if(!this.textBackgroundColor&&!this.styleHas('textBackgroundColor',i)){lineTopOffset+=heightOfLine;continue;}line=this._textLines[i];lineLeftOffset=this._getLineLeftOffset(i);boxWidth=0;boxStart=0;lastColor=this.getValueOfPropertyAt(i,0,'textBackgroundColor');for(var j=0,jlen=line.length;j<jlen;j++){charBox=this.__charBounds[i][j];currentColor=this.getValueOfPropertyAt(i,j,'textBackgroundColor');if(currentColor!==lastColor){ctx.fillStyle=lastColor;lastColor&&ctx.fillRect(leftOffset+lineLeftOffset+boxStart,topOffset+lineTopOffset,boxWidth,heightOfLine/this.lineHeight);boxStart=charBox.left;boxWidth=charBox.width;lastColor=currentColor;}else{boxWidth+=charBox.kernedWidth;}}if(currentColor){ctx.fillStyle=currentColor;ctx.fillRect(leftOffset+lineLeftOffset+boxStart,topOffset+lineTopOffset,boxWidth,heightOfLine/this.lineHeight);}lineTopOffset+=heightOfLine;}ctx.fillStyle=originalFill;// if there is text background color no\n// other shadows should be casted\nthis._removeShadow(ctx);},/**\n * @private\n * @param {Object} decl style declaration for cache\n * @param {String} decl.fontFamily fontFamily\n * @param {String} decl.fontStyle fontStyle\n * @param {String} decl.fontWeight fontWeight\n * @return {Object} reference to cache\n */getFontCache:function getFontCache(decl){var fontFamily=decl.fontFamily.toLowerCase();if(!fabric.charWidthsCache[fontFamily]){fabric.charWidthsCache[fontFamily]={};}var cache=fabric.charWidthsCache[fontFamily],cacheProp=decl.fontStyle.toLowerCase()+'_'+(decl.fontWeight+'').toLowerCase();if(!cache[cacheProp]){cache[cacheProp]={};}return cache[cacheProp];},/**\n * apply all the character style to canvas for rendering\n * @private\n * @param {String} _char\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @param {Object} [decl]\n */_applyCharStyles:function _applyCharStyles(method,ctx,lineIndex,charIndex,styleDeclaration){this._setFillStyles(ctx,styleDeclaration);this._setStrokeStyles(ctx,styleDeclaration);ctx.font=this._getFontDeclaration(styleDeclaration);},/**\n * measure and return the width of a single character.\n * possibly overridden to accommodate different measure logic or\n * to hook some external lib for character measurement\n * @private\n * @param {String} _char, char to be measured\n * @param {Object} charStyle style of char to be measured\n * @param {String} [previousChar] previous char\n * @param {Object} [prevCharStyle] style of previous char\n */_measureChar:function _measureChar(_char,charStyle,previousChar,prevCharStyle){// first i try to return from cache\nvar fontCache=this.getFontCache(charStyle),fontDeclaration=this._getFontDeclaration(charStyle),previousFontDeclaration=this._getFontDeclaration(prevCharStyle),couple=previousChar+_char,stylesAreEqual=fontDeclaration===previousFontDeclaration,width,coupleWidth,previousWidth,fontMultiplier=charStyle.fontSize/this.CACHE_FONT_SIZE,kernedWidth;if(previousChar&&fontCache[previousChar]!==undefined){previousWidth=fontCache[previousChar];}if(fontCache[_char]!==undefined){kernedWidth=width=fontCache[_char];}if(stylesAreEqual&&fontCache[couple]!==undefined){coupleWidth=fontCache[couple];kernedWidth=coupleWidth-previousWidth;}if(width===undefined||previousWidth===undefined||coupleWidth===undefined){var ctx=this.getMeasuringContext();// send a TRUE to specify measuring font size CACHE_FONT_SIZE\nthis._setTextStyles(ctx,charStyle,true);}if(width===undefined){kernedWidth=width=ctx.measureText(_char).width;fontCache[_char]=width;}if(previousWidth===undefined&&stylesAreEqual&&previousChar){previousWidth=ctx.measureText(previousChar).width;fontCache[previousChar]=previousWidth;}if(stylesAreEqual&&coupleWidth===undefined){// we can measure the kerning couple and subtract the width of the previous character\ncoupleWidth=ctx.measureText(couple).width;fontCache[couple]=coupleWidth;kernedWidth=coupleWidth-previousWidth;}return{width:width*fontMultiplier,kernedWidth:kernedWidth*fontMultiplier};},/**\n * Computes height of character at given position\n * @param {Number} line the line index number\n * @param {Number} _char the character index number\n * @return {Number} fontSize of the character\n */getHeightOfChar:function getHeightOfChar(line,_char){return this.getValueOfPropertyAt(line,_char,'fontSize');},/**\n * measure a text line measuring all characters.\n * @param {Number} lineIndex line number\n * @return {Number} Line width\n */measureLine:function measureLine(lineIndex){var lineInfo=this._measureLine(lineIndex);if(this.charSpacing!==0){lineInfo.width-=this._getWidthOfCharSpacing();}if(lineInfo.width<0){lineInfo.width=0;}return lineInfo;},/**\n * measure every grapheme of a line, populating __charBounds\n * @param {Number} lineIndex\n * @return {Object} object.width total width of characters\n * @return {Object} object.widthOfSpaces length of chars that match this._reSpacesAndTabs\n */_measureLine:function _measureLine(lineIndex){var width=0,i,grapheme,line=this._textLines[lineIndex],prevGrapheme,graphemeInfo,numOfSpaces=0,lineBounds=new Array(line.length);this.__charBounds[lineIndex]=lineBounds;for(i=0;i<line.length;i++){grapheme=line[i];graphemeInfo=this._getGraphemeBox(grapheme,lineIndex,i,prevGrapheme);lineBounds[i]=graphemeInfo;width+=graphemeInfo.kernedWidth;prevGrapheme=grapheme;}// this latest bound box represent the last character of the line\n// to simplify cursor handling in interactive mode.\nlineBounds[i]={left:graphemeInfo?graphemeInfo.left+graphemeInfo.width:0,width:0,kernedWidth:0,height:this.fontSize};return{width:width,numOfSpaces:numOfSpaces};},/**\n * Measure and return the info of a single grapheme.\n * needs the the info of previous graphemes already filled\n * @private\n * @param {String} grapheme to be measured\n * @param {Number} lineIndex index of the line where the char is\n * @param {Number} charIndex position in the line\n * @param {String} [prevGrapheme] character preceding the one to be measured\n */_getGraphemeBox:function _getGraphemeBox(grapheme,lineIndex,charIndex,prevGrapheme,skipLeft){var style=this.getCompleteStyleDeclaration(lineIndex,charIndex),prevStyle=prevGrapheme?this.getCompleteStyleDeclaration(lineIndex,charIndex-1):{},info=this._measureChar(grapheme,style,prevGrapheme,prevStyle),kernedWidth=info.kernedWidth,width=info.width,charSpacing;if(this.charSpacing!==0){charSpacing=this._getWidthOfCharSpacing();width+=charSpacing;kernedWidth+=charSpacing;}var box={width:width,left:0,height:style.fontSize,kernedWidth:kernedWidth,deltaY:style.deltaY};if(charIndex>0&&!skipLeft){var previousBox=this.__charBounds[lineIndex][charIndex-1];box.left=previousBox.left+previousBox.width+info.kernedWidth-info.width;}return box;},/**\n * Calculate height of line at 'lineIndex'\n * @param {Number} lineIndex index of line to calculate\n * @return {Number}\n */getHeightOfLine:function getHeightOfLine(lineIndex){if(this.__lineHeights[lineIndex]){return this.__lineHeights[lineIndex];}var line=this._textLines[lineIndex],// char 0 is measured before the line cycle because it nneds to char\n// emptylines\nmaxHeight=this.getHeightOfChar(lineIndex,0);for(var i=1,len=line.length;i<len;i++){maxHeight=Math.max(this.getHeightOfChar(lineIndex,i),maxHeight);}return this.__lineHeights[lineIndex]=maxHeight*this.lineHeight*this._fontSizeMult;},/**\n * Calculate text box height\n */calcTextHeight:function calcTextHeight(){var lineHeight,height=0;for(var i=0,len=this._textLines.length;i<len;i++){lineHeight=this.getHeightOfLine(i);height+=i===len-1?lineHeight/this.lineHeight:lineHeight;}return height;},/**\n * @private\n * @return {Number} Left offset\n */_getLeftOffset:function _getLeftOffset(){return-this.width/2;},/**\n * @private\n * @return {Number} Top offset\n */_getTopOffset:function _getTopOffset(){return-this.height/2;},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Object} filler fabric.Pattern or fabric.Gradient\n * @return {Object} offset.offsetX offset for text rendering\n * @return {Object} offset.offsetY offset for text rendering\n */_applyPatternGradientTransform:function _applyPatternGradientTransform(ctx,filler){if(!filler||!filler.toLive){return{offsetX:0,offsetY:0};}var offsetX=-this.width/2+filler.offsetX||0,offsetY=-this.height/2+filler.offsetY||0;ctx.transform(1,0,0,1,offsetX,offsetY);return{offsetX:offsetX,offsetY:offsetY};},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {String} method Method name (\"fillText\" or \"strokeText\")\n */_renderTextCommon:function _renderTextCommon(ctx,method){ctx.save();var lineHeights=0,left=this._getLeftOffset(),top=this._getTopOffset(),offsets=this._applyPatternGradientTransform(ctx,method==='fillText'?this.fill:this.stroke);for(var i=0,len=this._textLines.length;i<len;i++){var heightOfLine=this.getHeightOfLine(i),maxHeight=heightOfLine/this.lineHeight,leftOffset=this._getLineLeftOffset(i);this._renderTextLine(method,ctx,this._textLines[i],left+leftOffset-offsets.offsetX,top+lineHeights+maxHeight-offsets.offsetY,i);lineHeights+=heightOfLine;}ctx.restore();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderTextFill:function _renderTextFill(ctx){if(!this.fill&&!this.styleHas('fill')){return;}this._renderTextCommon(ctx,'fillText');},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderTextStroke:function _renderTextStroke(ctx){if((!this.stroke||this.strokeWidth===0)&&this.isEmptyStyles()){return;}if(this.shadow&&!this.shadow.affectStroke){this._removeShadow(ctx);}ctx.save();this._setLineDash(ctx,this.strokeDashArray);ctx.beginPath();this._renderTextCommon(ctx,'strokeText');ctx.closePath();ctx.restore();},/**\n * @private\n * @param {String} method\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {String} line Content of the line\n * @param {Number} left\n * @param {Number} top\n * @param {Number} lineIndex\n * @param {Number} charOffset\n */_renderChars:function _renderChars(method,ctx,line,left,top,lineIndex){// set proper line offset\nvar lineHeight=this.getHeightOfLine(lineIndex),isJustify=this.textAlign.indexOf('justify')!==-1,actualStyle,nextStyle,charsToRender='',charBox,boxWidth=0,timeToRender,shortCut=!isJustify&&this.charSpacing===0&&this.isEmptyStyles(lineIndex);ctx.save();top-=lineHeight*this._fontSizeFraction/this.lineHeight;if(shortCut){// render all the line in one pass without checking\nthis._renderChar(method,ctx,lineIndex,0,this.textLines[lineIndex],left,top,lineHeight);ctx.restore();return;}for(var i=0,len=line.length-1;i<=len;i++){timeToRender=i===len||this.charSpacing;charsToRender+=line[i];charBox=this.__charBounds[lineIndex][i];if(boxWidth===0){left+=charBox.kernedWidth-charBox.width;boxWidth+=charBox.width;}else{boxWidth+=charBox.kernedWidth;}if(isJustify&&!timeToRender){if(this._reSpaceAndTab.test(line[i])){timeToRender=true;}}if(!timeToRender){// if we have charSpacing, we render char by char\nactualStyle=actualStyle||this.getCompleteStyleDeclaration(lineIndex,i);nextStyle=this.getCompleteStyleDeclaration(lineIndex,i+1);timeToRender=this._hasStyleChanged(actualStyle,nextStyle);}if(timeToRender){this._renderChar(method,ctx,lineIndex,i,charsToRender,left,top,lineHeight);charsToRender='';actualStyle=nextStyle;left+=boxWidth;boxWidth=0;}}ctx.restore();},/**\n * @private\n * @param {String} method\n * @param {CanvasRenderingContext2D} ctx Context to render on\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @param {String} _char\n * @param {Number} left Left coordinate\n * @param {Number} top Top coordinate\n * @param {Number} lineHeight Height of the line\n */_renderChar:function _renderChar(method,ctx,lineIndex,charIndex,_char,left,top){var decl=this._getStyleDeclaration(lineIndex,charIndex),fullDecl=this.getCompleteStyleDeclaration(lineIndex,charIndex),shouldFill=method==='fillText'&&fullDecl.fill,shouldStroke=method==='strokeText'&&fullDecl.stroke&&fullDecl.strokeWidth;if(!shouldStroke&&!shouldFill){return;}decl&&ctx.save();this._applyCharStyles(method,ctx,lineIndex,charIndex,fullDecl);if(decl&&decl.textBackgroundColor){this._removeShadow(ctx);}if(decl&&decl.deltaY){top+=decl.deltaY;}shouldFill&&ctx.fillText(_char,left,top);shouldStroke&&ctx.strokeText(_char,left,top);decl&&ctx.restore();},/**\n * Turns the character into a 'superior figure' (i.e. 'superscript')\n * @param {Number} start selection start\n * @param {Number} end selection end\n * @returns {fabric.Text} thisArg\n * @chainable\n */setSuperscript:function setSuperscript(start,end){return this._setScript(start,end,this.superscript);},/**\n * Turns the character into an 'inferior figure' (i.e. 'subscript')\n * @param {Number} start selection start\n * @param {Number} end selection end\n * @returns {fabric.Text} thisArg\n * @chainable\n */setSubscript:function setSubscript(start,end){return this._setScript(start,end,this.subscript);},/**\n * Applies 'schema' at given position\n * @private\n * @param {Number} start selection start\n * @param {Number} end selection end\n * @param {Number} schema\n * @returns {fabric.Text} thisArg\n * @chainable\n */_setScript:function _setScript(start,end,schema){var loc=this.get2DCursorLocation(start,true),fontSize=this.getValueOfPropertyAt(loc.lineIndex,loc.charIndex,'fontSize'),dy=this.getValueOfPropertyAt(loc.lineIndex,loc.charIndex,'deltaY'),style={fontSize:fontSize*schema.size,deltaY:dy+fontSize*schema.baseline};this.setSelectionStyles(style,start,end);return this;},/**\n * @private\n * @param {Object} prevStyle\n * @param {Object} thisStyle\n */_hasStyleChanged:function _hasStyleChanged(prevStyle,thisStyle){return prevStyle.fill!==thisStyle.fill||prevStyle.stroke!==thisStyle.stroke||prevStyle.strokeWidth!==thisStyle.strokeWidth||prevStyle.fontSize!==thisStyle.fontSize||prevStyle.fontFamily!==thisStyle.fontFamily||prevStyle.fontWeight!==thisStyle.fontWeight||prevStyle.fontStyle!==thisStyle.fontStyle||prevStyle.deltaY!==thisStyle.deltaY;},/**\n * @private\n * @param {Object} prevStyle\n * @param {Object} thisStyle\n */_hasStyleChangedForSvg:function _hasStyleChangedForSvg(prevStyle,thisStyle){return this._hasStyleChanged(prevStyle,thisStyle)||prevStyle.overline!==thisStyle.overline||prevStyle.underline!==thisStyle.underline||prevStyle.linethrough!==thisStyle.linethrough;},/**\n * @private\n * @param {Number} lineIndex index text line\n * @return {Number} Line left offset\n */_getLineLeftOffset:function _getLineLeftOffset(lineIndex){var lineWidth=this.getLineWidth(lineIndex);if(this.textAlign==='center'){return(this.width-lineWidth)/2;}if(this.textAlign==='right'){return this.width-lineWidth;}if(this.textAlign==='justify-center'&&this.isEndOfWrapping(lineIndex)){return(this.width-lineWidth)/2;}if(this.textAlign==='justify-right'&&this.isEndOfWrapping(lineIndex)){return this.width-lineWidth;}return 0;},/**\n * @private\n */_clearCache:function _clearCache(){this.__lineWidths=[];this.__lineHeights=[];this.__charBounds=[];},/**\n * @private\n */_shouldClearDimensionCache:function _shouldClearDimensionCache(){var shouldClear=this._forceClearCache;shouldClear||(shouldClear=this.hasStateChanged('_dimensionAffectingProps'));if(shouldClear){this.dirty=true;this._forceClearCache=false;}return shouldClear;},/**\n * Measure a single line given its index. Used to calculate the initial\n * text bounding box. The values are calculated and stored in __lineWidths cache.\n * @private\n * @param {Number} lineIndex line number\n * @return {Number} Line width\n */getLineWidth:function getLineWidth(lineIndex){if(this.__lineWidths[lineIndex]){return this.__lineWidths[lineIndex];}var width,line=this._textLines[lineIndex],lineInfo;if(line===''){width=0;}else{lineInfo=this.measureLine(lineIndex);width=lineInfo.width;}this.__lineWidths[lineIndex]=width;return width;},_getWidthOfCharSpacing:function _getWidthOfCharSpacing(){if(this.charSpacing!==0){return this.fontSize*this.charSpacing/1000;}return 0;},/**\n * Retrieves the value of property at given character position\n * @param {Number} lineIndex the line number\n * @param {Number} charIndex the charater number\n * @param {String} property the property name\n * @returns the value of 'property'\n */getValueOfPropertyAt:function getValueOfPropertyAt(lineIndex,charIndex,property){var charStyle=this._getStyleDeclaration(lineIndex,charIndex);if(charStyle&&typeof charStyle[property]!=='undefined'){return charStyle[property];}return this[property];},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_renderTextDecoration:function _renderTextDecoration(ctx,type){if(!this[type]&&!this.styleHas(type)){return;}var heightOfLine,size,_size,lineLeftOffset,dy,_dy,line,lastDecoration,leftOffset=this._getLeftOffset(),topOffset=this._getTopOffset(),top,boxStart,boxWidth,charBox,currentDecoration,maxHeight,currentFill,lastFill,charSpacing=this._getWidthOfCharSpacing();for(var i=0,len=this._textLines.length;i<len;i++){heightOfLine=this.getHeightOfLine(i);if(!this[type]&&!this.styleHas(type,i)){topOffset+=heightOfLine;continue;}line=this._textLines[i];maxHeight=heightOfLine/this.lineHeight;lineLeftOffset=this._getLineLeftOffset(i);boxStart=0;boxWidth=0;lastDecoration=this.getValueOfPropertyAt(i,0,type);lastFill=this.getValueOfPropertyAt(i,0,'fill');top=topOffset+maxHeight*(1-this._fontSizeFraction);size=this.getHeightOfChar(i,0);dy=this.getValueOfPropertyAt(i,0,'deltaY');for(var j=0,jlen=line.length;j<jlen;j++){charBox=this.__charBounds[i][j];currentDecoration=this.getValueOfPropertyAt(i,j,type);currentFill=this.getValueOfPropertyAt(i,j,'fill');_size=this.getHeightOfChar(i,j);_dy=this.getValueOfPropertyAt(i,j,'deltaY');if((currentDecoration!==lastDecoration||currentFill!==lastFill||_size!==size||_dy!==dy)&&boxWidth>0){ctx.fillStyle=lastFill;lastDecoration&&lastFill&&ctx.fillRect(leftOffset+lineLeftOffset+boxStart,top+this.offsets[type]*size+dy,boxWidth,this.fontSize/15);boxStart=charBox.left;boxWidth=charBox.width;lastDecoration=currentDecoration;lastFill=currentFill;size=_size;dy=_dy;}else{boxWidth+=charBox.kernedWidth;}}ctx.fillStyle=currentFill;currentDecoration&¤tFill&&ctx.fillRect(leftOffset+lineLeftOffset+boxStart,top+this.offsets[type]*size+dy,boxWidth-charSpacing,this.fontSize/15);topOffset+=heightOfLine;}// if there is text background color no\n// other shadows should be casted\nthis._removeShadow(ctx);},/**\n * return font declaration string for canvas context\n * @param {Object} [styleObject] object\n * @returns {String} font declaration formatted for canvas context.\n */_getFontDeclaration:function _getFontDeclaration(styleObject,forMeasuring){var style=styleObject||this,family=this.fontFamily,fontIsGeneric=fabric.Text.genericFonts.indexOf(family.toLowerCase())>-1;var fontFamily=family===undefined||family.indexOf('\\'')>-1||family.indexOf(',')>-1||family.indexOf('\"')>-1||fontIsGeneric?style.fontFamily:'\"'+style.fontFamily+'\"';return[// node-canvas needs \"weight style\", while browsers need \"style weight\"\n// verify if this can be fixed in JSDOM\nfabric.isLikelyNode?style.fontWeight:style.fontStyle,fabric.isLikelyNode?style.fontStyle:style.fontWeight,forMeasuring?this.CACHE_FONT_SIZE+'px':style.fontSize+'px',fontFamily].join(' ');},/**\n * Renders text instance on a specified context\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */render:function render(ctx){// do not render if object is not visible\nif(!this.visible){return;}if(this.canvas&&this.canvas.skipOffscreen&&!this.group&&!this.isOnScreen()){return;}if(this._shouldClearDimensionCache()){this.initDimensions();}this.callSuper('render',ctx);},/**\n * Returns the text as an array of lines.\n * @param {String} text text to split\n * @returns {Array} Lines in the text\n */_splitTextIntoLines:function _splitTextIntoLines(text){var lines=text.split(this._reNewline),newLines=new Array(lines.length),newLine=['\\n'],newText=[];for(var i=0;i<lines.length;i++){newLines[i]=fabric.util.string.graphemeSplit(lines[i]);newText=newText.concat(newLines[i],newLine);}newText.pop();return{_unwrappedLines:newLines,lines:lines,graphemeText:newText,graphemeLines:newLines};},/**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */toObject:function toObject(propertiesToInclude){var additionalProperties=['text','fontSize','fontWeight','fontFamily','fontStyle','lineHeight','underline','overline','linethrough','textAlign','textBackgroundColor','charSpacing'].concat(propertiesToInclude);var obj=this.callSuper('toObject',additionalProperties);obj.styles=clone(this.styles,true);return obj;},/**\n * 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()`.\n * @param {String|Object} key Property name or object (if object, iterate over the object properties)\n * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)\n * @return {fabric.Object} thisArg\n * @chainable\n */set:function set(key,value){this.callSuper('set',key,value);var needsDims=false;if(_typeof(key)==='object'){for(var _key in key){needsDims=needsDims||this._dimensionAffectingProps.indexOf(_key)!==-1;}}else{needsDims=this._dimensionAffectingProps.indexOf(key)!==-1;}if(needsDims){this.initDimensions();this.setCoords();}return this;},/**\n * Returns complexity of an instance\n * @return {Number} complexity\n */complexity:function complexity(){return 1;}});/* _FROM_SVG_START_ */ /**\n * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})\n * @static\n * @memberOf fabric.Text\n * @see: http://www.w3.org/TR/SVG/text.html#TextElement\n */fabric.Text.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat('x y dx dy font-family font-style font-weight font-size letter-spacing text-decoration text-anchor'.split(' '));/**\n * Default SVG font size\n * @static\n * @memberOf fabric.Text\n */fabric.Text.DEFAULT_SVG_FONT_SIZE=16;/**\n * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)\n * @static\n * @memberOf fabric.Text\n * @param {SVGElement} element Element to parse\n * @param {Function} callback callback function invoked after parsing\n * @param {Object} [options] Options object\n */fabric.Text.fromElement=function(element,callback,options){if(!element){return callback(null);}var parsedAttributes=fabric.parseAttributes(element,fabric.Text.ATTRIBUTE_NAMES),parsedAnchor=parsedAttributes.textAnchor||'left';options=fabric.util.object.extend(options?clone(options):{},parsedAttributes);options.top=options.top||0;options.left=options.left||0;if(parsedAttributes.textDecoration){var textDecoration=parsedAttributes.textDecoration;if(textDecoration.indexOf('underline')!==-1){options.underline=true;}if(textDecoration.indexOf('overline')!==-1){options.overline=true;}if(textDecoration.indexOf('line-through')!==-1){options.linethrough=true;}delete options.textDecoration;}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;}var textContent='';// The XML is not properly parsed in IE9 so a workaround to get\n// textContent is through firstChild.data. Another workaround would be\n// to convert XML loaded from a file to be converted using DOMParser (same way loadSVGFromString() does)\nif(!('textContent'in element)){if('firstChild'in element&&element.firstChild!==null){if('data'in element.firstChild&&element.firstChild.data!==null){textContent=element.firstChild.data;}}}else{textContent=element.textContent;}textContent=textContent.replace(/^\\s+|\\s+$|\\n+/g,'').replace(/\\s+/g,' ');var originalStrokeWidth=options.strokeWidth;options.strokeWidth=0;var text=new fabric.Text(textContent,options),textHeightScaleFactor=text.getScaledHeight()/text.height,lineHeightDiff=(text.height+text.strokeWidth)*text.lineHeight-text.height,scaledDiff=lineHeightDiff*textHeightScaleFactor,textHeight=text.getScaledHeight()+scaledDiff,offX=0;/*\n Adjust positioning:\n x/y attributes in SVG correspond to the bottom-left corner of text bounding box\n fabric output by default at top, left.\n */if(parsedAnchor==='center'){offX=text.getScaledWidth()/2;}if(parsedAnchor==='right'){offX=text.getScaledWidth();}text.set({left:text.left-offX,top:text.top-(textHeight-text.fontSize*(0.07+text._fontSizeFraction))/text.lineHeight,strokeWidth:typeof originalStrokeWidth!=='undefined'?originalStrokeWidth:1});callback(text);};/* _FROM_SVG_END_ */ /**\n * Returns fabric.Text instance from an object representation\n * @static\n * @memberOf fabric.Text\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] Callback to invoke when an fabric.Text instance is created\n */fabric.Text.fromObject=function(object,callback){return fabric.Object._fromObject('Text',object,callback,'text');};fabric.Text.genericFonts=['sans-serif','serif','cursive','fantasy','monospace'];fabric.util.createAccessors&&fabric.util.createAccessors(fabric.Text);})( true?exports:undefined);(function(){fabric.util.object.extend(fabric.Text.prototype,/** @lends fabric.Text.prototype */{/**\n * Returns true if object has no styling or no styling in a line\n * @param {Number} lineIndex , lineIndex is on wrapped lines.\n * @return {Boolean}\n */isEmptyStyles:function isEmptyStyles(lineIndex){if(!this.styles){return true;}if(typeof lineIndex!=='undefined'&&!this.styles[lineIndex]){return true;}var obj=typeof lineIndex==='undefined'?this.styles:{line:this.styles[lineIndex]};for(var p1 in obj){for(var p2 in obj[p1]){// eslint-disable-next-line no-unused-vars\nfor(var p3 in obj[p1][p2]){return false;}}}return true;},/**\n * Returns true if object has a style property or has it ina specified line\n * This function is used to detect if a text will use a particular property or not.\n * @param {String} property to check for\n * @param {Number} lineIndex to check the style on\n * @return {Boolean}\n */styleHas:function styleHas(property,lineIndex){if(!this.styles||!property||property===''){return false;}if(typeof lineIndex!=='undefined'&&!this.styles[lineIndex]){return false;}var obj=typeof lineIndex==='undefined'?this.styles:{0:this.styles[lineIndex]};// eslint-disable-next-line\nfor(var p1 in obj){// eslint-disable-next-line\nfor(var p2 in obj[p1]){if(typeof obj[p1][p2][property]!=='undefined'){return true;}}}return false;},/**\n * Check if characters in a text have a value for a property\n * whose value matches the textbox's value for that property. If so,\n * the character-level property is deleted. If the character\n * has no other properties, then it is also deleted. Finally,\n * if the line containing that character has no other characters\n * then it also is deleted.\n *\n * @param {string} property The property to compare between characters and text.\n */cleanStyle:function cleanStyle(property){if(!this.styles||!property||property===''){return false;}var obj=this.styles,stylesCount=0,letterCount,stylePropertyValue,allStyleObjectPropertiesMatch=true,graphemeCount=0,styleObject;// eslint-disable-next-line\nfor(var p1 in obj){letterCount=0;// eslint-disable-next-line\nfor(var p2 in obj[p1]){var styleObject=obj[p1][p2],stylePropertyHasBeenSet=styleObject.hasOwnProperty(property);stylesCount++;if(stylePropertyHasBeenSet){if(!stylePropertyValue){stylePropertyValue=styleObject[property];}else if(styleObject[property]!==stylePropertyValue){allStyleObjectPropertiesMatch=false;}if(styleObject[property]===this[property]){delete styleObject[property];}}else{allStyleObjectPropertiesMatch=false;}if(Object.keys(styleObject).length!==0){letterCount++;}else{delete obj[p1][p2];}}if(letterCount===0){delete obj[p1];}}// if every grapheme has the same style set then\n// delete those styles and set it on the parent\nfor(var i=0;i<this._textLines.length;i++){graphemeCount+=this._textLines[i].length;}if(allStyleObjectPropertiesMatch&&stylesCount===graphemeCount){this[property]=stylePropertyValue;this.removeStyle(property);}},/**\n * Remove a style property or properties from all individual character styles\n * in a text object. Deletes the character style object if it contains no other style\n * props. Deletes a line style object if it contains no other character styles.\n *\n * @param {String} props The property to remove from character styles.\n */removeStyle:function removeStyle(property){if(!this.styles||!property||property===''){return;}var obj=this.styles,line,lineNum,charNum;for(lineNum in obj){line=obj[lineNum];for(charNum in line){delete line[charNum][property];if(Object.keys(line[charNum]).length===0){delete line[charNum];}}if(Object.keys(line).length===0){delete obj[lineNum];}}},/**\n * @private\n */_extendStyles:function _extendStyles(index,styles){var loc=this.get2DCursorLocation(index);if(!this._getLineStyle(loc.lineIndex)){this._setLineStyle(loc.lineIndex);}if(!this._getStyleDeclaration(loc.lineIndex,loc.charIndex)){this._setStyleDeclaration(loc.lineIndex,loc.charIndex,{});}fabric.util.object.extend(this._getStyleDeclaration(loc.lineIndex,loc.charIndex),styles);},/**\n * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)\n * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.\n * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. usefull to manage styles.\n */get2DCursorLocation:function get2DCursorLocation(selectionStart,skipWrapping){if(typeof selectionStart==='undefined'){selectionStart=this.selectionStart;}var lines=skipWrapping?this._unwrappedTextLines:this._textLines,len=lines.length;for(var i=0;i<len;i++){if(selectionStart<=lines[i].length){return{lineIndex:i,charIndex:selectionStart};}selectionStart-=lines[i].length+this.missingNewlineOffset(i);}return{lineIndex:i-1,charIndex:lines[i-1].length<selectionStart?lines[i-1].length:selectionStart};},/**\n * Gets style of a current selection/cursor (at the start position)\n * if startIndex or endIndex are not provided, slectionStart or selectionEnd will be used.\n * @param {Number} [startIndex] Start index to get styles at\n * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1\n * @param {Boolean} [complete] get full style or not\n * @return {Array} styles an array with one, zero or more Style objects\n */getSelectionStyles:function getSelectionStyles(startIndex,endIndex,complete){if(typeof startIndex==='undefined'){startIndex=this.selectionStart||0;}if(typeof endIndex==='undefined'){endIndex=this.selectionEnd||startIndex;}var styles=[];for(var i=startIndex;i<endIndex;i++){styles.push(this.getStyleAtPosition(i,complete));}return styles;},/**\n * Gets style of a current selection/cursor position\n * @param {Number} position to get styles at\n * @param {Boolean} [complete] full style if true\n * @return {Object} style Style object at a specified index\n * @private\n */getStyleAtPosition:function getStyleAtPosition(position,complete){var loc=this.get2DCursorLocation(position),style=complete?this.getCompleteStyleDeclaration(loc.lineIndex,loc.charIndex):this._getStyleDeclaration(loc.lineIndex,loc.charIndex);return style||{};},/**\n * Sets style of a current selection, if no selection exist, do not set anything.\n * @param {Object} [styles] Styles object\n * @param {Number} [startIndex] Start index to get styles at\n * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1\n * @return {fabric.IText} thisArg\n * @chainable\n */setSelectionStyles:function setSelectionStyles(styles,startIndex,endIndex){if(typeof startIndex==='undefined'){startIndex=this.selectionStart||0;}if(typeof endIndex==='undefined'){endIndex=this.selectionEnd||startIndex;}for(var i=startIndex;i<endIndex;i++){this._extendStyles(i,styles);}/* not included in _extendStyles to avoid clearing cache more than once */this._forceClearCache=true;return this;},/**\n * get the reference, not a clone, of the style object for a given character\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @return {Object} style object\n */_getStyleDeclaration:function _getStyleDeclaration(lineIndex,charIndex){var lineStyle=this.styles&&this.styles[lineIndex];if(!lineStyle){return null;}return lineStyle[charIndex];},/**\n * return a new object that contains all the style property for a character\n * the object returned is newly created\n * @param {Number} lineIndex of the line where the character is\n * @param {Number} charIndex position of the character on the line\n * @return {Object} style object\n */getCompleteStyleDeclaration:function getCompleteStyleDeclaration(lineIndex,charIndex){var style=this._getStyleDeclaration(lineIndex,charIndex)||{},styleObject={},prop;for(var i=0;i<this._styleProperties.length;i++){prop=this._styleProperties[i];styleObject[prop]=typeof style[prop]==='undefined'?this[prop]:style[prop];}return styleObject;},/**\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @param {Object} style\n * @private\n */_setStyleDeclaration:function _setStyleDeclaration(lineIndex,charIndex,style){this.styles[lineIndex][charIndex]=style;},/**\n *\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @private\n */_deleteStyleDeclaration:function _deleteStyleDeclaration(lineIndex,charIndex){delete this.styles[lineIndex][charIndex];},/**\n * @param {Number} lineIndex\n * @return {Boolean} if the line exists or not\n * @private\n */_getLineStyle:function _getLineStyle(lineIndex){return!!this.styles[lineIndex];},/**\n * Set the line style to an empty object so that is initialized\n * @param {Number} lineIndex\n * @private\n */_setLineStyle:function _setLineStyle(lineIndex){this.styles[lineIndex]={};},/**\n * @param {Number} lineIndex\n * @private\n */_deleteLineStyle:function _deleteLineStyle(lineIndex){delete this.styles[lineIndex];}});})();(function(){function parseDecoration(object){if(object.textDecoration){object.textDecoration.indexOf('underline')>-1&&(object.underline=true);object.textDecoration.indexOf('line-through')>-1&&(object.linethrough=true);object.textDecoration.indexOf('overline')>-1&&(object.overline=true);delete object.textDecoration;}}/**\n * IText class (introduced in <b>v1.4</b>) Events are also fired with \"text:\"\n * prefix when observing canvas.\n * @class fabric.IText\n * @extends fabric.Text\n * @mixes fabric.Observable\n *\n * @fires changed\n * @fires selection:changed\n * @fires editing:entered\n * @fires editing:exited\n *\n * @return {fabric.IText} thisArg\n * @see {@link fabric.IText#initialize} for constructor definition\n *\n * <p>Supported key combinations:</p>\n * <pre>\n * Move cursor: left, right, up, down\n * Select character: shift + left, shift + right\n * Select text vertically: shift + up, shift + down\n * Move cursor by word: alt + left, alt + right\n * Select words: shift + alt + left, shift + alt + right\n * Move cursor to line start/end: cmd + left, cmd + right or home, end\n * Select till start/end of line: cmd + shift + left, cmd + shift + right or shift + home, shift + end\n * Jump to start/end of text: cmd + up, cmd + down\n * Select till start/end of text: cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown\n * Delete character: backspace\n * Delete word: alt + backspace\n * Delete line: cmd + backspace\n * Forward delete: delete\n * Copy text: ctrl/cmd + c\n * Paste text: ctrl/cmd + v\n * Cut text: ctrl/cmd + x\n * Select entire text: ctrl/cmd + a\n * Quit editing tab or esc\n * </pre>\n *\n * <p>Supported mouse/touch combination</p>\n * <pre>\n * Position cursor: click/touch\n * Create selection: click/touch & drag\n * Create selection: click & shift + click\n * Select word: double click\n * Select line: triple click\n * </pre>\n */fabric.IText=fabric.util.createClass(fabric.Text,fabric.Observable,/** @lends fabric.IText.prototype */{/**\n * Type of an object\n * @type String\n * @default\n */type:'i-text',/**\n * Index where text selection starts (or where cursor is when there is no selection)\n * @type Number\n * @default\n */selectionStart:0,/**\n * Index where text selection ends\n * @type Number\n * @default\n */selectionEnd:0,/**\n * Color of text selection\n * @type String\n * @default\n */selectionColor:'rgba(17,119,255,0.3)',/**\n * Indicates whether text is in editing mode\n * @type Boolean\n * @default\n */isEditing:false,/**\n * Indicates whether a text can be edited\n * @type Boolean\n * @default\n */editable:true,/**\n * Border color of text object while it's in editing mode\n * @type String\n * @default\n */editingBorderColor:'rgba(102,153,255,0.25)',/**\n * Width of cursor (in px)\n * @type Number\n * @default\n */cursorWidth:2,/**\n * Color of default cursor (when not overwritten by character style)\n * @type String\n * @default\n */cursorColor:'#333',/**\n * Delay between cursor blink (in ms)\n * @type Number\n * @default\n */cursorDelay:1000,/**\n * Duration of cursor fadein (in ms)\n * @type Number\n * @default\n */cursorDuration:600,/**\n * Indicates whether internal text char widths can be cached\n * @type Boolean\n * @default\n */caching:true,/**\n * @private\n */_reSpace:/\\s|\\n/,/**\n * @private\n */_currentCursorOpacity:0,/**\n * @private\n */_selectionDirection:null,/**\n * @private\n */_abortCursorAnimation:false,/**\n * @private\n */__widthOfSpace:[],/**\n * Helps determining when the text is in composition, so that the cursor\n * rendering is altered.\n */inCompositionMode:false,/**\n * Constructor\n * @param {String} text Text string\n * @param {Object} [options] Options object\n * @return {fabric.IText} thisArg\n */initialize:function initialize(text,options){this.callSuper('initialize',text,options);this.initBehavior();},/**\n * Sets selection start (left boundary of a selection)\n * @param {Number} index Index to set selection start to\n */setSelectionStart:function setSelectionStart(index){index=Math.max(index,0);this._updateAndFire('selectionStart',index);},/**\n * Sets selection end (right boundary of a selection)\n * @param {Number} index Index to set selection end to\n */setSelectionEnd:function setSelectionEnd(index){index=Math.min(index,this.text.length);this._updateAndFire('selectionEnd',index);},/**\n * @private\n * @param {String} property 'selectionStart' or 'selectionEnd'\n * @param {Number} index new position of property\n */_updateAndFire:function _updateAndFire(property,index){if(this[property]!==index){this._fireSelectionChanged();this[property]=index;}this._updateTextarea();},/**\n * Fires the even of selection changed\n * @private\n */_fireSelectionChanged:function _fireSelectionChanged(){this.fire('selection:changed');this.canvas&&this.canvas.fire('text:selection:changed',{target:this});},/**\n * Initialize text dimensions. Render all text on given context\n * or on a offscreen canvas to get the text width with measureText.\n * Updates this.width and this.height with the proper values.\n * Does not return dimensions.\n * @private\n */initDimensions:function initDimensions(){this.isEditing&&this.initDelayedCursor();this.clearContextTop();this.callSuper('initDimensions');},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */render:function render(ctx){this.clearContextTop();this.callSuper('render',ctx);// clear the cursorOffsetCache, so we ensure to calculate once per renderCursor\n// the correct position but not at every cursor animation.\nthis.cursorOffsetCache={};this.renderCursorOrSelection();},/**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */_render:function _render(ctx){this.callSuper('_render',ctx);},/**\n * Prepare and clean the contextTop\n */clearContextTop:function clearContextTop(skipRestore){if(!this.isEditing||!this.canvas||!this.canvas.contextTop){return;}var ctx=this.canvas.contextTop,v=this.canvas.viewportTransform;ctx.save();ctx.transform(v[0],v[1],v[2],v[3],v[4],v[5]);this.transform(ctx);this.transformMatrix&&ctx.transform.apply(ctx,this.transformMatrix);this._clearTextArea(ctx);skipRestore||ctx.restore();},/**\n * Renders cursor or selection (depending on what exists)\n * it does on the contextTop. If contextTop is not available, do nothing.\n */renderCursorOrSelection:function renderCursorOrSelection(){if(!this.isEditing||!this.canvas||!this.canvas.contextTop){return;}var boundaries=this._getCursorBoundaries(),ctx=this.canvas.contextTop;this.clearContextTop(true);if(this.selectionStart===this.selectionEnd){this.renderCursor(boundaries,ctx);}else{this.renderSelection(boundaries,ctx);}ctx.restore();},_clearTextArea:function _clearTextArea(ctx){// we add 4 pixel, to be sure to do not leave any pixel out\nvar width=this.width+4,height=this.height+4;ctx.clearRect(-width/2,-height/2,width,height);},/**\n * Returns cursor boundaries (left, top, leftOffset, topOffset)\n * @private\n * @param {Array} chars Array of characters\n * @param {String} typeOfBoundaries\n */_getCursorBoundaries:function _getCursorBoundaries(position){// left/top are left/top of entire text box\n// leftOffset/topOffset are offset from that left/top point of a text box\nif(typeof position==='undefined'){position=this.selectionStart;}var left=this._getLeftOffset(),top=this._getTopOffset(),offsets=this._getCursorBoundariesOffsets(position);return{left:left,top:top,leftOffset:offsets.left,topOffset:offsets.top};},/**\n * @private\n */_getCursorBoundariesOffsets:function _getCursorBoundariesOffsets(position){if(this.cursorOffsetCache&&'top'in this.cursorOffsetCache){return this.cursorOffsetCache;}var lineLeftOffset,lineIndex,charIndex,topOffset=0,leftOffset=0,boundaries,cursorPosition=this.get2DCursorLocation(position);charIndex=cursorPosition.charIndex;lineIndex=cursorPosition.lineIndex;for(var i=0;i<lineIndex;i++){topOffset+=this.getHeightOfLine(i);}lineLeftOffset=this._getLineLeftOffset(lineIndex);var bound=this.__charBounds[lineIndex][charIndex];bound&&(leftOffset=bound.left);if(this.charSpacing!==0&&charIndex===this._textLines[lineIndex].length){leftOffset-=this._getWidthOfCharSpacing();}boundaries={top:topOffset,left:lineLeftOffset+(leftOffset>0?leftOffset:0)};this.cursorOffsetCache=boundaries;return this.cursorOffsetCache;},/**\n * Renders cursor\n * @param {Object} boundaries\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n */renderCursor:function renderCursor(boundaries,ctx){var cursorLocation=this.get2DCursorLocation(),lineIndex=cursorLocation.lineIndex,charIndex=cursorLocation.charIndex>0?cursorLocation.charIndex-1:0,charHeight=this.getValueOfPropertyAt(lineIndex,charIndex,'fontSize'),multiplier=this.scaleX*this.canvas.getZoom(),cursorWidth=this.cursorWidth/multiplier,topOffset=boundaries.topOffset,dy=this.getValueOfPropertyAt(lineIndex,charIndex,'deltaY');topOffset+=(1-this._fontSizeFraction)*this.getHeightOfLine(lineIndex)/this.lineHeight-charHeight*(1-this._fontSizeFraction);if(this.inCompositionMode){this.renderSelection(boundaries,ctx);}ctx.fillStyle=this.getValueOfPropertyAt(lineIndex,charIndex,'fill');ctx.globalAlpha=this.__isMousedown?1:this._currentCursorOpacity;ctx.fillRect(boundaries.left+boundaries.leftOffset-cursorWidth/2,topOffset+boundaries.top+dy,cursorWidth,charHeight);},/**\n * Renders text selection\n * @param {Object} boundaries Object with left/top/leftOffset/topOffset\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n */renderSelection:function renderSelection(boundaries,ctx){var selectionStart=this.inCompositionMode?this.hiddenTextarea.selectionStart:this.selectionStart,selectionEnd=this.inCompositionMode?this.hiddenTextarea.selectionEnd:this.selectionEnd,isJustify=this.textAlign.indexOf('justify')!==-1,start=this.get2DCursorLocation(selectionStart),end=this.get2DCursorLocation(selectionEnd),startLine=start.lineIndex,endLine=end.lineIndex,startChar=start.charIndex<0?0:start.charIndex,endChar=end.charIndex<0?0:end.charIndex;for(var i=startLine;i<=endLine;i++){var lineOffset=this._getLineLeftOffset(i)||0,lineHeight=this.getHeightOfLine(i),realLineHeight=0,boxStart=0,boxEnd=0;if(i===startLine){boxStart=this.__charBounds[startLine][startChar].left;}if(i>=startLine&&i<endLine){boxEnd=isJustify&&!this.isEndOfWrapping(i)?this.width:this.getLineWidth(i)||5;// WTF is this 5?\n}else if(i===endLine){if(endChar===0){boxEnd=this.__charBounds[endLine][endChar].left;}else{var charSpacing=this._getWidthOfCharSpacing();boxEnd=this.__charBounds[endLine][endChar-1].left+this.__charBounds[endLine][endChar-1].width-charSpacing;}}realLineHeight=lineHeight;if(this.lineHeight<1||i===endLine&&this.lineHeight>1){lineHeight/=this.lineHeight;}if(this.inCompositionMode){ctx.fillStyle=this.compositionColor||'black';ctx.fillRect(boundaries.left+lineOffset+boxStart,boundaries.top+boundaries.topOffset+lineHeight,boxEnd-boxStart,1);}else{ctx.fillStyle=this.selectionColor;ctx.fillRect(boundaries.left+lineOffset+boxStart,boundaries.top+boundaries.topOffset,boxEnd-boxStart,lineHeight);}boundaries.topOffset+=realLineHeight;}},/**\n * High level function to know the height of the cursor.\n * the currentChar is the one that precedes the cursor\n * Returns fontSize of char at the current cursor\n * @return {Number} Character font size\n */getCurrentCharFontSize:function getCurrentCharFontSize(){var cp=this._getCurrentCharIndex();return this.getValueOfPropertyAt(cp.l,cp.c,'fontSize');},/**\n * High level function to know the color of the cursor.\n * the currentChar is the one that precedes the cursor\n * Returns color (fill) of char at the current cursor\n * @return {String} Character color (fill)\n */getCurrentCharColor:function getCurrentCharColor(){var cp=this._getCurrentCharIndex();return this.getValueOfPropertyAt(cp.l,cp.c,'fill');},/**\n * Returns the cursor position for the getCurrent.. functions\n * @private\n */_getCurrentCharIndex:function _getCurrentCharIndex(){var cursorPosition=this.get2DCursorLocation(this.selectionStart,true),charIndex=cursorPosition.charIndex>0?cursorPosition.charIndex-1:0;return{l:cursorPosition.lineIndex,c:charIndex};}});/**\n * Returns fabric.IText instance from an object representation\n * @static\n * @memberOf fabric.IText\n * @param {Object} object Object to create an instance from\n * @param {function} [callback] invoked with new instance as argument\n */fabric.IText.fromObject=function(object,callback){parseDecoration(object);if(object.styles){for(var i in object.styles){for(var j in object.styles[i]){parseDecoration(object.styles[i][j]);}}}fabric.Object._fromObject('IText',object,callback,'text');};})();(function(){var clone=fabric.util.object.clone;fabric.util.object.extend(fabric.IText.prototype,/** @lends fabric.IText.prototype */{/**\n * Initializes all the interactive behavior of IText\n */initBehavior:function initBehavior(){this.initAddedHandler();this.initRemovedHandler();this.initCursorSelectionHandlers();this.initDoubleClickSimulation();this.mouseMoveHandler=this.mouseMoveHandler.bind(this);},onDeselect:function onDeselect(){this.isEditing&&this.exitEditing();this.selected=false;},/**\n * Initializes \"added\" event handler\n */initAddedHandler:function initAddedHandler(){var _this=this;this.on('added',function(){var canvas=_this.canvas;if(canvas){if(!canvas._hasITextHandlers){canvas._hasITextHandlers=true;_this._initCanvasHandlers(canvas);}canvas._iTextInstances=canvas._iTextInstances||[];canvas._iTextInstances.push(_this);}});},initRemovedHandler:function initRemovedHandler(){var _this=this;this.on('removed',function(){var canvas=_this.canvas;if(canvas){canvas._iTextInstances=canvas._iTextInstances||[];fabric.util.removeFromArray(canvas._iTextInstances,_this);if(canvas._iTextInstances.length===0){canvas._hasITextHandlers=false;_this._removeCanvasHandlers(canvas);}}});},/**\n * register canvas event to manage exiting on other instances\n * @private\n */_initCanvasHandlers:function _initCanvasHandlers(canvas){canvas._mouseUpITextHandler=function(){if(canvas._iTextInstances){canvas._iTextInstances.forEach(function(obj){obj.__isMousedown=false;});}};canvas.on('mouse:up',canvas._mouseUpITextHandler);},/**\n * remove canvas event to manage exiting on other instances\n * @private\n */_removeCanvasHandlers:function _removeCanvasHandlers(canvas){canvas.off('mouse:up',canvas._mouseUpITextHandler);},/**\n * @private\n */_tick:function _tick(){this._currentTickState=this._animateCursor(this,1,this.cursorDuration,'_onTickComplete');},/**\n * @private\n */_animateCursor:function _animateCursor(obj,targetOpacity,duration,completeMethod){var tickState;tickState={isAborted:false,abort:function abort(){this.isAborted=true;}};obj.animate('_currentCursorOpacity',targetOpacity,{duration:duration,onComplete:function onComplete(){if(!tickState.isAborted){obj[completeMethod]();}},onChange:function onChange(){// we do not want to animate a selection, only cursor\nif(obj.canvas&&obj.selectionStart===obj.selectionEnd){obj.renderCursorOrSelection();}},abort:function abort(){return tickState.isAborted;}});return tickState;},/**\n * @private\n */_onTickComplete:function _onTickComplete(){var _this=this;if(this._cursorTimeout1){clearTimeout(this._cursorTimeout1);}this._cursorTimeout1=setTimeout(function(){_this._currentTickCompleteState=_this._animateCursor(_this,0,this.cursorDuration/2,'_tick');},100);},/**\n * Initializes delayed cursor\n */initDelayedCursor:function initDelayedCursor(restart){var _this=this,delay=restart?0:this.cursorDelay;this.abortCursorAnimation();this._currentCursorOpacity=1;this._cursorTimeout2=setTimeout(function(){_this._tick();},delay);},/**\n * Aborts cursor animation and clears all timeouts\n */abortCursorAnimation:function abortCursorAnimation(){var shouldClear=this._currentTickState||this._currentTickCompleteState,canvas=this.canvas;this._currentTickState&&this._currentTickState.abort();this._currentTickCompleteState&&this._currentTickCompleteState.abort();clearTimeout(this._cursorTimeout1);clearTimeout(this._cursorTimeout2);this._currentCursorOpacity=0;// to clear just itext area we need to transform the context\n// it may not be worth it\nif(shouldClear&&canvas){canvas.clearContext(canvas.contextTop||canvas.contextContainer);}},/**\n * Selects entire text\n * @return {fabric.IText} thisArg\n * @chainable\n */selectAll:function selectAll(){this.selectionStart=0;this.selectionEnd=this._text.length;this._fireSelectionChanged();this._updateTextarea();return this;},/**\n * Returns selected text\n * @return {String}\n */getSelectedText:function getSelectedText(){return this._text.slice(this.selectionStart,this.selectionEnd).join('');},/**\n * Find new selection index representing start of current word according to current selection index\n * @param {Number} startFrom Current selection index\n * @return {Number} New selection index\n */findWordBoundaryLeft:function findWordBoundaryLeft(startFrom){var offset=0,index=startFrom-1;// remove space before cursor first\nif(this._reSpace.test(this._text[index])){while(this._reSpace.test(this._text[index])){offset++;index--;}}while(/\\S/.test(this._text[index])&&index>-1){offset++;index--;}return startFrom-offset;},/**\n * Find new selection index representing end of current word according to current selection index\n * @param {Number} startFrom Current selection index\n * @return {Number} New selection index\n */findWordBoundaryRight:function findWordBoundaryRight(startFrom){var offset=0,index=startFrom;// remove space after cursor first\nif(this._reSpace.test(this._text[index])){while(this._reSpace.test(this._text[index])){offset++;index++;}}while(/\\S/.test(this._text[index])&&index<this._text.length){offset++;index++;}return startFrom+offset;},/**\n * Find new selection index representing start of current line according to current selection index\n * @param {Number} startFrom Current selection index\n * @return {Number} New selection index\n */findLineBoundaryLeft:function findLineBoundaryLeft(startFrom){var offset=0,index=startFrom-1;while(!/\\n/.test(this._text[index])&&index>-1){offset++;index--;}return startFrom-offset;},/**\n * Find new selection index representing end of current line according to current selection index\n * @param {Number} startFrom Current selection index\n * @return {Number} New selection index\n */findLineBoundaryRight:function findLineBoundaryRight(startFrom){var offset=0,index=startFrom;while(!/\\n/.test(this._text[index])&&index<this._text.length){offset++;index++;}return startFrom+offset;},/**\n * Finds index corresponding to beginning or end of a word\n * @param {Number} selectionStart Index of a character\n * @param {Number} direction 1 or -1\n * @return {Number} Index of the beginning or end of a word\n */searchWordBoundary:function searchWordBoundary(selectionStart,direction){var text=this._text,index=this._reSpace.test(text[selectionStart])?selectionStart-1:selectionStart,_char=text[index],// wrong\nreNonWord=fabric.reNonWord;while(!reNonWord.test(_char)&&index>0&&index<text.length){index+=direction;_char=text[index];}if(reNonWord.test(_char)){index+=direction===1?0:1;}return index;},/**\n * Selects a word based on the index\n * @param {Number} selectionStart Index of a character\n */selectWord:function selectWord(selectionStart){selectionStart=selectionStart||this.selectionStart;var newSelectionStart=this.searchWordBoundary(selectionStart,-1),/* search backwards */newSelectionEnd=this.searchWordBoundary(selectionStart,1);/* search forward */this.selectionStart=newSelectionStart;this.selectionEnd=newSelectionEnd;this._fireSelectionChanged();this._updateTextarea();this.renderCursorOrSelection();},/**\n * Selects a line based on the index\n * @param {Number} selectionStart Index of a character\n * @return {fabric.IText} thisArg\n * @chainable\n */selectLine:function selectLine(selectionStart){selectionStart=selectionStart||this.selectionStart;var newSelectionStart=this.findLineBoundaryLeft(selectionStart),newSelectionEnd=this.findLineBoundaryRight(selectionStart);this.selectionStart=newSelectionStart;this.selectionEnd=newSelectionEnd;this._fireSelectionChanged();this._updateTextarea();return this;},/**\n * Enters editing state\n * @return {fabric.IText} thisArg\n * @chainable\n */enterEditing:function enterEditing(e){if(this.isEditing||!this.editable){return;}if(this.canvas){this.canvas.calcOffset();this.exitEditingOnOthers(this.canvas);}this.isEditing=true;this.initHiddenTextarea(e);this.hiddenTextarea.focus();this.hiddenTextarea.value=this.text;this._updateTextarea();this._saveEditingProps();this._setEditingProps();this._textBeforeEdit=this.text;this._tick();this.fire('editing:entered');this._fireSelectionChanged();if(!this.canvas){return this;}this.canvas.fire('text:editing:entered',{target:this});this.initMouseMoveHandler();this.canvas.requestRenderAll();return this;},exitEditingOnOthers:function exitEditingOnOthers(canvas){if(canvas._iTextInstances){canvas._iTextInstances.forEach(function(obj){obj.selected=false;if(obj.isEditing){obj.exitEditing();}});}},/**\n * Initializes \"mousemove\" event handler\n */initMouseMoveHandler:function initMouseMoveHandler(){this.canvas.on('mouse:move',this.mouseMoveHandler);},/**\n * @private\n */mouseMoveHandler:function mouseMoveHandler(options){if(!this.__isMousedown||!this.isEditing){return;}var newSelectionStart=this.getSelectionStartFromPointer(options.e),currentStart=this.selectionStart,currentEnd=this.selectionEnd;if((newSelectionStart!==this.__selectionStartOnMouseDown||currentStart===currentEnd)&&(currentStart===newSelectionStart||currentEnd===newSelectionStart)){return;}if(newSelectionStart>this.__selectionStartOnMouseDown){this.selectionStart=this.__selectionStartOnMouseDown;this.selectionEnd=newSelectionStart;}else{this.selectionStart=newSelectionStart;this.selectionEnd=this.__selectionStartOnMouseDown;}if(this.selectionStart!==currentStart||this.selectionEnd!==currentEnd){this.restartCursorIfNeeded();this._fireSelectionChanged();this._updateTextarea();this.renderCursorOrSelection();}},/**\n * @private\n */_setEditingProps:function _setEditingProps(){this.hoverCursor='text';if(this.canvas){this.canvas.defaultCursor=this.canvas.moveCursor='text';}this.borderColor=this.editingBorderColor;this.hasControls=this.selectable=false;this.lockMovementX=this.lockMovementY=true;},/**\n * convert from textarea to grapheme indexes\n */fromStringToGraphemeSelection:function fromStringToGraphemeSelection(start,end,text){var smallerTextStart=text.slice(0,start),graphemeStart=fabric.util.string.graphemeSplit(smallerTextStart).length;if(start===end){return{selectionStart:graphemeStart,selectionEnd:graphemeStart};}var smallerTextEnd=text.slice(start,end),graphemeEnd=fabric.util.string.graphemeSplit(smallerTextEnd).length;return{selectionStart:graphemeStart,selectionEnd:graphemeStart+graphemeEnd};},/**\n * convert from fabric to textarea values\n */fromGraphemeToStringSelection:function fromGraphemeToStringSelection(start,end,_text){var smallerTextStart=_text.slice(0,start),graphemeStart=smallerTextStart.join('').length;if(start===end){return{selectionStart:graphemeStart,selectionEnd:graphemeStart};}var smallerTextEnd=_text.slice(start,end),graphemeEnd=smallerTextEnd.join('').length;return{selectionStart:graphemeStart,selectionEnd:graphemeStart+graphemeEnd};},/**\n * @private\n */_updateTextarea:function _updateTextarea(){this.cursorOffsetCache={};if(!this.hiddenTextarea){return;}if(!this.inCompositionMode){var newSelection=this.fromGraphemeToStringSelection(this.selectionStart,this.selectionEnd,this._text);this.hiddenTextarea.selectionStart=newSelection.selectionStart;this.hiddenTextarea.selectionEnd=newSelection.selectionEnd;}this.updateTextareaPosition();},/**\n * @private\n */updateFromTextArea:function updateFromTextArea(){if(!this.hiddenTextarea){return;}this.cursorOffsetCache={};this.text=this.hiddenTextarea.value;if(this._shouldClearDimensionCache()){this.initDimensions();this.setCoords();}var newSelection=this.fromStringToGraphemeSelection(this.hiddenTextarea.selectionStart,this.hiddenTextarea.selectionEnd,this.hiddenTextarea.value);this.selectionEnd=this.selectionStart=newSelection.selectionEnd;if(!this.inCompositionMode){this.selectionStart=newSelection.selectionStart;}this.updateTextareaPosition();},/**\n * @private\n */updateTextareaPosition:function updateTextareaPosition(){if(this.selectionStart===this.selectionEnd){var style=this._calcTextareaPosition();this.hiddenTextarea.style.left=style.left;this.hiddenTextarea.style.top=style.top;}},/**\n * @private\n * @return {Object} style contains style for hiddenTextarea\n */_calcTextareaPosition:function _calcTextareaPosition(){if(!this.canvas){return{x:1,y:1};}var desiredPosition=this.inCompositionMode?this.compositionStart:this.selectionStart,boundaries=this._getCursorBoundaries(desiredPosition),cursorLocation=this.get2DCursorLocation(desiredPosition),lineIndex=cursorLocation.lineIndex,charIndex=cursorLocation.charIndex,charHeight=this.getValueOfPropertyAt(lineIndex,charIndex,'fontSize')*this.lineHeight,leftOffset=boundaries.leftOffset,m=this.calcTransformMatrix(),p={x:boundaries.left+leftOffset,y:boundaries.top+boundaries.topOffset+charHeight},retinaScaling=this.canvas.getRetinaScaling(),upperCanvas=this.canvas.upperCanvasEl,upperCanvasWidth=upperCanvas.width/retinaScaling,upperCanvasHeight=upperCanvas.height/retinaScaling,maxWidth=upperCanvasWidth-charHeight,maxHeight=upperCanvasHeight-charHeight,scaleX=upperCanvas.clientWidth/upperCanvasWidth,scaleY=upperCanvas.clientHeight/upperCanvasHeight;p=fabric.util.transformPoint(p,m);p=fabric.util.transformPoint(p,this.canvas.viewportTransform);p.x*=scaleX;p.y*=scaleY;if(p.x<0){p.x=0;}if(p.x>maxWidth){p.x=maxWidth;}if(p.y<0){p.y=0;}if(p.y>maxHeight){p.y=maxHeight;}// add canvas offset on document\np.x+=this.canvas._offset.left;p.y+=this.canvas._offset.top;return{left:p.x+'px',top:p.y+'px',fontSize:charHeight+'px',charHeight:charHeight};},/**\n * @private\n */_saveEditingProps:function _saveEditingProps(){this._savedProps={hasControls:this.hasControls,borderColor:this.borderColor,lockMovementX:this.lockMovementX,lockMovementY:this.lockMovementY,hoverCursor:this.hoverCursor,selectable:this.selectable,defaultCursor:this.canvas&&this.canvas.defaultCursor,moveCursor:this.canvas&&this.canvas.moveCursor};},/**\n * @private\n */_restoreEditingProps:function _restoreEditingProps(){if(!this._savedProps){return;}this.hoverCursor=this._savedProps.hoverCursor;this.hasControls=this._savedProps.hasControls;this.borderColor=this._savedProps.borderColor;this.selectable=this._savedProps.selectable;this.lockMovementX=this._savedProps.lockMovementX;this.lockMovementY=this._savedProps.lockMovementY;if(this.canvas){this.canvas.defaultCursor=this._savedProps.defaultCursor;this.canvas.moveCursor=this._savedProps.moveCursor;}},/**\n * Exits from editing state\n * @return {fabric.IText} thisArg\n * @chainable\n */exitEditing:function exitEditing(){var isTextChanged=this._textBeforeEdit!==this.text;this.selected=false;this.isEditing=false;this.selectionEnd=this.selectionStart;if(this.hiddenTextarea){this.hiddenTextarea.blur&&this.hiddenTextarea.blur();this.canvas&&this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);this.hiddenTextarea=null;}this.abortCursorAnimation();this._restoreEditingProps();this._currentCursorOpacity=0;if(this._shouldClearDimensionCache()){this.initDimensions();this.setCoords();}this.fire('editing:exited');isTextChanged&&this.fire('modified');if(this.canvas){this.canvas.off('mouse:move',this.mouseMoveHandler);this.canvas.fire('text:editing:exited',{target:this});isTextChanged&&this.canvas.fire('object:modified',{target:this});}return this;},/**\n * @private\n */_removeExtraneousStyles:function _removeExtraneousStyles(){for(var prop in this.styles){if(!this._textLines[prop]){delete this.styles[prop];}}},/**\n * remove and reflow a style block from start to end.\n * @param {Number} start linear start position for removal (included in removal)\n * @param {Number} end linear end position for removal ( excluded from removal )\n */removeStyleFromTo:function removeStyleFromTo(start,end){var cursorStart=this.get2DCursorLocation(start,true),cursorEnd=this.get2DCursorLocation(end,true),lineStart=cursorStart.lineIndex,charStart=cursorStart.charIndex,lineEnd=cursorEnd.lineIndex,charEnd=cursorEnd.charIndex,i,styleObj;if(lineStart!==lineEnd){// step1 remove the trailing of lineStart\nif(this.styles[lineStart]){for(i=charStart;i<this._unwrappedTextLines[lineStart].length;i++){delete this.styles[lineStart][i];}}// step2 move the trailing of lineEnd to lineStart if needed\nif(this.styles[lineEnd]){for(i=charEnd;i<this._unwrappedTextLines[lineEnd].length;i++){styleObj=this.styles[lineEnd][i];if(styleObj){this.styles[lineStart]||(this.styles[lineStart]={});this.styles[lineStart][charStart+i-charEnd]=styleObj;}}}// step3 detects lines will be completely removed.\nfor(i=lineStart+1;i<=lineEnd;i++){delete this.styles[i];}// step4 shift remaining lines.\nthis.shiftLineStyles(lineEnd,lineStart-lineEnd);}else{// remove and shift left on the same line\nif(this.styles[lineStart]){styleObj=this.styles[lineStart];var diff=charEnd-charStart,numericChar,_char;for(i=charStart;i<charEnd;i++){delete styleObj[i];}for(_char in this.styles[lineStart]){numericChar=parseInt(_char,10);if(numericChar>=charEnd){styleObj[numericChar-diff]=styleObj[_char];delete styleObj[_char];}}}}},/**\n * Shifts line styles up or down\n * @param {Number} lineIndex Index of a line\n * @param {Number} offset Can any number?\n */shiftLineStyles:function shiftLineStyles(lineIndex,offset){// shift all line styles by offset upward or downward\n// do not clone deep. we need new array, not new style objects\nvar clonedStyles=clone(this.styles);for(var line in this.styles){var numericLine=parseInt(line,10);if(numericLine>lineIndex){this.styles[numericLine+offset]=clonedStyles[numericLine];if(!clonedStyles[numericLine-offset]){delete this.styles[numericLine];}}}},restartCursorIfNeeded:function restartCursorIfNeeded(){if(!this._currentTickState||this._currentTickState.isAborted||!this._currentTickCompleteState||this._currentTickCompleteState.isAborted){this.initDelayedCursor();}},/**\n * Inserts new style object\n * @param {Number} lineIndex Index of a line\n * @param {Number} charIndex Index of a char\n * @param {Number} qty number of lines to add\n * @param {Array} copiedStyle Array of objects styles\n */insertNewlineStyleObject:function insertNewlineStyleObject(lineIndex,charIndex,qty,copiedStyle){var currentCharStyle,newLineStyles={},somethingAdded=false;qty||(qty=1);this.shiftLineStyles(lineIndex,qty);if(this.styles[lineIndex]){currentCharStyle=this.styles[lineIndex][charIndex===0?charIndex:charIndex-1];}// we clone styles of all chars\n// after cursor onto the current line\nfor(var index in this.styles[lineIndex]){var numIndex=parseInt(index,10);if(numIndex>=charIndex){somethingAdded=true;newLineStyles[numIndex-charIndex]=this.styles[lineIndex][index];// remove lines from the previous line since they're on a new line now\ndelete this.styles[lineIndex][index];}}if(somethingAdded){this.styles[lineIndex+qty]=newLineStyles;}else{delete this.styles[lineIndex+qty];}// for the other lines\n// we clone current char style onto the next (otherwise empty) line\nwhile(qty>1){qty--;if(copiedStyle&&copiedStyle[qty]){this.styles[lineIndex+qty]={0:clone(copiedStyle[qty])};}else if(currentCharStyle){this.styles[lineIndex+qty]={0:clone(currentCharStyle)};}else{delete this.styles[lineIndex+qty];}}this._forceClearCache=true;},/**\n * Inserts style object for a given line/char index\n * @param {Number} lineIndex Index of a line\n * @param {Number} charIndex Index of a char\n * @param {Number} quantity number Style object to insert, if given\n * @param {Array} copiedStyle array of style objects\n */insertCharStyleObject:function insertCharStyleObject(lineIndex,charIndex,quantity,copiedStyle){if(!this.styles){this.styles={};}var currentLineStyles=this.styles[lineIndex],currentLineStylesCloned=currentLineStyles?clone(currentLineStyles):{};quantity||(quantity=1);// shift all char styles by quantity forward\n// 0,1,2,3 -> (charIndex=2) -> 0,1,3,4 -> (insert 2) -> 0,1,2,3,4\nfor(var index in currentLineStylesCloned){var numericIndex=parseInt(index,10);if(numericIndex>=charIndex){currentLineStyles[numericIndex+quantity]=currentLineStylesCloned[numericIndex];// only delete the style if there was nothing moved there\nif(!currentLineStylesCloned[numericIndex-quantity]){delete currentLineStyles[numericIndex];}}}this._forceClearCache=true;if(copiedStyle){while(quantity--){if(!Object.keys(copiedStyle[quantity]).length){continue;}if(!this.styles[lineIndex]){this.styles[lineIndex]={};}this.styles[lineIndex][charIndex+quantity]=clone(copiedStyle[quantity]);}return;}if(!currentLineStyles){return;}var newStyle=currentLineStyles[charIndex?charIndex-1:1];while(newStyle&&quantity--){this.styles[lineIndex][charIndex+quantity]=clone(newStyle);}},/**\n * Inserts style object(s)\n * @param {Array} insertedText Characters at the location where style is inserted\n * @param {Number} start cursor index for inserting style\n * @param {Array} [copiedStyle] array of style objects to insert.\n */insertNewStyleBlock:function insertNewStyleBlock(insertedText,start,copiedStyle){var cursorLoc=this.get2DCursorLocation(start,true),addedLines=[0],linesLength=0;for(var i=0;i<insertedText.length;i++){if(insertedText[i]==='\\n'){linesLength++;addedLines[linesLength]=0;}else{addedLines[linesLength]++;}}if(addedLines[0]>0){this.insertCharStyleObject(cursorLoc.lineIndex,cursorLoc.charIndex,addedLines[0],copiedStyle);copiedStyle=copiedStyle&&copiedStyle.slice(addedLines[0]+1);}linesLength&&this.insertNewlineStyleObject(cursorLoc.lineIndex,cursorLoc.charIndex+addedLines[0],linesLength);for(var i=1;i<linesLength;i++){if(addedLines[i]>0){this.insertCharStyleObject(cursorLoc.lineIndex+i,0,addedLines[i],copiedStyle);}else if(copiedStyle){this.styles[cursorLoc.lineIndex+i][0]=copiedStyle[0];}copiedStyle=copiedStyle&&copiedStyle.slice(addedLines[i]+1);}// we use i outside the loop to get it like linesLength\nif(addedLines[i]>0){this.insertCharStyleObject(cursorLoc.lineIndex+i,0,addedLines[i],copiedStyle);}},/**\n * Set the selectionStart and selectionEnd according to the new position of cursor\n * mimic the key - mouse navigation when shift is pressed.\n */setSelectionStartEndWithShift:function setSelectionStartEndWithShift(start,end,newSelection){if(newSelection<=start){if(end===start){this._selectionDirection='left';}else if(this._selectionDirection==='right'){this._selectionDirection='left';this.selectionEnd=start;}this.selectionStart=newSelection;}else if(newSelection>start&&newSelection<end){if(this._selectionDirection==='right'){this.selectionEnd=newSelection;}else{this.selectionStart=newSelection;}}else{// newSelection is > selection start and end\nif(end===start){this._selectionDirection='right';}else if(this._selectionDirection==='left'){this._selectionDirection='right';this.selectionStart=end;}this.selectionEnd=newSelection;}},setSelectionInBoundaries:function setSelectionInBoundaries(){var length=this.text.length;if(this.selectionStart>length){this.selectionStart=length;}else if(this.selectionStart<0){this.selectionStart=0;}if(this.selectionEnd>length){this.selectionEnd=length;}else if(this.selectionEnd<0){this.selectionEnd=0;}}});})();fabric.util.object.extend(fabric.IText.prototype,/** @lends fabric.IText.prototype */{/**\n * Initializes \"dbclick\" event handler\n */initDoubleClickSimulation:function initDoubleClickSimulation(){// for double click\nthis.__lastClickTime=+new Date();// for triple click\nthis.__lastLastClickTime=+new Date();this.__lastPointer={};this.on('mousedown',this.onMouseDown);},/**\n * Default event handler to simulate triple click\n * @private\n */onMouseDown:function onMouseDown(options){if(!this.canvas){return;}this.__newClickTime=+new Date();var newPointer=options.pointer;if(this.isTripleClick(newPointer)){this.fire('tripleclick',options);this._stopEvent(options.e);}this.__lastLastClickTime=this.__lastClickTime;this.__lastClickTime=this.__newClickTime;this.__lastPointer=newPointer;this.__lastIsEditing=this.isEditing;this.__lastSelected=this.selected;},isTripleClick:function isTripleClick(newPointer){return this.__newClickTime-this.__lastClickTime<500&&this.__lastClickTime-this.__lastLastClickTime<500&&this.__lastPointer.x===newPointer.x&&this.__lastPointer.y===newPointer.y;},/**\n * @private\n */_stopEvent:function _stopEvent(e){e.preventDefault&&e.preventDefault();e.stopPropagation&&e.stopPropagation();},/**\n * Initializes event handlers related to cursor or selection\n */initCursorSelectionHandlers:function initCursorSelectionHandlers(){this.initMousedownHandler();this.initMouseupHandler();this.initClicks();},/**\n * Default handler for double click, select a word\n */doubleClickHandler:function doubleClickHandler(options){if(!this.isEditing){return;}this.selectWord(this.getSelectionStartFromPointer(options.e));},/**\n * Default handler for triple click, select a line\n */tripleClickHandler:function tripleClickHandler(options){if(!this.isEditing){return;}this.selectLine(this.getSelectionStartFromPointer(options.e));},/**\n * Initializes double and triple click event handlers\n */initClicks:function initClicks(){this.on('mousedblclick',this.doubleClickHandler);this.on('tripleclick',this.tripleClickHandler);},/**\n * Default event handler for the basic functionalities needed on _mouseDown\n * can be overridden to do something different.\n * Scope of this implementation is: find the click position, set selectionStart\n * find selectionEnd, initialize the drawing of either cursor or selection area\n */_mouseDownHandler:function _mouseDownHandler(options){if(!this.canvas||!this.editable||options.e.button&&options.e.button!==1){return;}this.__isMousedown=true;if(this.selected){this.setCursorByClick(options.e);}if(this.isEditing){this.__selectionStartOnMouseDown=this.selectionStart;if(this.selectionStart===this.selectionEnd){this.abortCursorAnimation();}this.renderCursorOrSelection();}},/**\n * Default event handler for the basic functionalities needed on mousedown:before\n * can be overridden to do something different.\n * Scope of this implementation is: verify the object is already selected when mousing down\n */_mouseDownHandlerBefore:function _mouseDownHandlerBefore(options){if(!this.canvas||!this.editable||options.e.button&&options.e.button!==1){return;}// we want to avoid that an object that was selected and then becomes unselectable,\n// may trigger editing mode in some way.\nthis.selected=this===this.canvas._activeObject;},/**\n * Initializes \"mousedown\" event handler\n */initMousedownHandler:function initMousedownHandler(){this.on('mousedown',this._mouseDownHandler);this.on('mousedown:before',this._mouseDownHandlerBefore);},/**\n * Initializes \"mouseup\" event handler\n */initMouseupHandler:function initMouseupHandler(){this.on('mouseup',this.mouseUpHandler);},/**\n * standard hander for mouse up, overridable\n * @private\n */mouseUpHandler:function mouseUpHandler(options){this.__isMousedown=false;if(!this.editable||this.group||options.transform&&options.transform.actionPerformed||options.e.button&&options.e.button!==1){return;}if(this.canvas){var currentActive=this.canvas._activeObject;if(currentActive&¤tActive!==this){// avoid running this logic when there is an active object\n// this because is possible with shift click and fast clicks,\n// to rapidly deselect and reselect this object and trigger an enterEdit\nreturn;}}if(this.__lastSelected&&!this.__corner){this.selected=false;this.__lastSelected=false;this.enterEditing(options.e);if(this.selectionStart===this.selectionEnd){this.initDelayedCursor(true);}else{this.renderCursorOrSelection();}}else{this.selected=true;}},/**\n * Changes cursor location in a text depending on passed pointer (x/y) object\n * @param {Event} e Event object\n */setCursorByClick:function setCursorByClick(e){var newSelection=this.getSelectionStartFromPointer(e),start=this.selectionStart,end=this.selectionEnd;if(e.shiftKey){this.setSelectionStartEndWithShift(start,end,newSelection);}else{this.selectionStart=newSelection;this.selectionEnd=newSelection;}if(this.isEditing){this._fireSelectionChanged();this._updateTextarea();}},/**\n * Returns index of a character corresponding to where an object was clicked\n * @param {Event} e Event object\n * @return {Number} Index of a character\n */getSelectionStartFromPointer:function getSelectionStartFromPointer(e){var mouseOffset=this.getLocalPointer(e),prevWidth=0,width=0,height=0,charIndex=0,lineIndex=0,lineLeftOffset,line;for(var i=0,len=this._textLines.length;i<len;i++){if(height<=mouseOffset.y){height+=this.getHeightOfLine(i)*this.scaleY;lineIndex=i;if(i>0){charIndex+=this._textLines[i-1].length+this.missingNewlineOffset(i-1);}}else{break;}}lineLeftOffset=this._getLineLeftOffset(lineIndex);width=lineLeftOffset*this.scaleX;line=this._textLines[lineIndex];for(var j=0,jlen=line.length;j<jlen;j++){prevWidth=width;// i removed something about flipX here, check.\nwidth+=this.__charBounds[lineIndex][j].kernedWidth*this.scaleX;if(width<=mouseOffset.x){charIndex++;}else{break;}}return this._getNewSelectionStartFromOffset(mouseOffset,prevWidth,width,charIndex,jlen);},/**\n * @private\n */_getNewSelectionStartFromOffset:function _getNewSelectionStartFromOffset(mouseOffset,prevWidth,width,index,jlen){// we need Math.abs because when width is after the last char, the offset is given as 1, while is 0\nvar distanceBtwLastCharAndCursor=mouseOffset.x-prevWidth,distanceBtwNextCharAndCursor=width-mouseOffset.x,offset=distanceBtwNextCharAndCursor>distanceBtwLastCharAndCursor||distanceBtwNextCharAndCursor<0?0:1,newSelectionStart=index+offset;// if object is horizontally flipped, mirror cursor location from the end\nif(this.flipX){newSelectionStart=jlen-newSelectionStart;}if(newSelectionStart>this._text.length){newSelectionStart=this._text.length;}return newSelectionStart;}});fabric.util.object.extend(fabric.IText.prototype,/** @lends fabric.IText.prototype */{/**\n * Initializes hidden textarea (needed to bring up keyboard in iOS)\n */initHiddenTextarea:function initHiddenTextarea(){this.hiddenTextarea=fabric.document.createElement('textarea');this.hiddenTextarea.setAttribute('autocapitalize','off');this.hiddenTextarea.setAttribute('autocorrect','off');this.hiddenTextarea.setAttribute('autocomplete','off');this.hiddenTextarea.setAttribute('spellcheck','false');this.hiddenTextarea.setAttribute('data-fabric-hiddentextarea','');this.hiddenTextarea.setAttribute('wrap','off');var style=this._calcTextareaPosition();// line-height: 1px; was removed from the style to fix this:\n// https://bugs.chromium.org/p/chromium/issues/detail?id=870966\nthis.hiddenTextarea.style.cssText='position: absolute; top: '+style.top+'; left: '+style.left+'; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px;'+' paddingーtop: '+style.fontSize+';';fabric.document.body.appendChild(this.hiddenTextarea);fabric.util.addListener(this.hiddenTextarea,'keydown',this.onKeyDown.bind(this));fabric.util.addListener(this.hiddenTextarea,'keyup',this.onKeyUp.bind(this));fabric.util.addListener(this.hiddenTextarea,'input',this.onInput.bind(this));fabric.util.addListener(this.hiddenTextarea,'copy',this.copy.bind(this));fabric.util.addListener(this.hiddenTextarea,'cut',this.copy.bind(this));fabric.util.addListener(this.hiddenTextarea,'paste',this.paste.bind(this));fabric.util.addListener(this.hiddenTextarea,'compositionstart',this.onCompositionStart.bind(this));fabric.util.addListener(this.hiddenTextarea,'compositionupdate',this.onCompositionUpdate.bind(this));fabric.util.addListener(this.hiddenTextarea,'compositionend',this.onCompositionEnd.bind(this));if(!this._clickHandlerInitialized&&this.canvas){fabric.util.addListener(this.canvas.upperCanvasEl,'click',this.onClick.bind(this));this._clickHandlerInitialized=true;}},/**\n * For functionalities on keyDown\n * Map a special key to a function of the instance/prototype\n * If you need different behaviour for ESC or TAB or arrows, you have to change\n * this map setting the name of a function that you build on the fabric.Itext or\n * your prototype.\n * the map change will affect all Instances unless you need for only some text Instances\n * in that case you have to clone this object and assign your Instance.\n * this.keysMap = fabric.util.object.clone(this.keysMap);\n * The function must be in fabric.Itext.prototype.myFunction And will receive event as args[0]\n */keysMap:{9:'exitEditing',27:'exitEditing',33:'moveCursorUp',34:'moveCursorDown',35:'moveCursorRight',36:'moveCursorLeft',37:'moveCursorLeft',38:'moveCursorUp',39:'moveCursorRight',40:'moveCursorDown'},/**\n * For functionalities on keyUp + ctrl || cmd\n */ctrlKeysMapUp:{67:'copy',88:'cut'},/**\n * For functionalities on keyDown + ctrl || cmd\n */ctrlKeysMapDown:{65:'selectAll'},onClick:function onClick(){// No need to trigger click event here, focus is enough to have the keyboard appear on Android\nthis.hiddenTextarea&&this.hiddenTextarea.focus();},/**\n * Handles keyup event\n * @param {Event} e Event object\n */onKeyDown:function onKeyDown(e){if(!this.isEditing||this.inCompositionMode){return;}if(e.keyCode in this.keysMap){this[this.keysMap[e.keyCode]](e);}else if(e.keyCode in this.ctrlKeysMapDown&&(e.ctrlKey||e.metaKey)){this[this.ctrlKeysMapDown[e.keyCode]](e);}else{return;}e.stopImmediatePropagation();e.preventDefault();if(e.keyCode>=33&&e.keyCode<=40){// if i press an arrow key just update selection\nthis.clearContextTop();this.renderCursorOrSelection();}else{this.canvas&&this.canvas.requestRenderAll();}},/**\n * Handles keyup event\n * We handle KeyUp because ie11 and edge have difficulties copy/pasting\n * if a copy/cut event fired, keyup is dismissed\n * @param {Event} e Event object\n */onKeyUp:function onKeyUp(e){if(!this.isEditing||this._copyDone||this.inCompositionMode){this._copyDone=false;return;}if(e.keyCode in this.ctrlKeysMapUp&&(e.ctrlKey||e.metaKey)){this[this.ctrlKeysMapUp[e.keyCode]](e);}else{return;}e.stopImmediatePropagation();e.preventDefault();this.canvas&&this.canvas.requestRenderAll();},/**\n * Handles onInput event\n * @param {Event} e Event object\n */onInput:function onInput(e){var fromPaste=this.fromPaste;this.fromPaste=false;e&&e.stopPropagation();if(!this.isEditing){return;}// decisions about style changes.\nvar nextText=this._splitTextIntoLines(this.hiddenTextarea.value).graphemeText,charCount=this._text.length,nextCharCount=nextText.length,removedText,insertedText,charDiff=nextCharCount-charCount;if(this.hiddenTextarea.value===''){this.styles={};this.updateFromTextArea();this.fire('changed');if(this.canvas){this.canvas.fire('text:changed',{target:this});this.canvas.requestRenderAll();}return;}var textareaSelection=this.fromStringToGraphemeSelection(this.hiddenTextarea.selectionStart,this.hiddenTextarea.selectionEnd,this.hiddenTextarea.value);var backDelete=this.selectionStart>textareaSelection.selectionStart;if(this.selectionStart!==this.selectionEnd){removedText=this._text.slice(this.selectionStart,this.selectionEnd);charDiff+=this.selectionEnd-this.selectionStart;}else if(nextCharCount<charCount){if(backDelete){removedText=this._text.slice(this.selectionEnd+charDiff,this.selectionEnd);}else{removedText=this._text.slice(this.selectionStart,this.selectionStart-charDiff);}}insertedText=nextText.slice(textareaSelection.selectionEnd-charDiff,textareaSelection.selectionEnd);if(removedText&&removedText.length){if(this.selectionStart!==this.selectionEnd){this.removeStyleFromTo(this.selectionStart,this.selectionEnd);}else if(backDelete){// detect differencies between forwardDelete and backDelete\nthis.removeStyleFromTo(this.selectionEnd-removedText.length,this.selectionEnd);}else{this.removeStyleFromTo(this.selectionEnd,this.selectionEnd+removedText.length);}}if(insertedText.length){if(fromPaste&&insertedText.join('')===fabric.copiedText&&!fabric.disableStyleCopyPaste){this.insertNewStyleBlock(insertedText,this.selectionStart,fabric.copiedTextStyle);}else{this.insertNewStyleBlock(insertedText,this.selectionStart);}}this.updateFromTextArea();this.fire('changed');if(this.canvas){this.canvas.fire('text:changed',{target:this});this.canvas.requestRenderAll();}},/**\n * Composition start\n */onCompositionStart:function onCompositionStart(){this.inCompositionMode=true;},/**\n * Composition end\n */onCompositionEnd:function onCompositionEnd(){this.inCompositionMode=false;},// /**\n// * Composition update\n// */\nonCompositionUpdate:function onCompositionUpdate(e){this.compositionStart=e.target.selectionStart;this.compositionEnd=e.target.selectionEnd;this.updateTextareaPosition();},/**\n * Copies selected text\n * @param {Event} e Event object\n */copy:function copy(){if(this.selectionStart===this.selectionEnd){//do not cut-copy if no selection\nreturn;}fabric.copiedText=this.getSelectedText();if(!fabric.disableStyleCopyPaste){fabric.copiedTextStyle=this.getSelectionStyles(this.selectionStart,this.selectionEnd,true);}else{fabric.copiedTextStyle=null;}this._copyDone=true;},/**\n * Pastes text\n * @param {Event} e Event object\n */paste:function paste(){this.fromPaste=true;},/**\n * @private\n * @param {Event} e Event object\n * @return {Object} Clipboard data object\n */_getClipboardData:function _getClipboardData(e){return e&&e.clipboardData||fabric.window.clipboardData;},/**\n * Finds the width in pixels before the cursor on the same line\n * @private\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @return {Number} widthBeforeCursor width before cursor\n */_getWidthBeforeCursor:function _getWidthBeforeCursor(lineIndex,charIndex){var widthBeforeCursor=this._getLineLeftOffset(lineIndex),bound;if(charIndex>0){bound=this.__charBounds[lineIndex][charIndex-1];widthBeforeCursor+=bound.left+bound.width;}return widthBeforeCursor;},/**\n * Gets start offset of a selection\n * @param {Event} e Event object\n * @param {Boolean} isRight\n * @return {Number}\n */getDownCursorOffset:function getDownCursorOffset(e,isRight){var selectionProp=this._getSelectionForOffset(e,isRight),cursorLocation=this.get2DCursorLocation(selectionProp),lineIndex=cursorLocation.lineIndex;// if on last line, down cursor goes to end of line\nif(lineIndex===this._textLines.length-1||e.metaKey||e.keyCode===34){// move to the end of a text\nreturn this._text.length-selectionProp;}var charIndex=cursorLocation.charIndex,widthBeforeCursor=this._getWidthBeforeCursor(lineIndex,charIndex),indexOnOtherLine=this._getIndexOnLine(lineIndex+1,widthBeforeCursor),textAfterCursor=this._textLines[lineIndex].slice(charIndex);return textAfterCursor.length+indexOnOtherLine+1+this.missingNewlineOffset(lineIndex);},/**\n * private\n * Helps finding if the offset should be counted from Start or End\n * @param {Event} e Event object\n * @param {Boolean} isRight\n * @return {Number}\n */_getSelectionForOffset:function _getSelectionForOffset(e,isRight){if(e.shiftKey&&this.selectionStart!==this.selectionEnd&&isRight){return this.selectionEnd;}else{return this.selectionStart;}},/**\n * @param {Event} e Event object\n * @param {Boolean} isRight\n * @return {Number}\n */getUpCursorOffset:function getUpCursorOffset(e,isRight){var selectionProp=this._getSelectionForOffset(e,isRight),cursorLocation=this.get2DCursorLocation(selectionProp),lineIndex=cursorLocation.lineIndex;if(lineIndex===0||e.metaKey||e.keyCode===33){// if on first line, up cursor goes to start of line\nreturn-selectionProp;}var charIndex=cursorLocation.charIndex,widthBeforeCursor=this._getWidthBeforeCursor(lineIndex,charIndex),indexOnOtherLine=this._getIndexOnLine(lineIndex-1,widthBeforeCursor),textBeforeCursor=this._textLines[lineIndex].slice(0,charIndex),missingNewlineOffset=this.missingNewlineOffset(lineIndex-1);// return a negative offset\nreturn-this._textLines[lineIndex-1].length+indexOnOtherLine-textBeforeCursor.length+(1-missingNewlineOffset);},/**\n * for a given width it founds the matching character.\n * @private\n */_getIndexOnLine:function _getIndexOnLine(lineIndex,width){var line=this._textLines[lineIndex],lineLeftOffset=this._getLineLeftOffset(lineIndex),widthOfCharsOnLine=lineLeftOffset,indexOnLine=0,charWidth,foundMatch;for(var j=0,jlen=line.length;j<jlen;j++){charWidth=this.__charBounds[lineIndex][j].width;widthOfCharsOnLine+=charWidth;if(widthOfCharsOnLine>width){foundMatch=true;var leftEdge=widthOfCharsOnLine-charWidth,rightEdge=widthOfCharsOnLine,offsetFromLeftEdge=Math.abs(leftEdge-width),offsetFromRightEdge=Math.abs(rightEdge-width);indexOnLine=offsetFromRightEdge<offsetFromLeftEdge?j:j-1;break;}}// reached end\nif(!foundMatch){indexOnLine=line.length-1;}return indexOnLine;},/**\n * Moves cursor down\n * @param {Event} e Event object\n */moveCursorDown:function moveCursorDown(e){if(this.selectionStart>=this._text.length&&this.selectionEnd>=this._text.length){return;}this._moveCursorUpOrDown('Down',e);},/**\n * Moves cursor up\n * @param {Event} e Event object\n */moveCursorUp:function moveCursorUp(e){if(this.selectionStart===0&&this.selectionEnd===0){return;}this._moveCursorUpOrDown('Up',e);},/**\n * Moves cursor up or down, fires the events\n * @param {String} direction 'Up' or 'Down'\n * @param {Event} e Event object\n */_moveCursorUpOrDown:function _moveCursorUpOrDown(direction,e){// getUpCursorOffset\n// getDownCursorOffset\nvar action='get'+direction+'CursorOffset',offset=this[action](e,this._selectionDirection==='right');if(e.shiftKey){this.moveCursorWithShift(offset);}else{this.moveCursorWithoutShift(offset);}if(offset!==0){this.setSelectionInBoundaries();this.abortCursorAnimation();this._currentCursorOpacity=1;this.initDelayedCursor();this._fireSelectionChanged();this._updateTextarea();}},/**\n * Moves cursor with shift\n * @param {Number} offset\n */moveCursorWithShift:function moveCursorWithShift(offset){var newSelection=this._selectionDirection==='left'?this.selectionStart+offset:this.selectionEnd+offset;this.setSelectionStartEndWithShift(this.selectionStart,this.selectionEnd,newSelection);return offset!==0;},/**\n * Moves cursor up without shift\n * @param {Number} offset\n */moveCursorWithoutShift:function moveCursorWithoutShift(offset){if(offset<0){this.selectionStart+=offset;this.selectionEnd=this.selectionStart;}else{this.selectionEnd+=offset;this.selectionStart=this.selectionEnd;}return offset!==0;},/**\n * Moves cursor left\n * @param {Event} e Event object\n */moveCursorLeft:function moveCursorLeft(e){if(this.selectionStart===0&&this.selectionEnd===0){return;}this._moveCursorLeftOrRight('Left',e);},/**\n * @private\n * @return {Boolean} true if a change happened\n */_move:function _move(e,prop,direction){var newValue;if(e.altKey){newValue=this['findWordBoundary'+direction](this[prop]);}else if(e.metaKey||e.keyCode===35||e.keyCode===36){newValue=this['findLineBoundary'+direction](this[prop]);}else{this[prop]+=direction==='Left'?-1:1;return true;}if(_typeof(newValue)!==undefined&&this[prop]!==newValue){this[prop]=newValue;return true;}},/**\n * @private\n */_moveLeft:function _moveLeft(e,prop){return this._move(e,prop,'Left');},/**\n * @private\n */_moveRight:function _moveRight(e,prop){return this._move(e,prop,'Right');},/**\n * Moves cursor left without keeping selection\n * @param {Event} e\n */moveCursorLeftWithoutShift:function moveCursorLeftWithoutShift(e){var change=true;this._selectionDirection='left';// only move cursor when there is no selection,\n// otherwise we discard it, and leave cursor on same place\nif(this.selectionEnd===this.selectionStart&&this.selectionStart!==0){change=this._moveLeft(e,'selectionStart');}this.selectionEnd=this.selectionStart;return change;},/**\n * Moves cursor left while keeping selection\n * @param {Event} e\n */moveCursorLeftWithShift:function moveCursorLeftWithShift(e){if(this._selectionDirection==='right'&&this.selectionStart!==this.selectionEnd){return this._moveLeft(e,'selectionEnd');}else if(this.selectionStart!==0){this._selectionDirection='left';return this._moveLeft(e,'selectionStart');}},/**\n * Moves cursor right\n * @param {Event} e Event object\n */moveCursorRight:function moveCursorRight(e){if(this.selectionStart>=this._text.length&&this.selectionEnd>=this._text.length){return;}this._moveCursorLeftOrRight('Right',e);},/**\n * Moves cursor right or Left, fires event\n * @param {String} direction 'Left', 'Right'\n * @param {Event} e Event object\n */_moveCursorLeftOrRight:function _moveCursorLeftOrRight(direction,e){var actionName='moveCursor'+direction+'With';this._currentCursorOpacity=1;if(e.shiftKey){actionName+='Shift';}else{actionName+='outShift';}if(this[actionName](e)){this.abortCursorAnimation();this.initDelayedCursor();this._fireSelectionChanged();this._updateTextarea();}},/**\n * Moves cursor right while keeping selection\n * @param {Event} e\n */moveCursorRightWithShift:function moveCursorRightWithShift(e){if(this._selectionDirection==='left'&&this.selectionStart!==this.selectionEnd){return this._moveRight(e,'selectionStart');}else if(this.selectionEnd!==this._text.length){this._selectionDirection='right';return this._moveRight(e,'selectionEnd');}},/**\n * Moves cursor right without keeping selection\n * @param {Event} e Event object\n */moveCursorRightWithoutShift:function moveCursorRightWithoutShift(e){var changed=true;this._selectionDirection='right';if(this.selectionStart===this.selectionEnd){changed=this._moveRight(e,'selectionStart');this.selectionEnd=this.selectionStart;}else{this.selectionStart=this.selectionEnd;}return changed;},/**\n * Removes characters from start/end\n * start/end ar per grapheme position in _text array.\n *\n * @param {Number} start\n * @param {Number} end default to start + 1\n */removeChars:function removeChars(start,end){if(typeof end==='undefined'){end=start+1;}this.removeStyleFromTo(start,end);this._text.splice(start,end-start);this.text=this._text.join('');this.set('dirty',true);if(this._shouldClearDimensionCache()){this.initDimensions();this.setCoords();}this._removeExtraneousStyles();},/**\n * insert characters at start position, before start position.\n * start equal 1 it means the text get inserted between actual grapheme 0 and 1\n * if style array is provided, it must be as the same length of text in graphemes\n * if end is provided and is bigger than start, old text is replaced.\n * start/end ar per grapheme position in _text array.\n *\n * @param {String} text text to insert\n * @param {Array} style array of style objects\n * @param {Number} start\n * @param {Number} end default to start + 1\n */insertChars:function insertChars(text,style,start,end){if(typeof end==='undefined'){end=start;}if(end>start){this.removeStyleFromTo(start,end);}var graphemes=fabric.util.string.graphemeSplit(text);this.insertNewStyleBlock(graphemes,start,style);this._text=[].concat(this._text.slice(0,start),graphemes,this._text.slice(end));this.text=this._text.join('');this.set('dirty',true);if(this._shouldClearDimensionCache()){this.initDimensions();this.setCoords();}this._removeExtraneousStyles();}});/* _TO_SVG_START_ */(function(){var toFixed=fabric.util.toFixed,multipleSpacesRegex=/ +/g;fabric.util.object.extend(fabric.Text.prototype,/** @lends fabric.Text.prototype */{/**\n * Returns SVG representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */_toSVG:function _toSVG(){var offsets=this._getSVGLeftTopOffsets(),textAndBg=this._getSVGTextAndBg(offsets.textTop,offsets.textLeft);return this._wrapSVGTextAndBg(textAndBg);},/**\n * Returns svg representation of an instance\n * @param {Function} [reviver] Method for further parsing of svg representation.\n * @return {String} svg representation of an instance\n */toSVG:function toSVG(reviver){return this._createBaseSVGMarkup(this._toSVG(),{reviver:reviver,noStyle:true,withShadow:true});},/**\n * @private\n */_getSVGLeftTopOffsets:function _getSVGLeftTopOffsets(){return{textLeft:-this.width/2,textTop:-this.height/2,lineTop:this.getHeightOfLine(0)};},/**\n * @private\n */_wrapSVGTextAndBg:function _wrapSVGTextAndBg(textAndBg){var noShadow=true,textDecoration=this.getSvgTextDecoration(this);return[textAndBg.textBgRects.join(''),'\\t\\t<text xml:space=\"preserve\" ',this.fontFamily?'font-family=\"'+this.fontFamily.replace(/\"/g,'\\'')+'\" ':'',this.fontSize?'font-size=\"'+this.fontSize+'\" ':'',this.fontStyle?'font-style=\"'+this.fontStyle+'\" ':'',this.fontWeight?'font-weight=\"'+this.fontWeight+'\" ':'',textDecoration?'text-decoration=\"'+textDecoration+'\" ':'','style=\"',this.getSvgStyles(noShadow),'\"',this.addPaintOrder(),' >',textAndBg.textSpans.join(''),'</text>\\n'];},/**\n * @private\n * @param {Number} textTopOffset Text top offset\n * @param {Number} textLeftOffset Text left offset\n * @return {Object}\n */_getSVGTextAndBg:function _getSVGTextAndBg(textTopOffset,textLeftOffset){var textSpans=[],textBgRects=[],height=textTopOffset,lineOffset;// bounding-box background\nthis._setSVGBg(textBgRects);// text and text-background\nfor(var i=0,len=this._textLines.length;i<len;i++){lineOffset=this._getLineLeftOffset(i);if(this.textBackgroundColor||this.styleHas('textBackgroundColor',i)){this._setSVGTextLineBg(textBgRects,i,textLeftOffset+lineOffset,height);}this._setSVGTextLineText(textSpans,i,textLeftOffset+lineOffset,height);height+=this.getHeightOfLine(i);}return{textSpans:textSpans,textBgRects:textBgRects};},/**\n * @private\n */_createTextCharSpan:function _createTextCharSpan(_char,styleDecl,left,top){var shouldUseWhitespace=_char!==_char.trim()||_char.match(multipleSpacesRegex),styleProps=this.getSvgSpanStyles(styleDecl,shouldUseWhitespace),fillStyles=styleProps?'style=\"'+styleProps+'\"':'',dy=styleDecl.deltaY,dySpan='',NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS;if(dy){dySpan=' dy=\"'+toFixed(dy,NUM_FRACTION_DIGITS)+'\" ';}return['<tspan x=\"',toFixed(left,NUM_FRACTION_DIGITS),'\" y=\"',toFixed(top,NUM_FRACTION_DIGITS),'\" ',dySpan,fillStyles,'>',fabric.util.string.escapeXml(_char),'</tspan>'].join('');},_setSVGTextLineText:function _setSVGTextLineText(textSpans,lineIndex,textLeftOffset,textTopOffset){// set proper line offset\nvar lineHeight=this.getHeightOfLine(lineIndex),isJustify=this.textAlign.indexOf('justify')!==-1,actualStyle,nextStyle,charsToRender='',charBox,style,boxWidth=0,line=this._textLines[lineIndex],timeToRender;textTopOffset+=lineHeight*(1-this._fontSizeFraction)/this.lineHeight;for(var i=0,len=line.length-1;i<=len;i++){timeToRender=i===len||this.charSpacing;charsToRender+=line[i];charBox=this.__charBounds[lineIndex][i];if(boxWidth===0){textLeftOffset+=charBox.kernedWidth-charBox.width;boxWidth+=charBox.width;}else{boxWidth+=charBox.kernedWidth;}if(isJustify&&!timeToRender){if(this._reSpaceAndTab.test(line[i])){timeToRender=true;}}if(!timeToRender){// if we have charSpacing, we render char by char\nactualStyle=actualStyle||this.getCompleteStyleDeclaration(lineIndex,i);nextStyle=this.getCompleteStyleDeclaration(lineIndex,i+1);timeToRender=this._hasStyleChangedForSvg(actualStyle,nextStyle);}if(timeToRender){style=this._getStyleDeclaration(lineIndex,i)||{};textSpans.push(this._createTextCharSpan(charsToRender,style,textLeftOffset,textTopOffset));charsToRender='';actualStyle=nextStyle;textLeftOffset+=boxWidth;boxWidth=0;}}},_pushTextBgRect:function _pushTextBgRect(textBgRects,color,left,top,width,height){var NUM_FRACTION_DIGITS=fabric.Object.NUM_FRACTION_DIGITS;textBgRects.push('\\t\\t<rect ',this._getFillAttributes(color),' x=\"',toFixed(left,NUM_FRACTION_DIGITS),'\" y=\"',toFixed(top,NUM_FRACTION_DIGITS),'\" width=\"',toFixed(width,NUM_FRACTION_DIGITS),'\" height=\"',toFixed(height,NUM_FRACTION_DIGITS),'\"></rect>\\n');},_setSVGTextLineBg:function _setSVGTextLineBg(textBgRects,i,leftOffset,textTopOffset){var line=this._textLines[i],heightOfLine=this.getHeightOfLine(i)/this.lineHeight,boxWidth=0,boxStart=0,charBox,currentColor,lastColor=this.getValueOfPropertyAt(i,0,'textBackgroundColor');for(var j=0,jlen=line.length;j<jlen;j++){charBox=this.__charBounds[i][j];currentColor=this.getValueOfPropertyAt(i,j,'textBackgroundColor');if(currentColor!==lastColor){lastColor&&this._pushTextBgRect(textBgRects,lastColor,leftOffset+boxStart,textTopOffset,boxWidth,heightOfLine);boxStart=charBox.left;boxWidth=charBox.width;lastColor=currentColor;}else{boxWidth+=charBox.kernedWidth;}}currentColor&&this._pushTextBgRect(textBgRects,currentColor,leftOffset+boxStart,textTopOffset,boxWidth,heightOfLine);},/**\n * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values\n * we work around it by \"moving\" alpha channel into opacity attribute and setting fill's alpha to 1\n *\n * @private\n * @param {*} value\n * @return {String}\n */_getFillAttributes:function _getFillAttributes(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()+'\"';},/**\n * @private\n */_getSVGLineTopOffset:function _getSVGLineTopOffset(lineIndex){var lineTopOffset=0,lastHeight=0;for(var j=0;j<lineIndex;j++){lineTopOffset+=this.getHeightOfLine(j);}lastHeight=this.getHeightOfLine(j);return{lineTop:lineTopOffset,offset:(this._fontSizeMult-this._fontSizeFraction)*lastHeight/(this.lineHeight*this._fontSizeMult)};},/**\n * Returns styles-string for svg-export\n * @param {Boolean} skipShadow a boolean to skip shadow filter output\n * @return {String}\n */getSvgStyles:function getSvgStyles(skipShadow){var svgStyle=fabric.Object.prototype.getSvgStyles.call(this,skipShadow);return svgStyle+' white-space: pre;';}});})();/* _TO_SVG_END_ */(function(global){'use strict';var fabric=global.fabric||(global.fabric={});/**\n * Textbox class, based on IText, allows the user to resize the text rectangle\n * and wraps lines automatically. Textboxes have their Y scaling locked, the\n * user can only change width. Height is adjusted automatically based on the\n * wrapping of lines.\n * @class fabric.Textbox\n * @extends fabric.IText\n * @mixes fabric.Observable\n * @return {fabric.Textbox} thisArg\n * @see {@link fabric.Textbox#initialize} for constructor definition\n */fabric.Textbox=fabric.util.createClass(fabric.IText,fabric.Observable,{/**\n * Type of an object\n * @type String\n * @default\n */type:'textbox',/**\n * Minimum width of textbox, in pixels.\n * @type Number\n * @default\n */minWidth:20,/**\n * Minimum calculated width of a textbox, in pixels.\n * fixed to 2 so that an empty textbox cannot go to 0\n * and is still selectable without text.\n * @type Number\n * @default\n */dynamicMinWidth:2,/**\n * Cached array of text wrapping.\n * @type Array\n */__cachedLines:null,/**\n * Override standard Object class values\n */lockScalingFlip:true,/**\n * Override standard Object class values\n * Textbox needs this on false\n */noScaleCache:false,/**\n * Properties which when set cause object to change dimensions\n * @type Object\n * @private\n */_dimensionAffectingProps:fabric.Text.prototype._dimensionAffectingProps.concat('width'),/**\n * Use this regular expression to split strings in breakable lines\n * @private\n */_wordJoiners:/[ \\t\\r]/,/**\n * Use this boolean property in order to split strings that have no white space concept.\n * this is a cheap way to help with chinese/japaense\n * @type Boolean\n * @since 2.6.0\n */splitByGrapheme:false,/**\n * Unlike superclass's version of this function, Textbox does not update\n * its width.\n * @private\n * @override\n */initDimensions:function initDimensions(){if(this.__skipDimension){return;}this.isEditing&&this.initDelayedCursor();this.clearContextTop();this._clearCache();// clear dynamicMinWidth as it will be different after we re-wrap line\nthis.dynamicMinWidth=0;// wrap lines\nthis._styleMap=this._generateStyleMap(this._splitText());// if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap\nif(this.dynamicMinWidth>this.width){this._set('width',this.dynamicMinWidth);}if(this.textAlign.indexOf('justify')!==-1){// once text is measured we need to make space fatter to make justified text.\nthis.enlargeSpaces();}// clear cache and re-calculate height\nthis.height=this.calcTextHeight();this.saveState({propertySet:'_dimensionAffectingProps'});},/**\n * Generate an object that translates the style object so that it is\n * broken up by visual lines (new lines and automatic wrapping).\n * The original text styles object is broken up by actual lines (new lines only),\n * which is only sufficient for Text / IText\n * @private\n */_generateStyleMap:function _generateStyleMap(textInfo){var realLineCount=0,realLineCharCount=0,charCount=0,map={};for(var i=0;i<textInfo.graphemeLines.length;i++){if(textInfo.graphemeText[charCount]==='\\n'&&i>0){realLineCharCount=0;charCount++;realLineCount++;}else if(!this.splitByGrapheme&&this._reSpaceAndTab.test(textInfo.graphemeText[charCount])&&i>0){// this case deals with space's that are removed from end of lines when wrapping\nrealLineCharCount++;charCount++;}map[i]={line:realLineCount,offset:realLineCharCount};charCount+=textInfo.graphemeLines[i].length;realLineCharCount+=textInfo.graphemeLines[i].length;}return map;},/**\n * Returns true if object has a style property or has it on a specified line\n * @param {Number} lineIndex\n * @return {Boolean}\n */styleHas:function styleHas(property,lineIndex){if(this._styleMap&&!this.isWrapping){var map=this._styleMap[lineIndex];if(map){lineIndex=map.line;}}return fabric.Text.prototype.styleHas.call(this,property,lineIndex);},/**\n * Returns true if object has no styling or no styling in a line\n * @param {Number} lineIndex , lineIndex is on wrapped lines.\n * @return {Boolean}\n */isEmptyStyles:function isEmptyStyles(lineIndex){if(!this.styles){return true;}var offset=0,nextLineIndex=lineIndex+1,nextOffset,obj,shouldLimit=false,map=this._styleMap[lineIndex],mapNextLine=this._styleMap[lineIndex+1];if(map){lineIndex=map.line;offset=map.offset;}if(mapNextLine){nextLineIndex=mapNextLine.line;shouldLimit=nextLineIndex===lineIndex;nextOffset=mapNextLine.offset;}obj=typeof lineIndex==='undefined'?this.styles:{line:this.styles[lineIndex]};for(var p1 in obj){for(var p2 in obj[p1]){if(p2>=offset&&(!shouldLimit||p2<nextOffset)){// eslint-disable-next-line no-unused-vars\nfor(var p3 in obj[p1][p2]){return false;}}}}return true;},/**\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @private\n */_getStyleDeclaration:function _getStyleDeclaration(lineIndex,charIndex){if(this._styleMap&&!this.isWrapping){var map=this._styleMap[lineIndex];if(!map){return null;}lineIndex=map.line;charIndex=map.offset+charIndex;}return this.callSuper('_getStyleDeclaration',lineIndex,charIndex);},/**\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @param {Object} style\n * @private\n */_setStyleDeclaration:function _setStyleDeclaration(lineIndex,charIndex,style){var map=this._styleMap[lineIndex];lineIndex=map.line;charIndex=map.offset+charIndex;this.styles[lineIndex][charIndex]=style;},/**\n * @param {Number} lineIndex\n * @param {Number} charIndex\n * @private\n */_deleteStyleDeclaration:function _deleteStyleDeclaration(lineIndex,charIndex){var map=this._styleMap[lineIndex];lineIndex=map.line;charIndex=map.offset+charIndex;delete this.styles[lineIndex][charIndex];},/**\n * probably broken need a fix\n * Returns the real style line that correspond to the wrapped lineIndex line\n * Used just to verify if the line does exist or not.\n * @param {Number} lineIndex\n * @returns {Boolean} if the line exists or not\n * @private\n */_getLineStyle:function _getLineStyle(lineIndex){var map=this._styleMap[lineIndex];return!!this.styles[map.line];},/**\n * Set the line style to an empty object so that is initialized\n * @param {Number} lineIndex\n * @param {Object} style\n * @private\n */_setLineStyle:function _setLineStyle(lineIndex){var map=this._styleMap[lineIndex];this.styles[map.line]={};},/**\n * Wraps text using the 'width' property of Textbox. First this function\n * splits text on newlines, so we preserve newlines entered by the user.\n * Then it wraps each line using the width of the Textbox by calling\n * _wrapLine().\n * @param {Array} lines The string array of text that is split into lines\n * @param {Number} desiredWidth width you want to wrap to\n * @returns {Array} Array of lines\n */_wrapText:function _wrapText(lines,desiredWidth){var wrapped=[],i;this.isWrapping=true;for(i=0;i<lines.length;i++){wrapped=wrapped.concat(this._wrapLine(lines[i],i,desiredWidth));}this.isWrapping=false;return wrapped;},/**\n * Helper function to measure a string of text, given its lineIndex and charIndex offset\n * it gets called when charBounds are not available yet.\n * @param {CanvasRenderingContext2D} ctx\n * @param {String} text\n * @param {number} lineIndex\n * @param {number} charOffset\n * @returns {number}\n * @private\n */_measureWord:function _measureWord(word,lineIndex,charOffset){var width=0,prevGrapheme,skipLeft=true;charOffset=charOffset||0;for(var i=0,len=word.length;i<len;i++){var box=this._getGraphemeBox(word[i],lineIndex,i+charOffset,prevGrapheme,skipLeft);width+=box.kernedWidth;prevGrapheme=word[i];}return width;},/**\n * Wraps a line of text using the width of the Textbox and a context.\n * @param {Array} line The grapheme array that represent the line\n * @param {Number} lineIndex\n * @param {Number} desiredWidth width you want to wrap the line to\n * @param {Number} reservedSpace space to remove from wrapping for custom functionalities\n * @returns {Array} Array of line(s) into which the given text is wrapped\n * to.\n */_wrapLine:function _wrapLine(_line,lineIndex,desiredWidth,reservedSpace){var lineWidth=0,splitByGrapheme=this.splitByGrapheme,graphemeLines=[],line=[],// spaces in different languges?\nwords=splitByGrapheme?fabric.util.string.graphemeSplit(_line):_line.split(this._wordJoiners),word='',offset=0,infix=splitByGrapheme?'':' ',wordWidth=0,infixWidth=0,largestWordWidth=0,lineJustStarted=true,additionalSpace=splitByGrapheme?0:this._getWidthOfCharSpacing(),reservedSpace=reservedSpace||0;// fix a difference between split and graphemeSplit\nif(words.length===0){words.push([]);}desiredWidth-=reservedSpace;for(var i=0;i<words.length;i++){// if using splitByGrapheme words are already in graphemes.\nword=splitByGrapheme?words[i]:fabric.util.string.graphemeSplit(words[i]);wordWidth=this._measureWord(word,lineIndex,offset);offset+=word.length;lineWidth+=infixWidth+wordWidth-additionalSpace;if(lineWidth>=desiredWidth&&!lineJustStarted){graphemeLines.push(line);line=[];lineWidth=wordWidth;lineJustStarted=true;}else{lineWidth+=additionalSpace;}if(!lineJustStarted&&!splitByGrapheme){line.push(infix);}line=line.concat(word);infixWidth=this._measureWord([infix],lineIndex,offset);offset++;lineJustStarted=false;// keep track of largest word\nif(wordWidth>largestWordWidth){largestWordWidth=wordWidth;}}i&&graphemeLines.push(line);if(largestWordWidth+reservedSpace>this.dynamicMinWidth){this.dynamicMinWidth=largestWordWidth-additionalSpace+reservedSpace;}return graphemeLines;},/**\n * Detect if the text line is ended with an hard break\n * text and itext do not have wrapping, return false\n * @param {Number} lineIndex text to split\n * @return {Boolean}\n */isEndOfWrapping:function isEndOfWrapping(lineIndex){if(!this._styleMap[lineIndex+1]){// is last line, return true;\nreturn true;}if(this._styleMap[lineIndex+1].line!==this._styleMap[lineIndex].line){// this is last line before a line break, return true;\nreturn true;}return false;},/**\n * Detect if a line has a linebreak and so we need to account for it when moving\n * and counting style.\n * @return Number\n */missingNewlineOffset:function missingNewlineOffset(lineIndex){if(this.splitByGrapheme){return this.isEndOfWrapping(lineIndex)?1:0;}return 1;},/**\n * Gets lines of text to render in the Textbox. This function calculates\n * text wrapping on the fly every time it is called.\n * @param {String} text text to split\n * @returns {Array} Array of lines in the Textbox.\n * @override\n */_splitTextIntoLines:function _splitTextIntoLines(text){var newText=fabric.Text.prototype._splitTextIntoLines.call(this,text),graphemeLines=this._wrapText(newText.lines,this.width),lines=new Array(graphemeLines.length);for(var i=0;i<graphemeLines.length;i++){lines[i]=graphemeLines[i].join('');}newText.lines=lines;newText.graphemeLines=graphemeLines;return newText;},getMinWidth:function getMinWidth(){return Math.max(this.minWidth,this.dynamicMinWidth);},_removeExtraneousStyles:function _removeExtraneousStyles(){var linesToKeep={};for(var prop in this._styleMap){if(this._textLines[prop]){linesToKeep[this._styleMap[prop].line]=1;}}for(var prop in this.styles){if(!linesToKeep[prop]){delete this.styles[prop];}}},/**\n * Returns object representation of an instance\n * @method toObject\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} object representation of an instance\n */toObject:function toObject(propertiesToInclude){return this.callSuper('toObject',['minWidth','splitByGrapheme'].concat(propertiesToInclude));}});/**\n * Returns fabric.Textbox instance from an object representation\n * @static\n * @memberOf fabric.Textbox\n * @param {Object} object Object to create an instance from\n * @param {Function} [callback] Callback to invoke when an fabric.Textbox instance is created\n */fabric.Textbox.fromObject=function(object,callback){return fabric.Object._fromObject('Textbox',object,callback,'text');};})( true?exports:undefined);\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/_buffer@4.9.2@buffer/index.js */ \"./node_modules/_buffer@4.9.2@buffer/index.js\").Buffer))\n\n//# sourceURL=webpack://QRCode/./js/fabric.js?")},"./js/qrcode.js":
/*!**********************!*\
!*** ./js/qrcode.js ***!
\**********************/
/*! no static exports found */function(module,exports,__webpack_require__){eval("var QRCode, QRCode_Art, fabric; //---------------------------------------------------------------------\n// QRCode for JavaScript\n//\n// Copyright (c) 2009 Kazuhiko Arase\n//\n// URL: http://www.d-project.com/\n//\n// Licensed under the MIT license:\n// http://www.opensource.org/licenses/mit-license.php\n//\n// The word \"QR Code\" is registered trademark of\n// DENSO WAVE INCORPORATED\n// http://www.denso-wave.com/qrcode/faqpatent-e.html\n//\n//---------------------------------------------------------------------\n\n(function () {\n fabric = __webpack_require__(/*! ./fabric */ \"./js/fabric.js\").fabric;\n\n function QR8bitByte(data) {\n this.mode = QRMode.MODE_8BIT_BYTE;\n this.data = data;\n this.parsedData = []; // Added to support UTF-8 Characters\n\n for (var i = 0, l = this.data.length; i < l; i++) {\n var byteArray = [];\n var code = this.data.charCodeAt(i);\n\n if (code > 0x10000) {\n byteArray[0] = 0xf0 | (code & 0x1c0000) >>> 18;\n byteArray[1] = 0x80 | (code & 0x3f000) >>> 12;\n byteArray[2] = 0x80 | (code & 0xfc0) >>> 6;\n byteArray[3] = 0x80 | code & 0x3f;\n } else if (code > 0x800) {\n byteArray[0] = 0xe0 | (code & 0xf000) >>> 12;\n byteArray[1] = 0x80 | (code & 0xfc0) >>> 6;\n byteArray[2] = 0x80 | code & 0x3f;\n } else if (code > 0x80) {\n byteArray[0] = 0xc0 | (code & 0x7c0) >>> 6;\n byteArray[1] = 0x80 | code & 0x3f;\n } else {\n byteArray[0] = code;\n }\n\n this.parsedData.push(byteArray);\n }\n\n this.parsedData = Array.prototype.concat.apply([], this.parsedData);\n\n if (this.parsedData.length != this.data.length) {\n this.parsedData.unshift(191);\n this.parsedData.unshift(187);\n this.parsedData.unshift(239);\n }\n }\n\n QR8bitByte.prototype = {\n getLength: function getLength(buffer) {\n return this.parsedData.length;\n },\n write: function write(buffer) {\n for (var i = 0, l = this.parsedData.length; i < l; i++) {\n buffer.put(this.parsedData[i], 8);\n }\n }\n };\n\n function QRCodeModel(typeNumber, errorCorrectLevel) {\n this.typeNumber = typeNumber;\n this.errorCorrectLevel = errorCorrectLevel;\n this.modules = null;\n this.moduleCount = 0;\n this.dataCache = null;\n this.dataList = [];\n }\n\n QRCodeModel.prototype = {\n addData: function addData(data) {\n var newData = new QR8bitByte(data);\n this.dataList.push(newData);\n this.dataCache = null;\n },\n isDark: function isDark(row, col) {\n if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {\n throw new Error(row + ',' + col);\n }\n\n return this.modules[row][col];\n },\n getModuleCount: function getModuleCount() {\n return this.moduleCount;\n },\n make: function make() {\n this.makeImpl(false, this.getBestMaskPattern());\n },\n makeImpl: function makeImpl(test, maskPattern) {\n this.moduleCount = this.typeNumber * 4 + 17;\n this.modules = new Array(this.moduleCount);\n\n for (var row = 0; row < this.moduleCount; row++) {\n this.modules[row] = new Array(this.moduleCount);\n\n for (var col = 0; col < this.moduleCount; col++) {\n this.modules[row][col] = null;\n }\n }\n\n this.setupPositionProbePattern(0, 0);\n this.setupPositionProbePattern(this.moduleCount - 7, 0);\n this.setupPositionProbePattern(0, this.moduleCount - 7);\n this.setupPositionAdjustPattern();\n this.setupTimingPattern();\n this.setupTypeInfo(test, maskPattern);\n\n if (this.typeNumber >= 7) {\n this.setupTypeNumber(test);\n }\n\n if (this.dataCache == null) {\n this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);\n }\n\n this.mapData(this.dataCache, maskPattern);\n },\n setupPositionProbePattern: function setupPositionProbePattern(row, col) {\n for (var r = -1; r <= 7; r++) {\n if (row + r <= -1 || this.moduleCount <= row + r) continue;\n\n for (var c = -1; c <= 7; c++) {\n if (col + c <= -1 || this.moduleCount <= col + c) continue;\n\n if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {\n this.modules[row + r][col + c] = true;\n } else {\n this.modules[row + r][col + c] = false;\n }\n }\n }\n },\n getBestMaskPattern: function getBestMaskPattern() {\n var minLostPoint = 0;\n var pattern = 0;\n\n for (var i = 0; i < 8; i++) {\n this.makeImpl(true, i);\n var lostPoint = QRUtil.getLostPoint(this);\n\n if (i == 0 || minLostPoint > lostPoint) {\n minLostPoint = lostPoint;\n pattern = i;\n }\n }\n\n return pattern;\n },\n createMovieClip: function createMovieClip(target_mc, instance_name, depth) {\n var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);\n var cs = 1;\n this.make();\n\n for (var row = 0; row < this.modules.length; row++) {\n var y = row * cs;\n\n for (var col = 0; col < this.modules[row].length; col++) {\n var x = col * cs;\n var dark = this.modules[row][col];\n\n if (dark) {\n qr_mc.beginFill(0, 100);\n qr_mc.moveTo(x, y);\n qr_mc.lineTo(x + cs, y);\n qr_mc.lineTo(x + cs, y + cs);\n qr_mc.lineTo(x, y + cs);\n qr_mc.endFill();\n }\n }\n }\n\n return qr_mc;\n },\n setupTimingPattern: function setupTimingPattern() {\n for (var r = 8; r < this.moduleCount - 8; r++) {\n if (this.modules[r][6] != null) {\n continue;\n }\n\n this.modules[r][6] = r % 2 == 0;\n }\n\n for (var c = 8; c < this.moduleCount - 8; c++) {\n if (this.modules[6][c] != null) {\n continue;\n }\n\n this.modules[6][c] = c % 2 == 0;\n }\n },\n setupPositionAdjustPattern: function setupPositionAdjustPattern() {\n var pos = QRUtil.getPatternPosition(this.typeNumber);\n\n for (var i = 0; i < pos.length; i++) {\n for (var j = 0; j < pos.length; j++) {\n var row = pos[i];\n var col = pos[j];\n\n if (this.modules[row][col] != null) {\n continue;\n }\n\n for (var r = -2; r <= 2; r++) {\n for (var c = -2; c <= 2; c++) {\n if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {\n this.modules[row + r][col + c] = true;\n } else {\n this.modules[row + r][col + c] = false;\n }\n }\n }\n }\n }\n },\n setupTypeNumber: function setupTypeNumber(test) {\n var bits = QRUtil.getBCHTypeNumber(this.typeNumber);\n\n for (var i = 0; i < 18; i++) {\n var mod = !test && (bits >> i & 1) == 1;\n this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;\n }\n\n for (var i = 0; i < 18; i++) {\n var mod = !test && (bits >> i & 1) == 1;\n this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;\n }\n },\n setupTypeInfo: function setupTypeInfo(test, maskPattern) {\n var data = this.errorCorrectLevel << 3 | maskPattern;\n var bits = QRUtil.getBCHTypeInfo(data);\n\n for (var i = 0; i < 15; i++) {\n var mod = !test && (bits >> i & 1) == 1;\n\n if (i < 6) {\n this.modules[i][8] = mod;\n } else if (i < 8) {\n this.modules[i + 1][8] = mod;\n } else {\n this.modules[this.moduleCount - 15 + i][8] = mod;\n }\n }\n\n for (var i = 0; i < 15; i++) {\n var mod = !test && (bits >> i & 1) == 1;\n\n if (i < 8) {\n this.modules[8][this.moduleCount - i - 1] = mod;\n } else if (i < 9) {\n this.modules[8][15 - i - 1 + 1] = mod;\n } else {\n this.modules[8][15 - i - 1] = mod;\n }\n }\n\n this.modules[this.moduleCount - 8][8] = !test;\n },\n mapData: function mapData(data, maskPattern) {\n var inc = -1;\n var row = this.moduleCount - 1;\n var bitIndex = 7;\n var byteIndex = 0;\n\n for (var col = this.moduleCount - 1; col > 0; col -= 2) {\n if (col == 6) col--;\n\n while (true) {\n for (var c = 0; c < 2; c++) {\n if (this.modules[row][col - c] == null) {\n var dark = false;\n\n if (byteIndex < data.length) {\n dark = (data[byteIndex] >>> bitIndex & 1) == 1;\n }\n\n var mask = QRUtil.getMask(maskPattern, row, col - c);\n\n if (mask) {\n dark = !dark;\n }\n\n this.modules[row][col - c] = dark;\n bitIndex--;\n\n if (bitIndex == -1) {\n byteIndex++;\n bitIndex = 7;\n }\n }\n }\n\n row += inc;\n\n if (row < 0 || this.moduleCount <= row) {\n row -= inc;\n inc = -inc;\n break;\n }\n }\n }\n }\n };\n QRCodeModel.PAD0 = 0xec;\n QRCodeModel.PAD1 = 0x11;\n\n QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) {\n var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);\n var buffer = new QRBitBuffer();\n\n for (var i = 0; i < dataList.length; i++) {\n var data = dataList[i];\n buffer.put(data.mode, 4);\n buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));\n data.write(buffer);\n }\n\n var totalDataCount = 0;\n\n for (var i = 0; i < rsBlocks.length; i++) {\n totalDataCount += rsBlocks[i].dataCount;\n }\n\n if (buffer.getLengthInBits() > totalDataCount * 8) {\n throw new Error('code length overflow. (' + buffer.getLengthInBits() + '>' + totalDataCount * 8 + ')');\n }\n\n if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {\n buffer.put(0, 4);\n }\n\n while (buffer.getLengthInBits() % 8 != 0) {\n buffer.putBit(false);\n }\n\n while (true) {\n if (buffer.getLengthInBits() >= totalDataCount * 8) {\n break;\n }\n\n buffer.put(QRCodeModel.PAD0, 8);\n\n if (buffer.getLengthInBits() >= totalDataCount * 8) {\n break;\n }\n\n buffer.put(QRCodeModel.PAD1, 8);\n }\n\n return QRCodeModel.createBytes(buffer, rsBlocks);\n };\n\n QRCodeModel.createBytes = function (buffer, rsBlocks) {\n var offset = 0;\n var maxDcCount = 0;\n var maxEcCount = 0;\n var dcdata = new Array(rsBlocks.length);\n var ecdata = new Array(rsBlocks.length);\n\n for (var r = 0; r < rsBlocks.length; r++) {\n var dcCount = rsBlocks[r].dataCount;\n var ecCount = rsBlocks[r].totalCount - dcCount;\n maxDcCount = Math.max(maxDcCount, dcCount);\n maxEcCount = Math.max(maxEcCount, ecCount);\n dcdata[r] = new Array(dcCount);\n\n for (var i = 0; i < dcdata[r].length; i++) {\n dcdata[r][i] = 0xff & buffer.buffer[i + offset];\n }\n\n offset += dcCount;\n var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);\n var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);\n var modPoly = rawPoly.mod(rsPoly);\n ecdata[r] = new Array(rsPoly.getLength() - 1);\n\n for (var i = 0; i < ecdata[r].length; i++) {\n var modIndex = i + modPoly.getLength() - ecdata[r].length;\n ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;\n }\n }\n\n var totalCodeCount = 0;\n\n for (var i = 0; i < rsBlocks.length; i++) {\n totalCodeCount += rsBlocks[i].totalCount;\n }\n\n var data = new Array(totalCodeCount);\n var index = 0;\n\n for (var i = 0; i < maxDcCount; i++) {\n for (var r = 0; r < rsBlocks.length; r++) {\n if (i < dcdata[r].length) {\n data[index++] = dcdata[r][i];\n }\n }\n }\n\n for (var i = 0; i < maxEcCount; i++) {\n for (var r = 0; r < rsBlocks.length; r++) {\n if (i < ecdata[r].length) {\n data[index++] = ecdata[r][i];\n }\n }\n }\n\n return data;\n };\n\n var QRMode = {\n MODE_NUMBER: 1 << 0,\n MODE_ALPHA_NUM: 1 << 1,\n MODE_8BIT_BYTE: 1 << 2,\n MODE_KANJI: 1 << 3\n };\n var QRErrorCorrectLevel = {\n L: 1,\n M: 0,\n Q: 3,\n H: 2\n };\n var QRMaskPattern = {\n PATTERN000: 0,\n PATTERN001: 1,\n PATTERN010: 2,\n PATTERN011: 3,\n PATTERN100: 4,\n PATTERN101: 5,\n PATTERN110: 6,\n PATTERN111: 7\n };\n var QRUtil = {\n PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]],\n G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,\n G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,\n G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,\n getBCHTypeInfo: function getBCHTypeInfo(data) {\n var d = data << 10;\n\n while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {\n d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);\n }\n\n return (data << 10 | d) ^ QRUtil.G15_MASK;\n },\n getBCHTypeNumber: function getBCHTypeNumber(data) {\n var d = data << 12;\n\n while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {\n d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);\n }\n\n return data << 12 | d;\n },\n getBCHDigit: function getBCHDigit(data) {\n var digit = 0;\n\n while (data != 0) {\n digit++;\n data >>>= 1;\n }\n\n return digit;\n },\n getPatternPosition: function getPatternPosition(typeNumber) {\n return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];\n },\n getMask: function getMask(maskPattern, i, j) {\n switch (maskPattern) {\n case QRMaskPattern.PATTERN000:\n return (i + j) % 2 == 0;\n\n case QRMaskPattern.PATTERN001:\n return i % 2 == 0;\n\n case QRMaskPattern.PATTERN010:\n return j % 3 == 0;\n\n case QRMaskPattern.PATTERN011:\n return (i + j) % 3 == 0;\n\n case QRMaskPattern.PATTERN100:\n return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;\n\n case QRMaskPattern.PATTERN101:\n return i * j % 2 + i * j % 3 == 0;\n\n case QRMaskPattern.PATTERN110:\n return (i * j % 2 + i * j % 3) % 2 == 0;\n\n case QRMaskPattern.PATTERN111:\n return (i * j % 3 + (i + j) % 2) % 2 == 0;\n\n default:\n throw new Error('bad maskPattern:' + maskPattern);\n }\n },\n getErrorCorrectPolynomial: function getErrorCorrectPolynomial(errorCorrectLength) {\n var a = new QRPolynomial([1], 0);\n\n for (var i = 0; i < errorCorrectLength; i++) {\n a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));\n }\n\n return a;\n },\n getLengthInBits: function getLengthInBits(mode, type) {\n if (1 <= type && type < 10) {\n switch (mode) {\n case QRMode.MODE_NUMBER:\n return 10;\n\n case QRMode.MODE_ALPHA_NUM:\n return 9;\n\n case QRMode.MODE_8BIT_BYTE:\n return 8;\n\n case QRMode.MODE_KANJI:\n return 8;\n\n default:\n throw new Error('mode:' + mode);\n }\n } else if (type < 27) {\n switch (mode) {\n case QRMode.MODE_NUMBER:\n return 12;\n\n case QRMode.MODE_ALPHA_NUM:\n return 11;\n\n case QRMode.MODE_8BIT_BYTE:\n return 16;\n\n case QRMode.MODE_KANJI:\n return 10;\n\n default:\n throw new Error('mode:' + mode);\n }\n } else if (type < 41) {\n switch (mode) {\n case QRMode.MODE_NUMBER:\n return 14;\n\n case QRMode.MODE_ALPHA_NUM:\n return 13;\n\n case QRMode.MODE_8BIT_BYTE:\n return 16;\n\n case QRMode.MODE_KANJI:\n return 12;\n\n default:\n throw new Error('mode:' + mode);\n }\n } else {\n throw new Error('type:' + type);\n }\n },\n getLostPoint: function getLostPoint(qrCode) {\n var moduleCount = qrCode.getModuleCount();\n var lostPoint = 0;\n\n for (var row = 0; row < moduleCount; row++) {\n for (var col = 0; col < moduleCount; col++) {\n var sameCount = 0;\n var dark = qrCode.isDark(row, col);\n\n for (var r = -1; r <= 1; r++) {\n if (row + r < 0 || moduleCount <= row + r) {\n continue;\n }\n\n for (var c = -1; c <= 1; c++) {\n if (col + c < 0 || moduleCount <= col + c) {\n continue;\n }\n\n if (r == 0 && c == 0) {\n continue;\n }\n\n if (dark == qrCode.isDark(row + r, col + c)) {\n sameCount++;\n }\n }\n }\n\n if (sameCount > 5) {\n lostPoint += 3 + sameCount - 5;\n }\n }\n }\n\n for (var row = 0; row < moduleCount - 1; row++) {\n for (var col = 0; col < moduleCount - 1; col++) {\n var count = 0;\n if (qrCode.isDark(row, col)) count++;\n if (qrCode.isDark(row + 1, col)) count++;\n if (qrCode.isDark(row, col + 1)) count++;\n if (qrCode.isDark(row + 1, col + 1)) count++;\n\n if (count == 0 || count == 4) {\n lostPoint += 3;\n }\n }\n }\n\n for (var row = 0; row < moduleCount; row++) {\n for (var col = 0; col < moduleCount - 6; col++) {\n if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) {\n lostPoint += 40;\n }\n }\n }\n\n for (var col = 0; col < moduleCount; col++) {\n for (var row = 0; row < moduleCount - 6; row++) {\n if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) {\n lostPoint += 40;\n }\n }\n }\n\n var darkCount = 0;\n\n for (var col = 0; col < moduleCount; col++) {\n for (var row = 0; row < moduleCount; row++) {\n if (qrCode.isDark(row, col)) {\n darkCount++;\n }\n }\n }\n\n var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;\n lostPoint += ratio * 10;\n return lostPoint;\n }\n };\n var QRMath = {\n glog: function glog(n) {\n if (n < 1) {\n throw new Error('glog(' + n + ')');\n }\n\n return QRMath.LOG_TABLE[n];\n },\n gexp: function gexp(n) {\n while (n < 0) {\n n += 255;\n }\n\n while (n >= 256) {\n n -= 255;\n }\n\n return QRMath.EXP_TABLE[n];\n },\n EXP_TABLE: new Array(256),\n LOG_TABLE: new Array(256)\n };\n\n for (var i = 0; i < 8; i++) {\n QRMath.EXP_TABLE[i] = 1 << i;\n }\n\n for (var i = 8; i < 256; i++) {\n QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];\n }\n\n for (var i = 0; i < 255; i++) {\n QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;\n }\n\n function QRPolynomial(num, shift) {\n if (num.length == undefined) {\n throw new Error(num.length + '/' + shift);\n }\n\n var offset = 0;\n\n while (offset < num.length && num[offset] == 0) {\n offset++;\n }\n\n this.num = new Array(num.length - offset + shift);\n\n for (var i = 0; i < num.length - offset; i++) {\n this.num[i] = num[i + offset];\n }\n }\n\n QRPolynomial.prototype = {\n get: function get(index) {\n return this.num[index];\n },\n getLength: function getLength() {\n return this.num.length;\n },\n multiply: function multiply(e) {\n var num = new Array(this.getLength() + e.getLength() - 1);\n\n for (var i = 0; i < this.getLength(); i++) {\n for (var j = 0; j < e.getLength(); j++) {\n num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));\n }\n }\n\n return new QRPolynomial(num, 0);\n },\n mod: function mod(e) {\n if (this.getLength() - e.getLength() < 0) {\n return this;\n }\n\n var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));\n var num = new Array(this.getLength());\n\n for (var i = 0; i < this.getLength(); i++) {\n num[i] = this.get(i);\n }\n\n for (var i = 0; i < e.getLength(); i++) {\n num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);\n }\n\n return new QRPolynomial(num, 0).mod(e);\n }\n };\n\n function QRRSBlock(totalCount, dataCount) {\n this.totalCount = totalCount;\n this.dataCount = dataCount;\n }\n\n QRRSBlock.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]];\n\n QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {\n var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);\n\n if (rsBlock == undefined) {\n throw new Error('bad rs block @ typeNumber:' + typeNumber + '/errorCorrectLevel:' + errorCorrectLevel);\n }\n\n var length = rsBlock.length / 3;\n var list = [];\n\n for (var i = 0; i < length; i++) {\n var count = rsBlock[i * 3 + 0];\n var totalCount = rsBlock[i * 3 + 1];\n var dataCount = rsBlock[i * 3 + 2];\n\n for (var j = 0; j < count; j++) {\n list.push(new QRRSBlock(totalCount, dataCount));\n }\n }\n\n return list;\n };\n\n QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {\n switch (errorCorrectLevel) {\n case QRErrorCorrectLevel.L:\n return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];\n\n case QRErrorCorrectLevel.M:\n return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];\n\n case QRErrorCorrectLevel.Q:\n return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];\n\n case QRErrorCorrectLevel.H:\n return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];\n\n default:\n return undefined;\n }\n };\n\n function QRBitBuffer() {\n this.buffer = [];\n this.length = 0;\n }\n\n QRBitBuffer.prototype = {\n get: function get(index) {\n var bufIndex = Math.floor(index / 8);\n return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;\n },\n put: function put(num, length) {\n for (var i = 0; i < length; i++) {\n this.putBit((num >>> length - i - 1 & 1) == 1);\n }\n },\n getLengthInBits: function getLengthInBits() {\n return this.length;\n },\n putBit: function putBit(bit) {\n var bufIndex = Math.floor(this.length / 8);\n\n if (this.buffer.length <= bufIndex) {\n this.buffer.push(0);\n }\n\n if (bit) {\n this.buffer[bufIndex] |= 0x80 >>> this.length % 8;\n }\n\n this.length++;\n }\n };\n var QRCodeLimitLength = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]];\n\n function _isSupportCanvas() {\n return typeof CanvasRenderingContext2D != 'undefined';\n } // android 2.x doesn't support Data-URI spec\n\n\n function _getAndroid() {\n var android = false;\n var sAgent = navigator.userAgent;\n\n if (/android/i.test(sAgent)) {\n // android\n android = true;\n var aMat = sAgent.toString().match(/android ([0-9]\\.[0-9])/i);\n\n if (aMat && aMat[1]) {\n android = parseFloat(aMat[1]);\n }\n }\n\n return android;\n }\n\n var svgDrawer = function () {\n var Drawing = function Drawing(el, htOption) {\n this._el = el;\n this._htOption = htOption;\n };\n\n Drawing.prototype.draw = function (oQRCode) {\n var _htOption = this._htOption;\n var _el = this._el;\n var nCount = oQRCode.getModuleCount();\n var nWidth = Math.floor(_htOption.width / nCount);\n var nHeight = Math.floor(_htOption.height / nCount);\n this.clear();\n\n function makeSVG(tag, attrs) {\n var el = document.createElementNS('http://www.w3.org/2000/svg', tag);\n\n for (var k in attrs) {\n if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);\n }\n\n return el;\n }\n\n var svg = makeSVG('svg', {\n viewBox: '0 0 ' + String(nCount) + ' ' + String(nCount),\n width: '100%',\n height: '100%',\n fill: _htOption.colorLight\n });\n svg.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', 'http://www.w3.org/1999/xlink');\n\n _el.appendChild(svg);\n\n svg.appendChild(makeSVG('rect', {\n fill: _htOption.colorLight,\n width: '100%',\n height: '100%'\n }));\n svg.appendChild(makeSVG('rect', {\n fill: _htOption.colorDark,\n width: '1',\n height: '1',\n id: 'template'\n }));\n\n for (var row = 0; row < nCount; row++) {\n for (var col = 0; col < nCount; col++) {\n if (oQRCode.isDark(row, col)) {\n var child = makeSVG('use', {\n x: String(col),\n y: String(row)\n });\n child.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#template');\n svg.appendChild(child);\n }\n }\n }\n };\n\n Drawing.prototype.clear = function () {\n while (this._el.hasChildNodes()) {\n this._el.removeChild(this._el.lastChild);\n }\n };\n\n return Drawing;\n }();\n\n var useSVG = document.documentElement.tagName.toLowerCase() === 'svg'; // Drawing in DOM by using Table tag\n\n var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? function () {\n var Drawing = function Drawing(el, htOption) {\n this._el = el;\n this._htOption = htOption;\n };\n /**\n * Draw the QRCode\n *\n * @param {QRCode} oQRCode\n */\n\n\n Drawing.prototype.draw = function (oQRCode) {\n var _htOption = this._htOption;\n var _el = this._el;\n var nCount = oQRCode.getModuleCount();\n var nWidth = Math.floor(_htOption.width / nCount);\n var nHeight = Math.floor(_htOption.height / nCount);\n var aHTML = ['<table style=\"border:0;border-collapse:collapse;\">'];\n\n for (var row = 0; row < nCount; row++) {\n aHTML.push('<tr>');\n\n for (var col = 0; col < nCount; col++) {\n aHTML.push('<td style=\"border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';\"></td>');\n }\n\n aHTML.push('</tr>');\n }\n\n aHTML.push('</table>');\n _el.innerHTML = aHTML.join(''); // Fix the margin values as real size.\n\n var elTable = _el.childNodes[0];\n var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;\n var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;\n\n if (nLeftMarginTable > 0 && nTopMarginTable > 0) {\n elTable.style.margin = nTopMarginTable + 'px ' + nLeftMarginTable + 'px';\n }\n };\n /**\n * Clear the QRCode\n */\n\n\n Drawing.prototype.clear = function () {\n this._el.innerHTML = '';\n };\n\n return Drawing;\n }() : function () {\n // Drawing in Canvas\n function _onMakeImage() {\n this._elCanvas.style.display = 'none';\n } // Android 2.1 bug workaround\n // http://code.google.com/p/android/issues/detail?id=5141\n\n\n if (window._android && window._android <= 2.1) {\n var factor = 1 / window.devicePixelRatio;\n var drawImage = CanvasRenderingContext2D.prototype.drawImage;\n\n CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {\n if ('nodeName' in image && /img/i.test(image.nodeName)) {\n for (var i = arguments.length - 1; i >= 1; i--) {\n arguments[i] = arguments[i] * factor;\n }\n } else if (typeof dw == 'undefined') {\n arguments[1] *= factor;\n arguments[2] *= factor;\n arguments[3] *= factor;\n arguments[4] *= factor;\n }\n\n drawImage.apply(this, arguments);\n };\n }\n /**\n * Check whether the user's browser supports Data URI or not\n *\n * @private\n * @param {Function} fSuccess Occurs if it supports Data URI\n * @param {Function} fFail Occurs if it doesn't support Data URI\n */\n\n\n function _safeSetDataURI(fSuccess, fFail) {\n var self = this;\n self._fFail = fFail;\n self._fSuccess = fSuccess; // Check it just once\n\n if (self._bSupportDataURI === null) {\n var el = document.createElement('img');\n\n var fOnError = function fOnError() {\n self._bSupportDataURI = false;\n\n if (self._fFail) {\n self._fFail.call(self);\n }\n };\n\n var fOnSuccess = function fOnSuccess() {\n self._bSupportDataURI = true;\n\n if (self._fSuccess) {\n self._fSuccess.call(self);\n }\n };\n\n el.onabort = fOnError;\n el.onerror = fOnError;\n el.onload = fOnSuccess;\n el.src = 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // the Image contains 1px data.\n\n return;\n } else if (self._bSupportDataURI === true && self._fSuccess) {\n self._fSuccess.call(self);\n } else if (self._bSupportDataURI === false && self._fFail) {\n self._fFail.call(self);\n }\n }\n /**\n * Drawing QRCode by using canvas\n *\n * @constructor\n * @param {HTMLElement} el\n * @param {Object} htOption QRCode Options\n */\n\n\n var Drawing = function Drawing(el, htOption, callback) {\n this._bIsPainted = false;\n this._android = _getAndroid();\n this._htOption = htOption;\n this._elCanvas = document.createElement('canvas');\n this._elCanvas.width = htOption.width;\n this._elCanvas.height = htOption.height;\n this._el = el;\n this._oContext = this._elCanvas.getContext('2d');\n this._bIsPainted = false;\n this._bSupportDataURI = null;\n this.callback = callback ? callback : function () {};\n };\n /**\n * Draw the QRCode\n *\n * @param {QRCode} oQRCode\n */\n\n\n Drawing.prototype.draw = function (oQRCode) {\n var _oContext = this._oContext;\n var _htOption = this._htOption;\n var nCount = oQRCode.getModuleCount();\n var nWidth = _htOption.width / nCount;\n var nHeight = _htOption.height / nCount;\n var nRoundedWidth = Math.round(nWidth);\n var nRoundedHeight = Math.round(nHeight);\n var imgWidth = _htOption.imgWidth;\n var imgHeight = _htOption.imgHeight;\n this.clear();\n\n for (var row = 0; row < nCount; row++) {\n for (var col = 0; col < nCount; col++) {\n var bIsDark = oQRCode.isDark(row, col);\n var nLeft = col * nWidth;\n var nTop = row * nHeight;\n _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;\n _oContext.lineWidth = 1;\n _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;\n\n _oContext.fillRect(nLeft, nTop, nWidth, nHeight); // 抗锯齿处理\n\n\n _oContext.strokeRect(Math.floor(nLeft) + 0.5, Math.floor(nTop) + 0.5, nRoundedWidth, nRoundedHeight);\n\n _oContext.strokeRect(Math.ceil(nLeft) - 0.5, Math.ceil(nTop) - 0.5, nRoundedWidth, nRoundedHeight);\n }\n }\n\n if (_htOption.src) {\n var image = new Image();\n var self = this;\n image.width = imgWidth;\n image.height = imgHeight;\n\n image.onload = function () {\n _oContext.drawImage(image, (_htOption.width - this.width) / 2, (_htOption.height - this.height) / 2, imgWidth, imgHeight);\n\n self._bIsPainted = true;\n\n self._el.appendChild(self._elCanvas);\n\n self.callback();\n };\n\n image.src = _htOption.src;\n } else {\n this._bIsPainted = true;\n\n this._el.appendChild(this._elCanvas);\n\n this.callback();\n }\n };\n /**\n * Make the image from Canvas if the browser supports Data URI.\n */\n\n\n Drawing.prototype.makeImage = function () {\n if (this._bIsPainted) {\n _safeSetDataURI.call(this, _onMakeImage);\n }\n };\n /**\n * Return whether the QRCode is painted or not\n *\n * @return {Boolean}\n */\n\n\n Drawing.prototype.isPainted = function () {\n return this._bIsPainted;\n };\n /**\n * Clear the QRCode\n */\n\n\n Drawing.prototype.clear = function () {\n this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);\n\n this._bIsPainted = false;\n };\n /**\n * @private\n * @param {Number} nNumber\n */\n\n\n Drawing.prototype.round = function (nNumber) {\n if (!nNumber) {\n return nNumber;\n }\n\n return Math.floor(nNumber * 1000) / 1000;\n };\n\n return Drawing;\n }();\n /**\n * Get the type by string length\n *\n * @private\n * @param {String} sText\n * @param {Number} nCorrectLevel\n * @return {Number} type\n */\n\n function _getTypeNumber(sText, nCorrectLevel) {\n var nType = 1;\n\n var length = _getUTF8Length(sText);\n\n for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {\n var nLimit = 0;\n\n switch (nCorrectLevel) {\n case QRErrorCorrectLevel.L:\n nLimit = QRCodeLimitLength[i][0];\n break;\n\n case QRErrorCorrectLevel.M:\n nLimit = QRCodeLimitLength[i][1];\n break;\n\n case QRErrorCorrectLevel.Q:\n nLimit = QRCodeLimitLength[i][2];\n break;\n\n case QRErrorCorrectLevel.H:\n nLimit = QRCodeLimitLength[i][3];\n break;\n }\n\n if (length <= nLimit) {\n break;\n } else {\n nType++;\n }\n }\n\n if (nType > QRCodeLimitLength.length) {\n throw new Error('Too long data');\n }\n\n return nType;\n }\n\n function _getUTF8Length(sText) {\n var replacedText = encodeURI(sText).toString().replace(/\\%[0-9a-fA-F]{2}/g, 'a');\n return replacedText.length + (replacedText.length != sText ? 3 : 0);\n }\n /**\n * @class QRCode\n * @constructor\n * @example\n * new QRCode(document.getElementById(\"test\"), \"http://jindo.dev.naver.com/collie\");\n *\n * @example\n * var oQRCode = new QRCode(\"test\", {\n * text : \"http://naver.com\",\n * width : 128,\n * height : 128\n * });\n *\n * oQRCode.clear(); // Clear the QRCode.\n * oQRCode.makeCode(\"http://map.naver.com\"); // Re-create the QRCode.\n *\n * @param {HTMLElement|String} el target element or 'id' attribute of element.\n * @param {Object|String} vOption\n * @param {String} vOption.text QRCode link data\n * @param {Number} [vOption.width=256]\n * @param {Number} [vOption.height=256]\n * @param {String} [vOption.colorDark=\"#000000\"]\n * @param {String} [vOption.colorLight=\"#ffffff\"]\n * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]\n */\n\n\n QRCode = function QRCode(el, vOption, callback) {\n this._htOption = {\n width: 256,\n height: 256,\n typeNumber: 4,\n colorDark: '#000000',\n colorLight: '#ffffff',\n correctLevel: QRErrorCorrectLevel.H,\n src: '',\n imgWidth: 58,\n imgHeight: 58\n };\n\n if (typeof vOption === 'string') {\n vOption = {\n text: vOption\n };\n } // Overwrites options\n\n\n if (vOption) {\n for (var i in vOption) {\n this._htOption[i] = vOption[i];\n }\n }\n\n if (typeof el == 'string') {\n el = document.getElementById(el);\n }\n\n if (this._htOption.useSVG) {\n Drawing = svgDrawer;\n }\n\n this._android = _getAndroid();\n this._el = el;\n this._oQRCode = null;\n this._oDrawing = new Drawing(this._el, this._htOption, callback);\n\n if (this._htOption.text) {\n this.makeCode(this._htOption.text);\n }\n };\n /**\n * Make the QRCode\n *\n * @param {String} sText link data\n */\n\n\n QRCode.prototype.makeCode = function (sText) {\n this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);\n\n this._oQRCode.addData(sText);\n\n this._oQRCode.make();\n\n this._el.title = sText;\n\n this._oDrawing.draw(this._oQRCode);\n\n this.makeImage();\n };\n /**\n * Make the Image from Canvas element\n * - It occurs automatically\n * - Android below 3 doesn't support Data-URI spec.\n *\n * @private\n */\n\n\n QRCode.prototype.makeImage = function () {\n if (typeof this._oDrawing.makeImage == 'function' && (!this._android || this._android >= 3)) {\n this._oDrawing.makeImage();\n }\n };\n /**\n * Clear the QRCode\n */\n\n\n QRCode.prototype.clear = function () {\n this._oDrawing.clear();\n };\n /**\n * @name QRCode.CorrectLevel\n */\n\n\n QRCode.CorrectLevel = QRErrorCorrectLevel;\n\n QRCode_Art = function QRCode_Art(qrcode, options) {\n if (!qrcode || !options) {\n throw new Error('参数有误');\n }\n\n this._htOption = qrcode._htOption;\n this.nCount = qrcode._oQRCode.getModuleCount();\n this.nWidth = this._htOption.width / this.nCount;\n this.nHeight = this._htOption.height / this.nCount;\n this.top = this._htOption.top || 0;\n this.left = this._htOption.left || 0;\n this._oQRCode = qrcode._oQRCode;\n this.canvas = new fabric.Canvas(options.canvasId);\n this.canvas.selection = false;\n this.canvas.hoverCursor = 'default';\n this.arrayObj = {\n row2col3: [],\n row3col2: [],\n row4: [],\n row2col2: [],\n corner: [],\n row3: [],\n col2: [],\n single: []\n };\n this.imgList = {\n type: 'img',\n list: {\n row2col3: [],\n row3col2: [],\n row3: [],\n row4: [],\n row2col2: [],\n corner: [],\n col2: [],\n single: []\n }\n };\n var list = Object.assign({}, this.imgList.list, options.imgList.list);\n this.imgList.list = list;\n\n for (var _i = 0, _Object$keys = Object.keys(this.imgList.list); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n this.imgList.list[key] = this.imgList.list[key].filter(function (item) {\n return !!item.url;\n }).map(function (item) {\n if (!item.count) item.count = 0;\n if (!item.limit) item.limit = Infinity;\n return item;\n });\n }\n\n if (options.grid) {\n this.initGridBackground();\n }\n\n this.getArray();\n this.createArtCode(options.eyeList);\n this.createArtCode(this.imgList);\n }; // 画码眼以外的格子\n\n\n QRCode_Art.prototype.drawImage = function (isDraw, imgList) {\n var nLeft, nTop, arr, row, col, nLeft, nTop, average; //row2col3\n\n var list = imgList.list;\n\n if (list.row2col3.length > 0) {\n var row2col3Len = list.row2col3.length;\n var arrIndex = 0;\n average = this.arrayObj.row2col3.length / row2col3Len;\n\n for (var i = 0; i < row2col3Len; i++) {\n while (list.row2col3[i].count < average && list.row2col3[i].count < list.row2col3[i].limit && arrIndex < this.arrayObj['row2col3'].length) {\n row = this.arrayObj['row2col3'][arrIndex][0];\n col = this.arrayObj['row2col3'][arrIndex][1];\n\n if (isDraw[row][col] && row + 1 < this.nCount && col + 2 < this.nCount && isDraw[row + 1][col] && isDraw[row][col + 1] && isDraw[row + 1][col + 1] && isDraw[row][col + 2] && isDraw[row + 1][col + 2]) {\n list.row2col3[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.row2col3[i].url, nLeft, nTop, this.nWidth * 3, this.nHeight * 2, list.row2col3[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = isDraw[row + 1][col + 1] = isDraw[row][col + 2] = isDraw[row + 1][col + 2] = false;\n }\n\n arrIndex++;\n }\n }\n } // row3col2\n\n\n if (list.row3col2.length > 0) {\n var row3col2Len = list.row3col2.length;\n var arrIndex = 0;\n average = this.arrayObj.row3col2.length / row3col2Len;\n\n for (var i = 0; i < row3col2Len; i++) {\n while (list.row3col2[i].count < average && list.row3col2[i].count < list.row3col2[i].limit && arrIndex < this.arrayObj['row3col2'].length) {\n row = this.arrayObj['row3col2'][arrIndex][0];\n col = this.arrayObj['row3col2'][arrIndex][1];\n\n if (isDraw[row][col] && row + 2 < this.nCount && col + 1 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col] && isDraw[row + 1][col + 1] && isDraw[row + 2][col + 1] && isDraw[row][col + 1]) {\n list.row3col2[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.row3col2[i].url, nLeft, nTop, this.nWidth * 2, this.nHeight * 3, list.row3col2[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = isDraw[row + 1][col + 1] = isDraw[row + 2][col + 1] = isDraw[row][col + 1] = false;\n }\n\n arrIndex++;\n }\n }\n } // row4的时候\n\n\n if (list.row4.length > 0) {\n var row4Len = list.row4.length;\n var arrIndex = 0;\n average = this.arrayObj.row4.length / row4Len;\n\n for (var i = 0; i < row4Len; i++) {\n while (list.row4[i].count < average && list.row4[i].count < list.row4[i].limit && arrIndex < this.arrayObj['row4'].length) {\n row = this.arrayObj['row4'][arrIndex][0];\n col = this.arrayObj['row4'][arrIndex][1];\n\n if (isDraw[row][col] && row + 3 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col] && isDraw[row + 3][col]) {\n list.row4[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.row4[i].url, nLeft, nTop, this.nWidth, this.nHeight * 4, list.row4[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = isDraw[row + 3][col] = false;\n }\n\n arrIndex++;\n }\n }\n } //正方形的时候\n\n\n if (list.row2col2.length > 0) {\n var row2col2Len = list.row2col2.length;\n var arrIndex = 0;\n average = this.arrayObj.row2col2.length / row2col2Len;\n\n for (var i = 0; i < row2col2Len; i++) {\n while (list.row2col2[i].count < average && list.row2col2[i].count < list.row2col2[i].limit && arrIndex < this.arrayObj['row2col2'].length) {\n row = this.arrayObj['row2col2'][arrIndex][0];\n col = this.arrayObj['row2col2'][arrIndex][1];\n\n if (isDraw[row][col] && col + 1 < this.nCount && row + 1 < this.nCount && isDraw[row][col + 1] && isDraw[row + 1][col] && isDraw[row + 1][col + 1]) {\n list.row2col2[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.row2col2[i].url, nLeft, nTop, this.nWidth * 2, this.nHeight, list.row2col2[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = isDraw[row + 1][col + 1] = false;\n }\n\n arrIndex++;\n }\n }\n } // corner的时候\n\n\n if (list.corner.length > 0) {\n var cornerLen = list.corner.length;\n var arrIndex = 0;\n average = this.arrayObj.corner.length / cornerLen;\n\n for (var i = 0; i < cornerLen; i++) {\n while (list.corner[i].count < average && list.corner[i].count < list.corner[i].limit && arrIndex < this.arrayObj['corner'].length) {\n row = this.arrayObj['corner'][arrIndex][0];\n col = this.arrayObj['corner'][arrIndex][1];\n\n if (isDraw[row][col] && col + 1 < this.nCount && row + 1 < this.nCount && isDraw[row][col + 1] && isDraw[row + 1][col]) {\n list.corner[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.corner[i].url, nLeft, nTop, this.nWidth * 2, this.nHeight * 2, list.corner[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = false;\n }\n\n arrIndex++;\n }\n }\n } // row3的时候\n\n\n if (list.row3.length > 0) {\n var row3Len = list.row3.length;\n var arrIndex = 0;\n average = this.arrayObj.row3.length / row3Len;\n\n for (var i = 0; i < row3Len; i++) {\n while (list.row3[i].count < average && list.row3[i].count < list.row3[i].limit && arrIndex < this.arrayObj['row3'].length) {\n row = this.arrayObj['row3'][arrIndex][0];\n col = this.arrayObj['row3'][arrIndex][1];\n\n if (isDraw[row][col] && row + 2 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col]) {\n list.row3[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.row3[i].url, nLeft, nTop, this.nWidth, this.nHeight * 3, list.row3[i].options);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = false;\n }\n\n arrIndex++;\n }\n }\n } //col2的时候\n\n\n if (list.col2.length > 0) {\n var col2Len = list.col2.length;\n var arrIndex = 0;\n average = this.arrayObj.col2.length / col2Len;\n\n for (var i = 0; i < col2Len; i++) {\n while (list.col2[i].count < average && list.col2[i].count < list.col2[i].limit && arrIndex < this.arrayObj['col2'].length) {\n row = this.arrayObj['col2'][arrIndex][0];\n col = this.arrayObj['col2'][arrIndex][1];\n\n if (isDraw[row][col] && col + 1 < this.nCount && isDraw[row][col + 1]) {\n list.col2[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.col2[i].url, nLeft, nTop, this.nWidth * 2, this.nHeight, list.col2[i].options);\n isDraw[row][col] = isDraw[row][col + 1] = false;\n }\n\n arrIndex++;\n }\n }\n } // 还是用es6吧\n\n\n var rest = isDraw.reduce(function (total, line) {\n return total + line.reduce(function (lineTotal, curr) {\n return curr ? lineTotal + 1 : lineTotal;\n }, 0);\n }, 0); // single的时候\n\n if (list.single.length > 0) {\n var singleLen = list.single.length;\n var arrIndex = 0;\n average = rest / singleLen;\n\n for (var i = 0; i < singleLen; i++) {\n while (list.single[i].count < average && list.single[i].count < list.single[i].limit && arrIndex < this.arrayObj['single'].length) {\n row = this.arrayObj['single'][arrIndex][0];\n col = this.arrayObj['single'][arrIndex][1];\n\n if (isDraw[row][col]) {\n list.single[i].count++;\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n this.setImage(list.single[i].url, nLeft, nTop, this.nWidth, this.nHeight, list.single[i].options);\n isDraw[row][col] = false;\n }\n\n arrIndex++;\n }\n }\n }\n }; // 画码眼\n\n\n QRCode_Art.prototype.drawEyes = function (isDraw, imgList) {\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n var nLeft = col * this.nWidth + this.left;\n var nTop = row * this.nHeight + this.top;\n\n if (isDraw[row][col]) {\n // 绘制左上码眼\n if (imgList.eye1 && row == 0 && col == 0) {\n this.setImage(imgList.eye1.url, nLeft, nTop, this.nWidth * 7, this.nHeight * 7, imgList.eye1.options); // 码眼占7个码字\n\n for (var i = 0; i < 7; i++) {\n for (var j = 0; j < 7; j++) {\n isDraw[row + i][col + j] = false;\n }\n }\n } // 绘制右上码眼\n else if (imgList.eye2 && row + 7 == this.nCount && col == 0) {\n this.setImage(imgList.eye2.url, nLeft, nTop, this.nWidth * 7, this.nHeight * 7, imgList.eye2.options); // 码眼占7个码字\n\n for (var i = 0; i < 7; i++) {\n for (var j = 0; j < 7; j++) {\n isDraw[row + i][col + j] = false;\n }\n }\n } // 绘制左下码眼\n else if (imgList.eye3 && row == 0 && col + 7 == this.nCount) {\n this.setImage(imgList.eye3.url, nLeft, nTop, this.nWidth * 7, this.nHeight * 7, imgList.eye3.options); // 码眼占7个码字\n\n for (var i = 0; i < 7; i++) {\n for (var j = 0; j < 7; j++) {\n isDraw[row + i][col + j] = false;\n }\n }\n }\n }\n }\n }\n };\n\n QRCode_Art.prototype.createArtCode = function (imgList) {\n var isDraw = JSON.parse(JSON.stringify(this._oQRCode.modules));\n\n if (imgList.type === 'eye') {\n this.drawEyes(isDraw, imgList);\n }\n\n if (imgList.type === 'img') {\n // 排除码眼的位置\n isDraw = this.filterEyeGrid();\n this.drawImage(isDraw, imgList);\n }\n }; // 获取每种类型图片的位置数组\n\n\n QRCode_Art.prototype.getArray = function () {\n var isDraw;\n var self = this; // row3col2\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (row + 1 < this.nCount && col + 2 < this.nCount && isDraw[row + 1][col] && isDraw[row][col + 1] && isDraw[row + 1][col + 1] && isDraw[row][col + 2] && isDraw[row + 1][col + 2] && isDraw[row][col]) {\n this.arrayObj.row2col3.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = isDraw[row + 1][col + 1] = isDraw[row][col + 2] = isDraw[row + 1][col + 2] = false;\n }\n }\n } // row3col2\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (row + 2 < this.nCount && col + 1 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col] && isDraw[row + 1][col + 1] && isDraw[row + 2][col + 1] && isDraw[row][col + 1] && isDraw[row][col]) {\n this.arrayObj.row3col2.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = isDraw[row + 1][col + 1] = isDraw[row + 2][col + 1] = isDraw[row][col + 1] = false;\n }\n }\n } // row4\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (row + 3 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col] && isDraw[row + 3][col] && isDraw[row][col]) {\n this.arrayObj.row4.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = isDraw[row + 3][col] = false;\n }\n }\n } // row2col2\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (col + 1 < this.nCount && row + 1 < this.nCount && isDraw[row][col + 1] && isDraw[row + 1][col] && isDraw[row + 1][col + 1] && isDraw[row][col]) {\n this.arrayObj.row2col2.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = isDraw[row + 1][col + 1] = false;\n }\n }\n } //corner\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (col + 1 < this.nCount && row + 1 < this.nCount && isDraw[row][col + 1] && isDraw[row + 1][col] && isDraw[row][col]) {\n this.arrayObj.corner.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row][col + 1] = false;\n }\n }\n } //row3\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (row + 2 < this.nCount && isDraw[row + 1][col] && isDraw[row + 2][col] && isDraw[row][col]) {\n this.arrayObj.row3.push([row, col]);\n isDraw[row][col] = isDraw[row + 1][col] = isDraw[row + 2][col] = false;\n }\n }\n } //col2\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (col + 1 < this.nCount && isDraw[row][col + 1] && isDraw[row][col]) {\n this.arrayObj.col2.push([row, col]);\n isDraw[row][col] = isDraw[row][col + 1] = false;\n }\n }\n } //single\n\n\n isDraw = this.filterEyeGrid();\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (isDraw[row][col]) {\n this.arrayObj.single.push([row, col]);\n isDraw[row][col] = false;\n }\n }\n }\n\n Object.keys(this.arrayObj).forEach(function (key) {\n self.shuffle(self.arrayObj[key]);\n });\n };\n\n QRCode_Art.prototype.filterEyeGrid = function () {\n var isDraw = JSON.parse(JSON.stringify(this._oQRCode.modules));\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n if (isDraw[row][col]) {\n if (row == 0 && col == 0 || row + 7 == this.nCount && col == 0 || row == 0 && col + 7 == this.nCount) {\n // 码眼占7个码字\n for (var i = 0; i < 7; i++) {\n for (var j = 0; j < 7; j++) {\n isDraw[row + i][col + j] = false;\n }\n }\n }\n }\n }\n }\n\n return isDraw;\n };\n\n QRCode_Art.prototype.initGridBackground = function () {\n var isDraw = JSON.parse(JSON.stringify(this._oQRCode.modules));\n var nLeft, nTop;\n\n for (var row = 0; row < this.nCount; row++) {\n for (var col = 0; col < this.nCount; col++) {\n nLeft = col * this.nWidth + this.left;\n nTop = row * this.nHeight + this.top;\n\n if (isDraw[row][col]) {\n var rect = new fabric.Rect({\n width: this.nWidth,\n height: this.nHeight,\n fill: '#aaa',\n left: nLeft,\n top: nTop\n });\n this.canvas.add(rect);\n }\n }\n }\n }; // 乱序算法\n\n\n QRCode_Art.prototype.shuffle = function (arr) {\n var i = arr.length,\n t,\n j;\n\n while (i) {\n j = Math.floor(Math.random() * i--);\n t = arr[i];\n arr[i] = arr[j];\n arr[j] = t;\n }\n }; // 绘制canvas\n\n\n QRCode_Art.prototype.setImage = function (url, left, top, width, height, options) {\n var _this = this;\n\n if (url.includes('base64') || /(.svg)$/.test(url)) {\n // url = URL.createObjectURL(base64ToBlob(url));\n fabric.loadSVGFromURL(url, function (result) {\n var shape = fabric.util.groupSVGElements(result);\n var scaleX = Math.abs(width / shape.width * 0.7);\n var scaleY = Math.abs(height / shape.width * 0.7);\n left = left + Math.abs(width - shape.width * scaleX) / 2 + 0.3;\n top = top + Math.abs(height - shape.height * scaleY) / 2 + 0.5;\n shape.set({\n hasControls: false,\n selectable: false,\n left: left,\n top: top,\n originLeft: left,\n originTop: top,\n scaleX: scaleX,\n scaleY: scaleY,\n originScaleX: scaleX,\n originScaleY: scaleY,\n name: options.name,\n type: options.type\n });\n\n _this.canvas.add(shape);\n });\n } else {\n fabric.Image.fromURL(url, function (img) {\n img.set({\n hasControls: false,\n selectable: false,\n left: left,\n top: top,\n originLeft: left,\n originTop: top,\n scaleX: width / img._element.width,\n scaleY: height / img._element.height,\n originScaleX: width / img._element.width,\n originScaleY: height / img._element.height,\n name: options.name,\n type: options.type\n });\n\n _this.canvas.add(img);\n });\n }\n }; // 创建样式\n\n\n QRCode.prototype.createStyle = function (qrcode, options) {\n this.qrCodeArt = new QRCode_Art(qrcode, options);\n };\n})();\n\nmodule.exports = QRCode;\n\n//# sourceURL=webpack://QRCode/./js/qrcode.js?")},"./node_modules/_base64-js@1.3.1@base64-js/index.js":
/*!**********************************************************!*\
!*** ./node_modules/_base64-js@1.3.1@base64-js/index.js ***!
\**********************************************************/
/*! no static exports found */function(module,exports,__webpack_require__){"use strict";eval("\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i]\n revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n var len = b64.length\n\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4')\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=')\n if (validLen === -1) validLen = len\n\n var placeHoldersLen = validLen === len\n ? 0\n : 4 - (validLen % 4)\n\n return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n var tmp\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n var curByte = 0\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0\n ? validLen - 4\n : validLen\n\n var i\n for (i = 0; i < len; i += 4) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 18) |\n (revLookup[b64.charCodeAt(i + 1)] << 12) |\n (revLookup[b64.charCodeAt(i + 2)] << 6) |\n revLookup[b64.charCodeAt(i + 3)]\n arr[curByte++] = (tmp >> 16) & 0xFF\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 2) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 2) |\n (revLookup[b64.charCodeAt(i + 1)] >> 4)\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 1) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 10) |\n (revLookup[b64.charCodeAt(i + 1)] << 4) |\n (revLookup[b64.charCodeAt(i + 2)] >> 2)\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n return arr\n}\n\nfunction tripletToBase64 (num) {\n return lookup[num >> 18 & 0x3F] +\n lookup[num >> 12 & 0x3F] +\n lookup[num >> 6 & 0x3F] +\n lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n var tmp\n var output = []\n for (var i = start; i < end; i += 3) {\n tmp =\n ((uint8[i] << 16) & 0xFF0000) +\n ((uint8[i + 1] << 8) & 0xFF00) +\n (uint8[i + 2] & 0xFF)\n output.push(tripletToBase64(tmp))\n }\n return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n var tmp\n var len = uint8.length\n var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n var parts = []\n var maxChunkLength = 16383 // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(\n uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n ))\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1]\n parts.push(\n lookup[tmp >> 2] +\n lookup[(tmp << 4) & 0x3F] +\n '=='\n )\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n parts.push(\n lookup[tmp >> 10] +\n lookup[(tmp >> 4) & 0x3F] +\n lookup[(tmp << 2) & 0x3F] +\n '='\n )\n }\n\n return parts.join('')\n}\n\n\n//# sourceURL=webpack://QRCode/./node_modules/_base64-js@1.3.1@base64-js/index.js?")},"./node_modules/_buffer@4.9.2@buffer/index.js":
/*!****************************************************!*\
!*** ./node_modules/_buffer@4.9.2@buffer/index.js ***!
\****************************************************/
/*! no static exports found */function(module,exports,__webpack_require__){"use strict";eval("/* WEBPACK VAR INJECTION */(function(global) {/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh <http://feross.org>\n * @license MIT\n */\n/* eslint-disable no-proto */\n\n\n\nvar base64 = __webpack_require__(/*! base64-js */ \"./node_modules/_base64-js@1.3.1@base64-js/index.js\")\nvar ieee754 = __webpack_require__(/*! ieee754 */ \"./node_modules/_ieee754@1.1.13@ieee754/index.js\")\nvar isArray = __webpack_require__(/*! isarray */ \"./node_modules/_isarray@1.0.0@isarray/index.js\")\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Use Object implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * Due to various browser bugs, sometimes the Object implementation will be used even\n * when the browser supports typed arrays.\n *\n * Note:\n *\n * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,\n * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.\n *\n * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.\n *\n * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of\n * incorrect length in some situations.\n\n * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they\n * get the Object implementation, which is slower but behaves correctly.\n */\nBuffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined\n ? global.TYPED_ARRAY_SUPPORT\n : typedArraySupport()\n\n/*\n * Export kMaxLength after typed array support is determined.\n */\nexports.kMaxLength = kMaxLength()\n\nfunction typedArraySupport () {\n try {\n var arr = new Uint8Array(1)\n arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}\n return arr.foo() === 42 && // typed array instances can be augmented\n typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`\n arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`\n } catch (e) {\n return false\n }\n}\n\nfunction kMaxLength () {\n return Buffer.TYPED_ARRAY_SUPPORT\n ? 0x7fffffff\n : 0x3fffffff\n}\n\nfunction createBuffer (that, length) {\n if (kMaxLength() < length) {\n throw new RangeError('Invalid typed array length')\n }\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = new Uint8Array(length)\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n if (that === null) {\n that = new Buffer(length)\n }\n that.length = length\n }\n\n return that\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {\n return new Buffer(arg, encodingOrOffset, length)\n }\n\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new Error(\n 'If encoding is specified then the first argument must be a string'\n )\n }\n return allocUnsafe(this, arg)\n }\n return from(this, arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\n// TODO: Legacy, not needed anymore. Remove in next major version.\nBuffer._augment = function (arr) {\n arr.__proto__ = Buffer.prototype\n return arr\n}\n\nfunction from (that, value, encodingOrOffset, length) {\n if (typeof value === 'number') {\n throw new TypeError('\"value\" argument must not be a number')\n }\n\n if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {\n return fromArrayBuffer(that, value, encodingOrOffset, length)\n }\n\n if (typeof value === 'string') {\n return fromString(that, value, encodingOrOffset)\n }\n\n return fromObject(that, value)\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(null, value, encodingOrOffset, length)\n}\n\nif (Buffer.TYPED_ARRAY_SUPPORT) {\n Buffer.prototype.__proto__ = Uint8Array.prototype\n Buffer.__proto__ = Uint8Array\n if (typeof Symbol !== 'undefined' && Symbol.species &&\n Buffer[Symbol.species] === Buffer) {\n // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97\n Object.defineProperty(Buffer, Symbol.species, {\n value: null,\n configurable: true\n })\n }\n}\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be a number')\n } else if (size < 0) {\n throw new RangeError('\"size\" argument must not be negative')\n }\n}\n\nfunction alloc (that, size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(that, size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpretted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(that, size).fill(fill, encoding)\n : createBuffer(that, size).fill(fill)\n }\n return createBuffer(that, size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(null, size, fill, encoding)\n}\n\nfunction allocUnsafe (that, size) {\n assertSize(size)\n that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) {\n for (var i = 0; i < size; ++i) {\n that[i] = 0\n }\n }\n return that\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(null, size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(null, size)\n}\n\nfunction fromString (that, string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('\"encoding\" must be a valid string encoding')\n }\n\n var length = byteLength(string, encoding) | 0\n that = createBuffer(that, length)\n\n var actual = that.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n that = that.slice(0, actual)\n }\n\n return that\n}\n\nfunction fromArrayLike (that, array) {\n var length = array.length < 0 ? 0 : checked(array.length) | 0\n that = createBuffer(that, length)\n for (var i = 0; i < length; i += 1) {\n that[i] = array[i] & 255\n }\n return that\n}\n\nfunction fromArrayBuffer (that, array, byteOffset, length) {\n array.byteLength // this throws if `array` is not a valid ArrayBuffer\n\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\\'offset\\' is out of bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\\'length\\' is out of bounds')\n }\n\n if (byteOffset === undefined && length === undefined) {\n array = new Uint8Array(array)\n } else if (length === undefined) {\n array = new Uint8Array(array, byteOffset)\n } else {\n array = new Uint8Array(array, byteOffset, length)\n }\n\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = array\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n that = fromArrayLike(that, array)\n }\n return that\n}\n\nfunction fromObject (that, obj) {\n if (Buffer.isBuffer(obj)) {\n var len = checked(obj.length) | 0\n that = createBuffer(that, len)\n\n if (that.length === 0) {\n return that\n }\n\n obj.copy(that, 0, 0, len)\n return that\n }\n\n if (obj) {\n if ((typeof ArrayBuffer !== 'undefined' &&\n obj.buffer instanceof ArrayBuffer) || 'length' in obj) {\n if (typeof obj.length !== 'number' || isnan(obj.length)) {\n return createBuffer(that, 0)\n }\n return fromArrayLike(that, obj)\n }\n\n if (obj.type === 'Buffer' && isArray(obj.data)) {\n return fromArrayLike(that, obj.data)\n }\n }\n\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')\n}\n\nfunction checked (length) {\n // Note: cannot use `length < kMaxLength()` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= kMaxLength()) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + kMaxLength().toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return !!(b != null && b._isBuffer)\n}\n\nBuffer.compare = function compare (a, b) {\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError('Arguments must be Buffers')\n }\n\n if (a === b) return 0\n\n var x = a.length\n var y = b.length\n\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n var i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n var buffer = Buffer.allocUnsafe(length)\n var pos = 0\n for (i = 0; i < list.length; ++i) {\n var buf = list[i]\n if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n buf.copy(buffer, pos)\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&\n (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n string = '' + string\n }\n\n var len = string.length\n if (len === 0) return 0\n\n // Use a for loop to avoid recursion\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n case undefined:\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) return utf8ToBytes(string).length // assume utf8\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n var loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coersion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect\n// Buffer instances.\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n var i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n var len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (var i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n var len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (var i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n var len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (var i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n var length = this.length | 0\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n var str = ''\n var max = exports.INSPECT_MAX_BYTES\n if (this.length > 0) {\n str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')\n if (this.length > max) str += ' ... '\n }\n return '<Buffer ' + str + '>'\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (!Buffer.isBuffer(target)) {\n throw new TypeError('Argument must be a Buffer')\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n var x = thisEnd - thisStart\n var y = end - start\n var len = Math.min(x, y)\n\n var thisCopy = this.slice(thisStart, thisEnd)\n var targetCopy = target.slice(start, end)\n\n for (var i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (isNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (Buffer.TYPED_ARRAY_SUPPORT &&\n typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n var indexSize = 1\n var arrLength = arr.length\n var valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n var i\n if (dir) {\n var foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n var found = true\n for (var j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n var remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n // must be an even number of digits\n var strLen = string.length\n if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n for (var i = 0; i < length; ++i) {\n var parsed = parseInt(string.substr(i * 2, 2), 16)\n if (isNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction latin1Write (buf, string, offset, length) {\n return asciiWrite(buf, string, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset | 0\n if (isFinite(length)) {\n length = length | 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n // legacy write(string, encoding, offset, length) - remove in v0.13\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n var remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n return asciiWrite(this, string, offset, length)\n\n case 'latin1':\n case 'binary':\n return latin1Write(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n var res = []\n\n var i = start\n while (i < end) {\n var firstByte = buf[i]\n var codePoint = null\n var bytesPerSequence = (firstByte > 0xEF) ? 4\n : (firstByte > 0xDF) ? 3\n : (firstByte > 0xBF) ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n var secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n var len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n var res = ''\n var i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n var len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n var out = ''\n for (var i = start; i < end; ++i) {\n out += toHex(buf[i])\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n var bytes = buf.slice(start, end)\n var res = ''\n for (var i = 0; i < bytes.length; i += 2) {\n res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n var len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n var newBuf\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n newBuf = this.subarray(start, end)\n newBuf.__proto__ = Buffer.prototype\n } else {\n var sliceLen = end - start\n newBuf = new Buffer(sliceLen, undefined)\n for (var i = 0; i < sliceLen; ++i) {\n newBuf[i] = this[i + start]\n }\n }\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n var val = this[offset + --byteLength]\n var mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var i = byteLength\n var mul = 1\n var val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var mul = 1\n var i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var i = byteLength - 1\n var mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nfunction objectWriteUInt16 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {\n buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>\n (littleEndian ? i : 1 - i) * 8\n }\n}\n\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nfunction objectWriteUInt32 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffffffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {\n buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff\n }\n}\n\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = 0\n var mul = 1\n var sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = byteLength - 1\n var mul = 1\n var sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n var len = end - start\n var i\n\n if (this === target && start < targetStart && targetStart < end) {\n // descending copy from end\n for (i = len - 1; i >= 0; --i) {\n target[i + targetStart] = this[i + start]\n }\n } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {\n // ascending copy from start\n for (i = 0; i < len; ++i) {\n target[i + targetStart] = this[i + start]\n }\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, start + len),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (val.length === 1) {\n var code = val.charCodeAt(0)\n if (code < 256) {\n val = code\n }\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n } else if (typeof val === 'number') {\n val = val & 255\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n var i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n var bytes = Buffer.isBuffer(val)\n ? val\n : utf8ToBytes(new Buffer(val, encoding).toString())\n var len = bytes.length\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+\\/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = stringtrim(str).replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction stringtrim (str) {\n if (str.trim) return str.trim()\n return str.replace(/^\\s+|\\s+$/g, '')\n}\n\nfunction toHex (n) {\n if (n < 16) return '0' + n.toString(16)\n return n.toString(16)\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n var codePoint\n var length = string.length\n var leadSurrogate = null\n var bytes = []\n\n for (var i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n var c, hi, lo\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n for (var i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\nfunction isnan (val) {\n return val !== val // eslint-disable-line no-self-compare\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../_webpack@4.43.0@webpack/buildin/global.js */ \"./node_modules/_webpack@4.43.0@webpack/buildin/global.js\")))\n\n//# sourceURL=webpack://QRCode/./node_modules/_buffer@4.9.2@buffer/index.js?")},"./node_modules/_ieee754@1.1.13@ieee754/index.js":
/*!*******************************************************!*\
!*** ./node_modules/_ieee754@1.1.13@ieee754/index.js ***!
\*******************************************************/
/*! no static exports found */function(module,exports){eval("exports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var nBits = -7\n var i = isLE ? (nBytes - 1) : 0\n var d = isLE ? -1 : 1\n var s = buffer[offset + i]\n\n i += d\n\n e = s & ((1 << (-nBits)) - 1)\n s >>= (-nBits)\n nBits += eLen\n for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n m = e & ((1 << (-nBits)) - 1)\n e >>= (-nBits)\n nBits += mLen\n for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n if (e === 0) {\n e = 1 - eBias\n } else if (e === eMax) {\n return m ? NaN : ((s ? -1 : 1) * Infinity)\n } else {\n m = m + Math.pow(2, mLen)\n e = e - eBias\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n var i = isLE ? 0 : (nBytes - 1)\n var d = isLE ? 1 : -1\n var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n value = Math.abs(value)\n\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0\n e = eMax\n } else {\n e = Math.floor(Math.log(value) / Math.LN2)\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--\n c *= 2\n }\n if (e + eBias >= 1) {\n value += rt / c\n } else {\n value += rt * Math.pow(2, 1 - eBias)\n }\n if (value * c >= 2) {\n e++\n c /= 2\n }\n\n if (e + eBias >= eMax) {\n m = 0\n e = eMax\n } else if (e + eBias >= 1) {\n m = ((value * c) - 1) * Math.pow(2, mLen)\n e = e + eBias\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n e = 0\n }\n }\n\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n e = (e << mLen) | m\n eLen += mLen\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n buffer[offset + i - d] |= s * 128\n}\n\n\n//# sourceURL=webpack://QRCode/./node_modules/_ieee754@1.1.13@ieee754/index.js?")},"./node_modules/_isarray@1.0.0@isarray/index.js":
/*!******************************************************!*\
!*** ./node_modules/_isarray@1.0.0@isarray/index.js ***!
\******************************************************/
/*! no static exports found */function(module,exports){eval("var toString = {}.toString;\n\nmodule.exports = Array.isArray || function (arr) {\n return toString.call(arr) == '[object Array]';\n};\n\n\n//# sourceURL=webpack://QRCode/./node_modules/_isarray@1.0.0@isarray/index.js?")},"./node_modules/_webpack@4.43.0@webpack/buildin/global.js":
/*!***********************************!*\
!*** (webpack)/buildin/global.js ***!
\***********************************/
/*! no static exports found */function(module,exports){eval('var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function("return this")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === "object") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it\'s\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n//# sourceURL=webpack://QRCode/(webpack)/buildin/global.js?')}},e={},f.m=d,f.c=e,f.d=function(e,t,n){f.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(t,e){if(1&e&&(t=f(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(f.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)f.d(n,r,function(e){return t[e]}.bind(null,r));return n},f.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(t,"a",t),t},f.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},f.p="",f(f.s="./js/qrcode.js");function f(t){if(e[t])return e[t].exports;var n=e[t]={i:t,l:!1,exports:{}};return d[t].call(n.exports,n,n.exports,f),n.l=!0,n.exports}var d,e});