text: (function() {
  
    var degreesToRadians = fabric.util.degreesToRadians;
  
    fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  
      /**
       * Object containing coordinates of object's controls
       * @type Object
       * @default
       */
      oCoords: null,
  
      /**
       * Checks if object intersects with an area formed by 2 points
       * @param {Object} pointTL top-left point of area
       * @param {Object} pointBR bottom-right point of area
       * @return {Boolean} true if object intersects with an area formed by 2 points
       */
      intersectsWithRect: function(pointTL, pointBR) {
        var oCoords = this.oCoords,
            tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
            tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
            bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
            br = new fabric.Point(oCoords.br.x, oCoords.br.y),
            intersection = fabric.Intersection.intersectPolygonRectangle(
              [tl, tr, br, bl],
              pointTL,
              pointBR
            );
        return intersection.status === 'Intersection';
      },
  
      /**
       * Checks if object intersects with another object
       * @param {Object} other Object to test
       * @return {Boolean} true if object intersects with another object
       */
      intersectsWithObject: function(other) {
        // extracts coords
        function getCoords(oCoords) {
          return {
            tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y),
            tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y),
            bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y),
            br: new fabric.Point(oCoords.br.x, oCoords.br.y)
          };
        }
        var thisCoords = getCoords(this.oCoords),
            otherCoords = getCoords(other.oCoords),
            intersection = fabric.Intersection.intersectPolygonPolygon(
              [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
              [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
            );
  
        return intersection.status === 'Intersection';
      },
  
      /**
       * Checks if object is fully contained within area of another object
       * @param {Object} other Object to test
       * @return {Boolean} true if object is fully contained within area of another object
       */
      isContainedWithinObject: function(other) {
        var boundingRect = other.getBoundingRect(),
            point1 = new fabric.Point(boundingRect.left, boundingRect.top),
            point2 = new fabric.Point(boundingRect.left + boundingRect.width, boundingRect.top + boundingRect.height);
  
        return this.isContainedWithinRect(point1, point2);
      },
  
      /**
       * Checks if object is fully contained within area formed by 2 points
       * @param {Object} pointTL top-left point of area
       * @param {Object} pointBR bottom-right point of area
       * @return {Boolean} true if object is fully contained within area formed by 2 points
       */
      isContainedWithinRect: function(pointTL, pointBR) {
        var boundingRect = this.getBoundingRect();
  
        return (
          boundingRect.left >= pointTL.x &&
          boundingRect.left + boundingRect.width <= pointBR.x &&
          boundingRect.top >= pointTL.y &&
          boundingRect.top + boundingRect.height <= pointBR.y
        );
      },
  
      /**
       * Checks if point is inside the object
       * @param {fabric.Point} point Point to check against
       * @return {Boolean} true if point is inside the object
       */
      containsPoint: function(point) {
        var lines = this._getImageLines(this.oCoords),
            xPoints = this._findCrossPoints(point, lines);
  
        // if xPoints is odd then point is inside the object
        return (xPoints !== 0 && xPoints % 2 === 1);
      },
  
      /**
       * Method that returns an object with the object edges in it, given the coordinates of the corners
       * @private
       * @param {Object} oCoords Coordinates of the object corners
       */
      _getImageLines: function(oCoords) {
        return {
          topline: {
            o: oCoords.tl,
            d: oCoords.tr
          },
          rightline: {
            o: oCoords.tr,
            d: oCoords.br
          },
          bottomline: {
            o: oCoords.br,
            d: oCoords.bl
          },
          leftline: {
            o: oCoords.bl,
            d: oCoords.tl
          }
        };
      },
  
      /**
       * Helper method to determine how many cross points are between the 4 object edges
       * and the horizontal line determined by a point on canvas
       * @private
       * @param {fabric.Point} point Point to check
       * @param {Object} oCoords Coordinates of the object being evaluated
       */
      _findCrossPoints: function(point, oCoords) {
        var b1, b2, a1, a2, xi, yi,
            xcount = 0,
            iLine;
  
        for (var lineKey in oCoords) {
          iLine = oCoords[lineKey];
          // optimisation 1: line below point. no cross
          if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
            continue;
          }
          // optimisation 2: line above point. no cross
          if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
            continue;
          }
          // optimisation 3: vertical line case
          if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
            xi = iLine.o.x;
            yi = point.y;
          }
          // calculate the intersection point
          else {
            b1 = 0;
            b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
            a1 = point.y - b1 * point.x;
            a2 = iLine.o.y - b2 * iLine.o.x;
  
            xi = - (a1 - a2) / (b1 - b2);
            yi = a1 + b1 * xi;
          }
          // dont count xi < point.x cases
          if (xi >= point.x) {
            xcount += 1;
          }
          // optimisation 4: specific for square images
          if (xcount === 2) {
            break;
          }
        }
        return xcount;
      },
  
      /**
       * Returns width of an object's bounding rectangle
       * @deprecated since 1.0.4
       * @return {Number} width value
       */
      getBoundingRectWidth: function() {
        return this.getBoundingRect().width;
      },
  
      /**
       * Returns height of an object's bounding rectangle
       * @deprecated since 1.0.4
       * @return {Number} height value
       */
      getBoundingRectHeight: function() {
        return this.getBoundingRect().height;
      },
  
      /**
       * Returns coordinates of object's bounding rectangle (left, top, width, height)
       * @return {Object} Object with left, top, width, height properties
       */
      getBoundingRect: function() {
        this.oCoords || this.setCoords();
  
        var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
            minX = fabric.util.array.min(xCoords),
            maxX = fabric.util.array.max(xCoords),
            width = Math.abs(minX - maxX),
  
            yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
            minY = fabric.util.array.min(yCoords),
            maxY = fabric.util.array.max(yCoords),
            height = Math.abs(minY - maxY);
  
        return {
          left: minX,
          top: minY,
          width: width,
          height: height
        };
      },
  
      /**
       * Returns width of an object
       * @return {Number} width value
       */
      getWidth: function() {
        return this.width * this.scaleX;
      },
  
      /**
       * Returns height of an object
       * @return {Number} height value
       */
      getHeight: function() {
        return this.height * this.scaleY;
      },
  
      /**
       * Makes sure the scale is valid and modifies it if necessary
       * @private
       * @param {Number} value
       * @return {Number}
       */
      _constrainScale: function(value) {
        if (Math.abs(value) < this.minScaleLimit) {
          if (value < 0) {
            return -this.minScaleLimit;
          }
          else {
            return this.minScaleLimit;
          }
        }
        return value;
      },
  
      /**
       * Scales an object (equally by x and y)
       * @param {Number} value Scale factor
       * @return {fabric.Object} thisArg
       * @chainable
       */
      scale: function(value) {
        value = this._constrainScale(value);
  
        if (value < 0) {
          this.flipX = !this.flipX;
          this.flipY = !this.flipY;
          value *= -1;
        }
  
        this.scaleX = value;
        this.scaleY = value;
        this.setCoords();
        return this;
      },
  
      /**
       * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
       * @param {Number} value New width value
       * @return {fabric.Object} thisArg
       * @chainable
       */
      scaleToWidth: function(value) {
        // adjust to bounding rect factor so that rotated shapes would fit as well
        var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth();
        return this.scale(value / this.width / boundingRectFactor);
      },
  
      /**
       * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
       * @param {Number} value New height value
       * @return {fabric.Object} thisArg
       * @chainable
       */
      scaleToHeight: function(value) {
        // adjust to bounding rect factor so that rotated shapes would fit as well
        var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight();
        return this.scale(value / this.height / boundingRectFactor);
      },
  
      /**
       * Sets corner position coordinates based on current angle, width and height
       * See https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords
       * @return {fabric.Object} thisArg
       * @chainable
       */
      setCoords: function() {
        var strokeWidth = this.strokeWidth,
            theta = degreesToRadians(this.angle),
            vpt = this.getViewportTransform(),
            f = function (p) {
              return fabric.util.transformPoint(p, vpt);
            },
            w = this.width, currentWidth,
            h = this.height, currentHeight,
            capped = this.strokeLineCap === 'round' || this.strokeLineCap === 'square',
            vLine = this.type === 'line' && this.width === 0,
            hLine = this.type === 'line' && this.height === 0,
            sLine = vLine || hLine,
            strokeW = (capped && hLine) || !sLine,
            strokeH = (capped && vLine) || !sLine;
  
        if (vLine) {
          w = strokeWidth;
        }
        else if (hLine) {
          h = strokeWidth;
        }
        if (strokeW) {
          w += w > 0 ? strokeWidth : -strokeWidth;
        }
        if (strokeH) {
          h += h > 0 ? strokeWidth : -strokeWidth;
        }
        currentWidth = w * this.scaleX + 2 * this.padding;
        currentHeight = h * this.scaleY + 2 * this.padding;
  
        // If width is negative, make postive. Fixes path selection issue
        if (currentWidth < 0) {
          currentWidth = Math.abs(currentWidth);
        }
  
        var _hypotenuse = Math.sqrt(
              Math.pow(currentWidth / 2, 2) +
              Math.pow(currentHeight / 2, 2)),
  
            _angle = Math.atan(
              isFinite(currentHeight / currentWidth)
                ? currentHeight / currentWidth
                : 0),
  
            // offset added for rotate and scale actions
            offsetX = Math.cos(_angle + theta) * _hypotenuse,
            offsetY = Math.sin(_angle + theta) * _hypotenuse,
            sinTh = Math.sin(theta),
            cosTh = Math.cos(theta),
            coords = this.getCenterPoint(),
            wh = new fabric.Point(currentWidth, currentHeight),
            _tl =   new fabric.Point(coords.x - offsetX, coords.y - offsetY),
            _tr =   new fabric.Point(_tl.x + (wh.x * cosTh),   _tl.y + (wh.x * sinTh)),
            _bl =   new fabric.Point(_tl.x - (wh.y * sinTh),   _tl.y + (wh.y * cosTh)),
            _mt =   new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh)),
            tl  = f(_tl),
            tr  = f(_tr),
            br  = f(new fabric.Point(_tr.x - (wh.y * sinTh),   _tr.y + (wh.y * cosTh))),
            bl  = f(_bl),
            ml  = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh))),
            mt  = f(_mt),
            mr  = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh))),
            mb  = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh))),
            mtr = f(new fabric.Point(_mt.x, _mt.y));
  
        // debugging
  
        // setTimeout(function() {
        //   canvas.contextTop.fillStyle = 'green';
        //   canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
        //   canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
        //   canvas.contextTop.fillRect(br.x, br.y, 3, 3);
        //   canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
        //   canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
        //   canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
        //   canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
        //   canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
        // }, 50);
  
        this.oCoords = {
          // corners
          tl: tl, tr: tr, br: br, bl: bl,
          // middle
          ml: ml, mt: mt, mr: mr, mb: mb,
          // rotating point
          mtr: mtr
        };
  
        // set coordinates of the draggable boxes in the corners used to scale/rotate the image
        this._setCornerCoords && this._setCornerCoords();
  
        return this;
      }
    });
  })();
  