diff --git a/helpers/getFontLoaderConfig.js b/helpers/getFontLoaderConfig.js new file mode 100644 index 00000000..3e91dbce --- /dev/null +++ b/helpers/getFontLoaderConfig.js @@ -0,0 +1,11 @@ +'use strict'; + +const getFonts = require('../lib/fonts'); + +module.exports = function(paper) { + const handlebars = paper.handlebars; + + handlebars.registerHelper('getFontLoaderConfig', function () { + return new handlebars.SafeString(JSON.stringify(getFonts(paper, 'webFontLoaderConfig'))); + }); +}; diff --git a/helpers/getFontsCollection.js b/helpers/getFontsCollection.js index 59580b75..30d3049e 100644 --- a/helpers/getFontsCollection.js +++ b/helpers/getFontsCollection.js @@ -1,85 +1,11 @@ 'use strict'; -var _ = require('lodash'); +const getFonts = require('../lib/fonts'); -function helper(paper) { - var handlebars = paper.handlebars; +module.exports = function(paper) { + const handlebars = paper.handlebars; handlebars.registerHelper('getFontsCollection', function () { - var fontKeyFormat = new RegExp(/\w+(-\w*)*-font$/), - googleFonts = [], - linkElements = []; - - _.each(paper.themeSettings, function (value, key) { - var split; - - if (fontKeyFormat.test(key)) { - split = value.split('_'); - - switch (split[0]) { - case 'Google': - googleFonts.push(value); - break; - - default: - break; - } - } - }); - - linkElements.push(googleParser(googleFonts)); - - return new handlebars.SafeString(linkElements.join('')); - }); -} - -/** - * Parser for Google fonts - * - * @param fonts - Array of fonts that might look like - * Google_Open+Sans - * Google_Open+Sans_400 - * Google_Open+Sans_400_sans - * Google_Open+Sans_400,700_sans - * Google_Open+Sans_400,700italic - * Google_Open+Sans_400,700italic_sans - * - * @returns {string} - */ - -function googleParser(fonts) { - var collection = [], - familyHash = {}; - - _.each(fonts, function fontsIterator(font) { - var split = font.split('_'), - familyKey = split[1], // Eg: Open+Sans - weights = split[2]; // Eg: 400,700italic - - if (_.isEmpty(familyKey)) { - return; - } - - if (_.isUndefined(weights)) { - weights = ''; - } - - if (!_.isArray(familyHash[familyKey])) { - familyHash[familyKey] = []; - } - - weights = weights.split(','); - - familyHash[familyKey].push(weights); - familyHash[familyKey] = _.uniq(_.flatten(familyHash[familyKey])); + return getFonts(paper, 'linkElements'); }); - - - _.each(familyHash, function fontHashIterator(weights, family) { - collection.push(family + ':' + weights.join(',')); - }); - - return ''; -} - -module.exports = helper; +}; diff --git a/helpers/resourceHints.js b/helpers/resourceHints.js new file mode 100644 index 00000000..8acf5211 --- /dev/null +++ b/helpers/resourceHints.js @@ -0,0 +1,38 @@ +'use strict'; + +const _ = require('lodash'); +const getFonts = require('../lib/fonts'); + +const fontResources = { + 'Google': [ + '//ajax.googleapis.com', + '//fonts.googleapis.com', + '//fonts.gstatic.com', + ], +}; + +module.exports = function(paper) { + paper.handlebars.registerHelper('resourceHints', function() { + function format(host) { + return ``; + } + + var hosts = []; + + // Add cdn + const cdnUrl = paper.settings['cdn_url'] || ''; + if (cdnUrl != '') { + hosts.push(cdnUrl); + } + + // Add font providers + const fontProviders = _.keys(getFonts(paper, 'providerLists')); + _.each(fontProviders, function(provider) { + if (typeof fontResources[provider] !== 'undefined') { + hosts = hosts.concat(fontResources[provider]); + } + }); + + return new paper.handlebars.SafeString(_.map(hosts, format).join('')); + }); +} diff --git a/lib/fonts.js b/lib/fonts.js new file mode 100644 index 00000000..c6514db9 --- /dev/null +++ b/lib/fonts.js @@ -0,0 +1,134 @@ +'use strict'; + +const _ = require('lodash'); +const fontKeyFormat = new RegExp(/\w+(-\w*)*-font$/); +const fontProviders = { + 'Google': { + /** + * Parser for Google fonts + * + * @param {Array} fonts - Array of fonts that might look like + * Google_Open+Sans + * Google_Open+Sans_400 + * Google_Open+Sans_400_sans + * Google_Open+Sans_400,700_sans + * Google_Open+Sans_400,700italic + * Google_Open+Sans_400,700italic_sans + * + * @returns {string} + */ + parser: function(fonts) { + var collection = [], + familyHash = {}; + + _.each(fonts, function fontsIterator(font) { + var split = font.split('_'), + familyKey = split[1], // Eg: Open+Sans + weights = split[2]; // Eg: 400,700italic + + if (_.isEmpty(familyKey)) { + return; + } + + if (_.isUndefined(weights)) { + weights = ''; + } + + if (!_.isArray(familyHash[familyKey])) { + familyHash[familyKey] = []; + } + + weights = weights.split(','); + + familyHash[familyKey].push(weights); + familyHash[familyKey] = _.uniq(_.flatten(familyHash[familyKey])); + }); + + _.each(familyHash, function fontHashIterator(weights, family) { + collection.push(family + ':' + weights.join(',')); + }); + + return collection; + }, + + buildLink: function(fonts) { + return ''; + }, + + buildFontLoaderConfig: function(fonts) { + function replaceSpaces(font) { + return font.split('+').join(' '); + } + + return { + google: { + families: _.map(fonts, replaceSpaces), + } + }; + }, + }, +}; + +/** + * Get collection of fonts used in theme settings. + * + * @param {Object} paper The paper instance + * @param {string} format The desired return value. If format == 'providerLists', return an object with provider names for keys + * and a list of fonts in the provider format as values, suitable for use with Web Font Loader. If format == 'linkElements', + * return a string containing elements to be directly inserted into the page. If format == 'webFontLoaderConfig', return an + * object that can be used to configure Web Font Loader. + * @returns {Object.|string} + */ +module.exports = function(paper, format) { + // Collect font strings from theme settings + const collectedFonts = {}; + _.each(paper.themeSettings, function(value, key) { + if (!fontKeyFormat.test(key)) { + return; + } + + const splits = value.split('_'); + const provider = splits[0]; + + if (typeof fontProviders[provider] === 'undefined') { + return; + } + + if (typeof collectedFonts[provider] === 'undefined') { + collectedFonts[provider] = []; + } + + collectedFonts[provider].push(value); + }); + + // Parse font strings based on provider + const parsedFonts = _.mapValues(collectedFonts, function(value, key) { + return fontProviders[key].parser(value); + }); + + // Format output based on requested format + switch(format) { + case 'linkElements': + const formattedFonts = _.mapValues(parsedFonts, function(value, key) { + return fontProviders[key].buildLink(value); + }); + return new paper.handlebars.SafeString(_.values(formattedFonts).join('')); + + case 'webFontLoaderConfig': + // Build configs + const configs = _.mapValues(parsedFonts, function(value, key) { + return fontProviders[key].buildFontLoaderConfig(value); + }); + + // Merge them + const fontLoaderConfig = {}; + _.each(configs, function(config) { + return Object.assign(fontLoaderConfig, config); + }); + return fontLoaderConfig; + + case 'providerLists': + default: + return parsedFonts; + } +} diff --git a/test/helpers/getFontLoaderConfig.js b/test/helpers/getFontLoaderConfig.js new file mode 100644 index 00000000..2bd71a56 --- /dev/null +++ b/test/helpers/getFontLoaderConfig.js @@ -0,0 +1,37 @@ +var Code = require('code'), + Lab = require('lab'), + Paper = require('../../index'), + lab = exports.lab = Lab.script(), + describe = lab.experiment, + expect = Code.expect, + it = lab.it; + +function c(template, themeSettings) { + return new Paper({}, themeSettings).loadTemplatesSync({template: template}).render('template', {}); +} + +describe('getFontLoaderConfig helper', function () { + it('should return the expected font url', function (done) { + const themeSettings = { + 'test1-font': 'Google_Open+Sans', + 'test2-font': 'Google_Open+Sans_400italic', + 'test3-font': 'Google_Open+Sans_700', + 'test4-font': 'Google_Karla_700', + 'test5-font': 'Google_Lora_400_sans', + 'test6-font': 'Google_Volkron', + 'test7-font': 'Google_Droid_400,700', + 'test8-font': 'Google_Crimson+Text_400,700_sans', + 'random-property': 'not a font' + }; + + const template = "{{getFontLoaderConfig}}"; + const expectedConfig = { + google: { + families: ['Open Sans:,400italic,700', 'Karla:700', 'Lora:400', 'Volkron:', 'Droid:400,700', 'Crimson Text:400,700'] + } + }; + + expect(c(template, themeSettings)).to.be.equal(JSON.stringify(expectedConfig)); + done(); + }); +}); diff --git a/test/helpers/resourceHints.js b/test/helpers/resourceHints.js new file mode 100644 index 00000000..acff6a82 --- /dev/null +++ b/test/helpers/resourceHints.js @@ -0,0 +1,33 @@ +var Code = require('code'), + Lab = require('lab'), + Paper = require('../../index'), + lab = exports.lab = Lab.script(), + describe = lab.experiment, + expect = Code.expect, + it = lab.it; + +function c(template, themeSettings) { + return new Paper({}, themeSettings).loadTemplatesSync({template: template}).render('template', {}); +} + +describe('resourceHints helper', function () { + it('should return the expected google resources', function (done) { + var themeSettings = { + 'test1-font': 'Google_Open+Sans', + 'test2-font': 'Google_Open+Sans_400italic', + 'test3-font': 'Google_Open+Sans_700', + 'test4-font': 'Google_Karla_700', + 'test5-font': 'Google_Lora_400_sans', + 'test6-font': 'Google_Volkron', + 'test7-font': 'Google_Droid_400,700', + 'test8-font': 'Google_Crimson+Text_400,700_sans', + 'random-property': 'not a font' + }; + + var template = "{{resourceHints}}"; + + expect(c(template, themeSettings)) + .to.be.equal(''); + done(); + }); +});