text: (function(global) {
  
    var sqrt = Math.sqrt,
        atan2 = Math.atan2,
        PiBy180 = Math.PI / 180;
  
    /**
     * @namespace fabric.util
     */
    fabric.util = {
  
      /**
       * Removes value from an array.
       * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
       * @static
       * @memberOf fabric.util
       * @param {Array} array
       * @param {Any} value
       * @return {Array} original array
       */
      removeFromArray: function(array, value) {
        var idx = array.indexOf(value);
        if (idx !== -1) {
          array.splice(idx, 1);
        }
        return array;
      },
  
      /**
       * Returns random number between 2 specified ones.
       * @static
       * @memberOf fabric.util
       * @param {Number} min lower limit
       * @param {Number} max upper limit
       * @return {Number} random value (between min and max)
       */
      getRandomInt: function(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
      },
  
      /**
       * Transforms degrees to radians.
       * @static
       * @memberOf fabric.util
       * @param {Number} degrees value in degrees
       * @return {Number} value in radians
       */
      degreesToRadians: function(degrees) {
        return degrees * PiBy180;
      },
  
      /**
       * Transforms radians to degrees.
       * @static
       * @memberOf fabric.util
       * @param {Number} radians value in radians
       * @return {Number} value in degrees
       */
      radiansToDegrees: function(radians) {
        return radians / PiBy180;
      },
  
      /**
       * Rotates `point` around `origin` with `radians`
       * @static
       * @memberOf fabric.util
       * @param {fabric.Point} point The point to rotate
       * @param {fabric.Point} origin The origin of the rotation
       * @param {Number} radians The radians of the angle for the rotation
       * @return {fabric.Point} The new rotated point
       */
      rotatePoint: function(point, origin, radians) {
        var sin = Math.sin(radians),
            cos = Math.cos(radians);
  
        point.subtractEquals(origin);
  
        var rx = point.x * cos - point.y * sin,
            ry = point.x * sin + point.y * cos;
  
        return new fabric.Point(rx, ry).addEquals(origin);
      },
  
      /**
       * Apply transform t to point p
       * @static
       * @memberOf fabric.util
       * @param  {fabric.Point} p The point to transform
       * @param  {Array} t The transform
       * @param  {Boolean} [ignoreOffset] Indicates that the offset should not be applied
       * @return {fabric.Point} The transformed point
       */
      transformPoint: function(p, t, ignoreOffset) {
        if (ignoreOffset) {
          return new fabric.Point(
            t[0] * p.x + t[1] * p.y,
            t[2] * p.x + t[3] * p.y
          );
        }
        return new fabric.Point(
          t[0] * p.x + t[1] * p.y + t[4],
          t[2] * p.x + t[3] * p.y + t[5]
        );
      },
  
      /**
       * Invert transformation t
       * @static
       * @memberOf fabric.util
       * @param {Array} t The transform
       * @return {Array} The inverted transform
       */
      invertTransform: function(t) {
        var r = t.slice(),
            a = 1 / (t[0] * t[3] - t[1] * t[2]);
        r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
        var o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r);
        r[4] = -o.x;
        r[5] = -o.y;
        return r;
      },
  
      /**
       * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
       * @static
       * @memberOf fabric.util
       * @param {Number|String} number number to operate on
       * @param {Number} fractionDigits number of fraction digits to "leave"
       * @return {Number}
       */
      toFixed: function(number, fractionDigits) {
        return parseFloat(Number(number).toFixed(fractionDigits));
      },
  
      /**
       * Converts from attribute value to pixel value if applicable.
       * Returns converted pixels or original value not converted.
       * @param {Number|String} value number to operate on
       * @return {Number|String}
       */
      parseUnit: function(value) {
        var unit = /\D{0,2}$/.exec(value),
            number = parseFloat(value);
  
        switch (unit[0]) {
          case 'mm':
            return number * fabric.DPI / 25.4;
  
          case 'cm':
            return number * fabric.DPI / 2.54;
  
          case 'in':
            return number * fabric.DPI;
  
          case 'pt':
            return number * fabric.DPI / 72; // or * 4 / 3
  
          case 'pc':
            return number * fabric.DPI / 72 * 12; // or * 16
  
          default:
            return number;
        }
      },
  
      /**
       * Function which always returns `false`.
       * @static
       * @memberOf fabric.util
       * @return {Boolean}
       */
      falseFunction: function() {
        return false;
      },
  
      /**
        * Returns klass "Class" object of given namespace
        * @memberOf fabric.util
        * @param {String} type Type of object (eg. 'circle')
        * @param {String} namespace Namespace to get klass "Class" object from
        * @return {Object} klass "Class"
        */
      getKlass: function(type, namespace) {
        // capitalize first letter only
        type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
        return fabric.util.resolveNamespace(namespace)[type];
      },
  
      /**
       * Returns object of given namespace
       * @memberOf fabric.util
       * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
       * @return {Object} Object for given namespace (default fabric)
       */
      resolveNamespace: function(namespace) {
        if (!namespace) {
          return fabric;
        }
  
        var parts = namespace.split('.'),
            len = parts.length,
            obj = global || fabric.window;
  
        for (var i = 0; i < len; ++i) {
          obj = obj[parts[i]];
        }
  
        return obj;
      },
  
      /**
       * Loads image element from given url and passes it to a callback
       * @memberOf fabric.util
       * @param {String} url URL representing an image
       * @param {Function} callback Callback; invoked with loaded image
       * @param {Any} [context] Context to invoke callback in
       * @param {Object} [crossOrigin] crossOrigin value to set image element to
       */
      loadImage: function(url, callback, context, crossOrigin) {
        if (!url) {
          callback && callback.call(context, url);
          return;
        }
  
        var img = fabric.util.createImage();
  
        /** @ignore */
        img.onload = function () {
          callback && callback.call(context, img);
          img = img.onload = img.onerror = null;
        };
  
        /** @ignore */
        img.onerror = function() {
          fabric.log('Error loading ' + img.src);
          callback && callback.call(context, null, true);
          img = img.onload = img.onerror = null;
        };
  
        // data-urls appear to be buggy with crossOrigin
        // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
        // see https://code.google.com/p/chromium/issues/detail?id=315152
        //     https://bugzilla.mozilla.org/show_bug.cgi?id=935069
        if (url.indexOf('data') !== 0 && typeof crossOrigin !== 'undefined') {
          img.crossOrigin = crossOrigin;
        }
  
        img.src = url;
      },
  
      /**
       * Creates corresponding fabric instances from their object representations
       * @static
       * @memberOf fabric.util
       * @param {Array} objects Objects to enliven
       * @param {Function} callback Callback to invoke when all objects are created
       * @param {String} namespace Namespace to get klass "Class" object from
       * @param {Function} reviver Method for further parsing of object elements,
       * called after each fabric object created.
       */
      enlivenObjects: function(objects, callback, namespace, reviver) {
        objects = objects || [ ];
  
        function onLoaded() {
          if (++numLoadedObjects === numTotalObjects) {
            callback && callback(enlivenedObjects);
          }
        }
  
        var enlivenedObjects = [ ],
            numLoadedObjects = 0,
            numTotalObjects = objects.length;
  
        if (!numTotalObjects) {
          callback && callback(enlivenedObjects);
          return;
        }
  
        objects.forEach(function (o, index) {
          // if sparse array
          if (!o || !o.type) {
            onLoaded();
            return;
          }
          var klass = fabric.util.getKlass(o.type, namespace);
          if (klass.async) {
            klass.fromObject(o, function (obj, error) {
              if (!error) {
                enlivenedObjects[index] = obj;
                reviver && reviver(o, enlivenedObjects[index]);
              }
              onLoaded();
            });
          }
          else {
            enlivenedObjects[index] = klass.fromObject(o);
            reviver && reviver(o, enlivenedObjects[index]);
            onLoaded();
          }
        });
      },
  
      /**
       * Groups SVG elements (usually those retrieved from SVG document)
       * @static
       * @memberOf fabric.util
       * @param {Array} elements SVG elements to group
       * @param {Object} [options] Options object
       * @return {fabric.Object|fabric.PathGroup}
       */
      groupSVGElements: function(elements, options, path) {
        var object;
  
        object = new fabric.PathGroup(elements, options);
  
        if (typeof path !== 'undefined') {
          object.setSourcePath(path);
        }
        return object;
      },
  
      /**
       * Populates an object with properties of another object
       * @static
       * @memberOf fabric.util
       * @param {Object} source Source object
       * @param {Object} destination Destination object
       * @return {Array} properties Propertie names to include
       */
      populateWithProperties: function(source, destination, properties) {
        if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
          for (var i = 0, len = properties.length; i < len; i++) {
            if (properties[i] in source) {
              destination[properties[i]] = source[properties[i]];
            }
          }
        }
      },
  
      /**
       * Draws a dashed line between two points
       *
       * This method is used to draw dashed line around selection area.
       * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
       *
       * @param {CanvasRenderingContext2D} ctx context
       * @param {Number} x  start x coordinate
       * @param {Number} y start y coordinate
       * @param {Number} x2 end x coordinate
       * @param {Number} y2 end y coordinate
       * @param {Array} da dash array pattern
       */
      drawDashedLine: function(ctx, x, y, x2, y2, da) {
        var dx = x2 - x,
            dy = y2 - y,
            len = sqrt(dx * dx + dy * dy),
            rot = atan2(dy, dx),
            dc = da.length,
            di = 0,
            draw = true;
  
        ctx.save();
        ctx.translate(x, y);
        ctx.moveTo(0, 0);
        ctx.rotate(rot);
  
        x = 0;
        while (len > x) {
          x += da[di++ % dc];
          if (x > len) {
            x = len;
          }
          ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
          draw = !draw;
        }
  
        ctx.restore();
      },
  
      /**
       * Creates canvas element and initializes it via excanvas if necessary
       * @static
       * @memberOf fabric.util
       * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
       * when not given, element is created implicitly
       * @return {CanvasElement} initialized canvas element
       */
      createCanvasElement: function(canvasEl) {
        canvasEl || (canvasEl = fabric.document.createElement('canvas'));
        //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
          G_vmlCanvasManager.initElement(canvasEl);
        }
        //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
        return canvasEl;
      },
  
      /**
       * Creates image element (works on client and node)
       * @static
       * @memberOf fabric.util
       * @return {HTMLImageElement} HTML image element
       */
      createImage: function() {
        return fabric.isLikelyNode
          ? new (require('canvas').Image)()
          : fabric.document.createElement('img');
      },
  
      /**
       * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
       * @static
       * @memberOf fabric.util
       * @param {Object} klass "Class" to create accessors for
       */
      createAccessors: function(klass) {
        var proto = klass.prototype;
  
        for (var i = proto.stateProperties.length; i--; ) {
  
          var propName = proto.stateProperties[i],
              capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1),
              setterName = 'set' + capitalizedPropName,
              getterName = 'get' + capitalizedPropName;
  
          // using `new Function` for better introspection
          if (!proto[getterName]) {
            proto[getterName] = (function(property) {
              return new Function('return this.get("' + property + '")');
            })(propName);
          }
          if (!proto[setterName]) {
            proto[setterName] = (function(property) {
              return new Function('value', 'return this.set("' + property + '", value)');
            })(propName);
          }
        }
      },
  
      /**
       * @static
       * @memberOf fabric.util
       * @param {fabric.Object} receiver Object implementing `clipTo` method
       * @param {CanvasRenderingContext2D} ctx Context to clip
       */
      clipContext: function(receiver, ctx) {
        ctx.save();
        ctx.beginPath();
        receiver.clipTo(ctx);
        ctx.clip();
      },
  
      /**
       * Multiply matrix A by matrix B to nest transformations
       * @static
       * @memberOf fabric.util
       * @param  {Array} matrixA First transformMatrix
       * @param  {Array} matrixB Second transformMatrix
       * @return {Array} The product of the two transform matrices
       */
      multiplyTransformMatrices: function(matrixA, matrixB) {
        // Matrix multiply matrixA * matrixB
        var a = [
          [matrixA[0], matrixA[2], matrixA[4]],
          [matrixA[1], matrixA[3], matrixA[5]],
          [0,          0,          1         ]
        ],
  
        b = [
          [matrixB[0], matrixB[2], matrixB[4]],
          [matrixB[1], matrixB[3], matrixB[5]],
          [0,          0,          1         ]
        ],
  
        result = [];
  
        for (var r = 0; r < 3; r++) {
          result[r] = [];
          for (var c = 0; c < 3; c++) {
            var sum = 0;
            for (var k = 0; k < 3; k++) {
              sum += a[r][k] * b[k][c];
            }
  
            result[r][c] = sum;
          }
        }
  
        return [
          result[0][0],
          result[1][0],
          result[0][1],
          result[1][1],
          result[0][2],
          result[1][2]
        ];
      },
  
      /**
       * Returns string representation of function body
       * @param {Function} fn Function to get body of
       * @return {String} Function body
       */
      getFunctionBody: function(fn) {
        return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
      },
  
      /**
       * Returns true if context has transparent pixel
       * at specified location (taking tolerance into account)
       * @param {CanvasRenderingContext2D} ctx context
       * @param {Number} x x coordinate
       * @param {Number} y y coordinate
       * @param {Number} tolerance Tolerance
       */
      isTransparent: function(ctx, x, y, tolerance) {
  
        // If tolerance is > 0 adjust start coords to take into account.
        // If moves off Canvas fix to 0
        if (tolerance > 0) {
          if (x > tolerance) {
            x -= tolerance;
          }
          else {
            x = 0;
          }
          if (y > tolerance) {
            y -= tolerance;
          }
          else {
            y = 0;
          }
        }
  
        var _isTransparent = true,
            imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
  
        // Split image data - for tolerance > 1, pixelDataSize = 4;
        for (var i = 3, l = imageData.data.length; i < l; i += 4) {
          var temp = imageData.data[i];
          _isTransparent = temp <= 0;
          if (_isTransparent === false) {
            break; // Stop if colour found
          }
        }
  
        imageData = null;
  
        return _isTransparent;
      }
    };
  
  })(typeof exports !== 'undefined' ? exports : this);
  