UNPKG

grunt-assemble-navigation

Version:

Assemble navigation plugin. Automatically generate Bootstrap-style, multi-level side nav. See the sidenav on assemble.io for a demonstration.

132 lines (107 loc) 3.56 kB
/*! * grunt-assemble-navigation <https://github.com/assemble/grunt-assemble-navigation> * Thanks to @lukeapage for making this awesome! * * Copyright (c) 2014-2015, Jon Schlinkert. * Licensed under the MIT License. */ var options = { stage: 'render:post:page' }; var path = require('path'); var template = require('template'); var cheerio = require('cheerio'); var log = require('verbalize'); /** * Anchor Plugin * @param {Object} params * @param {Function} callback */ module.exports = function (params, callback) { 'use strict'; // load current page content var $ = cheerio.load(params.content); var anchorOpts = params.assemble.options.anchors || {}; var navOpts = params.assemble.options.navigation || {}; // get all the anchor tags from inside the headers var headings = $('h1[id],h2[id]'); var navigation = []; var duplicateChecker = {}; var dupesFound = false; function findLocation(navigation, depth) { if (depth === 1) { return navigation; } var loc = navigation[navigation.length - 1]; if (!loc) { loc = { children: [] }; navigation.push(loc); } else if (!loc.children) { loc.children = []; } return findLocation(loc.children, depth - 1); } headings.map(function (i, e) { var $e = $(e); var text = $e.text().trim(); var link = $e.attr('id'); var node = { text: text, link: link, $e: $e }; var level = parseInt(e.name.replace(/h/gi, ''), 10); var depth = level <= 1 ? 1 : 2; var location = findLocation(navigation, depth); location.push(node); }); /** * Build the HTML for side navigation. * @param {[type]} navigation [description] * @param {[type]} first [description] * @param {[type]} sParentLink [description] * @return {[type]} [description] */ function buildHTML(navigation, first, sParentLink) { return '<ul class="nav' + (first ? ' sidenav' : '') + '">' + navigation.map(function (loc) { loc.link = (sParentLink ? sParentLink + '-' : '') + loc.link; loc.$e.attr('id', loc.link); if (!duplicateChecker[loc.link]) { duplicateChecker[loc.link] = loc; } else { log.warn('\n>> Duplicate found [text]:"' + duplicateChecker[loc.link].text + '" and "' + loc.text + '", [link]: ' + loc.link); dupesFound = true; } return '<li><a href="#' + loc.link + '">' + loc.text + '</a>' + (loc.children ? buildHTML(loc.children, false, loc.link) : '') + '</li>'; }).join('\n') + '</ul>'; } // if (dupesFound) { // throw new Error("Stopping, duplicates found."); // } $(navOpts.id || '#navigation').append(buildHTML(navigation, true)); // var anchorTemplate = require('./lib/anchor.js'); // If an anchor template is specified in the options, use that instead. if(anchorOpts && anchorOpts.template) { anchorOpts.template = path.resolve(anchorOpts.template); anchorTemplate = require(anchorOpts.template); } headings.map(function (i, e) { var $e = $(e); var id = $e.attr('id'); // Anchor template var anchor = template(anchorTemplate, {id: id}); $(this).append(anchor); // Adjust heading $(this).removeAttr('id').addClass('docs-heading'); if($(this).prev().children().hasClass('source-link')) { var sourceLink = $(this).prev().children('.source-link'); $(this).append(sourceLink); } }); params.content = $.html(); callback(); }; module.exports.options = options;