UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

406 lines (335 loc) 10.9 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. ************************************************************************ */ /** * Checks whether a given font is available on the document and fires events * accordingly. * * This class does not need to be disposed, unless you want to abort the validation * early */ qx.Class.define("qx.bom.webfonts.Validator", { extend : qx.core.Object, /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param fontFamily {String} The name of the font to be verified * @param comparisonString {String?} String to be used to detect whether a font was loaded or not * whether the font has loaded properly */ construct : function(fontFamily, comparisonString) { this.base(arguments); if (comparisonString) { this.setComparisonString(comparisonString); } if (fontFamily) { this.setFontFamily(fontFamily); this.__requestedHelpers = this._getRequestedHelpers(); } }, /* ***************************************************************************** STATICS ***************************************************************************** */ statics : { /** * Sets of serif and sans-serif fonts to be used for size comparisons. * At least one of these fonts should be present on any system. */ COMPARISON_FONTS : { sans : ["Arial", "Helvetica", "sans-serif"], serif : ["Times New Roman", "Georgia", "serif"] }, /** * Map of common CSS attributes to be used for all size comparison elements */ HELPER_CSS : { position: "absolute", margin: "0", padding: "0", top: "-1000px", left: "-1000px", fontSize: "350px", width: "auto", height: "auto", lineHeight: "normal", fontVariant: "normal", visibility: "hidden" }, /** * The string to be used in the size comparison elements. This is the default string * which is used for the {@link #COMPARISON_FONTS} and the font to be validated. It * can be overridden for the font to be validated using the {@link #comparisonString} * property. */ COMPARISON_STRING : "WEei", __defaultSizes : null, __defaultHelpers : null, /** * Removes the two common helper elements used for all size comparisons from * the DOM */ removeDefaultHelperElements : function() { var defaultHelpers = qx.bom.webfonts.Validator.__defaultHelpers; if (defaultHelpers) { for (var prop in defaultHelpers) { document.body.removeChild(defaultHelpers[prop]); } } delete qx.bom.webfonts.Validator.__defaultHelpers; } }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { /** * The font-family this validator should check */ fontFamily : { nullable : true, init : null, apply : "_applyFontFamily" }, /** * Comparison string used to check whether the font has loaded or not. */ comparisonString : { nullable : true, init : null }, /** * Time in milliseconds from the beginning of the check until it is assumed * that a font is not available */ timeout : { check : "Integer", init : 5000 } }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events : { /** * Fired when the status of a web font has been determined. The event data * is a map with the keys "family" (the font-family name) and "valid" * (Boolean). */ "changeStatus" : "qx.event.type.Data" }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __requestedHelpers : null, __checkTimer : null, __checkStarted : null, /* --------------------------------------------------------------------------- PUBLIC API --------------------------------------------------------------------------- */ /** * Validates the font */ validate : function() { this.__checkStarted = new Date().getTime(); if (this.__checkTimer) { this.__checkTimer.restart(); } else { this.__checkTimer = new qx.event.Timer(100); this.__checkTimer.addListener("interval", this.__onTimerInterval, this); // Give the browser a chance to render the new elements qx.event.Timer.once(function() { this.__checkTimer.start(); }, this, 0); } }, /* --------------------------------------------------------------------------- PROTECTED API --------------------------------------------------------------------------- */ /** * Removes the helper elements from the DOM */ _reset : function() { if (this.__requestedHelpers) { for (var prop in this.__requestedHelpers) { var elem = this.__requestedHelpers[prop]; document.body.removeChild(elem); } this.__requestedHelpers = null; } }, /** * Checks if the font is available by comparing the widths of the elements * using the generic fonts to the widths of the elements using the font to * be validated * * @return {Boolean} Whether or not the font caused the elements to differ * in size */ _isFontValid : function() { if (!qx.bom.webfonts.Validator.__defaultSizes) { this.__init(); } if (!this.__requestedHelpers) { this.__requestedHelpers = this._getRequestedHelpers(); } // force rerendering for chrome this.__requestedHelpers.sans.style.visibility = "visible"; this.__requestedHelpers.sans.style.visibility = "hidden"; this.__requestedHelpers.serif.style.visibility = "visible"; this.__requestedHelpers.serif.style.visibility = "hidden"; var requestedSans = qx.bom.element.Dimension.getWidth(this.__requestedHelpers.sans); var requestedSerif = qx.bom.element.Dimension.getWidth(this.__requestedHelpers.serif); var cls = qx.bom.webfonts.Validator; if (requestedSans !== cls.__defaultSizes.sans || requestedSerif !== cls.__defaultSizes.serif) { return true; } return false; }, /** * Creates the two helper elements styled with the font to be checked * * @return {Map} A map with the keys <pre>sans</pre> and <pre>serif</pre> * and the created span elements as values */ _getRequestedHelpers : function() { var fontsSans = [this.getFontFamily()].concat(qx.bom.webfonts.Validator.COMPARISON_FONTS.sans); var fontsSerif = [this.getFontFamily()].concat(qx.bom.webfonts.Validator.COMPARISON_FONTS.serif); return { sans : this._getHelperElement(fontsSans, this.getComparisonString()), serif : this._getHelperElement(fontsSerif, this.getComparisonString()) }; }, /** * Creates a span element with the comparison text (either {@link #COMPARISON_STRING} or * {@link #comparisonString}) and styled with the default CSS ({@link #HELPER_CSS}) plus * the given font-family value and appends it to the DOM * * @param fontFamily {String} font-family string * @param comparisonString {String?} String to be used to detect whether a font was loaded or not * @return {Element} the created DOM element */ _getHelperElement : function(fontFamily, comparisonString) { var styleMap = qx.lang.Object.clone(qx.bom.webfonts.Validator.HELPER_CSS); if (fontFamily) { if (styleMap.fontFamily) { styleMap.fontFamily += "," + fontFamily.join(","); } else { styleMap.fontFamily = fontFamily.join(","); } } var elem = document.createElement("span"); elem.innerHTML = comparisonString || qx.bom.webfonts.Validator.COMPARISON_STRING; qx.bom.element.Style.setStyles(elem, styleMap); document.body.appendChild(elem); return elem; }, // property apply _applyFontFamily : function(value, old) { if (value !== old) { this._reset(); } }, /* --------------------------------------------------------------------------- PRIVATE API --------------------------------------------------------------------------- */ /** * Creates the default helper elements and gets their widths */ __init : function() { var cls = qx.bom.webfonts.Validator; if (!cls.__defaultHelpers) { cls.__defaultHelpers = { sans : this._getHelperElement(cls.COMPARISON_FONTS.sans), serif : this._getHelperElement(cls.COMPARISON_FONTS.serif) }; } cls.__defaultSizes = { sans : qx.bom.element.Dimension.getWidth(cls.__defaultHelpers.sans), serif: qx.bom.element.Dimension.getWidth(cls.__defaultHelpers.serif) }; }, /** * Triggers helper element size comparison and fires a ({@link #changeStatus}) * event with the result. */ __onTimerInterval : function() { if (this._isFontValid()) { this.__checkTimer.stop(); this._reset(); this.fireDataEvent("changeStatus", { family : this.getFontFamily(), valid : true }); } else { var now = new Date().getTime(); if (now - this.__checkStarted >= this.getTimeout()) { this.__checkTimer.stop(); this._reset(); this.fireDataEvent("changeStatus", { family : this.getFontFamily(), valid : false }); } } } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct : function() { this._reset(); this.__checkTimer.stop(); this.__checkTimer.removeListener("interval", this.__onTimerInterval, this); this._disposeObjects("__checkTimer"); } });