/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2006 by 1&1 Internet AG, Germany, http://www.1and1.org License: LGPL 2.1: http://www.gnu.org/licenses/lgpl.html Authors: * Sebastian Werner (wpbasti) * Andreas Ecker (ecker) ************************************************************************ */ /* ************************************************************************ #module(ui_basic) #require(qx.renderer.font.FontCache) #after(qx.renderer.font.FontObject) ************************************************************************ */ qx.OO.defineClass("qx.ui.basic.Label", qx.ui.basic.Terminator, function(vHtml, vMnemonic) { qx.ui.basic.Terminator.call(this); // Apply constructor arguments if (qx.util.Validation.isValidString(vHtml)) { this.setHtml(vHtml); } if (qx.util.Validation.isValidString(vMnemonic)) { this.setMnemonic(vMnemonic); } // Prohibit stretching through layout handler this.setAllowStretchX(false); this.setAllowStretchY(false); // Auto Sized this.auto(); }); qx.Class._measureNodes = {}; /* --------------------------------------------------------------------------- PROPERTIES --------------------------------------------------------------------------- */ qx.OO.changeProperty({ name : "appearance", type : "string", defaultValue : "label" }); /*! Any text string which can contain HTML, too */ qx.OO.addProperty({ name : "html", type : "string" }); /*! The alignment of the text. */ qx.OO.addProperty({ name : "textAlign", type : "string", defaultValue : "left", possibleValues : [ "left", "center", "right", "justify" ] }); /*! The styles which should be copied */ qx.OO.addProperty({ name : "fontPropertiesProfile", type : "string", defaultValue : "default", possibleValues : [ "none", "default", "extended", "multiline", "extendedmultiline", "all" ] }); /*! A single character which will be underlined inside the text. */ qx.OO.addProperty({ name : "mnemonic", type : "string" }); /*! The font property describes how to paint the font on the widget. */ qx.OO.addProperty({ name : "font", type : "object", instance : "qx.renderer.font.Font", convert : qx.renderer.font.FontCache, allowMultipleArguments : true }); /*! Wrap the text? */ qx.OO.addProperty({ name : "wrap", type : "boolean", defaultValue : true }); /* ************************************************************************ Class data, properties and methods ************************************************************************ */ /* --------------------------------------------------------------------------- DATA --------------------------------------------------------------------------- */ qx.ui.basic.Label.SYMBOL_ELLIPSIS = String.fromCharCode(8230); qx.ui.basic.Label.SUPPORT_NATIVE_ELLIPSIS = qx.sys.Client.getInstance().isMshtml(); // these are the properties what will be copied to the measuring frame. qx.ui.basic.Label._fontProperties = { "none" : [], "default" : ["fontFamily", "fontSize", "fontStyle", "fontWeight", "textDecoration"], "extended" : ["fontFamily", "fontSize", "fontStyle", "fontWeight", "letterSpacing", "textDecoration", "textTransform", "whiteSpace", "wordSpacing"], "multiline" : ["fontFamily", "fontSize", "fontStyle", "fontWeight", "textDecoration", "lineHeight", "wordWrap"], "extendedmultiline" : ["fontFamily", "fontSize", "fontStyle", "fontWeight", "letterSpacing", "textDecoration", "textTransform", "whiteSpace", "wordSpacing", "lineHeight", "wordBreak", "wordWrap", "quotes"], "all" : ["fontFamily", "fontSize", "fontStyle", "fontVariant", "fontWeight", "letterSpacing", "lineBreak", "lineHeight", "quotes", "textDecoration", "textIndent", "textShadow", "textTransform", "textUnderlinePosition", "whiteSpace", "wordBreak", "wordSpacing", "wordWrap"] } qx.ui.basic.Label.htmlToText = function(s) { return String(s).replace(/\s+|<([^>])+>|&|<|>|"| |&#[0-9]+;|&#x[0-9a-fA-F];]/gi, qx.ui.basic.Label._htmlToText); } qx.ui.basic.Label._htmlToText = function(s) { switch(s) { case "&": return "&"; case "<": return "<"; case ">": return ">"; case """: return '"'; case " ": return String.fromCharCode(160); default: if (s.substring(0, 3) == "&#x") { return String.fromCharCode(parseInt("0x" + s.substring(3, s.length - 1))); } else if (s.substring(0, 2) == "&#") { return String.fromCharCode(s.substring(2, s.length - 1)); } else if (/\s+/.test(s)) { return " "; } else if (/^
|\n|\u00A0/g, qx.ui.basic.Label._textToHtml); } qx.ui.basic.Label._textToHtml = function(s) { switch(s) { case "&": return "&"; case "<": return "<"; case ">": return ">"; case "\n": return "
"; default: return " "; } } qx.ui.basic.Label.createMeasureNode = function(vId) { var vNode = qx.ui.basic.Label._measureNodes[vId]; if (!vNode) { vNode = document.createElement("div"); var vStyle = vNode.style; vStyle.width = vStyle.height = "auto"; vStyle.visibility = "hidden"; vStyle.position = "absolute"; vStyle.zIndex = "-1"; document.body.appendChild(vNode); qx.ui.basic.Label._measureNodes[vId] = vNode; } return vNode; } /* ************************************************************************ Instance data, properties and methods ************************************************************************ */ /* --------------------------------------------------------------------------- MODIFIER --------------------------------------------------------------------------- */ qx.Proto._htmlMode = false; qx.Proto._hasMnemonic = false; qx.Proto._mnemonicHtml = ""; qx.Proto._mnemonicTest = null; qx.Proto._modifyHtml = function(propValue, propOldValue, propData) { this._htmlMode = qx.util.Validation.isValidString(propValue) && propValue.match(/<.*>/) ? true : false; if (this._isCreated) { this._applyContent(); } return true; } qx.Proto._modifyTextAlign = function(propValue, propOldValue, propData) { this.setStyleProperty("textAlign", propValue); return true; } qx.Proto._modifyMnemonic = function(propValue, propOldValue, propData) { this._hasMnemonic = qx.util.Validation.isValidString(propValue) && propValue.length == 1; this._mnemonicHtml = this._hasMnemonic ? "(" + propValue + ")" : ""; this._mnemonicTest = this._hasMnemonic ? new RegExp("^(((<([^>]|" + propValue + ")+>)|(&([^;]|" + propValue + ")+;)|[^&" + propValue + "])*)(" + propValue + ")", "i") : null; return true; } qx.Proto._modifyFont = function(propValue, propOldValue, propData) { this._invalidatePreferredInnerDimensions(); if (propValue) { propValue._applyWidget(this); } else if (propOldValue) { propOldValue._resetWidget(this); } return true; } qx.Proto._modifyWrap = function(propValue, propOldValue, propData) { this.setStyleProperty("whiteSpace", propValue ? "normal" : "nowrap"); return true; } /* --------------------------------------------------------------------------- HELPER FOR PREFERRED DIMENSION --------------------------------------------------------------------------- */ qx.Proto._computeObjectNeededDimensions = function() { // copy styles var vNode = this._copyStyles(); // prepare html var vHtml = this.getHtml(); // test for mnemonic and fix content if (this._hasMnemonic && !this._mnemonicTest.test(vHtml)) { vHtml += this._mnemonicHtml; } // apply html vNode.innerHTML = vHtml; // store values this._cachedPreferredInnerWidth = vNode.scrollWidth; this._cachedPreferredInnerHeight = vNode.scrollHeight; } qx.Proto._copyStyles = function() { var vProps = this.getFontPropertiesProfile(); var vNode = qx.ui.basic.Label.createMeasureNode(vProps); var vUseProperties=qx.ui.basic.Label._fontProperties[vProps]; var vUsePropertiesLength=vUseProperties.length-1; var vProperty=vUseProperties[vUsePropertiesLength--]; var vStyle = vNode.style; var vTemp; if (!vProperty) { return vNode; } do { vStyle[vProperty] = qx.util.Validation.isValid(vTemp = this.getStyleProperty([vProperty])) ? vTemp : ""; } while(vProperty=vUseProperties[vUsePropertiesLength--]); return vNode; } /* --------------------------------------------------------------------------- PREFERRED DIMENSIONS --------------------------------------------------------------------------- */ qx.Proto._computePreferredInnerWidth = function() { this._computeObjectNeededDimensions(); return this._cachedPreferredInnerWidth; } qx.Proto._computePreferredInnerHeight = function() { this._computeObjectNeededDimensions(); return this._cachedPreferredInnerHeight; } /* --------------------------------------------------------------------------- LAYOUT APPLY --------------------------------------------------------------------------- */ qx.Proto._postApply = function() { var vHtml = this.getHtml(); var vElement = this._getTargetNode(); var vMnemonicMode = 0; if (qx.util.Validation.isInvalidString(vHtml)) { vElement.innerHTML = ""; return; } if (this._hasMnemonic) { vMnemonicMode = this._mnemonicTest.test(vHtml) ? 1 : 2; } // works only with text, don't use when wrap is enabled if (!this._htmlMode && !this.getWrap()) { switch(this._computedWidthType) { case qx.ui.core.Widget.TYPE_PIXEL: case qx.ui.core.Widget.TYPE_PERCENT: //carstenl: enabled truncation code for flex sizing, too. Appears to work except for the // truncation code (gecko version), which I have disabled (see below). case qx.ui.core.Widget.TYPE_FLEX: var vNeeded = this.getPreferredInnerWidth(); var vInner = this.getInnerWidth(); if (vInner < vNeeded) { vElement.style.overflow = "hidden"; if (qx.ui.basic.Label.SUPPORT_NATIVE_ELLIPSIS) { vElement.style.textOverflow = "ellipsis"; vHtml += this._mnemonicHtml; } else { var vMeasureNode = this._copyStyles(); var vSplitString = vHtml.split(" "); var vSplitLength = vSplitString.length; var vWordIterator = 0; var vCharaterIterator = 0; var vPost = qx.ui.basic.Label.SYMBOL_ELLIPSIS; var vUseInnerText = true; if (vMnemonicMode == 2) { var vPost = this._mnemonicHtml + vPost; vUseInnerText = false; } // Measure Words (if more than one) if (vSplitLength > 1) { var vSplitTemp = []; for (vWordIterator=0; vWordIterator vInner) /* carstenl: The following code (truncate the text to fit in the available * space, append ellipsis to indicate truncation) did not reliably * work in my tests. Problem was that sometimes the measurer returned * insanely high values for short texts, like "I..." requiring 738 px. * * I don't have time to examine this code in detail. Since all of my * tests used flex width and the truncation code never was intended * for this, I am disabling truncation if flex is active. */ && (this._computedWidthType != qx.ui.core.Widget.TYPE_FLEX)){ break; } } // Remove last word which does not fit vSplitTemp.pop(); // Building new temportary array vSplitTemp = [ vSplitTemp.join(" ") ]; // Extracting remaining string vCharaterString = vHtml.replace(vSplitTemp[0], ""); } else { var vSplitTemp = []; vCharaterString = vHtml; } var vCharaterLength = vCharaterString.length; // Measure Chars for (var vCharaterIterator=0; vCharaterIterator vInner) { break; } } // Remove last char which does not fit vSplitTemp.pop(); // Add mnemonic and ellipsis symbol vSplitTemp.push(vPost); // Building Final HTML String vHtml = vSplitTemp.join(""); } break; } else { vHtml += this._mnemonicHtml; } // no break here default: vElement.style.overflow = ""; if (qx.ui.basic.Label.SUPPORT_NATIVE_ELLIPSIS) { vElement.style.textOverflow = ""; } } } if (vMnemonicMode == 1) { // re-test: needed to make ellipsis handling correct this._mnemonicTest.test(vHtml); vHtml = RegExp.$1 + "" + RegExp.$7 + "" + RegExp.rightContext; } return this._postApplyHtml(vElement, vHtml, vMnemonicMode); } qx.Proto._postApplyHtml = function(vElement, vHtml, vMnemonicMode) { if (this._htmlMode || vMnemonicMode > 0) { vElement.innerHTML = vHtml; } else { try { qx.dom.Element.setTextContent(vElement, vHtml); } catch(ex) { vElement.innerHTML = vHtml; } } }