/**
 * @class Ext.util.TextMetrics
 * <p>
 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
 * should not contain any HTML, otherwise it may not be measured correctly.</p>
 * <p>The measurement works by copying the relevant CSS styles that can affect the font related display,
 * then checking the size of an element that is auto-sized. Note that if the text is multi-lined, you must
 * provide a <b>fixed width</b> when doing the measurement.</p>
 *
 * <p>
 * If multiple measurements are being done on the same element, you create a new instance to initialize
 * to avoid the overhead of copying the styles to the element repeatedly.
 * </p>
 */

Ext.define('Ext.util.TextMetrics', {
    statics
: {
        shared
: null,
        /**
         * Measures the size of the specified text
         * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
         * that can affect the size of the rendered text
         * @param {String} text The text to measure
         * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
         * in order to accurately measure the text height
         * @return {Object} An object containing the text's size {width: (width), height: (height)}
         */

        measure
: function(el, text, fixedWidth){
           
var me = this,
                shared
= me.shared;
           
           
if(!shared){
                shared
= me.shared = new me(el, fixedWidth);
           
}
            shared
.bind(el);
            shared
.setFixedWidth(fixedWidth || 'auto');
           
return shared.getSize(text);
       
},
       
        /**
          * Destroy the TextMetrics instance created by {@link #measure}.
          */

         destroy
: function(){
             
var me = this;
             
Ext.destroy(me.shared);
             me
.shared = null;
         
}
   
},
   
    /**
     * @constructor
     * @param {Mixed} bindTo The element to bind to.
     * @param {Number} fixedWidth A fixed width to apply to the measuring element.
     */

    constructor
: function(bindTo, fixedWidth){
       
var measure = this.measure = Ext.getBody().createChild({
            cls
: 'x-textmetrics'
       
});
       
this.el = Ext.get(bindTo);
       
        measure
.position('absolute');
        measure
.setLeftTop(-1000, -1000);
        measure
.hide();

       
if (fixedWidth) {
           measure
.setWidth(fixedWidth);
       
}
   
},
   
    /**
     * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
     * Returns the size of the specified text based on the internal element's style and width properties
     * @param {String} text The text to measure
     * @return {Object} An object containing the text's size {width: (width), height: (height)}
     */

    getSize
: function(text){
       
var measure = this.measure,
            size
;
       
        measure
.update(text);
        size
= measure.getSize();
        measure
.update('');
       
return size;
   
},
   
    /**
     * Binds this TextMetrics instance to a new element
     * @param {Mixed} el The element
     */

    bind
: function(el){
       
var me = this;
       
        me
.el = Ext.get(el);
        me
.measure.setStyle(
            me
.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
       
);
   
},
   
    /**
     * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
     * to set a fixed width in order to accurately measure the text height.
     * @param {Number} width The width to set on the element
     */

     setFixedWidth
: function(width){
         
this.measure.setWidth(width);
     
},
     
     /**
      * Returns the measured width of the specified text
      * @param {String} text The text to measure
      * @return {Number} width The width in pixels
      */

     getWidth
: function(text){
         
this.measure.dom.style.width = 'auto';
         
return this.getSize(text).width;
     
},
     
     /**
      * Returns the measured height of the specified text
      * @param {String} text The text to measure
      * @return {Number} height The height in pixels
      */

     getHeight
: function(text){
         
return this.getSize(text).height;
     
},
     
     /**
      * Destroy this instance
      */

     destroy
: function(){
         
var me = this;
         me
.measure.remove();
         
delete me.el;
         
delete me.measure;
     
}
}, function(){
   
Ext.core.Element.addMethods({
        /**
         * Returns the width in pixels of the passed text, or the width of the text in this Element.
         * @param {String} text The text to measure. Defaults to the innerHTML of the element.
         * @param {Number} min (Optional) The minumum value to return.
         * @param {Number} max (Optional) The maximum value to return.
         * @return {Number} The text width in pixels.
         * @member Ext.core.Element getTextWidth
         */

        getTextWidth
: function(text, min, max){
           
return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
       
}
   
});
});