1 /**
  2  * The MIT License (MIT)
  3  *
  4  * Copyright (c) 2016 DeNA Co., Ltd.
  5  *
  6  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7  * of this software and associated documentation files (the "Software"), to deal
  8  * in the Software without restriction, including without limitation the rights
  9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  * copies of the Software, and to permit persons to whom the Software is
 11  * furnished to do so, subject to the following conditions:
 12  *
 13  * The above copyright notice and this permission notice shall be included in
 14  * all copies or substantial portions of the Software.
 15  *
 16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22  * SOFTWARE.
 23  */
 24 
 25 /// <reference path="base.js"/>
 26 /// <reference path="display_object.js"/>
 27 /// <reference path="word_breaker.js"/>
 28 /// <reference path="tween_target.js"/>
 29 /// <reference path="shadow.js"/>
 30 /// <reference path="counter.js"/>
 31 
 32 /**
 33  * A class that displays one-line text or multi-line text.
 34  * @param {string} text
 35  * @param {string} font
 36  * @param {string} color
 37  * @extends {createjs.DisplayObject}
 38  * @constructor
 39  */
 40 createjs.Text = function(text, font, color) {
 41   /// <param type="string" name="text"/>
 42   /// <param type="string" name="font"/>
 43   /// <param type="string" name="color"/>
 44   createjs.DisplayObject.call(this);
 45 
 46   /**
 47    * The text to be displayed.
 48    * @type {string}
 49    * @private
 50    */
 51   this.text_ = text;
 52 
 53   /**
 54    * The font style to use. Any valid value for the CSS font attribute is
 55    * acceptable (ex. "bold 36px Arial").
 56    * @type {string}
 57    * @private
 58    */
 59   this.font_ = font;
 60 
 61   /**
 62    * The color to draw the text in. Any valid value for the CSS color attribute
 63    * is acceptable (ex. "#F00"). Default is "#000". It will also accept valid
 64    * canvas fillStyle values.
 65    * @type {string}
 66    * @private
 67    */
 68   this.textColor_ = color;
 69 
 70   /**
 71    * The offset position of the text.
 72    * @type {createjs.Point}
 73    * @private
 74    */
 75   this.offset_ = new createjs.Point(0, 0);
 76 };
 77 createjs.inherits('Text', createjs.Text, createjs.DisplayObject);
 78 
 79 /**
 80  * An inner class that draws text onto the specified canvas with the Canvas 2D
 81  * API.
 82  * @constructor
 83  */
 84 createjs.Text.Renderer = function() {
 85   /**
 86    * The output <canvas> element.
 87    * @type {HTMLCanvasElement}
 88    * @private
 89    */
 90   this.canvas_ = createjs.createCanvas();
 91 
 92   /**
 93    * The 2D rendering context attached to the output <canvas> element.
 94    * @type {CanvasRenderingContext2D}
 95    * @private
 96    */
 97   this.context_ = createjs.getRenderingContext2D(this.canvas_);
 98 
 99   // Use a low-color texture to save memory.
100   this.canvas_.format_ = createjs.Renderer.Format.RGBA4444;
101 };
102 
103 /**
104  * The current width of the output <canvas> element.
105  * @type {number}
106  * @private
107  */
108 createjs.Text.Renderer.prototype.width_ = 0;
109 
110 /**
111  * The current height of the output <canvas> element.
112  * @type {number}
113  * @private
114  */
115 createjs.Text.Renderer.prototype.height_ = 0;
116 
117 /**
118  * The current font style of the output <canvas> element.
119  * @type {string}
120  * @private
121  */
122 createjs.Text.Renderer.prototype.font_ = '';
123 
124 /**
125  * Deletes all resources attached to this renderer.
126  * @private
127  */
128 createjs.Text.Renderer.prototype.destroy_ = function() {
129   if (this.canvas_) {
130     this.canvas_.width = 0;
131     this.context_ = null;
132     this.canvas_ = null;
133   }
134 };
135 
136 /**
137  * Returns the HTMLCanvasElement object attached to this renderer.
138  * @return {HTMLCanvasElement}
139  * @private
140  */
141 createjs.Text.Renderer.prototype.getCanvas_ = function() {
142   /// <returns type="HTMLCanvasElement"/>
143   return this.canvas_;
144 };
145 
146 /**
147  * Returns the ID assigned to this renderer.
148  * @return {string}
149  * @private
150  */
151 createjs.Text.Renderer.prototype.getId_ = function() {
152   /// <returns type="string"/>
153   return this.canvas_.id;
154 };
155 
156 /**
157  * Sets the specified ID to this renderer.
158  * @param {string} id
159  * @private
160  */
161 createjs.Text.Renderer.prototype.setId_ = function(id) {
162   /// <param type="string" name="id"/>
163   if (createjs.DEBUG) {
164     this.canvas_.id = id;
165   }
166 };
167 
168 /**
169  * Sets the output size.
170  * @param {number} width
171  * @param {number} height
172  * @private
173  */
174 createjs.Text.Renderer.prototype.setSize_ = function(width, height) {
175   /// <param type="number" name="width"/>
176   /// <param type="number" name="height"/>
177   if (this.width_ != width) {
178     this.width_ = width;
179     this.canvas_.width = width;
180     this.font_ = '';
181   }
182   if (this.height_ != height) {
183     this.height_ = height;
184     this.canvas_.height = height;
185     this.font_ = '';
186   }
187 };
188 
189 /**
190  * Sets the font.
191  * @param {string} font
192  * @private
193  */
194 createjs.Text.Renderer.prototype.setFont_ = function(font) {
195   /// <param type="string" name="font"/>
196   if (this.font_ != font) {
197     this.font_ = font;
198     this.context_.font = font;
199   }
200 };
201 
202 /**
203  * Sets the alignment of text.
204  * @param {string} textAlign
205  * @private
206  */
207 createjs.Text.Renderer.prototype.setTextAlign_ = function(textAlign) {
208   /// <param type="string" name="textAlign"/>
209   this.context_.textAlign = textAlign;
210 };
211 
212 /**
213  * Sets the baseline of text.
214  * @param {string} textBaseline
215  * @private
216  */
217 createjs.Text.Renderer.prototype.setTextBaseline_ = function(textBaseline) {
218   /// <param type="string" name="textBaseline"/>
219   this.context_.textBaseline = textBaseline;
220 };
221 
222 /**
223  * Sets the text shadow.
224  * @param {createjs.Shadow} shadow
225  * @private
226  */
227 createjs.Text.Renderer.prototype.setShadow_ = function(shadow) {
228   /// <param type="createjs.Shadow" name="shadow"/>
229   if (shadow) {
230     this.context_.shadowColor = shadow.color;
231     this.context_.shadowOffsetX = shadow.offsetX;
232     this.context_.shadowOffsetY = shadow.offsetY;
233     this.context_.shadowBlur = shadow.blur;
234   } else {
235     this.context_.shadowColor = '#000';
236     this.context_.shadowOffsetX = 0;
237     this.context_.shadowOffsetY = 0;
238     this.context_.shadowBlur = 0;
239   }
240 };
241 
242 /**
243  * Sets the line width.
244  * @param {number} lineWidth
245  * @private
246  */
247 createjs.Text.Renderer.prototype.setLineWidth_ = function(lineWidth) {
248   /// <param type="number" name="lineWidth"/>
249   this.context_.lineWidth = lineWidth;
250 };
251 
252 /**
253  * Sets the fill color.
254  * @param {string} color
255  * @private
256  */
257 createjs.Text.Renderer.prototype.setFillColor_ = function(color) {
258   /// <param type="string" name="color"/>
259   this.context_.fillStyle = color;
260 };
261 
262 /**
263  * Sets the stroke color.
264  * @param {string} color
265  * @private
266  */
267 createjs.Text.Renderer.prototype.setStrokeColor_ = function(color) {
268   /// <param type="string" name="color"/>
269   this.context_.strokeStyle = color;
270 };
271 
272 /**
273  * Clears an rectangle.
274  * @param {number} x
275  * @param {number} y
276  * @param {number} width
277  * @param {number} height
278  * @private
279  */
280 createjs.Text.Renderer.prototype.clearRect_ = function(x, y, width, height) {
281   /// <param type="number" name="x"/>
282   /// <param type="number" name="y"/>
283   /// <param type="number" name="width"/>
284   /// <param type="number" name="height"/>
285   this.context_.clearRect(x, y, width, height);
286   this.canvas_.dirty_ = true;
287 };
288 
289 /**
290  * Draws filled text.
291  * @param {string} text
292  * @param {number} x
293  * @param {number} y
294  * @param {number} maxWidth
295  * @private
296  */
297 createjs.Text.Renderer.prototype.fillText_ = function(text, x, y, maxWidth) {
298   /// <param type="string" name="text"/>
299   /// <param type="number" name="x"/>
300   /// <param type="number" name="y"/>
301   /// <param type="number" name="maxWidth"/>
302   this.context_.fillText(text, x, y, maxWidth);
303 };
304 
305 /**
306  * Draws an outline of text.
307  * @param {string} text
308  * @param {number} x
309  * @param {number} y
310  * @param {number} maxWidth
311  * @private
312  */
313 createjs.Text.Renderer.prototype.strokeText_ = function(text, x, y, maxWidth) {
314   /// <param type="string" name="text"/>
315   /// <param type="number" name="x"/>
316   /// <param type="number" name="y"/>
317   /// <param type="number" name="maxWidth"/>
318   this.context_.strokeText(text, x, y, maxWidth);
319 };
320 
321 /**
322  * Returns text width.
323  * @param {string} text
324  * @return {TextMetrics}
325  * @private
326  */
327 createjs.Text.Renderer.prototype.measureText_ = function(text) {
328   /// <param type="string" name="text"/>
329   return this.context_.measureText(text);
330 };
331 
332 /**
333  * The font heights.
334  * @type {Object.<string,number>}
335  * @private
336  */
337 createjs.Text.lineAdvances_ = {};
338 
339 /**
340  * The horizontal text alignment. This value must be one of "start", "end",
341  * "left", "right", and "center".
342  * @type {string}
343  * @private
344  */
345 createjs.Text.prototype.textAlign_ = 'left';
346 
347 /**
348  * The vertical alignment point on the font. This value must be one of "top",
349  * "hanging", "middle", "alphabetic", "ideographic", or "bottom".
350  * @type {string}
351  * @private
352  */
353 createjs.Text.prototype.textBaseline_ = 'top';
354 
355 /**
356  * The maximum width to draw the text.
357  * @type {number}
358  * @private
359  */
360 createjs.Text.prototype.maxWidth_ = 0;
361 
362 /**
363  * The line width of the text outline. This object draws only the outline of
364  * its text if this value is not 0.
365  * @type {number}
366  * @private
367  */
368 createjs.Text.prototype.outline_ = 0;
369 
370 /**
371  * The line height (vertical distance between baselines) for multi-line text.
372  * If this value is 0, the value of getMeasuredLineHeight is used.
373  * @type {number}
374  * @private
375  */
376 createjs.Text.prototype.lineHeight_ = 0;
377 
378 /**
379  * Indicates the maximum width for a line of text before it is wrapped to
380  * multiple lines. If this value is 0, the text will not be wrapped.
381  * @type {number}
382  * @private
383  */
384 createjs.Text.prototype.lineWidth_ = 0;
385 
386 /**
387  * Whether this object needs to redraw its text.
388  * @type {boolean}
389  * @private
390  */
391 createjs.Text.prototype.textDirty_ = true;
392 
393 /**
394  * A list of text lines split from the source text to fit them into the source
395  * width.
396  * @type {Array.<string>}
397  * @private
398  */
399 createjs.Text.prototype.lines_ = null;
400 
401 /**
402  * The height of a line.
403  * @type {number}
404  * @private
405  */
406 createjs.Text.prototype.lineAdvance_ = 0;
407 
408 /**
409  * The width of a cached image.
410  * @type {number}
411  * @private
412  */
413 createjs.Text.prototype.width_ = 0;
414 
415 /**
416  * The height of a cached image.
417  * @type {number}
418  * @private
419  */
420 createjs.Text.prototype.height_ = 0;
421 
422 /**
423  * The renderer that draws text.
424  * @type {createjs.Text.Renderer}
425  * @private
426  */
427 createjs.Text.prototype.renderer_ = null;
428 
429 /**
430  * The renderer that draws this object.
431  * @type {createjs.Renderer}
432  * @private
433  */
434 createjs.Text.prototype.output_ = null;
435 
436 /**
437  * Returns the cache renderer used for drawing text to a cache.
438  * @return {createjs.Text.Renderer}
439  * @private
440  */
441 createjs.Text.prototype.getRenderer_ = function() {
442   /// <returns type="createjs.Text.Renderer"/>
443   if (!this.renderer_) {
444     if (createjs.DEBUG) {
445       ++createjs.Counter.totalRenderers;
446     }
447     this.renderer_= new createjs.Text.Renderer();
448   }
449   return this.renderer_;
450 };
451 
452 /**
453  * Breaks text into lines so the specified renderer can render each of them at
454  * one fillText() or strokeText() call.
455  * @param {createjs.Text.Renderer} renderer
456  * @param {string} text
457  * @return {Array.<string>} lines;
458  * @private
459  */
460 createjs.Text.prototype.breakText_ = function(renderer, text) {
461   /// <param type="createjs.JsonText.Renderer" name="renderer"/>
462   /// <param type="string" name="text"/>
463   /// <returns type="Array" elementType="string"/>
464 
465   // Return without calculating the line width (which is used as the width of a
466   // cache <canvas> element) when the it is specified by a game.
467   var lines = text.split('\n');
468   var width = this.maxWidth_;
469   if (width > 0) {
470     this.width_ = width;
471     return lines;
472   }
473 
474   // When the line width is not specified by a game, calculate the maximum line
475   // width as use it so each line of this text can be rendered without inserting
476   // line breaks.
477   var length = lines.length;
478   if (this.lineWidth_ <= 0) {
479     width = 0;
480     for (var i = 0; i < length; ++i) {
481       var lineWidth = renderer.measureText_(lines[i]).width;
482       width = createjs.max(lineWidth, width);
483     }
484     this.width_ = width;
485     return lines;
486   }
487 
488   // Insert line breaks to fit all lines into the specified line width.
489   this.width_ = this.lineWidth_ / this.getScaleX();
490   width = this.width_;
491   var renderLines = [];
492   for (var i = 0; i < length; ++i) {
493     var line = lines[i];
494     var lineWidth = renderer.measureText_(line).width;
495     if (lineWidth <= width) {
496       renderLines.push(line);
497       continue;
498     }
499     var words = createjs.WordBreaker.breakText(line);
500     if (words.length == 1) {
501       renderLines.push(line);
502       continue;
503     }
504     lineWidth = 0;
505     var lineText = '';
506     for (var j = 0; j < words.length; ++j) {
507       var wordWidth = renderer.measureText_(words[j]).width;
508       lineWidth += wordWidth;
509       if (lineWidth > width) {
510         renderLines.push(lineText);
511         lineText = words[j];
512         lineWidth = wordWidth;
513       } else {
514         lineText += words[j];
515       }
516     }
517     if (lineText) {
518       renderLines.push(lineText);
519     }
520   }
521   return renderLines;
522 };
523 
524 /**
525  * Draws multi-line text to the cache of this object.
526  * @private
527  */
528 createjs.Text.prototype.paintCache_ = function() {
529   // Break text into lines.
530   this.textDirty_ = false;
531   var renderer = this.getRenderer_();
532   renderer.setId_(this.text_);
533   if (!this.lines_) {
534     renderer.setFont_(this.font_);
535     this.lines_ = this.breakText_(renderer, this.text_);
536   }
537 
538   // Set the size of the cache HTMLCanvasElement object.
539   var lines = this.lines_;
540   var length = lines.length;
541   this.lineAdvance_ = this.lineHeight_ || this.getMeasuredLineHeight();
542   var lineAdvance = this.lineAdvance_;
543   this.height_ = (length + 0.5) * lineAdvance;
544   var margin = 0;
545   if (this.outline_) {
546     margin = this.outline_;
547   }
548   var shadow = this.getShadow();
549   if (shadow) {
550     var offset =
551       createjs.max(createjs.abs(shadow.offsetX), createjs.abs(shadow.offsetY));
552     margin = createjs.max(margin, offset + shadow.blur);
553   }
554   var padding = margin << 1;
555   this.width_ += padding;
556   this.height_ += padding;
557   renderer.setSize_(this.width_, this.height_);
558   renderer.setFont_(this.font_);
559   renderer.setTextAlign_(this.textAlign_);
560   renderer.setTextBaseline_(this.textBaseline_);
561   renderer.setShadow_(shadow);
562   renderer.clearRect_(0, 0, this.width_, this.height_);
563 
564   // Draw text to the cache line by line.
565   var x = margin;
566   if (this.textAlign_ == 'center') {
567     x = this.width_ >> 1;
568   } else if (this.textAlign_ == 'right') {
569     x = this.width_;
570   }
571   var y = margin;
572   this.offset_.x = -x;
573   this.offset_.y = -y;
574   var maxWidth = this.maxWidth_ || 10000;
575   if (this.outline_) {
576     renderer.setStrokeColor_(this.textColor_);
577     renderer.setLineWidth_(this.outline_);
578     for (var i = 0; i < length; ++i) {
579       renderer.strokeText_(lines[i], x, y, maxWidth);
580       y += lineAdvance;
581     }
582   } else {
583     renderer.setFillColor_(this.textColor_);
584     for (var i = 0; i < length; ++i) {
585       renderer.fillText_(lines[i], x, y, maxWidth);
586       y += lineAdvance;
587     }
588   }
589   this.setBoundingBox(-x, -y, this.width_ - x, this.height_ - y);
590 };
591 
592 /**
593  * Returns the text.
594  * @return {string}
595  */
596 createjs.Text.prototype.getText = function() {
597   /// <returns type="string"/>
598   return this.text_;
599 };
600 
601 /**
602  * Sets the text.
603  * @param {string} text
604  */
605 createjs.Text.prototype.setText = function(text) {
606   /// <param type="string" name="text"/>
607   text = (text == null) ? '' : createjs.parseString(text);
608   if (this.text_ != text) {
609     this.text_ = text;
610     this.lines_ = null;
611     this.textDirty_ = true;
612   }
613 };
614 
615 /**
616  * Returns the text font.
617  * @return {string}
618  */
619 createjs.Text.prototype.getFont = function() {
620   /// <returns type="string"/>
621   return this.text_;
622 };
623 
624 /**
625  * Sets the text font.
626  * @param {string} font
627  */
628 createjs.Text.prototype.setFont = function(font) {
629   /// <param type="string" name="font"/>
630   if (this.font_ != font) {
631     this.font_ = font;
632     this.lineAdvance_ = 0;
633     this.textDirty_ = true;
634   }
635 };
636 
637 /**
638  * Returns the text color.
639  * @return {string}
640  */
641 createjs.Text.prototype.getColor = function() {
642   /// <returns type="string"/>
643   return this.textColor_;
644 };
645 
646 /**
647  * Sets the text color.
648  * @param {string} value
649  */
650 createjs.Text.prototype.setColor = function(value) {
651   /// <param type="string" name="value"/>
652   var color = value || '#000';
653   if (this.textColor_ != color) {
654     this.textColor_ = color;
655     this.textDirty_ = true;
656   }
657 };
658 
659 /**
660  * Returns the text alignment.
661  * @return {string}
662  */
663 createjs.Text.prototype.getTextAlign = function() {
664   /// <returns type="string"/>
665   return this.textAlign_;
666 };
667 
668 /**
669  * Sets the text alignment.
670  * @param {string} value
671  */
672 createjs.Text.prototype.setTextAlign = function(value) {
673   /// <param type="string" name="value"/>
674   var textAlign = value || 'left';
675   if (this.textAlign_ != textAlign) {
676     this.textAlign_ = textAlign;
677     this.textDirty_ = true;
678   }
679 };
680 
681 /**
682  * Returns the text baseline.
683  * @return {string}
684  */
685 createjs.Text.prototype.getTextBaseline = function() {
686   /// <returns type="string"/>
687   return this.textBaseline_;
688 };
689 
690 /**
691  * Sets the text baseline.
692  * @param {string} value
693  */
694 createjs.Text.prototype.setTextBaseline = function(value) {
695   /// <param type="string" name="value"/>
696   var textBaseline = value || 'top';
697   if (this.textBaseline_ != textBaseline) {
698     this.textBaseline_ = textBaseline;
699     this.textDirty_ = true;
700   }
701 };
702 
703 /**
704  * Returns the maximum width.
705  * @return {number}
706  */
707 createjs.Text.prototype.getMaxWidth = function() {
708   /// <returns type="number"/>
709   return this.maxWidth_;
710 };
711 
712 /**
713  * Sets the maximum width.
714  * @param {number} maxWidth
715  */
716 createjs.Text.prototype.setMaxWidth = function(maxWidth) {
717   /// <param type="number" name="maxWidth"/>
718   if (this.maxWidth_ != maxWidth) {
719     this.maxWidth_ = maxWidth;
720     this.lines_ = null;
721     this.textDirty_ = true;
722   }
723 };
724 
725 /**
726  * Returns the thickness of text outlines.
727  * @return {number}
728  */
729 createjs.Text.prototype.getOutline = function() {
730   /// <returns type="number"/>
731   return this.outline_;
732 };
733 
734 /**
735  * Sets the thickness of text outlines.
736  * @param {number} outline
737  */
738 createjs.Text.prototype.setOutline = function(outline) {
739   /// <param type="number" name="outline"/>
740   var thickness = createjs.parseFloat(outline);
741   if (this.outline_ != outline) {
742     this.outline_ = outline;
743     this.textDirty_ = true;
744   }
745 };
746 
747 /**
748  * Returns the line height.
749  * @return {number}
750  */
751 createjs.Text.prototype.getLineHeight = function() {
752   /// <returns type="number"/>
753   return this.lineHeight_;
754 };
755 
756 /**
757  * Sets the line height.
758  * @param {number} lineHeight
759  */
760 createjs.Text.prototype.setLineHeight = function(lineHeight) {
761   /// <param type="number" name="lineHeight"/>
762   if (this.lineHeight_ != lineHeight) {
763     this.lineHeight_ = lineHeight;
764     this.textDirty_ = true;
765   }
766 };
767 
768 /**
769  * Returns the line width.
770  * @return {number}
771  */
772 createjs.Text.prototype.getLineWidth = function() {
773   /// <returns type="number"/>
774   return this.lineWidth_;
775 };
776 
777 /**
778  * Sets the line width.
779  * @param {number} lineWidth
780  */
781 createjs.Text.prototype.setLineWidth = function(lineWidth) {
782   /// <param type="number" name="lineWidth"/>
783   if (this.lineWidth_ != lineWidth) {
784     this.lineWidth_ = lineWidth;
785     this.lines_ = null;
786     this.textDirty_ = true;
787   }
788 };
789 
790 /**
791  * Returns the measured, untransformed width of the text without wrapping. Use
792  * Text.getBounds() for a more robust value.
793  * @return {number} The measured, untransformed width of the text.
794  */
795 createjs.Text.prototype.getMeasuredWidth = function() {
796   /// <returns type="number"/>
797   if (this.textDirty_ && this.text_) {
798     this.paintCache_();
799   }
800   return this.width_;
801 };
802 
803 /**
804  * Returns an approximate line height of the text.
805  * @return {number}
806  */
807 createjs.Text.prototype.getMeasuredLineHeight = function() {
808   /// <returns type="number"/>
809   if (!this.lineAdvance_) {
810     if (!createjs.Text.lineAdvances_[this.font_]) {
811       // Multiply 1.2 with the width of 'M' to get the measured line height used
812       // by CreateJS.
813       var renderer = this.getRenderer_();
814       renderer.setFont_(this.font_);
815       createjs.Text.lineAdvances_[this.font_] =
816           renderer.measureText_('M').width * 1.2;
817     }
818     this.lineAdvance_ = createjs.Text.lineAdvances_[this.font_];
819   }
820   return this.lineAdvance_;
821 };
822 
823 /**
824  * Returns the approximate height of multi-line text.
825  * @return {number}
826  */
827 createjs.Text.prototype.getMeasuredHeight = function() {
828   /// <returns type="number"/>
829   if (this.textDirty_ && this.text_) {
830     this.paintCache_();
831   }
832   return this.lineAdvance_ * this.lines_.length;
833 };
834   
835 /** @override */
836 createjs.Text.prototype.isVisible = function() {
837   /// <returns type="boolean"/>
838   return !!this.text_ && createjs.Text.superClass_.isVisible.call(this);
839 };
840 
841 /** @override */
842 createjs.Text.prototype.handleAttach = function(flag) {
843   /// <param type="number" name="flag"/>
844   if (flag && this.textDirty_ && this.text_) {
845     this.paintCache_();
846   }
847 };
848 
849 /** @override */
850 createjs.Text.prototype.removeAllChildren = function(opt_destroy) {
851   /// <param type="boolean" optional="true" name="opt_destroy"/>
852   this.handleDetach();
853   this.text_ = '';
854 };
855 
856 /** @override */
857 createjs.Text.prototype.handleDetach = function() {
858   if (createjs.DEBUG) {
859    --createjs.Counter.totalRenderers;
860   }
861   if (this.output_ && this.renderer_) {
862     this.output_.uncache(this.renderer_.getCanvas_());
863   }
864   this.output_ = null;
865   if (this.renderer_) {
866     this.renderer_.destroy_();
867   }
868   this.renderer_ = null;
869   this.textDirty_ = true;
870 };
871 
872 /** @override */
873 createjs.Text.prototype.layout =
874     function(renderer, parent, dirty, time, draw) {
875   /// <param type="createjs.Renderer" name="renderer"/>
876   /// <param type="createjs.DisplayObject" name="parent"/>
877   /// <param type="number" name="dirty"/>
878   /// <param type="number" name="time"/>
879   /// <param type="number" name="draw"/>
880   /// <returns type="number"/>
881   if (this.textDirty_ && draw) {
882     this.paintCache_();
883     this.setDirty(createjs.DisplayObject.DIRTY_SHAPE);
884   }
885   if (!this.output_) {
886     this.output_ = renderer;
887   }
888   return createjs.Text.superClass_.layout.call(
889       this, renderer, parent, dirty, time, draw);
890 };
891 
892 /** @override */
893 createjs.Text.prototype.paintObject = function(renderer) {
894   /// <param type="createjs.Renderer" name="renderer"/>
895   renderer.drawCanvas(this.renderer_.getCanvas_(),
896       this.offset_.x, this.offset_.y, this.width_, this.height_);
897 };
898 
899 /** @override */
900 createjs.Text.prototype.set = function(properties) {
901   createjs.Text.superClass_.set.call(this, properties);
902   var KEYS = {
903     'text': createjs.Text.prototype.setText,
904     'font': createjs.Text.prototype.setFont,
905     'color': createjs.Text.prototype.setColor,
906     'textAlign': createjs.Text.prototype.setTextAlign,
907     'textBaseline': createjs.Text.prototype.setTextBaseline,
908     'maxWidth': createjs.Text.prototype.setMaxWidth,
909     'outline': createjs.Text.prototype.setOutline,
910     'lineHeight': createjs.Text.prototype.setLineHeight,
911     'lineWidth': createjs.Text.prototype.setLineWidth,
912     'shadow': createjs.Text.prototype.setShadow
913   };
914   for (var key in properties) {
915     var setter = KEYS[key];
916     if (setter) {
917       var value = properties[key];
918       setter.call(this, value);
919     }
920   }
921   return this;
922 };
923 
924 /** @override */
925 createjs.Text.prototype.getBounds = function() {
926   if (this.getBoundingBox().isEmpty()) {
927     var width = this.getMeasuredWidth();
928     this.setBoundingBox(0, 0, width, 0);
929   }
930   return createjs.Text.superClass_.getBounds.call(this);
931 };
932 
933 /** @override */
934 createjs.Text.prototype.getSetters = function() {
935   /// <return type="Object" elementType="createjs.TweenTarget.Setter"/>
936   var setters = createjs.Text.superClass_.getSetters.call(this);
937   setters['text'].setString(this.text_);
938   return setters;
939 };
940 
941 // Add a setter to allow tweens to change this object.
942 createjs.TweenTarget.Property.addSetters({
943   'text': createjs.Text.prototype.setText
944 });
945 
946 // Add getters and setters for applications to access internal variables.
947 Object.defineProperties(createjs.Text.prototype, {
948   'text': {
949     get: createjs.Text.prototype.getText,
950     set: createjs.Text.prototype.setText
951   },
952   'font': {
953     get: createjs.Text.prototype.getFont,
954     set: createjs.Text.prototype.setFont
955   },
956   'color': {
957     get: createjs.Text.prototype.getColor,
958     set: createjs.Text.prototype.setColor
959   },
960   'textAlign': {
961     get: createjs.Text.prototype.getTextAlign,
962     set: createjs.Text.prototype.setTextAlign
963   },
964   'textBaseline': {
965     get: createjs.Text.prototype.getTextBaseline,
966     set: createjs.Text.prototype.setTextBaseline
967   },
968   'maxWidth': {
969     get: createjs.Text.prototype.getMaxWidth,
970     set: createjs.Text.prototype.setMaxWidth
971   },
972   'outline': {
973     get: createjs.Text.prototype.getOutline,
974     set: createjs.Text.prototype.setOutline
975   },
976   'lineHeight': {
977     get: createjs.Text.prototype.getLineHeight,
978     set: createjs.Text.prototype.setLineHeight
979   },
980   'lineWidth': {
981     get: createjs.Text.prototype.getLineWidth,
982     set: createjs.Text.prototype.setLineWidth
983   }
984 });
985 
986 // Export the createjs.Text object to the global namespace.
987 createjs.exportObject('createjs.Text', createjs.Text, {
988   // createjs.Text methods
989   'getMeasuredWidth': createjs.Text.prototype.getMeasuredWidth,
990   'getMeasuredLineHeight': createjs.Text.prototype.getMeasuredLineHeight,
991   'getMeasuredHeight': createjs.Text.prototype.getMeasuredHeight
992   // createjs.DisplayObject methods
993   // createjs.EventDispatcher methods
994   // createjs.Object methods.
995 });
996