diff --git a/core/frontend/filters/index.js b/core/frontend/filters/index.js index 32d022c65f..3252d78d80 100644 --- a/core/frontend/filters/index.js +++ b/core/frontend/filters/index.js @@ -6,8 +6,12 @@ coreFilters = function (ghost) { ghost.registerFilter('ghostNavItems', defaultCoreFilterPriority, function (args) { var selectedItem; - // Set the nav items based on the config - args.navItems = ghost.config().nav; + // we want to clone the config so the config remains unchanged + // we will need to make this recursive if we start supporting + // hierarchical menus + args.navItems = _.map(ghost.config().nav, function (value) { + return Object.create(value); + }); // Mark the current selected Item selectedItem = _.find(args.navItems, function (item) { diff --git a/core/frontend/helpers/index.js b/core/frontend/helpers/index.js index 5213f0fe48..b484dcbec0 100644 --- a/core/frontend/helpers/index.js +++ b/core/frontend/helpers/index.js @@ -1,12 +1,14 @@ var _ = require('underscore'), moment = require('moment'), when = require('when'), - pagination = require('./paginate'), - navHelper = require('./navigation'), hbs = require('express-hbs'), + errors = require('../../shared/errorHandling'), coreHelpers; coreHelpers = function (ghost) { + var navHelper, + paginationHelper; + /** * [ description] * @todo ghost core helpers + a way for themes to register them @@ -119,10 +121,40 @@ coreHelpers = function (ghost) { } return ret; }); - // Just one async helper for now, but could be more in the future + + // ## Template driven helpers + // Template driven helpers require that their template is loaded before they can be registered. + + // ###Nav Helper + // `{{nav}}` + // Outputs a navigation menu built from items in config.js + navHelper = ghost.loadTemplate('nav').then(function (templateFn) { + ghost.registerThemeHelper('nav', function (options) { + if (!_.isObject(this.navItems) || _.isFunction(this.navItems)) { + errors.logAndThrowError('navItems data is not an object or is a function'); + return; + } + return new hbs.handlebars.SafeString(templateFn({links: this.navItems})); + }); + }); + + // ### Pagination Helper + // `{{paginate}}` + // Outputs previous and next buttons, along with info about the current page + paginationHelper = ghost.loadTemplate('pagination').then(function (templateFn) { + ghost.registerThemeHelper('paginate', function (options) { + if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) { + errors.logAndThrowError('pagination data is not an object or is a function'); + return; + } + return new hbs.handlebars.SafeString(templateFn(this.pagination)); + }); + }); + + // Return once the template-driven helpers have loaded return when.join( - navHelper.registerWithGhost(ghost), - pagination.registerWithGhost(ghost) + navHelper, + paginationHelper ); }; diff --git a/core/frontend/helpers/navigation.js b/core/frontend/helpers/navigation.js deleted file mode 100644 index f88573d440..0000000000 --- a/core/frontend/helpers/navigation.js +++ /dev/null @@ -1,49 +0,0 @@ -var fs = require('fs'), - path = require('path'), - _ = require('underscore'), - handlebars = require('express-hbs').handlebars, - nodefn = require('when/node/function'), - NavHelper; - -NavHelper = function (navTemplate) { - // Bind the context for our methods. - _.bindAll(this, 'compileTemplate', 'renderNavItems'); - - if (_.isFunction(navTemplate)) { - this.navTemplateFunc = navTemplate; - } else { - this.navTemplatePath = navTemplate; - } -}; - -NavHelper.prototype.compileTemplate = function (templatePath) { - var self = this; - - // Allow people to overwrite the navTemplatePath - templatePath = templatePath || this.navTemplatePath; - - return nodefn.call(fs.readFile, templatePath).then(function (navTemplateContents) { - // TODO: Can handlebars compile async? - self.navTemplateFunc = handlebars.compile(navTemplateContents.toString()); - }); -}; - -NavHelper.prototype.renderNavItems = function (navItems) { - var output; - - output = this.navTemplateFunc({links: navItems}); - - return output; -}; - -// A static helper method for registering with ghost -NavHelper.registerWithGhost = function (ghost) { - var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'), - ghostNavHelper = new NavHelper(templatePath); - - return ghostNavHelper.compileTemplate().then(function () { - ghost.registerThemeHelper("nav", ghostNavHelper.renderNavItems); - }); -}; - -module.exports = NavHelper; \ No newline at end of file diff --git a/core/frontend/helpers/paginate.js b/core/frontend/helpers/paginate.js deleted file mode 100644 index 070d966be1..0000000000 --- a/core/frontend/helpers/paginate.js +++ /dev/null @@ -1,47 +0,0 @@ -var fs = require('fs'), - path = require('path'), - _ = require('underscore'), - handlebars = require('express-hbs').handlebars, - nodefn = require('when/node/function'), - - PaginationHelper; - -PaginationHelper = function (paginationTemplate) { - // Bind the context for our methods. - _.bindAll(this, 'compileTemplate', 'renderPagination'); - - if (_.isFunction(paginationTemplate)) { - this.paginationTemplateFunc = paginationTemplate; - } else { - this.paginationTemplatePath = paginationTemplate; - } -}; - -PaginationHelper.prototype.compileTemplate = function (templatePath) { - var self = this; - - - templatePath = templatePath || this.paginationTemplatePath; - - return nodefn.call(fs.readFile, templatePath).then(function (paginationContents) { - // TODO: Can handlebars compile async? - self.paginationTemplateFunc = handlebars.compile(paginationContents.toString()); - }); -}; - -PaginationHelper.prototype.renderPagination = function (context, options) { - var output = this.paginationTemplateFunc(context); - return output; -}; - - -PaginationHelper.registerWithGhost = function (ghost) { - var templatePath = path.join(ghost.paths().frontendViews, 'pagination.hbs'), - paginationHelper = new PaginationHelper(templatePath); - - return paginationHelper.compileTemplate().then(function () { - ghost.registerThemeHelper("paginate", paginationHelper.renderPagination); - }); -}; - -module.exports = PaginationHelper; \ No newline at end of file diff --git a/core/ghost.js b/core/ghost.js index 59c999e1d7..0d0e0b4bf8 100644 --- a/core/ghost.js +++ b/core/ghost.js @@ -6,8 +6,10 @@ var config = require('./../config'), when = require('when'), express = require('express'), errors = require('../core/shared/errorHandling'), + fs = require('fs'), path = require('path'), hbs = require('express-hbs'), + nodefn = require('when/node/function'), _ = require('underscore'), Polyglot = require('node-polyglot'), @@ -167,6 +169,21 @@ Ghost.prototype.updateSettingsCache = function (settings) { } }; +// ## Template utils + +Ghost.prototype.compileTemplate = function (templatePath) { + return nodefn.call(fs.readFile, templatePath).then(function (templateContents) { + return hbs.handlebars.compile(templateContents.toString()); + }, errors.logAndThrowError); +}; + +Ghost.prototype.loadTemplate = function (name) { + // TODO: allow themes to override these templates + var templatePath = path.join(this.paths().frontendViews, name + '.hbs'); + + return this.compileTemplate(templatePath); +}; + /** * @param {string} name * @param {Function} fn diff --git a/core/test/ghost/fixtures/test.hbs b/core/test/ghost/fixtures/test.hbs new file mode 100644 index 0000000000..b8264bd9f3 --- /dev/null +++ b/core/test/ghost/fixtures/test.hbs @@ -0,0 +1 @@ +