Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

View File

@ -0,0 +1,43 @@
var dom = {};
module.exports = dom;
/**
* @param {string} name
* @return {Element}
*/
dom.createElement = function (name) {
return document.createElement(name);
};
/**
* @param {string} text
* @return {Text}
*/
dom.createText = function (text) {
return document.createTextNode(text);
};
/**
* @param {Element} element
* @param {string} style
*/
dom.style = function (element, style) {
element.style.cssText = style;
};
/**
* @param {Node} parent
* @param {Node} child
*/
dom.append = function (parent, child) {
parent.appendChild(child);
};
/**
* @param {Node} parent
* @param {Node} child
*/
dom.remove = function (parent, child) {
parent.removeChild(child);
};

View File

@ -0,0 +1,207 @@
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);
});
};

View File

@ -0,0 +1,125 @@
var dom = require('./dom');
/**
* @constructor
* @param {string} text
*/
var Ruler = function (text) {
var style = 'display:inline-block;' +
'position:absolute;' +
'height:100%;' +
'width:100%;' +
'overflow:scroll;';
this.element = dom.createElement('div');
this.element.setAttribute('aria-hidden', 'true');
dom.append(this.element, dom.createText(text));
this.collapsible = dom.createElement('span');
this.expandable = dom.createElement('span');
this.collapsibleInner = dom.createElement('span');
this.expandableInner = dom.createElement('span');
this.lastOffsetWidth = -1;
dom.style(this.collapsible, style);
dom.style(this.expandable, style);
dom.style(this.expandableInner, style);
dom.style(this.collapsibleInner, 'display:inline-block;width:200%;height:200%;');
dom.append(this.collapsible, this.collapsibleInner);
dom.append(this.expandable, this.expandableInner);
dom.append(this.element, this.collapsible);
dom.append(this.element, this.expandable);
};
module.exports = Ruler;
/**
* @return {Element}
*/
Ruler.prototype.getElement = function () {
return this.element;
};
/**
* @param {string} family
* @param {string} description
*/
Ruler.prototype.setFont = function (family, description) {
dom.style(this.element, 'min-width:20px;' +
'min-height:20px;' +
'display:inline-block;' +
'position:absolute;' +
'width:auto;' +
'margin:0;' +
'padding:0;' +
'top:-999px;' +
'left:-999px;' +
'white-space:nowrap;' +
'font-size:100px;' +
'font-family:' + family + ';' +
description);
};
/**
* @return {number}
*/
Ruler.prototype.getWidth = function () {
return this.element.offsetWidth;
};
/**
* @param {string} width
*/
Ruler.prototype.setWidth = function (width) {
this.element.style.width = width + 'px';
};
/**
* @private
*
* @return {boolean}
*/
Ruler.prototype.reset = function () {
var offsetWidth = this.getWidth(),
width = offsetWidth + 100;
this.expandableInner.style.width = width + 'px';
this.expandable.scrollLeft = width;
this.collapsible.scrollLeft = this.collapsible.scrollWidth + 100;
if (this.lastOffsetWidth !== offsetWidth) {
this.lastOffsetWidth = offsetWidth;
return true;
} else {
return false;
}
};
/**
* @private
* @param {function(number)} callback
*/
Ruler.prototype.onScroll = function (callback) {
if (this.reset() && this.element.parentNode !== null) {
callback(this.lastOffsetWidth);
}
};
/**
* @param {function(number)} callback
*/
Ruler.prototype.onResize = function (callback) {
var that = this;
this.collapsible.addEventListener('scroll', function () {
that.onScroll(callback);
}, false);
this.expandable.addEventListener('scroll', function () {
that.onScroll(callback);
}, false);
this.reset();
};