forked from enviPath/enviPy
208 lines
5.6 KiB
JavaScript
208 lines
5.6 KiB
JavaScript
var Promise = require('promise');
|
|
var dom = require('./dom');
|
|
var Ruler = require('./ruler');
|
|
|
|
/**
|
|
* @constructor
|
|
*
|
|
* @param {string} family
|
|
* @param [fontface.Descriptors] descriptors
|
|
*/
|
|
var Observer = function (family, descriptors) {
|
|
descriptors = descriptors || {weight: 'normal'};
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['family'] = family;
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['style'] = descriptors.style || 'normal';
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['variant'] = descriptors.variant || 'normal';
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['weight'] = descriptors.weight || 'normal';
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['stretch'] = descriptors.stretch || 'stretch';
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
this['featureSettings'] = descriptors.featureSettings || 'normal';
|
|
};
|
|
|
|
module.exports = Observer;
|
|
|
|
/**
|
|
* @type {null|boolean}
|
|
*/
|
|
Observer.HAS_WEBKIT_FALLBACK_BUG = null;
|
|
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
Observer.DEFAULT_TIMEOUT = 3000;
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
Observer.getUserAgent = function () {
|
|
return window.navigator.userAgent;
|
|
};
|
|
|
|
/**
|
|
* Returns true if this browser is WebKit and it has the fallback bug
|
|
* which is present in WebKit 536.11 and earlier.
|
|
*
|
|
* @return {boolean}
|
|
*/
|
|
Observer.hasWebKitFallbackBug = function () {
|
|
if (Observer.HAS_WEBKIT_FALLBACK_BUG === null) {
|
|
var match = /AppleWeb[kK]it\/([0-9]+)(?:\.([0-9]+))/.exec(Observer.getUserAgent());
|
|
|
|
Observer.HAS_WEBKIT_FALLBACK_BUG = !!match &&
|
|
(parseInt(match[1], 10) < 536 ||
|
|
(parseInt(match[1], 10) === 536 &&
|
|
parseInt(match[2], 10) <= 11));
|
|
}
|
|
return Observer.HAS_WEBKIT_FALLBACK_BUG;
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @return {string}
|
|
*/
|
|
Observer.prototype.getStyle = function () {
|
|
return 'font-style:' + this['style'] + ';' +
|
|
'font-variant:' + this['variant'] + ';' +
|
|
'font-weight:' + this['weight'] + ';' +
|
|
'font-stretch:' + this['stretch'] + ';' +
|
|
'font-feature-settings:' + this['featureSettings'] + ';' +
|
|
'-moz-font-feature-settings:' + this['featureSettings'] + ';' +
|
|
'-webkit-font-feature-settings:' + this['featureSettings'] + ';';
|
|
};
|
|
|
|
/**
|
|
* @param {string=} text Optional test string to use for detecting if a font is available.
|
|
* @param {number=} timeout Optional timeout for giving up on font load detection and rejecting the promise (defaults to 3 seconds).
|
|
* @return {Promise.<fontface.Observer>}
|
|
*/
|
|
Observer.prototype.check = function (text, timeout) {
|
|
var testString = text || 'BESbswy',
|
|
timeoutValue = timeout || Observer.DEFAULT_TIMEOUT,
|
|
style = this.getStyle(),
|
|
container = dom.createElement('div'),
|
|
|
|
rulerA = new Ruler(testString),
|
|
rulerB = new Ruler(testString),
|
|
rulerC = new Ruler(testString),
|
|
|
|
widthA = -1,
|
|
widthB = -1,
|
|
widthC = -1,
|
|
|
|
fallbackWidthA = -1,
|
|
fallbackWidthB = -1,
|
|
fallbackWidthC = -1,
|
|
|
|
that = this;
|
|
|
|
rulerA.setFont('"Times New Roman", sans-serif', style);
|
|
rulerB.setFont('serif', style);
|
|
rulerC.setFont('monospace', style);
|
|
|
|
dom.append(container, rulerA.getElement());
|
|
dom.append(container, rulerB.getElement());
|
|
dom.append(container, rulerC.getElement());
|
|
|
|
dom.append(document.body, container);
|
|
|
|
fallbackWidthA = rulerA.getWidth();
|
|
fallbackWidthB = rulerB.getWidth();
|
|
fallbackWidthC = rulerC.getWidth();
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
/**
|
|
* @private
|
|
*/
|
|
function removeContainer() {
|
|
if (container.parentNode !== null) {
|
|
dom.remove(document.body, container);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*
|
|
* Cases:
|
|
* 1) Font loads: both a, b and c are called and have the same value.
|
|
* 2) Font fails to load: resize callback is never called and timeout happens.
|
|
* 3) WebKit bug: both a, b and c are called and have the same value, but the
|
|
* values are equal to one of the last resort fonts, we ignore this and
|
|
* continue waiting until we get new values (or a timeout).
|
|
*/
|
|
function check() {
|
|
if (widthA !== -1 && widthB !== -1 && widthC !== -1) {
|
|
// All values are changed from their initial state
|
|
|
|
if (widthA === widthB && widthB === widthC) {
|
|
// All values are the same, so the browser has most likely loaded the web font
|
|
|
|
if (Observer.hasWebKitFallbackBug()) {
|
|
// Except if the browser has the WebKit fallback bug, in which case we check to see if all
|
|
// values are set to one of the last resort fonts.
|
|
|
|
if (!((widthA === fallbackWidthA && widthB === fallbackWidthA && widthC === fallbackWidthA) ||
|
|
(widthA === fallbackWidthB && widthB === fallbackWidthB && widthC === fallbackWidthB) ||
|
|
(widthA === fallbackWidthC && widthB === fallbackWidthC && widthC === fallbackWidthC))) {
|
|
// The width we got doesn't match any of the known last resort fonts, so let's assume fonts are loaded.
|
|
removeContainer();
|
|
resolve(that);
|
|
}
|
|
} else {
|
|
removeContainer();
|
|
resolve(that);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setTimeout(function () {
|
|
removeContainer();
|
|
reject(that);
|
|
}, timeoutValue);
|
|
|
|
rulerA.onResize(function (width) {
|
|
widthA = width;
|
|
check();
|
|
});
|
|
|
|
rulerA.setFont(that['family'] + ',sans-serif', style);
|
|
|
|
rulerB.onResize(function (width) {
|
|
widthB = width;
|
|
check();
|
|
});
|
|
|
|
rulerB.setFont(that['family'] + ',serif', style);
|
|
|
|
rulerC.onResize(function (width) {
|
|
widthC = width;
|
|
check();
|
|
});
|
|
|
|
rulerC.setFont(that['family'] + ',monospace', style);
|
|
});
|
|
};
|