2015-01-21 10:00:38 +03:00
|
|
|
// ### Navigation Helper
|
|
|
|
// `{{navigation}}`
|
|
|
|
// Outputs navigation menu of static urls
|
2022-04-05 19:38:46 +03:00
|
|
|
const {SafeString, templates, hbs} = require('../services/handlebars');
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2021-09-26 23:01:13 +03:00
|
|
|
const errors = require('@tryghost/errors');
|
|
|
|
const tpl = require('@tryghost/tpl');
|
2020-03-30 23:23:02 +03:00
|
|
|
const {slugify} = require('@tryghost/string');
|
|
|
|
const _ = require('lodash');
|
2021-09-23 22:46:22 +03:00
|
|
|
|
|
|
|
const messages = {
|
|
|
|
invalidData: 'navigation data is not an object or is a function',
|
|
|
|
valuesMustBeDefined: 'All values must be defined for label, url and current',
|
|
|
|
valuesMustBeString: 'Invalid value, Url and Label must be strings'
|
|
|
|
};
|
|
|
|
|
2020-03-30 23:23:02 +03:00
|
|
|
const createFrame = hbs.handlebars.createFrame;
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2017-04-04 19:07:35 +03:00
|
|
|
module.exports = function navigation(options) {
|
2019-03-09 22:35:19 +03:00
|
|
|
options = options || {};
|
|
|
|
options.hash = options.hash || {};
|
|
|
|
options.data = options.data || {};
|
|
|
|
|
2019-12-04 07:12:02 +03:00
|
|
|
const key = options.hash.type && options.hash.type === 'secondary' ? 'secondary_navigation' : 'navigation';
|
2020-05-08 19:43:40 +03:00
|
|
|
// Set isSecondary so we can compare in the template
|
|
|
|
options.hash.isSecondary = !!(options.hash.type && options.hash.type === 'secondary');
|
|
|
|
// Remove type, so it's not accessible
|
2019-12-04 07:12:02 +03:00
|
|
|
delete options.hash.type;
|
|
|
|
|
2020-04-29 18:44:27 +03:00
|
|
|
const navigationData = options.data.site[key];
|
|
|
|
const currentUrl = options.data.root.relativeUrl;
|
|
|
|
const self = this;
|
|
|
|
let output;
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
if (!_.isObject(navigationData) || _.isFunction(navigationData)) {
|
2016-10-06 15:27:35 +03:00
|
|
|
throw new errors.IncorrectUsageError({
|
2021-09-23 22:46:22 +03:00
|
|
|
message: tpl(messages.invalidData)
|
2016-10-06 15:27:35 +03:00
|
|
|
});
|
2015-01-21 10:00:38 +03:00
|
|
|
}
|
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
if (navigationData.filter(function (e) {
|
2015-02-16 00:44:07 +03:00
|
|
|
return (_.isUndefined(e.label) || _.isUndefined(e.url));
|
|
|
|
}).length > 0) {
|
2016-10-06 15:27:35 +03:00
|
|
|
throw new errors.IncorrectUsageError({
|
2021-09-23 22:46:22 +03:00
|
|
|
message: tpl(messages.valuesMustBeDefined)
|
2016-10-06 15:27:35 +03:00
|
|
|
});
|
2015-02-16 00:44:07 +03:00
|
|
|
}
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
// check for non-null string values
|
2015-02-28 15:53:00 +03:00
|
|
|
if (navigationData.filter(function (e) {
|
2015-01-21 10:00:38 +03:00
|
|
|
return ((!_.isNull(e.label) && !_.isString(e.label)) ||
|
|
|
|
(!_.isNull(e.url) && !_.isString(e.url)));
|
|
|
|
}).length > 0) {
|
2016-10-06 15:27:35 +03:00
|
|
|
throw new errors.IncorrectUsageError({
|
2021-09-23 22:46:22 +03:00
|
|
|
message: tpl(messages.valuesMustBeString)
|
2016-10-06 15:27:35 +03:00
|
|
|
});
|
2015-01-21 10:00:38 +03:00
|
|
|
}
|
|
|
|
|
2016-02-02 13:29:05 +03:00
|
|
|
// strips trailing slashes and compares urls
|
2020-10-20 02:02:56 +03:00
|
|
|
function _isCurrentUrl(href, url) {
|
|
|
|
if (!url) {
|
2016-06-07 22:10:20 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:44:27 +03:00
|
|
|
const strippedHref = href.replace(/\/+$/, '');
|
2020-10-20 02:02:56 +03:00
|
|
|
const strippedCurrentUrl = url.replace(/\/+$/, '');
|
2016-02-02 13:29:05 +03:00
|
|
|
return strippedHref === strippedCurrentUrl;
|
|
|
|
}
|
|
|
|
|
2015-01-21 10:00:38 +03:00
|
|
|
// {{navigation}} should no-op if no data passed in
|
2015-02-28 15:53:00 +03:00
|
|
|
if (navigationData.length === 0) {
|
2017-04-04 19:07:35 +03:00
|
|
|
return new SafeString('');
|
2015-01-21 10:00:38 +03:00
|
|
|
}
|
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
output = navigationData.map(function (e) {
|
2020-04-29 18:44:27 +03:00
|
|
|
const out = {};
|
2016-02-02 13:29:05 +03:00
|
|
|
out.current = _isCurrentUrl(e.url, currentUrl);
|
2015-01-21 10:00:38 +03:00
|
|
|
out.label = e.label;
|
2020-03-30 23:23:02 +03:00
|
|
|
out.slug = slugify(e.label);
|
2017-01-11 13:45:56 +03:00
|
|
|
out.url = e.url;
|
2016-01-05 21:04:39 +03:00
|
|
|
out.secure = self.secure;
|
2015-01-21 10:00:38 +03:00
|
|
|
return out;
|
|
|
|
});
|
|
|
|
|
2019-05-20 13:31:56 +03:00
|
|
|
// CASE: The navigation helper should have access to the navigation items at the top level.
|
|
|
|
this.navigation = output;
|
|
|
|
// CASE: The navigation helper will forward attributes passed to it.
|
|
|
|
_.merge(this, options.hash);
|
2019-03-09 22:35:19 +03:00
|
|
|
const data = createFrame(options.data);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2019-05-20 13:31:56 +03:00
|
|
|
return templates.execute('navigation', this, {data});
|
2015-01-21 10:00:38 +03:00
|
|
|
};
|