Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / util / TextMetrics.js
1 /**
2  * @class Ext.util.TextMetrics
3  * <p>
4  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
5  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
6  * should not contain any HTML, otherwise it may not be measured correctly.</p> 
7  * <p>The measurement works by copying the relevant CSS styles that can affect the font related display, 
8  * then checking the size of an element that is auto-sized. Note that if the text is multi-lined, you must 
9  * provide a <b>fixed width</b> when doing the measurement.</p>
10  * 
11  * <p>
12  * If multiple measurements are being done on the same element, you create a new instance to initialize 
13  * to avoid the overhead of copying the styles to the element repeatedly.
14  * </p>
15  */
16 Ext.define('Ext.util.TextMetrics', {
17     statics: {
18         shared: null,
19         /**
20          * Measures the size of the specified text
21          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
22          * that can affect the size of the rendered text
23          * @param {String} text The text to measure
24          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
25          * in order to accurately measure the text height
26          * @return {Object} An object containing the text's size {width: (width), height: (height)}
27          */
28         measure: function(el, text, fixedWidth){
29             var me = this,
30                 shared = me.shared;
31             
32             if(!shared){
33                 shared = me.shared = new me(el, fixedWidth);
34             }
35             shared.bind(el);
36             shared.setFixedWidth(fixedWidth || 'auto');
37             return shared.getSize(text);
38         },
39         
40         /**
41           * Destroy the TextMetrics instance created by {@link #measure}.
42           */
43          destroy: function(){
44              var me = this;
45              Ext.destroy(me.shared);
46              me.shared = null;
47          }
48     },
49     
50     /**
51      * @constructor
52      * @param {Mixed} bindTo The element to bind to.
53      * @param {Number} fixedWidth A fixed width to apply to the measuring element.
54      */
55     constructor: function(bindTo, fixedWidth){
56         var measure = this.measure = Ext.getBody().createChild({
57             cls: 'x-textmetrics'
58         });
59         this.el = Ext.get(bindTo);
60         
61         measure.position('absolute');
62         measure.setLeftTop(-1000, -1000);
63         measure.hide();
64
65         if (fixedWidth) {
66            measure.setWidth(fixedWidth);
67         }
68     },
69     
70     /**
71      * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
72      * Returns the size of the specified text based on the internal element's style and width properties
73      * @param {String} text The text to measure
74      * @return {Object} An object containing the text's size {width: (width), height: (height)}
75      */
76     getSize: function(text){
77         var measure = this.measure,
78             size;
79         
80         measure.update(text);
81         size = measure.getSize();
82         measure.update('');
83         return size;
84     },
85     
86     /**
87      * Binds this TextMetrics instance to a new element
88      * @param {Mixed} el The element
89      */
90     bind: function(el){
91         var me = this;
92         
93         me.el = Ext.get(el);
94         me.measure.setStyle(
95             me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
96         );
97     },
98     
99     /**
100      * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
101      * to set a fixed width in order to accurately measure the text height.
102      * @param {Number} width The width to set on the element
103      */
104      setFixedWidth : function(width){
105          this.measure.setWidth(width);
106      },
107      
108      /**
109       * Returns the measured width of the specified text
110       * @param {String} text The text to measure
111       * @return {Number} width The width in pixels
112       */
113      getWidth : function(text){
114          this.measure.dom.style.width = 'auto';
115          return this.getSize(text).width;
116      },
117      
118      /**
119       * Returns the measured height of the specified text
120       * @param {String} text The text to measure
121       * @return {Number} height The height in pixels
122       */
123      getHeight : function(text){
124          return this.getSize(text).height;
125      },
126      
127      /**
128       * Destroy this instance
129       */
130      destroy: function(){
131          var me = this;
132          me.measure.remove();
133          delete me.el;
134          delete me.measure;
135      }
136 }, function(){
137     Ext.core.Element.addMethods({
138         /**
139          * Returns the width in pixels of the passed text, or the width of the text in this Element.
140          * @param {String} text The text to measure. Defaults to the innerHTML of the element.
141          * @param {Number} min (Optional) The minumum value to return.
142          * @param {Number} max (Optional) The maximum value to return.
143          * @return {Number} The text width in pixels.
144          * @member Ext.core.Element getTextWidth
145          */
146         getTextWidth : function(text, min, max){
147             return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
148         }
149     });
150 });