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