mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-20 01:03:23 +03:00
833fe49e6f
- {{#link}}{{/link}} working with all attributes supported and dynamic active class
102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
// # link helper
|
|
const _ = require('lodash');
|
|
const {config, SafeString} = require('./proxy');
|
|
|
|
const managedAttributes = ['href', 'class', 'activeClass', 'parentActiveClass', 'tagName', 'nohref'];
|
|
|
|
function _getHref(hash) {
|
|
let href = hash.href || '/';
|
|
return href.string ? href.string : href;
|
|
}
|
|
|
|
function _clean(url) {
|
|
// Strips anchors and leading and trailing slashes
|
|
return url.replace(/#.*?$/, '').replace(/^\/|\/$/g, '');
|
|
}
|
|
|
|
// strips trailing slashes and compares urls
|
|
function _urlMatch(href, location) {
|
|
if (!location) {
|
|
return false;
|
|
}
|
|
|
|
const strippedHref = _clean(href);
|
|
const strippedLocation = _clean(location);
|
|
|
|
return strippedHref === strippedLocation;
|
|
}
|
|
|
|
// We want to check if the first part of the current url is a match for href
|
|
function _parentMatch(href, location) {
|
|
if (!location) {
|
|
return false;
|
|
}
|
|
|
|
let parent = false;
|
|
let locParts = _clean(location).split('/');
|
|
let hrefParts = _clean(href).split('/');
|
|
|
|
if (locParts.length <= hrefParts.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let i = 0; i < hrefParts.length; i += 1) {
|
|
parent = hrefParts[i] === locParts[i];
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
function _formatAttrs(attributes) {
|
|
let attributeString = '';
|
|
Object.keys(attributes).forEach((key) => {
|
|
let value = attributes[key];
|
|
|
|
// @TODO handle non-string attributes?
|
|
attributeString += `${key}="${value}"`;
|
|
});
|
|
|
|
return attributeString;
|
|
}
|
|
|
|
module.exports = function link(options) {
|
|
options = options || {};
|
|
options.hash = options.hash || {};
|
|
options.data = options.data || {};
|
|
|
|
let href = _getHref(options.hash);
|
|
let location = options.data.root.relativeUrl;
|
|
let tagName = options.hash.tagName || 'a';
|
|
let activeClass = _.has(options.hash, 'activeClass') ? options.hash.activeClass : 'nav-current';
|
|
let parentActiveClass = _.has(options.hash, 'parentActiveClass') ? options.hash.parentActiveClass : `${activeClass || 'nav-current'}-parent`;
|
|
let classes = options.hash.class ? options.hash.class.toString().split(' ') : [];
|
|
let noHref = _.has(options.hash, 'nohref') ? options.hash.nohref : false;
|
|
|
|
// Remove all the attributes we don't want to do a one-to-one mapping of
|
|
managedAttributes.forEach((attr) => {
|
|
delete options.hash[attr];
|
|
});
|
|
|
|
// Setup our one-to-one mapping of attributes;
|
|
let attributes = options.hash;
|
|
|
|
// Calculate dynamic properties
|
|
let relativeHref = href.replace(config.get('url'), '');
|
|
if (_urlMatch(relativeHref, location) && activeClass) {
|
|
classes.push(activeClass);
|
|
} else if (_parentMatch(relativeHref, location) && parentActiveClass) {
|
|
classes.push(parentActiveClass);
|
|
}
|
|
|
|
// Prepare output
|
|
let classString = classes.length > 0 ? `class="${classes.join(' ')}"` : '';
|
|
let hrefString = !noHref ? `href="${href}"` : '';
|
|
let attributeString = _.size(attributes) > 0 ? _formatAttrs(attributes) : '';
|
|
let openingTag = `<${tagName} ${classString} ${hrefString} ${attributeString}>`;
|
|
let closingTag = `</${tagName}>`;
|
|
// Clean up any extra spaces
|
|
openingTag = openingTag.replace(/\s{2,}/g, ' ').replace(/\s>/, '>');
|
|
|
|
return new SafeString(`${openingTag}${options.fn(this)}${closingTag}`);
|
|
};
|