1 //= require "object.class"
  2 
  3 (function(global) {
  4   
  5   "use strict";
  6   
  7   var fabric = global.fabric || (global.fabric = { });
  8   
  9   if (fabric.Rect) {
 10     console.warn('fabric.Rect is already defined');
 11     return;
 12   }
 13   
 14   /** 
 15    * @class Rect
 16    * @extends fabric.Object
 17    */
 18   fabric.Rect = fabric.util.createClass(fabric.Object, /** @scope fabric.Rect.prototype */ {
 19     
 20     /**
 21      * @property
 22      * @type String
 23      */
 24     type: 'rect',
 25     
 26     /**
 27      * @property
 28      * @type Object
 29      */
 30     options: {
 31       rx: 0,
 32       ry: 0
 33     },
 34     
 35     /**
 36      * Constructor
 37      * @method initialize
 38      * @param options {Object} options object
 39      * @return {Object} thisArg
 40      */
 41     initialize: function(options) {
 42       this._initStateProperties();
 43       this.callSuper('initialize', options);
 44       this._initRxRy();
 45     },
 46     
 47     /**
 48      * Creates `stateProperties` list on an instance, and adds `fabric.Rect` -specific ones to it 
 49      * (such as "rx", "ry", etc.)
 50      * @private
 51      * @method _initStateProperties
 52      */
 53     _initStateProperties: function() {
 54       this.stateProperties = this.stateProperties.concat(['rx', 'ry']);
 55     },
 56     
 57     /**
 58      * @private
 59      * @method _initRxRy
 60      */
 61     _initRxRy: function() {
 62       if (this.rx && !this.ry) {
 63         this.ry = this.rx;
 64       }
 65       else if (this.ry && !this.rx) {
 66         this.rx = this.ry;
 67       }
 68     },
 69     
 70     /**
 71      * @private
 72      * @method _render
 73      * @param ctx {CanvasRenderingContext2D} context to render on
 74      */
 75     _render: function(ctx) {   
 76       var rx = this.rx || 0,
 77           ry = this.ry || 0,
 78           x = -this.width / 2,
 79           y = -this.height / 2,
 80           w = this.width,
 81           h = this.height;
 82       
 83       ctx.beginPath();
 84       ctx.globalAlpha *= this.opacity;
 85       
 86       ctx.moveTo(x+rx, y);
 87       ctx.lineTo(x+w-rx, y);
 88       ctx.bezierCurveTo(x+w, y, x+w, y+ry, x+w, y+ry);
 89       ctx.lineTo(x+w, y+h-ry);
 90       ctx.bezierCurveTo(x+w,y+h,x+w-rx,y+h,x+w-rx,y+h);
 91       ctx.lineTo(x+rx,y+h);
 92       ctx.bezierCurveTo(x,y+h,x,y+h-ry,x,y+h-ry);
 93       ctx.lineTo(x,y+ry);
 94       ctx.bezierCurveTo(x,y,x+rx,y,x+rx,y);
 95       ctx.closePath();
 96       
 97       if (this.fill) {
 98         ctx.fill();
 99       }
100       if (this.stroke) {
101         ctx.stroke();
102       }
103     },
104     
105     // since our coordinate system differs from that of SVG
106     _normalizeLeftTopProperties: function(parsedAttributes) {
107       if (parsedAttributes.left) {
108         this.set('left', parsedAttributes.left + this.getWidth() / 2);
109       }
110       if (parsedAttributes.top) {
111         this.set('top', parsedAttributes.top + this.getHeight() / 2);
112       }
113       return this;
114     },
115     
116     /**
117      * @method complexity
118      * @return {Number} complexity
119      */
120     complexity: function() {
121       return 1;
122     }
123   });
124   
125   // TODO (kangax): implement rounded rectangles (both parsing and rendering)
126   
127   /**
128    * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)
129    * @static
130    */
131   fabric.Rect.ATTRIBUTE_NAMES = 'x y width height rx ry fill fill-opacity opacity stroke stroke-width transform'.split(' ');
132   
133   /**
134    * @private
135    */
136   function _setDefaultLeftTopValues(attributes) {
137     attributes.left = attributes.left || 0;
138     attributes.top  = attributes.top  || 0;
139     return attributes;
140   }
141   
142   /**
143    * Returns fabric.Rect instance from an SVG element
144    * @static
145    * @method fabric.Rect.fromElement
146    * @param element {SVGElement} element to parse
147    * @param options {Object} options object
148    * @return {fabric.Rect} instance of fabric.Rect
149    */
150   fabric.Rect.fromElement = function(element, options) {
151     if (!element) {
152       return null;
153     }
154     
155     var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
156     parsedAttributes = _setDefaultLeftTopValues(parsedAttributes);
157     
158     var rect = new fabric.Rect(fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
159     rect._normalizeLeftTopProperties(parsedAttributes);
160     
161     return rect;
162   };
163   
164   /**
165    * Returns fabric.Rect instance from an object representation
166    * @static
167    * @method fabric.Rect.fromObject
168    * @param object {Object} object to create an instance from
169    * @return {Object} instance of fabric.Rect
170    */
171   fabric.Rect.fromObject = function(object) {
172     return new fabric.Rect(object);
173   };
174 })(this);