1 //= require "object.class"
  2 
  3 (function(global) {
  4   
  5   "use strict";
  6   
  7   var fabric = global.fabric || (global.fabric = { }),
  8       extend = fabric.util.object.extend,
  9       clone = fabric.util.object.clone;
 10 
 11   if (fabric.Text) {    
 12     fabric.warn('fabric.Text is already defined');
 13     return;
 14   }
 15   if (!fabric.Object) {
 16     fabric.warn('fabric.Text requires fabric.Object');
 17     return;
 18   }
 19   
 20   /** 
 21    * @class Text
 22    * @extends fabric.Object
 23    */
 24   fabric.Text = fabric.util.createClass(fabric.Object, /** @scope fabric.Text.prototype */ {
 25     
 26     /**
 27      * @property
 28      * @type Object
 29      */
 30     options: {
 31       top:         10,
 32       left:        10,
 33       fontsize:    20,
 34       fontweight:  100,
 35       fontfamily:  'Modernist_One_400',
 36       path:        null
 37     },
 38     
 39     /**
 40      * @property
 41      * @type String
 42      */
 43     type: 'text',
 44     
 45     /**
 46      * Constructor
 47      * @method initialize
 48      * @param {String} text
 49      * @param {Object} [options]
 50      * @return {fabric.Text} thisArg
 51      */
 52     initialize: function(text, options) {
 53       this.originalState = { };
 54       this._initStateProperties();
 55       this.text = text;
 56       this.setOptions(options);
 57       extend(this, this.options);
 58       this.theta = this.angle * (Math.PI/180);
 59       this.width = this.getWidth();
 60       this.setCoords();
 61     },
 62     
 63     /**
 64      * Creates `stateProperties` list on an instance, and adds `fabric.Text` -specific ones to it (such as "fontfamily", "fontweight", etc.)
 65      * @private
 66      * @method initStateProperties
 67      */
 68     _initStateProperties: function() {
 69       var o;
 70       if ((o = this.constructor) && 
 71           (o = o.superclass) &&
 72           (o = o.prototype) &&
 73           (o = o.stateProperties) &&
 74           o.clone) {
 75         this.stateProperties = o.clone();
 76         this.stateProperties.push('fontfamily', 'fontweight', 'path');
 77       }
 78     },
 79     
 80     /**
 81      * Returns string representation of an instance
 82      * @method toString
 83      * @return {String} String representation of text object
 84      */
 85     toString: function() {
 86       return '#<fabric.Text ('+ this.complexity() +'): ' + 
 87         JSON.stringify({ text: this.text, fontfamily: this.fontfamily }) + '>';
 88     },
 89     
 90     /**
 91      * @private
 92      * @method _render
 93      * @param {CanvasRenderingContext2D} ctx Context to render on
 94      */
 95     _render: function(context) {
 96       var o = Cufon.textOptions || (Cufon.textOptions = { });
 97       
 98       // export options to be used by cufon.js
 99       o.left = this.left;
100       o.top = this.top;
101       o.context = context;
102       o.color = this.fill;
103       
104       var el = this._initDummyElement();
105       
106       // set "cursor" to top/left corner
107       this.transform(context);
108       
109       // draw text
110       Cufon.replaceElement(el, {
111         separate: 'none', 
112         fontFamily: this.fontfamily
113       });
114       
115       // update width, height
116       this.width = o.width;
117       this.height = o.height;
118     },
119     
120     /**
121      * @private
122      * @method _initDummyElement
123      */
124     _initDummyElement: function() {
125       var el = document.createElement('div');
126       el.innerHTML = this.text;
127       
128       // need to specify these manually, since Jaxer doesn't support retrieving computed style
129       el.style.fontSize = '40px';
130       el.style.fontWeight = '400';
131       el.style.fontStyle = 'normal';
132       el.style.letterSpacing = 'normal';
133       el.style.color = '#000000';
134       el.style.fontWeight = '600';
135       el.style.fontFamily = 'Verdana';
136       
137       return el;
138     },
139     
140     /**
141      * Renders text instance on a specified context
142      * @method render
143      * @param ctx {CanvasRenderingContext2D} context to render on
144      */
145     render: function(context) {
146       context.save();
147       this._render(context);
148       if (this.active) {
149         this.drawBorders(context);
150         this.drawCorners(context);
151       }
152       context.restore();
153     },
154   	
155   	/**
156   	 * Returns object representation of an instance
157   	 * @method toObject
158   	 * @return {Object} Object representation of text object
159   	 */
160   	toObject: function() {
161   	  return extend(this.callSuper('toObject'), {
162   	    text:         this.text,
163   	    fontsize:     this.fontsize,
164   	    fontweight:   this.fontweight,
165   	    fontfamily:   this.fontfamily,
166   	    path:         this.path
167   	  });
168   	},
169   	
170   	/**
171   	 * Sets "color" of an instance (alias of `set('fill', …)`)
172   	 * @method setColor
173   	 * @param {String} value
174   	 * @return {fabric.Text} thisArg
175   	 * @chainable
176   	 */
177   	setColor: function(value) {
178   	  this.set('fill', value);
179   	  return this;
180   	},
181   	
182   	/**
183   	 * Sets fontsize of an instance and updates its coordinates
184   	 * @method setFontsize
185   	 * @param {Number} value
186   	 * @return {fabric.Text} thisArg
187   	 * @chainable
188   	 */
189   	setFontsize: function(value) {
190   	  this.set('fontsize', value);
191   	  this.setCoords();
192   	  return this;
193   	},
194   	
195   	/**
196   	 * Returns actual text value of an instance
197   	 * @method getText
198   	 * @return {String}
199   	 */
200   	getText: function() {
201   	  return this.text;
202   	},
203   	
204   	/**
205   	 * Sets text of an instance, and updates its coordinates
206   	 * @method setText
207   	 * @param {String} value
208   	 * @return {fabric.Text} thisArg
209   	 * @chainable
210   	 */
211   	setText: function(value) {
212   	  this.set('text', value);
213   	  this.setCoords();
214   	  return this;
215   	},
216   	
217   	/**
218   	 * Sets specified property to a specified value
219   	 * @method set
220   	 * @param {String} name
221   	 * @param {Any} value
222   	 * @return {fabric.Text} thisArg
223   	 * @chainable
224   	 */
225   	set: function(name, value) {
226   	  this[name] = value;
227   	  if (name === 'fontfamily') {
228   	    this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3');
229   	  }
230   	  return this;
231   	}
232   });
233 	
234 	/**
235 	 * Returns fabric.Text instance from an object representation
236    * @static
237    * @method fromObject
238    * @param {Object} object to create an instance from
239    * @return {fabric.Text} an instance
240    */
241 	fabric.Text.fromObject = function(object) {
242 	  return new fabric.Text(object.text, clone(object));
243 	};
244 	
245 	/**
246 	 * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
247    * @static
248    * @method fabric.Text.fromElement
249    * @return {fabric.Text} an instance
250    */
251 	fabric.Text.fromElement = function(element) {
252 	  // TODO (kangax): implement this
253 	};
254 })(this);