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();
+ });
+});