UNPKG

@smartface/smartface.font

Version:
355 lines (325 loc) 13.1 kB
/* 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 }); } });