in src/server/theme.js [123:200]
Theme.prototype._createThemeCssFile = function (themeCssFileName) {
var elementSelectors = this._simHostThemeInfo.elementSelectors;
// We don't want to pollute provided theme object (or hash will change)
var themeObject = JSON.parse(JSON.stringify(this.themeObject));
var css = [];
var defaultProperties = getOrCreate(themeObject, 'default', {});
var defaultElementNormalStateProperties = getOrCreate(defaultProperties, '', {});
// Ensure defaultElementNormalStateProperties is fully populated with basic properties
var defaultThemeProperties = this._simHostThemeInfo.defaultTheme.default[''];
['border', 'background', 'color', 'font-family', 'font-size', 'font-weight'].forEach(function (propertyName) {
defaultElementNormalStateProperties[propertyName] = defaultElementNormalStateProperties[propertyName] ||
defaultThemeProperties[propertyName];
});
Object.keys(elementSelectors).forEach(function (element) {
var themeElementData = getOrCreate(themeObject, element, {});
// Populate it with default data...
var elementNormalStateProperties = getOrCreate(themeElementData, '', {});
// First specific states...
if (appliedDefaultStates[element]) {
appliedDefaultStates[element].forEach(function (state) {
var elementStateProperties = getOrCreate(themeElementData, state, {});
var defaultElementStateProperties = getOrCreate(defaultProperties, state, {});
appliedDefaultProperties[element].forEach(function (propertyName) {
// If not defined, fallback on normal state, then default for this state, then default normal state
elementStateProperties[propertyName] = elementStateProperties[propertyName] ||
elementNormalStateProperties[propertyName] ||
defaultElementStateProperties[propertyName] ||
defaultElementNormalStateProperties[propertyName];
});
});
}
// Then the normal state (we do this second so that other states get right defaults)...
appliedDefaultProperties[element].forEach(function (propertyName) {
// If not defined, fallback on default
elementNormalStateProperties[propertyName] = elementNormalStateProperties[propertyName] ||
defaultElementNormalStateProperties[propertyName];
});
// Now generate the CSS for each state
var selector = elementSelectors[element];
Object.keys(themeElementData).forEach(function (state) {
outputSelectorProperties(css, selector, themeElementData[state], state);
});
});
// Allow the sim host to do any custom stuff that couldn't be handled with the default selector handling
if (this._simHostThemeInfo.doCustom) {
this._simHostThemeInfo.doCustom(css, themeObject);
}
// If sim host provides a CSS file to be scaled, scale and append it. Scaling searches for sizes declared in pixels,
// and scales them based on the current font size compared to the sim-host's default font size (always rounded to a
// whole pixel). This works better than, say, em sizing, which results in fractional pixel sizes and inconsistent
// results.
if (utils.existsSync(this._simHostScaledCssFile)) {
var defaultFontSize = this._simHostThemeInfo.defaultFontSize;
var fontSize = parseFontSize(themeObject.default['']) || 16;
var scaledCss = fs.readFileSync(this._simHostScaledCssFile, 'utf8');
if (fontSize === defaultFontSize) {
css.push(scaledCss);
} else {
var scale = fontSize / defaultFontSize;
css.push(scaledCss.replace(/\b([0-9.]+)px\b/g, function (_, pixels) {
return Math.round(pixels * scale) + 'px';
}));
}
}
fs.writeFileSync(themeCssFileName, css.join('\n'), 'utf8');
};