@smartface/smartface.font
Version:
Smartface Cloud IDE Font Loader
355 lines (325 loc) • 13.1 kB
JavaScript
/* globals DOMParser, XMLDocument XMLSerializer*/
define(function(require, exports, module) {
main.consumes = ["Plugin", "fs", "ui", "watcher", "commands", "smf.dispatcher", "jquery"];
main.provides = ["smf.font"];
return main;
function main(options, imports, register) {
const Plugin = imports.Plugin;
const ui = imports.ui;
const fs = imports.fs;
const $ = imports.jquery.jquery;
const dipatcherUrl = imports["smf.dispatcher"].httpURL;
var previewUrl = (dipatcherUrl.endsWith("/") ? dipatcherUrl : dipatcherUrl + "/") + "files/";
const defaultFontFolder = "/config/Fonts/";
const androidCss = require("text!./android-default.css");
const iOSCss = require("text!./ios-default.css");
const watcher = imports.watcher;
const commands = imports.commands;
const path = require("path");
/***** Initialization *****/
const plugin = new Plugin("Smartface.io", main.consumes);
const emit = plugin.getEmitter();
var loaded = false;
var fontInfo = null;
var _fontsObject = null;
var onUpdateList = [];
const STYLE_MAP = {
"Bold": "b",
"Italic": "i",
"BoldItalic": "bi",
"Regular": "r",
"Normal": "n"
};
const DEFAULT_STYLES = ["b", "n", "i", "bi", "r"];
ui.insertCss(androidCss, plugin);
ui.insertCss(iOSCss, plugin);
function load() {
if (loaded)
return;
loaded = true;
loadFontConfig(function(err) {
if (!err) {
emit("ready");
}
watchFontFolder();
});
commands.addCommand({
name: "update font list",
exec: function() {
loadFontConfig();
}
}, plugin);
fontInfo = {
getFamilies: function getFamilies() {
return Object.keys(_fontsObject).sort();
},
getFamilyObject: function(family) {
return Object.assign({}, _fontsObject[family]);
},
getStyles: function getThemes(family) {
var font = _fontsObject[family || "Default"] || {};
return Object
.values(font)
.map(function(value) {
return value.style;
}).sort();
},
getFont: function getFont(family, style) {
return Object.assign({}, _fontsObject[family] && _fontsObject[family][style]);
},
getFontFromUI: function getFontFromUI(font) {
var appropriateStyle = getCorrectStyle(font, font.style);
return fontInfo.getFont(font.family, appropriateStyle);
},
getCalculatedFont: getCalculatedFont,
getFontFamilyForUI: function getFontFamilyForUI(font, os) {
var _fontObj = fontInfo.getFontFromUI(font);
return JSON.stringify((font.family === "Default" ? os + "-" : "") + font.family + detectStyleWithBracket(_fontObj));
}
};
}
/***** Methods *****/
function loadFontConfig(callback) {
collectFontsFromFontFiles(function(err, fonts) {
if (err) return callback(err);
var css = generateCss(fonts);
var styleElement = document.getElementById("smf.font-style");
if (!styleElement) {
styleElement = document.createElement("style");
styleElement.setAttribute("id", "smf.font-style");
styleElement.type = 'text/css';
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(styleElement);
}
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = css;
}
else {
styleElement.innerHtml = "";
styleElement.firstChild && styleElement.removeChild(styleElement.firstChild);
styleElement.appendChild(document.createTextNode(css));
}
_fontsObject = fonts;
emit("update", getFonts());
callback && callback(null, getFonts());
});
}
function fontLazyLoading(_url) {
$.ajax({
url: _url
});
}
plugin.on("update", function(e) {
onUpdateList.forEach(function(cb) {
cb(e);
});
});
function watchFontFolder() {
watcher.watch(defaultFontFolder);
watcher.on("directory.all", function(e) {
if (e.path.startsWith(defaultFontFolder)) {
console.log("auto reloading font configuration");
loadFontConfig(function(err) {
err;
});
}
});
}
function generateCss(fonts) {
var css = [],
font, url;
for (var fontName in fonts) {
if (fontName === "Default") continue;
for (var fontFile in fonts[fontName]) {
font = fonts[fontName][fontFile];
url = previewUrl + path.join(defaultFontFolder, fontName, font.path);
css.push('@font-face {font-family:"' + font.name + '";' +
'src:url(' + url + ');}');
fontLazyLoading(url);
}
}
return css.join("\n");
}
function getFonts() {
if (_fontsObject)
return Object.assign({}, fontInfo);
else
return null;
}
function getFontsAsync(callback) {
var fonts = getFonts();
onUpdateList.push(callback);
fonts && callback && callback(fonts);
}
function collectFontsFromFontFiles(callback) {
var taskCount = 0,
fonts = {
Default: {
"Regular": {
name: "Default-Regular",
style: "Regular"
},
"Bold": {
bold: true,
name: "Default-Bold",
style: "Bold"
},
"Italic": {
italic: true,
name: "Default-Italic",
style: "Italic"
},
"BoldItalic": {
italic: true,
bold: true,
name: "Default-BoldItalic",
style: "BoldItalic"
}
}
};
fs.readdir(defaultFontFolder, function(err, files) {
if (err || (files.length === 0)) return callback(err, fonts);
taskCount += files.length;
files.forEach(function(file) {
fs.readdir(path.join(defaultFontFolder, file.name), function(_err, fontfiles) {
if (err || (fontfiles.length === 0)) return done(err);
fonts[file.name] = prepareFontObjects(fontfiles);
done();
});
});
});
function done(err) {
if (err || (--taskCount <= 0))
callback(err, fonts);
}
}
function prepareFontObjects(fontFiles) {
var res = {},
temp, pieces;
fontFiles.forEach(function(file) {
temp = {};
temp.path = file.name;
temp.name = path.basename(file.name).replace(path.extname(file.name), "");
pieces = temp.name.split(/-|_/);
temp.root = pieces[0];
temp.bracket = (temp.path.indexOf("_") !== -1) ? "_" : "-";
if (pieces[1]) {
temp.style = pieces[1];
/bold|^b$|^bi$/i.test(temp.style) && (temp.bold = true);
/italic|^i$|^bi$/i.test(temp.style) && (temp.italic = true);
}
res[temp.style || temp.name] = temp;
});
return res;
}
function getCalculatedFont(_font, key, value) {
var res = {};
if (_font) {
res = Object.assign({}, _font);
switch (key) {
case "font.family":
res.family = value;
break;
case "font.style":
res.style = value;
break;
case "font.bold":
res.style = getCorrectTheme(res, "bold", value);
res.bold = !!value;
break;
case "font.italic":
res.style = getCorrectTheme(res, "italic", value);
res.italic = !!value;
break;
case "font.size":
value ? (res.size = value) : (res.size = null);
}
if (res.family) {
res.style = getCorrectStyle(res, res.style);
var font = fontInfo.getFont(res.family, res.style);
res.bold = !!font.bold;
res.italic = !!font.italic;
}
}
return res;
}
function getCorrectStyle(font, style) {
var res;
var themes = fontInfo.getFamilyObject(font.family);
if (style && themes[style]) {
res = style;
}
else {
var tmpStyle = getCorrectTheme(font, "bold", font.bold);
if (themes[tmpStyle])
res = tmpStyle;
else if (themes[STYLE_MAP[tmpStyle]])
res = STYLE_MAP[tmpStyle];
else if ((tmpStyle === "Regular") && themes["n"])
res = "n";
else {
var styles = fontInfo.getStyles(font.family);
res = themes["n"] ? "n" : themes["r"] ? "r" : (styles.find(function(item) { return /regular/ig.test(item) }) || styles[0] || null);
}
}
return res;
}
function getCorrectTheme(font, key, value) {
if (key === "bold")
return font.italic ? value ? "BoldItalic" : "Italic" : value ? "Bold" : "Regular";
//key === "italic"
return font.bold ? value ? "BoldItalic" : "Bold" : value ? "Italic" : "Regular";
}
function detectStyleWithBracket(font) {
var res = "";
if (font.style) {
res = (DEFAULT_STYLES.indexOf(font.style) !== -1 ? "_" : "-") + font.style;
}
return res;
}
function assignFontStyle(element, options) {
var style = element.style;
var fontFamily = options.fontFamily || fontInfo[options.name].fontFamily;
style.fontFamily = fontFamily;
//TODO check it where using (calle)
}
/**
* @function insertFont
* insert specific font to smffonts.
* @param name font name
* @param path (absolute) font folder path
* @param options publish options
* @param callback (err, res)
*/
function insertFont(name, path, callback) {
if (!fontInfo) return callback(new Error("Fonts has not been initialized yet"));
if (fontInfo[name]) {
return callback(null, "already added");
}
fs.copy(path, defaultFontFolder + "/" + name, {
recursive: true
}, callback);
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("unload", function() {
loaded = false;
fontInfo = null;
_fontsObject = null;
onUpdateList = [];
});
/***** Register and define API *****/
plugin.freezePublicAPI({
loadFontConfig: loadFontConfig,
getFonts: getFonts,
getFontsAsync: getFontsAsync,
assignFontStyle: assignFontStyle,
insertFont: insertFont
});
register(null, {
"smf.font": plugin
});
}
});