/** * Utility class to provide a way to *approximately* measure the dimension of text * without a drawing context. */Ext.define('Ext.draw.TextMeasurer', { singleton: true, requires: ['Ext.util.TextMetrics'], measureDiv: null, measureCache: {}, /** * @cfg {Boolean} [precise=false] * This singleton tries not to make use of the Ext.util.TextMetrics because it is * several times slower than TextMeasurer's own solution. TextMetrics is more precise * though, so if you have a case where the error is too big, you may want to set * this config to `true` to get perfect results at the expense of performance. * Note: defaults to `true` in IE8. */ precise: Ext.isIE8, measureDivTpl: { id: 'ext-draw-text-measurer', tag: 'div', style: { overflow: 'hidden', position: 'relative', // 'float' is a reserved word. Don't unquote, or it will break the CMD build. 'float': 'left', width: 0, height: 0 }, //<debug> // Tell the spec runner to ignore this element when checking if the dom is clean. 'data-sticky': true, //</debug> children: { tag: 'div', style: { display: 'block', position: 'absolute', x: -100000, y: -100000, padding: 0, margin: 0, 'z-index': -100000, 'white-space': 'nowrap' } } }, /** * @private * Measure the size of a text with specific font by using DOM to measure it. * Could be very expensive therefore should be used lazily. * @param {String} text * @param {String} font * @return {Object} An object with `width` and `height` properties. * @return {Number} return.width * @return {Number} return.height */ actualMeasureText: function(text, font) { var me = Ext.draw.TextMeasurer, measureDiv = me.measureDiv, FARAWAY = 100000, size, parent; if (!measureDiv) { parent = Ext.Element.create({ //<debug> // Tell the spec runner to ignore this element when checking if the dom is clean. 'data-sticky': true, //</debug> style: { "overflow": "hidden", "position": "relative", "float": "left", // DO NOT REMOVE THE QUOTE OR IT WILL BREAK COMPRESSOR "width": 0, "height": 0 } }); me.measureDiv = measureDiv = Ext.Element.create({ style: { "position": 'absolute', "x": FARAWAY, "y": FARAWAY, "z-index": -FARAWAY, "white-space": "nowrap", "display": 'block', "padding": 0, "margin": 0 } }); Ext.getBody().appendChild(parent); parent.appendChild(measureDiv); } if (font) { measureDiv.setStyle({ font: font, lineHeight: 'normal' }); } measureDiv.setText('(' + text + ')'); size = measureDiv.getSize(); measureDiv.setText('()'); size.width -= measureDiv.getSize().width; return size; }, /** * Measure a single-line text with specific font. * This will split the text into characters and add up their size. * That may *not* be the exact size of the text as it is displayed. * @param {String} text * @param {String} font * @return {Object} An object with `width` and `height` properties. * @return {Number} return.width * @return {Number} return.height */ measureTextSingleLine: function(text, font) { var width = 0, height = 0, cache, cachedItem, chars, charactor, i, ln, size; if (this.precise) { return this.preciseMeasureTextSingleLine(text, font); } text = text.toString(); cache = this.measureCache; chars = text.split(''); if (!cache[font]) { cache[font] = {}; } cache = cache[font]; if (cache[text]) { return cache[text]; } for (i = 0, ln = chars.length; i < ln; i++) { charactor = chars[i]; if (!(cachedItem = cache[charactor])) { size = this.actualMeasureText(charactor, font); cachedItem = cache[charactor] = size; } width += cachedItem.width; height = Math.max(height, cachedItem.height); } return cache[text] = { width: width, height: height }; }, // A more precise but slower version of the measureTextSingleLine method. preciseMeasureTextSingleLine: function(text, font) { var measureDiv; text = text.toString(); measureDiv = this.measureDiv || (this.measureDiv = Ext.getBody().createChild(this.measureDivTpl).down('div')); measureDiv.setStyle({ font: font || '' }); return Ext.util.TextMetrics.measure(measureDiv, text); }, /** * Measure a text with specific font. * This will split the text to lines and add up their size. * That may *not* be the exact size of the text as it is displayed. * @param {String} text * @param {String} font * @return {Object} An object with `width`, `height` and `sizes` properties. * @return {Number} return.width * @return {Number} return.height * @return {Object} return.sizes Results of individual line measurements, in case of multiline * text. */ measureText: function(text, font) { var lines = text.split('\n'), ln = lines.length, height = 0, width = 0, line, i, sizes; if (ln === 1) { return this.measureTextSingleLine(text, font); } sizes = []; for (i = 0; i < ln; i++) { line = this.measureTextSingleLine(lines[i], font); sizes.push(line); height += line.height; width = Math.max(width, line.width); } return { width: width, height: height, sizes: sizes }; }});